diff --git a/.config/taplo.toml b/.config/taplo.toml index f5d0b7021ba898ea3ab96323fa3fbc4efdd7b307..2c6ccfb2b34440686764c39ed6db1c73ed940f06 100644 --- a/.config/taplo.toml +++ b/.config/taplo.toml @@ -2,10 +2,12 @@ # ignore zombienet as they do some deliberate custom toml stuff exclude = [ + "bridges/testing/**", "cumulus/zombienet/**", "polkadot/node/malus/integrationtests/**", "polkadot/zombienet_tests/**", "substrate/zombienet/**", + "target/**", ] # global rules diff --git a/.github/scripts/check-prdoc.py b/.github/scripts/check-prdoc.py new file mode 100644 index 0000000000000000000000000000000000000000..42b063f2885da148033986dfa49740f2b0416460 --- /dev/null +++ b/.github/scripts/check-prdoc.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +''' +Ensure that the prdoc files are valid. + +# Example + +```sh +python3 -m pip install cargo-workspace +python3 .github/scripts/check-prdoc.py Cargo.toml prdoc/*.prdoc +``` + +Produces example output: +```pre +🔎 Reading workspace polkadot-sdk/Cargo.toml +📦 Checking 32 prdocs against 493 crates. +✅ All prdocs are valid +``` +''' + +import os +import yaml +import argparse +import cargo_workspace + +def check_prdoc_crate_names(root, paths): + ''' + Check that all crates of the `crates` section of each prdoc is present in the workspace. + ''' + + print(f'🔎 Reading workspace {root}.') + workspace = cargo_workspace.Workspace.from_path(root) + crate_names = [crate.name for crate in workspace.crates] + + print(f'📦 Checking {len(paths)} prdocs against {len(crate_names)} crates.') + faulty = {} + + for path in paths: + with open(path, 'r') as f: + prdoc = yaml.safe_load(f) + + for crate in prdoc.get('crates', []): + crate = crate['name'] + if crate in crate_names: + continue + + faulty.setdefault(path, []).append(crate) + + if len(faulty) == 0: + print('✅ All prdocs are valid.') + else: + print('❌ Some prdocs are invalid.') + for path, crates in faulty.items(): + print(f'💥 {path} lists invalid crate: {", ".join(crates)}') + exit(1) + +def parse_args(): + parser = argparse.ArgumentParser(description='Check prdoc files') + parser.add_argument('root', help='The cargo workspace manifest', metavar='root', type=str, nargs=1) + parser.add_argument('prdoc', help='The prdoc files', metavar='prdoc', type=str, nargs='*') + args = parser.parse_args() + + if len(args.prdoc) == 0: + print('❌ Need at least one prdoc file as argument.') + exit(1) + + return { 'root': os.path.abspath(args.root[0]), 'prdocs': args.prdoc } + +if __name__ == '__main__': + args = parse_args() + check_prdoc_crate_names(args['root'], args['prdocs']) diff --git a/.github/scripts/check-workspace.py b/.github/scripts/check-workspace.py index fb3b53acb0c564c2880d58c51a86e627d8945e35..1f8f103e4e157a8c1c804a618652741193ca5a00 100644 --- a/.github/scripts/check-workspace.py +++ b/.github/scripts/check-workspace.py @@ -18,7 +18,7 @@ def parse_args(): parser.add_argument('workspace_dir', help='The directory to check', metavar='workspace_dir', type=str, nargs=1) parser.add_argument('--exclude', help='Exclude crate paths from the check', metavar='exclude', type=str, nargs='*', default=[]) - + args = parser.parse_args() return (args.workspace_dir[0], args.exclude) @@ -26,7 +26,8 @@ def main(root, exclude): workspace_crates = get_members(root, exclude) all_crates = get_crates(root, exclude) print(f'📦 Found {len(all_crates)} crates in total') - + + check_duplicates(workspace_crates) check_missing(workspace_crates, all_crates) check_links(all_crates) @@ -47,14 +48,14 @@ def get_members(workspace_dir, exclude): if not 'members' in root_manifest['workspace']: return [] - + members = [] for member in root_manifest['workspace']['members']: if member in exclude: print(f'❌ Excluded member should not appear in the workspace {member}') sys.exit(1) members.append(member) - + return members # List all members of the workspace. @@ -73,12 +74,12 @@ def get_crates(workspace_dir, exclude_crates) -> dict: with open(path, "r") as f: content = f.read() manifest = toml.loads(content) - + if 'workspace' in manifest: if root != workspace_dir: print("⏩ Excluded recursive workspace at %s" % path) continue - + # Cut off the root path and the trailing /Cargo.toml. path = path[len(workspace_dir)+1:-11] name = manifest['package']['name'] @@ -86,9 +87,19 @@ def get_crates(workspace_dir, exclude_crates) -> dict: print("⏩ Excluded crate %s at %s" % (name, path)) continue crates[name] = (path, manifest) - + return crates +# Check that there are no duplicate entries in the workspace. +def check_duplicates(workspace_crates): + print(f'🔎 Checking for duplicate crates') + found = {} + for path in workspace_crates: + if path in found: + print(f'❌ crate is listed twice in the workspace {path}') + sys.exit(1) + found[path] = True + # Check that all crates are in the workspace. def check_missing(workspace_crates, all_crates): print(f'🔎 Checking for missing crates') @@ -127,23 +138,23 @@ def check_links(all_crates): if not 'path' in deps[dep]: broken.append((name, dep_name, "crate must be linked via `path`")) return - + def check_crate(deps): to_checks = ['dependencies', 'dev-dependencies', 'build-dependencies'] for to_check in to_checks: if to_check in deps: check_deps(deps[to_check]) - + # There could possibly target dependant deps: if 'target' in manifest: # Target dependant deps can only have one level of nesting: for _, target in manifest['target'].items(): check_crate(target) - + check_crate(manifest) - + links.sort() broken.sort() diff --git a/.github/workflows/check-labels.yml b/.github/workflows/check-labels.yml index 97562f0da09569931582864bd764e6724900d619..1d1a8770058d33ba5e449c6a2e8b307e0ff02eb7 100644 --- a/.github/workflows/check-labels.yml +++ b/.github/workflows/check-labels.yml @@ -9,14 +9,6 @@ jobs: check-labels: runs-on: ubuntu-latest steps: - - name: Skip merge queue - if: ${{ contains(github.ref, 'gh-readonly-queue') }} - run: exit 0 - - name: Pull image - env: - IMAGE: paritytech/ruled_labels:0.4.0 - run: docker pull $IMAGE - - name: Check labels env: IMAGE: paritytech/ruled_labels:0.4.0 @@ -28,6 +20,16 @@ jobs: RULES_PATH: labels/ruled_labels CHECK_SPECS: "specs_polkadot-sdk.yaml" run: | + if [ ${{ github.ref }} == "refs/heads/master" ]; then + echo "Skipping master" + exit 0 + fi + if [ $(echo ${{ github.ref }} | grep -c "gh-readonly-queue") -eq 1 ]; then + echo "Skipping merge queue" + exit 0 + fi + + docker pull $IMAGE echo "REPO: ${REPO}" echo "GITHUB_PR: ${GITHUB_PR}" diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml index e1e92d288ceae235d23fa36c31d592092fe8b0ba..c32b6fcf89e06bb56cefc0517e1dcab1d1ef0f37 100644 --- a/.github/workflows/check-licenses.yml +++ b/.github/workflows/check-licenses.yml @@ -42,5 +42,4 @@ jobs: shopt -s globstar npx @paritytech/license-scanner scan \ --ensure-licenses ${{ env.LICENSES }} \ - --exclude ./substrate/bin/node-template \ -- ./substrate/**/*.rs diff --git a/.github/workflows/check-prdoc.yml b/.github/workflows/check-prdoc.yml index f47404744a49b86735b584e5c0f84bda3fe3078e..c31dee06ec54a0154efc3ad46ff24c79de4d0d7b 100644 --- a/.github/workflows/check-prdoc.yml +++ b/.github/workflows/check-prdoc.yml @@ -17,31 +17,25 @@ env: jobs: check-prdoc: runs-on: ubuntu-latest + if: github.event.pull_request.number != '' steps: + - name: Checkout repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 # we cannot show the version in this step (ie before checking out the repo) # due to https://github.com/paritytech/prdoc/issues/15 - - name: Skip merge queue - if: ${{ contains(github.ref, 'gh-readonly-queue') }} - run: exit 0 - - name: Pull image + - name: Check if PRdoc is required + id: get-labels run: | echo "Pulling $IMAGE" $ENGINE pull $IMAGE - - name: Check if PRdoc is required - id: get-labels - run: | # Fetch the labels for the PR under test echo "Fetch the labels for $API_BASE/${REPO}/pulls/${GITHUB_PR}" labels=$( curl -H "Authorization: token ${GITHUB_TOKEN}" -s "$API_BASE/${REPO}/pulls/${GITHUB_PR}" | jq '.labels | .[] | .name' | tr "\n" ",") echo "Labels: ${labels}" echo "labels=${labels}" >> "$GITHUB_OUTPUT" - - name: Checkout repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 - - - name: Check PRDoc version - run: | + echo "Checking PRdoc version" $ENGINE run --rm -v $PWD:/repo $IMAGE --version - name: Early exit if PR is silent @@ -62,4 +56,12 @@ jobs: run: | echo "Checking for PR#${GITHUB_PR}" echo "You can find more information about PRDoc at $PRDOC_DOC" - $ENGINE run --rm -v $PWD:/repo $IMAGE check -n ${GITHUB_PR} + $ENGINE run --rm -v $PWD:/repo -e RUST_LOG=info $IMAGE check -n ${GITHUB_PR} + + - name: Validate prdoc for PR#${{ github.event.pull_request.number }} + if: ${{ !contains(steps.get-labels.outputs.labels, 'R0') }} + run: | + echo "Validating PR#${GITHUB_PR}" + python3 --version + python3 -m pip install cargo-workspace==1.2.1 + python3 .github/scripts/check-prdoc.py Cargo.toml prdoc/pr_${GITHUB_PR}.prdoc diff --git a/.github/workflows/check-workspace.yml b/.github/workflows/check-workspace.yml index 3dd812d7d9b3743062553b700adba9d6abd93c50..81ec311ccce8153d7a28f68ff801cc917c8d1fd9 100644 --- a/.github/workflows/check-workspace.yml +++ b/.github/workflows/check-workspace.yml @@ -2,8 +2,6 @@ name: Check workspace on: pull_request: - paths: - - "*.toml" merge_group: jobs: @@ -19,5 +17,5 @@ jobs: run: > python3 .github/scripts/check-workspace.py . --exclude - "substrate/frame/contracts/fixtures/build" + "substrate/frame/contracts/fixtures/build" "substrate/frame/contracts/fixtures/contracts/common" diff --git a/.github/workflows/fmt-check.yml b/.github/workflows/fmt-check.yml index 99ac5120097d1d888b0c9207621433cc93a950c2..efcf278c46e83630a54fae3de01d0c9e19304dee 100644 --- a/.github/workflows/fmt-check.yml +++ b/.github/workflows/fmt-check.yml @@ -15,7 +15,7 @@ jobs: os: ["ubuntu-latest"] runs-on: ${{ matrix.os }} container: - image: paritytech/ci-unified:bullseye-1.74.0-2023-11-01-v20231204 + image: docker.io/paritytech/ci-unified:bullseye-1.75.0-2024-01-22-v20240109 steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 diff --git a/.github/workflows/gitspiegel-trigger.yml b/.github/workflows/gitspiegel-trigger.yml index b338f7a3f6254b9db628f8b2b45c88b8094ef390..01058ad74d0b71385a8096964ea6c779fc6f4869 100644 --- a/.github/workflows/gitspiegel-trigger.yml +++ b/.github/workflows/gitspiegel-trigger.yml @@ -13,14 +13,15 @@ on: - unlocked - ready_for_review - reopened + # doesn't work as intended, triggers "workflow_run" webhook in any case # the job doesn't check out any code, so it is relatively safe to run it on any event - pull_request_target: - types: - - opened - - synchronize - - unlocked - - ready_for_review - - reopened + # pull_request_target: + # types: + # - opened + # - synchronize + # - unlocked + # - ready_for_review + # - reopened merge_group: # drop all permissions for GITHUB_TOKEN diff --git a/.github/workflows/release-99_notif-published.yml b/.github/workflows/release-99_notif-published.yml index b35120ca4e128beaa37047b0ac3f21b02f4da663..732db15d9c0c056a9d037785bbce3c87f1bc2620 100644 --- a/.github/workflows/release-99_notif-published.yml +++ b/.github/workflows/release-99_notif-published.yml @@ -8,13 +8,11 @@ on: jobs: ping_matrix: runs-on: ubuntu-latest + environment: release strategy: matrix: channel: # Internal - - name: 'RelEng: Cumulus Release Coordination' - room: '!NAEMyPAHWOiOQHsvus:parity.io' - pre-releases: true - name: "RelEng: Polkadot Release Coordination" room: '!cqAmzdIcbOFwrdrubV:parity.io' pre-release: true @@ -31,18 +29,15 @@ jobs: pre-release: true # Public - # - name: '#KusamaValidatorLounge:polkadot.builders' - # room: '!LhjZccBOqFNYKLdmbb:polkadot.builders' - # pre-releases: false - # - name: '#kusama-announcements:matrix.parity.io' - # room: '!FMwxpQnYhRCNDRsYGI:matrix.parity.io' - # pre-release: false - # - name: '#polkadotvalidatorlounge:web3.foundation' - # room: '!NZrbtteFeqYKCUGQtr:matrix.parity.io' - # pre-release: false - # - name: '#polkadot-announcements:matrix.parity.io' - # room: '!UqHPWiCBGZWxrmYBkF:matrix.parity.io' - # pre-release: false + - name: '#polkadotvalidatorlounge:web3.foundation' + room: '!NZrbtteFeqYKCUGQtr:matrix.parity.io' + pre-releases: false + - name: '#polkadot-announcements:parity.io' + room: '!UqHPWiCBGZWxrmYBkF:matrix.parity.io' + pre-releases: false + - name: '#kusama-announce:parity.io' + room: '!FMwxpQnYhRCNDRsYGI:matrix.parity.io' + pre-releases: false steps: - name: Matrix notification to ${{ matrix.channel.name }} diff --git a/.github/workflows/review-bot.yml b/.github/workflows/review-bot.yml index 0a7e80f007c5b643ce183fdca85d91c57b61f53f..5b036115b2386c366b2f1e78e9ce1dc7d526eedd 100644 --- a/.github/workflows/review-bot.yml +++ b/.github/workflows/review-bot.yml @@ -23,7 +23,7 @@ jobs: app_id: ${{ secrets.REVIEW_APP_ID }} private_key: ${{ secrets.REVIEW_APP_KEY }} - name: "Evaluates PR reviews and assigns reviewers" - uses: paritytech/review-bot@v2.3.0 + uses: paritytech/review-bot@v2.4.0 with: repo-token: ${{ steps.app_token.outputs.token }} team-token: ${{ steps.app_token.outputs.token }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5e01feb84797e82b82d187307cdbaa17d0907b91..7f8796ca51248acb8be92fcb9f76e280b18e605e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -121,9 +121,8 @@ default: before_script: - 'curl --header "PRIVATE-TOKEN: $FL_CI_GROUP_TOKEN" -o forklift -L "${CI_API_V4_URL}/projects/676/packages/generic/forklift/${FL_FORKLIFT_VERSION}/forklift_${FL_FORKLIFT_VERSION}_linux_amd64"' - chmod +x forklift - - mkdir .forklift - - cp $FL_FORKLIFT_CONFIG .forklift/config.toml - - export FORKLIFT_PACKAGE_SUFFIX=${CI_JOB_NAME/ [0-9 \/]*} + - mkdir ~/.forklift + - cp $FL_FORKLIFT_CONFIG ~/.forklift/config.toml - shopt -s expand_aliases - export PATH=$PATH:$(pwd) - | @@ -131,12 +130,8 @@ default: echo "FORKLIFT_BYPASS not set, creating alias cargo='forklift cargo'" alias cargo="forklift cargo" fi - - ls -al - - rm -f forklift.sock - - forklift clean # - echo "FL_FORKLIFT_VERSION ${FL_FORKLIFT_VERSION}" - - echo "FORKLIFT_PACKAGE_SUFFIX $FORKLIFT_PACKAGE_SUFFIX" .common-refs: rules: diff --git a/.gitlab/check-each-crate.py b/.gitlab/check-each-crate.py index da2eaad36c522e5ebfdc0d43e78c38507807e1a6..9b654f8071ac7237fe9c7c943540e8e020cebd6e 100755 --- a/.gitlab/check-each-crate.py +++ b/.gitlab/check-each-crate.py @@ -55,7 +55,7 @@ for i in range(0, crates_per_group + overflow_crates): print(f"Checking {crates[crate][0]}", file=sys.stderr) - res = subprocess.run(["cargo", "check", "--locked"], cwd = crates[crate][1]) + res = subprocess.run(["forklift", "cargo", "check", "--locked"], cwd = crates[crate][1]) if res.returncode != 0: sys.exit(1) diff --git a/.gitlab/pipeline/build.yml b/.gitlab/pipeline/build.yml index d998b62c89936774be8ccaa63df0c0d0ff936513..f8de6135572565d9d16465e68aa3f0bace915cc5 100644 --- a/.gitlab/pipeline/build.yml +++ b/.gitlab/pipeline/build.yml @@ -91,7 +91,7 @@ build-rustdoc: - .run-immediately variables: SKIP_WASM_BUILD: 1 - RUSTDOCFLAGS: "" + RUSTDOCFLAGS: "--default-theme=ayu --html-in-header ./docs/sdk/headers/header.html --extend-css ./docs/sdk/headers/theme.css" artifacts: name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}-doc" when: on_success @@ -314,8 +314,9 @@ 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] script: - - WASM_BUILD_NO_COLOR=1 time cargo build --locked --release -p staging-node-cli + - time WASM_BUILD_NO_COLOR=1 cargo build --locked --release -p staging-node-cli - mv $CARGO_TARGET_DIR/release/substrate-node ./artifacts/substrate/substrate - echo -n "Substrate version = " - if [ "${CI_COMMIT_TAG}" ]; then @@ -329,6 +330,18 @@ 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: @@ -341,9 +354,10 @@ build-linux-substrate: CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" before_script: - mkdir -p ./artifacts/subkey + - !reference [.forklift-cache, before_script] script: - cd ./substrate/bin/utils/subkey - - SKIP_WASM_BUILD=1 time cargo build --locked --release + - time SKIP_WASM_BUILD=1 cargo build --locked --release # - cd - # - mv $CARGO_TARGET_DIR/release/subkey ./artifacts/subkey/. # - echo -n "Subkey version = " @@ -395,8 +409,5 @@ prepare-bridges-zombienet-artifacts: - .collect-artifacts before_script: - mkdir -p ./artifacts/bridges-polkadot-sdk/bridges - - mkdir -p ./artifacts/bridges-polkadot-sdk/cumulus/zombienet script: - - cp -r bridges/zombienet ./artifacts/bridges-polkadot-sdk/bridges/zombienet - - cp -r cumulus/scripts ./artifacts/bridges-polkadot-sdk/cumulus/scripts - - cp -r cumulus/zombienet/bridge-hubs ./artifacts/bridges-polkadot-sdk/cumulus/zombienet/bridge-hubs + - cp -r bridges/testing ./artifacts/bridges-polkadot-sdk/bridges/testing diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index 1ed12e68c2ce19b67dd5aca03cec85702351c039..4d71a473372d3f0caaccfd6f791392c0d5c992f3 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -108,8 +108,10 @@ check-toml-format: 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.0/try-runtime-x86_64-unknown-linux-musl -o try-runtime + 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 @@ -133,6 +135,7 @@ check-runtime-migration-westend: WASM: "westend_runtime.compact.compressed.wasm" URI: "wss://westend-try-runtime-node.parity-chains.parity.io:443" SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" + allow_failure: true check-runtime-migration-rococo: stage: check diff --git a/.gitlab/pipeline/short-benchmarks.yml b/.gitlab/pipeline/short-benchmarks.yml index e9dbe20088116721470e57a02b9b3d1353634c06..bc6dd04264c8e3a46a7c99e427ef6b60243af481 100644 --- a/.gitlab/pipeline/short-benchmarks.yml +++ b/.gitlab/pipeline/short-benchmarks.yml @@ -84,6 +84,16 @@ short-benchmark-coretime-westend: variables: RUNTIME_CHAIN: coretime-westend-dev +short-benchmark-people-rococo: + <<: *short-bench-cumulus + variables: + RUNTIME_CHAIN: people-rococo-dev + +short-benchmark-people-westend: + <<: *short-bench-cumulus + variables: + RUNTIME_CHAIN: people-westend-dev + short-benchmark-glutton-westend: <<: *short-bench-cumulus variables: diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index e75700ffddc468a918b216875294e362571754f5..5c41a3e6e08fae521c41a66735ac54df51e39057 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -25,6 +25,7 @@ test-linux-stable: # "upgrade_version_checks_should_work" is currently failing - | time cargo nextest run \ + --filter-expr 'not deps(/polkadot-subsystem-bench/)' \ --workspace \ --locked \ --release \ @@ -48,6 +49,7 @@ test-linux-stable: - target/nextest/default/junit.xml reports: junit: target/nextest/default/junit.xml + timeout: 90m test-linux-oldkernel-stable: extends: test-linux-stable @@ -68,7 +70,7 @@ test-linux-stable-runtime-benchmarks: # 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 + - time cargo nextest run --filter-expr 'not deps(/polkadot-subsystem-bench/)' --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet # can be used to run all tests # test-linux-stable-all: @@ -224,6 +226,7 @@ cargo-check-benches: 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 diff --git a/.gitlab/pipeline/zombienet/bridges.yml b/.gitlab/pipeline/zombienet/bridges.yml index 9414207a3bbf7031b3ba92e972c50a8db3bdf25e..4278f59b1e9a2e33f32bf255436d6af5d31b30fb 100644 --- a/.gitlab/pipeline/zombienet/bridges.yml +++ b/.gitlab/pipeline/zombienet/bridges.yml @@ -3,9 +3,16 @@ # common settings for all zombienet jobs .zombienet-bridges-common: + extends: + - .kubernetes-env + - .zombienet-refs + rules: + # Docker images have different tag in merge queues + - if: $CI_COMMIT_REF_NAME =~ /^gh-readonly-queue.*$/ + variables: + DOCKER_IMAGES_VERSION: ${CI_COMMIT_SHORT_SHA} + - !reference [.build-refs, rules] before_script: - # Exit if the job is not merge queue - # - if [[ $CI_COMMIT_REF_NAME != *"gh-readonly-queue"* ]]; then echo "I will run only in a merge queue"; exit 0; fi - echo "Zombienet Tests Config" - echo "${ZOMBIENET_IMAGE}" - echo "${GH_DIR}" @@ -19,14 +26,11 @@ needs: - job: build-push-image-bridges-zombienet-tests artifacts: true - extends: - - .kubernetes-env - - .zombienet-refs variables: BRIDGES_ZOMBIENET_TESTS_IMAGE_TAG: ${DOCKER_IMAGES_VERSION} BRIDGES_ZOMBIENET_TESTS_IMAGE: "docker.io/paritypr/bridges-zombienet-tests" - GH_DIR: "https://github.com/paritytech/polkadot-sdk/tree/${CI_COMMIT_SHA}/bridges/zombienet" - LOCAL_DIR: "/builds/parity/mirrors/polkadot-sdk/bridges/zombienet" + GH_DIR: "https://github.com/paritytech/polkadot-sdk/tree/${CI_COMMIT_SHA}/bridges/testing" + LOCAL_DIR: "/builds/parity/mirrors/polkadot-sdk/bridges/testing" FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR: 1 RUN_IN_CONTAINER: "1" artifacts: @@ -37,18 +41,23 @@ - ./zombienet-logs after_script: - mkdir -p ./zombienet-logs - # copy logs of tests runner (run-tests.sh) - - cp -r /tmp/bridges-zombienet-tests.*/tmp.*/tmp.* ./zombienet-logs/ - # copy logs of all nodes - - cp /tmp/zombie*/logs/* ./zombienet-logs/ -# following lines are causing spurious test failures ("At least one of the nodes fails to start") -# retry: 2 -# tags: -# - zombienet-polkadot-integration-test + # copy general logs + - cp -r /tmp/bridges-tests-run-*/logs/* ./zombienet-logs/ + # copy logs of rococo nodes + - cp -r /tmp/bridges-tests-run-*/bridge_hub_rococo_local_network/*.log ./zombienet-logs/ + # copy logs of westend nodes + - cp -r /tmp/bridges-tests-run-*/bridge_hub_westend_local_network/*.log ./zombienet-logs/ zombienet-bridges-0001-asset-transfer-works: extends: - .zombienet-bridges-common script: - - /home/nonroot/bridges-polkadot-sdk/bridges/zombienet/run-tests.sh --docker + - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0001-asset-transfer --docker + - echo "Done" + +zombienet-bridges-0002-mandatory-headers-synced-while-idle: + extends: + - .zombienet-bridges-common + script: + - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0002-mandatory-headers-synced-while-idle --docker - echo "Done" diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 7f5d424ec1b6d18c223f7404ff816646e0fc4c37..97572f029d0020f090a8fd16839028ac9f088cf9 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -150,6 +150,22 @@ zombienet-polkadot-functional-0010-validator-disabling: --local-dir="${LOCAL_DIR}/functional" --test="0010-validator-disabling.zndsl" +zombienet-polkadot-functional-0011-async-backing-6-seconds-rate: + extends: + - .zombienet-polkadot-common + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh + --local-dir="${LOCAL_DIR}/functional" + --test="0011-async-backing-6-seconds-rate.zndsl" + +zombienet-polkadot-functional-0012-elastic-scaling-mvp: + extends: + - .zombienet-polkadot-common + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh + --local-dir="${LOCAL_DIR}/functional" + --test="0012-elastic-scaling-mvp.zndsl" + zombienet-polkadot-smoke-0001-parachains-smoke-test: extends: - .zombienet-polkadot-common diff --git a/Cargo.lock b/Cargo.lock index 1251320df2a3b2df43539169549fa99d0b0644eb..e38f09db4d390e4e970291e393d22f0d2156bd5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,16 +86,16 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.9.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" dependencies = [ "aead 0.4.3", "aes 0.7.5", "cipher 0.3.0", - "ctr 0.8.0", + "ctr 0.7.0", "ghash 0.4.4", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -109,14 +109,14 @@ dependencies = [ "cipher 0.4.4", "ctr 0.9.2", "ghash 0.5.0", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom 0.2.10", "once_cell", @@ -125,9 +125,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "getrandom 0.2.10", @@ -165,7 +165,7 @@ dependencies = [ "hex-literal", "itoa", "proptest", - "rand 0.8.5", + "rand", "ruint", "serde", "tiny-keccak", @@ -191,7 +191,7 @@ checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -206,7 +206,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", "syn-solidity", "tiny-keccak", ] @@ -229,15 +229,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4436e0292ab1bb631b42973c61205e704475fe8126af845c8d923c0996328127" -[[package]] -name = "amcl" -version = "0.3.0" -source = "git+https://github.com/snowfork/milagro_bls?rev=a6d66e4eb89015e352fb1c9f7b661ecdbb5b2176#a6d66e4eb89015e352fb1c9f7b661ecdbb5b2176" -dependencies = [ - "parity-scale-codec", - "scale-info", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -270,9 +261,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.7" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -331,6 +322,20 @@ dependencies = [ "num-traits", ] +[[package]] +name = "aquamarine" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da02abba9f9063d786eab1509833ebb2fac0f966862ca59439c76b9c566760" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "aquamarine" version = "0.5.0" @@ -342,14 +347,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "arbitrary" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" [[package]] name = "ark-bls12-377" @@ -685,7 +690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.5", + "rand", ] [[package]] @@ -695,7 +700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand", "rayon", ] @@ -739,12 +744,6 @@ dependencies = [ "nodrop", ] -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.4" @@ -821,9 +820,8 @@ dependencies = [ "frame-support", "parachains-common", "rococo-emulated-chain", - "serde_json", "sp-core", - "sp-runtime", + "testnet-parachains-constants", ] [[package]] @@ -833,6 +831,7 @@ dependencies = [ "assert_matches", "asset-hub-rococo-runtime", "asset-test-utils", + "cumulus-pallet-parachain-system", "emulated-integration-tests-common", "frame-support", "pallet-asset-conversion", @@ -847,11 +846,12 @@ dependencies = [ "sp-runtime", "staging-xcm", "staging-xcm-executor", + "testnet-parachains-constants", ] [[package]] name = "asset-hub-rococo-runtime" -version = "0.9.420" +version = "0.11.0" dependencies = [ "asset-test-utils", "assets-common", @@ -901,13 +901,11 @@ dependencies = [ "pallet-xcm-bridge-hub-router", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "primitive-types", "rococo-runtime-constants", "scale-info", - "smallvec", "snowbridge-router-primitives", "sp-api", "sp-block-builder", @@ -918,8 +916,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "sp-weights", @@ -928,6 +926,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", ] [[package]] @@ -939,9 +938,8 @@ dependencies = [ "emulated-integration-tests-common", "frame-support", "parachains-common", - "serde_json", "sp-core", - "sp-runtime", + "testnet-parachains-constants", "westend-emulated-chain", ] @@ -952,14 +950,11 @@ dependencies = [ "assert_matches", "asset-hub-westend-runtime", "asset-test-utils", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", - "frame-system", "pallet-asset-conversion", - "pallet-asset-rate", "pallet-assets", "pallet-balances", "pallet-message-queue", @@ -970,16 +965,15 @@ dependencies = [ "polkadot-runtime-common", "sp-runtime", "staging-xcm", - "staging-xcm-builder", "staging-xcm-executor", + "testnet-parachains-constants", "westend-runtime", - "westend-runtime-constants", "westend-system-emulated-network", ] [[package]] name = "asset-hub-westend-runtime" -version = "0.9.420" +version = "0.15.0" dependencies = [ "asset-test-utils", "assets-common", @@ -992,6 +986,7 @@ dependencies = [ "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", @@ -1027,12 +1022,10 @@ dependencies = [ "pallet-xcm-bridge-hub-router", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "primitive-types", "scale-info", - "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -1042,8 +1035,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -1051,38 +1044,33 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", "westend-runtime-constants", ] [[package]] name = "asset-test-utils" -version = "1.0.0" +version = "7.0.0" dependencies = [ - "assets-common", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", - "cumulus-primitives-parachain-inherent", - "cumulus-test-relay-sproof-builder", "frame-support", "frame-system", "hex-literal", - "pallet-asset-conversion", "pallet-assets", "pallet-balances", "pallet-collator-selection", "pallet-session", + "pallet-timestamp", "pallet-xcm", "pallet-xcm-bridge-hub-router", "parachains-common", "parachains-runtimes-test-utils", "parity-scale-codec", - "polkadot-parachain-primitives", - "sp-consensus-aura", - "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", @@ -1092,21 +1080,20 @@ dependencies = [ [[package]] name = "assets-common" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-primitives-core", "frame-support", "impl-trait-for-tuples", "log", "pallet-asset-conversion", - "pallet-asset-tx-payment", "pallet-xcm", "parachains-common", "parity-scale-codec", "scale-info", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -1120,7 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] @@ -1130,7 +1117,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ - "async-lock", + "async-lock 2.8.0", "async-task", "concurrent-queue", "fastrand 1.9.0", @@ -1144,7 +1131,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "blocking", "futures-lite", @@ -1156,7 +1143,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", @@ -1176,7 +1163,18 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy", + "pin-project-lite 0.2.12", ] [[package]] @@ -1198,11 +1196,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" dependencies = [ "async-io", - "async-lock", + "async-lock 2.8.0", "autocfg", "blocking", "cfg-if", - "event-listener", + "event-listener 2.5.3", "futures-lite", "rustix 0.37.23", "signal-hook", @@ -1228,7 +1226,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1245,7 +1243,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1330,7 +1328,7 @@ dependencies = [ "ark-std 0.4.0", "dleq_vrf", "fflonk", - "merlin 3.0.0", + "merlin", "rand_chacha 0.3.1", "rand_core 0.6.4", "ring 0.1.0", @@ -1390,7 +1388,7 @@ dependencies = [ [[package]] name = "binary-merkle-tree" -version = "4.0.0-dev" +version = "13.0.0" dependencies = [ "array-bytes 6.1.0", "env_logger 0.9.3", @@ -1427,7 +1425,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1437,7 +1435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand", "rand_core 0.6.4", "serde", "unicode-normalization", @@ -1555,18 +1553,6 @@ dependencies = [ "constant_time_eq 0.3.0", ] -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -1585,15 +1571,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - [[package]] name = "blocking" version = "1.3.1" @@ -1601,7 +1578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel", - "async-lock", + "async-lock 2.8.0", "async-task", "atomic-waker", "fastrand 1.9.0", @@ -1611,9 +1588,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.1.9" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca548b6163b872067dc5eb82fd130c56881435e30367d2073594a3d9744120dd" +checksum = "d32385ecb91a31bddaf908e8dcf4a15aef1bcd3913cc03ebfad02ff6d568abc1" dependencies = [ "log", "parity-scale-codec", @@ -1633,7 +1610,7 @@ dependencies = [ [[package]] name = "bp-asset-hub-rococo" -version = "0.1.0" +version = "0.4.0" dependencies = [ "bp-xcm-bridge-hub-router", "frame-support", @@ -1643,7 +1620,7 @@ dependencies = [ [[package]] name = "bp-asset-hub-westend" -version = "0.1.0" +version = "0.3.0" dependencies = [ "bp-xcm-bridge-hub-router", "frame-support", @@ -1653,7 +1630,7 @@ dependencies = [ [[package]] name = "bp-bridge-hub-cumulus" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -1662,12 +1639,12 @@ dependencies = [ "frame-system", "polkadot-primitives", "sp-api", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-bridge-hub-kusama" -version = "0.1.0" +version = "0.6.0" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1675,12 +1652,12 @@ dependencies = [ "frame-support", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-bridge-hub-polkadot" -version = "0.1.0" +version = "0.6.0" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1688,12 +1665,12 @@ dependencies = [ "frame-support", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-bridge-hub-rococo" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1701,12 +1678,12 @@ dependencies = [ "frame-support", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-bridge-hub-westend" -version = "0.1.0" +version = "0.3.0" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1714,12 +1691,12 @@ dependencies = [ "frame-support", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-header-chain" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-runtime", "bp-test-utils", @@ -1733,24 +1710,24 @@ dependencies = [ "sp-consensus-grandpa", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-kusama" -version = "0.1.0" +version = "0.5.0" dependencies = [ "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", "sp-api", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-messages" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-header-chain", "bp-runtime", @@ -1761,12 +1738,12 @@ dependencies = [ "scale-info", "serde", "sp-core", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-parachains" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -1777,24 +1754,24 @@ dependencies = [ "scale-info", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-polkadot" -version = "0.1.0" +version = "0.5.0" dependencies = [ "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", "sp-api", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-polkadot-bulletin" -version = "0.1.0" +version = "0.4.0" dependencies = [ "bp-header-chain", "bp-messages", @@ -1806,12 +1783,12 @@ dependencies = [ "scale-info", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-polkadot-core" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-messages", "bp-runtime", @@ -1824,12 +1801,12 @@ dependencies = [ "serde", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-relayers" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-messages", "bp-runtime", @@ -1839,24 +1816,24 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-rococo" -version = "0.1.0" +version = "0.6.0" dependencies = [ "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", "sp-api", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-runtime" -version = "0.1.0" +version = "0.7.0" dependencies = [ "frame-support", "frame-system", @@ -1872,14 +1849,14 @@ dependencies = [ "sp-io", "sp-runtime", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", "trie-db", ] [[package]] name = "bp-test-utils" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-header-chain", "bp-parachains", @@ -1892,32 +1869,32 @@ dependencies = [ "sp-consensus-grandpa", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", ] [[package]] name = "bp-westend" -version = "0.1.0" +version = "0.3.0" dependencies = [ "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", "sp-api", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-xcm-bridge-hub" -version = "0.1.0" +version = "0.2.0" dependencies = [ - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "bp-xcm-bridge-hub-router" -version = "0.1.0" +version = "0.6.0" dependencies = [ "parity-scale-codec", "scale-info", @@ -1927,7 +1904,7 @@ dependencies = [ [[package]] name = "bridge-hub-common" -version = "0.1.0" +version = "0.0.0" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -1937,7 +1914,7 @@ dependencies = [ "snowbridge-core", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", ] @@ -1947,18 +1924,11 @@ version = "0.0.0" dependencies = [ "bridge-hub-common", "bridge-hub-rococo-runtime", - "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", "parachains-common", - "serde_json", - "snowbridge-core", - "snowbridge-pallet-inbound-queue", - "snowbridge-pallet-outbound-queue", - "snowbridge-pallet-system", - "snowbridge-router-primitives", "sp-core", - "sp-runtime", + "testnet-parachains-constants", ] [[package]] @@ -1966,14 +1936,11 @@ name = "bridge-hub-rococo-integration-tests" version = "1.0.0" dependencies = [ "asset-hub-rococo-runtime", - "asset-test-utils", "bp-messages", "bridge-hub-rococo-runtime", - "cumulus-pallet-dmp-queue", "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", - "hex", "hex-literal", "pallet-asset-conversion", "pallet-assets", @@ -1983,12 +1950,12 @@ dependencies = [ "pallet-xcm", "parachains-common", "parity-scale-codec", - "penpal-runtime", "rococo-system-emulated-network", "rococo-westend-system-emulated-network", "scale-info", "snowbridge-core", "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", @@ -1996,11 +1963,12 @@ dependencies = [ "sp-runtime", "staging-xcm", "staging-xcm-executor", + "testnet-parachains-constants", ] [[package]] name = "bridge-hub-rococo-runtime" -version = "0.1.0" +version = "0.5.0" dependencies = [ "bp-asset-hub-rococo", "bp-asset-hub-westend", @@ -2024,6 +1992,7 @@ dependencies = [ "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", @@ -2055,13 +2024,11 @@ dependencies = [ "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "rococo-runtime-constants", "scale-info", "serde", - "smallvec", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-outbound-queue-runtime-api", @@ -2084,8 +2051,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -2094,16 +2061,16 @@ dependencies = [ "staging-xcm-executor", "static_assertions", "substrate-wasm-builder", + "testnet-parachains-constants", ] [[package]] name = "bridge-hub-test-utils" -version = "0.1.0" +version = "0.7.0" dependencies = [ "asset-test-utils", "bp-header-chain", "bp-messages", - "bp-parachains", "bp-polkadot-core", "bp-relayers", "bp-runtime", @@ -2111,8 +2078,6 @@ dependencies = [ "bridge-runtime-common", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", - "frame-benchmarking", - "frame-executive", "frame-support", "frame-system", "impl-trait-for-tuples", @@ -2122,11 +2087,8 @@ dependencies = [ "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", - "pallet-collator-selection", - "pallet-session", + "pallet-timestamp", "pallet-utility", - "pallet-xcm", - "pallet-xcm-benchmarks", "parachains-common", "parachains-runtimes-test-utils", "parity-scale-codec", @@ -2134,9 +2096,8 @@ dependencies = [ "sp-io", "sp-keyring", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", - "staging-parachain-info", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -2148,23 +2109,19 @@ version = "0.0.0" dependencies = [ "bridge-hub-common", "bridge-hub-westend-runtime", - "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", "parachains-common", - "serde_json", "sp-core", - "sp-runtime", + "testnet-parachains-constants", ] [[package]] name = "bridge-hub-westend-integration-tests" version = "1.0.0" dependencies = [ - "asset-test-utils", "bp-messages", "bridge-hub-westend-runtime", - "cumulus-pallet-dmp-queue", "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", @@ -2175,7 +2132,6 @@ dependencies = [ "pallet-message-queue", "pallet-xcm", "parachains-common", - "parity-scale-codec", "rococo-westend-system-emulated-network", "sp-runtime", "staging-xcm", @@ -2184,7 +2140,7 @@ dependencies = [ [[package]] name = "bridge-hub-westend-runtime" -version = "0.1.0" +version = "0.2.0" dependencies = [ "bp-asset-hub-rococo", "bp-asset-hub-westend", @@ -2206,6 +2162,7 @@ dependencies = [ "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", @@ -2237,12 +2194,10 @@ dependencies = [ "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "scale-info", "serde", - "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -2254,8 +2209,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -2264,12 +2219,13 @@ dependencies = [ "staging-xcm-executor", "static_assertions", "substrate-wasm-builder", + "testnet-parachains-constants", "westend-runtime-constants", ] [[package]] name = "bridge-runtime-common" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-header-chain", "bp-messages", @@ -2297,7 +2253,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", "staging-xcm", "staging-xcm-builder", @@ -2634,40 +2590,40 @@ dependencies = [ "clap_lex 0.2.4", "indexmap 1.9.3", "once_cell", - "strsim", + "strsim 0.10.0", "termcolor", "textwrap", ] [[package]] name = "clap" -version = "4.4.18" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", - "clap_derive 4.4.7", + "clap_derive 4.5.0", ] [[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", ] [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", - "clap_lex 0.6.0", - "strsim", + "clap_lex 0.7.0", + "strsim 0.11.0", "terminal_size", ] @@ -2677,7 +2633,7 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", ] [[package]] @@ -2695,14 +2651,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -2716,9 +2672,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "coarsetime" @@ -2751,21 +2707,20 @@ dependencies = [ "emulated-integration-tests-common", "frame-support", "parachains-common", - "serde_json", "sp-core", - "sp-runtime", - "westend-emulated-chain", + "testnet-parachains-constants", ] [[package]] name = "collectives-westend-runtime" -version = "1.0.0" +version = "3.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", @@ -2803,11 +2758,9 @@ dependencies = [ "pallet-xcm", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "scale-info", - "smallvec", "sp-api", "sp-arithmetic", "sp-block-builder", @@ -2819,8 +2772,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -2828,6 +2781,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", "westend-runtime-constants", ] @@ -2873,11 +2827,10 @@ 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", ] @@ -2905,7 +2858,7 @@ dependencies = [ "ark-std 0.4.0", "fflonk", "getrandom_or_panic", - "merlin 3.0.0", + "merlin", "rand_chacha 0.3.1", ] @@ -3014,14 +2967,14 @@ checksum = "f272d0c4cf831b4fa80ee529c7707f76585986e910e1fbce1d7921970bc1a241" [[package]] name = "contracts-rococo-runtime" -version = "0.2.0" +version = "0.8.0" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", @@ -3050,12 +3003,10 @@ dependencies = [ "pallet-xcm", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "rococo-runtime-constants", "scale-info", - "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -3065,8 +3016,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -3074,6 +3025,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", ] [[package]] @@ -3116,6 +3068,7 @@ dependencies = [ "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", @@ -3144,13 +3097,11 @@ dependencies = [ "pallet-xcm-benchmarks", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "rococo-runtime-constants", "scale-info", "serde", - "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -3160,8 +3111,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -3169,6 +3120,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", ] [[package]] @@ -3180,6 +3132,7 @@ dependencies = [ "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "frame-benchmarking", @@ -3194,11 +3147,11 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", + "pallet-broker", "pallet-collator-selection", "pallet-message-queue", "pallet-multisig", "pallet-session", - "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", @@ -3207,12 +3160,10 @@ dependencies = [ "pallet-xcm-benchmarks", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "scale-info", "serde", - "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -3222,8 +3173,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -3231,6 +3182,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", "westend-runtime-constants", ] @@ -3415,7 +3367,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.4.18", + "clap 4.5.1", "criterion-plot", "futures", "is-terminal", @@ -3511,7 +3463,7 @@ checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] @@ -3543,24 +3495,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ "generic-array 0.14.7", - "subtle 2.4.1", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.7", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] name = "ctr" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" dependencies = [ "cipher 0.3.0", ] @@ -3576,9 +3518,9 @@ dependencies = [ [[package]] name = "cumulus-client-cli" -version = "0.1.0" +version = "0.7.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -3592,7 +3534,7 @@ dependencies = [ [[package]] name = "cumulus-client-collator" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -3616,13 +3558,13 @@ dependencies = [ "sp-maybe-compressed-blob", "sp-runtime", "sp-state-machine", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "tracing", ] [[package]] name = "cumulus-client-consensus-aura" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-client-collator", @@ -3663,7 +3605,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-client-pov-recovery", @@ -3687,7 +3629,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-timestamp", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-trie", "substrate-prometheus-endpoint", "tracing", @@ -3695,7 +3637,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-proposer" -version = "0.1.0" +version = "0.7.0" dependencies = [ "anyhow", "async-trait", @@ -3709,7 +3651,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-relay-chain" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -3731,7 +3673,7 @@ dependencies = [ [[package]] name = "cumulus-client-network" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -3775,19 +3717,19 @@ dependencies = [ "sc-client-api", "scale-info", "sp-api", - "sp-core", + "sp-crypto-hashing", "sp-inherents", "sp-runtime", "sp-state-machine", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-trie", "tracing", ] [[package]] name = "cumulus-client-pov-recovery" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -3801,7 +3743,7 @@ dependencies = [ "polkadot-overseer", "polkadot-primitives", "portpicker", - "rand 0.8.5", + "rand", "sc-cli", "sc-client-api", "sc-consensus", @@ -3815,7 +3757,7 @@ dependencies = [ [[package]] name = "cumulus-client-service" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", @@ -3850,7 +3792,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-aura-ext" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-pallet-parachain-system", "frame-support", @@ -3862,12 +3804,12 @@ dependencies = [ "sp-application-crypto", "sp-consensus-aura", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "cumulus-pallet-dmp-queue" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-primitives-core", "frame-benchmarking", @@ -3879,14 +3821,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", ] [[package]] name = "cumulus-pallet-parachain-system" -version = "0.1.0" +version = "0.7.0" dependencies = [ "assert_matches", "bytes", @@ -3908,19 +3850,21 @@ dependencies = [ "pallet-message-queue", "parity-scale-codec", "polkadot-parachain-primitives", + "polkadot-runtime-common", "polkadot-runtime-parachains", - "rand 0.8.5", + "rand", "sc-client-api", "scale-info", "sp-core", - "sp-externalities 0.19.0", + "sp-crypto-hashing", + "sp-externalities 0.25.0", "sp-inherents", "sp-io", "sp-keyring", "sp-runtime", "sp-state-machine", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "sp-trie", "sp-version", "staging-xcm", @@ -3930,17 +3874,17 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system-proc-macro" -version = "0.1.0" +version = "0.6.0" dependencies = [ "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "cumulus-pallet-session-benchmarking" -version = "3.0.0" +version = "9.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -3948,12 +3892,12 @@ dependencies = [ "pallet-session", "parity-scale-codec", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "cumulus-pallet-solo-to-para" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-pallet-parachain-system", "frame-support", @@ -3963,12 +3907,12 @@ dependencies = [ "polkadot-primitives", "scale-info", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "cumulus-pallet-xcm" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -3977,13 +3921,13 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", ] [[package]] name = "cumulus-pallet-xcmp-queue" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bounded-collections", "bp-xcm-bridge-hub-router", @@ -4002,7 +3946,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -4010,7 +3954,7 @@ dependencies = [ [[package]] name = "cumulus-ping" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-pallet-xcm", "cumulus-primitives-core", @@ -4019,13 +3963,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", ] [[package]] name = "cumulus-primitives-aura" -version = "0.1.0" +version = "0.7.0" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", @@ -4033,12 +3977,12 @@ dependencies = [ "sp-api", "sp-consensus-aura", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "cumulus-primitives-core" -version = "0.1.0" +version = "0.7.0" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", @@ -4047,14 +3991,14 @@ dependencies = [ "scale-info", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", "staging-xcm", ] [[package]] name = "cumulus-primitives-parachain-inherent" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -4064,49 +4008,69 @@ dependencies = [ "sp-inherents", "sp-runtime", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", ] [[package]] name = "cumulus-primitives-proof-size-hostfunction" -version = "0.1.0" +version = "0.2.0" dependencies = [ "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-io", - "sp-runtime-interface 17.0.0", + "sp-runtime-interface 24.0.0", "sp-state-machine", "sp-trie", ] +[[package]] +name = "cumulus-primitives-storage-weight-reclaim" +version = "1.0.0" +dependencies = [ + "cumulus-primitives-core", + "cumulus-primitives-proof-size-hostfunction", + "cumulus-test-runtime", + "docify 0.2.7", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "sp-trie", +] + [[package]] name = "cumulus-primitives-timestamp" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-primitives-core", "futures", "parity-scale-codec", "sp-inherents", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-timestamp", ] [[package]] name = "cumulus-primitives-utility" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-primitives-core", "frame-support", "log", "pallet-asset-conversion", - "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-runtime-common", "polkadot-runtime-parachains", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -4114,7 +4078,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-inprocess-interface" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -4142,7 +4106,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-interface" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -4159,7 +4123,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-minimal-node" -version = "0.1.0" +version = "0.7.0" dependencies = [ "array-bytes 6.1.0", "async-trait", @@ -4180,6 +4144,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-primitives", + "polkadot-service", "sc-authority-discovery", "sc-client-api", "sc-network", @@ -4199,7 +4164,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-rpc-interface" -version = "0.1.0" +version = "0.7.0" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -4211,7 +4176,7 @@ dependencies = [ "parity-scale-codec", "pin-project", "polkadot-overseer", - "rand 0.8.5", + "rand", "sc-client-api", "sc-rpc-api", "sc-service", @@ -4226,7 +4191,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-state-machine", - "sp-storage 13.0.0", + "sp-storage 19.0.0", "sp-version", "thiserror", "tokio", @@ -4242,6 +4207,7 @@ dependencies = [ "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", + "cumulus-primitives-storage-weight-reclaim", "cumulus-test-relay-sproof-builder", "cumulus-test-runtime", "cumulus-test-service", @@ -4269,14 +4235,14 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", "polkadot-primitives", "sp-runtime", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", ] @@ -4286,6 +4252,7 @@ version = "0.1.0" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", + "cumulus-primitives-storage-weight-reclaim", "frame-executive", "frame-support", "frame-system", @@ -4307,7 +4274,7 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-pool", "sp-version", "substrate-wasm-builder", @@ -4318,7 +4285,7 @@ name = "cumulus-test-service" version = "0.1.0" dependencies = [ "async-trait", - "clap 4.4.18", + "clap 4.5.1", "criterion 0.5.1", "cumulus-client-cli", "cumulus-client-consensus-common", @@ -4328,6 +4295,7 @@ dependencies = [ "cumulus-client-service", "cumulus-pallet-parachain-system", "cumulus-primitives-core", + "cumulus-primitives-storage-weight-reclaim", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", @@ -4350,7 +4318,7 @@ dependencies = [ "polkadot-service", "polkadot-test-service", "portpicker", - "rand 0.8.5", + "rand", "rococo-parachain-runtime", "sc-basic-authorship", "sc-block-builder", @@ -4381,7 +4349,7 @@ dependencies = [ "sp-runtime", "sp-state-machine", "sp-timestamp", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-test-client", "substrate-test-utils", "tempfile", @@ -4390,19 +4358,6 @@ dependencies = [ "url", ] -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle 2.4.1", - "zeroize", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -4412,15 +4367,15 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] [[package]] name = "curve25519-dalek" -version = "4.1.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if", "cpufeatures", @@ -4429,7 +4384,7 @@ dependencies = [ "fiat-crypto", "platforms", "rustc_version 0.4.0", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] @@ -4441,7 +4396,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -4481,7 +4436,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -4498,7 +4453,7 @@ checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -4653,7 +4608,7 @@ dependencies = [ "block-buffer 0.10.4", "const-oid", "crypto-common", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -4706,7 +4661,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -4748,18 +4703,44 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "docify" -version = "0.2.6" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1b04e6ef3d21119d3eb7b032bca17f99fe041e9c072f30f32cc0e1a2b1f3c4" +dependencies = [ + "docify_macros 0.1.16", +] + +[[package]] +name = "docify" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4235e9b248e2ba4b92007fe9c646f3adf0ffde16dc74713eacc92b8bc58d8d2f" +checksum = "7cc4fd38aaa9fb98ac70794c82a00360d1e165a87fbf96a8a91f9dfc602aaee2" dependencies = [ - "docify_macros", + "docify_macros 0.2.7", ] [[package]] name = "docify_macros" -version = "0.2.6" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5610df7f2acf89a1bb5d1a66ae56b1c7fcdcfe3948856fb3ace3f644d70eb7" +dependencies = [ + "common-path", + "derive-syn-parse", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "syn 2.0.50", + "termcolor", + "walkdir", +] + +[[package]] +name = "docify_macros" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47020e12d7c7505670d1363dd53d6c23724f71a90a3ae32ff8eba40de8404626" +checksum = "63fa215f3a0d40fb2a221b3aa90d8e1fbb8379785a990cb60d62ac71ebdc6460" dependencies = [ "common-path", "derive-syn-parse", @@ -4767,9 +4748,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.48", + "syn 2.0.50", "termcolor", - "toml 0.7.8", + "toml 0.8.8", "walkdir", ] @@ -4854,12 +4835,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" dependencies = [ - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "ed25519", "rand_core 0.6.4", "serde", "sha2 0.10.7", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] @@ -4883,7 +4864,7 @@ version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "ed25519", "hashbrown 0.14.3", "hex", @@ -4913,13 +4894,13 @@ dependencies = [ "pkcs8", "rand_core 0.6.4", "sec1", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] [[package]] name = "emulated-integration-tests-common" -version = "1.0.0" +version = "3.0.0" dependencies = [ "asset-test-utils", "bp-messages", @@ -4931,7 +4912,6 @@ dependencies = [ "pallet-assets", "pallet-balances", "pallet-bridge-messages", - "pallet-im-online", "pallet-message-queue", "pallet-xcm", "parachains-common", @@ -4939,9 +4919,7 @@ dependencies = [ "paste", "polkadot-primitives", "polkadot-runtime-parachains", - "polkadot-service", "sc-consensus-grandpa", - "serde_json", "sp-authority-discovery", "sp-consensus-babe", "sp-consensus-beefy", @@ -4995,7 +4973,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -5006,7 +4984,7 @@ checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -5099,8 +5077,9 @@ dependencies = [ [[package]] name = "ethabi-decode" -version = "1.4.0" -source = "git+https://github.com/snowfork/ethabi-decode.git?branch=master#7d215837b626650bd9a076821e57ad488101301f" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d398648d65820a727d6a81e58b962f874473396a047e4c30bafe3240953417" dependencies = [ "ethereum-types", "tiny-keccak", @@ -5143,6 +5122,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite 0.2.12", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite 0.2.12", +] + [[package]] name = "exit-future" version = "0.2.0" @@ -5174,7 +5174,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -5187,12 +5187,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -5289,7 +5283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -5302,7 +5296,7 @@ dependencies = [ "ark-poly", "ark-serialize 0.4.2", "ark-std 0.4.0", - "merlin 3.0.0", + "merlin", ] [[package]] @@ -5346,7 +5340,7 @@ dependencies = [ "num-traits", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "scale-info", ] @@ -5369,7 +5363,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.5", + "rand", "rustc-hex", "static_assertions", ] @@ -5408,7 +5402,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" -version = "3.0.0" +version = "12.0.0" dependencies = [ "parity-scale-codec", ] @@ -5442,7 +5436,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" name = "frame" version = "0.0.1-dev" dependencies = [ - "docify", + "docify 0.2.7", "frame-executive", "frame-support", "frame-system", @@ -5451,7 +5445,6 @@ dependencies = [ "pallet-examples", "parity-scale-codec", "scale-info", - "simple-mermaid", "sp-api", "sp-arithmetic", "sp-block-builder", @@ -5463,14 +5456,14 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-pool", "sp-version", ] [[package]] name = "frame-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "array-bytes 6.1.0", "frame-support", @@ -5489,20 +5482,20 @@ dependencies = [ "sp-io", "sp-keystore", "sp-runtime", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-runtime-interface 24.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "static_assertions", ] [[package]] name = "frame-benchmarking-cli" -version = "4.0.0-dev" +version = "32.0.0" dependencies = [ "Inflector", "array-bytes 6.1.0", "chrono", - "clap 4.4.18", + "clap 4.5.1", "comfy-table", "frame-benchmarking", "frame-support", @@ -5514,7 +5507,7 @@ dependencies = [ "linked-hash-map", "log", "parity-scale-codec", - "rand 0.8.5", + "rand", "rand_pcg", "sc-block-builder", "sc-cli", @@ -5529,22 +5522,22 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-database", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-inherents", "sp-io", "sp-keystore", "sp-runtime", "sp-state-machine", - "sp-storage 13.0.0", + "sp-storage 19.0.0", "sp-trie", - "sp-wasm-interface 14.0.0", + "sp-wasm-interface 20.0.0", "thiserror", "thousands", ] [[package]] name = "frame-benchmarking-pallet-pov" -version = "4.0.0-dev" +version = "18.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5553,12 +5546,12 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "frame-election-provider-solution-type" -version = "4.0.0-dev" +version = "13.0.0" dependencies = [ "frame-election-provider-support", "frame-support", @@ -5568,39 +5561,39 @@ dependencies = [ "quote", "scale-info", "sp-arithmetic", - "syn 2.0.48", + "syn 2.0.50", "trybuild", ] [[package]] name = "frame-election-provider-support" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-election-provider-solution-type", "frame-support", "frame-system", "parity-scale-codec", - "rand 0.8.5", + "rand", "scale-info", "sp-arithmetic", "sp-core", "sp-io", "sp-npos-elections", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-support", "honggfuzz", "parity-scale-codec", - "rand 0.8.5", + "rand", "scale-info", "sp-arithmetic", "sp-npos-elections", @@ -5609,8 +5602,9 @@ dependencies = [ [[package]] name = "frame-executive" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ + "aquamarine 0.3.3", "array-bytes 6.1.0", "frame-support", "frame-system", @@ -5624,8 +5618,8 @@ dependencies = [ "sp-inherents", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "sp-version", ] @@ -5643,7 +5637,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" -version = "0.10.0-dev" +version = "0.35.0" dependencies = [ "futures", "indicatif", @@ -5652,10 +5646,11 @@ dependencies = [ "parity-scale-codec", "serde", "sp-core", + "sp-crypto-hashing", "sp-io", "sp-runtime", "sp-state-machine", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "spinners", "substrate-rpc-client", "tokio", @@ -5664,13 +5659,13 @@ dependencies = [ [[package]] name = "frame-support" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ - "aquamarine", + "aquamarine 0.5.0", "array-bytes 6.1.0", "assert_matches", "bitflags 1.3.2", - "docify", + "docify 0.2.7", "environmental", "frame-metadata", "frame-support-procedural", @@ -5689,8 +5684,9 @@ dependencies = [ "sp-api", "sp-arithmetic", "sp-core", - "sp-core-hashing-proc-macro", - "sp-debug-derive 8.0.0", + "sp-crypto-hashing", + "sp-crypto-hashing-proc-macro", + "sp-debug-derive 14.0.0", "sp-genesis-builder", "sp-inherents", "sp-io", @@ -5698,8 +5694,9 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-state-machine", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-timestamp", + "sp-tracing 16.0.0", "sp-weights", "static_assertions", "tt-call", @@ -5707,7 +5704,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" -version = "4.0.0-dev" +version = "23.0.0" dependencies = [ "Inflector", "cfg-expr", @@ -5720,28 +5717,28 @@ dependencies = [ "proc-macro2", "quote", "regex", - "sp-core-hashing", - "syn 2.0.48", + "sp-crypto-hashing", + "syn 2.0.50", ] [[package]] name = "frame-support-procedural-tools" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "frame-support-procedural-tools-derive" -version = "3.0.0" +version = "11.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -5766,7 +5763,7 @@ dependencies = [ "sp-metadata-ir", "sp-runtime", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-version", "static_assertions", "trybuild", @@ -5808,21 +5805,21 @@ dependencies = [ [[package]] name = "frame-system" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "cfg-if", "criterion 0.4.0", - "docify", + "docify 0.2.7", "frame-support", "log", "parity-scale-codec", "scale-info", "serde", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-version", "sp-weights", "substrate-test-runtime-client", @@ -5830,7 +5827,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5838,16 +5835,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-version", ] [[package]] name = "frame-system-rpc-runtime-api" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -5855,13 +5852,13 @@ dependencies = [ [[package]] name = "frame-try-runtime" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "frame-support", "parity-scale-codec", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -5886,7 +5883,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", ] @@ -5904,9 +5901,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -5935,9 +5932,9 @@ checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -5974,7 +5971,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -6035,7 +6032,7 @@ dependencies = [ [[package]] name = "generate-bags" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "chrono", "frame-election-provider-support", @@ -6104,7 +6101,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" dependencies = [ - "rand 0.8.5", + "rand", "rand_core 0.6.4", ] @@ -6155,22 +6152,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "globset" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - [[package]] name = "glutton-westend-runtime" -version = "1.0.0" +version = "3.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -6202,8 +6186,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -6211,6 +6195,25 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", +] + +[[package]] +name = "governor" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "821239e5672ff23e2a7060901fa622950bbd80b649cdaadd78d1c1767ed14eb4" +dependencies = [ + "cfg-if", + "dashmap", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot 0.12.1", + "quanta", + "rand", + "smallvec", ] [[package]] @@ -6221,7 +6224,7 @@ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core 0.6.4", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -6236,7 +6239,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.0.0", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -6284,7 +6287,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.8", ] [[package]] @@ -6293,7 +6296,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", ] [[package]] @@ -6302,7 +6305,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "allocator-api2", "serde", ] @@ -6368,16 +6371,6 @@ dependencies = [ "digest 0.9.0", ] -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.12.1" @@ -6502,10 +6495,9 @@ dependencies = [ "hyper", "log", "rustls 0.21.6", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls", - "webpki-roots 0.23.1", + "tokio-rustls 0.24.1", ] [[package]] @@ -6668,9 +6660,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -6684,9 +6676,9 @@ checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" [[package]] name = "indicatif" -version = "0.17.6" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" dependencies = [ "console", "instant", @@ -6770,7 +6762,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", ] @@ -6831,22 +6823,11 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" -[[package]] -name = "json-patch" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658" -dependencies = [ - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "jsonrpsee" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "367a292944c07385839818bb71c8d76611138e2dedb0677d035b8da21d29c78b" +checksum = "4a95f7cc23d5fab0cdeeaf6bad8c8f5e7a3aa7f0d211957ea78232b327ab27b0" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -6854,85 +6835,85 @@ dependencies = [ "jsonrpsee-server", "jsonrpsee-types", "jsonrpsee-ws-client", + "tokio", "tracing", ] [[package]] name = "jsonrpsee-client-transport" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8b3815d9f5d5de348e5f162b316dc9cdf4548305ebb15b4eb9328e66cf27d7a" +checksum = "6b1736cfa3845fd9f8f43751f2b8e0e83f7b6081e754502f7d63b6587692cc83" dependencies = [ "futures-util", "http", "jsonrpsee-core", - "jsonrpsee-types", "pin-project", - "rustls-native-certs", + "rustls-native-certs 0.7.0", + "rustls-pki-types", "soketto", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "tokio-util", "tracing", - "webpki-roots 0.25.2", + "url", ] [[package]] name = "jsonrpsee-core" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5dde66c53d6dcdc8caea1874a45632ec0fcf5b437789f1e45766a1512ce803" +checksum = "82030d038658974732103e623ba2e0abec03bbbe175b39c0a2fafbada60c5868" dependencies = [ "anyhow", - "arrayvec 0.7.4", - "async-lock", + "async-lock 3.3.0", "async-trait", "beef", - "futures-channel", "futures-timer", "futures-util", - "globset", "hyper", "jsonrpsee-types", "parking_lot 0.12.1", - "rand 0.8.5", + "pin-project", + "rand", "rustc-hash", "serde", "serde_json", - "soketto", "thiserror", "tokio", + "tokio-stream", "tracing", ] [[package]] name = "jsonrpsee-http-client" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5f9fabdd5d79344728521bb65e3106b49ec405a78b66fbff073b72b389fa43" +checksum = "36a06ef0de060005fddf772d54597bb6a8b0413da47dcffd304b0306147b9678" dependencies = [ "async-trait", "hyper", "hyper-rustls", "jsonrpsee-core", "jsonrpsee-types", - "rustc-hash", "serde", "serde_json", "thiserror", "tokio", + "tower", "tracing", + "url", ] [[package]] name = "jsonrpsee-proc-macros" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e8ab85614a08792b9bff6c8feee23be78c98d0182d4c622c05256ab553892a" +checksum = "69fc56131589f82e57805f7338b87023db4aafef813555708b159787e34ad6bc" dependencies = [ "heck", - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.0.0", "proc-macro2", "quote", "syn 1.0.109", @@ -6940,19 +6921,21 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4d945a6008c9b03db3354fb3c83ee02d2faa9f2e755ec1dfb69c3551b8f4ba" +checksum = "d85be77fe5b2a94589e3164fb780017f7aff7d646b49278c0d0346af16975c8e" dependencies = [ - "futures-channel", "futures-util", "http", "hyper", "jsonrpsee-core", "jsonrpsee-types", + "pin-project", + "route-recognizer", "serde", "serde_json", "soketto", + "thiserror", "tokio", "tokio-stream", "tokio-util", @@ -6962,28 +6945,28 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245ba8e5aa633dd1c1e4fae72bce06e71f42d34c14a2767c6b4d173b57bee5e5" +checksum = "9a48fdc1202eafc51c63e00406575e59493284ace8b8b61aa16f3a6db5d64f1a" dependencies = [ "anyhow", "beef", "serde", "serde_json", "thiserror", - "tracing", ] [[package]] name = "jsonrpsee-ws-client" -version = "0.16.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1b3975ed5d73f456478681a417128597acd6a2487855fdb7b4a3d4d195bf5e" +checksum = "c5ce25d70a8e4d3cc574bbc3cad0137c326ad64b194793d5e7bbdd3fa4504181" dependencies = [ "http", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", + "url", ] [[package]] @@ -7075,6 +7058,7 @@ dependencies = [ "pallet-lottery", "pallet-membership", "pallet-message-queue", + "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", @@ -7087,6 +7071,7 @@ dependencies = [ "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", + "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", @@ -7138,8 +7123,8 @@ dependencies = [ "sp-session", "sp-staking", "sp-statement-store", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "static_assertions", @@ -7347,7 +7332,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project", "quick-protobuf", - "rand 0.8.5", + "rand", "rw-stream-sink", "smallvec", "thiserror", @@ -7403,7 +7388,7 @@ dependencies = [ "multiaddr", "multihash 0.17.0", "quick-protobuf", - "rand 0.8.5", + "rand", "sha2 0.10.7", "thiserror", "zeroize", @@ -7428,7 +7413,7 @@ dependencies = [ "libp2p-swarm", "log", "quick-protobuf", - "rand 0.8.5", + "rand", "sha2 0.10.7", "smallvec", "thiserror", @@ -7450,7 +7435,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "log", - "rand 0.8.5", + "rand", "smallvec", "socket2 0.4.9", "tokio", @@ -7486,7 +7471,7 @@ dependencies = [ "log", "once_cell", "quick-protobuf", - "rand 0.8.5", + "rand", "sha2 0.10.7", "snow", "static_assertions", @@ -7508,7 +7493,7 @@ dependencies = [ "libp2p-core", "libp2p-swarm", "log", - "rand 0.8.5", + "rand", "void", ] @@ -7528,7 +7513,7 @@ dependencies = [ "log", "parking_lot 0.12.1", "quinn-proto", - "rand 0.8.5", + "rand", "rustls 0.20.8", "thiserror", "tokio", @@ -7546,7 +7531,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "rand 0.8.5", + "rand", "smallvec", ] @@ -7565,7 +7550,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm-derive", "log", - "rand 0.8.5", + "rand", "smallvec", "tokio", "void", @@ -7691,7 +7676,7 @@ dependencies = [ "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand 0.8.5", + "rand", "serde", "sha2 0.9.9", "typenum", @@ -7705,7 +7690,7 @@ checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ "crunchy", "digest 0.9.0", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -7784,9 +7769,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" @@ -7900,6 +7885,15 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "macro_magic" version = "0.5.0" @@ -7909,7 +7903,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -7923,7 +7917,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -7934,7 +7928,7 @@ checksum = "9ea73aa640dc01d62a590d48c0c3521ed739d53b27f919b25c3551e233481654" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -7945,7 +7939,7 @@ checksum = "ef9d79ae96aaba821963320eb2b6e34d17df1e5a83d8a1985c29cc5be59577b3" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -7969,6 +7963,15 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matches" version = "0.1.10" @@ -8054,18 +8057,6 @@ dependencies = [ "hash-db", ] -[[package]] -name = "merlin" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.5.1", - "zeroize", -] - [[package]] name = "merlin" version = "3.0.0" @@ -8085,24 +8076,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" dependencies = [ "futures", - "rand 0.8.5", + "rand", "thrift", ] -[[package]] -name = "milagro_bls" -version = "1.5.0" -source = "git+https://github.com/snowfork/milagro_bls?rev=a6d66e4eb89015e352fb1c9f7b661ecdbb5b2176#a6d66e4eb89015e352fb1c9f7b661ecdbb5b2176" -dependencies = [ - "amcl", - "hex", - "lazy_static", - "parity-scale-codec", - "rand 0.8.5", - "scale-info", - "zeroize", -] - [[package]] name = "mime" version = "0.3.17" @@ -8116,15 +8093,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "minimal-node" -version = "4.0.0-dev" +name = "minimal-template-node" +version = "0.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "frame", "futures", "futures-timer", "jsonrpsee", - "minimal-runtime", + "minimal-template-runtime", "sc-basic-authorship", "sc-cli", "sc-client-api", @@ -8151,12 +8128,12 @@ dependencies = [ ] [[package]] -name = "minimal-runtime" -version = "0.1.0" +name = "minimal-template-runtime" +version = "0.0.0" dependencies = [ "frame", - "frame-support", "pallet-balances", + "pallet-minimal-template", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", @@ -8178,9 +8155,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -8198,23 +8175,23 @@ dependencies = [ "bitflags 1.3.2", "blake2 0.10.6", "c2-chacha", - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "either", "hashlink", "lioness", "log", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "rand_distr", - "subtle 2.4.1", + "subtle 2.5.0", "thiserror", "zeroize", ] [[package]] name = "mmr-gadget" -version = "4.0.0-dev" +version = "29.0.0" dependencies = [ "futures", "log", @@ -8230,16 +8207,15 @@ dependencies = [ "sp-core", "sp-mmr-primitives", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-test-runtime-client", "tokio", ] [[package]] name = "mmr-rpc" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ - "anyhow", "jsonrpsee", "parity-scale-codec", "serde", @@ -8278,12 +8254,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "multi-stash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a9ac4b61f4e728e1d2c6a7844609c16527aeb5e6c865915c08e619c16410f" - [[package]] name = "multiaddr" version = "0.17.1" @@ -8467,7 +8437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" dependencies = [ "clap 3.2.25", - "rand 0.8.5", + "rand", ] [[package]] @@ -8578,6 +8548,12 @@ dependencies = [ "libc", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "no-std-net" version = "0.6.0" @@ -8589,7 +8565,7 @@ name = "node-bench" version = "0.9.0-dev" dependencies = [ "array-bytes 6.1.0", - "clap 4.4.18", + "clap 4.5.1", "derive_more", "fs_extra", "futures", @@ -8602,7 +8578,7 @@ dependencies = [ "node-primitives", "node-testing", "parity-db", - "rand 0.8.5", + "rand", "sc-basic-authorship", "sc-client-api", "sc-transaction-pool", @@ -8615,7 +8591,7 @@ dependencies = [ "sp-runtime", "sp-state-machine", "sp-timestamp", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-trie", "tempfile", ] @@ -8666,60 +8642,16 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "generate-bags", "kitchensink-runtime", ] -[[package]] -name = "node-template" -version = "4.0.0-dev" -dependencies = [ - "clap 4.4.18", - "frame-benchmarking", - "frame-benchmarking-cli", - "frame-system", - "futures", - "jsonrpsee", - "node-template-runtime", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc", - "sc-basic-authorship", - "sc-cli", - "sc-client-api", - "sc-consensus", - "sc-consensus-aura", - "sc-consensus-grandpa", - "sc-executor", - "sc-network", - "sc-offchain", - "sc-rpc-api", - "sc-service", - "sc-telemetry", - "sc-transaction-pool", - "sc-transaction-pool-api", - "serde_json", - "sp-api", - "sp-block-builder", - "sp-blockchain", - "sp-consensus-aura", - "sp-consensus-grandpa", - "sp-core", - "sp-inherents", - "sp-io", - "sp-keyring", - "sp-runtime", - "sp-timestamp", - "substrate-build-script-utils", - "substrate-frame-rpc-system", - "try-runtime-cli", -] - [[package]] name = "node-template-release" version = "3.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "flate2", "fs_extra", "glob", @@ -8729,45 +8661,6 @@ dependencies = [ "toml_edit 0.19.15", ] -[[package]] -name = "node-template-runtime" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-executive", - "frame-support", - "frame-system", - "frame-system-benchmarking", - "frame-system-rpc-runtime-api", - "frame-try-runtime", - "pallet-aura", - "pallet-balances", - "pallet-grandpa", - "pallet-sudo", - "pallet-template", - "pallet-timestamp", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc-runtime-api", - "parity-scale-codec", - "scale-info", - "serde_json", - "sp-api", - "sp-block-builder", - "sp-consensus-aura", - "sp-consensus-grandpa", - "sp-core", - "sp-genesis-builder", - "sp-inherents", - "sp-offchain", - "sp-runtime", - "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "sp-transaction-pool", - "sp-version", - "substrate-wasm-builder", -] - [[package]] name = "node-testing" version = "3.0.0-dev" @@ -8795,6 +8688,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-core", + "sp-crypto-hashing", "sp-inherents", "sp-io", "sp-keyring", @@ -8827,12 +8721,28 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num" version = "0.4.1" @@ -8867,17 +8777,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "num-format" version = "0.4.4" @@ -8979,9 +8878,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -9015,9 +8914,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orchestra" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5edee0c1917703f8a28cd229cf6a5c91a7ee34be139ced16509ac5b53b9d0c51" +checksum = "2356622ffdfe72362a45a1e5e87bb113b8327e596e39b91f11f0ef4395c8da79" dependencies = [ "async-trait", "dyn-clonable", @@ -9032,12 +8931,12 @@ dependencies = [ [[package]] name = "orchestra-proc-macro" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f60e64a3808b5bb2786b9da09fc70714952aabcdd0eeba6f1718e3dbc34ad5b" +checksum = "eedb646674596266dc9bb2b5c7eea7c36b32ecc7777eba0d510196972d72c4fd" dependencies = [ "expander 2.0.0", - "indexmap 2.0.0", + "indexmap 2.2.3", "itertools 0.11.0", "petgraph", "proc-macro-crate 1.3.1", @@ -9061,6 +8960,12 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "3.5.0" @@ -9069,7 +8974,7 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "pallet-alliance" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "array-bytes 6.1.0", "frame-benchmarking", @@ -9082,15 +8987,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core", - "sp-core-hashing", + "sp-crypto-hashing", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-asset-conversion" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9105,13 +9010,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-asset-conversion-tx-payment" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-asset-conversion", @@ -9123,13 +9029,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", ] [[package]] name = "pallet-asset-rate" -version = "4.0.0-dev" +version = "7.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9140,12 +9046,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-asset-tx-payment" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9161,13 +9067,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", ] [[package]] name = "pallet-assets" -version = "4.0.0-dev" +version = "29.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9179,12 +9085,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-atomic-swap" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-support", "frame-system", @@ -9194,12 +9100,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-aura" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-support", "frame-system", @@ -9212,12 +9118,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-authority-discovery" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-support", "frame-system", @@ -9229,12 +9135,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-authorship" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-support", "frame-system", @@ -9244,12 +9150,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-babe" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -9272,15 +9178,15 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-bags-list" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ - "aquamarine", - "docify", + "aquamarine 0.5.0", + "docify 0.2.7", "frame-benchmarking", "frame-election-provider-support", "frame-support", @@ -9292,8 +9198,8 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", ] [[package]] @@ -9303,7 +9209,7 @@ dependencies = [ "frame-election-provider-support", "honggfuzz", "pallet-bags-list", - "rand 0.8.5", + "rand", ] [[package]] @@ -9319,15 +9225,16 @@ dependencies = [ "pallet-staking", "sp-core", "sp-runtime", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", + "sp-tracing 16.0.0", ] [[package]] name = "pallet-balances" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -9339,12 +9246,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-beefy" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-election-provider-support", "frame-support", @@ -9367,12 +9274,12 @@ dependencies = [ "sp-session", "sp-staking", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-beefy-mmr" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "array-bytes 6.1.0", "binary-merkle-tree", @@ -9392,12 +9299,12 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-bounties" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9410,12 +9317,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-bridge-grandpa" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-header-chain", "bp-runtime", @@ -9431,13 +9338,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", ] [[package]] name = "pallet-bridge-messages" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-messages", "bp-runtime", @@ -9452,12 +9359,12 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-bridge-parachains" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-header-chain", "bp-parachains", @@ -9474,13 +9381,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", ] [[package]] name = "pallet-bridge-relayers" -version = "0.1.0" +version = "0.7.0" dependencies = [ "bp-messages", "bp-relayers", @@ -9496,12 +9403,12 @@ dependencies = [ "sp-arithmetic", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-broker" -version = "0.1.0" +version = "0.6.0" dependencies = [ "bitvec", "frame-benchmarking", @@ -9513,12 +9420,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-child-bounties" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9532,12 +9439,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-collator-selection" -version = "3.0.0" +version = "9.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9549,20 +9456,20 @@ dependencies = [ "pallet-session", "pallet-timestamp", "parity-scale-codec", - "rand 0.8.5", + "rand", "scale-info", "sp-consensus-aura", "sp-core", "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", ] [[package]] name = "pallet-collective" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9573,12 +9480,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-collective-content" -version = "0.1.0" +version = "0.6.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9588,12 +9495,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-contracts" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "array-bytes 6.1.0", "assert_matches", @@ -9617,7 +9524,7 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "pretty_assertions", - "rand 0.8.5", + "rand", "rand_pcg", "scale-info", "serde", @@ -9627,12 +9534,12 @@ dependencies = [ "sp-io", "sp-keystore", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-builder", - "wasm-instrument 0.4.0", - "wasmi 0.32.0-beta.5", + "wasm-instrument", + "wasmi", "wat", ] @@ -9643,17 +9550,16 @@ dependencies = [ "anyhow", "frame-system", "parity-wasm", - "polkavm-linker", + "polkavm-linker 0.5.0", "sp-runtime", "tempfile", "toml 0.8.8", "twox-hash", - "wat", ] [[package]] name = "pallet-contracts-mock-network" -version = "1.0.0" +version = "3.0.0" dependencies = [ "assert_matches", "frame-support", @@ -9681,8 +9587,8 @@ dependencies = [ "sp-io", "sp-keystore", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -9691,27 +9597,27 @@ dependencies = [ [[package]] name = "pallet-contracts-proc-macro" -version = "4.0.0-dev" +version = "18.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "pallet-contracts-uapi" -version = "4.0.0-dev" +version = "5.0.0" dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive", + "polkavm-derive 0.5.0", "scale-info", ] [[package]] name = "pallet-conviction-voting" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "assert_matches", "frame-benchmarking", @@ -9725,29 +9631,30 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-core-fellowship" -version = "4.0.0-dev" +version = "12.0.0" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "log", + "pallet-ranked-collective", "parity-scale-codec", "scale-info", "sp-arithmetic", "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-default-config-example" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "frame-support", "frame-system", @@ -9756,12 +9663,12 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-democracy" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9776,12 +9683,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-dev-mode" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "frame-support", "frame-system", @@ -9792,7 +9699,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -9806,6 +9713,7 @@ dependencies = [ "pallet-bags-list", "pallet-balances", "pallet-election-provider-multi-phase", + "pallet-nomination-pools", "pallet-session", "pallet-staking", "pallet-timestamp", @@ -9817,13 +9725,13 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", ] [[package]] name = "pallet-election-provider-multi-phase" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -9834,21 +9742,21 @@ dependencies = [ "pallet-election-provider-support-benchmarking", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "scale-info", "sp-arithmetic", "sp-core", "sp-io", "sp-npos-elections", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "strum 0.24.1", ] [[package]] name = "pallet-election-provider-support-benchmarking" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -9856,12 +9764,12 @@ dependencies = [ "parity-scale-codec", "sp-npos-elections", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-elections-phragmen" -version = "5.0.0-dev" +version = "29.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9875,14 +9783,14 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "substrate-test-utils", ] [[package]] name = "pallet-example-basic" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9894,7 +9802,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -9920,12 +9828,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-example-offchain-worker" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-support", "frame-system", @@ -9937,12 +9845,32 @@ dependencies = [ "sp-io", "sp-keystore", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", +] + +[[package]] +name = "pallet-example-single-block-migrations" +version = "0.0.1" +dependencies = [ + "docify 0.2.7", + "frame-executive", + "frame-support", + "frame-system", + "frame-try-runtime", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "sp-version", ] [[package]] name = "pallet-example-split" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9952,12 +9880,12 @@ dependencies = [ "scale-info", "sp-core", "sp-io", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-example-tasks" -version = "1.0.0-dev" +version = "1.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9968,7 +9896,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -9981,15 +9909,16 @@ dependencies = [ "pallet-example-frame-crate", "pallet-example-kitchensink", "pallet-example-offchain-worker", + "pallet-example-single-block-migrations", "pallet-example-split", "pallet-example-tasks", ] [[package]] name = "pallet-fast-unstake" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-election-provider-support", "frame-support", @@ -10005,14 +9934,14 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "substrate-test-utils", ] [[package]] name = "pallet-glutton" -version = "4.0.0-dev" +version = "14.0.0" dependencies = [ "blake2 0.10.6", "frame-benchmarking", @@ -10025,12 +9954,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-grandpa" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "finality-grandpa", "frame-benchmarking", @@ -10055,12 +9984,12 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-identity" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "enumflags2", "frame-benchmarking", @@ -10074,12 +10003,12 @@ dependencies = [ "sp-io", "sp-keystore", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-im-online" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10094,12 +10023,12 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-indices" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10111,12 +10040,12 @@ dependencies = [ "sp-io", "sp-keyring", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-insecure-randomness-collective-flip" -version = "4.0.0-dev" +version = "16.0.0" dependencies = [ "frame-support", "frame-system", @@ -10126,12 +10055,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-lottery" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10143,12 +10072,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-membership" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10159,12 +10088,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-message-queue" -version = "7.0.0-dev" +version = "31.0.0" dependencies = [ "environmental", "frame-benchmarking", @@ -10172,22 +10101,56 @@ dependencies = [ "frame-system", "log", "parity-scale-codec", - "rand 0.8.5", + "rand", "rand_distr", "scale-info", "serde", "sp-arithmetic", "sp-core", + "sp-crypto-hashing", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "sp-weights", ] +[[package]] +name = "pallet-migrations" +version = "1.0.0" +dependencies = [ + "docify 0.1.16", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "pretty_assertions", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "sp-tracing 16.0.0", + "sp-version", +] + +[[package]] +name = "pallet-minimal-template" +version = "0.0.0" +dependencies = [ + "frame", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "pallet-mixnet" -version = "0.1.0-dev" +version = "0.4.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10201,12 +10164,12 @@ dependencies = [ "sp-io", "sp-mixnet", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-mmr" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "array-bytes 6.1.0", "env_logger 0.9.3", @@ -10221,12 +10184,12 @@ dependencies = [ "sp-io", "sp-mmr-primitives", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-multisig" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10237,12 +10200,12 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-nft-fractionalization" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10256,12 +10219,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-nfts" -version = "4.0.0-dev" +version = "22.0.0" dependencies = [ "enumflags2", "frame-benchmarking", @@ -10275,37 +10238,22 @@ dependencies = [ "sp-io", "sp-keystore", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-nfts-runtime-api" -version = "4.0.0-dev" +version = "14.0.0" dependencies = [ "pallet-nfts", "parity-scale-codec", "sp-api", - "sp-std 8.0.0", -] - -[[package]] -name = "pallet-nicks" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "frame-system", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-nis" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10317,12 +10265,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-node-authorization" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-support", "frame-system", @@ -10332,12 +10280,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-nomination-pools" -version = "1.0.0" +version = "25.0.0" dependencies = [ "frame-support", "frame-system", @@ -10349,13 +10297,13 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", ] [[package]] name = "pallet-nomination-pools-benchmarking" -version = "1.0.0" +version = "26.0.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -10372,9 +10320,9 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-runtime-interface 17.0.0", + "sp-runtime-interface 24.0.0", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -10386,20 +10334,20 @@ dependencies = [ "honggfuzz", "log", "pallet-nomination-pools", - "rand 0.8.5", + "rand", "sp-io", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", ] [[package]] name = "pallet-nomination-pools-runtime-api" -version = "1.0.0-dev" +version = "23.0.0" dependencies = [ "pallet-nomination-pools", "parity-scale-codec", "sp-api", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -10422,13 +10370,13 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", ] [[package]] name = "pallet-offences" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-support", "frame-system", @@ -10441,12 +10389,12 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-offences-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -10468,14 +10416,14 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-paged-list" -version = "0.1.0" +version = "0.6.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10485,7 +10433,7 @@ dependencies = [ "sp-io", "sp-metadata-ir", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -10501,22 +10449,41 @@ dependencies = [ [[package]] name = "pallet-parachain-template" -version = "0.1.0" +version = "0.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + +[[package]] +name = "pallet-parameters" +version = "0.0.1" dependencies = [ + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", + "pallet-balances", + "pallet-example-basic", "parity-scale-codec", + "paste", "scale-info", "serde", "sp-core", "sp-io", "sp-runtime", + "sp-std 14.0.0", ] [[package]] name = "pallet-preimage" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10528,12 +10495,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-proxy" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10545,16 +10512,17 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-ranked-collective" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", @@ -10562,12 +10530,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-recovery" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10578,12 +10546,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-referenda" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "assert_matches", "frame-benchmarking", @@ -10600,12 +10568,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-remark" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10616,12 +10584,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-root-offences" -version = "1.0.0-dev" +version = "25.0.0" dependencies = [ "frame-election-provider-support", "frame-support", @@ -10637,12 +10605,12 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-root-testing" -version = "1.0.0-dev" +version = "4.0.0" dependencies = [ "frame-support", "frame-system", @@ -10651,14 +10619,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-safe-mode" -version = "4.0.0-dev" +version = "9.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10671,24 +10639,25 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-salary" -version = "4.0.0-dev" +version = "13.0.0" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "log", + "pallet-ranked-collective", "parity-scale-codec", "scale-info", "sp-arithmetic", "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -10704,16 +10673,17 @@ dependencies = [ "scale-info", "sp-consensus-sassafras", "sp-core", + "sp-crypto-hashing", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-scheduler" -version = "4.0.0-dev" +version = "29.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10724,14 +10694,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-weights", "substrate-test-utils", ] [[package]] name = "pallet-scored-pool" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-support", "frame-system", @@ -10741,12 +10711,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-session" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-support", "frame-system", @@ -10761,13 +10731,13 @@ dependencies = [ "sp-session", "sp-staking", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", ] [[package]] name = "pallet-session-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -10779,30 +10749,30 @@ dependencies = [ "pallet-staking-reward-curve", "pallet-timestamp", "parity-scale-codec", - "rand 0.8.5", + "rand", "scale-info", "sp-core", "sp-io", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-skip-feeless-payment" -version = "1.0.0-dev" +version = "3.0.0" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-society" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10815,14 +10785,15 @@ dependencies = [ "scale-info", "sp-arithmetic", "sp-core", + "sp-crypto-hashing", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-staking" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -10845,25 +10816,25 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "substrate-test-utils", ] [[package]] name = "pallet-staking-reward-curve" -version = "4.0.0-dev" +version = "11.0.0" dependencies = [ "proc-macro-crate 3.0.0", "proc-macro2", "quote", "sp-runtime", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "pallet-staking-reward-fn" -version = "4.0.0-dev" +version = "19.0.0" dependencies = [ "log", "sp-arithmetic", @@ -10871,7 +10842,7 @@ dependencies = [ [[package]] name = "pallet-staking-runtime-api" -version = "4.0.0-dev" +version = "14.0.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -10880,7 +10851,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" -version = "4.0.0-dev" +version = "29.0.0" dependencies = [ "frame-benchmarking", "frame-remote-externalities", @@ -10895,8 +10866,8 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "substrate-state-trie-migration-rpc", "thousands", "tokio", @@ -10905,7 +10876,7 @@ dependencies = [ [[package]] name = "pallet-statement" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "frame-support", "frame-system", @@ -10918,14 +10889,14 @@ dependencies = [ "sp-io", "sp-runtime", "sp-statement-store", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-sudo" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10934,12 +10905,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-template" -version = "4.0.0-dev" +version = "0.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10949,14 +10920,13 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", ] [[package]] name = "pallet-timestamp" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -10967,14 +10937,14 @@ dependencies = [ "sp-inherents", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-timestamp", ] [[package]] name = "pallet-tips" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -10988,14 +10958,15 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", ] [[package]] name = "pallet-transaction-payment" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-balances", @@ -11006,12 +10977,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-transaction-payment-rpc" -version = "4.0.0-dev" +version = "30.0.0" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -11026,7 +10997,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -11037,7 +11008,7 @@ dependencies = [ [[package]] name = "pallet-transaction-storage" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "array-bytes 6.1.0", "frame-benchmarking", @@ -11052,15 +11023,15 @@ dependencies = [ "sp-inherents", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-storage-proof", ] [[package]] name = "pallet-treasury" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -11073,14 +11044,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-tx-pause" -version = "4.0.0-dev" +version = "9.0.0" dependencies = [ - "docify", + "docify 0.2.7", "frame-benchmarking", "frame-support", "frame-system", @@ -11092,12 +11063,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-uniques" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -11109,12 +11080,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-utility" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -11128,12 +11099,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-vesting" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -11145,12 +11116,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-whitelist" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -11163,12 +11134,12 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "pallet-xcm" -version = "1.0.0" +version = "7.0.0" dependencies = [ "bounded-collections", "frame-benchmarking", @@ -11185,7 +11156,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -11193,7 +11164,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" -version = "1.0.0" +version = "7.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -11209,8 +11180,8 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -11218,7 +11189,7 @@ dependencies = [ [[package]] name = "pallet-xcm-bridge-hub" -version = "0.1.0" +version = "0.2.0" dependencies = [ "bp-header-chain", "bp-messages", @@ -11235,7 +11206,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -11243,7 +11214,7 @@ dependencies = [ [[package]] name = "pallet-xcm-bridge-hub-router" -version = "0.1.0" +version = "0.5.0" dependencies = [ "bp-xcm-bridge-hub-router", "frame-benchmarking", @@ -11255,16 +11226,16 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", ] [[package]] name = "parachain-template-node" -version = "0.1.0" +version = "0.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "color-print", "cumulus-client-cli", "cumulus-client-collator", @@ -11320,15 +11291,15 @@ dependencies = [ [[package]] name = "parachain-template-runtime" -version = "0.1.0" +version = "0.0.0" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", + "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-utility", "frame-benchmarking", "frame-executive", @@ -11366,7 +11337,7 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -11378,14 +11349,13 @@ dependencies = [ [[package]] name = "parachains-common" -version = "1.0.0" +version = "7.0.0" dependencies = [ "cumulus-primitives-core", "cumulus-primitives-utility", "frame-support", "frame-system", "log", - "num-traits", "pallet-asset-tx-payment", "pallet-assets", "pallet-authorship", @@ -11394,29 +11364,23 @@ dependencies = [ "pallet-message-queue", "pallet-xcm", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-primitives", - "rococo-runtime-constants", "scale-info", - "smallvec", "sp-consensus-aura", "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-parachain-info", "staging-xcm", - "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", - "westend-runtime-constants", ] [[package]] name = "parachains-runtimes-test-utils" -version = "1.0.0" +version = "7.0.0" dependencies = [ - "assets-common", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", @@ -11425,20 +11389,19 @@ dependencies = [ "frame-support", "frame-system", "hex-literal", - "pallet-assets", "pallet-balances", "pallet-collator-selection", "pallet-session", + "pallet-timestamp", "pallet-xcm", - "parachains-common", "parity-scale-codec", "polkadot-parachain-primitives", "sp-consensus-aura", "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-parachain-info", "staging-xcm", "staging-xcm-executor", @@ -11466,7 +11429,7 @@ dependencies = [ "lz4", "memmap2 0.5.10", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "siphasher", "snap", ] @@ -11600,19 +11563,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7924d1d0ad836f665c9065e26d016c673ece3993f30d340068b16f282afc1156" [[package]] -name = "paste" -version = "1.0.14" +name = "password-hash" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle 2.5.0", +] [[package]] -name = "pbkdf2" -version = "0.8.0" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" -dependencies = [ - "crypto-mac 0.11.1", -] +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -11621,6 +11586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", + "password-hash", ] [[package]] @@ -11648,19 +11614,16 @@ dependencies = [ "parachains-common", "penpal-runtime", "rococo-emulated-chain", - "serde_json", "sp-core", - "sp-runtime", "westend-emulated-chain", ] [[package]] name = "penpal-runtime" -version = "0.9.27" +version = "0.14.0" dependencies = [ "assets-common", "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", @@ -11705,8 +11668,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -11725,31 +11688,23 @@ dependencies = [ "frame-support", "parachains-common", "people-rococo-runtime", - "rococo-emulated-chain", - "serde_json", "sp-core", - "sp-runtime", + "testnet-parachains-constants", ] [[package]] name = "people-rococo-integration-tests" version = "0.1.0" dependencies = [ - "assert_matches", "asset-test-utils", "emulated-integration-tests-common", "frame-support", - "pallet-asset-conversion", - "pallet-assets", "pallet-balances", "pallet-identity", "pallet-message-queue", - "pallet-xcm", "parachains-common", "parity-scale-codec", - "penpal-runtime", "people-rococo-runtime", - "polkadot-primitives", "polkadot-runtime-common", "rococo-runtime", "rococo-runtime-constants", @@ -11764,11 +11719,11 @@ name = "people-rococo-runtime" version = "0.1.0" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "enumflags2", @@ -11797,13 +11752,11 @@ dependencies = [ "pallet-xcm-benchmarks", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "rococo-runtime-constants", "scale-info", "serde", - "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -11813,8 +11766,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -11822,6 +11775,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", ] [[package]] @@ -11833,31 +11787,23 @@ dependencies = [ "frame-support", "parachains-common", "people-westend-runtime", - "serde_json", "sp-core", - "sp-runtime", - "westend-emulated-chain", + "testnet-parachains-constants", ] [[package]] name = "people-westend-integration-tests" version = "0.1.0" dependencies = [ - "assert_matches", "asset-test-utils", "emulated-integration-tests-common", "frame-support", - "pallet-asset-conversion", - "pallet-assets", "pallet-balances", "pallet-identity", "pallet-message-queue", - "pallet-xcm", "parachains-common", "parity-scale-codec", - "penpal-runtime", "people-westend-runtime", - "polkadot-primitives", "polkadot-runtime-common", "sp-runtime", "staging-xcm", @@ -11872,11 +11818,11 @@ name = "people-westend-runtime" version = "0.1.0" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", "enumflags2", @@ -11905,12 +11851,10 @@ dependencies = [ "pallet-xcm-benchmarks", "parachains-common", "parity-scale-codec", - "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", "scale-info", "serde", - "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -11920,8 +11864,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -11929,6 +11873,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", "westend-runtime-constants", ] @@ -11968,7 +11913,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -11989,7 +11934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.0", + "indexmap 2.2.3", ] [[package]] @@ -12009,7 +11954,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -12082,7 +12027,7 @@ dependencies = [ [[package]] name = "polkadot" -version = "1.6.0" +version = "6.0.0" dependencies = [ "assert_cmd", "color-eyre", @@ -12103,7 +12048,7 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "bitvec", @@ -12121,7 +12066,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "rand_core 0.6.4", "schnorrkel 0.11.4", @@ -12132,7 +12077,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" -version = "1.0.0" +version = "7.0.0" dependencies = [ "always-assert", "assert_matches", @@ -12147,7 +12092,7 @@ dependencies = [ "polkadot-node-subsystem-test-helpers", "polkadot-node-subsystem-util", "polkadot-primitives", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "sp-application-crypto", "sp-authority-discovery", @@ -12159,7 +12104,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "derive_more", @@ -12175,20 +12120,21 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", - "rand 0.8.5", + "polkadot-subsystem-bench", + "rand", "sc-network", "schnellru", "sp-core", "sp-keyring", "sp-keystore", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-availability-recovery" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-trait", @@ -12206,7 +12152,8 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", - "rand 0.8.5", + "polkadot-subsystem-bench", + "rand", "sc-network", "schnellru", "sp-application-crypto", @@ -12219,10 +12166,10 @@ dependencies = [ [[package]] name = "polkadot-cli" -version = "1.1.0" +version = "7.0.0" dependencies = [ "cfg-if", - "clap 4.4.18", + "clap 4.5.1", "frame-benchmarking-cli", "futures", "log", @@ -12241,6 +12188,7 @@ dependencies = [ "sp-io", "sp-keyring", "sp-maybe-compressed-blob", + "sp-runtime", "substrate-build-script-utils", "thiserror", "try-runtime-cli", @@ -12248,7 +12196,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "bitvec", @@ -12278,18 +12226,18 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" -version = "1.0.0" +version = "7.0.0" dependencies = [ "parity-scale-codec", "scale-info", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "polkadot-dispute-distribution" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-channel", @@ -12298,7 +12246,7 @@ dependencies = [ "fatality", "futures", "futures-timer", - "indexmap 2.0.0", + "indexmap 2.2.3", "lazy_static", "parity-scale-codec", "polkadot-erasure-coding", @@ -12315,14 +12263,14 @@ dependencies = [ "sp-application-crypto", "sp-keyring", "sp-keystore", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-erasure-coding" -version = "1.0.0" +version = "7.0.0" dependencies = [ "criterion 0.4.0", "parity-scale-codec", @@ -12336,20 +12284,21 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-trait", "futures", "futures-timer", "lazy_static", + "parking_lot 0.12.1", "polkadot-node-network-protocol", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", "polkadot-node-subsystem-util", "polkadot-primitives", "quickcheck", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "sc-network", "sc-network-common", @@ -12357,15 +12306,16 @@ dependencies = [ "sp-authority-discovery", "sp-consensus-babe", "sp-core", + "sp-crypto-hashing", "sp-keyring", "sp-keystore", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "tracing-gum", ] [[package]] name = "polkadot-network-bridge" -version = "1.0.0" +version = "7.0.0" dependencies = [ "always-assert", "assert_matches", @@ -12394,7 +12344,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "futures", @@ -12415,7 +12365,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-trait", @@ -12428,7 +12378,7 @@ dependencies = [ "kvdb", "kvdb-memorydb", "log", - "merlin 3.0.0", + "merlin", "parity-scale-codec", "parking_lot 0.12.1", "polkadot-node-jaeger", @@ -12439,7 +12389,7 @@ dependencies = [ "polkadot-overseer", "polkadot-primitives", "polkadot-primitives-test-helpers", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "rand_core 0.6.4", "sc-keystore", @@ -12459,7 +12409,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "bitvec", @@ -12489,7 +12439,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "bitvec", @@ -12503,19 +12453,21 @@ dependencies = [ "polkadot-primitives", "polkadot-primitives-test-helpers", "polkadot-statement-table", + "rstest", "sc-keystore", + "schnellru", "sp-application-crypto", "sp-core", "sp-keyring", "sp-keystore", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-node-core-bitfield-signing" -version = "1.0.0" +version = "7.0.0" dependencies = [ "futures", "polkadot-node-subsystem", @@ -12531,7 +12483,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-trait", @@ -12556,7 +12508,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" -version = "1.0.0" +version = "7.0.0" dependencies = [ "futures", "maplit", @@ -12576,7 +12528,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "futures", @@ -12597,7 +12549,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "fatality", @@ -12618,14 +12570,14 @@ dependencies = [ "sp-core", "sp-keyring", "sp-keystore", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-node-core-parachains-inherent" -version = "1.0.0" +version = "7.0.0" dependencies = [ "async-trait", "futures", @@ -12641,7 +12593,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-prospective-parachains" -version = "1.0.0" +version = "6.0.0" dependencies = [ "assert_matches", "bitvec", @@ -12655,6 +12607,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rstest", "sc-keystore", "sp-application-crypto", "sp-core", @@ -12666,7 +12619,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" -version = "1.0.0" +version = "7.0.0" dependencies = [ "bitvec", "fatality", @@ -12678,6 +12631,8 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rstest", + "schnellru", "sp-application-crypto", "sp-keystore", "thiserror", @@ -12686,7 +12641,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" -version = "1.0.0" +version = "7.0.0" dependencies = [ "always-assert", "array-bytes 6.1.0", @@ -12712,14 +12667,14 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-primitives", "procfs", - "rand 0.8.5", + "rand", "rococo-runtime", "rusty-fork", "sc-sysinfo", "slotmap", "sp-core", "sp-maybe-compressed-blob", - "sp-wasm-interface 14.0.0", + "sp-wasm-interface 20.0.0", "tempfile", "test-parachain-adder", "test-parachain-halt", @@ -12730,7 +12685,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" -version = "1.0.0" +version = "7.0.0" dependencies = [ "futures", "futures-timer", @@ -12753,7 +12708,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-common" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "cfg-if", @@ -12770,9 +12725,10 @@ dependencies = [ "sc-executor-wasmtime", "seccompiler", "sp-core", - "sp-externalities 0.19.0", + "sp-crypto-hashing", + "sp-externalities 0.25.0", "sp-io", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "tempfile", "thiserror", "tracing-gum", @@ -12780,7 +12736,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-execute-worker" -version = "1.0.0" +version = "7.0.0" dependencies = [ "cfg-if", "cpu-time", @@ -12795,7 +12751,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-prepare-worker" -version = "1.0.0" +version = "7.0.0" dependencies = [ "blake3", "cfg-if", @@ -12818,7 +12774,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" -version = "1.0.0" +version = "7.0.0" dependencies = [ "async-trait", "futures", @@ -12839,7 +12795,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" -version = "1.0.0" +version = "7.0.0" dependencies = [ "lazy_static", "log", @@ -12856,7 +12812,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_cmd", "bs58 0.5.0", @@ -12882,7 +12838,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" -version = "1.0.0" +version = "7.0.0" dependencies = [ "async-channel", "async-trait", @@ -12895,7 +12851,7 @@ dependencies = [ "polkadot-node-jaeger", "polkadot-node-primitives", "polkadot-primitives", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "sc-authority-discovery", "sc-network", @@ -12906,7 +12862,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" -version = "1.0.0" +version = "7.0.0" dependencies = [ "bitvec", "bounded-vec", @@ -12929,7 +12885,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" -version = "1.0.0" +version = "7.0.0" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -12959,7 +12915,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" -version = "1.0.0" +version = "7.0.0" dependencies = [ "async-trait", "bitvec", @@ -12986,7 +12942,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-trait", @@ -13016,7 +12972,7 @@ dependencies = [ "polkadot-primitives", "polkadot-primitives-test-helpers", "prioritized-metered-channel", - "rand 0.8.5", + "rand", "sc-client-api", "schnellru", "sp-application-crypto", @@ -13029,7 +12985,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-trait", @@ -13055,7 +13011,7 @@ dependencies = [ [[package]] name = "polkadot-parachain-bin" -version = "1.6.0" +version = "4.0.0" dependencies = [ "assert_cmd", "asset-hub-rococo-runtime", @@ -13063,7 +13019,7 @@ dependencies = [ "async-trait", "bridge-hub-rococo-runtime", "bridge-hub-westend-runtime", - "clap 4.4.18", + "clap 4.5.1", "collectives-westend-runtime", "color-print", "contracts-rococo-runtime", @@ -13134,9 +13090,9 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-timestamp", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-transaction-pool", "sp-version", "staging-xcm", @@ -13145,13 +13101,14 @@ dependencies = [ "substrate-prometheus-endpoint", "substrate-state-trie-migration-rpc", "tempfile", + "testnet-parachains-constants", "tokio", "wait-timeout", ] [[package]] name = "polkadot-parachain-primitives" -version = "1.0.0" +version = "6.0.0" dependencies = [ "bounded-collections", "derive_more", @@ -13161,16 +13118,17 @@ dependencies = [ "serde", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-weights", ] [[package]] name = "polkadot-primitives" -version = "1.0.0" +version = "7.0.0" dependencies = [ "bitvec", "hex-literal", + "log", "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", @@ -13187,7 +13145,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -13195,7 +13153,7 @@ name = "polkadot-primitives-test-helpers" version = "1.0.0" dependencies = [ "polkadot-primitives", - "rand 0.8.5", + "rand", "sp-application-crypto", "sp-core", "sp-keyring", @@ -13204,7 +13162,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" -version = "1.0.0" +version = "7.0.0" dependencies = [ "jsonrpsee", "mmr-rpc", @@ -13236,7 +13194,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" -version = "1.0.0" +version = "7.0.0" dependencies = [ "bitvec", "frame-benchmarking", @@ -13263,7 +13221,6 @@ dependencies = [ "pallet-transaction-payment", "pallet-treasury", "pallet-vesting", - "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-primitives", "polkadot-primitives-test-helpers", @@ -13284,7 +13241,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -13293,19 +13250,19 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" -version = "1.0.0" +version = "7.0.0" dependencies = [ "bs58 0.5.0", "frame-benchmarking", "parity-scale-codec", "polkadot-primitives", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", ] [[package]] name = "polkadot-runtime-parachains" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "bitflags 1.3.2", @@ -13335,8 +13292,9 @@ dependencies = [ "polkadot-primitives", "polkadot-primitives-test-helpers", "polkadot-runtime-metrics", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", + "rstest", "rustc-hex", "sc-keystore", "scale-info", @@ -13346,6 +13304,7 @@ dependencies = [ "sp-application-crypto", "sp-arithmetic", "sp-core", + "sp-crypto-hashing", "sp-inherents", "sp-io", "sp-keyring", @@ -13353,8 +13312,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-executor", "static_assertions", @@ -13367,13 +13326,28 @@ version = "0.0.1" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", - "docify", + "docify 0.2.7", "frame", + "frame-executive", + "frame-support", + "frame-system", "kitchensink-runtime", + "pallet-assets", "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collective", "pallet-default-config-example", + "pallet-democracy", + "pallet-example-offchain-worker", + "pallet-example-single-block-migrations", "pallet-examples", + "pallet-multisig", + "pallet-proxy", + "pallet-scheduler", "pallet-timestamp", + "pallet-transaction-payment", + "pallet-utility", "parity-scale-codec", "sc-cli", "sc-client-db", @@ -13392,20 +13366,24 @@ dependencies = [ "sp-core", "sp-io", "sp-keyring", + "sp-offchain", "sp-runtime", + "sp-version", "staging-chain-spec-builder", "staging-node-cli", "staging-parachain-info", + "staging-xcm", "subkey", "substrate-wasm-builder", ] [[package]] name = "polkadot-service" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "async-trait", + "bitvec", "env_logger 0.9.3", "frame-benchmarking", "frame-benchmarking-cli", @@ -13512,7 +13490,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-state-machine", - "sp-storage 13.0.0", + "sp-storage 19.0.0", "sp-timestamp", "sp-transaction-pool", "sp-version", @@ -13527,7 +13505,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" -version = "1.0.0" +version = "7.0.0" dependencies = [ "arrayvec 0.7.4", "assert_matches", @@ -13536,7 +13514,7 @@ dependencies = [ "fatality", "futures", "futures-timer", - "indexmap 2.0.0", + "indexmap 2.2.3", "parity-scale-codec", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -13554,18 +13532,19 @@ dependencies = [ "sp-keyring", "sp-keystore", "sp-staking", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-statement-table" -version = "1.0.0" +version = "7.0.0" dependencies = [ "parity-scale-codec", "polkadot-primitives", "sp-core", + "tracing-gum", ] [[package]] @@ -13574,20 +13553,30 @@ version = "1.0.0" dependencies = [ "assert_matches", "async-trait", - "clap 4.4.18", + "bincode", + "bitvec", + "clap 4.5.1", "clap-num", "color-eyre", "colored", "env_logger 0.9.3", "futures", "futures-timer", + "hex", "itertools 0.11.0", + "kvdb-memorydb", "log", "orchestra", "parity-scale-codec", "paste", + "polkadot-approval-distribution", + "polkadot-availability-bitfield-distribution", + "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-erasure-coding", + "polkadot-node-core-approval-voting", + "polkadot-node-core-av-store", + "polkadot-node-core-chain-api", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -13601,16 +13590,25 @@ dependencies = [ "prometheus", "pyroscope", "pyroscope_pprofrs", - "rand 0.8.5", + "rand", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "rand_distr", "sc-keystore", "sc-network", "sc-service", + "schnorrkel 0.11.4", "serde", "serde_yaml", + "sha1", "sp-application-crypto", + "sp-consensus", + "sp-consensus-babe", "sp-core", "sp-keyring", "sp-keystore", + "sp-runtime", + "sp-timestamp", "substrate-prometheus-endpoint", "tokio", "tracing-gum", @@ -13651,7 +13649,7 @@ version = "1.0.0" dependencies = [ "assert_matches", "async-trait", - "clap 4.4.18", + "clap 4.5.1", "color-eyre", "futures", "futures-timer", @@ -13669,7 +13667,7 @@ dependencies = [ "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-primitives", - "rand 0.8.5", + "rand", "sp-core", "sp-keystore", "substrate-build-script-utils", @@ -13730,7 +13728,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-pool", "sp-trie", "sp-version", @@ -13762,7 +13760,7 @@ dependencies = [ "polkadot-runtime-parachains", "polkadot-service", "polkadot-test-runtime", - "rand 0.8.5", + "rand", "sc-authority-discovery", "sc-chain-spec", "sc-cli", @@ -13796,9 +13794,9 @@ dependencies = [ [[package]] name = "polkadot-voter-bags" -version = "1.0.0" +version = "7.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "generate-bags", "sp-io", "westend-runtime", @@ -13806,36 +13804,67 @@ dependencies = [ [[package]] name = "polkavm-common" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecd2caacfc4a7ee34243758dd7348859e6dec73f5e5df059890f5742ee46f0e" +checksum = "88b4e215c80fe876147f3d58158d5dfeae7dabdd6047e175af77095b78d0035c" [[package]] name = "polkavm-common" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c99f7eee94e7be43ba37eef65ad0ee8cbaf89b7c00001c3f6d2be985cb1817" + +[[package]] +name = "polkavm-derive" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b4e215c80fe876147f3d58158d5dfeae7dabdd6047e175af77095b78d0035c" +checksum = "6380dbe1fb03ecc74ad55d841cfc75480222d153ba69ddcb00977866cbdabdb8" +dependencies = [ + "polkavm-derive-impl 0.5.0", + "syn 2.0.50", +] [[package]] name = "polkavm-derive" -version = "0.4.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db65a500d4adf574893c726ae365e37e4fbb7f2cbd403f6eaa1b665457456adc" +checksum = "79fa916f7962348bd1bb1a65a83401675e6fc86c51a0fdbcf92a3108e58e6125" dependencies = [ - "polkavm-derive-impl", - "syn 2.0.48", + "polkavm-derive-impl-macro", ] [[package]] name = "polkavm-derive-impl" -version = "0.4.0" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc8211b3365bbafb2fb32057d68b0e1ca55d079f5cf6f9da9b98079b94b3987d" +dependencies = [ + "polkavm-common 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99f4e7a9ff434ef9c885b874c99d824c3a5693bf5e3e8569bb1d2245a8c1b7f" +checksum = "c10b2654a8a10a83c260bfb93e97b262cf0017494ab94a65d389e0eda6de6c9c" dependencies = [ - "polkavm-common 0.4.0", + "polkavm-common 0.8.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e85319a0d5129dc9f021c62607e0804f5fb777a05cdda44d750ac0732def66" +dependencies = [ + "polkavm-derive-impl 0.8.0", + "syn 2.0.50", ] [[package]] @@ -13853,6 +13882,21 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "polkavm-linker" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdec1451cb18261d5d01de82acc15305e417fb59588cdcb3127d3dcc9672b925" +dependencies = [ + "gimli 0.28.0", + "hashbrown 0.14.3", + "log", + "object 0.32.2", + "polkavm-common 0.8.0", + "regalloc2 0.9.3", + "rustc-demangle", +] + [[package]] name = "polling" version = "2.8.0" @@ -13877,7 +13921,7 @@ checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "universal-hash 0.4.0", ] [[package]] @@ -13900,7 +13944,7 @@ dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "universal-hash 0.4.0", ] [[package]] @@ -13927,7 +13971,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9" dependencies = [ - "rand 0.8.5", + "rand", ] [[package]] @@ -14025,7 +14069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -14116,7 +14160,7 @@ checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -14140,7 +14184,7 @@ dependencies = [ "hex", "lazy_static", "procfs-core", - "rustix 0.38.21", + "rustix 0.38.25", ] [[package]] @@ -14188,7 +14232,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -14214,7 +14258,7 @@ dependencies = [ "bitflags 2.4.0", "lazy_static", "num-traits", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax 0.8.2", @@ -14288,7 +14332,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -14339,6 +14383,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "quanta" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +dependencies = [ + "crossbeam-utils", + "libc", + "mach2", + "once_cell", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -14375,7 +14435,7 @@ checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "env_logger 0.8.4", "log", - "rand 0.8.5", + "rand", ] [[package]] @@ -14396,7 +14456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c956be1b23f4261676aed05a0046e204e8a6836e50203902683a718af0797989" dependencies = [ "bytes", - "rand 0.8.5", + "rand", "ring 0.16.20", "rustc-hash", "rustls 0.20.8", @@ -14422,19 +14482,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -14491,16 +14538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "rand", ] [[package]] @@ -14521,6 +14559,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -14601,14 +14648,13 @@ dependencies = [ [[package]] name = "reed-solomon-novelpoly" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd8f48b2066e9f69ab192797d66da804d1935bf22763204ed3675740cb0f221" +checksum = "87413ebb313323d431e85d0afc5a68222aaed972843537cbfe5f061cf1b4bcab" dependencies = [ "derive_more", "fs-err", - "itertools 0.10.5", - "static_init 0.5.2", + "static_init", "thiserror", ] @@ -14629,7 +14675,7 @@ checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -14707,16 +14753,22 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "relative-path" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" + [[package]] name = "remote-ext-tests-bags-list" version = "1.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "frame-system", "log", "pallet-bags-list-remote-tests", "sp-core", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "tokio", "westend-runtime", "westend-runtime-constants", @@ -14746,12 +14798,12 @@ dependencies = [ "percent-encoding", "pin-project-lite 0.2.12", "rustls 0.21.6", - "rustls-pemfile", + "rustls-pemfile 1.0.3", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -14778,7 +14830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac 0.12.1", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -14794,7 +14846,7 @@ dependencies = [ "blake2 0.10.6", "common", "fflonk", - "merlin 3.0.0", + "merlin", ] [[package]] @@ -14866,26 +14918,22 @@ name = "rococo-emulated-chain" version = "0.0.0" dependencies = [ "emulated-integration-tests-common", - "pallet-im-online", "parachains-common", "polkadot-primitives", "rococo-runtime", "rococo-runtime-constants", "sc-consensus-grandpa", - "serde_json", "sp-authority-discovery", "sp-consensus-babe", "sp-consensus-beefy", "sp-core", - "sp-runtime", ] [[package]] name = "rococo-parachain-runtime" -version = "0.1.0" +version = "0.6.0" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", @@ -14921,7 +14969,7 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -14929,11 +14977,12 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "testnet-parachains-constants", ] [[package]] name = "rococo-runtime" -version = "1.0.0" +version = "7.0.0" dependencies = [ "binary-merkle-tree", "frame-benchmarking", @@ -15019,9 +15068,9 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", + "sp-tracing 16.0.0", "sp-transaction-pool", "sp-trie", "sp-version", @@ -15036,7 +15085,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" -version = "1.0.0" +version = "7.0.0" dependencies = [ "frame-support", "polkadot-primitives", @@ -15075,6 +15124,12 @@ dependencies = [ "westend-emulated-chain", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rpassword" version = "7.2.0" @@ -15086,6 +15141,35 @@ dependencies = [ "winapi", ] +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version 0.4.0", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.0", + "syn 2.0.50", + "unicode-ident", +] + [[package]] name = "rtnetlink" version = "0.10.1" @@ -15127,7 +15211,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand 0.8.5", + "rand", "rlp", "ruint-macro", "serde", @@ -15225,14 +15309,14 @@ 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.4.0", "errno", "libc", - "linux-raw-sys 0.4.10", + "linux-raw-sys 0.4.11", "windows-sys 0.48.0", ] @@ -15260,6 +15344,20 @@ dependencies = [ "sct", ] +[[package]] +name = "rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-pki-types", + "rustls-webpki 0.102.2", + "subtle 2.5.0", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -15267,7 +15365,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.3", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.0.0", + "rustls-pki-types", "schannel", "security-framework", ] @@ -15282,15 +15393,21 @@ dependencies = [ ] [[package]] -name = "rustls-webpki" -version = "0.100.2" +name = "rustls-pemfile" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98ff011474fa39949b7e5c0428f9b4937eda7da7848bbb947786b7be0b27dab" +checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "base64 0.21.2", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" + [[package]] name = "rustls-webpki" version = "0.101.4" @@ -15301,6 +15418,17 @@ dependencies = [ "untrusted 0.7.1", ] +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring 0.17.7", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -15376,17 +15504,17 @@ dependencies = [ [[package]] name = "sc-allocator" -version = "4.1.0-dev" +version = "23.0.0" dependencies = [ "log", "sp-core", - "sp-wasm-interface 14.0.0", + "sp-wasm-interface 20.0.0", "thiserror", ] [[package]] name = "sc-authority-discovery" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "async-trait", "futures", @@ -15400,7 +15528,7 @@ dependencies = [ "prost 0.12.3", "prost-build", "quickcheck", - "rand 0.8.5", + "rand", "sc-client-api", "sc-network", "sp-api", @@ -15409,7 +15537,7 @@ dependencies = [ "sp-core", "sp-keystore", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "thiserror", @@ -15417,7 +15545,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "futures", "futures-timer", @@ -15442,7 +15570,7 @@ dependencies = [ [[package]] name = "sc-block-builder" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -15458,10 +15586,10 @@ dependencies = [ [[package]] name = "sc-chain-spec" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "array-bytes 6.1.0", - "docify", + "docify 0.2.7", "log", "memmap2 0.9.3", "parity-scale-codec", @@ -15476,6 +15604,7 @@ dependencies = [ "sp-blockchain", "sp-consensus-babe", "sp-core", + "sp-crypto-hashing", "sp-genesis-builder", "sp-io", "sp-keyring", @@ -15486,22 +15615,22 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" -version = "4.0.0-dev" +version = "11.0.0" dependencies = [ "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "sc-cli" -version = "0.10.0-dev" +version = "0.36.0" dependencies = [ "array-bytes 6.1.0", "bip39", "chrono", - "clap 4.4.18", + "clap 4.5.1", "fdlimit", "futures", "futures-timer", @@ -15510,7 +15639,7 @@ dependencies = [ "log", "names", "parity-scale-codec", - "rand 0.8.5", + "rand", "regex", "rpassword", "sc-client-api", @@ -15530,7 +15659,7 @@ dependencies = [ "sp-keystore", "sp-panic-handler", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-version", "tempfile", "thiserror", @@ -15539,7 +15668,7 @@ dependencies = [ [[package]] name = "sc-client-api" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "fnv", "futures", @@ -15554,11 +15683,11 @@ dependencies = [ "sp-consensus", "sp-core", "sp-database", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-runtime", "sp-state-machine", "sp-statement-store", - "sp-storage 13.0.0", + "sp-storage 19.0.0", "sp-test-primitives", "sp-trie", "substrate-prometheus-endpoint", @@ -15568,7 +15697,7 @@ dependencies = [ [[package]] name = "sc-client-db" -version = "0.10.0-dev" +version = "0.35.0" dependencies = [ "array-bytes 6.1.0", "criterion 0.4.0", @@ -15583,7 +15712,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", "quickcheck", - "rand 0.8.5", + "rand", "sc-client-api", "sc-state-db", "schnellru", @@ -15593,7 +15722,7 @@ dependencies = [ "sp-database", "sp-runtime", "sp-state-machine", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-trie", "substrate-test-runtime-client", "tempfile", @@ -15601,7 +15730,7 @@ dependencies = [ [[package]] name = "sc-consensus" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "async-trait", "futures", @@ -15626,7 +15755,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "async-trait", "futures", @@ -15654,7 +15783,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-timestamp", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "tempfile", @@ -15664,7 +15793,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "async-trait", "fork-tree", @@ -15691,12 +15820,13 @@ dependencies = [ "sp-consensus-babe", "sp-consensus-slots", "sp-core", + "sp-crypto-hashing", "sp-inherents", "sp-keyring", "sp-keystore", "sp-runtime", "sp-timestamp", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "thiserror", @@ -15705,7 +15835,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "futures", "jsonrpsee", @@ -15733,7 +15863,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy" -version = "4.0.0-dev" +version = "13.0.0" dependencies = [ "array-bytes 6.1.0", "async-channel", @@ -15760,11 +15890,12 @@ dependencies = [ "sp-consensus-beefy", "sp-consensus-grandpa", "sp-core", + "sp-crypto-hashing", "sp-keyring", "sp-keystore", "sp-mmr-primitives", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "tempfile", @@ -15775,7 +15906,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy-rpc" -version = "4.0.0-dev" +version = "13.0.0" dependencies = [ "futures", "jsonrpsee", @@ -15796,7 +15927,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "fork-tree", "parity-scale-codec", @@ -15808,9 +15939,9 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" -version = "0.10.0-dev" +version = "0.19.0" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "array-bytes 6.1.0", "assert_matches", "async-trait", @@ -15822,7 +15953,7 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "sc-block-builder", "sc-chain-spec", "sc-client-api", @@ -15844,10 +15975,11 @@ dependencies = [ "sp-consensus", "sp-consensus-grandpa", "sp-core", + "sp-crypto-hashing", "sp-keyring", "sp-keystore", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "thiserror", @@ -15856,7 +15988,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa-rpc" -version = "0.10.0-dev" +version = "0.19.0" dependencies = [ "finality-grandpa", "futures", @@ -15880,7 +16012,7 @@ dependencies = [ [[package]] name = "sc-consensus-manual-seal" -version = "0.10.0-dev" +version = "0.35.0" dependencies = [ "assert_matches", "async-trait", @@ -15918,7 +16050,7 @@ dependencies = [ [[package]] name = "sc-consensus-pow" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "async-trait", "futures", @@ -15942,7 +16074,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "async-trait", "futures", @@ -15965,7 +16097,7 @@ dependencies = [ [[package]] name = "sc-executor" -version = "0.10.0-dev" +version = "0.32.0" dependencies = [ "array-bytes 6.1.0", "assert_matches", @@ -15983,38 +16115,39 @@ dependencies = [ "schnellru", "sp-api", "sp-core", - "sp-externalities 0.19.0", + "sp-crypto-hashing", + "sp-externalities 0.25.0", "sp-io", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-runtime", - "sp-runtime-interface 17.0.0", + "sp-runtime-interface 24.0.0", "sp-state-machine", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-trie", "sp-version", - "sp-wasm-interface 14.0.0", + "sp-wasm-interface 20.0.0", "substrate-test-runtime", "tempfile", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.2.25", "wat", ] [[package]] name = "sc-executor-common" -version = "0.10.0-dev" +version = "0.29.0" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface 14.0.0", + "sp-wasm-interface 20.0.0", "thiserror", - "wasm-instrument 0.3.0", + "wasm-instrument", ] [[package]] name = "sc-executor-wasmtime" -version = "0.10.0-dev" +version = "0.29.0" dependencies = [ "anyhow", "cargo_metadata", @@ -16029,8 +16162,8 @@ dependencies = [ "sc-executor-common", "sc-runtime-test", "sp-io", - "sp-runtime-interface 17.0.0", - "sp-wasm-interface 14.0.0", + "sp-runtime-interface 24.0.0", + "sp-wasm-interface 20.0.0", "tempfile", "wasmtime", "wat", @@ -16038,7 +16171,7 @@ dependencies = [ [[package]] name = "sc-informant" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "ansi_term", "futures", @@ -16054,7 +16187,7 @@ dependencies = [ [[package]] name = "sc-keystore" -version = "4.0.0-dev" +version = "25.0.0" dependencies = [ "array-bytes 6.1.0", "parking_lot 0.12.1", @@ -16068,7 +16201,7 @@ dependencies = [ [[package]] name = "sc-mixnet" -version = "0.1.0-dev" +version = "0.4.0" dependencies = [ "array-bytes 4.2.0", "arrayvec 0.7.4", @@ -16096,7 +16229,7 @@ dependencies = [ [[package]] name = "sc-network" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "array-bytes 6.1.0", "assert_matches", @@ -16118,7 +16251,7 @@ dependencies = [ "parking_lot 0.12.1", "partial_sort", "pin-project", - "rand 0.8.5", + "rand", "sc-client-api", "sc-network-common", "sc-network-light", @@ -16132,7 +16265,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-test-primitives", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "substrate-test-runtime", "substrate-test-runtime-client", @@ -16149,7 +16282,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "async-channel", "cid", @@ -16164,7 +16297,7 @@ dependencies = [ "sc-network", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-crypto-hashing", "sp-runtime", "substrate-test-runtime", "substrate-test-runtime-client", @@ -16175,7 +16308,7 @@ dependencies = [ [[package]] name = "sc-network-common" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "async-trait", "bitflags 1.3.2", @@ -16192,9 +16325,9 @@ dependencies = [ [[package]] name = "sc-network-gossip" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "async-trait", "futures", "futures-timer", @@ -16215,7 +16348,7 @@ dependencies = [ [[package]] name = "sc-network-light" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "array-bytes 6.1.0", "async-channel", @@ -16235,7 +16368,7 @@ dependencies = [ [[package]] name = "sc-network-statement" -version = "0.10.0-dev" +version = "0.16.0" dependencies = [ "array-bytes 6.1.0", "async-channel", @@ -16253,7 +16386,7 @@ dependencies = [ [[package]] name = "sc-network-sync" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "array-bytes 6.1.0", "async-channel", @@ -16283,7 +16416,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-test-primitives", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "thiserror", @@ -16301,7 +16434,7 @@ dependencies = [ "libp2p", "log", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "sc-block-builder", "sc-client-api", "sc-consensus", @@ -16315,7 +16448,7 @@ dependencies = [ "sp-consensus", "sp-core", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-test-runtime", "substrate-test-runtime-client", "tokio", @@ -16323,7 +16456,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "array-bytes 6.1.0", "futures", @@ -16341,7 +16474,7 @@ dependencies = [ [[package]] name = "sc-offchain" -version = "4.0.0-dev" +version = "29.0.0" dependencies = [ "array-bytes 6.1.0", "bytes", @@ -16357,7 +16490,7 @@ dependencies = [ "once_cell", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "sc-block-builder", "sc-client-api", "sc-client-db", @@ -16369,11 +16502,11 @@ dependencies = [ "sp-api", "sp-consensus", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-keystore", "sp-offchain", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-test-runtime-client", "threadpool", "tokio", @@ -16382,7 +16515,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" -version = "0.10.0-dev" +version = "0.17.0" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -16390,7 +16523,7 @@ dependencies = [ [[package]] name = "sc-rpc" -version = "4.0.0-dev" +version = "29.0.0" dependencies = [ "assert_matches", "env_logger 0.9.3", @@ -16416,6 +16549,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-core", + "sp-crypto-hashing", "sp-io", "sp-keystore", "sp-offchain", @@ -16426,11 +16560,12 @@ dependencies = [ "sp-version", "substrate-test-runtime-client", "tokio", + "tracing-subscriber 0.3.18", ] [[package]] name = "sc-rpc-api" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -16449,9 +16584,12 @@ dependencies = [ [[package]] name = "sc-rpc-server" -version = "4.0.0-dev" +version = "11.0.0" dependencies = [ + "futures", + "governor", "http", + "hyper", "jsonrpsee", "log", "serde_json", @@ -16463,7 +16601,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "array-bytes 6.1.0", "assert_matches", @@ -16475,10 +16613,13 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", "pretty_assertions", + "rand", "sc-block-builder", "sc-chain-spec", "sc-client-api", + "sc-rpc", "sc-service", + "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "serde", @@ -16487,13 +16628,14 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-maybe-compressed-blob", "sp-rpc", "sp-runtime", "sp-version", "substrate-test-runtime", "substrate-test-runtime-client", + "substrate-test-runtime-transaction-pool", "thiserror", "tokio", "tokio-stream", @@ -16506,14 +16648,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", + "sp-runtime-interface 24.0.0", + "sp-std 14.0.0", "substrate-wasm-builder", ] [[package]] name = "sc-service" -version = "0.10.0-dev" +version = "0.35.0" dependencies = [ "async-trait", "directories", @@ -16525,7 +16667,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", "pin-project", - "rand 0.8.5", + "rand", "sc-chain-spec", "sc-client-api", "sc-client-db", @@ -16548,23 +16690,24 @@ dependencies = [ "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", + "schnellru", "serde", "serde_json", "sp-api", "sp-blockchain", "sp-consensus", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-keystore", "sp-runtime", "sp-session", "sp-state-machine", - "sp-storage 13.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", - "static_init 1.0.3", + "static_init", "substrate-prometheus-endpoint", "substrate-test-runtime", "substrate-test-runtime-client", @@ -16602,8 +16745,8 @@ dependencies = [ "sp-io", "sp-runtime", "sp-state-machine", - "sp-storage 13.0.0", - "sp-tracing 10.0.0", + "sp-storage 19.0.0", + "sp-tracing 16.0.0", "sp-trie", "substrate-test-runtime", "substrate-test-runtime-client", @@ -16613,7 +16756,7 @@ dependencies = [ [[package]] name = "sc-state-db" -version = "0.10.0-dev" +version = "0.30.0" dependencies = [ "log", "parity-scale-codec", @@ -16623,7 +16766,7 @@ dependencies = [ [[package]] name = "sc-statement-store" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "env_logger 0.9.3", "log", @@ -16643,9 +16786,9 @@ dependencies = [ [[package]] name = "sc-storage-monitor" -version = "0.1.0" +version = "0.16.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "fs4", "log", "sp-core", @@ -16655,7 +16798,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" -version = "0.10.0-dev" +version = "0.34.0" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -16673,27 +16816,28 @@ dependencies = [ [[package]] name = "sc-sysinfo" -version = "6.0.0-dev" +version = "27.0.0" dependencies = [ "derive_more", "futures", "libc", "log", - "rand 0.8.5", + "rand", "rand_pcg", "regex", "sc-telemetry", "serde", "serde_json", "sp-core", + "sp-crypto-hashing", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sc-telemetry" -version = "4.0.0-dev" +version = "15.0.0" dependencies = [ "chrono", "futures", @@ -16701,7 +16845,7 @@ dependencies = [ "log", "parking_lot 0.12.1", "pin-project", - "rand 0.8.5", + "rand", "sc-utils", "serde", "serde_json", @@ -16711,7 +16855,7 @@ dependencies = [ [[package]] name = "sc-tracing" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "ansi_term", "chrono", @@ -16732,26 +16876,26 @@ dependencies = [ "sp-core", "sp-rpc", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "thiserror", "tracing", - "tracing-log", - "tracing-subscriber", + "tracing-log 0.1.3", + "tracing-subscriber 0.2.25", ] [[package]] name = "sc-tracing-proc-macro" -version = "4.0.0-dev" +version = "11.0.0" dependencies = [ "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "sc-transaction-pool" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "array-bytes 6.1.0", "assert_matches", @@ -16772,8 +16916,9 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-core", + "sp-crypto-hashing", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-transaction-pool", "substrate-prometheus-endpoint", "substrate-test-runtime", @@ -16784,7 +16929,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "async-trait", "futures", @@ -16800,7 +16945,7 @@ dependencies = [ [[package]] name = "sc-utils" -version = "4.0.0-dev" +version = "14.0.0" dependencies = [ "async-channel", "futures", @@ -16878,29 +17023,11 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "cfg-if", "hashbrown 0.13.2", ] -[[package]] -name = "schnorrkel" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "curve25519-dalek 2.1.3", - "getrandom 0.1.16", - "merlin 2.0.1", - "rand 0.7.3", - "rand_core 0.5.1", - "sha2 0.8.2", - "subtle 2.4.1", - "zeroize", -] - [[package]] name = "schnorrkel" version = "0.10.2" @@ -16910,7 +17037,7 @@ dependencies = [ "arrayref", "arrayvec 0.7.4", "curve25519-dalek-ng", - "merlin 3.0.0", + "merlin", "rand_core 0.6.4", "sha2 0.9.9", "subtle-ng", @@ -16926,13 +17053,13 @@ dependencies = [ "aead 0.5.2", "arrayref", "arrayvec 0.7.4", - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "getrandom_or_panic", - "merlin 3.0.0", + "merlin", "rand_core 0.6.4", "serde_bytes", "sha2 0.10.7", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] @@ -16974,7 +17101,7 @@ dependencies = [ "der", "generic-array 0.14.7", "pkcs8", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] @@ -17039,7 +17166,7 @@ dependencies = [ [[package]] name = "seedling-runtime" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -17065,7 +17192,7 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -17131,9 +17258,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -17158,13 +17285,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -17189,9 +17316,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -17221,11 +17348,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.30" +version = "0.9.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.3", "itoa", "ryu", "serde", @@ -17254,7 +17381,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -17283,27 +17410,15 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha2" version = "0.9.9" @@ -17349,7 +17464,7 @@ dependencies = [ [[package]] name = "shell-runtime" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -17374,7 +17489,7 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -17386,9 +17501,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" @@ -17434,8 +17549,9 @@ dependencies = [ [[package]] name = "simple-mermaid" -version = "0.1.0" -source = "git+https://github.com/kianenigma/simple-mermaid.git?rev=e48b187bcfd5cc75111acd9d241f1bd36604344b#e48b187bcfd5cc75111acd9d241f1bd36604344b" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "620a1d43d70e142b1d46a929af51d44f383db9c7a2ec122de2cd992ccfcf3c18" [[package]] name = "siphasher" @@ -17460,13 +17576,13 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "slot-range-helper" -version = "1.0.0" +version = "7.0.0" dependencies = [ "enumn", "parity-scale-codec", "paste", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -17494,7 +17610,7 @@ dependencies = [ "async-executor", "async-fs", "async-io", - "async-lock", + "async-lock 2.8.0", "async-net", "async-process", "blocking", @@ -17517,7 +17633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0bb30cf57b7b5f6109ce17c3164445e2d6f270af2cb48f6e4d31c2967c9a9f5" dependencies = [ "arrayvec 0.7.4", - "async-lock", + "async-lock 2.8.0", "atomic-take", "base64 0.21.2", "bip39", @@ -17528,7 +17644,7 @@ dependencies = [ "derive_more", "ed25519-zebra 4.0.3", "either", - "event-listener", + "event-listener 2.5.3", "fnv", "futures-lite", "futures-util", @@ -17537,16 +17653,16 @@ dependencies = [ "hmac 0.12.1", "itertools 0.11.0", "libsecp256k1", - "merlin 3.0.0", + "merlin", "no-std-net", "nom", "num-bigint", "num-rational", "num-traits", - "pbkdf2 0.12.2", + "pbkdf2", "pin-project", "poly1305 0.8.0", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "ruzstd", "schnorrkel 0.10.2", @@ -17559,7 +17675,7 @@ dependencies = [ "smallvec", "soketto", "twox-hash", - "wasmi 0.31.0", + "wasmi", "x25519-dalek 2.0.0", "zeroize", ] @@ -17571,12 +17687,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "256b5bad1d6b49045e95fe87492ce73d5af81545d8b4d8318a872d2007024c33" dependencies = [ "async-channel", - "async-lock", + "async-lock 2.8.0", "base64 0.21.2", "blake2-rfc", "derive_more", "either", - "event-listener", + "event-listener 2.5.3", "fnv", "futures-channel", "futures-lite", @@ -17589,7 +17705,7 @@ dependencies = [ "no-std-net", "parking_lot 0.12.1", "pin-project", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "serde", "serde_json", @@ -17612,36 +17728,46 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" dependencies = [ - "aes-gcm 0.9.4", + "aes-gcm 0.9.2", "blake2 0.10.6", "chacha20poly1305", - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "rand_core 0.6.4", "ring 0.16.20", "rustc_version 0.4.0", "sha2 0.10.7", - "subtle 2.4.1", + "subtle 2.5.0", +] + +[[package]] +name = "snowbridge-amcl" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460a9ed63cdf03c1b9847e8a12a5f5ba19c4efd5869e4a737e05be25d7c427e5" +dependencies = [ + "parity-scale-codec", + "scale-info", ] [[package]] name = "snowbridge-beacon-primitives" -version = "0.9.0" +version = "0.2.0" dependencies = [ "byte-slice-cast", "frame-support", "frame-system", "hex", "hex-literal", - "milagro_bls", "parity-scale-codec", "rlp", "scale-info", "serde", "snowbridge-ethereum", + "snowbridge-milagro-bls", "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "ssz_rs", "ssz_rs_derive", "static_assertions", @@ -17649,7 +17775,7 @@ dependencies = [ [[package]] name = "snowbridge-core" -version = "0.9.0" +version = "0.2.0" dependencies = [ "ethabi-decode", "frame-support", @@ -17665,14 +17791,14 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", ] [[package]] name = "snowbridge-ethereum" -version = "0.9.0" +version = "0.3.0" dependencies = [ "ethabi-decode", "ethbloom", @@ -17680,7 +17806,7 @@ dependencies = [ "hex-literal", "parity-bytes", "parity-scale-codec", - "rand 0.8.5", + "rand", "rlp", "rustc-hex", "scale-info", @@ -17690,13 +17816,28 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "wasm-bindgen-test", ] +[[package]] +name = "snowbridge-milagro-bls" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "026aa8638f690a53e3f7676024b9e913b1cab0111d1b7b92669d40a188f9d7e6" +dependencies = [ + "hex", + "lazy_static", + "parity-scale-codec", + "rand", + "scale-info", + "snowbridge-amcl", + "zeroize", +] + [[package]] name = "snowbridge-outbound-queue-merkle-tree" -version = "0.9.0" +version = "0.3.0" dependencies = [ "array-bytes 4.2.0", "env_logger 0.9.3", @@ -17705,12 +17846,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core", + "sp-crypto-hashing", "sp-runtime", ] [[package]] name = "snowbridge-outbound-queue-runtime-api" -version = "0.9.0" +version = "0.2.0" dependencies = [ "frame-support", "parity-scale-codec", @@ -17718,13 +17860,13 @@ dependencies = [ "snowbridge-outbound-queue-merkle-tree", "sp-api", "sp-core", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", ] [[package]] name = "snowbridge-pallet-ethereum-client" -version = "0.9.0" +version = "0.2.0" dependencies = [ "bp-runtime", "byte-slice-cast", @@ -17735,7 +17877,7 @@ dependencies = [ "log", "pallet-timestamp", "parity-scale-codec", - "rand 0.8.5", + "rand", "rlp", "scale-info", "serde", @@ -17743,19 +17885,34 @@ dependencies = [ "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", + "snowbridge-pallet-ethereum-client-fixtures", "sp-core", "sp-io", "sp-keyring", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "ssz_rs", "ssz_rs_derive", "static_assertions", ] [[package]] -name = "snowbridge-pallet-inbound-queue" +name = "snowbridge-pallet-ethereum-client-fixtures" version = "0.9.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "snowbridge-beacon-primitives", + "snowbridge-core", + "sp-core", + "sp-std 14.0.0", +] + +[[package]] +name = "snowbridge-pallet-inbound-queue" +version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -17774,20 +17931,35 @@ dependencies = [ "snowbridge-core", "snowbridge-ethereum", "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-router-primitives", "sp-core", "sp-io", "sp-keyring", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", ] +[[package]] +name = "snowbridge-pallet-inbound-queue-fixtures" +version = "0.10.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "snowbridge-beacon-primitives", + "snowbridge-core", + "sp-core", + "sp-std 14.0.0", +] + [[package]] name = "snowbridge-pallet-outbound-queue" -version = "0.9.0" +version = "0.2.0" dependencies = [ "bridge-hub-common", "ethabi-decode", @@ -17806,13 +17978,13 @@ dependencies = [ "sp-io", "sp-keyring", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", ] [[package]] name = "snowbridge-pallet-system" -version = "0.9.0" +version = "0.2.0" dependencies = [ "ethabi-decode", "frame-benchmarking", @@ -17832,7 +18004,7 @@ dependencies = [ "sp-io", "sp-keyring", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -17855,7 +18027,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -17863,13 +18035,15 @@ dependencies = [ [[package]] name = "snowbridge-runtime-common" -version = "0.9.0" +version = "0.2.0" dependencies = [ "frame-support", "frame-system", "log", + "parity-scale-codec", "snowbridge-core", "sp-arithmetic", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -17877,13 +18051,12 @@ dependencies = [ [[package]] name = "snowbridge-runtime-test-common" -version = "0.9.0" +version = "0.2.0" dependencies = [ "assets-common", "bridge-hub-test-utils", "bridge-runtime-common", "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-session-benchmarking", "cumulus-pallet-xcm", @@ -17925,6 +18098,7 @@ dependencies = [ "snowbridge-core", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", @@ -17941,8 +18115,8 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-session", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -17954,13 +18128,13 @@ dependencies = [ [[package]] name = "snowbridge-system-runtime-api" -version = "0.9.0" +version = "0.2.0" dependencies = [ "parity-scale-codec", "snowbridge-core", "sp-api", "sp-core", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", ] @@ -17997,13 +18171,94 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.5", + "rand", "sha-1 0.9.8", ] +[[package]] +name = "solochain-template-node" +version = "0.0.0" +dependencies = [ + "clap 4.5.1", + "frame-benchmarking-cli", + "frame-system", + "futures", + "jsonrpsee", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc", + "sc-basic-authorship", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-grandpa", + "sc-executor", + "sc-network", + "sc-offchain", + "sc-rpc-api", + "sc-service", + "sc-telemetry", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde_json", + "solochain-template-runtime", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus-aura", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "try-runtime-cli", +] + +[[package]] +name = "solochain-template-runtime" +version = "0.0.0" +dependencies = [ + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "pallet-aura", + "pallet-balances", + "pallet-grandpa", + "pallet-sudo", + "pallet-template", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-grandpa", + "sp-core", + "sp-genesis-builder", + "sp-inherents", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std 14.0.0", + "sp-storage 19.0.0", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", +] + [[package]] name = "sp-api" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "hash-db", "log", @@ -18011,11 +18266,12 @@ dependencies = [ "scale-info", "sp-api-proc-macro", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-metadata-ir", "sp-runtime", + "sp-runtime-interface 24.0.0", "sp-state-machine", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-test-primitives", "sp-trie", "sp-version", @@ -18024,7 +18280,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" -version = "4.0.0-dev" +version = "15.0.0" dependencies = [ "Inflector", "assert_matches", @@ -18033,7 +18289,7 @@ dependencies = [ "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -18052,7 +18308,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-state-machine", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-version", "static_assertions", "substrate-test-runtime-client", @@ -18061,14 +18317,14 @@ dependencies = [ [[package]] name = "sp-application-crypto" -version = "23.0.0" +version = "30.0.0" dependencies = [ "parity-scale-codec", "scale-info", "serde", "sp-core", "sp-io", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -18084,18 +18340,18 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "16.0.0" +version = "23.0.0" dependencies = [ "criterion 0.4.0", "integer-sqrt", "num-traits", "parity-scale-codec", "primitive-types", - "rand 0.8.5", + "rand", "scale-info", "serde", - "sp-core", - "sp-std 8.0.0", + "sp-crypto-hashing", + "sp-std 14.0.0", "static_assertions", ] @@ -18116,7 +18372,7 @@ version = "0.4.2" source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f" dependencies = [ "ark-bls12-381-ext", - "sp-crypto-ec-utils 0.4.1 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-crypto-ec-utils 0.4.1", ] [[package]] @@ -18125,34 +18381,34 @@ version = "0.4.2" source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f" dependencies = [ "ark-ed-on-bls12-381-bandersnatch-ext", - "sp-crypto-ec-utils 0.4.1 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-crypto-ec-utils 0.4.1", ] [[package]] name = "sp-authority-discovery" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "parity-scale-codec", "scale-info", "sp-api", "sp-application-crypto", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-block-builder" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "sp-api", "sp-inherents", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-blockchain" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "futures", "log", @@ -18169,7 +18425,7 @@ dependencies = [ [[package]] name = "sp-consensus" -version = "0.10.0-dev" +version = "0.32.0" dependencies = [ "async-trait", "futures", @@ -18184,7 +18440,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" -version = "0.10.0-dev" +version = "0.32.0" dependencies = [ "async-trait", "parity-scale-codec", @@ -18194,13 +18450,13 @@ dependencies = [ "sp-consensus-slots", "sp-inherents", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-babe" -version = "0.10.0-dev" +version = "0.32.0" dependencies = [ "async-trait", "parity-scale-codec", @@ -18212,13 +18468,13 @@ dependencies = [ "sp-core", "sp-inherents", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-beefy" -version = "4.0.0-dev" +version = "13.0.0" dependencies = [ "array-bytes 6.1.0", "lazy_static", @@ -18228,17 +18484,19 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-core", + "sp-crypto-hashing", "sp-io", + "sp-keystore", "sp-mmr-primitives", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "strum 0.24.1", "w3f-bls", ] [[package]] name = "sp-consensus-grandpa" -version = "4.0.0-dev" +version = "13.0.0" dependencies = [ "finality-grandpa", "log", @@ -18250,18 +18508,18 @@ dependencies = [ "sp-core", "sp-keystore", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-consensus-pow" -version = "0.10.0-dev" +version = "0.32.0" dependencies = [ "parity-scale-codec", "sp-api", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] @@ -18276,23 +18534,23 @@ dependencies = [ "sp-consensus-slots", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-consensus-slots" -version = "0.10.0-dev" +version = "0.32.0" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-timestamp", ] [[package]] name = "sp-core" -version = "21.0.0" +version = "28.0.0" dependencies = [ "array-bytes 6.1.0", "bandersnatch_vrfs", @@ -18312,12 +18570,12 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin 3.0.0", + "merlin", "parity-scale-codec", "parking_lot 0.12.1", "paste", "primitive-types", - "rand 0.8.5", + "rand", "regex", "scale-info", "schnorrkel 0.11.4", @@ -18325,13 +18583,12 @@ dependencies = [ "secrecy", "serde", "serde_json", - "sp-core-hashing", - "sp-core-hashing-proc-macro", - "sp-debug-derive 8.0.0", - "sp-externalities 0.19.0", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-crypto-hashing", + "sp-debug-derive 14.0.0", + "sp-externalities 0.25.0", + "sp-runtime-interface 24.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", "ss58-registry", "substrate-bip39", "thiserror", @@ -18352,28 +18609,22 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "9.0.0" +version = "15.0.0" dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.7", - "sha2 0.10.7", - "sha3", - "twox-hash", + "sp-crypto-hashing", ] [[package]] name = "sp-core-hashing-proc-macro" -version = "9.0.0" +version = "15.0.0" dependencies = [ - "quote", - "sp-core-hashing", - "syn 2.0.48", + "sp-crypto-hashing-proc-macro", ] [[package]] name = "sp-crypto-ec-utils" version = "0.4.1" +source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -18386,15 +18637,14 @@ dependencies = [ "ark-ed-on-bls12-377-ext", "ark-ed-on-bls12-381-bandersnatch", "ark-ed-on-bls12-381-bandersnatch-ext", - "ark-scale 0.0.12", + "ark-scale 0.0.11", "sp-runtime-interface 17.0.0", "sp-std 8.0.0", ] [[package]] name = "sp-crypto-ec-utils" -version = "0.4.1" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "0.10.0" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -18407,14 +18657,37 @@ dependencies = [ "ark-ed-on-bls12-377-ext", "ark-ed-on-bls12-381-bandersnatch", "ark-ed-on-bls12-381-bandersnatch-ext", - "ark-scale 0.0.11", - "sp-runtime-interface 17.0.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "ark-scale 0.0.12", + "sp-runtime-interface 24.0.0", + "sp-std 14.0.0", +] + +[[package]] +name = "sp-crypto-hashing" +version = "0.0.0" +dependencies = [ + "blake2b_simd", + "byteorder", + "criterion 0.4.0", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-crypto-hashing-proc-macro", + "twox-hash", +] + +[[package]] +name = "sp-crypto-hashing-proc-macro" +version = "0.0.0" +dependencies = [ + "quote", + "sp-crypto-hashing", + "syn 2.0.50", ] [[package]] name = "sp-database" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -18423,25 +18696,26 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "8.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "sp-debug-derive" -version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "14.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "sp-externalities" version = "0.19.0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "environmental", "parity-scale-codec", @@ -18451,28 +18725,27 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.19.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "0.25.0" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-storage 13.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-std 14.0.0", + "sp-storage 19.0.0", ] [[package]] name = "sp-genesis-builder" -version = "0.1.0" +version = "0.7.0" dependencies = [ "serde_json", "sp-api", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-inherents" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "async-trait", "futures", @@ -18480,13 +18753,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "thiserror", ] [[package]] name = "sp-io" -version = "23.0.0" +version = "30.0.0" dependencies = [ "bytes", "ed25519-dalek", @@ -18496,12 +18769,13 @@ dependencies = [ "rustversion", "secp256k1", "sp-core", - "sp-externalities 0.19.0", + "sp-crypto-hashing", + "sp-externalities 0.25.0", "sp-keystore", - "sp-runtime-interface 17.0.0", + "sp-runtime-interface 24.0.0", "sp-state-machine", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "sp-trie", "tracing", "tracing-core", @@ -18509,7 +18783,7 @@ dependencies = [ [[package]] name = "sp-keyring" -version = "24.0.0" +version = "31.0.0" dependencies = [ "sp-core", "sp-runtime", @@ -18518,20 +18792,19 @@ dependencies = [ [[package]] name = "sp-keystore" -version = "0.27.0" +version = "0.34.0" dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "rand_chacha 0.2.2", "sp-core", - "sp-externalities 0.19.0", - "thiserror", + "sp-externalities 0.25.0", ] [[package]] name = "sp-maybe-compressed-blob" -version = "4.1.0-dev" +version = "11.0.0" dependencies = [ "thiserror", "zstd 0.12.4", @@ -18539,28 +18812,28 @@ dependencies = [ [[package]] name = "sp-metadata-ir" -version = "0.1.0" +version = "0.6.0" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-mixnet" -version = "0.1.0-dev" +version = "0.4.0" dependencies = [ "parity-scale-codec", "scale-info", "sp-api", "sp-application-crypto", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-mmr-primitives" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "array-bytes 6.1.0", "ckb-merkle-mountain-range", @@ -18570,24 +18843,24 @@ dependencies = [ "serde", "sp-api", "sp-core", - "sp-debug-derive 8.0.0", + "sp-debug-derive 14.0.0", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "thiserror", ] [[package]] name = "sp-npos-elections" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "parity-scale-codec", - "rand 0.8.5", + "rand", "scale-info", "serde", "sp-arithmetic", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "substrate-test-utils", ] @@ -18595,16 +18868,16 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "honggfuzz", - "rand 0.8.5", + "rand", "sp-npos-elections", "sp-runtime", ] [[package]] name = "sp-offchain" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "sp-api", "sp-core", @@ -18613,7 +18886,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "8.0.0" +version = "13.0.0" dependencies = [ "backtrace", "lazy_static", @@ -18622,7 +18895,7 @@ dependencies = [ [[package]] name = "sp-rpc" -version = "6.0.0" +version = "26.0.0" dependencies = [ "rustc-hash", "serde", @@ -18632,16 +18905,16 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "24.0.0" +version = "31.0.1" dependencies = [ - "docify", + "docify 0.2.7", "either", "hash256-std-hasher", "impl-trait-for-tuples", "log", "parity-scale-codec", "paste", - "rand 0.8.5", + "rand", "scale-info", "serde", "serde_json", @@ -18652,76 +18925,78 @@ dependencies = [ "sp-core", "sp-io", "sp-state-machine", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "sp-weights", "substrate-test-runtime-client", + "tuplex", "zstd 0.12.4", ] [[package]] name = "sp-runtime-interface" version = "17.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", - "rustversion", - "sp-core", "sp-externalities 0.19.0", - "sp-io", "sp-runtime-interface-proc-macro 11.0.0", - "sp-runtime-interface-test-wasm", - "sp-state-machine", "sp-std 8.0.0", "sp-storage 13.0.0", "sp-tracing 10.0.0", "sp-wasm-interface 14.0.0", "static_assertions", - "trybuild", ] [[package]] name = "sp-runtime-interface" -version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "24.0.0" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", + "polkavm-derive 0.8.0", "primitive-types", - "sp-externalities 0.19.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-runtime-interface-proc-macro 11.0.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-storage 13.0.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-tracing 10.0.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-wasm-interface 14.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "rustversion", + "sp-core", + "sp-externalities 0.25.0", + "sp-io", + "sp-runtime-interface-proc-macro 17.0.0", + "sp-runtime-interface-test-wasm", + "sp-state-machine", + "sp-std 14.0.0", + "sp-storage 19.0.0", + "sp-tracing 16.0.0", + "sp-wasm-interface 20.0.0", "static_assertions", + "trybuild", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" dependencies = [ "Inflector", - "expander 2.0.0", - "proc-macro-crate 3.0.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] -name = "sp-runtime-interface-proc-macro" -version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +name = "sp-runtime-interface-proc-macro" +version = "17.0.0" dependencies = [ "Inflector", - "proc-macro-crate 1.3.1", + "expander 2.0.0", + "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -18732,7 +19007,7 @@ dependencies = [ "sc-executor-common", "sp-io", "sp-runtime", - "sp-runtime-interface 17.0.0", + "sp-runtime-interface 24.0.0", "sp-runtime-interface-test-wasm", "sp-runtime-interface-test-wasm-deprecated", "sp-state-machine", @@ -18747,8 +19022,8 @@ dependencies = [ "bytes", "sp-core", "sp-io", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", + "sp-runtime-interface 24.0.0", + "sp-std 14.0.0", "substrate-wasm-builder", ] @@ -18758,13 +19033,13 @@ version = "2.0.0" dependencies = [ "sp-core", "sp-io", - "sp-runtime-interface 17.0.0", + "sp-runtime-interface 24.0.0", "substrate-wasm-builder", ] [[package]] name = "sp-session" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "parity-scale-codec", "scale-info", @@ -18773,12 +19048,12 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-staking" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -18786,12 +19061,12 @@ dependencies = [ "serde", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-state-machine" -version = "0.28.0" +version = "0.35.0" dependencies = [ "array-bytes 6.1.0", "assert_matches", @@ -18800,13 +19075,13 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", "pretty_assertions", - "rand 0.8.5", + "rand", "smallvec", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-panic-handler", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", "thiserror", "tracing", @@ -18815,23 +19090,24 @@ dependencies = [ [[package]] name = "sp-statement-store" -version = "4.0.0-dev" +version = "10.0.0" dependencies = [ "aes-gcm 0.10.3", - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "ed25519-dalek", "hkdf", "parity-scale-codec", - "rand 0.8.5", + "rand", "scale-info", "sha2 0.10.7", "sp-api", "sp-application-crypto", "sp-core", - "sp-externalities 0.19.0", + "sp-crypto-hashing", + "sp-externalities 0.25.0", "sp-runtime", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", + "sp-runtime-interface 24.0.0", + "sp-std 14.0.0", "thiserror", "x25519-dalek 2.0.0", ] @@ -18839,15 +19115,16 @@ dependencies = [ [[package]] name = "sp-std" version = "8.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" [[package]] name = "sp-std" -version = "8.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "14.0.0" [[package]] name = "sp-storage" version = "13.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "impl-serde", "parity-scale-codec", @@ -18859,15 +19136,14 @@ dependencies = [ [[package]] name = "sp-storage" -version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "19.0.0" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)", - "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-debug-derive 14.0.0", + "sp-std 14.0.0", ] [[package]] @@ -18880,47 +19156,47 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "sp-timestamp" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "async-trait", "parity-scale-codec", "sp-inherents", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "thiserror", ] [[package]] name = "sp-tracing" version = "10.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "parity-scale-codec", "sp-std 8.0.0", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] name = "sp-tracing" -version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "16.0.0" dependencies = [ "parity-scale-codec", - "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-std 14.0.0", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] name = "sp-transaction-pool" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "sp-api", "sp-runtime", @@ -18928,7 +19204,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" -version = "4.0.0-dev" +version = "26.0.0" dependencies = [ "async-trait", "parity-scale-codec", @@ -18936,15 +19212,15 @@ dependencies = [ "sp-core", "sp-inherents", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-trie", ] [[package]] name = "sp-trie" -version = "22.0.0" +version = "29.0.0" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "array-bytes 6.1.0", "criterion 0.4.0", "hash-db", @@ -18953,13 +19229,13 @@ dependencies = [ "nohash-hasher", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "scale-info", "schnellru", "sp-core", - "sp-externalities 0.19.0", + "sp-externalities 0.25.0", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "thiserror", "tracing", "trie-bench", @@ -18970,34 +19246,35 @@ dependencies = [ [[package]] name = "sp-version" -version = "22.0.0" +version = "29.0.0" dependencies = [ "impl-serde", "parity-scale-codec", "parity-wasm", "scale-info", "serde", - "sp-core-hashing-proc-macro", + "sp-crypto-hashing-proc-macro", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-version-proc-macro", "thiserror", ] [[package]] name = "sp-version-proc-macro" -version = "8.0.0" +version = "13.0.0" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", "sp-version", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "sp-wasm-interface" version = "14.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -19009,20 +19286,19 @@ dependencies = [ [[package]] name = "sp-wasm-interface" -version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +version = "20.0.0" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk)", + "sp-std 14.0.0", "wasmtime", ] [[package]] name = "sp-weights" -version = "20.0.0" +version = "27.0.0" dependencies = [ "bounded-collections", "parity-scale-codec", @@ -19031,8 +19307,8 @@ dependencies = [ "serde", "smallvec", "sp-arithmetic", - "sp-debug-derive 8.0.0", - "sp-std 8.0.0", + "sp-debug-derive 14.0.0", + "sp-std 14.0.0", ] [[package]] @@ -19116,11 +19392,11 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "staging-chain-spec-builder" version = "2.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "log", "sc-chain-spec", "serde_json", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", ] [[package]] @@ -19129,7 +19405,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes 6.1.0", "assert_cmd", - "clap 4.4.18", + "clap 4.5.1", "clap_complete", "criterion 0.4.0", "frame-benchmarking", @@ -19161,7 +19437,7 @@ dependencies = [ "pallet-treasury", "parity-scale-codec", "platforms", - "rand 0.8.5", + "rand", "regex", "sc-authority-discovery", "sc-basic-authorship", @@ -19207,7 +19483,8 @@ dependencies = [ "sp-consensus-beefy", "sp-consensus-grandpa", "sp-core", - "sp-externalities 0.19.0", + "sp-crypto-hashing", + "sp-externalities 0.25.0", "sp-inherents", "sp-io", "sp-keyring", @@ -19218,7 +19495,7 @@ dependencies = [ "sp-state-machine", "sp-statement-store", "sp-timestamp", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "sp-transaction-storage-proof", "sp-trie", "staging-node-inspect", @@ -19236,9 +19513,9 @@ dependencies = [ [[package]] name = "staging-node-inspect" -version = "0.9.0-dev" +version = "0.12.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -19253,7 +19530,7 @@ dependencies = [ [[package]] name = "staging-parachain-info" -version = "0.1.0" +version = "0.7.0" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -19261,16 +19538,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", ] [[package]] name = "staging-tracking-allocator" -version = "1.0.0" +version = "2.0.0" [[package]] name = "staging-xcm" -version = "1.0.0" +version = "7.0.0" dependencies = [ "array-bytes 6.1.0", "bounded-collections", @@ -19291,7 +19568,7 @@ dependencies = [ [[package]] name = "staging-xcm-builder" -version = "1.0.0" +version = "7.0.0" dependencies = [ "assert_matches", "frame-support", @@ -19313,7 +19590,7 @@ dependencies = [ "sp-arithmetic", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-weights", "staging-xcm", "staging-xcm-executor", @@ -19321,7 +19598,7 @@ dependencies = [ [[package]] name = "staging-xcm-executor" -version = "1.0.0" +version = "7.0.0" dependencies = [ "environmental", "frame-benchmarking", @@ -19334,7 +19611,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "sp-weights", "staging-xcm", ] @@ -19345,18 +19622,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "static_init" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b73400442027c4adedda20a9f9b7945234a5bd8d5f7e86da22bd5d0622369c" -dependencies = [ - "cfg_aliases", - "libc", - "parking_lot 0.11.2", - "static_init_macro 0.5.0", -] - [[package]] name = "static_init" version = "1.0.3" @@ -19368,23 +19633,10 @@ dependencies = [ "libc", "parking_lot 0.11.2", "parking_lot_core 0.8.6", - "static_init_macro 1.0.2", + "static_init_macro", "winapi", ] -[[package]] -name = "static_init_macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2261c91034a1edc3fc4d1b80e89d82714faede0515c14a75da10cb941546bbf" -dependencies = [ - "cfg_aliases", - "memchr", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "static_init_macro" version = "1.0.2" @@ -19407,7 +19659,7 @@ dependencies = [ "bitflags 1.3.2", "byteorder", "keccak", - "subtle 2.4.1", + "subtle 2.5.0", "zeroize", ] @@ -19417,6 +19669,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "strum" version = "0.24.1" @@ -19455,33 +19713,33 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "subkey" -version = "3.0.0" +version = "9.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "sc-cli", ] [[package]] name = "substrate-bip39" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +version = "0.4.7" dependencies = [ - "hmac 0.11.0", - "pbkdf2 0.8.0", - "schnorrkel 0.9.1", - "sha2 0.9.9", + "bip39", + "hmac 0.12.1", + "pbkdf2", + "rustc-hex", + "schnorrkel 0.11.4", + "sha2 0.10.7", "zeroize", ] [[package]] name = "substrate-build-script-utils" -version = "3.0.0" +version = "11.0.0" [[package]] name = "substrate-cli-test-utils" @@ -19502,9 +19760,9 @@ dependencies = [ [[package]] name = "substrate-frame-cli" -version = "4.0.0-dev" +version = "32.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "frame-support", "frame-system", "sc-cli", @@ -19514,7 +19772,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-support" -version = "3.0.0" +version = "29.0.0" dependencies = [ "frame-support", "frame-system", @@ -19525,13 +19783,13 @@ dependencies = [ "serde", "sp-core", "sp-runtime", - "sp-storage 13.0.0", + "sp-storage 19.0.0", "tokio", ] [[package]] name = "substrate-frame-rpc-system" -version = "4.0.0-dev" +version = "28.0.0" dependencies = [ "assert_matches", "frame-system-rpc-runtime-api", @@ -19547,14 +19805,14 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "substrate-test-runtime-client", "tokio", ] [[package]] name = "substrate-prometheus-endpoint" -version = "0.10.0-dev" +version = "0.17.0" dependencies = [ "hyper", "log", @@ -19565,7 +19823,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" -version = "0.10.0-dev" +version = "0.33.0" dependencies = [ "async-trait", "jsonrpsee", @@ -19579,7 +19837,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" -version = "4.0.0-dev" +version = "27.0.0" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -19617,6 +19875,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-state-machine", + "tokio", ] [[package]] @@ -19629,13 +19888,13 @@ dependencies = [ "frame-system", "frame-system-rpc-runtime-api", "futures", - "json-patch", "log", "pallet-babe", "pallet-balances", "pallet-timestamp", "parity-scale-codec", "sc-block-builder", + "sc-chain-spec", "sc-executor", "sc-executor-common", "sc-service", @@ -19650,7 +19909,8 @@ dependencies = [ "sp-consensus-babe", "sp-consensus-grandpa", "sp-core", - "sp-externalities 0.19.0", + "sp-crypto-hashing", + "sp-externalities 0.25.0", "sp-genesis-builder", "sp-inherents", "sp-io", @@ -19659,8 +19919,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-state-machine", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "sp-transaction-pool", "sp-trie", "sp-version", @@ -19713,13 +19973,14 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" -version = "5.0.0-dev" +version = "17.0.0" dependencies = [ "build-helper", "cargo_metadata", "console", "filetime", "parity-wasm", + "polkavm-linker 0.8.2", "sp-maybe-compressed-blob", "strum 0.24.1", "tempfile", @@ -19736,9 +19997,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "subtle-ng" @@ -19850,9 +20111,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -19868,7 +20129,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -19936,7 +20197,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.0", "redox_syscall 0.4.1", - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -19955,7 +20216,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", ] @@ -19973,7 +20234,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "sp-io", - "sp-std 8.0.0", + "sp-std 14.0.0", "substrate-wasm-builder", "tiny-keccak", ] @@ -19982,7 +20243,7 @@ dependencies = [ name = "test-parachain-adder-collator" version = "1.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "futures", "futures-timer", "log", @@ -20021,7 +20282,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "sp-io", - "sp-std 8.0.0", + "sp-std 14.0.0", "substrate-wasm-builder", "tiny-keccak", ] @@ -20030,7 +20291,7 @@ dependencies = [ name = "test-parachain-undying-collator" version = "1.0.0" dependencies = [ - "clap 4.4.18", + "clap 4.5.1", "futures", "futures-timer", "log", @@ -20076,6 +20337,20 @@ dependencies = [ "sp-weights", ] +[[package]] +name = "testnet-parachains-constants" +version = "1.0.0" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "polkadot-core-primitives", + "rococo-runtime-constants", + "smallvec", + "sp-runtime", + "staging-xcm", + "westend-runtime-constants", +] + [[package]] name = "textwrap" version = "0.16.0" @@ -20119,7 +20394,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -20280,7 +20555,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -20290,7 +20565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" dependencies = [ "pin-project", - "rand 0.8.5", + "rand", "tokio", ] @@ -20304,6 +20579,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.2", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -20365,18 +20651,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.8" @@ -20404,9 +20678,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", + "indexmap 2.2.3", "toml_datetime", "winnow", ] @@ -20417,7 +20689,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", @@ -20430,6 +20702,10 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite 0.2.12", "tower-layer", "tower-service", "tracing", @@ -20486,7 +20762,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -20511,7 +20787,7 @@ dependencies = [ [[package]] name = "tracing-gum" -version = "1.0.0" +version = "7.0.0" dependencies = [ "coarsetime", "polkadot-primitives", @@ -20521,14 +20797,14 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" -version = "1.0.0" +version = "5.0.0" dependencies = [ "assert_matches", "expander 2.0.0", "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -20542,6 +20818,17 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-serde" version = "0.1.3" @@ -20561,7 +20848,7 @@ dependencies = [ "ansi_term", "chrono", "lazy_static", - "matchers", + "matchers 0.0.1", "parking_lot 0.11.2", "regex", "serde", @@ -20571,10 +20858,28 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.1.3", "tracing-serde", ] +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers 0.1.0", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log 0.2.0", +] + [[package]] name = "trie-bench" version = "0.38.0" @@ -20639,7 +20944,7 @@ dependencies = [ "idna 0.2.3", "ipnet", "lazy_static", - "rand 0.8.5", + "rand", "smallvec", "socket2 0.4.9", "thiserror", @@ -20677,11 +20982,11 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" -version = "0.10.0-dev" +version = "0.38.0" dependencies = [ "assert_cmd", "async-trait", - "clap 4.4.18", + "clap 4.5.1", "frame-remote-externalities", "frame-try-runtime", "hex", @@ -20697,8 +21002,8 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-babe", "sp-core", - "sp-debug-derive 8.0.0", - "sp-externalities 0.19.0", + "sp-debug-derive 14.0.0", + "sp-externalities 0.25.0", "sp-inherents", "sp-io", "sp-keystore", @@ -20718,9 +21023,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76de4f783e610194f6c98bfd53f9fc52bb2e0d02c947621e8a0f4ecc799b2880" +checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f" dependencies = [ "basic-toml", "dissimilar", @@ -20750,13 +21055,19 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.5", + "rand", "sha-1 0.10.1", "thiserror", "url", "utf-8", ] +[[package]] +name = "tuplex" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "676ac81d5454c4dcf37955d34fa8626ede3490f744b86ca14a7b90168d2a08aa" + [[package]] name = "twox-hash" version = "1.6.3" @@ -20765,7 +21076,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand 0.8.5", + "rand", "static_assertions", ] @@ -20834,12 +21145,12 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" dependencies = [ "generic-array 0.14.7", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -20849,7 +21160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -20986,7 +21297,7 @@ dependencies = [ "arrayref", "constcat", "digest 0.10.7", - "rand 0.8.5", + "rand", "rand_chacha 0.3.1", "rand_core 0.6.4", "sha2 0.10.7", @@ -21064,7 +21375,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", "wasm-bindgen-shared", ] @@ -21098,7 +21409,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -21142,15 +21453,6 @@ dependencies = [ "leb128", ] -[[package]] -name = "wasm-instrument" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" -dependencies = [ - "parity-wasm", -] - [[package]] name = "wasm-instrument" version = "0.4.0" @@ -21217,38 +21519,22 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f341edb80021141d4ae6468cbeefc50798716a347d4085c3811900049ea8945" -dependencies = [ - "smallvec", - "spin 0.9.8", - "wasmi_arena", - "wasmi_core 0.13.0", - "wasmparser-nostd", -] - -[[package]] -name = "wasmi" -version = "0.32.0-beta.5" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b962e531cc387ce15b1da00f06f727f9a9063619e74d1499c3e443d940378b0" +checksum = "77a8281d1d660cdf54c76a3efa9ddd0c270cada1383a995db3ccb43d166456c7" dependencies = [ - "multi-stash", - "num-derive", - "num-traits", "smallvec", "spin 0.9.8", "wasmi_arena", - "wasmi_core 0.15.0", + "wasmi_core", "wasmparser-nostd", ] [[package]] name = "wasmi_arena" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] name = "wasmi_core" @@ -21262,18 +21548,6 @@ dependencies = [ "paste", ] -[[package]] -name = "wasmi_core" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ac482df6761020b2b75c9aade41c105993c5b9f64156c349bb7ccad226a6ecd" -dependencies = [ - "downcast-rs", - "libm", - "num-traits", - "paste", -] - [[package]] name = "wasmparser" version = "0.102.0" @@ -21468,7 +21742,7 @@ dependencies = [ "memfd", "memoffset 0.8.0", "paste", - "rand 0.8.5", + "rand", "rustix 0.36.15", "wasmtime-asm-macros", "wasmtime-environ", @@ -21538,15 +21812,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.2", -] - [[package]] name = "webpki-roots" version = "0.25.2" @@ -21558,12 +21823,10 @@ name = "westend-emulated-chain" version = "0.0.0" dependencies = [ "emulated-integration-tests-common", - "pallet-im-online", "pallet-staking", "parachains-common", "polkadot-primitives", "sc-consensus-grandpa", - "serde_json", "sp-authority-discovery", "sp-consensus-babe", "sp-consensus-beefy", @@ -21575,7 +21838,7 @@ dependencies = [ [[package]] name = "westend-runtime" -version = "1.0.0" +version = "7.0.0" dependencies = [ "binary-merkle-tree", "bitvec", @@ -21670,9 +21933,9 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-storage 19.0.0", + "sp-tracing 16.0.0", "sp-transaction-pool", "sp-version", "staging-xcm", @@ -21686,7 +21949,7 @@ dependencies = [ [[package]] name = "westend-runtime-constants" -version = "1.0.0" +version = "7.0.0" dependencies = [ "frame-support", "polkadot-primitives", @@ -22065,7 +22328,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "rand_core 0.6.4", "serde", "zeroize", @@ -22100,7 +22363,7 @@ dependencies = [ [[package]] name = "xcm-emulator" -version = "0.1.0" +version = "0.5.0" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", @@ -22122,10 +22385,11 @@ dependencies = [ "polkadot-runtime-parachains", "sp-arithmetic", "sp-core", + "sp-crypto-hashing", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-executor", ] @@ -22140,33 +22404,35 @@ dependencies = [ "pallet-transaction-payment", "pallet-xcm", "parity-scale-codec", + "polkadot-service", "polkadot-test-client", "polkadot-test-runtime", "polkadot-test-service", "sp-consensus", + "sp-core", "sp-keyring", "sp-runtime", "sp-state-machine", - "sp-tracing 10.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-executor", ] [[package]] name = "xcm-procedural" -version = "1.0.0" +version = "7.0.0" dependencies = [ "Inflector", "proc-macro2", "quote", "staging-xcm", - "syn 2.0.48", + "syn 2.0.50", "trybuild", ] [[package]] name = "xcm-simulator" -version = "1.0.0" +version = "7.0.0" dependencies = [ "frame-support", "parity-scale-codec", @@ -22175,7 +22441,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-runtime-parachains", "sp-io", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -22183,7 +22449,7 @@ dependencies = [ [[package]] name = "xcm-simulator-example" -version = "1.0.0" +version = "7.0.0" dependencies = [ "frame-support", "frame-system", @@ -22200,8 +22466,8 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", - "sp-tracing 10.0.0", + "sp-std 14.0.0", + "sp-tracing 16.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -22213,8 +22479,10 @@ name = "xcm-simulator-fuzzer" version = "1.0.0" dependencies = [ "arbitrary", + "frame-executive", "frame-support", "frame-system", + "frame-try-runtime", "honggfuzz", "pallet-balances", "pallet-message-queue", @@ -22227,7 +22495,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 8.0.0", + "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -22244,7 +22512,7 @@ dependencies = [ "log", "nohash-hasher", "parking_lot 0.12.1", - "rand 0.8.5", + "rand", "static_assertions", ] @@ -22280,7 +22548,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -22300,7 +22568,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3afccc24992f40bb50432b0a78dcbc28f9dafa0d..800685966858156a3eea62234870237d1c819ed8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ authors = ["Parity Technologies "] edition = "2021" repository = "https://github.com/paritytech/polkadot-sdk.git" license = "GPL-3.0-only" +homepage = "https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html" [workspace] resolver = "2" @@ -36,19 +37,21 @@ members = [ "bridges/primitives/test-utils", "bridges/primitives/xcm-bridge-hub", "bridges/primitives/xcm-bridge-hub-router", - "bridges/snowbridge/parachain/pallets/ethereum-client", - "bridges/snowbridge/parachain/pallets/inbound-queue", - "bridges/snowbridge/parachain/pallets/outbound-queue", - "bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree", - "bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api", - "bridges/snowbridge/parachain/pallets/system", - "bridges/snowbridge/parachain/pallets/system/runtime-api", - "bridges/snowbridge/parachain/primitives/beacon", - "bridges/snowbridge/parachain/primitives/core", - "bridges/snowbridge/parachain/primitives/ethereum", - "bridges/snowbridge/parachain/primitives/router", - "bridges/snowbridge/parachain/runtime/runtime-common", - "bridges/snowbridge/parachain/runtime/test-common", + "bridges/snowbridge/pallets/ethereum-client", + "bridges/snowbridge/pallets/ethereum-client/fixtures", + "bridges/snowbridge/pallets/inbound-queue", + "bridges/snowbridge/pallets/inbound-queue/fixtures", + "bridges/snowbridge/pallets/outbound-queue", + "bridges/snowbridge/pallets/outbound-queue/merkle-tree", + "bridges/snowbridge/pallets/outbound-queue/runtime-api", + "bridges/snowbridge/pallets/system", + "bridges/snowbridge/pallets/system/runtime-api", + "bridges/snowbridge/primitives/beacon", + "bridges/snowbridge/primitives/core", + "bridges/snowbridge/primitives/ethereum", + "bridges/snowbridge/primitives/router", + "bridges/snowbridge/runtime/runtime-common", + "bridges/snowbridge/runtime/test-common", "cumulus/client/cli", "cumulus/client/collator", "cumulus/client/consensus/aura", @@ -72,9 +75,6 @@ members = [ "cumulus/pallets/solo-to-para", "cumulus/pallets/xcm", "cumulus/pallets/xcmp-queue", - "cumulus/parachain-template/node", - "cumulus/parachain-template/pallets/template", - "cumulus/parachain-template/runtime", "cumulus/parachains/common", "cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo", "cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend", @@ -108,6 +108,7 @@ members = [ "cumulus/parachains/runtimes/bridge-hubs/common", "cumulus/parachains/runtimes/bridge-hubs/test-utils", "cumulus/parachains/runtimes/collectives/collectives-westend", + "cumulus/parachains/runtimes/constants", "cumulus/parachains/runtimes/contracts/contracts-rococo", "cumulus/parachains/runtimes/coretime/coretime-rococo", "cumulus/parachains/runtimes/coretime/coretime-westend", @@ -124,6 +125,7 @@ members = [ "cumulus/primitives/core", "cumulus/primitives/parachain-inherent", "cumulus/primitives/proof-size-hostfunction", + "cumulus/primitives/storage-weight-reclaim", "cumulus/primitives/timestamp", "cumulus/primitives/utility", "cumulus/test/client", @@ -215,11 +217,6 @@ members = [ "polkadot/xcm/xcm-simulator", "polkadot/xcm/xcm-simulator/example", "polkadot/xcm/xcm-simulator/fuzzer", - "substrate/bin/minimal/node", - "substrate/bin/minimal/runtime", - "substrate/bin/node-template/node", - "substrate/bin/node-template/pallets/template", - "substrate/bin/node-template/runtime", "substrate/bin/node/bench", "substrate/bin/node/cli", "substrate/bin/node/inspect", @@ -287,6 +284,8 @@ members = [ "substrate/client/transaction-pool", "substrate/client/transaction-pool/api", "substrate/client/utils", + "substrate/deprecated/hashing", + "substrate/deprecated/hashing/proc-macro", "substrate/frame", "substrate/frame/alliance", "substrate/frame/asset-conversion", @@ -331,6 +330,7 @@ members = [ "substrate/frame/examples/frame-crate", "substrate/frame/examples/kitchensink", "substrate/frame/examples/offchain-worker", + "substrate/frame/examples/single-block-migrations", "substrate/frame/examples/split", "substrate/frame/examples/tasks", "substrate/frame/executive", @@ -345,12 +345,12 @@ members = [ "substrate/frame/membership", "substrate/frame/merkle-mountain-range", "substrate/frame/message-queue", + "substrate/frame/migrations", "substrate/frame/mixnet", "substrate/frame/multisig", "substrate/frame/nft-fractionalization", "substrate/frame/nfts", "substrate/frame/nfts/runtime-api", - "substrate/frame/nicks", "substrate/frame/nis", "substrate/frame/node-authorization", "substrate/frame/nomination-pools", @@ -362,6 +362,7 @@ members = [ "substrate/frame/offences/benchmarking", "substrate/frame/paged-list", "substrate/frame/paged-list/fuzzer", + "substrate/frame/parameters", "substrate/frame/preimage", "substrate/frame/proxy", "substrate/frame/ranked-collective", @@ -432,9 +433,9 @@ members = [ "substrate/primitives/consensus/slots", "substrate/primitives/core", "substrate/primitives/core/fuzz", - "substrate/primitives/core/hashing", - "substrate/primitives/core/hashing/proc-macro", "substrate/primitives/crypto/ec-utils", + "substrate/primitives/crypto/hashing", + "substrate/primitives/crypto/hashing/proc-macro", "substrate/primitives/database", "substrate/primitives/debug-derive", "substrate/primitives/externalities", @@ -495,7 +496,20 @@ members = [ "substrate/utils/frame/rpc/system", "substrate/utils/frame/try-runtime/cli", "substrate/utils/prometheus", + "substrate/utils/substrate-bip39", "substrate/utils/wasm-builder", + + "templates/minimal/node", + "templates/minimal/pallets/template", + "templates/minimal/runtime", + + "templates/solochain/node", + "templates/solochain/pallets/template", + "templates/solochain/runtime", + + "templates/parachain/node", + "templates/parachain/pallets/template", + "templates/parachain/runtime", ] default-members = ["polkadot", "substrate/bin/node/cli"] @@ -528,6 +542,19 @@ stable_sort_primitive = { level = "allow", priority = 2 } # prefer st extra-unused-type-parameters = { level = "allow", priority = 2 } # stylistic default_constructed_unit_structs = { level = "allow", priority = 2 } # stylistic +[workspace.dependencies] +polkavm-linker = "0.8.2" +polkavm-derive = "0.8.0" +log = { version = "0.4.20", default-features = false } +quote = { version = "1.0.33" } +serde = { version = "1.0.197", default-features = false } +serde-big-array = { version = "0.3.2" } +serde_derive = { version = "1.0.117" } +serde_json = { version = "1.0.114", default-features = false } +serde_yaml = { version = "0.9" } +syn = { version = "2.0.50" } +thiserror = { version = "1.0.48" } + [profile.release] # Polkadot runtime requires unwinding. panic = "unwind" @@ -587,6 +614,7 @@ num-bigint = { opt-level = 3 } parking_lot = { opt-level = 3 } parking_lot_core = { opt-level = 3 } percent-encoding = { opt-level = 3 } +polkavm-linker = { opt-level = 3 } primitive-types = { opt-level = 3 } reed-solomon-novelpoly = { opt-level = 3 } ring = { opt-level = 3 } diff --git a/README.md b/README.md index b94376b35ab0c52e680379b82a67a529fc9d84f7..63743a456f4c8f8561bbeee8c59d63b88d352285 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,9 @@ way. The Polkadot SDK comprises three main pieces of software: [![Polkadot-license](https://img.shields.io/badge/License-GPL3-blue)](./polkadot/LICENSE) Implementation of a node for the https://polkadot.network in Rust, using the Substrate framework. This directory -currently contains runtimes for the Polkadot, Kusama, Westend, and Rococo networks. In the future, these will be -relocated to the [`runtimes`](https://github.com/polkadot-fellows/runtimes/) repository. +currently contains runtimes for the Westend and Rococo test networks. Polkadot, Kusama and their system chain runtimes +are located in the [`runtimes`](https://github.com/polkadot-fellows/runtimes/) repository maintained by +[the Polkadot Technical Fellowship](https://polkadot-fellows.github.io/dashboard/#/overview). ## [Substrate](./substrate/) [![SubstrateRustDocs](https://img.shields.io/badge/Rust_Docs-Substrate-24CC85?logo=rust)](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/substrate/index.html) @@ -35,6 +36,26 @@ Polkadot. Cumulus is a set of tools for writing Substrate-based Polkadot parachains. +## 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). + +### 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. + +### Nightly + +`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`. + ## Upstream Dependencies Below are the primary upstream dependencies utilized in this project: diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 8c3e8c989dbcd0938e42356eca9681221654459c..ee54f15f38b4c0a2e9faae3a157874898701ba2a 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bridge-runtime-common" -version = "0.1.0" +version = "0.7.0" description = "Common types and functions that may be used by substrate-based runtimes of all bridged chains" authors.workspace = true edition.workspace = true @@ -13,7 +13,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } hash-db = { version = "0.16.0", default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } @@ -93,6 +93,7 @@ runtime-benchmarks = [ "pallet-bridge-messages/runtime-benchmarks", "pallet-bridge-parachains/runtime-benchmarks", "pallet-bridge-relayers/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index 2722f6f1c6d14f09ab215f8f020f2c449eda4d4b..035077408c40bd42c9e941c19af868989d66e362 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -105,43 +105,48 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { ($call:ty, $account_id:ty, $($filter_call:ty),*) => { #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)] pub struct BridgeRejectObsoleteHeadersAndMessages; - impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { + impl sp_runtime::traits::TransactionExtensionBase for BridgeRejectObsoleteHeadersAndMessages { const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; - type AccountId = $account_id; - type Call = $call; - type AdditionalSigned = (); + type Implicit = (); + } + impl sp_runtime::traits::TransactionExtension<$call, Context> for BridgeRejectObsoleteHeadersAndMessages { type Pre = (); - - fn additional_signed(&self) -> sp_std::result::Result< - (), - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(()) - } + type Val = (); fn validate( &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, + origin: <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + call: &$call, + _info: &sp_runtime::traits::DispatchInfoOf<$call>, _len: usize, - ) -> sp_runtime::transaction_validity::TransactionValidity { - let valid = sp_runtime::transaction_validity::ValidTransaction::default(); + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl codec::Encode, + ) -> Result< + ( + sp_runtime::transaction_validity::ValidTransaction, + Self::Val, + <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + ), sp_runtime::transaction_validity::TransactionValidityError + > { + let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default(); $( - let valid = valid - .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?); + let call_filter_validity = <$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?; + let tx_validity = tx_validity.combine_with(call_filter_validity); )* - Ok(valid) + Ok((tx_validity, (), origin)) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &sp_runtime::traits::DispatchInfoOf, - len: usize, + _val: Self::Val, + _origin: &<$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + _call: &$call, + _info: &sp_runtime::traits::DispatchInfoOf<$call>, + _len: usize, + _context: &Context, ) -> Result { - self.validate(who, call, info, len).map(drop) + Ok(()) } } }; @@ -150,12 +155,14 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { #[cfg(test)] mod tests { use crate::BridgeRuntimeFilterCall; - use frame_support::{assert_err, assert_ok}; + use codec::Encode; + use frame_support::assert_err; use sp_runtime::{ - traits::SignedExtension, + traits::DispatchTransaction, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, }; + #[derive(Encode)] pub struct MockCall { data: u32, } @@ -206,17 +213,20 @@ mod tests { ); assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), + BridgeRejectObsoleteHeadersAndMessages.validate_only((), &MockCall { data: 1 }, &(), 0), InvalidTransaction::Custom(1) ); assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), + BridgeRejectObsoleteHeadersAndMessages.validate_only((), &MockCall { data: 2 }, &(), 0), InvalidTransaction::Custom(2) ); - assert_ok!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), + assert_eq!( + BridgeRejectObsoleteHeadersAndMessages + .validate_only((), &MockCall { data: 3 }, &(), 0) + .unwrap() + .0, ValidTransaction { priority: 3, ..Default::default() } ) } diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 8877a4fd95ce33150824b78674f38860616cf820..f147f1404f06fb60c608d4ad049502c6e184a85f 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -164,6 +164,7 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig as pallet_transaction_payment::DefaultConfig)] impl pallet_transaction_payment::Config for TestRuntime { type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; type OperationalFeeMultiplier = ConstU8<5>; @@ -176,7 +177,6 @@ impl pallet_transaction_payment::Config for TestRuntime { MinimumMultiplier, MaximumMultiplier, >; - type RuntimeEvent = RuntimeEvent; } impl pallet_bridge_grandpa::Config for TestRuntime { diff --git a/bridges/bin/runtime-common/src/priority_calculator.rs b/bridges/bin/runtime-common/src/priority_calculator.rs index a597fb9e2f49289360acfd7ee305b44eb7874a3e..0c53018330ea0ebc2fbacb32808e01a9ec88960f 100644 --- a/bridges/bin/runtime-common/src/priority_calculator.rs +++ b/bridges/bin/runtime-common/src/priority_calculator.rs @@ -169,12 +169,15 @@ mod integrity_tests { // nodes to the proof (x0.5 because we expect some nodes to be reused) let estimated_message_size = 512; // let's say all our messages have the same dispatch weight - let estimated_message_dispatch_weight = - Runtime::WeightInfo::message_dispatch_weight(estimated_message_size); + let estimated_message_dispatch_weight = >::WeightInfo::message_dispatch_weight( + estimated_message_size + ); // messages proof argument size is (for every message) messages size + some additional // trie nodes. Some of them are reused by different messages, so let's take 2/3 of default // "overhead" constant - let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size() + let messages_proof_size = >::WeightInfo::expected_extra_storage_proof_size() .saturating_mul(2) .saturating_div(3) .saturating_add(estimated_message_size) @@ -182,7 +185,7 @@ mod integrity_tests { // finally we are able to estimate transaction size and weight let transaction_size = base_tx_size.saturating_add(messages_proof_size); - let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight( + let transaction_weight = >::WeightInfo::receive_messages_proof_weight( &PreComputedSize(transaction_size as _), messages as _, estimated_message_dispatch_weight.saturating_mul(messages), diff --git a/bridges/bin/runtime-common/src/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/refund_relayer_extension.rs index 27b7ff1a5519b70a35c304b96b0c25108155aa46..b912f8445ac1b455e95110d41347ac8e55afaa7c 100644 --- a/bridges/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bridges/bin/runtime-common/src/refund_relayer_extension.rs @@ -48,9 +48,12 @@ use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTra 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::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, Get, PostDispatchInfoOf, + TransactionExtension, TransactionExtensionBase, ValidateResult, Zero, + }, transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + InvalidTransaction, TransactionPriority, TransactionValidityError, ValidTransactionBuilder, }, DispatchResult, FixedPointOperand, RuntimeDebug, }; @@ -195,6 +198,19 @@ impl CallInfo { } } + /// Returns mutable reference to pre-dispatch `finality_target` sent to the + /// `SubmitFinalityProof` call. + #[cfg(test)] + 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), + _ => None, + } + } + /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { match self { @@ -226,8 +242,8 @@ pub enum RelayerAccountAction { Slash(AccountId, RewardsAccountParams), } -/// Everything common among our refund signed extensions. -pub trait RefundSignedExtension: +/// Everything common among our refund transaction extensions. +pub trait RefundTransactionExtension: 'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo where >::BridgedChain: @@ -443,8 +459,8 @@ where } } -/// Adapter that allow implementing `sp_runtime::traits::SignedExtension` for any -/// `RefundSignedExtension`. +/// Adapter that allow implementing `sp_runtime::traits::TransactionExtension` for any +/// `RefundTransactionExtension`. #[derive( DefaultNoBound, CloneNoBound, @@ -455,12 +471,13 @@ where RuntimeDebugNoBound, TypeInfo, )] -pub struct RefundSignedExtensionAdapter(T) +pub struct RefundTransactionExtensionAdapter(T) where >::BridgedChain: Chain; -impl SignedExtension for RefundSignedExtensionAdapter +impl TransactionExtensionBase + for RefundTransactionExtensionAdapter where >::BridgedChain: Chain, @@ -470,22 +487,35 @@ where + MessagesCallSubType::Instance>, { const IDENTIFIER: &'static str = T::Id::STR; - type AccountId = AccountIdOf; - type Call = CallOf; - type AdditionalSigned = (); - type Pre = Option>>; + type Implicit = (); +} - fn additional_signed(&self) -> Result<(), TransactionValidityError> { - Ok(()) - } +impl TransactionExtension, Context> + for RefundTransactionExtensionAdapter +where + >::BridgedChain: + Chain, + CallOf: Dispatchable + + IsSubType, T::Runtime>> + + GrandpaCallSubType + + MessagesCallSubType::Instance>, + as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner> + Clone, +{ + type Pre = Option>>; + type Val = Option; fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: as Dispatchable>::RuntimeOrigin, + call: &CallOf, + _info: &DispatchInfoOf>, _len: usize, - ) -> TransactionValidity { + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult> { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; // this is the only relevant line of code for the `pre_dispatch` // // we're not calling `validate` from `pre_dispatch` directly because of performance @@ -498,12 +528,12 @@ where // we only boost priority of presumably correct message delivery transactions let bundled_messages = match T::bundled_messages_for_priority_boost(parsed_call.as_ref()) { Some(bundled_messages) => bundled_messages, - None => return Ok(Default::default()), + None => return Ok((Default::default(), parsed_call, origin)), }; // we only boost priority if relayer has staked required balance if !RelayersPallet::::is_registration_active(who) { - return Ok(Default::default()) + return Ok((Default::default(), parsed_call, origin)) } // compute priority boost @@ -522,20 +552,21 @@ where priority_boost, ); - valid_transaction.build() + let validity = valid_transaction.build()?; + Ok((validity, parsed_call, origin)) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + val: Self::Val, + origin: & as Dispatchable>::RuntimeOrigin, + _call: &CallOf, + _info: &DispatchInfoOf>, _len: usize, + _context: &Context, ) -> 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)?; - - Ok(parsed_call.map(|call_info| { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + Ok(val.map(|call_info| { log::trace!( target: "runtime::bridge", "{} via {:?} parsed bridge transaction in pre-dispatch: {:?}", @@ -548,13 +579,14 @@ where } fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + pre: Self::Pre, + info: &DispatchInfoOf>, + post_info: &PostDispatchInfoOf>, len: usize, result: &DispatchResult, + _context: &Context, ) -> Result<(), TransactionValidityError> { - let call_result = T::analyze_call_result(pre, info, post_info, len, result); + let call_result = T::analyze_call_result(Some(pre), info, post_info, len, result); match call_result { RelayerAccountAction::None => (), @@ -582,7 +614,7 @@ where } } -/// Signed extension that refunds a relayer for new messages coming from a parachain. +/// Transaction 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 @@ -623,7 +655,7 @@ pub struct RefundBridgedParachainMessages, ); -impl RefundSignedExtension +impl RefundTransactionExtension for RefundBridgedParachainMessages where Self: 'static + Send + Sync, @@ -717,13 +749,13 @@ where } } -/// Signed extension that refunds a relayer for new messages coming from a standalone (GRANDPA) +/// Transaction 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. +/// 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( @@ -758,7 +790,7 @@ pub struct RefundBridgedGrandpaMessages, ); -impl RefundSignedExtension +impl RefundTransactionExtension for RefundBridgedGrandpaMessages where Self: 'static + Send + Sync, @@ -844,7 +876,7 @@ mod tests { use bp_parachains::{BestParaHeadHash, ParaInfo}; use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; use bp_runtime::{BasicOperatingMode, HeaderId}; - use bp_test_utils::{make_default_justification, test_keyring}; + use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; use frame_support::{ assert_storage_noop, parameter_types, traits::{fungible::Mutate, ReservableCurrency}, @@ -856,8 +888,8 @@ mod tests { Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, }; use sp_runtime::{ - traits::{ConstU64, Header as HeaderT}, - transaction_validity::{InvalidTransaction, ValidTransaction}, + traits::{ConstU64, DispatchTransaction, Header as HeaderT}, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, DispatchError, }; @@ -886,7 +918,7 @@ mod tests { ConstU64<1>, StrTestExtension, >; - type TestGrandpaExtension = RefundSignedExtensionAdapter; + type TestGrandpaExtension = RefundTransactionExtensionAdapter; type TestExtensionProvider = RefundBridgedParachainMessages< TestRuntime, DefaultRefundableParachainId<(), TestParachain>, @@ -895,7 +927,7 @@ mod tests { ConstU64<1>, StrTestExtension, >; - type TestExtension = RefundSignedExtensionAdapter; + type TestExtension = RefundTransactionExtensionAdapter; fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { let test_stake: ThisChainBalance = TestStake::get(); @@ -929,7 +961,7 @@ mod tests { let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); pallet_bridge_grandpa::CurrentAuthoritySet::::put( - StoredAuthoritySet::try_new(authorities, 0).unwrap(), + StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), ); pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); @@ -977,6 +1009,23 @@ mod tests { }) } + fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> 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_ex { + finality_target: Box::new(relay_header), + justification: relay_justification, + current_set_id: TEST_GRANDPA_SET_ID, + }) + } + fn submit_parachain_head_call( parachain_head_at_relay_header_number: RelayBlockNumber, ) -> RuntimeCall { @@ -1059,6 +1108,18 @@ mod tests { }) } + fn relay_finality_and_delivery_batch_call_ex( + relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call_ex(relay_header_number), + message_delivery_call(best_message), + ], + }) + } + fn relay_finality_and_confirmation_batch_call( relay_header_number: RelayBlockNumber, best_message: MessageNonce, @@ -1071,6 +1132,18 @@ mod tests { }) } + fn relay_finality_and_confirmation_batch_call_ex( + relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call_ex(relay_header_number), + message_confirmation_call(best_message), + ], + }) + } + fn all_finality_and_delivery_batch_call( relay_header_number: RelayBlockNumber, parachain_head_at_relay_header_number: RelayBlockNumber, @@ -1085,6 +1158,20 @@ mod tests { }) } + fn all_finality_and_delivery_batch_call_ex( + relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call_ex(relay_header_number), + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_delivery_call(best_message), + ], + }) + } + fn all_finality_and_confirmation_batch_call( relay_header_number: RelayBlockNumber, parachain_head_at_relay_header_number: RelayBlockNumber, @@ -1099,12 +1186,27 @@ mod tests { }) } + fn all_finality_and_confirmation_batch_call_ex( + relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call_ex(relay_header_number), + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_confirmation_call(best_message), + ], + }) + } + fn all_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), call_info: CallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, + current_set_id: None, extra_weight: Weight::zero(), extra_size: 0, }, @@ -1128,12 +1230,20 @@ mod tests { } } + 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 + } + fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), call_info: CallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, + current_set_id: None, extra_weight: Weight::zero(), extra_size: 0, }, @@ -1153,12 +1263,20 @@ mod tests { } } + 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 + } + fn relay_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), call_info: CallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, + current_set_id: None, extra_weight: Weight::zero(), extra_size: 0, }, @@ -1177,12 +1295,20 @@ mod tests { } } + 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 + } + fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), call_info: CallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, + current_set_id: None, extra_weight: Weight::zero(), extra_size: 0, }, @@ -1197,6 +1323,13 @@ mod tests { } } + 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 + } + fn parachain_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), @@ -1293,14 +1426,28 @@ mod tests { fn run_validate(call: RuntimeCall) -> TransactionValidity { let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + RefundTransactionExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|res| res.0) } fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + RefundTransactionExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|res| res.0) } fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity { @@ -1314,16 +1461,30 @@ mod tests { call: RuntimeCall, ) -> Result>, TransactionValidityError> { let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + RefundTransactionExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + extension + .validate_and_prepare( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|(pre, _)| pre) } fn run_grandpa_pre_dispatch( call: RuntimeCall, ) -> Result>, TransactionValidityError> { let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + RefundTransactionExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + extension + .validate_and_prepare( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|(pre, _)| pre) } fn dispatch_info() -> DispatchInfo { @@ -1346,11 +1507,12 @@ mod tests { dispatch_result: DispatchResult, ) { let post_dispatch_result = TestExtension::post_dispatch( - Some(pre_dispatch_data), + pre_dispatch_data, &dispatch_info(), &post_dispatch_info(), 1024, &dispatch_result, + &(), ); assert_eq!(post_dispatch_result, Ok(())); } @@ -1393,6 +1555,10 @@ mod tests { run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(Default::default()), ); + assert_eq!( + run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 200)), + Ok(Default::default()), + ); // message confirmation validation is passing assert_eq!( run_validate_ignore_priority(message_confirmation_call(200)), @@ -1410,6 +1576,12 @@ mod tests { )), Ok(Default::default()), ); + assert_eq!( + run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex( + 200, 200, 200 + )), + Ok(Default::default()), + ); }); } @@ -1500,12 +1672,24 @@ mod tests { run_validate_ignore_priority(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(ValidTransaction::default()), ); + assert_eq!( + run_validate_ignore_priority(all_finality_and_delivery_batch_call_ex( + 200, 200, 200 + )), + Ok(ValidTransaction::default()), + ); assert_eq!( run_validate_ignore_priority(all_finality_and_confirmation_batch_call( 200, 200, 200 )), Ok(ValidTransaction::default()), ); + assert_eq!( + run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex( + 200, 200, 200 + )), + Ok(ValidTransaction::default()), + ); }); } @@ -1518,11 +1702,19 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call_ex(100, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_validate(all_finality_and_delivery_batch_call(100, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_validate(all_finality_and_delivery_batch_call_ex(100, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); }); } @@ -1535,10 +1727,18 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call_ex(101, 100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_validate(all_finality_and_delivery_batch_call(101, 100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_validate(all_finality_and_delivery_batch_call_ex(101, 100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)), @@ -1560,19 +1760,35 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_validate(all_finality_and_delivery_batch_call(200, 200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_validate(all_finality_and_confirmation_batch_call(200, 200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_validate(all_finality_and_confirmation_batch_call_ex(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)), @@ -1609,10 +1825,18 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); assert_eq!( run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); }); } @@ -1631,10 +1855,18 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); assert_eq!( run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); assert_eq!( run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), @@ -1662,10 +1894,18 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); assert_eq!( run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), + ); assert_eq!( run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), @@ -1696,10 +1936,18 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(Some(all_finality_pre_dispatch_data())), ); + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), + Ok(Some(all_finality_pre_dispatch_data_ex())), + ); assert_eq!( run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), Ok(Some(all_finality_confirmation_pre_dispatch_data())), ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), + Ok(Some(all_finality_confirmation_pre_dispatch_data_ex())), + ); }); } @@ -2126,6 +2374,12 @@ mod tests { ), Ok(None), ); + assert_eq!( + TestGrandpaExtensionProvider::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!( @@ -2134,6 +2388,12 @@ mod tests { ), Ok(None), ); + assert_eq!( + TestGrandpaExtensionProvider::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!( @@ -2158,6 +2418,12 @@ mod tests { ), Ok(Some(relay_finality_pre_dispatch_data().call_info)), ); + assert_eq!( + TestGrandpaExtensionProvider::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)), + ); // relay + message confirmation call batch is accepted assert_eq!( @@ -2166,6 +2432,12 @@ mod tests { ), Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), ); + assert_eq!( + TestGrandpaExtensionProvider::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)), + ); // message delivery call batch is accepted assert_eq!( @@ -2194,11 +2466,19 @@ mod tests { run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_grandpa_validate(relay_finality_and_delivery_batch_call(100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); }); } @@ -2211,19 +2491,35 @@ mod tests { run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_grandpa_pre_dispatch(message_delivery_call(100)), @@ -2254,19 +2550,35 @@ mod tests { run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 200)), Ok(Some(relay_finality_pre_dispatch_data()),) ); + assert_eq!( + run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 200)), + Ok(Some(relay_finality_pre_dispatch_data_ex()),) + ); assert_eq!( run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 200)), Ok(Some(relay_finality_confirmation_pre_dispatch_data())), ); + assert_eq!( + run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 200)), + Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex())), + ); assert_eq!( run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 200)), Ok(Default::default()), ); + assert_eq!( + run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 200)), + Ok(Default::default()), + ); assert_eq!( run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 200)), Ok(Default::default()), ); + assert_eq!( + run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 200)), + Ok(Default::default()), + ); assert_eq!( run_grandpa_pre_dispatch(message_delivery_call(200)), diff --git a/bridges/modules/grandpa/Cargo.toml b/bridges/modules/grandpa/Cargo.toml index e346f2061e2e59d8cef9075ef164e6b203068a90..dccd7b3bdca3533cda4fec82ed0266d0b221b7a7 100644 --- a/bridges/modules/grandpa/Cargo.toml +++ b/bridges/modules/grandpa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-bridge-grandpa" -version = "0.1.0" +version = "0.7.0" description = "Module implementing GRANDPA on-chain light client used for bridging consensus of substrate-based chains." authors.workspace = true edition.workspace = true @@ -14,7 +14,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/bridges/modules/grandpa/README.md b/bridges/modules/grandpa/README.md index 27b4d2389c4085538362e6aea17303c44ef50795..43ee5c316d1b76ec8fc94b0c3819b1340a6ce75c 100644 --- a/bridges/modules/grandpa/README.md +++ b/bridges/modules/grandpa/README.md @@ -38,11 +38,11 @@ There are two main things in GRANDPA that help building light clients: ## Pallet Operations -The main entrypoint of the pallet is the `submit_finality_proof` call. It has two arguments - the finalized -headers and associated GRANDPA justification. The call simply verifies the justification using current -validators set and checks if header is better than the previous best header. If both checks are passed, the -header (only its useful fields) is inserted into the runtime storage and may be used by other pallets to verify -storage proofs. +The main entrypoint of the pallet is the `submit_finality_proof_ex` call. It has three arguments - the finalized +headers, associated GRANDPA justification and ID of the authority set that has generated this justification. The +call simply verifies the justification using current validators set and checks if header is better than the +previous best header. If both checks are passed, the header (only its useful fields) is inserted into the runtime +storage and may be used by other pallets to verify storage proofs. The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is required for the pallet operations, submitting such header is free. So if you're ok with session-length diff --git a/bridges/modules/grandpa/src/benchmarking.rs b/bridges/modules/grandpa/src/benchmarking.rs index 182b2f56eb1c57a165cf2eb1e86b585d70fd1801..11033373ce478fa9fefb613a1377449bb77daf1d 100644 --- a/bridges/modules/grandpa/src/benchmarking.rs +++ b/bridges/modules/grandpa/src/benchmarking.rs @@ -16,8 +16,9 @@ //! Benchmarks for the GRANDPA Pallet. //! -//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof`, so these benchmarks are -//! based around that. There are to main factors which affect finality proof verification: +//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof_ex`. Our benchmarks +//! are based around `submit_finality_proof`, though - from weight PoV they are the same calls. +//! There are to main factors which affect finality proof verification: //! //! 1. The number of `votes-ancestries` in the justification //! 2. The number of `pre-commits` in the justification diff --git a/bridges/modules/grandpa/src/call_ext.rs b/bridges/modules/grandpa/src/call_ext.rs index c1585020be13ca710178b59aefde4a0cde2ab87a..e3c778b480baa51a8b9e5d04564ac54bc7a68a21 100644 --- a/bridges/modules/grandpa/src/call_ext.rs +++ b/bridges/modules/grandpa/src/call_ext.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use crate::{weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, Error, Pallet}; +use crate::{ + weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, CurrentAuthoritySet, Error, + Pallet, +}; use bp_header_chain::{ justification::GrandpaJustification, max_expected_submit_finality_proof_arguments_size, ChainWithGrandpa, GrandpaConsensusLogReader, @@ -22,6 +25,7 @@ use bp_header_chain::{ use bp_runtime::{BlockNumberOf, OwnedBridgeModule}; use codec::Encode; use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight}; +use sp_consensus_grandpa::SetId; use sp_runtime::{ traits::{Header, Zero}, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, @@ -33,6 +37,9 @@ use sp_runtime::{ 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, /// Extra weight that we assume is included in the call. /// /// We have some assumptions about headers and justifications of the bridged chain. @@ -61,9 +68,11 @@ pub struct SubmitFinalityProofHelper, I: 'static> { impl, I: 'static> SubmitFinalityProofHelper { /// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best - /// one we know. + /// one we know. Additionally, checks if `current_set_id` matches the current authority set + /// id, if specified. pub fn check_obsolete( finality_target: BlockNumberOf, + current_set_id: Option, ) -> Result<(), Error> { let best_finalized = crate::BestFinalized::::get().ok_or_else(|| { log::trace!( @@ -85,6 +94,20 @@ impl, I: 'static> SubmitFinalityProofHelper { return Err(Error::::OldHeader) } + if let Some(current_set_id) = current_set_id { + let actual_set_id = >::get().set_id; + if current_set_id != actual_set_id { + log::trace!( + target: crate::LOG_TARGET, + "Cannot finalize header signed by unknown authority set: bundled {:?}, best {:?}", + current_set_id, + actual_set_id, + ); + + return Err(Error::::InvalidAuthoritySetId) + } + } + Ok(()) } @@ -111,6 +134,18 @@ pub trait CallSubType, I: 'static>: return Some(submit_finality_proof_info_from_args::( finality_target, justification, + None, + )) + } else if let Some(crate::Call::::submit_finality_proof_ex { + finality_target, + justification, + current_set_id, + }) = self.is_sub_type() + { + return Some(submit_finality_proof_info_from_args::( + finality_target, + justification, + Some(*current_set_id), )) } @@ -133,7 +168,10 @@ pub trait CallSubType, I: 'static>: return InvalidTransaction::Call.into() } - match SubmitFinalityProofHelper::::check_obsolete(finality_target.block_number) { + match SubmitFinalityProofHelper::::check_obsolete( + finality_target.block_number, + finality_target.current_set_id, + ) { Ok(_) => Ok(ValidTransaction::default()), Err(Error::::OldHeader) => InvalidTransaction::Stale.into(), Err(_) => InvalidTransaction::Call.into(), @@ -150,6 +188,7 @@ impl, I: 'static> CallSubType for T::RuntimeCall where pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( finality_target: &BridgedHeader, justification: &GrandpaJustification>, + current_set_id: Option, ) -> SubmitFinalityProofInfo> { let block_number = *finality_target.number(); @@ -191,7 +230,7 @@ pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( ); let extra_size = actual_call_size.saturating_sub(max_expected_call_size); - SubmitFinalityProofInfo { block_number, extra_weight, extra_size } + SubmitFinalityProofInfo { block_number, current_set_id, extra_weight, extra_size } } #[cfg(test)] @@ -199,20 +238,24 @@ mod tests { use crate::{ call_ext::CallSubType, mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime}, - BestFinalized, Config, PalletOperatingMode, WeightInfo, + BestFinalized, Config, CurrentAuthoritySet, PalletOperatingMode, StoredAuthoritySet, + SubmitFinalityProofInfo, WeightInfo, }; use bp_header_chain::ChainWithGrandpa; use bp_runtime::{BasicOperatingMode, HeaderId}; use bp_test_utils::{ make_default_justification, make_justification_for_header, JustificationGeneratorParams, + TEST_GRANDPA_SET_ID, }; use frame_support::weights::Weight; use sp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion}; fn validate_block_submit(num: TestNumber) -> bool { - let bridge_grandpa_call = crate::Call::::submit_finality_proof { + let bridge_grandpa_call = crate::Call::::submit_finality_proof_ex { finality_target: Box::new(test_header(num)), justification: make_default_justification(&test_header(num)), + // not initialized => zero + current_set_id: 0, }; RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa( bridge_grandpa_call, @@ -256,6 +299,18 @@ mod tests { }); } + #[test] + fn extension_rejects_new_header_if_set_id_is_invalid() { + run_test(|| { + // when set id is different from the passed one => tx is rejected + sync_to_header_10(); + let next_set = StoredAuthoritySet::::try_new(vec![], 0x42).unwrap(); + CurrentAuthoritySet::::put(next_set); + + assert!(!validate_block_submit(15)); + }); + } + #[test] fn extension_accepts_new_header() { run_test(|| { @@ -266,6 +321,42 @@ mod tests { }); } + #[test] + fn submit_finality_proof_info_is_parsed() { + // when `submit_finality_proof` is used, `current_set_id` is set to `None` + let deprecated_call = + RuntimeCall::Grandpa(crate::Call::::submit_finality_proof { + finality_target: Box::new(test_header(42)), + justification: make_default_justification(&test_header(42)), + }); + assert_eq!( + deprecated_call.submit_finality_proof_info(), + Some(SubmitFinalityProofInfo { + block_number: 42, + current_set_id: None, + extra_weight: Weight::zero(), + extra_size: 0, + }) + ); + + // when `submit_finality_proof_ex` is used, `current_set_id` is set to `Some` + let deprecated_call = + RuntimeCall::Grandpa(crate::Call::::submit_finality_proof_ex { + finality_target: Box::new(test_header(42)), + justification: make_default_justification(&test_header(42)), + current_set_id: 777, + }); + assert_eq!( + deprecated_call.submit_finality_proof_info(), + Some(SubmitFinalityProofInfo { + block_number: 42, + current_set_id: Some(777), + extra_weight: Weight::zero(), + extra_size: 0, + }) + ); + } + #[test] fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() { // when call arguments are below our limit => no refund @@ -275,9 +366,10 @@ mod tests { ..Default::default() }; let small_justification = make_justification_for_header(justification_params); - let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { finality_target: Box::new(small_finality_target), justification: small_justification, + current_set_id: TEST_GRANDPA_SET_ID, }); assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0); @@ -291,9 +383,10 @@ mod tests { ..Default::default() }; let large_justification = make_justification_for_header(justification_params); - let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { finality_target: Box::new(large_finality_target), justification: large_justification, + current_set_id: TEST_GRANDPA_SET_ID, }); assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0); } @@ -309,9 +402,10 @@ mod tests { // when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY` headers => no refund let justification = make_justification_for_header(justification_params.clone()); - let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { finality_target: Box::new(finality_target.clone()), justification, + current_set_id: TEST_GRANDPA_SET_ID, }); assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero()); @@ -322,9 +416,10 @@ mod tests { justification.commit.precommits.len().saturated_into(), justification.votes_ancestries.len().saturated_into(), ); - let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { finality_target: Box::new(finality_target), justification, + current_set_id: TEST_GRANDPA_SET_ID, }); assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight); } diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index f58db2481ada11e29e3cfe40315d95f468aa82cf..ce2c47da954fa46efc4c70e9608864735fa16277 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -151,7 +151,86 @@ pub mod pallet { #[pallet::call] impl, I: 'static> Pallet { - /// Verify a target header is finalized according to the given finality proof. + /// This call is deprecated and will be removed around May 2024. Use the + /// `submit_finality_proof_ex` instead. Semantically, this call is an equivalent of the + /// `submit_finality_proof_ex` call without current authority set id check. + #[pallet::call_index(0)] + #[pallet::weight(::submit_finality_proof( + justification.commit.precommits.len().saturated_into(), + justification.votes_ancestries.len().saturated_into(), + ))] + #[allow(deprecated)] + #[deprecated( + note = "`submit_finality_proof` will be removed in May 2024. Use `submit_finality_proof_ex` instead." + )] + pub fn submit_finality_proof( + origin: OriginFor, + finality_target: Box>, + justification: GrandpaJustification>, + ) -> DispatchResultWithPostInfo { + Self::submit_finality_proof_ex( + origin, + finality_target, + justification, + // the `submit_finality_proof_ex` also reads this value, but it is done from the + // cache, so we don't treat it as an additional db access + >::get().set_id, + ) + } + + /// Bootstrap the bridge pallet with an initial header and authority set from which to sync. + /// + /// The initial configuration provided does not need to be the genesis header of the bridged + /// chain, it can be any arbitrary header. You can also provide the next scheduled set + /// change if it is already know. + /// + /// This function is only allowed to be called from a trusted origin and writes to storage + /// with practically no checks in terms of the validity of the data. It is important that + /// you ensure that valid data is being passed in. + #[pallet::call_index(1)] + #[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))] + pub fn initialize( + origin: OriginFor, + init_data: super::InitializationData>, + ) -> DispatchResultWithPostInfo { + Self::ensure_owner_or_root(origin)?; + + let init_allowed = !>::exists(); + ensure!(init_allowed, >::AlreadyInitialized); + initialize_bridge::(init_data.clone())?; + + log::info!( + target: LOG_TARGET, + "Pallet has been initialized with the following parameters: {:?}", + init_data + ); + + Ok(().into()) + } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(2)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(3)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + + /// Verify a target header is finalized according to the given finality proof. The proof + /// is assumed to be signed by GRANDPA authorities set with `current_set_id` id. /// /// It will use the underlying storage pallet to fetch information about the current /// authorities and best finalized header in order to verify that the header is finalized. @@ -165,18 +244,22 @@ pub mod pallet { /// /// - the pallet knows better header than the `finality_target`; /// + /// - the id of best GRANDPA authority set, known to the pallet is not equal to the + /// `current_set_id`; + /// /// - verification is not optimized or invalid; /// /// - header contains forced authorities set change or change with non-zero delay. - #[pallet::call_index(0)] + #[pallet::call_index(4)] #[pallet::weight(::submit_finality_proof( justification.commit.precommits.len().saturated_into(), justification.votes_ancestries.len().saturated_into(), ))] - pub fn submit_finality_proof( + pub fn submit_finality_proof_ex( origin: OriginFor, finality_target: Box>, justification: GrandpaJustification>, + current_set_id: sp_consensus_grandpa::SetId, ) -> DispatchResultWithPostInfo { Self::ensure_not_halted().map_err(Error::::BridgeModule)?; ensure_signed(origin)?; @@ -188,7 +271,9 @@ pub mod pallet { finality_target ); - SubmitFinalityProofHelper::::check_obsolete(number)?; + // it checks whether the `number` is better than the current best block number + // and whether the `current_set_id` matches the best known set id + SubmitFinalityProofHelper::::check_obsolete(number, Some(current_set_id))?; let authority_set = >::get(); let unused_proof_size = authority_set.unused_proof_size(); @@ -202,7 +287,7 @@ pub mod pallet { // if we have seen too many mandatory headers in this block, we don't want to refund Self::free_mandatory_headers_remaining() > 0 && // if arguments out of expected bounds, we don't want to refund - submit_finality_proof_info_from_args::(&finality_target, &justification) + submit_finality_proof_info_from_args::(&finality_target, &justification, Some(current_set_id)) .fits_limits(); if may_refund_call_fee { FreeMandatoryHeadersRemaining::::mutate(|count| { @@ -248,57 +333,6 @@ pub mod pallet { Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) } - - /// Bootstrap the bridge pallet with an initial header and authority set from which to sync. - /// - /// The initial configuration provided does not need to be the genesis header of the bridged - /// chain, it can be any arbitrary header. You can also provide the next scheduled set - /// change if it is already know. - /// - /// This function is only allowed to be called from a trusted origin and writes to storage - /// with practically no checks in terms of the validity of the data. It is important that - /// you ensure that valid data is being passed in. - #[pallet::call_index(1)] - #[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))] - pub fn initialize( - origin: OriginFor, - init_data: super::InitializationData>, - ) -> DispatchResultWithPostInfo { - Self::ensure_owner_or_root(origin)?; - - let init_allowed = !>::exists(); - ensure!(init_allowed, >::AlreadyInitialized); - initialize_bridge::(init_data.clone())?; - - log::info!( - target: LOG_TARGET, - "Pallet has been initialized with the following parameters: {:?}", - init_data - ); - - Ok(().into()) - } - - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(2)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { - >::set_owner(origin, new_owner) - } - - /// Halt or resume all pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(3)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operating_mode( - origin: OriginFor, - operating_mode: BasicOperatingMode, - ) -> DispatchResult { - >::set_operating_mode(origin, operating_mode) - } } /// Number mandatory headers that we may accept in the current block for free (returning @@ -436,6 +470,9 @@ pub mod pallet { TooManyAuthoritiesInSet, /// Error generated by the `OwnedBridgeModule` trait. BridgeModule(bp_runtime::OwnedBridgeModuleError), + /// The `current_set_id` argument of the `submit_finality_proof_ex` doesn't match + /// the id of the current set, known to the pallet. + InvalidAuthoritySetId, } /// Check the given header for a GRANDPA scheduled authority set change. If a change @@ -663,6 +700,7 @@ mod tests { use bp_test_utils::{ authority_list, generate_owned_bridge_module_tests, make_default_justification, make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, + TEST_GRANDPA_SET_ID, }; use codec::Encode; use frame_support::{ @@ -693,7 +731,7 @@ mod tests { let init_data = InitializationData { header: Box::new(genesis), authority_list: authority_list(), - set_id: 1, + set_id: TEST_GRANDPA_SET_ID, operating_mode: BasicOperatingMode::Normal, }; @@ -704,10 +742,11 @@ mod tests { let header = test_header(header.into()); let justification = make_default_justification(&header); - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), justification, + TEST_GRANDPA_SET_ID, ) } @@ -722,10 +761,11 @@ mod tests { ..Default::default() }); - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), justification, + set_id, ) } @@ -749,10 +789,11 @@ mod tests { ..Default::default() }); - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), justification, + set_id, ) } @@ -955,17 +996,30 @@ mod tests { let header = test_header(1); - let params = - JustificationGeneratorParams:: { set_id: 2, ..Default::default() }; + let next_set_id = 2; + let params = JustificationGeneratorParams:: { + set_id: next_set_id, + ..Default::default() + }; let justification = make_justification_for_header(params); assert_err!( - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification.clone(), + TEST_GRANDPA_SET_ID, + ), + >::InvalidJustification + ); + assert_err!( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), justification, + next_set_id, ), - >::InvalidJustification + >::InvalidAuthoritySetId ); }) } @@ -980,10 +1034,11 @@ mod tests { justification.round = 42; assert_err!( - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), justification, + TEST_GRANDPA_SET_ID, ), >::InvalidJustification ); @@ -1009,10 +1064,11 @@ mod tests { let justification = make_default_justification(&header); assert_err!( - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), justification, + TEST_GRANDPA_SET_ID, ), >::InvalidAuthoritySet ); @@ -1047,10 +1103,11 @@ mod tests { let justification = make_default_justification(&header); // Let's import our test header - let result = Pallet::::submit_finality_proof( + let result = Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header.clone()), justification.clone(), + TEST_GRANDPA_SET_ID, ); assert_ok!(result); assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No); @@ -1109,10 +1166,11 @@ mod tests { // without large digest item ^^^ the relayer would have paid zero transaction fee // (`Pays::No`) - let result = Pallet::::submit_finality_proof( + let result = Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header.clone()), justification, + TEST_GRANDPA_SET_ID, ); assert_ok!(result); assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); @@ -1140,10 +1198,11 @@ mod tests { // without many headers in votes ancestries ^^^ the relayer would have paid zero // transaction fee (`Pays::No`) - let result = Pallet::::submit_finality_proof( + let result = Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header.clone()), justification, + TEST_GRANDPA_SET_ID, ); assert_ok!(result); assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); @@ -1169,10 +1228,11 @@ mod tests { // Should not be allowed to import this header assert_err!( - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), - justification + justification, + TEST_GRANDPA_SET_ID, ), >::UnsupportedScheduledChange ); @@ -1194,10 +1254,11 @@ mod tests { // Should not be allowed to import this header assert_err!( - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), - justification + justification, + TEST_GRANDPA_SET_ID, ), >::UnsupportedScheduledChange ); @@ -1219,10 +1280,11 @@ mod tests { // Should not be allowed to import this header assert_err!( - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), - justification + justification, + TEST_GRANDPA_SET_ID, ), >::TooManyAuthoritiesInSet ); @@ -1283,10 +1345,11 @@ mod tests { let mut invalid_justification = make_default_justification(&header); invalid_justification.round = 42; - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), invalid_justification, + TEST_GRANDPA_SET_ID, ) }; @@ -1451,10 +1514,11 @@ mod tests { let justification = make_default_justification(&header); assert_noop!( - Pallet::::submit_finality_proof( + Pallet::::submit_finality_proof_ex( RuntimeOrigin::root(), Box::new(header), justification, + TEST_GRANDPA_SET_ID, ), DispatchError::BadOrigin, ); diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml index 4d9371448df8a855db986095d77584c19559379c..173d6f1c16448517b7051cfba2f96625ff3d525a 100644 --- a/bridges/modules/messages/Cargo.toml +++ b/bridges/modules/messages/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-bridge-messages" description = "Module that allows bridged chains to exchange messages using lane concept." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } num-traits = { version = "0.2", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } diff --git a/bridges/modules/parachains/Cargo.toml b/bridges/modules/parachains/Cargo.toml index 77a5366c78daedd368d9a4f412075ac803a89530..e454a6f2888fa169a0b0795101172b2f260b4020 100644 --- a/bridges/modules/parachains/Cargo.toml +++ b/bridges/modules/parachains/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-bridge-parachains" -version = "0.1.0" +version = "0.7.0" description = "Module that allows bridged relay chains to exchange information on their parachains' heads." authors.workspace = true edition.workspace = true @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 87c57e84622aa7cb5eb1019ec88a680dc06fd8ee..1363a637604d1202ffc4bf799bf7ced180d9fe53 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -731,6 +731,7 @@ pub(crate) mod tests { }; use bp_test_utils::{ authority_list, generate_owned_bridge_module_tests, make_default_justification, + TEST_GRANDPA_SET_ID, }; use frame_support::{ assert_noop, assert_ok, @@ -777,10 +778,11 @@ pub(crate) mod tests { let hash = header.hash(); let justification = make_default_justification(&header); assert_ok!( - pallet_bridge_grandpa::Pallet::::submit_finality_proof( + pallet_bridge_grandpa::Pallet::::submit_finality_proof_ex( RuntimeOrigin::signed(1), Box::new(header), justification.clone(), + TEST_GRANDPA_SET_ID, ) ); diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index 8c8305ef64c9f7e419aead17a9989063ec28d290..b78da5cbeeca65a4f448cbc38928894d51e8f7b4 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-bridge-relayers" description = "Module used to store relayer rewards and coordinate relayers set." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/bridges/modules/xcm-bridge-hub-router/Cargo.toml b/bridges/modules/xcm-bridge-hub-router/Cargo.toml index 1d84f723ee9d490359c32bef05e9c9547abeb31d..20f8ff4407b2ad9882c64b334fa557a6c7dc4ef2 100644 --- a/bridges/modules/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub-router/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-xcm-bridge-hub-router" description = "Bridge hub interface for sibling/parent chains with dynamic fees support." -version = "0.1.0" +version = "0.5.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive", "serde"] } # Bridge dependencies diff --git a/bridges/modules/xcm-bridge-hub/Cargo.toml b/bridges/modules/xcm-bridge-hub/Cargo.toml index 061d4b7ced881d6ac7638fdcc0e56bccb08a7be3..e10119e864953f1777c43151092ae43a5e594b8c 100644 --- a/bridges/modules/xcm-bridge-hub/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-xcm-bridge-hub" description = "Module that adds dynamic bridges/lanes support to XCM infrastucture at the bridge hub." -version = "0.1.0" +version = "0.2.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/bridges/primitives/chain-asset-hub-rococo/Cargo.toml b/bridges/primitives/chain-asset-hub-rococo/Cargo.toml index d5f724e581fbf44c237b4634863f0d9c2437837b..4dfa149e0ea9ab4e0ac1804844a0c128f15bd5bb 100644 --- a/bridges/primitives/chain-asset-hub-rococo/Cargo.toml +++ b/bridges/primitives/chain-asset-hub-rococo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-asset-hub-rococo" description = "Primitives of AssetHubRococo parachain runtime." -version = "0.1.0" +version = "0.4.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-asset-hub-westend/Cargo.toml b/bridges/primitives/chain-asset-hub-westend/Cargo.toml index d309e50bfbfeafbf0e32103695e004bd1bf0f7a8..c9bd437562b86a97cdf2807c18b4905e695d1a5e 100644 --- a/bridges/primitives/chain-asset-hub-westend/Cargo.toml +++ b/bridges/primitives/chain-asset-hub-westend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-asset-hub-westend" description = "Primitives of AssetHubWestend parachain runtime." -version = "0.1.0" +version = "0.3.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-bridge-hub-cumulus/Cargo.toml b/bridges/primitives/chain-bridge-hub-cumulus/Cargo.toml index 73aaa53269fee9ae6e50c240043cc125fcf44a74..d35eefa1c45c3f7cf479dcea2ee4da87dbc31627 100644 --- a/bridges/primitives/chain-bridge-hub-cumulus/Cargo.toml +++ b/bridges/primitives/chain-bridge-hub-cumulus/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-bridge-hub-cumulus" description = "Primitives for BridgeHub parachain runtimes." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs b/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs index 285f00204810bca4e392eda23f56e590ccae0401..f186f6427ae7d5cbac37c0dea528665ea474803e 100644 --- a/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -26,7 +26,7 @@ pub use bp_polkadot_core::{ }; use bp_messages::*; -use bp_polkadot_core::SuffixedCommonSignedExtension; +use bp_polkadot_core::SuffixedCommonTransactionExtension; use bp_runtime::extensions::{ BridgeRejectObsoleteHeadersAndMessages, RefundBridgedParachainMessagesSchema, }; @@ -57,6 +57,12 @@ const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(constants::WEIGHT_REF_TI .saturating_div(2) .set_proof_size(polkadot_primitives::MAX_POV_SIZE as u64); +/// We allow for 2 seconds of compute with a 6 second average block. +const MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING: Weight = Weight::from_parts( + constants::WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), + polkadot_primitives::MAX_POV_SIZE as u64, +); + /// All cumulus bridge hubs assume that about 5 percent of the block weight is consumed by /// `on_initialize` handlers. This is used to limit the maximal weight of a single extrinsic. /// @@ -96,6 +102,26 @@ parameter_types! { }) .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) .build_or_panic(); + + /// Weight limit of the Cumulus-based bridge hub blocks when async backing is enabled. + pub BlockWeightsForAsyncBacking: limits::BlockWeights = limits::BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING); + // Operational transactions have an extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING, + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); } /// Public key of the chain account that may be used to verify signatures. @@ -138,7 +164,7 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; /// Signed extension that is used by all bridge hubs. -pub type SignedExtension = SuffixedCommonSignedExtension<( +pub type TransactionExtension = SuffixedCommonTransactionExtension<( BridgeRejectObsoleteHeadersAndMessages, RefundBridgedParachainMessagesSchema, )>; diff --git a/bridges/primitives/chain-bridge-hub-kusama/Cargo.toml b/bridges/primitives/chain-bridge-hub-kusama/Cargo.toml index ea09712ae304738ec1b468317ee5aa28eb1d0cca..8d71b3f5eb7646a81af55c0030814c604ead82ef 100644 --- a/bridges/primitives/chain-bridge-hub-kusama/Cargo.toml +++ b/bridges/primitives/chain-bridge-hub-kusama/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-bridge-hub-kusama" description = "Primitives of BridgeHubKusama parachain runtime." -version = "0.1.0" +version = "0.6.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-bridge-hub-polkadot/Cargo.toml b/bridges/primitives/chain-bridge-hub-polkadot/Cargo.toml index de208895fb4362c59705a6d1f9911f534d0d3669..4e89e8a5c9a173bc9a084af9e7c609a6bae287a8 100644 --- a/bridges/primitives/chain-bridge-hub-polkadot/Cargo.toml +++ b/bridges/primitives/chain-bridge-hub-polkadot/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-bridge-hub-polkadot" description = "Primitives of BridgeHubPolkadot parachain runtime." -version = "0.1.0" +version = "0.6.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-bridge-hub-rococo/Cargo.toml b/bridges/primitives/chain-bridge-hub-rococo/Cargo.toml index 281e1f7426178c1c1924ca40e382ea63f7a75963..1643d934a982ec0795fc370b221dc35d36d3b492 100644 --- a/bridges/primitives/chain-bridge-hub-rococo/Cargo.toml +++ b/bridges/primitives/chain-bridge-hub-rococo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-bridge-hub-rococo" description = "Primitives of BridgeHubRococo parachain runtime." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs b/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs index 7b109f30fe0b9700a513282640fd2baa6cba43f5..992ef1bd7a10f5fd95d1a5b4ec1a1267543ce615 100644 --- a/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs @@ -50,7 +50,7 @@ impl Chain for BridgeHubRococo { } fn max_extrinsic_weight() -> Weight { - BlockWeights::get() + BlockWeightsForAsyncBacking::get() .get(DispatchClass::Normal) .max_extrinsic .unwrap_or(Weight::MAX) @@ -99,7 +99,7 @@ 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 = 1_640_102_205; + pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 59_034_266; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) @@ -107,5 +107,5 @@ frame_support::parameter_types! { /// 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_complex_message_confirmation_transaction` + `33%`) - pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 4_045_736_577; + pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 5_380_904_835; } diff --git a/bridges/primitives/chain-bridge-hub-westend/Cargo.toml b/bridges/primitives/chain-bridge-hub-westend/Cargo.toml index cfd0c84eeecadf8bba1263e521f7dfc0e8c7f540..32a7850c5392fd892ec40e9968fa365ec59cbba3 100644 --- a/bridges/primitives/chain-bridge-hub-westend/Cargo.toml +++ b/bridges/primitives/chain-bridge-hub-westend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-bridge-hub-westend" description = "Primitives of BridgeHubWestend parachain runtime." -version = "0.1.0" +version = "0.3.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-bridge-hub-westend/src/lib.rs b/bridges/primitives/chain-bridge-hub-westend/src/lib.rs index 83d4d6e33a75907f4400200b16cd19ed1c361ec1..4af895cc6d328bdb350fa95b0e0a74f0cc731b04 100644 --- a/bridges/primitives/chain-bridge-hub-westend/src/lib.rs +++ b/bridges/primitives/chain-bridge-hub-westend/src/lib.rs @@ -49,7 +49,7 @@ impl Chain for BridgeHubWestend { } fn max_extrinsic_weight() -> Weight { - BlockWeights::get() + BlockWeightsForAsyncBacking::get() .get(DispatchClass::Normal) .max_extrinsic .unwrap_or(Weight::MAX) @@ -90,7 +90,7 @@ 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 = 492_077_333_333; + pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 17_756_830_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_complex_message_delivery_transaction` + `33%`) diff --git a/bridges/primitives/chain-kusama/Cargo.toml b/bridges/primitives/chain-kusama/Cargo.toml index 6ca4f051f1c15d8794ed50626588a72093206038..0660f34602389d1cf9e1ec88b57d1a9aeb9a3830 100644 --- a/bridges/primitives/chain-kusama/Cargo.toml +++ b/bridges/primitives/chain-kusama/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-kusama" description = "Primitives of Kusama runtime." -version = "0.1.0" +version = "0.5.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-kusama/src/lib.rs b/bridges/primitives/chain-kusama/src/lib.rs index e3b4d0520f61c858b54d78dfa4a45f57bac411fb..253a1010e83df928cceae91a18c309959c33d94e 100644 --- a/bridges/primitives/chain-kusama/src/lib.rs +++ b/bridges/primitives/chain-kusama/src/lib.rs @@ -59,8 +59,8 @@ impl ChainWithGrandpa for Kusama { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -// The SignedExtension used by Kusama. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; +// The TransactionExtension used by Kusama. +pub use bp_polkadot_core::CommonTransactionExtension as TransactionExtension; /// Name of the parachains pallet in the Kusama runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/primitives/chain-polkadot-bulletin/Cargo.toml b/bridges/primitives/chain-polkadot-bulletin/Cargo.toml index 98633847462e4653a57cd1a282b74e43cc253511..15c824fcbdb31c2c84f63bd56d4d0b3f90efc5b1 100644 --- a/bridges/primitives/chain-polkadot-bulletin/Cargo.toml +++ b/bridges/primitives/chain-polkadot-bulletin/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-polkadot-bulletin" description = "Primitives of Polkadot Bulletin chain runtime." -version = "0.1.0" +version = "0.4.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-polkadot-bulletin/src/lib.rs b/bridges/primitives/chain-polkadot-bulletin/src/lib.rs index f2eebf9312470a42e1d3a1c7d67ab8b7a38af189..73dd122bd153869b937ed65f8e7ea7f4dde79c7c 100644 --- a/bridges/primitives/chain-polkadot-bulletin/src/lib.rs +++ b/bridges/primitives/chain-polkadot-bulletin/src/lib.rs @@ -25,7 +25,7 @@ use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, extensions::{ CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, CheckSpecVersion, CheckTxVersion, - CheckWeight, GenericSignedExtension, GenericSignedExtensionSchema, + CheckWeight, GenericTransactionExtension, GenericTransactionExtensionSchema, }, Chain, ChainId, TransactionEra, }; @@ -37,7 +37,12 @@ use frame_support::{ }; use frame_system::limits; use scale_info::TypeInfo; -use sp_runtime::{traits::DispatchInfoOf, transaction_validity::TransactionValidityError, Perbill}; +use sp_runtime::{ + impl_tx_ext_default, + traits::{Dispatchable, TransactionExtensionBase}, + transaction_validity::TransactionValidityError, + Perbill, +}; // This chain reuses most of Polkadot primitives. pub use bp_polkadot_core::{ @@ -71,10 +76,10 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; /// This signed extension is used to ensure that the chain transactions are signed by proper -pub type ValidateSigned = GenericSignedExtensionSchema<(), ()>; +pub type ValidateSigned = GenericTransactionExtensionSchema<(), ()>; /// Signed extension schema, used by Polkadot Bulletin. -pub type SignedExtensionSchema = GenericSignedExtension<( +pub type TransactionExtensionSchema = GenericTransactionExtension<( ( CheckNonZeroSender, CheckSpecVersion, @@ -87,34 +92,30 @@ pub type SignedExtensionSchema = GenericSignedExtension<( ValidateSigned, )>; -/// Signed extension, used by Polkadot Bulletin. +/// Transaction extension, used by Polkadot Bulletin. #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct SignedExtension(SignedExtensionSchema); +pub struct TransactionExtension(TransactionExtensionSchema); -impl sp_runtime::traits::SignedExtension for SignedExtension { +impl TransactionExtensionBase for TransactionExtension { const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = - ::AdditionalSigned; - type Pre = (); + type Implicit = ::Implicit; - fn additional_signed(&self) -> Result { - self.0.additional_signed() + fn implicit(&self) -> Result { + ::implicit(&self.0) } +} - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } +impl sp_runtime::traits::TransactionExtension for TransactionExtension +where + C: Dispatchable, +{ + type Pre = (); + type Val = (); + + impl_tx_ext_default!(C; Context; validate prepare); } -impl SignedExtension { +impl TransactionExtension { /// Create signed extension from its components. pub fn from_params( spec_version: u32, @@ -123,7 +124,7 @@ impl SignedExtension { genesis_hash: Hash, nonce: Nonce, ) -> Self { - Self(GenericSignedExtension::new( + Self(GenericTransactionExtension::new( ( ( (), // non-zero sender diff --git a/bridges/primitives/chain-polkadot/Cargo.toml b/bridges/primitives/chain-polkadot/Cargo.toml index 361901b7ae09c121d27329b2f2a238dc61dd7f2e..6421b7f40106e404eeba04be72f5448fd5f65159 100644 --- a/bridges/primitives/chain-polkadot/Cargo.toml +++ b/bridges/primitives/chain-polkadot/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-polkadot" description = "Primitives of Polkadot runtime." -version = "0.1.0" +version = "0.5.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-polkadot/src/lib.rs b/bridges/primitives/chain-polkadot/src/lib.rs index fc5e10308a8e33463a74c041f157daaef09cc9c8..e5e2e7c3a042abddd8fb3dbd6f1decf62529cfe3 100644 --- a/bridges/primitives/chain-polkadot/src/lib.rs +++ b/bridges/primitives/chain-polkadot/src/lib.rs @@ -61,8 +61,8 @@ impl ChainWithGrandpa for Polkadot { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -/// The SignedExtension used by Polkadot. -pub type SignedExtension = SuffixedCommonSignedExtension; +/// The TransactionExtension used by Polkadot. +pub type TransactionExtension = SuffixedCommonTransactionExtension; /// Name of the parachains pallet in the Polkadot runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/primitives/chain-rococo/Cargo.toml b/bridges/primitives/chain-rococo/Cargo.toml index d59a00cfd147d4e5d3110e44cab906a72836a06a..de373f0ae64b8d9a8124dbcdfcca3c2bf05e2787 100644 --- a/bridges/primitives/chain-rococo/Cargo.toml +++ b/bridges/primitives/chain-rococo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-rococo" description = "Primitives of Rococo runtime." -version = "0.1.0" +version = "0.6.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-rococo/src/lib.rs b/bridges/primitives/chain-rococo/src/lib.rs index f1b256f0f090f048cc8db3a16c112ed8b938f6ce..267c6b2b1f0292b64ca8aaf969845129dae64dd8 100644 --- a/bridges/primitives/chain-rococo/src/lib.rs +++ b/bridges/primitives/chain-rococo/src/lib.rs @@ -59,8 +59,8 @@ impl ChainWithGrandpa for Rococo { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -// The SignedExtension used by Rococo. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; +// The TransactionExtension used by Rococo. +pub use bp_polkadot_core::CommonTransactionExtension as TransactionExtension; /// Name of the parachains pallet in the Rococo runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/primitives/chain-westend/Cargo.toml b/bridges/primitives/chain-westend/Cargo.toml index 6b6d2748aff7411d5247c8336aa2ac1251d11ea8..e55a8d649a88b88d84f4f3547c23709cef67d872 100644 --- a/bridges/primitives/chain-westend/Cargo.toml +++ b/bridges/primitives/chain-westend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-westend" description = "Primitives of Westend runtime." -version = "0.1.0" +version = "0.3.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/chain-westend/src/lib.rs b/bridges/primitives/chain-westend/src/lib.rs index f03fd2160a700eb3817a6feb629e9d366cc366aa..afa02e8ee541e5e2912b4b9a216feb633a07b617 100644 --- a/bridges/primitives/chain-westend/src/lib.rs +++ b/bridges/primitives/chain-westend/src/lib.rs @@ -59,8 +59,8 @@ impl ChainWithGrandpa for Westend { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -// The SignedExtension used by Westend. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; +// The TransactionExtension used by Westend. +pub use bp_polkadot_core::CommonTransactionExtension as TransactionExtension; /// Name of the parachains pallet in the Rococo runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/primitives/header-chain/Cargo.toml b/bridges/primitives/header-chain/Cargo.toml index 7338996d69f2231f9f1d9c576a1a245263ef414a..205b593365ef8216a2e501e5751303185d4f7537 100644 --- a/bridges/primitives/header-chain/Cargo.toml +++ b/bridges/primitives/header-chain/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-header-chain" description = "A common interface for describing what a bridge pallet should be able to do." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -13,7 +13,7 @@ workspace = true codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +serde = { features = ["alloc", "derive"], workspace = true } # Bridge dependencies diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs index f5485aca1ee8b485eedd8b26874e401ceb5f4ff5..84a6a881a835b8afc3b5cde8992df1733859d29a 100644 --- a/bridges/primitives/header-chain/src/lib.rs +++ b/bridges/primitives/header-chain/src/lib.rs @@ -244,6 +244,16 @@ pub enum BridgeGrandpaCall { /// 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. diff --git a/bridges/primitives/messages/Cargo.toml b/bridges/primitives/messages/Cargo.toml index 6333000a71ae8c7e0817362c03da0d17fc5739e6..8aa6b4b05e5efb2427a8548e91ec5f47ab494968 100644 --- a/bridges/primitives/messages/Cargo.toml +++ b/bridges/primitives/messages/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-messages" description = "Primitives of messages module." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["bit-vec", "derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive"] } -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +serde = { features = ["alloc", "derive"], workspace = true } # Bridge dependencies diff --git a/bridges/primitives/parachains/Cargo.toml b/bridges/primitives/parachains/Cargo.toml index 99b447f6c0aa92d9613aa6241c672e3a63808c72..575f26193eb68643c5c0a5fe6376d8b735ee2840 100644 --- a/bridges/primitives/parachains/Cargo.toml +++ b/bridges/primitives/parachains/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-parachains" description = "Primitives of parachains module." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/polkadot-core/Cargo.toml b/bridges/primitives/polkadot-core/Cargo.toml index 80382b3289faf94eed3adc58cb0500b4ee8d47be..c0dae684b5f2f3b7b9be096a808fc67d15dadfcf 100644 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ b/bridges/primitives/polkadot-core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-polkadot-core" description = "Primitives of Polkadot-like runtime." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -13,7 +13,7 @@ workspace = true codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } parity-util-mem = { version = "0.12.0", optional = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", optional = true, features = ["derive"] } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Bridge Dependencies diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs index df2836495bbe131e9cf810c43eb4af5eefaf43b7..d59b99db4b586dde7b2d645ff44c34b94f865f24 100644 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ b/bridges/primitives/polkadot-core/src/lib.rs @@ -24,8 +24,8 @@ use bp_runtime::{ self, extensions::{ ChargeTransactionPayment, CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, - CheckSpecVersion, CheckTxVersion, CheckWeight, GenericSignedExtension, - SignedExtensionSchema, + CheckSpecVersion, CheckTxVersion, CheckWeight, GenericTransactionExtension, + TransactionExtensionSchema, }, EncodedOrDecodedCall, StorageMapKeyProvider, TransactionEra, }; @@ -229,8 +229,12 @@ pub type SignedBlock = generic::SignedBlock; pub type Balance = u128; /// Unchecked Extrinsic type. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic, Signature, SignedExt>; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic< + AccountAddress, + EncodedOrDecodedCall, + Signature, + TransactionExt, +>; /// Account address, used by the Polkadot-like chain. pub type Address = MultiAddress; @@ -275,7 +279,7 @@ impl AccountInfoStorageMapKeyProvider { } /// Extra signed extension data that is used by most chains. -pub type CommonSignedExtra = ( +pub type CommonTransactionExtra = ( CheckNonZeroSender, CheckSpecVersion, CheckTxVersion, @@ -286,12 +290,12 @@ pub type CommonSignedExtra = ( ChargeTransactionPayment, ); -/// Extra signed extension data that starts with `CommonSignedExtra`. -pub type SuffixedCommonSignedExtension = - GenericSignedExtension<(CommonSignedExtra, Suffix)>; +/// Extra transaction extension data that starts with `CommonTransactionExtra`. +pub type SuffixedCommonTransactionExtension = + GenericTransactionExtension<(CommonTransactionExtra, Suffix)>; -/// Helper trait to define some extra methods on `SuffixedCommonSignedExtension`. -pub trait SuffixedCommonSignedExtensionExt { +/// Helper trait to define some extra methods on `SuffixedCommonTransactionExtension`. +pub trait SuffixedCommonTransactionExtensionExt { /// Create signed extension from its components. fn from_params( spec_version: u32, @@ -300,7 +304,7 @@ pub trait SuffixedCommonSignedExtensionExt { genesis_hash: Hash, nonce: Nonce, tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), + extra: (Suffix::Payload, Suffix::Implicit), ) -> Self; /// Return transaction nonce. @@ -310,9 +314,10 @@ pub trait SuffixedCommonSignedExtensionExt { fn tip(&self) -> Balance; } -impl SuffixedCommonSignedExtensionExt for SuffixedCommonSignedExtension +impl SuffixedCommonTransactionExtensionExt + for SuffixedCommonTransactionExtension where - Suffix: SignedExtensionSchema, + Suffix: TransactionExtensionSchema, { fn from_params( spec_version: u32, @@ -321,9 +326,9 @@ where genesis_hash: Hash, nonce: Nonce, tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), + extra: (Suffix::Payload, Suffix::Implicit), ) -> Self { - GenericSignedExtension::new( + GenericTransactionExtension::new( ( ( (), // non-zero sender @@ -365,7 +370,7 @@ where } /// Signed extension that is used by most chains. -pub type CommonSignedExtension = SuffixedCommonSignedExtension<()>; +pub type CommonTransactionExtension = SuffixedCommonTransactionExtension<()>; #[cfg(test)] mod tests { diff --git a/bridges/primitives/relayers/Cargo.toml b/bridges/primitives/relayers/Cargo.toml index 563d27c91c9eb9ac45c54784961d494b6f66b616..3bd6809d2789e0b3aced5b8d96448b63e1074ee4 100644 --- a/bridges/primitives/relayers/Cargo.toml +++ b/bridges/primitives/relayers/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-relayers" description = "Primitives of relayers module." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml index 779030b5278ad2fd1d14352ede6e1c31e2087bca..22206fb2c376ce53fee9dc8ff806baaef3ce7c28 100644 --- a/bridges/primitives/runtime/Cargo.toml +++ b/bridges/primitives/runtime/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-runtime" description = "Primitives that may be used at (bridges) runtime level." -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -13,10 +13,10 @@ workspace = true codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } hash-db = { version = "0.16.0", default-features = false } impl-trait-for-tuples = "0.2.2" -log = { version = "0.4.19", default-features = false } +log = { workspace = true } num-traits = { version = "0.2", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +serde = { features = ["alloc", "derive"], workspace = true } # Substrate Dependencies diff --git a/bridges/primitives/runtime/src/extensions.rs b/bridges/primitives/runtime/src/extensions.rs index d896bc92efffc4e8fcb427ffa7057dece6f17241..a31e7b5bb47a64ec2333bbaba3e9c520aa53ef5a 100644 --- a/bridges/primitives/runtime/src/extensions.rs +++ b/bridges/primitives/runtime/src/extensions.rs @@ -20,135 +20,138 @@ use codec::{Compact, Decode, Encode}; use impl_trait_for_tuples::impl_for_tuples; use scale_info::{StaticTypeInfo, TypeInfo}; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, + impl_tx_ext_default, + traits::{Dispatchable, TransactionExtension, TransactionExtensionBase}, transaction_validity::TransactionValidityError, }; use sp_std::{fmt::Debug, marker::PhantomData}; -/// Trait that describes some properties of a `SignedExtension` that are needed in order to send a -/// transaction to the chain. -pub trait SignedExtensionSchema: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo { +/// Trait that describes some properties of a `TransactionExtension` that are needed in order to +/// send a transaction to the chain. +pub trait TransactionExtensionSchema: + Encode + Decode + Debug + Eq + Clone + StaticTypeInfo +{ /// A type of the data encoded as part of the transaction. type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; /// Parameters which are part of the payload used to produce transaction signature, /// but don't end up in the transaction itself (i.e. inherent part of the runtime). - type AdditionalSigned: Encode + Debug + Eq + Clone + StaticTypeInfo; + type Implicit: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; } -impl SignedExtensionSchema for () { +impl TransactionExtensionSchema for () { type Payload = (); - type AdditionalSigned = (); + type Implicit = (); } -/// An implementation of `SignedExtensionSchema` using generic params. +/// An implementation of `TransactionExtensionSchema` using generic params. #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)] -pub struct GenericSignedExtensionSchema(PhantomData<(P, S)>); +pub struct GenericTransactionExtensionSchema(PhantomData<(P, S)>); -impl SignedExtensionSchema for GenericSignedExtensionSchema +impl TransactionExtensionSchema for GenericTransactionExtensionSchema where P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, - S: Encode + Debug + Eq + Clone + StaticTypeInfo, + S: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, { type Payload = P; - type AdditionalSigned = S; + type Implicit = S; } -/// The `SignedExtensionSchema` for `frame_system::CheckNonZeroSender`. -pub type CheckNonZeroSender = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `frame_system::CheckNonZeroSender`. +pub type CheckNonZeroSender = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `frame_system::CheckSpecVersion`. -pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; +/// The `TransactionExtensionSchema` for `frame_system::CheckSpecVersion`. +pub type CheckSpecVersion = GenericTransactionExtensionSchema<(), u32>; -/// The `SignedExtensionSchema` for `frame_system::CheckTxVersion`. -pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; +/// The `TransactionExtensionSchema` for `frame_system::CheckTxVersion`. +pub type CheckTxVersion = GenericTransactionExtensionSchema<(), u32>; -/// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. -pub type CheckGenesis = GenericSignedExtensionSchema<(), Hash>; +/// The `TransactionExtensionSchema` for `frame_system::CheckGenesis`. +pub type CheckGenesis = GenericTransactionExtensionSchema<(), Hash>; -/// The `SignedExtensionSchema` for `frame_system::CheckEra`. -pub type CheckEra = GenericSignedExtensionSchema; +/// The `TransactionExtensionSchema` for `frame_system::CheckEra`. +pub type CheckEra = GenericTransactionExtensionSchema; -/// The `SignedExtensionSchema` for `frame_system::CheckNonce`. -pub type CheckNonce = GenericSignedExtensionSchema, ()>; +/// The `TransactionExtensionSchema` for `frame_system::CheckNonce`. +pub type CheckNonce = GenericTransactionExtensionSchema, ()>; -/// The `SignedExtensionSchema` for `frame_system::CheckWeight`. -pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `frame_system::CheckWeight`. +pub type CheckWeight = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. -pub type ChargeTransactionPayment = GenericSignedExtensionSchema, ()>; +/// The `TransactionExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. +pub type ChargeTransactionPayment = + GenericTransactionExtensionSchema, ()>; -/// The `SignedExtensionSchema` for `polkadot-runtime-common::PrevalidateAttests`. -pub type PrevalidateAttests = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `polkadot-runtime-common::PrevalidateAttests`. +pub type PrevalidateAttests = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. -pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. +pub type BridgeRejectObsoleteHeadersAndMessages = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `RefundBridgedParachainMessages`. +/// The `TransactionExtensionSchema` for `RefundBridgedParachainMessages`. /// This schema is dedicated for `RefundBridgedParachainMessages` signed extension as /// wildcard/placeholder, which relies on the scale encoding for `()` or `((), ())`, or `((), (), /// ())` is the same. So runtime can contains any kind of tuple: /// `(BridgeRefundBridgeHubRococoMessages)` /// `(BridgeRefundBridgeHubRococoMessages, BridgeRefundBridgeHubWestendMessages)` /// `(BridgeRefundParachainMessages1, ..., BridgeRefundParachainMessagesN)` -pub type RefundBridgedParachainMessagesSchema = GenericSignedExtensionSchema<(), ()>; +pub type RefundBridgedParachainMessagesSchema = GenericTransactionExtensionSchema<(), ()>; #[impl_for_tuples(1, 12)] -impl SignedExtensionSchema for Tuple { +impl TransactionExtensionSchema for Tuple { for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); - for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); + for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); ); } /// A simplified version of signed extensions meant for producing signed transactions /// and signed payloads in the client code. #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct GenericSignedExtension { +pub struct GenericTransactionExtension { /// A payload that is included in the transaction. pub payload: S::Payload, #[codec(skip)] // It may be set to `None` if extensions are decoded. We are never reconstructing transactions - // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to - // read fields of the `payload`. And when resigning transaction, we're reconstructing - // `SignedExtensions` from scratch. - additional_signed: Option, + // (and it makes no sense to do that) => decoded version of `TransactionExtensions` is only + // used to read fields of the `payload`. And when resigning transaction, we're reconstructing + // `TransactionExtensions` from scratch. + implicit: Option, } -impl GenericSignedExtension { - /// Create new `GenericSignedExtension` object. - pub fn new(payload: S::Payload, additional_signed: Option) -> Self { - Self { payload, additional_signed } +impl GenericTransactionExtension { + /// Create new `GenericTransactionExtension` object. + pub fn new(payload: S::Payload, implicit: Option) -> Self { + Self { payload, implicit } } } -impl SignedExtension for GenericSignedExtension +impl TransactionExtensionBase for GenericTransactionExtension where - S: SignedExtensionSchema, + S: TransactionExtensionSchema, S::Payload: Send + Sync, - S::AdditionalSigned: Send + Sync, + S::Implicit: Send + Sync, { const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = S::AdditionalSigned; - type Pre = (); + type Implicit = S::Implicit; - fn additional_signed(&self) -> Result { + fn implicit(&self) -> Result { // we shall not ever see this error in relay, because we are never signing decoded // transactions. Instead we're constructing and signing new transactions. So the error code // is kinda random here - self.additional_signed.clone().ok_or( - frame_support::unsigned::TransactionValidityError::Unknown( + self.implicit + .clone() + .ok_or(frame_support::unsigned::TransactionValidityError::Unknown( frame_support::unsigned::UnknownTransaction::Custom(0xFF), - ), - ) + )) } +} +impl TransactionExtension for GenericTransactionExtension +where + C: Dispatchable, + S: TransactionExtensionSchema, + S::Payload: Send + Sync, + S::Implicit: Send + Sync, +{ + type Pre = (); + type Val = (); - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } + impl_tx_ext_default!(C; Context; validate prepare); } diff --git a/bridges/primitives/test-utils/Cargo.toml b/bridges/primitives/test-utils/Cargo.toml index 3ccec9d9033d782d73631a7594b98f8e38b61461..d379e950b86ef6f98754c651c980291c9b9c4012 100644 --- a/bridges/primitives/test-utils/Cargo.toml +++ b/bridges/primitives/test-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp-test-utils" -version = "0.1.0" +version = "0.7.0" description = "Utilities for testing substrate-based runtime bridge code" authors.workspace = true edition.workspace = true diff --git a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml index fa537bda960a4ac2b14c974df1e37fa9e6095489..9297a8603c0aa407e3dc5b860e21a0c227cf1bcc 100644 --- a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-xcm-bridge-hub-router" description = "Primitives of the xcm-bridge-hub fee pallet." -version = "0.1.0" +version = "0.6.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/primitives/xcm-bridge-hub/Cargo.toml b/bridges/primitives/xcm-bridge-hub/Cargo.toml index f9f44fd0f8d974e7aea66477ab818d8f96595031..ad49ec1e83152f60b8386d97d522c798ee87e618 100644 --- a/bridges/primitives/xcm-bridge-hub/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bp-xcm-bridge-hub" description = "Primitives of the xcm-bridge-hub pallet." -version = "0.1.0" +version = "0.2.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/bridges/snowbridge/README.md b/bridges/snowbridge/README.md index a38910da3164e853f54b284f8d38795d4220aafe..6561df401120e9c5c5d6ee2762eb1423b5d6daaf 100644 --- a/bridges/snowbridge/README.md +++ b/bridges/snowbridge/README.md @@ -1,33 +1,40 @@ # Snowbridge · -[![codecov](https://codecov.io/gh/Snowfork/snowbridge/branch/main/graph/badge.svg?token=9hvgSws4rN)] -(https://codecov.io/gh/Snowfork/snowbridge) +[![codecov](https://codecov.io/gh/Snowfork/polkadot-sdk/branch/snowbridge/graph/badge.svg?token=9hvgSws4rN)](https://codecov.io/gh/Snowfork/polkadot-sdk) ![GitHub](https://img.shields.io/github/license/Snowfork/snowbridge) Snowbridge is a trustless bridge between Polkadot and Ethereum. For documentation, visit https://docs.snowbridge.network. ## Components +The Snowbridge project lives in two repositories: + +- [Snowfork/Polkadot-sdk](https://github.com/Snowfork/polkadot-sdk): The Snowbridge parachain and pallets live in +a fork of the Polkadot SDK. Changes are eventually contributed back to +[paritytech/Polkadot-sdk](https://github.com/paritytech/polkadot-sdk) +- [Snowfork/snowbridge](https://github.com/Snowfork/snowbridge): The rest of the Snowbridge components, like contracts, +off-chain relayer, end-to-end tests and test-net setup code. + ### Parachain -Polkadot parachain and our pallets. See [parachain/README.md](https://github.com/Snowfork/snowbridge/blob/main/parachain/README.md). +Polkadot parachain and our pallets. See [README.md](https://github.com/Snowfork/polkadot-sdk/blob/snowbridge/bridges/snowbridge/README.md). ### Contracts -Ethereum contracts and unit tests. See [contracts/README.md](https://github.com/Snowfork/snowbridge/blob/main/contracts/README.md) +Ethereum contracts and unit tests. See [Snowfork/snowbridge/contracts/README.md](https://github.com/Snowfork/snowbridge/blob/main/contracts/README.md) ### Relayer Off-chain relayer services for relaying messages between Polkadot and Ethereum. See -[relayer/README.md](https://github.com/Snowfork/snowbridge/blob/main/relayer/README.md) +[Snowfork/snowbridge/relayer/README.md](https://github.com/Snowfork/snowbridge/blob/main/relayer/README.md) ### Local Testnet Scripts to provision a local testnet, running the above services to bridge between local deployments of Polkadot and -Ethereum. See [web/packages/test/README.md](https://github.com/Snowfork/snowbridge/blob/main/web/packages/test/README.md). +Ethereum. See [Snowfork/snowbridge/web/packages/test/README.md](https://github.com/Snowfork/snowbridge/blob/main/web/packages/test/README.md). ### Smoke Tests -Integration tests for our local testnet. See [smoketest/README.md](https://github.com/Snowfork/snowbridge/blob/main/smoketest/README.md). +Integration tests for our local testnet. See [Snowfork/snowbridge/smoketest/README.md](https://github.com/Snowfork/snowbridge/blob/main/smoketest/README.md). ## Development @@ -84,7 +91,7 @@ direnv allow ### Upgrading the Rust toolchain -Sometimes we would like to upgrade rust toolchain. First update `parachain/rust-toolchain.toml` as required and then +Sometimes we would like to upgrade rust toolchain. First update `rust-toolchain.toml` as required and then update `flake.lock` running ```sh nix flake lock --update-input rust-overlay diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/Cargo.toml b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml similarity index 56% rename from bridges/snowbridge/parachain/pallets/ethereum-client/Cargo.toml rename to bridges/snowbridge/pallets/ethereum-client/Cargo.toml index 2bf5bfcd12f8c2cd77a7b337bdf1087c1a5289de..c8999633c97abb00174e38e16ed5618e7baf0b59 100644 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/Cargo.toml +++ b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-ethereum-client" description = "Snowbridge Ethereum Client Pallet" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -15,8 +15,8 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -serde = { version = "1.0.195", optional = true } -serde_json = { version = "1.0.111", optional = true } +serde = { optional = true, workspace = true, default-features = true } +serde_json = { optional = true, workspace = true, default-features = true } codec = { version = "3.6.1", package = "parity-scale-codec", default-features = false, features = ["derive"] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } ssz_rs = { version = "0.9.0", default-features = false } @@ -24,31 +24,33 @@ ssz_rs_derive = { version = "0.9.0", default-features = false } byte-slice-cast = { version = "1.2.1", default-features = false } rlp = { version = "0.5.2", default-features = false } hex-literal = { version = "0.4.1", optional = true } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } -frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false, optional = true } +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false, optional = true } snowbridge-core = { path = "../../primitives/core", default-features = false } snowbridge-ethereum = { path = "../../primitives/ethereum", default-features = false } +snowbridge-pallet-ethereum-client-fixtures = { path = "./fixtures", default-features = false, optional = true } primitives = { package = "snowbridge-beacon-primitives", path = "../../primitives/beacon", default-features = false } static_assertions = { version = "1.1.0", default-features = false } -bp-runtime = { path = "../../../../primitives/runtime", default-features = false } -pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false, optional = true } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } +pallet-timestamp = { path = "../../../../substrate/frame/timestamp", default-features = false, optional = true } [dev-dependencies] rand = "0.8.5" -sp-keyring = { path = "../../../../../substrate/primitives/keyring" } -serde_json = "1.0.111" +sp-keyring = { path = "../../../../substrate/primitives/keyring" } +serde_json = { workspace = true, default-features = true } hex-literal = "0.4.1" -pallet-timestamp = { path = "../../../../../substrate/frame/timestamp" } -sp-io = { path = "../../../../../substrate/primitives/io" } -serde = "1.0.195" +pallet-timestamp = { path = "../../../../substrate/frame/timestamp" } +snowbridge-pallet-ethereum-client-fixtures = { path = "./fixtures" } +sp-io = { path = "../../../../substrate/primitives/io" } +serde = { workspace = true, default-features = true } [features] default = ["std"] @@ -73,6 +75,7 @@ std = [ "serde", "snowbridge-core/std", "snowbridge-ethereum/std", + "snowbridge-pallet-ethereum-client-fixtures/std", "sp-core/std", "sp-io/std", "sp-runtime/std", @@ -87,6 +90,7 @@ runtime-benchmarks = [ "hex-literal", "pallet-timestamp?/runtime-benchmarks", "snowbridge-core/runtime-benchmarks", + "snowbridge-pallet-ethereum-client-fixtures/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ @@ -95,5 +99,3 @@ try-runtime = [ "pallet-timestamp?/try-runtime", "sp-runtime/try-runtime", ] -beacon-spec-minimal = [] -fast-runtime = ["beacon-spec-minimal"] diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/README.md b/bridges/snowbridge/pallets/ethereum-client/README.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/ethereum-client/README.md rename to bridges/snowbridge/pallets/ethereum-client/README.md diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/benchmark.md b/bridges/snowbridge/pallets/ethereum-client/benchmark.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/ethereum-client/benchmark.md rename to bridges/snowbridge/pallets/ethereum-client/benchmark.md diff --git a/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml b/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..fd1914a7d30e81c3615c981d7ae8ba378a8a5724 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "snowbridge-pallet-ethereum-client-fixtures" +description = "Snowbridge Ethereum Client Test Fixtures" +version = "0.9.0" +authors = ["Snowfork "] +edition.workspace = true +repository.workspace = true +license = "Apache-2.0" +categories = ["cryptography::cryptocurrencies"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +hex-literal = { version = "0.4.1" } +sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } +frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../../substrate/frame/system", default-features = false } +snowbridge-core = { path = "../../../primitives/core", default-features = false } +snowbridge-beacon-primitives = { path = "../../../primitives/beacon", default-features = false } + +[features] +default = ["std"] +std = [ + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "snowbridge-beacon-primitives/std", + "snowbridge-core/std", + "sp-core/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", +] diff --git a/bridges/snowbridge/pallets/ethereum-client/fixtures/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/fixtures/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..facaffb8149cd88b0f3f369ed79dd922e9b2c983 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/fixtures/src/lib.rs @@ -0,0 +1,1225 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See README.md for instructions to generate +#![cfg_attr(not(feature = "std"), no_std)] + +use hex_literal::hex; +use snowbridge_beacon_primitives::{ + types::deneb, updates::AncestryProof, BeaconHeader, ExecutionHeaderUpdate, + NextSyncCommitteeUpdate, SyncAggregate, SyncCommittee, VersionedExecutionPayloadHeader, +}; +use sp_core::U256; +use sp_std::{boxed::Box, vec}; + +const SC_SIZE: usize = 512; +const SC_BITS_SIZE: usize = 64; +type CheckpointUpdate = snowbridge_beacon_primitives::CheckpointUpdate; +type Update = snowbridge_beacon_primitives::Update; + +pub fn make_checkpoint() -> Box { + Box::new(CheckpointUpdate { + header: BeaconHeader { + slot: 2496, + proposer_index: 2, + parent_root: hex!("c99e49787106733eeebab4d93eb326e1f2214575c9d928f0c4ab0da0776f1622").into(), + state_root: hex!("fbf8a08c86ef36bd173e37e733da4a78aa8e85fee99a990e858dd12a59087fde").into(), + body_root: hex!("a2a8ad06901447b2807a9059580a4c40d8a941f325b1343c69f7c7c6c90e4ab0").into(), + }, + current_sync_committee: SyncCommittee { + pubkeys: [ + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + ], + aggregate_pubkey: hex!("8fbd66eeec2ff69ef0b836f04b1d67d88bcd4dfd495061964ad757c77abe822a39fa1cd8ed0d4d9bc9276cea73fd745c").into(), + }, + current_sync_committee_branch: vec![ + hex!("3ade38d498a062b50880a9409e1ca3a7fd4315d91eeb3bb83e56ac6bfe8d6a59").into(), + hex!("93880225bf99a0c5ec22b266ff829837754e9c5edf37a68c05b8f803fd82fa45").into(), + hex!("4c60656ec9a95fcf11030ad309c716b5b15beb7f60a0bcfc7c9d4eff505472ff").into(), + hex!("22d1645fceb4bf9a695043dda19a53e784ec70df6a6b1bd66ea30eba1cca5f2f").into(), + hex!("a8fc6cad84ceefc633ec56c2d031d525e1cb4b51c70eb252919fce5bba9a1fde").into(), + ], + validators_root: hex!("270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69").into(), + block_roots_root: hex!("d160b7687041891b73e54b06fc4e04f82d0fa8fdd76705895e216c6b24709dfe").into(), + block_roots_branch: vec![ + hex!("105290e42d98ab6a0ada6e55453cede36c672abf645eeb986b88d7487616e135").into(), + hex!("9da41f274bcdf6122335443d9ce94d07163b48dba3e2f9499ff56f4e48b48b99").into(), + hex!("ecea7e1d3152d8130e83afdfe34b4de4ba2b69a33c9471991096daf454de9cf5").into(), + hex!("b2bf1758e50b2bfff29169fbc70fdb884b2b05bb615dbc53567574da6f4f1ae2").into(), + hex!("cd87069daf70975779126d6af833b7d636c75ca4d5e750ebcad0e76408a5e5bf").into(), + ], + }) +} + +pub fn make_sync_committee_update() -> Box { + Box::new(Update { + attested_header: BeaconHeader { + slot: 129, + proposer_index: 5, + parent_root: hex!("e32b6c18f029e755b0273dc1c4fa2bc4979794c8286ad40276c1b8a8e36049d8").into(), + state_root: hex!("5ec9dacf25a5f09f20be0c59246b3d8dcfe64bd085b4bac5cec180690339801e").into(), + body_root: hex!("4080cf2412d6ff77fc3164ad6155423a7112f207f173145ec16371a93f481f87").into(), + }, + sync_aggregate: SyncAggregate{ + sync_committee_bits: hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + sync_committee_signature: hex!("a761c3333fbb3d36bc8f65454f898da38001499dcd37494cf3d86940a995399ae649216ba4c985af154f83f72c8b1856079b7636a7a8d7d3f7602df2cbf699edb72b65253e82de4d9cc4db7377eafb22f799129f63f094a21c00675bdd5cc243").into(), + }, + signature_slot: 130, + next_sync_committee_update: Some(NextSyncCommitteeUpdate { + next_sync_committee: SyncCommittee { + pubkeys: [ + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), + hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), + hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), + hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), + hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), + hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), + hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), + hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), + ], + aggregate_pubkey: hex!("8fbd66eeec2ff69ef0b836f04b1d67d88bcd4dfd495061964ad757c77abe822a39fa1cd8ed0d4d9bc9276cea73fd745c").into(), + }, + next_sync_committee_branch: vec![ + hex!("3ade38d498a062b50880a9409e1ca3a7fd4315d91eeb3bb83e56ac6bfe8d6a59").into(), + hex!("fd1e5ff5d4a15081efe3ff17857b1f95984c9a271b1c41c2f81f43e60c2cc541").into(), + hex!("e1c97f93bb7352d395d1ff8ee29881572cb7eb5d71634783701171dcd30cd93d").into(), + hex!("77fa2170ddbd89b15dae02f2e6cf9f76c8e00d1c4217320acffbe01576d0da61").into(), + hex!("e97288e0627219087a024078d69445f34f0583a6350a7c3c40c39fd1fa6f8d68").into(), + ], + }), + finalized_header: BeaconHeader{ + slot: 64, + proposer_index: 4, + parent_root: hex!("0f7bc2353778c14c7f6dba0fc5fe6eec87228b0d3a5447b61dce67b4d9338de3").into(), + state_root: hex!("feb990de653ce494c0a263f820eaf05a9300dbdc30cb6065ede602827bfccde4").into(), + body_root: hex!("f5235cd8c24f2695fc5b7989926305c10ad8cf5a87d62a739f675f5543df2ec1").into(), + }, + finality_branch: vec![ + hex!("0200000000000000000000000000000000000000000000000000000000000000").into(), + hex!("10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7").into(), + hex!("98e9116c6bb7f20de18800dc63e73e689d06d6a47d35b5e2b32cf093d475840d").into(), + hex!("e1c97f93bb7352d395d1ff8ee29881572cb7eb5d71634783701171dcd30cd93d").into(), + hex!("77fa2170ddbd89b15dae02f2e6cf9f76c8e00d1c4217320acffbe01576d0da61").into(), + hex!("e97288e0627219087a024078d69445f34f0583a6350a7c3c40c39fd1fa6f8d68").into(), + ], + block_roots_root: hex!("6fcdfd1c3fb1bdd421fe59dddfff3855b5ed5e30373887991a0059d019ad12bc").into(), + block_roots_branch: vec![ + hex!("94b59531f172bc24f914bc0c10104ccb158676850f8cc3b47b6ddb7f096ebdd7").into(), + hex!("22470ed9155a938587d44d5fa19217c0f939d8862e504e67cd8cb4d1b960795e").into(), + hex!("feec3ef1a68f93849e71e84f90b99602cccc31868137b6887ca8244a4b979e8e").into(), + hex!("5340ad5877c72dca689ca04bc8fedb78d67a4801d99887937edd8ccd29f87e82").into(), + hex!("f5ff4b0c6190005015889879568f5f0d9c40134c7ec4ffdda47950dcd92395ad").into(), + ], + }) +} + +pub fn make_finalized_header_update() -> Box { + Box::new(Update { + attested_header: BeaconHeader { + slot: 2566, + proposer_index: 6, + parent_root: hex!("6eb9f13a2c496318ce1ab3087bbd872f5c9519a1a7ca8231a2453e3cb523af00").into(), + state_root: hex!("c8cb12766113dff7e46d2917267bf33d0626d99dd47715fcdbc5c65fad3c04b4").into(), + body_root: hex!("d8cfd0d7bc9bc3724417a1655bb0a67c0765ca36197320f4d834150b52ef1420").into(), + }, + sync_aggregate: SyncAggregate{ + sync_committee_bits: hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + sync_committee_signature: hex!("9296f9a0387f2cac47008e22ad7c3cd3d49d35384c13e6aa1eacca7dca7c3d2ca81515e50eb3396b9550ed20ef7d8fa2049a186598feb2c00e93728045fcff917733d1827481b8fc95f3913e27fc70112c2490496eb57bb7181f02c3f9fd471f").into(), + }, + signature_slot: 2567, + next_sync_committee_update: None, + finalized_header: BeaconHeader { + slot: 2496, + proposer_index: 2, + parent_root: hex!("c99e49787106733eeebab4d93eb326e1f2214575c9d928f0c4ab0da0776f1622").into(), + state_root: hex!("fbf8a08c86ef36bd173e37e733da4a78aa8e85fee99a990e858dd12a59087fde").into(), + body_root: hex!("a2a8ad06901447b2807a9059580a4c40d8a941f325b1343c69f7c7c6c90e4ab0").into(), + }, + finality_branch: vec![ + hex!("4e00000000000000000000000000000000000000000000000000000000000000").into(), + hex!("10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7").into(), + hex!("98e9116c6bb7f20de18800dc63e73e689d06d6a47d35b5e2b32cf093d475840d").into(), + hex!("958b8e43347f6df6fa5eb3d62d06a862381a6585aa40640dd1c0de11f1cf89c1").into(), + hex!("f107dce04faa86a28fc5d4a618be9cb8d4fc3c23d6c42c3624f3ff4bf6586a03").into(), + hex!("a501cdc02e86969ac3e4d0c5a36f4f049efaa1ab8cb6693f51d130eb52a80f30").into(), + ], + block_roots_root: hex!("d160b7687041891b73e54b06fc4e04f82d0fa8fdd76705895e216c6b24709dfe").into(), + block_roots_branch: vec![ + hex!("105290e42d98ab6a0ada6e55453cede36c672abf645eeb986b88d7487616e135").into(), + hex!("9da41f274bcdf6122335443d9ce94d07163b48dba3e2f9499ff56f4e48b48b99").into(), + hex!("ecea7e1d3152d8130e83afdfe34b4de4ba2b69a33c9471991096daf454de9cf5").into(), + hex!("b2bf1758e50b2bfff29169fbc70fdb884b2b05bb615dbc53567574da6f4f1ae2").into(), + hex!("cd87069daf70975779126d6af833b7d636c75ca4d5e750ebcad0e76408a5e5bf").into(), + ] + }) +} + +pub fn make_execution_header_update() -> Box { + Box::new(ExecutionHeaderUpdate { + header: BeaconHeader { + slot: 215, + proposer_index: 2, + parent_root: hex!("97518f531a252bb6ca547b21aca9da767943ec99211d3b15c804e34c3a523f45").into(), + state_root: hex!("b088b5a3a8c90d6dc919a695cd7bb0267c6f983ea2e675c559ceb8f46cb90b67").into(), + body_root: hex!("0ba23c8224fdd01531d5ad51486353bd524a0b4c20bca704e26d3210616f829b").into(), + }, + ancestry_proof: Some(AncestryProof { + header_branch: vec![ + hex!("97518f531a252bb6ca547b21aca9da767943ec99211d3b15c804e34c3a523f45").into(), + hex!("5ce0db996bd499c2b4f7a93263d5aafd052f420efb617cce6fdd54e25516aa45").into(), + hex!("84f0e373b66011ce774c7061440c0a50a51cce2b4b335395eee3e563d605597f").into(), + hex!("48f9ccc5f9594142c18c3b5c39a99f0549329c6ab3ba06c9a50030eadca87770").into(), + hex!("f89d6e311e05bc75a6f63ce118bccce254551f1a88d54c3b4f773f81f946bd99").into(), + hex!("2edd6d893c22636675147c07dfcdb541a146e87c3f15b51c388be4868246dc9b").into(), + hex!("d76b7de5f856e3208a91a42c9c398a7f4fab35e667bf916346050ae742514a2d").into(), + hex!("83a2e233e76385953ca41de4c3afe60471a61f0cc1b3846b4a0670e3e563b747").into(), + hex!("e783a5a109c2ad74e4eb53e8f6b11b31266a92a9e16c1fd5873109c5d41b282c").into(), + hex!("d4ea1ef3869ee6a0fd0b19d7d70027d144eecd4f1d32cbf47632a0a9069164b9").into(), + hex!("f8179564b58eb93a850d35e4156a04db651106442ad891c3e85155c1762792f1").into(), + hex!("4cbb1edb48cf1e32fb30db60aaaeaf6190ffe4d0c8dbc96cec307daecb78be12").into(), + hex!("b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f").into(), + ], + finalized_block_root: hex!("890a7f23b9ed2160901654be9efc575d6830ca860e2a97866ae3423fb7bd7231").into(), + }), + execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader { + parent_hash: hex!("d82ec63f5c5e6ba61d62f09c188f158e6449b94bdcc31941e68639eec3c4cf7a").into(), + fee_recipient: hex!("0000000000000000000000000000000000000000").into(), + state_root: hex!("8b65545fe5f3216b47b6339b9c91ca2b7f1032a970b04246d9e9fb4460ee34c3").into(), + receipts_root: hex!("7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095").into(), + logs_bloom: hex!("00000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010").into(), + prev_randao: hex!("6d9e2a012d82b1b6cb0a2c1c1ed24cc16dbb56e6e39ae545371e0666ab057862").into(), + block_number: 215, + gas_limit: 64842908, + gas_used: 119301, + timestamp: 1705859527, + extra_data: hex!("d983010d0a846765746888676f312e32312e358664617277696e").into(), + base_fee_per_gas: U256::from(7u64), + block_hash: hex!("48498dbfbcfae53a7f4c289ee00747aceea925f6260c50ead5a33e1c55c40f98").into(), + transactions_root: hex!("5ebc1347fe3df0611d4f66b19bd8e1c6f4eaed0371d850f14c83b1c77ea234e6").into(), + withdrawals_root: hex!("792930bbd5baac43bcc798ee49aa8185ef76bb3b44ba62b91d86ae569e4bb535").into(), + blob_gas_used: 0, + excess_blob_gas: 0, + }), + execution_branch: vec![ + hex!("f8c69d3830406d668619bcccc13c8dddde41e863326f7418b241d5924c4ad34a").into(), + hex!("b46f0c01805fe212e15907981b757e6c496b0cb06664224655613dcec82505bb").into(), + hex!("db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71").into(), + hex!("f4d6b5cf9c6e212615c3674fa625d04eb1114153fb221ef5ad02aa433fc67cfb").into(), + ], + }) +} diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/mod.rs b/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs similarity index 87% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/mod.rs rename to bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs index 807d4de14eeda3b92ab8456d48aa0b177b25c7d1..e1520cd715393e79f0265b07263fa9b762206c97 100644 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/mod.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs @@ -7,15 +7,7 @@ use crate::Pallet as EthereumBeaconClient; use frame_benchmarking::v2::*; use frame_system::RawOrigin; -#[cfg(feature = "beacon-spec-minimal")] -mod fixtures_minimal; -#[cfg(feature = "beacon-spec-minimal")] -use fixtures_minimal::*; - -#[cfg(not(feature = "beacon-spec-minimal"))] -mod fixtures_mainnet; -#[cfg(not(feature = "beacon-spec-minimal"))] -use fixtures_mainnet::*; +use snowbridge_pallet_ethereum_client_fixtures::*; use primitives::{ fast_aggregate_verify, prepare_aggregate_pubkey, prepare_aggregate_signature, @@ -79,7 +71,7 @@ mod benchmarks { let checkpoint_update = make_checkpoint(); let finalized_header_update = make_finalized_header_update(); let execution_header_update = make_execution_header_update(); - let execution_header_hash = execution_header_update.execution_header.block_hash; + let execution_header_hash = execution_header_update.execution_header.block_hash(); EthereumBeaconClient::::process_checkpoint_update(&checkpoint_update)?; EthereumBeaconClient::::process_update(&finalized_header_update)?; @@ -151,16 +143,5 @@ mod benchmarks { Ok(()) } - #[cfg(feature = "beacon-spec-minimal")] - impl_benchmark_test_suite!( - EthereumBeaconClient, - crate::mock::minimal::new_tester(), - crate::mock::minimal::Test - ); - #[cfg(not(feature = "beacon-spec-minimal"))] - impl_benchmark_test_suite!( - EthereumBeaconClient, - crate::mock::mainnet::new_tester(), - crate::mock::mainnet::Test - ); + impl_benchmark_test_suite!(EthereumBeaconClient, crate::mock::new_tester(), crate::mock::Test); } diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/util.rs b/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/util.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/util.rs rename to bridges/snowbridge/pallets/ethereum-client/src/benchmarking/util.rs diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/mod.rs b/bridges/snowbridge/pallets/ethereum-client/src/config/mod.rs similarity index 54% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/config/mod.rs rename to bridges/snowbridge/pallets/ethereum-client/src/config/mod.rs index d20743b846e447f199db15436bb755522a0fa9a9..ba3ea47b94f9acf3dade1aec2bf39bd4d18b51ee 100644 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/mod.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/config/mod.rs @@ -3,54 +3,64 @@ use primitives::merkle_proof::{generalized_index_length, subtree_index}; use static_assertions::const_assert; -pub mod mainnet; -pub mod minimal; - -#[cfg(feature = "beacon-spec-minimal")] -pub use minimal::*; - -#[cfg(not(feature = "beacon-spec-minimal"))] -pub use mainnet::*; - -// Generalized Indices - -// get_generalized_index(BeaconState, 'block_roots') +/// Generalized Indices +/// related to Merkle proofs +/// get_generalized_index(BeaconState, 'block_roots') pub const BLOCK_ROOTS_INDEX: usize = 37; pub const BLOCK_ROOTS_SUBTREE_INDEX: usize = subtree_index(BLOCK_ROOTS_INDEX); pub const BLOCK_ROOTS_DEPTH: usize = generalized_index_length(BLOCK_ROOTS_INDEX); -// get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') +/// get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') pub const FINALIZED_ROOT_INDEX: usize = 105; pub const FINALIZED_ROOT_SUBTREE_INDEX: usize = subtree_index(FINALIZED_ROOT_INDEX); pub const FINALIZED_ROOT_DEPTH: usize = generalized_index_length(FINALIZED_ROOT_INDEX); -// get_generalized_index(BeaconState, 'current_sync_committee') +/// get_generalized_index(BeaconState, 'current_sync_committee') pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54; pub const CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX: usize = subtree_index(CURRENT_SYNC_COMMITTEE_INDEX); pub const CURRENT_SYNC_COMMITTEE_DEPTH: usize = generalized_index_length(CURRENT_SYNC_COMMITTEE_INDEX); -// get_generalized_index(BeaconState, 'next_sync_committee') +/// get_generalized_index(BeaconState, 'next_sync_committee') pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55; pub const NEXT_SYNC_COMMITTEE_SUBTREE_INDEX: usize = subtree_index(NEXT_SYNC_COMMITTEE_INDEX); pub const NEXT_SYNC_COMMITTEE_DEPTH: usize = generalized_index_length(NEXT_SYNC_COMMITTEE_INDEX); -// get_generalized_index(BeaconBlockBody, 'execution_payload') +/// get_generalized_index(BeaconBlockBody, 'execution_payload') pub const EXECUTION_HEADER_INDEX: usize = 25; pub const EXECUTION_HEADER_SUBTREE_INDEX: usize = subtree_index(EXECUTION_HEADER_INDEX); pub const EXECUTION_HEADER_DEPTH: usize = generalized_index_length(EXECUTION_HEADER_INDEX); +/// Sizes related to SSZ encoding pub const MAX_EXTRA_DATA_BYTES: usize = 32; pub const MAX_LOGS_BLOOM_SIZE: usize = 256; pub const MAX_FEE_RECIPIENT_SIZE: usize = 20; +/// Sanity value to constrain the max size of a merkle branch proof. pub const MAX_BRANCH_PROOF_SIZE: usize = 20; /// DomainType('0x07000000') /// pub const DOMAIN_SYNC_COMMITTEE: [u8; 4] = [7, 0, 0, 0]; - +/// Validators public keys are 48 bytes. pub const PUBKEY_SIZE: usize = 48; +/// Signatures produced by validators are 96 bytes. pub const SIGNATURE_SIZE: usize = 96; +// Sanity check for the sync committee bits (see SYNC_COMMITTEE_BITS_SIZE). const_assert!(SYNC_COMMITTEE_BITS_SIZE == SYNC_COMMITTEE_SIZE / 8); + +/// Defined in +/// There are 32 slots in an epoch. An epoch is 6.4 minutes long. +pub const SLOTS_PER_EPOCH: usize = 32; +/// 256 epochs in a sync committee period. Frequency of sync committee (subset of Ethereum +/// validators) change is every ~27 hours. +pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: usize = 256; +/// A sync committee contains 512 randomly selected validators. +pub const SYNC_COMMITTEE_SIZE: usize = 512; +/// An array of sync committee block votes, one bit representing the vote of one validator. +pub const SYNC_COMMITTEE_BITS_SIZE: usize = SYNC_COMMITTEE_SIZE / 8; +/// The size of the block root array in the beacon state, used for ancestry proofs. +pub const SLOTS_PER_HISTORICAL_ROOT: usize = 8192; +/// The index of the block_roots field in the beacon state tree. +pub const BLOCK_ROOT_AT_INDEX_DEPTH: usize = 13; diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/functions.rs b/bridges/snowbridge/pallets/ethereum-client/src/functions.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/functions.rs rename to bridges/snowbridge/pallets/ethereum-client/src/functions.rs diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/impls.rs b/bridges/snowbridge/pallets/ethereum-client/src/impls.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/impls.rs rename to bridges/snowbridge/pallets/ethereum-client/src/impls.rs diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs similarity index 99% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/lib.rs rename to bridges/snowbridge/pallets/ethereum-client/src/lib.rs index c99458441a5c8a8d2805478277d365ed4763b5fa..a54d4a05ac5840df4aa7b00389527a0ec94ee22d 100644 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/lib.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs @@ -552,7 +552,7 @@ pub mod pallet { let latest_execution_state: ExecutionHeaderState = Self::latest_execution_state(); ensure!( latest_execution_state.block_number == 0 || - update.execution_header.block_number == + update.execution_header.block_number() == latest_execution_state.block_number + 1, Error::::ExecutionHeaderSkippedBlock ); @@ -603,7 +603,7 @@ pub mod pallet { } Self::store_execution_header( - update.execution_header.block_hash, + update.execution_header.block_hash(), update.execution_header.clone().into(), update.header.slot, block_root, @@ -786,6 +786,9 @@ pub mod pallet { /// Returns the fork version based on the current epoch. pub(super) fn select_fork_version(fork_versions: &ForkVersions, epoch: u64) -> ForkVersion { + if epoch >= fork_versions.deneb.epoch { + return fork_versions.deneb.version + } if epoch >= fork_versions.capella.epoch { return fork_versions.capella.version } @@ -795,7 +798,6 @@ pub mod pallet { if epoch >= fork_versions.altair.epoch { return fork_versions.altair.version } - fork_versions.genesis.version } @@ -813,7 +815,7 @@ pub mod pallet { let mut pubkeys: Vec = Vec::new(); for (bit, pubkey) in sync_committee_bits.iter().zip(sync_committee_pubkeys.iter()) { if *bit == u8::from(participant) { - pubkeys.push(*pubkey); + pubkeys.push(pubkey.clone()); } } pubkeys diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..3ce34eee191ac48fa95789e03d88e54ff017a2d7 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use crate as ethereum_beacon_client; +use crate::config; +use frame_support::{derive_impl, parameter_types}; +use hex_literal::hex; +use pallet_timestamp; +use primitives::{CompactExecutionHeader, Fork, ForkVersions}; +use snowbridge_core::inbound::{Log, Proof}; +use std::{fs::File, path::PathBuf}; +type Block = frame_system::mocking::MockBlock; +use sp_runtime::BuildStorage; + +fn load_fixture(basename: String) -> Result +where + T: for<'de> serde::Deserialize<'de>, +{ + let filepath: PathBuf = + [env!("CARGO_MANIFEST_DIR"), "tests", "fixtures", &basename].iter().collect(); + serde_json::from_reader(File::open(filepath).unwrap()) +} + +pub fn load_execution_header_update_fixture() -> primitives::ExecutionHeaderUpdate { + load_fixture("execution-header-update.json".to_string()).unwrap() +} + +pub fn load_checkpoint_update_fixture( +) -> primitives::CheckpointUpdate<{ config::SYNC_COMMITTEE_SIZE }> { + load_fixture("initial-checkpoint.json".to_string()).unwrap() +} + +pub fn load_sync_committee_update_fixture( +) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("sync-committee-update.json".to_string()).unwrap() +} + +pub fn load_finalized_header_update_fixture( +) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("finalized-header-update.json".to_string()).unwrap() +} + +pub fn load_next_sync_committee_update_fixture( +) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("next-sync-committee-update.json".to_string()).unwrap() +} + +pub fn load_next_finalized_header_update_fixture( +) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("next-finalized-header-update.json".to_string()).unwrap() +} + +pub fn get_message_verification_payload() -> (Log, Proof) { + ( + Log { + address: hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), + topics: vec![ + hex!("1b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ad").into(), + hex!("00000000000000000000000000000000000000000000000000000000000003e8").into(), + hex!("0000000000000000000000000000000000000000000000000000000000000001").into(), + ], + data: hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000").into(), + }, + Proof { + block_hash: hex!("05aaa60b0f27cce9e71909508527264b77ee14da7b5bf915fcc4e32715333213").into(), + tx_index: 0, + data: (vec![ + hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb").to_vec(), + hex!("d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c185510").to_vec(), + hex!("b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646").to_vec(), + ], vec![ + hex!("f90131a0b601337b3aa10a671caa724eba641e759399979856141d3aea6b6b4ac59b889ba00c7d5dd48be9060221a02fb8fa213860b4c50d47046c8fa65ffaba5737d569e0a094601b62a1086cd9c9cb71a7ebff9e718f3217fd6e837efe4246733c0a196f63a06a4b0dd0aefc37b3c77828c8f07d1b7a2455ceb5dbfd3c77d7d6aeeddc2f7e8ca0d6e8e23142cdd8ec219e1f5d8b56aa18e456702b195deeaa210327284d42ade4a08a313d4c87023005d1ab631bbfe3f5de1e405d0e66d0bef3e033f1e5711b5521a0bf09a5d9a48b10ade82b8d6a5362a15921c8b5228a3487479b467db97411d82fa0f95cccae2a7c572ef3c566503e30bac2b2feb2d2f26eebf6d870dcf7f8cf59cea0d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c1855108080808080808080").to_vec(), + hex!("f851a0b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646a060a634b9280e3a23fb63375e7bbdd9ab07fd379ab6a67e2312bbc112195fa358808080808080808080808080808080").to_vec(), + hex!("f9030820b9030402f90300018301d6e2b9010000000000000800000000000020040008000000000000000000000000400000008000000000000000000000000000000000000000000000000000000000042010000000001000000000000000000000000000000000040000000000000000000000000000000000000000000000008000000000000000002000000000000000000000000200000000000000200000000000100000000040000001000200008000000000000200000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000f901f5f87a942ffa5ecdbe006d30397c7636d3e015eee251369ff842a0c965575a00553e094ca7c5d14f02e107c258dda06867cbf9e0e69f80e71bbcc1a000000000000000000000000000000000000000000000000000000000000003e8a000000000000000000000000000000000000000000000000000000000000003e8f9011c94ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0f863a01b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ada000000000000000000000000000000000000000000000000000000000000003e8a00000000000000000000000000000000000000000000000000000000000000001b8a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000f858948cf6147918a5cbb672703f879f385036f8793a24e1a01449abf21e49fd025f33495e77f7b1461caefdd3d4bb646424a3f445c4576a5ba0000000000000000000000000440edffa1352b13227e8ee646f3ea37456dec701").to_vec(), + ]), + } + ) +} + +pub fn get_message_verification_header() -> CompactExecutionHeader { + CompactExecutionHeader { + parent_hash: hex!("04a7f6ab8282203562c62f38b0ab41d32aaebe2c7ea687702b463148a6429e04") + .into(), + block_number: 55, + state_root: hex!("894d968712976d613519f973a317cb0781c7b039c89f27ea2b7ca193f7befdb3").into(), + receipts_root: hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb") + .into(), + } +} + +frame_support::construct_runtime!( + pub enum Test { + System: frame_system::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + EthereumBeaconClient: ethereum_beacon_client::{Pallet, Call, Storage, Event}, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = (); + type WeightInfo = (); +} + +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], // 0x90000073 + epoch: 0, + } + }; + pub const ExecutionHeadersPruneThreshold: u32 = 8192; +} + +impl ethereum_beacon_client::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_tester() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + let _ = ext.execute_with(|| Timestamp::set(RuntimeOrigin::signed(1), 30_000)); + ext +} diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/tests.rs b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs similarity index 78% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/tests.rs rename to bridges/snowbridge/pallets/ethereum-client/src/tests.rs index d6346d3aafe3500092a0cb2e3ccf2e0af11062b6..50b6a25c3428dba843303031018a4ef5e93cf696 100644 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/tests.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs @@ -13,20 +13,14 @@ use crate::mock::{ load_next_sync_committee_update_fixture, load_sync_committee_update_fixture, }; -#[cfg(feature = "beacon-spec-minimal")] -pub use crate::config::minimal::*; -#[cfg(feature = "beacon-spec-minimal")] -pub use crate::mock::minimal::*; - -#[cfg(not(feature = "beacon-spec-minimal"))] -pub use crate::config::mainnet::*; -#[cfg(not(feature = "beacon-spec-minimal"))] -pub use crate::mock::mainnet::*; +pub use crate::mock::*; +use crate::config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH}; use frame_support::{assert_err, assert_noop, assert_ok}; use hex_literal::hex; use primitives::{ CompactExecutionHeader, ExecutionHeaderState, Fork, ForkVersions, NextSyncCommitteeUpdate, + VersionedExecutionPayloadHeader, }; use rand::{thread_rng, Rng}; use snowbridge_core::{ @@ -280,6 +274,7 @@ fn compute_fork_version() { altair: Fork { version: [0, 0, 0, 1], epoch: 10 }, bellatrix: Fork { version: [0, 0, 0, 2], epoch: 20 }, capella: Fork { version: [0, 0, 0, 3], epoch: 30 }, + deneb: Fork { version: [0, 0, 0, 4], epoch: 40 }, }; new_tester().execute_with(|| { assert_eq!(EthereumBeaconClient::select_fork_version(&mock_fork_versions, 0), [0, 0, 0, 0]); @@ -385,12 +380,12 @@ fn cross_check_execution_state() { #[test] fn process_initial_checkpoint() { - let checkpoint = load_checkpoint_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::force_checkpoint( RuntimeOrigin::root(), - Box::new(checkpoint.clone()) + checkpoint.clone() )); let block_root: H256 = checkpoint.header.hash_tree_root().unwrap(); assert!(>::contains_key(block_root)); @@ -399,12 +394,12 @@ fn process_initial_checkpoint() { #[test] fn process_initial_checkpoint_with_invalid_sync_committee_proof() { - let mut checkpoint = load_checkpoint_update_fixture(); + let mut checkpoint = Box::new(load_checkpoint_update_fixture()); checkpoint.current_sync_committee_branch[0] = TEST_HASH.into(); new_tester().execute_with(|| { assert_err!( - EthereumBeaconClient::force_checkpoint(RuntimeOrigin::root(), Box::new(checkpoint)), + EthereumBeaconClient::force_checkpoint(RuntimeOrigin::root(), checkpoint), Error::::InvalidSyncCommitteeMerkleProof ); }); @@ -412,12 +407,12 @@ fn process_initial_checkpoint_with_invalid_sync_committee_proof() { #[test] fn process_initial_checkpoint_with_invalid_blocks_root_proof() { - let mut checkpoint = load_checkpoint_update_fixture(); + let mut checkpoint = Box::new(load_checkpoint_update_fixture()); checkpoint.block_roots_branch[0] = TEST_HASH.into(); new_tester().execute_with(|| { assert_err!( - EthereumBeaconClient::force_checkpoint(RuntimeOrigin::root(), Box::new(checkpoint)), + EthereumBeaconClient::force_checkpoint(RuntimeOrigin::root(), checkpoint), Error::::InvalidBlockRootsRootMerkleProof ); }); @@ -425,18 +420,15 @@ fn process_initial_checkpoint_with_invalid_blocks_root_proof() { #[test] fn submit_update_in_current_period() { - let checkpoint = load_checkpoint_update_fixture(); - let update = load_finalized_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let update = Box::new(load_finalized_header_update_fixture()); let initial_period = compute_period(checkpoint.header.slot); let update_period = compute_period(update.finalized_header.slot); assert_eq!(initial_period, update_period); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(update.clone()) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); let block_root: H256 = update.finalized_header.hash_tree_root().unwrap(); assert!(>::contains_key(block_root)); }); @@ -444,8 +436,8 @@ fn submit_update_in_current_period() { #[test] fn submit_update_with_sync_committee_in_current_period() { - let checkpoint = load_checkpoint_update_fixture(); - let update = load_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let update = Box::new(load_sync_committee_update_fixture()); let init_period = compute_period(checkpoint.header.slot); let update_period = compute_period(update.finalized_header.slot); assert_eq!(init_period, update_period); @@ -453,16 +445,16 @@ 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), Box::new(update))); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); assert!(>::exists()); }); } #[test] fn submit_update_in_next_period() { - let checkpoint = load_checkpoint_update_fixture(); - let sync_committee_update = load_sync_committee_update_fixture(); - let update = load_next_finalized_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + let update = Box::new(load_next_finalized_header_update_fixture()); let sync_committee_period = compute_period(sync_committee_update.finalized_header.slot); let next_sync_committee_period = compute_period(update.finalized_header.slot); assert_eq!(sync_committee_period + 1, next_sync_committee_period); @@ -471,12 +463,9 @@ fn submit_update_in_next_period() { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert_ok!(EthereumBeaconClient::submit( RuntimeOrigin::signed(1), - Box::new(sync_committee_update.clone()) - )); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(update.clone()) + sync_committee_update.clone() )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); let block_root: H256 = update.finalized_header.clone().hash_tree_root().unwrap(); assert!(>::contains_key(block_root)); }); @@ -484,8 +473,8 @@ fn submit_update_in_next_period() { #[test] fn submit_update_with_invalid_header_proof() { - let checkpoint = load_checkpoint_update_fixture(); - let mut update = load_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let mut update = Box::new(load_sync_committee_update_fixture()); let init_period = compute_period(checkpoint.header.slot); let update_period = compute_period(update.finalized_header.slot); assert_eq!(init_period, update_period); @@ -495,7 +484,7 @@ fn submit_update_with_invalid_header_proof() { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::InvalidHeaderMerkleProof ); }); @@ -503,8 +492,8 @@ fn submit_update_with_invalid_header_proof() { #[test] fn submit_update_with_invalid_block_roots_proof() { - let checkpoint = load_checkpoint_update_fixture(); - let mut update = load_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let mut update = Box::new(load_sync_committee_update_fixture()); let init_period = compute_period(checkpoint.header.slot); let update_period = compute_period(update.finalized_header.slot); assert_eq!(init_period, update_period); @@ -514,7 +503,7 @@ fn submit_update_with_invalid_block_roots_proof() { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::InvalidBlockRootsRootMerkleProof ); }); @@ -522,8 +511,8 @@ fn submit_update_with_invalid_block_roots_proof() { #[test] fn submit_update_with_invalid_next_sync_committee_proof() { - let checkpoint = load_checkpoint_update_fixture(); - let mut update = load_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let mut update = Box::new(load_sync_committee_update_fixture()); let init_period = compute_period(checkpoint.header.slot); let update_period = compute_period(update.finalized_header.slot); assert_eq!(init_period, update_period); @@ -535,7 +524,7 @@ fn submit_update_with_invalid_next_sync_committee_proof() { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::InvalidSyncCommitteeMerkleProof ); }); @@ -543,9 +532,9 @@ fn submit_update_with_invalid_next_sync_committee_proof() { #[test] fn submit_update_with_skipped_period() { - let checkpoint = load_checkpoint_update_fixture(); - let sync_committee_update = load_sync_committee_update_fixture(); - let mut update = load_next_finalized_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + let mut update = Box::new(load_next_finalized_header_update_fixture()); update.signature_slot += (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * SLOTS_PER_EPOCH) as u64; update.attested_header.slot = update.signature_slot - 1; @@ -553,10 +542,10 @@ fn submit_update_with_skipped_period() { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert_ok!(EthereumBeaconClient::submit( RuntimeOrigin::signed(1), - Box::new(sync_committee_update.clone()) + sync_committee_update.clone() )); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::SkippedSyncCommitteePeriod ); }); @@ -564,9 +553,9 @@ fn submit_update_with_skipped_period() { #[test] fn submit_update_with_sync_committee_in_next_period() { - let checkpoint = load_checkpoint_update_fixture(); - let update = load_sync_committee_update_fixture(); - let next_update = load_next_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let update = Box::new(load_sync_committee_update_fixture()); + let next_update = Box::new(load_next_sync_committee_update_fixture()); let update_period = compute_period(update.finalized_header.slot); let next_update_period = compute_period(next_update.finalized_header.slot); assert_eq!(update_period + 1, next_update_period); @@ -574,15 +563,9 @@ 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), - Box::new(update.clone()) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); assert!(>::exists()); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(next_update.clone()) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone())); let last_finalized_state = FinalizedBeaconState::::get(LatestFinalizedBlockRoot::::get()).unwrap(); let last_synced_period = compute_period(last_finalized_state.slot); @@ -592,8 +575,8 @@ fn submit_update_with_sync_committee_in_next_period() { #[test] fn submit_update_with_sync_committee_invalid_signature_slot() { - let checkpoint = load_checkpoint_update_fixture(); - let mut update = load_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let mut update = Box::new(load_sync_committee_update_fixture()); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); @@ -602,7 +585,7 @@ fn submit_update_with_sync_committee_invalid_signature_slot() { update.signature_slot = update.attested_header.slot; assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::InvalidUpdateSlot ); }); @@ -610,8 +593,8 @@ fn submit_update_with_sync_committee_invalid_signature_slot() { #[test] fn submit_update_with_skipped_sync_committee_period() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_update = load_next_finalized_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_update = Box::new(load_next_finalized_header_update_fixture()); let checkpoint_period = compute_period(checkpoint.header.slot); let next_sync_committee_period = compute_period(finalized_update.finalized_header.slot); assert_eq!(checkpoint_period + 1, next_sync_committee_period); @@ -619,7 +602,7 @@ 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), Box::new(finalized_update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_update), Error::::SkippedSyncCommitteePeriod ); }); @@ -627,22 +610,19 @@ fn submit_update_with_skipped_sync_committee_period() { #[test] fn submit_update_execution_headers_too_far_behind() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let execution_header_update = load_execution_header_update_fixture(); - let next_update = load_next_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let execution_header_update = Box::new(load_execution_header_update_fixture()); + let next_update = Box::new(load_next_sync_committee_update_fixture()); new_tester().execute_with(|| { let far_ahead_finalized_header_slot = finalized_header_update.finalized_header.slot + (EPOCHS_PER_SYNC_COMMITTEE_PERIOD * SLOTS_PER_EPOCH * 2) as u64; assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); assert_ok!(EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update) + execution_header_update )); let header_root: H256 = TEST_HASH.into(); @@ -656,7 +636,7 @@ fn submit_update_execution_headers_too_far_behind() { LatestFinalizedBlockRoot::::set(header_root); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(next_update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update), Error::::ExecutionHeaderTooFarBehind ); }); @@ -664,8 +644,8 @@ fn submit_update_execution_headers_too_far_behind() { #[test] fn submit_irrelevant_update() { - let checkpoint = load_checkpoint_update_fixture(); - let mut update = load_next_finalized_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let mut update = Box::new(load_next_finalized_header_update_fixture()); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); @@ -677,7 +657,7 @@ fn submit_irrelevant_update() { update.signature_slot = checkpoint.header.slot + 1; assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::IrrelevantUpdate ); }); @@ -685,11 +665,11 @@ fn submit_irrelevant_update() { #[test] fn submit_update_with_missing_bootstrap() { - let update = load_next_finalized_header_update_fixture(); + let update = Box::new(load_next_finalized_header_update_fixture()); new_tester().execute_with(|| { assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::NotBootstrapped ); }); @@ -697,14 +677,14 @@ fn submit_update_with_missing_bootstrap() { #[test] fn submit_update_with_invalid_sync_committee_update() { - let checkpoint = load_checkpoint_update_fixture(); - let update = load_sync_committee_update_fixture(); - let mut next_update = load_next_sync_committee_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let update = Box::new(load_sync_committee_update_fixture()); + let mut next_update = Box::new(load_next_sync_committee_update_fixture()); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update))); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); // makes update with invalid next_sync_committee >::mutate(>::get(), |x| { @@ -717,7 +697,7 @@ fn submit_update_with_invalid_sync_committee_update() { next_update.next_sync_committee_update = Some(next_sync_committee); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(next_update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update), Error::::InvalidSyncCommitteeUpdate ); }); @@ -725,45 +705,39 @@ fn submit_update_with_invalid_sync_committee_update() { #[test] fn submit_execution_header_update() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let execution_header_update = load_execution_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let execution_header_update = Box::new(load_execution_header_update_fixture()); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); assert_ok!(EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update.clone()) + execution_header_update.clone() )); assert!(>::contains_key( - execution_header_update.execution_header.block_hash + execution_header_update.execution_header.block_hash() )); }); } #[test] fn submit_execution_header_update_invalid_ancestry_proof() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let mut execution_header_update = load_execution_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let mut execution_header_update = Box::new(load_execution_header_update_fixture()); if let Some(ref mut ancestry_proof) = execution_header_update.ancestry_proof { ancestry_proof.header_branch[0] = TEST_HASH.into() } new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); assert_err!( EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update) + execution_header_update ), Error::::InvalidAncestryMerkleProof ); @@ -772,21 +746,18 @@ fn submit_execution_header_update_invalid_ancestry_proof() { #[test] fn submit_execution_header_update_invalid_execution_header_proof() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let mut execution_header_update = load_execution_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let mut execution_header_update = Box::new(load_execution_header_update_fixture()); execution_header_update.execution_branch[0] = TEST_HASH.into(); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); assert_err!( EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update) + execution_header_update ), Error::::InvalidExecutionHeaderProof ); @@ -795,30 +766,43 @@ fn submit_execution_header_update_invalid_execution_header_proof() { #[test] fn submit_execution_header_update_that_skips_block() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let execution_header_update = load_execution_header_update_fixture(); - let mut skipped_block_execution_header_update = load_execution_header_update_fixture(); - skipped_block_execution_header_update.execution_header.block_number = - execution_header_update.execution_header.block_number + 2; + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let execution_header_update = Box::new(load_execution_header_update_fixture()); + let mut skipped_block_execution_header_update = + Box::new(load_execution_header_update_fixture()); + let mut skipped_execution_header = + skipped_block_execution_header_update.execution_header.clone(); + + skipped_execution_header = match skipped_execution_header { + VersionedExecutionPayloadHeader::Capella(execution_payload_header) => { + let mut mut_execution_payload_header = execution_payload_header.clone(); + mut_execution_payload_header.block_number = execution_payload_header.block_number + 2; + VersionedExecutionPayloadHeader::Capella(mut_execution_payload_header) + }, + VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => { + let mut mut_execution_payload_header = execution_payload_header.clone(); + mut_execution_payload_header.block_number = execution_payload_header.block_number + 2; + VersionedExecutionPayloadHeader::Deneb(mut_execution_payload_header) + }, + }; + + skipped_block_execution_header_update.execution_header = skipped_execution_header; new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); assert_ok!(EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update.clone()) + execution_header_update.clone() )); assert!(>::contains_key( - execution_header_update.execution_header.block_hash + execution_header_update.execution_header.block_hash() )); assert_err!( EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(skipped_block_execution_header_update) + skipped_block_execution_header_update ), Error::::ExecutionHeaderSkippedBlock ); @@ -827,21 +811,18 @@ fn submit_execution_header_update_that_skips_block() { #[test] fn submit_execution_header_update_that_is_also_finalized_header_which_is_not_stored() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let mut execution_header_update = load_execution_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let mut execution_header_update = Box::new(load_execution_header_update_fixture()); execution_header_update.ancestry_proof = None; new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); assert_err!( EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update) + execution_header_update ), Error::::ExpectedFinalizedHeaderNotStored ); @@ -851,17 +832,14 @@ fn submit_execution_header_update_that_is_also_finalized_header_which_is_not_sto #[test] fn submit_execution_header_update_that_is_also_finalized_header_which_is_stored_but_slots_dont_match( ) { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let mut execution_header_update = load_execution_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let mut execution_header_update = Box::new(load_execution_header_update_fixture()); execution_header_update.ancestry_proof = None; new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); let block_root: H256 = execution_header_update.header.hash_tree_root().unwrap(); @@ -877,7 +855,7 @@ fn submit_execution_header_update_that_is_also_finalized_header_which_is_stored_ assert_err!( EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update) + execution_header_update ), Error::::ExpectedFinalizedHeaderNotStored ); @@ -886,16 +864,13 @@ fn submit_execution_header_update_that_is_also_finalized_header_which_is_stored_ #[test] fn submit_execution_header_not_finalized() { - let checkpoint = load_checkpoint_update_fixture(); - let finalized_header_update = load_finalized_header_update_fixture(); - let update = load_execution_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let finalized_header_update = Box::new(load_finalized_header_update_fixture()); + let update = Box::new(load_execution_header_update_fixture()); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - Box::new(finalized_header_update) - )); + assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_header_update)); >::mutate(>::get(), |x| { let prev = x.unwrap(); @@ -903,10 +878,7 @@ fn submit_execution_header_not_finalized() { }); assert_err!( - EthereumBeaconClient::submit_execution_header( - RuntimeOrigin::signed(1), - Box::new(update) - ), + EthereumBeaconClient::submit_execution_header(RuntimeOrigin::signed(1), update), Error::::HeaderNotFinalized ); }); @@ -1004,9 +976,9 @@ fn verify_message_receipt_does_not_contain_log() { #[test] fn set_operating_mode() { - let checkpoint = load_checkpoint_update_fixture(); - let update = load_finalized_header_update_fixture(); - let execution_header_update = load_execution_header_update_fixture(); + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let update = Box::new(load_finalized_header_update_fixture()); + let execution_header_update = Box::new(load_execution_header_update_fixture()); new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); @@ -1017,14 +989,14 @@ fn set_operating_mode() { )); assert_noop!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), Box::new(update)), + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), Error::::Halted ); assert_noop!( EthereumBeaconClient::submit_execution_header( RuntimeOrigin::signed(1), - Box::new(execution_header_update) + execution_header_update ), Error::::Halted ); diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/types.rs b/bridges/snowbridge/pallets/ethereum-client/src/types.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/types.rs rename to bridges/snowbridge/pallets/ethereum-client/src/types.rs diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/weights.rs b/bridges/snowbridge/pallets/ethereum-client/src/weights.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/ethereum-client/src/weights.rs rename to bridges/snowbridge/pallets/ethereum-client/src/weights.rs diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/execution-header-update.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/execution-header-update.json new file mode 100755 index 0000000000000000000000000000000000000000..319014249c12d9de36ee9e73e08cdeabcd954f63 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/execution-header-update.json @@ -0,0 +1,54 @@ +{ + "header": { + "slot": 215, + "proposer_index": 2, + "parent_root": "0x97518f531a252bb6ca547b21aca9da767943ec99211d3b15c804e34c3a523f45", + "state_root": "0xb088b5a3a8c90d6dc919a695cd7bb0267c6f983ea2e675c559ceb8f46cb90b67", + "body_root": "0x0ba23c8224fdd01531d5ad51486353bd524a0b4c20bca704e26d3210616f829b" + }, + "ancestry_proof": { + "header_branch": [ + "0x97518f531a252bb6ca547b21aca9da767943ec99211d3b15c804e34c3a523f45", + "0x5ce0db996bd499c2b4f7a93263d5aafd052f420efb617cce6fdd54e25516aa45", + "0x84f0e373b66011ce774c7061440c0a50a51cce2b4b335395eee3e563d605597f", + "0x48f9ccc5f9594142c18c3b5c39a99f0549329c6ab3ba06c9a50030eadca87770", + "0xf89d6e311e05bc75a6f63ce118bccce254551f1a88d54c3b4f773f81f946bd99", + "0x2edd6d893c22636675147c07dfcdb541a146e87c3f15b51c388be4868246dc9b", + "0xd76b7de5f856e3208a91a42c9c398a7f4fab35e667bf916346050ae742514a2d", + "0x83a2e233e76385953ca41de4c3afe60471a61f0cc1b3846b4a0670e3e563b747", + "0xe783a5a109c2ad74e4eb53e8f6b11b31266a92a9e16c1fd5873109c5d41b282c", + "0xd4ea1ef3869ee6a0fd0b19d7d70027d144eecd4f1d32cbf47632a0a9069164b9", + "0xf8179564b58eb93a850d35e4156a04db651106442ad891c3e85155c1762792f1", + "0x4cbb1edb48cf1e32fb30db60aaaeaf6190ffe4d0c8dbc96cec307daecb78be12", + "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f" + ], + "finalized_block_root": "0x890a7f23b9ed2160901654be9efc575d6830ca860e2a97866ae3423fb7bd7231" + }, + "execution_header": { + "Deneb": { + "parent_hash": "0xd82ec63f5c5e6ba61d62f09c188f158e6449b94bdcc31941e68639eec3c4cf7a", + "fee_recipient": "0x0000000000000000000000000000000000000000", + "state_root": "0x8b65545fe5f3216b47b6339b9c91ca2b7f1032a970b04246d9e9fb4460ee34c3", + "receipts_root": "0x7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095", + "logs_bloom": "0x00000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010", + "prev_randao": "0x6d9e2a012d82b1b6cb0a2c1c1ed24cc16dbb56e6e39ae545371e0666ab057862", + "block_number": 215, + "gas_limit": 64842908, + "gas_used": 119301, + "timestamp": 1705859527, + "extra_data": "0xd983010d0a846765746888676f312e32312e358664617277696e", + "base_fee_per_gas": 7, + "block_hash": "0x48498dbfbcfae53a7f4c289ee00747aceea925f6260c50ead5a33e1c55c40f98", + "transactions_root": "0x5ebc1347fe3df0611d4f66b19bd8e1c6f4eaed0371d850f14c83b1c77ea234e6", + "withdrawals_root": "0x792930bbd5baac43bcc798ee49aa8185ef76bb3b44ba62b91d86ae569e4bb535", + "blob_gas_used": 0, + "excess_blob_gas": 0 + } + }, + "execution_branch": [ + "0xf8c69d3830406d668619bcccc13c8dddde41e863326f7418b241d5924c4ad34a", + "0xb46f0c01805fe212e15907981b757e6c496b0cb06664224655613dcec82505bb", + "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0xf4d6b5cf9c6e212615c3674fa625d04eb1114153fb221ef5ad02aa433fc67cfb" + ] +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/finalized-header-update.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/finalized-header-update.json new file mode 100755 index 0000000000000000000000000000000000000000..f9d5324d57b15fc657876b46bbf4a8e716d4c30c --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/finalized-header-update.json @@ -0,0 +1,38 @@ +{ + "attested_header": { + "slot": 2566, + "proposer_index": 6, + "parent_root": "0x6eb9f13a2c496318ce1ab3087bbd872f5c9519a1a7ca8231a2453e3cb523af00", + "state_root": "0xc8cb12766113dff7e46d2917267bf33d0626d99dd47715fcdbc5c65fad3c04b4", + "body_root": "0xd8cfd0d7bc9bc3724417a1655bb0a67c0765ca36197320f4d834150b52ef1420" + }, + "sync_aggregate": { + "sync_committee_bits": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sync_committee_signature": "0x9296f9a0387f2cac47008e22ad7c3cd3d49d35384c13e6aa1eacca7dca7c3d2ca81515e50eb3396b9550ed20ef7d8fa2049a186598feb2c00e93728045fcff917733d1827481b8fc95f3913e27fc70112c2490496eb57bb7181f02c3f9fd471f" + }, + "signature_slot": 2567, + "next_sync_committee_update": null, + "finalized_header": { + "slot": 2496, + "proposer_index": 2, + "parent_root": "0xc99e49787106733eeebab4d93eb326e1f2214575c9d928f0c4ab0da0776f1622", + "state_root": "0xfbf8a08c86ef36bd173e37e733da4a78aa8e85fee99a990e858dd12a59087fde", + "body_root": "0xa2a8ad06901447b2807a9059580a4c40d8a941f325b1343c69f7c7c6c90e4ab0" + }, + "finality_branch": [ + "0x4e00000000000000000000000000000000000000000000000000000000000000", + "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", + "0x98e9116c6bb7f20de18800dc63e73e689d06d6a47d35b5e2b32cf093d475840d", + "0x958b8e43347f6df6fa5eb3d62d06a862381a6585aa40640dd1c0de11f1cf89c1", + "0xf107dce04faa86a28fc5d4a618be9cb8d4fc3c23d6c42c3624f3ff4bf6586a03", + "0xa501cdc02e86969ac3e4d0c5a36f4f049efaa1ab8cb6693f51d130eb52a80f30" + ], + "block_roots_root": "0xd160b7687041891b73e54b06fc4e04f82d0fa8fdd76705895e216c6b24709dfe", + "block_roots_branch": [ + "0x105290e42d98ab6a0ada6e55453cede36c672abf645eeb986b88d7487616e135", + "0x9da41f274bcdf6122335443d9ce94d07163b48dba3e2f9499ff56f4e48b48b99", + "0xecea7e1d3152d8130e83afdfe34b4de4ba2b69a33c9471991096daf454de9cf5", + "0xb2bf1758e50b2bfff29169fbc70fdb884b2b05bb615dbc53567574da6f4f1ae2", + "0xcd87069daf70975779126d6af833b7d636c75ca4d5e750ebcad0e76408a5e5bf" + ] +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/inbound-message.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/inbound-message.json new file mode 100644 index 0000000000000000000000000000000000000000..5aa5a59f023761e248fd23ec3b29de19092e5e90 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/inbound-message.json @@ -0,0 +1,31 @@ +{ + "execution_header": { + "parent_hash": "0xd82ec63f5c5e6ba61d62f09c188f158e6449b94bdcc31941e68639eec3c4cf7a", + "state_root": "0x8b65545fe5f3216b47b6339b9c91ca2b7f1032a970b04246d9e9fb4460ee34c3", + "receipts_root": "0x7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095", + "block_number": 215 + }, + "message": { + "event_log": { + "address": "0xeda338e4dc46038493b885327842fd3e301cab39", + "topics": [ + "0x7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f", + "0xc173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539", + "0x5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e00a736aa00000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000" + }, + "Proof": { + "block_hash": "0x48498dbfbcfae53a7f4c289ee00747aceea925f6260c50ead5a33e1c55c40f98", + "tx_index": 0, + "data": { + "keys": [ + "0x7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095" + ], + "values": [ + "0xf9028e822080b9028802f90284018301d205b9010000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010f90179f85894eda338e4dc46038493b885327842fd3e301cab39e1a0f78bb28d4b1d7da699e5c0bc2be29c2b04b5aab6aacf6298fe5304f9db9c6d7ea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7df9011c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a05f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0b8a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e00a736aa00000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000" + ] + } + } + } +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/initial-checkpoint.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/initial-checkpoint.json new file mode 100755 index 0000000000000000000000000000000000000000..202790c1db5b53068c7aedf561dc7972cb863f00 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/initial-checkpoint.json @@ -0,0 +1,542 @@ +{ + "header": { + "slot": 2496, + "proposer_index": 2, + "parent_root": "0xc99e49787106733eeebab4d93eb326e1f2214575c9d928f0c4ab0da0776f1622", + "state_root": "0xfbf8a08c86ef36bd173e37e733da4a78aa8e85fee99a990e858dd12a59087fde", + "body_root": "0xa2a8ad06901447b2807a9059580a4c40d8a941f325b1343c69f7c7c6c90e4ab0" + }, + "current_sync_committee": { + "pubkeys": [ + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b" + ], + "aggregate_pubkey": "0x8fbd66eeec2ff69ef0b836f04b1d67d88bcd4dfd495061964ad757c77abe822a39fa1cd8ed0d4d9bc9276cea73fd745c" + }, + "current_sync_committee_branch": [ + "0x3ade38d498a062b50880a9409e1ca3a7fd4315d91eeb3bb83e56ac6bfe8d6a59", + "0x93880225bf99a0c5ec22b266ff829837754e9c5edf37a68c05b8f803fd82fa45", + "0x4c60656ec9a95fcf11030ad309c716b5b15beb7f60a0bcfc7c9d4eff505472ff", + "0x22d1645fceb4bf9a695043dda19a53e784ec70df6a6b1bd66ea30eba1cca5f2f", + "0xa8fc6cad84ceefc633ec56c2d031d525e1cb4b51c70eb252919fce5bba9a1fde" + ], + "validators_root": "0x270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69", + "block_roots_root": "0xd160b7687041891b73e54b06fc4e04f82d0fa8fdd76705895e216c6b24709dfe", + "block_roots_branch": [ + "0x105290e42d98ab6a0ada6e55453cede36c672abf645eeb986b88d7487616e135", + "0x9da41f274bcdf6122335443d9ce94d07163b48dba3e2f9499ff56f4e48b48b99", + "0xecea7e1d3152d8130e83afdfe34b4de4ba2b69a33c9471991096daf454de9cf5", + "0xb2bf1758e50b2bfff29169fbc70fdb884b2b05bb615dbc53567574da6f4f1ae2", + "0xcd87069daf70975779126d6af833b7d636c75ca4d5e750ebcad0e76408a5e5bf" + ] +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.json new file mode 100755 index 0000000000000000000000000000000000000000..d9bf025ad354df6165f5b101f575dc2355831ee2 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.json @@ -0,0 +1,38 @@ +{ + "attested_header": { + "slot": 8259, + "proposer_index": 0, + "parent_root": "0x877e9b66f04549e4c924ea6aeb4a33bb7d773b341845dd690f5d738145002f86", + "state_root": "0x724b67fde7de071886d930c5c10560896820cff056029f8519d74599ba244e60", + "body_root": "0x6a3cf016e2be639d86994dc76361195a9aec0a67a18978dbb512765adef02297" + }, + "sync_aggregate": { + "sync_committee_bits": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sync_committee_signature": "0xb817cf06ac29aa973099421c61a7d6bf91a2d04b825c4f860a999d59ccb78e4e53e604f6309f08c7ded14e9170e837150ad3a9994eb1c37d334bc03e35ddf9eadef503027485b339f16bcd5b79715a6bbd58bd823429a1a35d1be2d44a1152f1" + }, + "signature_slot": 8260, + "next_sync_committee_update": null, + "finalized_header": { + "slot": 8192, + "proposer_index": 5, + "parent_root": "0x15889c6c548ed2859150a8d46043ce2711f66a4b4bc61cc0185407d84304ad5b", + "state_root": "0x47f766a70bb799a34f9168e05f8e04b38f5a6c84398c519742a51e1fe7224148", + "body_root": "0x57fbc20d80ae3e3c6c837c98baa7885d9c7e016530625d231a58bb8bdeab2404" + }, + "finality_branch": [ + "0x0001000000000000000000000000000000000000000000000000000000000000", + "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", + "0x5e8a7e8804797705fbca4f2c30646ebea1e4a0da5d001a3455de0301a915e96c", + "0x067ba41a97d3634c7d6bcc5944487e35053644ce1cfe11868d27a97eadc6c012", + "0x995a48c6e5f01d6c345135705cdcfccd7e83d6f0f7d506a7ba9a2578a6ccee3a", + "0xaebdc6f600a419d1cdb7889921c93df7381e8dd934a31a448f31a25d6c2a83fa" + ], + "block_roots_root": "0x2fc4d44f8cea295d336a7f3341ea3eaa258533c917c1de3123fb605a5ed938d7", + "block_roots_branch": [ + "0xa94746addf566d1f83eaf46d2a4b78998b22ad7ae9a12b775d23cf8c50acb4ae", + "0x32a148bfd7e07c2ac056bcab18839d6a21227f3a9d96131c8462183dc42006d7", + "0x3ebcc8ac089dee384fab2122309f5aa64209da83dab2ba9985dcf8e146ed83eb", + "0x6588dcf3d33e1f7697a2d964e68c47df637e026a58b094335a133a726c0a063b", + "0x5ba1e52a00ea3dd089557faa97876f3222ee916ffa94437e0cb43c95ddddd0d3" + ] +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.json new file mode 100755 index 0000000000000000000000000000000000000000..4937f66ff0cd2819f75d5e053b6c577635472dfa --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.json @@ -0,0 +1,563 @@ +{ + "attested_header": { + "slot": 8259, + "proposer_index": 0, + "parent_root": "0x877e9b66f04549e4c924ea6aeb4a33bb7d773b341845dd690f5d738145002f86", + "state_root": "0x724b67fde7de071886d930c5c10560896820cff056029f8519d74599ba244e60", + "body_root": "0x6a3cf016e2be639d86994dc76361195a9aec0a67a18978dbb512765adef02297" + }, + "sync_aggregate": { + "sync_committee_bits": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sync_committee_signature": "0xb817cf06ac29aa973099421c61a7d6bf91a2d04b825c4f860a999d59ccb78e4e53e604f6309f08c7ded14e9170e837150ad3a9994eb1c37d334bc03e35ddf9eadef503027485b339f16bcd5b79715a6bbd58bd823429a1a35d1be2d44a1152f1" + }, + "signature_slot": 8260, + "next_sync_committee_update": { + "next_sync_committee": { + "pubkeys": [ + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373" + ], + "aggregate_pubkey": "0x8fbd66eeec2ff69ef0b836f04b1d67d88bcd4dfd495061964ad757c77abe822a39fa1cd8ed0d4d9bc9276cea73fd745c" + }, + "next_sync_committee_branch": [ + "0x3ade38d498a062b50880a9409e1ca3a7fd4315d91eeb3bb83e56ac6bfe8d6a59", + "0xcdc4165a88ae33b52f88df39f4620b936e5d23296fb670b22c04774293e8c4a9", + "0x067ba41a97d3634c7d6bcc5944487e35053644ce1cfe11868d27a97eadc6c012", + "0x995a48c6e5f01d6c345135705cdcfccd7e83d6f0f7d506a7ba9a2578a6ccee3a", + "0xaebdc6f600a419d1cdb7889921c93df7381e8dd934a31a448f31a25d6c2a83fa" + ] + }, + "finalized_header": { + "slot": 8192, + "proposer_index": 5, + "parent_root": "0x15889c6c548ed2859150a8d46043ce2711f66a4b4bc61cc0185407d84304ad5b", + "state_root": "0x47f766a70bb799a34f9168e05f8e04b38f5a6c84398c519742a51e1fe7224148", + "body_root": "0x57fbc20d80ae3e3c6c837c98baa7885d9c7e016530625d231a58bb8bdeab2404" + }, + "finality_branch": [ + "0x0001000000000000000000000000000000000000000000000000000000000000", + "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", + "0x5e8a7e8804797705fbca4f2c30646ebea1e4a0da5d001a3455de0301a915e96c", + "0x067ba41a97d3634c7d6bcc5944487e35053644ce1cfe11868d27a97eadc6c012", + "0x995a48c6e5f01d6c345135705cdcfccd7e83d6f0f7d506a7ba9a2578a6ccee3a", + "0xaebdc6f600a419d1cdb7889921c93df7381e8dd934a31a448f31a25d6c2a83fa" + ], + "block_roots_root": "0x2fc4d44f8cea295d336a7f3341ea3eaa258533c917c1de3123fb605a5ed938d7", + "block_roots_branch": [ + "0xa94746addf566d1f83eaf46d2a4b78998b22ad7ae9a12b775d23cf8c50acb4ae", + "0x32a148bfd7e07c2ac056bcab18839d6a21227f3a9d96131c8462183dc42006d7", + "0x3ebcc8ac089dee384fab2122309f5aa64209da83dab2ba9985dcf8e146ed83eb", + "0x6588dcf3d33e1f7697a2d964e68c47df637e026a58b094335a133a726c0a063b", + "0x5ba1e52a00ea3dd089557faa97876f3222ee916ffa94437e0cb43c95ddddd0d3" + ] +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update.json new file mode 100755 index 0000000000000000000000000000000000000000..6bf20355c7a34ea3b1a9949f39cabc363a029590 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update.json @@ -0,0 +1,563 @@ +{ + "attested_header": { + "slot": 129, + "proposer_index": 5, + "parent_root": "0xe32b6c18f029e755b0273dc1c4fa2bc4979794c8286ad40276c1b8a8e36049d8", + "state_root": "0x5ec9dacf25a5f09f20be0c59246b3d8dcfe64bd085b4bac5cec180690339801e", + "body_root": "0x4080cf2412d6ff77fc3164ad6155423a7112f207f173145ec16371a93f481f87" + }, + "sync_aggregate": { + "sync_committee_bits": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sync_committee_signature": "0xa761c3333fbb3d36bc8f65454f898da38001499dcd37494cf3d86940a995399ae649216ba4c985af154f83f72c8b1856079b7636a7a8d7d3f7602df2cbf699edb72b65253e82de4d9cc4db7377eafb22f799129f63f094a21c00675bdd5cc243" + }, + "signature_slot": 130, + "next_sync_committee_update": { + "next_sync_committee": { + "pubkeys": [ + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", + "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", + "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", + "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", + "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", + "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", + "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", + "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b" + ], + "aggregate_pubkey": "0x8fbd66eeec2ff69ef0b836f04b1d67d88bcd4dfd495061964ad757c77abe822a39fa1cd8ed0d4d9bc9276cea73fd745c" + }, + "next_sync_committee_branch": [ + "0x3ade38d498a062b50880a9409e1ca3a7fd4315d91eeb3bb83e56ac6bfe8d6a59", + "0xfd1e5ff5d4a15081efe3ff17857b1f95984c9a271b1c41c2f81f43e60c2cc541", + "0xe1c97f93bb7352d395d1ff8ee29881572cb7eb5d71634783701171dcd30cd93d", + "0x77fa2170ddbd89b15dae02f2e6cf9f76c8e00d1c4217320acffbe01576d0da61", + "0xe97288e0627219087a024078d69445f34f0583a6350a7c3c40c39fd1fa6f8d68" + ] + }, + "finalized_header": { + "slot": 64, + "proposer_index": 4, + "parent_root": "0x0f7bc2353778c14c7f6dba0fc5fe6eec87228b0d3a5447b61dce67b4d9338de3", + "state_root": "0xfeb990de653ce494c0a263f820eaf05a9300dbdc30cb6065ede602827bfccde4", + "body_root": "0xf5235cd8c24f2695fc5b7989926305c10ad8cf5a87d62a739f675f5543df2ec1" + }, + "finality_branch": [ + "0x0200000000000000000000000000000000000000000000000000000000000000", + "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", + "0x98e9116c6bb7f20de18800dc63e73e689d06d6a47d35b5e2b32cf093d475840d", + "0xe1c97f93bb7352d395d1ff8ee29881572cb7eb5d71634783701171dcd30cd93d", + "0x77fa2170ddbd89b15dae02f2e6cf9f76c8e00d1c4217320acffbe01576d0da61", + "0xe97288e0627219087a024078d69445f34f0583a6350a7c3c40c39fd1fa6f8d68" + ], + "block_roots_root": "0x6fcdfd1c3fb1bdd421fe59dddfff3855b5ed5e30373887991a0059d019ad12bc", + "block_roots_branch": [ + "0x94b59531f172bc24f914bc0c10104ccb158676850f8cc3b47b6ddb7f096ebdd7", + "0x22470ed9155a938587d44d5fa19217c0f939d8862e504e67cd8cb4d1b960795e", + "0xfeec3ef1a68f93849e71e84f90b99602cccc31868137b6887ca8244a4b979e8e", + "0x5340ad5877c72dca689ca04bc8fedb78d67a4801d99887937edd8ccd29f87e82", + "0xf5ff4b0c6190005015889879568f5f0d9c40134c7ec4ffdda47950dcd92395ad" + ] +} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue/Cargo.toml similarity index 64% rename from bridges/snowbridge/parachain/pallets/inbound-queue/Cargo.toml rename to bridges/snowbridge/pallets/inbound-queue/Cargo.toml index 8101ecd5a4833ea2117855f9d6ae8cf14dcb668e..b850496cd4e14cd906565d488450b339a29f463f 100644 --- a/bridges/snowbridge/parachain/pallets/inbound-queue/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-inbound-queue" description = "Snowbridge Inbound Queue Pallet" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -15,38 +15,38 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } codec = { version = "3.6.1", package = "parity-scale-codec", default-features = false, features = ["derive"] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } hex-literal = { version = "0.4.1", optional = true } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } alloy-primitives = { version = "0.4.2", default-features = false, features = ["rlp"] } alloy-sol-types = { version = "0.4.2", default-features = false } alloy-rlp = { version = "0.3.3", default-features = false, features = ["derive"] } num-traits = { version = "0.2.16", default-features = false } -frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +pallet-balances = { path = "../../../../substrate/frame/balances", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../polkadot/xcm/xcm-executor", default-features = false } snowbridge-core = { path = "../../primitives/core", default-features = false } snowbridge-ethereum = { path = "../../primitives/ethereum", default-features = false } snowbridge-router-primitives = { path = "../../primitives/router", default-features = false } -snowbridge-beacon-primitives = { path = "../../primitives/beacon", default-features = false, optional = true } +snowbridge-beacon-primitives = { path = "../../primitives/beacon", default-features = false } +snowbridge-pallet-inbound-queue-fixtures = { path = "./fixtures", default-features = false, optional = true } [dev-dependencies] -frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking" } -sp-keyring = { path = "../../../../../substrate/primitives/keyring" } -snowbridge-beacon-primitives = { path = "../../primitives/beacon" } +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking" } +sp-keyring = { path = "../../../../substrate/primitives/keyring" } snowbridge-pallet-ethereum-client = { path = "../ethereum-client" } hex-literal = { version = "0.4.1" } @@ -65,8 +65,10 @@ std = [ "pallet-balances/std", "scale-info/std", "serde", + "snowbridge-beacon-primitives/std", "snowbridge-core/std", "snowbridge-ethereum/std", + "snowbridge-pallet-inbound-queue-fixtures?/std", "snowbridge-router-primitives/std", "sp-core/std", "sp-io/std", @@ -83,9 +85,9 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "hex-literal", "pallet-balances/runtime-benchmarks", - "snowbridge-beacon-primitives", "snowbridge-core/runtime-benchmarks", "snowbridge-pallet-ethereum-client/runtime-benchmarks", + "snowbridge-pallet-inbound-queue-fixtures/runtime-benchmarks", "snowbridge-router-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/README.md b/bridges/snowbridge/pallets/inbound-queue/README.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/inbound-queue/README.md rename to bridges/snowbridge/pallets/inbound-queue/README.md diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..64605a42f0d383d838429eb9b82b5f6cf238ab09 --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "snowbridge-pallet-inbound-queue-fixtures" +description = "Snowbridge Inbound Queue Test Fixtures" +version = "0.10.0" +authors = ["Snowfork "] +edition.workspace = true +repository.workspace = true +license = "Apache-2.0" +categories = ["cryptography::cryptocurrencies"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +hex-literal = { version = "0.4.1" } +sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } +frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../../substrate/frame/system", default-features = false } +snowbridge-core = { path = "../../../primitives/core", default-features = false } +snowbridge-beacon-primitives = { path = "../../../primitives/beacon", default-features = false } + +[features] +default = ["std"] +std = [ + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "snowbridge-beacon-primitives/std", + "snowbridge-core/std", + "sp-core/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", +] diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..4f3445b2905364147d3974988fee0fad2f387d07 --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +#![cfg_attr(not(feature = "std"), no_std)] + +use snowbridge_beacon_primitives::CompactExecutionHeader; +use snowbridge_core::inbound::Message; +use sp_core::RuntimeDebug; + +pub mod register_token; +pub mod register_token_with_insufficient_fee; +pub mod send_token; +pub mod send_token_to_penpal; + +#[derive(Clone, RuntimeDebug)] +pub struct InboundQueueFixture { + pub execution_header: CompactExecutionHeader, + pub message: Message, +} diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/src/benchmarking/fixtures.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token.rs similarity index 73% rename from bridges/snowbridge/parachain/pallets/inbound-queue/src/benchmarking/fixtures.rs rename to bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token.rs index 4f2382d072abd5c145b800f0126ea076f3549cce..b8d510e6b13d02420191591ff917eb0366f818d6 100644 --- a/bridges/snowbridge/parachain/pallets/inbound-queue/src/benchmarking/fixtures.rs +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token.rs @@ -1,20 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See ethereum client README.md for instructions to generate + +use crate::InboundQueueFixture; use hex_literal::hex; use snowbridge_beacon_primitives::CompactExecutionHeader; use snowbridge_core::inbound::{Log, Message, Proof}; use sp_std::vec; -pub struct InboundQueueTest { - pub execution_header: CompactExecutionHeader, - pub message: Message, -} - -pub fn make_create_message() -> InboundQueueTest { - InboundQueueTest{ +pub fn make_register_token_message() -> InboundQueueFixture { + InboundQueueFixture { execution_header: CompactExecutionHeader{ - parent_hash: hex!("b5608f0af7c3b6fe5c593772fc25436b8d6549eb236adb0855c6ad33e0004e04").into(), - block_number: 115, - state_root: hex!("47ed174789836c622499d9659a4ac32c3b91a7b15642d39b0a11b82ff23995c1").into(), - receipts_root: hex!("42c08b5303fcdf9e49c833fe5f1182cdbc8206bf8aec581125fc34aba11e1f1a").into(), + parent_hash: hex!("d5de3dd02c96dbdc8aaa4db70a1e9fdab5ded5f4d52f18798acd56a3d37d1ad6").into(), + block_number: 772, + state_root: hex!("49cba2a79b23ad74cefe80c3a96699825d1cda0f75bfceb587c5549211c86245").into(), + receipts_root: hex!("7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095").into(), }, message: Message { event_log: Log { @@ -27,12 +28,12 @@ pub fn make_create_message() -> InboundQueueTest { data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e00a736aa00000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").into(), }, proof: Proof { - block_hash: hex!("add15f439c8a57fe375d0a679870b1359921d70cb0e3e44f0dd3e272849f4097").into(), + block_hash: hex!("392182a385b3a417e8ddea8b252953ee81e6ec0fb09d9056c96c89fbeb703a3f").into(), tx_index: 0, data: (vec![ - hex!("42c08b5303fcdf9e49c833fe5f1182cdbc8206bf8aec581125fc34aba11e1f1a").to_vec(), + hex!("7b1f61b9714c080ef0be014e01657a15f45f0304b477beebc7ca5596c8033095").to_vec(), ], vec![ - hex!("f9028e822080b9028802f90284018301ed20b9010000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010f90179f85894eda338e4dc46038493b885327842fd3e301cab39e1a0f78bb28d4b1d7da699e5c0bc2be29c2b04b5aab6aacf6298fe5304f9db9c6d7ea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7df9011c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a05f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0b8a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e00a736aa00000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("f9028e822080b9028802f90284018301d205b9010000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010f90179f85894eda338e4dc46038493b885327842fd3e301cab39e1a0f78bb28d4b1d7da699e5c0bc2be29c2b04b5aab6aacf6298fe5304f9db9c6d7ea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7df9011c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a05f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0b8a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e00a736aa00000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").to_vec(), ]), }, }, diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token_with_insufficient_fee.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token_with_insufficient_fee.rs new file mode 100644 index 0000000000000000000000000000000000000000..82ff2283101e331395b06dd14f3876076e71722e --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/register_token_with_insufficient_fee.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See ethereum client README.md for instructions to generate + +use crate::InboundQueueFixture; +use hex_literal::hex; +use snowbridge_beacon_primitives::CompactExecutionHeader; +use snowbridge_core::inbound::{Log, Message, Proof}; +use sp_std::vec; + +pub fn make_register_token_with_infufficient_fee_message() -> InboundQueueFixture { + InboundQueueFixture { + execution_header: CompactExecutionHeader{ + parent_hash: hex!("998e81dc6df788a920b67e058fbde0dc3f4ec6f11f3f7cd8c3148e6d99584885").into(), + block_number: 338, + state_root: hex!("30ef9c9db2609de19bbc6c3cbeddac889e82bbcb2db20304b3abdfbdc7134cbf").into(), + receipts_root: hex!("969335c3132a007cb8b5886a3c23dd8da63cba04aeda29857a86ee1c13dae782").into(), + }, + message: Message { + event_log: Log { + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), + ], + // insufficient xcm fee as only 1000(hex:e803) + data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e00a736aa00000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7de8030000000000000000000000000000000000000000000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("5976f37f0e331d194eb331df74355ef47565c3a1bd11c95a45b681f6917085c1").into(), + tx_index: 0, + data: (vec![ + hex!("969335c3132a007cb8b5886a3c23dd8da63cba04aeda29857a86ee1c13dae782").to_vec(), + ], vec![ + hex!("f9028e822080b9028802f90284018301d205b9010000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000080000000000000000000000000000004000000000080000000000000000000000000000000000010100000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000040004000000000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000200000000000010f90179f85894eda338e4dc46038493b885327842fd3e301cab39e1a0f78bb28d4b1d7da699e5c0bc2be29c2b04b5aab6aacf6298fe5304f9db9c6d7ea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7df9011c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a05f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0b8a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e00a736aa00000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7de8030000000000000000000000000000000000000000000000000000000000000000").to_vec(), + ]), + }, + }, + } +} diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_token.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_token.rs new file mode 100755 index 0000000000000000000000000000000000000000..2562217100eaf7cd0c5dff420c55826b277d3570 --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_token.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See ethereum client README.md for instructions to generate + +use crate::InboundQueueFixture; +use hex_literal::hex; +use snowbridge_beacon_primitives::CompactExecutionHeader; +use snowbridge_core::inbound::{Log, Message, Proof}; +use sp_std::vec; + +pub fn make_send_token_message() -> InboundQueueFixture { + InboundQueueFixture { + execution_header: CompactExecutionHeader{ + parent_hash: hex!("920cecde45d428e3a77590b70f8533cf4c2c36917b8a7b74c915e7fa3dae7075").into(), + block_number: 1148, + state_root: hex!("bbc6ba0e9940d641afecbbaf3f97abd2b9ffaf2f6bd4879c4a71e659eca89978").into(), + receipts_root: hex!("9f3340b57eddc1f86de30776db57faeca80269a3dd459031741988dec240ce34").into(), + }, + message: Message { + event_log: Log { + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26f").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f00a736aa00000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("d3c155f123c3cbff22f3d7869283e02179edea9ffa7a5e9a4d8414c2a6b8991f").into(), + tx_index: 0, + data: (vec![ + hex!("9f3340b57eddc1f86de30776db57faeca80269a3dd459031741988dec240ce34").to_vec(), + ], vec![ + hex!("f90451822080b9044b02f90447018301bcb9b9010000800000000000000000000020000000000000000000004000000000000000000400000000000000000000001000000010000000000000000000000008000000200000000000000001000008000000000000000000000000000000008000080000000000200000000000000000000000000100000000000000000011000000000000020200000000000000000000000000003000000040080008000000000000000000040044000021000000002000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000200800000000000f9033cf89b9487d1f7fdfee7f651fabc8bfcb6e086c278b77a7df863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000057a2d4ff0c3866d96556884bf09fecdd7ccd530ca00000000000000000000000000000000000000000000000000de0b6b3a7640000f9015d94eda338e4dc46038493b885327842fd3e301cab39f884a024c5d2de620c6e25186ae16f6919eba93b6e2c1a33857cc419d9f3a00d6967e9a000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7da000000000000000000000000000000000000000000000000000000000000003e8b8c000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000208eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48f9013c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a0c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26fb8c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f00a736aa00000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000").to_vec(), + ]), + }, + }, + } +} diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_token_to_penpal.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_token_to_penpal.rs new file mode 100755 index 0000000000000000000000000000000000000000..86ba3f7ecc18f3743fd22592fc23a50f26b447d7 --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_token_to_penpal.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See ethereum client README.md for instructions to generate + +use crate::InboundQueueFixture; +use hex_literal::hex; +use snowbridge_beacon_primitives::CompactExecutionHeader; +use snowbridge_core::inbound::{Log, Message, Proof}; +use sp_std::vec; + +pub fn make_send_token_to_penpal_message() -> InboundQueueFixture { + InboundQueueFixture { + execution_header: CompactExecutionHeader{ + parent_hash: hex!("434148c290f27ee4be34fa344cd7608bf942a4541b27c9d868439631b3f37a8d").into(), + block_number: 816, + state_root: hex!("595e643f9095870e30e85e2bbef7d9e3a39df5aae839d26cf455d3dbf3e5a539").into(), + receipts_root: hex!("c40ab2c4abcfdea4f42195e0ad822806e5423108021c3b542646c7193319a6c1").into(), + }, + message: Message { + event_log: Log { + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26f").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000007300a736aa00000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d01d00700001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c00286bee000000000000000000000000000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("6c49a7f8fb2014a23e58a949c95a6743174589a7ce83434b073dc05dec402f3d").into(), + tx_index: 0, + data: (vec![ + hex!("c40ab2c4abcfdea4f42195e0ad822806e5423108021c3b542646c7193319a6c1").to_vec(), + ], vec![ + hex!("f90471822080b9046b02f90467018301d30fb9010000800000000000000000000000000000000000000000004000000000000000000400000000004000000000001000000010000000000000000000000008000000200000000000000001000008000000000000000000000000000000008000080000000000200000000000000000000000000100000000000000000011000000000000020000000000000000000000000000003000000000080018000000000000000000040044000021000000002000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000200820000000000f9035cf89b9487d1f7fdfee7f651fabc8bfcb6e086c278b77a7df863a0ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000057a2d4ff0c3866d96556884bf09fecdd7ccd530ca00000000000000000000000000000000000000000000000000de0b6b3a7640000f9015d94eda338e4dc46038493b885327842fd3e301cab39f884a024c5d2de620c6e25186ae16f6919eba93b6e2c1a33857cc419d9f3a00d6967e9a000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7da000000000000000000000000000000000000000000000000000000000000007d0b8c000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000201cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07cf9015c94eda338e4dc46038493b885327842fd3e301cab39f863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a0c8eaf22f2cb07bac4679df0a660e7115ed87fcfd4e32ac269f6540265bbbd26fb8e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000007300a736aa00000000000187d1f7fdfee7f651fabc8bfcb6e086c278b77a7d01d00700001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c00286bee000000000000000000000000000064a7b3b6e00d000000000000000000e40b5402000000000000000000000000000000000000000000000000").to_vec(), + ]), + }, + }, + } +} diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/src/benchmarking/mod.rs b/bridges/snowbridge/pallets/inbound-queue/src/benchmarking/mod.rs similarity index 89% rename from bridges/snowbridge/parachain/pallets/inbound-queue/src/benchmarking/mod.rs rename to bridges/snowbridge/pallets/inbound-queue/src/benchmarking/mod.rs index c10de9dff2ff0e288068274c1d8b4075a471cf25..931befa2ac67810eaf7aac1b8156a46fc398f7a2 100644 --- a/bridges/snowbridge/parachain/pallets/inbound-queue/src/benchmarking/mod.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/benchmarking/mod.rs @@ -1,5 +1,3 @@ -mod fixtures; - // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork use super::*; @@ -8,17 +6,17 @@ use crate::Pallet as InboundQueue; use frame_benchmarking::v2::*; use frame_support::assert_ok; use frame_system::RawOrigin; +use snowbridge_pallet_inbound_queue_fixtures::register_token::make_register_token_message; #[benchmarks] mod benchmarks { use super::*; - use crate::benchmarking::fixtures::make_create_message; #[benchmark] fn submit() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); - let create_message = make_create_message(); + let create_message = make_register_token_message(); T::Helper::initialize_storage( create_message.message.proof.block_hash, diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/src/envelope.rs b/bridges/snowbridge/pallets/inbound-queue/src/envelope.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/inbound-queue/src/envelope.rs rename to bridges/snowbridge/pallets/inbound-queue/src/envelope.rs diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/inbound-queue/src/lib.rs rename to bridges/snowbridge/pallets/inbound-queue/src/lib.rs diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs similarity index 96% rename from bridges/snowbridge/parachain/pallets/inbound-queue/src/mock.rs rename to bridges/snowbridge/pallets/inbound-queue/src/mock.rs index 39374aa3d71ad85f2b7c94b966699888e9cd195a..749fb0367f332d743b01ad9d56238106ced36e72 100644 --- a/bridges/snowbridge/parachain/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -3,7 +3,7 @@ use super::*; use frame_support::{ - parameter_types, + derive_impl, parameter_types, traits::{ConstU128, ConstU32, Everything}, weights::IdentityFee, }; @@ -21,7 +21,7 @@ use sp_runtime::{ BuildStorage, FixedU128, MultiSignature, }; use sp_std::convert::From; -use xcm::v4::{prelude::*, SendXcm}; +use xcm::{latest::SendXcm, prelude::*}; use xcm_executor::AssetsInHolding; use crate::{self as inbound_queue}; @@ -47,10 +47,9 @@ parameter_types! { type Balance = u128; +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type RuntimeTask = RuntimeTask; @@ -60,16 +59,8 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; } @@ -88,7 +79,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { @@ -110,6 +100,10 @@ parameter_types! { version: [3, 0, 0, 1], // 0x03000001 epoch: 0, }, + deneb: Fork { + version: [4, 0, 0, 1], // 0x04000001 + epoch: 4294967295, + } }; } diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/src/test.rs b/bridges/snowbridge/pallets/inbound-queue/src/test.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/inbound-queue/src/test.rs rename to bridges/snowbridge/pallets/inbound-queue/src/test.rs diff --git a/bridges/snowbridge/parachain/pallets/inbound-queue/src/weights.rs b/bridges/snowbridge/pallets/inbound-queue/src/weights.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/inbound-queue/src/weights.rs rename to bridges/snowbridge/pallets/inbound-queue/src/weights.rs diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/Cargo.toml similarity index 55% rename from bridges/snowbridge/parachain/pallets/outbound-queue/Cargo.toml rename to bridges/snowbridge/pallets/outbound-queue/Cargo.toml index e499ab887294ea6f35849d5496344c69900d3ffc..f16a28cb1e457d9ebfb7804fa013e5b57858f79e 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-outbound-queue" description = "Snowbridge Outbound Queue Pallet" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -15,31 +15,31 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -serde = { version = "1.0.195", features = ["alloc", "derive"], default-features = false } +serde = { features = ["alloc", "derive"], workspace = true } codec = { version = "3.6.1", package = "parity-scale-codec", default-features = false, features = ["derive"] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } hex-literal = { version = "0.4.1", optional = true } -frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } -sp-arithmetic = { path = "../../../../../substrate/primitives/arithmetic", default-features = false } +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-arithmetic = { path = "../../../../substrate/primitives/arithmetic", default-features = false } -bridge-hub-common = { path = "../../../../../cumulus/parachains/runtimes/bridge-hubs/common", default-features = false } +bridge-hub-common = { path = "../../../../cumulus/parachains/runtimes/bridge-hubs/common", default-features = false } snowbridge-core = { path = "../../primitives/core", default-features = false, features = ["serde"] } snowbridge-outbound-queue-merkle-tree = { path = "merkle-tree", default-features = false } -ethabi = { git = "https://github.com/snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } +ethabi = { package = "ethabi-decode", version = "1.0.0", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } [dev-dependencies] -pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false } -sp-keyring = { path = "../../../../../substrate/primitives/keyring" } +pallet-message-queue = { path = "../../../../substrate/frame/message-queue", default-features = false } +sp-keyring = { path = "../../../../substrate/primitives/keyring" } hex-literal = { version = "0.4.1" } [features] diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/README.md b/bridges/snowbridge/pallets/outbound-queue/README.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/README.md rename to bridges/snowbridge/pallets/outbound-queue/README.md diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml similarity index 73% rename from bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/Cargo.toml rename to bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml index 27c4ae02e52e7272b334e8ea6b587d3950e21a8b..0606e9de33056c9dffae50befcc1da5e865dca44 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-outbound-queue-merkle-tree" description = "Snowbridge Outbound Queue Merkle Tree" -version = "0.9.0" +version = "0.3.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -18,14 +18,15 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { version = "3.1.5", package = "parity-scale-codec", default-features = false, features = ["derive"] } scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } -sp-core = { path = "../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../substrate/primitives/runtime", default-features = false } +sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } +sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } [dev-dependencies] hex-literal = { version = "0.4.1" } env_logger = "0.9" hex = "0.4" array-bytes = "4.1" +sp-crypto-hashing = { path = "../../../../../substrate/primitives/crypto/hashing" } [features] default = ["std"] diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/README.md b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/README.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/README.md rename to bridges/snowbridge/pallets/outbound-queue/merkle-tree/README.md diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs similarity index 99% rename from bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/src/lib.rs rename to bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs index d03eb578ef4d51f9505e63aa98e0a42b107a9958..8c91ccd04d9a6b001db3cf561b95ff0723d700aa 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/merkle-tree/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs @@ -325,7 +325,7 @@ where mod tests { use super::*; use hex_literal::hex; - use sp_core::keccak_256; + use sp_crypto_hashing::keccak_256; use sp_runtime::traits::Keccak256; fn make_leaves(count: u64) -> Vec { diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/runtime-api/Cargo.toml similarity index 64% rename from bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api/Cargo.toml rename to bridges/snowbridge/pallets/outbound-queue/runtime-api/Cargo.toml index 1f2b51a6752f974e4a6b9c6074e73819d5bdbc10..cb68fd0a250a92e7f6a6693f3aebf1c8553308aa 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/runtime-api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-outbound-queue-runtime-api" description = "Snowbridge Outbound Queue Runtime API" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -16,11 +16,11 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { version = "3.1.5", package = "parity-scale-codec", features = ["derive"], default-features = false } -sp-core = { path = "../../../../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../../../../substrate/primitives/std", default-features = false } -sp-api = { path = "../../../../../../substrate/primitives/api", default-features = false } -frame-support = { path = "../../../../../../substrate/frame/support", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../../polkadot/xcm", default-features = false } +sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } +sp-api = { path = "../../../../../substrate/primitives/api", default-features = false } +frame-support = { path = "../../../../../substrate/frame/support", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } snowbridge-outbound-queue-merkle-tree = { path = "../merkle-tree", default-features = false } snowbridge-core = { path = "../../../primitives/core", default-features = false } diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api/README.md b/bridges/snowbridge/pallets/outbound-queue/runtime-api/README.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api/README.md rename to bridges/snowbridge/pallets/outbound-queue/runtime-api/README.md diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/runtime-api/src/lib.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api/src/lib.rs rename to bridges/snowbridge/pallets/outbound-queue/runtime-api/src/lib.rs diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/api.rs b/bridges/snowbridge/pallets/outbound-queue/src/api.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/api.rs rename to bridges/snowbridge/pallets/outbound-queue/src/api.rs diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/benchmarking.rs b/bridges/snowbridge/pallets/outbound-queue/src/benchmarking.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/benchmarking.rs rename to bridges/snowbridge/pallets/outbound-queue/src/benchmarking.rs diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/src/lib.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/lib.rs rename to bridges/snowbridge/pallets/outbound-queue/src/lib.rs diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs similarity index 93% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/mock.rs rename to bridges/snowbridge/pallets/outbound-queue/src/mock.rs index dd8fee4e2ed08ec0f3090b765fa882b063a98300..6e78fb4467210e3cb5e1eb581b377cbbfeac74ad 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs @@ -3,7 +3,7 @@ use super::*; use frame_support::{ - parameter_types, + derive_impl, parameter_types, traits::{Everything, Hooks}, weights::IdentityFee, }; @@ -37,10 +37,9 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type RuntimeTask = RuntimeTask; @@ -50,16 +49,7 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; } diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/process_message_impl.rs b/bridges/snowbridge/pallets/outbound-queue/src/process_message_impl.rs similarity index 86% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/process_message_impl.rs rename to bridges/snowbridge/pallets/outbound-queue/src/process_message_impl.rs index 575ed9e0e7c225a8be4b3ad09f67a26975f5a94a..731aa6fa6d5cae98174d4936c8416bcf17ffccce 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/src/process_message_impl.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/process_message_impl.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork //! Implementation for [`frame_support::traits::ProcessMessage`] use super::*; use crate::weights::WeightInfo; diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/send_message_impl.rs b/bridges/snowbridge/pallets/outbound-queue/src/send_message_impl.rs similarity index 96% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/send_message_impl.rs rename to bridges/snowbridge/pallets/outbound-queue/src/send_message_impl.rs index a84e2c520e59000ab44ae6e160ba6071a263bf99..03be61819973d5bb4bcb663b7cd5f928189c9d1d 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/src/send_message_impl.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/send_message_impl.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork //! Implementation for [`snowbridge_core::outbound::SendMessage`] use super::*; use bridge_hub_common::AggregateMessageOrigin; diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/test.rs b/bridges/snowbridge/pallets/outbound-queue/src/test.rs similarity index 85% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/test.rs rename to bridges/snowbridge/pallets/outbound-queue/src/test.rs index 0028d75e7b79eea5ea17947f52b07af32558610b..8ed4a318d68e99181e7f1e0793cc66e23829132a 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/src/test.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/test.rs @@ -11,7 +11,7 @@ use frame_support::{ use codec::Encode; use snowbridge_core::{ outbound::{Command, SendError, SendMessage}, - ParaId, + ParaId, PricingParameters, Rewards, }; use sp_arithmetic::FixedU128; use sp_core::H256; @@ -266,3 +266,47 @@ fn encode_digest_item() { ); }); } + +#[test] +fn validate_messages_with_fees() { + new_tester().execute_with(|| { + let message = mock_message(1000); + let (_, fee) = OutboundQueue::validate(&message).unwrap(); + assert_eq!(fee.local, 698000000); + assert_eq!(fee.remote, 2680000000000); + }); +} + +#[test] +fn test_calculate_fees() { + new_tester().execute_with(|| { + let gas_used: u64 = 250000; + let illegal_price_params: PricingParameters<::Balance> = + PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: 10000_u32.into(), + rewards: Rewards { local: 1_u32.into(), remote: 1_u32.into() }, + }; + let fee = OutboundQueue::calculate_fee(gas_used, illegal_price_params); + assert_eq!(fee.local, 698000000); + assert_eq!(fee.remote, 1000000); + }); +} + +#[test] +fn test_calculate_fees_with_valid_exchange_rate_but_remote_fee_calculated_as_zero() { + new_tester().execute_with(|| { + let gas_used: u64 = 250000; + let illegal_price_params: PricingParameters<::Balance> = + PricingParameters { + exchange_rate: FixedU128::from_rational(1, 1), + fee_per_gas: 1_u32.into(), + rewards: Rewards { local: 1_u32.into(), remote: 1_u32.into() }, + }; + let fee = OutboundQueue::calculate_fee(gas_used, illegal_price_params.clone()); + assert_eq!(fee.local, 698000000); + // Though none zero pricing params the remote fee calculated here is invalid + // which should be avoided + assert_eq!(fee.remote, 0); + }); +} diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/types.rs b/bridges/snowbridge/pallets/outbound-queue/src/types.rs similarity index 94% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/types.rs rename to bridges/snowbridge/pallets/outbound-queue/src/types.rs index 28d400bb9d46a050d22717c4a39f9ab32525cd94..f65a15d3d2f90091b0f5090ca029764d78ef9bf5 100644 --- a/bridges/snowbridge/parachain/pallets/outbound-queue/src/types.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/types.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use codec::{Decode, Encode}; use ethabi::Token; use frame_support::traits::ProcessMessage; diff --git a/bridges/snowbridge/parachain/pallets/outbound-queue/src/weights.rs b/bridges/snowbridge/pallets/outbound-queue/src/weights.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/outbound-queue/src/weights.rs rename to bridges/snowbridge/pallets/outbound-queue/src/weights.rs diff --git a/bridges/snowbridge/parachain/pallets/system/Cargo.toml b/bridges/snowbridge/pallets/system/Cargo.toml similarity index 57% rename from bridges/snowbridge/parachain/pallets/system/Cargo.toml rename to bridges/snowbridge/pallets/system/Cargo.toml index 70155370d196962b4ed8c963d87e75a9a7c578fd..5ad04290de044a2c8ed13aa092f5ea033aaafb97 100644 --- a/bridges/snowbridge/parachain/pallets/system/Cargo.toml +++ b/bridges/snowbridge/pallets/system/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-system" description = "Snowbridge System Pallet" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -19,30 +19,30 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", ] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } -frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -log = { version = "0.4.20", default-features = false } +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +log = { workspace = true } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../polkadot/xcm/xcm-executor", default-features = false } -ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } +ethabi = { package = "ethabi-decode", version = "1.0.0", default-features = false } snowbridge-core = { path = "../../primitives/core", default-features = false } [dev-dependencies] hex = "0.4.1" hex-literal = { version = "0.4.1" } -pallet-balances = { path = "../../../../../substrate/frame/balances" } -sp-keyring = { path = "../../../../../substrate/primitives/keyring" } -polkadot-primitives = { path = "../../../../../polkadot/primitives" } -pallet-message-queue = { path = "../../../../../substrate/frame/message-queue" } +pallet-balances = { path = "../../../../substrate/frame/balances" } +sp-keyring = { path = "../../../../substrate/primitives/keyring" } +polkadot-primitives = { path = "../../../../polkadot/primitives" } +pallet-message-queue = { path = "../../../../substrate/frame/message-queue" } snowbridge-pallet-outbound-queue = { path = "../outbound-queue" } [features] diff --git a/bridges/snowbridge/parachain/pallets/system/README.md b/bridges/snowbridge/pallets/system/README.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/system/README.md rename to bridges/snowbridge/pallets/system/README.md diff --git a/bridges/snowbridge/parachain/pallets/system/runtime-api/Cargo.toml b/bridges/snowbridge/pallets/system/runtime-api/Cargo.toml similarity index 64% rename from bridges/snowbridge/parachain/pallets/system/runtime-api/Cargo.toml rename to bridges/snowbridge/pallets/system/runtime-api/Cargo.toml index 96d5aa522c0336f8dcd01cbd79503a6ad6a8d59c..eb02ae1db529730f51743e79a322e54db44fee51 100644 --- a/bridges/snowbridge/parachain/pallets/system/runtime-api/Cargo.toml +++ b/bridges/snowbridge/pallets/system/runtime-api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-system-runtime-api" description = "Snowbridge System Runtime API" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -18,10 +18,10 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -sp-core = { path = "../../../../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../../../../substrate/primitives/std", default-features = false } -sp-api = { path = "../../../../../../substrate/primitives/api", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../../polkadot/xcm", default-features = false } +sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } +sp-api = { path = "../../../../../substrate/primitives/api", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } snowbridge-core = { path = "../../../primitives/core", default-features = false } [features] diff --git a/bridges/snowbridge/parachain/pallets/system/runtime-api/README.md b/bridges/snowbridge/pallets/system/runtime-api/README.md similarity index 100% rename from bridges/snowbridge/parachain/pallets/system/runtime-api/README.md rename to bridges/snowbridge/pallets/system/runtime-api/README.md diff --git a/bridges/snowbridge/parachain/pallets/system/runtime-api/src/lib.rs b/bridges/snowbridge/pallets/system/runtime-api/src/lib.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/system/runtime-api/src/lib.rs rename to bridges/snowbridge/pallets/system/runtime-api/src/lib.rs diff --git a/bridges/snowbridge/parachain/pallets/system/src/api.rs b/bridges/snowbridge/pallets/system/src/api.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/system/src/api.rs rename to bridges/snowbridge/pallets/system/src/api.rs diff --git a/bridges/snowbridge/parachain/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/system/src/benchmarking.rs rename to bridges/snowbridge/pallets/system/src/benchmarking.rs diff --git a/bridges/snowbridge/parachain/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs similarity index 99% rename from bridges/snowbridge/parachain/pallets/system/src/lib.rs rename to bridges/snowbridge/pallets/system/src/lib.rs index e742bd8e11026bb0aaa89e2f6a4ff3beab607231..6e5ceb5e9b1d42796567c3da5e549b2af3cfd4de 100644 --- a/bridges/snowbridge/parachain/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -37,8 +37,6 @@ //! `force_update_channel` and extrinsics to manage agents and channels for system parachains. #![cfg_attr(not(feature = "std"), no_std)] -pub use pallet::*; - #[cfg(test)] mod mock; @@ -226,6 +224,7 @@ pub mod pallet { Send(SendError), InvalidTokenTransferFees, InvalidPricingParameters, + InvalidUpgradeParameters, } /// The set of registered agents @@ -282,6 +281,11 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; + ensure!( + !impl_address.eq(&H160::zero()) && !impl_code_hash.eq(&H256::zero()), + Error::::InvalidUpgradeParameters + ); + let initializer_params_hash: Option = initializer.as_ref().map(|i| H256::from(blake2_256(i.params.as_ref()))); let command = Command::Upgrade { impl_address, impl_code_hash, initializer }; diff --git a/bridges/snowbridge/parachain/pallets/system/src/migration.rs b/bridges/snowbridge/pallets/system/src/migration.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/system/src/migration.rs rename to bridges/snowbridge/pallets/system/src/migration.rs diff --git a/bridges/snowbridge/parachain/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs similarity index 95% rename from bridges/snowbridge/parachain/pallets/system/src/mock.rs rename to bridges/snowbridge/pallets/system/src/mock.rs index bc22957813279e462fe63fb4c110d7df9fe91ce3..de2970dd550ba75fe42de08dc4d297cd5cccdf1f 100644 --- a/bridges/snowbridge/parachain/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -2,8 +2,8 @@ // SPDX-FileCopyrightText: 2023 Snowfork use crate as snowbridge_system; use frame_support::{ - parameter_types, - traits::{tokens::fungible::Mutate, ConstU128, ConstU16, ConstU64, ConstU8}, + derive_impl, parameter_types, + traits::{tokens::fungible::Mutate, ConstU128, ConstU64, ConstU8}, weights::IdentityFee, PalletId, }; @@ -95,11 +95,9 @@ frame_support::construct_runtime!( } ); +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type RuntimeTask = RuntimeTask; @@ -109,15 +107,8 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; - type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; } @@ -136,7 +127,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_xcm_origin::Config for Test { diff --git a/bridges/snowbridge/parachain/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs similarity index 95% rename from bridges/snowbridge/parachain/pallets/system/src/tests.rs rename to bridges/snowbridge/pallets/system/src/tests.rs index 8b417c258ea1c0316301cef5cf2d3061756f1811..09f24195a30ad8ee13f4587c54102bc24eed4134 100644 --- a/bridges/snowbridge/parachain/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -3,7 +3,7 @@ use crate::{mock::*, *}; use frame_support::{assert_noop, assert_ok}; use hex_literal::hex; -use snowbridge_core::{eth, sibling_sovereign_account_raw}; +use snowbridge_core::eth; use sp_core::H256; use sp_runtime::{AccountId32, DispatchError::BadOrigin, TokenError}; @@ -84,8 +84,8 @@ fn create_agent_bad_origin() { fn upgrade_as_root() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::root(); - let address: H160 = Default::default(); - let code_hash: H256 = Default::default(); + let address: H160 = [1_u8; 20].into(); + let code_hash: H256 = [1_u8; 32].into(); assert_ok!(EthereumSystem::upgrade(origin, address, code_hash, None)); @@ -112,8 +112,8 @@ fn upgrade_as_signed_fails() { fn upgrade_with_params() { new_test_ext(true).execute_with(|| { let origin = RuntimeOrigin::root(); - let address: H160 = Default::default(); - let code_hash: H256 = Default::default(); + let address: H160 = [1_u8; 20].into(); + let code_hash: H256 = [1_u8; 32].into(); let initializer: Option = Some(Initializer { params: [0; 256].into(), maximum_required_gas: 10000 }); assert_ok!(EthereumSystem::upgrade(origin, address, code_hash, initializer)); @@ -536,22 +536,6 @@ fn force_transfer_native_from_agent_bad_origin() { // conversions for devops purposes. They need to be removed here and incorporated into a command // line utility. -#[ignore] -#[test] -fn check_sibling_sovereign_account() { - new_test_ext(true).execute_with(|| { - let para_id = 1001; - let sovereign_account = sibling_sovereign_account::(para_id.into()); - let sovereign_account_raw = sibling_sovereign_account_raw(para_id.into()); - println!( - "Sovereign account for parachain {}: {:#?}", - para_id, - hex::encode(sovereign_account.clone()) - ); - assert_eq!(sovereign_account, sovereign_account_raw.into()); - }); -} - #[test] fn charge_fee_for_create_agent() { new_test_ext(true).execute_with(|| { @@ -621,8 +605,8 @@ fn charge_fee_for_upgrade() { new_test_ext(true).execute_with(|| { let para_id: u32 = TestParaId::get(); let origin = RuntimeOrigin::root(); - let address: H160 = Default::default(); - let code_hash: H256 = Default::default(); + let address: H160 = [1_u8; 20].into(); + let code_hash: H256 = [1_u8; 32].into(); let initializer: Option = Some(Initializer { params: [0; 256].into(), maximum_required_gas: 10000 }); assert_ok!(EthereumSystem::upgrade(origin, address, code_hash, initializer.clone())); diff --git a/bridges/snowbridge/parachain/pallets/system/src/weights.rs b/bridges/snowbridge/pallets/system/src/weights.rs similarity index 100% rename from bridges/snowbridge/parachain/pallets/system/src/weights.rs rename to bridges/snowbridge/pallets/system/src/weights.rs diff --git a/bridges/snowbridge/parachain/LICENSE b/bridges/snowbridge/parachain/LICENSE deleted file mode 100644 index 261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/bridges/snowbridge/parachain/README.md b/bridges/snowbridge/parachain/README.md deleted file mode 100644 index a38910da3164e853f54b284f8d38795d4220aafe..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/README.md +++ /dev/null @@ -1,127 +0,0 @@ -# Snowbridge · -[![codecov](https://codecov.io/gh/Snowfork/snowbridge/branch/main/graph/badge.svg?token=9hvgSws4rN)] -(https://codecov.io/gh/Snowfork/snowbridge) -![GitHub](https://img.shields.io/github/license/Snowfork/snowbridge) - -Snowbridge is a trustless bridge between Polkadot and Ethereum. For documentation, visit https://docs.snowbridge.network. - -## Components - -### Parachain - -Polkadot parachain and our pallets. See [parachain/README.md](https://github.com/Snowfork/snowbridge/blob/main/parachain/README.md). - -### Contracts - -Ethereum contracts and unit tests. See [contracts/README.md](https://github.com/Snowfork/snowbridge/blob/main/contracts/README.md) - -### Relayer - -Off-chain relayer services for relaying messages between Polkadot and Ethereum. See -[relayer/README.md](https://github.com/Snowfork/snowbridge/blob/main/relayer/README.md) - -### Local Testnet - -Scripts to provision a local testnet, running the above services to bridge between local deployments of Polkadot and -Ethereum. See [web/packages/test/README.md](https://github.com/Snowfork/snowbridge/blob/main/web/packages/test/README.md). - -### Smoke Tests - -Integration tests for our local testnet. See [smoketest/README.md](https://github.com/Snowfork/snowbridge/blob/main/smoketest/README.md). - -## Development - -We use the Nix package manager to provide a reproducible and maintainable developer environment. - -After [installing nix](https://nixos.org/download.html) Nix, enable [flakes](https://nixos.wiki/wiki/Flakes): - -```sh -mkdir -p ~/.config/nix -echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf -``` - -Then activate a developer shell in the root of our repo, where -[`flake.nix`](https://github.com/Snowfork/snowbridge/blob/main/flake.nix) is located: - -```sh -nix develop -``` - -Also make sure to run this initialization script once: -```sh -scripts/init.sh -``` - -### Support for code editors - -To ensure your code editor (such as VS Code) can execute tools in the nix shell, startup your editor within the -interactive shell. - -Example for VS Code: - -```sh -nix develop -code . -``` - -### Custom shells - -The developer shell is bash by default. To preserve your existing shell: - -```sh -nix develop --command $SHELL -``` - -### Automatic developer shells - -To automatically enter the developer shell whenever you open the project, install -[`direnv`](https://direnv.net/docs/installation.html) and use the template `.envrc`: - -```sh -cp .envrc.example .envrc -direnv allow -``` - -### Upgrading the Rust toolchain - -Sometimes we would like to upgrade rust toolchain. First update `parachain/rust-toolchain.toml` as required and then -update `flake.lock` running -```sh -nix flake lock --update-input rust-overlay -``` - -## Troubleshooting - -Check the contents of all `.envrc` files. - -Remove untracked files: -```sh -git clean -idx -``` - -Ensure that the current Rust toolchain is the one selected in `scripts/init.sh`. - -Ensure submodules are up-to-date: -```sh -git submodule update -``` - -Check untracked files & directories: -```sh -git clean -ndx | awk '{print $3}' -``` -After removing `node_modules` directories (eg. with `git clean above`), clear the pnpm cache: -```sh -pnpm store prune -``` - -Check Nix config in `~/.config/nix/nix.conf`. - -Run a pure developer shell (note that this removes access to your local tools): -```sh -nix develop -i --pure-eval -``` - -## Security - -The security policy and procedures can be found in SECURITY.md. diff --git a/bridges/snowbridge/parachain/pallets/ethereum-beacon-client/src/mock.rs b/bridges/snowbridge/parachain/pallets/ethereum-beacon-client/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..77b5c1aa631db89a986837f258ee7dea45a580d0 --- /dev/null +++ b/bridges/snowbridge/parachain/pallets/ethereum-beacon-client/src/mock.rs @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use crate as ethereum_beacon_client; +use frame_support::parameter_types; +use pallet_timestamp; +use primitives::{Fork, ForkVersions}; +use sp_core::H256; +use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; + +#[cfg(not(feature = "beacon-spec-mainnet"))] +pub mod minimal { + use super::*; + + use crate::config; + use frame_support::derive_impl; + use hex_literal::hex; + use primitives::CompactExecutionHeader; + use snowbridge_core::inbound::{Log, Proof}; + use sp_runtime::BuildStorage; + use std::{fs::File, path::PathBuf}; + + type Block = frame_system::mocking::MockBlock; + + frame_support::construct_runtime!( + pub enum Test { + System: frame_system::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + EthereumBeaconClient: ethereum_beacon_client::{Pallet, Call, Storage, Event}, + } + ); + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; + } + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type PalletInfo = PalletInfo; + type SS58Prefix = SS58Prefix; + type Nonce = u64; + type Block = Block; + } + + impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = (); + type WeightInfo = (); + } + + parameter_types! { + pub const ExecutionHeadersPruneThreshold: u32 = 10; + pub const ChainForkVersions: ForkVersions = ForkVersions{ + genesis: Fork { + version: [0, 0, 0, 1], // 0x00000001 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 1], // 0x01000001 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 1], // 0x02000001 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 1], // 0x03000001 + epoch: 0, + }, + }; + } + + impl ethereum_beacon_client::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold; + type WeightInfo = (); + } + + // Build genesis storage according to the mock runtime. + pub fn new_tester() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + let _ = ext.execute_with(|| Timestamp::set(RuntimeOrigin::signed(1), 30_000)); + ext + } + + fn load_fixture(basename: &str) -> Result + where + T: for<'de> serde::Deserialize<'de>, + { + let filepath: PathBuf = + [env!("CARGO_MANIFEST_DIR"), "tests", "fixtures", basename].iter().collect(); + serde_json::from_reader(File::open(filepath).unwrap()) + } + + pub fn load_execution_header_update_fixture() -> primitives::ExecutionHeaderUpdate { + load_fixture("execution-header-update.minimal.json").unwrap() + } + + pub fn load_checkpoint_update_fixture( + ) -> primitives::CheckpointUpdate<{ config::SYNC_COMMITTEE_SIZE }> { + load_fixture("initial-checkpoint.minimal.json").unwrap() + } + + pub fn load_sync_committee_update_fixture( + ) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("sync-committee-update.minimal.json").unwrap() + } + + pub fn load_finalized_header_update_fixture( + ) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("finalized-header-update.minimal.json").unwrap() + } + + pub fn load_next_sync_committee_update_fixture( + ) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("next-sync-committee-update.minimal.json").unwrap() + } + + pub fn load_next_finalized_header_update_fixture( + ) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { + load_fixture("next-finalized-header-update.minimal.json").unwrap() + } + + pub fn get_message_verification_payload() -> (Log, Proof) { + ( + Log { + address: hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), + topics: vec![ + hex!("1b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ad").into(), + hex!("00000000000000000000000000000000000000000000000000000000000003e8").into(), + hex!("0000000000000000000000000000000000000000000000000000000000000001").into(), + ], + data: hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000").into(), + }, + Proof { + block_hash: hex!("05aaa60b0f27cce9e71909508527264b77ee14da7b5bf915fcc4e32715333213").into(), + tx_index: 0, + data: (vec![ + hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb").to_vec(), + hex!("d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c185510").to_vec(), + hex!("b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646").to_vec(), + ], vec![ + hex!("f90131a0b601337b3aa10a671caa724eba641e759399979856141d3aea6b6b4ac59b889ba00c7d5dd48be9060221a02fb8fa213860b4c50d47046c8fa65ffaba5737d569e0a094601b62a1086cd9c9cb71a7ebff9e718f3217fd6e837efe4246733c0a196f63a06a4b0dd0aefc37b3c77828c8f07d1b7a2455ceb5dbfd3c77d7d6aeeddc2f7e8ca0d6e8e23142cdd8ec219e1f5d8b56aa18e456702b195deeaa210327284d42ade4a08a313d4c87023005d1ab631bbfe3f5de1e405d0e66d0bef3e033f1e5711b5521a0bf09a5d9a48b10ade82b8d6a5362a15921c8b5228a3487479b467db97411d82fa0f95cccae2a7c572ef3c566503e30bac2b2feb2d2f26eebf6d870dcf7f8cf59cea0d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c1855108080808080808080").to_vec(), + hex!("f851a0b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646a060a634b9280e3a23fb63375e7bbdd9ab07fd379ab6a67e2312bbc112195fa358808080808080808080808080808080").to_vec(), + hex!("f9030820b9030402f90300018301d6e2b9010000000000000800000000000020040008000000000000000000000000400000008000000000000000000000000000000000000000000000000000000000042010000000001000000000000000000000000000000000040000000000000000000000000000000000000000000000008000000000000000002000000000000000000000000200000000000000200000000000100000000040000001000200008000000000000200000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000f901f5f87a942ffa5ecdbe006d30397c7636d3e015eee251369ff842a0c965575a00553e094ca7c5d14f02e107c258dda06867cbf9e0e69f80e71bbcc1a000000000000000000000000000000000000000000000000000000000000003e8a000000000000000000000000000000000000000000000000000000000000003e8f9011c94ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0f863a01b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ada000000000000000000000000000000000000000000000000000000000000003e8a00000000000000000000000000000000000000000000000000000000000000001b8a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000f858948cf6147918a5cbb672703f879f385036f8793a24e1a01449abf21e49fd025f33495e77f7b1461caefdd3d4bb646424a3f445c4576a5ba0000000000000000000000000440edffa1352b13227e8ee646f3ea37456dec701").to_vec(), + ]), + } + ) + } + + pub fn get_message_verification_header() -> CompactExecutionHeader { + CompactExecutionHeader { + parent_hash: hex!("04a7f6ab8282203562c62f38b0ab41d32aaebe2c7ea687702b463148a6429e04") + .into(), + block_number: 55, + state_root: hex!("894d968712976d613519f973a317cb0781c7b039c89f27ea2b7ca193f7befdb3") + .into(), + receipts_root: hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb") + .into(), + } + } +} + +#[cfg(feature = "beacon-spec-mainnet")] +pub mod mainnet { + use super::*; + use frame_support::derive_impl; + + type Block = frame_system::mocking::MockBlock; + use sp_runtime::BuildStorage; + + frame_support::construct_runtime!( + pub enum Test { + System: frame_system::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + EthereumBeaconClient: ethereum_beacon_client::{Pallet, Call, Storage, Event}, + } + ); + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; + } + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type PalletInfo = PalletInfo; + type SS58Prefix = SS58Prefix; + type Nonce = u64; + type Block = Block; + } + + impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = (); + type WeightInfo = (); + } + + parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions{ + genesis: Fork { + version: [0, 0, 16, 32], // 0x00001020 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 16, 32], // 0x01001020 + epoch: 36660, + }, + bellatrix: Fork { + version: [2, 0, 16, 32], // 0x02001020 + epoch: 112260, + }, + capella: Fork { + version: [3, 0, 16, 32], // 0x03001020 + epoch: 162304, + }, + }; + pub const ExecutionHeadersPruneThreshold: u32 = 10; + } + + impl ethereum_beacon_client::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold; + type WeightInfo = (); + } + + // Build genesis storage according to the mock runtime. + pub fn new_tester() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + let _ = ext.execute_with(|| Timestamp::set(RuntimeOrigin::signed(1), 30_000)); + ext + } +} diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/fixtures_mainnet.rs b/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/fixtures_mainnet.rs deleted file mode 100644 index 4b88d887091d845fa834b1c232df2a422072296f..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/fixtures_mainnet.rs +++ /dev/null @@ -1,1215 +0,0 @@ -// Generated, do not edit! -// See README.md for instructions to generate -use crate::{CheckpointUpdate, ExecutionHeaderUpdate, Update}; -use hex_literal::hex; -use primitives::{ - updates::AncestryProof, BeaconHeader, ExecutionPayloadHeader, NextSyncCommitteeUpdate, - SyncAggregate, SyncCommittee, -}; -use sp_core::U256; -use sp_std::{boxed::Box, vec}; - -pub fn make_checkpoint() -> Box { - Box::new(CheckpointUpdate { - header: BeaconHeader { - slot: 4058624, - proposer_index: 631, - parent_root: hex!("4abadda13b61df8d40fcb061d1ec15d0fb7bc5144252bb54c57b41893b642bf3").into(), - state_root: hex!("6fe9fa01a04dcdeaa64eba04a72fa6dbadc8596dc5ec1fff2ef343520fe722e1").into(), - body_root: hex!("6cd83df0ee7669e06e64496a1d2be083bc3bb9f2da17e9b1e62979d200328106").into(), - }, - current_sync_committee: SyncCommittee { - pubkeys: [ - hex!("a13bf1fc1826b61cceefcc941c5a4865cefdfa6c91e5223308fa6a0aa6e7b13a0499a63edf5d9fff48fdeae83e38dcbf").into(), - hex!("b3285148b91dab139b053442bdd14d627ba1e1250fe469f0f2df854b6e6ff4a18671ae3879ec9f7d8091f99f092162e9").into(), - hex!("b00d95908e72c6051478a422eb2231b5f797c2fa5c696ed1e6b9c9996ba1d8236f512443f18c01ce63312c38fa383fd4").into(), - hex!("a866633b4293e726accf6e97ac90c1898cac83e8531a25b50ae99f0ecb477a692e6a5f2488447ccd83ed869ab5abc406").into(), - hex!("a308ed8737b3a9346ff20dc9f112efccc193472e6fde6aa218ceae11e288bbd2c35fa45c1d8bb238696a96767cd68b46").into(), - hex!("81534e2a182da0c6831479c7e722953d267ba9c63a204ac96a178b1dc90d0a6ba8737002688ba5f102eda5669249f114").into(), - hex!("87c5670e16a84e27529677881dbedc5c1d6ebb4e4ff58c13ece43d21d5b42dc89470f41059bfa6ebcf18167f97ddacaa").into(), - hex!("b37334c41a3456b73b61d0eb0777260af9c2e400bbec0e0c0fdb45c39ce0dd19f021d9760f35da801f20486c6be30e9e").into(), - hex!("ab73a043ccdfe63437a339e6ee96ef1241264e04dd4d917f6d6bc99396006de54e1e156d38596ba3d15cb1aaa329f8f5").into(), - hex!("8983fdebbeba6e3cc3ee1c9feb24faaeee712356975e359b0ddca3f7c9c7448132d665f54a4629002252d3fcf375f7b0").into(), - hex!("a03c1e287ccc4d457f5e71e9dc769294835945561e6f236ac7de210d2e614eee8a85e21dfb46e2143c68de22ccee8660").into(), - hex!("8e6b888197010ebadd216da35b9716daa8675d93b3c33a96a19fd9ca42624f6b430b2ff115cd0f5b717341605dda24bf").into(), - hex!("8fb51e3ef3c1047ae7c527dc24dc8824b2655faff2c4c78da1fcedde48b531d19abaf517363bf30605a87336b8642073").into(), - hex!("893272a63650b08e5b8f9b3f17f8547b456192ad649c168bafd7166b4c08c5adf795c508b88fd2425f7be8334592afb2").into(), - hex!("a90c42266ca0a65976fb4dc18465b0a44a63ed3b2747cae74e46e3ccf158f98384e2e86c852e7c5556b083b3ded9d243").into(), - hex!("b67c621d9b6313a9f6744dfcdd77d4e9cb4bd413fb5e3199cdcd7f675fc39f1ba492860749bfddf98f4088756e844a98").into(), - hex!("a9760afaa51002be0948acf7aebd90ec4e60e0dba8456e445aea93408a0468b62bb6da4984b92f8f6061561c9d56f4c4").into(), - hex!("981b2d7c56ff38f1d02c5d7a7f8bfe71daaf94d48c3bc93e8083a0a23c1ae1ff05f90312deb09b35d4513c1ffa573d86").into(), - hex!("9515dedf061e654d58a43e4e525a63ad2a6274ea6f20b1d624a6ba7d3062ed68a0226eee6951ab8464906c52ba5556b0").into(), - hex!("901f724ee1891ca876e5551bd8f4ad4da422576c618465f63d65700c2dd7953496d83abe148c6a4875a46a5a36c218cf").into(), - hex!("a8d152e5d94b75cb9e249230db21af31de4d4f3d4ef60ccbf2212babf69aed2a38435a993ee2f13cca410ad55a4875ab").into(), - hex!("875ebfe737cea438e967d70ceaffb4360cce28ecc76c8c4ee612c47fb6b3e89af03c66981571066107323f49a6242772").into(), - hex!("a798a0371e8cc4dc42ccd79934b0db5a3a59f18a0ae09f2eb172596428fcb3f00312e783d6fd21cbc1610317f44e08cb").into(), - hex!("99c629c9cd603a9344b04d22d2bcc06cf45ebf62d97f968df19c73c7a50f4f6a2a2cc7fb633f509f961edfb94fbab94e").into(), - hex!("854410e6fb856da8b997ebf28ae2415ce6e1f9f6a4579fad15b5df61709c924a925397b33fe67c89ffad6143a39d756a").into(), - hex!("a3969926aa2e52f1a48ac53074b764648b4c71bd43430944679628463cd68398f700d874c14503b53756be451c8ba284").into(), - hex!("8ee8873de7cd28a54ba2c63a80b63399effed76b154e96ed26e7c0668b9f2476e298688b6a00c4b2ab9d020a897695d7").into(), - hex!("ad54241ba3de6a4426c788690d3f78d2eb678814edc49d3fb988d7fc752e43512972567bb384bcc1b18d083d15e376da").into(), - hex!("942bee9ee880ac5e2f8ba35518b60890a211974d273b2ae415d34ce842803de7d29a4d26f6ee79c09e910559bdcac6d3").into(), - hex!("96b478b1e5e49d4ea3fd97c4846ae0f781dcc9f9ff61ee022ca92c3d8dfba89c513c46e8bb38b73e6b678a79b9b12177").into(), - hex!("aad9577501d7f3a5dbac329f2c1fe711710869cc825740f365488fc55a278d687bb72423560f7cb2cbd60546a82ea1e6").into(), - hex!("8aa3d9dad1c122b9aed75e3cc94b3a9dab160fa4cad92ebab68a58c0151a5d93f0f6b40b86fba00e63d45bd29a93b982").into(), - hex!("a12fc78b8d3334a3eb7b535cd5e648bb030df645cda4e90272a1fc3b368ee43975051bbecc3275d6b1e4600cc07239b0").into(), - hex!("ab01a7b13c967620d98de736b8ff23d856daa26d5cd8576993ee02a5d694332c0464ed018ebffcd5c71bab5cada850ce").into(), - hex!("96cf5760c79cfc830d1d5bd6df6cfd67596bef24e22eed52cee04c290ad418add74e77965ea5748b7f0fb34ee4f43232").into(), - hex!("9443e6ba4400fb3370c573cd7e33f05e1475f9cf1d6adb905bee3aff8f1452d8d384c8a72c9110070f35c6aad940bba6").into(), - hex!("95c60b5561e53cfc26d620be90f84199ffd6dd9687c1be3a547048e7cba10a0be9bb6da000e7521cbd488d0901d48ee9").into(), - hex!("ab69cf79750436d310dc3c5e96c2b97003f4394f31dfa8a9ac420595dc7b4d96dad5787d93347ba2bc6f196c241a3dbf").into(), - hex!("9145ee1fb6e84114c903819db94fa5a72bcbc15fcb8a7fd8eefba23b156cc46309281dcf78b48a2847b3754f7d7d7a79").into(), - hex!("b118f77f99ac947df97e7682f0fb446175185b842380af4ee7394531e4f93002c72b41a57a7c1b923a4f24b10924c84f").into(), - hex!("a4aabd1890ebf35423565dbff3477a09eea4e35f5a26ed449eab38e0a21fb89e9ddfe3a2003cddc457db648a1b5891a3").into(), - hex!("8ff5d2e6c98b1fea70cb36ea8ed497fd1233b9418948ac58c6c379ed35fb10f8253ef188c909d5e77e81b5b8e2a4ad17").into(), - hex!("a23f076306c120dccf69d7d2ac7f83a377a72d35bf448f88feff8b6dba9307fdabf34452e30b87407b2258b9edfd1174").into(), - hex!("87d2217eb05d657aba7b048cf3c661b463e78e51135a5b937e71975ff5102e596434720f02349c73415decb88418cb0d").into(), - hex!("8bb51b380a8a52d61a94e7b382ff6ce601260fa9b8c5d616764a3df719b382ec43aec9266444a16951e102d8b1fb2f38").into(), - hex!("b0053550040ab3a3996cba5caf9ad5718867b5f5df273ed8c6520761571f03a94e50b5f8a6a8c42d725383cce97d3cae").into(), - hex!("ae50f93230983a82e732903d6ed50a506d678f35b6b4a4b3686a92b12aeb9d34cb095e8562b0900125bbced0359b37de").into(), - hex!("8bfa106ada4914419bf1d8900c5981dd5b90c3023196d7e918d62879fc3a575bd0a25f939366f7fd2240df6108b069ec").into(), - hex!("b3a5497365bd40a81202b8a94a5e28a8a039cc2e639d73de289294cbda2c0e987c1f9468daba09ea4390f8e4e806f3c8").into(), - hex!("b4f583e10aa9af79b4ebd647e0fffe1c720112727e5ffac4313f236737491fceeee194537786c561cd5777b453e5b03c").into(), - hex!("826be957cf66db958028fa95655b54b2337f78fb6ef26bd29e2e3a64b130b90521333f31d132c04779e4b23a6b6cd951").into(), - hex!("aace45334070c51cc8b3579598d4cd8cda2153bba51f56e3b1fe5e135c83ef70503c322756b9cad9d3cd28f1ecfc8227").into(), - hex!("94bb68c8180496472262455fd6ab338697810825fa4e82fc673f3ac2dacfd29ee539ac0bfe97eb39d4ef118db875bab6").into(), - hex!("b560c33950a355119845f63defb355807e56773f636fb836f7746155fad070e384fc1091b8e5c057e4cbc7da9275ecf7").into(), - hex!("820cc2ac3eed5bce7dc72df2aa3214e71690b91445d8bb1634c0488a671e3669028efbe1eae52f7132bde29b16a020b7").into(), - hex!("9244703338879e3ea00663dcde8f11095de3e38df9277d8c2acc26e72021c222ae40bcc91228789fdf0b69acc3144783").into(), - hex!("8d474636a638e7b398566a39b3f939a314f1cf88e64d81db0f556ca60951ec1dca1b93e3906a6654ed9ba06f2c31d4ea").into(), - hex!("889a5cf9315383bf64dfe88e562d772213c256b0eed15ce27c41c3767c048afe06410d7675e5d59a2302993e7dc45d83").into(), - hex!("b544c692b046aad8b6f5c2e3493bc8f638659795f06327fff1e9f4ffc8e9f7abdbf4b7f6fcdfb8fe19654d8fa7d68170").into(), - hex!("aefc682f8784b18d36202a069269be7dba8ab67ae3543838e6d473fbc5713d103abcc8da1729a288503b786baac182d3").into(), - hex!("a0f2092ac34d2363614fb2f57fc7b72db247eb1fa53f395881ce6b4aacd6fb920d6dc59507701d487288102e4c4fa389").into(), - hex!("b33de3de106be61481ccb7f07a7a63cf4d1674010e462388fb8ab5ea08f444ed7a277905207e0b3aa2f00bb9efca984f").into(), - hex!("84173aeaf3d96368dc7ca1ad5e5575da279113567e5815a364a0356a720c5e08cb58ca1fdd891924f4871d3eaae5de40").into(), - hex!("b6d6482ad7b9b412ffbefbbdcc28eb3d091b1291f54f77bdd53c4ac85f705c454940f466dc272dde7b03c26f0cd6ecb3").into(), - hex!("a2053719da2b7501dab42011ae144b3c8d72bd17493181bf3ae79a678068dc3ee2f19d29a60b5a323692c3f684f96392").into(), - hex!("8296f8caf58316af535def398a43357e48cb3b1e674b857eba1bd1b970da3dd045e22fe6d17dee4e9117f62ece3ec31c").into(), - hex!("84faf4d90edaa6cc837e5e04dc67761084ae24e410345f21923327c9cb5494ffa51b504c89bee168c11250edbdcbe194").into(), - hex!("879aea8f09dec92f354e31aa479d00cb77457d363de2d9a51ddf7d734061b6f83d6345cf33dbef22004cd23dd6c4b760").into(), - hex!("b8fca0f7bc276f03c526d42df9f88c19b8dc630ad1299689e2d52cd4717bbe5425479b13bdf6e6337c48832e4cd34bb5").into(), - hex!("b2a01dc47dd98f089f28eee67ba2f789153516b7d3b47127f430f542869ec42dd8fd4dc83cfbe625c5c40a2d2d0633ea").into(), - hex!("a19f2ce14e09ece5972fe5af1c1778b86d2ab6e825eccdb0ac368bb246cfe53433327abfe0c6fa00e0553863d0a8128e").into(), - hex!("95d1f944b0c53eb3e9fcd5632713602bbb9195b87a172a370ae2df98504612a55f3968615a39b569ce6a0fe9fb559be7").into(), - hex!("ae36ab11be96f8c8fcfd75382bb7f4727511596bc08c25814d22f2b894952489d08396b458f7884d6b3c0adb69856a6d").into(), - hex!("824d0dc002e158adef06fc38d79b01553be5a3903566029cf0beddb2248b11da40e66feb168e8e3e2a63ea033a75f382").into(), - hex!("8f9f85ae6377414fcf8297ed45a736210cd3803f54f33116b0f290b853dc61e99ea08f3c422ed9bc6bdc2f42ab4f56ba").into(), - hex!("86c53fc078846c3d9bc47682506f8285ba4551475921fd388b96291741970c34b8de4210202e40d2de4acb6e2892072b").into(), - hex!("853184f246d098139230962e511585368b44d46a115c5f06ccaeef746773951bead595fb6246c69975496bac61b42a4f").into(), - hex!("b91b4260e2884bae9778fe29a2c1e4525e4663ec004159def5d47320de304c96d2a33ad7a670e05acf90cbba3efdd4d9").into(), - hex!("83492e27e07e35c0836aee6bee95d040b8d3e82db6f94a3917d07797800f7200f5dbc6c9596c6c3c70f8f470b65a9b6e").into(), - hex!("b1bb33607d10ea8c954064ecb00c1f02b446355ef73763a122f43b9ea42cd5650b54c5c9d1cfa81d4a421d17a0a451aa").into(), - hex!("99cb1728157a1b7cdd9607cf15911bbcb56b64d52fb0d0117b457853a81ec55913f977850f26e188fa2652579efe9ddf").into(), - hex!("8b7cb5b8de09a6dfceddcbaa498bc65f86297bcf95d107880c08854ed2289441a67721340285cfe1749c62e8ef0f3c58").into(), - hex!("b97447233c8b97a8654749a840f12dab6764209c3a033154e045c76e0c8ed93b89788aac5cd1e24ed4a18c36de3fbf60").into(), - hex!("b4790910e2cbef848448f24f63e9dd4a1b122cf65feecf152d5fde282ad6fcc6ea3f9cc23178baf85612020795e4b13a").into(), - hex!("81fc724846b5781f3736795c32b217458bb29972af36cc4483dd98ab91680d3d9bc18842db2661487d3a85430dc9e326").into(), - hex!("a154892ff23b284040e623bba940a6a1ef1207b8b089fc699cb152b00bcce220464502cfa1dfb5a2f62e6f3960cdf349").into(), - hex!("af3f765fd293c253072b33a780ed68933f78d7e079d9a2079b6232755bedf6ebcbce9ba65c01f695602fa8ee17899867").into(), - hex!("97578474be98726192cb0eac3cb9195a54c7315e9c619d5c44c56b3f98671636c383416f73605d4ea7ca9fbeff8dd699").into(), - hex!("917c4fd52538d34c26ccdd816e54ebea09517712aa74cec68a2e3d759c6a69b5ccb4089ad1e0b988e916b2ce9f5c8918").into(), - hex!("8cf3c29531a17489a5f8232d56c5251ffddc95be3ff7ff61472e19fb38c5eaec841ef3b1ee36756b3dd8ff71ae199982").into(), - hex!("96d4b9b411319e531bab6af55c13f0adb1dd6b4286784ff807f283e7990dc368c16d536fc5db3d992deb4b0278914e6f").into(), - hex!("8903f7e0c9764ce844b15d84feea04406dc66b195a5f82ff4027f27361e11cf368538137d139368f5a6f42876b04f056").into(), - hex!("a4047173b5906c9b4292aaee1e91d9080ae74b1d3eb990449ed1f96bf22c3ee80f4915361e5bf7dccce24ae1618dae77").into(), - hex!("a4c4b96071e7bc92e41defba3507ddf423d93f3a94271b1f9812dfc4660e4c9fd24e0dd7aef324c46deb8d7a7c97eaa4").into(), - hex!("8289b65d6245fde8a768ce48d7c4cc7d861880ff5ff1b110db6b7e1ffbfdc5eadff0b172ba79fd426458811f2b7095eb").into(), - hex!("ab4119eef94133198adb684b81f5e90070d3ca8f578c4c6c3d07de592a9af4e9fa18314db825f4c31cea1e2c7c62ed87").into(), - hex!("a3ffc3dad920d41ec3f4c39743ef571bcabb4430465d9aa811d0f0a7daa12bee4ed256527d16a6e937bf709ebb560ebd").into(), - hex!("8553748da4e0b695967e843277d0f6efeb8ba24b44aa9fa3230f4b731caec6ed5e87d3a2fcd31d8ee206e2e4414d6cf4").into(), - hex!("b15e1b4ac64bafbc4fdfead9aeff126bf102fdd125c1c914f7979680ec1715fbeccf3dc35c77d284421ec1371ed8bc32").into(), - hex!("9377aab082c8ae33b26519d6a8c3f586c7c7fccc96ec29a6f698b67d72d9266ad07378ba90d18e8c86a2ec77ecc7f137").into(), - hex!("b71c11828ecad7731136cb1f5b80392a4add8d62f8866a781fdde797a201ebf6d483b2348aacbea2061a5108933b757d").into(), - hex!("86793899ef71740ab2ec221d0085701f7909251b1cf59a276c8d629492f9ef15fc0b471beedc446a25b777391ab00718").into(), - hex!("8100b48ac2785477a123a7967bfcea8bacef59391680a411692880098a08771ff9786bd3b8dfb034cae00d5a7665621c").into(), - hex!("8b027c14affe47f83ee59b504d83b2fd2d9303de2c03ee59d169bb199d9f4bd6533d7f8c812dd7a6f1e8155e3e185689").into(), - hex!("9615800f8c95f95bf25055ae079b964e0a64fa0176cc98da272662014f57e7cd2745929daf838df0094b9f54be18b415").into(), - hex!("951aa38464912a29df2101c60771d6de7fadb63f2db3f13527f8bdacb66e9e8a97aaac7b81b19e3d1025b54e2c8facff").into(), - hex!("a0e68d24f784fcb2b71acc2d5871285623c829d0e939146b145e04908b904468a67c07a2f156e6b17bf531adc5777c4b").into(), - hex!("86a533b02ae929f67c301649a2d58651b98cdffe731b63fa32aa1013c271634bbb088c0d02865913c11bbb1bf57c0e12").into(), - hex!("81f145ebb9a5674a5b052d0e9059acc8f8ab612dd9f54d43ff620202606e19a86a9b284dc6480d555a030e5fefee8c50").into(), - hex!("a698b04227e8593a6fed6a1f6f6d1eafe186b9e73f87e42e7997f264d97225165c3f76e929a3c562ec93ee2babe953ed").into(), - hex!("b3180ded54610b1b3a2db7db539197ced6a75e9bb381d1f4b802ca7cd450f5418522ad2bee3df1956ed63ff1ffe95dc1").into(), - hex!("86fa3d4b60e8282827115c50b1b49b29a371b52aa9c9b8f83cd5268b535859f86e1a60aade6bf4f52e234777bea30bda").into(), - hex!("97d076617cf0a64ab3d1f030cfd72a303b6b252c0a7b96157ff7fc8af5970f00d14492c46e8f6f37caafe837d0dc95c7").into(), - hex!("ac2c98a0ab3f9d041fc115d9be4a6c77bd2219bb4b851cbee0d9257a4de5791251735b5b8fad09c55d16eb0d97080eff").into(), - hex!("ace7fda25c2fb7c18710603c16a0ff0f963352d1582a42a20c9f5603c66f485df8383465c35c31e8379b4cb2ec15b4c4").into(), - hex!("a07b35ec8d6849e95cbd89645283050882209617a3bb53eae0149d78a60dbf8c1626d7af498e363025896febdba86ee7").into(), - hex!("b2fc4478830f2ae4234569346d80b59899247c609b75bd2190a896498539e1f30dca5edbad69f0224918d09f0d7eb332").into(), - hex!("84926cf2265981e5531d90d8f2da1041cb73bdb1a7e11eb8ab21dbe94fefad5bbd674f6cafbcaa597480567edf0b2029").into(), - hex!("b5f32034d0f66bcbccefe2a177a60f31132d98c0899aa1ffff5ebf807546ff3104103077b1435fa6587bfe3e67ac0266").into(), - hex!("938206740a33d82ffda3e01598216324731335d367965aa0b740486d60ba2e86a4ecd546851046a61a4b0fc88295b5cb").into(), - hex!("ad2b1ab32161e37ee553e3787f05f9281073d7ef7d0ae035daa353bc83da8ef8c76c99ad2928463c7c708f7404020476").into(), - hex!("94f4720c194e7ea4232048b0af18b8a920fde7b82869e2abcc7e14a9906530be1ef61132884bb159df019e66d83a0315").into(), - hex!("a26dd9b28564c3d95679aca03e3432ac26e287f80e870714c5946b05538b3cb43bba7b85c16bceb5430e81b7a04c1b1d").into(), - hex!("8ef0930db046c45ca5c69d565d54681d2b6d249e27092736aee582b29de3aac3fd96e1066a57cadd851b4e5334261594").into(), - hex!("92096ebf98ebac5c82345d3ef0db0f5a14af23ceea73279087426b281d6701997fe131fe65a7df7d624b4ff91d997ae8").into(), - hex!("81c850f419cf426223fc976032883d87daed6d8a505f652e363a10c7387c8946abee55cf9f71a9181b066f1cde353993").into(), - hex!("97070a33393a7c9ce99c51a7811b41d477d57086e7255f7647fd369de9d40baed63ce1ea23ad82b6412e79f364c2d9a3").into(), - hex!("a99cde5c7c85ae291c74c893e598cc0e6eb2dda2a81dbb504a638eb21dd2c41d6e5caf7baa29e3c1c32e94dca0d791f1").into(), - hex!("937ccbf8cd19b82af2755b4856cfcca3d791e33ae37e4881982ea89d3b21d205a9402d754fac63037243e699484d21f6").into(), - hex!("ad7dca7640444f1268f03b67544815d4366c6a4a2f0d25ee78f3361c63095416216fd31aa0bcce7448cdd7ba73a6344e").into(), - hex!("84991ca8ef255610ebc6aff6d66ea413a768e4d3a7764750fd02b5cd4735d41df399b36e87647fc83cf73421a39d09e9").into(), - hex!("91215fc3f7243638733fe293dab7029e0c4275550102acf5f1638773cf8f8ef2c53ffa5bdfc1b602c269a2b5ab164b7a").into(), - hex!("aa6cfb3a25f4d06c3ce1e8fd87496a74a5b951ab72557472a181a2e278c5e982d290dd4facf40bd2f4f8be62263dadb0").into(), - hex!("ac9f29ad08aaf27581fe1f12e210ad4ac6011507fe3100763a4120f9e439f3c6d191f3fb55aadf58bd865cfd4406c68e").into(), - hex!("87c6cb9ca628d4081000bc6c71425b95570291eb32ef2cf62416bd1ce3666eb2ce54accd69f79d506cefbfe6feb5a1da").into(), - hex!("93042dd42e56671155bb40d85d9d56f42caf27bd965c6a7a7948b39089dba8487d4d5fd30522dba6ba392964e3ffd590").into(), - hex!("a76adeddf2454d131c91d5e2e3a464ef5d3c40ee6a2ab95e70ef2e49e0920d24f9b09276250ed7b29851affbdbc7885a").into(), - hex!("92a488068e1b70bf01e6e417f81e1dc3bcec71d51e7eabbc53b6736e8afdb8b67d191940fe09c55783be9210e1cbd73c").into(), - hex!("8180ffffb5abe78c38f2a42a3b7f1a408a6d70d3f698d047d5f1eef3018068256110fcb9fb028c8bdccbc22c0a4c3a20").into(), - hex!("a50ab79cf3f6777a45f28d1b5cdad2c7ea718c60efeeb4c828d6307b29ef319445e6a9f98aa90f351c78b496575150c1").into(), - hex!("a4632399c1a813e41fb2055ef293466098ea7752a9d3722d019aa01620f8c5ecdc5954f176c6c0901a770cbe6990eb11").into(), - hex!("83bf5055d6332009c060fd50b8dc698d42b764b079c90a1fad8a83101f8dd1cc27acb27dc9d1c25ac8d3db4107471b4a").into(), - hex!("8c432e044af778fb5e5e5677dbd29cd52d6574a66b09b0cd6e2a5812e71c91559c3f257587bfc557b4b072a822973a60").into(), - hex!("8368a0f17c8427beb71dbf11a09a2fe8495a33f08c29c74a9a996a88aa01c0a09f9555abeb1ef1592cab99a9e05875cf").into(), - hex!("a8795e7f4c4c5d025ead0077c3aa374daaf9858f1025c0d3024d72f5d6c03355ae6ac7418bf0757fe49c220acff89f7f").into(), - hex!("a6d9f67ca319ea9de50c3fed513269b83fa067977adfd1e9d9ee07ad61b2ac1de64a39d7b6897ab55870cf982fe481dd").into(), - hex!("86b3a4ea9b1fde00cce79d5ae480353d60cb6ddce363c535bbbc3e41a4b8e39fcf2978eb430091ae1b10420d43193971").into(), - hex!("90fb5cac22a22fb8a6b619f1eacd95873be974d4d5d1f7080e523bb9b4b2644eda7340d780bd1ea8ce36407ca0410fea").into(), - hex!("b5036d4c241685bcd67156e4ab0eba42b97f639947d54b17af2c88fbcc5fc57359c7df4bc7f8df955a524fb1501a6fda").into(), - hex!("b1c56f028f31f0ff86bdf55788703b4d809becaf3e4d9d349f1b660a07d2f15e127eb72a0e2a5a2742313785a3de43a5").into(), - hex!("a3e909196f447e492200cc67000c5d7f0f585fb98e966cf9bf08257597fea8d92a90ceb054d4b5553d561330b5d0c89a").into(), - hex!("87cac423d0847ee3547f45ac5babf53bddb154814e291f368cbb62ddd4f2c6f18d77a1c39fddb482befe1a0e77d5b7fd").into(), - hex!("8605b88ce23190b1fa9d389b15e6907417239a72b97673d1479c4ccb8f4515c7921d14537775c74e738a9c3f122b1443").into(), - hex!("87587504e819bc7f0349705a05c15e8504fd6b2c25c3fd264096cdb7aaa22d8078da776215925d9d775a7f9355b6f0c0").into(), - hex!("afba279768f0f928b864645aa4e491e9c949bf3dab57efa24eeaa1a9a7d4d5a53c840019354068e64c65a2f5889b8f3c").into(), - hex!("86b1cdd26ea9a3ae04d31a0b34aa3edc9e8d038437152214d195381173e79e4ccf7f8f0ce9801086724a1c927c20e4c8").into(), - hex!("812d3ded3a3c9e58eecf13a29bb4cc13b01b2a0af322423a29bb0e4f6d9021d1d87ac4af7a2a6b88d34f44a8bc1b3c55").into(), - hex!("a988cfed9f481bc98beb5fc188ed3f6893a3ebba27c3ebace669792f6abf0997727023c3b6930a6421224f5b257b8b49").into(), - hex!("a38c974b57da968f0c4611f5d85d8014fd48594c8cd763ef2f721cfd2c738e828d41ff029e3591d7447e3125641db8ef").into(), - hex!("880b4ef2b278e1b2cccf36a3b5b7fbce94f106ed9fa2820cb9099a7a540a57e9fdeef5c0fb0a743049828fc2b8c46163").into(), - hex!("96e7d1bbd42195360267c2a324b4d9bccad3231ed8a7f070278472a90371867e2ef2c29c9979a1ec6e194893afd992df").into(), - hex!("a92beb343caf6a945990adcf84302c55d1fccdef96c34a21f2c00d3e206a9b2c6c6b412f66e5d4fafe26ef6446cde705").into(), - hex!("aa48afa77d5a81cd967b285c0035e941ca6d783493e1840d7cbc0f2829a114ace9146a8fbe31ecbd8e63e9b3c216a8c5").into(), - hex!("893a2d97ae067202c8401f626ab3938b135110105b719b94b8d54b56e9158665e96d8096effe9b15c5a40c6701b83c41").into(), - hex!("b614910b247c6ade31001b0435686c3026b425b9bff80b6c23df81c55968633349e1408a9a5a9398a7d5d6ed5d9d3835").into(), - hex!("991c660e4d476ad92aa32ef2c5b27669ab84026eeb5ca70af69bbbcd8ebc0a8fec17843423306edc78b4436629d55c25").into(), - hex!("b926a21f555c296603dc9e24e176243199a533914f48994b20abca16f19c30cfd0baf319268139fe3f83ce69afdc324d").into(), - hex!("8e70e4867d2731901d603928d72bbeb34b2e0339a4f5cf06e7a771640717421b4ea039c61dde951582a28c2ff152ff70").into(), - hex!("95aafa379cc6a2b4bdd0cad30b7f0a47839952af41f584219ec201c6c4d54610eb2c04b67b29080acb8cecc5e7543fbc").into(), - hex!("8465bd8be9bd9c2c6116d4ae44ec6618c109cb9aaee2d241e7a6ed906d398ef15a6fc18bc8b1d3398184241405954bba").into(), - hex!("b455f751232de0a48440d09983f4f4718b6169907979c9f282acf7177ab5b1f338fe1f2acd8d0bee4b4aad61d0340839").into(), - hex!("970df2314849c27daa16c6845f95b7be178c034d795b00a5b6757cc2f43c4c8d8c2e4d082bec28d58dd4de0cb5718d61").into(), - hex!("8d52413f981bc611427ad0534d25e914113d0ebcd6960aab6421608bec6648b89ae4b2ca2153c57d3cf4f1f37212aa5c").into(), - hex!("b7ac87da14b783914ab2e914fb7b536893b7a650cdc5baa1f3b4aca9da77b93a3336671335250e6467a8cd4aa8dc61e9").into(), - hex!("b37a2ec9dec3d7d9cbc911fa1e5310a47d23a841d02c8b99a923991c73fc0185d130a494748c64f2b5a4c07bcd06920e").into(), - hex!("86108b661fb2c363adcca84c114c83346413df748b959015c018452cfac14890bf585dc0a646d68727cc3cdfd2b61897").into(), - hex!("8421044f794a1bcb497de6d8705f57faaba7f70632f99982e1c66b7e7403a4fb10d9ef5fb2877b66da72fd556fd6ffb0").into(), - hex!("84d2eb008578aebd6f01254b7e46584c1524e6fd7a5a2ae5fa0ea560865ca50d52290cf2d12dd20b042f402e62181b4d").into(), - hex!("8d6bed5f6b3f47b1428f00c306df550784cd24212ebac7e6384a0b1226ab50129c0341d0a10d990bd59b229869e7665a").into(), - hex!("80e30cabe1b6b4c3454bc8632b9ba068a0bcfd20ce5b6d44c8b1e2e39cbe84792fd96c51cf45cf9855c847dc92ce9437").into(), - hex!("b7e74ab2b379ceb9e660087ee2160dafe1e36926dfab1d321a001a9c5adde6c60cd48c6da146d8adfa2bd33162eeaf1a").into(), - hex!("a2b1ea43f51460b3cb83657b4e296944658945d3ad6ae7b392e60f40829ba1da6a812d89f0380474578cbd0ab09801ac").into(), - hex!("91ead7dacf43905eb5d4b179af29f945479ed074126bad3b5a2bbc1663af5f664fe53a36684e9389ab5819e53f1344fc").into(), - hex!("927c030d5a69f0908c08f95715f7a8d1e33bed5e95fc4cfb17f7743cb0262755b1e6b56d409adcfb7351b2706c964d3b").into(), - hex!("883f38af3b2c1d50f6e7c515a5e02468d76890f6e669f7acd2df89365862fa65877095deb001b4e2868bc5b59439dbb1").into(), - hex!("a0ebae60a998907a19baa396ae5a82bfe6aa22cf71bfca4e1b4df7d297bd9367bbeb2463bda37aa852ad8fd51803e482").into(), - hex!("8ae80eeaed3fc456f8a25c2176bd09f52a2546d45d77a70f48a9e30aa29e35ff561c510ae1f64e476e4a0f330b9fdbdd").into(), - hex!("a7be457b8bc1bfde4865a35b7b1826118edba213b0f0d3cf5d877267cc1559cabe61cefb1e300142a978c29676036179").into(), - hex!("af51da717d2a45ab96fad5d9317ea867ec4c6a411af6fabd72e568230099a04c036a0f114158815b1a75da6474dc892a").into(), - hex!("b549cef11bf7c8bcf4bb11e5cdf5a289fc4bf145826e96a446fb4c729a2c839a4d8d38629cc599eda7efa05f3cf3425b").into(), - hex!("8d264fbfeeebb6c4df37ff02224e75e245e508f53fb3446192cd786ecf10d0f704c4fc2e53e7f7318ae1407e46fc0fb8").into(), - hex!("ac3195143035cdb4ddcd5f93c150035d327addee5503ea2087b1a10b2f73b02453ddd1a94d8e7d883e365f9f0e3c38c9").into(), - hex!("acbb398ea9d782388c834cf7b3d95b9ff80ee2a8d072acae8f9979595910849e657889b994531c949d2601b3ce7b235d").into(), - hex!("811e6a5478f708495addbb1445a2ef23e39ee90287f3a23ecd3d57d4b844e4f85b828bae8fa0f1893dfcc456f86f7889").into(), - hex!("8cde690247d4831dfe312145ae879f4e53cb26641b3a3bb9eb4d590c56c11ece3cfe77180bd809468df5cddaea4f5ab1").into(), - hex!("b42578df29a9eb23bed91db6a1698df49654d2bc1b0d7973b2a7e300e9cf32e0e6ac464d463d4d26e394e7598239c4bf").into(), - hex!("97ffcbf88b668cde86b2839c7f14d19cb7f634a4cf05d977e65f3cd0e8051b2670e521ae74edc572d88201cff225e38a").into(), - hex!("a322b5d2a6e3cb98b8aaa4c068e097188affef5dec2f08c3e9ce29e73687340d4e5a743a8be5f10e138f9cabbe0c7211").into(), - hex!("a076ea1084b7a1a33115ef62d6524f36e7820579868763a6ed1f8bce468f150cbfbf0ed04be2487aaa34100d828b0db6").into(), - hex!("944259a56e3b4f745996289912740281bde47e22705f142c2a483ffd701e780f51a01b177d2494dc8db9e69157f45d44").into(), - hex!("91cb79d52951d1b901e4a686bf4ad587e31db57ea5af6ffeb93eeafae3929879c386ddec860f803c2dc61055437e6bee").into(), - hex!("91efdbcaad9931312d7c41d24de977f94d7f3f7b88090a1f72d9a097a1e30cc805c5ea16180f463022d9b26b8863f958").into(), - hex!("b26f5ed09f7d5bb640ec94ddd1df0b76466f69a943b4699f53d45296d5d6b8010bb61477539bc377d1a673d89074d22f").into(), - hex!("80822499f96a1a8c0048f01f389dfcaaa5d8269c332dbb507fe46f270bcfd5f67c53f827fd867221592dbde77b6b37ab").into(), - hex!("8860ba25d5530cb8585975d8013a1c2d5b0f0f96066044fdc43ed13488ae44e379c624ff6993a18cb6e037809d7985e7").into(), - hex!("999d1c44e14184349064415ae28a149b3b11aba5baab6792744378d14df554a3625fac82038eaca920064822294dd513").into(), - hex!("a62c2e7c692403e874a16e08e46a067e19dd561993ca07ff79cecb53c753763b3e49d372638c96c0a8c921bfa0798a0c").into(), - hex!("a1c84730a5c41dcab9a5ef9e1508a48213dbc69b00c8f814baf3f5e676355fc0b432d58a23ad542b55b527a3909b3af6").into(), - hex!("a1c0c317e6e352e16e25c140820b927161ce5d2c4c2e10bca3057ba4d46b4f42ad7aba20de86dad9fc6368ea92695268").into(), - hex!("85c216e314eb7bd8ba02e092c90e132bc4bafb21c6a0fbe058b0dd4272cb76f183b83c6783fc321786065ff78c95f952").into(), - hex!("8e8f63ec8f4f1f7fcc61f893b671710c3c17f9d2d26c5c6ca40e671bd4b252bc0cc1655e6780d2ddcf2915d8f623b9a4").into(), - hex!("aaeb0005d77e120ef764f1764967833cba61f2b30b0e9fed1d3f0c90b5ad6588646b8153bdf1d66707ac2e59fd4a2671").into(), - hex!("92ff79402d5005d463006e0a6991eaacc3136c4823487d912cc7eec1fe9f61caf24cd10022afdab5f6b4f85bfb3eee4f").into(), - hex!("a32a5bd9b7bec31dd138c44d8365186b9323afbba359550414a01e1cdb529426bfa0b6f7daaf3536e9402821faa80003").into(), - hex!("845982c2672fdd44b33d2e56ad676e704c02f756b09e8765bea42b924c14724484567b55f0db42ac20cb70a7f5201c14").into(), - hex!("89cd9f6ae7d9a9ff2b4db916ba3af9fe700fcfbd16577bf73a965af938e8cf633020466b0298d3c31300360aa6851af2").into(), - hex!("8fa2d7b22af8e6b82679ebdfa13efdcb34289a554653ea6c1b16efb9f957f7fe64df787e7b03d8cdc8a732b91c916bd1").into(), - hex!("94f327bc57ed1ce88ce4504b4810cc8af5bd21a7e07b280a7866ce08e39b6cf7a6560bf73a5f10671271624cd7893970").into(), - hex!("90f4476224b64c2a5333198a4300ece8b3a59ae315469b23fd98dadcdceaaf38642d2076e9cd0bfacc515306f807819f").into(), - hex!("ae0db78548261216ad7d6a7ed4e6089ee17b3fa311494b2f2c559e215cd3de7e5f3a781a49dcff428a8a61c2a4f49a19").into(), - hex!("a83371f44e007c708dc4bcafa7bd3581f9080a4583c9be88624265014fd92f060127e628de5af3c442a25f049c7e7766").into(), - hex!("b471c72bd2971353f4b44248b8e6cf5316812861a88ccfc20fd0d89a5e010428c387228b2f6f14c12f79e31afc9d0753").into(), - hex!("8962afddcb1a26cc8ccd3c993109e79a4dd747ca473b8b5ef93d9c2e71d29623b834ac945074acf118248e3ae7878a6c").into(), - hex!("aa0940e4e5586e79a3d97397c8aff3d112c6f759d2efac29366acc5b5c6a7cfef8d50516bf309da8b787de265dc8deda").into(), - hex!("a211120e1bb3b10138df1fa58efb009a298b8771f884b82bb3de15822b1252124a68f3980f96122a775fb96f05ddc3d5").into(), - hex!("a1047401598b1e6e2613d746bb4689e0406eccdbadf319a6609a3261cd09deec215d90eba6d0ddc50dd3787d60104e7f").into(), - hex!("96791b2b8066b155de0b57a2e4b814bc9b6b7c5a1db3d2475a2183b09f9dcd9c6f273e2b0c922a23d1cf049a6ce602a3").into(), - hex!("91013e0d537fb085a49bf1aa3b727239b3e2c1d74c0f52050ff066982d23d5ee6104e70b533047b685e8b1529a0f14dc").into(), - hex!("aa65c11071be23c9bddaa5203f3166e5cf043efe5fb8f4b26f8a9cabe71db701a450e79eb001c401da5752755d9cf1af").into(), - hex!("8645cc44d180c18a6d8f57ba57bae05879451997533cfe558cad4d3d586caec877e348915e32a09ee73483283c4df744").into(), - hex!("8eafbb7002f5bc4cea23e7b1ba1ec10558de447c7b3e209b77f4df7b042804a07bb27c85d76aea591fa5693542c070de").into(), - hex!("919c81bd1f3d9918e121e4793690f9ddd96c925ae928536322d4b98132f21979c1f34731d393f0ae6e0871af4355a8ad").into(), - hex!("98181e9291622f3f3f72937c3828cee9a1661ca522250dfbbe1c39cda23b23be5b6e970faf400c6c7f15c9ca1d563868").into(), - hex!("8f44c43b80a3c5f488118859fab054745cfe5b0824821944b82fcf870fda6d93489ea9ca4220c24db2f4ad09c6080cb7").into(), - hex!("a683d4865ddcc099f7b698153007b92f853b80f49b3be75163ea8cd1f8ff584b43a68e68de3ae61cda8ad4b41f355c87").into(), - hex!("942772b7c7c47d4e5957ccf1d6f1450070930af3e2b7eaab0dd7699372445df0cc910e6c0efcf501887dd1adabdaee23").into(), - hex!("805c06e565ee67cab0cbccb92b6656fdb240b430766eade3c6b0a0b1b93c840e2b4f028601451dca135c783239463880").into(), - hex!("84d3e2a06e16ced26094b356a16a4fb6aad50ad9ab23ef804a5852a33ef0bff76f3c5fbf7beb062376c2e669cb598679").into(), - hex!("803df08aa745cc3c0a799f3a91bb6ed423cd520c9d255d36c21bed1a0c3b12e8cad32f54da09dadca97683e9548fba91").into(), - hex!("aa2c3ef95b8d4265f01666129646004b6950d3e8ce74b4ca12aa3b90fbb445079a569178df772c272463a44d48922b8f").into(), - hex!("b0a4c136fb93594913ffcebba98ee1cdf7bc60ad175af0bc2fb1afe7314524bbb85f620dd101e9af765588b7b4bf51d0").into(), - hex!("93e4c18896f3ebbbf3cdb5ca6b346e1a76bee6897f927f081d477993eefbc54bbdfaddc871a90d5e96bc445e1cfce24e").into(), - hex!("89019e9550648962420984e9fd03597a854ae824567d9aa6cd5db01a4616b4e1477230f2d1362a2d307e2425a3eeb898").into(), - hex!("861b710d5ec8ce873e921655a2ca877429e34d432643f65d50e8b2669929be40a9ce11c6353b0ada1fe115e45396b2b7").into(), - hex!("88554c83648ea97dac83d806cd81d92531980346b208d281fba489da15a0084fd4d9a00591d1ca67aad3c5793685d55f").into(), - hex!("851fcadebee06930186f35293feefd40d7daedec9b94e6fe5967536c2c0e4cc68f58d3f5fbc76f1e77b90c9580074f98").into(), - hex!("b96a11048c7c327709d52e72e6f6ed0b7653329a374ea341ad909311b5b303e5629d6dcf11dcdb195e8c7592ceefac21").into(), - hex!("836075979eaf386ff6cb459cfd48fed171ae812b0ac3b38dc24dd8ca905cac1c600be717d4a0defa0a854f40cfaf8c33").into(), - hex!("90fc170529bcc0b80c46a53fffd8323fd2cc5cfa9b75ea4d36db21bd1f198335ad2bfa87f8990cf9cd9fd7989ecca718").into(), - hex!("b7eb6a49bf8f942dd8c37c41c1b35df43e4536e07ca9f4c1cfbbf8a8c03f84c54c1a0d8e901c49de526900aeac0f922f").into(), - hex!("af6911edd6c7ad30f905a0a3f78634808832fdeb4206b006934822d673bcced8e378779261b3c4b772b34b8871987f57").into(), - hex!("a74d240d0d7ea0afe68813fab55388d77e75eca0519d21771dcb7170cedb11dc14b237b26c5ae1f7f728b52e5ec0f02d").into(), - hex!("a7b86e4f1366da44fd59a3ee68018a99c23ba3588789463bd88b0177a9b94030b58cb879a506e64421af966f261eaa86").into(), - hex!("ad8d94e46cc02a1c0ad27105e8f672ec15b8296051801f1918d0bd470625686e8e8a0abde8f6852b846ee8d9132b26bc").into(), - hex!("980508c4d1e655cc6200f89a884b3a25c0c05708a3e4a101205c4fd901c3e20a943071a6300bb2614be41a139d4ef1df").into(), - hex!("b0173651b4ba0590b1d2f0265183f3729b5bb09893523ca12c4936120cbe5ef0d9b98733734407d99fdc766792ff10ac").into(), - hex!("abf72ec0280d56971e599b3be7915f5f224c0ccde2c440237e67b95489f0c9154ace04b7763db228473715f68053f071").into(), - hex!("b404beebf60026ca6843f2953cfcdee494d495c8e2d18865147102ef29a8f0ee470961d2246fe5a450c622d20ca51d53").into(), - hex!("89461cb2dadf51d6f1208b0965c8eabec895d7b19b7d90d3c6e49dbe75a75c30fd26db3dfb169dd46a4342280225032a").into(), - hex!("a61cb5b148cb7ff34775dead8efa7d54d7141182356bf614070dfaa710ebf07a4dfb684dad151db60c0f8261c30a4f40").into(), - hex!("83a798f47a4f62dcb8b531d463b0fd4a876d47a8ca990710290549255033c909de709471b4e823a60bf94d8baf8b5acf").into(), - hex!("a23431589f3a25070a188deead9adb0ed423d6b00af267f3f125cdd4391c1527909b5cfa88130dc4b67915f5002128fa").into(), - hex!("8d77e65ba6250fe18c54ce70d0ba4571a7d3e68a8b169055cd208e4434b35a4297e154775c73e7dfba511faadb2598c5").into(), - hex!("90e5db75f3787b819df471712f87b6f3281437090f5db7a2c21b07164446292a414c687e41de2d1ca00786b093239c64").into(), - hex!("b382fa28670a5e14dc954b2db8ace250c73df71ab095304bd8ee28f455ab26cc54f82775a831428e110d1a3a2af709bb").into(), - hex!("a58d2fb1c2612d28c54fafa7f2e1e6c336c24435abdb53e1be9dce9aebecbf7468a348b872549535ac18aa003f83ea87").into(), - hex!("9545f94c4e9056e360dd999985f8ad06210556fa6f07cff77136a2460605afb0ff1fb1d1a2abe4a4e319fd6c29fff80f").into(), - hex!("93121aa60f904a48e624e00f5410cf8c8925d2b0719f90c20e00cba584626f833de7c8a18dbfa6a07df24b916156bfc0").into(), - hex!("aa3446aac25f6c23ea16e8f7d19c58d187746ef3c2ac7d8fdf9bdc329409a07589ec8eebafbe2b156e7ba60addc15af8").into(), - hex!("b964f50011f03135e993739e2e63a71933ba4583040b3af96c7e2dce874226518f7b68f622c4a1d78b9c3ec671d33ad7").into(), - hex!("8cb5cb7cba886af58acadc5a4348524b1395a39dc51196316d759a9b72d9fc0fe45b706e264393a13ff911f0d15de45c").into(), - hex!("b50c306f78143b37986e68efa10dbe1fb047d58562e9b5c5439b341dd8f1896c7ae586afac0a3213759784a905c1caaa").into(), - hex!("97825edba8410e8bcb85c5943628c02ea95ee7595f559c030b94395c0d1d0d84c38eca199fce9c1992e572b5029b124c").into(), - hex!("b0922acd6da2a95b36de6d0755316594a7e2e32ea774792dc314e8c3cd76d9f1d69df38231e166e24bd42c664f4fbac7").into(), - hex!("ae5ea228c1b91ef23c245928186fbafa1275ff1817535018d7d2d913abff0fd76bf41fd04a96d816f2f1891bd16e9264").into(), - hex!("b106c6d13ca17a4c8ea599306e84918127cf2de21027ac3fe5a57d35cf6f3b1d7671c70b866f6e02168ae4e7adb56860").into(), - hex!("a044cd5a3b727dc1cb59875e4025718375d12e706fffcdb48874e51a675dc2cabb209670192e408cdced5aeac65192e4").into(), - hex!("ab1cc44983e46a6ea2430aa6616ab28614f43624665e3e6ae31a9357c0c5434f34e56c720906e184327693cc4ebe1fa2").into(), - hex!("890def696fc04bbb9e9ed87a2a4965b896a9ae127bc0e1cc515549b88ddbcbc02647e983561cab691f7d25cf7c7eb254").into(), - hex!("a86be58fef115445b909dffac6f51da3fe9214afd9c31fd564bb8f39b1dc3cb895b1222f2c63226b54b60b278ec45edb").into(), - hex!("8757e9a6a2dac742ab66011c53fa76edb5ebc3c2fbd9a7265529a3e5608b5c24b4482fed095725e9b8fed5a8319c17a4").into(), - hex!("a7e0ddbae16e4491822684c0da3affecbbd17ef96c5c491ac093c6eb4e162fc7854c367535e296fd3d6265c2ed1210bb").into(), - hex!("941fe0dabcdb3225a625af70a132bc1e24ccab1f8331dde87db3e26cbee710b12b85535e46b55de7f5d1c67a52ddd5c8").into(), - hex!("a24d05b51c7c128bb49979cbd9019e6618545d95275a44b5c3d1d03e71bf2ebffdf43fff50c30846ec27d279043cef4e").into(), - hex!("b526f40d519e7a8f2c81b69f71b3e2ef079028004c0448ba0608296c2787972491ec6d05ed6a8fbd5ef2da76325a93cb").into(), - hex!("87ca4fa85a257adf7e21af302437e0fa094e09efced2d7ebab6cf848e6a77ae7bfc7cf76079117f6ed6eded9d79ce9cb").into(), - hex!("9276e8051bed8f5dbfc6b35765aac577dd9351d9d6ac1bb14496bd98091005b9a4737b213e347336413743f681f5043b").into(), - hex!("a7741c52498e0a24db3ce7699882de8f462a2b3ed5e9f77dc7200cbdf46b6cdd923b1128759909d6dddd64700c4c20c5").into(), - hex!("ab5b363ed9551e32042e43495a456e394cbc6d53b15d37a8859850162608bdf36d3d4564b88fdbaf36ff391bb4090b8c").into(), - hex!("838ff6630dc3908a04c51fb44a29eca5a0d88330f48c1d0dd68b8890411a394fd728f14215482b03477d33f39645dceb").into(), - hex!("997d3b82e4753f1fc3fc2595cfe25b22ac1956d89c0950767c6b9de20623d310b1d84aaa72ab967ef1ea6d397e13524b").into(), - hex!("85416cf3eef63d5530062d6f031aeddad101c7f1aea3bccb826c73f8a25d5d963caefd789a6b9832bd4ed459f268ae64").into(), - hex!("9194bc45e11d7276ed1c9ef3ad5a33d6a27372f5568563ca8ee213e2e7029dee404ab5acbaecaef698129798d35fd895").into(), - hex!("a4828a003513ab887082390262a932a7e8c5e25431824b7b4cc10fccba73265c0e5ee5b315ccef13906d971644913806").into(), - hex!("b907ec84b6ae5729d36e2acd585a350acacdeef148bcc5dc4a91edb57505526462bd4371574865541d8bb0d786a29b2f").into(), - hex!("aa5d1c1f0a7f6b9b3c3734f85864aa60bddad5121450218d76d82edefd2602685a820965c56d7eefe789d5115cb41e01").into(), - hex!("ad28fe70a8606f87bcb5d6f44e1fca499c24bcee791971f599ffef1f403dc7aec2ab6ebed73c1f8750a9b0ff8f69a1e6").into(), - hex!("a641eaa149c366de228a2833907ad60eea423dd3edf47e76042fdf6f5dc47a5b5fc1f1b92c8b96c70e6d8a68d3b8896c").into(), - hex!("abac08f4df786b2d524f758bca43b403b724d12601dc0a8362b7a2779d55b060c6682a5618fffea2e4def169fcbd2bfb").into(), - hex!("8658a15df961c25648fd444bdf48a8f7bb382d9212c0c65d56bf9cdb61aab3bd86604c687fb682260dbc0ad2dc84bf01").into(), - hex!("ac7983d50ec447b65e62ed38054d8e8242c31b40030f630098ce0a4e93536da9179c3f3ae0b34a0b02aad427a97ee60d").into(), - hex!("b8877a00a24b0ffcb2bd3fce8a8ba327d8ee2e98d85531cb61fec21fd49cd1696491cd51024a9c3820cf06a77cacf04b").into(), - hex!("ae075b66e5f211c2149c45b211d1297bbc1d9e6497cb3315363c492a9a51ae5b9d0a28bfecd755d68553736901ac6606").into(), - hex!("afdc091a224486e7bfac169e6a7b4e008d2d04144508a337fd93b6f4d385ee3f0d927b1f5c1cd79a15e0fd6078e45dd4").into(), - hex!("b75c28941ee3f91b3535b4eaa0fb17b59ca65b5256601a1f6d0cf2bb4d66837fd16e51d6942856679012a5730a66e519").into(), - hex!("84a6edac5ac68a7ca837c46d5ada8fab136748b6c3a3b9165dbbc231ec386b15328e4ef7d69a15d4cf354135348a4ee4").into(), - hex!("b01ee30d120b97e7b60ea89b9b6c537cdf20b6e36337e70d289ed5949355dd32679dc0a747525d6f2076f5be051d3a89").into(), - hex!("a80deb10bba4bc7e729145e4caf009a39f5c69388a2a86eaba3de275b441d5217d615554a610466a33cfe0bbe09ef355").into(), - hex!("8d6e3df29419bd0da1deba52c1feebe37744108685b49ca703e1b76fb4d612e3959d3b60b822506e5c0aac50b2f5eee2").into(), - hex!("a373408beb5e4e0d3ebd5ca3843fe39bb56b77a5d3d2121d4a7a87f9add3ec7376388e9d4b8da0ba69164850cb4b077d").into(), - hex!("ae0beb452af7479134a7fbc31a5f59d248e8a67d4c7f73a0e30a51db9cd33a1da3f0ae947fa7e5983aea1343e7daf06a").into(), - hex!("aa103a329b699d4102f948101ce5fae27226419f75d866d235da8956f11367e71db5c0a179dd63007ed53f7eec333aaa").into(), - hex!("a094cca9d120d92c0e92ce740bc774a89667c6f796b438b0d98df0b7aef0935d8c915d5b0dad4b53e383dc9f095c29fa").into(), - hex!("a4d88467136b99d6e55603b3665b6da0f7fb27c7759687f7e6977b6230272773d7b95049d999538c008f310c05ed948a").into(), - hex!("a23710308d8e25a0bb1db53c8598e526235c5e91e4605e402f6a25c126687d9de146b75c39a31c69ab76bab514320e05").into(), - hex!("a36d6952c2d7f88bf28032a76ed46c4dabbf1901a46efc50deb798d1b44adf7e0210fbdf2473a1ba408b5c98d76943e5").into(), - hex!("ab45f5b756ec6e0b98d0d4301c87675a0a1f0b1178b8a9780c1ab23e482cd821834835afa1de890962212159e464b10a").into(), - hex!("b800be1788175a01a9228b0d3e7eb4302484a2654eb2a86c0f0900b593da0a436ef031ac230e2b05e968b33e90a342ce").into(), - hex!("8cc8d279ec08d0a5a2a09ad07fabb0122eb65f48da2571d83f86efa2c1c5bc51b04ae94b145f0a8ef19a3988638b9380").into(), - hex!("b4cd409256819e8e4627edbba90ec40b7da17a57f95749104d90db0364f5007b1accc816f4d51a0dbe5ffbcb737cb37e").into(), - hex!("99365fe5ab8ea8bd768ae7181a6ba49b79d240f512ce309b02f09d465fea276298ff55b5b9cb5b4162a901b390606024").into(), - hex!("ad77fcac9753efba7a9d9ef8ff4ec9889aa4b9e43ba185e5df6bf6574a5cf9b9ad3f0f3ef2bcbea660c7eef869ce76c8").into(), - hex!("ad2456725ac3aeb0e4ca5c0502a8abb4dbd8a8897d9d91e673fea6a0cffd64d907b714b662d73c0877b98d4ab3ce6a89").into(), - hex!("ac8436e33619e2907659741d66082acbda32612d245fcc8ae31e55f99703fac1a15657342fa66751d3be44fc35d71c36").into(), - hex!("b0eecd04c8d09fd364f9ca724036995c16ba6830d6c13a480b30eb2118c66c019cfdc9dacce6bfd8215abe025733e43d").into(), - hex!("9439b663e4104d64433be7d49d0beaae263f20cfac0b5af402a59412056094bd71f0450bc52a294fc759ca8a3fddfee9").into(), - hex!("b72de0187809aaea904652d81dcabd38295e7988e3b98d5279c1b6d097b05e35ca381d4e32083d2cf24ca73cc8289d2b").into(), - hex!("93706f8d7daca7c3b339538fb7087ddbf09c733662b55c35f2a71073f4a17c91741955d4d549c2ee6c22eaa84193c1ad").into(), - hex!("926dc729e135f1f0bff4662ee3d6823a64597fe189b763ada34f246e77705fd4e062d85506a338e9fa98c4d225a3b27a").into(), - hex!("93f03495d53c781be8b76e37e68b64aa260523004eff6455ddc8a8552af39854e5181f8c5365812b1f65926534fba5dd").into(), - hex!("9834f66e5c946c3a8241ca2bbde046a7e88072124911d5d15c037a95b61e82b88b5c2058fa4a3721537dee39dee5da18").into(), - hex!("92ec1aeb2aa24c51cd5f724972c8b6095e77b237d83f93ed34ca0bc91a1dbf1ad95adccc59e0f0abbfef33f331f3298c").into(), - hex!("94402d05dbe02a7505da715c5b26438880d086e3130dce7d6c59a9cca1943fe88c44771619303ec71736774b3cc5b1f6").into(), - hex!("8368bb9b9bb2e17730c42ed1100eb870c88a8431601312aa8cb1e738cdb9ca2704dfd432cf1703c0db043259819631dc").into(), - hex!("a35189a105401f0cfba4b43be21723486c04659e5a01e67c43e8f9911030810b878beee696f04f63d314ccfe97ebb790").into(), - hex!("93ccd8c5f82374e0bef6562e16576f742d79b6f400e3485ef36e148088b61fbd882c3d2bb38ab0b43fa1dac77f31d543").into(), - hex!("b85d9a426a23ca9ee582bc16c203a9352dcc5f85440e46979de80eb572384479b697dc964cafd9457d9f34eeb77bb72a").into(), - hex!("aefb70e89dbf4456e077690509afcdcabf975416ff2fa16777fdf90b3abd3f5dcd865c43f1ebe6f8a669edc7f3bd6ad8").into(), - hex!("8eb03001ac9e22c6956a682ed458e650785c36d23ddbcd51ac4d9cc991325c02519ff1958987a08eb29ff56ff6e2c293").into(), - hex!("ab4a1ffef7e001723c71f5d28f3dd030a06c42d91773733d117247bbf9c01cd66fca2cff8c6ce04c4bfb68dfcdd851f2").into(), - hex!("a9ef845ab489f61dbfdcd71abcc29fc38f3494a00243b9c20b9cd0dd9e8a0f23304df84939b9652cdf5542d9b3ee085e").into(), - hex!("b5726aee939d8aee0d50bf15565f99e6d0c4df7388073b4534f581f572ad55893c5566eab1a7e22db8feeb8a90175b7d").into(), - hex!("9953a7cbc152f101a60e3e381f2af17ebe7401e16ef6462d132b8f0f6c6a18837914a1299d1605f9f289b9561112f4bb").into(), - hex!("a0c9b944a338325f5efb675c9c12619fb43c8e25e80d38d6140e31d5070573b1a0ed9bb52576e4f22f37d0292d36a648").into(), - hex!("8bfd6a173a56b73480cc950ef266a18933ecafc86915a7453ded09efd8a0cf4466101f1373f05d48eae3e7fc5c0f7f54").into(), - hex!("983fc1ddf17f9756c9cecc00b39bb2ad432587a5c6d1c3296a383b9f539c9afe84c6c818447a709c0b686ba26ce5ea3e").into(), - hex!("94bbc6b2742d21eff4fae77c720313015dd4bbcc5add8146bf1c4b89e32f6f5df46ca770e1f385fdd29dc5c7b9653361").into(), - hex!("b26b4d483bca73d3f3a976bb595a0e40f9a42094e0febbad3a1874934be1939a1b362ee4ea14a4f5cbfa9b1392796a12").into(), - hex!("8dbe8fcbcc414eb352245c52549973f73d987012de9d5f2b2f55dfdc43cf8cc9ea6b147abf149817f80f9e15aea566c6").into(), - hex!("9604da21e23c994a0a875ad5e0d279c79210f7a7de5c9699fac4aebbd76d39b703eeec5dd5efc9ad6b9dc58936089ddc").into(), - hex!("8934e9a3feababa12ed142daa30e91bd6d28b432d182ac625501fe1dc82f973c67f0fe82d39c9b1da3613bb8bfe2f77b").into(), - hex!("85f2ed3ffb03e50c8f22553b8e6349be6244d893aa37a7c6dbd221e9e121579e5a04466e60d6b4d3567bc747b1fc1e9f").into(), - hex!("ab0ad421f6fd056687b4fa5e99dff97bd08840b7c4e00435eb9da80e0d7d071a447a22f8e5c1c5e93a9c729e5b875a1e").into(), - hex!("841d77b358c4567396925040dffe17b3b82c6f199285ac621b2a95aa401ddb2bc6f07ebd5fa500af01f64d3bb44de2df").into(), - hex!("83f21dfe0272a5a8682c3c7814c5e0e4db6a9098f1fa80fda725f77ea81fdfd2fa36b0c8db013503a89bd035f86306fa").into(), - hex!("95c98e3b6b62f84edf7f297cae93ee5f82593478877f92fb5bf43fd4422c3c78e37d48c1ee7ca474f807ab3e848d4496").into(), - hex!("81c3a8c00cfe4e82f3d8cb48de7d4926d5ec2f7689f9cb85c1886a23758bc107a4bc6e978601c3519156a169d0bf6779").into(), - hex!("8e956ca6050684b113a6c09d575996a9c99cc0bf61c6fb5c9eaae57b453838821cc604cf8adb70111de2c5076ae9d456").into(), - hex!("83474776ef2341051b781a8feaf971915b4a1034fa30a9232c4bf4b1bd0b57bc069c72c79510acef92e75da6f6b8843d").into(), - hex!("b41780d9d67e9e8b81b1f62d25c0c72ecfda659d2bfe6825edb70ecd0e0724250ac364e7be521cdc112ba638f16360d4").into(), - hex!("842ba3c847c99532bf3a9339380e84839326d39d404f9c2994821eaf265185c1ac87d3dc04a7f851df4961e540330323").into(), - hex!("af17532b35bcb373ce1deebce1c84abe34f88a412082b97795b0c73570cb6b88ea4ba52e7f5eb5ca181277cdba7a2d6d").into(), - hex!("b9574edb9567f07f85c7c2e6ca6c02d90ad7c7b87d49796f1e2fb7240ad071fb755cf13ca8678668a56217c62df168eb").into(), - hex!("82212706111fb1cf5def02b5b0eb7ae9e6ea42b4c7a2b9fcacb7aec928326edb9ac940850dd933f2822f6cf519de0d50").into(), - hex!("9579973ee2559da09b327c62b1cc0177f2859872885dca709e24dcfbb9bdf9224a6d26869aafce498f44c0e6bd8a996c").into(), - hex!("a102c2ade15ea2f2b0cbc7dbd8c1171de0c8092fc4ecef84b5fd2bae7424aea8be1629f851c75e4d1d0e96104e54bfbc").into(), - hex!("a5d7e847ce7793386e17fe525f82aabb790d5417c3c6e3f6312f8e5ff52efa8b345c1ff60c4c9bf7636f5ff17b7a0061").into(), - hex!("b4b80d7fbdb1dbf1567dfb30d8e814e63de670839a8f6ff434fe171416599fef831b8e978d6498851b8a81e0bc8dfb85").into(), - hex!("b6e6277b86cd5284299ced867d37ab98090ac44a94deef6898aeadd177e64605440c15b9609c07e71fe54c95b61873b0").into(), - hex!("8135a0633082e4465090d6930b770340e82366bc5c37be6ef6dd105f85acf63361e17de8b5fcab4c82e9f9b4029954b7").into(), - hex!("864d5d9858cd881eecb0dde5e3e0c6c5de623cd9ef619e87b82fd25c5edf45a1a025b1dc763c27c5f4d520fd564b464a").into(), - hex!("8784a8fa62e0ce23283386175007bb781a8ec91b06fd94f22a20cd869929de37259847a94a0f22078ab14bb74709fac6").into(), - hex!("8adb748d5fa5c22ce4c76a1debf394b00d58add9f4e08524cf9c503f95981b36b8d0cb2dfaef0d59d07768e555733ecc").into(), - hex!("b76cb8cb446eb3cb4f682a5cd884f6c93086a8bf626c5b5c557a06499de9c13315618d48a0c5693512a3dc143a799c07").into(), - hex!("b2af1f7ece1fd640c205a09614122d69d5d2e81a7618bedefd6dbb91c7f432679be4ced1e6dddd3de323bd44991931c5").into(), - hex!("b950b457c34bfdfdd9d6da9628d41749ccae03659518a04b56487bf1b4c0681b719ec5230c0b0fd5dd710894df6aa2f8").into(), - hex!("b21785008910a949804d1291e7533752641d31beae3cb518806488f81d58c38a5efe5ed9534ac692e68c3121e2f9d97d").into(), - hex!("a9b120a77d70c1cbc0178a12d97a78b2dd0b98d0584e8e780b937800ceb18c90eaa1f0a83c5b50e34cae1c20468f004f").into(), - hex!("998c9ee20d33f96a2388b1df642aa602bc8900ba335e8810baab17060c1eace4bc5203672c257b9ae750008b707b0aa1").into(), - hex!("a7d1676816e81a752267d309014de1772b571b109c2901dc7c9810f45417faa18c81965c114be489ed178e54ac3687a1").into(), - hex!("b409f87f0632aae9bc081345b17a50a767ba4198f9ac9d352246fb3bebd29ed53c9d6f148c2f318c2eb12846b0aac4cb").into(), - hex!("b40a3bae2b08c13db00f993db49e2042be99cde3d6f4f03d9991e42297933d6049394c659e31f316fcb081b60461dabf").into(), - hex!("8c1de4264e04ff7e8282faf81c0bfb5943656451be52170211cb7adf4ff21bccbb789400735579c622f69982fcb8e9c6").into(), - hex!("8085c60b6b12ac8a5be8a7e24977663125c34827842aa3b2730854ab199dd0d2eaa93084c9599f0939be8db6758b198b").into(), - hex!("972cfaefda96f5edfe0614c01533b76153118712c1c02c505008204a5be2aa438675d97f43384199517b1c08c7c9fdb2").into(), - hex!("a1e47798a782a024da340d6d6a1b1e5e15a0f2d8668adf349ca375086964414a563cc1eea3226ae637f87e78c0a630b3").into(), - hex!("8171f20c020faae112bb92ca213c1df5b1050151496c70db5c5319212bada83b120d515bd7d8b24736090c574e1b7203").into(), - hex!("afe779a9ca4edc032fed08ee0dd069be277d7663e898dceaba6001399b0b77bbce653c9dc90f27137b4278d754c1551a").into(), - hex!("a5a07bf219432e9c80c38596c93560b49c7de287f31e30b7a06fcb4d15982add4a24085adbc5b753c462be989c64c96d").into(), - hex!("9210be290176d7e8a5005d27e7ed825067b1c678b174bc8180f92b5c03b6c3d1822356edba84f460caf6bf5275cd7efb").into(), - hex!("a95bec86a7c8417a8df3a0158199327ba0924d3b7dd94cd7c1ef8489b10270ae64b8537ed39cd3699a48942bfc80c35d").into(), - hex!("ad9725114b01152fff134c1a8ccb8d171b8cd11685ef6815b76f442d757d130bab9ef4c9845e66f4aa0237ee2b525c20").into(), - hex!("8018499ef720e28759133033833edfe17ed23e42f99058bb79fe844ddee823cfdc43916be2dc9724d18f9726e6f1b409").into(), - hex!("809c7a08fbef7caf4c137cd639f2e47a8ca60d13bca3990eac51ac2a9e4442cd1a1473bebb63c61d595b586525d7b027").into(), - hex!("9793a74fa578ace75b083578277a1ae8766d41a5c508b0f1135fb97dff1d0826002393a7276b18cbc4b3c5671360ce0b").into(), - hex!("a6d7e65bf9f889532090ae4f9067bb63f15b21f05f22c2540ff1bb5b0b5d98f205e150b1b1690e9aa13d0dee37222143").into(), - hex!("99c935fe18699bca9852200c292690a2b834bac508890c4ee9af1aa6999a8d590bf6a3a274bb55d5a73f1b7095d10f37").into(), - hex!("860f5649c5299211728a36722a142bf1aa7cbbfbd225b671d427c67546375de96832c06709c73b7a51439b091249d34f").into(), - hex!("9104ac7ad13b441c6b2234a319e1c54e7f172c9a3efcb8c5fab0ac1d388b01895a9a208f59910bc00fb998b0adab1bc3").into(), - hex!("a3b109249ac2900806f0f39338da72d4f2cc6d1ac403b59834b46da5705cf436af8499fa83717f954edb32312397c8d9").into(), - hex!("b9893f7a47af457a9efd90ddc0c0ef383ab34e9c1284e617c126965cd9f0de5c54ee8b7b5208ff190366fe445e9c1325").into(), - hex!("b77416ea9a6b819e63ae427057d5741788bd6301b02d180083c7aa662200f5ebed14a486efae63c3de81572fe0d92a9c").into(), - hex!("8b20a852fc8f0b7cdbbd808c04a0cfd2fbccbdc0cb2361434f0d96341c8bde6155695977768d563b95746dcb4339fe2c").into(), - hex!("96b15806d9009962fa07f8c32e92e3bc30be4ded0645ab9f486962a1b317e313830992179826d746ea26d4d906bdb7b6").into(), - hex!("b0d69b3861ca6791632ec8a87114b463e0da571bc076c22a8f0d9e88a1a5eaef24683f3efa8f34900d0112412e3dc4fa").into(), - hex!("b01a30d439def99e676c097e5f4b2aa249aa4d184eaace81819a698cb37d33f5a24089339916ee0acb539f0e62936d83").into(), - hex!("b19ca6e55f349bbb2dc3e429520ff5b2e817972470794f35c1aac8c118b37a694cfcc875b6d72225343799825d2f5c39").into(), - hex!("b7ea5e0d3cfcf0570204b0371d69df1ab8f1fdc4e58688ecd2b884399644f7d318d660c23bd4d6d60d44a43aa9cf656d").into(), - hex!("a778da56ddfe4a383816b43b027464d7a28689fc4a6b35b36883d3f36d9c41f0177bdbfc8f258afe8da90f02d3b64fea").into(), - hex!("910fd030feb5538f538e5ba74b9bd017d889ed6d2a797be9c26d2be8caeba7a473006102de27e87755742ba34e445bca").into(), - hex!("b49593ea6040ce82cfb5aa2881a4b0c42b78aa9fc8467d79c8e4a8ae4ee7355842841c8e1cc0558362047ed80de44fd3").into(), - hex!("b07447c7e87459315fcbda3fb86fef27f98373b1246e2ce367e26afd87f6d698a438501fdc13cc5de9eef8d545aab768").into(), - hex!("8a9f7e8d45f11c4bfb0921c6008f3c79ff923452bcfa7769beb3222f1f37dcb861be979e6eae187f06cf26af05e8ee5b").into(), - hex!("8ebfbcaccddd2489c4a29a374a2babc26987c3312607eadb2c4b0a53a17de97107c54eab34def09144b3098c082c286b").into(), - hex!("93be3d4363659fb6fbf3e4c91ac25524f486450a3937bc210c2043773131f81018dbc042f40be623192fbdd174369be2").into(), - hex!("8cf8412bd48b21b008f0207b1f430ed96bc6512c3712dffbbecb66e493e33698c051b27a2998c5bddd89d6c373d02d06").into(), - hex!("a5562fbaa952d4dcfe234023f969fa691307a8dfa46de1b2dcff73d3791d56b1c52d3b949365911fdff6dde44c08e855").into(), - hex!("a8c167b93023b60e2050e704fcaca8951df180b2ae17bfb6af464533395ece7ed9d9ec200fd08b27b6f04dafa3a7a0bd").into(), - hex!("93e4d7740847caeeaca68e0b8f9a81b9475435108861506e3d3ccd3d716e05ced294ac30743eb9f45496acd6438b255d").into(), - hex!("8016d3229030424cfeff6c5b813970ea193f8d012cfa767270ca9057d58eddc556e96c14544bf4c038dbed5f24aa8da0").into(), - hex!("ab7eff4ef8696db334bce564bc273af0412bb4de547056326dff2037e1eca7abde039a51953948dd61d3d15925cd92f6").into(), - hex!("a3f9dcc48290883d233100b69404b0b05cf34df5f6e6f6833a17cc7b23a2612b85c39df03c1e6e3cd380f259402c6120").into(), - hex!("8b476b3b065a3b95a3d11ec60a749c2258ddcc5885bfb50b8a086d3fd1e49ff73e1dde733b8981c3d2d206aa0c87b09b").into(), - hex!("af3e694ad71684f7214f86bed85149db039971e1c362119b979a135255aa226128802e58e2caaeaf8d89304371dd0440").into(), - hex!("aa19a75f21a14ad5f170e336a0bd07e0c98b9f5d71f91e784d1dc28a5f5eb6870a4eb35bb41edcf9e6efe982ae5c2c5b").into(), - hex!("b9528983419ab5766596683faebb3592982a76b68593f810186b4e5f94f6de60830739ad8dcc164c601d575b84bd2700").into(), - hex!("88e7a12a90428bb45bcf4b01442c11607433211fc2f9bee9545304eb66e0b4b5339360160bc782e185391385da7c5ad7").into(), - hex!("a4c4df0e29db19ab4c82dd6ca8570b337d15b59c7d84577a7a444a8f762ff16ff5ab3e4203a1d6b60a23ff949a93ea81").into(), - hex!("8f11ee58ef82b1bbd2240d3f548d8681e22bed5ce118d605bed4523b4bb39899ac78e15337daab92666750dfcaf32aff").into(), - hex!("8d3cba4d10f94bd3406a341c903ad144cfcfe6b61678d5c03084a56b4413bc30bd20d7a9fd5d839dbb565cc9b2aa99fe").into(), - hex!("ab8a8769c754008a7976b6799e81d7bfe97413d0a79b90715703c1f8f567675463ec93aabee59277121fc4df88b5c7a9").into(), - hex!("b201b0546f19c5db88df9c684cf55ed623bdb43927d06051bd595497df741feb1485961f64e8d3d1811d9e2e9e1e54ad").into(), - hex!("a04016e9e13ad845763cfe44af4e29fecf920b4aa42f581715fc34fb9ca27776feee45c82093c7274839eef1838b10c4").into(), - hex!("8f84cba7ceb7652023fc8ebde4b00ecde1f550935bab12feb630d6f49517b4148f3cde184bf55d4f6ec99a849fc6f862").into(), - hex!("a2248409026f35c3da8bc4d5c02315066df8fca44ff5a358cc42b5c88bdf6866dc133617c697bff004b1ef20ec4b5748").into(), - hex!("a52c5a63b55a8001b6b67c5db4fd5e95923052f03618369312896ed9892d99354aebc0dee8c3b365bafa29e211a5c3f9").into(), - hex!("8c9fefe233d0d657349b7efcdc368f5aaead27071d224af780874751e7d241f6b88f7650fbb4133043b24bbebc12aa48").into(), - hex!("86ceb649a337a5a79c17b496993ca07fa93b38a582367ca04f3dfec5cef8f268d4e8080e5a76b150f5be1b177ef6984e").into(), - hex!("87dcb537e38cefa32e629ae669da42e809b5afcabdeeef244b72ce057fc18584a1e8c3f073d5d33775232707f0cc59ca").into(), - hex!("a020404547407be6d42856780a1b9cf46b5bc48122902880909bdcf45b204c083f3b03447c6e90d97fd241975566e9bf").into(), - hex!("a1beb9f673409ec678020ea4dcbe65177aa18e2932ceb9cfb33fccb94b9a8ccb664f71647d58b3c8b2bdbbffbc02d5f7").into(), - hex!("ae47b31c5b62b38ee886ee04945649054369018dd6543c91f0138464af489a32c1fea339e0e0cbe82e3e8b9f2ef3918c").into(), - hex!("b18c41c0f827f6d8656d3fb93c90b663eb2eac034923972f8842cb30e96c32842b3fbc1127930e1ba4322d5b6641f04d").into(), - hex!("a6d6ef51a361df2e8f1d993980e4df93dbbb32248a8608e3e2b724093936f013edabb2e3374842b7cce9630e57c7e4dd").into(), - hex!("a49da42c27d019a21cc6489ada7b712b98c4ede28ba25dbcfa916acef48446a2baf73e03a48be763378a09774d4a03fc").into(), - hex!("8b62902fb2855300580e94830a4bc825d997ede33bf356fe3b7c08d6a8bd85a37879433fc6bee58f9b44ca280f4e8dfd").into(), - hex!("af9d13103868c854821ba518907b067cfba025d739125f1e9cce0a04fffc3a2a1f25506c1209a0cfe1d6c1572c229ff0").into(), - hex!("8910f41db6952c25dfbf6b6b5ba252a2d999c51537d35a0d86b7688bb54dcb6f11eb755a5dce366113dfb2f6b56802b7").into(), - hex!("b551d1ce88cbf4ffbdcb0113a6e319513bd676d0078dd4e6a6f23ad336c1d0fb47a4e427bdedbe0fc8f152353971f81d").into(), - hex!("b8876bda1e709ab16e1347a1107852a7898a334a84af978de39920790b4d82eb0739cbfc34da1c7154dd6e9f7674759c").into(), - hex!("a7d9ae9621dd1f3da3cd2d435e891cc3579c4c0d60d6a4565cac86c315cea21a9ad883559fe7b897ae6e05f1aa989ad9").into(), - hex!("aac995a41c14d379853ef18ffc854ad62ad77061ca9bdf5029cab3d6c2630de114e777a7fc3322455939d5205ed59c55").into(), - hex!("999cec6a31d9b2f280017ddd59138014829fa34cab58e6c35a5014ec364b84712441e7a2f717cf2f0de8d5451e250924").into(), - hex!("b1f43b498cba1797f9793dc794a437500c3c44a8a4b59f9125a4d358afa304fc05b88ac31ed40b6eb68f0396b60cb7cd").into(), - hex!("93ba2e000bdb7269818d390bc4232992d280e69abebe2db2ecb6fcb1390d323238c9793574509bc1fa34051ac1928f07").into(), - hex!("a64808a2d15c30460651c200a09b50fc83e9d84d87abc156d06cee73b76fbd74e6d64424cb5bb83d3f16b21bdb7ae9d2").into(), - hex!("a75bcd04fcb44ce5cbab7eef6649155ec0bef46202e4eb86c88b4ced65e111f764ee7fb37e9f68e38067040fedf715ee").into(), - hex!("b95fc0ec39596deee2c4363f57bb4786f5bb8dfb345c1e5b14e2927be482615971d0d81f9a88b3389fac7079b3cb2f46").into(), - hex!("aef456af90354ff88039d2dde02b0f5a6790aa762b23e0a9da8c6ec92c3b8b3320687bb21666608b4a22615843afd1ef").into(), - hex!("b38be9ada17ced704a34a7498c4fd6ba2503f6bd886b693d4712267847efa887a26e7da5d60f8bc5014b92bca8b3a12d").into(), - hex!("991a7c93f06d50ec6a4340c6751b73eb5825bad02a954e44e1e2d424af928819ebbb590c6129ce35b3f1e908e2152f33").into(), - hex!("84888f2efd897a2aca04e34505774f6f4d62a02a5ae93f71405f2d3b326366b4038854458fd6553d12da6d4891788e59").into(), - hex!("941e2e3ba414a371a11c3fe92cabf688ff363da6230ec7c83ac7303f652a19ebc89cc494427c456d0c2ae84c72053f73").into(), - hex!("925f3bb79c89a759cbf1fabdaa4d332dfe1b2d146c9b782fe4a9f85fee522834e05c4c0df8915f8f7b5389604ba66c19").into(), - hex!("941c8962debd2756f92a6a0451a2bf7fbc01f32ed03d0823dffd4a61186628a4c3c7c482b18589ff65e4c449fa35c2a4").into(), - hex!("88ce41025aa153a94f91f22e7b96f9342b5e0e1d76274fc70c4df7d08f66d9f7ac86e55a1c6e77693b8b01b2b38bf900").into(), - hex!("8f142bde50abe4dac8e059003db41610436d5ca60d2dfe2660ecaa5f9628aeb8b5d443e1b57662076f77363c89a1479d").into(), - hex!("b2affe048c187d311a185503d8958cacbe03796edf79bc32e8533941004d9178bd2e376e627e1ba61ed43850c0c455cf").into(), - hex!("8f7bbaaac458bada6d852fe665c87c646133bab16c0d5136c3dc922095b9d647d93a9de7671cb7bfd4cbd138ae0709d1").into(), - hex!("b76f598fd5c28d742bc1a81af84f35f1284d62239989f1025e9eba9bece2d746a52f246f9bb6bcfde888b9f7b67fc4f6").into(), - hex!("a4c665a3e4e25a7af51e433c978573841bfa2c75c075e17dd1f43b2f0369249f3d3a46ff51051e8ce7da528b0fa98d16").into(), - hex!("81e0992e7c1c54c21cac32e36b90b25e1e5b72aac99c953c3c4d019eced64d7e316cbc0840204a4a51a4ad17d8b1d508").into(), - hex!("96d7a69eaf2761bf0e5ebcd607b134d5dedba8e262ca1d6d3e8fbf23e6419a8ce1bbe4cd23b9e4b5f80db54a802a9795").into(), - hex!("b2235bdf60dde5d0d78c72cb69e6e09153b0154efdbab97e1bc91f18d3cec4f660a80311fe6a1acd419a448ab65b18f1").into(), - hex!("838d5eee51f5d65c9ed1632d042bb7f88161f3789e6bb461318c5400eaf6728e7ba0f92c18e1a994aa4743145c96164b").into(), - hex!("acfbac397ae2ff23b31bb27b90788fd0fd51a50f8e8c9f4b31be8499194252014f0b1972b204aeb9c2836a20beb3c868").into(), - hex!("ad85789bb62b60e9768bd330a31a16f711b6018445af6a47646f318f12df8d4d256ad00d1ed7c3afa4e98fef73c6c610").into(), - hex!("b2349265be33d90aaf51362d015ce47c5ffe33e9e6e018c8c6e39336d9327ccdd13d25e792eb33b43ed89a162f6ac2fd").into(), - hex!("aa458aaca6ecb43b6e45ea72d02682e5a7dc8dc22782669a0628e1638e73999319f011803f4ec8cf072467bf2c49c629").into(), - hex!("b9e6c9f2562e90bd3008669a42151538b70faf028cc5bbc09fd6ab3febc626df911fcc65744a2ad793ecaf3f91a1f701").into(), - hex!("a37185bd96faa526dfd3ddaff89b1eb29ceb4597bfc7e346bff9d6b3225b9ca87cbce0db94f05243c7232ead5f6607e8").into(), - hex!("824fde65f1ff4f1f83207d0045137070e0facc8e70070422369a3b72bbf486a9387375c5ef33f4cb6c658a04c3f2bd7e").into(), - hex!("b0ed68167a67490bd7d7d49e83341606d6e6fdd99b82e46747c2190d270719f81c5f5f8733646c246260f438a695aa3a").into(), - hex!("a87c2f13f2a824b7e2c39cfb63ca7b94ae6a11ade0c6b8e83f5092b933fa8b6157a5d2f09c23081f49d35cc85f5db36c").into(), - hex!("88f5e795cb36ab22bdcff01caca0e9d04db463c3d88cf656c3a0e0f5ac864b7092c738758b4c8f3b65e31995c6aaf267").into(), - hex!("ac66f3a7041586ac1576e33598f01921e16d99afbf4249c3350f0ee1654de98bd37a61c243eb6a18a942db529e36af0d").into(), - hex!("aca69a4095567331a665e2841210655636a3273d7b7590e021925fe50757617898e1883532f9cfd46428c2e3d854f9f7").into(), - hex!("946d585d7aa452d37a8c89d404757c3cce2adf2410e18613483c19199abd88f7a12e206f87a43f6009e42f4e31ed20c0").into(), - hex!("9722c1079db7e2e1c49756288a02302b43b8fd92d5671585ac1ea7491123742a2744a526c12c9a0b4c4a80f26342a3a6").into(), - hex!("b80e8516598c59dddcf13fdb7a42d8f5a52c84e01bd6a39880f4acaefe8e4b8f09cc1b1a2423cd5121f4952201f20078").into(), - hex!("85bca2f86423a09014b562f7dc613246bedffdcb3aa41fee02270c13e6b00c8d6704dcbfbafc5997df6a90c7fc08c29f").into(), - hex!("a36dad4f7cba9f4cc843fe40f6240e1973a4c412cae29b4a68712598523cfaecb05272fc47d30772bf06906b5a26e282").into(), - hex!("a3d8610c2522d330df02511710e52b1d9bdc9f2b156deca12b1bf754266caeac4f449ed965d9863558df43ce9ae65a44").into(), - hex!("b3e313e79d905a3cc9cc8a86bd4dba7286fb641c2f93706adb3b932443e32eff2cbed695beeb26d93101c53d5f49d7db").into(), - hex!("88d8a32231ff2bfc39f1f9d39ccf638727b4ead866660b1b8bfbdf59c5ab4d76efddd76930eff49ea0af048b2e396b6c").into(), - hex!("a90d9502a9785e55c199630456fcb1e794bbeb0f5f8c022e66f238a0789998b126cf9911fd0b7d463b7706dc6f9ec128").into(), - hex!("8c627caf25eae6764501b9eff35aa90bd4f24952cad712aae20344579e83ecd104ad1f7915edc4f9023b17fddbdb4cd7").into(), - hex!("87d4b20bbe2dcd4f65f4e1087f58532d5140b39a5288e1a63fc0b7e97a6a56946eafdd90ba09300c3d1fef6356ac6b7c").into(), - hex!("a18f4464cf5cebade8ee280fa00e0917cbf1743aeb0dacc748ab68773b909e30dc60f40fdef3041b5f082e650985f7a6").into(), - hex!("a6ae4fd03fbb4e2150795f75a241ab3a95c622b4615f553bab342a1803b86b1c1a2fc93bd92ee12786bf2de22d455786").into(), - hex!("a89bc7548ea245ce9556eeee3fba98a3256f87499f54a7c5eec0c43b9fb4ef2fe8f6810867ed0df814a88ee100c245af").into(), - hex!("97f1a7370b4f5acf83b466f519da361c366915f560385dd7eff9d53700ad81b25c9862bc71d35428e82372a5ae555ea0").into(), - hex!("825359cfe68ad6a75578a94be6419179e0aa088170b6c20fc5c249dc3be7a260d687c93d8d8a343c7c72c2ed6a716de3").into(), - hex!("aa9b9cc039d147677aedd1e47ad9013fcc0da0164070ff7305b18e5786c7fac0471368637a3adbb78f3af174a5c1592a").into(), - hex!("a61687511b627bde7b3977e9a34cb7fddc2aaa509a7b99b6b6c7b97133845c721e1e69f99758698d82cca265d8703610").into(), - hex!("8853c582e86cf916750d670a621246a63c7fd78f68c556642053bcdfa7937de58885d728209736b7d5521b591387e9a7").into(), - hex!("82b8c013f24fe64b8e0337ae8b6a682cae336b8404eafc1404744f80f765efdb8b2873d1d3f31141e8dfe4d93346ac56").into(), - hex!("866ec39b9eda580d96bc2bff76af5cd4887b6788675149ab33bfefe38db82ad01b8d64c6b60704210918f3564cde1110").into(), - hex!("81337ebe90d6942d8b61922ea880c4d28ebc745ddc10a1acc85b745a15c6c8754af1a73b1b3483b6a5024b783510b35c").into(), - hex!("807c510df25c0ba10d4aa06a462e02f050c69a977c64c071401ab74f9ac1e60788aa504743b4cc1982da835ff9ac2541").into(), - hex!("96b1c82b85cdb8a7026fd3431bea9cd008f0261ee7f4179f4e69a399872837ab836a14e2dd45f5448d54800a4ae7c7f2").into(), - hex!("a48b1031ca2f5a5acb4dbdd0e9a2b4e9add5ccfc0b17d94818273c8df11e825193fade364e0aec10f1ff91d57d03a52f").into(), - ], - aggregate_pubkey: hex!("a825959280c88c6716f6df807204feb84d745dfbee3cb3474ce81a1ef0c4cd142e5ca962a0335e68b7b4266769d631b8").into(), - }, - current_sync_committee_branch: vec![ - hex!("bcfe80e1d24fbdad7bc058b011403a4c26cb56967654494cde51517f888023f4").into(), - hex!("4710ae46156553fee6231622052f7fb2f6945cdb59a5501cef824b1925c87445").into(), - hex!("6df62e05ef3e1ac92c659c3a519b8fa651d1b649c2b46fda58c48cdeffe8337c").into(), - hex!("d26ff22ef5958a707a8d98eb1ffaaf1f9f412e816c04b79f5434cb1792ccf608").into(), - hex!("90d5e61f0af673ab4d8b3ab0b978d05142a8295a163b198e6dea9d8c8f1c6d89").into(), - ], - validators_root: hex!("d8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078").into(), - block_roots_root: hex!("5078f286fa90b88a09dcccd4ac72f6c3615b77c0ab3132508cb8c0c07b20282d").into(), - block_roots_branch: vec![ - hex!("558b658b1230e225ac3adce3daf0b066ed0484b4a768d55b74ba81579ca0e5d0").into(), - hex!("2cbb27d38065ff3f230247af918e67d1998b67bc7e2ce6c244bab7146e16b8ad").into(), - hex!("c63bfc96585f424385e3b4b39ce46e957017b716c54d105eb7e07c841d1d4309").into(), - hex!("e662b38f57427b58c46b09980db3856f17e56b60b36bd5471823b0f2cc1b6467").into(), - hex!("8b3bd99618b1e50cf284b4c3b03d0cc272312bce377d585eded77154aa5580a5").into(), - ], - }) -} - -pub fn make_sync_committee_update() -> Box { - Box::new(Update { - attested_header: BeaconHeader { - slot: 4055104, - proposer_index: 1862, - parent_root: hex!("5f44df1f70338aca4b3046f9e5b144bda8b27276320ca000077389d0aba35317").into(), - state_root: hex!("9daecfedc819b45231bf93819a73efd4304ca79528032fae2e852ebbe514cfdf").into(), - body_root: hex!("a33f5cdc6732684e6f248e5a7636c7e7bf705f0bf709098a46ba15c660b4fc65").into(), - }, - sync_aggregate: SyncAggregate{ - sync_committee_bits: hex!("ffffffffff7bfdfffff7fffff1effffef7f9ffbdfdffffffdffbfffbbf7ffffffffffefdf7ffbeffdffef7bfffffffffffbffffffffffbffffffffffffffdfda"), - sync_committee_signature: hex!("880f147850ac0a94130b81f94203e2f69d512c929df8b954b6adc20cb1e8598aafadf366c074b590c0905d584dd980af18e0c8a837bbcadf6b4438507aa46ef95879c0acd9c2d273ccb13d93e4938e9c601f0dd466f53369ae94e1287955cae6").into(), - }, - signature_slot: 4055105, - next_sync_committee_update: Some(NextSyncCommitteeUpdate { - next_sync_committee: SyncCommittee { - pubkeys: [ - hex!("aedc2d47fa2662be6ab58ddd3682bd5e53f508162968fce8326c75f92fb3c1a25c4d4d0e6904f9b6cb1ccbaaa9dc28d8").into(), - hex!("8097b13908662d245820f3b045d8c2c665fe9a054e9c661323924ec86dfa713b36b0c787ad4dfdeb979318810e687a48").into(), - hex!("98aebd4bf15916512508a5fe89d814d5d76423c562cd3f0a0af504c8cde53be30f4df00e3ba0229cbf8528e198a0df11").into(), - hex!("86bba46d0031989d0f1baccea4174fc3fa0d595e60d35a464e86c33c233e2d6def84fced7a00f59afe397cf4fb5b67c5").into(), - hex!("845982c2672fdd44b33d2e56ad676e704c02f756b09e8765bea42b924c14724484567b55f0db42ac20cb70a7f5201c14").into(), - hex!("926dc729e135f1f0bff4662ee3d6823a64597fe189b763ada34f246e77705fd4e062d85506a338e9fa98c4d225a3b27a").into(), - hex!("b930ecc2a26183240f8da107e80979b59da4e05f090316d982815ed6151d7750490b85273187ec4e07eb221813a4f279").into(), - hex!("81564bee5a3bd09476f658cf7719326c353485e2f4fea58d110071c5dddd3cabc349a8d1ecea45d589ed4479952a2ba2").into(), - hex!("a66d5b1cf24a38a598a45d16818d04e1c1331f8535591e7b9d3d13e390bfb466a0180098b4656131e087b72bf10be172").into(), - hex!("b9b9b6113301bd2b409b71afa1ab9e31d22f84f8cb03badaa408e1d37032350094124e533de766622d7975a047fade6c").into(), - hex!("8eb3b3e3135720036c1120c4e8b8d405b00d002f2bdbe601a06f2c2fffb940a9966d18636ee34fc003dfef547d8f3b76").into(), - hex!("898deb30ede570d391266c81132a78239083aa9e27a9068e26a3bc14ff6468c3f2423484efb2f808b4996c16bfee0932").into(), - hex!("a90d9502a9785e55c199630456fcb1e794bbeb0f5f8c022e66f238a0789998b126cf9911fd0b7d463b7706dc6f9ec128").into(), - hex!("ae8af784224b434b4dfa9ae94481da4c425602097936623e8abb875f25deb907aa7530bce357786a26ed64ef53d5e6b3").into(), - hex!("b544c692b046aad8b6f5c2e3493bc8f638659795f06327fff1e9f4ffc8e9f7abdbf4b7f6fcdfb8fe19654d8fa7d68170").into(), - hex!("ac5c01c51dac6ee1cb365c9b03f09906d9b7b9b4d1b73c44d9e8e06823025d7070f242898a975420bc87d6372382cab8").into(), - hex!("a4632399c1a813e41fb2055ef293466098ea7752a9d3722d019aa01620f8c5ecdc5954f176c6c0901a770cbe6990eb11").into(), - hex!("85f7ae1a7a7c793c408750ddec2d7f58b985fc3cdf9fcf6b2192bc57092b8a271b2fb6ced0639baaffe0bec3203e568b").into(), - hex!("aaeb466f4316874c2107a0de38dafafa65ce50039c20723e8797815238011426f4e77e29fc573e7c6d2df85c1bbfefdd").into(), - hex!("a8fd63da16dd6a4aa1532568058d7f12831698134049c156a2d20264df6539318f65ec1e1a733e0f03a9845076bb8df8").into(), - hex!("93600f65c090814cee5cbd5f22f98e79c69e63510501a0be6a74b111e4c52441133fc1c198c7bf235f9005aeacf1d441").into(), - hex!("a69f0a66173645ebda4f0be19235c620c1a1024c66f90e76715068804b0d86a23dc68b60bca5a3e685cce2501d76de97").into(), - hex!("ac6e7e9960207138d5b4b6a7f061756c01cc4a830e5988423d344f23544ed0eaa790aed63a22df375768f670cc9b9bd4").into(), - hex!("93042dd42e56671155bb40d85d9d56f42caf27bd965c6a7a7948b39089dba8487d4d5fd30522dba6ba392964e3ffd590").into(), - hex!("85bca2f86423a09014b562f7dc613246bedffdcb3aa41fee02270c13e6b00c8d6704dcbfbafc5997df6a90c7fc08c29f").into(), - hex!("9550072f64a48cb86bfa224d492be9a91e5a6c9def04b6a290b7a19d96981937d5a2c35de6515d1881bcb167f6e3d661").into(), - hex!("8d797819318cdf7b26405d1a327d80d4c289e56f830b28d4e303bcb019aeb0b3d69bfed58adcde8a2445dd5281b86af1").into(), - hex!("90c04eb3f3679cd630434418cb3a225a73254887692429960bd45b1613f85b2c14723cd8c7f1e875588ed82b7f5576b7").into(), - hex!("9953a7cbc152f101a60e3e381f2af17ebe7401e16ef6462d132b8f0f6c6a18837914a1299d1605f9f289b9561112f4bb").into(), - hex!("a9300a33927335f482dd0e44d0d57704ebeb278f732ae8301073cb7d5e457f02a0cb03268de71d284b8c23fb96947469").into(), - hex!("88554c83648ea97dac83d806cd81d92531980346b208d281fba489da15a0084fd4d9a00591d1ca67aad3c5793685d55f").into(), - hex!("9131874b09aa95ba186bcaa9e040fabc811b9c7b905b7dc79e902cf2bb5816d7ee39b0b55be609f22bc8c538760b2037").into(), - hex!("8368bb9b9bb2e17730c42ed1100eb870c88a8431601312aa8cb1e738cdb9ca2704dfd432cf1703c0db043259819631dc").into(), - hex!("b49379bbb9f954d2ef5574199607bc6b3aa2cc3b48dcc3745cc77406bba2a394929844fec1b87c4ce65cd0ca0f83062d").into(), - hex!("876afcd045c8a18967923733a3a43757652289b0974cd348238a693f30bb57f38664ecb97877a5e5f7d0185039a2bf54").into(), - hex!("8db19f6dd3789df179ab606508ca7e3da7967ad4340f630bda83df54c197d6bd3a14980c87fe10cbced7678c0c300ef1").into(), - hex!("997a91da55801acb6134d067ad65a9a44ead0b53d3871bb97b46ec36149d25e712d7230d38605479796190abd3d134b7").into(), - hex!("ab1abf9cf630d6cbcac0c503df44603142ac81acd647784ae0e8fc97800ef04378bc9d7f2087f959ad4bbbeec65b8dfe").into(), - hex!("95718b06017ba9d45894867fd67148645d25d9db2229aa89971f444641ba9db4c5c6f0785f3b25cf2cd7fadaa6adc5eb").into(), - hex!("8ec38c68afdfb6ba019204039c2fb49a35467058f561f626fa87314d705fd615a7b9966576052be1b3690028d3c5c7bc").into(), - hex!("a5d72ac4cdcd847d67cb5a68c6141cde99a91303ca84165bbdc6fd7f643422faec783de60739e1b2753088280c90a68b").into(), - hex!("a778da56ddfe4a383816b43b027464d7a28689fc4a6b35b36883d3f36d9c41f0177bdbfc8f258afe8da90f02d3b64fea").into(), - hex!("a2ab566033062b6481eb7e4bbc64ed022407f56aa8dddc1aade76aa54a30ce3256052ce99218b66e6265f70837137a10").into(), - hex!("b9c347c1c350fb7ef6ee9ca6780cf0604f30e3a74e9d0234bca6b3e7faed26148f2b736d9fbff6b04f5b947fda458e8c").into(), - hex!("815f53751f6d3e7d76c489f3c98d2b49214938cac8c2b417e2d17bb13446c285fa76fd32a97e9c4564a68f4faa069ad2").into(), - hex!("b3f1319ae34ad1d59207288f01d3d7b7e1bad7733fb4a819a09b011d72a4d736bd3c7afeb74cf56da0e00cf712042ad2").into(), - hex!("abbfb501071148e98b6aa56308197356fd993c93e27fd58987eca82036c1ae0ea89f9fb1a06c82851234643904c58453").into(), - hex!("a0899189bba608887c6cb729580e570ecce9ca7107865ebd30def867afaaa250bac407c30dbee11b7ef6cd423269a8fd").into(), - hex!("88e7a12a90428bb45bcf4b01442c11607433211fc2f9bee9545304eb66e0b4b5339360160bc782e185391385da7c5ad7").into(), - hex!("82714b00a822c30b317ffc1d4ba163990cc1ffe5769f91906a7f71ad1f62b39865a5314433a4ab2ba762b1d62b01003e").into(), - hex!("a3a930dd70aeeaff0f2e3790927d5425db40467ee106261615de5fcb937bb1621be213ccd8b3a14d96c5908bedc2e421").into(), - hex!("b2902161b565dd5b8e8c54187b26f01741a39ea8bc1120598661bd367cf8fc73e21eda2f0f6f9ba2270c80a59ff5985e").into(), - hex!("b77cdf45f39bf85ab3e8c8afa602f159de8352188aba5378957d468315a2d2326daef83d8ac6b227f1e7a514488afbc6").into(), - hex!("a9e573274f5a131d6c7641bc0576a2621b6466a5bf2cecb21058160a854b1b9e0be176da2b6b9b3ed562fc36c5f09119").into(), - hex!("876561bba29e656b7122f1cb51a02dff1ac7d470217d8a4799c01e61816c4660eea91843a5a42502ddf842d2daeb0586").into(), - hex!("a0ebae60a998907a19baa396ae5a82bfe6aa22cf71bfca4e1b4df7d297bd9367bbeb2463bda37aa852ad8fd51803e482").into(), - hex!("84a6edac5ac68a7ca837c46d5ada8fab136748b6c3a3b9165dbbc231ec386b15328e4ef7d69a15d4cf354135348a4ee4").into(), - hex!("8b476b3b065a3b95a3d11ec60a749c2258ddcc5885bfb50b8a086d3fd1e49ff73e1dde733b8981c3d2d206aa0c87b09b").into(), - hex!("b48490c5a3bc9e66cdc78994f7c73e0f2724fec8a304b4147799e5142396df155ef7c42065ed6d2c0393d138fb4d2a0b").into(), - hex!("849ddbdc3ac55ff22a3b2f4bc51892fed694490ab4dd342165ac38c725c8b38921eaefe3c443962925fc3726aa41e236").into(), - hex!("b49c45d9da4aaa64967c28f1fd77b7f709f5a331b58823eb1613856fd8f92635135981830a287e8dbda3a0e0fc481c5b").into(), - hex!("93ccd8c5f82374e0bef6562e16576f742d79b6f400e3485ef36e148088b61fbd882c3d2bb38ab0b43fa1dac77f31d543").into(), - hex!("81522576ae4ec9358f1a16934cd1c1116de609634e68f552924a972101eb7215f037ab8e0582d7b13541537d55554d31").into(), - hex!("aad60e58a19598c5013b37e2e4adc6721eaa7e6e184960d1dc4e6f012246abbb58a047c0679064d5eaaaaff02de881e5").into(), - hex!("b4f034f2b53ff9989e8a0f12c1484c58ed7942432a429af58a6659feaf23f7d2bf20ff7b9a7e0a28a2e09c9a730681d8").into(), - hex!("88b2c68b425269850c1a4f4608aca194da5c641adeb99e2f7fb92e34b8245dff066e73bde072be60f7f2c3d3d13de3b6").into(), - hex!("a749ab53fc2662a0796489be84fcfa59bb723ff748bd8980df0cb4b3d1e2943845b0d7c67576fa0a33c8b0ff8a86932d").into(), - hex!("abd7248ae069d3a3a45b0ef4dd5d7d54b62994e578ea20bdd3b7876596673953b94c5b109a6e4b953b517544b915368f").into(), - hex!("a49da42c27d019a21cc6489ada7b712b98c4ede28ba25dbcfa916acef48446a2baf73e03a48be763378a09774d4a03fc").into(), - hex!("b3c36fa39f668bbc3fec028875a820057dbf96f727bb423280da96d5d50e885d23bc23fb73457bf79089691ce7663a7b").into(), - hex!("8dca376df4847cb8fc2e54a31894c820860c30b5e123b76670a37435e950f53312f089a8e9bd713f68f59fd1bf09202f").into(), - hex!("95d1f944b0c53eb3e9fcd5632713602bbb9195b87a172a370ae2df98504612a55f3968615a39b569ce6a0fe9fb559be7").into(), - hex!("8c9fefe233d0d657349b7efcdc368f5aaead27071d224af780874751e7d241f6b88f7650fbb4133043b24bbebc12aa48").into(), - hex!("a2ee6c29efa982e9b9abd3c5e4f14b99d5d0369d7bfc3c8edae1ab927398dc8a147a89e127b3324d7f4e3a7494c5d811").into(), - hex!("b7ea5e0d3cfcf0570204b0371d69df1ab8f1fdc4e58688ecd2b884399644f7d318d660c23bd4d6d60d44a43aa9cf656d").into(), - hex!("a07826925f401a7b4222d869bb8794b5714ef2fc66fba2b1170fcac98bed4ba85d976cf9ee268be8a349ae99e17ac075").into(), - hex!("b0a47515752c15e4dbeaf9ee27fab3b5c0db82f5c685e8f716fd7d8764164944340430fe3db1a5679e6ffea5a16dd919").into(), - hex!("b7de6d7a4afb05984dce153e5570b104338265e45c8f0156f4d45c458f47add234a479e01c02d3c1817c170b5b65b100").into(), - hex!("acd4d1e11f81f4833353b09d4473ec8b15b8ff31dbf39e97654f5338a26c4020306d51018f1f4b9c4efdb92992408a6e").into(), - hex!("80e09f3bf3ea87d48e04b53d8f3b43b7e53d61f445f8c8a5a35472b84f6bb4f58f17d9832f5881bb44fc06156151e5c5").into(), - hex!("98d6d46f603afebcbc561c130e416d5a588a7e6c1f17f89ed6e30538b7f8dbf4b3c75b8a3331425c4ca21e03fe8b57f3").into(), - hex!("ab671eb947490c43fd05e42a787344b21af89babb705393c82748eaa0cfcf80bee498d275a1eaf1d647ca3b2923d76ea").into(), - hex!("80e1dbf3296bdfa98aeec1a7560682165d13bc628061bd3257f345aa1ba13f8bd1bea14f117af562be22118f5a8265af").into(), - hex!("aef456af90354ff88039d2dde02b0f5a6790aa762b23e0a9da8c6ec92c3b8b3320687bb21666608b4a22615843afd1ef").into(), - hex!("a5a07bf219432e9c80c38596c93560b49c7de287f31e30b7a06fcb4d15982add4a24085adbc5b753c462be989c64c96d").into(), - hex!("84d2eb008578aebd6f01254b7e46584c1524e6fd7a5a2ae5fa0ea560865ca50d52290cf2d12dd20b042f402e62181b4d").into(), - hex!("b8e5226ad3515627ae6840235f5f7b7ecd54e8f01079c324d126ec852f6665ebb77168b3f2b3b51580e04a6ff602d5b3").into(), - hex!("8c1de4264e04ff7e8282faf81c0bfb5943656451be52170211cb7adf4ff21bccbb789400735579c622f69982fcb8e9c6").into(), - hex!("90bfbe37ac3992432e68c95c0d4342a9712126d1f50089239c9f4f6c0c202b54334e08604d245b97dc8e8f6706f6992c").into(), - hex!("b0a771b9a0dd7e352d46c8efcc1834e610dd097711bf7117678a99d386890c93b9b901872d4dcacb6dcbcf3aea0883ea").into(), - hex!("a4eb903990bee2374b14fa66fc262d6821669537e9ba241c87b4b5c9e2b89b32fff4bfc28ab8471ef52e8eebc3e743d1").into(), - hex!("a6b74c706b33d3cae9b7adc5c7502ac98f7bf94a14d579d2bf77b613ae555634ad6fe631ba36dc14bf44526436355e24").into(), - hex!("8296f8caf58316af535def398a43357e48cb3b1e674b857eba1bd1b970da3dd045e22fe6d17dee4e9117f62ece3ec31c").into(), - hex!("a129c9cf33df42b5a98ad98be9d940207ae154c715d3bde701b7160dfe45304679fb0481a4f9dde242c22a9849fc2d9c").into(), - hex!("858b6f1bd3e68fc536bdf1f4bd96db032994eb76e71571e2d85af73b898478b82f9ab432732b0beebc0864ad8025ae33").into(), - hex!("8658a15df961c25648fd444bdf48a8f7bb382d9212c0c65d56bf9cdb61aab3bd86604c687fb682260dbc0ad2dc84bf01").into(), - hex!("a8d152e5d94b75cb9e249230db21af31de4d4f3d4ef60ccbf2212babf69aed2a38435a993ee2f13cca410ad55a4875ab").into(), - hex!("95757096c132e7f6c096d7b93a5a0d2594d5e609b9f13c4a9f878e95a389fa1a111b185dc1fd8f7d98b737dcf8d2af60").into(), - hex!("a22542a4a2ebde18cc6aa29d5dace8b4f6720703f519610dcf01e671018392aff15728e3764730840272c9cfb074b612").into(), - hex!("8f90e72a54e6894d511061957162e753010812346afd4d90cfedb678b99ba1aacf2b6bd0e49b4b0e684da8082a048619").into(), - hex!("b2affe048c187d311a185503d8958cacbe03796edf79bc32e8533941004d9178bd2e376e627e1ba61ed43850c0c455cf").into(), - hex!("b549d272a7f3180826a978d747507e4dc80d82784abb655cfcd3a69cc72e7d58c70febea1ce002a89852a8f934ea70fb").into(), - hex!("a9ef845ab489f61dbfdcd71abcc29fc38f3494a00243b9c20b9cd0dd9e8a0f23304df84939b9652cdf5542d9b3ee085e").into(), - hex!("aa744c552b5fc41e1ac6ca53184df87a1b7e54d73500751a6903674041f5f36af25711e7bc8a6fbba975dc247ddad52d").into(), - hex!("a802b9ffbd4f01b877791aba27da972be4bacacc64a1f45687be4af01b84bd4b83fe2ba1ea78e29d7683f6c777ab2543").into(), - hex!("b44d2d9510516c0abb4fc101241cf0e0223b179fb70686519628c27f0ef56381232961bc79a30f592ef093ffecbc4486").into(), - hex!("a684a09add047c0fe648d9c5618500d1816047168e055e8ac8c952c3544a462cc095b32fab07d939947a58fcb4ec7ba7").into(), - hex!("b5eb31e5cba0193e74968099ace5808dfc457c6f404f270fdc4949b60daa7607ba1811abab1bb19fccdad61d489b6657").into(), - hex!("a10f19657a9bc5a5c16ebab9f9fddc3f1d812749cd5d80cb331f51de651873ff899e0670f1b079b29a194572de387a17").into(), - hex!("af61f03e3ceef5bef36afa29ba2edc7a3b01ca26cec2589edbc9d124dd46e41410e0e3afbae959c83a6f839bbcf8049a").into(), - hex!("8e6bbfe492ecbbb8dc8889d3dcd7037a58db605bc6bb79131a72a9b9c1bad630e75f5e5e0c1bc407e73f3d13b116739f").into(), - hex!("983fc1ddf17f9756c9cecc00b39bb2ad432587a5c6d1c3296a383b9f539c9afe84c6c818447a709c0b686ba26ce5ea3e").into(), - hex!("8d52413f981bc611427ad0534d25e914113d0ebcd6960aab6421608bec6648b89ae4b2ca2153c57d3cf4f1f37212aa5c").into(), - hex!("ae075b66e5f211c2149c45b211d1297bbc1d9e6497cb3315363c492a9a51ae5b9d0a28bfecd755d68553736901ac6606").into(), - hex!("a02f7fec0661394399a82b2e3151009160b3f5392017ba579b301ed42c85100c295acbfed46b6c58a9d71796ed0930e6").into(), - hex!("a3a7196fecd25e9cc7cac79c35365676e48c7be1493df255676adff2209c0719f2190ceff3ce008d08efa07c244c11a6").into(), - hex!("a5fe3dfb5031517bb8db0d5ade1e9f438e84bcf23221de003b03d2a2f4ea97629e81c79abc3769bdb8c69a512c189b91").into(), - hex!("8cd9d7e953c7ae07ee785d68a999e702565960d376692d9ea468556ad141229b1f3bc97926818c078901f73ecc578e93").into(), - hex!("81d6fc2f01633e8eab3ba4d72588e14f45b00e68ab887bdd4ec5e8558965db21189310df973837106216777b07fc0805").into(), - hex!("b75ac3d5b3dad1edf40a9f6b5d8923a81872832eb3a38e515539cec871a353b07cb477f6d55cf15ba2815a70458aac32").into(), - hex!("94d3c9406dc6dd7241a726355643d706e46b35f1ffe4509ac43e97c64c07592821156ba02ec9a78978e66709995a0ac8").into(), - hex!("a3b109249ac2900806f0f39338da72d4f2cc6d1ac403b59834b46da5705cf436af8499fa83717f954edb32312397c8d9").into(), - hex!("8336744d8ef3a3bb3e9ed3d6b83e08cafffc76b7438adedd3a7358b32acec0e73a4635aa3166362ab4e158e68576255d").into(), - hex!("99b74edbac662fff69ba412de466a427a928ce2363c9e59dddd664f6fa50f2e1dd3d464701b01784aa224b3d96dedea3").into(), - hex!("b397ed7134f447d9bf1c511bf92f2d27d7b6d425b8b411365fbef696cff95c2175461cf5dd83d93bb700e50ebb99b949").into(), - hex!("a3e1fe11f38d3954a7f48c8b68ff956ea0b6f8a3e603fd258c9406ec2b685ff48241db5257179ea020a83c31dc963854").into(), - hex!("949cf015ce50e27cf5c2ff1b8e2e066679905ac91164e3423d3fb7e05c64429e77e432db0f549acb99f91fb134b6edad").into(), - hex!("a1c0c317e6e352e16e25c140820b927161ce5d2c4c2e10bca3057ba4d46b4f42ad7aba20de86dad9fc6368ea92695268").into(), - hex!("8d6e3df29419bd0da1deba52c1feebe37744108685b49ca703e1b76fb4d612e3959d3b60b822506e5c0aac50b2f5eee2").into(), - hex!("a15e0cb96a463ab81e661ca44c619b71a159680bbc04707ea5a5867ff38b15416e3abe55d2fabdab9aede1f157dd37e1").into(), - hex!("b6d6482ad7b9b412ffbefbbdcc28eb3d091b1291f54f77bdd53c4ac85f705c454940f466dc272dde7b03c26f0cd6ecb3").into(), - hex!("8391e3ad6ec2686bdc686671d579edac2d5efa8cf0923577df28fe0735e4d5103173d44452816e3c2b2a7fcc1fcc20d9").into(), - hex!("818202d7cb60e4148c71633ccbe1ce311de7b7ff93a1988e86ba29cc58037189f0f275b3323a6719dc9bdcfbc49c35c3").into(), - hex!("a7c0fcc422c6da878926cc6763ae6ec685a5d8fd1afe61269957be6bfb3f1705a8e4c6e6d85bd15636521f5a2ceb3a00").into(), - hex!("8f7bbaaac458bada6d852fe665c87c646133bab16c0d5136c3dc922095b9d647d93a9de7671cb7bfd4cbd138ae0709d1").into(), - hex!("b6e9fe9fa3d4c833c3beae7f798f30f07e3cdf6f6c8eb8e2b70cad51b37af2549dc9f2e7f97f194e5897d4dedb904a45").into(), - hex!("b455f751232de0a48440d09983f4f4718b6169907979c9f282acf7177ab5b1f338fe1f2acd8d0bee4b4aad61d0340839").into(), - hex!("8aadfcf3562f1c357068323352cb1745349a27a7362358d869e617c2410db747149b993ee9e881e252ecdd42fd75f351").into(), - hex!("91bf4c32fa8888d3829d3c33e12550d2ecb70762d5eeecd044d4902e4a7f8b7a2592cf6cb7736eb6bd9d312f85c2777c").into(), - hex!("859426bf6211e68924eefdb26cdc168ac0deab291aaff7036163997bff34d45809932f91e12d113784c05553ca773b15").into(), - hex!("81730b4bc5f755e5b99c321a6996c65e57ea2ebe6c0e4e404ed30920194fd76db65304635ad054a8b25bfd982cead47a").into(), - hex!("b26f5ed09f7d5bb640ec94ddd1df0b76466f69a943b4699f53d45296d5d6b8010bb61477539bc377d1a673d89074d22f").into(), - hex!("aa65c11071be23c9bddaa5203f3166e5cf043efe5fb8f4b26f8a9cabe71db701a450e79eb001c401da5752755d9cf1af").into(), - hex!("a019370ca799c2c98a605850910cf43531bfb616393039fdfc370848feedd3339b2427b750ccc91952b03a09f690e9ed").into(), - hex!("8f84cba7ceb7652023fc8ebde4b00ecde1f550935bab12feb630d6f49517b4148f3cde184bf55d4f6ec99a849fc6f862").into(), - hex!("838733220d1559c800cf1714db8a43a67a0c0d1d3a9fc1e2cdcf615d20406501e5146fe8b59bf64f4c5daa1a6d74f15c").into(), - hex!("9708cfcc9ff95cf23f544119e17518a338575018f153b1ef50118da0681304919a226b2089a417c2ab7b4320dffafc2a").into(), - hex!("a988cfed9f481bc98beb5fc188ed3f6893a3ebba27c3ebace669792f6abf0997727023c3b6930a6421224f5b257b8b49").into(), - hex!("812d3ded3a3c9e58eecf13a29bb4cc13b01b2a0af322423a29bb0e4f6d9021d1d87ac4af7a2a6b88d34f44a8bc1b3c55").into(), - hex!("b2baa7eba496ac4ef60ad8ef27a9677f9507820d95a1c572d322621c4d0226b36146bfc3a9ca1645d123acbd945de3f4").into(), - hex!("89ab1e5c2565f154f92c9b3554160832d176613f1a2f872b6ed62ed925a33fb0b40b71b7443eaaa15099ab24693c8d13").into(), - hex!("8982534f2c343dda20cccf5a9c8bf98240bba5f4e8eb2206e63a1847097deadb6bf0d24b358014d564c5ef1d0448c43e").into(), - hex!("a1e47798a782a024da340d6d6a1b1e5e15a0f2d8668adf349ca375086964414a563cc1eea3226ae637f87e78c0a630b3").into(), - hex!("847b58626f306ef2d785e3fe1b6515f98d9f72037eea0604d92e891a0219142fec485323bec4e93a4ee132af61026b80").into(), - hex!("8368a0f17c8427beb71dbf11a09a2fe8495a33f08c29c74a9a996a88aa01c0a09f9555abeb1ef1592cab99a9e05875cf").into(), - hex!("b38e558a5e62ad196be361651264f5c28ced6ab7c2229d7e33fb04b7f4e441e9dcb82b463b118e73e05055dcc9ce64b6").into(), - hex!("8018499ef720e28759133033833edfe17ed23e42f99058bb79fe844ddee823cfdc43916be2dc9724d18f9726e6f1b409").into(), - hex!("aa9b9cc039d147677aedd1e47ad9013fcc0da0164070ff7305b18e5786c7fac0471368637a3adbb78f3af174a5c1592a").into(), - hex!("a5c0e42851b769d2d822e39222e708068455aae3bdf782975b59d3201e67a58fd66e16d380558bf2086bcab890a92dd5").into(), - hex!("af3f765fd293c253072b33a780ed68933f78d7e079d9a2079b6232755bedf6ebcbce9ba65c01f695602fa8ee17899867").into(), - hex!("8a0192ef0903d7a5ed2e5614a715901f2554b324ee72390974dc90727ff08dafa580041a21a8e6c48a3e08e1b042afab").into(), - hex!("907c827a4fb5f698bf0e6f10ca07741c5b8e3ecb26aa53f938ba34ceb50c01be80c4afc5ac4358a5fda88eadea0cbe73").into(), - hex!("a35ee5c2d7800489723c78008b495e1742f0542dbb487172ef438f60424c81aa41c2397095821248066140662133f6f4").into(), - hex!("b1925ee53c9228cf80e2f5ac0117008a91e98d878f69bf03d00d873ef45f4351cb6988772d89d4ccddb40778d11e0dd6").into(), - hex!("95aafa379cc6a2b4bdd0cad30b7f0a47839952af41f584219ec201c6c4d54610eb2c04b67b29080acb8cecc5e7543fbc").into(), - hex!("8f44c43b80a3c5f488118859fab054745cfe5b0824821944b82fcf870fda6d93489ea9ca4220c24db2f4ad09c6080cb7").into(), - hex!("af861bb21f84c3e7cab55370839d18ac24c475a9e833607fcd7e65c773ee9d64bea203ee9e8fd9d337f1cd28d5ca0d90").into(), - hex!("8f6fde2ebbd7682c69026069cfe93aa5410071f05de9ccd7070c8c3299a6539b39b3798f01a0b4e9b1330510bdb51de7").into(), - hex!("a35d9d6d5dd5428cce7616842203b5fa3721cb4b20f50c0113f138604954fe0cf214ca3d065b578f921054b9efe823df").into(), - hex!("8d562d6c0e0d8325032e1fbf836022c82a8f600a6fbb56c553ee5d1fac0f052c7ce2504c0fd48c9fa6494a6bff63c9fc").into(), - hex!("b02ce594310f1eb8acc92bb80de524a43e663e12fb64fc28291ff207f9d8ae761631416410c3c8f4d6890b8b7e6ed24d").into(), - hex!("a258f8c0eff666c2bb70e505544c3d10d6b258310e4fb2206fd0999f69bfb739a1e232d1e810e7206f490f6c44f6934a").into(), - hex!("a8a77936ca91df3b2ee7394ea821f2bfe91c6ad8193f44651466c170b6ecca97ab356fa7d947ebb4b767e8967092f143").into(), - hex!("88ad79a0320a896415e15b827a89969b4590d4dfa269b662bdc8f4633618f67b249f7e35a35884b131772d08025bfc32").into(), - hex!("a3c66439724d737d20a640bceed8671b20cf6795671b6d442ed1ea5eda6723ae559396c24f44e982ba7751dcc6adef5c").into(), - hex!("8d0e6475acfa2b904e7d53bc7acd070a2ee4894ff5720a20e560e9ecb7872ea442a51cf2f2eee4bef66604a5c08ad9eb").into(), - hex!("804c021152c3304853941847e80480fdaceba3b9676fbe018268cf77d1a1856966c2f9686bb4d4aa0c4118a7e85f83cc").into(), - hex!("a6565a060dc98e2bfab26b59aff2e494777654015c3292653ecdcefbeeebd2ce9091a4f3d1da10f0a4061f81d721f6ec").into(), - hex!("8d5de60e934ea0471d9e0a46489f21e03abb9722f5b3633631a9a099b9524beac5d67716969c83d824498796d9c106b7").into(), - hex!("8c5a9f6eb0a3ea95e75362b06e5cd23968447a212cf22e1419c984d74432c51d290b717f80e8ed3e76b1232216f99758").into(), - hex!("918ebb73eef984d0ce28083306626d04817038056aab4a82ff9ac8176ffdfbf3173c0b05e936daf553248b323fe54f56").into(), - hex!("8f9aededb605db4e499d3c383b0984b1322007c748dea18dc2f1c73da104a5c0bece6bb41d83abdfac594954801b6b62").into(), - hex!("adb198f70a7f1969ed0958be4a9a60dcc1806bced79c63692b9aad6c5648ffea1fed60b24bf4b1862e817cf229e93e83").into(), - hex!("b118f77f99ac947df97e7682f0fb446175185b842380af4ee7394531e4f93002c72b41a57a7c1b923a4f24b10924c84f").into(), - hex!("944f722d9a4879b5997dc3a3b06299182d8f68d767229220a2c9e369c00539a7a076c95f998bea86595e8ec9f1b957bb").into(), - hex!("b7270f33011db1bad18e076a162d6e53d9123808609773eb46e3a4ac69c84c257407907bd5d05b6eb5e926b8d8c6d884").into(), - hex!("b09c0a505457c6b473fc7b2d634222905b36a6ffcc015dbdffa3bd62218c94e891615e77f28e6e18dd8474be8c156695").into(), - hex!("97d076617cf0a64ab3d1f030cfd72a303b6b252c0a7b96157ff7fc8af5970f00d14492c46e8f6f37caafe837d0dc95c7").into(), - hex!("b405520ef829a2a3b8947f1687ab56a7af4026c1a6f99f59aa192bc4f3b12a2de407862ff74ba1b2c51889b8d6b090c7").into(), - hex!("8bb045e7482b7abe670d72eb2f7afe4207b5a3d488364ff7bb4266f8784ea41893553a4bf7d01e78c99ed9008e2c13bb").into(), - hex!("b2df29442b469c8e9e85a03cb8ea6544598efe3e35109b14c8101a0d2da5837a0427d5559f4e48ae302dec73464fec04").into(), - hex!("99daf03fa434a482d9aa4d090884c99b7208c1f5380b9acbf304e1bc33d3d6902afa5d248d20ccf03795e26901356ede").into(), - hex!("b60df25a7ac1ad14aef7e8809c4bfc190b715f94f14b0534cc2e4015ff6c322883cbdc5309e05d67dca4bc641c69725a").into(), - hex!("b46a818f3e492e231d8fa8de8848c16f0d648a2e0d1c816adf9306a8596fdf45922e59cbf745430570a19e54f45e28f7").into(), - hex!("b3ca2ab7d64b71e40693bd3e2288a1f78741a139403c783d259cb9dc9c29f16c00796b6302cdcea4a4314e132b4f9d1c").into(), - hex!("af3d3dffbe55842dfb4417295a6ed1a82d26a579199494b305445215045785759be4cb57dc870c7ddaffbc101a854a92").into(), - hex!("b21785008910a949804d1291e7533752641d31beae3cb518806488f81d58c38a5efe5ed9534ac692e68c3121e2f9d97d").into(), - hex!("ab7eff4ef8696db334bce564bc273af0412bb4de547056326dff2037e1eca7abde039a51953948dd61d3d15925cd92f6").into(), - hex!("8bb4d08318386c91a0136d980a42da18c05743a5c52a861ce52a436e66a8ebe472dac7f7461db32ea5da59a23e9bd6c9").into(), - hex!("a1c84730a5c41dcab9a5ef9e1508a48213dbc69b00c8f814baf3f5e676355fc0b432d58a23ad542b55b527a3909b3af6").into(), - hex!("948f808c6b8e3e109a999657ef966e1e02c96a7aae6eecaf912344e1c7bf7ea51c911cecd3cea2b41ff55acc31df9454").into(), - hex!("8d264fbfeeebb6c4df37ff02224e75e245e508f53fb3446192cd786ecf10d0f704c4fc2e53e7f7318ae1407e46fc0fb8").into(), - hex!("8d47a7c2c62b459b91e8f67e9841b34a282ceb11e2c4b0549883b627c8526d9e0ebd7333ba70630bc0ec2478114b6ae8").into(), - hex!("a4348ad30c12bb7dd03dd014cca599c3499ddf348e7795b0392a18f998289979478374e374a8297b5b6c427441e2b5af").into(), - hex!("92ec1aeb2aa24c51cd5f724972c8b6095e77b237d83f93ed34ca0bc91a1dbf1ad95adccc59e0f0abbfef33f331f3298c").into(), - hex!("941f73b2138b4347ecafcc7b8c3d03f2a54dc49f580394ed08f22b0878ee7cb63d42978f1d320c09e7dbc67648c06f8c").into(), - hex!("a308ed8737b3a9346ff20dc9f112efccc193472e6fde6aa218ceae11e288bbd2c35fa45c1d8bb238696a96767cd68b46").into(), - hex!("8eb03001ac9e22c6956a682ed458e650785c36d23ddbcd51ac4d9cc991325c02519ff1958987a08eb29ff56ff6e2c293").into(), - hex!("80637db55287f891baa0e865d2423191b9a575620bc4493ea76166d47b99fd89ad8625c21f446b01e3ae17292c78f7ef").into(), - hex!("a5b3da08aad945effdb645c797a0a8bfa828c9d658df2783a214597acebda3ffc07ee48d0ce1147d77540b557d9ada91").into(), - hex!("8d5e0b8cde1f62cc8f15d9f596e31de09c221da91b10335b92ef1155802e742442def161223333573158723f3408bbd3").into(), - hex!("931923f0c1f75a197e6244d67525b524ceb07510a6aae8cb3d56167cc1aacc76d26fadfa1bdfc55d8439c6ee4d4d8174").into(), - hex!("963a298fc8876b702424a697929c7a1938d298075e38b616c8711f1c7116f74868113a7617e0b4783fc00f88c614e72d").into(), - hex!("91ead7dacf43905eb5d4b179af29f945479ed074126bad3b5a2bbc1663af5f664fe53a36684e9389ab5819e53f1344fc").into(), - hex!("b8a6c999068c13fb71a99d75eabadf7edd2d32e28607baf001a0aeec412fdd3575602c68d3feb4d743b90396705e37f3").into(), - hex!("938bbaa0ba14597067ff4c0a7cfc1529c44160d6f61cfad12246526d84fb7a1ba964d3bbb065a348cf7a98356ee15234").into(), - hex!("80e44d3577f12cabaed7074feeb57162ff80b61f86cce8f41d1d1250bc455070b09f6ea9d0c263b7b4824701480f4c14").into(), - hex!("8d74f4c192561ce3acf87ffadc523294197831f2c9ff764734baa61cbad179f8c59ef81c437faaf0480f2b1f0ba1d4c8").into(), - hex!("a0133deca5ae8f1100df8db69920b2d0c31aa21bd3849dbaf4c264eaeaec8937ab8f982770ce1ea17e0e258910a56d02").into(), - hex!("b6a25d493d708b035b853f1f7a6628d8e0b205d2678293f763d7ea4da11d298539533b22b43ed2e5f708648556f3094e").into(), - hex!("a58c3a4ba86d0d6b81c8411bb73a528b4f3bc2debac0e0208f788c080a3a96541d57c927143c165f595070afe14b0517").into(), - hex!("a52c15840b89d92897d1e140b2b8468a88886c5e1092861e598b3a433b340ded5b35b3d632a9879820fd56f20ca3a68b").into(), - hex!("8c122bea78deee98f00a86184ded61c10c97335bd672dadddc8224a1da21a325e221f8a7cfd4e723608ebcd85a2f19fe").into(), - hex!("87a51e0011dd0488009baac9c611fbde01878f9cf1584ea407599742bb32ef10586d9040dae3e9800a125de54f80c047").into(), - hex!("98181e9291622f3f3f72937c3828cee9a1661ca522250dfbbe1c39cda23b23be5b6e970faf400c6c7f15c9ca1d563868").into(), - hex!("b01a30d439def99e676c097e5f4b2aa249aa4d184eaace81819a698cb37d33f5a24089339916ee0acb539f0e62936d83").into(), - hex!("830e70476c6093d8b9c621ddf0468a7890942589cae744300416639a8b3bc59a57a7e1150b8207b6ab83dafcc5b65d3c").into(), - hex!("83ca733849830cb8fc2ef469e7e464fd94def561ce49ff0aa352a6ecd0e52c7aefcd69ab59f3d1ed2d5b8536d0a7895d").into(), - hex!("b76cb8cb446eb3cb4f682a5cd884f6c93086a8bf626c5b5c557a06499de9c13315618d48a0c5693512a3dc143a799c07").into(), - hex!("8b027c14affe47f83ee59b504d83b2fd2d9303de2c03ee59d169bb199d9f4bd6533d7f8c812dd7a6f1e8155e3e185689").into(), - hex!("8e6b888197010ebadd216da35b9716daa8675d93b3c33a96a19fd9ca42624f6b430b2ff115cd0f5b717341605dda24bf").into(), - hex!("b6652440bd01316523feefceb460158cd9ba268dd8dbe860a0271f0176230f057767597e4197885ba907318ca202ba06").into(), - hex!("a0b3dff15982a38a2f56d8c6cfc5c5543c045bf2db24571d23387ccab42abe2756f34d5f0bf6a426bbad3c358b8bdb00").into(), - hex!("a8b593de2c6c90392325b2d7a6cb3f54ec441b33ed584076cc9da4ec6012e3aaf02cec64cc1fd222df32bf1c452cc024").into(), - hex!("96b478b1e5e49d4ea3fd97c4846ae0f781dcc9f9ff61ee022ca92c3d8dfba89c513c46e8bb38b73e6b678a79b9b12177").into(), - hex!("946d585d7aa452d37a8c89d404757c3cce2adf2410e18613483c19199abd88f7a12e206f87a43f6009e42f4e31ed20c0").into(), - hex!("8c432e044af778fb5e5e5677dbd29cd52d6574a66b09b0cd6e2a5812e71c91559c3f257587bfc557b4b072a822973a60").into(), - hex!("ad2cdae4ce412c92c6d0e6d7401639eecb9e31de949b5e6c09941aeafb89753a00ea1eb79fa70b54699acbfa31eda6b7").into(), - hex!("b504cb87a024fd71b7ee7bed2833115567461d1ae8902cccd26809005c4a56078617ec5d3fa5b29a1c5954adc3867a26").into(), - hex!("b8876bda1e709ab16e1347a1107852a7898a334a84af978de39920790b4d82eb0739cbfc34da1c7154dd6e9f7674759c").into(), - hex!("973dcf44ab60f55f5d10a8753ea16db9faedd839466a130729538f3a0724f00f74b3ca1de16987d7c6e24e9467f62bc7").into(), - hex!("94df5fe87661101a89b49091a3d4de89331cdbd88531ebb08a95f2629886ee53b3dcbcc26bb6bc68b443303d8d397141").into(), - hex!("a92beb343caf6a945990adcf84302c55d1fccdef96c34a21f2c00d3e206a9b2c6c6b412f66e5d4fafe26ef6446cde705").into(), - hex!("b298aa927713c86adfe0de1a8d6f4083b718c8be27156da9fd11abd8edb3a54a926ad487801eb39cfc9363a0a3be0d44").into(), - hex!("b34d4d2e15079e7e80fdba30cddf4fc0e6c9a61f7ab06a6ea0a4e55fd5bf632c6d72e021d6264d935439d321de883bb6").into(), - hex!("b4a86fb5b0049718caead1bc036833a2caeb88e1afadbbbcb0cd021d95e1f33fcc916f0b97fc1b9226c37050e3463796").into(), - hex!("b6cec65e5268818c82c0a4a029b02f8d23de98b68730a445119fee670118eb34027c23c987fac950f9b0151631328a4e").into(), - hex!("880b4ef2b278e1b2cccf36a3b5b7fbce94f106ed9fa2820cb9099a7a540a57e9fdeef5c0fb0a743049828fc2b8c46163").into(), - hex!("ab2053c376c6bd113b89fdb2ae3b8401aa891135345885730c61cac7813f69ea7d906c531be752e28657f73af92d1f4e").into(), - hex!("8fa2d7b22af8e6b82679ebdfa13efdcb34289a554653ea6c1b16efb9f957f7fe64df787e7b03d8cdc8a732b91c916bd1").into(), - hex!("8e825c03c8409a3302266dc5f47fbfc381dfbafbadc37bd8d40f079ca8963d4c5ae6ef0d0ba6aef2d4688736f5f6bb45").into(), - hex!("a40ef3d2291d8782540961ce285054678b3d322d3cf7fc154207228c290708b1abfc37a4d7762dab3dfea582a112444a").into(), - hex!("8dbe8fcbcc414eb352245c52549973f73d987012de9d5f2b2f55dfdc43cf8cc9ea6b147abf149817f80f9e15aea566c6").into(), - hex!("a7e8775e04214e3b9898ffb9625dc8bcd1b683e333acdceddb8ca6db241df08a7b80e9d476a711b8b7d66aefca81e9cd").into(), - hex!("8b6ed54668f78a4a7624683b9bf3abf2bb0b6dccffccd8c0967df6297dadaf51732800fb9832b069437a6bf82ed7e6ae").into(), - hex!("9437ce85146202d3815df7f341a182678665dfb74b96006dc9d6acc16110d00b4a02717b702a765566457710ff5a7280").into(), - hex!("86a790072efa2cafa97be4b6b31f8c533f3b20cf3922fc0285fd403da436e4c49c65c5f08d77bfe823526c67bb58fab6").into(), - hex!("8ee8873de7cd28a54ba2c63a80b63399effed76b154e96ed26e7c0668b9f2476e298688b6a00c4b2ab9d020a897695d7").into(), - hex!("975c3261f0f32d59473e588f89593be38f5694cfa09394a861e4330b7800fb2528ea832106a928c54c76a303d49140e2").into(), - hex!("b5222582ed6604b9856a48039bd643340f4bf1bf0fc805e8453a9eb7630d6cea1452d28536706d6fa3ec16617617991c").into(), - hex!("b75c28941ee3f91b3535b4eaa0fb17b59ca65b5256601a1f6d0cf2bb4d66837fd16e51d6942856679012a5730a66e519").into(), - hex!("97b510f9f46bdf77a002b2403d8e42b6d6ad5329ea080940844429763ad3efd592652789c8d3d4fac0903c705f533cf7").into(), - hex!("a3fd9d8bbdc98394883022299fd9793e0c4f374d8e40d6ce89b2869d3173cb6a5476371d6095dad068ff217729f60af4").into(), - hex!("a57d5de556853484b1d88808d2529450238bc467376ded84cfd7b4a1ba258f6d43b5958220f962c57b033abcef1d5158").into(), - hex!("8465bd8be9bd9c2c6116d4ae44ec6618c109cb9aaee2d241e7a6ed906d398ef15a6fc18bc8b1d3398184241405954bba").into(), - hex!("ab1cc44983e46a6ea2430aa6616ab28614f43624665e3e6ae31a9357c0c5434f34e56c720906e184327693cc4ebe1fa2").into(), - hex!("aafe14dd3b680f096010788226d8413ca628feedad79a2bc78cb04d47c6ad910f7f46ca87b8f8281744625d8f42d5eea").into(), - hex!("86a533b02ae929f67c301649a2d58651b98cdffe731b63fa32aa1013c271634bbb088c0d02865913c11bbb1bf57c0e12").into(), - hex!("b28df3e04bde4ec373171401dbd0546f4cb6fa8e9a4a8019aadc653d4e05e0b5b9a64417715dd87f6df9e7b3145f6659").into(), - hex!("ac63fc758c1a3bc5cbff0f5e0b5a07a5aa801363b129d4e0360165c7dc1057ec37b0d808e9fd6b179e2c1e66bbc6090e").into(), - hex!("93f941b4fe6c05621e7a651b87669eefd60b6e8a4a8e630a51fa3fee27417b9eebce39f80a5bade9ca779133ad8388f6").into(), - hex!("96d7a69eaf2761bf0e5ebcd607b134d5dedba8e262ca1d6d3e8fbf23e6419a8ce1bbe4cd23b9e4b5f80db54a802a9795").into(), - hex!("8b62902fb2855300580e94830a4bc825d997ede33bf356fe3b7c08d6a8bd85a37879433fc6bee58f9b44ca280f4e8dfd").into(), - hex!("b043156fcd02b75dbe940c763fa8e8a7c7f6d74c1d5395db5ce544af3b6097eab61686950535a810aa95889ced12f74d").into(), - hex!("8614a7599c8d97aa9ca63b876f677977cf0daa969ff2a9a153f297a4a46e08fa5d91492995de94dc32cf009ce6bb5b5f").into(), - hex!("81351fd284d6d07092875f366bc5e53bfd7944b81eece85eab71a00443d1d2a9fc0337aaf34c980f6778dd211caa9f64").into(), - hex!("8c722aaf5d5dad1845056bf5e56dbff0f8b501f4846610f99da01130a49c96db9962bfd9be20670658cf276cc308be08").into(), - hex!("8effe3fb27c9f76bbd78687b743b52e6f3330eddc81bc9006ca81fd640f149d73630af578694f4530833c2151522dcc1").into(), - hex!("a3d327f48eb34998a3b19a745bca3fade6a71360022c9180efb60d5a6f4126c3f4dfa498f45b9a626ca567fdd66ffbff").into(), - hex!("a59a59db246f981e9c8e54aba52898c714d9787fef8969a6d8677fe6dec82950ff22a1364393b579f3e596cdf5bcd7b1").into(), - hex!("a15ebe9ab6de62a4d1ff30b7fc58acfb14ea54d7fa513cbcb045af3070db22bf9c1e987fa26c0c54356b9e38fa412626").into(), - hex!("b58396bce7d32ba6c70adbd37156d859e153c1932d2b0c7c874a1182ba831439e80d6fc6d7d88a870e193f515aef2264").into(), - hex!("b666dae42ea858c9b7d903ea3ca5279f619c71ac6e3fda7469e2bbba08c7e8e12d6a3c35ff2c6383673b1b7c21db5e0e").into(), - hex!("8eaaa21c8955f15bbcfd5756421a045e7b4825576379cc6229fe9751f7a7738b90be19ba52261db01c1e13af955675b0").into(), - hex!("8027bc62b59f9f15613e38da74ccc71fc3eaee26f096d187c613068195ce6eb64176013f2d86b00c4b0b6a7c11b9a9e5").into(), - hex!("8275eb1a7356f403d4e67a5a70d49e0e1ad13f368ab12527f8a84e71944f71dd0d725352157dbf09732160ec99f7b3b0").into(), - hex!("b8c41c09c228da62a548e49cfa107630166ac5c1469abf6d8aab55938ed1d142d5ddbc4f1043eed9496e9002cac99945").into(), - hex!("912750d2f1b21756662a400236f797b8ba76c73e5af95941a8c6ef9427838c4826715c80942cf8cb7ed01566bc490754").into(), - hex!("862af7dbb38ad7293a4e598cb52a8ac84dacee3d9bf007b5cb6a18a1acead0aa33f6dba796ce630e632c97aeb7100d68").into(), - hex!("890def696fc04bbb9e9ed87a2a4965b896a9ae127bc0e1cc515549b88ddbcbc02647e983561cab691f7d25cf7c7eb254").into(), - hex!("b835ffaf54c8b878d3c4262ca2bf5e6be2c691adced622b35d998ed72e1467ba907f4fde8d64ce43b43a8196f48e55db").into(), - hex!("a6d7e65bf9f889532090ae4f9067bb63f15b21f05f22c2540ff1bb5b0b5d98f205e150b1b1690e9aa13d0dee37222143").into(), - hex!("8ebfbcaccddd2489c4a29a374a2babc26987c3312607eadb2c4b0a53a17de97107c54eab34def09144b3098c082c286b").into(), - hex!("900b9972180a2c8753f5ff49fdd2cfe18c700d9927b3c3e16deb6376ad6ee665c698be72d4837b94911a0b4c183cb140").into(), - hex!("aa9679c01ecf1f1452c54688e01cb25bf157bde6b09b1ed460b8c175d65eba439c7ad4b7c1d72415f5622f3fbc068dc8").into(), - hex!("887a4277ee8754733f3692a90416eeac1ebee52ff23173a827f0ba569bd84efd806eb9139049f66cc577e370d3f0962d").into(), - hex!("951b27456e2af80436608aadec54ebd03bda37fa58452631da63bc5ff3eecb5ffb73d356b19f6c9c4225fcb0da8fda20").into(), - hex!("9104b5af82dbca914370eadb5518b26bee7ed7edeca74b741585ba8b249204e2c998bd47a02cef4335e236f8efafef94").into(), - hex!("8757e9a6a2dac742ab66011c53fa76edb5ebc3c2fbd9a7265529a3e5608b5c24b4482fed095725e9b8fed5a8319c17a4").into(), - hex!("a7acf82999de75f231fd80770bcb0f4c720d6b1e4a2558fa1ce854382fda92beb89fea5b5d229dad85fafee7a9e98329").into(), - hex!("8fb74891a8642f25a55b5eda5818367aac2fdf9663ad1e3dbc0166b484c0cda581b243cc75131167f1afa9caaf6705a0").into(), - hex!("897d7a19b70dcef1af006df3365981d73068c81f18017f32fb9966599481496efd5f6caceef943b31c52750c46d9590d").into(), - hex!("87ac804ccfe7f1fa156b8666664a397236832569f8945e11d4688a9e43ada4d4104efb3fda750f48ef655a29357c5e7d").into(), - hex!("af18cf1e3d094b4d8423da072f98b3023d296f6a1f2a35d500f02bde522bb81dc65e9741c5bc74f7d5613bd78ce6bc03").into(), - hex!("9752561179783f336937757b619b2fdcb9dfce05aa3c4fce6d582dc966182eb85ab4ccb63e7e1736a7c5fad9d33cccd2").into(), - hex!("89e19b665ce7f6617884afaf854e88bb7b501ecdd195a5662c79802d721f5340eca8c48341ad1d6c78f519f82e5a9836").into(), - hex!("94402d05dbe02a7505da715c5b26438880d086e3130dce7d6c59a9cca1943fe88c44771619303ec71736774b3cc5b1f6").into(), - hex!("a76adeddf2454d131c91d5e2e3a464ef5d3c40ee6a2ab95e70ef2e49e0920d24f9b09276250ed7b29851affbdbc7885a").into(), - hex!("8934e9a3feababa12ed142daa30e91bd6d28b432d182ac625501fe1dc82f973c67f0fe82d39c9b1da3613bb8bfe2f77b").into(), - hex!("a5c11337eb91ce0e9b6d61bbdadea0a063beee1bc471cc02dc1d81c5dd2095315c400cbc6c33d23c77e98bba6bdf5439").into(), - hex!("92d00e64ed711951aeb852908aeb6fd379ea516872dd512384b1e773ef078e52e6d618beb202d437d2a46bcb78087f7a").into(), - hex!("97f1a7370b4f5acf83b466f519da361c366915f560385dd7eff9d53700ad81b25c9862bc71d35428e82372a5ae555ea0").into(), - hex!("95614544f65808f096c8297d7cf45b274fc9b2b1bd63f8c3a95d84393f1d0784d18cacb59a7ddd2caf2764b675fba272").into(), - hex!("8d474636a638e7b398566a39b3f939a314f1cf88e64d81db0f556ca60951ec1dca1b93e3906a6654ed9ba06f2c31d4ea").into(), - hex!("b87e5f481b938ac8a481b775cc58be2a06604549e3c810fc4734bab76099e5c617f0243c4c140cb7dd6d36a6dc2286bf").into(), - hex!("806efb61d1c948efc10dbf9bef30197d1c269e5e7fcf20a84367b26223d33fade413a0bbf4e33f0d1f1a00967289015e").into(), - hex!("880b99e77a6efb26c0a69583abb8e1e09a5307ac037962ddf752407cacaf8f46b5a67faf9126bdbcb9b75abf854f1c89").into(), - hex!("82ffe4de0e474109c9d99ad861f90afd33c99eae86ea7930551be40f08f0a6b44cad094cdfc9ed7dd165065b390579d0").into(), - hex!("941cd102228aa81ef99506313a4492a17c506e7169808c6b14dd330164e9e8b71b757cbe6e1bb02184372a8c26f7ad1f").into(), - hex!("ae96dc808c316a677977831bad1e529ef965dadb5d6aea25ab008fe7bb1543e596e33052cfbe4279fa060201199d2c34").into(), - hex!("a2053719da2b7501dab42011ae144b3c8d72bd17493181bf3ae79a678068dc3ee2f19d29a60b5a323692c3f684f96392").into(), - hex!("b17171939519d90e243d41839c3c5ce7ac7e6a978e4a7e56b2c8e6a2b1b587c7eacea47f2e31a55d88555d252c810ebd").into(), - hex!("a958987c2b3c84df8176b600f5b75a8badef9653c71eff967e76648937def38826eaab4027a639f4f300b20019166250").into(), - hex!("854aafa329e2b2563355641eba95f2aba5b33d443ab16f5e342048f97d97c4e2812ff27c6f4180b8110272f3151be690").into(), - hex!("94d4a1e3a3d28a948f14d1507372701ac6fc884a4905405a63663e170831578a2719714ef56f920baa0ca27954823e39").into(), - hex!("826be957cf66db958028fa95655b54b2337f78fb6ef26bd29e2e3a64b130b90521333f31d132c04779e4b23a6b6cd951").into(), - hex!("8261f7e644b929d18197b3a5dcbba5897e03dea3f6270a7218119bd6ec3955591f369b693daff58133b62a07f4031394").into(), - hex!("84926cf2265981e5531d90d8f2da1041cb73bdb1a7e11eb8ab21dbe94fefad5bbd674f6cafbcaa597480567edf0b2029").into(), - hex!("8fb51e3ef3c1047ae7c527dc24dc8824b2655faff2c4c78da1fcedde48b531d19abaf517363bf30605a87336b8642073").into(), - hex!("8ba45888012549a343983c43cea12a0c268d2f7884fcf563d98e8c0e08686064a9231ae83680f225e46d021a4e7959bb").into(), - hex!("973ab82026d360e2cf5676d883906186bc61b43f60767ca58f11d0995e40780b163961e6e096299ccf1c86175203abde").into(), - hex!("b77416ea9a6b819e63ae427057d5741788bd6301b02d180083c7aa662200f5ebed14a486efae63c3de81572fe0d92a9c").into(), - hex!("820cc2ac3eed5bce7dc72df2aa3214e71690b91445d8bb1634c0488a671e3669028efbe1eae52f7132bde29b16a020b7").into(), - hex!("a6b434ac201b511dceeed63b731111d2b985934884f07d65c9d7642075b581604e8a66afc7164fbc0eb556282e8d83d2").into(), - hex!("b81821a79c9148b41d24d85dc997336a1e1719da0e31e42af30812b97a5af31708ca3e7bc2e803c3751cff23af5c509d").into(), - hex!("a013cc5e3fbb47951637426581c1d72764556798f93e413e1317849efd60f3ecb64c762f92544201eb5d6cfb68233050").into(), - hex!("b5f32034d0f66bcbccefe2a177a60f31132d98c0899aa1ffff5ebf807546ff3104103077b1435fa6587bfe3e67ac0266").into(), - hex!("86ca8ed7c475d33455fae4242b05b1b3576e6ec05ac512ca7d3f9c8d44376e909c734c25cd0e33f0f6b4857d40452024").into(), - hex!("b781956110d24e4510a8b5500b71529f8635aa419a009d314898e8c572a4f923ba643ae94bdfdf9224509177aa8e6b73").into(), - hex!("a3f9dcc48290883d233100b69404b0b05cf34df5f6e6f6833a17cc7b23a2612b85c39df03c1e6e3cd380f259402c6120").into(), - hex!("a104d4bad69f1720307ed12363d1ec97952acfe09d9e3650034c33f3f20c763271ebe0d5b50b1d3bd15c469f4573b09d").into(), - hex!("94bbc6b2742d21eff4fae77c720313015dd4bbcc5add8146bf1c4b89e32f6f5df46ca770e1f385fdd29dc5c7b9653361").into(), - hex!("951aa38464912a29df2101c60771d6de7fadb63f2db3f13527f8bdacb66e9e8a97aaac7b81b19e3d1025b54e2c8facff").into(), - hex!("919b5187af7dae210f151dc64a9cbd396d1ae04acadebf542a7123004efc7ce00d6e14c170d876fbc64dc1b5d141a5f4").into(), - hex!("88e1e459ee5aeb8b36ed004f6d03da296101106fbe1b18f9bbf63e92321db51670c34050fd3b7dc56a4bad76403823ee").into(), - hex!("86b3ec14a8ffb811a0ecc3771f600d8b08c098537d100fba66def19e7ee4d1c397a311977bf37e6cd2d47a8a2ee8c223").into(), - hex!("898c4873bd356ba8015f6f686d57088fa8f79f38a187a0ef177a6a5f2bc470f263454ee63d0863b62fca37e5a0292987").into(), - hex!("b471c72bd2971353f4b44248b8e6cf5316812861a88ccfc20fd0d89a5e010428c387228b2f6f14c12f79e31afc9d0753").into(), - hex!("8b7cb5b8de09a6dfceddcbaa498bc65f86297bcf95d107880c08854ed2289441a67721340285cfe1749c62e8ef0f3c58").into(), - hex!("983cb6bbfe83bce8326e699e83fca01ea2958c09808c703cac97a0ea777e5a5f3f5bba9169a47732de7459a3c7af47d2").into(), - hex!("8235a3f09078dd34ce2fc17cc625e061298713b113dda12d354b3d2ba80e11c443b1dd59c9eb5a29513a909645ae97d4").into(), - hex!("8bb51b380a8a52d61a94e7b382ff6ce601260fa9b8c5d616764a3df719b382ec43aec9266444a16951e102d8b1fb2f38").into(), - hex!("9579973ee2559da09b327c62b1cc0177f2859872885dca709e24dcfbb9bdf9224a6d26869aafce498f44c0e6bd8a996c").into(), - hex!("842ba3c847c99532bf3a9339380e84839326d39d404f9c2994821eaf265185c1ac87d3dc04a7f851df4961e540330323").into(), - hex!("99dad12f78e1a554f2163afc50aa26ee2a3067fc30f9c2382975d7da40c738313eaae7adbc2521f34c1c708f3a7475b7").into(), - hex!("b96e3ff8bdae47aa13067c29318b1e96a7fe3941869c17ce6662183b7b064bf261e1cea03e2a4643c993728a2606b5b5").into(), - hex!("955bc897171aa65d0159aa6e5d51855833f83d8bd5e65f93ad26c27781171783f3988a12bf987472edb39994bd071ea5").into(), - hex!("a59249e4dfb674dfdc648ae00b4226f85f8374076ecfccb43dfde2b9b299bb880943181e8b908ddeba2411843e288085").into(), - hex!("ac2955c1d48354e1f95f1b36e085b9ea9829e8de4f2a3e2418a403cb1286e2599ba00a6b82609dd489eda370218dcf4c").into(), - hex!("b2eedff11e346518fa54e161be1d45db77136b724d497e337a55edfc896417de3a180bf90dd5f9d92c19db48e8574760").into(), - hex!("976eb5543e043b88d87fda18634470911dfe0e0cabab874ca38c1009e64d43026d9637d39dcd777bc7f809bbfc3e2110").into(), - hex!("8ea5f88a79f4eb9e7c0b6b29f8ef2d1cc4c15ed0ed798ab11a13d28b17ab99278d16cd59c3fa8217776c6dfae3aedf88").into(), - hex!("b7f146a357e02a63cd79ca47bf93998b193ce1174446953f12fa751f85dc2a54c9ed00c01a9308509152b3bad21d7230").into(), - hex!("87fd7e26a0749350ebdcd7c5d30e4b969a76bda530c831262fc98b36be932a4d025310f695d5b210ead89ee70eb7e53b").into(), - hex!("b9445bafb56298082c43ccbdabac4b0bf5c2f0a60a3f9e65916af4108d773d62ffc898a35b0b8efb72ea846e214faa02").into(), - hex!("b106c6d13ca17a4c8ea599306e84918127cf2de21027ac3fe5a57d35cf6f3b1d7671c70b866f6e02168ae4e7adb56860").into(), - hex!("9276e8051bed8f5dbfc6b35765aac577dd9351d9d6ac1bb14496bd98091005b9a4737b213e347336413743f681f5043b").into(), - hex!("aefc682f8784b18d36202a069269be7dba8ab67ae3543838e6d473fbc5713d103abcc8da1729a288503b786baac182d3").into(), - hex!("97578474be98726192cb0eac3cb9195a54c7315e9c619d5c44c56b3f98671636c383416f73605d4ea7ca9fbeff8dd699").into(), - hex!("99c34f9bd0fcb18b3d931e562988cf91886a417f8678f22651bf3cf138df2bbec3f675de90f62dda769e0eda03d72b7e").into(), - hex!("a0047e03c89a95248543618e6b7ca2c7aad7acda3c9f85771ec5c93fa898c651e8b2ea3b6b799d8cd592290a986cdd7d").into(), - hex!("993726e0b1c2277b97b83c80192e14b67977bf21b6ebcde2bda30261aa1897251cd2e277cfcb6193517f1eb156d2fe86").into(), - hex!("974a5180e55eab23d4c973fbee6ad1010335161ecdb849fe6520b34c1f96530a4faff80bd738fe281019b79d968c472c").into(), - hex!("8f1d90034f998018c3f4b5947b40b139fcead2e40aa80fdec6a4337c60e9d5ff1923dda7f0b5b1731ff16f55027d41bf").into(), - hex!("a7d9ae9621dd1f3da3cd2d435e891cc3579c4c0d60d6a4565cac86c315cea21a9ad883559fe7b897ae6e05f1aa989ad9").into(), - hex!("800f6be579b31ea950a50be65f7de8f678b23b7466579c01ac26ebf9c19599fb2b446da40ad4fc92c6109fcd6793303f").into(), - hex!("86fa3d4b60e8282827115c50b1b49b29a371b52aa9c9b8f83cd5268b535859f86e1a60aade6bf4f52e234777bea30bda").into(), - hex!("a80ac2a197002879ef4db6e2b1e1b9c239e4f6c0f0abf1cc9b9b7bf3da7e078a21893c01eaaab236a7e8618ac146b4a6").into(), - hex!("b4bf70468eff528bf8815a8d07080a7e98d1b03da1b499573e0dbbd9846408654535657062e7a87a54773d5493fc5079").into(), - hex!("ae0e15a09238508b769de83b30582cc224b31cd854d04fdb7b8008d5d8d936dbdd3f4a70fff560a8be634c141772561b").into(), - hex!("936f7e20c96b97548fef667aa9fa9e6cfdc578f392774586abe124e7afc36be3a484735e88cc0d6de6d69cf4548b1227").into(), - hex!("8163eea18eacc062e71bb9f7406c58ebe1ce42a8b93656077dd781c2772e37775fe20e8d5b980dd52fdad98b72f10b71").into(), - hex!("86a6560763e95ba0b4c3aa16efd240b1873813386871681d075266511063b2f5077779a4fe49ffc35e1f320b613b8c94").into(), - hex!("b156d9d22722bb6e3b75b3b885b64642fa510ba7e6057657cd61bac43fb9c284d05bb09e2d4b78a2a4ddada85da9c702").into(), - hex!("9779ca2759dbed8081f0cbbfffcb3b842ba335e3ae48a60c5e5c77c7a2a0623e4c415ec3a023cc4e216885fcbac3ce52").into(), - hex!("b8137fd57ce7d3cfaf8bdbaa28704734d567d0e7a2d87fb84716722c524bb93acb2c1284249027f3c87bccc264c01f4e").into(), - hex!("8cf06b34e7021e9401eb705dde411ecf7e7e7185f8c0b0aeed949097df31812a9fdd4db7d18f9383a8a5a8d2d58fa176").into(), - hex!("8c65aa29a9ee9066b81cf24cf9e0beca3e8e8926e3df22d5ff1236297e158cc8bc7970a2c7016009cc4efa8f14b14347").into(), - hex!("ac7e49f2059e99ff65505742978f8d61a03f73f40141a2bd46fde5a2346f36ce5366e01ed7f0b7e807a5ce0730e9eaa9").into(), - hex!("a1c25eb9b73723982be78180770aa63c5f7b23c2e54a2ed7e75a860c4512d273008066f1124ac8a43c60fe1e2a8bf03c").into(), - hex!("9310722e360a5652737362f6b9cb6e9c3969a0c9bb79b488b3c7d19d9e8c42ebd841df346258ded2e393895c99b413cf").into(), - hex!("893272a63650b08e5b8f9b3f17f8547b456192ad649c168bafd7166b4c08c5adf795c508b88fd2425f7be8334592afb2").into(), - hex!("b576c49c2a7b7c3445bbf9ba8eac10e685cc3760d6819de43b7d1e20769772bcab9f557df96f28fd24409ac8c84d05c4").into(), - hex!("af17532b35bcb373ce1deebce1c84abe34f88a412082b97795b0c73570cb6b88ea4ba52e7f5eb5ca181277cdba7a2d6d").into(), - hex!("8b8813bd2c07001a4d745cd6d9491bc2c4a9177512459a75dc2a0fa989680d173de638f76f887de3303a266b1ede9480").into(), - hex!("ab73a043ccdfe63437a339e6ee96ef1241264e04dd4d917f6d6bc99396006de54e1e156d38596ba3d15cb1aaa329f8f5").into(), - hex!("b67146b202afec0132ac0070c005cf664081e860339f8f4d45ac3e630dda05560936e646673e652d08cecd8d18fc64da").into(), - hex!("a750404e9d4b1a48f767d2b6aa699200c92c3b8102597f8c5c1dbaaf08112a0587c05801dfebb3612fb6dfd76ddc9ccb").into(), - hex!("b0d4231814e40e53ab4eed8333d418a6e2e4bd3910148b610dec5f91961df1ad63f4661d533137a503d809ea1ad576fa").into(), - hex!("81fc724846b5781f3736795c32b217458bb29972af36cc4483dd98ab91680d3d9bc18842db2661487d3a85430dc9e326").into(), - hex!("a5cf6f4fd67aecb845eebc8d7304c98c69806d774d4c468350f7f82ff0f5baeecc56837705e39432a8d246aa2a7075ed").into(), - hex!("a71d2c8374776f773bad4de6edfc5f3ff1ea41f06eb807787d3fba5b1f0f741aae63503dbca533e7d4d7d46ab8e4988a").into(), - hex!("825359cfe68ad6a75578a94be6419179e0aa088170b6c20fc5c249dc3be7a260d687c93d8d8a343c7c72c2ed6a716de3").into(), - hex!("b6aeb7a9b934a54e811921494f271d5d717924c561cd7a23ab3ef3dd3e86184d211c53c418f0746cdb3a12a26a334fc8").into(), - hex!("8c6fc89428c74f0c025e980c5a1e576deadf8685f57136e50600175fa2d19389c853d532bb45a3e22b4a879fab1fcb0d").into(), - hex!("ae95ddcf3db88f6b107dcf5d8aa907b2035e0250f7d664027656146395a794349d08a6a306ce7317b728ca83f70f3eaf").into(), - hex!("8c03fb67dd8c11034bd03c74a53a3d55a75a5752ea390bd2e7f74090bf30c271541b83c984d495871d32c98018088939").into(), - hex!("b8d68610fdee190ec5a1f4be4c4f750b00ad78d3e9c96b576c6913eab9e7a81e1d6d6a675ee3c6efac5d02ed4b3c093a").into(), - hex!("87d4b20bbe2dcd4f65f4e1087f58532d5140b39a5288e1a63fc0b7e97a6a56946eafdd90ba09300c3d1fef6356ac6b7c").into(), - hex!("83e264b1d3d4622826ab98d06f28bbbd03014cc55a41aaf3f2a30eec50430877d62b28c9d6d4be98cb83e1e20f3b13db").into(), - hex!("97ffcbf88b668cde86b2839c7f14d19cb7f634a4cf05d977e65f3cd0e8051b2670e521ae74edc572d88201cff225e38a").into(), - hex!("91efdbcaad9931312d7c41d24de977f94d7f3f7b88090a1f72d9a097a1e30cc805c5ea16180f463022d9b26b8863f958").into(), - hex!("a4b507a4bc2bc424297bf8968bf385fae3acc686cff4b7933b4f5b6ef3149995393d5331dbac4b629e8adce01a91a4cc").into(), - hex!("a76a26c30d8abbbd4bf982bb8bd2066a2ec823a5eb6fbe37c664e67efbe2f72d8ce2d00223b900699149f8441bff5ada").into(), - hex!("b3a5497365bd40a81202b8a94a5e28a8a039cc2e639d73de289294cbda2c0e987c1f9468daba09ea4390f8e4e806f3c8").into(), - hex!("a09b2a07d861e01645accfb088f7f9ad524186bd439712775459a60f8a1fbbd43ee084e4d6e23ffce06daa189cd1e654").into(), - hex!("a41cf5d678a007044005d62f5368b55958b733e3fdde302ba699842b1c4ecc000036eda22174a8e0c6d3e7ef93663659").into(), - hex!("a698b04227e8593a6fed6a1f6f6d1eafe186b9e73f87e42e7997f264d97225165c3f76e929a3c562ec93ee2babe953ed").into(), - hex!("8bc66e370296649989a27117c17fbc705d5ac2bda37c5dad0e4990d44fcc40d3e1872945f8b11195538af97961b5c496").into(), - hex!("8bff10f91b8c0abb6c9542588da17fa0118ffda4c82626a754887e333d7d69661b3ae4e400da15c49565f8d10a77d0d7").into(), - hex!("ac715c7b3d794860a61d9c7bd224a2b14a6023f696afa30345aad2ce0a6ea6dbc142f34af1ffe7f783542851a28e8ece").into(), - hex!("91c5e0b9146fe5403fcc309b8c0eede5933b0ab1de71ab02fac6614753caac5d1097369bdeed3a101f62bbcae258e927").into(), - hex!("8553748da4e0b695967e843277d0f6efeb8ba24b44aa9fa3230f4b731caec6ed5e87d3a2fcd31d8ee206e2e4414d6cf4").into(), - hex!("ac722bd742374f925185ea7d4d62d7510b2d8a6ebf5c750af6ce83e2d8a28c95a3e298870ec8254ab2d1d0aa2a063c60").into(), - hex!("b083c4cefb555576bb37b71f30532822cb4b1e1998e35cb00ffb80ca14e2853193c16a6756417853d4a74d625744dd76").into(), - hex!("85745bd84c92ddfc55df11fe134cf70e3c340aa1c7cdd6188a03308cf3a840f4f19629f9730b2e6426424989ff03000d").into(), - hex!("845b4531dee808b583645f56fa98cbdecce3ea100db60524b64f68e29866173791f01137714f4dc7fb8612f7f7943263").into(), - hex!("93f03495d53c781be8b76e37e68b64aa260523004eff6455ddc8a8552af39854e5181f8c5365812b1f65926534fba5dd").into(), - hex!("801c126abff96fe9b042be8869d2907d0c6963a79901f9db46577a445418b7465a1f4b346933d433e539536a9a2df01c").into(), - hex!("952cf6782b0ad3e85625391cc5f486a16bb5b1f8ea20defcb6857bd7d068dcd2701bc7ed5c3b773a869180d9042f772b").into(), - hex!("b3b6eccb2ec8509b4eea8392877887180841ab5794c512b2447be5df7165466d7e293696deaabf063173e5f2238ce763").into(), - hex!("b63fd45023f850985813a3f54eceaccb523d98d4c2ba77310a21f396e19a47c4f655f906783b4891de32b61a05dc7933").into(), - hex!("a113b889be5dcc859a7f50421614a51516b3aadc60489a8c52f668e035c59d61640da74ba1a608856db4ff1fa1fe2dfd").into(), - hex!("87fec026beda4217b0a2014a2e86f5920e6113b54ac79ab727da2666f57ff8a9bc3a21b327ad7e091a07720a30c507c9").into(), - hex!("a3ee8fd53158dad3b6d9757033abf2f3d1d78a4da4022643920c233711ff5378ac4a94eadcaf0416fdcca525391d0c64").into(), - hex!("8d6bed5f6b3f47b1428f00c306df550784cd24212ebac7e6384a0b1226ab50129c0341d0a10d990bd59b229869e7665a").into(), - hex!("b549cef11bf7c8bcf4bb11e5cdf5a289fc4bf145826e96a446fb4c729a2c839a4d8d38629cc599eda7efa05f3cf3425b").into(), - hex!("982691766a549df56436acd0fe76da78132e027515f27174c10d117bfcc20ed73fc31f5465bd7a22a101094fe913e221").into(), - hex!("985af1d441b93fa2a86c86b6d7b70b16973d3971e4e89e093b65f0ae626d702202336869af8e3af3923e287547d5384b").into(), - hex!("994b7baecc8bb68d270a3a88c58e4054afdbd713b4472f9522b27c1762c637ef8f013d745ce9d1dc8fc4d986d4c9338c").into(), - hex!("827dabda84c7f7b1adc0f5ca0fccf0729e9d7f78e1ffa7c5e9c4f66610ff0ab776c880b00c77137cf7abe14df977febc").into(), - hex!("acd17cba1203748b55bd9d7b940a16bb7c02988c93007a80b87e0bdb049b91f5ecce577e3e4ea68a0abe998a72cd300d").into(), - hex!("989fa046d04b41fc95a04dabb7ab8b64e84afaa85c0aa49e1c6878d7b2814094402d62ae42dfbf3ac72e6770ee0926a8").into(), - hex!("99dc48a054f448792523dcdeec819e1b928b1bd66f60f457261f0554f8532eedd7152792df70ae5316ab2f9c02a57cdc").into(), - hex!("ab33c65587ecb3278325948c706aed26547e47ed2b4bc027e9119bb37bec67ddf5489fbc30304ef6c80699c10662d392").into(), - hex!("ae89e41d8cfbf26057a4078f8a5146978e658801b08814190cbce017d79beaeb71558231a72bde726fa592fb0828c01c").into(), - hex!("a9901df92e2d3abbb25f3bf4b913692c4cd57da327b01c8ee2362c02fbefcf66cdb792c17a81dcbde3c9b9dba313e4a1").into(), - hex!("8ee41011424da5e2ecea941cbf069ea32420749f2519434d3d8f9725ac789753404687a6745cffe4bd5dfc8fec71a719").into(), - hex!("8cfcdfa192b17321be4e447204e1a49ecaadca70a3b5dd96b0c70ab64d1a927d1f8c11a7e596367e5fa34e2307af86fc").into(), - hex!("b8a0003e949cf994b1bb25e270cb61358200c93b1c6f611a041cf8536e2e0de59342453c5a8d13c6d4cc95ed8ce058f3").into(), - hex!("adc806dfa5fbf8ce659aab56fe6cfe0b9162ddd5874b6dcf6d658bd2a626379baeb7df80d765846fa16ad6aad0320540").into(), - hex!("a83b036b01e12cadd7260b00a750093388666aff6d9b639e2ce7dfc771504ef8b2090da28ec4613988f2ec553d1d749e").into(), - hex!("825aca3d3dfa1d0b914e59fc3eeab6afcc5dc7e30fccd4879c592da4ea9a4e8a7a1057fc5b3faab12086e587126aa443").into(), - hex!("845a4a09941f48677e6c03699770f9a56ba72695089e432a6f232294dd8da6d34e394116a9a87f3b0902c78332af9439").into(), - hex!("b2f168afc35ed9b308ab86c8c4aaf1dcd6833ce09153bb5e124dad198b006e86a941832d387b1bd34b63c261c6b88678").into(), - hex!("a094cca9d120d92c0e92ce740bc774a89667c6f796b438b0d98df0b7aef0935d8c915d5b0dad4b53e383dc9f095c29fa").into(), - hex!("956ecb233b3529b2d9cb80ae49e48667f2a3120e4a0d7131d1e9ec36db3a59dc2ef2a579cbb99d6f14880ca83f02b64c").into(), - hex!("906cde18b34f777027d0c64b16c94c9d8f94250449d353e94972d42c94dd4d915aa1b6c73a581da2986e09f336af9673").into(), - hex!("824c8a1399ab199498f84e4baa49ff2c905cf94d6ac176e27ec5e2c7985140dbaa9cc6303d906a07ab5d8e19adf25d8a").into(), - hex!("889a5cf9315383bf64dfe88e562d772213c256b0eed15ce27c41c3767c048afe06410d7675e5d59a2302993e7dc45d83").into(), - hex!("95791fb6b08443445b8757906f3a2b1a8414a9016b5f8059c577752b701d6dc1fe9b784bac1fa57a1446b7adfd11c868").into(), - hex!("99049e9a23c59bb5e8df27976b9e09067d66e4a248926d28171d6c3fdd1ab338944a8b428b2eaae5e491932c68711c7c").into(), - hex!("95c810431c8d4af4aa2b889f9ab3d87892c65a3df793f2bfd35df5cfdb604ca0129010fa9f8acae594700bece707d67f").into(), - hex!("841d77b358c4567396925040dffe17b3b82c6f199285ac621b2a95aa401ddb2bc6f07ebd5fa500af01f64d3bb44de2df").into(), - hex!("90c402a39cd1237c1c91ff04548d6af806663cbc57ff338ed309419c44121108d1fbe23f3166f61e4ab7502e728e31fd").into(), - hex!("968d44188e2d9d1508b0659e96d6dabd0b46aa22df8d182e977c7f59e13aa05d5da09f293f14f6f2ee1b996071cd2f25").into(), - hex!("8ae9585caa3c73e679fe9b00f2c691732f7b7ca096e22d88c475a89d4d55cb9fba3cd0fe0cedd64ce75c591211664955").into(), - hex!("94b81d5ad72efb4dd60867e71afcd8e87e1f24bf958d42fc07db66f6185a1e610987ab9ceef63109a36fe5544a0cf826").into(), - hex!("8499a8c3d67d1f6eccf1c69274393dc498cff862ea8e6c11ffb8107ae190d258ddc1d294f2a8f050488df0212063ece2").into(), - hex!("921109a390e4d7fbc94dff3228db755f71cb00df70a1d48f92d1a6352f5169025bb68bcd04d96ac72f40000cc140f863").into(), - hex!("b464d763e5ef724ab7ee13a60015df5c9a7809a79188ff6a7e0d5e5400febd42ad7330406a59704a44a08f2289d659c8").into(), - hex!("96f1a36134e0d4137a7fe8bbb354f50aaa67f28f194ae2fdbe8be3eb24596678d8c9287765ee90c1f2778d0d607931e0").into(), - hex!("b031d93b8f119211af76cfafee7c157d3759b2167ee1495d79ad5f83647d38248b4345917309ef1a10ecaa579af34760").into(), - hex!("88a7dc337d89324f025f686f37d21240c7da9a1cb802259ea8d8a83e246dcc2adceca7ca3534bc7bf8f3ae1cbeafb5c0").into(), - hex!("b30e022b8a563655074e08e123b5f96956bbf0fe221cc629c5fedd2764a66b475916ceb98867f935b4a47212e53ae9f3").into(), - hex!("ab6e3180dae399d41243f23545e5e6d118844f9b8edba502a3503fd1162ed826f9fc610889a1d685d374b6c21e86067d").into(), - hex!("96cf5760c79cfc830d1d5bd6df6cfd67596bef24e22eed52cee04c290ad418add74e77965ea5748b7f0fb34ee4f43232").into(), - hex!("a5817c74a394b0359a4376ef7e9e8f7dfa6a7829602da225074fb392b715e1fd52c50cae0f128a7006f28b22f233fbf5").into(), - hex!("a50ab79cf3f6777a45f28d1b5cdad2c7ea718c60efeeb4c828d6307b29ef319445e6a9f98aa90f351c78b496575150c1").into(), - hex!("8b300dea07e73dd2f07b05d477e51f8424589f6b2fa6f461240e1322a3a7ab5bf227b74544bb5d66a297702cdbf6c6bf").into(), - hex!("b13b5cb86dc8b8fe87125f1a51fe98db36bdde4f600401408b75059a44e70b1bbfefd874e539691f3f1bf6f54db883c8").into(), - hex!("8d06205cd66703ce6776b38b98c32b27f45c7b3f65ea2d05e2b702c24d553f51c69bf0b17e8db7382475e3d370d2e8d6").into(), - hex!("a11a7496c712734aec80738e30d2bf245758b34245076149854eb209fa6403be8bb0d4e515becc881b7f3610749260c0").into(), - hex!("9615800f8c95f95bf25055ae079b964e0a64fa0176cc98da272662014f57e7cd2745929daf838df0094b9f54be18b415").into(), - hex!("b19ca6e55f349bbb2dc3e429520ff5b2e817972470794f35c1aac8c118b37a694cfcc875b6d72225343799825d2f5c39").into(), - hex!("a650864b7eb6769aaf0625c254891447351e702e40d2be34dfd25f3b5367370de354318d8935ba18db7929270455ae6a").into(), - hex!("a649208372f44f32eb1cd895de458ca1b8be782746356f08ac8ef629429d0780a0799fcff85736e19aead0b79bfff261").into(), - hex!("89461cb2dadf51d6f1208b0965c8eabec895d7b19b7d90d3c6e49dbe75a75c30fd26db3dfb169dd46a4342280225032a").into(), - hex!("b5d6f664ec92e5343792d5d6b629919c5fd8cfb874677df2264daf02bcd9d12facf9b859d5402839c9022396e20d260b").into(), - hex!("a7179d338fe5a0e4669364a364e17f8d00cb6c59a80a069afd5f4f14510df2eee90c07826553e4f7fe46d28f72b2903e").into(), - hex!("8ded37d67b5368619a090266e9b5585fbff60319a90a4244a3c3342641f5bfa5130998dd97d7a42505cd896c29255530").into(), - hex!("a3fd63e87a00b48ba46a646a26187ae6dcb16779721973ada13a545853e2e51b5e4df04630d670884ad4a2304cc60c67").into(), - hex!("92761b7e31f0c758b3b1f5b43a194b25aabec668101946eb6511132863d3afb9d18f833d43a8338d0e7bc78d8689e523").into(), - hex!("ab8a8769c754008a7976b6799e81d7bfe97413d0a79b90715703c1f8f567675463ec93aabee59277121fc4df88b5c7a9").into(), - hex!("b354d0d1bd942f79002a2eaf37eb99dab650170e7040c13c824803ed7c1670dc910ccae13bbe58bde003829b140b45ea").into(), - hex!("b9eed89e003894ad2cc9d9b93a45247e1367ac69a00b0ed5e3280c1188b4cb90eb870d449b83a852a798bd02f9d0c813").into(), - hex!("b900a55013d0427e5da6b21611d6ae3e648f54f794cb099b2d2beebae0a957477a94dc415e8ec5e68e9029ce50d43843").into(), - hex!("afbf44071c2c905f7c8ef396eaed7f13deb7a91719cb5e8b9226aaceb876d81a10076383edc6216bc2f5c38a480b2957").into(), - hex!("80bdb82b7d583bf1e41653966b0ba3b4fec0e7df2ff08e3fa06fd9064bca0364263e075e1582741a5243bde786c9c32e").into(), - hex!("a841fe9ff26db21ade698f6dbfba025d90ae9f81f02af9e008fa0a429b993fb04d06acb93e40a9f81c78f73334555a17").into(), - hex!("8cd49711b42af58a5aae75a38fea9ddc5e4183c467a3159b5b0629f01ba548513c577456d34c861911e85782e52c3b1b").into(), - hex!("a322b5d2a6e3cb98b8aaa4c068e097188affef5dec2f08c3e9ce29e73687340d4e5a743a8be5f10e138f9cabbe0c7211").into(), - hex!("942772b7c7c47d4e5957ccf1d6f1450070930af3e2b7eaab0dd7699372445df0cc910e6c0efcf501887dd1adabdaee23").into(), - hex!("9834f66e5c946c3a8241ca2bbde046a7e88072124911d5d15c037a95b61e82b88b5c2058fa4a3721537dee39dee5da18").into(), - hex!("a90cc5b9c4d84f36962d0d55d5bc123dbe5ec5f4fe7b6bf0d009028b3cf14e36c11bc5365391cb4ae548d5eb04fe371b").into(), - hex!("a7c2174eea2b66b2a71cc8095fae39c423a353c7d5020ec2d0551317a66202fcf082c6119ba768755523fff49791bb4e").into(), - hex!("ab92b2a177dfa55d202a653532f0e04d1339ca301aebe6a0e8419bf45be3e573b6b9ae4d3d822cc8279367a3d2c39b06").into(), - hex!("8a9ad977988eb8d98d9f549e4fd2305348a34e6874674bcd6e467c793bba6d7a2f3c20fa44aabbf7151ca53ecb1612f6").into(), - hex!("a7d1676816e81a752267d309014de1772b571b109c2901dc7c9810f45417faa18c81965c114be489ed178e54ac3687a1").into(), - hex!("a575be185551c40eb8edbdb21a0df381c801b6e99467fcf5882dd7cb34916960ce47ac732c1920ad3218f497b690cef4").into(), - hex!("b50c306f78143b37986e68efa10dbe1fb047d58562e9b5c5439b341dd8f1896c7ae586afac0a3213759784a905c1caaa").into(), - hex!("b31e89b4a034c1b73d43b3d63ea3bddea682a6a5327eff389c70b13e9e72185b0327682a0cb1ff3c4a4f8ba08b13d898").into(), - ], - aggregate_pubkey: hex!("948a4b8d91bd29969cd4470b1bc24d34586d38e73d5be71c98a9894899471a5f708747563276b4b6d2716fdb72860267").into(), - }, - next_sync_committee_branch: vec![ - hex!("cb3fea582872d90706ddc6d029bac0d791ea75d43c3ab04f056f62a11b89d27b").into(), - hex!("f26b4bb68eb7e6906c8ef4a9958398a48e4450bf9c8a462fafa4fde51cd5628f").into(), - hex!("690925f92c1d322d2ff2a5636539094825dfd2c9bf7538fe111b2358e03a71de").into(), - hex!("51d977e358166401c7cd3f6185468c1a8c69a4c3d8577535dd583bc427692e02").into(), - hex!("88d2089aaf2f6fd8f6e4ae499c6fe33cc34289eb9310780861e772204f07b670").into(), - ], - }), - finalized_header: BeaconHeader{ - slot: 4055040, - proposer_index: 854, - parent_root: hex!("8adf3b288deb17566a553fa7a06a2f63f4ac4cea4868af4d89ddca41f73ae9b9").into(), - state_root: hex!("595e9ebaaa23f027672b4d2a33173a22b2839baac709c7f8e66d3219f492ee9f").into(), - body_root: hex!("acf37b466d4f6b5d1db5b6ffe5d77c13972d116c2b0809de924427fd597d391a").into(), - }, - finality_branch: vec![ - hex!("00ef010000000000000000000000000000000000000000000000000000000000").into(), - hex!("3d4be5d019ba15ea3ef304a83b8a067f2e79f46a3fac8069306a6c814a0a35eb").into(), - hex!("a2e2a28c0bb0ad56c25f3c461a4bfe4f3b3b894bc0105a62e85f43a4ae1adc3f").into(), - hex!("690925f92c1d322d2ff2a5636539094825dfd2c9bf7538fe111b2358e03a71de").into(), - hex!("51d977e358166401c7cd3f6185468c1a8c69a4c3d8577535dd583bc427692e02").into(), - hex!("88d2089aaf2f6fd8f6e4ae499c6fe33cc34289eb9310780861e772204f07b670").into(), - ], - block_roots_root: hex!("7054ba439f83e9b2223911e25fad48eb28f5c362d94c0de2455a45436cc93897").into(), - block_roots_branch: vec![ - hex!("c99a842c81d0b956eef988dbcd90499457d61f942375c6cbf67262909b708db5").into(), - hex!("5d32345aeb10aa3ede4be021d2231dac47cc2074f74b72466f0b042e69adf70f").into(), - hex!("b69608a2377956c1a39c3423d3399ccfb12307623c9df6358f5fcfca64a80102").into(), - hex!("cefd9b668e49ece82bd4b0ee2f1efc88ecaf0d9af464fb622735663aaf106c4c").into(), - hex!("c113ad1c971779b15c64772ab69cd775edfb926a60447974913bb6f58fbd12fb").into(), - ], - }) -} - -pub fn make_finalized_header_update() -> Box { - Box::new(Update { - attested_header: BeaconHeader { - slot: 4058720, - proposer_index: 1088, - parent_root: hex!("37c7398a391c71da07258b48f41d4caaaf891fdf558e110b9c25db716fa8ff55").into(), - state_root: hex!("6cf68a66c993f1c27f499e054a5fbd7c0e625c34ea0057fda9691c561e990002").into(), - body_root: hex!("109e4c0c647d42dba0c8c6569997c51b5bcfc1a33475b1433ded6353f48423ff").into(), - }, - sync_aggregate: SyncAggregate{ - sync_committee_bits: hex!("ffffffffff7bfdfffff7fffff1effffef7f9ffbdfdffffffdffbfffbbf7ffffffffffefdf7ffbeffdffef7bfffffffffffbffffffffffbffffffffffffffdfda"), - sync_committee_signature: hex!("b5727bad1db101b6f0fd7ec4cb79b43dae90366fe0cae31a62439388eb03ebd75732cee3bde51f814203a4c0f5b23898120cd0870f0e662bdf0f85a048b534d9a906e6d32a65df019059d34f724221075734ec2849c09679febecf6929934f09").into(), - }, - signature_slot: 4058721, - next_sync_committee_update: None, - finalized_header: BeaconHeader { - slot: 4058656, - proposer_index: 810, - parent_root: hex!("bb6cfda1e02c3117d9fa41c1e1594ce0645352a073ad8b474f83c148e0d9954f").into(), - state_root: hex!("f938f0f0cba85234afbaace20545134c70b35e6ff9f74d944b0fea309109f3cf").into(), - body_root: hex!("cf6a7a6f653cb64b2b910278a478339b34eb08abf00e0766d10c7a8fe9bdb139").into(), - }, - finality_branch: vec![ - hex!("71ef010000000000000000000000000000000000000000000000000000000000").into(), - hex!("3d4be5d019ba15ea3ef304a83b8a067f2e79f46a3fac8069306a6c814a0a35eb").into(), - hex!("a2e2a28c0bb0ad56c25f3c461a4bfe4f3b3b894bc0105a62e85f43a4ae1adc3f").into(), - hex!("aa2bad8cf9b0433d3d79bc5b95c067048b018d1d2ffd0c66db6e7cf86e0a314e").into(), - hex!("027f238235d07ac9757543c19deabe1d553d6fc110e8bea4b037b1fab263b4dc").into(), - hex!("7dfa1cc1907e8927295f78a770bf86b28f5c2bddcac38beb3b4beb265ff5608f").into(), - ], - block_roots_root: hex!("4a5a57fb0769443f6472f59dbb78d7a9a69ff61d09aa89d5da645d634ec46a14").into(), - block_roots_branch: vec![ - hex!("a09ad7f3afd681bb6fd54abba339549f3b601beedea79a5b7d448b1cf1e1613e").into(), - hex!("acdfd4d5eba154f118a84af7a2d17ba6100fcd9c24fc235e927f584a5b56e32f").into(), - hex!("36ca106eac009ed605e680415b105b3a6591830c38034511f300aae44802235f").into(), - hex!("171561a9e1afb413132d0f4d3cbbd810766c151be32ec9f2d609f40aef9e2588").into(), - hex!("7cd963eba3fb57ee5ce37ee4b621d24fc14d642e32f48dc48ade528e11458ab9").into(), - ] - }) -} - -pub fn make_execution_header_update() -> Box { - Box::new(ExecutionHeaderUpdate { - header: BeaconHeader { - slot: 4058654, - proposer_index: 1039, - parent_root: hex!("758fa0a54047c0221b3107423e78b8910514ba7a0c4250401dac77108dce2ff8").into(), - state_root: hex!("53c79320b4b7649e39a9f07903f4fc68a7cfe7a81cb2e30293dd7b00ee13f69a").into(), - body_root: hex!("a76a7f037f12ee070dcb273d9787b13ff0cf17420a39564a101789318a157ec3").into(), - }, - ancestry_proof: Some(AncestryProof { - header_branch: vec![ - hex!("bb6cfda1e02c3117d9fa41c1e1594ce0645352a073ad8b474f83c148e0d9954f").into(), - hex!("b44967bd8ff799126cad993ff7369f7a28d60281304d0c7c058e6721b6ce4c61").into(), - hex!("0009710ef2467f95542c2e8e7a7249282c08f62aedbe7d21ae4a7af04e1a890d").into(), - hex!("7bc5dc20638cfe1cfd5d2547c3efbed4cfb533c76c27d41f723ed876ab9edc3a").into(), - hex!("6dcd59a8a041cf2591921417cb8133f0ff5af9bfe1f1ab491e75a751adc9c9e1").into(), - hex!("3f25a2852042c7bf2c421bf16c0b9373c1d27ccb1eca4088f8191712481261be").into(), - hex!("d1f9cfe1f04031459e5c45f47b2a49c780cabb8ca5b8d76461db2b88c337ed76").into(), - hex!("8c37e3119bf7d3cb8208e832669261b0eda1b3c7b11d321760ba1c0649f61416").into(), - hex!("1783d3b7041ecb78d9e1f913b82bd335229ca1f2cc5d3cc8dd28c94152e887b4").into(), - hex!("e39fc366c951c7cb2100308b0a9417581c46812a7d7e89f516c19863f7686f96").into(), - hex!("3c29b6d9b8715dbe5d6831364229efc91ad56ae69e93cb119d5ef4bdc53fcb37").into(), - hex!("3cf6fe5e8d91dbaa77e6befa81e04fb25a3e089317d83ff35fc8adfbbf2aab5a").into(), - hex!("b41c97cf3b1b4b5bf2999dfb1b3eaeac5c1b12205e9ab0809988e3779d119047").into(), - ], - finalized_block_root: hex!("b4802863fc1d32778211ce6aac8109c73c516a003213f4f5333c80472d08fe4e").into(), - }), - execution_header: ExecutionPayloadHeader { - parent_hash: hex!("9cffcba69c88a619483e13864704dde5db80e05f8d49018f615395ce09cd24ab").into(), - fee_recipient: hex!("ff58d746a67c2e42bcc07d6b3f58406e8837e883").into(), - state_root: hex!("6a4aafc93626778475a416721104229035e91f0db788d0099b57e756cd272f0a").into(), - receipts_root: hex!("e1ce670bdcf9acf4c62fee845cd7e81eabbb6db9ddbff56130020c6cf999a45d").into(), - logs_bloom: hex!("a000980408008000328c1458805c005048b84812c134200090b40428568108a0648090105085100301844800090802484420800d020282019002040d083004031a20420240a4a8900a43296938c040802040220170860210910020036b8c482228c004440300021c82c0400110402a10800582424001c00000828310210020a18130504020790dca716194100880ea5501149104002bc05e189080901c4001010e0a040658a410072021230a5224265030082404000aa11f28162e216636000408842103d41010760972060060000500c20130b000000065401483200081a84934f020020120009618002269c884724616040000840e18080300024490000230").into(), - prev_randao: hex!("5d9ac7ea788ecb534e98bc9079fa0bef199011dadadedaee5dc40e2cd702d664").into(), - block_number: 5025098, - gas_limit: 30000000, - gas_used: 13802943, - timestamp: 1704437448, - extra_data: hex!("476f65726c69205365706f6c69612d4265706f6c696120513966").into(), - base_fee_per_gas: U256::from(19959019915_u64), - block_hash: hex!("795021134c2b7f9c00b498ff7b0971dbbe061561868f702d8ca68a05e5eb5a99").into(), - transactions_root: hex!("3cb7c92fde5d511cc90cd67e375c4388794f4c01e375e8e8a06d003b5593fd12").into(), - withdrawals_root: hex!("5f5155fd8e5cd24b7ecb1e039792b0caff01dfda2990786d9ffc88325b5d1ea8").into(), - }, - execution_branch: vec![ - hex!("85ff3d1c2bc3dcdd5543bcd29a1224c6a8c24875224fbb1e3b69f0515ffaacda").into(), - hex!("336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e").into(), - hex!("db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71").into(), - hex!("fe1fa7acd413c54ebd394126ade19fc624ad9e4c60792d54ff3d60b07076b76d").into(), - ], - }) -} diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/fixtures_minimal.rs b/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/fixtures_minimal.rs deleted file mode 100755 index b9476a0fb0809381413725cff24458bec75d58c8..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/benchmarking/fixtures_minimal.rs +++ /dev/null @@ -1,248 +0,0 @@ -// Generated, do not edit! -// See README.md for instructions to generate -use crate::{CheckpointUpdate, ExecutionHeaderUpdate, Update}; -use hex_literal::hex; -use primitives::{ - updates::AncestryProof, BeaconHeader, ExecutionPayloadHeader, NextSyncCommitteeUpdate, - SyncAggregate, SyncCommittee, -}; -use sp_core::U256; -use sp_std::{boxed::Box, vec}; - -pub fn make_checkpoint() -> Box { - Box::new(CheckpointUpdate { - header: BeaconHeader { - slot: 152, - proposer_index: 7, - parent_root: hex!("7ecd45ee8bbacb20c6818eb46740b0f8d2fbd5af964e0dd844d3aa49ca75ce26").into(), - state_root: hex!("0859c78c54a2ecf1af0aecc05f3c8ba9274d1df500bfc4e6a7194c0235f55def").into(), - body_root: hex!("561bec3e5b200ee1bf6b60b5858ca2d7766ac9327b46355c070afafae8fca2ba").into(), - }, - current_sync_committee: SyncCommittee { - pubkeys: [ - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - ], - aggregate_pubkey: hex!("8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6").into(), - }, - current_sync_committee_branch: vec![ - hex!("c6dd41df7b4978b7ee7479dcbfdbfa9fb56d1464afe9806bfd6f527e9d1c69a4").into(), - hex!("b3824f305318071010d4f4d9505f9fc102cdd57f1c03cf4540e471b157f98e3f").into(), - hex!("66677299731d34be9d4dea7c0e89d8eae81b8dffa3c6218152484a063b1064a1").into(), - hex!("ef53c2b02cdb7f44fb37ea75f4c48c5086604a26be9635f88b1138f961f87773").into(), - hex!("1a3f86a88fb2ec99cb1d7b0f35227c0f646490773bac523c30fc7eccf87c0f84").into(), - ], - validators_root: hex!("270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69").into(), - block_roots_root: hex!("8ad7487f0e79a6cf67ce49916f74e5b272be9e38ea5f8a6a588eaa89ea7f858e").into(), - block_roots_branch: vec![ - hex!("90683050cb3f3b04b2a89c206365747bef4c42154feb5d3f4198dfd48b2067ae").into(), - hex!("34f521b45630273930475b8a4350fe2c71c804878ebcd89064eb1dc24a85e468").into(), - hex!("81e166eaeaff5df2d3a5df306c78af075106cbf63c2f1b50ab67eec2dbb0f5ce").into(), - hex!("857526b3bf20de3ec6e57565dd593304f83e47541cc18d7c2d5b4c9f2724ba14").into(), - hex!("1b4bedd5ecc523599da000387f5e33ffe5324b8e234c43ee702de8ccc88d713f").into(), - ], - }) -} - -pub fn make_sync_committee_update() -> Box { - Box::new(Update { - attested_header: BeaconHeader { - slot: 144, - proposer_index: 2, - parent_root: hex!("74e7da3093f79414b5ab593d1c12ba63767db7f4af62672fa1be641331c338e0").into(), - state_root: hex!("92a08dc74f6af7bfcd428aa1677d58f36fefc159c0134b312c186a69fcd00496").into(), - body_root: hex!("51506a8230d40d065a29ad01197c983b9b5d4b9abd26d3c48773941f4e6f482d").into(), - }, - sync_aggregate: SyncAggregate{ - sync_committee_bits: hex!("ffffffff"), - sync_committee_signature: hex!("92cbff711040bcc4a81616edbadbe712ea3ac4939c3d3121a4561b4b3950e2e5b09809606b79fdad2ce20fdef413120a04f02dc65149ca9b6ecab2a94ac4087062df4ab03e161ae56fa6fee4ba40af29c3b4327e21e846f98144cab30684d197").into(), - }, - signature_slot: 145, - next_sync_committee_update: Some(NextSyncCommitteeUpdate { - next_sync_committee: SyncCommittee { - pubkeys: [ - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - hex!("a8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac").into(), - hex!("81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e").into(), - hex!("b89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b").into(), - hex!("88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e").into(), - hex!("9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373").into(), - hex!("ab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34").into(), - hex!("a3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b").into(), - hex!("a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c").into(), - ], - aggregate_pubkey: hex!("8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6").into(), - }, - next_sync_committee_branch: vec![ - hex!("c243f5286cbf6c3c5f58ef6e1734a737bf96cfc0adbf2ee88d4a996f8dfc7097").into(), - hex!("6ae16cb8ea57014c75a9469485bc024bb1e64676656cb069753bf091e58de88f").into(), - hex!("1afa3c77cd75040c1bbbc5d1b7c07e65eeee44fbc659b64ccd55b4ba579038ae").into(), - hex!("89b14216f60859ae366de066eeff4c7b69f8b5738c5b70cdfab4b3a976f9f12f").into(), - hex!("b7d8fa1e64e6d6339a7c68a81e20daab65804777b93a04236fa503b575c2f1a9").into(), - ], - }), - finalized_header: BeaconHeader{ - slot: 128, - proposer_index: 3, - parent_root: hex!("3774d6a479fc6f71ccaff3f8a1881c424b0319e96a7da15f87e3b750ba50dae5").into(), - state_root: hex!("85937007c564046bcc147d53b1d2d78941e2787ec059bee191420369a7f80447").into(), - body_root: hex!("c8f31c33ffaeef2d534363b7ef9ec391f90c17bc5bbe439cf0dcf0de8eeb4d7d").into(), - }, - finality_branch: vec![ - hex!("1000000000000000000000000000000000000000000000000000000000000000").into(), - hex!("10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7").into(), - hex!("c2374959a44ad09eb825ada636c430d530e656d7f06dccbcf2ca8b6dc834b188").into(), - hex!("1afa3c77cd75040c1bbbc5d1b7c07e65eeee44fbc659b64ccd55b4ba579038ae").into(), - hex!("89b14216f60859ae366de066eeff4c7b69f8b5738c5b70cdfab4b3a976f9f12f").into(), - hex!("b7d8fa1e64e6d6339a7c68a81e20daab65804777b93a04236fa503b575c2f1a9").into(), - ], - block_roots_root: hex!("b10da1bbb1c0dcb01637b9e3dcb710d1013dc42a002bd837d5b4effa0ac995c9").into(), - block_roots_branch: vec![ - hex!("9c62693fd336e4b3ca61d3cbe9caeb85f1f1b59ef50aeddfce53dc7321d6058b").into(), - hex!("364947a6574f4acc97888e1fddb5ec718859d54cd7e3db98cda8db4dbea82ded").into(), - hex!("10a61d26b0c1b5b89cf5b82b965f772a1b6ae852fbd13a986c5c63bf12b99244").into(), - hex!("506ed8ca9908af4b5acb4cfdec6dd968acf38346c16385abca360c9976411c3e").into(), - hex!("ada571922caf3dde48e19f1482df8d17dd6bb05e525c016c8cf6796a5d7f4a00").into(), - ], - }) -} - -pub fn make_finalized_header_update() -> Box { - Box::new(Update { - attested_header: BeaconHeader { - slot: 184, - proposer_index: 5, - parent_root: hex!("13548d0b72ef2b352ba73a53274ad02e9310d8417d4782bf9cdad877da549595").into(), - state_root: hex!("cbd5e841afef5103a613bb9a89199d39c40751af1af7cabadfa8e1fd49c4be09").into(), - body_root: hex!("c9b5074ac3043ae2ad6da067b0787d61b6f1611b560ef80860ce62564534a53a").into(), - }, - sync_aggregate: SyncAggregate{ - sync_committee_bits: hex!("ffffffff"), - sync_committee_signature: hex!("80628825caef48ef13b4ffda96fcee74743609fe3537b7cd0ca8bc25c8a540b96dfbe23bdd0a60a8db70b2c48f090c1d03a25923207a193724d98e140db90ab692cd00ec2b4d3c0c4cbff13b4ab343e0c896e15cba08f9d82c392b1c982a8115").into(), - }, - signature_slot: 185, - next_sync_committee_update: None, - finalized_header: BeaconHeader { - slot: 168, - proposer_index: 1, - parent_root: hex!("bb8245e89f5d7d9191c559425b8522487d98b8f2c9b814a158656eaf06caaa06").into(), - state_root: hex!("98b4d1141cfc324ce7095417d81b28995587e9a50ebb4872254187155e6b160c").into(), - body_root: hex!("c1a089291dbc744be622356dcbdd65fe053dcb908056511e5148f73a3d5c8a7e").into(), - }, - finality_branch: vec![ - hex!("1500000000000000000000000000000000000000000000000000000000000000").into(), - hex!("10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7").into(), - hex!("c2374959a44ad09eb825ada636c430d530e656d7f06dccbcf2ca8b6dc834b188").into(), - hex!("97a3662f859b89f96d7c6ce03496c353df5f6c225455f3f2c5edde329f5a94d1").into(), - hex!("4c4720ad9a38628c6774dbd1180d026aceb0be3cb0085d531b1e09faf014328a").into(), - hex!("c3d59497774f8f1c13fda9f9c6b4f773efabbac8a24d914dc7cf02874d5f5658").into(), - ], - block_roots_root: hex!("dff54b382531f4af2cb6e5b27ea905cc8e19b48f3ae8e02955f859e6bfd37e42").into(), - block_roots_branch: vec![ - hex!("8f957e090dec42d80c118d24c0e841681e82d3b330707729cb939d538c208fb7").into(), - hex!("4d33691095103fbf0df53ae0ea14378d721356b54592019050fc532bfef42d0c").into(), - hex!("bc9b31cd5d18358bff3038fab52101cfd5c56c75539449d988c5a789200fb264").into(), - hex!("7d57e424243eeb39169edccf9dab962ba8d80a9575179799bbd509c95316d8df").into(), - hex!("c07eeb9da14bcedb7dd225a68b92f578ef0b86187724879e5171d5de8a00be3a").into(), - ] - }) -} - -pub fn make_execution_header_update() -> Box { - Box::new(ExecutionHeaderUpdate { - header: BeaconHeader { - slot: 166, - proposer_index: 7, - parent_root: hex!("da28a205118aaf4aa69a3fb4eb7a565541b7172cc77771ec886b54b6f5bc10f3").into(), - state_root: hex!("179a6249c3e86ebcc9c71a0fc604a825a5fb5dd1602177681c54dc7e09af4265").into(), - body_root: hex!("e1290e50d64f043594bcac66824b04eb3047ae4174188e40106235183f47074c").into(), - }, - ancestry_proof: Some(AncestryProof { - header_branch: vec![ - hex!("bb8245e89f5d7d9191c559425b8522487d98b8f2c9b814a158656eaf06caaa06").into(), - hex!("ab498fef414d709fcac589217246527b82c25706fa800f21d543c83a0ccc59a2").into(), - hex!("de054acbf636cf9f1692137f78179381b5b0328b49fbc4d324eb8e897b41f52c").into(), - hex!("c472fde1df644788c92208467ff5aad686ce55bab1570917e8de1922296666fd").into(), - hex!("11a71c67676c72696c452d875318501cd50968106f72fb611bfe5952285516f8").into(), - hex!("6d2bd2b6cd84ddadc89df27f3f7f9141bb88c742a30123c77eefebef9d5f6667").into(), - ], - finalized_block_root: hex!("be7d9cc4483ed0065fc7c32e2a783ca3782d8dbd7bfe899fd7c0bcee82f11629").into(), - }), - execution_header: ExecutionPayloadHeader { - parent_hash: hex!("96b27b6e0919c19a70c4a2f7136fd59d2e63a3ba0453a86775add3f2dd681cea").into(), - fee_recipient: hex!("0000000000000000000000000000000000000000").into(), - state_root: hex!("b847ee60946ebdb5bd92c22385da44b8a9aea4c6779f1a1402cc06e22b76fb4a").into(), - receipts_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), - logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), - prev_randao: hex!("e6c6665aa502e12dd8f5937da2eb7f7fe7a78bc9a900c9321412b8ddd4d72325").into(), - block_number: 166, - gas_limit: 68022694, - gas_used: 0, - timestamp: 1704700423, - extra_data: hex!("d983010d05846765746888676f312e32312e318664617277696e").into(), - base_fee_per_gas: U256::from(7_u64), - block_hash: hex!("1871ded7b2b8b4b5b358c904104704811b15aeefc24e49daa2a1a68176d6553a").into(), - transactions_root: hex!("7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1").into(), - withdrawals_root: hex!("28ba1834a3a7b657460ce79fa3a1d909ab8828fd557659d4d0554a9bdbc0ec30").into(), - }, - execution_branch: vec![ - hex!("276d006ecfe51451787321ef00417b194e90b35d4106bd7d51372f39918a4531").into(), - hex!("336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e").into(), - hex!("db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71").into(), - hex!("6b8ff91d1be713c644ef9b3e5b77323897930c342fd12615c0d2142bee5cd1d7").into(), - ], - }) -} diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/mainnet.rs b/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/mainnet.rs deleted file mode 100644 index 3d22ad82cec0a88aa4cd0c6ff9a1d6d7c6141165..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/mainnet.rs +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -pub const SLOTS_PER_EPOCH: usize = 32; -pub const SECONDS_PER_SLOT: usize = 12; -pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: usize = 256; -pub const SYNC_COMMITTEE_SIZE: usize = 512; -pub const SYNC_COMMITTEE_BITS_SIZE: usize = SYNC_COMMITTEE_SIZE / 8; -pub const SLOTS_PER_HISTORICAL_ROOT: usize = 8192; -pub const IS_MINIMAL: bool = false; -pub const BLOCK_ROOT_AT_INDEX_DEPTH: usize = 13; diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/minimal.rs b/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/minimal.rs deleted file mode 100644 index affa86db976143c70773e017ee4fc14337aa322e..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/config/minimal.rs +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -pub const SLOTS_PER_EPOCH: usize = 8; -pub const SECONDS_PER_SLOT: usize = 6; -pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: usize = 8; -pub const SYNC_COMMITTEE_SIZE: usize = 32; -pub const SYNC_COMMITTEE_BITS_SIZE: usize = SYNC_COMMITTEE_SIZE / 8; -pub const SLOTS_PER_HISTORICAL_ROOT: usize = 64; -pub const IS_MINIMAL: bool = true; -pub const BLOCK_ROOT_AT_INDEX_DEPTH: usize = 6; diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/parachain/pallets/ethereum-client/src/mock.rs deleted file mode 100644 index 217d37db8dfa77c6725f3e082206795e63bfc62c..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/src/mock.rs +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -use crate as ethereum_beacon_client; -use crate::config; -use frame_support::parameter_types; -use hex_literal::hex; -use pallet_timestamp; -use primitives::{CompactExecutionHeader, Fork, ForkVersions}; -use snowbridge_core::inbound::{Log, Proof}; -use sp_core::H256; -use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; -use std::{fs::File, path::PathBuf}; - -#[cfg(feature = "beacon-spec-minimal")] -const SPEC: &str = "minimal"; -#[cfg(not(feature = "beacon-spec-minimal"))] -const SPEC: &str = "mainnet"; - -fn load_fixture(basename: String) -> Result -where - T: for<'de> serde::Deserialize<'de>, -{ - let filepath: PathBuf = - [env!("CARGO_MANIFEST_DIR"), "tests", "fixtures", &basename].iter().collect(); - serde_json::from_reader(File::open(filepath).unwrap()) -} - -pub fn load_execution_header_update_fixture() -> primitives::ExecutionHeaderUpdate { - let basename = format!("execution-header-update.{}.json", SPEC); - load_fixture(basename).unwrap() -} - -pub fn load_checkpoint_update_fixture( -) -> primitives::CheckpointUpdate<{ config::SYNC_COMMITTEE_SIZE }> { - let basename = format!("initial-checkpoint.{}.json", SPEC); - load_fixture(basename).unwrap() -} - -pub fn load_sync_committee_update_fixture( -) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { - let basename = format!("sync-committee-update.{}.json", SPEC); - load_fixture(basename).unwrap() -} - -pub fn load_finalized_header_update_fixture( -) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { - let basename = format!("finalized-header-update.{}.json", SPEC); - load_fixture(basename).unwrap() -} - -pub fn load_next_sync_committee_update_fixture( -) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { - let basename = format!("next-sync-committee-update.{}.json", SPEC); - load_fixture(basename).unwrap() -} - -pub fn load_next_finalized_header_update_fixture( -) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> { - let basename = format!("next-finalized-header-update.{}.json", SPEC); - load_fixture(basename).unwrap() -} - -pub fn get_message_verification_payload() -> (Log, Proof) { - ( - Log { - address: hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), - topics: vec![ - hex!("1b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ad").into(), - hex!("00000000000000000000000000000000000000000000000000000000000003e8").into(), - hex!("0000000000000000000000000000000000000000000000000000000000000001").into(), - ], - data: hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000").into(), - }, - Proof { - block_hash: hex!("05aaa60b0f27cce9e71909508527264b77ee14da7b5bf915fcc4e32715333213").into(), - tx_index: 0, - data: (vec![ - hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb").to_vec(), - hex!("d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c185510").to_vec(), - hex!("b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646").to_vec(), - ], vec![ - hex!("f90131a0b601337b3aa10a671caa724eba641e759399979856141d3aea6b6b4ac59b889ba00c7d5dd48be9060221a02fb8fa213860b4c50d47046c8fa65ffaba5737d569e0a094601b62a1086cd9c9cb71a7ebff9e718f3217fd6e837efe4246733c0a196f63a06a4b0dd0aefc37b3c77828c8f07d1b7a2455ceb5dbfd3c77d7d6aeeddc2f7e8ca0d6e8e23142cdd8ec219e1f5d8b56aa18e456702b195deeaa210327284d42ade4a08a313d4c87023005d1ab631bbfe3f5de1e405d0e66d0bef3e033f1e5711b5521a0bf09a5d9a48b10ade82b8d6a5362a15921c8b5228a3487479b467db97411d82fa0f95cccae2a7c572ef3c566503e30bac2b2feb2d2f26eebf6d870dcf7f8cf59cea0d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c1855108080808080808080").to_vec(), - hex!("f851a0b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646a060a634b9280e3a23fb63375e7bbdd9ab07fd379ab6a67e2312bbc112195fa358808080808080808080808080808080").to_vec(), - hex!("f9030820b9030402f90300018301d6e2b9010000000000000800000000000020040008000000000000000000000000400000008000000000000000000000000000000000000000000000000000000000042010000000001000000000000000000000000000000000040000000000000000000000000000000000000000000000008000000000000000002000000000000000000000000200000000000000200000000000100000000040000001000200008000000000000200000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000f901f5f87a942ffa5ecdbe006d30397c7636d3e015eee251369ff842a0c965575a00553e094ca7c5d14f02e107c258dda06867cbf9e0e69f80e71bbcc1a000000000000000000000000000000000000000000000000000000000000003e8a000000000000000000000000000000000000000000000000000000000000003e8f9011c94ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0f863a01b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ada000000000000000000000000000000000000000000000000000000000000003e8a00000000000000000000000000000000000000000000000000000000000000001b8a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000f858948cf6147918a5cbb672703f879f385036f8793a24e1a01449abf21e49fd025f33495e77f7b1461caefdd3d4bb646424a3f445c4576a5ba0000000000000000000000000440edffa1352b13227e8ee646f3ea37456dec701").to_vec(), - ]), - } - ) -} - -pub fn get_message_verification_header() -> CompactExecutionHeader { - CompactExecutionHeader { - parent_hash: hex!("04a7f6ab8282203562c62f38b0ab41d32aaebe2c7ea687702b463148a6429e04") - .into(), - block_number: 55, - state_root: hex!("894d968712976d613519f973a317cb0781c7b039c89f27ea2b7ca193f7befdb3").into(), - receipts_root: hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb") - .into(), - } -} - -#[cfg(feature = "beacon-spec-minimal")] -pub mod minimal { - use super::*; - - use sp_runtime::BuildStorage; - - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Test { - System: frame_system::{Pallet, Call, Storage, Event}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - EthereumBeaconClient: ethereum_beacon_client::{Pallet, Call, Storage, Event}, - } - ); - - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; - } - - impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type OnSetCode = (); - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type MaxConsumers = frame_support::traits::ConstU32<16>; - type Nonce = u64; - type Block = Block; - } - - impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = (); - type WeightInfo = (); - } - - parameter_types! { - pub const ExecutionHeadersPruneThreshold: u32 = 10; - pub const ChainForkVersions: ForkVersions = ForkVersions{ - genesis: Fork { - version: [0, 0, 0, 1], // 0x00000001 - epoch: 0, - }, - altair: Fork { - version: [1, 0, 0, 1], // 0x01000001 - epoch: 0, - }, - bellatrix: Fork { - version: [2, 0, 0, 1], // 0x02000001 - epoch: 0, - }, - capella: Fork { - version: [3, 0, 0, 1], // 0x03000001 - epoch: 0, - }, - }; - } - - impl ethereum_beacon_client::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ForkVersions = ChainForkVersions; - type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold; - type WeightInfo = (); - } - - // Build genesis storage according to the mock runtime. - pub fn new_tester() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - let _ = ext.execute_with(|| Timestamp::set(RuntimeOrigin::signed(1), 30_000)); - ext - } -} - -#[cfg(not(feature = "beacon-spec-minimal"))] -pub mod mainnet { - use super::*; - - type Block = frame_system::mocking::MockBlock; - use sp_runtime::BuildStorage; - - frame_support::construct_runtime!( - pub enum Test { - System: frame_system::{Pallet, Call, Storage, Event}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - EthereumBeaconClient: ethereum_beacon_client::{Pallet, Call, Storage, Event}, - } - ); - - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const SS58Prefix: u8 = 42; - } - - impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type OnSetCode = (); - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type MaxConsumers = frame_support::traits::ConstU32<16>; - type Nonce = u64; - type Block = Block; - } - - impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = (); - type WeightInfo = (); - } - - 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, - }, - }; - pub const ExecutionHeadersPruneThreshold: u32 = 8192; - } - - impl ethereum_beacon_client::Config for Test { - type RuntimeEvent = RuntimeEvent; - type ForkVersions = ChainForkVersions; - type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold; - type WeightInfo = (); - } - - // Build genesis storage according to the mock runtime. - pub fn new_tester() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - let _ = ext.execute_with(|| Timestamp::set(RuntimeOrigin::signed(1), 30_000)); - ext - } -} diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/execution-header-update.mainnet.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/execution-header-update.mainnet.json deleted file mode 100755 index 97d498e2d9ec7800d7a5161d1c377957dc625779..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/execution-header-update.mainnet.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "header": { - "slot": 4058654, - "proposer_index": 1039, - "parent_root": "0x758fa0a54047c0221b3107423e78b8910514ba7a0c4250401dac77108dce2ff8", - "state_root": "0x53c79320b4b7649e39a9f07903f4fc68a7cfe7a81cb2e30293dd7b00ee13f69a", - "body_root": "0xa76a7f037f12ee070dcb273d9787b13ff0cf17420a39564a101789318a157ec3" - }, - "ancestry_proof": { - "header_branch": [ - "0xbb6cfda1e02c3117d9fa41c1e1594ce0645352a073ad8b474f83c148e0d9954f", - "0xb44967bd8ff799126cad993ff7369f7a28d60281304d0c7c058e6721b6ce4c61", - "0x0009710ef2467f95542c2e8e7a7249282c08f62aedbe7d21ae4a7af04e1a890d", - "0x7bc5dc20638cfe1cfd5d2547c3efbed4cfb533c76c27d41f723ed876ab9edc3a", - "0x6dcd59a8a041cf2591921417cb8133f0ff5af9bfe1f1ab491e75a751adc9c9e1", - "0x3f25a2852042c7bf2c421bf16c0b9373c1d27ccb1eca4088f8191712481261be", - "0xd1f9cfe1f04031459e5c45f47b2a49c780cabb8ca5b8d76461db2b88c337ed76", - "0x8c37e3119bf7d3cb8208e832669261b0eda1b3c7b11d321760ba1c0649f61416", - "0x1783d3b7041ecb78d9e1f913b82bd335229ca1f2cc5d3cc8dd28c94152e887b4", - "0xe39fc366c951c7cb2100308b0a9417581c46812a7d7e89f516c19863f7686f96", - "0x3c29b6d9b8715dbe5d6831364229efc91ad56ae69e93cb119d5ef4bdc53fcb37", - "0x3cf6fe5e8d91dbaa77e6befa81e04fb25a3e089317d83ff35fc8adfbbf2aab5a", - "0xb41c97cf3b1b4b5bf2999dfb1b3eaeac5c1b12205e9ab0809988e3779d119047" - ], - "finalized_block_root": "0xb4802863fc1d32778211ce6aac8109c73c516a003213f4f5333c80472d08fe4e" - }, - "execution_header": { - "parent_hash": "0x9cffcba69c88a619483e13864704dde5db80e05f8d49018f615395ce09cd24ab", - "fee_recipient": "0xff58d746a67c2e42bcc07d6b3f58406e8837e883", - "state_root": "0x6a4aafc93626778475a416721104229035e91f0db788d0099b57e756cd272f0a", - "receipts_root": "0xe1ce670bdcf9acf4c62fee845cd7e81eabbb6db9ddbff56130020c6cf999a45d", - "logs_bloom": "0xa000980408008000328c1458805c005048b84812c134200090b40428568108a0648090105085100301844800090802484420800d020282019002040d083004031a20420240a4a8900a43296938c040802040220170860210910020036b8c482228c004440300021c82c0400110402a10800582424001c00000828310210020a18130504020790dca716194100880ea5501149104002bc05e189080901c4001010e0a040658a410072021230a5224265030082404000aa11f28162e216636000408842103d41010760972060060000500c20130b000000065401483200081a84934f020020120009618002269c884724616040000840e18080300024490000230", - "prev_randao": "0x5d9ac7ea788ecb534e98bc9079fa0bef199011dadadedaee5dc40e2cd702d664", - "block_number": 5025098, - "gas_limit": 30000000, - "gas_used": 13802943, - "timestamp": 1704437448, - "extra_data": "0x476f65726c69205365706f6c69612d4265706f6c696120513966", - "base_fee_per_gas": 19959019915, - "block_hash": "0x795021134c2b7f9c00b498ff7b0971dbbe061561868f702d8ca68a05e5eb5a99", - "transactions_root": "0x3cb7c92fde5d511cc90cd67e375c4388794f4c01e375e8e8a06d003b5593fd12", - "withdrawals_root": "0x5f5155fd8e5cd24b7ecb1e039792b0caff01dfda2990786d9ffc88325b5d1ea8" - }, - "execution_branch": [ - "0x85ff3d1c2bc3dcdd5543bcd29a1224c6a8c24875224fbb1e3b69f0515ffaacda", - "0x336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e", - "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", - "0xfe1fa7acd413c54ebd394126ade19fc624ad9e4c60792d54ff3d60b07076b76d" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/execution-header-update.minimal.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/execution-header-update.minimal.json deleted file mode 100644 index 3e17c14f4adbf38d4c57919bb91f9574bf515cd3..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/execution-header-update.minimal.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "header": { - "slot": 3622, - "proposer_index": 7, - "parent_root": "0x254c9215f6cce83e21b9776afb482181639602d3cb58cf99452a6a4a4f603930", - "state_root": "0xea98df6d30817d63f3e54ea118e2b1ba8675753c72dec1661c503d4eb43f9bdd", - "body_root": "0x765a0616a31d38e0ca2d10f6e8b234dd3d07e16aa929bcbc4de775c93f1972fd" - }, - "ancestry_proof": { - "header_branch": [ - "0x7690506882ac8c5f01d00f3ade06439259a3a0261ef5d61ec44920678b4104e6", - "0xf01aa0fdd7c9ef7b1affb7854fe8cbcc5c70643ee5b83e032faa702a0675a8cb", - "0x273a7b300b75ffa2c765af50680aa836299264f2107f38010278822313181801", - "0x30fe73a3bae6a31af32656ab759a4b67d27a213e01012b96cc4fedd0f2e77c75", - "0x7246cb3a35f13a1f0bbf907887985bb5382c45f2aa1699dbca48a0a82d5330af", - "0x5e7270e88a22dd4a905b2e76da2c8c358baeddd34de6c64a71bb1c80070ab717" - ], - "finalized_block_root": "0xa6fdc5df11c1759d11c9f0353a666715e5677e9ffd7d414e44cff0970553f1c9" - }, - "execution_header": { - "parent_hash": "0x6c9657f1267ad6040ea017ff6d02b55c4ba25cb092b8326d321dd98d01d1ee64", - "fee_recipient": "0x0000000000000000000000000000000000000000", - "state_root": "0x01f975f7cdff9b0a8844304aa59062fe18af0fef4636539312dfe20d238600ba", - "receipts_root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "prev_randao": "0xcdfcab74bc26b3f4311afdc72d2d21d33a4b045187a01fa208a9d687a6d1d25c", - "block_number": 3622, - "gas_limit": 30000000, - "gas_used": 0, - "timestamp": 1685722543, - "extra_data": "0xd983010b02846765746888676f312e31392e358664617277696e", - "base_fee_per_gas": 7, - "block_hash": "0x38c80e0e26cb80730df627d32f50266bd0fe32fb12b7606300ad81aa2b4033db", - "transactions_root": "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1", - "withdrawals_root": "0x28ba1834a3a7b657460ce79fa3a1d909ab8828fd557659d4d0554a9bdbc0ec30" - }, - "execution_branch": [ - "0x005b8d55b34b4323bfd4773c28b09eb53bc87959e65411ccd23728c7e42d5ff2", - "0x336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e", - "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", - "0x7061330dada1ba1c602ba98f647a441885460ed0db00483fea1282385dfab84b" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/finalized-header-update.mainnet.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/finalized-header-update.mainnet.json deleted file mode 100755 index 49434dee72d316ebe47403e4e77a02bc87cc7cfc..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/finalized-header-update.mainnet.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "attested_header": { - "slot": 4058720, - "proposer_index": 1088, - "parent_root": "0x37c7398a391c71da07258b48f41d4caaaf891fdf558e110b9c25db716fa8ff55", - "state_root": "0x6cf68a66c993f1c27f499e054a5fbd7c0e625c34ea0057fda9691c561e990002", - "body_root": "0x109e4c0c647d42dba0c8c6569997c51b5bcfc1a33475b1433ded6353f48423ff" - }, - "sync_aggregate": { - "sync_committee_bits": "0xffffffffff7bfdfffff7fffff1effffef7f9ffbdfdffffffdffbfffbbf7ffffffffffefdf7ffbeffdffef7bfffffffffffbffffffffffbffffffffffffffdfda", - "sync_committee_signature": "0xb5727bad1db101b6f0fd7ec4cb79b43dae90366fe0cae31a62439388eb03ebd75732cee3bde51f814203a4c0f5b23898120cd0870f0e662bdf0f85a048b534d9a906e6d32a65df019059d34f724221075734ec2849c09679febecf6929934f09" - }, - "signature_slot": 4058721, - "next_sync_committee_update": null, - "finalized_header": { - "slot": 4058656, - "proposer_index": 810, - "parent_root": "0xbb6cfda1e02c3117d9fa41c1e1594ce0645352a073ad8b474f83c148e0d9954f", - "state_root": "0xf938f0f0cba85234afbaace20545134c70b35e6ff9f74d944b0fea309109f3cf", - "body_root": "0xcf6a7a6f653cb64b2b910278a478339b34eb08abf00e0766d10c7a8fe9bdb139" - }, - "finality_branch": [ - "0x71ef010000000000000000000000000000000000000000000000000000000000", - "0x3d4be5d019ba15ea3ef304a83b8a067f2e79f46a3fac8069306a6c814a0a35eb", - "0xa2e2a28c0bb0ad56c25f3c461a4bfe4f3b3b894bc0105a62e85f43a4ae1adc3f", - "0xaa2bad8cf9b0433d3d79bc5b95c067048b018d1d2ffd0c66db6e7cf86e0a314e", - "0x027f238235d07ac9757543c19deabe1d553d6fc110e8bea4b037b1fab263b4dc", - "0x7dfa1cc1907e8927295f78a770bf86b28f5c2bddcac38beb3b4beb265ff5608f" - ], - "block_roots_root": "0x4a5a57fb0769443f6472f59dbb78d7a9a69ff61d09aa89d5da645d634ec46a14", - "block_roots_branch": [ - "0xa09ad7f3afd681bb6fd54abba339549f3b601beedea79a5b7d448b1cf1e1613e", - "0xacdfd4d5eba154f118a84af7a2d17ba6100fcd9c24fc235e927f584a5b56e32f", - "0x36ca106eac009ed605e680415b105b3a6591830c38034511f300aae44802235f", - "0x171561a9e1afb413132d0f4d3cbbd810766c151be32ec9f2d609f40aef9e2588", - "0x7cd963eba3fb57ee5ce37ee4b621d24fc14d642e32f48dc48ade528e11458ab9" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/finalized-header-update.minimal.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/finalized-header-update.minimal.json deleted file mode 100644 index c6473529b10c6d32398e55e11a2f71cbdc50b279..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/finalized-header-update.minimal.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "attested_header": { - "slot": 3640, - "proposer_index": 5, - "parent_root": "0xf062fcec9c3379a08e6add37a834b1e39af395fc343973e44957ecebbf2ecddd", - "state_root": "0xb1581cb62fe376e305e02f26463153f5dfb804d8df97ef40fc315c1bc30731ba", - "body_root": "0x98461abcc6d130b7bcb9430292c8a269ea9f01082685347e2968d892f716067c" - }, - "sync_aggregate": { - "sync_committee_bits": "0xffffffff", - "sync_committee_signature": "0x925c6e4b67890a7e28a7ca19853f88247e92014b9d233ac9058efd4f3827f0055db308debe17596e635b93727b5a851e1366ca801f30b03fdec722f45011504702a27646488b5ab5e3428fe7b4d4a50132f374612f66e45d68db27c568f96f08" - }, - "signature_slot": 3641, - "next_sync_committee_update": null, - "finalized_header": { - "slot": 3624, - "proposer_index": 7, - "parent_root": "0x7690506882ac8c5f01d00f3ade06439259a3a0261ef5d61ec44920678b4104e6", - "state_root": "0x3726ebb8d9973977a71a8389caf5fc5830eeb8cd4fdfbbc7b0c4e6ca3e6a4090", - "body_root": "0x0f9a3f0fa5a4ffaf7c10504c86f23e7d554366ffd069fe958a160b253c3fd409" - }, - "finality_branch": [ - "0xc501000000000000000000000000000000000000000000000000000000000000", - "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", - "0x83c3d5360d254f4a44be712c1f433e88e810b6d1e0e789e90bada9e36126b857", - "0x97245fa01a89a6d7b4542cd731fef699f58b2bbaabdd6f641334c9e9eeae3a20", - "0xc3d19c773f66ab94bc2106d5e75a3205398dd6e94b6f8a5716f347741eb9fc5a", - "0x9e5040e56d765c1add56779a716be7497be27cba37f866cd8d34418d55e48715" - ], - "block_roots_root": "0x29a54625749fa25f9e36df14a3baa335c58246bba2f8c7eb8b1ec2e4908e2fd0", - "block_roots_branch": [ - "0x53616f9298818a8423c98adc47c92aaf82f0c5c911dc4ee5f88ba6d3022341c1", - "0x5d2f1c4bce6f63f26cbe3fbf480281c04a6b14bea74350a88ee945354ecbd79d", - "0x8333eefc7eaa4d10091e2014b3aae2bf6bd2d10c22c67100e189f8ab6caab261", - "0x3edfa69130bc193dec47c27a5903f03d5262b75899b69c0e95ac1816a664a3e7", - "0x5e046000f85aede8d4c28140b27778488d4ad21b1e16e345055d07ee53f2711b" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/initial-checkpoint.mainnet.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/initial-checkpoint.mainnet.json deleted file mode 100755 index 1f0f837f596caedeb2e1044640a2df89d431e3b9..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/initial-checkpoint.mainnet.json +++ /dev/null @@ -1,542 +0,0 @@ -{ - "header": { - "slot": 4058624, - "proposer_index": 631, - "parent_root": "0x4abadda13b61df8d40fcb061d1ec15d0fb7bc5144252bb54c57b41893b642bf3", - "state_root": "0x6fe9fa01a04dcdeaa64eba04a72fa6dbadc8596dc5ec1fff2ef343520fe722e1", - "body_root": "0x6cd83df0ee7669e06e64496a1d2be083bc3bb9f2da17e9b1e62979d200328106" - }, - "current_sync_committee": { - "pubkeys": [ - "0xa13bf1fc1826b61cceefcc941c5a4865cefdfa6c91e5223308fa6a0aa6e7b13a0499a63edf5d9fff48fdeae83e38dcbf", - "0xb3285148b91dab139b053442bdd14d627ba1e1250fe469f0f2df854b6e6ff4a18671ae3879ec9f7d8091f99f092162e9", - "0xb00d95908e72c6051478a422eb2231b5f797c2fa5c696ed1e6b9c9996ba1d8236f512443f18c01ce63312c38fa383fd4", - "0xa866633b4293e726accf6e97ac90c1898cac83e8531a25b50ae99f0ecb477a692e6a5f2488447ccd83ed869ab5abc406", - "0xa308ed8737b3a9346ff20dc9f112efccc193472e6fde6aa218ceae11e288bbd2c35fa45c1d8bb238696a96767cd68b46", - "0x81534e2a182da0c6831479c7e722953d267ba9c63a204ac96a178b1dc90d0a6ba8737002688ba5f102eda5669249f114", - "0x87c5670e16a84e27529677881dbedc5c1d6ebb4e4ff58c13ece43d21d5b42dc89470f41059bfa6ebcf18167f97ddacaa", - "0xb37334c41a3456b73b61d0eb0777260af9c2e400bbec0e0c0fdb45c39ce0dd19f021d9760f35da801f20486c6be30e9e", - "0xab73a043ccdfe63437a339e6ee96ef1241264e04dd4d917f6d6bc99396006de54e1e156d38596ba3d15cb1aaa329f8f5", - "0x8983fdebbeba6e3cc3ee1c9feb24faaeee712356975e359b0ddca3f7c9c7448132d665f54a4629002252d3fcf375f7b0", - "0xa03c1e287ccc4d457f5e71e9dc769294835945561e6f236ac7de210d2e614eee8a85e21dfb46e2143c68de22ccee8660", - "0x8e6b888197010ebadd216da35b9716daa8675d93b3c33a96a19fd9ca42624f6b430b2ff115cd0f5b717341605dda24bf", - "0x8fb51e3ef3c1047ae7c527dc24dc8824b2655faff2c4c78da1fcedde48b531d19abaf517363bf30605a87336b8642073", - "0x893272a63650b08e5b8f9b3f17f8547b456192ad649c168bafd7166b4c08c5adf795c508b88fd2425f7be8334592afb2", - "0xa90c42266ca0a65976fb4dc18465b0a44a63ed3b2747cae74e46e3ccf158f98384e2e86c852e7c5556b083b3ded9d243", - "0xb67c621d9b6313a9f6744dfcdd77d4e9cb4bd413fb5e3199cdcd7f675fc39f1ba492860749bfddf98f4088756e844a98", - "0xa9760afaa51002be0948acf7aebd90ec4e60e0dba8456e445aea93408a0468b62bb6da4984b92f8f6061561c9d56f4c4", - "0x981b2d7c56ff38f1d02c5d7a7f8bfe71daaf94d48c3bc93e8083a0a23c1ae1ff05f90312deb09b35d4513c1ffa573d86", - "0x9515dedf061e654d58a43e4e525a63ad2a6274ea6f20b1d624a6ba7d3062ed68a0226eee6951ab8464906c52ba5556b0", - "0x901f724ee1891ca876e5551bd8f4ad4da422576c618465f63d65700c2dd7953496d83abe148c6a4875a46a5a36c218cf", - "0xa8d152e5d94b75cb9e249230db21af31de4d4f3d4ef60ccbf2212babf69aed2a38435a993ee2f13cca410ad55a4875ab", - "0x875ebfe737cea438e967d70ceaffb4360cce28ecc76c8c4ee612c47fb6b3e89af03c66981571066107323f49a6242772", - "0xa798a0371e8cc4dc42ccd79934b0db5a3a59f18a0ae09f2eb172596428fcb3f00312e783d6fd21cbc1610317f44e08cb", - "0x99c629c9cd603a9344b04d22d2bcc06cf45ebf62d97f968df19c73c7a50f4f6a2a2cc7fb633f509f961edfb94fbab94e", - "0x854410e6fb856da8b997ebf28ae2415ce6e1f9f6a4579fad15b5df61709c924a925397b33fe67c89ffad6143a39d756a", - "0xa3969926aa2e52f1a48ac53074b764648b4c71bd43430944679628463cd68398f700d874c14503b53756be451c8ba284", - "0x8ee8873de7cd28a54ba2c63a80b63399effed76b154e96ed26e7c0668b9f2476e298688b6a00c4b2ab9d020a897695d7", - "0xad54241ba3de6a4426c788690d3f78d2eb678814edc49d3fb988d7fc752e43512972567bb384bcc1b18d083d15e376da", - "0x942bee9ee880ac5e2f8ba35518b60890a211974d273b2ae415d34ce842803de7d29a4d26f6ee79c09e910559bdcac6d3", - "0x96b478b1e5e49d4ea3fd97c4846ae0f781dcc9f9ff61ee022ca92c3d8dfba89c513c46e8bb38b73e6b678a79b9b12177", - "0xaad9577501d7f3a5dbac329f2c1fe711710869cc825740f365488fc55a278d687bb72423560f7cb2cbd60546a82ea1e6", - "0x8aa3d9dad1c122b9aed75e3cc94b3a9dab160fa4cad92ebab68a58c0151a5d93f0f6b40b86fba00e63d45bd29a93b982", - "0xa12fc78b8d3334a3eb7b535cd5e648bb030df645cda4e90272a1fc3b368ee43975051bbecc3275d6b1e4600cc07239b0", - "0xab01a7b13c967620d98de736b8ff23d856daa26d5cd8576993ee02a5d694332c0464ed018ebffcd5c71bab5cada850ce", - "0x96cf5760c79cfc830d1d5bd6df6cfd67596bef24e22eed52cee04c290ad418add74e77965ea5748b7f0fb34ee4f43232", - "0x9443e6ba4400fb3370c573cd7e33f05e1475f9cf1d6adb905bee3aff8f1452d8d384c8a72c9110070f35c6aad940bba6", - "0x95c60b5561e53cfc26d620be90f84199ffd6dd9687c1be3a547048e7cba10a0be9bb6da000e7521cbd488d0901d48ee9", - "0xab69cf79750436d310dc3c5e96c2b97003f4394f31dfa8a9ac420595dc7b4d96dad5787d93347ba2bc6f196c241a3dbf", - "0x9145ee1fb6e84114c903819db94fa5a72bcbc15fcb8a7fd8eefba23b156cc46309281dcf78b48a2847b3754f7d7d7a79", - "0xb118f77f99ac947df97e7682f0fb446175185b842380af4ee7394531e4f93002c72b41a57a7c1b923a4f24b10924c84f", - "0xa4aabd1890ebf35423565dbff3477a09eea4e35f5a26ed449eab38e0a21fb89e9ddfe3a2003cddc457db648a1b5891a3", - "0x8ff5d2e6c98b1fea70cb36ea8ed497fd1233b9418948ac58c6c379ed35fb10f8253ef188c909d5e77e81b5b8e2a4ad17", - "0xa23f076306c120dccf69d7d2ac7f83a377a72d35bf448f88feff8b6dba9307fdabf34452e30b87407b2258b9edfd1174", - "0x87d2217eb05d657aba7b048cf3c661b463e78e51135a5b937e71975ff5102e596434720f02349c73415decb88418cb0d", - "0x8bb51b380a8a52d61a94e7b382ff6ce601260fa9b8c5d616764a3df719b382ec43aec9266444a16951e102d8b1fb2f38", - "0xb0053550040ab3a3996cba5caf9ad5718867b5f5df273ed8c6520761571f03a94e50b5f8a6a8c42d725383cce97d3cae", - "0xae50f93230983a82e732903d6ed50a506d678f35b6b4a4b3686a92b12aeb9d34cb095e8562b0900125bbced0359b37de", - "0x8bfa106ada4914419bf1d8900c5981dd5b90c3023196d7e918d62879fc3a575bd0a25f939366f7fd2240df6108b069ec", - "0xb3a5497365bd40a81202b8a94a5e28a8a039cc2e639d73de289294cbda2c0e987c1f9468daba09ea4390f8e4e806f3c8", - "0xb4f583e10aa9af79b4ebd647e0fffe1c720112727e5ffac4313f236737491fceeee194537786c561cd5777b453e5b03c", - "0x826be957cf66db958028fa95655b54b2337f78fb6ef26bd29e2e3a64b130b90521333f31d132c04779e4b23a6b6cd951", - "0xaace45334070c51cc8b3579598d4cd8cda2153bba51f56e3b1fe5e135c83ef70503c322756b9cad9d3cd28f1ecfc8227", - "0x94bb68c8180496472262455fd6ab338697810825fa4e82fc673f3ac2dacfd29ee539ac0bfe97eb39d4ef118db875bab6", - "0xb560c33950a355119845f63defb355807e56773f636fb836f7746155fad070e384fc1091b8e5c057e4cbc7da9275ecf7", - "0x820cc2ac3eed5bce7dc72df2aa3214e71690b91445d8bb1634c0488a671e3669028efbe1eae52f7132bde29b16a020b7", - "0x9244703338879e3ea00663dcde8f11095de3e38df9277d8c2acc26e72021c222ae40bcc91228789fdf0b69acc3144783", - "0x8d474636a638e7b398566a39b3f939a314f1cf88e64d81db0f556ca60951ec1dca1b93e3906a6654ed9ba06f2c31d4ea", - "0x889a5cf9315383bf64dfe88e562d772213c256b0eed15ce27c41c3767c048afe06410d7675e5d59a2302993e7dc45d83", - "0xb544c692b046aad8b6f5c2e3493bc8f638659795f06327fff1e9f4ffc8e9f7abdbf4b7f6fcdfb8fe19654d8fa7d68170", - "0xaefc682f8784b18d36202a069269be7dba8ab67ae3543838e6d473fbc5713d103abcc8da1729a288503b786baac182d3", - "0xa0f2092ac34d2363614fb2f57fc7b72db247eb1fa53f395881ce6b4aacd6fb920d6dc59507701d487288102e4c4fa389", - "0xb33de3de106be61481ccb7f07a7a63cf4d1674010e462388fb8ab5ea08f444ed7a277905207e0b3aa2f00bb9efca984f", - "0x84173aeaf3d96368dc7ca1ad5e5575da279113567e5815a364a0356a720c5e08cb58ca1fdd891924f4871d3eaae5de40", - "0xb6d6482ad7b9b412ffbefbbdcc28eb3d091b1291f54f77bdd53c4ac85f705c454940f466dc272dde7b03c26f0cd6ecb3", - "0xa2053719da2b7501dab42011ae144b3c8d72bd17493181bf3ae79a678068dc3ee2f19d29a60b5a323692c3f684f96392", - "0x8296f8caf58316af535def398a43357e48cb3b1e674b857eba1bd1b970da3dd045e22fe6d17dee4e9117f62ece3ec31c", - "0x84faf4d90edaa6cc837e5e04dc67761084ae24e410345f21923327c9cb5494ffa51b504c89bee168c11250edbdcbe194", - "0x879aea8f09dec92f354e31aa479d00cb77457d363de2d9a51ddf7d734061b6f83d6345cf33dbef22004cd23dd6c4b760", - "0xb8fca0f7bc276f03c526d42df9f88c19b8dc630ad1299689e2d52cd4717bbe5425479b13bdf6e6337c48832e4cd34bb5", - "0xb2a01dc47dd98f089f28eee67ba2f789153516b7d3b47127f430f542869ec42dd8fd4dc83cfbe625c5c40a2d2d0633ea", - "0xa19f2ce14e09ece5972fe5af1c1778b86d2ab6e825eccdb0ac368bb246cfe53433327abfe0c6fa00e0553863d0a8128e", - "0x95d1f944b0c53eb3e9fcd5632713602bbb9195b87a172a370ae2df98504612a55f3968615a39b569ce6a0fe9fb559be7", - "0xae36ab11be96f8c8fcfd75382bb7f4727511596bc08c25814d22f2b894952489d08396b458f7884d6b3c0adb69856a6d", - "0x824d0dc002e158adef06fc38d79b01553be5a3903566029cf0beddb2248b11da40e66feb168e8e3e2a63ea033a75f382", - "0x8f9f85ae6377414fcf8297ed45a736210cd3803f54f33116b0f290b853dc61e99ea08f3c422ed9bc6bdc2f42ab4f56ba", - "0x86c53fc078846c3d9bc47682506f8285ba4551475921fd388b96291741970c34b8de4210202e40d2de4acb6e2892072b", - "0x853184f246d098139230962e511585368b44d46a115c5f06ccaeef746773951bead595fb6246c69975496bac61b42a4f", - "0xb91b4260e2884bae9778fe29a2c1e4525e4663ec004159def5d47320de304c96d2a33ad7a670e05acf90cbba3efdd4d9", - "0x83492e27e07e35c0836aee6bee95d040b8d3e82db6f94a3917d07797800f7200f5dbc6c9596c6c3c70f8f470b65a9b6e", - "0xb1bb33607d10ea8c954064ecb00c1f02b446355ef73763a122f43b9ea42cd5650b54c5c9d1cfa81d4a421d17a0a451aa", - "0x99cb1728157a1b7cdd9607cf15911bbcb56b64d52fb0d0117b457853a81ec55913f977850f26e188fa2652579efe9ddf", - "0x8b7cb5b8de09a6dfceddcbaa498bc65f86297bcf95d107880c08854ed2289441a67721340285cfe1749c62e8ef0f3c58", - "0xb97447233c8b97a8654749a840f12dab6764209c3a033154e045c76e0c8ed93b89788aac5cd1e24ed4a18c36de3fbf60", - "0xb4790910e2cbef848448f24f63e9dd4a1b122cf65feecf152d5fde282ad6fcc6ea3f9cc23178baf85612020795e4b13a", - "0x81fc724846b5781f3736795c32b217458bb29972af36cc4483dd98ab91680d3d9bc18842db2661487d3a85430dc9e326", - "0xa154892ff23b284040e623bba940a6a1ef1207b8b089fc699cb152b00bcce220464502cfa1dfb5a2f62e6f3960cdf349", - "0xaf3f765fd293c253072b33a780ed68933f78d7e079d9a2079b6232755bedf6ebcbce9ba65c01f695602fa8ee17899867", - "0x97578474be98726192cb0eac3cb9195a54c7315e9c619d5c44c56b3f98671636c383416f73605d4ea7ca9fbeff8dd699", - "0x917c4fd52538d34c26ccdd816e54ebea09517712aa74cec68a2e3d759c6a69b5ccb4089ad1e0b988e916b2ce9f5c8918", - "0x8cf3c29531a17489a5f8232d56c5251ffddc95be3ff7ff61472e19fb38c5eaec841ef3b1ee36756b3dd8ff71ae199982", - "0x96d4b9b411319e531bab6af55c13f0adb1dd6b4286784ff807f283e7990dc368c16d536fc5db3d992deb4b0278914e6f", - "0x8903f7e0c9764ce844b15d84feea04406dc66b195a5f82ff4027f27361e11cf368538137d139368f5a6f42876b04f056", - "0xa4047173b5906c9b4292aaee1e91d9080ae74b1d3eb990449ed1f96bf22c3ee80f4915361e5bf7dccce24ae1618dae77", - "0xa4c4b96071e7bc92e41defba3507ddf423d93f3a94271b1f9812dfc4660e4c9fd24e0dd7aef324c46deb8d7a7c97eaa4", - "0x8289b65d6245fde8a768ce48d7c4cc7d861880ff5ff1b110db6b7e1ffbfdc5eadff0b172ba79fd426458811f2b7095eb", - "0xab4119eef94133198adb684b81f5e90070d3ca8f578c4c6c3d07de592a9af4e9fa18314db825f4c31cea1e2c7c62ed87", - "0xa3ffc3dad920d41ec3f4c39743ef571bcabb4430465d9aa811d0f0a7daa12bee4ed256527d16a6e937bf709ebb560ebd", - "0x8553748da4e0b695967e843277d0f6efeb8ba24b44aa9fa3230f4b731caec6ed5e87d3a2fcd31d8ee206e2e4414d6cf4", - "0xb15e1b4ac64bafbc4fdfead9aeff126bf102fdd125c1c914f7979680ec1715fbeccf3dc35c77d284421ec1371ed8bc32", - "0x9377aab082c8ae33b26519d6a8c3f586c7c7fccc96ec29a6f698b67d72d9266ad07378ba90d18e8c86a2ec77ecc7f137", - "0xb71c11828ecad7731136cb1f5b80392a4add8d62f8866a781fdde797a201ebf6d483b2348aacbea2061a5108933b757d", - "0x86793899ef71740ab2ec221d0085701f7909251b1cf59a276c8d629492f9ef15fc0b471beedc446a25b777391ab00718", - "0x8100b48ac2785477a123a7967bfcea8bacef59391680a411692880098a08771ff9786bd3b8dfb034cae00d5a7665621c", - "0x8b027c14affe47f83ee59b504d83b2fd2d9303de2c03ee59d169bb199d9f4bd6533d7f8c812dd7a6f1e8155e3e185689", - "0x9615800f8c95f95bf25055ae079b964e0a64fa0176cc98da272662014f57e7cd2745929daf838df0094b9f54be18b415", - "0x951aa38464912a29df2101c60771d6de7fadb63f2db3f13527f8bdacb66e9e8a97aaac7b81b19e3d1025b54e2c8facff", - "0xa0e68d24f784fcb2b71acc2d5871285623c829d0e939146b145e04908b904468a67c07a2f156e6b17bf531adc5777c4b", - "0x86a533b02ae929f67c301649a2d58651b98cdffe731b63fa32aa1013c271634bbb088c0d02865913c11bbb1bf57c0e12", - "0x81f145ebb9a5674a5b052d0e9059acc8f8ab612dd9f54d43ff620202606e19a86a9b284dc6480d555a030e5fefee8c50", - "0xa698b04227e8593a6fed6a1f6f6d1eafe186b9e73f87e42e7997f264d97225165c3f76e929a3c562ec93ee2babe953ed", - "0xb3180ded54610b1b3a2db7db539197ced6a75e9bb381d1f4b802ca7cd450f5418522ad2bee3df1956ed63ff1ffe95dc1", - "0x86fa3d4b60e8282827115c50b1b49b29a371b52aa9c9b8f83cd5268b535859f86e1a60aade6bf4f52e234777bea30bda", - "0x97d076617cf0a64ab3d1f030cfd72a303b6b252c0a7b96157ff7fc8af5970f00d14492c46e8f6f37caafe837d0dc95c7", - "0xac2c98a0ab3f9d041fc115d9be4a6c77bd2219bb4b851cbee0d9257a4de5791251735b5b8fad09c55d16eb0d97080eff", - "0xace7fda25c2fb7c18710603c16a0ff0f963352d1582a42a20c9f5603c66f485df8383465c35c31e8379b4cb2ec15b4c4", - "0xa07b35ec8d6849e95cbd89645283050882209617a3bb53eae0149d78a60dbf8c1626d7af498e363025896febdba86ee7", - "0xb2fc4478830f2ae4234569346d80b59899247c609b75bd2190a896498539e1f30dca5edbad69f0224918d09f0d7eb332", - "0x84926cf2265981e5531d90d8f2da1041cb73bdb1a7e11eb8ab21dbe94fefad5bbd674f6cafbcaa597480567edf0b2029", - "0xb5f32034d0f66bcbccefe2a177a60f31132d98c0899aa1ffff5ebf807546ff3104103077b1435fa6587bfe3e67ac0266", - "0x938206740a33d82ffda3e01598216324731335d367965aa0b740486d60ba2e86a4ecd546851046a61a4b0fc88295b5cb", - "0xad2b1ab32161e37ee553e3787f05f9281073d7ef7d0ae035daa353bc83da8ef8c76c99ad2928463c7c708f7404020476", - "0x94f4720c194e7ea4232048b0af18b8a920fde7b82869e2abcc7e14a9906530be1ef61132884bb159df019e66d83a0315", - "0xa26dd9b28564c3d95679aca03e3432ac26e287f80e870714c5946b05538b3cb43bba7b85c16bceb5430e81b7a04c1b1d", - "0x8ef0930db046c45ca5c69d565d54681d2b6d249e27092736aee582b29de3aac3fd96e1066a57cadd851b4e5334261594", - "0x92096ebf98ebac5c82345d3ef0db0f5a14af23ceea73279087426b281d6701997fe131fe65a7df7d624b4ff91d997ae8", - "0x81c850f419cf426223fc976032883d87daed6d8a505f652e363a10c7387c8946abee55cf9f71a9181b066f1cde353993", - "0x97070a33393a7c9ce99c51a7811b41d477d57086e7255f7647fd369de9d40baed63ce1ea23ad82b6412e79f364c2d9a3", - "0xa99cde5c7c85ae291c74c893e598cc0e6eb2dda2a81dbb504a638eb21dd2c41d6e5caf7baa29e3c1c32e94dca0d791f1", - "0x937ccbf8cd19b82af2755b4856cfcca3d791e33ae37e4881982ea89d3b21d205a9402d754fac63037243e699484d21f6", - "0xad7dca7640444f1268f03b67544815d4366c6a4a2f0d25ee78f3361c63095416216fd31aa0bcce7448cdd7ba73a6344e", - "0x84991ca8ef255610ebc6aff6d66ea413a768e4d3a7764750fd02b5cd4735d41df399b36e87647fc83cf73421a39d09e9", - "0x91215fc3f7243638733fe293dab7029e0c4275550102acf5f1638773cf8f8ef2c53ffa5bdfc1b602c269a2b5ab164b7a", - "0xaa6cfb3a25f4d06c3ce1e8fd87496a74a5b951ab72557472a181a2e278c5e982d290dd4facf40bd2f4f8be62263dadb0", - "0xac9f29ad08aaf27581fe1f12e210ad4ac6011507fe3100763a4120f9e439f3c6d191f3fb55aadf58bd865cfd4406c68e", - "0x87c6cb9ca628d4081000bc6c71425b95570291eb32ef2cf62416bd1ce3666eb2ce54accd69f79d506cefbfe6feb5a1da", - "0x93042dd42e56671155bb40d85d9d56f42caf27bd965c6a7a7948b39089dba8487d4d5fd30522dba6ba392964e3ffd590", - "0xa76adeddf2454d131c91d5e2e3a464ef5d3c40ee6a2ab95e70ef2e49e0920d24f9b09276250ed7b29851affbdbc7885a", - "0x92a488068e1b70bf01e6e417f81e1dc3bcec71d51e7eabbc53b6736e8afdb8b67d191940fe09c55783be9210e1cbd73c", - "0x8180ffffb5abe78c38f2a42a3b7f1a408a6d70d3f698d047d5f1eef3018068256110fcb9fb028c8bdccbc22c0a4c3a20", - "0xa50ab79cf3f6777a45f28d1b5cdad2c7ea718c60efeeb4c828d6307b29ef319445e6a9f98aa90f351c78b496575150c1", - "0xa4632399c1a813e41fb2055ef293466098ea7752a9d3722d019aa01620f8c5ecdc5954f176c6c0901a770cbe6990eb11", - "0x83bf5055d6332009c060fd50b8dc698d42b764b079c90a1fad8a83101f8dd1cc27acb27dc9d1c25ac8d3db4107471b4a", - "0x8c432e044af778fb5e5e5677dbd29cd52d6574a66b09b0cd6e2a5812e71c91559c3f257587bfc557b4b072a822973a60", - "0x8368a0f17c8427beb71dbf11a09a2fe8495a33f08c29c74a9a996a88aa01c0a09f9555abeb1ef1592cab99a9e05875cf", - "0xa8795e7f4c4c5d025ead0077c3aa374daaf9858f1025c0d3024d72f5d6c03355ae6ac7418bf0757fe49c220acff89f7f", - "0xa6d9f67ca319ea9de50c3fed513269b83fa067977adfd1e9d9ee07ad61b2ac1de64a39d7b6897ab55870cf982fe481dd", - "0x86b3a4ea9b1fde00cce79d5ae480353d60cb6ddce363c535bbbc3e41a4b8e39fcf2978eb430091ae1b10420d43193971", - "0x90fb5cac22a22fb8a6b619f1eacd95873be974d4d5d1f7080e523bb9b4b2644eda7340d780bd1ea8ce36407ca0410fea", - "0xb5036d4c241685bcd67156e4ab0eba42b97f639947d54b17af2c88fbcc5fc57359c7df4bc7f8df955a524fb1501a6fda", - "0xb1c56f028f31f0ff86bdf55788703b4d809becaf3e4d9d349f1b660a07d2f15e127eb72a0e2a5a2742313785a3de43a5", - "0xa3e909196f447e492200cc67000c5d7f0f585fb98e966cf9bf08257597fea8d92a90ceb054d4b5553d561330b5d0c89a", - "0x87cac423d0847ee3547f45ac5babf53bddb154814e291f368cbb62ddd4f2c6f18d77a1c39fddb482befe1a0e77d5b7fd", - "0x8605b88ce23190b1fa9d389b15e6907417239a72b97673d1479c4ccb8f4515c7921d14537775c74e738a9c3f122b1443", - "0x87587504e819bc7f0349705a05c15e8504fd6b2c25c3fd264096cdb7aaa22d8078da776215925d9d775a7f9355b6f0c0", - "0xafba279768f0f928b864645aa4e491e9c949bf3dab57efa24eeaa1a9a7d4d5a53c840019354068e64c65a2f5889b8f3c", - "0x86b1cdd26ea9a3ae04d31a0b34aa3edc9e8d038437152214d195381173e79e4ccf7f8f0ce9801086724a1c927c20e4c8", - "0x812d3ded3a3c9e58eecf13a29bb4cc13b01b2a0af322423a29bb0e4f6d9021d1d87ac4af7a2a6b88d34f44a8bc1b3c55", - "0xa988cfed9f481bc98beb5fc188ed3f6893a3ebba27c3ebace669792f6abf0997727023c3b6930a6421224f5b257b8b49", - "0xa38c974b57da968f0c4611f5d85d8014fd48594c8cd763ef2f721cfd2c738e828d41ff029e3591d7447e3125641db8ef", - "0x880b4ef2b278e1b2cccf36a3b5b7fbce94f106ed9fa2820cb9099a7a540a57e9fdeef5c0fb0a743049828fc2b8c46163", - "0x96e7d1bbd42195360267c2a324b4d9bccad3231ed8a7f070278472a90371867e2ef2c29c9979a1ec6e194893afd992df", - "0xa92beb343caf6a945990adcf84302c55d1fccdef96c34a21f2c00d3e206a9b2c6c6b412f66e5d4fafe26ef6446cde705", - "0xaa48afa77d5a81cd967b285c0035e941ca6d783493e1840d7cbc0f2829a114ace9146a8fbe31ecbd8e63e9b3c216a8c5", - "0x893a2d97ae067202c8401f626ab3938b135110105b719b94b8d54b56e9158665e96d8096effe9b15c5a40c6701b83c41", - "0xb614910b247c6ade31001b0435686c3026b425b9bff80b6c23df81c55968633349e1408a9a5a9398a7d5d6ed5d9d3835", - "0x991c660e4d476ad92aa32ef2c5b27669ab84026eeb5ca70af69bbbcd8ebc0a8fec17843423306edc78b4436629d55c25", - "0xb926a21f555c296603dc9e24e176243199a533914f48994b20abca16f19c30cfd0baf319268139fe3f83ce69afdc324d", - "0x8e70e4867d2731901d603928d72bbeb34b2e0339a4f5cf06e7a771640717421b4ea039c61dde951582a28c2ff152ff70", - "0x95aafa379cc6a2b4bdd0cad30b7f0a47839952af41f584219ec201c6c4d54610eb2c04b67b29080acb8cecc5e7543fbc", - "0x8465bd8be9bd9c2c6116d4ae44ec6618c109cb9aaee2d241e7a6ed906d398ef15a6fc18bc8b1d3398184241405954bba", - "0xb455f751232de0a48440d09983f4f4718b6169907979c9f282acf7177ab5b1f338fe1f2acd8d0bee4b4aad61d0340839", - "0x970df2314849c27daa16c6845f95b7be178c034d795b00a5b6757cc2f43c4c8d8c2e4d082bec28d58dd4de0cb5718d61", - "0x8d52413f981bc611427ad0534d25e914113d0ebcd6960aab6421608bec6648b89ae4b2ca2153c57d3cf4f1f37212aa5c", - "0xb7ac87da14b783914ab2e914fb7b536893b7a650cdc5baa1f3b4aca9da77b93a3336671335250e6467a8cd4aa8dc61e9", - "0xb37a2ec9dec3d7d9cbc911fa1e5310a47d23a841d02c8b99a923991c73fc0185d130a494748c64f2b5a4c07bcd06920e", - "0x86108b661fb2c363adcca84c114c83346413df748b959015c018452cfac14890bf585dc0a646d68727cc3cdfd2b61897", - "0x8421044f794a1bcb497de6d8705f57faaba7f70632f99982e1c66b7e7403a4fb10d9ef5fb2877b66da72fd556fd6ffb0", - "0x84d2eb008578aebd6f01254b7e46584c1524e6fd7a5a2ae5fa0ea560865ca50d52290cf2d12dd20b042f402e62181b4d", - "0x8d6bed5f6b3f47b1428f00c306df550784cd24212ebac7e6384a0b1226ab50129c0341d0a10d990bd59b229869e7665a", - "0x80e30cabe1b6b4c3454bc8632b9ba068a0bcfd20ce5b6d44c8b1e2e39cbe84792fd96c51cf45cf9855c847dc92ce9437", - "0xb7e74ab2b379ceb9e660087ee2160dafe1e36926dfab1d321a001a9c5adde6c60cd48c6da146d8adfa2bd33162eeaf1a", - "0xa2b1ea43f51460b3cb83657b4e296944658945d3ad6ae7b392e60f40829ba1da6a812d89f0380474578cbd0ab09801ac", - "0x91ead7dacf43905eb5d4b179af29f945479ed074126bad3b5a2bbc1663af5f664fe53a36684e9389ab5819e53f1344fc", - "0x927c030d5a69f0908c08f95715f7a8d1e33bed5e95fc4cfb17f7743cb0262755b1e6b56d409adcfb7351b2706c964d3b", - "0x883f38af3b2c1d50f6e7c515a5e02468d76890f6e669f7acd2df89365862fa65877095deb001b4e2868bc5b59439dbb1", - "0xa0ebae60a998907a19baa396ae5a82bfe6aa22cf71bfca4e1b4df7d297bd9367bbeb2463bda37aa852ad8fd51803e482", - "0x8ae80eeaed3fc456f8a25c2176bd09f52a2546d45d77a70f48a9e30aa29e35ff561c510ae1f64e476e4a0f330b9fdbdd", - "0xa7be457b8bc1bfde4865a35b7b1826118edba213b0f0d3cf5d877267cc1559cabe61cefb1e300142a978c29676036179", - "0xaf51da717d2a45ab96fad5d9317ea867ec4c6a411af6fabd72e568230099a04c036a0f114158815b1a75da6474dc892a", - "0xb549cef11bf7c8bcf4bb11e5cdf5a289fc4bf145826e96a446fb4c729a2c839a4d8d38629cc599eda7efa05f3cf3425b", - "0x8d264fbfeeebb6c4df37ff02224e75e245e508f53fb3446192cd786ecf10d0f704c4fc2e53e7f7318ae1407e46fc0fb8", - "0xac3195143035cdb4ddcd5f93c150035d327addee5503ea2087b1a10b2f73b02453ddd1a94d8e7d883e365f9f0e3c38c9", - "0xacbb398ea9d782388c834cf7b3d95b9ff80ee2a8d072acae8f9979595910849e657889b994531c949d2601b3ce7b235d", - "0x811e6a5478f708495addbb1445a2ef23e39ee90287f3a23ecd3d57d4b844e4f85b828bae8fa0f1893dfcc456f86f7889", - "0x8cde690247d4831dfe312145ae879f4e53cb26641b3a3bb9eb4d590c56c11ece3cfe77180bd809468df5cddaea4f5ab1", - "0xb42578df29a9eb23bed91db6a1698df49654d2bc1b0d7973b2a7e300e9cf32e0e6ac464d463d4d26e394e7598239c4bf", - "0x97ffcbf88b668cde86b2839c7f14d19cb7f634a4cf05d977e65f3cd0e8051b2670e521ae74edc572d88201cff225e38a", - "0xa322b5d2a6e3cb98b8aaa4c068e097188affef5dec2f08c3e9ce29e73687340d4e5a743a8be5f10e138f9cabbe0c7211", - "0xa076ea1084b7a1a33115ef62d6524f36e7820579868763a6ed1f8bce468f150cbfbf0ed04be2487aaa34100d828b0db6", - "0x944259a56e3b4f745996289912740281bde47e22705f142c2a483ffd701e780f51a01b177d2494dc8db9e69157f45d44", - "0x91cb79d52951d1b901e4a686bf4ad587e31db57ea5af6ffeb93eeafae3929879c386ddec860f803c2dc61055437e6bee", - "0x91efdbcaad9931312d7c41d24de977f94d7f3f7b88090a1f72d9a097a1e30cc805c5ea16180f463022d9b26b8863f958", - "0xb26f5ed09f7d5bb640ec94ddd1df0b76466f69a943b4699f53d45296d5d6b8010bb61477539bc377d1a673d89074d22f", - "0x80822499f96a1a8c0048f01f389dfcaaa5d8269c332dbb507fe46f270bcfd5f67c53f827fd867221592dbde77b6b37ab", - "0x8860ba25d5530cb8585975d8013a1c2d5b0f0f96066044fdc43ed13488ae44e379c624ff6993a18cb6e037809d7985e7", - "0x999d1c44e14184349064415ae28a149b3b11aba5baab6792744378d14df554a3625fac82038eaca920064822294dd513", - "0xa62c2e7c692403e874a16e08e46a067e19dd561993ca07ff79cecb53c753763b3e49d372638c96c0a8c921bfa0798a0c", - "0xa1c84730a5c41dcab9a5ef9e1508a48213dbc69b00c8f814baf3f5e676355fc0b432d58a23ad542b55b527a3909b3af6", - "0xa1c0c317e6e352e16e25c140820b927161ce5d2c4c2e10bca3057ba4d46b4f42ad7aba20de86dad9fc6368ea92695268", - "0x85c216e314eb7bd8ba02e092c90e132bc4bafb21c6a0fbe058b0dd4272cb76f183b83c6783fc321786065ff78c95f952", - "0x8e8f63ec8f4f1f7fcc61f893b671710c3c17f9d2d26c5c6ca40e671bd4b252bc0cc1655e6780d2ddcf2915d8f623b9a4", - "0xaaeb0005d77e120ef764f1764967833cba61f2b30b0e9fed1d3f0c90b5ad6588646b8153bdf1d66707ac2e59fd4a2671", - "0x92ff79402d5005d463006e0a6991eaacc3136c4823487d912cc7eec1fe9f61caf24cd10022afdab5f6b4f85bfb3eee4f", - "0xa32a5bd9b7bec31dd138c44d8365186b9323afbba359550414a01e1cdb529426bfa0b6f7daaf3536e9402821faa80003", - "0x845982c2672fdd44b33d2e56ad676e704c02f756b09e8765bea42b924c14724484567b55f0db42ac20cb70a7f5201c14", - "0x89cd9f6ae7d9a9ff2b4db916ba3af9fe700fcfbd16577bf73a965af938e8cf633020466b0298d3c31300360aa6851af2", - "0x8fa2d7b22af8e6b82679ebdfa13efdcb34289a554653ea6c1b16efb9f957f7fe64df787e7b03d8cdc8a732b91c916bd1", - "0x94f327bc57ed1ce88ce4504b4810cc8af5bd21a7e07b280a7866ce08e39b6cf7a6560bf73a5f10671271624cd7893970", - "0x90f4476224b64c2a5333198a4300ece8b3a59ae315469b23fd98dadcdceaaf38642d2076e9cd0bfacc515306f807819f", - "0xae0db78548261216ad7d6a7ed4e6089ee17b3fa311494b2f2c559e215cd3de7e5f3a781a49dcff428a8a61c2a4f49a19", - "0xa83371f44e007c708dc4bcafa7bd3581f9080a4583c9be88624265014fd92f060127e628de5af3c442a25f049c7e7766", - "0xb471c72bd2971353f4b44248b8e6cf5316812861a88ccfc20fd0d89a5e010428c387228b2f6f14c12f79e31afc9d0753", - "0x8962afddcb1a26cc8ccd3c993109e79a4dd747ca473b8b5ef93d9c2e71d29623b834ac945074acf118248e3ae7878a6c", - "0xaa0940e4e5586e79a3d97397c8aff3d112c6f759d2efac29366acc5b5c6a7cfef8d50516bf309da8b787de265dc8deda", - "0xa211120e1bb3b10138df1fa58efb009a298b8771f884b82bb3de15822b1252124a68f3980f96122a775fb96f05ddc3d5", - "0xa1047401598b1e6e2613d746bb4689e0406eccdbadf319a6609a3261cd09deec215d90eba6d0ddc50dd3787d60104e7f", - "0x96791b2b8066b155de0b57a2e4b814bc9b6b7c5a1db3d2475a2183b09f9dcd9c6f273e2b0c922a23d1cf049a6ce602a3", - "0x91013e0d537fb085a49bf1aa3b727239b3e2c1d74c0f52050ff066982d23d5ee6104e70b533047b685e8b1529a0f14dc", - "0xaa65c11071be23c9bddaa5203f3166e5cf043efe5fb8f4b26f8a9cabe71db701a450e79eb001c401da5752755d9cf1af", - "0x8645cc44d180c18a6d8f57ba57bae05879451997533cfe558cad4d3d586caec877e348915e32a09ee73483283c4df744", - "0x8eafbb7002f5bc4cea23e7b1ba1ec10558de447c7b3e209b77f4df7b042804a07bb27c85d76aea591fa5693542c070de", - "0x919c81bd1f3d9918e121e4793690f9ddd96c925ae928536322d4b98132f21979c1f34731d393f0ae6e0871af4355a8ad", - "0x98181e9291622f3f3f72937c3828cee9a1661ca522250dfbbe1c39cda23b23be5b6e970faf400c6c7f15c9ca1d563868", - "0x8f44c43b80a3c5f488118859fab054745cfe5b0824821944b82fcf870fda6d93489ea9ca4220c24db2f4ad09c6080cb7", - "0xa683d4865ddcc099f7b698153007b92f853b80f49b3be75163ea8cd1f8ff584b43a68e68de3ae61cda8ad4b41f355c87", - "0x942772b7c7c47d4e5957ccf1d6f1450070930af3e2b7eaab0dd7699372445df0cc910e6c0efcf501887dd1adabdaee23", - "0x805c06e565ee67cab0cbccb92b6656fdb240b430766eade3c6b0a0b1b93c840e2b4f028601451dca135c783239463880", - "0x84d3e2a06e16ced26094b356a16a4fb6aad50ad9ab23ef804a5852a33ef0bff76f3c5fbf7beb062376c2e669cb598679", - "0x803df08aa745cc3c0a799f3a91bb6ed423cd520c9d255d36c21bed1a0c3b12e8cad32f54da09dadca97683e9548fba91", - "0xaa2c3ef95b8d4265f01666129646004b6950d3e8ce74b4ca12aa3b90fbb445079a569178df772c272463a44d48922b8f", - "0xb0a4c136fb93594913ffcebba98ee1cdf7bc60ad175af0bc2fb1afe7314524bbb85f620dd101e9af765588b7b4bf51d0", - "0x93e4c18896f3ebbbf3cdb5ca6b346e1a76bee6897f927f081d477993eefbc54bbdfaddc871a90d5e96bc445e1cfce24e", - "0x89019e9550648962420984e9fd03597a854ae824567d9aa6cd5db01a4616b4e1477230f2d1362a2d307e2425a3eeb898", - "0x861b710d5ec8ce873e921655a2ca877429e34d432643f65d50e8b2669929be40a9ce11c6353b0ada1fe115e45396b2b7", - "0x88554c83648ea97dac83d806cd81d92531980346b208d281fba489da15a0084fd4d9a00591d1ca67aad3c5793685d55f", - "0x851fcadebee06930186f35293feefd40d7daedec9b94e6fe5967536c2c0e4cc68f58d3f5fbc76f1e77b90c9580074f98", - "0xb96a11048c7c327709d52e72e6f6ed0b7653329a374ea341ad909311b5b303e5629d6dcf11dcdb195e8c7592ceefac21", - "0x836075979eaf386ff6cb459cfd48fed171ae812b0ac3b38dc24dd8ca905cac1c600be717d4a0defa0a854f40cfaf8c33", - "0x90fc170529bcc0b80c46a53fffd8323fd2cc5cfa9b75ea4d36db21bd1f198335ad2bfa87f8990cf9cd9fd7989ecca718", - "0xb7eb6a49bf8f942dd8c37c41c1b35df43e4536e07ca9f4c1cfbbf8a8c03f84c54c1a0d8e901c49de526900aeac0f922f", - "0xaf6911edd6c7ad30f905a0a3f78634808832fdeb4206b006934822d673bcced8e378779261b3c4b772b34b8871987f57", - "0xa74d240d0d7ea0afe68813fab55388d77e75eca0519d21771dcb7170cedb11dc14b237b26c5ae1f7f728b52e5ec0f02d", - "0xa7b86e4f1366da44fd59a3ee68018a99c23ba3588789463bd88b0177a9b94030b58cb879a506e64421af966f261eaa86", - "0xad8d94e46cc02a1c0ad27105e8f672ec15b8296051801f1918d0bd470625686e8e8a0abde8f6852b846ee8d9132b26bc", - "0x980508c4d1e655cc6200f89a884b3a25c0c05708a3e4a101205c4fd901c3e20a943071a6300bb2614be41a139d4ef1df", - "0xb0173651b4ba0590b1d2f0265183f3729b5bb09893523ca12c4936120cbe5ef0d9b98733734407d99fdc766792ff10ac", - "0xabf72ec0280d56971e599b3be7915f5f224c0ccde2c440237e67b95489f0c9154ace04b7763db228473715f68053f071", - "0xb404beebf60026ca6843f2953cfcdee494d495c8e2d18865147102ef29a8f0ee470961d2246fe5a450c622d20ca51d53", - "0x89461cb2dadf51d6f1208b0965c8eabec895d7b19b7d90d3c6e49dbe75a75c30fd26db3dfb169dd46a4342280225032a", - "0xa61cb5b148cb7ff34775dead8efa7d54d7141182356bf614070dfaa710ebf07a4dfb684dad151db60c0f8261c30a4f40", - "0x83a798f47a4f62dcb8b531d463b0fd4a876d47a8ca990710290549255033c909de709471b4e823a60bf94d8baf8b5acf", - "0xa23431589f3a25070a188deead9adb0ed423d6b00af267f3f125cdd4391c1527909b5cfa88130dc4b67915f5002128fa", - "0x8d77e65ba6250fe18c54ce70d0ba4571a7d3e68a8b169055cd208e4434b35a4297e154775c73e7dfba511faadb2598c5", - "0x90e5db75f3787b819df471712f87b6f3281437090f5db7a2c21b07164446292a414c687e41de2d1ca00786b093239c64", - "0xb382fa28670a5e14dc954b2db8ace250c73df71ab095304bd8ee28f455ab26cc54f82775a831428e110d1a3a2af709bb", - "0xa58d2fb1c2612d28c54fafa7f2e1e6c336c24435abdb53e1be9dce9aebecbf7468a348b872549535ac18aa003f83ea87", - "0x9545f94c4e9056e360dd999985f8ad06210556fa6f07cff77136a2460605afb0ff1fb1d1a2abe4a4e319fd6c29fff80f", - "0x93121aa60f904a48e624e00f5410cf8c8925d2b0719f90c20e00cba584626f833de7c8a18dbfa6a07df24b916156bfc0", - "0xaa3446aac25f6c23ea16e8f7d19c58d187746ef3c2ac7d8fdf9bdc329409a07589ec8eebafbe2b156e7ba60addc15af8", - "0xb964f50011f03135e993739e2e63a71933ba4583040b3af96c7e2dce874226518f7b68f622c4a1d78b9c3ec671d33ad7", - "0x8cb5cb7cba886af58acadc5a4348524b1395a39dc51196316d759a9b72d9fc0fe45b706e264393a13ff911f0d15de45c", - "0xb50c306f78143b37986e68efa10dbe1fb047d58562e9b5c5439b341dd8f1896c7ae586afac0a3213759784a905c1caaa", - "0x97825edba8410e8bcb85c5943628c02ea95ee7595f559c030b94395c0d1d0d84c38eca199fce9c1992e572b5029b124c", - "0xb0922acd6da2a95b36de6d0755316594a7e2e32ea774792dc314e8c3cd76d9f1d69df38231e166e24bd42c664f4fbac7", - "0xae5ea228c1b91ef23c245928186fbafa1275ff1817535018d7d2d913abff0fd76bf41fd04a96d816f2f1891bd16e9264", - "0xb106c6d13ca17a4c8ea599306e84918127cf2de21027ac3fe5a57d35cf6f3b1d7671c70b866f6e02168ae4e7adb56860", - "0xa044cd5a3b727dc1cb59875e4025718375d12e706fffcdb48874e51a675dc2cabb209670192e408cdced5aeac65192e4", - "0xab1cc44983e46a6ea2430aa6616ab28614f43624665e3e6ae31a9357c0c5434f34e56c720906e184327693cc4ebe1fa2", - "0x890def696fc04bbb9e9ed87a2a4965b896a9ae127bc0e1cc515549b88ddbcbc02647e983561cab691f7d25cf7c7eb254", - "0xa86be58fef115445b909dffac6f51da3fe9214afd9c31fd564bb8f39b1dc3cb895b1222f2c63226b54b60b278ec45edb", - "0x8757e9a6a2dac742ab66011c53fa76edb5ebc3c2fbd9a7265529a3e5608b5c24b4482fed095725e9b8fed5a8319c17a4", - "0xa7e0ddbae16e4491822684c0da3affecbbd17ef96c5c491ac093c6eb4e162fc7854c367535e296fd3d6265c2ed1210bb", - "0x941fe0dabcdb3225a625af70a132bc1e24ccab1f8331dde87db3e26cbee710b12b85535e46b55de7f5d1c67a52ddd5c8", - "0xa24d05b51c7c128bb49979cbd9019e6618545d95275a44b5c3d1d03e71bf2ebffdf43fff50c30846ec27d279043cef4e", - "0xb526f40d519e7a8f2c81b69f71b3e2ef079028004c0448ba0608296c2787972491ec6d05ed6a8fbd5ef2da76325a93cb", - "0x87ca4fa85a257adf7e21af302437e0fa094e09efced2d7ebab6cf848e6a77ae7bfc7cf76079117f6ed6eded9d79ce9cb", - "0x9276e8051bed8f5dbfc6b35765aac577dd9351d9d6ac1bb14496bd98091005b9a4737b213e347336413743f681f5043b", - "0xa7741c52498e0a24db3ce7699882de8f462a2b3ed5e9f77dc7200cbdf46b6cdd923b1128759909d6dddd64700c4c20c5", - "0xab5b363ed9551e32042e43495a456e394cbc6d53b15d37a8859850162608bdf36d3d4564b88fdbaf36ff391bb4090b8c", - "0x838ff6630dc3908a04c51fb44a29eca5a0d88330f48c1d0dd68b8890411a394fd728f14215482b03477d33f39645dceb", - "0x997d3b82e4753f1fc3fc2595cfe25b22ac1956d89c0950767c6b9de20623d310b1d84aaa72ab967ef1ea6d397e13524b", - "0x85416cf3eef63d5530062d6f031aeddad101c7f1aea3bccb826c73f8a25d5d963caefd789a6b9832bd4ed459f268ae64", - "0x9194bc45e11d7276ed1c9ef3ad5a33d6a27372f5568563ca8ee213e2e7029dee404ab5acbaecaef698129798d35fd895", - "0xa4828a003513ab887082390262a932a7e8c5e25431824b7b4cc10fccba73265c0e5ee5b315ccef13906d971644913806", - "0xb907ec84b6ae5729d36e2acd585a350acacdeef148bcc5dc4a91edb57505526462bd4371574865541d8bb0d786a29b2f", - "0xaa5d1c1f0a7f6b9b3c3734f85864aa60bddad5121450218d76d82edefd2602685a820965c56d7eefe789d5115cb41e01", - "0xad28fe70a8606f87bcb5d6f44e1fca499c24bcee791971f599ffef1f403dc7aec2ab6ebed73c1f8750a9b0ff8f69a1e6", - "0xa641eaa149c366de228a2833907ad60eea423dd3edf47e76042fdf6f5dc47a5b5fc1f1b92c8b96c70e6d8a68d3b8896c", - "0xabac08f4df786b2d524f758bca43b403b724d12601dc0a8362b7a2779d55b060c6682a5618fffea2e4def169fcbd2bfb", - "0x8658a15df961c25648fd444bdf48a8f7bb382d9212c0c65d56bf9cdb61aab3bd86604c687fb682260dbc0ad2dc84bf01", - "0xac7983d50ec447b65e62ed38054d8e8242c31b40030f630098ce0a4e93536da9179c3f3ae0b34a0b02aad427a97ee60d", - "0xb8877a00a24b0ffcb2bd3fce8a8ba327d8ee2e98d85531cb61fec21fd49cd1696491cd51024a9c3820cf06a77cacf04b", - "0xae075b66e5f211c2149c45b211d1297bbc1d9e6497cb3315363c492a9a51ae5b9d0a28bfecd755d68553736901ac6606", - "0xafdc091a224486e7bfac169e6a7b4e008d2d04144508a337fd93b6f4d385ee3f0d927b1f5c1cd79a15e0fd6078e45dd4", - "0xb75c28941ee3f91b3535b4eaa0fb17b59ca65b5256601a1f6d0cf2bb4d66837fd16e51d6942856679012a5730a66e519", - "0x84a6edac5ac68a7ca837c46d5ada8fab136748b6c3a3b9165dbbc231ec386b15328e4ef7d69a15d4cf354135348a4ee4", - "0xb01ee30d120b97e7b60ea89b9b6c537cdf20b6e36337e70d289ed5949355dd32679dc0a747525d6f2076f5be051d3a89", - "0xa80deb10bba4bc7e729145e4caf009a39f5c69388a2a86eaba3de275b441d5217d615554a610466a33cfe0bbe09ef355", - "0x8d6e3df29419bd0da1deba52c1feebe37744108685b49ca703e1b76fb4d612e3959d3b60b822506e5c0aac50b2f5eee2", - "0xa373408beb5e4e0d3ebd5ca3843fe39bb56b77a5d3d2121d4a7a87f9add3ec7376388e9d4b8da0ba69164850cb4b077d", - "0xae0beb452af7479134a7fbc31a5f59d248e8a67d4c7f73a0e30a51db9cd33a1da3f0ae947fa7e5983aea1343e7daf06a", - "0xaa103a329b699d4102f948101ce5fae27226419f75d866d235da8956f11367e71db5c0a179dd63007ed53f7eec333aaa", - "0xa094cca9d120d92c0e92ce740bc774a89667c6f796b438b0d98df0b7aef0935d8c915d5b0dad4b53e383dc9f095c29fa", - "0xa4d88467136b99d6e55603b3665b6da0f7fb27c7759687f7e6977b6230272773d7b95049d999538c008f310c05ed948a", - "0xa23710308d8e25a0bb1db53c8598e526235c5e91e4605e402f6a25c126687d9de146b75c39a31c69ab76bab514320e05", - "0xa36d6952c2d7f88bf28032a76ed46c4dabbf1901a46efc50deb798d1b44adf7e0210fbdf2473a1ba408b5c98d76943e5", - "0xab45f5b756ec6e0b98d0d4301c87675a0a1f0b1178b8a9780c1ab23e482cd821834835afa1de890962212159e464b10a", - "0xb800be1788175a01a9228b0d3e7eb4302484a2654eb2a86c0f0900b593da0a436ef031ac230e2b05e968b33e90a342ce", - "0x8cc8d279ec08d0a5a2a09ad07fabb0122eb65f48da2571d83f86efa2c1c5bc51b04ae94b145f0a8ef19a3988638b9380", - "0xb4cd409256819e8e4627edbba90ec40b7da17a57f95749104d90db0364f5007b1accc816f4d51a0dbe5ffbcb737cb37e", - "0x99365fe5ab8ea8bd768ae7181a6ba49b79d240f512ce309b02f09d465fea276298ff55b5b9cb5b4162a901b390606024", - "0xad77fcac9753efba7a9d9ef8ff4ec9889aa4b9e43ba185e5df6bf6574a5cf9b9ad3f0f3ef2bcbea660c7eef869ce76c8", - "0xad2456725ac3aeb0e4ca5c0502a8abb4dbd8a8897d9d91e673fea6a0cffd64d907b714b662d73c0877b98d4ab3ce6a89", - "0xac8436e33619e2907659741d66082acbda32612d245fcc8ae31e55f99703fac1a15657342fa66751d3be44fc35d71c36", - "0xb0eecd04c8d09fd364f9ca724036995c16ba6830d6c13a480b30eb2118c66c019cfdc9dacce6bfd8215abe025733e43d", - "0x9439b663e4104d64433be7d49d0beaae263f20cfac0b5af402a59412056094bd71f0450bc52a294fc759ca8a3fddfee9", - "0xb72de0187809aaea904652d81dcabd38295e7988e3b98d5279c1b6d097b05e35ca381d4e32083d2cf24ca73cc8289d2b", - "0x93706f8d7daca7c3b339538fb7087ddbf09c733662b55c35f2a71073f4a17c91741955d4d549c2ee6c22eaa84193c1ad", - "0x926dc729e135f1f0bff4662ee3d6823a64597fe189b763ada34f246e77705fd4e062d85506a338e9fa98c4d225a3b27a", - "0x93f03495d53c781be8b76e37e68b64aa260523004eff6455ddc8a8552af39854e5181f8c5365812b1f65926534fba5dd", - "0x9834f66e5c946c3a8241ca2bbde046a7e88072124911d5d15c037a95b61e82b88b5c2058fa4a3721537dee39dee5da18", - "0x92ec1aeb2aa24c51cd5f724972c8b6095e77b237d83f93ed34ca0bc91a1dbf1ad95adccc59e0f0abbfef33f331f3298c", - "0x94402d05dbe02a7505da715c5b26438880d086e3130dce7d6c59a9cca1943fe88c44771619303ec71736774b3cc5b1f6", - "0x8368bb9b9bb2e17730c42ed1100eb870c88a8431601312aa8cb1e738cdb9ca2704dfd432cf1703c0db043259819631dc", - "0xa35189a105401f0cfba4b43be21723486c04659e5a01e67c43e8f9911030810b878beee696f04f63d314ccfe97ebb790", - "0x93ccd8c5f82374e0bef6562e16576f742d79b6f400e3485ef36e148088b61fbd882c3d2bb38ab0b43fa1dac77f31d543", - "0xb85d9a426a23ca9ee582bc16c203a9352dcc5f85440e46979de80eb572384479b697dc964cafd9457d9f34eeb77bb72a", - "0xaefb70e89dbf4456e077690509afcdcabf975416ff2fa16777fdf90b3abd3f5dcd865c43f1ebe6f8a669edc7f3bd6ad8", - "0x8eb03001ac9e22c6956a682ed458e650785c36d23ddbcd51ac4d9cc991325c02519ff1958987a08eb29ff56ff6e2c293", - "0xab4a1ffef7e001723c71f5d28f3dd030a06c42d91773733d117247bbf9c01cd66fca2cff8c6ce04c4bfb68dfcdd851f2", - "0xa9ef845ab489f61dbfdcd71abcc29fc38f3494a00243b9c20b9cd0dd9e8a0f23304df84939b9652cdf5542d9b3ee085e", - "0xb5726aee939d8aee0d50bf15565f99e6d0c4df7388073b4534f581f572ad55893c5566eab1a7e22db8feeb8a90175b7d", - "0x9953a7cbc152f101a60e3e381f2af17ebe7401e16ef6462d132b8f0f6c6a18837914a1299d1605f9f289b9561112f4bb", - "0xa0c9b944a338325f5efb675c9c12619fb43c8e25e80d38d6140e31d5070573b1a0ed9bb52576e4f22f37d0292d36a648", - "0x8bfd6a173a56b73480cc950ef266a18933ecafc86915a7453ded09efd8a0cf4466101f1373f05d48eae3e7fc5c0f7f54", - "0x983fc1ddf17f9756c9cecc00b39bb2ad432587a5c6d1c3296a383b9f539c9afe84c6c818447a709c0b686ba26ce5ea3e", - "0x94bbc6b2742d21eff4fae77c720313015dd4bbcc5add8146bf1c4b89e32f6f5df46ca770e1f385fdd29dc5c7b9653361", - "0xb26b4d483bca73d3f3a976bb595a0e40f9a42094e0febbad3a1874934be1939a1b362ee4ea14a4f5cbfa9b1392796a12", - "0x8dbe8fcbcc414eb352245c52549973f73d987012de9d5f2b2f55dfdc43cf8cc9ea6b147abf149817f80f9e15aea566c6", - "0x9604da21e23c994a0a875ad5e0d279c79210f7a7de5c9699fac4aebbd76d39b703eeec5dd5efc9ad6b9dc58936089ddc", - "0x8934e9a3feababa12ed142daa30e91bd6d28b432d182ac625501fe1dc82f973c67f0fe82d39c9b1da3613bb8bfe2f77b", - "0x85f2ed3ffb03e50c8f22553b8e6349be6244d893aa37a7c6dbd221e9e121579e5a04466e60d6b4d3567bc747b1fc1e9f", - "0xab0ad421f6fd056687b4fa5e99dff97bd08840b7c4e00435eb9da80e0d7d071a447a22f8e5c1c5e93a9c729e5b875a1e", - "0x841d77b358c4567396925040dffe17b3b82c6f199285ac621b2a95aa401ddb2bc6f07ebd5fa500af01f64d3bb44de2df", - "0x83f21dfe0272a5a8682c3c7814c5e0e4db6a9098f1fa80fda725f77ea81fdfd2fa36b0c8db013503a89bd035f86306fa", - "0x95c98e3b6b62f84edf7f297cae93ee5f82593478877f92fb5bf43fd4422c3c78e37d48c1ee7ca474f807ab3e848d4496", - "0x81c3a8c00cfe4e82f3d8cb48de7d4926d5ec2f7689f9cb85c1886a23758bc107a4bc6e978601c3519156a169d0bf6779", - "0x8e956ca6050684b113a6c09d575996a9c99cc0bf61c6fb5c9eaae57b453838821cc604cf8adb70111de2c5076ae9d456", - "0x83474776ef2341051b781a8feaf971915b4a1034fa30a9232c4bf4b1bd0b57bc069c72c79510acef92e75da6f6b8843d", - "0xb41780d9d67e9e8b81b1f62d25c0c72ecfda659d2bfe6825edb70ecd0e0724250ac364e7be521cdc112ba638f16360d4", - "0x842ba3c847c99532bf3a9339380e84839326d39d404f9c2994821eaf265185c1ac87d3dc04a7f851df4961e540330323", - "0xaf17532b35bcb373ce1deebce1c84abe34f88a412082b97795b0c73570cb6b88ea4ba52e7f5eb5ca181277cdba7a2d6d", - "0xb9574edb9567f07f85c7c2e6ca6c02d90ad7c7b87d49796f1e2fb7240ad071fb755cf13ca8678668a56217c62df168eb", - "0x82212706111fb1cf5def02b5b0eb7ae9e6ea42b4c7a2b9fcacb7aec928326edb9ac940850dd933f2822f6cf519de0d50", - "0x9579973ee2559da09b327c62b1cc0177f2859872885dca709e24dcfbb9bdf9224a6d26869aafce498f44c0e6bd8a996c", - "0xa102c2ade15ea2f2b0cbc7dbd8c1171de0c8092fc4ecef84b5fd2bae7424aea8be1629f851c75e4d1d0e96104e54bfbc", - "0xa5d7e847ce7793386e17fe525f82aabb790d5417c3c6e3f6312f8e5ff52efa8b345c1ff60c4c9bf7636f5ff17b7a0061", - "0xb4b80d7fbdb1dbf1567dfb30d8e814e63de670839a8f6ff434fe171416599fef831b8e978d6498851b8a81e0bc8dfb85", - "0xb6e6277b86cd5284299ced867d37ab98090ac44a94deef6898aeadd177e64605440c15b9609c07e71fe54c95b61873b0", - "0x8135a0633082e4465090d6930b770340e82366bc5c37be6ef6dd105f85acf63361e17de8b5fcab4c82e9f9b4029954b7", - "0x864d5d9858cd881eecb0dde5e3e0c6c5de623cd9ef619e87b82fd25c5edf45a1a025b1dc763c27c5f4d520fd564b464a", - "0x8784a8fa62e0ce23283386175007bb781a8ec91b06fd94f22a20cd869929de37259847a94a0f22078ab14bb74709fac6", - "0x8adb748d5fa5c22ce4c76a1debf394b00d58add9f4e08524cf9c503f95981b36b8d0cb2dfaef0d59d07768e555733ecc", - "0xb76cb8cb446eb3cb4f682a5cd884f6c93086a8bf626c5b5c557a06499de9c13315618d48a0c5693512a3dc143a799c07", - "0xb2af1f7ece1fd640c205a09614122d69d5d2e81a7618bedefd6dbb91c7f432679be4ced1e6dddd3de323bd44991931c5", - "0xb950b457c34bfdfdd9d6da9628d41749ccae03659518a04b56487bf1b4c0681b719ec5230c0b0fd5dd710894df6aa2f8", - "0xb21785008910a949804d1291e7533752641d31beae3cb518806488f81d58c38a5efe5ed9534ac692e68c3121e2f9d97d", - "0xa9b120a77d70c1cbc0178a12d97a78b2dd0b98d0584e8e780b937800ceb18c90eaa1f0a83c5b50e34cae1c20468f004f", - "0x998c9ee20d33f96a2388b1df642aa602bc8900ba335e8810baab17060c1eace4bc5203672c257b9ae750008b707b0aa1", - "0xa7d1676816e81a752267d309014de1772b571b109c2901dc7c9810f45417faa18c81965c114be489ed178e54ac3687a1", - "0xb409f87f0632aae9bc081345b17a50a767ba4198f9ac9d352246fb3bebd29ed53c9d6f148c2f318c2eb12846b0aac4cb", - "0xb40a3bae2b08c13db00f993db49e2042be99cde3d6f4f03d9991e42297933d6049394c659e31f316fcb081b60461dabf", - "0x8c1de4264e04ff7e8282faf81c0bfb5943656451be52170211cb7adf4ff21bccbb789400735579c622f69982fcb8e9c6", - "0x8085c60b6b12ac8a5be8a7e24977663125c34827842aa3b2730854ab199dd0d2eaa93084c9599f0939be8db6758b198b", - "0x972cfaefda96f5edfe0614c01533b76153118712c1c02c505008204a5be2aa438675d97f43384199517b1c08c7c9fdb2", - "0xa1e47798a782a024da340d6d6a1b1e5e15a0f2d8668adf349ca375086964414a563cc1eea3226ae637f87e78c0a630b3", - "0x8171f20c020faae112bb92ca213c1df5b1050151496c70db5c5319212bada83b120d515bd7d8b24736090c574e1b7203", - "0xafe779a9ca4edc032fed08ee0dd069be277d7663e898dceaba6001399b0b77bbce653c9dc90f27137b4278d754c1551a", - "0xa5a07bf219432e9c80c38596c93560b49c7de287f31e30b7a06fcb4d15982add4a24085adbc5b753c462be989c64c96d", - "0x9210be290176d7e8a5005d27e7ed825067b1c678b174bc8180f92b5c03b6c3d1822356edba84f460caf6bf5275cd7efb", - "0xa95bec86a7c8417a8df3a0158199327ba0924d3b7dd94cd7c1ef8489b10270ae64b8537ed39cd3699a48942bfc80c35d", - "0xad9725114b01152fff134c1a8ccb8d171b8cd11685ef6815b76f442d757d130bab9ef4c9845e66f4aa0237ee2b525c20", - "0x8018499ef720e28759133033833edfe17ed23e42f99058bb79fe844ddee823cfdc43916be2dc9724d18f9726e6f1b409", - "0x809c7a08fbef7caf4c137cd639f2e47a8ca60d13bca3990eac51ac2a9e4442cd1a1473bebb63c61d595b586525d7b027", - "0x9793a74fa578ace75b083578277a1ae8766d41a5c508b0f1135fb97dff1d0826002393a7276b18cbc4b3c5671360ce0b", - "0xa6d7e65bf9f889532090ae4f9067bb63f15b21f05f22c2540ff1bb5b0b5d98f205e150b1b1690e9aa13d0dee37222143", - "0x99c935fe18699bca9852200c292690a2b834bac508890c4ee9af1aa6999a8d590bf6a3a274bb55d5a73f1b7095d10f37", - "0x860f5649c5299211728a36722a142bf1aa7cbbfbd225b671d427c67546375de96832c06709c73b7a51439b091249d34f", - "0x9104ac7ad13b441c6b2234a319e1c54e7f172c9a3efcb8c5fab0ac1d388b01895a9a208f59910bc00fb998b0adab1bc3", - "0xa3b109249ac2900806f0f39338da72d4f2cc6d1ac403b59834b46da5705cf436af8499fa83717f954edb32312397c8d9", - "0xb9893f7a47af457a9efd90ddc0c0ef383ab34e9c1284e617c126965cd9f0de5c54ee8b7b5208ff190366fe445e9c1325", - "0xb77416ea9a6b819e63ae427057d5741788bd6301b02d180083c7aa662200f5ebed14a486efae63c3de81572fe0d92a9c", - "0x8b20a852fc8f0b7cdbbd808c04a0cfd2fbccbdc0cb2361434f0d96341c8bde6155695977768d563b95746dcb4339fe2c", - "0x96b15806d9009962fa07f8c32e92e3bc30be4ded0645ab9f486962a1b317e313830992179826d746ea26d4d906bdb7b6", - "0xb0d69b3861ca6791632ec8a87114b463e0da571bc076c22a8f0d9e88a1a5eaef24683f3efa8f34900d0112412e3dc4fa", - "0xb01a30d439def99e676c097e5f4b2aa249aa4d184eaace81819a698cb37d33f5a24089339916ee0acb539f0e62936d83", - "0xb19ca6e55f349bbb2dc3e429520ff5b2e817972470794f35c1aac8c118b37a694cfcc875b6d72225343799825d2f5c39", - "0xb7ea5e0d3cfcf0570204b0371d69df1ab8f1fdc4e58688ecd2b884399644f7d318d660c23bd4d6d60d44a43aa9cf656d", - "0xa778da56ddfe4a383816b43b027464d7a28689fc4a6b35b36883d3f36d9c41f0177bdbfc8f258afe8da90f02d3b64fea", - "0x910fd030feb5538f538e5ba74b9bd017d889ed6d2a797be9c26d2be8caeba7a473006102de27e87755742ba34e445bca", - "0xb49593ea6040ce82cfb5aa2881a4b0c42b78aa9fc8467d79c8e4a8ae4ee7355842841c8e1cc0558362047ed80de44fd3", - "0xb07447c7e87459315fcbda3fb86fef27f98373b1246e2ce367e26afd87f6d698a438501fdc13cc5de9eef8d545aab768", - "0x8a9f7e8d45f11c4bfb0921c6008f3c79ff923452bcfa7769beb3222f1f37dcb861be979e6eae187f06cf26af05e8ee5b", - "0x8ebfbcaccddd2489c4a29a374a2babc26987c3312607eadb2c4b0a53a17de97107c54eab34def09144b3098c082c286b", - "0x93be3d4363659fb6fbf3e4c91ac25524f486450a3937bc210c2043773131f81018dbc042f40be623192fbdd174369be2", - "0x8cf8412bd48b21b008f0207b1f430ed96bc6512c3712dffbbecb66e493e33698c051b27a2998c5bddd89d6c373d02d06", - "0xa5562fbaa952d4dcfe234023f969fa691307a8dfa46de1b2dcff73d3791d56b1c52d3b949365911fdff6dde44c08e855", - "0xa8c167b93023b60e2050e704fcaca8951df180b2ae17bfb6af464533395ece7ed9d9ec200fd08b27b6f04dafa3a7a0bd", - "0x93e4d7740847caeeaca68e0b8f9a81b9475435108861506e3d3ccd3d716e05ced294ac30743eb9f45496acd6438b255d", - "0x8016d3229030424cfeff6c5b813970ea193f8d012cfa767270ca9057d58eddc556e96c14544bf4c038dbed5f24aa8da0", - "0xab7eff4ef8696db334bce564bc273af0412bb4de547056326dff2037e1eca7abde039a51953948dd61d3d15925cd92f6", - "0xa3f9dcc48290883d233100b69404b0b05cf34df5f6e6f6833a17cc7b23a2612b85c39df03c1e6e3cd380f259402c6120", - "0x8b476b3b065a3b95a3d11ec60a749c2258ddcc5885bfb50b8a086d3fd1e49ff73e1dde733b8981c3d2d206aa0c87b09b", - "0xaf3e694ad71684f7214f86bed85149db039971e1c362119b979a135255aa226128802e58e2caaeaf8d89304371dd0440", - "0xaa19a75f21a14ad5f170e336a0bd07e0c98b9f5d71f91e784d1dc28a5f5eb6870a4eb35bb41edcf9e6efe982ae5c2c5b", - "0xb9528983419ab5766596683faebb3592982a76b68593f810186b4e5f94f6de60830739ad8dcc164c601d575b84bd2700", - "0x88e7a12a90428bb45bcf4b01442c11607433211fc2f9bee9545304eb66e0b4b5339360160bc782e185391385da7c5ad7", - "0xa4c4df0e29db19ab4c82dd6ca8570b337d15b59c7d84577a7a444a8f762ff16ff5ab3e4203a1d6b60a23ff949a93ea81", - "0x8f11ee58ef82b1bbd2240d3f548d8681e22bed5ce118d605bed4523b4bb39899ac78e15337daab92666750dfcaf32aff", - "0x8d3cba4d10f94bd3406a341c903ad144cfcfe6b61678d5c03084a56b4413bc30bd20d7a9fd5d839dbb565cc9b2aa99fe", - "0xab8a8769c754008a7976b6799e81d7bfe97413d0a79b90715703c1f8f567675463ec93aabee59277121fc4df88b5c7a9", - "0xb201b0546f19c5db88df9c684cf55ed623bdb43927d06051bd595497df741feb1485961f64e8d3d1811d9e2e9e1e54ad", - "0xa04016e9e13ad845763cfe44af4e29fecf920b4aa42f581715fc34fb9ca27776feee45c82093c7274839eef1838b10c4", - "0x8f84cba7ceb7652023fc8ebde4b00ecde1f550935bab12feb630d6f49517b4148f3cde184bf55d4f6ec99a849fc6f862", - "0xa2248409026f35c3da8bc4d5c02315066df8fca44ff5a358cc42b5c88bdf6866dc133617c697bff004b1ef20ec4b5748", - "0xa52c5a63b55a8001b6b67c5db4fd5e95923052f03618369312896ed9892d99354aebc0dee8c3b365bafa29e211a5c3f9", - "0x8c9fefe233d0d657349b7efcdc368f5aaead27071d224af780874751e7d241f6b88f7650fbb4133043b24bbebc12aa48", - "0x86ceb649a337a5a79c17b496993ca07fa93b38a582367ca04f3dfec5cef8f268d4e8080e5a76b150f5be1b177ef6984e", - "0x87dcb537e38cefa32e629ae669da42e809b5afcabdeeef244b72ce057fc18584a1e8c3f073d5d33775232707f0cc59ca", - "0xa020404547407be6d42856780a1b9cf46b5bc48122902880909bdcf45b204c083f3b03447c6e90d97fd241975566e9bf", - "0xa1beb9f673409ec678020ea4dcbe65177aa18e2932ceb9cfb33fccb94b9a8ccb664f71647d58b3c8b2bdbbffbc02d5f7", - "0xae47b31c5b62b38ee886ee04945649054369018dd6543c91f0138464af489a32c1fea339e0e0cbe82e3e8b9f2ef3918c", - "0xb18c41c0f827f6d8656d3fb93c90b663eb2eac034923972f8842cb30e96c32842b3fbc1127930e1ba4322d5b6641f04d", - "0xa6d6ef51a361df2e8f1d993980e4df93dbbb32248a8608e3e2b724093936f013edabb2e3374842b7cce9630e57c7e4dd", - "0xa49da42c27d019a21cc6489ada7b712b98c4ede28ba25dbcfa916acef48446a2baf73e03a48be763378a09774d4a03fc", - "0x8b62902fb2855300580e94830a4bc825d997ede33bf356fe3b7c08d6a8bd85a37879433fc6bee58f9b44ca280f4e8dfd", - "0xaf9d13103868c854821ba518907b067cfba025d739125f1e9cce0a04fffc3a2a1f25506c1209a0cfe1d6c1572c229ff0", - "0x8910f41db6952c25dfbf6b6b5ba252a2d999c51537d35a0d86b7688bb54dcb6f11eb755a5dce366113dfb2f6b56802b7", - "0xb551d1ce88cbf4ffbdcb0113a6e319513bd676d0078dd4e6a6f23ad336c1d0fb47a4e427bdedbe0fc8f152353971f81d", - "0xb8876bda1e709ab16e1347a1107852a7898a334a84af978de39920790b4d82eb0739cbfc34da1c7154dd6e9f7674759c", - "0xa7d9ae9621dd1f3da3cd2d435e891cc3579c4c0d60d6a4565cac86c315cea21a9ad883559fe7b897ae6e05f1aa989ad9", - "0xaac995a41c14d379853ef18ffc854ad62ad77061ca9bdf5029cab3d6c2630de114e777a7fc3322455939d5205ed59c55", - "0x999cec6a31d9b2f280017ddd59138014829fa34cab58e6c35a5014ec364b84712441e7a2f717cf2f0de8d5451e250924", - "0xb1f43b498cba1797f9793dc794a437500c3c44a8a4b59f9125a4d358afa304fc05b88ac31ed40b6eb68f0396b60cb7cd", - "0x93ba2e000bdb7269818d390bc4232992d280e69abebe2db2ecb6fcb1390d323238c9793574509bc1fa34051ac1928f07", - "0xa64808a2d15c30460651c200a09b50fc83e9d84d87abc156d06cee73b76fbd74e6d64424cb5bb83d3f16b21bdb7ae9d2", - "0xa75bcd04fcb44ce5cbab7eef6649155ec0bef46202e4eb86c88b4ced65e111f764ee7fb37e9f68e38067040fedf715ee", - "0xb95fc0ec39596deee2c4363f57bb4786f5bb8dfb345c1e5b14e2927be482615971d0d81f9a88b3389fac7079b3cb2f46", - "0xaef456af90354ff88039d2dde02b0f5a6790aa762b23e0a9da8c6ec92c3b8b3320687bb21666608b4a22615843afd1ef", - "0xb38be9ada17ced704a34a7498c4fd6ba2503f6bd886b693d4712267847efa887a26e7da5d60f8bc5014b92bca8b3a12d", - "0x991a7c93f06d50ec6a4340c6751b73eb5825bad02a954e44e1e2d424af928819ebbb590c6129ce35b3f1e908e2152f33", - "0x84888f2efd897a2aca04e34505774f6f4d62a02a5ae93f71405f2d3b326366b4038854458fd6553d12da6d4891788e59", - "0x941e2e3ba414a371a11c3fe92cabf688ff363da6230ec7c83ac7303f652a19ebc89cc494427c456d0c2ae84c72053f73", - "0x925f3bb79c89a759cbf1fabdaa4d332dfe1b2d146c9b782fe4a9f85fee522834e05c4c0df8915f8f7b5389604ba66c19", - "0x941c8962debd2756f92a6a0451a2bf7fbc01f32ed03d0823dffd4a61186628a4c3c7c482b18589ff65e4c449fa35c2a4", - "0x88ce41025aa153a94f91f22e7b96f9342b5e0e1d76274fc70c4df7d08f66d9f7ac86e55a1c6e77693b8b01b2b38bf900", - "0x8f142bde50abe4dac8e059003db41610436d5ca60d2dfe2660ecaa5f9628aeb8b5d443e1b57662076f77363c89a1479d", - "0xb2affe048c187d311a185503d8958cacbe03796edf79bc32e8533941004d9178bd2e376e627e1ba61ed43850c0c455cf", - "0x8f7bbaaac458bada6d852fe665c87c646133bab16c0d5136c3dc922095b9d647d93a9de7671cb7bfd4cbd138ae0709d1", - "0xb76f598fd5c28d742bc1a81af84f35f1284d62239989f1025e9eba9bece2d746a52f246f9bb6bcfde888b9f7b67fc4f6", - "0xa4c665a3e4e25a7af51e433c978573841bfa2c75c075e17dd1f43b2f0369249f3d3a46ff51051e8ce7da528b0fa98d16", - "0x81e0992e7c1c54c21cac32e36b90b25e1e5b72aac99c953c3c4d019eced64d7e316cbc0840204a4a51a4ad17d8b1d508", - "0x96d7a69eaf2761bf0e5ebcd607b134d5dedba8e262ca1d6d3e8fbf23e6419a8ce1bbe4cd23b9e4b5f80db54a802a9795", - "0xb2235bdf60dde5d0d78c72cb69e6e09153b0154efdbab97e1bc91f18d3cec4f660a80311fe6a1acd419a448ab65b18f1", - "0x838d5eee51f5d65c9ed1632d042bb7f88161f3789e6bb461318c5400eaf6728e7ba0f92c18e1a994aa4743145c96164b", - "0xacfbac397ae2ff23b31bb27b90788fd0fd51a50f8e8c9f4b31be8499194252014f0b1972b204aeb9c2836a20beb3c868", - "0xad85789bb62b60e9768bd330a31a16f711b6018445af6a47646f318f12df8d4d256ad00d1ed7c3afa4e98fef73c6c610", - "0xb2349265be33d90aaf51362d015ce47c5ffe33e9e6e018c8c6e39336d9327ccdd13d25e792eb33b43ed89a162f6ac2fd", - "0xaa458aaca6ecb43b6e45ea72d02682e5a7dc8dc22782669a0628e1638e73999319f011803f4ec8cf072467bf2c49c629", - "0xb9e6c9f2562e90bd3008669a42151538b70faf028cc5bbc09fd6ab3febc626df911fcc65744a2ad793ecaf3f91a1f701", - "0xa37185bd96faa526dfd3ddaff89b1eb29ceb4597bfc7e346bff9d6b3225b9ca87cbce0db94f05243c7232ead5f6607e8", - "0x824fde65f1ff4f1f83207d0045137070e0facc8e70070422369a3b72bbf486a9387375c5ef33f4cb6c658a04c3f2bd7e", - "0xb0ed68167a67490bd7d7d49e83341606d6e6fdd99b82e46747c2190d270719f81c5f5f8733646c246260f438a695aa3a", - "0xa87c2f13f2a824b7e2c39cfb63ca7b94ae6a11ade0c6b8e83f5092b933fa8b6157a5d2f09c23081f49d35cc85f5db36c", - "0x88f5e795cb36ab22bdcff01caca0e9d04db463c3d88cf656c3a0e0f5ac864b7092c738758b4c8f3b65e31995c6aaf267", - "0xac66f3a7041586ac1576e33598f01921e16d99afbf4249c3350f0ee1654de98bd37a61c243eb6a18a942db529e36af0d", - "0xaca69a4095567331a665e2841210655636a3273d7b7590e021925fe50757617898e1883532f9cfd46428c2e3d854f9f7", - "0x946d585d7aa452d37a8c89d404757c3cce2adf2410e18613483c19199abd88f7a12e206f87a43f6009e42f4e31ed20c0", - "0x9722c1079db7e2e1c49756288a02302b43b8fd92d5671585ac1ea7491123742a2744a526c12c9a0b4c4a80f26342a3a6", - "0xb80e8516598c59dddcf13fdb7a42d8f5a52c84e01bd6a39880f4acaefe8e4b8f09cc1b1a2423cd5121f4952201f20078", - "0x85bca2f86423a09014b562f7dc613246bedffdcb3aa41fee02270c13e6b00c8d6704dcbfbafc5997df6a90c7fc08c29f", - "0xa36dad4f7cba9f4cc843fe40f6240e1973a4c412cae29b4a68712598523cfaecb05272fc47d30772bf06906b5a26e282", - "0xa3d8610c2522d330df02511710e52b1d9bdc9f2b156deca12b1bf754266caeac4f449ed965d9863558df43ce9ae65a44", - "0xb3e313e79d905a3cc9cc8a86bd4dba7286fb641c2f93706adb3b932443e32eff2cbed695beeb26d93101c53d5f49d7db", - "0x88d8a32231ff2bfc39f1f9d39ccf638727b4ead866660b1b8bfbdf59c5ab4d76efddd76930eff49ea0af048b2e396b6c", - "0xa90d9502a9785e55c199630456fcb1e794bbeb0f5f8c022e66f238a0789998b126cf9911fd0b7d463b7706dc6f9ec128", - "0x8c627caf25eae6764501b9eff35aa90bd4f24952cad712aae20344579e83ecd104ad1f7915edc4f9023b17fddbdb4cd7", - "0x87d4b20bbe2dcd4f65f4e1087f58532d5140b39a5288e1a63fc0b7e97a6a56946eafdd90ba09300c3d1fef6356ac6b7c", - "0xa18f4464cf5cebade8ee280fa00e0917cbf1743aeb0dacc748ab68773b909e30dc60f40fdef3041b5f082e650985f7a6", - "0xa6ae4fd03fbb4e2150795f75a241ab3a95c622b4615f553bab342a1803b86b1c1a2fc93bd92ee12786bf2de22d455786", - "0xa89bc7548ea245ce9556eeee3fba98a3256f87499f54a7c5eec0c43b9fb4ef2fe8f6810867ed0df814a88ee100c245af", - "0x97f1a7370b4f5acf83b466f519da361c366915f560385dd7eff9d53700ad81b25c9862bc71d35428e82372a5ae555ea0", - "0x825359cfe68ad6a75578a94be6419179e0aa088170b6c20fc5c249dc3be7a260d687c93d8d8a343c7c72c2ed6a716de3", - "0xaa9b9cc039d147677aedd1e47ad9013fcc0da0164070ff7305b18e5786c7fac0471368637a3adbb78f3af174a5c1592a", - "0xa61687511b627bde7b3977e9a34cb7fddc2aaa509a7b99b6b6c7b97133845c721e1e69f99758698d82cca265d8703610", - "0x8853c582e86cf916750d670a621246a63c7fd78f68c556642053bcdfa7937de58885d728209736b7d5521b591387e9a7", - "0x82b8c013f24fe64b8e0337ae8b6a682cae336b8404eafc1404744f80f765efdb8b2873d1d3f31141e8dfe4d93346ac56", - "0x866ec39b9eda580d96bc2bff76af5cd4887b6788675149ab33bfefe38db82ad01b8d64c6b60704210918f3564cde1110", - "0x81337ebe90d6942d8b61922ea880c4d28ebc745ddc10a1acc85b745a15c6c8754af1a73b1b3483b6a5024b783510b35c", - "0x807c510df25c0ba10d4aa06a462e02f050c69a977c64c071401ab74f9ac1e60788aa504743b4cc1982da835ff9ac2541", - "0x96b1c82b85cdb8a7026fd3431bea9cd008f0261ee7f4179f4e69a399872837ab836a14e2dd45f5448d54800a4ae7c7f2", - "0xa48b1031ca2f5a5acb4dbdd0e9a2b4e9add5ccfc0b17d94818273c8df11e825193fade364e0aec10f1ff91d57d03a52f" - ], - "aggregate_pubkey": "0xa825959280c88c6716f6df807204feb84d745dfbee3cb3474ce81a1ef0c4cd142e5ca962a0335e68b7b4266769d631b8" - }, - "current_sync_committee_branch": [ - "0xbcfe80e1d24fbdad7bc058b011403a4c26cb56967654494cde51517f888023f4", - "0x4710ae46156553fee6231622052f7fb2f6945cdb59a5501cef824b1925c87445", - "0x6df62e05ef3e1ac92c659c3a519b8fa651d1b649c2b46fda58c48cdeffe8337c", - "0xd26ff22ef5958a707a8d98eb1ffaaf1f9f412e816c04b79f5434cb1792ccf608", - "0x90d5e61f0af673ab4d8b3ab0b978d05142a8295a163b198e6dea9d8c8f1c6d89" - ], - "validators_root": "0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078", - "block_roots_root": "0x5078f286fa90b88a09dcccd4ac72f6c3615b77c0ab3132508cb8c0c07b20282d", - "block_roots_branch": [ - "0x558b658b1230e225ac3adce3daf0b066ed0484b4a768d55b74ba81579ca0e5d0", - "0x2cbb27d38065ff3f230247af918e67d1998b67bc7e2ce6c244bab7146e16b8ad", - "0xc63bfc96585f424385e3b4b39ce46e957017b716c54d105eb7e07c841d1d4309", - "0xe662b38f57427b58c46b09980db3856f17e56b60b36bd5471823b0f2cc1b6467", - "0x8b3bd99618b1e50cf284b4c3b03d0cc272312bce377d585eded77154aa5580a5" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/initial-checkpoint.minimal.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/initial-checkpoint.minimal.json deleted file mode 100644 index a7e48f459019e39af7e6eb016b3547840b7c028d..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/initial-checkpoint.minimal.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "header": { - "slot": 3616, - "proposer_index": 7, - "parent_root": "0x6c5e8c7b32b7bfbb250fa8fd7bc348d7325fb2bfc869e4c506af6802fcad87f4", - "state_root": "0x3e467e3429a1ae36572fe3fe1c953381242e950254cf97c7527a8cea8aa6c9de", - "body_root": "0x7da749680d2b0b4f779047fcfe7d0c13d247f6d23478817fe9c6fbe07993adb2" - }, - "current_sync_committee": { - "pubkeys": [ - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c" - ], - "aggregate_pubkey": "0x8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6" - }, - "current_sync_committee_branch": [ - "0x46af3f54acbea439b63aa5bb699c8f25ff584b23912366788f7c8e95011ce324", - "0x41dcb71ec3b3940399118d28e09fdc58a8e33b818b8c5cbb933c59929504ca08", - "0xfa53febb29348e3493a50c0e7c6d35796bf69c54dfc6f42f7600612789d0ed6d", - "0x5e7ea1693066b604fc60d4657b43e7a4aafd3f4f54d9a740d2abe765e92d8385", - "0x16c9bca64a82e80c23817bfec345d088e0adc3865e392965c1244f97979f816a" - ], - "validators_root": "0x270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69", - "block_roots_root": "0x00f6bbdeac1e1a922a9bf0e78720c0bffe558d8195e8ede8cb72bbd295f242f2", - "block_roots_branch": [ - "0x7a61086fb9e53ab4dd87243d6288c51793696168a73773277630da5b20bf6091", - "0x60733905cdc5dd65d05161bb3138eecc47d6d6057ab36b0d36cf5a3200484143", - "0x86d7de634ae45de5b3cbbc562dd976de7d06a3d96f83147413536e6b108c7a39", - "0x0ada571c9e0da6fce8dd13e6d9ce173768521ac32e0af456634556176789fa6e", - "0x2341538fd0aafbc1ff0f513545e5dcd4b8905dc9e00d6173480c18a4e8086ebc" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.mainnet.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.mainnet.json deleted file mode 100755 index 3440749102a7867c5d46fae39b15b3459e623136..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.mainnet.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "attested_header": { - "slot": 4066880, - "proposer_index": 1904, - "parent_root": "0xcab3b6fe3ca0c3ec6f9a8290d1cb3ade42b019f399dca754060518eda41fd429", - "state_root": "0xc47d2d0ec566de4bd41082ee14aa6294c05f264a4fb6a73766c75df917b57a55", - "body_root": "0x5dc66f1cdd8fe849e0aa63b096a8083c16b00814f37edd213819852cc43317d6" - }, - "sync_aggregate": { - "sync_committee_bits": "0x9fffefffffbfebfffffffeffffffbfffffdfffbffffff7ffffff7fddfffffaffcfeffff7ff3e6ffefffffbfffffdefbffdffffffbffffefffffff7ffffbfdffe", - "sync_committee_signature": "0x9146a1a3cc2520a69415103446e2c30676fcd164ef2beb42933bf4beb753f5e94f28fdcb6296e3ceda302b2a93c716d515a890894365a36ed535e099da76c7e585b8d6ed5fc2ec3c316049b14da844add39bc79cd3829a455f552438669ab70c" - }, - "signature_slot": 4066881, - "next_sync_committee_update": null, - "finalized_header": { - "slot": 4066816, - "proposer_index": 1396, - "parent_root": "0x17d77d07327f42b16d6d87a06c6ab5e56ce02e3ebc65ed2d28a64dd0c3a33d15", - "state_root": "0x9b498175419bce5a027c432fbb844f35138dc00083540a04ec0f54c77eaf061d", - "body_root": "0x6650167595152e94d0741c7a7b64d2c10ed01b0d11519f7d0c615c8a60dfae2e" - }, - "finality_branch": [ - "0x70f0010000000000000000000000000000000000000000000000000000000000", - "0x3d4be5d019ba15ea3ef304a83b8a067f2e79f46a3fac8069306a6c814a0a35eb", - "0x452c63d803b8bf301447a73b2f7a747e49f37f9c9a096d8ddb6c8302666b809e", - "0xdfebfd48ce69c56abd05de8266c76478936901404ac3869f0c068c5cfd33b301", - "0x6f1a65d993b2f461764934642be084a68bccee505bd3adc20cc69e151bc6330a", - "0xbd65074f41adb20717d733e64d219d832064b6417b90f22de6d065cbbd6b3d83" - ], - "block_roots_root": "0x4729dbb81e8ebbbae1acc10857551a3991fd5da08c27dbd304b62673edcd46a0", - "block_roots_branch": [ - "0xa3c286182434bdd4d55be8ef6e7212917dc272c204a4daff2d0244fa993d86cd", - "0x00d2df4cb3c0b06bb4f2722f975d6630a7f3c2a1ff12d635e2109f08379e3922", - "0x135a357782eb8f46d14db6e62bd3e9ad611d01f16a3e573cad9ea0c53010bdc7", - "0xdb944338cc4e7b11242b39bd83159afb122504a1934569bb500a4228b884b8c6", - "0xd381920000999813479b975b7cf0e5663b52c3ab06bb6ac66e723731e8f41e9b" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.minimal.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.minimal.json deleted file mode 100755 index 8f1ddc827c1f45ade879bfd611bdeb1cb223d17a..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-finalized-header-update.minimal.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "attested_header": { - "slot": 3696, - "proposer_index": 1, - "parent_root": "0x04a63c5dfb726c31a32a72c1c426ff89e21363223d7096486b629f1d58abe5d8", - "state_root": "0xbe20e69420cbf9400224ec5edeb0843776a2ccf945e9a3ba9311ae812cad1e30", - "body_root": "0x1d2acd1748f1c58096d1edc8badd3a1d7e1dc3c33bcb9229e4c03f3a84efeadb" - }, - "sync_aggregate": { - "sync_committee_bits": "0xffffffff", - "sync_committee_signature": "0xafa79bc0f3c731ab1eb6aeafc582a7dd1c100ea471df3af6ff485b58661b3ef8077264dea0b60df9aec2d3ca8ddab6770fc9d061462e5a6dc718146085425f863d00921c42413805cb5b4c5175f36f2087cfed740bb7d57e8d5b48352643cd5b" - }, - "signature_slot": 3697, - "next_sync_committee_update": null, - "finalized_header": { - "slot": 3680, - "proposer_index": 7, - "parent_root": "0x4d8f4fc47ad3eb045bd20cae13af6df02f96a3f8d7c8a285190ba10cfe2b84cf", - "state_root": "0xd498766d77277fe16a6a4609ab3ac3a6e9887d162d8dfffdfc9cc4ae833e4127", - "body_root": "0x9ba73bc9a4907cac0b887550e2b01a63dcc70473753ffcc243d33394cc64b4c0" - }, - "finality_branch": [ - "0xcc01000000000000000000000000000000000000000000000000000000000000", - "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", - "0x142061c4bc3673bf774cb8c7b6085057bd0ca85672b43afa2d9581b0b6a44e54", - "0x48b8cd8ca9d9563e30c1cca2a854cd7f75eb4cb013d10809b3138a72d94ea0c5", - "0x9b39523d05013ac7cbb9f43e5d6f9dc033b12aa1d6d6edd994ddc4f5efe7be9d", - "0x066c9aa26107bc8cb28bc73e518da6cc865ec1d67516b6ca24663b6b7ae3cb21" - ], - "block_roots_root": "0xb15aa2483811d8c5616cb93710f4fcb809d97443caac9de163f943a30f385db6", - "block_roots_branch": [ - "0xf7a43ad317417daa4c2a1e93c54895895a824ef1e43320eb44eab16673da5a61", - "0xe4b8d640660f765c2ef4dc886025dc8e54c6e70b66192582f42837ed5e9d8d41", - "0x841f113dc81e76419b6cdec8b0cf2fc20f9381492ed3c79e9b49179b4d3eacbc", - "0xeb5fdc4d8b5282b653ecbc9caa93bcfe482f6d6a32cbb0d9eb011bef947579bb", - "0x1f328cc5640efb191ae6aa86223b1aa9d083b26ac3e1fa3c071327bb09dc5727" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.mainnet.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.mainnet.json deleted file mode 100755 index c445bfb48a23bfae70ff5cc1a2626eb0c8072398..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.mainnet.json +++ /dev/null @@ -1,563 +0,0 @@ -{ - "attested_header": { - "slot": 4063296, - "proposer_index": 944, - "parent_root": "0x3e8712719bae71b519a25b7eeea9b412acb882ba6bbeeadc596b1682838cb6a6", - "state_root": "0x16add3b722d0b9075e7a3fe9cef6d237698053edeffe1b95f5b59a31f1bb4f1c", - "body_root": "0x724f1d72cbacde14baf82d273d3c4eafbbda3a56cb0f641cbfe76a426657327c" - }, - "sync_aggregate": { - "sync_committee_bits": "0x9fffefffffbfebfffffffeffffffbfffffdfffbffffff7ffffff7fddfffffaffcfeffff7ff3e6ffefffffbfffffdefbffdffffffbffffefffffff7ffffbfdffe", - "sync_committee_signature": "0xb2f86b054283455ef620757c13531ef64baaf8899527864aee1b86301c62c72b6c979865579e04880790c1ea418b53a717d7b8b85c70fda1760b2436ee04a25b13a8906d6aea8e94facef2444f33e4d5b4e91439adb981900c253186e742f1ec" - }, - "signature_slot": 4063297, - "next_sync_committee_update": { - "next_sync_committee": { - "pubkeys": [ - "0x936749ff47e5be307546564a5a4615bd8df52e2590034b2db19846939af3595a79ccabf0f6ff52ca46b9a1de3efd47a5", - "0x93fda62b785757b465e6f396f74674d5b95a08c938cf694e66beed7d2f317a4c9d736cb54d7c221d61e8cb3d64dca3ab", - "0xa39e96e33076fbb49c35a58b6e386d22fa7378337bb8b0d47699264f78e5ae8dc143f1f6d5f8b371deafc5c875adb60a", - "0xb5f69b7614fe07889b58142d7b438186d70214ff4cb209b6f271a3bf2bcdef5e6f1c7e95dbf5f2785aa471f0294cd029", - "0x987dd977d6b8d27c4065b61b3c078ec9ce3871fec02844ed00f5ad28c42f9cedecbe830ddd19e11a5c879b01eb0f8f80", - "0x9969ab62009b6aa81734579346766937d22ba73c008d24bebc183d1b3d3cfabc90b47f41b29bc6e23d70165594c2e774", - "0x840ac0e104b22eaebcaa1e49be43689f45434a6c5ddb71eec577323f38836ada5464b317fa3862773132166f2ac0a536", - "0xaf96a83f97ed0696fd29e59daa24e1857e16371f67089d08129f9c236753ea68c93590dce4d32c9e9818a21014da6f0d", - "0x8779a0376579008d0daa99895f548dd091b3abab37e91efc9cabf08835068c983ab0927e7c8eb0396eb83a5e0a713c56", - "0x99289672bfc48d2fbfd51a32d0d7f9d03fff37b8269ce45cbae1e9a091ee3983774d053efe8eae2f2d3f22a9eb532457", - "0xb60df25a7ac1ad14aef7e8809c4bfc190b715f94f14b0534cc2e4015ff6c322883cbdc5309e05d67dca4bc641c69725a", - "0x8c22f1f2a530879a93e744397fa6acca57b01fb62b62188ffa7487464815c605e1520ff4bb18e832753893649ab80d62", - "0xb3acfe8f25eb5153b880a03e07760f7fa30beca475843581b4878ac0412cd2038117f25a48c152e6d60ec16e4b1e9a45", - "0x8163eea18eacc062e71bb9f7406c58ebe1ce42a8b93656077dd781c2772e37775fe20e8d5b980dd52fdad98b72f10b71", - "0x8100b48ac2785477a123a7967bfcea8bacef59391680a411692880098a08771ff9786bd3b8dfb034cae00d5a7665621c", - "0x83bbd31e799ac14686085868e8ea7587c7c7969c7015bfe45fd8e3a3847ad5338005f9cdf58396b2ea833c4af98bd9ca", - "0xa59249e4dfb674dfdc648ae00b4226f85f8374076ecfccb43dfde2b9b299bb880943181e8b908ddeba2411843e288085", - "0x9793a74fa578ace75b083578277a1ae8766d41a5c508b0f1135fb97dff1d0826002393a7276b18cbc4b3c5671360ce0b", - "0xa734a3c947be4c7e6704639d4400aec2ccf5f1af0901b41a29e336afb37c53bf168774939ce51f32d4496bce1a32e612", - "0xb96a11048c7c327709d52e72e6f6ed0b7653329a374ea341ad909311b5b303e5629d6dcf11dcdb195e8c7592ceefac21", - "0xb3b7af9258af054362d461a74fcfeb6dcf3a37b6e33b6df32f8317d50d8be8e1970818a6e41c8232b89e1c8f964c6c1d", - "0x907c827a4fb5f698bf0e6f10ca07741c5b8e3ecb26aa53f938ba34ceb50c01be80c4afc5ac4358a5fda88eadea0cbe73", - "0x84173aeaf3d96368dc7ca1ad5e5575da279113567e5815a364a0356a720c5e08cb58ca1fdd891924f4871d3eaae5de40", - "0x92127d55535bf59f2b00511c82f74afe90529d4abfbaca6e53515d63303fe52b4b22383fb026a2a3f88e96d2bd235f6a", - "0xa9f261d19934fd26458421551e91f484d7a1522a7e7adbfb28f6371102a7650a5ae6efd49d9e33b03aefde647d134ce6", - "0x880b4ef2b278e1b2cccf36a3b5b7fbce94f106ed9fa2820cb9099a7a540a57e9fdeef5c0fb0a743049828fc2b8c46163", - "0xaace45334070c51cc8b3579598d4cd8cda2153bba51f56e3b1fe5e135c83ef70503c322756b9cad9d3cd28f1ecfc8227", - "0x97578474be98726192cb0eac3cb9195a54c7315e9c619d5c44c56b3f98671636c383416f73605d4ea7ca9fbeff8dd699", - "0xac5c01c51dac6ee1cb365c9b03f09906d9b7b9b4d1b73c44d9e8e06823025d7070f242898a975420bc87d6372382cab8", - "0xa9a591fdd18aec8746435eeead0a54bb88e055f55e91ffdd9bc663ce0bc2937fb296034ebb959d6adcf9af94bbd2f49b", - "0xb495404544c9335d5f184cd6873299a93174905fa34c14092f67d9b8545e71fab29545bc337e380dffcb533f7390e9cd", - "0xa3b7fabaabd4c2e555dce46add6c56851b68308c1bb7253576a9f32eda141522317b5c00a28b384ead3a880b8e7e40dc", - "0xae0beb452af7479134a7fbc31a5f59d248e8a67d4c7f73a0e30a51db9cd33a1da3f0ae947fa7e5983aea1343e7daf06a", - "0xa9d9a295590641b2b09d8473b50c0f6e036e1a009dcd1a0b16d84406763b4b078d5de6ca90898232e34f7f7bf147f61c", - "0xa5fe3dfb5031517bb8db0d5ade1e9f438e84bcf23221de003b03d2a2f4ea97629e81c79abc3769bdb8c69a512c189b91", - "0xb614910b247c6ade31001b0435686c3026b425b9bff80b6c23df81c55968633349e1408a9a5a9398a7d5d6ed5d9d3835", - "0xa19e7db50604f6b82cc28bc97135025459c8eac107a1c482919df10b8be2e4f24fceb93b963c0b8ac9f402e2f6ebf387", - "0xabf7da952c9d8f75fcc67fa7969fac0b26d4dc3e022961ed674ce85d734f11620a950fb1fb0ef830fba1d8b5bc3eced4", - "0xb382fa28670a5e14dc954b2db8ace250c73df71ab095304bd8ee28f455ab26cc54f82775a831428e110d1a3a2af709bb", - "0xb284286dd815e2897bb321e0b1f52f9c917b9ef36c9e85671f63b909c0b2c40a8132910325b20a543640b01dc63b48da", - "0xa2b1ea43f51460b3cb83657b4e296944658945d3ad6ae7b392e60f40829ba1da6a812d89f0380474578cbd0ab09801ac", - "0x8b886448cbbbeb40be3e71ccee251632186dccb51697f69eb5c746000b4327fd85be3a58fbd49f1df642a37f6388a8f2", - "0x9161ba220130eea190932ecdad9f114e385a31ec51c71cc8de451ffe5e75abcda37227c6a77f7090d4d8bbf134421bca", - "0xa24d05b51c7c128bb49979cbd9019e6618545d95275a44b5c3d1d03e71bf2ebffdf43fff50c30846ec27d279043cef4e", - "0x8cd1c73b7fe915e7169d351f88ade0f810d6a156fe20e4b52c7a697c3d93459e6d6c2f10dc1c6ec4114beae3e0a8c45a", - "0xb95e3032192bdc064306c683982d885f0ded8b907a532f15526a257ffeff2c8bdd7a2334c10d74b1484909b2e3ae0e47", - "0xac754a42f279472760bd36dd0cf36f5ec685e7fc2970c275811a70cd05843f94fe21745dddbd54135144edf1793aa0cc", - "0xb26b4d483bca73d3f3a976bb595a0e40f9a42094e0febbad3a1874934be1939a1b362ee4ea14a4f5cbfa9b1392796a12", - "0xa841594e74b66935efd295a6c06e2be03cc8c187b277cbf5cd2f590630d4812801ad55f3e502736d126441a2f22f1867", - "0xa845a8a3299f8e5fcf72358521a114c6077251e62ff6a885003f7281b0e1ee33715d9ca0b0082fbf7cb9d452d531c38c", - "0xa7b86e4f1366da44fd59a3ee68018a99c23ba3588789463bd88b0177a9b94030b58cb879a506e64421af966f261eaa86", - "0x85822227f6a96d3b6d6f5cf943e9fb819c8eaf42a9aa0bdd1527055442b1caf672522762831b2dac397af37a1c5ed702", - "0xb455f751232de0a48440d09983f4f4718b6169907979c9f282acf7177ab5b1f338fe1f2acd8d0bee4b4aad61d0340839", - "0xb97b2f1b2d6d744f2322812825ea1cf91453dfe1bbbb2678776e40e7d0fe682239d0dc8053f94d97e5a9678232b7a71f", - "0x815f53751f6d3e7d76c489f3c98d2b49214938cac8c2b417e2d17bb13446c285fa76fd32a97e9c4564a68f4faa069ad2", - "0x8ed7790f87f6975e0f3e501901b0bec1778c88bf39588989014c5dda76c2163732e7e5703c9cb2c1a6144ffdac5dcbab", - "0xb6cacc458ca5a0f04836c5640d34c70cab34cb5c87df28579a0d5e2c72edbc092814bdbe902747ebe3ce36808d8f4dac", - "0xab7c058199294c02e1edf9b790004f971cb8c41ae7efd25592705970141cdd5318e8eb187959f1ac8bf45c59f1ead0d9", - "0x99365fe5ab8ea8bd768ae7181a6ba49b79d240f512ce309b02f09d465fea276298ff55b5b9cb5b4162a901b390606024", - "0xb1afaefc9fb0e436c8fb93ba69feb5282e9f672c62cbb3a9fc56e5377985e9d8d1b8a068936a1007efa52ef8be55ce9c", - "0xb37334c41a3456b73b61d0eb0777260af9c2e400bbec0e0c0fdb45c39ce0dd19f021d9760f35da801f20486c6be30e9e", - "0x95757096c132e7f6c096d7b93a5a0d2594d5e609b9f13c4a9f878e95a389fa1a111b185dc1fd8f7d98b737dcf8d2af60", - "0x8f71f8edae59d6936846d8b50da29520f69b339f574ba9156d3d5f0cd4a279d36bad7ca7eb724dd48aefc4ca9ce26bdc", - "0xac2c98a0ab3f9d041fc115d9be4a6c77bd2219bb4b851cbee0d9257a4de5791251735b5b8fad09c55d16eb0d97080eff", - "0xa09f11d2bc6000d12a42b545ddc29c1973944a39787c5f27c96d4f6aa0d9c8fa9c479f2ed327fbd30376df3fa5b7d2a8", - "0xaa19a75f21a14ad5f170e336a0bd07e0c98b9f5d71f91e784d1dc28a5f5eb6870a4eb35bb41edcf9e6efe982ae5c2c5b", - "0x92a488068e1b70bf01e6e417f81e1dc3bcec71d51e7eabbc53b6736e8afdb8b67d191940fe09c55783be9210e1cbd73c", - "0x95cf2e038c790ce7a2960add7ab44804375f04ec6829f8cc63793dfe9fc48c7471079f81b932726509394fd3d46a52e9", - "0x87fdca39618051c4b3f03c816b13df2d4cd4c7c564e3d8693dcb58145b7b3b3db7884b0125b1e84d9bb82e91bed8bba3", - "0x815922ad356f490910e8cc3b0f7d3934b5e28c09711b5151ae8329876670f3de6d7a3a298fd97b580ac8f693305afb21", - "0x8bb4d08318386c91a0136d980a42da18c05743a5c52a861ce52a436e66a8ebe472dac7f7461db32ea5da59a23e9bd6c9", - "0xb0053550040ab3a3996cba5caf9ad5718867b5f5df273ed8c6520761571f03a94e50b5f8a6a8c42d725383cce97d3cae", - "0xa42c46a7e617d78b12053d7783f0d175fd9103db06d0c6982b38893a20b72fd8ad8501eacb3d47be06fd7c3ad89a8159", - "0x838ff6630dc3908a04c51fb44a29eca5a0d88330f48c1d0dd68b8890411a394fd728f14215482b03477d33f39645dceb", - "0x8ba45888012549a343983c43cea12a0c268d2f7884fcf563d98e8c0e08686064a9231ae83680f225e46d021a4e7959bb", - "0xa59a59db246f981e9c8e54aba52898c714d9787fef8969a6d8677fe6dec82950ff22a1364393b579f3e596cdf5bcd7b1", - "0x8722f3267a945f7123c1df8b6c2122456d81fed56e6369ba726b023c01c1f6738fc12e506e260d99e448fc920fd5e5af", - "0x89d356593ec09d838cd89306ce83c060ee797bf9eec8523f581cf263925699ef0f7161a790bd00bb09681534ed05ac82", - "0xb97fb8ebf2ee1bae5914cf96e5a07887ba41e712530eb2096ace318e989c0ad93060cfcf40507d927af6c7e945bcc289", - "0x88015bec478fd3ddff72efda0e8fc54b74faf804b0a3473cca38efbe5a7e6dc0be1cfe3dd62b8ac5a6a7a21971dcc58c", - "0x860c0eaee51b7de26e99033f352aa09c093943b59237f1313ecc35b0d711509bbe9f939c4bd646deb7de8103eea9ea13", - "0xaf9d13103868c854821ba518907b067cfba025d739125f1e9cce0a04fffc3a2a1f25506c1209a0cfe1d6c1572c229ff0", - "0xac7983d50ec447b65e62ed38054d8e8242c31b40030f630098ce0a4e93536da9179c3f3ae0b34a0b02aad427a97ee60d", - "0x8261f7e644b929d18197b3a5dcbba5897e03dea3f6270a7218119bd6ec3955591f369b693daff58133b62a07f4031394", - "0x8fa2d7b22af8e6b82679ebdfa13efdcb34289a554653ea6c1b16efb9f957f7fe64df787e7b03d8cdc8a732b91c916bd1", - "0x897eed8c65712e9b1ed8213abb85a6252ec30ab47eda4e36aeb8a72447ce7972861bc97957bc321714328c64af27544b", - "0x998c9ee20d33f96a2388b1df642aa602bc8900ba335e8810baab17060c1eace4bc5203672c257b9ae750008b707b0aa1", - "0x92378adc9d56996ce8ecdb9ed6510affccbcfd96712a23631edfd6ffdb1469847aa447db6b2bf61dad416ebcc5b7d1a7", - "0x83fc998e050cb1004fd016c7dc62885b07a95fc9b219fd6fde8ca2824c647f331f6b18ebdbd14569b906cd1ca1066189", - "0xa87c2f13f2a824b7e2c39cfb63ca7b94ae6a11ade0c6b8e83f5092b933fa8b6157a5d2f09c23081f49d35cc85f5db36c", - "0xa89bc7548ea245ce9556eeee3fba98a3256f87499f54a7c5eec0c43b9fb4ef2fe8f6810867ed0df814a88ee100c245af", - "0x8d52413f981bc611427ad0534d25e914113d0ebcd6960aab6421608bec6648b89ae4b2ca2153c57d3cf4f1f37212aa5c", - "0xb0a4c136fb93594913ffcebba98ee1cdf7bc60ad175af0bc2fb1afe7314524bbb85f620dd101e9af765588b7b4bf51d0", - "0xb7ac87da14b783914ab2e914fb7b536893b7a650cdc5baa1f3b4aca9da77b93a3336671335250e6467a8cd4aa8dc61e9", - "0xaaeb0005d77e120ef764f1764967833cba61f2b30b0e9fed1d3f0c90b5ad6588646b8153bdf1d66707ac2e59fd4a2671", - "0x8a60e066b13eabb372067a6b08704f3b6b98c0d468942738768127ebfcf122aef0ae2303f361c6338010fd371646769c", - "0xb7de6d7a4afb05984dce153e5570b104338265e45c8f0156f4d45c458f47add234a479e01c02d3c1817c170b5b65b100", - "0x983cb6bbfe83bce8326e699e83fca01ea2958c09808c703cac97a0ea777e5a5f3f5bba9169a47732de7459a3c7af47d2", - "0x8db19f6dd3789df179ab606508ca7e3da7967ad4340f630bda83df54c197d6bd3a14980c87fe10cbced7678c0c300ef1", - "0xb4e6bb207e08a1e096f6b27a5a60effc74fc8db0b6cdebc9ddbe88f434f4c8e0bd7fa77e015cc309db0f0922bd05b3f5", - "0x951d69f32685615df304c035151bd596d43bc3250f966e0c777544c506e3035d031afa4a3fcca1b85c41a4a041aefc01", - "0x942bee9ee880ac5e2f8ba35518b60890a211974d273b2ae415d34ce842803de7d29a4d26f6ee79c09e910559bdcac6d3", - "0xa0e68d24f784fcb2b71acc2d5871285623c829d0e939146b145e04908b904468a67c07a2f156e6b17bf531adc5777c4b", - "0x921da028f26a61a034f5425d6618eeb61adaa8ff10141bd65ac970adaefd3737a4bbd77d8a7a90cccfca35b0f4d585de", - "0xaf6911edd6c7ad30f905a0a3f78634808832fdeb4206b006934822d673bcced8e378779261b3c4b772b34b8871987f57", - "0xb044857d879d06e9be5dd70498b27a20aee758ef829d37d0ea12b92aa84b9d3c6194205368014d942ae0517cf6d0e201", - "0x8f90e72a54e6894d511061957162e753010812346afd4d90cfedb678b99ba1aacf2b6bd0e49b4b0e684da8082a048619", - "0x8317974fb1bdd174c7ef81a2a6478f887f44c1e8680c21730974e5c440846c4d43a76a3e90334b39508f507163e2ff8f", - "0x976eb5543e043b88d87fda18634470911dfe0e0cabab874ca38c1009e64d43026d9637d39dcd777bc7f809bbfc3e2110", - "0xb53fb1956a2a34a840de4ff0b5b1e0e2fb78a21ac8edbce6be6c26a4b4de6d37e9dce799110a802a344e8541912353d7", - "0x85bca2f86423a09014b562f7dc613246bedffdcb3aa41fee02270c13e6b00c8d6704dcbfbafc5997df6a90c7fc08c29f", - "0x8bca3560946189e4984126acb42153d8dad0b60e7f86518b55ea9ff7c899c9ec12821850943b6adeffbe9363bce4d217", - "0x952a95612aecce4321d2c17aabd2fb260b1cb41df5f76f5b82b46cf818d7a4f18e5e2944cddcd2280a993c0af4f834fe", - "0x9722c1079db7e2e1c49756288a02302b43b8fd92d5671585ac1ea7491123742a2744a526c12c9a0b4c4a80f26342a3a6", - "0x824c8a1399ab199498f84e4baa49ff2c905cf94d6ac176e27ec5e2c7985140dbaa9cc6303d906a07ab5d8e19adf25d8a", - "0x94bbc6b2742d21eff4fae77c720313015dd4bbcc5add8146bf1c4b89e32f6f5df46ca770e1f385fdd29dc5c7b9653361", - "0x90f7fa9a30d9f2812a20db97b3d03962a5b59719385c1881c61009e4c049809efe378b39cf74b64daa981358edd691de", - "0xa4632399c1a813e41fb2055ef293466098ea7752a9d3722d019aa01620f8c5ecdc5954f176c6c0901a770cbe6990eb11", - "0xac79f5491dbbd0eb47669225e781f94b98d04947cbc55baf287365831c100248bd0b39c911ac09b518715ba1ef0602f3", - "0xb9574edb9567f07f85c7c2e6ca6c02d90ad7c7b87d49796f1e2fb7240ad071fb755cf13ca8678668a56217c62df168eb", - "0x889a5cf9315383bf64dfe88e562d772213c256b0eed15ce27c41c3767c048afe06410d7675e5d59a2302993e7dc45d83", - "0xa0b3dff15982a38a2f56d8c6cfc5c5543c045bf2db24571d23387ccab42abe2756f34d5f0bf6a426bbad3c358b8bdb00", - "0x8009dff405aada0798a6cb7f418f73017d7a569a7576aff51348b15913a5e639dd232657cd775cfa0dd811ae5e301241", - "0xb31949c4a21181a54928f25f8598ea3dfcacab697a5653beb288d218d312133e5a93f434010ffdab3f3ebd0b43b207dd", - "0xb46f481155df4c4d576e5d76f1d4054e1129cc49398533ed32d0f681701276cecad4759e47b818f20d6a087989449529", - "0x88158d759eafd2205c770f166829fd61e8f17b2c13f440777eaf45f4d88a6e2028bc507680ff435882d5fb462f813735", - "0xa922d48a2a7da3540dd65bda3a8b5fb1f1741604e2335de285ac814c69c40b5373d92bc1babd3e4b2d32993f251c70b5", - "0xb7efcb232d3b639921ce21e80744c293ea77e25982b609e8cc82bd3999a734ca04ca43f41d9c7c15d162e0bbc3152495", - "0x96b1c82b85cdb8a7026fd3431bea9cd008f0261ee7f4179f4e69a399872837ab836a14e2dd45f5448d54800a4ae7c7f2", - "0x8295f613c162159f368340ca0fc2fd7776f7ad64eeafbd132bd3be1f1c30b5fbdc5f107f12fb0cff15b12c08621f457f", - "0x89e3ff351ce4f0d43cbb6385bac30b37431b31c7c073bacedbe0a60af3dd372aca672c6c4b4d05d2c4b7a040e80f3ef5", - "0xb075db32979df905cef986cfcd6db823ac21dd4013cecfe088885390ff8acd18d76dec793b80db5f7779426127daed7b", - "0xa18f4464cf5cebade8ee280fa00e0917cbf1743aeb0dacc748ab68773b909e30dc60f40fdef3041b5f082e650985f7a6", - "0x8fb51e3ef3c1047ae7c527dc24dc8824b2655faff2c4c78da1fcedde48b531d19abaf517363bf30605a87336b8642073", - "0x93e4d7740847caeeaca68e0b8f9a81b9475435108861506e3d3ccd3d716e05ced294ac30743eb9f45496acd6438b255d", - "0x946d585d7aa452d37a8c89d404757c3cce2adf2410e18613483c19199abd88f7a12e206f87a43f6009e42f4e31ed20c0", - "0xa4cfe97f6e61e45577ed6ce6eb7d1d9aca9e323b79b30736b407000555bf3e2ecbffd6314585b09000f09ee8381903af", - "0x87c5670e16a84e27529677881dbedc5c1d6ebb4e4ff58c13ece43d21d5b42dc89470f41059bfa6ebcf18167f97ddacaa", - "0xac7e49f2059e99ff65505742978f8d61a03f73f40141a2bd46fde5a2346f36ce5366e01ed7f0b7e807a5ce0730e9eaa9", - "0xb471c72bd2971353f4b44248b8e6cf5316812861a88ccfc20fd0d89a5e010428c387228b2f6f14c12f79e31afc9d0753", - "0x86b3a4ea9b1fde00cce79d5ae480353d60cb6ddce363c535bbbc3e41a4b8e39fcf2978eb430091ae1b10420d43193971", - "0x8167484b6a9bcbdef21464cee959a7a6aab5ac92ccc46214f4a2ed520cfb4d4de8917f9b9bd6fad71e66c17bd831eeeb", - "0x86ca8ed7c475d33455fae4242b05b1b3576e6ec05ac512ca7d3f9c8d44376e909c734c25cd0e33f0f6b4857d40452024", - "0xb49379bbb9f954d2ef5574199607bc6b3aa2cc3b48dcc3745cc77406bba2a394929844fec1b87c4ce65cd0ca0f83062d", - "0xab6366a7c6da8ca8ea43a3479e50ecf9a1f3b20ec01b8eae1d2a21ba2223a4ce62615836377c6395580a079c284947d3", - "0x9175ec473efbfaa029aadf1584f986371ecbeccd82ff6a52d1f6c66f51d7395e0ad67a5e8bef0600ffdb348978913e6e", - "0x8cfcdfa192b17321be4e447204e1a49ecaadca70a3b5dd96b0c70ab64d1a927d1f8c11a7e596367e5fa34e2307af86fc", - "0x905a97217fae8cfdc4a006b644e91b097df28e02da2f19f77e18f4b0c4aac2538ea83919a722eee5c0ff315a1daf3cc7", - "0x94b2d97448b452a986c039df1cfd651da59249b649182941556018af4ab61d2c6af82a29e69599153316f9b262efbcb5", - "0xb0eecd04c8d09fd364f9ca724036995c16ba6830d6c13a480b30eb2118c66c019cfdc9dacce6bfd8215abe025733e43d", - "0xb4d07d50fbc9634e5f4aeb884974068ea6b94e67e4527207f5f9c41a244943347d69d3c73af74d8de9ab3659d06c6d6a", - "0x8e54267871d8d3ce2a080e48786be3d97e5fc9404156436dc2a37bf05a588470b7656383bd79d58746d1667ceac54344", - "0x8277508c9aa4d1938c83b48d05fe3a440bfb50c5be79b30da1ac1853d19ee062797be19521f94b038cb991b1237abc59", - "0xb4aa92a60de61ad089cb027ef19a211c720ec0e51743b1166e3d71bac08a9ffff2f0687e250c6a7e1db866f7c4ae8f29", - "0xb9299f950db8cafd236a17f141cd2ea9ff441730749bab3571211d207ccafbf5a3990dc137400c405086c4d2879ab91f", - "0x8c38ab2a9558ac41c6ef736a5560e5960102e92f710efac3f631367a3f6d7227e0813579f349e661116bb29b2163b296", - "0xa5c11337eb91ce0e9b6d61bbdadea0a063beee1bc471cc02dc1d81c5dd2095315c400cbc6c33d23c77e98bba6bdf5439", - "0x8d3cba4d10f94bd3406a341c903ad144cfcfe6b61678d5c03084a56b4413bc30bd20d7a9fd5d839dbb565cc9b2aa99fe", - "0xac9f0b44105cf77ad721b97b0f04a37fddb2bb62c345b0d22a29e2870b8964d7484aad30e454c74608ce9901043501a5", - "0x8be4830a391aace561decdfea6aa610696d292a9e6b56448c6a590027df9f6762668671775272bac46ea335391ae157d", - "0x8e662149e22ce32383461ceb489b912f3c6320293d6edf61499164beaab7a265ffb9de3e0af6c95ca824d800718e1506", - "0x88b49b1130f9df26407ff3f6ac10539a6a67b6ddcc73eaf27fe2a18fb69aa2aff0581a5b0eef96b9ddd3cb761bdbbf51", - "0xa41cf5d678a007044005d62f5368b55958b733e3fdde302ba699842b1c4ecc000036eda22174a8e0c6d3e7ef93663659", - "0xa663c57b72e8acac40127fd3af579dcf9aba05910b26ed1155888543223d6558ee8e1c07f0a0e634e532ef6c5e9cf17c", - "0xaa103a329b699d4102f948101ce5fae27226419f75d866d235da8956f11367e71db5c0a179dd63007ed53f7eec333aaa", - "0x86c53fc078846c3d9bc47682506f8285ba4551475921fd388b96291741970c34b8de4210202e40d2de4acb6e2892072b", - "0xa7e0ddbae16e4491822684c0da3affecbbd17ef96c5c491ac093c6eb4e162fc7854c367535e296fd3d6265c2ed1210bb", - "0x88f0f11d0c2bf51453077cce0d3191931e73b104ee5c524da57e4eac0a88965f58b4abe423c1073f75fe3d3c666a209a", - "0xae8af784224b434b4dfa9ae94481da4c425602097936623e8abb875f25deb907aa7530bce357786a26ed64ef53d5e6b3", - "0x9408bfab1e7ac8b8b888c623bc0438b3a3460aff12436d13888315f496fdb808e9dc00894f272f348ed6aa475f848c49", - "0xabcf138d9363a73131f5bca56679d15606216bae1647c59c2857cb56818a0529c1b4b45e382273c993d62b7bcd552ded", - "0xab37a400dafa918d28ef43294b18dabcb4dd942261832f9839e59e53747c7b1bc44230967a9610b261f3abbd648e3dd8", - "0xa73b3c9d16f6c63659179b74b1aa5a0f4447c683ba38f9fc112efaccde4835e5b92c2f7041fa82cd90b2c4932704b9ac", - "0xa3fd9d8bbdc98394883022299fd9793e0c4f374d8e40d6ce89b2869d3173cb6a5476371d6095dad068ff217729f60af4", - "0xb4c5aa21659b3ae37fde62233b0bf41182fdd57c22fb5f47a236048e725a0e8636b9a595b13d9ecdf18c445f156ad7ee", - "0xb3a5497365bd40a81202b8a94a5e28a8a039cc2e639d73de289294cbda2c0e987c1f9468daba09ea4390f8e4e806f3c8", - "0x8cd49711b42af58a5aae75a38fea9ddc5e4183c467a3159b5b0629f01ba548513c577456d34c861911e85782e52c3b1b", - "0x868c13bb6bec7d56afd4e518f2f02b857a58d224fbe698be0e00bc178c1858e6bf5f0f7824fa013d5c8dd6f6e4147974", - "0x86f5a9bdeebd38fef93bf20a7451ef4c851d63f08e025a59109c68b46f4c61069a6c8c5fe90eb5af36943acc35e62f51", - "0x90c402a39cd1237c1c91ff04548d6af806663cbc57ff338ed309419c44121108d1fbe23f3166f61e4ab7502e728e31fd", - "0xa21477f0b51d73b0816b4b411c12db1e3a83698113ff9299ab2827e8da59baa85dbcc70afb831f5b0c038e0470562f00", - "0x82d09556978fa09b3d110e6066c20db31da2e18de90f973930f752970046f2df96b2a0248fdd833cbc50abad5c756026", - "0x92a346a321cfd73214be02f084ac2ff417900a1392d134b538099c92e7fdb7ba2174e9929c51b5e45bc3bcf718414dd2", - "0xac4b39bb8f0f62666a50574632764f8b6a1dc98afba5a5dad4409c920a0c0d5d2b5c2506c3a0d2f8727b7b7dce2ba1a8", - "0x8296f8caf58316af535def398a43357e48cb3b1e674b857eba1bd1b970da3dd045e22fe6d17dee4e9117f62ece3ec31c", - "0xa931bb29b6200899e8a8c257166400eff9888594daa1e37501390a1d219b019ed1b730d921a8f6d6fe62dff7b86ee387", - "0x8ae9585caa3c73e679fe9b00f2c691732f7b7ca096e22d88c475a89d4d55cb9fba3cd0fe0cedd64ce75c591211664955", - "0xb4a86fb5b0049718caead1bc036833a2caeb88e1afadbbbcb0cd021d95e1f33fcc916f0b97fc1b9226c37050e3463796", - "0xa15e0cb96a463ab81e661ca44c619b71a159680bbc04707ea5a5867ff38b15416e3abe55d2fabdab9aede1f157dd37e1", - "0x8b3f8fc8d2ec7a8db6ecadb8be90f55c1be4871bde10eb18c1773dc45dce042d93baa65b75c4688eb4125b6b7965c2d3", - "0xa4f964d672fa5579479e939d2d5dad6a5dac6fca4bcbf7d5ebbe7489f3809131667b41c3472addfe766d83202ea29c1a", - "0x86f0253db0918337e4e128e8056d2c793562c6b5cce8ba43695a02eae7df12605309722fd1e3b8c02ac513a4a49894a5", - "0x89cdbd610e7f57e86438e50874c3c7ba85afa63f5adcab9e454b5c203e4da65d74bb7cac5995a8652d10a6e438a1c2b8", - "0xaf2dc13a599c834b9af1b54a4fa675c0db92e807cab3bfc825f2c5571b3bc2e1c213cff941cc8b1080d894036f9f73f8", - "0xa0899189bba608887c6cb729580e570ecce9ca7107865ebd30def867afaaa250bac407c30dbee11b7ef6cd423269a8fd", - "0xa22542a4a2ebde18cc6aa29d5dace8b4f6720703f519610dcf01e671018392aff15728e3764730840272c9cfb074b612", - "0x88ce41025aa153a94f91f22e7b96f9342b5e0e1d76274fc70c4df7d08f66d9f7ac86e55a1c6e77693b8b01b2b38bf900", - "0xb76cb8cb446eb3cb4f682a5cd884f6c93086a8bf626c5b5c557a06499de9c13315618d48a0c5693512a3dc143a799c07", - "0xa7741c52498e0a24db3ce7699882de8f462a2b3ed5e9f77dc7200cbdf46b6cdd923b1128759909d6dddd64700c4c20c5", - "0xa211120e1bb3b10138df1fa58efb009a298b8771f884b82bb3de15822b1252124a68f3980f96122a775fb96f05ddc3d5", - "0x92ff79402d5005d463006e0a6991eaacc3136c4823487d912cc7eec1fe9f61caf24cd10022afdab5f6b4f85bfb3eee4f", - "0xa48b1031ca2f5a5acb4dbdd0e9a2b4e9add5ccfc0b17d94818273c8df11e825193fade364e0aec10f1ff91d57d03a52f", - "0xa0af9e02a7620e7ff119c3650d59d80169edd0ad452062b0e3e429c038cdaa4f55a18495e459367aaeb6a92c98003191", - "0xa61687511b627bde7b3977e9a34cb7fddc2aaa509a7b99b6b6c7b97133845c721e1e69f99758698d82cca265d8703610", - "0xb9691fb57be7aeb9d43995b8022051f199978d6ad635e1623a1bc1754b250fb8a94985cdc1e623e98767690a417e92a0", - "0xa6d6ef51a361df2e8f1d993980e4df93dbbb32248a8608e3e2b724093936f013edabb2e3374842b7cce9630e57c7e4dd", - "0x9820d98ef31bab813a0124ce48cacb9d99b2c1c625c41cb3d6e0b21f604ee215d5f37505c86766531dc302622d889766", - "0xa35ee5c2d7800489723c78008b495e1742f0542dbb487172ef438f60424c81aa41c2397095821248066140662133f6f4", - "0xae95ddcf3db88f6b107dcf5d8aa907b2035e0250f7d664027656146395a794349d08a6a306ce7317b728ca83f70f3eaf", - "0xa36dad4f7cba9f4cc843fe40f6240e1973a4c412cae29b4a68712598523cfaecb05272fc47d30772bf06906b5a26e282", - "0xaefc682f8784b18d36202a069269be7dba8ab67ae3543838e6d473fbc5713d103abcc8da1729a288503b786baac182d3", - "0xb551d1ce88cbf4ffbdcb0113a6e319513bd676d0078dd4e6a6f23ad336c1d0fb47a4e427bdedbe0fc8f152353971f81d", - "0xa11a7496c712734aec80738e30d2bf245758b34245076149854eb209fa6403be8bb0d4e515becc881b7f3610749260c0", - "0xb38e558a5e62ad196be361651264f5c28ced6ab7c2229d7e33fb04b7f4e441e9dcb82b463b118e73e05055dcc9ce64b6", - "0x9437ce85146202d3815df7f341a182678665dfb74b96006dc9d6acc16110d00b4a02717b702a765566457710ff5a7280", - "0x813bafdf6a64a9c40ef774e6c8cad52b19008f1207fc41bd10ad59c870fda8089299dd057fc6da34818e7a35b5a363e9", - "0xa5a1f7d42220d3740b3f353de74469fbd3a75ceccb3c84d0a87e43444855be0c51116a32a56cb1980294724d36bdda16", - "0xb4b80d7fbdb1dbf1567dfb30d8e814e63de670839a8f6ff434fe171416599fef831b8e978d6498851b8a81e0bc8dfb85", - "0x971882d02ad64729cc87251b49effc0b8db9880c25083bfa0ff34e7394e691288c7cefbb5cfdc76d6677ffb9da765dba", - "0x8144a5c583a61f809f6a9f5ba97dbed42f4086de71af955f5df5774f66a3581335926663502d7cc7b5129216da225f9c", - "0xb2eedff11e346518fa54e161be1d45db77136b724d497e337a55edfc896417de3a180bf90dd5f9d92c19db48e8574760", - "0xa6b434ac201b511dceeed63b731111d2b985934884f07d65c9d7642075b581604e8a66afc7164fbc0eb556282e8d83d2", - "0x85ab3c57517e3c348e7ec13a878b9303ff9aad78ec95b13242e087ec41f05f4a19366ae169fda8afec5300065db58f2f", - "0xa51f7858f1a7832b743a114127ebee1cffe176c988d4cb2348e45d4ebc52b43f80432c7276c6a5f8bfe39a432d4412ee", - "0x9332251b4b56579b201a2fd9e777e4be80aa213bc986ed5d1187cada9b225a7ed18f1f5bf68c2839bf330e00b2d63f22", - "0xb87e5f481b938ac8a481b775cc58be2a06604549e3c810fc4734bab76099e5c617f0243c4c140cb7dd6d36a6dc2286bf", - "0x83ca733849830cb8fc2ef469e7e464fd94def561ce49ff0aa352a6ecd0e52c7aefcd69ab59f3d1ed2d5b8536d0a7895d", - "0x8302ad0f2234535b55b975c5dd752c8a555d278b85b9e04e83b1db3bb2ae06f082f134d55216b5cacbf80444e1d0af84", - "0x8c627caf25eae6764501b9eff35aa90bd4f24952cad712aae20344579e83ecd104ad1f7915edc4f9023b17fddbdb4cd7", - "0x91066bac5341cead3d2cb168fde7da62b3dcf933ff5c1d379a4dd424b218c4e2ebcce038cc342e758795ecd4dbb8b790", - "0xa17e8874e2c59a2bdc31cc67095a271d31d5a4852ccf2a82eb7c457a3ba8c87ee5beb93a65a8f7bd04d10247e63d6b84", - "0xa15ebe9ab6de62a4d1ff30b7fc58acfb14ea54d7fa513cbcb045af3070db22bf9c1e987fa26c0c54356b9e38fa412626", - "0xa2ee6c29efa982e9b9abd3c5e4f14b99d5d0369d7bfc3c8edae1ab927398dc8a147a89e127b3324d7f4e3a7494c5d811", - "0x8cde690247d4831dfe312145ae879f4e53cb26641b3a3bb9eb4d590c56c11ece3cfe77180bd809468df5cddaea4f5ab1", - "0x8dc3c6478fe0150a2cc11b2bfb1b072620335516ad322dc5a644676a4a6aee71a8680eafb37db9065b5aa2f37696de07", - "0xa35fe9443b05f6632b080d0812e71142dba534b328f7d77e165aa89b370c158be708fed2ab8d8b3c60a3f83d6b1c4fd7", - "0x8266f9cc52944d85c50ba04d421c0ecb7ceac774f4485bca84115772ade238fdb5f5bf93f1f6c5288b3a44af177042e5", - "0x995194ca593943e772c58944789a30f8a91f20e58059967fa65364e4357b3483b0f94a3fe34e133bcf967859c5bd026d", - "0x83117ec2e506e292ff4759c270b3bca2ac221fc044ee7d3a4fcdd424ff0f4b961d6d268f7b9fce9ff07d29a4cb6ee3fd", - "0xb0a47515752c15e4dbeaf9ee27fab3b5c0db82f5c685e8f716fd7d8764164944340430fe3db1a5679e6ffea5a16dd919", - "0x91babaea18cf8f1e56feb0b89f0a3956c6469bb963d63312431057093b0ea0240a36abc3b7ac160e644e826cceb62530", - "0x9302bb41f741deaa5f2b6e3bca1427a6cf98b7ec2bf7967b7c0595efa258427323a022ef12f23426ff7a7c318462f07a", - "0x9022541f84e48b655e74bf3da484179e0e0040827fc71e777b68f19bcfd0e103d385ef957692e7091fe713561f38035c", - "0xa3b109249ac2900806f0f39338da72d4f2cc6d1ac403b59834b46da5705cf436af8499fa83717f954edb32312397c8d9", - "0xa94ccbf61b3a857744aa1540fc6d633afb8ab3c92d07fceef90a0dc54ecd2133490cbaac8832b26cf2f4b956471f36fe", - "0xaa744c552b5fc41e1ac6ca53184df87a1b7e54d73500751a6903674041f5f36af25711e7bc8a6fbba975dc247ddad52d", - "0xb544c692b046aad8b6f5c2e3493bc8f638659795f06327fff1e9f4ffc8e9f7abdbf4b7f6fcdfb8fe19654d8fa7d68170", - "0x94179fcc1fa644ff8a9776a4c03ac8bff759f1a810ca746a9be2b345546e01ddb58d871ddac4e6110b948173522eef06", - "0xa3a7196fecd25e9cc7cac79c35365676e48c7be1493df255676adff2209c0719f2190ceff3ce008d08efa07c244c11a6", - "0xb746447b0c0d7165f965672d71c318f2c1052a5ac6ebe320b14165c9276c839ed822a9183ea6e6dae63a4f826d421d65", - "0xa66d5b1cf24a38a598a45d16818d04e1c1331f8535591e7b9d3d13e390bfb466a0180098b4656131e087b72bf10be172", - "0xac1af27a7c67b1c6c082f0fe733046f8b155a7d66caa8ccc40a53ac5a55a4903d598b5f80543ea52c25205b02959f4f5", - "0xa485a082dee2987e528d1897dfc5ee99c8de9cdc0c955fc38c404c16c35b71bccd08770c93102110547381a2eb9d3782", - "0x900b9972180a2c8753f5ff49fdd2cfe18c700d9927b3c3e16deb6376ad6ee665c698be72d4837b94911a0b4c183cb140", - "0xb8454e8438641340b7fc8ac55b869abe54806f873ec0f2d8fc5425a1ba76ed8471425440621763b6e9d834b6e5451b98", - "0xb3ed0906d97f72f0fd5fe01cbd06b77d61c69f059f1e87a143a5630073ab69ef8876bc2a5e261d467a7f00f0050388d5", - "0x90a908b47d0c29a2d0e7e65a212d7e1788454062f46458c519c7f2ccd794ff21d4c24b91acf42a71a509aff6544f676a", - "0xb07d7c3f1d486f5657d5935e3d67403024ffdcf25da5c460fdadc980d8d6b931de623c4f8a3da5eb6af346193eb36573", - "0x87c2989f377be3751da3bc19172c5987d21c095cc3d851ee5120f67a5b3986d387b058688d54336d8510c49c6a66d754", - "0x8e70e4867d2731901d603928d72bbeb34b2e0339a4f5cf06e7a771640717421b4ea039c61dde951582a28c2ff152ff70", - "0x8336744d8ef3a3bb3e9ed3d6b83e08cafffc76b7438adedd3a7358b32acec0e73a4635aa3166362ab4e158e68576255d", - "0xa70132fe0c9580ecce2e3c0d4a531cabe48bbf6e7d1c1daf9ed2f315e81705bf1616b4cfda1c903b074e239ac6ab4c47", - "0xa978fb8ce8253f58e1a87da354f06af989b0bafaafec2fb3100bee272dd8664d2690f8ada7dd4817bc8b06ffb1fe23f9", - "0x94274299f0faca1152cca89282c10d00b5d3679cd4b7b02e018f653257b778262fb3c6c49d0eb83ce388869c283c3c05", - "0xb8233d647876eafe2746c10c1b41d99beea28b2627ea2ecb67a3eb0d166fadbceee34dfe942aa4ecf39e0d55f9d6d2a6", - "0x980a54f9e9d88a7ec08d04edbdd7c9222e99f270b1e978ce7140cc67e38a2e60cc1034dc5b0deb5b60e10697d3bc7295", - "0x8553bfd1a163df0d8bb1424383552b85a1c0d288dc1f46fdde90ce2d9be7e2688d7d06e1d6be06252c7099588d3488e5", - "0x8e8e48992d0394fcb9a0c56bbd3797400128e28fe395ad9acf582919d66d11a4811a7187897e60ee2ab4842800c8c36c", - "0x8853eff72fa4c7b4eda77e448e12bc8ee75f5cb0f35b721c7ee8184cf030a11e3e0278a4e76b326416fd645a9645d901", - "0x88e1e459ee5aeb8b36ed004f6d03da296101106fbe1b18f9bbf63e92321db51670c34050fd3b7dc56a4bad76403823ee", - "0x890def696fc04bbb9e9ed87a2a4965b896a9ae127bc0e1cc515549b88ddbcbc02647e983561cab691f7d25cf7c7eb254", - "0x95915d8ff2df795e7baac5433887c39ec6bbb9281c5d3406a4a1a2008f96c6f266adad4824c6c46429a158e36f5e1210", - "0xaf917d086e2e327d8d9e37ff85702536d7b15f444310d4aa832a61d850c7c3f09d31b3f5fd2a073e7fd64601275b6fca", - "0xa448516054e31866b54f1951b9a03f0a54fb13d938b105e3f67396ed3fbb015f290a37fa538baeb077fb4f9ac86c8305", - "0x8e58219fde5e9525e525b16b5332ef27fb6269e08e8c0bd3c20abb89397864b2c5bb55f5b6e03e8f0a0e0b04e5f72b14", - "0xa798a0371e8cc4dc42ccd79934b0db5a3a59f18a0ae09f2eb172596428fcb3f00312e783d6fd21cbc1610317f44e08cb", - "0xa8cbb85e8f38734d95b9d69346cbcb169c149b9801d9da46df5e27b5ff8d0ab7b870c83db3fac32a90d02efe5fb8fb49", - "0xa63868892ce200c7d82d7ae041db371c91ce03282adf796c8b1a1652732ec77add0945727b110339a80596c367c97deb", - "0xa0133deca5ae8f1100df8db69920b2d0c31aa21bd3849dbaf4c264eaeaec8937ab8f982770ce1ea17e0e258910a56d02", - "0xab88f81dc77f09a2b62078e7baf4b6c7503925a7a077bb30d72f4baeff8225039c5333242e325e824f7192d4d132b596", - "0xad83b3c5e9a08161950b1df9261d332dda2602cc68e0f5ee75bfa7e03bbef9edfb4945ca1f139df1bcb8affe8ae025da", - "0xa52c5a63b55a8001b6b67c5db4fd5e95923052f03618369312896ed9892d99354aebc0dee8c3b365bafa29e211a5c3f9", - "0x973dcf44ab60f55f5d10a8753ea16db9faedd839466a130729538f3a0724f00f74b3ca1de16987d7c6e24e9467f62bc7", - "0x991a7c93f06d50ec6a4340c6751b73eb5825bad02a954e44e1e2d424af928819ebbb590c6129ce35b3f1e908e2152f33", - "0xaa48afa77d5a81cd967b285c0035e941ca6d783493e1840d7cbc0f2829a114ace9146a8fbe31ecbd8e63e9b3c216a8c5", - "0x8c01b901e1067a89471927d911246a8b2f1284e93be9913406d7c88aba784694317e22a0a7635583dae7db45cafb73ed", - "0x942772b7c7c47d4e5957ccf1d6f1450070930af3e2b7eaab0dd7699372445df0cc910e6c0efcf501887dd1adabdaee23", - "0xafc555559b435c585b61096a34a15b8ad8722b2d3306ac8cbf158b46c135b293b08a5f37b109b138350dbcd1e0da9f8e", - "0xadc806dfa5fbf8ce659aab56fe6cfe0b9162ddd5874b6dcf6d658bd2a626379baeb7df80d765846fa16ad6aad0320540", - "0x80bdb82b7d583bf1e41653966b0ba3b4fec0e7df2ff08e3fa06fd9064bca0364263e075e1582741a5243bde786c9c32e", - "0x8d5776148c65e35d717da1902d74727b3bee21ceba8d337d77738932865f1b851e810b91346f705880da6cac63183717", - "0xb9d24940937b6e50a1797cad9ca58d4b2b2d8987bb8ec056ca2f397a2bdbb7af7939c0f4bcdf5a3b6fc80f65f9d535ce", - "0x81d6fc2f01633e8eab3ba4d72588e14f45b00e68ab887bdd4ec5e8558965db21189310df973837106216777b07fc0805", - "0xaf03bc1e94067741bca4978b9cf065cc6852090fde3aaf822bbe0744705ebda5baac6ed20b31144db0391309e474ba48", - "0x8cf8412bd48b21b008f0207b1f430ed96bc6512c3712dffbbecb66e493e33698c051b27a2998c5bddd89d6c373d02d06", - "0xb9c8a3894365780842a2096da49e48f7e77f05972e2acdeae8e8fed8ddc52a1e2fd754547087bc9292cf0c868155fbcd", - "0x85c8e7e1d7ee3ed366b530c5c9fe0a353f2907d8b80b16d00391780c04e3f7e060d433539780457732864e334039474f", - "0x8a3987de0131b7461bbbe54e59f6cefe8b3f5051ed3f35e4ad06e681c47beee6614b4e1fba2baa84dff8c94080dddda0", - "0x852ab89dc28bc26f6300800d9a3046bccfb3fe1491f29030f1389f40ca452f6b8a2f6d1541c1e523f1b59f8730823488", - "0xb5726aee939d8aee0d50bf15565f99e6d0c4df7388073b4534f581f572ad55893c5566eab1a7e22db8feeb8a90175b7d", - "0xa356e5b70bc478c625e32a38d29f0a619fdeb665503eedc304d1bf34562d4b6814dfc30aee5aee94ca4bc6394e412765", - "0x8b476b3b065a3b95a3d11ec60a749c2258ddcc5885bfb50b8a086d3fd1e49ff73e1dde733b8981c3d2d206aa0c87b09b", - "0x9171a7b23f3dbb32ab35712912ebf432bcc7d320c1e278d652200b5d49ad13a49ec8e56a0c85a90888be44de11fc11b5", - "0xaeddb53c6daac757916039e0992ec5305814e9deb113773f5ecf10355cc3723848fd9c55e0a6ffb6bcff4ad65ed5eb3c", - "0xb576c49c2a7b7c3445bbf9ba8eac10e685cc3760d6819de43b7d1e20769772bcab9f557df96f28fd24409ac8c84d05c4", - "0x8a277710379ba4fababb423026d9db3d8dcd484b2ee812439eb91b4b5177d03433b7a4486e43efbf2d2ce8ccfeabf323", - "0x80e30cabe1b6b4c3454bc8632b9ba068a0bcfd20ce5b6d44c8b1e2e39cbe84792fd96c51cf45cf9855c847dc92ce9437", - "0x804c021152c3304853941847e80480fdaceba3b9676fbe018268cf77d1a1856966c2f9686bb4d4aa0c4118a7e85f83cc", - "0x901f724ee1891ca876e5551bd8f4ad4da422576c618465f63d65700c2dd7953496d83abe148c6a4875a46a5a36c218cf", - "0x90fb5cac22a22fb8a6b619f1eacd95873be974d4d5d1f7080e523bb9b4b2644eda7340d780bd1ea8ce36407ca0410fea", - "0xa34eba9a41f2307891af1825ed501b74278f67eaef4bc57cae5c0c46202c19fa0d9a5dd8b91325f6c151a0644762ef29", - "0xa5c225b7bd946deb3e6df3197ce80d7448785a939e586413208227d5b8b4711dfd6518f091152d2da53bd4b905896f48", - "0x84888f2efd897a2aca04e34505774f6f4d62a02a5ae93f71405f2d3b326366b4038854458fd6553d12da6d4891788e59", - "0xb897fa90529458bdf3cede5ced3f3823dfb9b6d93b96b81429bf05e8f1a80f7c857d458045cfee58296b3ccbc4119abb", - "0x8117fbcf61d946bee1ce3dff9e568b83716907acfde9b352c3521cfed44158874af8dd5b3906b4a6b49da2fb212ef802", - "0xb6e9fe9fa3d4c833c3beae7f798f30f07e3cdf6f6c8eb8e2b70cad51b37af2549dc9f2e7f97f194e5897d4dedb904a45", - "0xa7555d66719916a2be7a7f0c8b7001aa2925bcb79723f78288f10831f9cec64923228b0e4b89dfd4342de8f70ce03cb4", - "0xa10788831a0cb2c3d14d8bc214d92bee6e2a9e92c423d2974760d84a6872a9465d12b628f9bd8a6e777a7db6f509b3a0", - "0x946948e31311703f64d34dc6faaae992e39b7ced92ecdc01df9761e3819a6db1266be718fdf434fbec912da37d1986f1", - "0x8675d210e67eddb3cefeed200b9e205679d36d8dcad70f09e678d8d1b3eb1059d12542f3aca300f384504458a881dd60", - "0xaaa18df4ad95f7443955accf8ec206f46d4d8ad9f1adb07b143b4225590917ed7ae050fc329d54310d3d0b198cedaf0b", - "0xad77fcac9753efba7a9d9ef8ff4ec9889aa4b9e43ba185e5df6bf6574a5cf9b9ad3f0f3ef2bcbea660c7eef869ce76c8", - "0xb201b0546f19c5db88df9c684cf55ed623bdb43927d06051bd595497df741feb1485961f64e8d3d1811d9e2e9e1e54ad", - "0x836075979eaf386ff6cb459cfd48fed171ae812b0ac3b38dc24dd8ca905cac1c600be717d4a0defa0a854f40cfaf8c33", - "0xa0617db822d559764a23c4361e849534d4b411e2cf9e1c4132c1104085175aa5f2ce475a6d1d5cb178056945ca782182", - "0x9831b8c836114f6d8213170dde1e7f48d5113974878ae831fc9b4da03f5ed3636342008228b380fd50d4affe909eb54a", - "0x97b43a6d1a47a1c415278344dba0cdfa952663a71fdcaf58d313c161e479ab5d1b980d8870055cc8f0d283bec8f97425", - "0x9340bfc34ffab8c28b1870a4125c559978ac2b278f76f462b5c859a00c3ba3426b176dc2c689096ad575b4cd4dbb76ae", - "0x88ad79a0320a896415e15b827a89969b4590d4dfa269b662bdc8f4633618f67b249f7e35a35884b131772d08025bfc32", - "0xa0230bdf83cd469c7248074bec535eba8280cfde587d7c63d307149e9626bc7642b4bacc9beff2d8e8f6ea398dc0ade7", - "0x8934e9a3feababa12ed142daa30e91bd6d28b432d182ac625501fe1dc82f973c67f0fe82d39c9b1da3613bb8bfe2f77b", - "0x87fd7e26a0749350ebdcd7c5d30e4b969a76bda530c831262fc98b36be932a4d025310f695d5b210ead89ee70eb7e53b", - "0x83a9cd621beecac8baebf7df4f7ee17bf4b70aac31df816ec3efb5cfef2dc5c0bf959c5227df3a7ef4c2b8d1e1b658a8", - "0x9529ea4a51324ed4ecd855faea43846a223da8cbb494e5854cef700ebbcf4d76119cef16192e6b7c51f82ab79371756e", - "0xb15e1b4ac64bafbc4fdfead9aeff126bf102fdd125c1c914f7979680ec1715fbeccf3dc35c77d284421ec1371ed8bc32", - "0x8f1d90034f998018c3f4b5947b40b139fcead2e40aa80fdec6a4337c60e9d5ff1923dda7f0b5b1731ff16f55027d41bf", - "0x86b706c5d3c5aca72cb23ddfb6452bc70dd3b1a98c8539a7c32f760778b401cbe90ef86c12d0468892dbcbd9a268a38b", - "0x9752561179783f336937757b619b2fdcb9dfce05aa3c4fce6d582dc966182eb85ab4ccb63e7e1736a7c5fad9d33cccd2", - "0x8bb51b380a8a52d61a94e7b382ff6ce601260fa9b8c5d616764a3df719b382ec43aec9266444a16951e102d8b1fb2f38", - "0x8097b13908662d245820f3b045d8c2c665fe9a054e9c661323924ec86dfa713b36b0c787ad4dfdeb979318810e687a48", - "0xa09b2a07d861e01645accfb088f7f9ad524186bd439712775459a60f8a1fbbd43ee084e4d6e23ffce06daa189cd1e654", - "0x941e2e3ba414a371a11c3fe92cabf688ff363da6230ec7c83ac7303f652a19ebc89cc494427c456d0c2ae84c72053f73", - "0x820cc2ac3eed5bce7dc72df2aa3214e71690b91445d8bb1634c0488a671e3669028efbe1eae52f7132bde29b16a020b7", - "0xb5222582ed6604b9856a48039bd643340f4bf1bf0fc805e8453a9eb7630d6cea1452d28536706d6fa3ec16617617991c", - "0x938bbaa0ba14597067ff4c0a7cfc1529c44160d6f61cfad12246526d84fb7a1ba964d3bbb065a348cf7a98356ee15234", - "0xa83371f44e007c708dc4bcafa7bd3581f9080a4583c9be88624265014fd92f060127e628de5af3c442a25f049c7e7766", - "0x8c17ccc763fcdf2ba7e27ea643654e52f62a6e3943ba25f66e1003fd52f728e38bfd1036c0d50eb3e3e878378bcc2e9d", - "0x9517cd84390fbbfb7862ca3e0171750b4c75a15ceb6030673e76b6fc1ce61ac264f6dd1758d817662abfc50095550bd3", - "0x936f7e20c96b97548fef667aa9fa9e6cfdc578f392774586abe124e7afc36be3a484735e88cc0d6de6d69cf4548b1227", - "0x908d762396519ce3c409551b3b5915033cdfe521a586d5c17f49c1d2faa6cb59fa51e1fb74f200487bea87a1d6f37477", - "0x8ec38c68afdfb6ba019204039c2fb49a35467058f561f626fa87314d705fd615a7b9966576052be1b3690028d3c5c7bc", - "0xb790669f1acb10911e520198795b259a18471cb3ac03f3885b4fa40626d414e26025790296fd078ef5c3681ebe4689cf", - "0xb659c05488f778fca3c918505d2d849c667471af03369ad9fa29e37bac4cc9caa18e749c62fcff22856945a74ef51356", - "0x97ffcbf88b668cde86b2839c7f14d19cb7f634a4cf05d977e65f3cd0e8051b2670e521ae74edc572d88201cff225e38a", - "0x9888c250b4b60306f4ecb1babbf95d0b6dbf6186503b2024b134478d721fb782d801bafd126cc3247bcdb1ee9d66de85", - "0x941f73b2138b4347ecafcc7b8c3d03f2a54dc49f580394ed08f22b0878ee7cb63d42978f1d320c09e7dbc67648c06f8c", - "0xac3195143035cdb4ddcd5f93c150035d327addee5503ea2087b1a10b2f73b02453ddd1a94d8e7d883e365f9f0e3c38c9", - "0xab6b47627cf76d9552c723818db5ebee7734542436b50ffe15b3a96e8e7a6b54f9a0965de78405e16e309193f147108d", - "0xb7c66da483b18f08344fc3c27bdf4914dabbcefd7ee7672fab651d05127d85d25ce363b0c338d6eed55c4e31f57bcb35", - "0xa641eaa149c366de228a2833907ad60eea423dd3edf47e76042fdf6f5dc47a5b5fc1f1b92c8b96c70e6d8a68d3b8896c", - "0x805c06e565ee67cab0cbccb92b6656fdb240b430766eade3c6b0a0b1b93c840e2b4f028601451dca135c783239463880", - "0x8528cf6ed82d9f729f9aee83c3ef763d85649d46019c4ca7dfb58d7824c2003f88ddb2bc5a40c4d78d86e68b675f4e56", - "0xac2955c1d48354e1f95f1b36e085b9ea9829e8de4f2a3e2418a403cb1286e2599ba00a6b82609dd489eda370218dcf4c", - "0xb043156fcd02b75dbe940c763fa8e8a7c7f6d74c1d5395db5ce544af3b6097eab61686950535a810aa95889ced12f74d", - "0x8e7d1dc7beb2de660b7da19ebf4cfef3ebb6a3d6f2f367e2dc91105653226e859137879171dccc586c10d9c4cccee7b6", - "0xa278bea51af1de8bbd2319f3a37ab14abc3bc0289ed31aae44f38897a7b24263a4dde1cb037e1441217bec0ddcf47266", - "0x8afa23226c47083bba80ab1be55b48c90c6629135533e3e4c14057d19febeba7f8e2cabe617b28ce1f0bd97a06972f66", - "0x9865218b0eb281e547e693055456d1d0c598bfcd0138dddb5edd5f5ff66cc2d52465f3e70c0f321246036d7ed8c606d1", - "0xa3a6d1ee35cc0ed9290a135086b32f136028b320650e1f3443434af7ff52dd74c546ffe2a1bebfc329f1b52cd72aca34", - "0x812d3ded3a3c9e58eecf13a29bb4cc13b01b2a0af322423a29bb0e4f6d9021d1d87ac4af7a2a6b88d34f44a8bc1b3c55", - "0x8d7dc174aa361d046cf183dd202cbc12fed780d7053f7047e11af9aded336318bf9928aab73ebfc81ca86f12007077b6", - "0xa9fdc2209bbf48970a404de3d803c65b11be96ab5a165183d05ed6477b3a0c633c3d6f0cb8eefb430fddb5b5be8cf887", - "0x83a798f47a4f62dcb8b531d463b0fd4a876d47a8ca990710290549255033c909de709471b4e823a60bf94d8baf8b5acf", - "0xa8b0bb9e1f8b0508c7d6e7382676663d27fb27e3f1c0e991a295e59498f4a5dbcc4cf89c73d3d587fb3b8f5838153885", - "0x80414adc7e0a9cb961b1f31682c33d8e01e3b8cf2aa2c2a911ab9b1f54d5c4bf92e18466cacf9b80333112ab015136d2", - "0x82d2b1053f6610064f838b3aeec585878f86323cac357c4aed054d87595c7734729a737b29b57940884ee839e984d612", - "0x91efdbcaad9931312d7c41d24de977f94d7f3f7b88090a1f72d9a097a1e30cc805c5ea16180f463022d9b26b8863f958", - "0x90ab68c372fd01bb210fb94094adb27296b7144d964bb1dd807ea8f718181747356b0f9db3feda78dd7a596209099ab8", - "0xb40a3bae2b08c13db00f993db49e2042be99cde3d6f4f03d9991e42297933d6049394c659e31f316fcb081b60461dabf", - "0x94402d05dbe02a7505da715c5b26438880d086e3130dce7d6c59a9cca1943fe88c44771619303ec71736774b3cc5b1f6", - "0x8adb748d5fa5c22ce4c76a1debf394b00d58add9f4e08524cf9c503f95981b36b8d0cb2dfaef0d59d07768e555733ecc", - "0xa129c9cf33df42b5a98ad98be9d940207ae154c715d3bde701b7160dfe45304679fb0481a4f9dde242c22a9849fc2d9c", - "0xb3180ded54610b1b3a2db7db539197ced6a75e9bb381d1f4b802ca7cd450f5418522ad2bee3df1956ed63ff1ffe95dc1", - "0x80e1dbf3296bdfa98aeec1a7560682165d13bc628061bd3257f345aa1ba13f8bd1bea14f117af562be22118f5a8265af", - "0x8d286e63f64a3e24c2e4c2b91bafb7c6a71d9438a2ffd7288c58ec6de9db6194eaf671b39c5a462c8658ad3cfce46f85", - "0xb4f4ed1bd274a852189719a8808a8f214c8386e844ca9ba13161b75d04c74633b1d8a758ce0b23ccbce8052494c81c3f", - "0xb1a3e6baed1cc37b9a67f38648f4fe365d23fb982027ab4202c3392d5459d7995264c2e9bb8e821a3e75e71390b6dc7c", - "0xabd7248ae069d3a3a45b0ef4dd5d7d54b62994e578ea20bdd3b7876596673953b94c5b109a6e4b953b517544b915368f", - "0x9377aab082c8ae33b26519d6a8c3f586c7c7fccc96ec29a6f698b67d72d9266ad07378ba90d18e8c86a2ec77ecc7f137", - "0x959675679fb41dd62595d8266e796834c1207dd70750e304b1ce45d3fc215ceb5214d6651fc97a061b6a570eba35b811", - "0xace7fda25c2fb7c18710603c16a0ff0f963352d1582a42a20c9f5603c66f485df8383465c35c31e8379b4cb2ec15b4c4", - "0xaaf15335f1fa2a187f24f3db7966fcda52c2859113ed8f460167538f5cde43429750349f9714edda0adb6705d401d27c", - "0xab99038a2a6f9228d5d7e67f47107abaf06af293586c3a6ab1adaf02aae373e3434ae3e26bb617302b8e3a7ce5107bd0", - "0xb3119de346a02c87743faa4a20fb90e7eac404a6f81ac681d593171cb29c5f79d4d5ab761b66ec71d4a86f43e0b4165c", - "0x89255902846cb35c706f6e8869a9122527afcf8a8b8f5f81497b5b71c6a96c601e7185acc78646e2a7884d148eeea815", - "0x8c26d4ec9fc8728b3f0340a457c5c05b14cc4345e6c0b9b9402f73e882812999e2b29b4bffdcb7fe645171071e2add88", - "0x903b9bf66c147ddfddacce63c8f12f62e45638383bf91b50e2fef29013ce54a3fd8c3eccc9b8961d91ca2920ba5b0c1e", - "0xb7a2c83971c4e4132f3fcaf3c4374872de67ea5d89814492309cf924520a23787401f9621681fcf526154e80849a7e72", - "0xb74f6e53b56856f88f8607b1c4e6c9e54aec15c5bb891e7bab00e2a13caab3b1d6529bf0d72d4ce99714b8cb8b973f1a", - "0xab8a8769c754008a7976b6799e81d7bfe97413d0a79b90715703c1f8f567675463ec93aabee59277121fc4df88b5c7a9", - "0xb34d4d2e15079e7e80fdba30cddf4fc0e6c9a61f7ab06a6ea0a4e55fd5bf632c6d72e021d6264d935439d321de883bb6", - "0x927c030d5a69f0908c08f95715f7a8d1e33bed5e95fc4cfb17f7743cb0262755b1e6b56d409adcfb7351b2706c964d3b", - "0x95718b06017ba9d45894867fd67148645d25d9db2229aa89971f444641ba9db4c5c6f0785f3b25cf2cd7fadaa6adc5eb", - "0xb30faf88fe203495aa268503bc82576f76a27f8bc8c4125b4c6f6e5e7b6880d495481cc9454713e0833317fa07da9b5f", - "0xa9ee291de5997232c68c9f6c3b568b05f46bfedfa18eb3776699d98cc7c6320003b7d862564d07fd28fc3691d1d28b21", - "0x8e54c7270d2c7041796f202e929ae921fd0fcdc8ef1e6eae7e67d461114fd45ecc7fb78247c072222e48d1292a12acf9", - "0xb7f146a357e02a63cd79ca47bf93998b193ce1174446953f12fa751f85dc2a54c9ed00c01a9308509152b3bad21d7230", - "0x8aadfcf3562f1c357068323352cb1745349a27a7362358d869e617c2410db747149b993ee9e881e252ecdd42fd75f351", - "0x8068da6d588f7633334da98340cb5316f61fcab31ddfca2ab0d085d02819b8e0131eb7cdef8507262ad891036280702c", - "0x89ab1e5c2565f154f92c9b3554160832d176613f1a2f872b6ed62ed925a33fb0b40b71b7443eaaa15099ab24693c8d13", - "0x8e9bccb749e66fbe47296f5dec33bd86e52987516263240f35ce9a212dbcf71348b60a016f830f2acd05482962403542", - "0x8fb74891a8642f25a55b5eda5818367aac2fdf9663ad1e3dbc0166b484c0cda581b243cc75131167f1afa9caaf6705a0", - "0x8027bc62b59f9f15613e38da74ccc71fc3eaee26f096d187c613068195ce6eb64176013f2d86b00c4b0b6a7c11b9a9e5", - "0x8658a15df961c25648fd444bdf48a8f7bb382d9212c0c65d56bf9cdb61aab3bd86604c687fb682260dbc0ad2dc84bf01", - "0xaf76d2de3664f45ed4024f1b944cd316cf758393232bb07bc695e5eaa7f04e7e09007f29e83f62ef6fa25d1000113ca9", - "0xa3c4269e6fdb75882f0bb83529388fb8e08d025d00d869a2ceefdbd38a060e59535bca43012815444cb84021787f6c7c", - "0x8784a8fa62e0ce23283386175007bb781a8ec91b06fd94f22a20cd869929de37259847a94a0f22078ab14bb74709fac6", - "0xa99cde5c7c85ae291c74c893e598cc0e6eb2dda2a81dbb504a638eb21dd2c41d6e5caf7baa29e3c1c32e94dca0d791f1", - "0x802f512bd4a97487491c0e07ab8a94d5580c72212032e34c42b7039b860a7cf8f1e2e24b7185b80d3ee00a9cd4c92903", - "0xb12fd5f747c5223c5150dca2728bb3a363c5bdade5a9d1415642b2201c51aa6bba20a988c51bb6452fee7e05a8586b42", - "0x99caf2cbdd4427666fcfb506bb6956772e058150b0638eacd5db2e8869c8565c1ff2c63f308bc3143874e0f31446292e", - "0xa9d47cb4c69fde551b2648a2444091502a56a778212ab544ac75cc1bd14d0f043f4e31de47fce9a890ef5428cc28dd41", - "0x94240350a53e7715c178382b174c4f918d35cde875faeda528c2f32073085c6032b47fcf00240dc264621041c105e0e8", - "0xa413befdecf9441fa6e6dd318af49173f19e8b95b8d928ebe1cc46cacc78b1377afa8867083be473457cd31dfff88221", - "0xb7c4e55e2b48ba55a71f72387475886e5b4715100e93cd2ae09582fd37e5646b54bd93fba311b65c842bd0aae1424bc7", - "0xb7ea5e0d3cfcf0570204b0371d69df1ab8f1fdc4e58688ecd2b884399644f7d318d660c23bd4d6d60d44a43aa9cf656d", - "0xa7c2174eea2b66b2a71cc8095fae39c423a353c7d5020ec2d0551317a66202fcf082c6119ba768755523fff49791bb4e", - "0x991e0fc7fddd0e316cf4bfe20478f10c15b8bbb618e6be52a5095e457ca52db8adc008f47d4624b6cf4f7d6c2b94a29e", - "0xa7d76c88daa3ba893d4bd023e039e1f587565d317609cc9ddce73f2d3c4d6d9facee20fca31c85322f10fdf15267fbec", - "0xb880555398668dc7d064a18ba82d574999a93a6843423703aa8e543fc196607239de7a4258710b85563f2889eacdd0fb", - "0x903f569a8de771406b9fd36384f1fea20d5d79374b8d9af24b4814f96c44739193662aa47be857543fa101aa70ab205d", - "0xb9ee3b7b95db0122edd90b641de3c07fbf63a4f70fee5f72051cbe25c92d88444314d0489a5ecdb1805f4f149f462ee6", - "0x8ee41011424da5e2ecea941cbf069ea32420749f2519434d3d8f9725ac789753404687a6745cffe4bd5dfc8fec71a719", - "0x8a978ee4be90254fd7003ee1e76e5257462cbb14a64dbca0b32cea078908d7da47588a40ffeb42af11a83a304608c0f7", - "0xab73a043ccdfe63437a339e6ee96ef1241264e04dd4d917f6d6bc99396006de54e1e156d38596ba3d15cb1aaa329f8f5", - "0x9439b663e4104d64433be7d49d0beaae263f20cfac0b5af402a59412056094bd71f0450bc52a294fc759ca8a3fddfee9", - "0xa53d2a4bef5f3d412fed35ac375f632eb72a6650efe811e2131a6ddcb530f88044f65b80b7d739998115b9f961bbe391", - "0x88e7a12a90428bb45bcf4b01442c11607433211fc2f9bee9545304eb66e0b4b5339360160bc782e185391385da7c5ad7", - "0xa163470735c16f800bed412bf0190d7c85cb2d3d588ffce245ec8e8d4872c756a109367e293caf4f5c0ca1ad31f8be5d", - "0x897d7a19b70dcef1af006df3365981d73068c81f18017f32fb9966599481496efd5f6caceef943b31c52750c46d9590d", - "0x90f4476224b64c2a5333198a4300ece8b3a59ae315469b23fd98dadcdceaaf38642d2076e9cd0bfacc515306f807819f", - "0xa8d152e5d94b75cb9e249230db21af31de4d4f3d4ef60ccbf2212babf69aed2a38435a993ee2f13cca410ad55a4875ab", - "0x95aafa379cc6a2b4bdd0cad30b7f0a47839952af41f584219ec201c6c4d54610eb2c04b67b29080acb8cecc5e7543fbc", - "0xa8795e7f4c4c5d025ead0077c3aa374daaf9858f1025c0d3024d72f5d6c03355ae6ac7418bf0757fe49c220acff89f7f", - "0x85e2013728a13c41601d4f984f0420a124db40154a98bbe8fddc99e87188b4a1272d20360406a9dbae9e49bfe3f1c11c", - "0xb380ee52038a0b622cd7eccf4bd52966573fadde4fe8f70f43fa9c43a5a99b3eaf58335a1948b561f5b368ab4e0710f6", - "0xb43fdb2ba9128fd24721209e958be7b9c84dca08387c982723f93ed4a272f933823ae084f1b1399ff6271e0da6f5aa3f", - "0xab2053c376c6bd113b89fdb2ae3b8401aa891135345885730c61cac7813f69ea7d906c531be752e28657f73af92d1f4e", - "0xb586e67ae1826a1cdd651ac785e4b38f8a0e042f103a9b7dbb0035626d5dec3ded04a4e2cc09e63b4b01aebe304e40d7", - "0xa86be58fef115445b909dffac6f51da3fe9214afd9c31fd564bb8f39b1dc3cb895b1222f2c63226b54b60b278ec45edb", - "0x8016d3229030424cfeff6c5b813970ea193f8d012cfa767270ca9057d58eddc556e96c14544bf4c038dbed5f24aa8da0", - "0xa8b593de2c6c90392325b2d7a6cb3f54ec441b33ed584076cc9da4ec6012e3aaf02cec64cc1fd222df32bf1c452cc024", - "0x860d581af35d522b5eb5fddd92a98a6b4cc483fda00820d1ce4530e07892890c096e99b33976ca3550bb900e830ad3b6", - "0x82212706111fb1cf5def02b5b0eb7ae9e6ea42b4c7a2b9fcacb7aec928326edb9ac940850dd933f2822f6cf519de0d50", - "0xa98f68569ced00cf2c9f85fe0b4bcaabed0652b9fbe438bb5a86612a0addb5975e3b98395f2a4788639c602cf21a8494", - "0x8600e2031c9113ad2a75c19872b5efef85765b524f74de98baf4efe4a75c6be563e9e19622388fbe9afe58aa6017b930", - "0x8e825c03c8409a3302266dc5f47fbfc381dfbafbadc37bd8d40f079ca8963d4c5ae6ef0d0ba6aef2d4688736f5f6bb45", - "0x8bff10f91b8c0abb6c9542588da17fa0118ffda4c82626a754887e333d7d69661b3ae4e400da15c49565f8d10a77d0d7", - "0x8421044f794a1bcb497de6d8705f57faaba7f70632f99982e1c66b7e7403a4fb10d9ef5fb2877b66da72fd556fd6ffb0", - "0xb504cb87a024fd71b7ee7bed2833115567461d1ae8902cccd26809005c4a56078617ec5d3fa5b29a1c5954adc3867a26", - "0xb5f32034d0f66bcbccefe2a177a60f31132d98c0899aa1ffff5ebf807546ff3104103077b1435fa6587bfe3e67ac0266", - "0x9604659740f6d473bd2c470c6751f2a129328e74e01b23368f692ad9b6cce0fe1509c3f82e9f01019b72f6bf3a8e4600", - "0xa6ae4fd03fbb4e2150795f75a241ab3a95c622b4615f553bab342a1803b86b1c1a2fc93bd92ee12786bf2de22d455786", - "0x8f142bde50abe4dac8e059003db41610436d5ca60d2dfe2660ecaa5f9628aeb8b5d443e1b57662076f77363c89a1479d", - "0x919b0dca4050f3304144debd653bce45768355d2faa698b99de06ca6ab8573a285764904cafc9262352c87d9287f0545", - "0xabf28b692bed19ee9152d5f8ade776f0a42a9762ea5f37d80f47ff219fc0a8ebe5e6eb920453e1ced3ea5bba19ae5be7", - "0xb67146b202afec0132ac0070c005cf664081e860339f8f4d45ac3e630dda05560936e646673e652d08cecd8d18fc64da", - "0xaa25208385573caee2a4830f09e1cc9bd041cdb78d3ee27a4b011815a62d0d2e0295c222480947ae427b1578fb5509f5", - "0xa35d9d6d5dd5428cce7616842203b5fa3721cb4b20f50c0113f138604954fe0cf214ca3d065b578f921054b9efe823df", - "0xa1c0c317e6e352e16e25c140820b927161ce5d2c4c2e10bca3057ba4d46b4f42ad7aba20de86dad9fc6368ea92695268", - "0xb9ed23f3f26fc9f31e1e30e8ae88482352fab6ef79a2eb8939dc78110580708f482ba3ab306ed6e09030653b9704a80e", - "0xb15978155af006d231888257c6e4beac0d3b0782bcbc99e61802a5c031252f05213c9ee9465e6816d9702e4a21cb9571", - "0x8c9fefe233d0d657349b7efcdc368f5aaead27071d224af780874751e7d241f6b88f7650fbb4133043b24bbebc12aa48", - "0x92f0bf3257e775c5c469cd9a43249421c9fd223996aeda09654045b885a512e86bd834b2947aef216b4d9dd5f8f2e9aa", - "0x81ad5baedeacae12f19cc6d268779c791ddbdbae859d218806cf887b91e83bee3472740b0736877c81c5c1969eeccfec", - "0xa54e104339286d3ce8271828fbac20f6cf7afd3b72d9b194b7cbaf65f6612416117be492bf4aa88faf6ada56cf4b6462", - "0xac2c341f0054876d28393d5125c84b913e754bafdadf769ded764b8dcd4b042b5dbc19b6f40ce8eb45edf7639b3d62d3", - "0x8eebee05702bf1574b12597b72a86d5badef064879fa9d1b9aff5ab75e5c71d81d8bc404f2614085855d6ed87f581238", - "0xa5a07bf219432e9c80c38596c93560b49c7de287f31e30b7a06fcb4d15982add4a24085adbc5b753c462be989c64c96d", - "0xa3d31b20198f326eac488e88fc5b9171276d4934b0bc573c8b55b2abd26380d5296d5bbea281de91c0945f34b37f42bb", - "0xb928a1a20f078a50f9c67da1d909e6656c3980f20b96bb8d06c0cc42557ccd290ed64cd78f9c9ca090cfdb9327eebd89", - "0x8a0a4b295761aa6d2d1b988fb0c65b4338cc3ea48410cc673671ca029ba6c51fd4e101b54472eae93611faee53d4eb2f", - "0x935f616bc620ddcde07f28b19a66c996798792b953264d1471f686e84f3c6f125e2a3d3a7a535c4175973c7ed2e4bece", - "0xa80deb10bba4bc7e729145e4caf009a39f5c69388a2a86eaba3de275b441d5217d615554a610466a33cfe0bbe09ef355", - "0x8dca376df4847cb8fc2e54a31894c820860c30b5e123b76670a37435e950f53312f089a8e9bd713f68f59fd1bf09202f", - "0xa04016e9e13ad845763cfe44af4e29fecf920b4aa42f581715fc34fb9ca27776feee45c82093c7274839eef1838b10c4", - "0xb8a0003e949cf994b1bb25e270cb61358200c93b1c6f611a041cf8536e2e0de59342453c5a8d13c6d4cc95ed8ce058f3", - "0xa50ab79cf3f6777a45f28d1b5cdad2c7ea718c60efeeb4c828d6307b29ef319445e6a9f98aa90f351c78b496575150c1", - "0x8253e3b0b85538d01b0ca90b0a1656ad80ee576d0c3fa6349df58df92683b510e56c524fa6144f79a5525f41e4a2ed34", - "0x917721639b1bd13c33ad5b332e4486c4202ed28ddd9fe97b4d2367a87829c742c9e4bfb508827f4b8cadd0bdab99708f", - "0xb0b8c15d67a443907315ba3e94a89491dfbfd04ff9238d856f46cd49a3324788ddff3be9d61b2987f6f5a3c7d852133c", - "0xb2a4000ce0ddd3f0543ebfe4906570853a85350d75418a1ff2608099c069f03510df576ea0cbb406b7ae8e4f21576811", - "0x801c126abff96fe9b042be8869d2907d0c6963a79901f9db46577a445418b7465a1f4b346933d433e539536a9a2df01c", - "0x9530f92929f61f9afeea5737bded7aaff3078367aaf65b2c75f0f4263b6e90990a2bf64927774c4f0289120d49558d6f", - "0xa065363b9c4b731b08fd361081f93d987ad336475487dd28bbda2dca92b0b5da4edf326995a4ae923a4b2add7aa1df4d", - "0x85c216e314eb7bd8ba02e092c90e132bc4bafb21c6a0fbe058b0dd4272cb76f183b83c6783fc321786065ff78c95f952", - "0xa53658aaddc51e20752454dcbc69dac133577a0163aaf8c7ff54018b39ba6c2e08259b0f31971eaff9cd463867f9fd2f", - "0xa013cc5e3fbb47951637426581c1d72764556798f93e413e1317849efd60f3ecb64c762f92544201eb5d6cfb68233050", - "0xb7e5497eda543c02a7b3245eece98d21dd4c587b5a05f21b5c785756a0b875715782f706fbbfeaa0edaa6faa6b03d8eb", - "0x8ded37d67b5368619a090266e9b5585fbff60319a90a4244a3c3342641f5bfa5130998dd97d7a42505cd896c29255530", - "0xa9b120a77d70c1cbc0178a12d97a78b2dd0b98d0584e8e780b937800ceb18c90eaa1f0a83c5b50e34cae1c20468f004f", - "0xa22b351f139096f9ed5baafe27affde1351685765805d458381e392e0bfc51cbd8af5909b3a1da05d0d176877028eb32", - "0xa16938f556b8c11d110d95b8584cecef8b95ef349ea64b59df806cc62c52ee48074d0b3f18d84533e41583aefd6a9d43", - "0xb95fc0ec39596deee2c4363f57bb4786f5bb8dfb345c1e5b14e2927be482615971d0d81f9a88b3389fac7079b3cb2f46", - "0x89ca7b7aecbb224d04839d36e4b323ae613c548a942830317aa0d51a111cb40d7e6d98600dc1a51e5a32f437951d6c7c", - "0xb28df3e04bde4ec373171401dbd0546f4cb6fa8e9a4a8019aadc653d4e05e0b5b9a64417715dd87f6df9e7b3145f6659", - "0x89df46082b8dc997c3e33fa94fb6ebfd19af29d619ed4d861f8ffcf83d02b9077b9754d0667c2fceb7aa31ab5f806f65", - "0xb0c707313762e66c681b0efe03ca11a49791173c1e5d42b235c3370e98c37ca4393e6babaabc3933e3d54e72843337d8", - "0xa4bf094dcd71e1a8dccca76dc7887476154e673551f25b0ca90d6dac8b3b3a2241bc601afeeb1564bde0432db1972999", - "0x8f88615a86867c4add4c6dbd2c717a7d5c9e6450e9540b56de14c31d9ff84e2495aca3f1d5be51940c183c6ced9da2d4", - "0x81351fd284d6d07092875f366bc5e53bfd7944b81eece85eab71a00443d1d2a9fc0337aaf34c980f6778dd211caa9f64", - "0xaf7616b8f2f56dc68e3e8ae5dc5dbb4b027e53ce652860687f1b15b2f820ea0349baea5af4e3ba4d865429330d3383d8" - ], - "aggregate_pubkey": "0x90ad8bc2718e8464a78d43480f280d3ae08068ad88c2686227ec6a25f4393099e7e21b6200e83ba1a8fc34a9c08f5069" - }, - "next_sync_committee_branch": [ - "0xbcfe80e1d24fbdad7bc058b011403a4c26cb56967654494cde51517f888023f4", - "0x621312d94927fb6e3633ca6b4a8f61e8fbc72799bd54639043f1abe818ba816f", - "0x16fc985fc30e89dee4524512296e2a5438c4c68d8f035d3377cdbc2c7f9e1804", - "0x88c597da85b7b1a0ca4f2e149ecaa3eb869e60e78e5127db801706005d3d0d3a", - "0x7b3e52c66c24e912c1c7aa3207753cd882ec3c691354ede99ec716da5fa0fe3e" - ] - }, - "finalized_header": { - "slot": 4063232, - "proposer_index": 1311, - "parent_root": "0x9f5209af620727873d563ec0386856f5100df2278ee157f8b058021e29a2d0a9", - "state_root": "0x9cfb7a4ceeb31d6be6dba26dace0a074066152dbe507e328886be16e8b38bd9c", - "body_root": "0x9b934ad271680073ffce322e54a0ba990caafa9a0ab95195e4553aa5936d3f41" - }, - "finality_branch": [ - "0x00f0010000000000000000000000000000000000000000000000000000000000", - "0x3d4be5d019ba15ea3ef304a83b8a067f2e79f46a3fac8069306a6c814a0a35eb", - "0x452c63d803b8bf301447a73b2f7a747e49f37f9c9a096d8ddb6c8302666b809e", - "0x16fc985fc30e89dee4524512296e2a5438c4c68d8f035d3377cdbc2c7f9e1804", - "0x88c597da85b7b1a0ca4f2e149ecaa3eb869e60e78e5127db801706005d3d0d3a", - "0x7b3e52c66c24e912c1c7aa3207753cd882ec3c691354ede99ec716da5fa0fe3e" - ], - "block_roots_root": "0x31ed28275cbaae8300426740b8f08d2d31b0bebac67939a5f6507adc361458d3", - "block_roots_branch": [ - "0x7fb8181acdb3d8c6c5e2db5dafd701f1439abfaf2da9435818dae5cc47683035", - "0xc439004cf29049836d890c28867350dc62197484a9c97ca0fcd7be38904e826b", - "0x433cf8bf2df73fd85edc9445bd609ffff88cdf3eaea77b593e3aa48e6648666e", - "0xf092d3324350b4889c0a16d9c2ed940798360d33e5d82b32e0049b6b25cad0a8", - "0x565c0d114cdf1e419edfdcda9941aabc3d9bb8ebf07a4afea082ba1ec2083331" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.minimal.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.minimal.json deleted file mode 100755 index 8f1c8b9ce21cda6d7a10fb7973c4e628cd40b122..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/next-sync-committee-update.minimal.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "attested_header": { - "slot": 3664, - "proposer_index": 4, - "parent_root": "0x15ac23a0c16bfa81e8595621118040c3e6cbddd4b09bae6fb39ba5fefd0258e8", - "state_root": "0x6fb81aa3827e7d580bb05b4df2686c9a49508bde2f8342fd75be609a23dd8362", - "body_root": "0x9906a1ae8065d268f8acb7f1b3119408d2f7f8e6e0764370c16ea3d15134981f" - }, - "sync_aggregate": { - "sync_committee_bits": "0xffffffff", - "sync_committee_signature": "0xa9b5584ec9290a4ac6c5616639d031f9ab1064d63b4889f1da52f6f4d66b645fca48bbe2fe8484adb0c05c647edd694d0340cf684b8ccf8e34c6d8cf447cfcfdcb856f5abdcfd85ada5a4a04d4c8f6f40c6e99308893c3941485a436d6c8e5f7" - }, - "signature_slot": 3665, - "next_sync_committee_update": { - "next_sync_committee": { - "pubkeys": [ - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373" - ], - "aggregate_pubkey": "0x8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6" - }, - "next_sync_committee_branch": [ - "0x46af3f54acbea439b63aa5bb699c8f25ff584b23912366788f7c8e95011ce324", - "0x5b118fe110ee4a1b0cf9823bc189fb38eb55a7b49adbdafcf466ec7cd4b7fd68", - "0xc2f12fb91a61abedb47f62a98258960edca21f31494cdf59b47a1c721e3e98f8", - "0x16fdfd5e6b591b3140a76efa4593a9c4d105b9e5c62d6f44edbd24790657be50", - "0xc8175ab66690cc94c0a24452754addd62a06948de5db9814e813437a130de452" - ] - }, - "finalized_header": { - "slot": 3648, - "proposer_index": 1, - "parent_root": "0x991ee98a70e8f90bdd61d0f5554e53d37473e75e16af171f6d88f27d20223dae", - "state_root": "0x59b04d660ac772005a13a7dc1d5f99bb0d0292f3c422f04f7365198d70dd30de", - "body_root": "0x5151f035e146258e7327ad9cf1df13f8ddec7a7842c19993cf739358717b5565" - }, - "finality_branch": [ - "0xc801000000000000000000000000000000000000000000000000000000000000", - "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", - "0x142061c4bc3673bf774cb8c7b6085057bd0ca85672b43afa2d9581b0b6a44e54", - "0xc2f12fb91a61abedb47f62a98258960edca21f31494cdf59b47a1c721e3e98f8", - "0x16fdfd5e6b591b3140a76efa4593a9c4d105b9e5c62d6f44edbd24790657be50", - "0xc8175ab66690cc94c0a24452754addd62a06948de5db9814e813437a130de452" - ], - "block_roots_root": "0xe6e2adaaad45363d7112945ef670e21c66bcb3276dc450962ade1e8950230380", - "block_roots_branch": [ - "0x386ede102258966d4c23031c5a02de2af8180d475c4c1716b07fb5b9f142a817", - "0x35e6c89bc38d993a1957f8a9fb1fbeab7420688091ba2cd7ee7b19b7e187f7d6", - "0x99249309825cafef7e694c09c4fdf95eb4b1e8743d3b23f6959d9980ad2d69b0", - "0x5e028d1d905db6430f0ce4aafbc78f442047ec3a132b4e69557fdf804a4cfbf3", - "0xd34afeab37851937920243683a1c926c41c626aacb145718fce755782d4996dd" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/sync-committee-update.mainnet.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/sync-committee-update.mainnet.json deleted file mode 100755 index 22a44e3cf7963ff844d7f4c7f1e235230818c21e..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/sync-committee-update.mainnet.json +++ /dev/null @@ -1,563 +0,0 @@ -{ - "attested_header": { - "slot": 4055104, - "proposer_index": 1862, - "parent_root": "0x5f44df1f70338aca4b3046f9e5b144bda8b27276320ca000077389d0aba35317", - "state_root": "0x9daecfedc819b45231bf93819a73efd4304ca79528032fae2e852ebbe514cfdf", - "body_root": "0xa33f5cdc6732684e6f248e5a7636c7e7bf705f0bf709098a46ba15c660b4fc65" - }, - "sync_aggregate": { - "sync_committee_bits": "0xffffffffff7bfdfffff7fffff1effffef7f9ffbdfdffffffdffbfffbbf7ffffffffffefdf7ffbeffdffef7bfffffffffffbffffffffffbffffffffffffffdfda", - "sync_committee_signature": "0x880f147850ac0a94130b81f94203e2f69d512c929df8b954b6adc20cb1e8598aafadf366c074b590c0905d584dd980af18e0c8a837bbcadf6b4438507aa46ef95879c0acd9c2d273ccb13d93e4938e9c601f0dd466f53369ae94e1287955cae6" - }, - "signature_slot": 4055105, - "next_sync_committee_update": { - "next_sync_committee": { - "pubkeys": [ - "0xaedc2d47fa2662be6ab58ddd3682bd5e53f508162968fce8326c75f92fb3c1a25c4d4d0e6904f9b6cb1ccbaaa9dc28d8", - "0x8097b13908662d245820f3b045d8c2c665fe9a054e9c661323924ec86dfa713b36b0c787ad4dfdeb979318810e687a48", - "0x98aebd4bf15916512508a5fe89d814d5d76423c562cd3f0a0af504c8cde53be30f4df00e3ba0229cbf8528e198a0df11", - "0x86bba46d0031989d0f1baccea4174fc3fa0d595e60d35a464e86c33c233e2d6def84fced7a00f59afe397cf4fb5b67c5", - "0x845982c2672fdd44b33d2e56ad676e704c02f756b09e8765bea42b924c14724484567b55f0db42ac20cb70a7f5201c14", - "0x926dc729e135f1f0bff4662ee3d6823a64597fe189b763ada34f246e77705fd4e062d85506a338e9fa98c4d225a3b27a", - "0xb930ecc2a26183240f8da107e80979b59da4e05f090316d982815ed6151d7750490b85273187ec4e07eb221813a4f279", - "0x81564bee5a3bd09476f658cf7719326c353485e2f4fea58d110071c5dddd3cabc349a8d1ecea45d589ed4479952a2ba2", - "0xa66d5b1cf24a38a598a45d16818d04e1c1331f8535591e7b9d3d13e390bfb466a0180098b4656131e087b72bf10be172", - "0xb9b9b6113301bd2b409b71afa1ab9e31d22f84f8cb03badaa408e1d37032350094124e533de766622d7975a047fade6c", - "0x8eb3b3e3135720036c1120c4e8b8d405b00d002f2bdbe601a06f2c2fffb940a9966d18636ee34fc003dfef547d8f3b76", - "0x898deb30ede570d391266c81132a78239083aa9e27a9068e26a3bc14ff6468c3f2423484efb2f808b4996c16bfee0932", - "0xa90d9502a9785e55c199630456fcb1e794bbeb0f5f8c022e66f238a0789998b126cf9911fd0b7d463b7706dc6f9ec128", - "0xae8af784224b434b4dfa9ae94481da4c425602097936623e8abb875f25deb907aa7530bce357786a26ed64ef53d5e6b3", - "0xb544c692b046aad8b6f5c2e3493bc8f638659795f06327fff1e9f4ffc8e9f7abdbf4b7f6fcdfb8fe19654d8fa7d68170", - "0xac5c01c51dac6ee1cb365c9b03f09906d9b7b9b4d1b73c44d9e8e06823025d7070f242898a975420bc87d6372382cab8", - "0xa4632399c1a813e41fb2055ef293466098ea7752a9d3722d019aa01620f8c5ecdc5954f176c6c0901a770cbe6990eb11", - "0x85f7ae1a7a7c793c408750ddec2d7f58b985fc3cdf9fcf6b2192bc57092b8a271b2fb6ced0639baaffe0bec3203e568b", - "0xaaeb466f4316874c2107a0de38dafafa65ce50039c20723e8797815238011426f4e77e29fc573e7c6d2df85c1bbfefdd", - "0xa8fd63da16dd6a4aa1532568058d7f12831698134049c156a2d20264df6539318f65ec1e1a733e0f03a9845076bb8df8", - "0x93600f65c090814cee5cbd5f22f98e79c69e63510501a0be6a74b111e4c52441133fc1c198c7bf235f9005aeacf1d441", - "0xa69f0a66173645ebda4f0be19235c620c1a1024c66f90e76715068804b0d86a23dc68b60bca5a3e685cce2501d76de97", - "0xac6e7e9960207138d5b4b6a7f061756c01cc4a830e5988423d344f23544ed0eaa790aed63a22df375768f670cc9b9bd4", - "0x93042dd42e56671155bb40d85d9d56f42caf27bd965c6a7a7948b39089dba8487d4d5fd30522dba6ba392964e3ffd590", - "0x85bca2f86423a09014b562f7dc613246bedffdcb3aa41fee02270c13e6b00c8d6704dcbfbafc5997df6a90c7fc08c29f", - "0x9550072f64a48cb86bfa224d492be9a91e5a6c9def04b6a290b7a19d96981937d5a2c35de6515d1881bcb167f6e3d661", - "0x8d797819318cdf7b26405d1a327d80d4c289e56f830b28d4e303bcb019aeb0b3d69bfed58adcde8a2445dd5281b86af1", - "0x90c04eb3f3679cd630434418cb3a225a73254887692429960bd45b1613f85b2c14723cd8c7f1e875588ed82b7f5576b7", - "0x9953a7cbc152f101a60e3e381f2af17ebe7401e16ef6462d132b8f0f6c6a18837914a1299d1605f9f289b9561112f4bb", - "0xa9300a33927335f482dd0e44d0d57704ebeb278f732ae8301073cb7d5e457f02a0cb03268de71d284b8c23fb96947469", - "0x88554c83648ea97dac83d806cd81d92531980346b208d281fba489da15a0084fd4d9a00591d1ca67aad3c5793685d55f", - "0x9131874b09aa95ba186bcaa9e040fabc811b9c7b905b7dc79e902cf2bb5816d7ee39b0b55be609f22bc8c538760b2037", - "0x8368bb9b9bb2e17730c42ed1100eb870c88a8431601312aa8cb1e738cdb9ca2704dfd432cf1703c0db043259819631dc", - "0xb49379bbb9f954d2ef5574199607bc6b3aa2cc3b48dcc3745cc77406bba2a394929844fec1b87c4ce65cd0ca0f83062d", - "0x876afcd045c8a18967923733a3a43757652289b0974cd348238a693f30bb57f38664ecb97877a5e5f7d0185039a2bf54", - "0x8db19f6dd3789df179ab606508ca7e3da7967ad4340f630bda83df54c197d6bd3a14980c87fe10cbced7678c0c300ef1", - "0x997a91da55801acb6134d067ad65a9a44ead0b53d3871bb97b46ec36149d25e712d7230d38605479796190abd3d134b7", - "0xab1abf9cf630d6cbcac0c503df44603142ac81acd647784ae0e8fc97800ef04378bc9d7f2087f959ad4bbbeec65b8dfe", - "0x95718b06017ba9d45894867fd67148645d25d9db2229aa89971f444641ba9db4c5c6f0785f3b25cf2cd7fadaa6adc5eb", - "0x8ec38c68afdfb6ba019204039c2fb49a35467058f561f626fa87314d705fd615a7b9966576052be1b3690028d3c5c7bc", - "0xa5d72ac4cdcd847d67cb5a68c6141cde99a91303ca84165bbdc6fd7f643422faec783de60739e1b2753088280c90a68b", - "0xa778da56ddfe4a383816b43b027464d7a28689fc4a6b35b36883d3f36d9c41f0177bdbfc8f258afe8da90f02d3b64fea", - "0xa2ab566033062b6481eb7e4bbc64ed022407f56aa8dddc1aade76aa54a30ce3256052ce99218b66e6265f70837137a10", - "0xb9c347c1c350fb7ef6ee9ca6780cf0604f30e3a74e9d0234bca6b3e7faed26148f2b736d9fbff6b04f5b947fda458e8c", - "0x815f53751f6d3e7d76c489f3c98d2b49214938cac8c2b417e2d17bb13446c285fa76fd32a97e9c4564a68f4faa069ad2", - "0xb3f1319ae34ad1d59207288f01d3d7b7e1bad7733fb4a819a09b011d72a4d736bd3c7afeb74cf56da0e00cf712042ad2", - "0xabbfb501071148e98b6aa56308197356fd993c93e27fd58987eca82036c1ae0ea89f9fb1a06c82851234643904c58453", - "0xa0899189bba608887c6cb729580e570ecce9ca7107865ebd30def867afaaa250bac407c30dbee11b7ef6cd423269a8fd", - "0x88e7a12a90428bb45bcf4b01442c11607433211fc2f9bee9545304eb66e0b4b5339360160bc782e185391385da7c5ad7", - "0x82714b00a822c30b317ffc1d4ba163990cc1ffe5769f91906a7f71ad1f62b39865a5314433a4ab2ba762b1d62b01003e", - "0xa3a930dd70aeeaff0f2e3790927d5425db40467ee106261615de5fcb937bb1621be213ccd8b3a14d96c5908bedc2e421", - "0xb2902161b565dd5b8e8c54187b26f01741a39ea8bc1120598661bd367cf8fc73e21eda2f0f6f9ba2270c80a59ff5985e", - "0xb77cdf45f39bf85ab3e8c8afa602f159de8352188aba5378957d468315a2d2326daef83d8ac6b227f1e7a514488afbc6", - "0xa9e573274f5a131d6c7641bc0576a2621b6466a5bf2cecb21058160a854b1b9e0be176da2b6b9b3ed562fc36c5f09119", - "0x876561bba29e656b7122f1cb51a02dff1ac7d470217d8a4799c01e61816c4660eea91843a5a42502ddf842d2daeb0586", - "0xa0ebae60a998907a19baa396ae5a82bfe6aa22cf71bfca4e1b4df7d297bd9367bbeb2463bda37aa852ad8fd51803e482", - "0x84a6edac5ac68a7ca837c46d5ada8fab136748b6c3a3b9165dbbc231ec386b15328e4ef7d69a15d4cf354135348a4ee4", - "0x8b476b3b065a3b95a3d11ec60a749c2258ddcc5885bfb50b8a086d3fd1e49ff73e1dde733b8981c3d2d206aa0c87b09b", - "0xb48490c5a3bc9e66cdc78994f7c73e0f2724fec8a304b4147799e5142396df155ef7c42065ed6d2c0393d138fb4d2a0b", - "0x849ddbdc3ac55ff22a3b2f4bc51892fed694490ab4dd342165ac38c725c8b38921eaefe3c443962925fc3726aa41e236", - "0xb49c45d9da4aaa64967c28f1fd77b7f709f5a331b58823eb1613856fd8f92635135981830a287e8dbda3a0e0fc481c5b", - "0x93ccd8c5f82374e0bef6562e16576f742d79b6f400e3485ef36e148088b61fbd882c3d2bb38ab0b43fa1dac77f31d543", - "0x81522576ae4ec9358f1a16934cd1c1116de609634e68f552924a972101eb7215f037ab8e0582d7b13541537d55554d31", - "0xaad60e58a19598c5013b37e2e4adc6721eaa7e6e184960d1dc4e6f012246abbb58a047c0679064d5eaaaaff02de881e5", - "0xb4f034f2b53ff9989e8a0f12c1484c58ed7942432a429af58a6659feaf23f7d2bf20ff7b9a7e0a28a2e09c9a730681d8", - "0x88b2c68b425269850c1a4f4608aca194da5c641adeb99e2f7fb92e34b8245dff066e73bde072be60f7f2c3d3d13de3b6", - "0xa749ab53fc2662a0796489be84fcfa59bb723ff748bd8980df0cb4b3d1e2943845b0d7c67576fa0a33c8b0ff8a86932d", - "0xabd7248ae069d3a3a45b0ef4dd5d7d54b62994e578ea20bdd3b7876596673953b94c5b109a6e4b953b517544b915368f", - "0xa49da42c27d019a21cc6489ada7b712b98c4ede28ba25dbcfa916acef48446a2baf73e03a48be763378a09774d4a03fc", - "0xb3c36fa39f668bbc3fec028875a820057dbf96f727bb423280da96d5d50e885d23bc23fb73457bf79089691ce7663a7b", - "0x8dca376df4847cb8fc2e54a31894c820860c30b5e123b76670a37435e950f53312f089a8e9bd713f68f59fd1bf09202f", - "0x95d1f944b0c53eb3e9fcd5632713602bbb9195b87a172a370ae2df98504612a55f3968615a39b569ce6a0fe9fb559be7", - "0x8c9fefe233d0d657349b7efcdc368f5aaead27071d224af780874751e7d241f6b88f7650fbb4133043b24bbebc12aa48", - "0xa2ee6c29efa982e9b9abd3c5e4f14b99d5d0369d7bfc3c8edae1ab927398dc8a147a89e127b3324d7f4e3a7494c5d811", - "0xb7ea5e0d3cfcf0570204b0371d69df1ab8f1fdc4e58688ecd2b884399644f7d318d660c23bd4d6d60d44a43aa9cf656d", - "0xa07826925f401a7b4222d869bb8794b5714ef2fc66fba2b1170fcac98bed4ba85d976cf9ee268be8a349ae99e17ac075", - "0xb0a47515752c15e4dbeaf9ee27fab3b5c0db82f5c685e8f716fd7d8764164944340430fe3db1a5679e6ffea5a16dd919", - "0xb7de6d7a4afb05984dce153e5570b104338265e45c8f0156f4d45c458f47add234a479e01c02d3c1817c170b5b65b100", - "0xacd4d1e11f81f4833353b09d4473ec8b15b8ff31dbf39e97654f5338a26c4020306d51018f1f4b9c4efdb92992408a6e", - "0x80e09f3bf3ea87d48e04b53d8f3b43b7e53d61f445f8c8a5a35472b84f6bb4f58f17d9832f5881bb44fc06156151e5c5", - "0x98d6d46f603afebcbc561c130e416d5a588a7e6c1f17f89ed6e30538b7f8dbf4b3c75b8a3331425c4ca21e03fe8b57f3", - "0xab671eb947490c43fd05e42a787344b21af89babb705393c82748eaa0cfcf80bee498d275a1eaf1d647ca3b2923d76ea", - "0x80e1dbf3296bdfa98aeec1a7560682165d13bc628061bd3257f345aa1ba13f8bd1bea14f117af562be22118f5a8265af", - "0xaef456af90354ff88039d2dde02b0f5a6790aa762b23e0a9da8c6ec92c3b8b3320687bb21666608b4a22615843afd1ef", - "0xa5a07bf219432e9c80c38596c93560b49c7de287f31e30b7a06fcb4d15982add4a24085adbc5b753c462be989c64c96d", - "0x84d2eb008578aebd6f01254b7e46584c1524e6fd7a5a2ae5fa0ea560865ca50d52290cf2d12dd20b042f402e62181b4d", - "0xb8e5226ad3515627ae6840235f5f7b7ecd54e8f01079c324d126ec852f6665ebb77168b3f2b3b51580e04a6ff602d5b3", - "0x8c1de4264e04ff7e8282faf81c0bfb5943656451be52170211cb7adf4ff21bccbb789400735579c622f69982fcb8e9c6", - "0x90bfbe37ac3992432e68c95c0d4342a9712126d1f50089239c9f4f6c0c202b54334e08604d245b97dc8e8f6706f6992c", - "0xb0a771b9a0dd7e352d46c8efcc1834e610dd097711bf7117678a99d386890c93b9b901872d4dcacb6dcbcf3aea0883ea", - "0xa4eb903990bee2374b14fa66fc262d6821669537e9ba241c87b4b5c9e2b89b32fff4bfc28ab8471ef52e8eebc3e743d1", - "0xa6b74c706b33d3cae9b7adc5c7502ac98f7bf94a14d579d2bf77b613ae555634ad6fe631ba36dc14bf44526436355e24", - "0x8296f8caf58316af535def398a43357e48cb3b1e674b857eba1bd1b970da3dd045e22fe6d17dee4e9117f62ece3ec31c", - "0xa129c9cf33df42b5a98ad98be9d940207ae154c715d3bde701b7160dfe45304679fb0481a4f9dde242c22a9849fc2d9c", - "0x858b6f1bd3e68fc536bdf1f4bd96db032994eb76e71571e2d85af73b898478b82f9ab432732b0beebc0864ad8025ae33", - "0x8658a15df961c25648fd444bdf48a8f7bb382d9212c0c65d56bf9cdb61aab3bd86604c687fb682260dbc0ad2dc84bf01", - "0xa8d152e5d94b75cb9e249230db21af31de4d4f3d4ef60ccbf2212babf69aed2a38435a993ee2f13cca410ad55a4875ab", - "0x95757096c132e7f6c096d7b93a5a0d2594d5e609b9f13c4a9f878e95a389fa1a111b185dc1fd8f7d98b737dcf8d2af60", - "0xa22542a4a2ebde18cc6aa29d5dace8b4f6720703f519610dcf01e671018392aff15728e3764730840272c9cfb074b612", - "0x8f90e72a54e6894d511061957162e753010812346afd4d90cfedb678b99ba1aacf2b6bd0e49b4b0e684da8082a048619", - "0xb2affe048c187d311a185503d8958cacbe03796edf79bc32e8533941004d9178bd2e376e627e1ba61ed43850c0c455cf", - "0xb549d272a7f3180826a978d747507e4dc80d82784abb655cfcd3a69cc72e7d58c70febea1ce002a89852a8f934ea70fb", - "0xa9ef845ab489f61dbfdcd71abcc29fc38f3494a00243b9c20b9cd0dd9e8a0f23304df84939b9652cdf5542d9b3ee085e", - "0xaa744c552b5fc41e1ac6ca53184df87a1b7e54d73500751a6903674041f5f36af25711e7bc8a6fbba975dc247ddad52d", - "0xa802b9ffbd4f01b877791aba27da972be4bacacc64a1f45687be4af01b84bd4b83fe2ba1ea78e29d7683f6c777ab2543", - "0xb44d2d9510516c0abb4fc101241cf0e0223b179fb70686519628c27f0ef56381232961bc79a30f592ef093ffecbc4486", - "0xa684a09add047c0fe648d9c5618500d1816047168e055e8ac8c952c3544a462cc095b32fab07d939947a58fcb4ec7ba7", - "0xb5eb31e5cba0193e74968099ace5808dfc457c6f404f270fdc4949b60daa7607ba1811abab1bb19fccdad61d489b6657", - "0xa10f19657a9bc5a5c16ebab9f9fddc3f1d812749cd5d80cb331f51de651873ff899e0670f1b079b29a194572de387a17", - "0xaf61f03e3ceef5bef36afa29ba2edc7a3b01ca26cec2589edbc9d124dd46e41410e0e3afbae959c83a6f839bbcf8049a", - "0x8e6bbfe492ecbbb8dc8889d3dcd7037a58db605bc6bb79131a72a9b9c1bad630e75f5e5e0c1bc407e73f3d13b116739f", - "0x983fc1ddf17f9756c9cecc00b39bb2ad432587a5c6d1c3296a383b9f539c9afe84c6c818447a709c0b686ba26ce5ea3e", - "0x8d52413f981bc611427ad0534d25e914113d0ebcd6960aab6421608bec6648b89ae4b2ca2153c57d3cf4f1f37212aa5c", - "0xae075b66e5f211c2149c45b211d1297bbc1d9e6497cb3315363c492a9a51ae5b9d0a28bfecd755d68553736901ac6606", - "0xa02f7fec0661394399a82b2e3151009160b3f5392017ba579b301ed42c85100c295acbfed46b6c58a9d71796ed0930e6", - "0xa3a7196fecd25e9cc7cac79c35365676e48c7be1493df255676adff2209c0719f2190ceff3ce008d08efa07c244c11a6", - "0xa5fe3dfb5031517bb8db0d5ade1e9f438e84bcf23221de003b03d2a2f4ea97629e81c79abc3769bdb8c69a512c189b91", - "0x8cd9d7e953c7ae07ee785d68a999e702565960d376692d9ea468556ad141229b1f3bc97926818c078901f73ecc578e93", - "0x81d6fc2f01633e8eab3ba4d72588e14f45b00e68ab887bdd4ec5e8558965db21189310df973837106216777b07fc0805", - "0xb75ac3d5b3dad1edf40a9f6b5d8923a81872832eb3a38e515539cec871a353b07cb477f6d55cf15ba2815a70458aac32", - "0x94d3c9406dc6dd7241a726355643d706e46b35f1ffe4509ac43e97c64c07592821156ba02ec9a78978e66709995a0ac8", - "0xa3b109249ac2900806f0f39338da72d4f2cc6d1ac403b59834b46da5705cf436af8499fa83717f954edb32312397c8d9", - "0x8336744d8ef3a3bb3e9ed3d6b83e08cafffc76b7438adedd3a7358b32acec0e73a4635aa3166362ab4e158e68576255d", - "0x99b74edbac662fff69ba412de466a427a928ce2363c9e59dddd664f6fa50f2e1dd3d464701b01784aa224b3d96dedea3", - "0xb397ed7134f447d9bf1c511bf92f2d27d7b6d425b8b411365fbef696cff95c2175461cf5dd83d93bb700e50ebb99b949", - "0xa3e1fe11f38d3954a7f48c8b68ff956ea0b6f8a3e603fd258c9406ec2b685ff48241db5257179ea020a83c31dc963854", - "0x949cf015ce50e27cf5c2ff1b8e2e066679905ac91164e3423d3fb7e05c64429e77e432db0f549acb99f91fb134b6edad", - "0xa1c0c317e6e352e16e25c140820b927161ce5d2c4c2e10bca3057ba4d46b4f42ad7aba20de86dad9fc6368ea92695268", - "0x8d6e3df29419bd0da1deba52c1feebe37744108685b49ca703e1b76fb4d612e3959d3b60b822506e5c0aac50b2f5eee2", - "0xa15e0cb96a463ab81e661ca44c619b71a159680bbc04707ea5a5867ff38b15416e3abe55d2fabdab9aede1f157dd37e1", - "0xb6d6482ad7b9b412ffbefbbdcc28eb3d091b1291f54f77bdd53c4ac85f705c454940f466dc272dde7b03c26f0cd6ecb3", - "0x8391e3ad6ec2686bdc686671d579edac2d5efa8cf0923577df28fe0735e4d5103173d44452816e3c2b2a7fcc1fcc20d9", - "0x818202d7cb60e4148c71633ccbe1ce311de7b7ff93a1988e86ba29cc58037189f0f275b3323a6719dc9bdcfbc49c35c3", - "0xa7c0fcc422c6da878926cc6763ae6ec685a5d8fd1afe61269957be6bfb3f1705a8e4c6e6d85bd15636521f5a2ceb3a00", - "0x8f7bbaaac458bada6d852fe665c87c646133bab16c0d5136c3dc922095b9d647d93a9de7671cb7bfd4cbd138ae0709d1", - "0xb6e9fe9fa3d4c833c3beae7f798f30f07e3cdf6f6c8eb8e2b70cad51b37af2549dc9f2e7f97f194e5897d4dedb904a45", - "0xb455f751232de0a48440d09983f4f4718b6169907979c9f282acf7177ab5b1f338fe1f2acd8d0bee4b4aad61d0340839", - "0x8aadfcf3562f1c357068323352cb1745349a27a7362358d869e617c2410db747149b993ee9e881e252ecdd42fd75f351", - "0x91bf4c32fa8888d3829d3c33e12550d2ecb70762d5eeecd044d4902e4a7f8b7a2592cf6cb7736eb6bd9d312f85c2777c", - "0x859426bf6211e68924eefdb26cdc168ac0deab291aaff7036163997bff34d45809932f91e12d113784c05553ca773b15", - "0x81730b4bc5f755e5b99c321a6996c65e57ea2ebe6c0e4e404ed30920194fd76db65304635ad054a8b25bfd982cead47a", - "0xb26f5ed09f7d5bb640ec94ddd1df0b76466f69a943b4699f53d45296d5d6b8010bb61477539bc377d1a673d89074d22f", - "0xaa65c11071be23c9bddaa5203f3166e5cf043efe5fb8f4b26f8a9cabe71db701a450e79eb001c401da5752755d9cf1af", - "0xa019370ca799c2c98a605850910cf43531bfb616393039fdfc370848feedd3339b2427b750ccc91952b03a09f690e9ed", - "0x8f84cba7ceb7652023fc8ebde4b00ecde1f550935bab12feb630d6f49517b4148f3cde184bf55d4f6ec99a849fc6f862", - "0x838733220d1559c800cf1714db8a43a67a0c0d1d3a9fc1e2cdcf615d20406501e5146fe8b59bf64f4c5daa1a6d74f15c", - "0x9708cfcc9ff95cf23f544119e17518a338575018f153b1ef50118da0681304919a226b2089a417c2ab7b4320dffafc2a", - "0xa988cfed9f481bc98beb5fc188ed3f6893a3ebba27c3ebace669792f6abf0997727023c3b6930a6421224f5b257b8b49", - "0x812d3ded3a3c9e58eecf13a29bb4cc13b01b2a0af322423a29bb0e4f6d9021d1d87ac4af7a2a6b88d34f44a8bc1b3c55", - "0xb2baa7eba496ac4ef60ad8ef27a9677f9507820d95a1c572d322621c4d0226b36146bfc3a9ca1645d123acbd945de3f4", - "0x89ab1e5c2565f154f92c9b3554160832d176613f1a2f872b6ed62ed925a33fb0b40b71b7443eaaa15099ab24693c8d13", - "0x8982534f2c343dda20cccf5a9c8bf98240bba5f4e8eb2206e63a1847097deadb6bf0d24b358014d564c5ef1d0448c43e", - "0xa1e47798a782a024da340d6d6a1b1e5e15a0f2d8668adf349ca375086964414a563cc1eea3226ae637f87e78c0a630b3", - "0x847b58626f306ef2d785e3fe1b6515f98d9f72037eea0604d92e891a0219142fec485323bec4e93a4ee132af61026b80", - "0x8368a0f17c8427beb71dbf11a09a2fe8495a33f08c29c74a9a996a88aa01c0a09f9555abeb1ef1592cab99a9e05875cf", - "0xb38e558a5e62ad196be361651264f5c28ced6ab7c2229d7e33fb04b7f4e441e9dcb82b463b118e73e05055dcc9ce64b6", - "0x8018499ef720e28759133033833edfe17ed23e42f99058bb79fe844ddee823cfdc43916be2dc9724d18f9726e6f1b409", - "0xaa9b9cc039d147677aedd1e47ad9013fcc0da0164070ff7305b18e5786c7fac0471368637a3adbb78f3af174a5c1592a", - "0xa5c0e42851b769d2d822e39222e708068455aae3bdf782975b59d3201e67a58fd66e16d380558bf2086bcab890a92dd5", - "0xaf3f765fd293c253072b33a780ed68933f78d7e079d9a2079b6232755bedf6ebcbce9ba65c01f695602fa8ee17899867", - "0x8a0192ef0903d7a5ed2e5614a715901f2554b324ee72390974dc90727ff08dafa580041a21a8e6c48a3e08e1b042afab", - "0x907c827a4fb5f698bf0e6f10ca07741c5b8e3ecb26aa53f938ba34ceb50c01be80c4afc5ac4358a5fda88eadea0cbe73", - "0xa35ee5c2d7800489723c78008b495e1742f0542dbb487172ef438f60424c81aa41c2397095821248066140662133f6f4", - "0xb1925ee53c9228cf80e2f5ac0117008a91e98d878f69bf03d00d873ef45f4351cb6988772d89d4ccddb40778d11e0dd6", - "0x95aafa379cc6a2b4bdd0cad30b7f0a47839952af41f584219ec201c6c4d54610eb2c04b67b29080acb8cecc5e7543fbc", - "0x8f44c43b80a3c5f488118859fab054745cfe5b0824821944b82fcf870fda6d93489ea9ca4220c24db2f4ad09c6080cb7", - "0xaf861bb21f84c3e7cab55370839d18ac24c475a9e833607fcd7e65c773ee9d64bea203ee9e8fd9d337f1cd28d5ca0d90", - "0x8f6fde2ebbd7682c69026069cfe93aa5410071f05de9ccd7070c8c3299a6539b39b3798f01a0b4e9b1330510bdb51de7", - "0xa35d9d6d5dd5428cce7616842203b5fa3721cb4b20f50c0113f138604954fe0cf214ca3d065b578f921054b9efe823df", - "0x8d562d6c0e0d8325032e1fbf836022c82a8f600a6fbb56c553ee5d1fac0f052c7ce2504c0fd48c9fa6494a6bff63c9fc", - "0xb02ce594310f1eb8acc92bb80de524a43e663e12fb64fc28291ff207f9d8ae761631416410c3c8f4d6890b8b7e6ed24d", - "0xa258f8c0eff666c2bb70e505544c3d10d6b258310e4fb2206fd0999f69bfb739a1e232d1e810e7206f490f6c44f6934a", - "0xa8a77936ca91df3b2ee7394ea821f2bfe91c6ad8193f44651466c170b6ecca97ab356fa7d947ebb4b767e8967092f143", - "0x88ad79a0320a896415e15b827a89969b4590d4dfa269b662bdc8f4633618f67b249f7e35a35884b131772d08025bfc32", - "0xa3c66439724d737d20a640bceed8671b20cf6795671b6d442ed1ea5eda6723ae559396c24f44e982ba7751dcc6adef5c", - "0x8d0e6475acfa2b904e7d53bc7acd070a2ee4894ff5720a20e560e9ecb7872ea442a51cf2f2eee4bef66604a5c08ad9eb", - "0x804c021152c3304853941847e80480fdaceba3b9676fbe018268cf77d1a1856966c2f9686bb4d4aa0c4118a7e85f83cc", - "0xa6565a060dc98e2bfab26b59aff2e494777654015c3292653ecdcefbeeebd2ce9091a4f3d1da10f0a4061f81d721f6ec", - "0x8d5de60e934ea0471d9e0a46489f21e03abb9722f5b3633631a9a099b9524beac5d67716969c83d824498796d9c106b7", - "0x8c5a9f6eb0a3ea95e75362b06e5cd23968447a212cf22e1419c984d74432c51d290b717f80e8ed3e76b1232216f99758", - "0x918ebb73eef984d0ce28083306626d04817038056aab4a82ff9ac8176ffdfbf3173c0b05e936daf553248b323fe54f56", - "0x8f9aededb605db4e499d3c383b0984b1322007c748dea18dc2f1c73da104a5c0bece6bb41d83abdfac594954801b6b62", - "0xadb198f70a7f1969ed0958be4a9a60dcc1806bced79c63692b9aad6c5648ffea1fed60b24bf4b1862e817cf229e93e83", - "0xb118f77f99ac947df97e7682f0fb446175185b842380af4ee7394531e4f93002c72b41a57a7c1b923a4f24b10924c84f", - "0x944f722d9a4879b5997dc3a3b06299182d8f68d767229220a2c9e369c00539a7a076c95f998bea86595e8ec9f1b957bb", - "0xb7270f33011db1bad18e076a162d6e53d9123808609773eb46e3a4ac69c84c257407907bd5d05b6eb5e926b8d8c6d884", - "0xb09c0a505457c6b473fc7b2d634222905b36a6ffcc015dbdffa3bd62218c94e891615e77f28e6e18dd8474be8c156695", - "0x97d076617cf0a64ab3d1f030cfd72a303b6b252c0a7b96157ff7fc8af5970f00d14492c46e8f6f37caafe837d0dc95c7", - "0xb405520ef829a2a3b8947f1687ab56a7af4026c1a6f99f59aa192bc4f3b12a2de407862ff74ba1b2c51889b8d6b090c7", - "0x8bb045e7482b7abe670d72eb2f7afe4207b5a3d488364ff7bb4266f8784ea41893553a4bf7d01e78c99ed9008e2c13bb", - "0xb2df29442b469c8e9e85a03cb8ea6544598efe3e35109b14c8101a0d2da5837a0427d5559f4e48ae302dec73464fec04", - "0x99daf03fa434a482d9aa4d090884c99b7208c1f5380b9acbf304e1bc33d3d6902afa5d248d20ccf03795e26901356ede", - "0xb60df25a7ac1ad14aef7e8809c4bfc190b715f94f14b0534cc2e4015ff6c322883cbdc5309e05d67dca4bc641c69725a", - "0xb46a818f3e492e231d8fa8de8848c16f0d648a2e0d1c816adf9306a8596fdf45922e59cbf745430570a19e54f45e28f7", - "0xb3ca2ab7d64b71e40693bd3e2288a1f78741a139403c783d259cb9dc9c29f16c00796b6302cdcea4a4314e132b4f9d1c", - "0xaf3d3dffbe55842dfb4417295a6ed1a82d26a579199494b305445215045785759be4cb57dc870c7ddaffbc101a854a92", - "0xb21785008910a949804d1291e7533752641d31beae3cb518806488f81d58c38a5efe5ed9534ac692e68c3121e2f9d97d", - "0xab7eff4ef8696db334bce564bc273af0412bb4de547056326dff2037e1eca7abde039a51953948dd61d3d15925cd92f6", - "0x8bb4d08318386c91a0136d980a42da18c05743a5c52a861ce52a436e66a8ebe472dac7f7461db32ea5da59a23e9bd6c9", - "0xa1c84730a5c41dcab9a5ef9e1508a48213dbc69b00c8f814baf3f5e676355fc0b432d58a23ad542b55b527a3909b3af6", - "0x948f808c6b8e3e109a999657ef966e1e02c96a7aae6eecaf912344e1c7bf7ea51c911cecd3cea2b41ff55acc31df9454", - "0x8d264fbfeeebb6c4df37ff02224e75e245e508f53fb3446192cd786ecf10d0f704c4fc2e53e7f7318ae1407e46fc0fb8", - "0x8d47a7c2c62b459b91e8f67e9841b34a282ceb11e2c4b0549883b627c8526d9e0ebd7333ba70630bc0ec2478114b6ae8", - "0xa4348ad30c12bb7dd03dd014cca599c3499ddf348e7795b0392a18f998289979478374e374a8297b5b6c427441e2b5af", - "0x92ec1aeb2aa24c51cd5f724972c8b6095e77b237d83f93ed34ca0bc91a1dbf1ad95adccc59e0f0abbfef33f331f3298c", - "0x941f73b2138b4347ecafcc7b8c3d03f2a54dc49f580394ed08f22b0878ee7cb63d42978f1d320c09e7dbc67648c06f8c", - "0xa308ed8737b3a9346ff20dc9f112efccc193472e6fde6aa218ceae11e288bbd2c35fa45c1d8bb238696a96767cd68b46", - "0x8eb03001ac9e22c6956a682ed458e650785c36d23ddbcd51ac4d9cc991325c02519ff1958987a08eb29ff56ff6e2c293", - "0x80637db55287f891baa0e865d2423191b9a575620bc4493ea76166d47b99fd89ad8625c21f446b01e3ae17292c78f7ef", - "0xa5b3da08aad945effdb645c797a0a8bfa828c9d658df2783a214597acebda3ffc07ee48d0ce1147d77540b557d9ada91", - "0x8d5e0b8cde1f62cc8f15d9f596e31de09c221da91b10335b92ef1155802e742442def161223333573158723f3408bbd3", - "0x931923f0c1f75a197e6244d67525b524ceb07510a6aae8cb3d56167cc1aacc76d26fadfa1bdfc55d8439c6ee4d4d8174", - "0x963a298fc8876b702424a697929c7a1938d298075e38b616c8711f1c7116f74868113a7617e0b4783fc00f88c614e72d", - "0x91ead7dacf43905eb5d4b179af29f945479ed074126bad3b5a2bbc1663af5f664fe53a36684e9389ab5819e53f1344fc", - "0xb8a6c999068c13fb71a99d75eabadf7edd2d32e28607baf001a0aeec412fdd3575602c68d3feb4d743b90396705e37f3", - "0x938bbaa0ba14597067ff4c0a7cfc1529c44160d6f61cfad12246526d84fb7a1ba964d3bbb065a348cf7a98356ee15234", - "0x80e44d3577f12cabaed7074feeb57162ff80b61f86cce8f41d1d1250bc455070b09f6ea9d0c263b7b4824701480f4c14", - "0x8d74f4c192561ce3acf87ffadc523294197831f2c9ff764734baa61cbad179f8c59ef81c437faaf0480f2b1f0ba1d4c8", - "0xa0133deca5ae8f1100df8db69920b2d0c31aa21bd3849dbaf4c264eaeaec8937ab8f982770ce1ea17e0e258910a56d02", - "0xb6a25d493d708b035b853f1f7a6628d8e0b205d2678293f763d7ea4da11d298539533b22b43ed2e5f708648556f3094e", - "0xa58c3a4ba86d0d6b81c8411bb73a528b4f3bc2debac0e0208f788c080a3a96541d57c927143c165f595070afe14b0517", - "0xa52c15840b89d92897d1e140b2b8468a88886c5e1092861e598b3a433b340ded5b35b3d632a9879820fd56f20ca3a68b", - "0x8c122bea78deee98f00a86184ded61c10c97335bd672dadddc8224a1da21a325e221f8a7cfd4e723608ebcd85a2f19fe", - "0x87a51e0011dd0488009baac9c611fbde01878f9cf1584ea407599742bb32ef10586d9040dae3e9800a125de54f80c047", - "0x98181e9291622f3f3f72937c3828cee9a1661ca522250dfbbe1c39cda23b23be5b6e970faf400c6c7f15c9ca1d563868", - "0xb01a30d439def99e676c097e5f4b2aa249aa4d184eaace81819a698cb37d33f5a24089339916ee0acb539f0e62936d83", - "0x830e70476c6093d8b9c621ddf0468a7890942589cae744300416639a8b3bc59a57a7e1150b8207b6ab83dafcc5b65d3c", - "0x83ca733849830cb8fc2ef469e7e464fd94def561ce49ff0aa352a6ecd0e52c7aefcd69ab59f3d1ed2d5b8536d0a7895d", - "0xb76cb8cb446eb3cb4f682a5cd884f6c93086a8bf626c5b5c557a06499de9c13315618d48a0c5693512a3dc143a799c07", - "0x8b027c14affe47f83ee59b504d83b2fd2d9303de2c03ee59d169bb199d9f4bd6533d7f8c812dd7a6f1e8155e3e185689", - "0x8e6b888197010ebadd216da35b9716daa8675d93b3c33a96a19fd9ca42624f6b430b2ff115cd0f5b717341605dda24bf", - "0xb6652440bd01316523feefceb460158cd9ba268dd8dbe860a0271f0176230f057767597e4197885ba907318ca202ba06", - "0xa0b3dff15982a38a2f56d8c6cfc5c5543c045bf2db24571d23387ccab42abe2756f34d5f0bf6a426bbad3c358b8bdb00", - "0xa8b593de2c6c90392325b2d7a6cb3f54ec441b33ed584076cc9da4ec6012e3aaf02cec64cc1fd222df32bf1c452cc024", - "0x96b478b1e5e49d4ea3fd97c4846ae0f781dcc9f9ff61ee022ca92c3d8dfba89c513c46e8bb38b73e6b678a79b9b12177", - "0x946d585d7aa452d37a8c89d404757c3cce2adf2410e18613483c19199abd88f7a12e206f87a43f6009e42f4e31ed20c0", - "0x8c432e044af778fb5e5e5677dbd29cd52d6574a66b09b0cd6e2a5812e71c91559c3f257587bfc557b4b072a822973a60", - "0xad2cdae4ce412c92c6d0e6d7401639eecb9e31de949b5e6c09941aeafb89753a00ea1eb79fa70b54699acbfa31eda6b7", - "0xb504cb87a024fd71b7ee7bed2833115567461d1ae8902cccd26809005c4a56078617ec5d3fa5b29a1c5954adc3867a26", - "0xb8876bda1e709ab16e1347a1107852a7898a334a84af978de39920790b4d82eb0739cbfc34da1c7154dd6e9f7674759c", - "0x973dcf44ab60f55f5d10a8753ea16db9faedd839466a130729538f3a0724f00f74b3ca1de16987d7c6e24e9467f62bc7", - "0x94df5fe87661101a89b49091a3d4de89331cdbd88531ebb08a95f2629886ee53b3dcbcc26bb6bc68b443303d8d397141", - "0xa92beb343caf6a945990adcf84302c55d1fccdef96c34a21f2c00d3e206a9b2c6c6b412f66e5d4fafe26ef6446cde705", - "0xb298aa927713c86adfe0de1a8d6f4083b718c8be27156da9fd11abd8edb3a54a926ad487801eb39cfc9363a0a3be0d44", - "0xb34d4d2e15079e7e80fdba30cddf4fc0e6c9a61f7ab06a6ea0a4e55fd5bf632c6d72e021d6264d935439d321de883bb6", - "0xb4a86fb5b0049718caead1bc036833a2caeb88e1afadbbbcb0cd021d95e1f33fcc916f0b97fc1b9226c37050e3463796", - "0xb6cec65e5268818c82c0a4a029b02f8d23de98b68730a445119fee670118eb34027c23c987fac950f9b0151631328a4e", - "0x880b4ef2b278e1b2cccf36a3b5b7fbce94f106ed9fa2820cb9099a7a540a57e9fdeef5c0fb0a743049828fc2b8c46163", - "0xab2053c376c6bd113b89fdb2ae3b8401aa891135345885730c61cac7813f69ea7d906c531be752e28657f73af92d1f4e", - "0x8fa2d7b22af8e6b82679ebdfa13efdcb34289a554653ea6c1b16efb9f957f7fe64df787e7b03d8cdc8a732b91c916bd1", - "0x8e825c03c8409a3302266dc5f47fbfc381dfbafbadc37bd8d40f079ca8963d4c5ae6ef0d0ba6aef2d4688736f5f6bb45", - "0xa40ef3d2291d8782540961ce285054678b3d322d3cf7fc154207228c290708b1abfc37a4d7762dab3dfea582a112444a", - "0x8dbe8fcbcc414eb352245c52549973f73d987012de9d5f2b2f55dfdc43cf8cc9ea6b147abf149817f80f9e15aea566c6", - "0xa7e8775e04214e3b9898ffb9625dc8bcd1b683e333acdceddb8ca6db241df08a7b80e9d476a711b8b7d66aefca81e9cd", - "0x8b6ed54668f78a4a7624683b9bf3abf2bb0b6dccffccd8c0967df6297dadaf51732800fb9832b069437a6bf82ed7e6ae", - "0x9437ce85146202d3815df7f341a182678665dfb74b96006dc9d6acc16110d00b4a02717b702a765566457710ff5a7280", - "0x86a790072efa2cafa97be4b6b31f8c533f3b20cf3922fc0285fd403da436e4c49c65c5f08d77bfe823526c67bb58fab6", - "0x8ee8873de7cd28a54ba2c63a80b63399effed76b154e96ed26e7c0668b9f2476e298688b6a00c4b2ab9d020a897695d7", - "0x975c3261f0f32d59473e588f89593be38f5694cfa09394a861e4330b7800fb2528ea832106a928c54c76a303d49140e2", - "0xb5222582ed6604b9856a48039bd643340f4bf1bf0fc805e8453a9eb7630d6cea1452d28536706d6fa3ec16617617991c", - "0xb75c28941ee3f91b3535b4eaa0fb17b59ca65b5256601a1f6d0cf2bb4d66837fd16e51d6942856679012a5730a66e519", - "0x97b510f9f46bdf77a002b2403d8e42b6d6ad5329ea080940844429763ad3efd592652789c8d3d4fac0903c705f533cf7", - "0xa3fd9d8bbdc98394883022299fd9793e0c4f374d8e40d6ce89b2869d3173cb6a5476371d6095dad068ff217729f60af4", - "0xa57d5de556853484b1d88808d2529450238bc467376ded84cfd7b4a1ba258f6d43b5958220f962c57b033abcef1d5158", - "0x8465bd8be9bd9c2c6116d4ae44ec6618c109cb9aaee2d241e7a6ed906d398ef15a6fc18bc8b1d3398184241405954bba", - "0xab1cc44983e46a6ea2430aa6616ab28614f43624665e3e6ae31a9357c0c5434f34e56c720906e184327693cc4ebe1fa2", - "0xaafe14dd3b680f096010788226d8413ca628feedad79a2bc78cb04d47c6ad910f7f46ca87b8f8281744625d8f42d5eea", - "0x86a533b02ae929f67c301649a2d58651b98cdffe731b63fa32aa1013c271634bbb088c0d02865913c11bbb1bf57c0e12", - "0xb28df3e04bde4ec373171401dbd0546f4cb6fa8e9a4a8019aadc653d4e05e0b5b9a64417715dd87f6df9e7b3145f6659", - "0xac63fc758c1a3bc5cbff0f5e0b5a07a5aa801363b129d4e0360165c7dc1057ec37b0d808e9fd6b179e2c1e66bbc6090e", - "0x93f941b4fe6c05621e7a651b87669eefd60b6e8a4a8e630a51fa3fee27417b9eebce39f80a5bade9ca779133ad8388f6", - "0x96d7a69eaf2761bf0e5ebcd607b134d5dedba8e262ca1d6d3e8fbf23e6419a8ce1bbe4cd23b9e4b5f80db54a802a9795", - "0x8b62902fb2855300580e94830a4bc825d997ede33bf356fe3b7c08d6a8bd85a37879433fc6bee58f9b44ca280f4e8dfd", - "0xb043156fcd02b75dbe940c763fa8e8a7c7f6d74c1d5395db5ce544af3b6097eab61686950535a810aa95889ced12f74d", - "0x8614a7599c8d97aa9ca63b876f677977cf0daa969ff2a9a153f297a4a46e08fa5d91492995de94dc32cf009ce6bb5b5f", - "0x81351fd284d6d07092875f366bc5e53bfd7944b81eece85eab71a00443d1d2a9fc0337aaf34c980f6778dd211caa9f64", - "0x8c722aaf5d5dad1845056bf5e56dbff0f8b501f4846610f99da01130a49c96db9962bfd9be20670658cf276cc308be08", - "0x8effe3fb27c9f76bbd78687b743b52e6f3330eddc81bc9006ca81fd640f149d73630af578694f4530833c2151522dcc1", - "0xa3d327f48eb34998a3b19a745bca3fade6a71360022c9180efb60d5a6f4126c3f4dfa498f45b9a626ca567fdd66ffbff", - "0xa59a59db246f981e9c8e54aba52898c714d9787fef8969a6d8677fe6dec82950ff22a1364393b579f3e596cdf5bcd7b1", - "0xa15ebe9ab6de62a4d1ff30b7fc58acfb14ea54d7fa513cbcb045af3070db22bf9c1e987fa26c0c54356b9e38fa412626", - "0xb58396bce7d32ba6c70adbd37156d859e153c1932d2b0c7c874a1182ba831439e80d6fc6d7d88a870e193f515aef2264", - "0xb666dae42ea858c9b7d903ea3ca5279f619c71ac6e3fda7469e2bbba08c7e8e12d6a3c35ff2c6383673b1b7c21db5e0e", - "0x8eaaa21c8955f15bbcfd5756421a045e7b4825576379cc6229fe9751f7a7738b90be19ba52261db01c1e13af955675b0", - "0x8027bc62b59f9f15613e38da74ccc71fc3eaee26f096d187c613068195ce6eb64176013f2d86b00c4b0b6a7c11b9a9e5", - "0x8275eb1a7356f403d4e67a5a70d49e0e1ad13f368ab12527f8a84e71944f71dd0d725352157dbf09732160ec99f7b3b0", - "0xb8c41c09c228da62a548e49cfa107630166ac5c1469abf6d8aab55938ed1d142d5ddbc4f1043eed9496e9002cac99945", - "0x912750d2f1b21756662a400236f797b8ba76c73e5af95941a8c6ef9427838c4826715c80942cf8cb7ed01566bc490754", - "0x862af7dbb38ad7293a4e598cb52a8ac84dacee3d9bf007b5cb6a18a1acead0aa33f6dba796ce630e632c97aeb7100d68", - "0x890def696fc04bbb9e9ed87a2a4965b896a9ae127bc0e1cc515549b88ddbcbc02647e983561cab691f7d25cf7c7eb254", - "0xb835ffaf54c8b878d3c4262ca2bf5e6be2c691adced622b35d998ed72e1467ba907f4fde8d64ce43b43a8196f48e55db", - "0xa6d7e65bf9f889532090ae4f9067bb63f15b21f05f22c2540ff1bb5b0b5d98f205e150b1b1690e9aa13d0dee37222143", - "0x8ebfbcaccddd2489c4a29a374a2babc26987c3312607eadb2c4b0a53a17de97107c54eab34def09144b3098c082c286b", - "0x900b9972180a2c8753f5ff49fdd2cfe18c700d9927b3c3e16deb6376ad6ee665c698be72d4837b94911a0b4c183cb140", - "0xaa9679c01ecf1f1452c54688e01cb25bf157bde6b09b1ed460b8c175d65eba439c7ad4b7c1d72415f5622f3fbc068dc8", - "0x887a4277ee8754733f3692a90416eeac1ebee52ff23173a827f0ba569bd84efd806eb9139049f66cc577e370d3f0962d", - "0x951b27456e2af80436608aadec54ebd03bda37fa58452631da63bc5ff3eecb5ffb73d356b19f6c9c4225fcb0da8fda20", - "0x9104b5af82dbca914370eadb5518b26bee7ed7edeca74b741585ba8b249204e2c998bd47a02cef4335e236f8efafef94", - "0x8757e9a6a2dac742ab66011c53fa76edb5ebc3c2fbd9a7265529a3e5608b5c24b4482fed095725e9b8fed5a8319c17a4", - "0xa7acf82999de75f231fd80770bcb0f4c720d6b1e4a2558fa1ce854382fda92beb89fea5b5d229dad85fafee7a9e98329", - "0x8fb74891a8642f25a55b5eda5818367aac2fdf9663ad1e3dbc0166b484c0cda581b243cc75131167f1afa9caaf6705a0", - "0x897d7a19b70dcef1af006df3365981d73068c81f18017f32fb9966599481496efd5f6caceef943b31c52750c46d9590d", - "0x87ac804ccfe7f1fa156b8666664a397236832569f8945e11d4688a9e43ada4d4104efb3fda750f48ef655a29357c5e7d", - "0xaf18cf1e3d094b4d8423da072f98b3023d296f6a1f2a35d500f02bde522bb81dc65e9741c5bc74f7d5613bd78ce6bc03", - "0x9752561179783f336937757b619b2fdcb9dfce05aa3c4fce6d582dc966182eb85ab4ccb63e7e1736a7c5fad9d33cccd2", - "0x89e19b665ce7f6617884afaf854e88bb7b501ecdd195a5662c79802d721f5340eca8c48341ad1d6c78f519f82e5a9836", - "0x94402d05dbe02a7505da715c5b26438880d086e3130dce7d6c59a9cca1943fe88c44771619303ec71736774b3cc5b1f6", - "0xa76adeddf2454d131c91d5e2e3a464ef5d3c40ee6a2ab95e70ef2e49e0920d24f9b09276250ed7b29851affbdbc7885a", - "0x8934e9a3feababa12ed142daa30e91bd6d28b432d182ac625501fe1dc82f973c67f0fe82d39c9b1da3613bb8bfe2f77b", - "0xa5c11337eb91ce0e9b6d61bbdadea0a063beee1bc471cc02dc1d81c5dd2095315c400cbc6c33d23c77e98bba6bdf5439", - "0x92d00e64ed711951aeb852908aeb6fd379ea516872dd512384b1e773ef078e52e6d618beb202d437d2a46bcb78087f7a", - "0x97f1a7370b4f5acf83b466f519da361c366915f560385dd7eff9d53700ad81b25c9862bc71d35428e82372a5ae555ea0", - "0x95614544f65808f096c8297d7cf45b274fc9b2b1bd63f8c3a95d84393f1d0784d18cacb59a7ddd2caf2764b675fba272", - "0x8d474636a638e7b398566a39b3f939a314f1cf88e64d81db0f556ca60951ec1dca1b93e3906a6654ed9ba06f2c31d4ea", - "0xb87e5f481b938ac8a481b775cc58be2a06604549e3c810fc4734bab76099e5c617f0243c4c140cb7dd6d36a6dc2286bf", - "0x806efb61d1c948efc10dbf9bef30197d1c269e5e7fcf20a84367b26223d33fade413a0bbf4e33f0d1f1a00967289015e", - "0x880b99e77a6efb26c0a69583abb8e1e09a5307ac037962ddf752407cacaf8f46b5a67faf9126bdbcb9b75abf854f1c89", - "0x82ffe4de0e474109c9d99ad861f90afd33c99eae86ea7930551be40f08f0a6b44cad094cdfc9ed7dd165065b390579d0", - "0x941cd102228aa81ef99506313a4492a17c506e7169808c6b14dd330164e9e8b71b757cbe6e1bb02184372a8c26f7ad1f", - "0xae96dc808c316a677977831bad1e529ef965dadb5d6aea25ab008fe7bb1543e596e33052cfbe4279fa060201199d2c34", - "0xa2053719da2b7501dab42011ae144b3c8d72bd17493181bf3ae79a678068dc3ee2f19d29a60b5a323692c3f684f96392", - "0xb17171939519d90e243d41839c3c5ce7ac7e6a978e4a7e56b2c8e6a2b1b587c7eacea47f2e31a55d88555d252c810ebd", - "0xa958987c2b3c84df8176b600f5b75a8badef9653c71eff967e76648937def38826eaab4027a639f4f300b20019166250", - "0x854aafa329e2b2563355641eba95f2aba5b33d443ab16f5e342048f97d97c4e2812ff27c6f4180b8110272f3151be690", - "0x94d4a1e3a3d28a948f14d1507372701ac6fc884a4905405a63663e170831578a2719714ef56f920baa0ca27954823e39", - "0x826be957cf66db958028fa95655b54b2337f78fb6ef26bd29e2e3a64b130b90521333f31d132c04779e4b23a6b6cd951", - "0x8261f7e644b929d18197b3a5dcbba5897e03dea3f6270a7218119bd6ec3955591f369b693daff58133b62a07f4031394", - "0x84926cf2265981e5531d90d8f2da1041cb73bdb1a7e11eb8ab21dbe94fefad5bbd674f6cafbcaa597480567edf0b2029", - "0x8fb51e3ef3c1047ae7c527dc24dc8824b2655faff2c4c78da1fcedde48b531d19abaf517363bf30605a87336b8642073", - "0x8ba45888012549a343983c43cea12a0c268d2f7884fcf563d98e8c0e08686064a9231ae83680f225e46d021a4e7959bb", - "0x973ab82026d360e2cf5676d883906186bc61b43f60767ca58f11d0995e40780b163961e6e096299ccf1c86175203abde", - "0xb77416ea9a6b819e63ae427057d5741788bd6301b02d180083c7aa662200f5ebed14a486efae63c3de81572fe0d92a9c", - "0x820cc2ac3eed5bce7dc72df2aa3214e71690b91445d8bb1634c0488a671e3669028efbe1eae52f7132bde29b16a020b7", - "0xa6b434ac201b511dceeed63b731111d2b985934884f07d65c9d7642075b581604e8a66afc7164fbc0eb556282e8d83d2", - "0xb81821a79c9148b41d24d85dc997336a1e1719da0e31e42af30812b97a5af31708ca3e7bc2e803c3751cff23af5c509d", - "0xa013cc5e3fbb47951637426581c1d72764556798f93e413e1317849efd60f3ecb64c762f92544201eb5d6cfb68233050", - "0xb5f32034d0f66bcbccefe2a177a60f31132d98c0899aa1ffff5ebf807546ff3104103077b1435fa6587bfe3e67ac0266", - "0x86ca8ed7c475d33455fae4242b05b1b3576e6ec05ac512ca7d3f9c8d44376e909c734c25cd0e33f0f6b4857d40452024", - "0xb781956110d24e4510a8b5500b71529f8635aa419a009d314898e8c572a4f923ba643ae94bdfdf9224509177aa8e6b73", - "0xa3f9dcc48290883d233100b69404b0b05cf34df5f6e6f6833a17cc7b23a2612b85c39df03c1e6e3cd380f259402c6120", - "0xa104d4bad69f1720307ed12363d1ec97952acfe09d9e3650034c33f3f20c763271ebe0d5b50b1d3bd15c469f4573b09d", - "0x94bbc6b2742d21eff4fae77c720313015dd4bbcc5add8146bf1c4b89e32f6f5df46ca770e1f385fdd29dc5c7b9653361", - "0x951aa38464912a29df2101c60771d6de7fadb63f2db3f13527f8bdacb66e9e8a97aaac7b81b19e3d1025b54e2c8facff", - "0x919b5187af7dae210f151dc64a9cbd396d1ae04acadebf542a7123004efc7ce00d6e14c170d876fbc64dc1b5d141a5f4", - "0x88e1e459ee5aeb8b36ed004f6d03da296101106fbe1b18f9bbf63e92321db51670c34050fd3b7dc56a4bad76403823ee", - "0x86b3ec14a8ffb811a0ecc3771f600d8b08c098537d100fba66def19e7ee4d1c397a311977bf37e6cd2d47a8a2ee8c223", - "0x898c4873bd356ba8015f6f686d57088fa8f79f38a187a0ef177a6a5f2bc470f263454ee63d0863b62fca37e5a0292987", - "0xb471c72bd2971353f4b44248b8e6cf5316812861a88ccfc20fd0d89a5e010428c387228b2f6f14c12f79e31afc9d0753", - "0x8b7cb5b8de09a6dfceddcbaa498bc65f86297bcf95d107880c08854ed2289441a67721340285cfe1749c62e8ef0f3c58", - "0x983cb6bbfe83bce8326e699e83fca01ea2958c09808c703cac97a0ea777e5a5f3f5bba9169a47732de7459a3c7af47d2", - "0x8235a3f09078dd34ce2fc17cc625e061298713b113dda12d354b3d2ba80e11c443b1dd59c9eb5a29513a909645ae97d4", - "0x8bb51b380a8a52d61a94e7b382ff6ce601260fa9b8c5d616764a3df719b382ec43aec9266444a16951e102d8b1fb2f38", - "0x9579973ee2559da09b327c62b1cc0177f2859872885dca709e24dcfbb9bdf9224a6d26869aafce498f44c0e6bd8a996c", - "0x842ba3c847c99532bf3a9339380e84839326d39d404f9c2994821eaf265185c1ac87d3dc04a7f851df4961e540330323", - "0x99dad12f78e1a554f2163afc50aa26ee2a3067fc30f9c2382975d7da40c738313eaae7adbc2521f34c1c708f3a7475b7", - "0xb96e3ff8bdae47aa13067c29318b1e96a7fe3941869c17ce6662183b7b064bf261e1cea03e2a4643c993728a2606b5b5", - "0x955bc897171aa65d0159aa6e5d51855833f83d8bd5e65f93ad26c27781171783f3988a12bf987472edb39994bd071ea5", - "0xa59249e4dfb674dfdc648ae00b4226f85f8374076ecfccb43dfde2b9b299bb880943181e8b908ddeba2411843e288085", - "0xac2955c1d48354e1f95f1b36e085b9ea9829e8de4f2a3e2418a403cb1286e2599ba00a6b82609dd489eda370218dcf4c", - "0xb2eedff11e346518fa54e161be1d45db77136b724d497e337a55edfc896417de3a180bf90dd5f9d92c19db48e8574760", - "0x976eb5543e043b88d87fda18634470911dfe0e0cabab874ca38c1009e64d43026d9637d39dcd777bc7f809bbfc3e2110", - "0x8ea5f88a79f4eb9e7c0b6b29f8ef2d1cc4c15ed0ed798ab11a13d28b17ab99278d16cd59c3fa8217776c6dfae3aedf88", - "0xb7f146a357e02a63cd79ca47bf93998b193ce1174446953f12fa751f85dc2a54c9ed00c01a9308509152b3bad21d7230", - "0x87fd7e26a0749350ebdcd7c5d30e4b969a76bda530c831262fc98b36be932a4d025310f695d5b210ead89ee70eb7e53b", - "0xb9445bafb56298082c43ccbdabac4b0bf5c2f0a60a3f9e65916af4108d773d62ffc898a35b0b8efb72ea846e214faa02", - "0xb106c6d13ca17a4c8ea599306e84918127cf2de21027ac3fe5a57d35cf6f3b1d7671c70b866f6e02168ae4e7adb56860", - "0x9276e8051bed8f5dbfc6b35765aac577dd9351d9d6ac1bb14496bd98091005b9a4737b213e347336413743f681f5043b", - "0xaefc682f8784b18d36202a069269be7dba8ab67ae3543838e6d473fbc5713d103abcc8da1729a288503b786baac182d3", - "0x97578474be98726192cb0eac3cb9195a54c7315e9c619d5c44c56b3f98671636c383416f73605d4ea7ca9fbeff8dd699", - "0x99c34f9bd0fcb18b3d931e562988cf91886a417f8678f22651bf3cf138df2bbec3f675de90f62dda769e0eda03d72b7e", - "0xa0047e03c89a95248543618e6b7ca2c7aad7acda3c9f85771ec5c93fa898c651e8b2ea3b6b799d8cd592290a986cdd7d", - "0x993726e0b1c2277b97b83c80192e14b67977bf21b6ebcde2bda30261aa1897251cd2e277cfcb6193517f1eb156d2fe86", - "0x974a5180e55eab23d4c973fbee6ad1010335161ecdb849fe6520b34c1f96530a4faff80bd738fe281019b79d968c472c", - "0x8f1d90034f998018c3f4b5947b40b139fcead2e40aa80fdec6a4337c60e9d5ff1923dda7f0b5b1731ff16f55027d41bf", - "0xa7d9ae9621dd1f3da3cd2d435e891cc3579c4c0d60d6a4565cac86c315cea21a9ad883559fe7b897ae6e05f1aa989ad9", - "0x800f6be579b31ea950a50be65f7de8f678b23b7466579c01ac26ebf9c19599fb2b446da40ad4fc92c6109fcd6793303f", - "0x86fa3d4b60e8282827115c50b1b49b29a371b52aa9c9b8f83cd5268b535859f86e1a60aade6bf4f52e234777bea30bda", - "0xa80ac2a197002879ef4db6e2b1e1b9c239e4f6c0f0abf1cc9b9b7bf3da7e078a21893c01eaaab236a7e8618ac146b4a6", - "0xb4bf70468eff528bf8815a8d07080a7e98d1b03da1b499573e0dbbd9846408654535657062e7a87a54773d5493fc5079", - "0xae0e15a09238508b769de83b30582cc224b31cd854d04fdb7b8008d5d8d936dbdd3f4a70fff560a8be634c141772561b", - "0x936f7e20c96b97548fef667aa9fa9e6cfdc578f392774586abe124e7afc36be3a484735e88cc0d6de6d69cf4548b1227", - "0x8163eea18eacc062e71bb9f7406c58ebe1ce42a8b93656077dd781c2772e37775fe20e8d5b980dd52fdad98b72f10b71", - "0x86a6560763e95ba0b4c3aa16efd240b1873813386871681d075266511063b2f5077779a4fe49ffc35e1f320b613b8c94", - "0xb156d9d22722bb6e3b75b3b885b64642fa510ba7e6057657cd61bac43fb9c284d05bb09e2d4b78a2a4ddada85da9c702", - "0x9779ca2759dbed8081f0cbbfffcb3b842ba335e3ae48a60c5e5c77c7a2a0623e4c415ec3a023cc4e216885fcbac3ce52", - "0xb8137fd57ce7d3cfaf8bdbaa28704734d567d0e7a2d87fb84716722c524bb93acb2c1284249027f3c87bccc264c01f4e", - "0x8cf06b34e7021e9401eb705dde411ecf7e7e7185f8c0b0aeed949097df31812a9fdd4db7d18f9383a8a5a8d2d58fa176", - "0x8c65aa29a9ee9066b81cf24cf9e0beca3e8e8926e3df22d5ff1236297e158cc8bc7970a2c7016009cc4efa8f14b14347", - "0xac7e49f2059e99ff65505742978f8d61a03f73f40141a2bd46fde5a2346f36ce5366e01ed7f0b7e807a5ce0730e9eaa9", - "0xa1c25eb9b73723982be78180770aa63c5f7b23c2e54a2ed7e75a860c4512d273008066f1124ac8a43c60fe1e2a8bf03c", - "0x9310722e360a5652737362f6b9cb6e9c3969a0c9bb79b488b3c7d19d9e8c42ebd841df346258ded2e393895c99b413cf", - "0x893272a63650b08e5b8f9b3f17f8547b456192ad649c168bafd7166b4c08c5adf795c508b88fd2425f7be8334592afb2", - "0xb576c49c2a7b7c3445bbf9ba8eac10e685cc3760d6819de43b7d1e20769772bcab9f557df96f28fd24409ac8c84d05c4", - "0xaf17532b35bcb373ce1deebce1c84abe34f88a412082b97795b0c73570cb6b88ea4ba52e7f5eb5ca181277cdba7a2d6d", - "0x8b8813bd2c07001a4d745cd6d9491bc2c4a9177512459a75dc2a0fa989680d173de638f76f887de3303a266b1ede9480", - "0xab73a043ccdfe63437a339e6ee96ef1241264e04dd4d917f6d6bc99396006de54e1e156d38596ba3d15cb1aaa329f8f5", - "0xb67146b202afec0132ac0070c005cf664081e860339f8f4d45ac3e630dda05560936e646673e652d08cecd8d18fc64da", - "0xa750404e9d4b1a48f767d2b6aa699200c92c3b8102597f8c5c1dbaaf08112a0587c05801dfebb3612fb6dfd76ddc9ccb", - "0xb0d4231814e40e53ab4eed8333d418a6e2e4bd3910148b610dec5f91961df1ad63f4661d533137a503d809ea1ad576fa", - "0x81fc724846b5781f3736795c32b217458bb29972af36cc4483dd98ab91680d3d9bc18842db2661487d3a85430dc9e326", - "0xa5cf6f4fd67aecb845eebc8d7304c98c69806d774d4c468350f7f82ff0f5baeecc56837705e39432a8d246aa2a7075ed", - "0xa71d2c8374776f773bad4de6edfc5f3ff1ea41f06eb807787d3fba5b1f0f741aae63503dbca533e7d4d7d46ab8e4988a", - "0x825359cfe68ad6a75578a94be6419179e0aa088170b6c20fc5c249dc3be7a260d687c93d8d8a343c7c72c2ed6a716de3", - "0xb6aeb7a9b934a54e811921494f271d5d717924c561cd7a23ab3ef3dd3e86184d211c53c418f0746cdb3a12a26a334fc8", - "0x8c6fc89428c74f0c025e980c5a1e576deadf8685f57136e50600175fa2d19389c853d532bb45a3e22b4a879fab1fcb0d", - "0xae95ddcf3db88f6b107dcf5d8aa907b2035e0250f7d664027656146395a794349d08a6a306ce7317b728ca83f70f3eaf", - "0x8c03fb67dd8c11034bd03c74a53a3d55a75a5752ea390bd2e7f74090bf30c271541b83c984d495871d32c98018088939", - "0xb8d68610fdee190ec5a1f4be4c4f750b00ad78d3e9c96b576c6913eab9e7a81e1d6d6a675ee3c6efac5d02ed4b3c093a", - "0x87d4b20bbe2dcd4f65f4e1087f58532d5140b39a5288e1a63fc0b7e97a6a56946eafdd90ba09300c3d1fef6356ac6b7c", - "0x83e264b1d3d4622826ab98d06f28bbbd03014cc55a41aaf3f2a30eec50430877d62b28c9d6d4be98cb83e1e20f3b13db", - "0x97ffcbf88b668cde86b2839c7f14d19cb7f634a4cf05d977e65f3cd0e8051b2670e521ae74edc572d88201cff225e38a", - "0x91efdbcaad9931312d7c41d24de977f94d7f3f7b88090a1f72d9a097a1e30cc805c5ea16180f463022d9b26b8863f958", - "0xa4b507a4bc2bc424297bf8968bf385fae3acc686cff4b7933b4f5b6ef3149995393d5331dbac4b629e8adce01a91a4cc", - "0xa76a26c30d8abbbd4bf982bb8bd2066a2ec823a5eb6fbe37c664e67efbe2f72d8ce2d00223b900699149f8441bff5ada", - "0xb3a5497365bd40a81202b8a94a5e28a8a039cc2e639d73de289294cbda2c0e987c1f9468daba09ea4390f8e4e806f3c8", - "0xa09b2a07d861e01645accfb088f7f9ad524186bd439712775459a60f8a1fbbd43ee084e4d6e23ffce06daa189cd1e654", - "0xa41cf5d678a007044005d62f5368b55958b733e3fdde302ba699842b1c4ecc000036eda22174a8e0c6d3e7ef93663659", - "0xa698b04227e8593a6fed6a1f6f6d1eafe186b9e73f87e42e7997f264d97225165c3f76e929a3c562ec93ee2babe953ed", - "0x8bc66e370296649989a27117c17fbc705d5ac2bda37c5dad0e4990d44fcc40d3e1872945f8b11195538af97961b5c496", - "0x8bff10f91b8c0abb6c9542588da17fa0118ffda4c82626a754887e333d7d69661b3ae4e400da15c49565f8d10a77d0d7", - "0xac715c7b3d794860a61d9c7bd224a2b14a6023f696afa30345aad2ce0a6ea6dbc142f34af1ffe7f783542851a28e8ece", - "0x91c5e0b9146fe5403fcc309b8c0eede5933b0ab1de71ab02fac6614753caac5d1097369bdeed3a101f62bbcae258e927", - "0x8553748da4e0b695967e843277d0f6efeb8ba24b44aa9fa3230f4b731caec6ed5e87d3a2fcd31d8ee206e2e4414d6cf4", - "0xac722bd742374f925185ea7d4d62d7510b2d8a6ebf5c750af6ce83e2d8a28c95a3e298870ec8254ab2d1d0aa2a063c60", - "0xb083c4cefb555576bb37b71f30532822cb4b1e1998e35cb00ffb80ca14e2853193c16a6756417853d4a74d625744dd76", - "0x85745bd84c92ddfc55df11fe134cf70e3c340aa1c7cdd6188a03308cf3a840f4f19629f9730b2e6426424989ff03000d", - "0x845b4531dee808b583645f56fa98cbdecce3ea100db60524b64f68e29866173791f01137714f4dc7fb8612f7f7943263", - "0x93f03495d53c781be8b76e37e68b64aa260523004eff6455ddc8a8552af39854e5181f8c5365812b1f65926534fba5dd", - "0x801c126abff96fe9b042be8869d2907d0c6963a79901f9db46577a445418b7465a1f4b346933d433e539536a9a2df01c", - "0x952cf6782b0ad3e85625391cc5f486a16bb5b1f8ea20defcb6857bd7d068dcd2701bc7ed5c3b773a869180d9042f772b", - "0xb3b6eccb2ec8509b4eea8392877887180841ab5794c512b2447be5df7165466d7e293696deaabf063173e5f2238ce763", - "0xb63fd45023f850985813a3f54eceaccb523d98d4c2ba77310a21f396e19a47c4f655f906783b4891de32b61a05dc7933", - "0xa113b889be5dcc859a7f50421614a51516b3aadc60489a8c52f668e035c59d61640da74ba1a608856db4ff1fa1fe2dfd", - "0x87fec026beda4217b0a2014a2e86f5920e6113b54ac79ab727da2666f57ff8a9bc3a21b327ad7e091a07720a30c507c9", - "0xa3ee8fd53158dad3b6d9757033abf2f3d1d78a4da4022643920c233711ff5378ac4a94eadcaf0416fdcca525391d0c64", - "0x8d6bed5f6b3f47b1428f00c306df550784cd24212ebac7e6384a0b1226ab50129c0341d0a10d990bd59b229869e7665a", - "0xb549cef11bf7c8bcf4bb11e5cdf5a289fc4bf145826e96a446fb4c729a2c839a4d8d38629cc599eda7efa05f3cf3425b", - "0x982691766a549df56436acd0fe76da78132e027515f27174c10d117bfcc20ed73fc31f5465bd7a22a101094fe913e221", - "0x985af1d441b93fa2a86c86b6d7b70b16973d3971e4e89e093b65f0ae626d702202336869af8e3af3923e287547d5384b", - "0x994b7baecc8bb68d270a3a88c58e4054afdbd713b4472f9522b27c1762c637ef8f013d745ce9d1dc8fc4d986d4c9338c", - "0x827dabda84c7f7b1adc0f5ca0fccf0729e9d7f78e1ffa7c5e9c4f66610ff0ab776c880b00c77137cf7abe14df977febc", - "0xacd17cba1203748b55bd9d7b940a16bb7c02988c93007a80b87e0bdb049b91f5ecce577e3e4ea68a0abe998a72cd300d", - "0x989fa046d04b41fc95a04dabb7ab8b64e84afaa85c0aa49e1c6878d7b2814094402d62ae42dfbf3ac72e6770ee0926a8", - "0x99dc48a054f448792523dcdeec819e1b928b1bd66f60f457261f0554f8532eedd7152792df70ae5316ab2f9c02a57cdc", - "0xab33c65587ecb3278325948c706aed26547e47ed2b4bc027e9119bb37bec67ddf5489fbc30304ef6c80699c10662d392", - "0xae89e41d8cfbf26057a4078f8a5146978e658801b08814190cbce017d79beaeb71558231a72bde726fa592fb0828c01c", - "0xa9901df92e2d3abbb25f3bf4b913692c4cd57da327b01c8ee2362c02fbefcf66cdb792c17a81dcbde3c9b9dba313e4a1", - "0x8ee41011424da5e2ecea941cbf069ea32420749f2519434d3d8f9725ac789753404687a6745cffe4bd5dfc8fec71a719", - "0x8cfcdfa192b17321be4e447204e1a49ecaadca70a3b5dd96b0c70ab64d1a927d1f8c11a7e596367e5fa34e2307af86fc", - "0xb8a0003e949cf994b1bb25e270cb61358200c93b1c6f611a041cf8536e2e0de59342453c5a8d13c6d4cc95ed8ce058f3", - "0xadc806dfa5fbf8ce659aab56fe6cfe0b9162ddd5874b6dcf6d658bd2a626379baeb7df80d765846fa16ad6aad0320540", - "0xa83b036b01e12cadd7260b00a750093388666aff6d9b639e2ce7dfc771504ef8b2090da28ec4613988f2ec553d1d749e", - "0x825aca3d3dfa1d0b914e59fc3eeab6afcc5dc7e30fccd4879c592da4ea9a4e8a7a1057fc5b3faab12086e587126aa443", - "0x845a4a09941f48677e6c03699770f9a56ba72695089e432a6f232294dd8da6d34e394116a9a87f3b0902c78332af9439", - "0xb2f168afc35ed9b308ab86c8c4aaf1dcd6833ce09153bb5e124dad198b006e86a941832d387b1bd34b63c261c6b88678", - "0xa094cca9d120d92c0e92ce740bc774a89667c6f796b438b0d98df0b7aef0935d8c915d5b0dad4b53e383dc9f095c29fa", - "0x956ecb233b3529b2d9cb80ae49e48667f2a3120e4a0d7131d1e9ec36db3a59dc2ef2a579cbb99d6f14880ca83f02b64c", - "0x906cde18b34f777027d0c64b16c94c9d8f94250449d353e94972d42c94dd4d915aa1b6c73a581da2986e09f336af9673", - "0x824c8a1399ab199498f84e4baa49ff2c905cf94d6ac176e27ec5e2c7985140dbaa9cc6303d906a07ab5d8e19adf25d8a", - "0x889a5cf9315383bf64dfe88e562d772213c256b0eed15ce27c41c3767c048afe06410d7675e5d59a2302993e7dc45d83", - "0x95791fb6b08443445b8757906f3a2b1a8414a9016b5f8059c577752b701d6dc1fe9b784bac1fa57a1446b7adfd11c868", - "0x99049e9a23c59bb5e8df27976b9e09067d66e4a248926d28171d6c3fdd1ab338944a8b428b2eaae5e491932c68711c7c", - "0x95c810431c8d4af4aa2b889f9ab3d87892c65a3df793f2bfd35df5cfdb604ca0129010fa9f8acae594700bece707d67f", - "0x841d77b358c4567396925040dffe17b3b82c6f199285ac621b2a95aa401ddb2bc6f07ebd5fa500af01f64d3bb44de2df", - "0x90c402a39cd1237c1c91ff04548d6af806663cbc57ff338ed309419c44121108d1fbe23f3166f61e4ab7502e728e31fd", - "0x968d44188e2d9d1508b0659e96d6dabd0b46aa22df8d182e977c7f59e13aa05d5da09f293f14f6f2ee1b996071cd2f25", - "0x8ae9585caa3c73e679fe9b00f2c691732f7b7ca096e22d88c475a89d4d55cb9fba3cd0fe0cedd64ce75c591211664955", - "0x94b81d5ad72efb4dd60867e71afcd8e87e1f24bf958d42fc07db66f6185a1e610987ab9ceef63109a36fe5544a0cf826", - "0x8499a8c3d67d1f6eccf1c69274393dc498cff862ea8e6c11ffb8107ae190d258ddc1d294f2a8f050488df0212063ece2", - "0x921109a390e4d7fbc94dff3228db755f71cb00df70a1d48f92d1a6352f5169025bb68bcd04d96ac72f40000cc140f863", - "0xb464d763e5ef724ab7ee13a60015df5c9a7809a79188ff6a7e0d5e5400febd42ad7330406a59704a44a08f2289d659c8", - "0x96f1a36134e0d4137a7fe8bbb354f50aaa67f28f194ae2fdbe8be3eb24596678d8c9287765ee90c1f2778d0d607931e0", - "0xb031d93b8f119211af76cfafee7c157d3759b2167ee1495d79ad5f83647d38248b4345917309ef1a10ecaa579af34760", - "0x88a7dc337d89324f025f686f37d21240c7da9a1cb802259ea8d8a83e246dcc2adceca7ca3534bc7bf8f3ae1cbeafb5c0", - "0xb30e022b8a563655074e08e123b5f96956bbf0fe221cc629c5fedd2764a66b475916ceb98867f935b4a47212e53ae9f3", - "0xab6e3180dae399d41243f23545e5e6d118844f9b8edba502a3503fd1162ed826f9fc610889a1d685d374b6c21e86067d", - "0x96cf5760c79cfc830d1d5bd6df6cfd67596bef24e22eed52cee04c290ad418add74e77965ea5748b7f0fb34ee4f43232", - "0xa5817c74a394b0359a4376ef7e9e8f7dfa6a7829602da225074fb392b715e1fd52c50cae0f128a7006f28b22f233fbf5", - "0xa50ab79cf3f6777a45f28d1b5cdad2c7ea718c60efeeb4c828d6307b29ef319445e6a9f98aa90f351c78b496575150c1", - "0x8b300dea07e73dd2f07b05d477e51f8424589f6b2fa6f461240e1322a3a7ab5bf227b74544bb5d66a297702cdbf6c6bf", - "0xb13b5cb86dc8b8fe87125f1a51fe98db36bdde4f600401408b75059a44e70b1bbfefd874e539691f3f1bf6f54db883c8", - "0x8d06205cd66703ce6776b38b98c32b27f45c7b3f65ea2d05e2b702c24d553f51c69bf0b17e8db7382475e3d370d2e8d6", - "0xa11a7496c712734aec80738e30d2bf245758b34245076149854eb209fa6403be8bb0d4e515becc881b7f3610749260c0", - "0x9615800f8c95f95bf25055ae079b964e0a64fa0176cc98da272662014f57e7cd2745929daf838df0094b9f54be18b415", - "0xb19ca6e55f349bbb2dc3e429520ff5b2e817972470794f35c1aac8c118b37a694cfcc875b6d72225343799825d2f5c39", - "0xa650864b7eb6769aaf0625c254891447351e702e40d2be34dfd25f3b5367370de354318d8935ba18db7929270455ae6a", - "0xa649208372f44f32eb1cd895de458ca1b8be782746356f08ac8ef629429d0780a0799fcff85736e19aead0b79bfff261", - "0x89461cb2dadf51d6f1208b0965c8eabec895d7b19b7d90d3c6e49dbe75a75c30fd26db3dfb169dd46a4342280225032a", - "0xb5d6f664ec92e5343792d5d6b629919c5fd8cfb874677df2264daf02bcd9d12facf9b859d5402839c9022396e20d260b", - "0xa7179d338fe5a0e4669364a364e17f8d00cb6c59a80a069afd5f4f14510df2eee90c07826553e4f7fe46d28f72b2903e", - "0x8ded37d67b5368619a090266e9b5585fbff60319a90a4244a3c3342641f5bfa5130998dd97d7a42505cd896c29255530", - "0xa3fd63e87a00b48ba46a646a26187ae6dcb16779721973ada13a545853e2e51b5e4df04630d670884ad4a2304cc60c67", - "0x92761b7e31f0c758b3b1f5b43a194b25aabec668101946eb6511132863d3afb9d18f833d43a8338d0e7bc78d8689e523", - "0xab8a8769c754008a7976b6799e81d7bfe97413d0a79b90715703c1f8f567675463ec93aabee59277121fc4df88b5c7a9", - "0xb354d0d1bd942f79002a2eaf37eb99dab650170e7040c13c824803ed7c1670dc910ccae13bbe58bde003829b140b45ea", - "0xb9eed89e003894ad2cc9d9b93a45247e1367ac69a00b0ed5e3280c1188b4cb90eb870d449b83a852a798bd02f9d0c813", - "0xb900a55013d0427e5da6b21611d6ae3e648f54f794cb099b2d2beebae0a957477a94dc415e8ec5e68e9029ce50d43843", - "0xafbf44071c2c905f7c8ef396eaed7f13deb7a91719cb5e8b9226aaceb876d81a10076383edc6216bc2f5c38a480b2957", - "0x80bdb82b7d583bf1e41653966b0ba3b4fec0e7df2ff08e3fa06fd9064bca0364263e075e1582741a5243bde786c9c32e", - "0xa841fe9ff26db21ade698f6dbfba025d90ae9f81f02af9e008fa0a429b993fb04d06acb93e40a9f81c78f73334555a17", - "0x8cd49711b42af58a5aae75a38fea9ddc5e4183c467a3159b5b0629f01ba548513c577456d34c861911e85782e52c3b1b", - "0xa322b5d2a6e3cb98b8aaa4c068e097188affef5dec2f08c3e9ce29e73687340d4e5a743a8be5f10e138f9cabbe0c7211", - "0x942772b7c7c47d4e5957ccf1d6f1450070930af3e2b7eaab0dd7699372445df0cc910e6c0efcf501887dd1adabdaee23", - "0x9834f66e5c946c3a8241ca2bbde046a7e88072124911d5d15c037a95b61e82b88b5c2058fa4a3721537dee39dee5da18", - "0xa90cc5b9c4d84f36962d0d55d5bc123dbe5ec5f4fe7b6bf0d009028b3cf14e36c11bc5365391cb4ae548d5eb04fe371b", - "0xa7c2174eea2b66b2a71cc8095fae39c423a353c7d5020ec2d0551317a66202fcf082c6119ba768755523fff49791bb4e", - "0xab92b2a177dfa55d202a653532f0e04d1339ca301aebe6a0e8419bf45be3e573b6b9ae4d3d822cc8279367a3d2c39b06", - "0x8a9ad977988eb8d98d9f549e4fd2305348a34e6874674bcd6e467c793bba6d7a2f3c20fa44aabbf7151ca53ecb1612f6", - "0xa7d1676816e81a752267d309014de1772b571b109c2901dc7c9810f45417faa18c81965c114be489ed178e54ac3687a1", - "0xa575be185551c40eb8edbdb21a0df381c801b6e99467fcf5882dd7cb34916960ce47ac732c1920ad3218f497b690cef4", - "0xb50c306f78143b37986e68efa10dbe1fb047d58562e9b5c5439b341dd8f1896c7ae586afac0a3213759784a905c1caaa", - "0xb31e89b4a034c1b73d43b3d63ea3bddea682a6a5327eff389c70b13e9e72185b0327682a0cb1ff3c4a4f8ba08b13d898" - ], - "aggregate_pubkey": "0x948a4b8d91bd29969cd4470b1bc24d34586d38e73d5be71c98a9894899471a5f708747563276b4b6d2716fdb72860267" - }, - "next_sync_committee_branch": [ - "0xcb3fea582872d90706ddc6d029bac0d791ea75d43c3ab04f056f62a11b89d27b", - "0xf26b4bb68eb7e6906c8ef4a9958398a48e4450bf9c8a462fafa4fde51cd5628f", - "0x690925f92c1d322d2ff2a5636539094825dfd2c9bf7538fe111b2358e03a71de", - "0x51d977e358166401c7cd3f6185468c1a8c69a4c3d8577535dd583bc427692e02", - "0x88d2089aaf2f6fd8f6e4ae499c6fe33cc34289eb9310780861e772204f07b670" - ] - }, - "finalized_header": { - "slot": 4055040, - "proposer_index": 854, - "parent_root": "0x8adf3b288deb17566a553fa7a06a2f63f4ac4cea4868af4d89ddca41f73ae9b9", - "state_root": "0x595e9ebaaa23f027672b4d2a33173a22b2839baac709c7f8e66d3219f492ee9f", - "body_root": "0xacf37b466d4f6b5d1db5b6ffe5d77c13972d116c2b0809de924427fd597d391a" - }, - "finality_branch": [ - "0x00ef010000000000000000000000000000000000000000000000000000000000", - "0x3d4be5d019ba15ea3ef304a83b8a067f2e79f46a3fac8069306a6c814a0a35eb", - "0xa2e2a28c0bb0ad56c25f3c461a4bfe4f3b3b894bc0105a62e85f43a4ae1adc3f", - "0x690925f92c1d322d2ff2a5636539094825dfd2c9bf7538fe111b2358e03a71de", - "0x51d977e358166401c7cd3f6185468c1a8c69a4c3d8577535dd583bc427692e02", - "0x88d2089aaf2f6fd8f6e4ae499c6fe33cc34289eb9310780861e772204f07b670" - ], - "block_roots_root": "0x7054ba439f83e9b2223911e25fad48eb28f5c362d94c0de2455a45436cc93897", - "block_roots_branch": [ - "0xc99a842c81d0b956eef988dbcd90499457d61f942375c6cbf67262909b708db5", - "0x5d32345aeb10aa3ede4be021d2231dac47cc2074f74b72466f0b042e69adf70f", - "0xb69608a2377956c1a39c3423d3399ccfb12307623c9df6358f5fcfca64a80102", - "0xcefd9b668e49ece82bd4b0ee2f1efc88ecaf0d9af464fb622735663aaf106c4c", - "0xc113ad1c971779b15c64772ab69cd775edfb926a60447974913bb6f58fbd12fb" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/sync-committee-update.minimal.json b/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/sync-committee-update.minimal.json deleted file mode 100644 index a962a0c87c4c5eedee944bd85f54a754576935f1..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/pallets/ethereum-client/tests/fixtures/sync-committee-update.minimal.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "attested_header": { - "slot": 3600, - "proposer_index": 7, - "parent_root": "0xdf60c2d58beccd89678b9267c689e9ba1cf1d58ce5114ad5c16e8341459cfd75", - "state_root": "0x023f14c7a38ef4d6ec19b522edfb427c6b70c6ffbd8610ca802dd1491c92c852", - "body_root": "0x0f78a1c45e42711efc5fb7b7f6238be1bee9273f7c44ff6892d815858bb77e25" - }, - "sync_aggregate": { - "sync_committee_bits": "0xffffffff", - "sync_committee_signature": "0xa4dd8f0991de88ca6f81476f72f48cdb67b9414ad7bf6bba37f627c5ec84dd2c2ebc12cddd5d2e7c927276cee2d3d144158b4c067db3e9911fe52fe1875b14c93f90e4eb57bf5e8f0e6e6effe22f9ba076f30207e0ec683354961ae8e9779556" - }, - "signature_slot": 3601, - "next_sync_committee_update": { - "next_sync_committee": { - "pubkeys": [ - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c", - "0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34", - "0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac", - "0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b", - "0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b", - "0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e", - "0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e", - "0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373", - "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c" - ], - "aggregate_pubkey": "0x8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6" - }, - "next_sync_committee_branch": [ - "0x1446606d0129c324a4ea374bd29a625175e0659512cd8650097e0a9c38ce6379", - "0xd92466c7e9a53b7b55f4fdb151746a3058931d7559b7e84e7b15384ddc903ca0", - "0x9fd10c3f68b75cfd3ebd2af0d4e2cbbfbe120e0b5423dde89ff0f743c7a4f937", - "0x1ed6aac0ab29a883de2bb2e3579ad4d6807ddcf3db8afcaf0ae25a076ac9a5f4", - "0xf17a840df410a15f0e4e48abf521c29ad0d296d3fb4e8b847ea37f2cc8236f1f" - ] - }, - "finalized_header": { - "slot": 3584, - "proposer_index": 1, - "parent_root": "0x91c285af2ec25d485310391afe667108b787ec570cdbb0e3fd87b1e0e2c47bd7", - "state_root": "0xccc4baf90024e035f1252520d2f2ef1e50f840ff0ecc8e6e365721e083871a32", - "body_root": "0x91df5e0077434aad609aaa7e030005cee77cca83868ffc2724e5befe9a3f6a02" - }, - "finality_branch": [ - "0xc001000000000000000000000000000000000000000000000000000000000000", - "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", - "0x83c3d5360d254f4a44be712c1f433e88e810b6d1e0e789e90bada9e36126b857", - "0x9fd10c3f68b75cfd3ebd2af0d4e2cbbfbe120e0b5423dde89ff0f743c7a4f937", - "0x1ed6aac0ab29a883de2bb2e3579ad4d6807ddcf3db8afcaf0ae25a076ac9a5f4", - "0xf17a840df410a15f0e4e48abf521c29ad0d296d3fb4e8b847ea37f2cc8236f1f" - ], - "block_roots_root": "0x9eab8a05c396a29c32f4f8ac9654fc0fb7cd97ec659236392ede48951a794505", - "block_roots_branch": [ - "0x5c175efdbafacdfdab21c93a318b0e8e2291a5a86c40b1fc564f91ad33c106d4", - "0x5c1e0b76176ab033858b2835f90d5e25d708b563f77efd7d9938f0faa1c20878", - "0x7aea32464adee801e2a05c3af227f24231d3c088e3b7265a5fada9ac850549fe", - "0x9d9fca29e23c5d4ae433adf17e7fd9a0e4d1b09b68f5c45e7ca1b13ebe4a9e98", - "0x6b35238f188021c859d6b317457ebb6fe4cf362cab35c988010cb1343eabbfc5" - ] -} \ No newline at end of file diff --git a/bridges/snowbridge/parachain/primitives/ethereum/.cargo/config.toml b/bridges/snowbridge/parachain/primitives/ethereum/.cargo/config.toml deleted file mode 100644 index 4ec2f3b8620332641758c95f2c1c685e261cba42..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/primitives/ethereum/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.wasm32-unknown-unknown] -runner = 'wasm-bindgen-test-runner' diff --git a/bridges/snowbridge/parachain/runtime/test-common/Cargo.toml b/bridges/snowbridge/parachain/runtime/test-common/Cargo.toml deleted file mode 100644 index bf7791bbabeafebb6bdba5f2c411ce38996a8817..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/runtime/test-common/Cargo.toml +++ /dev/null @@ -1,199 +0,0 @@ -[package] -name = "snowbridge-runtime-test-common" -description = "Snowbridge Runtime Tests" -version = "0.9.0" -authors = ["Snowfork "] -edition = "2021" -license = "Apache-2.0" -categories = ["cryptography::cryptocurrencies"] - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -hex-literal = { version = "0.4.1" } -log = { version = "0.4.20", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -smallvec = "1.11.0" - -# Substrate -frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-executive = { path = "../../../../../substrate/frame/executive", default-features = false } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -frame-system-benchmarking = { path = "../../../../../substrate/frame/system/benchmarking", default-features = false, optional = true } -frame-system-rpc-runtime-api = { path = "../../../../../substrate/frame/system/rpc/runtime-api", default-features = false } -frame-try-runtime = { path = "../../../../../substrate/frame/try-runtime", default-features = false, optional = true } -pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false } -pallet-authorship = { path = "../../../../../substrate/frame/authorship", default-features = false } -pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } -pallet-session = { path = "../../../../../substrate/frame/session", default-features = false } -pallet-multisig = { path = "../../../../../substrate/frame/multisig", default-features = false } -pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false } -pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false } -pallet-transaction-payment = { path = "../../../../../substrate/frame/transaction-payment", default-features = false } -pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } -pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false } -sp-api = { path = "../../../../../substrate/primitives/api", default-features = false } -sp-block-builder = { path = "../../../../../substrate/primitives/block-builder", default-features = false } -sp-consensus-aura = { path = "../../../../../substrate/primitives/consensus/aura", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-genesis-builder = { path = "../../../../../substrate/primitives/genesis-builder", default-features = false } -sp-inherents = { path = "../../../../../substrate/primitives/inherents", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } -sp-offchain = { path = "../../../../../substrate/primitives/offchain", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } -sp-session = { path = "../../../../../substrate/primitives/session", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-storage = { path = "../../../../../substrate/primitives/storage", default-features = false } -sp-transaction-pool = { path = "../../../../../substrate/primitives/transaction-pool", default-features = false } -sp-version = { path = "../../../../../substrate/primitives/version", default-features = false } - -# Polkadot -pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } -pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } -polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } -polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } - -# Cumulus -cumulus-pallet-aura-ext = { path = "../../../../../cumulus/pallets/aura-ext", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../../cumulus/pallets/dmp-queue", default-features = false } -cumulus-pallet-parachain-system = { path = "../../../../../cumulus/pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } -cumulus-pallet-session-benchmarking = { path = "../../../../../cumulus/pallets/session-benchmarking", default-features = false } -cumulus-pallet-xcm = { path = "../../../../../cumulus/pallets/xcm", default-features = false } -cumulus-pallet-xcmp-queue = { path = "../../../../../cumulus/pallets/xcmp-queue", default-features = false, features = ["bridging"] } -cumulus-primitives-core = { path = "../../../../../cumulus/primitives/core", default-features = false } -cumulus-primitives-utility = { path = "../../../../../cumulus/primitives/utility", default-features = false } -pallet-collator-selection = { path = "../../../../../cumulus/pallets/collator-selection", default-features = false } -parachain-info = { package = "staging-parachain-info", path = "../../../../../cumulus/parachains/pallets/parachain-info", default-features = false } -parachains-common = { path = "../../../../../cumulus/parachains/common", default-features = false } -parachains-runtimes-test-utils = { path = "../../../../../cumulus/parachains/runtimes/test-utils", default-features = false } -assets-common = { path = "../../../../../cumulus/parachains/runtimes/assets/common", default-features = false } - -# Ethereum Bridge (Snowbridge) -snowbridge-core = { path = "../../primitives/core", default-features = false } -snowbridge-beacon-primitives = { path = "../../primitives/beacon", default-features = false } -snowbridge-router-primitives = { path = "../../primitives/router", default-features = false } -snowbridge-pallet-ethereum-client = { path = "../../pallets/ethereum-client", default-features = false } -snowbridge-pallet-inbound-queue = { path = "../../pallets/inbound-queue", default-features = false } -snowbridge-pallet-outbound-queue = { path = "../../pallets/outbound-queue", default-features = false } -snowbridge-outbound-queue-runtime-api = { path = "../../pallets/outbound-queue/runtime-api", default-features = false } -snowbridge-pallet-system = { path = "../../pallets/system", default-features = false } -snowbridge-system-runtime-api = { path = "../../pallets/system/runtime-api", default-features = false } - -[dev-dependencies] -static_assertions = "1.1" -bridge-hub-test-utils = { path = "../../../../../cumulus/parachains/runtimes/bridge-hubs/test-utils" } -bridge-runtime-common = { path = "../../../../bin/runtime-common", features = ["integrity-test"] } -sp-keyring = { path = "../../../../../substrate/primitives/keyring" } - -[features] -default = ["std"] -std = [ - "assets-common/std", - "codec/std", - "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", - "cumulus-pallet-parachain-system/std", - "cumulus-pallet-session-benchmarking/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", - "cumulus-primitives-core/std", - "cumulus-primitives-utility/std", - "frame-benchmarking/std", - "frame-executive/std", - "frame-support/std", - "frame-system-benchmarking?/std", - "frame-system-rpc-runtime-api/std", - "frame-system/std", - "frame-try-runtime?/std", - "log/std", - "pallet-aura/std", - "pallet-authorship/std", - "pallet-balances/std", - "pallet-collator-selection/std", - "pallet-message-queue/std", - "pallet-multisig/std", - "pallet-session/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "pallet-utility/std", - "pallet-xcm-benchmarks?/std", - "pallet-xcm/std", - "parachain-info/std", - "parachains-common/std", - "parachains-runtimes-test-utils/std", - "polkadot-core-primitives/std", - "polkadot-parachain-primitives/std", - "polkadot-runtime-common/std", - "scale-info/std", - "serde", - "snowbridge-beacon-primitives/std", - "snowbridge-core/std", - "snowbridge-outbound-queue-runtime-api/std", - "snowbridge-pallet-ethereum-client/std", - "snowbridge-pallet-inbound-queue/std", - "snowbridge-pallet-outbound-queue/std", - "snowbridge-pallet-system/std", - "snowbridge-router-primitives/std", - "snowbridge-system-runtime-api/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-core/std", - "sp-genesis-builder/std", - "sp-inherents/std", - "sp-io/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-std/std", - "sp-storage/std", - "sp-transaction-pool/std", - "sp-version/std", - "xcm-builder/std", - "xcm-executor/std", - "xcm/std", -] - -runtime-benchmarks = [ - "assets-common/runtime-benchmarks", - "bridge-runtime-common/runtime-benchmarks", - "cumulus-pallet-dmp-queue/runtime-benchmarks", - "cumulus-pallet-parachain-system/runtime-benchmarks", - "cumulus-pallet-session-benchmarking/runtime-benchmarks", - "cumulus-pallet-xcmp-queue/runtime-benchmarks", - "cumulus-primitives-core/runtime-benchmarks", - "cumulus-primitives-utility/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-collator-selection/runtime-benchmarks", - "pallet-message-queue/runtime-benchmarks", - "pallet-multisig/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks", - "pallet-utility/runtime-benchmarks", - "pallet-xcm-benchmarks/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "parachains-common/runtime-benchmarks", - "polkadot-parachain-primitives/runtime-benchmarks", - "polkadot-runtime-common/runtime-benchmarks", - "snowbridge-core/runtime-benchmarks", - "snowbridge-pallet-ethereum-client/runtime-benchmarks", - "snowbridge-pallet-inbound-queue/runtime-benchmarks", - "snowbridge-pallet-outbound-queue/runtime-benchmarks", - "snowbridge-pallet-system/runtime-benchmarks", - "snowbridge-router-primitives/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", -] diff --git a/bridges/snowbridge/parachain/runtime/test-common/src/lib.rs b/bridges/snowbridge/parachain/runtime/test-common/src/lib.rs deleted file mode 100644 index 7935a77952385acb6eb3f7647abf1eafe1d5ec9f..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/runtime/test-common/src/lib.rs +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork - -use codec::Encode; -use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; -pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works; -use parachains_runtimes_test_utils::{ - AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, ValidatorIdOf, XcmReceivedFrom, -}; -use sp_core::H160; -use sp_runtime::SaturatedConversion; -use xcm::{ - latest::prelude::*, - v3::Error::{self, Barrier}, -}; -use xcm_executor::XcmExecutor; - -type RuntimeHelper = - parachains_runtimes_test_utils::RuntimeHelper; - -pub fn initial_fund(assethub_parachain_id: u32, initial_amount: u128) -where - Runtime: frame_system::Config + pallet_balances::Config, -{ - // fund asset hub sovereign account enough so it can pay fees - let asset_hub_sovereign_account = - snowbridge_core::sibling_sovereign_account::(assethub_parachain_id.into()); - >::mint_into( - &asset_hub_sovereign_account, - initial_amount.saturated_into::>(), - ) - .unwrap(); -} - -pub fn send_transfer_token_message( - assethub_parachain_id: u32, - weth_contract_address: H160, - destination_address: H160, - fee_amount: u128, -) -> Outcome -where - Runtime: frame_system::Config - + pallet_balances::Config - + pallet_session::Config - + pallet_xcm::Config - + parachain_info::Config - + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config - + snowbridge_pallet_outbound_queue::Config, - XcmConfig: xcm_executor::Config, -{ - let assethub_parachain_location = Location::new(1, Parachain(assethub_parachain_id)); - let asset = Asset { - id: AssetId(Location::new( - 0, - [AccountKey20 { network: None, key: weth_contract_address.into() }], - )), - fun: Fungible(1000000000), - }; - let assets = vec![asset.clone()]; - - let inner_xcm = Xcm(vec![ - WithdrawAsset(Assets::from(assets.clone())), - ClearOrigin, - BuyExecution { fees: asset, weight_limit: Unlimited }, - DepositAsset { - assets: Wild(All), - beneficiary: Location::new( - 0, - [AccountKey20 { network: None, key: destination_address.into() }], - ), - }, - SetTopic([0; 32]), - ]); - - let fee = - Asset { id: AssetId(Location { parents: 1, interior: Here }), fun: Fungible(fee_amount) }; - - // prepare transfer token message - let xcm = Xcm(vec![ - WithdrawAsset(Assets::from(vec![fee.clone()])), - BuyExecution { fees: fee, weight_limit: Unlimited }, - ExportMessage { - network: Ethereum { chain_id: 11155111 }, - destination: Here, - xcm: inner_xcm, - }, - ]); - - // execute XCM - let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); - XcmExecutor::::prepare_and_execute( - assethub_parachain_location, - xcm, - &mut hash, - RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), - Weight::zero(), - ) -} - -pub fn send_transfer_token_message_success( - collator_session_key: CollatorSessionKeys, - runtime_para_id: u32, - assethub_parachain_id: u32, - weth_contract_address: H160, - destination_address: H160, - fee_amount: u128, - snowbridge_pallet_outbound_queue: Box< - dyn Fn(Vec) -> Option>, - >, -) where - Runtime: frame_system::Config - + pallet_balances::Config - + pallet_session::Config - + pallet_xcm::Config - + parachain_info::Config - + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config - + snowbridge_pallet_outbound_queue::Config - + snowbridge_pallet_system::Config, - XcmConfig: xcm_executor::Config, - ValidatorIdOf: From>, -{ - ExtBuilder::::default() - .with_collators(collator_session_key.collators()) - .with_session_keys(collator_session_key.session_keys()) - .with_para_id(runtime_para_id.into()) - .with_tracing() - .build() - .execute_with(|| { - >::initialize( - runtime_para_id.into(), - assethub_parachain_id.into(), - ) - .unwrap(); - - // fund asset hub sovereign account enough so it can pay fees - initial_fund::(assethub_parachain_id, 5_000_000_000_000); - - let outcome = send_transfer_token_message::( - assethub_parachain_id, - weth_contract_address, - destination_address, - fee_amount, - ); - - assert_ok!(outcome.ensure_complete()); - - // check events - let mut events = >::events() - .into_iter() - .filter_map(|e| snowbridge_pallet_outbound_queue(e.event.encode())); - assert!(events.any(|e| matches!( - e, - snowbridge_pallet_outbound_queue::Event::MessageQueued { .. } - ))); - }); -} - -pub fn send_unpaid_transfer_token_message( - collator_session_key: CollatorSessionKeys, - runtime_para_id: u32, - assethub_parachain_id: u32, - weth_contract_address: H160, - destination_contract: H160, -) where - Runtime: frame_system::Config - + pallet_balances::Config - + pallet_session::Config - + pallet_xcm::Config - + parachain_info::Config - + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config - + snowbridge_pallet_outbound_queue::Config, - XcmConfig: xcm_executor::Config, - ValidatorIdOf: From>, -{ - let assethub_parachain_location = Location::new(1, Parachain(assethub_parachain_id)); - - ExtBuilder::::default() - .with_collators(collator_session_key.collators()) - .with_session_keys(collator_session_key.session_keys()) - .with_para_id(runtime_para_id.into()) - .with_tracing() - .build() - .execute_with(|| { - let asset_hub_sovereign_account = - snowbridge_core::sibling_sovereign_account::(assethub_parachain_id.into()); - - >::mint_into( - &asset_hub_sovereign_account, - 4000000000u32.into(), - ) - .unwrap(); - - let asset = Asset { - id: AssetId(Location::new( - 0, - [AccountKey20 { network: None, key: weth_contract_address.into() }], - )), - fun: Fungible(1000000000), - }; - let assets = vec![asset.clone()]; - - let inner_xcm = Xcm(vec![ - WithdrawAsset(Assets::from(assets.clone())), - ClearOrigin, - BuyExecution { fees: asset, weight_limit: Unlimited }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: Location::new( - 0, - [AccountKey20 { network: None, key: destination_contract.into() }], - ), - }, - SetTopic([0; 32]), - ]); - - // prepare transfer token message - let xcm = Xcm(vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - ExportMessage { - network: Ethereum { chain_id: 11155111 }, - destination: Here, - xcm: inner_xcm, - }, - ]); - - // execute XCM - let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); - let outcome = XcmExecutor::::prepare_and_execute( - assethub_parachain_location, - xcm, - &mut hash, - RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), - Weight::zero(), - ); - // check error is barrier - assert_err!(outcome.ensure_complete(), Barrier); - }); -} - -#[allow(clippy::too_many_arguments)] -pub fn send_transfer_token_message_failure( - collator_session_key: CollatorSessionKeys, - runtime_para_id: u32, - assethub_parachain_id: u32, - initial_amount: u128, - weth_contract_address: H160, - destination_address: H160, - fee_amount: u128, - expected_error: Error, -) where - Runtime: frame_system::Config - + pallet_balances::Config - + pallet_session::Config - + pallet_xcm::Config - + parachain_info::Config - + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config - + snowbridge_pallet_outbound_queue::Config - + snowbridge_pallet_system::Config, - XcmConfig: xcm_executor::Config, - ValidatorIdOf: From>, -{ - ExtBuilder::::default() - .with_collators(collator_session_key.collators()) - .with_session_keys(collator_session_key.session_keys()) - .with_para_id(runtime_para_id.into()) - .with_tracing() - .build() - .execute_with(|| { - >::initialize( - runtime_para_id.into(), - assethub_parachain_id.into(), - ) - .unwrap(); - - // fund asset hub sovereign account enough so it can pay fees - initial_fund::(assethub_parachain_id, initial_amount); - - let outcome = send_transfer_token_message::( - assethub_parachain_id, - weth_contract_address, - destination_address, - fee_amount, - ); - // check err is NotHoldingFees - assert_err!(outcome.ensure_complete(), expected_error); - }); -} diff --git a/bridges/snowbridge/parachain/scripts/verify-pallets-build.sh b/bridges/snowbridge/parachain/scripts/verify-pallets-build.sh deleted file mode 100755 index a62f48c84d4fd34731c20365a20097e086aa2c99..0000000000000000000000000000000000000000 --- a/bridges/snowbridge/parachain/scripts/verify-pallets-build.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash - -# A script to remove everything from snowbridge repository/subtree, except: -# -# - parachain -# - readme -# - license - -set -eu - -# show CLI help -function show_help() { - set +x - echo " " - echo Error: $1 - echo "Usage:" - echo " ./scripts/verify-pallets-build.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" - echo "Options:" - echo " --no-revert Leaves only runtime code on exit" - echo " --ignore-git-state Ignores git actual state" - exit 1 -} - -# parse CLI args -NO_REVERT= -IGNORE_GIT_STATE= -for i in "$@" -do - case $i in - --no-revert) - NO_REVERT=true - shift - ;; - --ignore-git-state) - IGNORE_GIT_STATE=true - shift - ;; - *) - show_help "Unknown option: $i" - ;; - esac -done - -# the script is able to work only on clean git copy, unless we want to ignore this check -[[ ! -z "${IGNORE_GIT_STATE}" ]] || [[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } - -# let's avoid any restrictions on where this script can be called for - snowbridge repo may be -# plugged into any other repo folder. So the script (and other stuff that needs to be removed) -# may be located either in call dir, or one of it subdirs. -SNOWBRIDGE_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/../.." - -# remove everything we think is not required for our needs -rm -rf $SNOWBRIDGE_FOLDER/.cargo -rm -rf $SNOWBRIDGE_FOLDER/.github -rm -rf $SNOWBRIDGE_FOLDER/contracts -rm -rf $SNOWBRIDGE_FOLDER/codecov.yml -rm -rf $SNOWBRIDGE_FOLDER/docs -rm -rf $SNOWBRIDGE_FOLDER/hooks -rm -rf $SNOWBRIDGE_FOLDER/relayer -rm -rf $SNOWBRIDGE_FOLDER/scripts -rm -rf $SNOWBRIDGE_FOLDER/SECURITY.md -rm -rf $SNOWBRIDGE_FOLDER/smoketest -rm -rf $SNOWBRIDGE_FOLDER/web -rm -rf $SNOWBRIDGE_FOLDER/.envrc-example -rm -rf $SNOWBRIDGE_FOLDER/.gitbook.yaml -rm -rf $SNOWBRIDGE_FOLDER/.gitignore -rm -rf $SNOWBRIDGE_FOLDER/.gitmodules -rm -rf $SNOWBRIDGE_FOLDER/_typos.toml -rm -rf $SNOWBRIDGE_FOLDER/_codecov.yml -rm -rf $SNOWBRIDGE_FOLDER/flake.lock -rm -rf $SNOWBRIDGE_FOLDER/flake.nix -rm -rf $SNOWBRIDGE_FOLDER/go.work -rm -rf $SNOWBRIDGE_FOLDER/go.work.sum -rm -rf $SNOWBRIDGE_FOLDER/polkadot-sdk -rm -rf $SNOWBRIDGE_FOLDER/rust-toolchain.toml -rm -rf $SNOWBRIDGE_FOLDER/parachain/rustfmt.toml -rm -rf $SNOWBRIDGE_FOLDER/parachain/.gitignore -rm -rf $SNOWBRIDGE_FOLDER/parachain/templates -rm -rf $SNOWBRIDGE_FOLDER/parachain/.cargo -rm -rf $SNOWBRIDGE_FOLDER/parachain/.config -rm -rf $SNOWBRIDGE_FOLDER/parachain/pallets/ethereum-client/fuzz - -cd bridges/snowbridge/parachain - -# fix polkadot-sdk paths in Cargo.toml files -find "." -name 'Cargo.toml' | while read -r file; do - replace=$(printf '../../' ) - if [[ "$(uname)" = "Darwin" ]] || [[ "$(uname)" = *BSD ]]; then - sed -i '' "s|polkadot-sdk/|$replace|g" "$file" - else - sed -i "s|polkadot-sdk/|$replace|g" "$file" - fi -done - -# let's test if everything we need compiles -cargo check -p snowbridge-pallet-ethereum-client -cargo check -p snowbridge-pallet-ethereum-client --features runtime-benchmarks -cargo check -p snowbridge-pallet-ethereum-client --features try-runtime -cargo check -p snowbridge-pallet-inbound-queue -cargo check -p snowbridge-pallet-inbound-queue --features runtime-benchmarks -cargo check -p snowbridge-pallet-inbound-queue --features try-runtime -cargo check -p snowbridge-pallet-outbound-queue -cargo check -p snowbridge-pallet-outbound-queue --features runtime-benchmarks -cargo check -p snowbridge-pallet-outbound-queue --features try-runtime -cargo check -p snowbridge-pallet-system -cargo check -p snowbridge-pallet-system --features runtime-benchmarks -cargo check -p snowbridge-pallet-system --features try-runtime - -cd - - -# we're removing lock file after all checks are done. Otherwise we may use different -# Substrate/Polkadot/Cumulus commits and our checks will fail -rm -f $SNOWBRIDGE_FOLDER/parachain/Cargo.toml -rm -f $SNOWBRIDGE_FOLDER/parachain/Cargo.lock - -echo "OK" diff --git a/bridges/snowbridge/parachain/primitives/beacon/Cargo.toml b/bridges/snowbridge/primitives/beacon/Cargo.toml similarity index 60% rename from bridges/snowbridge/parachain/primitives/beacon/Cargo.toml rename to bridges/snowbridge/primitives/beacon/Cargo.toml index 87f13ad5f6ea9c8b8eb79e78c4ecd55cc98a7a3f..d181fa1d3945a704a3d1e1e28fea67b7dea0ee15 100644 --- a/bridges/snowbridge/parachain/primitives/beacon/Cargo.toml +++ b/bridges/snowbridge/primitives/beacon/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-beacon-primitives" description = "Snowbridge Beacon Primitives" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -12,18 +12,18 @@ categories = ["cryptography::cryptocurrencies"] workspace = true [dependencies] -serde = { version = "1.0.195", optional = true, features = ["derive"] } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } hex = { version = "0.4", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } rlp = { version = "0.5", default-features = false } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } ssz_rs = { version = "0.9.0", default-features = false } ssz_rs_derive = { version = "0.9.0", default-features = false } @@ -31,7 +31,7 @@ byte-slice-cast = { version = "1.2.1", default-features = false } snowbridge-ethereum = { path = "../ethereum", default-features = false } static_assertions = { version = "1.1.0" } -milagro_bls = { git = "https://github.com/snowfork/milagro_bls", default-features = false, rev = "a6d66e4eb89015e352fb1c9f7b661ecdbb5b2176" } +milagro-bls = { package = "snowbridge-milagro-bls", version = "1.5.4", default-features = false } [dev-dependencies] hex-literal = { version = "0.4.1" } @@ -44,7 +44,7 @@ std = [ "frame-support/std", "frame-system/std", "hex/std", - "milagro_bls/std", + "milagro-bls/std", "rlp/std", "scale-info/std", "serde", diff --git a/bridges/snowbridge/parachain/primitives/beacon/README.md b/bridges/snowbridge/primitives/beacon/README.md similarity index 100% rename from bridges/snowbridge/parachain/primitives/beacon/README.md rename to bridges/snowbridge/primitives/beacon/README.md diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/bits.rs b/bridges/snowbridge/primitives/beacon/src/bits.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/beacon/src/bits.rs rename to bridges/snowbridge/primitives/beacon/src/bits.rs diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/bls.rs b/bridges/snowbridge/primitives/beacon/src/bls.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/beacon/src/bls.rs rename to bridges/snowbridge/primitives/beacon/src/bls.rs diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/config.rs b/bridges/snowbridge/primitives/beacon/src/config.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/beacon/src/config.rs rename to bridges/snowbridge/primitives/beacon/src/config.rs diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/lib.rs b/bridges/snowbridge/primitives/beacon/src/lib.rs similarity index 96% rename from bridges/snowbridge/parachain/primitives/beacon/src/lib.rs rename to bridges/snowbridge/primitives/beacon/src/lib.rs index 3527e1ff0d195a5c4c3b988f17f4eebc67bd0e52..4c569d0176c21882aa3875024551848b1cc97494 100644 --- a/bridges/snowbridge/parachain/primitives/beacon/src/lib.rs +++ b/bridges/snowbridge/primitives/beacon/src/lib.rs @@ -18,6 +18,7 @@ pub use types::{ BeaconHeader, CompactBeaconState, CompactExecutionHeader, ExecutionHeaderState, ExecutionPayloadHeader, FinalizedHeaderState, Fork, ForkData, ForkVersion, ForkVersions, Mode, PublicKey, Signature, SigningData, SyncAggregate, SyncCommittee, SyncCommitteePrepared, + VersionedExecutionPayloadHeader, }; pub use updates::{CheckpointUpdate, ExecutionHeaderUpdate, NextSyncCommitteeUpdate, Update}; diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/merkle_proof.rs b/bridges/snowbridge/primitives/beacon/src/merkle_proof.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/beacon/src/merkle_proof.rs rename to bridges/snowbridge/primitives/beacon/src/merkle_proof.rs diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/receipt.rs b/bridges/snowbridge/primitives/beacon/src/receipt.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/beacon/src/receipt.rs rename to bridges/snowbridge/primitives/beacon/src/receipt.rs diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/serde_utils.rs b/bridges/snowbridge/primitives/beacon/src/serde_utils.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/beacon/src/serde_utils.rs rename to bridges/snowbridge/primitives/beacon/src/serde_utils.rs diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/ssz.rs b/bridges/snowbridge/primitives/beacon/src/ssz.rs similarity index 69% rename from bridges/snowbridge/parachain/primitives/beacon/src/ssz.rs rename to bridges/snowbridge/primitives/beacon/src/ssz.rs index 4f8b19ca8892ceceaff6e039712b67ff9cb98d2f..0925c3cc701d1b7493963599cde97bf939534724 100644 --- a/bridges/snowbridge/parachain/primitives/beacon/src/ssz.rs +++ b/bridges/snowbridge/primitives/beacon/src/ssz.rs @@ -192,3 +192,87 @@ pub fn hash_tree_root(mut object: T) -> Result Err(err.into()), } } + +pub mod deneb { + use crate::{ + config::{EXTRA_DATA_SIZE, FEE_RECIPIENT_SIZE, LOGS_BLOOM_SIZE}, + ssz::hash_tree_root, + types::deneb::ExecutionPayloadHeader, + }; + use byte_slice_cast::AsByteSlice; + use sp_core::H256; + use sp_std::{vec, vec::Vec}; + use ssz_rs::{ + prelude::{List, Vector}, + Deserialize, DeserializeError, SimpleSerializeError, Sized, U256, + }; + use ssz_rs_derive::SimpleSerialize as SimpleSerializeDerive; + + #[derive(Default, SimpleSerializeDerive, Clone, Debug)] + pub struct SSZExecutionPayloadHeader { + pub parent_hash: [u8; 32], + pub fee_recipient: Vector, + pub state_root: [u8; 32], + pub receipts_root: [u8; 32], + pub logs_bloom: Vector, + pub prev_randao: [u8; 32], + pub block_number: u64, + pub gas_limit: u64, + pub gas_used: u64, + pub timestamp: u64, + pub extra_data: List, + pub base_fee_per_gas: U256, + pub block_hash: [u8; 32], + pub transactions_root: [u8; 32], + pub withdrawals_root: [u8; 32], + pub blob_gas_used: u64, + pub excess_blob_gas: u64, + } + + impl TryFrom for SSZExecutionPayloadHeader { + type Error = SimpleSerializeError; + + fn try_from(payload: ExecutionPayloadHeader) -> Result { + Ok(SSZExecutionPayloadHeader { + parent_hash: payload.parent_hash.to_fixed_bytes(), + fee_recipient: Vector::::try_from( + payload.fee_recipient.to_fixed_bytes().to_vec(), + ) + .expect("checked statically; qed"), + state_root: payload.state_root.to_fixed_bytes(), + receipts_root: payload.receipts_root.to_fixed_bytes(), + // Logs bloom bytes size is not constrained, so here we do need to check the + // try_from error + logs_bloom: Vector::::try_from(payload.logs_bloom) + .map_err(|(_, err)| err)?, + prev_randao: payload.prev_randao.to_fixed_bytes(), + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + // Extra data bytes size is not constrained, so here we do need to check the + // try_from error + extra_data: List::::try_from(payload.extra_data) + .map_err(|(_, err)| err)?, + base_fee_per_gas: U256::from_bytes_le( + payload + .base_fee_per_gas + .as_byte_slice() + .try_into() + .expect("checked in prep; qed"), + ), + block_hash: payload.block_hash.to_fixed_bytes(), + transactions_root: payload.transactions_root.to_fixed_bytes(), + withdrawals_root: payload.withdrawals_root.to_fixed_bytes(), + blob_gas_used: payload.blob_gas_used, + excess_blob_gas: payload.excess_blob_gas, + }) + } + } + + impl ExecutionPayloadHeader { + pub fn hash_tree_root(&self) -> Result { + hash_tree_root::(self.clone().try_into()?) + } + } +} diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/types.rs b/bridges/snowbridge/primitives/beacon/src/types.rs similarity index 77% rename from bridges/snowbridge/parachain/primitives/beacon/src/types.rs rename to bridges/snowbridge/primitives/beacon/src/types.rs index f893551d9d1720bf12a32982c6783c457dbc92ba..2af522f56b0dc4b00a155252e927a90e9bca9ed1 100644 --- a/bridges/snowbridge/parachain/primitives/beacon/src/types.rs +++ b/bridges/snowbridge/primitives/beacon/src/types.rs @@ -5,7 +5,7 @@ use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use scale_info::TypeInfo; use sp_core::{H160, H256, U256}; use sp_runtime::RuntimeDebug; -use sp_std::{boxed::Box, prelude::*}; +use sp_std::{boxed::Box, iter::repeat, prelude::*}; use crate::config::{PUBKEY_SIZE, SIGNATURE_SIZE}; @@ -35,6 +35,7 @@ pub struct ForkVersions { pub altair: Fork, pub bellatrix: Fork, pub capella: Fork, + pub deneb: Fork, } #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] @@ -189,9 +190,14 @@ pub struct SyncCommitteePrepared { impl Default for SyncCommitteePrepared { fn default() -> Self { + let pubkeys: Vec = + repeat(PublicKeyPrepared::default()).take(COMMITTEE_SIZE).collect(); + let pubkeys: Box<[PublicKeyPrepared; COMMITTEE_SIZE]> = + Box::new(pubkeys.try_into().map_err(|_| ()).expect("checked statically; qed")); + SyncCommitteePrepared { root: H256::default(), - pubkeys: Box::new([PublicKeyPrepared::default(); COMMITTEE_SIZE]), + pubkeys, aggregate_pubkey: PublicKeyPrepared::default(), } } @@ -207,7 +213,7 @@ impl TryFrom<&SyncCommittee> let sync_committee_root = sync_committee.hash_tree_root().expect("checked statically; qed"); Ok(SyncCommitteePrepared:: { - pubkeys: g1_pubkeys.try_into().expect("checked statically; qed"), + pubkeys: g1_pubkeys.try_into().map_err(|_| ()).expect("checked statically; qed"), aggregate_pubkey: prepare_milagro_pubkey(&sync_committee.aggregate_pubkey)?, root: sync_committee_root, }) @@ -309,7 +315,7 @@ impl )] #[cfg_attr( feature = "std", - derive(Deserialize), + derive(Serialize, Deserialize), serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = "")) )] #[codec(mel_bound())] @@ -386,6 +392,64 @@ pub struct CompactBeaconState { pub block_roots_root: H256, } +/// VersionedExecutionPayloadHeader +#[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo)] +#[cfg_attr( + feature = "std", + derive(Serialize, Deserialize), + serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = "")) +)] +#[codec(mel_bound())] +pub enum VersionedExecutionPayloadHeader { + Capella(ExecutionPayloadHeader), + Deneb(deneb::ExecutionPayloadHeader), +} + +/// Convert VersionedExecutionPayloadHeader to CompactExecutionHeader +impl From for CompactExecutionHeader { + fn from(versioned_execution_header: VersionedExecutionPayloadHeader) -> Self { + match versioned_execution_header { + VersionedExecutionPayloadHeader::Capella(execution_payload_header) => + execution_payload_header.into(), + VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => + execution_payload_header.into(), + } + } +} + +impl VersionedExecutionPayloadHeader { + pub fn hash_tree_root(&self) -> Result { + match self { + VersionedExecutionPayloadHeader::Capella(execution_payload_header) => + hash_tree_root::( + execution_payload_header.clone().try_into()?, + ), + VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => + hash_tree_root::( + execution_payload_header.clone().try_into()?, + ), + } + } + + pub fn block_hash(&self) -> H256 { + match self { + VersionedExecutionPayloadHeader::Capella(execution_payload_header) => + execution_payload_header.block_hash, + VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => + execution_payload_header.block_hash, + } + } + + pub fn block_number(&self) -> u64 { + match self { + VersionedExecutionPayloadHeader::Capella(execution_payload_header) => + execution_payload_header.block_number, + VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => + execution_payload_header.block_number, + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -510,3 +574,68 @@ pub enum Mode { Active, Blocked, } + +pub mod deneb { + use crate::CompactExecutionHeader; + use codec::{Decode, Encode}; + use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound}; + use scale_info::TypeInfo; + #[cfg(feature = "std")] + use serde::{Deserialize, Serialize}; + use sp_core::{H160, H256, U256}; + use sp_std::prelude::*; + + /// ExecutionPayloadHeader + /// + #[derive( + Default, Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, + )] + #[cfg_attr( + feature = "std", + derive(Serialize, Deserialize), + serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = "")) + )] + #[codec(mel_bound())] + pub struct ExecutionPayloadHeader { + pub parent_hash: H256, + pub fee_recipient: H160, + pub state_root: H256, + pub receipts_root: H256, + #[cfg_attr( + feature = "std", + serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes") + )] + pub logs_bloom: Vec, + pub prev_randao: H256, + pub block_number: u64, + pub gas_limit: u64, + pub gas_used: u64, + pub timestamp: u64, + #[cfg_attr( + feature = "std", + serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes") + )] + pub extra_data: Vec, + #[cfg_attr( + feature = "std", + serde(deserialize_with = "crate::serde_utils::from_int_to_u256") + )] + pub base_fee_per_gas: U256, + pub block_hash: H256, + pub transactions_root: H256, + pub withdrawals_root: H256, + pub blob_gas_used: u64, // [New in Deneb:EIP4844] + pub excess_blob_gas: u64, // [New in Deneb:EIP4844] + } + + impl From for CompactExecutionHeader { + fn from(execution_payload: ExecutionPayloadHeader) -> Self { + Self { + parent_hash: execution_payload.parent_hash, + block_number: execution_payload.block_number, + state_root: execution_payload.state_root, + receipts_root: execution_payload.receipts_root, + } + } + } +} diff --git a/bridges/snowbridge/parachain/primitives/beacon/src/updates.rs b/bridges/snowbridge/primitives/beacon/src/updates.rs similarity index 96% rename from bridges/snowbridge/parachain/primitives/beacon/src/updates.rs rename to bridges/snowbridge/primitives/beacon/src/updates.rs index 9a78b4f1e2d3de4af23c27c7e4fc111a79e22dc6..1ecd32c6d7b79f34854d9387fba78b99cce333b2 100644 --- a/bridges/snowbridge/parachain/primitives/beacon/src/updates.rs +++ b/bridges/snowbridge/primitives/beacon/src/updates.rs @@ -6,7 +6,7 @@ use scale_info::TypeInfo; use sp_core::H256; use sp_std::prelude::*; -use crate::types::{BeaconHeader, ExecutionPayloadHeader, SyncAggregate, SyncCommittee}; +use crate::types::{BeaconHeader, SyncAggregate, SyncCommittee, VersionedExecutionPayloadHeader}; #[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo)] #[cfg_attr( @@ -91,7 +91,7 @@ pub struct ExecutionHeaderUpdate { /// Proof that `header` is an ancestor of a finalized header pub ancestry_proof: Option, /// Execution header to be imported - pub execution_header: ExecutionPayloadHeader, + pub execution_header: VersionedExecutionPayloadHeader, /// Merkle proof that execution payload is contained within `header` pub execution_branch: Vec, } diff --git a/bridges/snowbridge/parachain/primitives/core/Cargo.toml b/bridges/snowbridge/primitives/core/Cargo.toml similarity index 52% rename from bridges/snowbridge/parachain/primitives/core/Cargo.toml rename to bridges/snowbridge/primitives/core/Cargo.toml index a4092b4ee6f0b2bbbf3cdb94b1e066b37529b2aa..9a299ad0ae92326a6d0bb0391baf81e6e5bad663 100644 --- a/bridges/snowbridge/parachain/primitives/core/Cargo.toml +++ b/bridges/snowbridge/primitives/core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-core" description = "Snowbridge Core" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -12,26 +12,26 @@ categories = ["cryptography::cryptocurrencies"] workspace = true [dependencies] -serde = { version = "1.0.195", optional = true, features = ["alloc", "derive"], default-features = false } +serde = { optional = true, features = ["alloc", "derive"], workspace = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } hex-literal = { version = "0.4.1" } -polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } +polkadot-parachain-primitives = { path = "../../../../polkadot/parachain", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder", default-features = false } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-arithmetic = { path = "../../../../../substrate/primitives/arithmetic", default-features = false } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-arithmetic = { path = "../../../../substrate/primitives/arithmetic", default-features = false } snowbridge-beacon-primitives = { path = "../beacon", default-features = false } -ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } +ethabi = { package = "ethabi-decode", version = "1.0.0", default-features = false } [dev-dependencies] hex = { version = "0.4.3" } diff --git a/bridges/snowbridge/parachain/primitives/core/README.md b/bridges/snowbridge/primitives/core/README.md similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/README.md rename to bridges/snowbridge/primitives/core/README.md diff --git a/bridges/snowbridge/parachain/primitives/core/src/inbound.rs b/bridges/snowbridge/primitives/core/src/inbound.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/src/inbound.rs rename to bridges/snowbridge/primitives/core/src/inbound.rs diff --git a/bridges/snowbridge/parachain/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs similarity index 97% rename from bridges/snowbridge/parachain/primitives/core/src/lib.rs rename to bridges/snowbridge/primitives/core/src/lib.rs index a464557a72b479f2cc6fe803a640a0d0a2cd7a9a..ed1af4225d24bb9ae8d08f7adda2925e7d5029d3 100644 --- a/bridges/snowbridge/parachain/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -44,10 +44,6 @@ where SiblingParaId::from(para_id).into_account_truncating() } -pub fn sibling_sovereign_account_raw(para_id: ParaId) -> [u8; 32] { - SiblingParaId::from(para_id).into_account_truncating() -} - pub struct AllowSiblingsOnly; impl Contains for AllowSiblingsOnly { fn contains(location: &Location) -> bool { diff --git a/bridges/snowbridge/parachain/primitives/core/src/operating_mode.rs b/bridges/snowbridge/primitives/core/src/operating_mode.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/src/operating_mode.rs rename to bridges/snowbridge/primitives/core/src/operating_mode.rs diff --git a/bridges/snowbridge/parachain/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/src/outbound.rs rename to bridges/snowbridge/primitives/core/src/outbound.rs diff --git a/bridges/snowbridge/parachain/primitives/core/src/pricing.rs b/bridges/snowbridge/primitives/core/src/pricing.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/src/pricing.rs rename to bridges/snowbridge/primitives/core/src/pricing.rs diff --git a/bridges/snowbridge/parachain/primitives/core/src/ringbuffer.rs b/bridges/snowbridge/primitives/core/src/ringbuffer.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/src/ringbuffer.rs rename to bridges/snowbridge/primitives/core/src/ringbuffer.rs diff --git a/bridges/snowbridge/parachain/primitives/core/src/tests.rs b/bridges/snowbridge/primitives/core/src/tests.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/src/tests.rs rename to bridges/snowbridge/primitives/core/src/tests.rs diff --git a/bridges/snowbridge/parachain/primitives/core/tests/fixtures/packet.scale b/bridges/snowbridge/primitives/core/tests/fixtures/packet.scale similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/tests/fixtures/packet.scale rename to bridges/snowbridge/primitives/core/tests/fixtures/packet.scale diff --git a/bridges/snowbridge/parachain/primitives/core/tests/mod.rs b/bridges/snowbridge/primitives/core/tests/mod.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/core/tests/mod.rs rename to bridges/snowbridge/primitives/core/tests/mod.rs diff --git a/bridges/snowbridge/parachain/primitives/ethereum/Cargo.toml b/bridges/snowbridge/primitives/ethereum/Cargo.toml similarity index 64% rename from bridges/snowbridge/parachain/primitives/ethereum/Cargo.toml rename to bridges/snowbridge/primitives/ethereum/Cargo.toml index 016f16ce0e6a5dad4c84bd8078947321c29b370e..9fa725a6c0565a5f42847d89149878f8997d07a0 100644 --- a/bridges/snowbridge/parachain/primitives/ethereum/Cargo.toml +++ b/bridges/snowbridge/primitives/ethereum/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-ethereum" description = "Snowbridge Ethereum" -version = "0.9.0" +version = "0.3.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -12,8 +12,8 @@ categories = ["cryptography::cryptocurrencies"] workspace = true [dependencies] -serde = { version = "1.0.195", optional = true, features = ["derive"] } -serde-big-array = { version = "0.3.2", optional = true, features = ["const-generics"] } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +serde-big-array = { optional = true, features = ["const-generics"], workspace = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } ethbloom = { version = "0.13.0", default-features = false } @@ -23,17 +23,17 @@ hex-literal = { version = "0.4.1", default-features = false } parity-bytes = { version = "0.1.2", default-features = false } rlp = { version = "0.5.2", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } -ethabi = { git = "https://github.com/snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } +ethabi = { package = "ethabi-decode", version = "1.0.0", default-features = false } [dev-dependencies] wasm-bindgen-test = "0.3.19" rand = "0.8.5" -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } [features] default = ["std"] diff --git a/bridges/snowbridge/parachain/primitives/ethereum/README.md b/bridges/snowbridge/primitives/ethereum/README.md similarity index 100% rename from bridges/snowbridge/parachain/primitives/ethereum/README.md rename to bridges/snowbridge/primitives/ethereum/README.md diff --git a/bridges/snowbridge/parachain/primitives/ethereum/src/header.rs b/bridges/snowbridge/primitives/ethereum/src/header.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/ethereum/src/header.rs rename to bridges/snowbridge/primitives/ethereum/src/header.rs diff --git a/bridges/snowbridge/parachain/primitives/ethereum/src/lib.rs b/bridges/snowbridge/primitives/ethereum/src/lib.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/ethereum/src/lib.rs rename to bridges/snowbridge/primitives/ethereum/src/lib.rs diff --git a/bridges/snowbridge/parachain/primitives/ethereum/src/log.rs b/bridges/snowbridge/primitives/ethereum/src/log.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/ethereum/src/log.rs rename to bridges/snowbridge/primitives/ethereum/src/log.rs diff --git a/bridges/snowbridge/parachain/primitives/ethereum/src/mpt.rs b/bridges/snowbridge/primitives/ethereum/src/mpt.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/ethereum/src/mpt.rs rename to bridges/snowbridge/primitives/ethereum/src/mpt.rs diff --git a/bridges/snowbridge/parachain/primitives/ethereum/src/receipt.rs b/bridges/snowbridge/primitives/ethereum/src/receipt.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/ethereum/src/receipt.rs rename to bridges/snowbridge/primitives/ethereum/src/receipt.rs diff --git a/bridges/snowbridge/parachain/primitives/router/Cargo.toml b/bridges/snowbridge/primitives/router/Cargo.toml similarity index 56% rename from bridges/snowbridge/parachain/primitives/router/Cargo.toml rename to bridges/snowbridge/primitives/router/Cargo.toml index ed8f68d7bc077164d3db21739f766cee874d361e..ded773e0d38917b7834679b3e521dfbe9539e51b 100644 --- a/bridges/snowbridge/parachain/primitives/router/Cargo.toml +++ b/bridges/snowbridge/primitives/router/Cargo.toml @@ -12,25 +12,25 @@ categories = ["cryptography::cryptocurrencies"] workspace = true [dependencies] -serde = { version = "1.0.195", optional = true, features = ["derive"] } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } -sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../polkadot/xcm/xcm-executor", default-features = false } snowbridge-core = { path = "../core", default-features = false } -ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } +ethabi = { package = "ethabi-decode", version = "1.0.0", default-features = false } hex-literal = { version = "0.4.1" } diff --git a/bridges/snowbridge/parachain/primitives/router/README.md b/bridges/snowbridge/primitives/router/README.md similarity index 100% rename from bridges/snowbridge/parachain/primitives/router/README.md rename to bridges/snowbridge/primitives/router/README.md diff --git a/bridges/snowbridge/parachain/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/router/src/inbound/mod.rs rename to bridges/snowbridge/primitives/router/src/inbound/mod.rs diff --git a/bridges/snowbridge/parachain/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs similarity index 97% rename from bridges/snowbridge/parachain/primitives/router/src/inbound/tests.rs rename to bridges/snowbridge/primitives/router/src/inbound/tests.rs index c46b88a84a4b822eff1098c5c135bf4292a34f8c..75670b05c1004d65f16e20848c05ca9b28c38452 100644 --- a/bridges/snowbridge/parachain/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -2,7 +2,7 @@ use super::GlobalConsensusEthereumConvertsFor; use crate::inbound::CallIndex; use frame_support::parameter_types; use hex_literal::hex; -use xcm::v4::prelude::*; +use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; const NETWORK: NetworkId = Ethereum { chain_id: 11155111 }; diff --git a/bridges/snowbridge/parachain/primitives/router/src/lib.rs b/bridges/snowbridge/primitives/router/src/lib.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/router/src/lib.rs rename to bridges/snowbridge/primitives/router/src/lib.rs diff --git a/bridges/snowbridge/parachain/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs similarity index 99% rename from bridges/snowbridge/parachain/primitives/router/src/outbound/mod.rs rename to bridges/snowbridge/primitives/router/src/outbound/mod.rs index 21823992db7c3e70b3bc4015fe09d59fe4e45587..ddc36ce8cb61b370e862de1784a4f6b9fc6f16b0 100644 --- a/bridges/snowbridge/parachain/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -16,7 +16,7 @@ use snowbridge_core::{ }; use sp_core::{H160, H256}; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; -use xcm::v4::prelude::*; +use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; pub struct EthereumBlobExporter< diff --git a/bridges/snowbridge/parachain/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs similarity index 100% rename from bridges/snowbridge/parachain/primitives/router/src/outbound/tests.rs rename to bridges/snowbridge/primitives/router/src/outbound/tests.rs diff --git a/bridges/snowbridge/parachain/runtime/runtime-common/Cargo.toml b/bridges/snowbridge/runtime/runtime-common/Cargo.toml similarity index 55% rename from bridges/snowbridge/parachain/runtime/runtime-common/Cargo.toml rename to bridges/snowbridge/runtime/runtime-common/Cargo.toml index b81c5d496e83984727ff772b555ddb6eb5bfb4f8..bf5e9a8832dcf48113d5f74a92a060687da2fe4e 100644 --- a/bridges/snowbridge/parachain/runtime/runtime-common/Cargo.toml +++ b/bridges/snowbridge/runtime/runtime-common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-runtime-common" description = "Snowbridge Runtime Common" -version = "0.9.0" +version = "0.2.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true @@ -12,14 +12,15 @@ categories = ["cryptography::cryptocurrencies"] workspace = true [dependencies] -log = { version = "0.4.20", default-features = false } - -frame-support = { path = "../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../substrate/frame/system", default-features = false } -sp-arithmetic = { path = "../../../../../substrate/primitives/arithmetic", default-features = false } -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +log = { workspace = true } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-arithmetic = { path = "../../../../substrate/primitives/arithmetic", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../polkadot/xcm/xcm-executor", default-features = false } snowbridge-core = { path = "../../primitives/core", default-features = false } @@ -28,11 +29,13 @@ snowbridge-core = { path = "../../primitives/core", default-features = false } [features] default = ["std"] std = [ + "codec/std", "frame-support/std", "frame-system/std", "log/std", "snowbridge-core/std", "sp-arithmetic/std", + "sp-std/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", diff --git a/bridges/snowbridge/parachain/runtime/runtime-common/README.md b/bridges/snowbridge/runtime/runtime-common/README.md similarity index 100% rename from bridges/snowbridge/parachain/runtime/runtime-common/README.md rename to bridges/snowbridge/runtime/runtime-common/README.md diff --git a/bridges/snowbridge/parachain/runtime/runtime-common/src/lib.rs b/bridges/snowbridge/runtime/runtime-common/src/lib.rs similarity index 58% rename from bridges/snowbridge/parachain/runtime/runtime-common/src/lib.rs rename to bridges/snowbridge/runtime/runtime-common/src/lib.rs index 1a9b704f356cdc61c72a30d36091f7ad2726e9f8..aae45520ff4bd84545cef260dc363c4fd6053ea3 100644 --- a/bridges/snowbridge/parachain/runtime/runtime-common/src/lib.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/lib.rs @@ -5,14 +5,21 @@ //! Common traits and types shared by runtimes. #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod tests; + +use codec::FullCodec; use core::marker::PhantomData; use frame_support::traits::Get; -use snowbridge_core::{outbound::SendMessageFeeProvider, sibling_sovereign_account_raw}; +use snowbridge_core::outbound::SendMessageFeeProvider; use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; +use sp_std::fmt::Debug; use xcm::prelude::*; -use xcm_builder::{deposit_or_burn_fee, HandleFee}; +use xcm_builder::HandleFee; use xcm_executor::traits::{FeeReason, TransactAsset}; +pub const LOG_TARGET: &str = "xcm::export-fee-to-sibling"; + /// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions /// to Snowbridge and splits off the remote fee and deposits it to the origin /// parachain sovereign account. The local fee is then returned back to be handled by @@ -44,8 +51,8 @@ impl where - Balance: BaseArithmetic + Unsigned + Copy + From + Into, - AccountId: Clone + Into<[u8; 32]> + From<[u8; 32]>, + Balance: BaseArithmetic + Unsigned + Copy + From + Into + Debug, + AccountId: Clone + FullCodec, FeeAssetLocation: Get, EthereumNetwork: Get, AssetTransactor: TransactAsset, @@ -64,20 +71,27 @@ impl = if let Some(XcmContext { origin: Some(Location { parents: 1, interior }), .. }) = context { if let Some(Parachain(sibling_para_id)) = interior.first() { - let account: AccountId = - sibling_sovereign_account_raw((*sibling_para_id).into()).into(); - account + Some(*sibling_para_id) } else { - return fees + None } } else { - return fees + None }; + if maybe_para_id.is_none() { + log::error!( + target: LOG_TARGET, + "invalid location in context {:?}", + context, + ); + return fees + } + let para_id = maybe_para_id.unwrap(); // Get the total fee offered by export message. let maybe_total_supplied_fee: Option<(usize, Balance)> = fees @@ -93,32 +107,45 @@ impl (0u128).into() { - // Refund remote component of fee to physical origin - deposit_or_burn_fee::( - Asset { id: AssetId(token_location.clone()), fun: Fungible(remote_fee.into()) } - .into(), - context, - para_sovereign, - ); - // Return remaining fee to the next fee handler in the chain. - let mut modified_fees = fees.inner().clone(); - modified_fees.remove(fee_index); - modified_fees.push(Asset { - id: AssetId(token_location), - fun: Fungible((total_fee - remote_fee).into()), - }); - return modified_fees.into() - } + if maybe_total_supplied_fee.is_none() { + log::error!( + target: LOG_TARGET, + "could not find fee asset item in fees: {:?}", + fees, + ); + return fees } - - log::info!( - target: "xcm::fees", - "XcmExportFeeToSibling skipped: {fees:?}, context: {context:?}, reason: {reason:?}", + let (fee_index, total_fee) = maybe_total_supplied_fee.unwrap(); + let local_fee = FeeProvider::local_fee(); + let remote_fee = total_fee.saturating_sub(local_fee); + if local_fee == Balance::zero() || remote_fee == Balance::zero() { + log::error!( + target: LOG_TARGET, + "calculated refund incorrect with local_fee: {:?} and remote_fee: {:?}", + local_fee, + remote_fee, + ); + return fees + } + // Refund remote component of fee to physical origin + let result = AssetTransactor::deposit_asset( + &Asset { id: AssetId(token_location.clone()), fun: Fungible(remote_fee.into()) }, + &Location::new(1, [Parachain(para_id)]), + context, ); - fees + if result.is_err() { + log::error!( + target: LOG_TARGET, + "transact fee asset failed: {:?}", + result.unwrap_err() + ); + return fees + } + + // Return remaining fee to the next fee handler in the chain. + let mut modified_fees = fees.inner().clone(); + modified_fees.remove(fee_index); + modified_fees.push(Asset { id: AssetId(token_location), fun: Fungible(local_fee.into()) }); + modified_fees.into() } } diff --git a/bridges/snowbridge/runtime/runtime-common/src/tests.rs b/bridges/snowbridge/runtime/runtime-common/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..8d9a88f42933bd4fbe324aca5d2e30d56508d1df --- /dev/null +++ b/bridges/snowbridge/runtime/runtime-common/src/tests.rs @@ -0,0 +1,168 @@ +use crate::XcmExportFeeToSibling; +use frame_support::{parameter_types, sp_runtime::testing::H256}; +use snowbridge_core::outbound::{Fee, Message, SendError, SendMessage, SendMessageFeeProvider}; +use xcm::prelude::{ + Asset, Assets, Here, Kusama, Location, NetworkId, Parachain, XcmContext, XcmError, XcmHash, + XcmResult, +}; +use xcm_builder::HandleFee; +use xcm_executor::{ + traits::{FeeReason, TransactAsset}, + AssetsInHolding, +}; + +parameter_types! { + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub TokenLocation: Location = Location::parent(); +} + +struct MockOkOutboundQueue; +impl SendMessage for MockOkOutboundQueue { + type Ticket = (); + + fn validate(_: &Message) -> Result<(Self::Ticket, Fee), SendError> { + Ok(((), Fee { local: 1, remote: 1 })) + } + + fn deliver(_: Self::Ticket) -> Result { + Ok(H256::zero()) + } +} + +impl SendMessageFeeProvider for MockOkOutboundQueue { + type Balance = u128; + + fn local_fee() -> Self::Balance { + 1 + } +} +struct MockErrOutboundQueue; +impl SendMessage for MockErrOutboundQueue { + type Ticket = (); + + fn validate(_: &Message) -> Result<(Self::Ticket, Fee), SendError> { + Err(SendError::MessageTooLarge) + } + + fn deliver(_: Self::Ticket) -> Result { + Err(SendError::MessageTooLarge) + } +} + +impl SendMessageFeeProvider for MockErrOutboundQueue { + type Balance = u128; + + fn local_fee() -> Self::Balance { + 1 + } +} + +pub struct MockAssetTransactor; +impl TransactAsset for MockAssetTransactor { + fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { + Ok(()) + } + + fn withdraw_asset( + _what: &Asset, + _who: &Location, + _context: Option<&XcmContext>, + ) -> Result { + Ok(Assets::default().into()) + } + + fn internal_transfer_asset( + _what: &Asset, + _from: &Location, + _to: &Location, + _context: &XcmContext, + ) -> Result { + Ok(Assets::default().into()) + } +} + +#[test] +fn handle_fee_success() { + let fee: Assets = Asset::from((Location::parent(), 10_u128)).into(); + let ctx = XcmContext { + origin: Some(Location::new(1, Parachain(1000))), + message_id: XcmHash::default(), + topic: None, + }; + let reason = FeeReason::Export { network: EthereumNetwork::get(), destination: Here }; + let result = XcmExportFeeToSibling::< + u128, + u64, + TokenLocation, + EthereumNetwork, + MockAssetTransactor, + MockOkOutboundQueue, + >::handle_fee(fee, Some(&ctx), reason); + let local_fee = Asset::from((Location::parent(), MockOkOutboundQueue::local_fee())).into(); + // assert only local fee left + assert_eq!(result, local_fee) +} + +#[test] +fn handle_fee_success_but_not_for_ethereum() { + let fee: Assets = Asset::from((Location::parent(), 10_u128)).into(); + let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + // invalid network not for ethereum + let reason = FeeReason::Export { network: Kusama, destination: Here }; + let result = XcmExportFeeToSibling::< + u128, + u64, + TokenLocation, + EthereumNetwork, + MockAssetTransactor, + MockOkOutboundQueue, + >::handle_fee(fee.clone(), Some(&ctx), reason); + // assert fee not touched and just forward to the next handler + assert_eq!(result, fee) +} + +#[test] +fn handle_fee_success_even_from_an_invalid_none_origin_location() { + let fee: Assets = Asset::from((Location::parent(), 10_u128)).into(); + // invalid origin None here not from a sibling chain + let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + let reason = FeeReason::Export { network: EthereumNetwork::get(), destination: Here }; + let result = XcmExportFeeToSibling::< + u128, + u64, + TokenLocation, + EthereumNetwork, + MockAssetTransactor, + MockOkOutboundQueue, + >::handle_fee(fee.clone(), Some(&ctx), reason); + assert_eq!(result, fee) +} + +#[test] +fn handle_fee_success_even_when_fee_insufficient() { + // insufficient fee not cover the (local_fee + remote_fee) required + let fee: Assets = Asset::from((Location::parent(), 1_u128)).into(); + let ctx = XcmContext { + origin: Some(Location::new(1, Parachain(1000))), + message_id: XcmHash::default(), + topic: None, + }; + let reason = FeeReason::Export { network: EthereumNetwork::get(), destination: Here }; + let result = XcmExportFeeToSibling::< + u128, + u64, + TokenLocation, + EthereumNetwork, + MockAssetTransactor, + MockOkOutboundQueue, + >::handle_fee(fee.clone(), Some(&ctx), reason); + assert_eq!(result, fee) +} diff --git a/bridges/snowbridge/runtime/test-common/Cargo.toml b/bridges/snowbridge/runtime/test-common/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5f169e82f49346742bd97028da583105bf02335d --- /dev/null +++ b/bridges/snowbridge/runtime/test-common/Cargo.toml @@ -0,0 +1,202 @@ +[package] +name = "snowbridge-runtime-test-common" +description = "Snowbridge Runtime Tests" +version = "0.2.0" +authors = ["Snowfork "] +edition = "2021" +license = "Apache-2.0" +categories = ["cryptography::cryptocurrencies"] + +[lints] +workspace = true + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +hex-literal = { version = "0.4.1" } +log = { workspace = true } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +smallvec = "1.11.0" + +# Substrate +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-executive = { path = "../../../../substrate/frame/executive", default-features = false } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +frame-system-benchmarking = { path = "../../../../substrate/frame/system/benchmarking", default-features = false, optional = true } +frame-system-rpc-runtime-api = { path = "../../../../substrate/frame/system/rpc/runtime-api", default-features = false } +frame-try-runtime = { path = "../../../../substrate/frame/try-runtime", default-features = false, optional = true } +pallet-aura = { path = "../../../../substrate/frame/aura", default-features = false } +pallet-authorship = { path = "../../../../substrate/frame/authorship", default-features = false } +pallet-balances = { path = "../../../../substrate/frame/balances", default-features = false } +pallet-session = { path = "../../../../substrate/frame/session", default-features = false } +pallet-multisig = { path = "../../../../substrate/frame/multisig", default-features = false } +pallet-message-queue = { path = "../../../../substrate/frame/message-queue", default-features = false } +pallet-timestamp = { path = "../../../../substrate/frame/timestamp", default-features = false } +pallet-transaction-payment = { path = "../../../../substrate/frame/transaction-payment", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { path = "../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } +pallet-utility = { path = "../../../../substrate/frame/utility", default-features = false } +sp-api = { path = "../../../../substrate/primitives/api", default-features = false } +sp-block-builder = { path = "../../../../substrate/primitives/block-builder", default-features = false } +sp-consensus-aura = { path = "../../../../substrate/primitives/consensus/aura", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-genesis-builder = { path = "../../../../substrate/primitives/genesis-builder", default-features = false } +sp-inherents = { path = "../../../../substrate/primitives/inherents", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-keyring = { path = "../../../../substrate/primitives/keyring" } +sp-offchain = { path = "../../../../substrate/primitives/offchain", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-session = { path = "../../../../substrate/primitives/session", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-storage = { path = "../../../../substrate/primitives/storage", default-features = false } +sp-transaction-pool = { path = "../../../../substrate/primitives/transaction-pool", default-features = false } +sp-version = { path = "../../../../substrate/primitives/version", default-features = false } + +# Polkadot +pallet-xcm = { path = "../../../../polkadot/xcm/pallet-xcm", default-features = false } +pallet-xcm-benchmarks = { path = "../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } +polkadot-core-primitives = { path = "../../../../polkadot/core-primitives", default-features = false } +polkadot-parachain-primitives = { path = "../../../../polkadot/parachain", default-features = false } +polkadot-runtime-common = { path = "../../../../polkadot/runtime/common", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../polkadot/xcm/xcm-executor", default-features = false } + +# Cumulus +cumulus-pallet-aura-ext = { path = "../../../../cumulus/pallets/aura-ext", default-features = false } +cumulus-pallet-parachain-system = { path = "../../../../cumulus/pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } +cumulus-pallet-session-benchmarking = { path = "../../../../cumulus/pallets/session-benchmarking", default-features = false } +cumulus-pallet-xcm = { path = "../../../../cumulus/pallets/xcm", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../../cumulus/pallets/xcmp-queue", default-features = false, features = ["bridging"] } +cumulus-primitives-core = { path = "../../../../cumulus/primitives/core", default-features = false } +cumulus-primitives-utility = { path = "../../../../cumulus/primitives/utility", default-features = false } +pallet-collator-selection = { path = "../../../../cumulus/pallets/collator-selection", default-features = false } +parachain-info = { package = "staging-parachain-info", path = "../../../../cumulus/parachains/pallets/parachain-info", default-features = false } +parachains-common = { path = "../../../../cumulus/parachains/common", default-features = false } +parachains-runtimes-test-utils = { path = "../../../../cumulus/parachains/runtimes/test-utils", default-features = false } +assets-common = { path = "../../../../cumulus/parachains/runtimes/assets/common", default-features = false } + +# Ethereum Bridge (Snowbridge) +snowbridge-core = { path = "../../primitives/core", default-features = false } +snowbridge-beacon-primitives = { path = "../../primitives/beacon", default-features = false } +snowbridge-router-primitives = { path = "../../primitives/router", default-features = false } +snowbridge-pallet-ethereum-client = { path = "../../pallets/ethereum-client", default-features = false } +snowbridge-pallet-ethereum-client-fixtures = { path = "../../pallets/ethereum-client/fixtures", default-features = false } +snowbridge-pallet-inbound-queue = { path = "../../pallets/inbound-queue", default-features = false } +snowbridge-pallet-outbound-queue = { path = "../../pallets/outbound-queue", default-features = false } +snowbridge-outbound-queue-runtime-api = { path = "../../pallets/outbound-queue/runtime-api", default-features = false } +snowbridge-pallet-system = { path = "../../pallets/system", default-features = false } +snowbridge-system-runtime-api = { path = "../../pallets/system/runtime-api", default-features = false } + +[dev-dependencies] +static_assertions = "1.1" +bridge-hub-test-utils = { path = "../../../../cumulus/parachains/runtimes/bridge-hubs/test-utils" } +bridge-runtime-common = { path = "../../../bin/runtime-common", features = ["integrity-test"] } +sp-keyring = { path = "../../../../substrate/primitives/keyring" } + +[features] +default = ["std"] +std = [ + "assets-common/std", + "codec/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-session-benchmarking/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-utility/std", + "frame-benchmarking/std", + "frame-executive/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "frame-try-runtime?/std", + "log/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-collator-selection/std", + "pallet-message-queue/std", + "pallet-multisig/std", + "pallet-session/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-utility/std", + "pallet-xcm-benchmarks?/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "parachains-runtimes-test-utils/std", + "polkadot-core-primitives/std", + "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", + "scale-info/std", + "serde", + "snowbridge-beacon-primitives/std", + "snowbridge-core/std", + "snowbridge-outbound-queue-runtime-api/std", + "snowbridge-pallet-ethereum-client-fixtures/std", + "snowbridge-pallet-ethereum-client/std", + "snowbridge-pallet-inbound-queue/std", + "snowbridge-pallet-outbound-queue/std", + "snowbridge-pallet-system/std", + "snowbridge-router-primitives/std", + "snowbridge-system-runtime-api/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-genesis-builder/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-storage/std", + "sp-transaction-pool/std", + "sp-version/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm/std", +] + +runtime-benchmarks = [ + "assets-common/runtime-benchmarks", + "bridge-runtime-common/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-utility/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "snowbridge-pallet-ethereum-client-fixtures/runtime-benchmarks", + "snowbridge-pallet-ethereum-client/runtime-benchmarks", + "snowbridge-pallet-inbound-queue/runtime-benchmarks", + "snowbridge-pallet-outbound-queue/runtime-benchmarks", + "snowbridge-pallet-system/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] +fast-runtime = [] diff --git a/bridges/snowbridge/parachain/runtime/test-common/README.md b/bridges/snowbridge/runtime/test-common/README.md similarity index 100% rename from bridges/snowbridge/parachain/runtime/test-common/README.md rename to bridges/snowbridge/runtime/test-common/README.md diff --git a/bridges/snowbridge/runtime/test-common/src/lib.rs b/bridges/snowbridge/runtime/test-common/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..7455adf76170acefd50f06e8a40ef1c79028f49f --- /dev/null +++ b/bridges/snowbridge/runtime/test-common/src/lib.rs @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +use codec::Encode; +use frame_support::{ + assert_err, assert_ok, + traits::{fungible::Mutate, OnFinalize, OnInitialize}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works; +use parachains_runtimes_test_utils::{ + AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, ValidatorIdOf, XcmReceivedFrom, +}; +use snowbridge_core::{ChannelId, ParaId}; +use snowbridge_pallet_ethereum_client_fixtures::*; +use sp_core::{H160, U256}; +use sp_keyring::AccountKeyring::*; +use sp_runtime::{traits::Header, AccountId32, DigestItem, SaturatedConversion, Saturating}; +use xcm::{ + latest::prelude::*, + v3::Error::{self, Barrier}, +}; +use xcm_executor::XcmExecutor; + +type RuntimeHelper = + parachains_runtimes_test_utils::RuntimeHelper; + +pub fn initial_fund(assethub_parachain_id: u32, initial_amount: u128) +where + Runtime: frame_system::Config + pallet_balances::Config, +{ + // fund asset hub sovereign account enough so it can pay fees + let asset_hub_sovereign_account = + snowbridge_core::sibling_sovereign_account::(assethub_parachain_id.into()); + >::mint_into( + &asset_hub_sovereign_account, + initial_amount.saturated_into::>(), + ) + .unwrap(); +} + +pub fn send_transfer_token_message( + ethereum_chain_id: u64, + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, +) -> Outcome +where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + pallet_timestamp::Config, + XcmConfig: xcm_executor::Config, +{ + let assethub_parachain_location = Location::new(1, Parachain(assethub_parachain_id)); + let asset = Asset { + id: AssetId(Location::new( + 0, + [AccountKey20 { network: None, key: weth_contract_address.into() }], + )), + fun: Fungible(1000000000), + }; + let assets = vec![asset.clone()]; + + let inner_xcm = Xcm(vec![ + WithdrawAsset(Assets::from(assets.clone())), + ClearOrigin, + BuyExecution { fees: asset, weight_limit: Unlimited }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { network: None, key: destination_address.into() }], + ), + }, + SetTopic([0; 32]), + ]); + + let fee = + Asset { id: AssetId(Location { parents: 1, interior: Here }), fun: Fungible(fee_amount) }; + + // prepare transfer token message + let xcm = Xcm(vec![ + WithdrawAsset(Assets::from(vec![fee.clone()])), + BuyExecution { fees: fee, weight_limit: Unlimited }, + ExportMessage { + network: Ethereum { chain_id: ethereum_chain_id }, + destination: Here, + xcm: inner_xcm, + }, + ]); + + // execute XCM + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + assethub_parachain_location, + xcm, + &mut hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + Weight::zero(), + ) +} + +pub fn send_transfer_token_message_success( + ethereum_chain_id: u64, + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + snowbridge_pallet_outbound_queue: Box< + dyn Fn(Vec) -> Option>, + >, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + pallet_message_queue::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + + pallet_timestamp::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, + ::AccountId: From + AsRef<[u8]>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + >::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + ) + .unwrap(); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, 5_000_000_000_000); + + let outcome = send_transfer_token_message::( + ethereum_chain_id, + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + + assert_ok!(outcome.ensure_complete()); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| snowbridge_pallet_outbound_queue(e.event.encode())); + assert!(events.any(|e| matches!( + e, + snowbridge_pallet_outbound_queue::Event::MessageQueued { .. } + ))); + + let block_number = >::block_number(); + let next_block_number = >::block_number() + .saturating_add(BlockNumberFor::::from(1u32)); + + // finish current block + >::on_finalize(block_number); + >::on_finalize(block_number); + >::on_finalize(block_number); + + // start next block + >::set_block_number(next_block_number); + >::on_initialize(next_block_number); + >::on_initialize(next_block_number); + >::on_initialize(next_block_number); + + // finish next block + >::on_finalize(next_block_number); + >::on_finalize(next_block_number); + let included_head = >::finalize(); + + let origin: ParaId = assethub_parachain_id.into(); + let channel_id: ChannelId = origin.into(); + + let nonce = snowbridge_pallet_outbound_queue::Nonce::::try_get(channel_id); + assert_ok!(nonce); + assert_eq!(nonce.unwrap(), 1); + + let digest = included_head.digest(); + + let digest_items = digest.logs(); + assert!(digest_items.len() == 1 && digest_items[0].as_other().is_some()); + }); +} + +pub fn ethereum_outbound_queue_processes_messages_before_message_queue_works< + Runtime, + XcmConfig, + AllPalletsWithoutSystem, +>( + ethereum_chain_id: u64, + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + snowbridge_pallet_outbound_queue: Box< + dyn Fn(Vec) -> Option>, + >, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + pallet_message_queue::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + + pallet_timestamp::Config, + XcmConfig: xcm_executor::Config, + AllPalletsWithoutSystem: + OnInitialize> + OnFinalize>, + ValidatorIdOf: From>, + ::AccountId: From + AsRef<[u8]>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + >::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + ) + .unwrap(); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, 5_000_000_000_000); + + let outcome = send_transfer_token_message::( + ethereum_chain_id, + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + + assert_ok!(outcome.ensure_complete()); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| snowbridge_pallet_outbound_queue(e.event.encode())); + assert!(events.any(|e| matches!( + e, + snowbridge_pallet_outbound_queue::Event::MessageQueued { .. } + ))); + + let next_block_number: U256 = >::block_number() + .saturating_add(BlockNumberFor::::from(1u32)) + .into(); + + let included_head = + RuntimeHelper::::run_to_block_with_finalize( + next_block_number.as_u32(), + ); + let digest = included_head.digest(); + let digest_items = digest.logs(); + + let mut found_outbound_digest = false; + for digest_item in digest_items { + match digest_item { + DigestItem::Other(_) => found_outbound_digest = true, + _ => {}, + } + } + + assert_eq!(found_outbound_digest, true); + }); +} + +pub fn send_unpaid_transfer_token_message( + ethereum_chain_id: u64, + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + weth_contract_address: H160, + destination_contract: H160, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + pallet_timestamp::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, +{ + let assethub_parachain_location = Location::new(1, Parachain(assethub_parachain_id)); + + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + let asset_hub_sovereign_account = + snowbridge_core::sibling_sovereign_account::(assethub_parachain_id.into()); + + >::mint_into( + &asset_hub_sovereign_account, + 4000000000u32.into(), + ) + .unwrap(); + + let asset = Asset { + id: AssetId(Location::new( + 0, + [AccountKey20 { network: None, key: weth_contract_address.into() }], + )), + fun: Fungible(1000000000), + }; + let assets = vec![asset.clone()]; + + let inner_xcm = Xcm(vec![ + WithdrawAsset(Assets::from(assets.clone())), + ClearOrigin, + BuyExecution { fees: asset, weight_limit: Unlimited }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: Location::new( + 0, + [AccountKey20 { network: None, key: destination_contract.into() }], + ), + }, + SetTopic([0; 32]), + ]); + + // prepare transfer token message + let xcm = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + ExportMessage { + network: Ethereum { chain_id: ethereum_chain_id }, + destination: Here, + xcm: inner_xcm, + }, + ]); + + // execute XCM + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let outcome = XcmExecutor::::prepare_and_execute( + assethub_parachain_location, + xcm, + &mut hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + Weight::zero(), + ); + // check error is barrier + assert_err!(outcome.ensure_complete(), Barrier); + }); +} + +#[allow(clippy::too_many_arguments)] +pub fn send_transfer_token_message_failure( + ethereum_chain_id: u64, + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + assethub_parachain_id: u32, + initial_amount: u128, + weth_contract_address: H160, + destination_address: H160, + fee_amount: u128, + expected_error: Error, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + + pallet_timestamp::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + >::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), + ) + .unwrap(); + + // fund asset hub sovereign account enough so it can pay fees + initial_fund::(assethub_parachain_id, initial_amount); + + let outcome = send_transfer_token_message::( + ethereum_chain_id, + assethub_parachain_id, + weth_contract_address, + destination_address, + fee_amount, + ); + assert_err!(outcome.ensure_complete(), expected_error); + }); +} + +pub fn ethereum_extrinsic( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + construct_and_apply_extrinsic: fn( + sp_keyring::AccountKeyring, + ::RuntimeCall, + ) -> sp_runtime::DispatchOutcome, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + pallet_utility::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + + snowbridge_pallet_ethereum_client::Config + + pallet_timestamp::Config, + ValidatorIdOf: From>, + ::RuntimeCall: + From>, + ::RuntimeCall: From>, + AccountIdOf: From, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + let initial_checkpoint = make_checkpoint(); + let update = make_finalized_header_update(); + let sync_committee_update = make_sync_committee_update(); + let execution_header_update = make_execution_header_update(); + + let alice = Alice; + let alice_account = alice.to_account_id(); + >::mint_into( + &alice_account.into(), + 10_000_000_000_000_u128.saturated_into::>(), + ) + .unwrap(); + + assert_ok!(>::force_checkpoint( + RuntimeHelper::::root_origin(), + initial_checkpoint, + )); + + let update_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*update), + } + .into(); + + let update_sync_committee_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*sync_committee_update), + } + .into(); + + let execution_header_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit_execution_header { + update: Box::new(*execution_header_update), + } + .into(); + + let update_outcome = construct_and_apply_extrinsic(alice, update_call.into()); + assert_ok!(update_outcome); + + let sync_committee_outcome = + construct_and_apply_extrinsic(alice, update_sync_committee_call.into()); + assert_ok!(sync_committee_outcome); + + let execution_header_outcome = + construct_and_apply_extrinsic(alice, execution_header_call.into()); + assert_ok!(execution_header_outcome); + }); +} + +pub fn ethereum_to_polkadot_message_extrinsics_work( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + construct_and_apply_extrinsic: fn( + sp_keyring::AccountKeyring, + ::RuntimeCall, + ) -> sp_runtime::DispatchOutcome, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + pallet_utility::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + + snowbridge_pallet_ethereum_client::Config + + pallet_timestamp::Config, + ValidatorIdOf: From>, + ::RuntimeCall: + From>, + ::RuntimeCall: From>, + AccountIdOf: From, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + let initial_checkpoint = make_checkpoint(); + let sync_committee_update = make_sync_committee_update(); + let execution_header_update = make_execution_header_update(); + + let alice = Alice; + let alice_account = alice.to_account_id(); + >::mint_into( + &alice_account.into(), + 10_000_000_000_000_u128.saturated_into::>(), + ) + .unwrap(); + + assert_ok!(>::force_checkpoint( + RuntimeHelper::::root_origin(), + initial_checkpoint, + )); + + let update_sync_committee_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*sync_committee_update), + } + .into(); + + let execution_header_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit_execution_header { + update: Box::new(*execution_header_update), + } + .into(); + + let sync_committee_outcome = + construct_and_apply_extrinsic(alice, update_sync_committee_call.into()); + assert_ok!(sync_committee_outcome); + + let execution_header_outcome = + construct_and_apply_extrinsic(alice, execution_header_call.into()); + assert_ok!(execution_header_outcome); + }); +} diff --git a/bridges/snowbridge/parachain/scripts/benchmark.sh b/bridges/snowbridge/scripts/benchmark.sh similarity index 100% rename from bridges/snowbridge/parachain/scripts/benchmark.sh rename to bridges/snowbridge/scripts/benchmark.sh diff --git a/bridges/snowbridge/scripts/contribute-upstream.sh b/bridges/snowbridge/scripts/contribute-upstream.sh new file mode 100755 index 0000000000000000000000000000000000000000..32005b770ecf44cb9af18c61f830243ed5287e68 --- /dev/null +++ b/bridges/snowbridge/scripts/contribute-upstream.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +# A script to cleanup the Snowfork fork of the polkadot-sdk to contribute it upstream back to parity/polkadot-sdk +# ./bridges/snowbridge/scripts/contribute-upstream.sh + +# show CLI help +function show_help() { + set +x + echo " " + echo Error: $1 + echo "Usage:" + echo " ./bridges/snowbridge/scripts/contribute-upstream.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" + exit 1 +} + +if [[ -z "$1" ]]; then + echo "Please provide a branch name you would like your upstream branch to be named" + exit 1 +fi + +branch_name=$1 + +set -eux + +# let's avoid any restrictions on where this script can be called for - snowbridge repo may be +# plugged into any other repo folder. So the script (and other stuff that needs to be removed) +# may be located either in call dir, or one of it subdirs. +SNOWBRIDGE_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/../" + +# Get the current Git branch name +current_branch=$(git rev-parse --abbrev-ref HEAD) + +if [ "$current_branch" = "$branch_name" ] || git branch | grep -q "$branch_name"; then + echo "Already on requested branch or branch exists, not creating." +else + git branch "$branch_name" +fi + +git checkout "$branch_name" + +# remove everything we think is not required for our needs +rm -rf rust-toolchain.toml +rm -rf codecov.yml +rm -rf $SNOWBRIDGE_FOLDER/.cargo +rm -rf $SNOWBRIDGE_FOLDER/.github +rm -rf $SNOWBRIDGE_FOLDER/SECURITY.md +rm -rf $SNOWBRIDGE_FOLDER/.gitignore +rm -rf $SNOWBRIDGE_FOLDER/rustfmt.toml +rm -rf $SNOWBRIDGE_FOLDER/templates +rm -rf $SNOWBRIDGE_FOLDER/pallets/ethereum-client/fuzz + +pushd $SNOWBRIDGE_FOLDER + +# let's test if everything we need compiles +cargo check -p snowbridge-pallet-ethereum-client +cargo check -p snowbridge-pallet-ethereum-client --features runtime-benchmarks +cargo check -p snowbridge-pallet-ethereum-client --features try-runtime +cargo check -p snowbridge-pallet-inbound-queue +cargo check -p snowbridge-pallet-inbound-queue --features runtime-benchmarks +cargo check -p snowbridge-pallet-inbound-queue --features try-runtime +cargo check -p snowbridge-pallet-outbound-queue +cargo check -p snowbridge-pallet-outbound-queue --features runtime-benchmarks +cargo check -p snowbridge-pallet-outbound-queue --features try-runtime +cargo check -p snowbridge-pallet-system +cargo check -p snowbridge-pallet-system --features runtime-benchmarks +cargo check -p snowbridge-pallet-system --features try-runtime + +# we're removing lock file after all checks are done. Otherwise we may use different +# Substrate/Polkadot/Cumulus commits and our checks will fail +rm -f $SNOWBRIDGE_FOLDER/Cargo.toml +rm -f $SNOWBRIDGE_FOLDER/Cargo.lock + +popd + +# Replace Parity's CI files, that we have overwritten in our fork, to run our own CI +rm -rf .github +git remote -v | grep -w parity || git remote add parity https://github.com/paritytech/polkadot-sdk +git fetch parity master +git checkout parity/master -- .github +git add -- .github + +echo "OK" diff --git a/bridges/snowbridge/parachain/scripts/hexliteral.sh b/bridges/snowbridge/scripts/hexliteral.sh similarity index 100% rename from bridges/snowbridge/parachain/scripts/hexliteral.sh rename to bridges/snowbridge/scripts/hexliteral.sh diff --git a/bridges/snowbridge/parachain/scripts/init.sh b/bridges/snowbridge/scripts/init.sh similarity index 100% rename from bridges/snowbridge/parachain/scripts/init.sh rename to bridges/snowbridge/scripts/init.sh diff --git a/bridges/snowbridge/parachain/scripts/make-build-config.sh b/bridges/snowbridge/scripts/make-build-config.sh similarity index 100% rename from bridges/snowbridge/parachain/scripts/make-build-config.sh rename to bridges/snowbridge/scripts/make-build-config.sh diff --git a/bridges/zombienet/README.md b/bridges/testing/README.md similarity index 94% rename from bridges/zombienet/README.md rename to bridges/testing/README.md index b601154b624ce69ed921ea6c2453d17c4d37b6c8..bd467a410d013c363913a8e4b2be8ca7b184e2dc 100644 --- a/bridges/zombienet/README.md +++ b/bridges/testing/README.md @@ -23,7 +23,7 @@ To start those tests, you need to: - copy fresh `substrate-relay` binary, built in previous point, to the `~/local_bridge_testing/bin/substrate-relay`; -- change the `POLKADOT_SDK_FOLDER` and `ZOMBIENET_BINARY_PATH` (and ensure that the nearby variables +- 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, you could run tests with the `./run-tests.sh` command. Hopefully, it'll show the diff --git a/cumulus/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml similarity index 82% rename from cumulus/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml rename to bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml index 99a7d0035b511c57ccf5c10fa94165933c495ba9..52271f9442131923f8a758b16df7610e73813d15 100644 --- a/cumulus/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +++ b/bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml @@ -2,7 +2,7 @@ node_spawn_timeout = 240 [relaychain] -default_command = "{{POLKADOT_BINARY_PATH}}" +default_command = "{{POLKADOT_BINARY}}" default_args = [ "-lparachain=debug,xcm=trace" ] chain = "rococo-local" @@ -36,24 +36,22 @@ cumulus_based = true [[parachains.collators]] name = "bridge-hub-rococo-collator1" validator = true - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" rpc_port = 8933 ws_port = 8943 args = [ - "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", - "--force-authoring" + "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" ] # run bob as parachain collator [[parachains.collators]] name = "bridge-hub-rococo-collator2" validator = true - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" rpc_port = 8934 ws_port = 8944 args = [ - "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", - "--force-authoring" + "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" ] [[parachains]] @@ -65,14 +63,14 @@ cumulus_based = true name = "asset-hub-rococo-collator1" rpc_port = 9911 ws_port = 9910 - command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_ROCOCO}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" args = [ "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" ] [[parachains.collators]] name = "asset-hub-rococo-collator2" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_ROCOCO}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" args = [ "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" ] diff --git a/cumulus/zombienet/bridge-hubs/bridge_hub_westend_local_network.toml b/bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml similarity index 84% rename from cumulus/zombienet/bridge-hubs/bridge_hub_westend_local_network.toml rename to bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml index 1919d1c63f25f154e4676599afb8a2969598c10b..f2550bcc9959638b21ea78043cca3bc12d3d23ea 100644 --- a/cumulus/zombienet/bridge-hubs/bridge_hub_westend_local_network.toml +++ b/bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml @@ -2,7 +2,7 @@ node_spawn_timeout = 240 [relaychain] -default_command = "{{POLKADOT_BINARY_PATH}}" +default_command = "{{POLKADOT_BINARY}}" default_args = [ "-lparachain=debug,xcm=trace" ] chain = "westend-local" @@ -36,24 +36,22 @@ cumulus_based = true [[parachains.collators]] name = "bridge-hub-westend-collator1" validator = true - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" rpc_port = 8935 ws_port = 8945 args = [ - "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", - "--force-authoring" + "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" ] # run bob as parachain collator [[parachains.collators]] name = "bridge-hub-westend-collator2" validator = true - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" rpc_port = 8936 ws_port = 8946 args = [ - "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", - "--force-authoring" + "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" ] [[parachains]] @@ -65,14 +63,14 @@ cumulus_based = true name = "asset-hub-westend-collator1" rpc_port = 9011 ws_port = 9010 - command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_WESTEND}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" args = [ "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" ] [[parachains.collators]] name = "asset-hub-westend-collator2" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_WESTEND}}" + command = "{{POLKADOT_PARACHAIN_BINARY}}" args = [ "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" ] diff --git a/cumulus/scripts/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh similarity index 97% rename from cumulus/scripts/bridges_rococo_westend.sh rename to bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh index 3b6f8e892858ad034a9db23a717b4290f9024bde..479ab833abfc38dd978c7b7d3abdd4c1fe37ad64 100755 --- a/cumulus/scripts/bridges_rococo_westend.sh +++ b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh @@ -1,7 +1,7 @@ #!/bin/bash # import common functions -source "$(dirname "$0")"/bridges_common.sh +source "$FRAMEWORK_PATH/utils/bridges.sh" # Expected sovereign accounts. # @@ -185,8 +185,8 @@ function run_relay() { case "$1" in run-relay) - init_ro_wnd init_wnd_ro + init_ro_wnd run_relay ;; init-asset-hub-rococo-local) @@ -319,6 +319,7 @@ case "$1" in $XCM_VERSION ;; reserve-transfer-assets-from-asset-hub-rococo-local) + amount=$2 ensure_polkadot_js_api # send ROCs to Alice account on AHW limited_reserve_transfer_assets \ @@ -326,11 +327,12 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": 5000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; withdraw-reserve-assets-from-asset-hub-rococo-local) + amount=$2 ensure_polkadot_js_api # send back only 100000000000 wrappedWNDs to Alice account on AHW limited_reserve_transfer_assets \ @@ -338,11 +340,12 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } } }, "fun": { "Fungible": 3000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; reserve-transfer-assets-from-asset-hub-westend-local) + amount=$2 ensure_polkadot_js_api # send WNDs to Alice account on AHR limited_reserve_transfer_assets \ @@ -350,11 +353,12 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": 5000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; withdraw-reserve-assets-from-asset-hub-westend-local) + amount=$2 ensure_polkadot_js_api # send back only 100000000000 wrappedROCs to Alice account on AHR limited_reserve_transfer_assets \ @@ -362,7 +366,7 @@ case "$1" in "//Alice" \ "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } } }')" \ "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [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] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } } }, "fun": { "Fungible": 3000000000000 } } ] }')" \ + "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } } }, "fun": { "Fungible": '$amount' } } ] }')" \ 0 \ "Unlimited" ;; diff --git a/bridges/testing/environments/rococo-westend/helper.sh b/bridges/testing/environments/rococo-westend/helper.sh new file mode 100755 index 0000000000000000000000000000000000000000..0a13ded213f5d3a0920cb466fc974c129e9ad79a --- /dev/null +++ b/bridges/testing/environments/rococo-westend/helper.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +$ENV_PATH/bridges_rococo_westend.sh "$@" diff --git a/bridges/testing/environments/rococo-westend/rococo-init.zndsl b/bridges/testing/environments/rococo-westend/rococo-init.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..c913e4db31f49184eb8214fda4d525c3594b358b --- /dev/null +++ b/bridges/testing/environments/rococo-westend/rococo-init.zndsl @@ -0,0 +1,8 @@ +Description: Check if the HRMP channel between Rococo BH and Rococo AH was opened successfully +Network: ./bridge_hub_rococo_local_network.toml +Creds: config + +# ensure that initialization has completed +asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wait-hrmp-channel-opened.js with "1013" within 300 seconds + + diff --git a/bridges/testing/environments/rococo-westend/rococo.zndsl b/bridges/testing/environments/rococo-westend/rococo.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..5b49c7c632fa4dd0ce77134858a2f697acbfff16 --- /dev/null +++ b/bridges/testing/environments/rococo-westend/rococo.zndsl @@ -0,0 +1,7 @@ +Description: Check if the with-Westend GRANPDA pallet was initialized at Rococo BH +Network: ./bridge_hub_rococo_local_network.toml +Creds: config + +# relay is already started - let's wait until with-Westend GRANPDA pallet is initialized at Rococo +bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/best-finalized-header-at-bridged-chain.js with "Westend,0" within 400 seconds + diff --git a/bridges/testing/environments/rococo-westend/spawn.sh b/bridges/testing/environments/rococo-westend/spawn.sh new file mode 100755 index 0000000000000000000000000000000000000000..cbd0b1bc623ab77876ed5ce3beefd7ab72db2d37 --- /dev/null +++ b/bridges/testing/environments/rococo-westend/spawn.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +set -e + +trap "trap - SIGTERM && kill -9 -$$" SIGINT SIGTERM EXIT + +source "$FRAMEWORK_PATH/utils/zombienet.sh" + +# whether to init the chains (open HRMP channels, set XCM version, create reserve assets, etc) +init=0 +start_relayer=0 +while [ $# -ne 0 ] +do + arg="$1" + case "$arg" in + --init) + init=1 + ;; + --start-relayer) + start_relayer=1 + ;; + esac + shift +done + +logs_dir=$TEST_DIR/logs +helper_script="${BASH_SOURCE%/*}/helper.sh" + +rococo_def=${BASH_SOURCE%/*}/bridge_hub_rococo_local_network.toml +start_zombienet $TEST_DIR $rococo_def rococo_dir rococo_pid +echo + +westend_def=${BASH_SOURCE%/*}/bridge_hub_westend_local_network.toml +start_zombienet $TEST_DIR $westend_def westend_dir westend_pid +echo + +if [[ $init -eq 1 ]]; then + rococo_init_log=$logs_dir/rococo-init.log + echo -e "Setting up the rococo side of the bridge. Logs available at: $rococo_init_log\n" + + westend_init_log=$logs_dir/westend-init.log + echo -e "Setting up the westend side of the bridge. Logs available at: $westend_init_log\n" + + $helper_script init-asset-hub-rococo-local >> $rococo_init_log 2>&1 & + rococo_init_pid=$! + $helper_script init-asset-hub-westend-local >> $westend_init_log 2>&1 & + westend_init_pid=$! + wait -n $rococo_init_pid $westend_init_pid + + + $helper_script init-bridge-hub-rococo-local >> $rococo_init_log 2>&1 & + rococo_init_pid=$! + $helper_script init-bridge-hub-westend-local >> $westend_init_log 2>&1 & + westend_init_pid=$! + wait -n $rococo_init_pid $westend_init_pid + + run_zndsl ${BASH_SOURCE%/*}/rococo-init.zndsl $rococo_dir + run_zndsl ${BASH_SOURCE%/*}/westend-init.zndsl $westend_dir +fi + +if [[ $start_relayer -eq 1 ]]; then + ${BASH_SOURCE%/*}/start_relayer.sh $rococo_dir $westend_dir relayer_pid +fi + +echo $rococo_dir > $TEST_DIR/rococo.env +echo $westend_dir > $TEST_DIR/westend.env +echo + +wait -n $rococo_pid $westend_pid $relayer_pid +kill -9 -$$ diff --git a/bridges/testing/environments/rococo-westend/start_relayer.sh b/bridges/testing/environments/rococo-westend/start_relayer.sh new file mode 100755 index 0000000000000000000000000000000000000000..7ddd312d395aa8733d2afea59277b48721c8a36b --- /dev/null +++ b/bridges/testing/environments/rococo-westend/start_relayer.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +source "$FRAMEWORK_PATH/utils/common.sh" +source "$FRAMEWORK_PATH/utils/zombienet.sh" + +rococo_dir=$1 +westend_dir=$2 +__relayer_pid=$3 + +logs_dir=$TEST_DIR/logs +helper_script="${BASH_SOURCE%/*}/helper.sh" + +relayer_log=$logs_dir/relayer.log +echo -e "Starting rococo-westend relayer. Logs available at: $relayer_log\n" +start_background_process "$helper_script run-relay" $relayer_log relayer_pid + +run_zndsl ${BASH_SOURCE%/*}/rococo.zndsl $rococo_dir +run_zndsl ${BASH_SOURCE%/*}/westend.zndsl $westend_dir + +eval $__relayer_pid="'$relayer_pid'" + diff --git a/bridges/testing/environments/rococo-westend/westend-init.zndsl b/bridges/testing/environments/rococo-westend/westend-init.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..0f5428eed3b01c042f8aad3b3df51c3a800a9b72 --- /dev/null +++ b/bridges/testing/environments/rococo-westend/westend-init.zndsl @@ -0,0 +1,7 @@ +Description: Check if the HRMP channel between Westend BH and Westend AH was opened successfully +Network: ./bridge_hub_westend_local_network.toml +Creds: config + +# ensure that initialization has completed +asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wait-hrmp-channel-opened.js with "1002" within 600 seconds + diff --git a/bridges/testing/environments/rococo-westend/westend.zndsl b/bridges/testing/environments/rococo-westend/westend.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..07968838852f7c0a00131db3080c460c07d08206 --- /dev/null +++ b/bridges/testing/environments/rococo-westend/westend.zndsl @@ -0,0 +1,6 @@ +Description: Check if the with-Rococo GRANPDA pallet was initialized at Westend BH +Network: ./bridge_hub_westend_local_network.toml +Creds: config + +# relay is already started - let's wait until with-Rococo GRANPDA pallet is initialized at Westend +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/best-finalized-header-at-bridged-chain.js with "Rococo,0" within 400 seconds diff --git a/bridges/zombienet/helpers/best-finalized-header-at-bridged-chain.js b/bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js similarity index 94% rename from bridges/zombienet/helpers/best-finalized-header-at-bridged-chain.js rename to bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js index f7e1eefc84b3fa3e799d7111608cfc39783f5e21..af4f18aee9b2710612ed142c50b28caf8313326d 100644 --- a/bridges/zombienet/helpers/best-finalized-header-at-bridged-chain.js +++ b/bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js @@ -18,7 +18,7 @@ async function run(nodeName, networkInfo, args) { } // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 12000)); + await new Promise((resolve) => setTimeout(resolve, 6000)); } } diff --git a/bridges/testing/framework/js-helpers/chains/rococo-at-westend.js b/bridges/testing/framework/js-helpers/chains/rococo-at-westend.js new file mode 100644 index 0000000000000000000000000000000000000000..bcce3b3a303f55a16e766c6558878650ed03ab80 --- /dev/null +++ b/bridges/testing/framework/js-helpers/chains/rococo-at-westend.js @@ -0,0 +1,6 @@ +module.exports = { + grandpaPalletName: "bridgeRococoGrandpa", + parachainsPalletName: "bridgeRococoParachains", + messagesPalletName: "bridgeRococoMessages", + bridgedBridgeHubParaId: 1013, +} diff --git a/bridges/testing/framework/js-helpers/chains/westend-at-rococo.js b/bridges/testing/framework/js-helpers/chains/westend-at-rococo.js new file mode 100644 index 0000000000000000000000000000000000000000..6a15b64a23b7c28f2b66a6491caebafc4c93dff5 --- /dev/null +++ b/bridges/testing/framework/js-helpers/chains/westend-at-rococo.js @@ -0,0 +1,6 @@ +module.exports = { + grandpaPalletName: "bridgeWestendGrandpa", + parachainsPalletName: "bridgeWestendParachains", + messagesPalletName: "bridgeWestendMessages", + bridgedBridgeHubParaId: 1002, +} diff --git a/bridges/zombienet/helpers/native-assets-balance-increased.js b/bridges/testing/framework/js-helpers/native-assets-balance-increased.js similarity index 74% rename from bridges/zombienet/helpers/native-assets-balance-increased.js rename to bridges/testing/framework/js-helpers/native-assets-balance-increased.js index 9ee1a769e9f2807ed7b73ca9c6aa4b89d5c135f9..749c3e2fec32ac0af4d244c53cb4ac1c6237817a 100644 --- a/bridges/zombienet/helpers/native-assets-balance-increased.js +++ b/bridges/testing/framework/js-helpers/native-assets-balance-increased.js @@ -3,18 +3,19 @@ async function run(nodeName, networkInfo, args) { const api = await zombie.connect(wsUri, userDefinedTypes); const accountAddress = args[0]; + const expectedIncrease = BigInt(args[1]); const initialAccountData = await api.query.system.account(accountAddress); const initialAccountBalance = initialAccountData.data['free']; while (true) { const accountData = await api.query.system.account(accountAddress); const accountBalance = accountData.data['free']; - if (accountBalance > initialAccountBalance) { + if (accountBalance > initialAccountBalance + expectedIncrease) { return accountBalance; } // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 12000)); + await new Promise((resolve) => setTimeout(resolve, 6000)); } } -module.exports = { run } +module.exports = {run} diff --git a/bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js b/bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js new file mode 100644 index 0000000000000000000000000000000000000000..979179245ebe9f5b250efca6f2e6199ef0ac86d7 --- /dev/null +++ b/bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js @@ -0,0 +1,44 @@ +const utils = require("./utils"); + +async function run(nodeName, networkInfo, args) { + const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + + // parse arguments + const exitAfterSeconds = Number(args[0]); + const bridgedChain = require("./chains/" + args[1]); + + // start listening to new blocks + let totalGrandpaHeaders = 0; + let initialParachainHeaderImported = false; + api.rpc.chain.subscribeNewHeads(async function (header) { + const apiAtParent = await api.at(header.parentHash); + const apiAtCurrent = await api.at(header.hash); + const currentEvents = await apiAtCurrent.query.system.events(); + + totalGrandpaHeaders += await utils.ensureOnlyMandatoryGrandpaHeadersImported( + bridgedChain, + apiAtParent, + apiAtCurrent, + currentEvents, + ); + initialParachainHeaderImported = await utils.ensureOnlyInitialParachainHeaderImported( + bridgedChain, + apiAtParent, + apiAtCurrent, + currentEvents, + ); + }); + + // wait given time + await new Promise(resolve => setTimeout(resolve, exitAfterSeconds * 1000)); + // if we haven't seen any new GRANDPA or parachain headers => fail + if (totalGrandpaHeaders == 0) { + throw new Error("No bridged relay chain headers imported"); + } + if (!initialParachainHeaderImported) { + throw new Error("No bridged parachain headers imported"); + } +} + +module.exports = { run } 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-idle.js new file mode 100644 index 0000000000000000000000000000000000000000..8c3130e4fd960601d377dde5101520c95531cdf6 --- /dev/null +++ b/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js @@ -0,0 +1,81 @@ +const utils = require("./utils"); + +async function run(nodeName, networkInfo, args) { + const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + + // parse arguments + const exitAfterSeconds = Number(args[0]); + const bridgedChain = require("./chains/" + args[1]); + + // start listening to new blocks + let atLeastOneMessageReceived = false; + let atLeastOneMessageDelivered = false; + const unsubscribe = await api.rpc.chain.subscribeNewHeads(async function (header) { + const apiAtParent = await api.at(header.parentHash); + const apiAtCurrent = await api.at(header.hash); + const currentEvents = await apiAtCurrent.query.system.events(); + + const messagesReceived = currentEvents.find((record) => { + return record.event.section == bridgedChain.messagesPalletName + && record.event.method == "MessagesReceived"; + }) != undefined; + const messagesDelivered = currentEvents.find((record) => { + return record.event.section == bridgedChain.messagesPalletName && + record.event.method == "MessagesDelivered"; + }) != undefined; + const hasMessageUpdates = messagesReceived || messagesDelivered; + atLeastOneMessageReceived = atLeastOneMessageReceived || messagesReceived; + atLeastOneMessageDelivered = atLeastOneMessageDelivered || messagesDelivered; + + if (!hasMessageUpdates) { + // if there are no any message update transactions, we only expect mandatory GRANDPA + // headers and initial parachain headers + await utils.ensureOnlyMandatoryGrandpaHeadersImported( + bridgedChain, + apiAtParent, + apiAtCurrent, + currentEvents, + ); + await utils.ensureOnlyInitialParachainHeaderImported( + bridgedChain, + apiAtParent, + apiAtCurrent, + currentEvents, + ); + } else { + const messageTransactions = (messagesReceived ? 1 : 0) + (messagesDelivered ? 1 : 0); + + // otherwise we only accept at most one GRANDPA header + const newGrandpaHeaders = utils.countGrandpaHeaderImports(bridgedChain, currentEvents); + if (newGrandpaHeaders > 1) { + utils.logEvents(currentEvents); + throw new Error("Unexpected relay chain header import: " + newGrandpaHeaders + " / " + messageTransactions); + } + + // ...and at most one parachain header + const newParachainHeaders = utils.countParachainHeaderImports(bridgedChain, currentEvents); + if (newParachainHeaders > 1) { + utils.logEvents(currentEvents); + throw new Error("Unexpected parachain header import: " + newParachainHeaders + " / " + messageTransactions); + } + } + }); + + // wait until we have received + delivered messages OR until timeout + await utils.pollUntil( + exitAfterSeconds, + () => { return atLeastOneMessageReceived && atLeastOneMessageDelivered; }, + () => { unsubscribe(); }, + () => { + if (!atLeastOneMessageReceived) { + throw new Error("No messages received from bridged chain"); + } + if (!atLeastOneMessageDelivered) { + throw new Error("No messages delivered to bridged chain"); + } + }, + ); +} + +module.exports = { run } diff --git a/bridges/zombienet/helpers/relayer-rewards.js b/bridges/testing/framework/js-helpers/relayer-rewards.js similarity index 93% rename from bridges/zombienet/helpers/relayer-rewards.js rename to bridges/testing/framework/js-helpers/relayer-rewards.js index a5f567db797722e04d3bfae90745a728ff1abdff..5347c649604fc209042725c9cf269c9d3ca0290f 100644 --- a/bridges/zombienet/helpers/relayer-rewards.js +++ b/bridges/testing/framework/js-helpers/relayer-rewards.js @@ -21,7 +21,7 @@ async function run(nodeName, networkInfo, args) { } // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 12000)); + await new Promise((resolve) => setTimeout(resolve, 6000)); } } diff --git a/bridges/testing/framework/js-helpers/utils.js b/bridges/testing/framework/js-helpers/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..f6e9f5623b47b3cb3c642245e86654ae9f65358a --- /dev/null +++ b/bridges/testing/framework/js-helpers/utils.js @@ -0,0 +1,103 @@ +module.exports = { + logEvents: function(events) { + let stringifiedEvents = ""; + events.forEach((record) => { + if (stringifiedEvents != "") { + stringifiedEvents += ", "; + } + stringifiedEvents += record.event.section + "::" + record.event.method; + }); + console.log("Block events: " + stringifiedEvents); + }, + countGrandpaHeaderImports: function(bridgedChain, events) { + return events.reduce( + (count, record) => { + const { event } = record; + if (event.section == bridgedChain.grandpaPalletName && event.method == "UpdatedBestFinalizedHeader") { + count += 1; + } + return count; + }, + 0, + ); + }, + countParachainHeaderImports: function(bridgedChain, events) { + return events.reduce( + (count, record) => { + const { event } = record; + if (event.section == bridgedChain.parachainsPalletName && event.method == "UpdatedParachainHead") { + count += 1; + } + return count; + }, + 0, + ); + }, + pollUntil: async function( + timeoutInSecs, + predicate, + cleanup, + onFailure, + ) { + const begin = new Date().getTime(); + const end = begin + timeoutInSecs * 1000; + while (new Date().getTime() < end) { + if (predicate()) { + cleanup(); + return; + } + await new Promise(resolve => setTimeout(resolve, 100)); + } + + cleanup(); + onFailure(); + }, + ensureOnlyMandatoryGrandpaHeadersImported: async function( + bridgedChain, + apiAtParent, + apiAtCurrent, + currentEvents, + ) { + // remember id of bridged relay chain GRANDPA authorities set at parent block + const authoritySetAtParent = await apiAtParent.query[bridgedChain.grandpaPalletName].currentAuthoritySet(); + const authoritySetIdAtParent = authoritySetAtParent["setId"]; + + // now read the id of bridged relay chain GRANDPA authorities set at current block + const authoritySetAtCurrent = await apiAtCurrent.query[bridgedChain.grandpaPalletName].currentAuthoritySet(); + const authoritySetIdAtCurrent = authoritySetAtCurrent["setId"]; + + // we expect to see no more than `authoritySetIdAtCurrent - authoritySetIdAtParent` new GRANDPA headers + const maxNewGrandpaHeaders = authoritySetIdAtCurrent - authoritySetIdAtParent; + const newGrandpaHeaders = module.exports.countGrandpaHeaderImports(bridgedChain, currentEvents); + + // check that our assumptions are correct + if (newGrandpaHeaders > maxNewGrandpaHeaders) { + module.exports.logEvents(currentEvents); + throw new Error("Unexpected relay chain header import: " + newGrandpaHeaders + " / " + maxNewGrandpaHeaders); + } + + return newGrandpaHeaders; + }, + ensureOnlyInitialParachainHeaderImported: async function( + bridgedChain, + apiAtParent, + apiAtCurrent, + currentEvents, + ) { + // remember whether we already know bridged parachain header at a parent block + const bestBridgedParachainHeader = await apiAtParent.query[bridgedChain.parachainsPalletName].parasInfo(bridgedChain.bridgedBridgeHubParaId);; + const hasBestBridgedParachainHeader = bestBridgedParachainHeader.isSome; + + // we expect to see: no more than `1` bridged parachain header if there were no parachain header before. + const maxNewParachainHeaders = hasBestBridgedParachainHeader ? 0 : 1; + const newParachainHeaders = module.exports.countParachainHeaderImports(bridgedChain, currentEvents); + + // check that our assumptions are correct + if (newParachainHeaders > maxNewParachainHeaders) { + module.exports.logEvents(currentEvents); + throw new Error("Unexpected parachain header import: " + newParachainHeaders + " / " + maxNewParachainHeaders); + } + + return hasBestBridgedParachainHeader; + }, +} diff --git a/bridges/zombienet/helpers/wait-hrmp-channel-opened.js b/bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js similarity index 91% rename from bridges/zombienet/helpers/wait-hrmp-channel-opened.js rename to bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js index e700cab1d7481d77631e55492e4b0032f4382028..765d48cc49848ab7a4389f6e0d9b9b3b8cb38f2b 100644 --- a/bridges/zombienet/helpers/wait-hrmp-channel-opened.js +++ b/bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js @@ -15,7 +15,7 @@ async function run(nodeName, networkInfo, args) { } // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 12000)); + await new Promise((resolve) => setTimeout(resolve, 6000)); } } diff --git a/bridges/zombienet/helpers/wrapped-assets-balance.js b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js similarity index 93% rename from bridges/zombienet/helpers/wrapped-assets-balance.js rename to bridges/testing/framework/js-helpers/wrapped-assets-balance.js index bb3cea8858a850e551ba0380b1557ccad0761717..27287118547f702b3e94eb635f9e3855d1cab535 100644 --- a/bridges/zombienet/helpers/wrapped-assets-balance.js +++ b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js @@ -19,7 +19,7 @@ async function run(nodeName, networkInfo, args) { } // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 12000)); + await new Promise((resolve) => setTimeout(resolve, 6000)); } } diff --git a/cumulus/scripts/bridges_common.sh b/bridges/testing/framework/utils/bridges.sh similarity index 92% rename from cumulus/scripts/bridges_common.sh rename to bridges/testing/framework/utils/bridges.sh index 5d5b7b7a482a1ec5dbe6a9db99e333c8b9029ebb..7c8399461584a85e4e8eedf5f347d9d74725f1c9 100755 --- a/cumulus/scripts/bridges_common.sh +++ b/bridges/testing/framework/utils/bridges.sh @@ -2,7 +2,7 @@ function relayer_path() { local default_path=~/local_bridge_testing/bin/substrate-relay - local path="${SUBSTRATE_RELAY_PATH:-$default_path}" + local path="${SUBSTRATE_RELAY_BINARY:-$default_path}" echo "$path" } @@ -41,13 +41,21 @@ function ensure_polkadot_js_api() { echo "" echo "" echo "-------------------" - echo "Installing (nodejs) sub module: $(dirname "$0")/generate_hex_encoded_call" - pushd $(dirname "$0")/generate_hex_encoded_call + echo "Installing (nodejs) sub module: ${BASH_SOURCE%/*}/generate_hex_encoded_call" + pushd ${BASH_SOURCE%/*}/generate_hex_encoded_call npm install popd fi } +function call_polkadot_js_api() { + # --noWait: without that argument `polkadot-js-api` waits until transaction is included into the block. + # With it, it just submits it to the tx pool and exits. + # --nonce -1: means to compute transaction nonce using `system_accountNextIndex` RPC, which includes all + # transaction that are in the tx pool. + polkadot-js-api --noWait --nonce -1 "$@" +} + function generate_hex_encoded_call_data() { local type=$1 local endpoint=$2 @@ -57,7 +65,7 @@ function generate_hex_encoded_call_data() { shift echo "Input params: $@" - node $(dirname "$0")/generate_hex_encoded_call "$type" "$endpoint" "$output" "$@" + node ${BASH_SOURCE%/*}/../utils/generate_hex_encoded_call "$type" "$endpoint" "$output" "$@" local retVal=$? if [ $type != "check" ]; then @@ -80,7 +88,7 @@ function transfer_balance() { echo " amount: ${amount}" echo "--------------------------------------------------" - polkadot-js-api \ + call_polkadot_js_api \ --ws "${runtime_para_endpoint}" \ --seed "${seed?}" \ tx.balances.transferAllowDeath \ @@ -145,7 +153,7 @@ function send_governance_transact() { echo "" echo "--------------------------------------------------" - polkadot-js-api \ + call_polkadot_js_api \ --ws "${relay_url?}" \ --seed "${relay_chain_seed?}" \ --sudo \ @@ -170,7 +178,7 @@ function open_hrmp_channels() { echo " max_message_size: ${max_message_size}" echo " params:" echo "--------------------------------------------------" - polkadot-js-api \ + call_polkadot_js_api \ --ws "${relay_url?}" \ --seed "${relay_chain_seed?}" \ --sudo \ @@ -254,7 +262,7 @@ function limited_reserve_transfer_assets() { echo "" echo "--------------------------------------------------" - polkadot-js-api \ + call_polkadot_js_api \ --ws "${url?}" \ --seed "${seed?}" \ tx.polkadotXcm.limitedReserveTransferAssets \ @@ -293,7 +301,7 @@ function claim_rewards() { echo "${rewards_account_params}" echo "--------------------------------------------------" - polkadot-js-api \ + call_polkadot_js_api \ --ws "${runtime_para_endpoint}" \ --seed "${seed?}" \ tx.bridgeRelayers.claimRewards \ diff --git a/bridges/testing/framework/utils/common.sh b/bridges/testing/framework/utils/common.sh new file mode 100644 index 0000000000000000000000000000000000000000..06f41320be1353720fccc76b7b76e69ba56a3b94 --- /dev/null +++ b/bridges/testing/framework/utils/common.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +function start_background_process() { + local command=$1 + local log_file=$2 + local __pid=$3 + + $command > $log_file 2>&1 & + eval $__pid="'$!'" +} + +function wait_for_process_file() { + local pid=$1 + local file=$2 + local timeout=$3 + local __found=$4 + + local time=0 + until [ -e $file ]; do + if ! kill -0 $pid; then + echo "Process finished unsuccessfully" + return + fi + if (( time++ >= timeout )); then + echo "Timeout waiting for file $file: $timeout seconds" + eval $__found=0 + return + fi + sleep 1 + done + + echo "File $file found after $time seconds" + eval $__found=1 +} + +function ensure_process_file() { + local pid=$1 + local file=$2 + local timeout=$3 + + wait_for_process_file $pid $file $timeout file_found + if [ "$file_found" != "1" ]; then + exit 1 + fi +} diff --git a/cumulus/scripts/generate_hex_encoded_call/index.js b/bridges/testing/framework/utils/generate_hex_encoded_call/index.js similarity index 100% rename from cumulus/scripts/generate_hex_encoded_call/index.js rename to bridges/testing/framework/utils/generate_hex_encoded_call/index.js diff --git a/cumulus/scripts/generate_hex_encoded_call/package-lock.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json similarity index 100% rename from cumulus/scripts/generate_hex_encoded_call/package-lock.json rename to bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json diff --git a/cumulus/scripts/generate_hex_encoded_call/package.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package.json similarity index 100% rename from cumulus/scripts/generate_hex_encoded_call/package.json rename to bridges/testing/framework/utils/generate_hex_encoded_call/package.json diff --git a/bridges/testing/framework/utils/zombienet.sh b/bridges/testing/framework/utils/zombienet.sh new file mode 100644 index 0000000000000000000000000000000000000000..bbcd1a30620252d8740473c3924e0988e5bff4d6 --- /dev/null +++ b/bridges/testing/framework/utils/zombienet.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +source "${BASH_SOURCE%/*}/common.sh" + +function start_zombienet() { + local test_dir=$1 + local definition_path=$2 + local __zombienet_dir=$3 + local __zombienet_pid=$4 + + local zombienet_name=`basename $definition_path .toml` + local zombienet_dir=$test_dir/$zombienet_name + eval $__zombienet_dir="'$zombienet_dir'" + mkdir -p $zombienet_dir + rm -rf $zombienet_dir + + local logs_dir=$test_dir/logs + mkdir -p $logs_dir + local zombienet_log=$logs_dir/$zombienet_name.log + + echo "Starting $zombienet_name zombienet. Logs available at: $zombienet_log" + start_background_process \ + "$ZOMBIENET_BINARY spawn --dir $zombienet_dir --provider native $definition_path" \ + "$zombienet_log" zombienet_pid + + ensure_process_file $zombienet_pid "$zombienet_dir/zombie.json" 180 + echo "$zombienet_name zombienet started successfully" + + eval $__zombienet_pid="'$zombienet_pid'" +} + +function run_zndsl() { + local zndsl_file=$1 + local zombienet_dir=$2 + + echo "Running $zndsl_file." + $ZOMBIENET_BINARY test --dir $zombienet_dir --provider native $zndsl_file $zombienet_dir/zombie.json + echo +} diff --git a/bridges/testing/run-new-test.sh b/bridges/testing/run-new-test.sh new file mode 100755 index 0000000000000000000000000000000000000000..7c84a69aa47de84439091cb7b908233d02238175 --- /dev/null +++ b/bridges/testing/run-new-test.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e + +trap 'kill -9 -$$ || echo "Environment already teared down"' SIGINT SIGTERM EXIT + +test=$1 +shift + +# 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 + ;; + esac + shift +done + +export POLKADOT_SDK_PATH=`realpath ${BASH_SOURCE%/*}/../..` +export FRAMEWORK_PATH=`realpath ${BASH_SOURCE%/*}/framework` + +# set path to binaries +if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then + # otherwise zombienet uses some hardcoded paths + unset RUN_IN_CONTAINER + unset ZOMBIENET_IMAGE + + export POLKADOT_BINARY=/usr/local/bin/polkadot + export POLKADOT_PARACHAIN_BINARY=/usr/local/bin/polkadot-parachain + + export ZOMBIENET_BINARY=/usr/local/bin/zombie + export SUBSTRATE_RELAY_BINARY=/usr/local/bin/substrate-relay +else + export POLKADOT_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot + export POLKADOT_PARACHAIN_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot-parachain + + export ZOMBIENET_BINARY=~/local_bridge_testing/bin/zombienet-linux-x64 + export SUBSTRATE_RELAY_BINARY=~/local_bridge_testing/bin/substrate-relay +fi + +export TEST_DIR=`mktemp -d /tmp/bridges-tests-run-XXXXX` +echo -e "Test folder: $TEST_DIR\n" + +${BASH_SOURCE%/*}/tests/$test/run.sh diff --git a/bridges/zombienet/run-tests.sh b/bridges/testing/run-tests.sh similarity index 63% rename from bridges/zombienet/run-tests.sh rename to bridges/testing/run-tests.sh index 34487e13261f375d0072817d667212d21b31f498..6149d9912653c79968a0229759c8f1bf46f68a9f 100755 --- a/bridges/zombienet/run-tests.sh +++ b/bridges/testing/run-tests.sh @@ -1,10 +1,12 @@ #!/bin/bash -#set -eu set -x shopt -s nullglob -trap "trap - SIGINT SIGTERM EXIT && kill -- -$$" SIGINT SIGTERM EXIT +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 ] @@ -14,40 +16,38 @@ do --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_FOLDER=`realpath $(dirname "$0")/../..` -export BRIDGE_TESTS_FOLDER=$POLKADOT_SDK_FOLDER/bridges/zombienet/tests +export POLKADOT_SDK_PATH=`realpath $(dirname "$0")/../..` +export BRIDGE_TESTS_FOLDER=$POLKADOT_SDK_PATH/bridges/testing/tests # set pathc to binaries if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then - export POLKADOT_BINARY_PATH=/usr/local/bin/polkadot - export POLKADOT_PARACHAIN_BINARY_PATH=/usr/local/bin/polkadot-parachain - export POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_ROCOCO=/usr/local/bin/polkadot-parachain - export POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_WESTEND=/usr/local/bin/polkadot-parachain + export POLKADOT_BINARY=/usr/local/bin/polkadot + export POLKADOT_PARACHAIN_BINARY=/usr/local/bin/polkadot-parachain - export SUBSTRATE_RELAY_PATH=/usr/local/bin/substrate-relay + export SUBSTRATE_RELAY_BINARY=/usr/local/bin/substrate-relay export ZOMBIENET_BINARY_PATH=/usr/local/bin/zombie else - export POLKADOT_BINARY_PATH=$POLKADOT_SDK_FOLDER/target/release/polkadot - export POLKADOT_PARACHAIN_BINARY_PATH=$POLKADOT_SDK_FOLDER/target/release/polkadot-parachain - export POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_ROCOCO=$POLKADOT_PARACHAIN_BINARY_PATH - export POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_WESTEND=$POLKADOT_PARACHAIN_BINARY_PATH + export POLKADOT_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot + export POLKADOT_PARACHAIN_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot-parachain - export SUBSTRATE_RELAY_PATH=~/local_bridge_testing/bin/substrate-relay + 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 -# 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" @@ -57,7 +57,8 @@ ALL_TESTS_FOLDER=`mktemp -d /tmp/bridges-zombienet-tests.XXXXX` function start_coproc() { local command=$1 local name=$2 - local coproc_log=`mktemp -p $TEST_FOLDER` + 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 @@ -73,7 +74,7 @@ function start_coproc() { } # execute every test from tests folder -TEST_INDEX=1 +TEST_INDEX=$TESTS_BEGIN while true do declare -A TEST_COPROCS @@ -81,7 +82,7 @@ do 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` + 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) @@ -89,12 +90,6 @@ do break fi - # start relay - if [ -f $BRIDGE_TESTS_FOLDER/$TEST_PREFIX-start-relay.sh ]; then - start_coproc "${BRIDGE_TESTS_FOLDER}/${TEST_PREFIX}-start-relay.sh" "relay" - RELAY_COPROC=$COPROC_PID - ((TEST_COPROCS_COUNT++)) - fi # start tests for zndsl_file in "${zndsl_files[@]}"; do start_coproc "$ZOMBIENET_BINARY_PATH --provider native test $zndsl_file" "$zndsl_file" @@ -102,7 +97,6 @@ do ((TEST_COPROCS_COUNT++)) done # wait until all tests are completed - relay_exited=0 for n in `seq 1 $TEST_COPROCS_COUNT`; do if [ "$IS_BASH_5_1" -eq 1 ]; then wait -n -p COPROC_PID @@ -110,7 +104,6 @@ do coproc_name=${TEST_COPROCS[$COPROC_PID, 0]} coproc_log=${TEST_COPROCS[$COPROC_PID, 1]} coproc_stdout=$(cat $coproc_log) - relay_exited=$(expr "${coproc_name}" == "relay") else wait -n exit_code=$? @@ -124,18 +117,20 @@ do echo "=====================================================================" echo "=== Shutting down. Log of failed process below ===" echo "=====================================================================" - echo $coproc_stdout + echo "$coproc_stdout" exit 1 fi - - # if last test has exited, exit relay too - if [ $n -eq $(($TEST_COPROCS_COUNT - 1)) ] && [ $relay_exited -eq 0 ]; then - kill $RELAY_COPROC - break - 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 "=====================================================================" diff --git a/bridges/testing/scripts/invoke-script.sh b/bridges/testing/scripts/invoke-script.sh new file mode 100755 index 0000000000000000000000000000000000000000..cd0557b071bbadc41e056a2e50c9f1aa0b677312 --- /dev/null +++ b/bridges/testing/scripts/invoke-script.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +INVOKE_LOG=`mktemp -p $TEST_FOLDER invoke.XXXXX` + +pushd $POLKADOT_SDK_PATH/bridges/testing/environments/rococo-westend +./bridges_rococo_westend.sh $1 >$INVOKE_LOG 2>&1 +popd diff --git a/bridges/testing/scripts/start-relayer.sh b/bridges/testing/scripts/start-relayer.sh new file mode 100755 index 0000000000000000000000000000000000000000..38ea62fad524486c40cf88943c48a2e4df4b86e8 --- /dev/null +++ b/bridges/testing/scripts/start-relayer.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +RELAY_LOG=`mktemp -p $TEST_FOLDER relay.XXXXX` + +pushd $POLKADOT_SDK_PATH/bridges/testing/environments/rococo-westend +./bridges_rococo_westend.sh run-relay >$RELAY_LOG 2>&1& +popd diff --git a/bridges/zombienet/scripts/sync-exit.sh b/bridges/testing/scripts/sync-exit.sh similarity index 100% rename from bridges/zombienet/scripts/sync-exit.sh rename to bridges/testing/scripts/sync-exit.sh diff --git a/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl b/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..a58520ccea65b50dd0db1f67a72f6f8a4c5cdb38 --- /dev/null +++ b/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl @@ -0,0 +1,12 @@ +Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml +Creds: config + +# send 5 ROC to //Alice from Rococo AH to Westend AH +asset-hub-westend-collator1: run {{ENV_PATH}}/helper.sh with "reserve-transfer-assets-from-asset-hub-rococo-local 5000000000000" within 120 seconds + +# check that //Alice received at least 4.8 ROC on Westend AH +asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,4800000000000,Rococo" within 300 seconds + +# check that the relayer //Charlie is rewarded by Westend AH +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x6268726F,ThisChain,0" within 30 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/run.sh b/bridges/testing/tests/0001-asset-transfer/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..a7bb122919b40187c49e89c489d2271d646bff40 --- /dev/null +++ b/bridges/testing/tests/0001-asset-transfer/run.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e + +source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" + +export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` + +$ENV_PATH/spawn.sh --init --start-relayer & +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 + +run_zndsl ${BASH_SOURCE%/*}/roc-reaches-westend.zndsl $westend_dir +run_zndsl ${BASH_SOURCE%/*}/wnd-reaches-rococo.zndsl $rococo_dir + +run_zndsl ${BASH_SOURCE%/*}/wroc-reaches-rococo.zndsl $rococo_dir +run_zndsl ${BASH_SOURCE%/*}/wwnd-reaches-westend.zndsl $westend_dir diff --git a/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl b/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..fedb78cc2103555a1d15c446dd2f08fca94643e1 --- /dev/null +++ b/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl @@ -0,0 +1,12 @@ +Description: User is able to transfer WND from Westend Asset Hub to Rococo Asset Hub and back +Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml +Creds: config + +# send 5 WND to //Alice from Westend AH to Rococo AH +asset-hub-rococo-collator1: run {{ENV_PATH}}/helper.sh with "reserve-transfer-assets-from-asset-hub-westend-local 5000000000000" within 120 seconds + +# check that //Alice received at least 4.8 WND on Rococo AH +asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,4800000000000,Westend" within 300 seconds + +# check that the relayer //Charlie is rewarded by Rococo AH +bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x62687764,ThisChain,0" within 30 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl b/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..68b888b6858e86b8fe846b887bc101e221b2f21d --- /dev/null +++ b/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl @@ -0,0 +1,10 @@ +Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml +Creds: config + +# send 3 wROC back to Alice from Westend AH to Rococo AH +asset-hub-rococo-collator1: run {{ENV_PATH}}/helper.sh with "withdraw-reserve-assets-from-asset-hub-westend-local 3000000000000" within 120 seconds + +# check that //Alice received at least 2.8 wROC on Rococo AH +# (we wait until //Alice account increases here - there are no other transactions that may increase it) +asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,2800000000000" within 300 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl b/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..1a8a161819542e281094aed0681d52167aaea8e6 --- /dev/null +++ b/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl @@ -0,0 +1,10 @@ +Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml +Creds: config + +# send 3 wWND back to Alice from Rococo AH to Westend AH +asset-hub-westend-collator1: run {{ENV_PATH}}/helper.sh with "withdraw-reserve-assets-from-asset-hub-rococo-local 3000000000000" within 120 seconds + +# check that //Alice received at least 2.8 wWND on Westend AH +# (we wait until //Alice account increases here - there are no other transactions that may increase it) +asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,2800000000000" within 300 seconds diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..6e381f5377329430c0d7a8723f9ea9081556bfeb --- /dev/null +++ b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl @@ -0,0 +1,8 @@ +Description: While relayer is idle, we only sync mandatory Rococo (and a single Rococo BH) headers to Westend BH. +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml +Creds: config + +# ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were +# generated while relay was offline and those in the next 100 seconds while script is active. +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,rococo-at-westend" within 600 seconds + diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..7d5b8d9273664b0861e8ffe1c528e9e1718c4df4 --- /dev/null +++ b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e + +source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" + +export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` + +$ENV_PATH/spawn.sh & +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 + +# Sleep for some time before starting the relayer. We want to sleep for at least 1 session, +# which is expected to be 60 seconds for the test environment. +echo -e "Sleeping 90s before starting relayer ...\n" +sleep 90 +${BASH_SOURCE%/*}/../../environments/rococo-westend/start_relayer.sh $rococo_dir $westend_dir relayer_pid + +# Sometimes the relayer syncs multiple parachain heads in the begining leading to test failures. +# See issue: https://github.com/paritytech/parity-bridges-common/issues/2838. +# TODO: Remove this sleep after the issue is fixed. +echo -e "Sleeping 180s before runing the tests ...\n" +sleep 180 + +run_zndsl ${BASH_SOURCE%/*}/rococo-to-westend.zndsl $westend_dir +run_zndsl ${BASH_SOURCE%/*}/westend-to-rococo.zndsl $rococo_dir + diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..b4b3e43679162feb8c3c5253f3f963d950f31d55 --- /dev/null +++ b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl @@ -0,0 +1,7 @@ +Description: While relayer is idle, we only sync mandatory Westend (and a single Westend BH) headers to Rococo BH. +Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml +Creds: config + +# ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were +# generated while relay was offline and those in the next 100 seconds while script is active. +bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,westend-at-rococo" 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 0000000000000000000000000000000000000000..07b91481dc7cf995b913a9bf84edd3728982eaae --- /dev/null +++ b/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl @@ -0,0 +1,26 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..a6b11fc24052aadf562bc34704aeda9ee115eccf --- /dev/null +++ b/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl @@ -0,0 +1,26 @@ +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/zombienet/scripts/invoke-script.sh b/bridges/zombienet/scripts/invoke-script.sh deleted file mode 100755 index 6a3754a8824017e18409cde031be9a09e9392a75..0000000000000000000000000000000000000000 --- a/bridges/zombienet/scripts/invoke-script.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -pushd $POLKADOT_SDK_FOLDER/cumulus/scripts -./bridges_rococo_westend.sh $1 -popd diff --git a/bridges/zombienet/tests/0001-asset-transfer-works-rococo-to-westend.zndsl b/bridges/zombienet/tests/0001-asset-transfer-works-rococo-to-westend.zndsl deleted file mode 100644 index fe7dc26b001125342f449911c8808b9b9dcf5775..0000000000000000000000000000000000000000 --- a/bridges/zombienet/tests/0001-asset-transfer-works-rococo-to-westend.zndsl +++ /dev/null @@ -1,34 +0,0 @@ -Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back -Network: ../../../cumulus/zombienet/bridge-hubs/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 240 seconds -asset-hub-westend-collator1: js-script ../helpers/wait-hrmp-channel-opened.js with "1002" within 400 seconds - -# step 2: initialize Westend bridge hub -bridge-hub-westend-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-westend-local" within 120 seconds - -# step 3: relay is started elsewhere - let's wait until with-Rococo GRANPDA pallet is initialized at Westend -bridge-hub-westend-collator1: js-script ../helpers/best-finalized-header-at-bridged-chain.js with "Rococo,0" within 400 seconds - -# step 4: send WND to //Alice on Rococo AH -# (that's a required part of a sibling 0001-asset-transfer-works-westend-to-rococo.zndsl test) -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-westend-local" within 120 seconds - -# step 5: elsewhere Rococo has sent ROC to //Alice - let's wait for it -asset-hub-westend-collator1: js-script ../helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,0,Rococo" within 600 seconds - -# step 6: check that the relayer //Charlie is rewarded by both our AH and target AH -bridge-hub-westend-collator1: js-script ../helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x6268726f,BridgedChain,0" within 300 seconds -bridge-hub-westend-collator1: js-script ../helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x6268726F,ThisChain,0" within 300 seconds - -# step 7: send wROC back to Alice at Rococo AH -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "withdraw-reserve-assets-from-asset-hub-westend-local" within 120 seconds - -# step 8: elsewhere Rococo has sent wWND to //Alice - let's wait for it -# (we wait until //Alice account increases here - there are no other transactionc that may increase it) -asset-hub-westend-collator1: js-script ../helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" 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/zombienet/tests/0001-asset-transfer-works-westend-to-rococo.zndsl b/bridges/zombienet/tests/0001-asset-transfer-works-westend-to-rococo.zndsl deleted file mode 100644 index 610b4ca7acdc372323f8781478722c0b9613d162..0000000000000000000000000000000000000000 --- a/bridges/zombienet/tests/0001-asset-transfer-works-westend-to-rococo.zndsl +++ /dev/null @@ -1,34 +0,0 @@ -Description: User is able to transfer WND from Westend Asset Hub to Rococo Asset Hub and back -Network: ../../../cumulus/zombienet/bridge-hubs/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 240 seconds -asset-hub-rococo-collator1: js-script ../helpers/wait-hrmp-channel-opened.js with "1013" within 400 seconds - -# step 2: initialize Rococo bridge hub -bridge-hub-rococo-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-rococo-local" within 120 seconds - -# step 3: relay is started elsewhere - let's wait until with-Westend GRANPDA pallet is initialized at Rococo -bridge-hub-rococo-collator1: js-script ../helpers/best-finalized-header-at-bridged-chain.js with "Westend,0" within 400 seconds - -# step 4: send ROC to //Alice on Westend AH -# (that's a required part of a sibling 0001-asset-transfer-works-rococo-to-westend.zndsl test) -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-rococo-local" within 120 seconds - -# step 5: elsewhere Westend has sent WND to //Alice - let's wait for it -asset-hub-rococo-collator1: js-script ../helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,0,Westend" within 600 seconds - -# step 6: check that the relayer //Charlie is rewarded by both our AH and target AH -bridge-hub-rococo-collator1: js-script ../helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x62687764,BridgedChain,0" within 300 seconds -bridge-hub-rococo-collator1: js-script ../helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x62687764,ThisChain,0" within 300 seconds - -# step 7: send wWND back to Alice at Westend AH -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "withdraw-reserve-assets-from-asset-hub-rococo-local" within 120 seconds - -# step 8: elsewhere Westend has sent wROC to //Alice - let's wait for it -# (we wait until //Alice account increases here - there are no other transactionc that may increase it) -asset-hub-rococo-collator1: js-script ../helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" 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/zombienet/tests/0001-start-relay.sh b/bridges/zombienet/tests/0001-start-relay.sh deleted file mode 100755 index 7be2cf4d5938797b98b86e8abf08ae43a5cee449..0000000000000000000000000000000000000000 --- a/bridges/zombienet/tests/0001-start-relay.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -pushd $POLKADOT_SDK_FOLDER/cumulus/scripts -./bridges_rococo_westend.sh run-relay -popd diff --git a/cumulus/client/cli/Cargo.toml b/cumulus/client/cli/Cargo.toml index 96016da5c9a68c26fc38c1bfd0fd1d4632151bfc..eaf0d5d5d7f78e578644bf35f83d9543ad9af4bd 100644 --- a/cumulus/client/cli/Cargo.toml +++ b/cumulus/client/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-client-cli" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Parachain node CLI utilities." @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" workspace = true [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } url = "2.4.0" diff --git a/cumulus/client/cli/src/lib.rs b/cumulus/client/cli/src/lib.rs index 1807b8a1718e8b5c800b3bf27b58e0f39cd2948a..a7b2eb19de88a5c585ec3f6dfe5ad46ef0399b88 100644 --- a/cumulus/client/cli/src/lib.rs +++ b/cumulus/client/cli/src/lib.rs @@ -30,7 +30,7 @@ use codec::Encode; use sc_chain_spec::ChainSpec; use sc_client_api::HeaderBackend; use sc_service::{ - config::{PrometheusConfig, TelemetryEndpoints}, + config::{PrometheusConfig, RpcBatchRequestConfig, TelemetryEndpoints}, BasePath, TransactionPoolOptions, }; use sp_core::hexdisplay::HexDisplay; @@ -443,6 +443,14 @@ impl sc_cli::CliConfiguration for NormalizedRunCmd { Ok(self.base.rpc_max_subscriptions_per_connection) } + fn rpc_buffer_capacity_per_connection(&self) -> sc_cli::Result { + Ok(self.base.rpc_message_buffer_capacity_per_connection) + } + + fn rpc_batch_config(&self) -> sc_cli::Result { + self.base.rpc_batch_config() + } + fn transaction_pool(&self, is_dev: bool) -> sc_cli::Result { self.base.transaction_pool(is_dev) } diff --git a/cumulus/client/collator/Cargo.toml b/cumulus/client/collator/Cargo.toml index 5aa260eae1b4c34c158194bbcb299b95448e7a76..0e911b9f3abfec1a894b50d63620f914268b78a9 100644 --- a/cumulus/client/collator/Cargo.toml +++ b/cumulus/client/collator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-client-collator" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Common node-side functionality and glue code to collate parachain blocks." diff --git a/cumulus/client/consensus/aura/Cargo.toml b/cumulus/client/consensus/aura/Cargo.toml index d022fe4df7e5a89725af4d6c9201cedc3777ebe4..e815e89d8ce3bebcdcf52b363917ba17c0382708 100644 --- a/cumulus/client/consensus/aura/Cargo.toml +++ b/cumulus/client/consensus/aura/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cumulus-client-consensus-aura" description = "AURA consensus algorithm for parachains" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/cumulus/client/consensus/aura/src/collator.rs b/cumulus/client/consensus/aura/src/collator.rs index db0799235bca27aaa4456da6c8649b0b76fef030..5b7669c88f473b8765b6b343d1797aa707ed5916 100644 --- a/cumulus/client/consensus/aura/src/collator.rs +++ b/cumulus/client/consensus/aura/src/collator.rs @@ -258,6 +258,7 @@ where pub struct SlotClaim { author_pub: Pub, pre_digest: DigestItem, + slot: Slot, timestamp: Timestamp, } @@ -272,7 +273,7 @@ impl SlotClaim { P::Public: Codec, P::Signature: Codec, { - SlotClaim { author_pub, timestamp, pre_digest: aura_internal::pre_digest::

(slot) } + SlotClaim { author_pub, timestamp, pre_digest: aura_internal::pre_digest::

(slot), slot } } /// Get the author's public key. @@ -285,6 +286,11 @@ impl SlotClaim { &self.pre_digest } + /// Get the slot assigned to this claim. + pub fn slot(&self) -> Slot { + self.slot + } + /// Get the timestamp corresponding to the relay-chain slot this claim was /// generated against. pub fn timestamp(&self) -> Timestamp { diff --git a/cumulus/client/consensus/aura/src/collators/basic.rs b/cumulus/client/consensus/aura/src/collators/basic.rs index 78f6b726aff0cb63cd08259c327bfbda71c05b8b..52b83254951f0e0ba0fd9ad5420d7faca2402066 100644 --- a/cumulus/client/consensus/aura/src/collators/basic.rs +++ b/cumulus/client/consensus/aura/src/collators/basic.rs @@ -33,12 +33,12 @@ use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_node_primitives::CollationResult; use polkadot_overseer::Handle as OverseerHandle; -use polkadot_primitives::{CollatorPair, Id as ParaId}; +use polkadot_primitives::{CollatorPair, Id as ParaId, ValidationCode}; use futures::{channel::mpsc::Receiver, prelude::*}; use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf}; use sc_consensus::BlockImport; -use sp_api::ProvideRuntimeApi; +use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_application_crypto::AppPublic; use sp_blockchain::HeaderBackend; use sp_consensus::SyncOracle; @@ -47,6 +47,7 @@ use sp_core::crypto::Pair; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member}; +use sp_state_machine::Backend as _; use std::{convert::TryFrom, sync::Arc, time::Duration}; use crate::collator as collator_util; @@ -100,6 +101,7 @@ where + AuxStore + HeaderBackend + BlockBackend + + CallApiAt + Send + Sync + 'static, @@ -141,6 +143,8 @@ where collator_util::Collator::::new(params) }; + let mut last_processed_slot = 0; + while let Some(request) = collation_requests.next().await { macro_rules! reject_with_error { ($err:expr) => {{ @@ -170,6 +174,22 @@ where continue } + let Ok(Some(code)) = + params.para_client.state_at(parent_hash).map_err(drop).and_then(|s| { + s.storage(&sp_core::storage::well_known_keys::CODE).map_err(drop) + }) + else { + continue; + }; + + super::check_validation_code_or_log( + &ValidationCode::from(code).hash(), + params.para_id, + ¶ms.relay_client, + *request.relay_parent(), + ) + .await; + let relay_parent_header = match params.relay_client.header(RBlockId::hash(*request.relay_parent())).await { Err(e) => reject_with_error!(e), @@ -192,6 +212,18 @@ where Err(e) => reject_with_error!(e), }; + // With async backing this function will be called every relay chain block. + // + // 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. + // + // 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() { + continue + } + let (parachain_inherent_data, other_inherent_data) = try_request!( collator .create_inherent_data( @@ -228,6 +260,8 @@ where request.complete(None); tracing::debug!(target: crate::LOG_TARGET, "No block proposal"); } + + last_processed_slot = *claim.slot(); } } } diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index e24b7f6f1c93b9bbe92cdf9ce5958194065862ae..161f10d55a193de35a2585e1a1f5725f30e19bf7 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -59,7 +59,7 @@ use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppPublic; use sp_blockchain::HeaderBackend; use sp_consensus::SyncOracle; -use sp_consensus_aura::{AuraApi, Slot, SlotDuration}; +use sp_consensus_aura::{AuraApi, Slot}; use sp_core::crypto::Pair; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; @@ -95,8 +95,6 @@ pub struct Params { pub para_id: ParaId, /// A handle to the relay-chain client's "Overseer" or task orchestrator. pub overseer_handle: OverseerHandle, - /// The length of slots in this chain. - pub slot_duration: SlotDuration, /// The length of slots in the relay chain. pub relay_chain_slot_duration: Duration, /// The underlying block proposer this should call into. @@ -214,26 +212,6 @@ where }, }; - let (slot_now, timestamp) = match consensus_common::relay_slot_and_timestamp( - &relay_parent_header, - params.relay_chain_slot_duration, - ) { - None => continue, - Some((r_s, t)) => { - let our_slot = Slot::from_timestamp(t, params.slot_duration); - tracing::debug!( - target: crate::LOG_TARGET, - relay_slot = ?r_s, - para_slot = ?our_slot, - timestamp = ?t, - slot_duration = ?params.slot_duration, - relay_chain_slot_duration = ?params.relay_chain_slot_duration, - "Adjusted relay-chain slot to parachain slot" - ); - (our_slot, t) - }, - }; - let parent_search_params = ParentSearchParams { relay_parent, para_id: params.para_id, @@ -272,14 +250,39 @@ where let para_client = &*params.para_client; let keystore = ¶ms.keystore; let can_build_upon = |block_hash| { - can_build_upon::<_, _, P>( + let slot_duration = match sc_consensus_aura::standalone::slot_duration_at( + &*params.para_client, + block_hash, + ) { + Ok(sd) => sd, + Err(err) => { + tracing::error!(target: crate::LOG_TARGET, ?err, "Failed to acquire parachain slot duration"); + return None + }, + }; + tracing::debug!(target: crate::LOG_TARGET, ?slot_duration, ?block_hash, "Parachain slot duration acquired"); + let (relay_slot, timestamp) = consensus_common::relay_slot_and_timestamp( + &relay_parent_header, + params.relay_chain_slot_duration, + )?; + let slot_now = Slot::from_timestamp(timestamp, slot_duration); + tracing::debug!( + target: crate::LOG_TARGET, + ?relay_slot, + para_slot = ?slot_now, + ?timestamp, + ?slot_duration, + relay_chain_slot_duration = ?params.relay_chain_slot_duration, + "Adjusted relay-chain slot to parachain slot" + ); + Some(can_build_upon::<_, _, P>( slot_now, timestamp, block_hash, included_block, para_client, &keystore, - ) + )) }; // Sort by depth, ascending, to choose the longest chain. @@ -287,10 +290,7 @@ where // If the longest chain has space, build upon that. Otherwise, don't // build at all. potential_parents.sort_by_key(|a| a.depth); - let initial_parent = match potential_parents.pop() { - None => continue, - Some(p) => p, - }; + let Some(initial_parent) = potential_parents.pop() else { continue }; // Build in a loop until not allowed. Note that the authorities can change // at any block, so we need to re-claim our slot every time. @@ -298,12 +298,19 @@ where let mut parent_header = initial_parent.header; let overseer_handle = &mut params.overseer_handle; + // We mainly call this to inform users at genesis if there is a mismatch with the + // on-chain data. + collator.collator_service().check_block_status(parent_hash, &parent_header); + // This needs to change to support elastic scaling, but for continuously // scheduled chains this ensures that the backlog will grow steadily. for n_built in 0..2 { - let slot_claim = match can_build_upon(parent_hash).await { + let slot_claim = match can_build_upon(parent_hash) { + Some(fut) => match fut.await { + None => break, + Some(c) => c, + }, None => break, - Some(c) => c, }; tracing::debug!( @@ -347,6 +354,14 @@ where Some(v) => v, }; + super::check_validation_code_or_log( + &validation_code_hash, + params.para_id, + ¶ms.relay_client, + relay_parent, + ) + .await; + match collator .collate( &parent_header, diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs index 4c7b759daf736f69de48b586b082a7d01534d7e3..6e0067d0cedb602face8943737f99f3cb1a201a3 100644 --- a/cumulus/client/consensus/aura/src/collators/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/mod.rs @@ -20,5 +20,60 @@ //! included parachain block, as well as the [`lookahead`] collator, which prospectively //! builds on parachain blocks which have not yet been included in the relay chain. +use cumulus_relay_chain_interface::RelayChainInterface; +use polkadot_primitives::{ + Hash as RHash, Id as ParaId, OccupiedCoreAssumption, ValidationCodeHash, +}; + pub mod basic; pub mod lookahead; + +/// Check the `local_validation_code_hash` against the validation code hash in the relay chain +/// state. +/// +/// If the code hashes do not match, it prints a warning. +async fn check_validation_code_or_log( + local_validation_code_hash: &ValidationCodeHash, + para_id: ParaId, + relay_client: &impl RelayChainInterface, + relay_parent: RHash, +) { + let state_validation_code_hash = match relay_client + .validation_code_hash(relay_parent, para_id, OccupiedCoreAssumption::Included) + .await + { + Ok(hash) => hash, + Err(error) => { + tracing::debug!( + target: super::LOG_TARGET, + %error, + ?relay_parent, + %para_id, + "Failed to fetch validation code hash", + ); + return + }, + }; + + match state_validation_code_hash { + Some(state) => + if state != *local_validation_code_hash { + tracing::warn!( + target: super::LOG_TARGET, + %para_id, + ?relay_parent, + ?local_validation_code_hash, + relay_validation_code_hash = ?state, + "Parachain code doesn't match validation code stored in the relay chain state", + ); + }, + None => { + tracing::warn!( + target: super::LOG_TARGET, + %para_id, + ?relay_parent, + "Could not find validation code for parachain in the relay chain state.", + ); + }, + } +} diff --git a/cumulus/client/consensus/aura/src/lib.rs b/cumulus/client/consensus/aura/src/lib.rs index 6ededa7a92c11cb8c313f7da01017eeef256fb06..ed6f5bdd4d6984350c5f59a3753618c3a038f323 100644 --- a/cumulus/client/consensus/aura/src/lib.rs +++ b/cumulus/client/consensus/aura/src/lib.rs @@ -42,12 +42,22 @@ use sp_core::crypto::Pair; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member, NumberFor}; -use std::{convert::TryFrom, marker::PhantomData, sync::Arc}; +use std::{ + convert::TryFrom, + marker::PhantomData, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, +}; mod import_queue; pub use import_queue::{build_verifier, import_queue, BuildVerifierParams, ImportQueueParams}; -pub use sc_consensus_aura::{slot_duration, AuraVerifier, BuildAuraWorkerParams, SlotProportion}; +pub use sc_consensus_aura::{ + slot_duration, standalone::slot_duration_at, AuraVerifier, BuildAuraWorkerParams, + SlotProportion, +}; pub use sc_consensus_slots::InherentDataProviderExt; pub mod collator; @@ -61,6 +71,7 @@ pub struct AuraConsensus { create_inherent_data_providers: Arc, aura_worker: Arc>, slot_duration: SlotDuration, + last_slot_processed: Arc, _phantom: PhantomData, } @@ -70,6 +81,7 @@ impl Clone for AuraConsensus { create_inherent_data_providers: self.create_inherent_data_providers.clone(), aura_worker: self.aura_worker.clone(), slot_duration: self.slot_duration, + last_slot_processed: self.last_slot_processed.clone(), _phantom: PhantomData, } } @@ -156,6 +168,7 @@ where Box::new(AuraConsensus { create_inherent_data_providers: Arc::new(create_inherent_data_providers), aura_worker: Arc::new(Mutex::new(worker)), + last_slot_processed: Default::default(), slot_duration, _phantom: PhantomData, }) @@ -221,6 +234,18 @@ where Some((validation_data.max_pov_size / 2) as usize), ); + // With async backing this function will be called every relay chain block. + // + // 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. + // + // With https://github.com/paritytech/polkadot-sdk/issues/3168 this implementation will be + // obsolete and also the underlying issue will be fixed. + if self.last_slot_processed.fetch_max(*info.slot, Ordering::Relaxed) >= *info.slot { + return None + } + let res = self.aura_worker.lock().await.on_slot(info).await?; Some(ParachainCandidate { block: res.block, proof: res.storage_proof }) diff --git a/cumulus/client/consensus/common/Cargo.toml b/cumulus/client/consensus/common/Cargo.toml index e7fc7a88640e2c1f9576817549fd7cebff62fb9e..5a014b10e35f39b0a5e00ca01da7cfd3ecc50a5f 100644 --- a/cumulus/client/consensus/common/Cargo.toml +++ b/cumulus/client/consensus/common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cumulus-client-consensus-common" description = "Cumulus specific common consensus implementations" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -14,7 +14,7 @@ async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } dyn-clone = "1.0.16" futures = "0.3.28" -log = "0.4.20" +log = { workspace = true, default-features = true } tracing = "0.1.37" # Substrate diff --git a/cumulus/client/consensus/common/src/tests.rs b/cumulus/client/consensus/common/src/tests.rs index 597d1ab2acc2cff42d3230898c1129a7ba63b6f3..bfb95ae388ae3cd31f5035a9c6195631adbb8809 100644 --- a/cumulus/client/consensus/common/src/tests.rs +++ b/cumulus/client/consensus/common/src/tests.rs @@ -136,6 +136,15 @@ impl RelayChainInterface for Relaychain { Ok(Some(PersistedValidationData { parent_head, ..Default::default() })) } + async fn validation_code_hash( + &self, + _: PHash, + _: ParaId, + _: OccupiedCoreAssumption, + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } + async fn candidate_pending_availability( &self, _: PHash, diff --git a/cumulus/client/consensus/proposer/Cargo.toml b/cumulus/client/consensus/proposer/Cargo.toml index 107f466ca1049623c14c009f060deb010c2d8ac5..b37232bb4485d6f5ece63a3c940bd065c1d3f083 100644 --- a/cumulus/client/consensus/proposer/Cargo.toml +++ b/cumulus/client/consensus/proposer/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cumulus-client-consensus-proposer" description = "A Substrate `Proposer` for building parachain blocks" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -12,7 +12,7 @@ workspace = true [dependencies] anyhow = "1.0" async-trait = "0.1.74" -thiserror = "1.0.48" +thiserror = { workspace = true } # Substrate sp-consensus = { path = "../../../../substrate/primitives/consensus/common" } diff --git a/cumulus/client/consensus/relay-chain/Cargo.toml b/cumulus/client/consensus/relay-chain/Cargo.toml index d7702809779db34e0cf47f953e45475e37b674c4..3d06d6b89ef7447b6196b1fc395ed7be54e3ba62 100644 --- a/cumulus/client/consensus/relay-chain/Cargo.toml +++ b/cumulus/client/consensus/relay-chain/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cumulus-client-consensus-relay-chain" description = "The relay-chain provided consensus algorithm" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/cumulus/client/network/Cargo.toml b/cumulus/client/network/Cargo.toml index edd349155fa6b2f6659f445683c7b3605ac14386..995ef606d270ed688af3751016be9c8c37f80305 100644 --- a/cumulus/client/network/Cargo.toml +++ b/cumulus/client/network/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-client-network" -version = "0.1.0" +version = "0.7.0" authors.workspace = true description = "Cumulus-specific networking protocol" edition.workspace = true diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index e03f470753bb6c32c4410a7c694dea8312c74c31..d986635f961c914c9ec6fa970b20170f9f8b9cbc 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -117,6 +117,15 @@ impl RelayChainInterface for DummyRelayChainInterface { })) } + async fn validation_code_hash( + &self, + _: PHash, + _: ParaId, + _: OccupiedCoreAssumption, + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } + async fn candidate_pending_availability( &self, _: PHash, diff --git a/cumulus/client/parachain-inherent/Cargo.toml b/cumulus/client/parachain-inherent/Cargo.toml index b6d477519ecc6bf20747f9286490b43eff6ae9e5..e00f3ba26066c037aca5e11fcb539ae6d95a4c85 100644 --- a/cumulus/client/parachain-inherent/Cargo.toml +++ b/cumulus/client/parachain-inherent/Cargo.toml @@ -15,7 +15,7 @@ tracing = { version = "0.1.37" } # Substrate sc-client-api = { path = "../../../substrate/client/api" } sp-api = { path = "../../../substrate/primitives/api" } -sp-core = { path = "../../../substrate/primitives/core" } +sp-crypto-hashing = { path = "../../../substrate/primitives/crypto/hashing" } sp-inherents = { path = "../../../substrate/primitives/inherents" } sp-runtime = { path = "../../../substrate/primitives/runtime" } sp-state-machine = { path = "../../../substrate/primitives/state-machine" } diff --git a/cumulus/client/parachain-inherent/src/mock.rs b/cumulus/client/parachain-inherent/src/mock.rs index 7af10a661e09f32ce3a2481a8b3a003ec4928266..22691006f93ed264e3e7d37b7b2120ea576e9da3 100644 --- a/cumulus/client/parachain-inherent/src/mock.rs +++ b/cumulus/client/parachain-inherent/src/mock.rs @@ -21,7 +21,7 @@ use cumulus_primitives_core::{ }; use cumulus_primitives_parachain_inherent::MessageQueueChain; use sc_client_api::{Backend, StorageProvider}; -use sp_core::twox_128; +use sp_crypto_hashing::twox_128; use sp_inherents::{InherentData, InherentDataProvider}; use sp_runtime::traits::Block; use std::collections::BTreeMap; diff --git a/cumulus/client/pov-recovery/Cargo.toml b/cumulus/client/pov-recovery/Cargo.toml index ad55e0e9c4b846306abf3101c89fc4d4c35e2d7a..375a57a87c2aa3fb92a6317c756b18c8117736f8 100644 --- a/cumulus/client/pov-recovery/Cargo.toml +++ b/cumulus/client/pov-recovery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-client-pov-recovery" -version = "0.1.0" +version = "0.7.0" authors.workspace = true description = "Cumulus-specific networking protocol" edition.workspace = true diff --git a/cumulus/client/relay-chain-inprocess-interface/Cargo.toml b/cumulus/client/relay-chain-inprocess-interface/Cargo.toml index 7c7edc502d4cf7d779025670cf4b180c31a601b8..aa16230cd8aff0f313311c15269508156e57b544 100644 --- a/cumulus/client/relay-chain-inprocess-interface/Cargo.toml +++ b/cumulus/client/relay-chain-inprocess-interface/Cargo.toml @@ -1,7 +1,7 @@ [package] authors.workspace = true name = "cumulus-relay-chain-inprocess-interface" -version = "0.1.0" +version = "0.7.0" edition.workspace = true description = "Implementation of the RelayChainInterface trait for Polkadot full-nodes." license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index d384c9d9bd22028e835c80870306b2b0bf88121b..6ea02b2e7c1f6d9b5313459890dd2147015359e5 100644 --- a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs +++ b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs @@ -21,7 +21,7 @@ use cumulus_primitives_core::{ relay_chain::{ runtime_api::ParachainHost, Block as PBlock, BlockId, CommittedCandidateReceipt, Hash as PHash, Header as PHeader, InboundHrmpMessage, OccupiedCoreAssumption, SessionIndex, - ValidatorId, + ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -115,6 +115,19 @@ impl RelayChainInterface for RelayChainInProcessInterface { )?) } + async fn validation_code_hash( + &self, + hash: PHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult> { + Ok(self.full_client.runtime_api().validation_code_hash( + hash, + para_id, + occupied_core_assumption, + )?) + } + async fn candidate_pending_availability( &self, hash: PHash, @@ -295,7 +308,7 @@ fn build_polkadot_full_node( workers_path: None, workers_names: None, - overseer_gen: polkadot_service::RealOverseerGen, + overseer_gen: polkadot_service::CollatorOverseerGen, overseer_message_channel_capacity_override: None, malus_finality_delay: None, hwbench, diff --git a/cumulus/client/relay-chain-interface/Cargo.toml b/cumulus/client/relay-chain-interface/Cargo.toml index 5100119a2e49f41e7822f9810589b3813f484328..6e652b892104e929e5e0a5bb7ce8cf33d364e8e6 100644 --- a/cumulus/client/relay-chain-interface/Cargo.toml +++ b/cumulus/client/relay-chain-interface/Cargo.toml @@ -1,7 +1,7 @@ [package] authors.workspace = true name = "cumulus-relay-chain-interface" -version = "0.1.0" +version = "0.7.0" edition.workspace = true description = "Common interface for different relay chain datasources." license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -21,6 +21,6 @@ sc-client-api = { path = "../../../substrate/client/api" } futures = "0.3.28" async-trait = "0.1.74" -thiserror = "1.0.48" -jsonrpsee-core = "0.16.2" +thiserror = { workspace = true } +jsonrpsee-core = "0.22" parity-scale-codec = "3.6.4" diff --git a/cumulus/client/relay-chain-interface/src/lib.rs b/cumulus/client/relay-chain-interface/src/lib.rs index 3dda61635804d47440689872271751e0bba27d87..bb93e6a168c849fa9d41586ad8b9ec0013e6c01f 100644 --- a/cumulus/client/relay-chain-interface/src/lib.rs +++ b/cumulus/client/relay-chain-interface/src/lib.rs @@ -22,7 +22,7 @@ use sc_client_api::StorageProof; use futures::Stream; use async_trait::async_trait; -use jsonrpsee_core::Error as JsonRpcError; +use jsonrpsee_core::ClientError as JsonRpcError; use parity_scale_codec::Error as CodecError; use sp_api::ApiError; @@ -30,7 +30,7 @@ use cumulus_primitives_core::relay_chain::BlockId; pub use cumulus_primitives_core::{ relay_chain::{ CommittedCandidateReceipt, Hash as PHash, Header as PHeader, InboundHrmpMessage, - OccupiedCoreAssumption, SessionIndex, ValidatorId, + OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -194,6 +194,15 @@ pub trait RelayChainInterface: Send + Sync { relay_parent: PHash, relevant_keys: &Vec>, ) -> RelayChainResult; + + /// Returns the validation code hash for the given `para_id` using the given + /// `occupied_core_assumption`. + async fn validation_code_hash( + &self, + relay_parent: PHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult>; } #[async_trait] @@ -301,4 +310,15 @@ where async fn header(&self, block_id: BlockId) -> RelayChainResult> { (**self).header(block_id).await } + + async fn validation_code_hash( + &self, + relay_parent: PHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult> { + (**self) + .validation_code_hash(relay_parent, para_id, occupied_core_assumption) + .await + } } diff --git a/cumulus/client/relay-chain-minimal-node/Cargo.toml b/cumulus/client/relay-chain-minimal-node/Cargo.toml index 45b958998bd8447416d47c56b1aa1d9ef07b90a7..98240c92adab38ea103f14699e1e49f7449b3d24 100644 --- a/cumulus/client/relay-chain-minimal-node/Cargo.toml +++ b/cumulus/client/relay-chain-minimal-node/Cargo.toml @@ -1,7 +1,7 @@ [package] authors.workspace = true name = "cumulus-relay-chain-minimal-node" -version = "0.1.0" +version = "0.7.0" edition.workspace = true description = "Minimal node implementation to be used in tandem with RPC or light-client mode." license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -24,6 +24,7 @@ polkadot-node-collation-generation = { path = "../../../polkadot/node/collation- polkadot-node-core-runtime-api = { path = "../../../polkadot/node/core/runtime-api" } polkadot-node-core-chain-api = { path = "../../../polkadot/node/core/chain-api" } polkadot-node-core-prospective-parachains = { path = "../../../polkadot/node/core/prospective-parachains" } +polkadot-service = { path = "../../../polkadot/node/service" } # substrate deps sc-authority-discovery = { path = "../../../substrate/client/authority-discovery" } diff --git a/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs b/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs index 5f5bf338ef9907756adb1eab3f0541e870677fe5..f01ef8b05ecba77cf6458fdfe80e966b3a67e6a3 100644 --- a/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs +++ b/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs @@ -15,159 +15,29 @@ // along with Polkadot. If not, see . use futures::{select, StreamExt}; -use parking_lot::Mutex; -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; -use polkadot_availability_recovery::AvailabilityRecoverySubsystem; -use polkadot_collator_protocol::{CollatorProtocolSubsystem, ProtocolSide}; -use polkadot_network_bridge::{ - Metrics as NetworkBridgeMetrics, NetworkBridgeRx as NetworkBridgeRxSubsystem, - NetworkBridgeTx as NetworkBridgeTxSubsystem, -}; -use polkadot_node_collation_generation::CollationGenerationSubsystem; -use polkadot_node_core_chain_api::ChainApiSubsystem; -use polkadot_node_core_prospective_parachains::ProspectiveParachainsSubsystem; -use polkadot_node_core_runtime_api::RuntimeApiSubsystem; -use polkadot_node_network_protocol::{ - peer_set::{PeerSet, PeerSetProtocolNames}, - request_response::{ - v1::{self, AvailableDataFetchingRequest}, - v2, IncomingRequestReceiver, ReqProtocolNames, - }, -}; -use polkadot_node_subsystem_util::metrics::{prometheus::Registry, Metrics}; use polkadot_overseer::{ - BlockInfo, DummySubsystem, Handle, Overseer, OverseerConnector, OverseerHandle, SpawnGlue, - UnpinHandle, + BlockInfo, Handle, Overseer, OverseerConnector, OverseerHandle, SpawnGlue, UnpinHandle, }; -use polkadot_primitives::CollatorPair; +use polkadot_service::overseer::{collator_overseer_builder, OverseerGenArgs}; -use sc_authority_discovery::Service as AuthorityDiscoveryService; -use sc_network::{NetworkStateInfo, NotificationService}; use sc_service::TaskManager; use sc_utils::mpsc::tracing_unbounded; -use cumulus_primitives_core::relay_chain::{Block, Hash as PHash}; use cumulus_relay_chain_interface::RelayChainError; use crate::BlockChainRpcClient; -/// Arguments passed for overseer construction. -pub(crate) struct CollatorOverseerGenArgs<'a> { - /// Runtime client generic, providing the `ProvieRuntimeApi` trait besides others. - pub runtime_client: Arc, - /// Underlying network service implementation. - pub network_service: Arc>, - /// Syncing oracle. - pub sync_oracle: Box, - /// Underlying authority discovery service. - pub authority_discovery_service: AuthorityDiscoveryService, - /// Receiver for collation request protocol v1. - pub collation_req_receiver_v1: IncomingRequestReceiver, - /// Receiver for collation request protocol v2. - pub collation_req_receiver_v2: IncomingRequestReceiver, - /// Receiver for availability request protocol - pub available_data_req_receiver: IncomingRequestReceiver, - /// Prometheus registry, commonly used for production systems, less so for test. - pub registry: Option<&'a Registry>, - /// Task spawner to be used throughout the overseer and the APIs it provides. - pub spawner: sc_service::SpawnTaskHandle, - /// Determines the behavior of the collator. - pub collator_pair: CollatorPair, - /// Request response protocols - pub req_protocol_names: ReqProtocolNames, - /// Peerset protocols name mapping - pub peer_set_protocol_names: PeerSetProtocolNames, - /// Notification services for validation/collation protocols. - pub notification_services: HashMap>, -} - fn build_overseer( connector: OverseerConnector, - CollatorOverseerGenArgs { - runtime_client, - network_service, - sync_oracle, - authority_discovery_service, - collation_req_receiver_v1, - collation_req_receiver_v2, - available_data_req_receiver, - registry, - spawner, - collator_pair, - req_protocol_names, - peer_set_protocol_names, - notification_services, - }: CollatorOverseerGenArgs<'_>, + args: OverseerGenArgs, ) -> Result< (Overseer, Arc>, OverseerHandle), RelayChainError, > { - let spawner = SpawnGlue(spawner); - let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; - let notification_sinks = Arc::new(Mutex::new(HashMap::new())); - - let builder = Overseer::builder() - .availability_distribution(DummySubsystem) - .availability_recovery(AvailabilityRecoverySubsystem::for_collator( - available_data_req_receiver, - Metrics::register(registry)?, - )) - .availability_store(DummySubsystem) - .bitfield_distribution(DummySubsystem) - .bitfield_signing(DummySubsystem) - .candidate_backing(DummySubsystem) - .candidate_validation(DummySubsystem) - .pvf_checker(DummySubsystem) - .chain_api(ChainApiSubsystem::new(runtime_client.clone(), Metrics::register(registry)?)) - .collation_generation(CollationGenerationSubsystem::new(Metrics::register(registry)?)) - .collator_protocol({ - let side = ProtocolSide::Collator { - peer_id: network_service.local_peer_id(), - collator_pair, - request_receiver_v1: collation_req_receiver_v1, - request_receiver_v2: collation_req_receiver_v2, - metrics: Metrics::register(registry)?, - }; - CollatorProtocolSubsystem::new(side) - }) - .network_bridge_rx(NetworkBridgeRxSubsystem::new( - network_service.clone(), - authority_discovery_service.clone(), - sync_oracle, - network_bridge_metrics.clone(), - peer_set_protocol_names.clone(), - notification_services, - notification_sinks.clone(), - )) - .network_bridge_tx(NetworkBridgeTxSubsystem::new( - network_service, - authority_discovery_service, - network_bridge_metrics, - req_protocol_names, - peer_set_protocol_names, - notification_sinks, - )) - .provisioner(DummySubsystem) - .runtime_api(RuntimeApiSubsystem::new( - runtime_client.clone(), - Metrics::register(registry)?, - spawner.clone(), - )) - .statement_distribution(DummySubsystem) - .prospective_parachains(ProspectiveParachainsSubsystem::new(Metrics::register(registry)?)) - .approval_distribution(DummySubsystem) - .approval_voting(DummySubsystem) - .gossip_support(DummySubsystem) - .dispute_coordinator(DummySubsystem) - .dispute_distribution(DummySubsystem) - .chain_selection(DummySubsystem) - .activation_external_listeners(Default::default()) - .span_per_active_leaf(Default::default()) - .active_leaves(Default::default()) - .supports_parachains(runtime_client) - .metrics(Metrics::register(registry)?) - .spawner(spawner); + let builder = + collator_overseer_builder(args).map_err(|e| RelayChainError::Application(e.into()))?; builder .build_with_connector(connector) @@ -175,7 +45,7 @@ fn build_overseer( } pub(crate) fn spawn_overseer( - overseer_args: CollatorOverseerGenArgs, + overseer_args: OverseerGenArgs, task_manager: &TaskManager, relay_chain_rpc_client: Arc, ) -> Result { diff --git a/cumulus/client/relay-chain-minimal-node/src/lib.rs b/cumulus/client/relay-chain-minimal-node/src/lib.rs index d121d2d3356765d9327fdaa0a8c0563c3917266f..4bccca59fe3ea2eec816513778885f43c9504d51 100644 --- a/cumulus/client/relay-chain-minimal-node/src/lib.rs +++ b/cumulus/client/relay-chain-minimal-node/src/lib.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use collator_overseer::{CollatorOverseerGenArgs, NewMinimalNode}; +use collator_overseer::NewMinimalNode; use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult}; use cumulus_relay_chain_rpc_interface::{RelayChainRpcClient, RelayChainRpcInterface, Url}; @@ -29,6 +29,7 @@ use polkadot_node_network_protocol::{ use polkadot_node_subsystem_util::metrics::prometheus::Registry; use polkadot_primitives::CollatorPair; +use polkadot_service::{overseer::OverseerGenArgs, IsParachainNode}; use sc_authority_discovery::Service as AuthorityDiscoveryService; use sc_network::{config::FullNetworkConfiguration, Event, NetworkEventStream, NetworkService}; @@ -172,10 +173,10 @@ async fn new_minimal_relay_chain( } let genesis_hash = relay_chain_rpc_client.block_get_hash(Some(0)).await?.unwrap_or_default(); - let peer_set_protocol_names = + let peerset_protocol_names = PeerSetProtocolNames::new(genesis_hash, config.chain_spec.fork_id()); let is_authority = if role.is_authority() { IsAuthority::Yes } else { IsAuthority::No }; - let notification_services = peer_sets_info(is_authority, &peer_set_protocol_names) + let notification_services = peer_sets_info(is_authority, &peerset_protocol_names) .into_iter() .map(|(config, (peerset, service))| { net_config.add_notification_protocol(config); @@ -184,14 +185,14 @@ async fn new_minimal_relay_chain( .collect::>>(); let request_protocol_names = ReqProtocolNames::new(genesis_hash, config.chain_spec.fork_id()); - let (collation_req_receiver_v1, collation_req_receiver_v2, available_data_req_receiver) = + let (collation_req_v1_receiver, collation_req_v2_receiver, available_data_req_receiver) = build_request_response_protocol_receivers(&request_protocol_names, &mut net_config); let best_header = relay_chain_rpc_client .chain_get_header(None) .await? .ok_or_else(|| RelayChainError::RpcCallError("Unable to fetch best header".to_string()))?; - let (network, network_starter, sync_oracle) = build_collator_network( + let (network, network_starter, sync_service) = build_collator_network( &config, net_config, task_manager.spawn_handle(), @@ -208,19 +209,20 @@ async fn new_minimal_relay_chain( prometheus_registry.cloned(), ); - let overseer_args = CollatorOverseerGenArgs { + let overseer_args = OverseerGenArgs { runtime_client: relay_chain_rpc_client.clone(), network_service: network, - sync_oracle, + sync_service, authority_discovery_service, - collation_req_receiver_v1, - collation_req_receiver_v2, + collation_req_v1_receiver, + collation_req_v2_receiver, available_data_req_receiver, registry: prometheus_registry, spawner: task_manager.spawn_handle(), - collator_pair, + is_parachain_node: IsParachainNode::Collator(collator_pair), + overseer_message_channel_capacity_override: None, req_protocol_names: request_protocol_names, - peer_set_protocol_names, + peerset_protocol_names, notification_services, }; @@ -240,10 +242,10 @@ fn build_request_response_protocol_receivers( IncomingRequestReceiver, IncomingRequestReceiver, ) { - let (collation_req_receiver_v1, cfg) = + let (collation_req_v1_receiver, cfg) = IncomingRequest::get_config_receiver(request_protocol_names); config.add_request_response_protocol(cfg); - let (collation_req_receiver_v2, cfg) = + let (collation_req_v2_receiver, cfg) = IncomingRequest::get_config_receiver(request_protocol_names); config.add_request_response_protocol(cfg); let (available_data_req_receiver, cfg) = @@ -251,5 +253,5 @@ fn build_request_response_protocol_receivers( config.add_request_response_protocol(cfg); let cfg = Protocol::ChunkFetchingV1.get_outbound_only_config(request_protocol_names); config.add_request_response_protocol(cfg); - (collation_req_receiver_v1, collation_req_receiver_v2, available_data_req_receiver) + (collation_req_v1_receiver, collation_req_v2_receiver, available_data_req_receiver) } diff --git a/cumulus/client/relay-chain-minimal-node/src/network.rs b/cumulus/client/relay-chain-minimal-node/src/network.rs index 95785063c1aeb6649d7154fa39e4e111e226def3..7286fab7907cb6d475b81a4dfd97c5d001180873 100644 --- a/cumulus/client/relay-chain-minimal-node/src/network.rs +++ b/cumulus/client/relay-chain-minimal-node/src/network.rs @@ -40,7 +40,11 @@ pub(crate) fn build_collator_network( genesis_hash: Hash, best_header: Header, ) -> Result< - (Arc>, NetworkStarter, Box), + ( + Arc>, + NetworkStarter, + Arc, + ), Error, > { let protocol_id = config.protocol_id(); @@ -112,7 +116,7 @@ pub(crate) fn build_collator_network( let network_starter = NetworkStarter::new(network_start_tx); - Ok((network_service, network_starter, Box::new(SyncOracle {}))) + Ok((network_service, network_starter, Arc::new(SyncOracle {}))) } fn adjust_network_config_light_in_peers(config: &mut NetworkConfiguration) { diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index 515c8ead32aaa56dfb59eb1df0e42c712973c3d4..801712b1ad150a8a63e85c40ec0854a62b5969bc 100644 --- a/cumulus/client/relay-chain-rpc-interface/Cargo.toml +++ b/cumulus/client/relay-chain-rpc-interface/Cargo.toml @@ -1,7 +1,7 @@ [package] authors.workspace = true name = "cumulus-relay-chain-rpc-interface" -version = "0.1.0" +version = "0.7.0" edition.workspace = true description = "Implementation of the RelayChainInterface trait that connects to a remote RPC-node." license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -33,16 +33,16 @@ tokio-util = { version = "0.7.8", features = ["compat"] } futures = "0.3.28" futures-timer = "3.0.2" parity-scale-codec = "3.6.4" -jsonrpsee = { version = "0.16.2", features = ["ws-client"] } +jsonrpsee = { version = "0.22", features = ["ws-client"] } tracing = "0.1.37" async-trait = "0.1.74" url = "2.4.0" -serde_json = "1.0.111" -serde = "1.0.195" +serde_json = { workspace = true, default-features = true } +serde = { workspace = true, default-features = true } schnellru = "0.2.1" smoldot = { version = "0.11.0", default_features = false, features = ["std"] } smoldot-light = { version = "0.9.0", default_features = false, features = ["std"] } either = "1.8.1" -thiserror = "1.0.48" +thiserror = { workspace = true } rand = "0.8.5" pin-project = "1.1.3" diff --git a/cumulus/client/relay-chain-rpc-interface/src/lib.rs b/cumulus/client/relay-chain-rpc-interface/src/lib.rs index 96f8fc8b5563335eb7796bc8fd105fced15bc5e1..3a4c186e301eab295aa3befbd3c5549636fdb2c0 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/lib.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/lib.rs @@ -19,7 +19,7 @@ use core::time::Duration; use cumulus_primitives_core::{ relay_chain::{ CommittedCandidateReceipt, Hash as RelayHash, Header as RelayHeader, InboundHrmpMessage, - OccupiedCoreAssumption, SessionIndex, ValidatorId, + OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -110,6 +110,17 @@ impl RelayChainInterface for RelayChainRpcInterface { .await } + async fn validation_code_hash( + &self, + hash: RelayHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> RelayChainResult> { + self.rpc_client + .validation_code_hash(hash, para_id, occupied_core_assumption) + .await + } + async fn candidate_pending_availability( &self, hash: RelayHash, diff --git a/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs b/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs index 6fd057e170b715a1586dbd2dbf3f608268c539bd..9a49b60281b3c51fa1426903a0e73157a6f04e0e 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs @@ -19,12 +19,9 @@ //! we treat the light-client as a normal JsonRPC target. use futures::{channel::mpsc::Sender, prelude::*, stream::FuturesUnordered}; -use jsonrpsee::core::{ - client::{ - Client as JsonRpseeClient, ClientBuilder, ClientT, ReceivedMessage, TransportReceiverT, - TransportSenderT, - }, - Error, +use jsonrpsee::core::client::{ + Client as JsonRpseeClient, ClientBuilder, ClientT, Error, ReceivedMessage, TransportReceiverT, + TransportSenderT, }; use smoldot_light::{ChainId, Client as SmoldotClient, JsonRpcResponses}; use std::{num::NonZeroU32, sync::Arc}; 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 322bcc93dae6d8158a0e6cfb99ded9a29bee79c5..b716feef1c998d66eba3c5ea28ee1dd98c4959e3 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 @@ -27,7 +27,7 @@ use jsonrpsee::{ core::{ client::{Client as JsonRpcClient, ClientT, Subscription}, params::ArrayParams, - Error as JsonRpseeError, JsonValue, + ClientError as JsonRpseeError, JsonValue, }, ws_client::WsClientBuilder, }; diff --git a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs index c64fff77a29fd016d7e1723ab461dc8082770682..6578210a259c956b1c817f579f66e84258a8ab3e 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs @@ -19,7 +19,7 @@ use futures::channel::{ oneshot::Sender as OneshotSender, }; use jsonrpsee::{ - core::{params::ArrayParams, Error as JsonRpseeError}, + core::{params::ArrayParams, ClientError as JsonRpseeError}, rpc_params, }; use serde::de::DeserializeOwned; @@ -647,6 +647,20 @@ impl RelayChainRpcClient { .await } + pub async fn validation_code_hash( + &self, + at: RelayHash, + para_id: ParaId, + occupied_core_assumption: OccupiedCoreAssumption, + ) -> Result, RelayChainError> { + self.call_remote_runtime_function( + "ParachainHost_validation_code_hash", + at, + Some((para_id, occupied_core_assumption)), + ) + .await + } + fn send_register_message_to_worker( &self, message: RpcDispatcherMessage, diff --git a/cumulus/client/service/Cargo.toml b/cumulus/client/service/Cargo.toml index 997413ad0da8302c615dc5b6738d7871580425ea..2bafbee951a950ce7144e593cab9ae4387b1a5f6 100644 --- a/cumulus/client/service/Cargo.toml +++ b/cumulus/client/service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-client-service" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Common functions used to assemble the components of a parachain node." diff --git a/cumulus/pallets/aura-ext/Cargo.toml b/cumulus/pallets/aura-ext/Cargo.toml index 14dcd10ddfcbfb42f38e068ac7b75c7ee6356f51..ff30dce7b03394a523a811217de0db9b7dc5cd02 100644 --- a/cumulus/pallets/aura-ext/Cargo.toml +++ b/cumulus/pallets/aura-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-pallet-aura-ext" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "AURA consensus extension pallet for parachains" diff --git a/cumulus/pallets/aura-ext/src/lib.rs b/cumulus/pallets/aura-ext/src/lib.rs index 34a41557152d8df38e0abc2d612151b2f74dc779..31b571816a0c15837c6175ab233204565cc7eb36 100644 --- a/cumulus/pallets/aura-ext/src/lib.rs +++ b/cumulus/pallets/aura-ext/src/lib.rs @@ -117,12 +117,6 @@ pub mod pallet { impl BuildGenesisConfig for GenesisConfig { fn build(&self) { let authorities = Aura::::authorities(); - - assert!( - !authorities.is_empty(), - "AuRa authorities empty, maybe wrong order in `construct_runtime!`?", - ); - Authorities::::put(authorities); } } diff --git a/cumulus/pallets/collator-selection/Cargo.toml b/cumulus/pallets/collator-selection/Cargo.toml index 9c2af8893ca11ecf005be4c14ee1b718a3674f5c..20f048b97d558962ea270ef51399f6d2905ab1a0 100644 --- a/cumulus/pallets/collator-selection/Cargo.toml +++ b/cumulus/pallets/collator-selection/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0" name = "pallet-collator-selection" readme = "README.md" repository.workspace = true -version = "3.0.0" +version = "9.0.0" [lints] workspace = true @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = { version = "0.4.20", default-features = false } +log = { workspace = true } codec = { default-features = false, features = ["derive"], package = "parity-scale-codec", version = "3.0.0" } rand = { version = "0.8.5", features = ["std_rng"], default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } diff --git a/cumulus/pallets/collator-selection/src/benchmarking.rs b/cumulus/pallets/collator-selection/src/benchmarking.rs index fa95303495dd1039f93f79baf93dc5301a31840d..e2af74a6e60ef00ca9d1111518d86df3fdfb0656 100644 --- a/cumulus/pallets/collator-selection/src/benchmarking.rs +++ b/cumulus/pallets/collator-selection/src/benchmarking.rs @@ -22,9 +22,7 @@ use super::*; #[allow(unused)] use crate::Pallet as CollatorSelection; use codec::Decode; -use frame_benchmarking::{ - account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError, -}; +use frame_benchmarking::{account, v2::*, whitelisted_caller, BenchmarkError}; use frame_support::traits::{Currency, EnsureOrigin, Get, ReservableCurrency}; use frame_system::{pallet_prelude::BlockNumberFor, EventRecord, RawOrigin}; use pallet_authorship::EventHandler; @@ -394,7 +392,7 @@ mod benchmarks { register_validators::(c); register_candidates::(c); - let new_block: BlockNumberFor = 1800u32.into(); + let new_block: BlockNumberFor = T::KickThreshold::get(); let zero_block: BlockNumberFor = 0u32.into(); let candidates: Vec = >::get() .iter() diff --git a/cumulus/pallets/collator-selection/src/lib.rs b/cumulus/pallets/collator-selection/src/lib.rs index 7449f4d68c7eacc8b07fa45f2f991c13f3d5713b..fb4c4a445df296b3650901326db9edccbbbd61cf 100644 --- a/cumulus/pallets/collator-selection/src/lib.rs +++ b/cumulus/pallets/collator-selection/src/lib.rs @@ -118,7 +118,7 @@ pub mod pallet { use sp_staking::SessionIndex; use sp_std::vec::Vec; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); type BalanceOf = diff --git a/cumulus/pallets/collator-selection/src/migration.rs b/cumulus/pallets/collator-selection/src/migration.rs index 58b4cc5b06a1ab7da17b31ad3c457fd992c59136..f384981dbae8e034a4465ac0f226fb4422be8089 100644 --- a/cumulus/pallets/collator-selection/src/migration.rs +++ b/cumulus/pallets/collator-selection/src/migration.rs @@ -31,8 +31,8 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 0 { + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version == 0 { let invulnerables_len = Invulnerables::::get().to_vec().len(); >::mutate(|invulnerables| { invulnerables.sort(); @@ -45,7 +45,7 @@ pub mod v1 { invulnerables_len, ); // Similar complexity to `set_invulnerables` (put storage value) - // Plus 1 read for length, 1 read for `onchain_version`, 1 write to put version + // Plus 1 read for length, 1 read for `on_chain_version`, 1 write to put version T::WeightInfo::set_invulnerables(invulnerables_len as u32) .saturating_add(T::DbWeight::get().reads_writes(2, 1)) } else { @@ -83,8 +83,8 @@ pub mod v1 { "after migration, there should be the same number of invulnerables" ); - let onchain_version = Pallet::::on_chain_storage_version(); - frame_support::ensure!(onchain_version >= 1, "must_upgrade"); + let on_chain_version = Pallet::::on_chain_storage_version(); + frame_support::ensure!(on_chain_version >= 1, "must_upgrade"); Ok(()) } diff --git a/cumulus/pallets/collator-selection/src/mock.rs b/cumulus/pallets/collator-selection/src/mock.rs index ab9ad5ec11a21420c289f92803124b7106a48554..fe41e7318bcdafa025ec33a74c9651d664b03be2 100644 --- a/cumulus/pallets/collator-selection/src/mock.rs +++ b/cumulus/pallets/collator-selection/src/mock.rs @@ -95,7 +95,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } diff --git a/cumulus/pallets/dmp-queue/Cargo.toml b/cumulus/pallets/dmp-queue/Cargo.toml index bdcee0f5ff857a9323b4b3568d7b97bad4630dd6..83ed994d04167607e3df54587c49e6b88576d4ec 100644 --- a/cumulus/pallets/dmp-queue/Cargo.toml +++ b/cumulus/pallets/dmp-queue/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-pallet-dmp-queue" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true repository.workspace = true @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index d24fdfe101e9e94b7d84008cc0ff909b630defd1..7e0442f0b5856fa5153e29ff497cfee876c067ec 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-pallet-parachain-system" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Base pallet for cumulus-based parachains" @@ -14,7 +14,7 @@ bytes = { version = "1.4.0", default-features = false } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } environmental = { version = "1.1.4", default-features = false } impl-trait-for-tuples = "0.2.1" -log = { version = "0.4.20", default-features = false } +log = { workspace = true } trie-db = { version = "0.28.0", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } @@ -36,6 +36,7 @@ sp-version = { path = "../../../substrate/primitives/version", default-features # Polkadot polkadot-parachain-primitives = { path = "../../../polkadot/parachain", default-features = false, features = ["wasm-api"] } polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } +polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false, optional = true } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } # Cumulus @@ -55,6 +56,7 @@ futures = "0.3.28" # Substrate sc-client-api = { path = "../../../substrate/client/api" } sp-keyring = { path = "../../../substrate/primitives/keyring" } +sp-crypto-hashing = { path = "../../../substrate/primitives/crypto/hashing" } sp-tracing = { path = "../../../substrate/primitives/tracing" } sp-version = { path = "../../../substrate/primitives/version" } @@ -78,6 +80,7 @@ std = [ "log/std", "pallet-message-queue/std", "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", "polkadot-runtime-parachains/std", "scale-info/std", "sp-core/std", @@ -101,6 +104,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] @@ -109,6 +113,7 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-message-queue/try-runtime", + "polkadot-runtime-common?/try-runtime", "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] diff --git a/cumulus/pallets/parachain-system/proc-macro/Cargo.toml b/cumulus/pallets/parachain-system/proc-macro/Cargo.toml index bbef5d05579e24edbb5f40532881faca204df9b4..0a90c30e0331261026125f429efe70eff07ac069 100644 --- a/cumulus/pallets/parachain-system/proc-macro/Cargo.toml +++ b/cumulus/pallets/parachain-system/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-pallet-parachain-system-proc-macro" -version = "0.1.0" +version = "0.6.0" authors.workspace = true edition.workspace = true description = "Proc macros provided by the parachain-system pallet" @@ -13,9 +13,9 @@ workspace = true proc-macro = true [dependencies] -syn = "2.0.48" +syn = { workspace = true } proc-macro2 = "1.0.64" -quote = "1.0.33" +quote = { workspace = true } proc-macro-crate = "3.0.0" [features] diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 5a0fa57fb171c60f13b87d70305a6d58dad475a1..81965a2a313875fd8783cf9e22ca3dd652f688c3 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -205,6 +205,7 @@ pub mod pallet { type OnSystemEvent: OnSystemEvent; /// Returns the parachain ID we are running with. + #[pallet::constant] type SelfParaId: Get; /// The place where outbound XCMP messages come from. This is queried in `finalize_block`. @@ -840,6 +841,7 @@ pub mod pallet { /// /// This data is also absent from the genesis. #[pallet::storage] + #[pallet::disable_try_decode_storage] #[pallet::getter(fn host_configuration)] pub(super) type HostConfiguration = StorageValue<_, AbridgedHostConfiguration>; @@ -1610,6 +1612,15 @@ impl UpwardMessageSender for Pallet { } } +#[cfg(feature = "runtime-benchmarks")] +impl polkadot_runtime_common::xcm_sender::EnsureForParachain for Pallet { + fn ensure(para_id: ParaId) { + if let ChannelStatus::Closed = Self::get_channel_status(para_id) { + Self::open_outbound_hrmp_channel_for_benchmarks_or_tests(para_id) + } + } +} + /// Something that can check the inherents of a block. #[cfg_attr( feature = "parameterized-consensus-hook", diff --git a/cumulus/pallets/parachain-system/src/migration.rs b/cumulus/pallets/parachain-system/src/migration.rs index a92f85b9cd420e9e1027d1f82c643d90fea8b97c..30106aceab5a442c34360563fdfb096fdc6ac9b1 100644 --- a/cumulus/pallets/parachain-system/src/migration.rs +++ b/cumulus/pallets/parachain-system/src/migration.rs @@ -21,7 +21,7 @@ use frame_support::{ weights::Weight, }; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); /// Migrates the pallet storage to the most recent version. diff --git a/cumulus/pallets/parachain-system/src/tests.rs b/cumulus/pallets/parachain-system/src/tests.rs index 7528d3d9fe8d97a24602c4206f5489f1602bd957..5ff15036fb6e48b5e1a0567730b1f9035357edc2 100755 --- a/cumulus/pallets/parachain-system/src/tests.rs +++ b/cumulus/pallets/parachain-system/src/tests.rs @@ -1125,7 +1125,7 @@ fn upgrade_version_checks_should_work() { ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(read_runtime_version)); ext.execute_with(|| { let new_code = vec![1, 2, 3, 4]; - let new_code_hash = H256(sp_core::blake2_256(&new_code)); + 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); diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index ce3b724420f1c9e50b65a81074e023c6b735227b..ecab7a9a09311ae0f952a7842e9f1826b00adc69 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -16,7 +16,7 @@ //! The actual implementation of the validate block functionality. -use super::{trie_cache, MemoryOptimizedValidationParams}; +use super::{trie_cache, trie_recorder, MemoryOptimizedValidationParams}; use cumulus_primitives_core::{ relay_chain::Hash as RHash, ParachainBlockData, PersistedValidationData, }; @@ -34,12 +34,14 @@ use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::KillStorageResult; use sp_runtime::traits::{Block as BlockT, Extrinsic, HashingFor, Header as HeaderT}; use sp_std::prelude::*; -use sp_trie::MemoryDB; +use sp_trie::{MemoryDB, ProofSizeProvider}; +use trie_recorder::SizeOnlyRecorderProvider; type TrieBackend = sp_state_machine::TrieBackend< MemoryDB>, HashingFor, trie_cache::CacheProvider>, + SizeOnlyRecorderProvider>, >; type Ext<'a, B> = sp_state_machine::Ext<'a, HashingFor, TrieBackend>; @@ -48,6 +50,9 @@ fn with_externalities R, R>(f: F) -> R { sp_externalities::with_externalities(f).expect("Environmental externalities not set.") } +// Recorder instance to be used during this validate_block call. +environmental::environmental!(recorder: trait ProofSizeProvider); + /// Validate the given parachain block. /// /// This function is doing roughly the following: @@ -120,6 +125,7 @@ where sp_std::mem::drop(storage_proof); + let mut recorder = SizeOnlyRecorderProvider::new(); let cache_provider = trie_cache::CacheProvider::new(); // We use the storage root of the `parent_head` to ensure that it is the correct root. // This is already being done above while creating the in-memory db, but let's be paranoid!! @@ -128,6 +134,7 @@ where *parent_header.state_root(), cache_provider, ) + .with_recorder(recorder.clone()) .build(); let _guard = ( @@ -167,9 +174,11 @@ where .replace_implementation(host_default_child_storage_next_key), sp_io::offchain_index::host_set.replace_implementation(host_offchain_index_set), sp_io::offchain_index::host_clear.replace_implementation(host_offchain_index_clear), + cumulus_primitives_proof_size_hostfunction::storage_proof_size::host_storage_proof_size + .replace_implementation(host_storage_proof_size), ); - run_with_externalities::(&backend, || { + run_with_externalities_and_recorder::(&backend, &mut recorder, || { let relay_chain_proof = crate::RelayChainStateProof::new( PSC::SelfParaId::get(), inherent_data.validation_data.relay_parent_storage_root, @@ -190,7 +199,7 @@ where } }); - run_with_externalities::(&backend, || { + run_with_externalities_and_recorder::(&backend, &mut recorder, || { let head_data = HeadData(block.header().encode()); E::execute_block(block); @@ -265,15 +274,17 @@ fn validate_validation_data( ); } -/// Run the given closure with the externalities set. -fn run_with_externalities R>( +/// Run the given closure with the externalities and recorder set. +fn run_with_externalities_and_recorder R>( backend: &TrieBackend, + recorder: &mut SizeOnlyRecorderProvider>, execute: F, ) -> R { let mut overlay = sp_state_machine::OverlayedChanges::default(); let mut ext = Ext::::new(&mut overlay, backend); + recorder.reset(); - set_and_run_with_externalities(&mut ext, || execute()) + recorder::using(recorder, || set_and_run_with_externalities(&mut ext, || execute())) } fn host_storage_read(key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option { @@ -305,6 +316,10 @@ fn host_storage_clear(key: &[u8]) { with_externalities(|ext| ext.place_storage(key.to_vec(), None)) } +fn host_storage_proof_size() -> u64 { + recorder::with(|rec| rec.estimate_encoded_size()).expect("Recorder is always set; qed") as _ +} + fn host_storage_root(version: StateVersion) -> Vec { with_externalities(|ext| ext.storage_root(version)) } diff --git a/cumulus/pallets/parachain-system/src/validate_block/tests.rs b/cumulus/pallets/parachain-system/src/validate_block/tests.rs index f17ac6007a09bf02011473f95a537855eb43f3b0..a9fb65e11089fab87c543fbb6d7b54f6231a1dd7 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/tests.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/tests.rs @@ -59,7 +59,7 @@ fn call_validate_block( } fn create_test_client() -> (Client, Header) { - let client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().enable_import_proof_recording().build(); let genesis_header = client .header(client.chain_info().genesis_hash) diff --git a/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs b/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs index e73aef70aa491fc68aad4f9479222d9a076e7edc..48310670c074d1be2ec86f582c0c84622abc086c 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs @@ -97,6 +97,7 @@ pub(crate) struct SizeOnlyRecorderProvider { } impl SizeOnlyRecorderProvider { + /// Create a new instance of [`SizeOnlyRecorderProvider`] pub fn new() -> Self { Self { seen_nodes: Default::default(), @@ -104,6 +105,13 @@ impl SizeOnlyRecorderProvider { recorded_keys: Default::default(), } } + + /// Reset the internal state. + pub fn reset(&self) { + self.seen_nodes.borrow_mut().clear(); + *self.encoded_size.borrow_mut() = 0; + self.recorded_keys.borrow_mut().clear(); + } } impl sp_trie::TrieRecorderProvider for SizeOnlyRecorderProvider { @@ -281,6 +289,9 @@ mod tests { reference_recorder.estimate_encoded_size(), recorder_for_test.estimate_encoded_size() ); + + recorder_for_test.reset(); + assert_eq!(recorder_for_test.estimate_encoded_size(), 0) } } } diff --git a/cumulus/pallets/session-benchmarking/Cargo.toml b/cumulus/pallets/session-benchmarking/Cargo.toml index af2dc2300d74b1822c9e926c719d449c5f1f792a..43fde4ea6009cce7d2dedceb815e2948beb80196 100644 --- a/cumulus/pallets/session-benchmarking/Cargo.toml +++ b/cumulus/pallets/session-benchmarking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-pallet-session-benchmarking" -version = "3.0.0" +version = "9.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/pallets/solo-to-para/Cargo.toml b/cumulus/pallets/solo-to-para/Cargo.toml index e1c94cbfde96ebe27793f713f92dc0e7b915711f..f7dc5fe4de372b1b5a03ea00cc041e3e568332d3 100644 --- a/cumulus/pallets/solo-to-para/Cargo.toml +++ b/cumulus/pallets/solo-to-para/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-pallet-solo-to-para" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Adds functionality to migrate from a Solo to a Parachain" diff --git a/cumulus/pallets/xcm/Cargo.toml b/cumulus/pallets/xcm/Cargo.toml index 9bbc281154ce3a7936ca3aa69dea615de43adbd5..63cb14b16e769fe8ddd309baf8d51594b47da49b 100644 --- a/cumulus/pallets/xcm/Cargo.toml +++ b/cumulus/pallets/xcm/Cargo.toml @@ -2,7 +2,7 @@ authors.workspace = true edition.workspace = true name = "cumulus-pallet-xcm" -version = "0.1.0" +version = "0.7.0" license = "Apache-2.0" description = "Pallet for stuff specific to parachains' usage of XCM" diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index 50ec5cacb2e9d022e3c5c4f1bb361fdfdecf4572..9078d5eda997526b8f3ed7d9f118cc9c927dedc1 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-pallet-xcmp-queue" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Pallet to queue outbound and inbound XCMP messages." @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"], default-features = false } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } # Substrate @@ -34,7 +34,7 @@ cumulus-primitives-core = { path = "../../primitives/core", default-features = f # Optional import for benchmarking frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } -bounded-collections = { version = "0.1.4", default-features = false } +bounded-collections = { version = "0.2.0", default-features = false } # Bridges bp-xcm-bridge-hub-router = { path = "../../../bridges/primitives/xcm-bridge-hub-router", default-features = false, optional = true } diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 5b900769622afa3cba2c47f493ebff1a279b182b..e92169be16b0b8466582f3dd143d9e3e1807a42d 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -600,7 +600,7 @@ impl Pallet { let QueueConfigData { drop_threshold, .. } = >::get(); let fp = T::XcmpQueue::footprint(sender); // Assume that it will not fit into the current page: - let new_pages = fp.pages.saturating_add(1); + let new_pages = fp.ready_pages.saturating_add(1); if new_pages > drop_threshold { // This should not happen since the channel should have been suspended in // [`on_queue_changed`]. @@ -663,12 +663,12 @@ impl OnQueueChanged for Pallet { let mut suspended_channels = >::get(); let suspended = suspended_channels.contains(¶); - if suspended && fp.pages <= resume_threshold { + if suspended && fp.ready_pages <= resume_threshold { Self::send_signal(para, ChannelSignal::Resume); suspended_channels.remove(¶); >::put(suspended_channels); - } else if !suspended && fp.pages >= suspend_threshold { + } else if !suspended && fp.ready_pages >= suspend_threshold { log::warn!("XCMP queue for sibling {:?} is full; suspending channel.", para); Self::send_signal(para, ChannelSignal::Suspend); diff --git a/cumulus/pallets/xcmp-queue/src/migration.rs b/cumulus/pallets/xcmp-queue/src/migration.rs index 6c86c3011d23807adfbde801ec6865b6731822df..c7fa61a3e3f0513e53d67fcee4efd6c3cecf29f6 100644 --- a/cumulus/pallets/xcmp-queue/src/migration.rs +++ b/cumulus/pallets/xcmp-queue/src/migration.rs @@ -24,7 +24,7 @@ use frame_support::{ weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, Weight}, }; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); pub const LOG: &str = "runtime::xcmp-queue-migration"; diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index 031c07217537f581e6be5a470a204d5b387613a5..1fb88cafd93c425833d38c0904f59490cc479294 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -30,9 +30,10 @@ use sp_runtime::{ BuildStorage, }; use xcm::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; -use xcm_builder::{FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset}; +use xcm_builder::{ + FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, + ParentIsPreset, +}; use xcm_executor::traits::ConvertOrigin; type Block = frame_system::mocking::MockBlock; @@ -104,7 +105,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -132,8 +132,7 @@ parameter_types! { } /// Means for transacting assets on this chain. -#[allow(deprecated)] -pub type LocalAssetTransactor = CurrencyAdapter< +pub type LocalAssetTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -175,6 +174,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type XcmRouter = ( @@ -247,6 +247,7 @@ impl> EnqueueMessage for EnqueueToLocalStorage } } footprint.pages = footprint.storage.size as u32 / 16; // Number does not matter + footprint.ready_pages = footprint.pages; footprint } } diff --git a/cumulus/parachain-template/pallets/template/README.md b/cumulus/parachain-template/pallets/template/README.md deleted file mode 100644 index 5a6461233465c327d233fc5d97bdb3a6a85f8fd9..0000000000000000000000000000000000000000 --- a/cumulus/parachain-template/pallets/template/README.md +++ /dev/null @@ -1 +0,0 @@ -License: Unlicense diff --git a/cumulus/parachain-template/pallets/template/src/benchmarking.rs b/cumulus/parachain-template/pallets/template/src/benchmarking.rs deleted file mode 100644 index 8bba2a09867dea1d55e487661c01fd72acdf4dc9..0000000000000000000000000000000000000000 --- a/cumulus/parachain-template/pallets/template/src/benchmarking.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Benchmarking setup for pallet-parachain-template - -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; -use frame_system::RawOrigin; - -benchmarks! { - do_something { - let s in 0 .. 100; - let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), s) - verify { - assert_eq!(Something::::get(), Some(s)); - } -} - -impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test,); diff --git a/cumulus/parachains/chain-specs/asset-hub-kusama.json b/cumulus/parachains/chain-specs/asset-hub-kusama.json index fba74b17f9607f58bdb17d7a0b05c8b764a9c4e5..654275eade81e5379004e649e11eb7e4acb2ec1f 100644 --- a/cumulus/parachains/chain-specs/asset-hub-kusama.json +++ b/cumulus/parachains/chain-specs/asset-hub-kusama.json @@ -25,7 +25,9 @@ "/dns/statemine-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWCKUrE5uaXQ288ko3Ex3zCyozyJLG47KEYTopinnXNtYL", "/dns/mine14.rotko.net/tcp/33524/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", "/dns/mine14.rotko.net/tcp/34524/ws/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", - "/dns/mine14.rotko.net/tcp/35524/wss/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu" + "/dns/mine14.rotko.net/tcp/35524/wss/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", + "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30511/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU", + "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30513/wss/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/asset-hub-polkadot.json b/cumulus/parachains/chain-specs/asset-hub-polkadot.json index 685a00ddc7145ed650f7cb5496fe81cfb23f0ffb..454060d2a87afb84407323263eea97d2f12d5a68 100644 --- a/cumulus/parachains/chain-specs/asset-hub-polkadot.json +++ b/cumulus/parachains/chain-specs/asset-hub-polkadot.json @@ -25,7 +25,9 @@ "/dns/statemint-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWLKxHom7f3XawRJqrF8RwiKK5Sj3qZqz5c7hF6eJeXhTx", "/dns/mint14.rotko.net/tcp/33514/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", "/dns/mint14.rotko.net/tcp/34514/ws/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", - "/dns/mint14.rotko.net/tcp/35514/wss/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW" + "/dns/mint14.rotko.net/tcp/35514/wss/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", + "/dns/asset-hub-polkadot.bootnodes.polkadotters.com/tcp/30508/p2p/12D3KooWKbfY9a9oywxMJKiALmt7yhrdQkjXMtvxhhDDN23vG93R", + "/dns/asset-hub-polkadot.bootnodes.polkadotters.com/tcp/30510/wss/p2p/12D3KooWKbfY9a9oywxMJKiALmt7yhrdQkjXMtvxhhDDN23vG93R" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/asset-hub-westend.json b/cumulus/parachains/chain-specs/asset-hub-westend.json index 6f42b5f7d8bb4a76c7390b538a68b1bb0acc1613..670935c9d2474242307d691d8707030c70f49982 100644 --- a/cumulus/parachains/chain-specs/asset-hub-westend.json +++ b/cumulus/parachains/chain-specs/asset-hub-westend.json @@ -23,7 +23,9 @@ "/dns/westmint-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWDoq4PVdWm5nzRSvEz3DSSKjVgRhWVUaKyi5JMKwJKYbk", "/dns/wmint14.rotko.net/tcp/33534/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", "/dns/wmint14.rotko.net/tcp/34534/ws/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", - "/dns/wmint14.rotko.net/tcp/35534/wss/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN" + "/dns/wmint14.rotko.net/tcp/35534/wss/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", + "/dns/asset-hub-westend.bootnodes.polkadotters.com/tcp/30514/p2p/12D3KooWNFYysCqmojxqjjaTfD2VkWBNngfyUKWjcR4WFixfHNTk", + "/dns/asset-hub-westend.bootnodes.polkadotters.com/tcp/30516/wss/p2p/12D3KooWNFYysCqmojxqjjaTfD2VkWBNngfyUKWjcR4WFixfHNTk" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/asset-hub-wococo.json b/cumulus/parachains/chain-specs/asset-hub-wococo.json deleted file mode 100644 index 6afb4f1743debef4b9ac05a4fae0626bad5057b0..0000000000000000000000000000000000000000 --- a/cumulus/parachains/chain-specs/asset-hub-wococo.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "name": "Wococo Asset Hub", - "id": "asset-hub-wococo", - "chainType": "Live", - "bootNodes": [ - "/dns/wococo-wockmint-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWJHWGqJW76brGnS4VXW9AFREKCW8L1mYmtTgChU1xrTCL", - "/dns/wococo-wockmint-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMFRVSLCGDgV9NR7whE1nYyRZvQPzPtwPEkatPi7N9Bpg", - "/dns/wococo-wockmint-collator-node-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWQXxjpGQn8MoYeGe5kfg7WvUcGtKXUihCfXaWooZc4TD5", - "/dns/wococo-wockmint-collator-node-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMzMAbKH6o6yQpGCpNFqQRJCivCUDszxN31KCF1QCGj8w" - ], - "telemetryEndpoints": null, - "protocolId": null, - "properties": { - "tokenDecimals": 12, - "tokenSymbol": "WND" - }, - "relay_chain": "wococo", - "para_id": 1000, - "codeSubstitutes": {}, - "genesis": { - "raw": { - "top": { - "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xe8030000", - "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", - "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x1002d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d6318141ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f9657ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66", - "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x00a0acb9030000000000000000000000", - "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x267ada16405529c2f7ef2727d71edbde4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x0000000002136c670a700600", - "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92d548e67179c2d8ec20a201550b5233b02d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da931e06c9b9fb6579408dd7c7efc6971d21ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b46e8b924e2d9b5085de1837f6346bf1caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ca6e2d154871bec94e6469892e75acee08d401e08c1173a01aa54ac8f8e901370077df2f8ddfa3cbf275c496cd17fd16": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d6da2c551fad8f5e3026ed1418ffcf4c7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x959220776573746d696e74", - "0x30e64a56026f4b5e3c2d196283a9a17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x3a63": "0x", - "0x3a636f6465": "0x52bc537646db8e0528b52ffd005804e9047ed30530125110582a271d6243fb8525139496cd603b47cb23211a000582c82f9cd0f269f878309c9b48a767dd4259df66ab796efb233f2eff50842e693b8abe55048f05e4ecc33eeb840f6bef46f6de64cbbda54c29a5ac13fc102111eb29abe20759a5357413f5eced701f75e6d97b186885b455c5ccb3b3003d14ab66cf2fdc4886a616e8b45545ccc7d796106b5e75ca2c8449da77adcae93dd229b700d643f8c96f57015a2420499930ddaaecbca9334703eb8bf43b92f2ea93bd5b2e8428f3da12628a9ff2a107e0b545e5cd43edf2b48d1c0dac8746f62f9facbf3ec99e0a61a26b901b8acdd1c0fa56ee729eeb2bb27ff9f54956adebbea6e057d46dd19ed753418b5d2da2e991ee8acdce5f3b8c9711f6f620565ef114616fef6b462f6a877e5d2e79c57fb5343dd2c1df4fb20a3a742d478623290f157e1ad0943b47f2aa9d1dbaf20a6acc0dc1e56860416f5fb9be80a6bce2ded4d3b4850efef65a8e42b9f12a3af435bf1ed23e3b7b7bd0951b3adae8698bca1bafedb69e3e7bede9478f835bf4b425049a575e31f475c9abfd75e686767be58ac0af3fc91e0bee86a1698acad2f63fbc9a3138afa62a065e416f678157d8b777bcd21695d6b7ef30100b75eadb9557d8cf17be5ddb4d19a86b1f85bc2a625fdfe1157bbb3290b64f431d65d5c6aa75e81a6febdbba32900aa96fad575f8e5db922ecd08fb0c38d57ac2d364353d6d4353557e95957a9d39611657e95807a536f7645edfa7395f8b565049adf5f5fae869df2facd5582ae1e14f8ead04392f3eaeda91299d0798e981e7e4739afae69fccf4f8df3b39df5d55356a9b757baf57de502a82f77e447ddf7b74a4c46106082d852818e7dcaeff66eefee36dfdddda93602a28cf38a9d5dc5ab099fd9532d20ac006a2166817ff63cbbb23bf9d140a7ad29635e5b53b4fc543d3b015e5b4a6b5eb9a11abad90571d76dbc52e7e8afae454fbfbd7d4d5be8f6afdea277f972437db97234b0fed2963b12fdfa66f4e81a7347ba8fae1c6415740d6eeded6bbd5d5ef4e5945573e7d7354db9eb9bacbfbcbd29a3777b13fee541acb53715f0ed9a72d8377f78ccdb9bdafaf6e84d65d50f3df0a004876f9fcaaa69c3b7cf38c3b70775f4f431efa8e3c489f3e6a3b7376378e8da6eed418f9ebe7235607f7947d8c76feef777b471e2c489f393f5edd05356ed0759b5fef39a02eb5a3845d3588549d3152ad0694b69e9a59957df186885d451bcd296d29657ef79754da90c9abad0adab6ba669ca2bdd5f57d77294570a79c5aeaead0637e595fe94afaa4383196c58a5dfd0dbd99b1be24f57870633de6095fe7a7b0abfe58624abda959b41b921f6205e32347d665fe62f8859bb3468ebebd5d0b594947ec26797eb526a0329fbcceed767f7703787dee66d0fe2edeeeef5ee6576f9fcf1206187ce7b649dbfc9debcbba4696afab36bfad369da42a7deaedc90fa72457e5052529af2fa29aba67c768d37e5d53aaf9455ec41dc1e6415bb00bad93db36b286db78e57ec3bbc6a6747f16a9f5db9a1861e1414fe094bfebafcfd7890ac435f875fd27e7fca0ded37f7d783388835c55f91bfa6c0ea5d33347d4dcdeed935c9abf9438530ddfadc9e7db62babd4b5f624abd403d0adb7ab2b57c37a517f925513be7a106bebf5739b1241da350f653b69bbad077f532248ebe68fb087a2e3d7d9392582b44a7f24c343d97e9dfda4449056e98f647828d7afb3e72811a455fa23197e9d5db9235049e9d78358e34d95c8fa6c5756a94b5ecdfd7675ad3dc92a750074edeaca1981ac52ef4fb24a3d8835ded6e3df9408d2b61ecaf5ebec9c12415aa53f92e1d7d997aba153faf58d57faecca1d59a55f0fe2200ee220564f59a5ae29fe8abad71458bcdc86a6ace900ba7575cd546862798002294c2a7c514104418610c44d10230449820a4a418410e40841c2086226489620ad540c524da4d804f912a44baa8dd40e8234915a23d583d49a5412a92ba93641b0a4e2a45084940c5246a454904a416a05a92b525f82780942458a072914a48a4889204544ea88d496204ea49648654929118404292d41944831911a2265454a8814961486908211524042ca4a8a4a0a895495149210844a0a882050048141902f825c0962444a8a2052a4a808628220270812454a291544ca4b6a8e149690029c2060a4102788174196489d2088529029a9292931824c11a48814185f0f3e345f199f19df19dfd237836f071f0d3e361f1a9f0dbe1aec8862870952a8a291e181068f15de133b86d09474aa6865ec58e3616142e3a1d1bde1d4f87043c7053ddaf8aa300a5070506f744a3d92f0369a4ca7e1c3ca8e3578aaf04ac0e30b8bf121448f568a0b4e8d1e6cec68a2eb02cee151d1834c8e10261ab415de193d4b34989c22728ce01ff06871d2628ad3a34cd7dac4e0a1060b2105371e1a54125234e0c1c647123d6210a44a902372a8f408634715531a1d21b81ce820913325c74a8e928f367c48d18dc92122c525058649d0c34cea891d3bd8c1048f3382201144053ee0d0d122d5da2144cf14ed8c46c5cba2cfe420a1871841c8f070c3f34110227280c8b9d221a179d0030829287ac0c0c7154182d8c2c839420a4208522665028d083d28f871a647053d637ab6e84941cf173dad1e2f7ac8f474d123464f183d55b418f00dd412504340c1817a831202aa08a82304b1b20da15be0c30427256c32d862b099e9ced8c630b9d12df134919a022507cf123e48e04389078914e2f80105ca08419010248e206f82c0e1238814ac74657286f041c50714bd829c207c74f1c144f7850f25fa8a931c283676b6e8f185ef00d503d41cfc65478cbb01e33825c1d9745b745c9cb0742fd8c8f81af6927a415442e98047991d627690f154e0ade0e404d7859606cf1a3e84a073042a0b141729334180904ac8a8f01c3da5ada08220af7c40ec113d72a053e69bd263049f528e12761061471c3972f458a207971e5db81ff4d8d243043949e8f1c40f267e28d143891e4dfcd0e283cdc9049a117a90a047143b38d8c9c1ce0d76d4eca4d95963478d1e26e8712515450f2374ce48559113a7c7102825f800228545902a78d460871a3b64ecb0b1c1009503c9821d6f768cd829620712506eb604cb65a7053b2e40b9e1a3042928a1a9f041066a8d1e259e389a0c3e3578e0fca0a28de1a3891d2d76c8c06403f7018f123c47482c3a303a2f3a325b1c3da6f0e2f460a287961e59bc397a60e901c58e0976aeec28b19365a7cb4e133b5f76bcec50b113c5ce143b27d8916247cbce969d2476b8ec78c18e133b47ec9060078a1d26764ab0b3c48e2e7670b163cc8e2d76b0ec886007899d11ec3ca193041d3974defc3043478cb8844e1850c953e3a30a8a085b0b9a20a70a8f333bc6ca60cd6c0c568c0d63cb2c180b8325b3adfd62bdd82e76cc72b12fd82d56ccba60b5d8166c181d255071a0e2a0d490436c607440682ebdc4f64593e0b4c422d14df1408f979e2f3d9cf89125070e0a093e86f031a577c0549a081e5e4e4c9c94f8d6f010e3e30828858f2d7a80c1a3c50712a730a72c4e22383571d2c1c9cbe906a7149c707052c10905a72f272e4e579c5820e750c10b4cc20f32b83824159e25f0284133e34305a836bc07a81df07041bb00c5031f59b418ecd0018a4d2786b7f1c1c579e03f3865e92c38317614811bc309c105c14dd9e6d8e0702fd8caa43087b3c15ba0d49ce6f8318686841f65687058881e2e7a5ea02981c7153cacd0deeca4c133070a08a7383c49a07ed085715a024f16940f78b09ce0c038a835df18720a0f131d0cb42bb42a3414685e7498a1a30c9d317a62806ad36c78e4f8b1448f12ec70c143c8469035910121df4838520e9904f98614827423cd9065c81948154036d20b76042c0e06072b023607c606d686549256303798103038b020603bc0d86040c0dcc0da603dc07c80f1205e81418844c8206415094434024302468448453c41f4227611a788524415c42ba215714cd422b620868959c428a209629708457c229620728924884ec42de20be2185106d14c8c4114238611cb4430220c2299d88a5f442e220b2216114c5c0104226689494425229688443c220211a7442a31885804dc017403ae81338063c035200b6016b00bccd26d1a8e1642fba0d74875d169601852881ea3b5e830cd82c6a255d062fa0441ac48a901cbf4117da58de822b01fb01cadc47158094d8593d042f01bec86dbb00f98070c07b7c141805358058c055f21e1e02cb80590090ec32be017c0167301c9c02f980a36019f008a80a78056ac1c7bc52a61d37011f00a076c12568dd5c1b6b16a768dadc17ed9142c0ad6060fd8334b03366271b02a5834bb0218056c028201612097b04eec96c5b2599c34e18051510e36d0b04534400a039628a1c004248084b546cc60a4c82e3164480109100a42c001628001689360c147880a074c49dbd7e5d6c8b8899e2cd140921e9610f1902489b6ad4313a224494c3c574e3a4670b243932346464e12908000e8c4a14f887268b2c46889069e10196980c9069af85024059d22104188ce107e74dc18192dc9c1c868490284c043d2069a1019c1a144443b247192942489870d3021ea808f4e10f48911932549443a242122da61059d37d4c90e4d9824f10012910f474998f4f0494d8838e0812536b80e107a747e4094f4c4034f7cc00127494f7c68a344493e6880095252d2121d9d1e10191939c901a5c30345226ad28127493b3480d3d98112e5d0c44993244b9230e9e1897268e2a401494d7c7092430e0f44878d0e4e7220d201051d37f48907888a923c79e20122244d9c3079f28408029e4e1bda01263d3c125111929309d4e0e9b0a144494b746062b4044a12221f887c70c00d3a106d60034b6e0880ce1a7d529403110f39aca14f929a102111f1d08489eba8d184c90e3c3a3ad01c983c69c20429e907a2241e30eaa18725499088743062e28350510e4e8c847a74d4e8931f3a39d025497a7470a01c48b22469890e4b928818a0a373830f30315ac224894913263c74d268510e4e8c34908428c9871c887e20d2a19386223949c0a663037d42c424890992131e927c58b243a706dac40993a425483ee89024c989d193244c7af825483e3c0f5d22230d2c496ae2c30e499272204262a24313264992f808927dc1c09c99c1975796a62d25a55f56fbb8b029d894ed613118249e104f38e18418642320bb550783988e62dcaaac18c6183386618ca92aa6ac0d9b59bb19630c638dac1cb9b556d6c13adcac3730b309dbc62b4015042075e88e54a8cc18738c10fef8f1e3c78e1d58373304ba5b4a4d15728eb67563acf024a1840da1005a0733840f7000d41ada00610d1a94cc3b4ed287b2895b636ec654595b99b7ad6b40b72d037837d86dcbbcaa18c6baa96edaad3d54bba15e18ebd5da596fdce5c5306c555987064c0712c5305535811baa9432c2085b428ca5aaa658e7b09a3055b8ab92554a0e9e7c4028197664960d777084124ad6b61f526e10c2eeae1322840516babb75e860eeeed6341d3ab8000440051580f0c05831e514987b98997bb8879bb91976c3de95b29b6163dadccd0d2137774a47c70486404c600844a70340e7a30304009dcf0258f9c80c9baab2911e3d984dba8c754b29314c37d9cdcc41308c21434c8b70f70eb6dcade5685a8f5e55e62db58a61d8ea5eca97aa5e7da95e3c545587326666056012424d6b5695524ad912c2d3099e4e10b2a6490865b7d692a1d45825b166d5b4d3c907c60cb7146facdcaa2d318961ccd82ac6184bd6d61edddd2db16eaca564960c5bb1865ace2a2f6b6429a584cdcdddcc2a99b937664e00f3a6da0d25ef76a79042967177376498a3dc7c623e0935e7aa0fb995394618e12e841884b065c36ed8aa1cc618646eb88bc55db9bbca61905b42c5b09c886152ca66868d410ce3d61377ab6a3343d81242c8cc0c5b99595519c39495316dd588618ac1c817e44bebc62ec9bdda98623dcd7573ddad90399390a5067748c99d20638a61580761da43310cc354b9b1968d2994cc58378432608a6dddccac90bb2133c78861cc52aa6a430821e466c80a95592a63aa8ab162caacaa1062aaaa182b3702989b9931d8aacacaaaccdacd0c15723343664c95a12aa6513562ca504ac57461c4b0c5604308176bacbba3d458752933fbc0585b310c63e686aacd50c21327a1b6435ed7d52d99b1861a94522a86298c5019c266e66ea8ca98546e86d80130ec93b2314cee462c0618887c9f01620e866111c3561802d43107468961a76e6ec6300cc3ba31cc8b18a6187635841a6c08b915e36666be1846ac195356a10915b6f48eb207909c7080880317d292a4157c783f8a7258a2012323270058fd40e104147ca07aaea41c963c69b20429294912130e70c0898ae7e36942d4430f4b7a9603272811d20e49909c242dc9c18991d1929e02e8408444a403929324ab020c80880952120006f082ba90b40489a809d1069082987400444f9e1021f5c001273924412282e2240722a4244b9ee890035112178a560518400f39103d01f21d98201141496200170a508001e84094c449520f1e40ca21851756407e70d281a41356051880a3860cc0852403202a5a62a481241b68c28487274b7840415d20e20093a4263e207d437648f20108110f49929a3041ea21490e4f9870a027531788a0101925f96109075030415dd88013a3241b68c264031b7062e463c779fc40a4431326499a3059c2430e3bece0e42380911193a21f023c5961003a8024274e5cd061091192ca03444549927e206ac2024f12d10656f851179e243d61f20311005ad001fcc0640722a41e7ea03421ea1d75214913274c9e2011414982b484c8288724441b5882e424e949121d8c98f090439224231f96ec40a44392a41f887ca034216ad5b60366b158502a8ba552a4b05851ca2a4bca25056349694e096b73a44881dd4a582b85c54a54cab25852a448814a582b458a9456a22c2952a45c523425ac85ac84b55296b54a58cb92c26a2552a448912245a5b05889949522859528cba444599912d64a59252a450a4b9522458a14d6b23025ac9522e5522265951595b096c56225aa4a58cb624125ca62b15a89b258ac84b5ac55a2ac552281988355d5e5c093251a0ad7d5552f30901452d714029db6be74799dd7969730af9c083ae8d1d7513d73a3b7d3402c2a2aa0c28b11507421860884a04b5e09e0a177601efa0eaf4e0ffd8b170fbd8781b4a5a4a45445087a4ad5402a04bfd99fd14fc744a75d0349a17514afd8eb76d8671d96d1a15e5b5544f1a6d756155b7e76fb15f90c5230050ac4f4ec083a6d5561e55320a60a2514b496bb81958430d706ba9c5d530e6b875eb0eff2a0fee1e117a35794c40f3f28fdea3b5e39242bbc7e5e3c0057b0b9e3c46118f8d5f78b90f6354e1ca14b9508f44944b7afbe2ea197c4efe572e5242083d0f569fbf4e73007394040cdd5b053bcc9faa1e89767450a5d1ebd1b584948ca2b14f8d1798e6c978fb0bf29e5864e4889cc18bfa39c879f974b57e92f4fdb4721371b4d9374d0a79cf0a36bcbcb14edb1e3ec9e074d59c35c5dfba18216ed35f9f9a2f68d55ab984be7cba14fa39f979eb9263dd94487b93e679e7c6d2029c42e650d50f6f7c3be263f7f7d45d0e307572e1bd1ddddebddcbcc0ed5582dd1a1d1cfcba4fe233c5250832a7e1d7a463ff0d7e1aa19dad8dc50b787b12fb7ce5cf3ee11bc6cd9dddddd650fe2f53615c25eac7801e2893aa6628ebf5e5b54bcf94582d6804e535f57f1ca0a95166214ab76d4e341ddf3ff5922beb6ba50691f753da90b107ab32b82bec2cf75f6c88a521efacc18a06e7cfcbc480f62574f7af4a0171b0b3742f2535233b0409fd9b70735f456786d95abc00a2f655991190dd59ef2fdcd557ae590e43cf4f6a0c087edaaadaa2a447db968da802d005ac0fce6d1d97bb2cfcedc0cfad957b49e31d2aa90e849f28f34ce6bce5c91cd4d26edf301f549a8df4f554874ce3c0502faa6a5cb6f9eb9a6c58d4d9eb13759cfce1c127824f3cd83d808e69aafa779e66dde1c12e9bb93bf5521eb996bdee4df0c8afca39e43296959d7721056a107e0350d4107fdf225dafce5980d3828a1a585785ac52b9ecb3da552c2c312534421144e78e3230bbc9a97ab943faaf49e61c34109afa29aa18157fbd17f9458fae82f44f8f351d198f9e8ca2b9e9fcae62357430f8f39ab275d295e45245e0e4992c75c62ce8a7daa2ce8a76597e972f5b998b3273fccbbbcc9faabe8e28e609863df0cc1b3eb5fec75917646b2d787649da3f76859def8cb8b3be5a7746d0db1d4693bbcba3c3ae48c489f21887364fdfa264f79e99ab292c4bc79b97a505f537eb9dd481e68338657a01c57ef78c5b9fa0eaf4caeea3d9ee295747515af2e67bdc9e35c7a2c4dd33c88378f477a06c49587ce51b95ce3a2e902d1692b8b150f5d83ae69acdd3ccb34d756f3a37e4d5f2be241827984d80943829d30cda56b99c9f533d357447ae6999a876ef2330f8ba267dfdcc77c73e9ed67cd1531994ca6af28fa84fe8455c313d66f5e7439ac013af6cdcbb72ff3a0fe4c6a71536efb6694dffcf226fccb73bce62aaa2eac10951cafa2eac2e52b14d49ba7c5edf2da733c15921e92ec2be21c3ae7ca0da900a6e3bc2fcdaf6f72ae7d3c9a631e9140e73e1eed4322fd72a95d9ec9b122293124129347a067dfbc5c734df3e6e63c99eb6b7e793cd95724ba76b97e96310f5d738db722936bdfdc9f976faef566f2369ffb1b7345b423daa72a646ade9e7d2a6466ded0b56f9a7cf3b4fd6b7fffdaf9420cafae2db78581a7bcfa4989e8abff0c91b22c136f496041d358a808b11444126076caa6adafe728562d52a7eb9aaa7227f80a45c7abaa6ac7aa95ac62301a3b3b4f8cab97c7137d1f734c07fa0adf8e397bd1972fbef8c2d653e74b2aaf5e1ed497175f8b1ebbb6513af68dbd45b8c508828e5d8b5c11557361aa1347efe757bf8e552b7cb3b0ed1968ba5688e87805af2d11ace0198ad71613585ef5dad2d23a9214849b9fac31d74f1fb288f1ea394b44d730786d91408edf32af2d27b478165e5b2350633923a632d6fc919277f3c41f4939fd4c6a430d14143f93be40a1b5f133c98836e2cf27376841005eb0f4f38998a557a9a4f4aa715efdd59909ed689fa160f3de7befbdc61b7b91bfb9af18e8b445459b8f69fcba16b7fd66cb04587e1daec78324a651d4aecedcd084c2631dbad6de91c68913a706efa3c76fb25e5f831e141e58fc39a6f1eb253112fff63466cec8c6f9a38d133f9e23ebed46d6fb4bd2b4d5e5ca27f1c76fb243d96f677d7bdf9ef79ca4bd3a2b6e5719f39bf3ed6bcbaede5f40f6160b41a72d24ac78e99891cbe4d29b262fca5c7a74ccdb35f636d74e1ecfc9f527eb4f8e7993f93bca792849fc27df723c9e93ef77aec18d5d638fe70854527acde1f76bce1e3f73464e9ee3758e792767afb71c0fbe0637cc53237c8ed7afc50df354099fe3f16b90572306afce37c73c5dc2e7685c8627cefbb9cdd5fce6b1775e8e07758e17b50873e94d03eb0be92aa51659c7963b82b9e439d2bf79fff64dd61825a5674f736db9289de6274ff329aff176f234d71a8ed72011348ed7e276f29e600f857f0348fb9a436e08049de61ad45cd3d8bb90650a27cdcb2801f127efe7359337e167dac7b9aa1013e79bc99bfd996fd966ca5cf390368e63df347693a71c0dca42a6ae1bbf8e55f0fbbe3f14ab4e8bb1d1903af8da42a20b52175f5b484ca12a24058edb368ef3f576e0388ee35c53dc1b4d339934cdd7db41d3344d734d6944c83229b34c3ae65996652e9548e69acabea26edff0ac00a12dc7533c8a552a0463660fb20a932a04f3f576d8017398299121a94478a069063a0c732dc7887e4abbbaafeefeb4fbc2fcf23615824149d4690b092c1f1d831ec4d8e518e69767e4c2780acd887e8e6beae9138f629542f1aa42d6d5db5f4e85e46c2a24c7d923ea4ecef9e69a137526cf5c3ae644dde571cacfc9f14d89b0e738a744f4739c73d4d0dab5f6728e60469ab9ec7296de994bbfbae5956998f4f6a0ec4be9d82af7a9dbfbf3975f9174cc956380ccbec9fa6cb328b2bba507f55194e775c947fd2cd983b8878e7db51c55f778dad7dbd9e3696f6f86a0576561628e797bfa9863471be72552d2d6c34b57ee8892972ed508e64de945ed986bd2fb908ed48d92d227e9789377276d6f72e9cad5d0c3671ec4d2433a92e15985b4275d2ff9db330f497ad2f526570e4992cf1c85cf5c7a4732fcaa90acb34f958576ae487429bd756ceeab476fb2af2b6764bfdbeb58a5ce928b91a169d4e9e85a6cd7966317d230fe26eb7979bf19e5d7d5c354551dd35fef28ca6347fdcaad07b196a37ed4a3ae499e0a59a59eeaa79763dec41c4af7465069ca4ff6230cce94380f45ceac08ba74fd66f7eaec6dec50b0232428d83f61fd7e43ea41addee59cc46ff2cb4ddfd1c279987949fcd03f9e23dbfa76e686da2f4fe7fae5ed41e18be7e5df3c8af2d027ebb1c5e437a3bc74f6969959fa3e61bd3afb51175167bf8efae1c3afa87d5d392410c97a7f45eaacd95e705895c312b41c3ae68e38e34aba1c94c8fab71ecac5359703953aa5df557a3d82b6aee5e86faeae3f9fa9c995c86e9eab909f57bf8e48cf932e3b88d7d35fcea4998a6439fb9a72438ae974ac3279e69a2f37b4df64fd669ec9931e94fdb99ff97a50f6b3cca5a70fb5e6ed3eed77d4cf53a47f7d3f5721eafbd2cbbe22e99a43ee48fee5daa79ea3516773e8992f778479f6a9af26bd293d337d93f5a61d3269df8cf29aaf9779cdab2caccf05b0be785516d6330feaa328df599bbca3fe76cdbc1ebaeedb83983d7dcc13f297273d6d37e9cde8eb98c7b3dfd1fef5691925a50fc0637b51a1359687dedeec8aa2aff0337e7b8555fada1a6249db2bbcd2874ea444907ede8b3a4b79e0ebaadff551a92244e8722b3b84aef06ae7a1ff50818b6e1afd5e6115f49f1dd840e5f2f520e6a19b7be5a14fddc1438fa2deb58f11b8b8000a178059a3a4247439145a4f09568184144044d18510952a4284d6adec1052a752858530b78200a1f5cbb38200a12857dcb01ec586f681be4b0ffdf22857dc80f9f545096a1fe8d817e58a1b2edf2f8ab70ff4ebfbd1566b7dea921e6108197809020e52c0061afed055c0a25321161367ba78e20932695850042115529f3f0f5db91b5c494968aff06a8587be5898e1139a2a91f9c3ca5999a04708a57c47371e7e715e1874ec6b727df372a3c6c2cdc7eff2d9df8cf2b091a81949fcf57991d1a794376271f9abc901a8b43efa84dffce1bb899a81e5a3cf0f3cbcbe26cae6e3b7f4c58b16d280013c62ccf7bcb68e78e235dea2e7258aba949c3871e27011ca36ce7b51eaa1061641f84009fa1e759c38ef4509c81127d0d2052948971598514aea41d9bc17a516d280811294035069fda0a4666011529f1ff8e8aa30a8b630b565840d3e69df0b10ca4628fb94948d907e51d4a53479c204379802474949487e4a54949484d46714ef6ff6a0c14d7aead06b674e022da441268e90ba7212d061430d2ae608224e1c21f59d6fbebad01a422ffa2e2663f476e975315ebc2d37c423682efabeb68c38c1a358151d7afb5a6fd0af08a1b70f9975a02e95486f4befccfbf2bebc3dbb5c5eed7d7963dedddae5e9c721364110b35fbe97f340dfe7e781525c1eb16b1f7a50aa10e520681a1d2a91cbd5a312b9bc17581545f03fac8aae5c4f9983f4063279745503691ebd87579055d2a30f5122ae42a447d7bc8e7da49b3cc93ed204d94028f6911e1dc5abcca3eb9b9f2f7c741394c87ef44b7a6bb93cc5a4b717c1e67b58259d7d7d39e93c98f7f3f360dfd1c679ccb56504095e5b468ce051ac927e1dc1cf3e65958c2a445d39a842d483f8f2f6a5302b7263a129afb076f8cd7db861de7e34121d3a47ef68e31469878ec41bca31a01dba74f84df910f3e58e7ea002c414a53f9a1d50c212e2fce54f587f7d4fa22babb06f42e7e819492a404cf9a38d337f600e89fce60f9a7246a243875c91cb5585cc8879fb9777b4717e3d0ec18fe7483bf476f815b1c3ef28cacf7e8ecb6068986bcb0d69be7974e971ae4ae4e4976b27af08ebe7e69a2b57449e8aa4ab6fde94beaeed6bcb082c1f55c8e541ccec9c27dde4ed6b9a720c60cf32e7bccc4d9eae2b37947d3373cdbc7dcd8358536f73cdd3d79433a2dfdc57df5ce34d33627a5521535dd376d3360f62dd344f7a1babda75e836d7dcf4531e91bf7d4599b37b33f3f5a0560e097bb6dcd1c679930775109b3cfdccd397fa97173de84156e9b6bd45d30a74d027eaa31cc2abf8f3d1a3bf101dd5e6a3f730100bc5183eb2f0d13b5eb1c718a36b2a7e45da92a2a9aeeaeaa029a6b3b10aeed8c0abe95d80fc54340f3d3a26a33f76f9d4348ff9105e4d5410afa69c8157b3e3815798c7c02b9ec71c075e69ab88328ff90518080ab5a6d1328fb9f2aae7e7cecf218f450fe21e8d3d2ccdaef1986bbcb1c78f62e331d79457fb74b4715e31dfe115740c73cc35857d45eaa6bf689aa4d316124a3f650b8a38be5d9fbd67d75ea4e2959538781dd5a34bcf3bef461a85ea810cb442ed5defbc114d630abab9b55689a4b8d3f4a421759055fab9f2904cb4764d4d6d59e96e9e67f7ed67f7a98d553f1ea0714491459a23b460892c4010524f07aade871001068885d863686f7ff3f1ecdb7edb6b8b882e0f3f14ab565b72687a818e25afb6209aded0b16ba72cb4d796952f8fead158263841133e60c3043188020742de6a0b59b5906b80ac624d53cec8f54dffcba5623a9055df4c6a93a378a5797b0faf300dcbb26f666ef20cdb58d5f10acbbc5dc3bc7d2d63d5e6cb1941fdf6cdcd4d8e79fa9b97d49fb9b6acdb27ccd3d73c7d5386fa75d94044b890b28faa90d9f3fbcdcba3b76bbd7b79fa32ac4b2f29fe6e1a6f3ffce5ea4dd99c623aeb0a398d3a1bab52ac6ac7dc59857d41ad1ef4e6befae525f5abafd7b18a7db921fde22759c5ddbb6c20a8a4a4a4a424d4ae5222fd9d7ad3c612b4b7d7806e5d469756ba3cfb861a62e9d97b22e41ad0adebbef7a0296b9a6eacfea1adaf67267e93f5d1d7d37a5b6f5fdb343bc498e686d6b009cfdd1bcfdee3713c0b1dafa6f6dce388aef1471be7a792e78aa89a57d7e206b75c5b9c02dbae21c0687d2984715338a96b4a443f55211b0e63dceb8a314208638c70ee4479ec9b50cab8d7eeeec28d0b312d767b19ea1bcc0c61733333ef2e332f431823f7f2a2d1925b8707569934e69598668a30f3a4b775579a5e0385f0eeeeeeeeeeeeee4a1e4023fa0ca1b2a0ce0558255d51d8e5ec3b17ccbc6eefbeaede306f7b87b57bee185ba1f636981d4b505e4e74c4302c0e613b3e74d03c94bd506e52afbda48851ea60cebc9b2eb7db3697315ddea03777777d779b2bb2bfc6183863def042e9b5e505989fa86f2fba7c176eb49ce8f3fa26bc2ee831865756b124ab262bba4efe8d57907feebc7a81e5b5e5459a47bdb6bc68fd7247b0387120906c9f9e3b51348839f64dd66b92c782aaa3ac520c7ed0d3c0351f5d536ea8c7b5ec82f04b03311828a6817090f08b0acaeb877b52dcb52da373ef0a200e3ffd8038f47520aedc0201e2da6e403c7d209eb9a69a6983bcca5c5ddb366e0a795dc22f2b1d7af0cbee57645dba84d0cb3ccb56582850c75bc8d60f88d735100403cd7c74ec9ba808bf44f825c22f53df446155f41ffcd2344a441f68d50788666380f8dc612060b0cc817844c1198b133cf21a5eb1cb2d3a321f4ff0c93278361f39cd03f1c96a50b8b8a12324fe40540fc431cf3c4db9ccd7839e7946d6a1af43879ff42b3bc19bfb40e4976215100f622dd3cdfaa42a0408dc80b01806c3aae899c761dce84c7ef9d437569603f269bca24ec7aa8882a360e50aafb63410fce14df7e6cdd43753f553dfcc1f93b727df93af5f9bea42bdc9d73b5e20bebef14a2e8f2f37d4e3382c0c9747af8012811edd02ba3279749f92880e5705c2d4675d879bbc566d615d7aaa42260abfee99f0eb9eb3403c15c274d2a39bbcad4ba72f3dbb34b92d6744fa3a9c991526305fbe4c06c3667889d3f0183ec368b8f5b1ccdb6003abb4222bae2ff0cb97cbdb77c39d7250701478c7b4ca30981320ab4c2e3dc92a935f5e110a8eb40fc46d905bac79e9d0b5131756751becf2714bfb44d72dacca41d7c351ee63fb09bf7c815f78753990e7d161cb0caf7c7874b8c42bcda3c330bce2ca3cca273cf3118af908c77cbc723af8057e815640306dfaa6d106dd049f0c463afc8ae0273f1a5885c2a7ac3239902f691f5ef9b221ba66c32b143c7ab76920f71d0e79253d3ab4d240dc05889b3e2b1fddf3a052fb44d738c815b93c1ba29b6ac648910dac8a9eb44f03aba25f5f11e4b2a581b84bfb44b8e5a36ba74d37cc64312956c586abf1c47304bec9e19bbe1982bf50f0da0daba2bbd771b04ffcf298f4b80cb7784c97066230ed134ff8a67c20dce58737f77ff8c515c1fefa1a0e19dd3a8ff3f01ece60d0701a5ef1787456a3b2f3d9f944fd8eabeeb872433d3c887bbc28de2ab12a5e1c8369201cda277a940c067e3cdf0fac8a8e721f3ef5cd167bf2f5e6909c9cc1f08af3e81c86c52811fde89143723a82dfdc3cd3ffe1e9fbf05026785edc7b78ca2acd79a43efa8e87b43bbcb9a17e32988faec343c270c85ce79bb2fb669793e6a3cfc03ed04f9cb745cebee9aa5fbf3ad95d0ed7f0f00a6c689a9a0ce62f1d9ee3b0fd724d537ea6a2654e74ea41acdda7ddf97a2b86a7bf22ed9d775f63de1e14c32b6cccb2a1405dfb5c8ef2810fe1afcbe726e12f577d0cf31c9deeea7ebaae35cc83aceaa18178f80edf5c1b68c7351fdf6e721edeaa4ffb8eceb7eff0547d66f66354878e0e9ffc05e51d4a87a6f3c29621daeaa266bee81298bfdca4f3c20fab303799ae2c96e641d3b4c53246e9db3bd730cc14211b3367201aa806b2c93153a6753a6dd77d50166e8b65b1b0eaf20bfbe67ec7859b6fd7e19a94daa6c3db31acbafca781b6d53e973b4abbbbb821ec5b316c74db642b6c05afba6f76dba681760b5b69a01d7e396b612e3cc574ce734cbe6b78d5b90ebf7c3296bf78cbe42e7f4dfef297eff06c689fcbb1ddd240bba67d2ebfb46f8be9e3be2d399fceb7e52fd754a56b209cc79c4a9b2b6e3e6a79cce7bef9cbbbeebc731dde9befe2782c8739233c7f7e1aa59ee7685fc7a1c06f3fcaf9d6e14a7cb91ada757c50f675beb9deb97245ba8f0556615eb41f0facba5c0756715c664487f9aee9b4d306fff265c32be9972baf329fdbe6af53c3a8ed106b9dcb990aaf30bf5c5595f3f6fabb408e37f7739c39ccdba4cec9b5f502a587be62d48c71d3e2d5d4a53258ccf06aae965dd3403db4cfe59acf0ea58557397eb9a77ff2380f697fee9abf7cb1f04afff21e1a08a53eed976bb7ea3b93baf9cb95f894975f20f3a467c3857d0ce6350529cde2f8042eb4ddba897a45f973e63328117ecd8d7ee055bbe6471408d53e9a6bae8a3a437583d77e5ef3a96cbc06e73557e953ddbc16c77a5b2cbdbef1da12c3e6b5e524c7f1ebcd0d79139679d862d57af3e7d7233724bf9f0bf25e7a5d279e23d74b872fbf29e5b10fbad1365669ae52678755976b4bccd2cf21af798c81579acfa066d48c4fd4a31a08aacffaf6cd4d62dfdc91dfec79d46b657453cdbcf63b03ab349f28ff950a24d5675d25e6d8279d065e5d1ff41978255dc35c5ba58b0b2ed63c17695e657875819a576540af79c6c099319f39743272a8f9cc07e65387a71d7e8c911bc2fcf229678389994f5583691f1e6a8169a04dd33eec7c351894fca6ab7e36988b1bc29cb5742a84e936cdcf4d837d4f414627e7a6815d9a6614f6cdaee19706469f9b66d72891a37583f95c36be86cc611a5e61cebe6a78059d7dd7281124d9d166f09b9255d82715f69a6b0a6dd5e095dad35f4237bcc21e4a2f23418762d50eab78b98e55dc1edd216c1ffcbca65ba5d39698d69bdc4deae633d75cb921d6dce4ca2bd3c50d996860a92e7de6d3c7af9af9cca7ceafb63ef399fdfa2ad090f6c986f6e78734e7da4326ea35de4c12f3214a843ff30bf0c0abf6cc0da040b27d32efc8a064f0997fe653cff84cdfd07cea9acf7c2a9bcf586055e6ebfd689f8a5519d4d9587579f4a9facc8764dc37e5cf219fb9d47cfba6347db3d3959f797346fc4d26d77c7d4d269349ddc43234e8d0cb7f1ac45af4a00ae9de40a7ad15b079a84494882e15bfedc92e0653c8065219b0198384166e70792384807740ecf8cdfdf664145d34025555d715e5215428b1e566d08f5f1287e4ba3c7e50f8615cde3a3ea253d75437906a0d78f90c4de1eeaa105eec5b554510c06f0c5efdd1575bd828cfdecc45ec72edba30ec3beabf1442d6b5fcc551c781f3aba42a040aef878479927ca4a48e5bd86f42bf8e40f9a9aa26f4cb9bab210a8cd0973b1282c82189dedf84df648690a63923ac87ad611bf363aec9adb923b079fb245d7e47fdd8c58022782e183755222c375d18788a69d38561376e83afbede2667d0ad4be736552226ee503082c2cbef08853f757fb46e5e1eed9a971ad77cba62cea64a24be46377967a6ec9bcb7135446fe8d293e12567448697df910c2f5debb45ec80d4f4ff8a15c2fdbb12f7ef093d95e37203384367f390f922b7a43065c3d355bc798abe1f28d2ebd9cbf3eb81f3f69248785ed6ea1051c175c796d6981e5bbd796164a986bb66c3a19a48019fcd24ccb396ac8fcf9a9fa9d3ff9ec79e89ac93b719c07a5e66d1e6495941ec45acc32e78ce7fa7a58b5ce7acc97536e48f25cdfb6f49a024bdb26a34ded591deae75ca4486a462c7b8657d82b176d4ff10a097abb8a851f5e9dbc670cdffe020399bc5d93de3e12a8b9d4fce4ed33c0f4cd9ee650a86733cb21814bbd0f7ff3bce4b8c9f362c585721c7a4752941e6bfd64a5efeee507bdb9c5cbdb88bfc937bebe69fa784c1ebf8b39d327a11ce5bc74ce7b494d9b8e43e9ca9629c08c318366cd1bf04da4c22bf6a85cb1791517ba025e1c92cbe137bfd74ede3eb7c0cd6f9e7148ba0cab7658d5be7d274f3fe33a56b5ef19cc1de18f0eb9212423fa263f79fc1937c47daa2d4ccdd79743c24842f09ab7195e6ddebe7d93915c5f97f9a6f4b6056bfeda9663400ee6ebd2e335797d860a612efb8eb6cdb0aa5d8bbaba319e2074cc837e798dc69b21f8f8f0eb2515d24bddecd6b7cf5efaf66e75195e99e195462c1bcb2bd80d84107afb405f247433e7a177b0bb7394c80f76611786f9f6a09bfe18d6b19052753ba82b5e3f49200219dd0eab227345f8f9d32174dd0e0a4208d52c843ee866f710b643873146e8c55e925517abb8a18da39ba8bf1cc5abcb7b78c57eb943bfaecbb78e579e0a61306c17db6b061293d0e4d8857d451886993ee81183df0eaba49452ba94524a99659964e616f0d00efb44ef58157d79d04dff1823744f8530ddc597b73f6da47900bcb6c2585184305cb270f3abbc7d2ad2af6fba22da1bdd099aa6a0c211012074537ea330103dc8aa08db74b3fb181dd5edc4285ad7b1ea07ddcc79ce51229b8376844f33a2bf46f8f7db41b7ae3f3bdf1bfbb4ab64550721d538689a526e8dd0c120860e1bc002309f7a6d6161e6f282fd50214cc77304c6af287e3cfc79b1e242d87775d4154277e60c98eb2bea9637053a9255d12f9f7259d0692b8b2e3fe5840fb3d802a6d3160bd8340bd42c169db6b258f39f459a87100b330fbd87f59b3baf46d6374e05305d74fd9455fda93a05b9cb14536801a3050c9515c4b1808e612320e795cc1071ac5ad1b16bccdd0c61bf7e33ca0759d5dd2755218a85065df7ade8d6b55dde20ab34e5ed863c4762f4fef8cd28af2a04fa4235033a8a57ebda72019787dec30203a950eae10b9f825adcdbe786146e3ac44874db71ab6fb752da2a29294d11d2874090976324baae5500d3f93ac3dd4253065aa59f21af03f889fa07722525252084d455410c048574a4f84118379070c40da4107241d7f00111729439c34d9b2e8456bebf72eb3f82152c7d005e5b2a08f35a8e4ea3eddba3a641ef08f692bf23ecaf29e519ee15cc2347ef4fb785cba117ad3c841ed4456c0bd8b20b34d574bb9c7dbd9ddb6b1fcf0cfad28de8cb4f05309dff5c312fbd9df59837fd31e94dcc19f3e8ac8f5e527cc81ed4acb89b46cd6fdfe36c4605537c015e5b57bcf1578cb942cb5f71e6b5d71c6ed9a587ae99e0f3c7e453e5a82131f00a7375f63457ffe13e1ce52637a1b32ccb8a389965dd7bd47267ec71f94d7ece7bbec9df136e716e469ff18bd957d4ce79cc5c831be495ae725cbd7f62cedeec9e2f5694fd786708fe79c2297e5e9ec445994bf9e8cdfefe66c726e41287e67ef385f1b7cffd26dcf29727f1c77509bfbde136dc86dbebb3e736e1af940ab4d13367af178bc9e4f2874fe9c367876a1fc8006e9d3db667df7e3cdc6291de9de03a50f09dcd79a87a1c618f35b0475fdf300b43f455214c97c41f6712fffae5136e79cc353f71467a3c7e45d133e77abcb9cf982f16203ee56fae0cb4a1f09df0cd4e7e53465e3f817df66c9c11f6a295ae7135b44e00f2fdac9f74f6ebd9c1d1a5c9cc6b1dafe73cfd8e1efcae2172996e0b73c3fcfac51961d7fcba9cc7c33ca8d9376f7afc1ae8a77da09ffcc737a58efbf866b7c351df4445377dd3d94df8a6eaa1ff349012ceb31edf54f137553cbee9f19bbef34dd48e6fa2747cb3d3f966f7f3e7bbeff4e5642699699aa671f2d36261b83ce89b70fbf85dd1d9f9870a5a7c45d1315fd75cdbdd6500e6d9a7f12665d74dd46bae0aa4eac3aec5235c3657cc671f8f74cc2f57d240eed0170bf4f6996b25059fee3e519ecfeed335297cdb6a1fe8d33f29bd4f1b2e16ffb47d26eae762c9fc82f0393ff9848f25c5d77c26c5b9621ef38b33a27911f7993c72356caff9b63887f0333f7d13feb5ade75ccad93d549f897aee9b49f133dfbeb962fef2897ae8abc5c2a0656e72cc157243d807b7fc7a1aaf971ef8e89a2ef36982e0e391f8f0e511edb16fb5d816ae6fe935052990d5c33c6f3cf1f34412c3c84a4a4a4a49ac832762803134d7aef5ba7e3bb73a689083db0f15b8e89ab92186be5cbb724774ac9491e2b73fa3185ec74a99287e1dbe72fbe507d5798a57edeb2a16e2e7acdaf652ac1252f3c40dba94e08a3748f0c346378424e067f4f331c618bd5de30da4bba7823efd53ac5a6fa0ae81b0cfdb675d0a0de02083861a3669d84028b6f0b1cc47d347333e1a211463fca6d1cfc3cf5d3f4f712ff8e93f71cbfa76f964a13563109a5ae6d5033edb9555ec05cf1eec02a9d6b59adc9438993804bd8755ea2a4f2815a2de435746128c13c3300c5e49ad69d94df20c73c843e730ba75d5fd820531baf5ee6ea89a286dcc436f8fcd74eb6da4651eee98d1ad7b01a1370c5e73c9414a040715b2ce9d36161848d5a908593a1addecbaef2733699c42f2811aa4a00658dee8070d0dac5afa3d65fb9c07c9e872bc3d28c7d3f6d906da619ff6f69dcd69d04dd5b78a859f210d44e50ae9555c28c7db1590a63d68065eb1a947fba67f9bda680892283250620a306fa4693e3365df7a0ee60d595dba5c20b7f571aa1ec54033b44f041ad23e11aae920cc41076dd0cdddb25b78c5ce5c788575b18257d8e7050c0f7ff28a6e3e37908c2efa0c38f019ddd4351f5d890e3c28f5c05178358d54bf3fa0f9787d4970c31460eefcdc2d908c6eb774dfcc4bdd54375b3e7a0f493ff0ea0b952ba05771a12978b5de25065ead949f919679109c008d366bde48d7c0bf3cf8d02bc0fa0e03a990148a2cbcdc16968416a44262187281540c8c521fe30beaaad442d8c36756e3350438638c51fb52554729a1412fe6c65eb8dceb6ae525c57c767763faa3f2c28be86998cf8875efe5cd1d9fdbebf7820a815f3482a6290dac5a63c3b7efb2ea6930faf92db3df901874d37cfbd01056b191aef9766eaf89aef922fe9605b7b718f2bc838a9b945d54e65687bcfa0eeabafcbdeb533943f69d2172631a9aa7cec2cfb396f90ba45e3d4a0f49b3e78d52afae2ff07e73888a051f5a876b34e4d5212bb39196615ddb4740ef56f5e8f3bad42ff5a24bd55b7b7ad1ad4f46d3adef1a9a1655a36b5174d8b3139530996e1d26c5f8b964d5fa7a51fc027efd45e16b77372bab163458d0a0011a251a98f136385383d6c3a5e818a63ee177f7fd4de91dafc819b93cba5ad922b821c53cef899df2b631ddd456d09006ea54f08a02755c842ec131dafcf63ace5a6a0006cd9b4763064d1734695e5b36da4471997717100ff5385fafcb71ada594123349d92d4d5f513b66ea26ebb8e99bfc98eb7cde5c363f8c197dccf2c4df84ebcdee37b2a25c7bcd10fcef5c373fa3675e917429cfbbd96e9bbfa4af773e974d767d73d97cf4f66baffe8a78c2bfe075495fefea9f06521303b0cbf7636f26f595c38023be97af958581fde2a07ec93389e7b2f9ccb9136744c7f92b6297cee9641b5fbee3721d1b6764bde8d271ed7278ed307d3ad8754cdc50a6e5b5eb3166666679711e3deebccc773c1e1ed4cb790a5d773e2f62fb72bed34a9fddcb4f625f113be7b2888561ee5fccfc45a3ed257be6fbf5cbaf88fdf2cca56b7b79f62535d08ecb225c7a33a99ff7e3c13cf3e8be45181965fdeb33a99f73cde75ef96ce5fa0faf38e9db37218fa47ee9dab7452c0cfbcd27ac976ef2cc157243199bbf3c8d17333bc3c7ae6f8bd816e2b7e3b53ade81068e1e42123dba40b9d4ebbae013297620c51653b8588192d0b56606368038d0023bd40eabb6bbbaab0c4686115548a8351a2c7854878613afe3b58586959f9d4330203c230c11ddce1948f8d9b91860562d98438d1a0871e58d126b2cbdd971861128c0229734c880a383316ce0800b3596cee82ce9800455b40183840a4ac046195bac40074b54e82cb980892a78686026ce951fc029411b4251cd9252ce521222a822c86beb0c34cee8e22ae3cfcc2af79b523a0a5f246587da6fa69e255c499fcdccfed844cd4ebaaafc26f649223a7f7dde7ee337bbef9e5f15e2ef6017df84aeb08b9a59d0b56f10425e1ea47162852d6f54b10286cb521339603017bcaecf4bb1ca8c3836a520e267f4f3d7755df2baae207c70d1ab043b6600664a1512ca088029707cd9028d08a6e881d08583bf6680c55f331863c5e111da08981a6fc26bab8c185c8c75a129afe06baa2bf6d9ddddbf6a46e26f85013223d138719e5986e74f59bd3d84196cca40c2ff603e597875558fd14023c6046fb480a0411042f1061fbd4369155d9a17e3a7eabb8c30be7d07d5402c26e618428b11c670a2cd0984da8c4ebeb6cae8a26b252b84b458424d0ed480019b2dd278c0162c8802cc0dde14a10856d880958519699431c61073d80007358000a7805370843349fca822078f2a104208239b2e8e2a683a308210e314440b64b040870c3082c8796d959125abf2c514448c31461e4a104669076da0516283c609ed04417c700454c831471341f8c28b2e684006111c194d5011e20828c89041810b4680069c238a90f55002e6064ba172d8208e135061038b385f9c31c616dd183248418c31c600a4b1042c55b698011b6008c5a531bec431aeb8e2c2185cae31a2a8c225e0f1da3ac303127037883bc6151940c151f4344abd3cb344841084d093b03b630610fc44e7e3b575a605bfbdb6cea0e067e7657406970eb2704209166451c51b3f3f5da4fc49a5180a08533dbab022cc9817b801471b2135e31fc0e5870b971473f9896b064d0cdaf008a051ea2574a2eb5e5b3248a2b7872d19142184107c083df5e3b565c6cd5faf2d333ff8e93f48d0eb52b8c34050e8babe6994facbd5934d749743895d977766d8fc755d57fc44d0e9438750a452bc05c214339162268c523d6286c026755ab31b6cc56f876578e94c1a346bd47c676d9efb6b6566a4ec3a140b8c4249d91efb2bc21fdd3f0669beddc9cc01e6a11aee88cfc334accae0601b63e966f6e6e2867e70956e666650d91b35ad5fcede706999d858b162498b63cb986d0d952f673837272d4a8437c6a2657e12d33eec12c2e9a8a8472b6fa67dd8cd983163e624a6a5ecba9398931835300ef80664f3ec1967c43fc23469bafe60c713fd72fdf815e16ffd44c935134766e60713d1656f78354f62d8c036bc9a9999e7d5dcd49cf9359999189450f97266dd6811c36a94a630d36f60816194086f3f2fc05729bb0e85cae0b08a7da9d356a604020099945c5018b224680becda5ff64685b0f3121d06d350316daf42980ebaf168a58150304d03656fda87ddbfa0ba745b3e6291b2eb50a8ec0d4c03d33cbbd6c32f3ceb44f1d7a58cf837aa81a4b34f6f9fd81fb76548743c50e0637e94f3d877f9ccde3cfb05659f07c9c6afa83dfa55a43f99bd9962836e78c50d3904bf6758b57ebab847125c1e726e94c8462df10a472a10ceafc33818a8c5b4ef6c36cf5cbec5341057867dd8211cf9cd6e51c1bed9669e79caa55e7a8e5ae21b8f049bcdae5c6a2897e4925cd3402a24512d97201b91df1fd7f2e492966eee9aa5e734bc68960cd5aafc17f52b3b8d517013ba65b7aa5ab585765d7fc8716eb0155c41e5fdb555450cde8a38fee7b565050c5e0c3761b4f1eb5a941ea5ceedfa92b00bbb5aec1f87c4f4ad0e3923985fadbaa5b9218d5e0c433e1e1a48bb1bbaab543fff059da0c381d33c3b0f1dbf29bb0dfcda3cfb0c0aa1dc544b6ee6a1ebdcd68a51a04e7dd4a1eb3e6c6f63d575842c59b264c992254b962c59b264c992254b962c59b264c992254b962c59b264c992254b962c5a16cd92254b962c59b264c992254b962c59b27490558aa14003230d0cce747976bed2ad8a55ec26e89a9a16a01fbfb93dfcc14a7473b13c6f5ab6f04a73f6e5a28457fa271dce6ec4fe4303f5707665a01d9863dec3e3a17dd879784a7ae753f691ce9fb28fa6c33b9d3cc79baca11cd722376432994c65dee4ac6f6ffadcd9aee31da7f3cda43f79f4585c77f2a0ce399d9434d00fcb359ab6b9c60d6d9c739e0a603aad88e9db5408d369fbfad1afac378e06d66b5ebaab815668a76334b916bdbdc2aa18d447d8376b8bda27a9eacc9b3bdf5d1e121c8ace1e1dbaf6ec5ec360e327aabbe8702687c650a1d6587333ec63d1e4fbcb9fc98b99c7abb92193c9e4ac5f6ffa2be91ef3cce4ad43c97a96d1377e6cc6b68300b73c34132609c22d5ac02d708bafc7beca10b69cbdcbd55b1ef6bdf23be6835883565efdd257e81737a4d7857910ef62ebfbca62f6dd4bbfc9baa2df11f6acbf0ebdb8bf310563c86441a36504d1c2ae775b5a6c45358e5f3a63c51768055261b3a635a6b9f4963771a0590263055f612b6dd894696d97e502e7cda75134ffc2cffa7eeaab0ebb402e70dea44113064c63e92b6fb43153660aeec24a70d4a4111366b52c1637467a66061ea253adaea678880ee6a87ef385576f6ee84785e46c19ddfc7975a8fce3b36d199d7a5c081bf669bdf7bbb4198babaeb3867679b36b56baebbaaeab5fd334879ee69737537f7d13d54df917e7931faac290da163cf0d7a5a3c379884e59a5e39b51beccd2772f5d635eee930e5fae48ff51bf8e4f55888e4b6f2055fbb4b7a71606056c0bdd7d13d5e57cb33b7d92fb66917ee89b4703abda5599b72b6061506dc1e48d9af295dfe4935b219fa6943fea87df0c2aa49d97e8a06b9f9ad44cfbe54df9d328c583a47bccbbc73e093f75d3ed85455b8697346da76ebdddd9d683d2b55a4affdafaa208bfdec9dd5dee1e5d2084305e266821370cc33099f52843db4c269349db4c6e9c368ee3b853ce15049dadebba4e47878ed756ab083bdb8e1d3b76ecf0380961c2d6a3478f1e26a0e0123f361f3e7cf8f8c1a32302205b0f37b4dbe33d3e38230b84d34e6094b743a7ad1696dff1d1655f18856dbd0d8b4c625784cdabdb0e37301843c68c1ba09113458c7123175be6a0128695248268238330ba74618089420a655191c415247803c712aa80315366093ec268a2082914083d30c29b2ace00230b2308c5189459b39579238b1405786d7de1c5f7786d7df1e51263238c9e11fdb8b301600117a5295a10042410417ff00332259042046068c941735759f30c9548fbf6d1792edf8f5e11fe8efbdd1ecfba3e4f91e5df57ee860efbf8b5dda211f5f8978b9f54a8001a78f542d00c38bcf0c2b3cf212f740ad43520230d1a285de1461942ec280562695952c38c125a64f1023484d85d81582478220973a0a902040a9e1062572910048a98811557d4c8c10be4d0355da665032d54f5d985425f5859de2e344da912599ff1152a110de21d06ea7ad8150628f2d7b1879b47b8fc972f5743f7d3e87777f73b8ab29f177f6d79d1e627fc8ecf283d07292047e7b473e75d8964aeee290cd2d553502298aba794c8e5b3e7a7934266015e5778de61a0a0f6e11f751844951bd231999cf31966d0d1133483b61430830ede37fb060eaf7a4c9ee3416de2bc24fece81e40cbc82a2d3ef5c879b9a1b3a99721c72433afe33f4c2497bdd39bdce6e235744c737e1ebf01d3e6408ab7cf81cf2ed82cff4be5d15c886f6619f3e80f88cdfae0b438fcfeb7bf5c3e77682cf8e473b54223bdedf348ae1db7f5835c4045dfbfc31f2c237a481781e5021ec3dde07b605761ef61fec27784b857dd86d88ceeee39b28a0be8932e1ebf1f1f8f68a0a61dff17e5021ec0b6767d128117ef61d0fa240061744b1b40239b020a3c501948a40032bae70424c1a42ec41bc3239bb0278a56ed2e19c8e3de2d977fcf0aa75b87226d7117d4dd13766bf615408fbe42e6ee8866bfbe1157cf61786f02aea7439261318715cb9c11180b094c6107c8d11485c397344112de80db36258c57d0b66091d84428c830dacfac1f23386081e84a9c10e949cf881c2c1620c1a4a5520b1244687d084155a24f1850d1c255033569ca104a78a1928097103abd8e49ba779504d1f5211f84751dee447e09b4e5833bf79fa5a7f5b8655db62150e36b4a22c155e5df96195d60a6b6bc1045521021246e0860470c81881b58412644ce981d252173037246184379e88620d29e44040126914e1cb0872500432cade3e4d30e1fb9b26b06b99a77fb51a4885e62eadf4e7a6f1ec721bda30ac62ee9508f6fa270c7939c1334f7aab2db463dee54115d2bead068a3d69f0587ae6be7dae99e71398d957ccb7603e4845c7ec37f06aee057835b5a503afe68f0dbcdae75da38499cdcf9f67fee1554b815858e0484205282893051c6e08b1af1905da82c00512843082244c510421f65d52205609daf801992b5eba98a30c21f64da340444f4851831860a9e28c2f8458a9877bd0f011dd940f3b253193b93c0d67d0bcb656b006fa0f41bd58e62a3d94b1eb50ee2ad594bf3309fe99876e865bac82be54a820dca26aabd5df447d1241bc04a3d034087441f681aefb291539a49f2a9176ed6e96dea62e44c7bc2a52884a1517a252a513527561ee20c0d4f9e89c1620faa6d141940a619e0ab5a78b4687002b2e747d7193becc467043845f40099941803c94eed979e8a52b6ca59dee289175f555182084d0a1b3077db505e8cb15e1e7a13ed05f4041082ac8439f1d90875aeb8e36906a6bad210a13858f5e3828f22f5735900ded137d5369aa25d547d76c800ebfc9e37be988ff2c7ae81aebf269bf1d15d2a9aa3df33655b54b4f2a5455fbcec3df9e9fbfa328dc77830d5548be4ea0296b5d9001e351396464a04c346537ab06b28998aa0ae9642719b6e9eeeb724535c6d1c07abe30a99d54ee308a03ab3293a665507a2a3738d12f85ecd053214ce71ac60d6d51faeeeebe66dad52b935bd749ed3a4c88ddddf69eec376f1cddab06858231c6c8e97cdc2084fcf08aa84ea250dbca3590f2827a3af3984b18e375c5cb932a75af6518210ad4cc36ec356ec032178c900d8410c218e736bbbfd8b5c63ca46d869863dc10c69fc618a4e1a824cb94d5b12007a16388686624000212531440403820120d4704224d15561f14000ba0b45058a20ab424c86114858c21c810630021608c88008cd0d09800f40f0c638b469e26bd230e98288ddc9bc24d1fb3585e0cd9468b594011a94601b63a690eeb2e266a487c948927d4143bf522d2db796244f0bb6edaafebdcb440be312768b2983e671bb2b2b6a1d67edb566789620659ace33f194a8a921be0b1c38a594c0eafb24a5103c5552ee5fe9f06310a98333fa6a1177262cc1b519040bb1c6731153f5fc46af61179a5451c1837a7ca0c8593d90e2bf087aa533eb4363fa66362f0f10da10f87f75f1160d9e4ca69af7a64b731bb74da88abf0460bcbd9377eb8ff83ca2dcda3e1b185d77a722969f118698265c58aea1d32816d7719fc1ec998568c540d347e4e5a04d2fd405169fc4d39a4a21b167a8034744bc485fe7d70445b58fa4ed54655f137dd0595a4e5465fa90730da2dd171047bd829d9341f1771f530aa60dfb2d44d2811bea6228e341d9734daba9f71bf0b800a4a3d1c016d5deb60fdcb6130e16df5b435fb9499a09ac060bc88abb06415564be5a6ffa2e577bd3b8689755b87f045c5c70d137c066b8647aeb9283e84ee489a9a471b8a250c169e2a6fa3cfe40ff80b3b675feb5ae24638e644914f6a2bad2ef78e1774e61daa4c4dcc95969e3518a67187faeab1a748b7850aeb0201676f8e86351388171f08ef32d05af18896e0a5d471dd1f112e7b4833d98069953ec3a5a4925356291095aa4bf134f487c5ec53dc7e24e1a8c095028405cbf0767ff21da01d9a541daa18afb1644ffc3027bf82108c58f4b5607cc97ff6135172d1a585a5074405ba64be41476bac379a7d92fcb6d60f289c002c62a82bdde4f97e78a462f0ea2014d366ee64ab4315ee966fc2ca87c700d9623873512e2c72522f8afadffb712ed3f04660fb02ff4e3c5a620982585d1c70250cdbbc77d574bf61334475db3b278706bfc0847e4fa2a2d24b500e76bcd596c492da6537026843d30525e3a2d55bd6e1bd4477a18de7b924ac6c15bf115ba1e2d4b33fc8b48bb842119ff2b38505a09cb7e338daff0a755ea194ccce3f2a2fa255ecb271ac591fe7dd55100dc14a9b0dca02a45beb3bbb95ae7753e75c7b5cb1078ec8917bcda6440f9457fbfbbd5edff7600e85b29fd3d0fb53fde9cc6ee8f9a49f3ec0dfb0a0255f8cd38eaf5a0b36fc818392988f828a7b104ed184f60101974c611bcc54eb22106f459c2b626815dd2a5895ac6ae3e11183856aee51194bcfd0270fd6a0abc266f7d6f1edc17b80af3b2dd9510931e4e6ae48aafa931af5fa228c244870597ae848c097843491a5d950321e7e0ec55271bd933f836ba0092545bae57aff489146b87f8e928af8f57bbea36c5a85d2234fc9e1db4e1829deda15cb2b1948b01e5e9323114525401f27be01a37813612cad467c836aa91e1602acaef3f93854ee459d89ddcab95e71c3444654ea0c1631c4da25e69474c88a03ffc42cb2998d468160408b1b6456b46c284dd586abf3f9542e56538356f6417c137c8e1dc92a3262d7ec7c9dfa5fc79a61e5e33e0dacc1def9e34134795f7cd1d54c18682821ac55ee7f519a2c7e4a2dafb18b23908197861b54e66687afecfdc4c0609b00f347829b45b1b8b4f2c7de0ca777f05c60eda0e7af9d17767512c19873024adfe3a251aa7dffc2d1a8d0184378f0416430c20b3675e165614495b2b6df9a4b3f16eff2a6b2b239329bb755701b8aec423e074d96e4c5020df3fd2a44d56ebe571c3aa34ceb8975d20395ae73bcae9c7f538f74fcfe0c6ce63bc23e5ecdc9d3a5e322bb77b6f5ec4761f709dc8a4559c4743a904d387cde1e2b4225bb71c40f08730ecfcfe43b8dd4440220d0d854bc1edea2f4de56e31a91a6abf65660d0fd89db123f5dc6ddf1e122785bbcf4df050537b61368815d8ca95fd36cc8d699c0a75d60a2cae47ab6e97850ef906824217c4e51dd2aedba280f8b83130947bcbeb050b8d68bb4500bba08a70ef4701a68b61cbc758c14f7c1a734ed12319fc872a26ef6697c22e841a761d643640b0f4935f199d3788f6328e7d92d8ad3c19c7b2856a740d52398a86c2bc161668a0a8c52000c159ea9dfee555d7175c371aee093bee505ab91d238cb8a7c4ea1f12c7306674349e6df8721805b4a1b732ad9b0dd4a0256231c528b3d258e0c3ed4e4b6dcda8d65183c87d2107db38ac28bf52b9aad0ca78dd9046d0cc6be3a3596fc4698589a5c317ed7c79b406bb469abdc0f159c01dc75dc4ffaa87c5f436f98c73c000a724d82a8d44518ed25cf930484e7eaf0e601df602487fe7a85b4a735d84b80e4b411c2187359f45fc3344c382bfea7e4c8a076b7298610baa97b24d50173d6ee222a8c032d3a1135425ac553c29739c8207869a5bbadcf2388f63f11b0c45cb970a23c14472453605c378c015554df9037ad7997d38f4f753cedc4adb6d6a65cd4eb0fecf4884ef1f4502ca554505325f56c39f3299d1ad4e378b622def40342d38dea50266bf274a943374df4860eb27362daef651775c933d2f57cf886c964ab42a19973cd87b225ec72ceca6ee3daf93d274b39a91f65c19828d123af5c45029273ddc1ed2811d90ca0cdcf23b0f13dfc1215d56c83b6cd61ea0456a8ba9d44733a791a2146e90282190e16c26d4523aed68506ee07ce3d121fe4913358145fc2f288b32c3e1ddf01ff1f3e2e1730a4b3864691e36a33e541749ffb09fba0e94aea336990a3cbb20eb138eee6da10e90ef51a27f30380e0229450f6e3d4061e75726c6b70235307332542acac56db381498ab8b3f0b773858f88cf392ea1e6b9e25a00dab566f3adc54fc883b43acf6e6a33050543d5220856b07b3d737d839d3af542d0e231177716fff1c3edff133835bc7fe2167a0a0ba158f08d73488608acac4a1ec51762f3c6731493e98a067eb193795ce221ee9455eda27aa2ab2548a390df17121a3e411740de0a55d6d147a2be6048cb17900bf0d30df0ac26125c59c05dbad5a37d0e5f758ca55ff294ce936178093a36df7a69cf629ecbd2ae658b2785baade697b94a201c229b12d76776e9544cbcfafbee214eb120b5b4c297085f5260c0f122371f4ea8c5d19192318a8150dc4198fe2af6c28581dd684c5bc4579c0087a129ecb663fc420efdc5c4022fa90e52c559cfd17743cc757b551b6516e3d97c8d099ce31eb815c0c0d6c5c7d0b7a641679132e784b912c0de4a4b3385fa3601574012c9be558602218950c26ae0bb51a08606624df26eeff7ba6a852e35e4f35a996956dbfd47fba0e3131fdd4eb419872a47d23ba5bfc98880a053aaa2d21b14e8fe6789e924003f08b0b6b229812a4e3c03a86e4cc82c56a76b05bf7d4e550c167cc010ce3d87eacb1b043d69b3978b657c732f777415deff1fd357b19ae6b5a18b002d57c4ae58a4d1d462299723bc93fb3bc3b9194af91cfe8e0ea6a2bbf01795126a96ba69ea2980d6e88f0ec0242f2ddad280107392eafb77105625d3c65ced3e6027d589e720470170480721530eacec86708d977f356b361eb25ab94c69da7fbde79ae89f5c0900c6027c6713a20188248e9557f4df7560c8acb22cd792a8f50a7ba66b19c85fb858d46f5cb37faef58f495f49cb1589e96fa46fe2d0a21c79604b4dfc3c0aa4d3015583414d7e847d7de299a7a0d402450123cf9042376eb852953767d360e578bf8742e24efb0c11a9be2a281b4bb5c745e9371336bad82854dbfb08a7bb31cec4602e6d46f889447e620182f0d748a113023661a506c5a8d8905152054315a337a42b5724b105a6081fe0c0e16b0a6e6af3a7721600cc90562ae812655cf20bf5d4e65003a9689b0564b43b52b312d73f9eeac5a34f3b81c1c62c1981a8d141e98998f23f68db71f102a921d937522eab846e522c4849d6d2ffcc3ea4dc7a9e24041b6aa111c0e3843b812007661a4df9a362033433842e382d0e218462aaf5d8ef4f2a40a5383506b34346ffe639a79d9b5bac57f6fb47167382f88a3a14dd8b5d940ebc3ded973d11a7e59f9a03385efdecf6c7f421795c4dadf011c1412db5f9db0ae979a7c3130696b23181c0688335058c2ae8003555766c888bfc6036202039a531c8d93960fa110f3ab33b37045a0b1f41074ae5e5cee115dc3f9f1beaf7d083dbee45328e8bc891dc9151d5310c7dfbea6621a3ebb6a71015c8a1b382cffa015b6d78d8b610f544d51bf1a45b9e107dac18fdb84a65c98fac414411d84bc3b2016f31f4220eb009ccd3f2c811f34775c0bacc852cc2aab06a993aac74b3e04087c0aeea7b274c5f9b6fb356435f726ece2ffd97563457bad56ca3a844419b76bf7743709242156257ebfe8e1f505d207798d6542652d698eaaa4c00d3b93a37ef99ed522b465fc11f675a353f7f5c054d47e11f4c4a061e4c57597a636bb1cb788a62a67ec6130e0f23a7bf10dfd9bda1801cb87373dd7cc81c915984dd991000c90e870fc4992543f8470bde6a401e9461d5207e11a2c2e0abb6c0e2eb34fb5c6bf555fb78b7e78fcd68a331bf85662367dfb4a6d7118696797ae71df5947fcf9179128573fc1a6a2d43b611669ed92fcd32c2f9053f5675b83c35b38483c8d75ad268a91300ef6108c89a6b27c0d920f3434058f7d90d514d15165ee95175f19d89b10aa2617da63d97a555058659864533393a6c0c79020095843281cf38c51d95f67d84f0a032b0cf3b1e9910b9fe4fba608237e3a3fedc5b139f80e53077ca3b1a1ec02c5fb6a6efb68ebc4905e9cfcc8fd31d6aee3d94fd29ae397175980451a1e5f51f6dfb8619b6e2c7664d3bd7fc94777a3ea9e46e09f1659ef89e960a9a205b8aaa4829151b7585d43e09beaef1489ba410e14fdcc87506745a358f3fcd4435e3037c1f007da92b03162669b1f1b1678460bca4968b755d44e73fd38d65a2dfaf632207ad84f4d8f0f35eb1b2d5239e98d5520d041a466fcd2713ed0d831b3b6b8d1526f62ae8dd6b0d5eb5d53f138948a7cbf834c3abd7b35640dceffea2df6660c0b4ac469dd60c6ca8f8251dfb61b1399da5a4de0b3102e807dff9cdab5c946dbdbefeb632d54206fa088c1cb33829411ebc24e4cec3f8f99865e880e3ab5fb00244799324929cc75dc0c39da0f1d302f1dac94ce082555854950b541bea897248697aa9cba58cbce16b55de492223c18f3a1221cf5b8289bf92e07eed09a533b9e3ffa0859f840f7e78352af87df808f7b872e82231d90b52978ff3747babac1a6a0c3937b479f68f9cf9cb8ee973f2fee1d9d47888521871b64cebd3a2c9b2e316f703c37ea6b959668a822fbbf072cf1e872d3fb9c3a6bebf4726d8256b216f4c210a129dffa8cc5e2634f54b3069cee1342c8c9a6d16e4f1cb74690d599d028d653b5af8a0ce8eeab66049cd6c7579da2557832abe30126336c0a9028608380d13e39172bf50964f260d2e9cafc67e105af6a68994fb1bf15aad540258b72fad748cc33b89786bef5653dc57179002e6af70b3d64d5899bb6fc1b709b4a21ee3d5d8536139f89bf703aa5adaf01a5dfee4738215ff5ab7fbe1fabd29c4a259d2bf57d0300013c800a1f2ee274421a7d4a21afdf44aaf1e317949223e38d2caa07d70ddd21d62ac3c7fd79d63fee566e23baaa75825527702ff9f0e895452f3225f2aad03d748d4e88a594ef64a05bf66e6678a24bc388039fdfcef666bc1431d047702b92f2d7a94ae4547706ba12296ea6152b82aa91482224e6b3aabf631573540fd2d6c35cc1dcd9e329ed41e9c444ea71cf2ccb3b7402d4b6646e355008b2eefcca9843bc5fc39308b259a2e1cf16b0be47b67169c1c5ccd2490a3a3ff7d59415df9735e0b7e2f711f9d0a7d14b567c40499432e598392a9c48d6c4f4cc2e2c82eb790541af7cf11589008f4169cc213c549b39a0beb8d262c4f0c201ee748dc10844d04e780d7392731f37e8600de71aef921a713a6930d4a682622d78c6348c78850c1bf20b75e1d2b7277b972006747d9b8e247b98348d19203ba9c55c2ec4d42a631aab15406f16c19ea3438c4b47ceec7350e402b36a2deb17f592e852d14f41f49ee60094f52696aff93105d5a1490295de9de82c96889b247b12e2a324c265309538b48e7b9f7ff9bb10c58d42b7960ec3287fc085af0ccf47ff7c0c7d699ac099f0f52ef12fbc3a1ed408aeed72ba7c1d24798c2688b14e6b1a8e433a67684657e695db23400293bc31be002f3f8423d95c7d653de15285cda8f031e8a56d9fede084d21f27b6d4c3ef7b026701b92d82472c6bae8c08832b09ae7a553272f470bc36ede1fdeb5d70b7b98df88eea7beee4fe7546619036d5c7c9a8bba8ff606930b1db53f61eb245d04c30d41878ea73889166398133037ed099fa2159910cedf6484179f9c681a90dd290d870f82d6daa4e17348422e519bfab74d4164c91990143402b1be2dc8df6592a78417528a611b55093aab15a2255c1672d7e917294e7707ba406dd044fd9de14bbe389b34c0ee5df491364b6aa6788247886bd981efec9975dc5f2e39945cb4bad367dba333efc56521f57bfbeefa1c1c4ca3b112c12012e73ae65608c3a17f12fc5289be6a0b40f865c1c85dae78bdc9a55a944891bd9d9591645df6aae48f1fedb4870fabd63bd66009315cd9e42bc95e5d65300d79a35821576cc8dc758498593400daa4ed2497288bf7bc8303c1d8e69066a46766ee19dc710ccf7c638aeccd617260c3701fb120dd207c5102c54def068b6c12accef365f6b7fdca8300ca308382eb5f124ccf340d58f543fbeece6ef0fc027a3e56925d3d96660bbd26aa825b492d3dbef7ad853e88c08669698400961687940f07146c95fbbbb3c27f9de6530843d8d163e541323a07eab94aa3f9f3fe2809baf19d268fa42b5d82ff0071fe6253bf9f0f89751067e64664d88879d183820600ff63640a25c5b2849b1a722d521e2f8603c6ad60b3eeead78219f8ff207dd85141088407f9fcdf48bbfeb2e9baee6315c851f041f4c9a08f0548cb839cf3714efbd0f6530ab08659776f1840ad38485132700ddc00f15cf3859eaffc567019fb880dccd8971006dbd1d9580846ef70a459ffde443e858a416b8b99068511d0f2d15494d43ab2ac9d0e918d27c74f7bd420f0986bddf2b19b10d3702d161f55c326e9b0f77638b72dd62b938b66b21078f727af3ae55e96035c6ee3f98059ce29a4d5b7a96fce8b9e892874f5382c4d88cdeb05aaea8c5d06284d8229616abc456629458524c29568b25c410b15a4c25e690deefe3e5ba99fd0f87eb32fb9f1ef7cbe47d5eae3753ff69b857e607fa6bdba257985fe4ab3f5dae9fd9f978dc3fb3f7f1b8bfccefc7e5f233359f167716272321c8e5871dcfa66cd82d5460d9ce09094b58a13425bf5aa04babd977b52475d88630d8ce652a9bc2ca2f0559b3b000a07668cd29df36bc5aec40c8dabdcc5346386b968ecc95720028263ac9bf122e5d3bc619c890bbd31eb130c7cb919293c1549552140eae8b9d6d02439878c10b7ab39542989b89b989bb73b66b11c09877657c6a58fb78d86dd3376340e104c23b906159834671b87860235ab8a3d4c5a18a4ac4c9288f73e8db446a487301b6836b8d0c4c10e01ccfc8606947eb05baba540107429f50f249d2b5f33c5867498215db707c21455f789ae760cbe8f3dc55d1d34b4c0046bdafb94bb78c64dbb8bfa625ec9ab6c0ac3a840718d98b0cb210ec5bf38c794e85933f950c9a7c9ed5f2ccefadaaad0f66730ffdf6280a40caa5f73b5a16c89c90b48ca04c1c10a1113220dc44d7f1f90b2224b779908a52245d7cb224d1e42a7cb5e6d5531bab1b477630e73ca900516d8e53f9a454cb0b4eac398af34f3740007020a821d52536c2217d3d7cf5b7cd1bce86a1372aa6e560068a93dbe816a3427a4ad6182acccae113299e78b038a13b44532aee7c9c7e102241839cd98f9735f319dfb8f4e108943ca884af8d134889d97ebcf9c76f5913be1b61eae0a6cd6529d79758bb3777763ada2e251858d2a4cf84fe5465d77c9b803cb705f9bfb46671d9c056083c366a8e8107db4af8a714af5f0f9c3c04095b3d1b801c7a7e8f11d71019df15951be6e9bf685eeeb2abc8839782261ebc1c5f2654b5dbbd61e8c01f8ccc0d345e1ce4e695d31d12c683d5b54f39850f41a932fa7bfe29d25eafe6a4effe70cdb86057b32f1a9c18fa7fd68725de60c3c8853338fe53913586e4b6d9ff5c589aa8d590f5fc4e6a2b76f1467f82d825a857d5c945df0c1c76434bcd6c2fa0730529a3ae71bec3f93661a8fd9db88f570663058c0268dfd432c83b7b88eeed3ffa7f41a1a2568560a079464269d0e8460b3fbbb8fe4f290210e0d3cc33ae14f05c4404b09d478cee03335b65d6a1faa5e03b9f3b9336e2bdef658efd73e23d0525bdefbfb3d75ca7dd305e90317a692612deb4f5c5bf471db4798eafbf78a56ba24e24f18a542c9aa3982295574a4c547aad9b4a7ab5ee87f81fae3e1d1cc0a026e1e298e80e592343817d6ff77bd6104be681f56eed27d624ae0c07c27b81fc2eed2bd622a64c06f2bbd967ac2e310422a3f2b20f1023c54f5393f8c7b93144370119e80010f7d31c0c7d6ab046b54c88c29b009470688d3b9cc61d56630d8c29c5018d7c288d3b50515ea25ef74334eec0a5116f812d16251fd853ae4f4ccbb84633762709d18f4b2d386a8aef427885a507536f02142613bac697bc2f6951e79da28287499a0486207d3ebb94b7be2b54e521e618f81888bf4cba72f5e2b7c414fec27210620d73ea78e2b9c7607becec99a79242a7eb37de320ff046f8282e4e9e93e75497113bfd8fe9f6e1a1a5929aff41682c7a4b53864f62f17540bbdde0be30e274b79ae4c965ad5e212cd400373ece183160c8d03823d412b04b2b75730ef988ad4e139d247613945386c2cd23c2d7bdc8f20b91b1137dddf00e882689f3a5bf69ad473fc8051e48e26e65efb0f0720a3588681f05cd6e143e32b013b8cfff3ad890719c694391095dcad567c8356e43e7af35a6e001f67bbef8409dfa85f412036f7c7f64213add10bf60d90920e45ad72f93dc2a4f6dd710fc698f6d0d362e06cd21db980f895879e008f520ffcafff913fec6dff933fe36fe7159016239a45a621ada66f9e77d330cacc76e7ac5d43a97c50dd22f2e2814747ce4a73698dad8892f9e86aba6bd43a56e883cdafbc469db6bea9c54bdae7546d66668d23ba9665df28ca87da2a9f9ab7a5de517a9e5ae49e35085bbc81749eb54d3f76e4063eb6e6f215db74bcfb2c66c8e5f435de916cf510d2fb8bcc6bbea96759641b5640e98ab64c965582a53c09c0fd93f99eac7f6ce8b526a33e1fedbc5843366f86c2ca74a0a33244e75421c7498bbea84049ca26533b765c860008b814ba7f05d5e67d8786c970b1ac3c7bff9cc60f38a8c7750a81271d9c8073bde2d71983ad9e818653574e8991d4b4d1e841b21d8116b9920a253751b96bd6e14877d8d871b5b4f35946c99dc026a41de58830f2381b918b6ad59b7483ce08959bdc3e83217d6388be12617fcffbb49f3dc9e0319965ec8c5098eec8547f77f5f620721500fbf80943c8553ce0049fef9b84ceafa23f823e03f0af29496eb740fec981ab42657df7cdedaefe26160e549e93420a9171a8d44909457caa6c51ead49645b3db764fd900444028f4b00b1d3ecbb803328e1a5f456dce63507ec11ebe45d9349c08806979c4ef00d23d483686bdc23bde33e2706b23e4be3d0e59ac19b83106316040a2f370159404bd2757cb6e62262856df72ce0e16226797f1c40aa81fcd34319a5a048e8855aad0d5b69e73d16087d18f49b257664b5c456d0cb1eb0f03603edc3cfea642a15a04c65b04647c5b1216faaca3755fa9ee4a9148439585556b7d9ff6f83e83218259cec91e9f9306c1a52c3520e429c0ed70bd509e6ead23306e2979591b92cbfc53b001ed7743b437d84634a3d26ee3a066d7e3b0148b4b199f2689e9f54bfe712b415901113899a9ee4713b13b370d8f46dc729c13e5d91e4a948356ab587429b53407d01fbffe1427ab945ef85cc4a74aeb8d1fcfd3a750bcbe6b645f1c16eaa960499aeb1f58177ae23ecd4070293a15a1b8a9931172df8dc589bb7696a438c3e942c7886a44a2de269f40e04ed4f38716af0536fc64aca773742d59f3c1b7dd544226e4000cd20eb8790a853a78dde80b8518bf223531dd016eda6d61595a57c228262104ed90720955f5429356c16c73a7a54c2aa6c5dd565ae13b1c5d092c470e81519e92de0609d63f82b597533c95cae84db26a6c15ded7c5229b18a1a931ca377635311a6947b700b43e19910c28e01fdbe0ce0e13d8b4f78cdcf4348a3d824ba9811257687758285244ed9cd6b4f1bafd8d7c2e1b7460270dc84a94eda4034b9b945f67cd3198eeae8f576744b040b1b6123e6f15ed7d625b5291975ab9575611aee5e3461f76f23b0a781327a33f141d9ac9b81f98b487bfa7abb2a8873e325198ef562d03c77332c5ea20fb21a74186741ca115d072281223183637e7f09fc94fd2e5c28a32d8b266252ee3297ec406e3833575f1d231b05e349a7714f421b3244520e574ab96c7c524b8dbd4b6ac7505c036affd33acc4c27787c278f4e6dc3993a8a9a9337b049605d384153c3a95e93196ca21b226924a938955d72fa96eb6b7495948b9a05705e2a6564bc8ed0d8d7bd52b53f892a4b4ac2218aebd64aac37c93913db16aea16397ff96fa8d155b32f0b1a1fc14b67f9d6e2143b7fc6542ce02f1427183451606927ad3abe70a43459b88b8ec4476c15b73d7238dcc0f8a1d55fbc6b0ad01c6cd6aa331592d62dd87dff5a452a33e4986f9e30dabc64f1a086fb5b645cbf519f639dae4bb4683c737b8ef81b215805559ac9a14eb4f03c8d3f23fdde53fc0ce8bc0f3c92c10a0d5f65e33228ea54c57435738e7a334e2822982b300ccb6987c114d9ceab047696276a99ce490270a6719eda079c51f17b65ffffac3c64450e21f8a8646735d84ac96efe525f8b5f42b94c83dd426f0e0cb1b4b701234d675df815a61f0258158c7df13530a2e04f93b4012b589499607d422f8c2eb33a05333fd9bffe1bde787ef52b383b540548f6a604d038694773709b05586bfb624ddb143d5eb0d6c00d8e7b35f6ac53139f20465589f01abc59bbbc0c459ef1450c8ef5cf421264ed6d4fe6c7e4348a7c8c95642baa13b2018fad9e98fe31db79ac5cef0b07b0e7dbda5f3d68802ca986a91ff60af882e10080722968c88729aabbd4809c34624359c80e92548c5d2dd2e6f19f4643cf4e0ac5a5a4e404843ed8191ba7221cc2b4d9d32f2eaf6c1f2d34b4eb12b588de8abd0d8a08cdf80f9ecf2e7af37373985506c837f2adbf579ca75cb1f79d5605c3ae4305460842984830ec95cbbac1839fa518c3643396a968afbed590b74c4a86b22e5b80b70f63589dbd1d61b142cecc7c8ac19c00b2dab60bd10df68a55b0116e95ffdccd45eb408daf1687fdb639d85e8739370df5823e7540a6bc570078dd72cf524e5ed3e137024f12a9b7dcf4b9ff7e0ddba0cdbb719a157becb628c9be2f79bc819107378ab9fbc6307e0e9f5dff1a8d85878a35b3cdd9617ab2d4dccf9f08f4f028f0ba9b25eec06701feddb049f77499cdb444ec518726a88df71175361c5bc24ef9b4f42ba802d7d95e863baebe0771af51c03992447918cca3fea3f673b66170c40c71042c8a853764018116ed540a5dad84f8e917b49609d7443ae6e7b8152596a6c9a6b192fb87e86aa1abbd65ebb9d140672747f466dd96caea25af4ee57473a619e21c74ed417bc3f80d45d2449bdcf018831bee489bc37827a3784ad5d3874605dc9e7a4744a2a8fcbcfef564cf51ca443c34380c10c27d698b827ba6222f64c483cd109f2f53b45e581f65437b63b2c0dbbfc152afff6fe2471e0ae45345d9bd05814a6a6bbbcb49c3d3fefef928669d1d8669297faa7ec24031ace190d22bd02b4e49810e6ef19ec7d1b3590dff463c137224d286bfb066b0bc02e6b2d6eb4854b9a59c6e83bad00b373a9ba4c54f5309557e367c3c1a64a122a672650ce44fad49dd7da68578527635c9fe2c75d5153e0e8e8797adcd2311c499b1934344e917d5bf54db9608c78187aaf74e0b0a674a9bdf14b3ab3a8fb73816df44d6a792f7706964cbf1a8e49872ca78cd4c3128eeffe99f07d509ab231b9f4912692e986e3a91c73217a5985b338c35eaae25677ec2ad662a076e41ea3455d178fa6d6d73786bd776b807c8cef0e641b7f6a6e72b33bdb8efd94e37f88d3a5edcd86f05c80d65d7578054f08a3904abaf13df4860b601d7fa70e3c2d326f3e5f13ef7aa37578a75fdcff69ca11b7d2dbe688cef4a7558907e948df5f3cd2f6a8c4c0489b7f8f164461ee7a25f8266276b86822ec0b87686a36ba831b4c6a8feac94a049b1be25a4f34291992459bde01600719c7d0a677e0eb24a18d54ee4991eb240ebbb5d9afb6554d272c4ee9a1c400669b8a527150e2ea068218f879ff0dfb40ad0d9a50e2c401e51ca7458a7ae08a69d0292c896bb49695e048959480d4b997637d1624134250954dbe5ee833d51faa04d561408ae7c65bd91d7ddc9d5db911261c3bbf7863a0566dc534bec8114e932fe25a4569848a3872bd68aed8006e4f6bbf76bca4d1897e74de0371f966430ca4f7be1b6fbeb81736e5776dc7fa6cdc927456ddc9f462e38880d3115726c403803e704eb58104fb1350ef54f495ef40c81a2769bb2b24f5d209a4fcd6bbdc3f05dfd3fc0e5707ab4dc03d74ae6873952191cdbb66d9aeff820b5fcbced5df5d8b06e2fa0e127ffabe46d03a71ae5e0ff242ee01c2d222a5f5a4e8a10312a93cb0ba491fa0f46260a4d4abec19674b4cf57e4272b4a9a6d5e45160495c2dbddbb5c87ebba2bc8c7ae91081d5035a38c9a8eb506ba8b30689ba7bba37b1911746f2bd4eb93b075a2a64d50d8add07ca3153ce3cdc02439d90116f8e7f7a914113073c109c42852a212adc8de2d955122f3f19b43f6a72456e7ca83ac21cdffe6b14198e3f92aca325f43b3ef2286e412c2694f4761a078265291018c3f0214137be14183cbb91300ccdbca75eaabb0ad50b5c561d1979db92b56d0fbdf4e175c2493e9f485ed74eb20dcd4301c76da4c9f98f6542bcabd06b3daee9c293fc2868af4cc309d5b6801030b4de3dca1b1d825492b49e6ced0a17c9d0f9b7324cca60f949f13b83e32d5b45dc3b29122c987f31fc6789facbb689a9684cef4680acc2a4f60f3d9e9d845b314ff0ba220135737d1bf037a3988997c481c19dcbd43f3eadfad23ed75749c505faa8e37d423eaa21217d0f48d0338fd2bec95ae346e9768e736704180b489b79ea383d31f695151039d06e3844a8e413852e889ebb57163ef292ea9995d1644181e021a743a7dfbcd35e339667fef0719c06327a2e75a90c1de28dc2551f69dbc861219cbd5437e4888bcc1c7ad1c87202f0859e234b9640d1144bce3647bd2ab2449c9e4cac9dc3ff15cb89abc150808805b8b6265046a64a280bfca17f34685109cc49b6cda27b29c49e5b374d727eb241709f3ca363a1795c49df012f056b68fb6069626416a8bab9a637defaa4c0e25bce48a367eafb8152b473f02b5546d7705cb61ca75579db5a8327ebba1d746273a32de8d2883c72ff960a5ec940cfd12c1c1087117a3d836cb7e4102d2e1127c4862227f28d26e641aacb905b076dab950586e867e7451596d29feca6167c56acdeb3c2df97756d21279774b74bd6046f6ecef31076889483ccc8d2af7fdc29bab05a5a9395ad74c62a788407d04f36df4dc5b7e0ae2ce3ca534e891133ff12508a7424f440eb1057730df69ecf8ea7259708fcce218e7bc2a67805b45e96cf72978c262d1b273a00d32258c49bd6eacf92f42316aa7c42b3660ac25bea765e08d5cf000e51b1ca3fb205d014cd966a0f06a0c4cbe7860f4152c099cd0a9ea47be8be2b93abd5fe2802c61239ce057b462ad4e6bd0f717623d7a2f9af7703cccb5d3955ba286d47746548e1fd552ae52338eebb8f0983cebd1c95511012d3fcc75934b6f0cb3197fbe0527f1f728ffeab2a2c881f30b40bde3e5a8f06af886ce269f9b8d50253aa5699a6eac1e1c3b71b144897527852921b422c3dc29477491dba2659c9e1011def6ea92529fe90c405af60b6203c44c893214c32c004e156e1c14f01295e49a9267c5db29f499e2d246f41c0fabbb8b2fce1e67c0aa9e08e76542cc6c9e264e24c5971fd55aea756f625122083ab4dc93e8a6b30a90dab7f0d2dbaf304ef7690206e30c01be64436ae2961c0cec50431a5efbb20a4c6bd9d1d6161d2463644b8d506c7fc45ee322ce4f18f52576bb71cfc70b19b701ba9a63542d85f8adf4e90102f23c98228c3228188a0f0284a9aa2c40b3d0f413bf4c8a043a3921b84a29e2d85798b7b3a083c3bd9ee8ff8a4473c49905039775a5a480f47f5b39463037e4c2a8824b5e3d1d4b34dfd8475af5d046484b56aa7a641bd12a08903447691cb5cb23c3f2b158779266bd67843d3edcdc7803f108a4b299d2e7b7676b4b886c89c98f733e7480a1464b58f64be06e4b093746c94bce0e677a2c411081260718290990e6a254b6561d3f1649954302061a25f220614493716ee9166b981fae1ec96b91ecc7c30026fab561613ec247dc300767d5f37ede27cbd7a68d75b4440342b4c2e21dd79c025ad662278ac1b161af115073cbd6f91f53646aa81a88dfef452a1a6e2750082dd89b000d2146086b894d714c73fe3bf00d0088eb090c146736221bcd501ad958766567051d211c6b3d7964ce7d3a8ee0e59087060056ce22dc16eb3fc50c7264ec881754a5bbb26711bd4fc3911020885823a00fba84a6095ff9a95c52559b7919bf277402ad70cb690345e3de94199a5c227ab182b02829d7a842fba006db1db8a7e923f232ef3a3cbf8fb2cdcd811fbdb5f66a4a37078a810edcf67d8a0f2fc4109c3e881d6dce057b82f8cbb53252d733c270d1c9d44ca91815df377f75d02a937b25dc4439a6faca5b5253fe07cc7131808d76cdcf659809546c3e75a30b56a7c094632401d08cc87fc9350d6475c19086c88285885754c05444e5ee1e5e78e745d6f310d30494d5744140c8280902cf041b0aeebb9ee10d935e8842987e32551b68fceb919b3ca1751885ae718c0442c0b07895c17bf921740c5adf18980ed136be6e0a4a005d3e0c68a566dfd36bdc1e723ce9fdbf73dbd4fd4568ea27b26204abe7149c39f1da578c7f64157e8d48227452b9c92f5b489ccc2125e079fd37ca23e4e2d6d88fc4c9fa88b648dff32864b2d524366fd5f1139e016b300f95bf07b0eff2b864acf83bf7c6c716c658c9fef920e3a450cd09f3543f48ac25f36ea288aa67d6f131e6e5bc47f1e49793afb26db171802eaee8f64c03dc5c4d52de00481c05ac5bf7e9668543128c7663e639d4ac16e141118d16da772c37b39f31d0c9845c9ce471a4be909dcad6c4ae5555bd255cc6fec4079683c9fefc232450feed3ebce2faa39dac45c849d16743f3d7d3eeeffeea8d62471989a6aac9342099f90b8ca833111bcecc44b31e17ea6a000f30e600193c43ccb0fbc7d9ea0d190a6941efaa1e85d264b55d8325e69988aa724492000c61993ef3b764c67aca8106aa1776ff5608e4d5921983793d507e18ec487d92fb53673a966b2ea9d54a894e207f6cba097ac96f0f5abb8eea71bb87d4d5c0ef5e3c232b9e7c423df326b5b15775d56a46e925cbe02762428d4ed6d89a62aec03527b7d9ae453ac732b80d1ae2ad62769fb27cf3c484cd875265d574a053d86b569c9d7e7d1c77c13f11a26c3a562961ae9e376eb7be9363d95742fadf843089b3d2e9849ca27fb7e2c39473afce0b1b11348527285fd350ce9b30eccae167560f2f80d3c5217910eb8e399b7ac008b36a22dd3e6fd0ec4a9c8f22049b5ca7cd576cf586f9d42f1bb55c9485a87e300e4b0ff683b40e165b5695da24e851755bc56004c01b2f62798817d985a7bc57276f9112ea9de06dc2abd705a58e133fad70eb106823f7d3e27bcc82dcbbca9131581c9d2f64dc7b223caee2937508ad9381a97bab59e8cb1aa800c2447e3220ed7f962f16bbabc21b668731fc50e0757003e3a37f8275b0aa121d3f0306d774aa017b81e5cbb8714c7afc0b8506c92b3beab8fa03704f416a1108d041b1851ad8d637c2d94feabe191b9322f32345f3538111e270d048d11c556539a028cc7fa8745ed495a83b7a83094fb3ee7c0a6d75c248c4a137a672f4e7826d404c8d0d922220f8efe16c73b21fde124461aaf11a3a0c91d5190bce3b244b8873c417cae45852226c4997692bc26a4a5efc80ba77b2474bfd4a1e3ee4828d4d5ab749ec6024d2d8982a1b54532194db093a62365772f10269a58c1ee994ef2e4227e430a2dd917f6ed541ba58bcfa45e234eef988f8a018d1d6687d3617647777f9a8da5b39b2b15f56a82f3387d3e7817a6e087608c7bcf8cce24983aac4cf4319036f99f1ed2239a351fb0d04597d14ef432a0b1552495eb1ae10e50c10f0a8865c99a035988d83cea2b768841121be26e068209b3a15363e11b9e2e5314f59ed46a8dc5687ca064b89c5d0b46dadda00b3e1c4b3faf85751bc89fbd45c1ec32990ca71ffcfdf4f2baf16826d646691d26c16cfe1c4ae2bc68bfb7f9e01babde0fd7c1a8617af0e8f510d5d8d209f319caf8c9dbf36e02d5fc038d05ed93b000716c2226229274082a3fb0506c1a3c06ee27ef69935f72d98a0dc926281a630062a1d337a4088d07c34ff4d004122549ed27d9df9869174bb29181456136b09a28fd5dc529b079ef698679891cec2c75d7773e9ac803113a59d2ea7bcd8214be581590fccd66b78c773b213e51af6c48fd26e824495298243444291c7ddf0ba92aee68fe97df73a1c83dc80c1eadcd57b0268eb3062945c9928fc4feb7ef1a05534ffdf8a6e58941fa26b406ab5b05969dc8c492a4e623a2e7054687ac4f45852b22eca37e2fc9716298c3ee707b6ed789d9e2ac8dae65b15d0f7fedc875c3d764114a945fa0d68bf058729b9420305859ecf544dd8e2ede3f89dadf7a5610027b3a4cc9285b85529338bba8f72207e7a64c1e7930628c4ba57da682935932600bd2a29ed23e770f65d6e998413dce62e3022e793ea38e71991b0a1663202371234533347eddb0c3cdc775459242377e8827ccdbceda28c29c008b52e62c0d7139ef8706ee322acf4a19abd7004af0567b80de4e7a4176d53d783d61d218ed093fea716f5499faf9aec6e9136683e0eecea19f3e56ddffe4a066178a676b17b036076d202cb71c66d3b5bc09c35a8e4a4bc41e7da2bc1569ffd8501de0545830c94818745b9125458e541f5af63be2b9a29dc6f110d58cfd2d73c46e3564a427f986c29e3bdd0ace0f800ceaefa764bf36da3b828fe94fcbaea10211222429ce42c9650934811902808332eaecc908f18948e3571898d4ab131bb63f9da35aa1b8e3f327af4787209b4940b923bc5ec40b92d01d716eeb4470b907e197ccc26581282cf608c5abe4c7d18f985453e063ddbb881c902da84b2cedd2101a93ac1fe40ae8156f07bfda353ed93b79fb34d74f583f5fa910459340113442121ebd6661c2ded3dd4461afb28f90ef129965e789b31d26a2ec20c43e95394418fe0fb8d6fa348177828407873e8f63c563f9517eb3e120ed79385cb966145742acc48d70ce44a90ba4d77171620a0e7376e03c2ed7ad5105c4062435be77601ab81083d6cf186b14d4fc02c4a4b79b07af7b922912e0807552375821cf6777a03c81af4cb974c3845aae9d26f77585e35dfca58214a0421cc4f16c4dce5de2593727c6203b9172f2206cf1f0aa68748f9cab3e82dd53c9976f592d13d6a63054c91c99370824116245d3551356ff92d912519904ce11cc07c8a724ed17a8570af8645bdb66896dfca684c7684184dd2ce12818fdfc8746c19929cba0bad00891944a8c8a38ebd46062773c5687c3f335564e9be1085293842f15685ffc990898aa1e2f39b0ac0cb201272a79eb1c27e6e22d8460f1e411bac61df4f3bd506c500060bc5f5edb6f3a460b112e948f9843684a946f1055899aefb88e012ea2791b4d203e4436f289f58a3a728ad58e77f06ac34c0ad8c74a9570839e6960ecca48fb8eb7f1981a1431432a328aa9b5c4b09d16fc0db8ef53e803f76dcfba0003815f60ca2016aa9832590e0a462a0a231b1308801f0890f7133c5e2b495213650ad4c60219f0d0554f84ba28c6bd3ca2ff1746dd558778901d7a3d9c0b7a32d2d7199b18b9111a06fabd2ec555851fe29d14d73ec1dce79941cfb1c4e54a05f90b3ffe050084c590f50a492d8d3c2f028823c4a7c1393dbb43803794150721c4e5de599b35d578ca73b3bf9926004a41423fc7fe70b9ccecc4d324759600e74dcb5d8bae1c7590e51c729e3a58872c1f9bc26b37808ec60a1e43aa713cdd325241e94ac490b0d3104b944d2163fe592ffbaab3006db118b21ad4b3a8c55a98569973bb815e3a4d9745a90ce81c84c7e1bdc5fd0d0b787412523535e6825f50c11db31c77e9dffafe11cfdb0a62f076ca08c004adbbdc86bd1103102a14cd17d79745c66e23535e038cea0fee7bb66a302c3bad5cbbbcb4016fee578c804fbaa5697cceb114a75014c806eb97c682a147efdca9f31a6973476144637a83faaeee95077873e7520165d341192feb85dfbcd54becfe4c44c3b8fe222f623c0611dab11bffbd1ed5bd1ecc247781423e0add3797f8c7c3ce008b702b5b6c364bc20faa6c138d1fbfe3ba8748ef5f5b366e96aa0fd1cd00ffe418212d8b9cdd8522f6a08cdb368595c10c49b2d39ca3be1059bee9e567defd8453727d2bf327a35825823b989c7d6b8065c7dc4d9b48201fca3c7776cffb09809a4a993f522d02e880ba737bc216a9370796cf4193e5057dade88b152ac189b6ec8f8485bfaa64b43879ba17f2bf3015d12308e30a2cbafa339504644d10f9948c437b3fa9c5ae3302b6e8c4ca289bb56d5a70c934a8a662d70fc411b02df4d43fb49ede293db5c8315380f330c3a1cc9c32ea3232e50c597fe64045eac81f1c60c6334f06794d0db0cbbf2194508a91bbc090cd329af8c62394a50dc80a674ff3928b11e0ff41d8d5fce2eaa7d31b29d82444df3cc4767452ba85aa376c965dcf31180b36a535a034faa06c1a29e690fa22eef2361da6a751e83c36ed562e6a66b3a4511a959490e1fc24900ce851a597a9b459536417455defaa11e218625457e853b5289bc40f5cd7cbb82a4791d08c548790d1e7f020ef80d7ed90f359577f42106d0ad8fc515a873ce5bbf9d859051784e9a038b8252901fb8babbb853e7ffcba9b9541156494ec04886476e542d43e365902dfefb44f474bf07c066a813bfb919111019d380b94d44f0af2830b4dc007148fbe471a2438112fd406c8698afb4f0e8c8ddb399ba626c317d75bdc6a2ee03c340f35f425c16dd614ea7a56a7c172afdb2b069d6b0f226b820a4560bed26ad953859e677d1ba02b20b8df2fb8000ac7e4e5520aca43a1101919f238e8cd1822261e74473cedc288ff904fddff6ebe38005c160d6f69ad53c13bd04e4f598752c3ce8f31b3a3abf13ac68a09c0967bfd1c9f33ff86130344f82832390644e9916ab61c49f81f2b5e7ebc812fece27858c3a7656a70debc51d2149a3f13408c2377b50aa392e15e6f7a8de440139f1c381fa170e26c111d7dc05b926dfb97617458aecb1a4fc56d2c46e9ea90872eb1741128ef55cc7671f2ec4fe8ea58ce52f8606f28efb2e16531f7789e7f09a31644cd635e2f40411cd45bc6ac05f88dd8dc4655de01443fb31333fd525a053b9b87f728b2aaf1a3dce1dcc76c58452a0649418e6b7d03ea1b09603794d8e3229884a90c651524c1cdb02652111929601e3c346ba2816fa866d08382d61ad09ba635bade0f9be85014d73b28b6ca0dbf641f289b0cfca23b5cdefd3876a6a55c5d94dbdb8a2a010d72b96fb27df6bf3b6c149e1a17c2f8d8757f1fda82f777644371ea32e2dcfbc83201da59e27afb9566206153351c9e2e002537444b045f1dfea3f5a804910a4cfe15e4bb3e76b60c627f5306f3f5db1df971c45c370c841b2b5ed8a2437814f57aafad6fa5ff120971f49862c9fe8757b216aa2b9b22492c4c9ad9c2bf5015ebfe6ad3bce67adc381142cf5d0a4d7ef68fe8c031a86d38e5ab4314e40f3b39fdbcc0412decf2af8cff66ec06eadc0e1d30a53472d580140a9e535e5eff866242a675ded0574aafbcde7931927833117bbc8c2a5e8976a1663160d5563fca60370238b7021edd93541ca478fda0a98ac594767355a1e5908263f61cb6b95a6c60ac6f92be98e3c44ff3b50ff6c8f1561b0598f9697a8857f776ab6349276de3a40183884cad79bbc7d85ec023484a5ea37b4a411e728ee5751c6e83c93e06fcd83b805fd603de55081518687b017231bf35e0fb97c784e574584fede3860d997a38e4049c0f709404611052a5d77c267587803ec9306818e8fa5ff793048a7cc60b8ec8c6212da4b200c1a9dd160f80cce51dfbff3a569245b0349ad3dfdce207e073f2a7f7f6411ee2f49b04372a8205703e96dd1df2b617271ac04dec5f768004ff68943772a3991776c71919f755e243b2c78ee6b7e701630dce508d47b8304d4cc0a027168131bc84a168cfbdd5181ee11a5112b5be66b952bdfeb88b8f94196adc57bf358b9f42b59dbcbbff08c07135f65a92467307cd8a321df1b074ee0a36da066c58dfc763abbf54d67e8cf92a425f9b32a33b4f42c07d8225237ac042da5fc7ab7ce06913aa704d1a4033c45dd3c6e580676c3047aab3f29ca6fa939be5c36e01690210f09ed583aac136004ddc7a043ce056735d89da36ac8e7b4bcf6e517d752d03f6ca77a9b4b7070eef5518a08944d2198e67cd3c9ae5026fe85e48bf6e88fd7c657d2ff78efea72a8dcfae4b4c3c377bd4cc5b51542225c0a788a41ca4fed4b9c2578929cb0bee6a45c1697a0f20001a611522b35b7928e1af7136e988c58653e57002c33ebc735b566dd5664fae29c4adbac2631862f603f0524d8d2f04557ac509249ac000edaba257c1d405fe1438d2aa6bac3a8b0604276730b125087f7203d844f4c8e0ea7278ccf584dc5aea99df689285145a1e348612e56f254df6bdc7312b27ca5c45e366f3e50135445787c0448a44de715079230878c6d00acc5e60f3aa50a4ec9f91efc3e72b1c0c44bc489ee37e578b6671fc0732990cfa6c4e838a84dca0ba9c95506d81b10566d0dceabe0aa498590d74211458e02e63cf6179871e38d0345a2d0b2410e8683b279c8cf1992088f2e3a7cbe3f2ef08a1c3329f8f7fd0d5ddec928a23d829e02ec8416e873ba4b9e6820df5c71c26e7645fb77d61a903df199b3c560040ecec79814b406dbf152abaa87a3803bb1dec7af19cdfa07458df478ce1d3842fe744720b3b777163c1726ef4defc58c93d93bff1b8c094f6d0637419271f6631817e536758318e1b51014a55614a6c7d6b65018082a1ab2973ef4530cf3b71e3075bdcef47d635008890cb52d9a4bd152d4271029496a39491cb123a3295bb361b58c3e3a58b75e391474946674f3714d6cbc5639bddfc0eb3e780eb60fcb41fc63e939d84b5fd981297409b643c9aca76ad1d53a5e7d09292eb42b37be2a5a51c5b2072d394b73692c1e40191fce536b565f9f89e22d593367ff385667a38b96dba2d4d21fece91246877892b58389d7315952b4d7615e628fe994efb80aaa9cdc03e545e3ea2b46baa4d25901f934e0c5a2202581262c22c8d3e87b87a1833ea34994483555297b5eec40c797b9c2a5ad53889fda7df48b111ded26fee88bc479aceb05abf450bb095f4f58417bb8b770c2538dc9c5e05ed0fe0d33f117dd2a12160ae0c9f58d89b8cd5890f06c6d8b19c4c2f0e0b160c2a936e993a9389a8bd3f305b885df364b75690309b066e251da413bed06d1ee6bd88020c11ac0041138d84a320f664d704756cf851b42527181e7a3d86fd977dc74c96fdb05b9eff17ea865f54195fe845fb32f67edf4f2013a1e71b04cf1943d0249bce9786fe3126814699d7960e0fcb9a42498baa5c7e7f46d72e24be8470581e321d22c29da6005cfde219560449aa565ddd7d8e753a4788d345cff0de655261d5b1f23f7dc50936e228970f92a4aada3509f81f9f42c4087b309318c3d478107c04e82fe64fa69f3a701930f5c0d9416afea1a1a00facfb58539464dda19434851b2e6a1a70f85983327797fc8790a1de2c681fa65a02caee539c2cf27e56c556b6b61dc086bf425b3ee30871f224cc1b038c3e15de9847cb6bcc36d4f11a7231772d51125febd86372ed7ba63e8423be2cdbfbd2b26f3c86cad78a87c59f0de614442cc717f28f34bd1c0b1c5fdca408f880bc16f25d7981785e14ef7a5ac34ad5eae715ee76c6f2ccab737a4a805a77d88b6c4ca5f42dc9082c82d53c29f5658bf66dd6d53b1f8966df883577b3fbe0839f000ae416e8b078765d414685fbb7d8dc0124120c3505cd4ae2a0931c7305fd9eafc8417e03add2d0c40cb3c40699f5092b62e4950bc50c1e69b07c7c004852868e1f1798915a25498bbe45c5295cc69220fe807009221a445878871f23a0bef8b00ad4e58ec245e976226ebc5f0c3d009bba44f1bcd6aa5e80f08584f955ad36500c09c077c59bcd938adcaef6f9ed306cfde4fbdbb0766f88840f8afd10629d1d8049d159b8c17a3c29e3730c5834b5be46c2405331e77c5f78bc62ab9392a85f9df3b0b5c610ca36a5e850e2b9b73d3ed04aeaaa78a73de1fdc2e3cda8985c603ab6a22bc424b93202a56ca6053dc1837ec0c85a435c5c80fa77d299f330d8947d54ed9ebdc5ffcaf088f3fffa48ebfe7d8fb011b53a414878961a59fc879096a54cf5fbb35e09d04b470af17b4374404b0b09e112f2e82fefa9dec0f68124bd7ae5eb04cc8f1cafd83ec7f711050eb413c6b87b7efa14640398be1f9290128e7c91092b923147d1b7264f7c38134b9723c1cdee335c391aa5bc6d678157bb6a82c88bfacf5ff0fd1a9f01586bb2327c385811c8de5ac7179f3f7e3e895472e3ff208eb864d004c0071001fcced8bb028af138d002d2a56265efb7d7f1c1321c4eca9cc9f33974483363869bbab46be806905a3a1b8d655af49fec8894130445d8e9ba8802ff0a99167d1de32657e5a75b4a73f69f02e9c6a7b24d2331a7e8ccc5c8455d066e3464fb717f8ab3f0dc54254593cc43ff262a43d16c321b2ac58d6c8831f38da10674308be5e2a93a9a11eb81d55a23c3abe7233fae1c3b1335557ba146861d39ae262c447ad2c6549abed5667851a0c0477537e36bbb9dac6d604b79e5797378015f34e22be96a0a9678542481fc5ad565e0af82843d17520452ea81970547f1d0745a927be2a4eafc9e23b303bf3fe1c2e03753b27565aa9ec6a85cca8537f79ca881db3ef3dbe22b2ef205dcf8b242834c13f714777f3545acb020690ddd6fe9d15e64745ebde4fd6f21d0289599540bcf22b045afdd39020de604f792db723c17adadc5874b35ba4de3a0047c802497fbd530de1ebdb0098231fb48d2b50462e29004bc8572ce17b840ef7f15dfc6d05003ece0f255a42c78145f82f7e9aab11ea30042afde303b0e3436f95f04bf570a3cd6e40096f9ac735ce4556732adf4ad88a562a97975ff4334c2e393ac80544be9181c5006a2336daead506d31a5c6b878059fd1cdeecb1614ef9d6c55805474817c84415bec3b385607d6e45ab1bdc534e9c26427c82fb292b46064215e49f71c1153b335617ca0b7477ea6c0d748a94ec9c9e5e5c41a91711702666c14701c839ef49275d15cb83f09d90bfe57ff095d7f0762d613f866667379ef08d4dd2a28738f01048041a87e1107fbbbdcb8d2a6d36678d3983891b63881c49e0a49c3a09647daa8c180371589c5b4903ea5e3d51c731dd91a2dff9c1059e0b3d04f11cd2ce182f98d14d9552def90272e3d8ac5230aff3e89d8bf932d27f97f27b8235ad11e71c24fe0883481ee44dbc2c62c017fc7118d705b925a1aa69790b59f8a9ff6e675737328d995e3ba8aa44d014e7bf924023f3d1d0475ab908617dd937dda620747940598b278d626bc7c4cdc871e6811d6065d0487823073775a8e88204e42c56b3af7f5cd10460fae7920093c9337c19d42a1caee5cb037579f0347401b4b70229684f5cfe9b6b54dc789aab3c50b031206f748f47a25926eceedea00ddb6df2565590018f4a77ce5266bb8a5a74251b95763c7ded8e46a20e79b139b76e55cf9d7c3d4af292612a93b10ed63bfc40bcc7ebd877f54b25d13a1b100e43be1ec0b283457b346fb3d9133ccc7142000014f430cf1661dd80877bd312aa99822d8e609865f53b09719ea606270f84146fd59056acc1e0432790bb38f5b0ef84a7d6bc32378c973d3a320168ee768fe1effac4a3b04f439431df56bb549a4a3c77052956bccde3712f8c2fde204a167ec571a8bc741aeaa4ae758ce242fb1009d32e4288dc5e3205755a5bce871492d7193adb34f7bfcf27f84ceb3fbfd878128778e5e45ec3bc074ad030d6f383f278576abacafb204bcf19376fc9301267c30f24cef41fbf90010791294d25325704784e6132e7e60f5cf4dbc9d42cbf46de4e530028ccda45c10a6a247a45889fd9a85f5ce9b546c39b29f81078cf7f39dc169fa09a7814535e1e635fae2bc10f71121d2cb13611eed994e4f9274cfaf295968671cccd4be2702b76628b93dbea3c0048a1071d33da009862ba4c405f1167a1489791d206f406cb15f4242c5bbb5bac320119777958e7486343c1caabb70e62049ca6d0e5aa37bcb9410740aac13c83eae70c05d571ac8fa51938790634d76b30463a166e0c93823ac653a2d0ae6aa8bf43fb9964cc7dfa1ac412a413b8ce11632ac0552cb7f46c2997bf0209f284ccf0c12307500ba1dc0337e17944693c2bd2b467c80d202f20e03af65efd4faa467bcc93dd9ba03d13056796216b401418850180b1f7bbc10350656ad1d6afae7d1a0071d8e7188afe81b7f8a30a498725daea0124b0a8723c94d31dfe1aa8ba9dda42c251bf18bd28a768f74043040454ce095debe5bf4a20219c245d494a4a25ef0a629a847aa78e964b3eed0f0a1e5eaa454ee77e7162f85aa19f87d0a5e7a1b439b86ede92b127a172a68c23e3de3f3e8059955446b10c958a5ae4d95950f3d99e05e72ce86f5436d7693009449291a84e9342a75c2165289f511ef05e6020556748e13a1292e4e0bc57494e453437c154230042c22eef61e8f2e49a617d8ee77ab6e6bb0902f06bf9bb43ab345fba8a6414c08534e7ebda421824c943bc88573aba9b5c1c652391a1316f65d47eeaa5b5bbe1c932d3741b308fc515f4be874a09310e8b837983465a835e491585ea27c63c3f45dba9331f13d3ce3a2e77192982b5dd4258a5105baed6923b2e66cd673decda6701578018cbbab6bbcc3eec0e8b0cf43220b0de3f4862ac5cdff67c2b6a0715fc81a586aadfd9b50599bb095a0351c421c43eac82ff305239fb4235eb089a22cae26a82f6d40365e5c02065bc524c4e40a4b572a3722104dd511042f2109e999fea80db0a02daff876644f9f1760772fd3de435316dcb89108091304f63a45459840fc55d695cd209fc56a9f98945eb8debeeeb5fbb6bc0841cb6aa0017d10e947c400ea0ef48c80aadc2f89250600b96755de0fc364c3370bc6741450fd933850e6ecb317b4bab4b780d235187dcb4c89041b9b96faa2d78339b6fb467f544403fbaeb801f5976769c1d95ce9f82f531a8cad0aacb0f86b74d773afd18285d3b5b337cfb5dad58c58f9f47c02249a471c428f3233a29b96023da3b66b34ff13c71136fe4bcd12c99227fd7d3ac6f9889cf85e04917a0efe000bdf5f460b5315fa930a538eebc438fbbb3a7c42ee136ab78255d60082e68c97564006fa7dd8edb3467091b3ed5ce0acf3bfaea8e7e5b520531a6d9841ce954c017d350d8ef99ea74e12af4242759e38db4eecd38a44b0458b8be6d834ee0584f6d853629cb19128b112cbb07c00ba3f66c5918e3352626fcded5478a2fa0c787c9d1fb2e08c87ad2c5d9102a2244f99865c7cc8412b9303638fe16f0977d6ceeb9acc294faa75cc18bf31d78becd677c57a7c6374703b7b9fbd75d74ba83fc2ea1b28286c9ae4c296836e89842f4192ebc391c9c26ea7bca03741c710956f5bad3630b7695f9d5e04e2d4c2999af622e05c79f6a5a8b204b6c4e93fb4f88d8aaa9b07280db9f7b9462e1502677b7504935f01ad003371e9e9bd27399150aba9660d68e8ef400ecfbf72d3bb91bdecc1844bb8e9faa176c01a9eb817d42a615c959dced413dd28512e40a7cd072daab2adcee8bd7390b61cc8b5945e7201fce487c81b7df2f247210e3af78434cb5a28110abe0a7bf94b919e5e129286e74a8fdd224aa53722f65bf95e565638ded7a732a9042f74b38ef5fb376f81d9de9e239d0bc883112b447cb71cb11c74e1530ffc1b945aa7d3902ac84947f4c6163e492b7d12c7ad38fe1037097cd07c9f02f3766927c9993d9189091136f17c860108fe5fa8f781edbe0bbf008ae1c954b0058305e0d3cd0fa1ff61b0ced78f314f9ac66923efa579e5e89890f8a267fa3fcd72e59818c0ab181b495da3a17b4ae5bbe4824572998cae423646650106881c8c9c824c5beb5b617c6e4049d38cc5dc5acf2c31151b2c47b65c2a8d8cb43457ec140b9079c795533134985d418752af9c0f1ca405b1f6597deb6028d4186343f1911ca8743825775ccd445af943154d1678574d4d2e404a417ee29c47b76d1d9916326fbacb898042633e165a3e81e337cda34f64226b8d0e6a264f6940ad142a2b79cb50a5978d5b0e233b581a91e5e4e67d810adb98c636d648c34874b924af0bdd5452c7a54c24c3ac3586a8d7dc2d19b2fff3de2a641df5c168a2faaf4f6a91165049b17d56061dd184dbdc98c5701aa2c912289b6aa7ed92bc3bf0822be20034078a4a18bd297c2ae2e003e65a07f637cc8633222faaf83cdaab087243fcccb7cb17b23b29a94722b03a7b245d0377757053256b562ff43d8955e6615f229f347f378083f9b41a3d439b7e7c935b10b46654cb1af15192286a201fd84936415ae9dc4e29ce55c77b08190b666f8b1d11ddcc6ad54499be68c48cc349800465d6c820a50736f834af2894bb77d70fe4881eee13d0922dafb55951028515dda9edc25de9c1d78830e76e8fa7781b03dc7bcbf1de5f5f0a382e1aa864245d2ad898750e90b35a49ea10748cd32a32f8040d7acf749f3cb92b23f1f5d725286a545549a67fd87ba07b4bc0b2af37ca57ed27e0c32830684911d39c90bf6a9fabfa7740d1585296098143f927b4189daffa354c55f500d8c0b04ee4058d963451eba57ea22a6ef3976a55be169333a4012e65e487f9682a70aaecf14aa5fe2b4439aabc30ddee577d9f5121d507ed423de2b3f872bd215b1e3f562e54a93833370fd4e6406bbf0c5d378d666ced966f57988e9455bce157d430c77ef30014734366a5cf1694c476d096d62060860e7574b8e3d355ce85a3f21fb8607e1fe097bbd8a7fa05a4e847ad0cdba43e805788213f94a8873379cf7e14e4f2839b59514454d4c37b0af6b4180862065d3a5cb1f2a6141b26ef496e2689d2f9058a848244128f09644cfb10c90c860f877a0ce018676612467281bb66ceeaa5323a4d34f09e91f156bc2afbfb32ee0bc5d6180a44910dae8abcafc707f02d5d9a8bd9f4bfb1409ce1265317ba0ced83f02fa47a39b1d634430f07988e3aaabb620dd46a85c81091cb8a3ba2f7cc54fd6f25a1d018cbc1bacd482174abe3299718615323b65ee54260c024dcdd006414bf8d8bc4a19420a8ffbbc035e16e3530c33dcbfa84d6dc3bfc9ecf8faa621fb010b8ed52d7754b524da3595a59c797d6780c85139eaef31aa35047ae383204e952f3d38aac87c259924de9a76f4a2915f53f85f70d239f7eaedbe41300adce85d452a05ab4b4fd6f9168e2001831bc6a9e49710c2f5cced6214a1c37393fb588edd980ca5f7c481fd6c50cb0824259b71604956d48c7d45681dd2d6d06f2b5346576fdb710b1c6c8ae2d29943f18e1301dcaeaba5140b82fb93d987bec8d31baee6134e8ab8785ce2686d6b3637443dbf9ac24104dfefa5d7aab0b13afa460cdbffebc0dc2f0dc9502f4c3f177f1a77552cf2286fb701f8c4dd379d7a925c114d850e8deea4ef93d9b9d92a81deb7bd1e7578ac22c770f8ddd40116a621b7723e4342a0c12a8a20ea9c9e2da579cd5531354419ac5c3f1c40c9ddb65799357bb60d138d55f5feccef2ae9b8768bd582db4746efb7b65fe8fa5e34f93f15384f32abb4338cefe82e6029009e08b82d3defdc4d28a67f8b11650f80b075b5c11cfe5b3f10145e7a1300d6e7117735f250013f84ed4202a2cde40724ccdc289dbcd65ffdd74ecf7547f7d6565b7cc8191deba0f0c935aaae38932063a89308dc3057ab36f6d54f240f0a0edc4e4e53073ef2bb3995daa20dd4ebc25a122955ba63b53d45808550bfba052158f1d9904b6e606330e91450706fbc5fa1fef6426268e2918cecafb46d8f6da499d54d5be9afe306bbb0b8ab3bc92265001826b6421c9c4d9d37a92434ffdafa70ac463f29a3d2dc146ae88bf84f30442d68075844571a28151b625510c743c71405900a2ec33a335d7b8e70758abe05080959f03c54242c016fa3d590a153f5408d8bd4e5f36c7c8cb7f4abca6168a4e7d510c1c17a7715e24cf0c8f7e521c981224eeb6504bd0b39213f676f090501c88113b605b3650ee10fa9eee810281e240efb6b4bfc71e6540b0b9d47ebd6cd2144f7110f71a0574b306422f10595d8fad4c0d1eb60c25ae0e216c4de916d92d6174b7bb47c792b3b516fb6f5c29fd159c5bb6117a2dd5cbf3ad68ffc7135b0064726cbdce4842e9152f9919ab7ae51c734f5c626f1cef5f29ec9d7fc6c05640344c67768b2caa214d201be8b7986d6634800d2665a8a26e031b36c0e94526f8ed2a0b2672571ef14c10d86d2020c44d2a5b1f9332deabc1bde39f1a8d1ff6a1943253a03220453575a3c32956bec26c13e3e75b0db01396fcf5d33c5cc9ec1ab55a39c8ae136910039f4122e2f7ada64b81c7ae820bf94cbf4217ebd3fd6f660771ba5b6d2fdb891516b7348ca35a510723892936dcca0fff87999bce2c94087e672d3791367358bd8192fc36b449b2d4f8d640af7d9754afe40e9aafe0571665e5ee78d56e6c44ce9ab54c10b718adddae310c5fcc17aa178d2d94afd7b414b439f2c659305428c17d806357515656e7ea584214f785844b7c466b527a5c99615855b1cf872a5061ac0515e1b1accdfcff4c5a850c250d6f18fb06c6aa7afd1eb63d3d35c76744b73825acf0c39d82b9a22fabaa46dda1392b0ed60b3149806ce792f5e0b7bf69ca27214524d266a7b2739be36039746a6573b3c102e6b58395aab29d0119c62c5310728e05a60be81ebde3c2ae4be17b128dcf39488111f4619f40f3fd6a36ccee88c93ea2bbc3ae4b08b63280251a89ba98f74720b1cf03da560521a148ae1a9791ee8f279c8b294c12e05b84dfaab6c7a4ca1ec3f4036365955285269718ca87671a04aa512a210803fba6b14661877eb9233501ec39f2f4d8cc26aeba398c0893d31d90decead177bf5a3394098b07fb206ce62cb085d551ff85e39c4739e6158ee17ef819fa0a439f848a644c38e6ee813f50dcbb0e2b7826f533d1b8c778140529b21a3ebc61d0c9e06c369729c1629dd3a8f45a6369221127b7d02be90ea0c43d9b9b20c28e8f18f00a392b221e825551549c6f8ca4fa9e902bb63e3b5162bc8330c9805e134b491c5be108b466237cc082a7a8b56da9df4b46027104b51c07d74fb38708f7b13461c7adf4edbcc69838984f308c04321c2cb0018e0687500391bb44d83c2c4328d9a0563c6bddb9307983407e46a9f885e61d166346ef1f416e90b2d7dd8369911c1c646ce5c84a2897fb2e84167802173eaebefe291c1145c35449bdfc7cd96836a915458addee29fd0d34e2d9e132c17301824a280abd0eee90b4b6fdf2b8111a9c324d8c95d6e2a866dd796f9daa2075fb32f4fb9b02ccaab183ae6777c68861faa8c1266d8a9a09f3a411198262e33c1af30091d0a73278279ecad31266a382e2f2ee3ed3041af317808a9fbc249bfcf144be6c71cc084e904955a58d4abb0eee3ab923f16494edd856bb26ff2c6752aea586c7cdfc04a111e78f40a9423039a90a682592090424e07fad2ac3f313bf0cefeaff6ce9c34dbe744ee5e2e9dc2ff989a4c3c27a8e124fee9e038409605adabc2712c04148012e60406b57e7af909a70eafbab0ae7c294e5048912b9b40f5abfd16d2f6eeb6a5dc3225295315089c08a70762107591182bc46861b11a077756dfc0f2d2378ca1c4652c2ec3c27c11fb75e2843df4e5e3d83142e481f4c6c7baf23f97e9b9c69db81377e2cf0ca92324e3b26f1ca54be92480b159c1193b98315232e30c2f8cecd0755555b516c9c0c41a2762509489e12595c07060c46852b3306070bffabd24db487bb71bd95057dd1d8694db4a5eddffdddd514d3d5d868561834bee322c8c18c040238cedabe26d8b1cf11f010911c21fed082e7c1f1b8d3c113e33845128420805cd6446a5cc1e82d13118693064599665990bb6335496f16589ec4b0b603843858c288db1c3982f9c4461f3ea32c060f3608071eb65181858dcef5d920c162b8d3bcbe54243a9eb054410f1450c982368b620438406e6881fa494528209421be308181e4c603c216563169826364829a794609268ee4e012762d228b5717283a2315fcae8beb0b1e1cc972d4e5fc638f32c24988081a1a463f0a28a121a222f70f8f2e5e562bed0e0fa971a3c39a3ba0c034388bb5d8681c103d2c6accfd9d42f1ac5ffa25152f76461a05206917a27b32f64435cb2a52424b0c8052324aeee99efffed903f3df872ef7b2743929e5b1d15fce54bfb027d7ff16b83dcec0184bb02f095bdfca022d9fb4f5b247b9741472ac032464031f4a2ef1f54c416c9fe8522d30e7597314e865efe2e3f32a08796a2d117af5cf8463a2a20e9a8e09fbd7f96027c71efd4de89ef9e752914e3ec6173754f7cf16f28a30b517f7a167e4caf7dbce1ee2788fac414badf7eb32fdc7cf7fc9adfdb2bf56d7bf80d4d85b9f03de9c29f4a5de33de73d356fbea3bf75bf759bd589afc521a4de817fc345272e5df8a788244e894511ca852f5990df6df46ffec6be907afafcea6e1ee771ec0bf36f9e5fdb47db23aa1e27f5aae7ee673ece5604915cf81d940b5fe5e9642ff452fd263d16e2ffa05efb7e167e4c7ffafe212a046dcf3dea5508eabebee937d8fda49e3eec7ee6df3c1022fa37dcd3bfb13afe39462ef40efc9437a4e370ca855f03625179f0713c9d0c6691d55bf50e0b2dc03b64080b29a4f04358f8d151c1f4a847d9175ef4e3b3f0a3a302ea85ee51cfaf9b8fb6877cffd4cfdf52cfdd4ff7f3e1bdd9a1135fe8457ff32e057e11991ea5c2f6a68fdd4ff7a8f79f9f6a5156a7ff84e413398f7adc3b264fa72d102d5ca8a774a3d02bcc855fa40ac948add83866207c819264c595c1da46a44be94bd52fc3bca8925929a5172ab8524afb0961e9c0bd11e0051077f52c2fa4c8000138643a00010c1723678030c332e332e332c362fe1e9da424498a998d63120c48ba408368003998828b26200870015b368e3c91274ee63133e69149e6219b7974641e11320fc839b2e70c5aa0ecfbb32cdbe87c38679649e9de1db38792d2cfdea7a9173f3e77d30e9917287e7b5f7f0b423732dce2034b18ad20096646933362782943bda42192524a19420f5ec6c8e00b242b8e08c34b17e9e5cb922c1c015e90322f591c8959fcb3ba88a520eb52579d85865857ecddc5510d5bbaa87224eb228ac3bcc0b8b8a28b973362d8e2858d912d5d18b97e19e6a50d0c1366cbc6ab8d2390fc6cd5da67defc0ffa989739c74b69ebf70e5cb560652806b2961fe6e10146faf88ad4a3de549fff84c83353529bdffdd679274b354dd3ec273ffb80dcccfbb4d7fe03a2d9cf75b5cf7a9ceca7fdfca815e2bada4f5b3794c7264bb7c845fd5ecb78476c41576ce9903b6c4fd49dafdd63faf82b1ccc231b41eb02f9cbf9d2d3fee7f64bf9b515d2bafdbe725ba9ea8697fad51b61a47545251bc78db98523b6bed68daad8d2c99e61629cb9fe3a99f5010bf3c595ef3382d18dbf42e4e19e86c53f21f230ac4bd1fd5c5d6e7c7db5c617910f65b91f8721eaa60c0d0d0dbd8e18d685cbfdf8cc8d9f79275031c53b410a23ef0429aec45644a223f23098fbe57e21dc13a8a0125b7cd9b2aa4c5b7b27c61bb115a770125bff55fa1116bd600b172e4a64a194c5128a59718c363f7a3407d5f47cf9a7699a36d9f5d01c6eddfd980fed07341f76fed303ca388d5e76cfd5657e62ab4b84f099999919323f33335316a26547182b308a512b87e9dfe8bb47edc7613829e9caf845fd78c9858ef41d3a74e8d0a12ff9922ff9d2d7826e3f78e5c67799e4021656b04072a5f6917a23e030575a20b734c981d898f5412cd9cfeda594524a29e5b6bd7cd90387a8e572e7efe81e9c8fffc33c6e521f7b3ad0f14d118cfac121fa5276f2b9cd6f3c209c433e8ee78a3b7284b882fc5455a296be4f3fca3b79dfdfed3bef5bdded39efab77b32cf8fde010c33e96825a9a59eb104b2afa19168b8b3b4b8bae020b2cb0c0e26366768662e69c33049f42e60898cbf43ee6c25c70b874308fed59370775931201c9bf6aa8709effbb27c7b2e52de555a9f138aa6fd5556ccd8f7f83e3e5781f77e69bbc566cdd787c53ffdd5323f5f16b7834eecc4f7935b6e657cf155bf3519e8ed89a7ff24688adf99d57426ccdf99b078473ec883bd344234e94426ccc929dfd5c5cecd73dec84b8ee085c17887bd37fae0b3fce7eaecb3d4b957a7ed5c5f99b9417426cc557793ae20eac37def70e7c14ebc2a8eabaafdd73533ffe8dfdb89fdfdfee4fde576f6739f622900b349fc26a741ea6fd5a77b2f7d5d3f3d328ead393d7ea9df8b47b4eddc7f9dcfcf9db8d3bffe471ef4c1a5bf37d9af33c9566a72b0eb131ab094e2221b3900045546ee2c20845a5df81eee186455d5a57bc142b6e22842b75c41df9f3b95b72922bb6648dad0f3ac9acaa8ae69ae5b8039bb18f360863ec7697de47e1cfae4764a272a441659b4551f9e5896917b9dedc24b58ca391e7eb97b25fa2d00f24d94666669ea7cf85eb973e5fbe2634eebd4d9879c039f8a507f907178e105b55eaf71fbfb6778497155bd1abd4f831ae1c8bcbd503a52e16cba5b8b39c4a77dbf0e476370f3797633618b95f7d266ebae8c24b182bc65822e9256b58d2035d128492299734312d49a29d41464912877339b64489c883d0b2aa414a29a5122074942c51839221db85e5840d2c35a2e4b87b0d486e8c2459d3c5922c19f128b125b0ebc1e5d8922d9c18b1246162349ce154dc87254a2cc96283d89223624b5ae03c7814d70eac1d76702663668848ae40a2041457b2023546e2104dc468488a2131e34d6092180d4a33780daa005c8e21a9e1d2cb312446ee57df091db1d5905d3754c6d1dc85feec510877e5432faafc1f779d99991976195fe2364008c970f8524a292d941a1b66f23f0823accef2249ec491701d23ece073e7123eecbc90427793dd04609b0b2d074188ef9d13d5081db04d8e4a2eb688aadf8f93cc83becc7eb60928f87ef805ca5ed2df340e763ffcd297edb04f52da217da9fd917d7ff505cc23d9df799c393a203247ca1ce319f21de5bf49ffe1ed9d09dd8fd8d232f3606f1873d818c6d09fcdf0c5cdde6be4c93e8f9d09fdc33ffb111fc82d3bfc60438f426e11930189477f2d617777b7f71187dd6ad444e82cfd066666666666666676c8cc9176b7c69c11b165936a9491e014a508eba24e722c2eb34927d214a508eba22499cd0c698a528475111a349291a62845181a479aa2e4ed682a7c8e3132121a1e02db60e11cfc94892d9b54a32e4e824530a53805c96536e9a4b308c294e21497d9cc262c8229652f33580473e92e4e8245deae8f93e2c7cfd5624046fa1ee21c14c9356575a385630cec7e2b19b0dc8fae748064cf37481c7a694f7f528fe8c88e57f648e84bd32cfdac57588018976d151dff2086bd4c90bd09a6f37847cbfcfe3a6c81cc779b02bfa6f5288d88115d31026b3195633214c58860311990c48c68d1989967672ea567cdcc4c698f3f9daddee1eed6e0e59986e6b15b55d55428bdb90653b5db602abd104c9d1782a9d98560aabc0da6fa85606a5fa8a6c1d478e199da602a0453f9c2ee66e61740bc0c5dc2acaa7076b894dd4b9d06323377a4dcdcabeeee341542f66e4d3399ee10d8a604b75ad93802d9194ba8c42d861063e906234d9861e5c92ba361cc0f59e7e00be69e1e7fe7d7c99e8990d0177ded277dcd3bb2e335a54e6681cce71ba4be6896d921a8def14420a327b1351b88ea35951f46c7c2395cb0f1c3e6a2765b206653e3336c4f0e5125ae5c89c120e500172629c560d0a1c916de32dc8681c95dcd180c4aeef72e2e9d4645835935a1ffd7dd7d0673dd3b90fc5f77b7730e57357fdbaf5b751be702f5d6dd4b1e7d72b4ae2ce4876da03be55a0dd558b3c15580e71dd8530f20bddd5b07955e8ec14064a6f6e5180c30b49e955d8e0dadb9dfbb620bb8a7328ece85495fa2b4b719557539363405c34007b8456cc807dfa256558dcbb1a124f7abef63623118c56050e28b17328c911c7091e59003a53065a4e164062a7ed0021231216d90430ed264c96231c88028076d96ee0797616dbcd0a0d22e495dc884e992d405c6d8ed2edb5dca2c9bd429ed08a97332cbe6a454cbb849a9a6994c1be71cd7117294d34ca66de3b84e8a939f4e1de1498b89db38aeeb4e2714c79dae9c94885f2b961b3f15afdcf8cd4d4778138d622b3e1b36514944c2260eb1594292a20b172990a4d002af4023284542291742896c96988c129309c117426429be10766c30c66e779979967584198cddee5266d3e7ec08a7cb2c9b9352cde4265347689a54d34ca66de33aefba8eb0336d1cd775a713aa7aad1d61ed4e2854ada9d40d8ee3e074843837382a558d1a39393656be5a7584ab14d738385c7baa8e0478572aae6b6c35b8f6726cb80d1b1da10d7ad2f9a1dd7e786d0d7ad2f9416f3fbd6dbf166ee4125bf14900c0f52c9c838cfad19b8579d4b8f12512e6c1b208843436661ecc0f70b9ba50eaf262012d585ab8b46069e1caaaa8bd050e6abc0c6ba3050c6db8749b2daeac4d1549ce18da4193e9595cd1c689b6a24d12b77b6a66eb13b1a4ebaaaa56495dbbe7bfe600257eeb592d3645f1bba7c23648ae94affad51a2ebdc3d71b8b34966e1a4add130d33d0209399419a96ce6cce9f73ce39e7194b72cd90949a57a9a79a454f96b8bb9b6ccde64472e75cb3e4ce396766b9b2fd2289a4c314279aac408d14a32946d0e092a1e1c506ea3740038ba6ad29926b9e2ca162cb10a533a24471521403353138b90116353a487244120eae3002c240c514ac29a6602dc1d45c11c552185ab89cb4c0a12524b98485961a97614b692c29b95c4f50ea6a1bb86ce052d3449a2d4ca4c1228626d248414313695c90c60a0d583460b19c7067e9c0ccd25df1f4cf00564d976169a6b4d240e91dbe144d14344feef72e16d4bba1567cb32c6b256a9691893bec73e64cdce15bef193637434383925e227e218324584924e17259a1d4c562adc09d25b310638307667a20e308326069626d9c28992183929819be3872ca828b252d2ad690d1a48c18a041451a4c94aa9041490b0d4a324872c40c9d1589440198a8e2654b15675af0c3cbd1788256a144690a1532a861036592b0220d1b2c49b460c344c9891223251a9640c2fdcaf59beb468610c0655819626ef0949158dda86678c0610836a3063b1ca5c9218b1228af5e53c618f432ac8c2ff7abdf040737d4180ed496c102265e860f45b10c1b70f05a5565d0c892e818944933835119336df485195f28a5410207931129a5946664e08c0c718619674e666230d243dc5c8695c1c13d5d869569a24a952a5554506688895c1ac3c6114bd0a4a1010d63a64063258d1d90d1040e195b984c778ec20c152b4062e2023162f0924b6418a1c8386249fb50868832409041e60888a652260565a27897d9a14c9319740a905863460c9728a620238b57a6a67d6032e5871e7ae8a187282edd250a678ca802863136ec808997a7b93e6689a873305d869151811363c28859b3039929469e909142f484cc0e469ee45c86913172e765d818362e970e94baa0b4132762b28c2145331163461231638e8809539b536a3e516a9a465ff33e97cce8cc4cfef4a53702d6bdd973aea5726c49eb1fad49562b2456b12848775e8e45792216254b94a4bbe2784314a3fbad6edb703b1605caedeef662f4f856df52bf15d111fc23ff3a8adffd1e9bc4760873390685cd55c0e51814282e0acaa0c2cf3c787fd2c843a46596eaef55220109f2031d00afd472fdb1e0604eed43e0ddb7bad9f79fbe3ba3b195a962cb646b6c490d524fc8ea6631ce69893804628fbf9cd55b8c506cfddb07ce452da17bb867f65fcb1f429972dabe3b6da8e74fa55229aed6cad55aaba5b1957a2e95f284b0525bada93fc1d3e9943a3d4cc1f789a8f7899b4f8435b39fe26abf1b39aa3d7928ce7ef5b954f53e1db7fe16b9cefb84badf4e5e7fca7efcdca3de27deeaf1457944e23d71efd39dc7b1b5713bba07226993fd5c5a547ecd9660e1b45fad99fd562bcb5a5c8a1a83e24e7c487469fceaba291a5b90720e0e4c856d356c1c514fb7a7af3dfdff6edcff00a07d2c1caed953ffab97a5657a7214aabb4fad6de3bcc979d9d5e4ed3cd8bcec3f98db16b3d7a8e697b26e705e7bcf79f16a9c07afe0aaa7f25656d47e1c547f5336bdd79e3baac5ef366d66b4c1c6ac38ffdbecc7badb7f8f23f2a0e8b3224f2a46dabd94379cad410512793e22f2aa3e47f5f283dcd878c9fd0dcfcdcb2f21f2985efe96e3a9bc4fa272cff7a328705fe3732c8d2dc953c372ef4754be1cf71fe58bc3fd8dc7bd0dee729ff2b8af1ef7288ffbd35f8e7a94b3dff6d956db72f226ba0cf54d902b3cc2f4d06308bf4dde27c4754dbf2141e86ddfde10deacc93399ece7baa69fdf8f7ad8537b52ad4f556fe5791415e5d5ade3788bf164faf93e6d824e047df18f1367a2990c2148076cf263508ab8f3fde7671bc7ae049fcbb685865d8f21f3b5211c7013906e5a1fad7933f840c807eee24acb9c43c2b69a076f773ee0d3f9dc6f363cf9395e7b384477be8e09fdf33df8d963f5f2eb57b5f6f5618702a572670c8a93bb7df7587d7b355e7aa7dfb8f3c19c43fbd8a13051d8beed89db3a12e6e33cf570fec603dafee6abf76d5f1fe5e9ac9e6fce73eaddd359bd8de7cb1d09edc19b3c1d13b6f7e0b7f7c0eaacac0fa18b7a1d136c7cea6d7c0a8811f842c29c804f7ef61f7424f453fa363cd4e778a8afe17dddd357794037cf1776284c1cfb6d4f6f3c20fa295bbd8fa2bc9307d459a1bb591ff3a9d5f1e1d21b02dbb4a674fb7da2469f48763516b4a4db432070fbb7c9c14e024233c0417f7613f09f1ebb21f0faf09fdfdc75e61cdfe9c64f876b861a2fc7a07cdc9c71087d7bf03fef6714dc7e413a3ef8af4f8ccc2df67ed82a9a60d0dddd0dbb0b213b6ea83cd144c80e22d2894818ac0c8085952c56b4b9701834ec1f1a1a222205d0a64d1b28a7704ad77f63318fd4f3fb8c1079e67f37543938d3c9e4c5ffd1f9cfc6473f5a7bdb384dd334ce033231d5348ef3807b9397517d4462d3cd7ef172ef131b767777d79ce9eb6fdb9aedff9a6b3b5fca6ed96c8072bec86d2f763f24197ba651d054f3b31cf84462ce43ef73a59bc12d49558b2ae00a2eba03da2c673e9cf0e1e7bc37612b74cfd7edb9e973f770e4c1f9b6ed7d1e58cd9a7e7ef6a3f31fd8afae6ce4fc90d0c9400774777f1f7cf69f2bddd99ff3a80e8559df03fbd940d9cedab05fbd2b5bd4f0571f5894fc9366bbefa094fd2b1bb61f05bd4c8f29a7dc8df3f87673dddd39ed6464bf4fd7f052aff2268e777a68faea7d3662673b1efe67e33ab34a883c37cf34f53bba6705cd42fbd9b8b0a87ba01194b6e8735d0d6ad502c9e75ef33237f673eda8b0959616976b33a9c3c5713f38f8f235096146ddc3366aad151a5d7f2fa3e372ff09d171eba3fe73ddc9fdd7a5bc15e7804f3b12b807aaf68393f380e1969dea5df842745ced51f673716c755e063a80dbac97c994aec9cb401032eb2eb4826f84e57237fba5cb80cbb127b01b8b3909739f4cb9db855f7437d476ba71b97b6e9e9f5615f426b4f5662e1454e4c7f4452eccb6ffc9be61ddac4fcc5040db9bba2f5ed356ad9b3edfdef424f4fddcd21ffff11fff712940fd45aefb74ff1f4aa193217d81fc3b14bec89107f51f2ca2db7f5a51b6d9cc7ed31289d76d96c1ac33da3e0193fdcc7ed0ad8ee9e94bb7df0fff9fc843ddbf54d109c8bf33ea295b69e6d5a13ffd81dd6d93d7e4f1cdeee6b9dfcd7337b5cf979e7ff46ebef3389497fd4068a2f329a4260be4bf3d9d2aeee21f7cb85920a790c2ee895bf7126e6feaacf63744fa721ec796c97edce5caeda7c8ddddbd08143ac50a83d8f2a1e363000b112e229a103181d20212e82006145ccc20091d62b0a48913242c405d8ee1d0c4f5cb31264ee05f683f3f0395c00e85faad1e7a10872270c0b26a23eefe6cafbc62fb8eff98daee2ebf050984a83f3e34038729f7ab4546b75f5584453e67f64a0b42f795114909087deff44b8f9b6beff4b730a6eef4576f774a4759209144d1d0d08b5f4740fe2db82d81002e84c5232e445df5b3dea56347e4f9be69757f0e7a5e6babbd7eb2314b29a59452caa710ca876c81e4bb7f0b42574a6dce39e79c734ea6f4e79c43e6d52cd0fc6cfedc3408e9fb440ae16681e86bdab72074a9a59e7955b56a1394406c5bf450e8d5d65ed71f3d9d1ff2a3058a2fad4e5b17bab0ebe19ece8ff8f0210a11a81fc8bf877fc34e15d38072039beb830548961baf70a3fa715207da5ff63cec5350b3e7ac1eb35d80e3c6c295331195c59bdd882dcaed506f782cc09bfd06335b05ba0761191bb37a883832a09ba73797524aeb0f5f4e914929a515c2dd1891b01139a7121dad22035a95438d4f99d8200895738c105bfd21c456ff8ed8ba41fd7694c05dab6ac5ced560c1e6035e55edaf81c3a2f44607af56a9209bad55477777e3d8d1ea68371e54048fdd8daa686ca7ce5e8e5949e2320036669a3ae10667628bad1471e19543549746f57bce15792015f8acd8035f3a2e8435c4c8d37101f5a3d7dd5b273ec7ac5cb97c5da8a106268473ca19b7861c4f71072797632b7072755c8e59e1e20ea90c816d8b5919326f0b383693564f1f65cb7f116857d58da69656a2a3f9da536dfa4f6d121dc13833abd3b26374629b95e8c8e5db8da308fedc03a3dbac368cc0c62c8f2d2ab35459e7131dc9ec5954056796bc11b9ebb18aad4e511cb125bf155b3ad4afd5922f694b84ece5e37051f99c7c55579f088b336c3ee08d3a57beb81cbb72c545c0e5d8152a746aef1e07a5a5d2c9fc8d47fdaa08a69fcf6a51b973c6ae2c4958898eb46724e6c95eabcc437a268fe8a875a2151b57a2a3d84f74c4f07915e3cbe8c13b3de6563501059f048a59f6838478853208c11c51ba9f0b07100df8c0b123487ae2cb872b378ecbec8a155bf11b50fb617f8540fef287bfc41179dcfa0c153ec74c763da4f5c14952a09815e5931a4585ffd1937f8d3c6dab6bc110c2673845e50f7a28b850ba8c3540f51439f2fac7070448a2c667f931faf3abaad48fde8f0265b9abd8825e09ea105ca0b8f02bdb18637b6c8830b121963ec6311bc7addb6726bd23082ac78630ba2e042f81455ce900861152186152050b2aa0d8e0743936c40d3dd420cd10479e644963a4c464cc0f323c7183105474a00249bd1c33da6224867feeee325466668fc2e5428352178bc5c69de58f8158c70c3ce9c10a1bae4c09c24a961c60335471819ce189519635555470b7cbb12a501a1a195549e27277a476cfcbb12a4baa3881b033ce609d11132216841a5f7221ce18491313628d106a9a5083060d0721850a0b7460c21a1a9e10c2488c8a127412c4951bcc28c0e55810484140d18519259792cbe51a8352178b25c69d554605676c10834a0c48c4a092840631bacb312a405cd4e5189526b12958623e34e1654c81118589a960e86a97632ab8329f764ffcaddfb431eaa317741484dbb6f8db6b36c8e9db06c18fb6d6de993f9f0046f89b6cffb6451b9f53e36d7c8d0f3a0aa282f0fd83c0777ff89a0db2fab641fd2b0bc4061d05c97968835cbd333fe76dd89cf9d0ceaf6183e6abacc9db1ee59dbef3381b74a45301d36f1fc4f49b0d3ad23e887bd36b3608ea4f360816f5ce7cd4c38796b3412bf4cefcf8db66a30d8243bd337fb345f1a10de2de89366828e82808f7d00675a077e6731ff4d5957a7e7112cef36bde3cbfe07c9f3679fc4279fca2de47e7e378414741b6bfb141f0b53dea53de0bdc9bbe7a2f1c05e12c9cd23bf34dffc21402b400652dc4d23bf3096004675f083a4a815f2f937d413ebf3e88e5be4e1ebf3a8f5f5c7cf84311d783cbb11f90eec691e3a98ae417a7c0afec55802ff962d64409759c43b09c1586cbc3804c2a7ce67ca0925d8ef920855e8ef9e0e4ae78eae5980f45dc556ca954dc3d91d33c550caaf61f77551fb897ea7f23f2401190fc2b8a10bfbfb626e51639ceeaa810bffb14e27756e86ade4737ef83577b395fe89a3ce642659b622c636bc5166c4fb6ec9e340e81408c9db58c2fbd28a394d2e37b73941354f85e21ec87d2ec933c7ffc7f8aa8c77acda3dd8aad222afccfe5ea6f0777d140e4694fc54b5cee5f8e3591729d68b9aecb31273a5c1f887a58baf2a9f65473d8dddd6e3f95db4fb3707624ccef67befe4ad43ba1847a71700e20d588fa71988fb9505aeb6af5df0af3d33dce71f95c492d2e5a5cde700a17ce5ce9ca29fc19fb97aaa4a454f453e444165d08933f59d7c3852bb3222933e945ee45b248baa7a103951c90dc8f48949e8e9b7dbfbb5377ff22b1fb33cb1004ffe835991b63190842fcec63d643987b80cb31295de00b3e7cce05578aad289dfbf491e75f30270ff44a7f0533f86c03223eedc75dea2d82f6df3ddd2fe1778fa655e10fe1ac41f5f9124678d7be1f47f46a5cc96f4b1b5ace3d6700d21cba7f9194a80b91e70bbaf37d898f9588c0db1e77334f155b120ed51588c49e1adfdbbbb18aad88458d485ce667ef9efc2ca34f23fc0d72974d9841977186c8e3d33b9c5742e4690b2184d095a82bc4567ccea77bc7e57a404602b2421aa184cd7e9456cd7eabd57fabe5ba2e38fc2864c7bda1f20413f842175a874e9ea8aa1cd46fb56a4aeb675ba44871fdb31fcc03e68023f8300fc83ce03781268ffbf80df9d96f385e777323f264b02b5fc704fad9ab5ef2edeee633efa3ff657f9ad4a35f43fe473fe321e9ca9f3c20ddede9cd53affe0dd4670fe737954fb78e63759d0fa19bfa1b91c76977f33854f535bc2ffb56fdf62aab73f33a26e07cf738df7d47bf3fe771bcd4df783e846ecae6540f0865af0ece66d973cca305b865f6a394c24be9a42b5c3a69072ea573da2ffbfe4deb4af0819d8fecfdfd9d7340d9e307e72802855471b639c9f767aa924e4deef6747aee9db539eab93ba1e449ca93e41cf0396af22bbb1edac3ce07fdd94f85b06e6730f3bc6357025fb7425826dcc86286177d821d467d14e554b7d97ef3e5a33c02145151af7d7dee50b504be284ba44d70d489729388940240c1e6428a12fefdeef9c6713fbdeb411f763edc3f227ea394ceb0a1a1a1eb345aa10dc62628a594d2497f52da9a169d6ea8948c97753d2b95aa1908000000f315000020100a88442291581c282a3f14800b819a44684294cc22a124466110434110031886100490210619800c5310950db8b9487decbb16b870bb779ab33b89eecd4a8736ee4e54b10a74597fd539dd3c0962ae8d94a0b1330dfefa2323acc8938b83c455d7f86edb66ff51907e74422133d08a4ad7300e99c72e07814335cfe86ca3a006d9734a71854a6b826f0c5db1304fc6b2d620fac81763cc28f36e067bc69415b3ad4ab3028bed93f7b979d2e5a812716d5d392472d9586bf546d884cc0c732e5928c989cf89625f56a243df0d1316b46f48df0618d0857824e96e94b9594f0c4307d0b54e336cc0e38ea1326160a069f2e3296ec59eb1d41affcf7112dbbc941e1f4b63a980115710f101ae39d11874ea4d80c0f82028c09dcbc7743a6ed5e8a38d256a3c1651ee822c30b056b596b27f5947acdd0cfb364e6c296a2b16924099aeec05bc53c675c1a74e8681c32e548f3c0236092f8ef9006a017a6c2621c001554b7534b16ffde79d7ba54e31097bee1cd9c32047e3edf741789181dd0fd16bf769ad2be274e143432ea3407aaec67c858d1d85993126b5c3aa42bb3173eb70935716d341e26363e602df3ec3f27d4a9b27da55a3364e4398825148020ebf0468f31d5d19a0c87c201c35ee498df889ca976b531921bae8b6c80f43728d8821cf433593f60ce394331e811f74346342a82c0ea2c6d7fa66e06a350536fa15b0e3c4cf1848538b03fba583abf762dbbf5f45c9f08740b0b0f87f844687a81b796da2bb314271576bfc5d8216e8704f2ca98a7dc53c7112d5a7b5d6b6cd57452d7a79bdb5ce7f2305542be036ad9a2a803bc091874075fe3dd87b84b9d10eb7329a0d4ab3e91a3171cf0b7afd53b34136314abb0731c7a88ba0388d2127477d9cfa8523e0d5312a8b2e8cd47f1608863599087f0571239a352f61bfece7320464a6c3c4a8aa792e1bce66e4361497d114c4779c27aaa826144e6988815d79d8390db00de99a02d6fcffce31b6d51be49e6a8bc1439ca2a6e70512c805b9b3193f5fed83387c4a2845fcb6289590de2562bb7a341a0ac7e10499c1e8886687076c7998f2c7fec71aab4479a7c674c087513d4009e4047cc0d8939218a39390e05018255475db8b34a728e05b490f0c76f4fa9e5074619f7efc3d7ed6c9421948c1cc795f1808677a882ba372c3741fab95bd364dd0002fc6171d719d664c2c7daa63a3ee56e445f36969bd6400643c365ff4bb8f42e4b3c91a35deb352ef7230ba2de2402ca190508478dd43515a067a616c03b4f2fb7d860796ef4e0f9fce28626e50f5717c3a3680da6ee64db7114deecc45f9cdeb08a7ab284565c0fdc1808b46ee8a2440d39f4d6bb67639fe41187d809afbf3f8b043ab4cc7c705f0bb34f27f301837570d1fe8adefc3c25e124a1c0baf9d95480ba510bfbed4fac13a93205b4b031226033be1d3475919e59c75fe09bc6d7aa3fec84fcde876b61543d6d61e3b5aaca99690d15a292f149d3609f94f56a046e42cc88d6227650e63c7e18433253ac1ec7667b8dc138dd4d8b2c84af624f26a7279dd3badb908c0b3aafd8d541211c0b54513d810e5dc8d0502a8ec2479924d5f5a1edcef96e161a83e8fb1947ec8883d49fe8103aff1b8bec7cc613775a7789043995124ffdad24b5646e5967bd841da040ac1132d0cb55e01c4d3c261346848786eb2daa08e77b14ef13836bd086d6ea5c22f1104d80bb463c7b48e8d8dce2382befe04bc0cfd980fbf44cbdfcf5e394aa4afb93a338855b0112368970740c275a3279508546f2a34b2b7dc24b76f7e65c7a35969a574c913d5ad60306ed916aed0f559b92232ad5b1ff2add0a210299a5642e93f518218e5ca1baf7cefe9330dcd7486f6e355988d5c4dfceae45db0af3d2d4c587d664680377e9bdd29230d1fc4db7d2180e062baee918b725d68933710f6874e5cfc41c515e897fe1b688fde9a96de822d5d7cede8b38b54a177b9cb040efdcec84bdf56a4582cd55295bccc1807b1500ebddc7c28b8da64c5bc8fe78af41ae0f1bbf48208edda6873cdcb5d77180bd3d038d47edd4703f5096dd03e5b9222fc90c6c28282974ba37fd8d973220824759d07750e240b91518543256221d55c328195f8b5a01083c69a90068f89ede62f0bc40601ff289d58f04445a6a34fb0a9ec00a2a647cc5e9ed3ebe39ccf64fa04a31e4e72e5cfc495ec2d35a194874765498a3400272cd1ec68591f90fe8c7176c4eb6961b01c5309602261ce577b12a4b0d32f514ff0acc1584fea77d1b11d3246faaf0169be8adb51eec3ff63e8312a24270a5d9a7593940605d4df10ed39a900265c1afe1f9e4b2dbaeb4401ec90b406201ca8f4558ee5e2522b2e89d0a35f1537ea907e6616b5cbc97ed074083794b280fbadc5d714fa10f39913684f6b6927cb1a6265924b5368f31b18f090911857a281306310447addf598e307c6036a3db7f42e2ed14e6fa85dcc021aea87c864d22b34aef201bffab1e24d36e457d92363e27959b628f0f0d797e33807c9082b4b50971c27b3e1522bb5ba9f8e787187e138d06b2539e5ea0d961ad06dac6325fbb99a238fba34ae8012be6b85d4f52615e9836f31a71f5b20eac2bb463267e6b51d227ef3746a84b6e7b4b35d125f40eccd4d5368ecce02026f0c64680f4d7fed8656b304943dc954e89393fd4be6feb13713f8d7998671f58f9beb62079e440f0254ca6c64ee414cf58d1eb072a2fecd70d0a6ade34b5ac4c5b41b67fcaf6fc09edf332bc71510efeb1af6136700c503a602c29ae2ea59a6dd709c6c06a3ed10a50d7b9de4cef4d66fb94e9dd6b5ef22740997b2d1b9c45024f392af3e89ffda40d0609606b6e7616edd4d614960b078fe5a23900549c6f060917d6e6e46d59a2bb9b33b2abb73c0930b2ee773d2208aa2e2362a046dd49093cf92944bee1c173c2adc0bec65b925a8d5533714cfe058c07b24b6f98a312736ea4d0a42ffb011c69ff4ce544a00f7446d48d49c7bc47c99f7b76d47a8f1652d263d29954b249c6713f7e3a304e8cdf03398cf55a77e5e1e6f15ca0967a5dc8d425665c0687f1284737aed8c1690cd41086267a71e2aa31c2d8042d0c2fc5948e18c99641d3e23fd7f4462ef690f89db443499ccd987304da9e70d87a373b5c40a131eff79891b11dd3de640e6c22ada37a7653c2bb75470d2a5e2e2caa2e4884e87972f283f8ed3f920f0274dc8b1efc3362a9b1b45d0fd03293d787508e785e9ba01e42cc26d9a6b9ab08b98feb471961be1efae4a4df4e973d91eb1f294098e48a4f0ce6d3a6fb0cf64583571ee60d6e69caf361de4f8f0e82a02386aa11ef6831a1d012ca84093dceca2fc40011a23e8c5e83dd60004e50e0302c709a87fcab13e4259c8ea84b1fecaa1e583e9dbaaac2d04ef6edb744cedd7fee52d7fafd3296ab6dc2dc591372dce6b29c939440b1183c6e0731b868fb7e7573c0b44b68d95325e38314d4d793c888fd799dab845e4c6632a5ce4b9aff7a715f60e5ff14f074a84fe71ddb953fe7c25c29c2e581074313cc3d31932876ddb4610aa8696206d56dd7e746ea00b45c788e38b96111e94d7d87a1fc21d4795624fd6840bcbb1213682968b06a4ac6e861f6c1158eef1c4e7d07b774a7fccb78ef705830c51b516750c2fdaf09685b7bba85303d3ff87f144a1182ec91a31d4b94ed7a8541a2b08fed867e22c70392c45e0e62799905b1af6ea7a2f8efd0205bd1b14d1898b6e28f2104a27759f642f9773c8343af13c66e2228857e79eddf3c62ee595c2d1bca7f36b5b5753903a597c8fd7b57df548db65ca90b965541996619ab58bb1bb3ad07834560a358c30a0617b3685261ad0166c2002aab63fe35b22d825a8b41eccc3116552507a7f1f17b5d4d9d16f9071041e746c94ff5b839310279c5f46159702d971c14e2df44420461e0a510adeb8a22212ea2667045358899340b78610a7ba14e5412ec69be891c8f5cdb21363b86a31a879dca993350d88a9531d1b266e8780646ae2669689341f97edd51b5118cd491979b5070eb2cab717f6490f36fb9901d34c1cd4f533a9436a4c320c58776b97b4d564f57057e59817a9ef1d262b4189816b94435dfa6bb2eb5d22438d0f717d699c013243ad78eba644d3f43910e116ebfb823d1d662ac444adb5182c40ff484fe3517597a783ffe08fa15d69710d149a93b40b7fe07c9a5cd639bef74c44d72fc88361e378ee0c656559918d69a9bfb51a7384823ba7bc63f9ea8ec1cef9c7f2d73ac70a683fa949536eb29e70e5ad58a9a97b1fd85e72b6c51c0eff1de7893d5ff6fffe39c484800e1d093f186495e34a65a5da04eedc4cb02fb768da16d0394cd0acc8b8d8f9173c24fd5c0d47c0838c04a42dc4b1fe328c4e14dc310fb4403a8697815b5508847f41421387bc8af3f47716fc5bd01be1a6352f93807b989261d63fe7ee0b84c71401b0201b5b9da8fe78f353013c7d6edf3742183a04e1a43fe5fb9f8fde412fee769b58b2723a59b1955ffcceca60143fb2e53d8308b587ab5cef6be365cae962f9d0b65178845ec43dd1eb40fd02f324b739d23a57d066aa2c8735695c768643061e99f143bc1e4b654419ab5ac74817143beec154700e8002c166b5375ca98ef663fb5f76a912cac0f5482bd1411091054a651928cff3a8641622a6af8635de343c35c1a8cceb78a3fee0922a1cd02b2561fa14ff7fad17392a8545a06eda029c0096b1028678b54cb318d9019a85784f0d5f2e658eca799dd549802c2e46a6d41f405dafb8be0551a4cf2e3d82ee2f367f454ebbb579a3ffbc66e80e7f3a133a7f5cb5d5caaf1798c0086db9765d0caf8559d7929cef8111cec84a186d8891eb2a2e8f9c129c4d38414fb5a4e85e1b3ed8893ed3a1d3c258749cb5bbe3db2888c06835d1a64484292c6e1617259f3aec67f794aac4490ba86f5a29d83633e030b1b21a6d2d5b4331ac7c2cd8c68a26c25fcf45777907ce75afed7414955eb50ea640c39f20033b0518d5efca057d259d9395154536c2b0f6f097756e0d4a37cdc647620e06391e597dc347de0449486b9c8cfb29ef231fd306c85b850a03086c67e6b0355989d161192651c9a9b92fc7b6ad534274c6cf743a80842cb26049b02a4ff37b5751edcc4279b18544a86cadb0d8b3fe5a5a65ac822b31a020001f0ae5618942296673da4a9411b8099590c4c812729762c4655e04b5d2886fc27b348b53f9d2ca39da2284d6c69c7e98a21af99a74e2eee6db01fad56f91f11933d7c2bc8753785b29a654123101be02d03e0f3206e3369d63ed00ecfd299e87a821b2d56b3395e6714f5364cba0b4723ae672cbd89d4a73869127575ad5a4b9e0e4f4a704bf99c404d1700c0803ac83ced0127b4aae3e56b7099a2375580e6fde3a9a6c781c033c7d309a0dad60d51e480096ab9d9038c5a04d3f1940f82356dfed575ef67186f2c7df97911e2922367658f1e86f7bd9f0a63285e4a67320cb0a43660647c9b8b1391eaf1c8f13b8ac490258dd9cecbe900baa477648680ac5ae59ced62e56cba955bd4752de1babfe75a4c2174922cd028aeb4fc36c8b44e95e410e6f192cdc0fb130bca33df78213df77fd00f2ea8699de55cb9f8d0a595585073e2600530404e2f7084851421a7d0748488a438a9e6b72da854a7282ba6a00745584b44923d0b4ac52fba92109d980a1911516b3edaf98d61580b4870c914a7beaf955999fbdd7c801b7bafeb0b3821c00623f7d2438ec0173e80f11fb535f50ab3ea49bf3521fdedd4338b322e6c0d5ca34f1834b7134b1fa43d2c52af31fd8878ff5fd50e14a10a314798be0d3153629d44d23224ced63a61044a2aec12350cf5221ea100e42b4a124cadd584f416c9a9f2b0d8a85c20c62bfb42005db7cf47d3dc2be9d6ee0d164b19d32124eb436030fb091f97a78b84a871efabc849eb455884bb70797262418ac2de5bddfad7bca2107e129f7ec268d8a9ec3d2c3d35a229558a3664590a6c2ca801b8064983c3146ca2398a49adc1429b7c32452da5e499ec1ce2e0a90740e5585c67a4aec6cb2344dcb8cb438fb8557b3a82550934e860ae36a32bf77bc9b7e5079eade869abe2af8f2dde1730d772be56d5fc6d2023c01476a9a1e3d9302dbc177723e2c777784709427e10665357aaf45dc339a5666fe89d12814298694857be9761b3cbb5e8c3be3a07309f086eb7e98bac93b4d5fbc158f082bac2d6467e934d90b35bd5901f8bced8e2fe727a290cf828470d60fd0e24824d76caefecad41cafddca4681fd53878a9d27a40a6f942ec94f635149ca394af4dfa1c2c591533aceefb97876573d11fbb6c921aec099b8269c530e0bc825a33f7ef40015cd423ce40dc2a053a9346365653c48868d939346d7f524b5c26f27d14e8e952b06c6062193255555d057c7eecd2d4f8a77acffce9088e43dec79fa84169eb2bbb4b234d39aef8340279a62544927f5ff96a38a3d127f40e38325965208328fcf0796cb72981ff1f102068bb170af5b0a0bad31281876559be5b84ab8a68b59cb6c04955729a9d4183e54cd1d7622d4aa7b6277a60de2e41baad3665a339798c59eac62e9d41f2531426c1a17c6f77e247408cd1f114eeef94733732a4c6907eb634f7982cab9a8001826550a84c02336dcc746d713c8a27e3ebf2183e38e732d194b5cc3c4f9c6e49609c931ac405cf550f3fb73081b5b38d2646448163da41e384fdb592ab434195ccba2becd05cc02392acfc361aff70cb22f8a161c4b33536a5a674f00f9c1d36f331b3c7c51386ae9363fc2f2331c2ceb3ee1ee9ce5cd7ae6949050555bb61bd1ed61374b74927a7287d01104bf872c00bf3cbd892ec7010133f5b22eba18fb5dc7addc06ce2971eb40539ee47952e672aba117abbd4535e38cbaa58a280a2d7d75525b00658c1049480955afc4a053f876860df136a589a49639e500accb05cb093027c85fd56415d2be7ed6623a6ac5547975801a15d1dcaa60a34bd242d377aa36d7149c8cfde3a027519af65ea79f89cf76aee485903e93a74fb231d0faaa6632c5fba8884323ba77188a968b8dffd8e2866469d18c165c3ae791d33f66f8db8c2e935f6b167d39528c97db2dbbe57a1ff3d35b8112d9560d6c2aef6a95f36dbca3b6351ba7aa1fa478473837a09a7475c930a6a96a2496da2b370f9ac70ecc03c05485d0c1a67503fa765a6ecbce8ca7f075761a6c797b6d098ad2ac07090313f5914ffd488c2c889e4db679ab53796a71116291d6c60d6dc33e9bcb429bfb611d52c2b981998fe1aa37cb392f75a0a7283bb990be9ca34c3e8441c8eae40df0a7368f1d3d0ab60575359016cb5225b507d4844d9e05e9be665dab6d23fe76ffeef260a900f49e82d4cb1418525cc684b49cc3e8db6b7d49aacf1f765204ee4955cdc0750c454e758d297f5057833d27fcf2d2a9d427554147ab0920d001d723720cb85f7e843f46244ad72aa78f645972518a27dc43114d4aedfef5c7d5f148f103b99669952880e65138c95eab664c10c34da16dd381a69812437dc9156106439e7a9fb369e1d14df1e8597bec0c68407048e936d5cece053592cd92e96312924d6e2a6117878587f7924dade00945045fdbab82824c93418fa500f46b19cb063e5bfd022180dfa39f21802c267fe9f01b01b309117eb69af7d1af750304ae688e64f29baa554f1572251fad8118f60c5f0cd2e0f0eb295082e46706da5b012ec4fcd5063babd2b5c647ae08a1dd3362fff943bf59e883850e7b4a3577fb0124df9588f2ecefcd117a505fa111da07946ae088bd31f9c5d6d38a7f804c2f6deb2a870f9ade1b6db8359ff539faf478671429a982d10704e158fd3d64f9a42b90610da7959d381945a1f5ae5d4cb7119a61747224bcc8cff3406d66b1dfa6a66491c476c45d80ea4ef6370933f87c703d35fe6de2114619bc2a92000ef7e0c8cc2fbe286cbb0e1a425ea21a8074aa488e3e77f5825e2f0009edcdd71cdc5b7a3625dc56855d5de79d35c3c0d60bd5f69ee0c684c5466b494a036783b3ccaa573a2bddcc6b27c48823546a8d749ca5b98da42af9d053d207b1c1e27686bfba4cca16a023abb002ffbf244dee271fa9090b4203b3a12335d5ad6940a4dc0e4a627d1c4d49d2207c5f328e6d0720fca9cfea414da7cdd17702766f89259b605e1bad68291400f8a588b6dea4e96e9a6f5aa7d377cab6b51283c79d7c5ec9744757e28b76774d3b1d623a4d80af039cc5231ad6936f3135882636286e7014818f48658b329341c2fa7ccd17cf5eb470dc9c84ce3461f5a54726204fe48edec4a252c28de8365dec5cce59a89bad6619cf9393785c68804d4ac01a99604e794d7a58fbe30d8fe69aa2854e57b685b1645d7e0146a76823bbc1bf492089500f20694a5b316a37aec1c3689fc80874dc34e864009918c34e54173c79082f344c03126c969100e4e02be20d952fe2693306bcf9c0782cba95434023bc7a4544ef949e11a689f9b75ba1b3a22a5ac42efc204add8b4e5e9f6edb3b0d9856baf4aca772b6509478d4f519552319ead2c22e2ac292a10e8b9f01174b7218810706e168c907fd9585fede152bbc54f70351dd0cb7a5a95b29b98c29f342c1e39c0b877e7673fd99eb365b046f3576bb992fe2a2fa8e2365c1a910082dcb1a24205c001ef6ecbcf132c7a7f2e992f8fa49b3d15a6885047f311493fd94a22e7cf2fb2000cbf7037134b6421554104862286af50ad02e8e13d797e8be1681191c2473ba0969a0093c9e25a3d3ddbf8be77d6a0947525a1de6da7c8e9b7b76349b949f89c3e245fda1431a7bc28399d8c9a370619824738a9786332260db3a684c28158fe4f2e3641cb9cf44f6fbe96420cdd6a0ddd6c2ce4e2685a89921f92713675d77527fc86cafb4a87b9a4aab5cb60b1e41976e241c9b0aaa654d5f3ef2830026001d15130508154eda19d8bcb8a66cd9354765d5458dbdf82075211a7af0225734d14442ef83eb60beb50e5c26514eee8087107191e817d74ab2466934c451a1259124b933c9ff92e87ec358388004ae971ff00a9a9af7924bf646144e1e5e3a6204374532bd829a214cae9ade28b3115a7be393938aaca27f85ef0648c137c6bb38256ad7e790bd513375069db6ccc594da745f97480ac1b09a59eeeda7ff32789d48ebb25ff9f75733fdee77c1d88b5b5c14a81a7947950e2532b0432e32b79002f266aa557f9a5c3c8cd4cf7b4ace54089d2617ab2c0d190bb391af53a9ab14dca93775bb612275e8908fd44dd7398fd333fb747bd9872063c2b5da235bc80ab1103ab1257c97e689cca3bc323c295bde0d8aaab0718c040f4feb759f3ec6f3856a1e01f0cbfa54b38fd51d11203a4e76ded5eff7ca605a4c88f9d81a8508cf2c7a15eefda10ad358f09ae2f8af4ed2d36308e2f7b2b4c9b6c074f3cef23b6055c66b0c85a0aeda674c922d8f5ad4d2d1f79ec1c1b85f152485ad2948e224512af3442daabfb9ba6a1fb3ac56eb49f14922a03f0f84ef49bdc7fbbb6e0e811b45b89d006710016e893f67852d5949e1591042fd44fc42873b6d01ef4f4d02b0d384435bdea148785606349262631986605f9384319135e845f96002ee7b71689d45010272f35dcac969ba312cbbbb787df33a5f28cfd731a6fc75a51b35bba175feb16b1ee2a030a8044c909e18d0c73f84c79cb9d3c9267923b6d83680644a10c17ba83c058ea8eab6c3c84a942cd629d637959fe56e6e2b0121fd36523abee45f8001504f75f671e5eca372f24437a5122ab714c777e4a801d36c869f052c43b447ac993fc5abae9c319c9ac985e7a7d6339016a477249a004cd2e327e09db219b1a3c07f6975ae1ee117a6fb321acb84ab7832c92d904a243a5500d5fd20218fd63da0aa9293903b8459eeb081e0255ab2ac67dc1ede5753141898b711060c9380a4e21ed9f4b08a699e5385926f858485514cf89c60d37eb0b2d35c0c0efa3913c1d63ec30a8cacf0d8b94c607a4211eef0066a1f16243269ed4e8842f075a4e5f22e332bf403916f5a79f0bc9874d6f1ec3349a81207a085115c3b82391c584d8c29d3175488bab3ef948aa3529533d41a053fd8abe4b1d0e6735305412f3811a3d79c3ad2e8ad074fd9530dc282d18c4836fac87960cd38089cf05044e0938254d846408909c40aef514b229dc2b4baa8aab5d154a0427f92609af96a8c9146851875c6f47db3824a43bdf4a052ac72c16d00a27fc26e26e9cbb3a222fd13233458c54496d9212fd5f09a505149179905a8274b9ceadde06238d0888150b30e2e210fe224bb19f005924c5221eb0c6c7c4853c1c24c6b0604cd9d39d9f9beefce2985d46675f9648450897535984c6b527ea3c8ccae5886087f3bea0528b6709304f2945cc28f046cbacaa08c8d19e125f843351fecc822b301fe6cbbee267821148ae0ec3b6749427a059114b267fe66b17f2d1d7d9a2cd49b18b99ef1e632f5ce4904e950663dd55e3da0de4551b5cbc5064c6b04754454e1807019e4a88d1ee053bcc61d34e45e5c9fe0c8e472ca14612347f2c84bb81099dd904f4b66c6bd5e8089facdbcca5a515dc0046d7853f1553bae35a39df80dc632762212158890be7de1366b22968e1995ed9fc44cdd29b3dc9d9224851d7396577528d203bcdb1036a5d30a6f66daf58bb4ae8a32b38117009e08627ec2635f90a6b0bf25d01bfdae576409297a6896e8430269c85151b495c113309f99046a99687ea3f7d8655ca1b023151be89b3c7dcdd71e671bbe3b8fdd785f62ca523b76b26e213b9f4a815ac73e3b623573d8a679e30dcf8c474c2b0fd899e5e9413ebae93b8cf5884e7c3b25d760a4d4e6c53a7e1ed1ec4c918c9327bdae0a8b7da141a84b9d538a924f8922e32420581414b934d1e80f1b61a9780430bdce3f5cab0a02bd9bd1f5dcfcab883fd9e86cf855437ab84e66b67235f03f477aac0fe90f77a891efa49875520bdd6063f1cfc76c1dfc9f761ac51034c07411f287621848b5aaa2f6ae92034d8e229df569284d7f2d00bc1288511fcd845c4972d642beec2a47d7064cc0578ebed4f788476fd7c5044ff7856216e24d6793f50935a7dba16608dafc7d83ec48fa872458e81acb505dbfc321310e458f68f4ff5f918b0ac1c1825d3c993088c64a195e0b155d6119de777d599abed7ff5a23e79d88cc00228602e88e5e1560bbcd958c32352d02dd2b73e0cfe146747186f1a681f00975a5e65cf045846c0182827835c4e7a1316007f96a981a1d0882cb982e2c8a36e954facb1a4a7ee06c48def682c91aee9e2995919aa1590da68494a87cd02c21500def56778225a8f07e6e38ce48e054814dd4a2c9f7a71bcdb208f097cd2e6894606ecb3e37d4fd6d18591c9455d62978bbaae1b821d437f3d771a624f26b04df3c2441fdf579541f5d6aa2633c7182c721a0b5197a7dfe0767b00b34d8f04636e1c6bfecb7750dc7364c8d1c67ebe7900c61dc44c43c5a74d886d7279c985d20b2b0483a449276887f41f8161bb05e6d4e751d61172841dce59efcd3684e9bd7be371d16d958c7be6a79fbaf2b150f87c8fa3ab2935ecd623b5be9b0b3b9d31493f75b0ab44a7e9a099fc4ac18a777e8be0e450c280ff1ad88cfad712088b877ae506d8afe2728cb27fdcee15163ce06f12e82e91d91f98178f1d2bc8759ff208aa359b0883a32ae70cd22b7ad0927b3e476474733d899ac2e79fc8385d90581558d3074a09a3bfbf04c26285aede37caf835a61579a9ab23cc1f40edc89de0568038c39f91a00e12ff10e3db4949d5d7ec06559566fd2ca9e27d6e1336527915c77fd6298f387197205f0b2cc3e645f11ee8e942f230b4efc0d83e10537fc69f573a59a10767c254d74f232a6771c2724217b850916b9ada09fbb5d424b9847280f3edfc104fef8d8670621b2370e369cd68cbd418e74c18b825c415777d09faa021125bcd283a794e86f3f724051ddd1acd0a30b1f8a564746ab5ab265ddc4ebdb8bdaa57f6e097390ce6bc424bbb0e87f0040c8678ccc0fb460c9ff604d741c21ef28030f9fcd1f161f91caf79d3dd1c9836acc994c01e6b04a3571eafdd96541925dfd66481a9962771c1015202cd1eca436900d82aca8ac2c2ca6b451ac11ad0662386d23b31c33f6982b1e7e0a6f636df85379dc78c058cc84d5545545515fbe1c3606a7a30702d223c62de54671b7082e0918a1d781e4f37468eb301e01ff03c29104a251833a2a4f8724a23b0650c7107063f2617774e151bff7b7db3d7eba90580a771ca1d931a7b82a70b796100840485d00d8818383bc8265ef44911b3a207bf05b36c8076c0b4f0bc91e25da8a96be76dc12047a3751b192c70172c43121a10ea50af10d8da1cae133eacf219dc9d8e8d92be1b9414fc2c0ca47bd6721d540f92db5da30491304d3dea28f1527da36d3493768e7c1968ef8487d0e08db5e28cab7475c0fa8a3dfba8499888b7ff171604c825e4c1787e9cc960a8f8858ed6ce4dc015bc12cd59933466647ae906e06e36ca8379c273c4f8a7aca8e35102abbb0c01bde36ae04ead0370bdc3b5427f4a033a4cef92f1daa026808ee65679c504c86664e9010660615349b458efebe62e57ef06d97db4f14eea0f14101d6b6920acfa14f37405224c583d80c2c0b5060f54ded923993f59053d549b01e1770f29595b5c9f240bd2871ffe2a36655f5226812d28a8c80cfdf815ea340efe713312dd8b0514d4b421cb09eccddf8e17b4d5b43f1b10cc9f6214cb3ec3511a39cde9aed1310a4d083ae3924e5fd23eda69e3c40ba06b3a3abd6e0d32c6583624a91e8a49c88b68c44c6fc87471f574aa4ef32e540c84907f0a2ccc099f78a77236c1c08df3acbe469ab359110eb2129b21a57ec81417ce1129ea579eb94e0c284235d6e52f0bfa38ccd7f08324e1b3d374280034dd5705322c022e3b895082ca715699363f9131175431aba4912e364e7559d89147d99d65a5761ae54950748c830a86d20fbb472d390466a72f1d0d1d02df2a09b5ea1b7fb9a939b0b49f904fc7fa90e4e1f4656149d39e87b68b9cfad8b8a544a0ec6a34a11cba620f315051aeb110f4878b314f7d3ad9bf0b84f04e0bd35519f967eac13e9b947edb83e3e877040691cc25d6b2b05f283d56bf898e8d7edfbe23bb900e2d2e73f067ba197c8ed56fae2e344238af3b5a7db379bd1205deb399b25e524e7e0bfe21f0c93b26c4fa6930887bc1bb42989c3266fb3eb540e4abc477283e9870d1773ca01f3501076a00e382243c5f61c5316e301c852bcd4274653e420171638ebf81a81920d598223cf4acccf62434c398a433a76a0545775253f32fba4274fa8871a21da06b38f50563a39468ad34b38f449e0f31315afca9ffce5a8ca59c0ffa79dd640660ab0a77a4ed2047a65047f8c8f65bf420f4a640beb3a3d0c30906d758452461b6fbd2e631921f5846066badff5059f0bcfaf657b157eadd4172a7489aaea04b47f18de38ae0ca5159f2f9aaea2712ec7c51c0d982ae69300ffc86039724651080d23f54fbf8b766248364b455970e5f917e4055629075ef5027e286706894d814c8924cf91fb989d5df962ba3f0c31b3712390acd4a36592ef0753d4b6f40cb5d31303c5a3d658c0fbb84f79924f576e78ddd96fbad03a0436d123f3d4eb73923f735e501eaf33b059a721eb7513f70370ead11b8d2f286a57f7f4304b097419ab560c009d82462de52da8725676e42a3de20ae6b405af9a1a5801fbc813afc487a928869e5892e24f1829e49ab82492307b7c5c9edf959ae2fee1e10d8293ba8822a2dd62fa3a66b56eb14592f4ae4ec840954a628291bc0f34d02f65b13bd5ff9cec78394e2c348d690e6a85030da89c08d06c96a55230c18675b05828e9df2530e4cad139a86f5b9eecf8f0ac386789a7b8115c76f6e746ec320efe1924780ed635e6beda5b8fb37bd72da64e1e5e5bcf565c8fa27d77836259a79bd4de05091fda4753aab91ecdc39a0e2d297e21b9fdfbd645bf2f51f508440ab7d0e699ef06a2526ca5883e3456595ab740ae758d8d6a5167174fe03b6d42fb2a5582716177294efb71e7b4f5b1c7d9ea1f241e02fd099ec2c4153733e60f557c21021fe0af14159397f4e4bb6b89be9455707269dc5386c1775f564254dadad5d74f96da078ece2e9281b99c1eb67e5059de3152c86bbdc07cfbabbcac93b1bb20a24cad543bf20974112fded5c1665e1f104f78579c085f0ccba22d346a9b62073cc8475153ac00c39a4bda38776a6f5e6b789e4a1ccec1991330aa50a05fc8112636d8cc3f7ecca1a5457e8e7b9a2c9788407c0bb49ae04e6ecc95c5e4d134dcd205a0268040abbcd2cdba9452b768a63e28fef1f09fbfe4ec8649fe922c0adb5270f94ae26f6d228ae9e705f20cbbec26c59df20b6f0b043f1073d52a85a4bdfcf27b53bd66bf8b27922e8c7b4c44eef2434629e2be5a983b1654a87987a9bc716cba4a409990a7d3dbf6594cf425a1ceab02b77cdf15a84ab39c0b428c4e10e38ba81b6cf9c12de399dc8bcb8e476ceeed16cdf9d4e883c9c9e75758be1406ddb95d3d14b0592981cd75d5942a128afc8260484925e28c68d6c161baec2c21173667f7ee895f33be9414d7f9c94e0ed0bdbe2cb5d929d3f479a5f2fa7ab21ade01200364b408b005965918cba90630d894ef04a1a46882850ec755cf8f1aae688faaa0c87b67093c211ff5f1dc78b9067eff494e4f6652495f0e7c26cd7e54ea31f74ae8a97bfb497c1982a3f16caac75f36c8a502c87ca982dbafe2140a9ed03cf1783b9bf7aeb81410ef0e0a90a4519e8f310ea43da4cd4af02efe95cede715b9a6e795878c4428569ee77109ae7502c81a8174c8e327e6e18b106f1f3e63bb2e7dfd5986577df20db447ef10e8a8183685337a7b02f3fa78852ad928486f227eb9e1ba0eba95cf2144a055de7b45bb5701ac4f8b234d70b1f3bfd031b847e9affef977fd8d817fe64f357051fe71a9ba0943075d1f7800ff1f9cbe8e02bfd3e75fc7356a5088b567e0f9a330a6fc8676f62f3d4786bdf53a8b204fa35dffe960bdac31c5e902df61ef559d8d286cbe82dc6f34709246c4f1fe75317f6be97877a27bd45b26293e3477091d93aba6283a42902258d3138e1a2a02b626f138442d391d18540690047105e596ac303012c5c2f8a3c1731f0fe948c7ceb8f21b7e1033b36ac5b202bea8a830150fd0925ba509e17831de5573db26cb4ffca4464066073b41ee3e0150b94a90c4954334cb5e181558294961434e9b78f3c0249e3386d6ea4bc6ac4142264220b897dab1d615f6f18950bc5c2f0268a4aa935bf9fc96190f2af3a75bfa08983653d20129f3ea123301c16f4dfbf1619e93e0bdfc70c7302796a3471266ebcc25b08c9eb7e99560ed179a276391e0498677215252363e6e5df1f99d3f2bd011c6c332c010a54a88fdfe32a4dc73dca00c87d96499fe55d9cbbb997146303462f48c8fa803e7674608f36e38fd146a5b3b250ea8973e544181a18d12ad1c540f27c72c48c0357feaaea7f1084bb669109b2d7a3b14e7369f143a3c2863cdcc0afb5ef2c71beca4a50b79552dfc6cf3be2d0d45fcd332cfbe81653e69da6d2f89634644d96a44b31e009f74e02d8978e91f9bb0a7b183a167d607e0476b2d1734334cd3b440b5e2f9875ea3621e6a76c2b926012072377c027fa7dd57428635e0f4c708010a15e1e9980da1f141b180fc896baa034bdd34a005a20355073870a0b3e9e1dd5338aa27025b4460414bf5972b8d99b995e73cc4827588f2e0c884fd38a243fd22ee0f5c56b263ee4acc6818e193a997a19d6dab0fdc84f04929351eafe02943d4c84dd64bc2868c7e19cc980976b4ca6526033853119b92a91ea141931b227b532199d19cb56d9a629d780010826c358fb0a2b974c2fb5ffb85261adfe1580eef178b850a29150019504273078634fdc3bf8117a4867639d84d34d524db60ba419d9bc406306f2a712656bd64458ac349d3890eef07303f273f309be11c27a2c93ac2ee51256585ac63eeb4d9517fab9918dfd2c690d27813249c12ec914586057a8df5cadb40e100259960a4064cfa844a1f2b1f075cef85308a0439ed680688464842bbe71f2561f2cf36edf7c279852205666c9782545055450b0471172bd6795ee1b447f37011f2b60506743e533df3b9cec9aad2e1b81ee89bcb815f1a9ceb955003559610e86fb7915321cfa7bec975d5c48a07a42119dc6042ea73263ade5617dbf3b88f8bbb536f4124ebb98aace766d12f66b5a40bfa6a5590f0b223c8e229404682313731e40c1d09e1b8f3d95ae190680d591077bd9004ccdf0a2b7ae7a9a61e1cce37ceb6bd0b1d6b9f4552822f249107e29df26492fb6938d3c94a4cda6a543eac138e05b89adac66be15748f4315e570e1df80311d574ac628b32ee87f39f31d9a4a7fc92492e38f201dbff072f95f81632129cd08366a47c8e1b20838dfe82a565c8868aed0a6ab3a70749b0f489649d6c6a6e7f494ba6b6686f6fca82a5e0117786da85c741e284a48c2991c57d637cf69b8cd0cbcd3778478cd9b7b07ad5265a38694432482eaf962516516d10aaf088016a9fb8edeb671c09574409d071951c844083cc127601d8069c6ebe8740d8d778fc76ac1b3dcb1f3994627f9d4c0fa20ad223a265d6637ef822e2acfa60fa1b84e5a5bc8c4130d41175c2d8a33083afab62f6bd0c06dd2bb4c6879f6a5d6676196cd1724f83d69e8df7f44ce4e95745f92454140554746b7a5f4bfd812dac211ca2f252b33f83889a5ea8b28b709b83b602b15d40dde01cbdd7da6a44920993e29f91d2826b2b3206785dc3f6f67c9e3d72fdba9a10bc143cc4d0d70abad5a4c3178876e17f8007590f821d68973c1a7e79cc6ded77806a47bf1468a097dbbb4123488bfa4ff066831b8d999a41e9a661500b4588b79bae5e90b97839d2158863c5f308cff6a83c1cfc3326c880fe98dec68fd5ee48eba4c5b5b1f2015fc2f08992f0106ab06ee8580b5d6c7897b55f07185dabcebff9b2365667973b70ab0e4ef2b0009e1defa78decca39be1f0579692e1df7b9f731510c9969d02aac610f178740e7eba302785c8ebaab62573e1c7fc25a360b2fafc0a6b57435809ae30bf5b29119c0a529dc0b280aa259f64095380246dd97b18bcc6dd5c4f19c02ddce3001dd344cf577d48cbd1424ee7dc089a3bdf6b326455e2dc38ba73d669b364b5845c38ba7fded13064bdc45c119a7fded732c8aa84dc315a77d6d7b2c99a4b2862beb42f1cc624824b9a53a87f26bc7df33e5b1160619201860e00cf08a5df4579c279e9ec3c07010f3eb17aa1e30864b0fe1beec384e64db0265821d0ff95577c3e7e0d64b9df228ea765790f171e8143ac8efd3da6702bbb05725d16b0543f8466cc59ed5647316315dc1fc73766ccfef09496080371ec9cbeb75e433fb552f6261edcf731df608713c89bc032df3243f62e0a47fadee91e5379662881582309b6f7780a102bfc4126ef058403c594a4a2e3568b976de7aa80297dbff9de38e404d10b386fd67d8347a37bce3188edee21109b3a84891639c5bc66d984dcdaef028b03837a5990f65f68c58670f058f4ea5771431ced58cac96311b5d5969680feafdcaf924c7084fb5a3b63039cc3385abb62bfec3a91f8fedde73228ee399f6b9295533e0332f10bea5ccf969c38e1fd74744cb9c4151a1f548c2ab2c2b75615dae928c4dc41aecb974c9b6000b669cf4674101790101729520cbbb961439ffec4cb518ad11405ddbbc5e7e5f74d8501fd41d81fc3d8a0b56bedf9494b80e90f92a9ee769320efe09dca0ad21f20c250f8bab1c27a19806cc2a0e30a6c336327a36454e74384294516b21337331123e0283958cd60b915f6e4b5d56cbeca3f165db05e6c098b4965eeb213efc1401f3e8958e946c0616fd48a010bca7713178a7ecee00ad14a874d53901c57c24462a0a20b09c481f319d14f045856f37f5bd2ccc6809a9484b4b03862bf40bcc00d7c3a8ad971de3b9f23cecdb891958297d0105f49bc23dd0e91cefc9c464925aa41a8c195debba4b4c1efa68d184ef28a6f27e3f96a25386d2c2057819bf71a17b1709ab11e5cd3d6789c0c86a03d611e577481efd7f9c777fc77726a8e4866768f5742638b8058434c7be31385a6249e729ce85baec8bf9ee2a2730648db6c84cc53d43b9988da93b23b3857ef12bbddbb27bc3bbd35b71e09a2d9a9081ba0d42b199020725b09ec35b0905ab8a0a982f656c2d65b4a814493ee6c19f34dd38a03ec0021a61fa0ea86c1068868c80cec4889836675d2467b39c41aaa436080596cd8263fb7735cc519cc135795155692f7b67c093aaf045dabc40f02c644f24907024611d3d5a0383f8731fe4dce40fb30c6fc8856b838c508f256cb5706de7afd8bb5267e67d8f2b60f41fa652d13dd356ce9ed7bb7497f474b42b93e7d48071c3d182bce81f45c19f619ad4358288a0d705a3dc122f9247967a817c0d73dd4f7e734a1ba055ac365fd843eac66ab8b168845904d8f417925844dc0cb416965828df841671bc66be8a44f8ddeec0c8cf35df36b5a7417460c0c11f7fa700333261dab2bdaa23a69ec3aa823a8122d82107a9b4a3c801895997f079b051e098cabaea0b792e3cd9df5187ea2e1f8355b17d3fa872ebf83a919e4a5fefdd3d4dc65eb46c3a5cfd6327e45a07eef17a1c395ca0e91a76c52545209bd27c160db8d304d38f7434cffaae8406057421250b4fe6cf67b0263ab548d57d1ac75411f6d06f4c36ca62e3c194e64cb8caba3e43b0403403fb2eed25a8bf98f2cd2adc55e433f1cabae5f28393f9b48926dbe92c62b838df6cf268a4460c90478c3ba5bad35192042edc056a4a53c4acb0b3206ee36f50d7ec7029734ce87806342a622c534e5329a0b7eb1f4d703e2a8df9f8f1a22612ab4f924be7fe4388404a083e2429feba6752b025d490f5c567888c520ab9a15847f260d878b5d0521492da5b583e845ac5e7e73b3281040f7b5a26ba38a33516344fb482acb93436c2414758cb148f026f24477c72720bc33682a456f0247da050a5734829e96ab83fdadb085b151fd82636fc11478b1cc1adf47b06eebb72b21d4ca44438ff35bec63d04bb8e7f78efb2a97abd6c50ad410fdae7d55ba23e3dc2f052bce11d5adbd21e2e216e2035f7e91f55a205dc71775f21239742f331593b3e8a94ee2bf6106e8693c704d7b63719819d4f621a6c7143767fcfbfef8466fdd1764fdc33b30f1bed29d281188603db9960a8693bcca8ca36772d8717a0e79c461f78837fe9d8f7348db3a784cbb915eb56ef0e4c8db10202ce49ff8ceb62e5628e1a645d86d0f14a85e4090aacf01c34eb41809b8147193021a182789bb272fab02d28a1e79d75b29df7f3e4d69a03d49b0eeffcc92544701b3375b6187eacc2b5dbfbb06e76e38693bf162b82ba7ca1d722d57648b73dab36c176fa4f176eaf5a4dd7ecea5fe30c08b189815c6e94669999d708556233033756d8f1867d579a12e347cb8fef58c9c514432776823415d31422555738a0e8341e141c99d600d7287f46e8dd6062809d2e6c7b8774843b92015cf8acd29084abd686c249ea5459d09c0ed238c04e58ecc4b6c7b7085208f45fb26f5b1ecaace9c34328defec703c4808808e003d6c0865857f1a1f3481aa3fe3ee2dcb20523bac3ebcf4d0de6a9caebafe506b7f299b1305ac0e9f96998d814c4d1d1d2b52ae8d5d848a922da1bb97bb8812954da07f076f91e031013d5ab634e8ff228f5a992d89d9534711ae3ce24b9dc734ec7d48633e80f3a85cf8bcb9629af263833f6a8ae05b72af015ad063f63157818d8dc2b48692987452f7037f5319fe86c5dc1b26e42a334fe56d286adc89059e2d6b9be48ec9d6dc7288d396bd267f7770abb5be0ff734184ac5945b39a737c2f2bdb935839380224c15d19cd358737b30dd6aed65f6fd8f5c4785993caa0055f7d4ce3a51d9df477637bd67969053298fb9e2365f562af11066043d4470369463909ad6b290fb1a725bc9579067ddfcd188cb86dabc9d86882e0dd3e5b8e112220730aeca3da047a1cb0ca5fe1ab6e3dea0a682713ea374e8f036eb6e61ef00f1ab0d457bc25daff57cfe1616c3ff39df08ab784574a044d9a92bb4e75b7eca9b316a8164a140cc296cffefa455541c3199970865cd34950a18d348d6ca6575625b9828a91026b77ed5b762c5cc76cc2bee91d8e0e96528649258370e9eae60ff9e81509c61a63193099bcf717025b40a64781e69ac384dd00d53db06dcf1937d1d21b610d545f6899bbe635497151aa58a2b4409d26b08746eb7107defbcc1816ecacfe67672f4cc715414f1b3b92347e1bc8a81211f10a87bccaecf88c15edee4c96f1430170459d0d40727becbabadf19b50971673092753ae6bef0c7398b0beba046d4650f79a8e185d0e891df4292c7923b21b7b129dcdf2d3deab3d347c41b72b6a6734bd5cd65a759c4c7220bfcd56da0da0c19eaea17ce3ca8b2be300ff2304d6ff3491bd968967a03ac6ee3a00b7b323ddc845a51758fb2fc5d4423929c692974a697292449473a2bb6cf54e54e7b06b08c93cfef9bd5456df53213551fdb27bcbcecdf39010ced95eb9b94c8f9931823cc52ad318169fabc421a5eea33f54d319205d37e7782e54d4cd87affdc1495e69a6972c0ad5ea496e4f544f3f4ba9b1a928594230b3a7261990953f56d3004ede33563baeef9c737a58cc5e002a179ac7f4667dbf06d46e638850b1ebbb785cd260ec7b8e5f5abf0d30c2114eb80ac260b8a1bcfcf9c17a7b0990067ba3b867b2e6671bbc74a0b0d26fde09fe36596024247406e9af2f0f91d07f423bf5cd424306c7f3804f6cfa2620bec1efac49de38c93ab8d6fc54d02447e3bc45e5cd9ad5a116271272c10337e7dae9e4fb2bb5a6b99fd87adf3f7f437d4e20dc0094a80d98e30705247df6f41ca6349054219a9f5b53d025175956c36c5c15b4746a5b417ff2c11ab49015fcde18676074b7f3e262a1fdf6e43aa10f917afc9e0dccb1bf7e707d42e95e2af6bdd6a07de57c1eba5aec36e0440c553d1ef03905901c0208553c5b7a3ab666fed918559b82449852ae2bcbc856abbb94d3076cb59bacce2a075d903135f6c863c5d0df1c08d92de5e45a6d797b4afbc0480def944c7cf49d2098e276cb1bb84d29cea20d3f2fd766c1f5609cf6d0639b1c91268cfa6804e76e32d0af3ed3a71de2b0bcee4282d64eac5c8369fec71d8f8d1e183ae0e5ccefdb7f80843ce85284f8934d26aaefabb2f5e87f7bd23b0465e65c390b3966c261abdf9af332d8d5b050ff0cad66b572e2cfcde4e90572d8a4abeaf70b3ba30221d53a29c044f981fcf06fef7219229e2430067a19812a8ed3801541aa4d20d23dff5d192216f15fe3279571b568abba97681cae8cf248e54c1b205bcd8c6beab7b99e3ca5cf5854c1a2089e5f015f9fee771d796f91cf4244997f6e9100f4d2230e3b5d9d76ecbef6e0ecb8ade5bfa0583fe143515905881fff3a3dd6063e200259347404bb6421affafa6e71248b2014e29cbdbc65365a17370a49f8f74727be19922277d085c3a3b4f007433abcb38770b87ff77c0f220e9b10d57c11d65d9d47d0418e6adacc05764d181705ccc810120f84fa4973f53c598eb7e1e7e2bda9be0b69d4d6b6ad3d67166da0c6554d3493b80b6f9af9fac4ec833331aad6e0b9ca0f4736fa73aaab133865a028047ed950aec2d413848bd55a9e3b43e165119ad29101200d014924bdca1a915c2ef7c31301780a9192c2a1139c1027d261b5abbabfad54c22823a18bc5a24ab577b3583189226671d6157dc29d25f91b6f04ddc8fe93a3853a9721da17f98a578f4b650898c47b451cb6a91d1504c5d7502db3e1180aa4ce9a8541a5d36a949a3dbd76ef180d9eadf756b81496b9cc69502d5de0c1c397557317f07d1829938d5f5aae9a0feba2471f1e0b41250e409fb5cd15c53cc1e7968d18f75618be3ff2de8c86824d36220e9f259ce0191db8cd76804885147dc06a7da99a78a756fd04268f7f2e5653f8a58dd1122fd4c314d2befedca15a1e5af8f46cf0f5f728a1b0a5b6c187e1d74edbbb0d8fd28b09b1aae5c5c2d101ca435ca39c392f8e9e65521ba37215945b9700459a55d55c574d19d71dfaabb59073697ed77dc3eff32dcb5df2eac5c06aa2b67151d8501da482cd1b0e1e465e8a72d453468ab4d7ea88580fb8763afb805a9801a680eb8939527c0992b4945a151d7c4b8618ddeb290a1cee3cd81210a688f1c46d1ccbe74fa8381138fae65331c4706ae49527f95e8a98b90b29349cffbc47b84e6c65e7ec1b28ab186b49331f6bdb7a1e404148977f07a5791fda942f3e43f0abc42059bb0e58cbb44c92540f9b796f4c72d6870a7701d5572da1cb290614d520e85b4a06a55c7577359183693cf9074e0fe9249c6dbfba39ee517774f8be997f1375b238e0367342b595d07433a9278f10e1a780f4e9e214fd0e07603e4dbe147f1c841dfa4d81d5427f6d3c16d5e8fb19fa38d6eb6bb8819886a8b752a7b68d3c951a16b6193ef55411dc369bc10b31c1371f43fadcfd315c0fb7f0c96d18a3b5a0b93154954a1a36de31d34137bbc48dfbf6e9c565c6eca88a0c274ebb6707ffc6963b885ef6a2d7702088a440178c4b5541f92bf5dda5d3acba75a0140fd3a765d3172f159985b57bb09bc1687e03ac02653cf152fc30ca18248d32c36e8bc0bcad0c069178df309bf415f016812d70596e30a34287d1f134d12cd64b5248465443ec1e2e36067a1221eaf5b8f63cb252cfc8bc27082c9d37c98e464590b24d8bb41e1f329ce7ad10efe2cb035ea4ba77991453f9b7b49493f67df5359201e5c54f5904b03aa334f60a9c1856c9e62cb72b1f1b91d93f35c8a3cb1fa2782446047c7e626068c4146e0ac02ddcd8adad43c95a138ad184851d10aa419b712ef67ccee478a6ac320c98a94a72351b89e65b1f8d9906c678205445b5eceabfa347e04e749ced433b413e59dfcfcbd59133cc3d7794a1881224106ea5a33c580800177a3c3df66835720bd098b95aacd64d284647be02079049cdbb7d387d6898acf6104c185ee10231297aeba00eaffa52ab2d6a59160c9f9be076e35a6ca29f8dc65969e0bad5b423c2fd73c06a49b73519cf5222e3f673e9075608b2639a2de9750dfc6693b17f6a59515d45b5bc4ee40de38843d99dfc0f32109e042fe0e24fd630c472c6632fe332761a1a4bc0de6deca9dd8cedf4c0caf69d958886b16a725d3122492d99303b1e986c078660e66aacf4b2ec202d4aaa22c98af9babf64c728ae1080f676d2e5e4a2f2d173db7145cac31381da05f17f4a4c4a9a1242664477c0514f24cd24c02f06ada9e1c3536960899f0a192a627de1eacb9759a1116ad6ff572c2814e33fbee04c020a2784715e6db9d70a3534f2a369fcff610762392e787c3feb6c423b042be20c65c7178ff284285745927b79ccb654aeb12e55e6bf57fbde4b0c25656ec7850a482bac79b2ec1fcaa39edcda25343d216073a761ccf5f3c70813627ab3eb43d918d50bca626118bd1be56957c2be9a6cb813f29c38a3cd92d9866e500b9c551b01caaf6c207336dd274934344e0ab255693ffbb84bf663f91a99ef93d18dc3ed1726e998009c69cbd6c7eb0b57ef3a196c778eb27618d6eda44b18d1a232f0645ce594931c99573aa1c89773228299f4f24f18d38dc81d52c411ec747de7d3e5cbf66e90e8d5786386e0db0cc8154561488475532e3cd488faa892bdf34d42557727924d6f518054906d7d70354a374876389ca3c8126644aff1892f7eb7d09188ed26de5b7b46d3c9f12b583ed1372ab797198aa8eefa1867f65115f40433af8c7468e52d8409cc0060a957501964b402109f5fcf3136d53499f135f051a7f0ec680da0762680bd0658a7b43fd30e61f881e04e3578041688e010a7cd7400c0c95939146b8aa81f8f96f3881b0896ec4bb8e148ab6de245df1e37af156de9c5aacd85a6b51490cc4886f90184b03f304ca323acdb7b054f81707b04c92aa05749b79813a5f1530388cfdac5794227a0dcbeb602492d9032f734143551ab04b2a9155928babb891084de5121b144818b350a2a36d11088fd7d7c0cfaf497b2dfb9c24d70c5caa1145c8ded7c4d40598249c1b3a8acc78212eb47f7d9cf7f869ed030921e7452674313f6a56603286d102c0fea8cd9cf77541a8173599ad0e9172e9521942aec678af80c8e9791d046535960a947a1e484162be717ff021f2cc1d14439651a24ad2f24f73b885436508a007e0d568e14cde40703fe595f90c47f334e148d82b80348d44f6975cd415506b4ec011a78b14d5c40ce514a1b69e56acfbeb65ed934fba59b440113890842f49440304c237a3d1c634772ac96a537a3471bdbf406284309951dcf1c53fb8ed877803d064adadb3b409de266002cb3295822112634528b7008e8db6e4d526865777777ef5705fa044704978990dbc4876db3c316e5b861beeb44c98c0a46898b1ae74d929a21331685321726a6a2d162ab4cb164967459ba2f8440f161a3ecc092e3cef0bd5e336a98978b4bc39b2b44cd153356479915c418d1627b9872a52cb964962c1042ac0c3e2e8e1d974a8e223ecba669864da1c9c575bdb932a8a945989152e6ba10636568a9c1f194aa03c74b7860b22c10b14c8034c97073b8ae0d1cf64dab7b3f2fc40c35fc76f8c2e145f77ee68f4c32d5e7d3397f7ea8ec605302a698a46860a6218a2e12132c92221815490a4414c90b44130910222447903829ca3a7c21e9921d8931632f19c9d04b2cc4c8ae0e7e7bf03b832fb529b24333ea778302c1e373a61892a90951c784c504adc3d70d4ffa044d2dce9662f46d47ce6869ce49eb104d86bbadb5f662a78c49b673ce39d34b524b30be23b7d486a58772581fdfabd56ac57992e5b1939922c7d4a46fd267473bb1b3b373c105da09da317d61c2a2b3ac692f5127bc8e9821c2428fb0796c2174436b0d416cece03b9c57203e2a10a5126a16ec46d71f687ebcb845dc31dc46ed61a85f249e75f8ba814d5775f8baa187eefd0fd209387c1dd9d22f15a6eee1211dbe8ef0781d69e18851861d0af491a8236632a0b50e5f4c524c4368136c510a2f0079213982c4894b5f38378d939e9eba85d29eda65db2a178cb74d48191ad8c4f0f4f110c9d02541d674d1af1d6b7894f0e221830454720c42e09e4f4f97db8bc851ec91401efe305d3e4fa2238f07f66baf752c38e0f180f6aad75ef5b0a76f50fa400a640e05ec3e9001d20732206a3dd91dd1d313e9cfccc1be49a0e8a9d85a7827642bc47262d150ea58829ad0a9138fd3fbe9b027f6c75282f00ac2244e5c86c79f93888692045cee9a7f05897a7a058981012d017491d0498b2f71e2a4a901a64b9638452e402969be0d5a2df86600f94898894e5cc8e13b402b09cb97c3d774011106337c0838fa2010d492402b3211f219c0b6a8c4d1b1c3c702d8038e6f44eb4c8b143e1ebe6f417d5ccb480be29223d08d960d9660c05185060a683e2c546cb87a5a1668edd8014a479714e0380111b383b7e248f996be0bb45edfd0c76a4df930d08a4264aee5a98822578ec068ba12a48696abb6c07c0068f9b026478c6205881ebe305fd4475b302f6aee8b196374a0c10508a3272b4a64b850c3f7e4d3a055e3bb52e5f369692d182512a1e4e3f24da0f5e48b7d73a755c407a4d33a01062f5022d2d307ad347d5beb06269f980f56e18965720b42c53e1746aea886cd9780561025710534f00748442b4e552844d40106c1e633e173f215a005e653402bcc9b4f95410bce07a3a018d8e6c670f3e32bd1e2624374fa20992803ea30e5b3c013dc5224c298fa220a3dbe29374ed8c1e3fb7e7624c1e1ab40005a43c6c42f54a0191648f8983edcbaf111a0a5e51bc0103f7c6bbe9c56035ab08c8f0fc9d7420480f8742b886fcbd764b7a20a4a4d5ef8e098183e1a86804b8af81cd022b2c317b3c02cab16ec12afd068e3838db225c3b780160fb304473e24a5d9f14ae251a5c80c574cf3024a7d8922f851a40c9a3466a468500172a6e58a2550e095b5209637369ca4be335f4453f441a9f7defb3bf7de7be9941b27e47ca5f4fb5c02c1179016379474ad07b4200c4ec5626b96ef67ab577ae69c73ce08b9f4e0e26393f9e9a957762080830a0f3b12c617b404caa2731d6e0260e1dce1c79757fb40eb8045a77dca37212508f2e757ed830f74fa93f3bace7521fab2be7cab9d90f346a552a964c021d507e09b8c31caae87c709680370482db5db08ef78dd0037e7e4d2678305f81c71cf47023e90ee67a33b88bd002c4d000ee90046441efee24784f11c4019810a680c04c0e1434be5d6219490f3b60853facc6ddb38eeb649a208af0131fb8c144bc4cfb252bd527eccdede0b42fd95ecabbf9c9793ba4d01844530fd3e8414d8d76908c23278b3fb5ef63aded5cd4c9002187473de934e35f7b81d8f169d66908c5e60908cf17e46ba52e6a4a49e710dc55dfdea66aca9a4aa3b2ad979f3c9fd0e4012f7ee4ca5fbf46fa7a3dada892c3f759d73ce5caddaaacb3d3252d58fb9ec73386fb3b859e585419283600512a8beec22c4a619349ba67c0283e693f89c93ef45a92e3b4d5b69efed9e7b601087baa702c2202cfbd8c998b34cc5d6b44ebb5deea171d9dba7d93483f89c4f6050002009fa34c2ba749a41d16992a02fefea55efc5a19ead3ed31eaf3a2fde00c02008b50ea83a2fd28f4ef777ee114bc4dc23cbb29cd4f3bdf7e61e51033b5971ebe14bdf7df5928b6b8b54df62644bd116285df7ec65d17cfdda431c638cf531278463ac3216d9af992cea34621c238e594f9f8fe17ec9ed87197d4c1f4b222c75649cd0fd113a554999bd248a25e467d228fba8ea843296ac114be08f19cd79cacae93c298b68975d2a3fb8347a30837e74a6f4299544116359d4bd15bf394d43b17ada6d69599ccaf85c671e4cdc792a362a5727ac6cea3abb5996a5d01d4ba22e7f47aebe8ab36f624a59c327da8f1f71ece0f4f805ddcf24317fbeac91ed747c934fb72e443ffbfbded62f0d21d3bad6f1dc9f19e4c12431bbbdf56cbf7eaca1c85e6877397b0f76e7a9d83aedb2462c91755eee648da8417d2b8b7a9435603960e01a1a5690c8c482c878c243a7cf8f3a05a2c30696231a638c31c61863a46f4232799cefc06d44dcd76a135f4e66e8133f421f28fbe4668792b5012b4ca40fec3b1dbeb0bce83ad34e64a0c4e7670d45a69a9d551383cb87401b4739379e1d840cf8a2358658d64927e7cd8f31c608bdd8d92775d239e99c4e66fa9c73ce39a70de2b483edc269872fa7581503a6afe1573a4f872fa7214e3d7068d8f39344fcdb867b3ffd470253fe0612884e6ffe6cf0726af5d9e1cbc946f77e627082d1bd1f3070aa97355ecc4c89593760943855229e6449420291129254d8617358e1b2e0ca8d7ae6ca50858a63691061e34c91282b864b967b14a5d5042b728700a97060d8a2e3167d56ca8a0c3b6585846ba54d8d2090ec706b1803c3161ba6985142e5062b4a96870c6b58b85028581caa1059a22a2e7c1451a7d2dc2b5296894c6dd3c5a8ca3581090e4bd6480d178619507059352adc18546a6ca162829d317597a801e2831d93c37de1cbad726506273d90d8d610db828fcbe3851f38ce4c29ba4a534eb0435a906488da821f6c0c65ae92305706161750ec100e503678f9f1e2719d72d8afa52425c68d22a585106c6a1a205ee830c413102dd6871418afabc5482c88bd11837581054b4617499419294429c13eb9b937a079c2430ca84b840b0d54ca2cf971c47ea1c1b200a98e692196c20950685436505edc296fee1721ec911e96c0ae0b5e88b0628534c1c2f48390a81e55ca05eb3ab2e1898d9be4098c1b048e85b2a60633b54cac05989be4d4e4092c0918221647920a3b8856e0e2e4c615e364c88a8963b51071bf33954c940d23c6be90658828f748533553a40601e432c17003cf3a9ef0fc59a32633ac0e4d5cdc2c6fea17354b6676286382189b468b7532e5de58b2c2920f422e0d1fd5cd8e38392c91cfe66032e30d131735ea4dcd41cd8561064d19eb8318bb434b0b5396dc1296ea9310383e9c769c90c3fef0591296ccb82d96b8a83bbcb141a8b92c98b93bca14116391b4344da93c2cb12d580afc592cfe946bbfa2d9df57697b4b1fddb3ceb39f9f473ebe9dad6e5c95d7e73bc639ff0de39c6f18e77c4749a29744de1c9244bfadb4adadf888aefdeaf14a7b55277736de776cc7766cc756f18a01d3d413d0e10b8c51d7ab6c3921ef3efd117aac5a57ebaafe4abbd8a33df6fc26a486cae67e6db5d2562bed4d4c2dfb159c907ed8a3b6ea346d6bbfb35527c2ec2b6f842e1443d0afbd09b9daaf75bbd3b8b6d2b44ea5f24ec0c2e37d0665f2e76b0e775e2fba364c296152ca28b794dc36c9925376acadd2f6de5b4a2959acbf40b23695d4f68e6d3ce330a79df1f59cf9cebff7de873d725a07f4c678f3083dc2f8f9be8979e7534ea83eecf1de4befd31941a0f9b3f572d41fd0e1ab4b941758f6e2e40529e7fcf9578f41c62083219ef6544321743b2f665bbbdaef7befd5b4577150fa689db759af62751eef563a2a6d5f9d751e8b8d63364afdeea80ddb513b6a85b9aac50c8a479fbbd8e24afef2dedc795c074a12b9f3767ac624f20ea7c36d2ab6cebbee72abdc799248b56d9268db766cc7766cc794b8bc5134ecf1a6ab4b38df462a37a65848841574fc7376ac61dd5b213dda57482fcee712d521d0e18b8b52972d5d92ba9e1afe2e3a3a023a7c7159d3b5dc76efdccf865efb3c24f1abba216fefbd72e5bcc4f839e7edce1fca20d66f0e9360759e4a753b9dbdd6a9d8fd6a141e0b217de967f9e3c49d50fcfb56f73abbe9e2f292a08a4c445daf464820edaf8410c2232109372cdaf289a6a56f3dcb792ab66ea1eab59652caa30db5942be45ed915743fef3cd979184bfcfb3dbaa9ed78f6e7cf28b5526a1a0ad5d36eab5674a53f5b451939e7c8d435de9b831d63bc6517655c1d6540bc37cd97f513b3ba9f2464c4121abeefed7ea3f6dadcaff1be5bcf1c89ba0d22939a91de51b5850e09345d12e8f2309ad288de30cd09693867adb5ce764c6a99e91dd5a356bb5af9a6556c1b8ddeb02da5a3ee5bbda3e2c7ce8b51b362bcd249568e77ac6a6dc376d48eeaf7e7cce4e5546a92a05fdf7a3a1ecd2495a23954aa53d5a6529a51d75a6bbd631b36b17c16c7b949a34b67493487b2a6f14d6fdb8ef190445a4a29a5de22afaff9b66351839cafd174729a41d8494ba27ef57b2ab6ab336da9a66d3bb663f7de1dd5e38ed5160000b3779186291545230994fd6ae84e9973ce4258e72ce5cdfb76aace931ad6ef3cfa3c55e563fd4386fa2996889fe9a82b354c43c9ced3b06efffed4bdac2c598f3b0d25f39cfa29670dd3b09eedaf3be33f8f661075cd7a5f27675f9df9d43573a8abcfd7501a2adb455d4b2ca5ccaf9f34cd65f7591ce7269545790acb1753386ba8782481e6d4d454ee34541c92457b1e79f3284ff5f8daabac56a7baa7024ef5fc2ab935542c113514d5b07e73ce5943450d2c55da2173637e829293260856a8400265cf730832377ffee5ac53f41261936b9da5949c90065752decff9ca55279457df2396907f71cc71af76e7c97e6bad55aaa8f5d62a3ba1fcdad7ccd6bfb6fecd4656f26326a6e4702c91659ff9ab86227bdcf1a48859769d02cbae5f87a0b3da698f5a8ad8a245c74d3d4b811f771406f7f0be1c84190f0d257ec2efc3fe3d3ed25a6933ff7b317e3db51338c79efb82dcfafc4d3b4e8419e7679290ef83ffcca07f1c84cbaf1dec389cb3098eae2fed7b527642d262250e218467b66ddbb6a6268d2b842bfa7c9c84cb1e3756b1bbec324a1f28a412485ea8a10e5f63da740574f81a33a5e3fdd4bfc3171453d773d78753ab1fdc0bc28d1327ce54975bc289f761d55258bce989979c0c5937c2e6e0077eab7580535889dfe2d87952c82e1f0523f058c01e82b55fbff6da41977602cf6d6246a8e956b131775f73573fa4da09ad3b1efdd9531efd90476f5d9fdb0f95d4591dbea08eba9e1b27e13e12a85b1fe12da0cb13f9a38a7268d3a376225aad038c84536d37d450e80e73deee18630ef0c6633f7b1ac2ee78ecd78e1f7ec67994c7761fd48e3b082db431708867783d45bd9e64c849392923e51e4a34dfc9a2e630ec396317380edaf2334e7ed434dc91c3f425fecaf70b7cfe7dfa3d9bcbb60bdcfb4ebf4702e5a7ef2381f0533146c4286d400373536d03b0d7c7f5df462855754aa9ba1fe6871e26aad3dff4379a8112c76f515c19a56ac67e1f7674ffc0f49cf3e78ab288de8a6eedf5846aa762773acfe82d6f0b0ee8ecf0294f7e2198dfee15175f8e45b71ce5f0eeb4e326170d95a9de8454bdd62a0e05ecaafd50e300a5afea549da75f7e55a954aab7aab7af82cfa29b9051e3368b9e39155bc7dd8e4dc8b1d051bac94918e3d873c7e301ecf951c09e3b9eec85ba0c94384f8afcf473473fab9cb76fc6d987da65d12fa762776b6374033b8d54d26e47d51d5525d1e7bf99c3dcedbc5d3b6fa7d34e6fd88e4da63e87489ff93807973b96c3e5e281d424c53524816097c1101e87a00a344cc3a4cfbd1ae6a274ba7aa57148eba8c14bced34f9dbea59342450da8d151af2f8ba44fedfa5c416762d658ed0a5cbe1787e21091042ac2d4656fc08c8e74f4d86dfc35318142c569cb170a5568e24dbc89a3d0c4040a15a72d5f24909e5fe697f99484a48494a48463671bf13d3e4a5fae3b0a07c70ff9ce36e27b7c50e071671bf13d3e3bb6a5b81312dff9a42626bc69c764136f9a7fbf297badb3a66d4444caf0b663302243b1a8c72ec66e87f3d67e1dffc66e733885a83449ccc7986f1bec465c287fd551290e8946229304b24c221409a4a7916964325d1d3f6a1fd09e230fe9e3923e481149027911b7728df68f387ed8bdcd65c4e388eff15940fcddbdd83d0d1535883aaadbd74225baa2ab33e9f3476071f539803e3f2a4920f8d282d4e7c7214ab9a32154a5a92495665074cd1af36759ee94228fae19343b0fcfb79c869a4dfb864b67a3d1d1d952554747e7374c474767c3747474ae0effd58a6bd4d6fc392cdee5e88ce8fc731ee7b0e8b6633bb66314b3f0a2c71b1dbebca0d1af75395c7bceb5e73787131aa16bd16a57cbaf5dcc2308f8335bb7f61acfc9e13939fc4d4caefd8a135271564ece73bee2bfd27272de8464a954cf73567ca5d9f738bf39fc572baed1ca591eee2c7e73540193aea8848b9a6f6ac8680600021263170000280c0a87c4b2284962100ffc0114800c6eaa3e5c42925065712005610c04410c82300c83208001002180106390219e7b12285989061505bbf42a524ffccab071422f8041f131fb41f4ea57c88942580a91d21206dfd5957c35a087cace3608d4af53f631e923629cf242bd6b238b34e904b0ce5b7f01a4f816031b351aac327b018fb6e08de38248ec3c0406579836e40df680f21543d19647dca8c1709e25d2d2e42223b002236802cc048eda398371100d96029b142d45552b44d513346ca8e30419721618688100e2d54cb00e3b6e9282cb298871f17224332117c69fe52952fe1b85d9ae43eec032b537537551b0903e41287062332d29d0ba2baa6a3f39c895732053a45a408ca04cc99e106cb320380fbfd17a8a7e306cc4011c7c5f4b210706b847d4b28608e61da6c0932a7ba02383eee82cb8e40bd71dfbc57ece02b6a45a909dae7f4883497ee5eb436ba06fb5b546009cb726e1bc5c89fe6c80ff25cbf94fa41454aac0bafa08a913962517afb442b59dd5ed0a99e8017a90a43f83e9afc886b1d7f8079f3dc592d22052a7ddf6c40140b0d42ba52b1c9e048e8362aa53c99aa07f45516c06e0cf9f2690b77dfd36d63d03e3cca2a721bec8c19d0228ec2e10829aac8b1405831afa231d419579ef4ac00bfdade159f3d0efd14ceba8280e363e965f39efcd065099f47954557afb14a5faae48d512b934522c405f60f06b4b096254871cbb5cac52baba6fd501e45310654c026fb9cd18109988ced438ad539f0b139629e627598d09441e38c8402e25cd909a437ff7bed280415d27d9089243b2b78c7ba06b7c7efe132664d2e6c06e8f985a165f394c045ac2ae8f0eecae05e13e2cdb3e988e7323852480c5fed4e68995bd69addd322b905fd46cc52afea52bcff65077c51bb8b7689cbd01447aaa1acfb753eb39a73dfee37e857684450a649bc244ccd84972ad63c19a925126af4d9415062ef4b201a9a97dd2e82f60eb319f71ee148742c8de8804d711ab217dc0ae1e09b79a24e16700be7e6457fb8ea0b59b5cdcba1ca07fcaae7c3580b324954192cbf63918b35a1be051838fc7a5cf816d2d3919383f194f3a70b437ffc2b827dc5ed0f73fce51379a3478b9be8cec21b7db064eec248d1d3c6ebec5b3454c7e8a638f52192201e70624712214a8517ab964c97c2925b8d20d8bb29a1af5e9b72a2398de0511876c6d453dad12a739f4878845cb17298466153c89b67e19e1f0031141a0c5a614fc19a72e4982b104f9dd0ca88750acec475a4ed8ae5f173d9852e2e6cc14114f76667149c0c1922f9b89b6f1690cbc52026c51fab2e231791abfe1364a9d1703db4d3d54ee868a487911be2de2b639eae8d9d2509dc853244d8a36d8e991292349b101136531b5a890c115872e508b048e2c652afbdabd055e2bd9e38ffeb16f0faada31a32bb2461f7623713fd2d18e0890920fe4b86636bce3053b147047be03bfb3529d72c5e5802e0204454be567f52fcfd73f4b9d52d191af71f7e44b740c1da1147d5298ffb3fa9a4652106f7fc200a56f16315ec2e512fb65b0ca402976666e1a09e065c07957871234dc00a8b73131cb9ea2c6ea911be9da639322c56a1fef3dd319152a81513ba9d7db4a096c4c9f1b9582ee01ca68de7f274c49598b2592fed116b8f4b26f0403734cd12cb4c5de65e8f1ba23ccc247b2058693a204c72ed78c5ccb124d2edb8493b71576b3bc9b2e165ce5449996e816f38be4a2ce4cb41969253a7850498d55d2251106d16cf359e3489abbb41597c40144ebbd46d2377c070c7135ebe492ffb985c4162763c21dea30a5ab08c8158ec2a03acee282df7511fa57ab1b9d9ef4942eeddbd80d92ef0ed341ec4f4cd0e73801824ac66a78e8e4454a8f7a7bdb7f10edf0589c70342278a66e72a7824058147320c3347881e748d8f6752101d7cbfc429a17f18be2bcd9f6a68d63bd8e323d12846ce5975e32e1334ae166e5c469c20769f21fd061015a5c6a3eb40387fef6d6632248b3daa0572d09a8479f3900a037e93a976d5a594096cc59ba950c3742f9a3dbdc2f69a65a0920bf3701580df063a4b8f21084e1f33fd4ac43110a498884761d376b4e5c63046106a9acdb857c31dd66d8923f189cc33bdbd3004074818d10e7980f6056e9a8895fe77bcfb8280a49bdb9070ea3d9a3af6f5f9ec5371f8f06fccce7f871141478448b44a8e36cb3d6db05181dd8a2794891e1029c7029ad671fb96cd3d50e690a0a4a88dacc32fa3b62825e302f65cc46387877117d1c1e0e79983c5153b82ac19c7571d527ad327204b76ee2f86e769e4d505f58a390d26ec42ba120b25001084303ea5aed2ffff6ca1981fc87bb847a8d005c42bac986e49cd9b637c73097021b1013f16566162967fd72fd9e57ff1150229e2a43f64aac838ac38cc276a86ab93e876324de5543403f7d0a8f7c8f75d20206c00fab05063d9d7d39135b3e6b3e9056543e58a08ef323f4a3fe72fee3b31b4e3e2bc567eb5f2cb0b2b6183a1d85f632008dccc3246c61919826e10675b4341898dfc49f4ba208786ef151d01f82abd2777e70631310b439bc71bd88e08b562451f6c13ad40c0c30c818959500701185f5487c856bc90a1a18cbf306f67f60de17f186b07dbbb02ec6bccedf91219e0756470ac1f42be1ffc999362cfafb21ed2c2968809468d316c612baf839c2f7c3f96f305a2e92ceb9e17d75be738f641608099517e481214e1b78e6977743c8974b94efd6c2bbd1cfc333b891694a0de3006aae9c5aa2b72561b269f33c0513a4ccd3bfd188cf69ba98caf9e8af7ab279a76d8ada680a440eb0cfecef94e105918882f59205d1c8c805825ad4a9c30e13bcb5cccad949ebbb605232e39140cded5843c395edd6e1097019807e8079fc79f5c34f247719512da4a5b3571b58d6f18bd5c351214db35de8ea3b67df0fde39b2f4d231fb912cf13ec7337544453f8b591dffa5db801340cc3948f456f740fcb19ecda00a412502a18ed7cd7fe7b866d2a25514f9be2482ef115d2e21ce96b640f7e9a5aa3d3affc44a1418858f65431158c1ea9568b6385a4e220248a4908f0b3645d5e3be1eff1cca793bdc2c9750093dc66bbdb94215f9635b507bc40b7b2819e7e980d9ce23ea910ead898bc62caff1b1826bbfe2ce9c05ef3af21e170fbdc531a4ae23715b071156f55832c1573de93b8530763c5d6efca80b8605a84c2c646eee8896322edf47f634035016c2e2657c11cc623112f60cbca60433ee5b4fe73b69dd64c5b8ddc2d865b194d82b2e4b6699c488caf6592c89228e0254a97d5235da22c752f6f5ec2f82c1cfff3a61ec180611ee5eb6bbc848c185e43ee1d0e5f93adb9e8d22793a353e13396236f9b55d7a822aa4f35cc4fe3c8e60a61220a9bc42ba7335503661e28fba1cdcc5cea3410881196f7705b84d8f50fd09efa5024ba82bb415be9bb3227c09ce1962f3501fd15b9b36a244154443b59c4f1c27fea4cc7e089a5f126476d5610b78316be5c8dceff1db8a25c9f33e2608181d0afd407b0d987211a4c8e28da6cfcd5221b035d6d71f88bebb36891dd84885bc75124140356cac656dcd8caa024b84ff8165b8f9c6a2c3818121612cc690631739fee5c18e1b05611c2bac91ee1c9b7f5d3e6332169fd6e3f2c84589b08071db8944460142667b47fe71ba0df5dd3d7687d4af463978018e47c083f352981c2a86dfc3899f982e1f23180c343487a21296c82b06d19ae2fa4794a51b7a8f5dd33f042970f955d609e6135fd93568c2f229850b97446721a79b9d3e6be7bca61d3e12ca5f68af2e4ce716438f61d1d5ef55e02f030485e598ddc21d2847a11159c042c5783e8c0be10f9b0707f5044e7197c0140e167f2b2bb8939c6da83b570aed6c3f3a6e765b60a645f251dc9c4f6195339fb9e3999ec0e384cf6391e53883e5cc08d34ae58db4a0982ced47f77112119ceca8bd38e65042d17133ecff691cec16dcadfc16f9a0fde0dd015060f3b1fcf5273701ff70b1b2487a496dbfd8d0f245cfc909181bea33aa8de057d789db5f2e5b807aac8c39de7fa5d679bb1d628699cf460801b8b6669941812a2d40932c863166625e0b2193c5a0058786e8d4889a6998b03ec721bf088f6fbf775b8bebbe8b4c3cb7615e176f14c1c415e5c33a5b60db46937f9d18f6ec146c054cf801d19a4081a93fc12c40675273d1d5dcc9edbf003030ccebecf46ad296ec9ed5872b9f75540330ac76d06810c7d9075f16247dfb12a9f812b2ab6e80ef9deaa2886b0f6250f302280bb3d723b937e952ab9e39b196242178a6b85590ebc58b78946e1ec9ae1b8d28387c3e6fcebc7d82c5453e46f00992d3da5dfc3b2b60e5507058aadc114aee88f436404c83e447a1391fc62ece3e738f277a906ace77c2f3b8c0b1282349c0200121b4f91af0ad8711b42df1c8fe04c71ab8f1856e3a68b9a3f1f4f2f246157312698ca91eb3357cf3bd475dc736ef1ebadaea30c5cd21add8456b732ae701fa56ca1fc0e9833386df92c52ef2268aaf62f9e5fc7c80be053f478914bc4f0c4a564a549ac72783b785dd0c96a852fb7b337dff1af8ac34a30f42c4e29fb5333bb488c24a98bf7a375c06866cd722e7b319d83ac6996d7e64c00d7abb808d98069381ad45bbc940d5deb2cd38b243aff4f44e06a1337b2bcea17606df621e91afb89ad78882813f158a856dade3c3598b37839078cd4492fd97df384ca8578e7901147ec689a552d7bea48d4fc0384825a2f1861224ca6ac400928a985d69e88e1039977920d81cdc332134d4a23a779b15c26b2e1524f8968353a2857e4240805d5990f042c0ac72828e1a62649b3effe9ae27487f18b48964d21050514d7dc4945646fa3d075f64901719752252f70acc0582d447288922c6f1796773c4746814de5999dcd63e3ec73b9f7e0091b4bf4e620a4f801bbf89c22d38a9f3927852d7850e4da72ddee2d453cf940f1032471a3bb34701fb9e343e1d996d1dbb7d27c7f51f26c4e96c7bc89b7ad0c85cf4a37e45cfa3ba7c10306364251f68eae030bffa6f5894d689b18570ff5ab356433e14b7a46fb573349eadccef529bb6c67f55e58c1762ed6c847d64b5d91124a74d0bd79a71c9f14161576cf6e8c0a8aff3bfc5bb80859d0c14a13a3289478f9f9151634025ee203c77da520574aa464fdb01de988b0fa2b5c54dbf78fe59cade7d20e649aacb0872c28e986c8437362d2467b7b6ddadad81e31651044962f96957d73496fc7f9ae35f6097496263e68589fef7aef3b796a7fad04909e621c2070c7f80f581425f54cb3edeeb1e0bc961d84abb4dac1f08b3bc099fff8de6ebfcb898699f891d5abc5cc4b8cf7c7634ebe5aa30b44793c123e30c614f037b082e747a6e9075ba7aab71e0edbafaca6d25a2a547597b12ddb0c1c07fd409e83fab13a9205e844b0e87012ca7638c8ec6ecd76796bf692ec240c2ce19b6f3bf5a0c69506fcdae18aac1a76c8b669ca3abc43785cb4259e83c8bdb52303a29380a708a14dd391666515f32b65c68f23a91c9fc51067ff88ba8b155d9b72d108c03023b7dbb6a17e558f03d0e328766e0263896953f0e825e356f4c58f0d1e912cb6f2f7022bf535543a9ab2bf4cd3d90103ee0042a53936025d20dc3ba23a1ff0a9dcafe11039eee0f7a626c48b4fcc0e0b92e579f9864523688d463fa306934b3996b8a02ef49112d5b81b2e971c3a48847323ad4920851c79cb4b0e80169ad5448ce4558a0916e547121810510ed0e206db320f318eaa94cade60999537d28117abcf165f80c38e05d015c6981421ad270bf5e14506fd90e0cb38d91a5fba838550d7f0e53db78d89737ddc21459bb68a1fb10150240c180d558e5876a365108e0b5380a4911750a06e3dddf2c1085ab01ddc5ed9bd80ab8984f0c034f1815de7f29218807a76e13d2627290025ff30a473e155ee4c183932c4506c04ec31d35016c15a9e321908e1637521640a3f59a0c85386ddb5c65cf297fb72404f00955036a9ce1cdab79fcd821f707ce6c22fc30875a307b204cc8e886e26a4387452694e1fd30ae90c7a81776952f4ce127e6e54d850a87abb3f9ef7e37a7c5e5e15ae1695d76d70c14e73b15b7570e0f3e871928fbbced0b11b4ae64ed249f9c7e0b94dd6d2acb43724a2f5accaa85d615a54f2066eee2657c9a10ef10145d074e6945eeadcf5db53b8c25d25d25b8f3c9ef0186d26c477df9b27a306732599a14406ec202ff63cf0653cb81b1d56d556c93f40d86381149fdf1e79a49582f8a0fc1399897142b0f91214e90908828049f645e7de5580cf685569c9475fdfee17f1465148f36754fe8f01649026902e616786659d5b70c6271e0eb5c59ecf1e266a256dcc6c11eebde91e0e72af53bf529ce83d43684e26c509cc1a1dfccfd2dc29efa726a541daf583573c8565f04460bbb2f8de6c0a915fae5674f9d082b654ce2a1421474a70ea084d44b4a1e250aaadb09ab39fe8a67a9f094abb653c09e457b62440d1faa9e7528e4f86c5e1ba3b1ee7a54275a071f2f424efa21608db5e4b22a4368da19d8efb4bb3abd0d055865992dd9bd6a27ab54ddff2893ba3082ddd5978919901ab251961bac96050f53a8ba042fb554cdb5416cca7b2ef1fd5c8c8a6267f5001292297a6b79877a0ba4120e485492eb0e21f87b02dc6a92bdcb56ef71afeb5c19721657a753ba7021a9e90a87710293847d215e54e2f04b3038f05a0d49b107b114546fd02f3ae9ba4cbed4deb3875b635dc26129f68b5541c494adea44b00187267b0fd0c9735815947d1059b7a061d2e3c49abce8ef25d780ebb5bc3b44ac192beff2b497750cfa1e8d62ca8458e7029105d176cb0aea24362e7dfc200a30326e1298b1b4c0b598acdfd859d3640539d36eb7ee1e1eaac9421060ff108486fbdc03260808a696d8401140ca51804f06885c508fb231f815b0acfb967e7383546c8fe8041dcc89671588d6626dbfabc87025513101d8a6cfa0f9e98597a06ce54771e1089cef20be2ba89d3718007a2ee898794eaecd5ff42d90c3e8c5313cecc040b9a6f5ff50bd43e7cc5cabfb2a90254057a557a159c4b28aec0569be68de25df16aa9ff960e317a413b2f9e4c17ceaaf41ecd9e49feb09592baad4effe2bcf2fc889cc243b13b9fab62dc6d40f9acfc35a6628e9bd37e6a7fb137abfc7171fec534bb7f980ff181c3f82f5538ba3d140c75e209d00bd0d6f0b62a95d17ea5b39193ca67ba8594ecc633dafb4cdae6dcdec0d6292da67ca0f655b99980e1c35b5f3134bd9203e5e1d7faf0bc0d5cb6e6f087f46472913bdd840d2fad7ae696df9a9b846a9720dfd629a18eb525e3d859e58a5029165f494e2fb00d5dabee9524fa6de3314125a49d374d24a1b896bf7ed338999ab745b0c0fb2ba080b0ebff10d1a393099f486c88461612896ec090cc736904e23d634da34dd12ac4b5a563e1df2aa1926058ea39d347a805de97926474ebd055ff46509dba7a027031f6a245e30e42d98624dee00bc7689bb25a10e27d6c1623d3c4e1b1a851480bf58e6730fa9e19fe7e9f6a51ab2852c1365fef5e1fd8dd6b309ee5e9fab26d68e8937d4b45e1c77a58626f6f25d574d9d0e050bb667a24fae67222ad1e765d1b8e006678dbb04660b2d7a6193d0f6f6ea33caaeaf441fef11d878e1feaf05c78f70bb86190e0639c1ab3aa64c4c084840fd7b30d2c3831740c3318f0e0934170f0e915a7915c9a2c159b28ce2e2d2d86e6f2d4408b272edbe4d56911993946165d64f7c081440636bd14cc770afe339e4d8acf4d8089e788e94fab001bee410f86ec80f87b8530d864e6a4bfddab618b59f03daae368d783c9e042c9c4f7d3740acadf9c775c782c92a11c6a5e4f3189de621f4efcb0104adfce065af74965aa2ffeead8b0f0cc23403becdaeb06fb72a15f02d72e0601251f4f90efe0264a8739d156fc5f090ada03e624b556b858a2bd77e437268867590b33c04eeea7c2f0b7f4eab650cb467e0660a2b3cd2a1ee5b486d6bb124caf1f245b27b27c600113743b1715efeb72b2f6c2803a1fb0bb932544c8fd67d209261fc84f0f6df66e06a615084c269313e0e0c0a61d743dcf8239e921a6b0f975d250f0c1a060acecf849f46dc8d6387192461fb83305eb4de2505ca67b0f130c53ebd04e013b052b70e1d6f80651ac5b0c0291067b540949203f798dc1d478169f657d7eb1bc177af49cc3a35a14a0fd366b692c7b9894b639fe22e2a5fe9a970570a17018ca1fbfa2ea6fdbce313847edc41381c4be4f979e7d6a5bfbefce63db63f10e30a23e0c73b1a9061a7f06e5b8f254d3f7e9051563bef0dc2ce32efd08c4736889925f82b215b1ff2cef9626919562f6cd90a5c28f34ebe8394c134f9a1c8f35195cc3b1aa5bc41c41d88b637089af2888602ae972a751868940625a3ec39d7c3933f0a99f18ca03fa168f59984f82acc4bc9fd9d366a6785b591b7a265d18dfefb5cd34323f1b54e0a0c4eca6a61d789e95c0a816c3191817bfeeaef73cf1ff6838138a22bf23c8b9b9fb9f95a7a27986f43f43b6e087e101755c7973749156020962581cd75bc33e07f673b1bc55371b139389e27c2ca31a6f6aa4d275a404178f30efb77f9415de105989bedb879c7c56f80ac5af07e7604836af78ddb1140bb143c2d0dd662223a06b95e43c41b17cd73e2ccd1ac3c6d0616ed0b4ed3ab6bbe207c441b9dd1cf3c36fb0d0a0dfbfd5214bd13da7862193be2f3a546537239453a80f2bd36d6da0adbf4ce2af086128a7052d23b3179f3204f5fcc6decbf62585b2480428f85fb3610e23869a89f88e4a8e4fa2a070c1da0d433090326bf6cda47c74c3785e624be7f6c5d88d51cab4e73988603a916d0e36fc439e1fa14e27a6980616d4e0f80fcebd48df93b26225b21641deb76f1884139037c067e155af355987d0f489192a704fbe03a82174555bdcb64bdd9d1eb963298b83902440086596f67381c96618d67871a8eca21ae62028e384bc311e5a4a3511761ddb4c5dadf27c14f608d89e28940626a0df67a85c8bf5c66cd834c5701e45af3746367031cdb69cc9b97d5d43c4ce67cd74ccd83ac2634f9332648199e7d56c599734ec1554cda40a7e2584371f6123b782ac90439f52460c5012325ff74a2b6c74f297a04582a19a756179e20de9348e4faf6589a3cd670a5468f30b19553be117ccb7fe32943cba3c7e108fcee4cf4296af09e10076743d21ef253978e8e2899609ce949569b7b2dd7b059d4937894d66689b3e05773960bcf6b29547d69924eb9aba1c787f87420ae1cfe9c047cd0a86c7e99f3a2dce783888b8e44c2f746d3a407026ac3f946b060fb174bfcd0c19960214b5c85112bac95c41f391faa72bf90bdc35ddce8a943e0450572bceffb2d3a84aadd1b137148a220a47e3ab0e8ab913e8dc071914541c3c0cb93da0678aa8423d557cf144f23c8c0233baa84e3642b3a748f654d8688f6690441901e499f264b1eefa2f9cc8dc45e85b69bfa38f004d2a7a9762eac54204f41a7d19d13b25802d24d0a9948a1a5b5dae39ab7a0188214801a98c2f45685870b8e43a7dadc8d1c32f8bf7e4e1355e6e9cef20d7acc4871fae953e4f42a99c077e6d846ae117e4918ee8c4871bcb12cec36d9b12d470d4f998009a754d30be60fc35a9174e18185bdf1967a98e490b415d0c9c791fecc99241bc6e4729683b5cc591c316239bb91029cd5dc31a2c0661dcd952fc537bedba7161347d83102befd36e1bd57e6fde339c3094020bfa18f3a326294f2c272f34a4062d5e008cd59ef7aea58f8a8394c2aa82226ac78bd019872126b5898e9a39481cdb622a6a14867927b6cf8f34bbd283cdf7e6dc2a04a5f2c92a545f4f2d977f30860eeb814cf72f1755d4bc5af94522eefe3b730c03d782438e4121d0b6b88fe5d1c99c4388ba2a9ce98d24129ac007d757a07fe5d49095cd9cfe5a21ae80af8220696d08769ca37150debe585b77de2a016532a4d2a140e6655dfaf23a2433bca41ef460180734b82a916e082d6e9b3bc2228fafa291b779bdcbe30b5e2b8d901d5249387b6fe2f5ad61d7d21c57306a4360bf3659fd426a22032b7bea299d3ef25340e1c076cebe9d64852c70026c2e3fa259fceca5fd8f2244d3b5a58dfb0b5ff6422b4f69245e1cd6fb97932883500260eb2d68f296b7a20c51215d7648b02f9bf84482320d08e793ce47d47d60171165805cd95515cc9906d2e9f5c0c9485949637da7bc3647b8f7eeaea06122b9e07904bb6df1d1f17412c3ef78976426b54e1040ac3a82f1e4f6caf8f9327c9893ca533167769f7e240e97296f99329238b84ec141e2a7f328494da066205f3255044e3029318d5e847cb4c81cf7f790aad5a7ae4ed28d2ccd5f25cccbee53c707d61c427e30d347ceb90acec50f65d61a6a2f890cf250aa64941464e14ec2366126f25142a266d14a055b8f6242e9d3e7da85a711111b6422473ad49fa98e4f86fa3bced385ad64b2e07c342946b7d784fe49819830a8c5be1c73d39199d1761c46b3bbcc75cf577c7fc9069d97ace6fdc979e33e3c5660fd70a2f9a278ecbdce5ae2badba3dbcee236eb56a523ce1062bafd9af739f3409c9a3b8d973d5ab30fe941fd8b480c86da1611a9857faf14dc76450146a2e34ff44221e4dce5cf46ff3b16c35b9898a9b23357eb11f39aa128b8808a9f872c32bdfc35b355a740ef920a07cb650edc618fed4e819fcf4902ddf231deff3baf3e2d005426a3c6933a65633b6ca17b4b059260ac8feb3679d400af5af73880b1a1a68f69a170cc890c149eb2996b462f0cbec85149498d0ec39b9c11127025d5c9c7c4628f828e5e8ed3042e9e0e56e5e184c460ec5d99e5305e4095f5eb2ba28c565806dba73142fc46352e6f2d0898b0ec5fe2aae431a180a728c2bcb0fe2904757f9c9d4c8a4e4e3bc326e28e440e33a64897f8650afefa86ee51084eedc1f11d5022bd61f3bdc0595a1f0edcf765c4f4d1d0fafa79a9a4a6cb09ad7a46c8e886e2ead591c7147c16b7bec0cbc30b59675c416c6090b7fb043292a833453b0bd4a0d9bb02e1fdb7dc0d4e462d173a722de011b2e9be966da638d6596fb3f247a2a0ba37e63463668994e2fee5c4e86f263ee632143260703a91b71d1497c01abfe73829a5198acb5351d245247038683f9838d149b315a615904fb3862316f7d8b9eac09d132d721c564911dbaf375c6c9c271ff7cfc42aa056ce8c2f0c4a8612d96fa46471bad3c7e1c4d7f9e44e2d0de736f31968186c92c1bbb2a080878e0b89e603530cf6187f1614783962eee9eb1e2ca3a4e287ca8c6ab2398880841d8d5ba065624038d87b9647b06a4be2868158c3c1466815e6ae82bb3b5ad461289629de1b2db17b9a499894cd6039f0930543f999096629b5a4263c0a601f9609077bd8bcd0a4b83c037a7a32ab084c68085439932f36ad1f4a45a016034b9f9ef193bf53289ca7681a02e71f6d151a8283650f5db6e4cab8659a8a82fc9ae127dcd64940313e3b9307685aa8bd49433304020ebe494efaa3e7dc44fa3520b10c7b9034e8309899bfec0ee83780edce770b854f45192c390ed701855f450a7b4a9cc452227b4e5857a447021664762c42ce19abde150adbf3e7ecadf593253d494a4d77b0067e4486c361be7331d891ef64ed1b1ac3030a64038a36da0a8913f39f3e3a7f16838b65fdc9b4aa634f25f21db90e0c8b70ef4cfd5f7f349eea38e844ef17bf5dc17793b70172a61a134517a36499326bc70152e2ebc18205d2a1ffe41aec2c4851703189fac2ca8c55b852fe34441251a64c7f21ad073d4506520b651e35a47eeef3e449b59207650b51a05d77cf7481aaff869549fdd2a4e24a1bc62cbd5b7630562946e5be0ae376631f3245c660764264ddc2985c177edc27d8fb43a4896b827c3c78823a30d77d0bc49457e590bbcf27948a8d72810d46504a8da90fe871804eec5a1295b3e572f97e2c31fe9fe3185852ad02aaa2dc04e3f111928342501ec9edfce65da23dc7e3cccf67c9f2e57cbd57901766dcd122a42c91b601ee7fc76e256f9e731e4f5541d83b778add1d1e7b7bb281a2a5e2443e76c6750de1f421a08628fcc34704cc650ded92c0f9ec53fb9da85ea49af64198f0c8b9c82bff6ca593217860836bd65f139c2074dd9c9b4140705951ca03f1e42cc862b6eacf24d1a82d08fa46405a394d50337aaf46910f4bcaa2632e79ca138e32c9f1d85a705ed96688147baca3c963d1bd4207efe9780d17d1aa1360c1a8acf853633b5a3fd880bcbebb72d911f69d5b1a3488100864cc59680ee77e1debf39e179d2a47d0dd9e60b0469e0fd7ba6bc4f50be18c4309011af8c21db2bc4410255f34b5fed1adc0fa4eb11fd62c1f5dc4a333d3bc7b4de6014e3e88a3f40a8f4afc110f2f5cbc8568342210cb0f61f9e972a2bb2e4c947b098721ad0ee6c49cac74b4c70e2245729aa2fcf8e7256cb6b7ef67b3194f67314eeb298239fa193e7703431279767fae8399a210818d30694293005300adf80fdc2318a2919195237bfad27d1836491ceef42962a7b4bc6680f56b8a01f77ee8c1a90301ff02232534d1ccbbcbc3c92cb2c650240d93fffafd94896c7eb1032fbf350921195bf06abb05f98aca9f5d8067a6fd00ec6463a355e81087336af0cfe12287396091ca8a4796245b03aa6cf94d22ce59bc2c7cd2eedf10d8bd95ac03b2295a242cc75e5757f96934b744267e53457275cae5cf58f90e8031a37df20d8330d6f42d8e8a581b973f4e240a344d70f06c5d1a60801add7ed2e737a717a77b7a050526a2d32c62e4805a38325e87f71b937bb9fecb981c819a010ef6f11743a75e6a72bdc9bca815ba41f7e0fe549b56a22ec79186c515b1e1098004bdd162624bfa67a331e73981669c58e1e2816097a6aa81fac4fd1e7b00d20b167bfa4a16c637e7afe33cf01d99051eb2a36aab6690dd4f3975e6135dc65f730ea8395415d152f31acad10f93cda52c40544a177239cc18e6681af5ae616a75a1b237c43ec838f76b1f8b8cdb518f1713a2a22825af8501b50c45f0856c42847205ffae3f0ed82fbb3757c8dc070f9c8ab53abf991707408fa8828887445b0e8f8981ba511548cfaa836ce425ca03e876f2c747f34365e227cc3faf9d2b80171c1fd9c9d64047444bf225a707d4e6e3244474758f1fa5c62bba87d405c23a23b2230f61fa23816403ecd6f17ba4fdf9d15223ff56c17cb8fdd8d441c0be21fc11e4211a15023b0308b91f28c66fe0904d778607d1e25c20e06d5e71965341ba1a7f1f37601f9d377ac8bc0507e8ed2b100fe3cbf5de83f67e330463ee47455287cead62e463f4e378a64e629e1edcd29d32759a3414c3d72ca89394af6e673621b20db2e223ece73bfd17ff0ae5d8c7e9c6e19b11c81168218013a426e112db81f3b392450a6c959cb75f8c9396af87c9c76c1f96c9d4584256216c2f2a15918d028595f8eaf81646f87e436dee4300c9f8e772c383ead6d17d64f0fba169c9fad534b4415569ffa6a178d0f215b509a4f688d21c4742330ec3fa2762c803ed66b17f61fd9b97d8e7edce9802e3ff78dc71017c63ee27194ef3f6e0dc41017623f7da78f5046a4441082e0e8088cf727d206ba88a0f41f7bb40b4b1f9bcb28a2c5f8a7795cd0fde77e2386100b22bf102cda9fb69bc5116c8d7df4387d8432223d441247000afdf975bbc07caa1d70842f885690cd8987200e842fa21dc432444243e1aa459a0b10af2196230243f7398a0b0afc791e770afd07efed62f4e38e2ee8f2e3b77621eaf370ac889096f243d11a18e2c2ff39adc3b8fc08daba416c05b1844045104428748415eecf27b70bfa27d4312238217a2fd102f589af7580680b618b116ba53f71ad13d1268432023d041723c002ea7355c38ad8f4270e45c5b89809087403bcd5cdb7db3b2bc1212f661c2322acf07d6c346bc4354232116afdfd681b5f202eac9fef8e11c10162178112822082a32330bc3f423a16f41f0b6d171a1f2138b5ea1f0b69179a1f213a2bea1fa18d19818e08b5741fa73aa0413edd6b17fb4fdd1b0b919f876d141118961f51b9a0c01f7da783c052048bf4a7e1da45ee07b51d86f573411754f747eb78856071f9f4ba31467423a48b6821ed036f6327a272d6c7dd89468c0f215fc45adb1fb46b17a29fa763450486d2a76a030e627c1041d99f1b4d24c4b888ff8876c41284a4a1a8b7d0f035c4c5be887fef0266dd18233fc86b17facf9d134388dde5098dcc2fafb6e344334614851815f10e4a369aeaa5b9f158555483afb1de6854c353986a8610c408858260f1fa20d9e811f588e8087e8968c1fdbcd90d41fae3b8b57284cb8a4fd5114888a07d8cea19c494a0dd789ee9f3765173f4f2bdb279da5290fb24d30e7b1792fdada4aa5dff89f154d2a0ca47e29a2f498ca0047a2e617cb4578d637c316eada3c472fb6e614ebbb07e4eb0b1e0fe6c6f65056144d0297b700c8e123f5a1b7f6f7d87f55a0de5e0dd4475699938fd07a8797684fd3e52be1394cd3fb67edc3eed37f8ffd7d510d52df22b727ed63bb660a63ad6af28df3db1047b41f94b12602db41bfa0ec6d224eb054bb18851614200f6238c58a91e290db6ba7e6295eb6b8efbef7a6af030ac4259df5c837577afd2bb3baee5fcb0501cd2d52bed735df50af3bace196d717d06a90504fe4d6e3b5515974ebac5fefaa0e474d50e375c91acf9046bcd1cff68366e9774ae785adfa914dc81696d7cdb4febb97ee8ddc9a03f20dcc010b1cfb61bbdf7e21e3e5d9e18578f832303b4af2d4f98cd2cbf1f61f3816c78bef2ff0a3d369b8492a4f3011fdefc44d98a54443b38229e97c63d947dcef200500af81f883ba464a2960df043638bf77fd3175c92f9c340e1a59a5185660cc284e261b1f3b53ab8a63e17ae00a39f09d16717f94b5e7d668556746e330c088eab2e1fe913c7078e32c6e0db53edc8a3d0b3a40a7bb2d5f418f23760b2835bac8e71eb878bfb2691399cbdfbbefb17fcdbadc8adbe50352f938a1a264c9bfcc8157b4d2e44eafa11f652fa6728a90c1a91f24a6cab701e0f50a127fd0abb2ce99d277c1365da65819b539beac30b05d63af0b32bc002ec0404faddb28b59505a19a5036d62ae53137757f5cf7a1da4f5b492c008255402581418215cd40f34590561612cbaf8dddc2b34d19ceca920f47cd678c72b13af283eb8ba0bd68baa6846fcd645de38241c69f2db2df7967bef2da59452a624036d08dd0877083eed23c4446449c1e2b1a4604109921364e7081cbbf18e11213b468c6c6c44c8100fb58f152c7c80a0852351bcf0bdf3b1e261e2d9d1bbc109673b1ec9383a1548082c3beb3011e623424d82fcfeb9faae1535a977e208660c2cf42851de05c14677efa84f74d1b6e22184511cc9fd47d98223b9dfde3f6de39c1fc392284bbc3b1265c81f7977a4899eaf3018f73c848b4479e7ece368b0cfbb1cba65b483a3c13bb817cf6a5284efc18d7db8171cb9a554ed4c94cc70f5850ac13a3a04eb7ee39c242c5bf8dc6f4ee70a58f0b9820bbec2dad92bae7408969dd3234d187977e40a507ec6bb234dd83cfc691207358927e730d12426422c75da03bb8303bbb50fcfb30dcfd63ecf50ba87e166bdcdb34bf6e15edcc06e941f4777cf40a8102c4f0c65c00e468f103ae721cf4588bc2bf2cea9663dcdc976db7d43cfa74b6df41cba7cfddac2154d122561492e58f8b113809df31f4d727e83b3681243e9cfdd483aacb18da45bf23b910a5574c7226a748995edf4bd18638c2f366bd613d5a344682ae5ab2fd26da829af4eb594d2b179bd3ae7f482e7d48f27a7842c5ea8943c16c8429d58ae6c48446d741b12fd8acd57748bb330b72cafb5662e43dd52cf9cf29a9f3e399669ee7257274cabf9e9b27c6a1b7c2b5a95e536fef1e84459c5a546a3ca53712c60dce9eeae79926b1274697e2925bc23e8ee9f47f85e7cef3d9a13a5c9bf7e16dbb817ec4d08014295586b739a04eb9ee5e833deb1b8ebde24c7d243c41863ecb6babb8775cbba6fabdb67443f2d6f6f67ebde886fb9cbb19cf268b5e65eba4b5f98cb4cbfa6634e69dea2aeb6ac8ad236172ff9879b0c24ee80e7260a41ef8e3891f2d06d942032f698ee5e0bb64deac13750d7247ecfe3bbf1d21cfb9cad92cab52de20a37d2db86b41c9a34e988000fddd4eb89d9c126d15b64721bd3952b9a149da8889366b860e9514767a742900a16a140b0d3e438e4b003157d9aee68f2e9d51d4d934f93534e3518f79482d40cdad447f441ab88dd2c6dd91c13299dc548ba2f54d097d0c121073aa94f3ad3a4e68109264b94d425748a10b18e10120448e50307081b9a0bccf0108125ece694f0b834b4f5655e6b047df2e9de1bf13dc8cc31c218d9868128d1a1040f0e4d6aaa8ad49c6f4ebf1b0f1a12d79ce235ab187d461e44f1ea8857a446d1a7ad66defbde8bcf88dd1c4f741d3c3cf4f6f012ef28ce3b8a334d6ae7418711f6390e17765db7e0ebd21ee0c3d6a1048fd5564a0597e8097a56c2bb2356c8f9aa2d1ebcf71e84d159db620e4dc2a149458c34a9fd0d612158901cfec138d6816852dff4b84036538108e868523fa7bc01d2736812d46644ffdebb076852ebc06e2c232454dca5f9ddd1f317764d9ab0330d47c8774569329ad495fee0dd8d878f48a8f8d3369a8777c892ae2f52082f7841e83322e8d05b45b3dd02fa125cebb59a047d04fdf9751fbc37a20efca3179a03fb7cc62ab1bc1e5ede7d81f75dab496eaa3ec5fa52c518237bbcce2fa7340f7d6a9b93f2f35558f3d09aac4a4342c559c55f7d9954ee01ba45bbd8c06ef4df7bef8ddee8b9f459e346ef5a1f7aa7430e4d723eba11fff9832f3bbc0c07956301040bdd5ff3819fe6c0cecb5dc47cce5a2f7fb4898d5e3948dd462d6b59d4e5d14514e5556318e630eeb13bb22e83f974ec4eb73e79cdd35c6e31edbab49aaf1ad5a81a8a504a59b230d9304d62a769a2f9e96eec92254f53333b7637eb0bf385938f46d42777d4e3b34f3ab99b981d3b669f11c5fac277e47ce4ee8df8ecec95d3a8114bf39b731a5dba72d78ee6f4792d4ab350b00468924b93a20cabe347df44314ebef90b0e3dc0a746d829baa0afc412e9742101a838674c2cdd423dba0cea3c49cdb10de581255518f24ecabb235072de6df46964122bddddcdf98ce87b244752925e8b5b95d4266ad117587ee7a3e9d447d39df7f4f8d2a71bb179f84dfa8c68d4a3be3762843da048ba1b0a148968014d126237fef1cf690facbc4fca69c82c42d43853d8b57b61e75c7d81ee6ea277d3f9a4b3e2790bc7f97cad03be6b1cbc92c1d24f5a216a52d7e077de36be38ef7a234aa1537d715e31cf39a5741e1dce1835f91c4e53a8bc35a971bff7d8bdab9fd64f72cf29e59453cef9a406ad263976f33dd9ef79939c4b275510244439420e129a2061c843b1a1e066fd4539a4dd92551cd557ca2baabe502e4d5deef2e0065dbab25cb561dd8dbaa5aeccafc969adb7ca6e745a6f505f6785a93ebdba2eaf9afb595fae8aad7a434d7a22e79856fdbaa24b5f94bb3ccba2ea0b75be611bdcc7c81ebdeb4b9c1aefc0b20fae11ca25e8e4cc2372684e0e8dd3742d28d18822449a34a48fc84981ddd888c73e7eb0116c84d52181cd6bef8e24a1c957d873f0b7be81154f9360103536bf354efb68d24d10bb7110fffc4d3e3988394d53add561dcd7eab17af41afd7259691bcd5757a5dcd5aae62b6d52075f0e54017d62dbf27e1b123d0b8e2a97a13c3a755de5ef6935516dc6fa694108bb452fa1722c36de61061a05e89b5f0eddb295c3ee5b3912377e03a65d5e4547e2724cf380df10032929a596c3b8b7eea8ba8ce5d3ad9a6f6db3be69ea373563fdd49acbbb6ab30dd54f0533501ebbd1520c9e7763deee82b2354d4e695d5f285bfd62cd6a30eead77c5e13db7f7b9b5d661dcdb3bba5cc6baf4cbba74eb358f6995bb0a53ddbaea8ce8abc9597379d434a50db13c27543c0f690e47a3a7213f35b182e54222db44344ee37ceb344ee338a99a8907fb49d80e217cef69e09f9477ab6919e3ab5e04e275ec0721ac2fb01bc78470c7ac61f7aee710f6cb2248a99a543c939029fc9886d8a07a0285f989cf1328df0c28f2d3a51f7972c44fa77c3aada065edf4ad9d5a1be593bf0a33afcc95a136289f77c617c9e05a267ff35953832f6a518ba865ad39ad79ef2643397579e409134f9d7a92e49f9b4e51f1a9a3e1e0d36ee9a17fe2a78e064dff049d834e3ae5ee0356a6292d0c63ba4bc855acc778348c56bcd1b3281ae85df1463746120cc362b03cef19b4ea32b4e86ef455a49850641775193fb9ab281a43c6c7b88426d2441955556137460cb6617229e3278661986377a389325e7ad562c4783f62ccc02a66207a47543f5f619745e2c7e26850985f16ada629059c525c992a85ab10a510f5cff3e45be531f265d14a464d8f6753c50e52e15bffc8fe61e7bb754f55d148e10cf9b2a8a489d4c5aeb84aa87ffa4724920c63a2581979e6d257f8d63d32acf0d1dde80c5706d195ef88afaaaaaa26a8429b9e42a3fcba00804eef267abf2e32005714ba9b0800d7bd162b57e5d270dd6b915d8b4d16b5a40c8f5b7439f550c9305c442259f956fdf2ad7ba89e97aec22ae59cb37f9e25ac91a155144102021b242050820404384884e0412284112442f8d0389077effe0725ef7e30f22e080fc2c843670f42c843df6a1c0d1e70d617579d76839f9dd33848ab0eecce67d561dead6604fde11d69d25993cc1cb52dfa88a33b490367ac3ab00e5bcd68c2973862c8ce9fac1507fb731188a27ca2289fe674ba6487997867d189ce48f9ac379e532eab8de9c27b47935b35fafb7b3a5320a84f9d6ea1ce497e4ef9f3c208a9cc746a635e78475fa3bf9f09939e1e263132e900931e0edcfc740ac474fa3c22e940d047972b72925368655a51817649b7bc14b506ebccbb4d954c867af4cc561bf4c24ba95ba37f7ad5eff2e6662d110b1602e0b0001d3317000227481115b15a1de8b1262ab25cb7c8509f6ed51b8cc4034068b5416dc886bcf03564f7e2a58301691add0709103cef90000104921f98784758fdcf370eee3b09234838a0e49ff34dfaf4a7c9e99c2351d2532e272af31c3ee76f92f451f427dff597d32c66ac0859345a74725ad9a8aeea95c6c2d250937af3ea95369ad5acacaeac1dfc5d7154f7f2ebd21cf5aa6d24caabaa6e11870cb54d4836b0e4ab6fb1f26a6a3decc07e3a0d9c778bfeaa0e75341dfa05eb1d4d3a791c4197ce020775a74f77decd7b80ee63840cef0efe9d294af71426de1d19439431e84c243646be82168c7146870870df7192385a7ce40ad33e9bb60d272d69e73b1dba37c938e5ab355abaa3b1419fd2e1447a799f0c7b1f315d62ec8e4545847dee64a64f23b0ec684c0e1dca489f95d4289fa4537763aa83f4c9725efa909fc81903cecf31d8bc7bea486c843c00de21b1b9797ecbb719278553e596f4a95973ced1f438494fdd9ab4a2267cd3e3019f999d3df246f39ecab0b38fa8471bf4ce399f3bdf28cd8444d81dd8b37bc12e7bc4e0f3530c3cef5e0c3bcf6940286dc8b25bae8dd89d4b863c79bdb0e7019f3a0dfc6889a81522eb6eecd5a7cbe397ab07e83cea64ddadea405d1e3c8a3e391ad4374a3ad42809e108fa74d824e8ce25846f8ea0bb6892ab3c4750b58fa2572ebac58901e7dd77df51bc2e34f01c638c32d17d52977d6f90de45990dfa70473748efee0e19eeeb22081a62b0f9db8407fc1dd83bf48da22233d39174ead3237b9c9c9aae0bca91f4fa8994254fe3dd11295382a64079172e9ae41c8d1ed89d7bf17c7b2e478f448306bea90496dfb984ce1fe9a34f796f903e4217ee85eb1d8401663a648da77c8d1afc7c47369cb3c3b82befab79587144779707bff4e76c4c1979655eb78de5539094873e253fbba4a873d54754454dd6a437a4bb6a55148c8bdb3d526cbe721ae5610d9a20ec1d4d5d6334513ee201dff90eecddb576b00ef4f2f057714077f7dd1aecb4faee68ba37483f9dbabc2e9ac4cef513da5ffbebc7ef9a5ad5d1883500439087fec030e4a13f99e79086a311fdedc05ef6837d23b30caae7e32504a47c748e62d3e4dba7255f5802a35e6d3c2bcc34dac1ef9c077cf61dd8f3ade474b7e7d063c5119d2f0f7e77dd16bd071bf67de8aa79faa96dd16978f27765aecca481771a6bb0da80b4eec0de07e75e6c93bb17dbab38de8553843c98073fa4776463faf37965a0a4e18f3bf877500c91a8a0cf20d0e917863c75092d76d049e7587a7483f4f0addf586698526077f0b7b3b868d2736fb7a20bdbb9f8e733b868890ec7bc1b432b426eb1d19707d750c4727d81dc42ba8bd546ddc13f7ddead7970bb4cbc3c1da85cb7ecf86137beee0b3ebe1d5eaa8027be60a313903e3186a09bdfac3f27a60db04c99029484277680a430fdd004f543b4f1c50a3f617842c40e12429c7882871e79a2e787c9c61e01e2019091279ef87142021c68020927b879c2092bfc3831c509293f4dbce0439f704209d5092970202259a901088c1003213cf0a4e786e8851b401d040103cb08eb0710644e04f9013a7482034340c8859f28d38f134350275c3ad10527a0fce62c5511288c99266e10052953863042083770179edd16f1002384c007064178b2851e708185850251caf2437f581c0b29a54cfb8b4d64c13a879076c15a8e86dca101e8903a1afcd02747833af4ea684cef875ef10e7e007c7b0f0e806f07c0b71be065a4cf7ba300dfde83d3797947360af0ed05f87637e5390aa856356147d46b9e6e81c8cbf0eec816747e64837af57ab75ad50a408f4e01e8916a153f66b6aa3faff5524eabc35aa3bf564f6e30ee6566a05300fa7b9546bd9296d6030d7ce75e488f9a0c7b81e7f9f9bdb0f3eef9be446fab5b9e7bee5d2e6d69d39fbfade6d92b68515abfa5f19477ddb26960036fa36daa3b62efc92b8d523bb8c6df1aef298d6a93666955f47872808585628185b5a000ca85162d5022063f34e1841260c03e4c50477c9c88e0881688f0d6bb235ae8c1119f2447b6d004ca052b093e5cf0c2089a00a507bc05daa3052df0e04536a40525c440adf098e5393102b502a582f3f8547cf406428d702ddc6fd86b01c7b5702fef165dd22a5815efbe27b0a9786e1848814700de871013767c5c16826fd887af103589f20c91c54204769b3183f44a5688c4423464824ed53e2054127a189b8818db07ec26a271ba054e8d85bb2c5834c99186b09bd3812c627fdc1a8b153c3f624f429ef325be9de7953c096137bd601af234b6e9c157d424e77e84dcfe19068208bbb110478158ddc2856e78e0d3449dc301e73c1a8d82dd9ca65bd883fe3929ecdc9da980a3b1837be1e2b91128449a08616289c825b104a02cb0a31b1196dc0d6c53723738c73baf091f8bc782125c4311bbe29b8a4b6fa50e7fe5ca642b93fb743719296ef58dbad1ba3299aff8ca8d8162abc9ae0ca53706c390d78a31e2c498df4dd4ac77855a3a5ea5434a9e27bdbab4304da39675ec3154f4ca082a57b91ed2e5ad6196cc31731adddaece2e831d24335368279a661ac0435f2152b21b9e7a96466253696a79b0c56a265748db2cfe5b3e212a8e425691d7ebac4cb225bf5e9d472ea912bcf9c536ead8a3f7aad38d34bade69a3459a7d762f6ecce589fb1fc7477ab5c6164d8e16757a6ba993563b1bc7569fa3bbecc7c7ad71bd9bc34fdf7551cf1f9b2120cc329e19c4551a64c29a599f142596ba8682b2eb5cb2b57d1567c6a93ab3015f9943b1c85a17ed1158d4e2b9a5718ea9a60932a5a83f2aab22fd6416a895658eaf4b21258ab0cf9c08f2094fc9c6cbdcc58c9a0eae280f5a57a1aa5fe1e5050b73ca72eefd517cb2bcd7ace3d75e9d32dee839ef479d57434367e4204935e4d30b0328cf3e365788724044136e2db6e7129ea16faac90ab2f34eed024c9ce5d434ab0d239233c6584272f5da2784189381a5a03723409c50636f092af732fdedddccebf1af3e6b80d8049f1295e3abbcba326ad0796b78622f6f5b833caa0076218d1e3ab79e9762a10403bedb4d1caac2d312935b1a528d6969a586b4b3c739bf6c21cc34a4e4a4e4a4e4a4e228e8d85b5ca9f0965ad70f28a450bd336fb353d205621368190b930150cc33cdbf15430986132484eebdb8417e6985bcca14b202020a030d85a6b9db25638790d758f9499b5d066d66621cf329bd92c44999969669d9999332aa59c3f1882840820594d8f50c87384de922c14ca3c47f69284a255bd86b22c94798e2ce42e2fa46d6f49c86d080b856cc86dcdb090cd429ed5cc5d9752f8964d1b4a81659be341e1ce39e736fb190a1bb29eb97b2b2a34d156c76e4c4c4c0c54695ac47bef65999457adb07fb2ccca32785957c6cc4cafeacccc7c5129a59c1ca8efbd7759164ba06eaabbe54f77cba0e8afdd4ed5ac7609248124900432f2de7b35ce18afe8571613b3d9afe97165ee725c99bbbc4cdbde929ab975dd2cab99d7cbcaaebb3d9de7d776657eb9e52b948a3be75ce982ae2dc6313131313e978024043d4842b0447cefbd2b4ecbf2184bd52a51cfaad5aa5e6a62952cbf2cccb228bf2cbfa85adda5b1aa72ab5e5b29fadbe857cb2deb6e33f1a6542a95e40e608510c24ad55a1dd6fea1aa5a29875447a9b5764fad9572ca72aa5699c5ae5aa7699a6a75153b79a6590dd3ac4b13bf929314599ac4cbe8cc91d5f4c832cf91bd255996836d982f49966533cbb2f7224d9c9366ce3967765996756573ce2ccbb2ccaf2cc3ae6a5597e6c5c488ec64847c6f02217fa4141944abeb76a60492405597de7b6fd6f4982526a526b3146582a5262528734e3984cec7788764081f44dc0c1134c40e7d3d55d49d8935b3e444642510e7c8a0278364d0ce8cdba1dcce9340124802cd09bb674a082194b37ba8ee9973cecd5ebe89328b55d7bcbc35ff69bd6a51dd4df4532573ea2ab67a66318db6a877a316a5a494d26a79a4722ce8d3996dd11cf3e96c98532bbe5915df444faf5395bbe22a7e79e8bdf5cbba9b2885ab70ea825038f5143e299cba0acdb916338546431abd1b8daf68ceb5c8ec45137ffa4613a70a3a45d1dc9898981891acb13d6540e17489b5cdb6454b4c4a1dbb4b5389e79ffbf801bc430202262f05890f261a898f24487c04e99ef7c59bdf66e20d45dd2dded450c4ce3bdff49e974aa51285d36e87053420878e2944067d45c529a5fc9932884e193431e953555c98d247fe4c20a6f4913f534a995302cd29835efa36a504622e481ff9238364d053af2aea52c229e4ab39e79440f2a7a2aa4afab824fc733450bc731d1208c50a5c12daed480944718157f0cf2510e576dc8e119e57779b713b124802491f1924815ac7eed8f6e1a763b9a3ee34e89846432bf6551445510fa3a81cd414c3365098bcf5e93cbba43629a38c32caca9f4b4ade0d4a2973c8b7e4b729bb6296d4736c2fc96fb39f4e0cdb407db6ce4f6787dd529d9d39c6182966a65a8b32cae84ede18ea86acbc9b0de939d806e9f2289dede954184b39666f0cc3a02b377455ae6c76142a28aecb6b0cddcdaadccddebe2c9fafaaf7a4945252d235813e318d7a5b3e69977305e58ca46ee996ec8a611b441be5caae29d7946baa5e19e9d854b515436176695ee51bcd9b143b1e8aa228e9540cc360974edd51f418638cc1dc625e7d733c4faf8b726bf9e4cbe3e5f1f278dd8d263a9e8fa1dc72fa4275bed22a197fd2dccb97524a77694a833fc5308c4965d0b99881503dcfc20c7f30aa1da451280ceb91b4c73d6b9d48e4308c266ed47a27518f72564d8f1feba214422b8a65f95cb1422b21abc765ce5a271239cba74513a54575bc928b4ab642d50ac3985b2c57329b39bd1b53dc77a389543e205f5996654986111d529426596822a5d44625db50e7a5ee669dcfbb51499f573c8e7a2e234da494e5cf7ad6b32e95b27b9e29a5543e150e255139165b081271342c871e6fa28f6ec1e996e9d0e38f08c4fa0c9c12cf57149532762565a9899c9c2b8f51cad601d617eaaccdf881f801ccc7d6f428312935e952144d867257778949e5a52655094a75959c5c5455e279e94c83870e89381a98c3db15dfa06e6944718949a94989a7c4f3b4aa30969f4e3e51e91b56849470cec0e95293971e2dbfb48a7a4595985022883c1624e268d0f0ce23906e8951c6ea5d616878ac084a04544fe4a9f1e6db65a0dfbcbcd06bc909c3a83c6e5811ff36aa47d650c4cec49b78e31587c584df200dbef2cdaa9cc857779b893798cf3f2f39a16ec9498949b7b89f4a3c4fa594b2e42494a392c32fc9fb115ac162bad5fd1ecb63792c8fe561b41ac590e1618fa29853ef870e9764d22adf223be6f1a1788fe5b13c96b765e0e5bcf71ecb03c2930cbe61efc74bef472f4d7f855d9afee796b00cf9fad2b48e577216350376633c88652bef3d96b88dde8f9fdecd3102ae5b3638197e8c30348022965401865e8cc1a37292524617849a68cf3763e411df9015fc504477bcc9a4f7a00088eb01c0cb3b29712f9e6b60b0ec5b0412814c91180e0682b66a86135848e4e14cbc719456be459b872a9443f764030f5bca43e81d0583d09b892e35ce6b61e3bd7351b0a96cf1c9bfe78df34a13e7b5784ead48e43d75a66f7104ff66f0d4b7f8a4e2b817cf278de46e2da5e1a97be9b817cf5184a1d268f88943a9b522914f9c7f3bbf4d9d1a287eba168f0770d8bde887935791a37bd75633ddb7014883df289c7f1e6f78082b7842e3cd6313112a09a1e8a70c62a5aca8c9a38e971e2d1bfed2279c17c6005c1ef1fe480f891c522821b56e879b024bd363ccb1315ad16bf1bc732cfb83114a1979c097b1ef6398820c03d900cbc6f2f1aa1e5df44a7666c6cf99023b27c9b5885e35e75a3c27a919158024e848fe143a52f4bef2c9f87246e7282305663cbcf0880d09200fa2877046f431b6e05952939ecbe063d9371249240025bc40095034054847d9110819fb8a20e25c122e5e1d950ace396e42db821a7c3109142faf0ef7e279ad82753cceaa421245b89d874de297824a640613d8e7ce0851ee08914678e95bb6e2e3953c588fe4919247eef00994e040ee3cac524081f3cf2da033c30a2c9ca174deb2b42da3741ec23bc4c21e7cf42d637979a9141db0ec1bb5f3d4ce3f977186d2b9e19ef8c0030f9d8e6cb45f0a503a0f1905ce5b5241af3007217337d41e85b0f9df757d04fd8d7dd0c7642282e5ee8679ef3d1808218431c618a394524a0983412290080fcfc37938add33a3e3e9cc339b3ef44047b434e7a6d350ffd397cf14222cf1bde68136ddc0b570379e61421dec41b2a9750350b4bbf5771604822bdb88089bee7dd3ec0c0505f83fa47dd98bac2697a8f85851fce87de8e9b5eb0638d50854d3b76b3f86bded517f85b7cf62db68d487babf9f68617ba172bddd0db4684b0c264ee06203a3039f50e544e79aca3044c4e7d01d5a5d4a5b1c2581b7c2fbb17b2c2602318f77d6b2062435ff315ad70ea8a0355d5054c392bcca5806901d39d71674c153aae08700fa78a85e5ab0226a70e98ae7bee4d072184909b5eed15063a8ef601867ae830144ff97e78a97bc1d4bd704e2b4cc560d89d4fd5065f3855984a525352d45197c73be5ddc1533ede910d0aba051c4f790dc01ad9758708964a283dca19b713049d1826bc1f73564aef08063abd35ff9e4b9f3206c3e89ac8dc848a3ec7dd3db17b62f5695a0ebbaad339ddcd25944994b834c9fd412aa59447dc599076a89649e01d183694413208ba1df9237d24101fc142a20f6cbb0fec0e0b680013294145e266417a3af4e95038cdcb9dcb34ab611ae9ba1e78c5b8981d9af4bc6a39683834e93945afe82724a41824b67d23915cb0583d6540c5184b3cb3c43361bc29954a4c4a14454d3ab1542a954aa552a9542a95269d1863a439b188cab1d876704cf0b51606550dece67a78782556f25a3ca7aca427064ae80e134c9af0744bd5d31345fa4820e9b38411d631b2840e4fac0303f629b212aec7480c7aa88411b203310c96525eb2111e662eb0042b713dac4402499f1e09c4ae4702b10f0a3a6bdb18a969e082181ac820868a2922e1e482294eb08884329030e6257918c330a084998d91ef51c7340d567963d8f1584370fed177d5497a43d6bec0724ab3e0b592b09407d733c3f5cc9831cdc0ca488f140fdd32f9f3990ad05ca007c537906248e5e32a87d6001d0dc8a183a58ee90532521bcc9881e5df663c3c000e39744be54f320cda519ee98c0bcc20000e39789899c130585e58b1e895665cb75ebaf950f3d379a38974068553b96a82f9d6504464ad90aa442722768b3931e7952823afc5731a73620e65e44d43accce4b132422d41e95049ba45524ba89d6ea9983069522a31295d6a082584224209e916e814916e993c514426fb144b3cd30dec7437c432f51621dd3284482c128d944a483ef04309e9962de6504aba65a384c49c28ff3c1e41a45b9e53432823dde2907c60e79f534bc41cca48a9444b4c4a9b0f35cc37523c4b6077ebf198472d26e68ac1300ccb5155130e9823e6e94cda85c56039629e0e86c5b00b7ae97c5ecacdf150216dc66bf17c45d3322d068b99f1eab5335c6065a847db2d35a2908bd1414a9398637e69fd32ba457ae69b7d4a928e69de2449338dd417e8d100cba4925a773b0b9bbb919e9b9b9bd9a7b368329a5a6cdfc6a213a313a313a313a31373f595849803833c20fd436eec234ef752ee1eb4973b0d73d7dd2245d52bd3b752b25e938c31ca28a5a4942975a63c6a14a07c4e1f3e7cf8b87ccc3927631c1dc3c12e0fd3aec9cc3e26fbe039277b5503112b7d8241e2c66fc85c5ad8944f94addae42e6f7aecae4e0c6b0dcaa955d1c9faa87d6144aa49889599936f842fc20aa7492ffef8c793166589475e1c327a8949a989139ec9a536cd59b91293529312cfd4625541b85142fec17869f8692704051b73fe0181da7579d0e18b1891e9c6cac9b967baceba49525f58932cd9861e9707eb8bf549b36e32946c430f4b6af94b6963c551bdbc6c735953e468487f55bdd5f4022b133d8abac5a3f59c5239f9794a2b990a75524e1dafc436b7de8a9d6d28b596b2b3cd7b5724aa31b974db32fc7bef35752939e7ea2b0850848022c4e514ca5f17c775dd0801e51202ca8d1050fa8710d00a91798c41f0e7f97414d9f3f1e6329426459f61ba6eac94bbca312b6b8bfa6ca5ceb5a8a188a5ae83d331eaca4c97da30bf1c8bce42b00df5ca5c4e51f36e35d795a1f56e0ce5a564ead2a326e3cb36137a6a855e464d3ea71a05422f237d9a7caa5c3b1d09eb2e0dab02563ca32a9ad52de454d1ec6bf164e6765214e53c39455977905a2b9ae4dd828560a96d7bb0b2c232acacd0959588a22573141e3d06db80e2e24071b79a47e19996394b8c4c0647a1c990790cb6c17de65c5fb2958c856d88f17e740bcde8ca4a0caa4237026c32748454acc7a02b2a5288a21078c4cbe920fcf10289f7fda054387b4ce1d137cae3ddde0f1a7c856f74e49bfd19ee96f98abb6e41e19b2dd170578caece0c57e4446ed221ba9b5b227277734908405150141edddfa39b0dad68f1577ca389323ea81ea8e48d767541ea108d000020001315000030100c060483b180381134c90f14800e8ea04a6a4c9788c32c88510c21840c31868c8008800080cc6c080a026262995f7dc3f061364b102a9cd224f894cf9989d9d7abe28ca134fed691d4ad167ee535d460ba4b3b008542912ced2c84a21057c24c35ea8e835668ddd855c92ae28cca036ad8204f98cd8d4a09eba371016da3777a644986f1d418b62c215628855282bca18ab33126a14b080503651a0b574709596e2c836ecc29fe1f6d35f617efadef51ce4f04a8f1b3d39bd124f11dbe3d7f69f405d7c013bf68573dcd6bfa20ab17d37187e0e1de63193af1cd10ebd38c2d9fcc42ce148f3f6e53ec6b95350daf7f9d2745bd1b8a5a2cd5517f045ad56bd03b6db36ea27227d637a8fb9c35be9b044aae1ab1301db053e8c03eed3454f8913fd0324cf54752741d82282ad2671b112ce1580c2cca5efbe71e034f615e2f840a559ab449c258ad1f8f666003812e8568a646d8631f38b0201ba8b3a74cc643016d27143bb2b4ba4bbb86d5c4100fae1b062a0a20961852bd12d4653757e87d7f9ad60a770e59727b3765cf160581e70700eeec91e90f8708b8b48a563a446c54e12d4133002fb894f324998dbadc7700928bfa788fe89eabdbdbed94be29879320c987fe2c091d050279322c035c385f492cfbd4a47071ca0e5457181b8f65c660d72148dd24ffb10d04f7e07b87835356d627d7510268816c0fe53f939ab68c8b1ef4c715ee3069a83301a7a635766d1d779850f93ab29208a3cc567e52b49959c926b0e00260859cf1c9e7cd18fd00da180f5a4b9d72175144793be699a27bf98f7f84a6b19b1eed3c02f2d320a4441ecd6ec108b044d38eba94c239471fa77749f56be59e314c9cf15d34d9d24995fa94a1b3f2082e3c5af88009f4fa5bd488fe8c95ce779fba204524039650d30365d255ebe0c978f084aa19b3df7a0ea2a4b4beac9e83315246fd24e5ab948fce4a9958167b2d2b6e6ac820eb10b7c1a1c624302486224a810f7af216907e48dfef4252d6509d1058ee8c51755dc28cfd3ae58acd21abd2de43b0bbc6a41823e3bfa6ca6a87eb1791a49794e33f66dac4bc78fbb72b26b8fb464db81c85cf752bdaaece1287e18527d11683bb0ea8310a2fd36d4d1153a06e8e1716c27f2226e1f575b27e395f525dfd23040df1d4da190082b038b7be5ac13dc1ce8ce17f407340e3d0f7bb04c546a547c13dbdbbe2fda7fb717aba89fee1932b1a4509be1ea624a1ecb9803265e3fde3494a3eb3e80f5342029c660d4a3b240b60d41b0a0c2f5aa26a293680c4f6c80635db8fa3045ac3ea5cd437fc6b667bd95c0a6b2b33f3735074d00d2ecc88e6b63ce4fae1a26b01168998aac0894161d2cfb9be36bebb7c21a44097d544204cbf4f131a2d7ab5af423c13e2cc1fca0475a6e934cf2ca2808485b4fa38b4691c7c3e254c86c8ad63e10e93b007c03c03299ca67fce6f0cb342265c7b665ff2c14cf7e185ba0a0fecf6bfd5cf2399e22235e68f62cff5d5e3eb2086bf80b3daef50a3980cb09132e6b7e92bb16ae16418e8c7ce5454a405e9eaff5a1f097845fc0f6cbaff40bc1bccb58bfcb6d17f28a163788a776908396ed6acc707fc16fa21a3b138cd146406b49df79ab30b39df7f23ce93769374abdf3ea6bc9c592071c8e1c9fab436d3e86e0c9ce954fb5116fcfbcb8b65cbc7125b97fc2c4a52b94eea629f855f0ff8720e619c65e2e8087729e6c1b3748df4d30fb3dde23a7cc6f6af7096736a9865005d80f909239d385948c7a2b61b73002795c6604261c5aea38072aa5cf087ee454b90234301140a6cf3c9e134a1f542e30002dd2f96b5a016c558058b82a53b02df8af6badb6f25dc9933509602a86b97f0d802830b90d86e6ded9b86d301fff3ed63abed1b69ac14c09a93d103f41bb05768070bd50a9c4064b552fa40af5479713b85ba2bb9a1bc1db5d6d7590f98be7b0e65fa260c1f79e39ae6eac39c7b910b7fbedc8ff0237057d54e137606bc7f845a71b858c7eefe3c8829a6ac6b8e4f1f58e29c1ae75be68b656765b899e7431bc533a0524b35f6f6a97732724077295f252d9fa8c206118bba49f474d1a6bd2794ea5df5488a50283735a881357564a07ee934dfbfce1d1b42a29e4cd47f32d3400922c113679381197b86fee9bc794b022c64c82aadf3d476a71971cc20d27cbddb9103b41aa3791c6d232ee02db31f34009b46659d6ee7e267e3764563cf3353aaceadd8b823aaaaac63a4c52041e033909ada7676a5b3941d0c5e0b1fe027b0411ab743bd79e9bfc3d88f7ecf61abd9458af6398bf09f8008873926984da88d0c785be49c10ae1df9c7d181dbeba8b6f46fb48e43924afb9fb985436b6186ac8b50b4d9f8d39ca73a78bb1c78d3346455f22d600e42d23cd222baa15eed22b03689b3d0309efaafc717274d6dd3ac5aa74ea90838902f7ee97a3d38106e4898adbbab313b84d2f5ce5d91127f533dfc75b4a1d8206ebb84f69e4c09a264e95230d40fd1c10d62771cfd983954581d7f58f3b74a463da1003fa70d2e1217886aaa4fcb1e7c6b55b1f5c922777328eeea8fadd6386fe65f459dbd721c67984e52644a16aaa3baef3bcc9f101b56e5c84186844109bcaa5724517893f377b28a9ce9cb0f121b0986c723c2c24e171f453c3852d0336d2f6f857cf87e917d6c920faad68ecdc99b82dae427d9774442c4771f394989e8345e0ea33b97dcc8539833da0ad9199cc979c4755f169659bc710b1fd93605b819d0d546f07e15fcfa750a5871e2e2ce71d9674bedc3d322e3fa21c1646daaba0ccbbcd2df5dde784521a96dd200e34400e3907ee9e39d9fa10b4893eb92f11df667bf18e47d92e17a7eb4492ec866f5049f968248d9e9ba697f77abc0f91e3d39127769ca5271736e9f683e422a027d998df4822c4e5d08d32536843270ef90b762c8cd4fd2b51c745dcba91fda233736599603c6e30ab9ec5ee2189d5ac8b988dcc66de423717a30a420ed0cd4fc78dbc0890a08da684cbe894a7a663554d96b9ebd688ac43521d9a3999aa20ce14506a7d99c28c9f37588297f803bb3b4b740030f99d77823d2d0bb1af527ab02fe35aa3fd9a17dc643a4ca8a28c6e2ea8a85f1986ef0fe2a5f23fafae08b5e54f93ee2ef7016128c0b9333b7b43e062548c222902b7fda8dc87adae41bb0708daf082b4c76e747e92283decbc40376bf48245edc5a30d6e5cc84a83125a155296fb91207630f88e46024194976443cb4add453427e6244ca80822bb9759240178000d132232a07cd256b1bf7af0921a8f5920a7e19ccfa1a3e3bcb75b0d67e98b88cd97f2c8b793a9940616c3c3f4b3656582353b362fb08178793f581113903024d37e4eb7ff4f89fd418d184bf8b6b6e45618f6523823d283ec12d19ff5725235c3f3c1fe3454c83336a34d70577c932245f3085108a7b56c413137b7b3fd0a933b917abed6bb2f72a6dcc49a5ce89e129c0d7abc6d3d1056a6a07d3113c16bb7847b9239d32fc16a117c9e9eef72503b76b5ff7449cb4f4b5a03dc59fb5465b833999279de7704aae65fa67eb8b4c9b9b95a70d8f63a625be00c7d37d19beb2c946cdd3c43cc9ed975709fce58ebbbf1ca65c5903984fe1aa5402cc712e589ba3183be66c71824f47b6be50750e5d815218ee1ced98a4c36be9d9bfb76460ce40aa7253e682146e1e32aff144ac833818e98a8bab69dcd492c3ff68dd760036675cc39f39f5d155aaf23cc2301d25f4017bdb8741c094e83a69ca5acf4ddd42247ee68aa279679e2972b16591805b44dea0b7d75d3b8501bca11197309cfebb90aa381ae3dd52a44b6fd94abdd19462a7654f6cdf5021102cf63a34f800a2210a78e71bbec56029dc2ddc1fbc10d27ff83575e6c1b76c7c4387d46d21ffed722bc6a81e1c70b2e559d9bdb255f68a99d7527aafb27b419e102f253bca064bb81cb2e3c8a394206de01b0dc4ea2874cabedd87ea77afd86219858190e980bfdbf231458bdb37ea2332588ca628f312a13b295a042df84fe47560fc93a0fa8657f54f0d73dcd654d74f508130f82d011048d734d31883a737dc411cdf5f6867b0e250f6d75b1260dc01419ff5e8ea1e23183803223400f1a2219e6680a9dfb1f7b483d1667bedffd86b5afeea6326a311b921a58628c9cc45bd32137f9c889abe0990f1d31da5709343a6a9a229ec87f10f4dd440af4baf48dc830dd8226a7f0d3ebb873c6cdd54bc39dd1d85f8634e452dbeea363606547a727727dffbbdd08e9785401d53f9911bdfcf0fdf82724da26ccd08aa97ca43c1d022366722523dd1c946f86ca473d1ff3afcf12f72c37d51197c0f3e9421bdb7ec88da166c20b490cb031f35243613e8dc588efcf4c6ea00feacf097eebc5573e2b4f670ac18399806ef375efce33f8a1a27c08cba9fd5e12ee614d3d0094a688772d6a7478f3b1e3d44206465ca1ae4908968c08260531bc9bb906c2818461040b7547b944c605d4bbacbb49a0ec10b7e9bfcbebaa9713f61fe244be4c01ea42a83dd0f1058c394e6ea4eedbc9594e5e4b2f1f1a3fa156a8448b4a04f844c87b5c5e82ff1800145aa0c7598315610046eaf426776b723788f0f3722fbfb30ac75757511a36262c98187b09b64324698187e22c55c9fa4b8ae8afb5f8e0717f50220f73032cbfcd1a3a27a996a3ccd83a7899223770440e189454cf7f4fca09194c8e916f526a9403204c138a9d5c485b3983d3072d61e95f5abe21ffb3349b8f5beff76da515a67eb26472ec1b8d1b502174abb650281419d33a84b25696000c7c4f669e3238532fdda31920cd334e62b410f2b99374efb6b7705e812b2af2e552f7e0339171b4031f2b5b113fdc3dae0ddbaf695ca68bda8d68cf1bbd432b55016a99bf6f5aa3c3dfd084e99c223e4c4c0f86424d1b1c1bc4c48ae565f640082e56e87f4eb126b11eef4f13e342cb8d514e3bbf610a0e68fb8ddb5793456154e2771a2a42651d35ddd2f3bbcb380a42274a162a2205686535fabd42505013ec11db280c5c7af3e048287ef28f55f03820d2af1c1f4e81bae524a3940833e0644ec04d82f51bdf89860de35be2ff48992b59ab531ffc20897ee47e57a449c0af34b2cb2a14b59c9ed646d3cdc7582f246d64e9e63ed68096d9d3265f1986c9618b6c519ee053d50810a71824d4c2a45320ca780fa8b0029b57de7c5d5d6e3378220347bc005518038f8dd876b41ea5ab36f70ff84e72c2cfef2efaeb2fdb9354c9d44626b59f78a24442ec55541a1d7f368f5c680032e1b6ae36eeb71dd45933bcb66be17b131c5e0f12cf9b271a7e33d16d0e562846654f2fc5e62146cc8944595663ce0301ebb19e1e1c5185aa8c9269296f103dc0bb2e748072180ae3fd19df21898cda76109e059317fddadd3aec190a947b17752d61b2d8bbdee6306689b8aedcfbbe86ec326e6adee2352383c49cadce56c377198aea8a49b0972e450905910e99ee71579dd40c21210e35bc74d593e1096bee36cdcf05e46a8c1eff2e67ff98d835f63da0d5871ac8cbfd3ce69f8b60272075c3645118c11a35663a2c59f0bc0ec15c58f9a2b8bc68fcd2be0a1f100bf687cd4abde48da17ba30eef33c0114397e7552b49d254f0f8d3f30649526adeb42a4b35394e705ad633e9eedac1a06c907c6b881532567283874488cd1fe6cd670a8baecde95d938e3b58177bd4e9a8ab305e44e4c856e03fac6f41d9aff5d8eb5e2936684fb93a6c1f1a468eae1878da4b890f89ca8ef450dc3e2fc514b18990dc865cd837594adf1268813999f37b3025a4aa47795fc0cf1854f077631ad5ae9f7c48379c300a395f71020f7a03d987092ed3b19c916d39ed68044306d043328169c50fee3ecc2e65798513640dc26b9f060311a563453d12f5f481ebfa83ab2714a6c54f6dcc399e516256e382b61629b185adcdba8126201d2340b73634f0b29348b4433020370f08d937a4b4ead6f35649b0bd0902b3a73adc9b7aa27f402a330903da95e03b898e221d5d0fdba3108ac38c75d9be439ffe906b409e6f6e5081b8982173825e510e86274e12bf89202f235f546f13ebb59f83d846e418d9060ef03f4b36365a5c4d37435f7c559ffa91f4a4ceee11f91a08e4590f928c75466592078e85347a96bbde727d1c91fa708a1b15e115b56efb58390170614f9614111981bf9a6bf336c2e8228a0c22c7f495e886f8f1c28a0f7b5413755a77c0eacfa9328ab53b8714ff5c249c6f6e9d5635537b1e0f93d1bc2919ed7c7901cb0c67e98ae00949c3411e88cd93ae20f7ccf35e856ee268ef9c743f0fe828f6999ea2fcb734bc0e4599c0cc5af46c6ddfa4b59e58cddd1e73105a8441c354a45511137b7aa42f7e8631914754270c58271ac99f0cb7301c6d452e859f92f0768547cce4ba6f2e82ce8977d8794d58b1f8571db8e0490ea90031426ad074a5d4cc814a1e4ab862254a89129425b9b764a853cf274ea180d26edfc36539fbd20bd11d963600afccbfb774e60b44424c3871313fb62b820510a0bae07f5d37950008ec3c9901257f2ea19b868580898988960c3df4d8c49d0043b3c863c57f761301ceaf3a6001d5402192004d9bddc086fdf974f2e5606c2012a4730b181f7d6eb6629f5a68bfcc1853ec962dd82449dff04da2325f2d46c37b1925c86e1d77c07485e7cfcc41710e7ca49e458b28704b28f951ea200761ec3188f51274cb3fd241cfa55224adacd95edb329e9d9150b26379520d5d907a7b958511b81b81da9a0922c3cf14817a2736e14d6eada46c1bd6a08b7663853bd0e4c0a3f5cb508ebbe2c0bdae641ccc4d76b5b4d6ee2b44ec7eaeb235185a35a0ff08f245d4c12d2d0e34a4800120bcf89dd853fed0fc6e2b46750b617419b8d07d12202082a4635ef9ac2de3bc80c6231b8b1b285f64cf413a47c25088a74ca7df0072efa9f6d5d619a9a23d75e9044feab6db14cf51559146881a2423e640d897f22c3809811c515bde6f810957a843c0e2d6ddc07a8a0ce1958c2220e230f56dc8bcf616c2383d919cece2c5b232b7893e8afb99deb7ec7101a927ddaba014d185c467259a82fee1070e064e2ebc75897249bbbff7a5f4908ed954ef4365788a74811a2251d0edab9a19db86e0b58c725fe574c130bef71e9c2f6d503e09be200e488cf2044516d4c62b51a0d894606d91576d7b8f72da2d4891f0e69fdea8b8f9046e4727b7a0ddf4b4d5bb1dc5815579bb70baed6616d44322629cba0e5b24182cc2f21a4c2ed183a7594d55896014fbaf3fd2cef131c23b58803091e9dfcc7f2ad248e444f69fef4cb3ad997539f327f819062a90ce495b72b94ca3b598bbba7d35a9a9b0293502b50b598fd45fe346387fb801a435d5f673561d7cb1af9c25cba7e06e4864341f81f1b635eedfed9821732f42a60e40b35deb7e51fa3d144d731d71ec81aeb77ea6df1e0430ff9f32809e4894d7ad671570b18a4126f5ba56f0d8fe1e89d42dbbf3237a265b0e1245da14f26a753645a6e85b047bf2e401780df4395efc8059110c102d9745472b68af04645571b626938b55c008d4370655a652204bed0080f93101e5f5ee2b77e80ac105f0d652d4263cb4d5df1b227251ebcc7c6e3c72ec85f9b3284d0397b2e2c9dafd52f5fc8d0fd362e3726c3c98eccb56962cd97e58fda6237717e0000a3b4e67906f7231946aa135ef0580bbb62fa894f5f1aecfa3e526f4eb9fd3fcd8e07e910c748b5f1e253a728416dd3b49587271e8ddae7182fc2958ced643a48525198e9ad64afb6a596fd133964e33a4b15c6298eea5c136db746b7ca628b147244c93ea3b53267001b9caf3c93c2301bee9f6ead6eb12557fdc68a918a5e21ae4a571357b82336d5059470aa0edd2f2e9c62598c399ae3a6f18e1ee8fc9fe348e87d81b18ea71a9e3a2cf244d67264066563bb46d39341fc312c9a2c960e846d98458c17f44d89fbee3c534324c2df6d46c2126667ad7dd8bb76194b3c62230d681901462e935eb9997f786a5800dc158c7ce41d0a68875841fb7c8126583778d4f789adeaa0a3350b9ac6d86491eb248486781564688a701445624b56ba6b69004b9c9e0c98e9dd44cad4ed11792b3fc2b847330adf82a4ac86f8a480d4d33d8af3e4b3ddc13ee3b95d1081e75e295760634a085f1180e5c2b30d09f2d5c2e8a0c408caea7edd07c4e16d11c527362467d587cbf3f3ce1b426a56b0d5c90dafbab34876b424387588d507a17009f3fd4f6f2dbe28f99240f0ba1d6be3cf6ed3fea416b29c0dbe6302c99d760b7fcc0511a12fcd035ca0dc8cd5fd0d6c182684bca149e339202a42a6d6a0743704d6f19f35be458326f4d11eb3e41a32871e78069b731940f89380721c710a3caaee3aabbae826008a22dd5e4f0f2e86e33d005e3692cef9a8321cfc2316775613e635514400c9462180185bb4582d3e18f83b94a407662dc284d4dbcde698dc66e362edc8423b764cff518ab26564042e22310bf0882b558dd52af0fecf93205503db3664a266dd45a168d8ce792c6b4fc09819a350415461fb2e0db7c1e866fa1fd492100fc6027b25c7441b00583a2c2a2403f2917ac4a350cce001bfacf260c769cb13a44f842debfe6ddfd279ce59ffed17bcc0e0494af8483e2867300856bb1a88b68d6c60c8ce3c9425868b39f7339bbdff732fb666c4b135f89f4ba412a804fbe09324222641ef9cc17acd90353bd570e1470ee91d354e26cace88b0b21ff578af2501994097850ba27daa490277c4308cbe5248ecc256d3dde23d7ff020a866bd1a09b42b87a24b41a28c06bc9b57dde569a6eb88be32bcc8ed4074ef3ce477d30ad2eddf0a787c4b246f9581afebe67af67f6dd98e61fc904c9006748e083c2f7f7e4b24e49ecebe5a214464fc9a89d912d8c4f66ce0739501001726d4d612f31037030f6d6e5b92ef9022c7d17961d416d7dbb62da438fd8a0c18b90900a434f7fa88946857aadb29765229650675d70000a09cf40136abd9b373f6afe1ecb3ca9bb20f388d0d752b75dfd5a97649660343aef0c33c1e94ce135cca1612abd0f73d612247ab91ce62ae0a3f167c6d6ee6da1964c94af51e1729f21847e1cf949febebb8f830d984dc0bf69c2b0093b01eb48428e33c4d2c801457126e73ba91bd874cf6f1375b506af307a203da386c3e348ea12dc78f4fe42d072dde70eeb452e8f7b7f63076607f6fe407301f5cacd2e64bcf4e88dfa51b99db8a010ac9f896bb1fe020c919a34979a7e48de1a66536d13207c6140b315bfa78e04b837e7103fc7941ea3abefc983743d34102d6165d60bc6c13dbea4dfdef81015ab801e8ea0485fbbd08347e2354c3d304c0b8e55695dcc5240447b770347d999fba47d2f88f4a98f05b4004e3f7889c53de1ef353fa5b4a02154a1954de4a4d1fdb3f5b915b728034d60b7fada566e7b4e6e10b1ac6e38c523dfa4118ebbe28f291d1b152d5cddc9656cf9c04ebe8eeb7e570815fdd4f42f172347bd8f6dcc747611e2048f67e112089c697192790745d253f63aee89cf9b68a1ce5dddd54167fb3216f919daec5e4dd9effd404a035038f45dc2453e8191cc94acd9ed29eaafb6229dfae5bc398f12433f8a88a09f45a4929a66fe4fffe9fb884945dcedb8392228d62365fda8ec25bcd8d7052c2455b9d471846b316e4e31407ebf682df2c52f850a333bd208ae510e0b7c79a1fd5ded55ff27559b38016da3fd49fe0d72371651f8805797079ef512e69735fedf85d97e87d1f2973c5b3fd1303b0d5ca6047aff7b1be68605c228bf86c369ffeb262420432d57494952bb225cf4e468f7aa72a85486dc4755a78995bc0706ff94df9466aec0951940c259edacc1a7c3134697c04f6bf025258765353bd0d736dee0020b1caf50969d3354e5cbf18d3fbc3e88212daa77fa507ed65f3df9c09361670de7e00c4748c6dc982b29853d1c1723abf3e932fcd9a467ac32cdac7228820358e81717144ea93c1e0bc7630e856270b83601dbc558ecbd56b6d002e7aca95381d106ca696ab2d2e95d4fafbcfb2475a67dda02d0507dda66e2c8816c100a8eb35033b2690c19df9d4272e72b1ca0f96cebbbcbcd99fe0eb0bef373b31a43c5288e19f3e9b01e21c1bf8ab9b126ea21fa5534416cf693c628a8697d7d1dc7dad7f866cdd4f0513ed9a12260f6a3cb167c3dff0340776f181ffb71fa6db5833f0a39c00a359e6530f1e68daa2b620e233040d00c457207ac0cf36c9486ffec20bc2610e66880aef46d1c75fc8e07a55207262374d0750025741847075ce1d8c78939459c98b223e27481460bfeacdb450a316e43e1a25c89100eb4fdc767d8b91cae516bc03e1053f9ba8e698a440f7aa4ddcecaf381ba46dbb572ecb1fdb2bbf08b55c118a6d24dba5af399778945daf0cfde248bac0440f92cd8ea5e5683b613526cc55fa9483720eb669b59d1dfab241d129dd4bfd341978fbeb5598aa2b9547bcc29460383c222edcbced03ddbb324dcecddda98bc55b1faa7b0a6f9760d80618b60a7e85d3aeb30e28ea1ca9cc2c78ec2299ee0d49904a8e8389f108a5f0872b9fd30638d94bd4a1f2e8cb545bd0f4777dc4c44860844da1f983337bc7cea3679e9277d9c171174b7043fa2d2c2d83635657dd0ff1446382742d3c78e39968065f8b5f9b3be3e0141749e9427d9b20cd797c3bc0844157dc00605967ad5a7ef8448d07a769f1a1ad4f6ab53f1cd9fe693151a109602b22754b7b69c33dbeee9ce2cad705623c14e9d4b8fbda8c3828f65c9539be4ec21d2554e7613a9a3737f3dc24bf0ab25572149af469ca7bbfa1cee3b974433a61b2621b6dcb1c3943ff6b3acc1a7ff9c81e8ea3ced069939d4e42ccc13c77574689e28a223ac4c5498627a05a7e7b8db5816e8f81a5f4652c9df5865049cf895a9aabfe943feb8075fb1dbff235daf2ecfd6232f0250a0f762e35c028bddfe2b62a92c72f10df20a123f11c8d03edfde687bf8213a1ed3f653ebf895e2ea961bc97f952035b1c7c9984425cff0c0ef99c515bfc27c97d55e6aa3571549daa0aac32509492f091f08a0233ad84ac4635032cc2685984d7ef4caeb0965683a945f216e3ba4e7fafda9aa3879902b5d9c9f08d6e75de03317330b5701da5d52be8ca23fd216a517a4943a255ada71035f99cf641ab0c5b127f7acef9f4ec7b01145bd04c7619d4381849dde276a7aa5d25010b8e75e355c309156606e7128335fb85694215f3a5c9b814b47b341091026171688de4fe1f92e6c0abcbac4c31f757e596c12a991656f93d8132c54b05fb7d42ec40132b928fa38f1326364760c8fef47744aa0e49b27c886075ddd02dfbd416623b08aee99831e93958b730a60df6cec854d5ffba4a63d09bb451569082fe8e574102ed2b1fac505ccac75a10f40a41ad403cba0843ac4bd2f8af23e6563d82fd91b79c357e758a25ae4ba8cce1c34d4c8317bb333d497876065a9e1fb815e37ce01c78862500772f3445cb4a4aee20f3ccc3f80a260f68c7b40f41bf9c4ca83df215a6de73f598767de790ff9988a4fbe89a0346732c40346218ad564c1e4448e0b5549f68c13c4efd01317cae6db92f318c4f7f815ced1747928b1c057827f5719fc6dbddee2483643ae47e02d058968fb9cf13077bf2278699c2ab108cab7e497c20c07e231c2ef36d0b4e897e374a6af736bd24995d1a5f10b67b229028b59a274d43b3800f22ef0cabe9fec5ddaa540c2d6331de77fd8ce9a1917cdb3f22447aa493a1844f629244348431cfa8e7a6e16224716ac44529fdd37ea0189d225f489e66ac969d4eccfa555ffad31220d5cde85a920a2ec86df8f4083344fa66dd2ca2e6fc64e800d89939d71c2d51df9c1103bdbca57372f64646c69c0dd639ca10c1017a6ab32c51999665cc0edd3ee56751915b00212a89955839de07ce0133260e4f4977dbe553aba8056d457b3a25f302e34702f8b58173811d4b74a619a57525364e56fe170d8ac47853aa99eaa3767c9f5bdeb572d4348bd6b4bde996ba5e9829b9862aa36c942aa2b85f5aae8f543a47cb7228bae2955ef2ec3e22e49892d6e3f5a09b0730c9e76b6ff85669aa30f913edcb422eab3d63122af68586d28dc9ee47a383f0117a1210714505864888d26fc8e8637c6715f4bec837e4b15f591887c459017fcaf5d3fd70158966233a4860ff381cdf1578a23a630953e250b65b22922e05b7c033a48d3b6d68ebaa6a9de9dd40272551632ea2afa9a060c1be94ce1e369d7d849d9d10415d7177be15603a682dc4c01da9cddb5f092982c6e7ff45ff7b5d0df1d1c13fabf2f6809d31b0a2892148c5c3fc92f9871517a4ca50b80b5927156570c9116d15c52f5de875724c5a69d00b1876120e9cfdd48d88ec930783e7d7e0180be2ab3de286c1db015e551ad08654facf8334c0d9c902aac38f0a1a66d8f38e8314a0355d6ff5342740402d00d53e8453f860f56cd0ef9373c55990420d921d5364c812a6419e44fe9ffa1daf0f063c16fc862592ddf83ffd773c00ed0ce83d942310fe5f18544f01bbbc6d5e159afe8c67bc4f5b5be595335464c2cc41c47c454d3b944f5950f6948ce0a6c86f8a9eb2b408c851e732f6da36a79851605c3ec8bd96039c046eee2d0ef836953752522295a936e659e521d73196d8c76b2fe4d3c939cd86e47e1235756a5c3449e7596597f978f3c99c3fdd6b6defa5616c9077e96264433405aa6ab4f9a2429e28876fb40420355e22c5a54c38c34c6c783cf4f0f5e8eae2d3be34ab0a2f98343c02d31b361c0efc990e847a028e0e7b7f4bd92f6fc7dddf79fe5ed80a18d685030c9d05a111793b02a6e9ef9b2e4a87c7764c39c5f326bb8feddc69e92bc90856f73305e2c9ef399d8332937d32022e0a946188c8c849b8a7975159ff93ea4b89cc7338ae2c7a7f3ed0b8d4a884b8c8795fb760d959ba3d1717998d50e1f95c021a178035940c33f71097b7d0d41bb5c8724570c45998b345bcbfca2623c71997b3663cbd03e5dae25e417ce42b56a0594712f3febf578e468ce6016abcb4b74fb3f38ac9061ec1397b465e33507b24a8b33f761e2a92c42c7298046f07b079a7afdc5da77d702970dd5c3efb805b437dd05715b64f52ddbbeefabb356d271f8b4c3db39d9614ceaf356f676443396001df22c4ff67a7fb227dd6a37edf7576c0c63a405c605aed2713de65dedc52c9246312c5bc850c0b06b480a62db872f1dff31df676b550c5fca5ff0bec1604a8c9c0d5551ca6ba52df275eb97a5c792d8cda278383a766f9ba57d1bc5db05bff767e9a9505d7dda820f76f2d8691ebd0cba10d0b5b6b76dc7a8e1ab682a35a89c3d31d235dfb4d1c00e9a25505a61b8afb4c758fd6d2fdcd9ea03e6079a8e4a0f9e0a071ef4bb11e52515d433641f087c90dbde3022a93928baed41b0735c5d5ea9bb4012dd393c5a49da8ff0c462eaef9f8bf587fbae1b652604b0bf692f59b0f3fb6887f7e2c24bc1f740f7890860c1ee4a93de2c2ba2178c6e21bfb760f20f43cc2ce331f17e62f8ccb27d3ea084125a163c3b24fbf19ff9c1bde3f73b7b57df88ac66c2c71b431fdcf0d9b43fc24950ca0dd8664fc9280a942c9e9d9c12a281516b8279eb786386a62387ad898607a9afa9003e96fc7328f7e2f9f1267ca1f727d463e4f26f5e327aa8e6b836bf488994cc6e47d026e9f4322c96e472058a146939bd4f5214dc1671c17c16586260d8909f30a9f7650dd055e9a1d6955e3a8206e9b997cb08096b9937b68974ea31cbd3678ff27f8d056c9715ac5c58cd0ed445a2826488debbb198f8245870f7f122c11dad097068c21cd278ac01a47e774c86fe4f8f2020b4dd5c1af527f5d73e57eb72fde5f4a4a4717e3356866578d0239d8272a1dff14dbf32c25515186c7f13c813ac616de048fcbaa25620c524dc47deccdebbde09159df2d8892991812351034bfecdcdcf948f984a19203d22003798fcd19934f7b98d7c6741959da44e0ef3be5c854de85a40008d0b1c743857f00681998d432839a69fd6052fe312c12762905eae0476a3794c278c222ea47baabbe0802f50b9b7fe4da2b96866806a3cd52f2867d150f2d89b2ade2b9bd8de55f02511a7c7a9260637213a08f492e0b3b607897e1dc724e584fac365895cfb4bf1009f88bf9bc0529e1bea327acd81039ddd5f46084e0f92c6ca7997b8584761e62979905d53a75ffb0a515016c8dba320dad8af8fd1fce116b87fb6103a2a7cd435b3f727d9fed8d126d433348df60b69327725b38d67a2683f538c0e4c97914ecdbf733043192a45f6d38343d8ad5845cc5712019712d7a5c0799637a5d93b3c52ab3e45e100e19a96e06bf211d28f5fcdf4b02b06f6eac14496d2346bbd821aee6c8b164518a596d6ea4a76ca0cbf34136fb2eefca0740dc2fd1c0e5d8d9683b3be3d126c7b002ef975b87581abded67975e87040d58d0a3be1f99949dd8338cdb5528d2cc12f4d228b7013c7162053db662a702093e82385a8331dfe9cb4829093676e93600eb099d03d82d51a56b699ba87e1a0adf438f212b44b9112e49326582bd78c360ebb23b0f9498704d8492760b35c32b5492ae22ad0005d66d4150eb60664eb712d12132f8af4705b09534c96380cfd6bab525ab1703bc46c3768de739062c6fe63fa7fb2cea630936a42aa553a976acdad634ca685f34f380c38bb662a80a827c3de2c3961a77f929242e72492274fab8873c1e446bf6ac616ddc9f76449cd486221c2af91f1772a8676df37f10832e51d80b7d0d3d6f4fcca1841ef6e16640b70759070156a33ab989fa1d68d47e5cb6d8736f5d0f73a8b1016a4c14169f650cfc253dc247ef4cb6838667e4f4b4fc4a540c09f9ee130cf3234e6550bc49fe1997883c66d4f71387aceac144cb0a1d6654fcc79a93aaf93f4736a384a0b4529e3e1a9da798537d382b27f978484d1813bfffec52c9bcef7438e8fbbf503d543e1b665ab3e86b41ec43305072206eb4ee5db95870b37e0bd675e66144d914c082cb5e99cc456ef9a9f140d48a1af78c469a0b8bfd2765e690b0e8dcf6b2d7a3db0ea3d01f5356deca49ada28a20790120c5cb727f8de66de057a4685d7778c2b34e6bd0286a745bb4191c5cf7ca5b800f2ca3c0405ce85f9a3044ea3ed0821434ef5740a6341e9687133615089dcf0b8bda1ef1508f1f789bda69743e1393532e8853c17f7c6c557dcc7c74d3006e9af13a7533f49153acf0279b974f0f8a580080cb61ff30001c0f713492fe6579f609fb848e9d1c5f862dc5ef59edc3786316ab698b1ababbbd46c4c49f83fa57e58feea606d7358bc28f48336c870014071d27ceb659fcfe91d7ea6631c39683884189c136635ee63f3026458238c7dd47f24c2bf2f6bb58e43777572f16938dbfea974145f2399db6fa640761faa7d60db9ed28fec122e0db74ac9bf410307aee5714502c329a4926f12af82b1283469339032c666e3be1545ad109a612b058624a78cf24d805bdce6dea5d0d4d60316a6a3bf80ccdea0659faeab7038b7106006c2de6fe3f8778c5c4cb6b9e13f2d778c58148f7bf93578cd540145d1194cfc962a52b3ea595011b7552c75404e54a0a6ca887ef3a1994d86245693312570600c8b988a6a993fe0034d59c98a4b3340fddc619710e6940884f4e06f469c2912c020873f883e85c3fec0f6085c9252bcb48ae0f791c218e541682ca09d59bff7661c5e67303f81f4fdb6d09b1e21b4702ce1831bcb503172285b3f9b467ee0731057cdc5e7b1c622b94e35772545730cc63fa1a9b58353b46364631a5f9aa089699b601e5d457075bfff46bbbb162ec1c222a23f95e704e31326858a671b1f6d5cd93b9e9507764f1e98facb987663ba544dccd762759370ab1dddb72c3062ab6066463ca2d15123c608af73853b46014c6b39f3b752215565a7364d38899a25790ed3e49a47bc8640aaa2723978ab2c846b2a614bf964a91bff52f032b5b0c9a44b22761214b72dcfa1246af1b7c70913e613a97777ce342bb8950e5e493b0158037c62413d43218e8aceba3db83ef224af2724823c486c27f9470f68115e5d677ff7172ae9f2af24df045826e0f9996d284daa7889aab381432ed6f88b48783730a7b0e0d4a77ac3e3b1dde044bf2bb76274b0d99c0178ab5f0acb00f03051a32e92f889f98a8699298692f8aebf3a04755d52e399a0c8584043776ec30d91ff6e6e64e4149a0e27fd9ccf5b6e0b175f629ca7ae9e76a3e075881fa9ab103faafe217284a44e8470c4a937daa9f66a90d3f3732c1c9411a21ed988ef595d3610851e489c6329928fbabeca39df6dce58b7426661532d42b4e564e7150105d6ee91bd1e18561527c20d1408e8747ec2280630ef565c9675d21ee0ae9a62ec72007fd606b3ce3557a8102650ac0d42f6d4cde5f2c5ea2aea909d5ab96b1215ddc26f32af4b301d240a3a6106de35d8074b033600e38cc4cbda2dc0c483fc377d2b8a27366eed47646205dc6a217bd6ba85308a04803e7b0798f4601fb0aeda827fdc1658733dc38baa1dee1c40a6aee7fa43216f3b2acf8434591c0a131783af728609183fb8759e285da75991edcbf2e0836fc147fcea20824f223b0a50e318ceb2c1d1b8cbabada0c2cfd6d15d27ff470a3866cd226b7833b805a4833f6e38aac114c40adf52386f4dd938216729ce9d7d7a68476c052204e5eb5b6839f63029bd3fb894ed923ab1dbbe9f2fcc889b7c853ba6eaa3136756a8c13c96d8c99adb0c181f260c10c841e565aae20300ee8c163150f473be8d1457abb9bca7bd56a6fffce8f5b0d55d8520862d657653c5f405a5418b88df85749eeef8c306641667f672d7c6f010e2786fdc33529ed6474623d3c3fc4de806c1f154bc7d5f761090bd9129a7592b707a8b918fb8fe271e2b5bd5ff3e0d630cffe2c9e94c4cb4b2806326c69c20760a422cae77c1deae7c54b45dd43571bd5b5ce03f662c5b05989bb3ab144dd4d8600f53a8f0239f4de13e835cfad604aaa2a9ef6effc08a3270e7116d36b39ff2e51d360690c0612e318ba065ab95c6f0d0f3bd2f98fb4eb663f68d055518a69a9bf73c459cf491b40b087a16c0fa78451e37ae937bb940f82b9df0cbbe1c568d1195f5731cdd972953b4484eee6e197065c95fde7209e3e3e5aef75193332a88c581b3ac5d7c3d0c3aa2792f5e2449f4668e32b2b220ff965261e1632bc89e3a5eaee41b594bece66df098b626bc00cfbf49fe2b0e81af755c5a7fcc9212dba4881a31767ea2c43049c33723ddd1b0bc5d58b35e4190d2d9ab3f8693a1e1a6074cb533eb5ab35c96f16c944775feb2ce144754c6d98506b2bc301e890a83758168e708258a2c2686f6bc66bf67b26d83eae92745d08d88056830214a11f88c569d35ed6a660e9a4c2d44edd3f2872f21ec4ff8d8f0de0c4fa6e5cced8f9d4f962f7fec38080c28399581ab84fbf712ec7dfc1963b1d8a246eb5ba4b6f0ae0eb6963a4a66270e18797603eab844eb94cb37ef837123b59aaea67bf09688f61f44e14fb56833a4b2259a5af914ca61f7cbaaab66fe810fcb97bbf092e41448661857b0a992dd930263ca23caeb82aa9d05e4565bd51f10e5b2824fa7e622a9c034c89bc6af1c0acb16eb7f783b9b06372dd715d9634267fdaa05bfbc9833bb984ed074d84e306ab5cbc9fa4aec792c7bab69f13761977ea90f7b31f7a386f6eaebc9883ad86faba4f3d915bf324affc391b0e1f67e213594a4ca259d1ee92a96e4d984fe4a6aed46e31085c327f663a150b456069d899357f8efd76750cc3775ebb4daab503ef1645f6e6f150f025280da97eccf58c4b4287a13925b86f37f4fc391957e42d04789efe224645b6c1fad34f0046eb0f64a03e9b097821094699a380ec956445867bc25b74f498d729cf66fe707900f5de70151cc31d38f62fc1fed42e1c021f418b3cb3eb836940a2479cc305a2fe001560530764c469a23ed6a89d89c6b0268c706b9147aa01cd330c84fda197d322cf938bae83d3769755ab3144039f59cc8a1567b33fef5f7fc020f14ff48e6484b9e972e4c523d778c714e1f5dd9e030d72e1ec22afe58b04b7f33a28bd39e45438bb63a9b3d050312a34c09eef51d228066e40317226518a91452d3a90b63a0f0e60ff49e5a08b54fb2a2423117f35c948f721faefa0040897570273c730ffcc365baae658c991de76b2f2a1d27f362c8f6f52c26d8d8c61219bc9eebaf643d9fffc589ea7f1305861b0720d355a432af30957f9d36523b1221cd2fb426e94d14f82073259f36ad424fbd424579ec20a38eeba68f8313d72e4a11ec813bc1cdfbe18725fbb71545bb4802e7c6a2f62700e1109f878ff87a6d96283dccdf615a0f1e6267b1a3ced268b840faa0435a4baa7a2b2dfcefe91a9c668d587b5e0828c81fc2fc9285bda90bc6441a6a45bc0dce3288b2c1e6c4854ed7b89fd8607a9ab089bb1d84a35915203cd56535dfdf425a2e1f64a9e8bcaf32630b5f216ca136656444dc3eb5df7b248d63a70fdf73bedd0dc475112e343813a8d049eb28772c1702986581e6527e429ce67591d6ea5cd0baaad702815448b8240a54b59ca49d8b69d54053783b7053008d074df7970b6a30032e09b20ca2264fda9cbad1be159c878218addcd697a048a2b3ab6a0766a8c971d7b636bc736525ff8586d4bd9057dd01f775dda29e4a8bfdc4f34181870df81994153b03e23c310cf4cb7d41aa32d5861343d8ab32a584f8e134f882141050a505cb786fc9f3e2fe56ac2b776ae453028dcfeb8c4f3c392131f599e0edb46a0dd069144a3ec031323931bac37c8c8bed84f649248d9fa4bb695001eee2b4dcdffb9041ffc40a9b61198ab830088e0e89ab026c0bda19a7cabcb8fa9bebb3dc496b97dc1bc805150e56a4a229785444b3412817b36e461518711d9778c599145abf3d6d9d7bc100c76079e263d55e26460deb9eeebcbd4d523710d925aaa8f98b8594e1a8f6d67b75deda36ecbf5818766e2ad99b4ef3f1d391b62c5389373abf06a7b9d12270c5a64cab4928149629c4d82e55edebe2da33720633024d971351ea0dc7f63facd3db4256569cbf89f83b1f1038f9e7c1a713451822fd52c25e6cd5a96f16ecc6fbc28caa60946ba209d9172a5b439284b3d957707b04dc748fe1ea519a8f6707732fefacdc55e97679a3a021861a177afc0d249fa4d2bc198bb54e17da150fa946e7d98ec55ac728f1eafe5073cf1b5621560eaed6d8b958e3980b335a1d176bc5614a470b6cf87e6e53de57a71be766a9ec119562c1f0458b76760bd29c50c50c28ac6ec4767e31b585fc19e48b807ddf325037ad1ecc375437a057806b58ab40a41b2c3bec17320f3a3d4be2363589924d6e2280c068e77b60c6c7d25c727fd9651e2bf413b87204a846586d56f3481454b7e3a132703d1517765445292f8f2238eca7b5d123d73afeac362aba791a7bb45e441e336d9170fc6ccaa087fd1d27c226983f873861b68836e5fbe115088f81f3d27109a014c4e13df9b9505a609b1a1937daa1861250d40286458d1d9cf4d5ea01d30d2497d46d27cc52d30ca3052ef6562977172da932fe3759611671c25387d2f568d2b02687c31b665abe4a0a6982a2c06fac6e361bc5a45427c84ef0e36a8c15bf2bad721ca0afa9f32a23363a8b2f133f914eae4bbd2c2c8432066c74a2a1c0052f799b516ba9e09d4f1e0f376be37427eeaafc6178172b4dbfce202a61db63fb4efbc264223d1d22c66309faea7212447fb4a36897c7a46134dbf2bf843a782eab96a37a2b56bbdb2044eb3bc0dd5e2dc2adce6cfde9f166275707d74ac40d378773a2bda0604578b98487a3dfffbc92d206121a4fa8ec7dc3733f534720ed897ef11aaf7731e4eb842ffd4f9830e8cfa21f51a9efd2a8040b6c7d2e362addb6437a74069a528ae6ea8cd43ec4a62de2c0b6917cc343439eb40dcb0e8320277fda44ce1897578212aa4196313719f990fe29d426936c52cbd8f5f5c8dd009dd7c530e237ef78216e8162d0dba75a2dd26866064ccec7911b308d00c283318192e46e449657af1d8bd0f08439b3671ac46badc731e4f086f3cb0c6a56609765b8d9da8ad00cc3e68bf1af5636ad6941f9190ac8ff1cc96a0000eb8abd5ad63949602b9e6bf490c8e84c971488b1b2fb6a07beb639cb1e648e5fa7d889f50e5ae0a6c440b871e1e60aaba596a2f055a00c775f779bc89878e9eb29f4a9a91c191a426154fe25780a06d1458845bd0c16b44e6434051119821abee230340ef5fb93fd9d9b9ac3064b40a69164499ae8da3d5b7982d6986b044ce6d29478d75b67b59432c2ee6fddbf9b82418df34026920dcd4c73beba72e26d16c887ac3369815e6e810f8a275424a107ec4f6bca411013c7f38681bb380da6e5857b80d1d41a88dc678a2d63a95aa5aeed55d5b2f3c6cf32549c53d0762ec993c1316b7eac9739055d7c360c18d4474c2c06a98f273bd95d5909324293f07369f30312f51443e34d11e30912dddbe516512bb77a84a08d8f3bb2a452e44456cab60d50fd069006a8cebc92efbed3c4200c0c2736686d6459a283f389ccaaefc0202d7b36b0aaea4457aa3ea19c0ab98201008f7b0cc277f34ad8735cf0ff9d0793b8421787b7bc76339a9fedb63edd002db08e8ee1f2a3636ab569a97f9565e8c4b35765b87934619d6ee749b4743b108c5241aa82642b4250b7386941cda118a34ac5e08ea80182842519adcd29b0407651ee8c3a2f2911880fa32dc915ea9472ed1716293a94d0e672ed8309edab30da3654e0928f19e3effed85766a890d358647be4646f40e19d2c56ea98aafd5a77faaa4155a68988fc37c1a8ba733344a18509d389cdcfaeda4a69083e9393cabb7cfd2925f3e3e3d1a177830f951ab0f1a8ba2e7ab4ce2b030df515f0919cf29a935c42362f8428cd82d68c6f73d97b3ebec2347cd3f66e6a41f814804f89bb1f81e282c3a6497f1f0244ced22383aab588528c827ca7d74fe7ced6591142e68006456e08680afaef6af8d1fabf8a5aa29580c04289ece3767258c2aca7b5470ab2af859712fece38d68bc59187e854f2b703bff3f1723ad00ed17ba74a7252126a8f062fe985f19239f8fab986a1f6bd0c957ac2da66c5c60a0209a0c16a4007a3aa300b714a1d1c6b8f12c2cabbaf3d5706f5aa1148711bf0e8055e0dc174146d372e1bfb6a229ce9ab0903ad05d0b9baa1103a5613c689718f18a46b81aa3653b726ed511711dde9f51dfa1073012e75442ae1144df4f43572d282b791af54ed1f7b58285f270d20a2f9d134ec749ac9f30e1abd5e5382580d82c974d7797fcd2b408743aa3d6003b0e5f05e681a2c9eea183a55523440307e112635e606c06134e8dc769baaa48b32eb73b4bdc914b6613b47d80826b477ebe5fceb340d772295b93f08b46fb94f354e2298a66823d56c5a72df8323343e7b96b41be075cbae8b6f2b8a402c26d1f618a32f0dc9b654d5bcadeac519a3ac4de23b7e89a9cf92e7e5a1e7eb625e19baf0eef1ff7c9cb5b8d137930bc4de9b773d5f2de2f3b92d819457c7ca1accc79fc2d0396826061549b3ffdb5bf2ddc93cb497e1b0370527e62b0e3d9ccea4014a362d74d2571d8a6d094123ff7b04459b796c1b1548654c02792b88f3ae50c0c2df5c226584e640a6a0df1b4b9bd1c4aca937b600267d62b5b255264850cc966bbe1e6213fafd47a9767c476de260b9115ae57de82b921e72a1a914529a5b1944831dd6ddc8bcd83db00354af0e0ef61b32e73875b3e7cfdf1db0bd4db7b609b842720d7274e2f772a17ce44a358bf7c02d2799d3aac462028734bb9c83ed44614d1d3d61f9b36c25ac6d121d85511442bebc458a9f781992bf4b52e35dbcae93b06b6d2cd506d284a5fc8f2e23d9aa553020c8764f2418cf9362a55ef9e8297c9d2c60f67557ae286ca2cead33dc1aada41852ca2b61650e9535ea29c4e25c4e1422901b12842bea25d730c683d0204feb9828cab7d5b6891c96096170f6fc9664502135456b7492f1b2154d10190014d5fd3ff8087b441b5c061b7370d40c495e35feb317971b8663f8373cc85de120d820414050ba7ceeab26735a8042c50a3abee020163675c1713922b5bf824fa5c4cc762ef0477a49b7ab2ccaa2f82ae68de12082153687bd5e3995851e1e95b297c1aa059e39e60c6531e0aa9f12bac35796ac90b56189a72119c1327ad4595695c30875e346fb5206193f6324d1b097d408e069519aeb4266f2026a036b437f3ad3937b3cf1d6617759288b0bcaf31d1f57303e5238ab079b9187a440f4687b70c3ba505df30308f9e9f4412d027180766458092d03e8c48ef9a8f4ea5623f7074ef2baaa6e4b829d2cf3931e9345decfb6a07e7cbccb7c769d16b5c18d15d97b0946c4bdc41e6491c698348683e3ab6c84b5aeebf51067b35aa67acd3deccf6b2688a1dbf1bed15c9418e559ed4057248fcf1099bb11f333808f9bbf88aec97a32474ac17c7573ac9dc1b8dde113e2c68239d5627d55abb511c63b4e4002e41d0246b2b56a74dbedb2c4870d68f1a83ab05a3762b8b0361261ede88e256f0d03f733848bbb3646ff443a4da7422813898b1db10fa73ff3a972a37f921660dea2e228cd4a645491bd90c9ad9b8d669fb804c6c1e3a94bd258a8b571624165dfc44bd87a6622d8e5eca56bcaea71dda525f0cf007beb78b1c698583d154c8f495eda7c7095ee0b751bc91b2792ae8d59712770377e04790ce791747425d1bc4b8e60b9104339f50575a2ae5118511c0e6011aa050bc5d1731ab849f9ab0d2616388a90999fa4b1f14b7d47263ece9fd25911d9e31140a9abb23b3db4baa83628699e83d79996ee29fc5b40773835d475c298e6050b82aaf5cdc045b38d44c48e29a24d84ed4b96d9f55a4692a71410e89d3854474879a2bfc20d2344fa47a1dd47295f24698a4f7db465950d096562ae73f82a1f34b285063e121ca27c1e63c83f41f3ae7b2aaf1211a98083d117dd42e2c5ba7b34f6de809fa3e79abb35452f8252cd83f2889fb3330505bf22939d40360a0c5ac8cf0f6602a8df576b55ed3c4ffe60ee54b5cb8969e3cf8b9f7a8ceedc91bc632fccf19fb339b42c974bbddd6782e24f5ffa72fb9d29bde0b5dc3bf56af8896782df8ee99877b6d7ea0e9fbafa8181db35b3dc42d6be2fdc681983698ff10c4ff46d895e29ef000397e823b4f0a005f9fa242a49bbd0aae74532de36b1256ed519dd58c046d09c8d37b4c78120677427ef594734b454657ad410e038bf67ea4b5726e2785c28f4ce5c258b5b289f9ab9eb58df0a905588ec868fe49faa0b41817c04dca81f9278237c218802136f842f0c416540c104def7a619b508331d6024a75ca279a7322a18dd2964d866295be75601ee8710590e9763b469d87fd0991631bfdb9c191c3dc5939306bda24208647fa384dd284ccb3634c8f8ab66dededbf3fdc5de98b701e5bcea1f97128666d866c78a2cada14a1ee0cb528306bba57a840102f0d9766e79c1253a80aa1e59716cb26152c68fd5b1846a537bb2063438adfe2aeb66b6648b80f77a4d14f1b68e33a451de1bf67d97a47144b90552380480a2c5b4c682b460d286c4fea80b6b00b0e12706d9bb8a3513b511e9be71f4634b0d21a3ecc90df9f1767aa495481a71ef8fae908fce2ecc43a1456ab8461bff14214143707b3de2e79dc28b5f355063a5ce68fe034ddaedb3c7eefe6e2a328f8571599145324e0222e66220e62cd174485a7aedd3db5e931acd7c24e21cd168b3ffc64ce9289f75726c37ffffbba00f896bd3a644f45f3ca09aa9492855987461cb3dfaf7fc97c3cb92f11c807e2e985f96972ac1563aa9220de9e0c6fed1bbc3dd27abdf9c6c6b34889244afbb93cba11e12c3890cda75e0fc3aa52bd4131900973fa0d8d96fbc7b8b5e21dce918857c8c9f9db35265431ce1b04f694c68da7a224af94d5c35e6a5c1349570eb8532a902da7fbb99f34d9e9ac980ce765aa1cd52917f8d5dd8a4f3ada09badfe433b13ef6788703abd68481ef5a6351a853e56ecdcb0d7f7d2741dd50e3f4ab88293a0e8b5598d0fdd63ac45fbdfcd75591f163aac212287582fd470843cec4ec8ea8b0f97259698ea9ede799151d56d2c129440871899d09f25749cb3340189d0d655fbb6b659377e0486a5e00c8f2636bdca1201d624ab11b009e3a89133ccbd99d7142c73e266c3e89724d6c2a0743cad396c948c42f1eef48086277f130a7d72019f76bc40237aab0f4b7e6854f6b5d0d4aba3c6f97ec1e099de84a0ef803bc4e2534e9586242efe3a4a6d24fd93096c9e7a844963a1d1404d6ee6b09a20883fc79a991db9b493e97e410e2492709de656273b3bcbb3bf7601e1a7e363fe288540e0b3bffe52c0b39ba58293c810d932bba503ecbfa3e3c98943b7c65f9b0891909fcb5ce292b23f41cb26b66d1d4fbc9b6aa8753f631d48a72cac221257d6a08bf403aea5b09df43e06f39a5139a43082531c873dcf77038d744416f636ce3aa2442eac1844d2ffe10b545d2b77b4b0d906c58b4c4cdd24e5fceea5ee26d143032eef562d3e16eb778117fcf24c0f3e5b42362af5938be381d4ce05ce86b8dd20e861ff8578e8864d2f471484f5cebf37329d49f0295ff3b02cd17e2e7f21bfe89d4588d52f47fd7975eee5a8f41735bc3242a039867879ffb818f03365c4d9fa9e8357db553df3a8688b7642e1f0f7d673e90a2859944c59a66d904110ad7c19d4fa19a27385c00baede75a6973ad20931d90f611208324c13e213f16f97c8693aa34b68d4a49f6be4ef7f11d7764ce4554f9f5abe24309e3bf1e2928c6548b573a1eb4483cc44d2b92771d74cf3ab906a5d3d90a395893de3bce8e8a33c93d4e0c2a654630ef28d121c77d6de5d04082f767e02ab0bbefb7899cab93d5b3ac8117009a971c62ca238fc08d0bd6f337eaa495eae4afc5b36814f94da3883dcb6630449d73e951060be8d796fe838e297f7469cce5352cb6a04effcffe2e324e326f187118698bdaa11a034fc5387655804e7b4fc50133e67318bf190786019af2494e02cffce085652fa99e18ccf3b927c2fbf2fdd4850b463e3b0aa0c439f89544165f371e89adfe927112bffaa7d01f4625d221fc07525833ec401e22d06f5a33227392ba1e7307c329808a00d467e15c95970469f11a212c6cd8a36e30c0232e692409d3cdc85860469e46305f7566eccc7b92d44c1f6b4456be6202b24089abfdf2272f75b0f4268cdb18c03a51a22481cd829c9189eb420a4549f1a855c085d0525eb011858e4a372b242ef8e5f39387e7dc529b0572233cbfd1528a08cf60d9759ce7f20cbe778a60e2b2a367606592d1f54e4195615c94bfd4429faddec68e0077f825b6a23f25d081171401c47d59f9ba1aea34320fa93b798f0085265279a73ab7d3394da5952b9ab1bddbde9cf35aef5579ef22d6030a493ffcb84dc7a93694edf862fa703a982767f702c361ace7e466b76ad3cc5cd8610848875551e9261b12dc99d02d63e826e68f39456a713ab5e8bb7ffdd3bcef8f358a56fc6a5ca4ceabc81fa52871c93769d31e633058d96e96d1acad8378ec83ef1743941c7d814e21c753458cbe4172b63f86c27fb0de6a41098bf919a4adc9aa2ce3f21ab7fef26339fe9347daee1dcc3b7d47a9790299023e487aab10d8e02cb4a8c53ef529d9934eb8967e6d77a33bf6efac2130cc62d1e532bfc1b41d5f2005be5ffe8e1036c664bd73c104b40d57d172ead4b6520deaf2bdb6ff5dba5d006a559abc422070ad4737f4f970580237634b68ebb903debeaabcb3397c67c4754671a99c3f3579145b81b506a4baa761a781597ed110b97e78eb3a9987cd93403ab4fed0c4fee7146e058d50524284d15453d5d5f1e6152968b7730c45c32f690b011b5fc409a1b817617af0981794e9171c93e293f3005db3dd72dbfd74f770395927bd9828a10bba26a88980034db73335cda3b302d8e3c5e75407c67c7c43df80a809ef51ee07db7711ad568bb32e5b374a375020f03ba5461d041baac20f31483837d05a4edbd5c5ffaaa26321b86dd2f6692797f7cad43aa1b18d38301680a0c36174a1a980f41d8871325ffbbb0541df7e8c61550ddf35d81c3d1684992a3473a5005df8c50db0ac9ef4fd0cae1939ed1d75708930bb098b518ab64cb8befe6faac889d19764abdb651cfa8938215b9e02e768120f35259c9cbdaea5b4f3656e662d1e202902b38629f02ab8bf472266ad73320b9f6f2a5eac226cc7f1f5ec0118f939f1b3acf9b4278452cb42b5ba8b0ab4350237233b40cb0074513c8e930cf1f1d7bc22a7b052f86d8617d2f35d54124cd6eacd5889f119540342feed2d09f62ea1013826968b6fdf46ba1c92a80eff6f0f1818a20370d02f62b662915a6adb9850389b150b22ce97b1e22768ab4d7db3268f930fe3ccfb1d34b3129963f300be678cad901ae21fa7cb99f9f1b91a92325fc18f91bd944137e4fc62214de3f8377e16e5150ba2f56ab1205be90102428a5386e8887355f3aa4a7e2864d288c056b336cb87e629c2c4f19cdec063e15d987f4b97b4864941aba20cfb64a6f03e379c713efde57cdbf6cc7caad6ce11966087621e682e22c6d5b8df369287d55b3900522368e7db70889b0b18b8cc4f7a87b560259e69cf969c509890f2be1879030bd1c2c925cbdf2f24cd1bc0c35c552da3b24bbe02bac1329881abc1cd7135ce200b4c01667b6017281d78496274bd9b4042c7069a34b27f4971d6779d9f610c65b4211ce832f7d4947def6715daf1b899cd8911b59303224eababd61788964b60f962705a638d26c83e73b26a90fd35395b95c4f9eb6def690a6f22e2a865a03e04e65164a58dbf8ecbf4ed962b1b045c73451b020fc7823dc4268d1ed861270cc7e8d0ce22058ecaccd96b13a127116a570af520008a1c66855f547ecbf6da1d6ac4ba327ff305961d41cc02b82340dcc6d2f198dfeb4ed998e1cc2914177adbe73f9d995fb96aeb0e03f2e86fac84ded4355642c83c45cf9af28557a1b274127f17dec6f42627e9fba8f44e621918de94aa76d6252312d636e2f585c765ace029efe0f8e3668189a74d8b6f10a8dbab4d9a62d81eddb111af568371cc3b3f3bb4b44aacbb4ddb0a3e9b6e77545726eb98cf5778681cf9c242edfec9eed37e66c58c6b885c6679af578e3069345727d8ac9b71bca6419990fe4e8af8896b3252a86dd3e474ed1bdf0668f73b54c85db13886edfa36cd14f2f57bce937b368d09375a24577d3760167a6cc1fb19bcfd18820213ce0e0bd89a6f2183d51839bb9dd1b7deae3e01c8e5d84ac6a7c7bceeb67d8bbbc67a425eb72cae6291e7f2d563d2b4e0d318887a71e67f93329f073ef82172c2dd24b1ab7100608633a3fa0573ad2d32974cd133c95d841a9fad651bf08b75e0c0699fad9a2767400365b77f1f1b39a420f07e8089710dfbaa14ea0f5ef08a112f841f80ac87f4b4be079a661e862b2630ef25e3868440f3c27019ade6ca46b5f8f36c131eff630d967ba84014916619a1ed5cadae250f9ede4202de4933799216c12fca17f77aed5186427731bd5589f75f49814d429c7db4dd4033c07ac977de8ce5c837f16e2869dacbda14a00aa2a96ab89c43792f3e255471ab030d98d7f2ad5aec5e765b9e35d6f1c1c109f52d2fd2864c61c89da2724f414519458125c1bca07ab929ea6a5cafbc4dfcc87bb1c659651a5a2909aa0a3970243fb58a461bdd82c8543978d9bcff132008968433c1508baa8affc05d0134f061966e0206f4d2c06eaaa595f8af87d445ac37128904338afe48bb8fc7716c1d5476b12d43d9206852a251a68b53a81baa28b56115352f25c1e7a7fcab9f9f72ddacece40f669a8b4c0ebdec05782ace22cc076125d07e581049f7b153b8dab3281105b283cdad25dbd6b27dddf02c92fe0577166e218ea6880638cc75a8c5882a139fc164bbc9ad212a0d5ba8d71246b9a9f62d678c858cdb525cb206ae7cf5603fd687c5aad19344ef181ca07f9ca6ba13ef91a132311823228d1861b477c0bf703e8d811248a210f45aa1a00944c56c7e407bd15609121881d2ff559ee19be6569e23c4d333849767fff36e8521ec7b8a275d89fa19d5bbedab98ad7d5601890a433dd101a10f3018321340c39afb85762932251c30f0f20baadb7c0d545f19d58aea3609921f60c12cff1cbc98b8044e4d34412a8e949e384de32996ea3cc2832fafa79e96fedfbf70b2c08130867f11380bb47f794c9f305eec75a5486168757c929f9c4e964ffe3c6b12cfe99dd53f5dcb35a84ee4110b2c2ef69b76943a8f18ce3fad2dd4e454a92bffe1d419f1887433105a49ef91040bb6a0397ca16d23d05347c58e9716135bd393cd990b8014b05e7703ac091d900a5db7f78869a5f481a1c52ef688d18ad8829134fe89cf46c4740e9199667995cae0867799b3ddce1644f0297439c59082a2fce0d091787f2e6cc3f275d732a31d4d9df213e65c2aca3557a9b6a56457a378c8b13b35203ccc583d263037519f566d053fdc159a52058b2e40863f2a9111d6092c0c465534b52a0a5b6cd9c4ac855230b0f26b7c78846bee87c00881f8b54cb1726dc08beec0bf82ea0f4a4bf398be3024b40299ca3da5706f6132e4f3a760e951f03c809ca3c0f87178770f122dbb57eb13ea0b53552bfdd76ff22c61720403cb9c7c45687f0daeb0422737f4a1bda05a498efddee2c47642d1e28a41d56e8a9e7a027c81f2caa2166bf217f5c8c20d4319b353b350a92aaa9c1b425815608d6906652175d1e89fa88ad934f0d4856bbda8645a174286a01f6457eedd415481a95af58cdada3bb01652a585d4e0d2e6be4b18a30006399aa641e212be93651f9db35b2bc7f4999fa8343cb45c21df2e6c43351d950f3cb16c034550ef895b498581630765fab9a8a458024721b1b74df6f4a55f051c1fc01ce19e394618623fee461a5dbf48789573308421452593b639b0d1f0200cc13e7461c31cb68bb1451e2de11cf5648b1e1440aa96990add281d1830ceaac377b64fa720a184af1b383fe1f77bb0e9fc4689111d508031524a6f50cde660bd36e92746c2af5b4e74f5864d4ad061056522d2e1055b23b96f0befdce189caa1d2afc1d5755cfa9d95f18a628592665af6744cc931d75e41fabb4a8643dbb15a9cd069bf49ebb7ae64b2c6354708c9962d93729ac9f8dd3696dd7281fcf4f832dd3021bd219631318d33a91b171744e26b30e2ca1df459483f4e8f299e39691a091a771ea4b35e66ef06cf5222bf790fa0f8a533926c094065d789bc1622c78a60e3b7b4bbeeb9ad577e671cbbe0474cac8c1e3b37cf511ad616942895754249c24099453cc805b0d5bec06696e082e1cc4f91b4341235899d5550bb9ea6d19a16ea0ade3a465a1d86162362270cc89f15a3144f8e1892e95bc2153800ace6f54ae8889c3c6c0ba231b3edb1e65d1839e89cc16f25a65d5f63ff28a789e9e623755ebef1e10c6d4658d4501b388b60293f687549fa4bd864a5f6ca42276de88e144b51e5569a95848e81ebda69e26b50390898a4c169562a8331687d9cc318b209ed58c16011930674411074d3866640cbabe8b2ed979da4c0f18e776ff054620acb1cf70b890ab483834b92b730be04a7a5a51697b4c0711722b35dfc6ca5d0a75021516da1281f9584fe0561dd0ea61b4ce2f124f6e8d7eb72fc51222289ac095fc0d8831b66a6456728d66fac2e39f29acc3b27aa214744a2eba89a0c49346a24577b740f34a0d296e4c5b177ff38eb742ef8ed76f47bb0877e1069a37e14ca73ae65f259b3609462914605251e19020f4072fbd81ff3d1329bc3c81d7345a02c9cf8e9689e0089b8c3a74b0e94c98f92c0c3c25ba8c5d423ae482dcbb0e6363c75843a4b8e6090130f2fb1abb075da3ce327c91b6f68d021726c83ce78561129612b57fd2aa4d4c278e66084a55a23e06d070b5d65283eb133dedbf1c71e186bfe5b08b944405a501a4d5958a7eeb27d11906f0db2f25575d725a0ec06b39bbf85d8ae8538b6cf563072134ea9200288409562970ac36ba1d3454f5f980d731f8d087d9b6e67e6884519b436217cbdc5972cb8af133c3846d98647d14fdfc4e6ed456d9256cd7143e07887b166dd538b20ad4806d751eeffd7da7e277f31e0e0223474aad525f2efadc9ab8909104872edcc575406543e111f96a9e8cd3202e59ca031373e787639a0d03c511d2a123e77c24e1c7ae1440205a472a524582200874a17ec3d5428f3a8ee4f04ef23072bc6e635a89b8d2715b69f482d9e53d608f5ec1b86a05600ea740d240ceb085850ed2992383d83f4432491c82c92aa465418d19cbb4ad7a13e67c299a072129f470b4118925ed5e1cd915862b9c22a25147bab781243608fc804507e42321b70f906db8b4d2df42b7e84ad796905478360a32d5685abf177fb3b75fedd93ae97f8f3bd3fd7907ece0821d68ba187427bb61e8ab54e2f9992e7b91f0852566341c27bf01b73052ef5b7d71fa14ca703907b6f353bd073fc9aaa73f652a6aa570187479a96e8c9b1ceeb99b08ea55a90697bb5ab1cd2270c2e9645a4846cc97f26cd9ca6544d9ef4349c4b8890b36b091ceb3f4c7f99428b0390f5e2d376501a1ebf234deeb5c1d60ccb79d531e45e76c5be11a91bc1ee85fd2cca54f1572a9353343422b34774213bf22f916502943e4042e119d2c2ef0dcb92f34ceb239c1ab79231964744b7798e7426f754af0d319a3f4e7e184466ec101275754065343c97e0b0a4cf76b0cf4513f7ffb9b2565468e0c94211025c0109ea7411825fe9e002c88d72c0231173fe6723e279df1de976214418d3c2fe14f9db51fa29830d670906ab40c30d642b226ee9f60fa069961b14059a050200def4c0dcdb3577ac48bfb00092f9264b0ba1ca71946a901beb7a7a7bcc0545453bc4940896c7ec34c020287096fa42f5d7e39431d908bfa8955f55e12d3a72b64fecb68ebd6197c7b63a751f228bdbb72a2648bfc4722545354d9c0460d77210340167941474d82b5932264217055b6b73b881ff7b1c11446644090f264a6b4a96c243db161d283cce45b9e600cc7ba8c05db4353d9c7b64396bf2e071ecc84b0786538f985dfa85fa78623027559ba6a3372c9c1868c28d6aa17fe2c1d0ffb39acfd566cbd202421c9b154cc55a2ac963fb53897f9d333903b7249476e838a500bf6d6aec65ca4c15fa3f9be688d609daac251a62c7a1316425e2c6bbb483cc83d246e1613361f4e5881d8d1110ae054a4a4a5792c40f82d5cb20a9e04e130a8d88ab1970a903f1ee0ecbaea469aa66e81e584aba9b67be7a40db1dbc42ccdd2314d93f7f2583ff3fed118a610edd23d3640c983e771a0fba5366c50ad051a26cf2132e4e4960808cdc5efe1a21fef16c266fb00bbf24e272c7cbef8c42349f3972dc85833e2a746111669bd8c8e5b5170b358f7daf8710bf13a81b3d2bf1ed5d0e3a053e9760497292577a19535ba174c861ee88b7392b59cfb6baf29198c0122b1f02977d655642f689009b74934b0b9830d292edbfd0f99a341f3bb52c0927933911beed2e64b5a5796f721ef99008883119e30bb52d608560417bd88fefc6cb586f2440a7ba6880f494f7c2ee1a841a08f4849ce66d27c114861ba430e4d47e8cc230f6fb34037a0b8d2e015d439f9e64de2901f9345d2f7a6b810590779b449eba70fa8fa888949a552289fe67c1bfdc6ca2fd0fba9b07b0d25a322f97bcaec729237c01f79d5657b8ec8568d34b8cce917d1217bf87af10896f1861451953c7949db2c4880a84c84afab0f61968ea42bfaffae30d09f4e1c387965a21370cd74d3f3dba26ee9e76037dfbf12dcfe92715eff808603e2479d963a0cf2c0ae1d3b8a4de1d289c23800a46748cad90b87db5d25ae5e45c9f41922c6e4dd025d8e59790e46575b31501891f1ba9d52a8bb0c5ff62e910040344104860f2ccc7023505c91c2f7b6e0c02f649b6e49abe99f768125d9c008528dd06256d7463c2139340c13cc33331b1f974bed10b06bd4231ca335b14108c96b02557d4952b7967699892e30ee4f06de485dda2f84e9e31e6d5d0bb124cb69bf3c5888f1400e45984d8c19966f611169f1d1916ab02e20c11e497eeb3b3e82abbc6f7145a345b1bf53c07a9da070a9e6e18286497557866e5956ba6263a6b15d9e6ac0669efcee4c52be8514e889e4dddf32c71e0f469f3ac0e8998ae0f4aa9c9014ae0cb2226e9c4b678897af5a0946674917541c042bded71004dcf034e965d3e03c2e7cd1dbb2cf988173d1ad814b806d31f5c1a40ff00c28786796ed2e2d0356331a884bba3f01b83347684b7eb483113426c0976c84d179839f47163b9a4b003d10d64e44f4eb15c34dde2e69f9553fa39d1d655e25d33082cb9983fa91c6dee441bc8182de776925cb6257fbc560c79f1fe8f6ea5f75e0e54e6c19a0173c61d4d564ae11ec229fc420cf1270bd5823be97f272c31ee9f65506b1a03eb655ba4e83254c648c3d53e00db9f963732c09104d5886e89bd3fc5f01f797730aac405adeffbd14a1fc14ba6040480e30f433409582d3b047476a47fe76c1fc20b92efd06a150187c711c9c6bf29b0687f78ad662b3156b74cfe648ca279de65bd9870bf5f35771a354499b3cb51385639e3d4c44d4a69231703cffa9bb1e19d889cc0718ecd52be503a6d5d5c948d23f6280909d56eef9a59178d2aa36abd093660c7964e5463911e8bb363975f54421d911b7a7276ca0f6238ae955712eb65ff003f6556f129d6146967ac25a9768761bd31526ed8f2cf435219458fc54cb2e3376ab14e33b719e70f7faa7518de98bd294da529be1775fc7ee8e57d0834353a30fd052ae36f91c3beaa7a03a4c42a883841ddea93c85de5bd14b72af5a089468e1da8aecde70fc97df0835462d1d9251c60122995437d613459722601d6cfd2609edacf6086f98ff7513ba940f213ac2e3f3c09041b9ccfc182bab075845b68e25c7c4f9ebab2eaadaa3988e1793b5af9379847e5a7f6526efaea48ecb609459a770ac3418c60ebb463ef06c510110efefe92a291d927b05499899f70b01a9efdb2eaa2b0d08cbde47af0b3c863a2ce7a4b5c7fedbd1017ad30ca2c42bd09770d5228a145d298198711f68801f7d8cd7ec8afcac83c6d223b8513cea754ec1df19497d2227a1d9184c4989a114a2394663ab4c78b24bd97cbde9cf912b3ccb168e07eb0dfcbc83805f8b3fccf627b459cd4878908167ef9b288b1cba52cf60300bc4a234ab776b978ef5d7163851f5ee7baf9f697ee5e03189ea3107df87c2b8c821c4b3264dbdf3b29a0d836f165b6e5767aa628109a30dc04409cb1e798cd4c7aec8b82059e8d903b5d062536525fa6156d23bcead481f190a4931a0b6672afa80db1936539111e773cc0b7f0ef5253a009aab9e524748c3ec2b4f98fd012b7690312ef493a4b8529dc3e2566169333c9b535aa5d202ee07e20dfcd7a296c573cbb33e17ffb8e409786f58ee8aa24b113dda5e11eb1b3319de065204629730c6db0bb12b5372d9287ce8452062f7912a1e78584d6e890c97ec520d11b549cd711a0949a0a56b84e8de3e03b90dc5b56a35e0dad3d22408b27420bbf9085a0627b02cdf6f0092c9658f25f814b0d1e1f3f9355245c7a7f7847d23c449346e62faf024cc5a3ab0313f982abfab15a146124e6a67d6dbbb688506271cc19033a5dc6785b30d8554d0775e59ea49176ccd1929b22d2040a364b4ffc3b47b2078c780f64f54edcaa792213bba4b0078440f061351e367dfe3f3ce007ca93d7fc850259fc570c26d4a89519e16eb71924b6b9166da295ddf6dedde49652ca24533c075f078f075c24729f8b0841c4d3f9d16b5ebff2d16db040b35086a2bfd02b165241cfc718bd5d523c176bb55a2d1d26584805b20f39c9479d763a630b1f3d7a0b1186f8f1c9474f4518a4973e167d08f343d73f54999ea37031f906439c8f493e547dfc2325e062c5c92f627461c18de036c58dde793bcc36b1063d5723cfde83ab9b9543f31e5c470399985ea78cd46b774f01081af4c46771236a99d28e43e5e8bc07d7a11d58bb8adae6c8bb8b1ef5eacde0c24fe71d1bbfebdaa3e1e6a94f6f5aeaa518837a875a6117a87328167681baa52dbcee74b0a3411d55c10eb4af43dfd07a5ec1715c6cae6568bd9d3929ad55d36cdb518f82ecbc9e7df368cca73f1bc7759de791fab20d4754d8edccb7df458931364a8ad7dddd6c43f5dd2a5d44954237f5ca4a5ce98f60e8009552c9b4c26262616129a9a4b0b0a05c1616968f85e4b1b0b0c02045bfe18ee0b8187bb24b297b66dcd1e8ecd9b7282e9dd5cecef5ec5aa93474adedec0f0cccec247d9ebd8be276e792146640d67d3ca7eb461b75fa16c5fdbc1d9721dc087e1e0d98bf285f8a8c91d218636c1bf2153995141594abf2913c15151595ca2da4e831c0acc038ee7399621bb20d7199c2658aedd571caa0efa1b631093444b93381ec71e3d09266c521298786603616ca2124bdd2bedd4a964592e4e94bd0c678b8118cd14ae7647b04ce3aa79eb3b6566badb5761bb2bdaeb5d65a6baf70fd7d4d5ae7969b10ab55bf0f044be349337db4c5ed5a756dc7e346d0a30f379b95c3824c50ea5ad3063b9034831b9d70d1de126e0e1ee4175fe8905801adc343fc42870432d179f476463e472846795ce7d26b8742e1d07f830eadef9c9b9d871a7987c2216ece71ddfacd47a830150375eb3ab43e07fb231f81d42f4b8d1ff0129cbd44a314d4c04b9f5ad4a128bef286ecf823c6178e7f19d57f43460f4816f5ca341e78c93a141575d84b80951d81508960cee608438441c60825095efa8c09e8cf806c1e272c257b9204df0df1db67d886d993898179716939b1ac984a2a2928f723791db78dac56e994cd366c9384f4191d639420b70ef146772f91524e29254b2925bb94523ee19861e88e31c68d1869d4d00322ade29f3ce79c737673ce39e33ee61cda3766ec40113a2762115440278c939469adb5564989b61df0b43c6d50659d13cca1299db3d64a6faf426a3d1a34afae7933506badc68308d7129756a7dd12397ab8d1b14c00ea3946de83eb6c5b650250ab43bf05696d8f86486901e80866a55ad5344a2b9550a8b55a5a6bad5cd8eef2122aa5d1867cd1dab4883ac46e1d67e3e608e7547fe37e63027205901bdb728b5a6d9cbe32adb5d227ccd36ce245d4d3a384a88709252ea2180d5cae9e2994c043cf6f523481ce968c2e3d82b30695c87660285f52c74fd9f3f306eda3e421e29a9e5b55a5bd37496522f9fa396770e7a41e969868e52b587b5a251dc6c76d241fca20db5df96a214f08215f752e999146a74d298db463bcd4da189d523a279db376482082f7ea9912beabf994de0cd56775eab63abf56a96b5aad14ccc009ecbd9152526b6fdd541f9efeb400d6a92169a4b7fcdce8d3e24c22a804bed27ba21af7f03aa6be39679d86083c1d596da46916ec00fd0a668082d209b5d6da94d25825d35aaba4295075c1234a212f2484ec8043e081d6711b65cea1be669b507195ea26bc21c770ba941a92a502a69c4e28f7f2c88eec68e496d62fbef8a2fe04a9125a9b3ea92e212f24847c32273cf15cf84983fa695bf8c4399acbf0719ae181e1cc87a7e78e39de4656036df0e0ce39277351e794f13d4c9854ce392538a30f1428e06959c004e8081c75c8860f581baa9ff65e3fb14e9bea074eaba49fec4906136ef41087d63aa7ea6713d6d246425b333277553f38148a55d4efbd2c3c9df52be86c43514f552d4792e0846afac0559fa85a678ce0bd41d67ae7c8e79cd3d96b756d6a5d022657a7cb59bd19260872ed716fe914bb6747844a14c8d127385ba09e8008e6d08f9a4f5bde20bf488216f35e785af3b4e4a99e6ce40956799db5ee6ce4f3a14816938dcc28b884bc9010126484a0a048238631c61841ae241f8d001a23141bd1cf4f6ac44fcae327ddf1d3e90f38f415836d743acdccb804bdf4de549a046d3b78e974be70a0031cbc7e124da1d69f0af315ac43df16b556cf07cf09c29c38c6384b4f676863536f2e7294d9262cb5bc3ce29cc82520ccdff7e1a55f4115db48ef86507d3715b2d12dc1aada7d9d34d57ec0dae8f80a205700e9884a1cf588a231faa5f39e4ea37b9294baf548dda5051b4cd768774ba7d84d57f0b2dd075e1fdd8587a7809e606b317b2f06244bf309d6222df6afdbdd219e7da8d52205481d0db4dc15377aa80dbd64a20f35db5d2df6808411b4d774a2230241acbe78d49790fa42a20a095a2188a0a0972bf6f3fa8ee879854dbbdf88abb5d6ef66d3421910f0fde6374ef23a1bb60f8c669d617ea5ad9a9408b59c53a51234f4d75ae708a6fcfac5943fc126f2de52547d8cb17acf52d7f9a7cb39d4352ffdf47b02724ba77a6ad58d116ee7e18ded5a70e35370b2cd9caae9013621caeba506c6ccd846965e2f5f1a18f43eaec84da23ea33906f972b61069956c09025b52b6b8d1c316a097324152e038a097ed73c2ccc929184a238d30d2615ac5301fbd63937822c175b7db638c31461baa8fb1d452721338fe6247d953d2f91b609b1a285b2d46a3bf06b661afb951d96dc46d9da7b21dc9fb48f743b92976964a5d89db46a592d54aa552641752734577ce12d5346b471b67a4eb3c8f44fabed229254545a5542a994c2b2b2cde4e113b319d4ea7d249e5743a9d524edde9c46da791d54ea753a5a7294f1d5b90b2344bf4543f2db6728c46dbc671a58e44fabed2bd2514942f25e553515121a9789d8a8a8a0a9db25522b7cc4969ad5a6c3dbbf5c299d9c2303bc7b267c791f102d7759e47fa5e289dee45414949515129ada0dc8fe4ad742bdc365a59b1dacacaca4a64174a6bd53495b5a391ddb69b176aeca8a66ecdb38fa436e278c4d2146ed8d118a3f37161c16d90e3220782b6a023b6a018bddb5d5cb7bdce594219ed8c2ec7759e47227ddfbd37cfce31a3a0dc9d128dfe5ba2d1cf6c6377483edc23ddddd35e8e79443794f1ecdc4ef77594936d3c52c7ee9e51f6fc84705152be9d52d476b8274f6ed8755c77b72a7ad3e4edb828b90d869d56e95c911d2d29bce17df5a3537e1d2f690694ffbec190021f4103b438d9b58dde301d43cb2980e76494f1a52451b0b379761724bdaae99a6501f0ec2e40b861677b45f34c2357d941705cf7926797438d448664d2aa55cd8ed88e366ee33aaf632f3ae1efa6fb41223df1ecdfeb19751915f6cff38fe776b58abdc849e7e896932227443023486246604682e20f571c820e117bdac9b34b225ec24e9748e0857e81bef0b15bca39696c29e7a4b44e5aaba6593bd2ec68b46d1cd77531765d17bd482275f1f348df772f0a4aca083a4685934b6ce295958e595052544a343a7afc28002e388e33c1e4c7452f8075aca491722593b8321ff48ceae2466f49873a1c3b2ed7dce9e9e97074383a1c376ade0c7682337c5726709c0a65f4bca54043710777e41c4b8e3d495b2cc1b42c71cf1927e73d63dc8ed8503d927884cee8708bbd75246f7b3bd2b5c63abd7f82a1037e83b5cababc28d66914eb68dfcc8e1af5121dc5ab5fd44b0493a07e3dc5b90ac32569ac57148c1f49639db35da358e7a25887db6077fa36c5a5d7addbb4a40d8f6451b76ed32359d5e75bafd18273ba2d3867e41405c3d505b09ef2f3d62b8cd7025a8782307c78b8339fb7600b9cddb066c65e036a03a85fb0662669accf6814540d91a4b1be028e1bbec45470dc7e7d6872516fc7649ae28625a21251a988733ab75e7ac23923b76e6a49d616b4c1de96886ecffb38e176731e75e21624696ccdca0e6e4d9164bdc4248d75f9d66b8824abfd64d3f312b3e1793bc1f0f49b1497dbd98adcf09bc5d2aca44589890f4a3cde7ac927458bb79e32e39ccf4d2e94fcb260d5b7e4addb6f8797c587920e7e3dde10dbd8701bea611beb5c0803146f1d0620ce99cee2418979aeaf9c1246ccdb8adcd0e4e2cd698b6da47417237805c3a755765bc16801abac872e3fdefa379b60ebf30d45f20fb5390c9f8602460b36226fddfa480b0b058c18900b3cde7af8cd3e86166f3d045b31666cd39f872fb1989f6e81420a246b88bcb75ec3a457249f2fb15e91bc66564344c40d61c4c29a195dbd3827e6633ee50937ac994d3087c9d52ad38f5659d34fabac9b5cadb29b911b9a5ca69f5ef15b37b9242bea589f28b0d52aeb1105f2b40a6c7d106d61bc0001464c0b07d0b2456ef4d0e4a2e1e2621b9bc55b0f5baa78eb2d31cef9dcca216f3d3c19f1d64f2657373d1a323fc1d95be73a00bcf55100de86dbd05bdfacbd770b6a8971ce74eb2e2ece916e878426d7db09bae0c336d653486e583393ad9f1ebec468c4fc070ebd48288f66858aeb42110a3e7c89bd751ce28735b3b75e33e39c7e1b84b737a8d8c67a0c35cf817d3add5bc3a455d66774a90ab78608ac99b5ca5a4fd9e2b6873533193db6657cc0712a349174c4d8b4a3405c1a67bd624289a81773858a1bd2998e0f19d6e3a7d71f9845af602d98ce3887fb4997f8fc0ceb8ebac3939036f9e954c6ac93a4993eb90fe9155fc37cb125296e056b8032b46aa670a10267ad62d2aa099aa024e486744654414ad42ad5a474262951a9865e85a754932b5932cc1696b8219dfda4b35e3513a25e15599f4ebd68559e5ebd58e7aaa357b567885ef57079fd5183f4eaa5c1db2ac7a182b464eaa901c7a9b0babe9d9341023c188a2b4e6032c4bea2c749d61e15cf776d7d4b962da40d4298271c72ce10a239e8950db1b23c116425e03737cf2ac9ba91803d2da0752a68254debb0a4c00b5a24430d4963617614ab397a555bbd92b7552702300cd2431758d527eac436ed36441824d8c23124ab82f6e5db4316c25310c66f5970dc90699164516f1adea8aee7154c537811f42cf4c4c9932cbe8891224f70ae9737289d5daf6e744117d6751124e2d4d15edd98ce1e85b5aa3d0a1e2a7b4963924616b9eeb390153d6f64f62c6405ec43ce47c88a57a431d837e7d28e00400f3ce80f3b3efa8500c7d127f7f1084fd0c3c8110484cee9d98b13e038cb5d2057ba0c674729a5dbf6e1ac0c5f113c283e960082e4e3fa90810ecfc7e743073bba1e43241f5604c111c1e3f91832c1563a19d163844f777d2c0188118f18e7030a27b02f906d384ea0b95c231404515b00645d50025ac4907684cf3cc2a7d64008198311313ec2d3f7543a15012469985e8924bee0c70b824896ac2b69a20bb994bca63983422ec3627041ec9e9808ba25fa2c14640a1923a49042f628cf5244c185f6a1b7c42aad4882bc669470679e8582ec8039c2f52631565c991f50715b6ab0828ba2c4cc12aef62cb4c41132495c9667a1254c204385fb3d0b01f1e2c31b93e316a974801045212046bc9ccf42405c70038e53e14ceba7b3ab554c74c34ff6c97a15930c4916e7d3bfd9c7a4579b4fff883e272b54dc9216bcda80605569c639bc9de0e79c5ef2e19cf60f0b667d3e4c23e31cd34f1f2d60d6077bc1cb92b40a0837fc60b2efc817ebd517f337c42f3ed717e47b7d4f7c401f9156cdefa755d37dbe1ead9a0e6bd577a45552bacb11577eb10f466a72c30ff6c1c20ff633ce4fe65f92253ffd1bea95f7d32fcf10cd8aafd7b7a408243f3f9f9f1e7e4e8a7e461e433ebcad9f57c7cff09bfd8ca81a8e04f1e955f80df1d3d5abf083c17a3544afa67f3d7a15bb98c287df8f9f92f3c18e9fde33ce4ff6ddce6d41bf057d8340373e7597f053b215b92cc423e863baf533ad9112292db41625af397d1aa664436056aa25f6f0c518638cdf73dd459a840e81547a48655d4a3214298cca28ac57473eb64bedd94b828b30b5640a384e852d44df416cd39c9cb447ab9acafc7bcfd1a691e9a41a89fab4aafd7b6e76294fb8e1f683a46549cbac55aa6e99f58a252beab4c45a64ad6a4fd9e2520f5b6241e0d01f6e3f1186488bf8f619236a7bad5071b71fce51f9f62dc8f6922c4a8402b5aa5b86be5b865a969c5c7e4e1ddb4e7d24eba546f50a8e20eae850bfa15382eb5d4488207e82e106f4adfd16647b81db4fd1951f6e3fdf2d44d5081ca7c2175aaf794b8f46cc5b97d1471c8a9952768e4dad0740eb8b38b1e4190acf459ad0f15c05badbe6ed5ca06b391f6f27d5a3559afbb8ba144fab523a5aa53933335fa03b9f5332cee1d73c8505b35a644ca3852cf0a858d8e1b527af79986af21aca872919e7612a8bd7bc458955c26dc2490adef2c56ba08b0faff90acf45f11447f11487fd308de6f6ded80293ac948fa4d11c46c3c339d62f8f290a9fd4cf6b9e824a0dc9e8e3d3ab1e9a0f3da534153ddc8c841bec690cc95394a7295a2a954aa594940f0bfa62560948d244a73b7855823905923448b24c1e9dc224abe4d1b76dfb641f9df6700ec9a3531f66a53865cb39d7a38f4cade74c282b69aa97501ca9bb4057fb0b74ebaba0a24e2a08a53e37fa16947a9950a9a1526a56232593349a5f24dc30f523fb09d2da129ce4e386c5471ad9c7ad099a1f360f69783e8630b0f80843f6b17b61f1d1e5c56a91499a1f2a78888fa1cb8e8f3e13c439decfc03e7a3873e4634a06fba813ba007d7409fa18baf47c74f171717d34fdf0d1b720ced93ebaa9c5392a7f3d34f17c4c2949c998468b9e1a02a78ef05a8a2702a0f52d32c94ac952b05ecdf0a4a8e0d58c095232c92a01cdf0f4aa04eb1913f0aa4405ab341530d524c5d3abe89aa792f4aa73cd5343bdfa5cf31494cc4cd2682520c992299234b05ec964c1252a4a404f7d68b9a81619db68245066886d344f31e1cef0704ee9359fe9f11919e770aff94c925e5dd77c0520ce6179cd69863887e45a9892cdf0d0147d6a091da99f56691ed264f15a48337b8da666785c3ffd434ac753d7e8cf530f677a5eeb79ea28289fa74f02290fdb50d0067de125dfb54a2b4971fd8be860c287a9d96b2592e760bf73eaed5cf0b64af3155a37bcafb5bc5e73970f6c196a554b12b6d1bc43e190c3fd16256ca3790b113886d6226b4911a5643125db7e7af52dc1abcf095669be35c1edf061f77acd4371fe49d7fc9352460974376f44b7b8811c4a46948b73629cc3b9e69b11bcea5e9b8b733ad79c79d539c1aa50ee605d9c33721ca2d6bd7a45776055bbe69d1339be5deb1dbe3d9ce1094b556c4ef303cf6b30b078cdc3199ed77c848221631b1ce287a952152919db68de1c48c3c3365a4baabff891e3105b649c135de3c0f942c916dbb48f5a5ed32c28c3c3369abf0ce1862db2d752b2d7fc85d6d62822b6a08fe8abb34ce73de9b9579d6ba83be5ec38796a24fbbdea3b10888b6323eaca20592dbd7a7509f44a7ef5152aaeecabcbc039295fbd861a9255d334d5ab0a060637c4f98a23a36ab2e635bf912c49c43931415fb5d7aa6b2835f440072843ab6ac8b08f482ae138a57a8e2047e146970ae04c9799d239e3cb7074fc74fa8459340a2c98759271ce0a129e287eaab438650163969d93bd516a38e2a455fc118cb238a3715ad5ce3f033c6931c61863acf235d4aa99a45538ad92812aba11c7430ace68d5741c1c9c19bd622e164193481a167a5cea214e12d6ad645910072795831be2489605dba341653f5d80d0eaa2c04de754281374430c31eff312f6f286b663936e904f9f03329f83cc53d74469b775794079cdcdd5a54655bfadcb83d6f3b27ac7cc3aa8360d1920b691a721b661818920b400b1bb1824c5d846ca4897818257dfcfb7840c918f99718ee630409c635da24cff7e7ac7b7c410530772871d519e182d5e7af80d790903450b06880331a253144b9a9061fde4d5ab908b1e28567ff413416ad8f001c9baa886913c3c3c91e726f2449ec85374c3c8e392f527bc41b2418d588c818c2d9ed141f2c4e307d7a1f2e4c38cc562356ac462b11ab158cc0737e4d88cc9291661e81883c662b11a3562b1588d582c666fd0a1296ec841b369ba1a6344a72932d8662f202637a4402fa0d72b08689b6d33a057a90434d87ce8c18e930d2eb2b8d20511ce01ead51c7a421624f603f631f6c9042d87a898c69e02d0273b4fb2d30d1123479024b93142093ae4a35316247ad55e4575f52a642b5e010b8830f48aba91a7958b909138e915e7486740a444548dc50acbd2033b3a01915a9d615f036fb8c837c872bb31e252a21e1fbd7fd0a2204f10f9e861edf99e3c823e3af5619ba0d92f3f6432d9d08c488b1b52a219d14c362b2292c9886643488845b8d2ef9054329340b5a847b8d3258db5b7a9df3cabae0d7168ebaccc6e486334265933f68d63874c59a880d0789c047153ac1ba669e728eb0b0d332fdd4309beccf0b1e7a778558fb0a3085cbc74fefe01bfcc2aeea98567a1193cd1840b7ca86da215538421452750c77d91a99a97ce49273d17d981e3836995744fe978165e7a8d07342b0a0db55aad1d74248f1c2f7f5e7a2843debcfce955081344c2cc54498aabf28838d157cd41af7434c92e4dd9a372e99c3445cafbbad12765a62e2951ead5505d7cffd284e4712188d1d74b5761024b9f6eb439d0f3f46ab73927b779a3fbb29f76abeaf4dd93a634db4c3b4d3029a59456d2c852c7cce23c64ab7d0618457b24cda43c3db9958e3d66d3931b5d7ae7449552cacde77d9ee35de96090b8d12533af984a2a2928f723b18c1a003e2393134e526a524af95d71a3cb92901ba994527630b831bc304035e95b74a377114411dde8a82e2e0be1a49494522925a554a394d670a48b32da3a12cb664b1f8a4a0b85d1177dd1179df4896be43bb68862ab361dadb058961378d2c13dd55a9b5521d05a5c28a537b3e5ed48fa7a75a3a1d6a173b979b9d6c7f5938f1b29376f47725cb09c5c64a44bb725b93174f60d6ee462e28cad38135b92459f787df3bc98f03ae9805f292dd86252ca39a79472ce29a59c73064929e79c1be7b16cde4a67fa6e4a8b8c542a2527ebe2dcc09a5035afb5a69c228dd3f376e4643959e9edc896c82a1168159c1ae7d2d95e491a69cb07947b899e0f46453308c68e50968bb8a75bc48d4e25b533664f2d32d245e6a5db925c39397a84e5e0c69899196994e9e4acd3674e1fc9a247604d614da02168fa7cde8e8405c17882a322b6691aace49847c1718c524a1965e4a6d46777addddd0d863d513664ecd803c7a9906b8d935226b975c5235d97ad061960b7e53ba5a8989849cd4d3d121806f44856162de2991d5240ebd0a2260798b101d38bcb8bd522c9db991c61ba98ce3efb9c382c8891e96c4ba774a6c3113d0450af8ee2d12082975e5112bce0090797083764222b599a759e6f51de9d6ebc24549d6b4ecfdb99a425bcc0a064e9de18944cb725b933dded0d891b85ae94d239ef28cfb72723453d41757382dde456885e8c30c9a25ed0a256b5a398c808bbdecee4f92efa8e41e625b8fdb00dea49d126b9d64b1119b952d6e817b7ce186e3ca52e2905e23815d216eb4c20ce89138ae78e5470daea188c982486bc627e0cf98929724fcf42437a60a27057a6e03b2461c60897f42cf492f1f02a12b3e40564c72d3d0bbd56e03cbe701cd7b4854ceb7a4490795d2f0624048a280851a20842583073c5557916129263a6e75e27313aae6512d383eb292122063df02aee7c168ac10e66b0b8a367a11804c500488809180eda99f907ed21a9ebc737edf1edaa93378fa4e919a20e788a614202757dbbcb106e487bc0d01ec9da7c58e7e4fa7609f2ccfce6c2e2460f7568e9d18879eab49f83f8fe08caf8f9704ef50e493bf876128c73a87f4d787c7bc7a459282ee9f9708ef4f66ed6ab89021b2248079097e3e5c537ca4fe70425c67dae6fd93026403f149caf984771495623e92428bd04a599a0b41394f60265821dd36146ceb1cede0cc3284aa9798ce252af1e8d1819616c43dda341e29083fd669bea234982c836d5abc6c96e921679362f86991f01b10df5eb73a387b4c5399ad3264fa9cf174aec29fd5cb2c539f3837911e40488c9eb29fd7e9e52d0078a26df3f7c7b88127b1e14cc09d08f0447406cd36eea428bf9cf25595a92a6695ff2c3e4e50448f322e83bc7b7871fec7bf3f9f6d65c7ab86c43654882972e5b7a94d467349d2557e9488d74523a5d0821203eaee3871e80e088a184142af72ca4041315c879bad7235b15244a3c81f3d34972fa95ac1054e10317301f4c7ee8d08955b8801028bcc80112217e64a033a92044458f89f2e47e3e3acb3e3a179b1456e0676cc63847e64bf0f2a577de8efcd92b193f453bfd1df9a962bb69b55a3c380e0182208416495022880f48d0995ef404f1b3db887cf48d48aff8a34ef453d8adf06109064fb048c28aef9f187c7b3b60e1c3ee6751121820f9e9fd00091afcf4395df3b0f8d1858c9f5e63ecc041e21c6fa757ec839ffd23c80b907cab60c144f810e6e70a5c7e0a09f1fa29f48213a84871e3113bbce96728e3e77ca157dd4fc73940afdaa70c2ce24e8f31b2ceec15f86d8492ef2d88e463af7db12b1ad5b4229a94408547cb54441474a4944e31f8fcc892c581d2a34745470a2e1143293e7ce8089d6fa8a4ed3645350c15cd0c000000008315000020180e0ac542c1601aa891e03e14000b77883e70443e188783410ce4288a81200621420821c600c30c4086ca661d0000badcfe841fd157c7ec1f1bc9896871c79ccbf4f64a99a6e1b85ce87f2a4e7f603bbebd24ec13f2cac82a548a8ae0270b905c6f6d64efed24022a2aa063f2da8144cdb4cb5614bb280dfea13418bdd945509a2fb764b92ad0257696dc5cdcc1277a72e04e0b69b5c1a692c13967412c39b1c659aa6e6215c2ab820bc0f35641f0d1a81c8fabce74423ff4f091f3e94e6e0f62e454c0ce435b500f4db069c42c40a55ea051fabfa89bfc366488364225f5a854653b534c35b656daffe6647cb4964db922ea7d965e91c5e68938e297bb06f77cdc959bceaff4c74faa96a99a263d3a9381f6bce75d4e45719f8f5d77d3fc947e1cf8caee41d4a124274373e18e9afb8880665a5d8a94ea1f6cb4f7f979dcc8324e563219a3460455d948e1d78faf9acbbd22b7588393afea505150437b81aa3cade1c181a5393db4fbc8c9bc747e1c480dcc817b4571a302fb208b30e14f307fa18e7aa6a3bd4d0fc5c0ca8eaef4fc6866371c3dea72f54c2e2222ed9d30196b3c84122059d66f1fd92efde006408c8619f40366908b03b7f20e13eac0103a2aabd74e1870411256efcb3a1ca179fb247262d183c2173dded7da818536aa320c1ed777c07eec4450c7172a4c872683dd16de4e1a3abce6048fcfb68a25d6b20ae34d7afff50cb56452cb8bf476bbe959ec88a69828413adf5702261a412b7cc8fa4a4661f4ba8e300de9ea9b9349fe06470d228a85ccc09ec2e9b24b7978b55ffa56a1225fa9bda988114c983fe0c1e3b1ceed7c2d147184c15ba36148a96b4299c0263e7432ea05df07c376d1de827a8da2e0775ad8f057e53f03372ef8a0090dd179d44b866ea3a64c7ac32e32308affe6444392ec7d79454d435f8d5bb993a69d21ec23ff22de7396b684b4c8663a46c44e51f53f53973422e2cce849c6c2f615d37d8369dcbc994a1f899690c7861106444c83b4ea275364cd310a4d4d29506c7c13f44c5d304fb8c95cf58c18ae1235018c9cc047a7c7f204f89f99124e4d1fad087f88e4971ec5d2d8991231216b3b4ca3efa484cc0139efbee8e3b449a847a9644ffcca7a88bf61b98a50b9dd71bf26f6b0c627f6d24626ae875a343ee17b58691baf091fae8ede8501ffa8399632dcce6641ac74ff90783be2bd9ab40976971ef15e148de6bb3f9a0a8f79d2a4be4d8c7cd4cea7795910d150d21e8c7154e55b5b5a87b5160261f3c40abec513380bf57f27cf5d5ead24b16feb141ed1f44d90b39d842cf393bdead2239a11e222ce87697a1c26e2faf3d50f05f18192f71752aae25079443abb4f26037a9e5345a9bde1cddd5b7b8d9deb57088c32e34a5b607079859a0e6951221648664b4e66e05140b73088122a44f4174a60c4f6bf00ac563f20647f4e5d49746d0c24c25e853a22ce0447a6ec4baf0a84cb7892b41589587acb711afb1617cd5fa746871429370cf41b5e668287faa05175028542c2d4df9f00dda73b3afe919aba14ee07a91f9fdb7922076bf44145e372cf733990d8c209d6289d107ec16a75c0a8ed7767e1eab8a14e8135619d05ae92ab24322265b2407641495d8a79f122b6b5c8ae806689d62335fdf47df7632e638857628a94f618cdc6acaf9cd8ecf62dc588e1146e2e55c62cd283e7a56047bd92311f870f8d2fc08a624df47a04a7d9497ef954409dab0b5060ed51f5c47cbdce3af30f61056f9e7125f42f3bf7db87d9acce50d065153c9011961247ca15cd49c73b9f7f633b1d2fb308031d405e66a63f64c2070bfabf3800a831861fa77c2aebf0f7cca463edf815434b7d3b1111783b462ec4c686f7f0e68fc58d50918020e3e4fc758f14623230896c0dacb6217c488e499f87c5687234a657a657e9d76d0fecb8a1cb1656cc578aaa620c35ba88bdc48da19963224af22bc5a751ca56d43e320a5b66d144ee9833564a3494081d7454c5df940d95fa95ca530d79cc28e4cdc63aaa15a6b20280cbe0969c515d23d4da571b719682d98a74d622f74c10c153bc27f4e003770ad92990cad610f0e68828281c22fb26ce1ecd97f92de7dbc026ea0b2f2b97f93411d9c0b8a0fedc828e102b8680efe4a34536989177c6047ca4041cfe7037a17fef085fd2b9b8ed66b11a31c78871fea2e372cd74d8b3ce3eb732afa83fb2c35fd2d40c2dcc685525ff706fb715b3a0ca6ad9718a9b4582b31f633053f18887af51713415d9fc13e197f38b2d10c2ac965e7fcef18e7a2dd99911bbbf0789d4310b1feb79180d18e188d0c807fc39d6fd2019271d2ed07535e6600a3c6c0c64d64876d56a3eee2795d3f185b79c66b01507d92a7eca6a23b7ceb2338031236594a2e752f115abbe18373ae2bc7554f91ce445d9c36245fc04fac7049c34f39aea34d506ce41eeb124e2cf0b48e848b82de31c095df9ad3258a0d1ceffeb51a6bb186c65ad0611a44aa988fe3593e9c89b18060486ca55398b72ff8ac2fe9f59076cb416ec155fc3f0b82ff0c3b1dfb22306ab80c000965486113d2e3d7749e7dac4a9748c5b5f4275313ab1bc0cdd9210b86995b951cb71d7b9a7a09aa65d1c5a486f64d910b96985171701956ebd2ad375a23b846b30712496dca5910d2f49021eebc6338cbd35d67158ef0442e7614431bb6f89d1b4a6f5c59420d04478ac23e3ce9e0e714fbac95573fd8fd283cd6fa273b7a42b57ad5d129213d0e4248bb11cae8a72693db1445de77a568f7b94df3cdba4b9b2d9042156b05e63ad5f487406566a84f9d0841f50795f414787628e5a09d4d9a321e5363cfe589e323e7dcf92f16b8091a9b03fdc25ea02454b381d14076dd99dd55b761e504b29147d25c0b75e912ae52ba752d9a0b0ab0733b83228b2496284ce5719eda759dd649d3a313810494160cf698839b1b53d5ecba2df2f5c124c4a7e945fca41f948d094b2e935b4e7ea478ca1a0755d779771b129924d74e41f57ab6d894ec23af173fc00f15fac46284e5bec142d712b71431aae625464604a7694c5284d58a158bfa54bd4bc80f6ecebfa720bf45db05bb0569a042d12417069af0aae1be4d3bd89f31815e4839d315048514fb0233a6e3defd03bd2c8da69f7ce1f1f0f552e4dbdfe39ece1c1c983c5a985e250dbb1396a9c06208730a34bdf1b2f4bb22abaf2957cf0fe1932e9a5c1cc9f7bb688d3347ec22d353b535330bab075a017d49e659c3a1c98bbb381a49c1c60be28f56668c8df5065324d340b2815035294c3c88b437f8e28879a4621ef0ed320fe017616b645b7c9044466cc46ace41ca5c377243d5c233e4a38a4cc4c5e18e6494c7ef407542494f60297d5cdde8e56f7aaac3a3e4f08528b626cf83755023dc108a1a80b81f59c261c5466bd8d8f29d880334abd2e4cd3c81641d31e94cdef6bdb60ad4f7350ece719733acbb3500221c480b9453ad31ac4de37cfe8a1caaa469a037b185797ff83761d347ae677142d64d205cb30bc24bd6b7f935234b974b29ee20932b2b54b70e371b42c268f904638810e61303a6a251da1c486e69d8e1f9176b331c1f357047538412b37591d5a0ac549c51bb255dae93c00f17cf6aa6d39e81f4ebce47bff5cb987770491d0dd32922a3275d15109a50f6d7bd517e653284086db1d1960cdf199624e457f15c1a22446070589b443b69770692ea9d567cd7fec0d14c58df92e8a19977c65c1cc3512d0a6b2bb1180033a053293a1569f79af2c3f89ae40c86bb9b755e833e643314583b51ad5e1c8863f5d215ee777b6a36a2653e0de2700594b63a92598f68390acc66f286ac71b7d7bc7b861a7d56e9a08c8f1690519c18cd4cd43bfe8863fa0234cf90ec221042b7434a16f3c03df6ab4f4e2fd838b22841ddef4e339ae0820c158af913a7896c0db2dbc6e4a17b0b3ed504215c5b2be95a7f226f38d697963320dc72cf496f7ee2c5deefaf30e9d4c8adf1fb2624a3f728b0cc07e8f4360fcb5f72694bd78355c064082d3ce64976e2ca795706aeb55f71fcaaa734ff4622a949a4afe22fede974cf099705694d6d3185067ad41c86cec5664a3afc220bcabc337eec9bd257693dfa81cdff96dac90f88337f07d35b46a6c41b5ac5bf68fe5b12ce5ce63f21d49ff176f0ac1386f1eab350d99977bf9cd66f20239a1e30271419ce36368bbf2b3d003f14854cc397ae02deef75447f780f1af15bb1f483723782bff14d45249d835c3c85d0632f20fe15310163066573b3f4c1ce53add089882822db2b1c83a849472073da6e529c5ec62117a79e508bc72733c28fdc120be0a1c73b557264b0f38a3cc645a0c3f26176ec74cc9c7d45a9d0d63038a402f57e8570c8821c0d7af53974f7db468b8d615b47d44b31cac3b7da922509f43f421b37903a7211c0ab330e958c9e6c8f8118d3dbb1c3dba81540480066bd618600aaa8c36261021eaa9ffe67ce145607f496cdaf3a261cea8af1e677b67af92009d949811956d485e60593ac01612fb86273d008193dd59c313eae005a74fe2608c8f99d0e782717a2722afdbec3b5b952048593af96b0d112a287c0538612fe6379edc24ed8925ac40cf083bedec90c4bee06dfb3189a1b980b1b57bc25937d2a8ccc50af65c46712a772996f51692d135f0c24c6224e9d10936b90ecdfd607b10a7fd40857f106e384f10e202139521b3235400ea63ce46c49c19957aed244ea348763bfd74f68d0216b0ae5c6ff8a50e37f88bcb525f520acb5d416d1b73375ef12153ae794ee329ff97813f25144343f834e26893c8fe40722226d7b43a2b613b2e9a5f494c12948f0b57260a17e9f89cc036136b45e33ef2ae6c4328487c1f0d7a9db2139320df9eff64aa0dd7ef2904906aabbb6a0094852eb1cf770e095ea1df8af2151514743c9cec0f0954cf0745a44c0a171a480f31e0d3a2f40fe3b83dcfeabe9f4ed80bf41d02608faf39a02f82184b6626a0cddc52c969f7c7f6a0f83c8cf2e55603d0fcc00a2f45c2be7de6027888ac180f16a36399c92659a8cb9aec6063290c98a5a865922dd3a1845fd54ea007ed5bbebe5199eb71ddf3bb3db5bfdb1473762ce12965d2e0c85e42a65ae9d9152184ab116261c86d7c42d3820645a51a154c04637ba15b1b3f1e534e3a59902d1a48984e23028af5463cb2ca091ba4189515b0e7412be59e0b10e9b75d814b6c889df91506d7b95184e251c949b9c9d4f2588afc452d2056ee01aa04ab4a8026bbce4848b94905fe3b3175742e6665fca79e7e8afd6e9575a32a02c563e388fe1b3262b8d5879110e8db18f414a7ac71f2ec61f898fb54d506ca6385cf3f8932e23124d53db30698f5d0091954814c6d5626871f3564bcc18e0df8f9a4752f959e03dee7703e4e36a71019c81dd02e3f9bf4e54e9413b46b5ffed94d6e39c6e51f6fd471230d1c830c9db1c3391303cc2599d2835f88abc74b2109ec8c7a09ba7837f69d4859c5235b1c24315e26bf5e12cd9775edb7c12ba882cd173724bcdfc8d1ede711f65da6ca35ec7b2d2232833cf9f925eb432429cfe5e642e01c9c6c57e9b534c40841dec084c4a661461d27926d07b7aad059621ffad7b3ee5755fa4322b3f844ae52014136609c8be4df82845c06a2b94c968444ed1917929a64bafed560d89f0551aa4e2f4ac6d56780456ec3680b6855269d89897eac1fc36066c92feac37abb178ec814f1e9356c16dcfe8bafab5e949e8f43d7ebc466ea05423a08f1eb9ffe5b569877949ff63cc85756ef5c5ecaad5f9829c212da2f83da0218deeba88330fa93e41cfbab3affec18a165e86a86b9166de4c00fa1872ee2d092a3106e8443b165c883b3c3896f91311dfd74b0796a6e143a1415ee96462b2a1224efeb5f9c3145591f6e5980de45a380cb7a9bec1181efb2dcced8ab1044c2965ff44388c0465c7b61f0b1fc38611464c9bc77131820bd0bc3d28108b3058aec9e44f4c9acb1d4c561f3cbd39615162c90e709dfa0e50a14b01e144210cf107ad0e35843b6e841e161f5713d2a0408faf6ee95013356bf3a597a5c0e9def986e9e9e1a6fd33bf2bbca7488f19cc3c1588a06a96291b409c6c2e113e92b3ce5894de5b9be45595353692d35ab3827b5a19ebe179b1144a736b4a89ca0087914ad317313646d21734ae100bf5af4aac863f2ac117cd606cf7180b28be51e225a6e6c2fd6873ab450ae99f2f7c9232ba0a60d9ed9bab5225ec0b6787666738bdd0b18df706b5f77067f3a42a251af6d1b0f75a413f4adf9d7873d974e089191ed0fe55e6353e806f66f3bb75e36e60eebabe6ca8ceadcefda50939fd2435211972f9d3fb74de5f8fea7179805d8dbdfd524c96ea5e98ca600a3de853763d0747494834a540be7f8d7eff766933ee63017e62b87eaa6f8fafd8ed84769a29b9ebc7454f46cfd08802c6e562124132d8ad1c943c599aad853d939a72a63bd42d2993217d63dad9fe4fdce1d1050bc9056b88d2372932172db05fc3b621b666547361c182fcf6b5b68747e74b2f3bef83fede2ba2bf61322f34e20aeb4a54549b223c93b21d83b11ab1663c6e096ce2c42d3a8d2840aaab93a56cb328de5fb4de5f2f514c0552eb78de3f13324ceeee0b2b56e050d54d404eab5865fdb6f119e21830b563adacbeaa86b035f4905d0df7da1f250007e44104c061c33837af0d93f0f33d29c452305dc78de05bf8c13b23b368e9220e66749626f9da71eb49f97790f8d248f833e66287a4b0652f2ed7af062edd8bc5db61eb8d08ad6fccbd908ba467b7fb02040d29479548970fe2dbc923204490042961e52810d8c0e88e1f81c935ddac642ae8ff5a79cdb4f1a814fa04cd55bdd2fda8308cdecb7f718ed667a362ea099dc9a2477c83b315f0b54c06368a71103a3ebc8f0071a3a56471acdc7b9aa7bdd3078a2196d06b068bced412379758d13d7c64cc3fa52c6618eaf69ab9f42e820f5bdd16b7fd992be9a28eac95c58ece805f5b74b4a5dd3e9bc480f17dda7ca8a98af124faebff6a45a3892d58d25cb5b4720c1ead3f665b2bba278c46b755cfdda7e7004154bac3c947cb21eada4bb60c4ca169b7f254feca72de9178f58d922f3a1e409effde9e4a823d8d46966d2f974bab4e15177aa79d0085cc604ed368da3c286f05c94817bc251442a0e567d21155fa6f3df1200a4d0f63b1a2610943b3bac6052ee33e0095b6e99532f1591f1e90aeed945723f6be3aa620e96a9c00a3b30dfa21a296456af9e23a85bdcde64c9f78c7dbbaab45e314d6177d8f5bc0c6a8aade8f34de8611484a7ad8454f1209809da7d374378c33aa97d17d809000ef302b81a7a9be2b3c75f27d6f59f078d37f6da62708ec3dabb4025ff9f97bc77544f77c188cc2d1ede8750032b308e96ab3eff947177aa640f8cc7f713bf31324ee86624d22d35378be4b3b3bf97f01c149598c82caa748092b5723e95ca9b83b1246cbe192021703ec0b8487227e6d73a41872bb68cbc3f410b31a355bea48a242c561c99ce9055064d55685eb6a127da2e0aee3b2bbf1760526af12e11c9f8caae58b59f82e56e0c8aec891ac637a01196027474d2006bf10323e750bd0588f656061ce8669168c96682c6f93c66a3a9f3f4213c7cf443ad1c81ddb27161d437ee81494dbada3f18c1659378abbe030d8c71bed929a15371be0dd4e6db5468be0514375756b7f223d95a1ed1c19c0537e66c9f2e2b5bc580aadeec13da66c996871258a85aa073f6db3cdd5105a20e2f962a538ac5d1af2a15574ccb255ece4e020fd6f1721e301bee447cd8591f67a8ce672fec5771f471e85e9c2dd240153fce30985481019bcbd97cb7bf5ecb07c901c2b7c8afa483155a351d78e9d5e5ac54e13bad52e6ff1d5dab930fe1a0a06172c06abc47d096fb3fb954abfe6bf31e26eb01298c96f82554899a6a769ec5445b9d7d92ee1de2be6517e3e213aa00462ce80f34f34d3c69152ce108b7103ca7cc98b04acfe861455594dc6444c2ac5a1b72428872feeae9f61e6b56e3ba141d47750f6bb520766e023f546dfe7a71ae0bb8b1c26156cbfaeb1bb30acf5b397cc224e481019a0e2e4b95248b1184caba49b58dd3a4f60fc7ff29c6589b5192df11e5ff95903649700141428e0b844989cb786627155906a280fd419c5dca9e4d1dcea4cc6fce2ebf2d79c28b29fddbd352c239f153cd9a01762c729e55ad2c578d2ec450106199fe760c5e7c720a71101da8fb33a8429b40c6de8934afce37d75e06c6b4f9c575d455dda8569475e782fb68a5b94d44a9ff10bf17ad1d014ce275f711646401ce9c40090f0b1718a83317afa619302a214836afa0f30e36fb18e2e0cd48cb2b7e008adaae922db3dbbf62f9ce9e9720bd8fa5bd3423a430a15afb446ff3c0cc9d7203d0373b64cf29479a54885504601a9a27c57eba25b41f26f53221257b394ab0ed5fc44bb49104c4b1e91f1dede351353f1e0295da6e43d4c1db2e388ac48637d100baa04990a4f00de11ebb583f335b2935e0c7378ed18fe628b12abdb7b7fefc3445f02399fab7034921d7a77a44a43c3218fc1e50ec7f080ffa0792b9a3f85eff496d5e38737e2611fd5dc37d3aa2db17a843555dd72a52248652606a3519e80c47cf76984b047920fd3daa70cdf43ff72e6427d13f195c1f643109d50969f23a00ebc4d5ce9719a2669c652e0a1d9c01b3a01b66fab14f0f64022dd0cea9bbd2072074b97c04f806c6e82ae357615d9a7bea20db5751e70a86cef95558c005f05cef1888347836dcbf4fa4960d25390ddecdeca3f88e75b6d6704e85af0902bc96586dd3ba704f5a4370f0a02bc3bf3777f39ec88598adc094b80700e0e2f4aca4eaad5475beb2d38fc5a34b13298b61b98200b8f6d9e43717dc354f9eb39d0fba9cf5c75b629e4eb539741273e3c088d48bde2c25e54ed2d4585ba15a2079da0468e17d29684d3872bd4152785f4e00b818aa566438a6b4ce111dc76c905d7b315dd6a58b211625b52d44bda9f4064d08e8b7e21ee82e7e226a998a0ed8e8629b63d2fd0abc14e2207430676824f1d7817e2e613954c695e470e79573a2fd81add09797bebc0fbb58ddda56d672437b3202dc237dabd458d67bd8797512a9d82bd3cd4cace84d013285ea01606840db8ff5593142db00d16ae6964201bdf90633a2e004b9a3d758ccfa015a9628379936322994b2731039284a19304d3a2efd2f2d1d14fbc4933a9e2753624ea0a9e7872d7fccf2665ba0ab223fbe9f486f512150cd64a05711b98482cb664e204150a925d2eaca6df8e1bb79714f787d5536bd24fb957479a2e942ae69164c606389d49d71d08f5eabfb21910fa5d1c8c39d2a6f989614d5c35fb6458eccce13a2c287c4d1dc5216cf7a8f8af1a4e9ef5a24495f7c349e142a2cad7cc9fce12fb17a5df12e98ebfba3a1874e909687591afc463e2069484aeb83f1a943d011f36272e37efd649ffeda1b7d7b0ef11121975b79c58cb7ad17de8b7a4896ada068d729eb785313d7650fb9ab21321ab3490e65c6051449b89c99919880464370f42698ad9a5ebca25e96e4a6ee932b449f8b14dfde4d59210849163686e7460a6ec0280c8c83f0d5ed2996b4139727dd90871e00eb01e701a55b18b2022b2e5bbc63307664a8af9c92dfe5e16b030a7e1368de1c61d4ddb5a70dff4e15b3125aea7d3ad12beba8a98572854abb75ebbb383339b4cb4f7b86a4df63ebc42322c0fdb47ddd83fe53d936a82172c449a3233df4bf5c27e2eba5d3036416ac6ef124ac0f2bc0ad0568439c3ee00ffbc6609d7f8576e87bda663ce89bef481f032ef44f1c20da456344129c2506dab63e26176a059f3c23b5f7b839f494dce1eb535ceda95335c361fad64f92ffa389ae20393efa3068e6c7ebe9db670228cf8b5d629f7e82fcf641e74512a11fcf106120ea4899c9d95128e18745ff5ffc6c6cc17581237742ef81d14b409466a49ecf99a0d41fc73a00c3aa5dd1c49c880e34dc03bfe6663a03211481de09ee773c62025b256b7c1877b87b122bf9395f220dcb67dbffa47554e9c22447b9479bad64bcde7c092d08bb1e6b8480dc415d509a460888300e3937406ef196461fadfef4d4546e14328db2b8e3d7c9e20fdfaeaff5fdd065f94e224062dadeac9461c9c93e8b1a8c9b2c8d3860bb4813210d01b0015e87494828cec6efafa35ecb8647f898248133b7d9ae4b421e06fb04658c74b134b4c1f0ab3c5e9c6cb322c0dfb4c80710cdd2ae1f235916da10172aefdaccead5a93a0aa393dc43c12b4ac620191de149c719e62cd0c6e98a6f3ab7514cea8e3fd1256af7d5ba7c77985913afb4bf4e3adab15611f489343ff02891ef795b7f5939f880abe4b60595731d096f12e6dfc0b20bef5121ee75528a0597bd58e4b2fc35051f631e9df9373744bf212f733f762af95b25a44404532f722cde623cf0cb2578b66fd2d843e6ccfbdca30373b1c7551547c55d0a76a911520b79bd134dce985632a72760e991599be0ce31acc9eb3c9ce11cf1c93e14ee8068f9118acd32be12a136f7ebfc46f0d880aa587f0077398034c32344369b470204e4ee926ca3a4d98a06516086fdae7bb599921e98e4f81d077df10eb015d33d7e1bacfda76d07074284e8d3078eae6e65650b65bc361978881a78c6a3888d01ad318067f2e1c1ecae143303412061750772347353cd800708c1034bb9c33c92c373b28606d0d6fcdac3c264a2b7d700c431f917e9d37c91dabe6060c0c5dc18e6fc1805818bad4b91c817f30eb0bcf7f595a414f3e26656b63d7e7a679ad9a885682a509a8f6b71f346757c2bb3b25335cf19cebea6f937654efcfd76060454914094f40d37fc105211a5106e5cb9ba0d3a031964d55db4c77645f5e2c6402831e4c051958c4d8ebb8ea57db5c565ca7740c02facd9c615de54863d5d38da19177a1f39d0be68187d64bedb398d473898c6c55438a04b848757e8e450ca0402f2315098e89024bbd97968ea26349636a59ca1a4f803449f25530e8d651c110623b92ee5b73877fa3dbf336cac9d6eafa415221a8bb54dcc0aa583121a21c208de32eaf47ba6611be5487a180dbd023c25f830becc456d6ccea638135ba558b3899192b22b3056e23711344d66c502b8a040ce484754f609fce187de09863c2fd7ed6b7ef1fc57730a6913351b28977f2507d6b54e67a711eefcc7d9dd8894654df6bd2488eb8417ca1822f233577a9b3a1d6217d42bbce54b793dca4b27fe903032c1936b9000e6db720be5ad4605f7c2bb8dd280dd77ff1db31dd10a46d61e4a33186e374e59d6555fafcab24484374d70a975774faea595b79d6914f4b583b6c7ce46c71efedde77c4b7778f64f89dd75c58041977b052f7cd2d30ba7e4c0a3a3024356a3cd6e09ac1976306800d10d06b31e58bb25373eb4c387138917e008a504c6c767e90b04b9f3998c042d82971910b1054c13f025fb28e92b57fcf646c9aa3b7e693ec898a2af00a6b40f248a29952bb1eaf8886dacf92d5edb0d465f03e5db01e4a5485268b3bbc0a3fc55f0053d5447c082db07333b65fc1971eb378ea3dfb06a7f61ce419e6f0cdac53fe4199b82f5a6daa33c1d4154faf1941482aaa77948fe86710113ae3aa5a0024e07e1656ce8a939c236c3f75b06691ef30be28d280e8b6cdcedaac4764217e2e4fbf7ce134b9cbc66b885056d0b805f5a47cea5f95b19331e1407ace8095f2eac8b69c9c49f5ab239c1326289011824487ca3e60016ba41c60a6b14ec80ef87a335410860b7f50d2ba396ba814b68f7bfead4baebdcef571c5f8a57ad4cac94712efa9ba73e7242730660c0f5b45f51fdb710efefcbeefa3c39d056b607fd3a76e71da461f21c4eeda03fb2624005c746e465241119188b3022db779ca5e6de643b36702808644f84dcbe79555f4fd6b431a2f9017354b5ae1b9aef465e58f03ead16d434526afcf7f6f97697e913c8460a94ff2323128e917a8c373f0de7c80a470a69d7358ea3d3353325bdf73c90b83126404ba61f7895aa0caf4e560848ab19a1f404361d744b428ed0768d544b297ea86085cf91cf18417d50a799d137e0ba758d7a5d8cb229dc343b7a941b8e3c0ba84eb48296b5accba2686a345a3f3f0a274afc8d6586bd8eed15e952413f404e4f4304ec398c924917ba2d62a817a7fd1449201c11c37ca2c2178d43989d4574706bad61e1f91feb055d8fcf442c9845e26c533d63eefc244f64951aaeb55a61cfc7bcf604655724069e631962ca3270c05b89a7dd8a3d1526bf3c1dc133956a0e89f7f0a48a3db281ad6a091385028172e998ca144cd6ab2cf6db96e525aa98f8e8bdf4cf1b61ce6e7bc6d2c45fc23ad3a96d4860a05a44537082c5a00a5107bc3dae369f3396a81b9b550d2f7e3236d7db6217f0b8e9ed32cd84e4eb7f0d3c97b0081f83e543d29e4a22dc82571808801502c0b9ead517491e2f8b17d6e38e3d9546127ee8b4702608597a7709bd3a38f35fb9ce300da90b42442f84b81db6a8e8d0410fcb288d3b7458ad387181601d8aa945632a9f4c7dd256fed1bd8aa8f281980d46f3b88f780055030971d5e16fdf885f85a9ced23754f1c2de5746c42f0a21921aad1ec9163b6ca9e0702c13e82b0fcb34c8b3b5147b2c7bb0396a3c23fa144ca186adc81bd090ccd75741ed03a36b148d85ff223c52235ad963f7892aa610db046112a604bde42897522a3e77d294ba2deb15274287e6b4eb39d0ae742b1fea8375d0c16105e0c0ac29517645eb1929deaa821b5b32537bf07e6b0ffef84f0591bff8d769efd6a6282ee8c56c93d12bc6bc49053244e2b2541ba121fb7b1f3db75551931ec0ad907d8fb25d194a59b21abd9901e6313af7d464f00c900bbcbf8b8c2aaff067575d424cbf3b3d5ab149c8c70e7032546d80a390bba006cdd67d97efa0d37da2d35649e8e121aac9e73f4d5761ce0773625675ade3d87a7db4c59399fb84472b128beabc2f3b1bfacb5d0ec766391bdd97078934a826e8f8d881f5ad398d8fbc34e8e7db6f4adb4e0b9002d96c7d8b47b86945b3b7f63e67bf8a31b26e0379e10e97758a86ea9026f73529b3df6ab8012cf64ed11862069cfdd54883a2d9bb2058363b870a933f26e70317677c3c340813ffd833df0e6adb023a0bd5a5fdfc92b3f20b4239105c2b2ff5316d268183196236cd3b44d150f67e0da2dab0929e8ae87f1be7f10dc755407be681a109d1c2ec3f7f1ec3f108b9ec2a2dac2c81feacfa7865d9339b69ba3a6a1aa077204520d879c0881eb80d7044065c29b820b7442073a6edcd39afad9ea6bc58e1f28db57d893355a6d55be26d75363c0e77760a9570dcbfcb2d5a4b44bc3f55fc92b9a7547132bc9cf0f28edae1eb6ba09cf2be90b8931a6b2bfc7ae0bcb72cc995139d7c6ad6a4744fc419c0a5ab3f3ca1fdcd0de74332c18623c4e9cd5a2b932f9777c2bc5e1ae7deb2b1f8312496f218e5cd79b922e1537c4507c9e7cb524911f1ded80e45386e7dc871a499fc9b29cd90982db9b0c856b2618ab4d1dc1fe547b514f953de5faaec5520896eee9c97e81ca3978f2eb3b7aee140f223f12803fdb37f8b241dc33af19f1b4d9ca397bb9bf20a45a0ec87aeaea1087343a21333af13c325b0abe710e536ffefc553df50c38aa6aacd79b0f823c06354c71c129a70ecbec0fdac46cea283f323ce972bc35018437c8b6b0030f695fc483406e275eeb22a8e0d72a12d0a6de504fb58000358bb46083638119ef0d06a822d2e933fc64130291ad7be3b734618f290ef51a99b9b7805dabf352f477cd58f9113ef7ceef708839be20f46197aba096da0aa821491a80482b40bb5b2d18e55e65a21df35473a1f4c34c9ab2a8a44c2b5402a397329a64a92d659e7429c4f4188f1a7e84daca1187a37250f4e3ae9a1ba8b44c205723cff6f0a0a1e6c75407207d7d85b70fb66504aa01c5f0df9396a8c44f20901194cdcefa9385fe63b3858234e3e0352b7a9318950a0aa7378e07799809a4224a49168aaa01afac8efd067eef39b7708f487cdd768349854bbd309ab86ca2f016e655be12b7aaa600487759ea3ca32bb10202017a6cb6c2ef8e562ba2dc025799c5767c97e39f1ed1345230698d25b423e5b91b82c99e39322256ac8053583e50d32fe8865fbe7e67fdcd3004ea245e9ebbf1aa4e3c06ebb1dc144fce7f20dfca3d7ec4f3914af38286ecd50226d5712bedce4ad2c6c71fdd46bfab6f101216bba741c4bf336ae9f28f06751c12d722291e780f93120e4daa3950161a4c04b0d08dfd5dc4be2ebc77ad8e50212206e9ff82eb42aae9405c2bdf0ce0f08a2ee2472219ca808b60481d09dff723c31348b54e2eb442d0281687907d0941d8c4130ac85722abdc4b831e1ba8102e12bef57073a0254916f248130e0db82c5ecf991a8120d31bc4052dd2e180293bce19816abc4e7daca40d0ed093b7e0f4bbddbe383a5c21b8acc5d3b7d6201da14afe7901efaaa7df914062de317e7a2b74af497c836049a932a06778bbf2449ec4ab3af25f847f5439b1534b2831ee9a7be1f706e984d5bd38e94301a7ac084784d09afd1d8d0e8a1f2cae823a99e563aa0f78867276d3aa9711b7157a18c83f7637db8e66e70585c82b89d89078c02ae93422c91183633104851db10f6b7a85ce309eb3c332dd425bb412262c334a59277252187611c726c5a9c507d7efe94645be73dd0e4873e33cf674b97e2c48a92e20e06beff3db6c3c0838938fc252f7812ca318ccfee7c29225a745c766d561e39d789c3a7810cff65bbcb3f62a9bae515d016261bbcced6e92ea993f13eec5c9bbb0493e080634855045a45003d8da5c49f49e1e4d7c3a0987bbba457a16560dc93a592f3fff637f91992ad21d26b79ba29f7f280f410e037aea407d4f2096ef3a9300ebc44250dd190c6646a45af4be1ff17ef0542e421c51b80c50c7acc48b691e63f6414287719a1fc949c76e6ddfa09e047065b730c2322523719114c572d9bda2c2bbc658c826aa7c4cbf75625f35ed00cf0a02e762250e22cb5d44317664352746ac6ce6f18439e54b438d9b74e8886e51740191277c4d87082bb7abf03c3821062d40cba03e72dd22917da6a6c3ee9f4f06cff242352f734d2780505238c40e13040fc61e123fc1943f4a42215b78db411c7ed527711e30454586e5ceb82a6f66ad296581a8077dfebc24e7062b8bdcd5c2f7d397659b49c2c911005185c1782e478366441e8b37c4a905f19337fe15da83950f44e7518df5418921ad9675db4ab0718704b25c86b81060db8c67b323a8e80be7c5d36be21df11c4c2eecaea43db3d7cd9bc5f56d667b84144cc0e116cca13891249011415d0495e2804f0cc7353969713aa80edfc7cfa31a1cb10523aa8df8aed15f9a98a37ff4bcedb89a158d75cf7977381bf70887c58bb69682a91f8427f10c9d7fff664b18309a137af53ad19afd57703574ad464cba76852211a14070a73533a705b6ba595f3b31d97c23bbe4627dda0f7e89a6dc07815f2b82ed4ab624477855a0f9c26d1bce610c9183ee1968b57e50bf8f32f7409937cbbde45f56bf28c3067585ba86afec39c963f401b239b986d84ca87887471b9c313ab15d760f61d52f4f8854a9838581480eaaedf8cef6a0e401a548b9a8ab4e905989ff41b9df4a517e111838a706613cf421fb3e69c294878e5fc9d09d1891cb473efc7d0cd44df071e280095d818a910cb49fd5b087dbeae0916e0d162c5cdc001b38a7e7043231dd340aeaa71ea84b6f728a1edd243098705173742cfd66ffcf62ab1b1ae1234b3218489287cd59355b3a9dad941eb2803fa9de45d354baa1725363a21f59fae913ecedaf4bb76502f2b1d37454e07db751c3e98f78d95780609128bda3b3b5a0a3cb84858481d4a2a2b69e8904383de9d6fcf03d7e5bc8743741a4ac333efaa160684dd4134f2dbd0c70ffec3c4d218b4352f4f1773b72a3bcee509a75fd9a492b70f5c391870a75d094795ac7181be8d02caaf27920c74717c8b6fee8dfc441517993d4a6a392df447a93e120f8060c981c4720bd19623f018be5125192b4db44cbe0f37a2922c10a2060123d7ab4c2412e88e4f852e1de5ad2f368f345b0ff33dc883659fdc71a581c28b9caac03273ea8b6693d152f25991a9eee82daf4d68191be2305dc6f46e3f030d5c25a47cec6780a029c03395e28c24395883f6fe40ec8cf977c17d26fa953b46ae04d087baf794321ec7bfd9487fcd05488d99738629497cef03ac3a40974501fe6ec0131bdffdae34d66c619b0a8381bc6578e3266d1b29b46441835801f247c9710c03b5e9b3e426638657c0aaeec2c21566b35d03c634b5264bd7b3bb8e8cae3a9da6d07faeb16ae8d4c0ca7891d28078801088a97ff80222cb7ed52870f17873b1d9f0326ce23321f1d13ae98258e82fe4cc57503814eb7170a903570d8e09990776c1501f7baf87f7b3e6f013b720e9c93fe4130d7e4e1f8c6707252b0dc1217c1543bb6510381bf89dd9a3def0ecdbfcfccededadc3e40cca5dd111762b5fc8795cf5bda07936d7cb572887a3f65414321163ab0eeafc09a884f16c05cb6a2140466148c3b21f9ac32a050b67a0f2879ee331ea5ed4fbab08b9a0853655ca2e7ddf1d10b3109cbdeecfd9d9c6b87cae677934f74a574a37bcd597b8eeeb6f1b73225ddb180266bf5203a8638d3348c73e527073ea3c24532b8bb9e46dfaeabdc221464ee1cfcf42f3a6b0d84b24cb114e4f849d4ac07532a26d50d4ec54288481349aa82467d6988fac140f922103c500c40428915989741c0de1102d95845f722f0b1dd86e7ff155b3749f96dd51bde9940ba7f3a6776768edb7507a18052e106b94e9d64d5ebd847b33fea1641da235dcca2e1737c6f66e19c83be6251ffa08b3b05ea1fb43ccf350112c681165adfebe7235efdc5d9c1bd94cc2a737d8ffb30fdaaa97708278d165eb86dcc2f134d697e5457b849e649a7ecbf3ee227379c389bcec296709968f09b618d10e357d56d94678d54af69da23a7db29c808294c307aca3398a9b396186af622d2ef6d3e4d0d9068af2bbdc5ab49900aa2394e3ed145f1061590d113edcc9211ee123002ef73a60962ab118fdd572a2bd03c0636f128e181ba3180024427cea99d21e3c16ff42a8e0b13a7f0561ee0d0e056312bf3e381ca04d86e6e7354c59bbbe1392d44457ba0d36aa8e5365ab004063aad747c0d4161aa8589438aec51a2c6c4bd604a890a0b9cbd3ab158ecec3f23dc00bb912a4e88edcaadbe799b53e44cdffc6ef030945375afe24580928c9372c53482ce65fcb8c5b0979d6e6d7ecb92ae7e1ac7b1ba88849bd84230273dc0ff3f94dc7440e7bd22669a03fc49911e5938841e99b5b26f521244e8ba36287e539bafb2712c36de20770c5ca73cc6d047c64ff9699a082d2234993a2d9868e6c604fb821ccd0d0bf8fa2f29ee9aadcb3004da32bb291114144fdaac7f092d48d52ba8bf195987fef9a1fec34363498ddeb44d44b4faae5de8dc8c7945ae98f4759b686caf96bd5dcede266f8b4a4de80d0443cdc277f965b3d8e5c8d9b543dd08a406757fbcac0c8cfef09152a457e62d42d1b72c756cd92b991f2009ca08f2577da71766e82832d2b567acfb202b7f183e1f943ac255ba32704e6ab9c5a939e02b2cad76106ce4e0060f33f9f8fb92938227a3f2c2b8f18bc46532fd33bd668e02ba9922799c982a5cddaeadec23295b8a4fe05177b378a3ee22994121bba50cb60cc312facdc200c30f9b24b2617032e5ceb16fd8567678ff4c17d37da79e694782ceea0083552850b604d1cda4f4b23b1e8eecbf7d4897d250ff636483e15c5c8e7c125ef4f3fce9df1bcf24aa70aec2d62cd34171efa78fefc92bbc0599adb0842d4b376e338b6def01778de7807905d51ff98822ddbaeb81b689e2142ebdf5b6b3e01f90251cd628b9aa7d3b76dfa1a769dae6f1c8cd03973bbd5a4aa26350e61ac496eb9139223448f6a19eef78ccaf7bc34e57485a6a346220f8a4943d21baabc9878c1cceee0a60ee74dcb3e2b6e775327063ce28fcff6bbae25c9395c49370d18a6304dc8977eacaee9f52a0ee725d2d30f914544d2d5b12d66d5afb4c06c3197d41444a7404798c0798cc32f500e411537446d079495a3fb0956b3e514e68c0d627936d91ba2f768572e8841eb335c6acd4bf7f2832140d60c02c6140e49dfc412273872efa4e9db176f43cf4f022ba1b7a0a22133d6e63d96bbd41daf1781fa1a51a5365e5bad1d9ebf5e7b53935edbbad7fa7c5128be1288fdf55584a72904695100ba52278951384d7e9156f95c1a4c0d793f67377d02672a1a1f16159d1566043aec27d0ea2c56bed0e89e2fb6dadc8bac152002509e4d4075cfacccfc259469eeb24c751c0994e77d2896936598f265de60e15fbace0771743fa1b3d2aa8d199175e23505728fba29a24f5d6123fd12238971772ec9f0de90651790de8cd5297e389448cdfdb44d4203bf7801eea4b245d479365d77b76231c78bb651eef44634e3cee94c63a75ec938c79ea98278ef5d4714f1cef89639e3ace53c73d75c6034f4f6a8e5168690888f8e25c9303b88f62560ec11dd5b3380a6794ceea289c519cb5a3704771f6e3e085336ab385cbbdfd8b2f1ccc9f9cf7140762dc088cc1564aa41d9e345ee2abb70762bd08e2df1b7e58fc56f9af4c7b91561847f3c5da4f5fda0b71882d497a3fb8ae213049517a8d117c37cc5de7084f7a09464b224345a769d5696b27bd14004fe6ea256856eae3abb87aed580ebd6ae70c7248f866a9830c2d89cb9352744177c41df522499a0adce2b078708e78cd03c3030243f68c76b2ea1a4ca7ba6cc3304202c15230f9cd202fcbb3a03d9d4f19c47ff0b4edcb851efee3a1bb61c1f41af7cb8603ffcaf8ce0e70a0d43fcec2ce5f7a4f4668adb05f60d966a70622aebb9aa87efa5606a645a9ed63d299315fcf6493edda247fc9c0f6e078ae61533cb60f2bccae4f3a86dab32a7bec240a07deeb953534f6c8d96313a9d06034f6d8003f658f271985471a404c54f5ade0722ead480c9b7fe6002d2bc832765e49019851a2e185b7de688ea839a7a7baf3b52484cdf5971327c4f037687fc90bfffcf2df3fc0ae59feb7f7151ce61a50d33874d0a0dc80af297c193649acb68f1873b1361b3e6377ac36c0c5c5250c2571ae0a50f38533214c40e1fd69b60548eb4190223197a241656a9b06f7c46aa558a83ce1f392910f1996b6b54a1375037ab274c5872c4a463614ae9d81c0615253dab9b0ee39ed36a9032f63f3a8321b81fe7c6a0668ed8c8f4d18a3bcf60e176c7c9e82384a3f6a412d50c33cda7f1cc205cf14962a564319b6ff26313e5fcdfee533a4d54b3951f528ce2e8c3f1b358691abf48edbfb4d030525d03ed927e8d8b9881088b1d78eff842f763d3b8fe0c24ae62112769565e5fda22e04d03136049f2be54589a455b655e2c27cb05b407965857679e0e115ef10026aefae170e1c8bb2797af364431d0947b2b5f8ae24dcdf932fc53794884d61d5d65df21765c820cbb07af79c56d2e2934a00cc98df96820c2f5e7ed48c688ce84de9242b7ef7cecfdf1d194ed9f814bc3ab9620f1886fbd99d9175e98162a51f254e4a7410cb3782c94cb08d1a5d2a81a145b92a681457c253d3c0dd3686385f0cf3e09e60d5cd8c77dc3c2c6a671943d0829a3ddd36486b15b34c2fbee569b51fd0e50fee522a359e20be65b8be63c5308db479d987b4e9bdde69ad11cc03107919978496458064be4616329490298f395cbc2946743fbfc5a8178fba4b8daebcd0d1981427f738710ca9f54584842117a73d854e952fd62d78717c61de3b0ae30ad96d259918f279174449aa97ac92e96819937a5ba5301d095e45e08337d1302b85beb6a5411028decd89d75a82c2676c988f37fccf3294efb00788a85f10d020c556020deaa0e080da67f22b8ef408d2fa1ff078eae7356fd93ae986c7ce08d15e41803f397d3b7bd55e5309393257f109a4de658498b61a9b57b275fc4a26f9e14d28269d476a619f1b7e018ce8b3f61809c2d3b139b488b1b20847b310639bbd8888270ffdc2dcfa4b90eff966c87689d2ec1110e003ed5705165e446b4a2a4f14f64331b40b3655563a9061bbb295fb526da5772f0070382283381474c3e27653f1a9119c50e04c39934495dbc5ab581406e1515b08f110177ffd0c7260b9108c3b2b7149d06f06c6e0e676deb7af8872722e35912972865c22e54e9d3bae013516bed511411ab448876e6bc55d17e608235edc44e5acf435fba51c3d9652837e4dc9bfe562fa23e8feb0319cc9fae8a447c7bad34ac75689f051fff185fd196182e1bcc9c7dfec80492a8a1161753b0190b94b31efafcdd237983f4f5016a1f8a4d0359de7a9f86907e7b31b384ffbc80565069c26d942b2c3c3358d7b000d39cb1cc719e45e3073d7c91d3d10e9983f14cae0b092dac66b4fd7801f6a84901b2f34ccf80b311cf06b4da3b53273cdc915beb11dceae657f576d90f19e96e0aa2405ef3cdb61c7f69369ee1ce08c4e2829b65b974810fac7c36a10b529aa989563874fbb38b2f29c57d8cdb639b2f28e1b1e9b2c0df9e3d9a77bdb1adfed130ab425ed897d076c3fce2b550ee6ec42117564f763f74d4b209a26bd417b1f7391ce1e23ef72c8ccd2d0b5803b5a2047107fb1685aaaa4c96ccfe9757f358c24f12cd85930ff93edbf79cc13083b4bbdd98457824e24b8a3c1bba6167a1e8852b8cdfd34afa0466db4806d78055b1ee677e66842b7d051c59266b1413b02b6c6d80bfd9bdd6f9801ecbeb064d5690a4422d8c977b5252904e9a992f4a15b8ffdb0dd21a1e4e17cc2183055aa252870c45fb8a9a7a1f555c0a619cc891c3d7d15978011495fae8a8e195ec7e0046899cdb0667d9aefd529742aca5efb45ae67cfb0d143b4ea20db46002c65d57b86b793c63a4806008e4677b9585999f216f7e52e06f83250ed251b8ea333d6ee0cbf876d1da391aa56d0f8d5d7ff4a6d588ecbb99b7416e352a3f14bf3879076602c7c3ab5c8a0368d63f90a2a83fc4110884ec66008ccda69bf56cd68850573e90b2989096f500ad4ca6cbbd01f37690ddcb2eb05085019774487c261cc3e389f7dfeabc25ceb63093a8c473a6fde2f91021593a846c04f18856f0241b0c5c3219efb55636bee5792a28c0f046bf1aff0195884d3f7cbc80062f2e019cd9637822f9160d08033200cf0eabfa02d95a399d9cb6081d7f6894c19c85680a50a961792bb40366b9bf8117d92933202f6fe662d81ba0d7ebf8081b7d7d8af4af066ccb9f6512a4599822a45a85003e575b8f448b79504473c6d07e7304ab11d59a41050b2e5666256b22f14a5fdcc8c8654eec057284c5a8e061d1a62f42e059d3db499a2a278e09678a3a7cba9dcf84899667ba9b47069a770c529959c4194c60f556034648529295904f5fc54ed12b2c34c864c584ada7f9bea6d735205e6e01dacd1beaebc0d72d92329e32c604c7b5a03d3f19cbfb80f19b98ab497936980f18104e8722fdcbda552c9571ae10f39db206284524e59a643949ca97041865510ec7c27fb01f02c74839565d2e70104e06702ab905b981ae15ae399fdb013e9334f31d7c955f658998c81209c5449e0b094b2fe595844cae1e08dd7198e8221641559ab33ca46b3ff86b8e82d6007c702b91ab2ea4e08a19b223e1c1d834496f480b8b5e1ea5be691508b790729c46548682f0441c3b95b3049bb599465eaed31bce9ef94326f93c5b2c1689d5dff16769ac1e6fbae2c66170cf397172a8536d707900b6ae7677d6fddae6d780a77648a4f1a4eae6ff4caa59f5a7853171e9096133c7f9e3bbfe267a112ca8dc570d6b4aa1a5ca4d42bbd4bd58d8794df97200b67291f7c3220f2c7dd4a259315e92ff9a07406d67cdef466ca946538fcfd7729303c686c1cabf707fde6e8e8e17fc2597faf000a8813f14800cfb1c205401faaa4cbacf14cb2b9fcc86ab494d829895738c23bde9a9ea8e400faa01ccc27d295397c66160e09d9e143b3ea68cf6687ff3288a344ed1a6514f065b842b13bc8f055d6121ad4fdecf114c9311fd54f98c6b3169e8fd2a074c5082b6b6a17ac6fb73ad9c5b68775983f58978cfc8c848101ef18f84af619bb81064ceb9ed43a883d73b21c4c5126281ca20ac3b1c785d6767154891a42288770745c1d7cca3cb577d330239eecefe9b1ca02409046e8684f4baa697990f0df184d2430915483369409e2e282ef38de4b2c60c255e45b4fde08bd7b32a00d61d9eb3fe3eb05fa7aa19a1589dc55c3644ece95c0bd3ec035f7dd230add45c172ecb6d418a99505459ef5aa2a5dfececc0bdb8dbe69e37090dd4803e73cead12209fff6612df404fdf931f34e748268ba0e60b6e6615459c2bab8e649b351edf43cac6676e6cc43bdfde4a8f583327447978163160f2f2f714a455834c767c7d828e4cfdbbcf0707fc6b76aa40139f28506e0572dffe403cf4a1dfc123dd057e662e39d3146b236ee89d2be2bd23ca584d94515e56a9f15e8178d1a78d37eb9d65eadb4c80def84d9fcb4da9a399374c90469289eab034b7286b253832a7ceda9f6c284405c733491131fc9e40eb8cb40e92b7b04fa18b3afb2c19751b3f04fbf7720784438334feb3f6cbe5ee75fdf4266825b309048abb4485dc9dbb29c022d4dae41d572b35d44204e4d3ad01fbf4b8747ef199dc90fc7e62fb31d00807af85fe5e418c9df9f248ee45fb5d133534fab1895cca2f0b6e637fcea6644ada9342a938e8decf2ae55023d0c689c6954801c6f9c0b019f399631c7d812577ed14e48efc52504c14561b459e7f938c1794b79eeaf880a4c052b1488c566104c46d5cfa2f1472317ad07341b6b70fcfe4fbab3d308827284cf9a1391dc557bbf95098ad7e1462875903e7492a888c12881c1b84dcaf49a8f26ae8ba54e0f0cdd1ede6728a6dc042f7a1d71b3136cee1401c8a8d3c3b1d8df4280fd202f8d7aaac4d825cf0cb006b4101afe34eadb6eb79bb012227c5280acde45cf009e4d3624d9208f3f9e6cfe8cc6ef477860e96309cc4b869ac8d21b8ec2e6f9d73611b943bdaa2ae820b005be95015f6c8340cc8b6276b6e0629a49b1fb304f6787859d1e9d58217296b47e5d567490a46283c92132cfaae069e9dd8870d46c24381a8f6281b5e62f211ba935f1c077b5485c2d5784fb019ad405ec4a33adcc385b72dcd33052f75228bd27b82dd28975592904520624cbb5e633b0bec451a20f480025de4c5c6dbbf04422e3281b942fdb7e8821936d6ef3601cce035d17471d492d2f2ffc16742bf3d3c37c3def1be94190606a2bfcaa02ff8aaf7e678547923d48e397fae0067269ec03b4b1ba5a990a2f06b335287d33b29437dcdbd59bbf1953ce9572075d613057295dae1888f6ef845c188b0d5671e5887cc8b21ce08f10eb2dc26898cd44e246e1730bd04e5514174c768cb91167c7214253e6883c3621c112e23b160bc7c7a0a792da6e4bd68eac40384a9e306e5f3b4c69a425889526230a377f954675a6b000738d503a589c3e33a5e17e107fe4be90b45c29105b10be8c99a2a71163f50b70a0e8456b76b46313a17d73fe7b38c4d7523203a05d05fd645caa27d67a2598d4b4400d51ca725ea81ed8fedad71be75403233404d05d0a1cee023e23bf43f8cd11f8f6a0c5145e0ead1f97da37e106313556f3669a7bd450a87d7249711046fee3222bd135a3877ee8a8ee70450ae2a0411d887e8095d7baa3b8b9f06c5726dabb4b22e658f141abf366f2a749f193c0d5dc4b7949a0c620445cc9f0a1d4bd430775124d6ec6c54084bd9c4923884002e38585a8ee4829e056111071d7271022a4a42a166fa2221c15009c100d62ab66df82ab19b135d85b356339522ee4518785fdd21e5ddce812c800e97c6f14a5184f451656ae92af7e267f1008e43f239f338aabe9aecb291a49d87c251f43bb945a68ad8f762859bd26f2abb322419bc851b9adbca7d381cbf42cb9836962c5c1568ceec2952a441535bdc2e66fa53a5a5c1674557111ff13e50c2a4c33e1d4dfebc3f9a1855d1c79f381989bfcd6e444b4a4fa297f324a6e31c8b77517cbbfe1d1d603cfa65f6b06163382b8c1be328336ef920910f126e78a1b5d0d97e2d1dd4136a62fc4ce89b5da4027c4080808610d4ec818587a4a6b2a50c88311119b75215ec4e45e24c42e1e25aa83363a2dc733f93230a2d2f5a3833d00c17738be237f3c05c2fb743a5f5cef877b1b2aa45ff728fc4f4645d56f335426d624ede1962b04f00386b2071b133b94bf0875fcf0a2b528ba9d37a577754048fb62781d5b02f615125b33e0a5af70f01e50bfab2b52098c5bc3ef8bfb5864936e34105061c630a5df3bf13f86112d60eb2e0c06c6308b96afd729f21d03d1e5eb1b08e85fc6465a921029bc6531aae52b5bf203cf83746008326ebc7880ac0cfa16ab0194e0bb1c20d23cba17cfb1175babb4d3dbdc07c86eb7f77719a8f481a83e9db0bf2302253336ae365cf241c13a8c991a61b8329e705e2b605049e95dfeaea2500d6372e62408c230930c90d97860e07dacc0b20f91f9b52a898a89060e32a4b9e8f9e3efaa3f3a7a8eb400958fd286b22cb16b64c8bc24c4c73557ad7a3aa342d304c3fe201e28efd0f206011d3f04e63ced348dbc69f78d6529415729d8862527e9f9d07ead5bb94cac67a09e9b2137a8f1d6bc73fc8ef4ba0a5b66bda247372ffe32958b9060e4d6152d4fe65e952f1b113dbacd89b6b911c19a5f29bfa529c8c7514dab93d56de7da4cbdac65e07b85c234d4d6028a057e695d3054aa9a0e6a0566043648df8a781c00dbbe4c0f8609ebec800eb7d3a5001c762504bcf2e59d22ae78d18e86707c5728c7c2ad6170e0c3f90b019d78f36f04c59a6f1b7774ac60b0c2cbef5d9430ebfc6ea365edde7b6fb9b79432c914db0664066c063e3d3c16ca1559406e3e509d74b82b70d934ecb2cf91271a8db683310dd34e34d50e4d89988669a7a6e130f016abb94cab284653f64476b7cda89330c30e0c55d4a64bad45d3615db0c9c08e10842c4de63b2f63340d7659ad1897cc0c0c76314dd9cd3a155d602354c519643567c8c27286b6212eecb3e50c714970f725c946aab0db7dd1d908792f4d7a52310e4474e57c23c065d74a0e96fd0889ab95640993272c20da3393c57c601f28b6583d2d2019588c0b0c4b3013a6bc8ce0c98ca6684546352223435aae17cc5553115d2a5ab86a2b2f4ab86a2c2f2c609171821e19a49720a49697222d2e2e5fb8bcbc2c018ab166b82036b55a0d06186ab51a0cb55ad76ee8b9e20c1108975ddb8270415a2f12e6aa05a19b157366d0f801975ddb585b4f107f202efef8b86a4d83cb56803b554c1f2b9e96cb99e0a1d7a908c4a5552481450190f7b88fff0099d96d0a72e3987057c401f4015d8e2a2d10726da6566895d68805b2c0d5d111124fe24b9864184d919f2cf6057d40b9c702a16be8d79faeed454d1bac7b41a2f380bceaf43e972eee0782e08b13b8b446df4f0becb1e91afaff81611013194dad80e49b25f968fe46b916a6bcd4c81415980a164d70f14a112b2c2fae55cc0fc922e3c50b1991a352a49ba6ec33121ba212e2a7202cdaa2ae57cf68453248620fa9258a1697971a5c5e4416ec4524127f6658a3939135b24647ee10f88279c10a26a606b8b446406c16415694b2688bba4a6b9445d7d018d90847e0d21a7d304795e0ebe371b087e4f3fd64990fc86657e08ba66016889a6c5006213004f7e28498361c41c1777a0e4e8540abe8c312c123f6f8fcd84dec61e35e84b8fbd2c3f5702d47957d850f161d70d37c9d230aa5468424c992192341386cf06cb00a58c0454db5a341e78941b8fc7ba1e2035f78e0c3a25d33c150dc61492c269bd16a2d5798f2c2452c45450ca2826fc02b3eacb0882b88bd0f8b0c17306246b69f214282e8c8202921b5bc18697111797801c24b2c82a883195d04e5c5bd7286b817cccb106c84628070d9453f8dc551cb6581aea931608e6c841460ee95fd539747ecf18115713f3fdc8b83d1548fc0013d600726f3c6e6d9c1556e31778f9bd9ff79c8425b5affc54cbdc2a4b0bdbf8f305ba529f83cab208123c049a0fb1b43f73706eebb124c8edb8d97c2e4d8befbedbbb17447ae347de63084c990232c26469ce0903f150e80abbc7dc9738b0a705965333663b5fdab8ca6eea55446ca9e8e25ae323ade6da7a9e97e7f95b5e0e5fdf2bb245ddc58cec880bf8d654c4ee1663296bd041c63beb1a9a99c3dbe7d3993cbd993fdbfd90c2a51a2448972e37180856d2cc9b1145f2c541a35f907a58c3c2aacf6b8e971c3425313f8336441277c1bff4a60e952d3078e9df2608f9b07471ee0a78c97e648f96fe4f1d520587657288179e37d87119837e14852930d35f58325981cb7c7cd5782c9d1bdf7dec8c3fbee7bdc7c9babb4b16a542abb74a91135f9df8bababbaa851396346345567d4e4b426e217654f52239aea1b5ac32ba35c5bd9bfca4851f0eb70825ce2eca3133c62e1f3bfd93d690a7d9814c652cc3d328909fc57e4126799b2dad1092e9fda803db65a65ffc1cc8855a6a58debb1b94a185fcb945c2bfb7f3d593e451d9214d1ed45531870d37c1e1c4d419f3062027b02d4508278623db2e9331d0723269e62409be6f380e399024aa6fdc4934c9f462fc9f34b96502e679e74089793c523f31cf175a4a84f6020b5e60cd9982ce86848ccc88ead7bd1c96e234f3a526caf1c9da11927feac603aa80b989ae67de941ecaa60c8d980fbacd3d1025c4e56fb9c9d270ae1e50c7d26c0e564bd669f8e146017b89cac17188a40709030c505292a4e5e40602420aa828540d17ac22b55d41596196020243e83c88d74434819161932c0e564b5a0439728e9d4d82dc4f9a801c3004db20f408b1c1464e40c91b098a570b869c268ea446343325027db661b2d7b29931f49478a961715c01cc5a36b6cd778d798dfe2e212c5e5451ce2457c09429cb1037d4affcea0c1030d181faec7683ead5694671bad32c7caa919374da225d9936447628328c3c418e172b28c4c32169333348208039d1100f07202009020968084e7c1e564c9ae9be6e320395fb20297936544262b8d338c39432588ab958c68564fd434df2f877556eed832b2bf4c0f81cb594e1e998cc853dc3bc9635cc667268fa3e873a5930cf7e4dfb1a9c96fc41a70395933948e0e9aba013041071368bd4fc2d035e8e7cc1b75dc40d3d0d41d7538c9948e4d4d02d0f1f2041b71147d0ac2484099c604653a81a82c97e4d7260440a684b1f42400608418181a33c4175b05c0b52e20c666755ca069fcb751c7075387862b246673af789a62114266475fc8b869ce7c60dea0abd9a6f9dd5dc9f3e7dbd0d44915b319673823575281fbfb2921cea55b91134734b64a8d8a6a765430604c4c88633fa6fa91ed079e2b1e4e4e1ed5bc010211ec70e20866fb2d045f237831a7c7d5a9c241001bf7fb4fa6be9163d2b86863d2f8077a571b703337b2f055e5b8aa46627337ee740dff2ada10c7b299d8a8b5daadb3d66edcddb60b76a3eaf7a94b0180efadccf04a6da76ae4366ec6dd6fb54e0a311098867260f7dcf61b57ef56b7a6b93bb83005cf750bc10368aecf715da5dc0782e0e7dcbc170cc350050a012e736357ef0427ca7eff4f1ad9bf9c2995ed1be2a4f14f0153421cfb2973146318025ceec6d935eef5becfe53ba78e169cf457cd1b13681aa740d770313b1d77b00ccff7be0e8627c60a0ce61622f2440c11f6720b1121c24324080a22455c41e445c577af285654ad140527a9f738e94f3abdc72612e23815c57bfd467fe9eeee3793a63f778d7e777f6bad1d4b73e2275c65c8e35bc98afd953f95eea2febef3b10ce7f962cc4935448b99654d947ba6a38d6c70449e470d65f2cc164d2ea74b091e324c489edfe50c8ac99610cae59c65da4cf27d39974ca33c7fd6e617b99c477912b12197becab9749e3cbf749697de53ba4f9edfe17749ca61b804153944400ec3c7b111e4f0c5d8921c3e18e284af72854fec06c4df7749cadf4794bf4f09a3fc2d9144fe94f8c9df7b21cee7022321ab1bcffb2ec4f13a1b10c1e9ea103132f75b88c335ea71b66a6f904b52b6310ac8d55134d9da1ad8906d0d90642b342448b649d0c8dc9de28680c3eba682b99dc8654ca64632a5482c91291251321523d3ee54005af04488d58dbf72383f14d4f0c316f1a01901b2bb0c212fc1238e88228895fdc9638f0641e08e907534f02287ade81eb1eab145dc113d37d41c245adb1125c8810619d9235e2ccf49e2558f80b1664e0d58d4d4f708201b72825cd4d437b4c157b1c32c08163f76e862842cf0a5618413e83003fce516e2c18798283ca4e08130985b88072372c0430d76e0c188900f4868b4327d9727b0d7a4535626d3f79878b5ae99df5f8ad58e9e918a4a2eedcc7a4634659168137d6a6544c89448a69e8b9a68984b9801bb5ca30fb04cd7e8a73f0201979e51f6b4e8547b465d3333a56064fa5e934c69b4c4a0b76bd4fc3f63c64a1700eebb36d56f9cfb325dc3e57e6c8469ee6da3dfa4dff67d0df0fce98c00f27cb0d61b01689af99c0c9d65e905326fde58830315e66fe6c6db19b7f460d5e9d4311bfbf2376f869e748dce37435d344d8bd3888b7c7333d4a4c9be19f2a2693acffc51b0029d6cb45d3d99fb71d37eeba3699e63697df0e4d945b96531d509a1f7d71f78c16a3ba5e491374042589af973309c2a741ec89c03e6fdb014fecd617942a9ca5d80ecbffdcca4f1f79ff917c5b1bc9e67e68d15268dbf3b065a47d7f03fd1940bbdc2bce19dcb26f23a4f03738e30297ceefe73f7e51c77749cf41c3748e124cfe6babfb9bb639f4a369a9c4f57de903ae704593e70ced1fb597ae1946d8aca4af36ca599a46bcc5f29f128cd55e781960568fbba7d3b39ca6abd15638c31a6791b4b1e1b7040e7ee1a0db019fff4d135ee94fee09889bf863843c47c0d71708bd2167e8b639fc67ca53e4e564b5b4ed64f29824b6779cb51e504ca8ea28ef27107e2a9bef9c9d58f322d95b4c751d5513c8eaa4f598eb22f245b92e9ca515d7f0229c9f5b5080223043fec23d78f29bdc0e8b295cc2172fd2e9a365230e5fa3959abce474386374a4f3276a6824eb268caad6853fd1fe42d72fd12bb17b93a943904123c7a92ae51bfa588b75c8efa71d48b06912161f90bb48f9f32cc1b3456bebe8e0623d76e926b6b91ebdbf094b3f0c7bccad78ff114ccd7fae4f7b591944dd44668cc31f27c3b8b9027108ba6c41963298af365ac2e63f92d632992c612cb7056aeffe1ac88e2bd260075f702d5c7a5c972727e4c09a6d49e5282cd5ee6de38adc8d347fd4b45999916bac178c224d7ef5e92abad61e7efd8e1e47c0af4e2337af881a36017554053f4050847d9a24a6bf51b91066c69767a78c3b6d9ad68ba1461ba11aeb2a9895122e099755a5dc2a8d607bbc54045391722709bb5d984ecec4281cb2e2a2a2ad2f164d117b50d70d945508a2cb5aed54a1240399fc2a89aa2112ec6c02909eacb0fa675192365b1489e54767bf9e1744fc07f000f738f025c4ed6aba84aa52a55dc2da239388557f513d9bf64e95459a89a76bb4d0612600b5268600291062b5a793ed6e1333de6a41b71d2c589ece6781549fd52d3e012ee16b3580c7c03b3229bc47c75df2509140483a6a14f6590a90dca77bc2f7f1de1d2c63c9aa34a1b94e9db204a65311b2bf2a8d8620b994c367bc1c0c0a585bd602fd90b082693c15e331aed038a0979ad8bde01b9ddb05f9e7080dcbf95ac8dcd1b76b45a340d7d3a0a4188b4341a0db3a5750d2a237292084d96ba9f0fa5a4cc1279eafee5c2fa75abf6c74d5ea3d12c4d59206ef2777f1248e5c9f77d5fede80897f6a87654fb6adbeae8fb8e6a9fe785e7454b4b4b8be77d37e0d2f33cafc5f3bc9616cf6bc9220bfbfaba567b2fc6b6464df4bbb7b21a4d591835d1c737569380df74a37d95fc467c6116edeb711475155923da952ec76d1cb76d5b589f86d6e53aaad9977d595babb5b5e6fa5aa110168bd58ac9bac0e5268bc962acd84cc662c9b658d87a71bdbc9ce0f29b7db34ff6d1bed98bebc5f5c95e5e9ee779d686814b6badf5acb59e67ad071ad580829ee0720b020a02028a058146a051d006548382496e21285c9984dc42370023639a7217cd42384822840324300f42c49193ee240f5783b280ecef412e73224ff1dc1814483ce5b272208e6a21295634e537feb42bcb5165adb55b6b1de2d42227a7a840314755214146668e82a96305922b0c85a1b47f1cd5425040c90bc8b42b8e53da0d824eb607d55a69e98553a65f4b42e89020b057cb6b9e5203092402c33cc8512e4779ae3097a33afb8fa35ef5e5418038e935c868c58b2797ee82cd317059817a49ae41c904c82db4c551fe265885b805c328787b1b15479bea97a24cae33b932e1ae1256531c7fc0d85ace75f4a2dd6d2ddd364b9b369deeca2c537c47e06af5f5e0725b657ab4b24747acd577f41dadec11b80a5de18b0b5c6eb36db6c968b3d015ba669b2c7cc97229b7901028b9c81392d00c68b984dc4242a8c8e50b4e807202720b398965db8ac2486ea12884e420b7d02c89556129519b11e5169a0de91be416c20193bcb528fdcd35e44314b4c46a88670641b9dc5a3300da5e4d0320b9ac4c327d5a52dbf2d95a3e99fee6b301c9344a2eed4f15bd108c197192facc494abfc4292fe3299e226c7b7f5b8ba6f0d86e029f8e30dd685b4eb6ba080988c85d056f88038e4dff2b5df086b98bb48672b7b5485d3762ec8510982b1f7953d9ac6dc92177911e7cc836661345999f91bb484b8b6cc7727b653a53492e5461ae56991b47ae6f6aa966283db072b5f19424c64aee2223b821d711c7c804bf00975bcbb6b6168d5159a65f6e2c9f852b2b405176c07e2e6b2d53fafe26c0a5cf6a751c22e5e9fb38496528a27ecae65289e1726b656ac3146eb63938110425790b716cd7a8e50bf9055246e224fd0aa329aaa44df4b5006a1244c1c8f44b5a24bb61b643a6b4d6225ad43594a0cf819a4b2fea9a6d8894f71245e2a994a74f678eaa4f3fa5647ddc29a551db2279813f975b8b875c6eaf726bd1184d6d5456a3bfad688a5b5153add916cc112e67c762798ad9dacdb5b56ccbb626e85ceb7317259c6ac2e66c02c9266c523a5f5a804b95cab6706bf2e01279703961aa20b23f1744d7a864484c57cf1f680b971316032283c16030123661b00963a5268f9b56b4c727af58a917dcb44101220b3213628960b44cc7d2fe54ee05cee69c74d65aab0ab98e4dbbbb7bb64f2a8a2c2982e4456925b32dcd6cc30778d98ef42b054d33acb4d64a29755add12c16b2170aab5dbc671e0bdddddddfcaef3be0f04c31a5e3a670a8893e23581fb555a9627510649250d2170d36f65c593e112cb904122b5b4b8b4b8b8b89064b0b8b8ac60171717159794d0c5c5a54997c6970369087bb1f7624e4e52c64deea64cc777f2c30137110e7872f95e11786485ad23e775ce6e9ce5ec66edb6826b6fb84f9f34d676736eadb59674d25a4b27383e81e79341f0109e5fad9d131b61376fab96dbaae58a783f9cb55c1375b35c13b5daadb3d66edcdd38cf36c16ddce68921f8d5ba719e6da27676fbe97e304df7b5721cb7d926ea9c4f27ddeac8d10f04c1cd36f179b59b28729cddb8fa75b34d78d56ed46e958e6211afe39edeceeb386eb31410779aa085dd394c181fe5eef7c05da53b4f2ed781fbaa1c2b2b2b5506778f743fb89d0e386d5e1e104c1f950856585858647424193264906e0b89446aa974879afc55de07cecf7b00f7f75942152c905f034e1cb558ee1a15d5ec6c5425f3333242f207698049d33533c439b5ace0164b63d2f8b7841e90264beeb7500511642ed76fe19e7e4ba8c29602f7f441ae256cc08b48ed46493368d0a081315671e26886db4a8d8a541f0d189810873e4c0d64fdbf5f304124259733425c5c081921c31746ad1bea3d990a311194297df194995001132e253f3f3f400001840ccaceda3f6cff8811c11f7fc32afb7dbc0318b07c5f96b173a24dfe2b515c4c4556199564ffaed6c950a5f539b2adaee1de11695477ac33726b5d73df8fa87f7de2a8fa9815fad726b5c8fb57226db7aee1deab935c8db2886f8873ca9734fc94e740103c4165e5ef77f7fdbd3276326aeac68ec9c417ccde3d71d2bf497fdac9684aec8ec9bc21529a4bdc31d9be33dd70773476b5ee4957f3c031c65263f87e8de1efff45b18bd11afb66792b4d232e329efe2cd9bfce64f58ede2af450a6e2df854be0f262c051fe2e740d7dcfadd335481ecbcb097972b3e79c73da6de356ee1c4ff544533adae4d7167160e6f2d2ba594a6ba5a5e9d74dfe9d32537d73a373690200b2df942ef9cb46d5918edea103eadf5c2f105d04e3398cf8e290cb27e204b99cb921972a2dca36928f7491cb4e92a90e3d55b05c61c4200835b741e8480f38175c3184c805434ab618e32695032eac98e2e7872282b8e9f1230a22782812044b06d90d2dca9844e6163a924466c92d7404964bfc4356b1c7ab2eb239609ce07efb3bbf7d39df8e3aa51d276bef386a6e73ce3996dbefe894723859673b736b9d9d6a6dc03ab57ef5af45f5075793188d3dee1869f63cc15d676b3e1d27cbc9d99aabf9bdc3c9499bb4499b3b73b6266bb27a1cd5f239592d67dd8bb128fe4f166dba2ca602ca03a60b3c5f70d2fbe9b8c34987a1efed96e5ac4a3b664f6fba63ae6070d27dc03bbc7b52a74e9d3ab545cc707cd7e8696d2d810aafa07d7a5a5b65f0018515889e4cfab35afb755a5b79eccc069f9a09a64f9f3e7d5277ea3a40a18b39cef0988ad1b19e3f847d3bced98293736211e7d8883d3149c4628eceecf445750a1c24233ce7580215761cbae842c74e0bb41674e8d8d9d16199d85a145c366d5b59a31f1b8bfec6dab69eadb5b1b6160db86c1a6d031af2f9379be02fb7737f3b95f2772cfbbdd58eaec4e3a8bf1b7938b972548fa9efbbb1ec71ae76ec38d1d4bc09ff7bef03598a806714e194c76f717478be0f6b1deec870e1ce2a0f7ab89452828940ca7fcf42ca7f630c45c4703fe5fb708c011c7b545113d942e9fdfccec727bd9f3a7648ef743a9d3caf44becc77ff32a4f76437ea7092ae306d7a298db960bd7e83c0aa0942bed6bbb2021114145cacdc886db5d5fefc5aabadb65a4ec4b56e5f6bb55cad5b3de56a6b137cc4c662aa1a6c666c5436aa136973efbd362a1c2a150e1b95cd89075825dad812b4dad8e91aef9b1413eb264db4682594b68caa26a0a25e577ac149fbb4e48293f6bd840127c11bb3d91ed0f0515a5d84db54ea7b5291a727450a43aedf29b4c97e14d92aa1f6cb53b6d3472ca76405f66f51256622e6115110b19863238ad83149c4624e157f9aa753d7504b2291c0ed55d464ed2720972a27ec974dc47e99806c634ee8d404dab4033304acc2e126fb5d2a02f69f3acc59cc0cb9bf3a7366cea15f470dd09fa33b75ead49f8ea5fd1c1bf78a82a3e6cf7628b8ec98cdbd188be23fa61636abe198b151d9a84ea4cdbdf7daa870a854386c543627172e55e4bd188be27f97a7a13172bd8d451ff141ec08911379521b39fe74b47132a7df7f5439d99fe38e6b29a76443df06859c6a93d3745439397d601ba12122e4f2c1bfe316b8bc79f2ec202787a64aa6bc03f2656448f29ffc8fa9c87f0cb8a0c305162e5b9643534dafac041f5dfbf1b50c89a6cbdfd35404a8689a4c7f9855d04b61fe4a9ceff6b75d2787bf06fced9863c7513ec26c9ff23d6eecdf4ec16c234c04bc0f3f87f7e16f1f8e3c8ad8c61cdda78c3c52be7b1ee17bdfe3c68ea58f3b3a3877d6094bdf570a1d10661f6728c13184df37317c6fbfec59df84251e45b0409308c16775f33df8df0c6174c09740df806353130b3489107a8872f38d2e5013fdae74f375a5bee91ed9c7161bf01c4514444795fed345d7c07cb7b94f67df9238caf0e7d691b9c78e9a3da9486bc065cf5a797e975be8091ba818f5daa2cdaf0e5e227287703b06b843161ce086c13765668081491623dcad6204ee906526e32f23417e8cfc58c5303ed56f2ef0cce965485ee0f2b148f202bfcccc49e5289721f7e3f8ffff07ff7281cb5b899e90c228b717b99f88a287fe2c92208c4c9f74db9c6c4cff77824f381cf5ba691fb97f85aee9aeb5de3b96e2a94dfd5e6e23403ff21c4bdc3f9fe012f7ac069be1a8c42c9aea69ebd48c00200049001316000028100806c562c15816a5b1a67b14000f6c883e64483a9987233910c32808622086c100c3184200309a006394a9a11e679e82f972111765844447f297cfbf8eeaf20beb3a636dadc7291082ec5ca3078487d5234f68f6fc0805e34b84ed51d1a3ce7842464aa59986700432f08561eb590755bd1799a0b65a7a307d311a62dda246d81e5278934c5e9231fc13c045c3b7bbd9dceaf1104a3d7bda274591cba5cd32799136772897790db01f1e46b4222f27c942714d14ae9ad80e5eedebfabab263691e0aca30ff0b1b7a46c831a9a24e378d00e1a80af4cf88e93d121c7d6866629429a1eddd0d2417b9cf794f89b811440a18af94862a29301db4409148e145a9f294b24b033f18bcb6268d5080881e0a0019f2c9a145c82b03dbe7eb314c4b3d99235396276eb174e91082295de3e36b499de9215109186ce9932ce7beef28db0ca9a24460a2378989ec6a29a41450f4cc0f49eabb078a46ab3791bfb18910a91d31698a4688548dfce9026a3a0ccce13602b1af83ba0c238a30d04962c674a6166da0d7909dc501cb249cab5c545ac5c9ce4893794a29aa354f496471780bc5860e46270e50b06b306672970f17cb63162da6a0143824c274cf8e47d9e6deeaa448569426ba839e8bd693c2fc3e2970e4d05c732745a61b91bc9d93a24cbd49815d3d17801c4be189f41dd9bd905408187e5ecc8e84a9a6701d09d7349d23b144ecf8ec3930b91d4cb5245591168930b5258a7ba448a4df0e905713fba66e6e07a43cd84167de0063cab55a64f61c186c3210522484387c10b1902fdd5c665d0b137dc53ff1931062238b0979cf912b8980233bbac8461f765f3718372d0e871c640008c1bf0515580b6d411059905910e2e7358685603ddbce31cfe9c662cc5317dcfea29d9e6dcf39a1db3b8553861d77502ee29872c7d52f615f0c1ee043d087b6f3cd533500db196299074e79923be82d72b950d3dd61f8d804903422938b68d799f9f305cdd645056207060aa9d3a11f3f9e4b138e946d75e2154be9731e8a28b1e72b5203a0984b539d2bd2c7844d099c93473b616e8ffa30e6f2e4522e89c86e8b2dec7d0091f421f77a80ae475c2be36a50164d32ca2394eadaaf6a129c25332f6dba39b10f50b72843aa0fca87546317a254633f34b0bcc44e0b9b3545544ed9cf1b8e867b3608953ae777afb3fbf0a6840eed269362fb8fb611f8535f59005f641d81e800ced37f722ab4627c3515ac8f91a13503e9ff8037826cedf2457abb0f438578f59e784d1e3efe9fd9d8bb8f617e44a12ac2eecc4b496bb0eeacb6a92280723def6b3db8594edf82ae1e10a8a3e6475a882fefe6e2590cb60d4d3b2c841e3f5ef717f9526ea51c8eaae90c659e0ba7784db9281b628b8ce5e297a32971aa49a9572f7a7b4d19e1f6b2568a0cd2dc3ab143474957fe8869530aec72bb7b4dc5ad290a6e821423bd0c507e8a1ae7dd1dbb4e4eddac85adc203350f9187d3517c5236190539f525e8662fbcf7e4a663e15f1d940de4017b56427c8bf8984aa0a45f3e7219dd94d20debc367e4b319caf19ee0c0f64befced88c8b30eee123ddc5a5f173c3922ef77a98f83dc30fbc4c6e5e3eabf3c819368e468fab3d879c7d6b91c9a7290c7a5cbc15af963cd85462a81402702b009f744552d280dcf0d4f6ba6b0c2455246ec89198fb072f1f5c20e1607b1d5cae89aad6b4ab2de079a01a5843003d4ca971a96113e046c8ade7987350737bfbcaf0e6e4b02901f69caea5990f070d3969586d6dbe086dc679c566e6870c9e92b968cc92ba0c42d9ca0ed405ae8512d6853ca216aafb0106c30ff63f8656851aec0e086a52275e49dce69c42d274fed00a9b4b7c8145face7c44a76ff0c15a183ad9081a33382726a24d2b3ae8f72064509e2f49ca7b1545568c510f3bab5f662341ea4a27dfa7a65ac178af41563b16a6f32dee052a01fea8b757bb0fecd88dd1cd0e23aac64200fd689e3193c44405d4fe384ecb68d783460d373bef539eafde0f021f7cc838f10d3903416a0737466b4cd91cdd5d46000e320dc735da777067e7a9cd1a08729ec831dd4a514f056c0a4ee24ac67181cfc3d0a159f186361324538763ae591bf6a4901ffb0e4cdc2dc85bb667b3eb446b0c04e2f89adadda66a56e03c12fb4abe3ab5c349caff53315498a38ac34cb7404c827fca0cb169f594e104409cc39f8bdbb81ceab2b39f6208464b174622888d29a2a59fbff22c2e1bd89fd940e76cffdf9bf5c96519b8e582c408835ebf0fe6d2689bbd32c3670e24c8dc52ac9775b6135efe61508789516079945181ba0606cd26734a1c98596e424d7da5245dd934f51656ef664ac01ebff5ec7104d5c40db9b7506238ad506ff5a994579c3353e72fd1739b7ed2f5904cd9183f51401b05e8fc165e801e7b72b561ed03551538df8f9f3083e83b9e4a92420341c5370fe29badeae2507c644c6e7258a819346e0be02ae0e160d31dbc3d9570e7e8d734c52dbbab5e9154dd348df81fc5a5a8c2b8ed8fc4641c3627f1c8097aa821e617a8d3e5f94b742892b1316467207947f0292522df20a7bcc7785cddd67118cc4140c28b006807282f451b065fc03fa48d528ea4b1ac2dd78afff4c1a6d155978dc219e736155d6cd3769eecac5d04683429cc1fb9a6f86a6da7c36a22b6158c1660b472caeb419829352b422cfcfaf53b049bf748a18c88e7abe293caa18ae0c58c4ac42b57553b549f06ce79a73318133ce33d11d71caf72f9933f063cdf35e1413b4a483892b06aca112a5b226079c7db60dec0eb1d0fd5873bf4c6b0663d43b8d24d3f1c0181b43246d2ec65dd985371548f371d0ec3a5ffdd06d02a95ae33e0f8fab61c1d97225c8f3ed309ee09d981541d71c462732145a3c6c7ca4e778fc0a50478f9f53437c6760ecc30ad6e2e156a0a250137c68a686804ca4e54de7a0ad329c0096accd83b349e512be862df5a95adf0504b5004d42a522b1a56f00d69fe72abd8df71707ec5584f2a51f9ad1a731afb649a77962ded9d7f3c876210c9f5607a0046c192aa2338142ec0d16581c5a9f0b9ef93af0e5058df663d1d20730c50ee7cd6b3efa1caa79431b7b43e0e12941382180b5b4fb45cf0963a92c2f020d88bdae3e214b4f038add727f13bbacf611b26b2795dcdb9638ee093373677ec4bc487701fc20e61f5ef92b5bf22add85f3d74ff9590360ffeb196c5e00c93d2e62b0d221c278334152c168640e759ab1b3e11c70934e322d0de30ca01090a2f6511216e10cbf611ccd1704eb883cc9eb67c2da92385ff025b3c827999f36e5acaa1d46a4e20891987642843256c8f11d644dbaf8f0883963afb41ac9082c71eceed615676b75772e2688ef468317c02429d3fcc44a67eebdde1f603020ae1cccbbd32d04af864e05eb03838a6f05e24a53eea03f20f12441e9009752ce466192e8314364d3f352efa8e53d4e68a665ba4fea34a31610ef7ae3e69afa8a32d5a0c5a218847f20df900118247a92ff878079d372920006af437f243826cb879c5401ad880dfeea032ddbd05c3f04b585c240c71367036a08660764a705d81791cd39bec18012ddd27496876b307bff314826fceab590d4cfe69d04df97295cc600e57fddce0bdbcc1d422e57e7fb6a0a3201c8fd664115f80624236dbd3e69b29a2007d2dff6a252f5c270e022a46bcdf3f5f190427cba1e183ed7b9332994932acf389bb5935813d6a1a2535be3ff002ee7377afb77d6071c5e11039c355cdad6254cb01dbb7af73dc85134a0e64734af01f35fecbac84c4e730b9fbee72b87aeaf26464c997cd85b6fcdc9570aa751b089439b6e99e0c964f5aa5398d2bb5ef976898eb6c05d3d38c2f72d6b03ac69d9c7328afb3730bfcbdce04239a228f6850b1d56cecdd40d5b57c5dd7060277e6ebed8ce78c9ed5b8cd1c05e160b79a00d12f9e7d59af386d37084176f7fa13a6ee0ce3fb5f8de8537fa6be4f480bb427a6fd380f72a1d0dbb9be5858aec8ddca4a2037b49003417948db297b58226caa397a76ad32bd0d51d3544fe5121e51159ea91175bf93caaa478fcdf7656f3261a2f6f93edcbd4830db3ba43ee278fafa37dd680b76cbde069baef1e7e33d449bdeabdf3d14af3f00a9d878861a2900afef4506ab02bb5e3a86d7e9ed5c59a3400d0e851ed56cea9b5efc81231bcc6f1a9090dee68adda7eb0a31844940a90caf44946069429074017762d8c658e31c07db8e4e1670006e406dde710d06251c9a06075116f35802d06d6471c946209e9ee2fe9cd03030517ef84e2e07ea834b1cc39486d83e79f9c0282fffe41b2e7ca22c551756d39d3418347a9307ee0f9829179af8db9e204ee2c72a487e0f0e6dfa10b1f923bff0babb5bfe564c709ef985f40d5eaed973e16cdba0827fc90bac89c94016907751bbbbac72ea1769a28104bdba36e10871b6c425ba9b581299c311ce3f83aa10130f67898f063019ab106a9aee2221cd1e37edab1fc7e7999501e455e2f454ad1a73776af626b39d1e7b5636189a39c8f22ea3a76f2b4ba8c4664645f3109d1eeb5ee7b61cfad9643070fbcfcfb6bdfce91d01b7531ef9a3a0d13034bc979082998801c72608d96d0a8f45cd0edbd63b81f15318f3cd126cbf97ca88f97981f5d3c9dca4b97f7183206a4d931419eb44ba82853cba44c6c03a4b81425bef20c6c48e1f969534fd6d5ce06b3b2004626a59c5da32c33de825f6ecb98bd83c0c7eed673de06608b26d046c1040cd2b4349af9d65efbac23ff96402828c64ef4bdcb18bd3154acf23c00bcdafd34f771d20aa0c5156f2d4f6ebaa19de70c4f1a592e55d348baf6bc4ce5494daa538a7dd45580f56d6a8a603acd180eb63a4b32807a683e2dfb3894870e5309f3860da5c4b276002fe86b7d80176750181e41a7820d0eacfe40fde9d64f9fcb120bcc312e0552b8953082f5034e5608209d31066b0af71a304fb69220e4a405e535720a8f6f7395a6df48a84f62a379de31e1cd787023563ce600b124b4269b7c2bcaefcd1967b555b5cbaeb0627e17a832dac937af67a8f17927a6a28e177339382579107fa2c93047bca43ef340229a3e1b020ecef68d984eedabab330ab48a73968ab920e2c411d0a19ac096b5300fd386f3dbd3fe5b48206d4b65fe17de80db2f0432d29deed5f1d06a7f29eabd14132f52418e60756f1895069cfbd66d8750f496cf77752aa091727710fd635ba266e8f6b216a90d41a635d8c111fdd55792f02d09df9ead0efb8a6c03763f7092aaabbe9ee6c8fa800bc19ef548d8c51091352932f0953368c26672bb1a5cde165f73eee1f65f3e5d27468624d22469ed77b56bda82595c346ebb44a0f1089b2273b660afb35a233462f50eca9ca512736a5e9a0cb367f683593b4ba5057766ae74a4b9f7ec4084dd5527dd0d48a01ab03c99240ffc4e5394ec390ca6a54dae6d35ed57603357bd5236f9439e3cc0be39f5add6e4ab745199d9754e2912712e28ea935c66cb03fb0064bfd3b481cfc3b614410ed76bd44373c35827fcd991a2b1ca492550c8173c0f887a92ed42d448e3dddfd873985aad7aeae9d08debc94e9a07dc651204a6d9d394fca570d5f19488b07b0842c566590ec015e134dead8447433ff8108ae5e87affb749d66439579ac8ba6ec096b9e2f8c544d1868991b90fd8e99617b1fcd608ca3e4320a9c41faf97980b585825d9aa9ce4526c2cc99eee241b3a0efb90ab5da3a9c727d774a6e734dcfea06d01dec636d1f6815566aa43d8466159d32ec1194c3a407e00e8647b76b96f074c6e239b11d391ea8d1844f22510b56004a66b77fde19d57050b2c2ff40b106aafa63b053f022c803daf0b0f71ee1a1cefa07d0fca1b18e425b5f159a0531933196994d121c0dc4ccc855e7e15399e47e9db5eadb68d6a0640fcb20ab751fa8a4122d654a081b9867a1f13d5d94a3aae656823ca1b5f673cd0ad7b9536116cc63f6702d1fa8a13e2d1f2403f33351f8b5987c1447fd8cd76142d8ba08c766e29c0b24da92d3b2c05b36742d629afd70c0cf412d8937649589a0dbbd852119fe3e7e2c669121ae7bfb3b24b8bd1a1f883da822d7fd80ae4f6933a99a62fd8f1ddedce7d14d5957d3de82a65866525a7d0fe8d697683af8787c98f5f7caf7016a25acb1c96c0a8bf02453bf75d78b04b41e43df3d588261911173ec70c15bda394f0107eeb9a549c52c27bbf7dd264bddd89b8987c4d05937d4260293d80a2beecfbd7fe2236131d7b2689c6ca0e09de14089a0b24ae99b0d4533fe35fb4229a77d2501106c3d83cf3f6e7e0fe0f43c00eff7203038719d609d71a30e5df156638674bb2818b11958c5074a91fc8ee13ec83e6bbaa87969f774c88d70f4f3825034ae935bd3b864fc90ec03eb0a3613171b52cf0a20c62abd5d30c6eb114bc4df5da9986b3c615cc79c982a7c312b2ab55d79f05295f4f52fe7d403f908643012f79332cbab69d7f6e4a159ff1fdd34e57f156dc859b2f9cf3729de957c1c483cdecd418ae97e34acbf3433e226388daf07d9c17cace4570c1325495386199d26efe09acf117b5169d88b885a79ea08a2c9b9cb39d7a7045384a49b174f3b97485472f69304c8f5101fbc81d9afb1afbba65b3eaadf4f31b972b656cd50d15640c034dba70e87b4d109cdb3f140d7a24b7be197bcdbc43e49f5cf9dc29dd6928b98ac1f4cf3addb2880106ee8ba396b1c85e5e35719b0a6d31af93cafcb36ee00ac87db5c19e59eeef77062be5863041f2a1439e7d040f915bebd8fe70381c82947e8fbd0a56371477ba9e86c422a576eba39ac6c39f67573d5c366bd70666e6a1f2786e64bb188ad5113dc16785afc8f2a31373bf1869f0ade7ed40e53999e56c09f7c9e0e0c54528e17f731827f7a617602ec2e4c5af91c8ec1545a2c91d49fa6298998650089c511a821cda0bcd2e3e26b57440bfb4a674ec404affdc4c1db3c8fdd9e55b5c6add2380c93711191464a5bda987d981a5cd89746767626497ef0293a08089871563bfc2c29044a506b4cf4770d9166a7b94696cb69a7417f9266529b62fa7c20b2e6f351495f324a97048d18665a7ae53fed2544def6f1f88ec2c0e5a7e09231090c983efc39d1a38f07fc5806851f8ffe19311998b92f5212fce4b9b1a8cd891e3b3e8835bdc156c5d54ea4053805de36716ec00f969f399e20115a1589b88c1d0c08715ec70b48b1bb83942fb4c106172c8290154477ebd129b2cea86344279d165b8213add6f234efb2c56183729697e7ee6c050cda13129a0406c2c3bbe5cfce02c44272e636ff7e151f450f523aefbafcc1b799d0199b70eeee05dff8854e40c7b4c9cd59931cbf35df21b2bf142437c7e9cccaba63743371dfe8b31b4be1174d25f50c7fd2a5db21637ea7d3df6fd15c0d0d88353f36ee9743d2d6725861b2a1dc499653fb8169e1597c9b24b04edae230accf47c40721682b2ea146cfd7e0fd3b9984574ce3dfe60c6e446dbb7593efa722f58e263453be0ad9705b0370de4401589292bc43220f0a05935f9c17327c63d100540575b6ad558c4b4a3463a2d6a9e09426aa5606262b551c8349fc8c74937b3d10eb0a14d627443488d2ede0fcaa01b6658f822bea820814c0391bd097035402ae77a8a94d1164a88a912c950c3632348d716739e0c6deee792912a04a10b485b30216dce47f3cb232b6071fc510cea232dcac18b11867a2182335fc56d7a03f4d56f6e783d33a73933b12d9722e42888554414e71d7270ce76c53e6fa20406b7fbe6a22a32c1900a8bb5144b90288d806d792a51b3f489e5248c0ca8be9cc40c142e0f631533b3273b31f0146d7501b377bce98b94ed8fb0b156b97e62b30924292c0be958c91010334b222f0354a5b55c7a4c727146f10df3d5ca3a000982b34587127f7dc01e2b7d57ac3c8efd09fff56b370933a2e7c9877c0ab7d46fd258d129f1f859229d5bec808e177a147e4932bd9bb7670d7bce8254c394027d7f3d22c055ec671954a977d31f877e2e485b850b7010ae83426ba00689a8bdf7bdbf913d3cca2a3811fc74d562bfc17085747c124e092bde589ae142581f30d557b09b1104e77025bcbd4528aaa4a1f466316eee522eebdc903ecdb707a8d1f1c9f97118e687ff2cac500ce0640aba2303091875df27bfa924e1ff341074e6114b920bd9a32f280be71505176c66dc3f923ac0a04a8bc1497807323336bdc292e90bece6c4367e73c0f880d8fa475f633389f15bfbfec1dcfd173e3a889d71993dfac5e371c693de912ab3c86ed0d0d0773eda3d2ad639958c14d4bff4f66fa980555b3a99c92363d779cd017732ff3e48964d16aaa7016dcb8920fb12c9d1eca8a3911704b15751f34791fe5eb8f4810eb21843f05b4e48389ec1ea395b87523fe05921b13124110b6da2117528638e5c40a8b4513344f5ea895abc4e1f9be90845e3dcf2753f8b5b1bd5e4c263b5bd3332851d3676d662c0cac878085e84b18967e34ae9c16e394270dab3276e0f5eb806418d613c87be36302af1bf9b78e768948dc8c1e4eaa1adc6b5c1674de9a8e3b84f11d93a89ba0a33bc6bdb822f969441f69bd4a66c244651ce3c2857364cac51592bc55640f53f0abad1153eec654242e3ac65a92bb3eb376dcaebf026aa220a69fa182550a5261567371243cca1c06844bfb575bb273ec2ffec995a7c17a19dc4a002464e2fe012a938d80416fa5ea5975691102cbb50ce33529d830de9a7d0348a43010bd613ba59591639eda6b893e7e6c0a9468d14ea599723c4f24cbd01c54e74e0b227de6d2afb8a45beb2226366d962a4b674c95b15287782db07fb8962a2b30032aeff7651d8932fcf8ebc4dd1e1723d6ff0f09d440c0e50735994a4270c8028299dc1ead3e0caea632b568b93efe57a6826ee2f127789b432e4911e77841ea2b41327dd82fc19ce72828586de2dc6ddce026a086f69422e0d667a09db97c11206c827c74597b2dd526b6b9a0c0ac7ee25d4a15737333ed5c2135a01dc9e6f774c4d39a12ae6e22c07297a366f9902c07136b06f09591091329dad9461c9c556066a0f8428e2ab62fec170b2c452d7691938fcc1b592db13be0fc635b95d352bd803608eceb735c1fc211e36e49842684635b9a22b458b8515b5334c2fc51577e6d767d2c63c782ea1678f61333707d654b91f0b27f17e4ac7a8af85355c488a0222ab7342c522dbf0633b4ee14718b6c12c78a9dc56712bf60613ad78326719c055ba4ffd68660b34e74fdcedb799302e4690003e6f5dbcc4d00018c8e5b226db91da9f743a0cf4e731bbef713213d2cf35c95a736743d460eda95e483a054d2d75e2c063606f88e8056f9182f19c558258fb32d9f1888835d17495a471cc4539f7212a4ef3555a384cf3eb0d2c01440babf3e863a8040ef31e7968a216f503ed116e8ddcf83ffc080c7957bdd4d95ca43a10235a12e5d7dada53b99d3aea5161106acc423a038474074eb11502a4612f1195bab49e22dac0c3767cb03f85369fa66b924697bf7f9efe21add3da4e35e0907032c8afe34f769dbc0c6c0a493ae0870f7a7e7a8312f5b51f55be8e8f3f5ad0194868f1b80eb62a58ed31ebb49d5b3fedddb6a2f344687b8557f64dfa2d047df3a791a23299137ba0bdc3c0a7402496fda8e8e5d36cd9c7a5561f186c31d6a805b045bba444114252421553924d552403b51a31dfc5e0e51da7526379a1fe7fc90a01b7834c6bab7488ca57a8fc9ec3e46dfc41955d19c3de36fac7588923a4b339c8d9ec86f58802ebbd40f54f4505ebe7da00cff214b0fb994f80f606f336ce0c8497a41894433a1ca8b6e9219ed83f429f513ba858a1a7c437efb701db9aa74a989da1a0956cd7a4734d3f003ab9e577d93a18f5c094592371b590e060126611df9558fff21de3aca20732ae496c89a2f89a898276432f56655034d405e0618e16e1700a9ffe3c714dc1344cb8f0ecb7cfcdda73e1f4e2940abc9c1a31a1b87231528526590339cc5f591c023a9f21b3b4c1aaf6872886a5e5c67173c7837fc0b3b5aa446bd97e42d7306a39ea191a82903aa23ba2b059fb09e457d3720c7b7a73ba47a982ad221a28c54d9fc1969b67ad33bf740911cb8478ce58720b8b26da81063004f62642d0e998b4cf65237cc7b67cad6ec7f36db4eeeb7f9a28a1628e9133b4eaa5142963483b603190438db6126660a545069d990498f4ea079e1ceec14d89e197378bbd8e9052e3198fe8a6509a1ae1be3b296742e2c4df5d4a06a7e465faa20ca0a32f7339ac48b7888952a8fc49310bac4ca0574793ec61269364aee148c1210e63b3703de4194cea53ce7aa8976e9e5a38427b7fd8b1f2f7bf0f24025967ab516da205b0b3dcebc80646ae43beecae1dcc8ee54615e776de3010ff4fcccba15e860562bde705f11ac9acc36cbd1007433e5f5968f6792f6300744fae4b0367bfbb9b7c84592d301ffcccc5b2ff3968d68306071eb934c25e8f3ae40293a6bcf0501a844705269ed8be7e4771f7213ba962e8e6a70868673b3957a8b8b5a531006c5bec906af2ed1341e815f02d45ceb68d75907e5098225b659af1b7409bc29b30db837a46679ccafb4e34d0975c7bbdb5f192ec04309179300cd6711760b34f5a838ec7315ce42095b5000d8e358defafd4bb542a77dad6c1bd4e2eabe834fe9fa89765c408856d7140f3152ff7d152eb6bd7f995fee4fc1c14137b574fd290134687493f0a921c4dbc1718b42076ceb900f7d30289f444eac28b32fa205a4ddf5cb6cd1d185d46bbee87b99e3421364560652f624c3d1ab75dfe734206eb40c7a3a03c0c69921601df9c6b0f42d74d695a96b878c7a92572d42cd49ecd9d5f40d557f966dfde9d95353650599ecd5ed38bda34233a6ef3cb903ad233b6b98b9fd144cbca89ca4f8533f7f6ed44d6eeb0f27ac5f3c3b13fc421479109ff384c52acb6bd46ac4f4d46f91a4356938673800bae08aa3b0695f672f90347126a37743390dc745208b47ade62b2a5d0f02d650b3bd85032cc722c22803056a05223aa19e226806e3f65a65e47f20307f8f86c35cbecb38b9ccd8133512f1c9cab808fff6a0c664b9c186aae313fbf52588ecd1f8fe3fb93ee0e62f60c289bfd0c25918f19eaa731cff5572518822cb141fa97c22200548e9f1808d8c080e955eb023f198124eb64582bb4a1304cec414de90fed9555d2488dc7bda00261e3428a6fdf0b6e335b41d83ac414159743dc2b45f53f2a7731a83137a6abb653d6803d1f12ad365910431cc690f244acd331edd9c802ebf3483964ef0f5096320182455ce3850bc51a9ba0c8e721b149ac6480ef3f6c7c67f83166c8d89bfcb0db90e791a6be042d3a9ae5fc1080a67ec8aa827efe4d38bac908915ef2bc5be19bf9378f638d6bbc2243160cc44451ce9767ed5bc57ea416470067d67793dc3ada6f2dc1381b816331b9943aede56244aaa78472971c68bea7962e19591b13d30984b9eb38b400b2d25331221747da8e4adcbfbaab38c6d4c4695092e64ad93fac8ed8e0e379119351c8acb05f9b1a99b84e2cb4e1ea822dedfd26f1ccb31972b1b44e4c1d35db996188e5f68bb9e4f0ad947ec4d8b10ccecca58ae419c55a5223db466b90fe3c313ceaa78b7c098e0ea0b92d779583f76a03f2d2080904ce46ba68dd01e2df47eb4841d0b89b6b2ccf2d7955f31d90dae0806a0565c84c45ad259f70596e5cd2a629276a5a8aa6b7336592107d28d2c75e2aa0ef9d01d3b07afefd91a71f201378d87bc95fda97940e7cb79ceccf0821d0a4ef035a26d95071ecfe164c43f9034be36a5b07a302b4e402751e69c1b7f476828a4e936850c929b92b2aac68e5a9c7c78b70e16d362515490e6039f2e14f7e62058f4676bd4da59c89f2b55970c3c818c87e4ceee0fbfb0e381bcc5f1f3be23f777c31074c12660cb7d3156fa4166db27e2b68a4b74122686674bee6911f95e74295b3cacb037dd4962bf51af8056c3742b6a861ec8ab83b96fae5bdb274b7750729f83af4412ecd3569c81047e14663811298591d9a3de4c9b77f92892a5909102a2dc7cfbd8cc9d571054572f43e375acd30658627c19c6c46322253918e2d566da70f67a10d5cee7609cef96f4ea93291de78765b43b5bf0b66aef4092e06b901943be974d36cb23e5de4f75794ab0722ea8c2ea4c59fe4acfaca2ae543d2ffe86c55ccd611d7b81339c7f489cadeb40521cf5658899defe52c095deca336153e1038ddab448cca086ae53d4d36c61ed144bb01ddfd12f02c2fe68beeb5b55118b896896b41fd50433c63d7ec1de4136cb60ff3d253c5648da53cb11070fa8590b6113d5810445f765329a4d99ae951cf3a08e968f96846e0a6ba52b50a321b89ab217442fcc7c7a61f07d20f616da8d8f943aba0254cb78421db65f0ec81f1d8dc37d20ffff01535b69c0d0bb853d5f90a7a922570771a251f20fd836e10a6b5f5c2ccd19a0edc90c43a0359bb66fc962be6f51bb80ca04b203a0a59562aaa35e12392d638acf247a8591d29b6d2f2f69eb7924855e6c730508fb18988e36e0577fabd83bc8ef6b0db32218b37293965809e44ce11bcfb87f62b376a357a75997377e34e8663b24830887a453295b384c75987b50de670130daf0297148bc6eb4adcae42b444bfd98ac8150d7837d8e98617793e9f17da7b336dc71b08a092357e521cbb97abaf2838aa9ffd42ef911dfa3dbb5de6a3e7d2b1f46ae5c901bd5c133bdb45768fae256ce880697c215879be0f448f891886e621949986345c3d885732a86c60d627c08239a85f4bfd44974574497de4c1418c18ea4ed1b42ebc04b271d5ecdbffa9c036bedafaa8cc840e2edc4dae7ea7903e6b3ea8096765a3708042be38cfb05fa3c1250f033338b226a2428e3ff955f911d4706a474e2124d8886c35961b84a09b54137326df694f4bc4bf644b252bed1ecebc909d5876c7d596536d108fc3f923f47ead8b09a30f50cf5bf586070524f5fa1c612ea6c267b4db0172ce3508778b59687faf1f85ae28d0f6c47809a67874f53dbfa86a593afc443fa79cb9145ff094da2ec2e8138f23a367b002ca66410bb3e69a2c78f248febe1f600a5d64b9384a8f2f3f87839ecfb01a9fadcb8fb51baf26777fd145e2309ff72ab178e79bb74c5cebb91eb04a26640df80d483fd62e5d9534eda08bea4ced347cd2080f9678535a899f5e04f04f763b450c28ad95df470518c01cc9373d622d75569cd849348769eb002e256d4c497ddb316448f3c9de3de0464268490565536a9adca10af873660d9efcb721086a82ca66e3511fb603da763a5c761715bfb14eb587c6bc35ca7d09ec51e31654ce420a02acf064e521e470e1c4db35291522261d14de8cefe12585ddab81c307a8b202e63efcdb284c52c1f0ec68cb096cc6677fb902597c6f2a68a1fb5b282d0dc66ac840186520b4053b587bb5c9c6e0a060c1ec910f985d0dfb26e4417ffc846a0a943712b2ff9aacb3fe16370a9e9e114ae9e0d1c3d9275302da7875b8896084173198349da90e6c18bfbff1ff1763cf7d6dc2e462686f985dd80b057ff69a3c80caf1a375b57053ccaa4ba25155738077e7650635227e853c4e32e62199220d38e70a8b0c408f1c5ad4ca83957b58c8c9f4049c9c47d39f36b55703ed8620a393b38be48e42b44395d29d5c805ac783ab4a1c3a86fc7131ef98bd79c65a2085e6b8a6ff8e22495ee2429e1c7dd103ab5dbb03905bc32ff8a034b41d5cfe19e3626e6dbb680807ddd519bc0a93056866f8210a65c09027b524d7cd092e8437322542fc2c121f62fa08d9a720e6fd8ca9f75f85f6352236bccfcf5af165aa82b9f3182ae4e877183ba0eea360895afe36dcb3413ae5ad34cf2a1ab94749420a4a70ab55865494f7257af2ad74aafa5247f103f3675a32e4dafc8a4ccb6c1a61c3d00b2b79e89be4d5c1e919d42cf231749234d7bdb27ad8982d4e5688510a6573dda3fcea7695c588fed7f0e0f01c0a741907f5c5bf568dcc98f5b086beb2b1981f0a9bed8901b771a633926631a6f086b16664cd322b60be78b563f1881e45aaa1e61d354221fa3e46cf732c98cada8bfed0d7686cc30b33e87792528c08cc32a9d50c92e748fd5f507083cfdf8013e120fbe630a278d6d1199010d1943ba721e27a916deb0234bc9f3c1ec2b6f3ec08040a16e99ad6ae4feb3827ab59fed0d744867da6170d656793d088573089356d941eda72bca2c4a562d3a1246de70638732f542938ce599405d197d9f9175f99568b0590e331a34a83e06d22f3717d69bdef5d3693611c54f15c9f100c899ddfc53fd3e158f8fa34d93cbce0fe3847ff406f18e07f5877c16dc468e87a6b2aa9598562e5f110d5a3daf30e55cf6ce79282361131dbdd0e5ab6674d5d4e19f5af16a236d8d666f5346ab42ef0ba8956e8bb72724b26e16724eba92518de6d0c81e8c223ddf7e0db9ee6e02f258709223d39f8c64ea1aa0b29578dfee14b80b571bd8d1e3435f49c29b36d692fb562250a4bd8cdec086b4c154ba15c4faf55cf8ca44c37449cc0c5129436e91fa6261b368b56444e844e444352fcd40fd738801f75fcea41daa081bef2a7b6aade21d8bf9a5a2cf4a64678d55648779018001a9614a4529ba17deee26e007640d29d7b7155b0a10542fe70e479a2dd38a52f412a02c8ec4b1b1a84b17636368889f3b62c9107ecec8c2d05f92e01dd93db1973278cdea77a2693aec142bbaf402395293e8fc96a9ad5598794479035bd6ba359e2f9205f2da214f2e5023901b6aafe79d1fc1f3bf8e573e0855c0e2b954d43d4a239f22f2c86cbb1be64d655f658166578748d5c4ebbfd307d79705785723ad012f10eca2dcc808e33ef7fbae639d8e6ae26069ae9c8c8a1ec9ac6168cca87f9b290295a956ab13ca94fbb68cae77653de28ea20d805b1f2cf008eed459a8028d96a24f2ae85598f56e158d75185754927f983928da954f44bf5d143485a3c98efe586cd336fc9c5d0238976f6d1e715020844c13acb16c324a6227ff9d7f1c3628d25bc257d00d8cbc7f1d04e59802e51d458e634d803478c3410179ef429a3928507d220ae45f1d558584d2fb9a1b8ee5365ffdd28935d04f842da0a83bc60c63d9891964971137d71c2c1e5e696bb022b221c436468448163a34ee21535518e2bffd9fdefdb8e42ca0af89c1e8452cfbcde150a8624e67bedf1e84d214311dd779720062fffc80e8d51f0529c8d055c303092cb8b6519a57344f2b27ca805a13d54ea5fbaea8793ecfc4abbb13370b864c3ad5c44b3725776c68fecab9449e58344312ec3ac1798c301ec7641c1a027541443a708ec7fa6bcc85b0580e423b9341c68c583cb04f3a63626b231e1f30e0637ca7bc2a6b746947877c41d6d6f1d58e8ffaa20326c9a4a683ccee11f251e78b7f8b0408cc82d5684f756dca7122d47f97adcd03b6047ae6d192a0fc54e793766fe07a197ec445e3f892cfe67fccdd30c7ee46cd102c88e7671af143e1665c456ec55bfd5766ca152658aa702e4806d4116c9107fdd0075a5f2fd72a5cf31c2dcdd0cd7f41a223ba8407e0d3f0f2fba0b9aa1933b99da69d992331533116d6107402a8dbdc88decb312a4c18b5fa669495f9d1f21252313518d72dd1eb9c45e92a68af0aaefe323b6c72566ec6a6aa953c6ca6a7a2ea2d2ca02f5120ae80cf9b8b4215dbeaf204cd89839fb279e31c6d3f873e0138fc1f902f8829fb62eb16dd91aab2b9d167c406dec2c7e7e725546f838c8742dae9663baf6e311906adcdfe6e6db80d01c4e411f9204f19b697c95350e8d1c64b7cd74a0c98afa1b879d3800deef10a99285a5ef4457d143e75478ebd46fea904dd1e420de8ac8451dfd2a93d39eccfcf20dd9e2a944f2996054733d7ca775fa1d02501313a06b9a0b2a948189971b418f6a222151f711749725de070f361f3daa70b997fe9921348f20d4f5e5439263462f8e876104a468bf78392e9b83d900328236c5416c8c333e769d13bb18498fd2aa4980d7be53f745ed473c9ae8e7f8959c66477b7fa71c53f4c409612556a5c5fd954910d69fa1435e7ce503b5167286d98a6a842d3874cf80df8cefa8cebfdaf06927f6a3285480225563ebc30b0ae6abb04904ddb79d7e986b9a19bc0e944334f1cead234bf5dc4a21cbeb73c952f019aeee758c65d9f01fa1474a3cebb49b94ede19036f1adae5d0aa15c67213594b3c5aee54df42190e67a44d0f6a1e4b686ff646183314bbd21035475050c0b89608c635e0c83c0c19c5ca6d34f2df4809df17f1b142952b9fe83a1296cefc6704676441ef64a38ac7f96e159ea06fdad09c9e7cb58bf535d576e847c8504668611fcee83ccba2ba928b90df28c0e8689a7eb297d4d6517fb1ed4e59fbd1c7e4068e2610d19484a5169718f76d8205b4efb923e3de4896b5fd41246afe1818a5cb73d19980a4d7d2e8fa7344a27a055edca58d49bb0b7d80514eafb62a4a443b13e733fa67139740ee9460c1c05b309c68c655f6e143a6d8ce66ffef418a5754cffe0329274babdc751d9691ce576137469bab81e856c0ade55d0fac6c1d54be5ef6a67b150d2c159eafe2785e5bf7873f25b0f168ffa77da3a41a31825f2c7f9f20f5b07f181801d85561466ef3835f963a21380cd66625706c8aca2cb5d3164e21a61d7944ea7f0f7f330615ab327db985cd29f226a5db4dc55df0d7dd15da9247fa00e01dbc200fd3f5f26456cdf06fe6f085a7a6941fa7fd59e1bf3d0d55dc030419af7cb2d8f1e2dbd9b5b49e8f3800e423717a3da2429dd8be8168416440704474c231d1157688c8018853c15cd2fcc415cd6fb1ac8e7c986080c75f45d6040344180c331b3f0fb335a0d0dc99e516d0070fa4c2fd6a58509eff2def3636ee89b20a0e3e3b1d602d97bad262dedf7848a185b98be921399aa2783b2c745eba4957c8fc09a6308c0a6fa302911ae70c657f9c4d171c00f207119a5cb172cc8a7add900c9dd248c00d7042da6ff918eb1d25aeb5f0e2d9b02e62a1807db0cf8b43c277c1ac25f0baf22ef15443ce19c8244ef8b3e873083365c0c2870456d08368f95ce19727f5eb95afa7b97333fcd4fd060bca5bdc9ea0de90702d9f5642669d6d3087c62ef3da997f7a93d4c93f4eb3c5d16a973a18b90bb9c77c9921e7af7ad82d52495dea2aa9f00d96dbd957205395f05ed3f30a4811660fafbd49105230c644045523fe3377ab673b45bcdf64bb391d465839442e7c6a6e251b4a2b29d7846af987ff47f8ca55f29ae9b448c5f90d49833d6a4f4d6bcf9b1ce7f7dd98a7ea73d8c65dcc693f1478376cda5ea58ff0b571557c0f91ddc4206245f60eca60c4ce00580317d1b532a078b2c0f226643e3163a8cb120f00c029602a95e1ef0b2256ee2b31e130451fa33a621d820e3ce5371745f04675434cbf7d634722048700cd02499d755ac56ab649e468f65029651947c5879bda1cbbb40ecb17d640c4260d9ba6aa891a632836f8fda94ea231df5317c650746a09fcfbd458f19085b4e3fd2d50644de94b72e6c0c0bd15b323158a2e2210a3ab7bc569c5e3e0fe117acd216982e1048e995122e045d71ffc17813fd02170769959616d7e90afe454057ac7e28e2942511e96b33b59be90219560fed3b2b7da5be640defff5eda52453f6ecf299f68920a37e09304a89227076e08b5fa6fc4e7f77d71483f111a6d6c92eddbf10b9634d201941fc1a4f47b11623866b606abd3927312703e373780e45411e86d54096ef9d0f9994089becf1e614c44669d6c6560a7325079f5c52d37ce599cfe809bb49123d0f91d520e7c0ec0fcaf3f723cdef62d1f893af5f7815b0179682f4e092defb385bba8ae131e880a372f103e5cb2db2ebc39aca303f67bc02d88108723cd5c3eeb9d8d85aa8e8a166f3c321717a9f9ba2530c1205465746699675cdc80eb4e3f295788c8722e128cd4fe7f5fb14e38007060496b45080bf1813bdaa302210a107d69cdf7db25313274d7be84e3f348823c661be5c63ef5b917d1ab46766f9e0878b6bd0da8a1a38fc53842d845fe0ab962ffa8289805aa5923424de6e2b220fe900cf1697b44d0350de384a400e63c57c60fc3063eefeec24663390b2da05b6fc4e07fac814872e6e043cacfc35ad034a49f181f866111d74b90e4834732e08faf2d2136d7c4970cdb4ece0875c26fac6b74792d840be431744775e8778a2cbc40abf381813e2abbb65cf4fb1df73f15d3302658125749955a7299590ec56762dd6accc8d61aa897e15c3e4d64f157f6ef2685a43fdb03d28b04d2c84ebc34efa83690d30ed0e9b756b9a719b04215e6460d5cfe3202b2521a1bd18a245e551ce9b95e6d8c05cfd53284b4d2982419794079275f20a90b9d3fbf9e49ad0824105c5271c2c7e00847fb6f18ebd7e151e5d93552a1be0498a8bd99c39a10fcf2e721ca340e99ecf7ae88d4ef28939b6ae14a925db08b167112579ef8b8dfb21ed6d4450275c35cc131191ea36ced9147c06c0819784007d1fbf2f8d307bce6cb9265addb2588731393e07c0a439cd084682dc3e8f658cfa8eba916a080602a00efd8a1130d78e1ecf12f1af8d422cf62f51bc1b55ac758943bbcaeb4913fd070bab68afdaba8c909dd85d62bde66fa885bea15569b6b31d2c9e6a0c7021ae875e77c0a6595e5c8a2b29e25838277bd1ab32ff6b925aeee51d728eec7b3450ca2695a7642412592471b4246371fb1e7118e48b74679fa162576750b8d71943580587bb82b01e1d54541cbc5b87480031c93b921948889281fc1c2a22fcbacb4b79cd08934f7002c0994f32dd07d1a33e15e18eede506983acd57c87e42992ea27d37bec3d08259f57f42bafb78d491f7381f164705b18ff431828478421e0104d0e0a4bf29170b18d2de611d8364421a5b665cc4c36b11b8573a0cbc9f280121d9eb730e0eb17d9703dbbbdaf740e0bb6b0551ba8468bccfec62aeeb772539175e7de4decfc44951bcd9b1613e250fec93cd1d3b0cd768a92f90812bcaf1675b4a1859c098c116a9e5a3b541820f9bb5a193ec64ad41aa604371e7071710f4f80f4aff7b5a8774247bcf4ec7e12ace99476a76d4bf4788e43ba83f4e114ae65df96609d7bbee1f02c44670b9d059bd330319855c8f573ad840c12255dd382b292df4bf6450dabf18fa9d4f87f7cf8318f328955f5ef380aa8e8bef3b48f75ae35e2de109237914dd2afdc47cc14baa4f6295013d07917249d4a96668f07fa67d0a1d1ff2588859742a79992da00671dd00f8fc30a5cc7ba0f397587222a43e4a134148371107cf682d614682db4c949260bba6ea701494281f95ddff0fedf0db34202c0502e13843e08c369856ee2f101f467ff64ad06a697684a15f4fd76c09be4fecd795d335c39a2e6f9c706b70d574c6392a15c6618f707c6049f8879f1bda1b09aa7f80cce672509bc444569d005fd5932ba023c4dfbc546cb2e826ec80f8a6c8efd02566714811d4e33ac36005a2a1a9344d4c64ee940c2749f680d2dc84cf5604974680d0db69e4c9b5075cb0ab898878511830ef187070313068a0992519e6b64014f042e6ffc9d2ec0ff3042116b9a05f1d48a014d002acf110d5d14b414fbfd66ba9297d026e29ef64cf23e8196e0f62c29236b08eeb234169de64471cb6258e45e38090f353fb9839cb9cbdbacca3ecffed7b025f89f6017ef7ece98dc83e7d9cf5438c68276752082f48211167d3369ae171ec35b7ec650c97f08d7af6af0ae1b22a00a8e03a27484300c22463d7854d7cca6222f7a9abec3212f685ba485339ff9ed7edaa8d0458db617da08d55521504019ccd3447420ddaf35a9cdb6482db3b496bf1192719f3e2702062c988c8427603dd474842bfa7879c978e8b2aa8fc62406c2d025ec8b5b6776da2f38db878bfcfbc6bb708b9742de6a1174120e2686c7e597f50095b0d05e05609d8e0743ed233c9d103b848a33c101fbc7d7bed600550246a3fa2c4b5b3ca439b1edd8396c4995142d2c92472e98d7e789f10e63b288e1f8c7d4893b39fd28a63f09407e389df826db23917f2bcf8f3c9c3a4db9d05c42b14d06e400faeb01eaa45fe67d8e3b37b04ce000d5d7c4c8f9be86a675234b0e97be63a67e58e8792456cbb2431ddbeef8281164f4ff0cb8a20738d9ed3a1eed03ea0219e5ddfe4412b6b8936194e3052cde333422bbb1f9f9ad5da37f49ab90659dc652fe0a4dc0cfc92965f4704422d9b89202e69a74dc10150d63b180d590d75835b6c823563eab5e398098dac618a3ff0878888d3c1585fc66014a857112853aaf55e74634f8707163136febab789054f13aea41804169ac920ff09397306713c81fc3a5cacf2fee8afede4006889151b5d0841cb6b8b7c5da03e24951317dabbb64cea5d2c0ff749797a9b58c457924d3c918ee9c13168efde109afe9b4c136fbbc4c3e26d5d13770f0b96990651fc03846fa50a2990085194a13eb504b5fe21992cabc71210dae6f683883852406883300dffbd01ff1af0c208183b8a78179b2110df8b8938f201608e967ea178b12606a22247abdd2088d3748144f5cdaff7800fcd0b1dbefb3188ff708ae9c6a8195da563555a163b8142c68e2645e73ecb98f405add7754191c33a35cc0f6a3ac125d5c6871751d5ebc4dd921d568a619a8fb334903e9d7d1ceb44686a130d66b6d20b42780bdeea88df54ad7619c8c86194c98cea2e421a66b8126e9794a50083011c0529322068d492cd269ee173eb19ddf4878f8d6210316ca49ce2aaeedcf5526c9aa52c5d87558c53d58833a726e49405ca38b73269f97084b40c0983232c881c58fd7b85fda863f831faf459b91a7eddb35856c303c70db07b4b1ca7b15f2e05189d2cc8300c0fb57976a01facdb2ce13cfa9d808ff66c39570ca062447938b2d3c9f407e3e3218c0b0f11080605cfa432edfe91d1f0b6e53399945d3f58a4ec51dc774d5956791f331cb8691d65e499eb380667226e7058cd9c9f739e8f1b9c48e1fc108dd4f6dc42ec84add75f1b6f5b530f1599b766f62300f5ab59a2727c62b205ef12197762d1418be958b8a9bcbd4cd29ff089f11f90ad0631562e15a0bddb2645cb0fd0dc4bd9b82c82d434e5d82fac24e6a4a8038946698b83e57be2d93fd0e0c4baada066a4eefcb92b94a736b4fb0b365fdabd334e492818300402c0a98abe0b3ea3d27dab6a3ff6607bec091e0a8b373f974659e8ae50f4c9c58c467d22d0fe13dd81ad28a734957e71c223753ab2086cc470f42b4dad61ad63d19065b95158c22f5687f4e0ffa1877867848aa814c517cf16313da4d59e9597a2e3dfe56ea115aa8b5ff7d99a04bda07b105d6e50f3dcd8e1a55430f9900d256347b28e447bce9a7279ba5c913589823f19926d588968f0c1e600e37a549c4db8da10ed3e5c1eed5f79b2abca5bf187311beec2f509a723dcc47fa832470758780995ea2528850994f63194e92b5967db7660b8994aa08e7eb35db7c4083d13d3dba70965d7aa5d3d5bc9ffbe5ebc6c5c62fa544ca5d75ecb6f6bdb6345e821a0eb7f532e2657a123a6672a8fac1306a7e7107059baed9e5acf80cd87909c222148225750be23fe876bc2c688d7b2cc4416775acb72cf2ec957b7815ab8c732ee645506f42dab9a7ccc8dc6ab412b90c15d4b45c3d19f9182398ad48da3786b355b251c9ef7673156d9cd566dc7c7040b67a31003589a1803ca2d76e4d58106aaa9d0f70523d7565bd53d07e0b8d04104403c4ad210bb1cc8d1dcebab3a507629817f1cc36062ad79320cb64294c2760c9c4093b52cdb215250c93554f073f100c2982fd111bf594db8ed6b9985db87e130025f58e64fc516ee2fa55df2338220c1ea5527b1d5da4972c21edf5df419522dafb2860f35d0185e7e03d02cf58e7a4eb98ebd8b69e76d3d5b1a7beb3911977e01687caca79a74af99403cc46f01ad8ff012bcb3d13ca96f4bffabe18acb89e82760d9dcd4318a785ba0c0961067e85aea395035c04ffa09b95254ae74d5691c022458ddb55497e4417b92fd8865681194f97d8cdb7c22ca8a206da9161a12667d7caaa1e65fe0efba1a84443ddb2413d1f74c7f703a3249485351c0c68a93c1a4e85c1b9f022088110dbc1d4f4238bfa87d517ef95ce4fd459139012dfc1007f4e134b9faa702440bae4310a0264a2b8061950468b993885d3b69e243359ef25a29cf7b94895f0635a816ee6a0514f5802277d19547ca58c9325ec858d2c53f23ae0f0acca34fa67aecbe8e845b93b6edcbb6afa58869555cf371e866bf0d288f556452c8fa0560a6687382dee62a45558a90269099e317797d0f179b4001f46bc5efc63409f33117d63b85db0355021f5e2a84c609288ab686219b827ef92c2f0aaddb9d97f4ed27fc7dcb42ede6a94b3d09208412768e4842b6cd61b25102999515e22a2888b545aba1be2eab6ce1e27a8563af452900fd554e3eee274e45028ffbd399bac41f34b16ea8d110b6189fb137d28e0bdd1f4d9085f4065fa42e01d532d637ff7e4430bc0f8afc49c9c866afc600c20239a90b411696cf01d49141c059beaee9289f74ab96d004b06101b2cadfcb7f782531eb1f0507397ade04bb71db6b47ce412d314c7caf7700e2443036cef24913254e46f9850c35306979417e7d59f2fa9a78278ee42babc033cd7488a9af66d625fc7e954c3a33acddfd07d082a2456832e8584fdb8d3d903f0e9e7ef0ed4167e1503c8b9fd94489ef9cd0a755cb1035634096c45b64405b51e650f607ccaccdd75417c3e21fa15d8f2d4ce786fb2b89982f878e1d4c5173ebaed0e9ebba977429fb9c313734ab77883e5db2740f1be8922f6dd2f9b86a4ede7e28e9e9a43ba6bd51db6369b7259f46e2462a0b656bf7acedd4ce59c2cae5087a2c49c8f3ca1f6e2f4a37b08f0a707fd2d92efa0c672f13067c0d2321e687f1c329755aa0436bf2475d146dd03fcdc65870be5b691f0046494be679b524a2e4bc2264df61ffaf08dc1bb5492a47b5dada7d693a0ed376b467787d93f7ebefc060779e8b49a5474e8407133d2440d6d4c89aa31f01badf1e436c24d83ef164f2e6690f07ab9e38dc8190b1c29efdd8572be180ab23a00e04944f75a8d3a3bf8a1e380c305f4bd5d798b249602b4f10536cd40f8871c70f951600a38c1dce051d9cecfee7b7b391fb7c4d26cbea9c7c8f811f386b279804a4b303ba07139d541060a96a3ae0498be44023be9b37472843a89658e56523b906cfd474bd885a6764e0726df59d0cd2dde13ed82d2eb3b6150622f75499cdbccca49c84534e2aebbb59a9a1ce5f91b06e24a4eddd6defbda54c49cafb09c70973090fc37d7d01ee0befc285e1716e01be85ebc2bb2eceb3705bf899ebfa7b59787abf1c0347871deb9c51472c77eb27662966471c1b6396fcf5dfaf43e6af99efd731e4affbfd3a8876b83604d70e350c2dd311c33a623aa8e098bf40f0fd588687fc95e4fb3191bf74f87e5ce4af0f7c3f36f29707be1f1ff9ab03df8f91fc85e4fbb192bf8e7c3f9ef96bfc7ebce42f0e7cffcc8fbf8c7cff4c90bf36f0fd33307f69e0fb6784fc9581ef9f99e22f0c7c5fe0fb6764feb2c0f7cf0cf9ab02df3f43e4af1cbe7fa6c85f14f8fe19237f4de0fb678efc85c3f7cf20cd28cdcc66965c3fae2017cc25e49ae28ab964ae211791abc865e43a7221b9945c33d712cd0f4d90bf807c3f0dcc5f3fbe9f4688660a4dcc5f3418e887198758e5f4503891e6fb69643443fe12bf9f8688a688c68886068946896646b354f35313e42f9defaf81f9ebf5fd3535536a6235327fb9f0fd35433544fe6ae1fb6b8a6a8c6a8efce5c24055ea43e144d7f7d720f98b00df5fa3e4af9befaf99f96b85efaf59f2d700be7fc78fbf04f0fd3b82fc65f3fd3b60fe0abf7f8790bf767cff8e29feaaf9fe1d317fe16ffa4fe9ccd3d7017fb4ea5ffaff4e4417a11c453e327f311399c515315812659e5e9fd63f6622d1e881d8f24cdf3bdd27963c082e8e61191ec244b8081be1238c8495f00c2fcdfccc04cdc0668466a6ccc46664334333443345334638cc20cd28cdcc66967e8260425362b221a222a32324a5d912cd0f4d100d8c4688668adbd0c868866888688a688c688e689068946866344b353f354135b01aa19a2935b11a59cd500d514d518d510d528d52cdac6669c7cf8ea01db01d423ba6b84c2fa50bded7882161e8c7170887ffa1871f113fcd0de2c4259826e636f548c9370bdf38df2d14e0fbb51f7fb9f0c2370cfeca79e9f0f87e6dc85f3bcfd3c300dfaf1df9eb00df08f8eef97e6de6af2c26e0fbb7207fc5e02f1fdfbf09f9eb079020dfbfc9fc25c3f76f43fe52c0f76f44fe12f2fd5b91bf66f8fecdc85f347cff76e4af057cff86e4af1abe7f53f2d790efdf66fe22f2fddb92bf18f0fddc8fbf1af0fd5c90bf7cbe9f83f9cb01dfcf09f9cb86efe7a6f8eb01dfcfc5fc55e4fb3999bf20f0fddc90bf22f0fd1c91bf24f0fd5c91bf6ef87ecec85f34344234309a209a1fd7926be6527221b98e5c46ae2217916bc82573c55c535c422e982bc8f533b334339b519a419a39729b19a399a219a29919d94c6c66ca8cd00c6c2668e607e31956c248f8081be1224c84b10cc7a8c0e1b284640b20b82e4aae4b92eba2c375f9c075f1c075e9c07541725d8e5c97f1ba70e0ba18b92e1bb82e1ab82f19b82f18b82f17b82f16b82f15b82f39dc170adc9709dc171ceecb0df74502f72502f70502f7a5c87d79c07db1e1c238e0c2f85c98065c18065c1822172636e4c2c86ab830430bb83044345c98a2192e8c91900b73a4800b8324c385510a726166402eccd28fcbfaf1715941315c16ec745942f2a63f01973545bcac58be2c59cf650d21e0b2880e70594506b8ac1e97c573593b97c5e3b2742eeb7573e4dc1c30dc1c2fdc1c2edc1c05b8395ab839646ed38f7373b0707310e0e6b8b93956b83906707308e0e6b0b93996dca63fbc313b6e4ccd8d81b94d3fbe3134df4be902f82c23240cfdae4b71205d70e142c290a348def4ffad43b712b94dffbdb5c86daa8b504fcbdc7ff08eac24582f18e91479c4b191e2208f38a664c7951d4732f75f82407e0a769489c94831e42f1ddfef49d1610f7d6c58cfa3cc04e7932759502144040cac8cccdd21739bfac4ca6622d6e1cc8f1c9599c1d7dae922d4a18b940e3b0200f8d673600402f08f5b3e3f5d30451a3fe2bf13b1755b3ead977919cc4412010562eb9d881e0885a53fd623249715d6f3588f706c87ac5573246ffed9b02e42feba2ff3a37581f9eb62fc1c87631596fbc11f2bf38863f4ff4a51c7e5ae1403707f4c2206c0fd3189d26d2a8ee11895b9526c5d29c65c29ba2461ff081c6bc168024e58eda344435153260fc52f45938518c89d895f7e206f9cfbc9dc1152762f62599d211c4d29f49736e436fe9a92db6cdc698adbf86b435b18feb358d66a432c96b59b9112be62c7cd28e834c55fdda3a6ccb2ffbc2ea758f67f29422db918cd207577c615f3d7ccc7c697a2ec4a681e67904c5bd897a2c9e392c1cb0fe48e8b06f2c6df632d2558ff9998bfa48facf1433a13cbfe324bd97f13c218f2f5491ba3c933250bf3fdb721c82d0c994337a3ec2d48c8be8515a9e663c6b8b81669a698609ef497b4d328d7223bbf28aca41c1de7fcc9f3fd7696337f9d333187bbb0fd5e4a0a901b719bf9afcd486ca3ec40b37dd33a07817857eb11daa1e4b88ed33637993a24dcb57aa9944d08596effaa117f9974f017d73ab388dc33ca64a1b7af2010222d9adedb4c5b58077f68df12428f508a8fe4692477baef1fcc4d8eb396c5facf23c74da31965f2cc2bf34aee19e5b6e8993e23fe72510af98b9bb94d3fb734e5271624830d7542b939218c721007d4e1cf95c9c3f9f083bce91f27109af6e909ef2568c3143dcca407d1843925b7491d5193c83872d5fc24dbaef094f44f966fcaa82a9cbfa9f325c386fea6ff26557cbaf76fb23df7234e36b58868cf7d4bb67cea9b5a12fb689ce93dee4ad151323bda6c7a071930c5eda5f7f4534bfe92995bdffb6cdf3dd892dfa48af74dbadfbe491510fbd0e7bec9f6ddfb6d49dcfab04ff71b6ec997a2877db8a7df84e296c4dfa1e943b731bd14b9cf91c5b6bcdf5e8a2d0fb78874bfbd0ddd6fb849151f7fee9bd0efde06ce9f7b29ca1fff89ed7bda647ae92f97e909afb64f29732875da617757aad1323cf749678270ad7117b6dda94f6d0446b80b3be545a2c9e47f51b87b6b2d7881f6821e82562d0909cd37e786d084d8b8242438efeacc109285ced4252161aa943ba3dd9d6adba6849d3a3894d25a65582c4ae9465b88fb6ba00fd7f0367302aa6d529c7389c770ae33f920a5d3ba2510b699b6b05cf8af83a3719da9cab0589367f250ee6bfa26d00785652255dcf0f6e3363328f8bcc54e9ece50cc0ccaf2046559c9ccdb0c11d6d481429ae49ae94b4e9b718392fbe76bfb99db93f91ae236d3e80443a260bf0e7bf26c5a0772b2ae07accdb5aa699ab6851159abac0e150084d4f23a96c2f81c98f15158b04600886c97a515162c7d11c58700a0616b9656a20c61a508a59cc41ade94c28403ca5208297f4a9949c89dee596e58354699c4c262afc82c5c2bdc20bfc7cbc8979c7826a144a666e47e236498041272cf1618a184dc2a90216d411aee13266fa80a9a307fa40d7def823e603dcc72d380cafef2c725929329b9bd27b7e7905b86cf5dd72caa373a20ad8a098cd30f38a845ece087263e2021f08d21c490c21604ae4488420d620b3162a005914a024a0a1ca021702145ac09207eb0e28d1e020bbac516486c6f0c318697c715b2bbbff5fc7da13981230720a1b08519db1b43705a954328e241129d1652100292d54544128200a5880930f8410ab820827145265081e683a258f5c1163f04111dfaa3ee0e33abf00c1da6aeccb81ec5519d206f669f09ece8332b58a4649951ef815fd3afe58e9dedd7a32cfea92b3b94a86fb92ed58ace51bdbc4b1963854d7df775a643078b655b48da5ce99cd5fb57a3ce49bd7fc5d239a92bf3eabafc045718499853efa5aecd9eea43a1c28c7ad9a8f77cce76afa7960c34af9ee6151e9b64d5a7eecc2fd795dcc6bd6fb92dd4a14bea5137f5de958c72a3d41d1d29a3b264146ace72eae7cb2c1da2264a4ba5fc73a432aacb28d44ff99222ea37d04783a83af3570b499163a5f018e6b1aab02749f159e7b498c0cf935ac85f1b12d782eb6c433abd9ffefbc1a6f011d4a35ef51b2803ea53efa1ee8f99a5bc51a52e7dd5adefcd0ff529d4a3b0ecb051cff538eafae09450b0ce5af2e8574809cda3cfc67a45c9f4ff193a47b2b655c374e149d738c33adb290dedd1af412132a6ca62b51a59cea7de6b4410a26adb777a99fc4ddcf54d200dae91c39f899bff394e8f203e3920e1010826252605d195cee99ce49d2e5015af334dd3371e71d6dea3c185c79609da24ecf852494f83b7b3942c8c9af70d0a6992b50f3bc7ebacbd09bb34383295336b610bf2c6c10d27996187b4bd7dd6137dedbe80236f5e7260c7f7ee065ebf87fa31fb878340ec373fcbff417170e89c5e9d33749bf9a1db3448c489e8e8546d037b82442648a4816ceeee9eebfb00011053ae18c88f214026300187f231e50df76de08f0efcc199e4eca7281ff43d940f10c8cc443e5082200882d333259971e4eaa4ce8e099e748d30475bb873f2e67b5a077f1d75ce94334e86949ab6b06307cbfe9e1b1dfab694fde5369337ce1db98dd74037a40e37a33ca28888b858b18db98d8361fd37a42d49a973e6c350018cad0a6fd35ee2ba694e44dec8b74d58af55ca2b044f4e28565a718292879a9f9d7648587f2a639322f924fd5c089eec2daccc316737f636ea706eb906c99b398b70f79e13069ea49452ea9ecdee584e303ccf64832674e01cc7b574b5af31ec69220aca1882f4c081844b866191ec2fc32aeb0d2c96b5d4fd871e1df60472a9c69422674152603f55872513a85f2c7fe9f4c87205cdb1cb7ffb206e6367e8b03fc8e7efdf13df402125688a6d873388bf8ab81f55249d4331570403fd754a0162ca4604d6b4e2451af90668e040f15060d8e93ee5747fad7ad849a1113689637077242afcb8e0085cb4414692e8ed82ecee65777797c9734a39caf4c8d228374deeeee209b9af04215bf1028adc596c8f2c5bb27c9d183a474a2973e400ba68220bab84a7554a29a5f552e9d8092bedcc22db8cf2d48eb447043b5f22d98ce613268b65adfcfaf52548838e7ca4b6f4975362253453bca4e67e6f281ed7399afc8dce49ddc6bdbf6269eb4fdc3d7477119e4b9d7bb925d8d95282f16758fab2254f4a2992963cdfdd65e40dad4e9d622ba58dc2a50beba61ed9f4d4dd44a9e947fa1ced6993897bcf744defa6af8ebd9a4c263c769873ff50e7eb159e943b9226216f2665b5d426fde6b4979aa5b4a58cdad7f7b4dada4bb7714aabd63d5959d3f0e8df2dbf623af6c8f305d60a79bab89791368ee517218d90832018e1e00745fc40a588ce91a02b238094e0b8a28b32d668c11862fc10071536c041185e74942a74393608e84911ad4407701cf1431950cca8010b9cc460075aa40193020ba63042afa00b20ba2388943133630da1280081835681c402041b51f080882a32286a58153a8a12826d77c72142f5c81f437709bc1882104b08d18009618894884cffe9132b3f78639392873ec9fdddd3460091653e21b16c295bb66c5a69a595b6d4ef295f1dd7dd653ca672e3f97db1b05aedc417da06b7fa696e8cc3e1c0aa701f8d711bdfe4e4cd7479623f735a9658a8000979c4d1983014abefc5606464391256debe9bd3e1ba570078afe06fdf41fe7a3c8774e06fc31ef8d25f32610c5ee19173171d8c4858f99b4ae7f8b7c7c2e307e9b07a413afc96b66ddb6a90cef9b60ff778173b613d2031e02093cacbd7d58fac961fadcb8f5cfe5ef0a4d261589f0661f23cebf1e036f5bda7a00f6f52e91c995f539c4b7505643b68d3be2914fe88c32559efc7b994038f1c0b8f16068fac173cbe0b1e433ce2e4d59d3cd4af1c67e592cb1596562c1d24c449a8a5a0be7ec73ae7fbfa1eea7a9abc5c879be68d93895cadc8bee1af0b3b84e3aaf778900e67710452e6b094b9895c1ae583d00f82322a4b2b5c089165ca93aeb105a3fcf23d184a096bfade3ac70a9149d9e223e0cbecc9378e38e3c79b600dbfb0238eed88c99def9b0632ecef74c4340bab39923ba7a789c91d8af37daf23d63e64a0d14588ab3f997ab84f3f999e301eabfbe01e09661949851da13fb3047dd02762fe79bcb801114b0834e64defc5dc6a32e17ae41284056c88ec593aec7a6485fdd7d79a94fb717a5c61bac4f04bffde7bcf70d89f5987ed865b580b917faedc63981bc77a075ffc80349b886247cabbac1c57878c757550d111fb8b03f30df34bf9e5bd982599988c4c47918c8c0ca69f366778451b4158a060c710f83826775057088a2419118ad0111b710c47e783821d5d8470f2638cb3a81f2324774edf4bc830664a4c2c4791dca18f7a9690dce95e26e6b9fc18b3548f645c6296b0df1f33a55b7e8ce954ab1f63966862f24686e2e8f8d111cb16b682c91bdc42246f4c482625d3ec9a96ae098e0efbc8c24c41f2a61f3543cdfc85e238d40c658452e363c37a3fa28c728fafdc0f73471d45b9ffe5bab45858f561a5c2317711aa3ea46efdeaed2c1393a1d261cbb4beff5f9ab03252742882ab444a3002017819dcf291996c18010d2251e69d883dfdd2c557418b981a296cb79463626dd331b1dc984ae778068192243a7cc0031d407264e480910d68200318b880052a9003052680c30d128800048a3cc00607f83480014486d4b0001a6610a200198200f9e123069a0488b907010730400f9e1d1e3aaf1c185e70a1002de0b0e0ba3304b85941684a4c36b483c85f4bbe7f47118ec91d8a9ae5fe51f2a0669285feeebdf0d6fc8e8b5f00377c9bbbe357b802f8015c9b27c05de16fee007ee6defcbd04787a4f79c905baa8f7ee0d80e8cdefde4ff4fabb17143dffecb542d746e9b0ffde936861f7a2441d5474c8b08e988e98920b1482ff41879f9f9f1f51040f82bbc327b920f80fdc24afc355f21db81f780f5c1dfec8edc023b91e780edc233f5e24bf81cb813772c7cfc0ddc06be01af90bdc0c3c06ae06be02f7026f818b81a7c0adc0e7702df0385c0afc046e0e2f818bc3df7027f010b812f808dc1bfe0117025fe446e01d701ff036dc22df80eb80ff5bf33ed78627721bf00cb83e5fc325f2432e039e865bc32fe00e792197869fe12ee065b8425e017786077265f8205701efe302f92517ff8f1be469ae8f8fe1fe78f1d27c026e0cdf73c5cf37017f80dbf308b8f97bdc03bc012e027ee7f6789e6b80d7b93bcfe3f27cced5f9d7e5f12fdc9c2a993525d9512ecd33a638aa81164804810c23713eeb394e4ad644aa67e050f0a3e0d7efa2f96e951d9a8615ba77d1801486d3075e2952d4f75d096229b1f781940c76a268b0aa8fde1691d3835fe4047ee005025fba8d279e40eba552df4767835c8763ea6d053a78ae3f6f7edead22b90fa73e60ab57ab6bdcf5a153c2505b44c0ffbe4811e93656d82472492a6527a213b169c59bbd40df4bb731d5406193c8a52c45295bd0fe87cbec77ec21fb530d14e242f6b65aabbf67edab207dce35f7eaee14ec4ae4495715faf4fa50184ae01ff5b01011e43a76991ea99fabcc4e32c779df0485781c71740eddbef34c3f72ef5d204fca16b8f9c349aeef7d9f5f296e54c82a6f55ba39a7a99e3abc435319aa51956d661f0a03a5ed3e1d9ed2867622e0bcee56a19476b74a0529a5db2d125425894e3e12662762f79e0465708a47edbb0bd43d942a4974f2126f0104e224f7d3b187910a5965ad06cffedb9dd9ab5f77abecd0597bcf3777e7ea89c33b346d4116f98aef88ef082051983a4d9b796269772b1652ca90524615b4d22264e1b66d63011613508992a59a0e30033186c40e73b4ebbcd16af807fdfa1d152d00129998be874e02094062157f27a2d75dd395a00f6eeb01e7b67c3ad36df998a4dbcc37f57815b256bfbb5234d5c0caf393ccec01569ebfddb1fea6c9669e588d22d130129614ebe532bd5cee7c6b900d9b923b32cb2904319c60855ca3ca0c5d44460d1c480b1e48a1a80bd1a3b30540305043a457ae5145f4aa16704041123dadc8882755446fd3820a133f103dee9312c54cf4b8af4a232a4dccc48a397a5b3e9589cb0cb1623bf3c4861d4e69aa571605c18327447a5d6688d2d5dddd86275de39de596cfd920109457a4983134c20892852dcb614b11826d5899b9fc2d750efdf9146cc37ac044948ff84761c7ae38b9df93a0fbf8cd5cccb02d933726220dcf9f0e4fa7d92773e84f4e1c4d5998aee469c2a2c957f76077addba050f6b3a08f1697a0318ebed908237337acb52d150632e7e9d320e4f9d448eef83b0cf27c9f0224f3bdf1cda44d3f8c14b9ff6b43be64eae3ae13d19372f5cd7c7cee26a26ff62d75d85e10442722cc10d888891d861982913911b9eb44b447b0fd49b9a974d894527f1859e7d4dc0f238510b91f66a873bcdc0f03a346bfba198c159d23b180022837cc15a3dc3217e411e628f707c3028a19e4112629378cb750eeec337cc1841bfddf6c4626e5b1a58c3ce59ca62c4c45b905090607b96164d266862ac82db03ce55219798e2aa43ce7c344215fd6bd0cfa98e08f16f09b73fae775b78b98a5a048e4ae64c30bd173f9badb42a881124b2277a51b6988decbd7dd16a45489891eccc7dd8e7b0e33a14b90a224764e444feb402000c828c99dbaeb44f4bc967ae9fffcfe99feb069ab0c1bc4c8e44d0be99bc1c8fc67c2c8c2f0a08c9ab9a898e5fe3a1d53d909f441af9d4ac0c828cc108591e5aed4e9fdbaeb44fc408a79e8d0e361e94507768491c1c81c4606331f46161386a55f6384602b8c0cf4315b2f7451642472578a77a6f50b4fe84cde07b23c93046dc7cd130db98d3a6a0946582beb1158d26b45a2569fb39b02124fecd0d94ea59461f56cb1539b74cb8eff0416941c27a9b79472764dc368028542a1502814ca89eff4360582e05b107502bf6bc1995a5a62fb39df544dbfe126de7429d196ccec1ebd4ebb0ea7bfc739ce5a79a22c96b52387c7ee65d7d5ee7b5a2671f087e70e73f2a6fe89091c144ebf110ec89dadbf763feac8dc775d8771c04f7d55fda7f37dfdeed20ebf4f5daec3cfbfd63adb3bd28ce224d1c8e54f03e261fabd4dffe61dfbe79c9b36e79cc29a4cdfd3f39a7af66c1f663bf51eb9e3d9bb8d721db71de13aaee38e70df4fb9ae67f2cc5c2bb771db4c150465514a29a5947263387eb8828e8bfb426e4e262f1fe5b3bd867b68d93045eda7a8e1a04fdc3e888a40da43e17ca8b861283fa044ed5bec69adfa18a261db39f4c7123598020a2b60c83c28b04c39b0238bc5f2a0b01463a2c2f6d29209072e6b7812c736ad476addea56ebcbd095ceaedd3df1a6d524a80cf66d09e923daf67db59fbfd5d0afcdeda50adb9da95635af74a382922ad5fca6ea5757b55a7d4fab56ab956ab552a956ab55ea57ab95cbaf52bf52bdac56aad56ab55aad52abd42ab54a7ddaeafd57abd56ab55aad562bd54aa552a556af4aad3ea55aa956aad5e55ed5f2aad5ea532eab4fb9acde25d5b24aad54abd5eabdef5bad56abd54fd0c76af528946a85eb5bd47d797a6d0ac645d5a27a515d995b5e7e822d2faaf754a9d56ab55aa956ab96a7b925f5aa3b338ccb4a3553aa4fb9b4b8e03aebd0bf45858fac5ef5293cae3eb57a55eabdf9ad5478fc1ea72d0d63734b1ca4504d6a52d3a8e6dd0a6cd39ff4ebe6ee9ba443d879d4a19cb2bbd6937c5fa29f9cbe52c9f5412bf267edf108be1472fa0a3ef8de7677e0c00e8f4572f77d95f49299b7ede9c7bcf6600d376bb83eaabe89c5b2f633f7f43d8e729cf6fea8db48c28c6ad477cee9fbf1742ab2de9fbc3f72fa5314de69dbb6292c0882b8e7a4de3e58bbc906b8b2b482e54ac6620511a414b941ca9ba420ecf420acd7d339adfeec5fa570dc6fb848de6c26f0c7a9f34b1556fe4f9e99639233d0bcc3cca6f736c97db7dfbb9f938e987ec91d4fbfbd768f9c9e43e5ca715feb8338c9cca64bab098c2cd6c1c092fd5d8dd3d2b30237b27f0cdfe35f7fbd47b45f32731dbbe7fe48f7f57d851b6fbb49b8efe8f6b57677fb9ab51d26d7399ef4e672c563934cf169b658e149d78f373f3979fae7e8a9af20f773fd9a4a6ed3a677d864a11fcf2c53a961fdcda24a73e60df3b94528da295f9388fac442bee8d745eefe9311769c59744e51d73b8f3a9c54a3b2c1efc7ce9e943c3369b2e0efe17104b9f23089ee54a343ffed9be56d12258d967b107f3329b70bb2d75adf4ef96a98b4f11d16c84622bb87a792bc31c2366c56d92725b39b4a4a4979be7647245f87484221a84cf13c1abfe97e1a7bc8dd0ea451879e4a491c6453b2ea515420caab4761a192558f7a8ffb3829a5cb87bad58d0e25e8e33381ad491d1615b172c894447dbd52830a572cb816c1ec9125337f1dcc1e51fd929951afbabee46e744884bd2dcfea1c7fd49d3965039bfa80f0d974a7a8a71985c726b95ad1a1cf594671e08f2dcf2b1da230ea6d878e51980564ab5287fe295c913af49f499b92dc49fdcc1b1aa94fe196ca8234a03e95c22dd586d439ad142a95fa542a85678062513fd619d22c8f3e933ba9f7f750976b01b2f653a877d5fb7baa3b738d82ab706b75026940bd4a855bab96eab57781a00ca85fe1960a0b09b3ea55f8b3ea6b963d832c5f46eec85aeba42b6cdbb605e99c4ddbb64dd3eaac5aed9c992b4e871d3652a833be30c30b6da58ca33cb2e2b04f3ce99239c7f5518799532dbd7ad9598b1bb9beec83c3431283883cf6984757f248dbeb4feb376a82ecddbdb9e62a87bd8d9baf55ce39d7aa341ad223dfd03d720f2b6658c9dede34064899523a6484e5c8a88d58474633644dc9416544343b6e1daa436cd83a14b272ec605d59a6b429a59456a4256418863a2ce1418739e34187f97d4f0fe9913b59bebc573240d07bd123084734fd014c3f4a2e8238117cef0241d111c10fe244d403017348452a8612a77ec5895155b155087e216f546c7d8fbaec494f4f7c3e27dce21e7c29fa3c6e71b84504f5e0db807a1037a9e2737af09ba03050908ee83d144e34bd0de009fb43e144efa5f8e1216e43bf27cb2f3205f3288960182517458890661371dffcd180e8eeeeeed93bf0fafbc0d3a99b9e49123d8e6b86383a7fc4e34ccfd4716a8001eb1bfedf08fd274e6f4407b9c365fa49e48ef7f495c81df0e92fe1a173a807de99694ff7b427fd681f75c9e38bf2b6e60f39612e0812ac7fcfe0bd3fc545dcff860ea9d36eca7146f0f83df8de77732872c32c9203f85f0e9d233d3c442748874558b060f1e91c24fb47a1ab731c531ac44a1957f268f3b332e53e18c745777777f7ec6e8efbbb11318535265dc2e5fa81a753d79ec9eb6ecbbdf7e9defb0f3301df7b26dffc9a8a5fc52dddddefbd0bfec9c475dffd09bfdb80f77aa68e4383364563d266a2431a9b32a5c6ea94f92c1d1e32a70ad9b0222143fa638f4caded96667d6891b0ddaf63c3ae22d123531eb3caaaf0a48b2a51b2c4e6f949bc0a5aea1fccde906f693484368ebfc0373dfd2072877bfa32c81c56879f97d392214d32e3a0626a076e079b1dbb3aa4a8ffee29fcd511d6f4a697a2f6d5faf73ea6ffde2d66f23d13cfbfe977cb13f4d122023eea7d40ed51d8e7dd467310df20f286fe77df4db7754506e95284ddb01d3917ed9cd4ae4e78312f54ec685bfa2be5e67798f37a411f743ac91ad6260b5286f3adb4ee55fc6cf4ff757a8adcd02fcb92495f810e28a8480e72072653e9048946cc77861de551118eb396daffff96c934411f9e3771cb49ee3b3e386f8dcc3e783ff03ac97e97d4fce1be3dee5ddb216d912100d9ff06c754eab4b4b46139cc7548b52b7f36275a29bb1055d2622700a2b895401f9abcf96e0b3ba27a6290b9ab697bcff33c27995ed3739db65d0dbbe3ced267c0badb34bf677777d3f6ee9e57ae5c9961e628620d25a44cabf721c1fa10b5811d25564287748e9ce2cc1e8edc01730f923a47a9737a6747a6f42791dc61656a839029c78523c18e8f437f801947ded0b764582d480e240bfef71b11290281ffbd0485c80e9b890e694c488ad7efa4e57f7a2722785bfedf3b118bae8cb335df7bb7a2c3eb03fef7f31201ffc34caa38f6f98e9ec983611ee7504f257943c597c214a2cf71d6d2f79cb03f873aa7c3417e5ad13959c81da76a1821098ce9c5cba76de88f538b4c3fe633c36e3808c759cb628d619e5634fd31ccae9e432d6d580db3be32466b913a2b39e0b035097536bfa25a548b94903a7cbb86ad4a156986fe59e6d4291dd6a125b2a8456cd8ce632dea91375e8b42b7a94b64916518f3d85887e67704db5f67146812bd4063ba2b299ddf61dae19bdce4a6cddfe47526d33451ee6639521c44a7437f1c3b4aa41c8c8c20993f27ab916c0e9d6304031689bc620611f9b39c53415dc77f067518da35ec14a9c8c308648e3592b39270f85b27d8cea30e0ff24647ee80998a15e7e0362b384620437f236ee3ef5284a59dc5f64f4f22ba22cc9e47693401222ee2901571dc188f4b7c68888a215a895c7a9022c3081c3cd939b21f8f483e6e0d4a59f64bfaccf892bea40c46f992de80030e2f8b87e421d17e714f7570a8144343540c0da5e171fde2c62b93929266921a4af06a5085e5468294e973afa34cb187a232b913001e42f008e201cb3c887804d1e601f33af3a4dca95fabd35ab9973f76399595f278b3345d296d2e776b6d37deab7bbb511d456f75233b13b9a76bbbf175bb517d5bc2938d9b3e11eb3b47b64b4129a5747381d741ed3c5172bfc603e311a23c4299a728f7b45f1e188f100f3e4f4e1eed27cb0f65bef34c77e6aebfebaebdef388ef3ba9f5dd7756faa613e97b9eed2dcddad7beefbadbbeebafb30d76187db95dd6f5b833e3609cac0fdc418680de1dc46fb21d985086b24870e3595ccf68f1249ce8cf094c85fc2e5296b0991bf7d67cf328714e40c12a2593cf88bfbfa5138ae88e5aca2c9c456dd264adbd41fc2598e659f559dfc482ec41183b84d155d88b03336a774a892d929ab52ba5837838eca0a583228fa618c5c6b665919e38a6c9a29b1429656b250912d929531cec81f9631920490a5952c42e4539656c4f0c17cc2001c229a84420b55c842892a8690de38a263db1040143d41c2ad4da3684be30adb72b3b432861219ccd24a1a37a844380162092025981054c41a513ce1c41351dc108a021939c841161fb4aa562b165180e24515573801054d34d14c88a1c5b4224616464c11032a6734a103358a441c31acc8a37dcd5f6eef7c82086e5011fbfa932fc4994516031a887d35a42f44176860022491c98b0a281c41ec018c024610a27f5f261d4cec612505d1f1a42e40f1c5510f3d27acca788478a2f0c0788ae011e21122421a696e68330d0a4434ac8580c1907c42087182a0134c2160486ad02bcbebe845c60b89082e8f4b6a2b69291869ced37d73ea740e51adf6572c270bfe74fea555cae9d4b194f3256c8acee9f17594c725346b23921ac403c64388dc3c82780c1185078c07ac9a8cb0f3e5d74a399c5c39aee3386e48e3be5efadc1da714b956add2cad1d7ee562b45025115ecfcfa52939ad4344dd330d7e144cae3924a97787fd799a4eca8dc362c270bdd6b72029305ca4396b47322e9a4c7353a1d73b40a540c79018791f428d9b3c83ded511985d20278148f02075215485c5d802fec283fa60a48540c79d18242a5067d6bdd6a1daadb76b79f43792a695264ffde6a4fd7f75b9d1b976cee57fb4d47c3234e76dcdb3654eb56b75aeb3654b7aa51a16ddae6dadf39d4a16fb572450ce56009a562c88bd64f921515c8acc09848ca1d652635016826cd2432e69559a6c3963ab0a34cd892fd674b52a697b28ae61b76fcdcd3d54d25cf50ca1c862bb07ae4269a2f03db30e10a3830724752ea3fb0728474c54a2b62fce49135b312c62c0ca51410acb4726464e5e8c7ca5191953066703861e78c0c14c0c41e3610e7179958fae84e951a76944b4546960f96b821d6c894cef9933559d9cb0186ed1c3445ee81a02040e43e688adbb78670b825bf7b29caecd3e2865b12b7b8b761fb0e37a9e2b3e196b481099325a2a8f12372b825f190eeb99e969307b7f9d95c88b039e0221dce0f9ffda8040d35a6490c154233020000000000f314000028140a878462a150284b6455d90314000c86aa50684c1709a420c75114848c21c41862083104114444484668b8011e4bd82e8c8d1b6a73b1a465a3c7f4085b59df3eda020849fe15f0e7c9eab5efc632e7921ea28e00c1c021e15014ae5a94779e7a1b77bbfcd0e728e76ebb03c508832c9128bc85613bee27218526ea4e1c383171679b33c83a5a6c15d8d20ec7b35581a6ba8444d66ca59de9f9ea5531089771309dc2caf0a1192f9305a30d598f3639c523e6040a8530c9e040b5fc3dd30ab56555df177efc65e63f2ab2e8e4b321f535312424efedb62263b7a457d42e0a4c6af202db2b3041e7b220d82c4c253fe802f47f8eea2de7873f326c03b3c6278c95147eb9afea9f058f0db93d21494d320f4f685294c0eeb6f1de380985438a8d96546cc7d20f1d6167a0272b7c8643182814e29dee337bb99ef6d239323a00c3c065012c7aaecbb64081dd506fe4da8c2f0abe175ac61bb25f3d52ed68a8a9deb40708185d706499488ca9c8aa49b1af6de49ebd647b7879313c6c8d5c82df0e6e8d94f2b927405cde685f165054497aa92d2c0cb2f6c8e4ee26ca2941c4f0ca902963035f37257e26bcec2a2ec48d3c0c67010fa7ca0dd606988d26a4f80ae2c41a5ce318b5e55f920f2bfcb3d2ed48085f76d06c3974bb9f7840b20b3198b423f45f749554fb8f605a0740770aa00810489255497c4dfab2e524d166127f94f481b2250c9154e23037a6148ce43c92d05792605e71ee82b06c0a438301c8c42ff9a663eeee95a95aa10b5c4bffd437c1e82d91c3916a2fdc61838b6c9895e430996267f187fb9b8b3201317f085eaa4005164c20874d620654f0b4895443692436d9eb3723e94127203da5d82c4d3412855a43c6c3200487858dd3a5193b52220c17bb4803656c0edfd9afe534baa0339aaf907f17fe46019c64d1f772b9d9716de1cd0595e16c25c4df02c03caa458ded593aad0e7e7cb917a2ff0123adfcad2bbe9e8b5dc60189ec16e85086db52c1b4d8d216dcd47fd74ed35e3dcd7b75d42ff0717106fe337399ee227479aca6a37136099d1298dc42836a4a324e1e8d0c5c6608b73fa2e49367e0e66837e952013dbf6d6488a5271211449f4c498674e146c638e8bb9ee1e65a17eb7b35143d87a194a98d88ce1137ff73c5636c00be994c8bc277cf711fbf8ef26a4757cd186094f545d6c970f4041f4000f6a8d1b759d6e2aca69df70e5be6cd32733925e5bdf8f10682c12fc8e7482230d918ef22e498bed91113e4b9a2da29dc4ba09af342c8106aad42c50cffe96e638feb422aa1218ef930118747d96490d81c411058738a631b274b53a51490a6c21b5506bd1195dc8d28e50a99dafbcb85fd80dd5554430d836c1b71a6763d835f68e08a21c7aed8b7020051bedff6f63cfbe99b814a0a744a253c1c5b499740528449891d08f89144bbfd07894de92aec0e3ee9a175187b419b1a910dfe80ba53139abbe66f07f795a1fa51fa1c700e0fd6fc332395e0dc5907a9f539dbd4a71d5388dd4f68e4cd62db23664e25600c63473ca691dbb6d53d539c4c35f8db2e9463090941b820fd59a998c1ecbbf7d3bb8e72c1062eecfbef5a732cfa785112a8cdbda865d3ae90225fa79b2e02859c06a8f64ede7c68464890ffbe81847bcfb401ddba72e7f84e9df904c847017d52d7a9c1953fd00d62c804211eedf3a8939016dc7ddd6c6f243e71b8cd030c3b5b25648e55409699fc3e215761e9ad7ca0e42026eaa6e16ccff8cfdd33e61f226ee430b261a9f4d1b0b7417efa952cd43c81e214c4c3fb8805c17852f18682bb2495a213f96f8586628db195b6a3e1401ca780e8ac84a51fd253a00bd85682eed4b340ba93981111f5f78c3b614cf8700111c9d29df340512226b2ae56f24bee80be6ee009d698cc575bf8b53997c07a4350be95adf02ee8072575f173992ecd9c836db599cb09cc7171c57913ae885fb3367268ae78d1ed53ef4f2db32a7332c2c965d4367827530b116d447d2e5b1de45a9428b854fdccc4079b6e5a2ec7c4ceeeba57ad7dfe3b82e0a025eac55455df4d8a9f23b2a0f7bd90497162d6295a84e92a61c06fb79aff40dd032818088d7f884423eb46185ae80b53d5da44a62be3955649f463a2b7540f38df896b111b8aba17c88b6a43c7a011367757ca2d2dd5163209400c56b795b53ddaf43d5cdb71932c20b9b1e8bdd2bf0958529baa402834bbf9d6fca770850c59be9cbc72113e85b69bf07dcd2a60fd58035df1fc14624aa1967c55b9737edd8dec4b526782162cb84b9728c54f52864a42b209792f33efd9ff2c586ad95a0e7387a12596a139b34d7f0421abd71aa3cb9bc23e4b59a3b0c21a97c3a918a813319f0117a6b9e390d3f3246acbd05e72df798df587fffb19959bbcd10c5a51365e463433bafa12d5e4740b34b98092ffb810174a14beb241ad5fd7436d48f13a180084a19bb334fac07bb8f8fdd0858aefa312b2053c133dd2bb25e3fd3705e5da2742d666b9488eb19761e889bc551429d3410d3779f1ad7e850ee8d60b8eb60f38fff7ddc04f3e33d16da20ad1b668fd75bd2754d02a9ef68e84b0afcfa6aa416002611ee95552711a07480ad61321a4a25db941c5c0464ed4cdc970424cb89bfcaef4984e76831f0ab54ed72affabfbec94a92c8b26ee4478581a55edcf65064b0fb477fe1c4fb2610e5cff682bc68c84d8465d79893cb70868f3ead3d908ec36a2d91b022553c7359438770f95d936229383d8a09a2fbcd872342343d4b614cfc5d532f33c9c282adab692797c9e972d9a347bdc8c7197ce785ebc49ebc088c7a41c3d12577528697d283a37ddf87827bfd02734ab9b8b2610a12483bff2bca3d3fc72efec69e026fa471a38f798957af436d37bdc110d3a0512fb2c1861f8ab30231158d7fc1289580e528323e133cf35d3e280ff9dce06d4baec07871bb90c68cc88d6445afd4df3b26719ea1eb1740732ab2f6d6b9e9b573263addaca7ccb0bfd5846bffdec951dc62fc80b0e71138f76a474177ad6e9ce8e44c29c6ed1c97541e6996e7ca01986d8ff3ecdd5c3c963a5d88cc243ed71520db18059181e7ac73963df6b1115795922fcc2b2be603f809499ba479afba4c449c831955b6515805a583fbf2ee87bffc4772eba89ff52792789e4a1b0aa2294aba15c342da063a088a4d8e0d98532d09767a3252d5143c5e3c062aaa7d8c7ca5c8d16a83e88292740f5758185ffca328c68f4ad4ca7cd5e86ca9f6d2affebcabce9d6422ceac08c36531e0646ad2788c7ebe8b7377432d0c3e05d5c23328480bbaef1171984e4fac33fe8bc4fc391e1489cd89f8920f52033023a543b2d02b92c4bda76f3d5125f96d13d5f217340d3c1977746b92100365b746111112ae3ad1c5384d0fc6b5b2abc251a2915653addb605f02fa83ba527af215f79a99b6b6fa393570e33b77af3350662e769be27ef92d12d7c600c60afc68dba2d4261c43730612c2b40120a7a8e8c4fa1367cdec174a9245c46952f977950c6108cecb4c11cdf3ada3501c1da9762f5e182ce5556b6d0dd90841197eaba1ae8f2bc92f73f729e18a5d2630e6b358a1455a593b60fb1c3e9cf7fa1db7b78e127df17704d42846a0526df648ab4c700e9c76a4d29f7a6691bd89268e27951fd0315bbcdaf6506656100a9cbc9e4aa99f4a01345a132a79da64319a4518f68b1b531b2fb64915eb92bc40b1911bb289eda064563adc8eefdd143ec249d5a3c9e1644d514010b3186f35c67d6c466b5e1e2481bb8d68645da776f9a168476348b6215b04c01c09f744ae384a49311118d27986ceb4bce441e07097c1322d2d50386d51b30596a8ac33af6bdbe07c50ebc467c242e193f9477edd85636a5ebc98be82ecee29e1c5362383ac3c426cd4b3809ab67a719ab217f78200b7246f1833af9d8acc74271c1b78ecfd9d5b07f64394e82d61aa20ce57b537ea0203929cf33649c351676916fe2cf010981a79fb872ab5abd524660f6d22d02a2bd7c42fa3b0f403416a92166cee8cf046685c57786cd4b8a8278600d13a043b30520e6b1e846600ae22853acee140a962a89e164669cd6a02a3b0408177baad8bf25ca958170aa025e7fa5e269877abb4966ba2aa391b4e1cb5882f50f9657f49c4578d575ec25c28f3be4b7290a5c06557ccb05b001be64dd484c78c35d208ee4c2bbf8d09d2317e3cd41782f3301e4bb263a3b6a621c84503daccd6b3c553a7cbae469783222d7a47dab7a834455a0035e5001655b28efd576084949d934e148e5b201412c3d98d341d5153d8cea030d64ea7d57b32a32ed722e84e235a435852380b5e44b646f82513ca250fb474e6adc12673fd3e210206ddbf3735b18c6434788a74dc1e7d2cb913c31be82d68c748d990df8ddafa0338a07eb2ae2abd8958b2368eb9e7560dfa25b8009b73f9a05d5500ae6020df76ac5576fe73b62e6e9bcfcbe13735e2c00e6a8a34d12e31eb5ea9388e210b8d9583002343f625dc01e72b242277ee70048150239e298edce44c5c227e1601428bc34539ef0a6e0ae39c93c590e8b60828016c97dbf949cf6b72e1242125f7d81e48ac156fb4a78797105e5984fc9f13383084644038561d5a76412102b4ad1eb322ce81958ec4a0a9cd6847b8730cc89cad4e8f81fa02ef3d70f2e9c65e892442f0fb338248a41294296c7a40802c3d794e56e115234729463c581ce7041a808162b9413b7dc3d7781b23a0d3231eaec7c8cb07172141d2c912744889000ae424f4301fd19e51212edcec9dfbb0130d8df88c7c93ef90367c71254ec0222a09c167253caa04b0e3f1303a3341ac5a39ed044667dd5d34db86cf1a0903c20752ad19d7c717ce20e5c8941021a831a306a202161b586d484ad5684d8e067ddbaa7a5a94c16030a1b7f6fe61ffbbe27a5c48d53c7c4acd4eb424a6f6c1e57c4761ff3767a1b62d3f9801b6023c38d4c16803d1bffdec91f4a176de863d51a96a7528cab6ff12323eebc2e6881f5a18ed412e73c6b8e04f8a87576703dd9a0a16d63992c067d0dda87edf3492971e31ce532f55e9c4dca64ed594cb8a58a7b3ef3885963625098fcc7cce62570af47b1a4d59cf99522e81c8cc117bcdb3deba7dd09b1fb1f2d1e570dda07978f71db7aaa91d8e378049d2837b89a29cb2d6851976a8671385cc45bd75014ce923eaac063afb815339de564b3b2abd085831cd3e93dcabb7740f33515e5853596b1f22e59c0da0a9b010639cc5ee31aaf65900e65e11f72987966c53d945a086cf6fdbdd3b71bc451f9a4b1044a65578d579a98e841a5a6cddc45284da99f011fb1e8b58f1d98abbab1cd4470dfb494986e32551f8ecc506d1e94bdf92daa273dcec3c0aecae007938ff3f073874413bf34cd44032bfdd71ed28a6b380491ad1703ef2b62242461e48cd5d465a90d32c16de31095e8032928301acc69b923699e9e2a39178eef9200b08cacea3d408a84b217966ff76ea6c12b0facf36bba0fe41122ea2e729d20043f16dadb871227d2cf5870706eeec9b8e5c160d0cc6a851058583ef22c257c44ef9c4d8ac192afbcffbf5057aeacc7b98a489ab01d002d88b07c4d39ca88986c5b3477a4b99464b23e01477d33f72921246a159c0b19e04c4e33747f9b1e6f1bda7b59eaa74990d51ad2e09fc0cdd3b3d72ae82fa0863bf8186af7ba7b4946908bc19829046decf26c5830a2ef2183c6a26a1d8be90116ba8886ca07d0fce6839fa22fdee58baa08cdf108de138ab9b494705d184061dd67ff68282543178074320eaaf3a963e34758b6acd1e6f13c3983830f23dd76eb90166b2a32f689120b81cdd6df7bfa764b5c754e4afb30f4711b25c7813ecba4e671a28ab50543608eaef9ca31083744be78ef1a38c214c21a37b6d43acfffbed7c42bce4cc8df78adb17ee535be66a7014f384c83f2eee09337f18dee3a57376b07663893edf010e177c3033ca1af4389af07c465f69adbf82adaf243671a5586650d9da2dc99fde172095672e1ead1947871cdcd82dde67f7609f71fe3ce5957c1ccc1e528e639adb652c8fe3ff59edc579fee1bf1bca43e758475ff921bf153d61f3ed0734b411345598e4239a96eae168fc88823d8ec2cc1658c8a25a54fb78ce0539694ff6d195da4e1c8a258ca03c0296ce34a42cb4edb1fda02b6071d5a9c7e30ab4d8c364f1f8d4f880af46270e56e6d9aa308cf5feabe423b4321a4fae9748b138742e6c8515a29edd5218f4211ba25d79404a51ad70691339916d7f01cc3ade0822cca1a058315650a1d3de6b38a885745ceaaed8e7332bb8452d6a638b5134b0a5e5c57d41f5d2a50ed6406b7ce3057b725592c415bd2829d52b4806ae3dbb8d417f0813bad305ee818ea24bc9011d5d06238e0efc3bcae386e9beb44c83b0aee15a65cbf358f92808fa152ebf7651bf11a5408bd71ea3c7a5f21c7d6feafbb4c23d0847d640dc538838fd59b865dd93736a03b4b64778dd51c891fd520e6f37319742f9b7d4e1927d25218fed7e30475283fd9c3853ff7d88a6cc5ab6713956892f86fa13174bc3a09e35198d5d0b92419307a2b02a7cef8d391cf93dfd8618957f32df169a165dee832ce3c241ba11296831176442a64724028775817aaffb2902e886a216363ecd8b2f6c5a7305c403fb492af21a6a4ff24a24705ad9f86d3b5dde9bae1ecc093eee3bd0a712ec302b94f78b7c354c2ad21dcde1349a3be2b6427064a00e42d74d9936513854a0c42ee322810475eb877b28068ce6f7dfe4715c2ab731d936a3d4784a9ec436255fb4a84726a69c79c82fd0b8df914b0cdb99ca5145678505055bf1f7a653804c7ae47ca9aeda59d10cc5faf4be5d7eb891aaecc30378a32a6a54096fdcf7bc97d6a48beb7d984c85d80965fff9047464b12668acf2797e418a2785b5bdffbbb2d6a029a3c71a03ed9b98982aed721d863f5d7ef1231d6b664ef3759aa093407e75ffb415e85b6f1fc6c8ea3aea8eb97e0fd0da7362a21d7cd0a08acde89eaa45977d3ba4cc69811eb11f61956c73e3036e9d4806654845be273edd1fcde51d544ed8342d6e8580385eb2acc2c22e2dae57240e64a8d30f9f778b9454d40e76029b43f80a44dfcb697605d3d01ea7e0961dd27e9a8b61dc83179454deefd5f6d111390e64c9090d3e35a229a00f130f3235360e229e1bd9d746abb925708689f98f80664bfe70b48c8e9d78957509b69659a1cfc5a773d2c8ead4df2416d0390ebd9327b6bcf2a937963b1b3e616023be24254f7361f93cc9c0c390273a3053bd7e59d996541be3f3a6745ce2cd2f2f019adddd010e50e27f3b4b07adba647d5e74e93b416fe9b5bc557d13f8228240598d2989e04f57cd22496a35373163f584263ac26ebd8e3ba706b325ed8debb2df14d0b80b527d00b07badbc40945561a2b0493d9a88b2bbb1067ec679e58b3fe49a82387778c11cf129c4cd7a0b23a0787e1531bf7942c0e2c1d39acd840191ff283afa71c61c3fb187b4e4260718f3f02b23e25131db23fb690e622651bdbd53ce604e1b999c4657457430e985bf806ad7c37999819a5c07f99562e51c6e92fbb785060d26a094cb0cd17775404cacc7ca56e6308d6d266d0a3dddd6b655aad22902c11e63b49c94f988bc0927bc0f151c6b488ca479aa9a866eb24cb90685dba0f5641e4dd50fd0f2ec29f37ad0f7443b6d2b70101eee01d38cecf068534c9a22e033a1095bc60600a4caf750eae5da05c955be289b26535495b72c77cec9a3e641be74d11fca76863ce55b5424838aeeef2d14809be9371d3ccfe1179be39483af25aeaede46c6414a53128103d8c1752d76ca9cfd117e0c99e67529e174fb936f593d88f0560b449744ba4099c47d6a45e1b70f04a46d23b780e3f20086e07ab6dca68fb31f0110fe918d9a84c289be0040d9d894e1e3ada537e11cf682c495ce86c55950818bae946f6a90693fa3f6ecebdbe288535274b3f1fc9f832d0a78e25de2dc6e44daaa839c1150a99bc71c4698ef4ecc3cee54b7a3d43266fb47bffcc48d04e856e22ca5de0ff7a65a968f2060313f0f0e13e8abd50847e790d3c919c042e093e41fcbb1be68970df0fa8e680b120ad6d6147e8c7dcf073b1a1526f7d9f35a66e190edd51a1ab230312cfa1bf4e76efa50b7fbad19124ef6d2ae3a4dcbbbeb734ce10da06f6c681aff3a74043584281e4f2bde57511701683f955b1800c27720ef5646421ebb107242b9b0bfeeb063ae7f4671e91d39b168194e31506458f743a97eaa206ccc06e613eb47de4ebc037351b25ad49d02259b14592539248e2d2c76d52cf0348b88ca9fef1f241bf6e81752fc8ef488cb7ef3f7fdb04e374e14b7768e339d39f28fc8f87dd20700580033cdc4c577a68c3c904637e8904a9944d2cdcd237759599489a2029b9e09e4c40b11ed7d667dac27071d8ce882ebe3391ef4f29673813dbeddf12960945a1860c6da97446a8f9c701cfb20fe5489ac05f47961b70ccdcd75a4ed83f8c243646bb6f8d58234aa715fa776d1169b724a63444aeab44b019c4813a26241d0a73c7d12300691c965cdaf4e7e1d7f399bb1aa40981256ab76eb43c527da4c085d1453ba0ed9c188e1880568a134c199a962ede24acea93acadf90653520e2fe6474acaef08cce2b0a6e36d822d59cec1b779f07c0758ec33f963125cb648db2c5a990b5de01ed2e693f18f49d30b80d6d29d8fcb4d966e0ccfcc331a2eb648475fea5b6b238a31fd6ba4dacf32ab13c731cba3fc99645081854f49f43472cdb75fe8b91d97a81205c7d1ac6ecbf0be423ca613413cbda19cd5b612ff833ae1bf2e74e1cddfe290d103c13bc5fd15b34012665910fb46b32dec1125debcd2616b62794298a8733b40bae728bafbdb6a26ad43210d7795edba6c60ff0cd0d1005f144c26ca8f126fb31e262f9e3220b9cdd0813c041f2af61b629c64fa10b3a3c8979dc9c1aa80f43cd6ba5066ad4afb0a35d9383e8e3a829ed1328e120494e882a6d7078a11893a94283828e380accb19cc7d3bf68c94f309aab54e1e412e6b8327b5503bd8ce0b5e95357efb5a04e28bfd25f74d3e2b6a27822b0ca195a4f0096936e3fcf29fc4637e8936833022a075cd94ef3ff2874d9a8279f9b36de4dade908ad424cc9fdd525cf65e6ee8dca0692abf350dae0217f86f4db95b626c8ece2bd23f380081557689ca8c460653bce6a842877183ac9a959298649789f919030da4952623370a4fb773b415812a8141e63c290d28d7a5fbf260519e189994e43c0999025bb3e4c114e1154442940db78ab5e8343f3fd100979f0b212b349b7ff748fd29e33fadadb06e53e633fbe35525b4f3d8514028c49721e232b5d8a2e48ba4fead439449740418c57748ee8633130d5c622c46e027a497d88286ae3a8dfa910d61e9b59bceaacf64b006ae69a19bfb7857b60790fdb091de1205ccffff9929d658ce939911c46297ba69af66a89a580229b4ecc4dc15922ab8e91c5a3e388ba16581dac90f118d24ccb9481f65ce0f41399e670168c555c5c53d48d09b4dd3701ad1165efb429191bb318417b0fc8343c7aaea4aa5688faa447deb1d05308c4d401f07e771ebbc7b8f76a6e658aa1ced289a9590ae2432541d7fa65fb2aed37d3a3dc525377c76809cece3ba76a1894539bf5afb196cd55303e638138bfe67b6ad6cafe5fa3158fb98f8e69d24578ed346121df83bdb1182279bc48697f9e816c5b5f810e62a1ad11cfdf58869b5f8819a13c24152308159040979162530e7935c6d84b57e882321e8825468c8bda7451b060a9c44d39666d8a8cea944ee8e18664f786f526b725005bd12a67ed672b8b992eb3d2736dcf249b4d001e238ed9f60c343ae720d1f183d814913c626bf57c888dec28c9b246b15cf92c81d09a96b3378d753db029479dc87c881e3980a108e67138781dc40f490443791d879cf4fdc026cea1f66e72d96b85954d261f9959bc60a8b7b05d54bc7616b38b1ba101f20e4eb0bf0590970bfa624c1f778adcf12e7a74ac7176fb9cb4abf7559f5fce50c95000ffb006bf74cb0d0f091baba1d3440bf4d62617c2c0b8d44e286415b7c186eadd2a899588b8c6a18dd159f3bb18ae0e6043863cd7a30100e680c72040901867edcbc5d1020a506995cfb8868d6472fb1f31e5becef1062cdc5d77efece6096bdd658384a3e60ba7c2606ef91eb7d497a8d19824c8912dc0fd73bb675a8e47a16addb66a24c69d244c5c04cf4968e3df54741f80cc5b5e73e78e0257eec04270c82933a57b5797e124a3ec57bcba917abe44be0e58e9b176fd6b1c680d3e7af698210cae21609e3cdbb4d35bcbc3a58472108ff0dac1e583c1df11b05db27bc1118e9ad29a81301c096f4f10bca498a94db839ef09351829a316b50c62f7ba6451a2910231f38c4cdebe4b8f42af995334cd728fc0a29435212d0bcf05aa7ab97b8485615db192bb0ef5a7f5ad025e07dded54f1f58eb7c81eb512b2e758e918683badcb02766102a129db241e4c427ceb5d14908604073fb46240917716998390b6eb2307751cb8d40d8772e94d103736d25830ad5b1752b236941815d66ee6e0a4bbd5bf6de62e32038c39ea84b51c87e79e39b527dc2f97df69714104f1075c6921d5ce8b8aed6f94898b49977b5713f9e3eec01cfdffd67475bb06b12f41e937f644a678aca0fbfb2f59d14f6599d8ea02fe13643c6ecd0cc97cd5583578782541af7a3c394a1bafc8de827ec5a0270094526295d40aeb02354a2109a3bbe3d7c02683c3fe1479a1fe1bf9e48cc4d7969ee385ea308bcc6bc67fc5a90ac6b587ba1c058a4ac690822ba4c10ce75058c4c41dc602a778e79b37431728fa58211900fc00be916a1eb74040b51dcfe4012dc47fc266c48e962c30da79e92047b7a58b27861940a63fb2333bbf32013ba05bcfe9d3b19f075e5d1824ddbd8987a60246623a6aa45d03ad73c3d3006e1225c9eb3c74b7444bf3b4987b3ec29c8f202a1398391272cdd35b3aa1a09974ba8218d08cf1caf11c6c6294ad7fea165905be30d74927de02360154abf528bd22838222dbe6fdc608cdb164ead026664545183d0453dab3a2d60d3b4f095fe9934c8a70525357de1e8ac424ab7e6f1ab4303af8c07237c122340d9e8dbca030ac3cadf8e0c9171f72595f14c4f7317752ba4d61174e7216ee0d5316f4c8f446f127cc369eae76349a960028ba8acc8be411f583f70be1b000e425eab6e6b8eafc7ececeb86b60978599efcf8635da956a59ca2e2393895dd33dc0ca0cf3ab88d559f9a83e7f62603fc16f75a9cfe1949f560c3784f5ac7a99b5fd9ca986da573b295d16a2b7a870eb7fa973b472776c65594aeff647a4f6562af10aca72273540df597c261a5cadc22464f9beebca081c08363e33f3a266da9ad18ee16c3927cbf50f280aa97223435cf0bd1745612d04b7f5e0795fca1b232bf30bc84aaed77e8146922c770e8e42ac25252ec2334a38e1fdd8e0d6d8a9bcd618e0012200f69330fe269573020bdbe09b885b294a908880997ae4ccfe9a2bf2f03b39cf87ad9fe36a04f4ed5e6dffa59de874f3da9a46c651b1e7223dcab3a4847541733e2e752a0c7007a49453c429f3c989104ce4a2644e85f1d36acd8f20a9843b70a47701b171152f3d65c24b8a6872e693c79ed778905f8245d166a5220568dfad13f010d24cde8b18a26d009a0cdba3162cc9476109838e24eefbe167dfe545aa7b4cd96ff455ba9063fe3f2e470159e73402d5d5413c1aafcf78ba3ecedabf9f1dc30c1c830f1eb5babce617c291820fa0e84d35313316886e122a718fce96dc8d5344ff3e3d1d29a4b3952d4b6d6e6c65b0532aabe6655c3d3a9c0e8a12c746d12d298515a5860ab293878d549efb8414a90a38c6401d69f64282a680e61949a259157df82908a4560bf0748754d5cbd09796305d462c46f7e4431a299a8bb6d7f48d43f80c22398c92c5c89abe6a7aa620964247598dfdca8ddef80ea17313a9154d0dd2e7a1f2a55843b64cca9382d4f8c72b6733b8f1587c6eadb8b0472c2b58be5c9d6f28fdc6fc0fcd716c705e6948efbe6d9fee2b06d10224057aa35c4adf9a0414d1eafcbc863d44cc9b4923a8167b8652f1e742af423a6580c08eaea0c9a0bc6cc864fc2c1b4f5ebed71b1e185f7137185c52e1c611efb7fae6f1e066139e2b3e16223465282ca42f64241c5cb6f2a0f7ec757f5c6b0c0518f97058dfe99ce21e9e6c5a8de41b37850c5dced977a2a4650e0c92052f7aa5a981d4a861aa1828abb7fcad2d36f4177b3668128ccbcd90ef485ef41bec7c606f36a27d83cb03ea5a586ac9ffa48dbbd046732d110903ac24558f1e56d4aeacf53c51b67a860d424b9fb002e9e0ec80f68f69e198426e137c837efed02c7ab4d8e414085ed854fb6ecad80e3dceaffe1a1636233a1ced3652daecb80f3012e1ea0d58bc3432be551a91997d5d565286e7f60702e3a9601ce4479da3a2eabbc2fa535f3884610ff59b71ff97750a313640c499371962f986e1582bc6183e47a484daa5ed5a169c396a7422132dd41b006113f6d4dc0e15f04f51fbf0ec9a7f643d01fdb3c84bf5b3ebda2439bd86fe0a3dc50571ec9adc20d95e2885688729fc58a22e39cd3370d627ec9787f05af0c36ace4b5a2c758c23a1a6ea425e87f7469bb64dd3cf8950980f2bc11c80965b0bb4d32e96ea040666ea8d7fd2f5d79e8a961b44f684719d7bf6e5267f741e3d1fa7566bb47396a6f66fbe102b680ac3f4ed0127949263ac43d2033a76a7a285be993b0276b4083afe44954160e0efef1fc8a7746ecefe5ab84f1328b7e7a2ca545fa14070aef32abdc5a453c2cfaa1255cdffe348eeb4947e7fcaaddc7d0afb7c6be740501e282d8fd261ee64b4f1ff745ad56deb5a5a72e2aa0b17773d3b85c4ce05f9408d14946d8069ab65959527d14c26759327842d13b83f5b0f85d5ee2bb7fe89bda29a18b581500a48970bd870417b35ec1a049760765caf199158b202d3ce8c184214897edc60c977b696878c2e90524128fd65dcaefac12bbb36e5cc75de89f6af4b4567684ed0d4e53e1138381fdee9e9a35506533b9a6a2cb5266b3ed80d415d7e78072e87f41af850ded82045bd764a52bfd94e454a8169a1fc2d865305dacf06e5cf24676beba79911a21ef2e7e99c2ff3845aa3ede25d3238940971abc05d4bf43c73e31a5729877bd8bc695077cb7def68e3141f72920cd2ee9dd19896de58af49376034589a787f44be316d8347c203bc93c70e7d83f03b45baf8f485219a7470ee1c1a1c94931012ed40e5b2558ff975dfc61e823cdd9911cfef6792bbb3d3e000a6cba1bddf9c636763768415d20defd9507a04054d257e5d8ffca23fafd90c8c1c973fb7b416e8137a8b0bef18c1b81a82847ce023caea3b3925915a2af5127e27b4b6f9fdc0447cbde65fbd1a392bff01fe3fe2917bde2c98b278f5e2f5b29fd0ad0cba7036eba038dc2af94e13b87e60f34f136bf9eff8196bfd167af62e9a10e2068fc6050f36dfa8cb47cc8262151200041af731041a3ffb94c0a2b43dc362835133ce44a2acc0e78f618b69075561ee14cb343d4ec2927ae66cc9ea41eedbd65a074ade2276d290f119d28eb0d5704f97876b1da0d0dc2d263daf8fbb2fed35eafa6087303370488d3d03809f7bc91bccce26626b40e87439a506bcb7b5ecec840a27cb59feea3592905bc0f50a59564dc123eb98c3832a3c87196a4c46d5cad4b2f31b63a29658335839f5becb29110353b3e49a60617beeb96b26c4d87cecab98c2d48faec1c448be8c383ba834c7a38d9bdbda8e005a596c5856165d296f0c0d0bcdca1e879c08643aae4135251b8ca467a39b4b2ea36b06b3dfe130f6502f4199a3628592fac2895ff04415728cf46436c847ee5e3f72187a645807bbc8508a10e4b85f7bc546eb49f7f26c867b44af1aaed1b07a8b1c38084acaa34d344385c331bb564a6ed838694a7aeda294619f86fe424295e75a879dc55776512ed8a64882238ea25ed23c551782eb9f6aea5d7b64d0e712f015f5e19d6dd9ecd736d515e1fcebd65717da1e62de97090e66dbcec80a0324df24aba8ecf7dda0b2f797d158276c73122e976de3d5aaddad5c8ab7ca873649df5950a43354505c383f33154239e23a63d6caf8c4bf9c52f5902fb7d425679a6c10b37176ad18a4478695e9b260ce643fbb85549bf3227bd7275a75cc96f6e989bf190c569f19123adccbf1ded552e272fe9869cd7a2d51e5fada4d03009b7c14bc7128565eb0e912e95d8f884484f11de5333343fac54534eef25fce9ba54ae5222b21f5a1fdd6ceb789dd33ab4d81507c442fe253916a89f17672de07a5650dc667b6b5622a453df590b1c8c7f10cc1ef91dd5bf6d739cd308486c34de174fd0ec2f53985e5a4241751ee1397bc6f53e1b9233041287c4c7f556d28a230299076996136eaa22bc980de7514050c5c7a83a56e9c02dee7727845748173b17c51d3db2b860ceaf2f0f4d465dd41139c831e31dbd49655cf6b664eaa4089bde1c583a88055211b7becfef6ee65885e7e6fb41103687dc259297343ce465191924470466cb6cdf983a893d916abfb54c3ecd0b1ae0999579d8d59292a722f263ad2649f9077e2f1cf77b6d9c06e861fdcfbd770ad29ec5d3a9a0f26ee4578a275cf0717016d2b5598e389099e838a61e1cbf5144565fb47eac33af65ab15a588caa1cc6aecd6d1e91d36dda087348414c10097f15008ea6aadb67bf03786967b6183b62c29b2514fe38a7bffbdc80ecb5f8c7b334809e6682e3940276a983a5fb0849ff0a30e04d13397d2c1577e89820d67fd303e22f5d7c2c24485a9684f461423006250baea2210c860458024756bec908cdd0593d9b1e134f9f777fa15436665445aabc0af0794a18eae9035e20d393efa889059b988680313eb823347804670df4a9c7284d68a9d84a865703e1813328d60ef9cdc81e79289a9d79090142d1e1998c7b971d004486c18ff9b27a6aec070d9228e96b55d6214c402542980200243538a27b4e351c86ca8336b633fce3b9059ac02a15956571dd3186f4c00596d159d060d85c00d307faf401a33caf44406e76f3da2753ae2b522810db61103343e16b72889238bc1afc231774a11e8a52d20e505389ffb1b65211451466e1ef057277edeac0f9aeb2124e696dcc046c4a1385b29c95a2bc0609fc2bf139c96d24be8954fd4728cda4cfcf463ad129e4b797cf809946977124110196045fd2508f57d07b9b95d0a125c8330097df51ff2c4188feabcc575f956dcce1fc2f62747b3e47cbfcb6663a53a0da0e2c282d6a667e7450146b6e5c6325c765949bcd08b5a725dc8b1e049c6487f09915156adb4ff30726003c03214ff538f2b44de30e37797bfc61cbd01594d70058e30f3b2697de5591b48cc6c4296e0c12f0e46b43764438dc411d15bc80c0d85dbcceb016939bc63f3a7a5bfa2d78cc0fe4ca2e6aba4890d6ad7ae39415e221ced1688abec207d1293fbdbd3a7414f1ec3e590d5cdaea04c104bdb9b7bcd45ee1498c5a4050518ed7383e2023bfbbbba64388d4f3b4a3d9318e47b38a9781ad8974734a06749c955d1dc962648a56f270eab5f9d11e862e220aad1c85e8b00b725c5d1b54c4ea06986e7f8b28286f8d6b86cb8b6e061af4ef93c99e806219a2b23b82d376a958e6195b4f6550c5dc8896f0205a97605b4ee10a60ec49c37eff895c1ce08968a4a8a0534d12950bc23a5acd8afdf893886ea47dacf52dc27bfd113d62339553f7908cd5bc7fb50917f45a03ac6b4c346e6a4eb38d99015fa89bee60856e01e256964522f352191278438e68924552f70e8c2b4d9ccc9b483d171d8dab6c45cfcace7b42be013918fbe8c25a5447405b62ccf51b26b93690b1f4bc0f137f76fbea556cb6194e7f7368fbe307048333edd08a9685cb613f351d657e2bbc87ac98c00e79bb23fa28c1d3c00b51240aa7cea672919416cbff7cfb88077df8f1e916c70a787f13ca0d279365a796eb3abec0efe6765a9db7c5342859e402941ff8763ff23645bd1344892b1fac829ced021c88768741100ee9ac7b62203bb21abdf743a7c09a28b24b367ee9da009e8f70aa6f52007002dfe66b98a4cf6a9220b92c527c85a7ed2f09b0a63ad01b1cb9d7901e7c44c34e2b81244464b899be0ca884dd051c386b3ced7de821ef51cca106a315df1c2f4e47b0f60a3e1a91579525a6b2792d656eca39ada1640e20d33d936beed218b6bbe321f59705d2fdc45100078c28c17496483530e4a54634ccccb25cbc669ce15a44cbb32d10028c06be7ba5e30a4fe1623fb9f71c24e8344eade2822a7174703b565b7c53301170e022a057aca296abb1bd3b0d9a61f5d91bcc6155eae6675cf00173e3240737ae7bdc121db54b97e5d636a9625fe1abe5a12803cf2a1b43831979e0286848f0587de02c7247df0534e2c0791fab1683c422eadda111b2cc6b4bb88b11b7b8d2e0b1e9a379c42ba50045cd9ee3b164db8c5de5219b5947ffe4a112781e898236aba231d30376193bbed2d8d81992c1d4643b6fcbf31c52c0ad1e7f4ee4032ef1abc5bae7be99b67182a07de2bb200aa19f42272d5136a697fa41199b88396aa38eb8cfae375ed037c79a5f51872e44699d8b644c4c3d73cf72d5c05a7d1a8b90fa4ad3690533d162dd697fa793fa34a6a2b5358c6c4bf19028d89625f729a9d88d28ac320b7072af633fe4e32e60ab484301198f80482810fab9e569e1b4cc330025ed4fde34b04d815e2aa3d273bcad2e8aa98e95805335c4ef288085e226bff5950ea5064a132319b043a062154acebeece7e09646664addfba021b12d6f9fd78fb774e04ecdc6f2a09ce0974ce61b0ae5c0cb97044d4ef28333f5dca7ac5551bc19697459a327062f75b4e3b5a7519417387b5683e2a737e7518f82d1b1309ad9c018ce4e32dd9d65d1979e7943f09fac38c3d1216446f2d1a790094ee3907f28aabdc9294a234a046b3d0a077361f1ab6f46e6893ab71e613419a42d384a14169df010cec43b05ad93fae70f1d563c57db53042f42ce839be9b3d790d6c70f173c092bee86f98ac5ad19137cdfa4fc826723cac18d819f51488097ba065108d042da42987ce40c2f599dfec806ed17b52acb61a1d8d8d018a846dcc0485e1034b270ab799099509bb4f6db0d78c7a2e5776bb5d5d1460ef7e86fd0c0967a98472663a4b34a938412f8eeba5ca2db4fc0c6d3b9474eafed1b5749d7de60c78e827fa54b5d955dabfa2195d5672871aa0173b456d70881ac1c5b34590c4346fef964d3b122e80c2cf33ec211a20d5a2911272d42746fed729244c698259f391413aeaeb5a7dc91ad0d5aa98c1e245ed3102c5c6c62c902e155153b0427206e3ced2d8cd8f22c089fa90e8dec3e3cc5c030786ebe2570d9697d43787af8964a6ea4516cb31cf166c1bba093b038d99c6b2f71017aacb39dd61c53aafeb7a987f6d5680333dd7f26d621b44a1976480f29f93a8af4c61baf6eca7a4b248376bfee4f50136ab34d497dffc2454c0b54aade4a0e502447ddcaedfa243804de74dbe312c3485fa32cd3d9fb5d7bcc9fa50ccb11ece6dd0c7a63baa0b6c856241548943be22c378575909f706f5b5efdb61be3aaddeef85f9f37c1c5cbc6a1a2020a7f5a43529535c38e4a2b143484984960b2ebee7a790e967c992de3c2d1bdcebc104718e99866a18215b4bccda717decfc6e9fdfecfc0d4dde2ee649517a4c01f9ff62d9ccf0775ca7842f70b22fca6dbb5cd77ee1118b516f40c8e058cd89fc7f6ec75fd4fa47c8401075350a59de8b6a6055b06ea37b93500621064eddd357bd985d3a5088db3ff6456cdb396a983fe5216e19d8aa11ab7fc3cabf000971cda98eb8911a7b2089d73d0ac5bc71999786cb346d51d447a26ab98c693f4e0ad557f9ab20d41227386304afa0f81a2133792da3b1167434db519694f97576a34dae6cdf321f6347b27659d6206d8861ba9ee1ebc86164aaf2dc66c20dca106570e75a8f55bf28372a3699136e916270c6904ba0c1e8f28c39cd808486c634e608445c11a693793721331ecf0e0eddeaadaf326add1389ebab0cf5569ddb36e8b50644a84269445ad7826a0ce78023e56fb12c83355dc1760397c821295d12cf74122724241f0f15dd2779ceb394524880a876e5a73ce63a98f2a79651166d83d39e874e51699c948459af76f0ddbfd97c3131eac20e5014ad62c0ce24b24f1019324ee2497a1d62f895bf8a981e965a0fff871da1aa7e12844bd0c4b47531277df55e09b81a46cc777b624f531658ff2c8cbb7b6770a23b029a85d55f4b5a3ccb288505cac627ddf60a2a6f3230676081ccbf0a31ec73ea8992c1d61ea1b8c3710d1fe636773ab1d5a474dd6504e58bc23f85bc3174959fb42639dc9cf4deb490cf8b886dfe2be1506ef435237a995d484baec46245f6ce33cc45b6ce9e59161a029ac7ef84532b3929de0c5341f2ef809322d92f51b9e4ed859ac6b6ba87126d69cccac03f287c91251a779ca2f81c629501970846508de6fb647cd29290e3225602b80791b61c4e7bff8f854bbd2c241f81beca9ee897a7bbb9e08dce69e4ce8432836e54dce4790c7caad508a71e434c27894f75760806e6b88236fedb939dc3cdf76bca7e10c6a07a48a0831d9448b420a054e3a08cce381bd15024f285058f7a9c77c0bfda4640002af632448a847bd100113596c85a20b9c00961cdb1b45ef6ae65bfb7d43b2f79d7d2ef5aeaddcbbd6ff9f79605ef7d4bbd6759d644cf0113300a40dea24cd4acc408541355f55135acad5f9a15151e10c1b38bbad241f328c67157ddde5aa8c436ffafe1c87ae5ec5f9482e049ced792020e2956d432cd8551c9749587172752226cd533e2d63d273936706e93407d0df80ace70c771fd59a8458ccb7996638e0b2f126a83ef5868b55b7c128d587289240b2937682753061b81edf9be23544ed31d4c7c6e4f404c07c3f11253abbfb48049552a2aa0f5591b29bd68d910c78eaf976d7228d8b5366bcfaaa7958078cf844e8d7509f9d4ef9b9735642c1403b662991b57c9df3501ba6b3abe13c20951b51b4963b3c726b93e2b8e860a85365dd87a273c0715a66d9de2fd49036dab25cb5704e26d13216534f7d5efb6893e85122e3ed14ccab3c621d80605ba40721f23c2c4888879e4bc537e311f2592a716d3af23f42b5e6828fd56f6e3f83367e33a5ed1f01071f25a173397a7eaf7fad57e7d04b3b195c9585f55a435803138fe9d672fba263578869827ec6407fd259be15789b8dc58c5557e9a9a2d76e3fbd1bf8b5ee889105c329539a110a6e20ffbcae5da43aabc5ab04da5e9660332b0c272379c90bfbb0ba864c3897556de44da39ea89e14495ac1939d2b022367cdd35c132068bcd97d2f965539d16e5e53e43aaba8380f70b1ae930c806f4d13dd036879db711bbad9d526815e1f1732c2f7b85e52b5060a952d83918630104d1821c9ea87872456c2c8bb99ddfb8f621f11f822eb495ece11e504043fd40aaf277258ab8b0cf418924affe93d04aebbb78ef5b777123468c20aaf8be6342f19e3dc11622591e043964307051670f3aee9a186533e10938568a74f39f03074fe9eb4ed9ea714ddcb0036ac2ec3dc5c2c7f8a49a48390a0e0f645746cd8530c71118700961e6949cbe11744f14863b53f76f349fa64561a17f3d6f8429c97bd07124b5debbbeb3077e7846398d25c1ccb86c8f8826651d917ea0c3248be257246cd15e3ce8a2ae5ce29af1d68b7e18e666f4438e16bdf30468643d9625f75e50e2e15a3ba61caaae40482bc9a4f04d20716361d71de819e50b44c72760e1b3ed1b62f59d4026f594baa030e7b6a1802157f125663553d0410b5c25deac9f2d93c62e0110b6d330cf2fb1c1e204c7cd1e48d96c409d54d4333eef55a05bd43f6782430ba8527cc5b05fcf8fbbe083f060c2038853115219dec4164ea68aa158e329500fe5f3b172a60e1be5f793b03803248eeb69c6cfcc4f8ba8f01d16618fe1c4982cadfc279b39c95ad0cad58040d40f97d338e9def73047c5dce41268728cf8a0f09448613847e4a5eb9ea74f0b5eca10135ce85233bde4f050055aea4fd3ecbd86f16630ee94f7fe1675d11f0af5cebff03c44579cb8292a0d08d7768cf5f929a5f894e6bfd0c249214b1d68c088f91464af3e31f0196782794d1e2785ec680d7d21cc428bd5eb197c443de6ef2d72b886d2a26ec243febe8d7a10e63f0270de1b423013083be65c55424616350d21155f26027016d8acef9bcc15fe9cec8d411f8f294f0998b5117092473bc2b5f761d2a86981d6c6e4bd4c1c4485138080b5295a799faff38823c066787b5f85edb6d276cd078750b8c9623478484f07e5889764ee6c538ead82611a68868e150afef0d6a46cf93aa47388a2a82f943d49e84e838468a0465cd6ecb0f0a874885ca1dc85c79e5a6307e901498132c16abccb3a9be23135ac7824836a60ee79803f68b90281a47f2996f9bae6aca84fbc03749ad748b77e0370b1a02d578dae0c026ffebb212046f27e27aaf8d8910bb92016c638e0c9d2ac770b18ca43778d1a8b56f762ce064f8ddde23d72564860e7e049f6f5170e15b8b7ddb67dae4ffd7f7041b484dc09b3113cf1d77343c4cec415d5b7a4611289b0e0be206eb6808d0e86c5d0200c90d97408dcccdc32edf922135bd9971c9d1fae7abecac07572448864ca9693913495d4a272696873c47987e8d2949429098433d241912867c1ac29bebaec29acc61cbc12e9b0af4cd9a35e094b033345b883c9427632a2b4a85cc6784105a038172532c31a80b389e9d999af6c3c32ca39b43d2ecb180f7845a91acd0d11193e7c86bfb6622bdb1c3748688f1a6bdf10fb2cc101fcbf740330c59df64afc8dd66f73d58e4784232cef3b8dedf1d54e609c1dc1149ddd459d786068f7ef9be040f554e544925b425f2f510361396214e8c41e3de5f8949d6764040e0f82b5c5c49005df607ba09f603d77d16853f96984207fb4ef466af4e29d16abacba792872760e402725608ba91077151d1a9d824be949b775aa0271e8e82896782d234bd858921beb0ce72851aa0d3e9ce20de0d39ce89b38585a88de8e756ba8efc2c337dc34c02bf6922c3bf3616016f2d75d38b29abff219dbac0b134c584d406b7ad3e21d301a41d998fc4cc848fcbdb5e89243231d0380d7ecea5cf97bc1ebd783ab1207aec2d88f401d553f3a493362675a8d268c891a035eb21f52082af508f78d71ef671c84fd88b09feb50b65d4972f9895e347a5d3b2ac4efb99dbcd51e00076d92165f6b9413102157f49f200a3f97d532194074ad482a106871e4876f1709d517828c2f43672b0392e6a8c5218c0b892c002857095907d9047ca098dbb2dd1b798ac17f31dfd2305c14ab29dce599166c1cfcf38209824ef4bfa0cccc82a4dc340219d38a10c108e8ff0b061be3452635569b5bbb2b16d0859f16dd58b2ada663f3a412fc0395a6ab04b7bfa2978a56899c031be7aa4cbed1ce3c3a05efdc9f243238ed538e49995714a48c68502ad7934190adb7ee0bbcc6c1bd11a8d0a811f6c0a9fec5379337b39128768f70f70c845b129228413b487da43b91dd9464c52066bf4d644dfd23f1eb767c0a8d7b3185e157e64e341fc83857a336f075eaa869b280e047f3d2ef04ad7a14c62f5f36309fd5351775b23ee75c76dd91b012945e7254bc71f22f06601d3223b8b2557cef83ea01ef147df191de714484c614044c4f5e36d7e494758b5cccf94d1841f59aa375d8303c924c3d4e1b223338e73a81c5ca1a802b3d424df6620141341cfc435c1c16ce675f4d70daf14d2ad368d8be21ba5e2cd5bc98efc5bc783105a995e2d80fb0ba9dbceae628fed6087c566cb5700b245c7ceeb4bd8f29edd6d9e4a4755938315b4c25a29dcf0ff9f6593b8ded3c28eb9a47a05b29c2f4f26392b59e7363ca2a0b65bd322b2fdb35a8e9341b6774288a057554574be8d8709d0f4100bdb8d01457cefbb4a305aecf6250a682f48c27d11a9f6b22c5da86fa873f0ab52a313a34ee3bd37a52a7e639d113f2aeb1b1e293b1b5780893eeeadcc3da0a019c992c0be65661b9b5e4002c939100bcb544a5884811c2179438397d40079870c5363be6ad7a16849bcb13fba0f72013509a4eb7ff960f2285d5dc40c739066387ac43aa024b6abeb6cf40809c9e7f585504c749f71e0e2873b9421d6ad4e5b6f22873f0a605ef14a07b9b6a945197e0fbf94f1045e437a057c972a445819c13a70c3b2cfe7a0d9bb2abc2239c7b8b37c89bb1a1753e5cf186f5bfa3c7e6ed870502410073e2eb79fafe9b98baa1edf716943ffde7a47938d995dc19fbe15bc634768f237160bb790254cc99c504f649887b2563e5736311354a41756650dcde11e070c9d21c5ada731654c65ac68210840415cbcd179b050a3a62188ff98b3e851915562b971adf705b74a375468cfa588a655ec973eb6003261106a3432c656103ed3a8912ecd139870648c94996622463f14e1a2294296f54a452c400d24675731d08f41e91ee913ae58637396faf362966062907fe9807d7b876f381c537b8a35b10bf7ede02323232fa9b363bbee97b0b80e64b9189ca02563c65df9200e471ff8e089c052a308493d0a98637d290d744f824e287fb6a0fc11966ee1d0870f056b0a44705e20c518820582d0c80efae0685cf68ea78b823111089d00a9621f9f97216d4728a974414dabc454d8d8173ab899fb2690e3f77b2a60c3aee7dcbe64fcb7e813137d63414a79cded4bcea85e8e9829cd3193398fcb3346ad852428a053fe521495d54e3ba5a4d48be4c71102c8f4e22b64f1e9cd78ee561775c68918185d235c4e8f54b023e1d3a8a9ae85822d6eb481dc5da8a658aeb7a2dc836d45234f2f59ce92cae5c58543e6fdcd204147f6b82ac6602605b09d861b3c7513dcbec1ff1dc11b6da735fe1d023c7ef41684ec00f53bc614f9fbf475f9921ff0a471fa3b69b2ef18c49b56d15e86b41f0d8f4dd1c12ff63141d41b42e3f50a5d1693e61967bf10ef1b0f44900856166102626277e3ca7c2ad960277f42d5d9ed45faaf0fb3b643f157ea3b18dbbae7f6c5a35d7d198821cb76c73c630399304bbae044d921f31a83bdd4eb2f5302d6994434cf4fddcdb32b419ff730423f2e9fc32680d6745e223e613c232afc02a04c5d7c99a3fd6a4c01ae870777f85339340613503199d81ee8f740ab372c5a53eb6d6a36dda6ea5f8390a27458065b1186c1824ea5384f838a46b4ee914d576a6d100ec8553549a98111d0b5c2c67b5a4d1123ff4a3117addcf739109e2aba5a5459e7839aa03d42f1d84da0f4e81a6af9877d7bd7b09e60b4db280ef2b3158d15752a39de038abc68285c5dcbce87fa93aae915a937ddb3c87b5c18ed69ccdadd3c3726734cdddf847ff65991b9ee51459197013eaa1fd8a35e0cbcb6a96c6a5e0dc2bc3aae66562f81de0da7612768579a2f59253ae299124c2879494882238c258925286b5f8edab47411460c95bde914402b76d8e683474bad3dee476736a7a5ff70705c1522c357145a12614251fbbd11a31eb296f1cc3fd4c6645ffd2b0f6a5b5042e9a073c9fad059b7fa1e63b7db96354ba9bba1997870575600ab36a70765d790133386463c4de25b778870d4aeac48e05a35acfa660661a445923602c853ea8288f004e8da4307fed0ebdf1e25b25a816972449e554bba56dac67ae335c4c197adff913bf05eb3cc15fc7fba7a8e28cd40de4cd0f39ba94a47760c9d3499ed4a5ab3e786a751791ea19a91b929b5f52a0af5de6a87dc0e47a1564aba9ba13305d1ee5dce272de12fda0d9ee2e2d2e82f1cd85b7863a970b4d9f27896a73d82c1f7bb3a14b9f6cecc07d5fb5300ac950f2f368636633389455624cef2bfb304bd97b3260b1cecb123f3eeb9912240f625924abf144e149c1dedd70a928c7743a3039c01851d6626c4a056311e5e203a304b8cd0153ae9dcf741c5337e901176b18a2f732b8ceb722f525663fe055d4acf28bd3cf1915329fbcb0f9655c0ac524ef3c7e328b6238e431063fc59f78f63899bbff30271a6fdc62ed77ce44ec31c6bb5c7d30654a9c7f12370efad13d52cfe1d935ff768c7cb7707fca375d2285790857bee20747886d3770c55ef08ffb7872c0f33c00d03ebee32309cb576b8df2f11d3d8cd94accefd36af31c7d8846d3a619a2070f5430e12aabad2c28843f70937567e0c7ad283f8416e58d55d24cf265136995bce7469ba9eecad7094f5631469caf8839c19527a26e206bbe89d1b594bcd4eedc23a8c68829c27107b488635cc2192279ce21b7214ebb80652ccb2adc18cd676af8ae4fd8c69da777e71949f9ed0e6284c021aa72f4c45379c4347735d92fd0cf5ff1c0f03f323737528abf7ce2623ba358fdbda1a0899d04bdce7781e1299590351aaaa3f9c0828a0e01bfc51f952a8c777a0aa8fdcb853bb40e96d8ac25b80332ef04ea0eccbd03bcc61a6b12030dc0d054ae5873ed05eb2e1e652fb9e445e9b855a0ad19320e83d75521007285ef40323c1652fc5753fdab693e6b297eebce41f1b349f1b249f1b24df5dea679d9a578d71ae895b16d0efc0bca391327c5576a6e94e47d79b670bd4d7f8e97a04d1c9b606c497c6c29fc4c243bc4cec8aa1cffe2930b8f789110107e0af3992a5cbef00745263e7c8ffca4d2063f701b8fe45cbba9de4fd81afeca8bc69c16b000483cf500571543bc76d8b34d64bf21b424e4e6e7eeebf0e5e7be1c32b70550919d38fbd077d565dcab90599ce9b2e2cc2299a7d39c2d633004989c116a8bb89052cb6833784fafe6ad84178bc82c1f795c281b914f409c3fcde9b3bed5690f37d32f9d1b0243639c58f18ee9f8c44024f8c8422a823060b2f77a4d462ace6b6bd25899747b7b9b2a1d37d36534355b4d58e8ef5077b0e9bce3f705b365efc3c202e670143cf5aaa10b6abc74f8f6cb2f27db48590dcc56180f4974ad49f5b35ef422955f9f3cfa1b1565eb25ab393bf57c7caba913ee6b958d53975f514c07ea4ddbee8d4d32d48e57b9a759c7b31703dcf691a0e05379a1f9e99fd0fbdeb24b189f699bc56333c0217ee00d55778b12343de934b8dd6ed1c75af9fc5447afa5df38d07e70493c357940a7fb51805a1d7ec60207f00848601935206f29081ade220020ac0a75859b7cb4d8a1f038a63ba654535a56a0e680c1d20b503ca4ccf22f9eef6317c4134334c8741dfa5e9605a4270159488ccb99b6299fbd770c84379444561cdc15199be815b9a0fde325fe2156e9172a0d9734e1186b33539197571d805f63252360b108e21ee41d39ddf5e236c8fe8f9cd0b9d67e4da11838aecca1602f949bc7d6bcb202228bd7137bb9def02b972e0a011ac87735c9d00671b12e207ed7291b45c9accea46c607bedc3671e1e51fa359ec5f4b53f279a6a784a7f6cffb99f9ed42d889fe615dfd06bfde619949e8e2f45deeb05a2a878746c79f5c5a2774b1ea468b6d0920507c86cf1ec85185fd605dd45102b13c4118d64634ed686a48d7eede74437a456c44f9ef3549c8b3d35fceb6af049dfdf905f3fc22cf1e1bf9ad58d8fb15f4f06bafd1f11853d522b902f404cab3764e365114c6075ef969df26a0d7ce0cf68dbdeb46c6ed52142ebfee76d305641f03fca594dc4961b50699db2d4af7a6eb90bd1854d4026d665022d1ff6c03ec07f604cf08c6831d7880cdccb68941a63a94693c4a30fec3caf919a1d9d3750631479a957f3f720885f845cf340cbcf0b589c4a1d50fe0b0bd6020461cec5ab006b24b4ce88b7d0ab9110fc37a20e88882ffd0cad794ee9618358abe7eb6d0e7985d117bc550785c1979a09a703ebcdd2dcb3d4dbf674efe06210f612b907c95bac4b98690d571804546237d853aa3183d5e31d30ae3ab626c1d6881fd75656a82562989a819d3349016952fb4cf4da6790ba36c59fe48aea5cb858b70ae3e9e26abfa1a7c011664b9c3ec9c7a0cc514a57059b867614ca5010f015ae43091422b717e54678e895144bd51d09f6304c8f4cf42e97d9594453adce4c42831035f8d974773dc8efc0252d64736652c93e56b578a0e68f5aab3e25958a5f1b53f6a834e7d9c8c0dc93080f182803de069df811974cbf83c7ef3aed8c219ac1aa8be443ae12028be99bd9799f87fc4e38450122927d588cd675a25457a76a31976e5a13463e77954e19238331cedb4ed265386918242b3d8dd0e4bd77924d549763f16ec05213f1ad4f7638451e8fcbdeedda32fc7b6551250802e22697cb27991887ea0140f6f687c408d3c655be128c2a8159602f863c303587b8547df2b6a023ae032895e97bca149cb5854a70afea199aa1c671ed12fec3ba22bd3a8e34dacb0be41524b0d9e33f7ff574700f8b7f17b77cee03f4eae154fc1b15b0f8eb3e46de433868a8d4132cbf2a6a19ea8a2d658069c99f56d64fbf66ac976bbfac6d81797d58f68cdbe1a5ff298f6b5c301b4d26fb7ae07598cea29d04c58ece6c1e093be816d77398f2fdf8df74904c681ae3b0c579f2f70ca91ba244a359e7d157c5cd6c88c87fb53b4dc157c142192cc4a958f3b2d0b24f3c6653494c9323065343832ea725a70df0710d0a24811b18601dcaed9d47dfa67c9bb06477be92edbb4bccb6cbe12a6eb8fa1631e1f5e672aaedb8482bcb547ab609b91ab8049bac55e9cb8495f79972b62c6ba4ac647988cf658eaf66e5b4cb0dd916b9ccc63445c8c848dc36931141f293f838f00aaa68e48ff9491d68b9704f0ad859c983398d39db5664cb201b7eac5886d3ecc0abb181d24e538c4f06d890a94d244c9c993973e671359fd5eee9acdca0f64159c73c50d5ee8fd2b9e88a5d347188b432f5e1e41df9973dce0017022d05b4c018e0b416a52a561eef4010126e2cc8013fbe6abbba1c723a50f34616fb40b57e644a411ca0b77e0856bad266bd8335fb99964964f5389c05350471704b5721f2618a48c92fb71a097434eb133e10350d0b3dec814272374d4ff3ca4e1f1bccabfce4b8924702470e33350e8404831cd7f718f2561f29131ddb832d584d3c9ba39ad451483ce1fae1f0d515a9122a018e3582f11202034c9a6a71a0176ca62a115db6f6bb5d667e55444834d5d6a55663f2ef56851bfa211f0adcca83509660c1132218accb9f443d848b8086c0b860930f2c931b8be16070f3bc67d2db39c64847b2838f75d82d1586ca9bab5314044acb9ae39ab93d977e68d55413290c39ebe944e40dc4e1ebe96e94595d70291aba04daa1be0bb0c5d8c760f80dd3953f8230dda69aa10326df2dc904035d69d57cee76a50da549bd9697296219d54ef90e2014b3ac727f5b83c58db08f23dbc1d07909470b608e7f92dbf9932b3279cd15337f22f0fac1130112ef6d22b1a1050218ba5d5fd372059e01864034187d10b31b10b3d8cabe72f7ceb876bedf5f5a4ccee77bf73bf4ad2f12a0915e321d0455ad38c41c82ab1e9f91cf427b0e418b34c44a98a83244dab79a883a8f2b8254164d5a45aab1d102caaa5bb256e8ad5690a0924b84d834a8cd82da03d84a180aba28ae10e0282265b6fa9842cd5ce5df3df57dd719344741ead48014c24d90e19cc3508fac963c2482fdc9962eda66991e155ac592add9ba1d7a19c9f8422fbccce6c3c99a893d00faf938786bcbb57c07e41910597c8058d20c925cdc7fcc40dae79900e87ef3f5f99092073ccfe464f1e1f7b298366cc510c195cd626fa4f61945ca51997e34e363c3ca5a05e5bb8742fb57df8608f0a5ed56261077d850fad2c5c31d986733163dec4653c06ef924ade4800f9d2b59713f76a2fb4e5d327e08e3d2f8a9d4f6169ac8c6da2d466d1ed9b2b1adaa44b2e543eecf47a33e52c2d386d5586e54f324c5640822538685eb43aba35baec564f5721c7638fb86720bb6a5fd172d673e7e6aaf6996cf85dd3085c9034411d4751a138e5c73615d457625d7604d07efcbd5b37b14389c08336a82aee5b17d72fafe79443a404c6bfaf34c733bb6dc8141ba1cc80b98613d617fb315aec4a7347771dbb8b9f6478e23a91ad70ffb6d08d49c485602b1b14a7cf39524702bd416a1c0858a5774617ca952cfc792e6d412666d434be355311cd538a491eb22dba3ef22c2b6cd04fd225c6c85bc70947bdcf05d183a2944a1234c3f7ce9be4bc07eef79607138fa10c39d63288f19dccab87b585b80b729979bdeaf0b45d26ca981fdbaa6d4cd8c9ae3db49666852fb2c5ff306ea6ae5f576b94030521d31e9c13a4df0dc29236afacee35208a1e669f310de0d50f30c306afb03b53a21db6d36d88986c44d6d83a784687b6453833ad3eaa49881685f7261da92929695abc9ac462d83564e3a706759556bb872183afcc0a83a0b3b93b79e92926c86125151b2ab0659f505379c49093f273b2245e13620389430881e91e1ae380bfa1554d0023ae865625b3052b29a0e65b0802c01264208ddf6249d56545ab7552366cc480dbb787f33b52d97632721dfdca9ae96a0dd5c6031988b8703608a41dc02105c8e343513d7cf1aca6676603ab6f770fb38f06839a1a116d2f6de7bcb2da54c49a608066a06db05958bdc16b51a6326dd5dc2b4387f9829aab1d15ea0cb96275cbc205c78bdae2f5d292bf02202e7016143569c21c2f2c7fe208dd3efd379b1f11924a18acd408c71847234a1c8b26ae377919999bab3b333575b639c48f3d52b0f553eb35cf77066c1618f3e95809260524a29a594724a29a56c18aa08461cb9b8285c9dc1368f148a41a0d3d84241bd72202d02f5d8e1a3570d0bea1f9fa59a8ebd968462509258ccc7536f43dbeff22202a949598b03851f9f9f18909f1f9fd88fb5d6fef8008901f1f9f101f2d323d66852445f99854fe99994d6080844559670f4cb96524a29a594d25a2bad94544e2965ebb06c96eeee2a659294524aa9e9008ccc06560a8c1e6c68dfba5bd60b8a98cb4657fc400c2fb824196d59d29ccb565926b6ff892c799104438ae2861c454fd4d1efff427ab97f6a08627451047283c581c28fcf4f0cc8cf8f4fecc75a6b7f7c80c480f8fcf800f9f14cbaf4a0e6b667f7df525b68f6fd9e4550487f44b2efec5fe81fd22bef18a3fcf844b82a6448af5c08114dc81022e08f1e3b7cf40aa6458b160a8059fc6b9e0621c4a565826cedd57b4a78dce0a13fb9c824a0ae7c18861029b255bed19c021d4408f8d32bc961617f749f8f7cf93b1aa7470b1ebd73d353f12f82fa93833c27fb2790fb396b7a23f64f1fd6745fc6003bf863df88e9516fa4267a342ea0def43df64fcf45d4f7981e0516e939bd45812ab7892dfba4857a6e75308ffa1e98478146a8f09c1ef5464c0f03b2db74600feaf428905bfda68f5ba78f5b3cdd1f39997878a76f7b07f2f08d7c4fa96ab0b01464ad7456aeca85a90a8a68f28f322cfde93ff8a64b037550e3786c1793f86505b681629728c6950f8302311a27f6f01cd4cbf7e1393040dd2f01ceb8305be13934dedbe796e9d973526fbf27f5168c7923a74f8134dea7fe48ab27e6655e0634623f06a4f1401a17623ef53c319f028b50f1be088c09f4409e984f7d0a2c82fa18f0488b5bf1b767def1be460cf5f68dc0fce98da0ded6d846a818393dcc1bb15ed846be773d3076e11bf935acb0308f3abdfd30d278cf737a98e7b18ffae8d178a00ba787f91efba867180c0603694cef7d185dceb1c8f3bc0fff16a1c203f328b0c80066e01282a80503aedc46fe0846442d6e516918ec65bef1b885fab8c59daefdb8f5c3f447ae090ce34dc9a0eff449806d644be123d7956f027f446f4b7d6129e88dc0f6471411d7dc17242e5e63bfd813bc14faa2a085edf7ec179b310930573e909fcce7c7c7e7b0b0d9878cd4a3c244b1b47bed7b3aedb937b27d07f670df7d071ad19e03b9957d8d58cdcd7ec526c8a7a10609e9b434d0866de4d3505b390930e1ca472115bf7e8d60caab28f04d0a0bdba0832acf86d992cb9191155dcc0c29966991a7ce4cc3a13dfd50d3b4d73ed3aad5ac6635ab8161ad5956339e1fd994544a29a594728b1439a59c50967474542c57e9488077b4c836adca410ecef1217d78ad96697064063103ff0b2d70e5d37f77972ee3bc2234c41c2296a466aecc06472c3879506c587b84cbbcb23285e6b4414be7642e21de8934b3e2414599948c281be24c34c8e14e1ceefc705ae1b9f8663efdac7c9132d4a0e9c537f36586907597d436f3bb181283a8fb22e54e2fa8e84e2f89953bc3ec756717ebd5fc191aecfc7073a1f826c2dcf83303b77f520ddc9ecf4929a38c7d7265475660473ba8a0d0dd5b8eb8324cc9964c1b542a7e568f67798bebae7eddda3e186d66057bc30c5cf9c516274d28d495b741cb373ecb90af59a74829e39d586c96554b3489b24934896ca5f2a395f3a857fdaf5075bb67519c4c706c43a5e21d15efc8a7d56d8e8eaa09396c4cb0a9c13935547ce3839d1f56cbab1865d24c228a21f958c19c387efcf001fb61adb53f7cf0c07cf0fcf0c1f3c354c58b99648b175b8ab26469e1b291156478f185d111ec46adb54a67adbe246cbf7bbdeeef387a50ab842d1162207152caa04faf7e7ad5f1e303f9e18780ff3c33fff800e955fbfc74f4699c78bb81f4770bdd3f3c87e04265e82929a5934ec9b334a43d823f7cf4cab7d8b05b12122312b252115812cc87b747a01b666739062ab5d6396f05555d18367e683fc638b6412daf7ab256067291566cb53a09a01fa6510d868c88b8082cbc904ac5c0d0d4541dd4ad117e37b4995aad60065a8992268f85148e740a3519eb44d8a7aa718ba6fc298bbc566b3bc6c25844801b461bf39c0fb04dbf0c4a362cddc6c17b00ba1d896a4861bb1948846ffabd244bb77b3201804790e91a427f638c32bac7d59c524a29a5ac4938c91c3233c7088633c6586f6d9cd92ea504a3bb8fb3b33333f3f34f779f93098e6dd02eabdaa6a16ce33c7de6ba0d490f5dcddab7982cd96b65216e1b87637cffd8695a707852e69b78e4cb0844506aaaf801931164f84110165c2c01c0096b2f1b7111e302690b2515eaf2b3e72083472dec005a086531c5ed9fee4ac2b8fe38dcdd5d5271c3d4083c58c282085854713be6091b53495c772a4f14218b201031059293255c54362c969470fbe3111e622d98e18b25a6e0018b1239e4c08b495d08402b41ad5d134d341bb82d311eaa0d42db163274c884ac683730a142aeea439259832b13c2259768d21137380b342ccd242c35740d4732c90957cc614108c96db826f50007166a28721babda22a6d485ede18b9042083fc414343c1116e6b2d1144e02803405954fc11457ba207a1b3fc0c10d9f1505c736c218bbfd128c1c23478e71ba4f9f3edddd9f99e3cf08cecf9ebf2019ec5629e594428610e92211a873b667bb7d433c1e71dbc1cad6be21447ad542c2b01148089121bdea1fb2f1a717256c39c2ceb04337410350105bdc70a508586a200586872d4fba1d4c4dc84087212bb83cf105cb96259e3341050ac228e28a2b5eae1401b5434c18455051c3cb0b103730bae207312760f560861d980650bc9060890e4244b18406475c2c90c116af1cbe60117ae9409b21891c24b2b881881a242185881918d9ded14d846410ea41092520b41022092396643c3cc00450849a50d2c475850caea862124193ee0143a908d8175eac8c81c410b3893351042c08207088c59888c1154d6a745b6c0b6c7f68ab74411558ca50c1165792d8e008ae41184b36a8e1063e40228b11572cc99a87fea8002bae6c21c30932beb0e04760c51052335536115c3f419529505cb96107323882728222c890226025134031220b295390200a0c8e20a1022f576cf172c1acbc002935c3e54ed5ca2a57c6a0580a5a02090f455034115664e99a87c8748976b99994bba339fed96308400c15f8b6b7840d55574a0ff9f9eb91cb201de3ab8909b2fca18a55f16bbc1bbf074fe748980d2b90c0a1736394ceed828e1fc3220769e6c4ae8e8b7fe6204d202efed1d012395c94103685c5bad7e72f9c42da0d2c3f08ba64cf5e0c92e3835882bcd136fd5dd339bcf2c2fe0d75685cbea2c3971bf29117cbb98ece0953bc3ba473289051ecc9ed76981137e4188dc5a2b82177c171c5ea6ec1ed21dc1087b4a9303e232c778c31466721403a9318638c3ca59cd2250faa9492e365b6c102cbbf39b1fcf2fd9db3ac726fd10571bbfb6b8dfd956f6275ef5ec5f75eb67fb6ee8e069ab557ff1d114a1c93c944402c61b22602e67426bd9a93c3aaaa6ee34ad006b34e41d8227e849756911862905addc7905a41cbfb22ac050f7c88b56260268610238756075279214381773af4dfbee7ddb64f876584f5df60604019659411c5f4f28bd27d94d3ef689deee3b68f2f8be312bc50badb6f4a562e1b2949815d36d202e9f6f01cd3672f3bc9f5519ffd346efb88e3b9fa6ddf71fbf6786c271047dbf4490359f1a6bcd8bf39e4e06e713350e763b94dff8c1236e4a31f1468a117d07d47f140a16f75204d7490c62444532b8e8fc585bde2a2721d8c5da489dfedcf59c0124fe3b051154a49ae167c08e74b0ad78d3f9da583a328de689cf85b6f4fbc74a6cb73b449d438f2c1d58d39ab367f9855c98ad116140723ee7c4fd239d9cb0aef44a52a319f82e440c0fcc9cb992ce97031a201c5e1c5374a53908e5e4b4b21ebbe58385e9ee331f19e1c7940c0bc294855624828bd963c57c8ba2f160e96cecf10144e40e22e5cae6e9c5edeb54ba7c5dddddd3dc618dddda94f778f364b8cb1633e30ff6ed7740046e6c30fac8e1d3bba720862071b651307706c239c44b72d58abb0f199c88763458ebf707b4ac1babdb63d7fa87dff0b1f83a19602f30dfd4ddbaec645e1dea63a87036db48dff6682067f083603ab878333b1229b48d101794a5097532ad5bcadb2a86b7927a2aee51dd4f5af7147f3c75e0ef6f9c6fb061c6f68e35bdee96766666666e6c992f9fd7f38933e3066666666698db4701e65886cc8b1587ff8e8c1fa78ad3542118b6e64db96659c941b2725a755ae7295dbbe6e9c161948d3836ff62af0cdc07abbec0bfbe9f6b53b72eb3764de0dac806b927905ca5ecd395fde25120d61437645268de38d9335a0cae0cca0f1fd97759cb0837752301f83ea1c4e934fe3f3f1b5f0f548813b78fa84a8e37209313060a873db8221eb04860c862b0fecc0f039700339c982216b6900df2ce0f6338c617782748c3823ca2cb8f833586037a02d6813da92685278a77ec848b24be37c691ca019d438dadf190e0c35156852f8a689ee068653e8ce20e9dd85bd14621937945dc22944a7b3cb2e9bcba3c43ae79c3229e906a11b92a0c4e87d61fd3b7f61b13058154e0620c5dc2bffa6a45995fe71dc2639d9799ef42fec78d8930723a5fc826c92512c5de6fd0b9f068d4863faf775308c40cc8c2e45a393f15431302a15caaa54aa93cae4a954aaedb5bd362738eb3176d3f8eefeeea0cb65e4a434be0fc5f7a0d839a9a4557644377e96a5525615b3b5d68e4aa9a951bd924af3a15bbbfa5cd76f8f470dab07c57a9406a145b657f4a307e55473fa1a6b512818184a7fba6799bbbb478f203f2dba0118c3fbe28624ec2c8694724e4a5535b5569d1390b4703a5da7aa49a554f6744aa9ac85818189898949c9c8c8cc9831c3dddd65475535b194caaa6a52aa9a544a65553529554dcada946ace4929cdb255ad75dbb81bb6529ad9b09cf5cce362352de571b161777bf0b8d48eebba21eb3e7b82a13479ca761d17dd7b86c87a103aa775f776b7deb9c6715c673dcf33994ca7ee746218989818aeb5eba638f377d5360e43719ddb7d5ad9ed75bb7b4bef097e30a4dcc1b0b31685b226eb81613d306cd8c574168a75073b9fe9816d96e9dc4fd7c1f002b7c1b823b6025b35558dfe1112877c07888d9492ae732ce87a90f864be8c7dd447d7a30f6f8185155847f605e9550585b88d5310035c82bfc037fe4e58187e78a7e6fa0b89364419aeff9c9af070dd456f02ff512ad842db340bf2059aec7947fbe8a1c4bd7fb54aee6efda32bd3e26313c28651886502eff0e5b8c69045e5644a29bfbc0c5ed68db1841b85b884fef0660000b81129f46e7cae1d1806891f88021b3f2b810da3900af14814ea4fc69bc291cb1acb59b23e0dff06648cce72d6edf92dc69cb58b418d13659617959169536a7a0d073bbf67dc21342bd00b86315c06e5d2ddd19ca6c44611aaadedbb8cd676a3716eacfe55aa2f071bfe0ce5f561135d5c8c1340972cfba1062ada44e07a649bf9dcb66994526a81eb1b687d08deee2ee79c9e65ef31ca8eee3e25a5e017a63a63e618dacd871924d6bd57c8ccdccd20b1da0c921924b39b4142bb1924593783c4ca1924d66f7c4eabfe838d1f767703f10e69f619247688cf20d9ba4d0533486cbc33482c6b937651aa1684f7e2e2e643337b1dcb5615c57639a70f72d2acb6367fdbaa6f7163e99aee1cb76931e9dc80f8c49ef8a51f8f8fec495f5a9fd42727e83cd1919b104e409da0c718637cbe911bc77dabedff04cf91eb4c52b26cbe1a5a2a484aab199d9de7db945bcde8949ec98ff8a573f9592b2d4a29a35633bab916dc764d27af276e73cd4801c736c28eed92c51de77d03798e0746a00e64079fe87022ac57bd7d443c8a10540d9baf003dcd36f37deb40bdf4d3d8667e7c2243b1cdfc6eb00253ca96524a197933493a297529a913eac4afcc9c644e6475529db8e6647330c88f13a5e604cc99d50eacc0f58ff3398d3ef79d31486d8322137927729d496788283f6471492a484a46c773b48f400cc4400c04c4b106ce89b0b884827188db3f64544ef74c5d112c1ef5eadb1ba881600d046b205803fd8cb014b8db404e804e4e4eddc6694559cbc060abf57253cf668f9248ecdfd8527db3e001adda0dc58205bedfe60e1cdb9047ae8c49752c226cd3bfb2f94f55c3a2765297ff0883618c723bfea0e2c12ea9b186d52ac6bca418e3979c1344854e97d73f0afc4002e213f3eb5f0b3d8e7866783e4ed0d14161c7fc70a83cd634510cf04dbf0743bc3f4f946850b5a80cf18410e904f4cff024122b6fe468706f2609722b062635a3e3e768a8663a43c9782b80a9984ca56dffa1c0ca44fe18189a8ebf5696cdd1574347ab497901bf7d351fd89a0ed60645c2075ab877ee9d7be7aab0719535e7542d44d5b89acbd4031baeeed378345efdae99bdd5bf8ab2d1abfaf3b3d7fe46c6a1093703695858dde93dc09fab345bf6d25bc19f0369b6edb7ed6f7c35206b3ad8c87a3022c2942b49a64c4925d5b870e631a464f0bbaa8413fb43e48d1f043785843c46d0a710ef643c7c04354813411a4a93f5f7f83768840a4ffdfe9e3652bf9f5b1e055dd0def434f3eb6f4f13c19efadb576e6d200dfd13d1047bb437bd06d2649909a4a1208d0bfef57bfa4fa011effb2b68840a8ff7fd46baf7e7568f6684dd46f6d437bd093452dd464e24155677a2aed83aab6449e692dbc8dfaccca25e4998267682d34ad1d12ce21b94141b4ed886c43bf3e5d665e95b8a2b804dcb95e116bbe14afeaa05c03806e0957c49c13a612bf90688eff3b3f934d984c99f5048ee4abe39aaa5a965ca95ab2e96ae9441199df26f4829b97a44760d02c73642ee75e773a6245276bd925c445db332e14b87a4e46ae3d416a74c49a49414883ba9943ba96b7ef6c56e1797a2df0a692a651fa5a7e78f3f893cc73f8653ca8da6eeee7f538b14cf3175139c4824a4801be21d7a2748e382bfe969b2ef3f3d4d4fffe9fb4f208dfc13d1b5204d06f6f89b4ccfad909342bb74cf2e7fe736b32c7c33fff4d1229912d80d9c48304d6c6dff52bd9abe729b3937f051576c05e9d05394149b8134d9b3aa5773d2d9857b125660e7d32a9df3148aa659a6c64403824aa145da4f97dbcc57a9c00a86fcfa70881aa7ef8c4d2512a6d8fadcd7e7c01952d7eca257732a4da4a965be09c8d2ef7a359fe536b5740eb74c32d8f9d1e351e97c9a5ab3a7aec66152e5cea7438dc3ddf9f4d538593a072646bb0071e78774ca9db4e84ea2194cf9ea22c50d29963b29d19d5f33fab590650ace6c1bdcf9ab2c37bc71e7e45e2819382d2a64efa06bee1aa785d52333735556596595d2a57c8d8c181930328ec2602c1f2f71050540add30eb6ff4464b9676f02dc9f9c586f85ca0561e9d5ea3e6ecdc46c685bfe55ab1e06715082f38bb2fd06f28b552ff58135afe11e79c814e8cbcebac894842a7fab9511ac415449219dbde8b0640b911456968041cb9dd0610aaeeb003e8c2f6374314696abe2c668c1eddb7dc471e37480de17b8579104eeb9410e3f15b22eeb5cd58de2898a4efa2390609880096d020c265a1333a440047dc394095eaa30f2b284972a920a1a5248a1194620b4a3dd5dedca80daa518b4c5a8a2c3a56328490ea11c425936e79c3387cb8ae906da3141f8a29b52450e0dc1400e3173103333771095ab5dadc4e5449228442917fbad0cf6e2fb17dddda95377774a9d3a6da7d4a953a79452a79452a794d214e29dd4d59d6447d330882acbfc67c711156935c78bfb4eb5d6ef6618efc7f574fa6eec660e2539847228f142ed4d2fbf5086d3c2fce9abe7a0fe048643e43d3df733a7b7603d813994c412ea2987500e25a7490484e99b5264209adf3ffbb9ca24cce1ba358712edb33587508e2439841a47490e57ad395e37fb6a2783cde1ead5911cae1cae22535325b8a36c661e04b81b63f420a0a3090cf1c6ded15547138eeb66d1dd4c1f5ff3dee4813a9a782a789f3dd7c4e311e76b62e995ff3cdafae517ea78a263e8ea68a2c3d538f3e3eb60a2c3a5a3898e211d472620ab6348c7908ea1d9c5a9850d414642502c75e19501a5a59cb56adad0b66d1c4729a5d493a64a4f276bdd524a29ad30eeeeeeee3533356564522919198f4783fdbdbd68f494f80955b755d755b187ae5fe5386efbda11ddeefaaf9c35e5faeb9cd0394193880a26ae572237a262e8faab6c6c2b346b4f3fbb13c83beaf100415108806e7b3ca47784328db429a56e4de1110ec46e83617ded63d98155e2206909e8d6d73e158bd153217b1a2f05e29d17bae7be5e8efb4f7bf1cdb48183f014b39103eb3de77d369cb0dd8733b6cb49b38efbaa66ba3b7c26350e0df74aa6eefc393463bcc3ddf9da8b7768dc39378f0737e78c3b7f8604d64f3115b83b80db81cc371c183e43a05ede88ee7ceda3610194604753b12766b44072ddf75784f69d9951c4baaa9a9afdfc1b9d097c90dd26957d2a2e0b9b5da894adf824ad644a680000004000b315000020100a87c3219150248e436df50114800d6e96406850321707634192e3400862180619630c31c000630c410691a2aa0d00ffe0ccab8c9176b2dac0dc7f04b15d3d286aa8ba43390d5822007e77885ee5db24d68ffabe04e229bfc2d5d3e416ede6ac302ff663ee64507444d876a60cd4054849c917a22db79d7ca11cca17e50b79570b5b7a25a87ce19504ef031b48e22e1ab9b8e3daed38048d57d441615a470d6f65b00af3ec903f162acf67517542e73babc83b2f3c4175f02865dbd748e3c00fee0af535a4e2005868d8be9eb4b80cd835aece6e9c50778e16d7a1d247003a7c316d71bc4420e1179fae2c8a989ec4845296d9ce5c1d030828e04b0ffc5eb016f5ad48b11aeec8f607a39ef13c78d657e4f69382dc3fe7768392c47608218c58b0a2a3db138e97d481bd5340f98d72316bee70c536bdd3b399a1f82665c643231567cdb0dcfef5f59c7dfd5e7a7faf63d3abb4a81fd6edc7defa6ec7d0bb380aa2cf72e6ccb8ac478d1c52e5f2d5deed1006ad57fbd8b917195dfeb197478ac48b45ca81ba99e1c766de7b98354930586abc68d68784d4ff1f1708860ed2e9111c73af8b934a64efb6dc257ac680cd7f3feced064e50b7abe58cb6b60785360a77e36a56601cb86b33228a2eff6adcad3fb102608ad556813a48225926356cb2b937159c08296c12440a805d992d6693bd33f6c481a244854038d6ebb3796ab28d2d9b7db3e6f1ed288eaf4037abf67d877a448026a1f16bae67602b50b052f8b502a8626bf0a87bb548f48e41c21c6f328dca5bbc70f682d870dd16d4c83070c78bac1498da0bdd0a0a4b673e94645c9c4969b48f1d35803f85f1adbcf1b0612899efc55e2c8f5623a4eb3ccfee2fb514dc5965709c95e967713f778563d389f303b45d620cae1a7a02cf6331e838d989f3e0a644451b56f17c5c5c1d337b7606c40360f6744098856b3db9063bd61d3bee0862f350f94e9b2ca8cd82e8f1971d8e482889e53b384da41264a93254be41c2ade9deb0cdbf32ab55e61a223800d6aa736561adbc31c33bc3a19622e9a07d06fce8914659b61c3f1d68b47b8cf3d7caaea442c6bf3d4b3791586d6803bb3a3f8f878f5ac6c3dda7c39b968c54d2ed1f12023a7dc21e3a74accc10343f47c56e509d8a28085aa31be809f6f357334890cfa51f483005fe91b128462bbfe49f0db6956ef710e878b76f227ad07732c5f8aca3c0784d73ec248ad38f6ca5af88117cc31826b6f375e07b77fdbc7b6d434922dae40c9c5130d88a5601d6d486ba3f57543e281b7f8181722f601caa6af7899a1e858be5c66903b94460bc8e198abbbc8eebd43ee82297a538c5ada08326f8b8ce045acbaf8e27bf003db21cccb3056e07c5f2b5b8605f305f5285b8f6916156977de5090eaa069e4b1828ded3e1af211825da7f9d0ba55bdbdcdb68c6170dab99c0f4a09515ae557272422a69c02dbd1c6f4bd23465e470129761c2df7426e61223d3651c126ad478fdd9499c00f6948895570d40610db496c247a154ff1101646acd12d5b2e5b1c991ce162951a5c30cdfe279569a1e1bcbc54d71c9f2a5b14a62bf45aa9a5f718a75ed80c70b8b25d2b88820f863128e73831c234894447d0c6e42ca275d85a8e7cae967bd5ac5addd1d0c97b5b2991824bbc368388c97aed14554d925f1ea833387c2a7ca064a6312841cf1cd431b160d8665fe59e41a0f5d4194843ef3d99d41a51219272a424ce203a377e2ef31d5fe618b44eb7dddcf9bda0f3076137a776aa37bfdfb430d7eb0b3a0c75e21150f9589cbed7b2d86a08a099a97579a752e40b53f5f76c8623ff943aef180cf34bb49705595b8b5ffff451c123221b61d6d333c93e479048f3fbed1b84e6631c09724933125e6b03c0317f633849b35ce6d7bc35d390be0df28888aa20beb7569f0c32edef2a3949958272ee990d9242e389f5aea4df7d90688acb62c726c6f69b8b3211411b284e5501626df54eba84e5db979208e5f434cf24c0191134b5e182f25a492083c0ab5585afda80e00805decf58384df5b5a138e33380375cabc71bd9c344ad763d7161c4d999388b9f690e8d4ade06dd090294c445a78d556497a30c3605eecc97ad988bd1053ac2633d2abc8be6b43ea45a004195d2846387d5e1f3c8d2f104608441fab2ae58f0165792693845de9547fc25741366f7ba486d8b1797933aa27e20f1396d38b1608c1b1798ea2e8b78e16bdb591fe4c6dfd6ba90ad29085b6ab229034aed62883595a9c251ff5873e997242a203f2fec87318163db436834388cf1c7823f329fb5608309f169b0cce8d04db9e40f82cae24462553a4872ad7e1dafcd05ebb0d61c5912f25dd4a867de2a15e6894e056b28df727ba80050e691dc532bfdf457b7fcc85334c06d30d1fcb57f88367be1a0e433e6ea2c21a0d223d8d03e3aeae82711993f6672722a7ce9b3ac5c05d7f22131a143f46556a5ba69929abc214d79ce4c853b59405ecae314b84261f716fe65bb5a6b96e7ea28f2fab354564ae997861cfe0400d636e966ca50f49ba948783e4478d164be185a7af4bf3e9cf44e44c8b02c854aad008342e759dbeac9fea174396cc2473044625d70cc83d11d5a6ce2f764cd9f875bd8fcb2725a175cd57ec0f2c879bdfd99602dae66ec10e424fc5cb8aa6e9bbd061beb3282738f3bb39a8811e5a29b5154ea66c05e4ec1dcc6fc49c8e9274c021e75f1a7fed38bae859b5de3238e6ebfcc2b747f62d095c37eb6b0dafbd624b5301da445f55116667a4d34d74bb5f0740cff48ca9da930357fe115a149aea6edd60441528c8f46c5e87ee4a8665db49341e96e125adaf37a5eff6312b6148c929ff6ce2244b391659d696940d75f9be9f722d053b6ca4f7f83772d52176004fceb7e4239bc6ed97d936b702b64d0a827c1a97a478f39f06c249558e7f56a29309711fbba118e018ba0cfb1f6d22862be3252921bca9397b0880cc7dd2a8779ba44c2de6283adcdfcb16014bc8df3135919caa078bf649717075a0f6c227be8ea6789895dfb12a85c96b445fc299b04f9354bf44e870a71972c0549f326615570bebd139b7060eda8875a242c5c9c3449badee9c1c08662748715acdf9fc5943bea9e34e1eff75310abb42140dd3e73276a7135274051fe18955700eeb5235585021d38c6f5c9e41e9b5aa23a343ef9941bfac62bafc069d32f4f70c23c555a22884d2811bbfc7ac2713cc26de02c7ca00c7e574556f839520ccdb10d39c205ec36534ab21ace4e138d6ac34857614ad9931e293911a8e4b2c87111b46b1732f1c6a0567e938b551262c74e53aade99e06c05be55fde979bdd8492e4a8daa2cf82ef67512cab6345f40487676653b3e3dda7ce98f3ff29d5bdb5cfa9ce1484c5d660d1d0d90e1120407fbe537e9d496c203eddcae97f17424c8a2dd8fe808856971c45c336140a0ea24acc54c7b6df655a5c6ef8e44f3df0229ab6271c9c68d9394b0050f38181097dd2e19e8226984e1cbfc6ee77c00b021427274f51139499ccefcc5f40066e3b3e758a3728b1d67204165f2034930b9c963d04eda615f609ff01ca36c16bd0bdad3f7b029916f7b46203ea04fc7715b55cac1255c0ee170c64ff2b5d269cba1156ef43b18e9c8183d3a40687fb6c4165c774286d62a042a5e0109436921ee9063ce2d4fd938043c0ac53e44cd5660e645841d8f5a64009ac8ee3cfef91e27af745a292190ca3ef4a50fd391e7f863fcf562b34b3b68db880a9d851284377609898391c4d6b19305aafc16c212f23045d9feed0366a8295ac3b48b6c8046141a10c1d188111d5de7cd67d48a83c61d84173e58a79c62ec4192808322894e18201cfd33733b3c27c10be90b837fc3810b69a2435d19b24b9ebb202805a0a60193e970022c347291db464b70c2bc2f119fc93023c481d5b3b98380d8011dd3e03c52d9045097cdf6c9d5ea0bdf4673e49a7706ca1c6207fe3a27dd31012720f1be7f5770814a4abe963507c74337587eae014174814744ec48c120b096a182f33f878169ef8a4d4055b86cabaccd81e86b85ffc4139f57304ee5e36625b240fc00bb00d2cab3f0ae8021c28bcd4a4c25ee2c7cfa01bd410ccc05198c42157c170bc6f736d3a020e18a82bc6b46484daa27dee4d81847e418cd9c6075d4a8a7dc7e327a51fa8e575ac9940c9f179c7370b94e920a306df32255913beffc35510ec9d36d871378f7dd8cd95a6d17d5d661c07c43e30cf719ef5b2640dc52aff427145c062ccd26dc94692e01ad54020ab0c2f759670a1847eb8e4f9e45d701d194448a1695d5230daf75644cb7260c86f87c67bf7154439468f731fb5c548123d199e3b7e1d92b6f6a50f584e828bc2836d0539de8aae2773d1c788428d870d006ccdda98f43f3b9f38d471471f4fcb7cb8e3d2d2e93ca74ac701b7999984d721cc7d013d5afc552e62a00ceb1ef1a2ef16b3eb5b10a96814367b283d9b3b2f02c8687231b3415987349ec21451a08c0bd67740ba8f93d09d2fb790e1bcecb56d9f9801eaa1d196cbd241d9a25323c23ca73d58ece798b9a8774bacb9d2dd42265b59d6f5c940354d992bec4795e0ba33f617926b8bbdf59247ca744b0cea638470c7b221624e6004cfc564af0bba1b34625a4fcf7ffd8b28fbf072112e9889796976c37736adf445eeeb1a84d697e5fae89ed91b2172e9c6416a204b9ca68fda0a83fb00c429f757d80473175672ab5ec2c77c54673998ab304a664f8e078ca9af0872b11029c4b8010efc720d35b378a5aa321e2b608feba4220ac028adde00a2929c926ed85ec88c00d33b79044f135f007c91f43822c0da74f86922d66d1b81a1e99384b35e42e1c2938a39458000d7ee00ed8acf80d0672810ad1f507e54aa6d61ad4d285391f61b6b10c6cc53005cfa72e789557d30e9e661de0c79ebe091180379b2b53d42a01aa524501374405910aa7fe5b9696ff5197f64f135541ca027a639c19e224dbe34d656a80f5909b2e129878e465d9d4194be7788430d92432954eebb0caf7573467b89cc9dfefb46c98c66c913b52579fca24fb599009ff3e913217b15de4d24677c25fbd395a35ae254deb5893cd7b9d74c946b337a6526c933596d02cc7a4f85c45c166c6217e4d29ac5f87559b61ac3bb9c15c2cab7178cb6899006eeb84d7a8c2435b9d223b891366b26570725997fdbab8b79d7e890a1c28b86e3693e5c8262a48bb63cc67bfcab7993dff89b00b805020a94e100c054cd7a863c0bf0d308494e07ab84862be3323a9cf9f1fe5cbdc25c6346842e9dcb291373a5a4f30a22d39d005ba5a0b2a35d41a908e02b9822e0c60588d8fce8285d05581936eb43f61545acba3878a1c6fbb5626525a567cc695acad540cc15ce9463a9ccb195d25ab7657a7dd30ba2518452d6b710a9d844997612bcf705d18e30d3d1c244a1b5f602fb8968b9deef00effdd236c1d4bf236190b445b5b2c4bf4f521afab098b2d14964e00bad524dbed11c508788759aff4c7722ef32006017a686a91758e7be8f0774a3c395c60f8324b360c61ba20282f8aa5cc28017542e23436e0684e6d2c0b5b9621ecfa04d4a541d2ffab14ffcb2941c9d42d61c624b1ab152439a72ead415ca705a549f8c28f488634e51b6d62b294f07ae47beade113e8fa4dc1bf94507610c3f0182789455290e7f1494a81795e2eabf8b562389a30acea0e565d2d11b78a2b63c3ca03c17b18160f1f4623e7a2651c7c9c94532f916fa8a77bc1e13597689477d4247e1fd94694d7fc0ac4b7881d4ae798e8248c10b5dfaf6e1c2fd7a45100ee65b4066c645458e90f33afe1bf3bd7f812b3f8392ff7e949f965be4e1af7ad139dad5b292f51bb1ae9c444a457e1ae4394f7cfc6295519888db191f202717588822fe4848c3aec0d37dc09e2cce0770b775c2c80a0d7549feb8e83703ed4e59ace75a3c4288c18d3c797c9a518b186f850f0a07ba5078073428d50b31cdece630fb81c160d18a2475cadb8f8ab631c3abff814f4a21eac9c07f3a1592f67a13cafc7d5ed54b31617acf10d011ea3850d6ab953b7adc010f3d19f4a9ae1b53abe30a23b6988e36bfe56cc80101363496ab304cfe673914fcaf4332bc2e253b5cc3ecfef074a3ed31dea2a46d098dd508cf9bd591f9179961fecef463f51acca7e0189ddce57843994467a00f846066aad9866916b82c5311e1038b3ccc9fabf03f1188027fdbda731943712e58d250df995f7e5bfd44fd6b3509ecf80ba584d9fbaa28ce870a1363d78b0683d31e60159d2533a929c2ec94f9d1d10ba1364400622734a9cdda9f33c350d41d6dc2941e3cf64829788ae7bdfb5acfa90cb382760cd613b5160e2cc47d19ef46197bacdd69cc6d48d12bb3577425834af5c1c94647a74942eac49b502e2952ed9acb0bd99c17ee0a99bc485a03688603083b0e4aa736047c1407da2b396bd041eadaa4c0e76d2229ea37321bc50390c310fae24f346022d659b44905452afa19018ba418af00ec99f1144763fa7a2183a0a6cbcb3d91de995397c9b31b7c14d0b3557fba3d8345412f080b5cce25728f89d04a10a1b125d12dedae34c887c8993e550128455d55f8aec5412d59255a83a03dc81d3595e2d3f629640e9b4d80715f5fb31772642dcec508c2f4b4114bf2e8c9e75f15fdc048c6d36b98ec44926450f709d9cadeefa6d29be099255923de37ade043bd651bd5fcc84c052d013304502f4ecdd45f9383c88ee5a9ba11a71fb2cee059cefcced3660a0672f385dd8333f30f01a16128ac57e0fd21deebf73f1db49fd1e82fb94cbdf535ebdff110af145fa696d6420706d4fc5acb554623b31da44e682a0d3b249129cba1d751cf51fa2dc4e9f3287f5204903cfb68da92e5e1433e3a2c35c1652c92a068a59b3377f28c621ddcd726d03f7854de46c865fb6a15442dad4f272b3b1b0cf0ca280daabbd8835d052d36dc4e6edeb15d1ceb386b6a754d76276dcda0bce60eaecac6bae8803db1b343a2fad44bd5476cf4eb2e691e3d388c02831c6d7aef55f469789895b952940fb11bbb1b75f2b940ec0f28cd098eda028fed5be1175eee5eadfaa35ea97b3d469a5f2292c76e30d48260054fc27b16941da5df8c64e1c4ad0c98b1b72cca81a731aff33810a5206961e71aa5881af6bf909e886ab2af533e66e4c57bcbb49e656556f354ca01308244e849d60704f07dc8d47629193a8084bee3077ff7794da039ae06db8f3784872953dc25260f43d15eaa286d808e9c4ed4c32ba94526f1bc3934b7c654780c5ee2f31ab77e0282506e4c990c71a34fceb5ef122290d10cbb514cb7e7f3e4f95925d13255b0171a299e09b21c1158598ec8a8bc70af77d177f6ad9414c1e34749ede85ff0262722e066fc76d95c4531b9678f1753aa3df20de573ad1a877eb88be59dccf9fc324be522a50d9ebcd5593989a1b49cb36edb1f90cd202e5766ac29c363bf478368eb29c30116da9270cbf9f4c1866eb23a632f926493e2a7763c0a3fad21cf9c26404cb6e0ccb29d8ce82689255a885ec8e5898d4c5eca8eb4bc26bba74abe56d60549884fe48089425e614267b5ae37a1ca6c641b29547dd0993cdfa2b9fbb122d39c4844972cc4f1a4a44f48f0ee4881dd7e681b41f44d8336fa73b4fba0cc122306772b45ef4a86f8449998d7a4573f4958830a9bb9659f7036ac38e9e18c7cde50c26598290454e78f8ffd35c50e2ed7873303975a7f9a97ea3d41545e19d001a0f6f6a2a5696de49f987f614a8cd1078e482495b736a0e2561735e970766db8ef1cc74b51b6851144cdafa3306eb8f61ebd5ca1fb4824b1799afc698c3a135083dc8be25c57842304914f71a212a9656c2c9dbd989eeb62908b0b0e1a87adde9374e0023de17da9fb8179884db62a12a2683806a081f708544259ba1ff2e400a4c4aba26a86f886857ca3498d7ebebda16e862455364cdc322d707c418d252ca70722104fddd458fcc0d48385b46dfd2ee68e6778f5850a062be5ef608606ea9ebeff7a03f6032be54d8182696227d8cec61654bea87fcb1a81e758059aa07ca7cf6d6a45a64bdd963641d2ccf46de93e6b32d4a127ff9b24de2bb1fb882d4cb6ee52c7d481a5522391932154cce1e2cb86d0df020d8783b8a275a1ff371629d0026673b3e4285bb0cf7d9d79a0a057a032643c76ff1df95e729efabfdbfe4263b3941f4222dd630c66d7628cdd61a86fdffde9662ff2791bfe4f7a6f8cca04f94ca536fc35f521636e494c716e5c87cda60a20b89721cd8b3cc55ccd182d9cf95b01ec293924c20c6a84d5fbc4071f06657aa077bb43a2cbdbaae1c6cbc6a6c402a48b4a4c8189af43561b3a2d99e45f80ebe1e40824b26882c25e5829fc63b0f595d673ac45aab2fc0c6c22fb864922214e94f90c025e9dd607479f552e24c5dc2f85c87f748f5df92a608f9c7479ed580aa7192ba70758c981dfc4208263dff6d751ad6aeabbc182da99ecb8f979dfa2d790fde6daf72c6e5136fb1b76466160edda1b764704ae1777ac11b99e0054db236068e44b027fef423b4af8f03f289993703d84160d0edabd99809fb268ba7b772b02859a6b1a8f53ba06c4424de90da3a97cf055ca6e36f035074b5ab85b8ec3bb2158483cb64356a4541c0c5015b0203b084658ac82dd9ce451a082aefefad1bd48e753ca0df361cf66ba24d3f2589efd6f0c304e239b9a824a119604beaec0cf8bc54268fb2a3b0416a70a71bbe3d8a3447bd58683e83874b95b500ecac42c8cb0ac2949b7f4c272d67c5a7a49a117862519c58cd4ea366f46fe77e8f6d1052fc5acc2cecd303ddb953a8f4871f3a3408a1dcf68bb4a8caab6b36f76d04bf9d7a00018c451f2fcc14229fbca8e2be58c1cc4bc28944731d9124f7dc02b5601bc1ed87e5f9b4e84ee03988d80be45f2707ce212f5c78ce89052c0502694e91a93f9d219edd54a919026334f4f068087688dc40bbacfbdb484228103fa24e011d17438628d2db03d85b872736d03471cc8fa2377585c38acae6e7923fc0b22ff8361e7bbc90cc0a2c1c775758a6355d0fefa32dac046022decccd3d7188c585d6b7a78790af160c8cf021da7fc362e4025aab7445fa2aa7d7aa4315dd8243839a9598fc3b60a90d78440c90101362cc6a6420b07b212cbcae9b6110fe554f6c6bbaf669dc9693bb46920980689a77d5f7a0ce312c4ab307a18e2ba8312b736b01477a672ae74cb1de6925797603b9472444da50ba28f1b90e834915dc2e7bd8144dcd8da3f801ca2d1a9882855513953b99fc48d4cffa005bc69e2db5eda68d46fc3bd00241d246b2f798aa175c466a6ceb36056acf556dc254b58d872c270710612aadb9f2a7048a479ad18a34d808e9cdc8e41a48092563e7226c2c1e1fd0cd2045ef71f90ccc5405988d4b2ba5542c56472ec5ff1d56887fb7d65c27c819fed2341c3f9cb3420096af5e6b6d5c38d3e5a9dbd856f21dad8730710aad07e61b2179aac91a701c3177dfe1e819c0b2e53bd068ca95ce17ea2037e2cc578d4fe3975ad187a8beb7e7dfca56a0c608718927f496c243e18dc8954a8973ce7d6e86bb541b4bfe61ec1e922cbd25ab6d80982e6b7a22a515a0f01f409cd3d0966f4b1fe1980358c132d43cf4407155e9495b2adda51c7a3559ca2732aab2df3e4e382454ea53ac6c2ac07413abbd2696c90ae1e6ce5a2bb71c5e75d2071a992a65a7ddc7f88f94157c5460bc8da0700ac4cefda332dcdb27dfae2c902e3e54adf646f0da46f0ea19658bd51c07deb653cb9082cc95fcfcf19f4a6e19cad34bc5377f09dd65e93779876f0a2e63106842957442e9795829667f42d9f032fbee9b2fc6ef3ac11b1ec31d1fdbab2fa8ff80900b6c6e67037c98cc12db38890de9afd696fb3ab4c68652759bfc8a0ce9efaea4cdb73534f8a933703fb975994649f7325036d728305cd41067a0dc4491e4260de32db6e17a9300bf7921e8159dd093578dd621ceaa8fdc23d71e492608674cb327141144982f3bc46376a9bc4e68b7e77aa190570eae84000007718c176ade64e5a59895b6e1b60a7d41a44925d6f38582367b8f63eb3857708471e524441d883c24fd9c3f51f8ff01d1ea7fbbf090e07a2112cbe4d11d38c5564f405de597696ccb2e3859dcee228ee493747ed16e196810e5ae8fdb3b64c4f81f4b5109a7b7633bf81582771a1de2f0bef400d10aac361f017d75e081d4042a973a044fee18b6a8bda988ee1d729494ee0b7dd7b846d21703a5874986ed46dcaa92ea0e82eadb529bc298dd183b0533ef39002baaa5ce6a0d80345174fb760a16a9a11058c8f06a461f5376d8ae466fcecf143d3b35cc12384be9649d38f862858dea067ea18b7cc008a1a47bc123d7ee99d079427644156a3cc8c09d7f804394293a9a1a060a0a586c8f43f5a0f7c85850c6243643823407014d33fa0a09722e64c0a72dbb9fb5e5fcf40348a2f318a01308a0b218b7acc0fd3c3157b1dde8d45d7c16bec5542afeeb7fedff9cfbd2449d4548086af49a67afdf4e78ef33a9041a3d55979928107e2cb60cc83bd9a675f1bad792da471e90bb653ee77b39b9a81b862f5509551a2987fd57060ca53c6c098a2c114a0acc62e55b89ca86bab6f77e8a69e1252520775515529b1bdd6c4b210de8291c7d0394f917c4a3cc0850b44312e00bd21ca8466fbc99df19933d01fdd519593738c69382d417a191b6511db0f75db125a66a0b671f784806dba8acbc4f199933119c8ec8cac15b22f501f58d45299c5f7135c133ab7d2d547f8ad00a94ee5d7a77526db62e950b6090c711536654c8337b68a1e6e9a7151801ef9a3797885e63ee1c8ca3c62faa4a45c87a845b78245f3b4219756e367b818a7716e0b1d196043ca22c981c5aaa7fa79cdd20a248548155361570fd1d2f2c9e99a380764420b8e742c1f8a36b9b81ca5d16006e0a32d07c17cc1f0ade477628b933a4a569572450bf1e86fc2ad17faaa8973f0cd075b8fa4b9924a3d2f2da426b4c93e806f51ce7186be0b88ba6edab63c21d43501f72a42b1cd46efde36953f2a7a745e2d36fae902aafdfaafc94e87f1a59ac8562a4d49af9fb7c64028d7663af1cebb5dd634375ebf17200c6da9b442e0467946a74bf61fe2433a9c2c426d0fd240c2a76109041288c3d0818ffb28145978938f1a9ecf4c08389834eebc39751ba1a63f4d56dd0d4cb907ed00b1dceb2cf6f14789a0f405986a9f68a3892ae8cc5642b00626f082d544185e3041926708a7a13c4a05bb4bee897029a7d7db452e2848270311573399a447e58f362cac51059af16a4a0806cb29500ee36052b5b71d5a6088d6be342095c8d85654d139c28ef1dec1c18bf64269c30d6be3eb47edd21711406c8d2b9a71f81228a9f73dea9bee3af8a3e5ba2ef592a3a2bfe8913cf51637471f467842cbb38bc19ba3db8a790244198d89a3e23c35460025781d2455b05f3f5bd611241d59ddd6a37b1ef7b695f19eff38799098db5fd2bab15c066ffb083e21617cb8e705011fcd88476725f479682488d8e0bedaae78d6d9989d427ec6da19a536c4b89817cd7f765451b1287bc80c45a2413f462417ff620c312474ff2dc2c31676468a1d086585aba0e1d2e8d710d778064154bcdbda2343000e86f3809fd5ac8b616ad57762d72b48ef096b8e4fb095a0b582c0ff68032e300a829e60a0c877486dcaf82736c46a68d157239f25f4fce023421c43bf10dc8b3980b839ec4303d905927b99a5b523b0be53289bffb1d5e5bc0a3fec4e9a7a1f52520a63fd4f697c777a0cc089d0a9fe48bb989c1db8126e0071d80e93d04d49b7fc9d28176d66188482267eb96816074c6b23d97b7633367b1f03ff0a4d993b199273de7904f0fabede261ad5888d49e30d7957c044cba9c56e6fd916858fedcab1562ad5c5a19297130d56c411fc4a18f13a7148a73ba9d5db5c45d2505c95187cfbda79b3c2ef814effb7dc99ed9ca87451185335f50002759b1f94d26a6611ae9525dfc739fb66c7949e4b2c4eb0a7774ae607220f5d973f198db79bcbe793898c0f4a36cc061a5fe8ffa86bfc66abc6e52e9beac085759372982ce0a543c92871e134e7618c4e8ad6f43c25b12cca3fe28978e13c9e359f68dfbfb44a067ee5765cfa5f6627497966b5b0ec8034f8e7eb859ce8ea601c92c75dcf262b3c36387a734a019b3bbc4caa8784ca38f7594ad87cde7e87c1f7187fe1e11e319b2e0e297c9b1d1ee6e6cff3404efade0a953d70fd66ee5e7614464793f1dc099ca21d99a937fea3fc311de1e0f4bd8351faa1bba18e09c6f968b9d4360e6f1ecb7a6c8ac54315de5f1c6a29d6e1c8d148ab32c60d77e848c10ccc73742a29cbf393983c3ce29261284c54f32fca47e0ebf0e0cf7e69a6d33e42508e1dc635499b480a4df9cd7364cf522e7ae2c63279e1834c52f1d9eac4e6e2d4598ca09f72226ed112ec51149ac68a6c265e053fa87e480a1f479b0a46d03d711eb08d92496faffb44b339cbe5c932684cb704ab568ecf6f1caca31578756be6badffad3634465e9b119f59d8faa4d5cb8652108f4a62b52cace3c9c01a9a892af9bf82640e6904380654bf12c947706c1142a6b94c9300f1a7f035f86c9e76127aea575068ebfb9eeb1b19959f4ae6fd394532b4b55c913f49ccce76e165b21cf387cadd367023deb1e51be415475086cf4eb7d74e68e85e4adbece3e01af6af329f77a7608a9d9b4b4fe5529e706c64ad9075555e2600d66bc5e5dc522de0d3c6dd17df6f680db3fc945f14171d9eff5d329cfe405def9bec2cf5664f5fc9af6c4aba73f211ad944c44f4ca76d1364f65c362d420dbdf76aad4affceaeea64dd4222ffd85a3f85b7fdd0fd469280b0337279a61cd04bbb7e9ed6dfbaaa82a5d5f8d31b2d65012b87bf3786c0678d842d8d7bfda5ff595fba8fa81ee009c7dbfb65baa6da69c1a4640add054c3474579a12d22911c1a97169cf9ffbe38227866b32f5e88ac678d2f6bc88658569541a154099c7ddad6bcfc46ddd0c85105d9b439cb43d4c2802cd1593f0b6a820fcb661ea463526710b3fc612a8ce9885591b90c902d3937ade4a02f75c2f5684a51c8db96e4a6d07d7b0d6d22118c8bf75aaf2984a93e02be6e9a42c84186bae221dc71f476a81720bfb8ecddad2947ff06bd3658e6f90fb7f0af6c52fa6edb5b6cd86ef2b8bf1e349401ca88d41b2ca8c721b4a0dc6b39bf4a906958aac0cda36bf06ec263ffed444c90b6ccc954366e5c22fe2ca5e755e5202aad0bbe270d4b239c33c8fd2efb42d1b4ac6f770eb38920cf7c34e671a2c430c3c93a23e59d0d61421868e2706c19d3eba347606addaa9c93ed5b25b32bf97b30a7e1bd9b98592ddad0aaaf1b6d8b4ef35e4d372add18dfd789a82517fe52d00869fa21cb314a6572baaf650f31df41d22342b3c6a6adeb720820e979c145e10944048d1bfe7c07bc016f69813e5c0ae9387fe54c754a8ee29df30f04893998e844c43005d3488e47099b7e6f35f5ff3b4ad994af82f7c0545ca94e6dc96e08926689c44887cb131b2dd24b9236b6910e213f5cba94b4dd33f99d3838b108ca04759938483a9b29000c581e0df21ab7a3f3187f09e151f6e4cb18fc13510b6c2d99c57fc9de4756dcdeb0c651e7107fa18320986b428303af513f762012b61f332d04b0a0b2c21f6bcd08407273dcc1a9fb30a79a67ccc8183270c45b5853a59385a7421010907dcf84f96f38ee2cd44eb2542cf66dfba483fc8b8e052d406ce49974ccaaadce6cd08ff578b3fefd5df5f7c2d0ea9a052a9e3d696b1aea5a42f438d7e0db27d69bac06ef6772d44340f9c90cf7428057343953caf519913807010e3fb7f38f5e3cf726b004d0779c046433719911bd150762c932b17757e29484afb54a5f044ae9d68045228b520a90361bb06b51c169ab08600bc26f5e06ec2c4a30db764e35306a28cfbc3164274d09aa0d98ed945ab0aa8274dbd18e6d85cb819624ea2bd72fc4adf9f10a64d519d74479222035e09aa65c7a0fdde8bfed3c2ff203d081e1c14c257c1d9d11ea8ff4016f1046a8539b2330db4859570659a2b48837fb770dd94e6dabf118af6cf4a4799ff93efe2e9fa655250aa57b5b5677d4c8d27a05b37616cdcbb172d404c05e38561da62eb144f63ea040acbdf097a560bcdb13f61e513013bb6485ac5a0d022cfab479b771edddb89f906a08a7a52828a26233b0be258c11c84071958d98d04ee5f980130b58a2e5f215ce978b54784e42901cbaf0d794688e47d631f282b122b432f951d5ca83f31e6701dcb3ea4f189b5fd05dd979bad15fd889a8e1a8b24074ef0bec3922f2e09eb583d560dfced8e28a1f261c2368364fcbc2f5db4ba139471caa0438f575ffbdc48d39708fe760cf87fe32f1e13a11be830b1e83c42732063c40b23e9c418fe0682e28dbbf62f738f3b9b789b73abd460da3f47ad770815b042210228319e818bdefd5b236d2faec84fdb6165913b55d15ca77b029ae1bfec51598c55b0a0df20ace5a6bdb34a69238409203750383c488c1863051e1e23fe8f9dc6b941b75d8d41a29f90b6b4cab9388265188f9d2a170cd67d561a9a1c746beed657e78a05840d4ccf207e63f1238f9aba01e3fc1191b75616dc057c66da126b43eac67023fb040eee03969c44873f882e6628534c8fc99359c74a509dad0f4c64987733a8b3ef283c722cda30d6189e920a97c61d214a1a8fa7a8f52ff0bd71ae55587c72eeefea104912b4379aad4f74d231caae50ea84f1e8ceb84cae893cb6414b0665c0a1c3338e4c152125314a7fa17f0c961ac010067b612d989f9ca9657645d61d81e3f21682e30525533bc708e1ba72f13e91df2f5150bc7ec0288f2234acae5a71ca7f2bf1abb4e94b79ba911d493077d8aff1816c86ea06585a7484a0e020b4c4b57b3c3b690234f99b0fa72adcd223cf7c15bfb5f601d97e58a0e6d9432c5006d5e074503dcd81ca934140ceb13b9bd2cf1b6cd913ed685517de48146573f79587d1da860d3dae8a2a60a565d04f1e36c61bc61fe580cb4bf15124cb6e4941bc94fe8cfadbb88b718da0b4dfbb468e00a0f21d09eeb574921ac0cbb425227509476b9f4e998743190f05f260b07b026ccd8868f5b5538066119eb68ae4d10230997508f3e5b3f284c9281028ea26040ded82a6eddfd4d829f759dadea2c1a4409664f75832bb1bf7b096df54be7da4596ee9eccc967c183fea8e232a9b65834aad2514d27f2d45b340c1eb0d7862ebf2620fc4e07f04e732be90c81feff04034664073c8d583ccdca5ac0740eb1e650465f3b3401e6e5b863e22ee3004927e0013dbb3d8578e90124928b1e07384908ee546599a4488077dd7c3f60df7cba6ebdb5dae02cb5c4aed54e3c37ae04997a39c9321112974042e34bf692ffb9e6400eb5dc3c6408fb29d525ccc86e14819072108ef8b940dfe8458e02ded7527c7f45a0355e56c9e97b61a5548ae13b7214d6a77a30579c4a3029864c998a09014bb4506673074b5b726dd149a31118f61903650923b906458db0afdc7e0c0c296172ed79067315e1042a86a64738305448722d56ac217100147d920ba1e452431a137a225294e05a595453ffd4621ca07113ecbe842425576c8e22530cef97fa7896e23b181f5a07293937469460f155b2f375f80903f870ed5f9cc3bbf22b68c1b52ed18f79a1f63dd676721554aad9a0cdbf3e29ee8bc178ac92d3bc29a10f051823be3f00ee8ea1508ac98cad1686cc2a573d79998e4297ebab0d750963e87f1caccba3294b2d5a1da04d2c32f4bddc883bc1820db8325ad01a5cc737d01b0fc0002f2c8acece26a6a9513ed0f1edf5289ad33f023c74673e65085b3559a98526440c3d852c3705028182206f62951101db27203abaa2c8ebaa223e7fc6a5be3f29ec38c95303a429b8f5181c8685b595a4da48cf9fd3fb1993dcc4b396539278c40383c973413c174fa54a337f5cb7ca09290a5f49edde4ac2400564e160182f3249a0f8da737487e4f47610862518490c0743eac22f05637bc5501f0f5357802a5ce57e6b1ac7aa4b012486a790abd12f3266e7321a57bd2e24a980d8ba7e8836ae8eb5984acdee6b2a5ad87f80c2ca9da8ac8dabde36763903461167a69c02071d06a12ffcf6bf6c3b16fb8bad8d2311251f80fdd3e67060bdff8d05c2dc31b25333899fceab28d0c38c187ca3aa64ef5e89f5a7b9274182ac2130f9e342809f4f4068a83890606b57e8107732e55fabd1ba7d62343a07b36ba83fe04467eb81f6b1536bcc2dac9ee49cc367c455be6d5bdebb9c9d702030e64ed0a17c96d5526e34668eecd3061a0c495507efc95e6432f57709bb6c95ee843db9ca1514bb8f0668ab01f0082e9e7bd5774c93a2c95c7b00569f01e8dc34bec2d9b2ad40dcd42c600211357419d0347b7dcd3b63451ac0f24c61fbd82d8d5a96e235a909beb4e70b2a46f3c20c9f18dc2714d7712d34a3863c110e5a71a02f12d1110d4bd807f0f60b29c605ae4e54dc90a10446ba46de1052076d6bb3714403a2d6b063fe4882bfaa5a726e5e8c88a0d4a06f5b56243982f6f805887ab43b343e84603dcdd5ae1645b25acd94020a67a60c02d314748a80d45a6fc6aca6f89200464fc3b485a5e95f0721613c6c0915d4b653c5c1b00e4c84dc645dd84de64ade0e4a0348f27be572481bc0035e32011c64350e45943224a71d117a6b92e09e59662b51cd47765e6f0e8109d7cc6d43e1212eaf91839468c92100a6a9e6189566e0981c32a0a7dd24fed434cc21839b4fff4d43985c1b68ac2775c175c71b729223142bb66f550fccbcd42c28acd7fab4149e0ab3fc4553f1404d51b25a5e6821abf3bada145033f23d735e68aee28700705184f5a5067a30884a5c29ab3cf938c31ea3318b14b041e5c752f7b45c2c2dc87ce82698622acfcec3043990afe688123bc2e94d4be1cf9b8a4824c5fab05a24113418385b5c25684bb32dfba1925188b569216953d6072a1086382f9126689ca765ca59625abe455d96e95d6909de2ad713847c2314b255011f46222abb6a058f9b537685f6194987a3ecf82dd404782105da2b61370b532e559096e5ab6d0087aae5af5ef4b57d589bef549d2799c863811f2d8ce73fedd564609ed9d6b8ac546058a1a1a1c43302c7d097ef62d880f658e1787af11b8bd3725b7e0b74e39946b6aa890994d62659cc69da5dafda9643e27d577b0e571124185c4334ce9d56c36e8fbb96541f01602d61d84394311b03c153cce91a814888ecce45ef04563b1127a93f02ae8a63b137c344a9adf548b593a5ed7b0fae39c04140fb1e8f545c7c0e4cadfa49c2942ef18fe535f3431335208e91989230f3dc142757fdb1a408335da917fcf8c8bb20bb5cc46ec132c68cef00ba302b792e0df6119579095e4fc7967794261493634ae402cce3e55a56d51ce6828927687b2dfbd5385cbe1cb52cc215d623553e68ac0098b2e12077faa737ff82b854ef1468d062302a65f49e7c34b50f544911c39ca3db7687bfb3f48a9f51c8fd1d26d44abb4c84218491f83150a507aab022f4b9447705da1c2b29fde6c7885c0fa4c84c5dd6c7ff453ca88f852d30ea96cbdc4b3726b6e683b93afadb3732ed085aa15cf42721d6f08916f551285133d2c014563845fadc66b86d072c737d04d4520fe23d34119594da97d909ca6953c0e4b99b8a05316f6e9756b1e04690a4c226b832e2cc8e7b5cdd75c68e3e05c66a1a5044359f2fc5e91e6e75a0e0dd1f87e3d7a508572a3348cf4c6b0f1c88b1e561d9f51bb68263fac56bdaede28db713695821707304b613f6e8115f1aa5fcc8480dc34fbeb775e340a152984b41b2949ec3adb84c11467e6c8e7b0bee2fdcef4f6d3c4348ab8507eefa9a9c1e6f4dd7700e7e3a94934efbcbaef5d57f119cd5cdc738ac46aa5090d76abcaf40984a424df2acc542b83d168b431b48424add0867478ef62f5a2b7cd77994158784c2387006974715d3f00ed4378371b72f87d0d9ebf3fa6cc5d502e35a7ea85f211f25940215612f5161421f534e828b6cd933048395298b37b084877273c85447ed731a834825653a58f229df6319790aa6778041ed88b769618607062fa8932380ec5eef78803460d8650e2019249cb5b6325297a016f74485500d3295727a355377b85c51d79e236e98127d114b8a6f998bd9ff43f11351f6862d1f438bbdfa47e3d62ed3b89b31961b9e00ad99ab8e5e8c5d656f3821cec7414a19cd39e49f74838027717a6c884db50e67aba37d3a41dcca94ab5bb6ebbcec72b91829169a7bee2623c594e747cf3c7f6c901aab8c5af5985340f491935b76269a83d5722aa12cc4644edbb2cb5063439b2573f263c65612e8e117fec17f124d151430c4b0dddb3290ba6c639b20af0d70b83b033a02dc8a0ca73c83936470dfa4617667b8d69aafe467973893967d4fffae8925219c4575b2309c49987a2189b5e6d2ee01018db081d31ebce5e52271b014d6b9e72919fa67b20a9259d9cd22db74d6c26f0e5d5069754d5bae44f9d86dd48f1a080dc45b2cdc1278cb2c34b3f19b4538ce341d5b4acd29b8698694c136fa832e3ea557ec139ba304aa6d35fa9d297998d7f0278764d9b8384389f2a196f67160c6e526cc1a2b8841f8771ebb460a83bafc08fc8c743c3460a89a3204351846894664f61ed73a2740a58c97f0d6e24f5442d8c699d9f4299c0b07ab06017783d8063ded9dc9b6a8e8245e5436df694ebb6be4a9c44942b454109efe4e87ff742c96b340adcc0d6a77e5d999423343fd99f431cefa34bb691d2ee50e70851545aa19667310e29f4958b4fe4c2de943b1d08eec843648ab2406dde76eadb5802b5b2d4434fb386b90fc94df816b1b1502090918f193a53feb66107db31f7bee57405f87aa11f0c940dc646cd778a69c8114eefc49009176cc47835ee98a0275c7a04da49d4a4833ce64ee21a24a285ff83ef127e30e5c34954d047c4216c19835b9527924c207061b2828cac52905c2605d51d590f13517ea65ca38ff4f757fda77ffbf9f8ba67f7ae06e2a772f6941056dd8721f8794f84a247261f014330bae724487baf509ecfd48a812af0ee6b7621e276d1e3d47c8418c9641f37ea6e14feec14fcd68ec0fe25842e95c4dd6374cc6f2be468006949508e70349941e2132681b29be27c34640cca8a2685f9ead64c5e268e85b5c55064853d399050df52b4d60145e355a9d76763278c84187360371d7b8b81e16d30e08bc128e0bec3f75594b15a334208fad929f4acbc5dafc327134508506efe2d583d6462b304d1f5201fd469fcab9a8d37dd4ee6e35191b4ffc1bb20a1d28ae287d7af9545e39d68677e13a10146989cd43c0661153e83d756452d07a34815080cf3cdee0dcc0f81fd51b6e5006862df4a8431d89f854c82570b14531f82ab3bc214a0cb1c04364b107265ded638f73ee93908bd15bae217c042bebb1fdd4c0721d3708cef8014886fe634700dc1ebd08980aa886bd38db3e8e9e7f6620554650543ddcf6ea3004cbbd7a28a3f3f5f0d2f12abd5b6179c58efe0049485917928de116d8b687649a3c0984e5c7b0a6e5107b096942efa81225155a49944d52e859584d484a0523945fdb6d0eacdf3fc8207efec6c04d298f17c42886469795bbab948d57d38b206162d04b6586d1f4547c193314ed95d3f49c61009c23ca72186e96380c9974696a89b3c304f32e2c8cb0f4fb2a9561d69d72e438d802f2ec039de5e45f416b837409ddc9581032b7c854771962964e42d4aec8d4dab8942bbc68bee390d1bffd2d7d276722531f0b35c7cc8e383da6b6d852892680dae4e4f40818c2e720aeddf7a458c1e4a3054f0df4bc70d442db1e54e2acd385c4e0703192d69ba3457b57435d4954291ed5d5187582c335edefe2be8d50443da637d94e95aa23b7a7cfc5b729b5f162271ba0bc2db0aea2815a5317f1e5b715009df5ca76bf7039e951ed298d49befce8ec61b76067af3be6ad8f0a25f00480b7aaba0fb07bdab85875b0706ffd32cec0a78f0d6c77456cbbafd8ddd9218e5e9df3ef6645beb2011f05a007710d5aeda225548e0bd3dadb5f00ac1f231857e6a545c6163ad81bde7b66ab658f046e045ff987b70202939915dcb95169970024997799408b699bf64db94145806e85324703b0bd56c1ddf24be7e657b75a06d9d2e741158ba200272f60686f1460fd2031c88ebf7d37a33cbcf915d73021e7c5d11c07ffbf8e9f50e9463db9285e97904744e49582e35e7ecbb58a18d2be2df55e760a5c4c24f01b41e8aee7361ed47ba408b0571aeba5461559642e315446927626a086444615fdbcc802a1cb5603d166b9e86d2e4c533d37afb0e0793e463fbc8066ce915cf532aab9c95e22e348b0c589767278e13d9d2a55b83a0c5fa8d290ef2657592cb8d377bbfcd8a7b8bf8e5c1587e869eb7b7e2b7744c042cb371c44c34a8ed5a61ea77f934bca6a1ca37a30795259d1189eff0c39d8f62a5504f0a32ae1845dda45d9604653c8a602f62e16dc6526e895ac59c7211b15296bf373947cd389739135ea2895a7ff334dbb11d64fa4dc2c8e809794d05bb028e4e93df4e7891200d73aaa0a96a4c933dca4b53b215168ffcb3211e7b1e8f2d04151540a78fcf07ac369d6689da31d99c6075eb8dbcf7cb5feeae2ce293d7c5039500595647324f363265d930a9019172a587c47a83e5a3ce016f757e334b539e9bb523fa481ed38e02935c8ac36875d2ef02c84f9a7debf463e3cce718efd093bb72fa449b989aa05e2409f55af8e6c46331ecc0574343aeca7feda47920182e1d0636fb3af3b26106b63eda0112caf482fd0442dc97668f9642e047634c977c061414c2f9f57223c87a91d298f4a38d0c7bbe5dc6d3dadce3cef23aebecd8dcf6dc6f852794c7ef6c71802fd81614db51ca884735875653ba60cd3924f249b68cc44900714ca421373825d7afa8eda903af5c10ebf78bb28e1131ed6d8a5088143d2f410240d5c4391425bb574d54100f01cc246c8f5eada193a402fefa77675e766d279dccb29d7bb7ac5f90c3dff958f008cb9c6650bc1cda76892a564993fff13cb6af821c02ac9339867d4b5edc5dc1b846fee4e8df4ffd72675fed3b604894593dfa472657cb49e12debb5b23fd438c98e75c3a0be8a547eea7c17cbd9fed10e701e133610696cac2e545bca0ff9ab9e56edf10a64847536df6d0953aa8d8f70e5ce1f40d7b878edca7673870d648ed683f2cf4af0eae88618e9cf0ad8fe14b46555ad781d292a5731d800b8c8d8708f6c17b39b0a06e01c9dd48756a6fc9bb07d4a722d8567e1e3c03d48eec858932ccba8d863ace37ad63134461e49a42d65ef9bf94608adf5bd628b7f67868867e9fff3c13d02de840c8c926aa90f3daf33052fce7a1801fa86b6559de6f6aa0b33ddae36bc71a671b7b75ab747901b19d4fc118d320c3d6fc8948b393d7e64839c655abd6bc1fe2c694fbcf8c617ce370724bfe2435d0ad1893d3244e6e0664a36349762f157c84db0c7f1186a4486ed1a2859d640c0c3190833ad73c155678599de49042439cc94a271b94a43d92f46d5eea4f8c283506d28d4a83935e1d9b5d2e683d75569691a53bbe24d15a4394efa3669878f5bdd1aca182cbf35bd68031b2f61573b6f7a1541d782e04d1a4d209c77fe0edddc0e8fddc5342776a4ad975efe666a5e82e3e00e8bcddad84faaf18a2c360aa9f5c71d04307775db3f69337701bfeed22005a2eb94416346d204c90063ede628722b34eaa446b7d8586a7d310f4c77c5026a528e16258614ef741ed6e217a200b94a9e5809a43b443d28604362a5dcf5e987e9a2a8e2720add9d5f4677c21c4150abd97e0328463265e968a19db1553157832b976c5c5fd05a52d15905e65ea926470be4af12d59dd09ba95caafe61565e9dd02532000651b15bef051e4525c6d0429b3aba601782f5f43c04a2f8f9ee988a35a31ebef7ab45bff0c501261cf2bae541c78ff49178df6aafbb4dc0ddbf23751cd18fec069a95c3c985496b9cfd0a7a5f259598e68c7fd94110e42fd1183a7e050f30bd83503f4189324256b215e457912ab108809544e3cd5883eef74d4c7af046e8696a0af5a05d27eb0c9289804ef2f4c7e867a70ea44653c542031638c636b8b71b30646346254db3092405a61976cb8afcc1311066cc5d2356e1bc9d6fc9bec8810148a13ba1ff1c5becc93e3621febe8669b8114191dbd66d99928e2e7ee4bbd740d82e51a3744954b341dc563b5d5327ca6d2db0c0cd28261bc94102daefb7448f68e98260c8ccb4a1bd8fbedd1044bbac856fceec8c6aa8a3e44d5c190e6f58b7c099701468e14fa5f83a3c5a6afac714df8c699614a81a39071027ca441dccea434cce5799286dde7eadb4967d0d4d594475bd365042d4568e65666b2d5760ee03dcb87627752c5107b198218c853e876a399f24a71cdfa515d335ec43a3a797f244acd8c7329415cc693666d3d01623ab998848d2ae3c3a5aaff62992c0c2bc88b7a812b5062b24dabb898e1bb84b1c059de9c26ffa31d8fa2b553c45ccf636369bd4a98739e309b33b2f746e685cbe569d27cabc7552cfb6a0ef80f18106c963cbfff47b36ddaa1d1f83d561acc32314b6dfb57acdd1281ba55c912369a1ae87f65aef7c08cee23062bc93bab00a039f6c0dcad031aedb43b616850c6aa3c293d12ef03c54cd474e5e6940e10ad555d6bdb72594bc1408f81f8850baca7381575c61d7372e16c436a54f5e63975baee1024c6e5d805b894cc66f83349fbffeefd17afca61415805a0f9d631801ea0fe655b35d699744791039b6be3375e3b1b01d4e43c90412d53e879ed7fdd71e36657a85deba2cd97e704c8b235d04eb1d820f65d5d70bb935c1165a0e00f8a0bcd95ef951ad8cdea9c4ecb3daa72ff558758d0331bc871412010118980402e1368b76c4a7e6793648732892b23846d66e755bd2585c0fc4f3745c952a1a090214d4cbe8bc270947d260d55dbc67491509982fb19d976b00299a47007f2e04f6b4203fab759fec7e014c43543d7c4782881b22821fed284227a55922e73dbfd8103fa72f2a82d01a4b46c15b189d16c5f87fbea1d09d6960f5b7ba096ba2039abb9b62990c28a9bf7b3ee35e7e95a015f18f51a0aba2061caf2d33a84f25a57fe554d63ed022caf87f6862b811ef4f39b134a71991b06e6e2d9fc6ee7f0b20c2d1b0c3a64af58815cd6166380e46c8ea59aa7c614c1bc9019103b06a10246f9f82fe9b908cdb6b4604455dad52a42f2f382480b2e0e4a520683f80aa2bd27abb994ab742117393af15a22ac92386a0f0a54bb2a0f6b80050c049706563f1a7912c8a532afab65b849c03119b6cd036b0162515a2f01904d205a301e5835cc50da15ccfa319b25134b3b73232196944d91af3fd1febc4815810645934eb97bc452b4111b746a8468a9f3392a94f7e2bae5dc24a448f88648067d88a00da03a60b5e1eab8316913d0d60c61c09280d242380c74b124ee17a31a50048d913855b50b2c05030a7d027c5c505815a70d1923b63b94f45053a5a645554f76e7292429c929b6de44bd60112d7a7d985c7ee2f19e419cf716c0fd4911ba3524321f41e80fd30f51a4d54a431cd44e9d8602856c660abd9970159fe4a83d42ea26092112d9dd9b5b06a207a007f507f0e5c60e37f91793f8571f62475c0f703beee85a7fd2fc9c71d2dea60f27edd81ee7c0319bd532971166d403fd0365fc10762ef45da16fa81473b0bebf377d6f6601773fe2afe129f56e5e52f90c43a16b0dafe16e7252fd9824e6c852453d94dcee6aa63b2859a250c011fd10688a044a6b332adef4e1239ae5f9c33f3a67fca37ea2f1488ecda19d4a7dec3fbd6990f6acbdd945dbaff5ad575b289e938826ad7d342821fc816491d74b90ca77e9ee067a69284b31898cf69f3db47f7dd91f2ca8247a732c5ee3237720fe394a045c9c37b254a30a457c04758d2a18f10fde38096afaaf6d68be263d5914b5f907e1c71825f48070b910fa4f17f831efe0edc079e735f2ce2ce0e4cffb8306c245fbac712fc775ddffcd8da9a77f326f205c668cf1faa761cbf4dc70f29db4837f13c78c192e2edff7df751e1f70b7042cec755fa91f52ddbf54e8ab859701aec56b2dbc1988e84b241289442227c5cd7320d96ffa4add9b02725318f69e7220f40958aa008542d35a84a615094d5f8abc0ac892fb2a4577c041ffa68ce9eb5f4e24fa4d93885ef4bbf6a01643929f3ef14f1db5291445ec8a048de877fd914522d18b44a2dfa12f2442b8ecebef696a89a60f4d73cefe83fb344dd3344d93c7e3b958f6fae100088024db02c402ba20d0dbcbd20266dec438ae7ba5302458ea75b529b3cdd3344d53cad71ed5473555191c3830dd59b132759aa649969cc64e93e370e2c395782d3cee7571ec107a9aa49aaacc0bc77d8d03e21f7dea294f0b38d1abf894dfec975868504e4a49f57012fea1e9ac99d1d4872d99a6699a32f3b472efd778dd8b63079dd64d4d0b941b9d86c5d33e2bf0268563072034ae2159b1ef72c247297ec3971095e741fca39e4a398dcd12c934999c745b5976f2264f5b18d6ba99be569ebee04bac3fbaae7b08bb5f9932f3f49a26159ff27b9aa0c048763c9e979330921dcfa6c2db3cf5b2d88bbe3648ffc062dddc7befbdd7b258f8e887670778092080a10810a26801218a22294cb0681e2b0f20b6c1e2b510cb52962c7dddc7595558a410c9e477d5d0a3214a8a8ef01ff4c2fb18bc084725294c5022b08d90c953d1d395a72efa42a239dd89d64dfec9f847a79bfca3f6ed9c2727ef26977112e863fe4d594680a4d0d3a74980249317fda64cd0f4377582a6bf691448de6f4a054dafa0a90bef7570f19a064d4490e572060d78035752860bd6a39b2e59d6b9b4fdd06bdb46ad3411adbcd79a3af61699ac6964e6cc23b315ffae706e309ed686ec87f21051dbce49db7ee73287987274e84dbe5a69452bef4d171e84386cea637a4abc7817dee97578f15a0a135c8c27a19944c73731e9f8226fc53b7dd89c277ff2a078126dcc97278a7e88a943cfe3a3435947e84df210518bf29e6f77ccece25f68c392e0a807071c1c625a6938c474a335bcc539e3fca967fc156f47bd322dfcfb0fe17f9647fcc39c07ba252a1e0e31ed547984d29b04c497a620efe36bc5835a8507f22fff53b92347f44d9a3e28ef7b92bdfcbd3ce2a47b13f222fcec9b8856bc0d7ef1dff340f08befc2ebe028be16bd1d23ac56e7ea1a89a6af848bafe12d972d8eebba7ff71c312f14d1d87f67f2f339a73f45bfe223b6c1e2bf08539fbe0cf0253efd192069e5e9d5289c4679085f50507ec593f09b3166cb5ed1f35f787abe0b6f4e91f7228889b79d20c4715da76fbc9be4b6012eb244de167dfdb8f22f44b7f8d79a872241b6e812e816e40eb5827c5128481765d12350962c41085930caf9ff1ab703fab7a25365abb592f7d5ef84c949adb5aeac7cf5bc139fd1533c3dd2d261491cffe8af7872e51ff5e6dd513f88a8f33f188ee8f976825eb888610a50c790a515e0883e9561a079d18519ed226fe1245b31069552802df4f76cb13cbd1ccbefee750bd9e2597ecbd60a28245bb22575a44b6f7676fecdb7d6e69f595433c5a137324ef29ebaf8ee296be6dcfcd777e1491c1fd167f124ab85278d986c9d9b5ee4326e92a5f89223aa9d189133ffd4396f0ce895973731a555fc862bf7e11ff51e2f5972d3e7d70dab57b22ce21f95ad1b39a26f6202fa93a737a1a7a11bbde255bc1db58acd94e51ffd5ff1251b65511645e224489338297ecae6744aca4398c2c2933afe55cc92a56fb25664d9f28f6ecadab2c5841c8f2f238b1aa139d87dd16b150fea142f88a8c2a3df797b4be1ed0ec50bc286286b83f4a6acd6a4ffc95277bf4f77b1373910c4bc696dde69acc0060bcb075be2c7bf0ca5c77884ec049c7ba4c1c81770bca09ad6be36dda54b2dcea7f4a5379ffe4ba867b651ee204201042fd8e0d0e69c73c6a9899031e2408b7287061d7a716e161e58fc8b80a67de2a6210c096fcc48cb1934ba47066c6182e381868637b27b62cc33600b7c19737aa064a2031e201ecffb02396ae0082734048286438e4869cd0384dbd2e63a122fdd9964269191e9d1c3878ff9d232e0a4cd4d82cb7e642859aabfb1644a8e8ae81d7332eb1af1526673594e9af4b26c6c64102944dadc4c1a8842c9987369fe2f62764a8aa25b12353fb86813c44b74464a1b908da63514551373bc85658a922aff1c25835094b4a1bea54d0abe44ed9bc640a3288ad6d0149cd694a37962c06176be67e8483ce1c8f671fbb87dacaf4181f3af5ebdfa57bc65f4abcaab78ce712bae75ab358b1e967df5ea14fb107f7377edf7fc704cfc2dab68af654dcb30fcb3011a3f0c2fd9540f4ec21e3f0ef8321fbff81d83c6cf75f9b7d4f85d40188bc3025fbc64f14f27718f3f4218ab1363d0c7f85f601bf846cf932cc5162717a79a08a30121d5a464493bcd542a851361b4140e382b59ca5cae956be55a69fa5327c26832325cb294ad56d3b59aaed574c944984ce70699932c65a9d429754a9d6a224c8663438dce4ea7542a851361b29a1a7056b284b95c2bd7cab5d2f4a94e84c9645cf2768ddd2b1361309d18322759c252a953ea943ad544180c8786bc5d63a754aae24418aca6256fd7d7e55ab956ae95a65f75220c261331185547d3bb9223faabea928930576706197d53a953ea943ad544988b23438dbea7532a657122ccad892f7034b52e39a2bfb2ab95a6cfe269417422cc9589315c5326e7ed7a5b1dfd2f5dd235ca5346d397df2786a802f222bbc82c2d725465950644d377b1e261714a69294d9fc5c3efd264565c2c5c2e4d46d36fa1b2a5ac74545629ab958ea69f5768a22c85a392a552b25496f2526428d929ab49919d50b213ce4e598da6ff2ab0ce95c9a470a1b85c998ca6bf7272ed4ae764855776a5a3e9b308591096c239c152184bd99cc2d1f457d42ab1530d76c24ed8a946d31799d092ebc2643496cb755d988ca6afc20437f5963977a56357ab958eca8d3888a641fc4be1582ea7705226c529ffa8eb7baae9eee99e34cd35ae350fa7cda8f08d7f58972c69a72b1361b255c4f2ee3a2ebb34bd321affe03c2d48b68a305a0d600cfa5463759acd0d600bcdf00dd79fec0902376509cfc8117d97760d739ca4e51127456ec649d8977278faf80746e11a277d9b3cc9117d2953a7936bcd460ba2a94eb2a415d1543b6938da49b3812d547b82c06d992b73654e64afa8a2d9e3190f801c6d3225676449e3b8997b39aeeb3ea5e96b1f4fc2cfdfe3591844d4425c1d84109c8ee1afd9c8920647aef5e0445fe84d3c9302eee21927e11c9e2a9143c43eb419361a239e99614a8f3350cf965562a6de7eea2d6f0cf88e7ff45f3c4fe2a32cab30fa39785148c489ab22fed18851b204219e91a5a852fda76596323fb4205e922939da341b4d5fa6f08cb4913251456bc678467b5e323c9e80dc0c29c2a5efdf2ccb340df34f520aea6a3ce59f949f43c06db7c99ee29032c33cbcbef753340ddbb0d98518923008e7dbaf1afd0c9b221fe8672a18fd2cab605ae61175dd214b25b6bf9e12a7d761fb69134495fdcd6e7631a791239b204e925a0d305516b5631a9625c03ff9d1937fb1ecca78b725fcfe40d43df6147b8ad5a716c78c3485565b0ebe601b16621bf735778f47053095faf34531f4b15cbf661e515b9b77b8a99a61b2d60cc70ffe8980db8ed272bfa3e00b0be08badd63ec57af86173cc836b3f79366d9371f3e9d944689bdd07f48fc2f8eefbefbafbf6b1c770807210f3079c7f741bcc46abafd55a6bad15be7f0c0d92b85b6bad31beb5b6fed5628c31d6df3fe84dafbfcfaf3b689deed98f3bfe9d975a69ddda785f7a7346992d40848ed807ec77dcb1636687d683eeeda8015bea7b6e38fa3731b2ce7071f9beffaee36af8573f9ab215e53eaf510cc3ae7f9f7bf7e5bc1ff4c63c9e1a4ec2ee6f9ba7c334b0d270081a0e2962476b3177b2747d84fda7b1e7f27e8c7af2be794297a7e4e9ce0e7d0f7dcfe73d1f0de4684e5327a940bdb1c6b0df1bf47c3e9f1abe6d52d36d6e2234910a9410086452528263ee53cf85a1e2f3dce7e9f5783c3e9cf69468fef13c0f718ce73f9e0f473f9e946d3a89528ee33e1cfdd08fd4d0e1cd0e262f1f47c9c9cbd7265173d807fa25254f6fc96b13afc483737c9e3ec63137d3ac795bdcb60d031ed4ae37065cabc448bd33a0413a6a4d7a8452eae9692cb608b78bee39cc47f2b18718864dec334dd3b4dfefd21bde783cefc1363c3db4583aeef22b785a4ea259e5f3d507cf539a553e2d27a9d0af2f7fc3319eff64159a69defc8b7488a8e3bb1127edfbdccb1df773c4cfe7358663ee538fd6618dbd0cad70c162c31e7bede3b9f6783bea1c1e4d633e9a96bcd49a6f97db600bf6d80738faf63d38c7a70ed980110db1a0e1100db8b4c662c366b81df5ca868180b3bfb9f7bc1051eff0bc7dcdf3083d8f7a3c25796340df4c3db79582fcf3bca67984daf47c3c4fae2157e8dd4597dedd0f7adf6ceb6ba5b634bdf01573ecdb5bc23d674dea6b35f427a13f09ad527473be9d6fef7f3e7ff2f993d756b693b7276f43a14701813e743fc6df5e26474323683864033a5a1b657c43934186172c2d5a781ee4fdb3c068d9227ca9978b9847d49f508a0ee5c453f1ddc9778fe26d15b9cb338517b5877d38f9108435dcfb67b162450a67ed6336140aa1a8c03a428fd2e11b36c724336c7efaaee3b85abfab5f2beb06b5e70c13e00b8ff8cae958d588937252e01cf3adc7137acf495d3969c7c97fbe7ebdd1423847bcfff90f08c79cfcc7a2e4efaea26409ea70395409bee15a84f220949f344eda61ff04be745f7f8600bea84891f22a547c0a6fa7a8c89d177509f6c1fe8907f5b62538c6fe498ab729f21053a33c8f8f46f97df5893e7994141f77626c11beb86cf103dc47638f8980c31e3b717b9af4c96b3560479864541ae553bce318944f918788fa85881a25bb29044328281ffafa9ed0631c8312925be7419dc2831ac5db273f5bbc631e51a37cc843c97a839e871df3e45fad0f8a3ba71d9271d28ed083be3e06fd866342a01bb618b23475a0960e39c2c42980c21c0a3d15f950b3d08f165ee0024a29a5b4568a824929a593ce39e79c1306dc9c73ce39299d343f341cc2011f34bede775e0ebc341cc20197761c77a6a3898e29fe42a9530faee843192c3f23010a802fb03e952828028bc562b1582c168bc562b1582c168bc562b1582c168bc562b1582c5611e29513c34a6284a3d7131a9c1f5946b4341c62448ed67ceb8e9036cc061261ae991cb47f210cf0a31b80a31da249e828710f48765efe15d12050de33a5a78cde3131c95bb6f4ca0aad1c9610cd71794b1a8d04b3b6e06ae5783c793b4ba798b851d900d1db96b79f744e175eae968eceb2f843db5618765eae7b554b60ad6e2a34a2298d8b7691db2bd3f2b7a6e1832685b931067c899308fc8a00bf389100472d2ecc26789d52433c6fa60a606906027ecf171ce0f79c81017e4f1cfce026ef2fa180a506e8f83d8b28c0ef89840fbf67087af89324c0523ce5f82d9d4080df920a31bf651670385703588a363cfc963ab8f15bfec0c66f29841dbce4fda5076029e60ce0b724810ebfa50af46ff9821aeeb9022cb94900bffd8900fc762800f05b1641c33f4bc092a7607efb105e7e7b1272f8ed4e98e1ef38ce02587256e9b7c780f4db6b80c36fd7810cc751c092efdcf0db8db0e1b77ba086df4e0217cfde3f320196244a8ee4c7f81da340c3ef788596dff1090dc3b5ef773432fa1d8720c3efb8c40cbf630fb47fc4012cc9951c4996dfd126c6404716c4f03b9e40fbc710c0927cdd77acc5efd8231aa11d4601b9a0fd27004b93468ea4e83774adf80da30099b0e21f035fe08b7d873800d08e83f6160dfafd2ab4976877014b932542b7e133e44540def700d900336660373af2be05c89bf321f790f70a469323ef4b80bcb9981346a3e5e3c87be5be78c8fbdec89bb361f3d2f277b091a5bb1a40de5787bc391d73e4d7c87b455e9400f2be01c89b0380e9a2b47c1a79af48bb0393f77dc99bcb21cfc87b45beb36e29efcb915838b064ecc8924ddd90f7b5216fae06df91efe2a818795f1af2e65a50d6a4e5c358c952cd912f59aa3672247f86bcaf0c797323b9aa395afe275731e47d5fe4cdb9902f96bc57ea89b6c87ba5cb79779c9737f7349745dedc8a7923ca7b45fe745195bc57ba94bc3b8eebe6bd1c27e9cca6332b7a6326317cc7cd1ff3076c893750c861ad7676608be7dac473ad71184d0e6c5218ca8811d8125f1808b01366dab1b1812dd135b7b2422b87257bc81e17e503954dcae572ad6d3bd70477e69eae090787b505572bc7518e822d71f35c6b598a891b954d92249a35d915581a3b634f39399eeb2ca70b2f572b4810d8625384d58d2acec419d8125fd77aaa4a5454a5a9334e00615b61d879b970c016d7da542d81b5bad92c9a05b6c4c75cb0455e213bd1202b220585964f68f91e2250cff5f45c632e1a17a7a4e16f2091b3681830d84d8c2165c0973ddd5d47fe74c73a668e1e87c0f9bb3097bc912c93c676b474ec157f634070c14cc0d14d067cf107808670090de1c641c3190eb30c1d1f3301173f33405c40ab0921142952a4489122458a142952a489269a68a2092244881021428408112244881069a289269a6862c7a5b99674f90f3d41e0a07f97f5837c172967768fbefaccd7bacf39e8fef37632e8f95bc27c31c6744fc28ffe95250b3ffa2b3385fc68fae9392489978eeff74a25b8f8726f3a460f0d38951b54c33da38c8ef97910a16f4418b1473c7996e0e4d38faff9d2f35d62b7d886cb1beeee237869fa8179f80d7012f71ceb725cd7fd579233349ce4912e27adf66299b661cf872b99375a3b01855052742a5254442b58acbcf7448b0dd3c48c34f6e11f7d962db26c326593b249c9d4a745c1eda88a425c7bb1c17831478ee8735e6c4521f146a5b28982dbdae9e4a41e5e8279aa822f9a8d66536bad2fbe5619eae86badb57e5feb8cfa5f6bad75e56b2dd5fcb5d65abdaf9554455f6bad55e56bc5a1b2f85a6bad2bbe5619f5dd556bad29bed61b6acad75a6b55f1b5da50415f6badf5e46bada1a27cadb5d6d0d7ea524dbed65aebc75a63d4f8b5d61a7dd0506badb5b6d43abf967ce5bed65a2b8c5a6badb5d63a43adb5b25496fab3d607611bb5858b1866944838c8b8c1861a5c62d0d0026386172c2f5864088dbe185eb8606971f22bff26d973bd6bc55522da54563a2bd14a65c5b158812352f97429de93a2a24b8141ab93ba5aad36141b4ae1804e3493558661595c71d4f5369b379adeac74b25c82711c37ab76dd4eafdcb59e5aed745c5d7b3c389e952cbd3cfd8a7f601f9806c6932739a236f045f250e325292710398308d198e5f14f1540982933fa3d57a0f1cf1ff0e57bfcd3e4a5793a9de6cc69d22471520c5040985943fa2dbfb0035f568e7869a6523a29275248729cc422091066e2e0f05b2ec1087cf1545e9aabd5cd6ab5922c8d5f0671d23f7e590308337564fc9637d0f8a50d7c5179fcd28797a64bfea8c12ff14b0f40182a73c36f09028d5ff6802f2b5e5ea2a7d3ce499a4ef2e4849344578030b4c686df8e058ddf5df025c5639a4a19491d49e9ac9c84b20484a13835fc762670e08b8a9497e86a65b352e1eef13b0b200cd571f9ed2ed0f8e4f1bb8c97e8eda1b1cb7dfcd0380a08536562fc8e6148025f422d2fd5d3c9757a9d768e3809c40408536b68f81d9ba0f1c71cf8521fd7540a2755047f54451c40988a1363d096df31071a7f0c025f4c1e7fa4f1525dad226a95c21f61fc8e23d0f86308204cd571c9114d32c36fb8058d7f0210c6cac419f8121f7f059c943d7e20e0cb7d17d7fac14bf624475486df9088c61f03616c4d8c413f95a22123fb41ff9b43c9a1bce5ed493a93f6fb66db63f8797c347edf2e77efbd17b66e77effd19feb9fcd77de95eee5e7db7873aee762fb7e9cf86f16f99468b75dc0c01eda7263dd9c31301b7553e1b738e4fcb1c8dc49c167693e6a7ed48684d47f67e822f3047b6bd63188661d826a15ca1a6f6f83f2548f4d0f8fd9444cba0e190134bfb4bfc9e3ae74469ec35ecb1df66f47df9f2b3aff7fdbedff70b458f157cc3f38e2d4b0002193b82b332b0bf1290e1237f1ada1f853a128386ad7a12c7ed4b1b9554018c619f7ab2c69328ff2c6cb18f7992c6731d77ad405b277cf440d292129398942ba9310cfb0861e42ac6b0db639ee7f1d19ef7d7362dae300d0d770e1a638c317e8c31c61f8c57f86938497b0ca3f1e700f1cf802fdb638c31c6f831fe64ac037f30fee00ff7f6f3bbe43f1f6f05c794e421a2e65a7215b9eb1f77777777777717611beef156601df8be0abe71dff31a0d9807b574777793bfcb1558874b77d8d2a961ab618b36a2a4f7f7fc9b1b20c231330f1135cd1280804bfa14a2a06d986b366cd3755a2db99a326eb5b7dd0fcec7eca1edcb2892c097ed4b9115066dddbe9ce874387944dbc87292f5c77f7f7bfeda8f46ecabe0184fdefef8c68efb9e77097842448d3304b023360e3454ee63af84c9db87809354eefd9b630d4a021e8093b7bf7932604b44c9d2499e3eca2a5410331a41a2edfb4324fe59087364dd80619808c3b00c53f1d88dcede5f236dd193ab4ea4a262a5cc9f8275cc77a902e7982e232bb2228be5268eeba49b222bb2dc84656dc696036a895cf6378c1d6d7fcb70b92c3931c730ec66d97b1e22dec0b2eebf2963cbf1cfbe0c1c33d29e1a709a8cad7af1e4d1e44d0f02fed9a79e041e005bec3b00c6b0b19325207c643fa7e5adae9b71e68426fab811064e7745980538697acb5bdef296b76ee8de96564f36bab4653a484fea979b65d80cbd73909fcb7fdd4b96b672a50424ed0ff4fb438f92e2e5e70061a096305a7e0a4fae7c641fc5830ef2a08fa09b78d0471c16c2f2b6c2d8cbdfdbcbdf5316715236c1315b1e226a9cf783b00eecb53c44f4967f767b8b8571e0e6ed9f650cc38698fa3e8f8fbe5f5fbb99123a7fc45134bc14519fcb7f9db5f6a5113a48993dd42173c663137183cb3264c101847b4ce228ef616344ee78bdde0c8775481a54444554444594fc5d1fea191c53676cd2fef7df751cb7f24881dbd15b517b2dda879b07ebb8efa8581353f0056afb18e7b8ef7b077e7f6fc97b39aeebde7a2bfaf08f6693abd5ca7fcb712646f3224df4e15171db4dda7e03608b852fbed1b08ff32a62a3e908843f033302c7496511d8625f7a721505ae7eb4ef5168c197fbf655a828299110b678de40b064eed73fab651d5adeb63f8763b2f71ddcb643cb91c63f1b7d60477071e6da8f3ea211ffec663fd23829b21a204b337ce47f73575c4e0bb622cbdf3a8813752c6f5f93b16526de0cae8b2cb98e8fec5b6bad4da2ad95d1d65aff9a8398f65d078b9199cb9b7ff661743921f5d869a3390f1b244104468cbcc0c87c2925037ef430e96cf3d080db1c948153081fcd87f8c605e29f66ab6709dc7695c6f1f9d8d327653f75ba2352753bbd8005d8f78fda6fa922ddc097f8f3b5f8031d7d90bfe5f85ce524958a63e46fb5e6cd135d5872151ccdbfd1f337367f97384beff0f7dfaef254761b0d56ff1aee790b01b765cb7774bcf4711269cf1b5d6f56264fe2244d06be6c46c017ec79ec6d0fdebca8339c496923814895101fa9ac7cc5fc7ab9112fe2381ee0b654e9d7cf974234e8c99744e22357b9e611c3a095f8a864e993e5bd5276f2ff83db07b82d5f42442d7fc8d64bcf49035fa0f69d2d91e0b6a49134aeca23e0e010234eaeda99d225f423ee2a7795bbea2666a4839812897f734f1f7abec3e9c3bf295fb0653e12dc0ef94248959e53b6e468b6769cb443aa84481cf8b244f5f6967794291ffca3f697291ce312887ff365969e2370db537afe7c9972928aabfc03e22415cf39f27c69e324f9f59fa51371a04d6f29c4a7ceb37422776d6e0ec45336dc7e0da7f19793e63b122715893498abfc9b9f792ec41431117012899376265ff2896c27f38a7d70b7eff107f40ff354b43c3dd2689f5147254bf2f59dab5ef2a38a9c9bc7dd6f6e807fd7bf19c4d4918607aa39ffa80eee9bf3c19d477dce4309c2d91d3c76c8ceff6ea89526747fb93cfd3b82d394735d9fc6c395ae73e2f07ac041bf86d37c834a1c4e82af02de3a82d283539b48f49c73c239e79c73d621477c7d1c4efa1e3a792fc75508b11470f3a585ac283a904416727c4c8c05aec35e829c8459f8735eecb518edfd9b81d07173d2023474ad416fbb6c17191d4a59e51034c8726784516f38347cbb353d3d4f995d622210333c0accd03bb4c7af45896346c6f907fa93161ddfa484fbcf4358c27014dff320ccbb776e2f3ded4fbc1d4389b73b4f0e22e6ce1301314343fa2981db3027ca382901fed1efbfebe05f15983fffe8bb58b1433a4a158c417fb7502ccbbe7781c83ffa54251fca972a19efcd892af28398580f4464594562a1a77ef21384fb5a1b2efed924afc989a3ed4f6c63de649b866d788070d65a6be5f45865ad1efbdffc0be244dd7eb751f38da8bb9bd9a804ccd70b9b9eaa9a110000009315000030140c074462b168988599a8e70714800c8198486e5a1bca932cc8910c52c6104500210000000000002032533500819a244309f0b0544a8228b654742aa02111537474b1f8cc7d87a84e3f0b706ccc1b0ec8bac01862ec4bfff4b149263911ab7ff72b095e2e26069fb6246d7e3615134e2563e6b5929604659317243dee2c2e70eef5dd0ac7cb84340287a63b95e2a1a69d15107e28380e926a0e910ca11a98142789cd5129cdda890318f638102f08e1b360ea2eb5bdbc5e8d7ac0c362282b796f07dceea2d9929983ea304a7489b13d24af6ff352b0a749d31e80208f94777f58aef39dab5e13a5814d9b742de55126bb9d4be05d232439a4105c1f83c9300150c118613dab975dbbf1642352489b48e02fbae7cbe7132d1e575db8a266fef2c1d0e4000d697134618c79bb871b22a194563f3a71faf51e8436070c82026030a57e7c510600d2a24276b340b241eeef29a5d314db36ad5d204c377b1e0ea011b65d14ed90df295ed3291bc72f2be5c064841729b8f78575252132c59a2fe42894cfddca1f9f2c4a0eb1dbdeb41c9e61413c1a521bf2ed68a8ed0a3478cf6832b99a1beaf10826fcbf986af4377555335e70a67978cba2474ed0c09e05125180040622edb80781d833373a2feb62795183f75f4c6d6613e4f754ed1a2e69aa7548b52e6e74fe28783bb410f7efb0fe8f3d3bf3e5097d195de649350e5b7a9f896f6e56ffabccf2dd5ab8df42710123f54671292e76f4e5f9e2bd8b70bfb4183c6a15afc12cb60b938faca6de3a23bab5579f985a805aa1864db5fc9458b20989b9acc1013da97883f0404842c4341e45dae4cee3068bba8b63818b2a7a95166cad62fa632e2de189ad9fb084ff7fc4fa53c3e0373baba6f73148d3cdfc554292d5c86943e8992de446170f0797afa5161f668a8a57cf032d5e6b24aeb6c8711a8cf98e0fcdf154a432283b3d9f15d85b46016215238c562a76ad46145d5fd44f2a54b487ae8bb57051b960d90a8ebe7b223a3956eca62195c276ddc1a072a8536d97b09394a10ddfd28aa4e07a145f9f2e8febda44b1c6f62773756faac7155afdbc67748bd6ff43449eb927e024ba24fcf9445f1de672deff6168493019659f924de818041434a8f1648258841d7fec148adff8749f97fbe7717533a1c6bbfe25aedb89ce5d29335b896552113e191bbf3bed247bc5a5d6f317f0a269fe523dececbdab59cf735abbf51ae49e00274de880f71637501ea5faf696975598a1d264d7a5420255852f36b810e0b17deeb8814d8865850508e993bc6ccd0fc89b10686ee9af20de60164d5bf7a233cc1e48f5352d28b5d2cfe7774b08d2cf3d69791d2c4c71b442822e180b0b5a069341590593202a459315aa9968499c961d3dc17491131ad63ce6e39eefeaba6fe7517d5db7d3524fd7ace008f6a8da40ea9af23bec791938b3a076d2b0d629e413569843e2bb8c99c54732e26ae61b34d0782ed03f2218977331d199c2e7e449d8ea77730f9efacfdd91881bd1170d70e4d542b7a213b975036e4e0a6d748efacc850a430b85443f761e1e15876ef8fa0dbf60ab544b951b3bc692d503caf38fe2aa9a3059ae1cf0c9ddf485fcaa1ac78dfa7e5ad2ac920ebdc85165f456556c66d08ac494a7ea53e38d111bf9b9c5a934055e6cbc8d5d5561fc44253330d288ba659efb1a233199520ac2f2142f1851c3b21ee04f48c3d56982f37efdfe96fff2217d2cad37e5287bcb3aab7081dd3eae45e6e5ba49fb02fc873d8bb21d90f070746efcc0ec63cb3cc967f8b7c032af7c198ecec73413cba1a31d29531db804046969e14aedc460faf7ac8e91c3aefa465a4f221660cef8e58b7d4481b8ea6e0429c31784b6e5da6d830072b0e316606b3bbd5108c0f285dbce02204b56001eff7b8637eda032a28972151d529170f87f2b4256565d5a3e2cc7ed4248f27f705d5ffc858ea7494a57f367d604cf5f32adca17f3c9bff0629063de91d4b8ab542c841bd0fdceb9c06a65d10e59cb255d80322e17c4200092847c81a2e0b302e472fff917a541030d0cb806b85d370f547c5ee1dfa2dfc344c02b70e4ed068d127d74f59098f3bf98d1d240fdf43e92d6f73416916e2de45eb1b1a7836f02a9bad7a46ed12a189a5b781e79d43f5dff95fa3bac4420fd5461c23e2865af7d609a331b537481ba416c7ad7276f9452b5a970503c7f024d2668a60340268962daa07a456f489666c1f665ba74858fc82c44806d66aa8fb40108faeec422034267573c720f9f52c78004d35c99be3e1f6bc814581122488c96d7b9f03f8785525327f7c0f01cdb58715b970484fa3591a35cb3094d6f18bc4548d1d065490d93b12f920acee93d91d78b864fd25673ccb245b9d2a88ce5e6af30decdccb3a7013031576f017e0ed583905cfa143a5e14f6cf470b0142a59810f5359ae9601834ba0b57d0d92711cf983c8c9ed05b9665a6254201b9ac1279874c174702313c23f59e14cfb116a472d22df611b53e76868e54aff6c05eb4c5e7357f4af0eaffee8fb3c887274d9513341238180bfcc949d3695ceaafa38bb6c913586e783df4b49a9a5204af522428ef95a766b8e4bc1f37fb575ce2fa8bac6e35277782d2937e3876e0bb929e1910f3ee7ad7a3d079a2013be0956616fd131aa17a312fec92dbb3177df88a0c4698a7454688ecd8ab13355381924a454a07561d959bf88c1c6a156b2ad4ff617dab32142af4520e451016e0ef28a3e7d6997570343a4f398a9d4c626b45286e1005a089cbefac12bedafaebc0c5bd008abc118efca299d2085c9b61919acb8d811623d8f1d0cff3b0cee5e9bfca2c8c5008b048aace79ae515bb047d76456f8792d7a5af57bfd9371262e0ebe6e0f127e3d2f09856546063dfedffb5151cdb33c5b5f9dc0268f4101722fc9dfa5a4c720a38410a60dc94db6685eee8b48d905de8052dd38afe8c536ab028b76963ae8cd082da9f405a22add4efc5f04c010c56c35863a56a75ae7794b6c0c215b4008836ca7649540c0ea3bf8e815635bfc8f9fb3127155e1ae6ab8f55709fdc65cf264876989e9b1084abc8ad067321afa4b9d924094d0d7f2d7a89ef49c09b8cf3a41db76514b0158ef9a915000f380a0fe6bc28a0cce7a2d71ad60ed7697114cf25292307eeefc88f19c49ba2238dede05e204f3e58ce8719dcb90278a3fe64820889bd0fdfb6fcb4e894311525b6ee01282e1cd8d05f7800b7453281b28b9fc0b04830c99fd560539d866429aa56056485e5eea23f31b477aa42efbd2ff0f43c6812975e09821e7edafb01d40504e378cccfca62502d6ac415cee73c2fb265a3c43cae973e5b6b402ca61d94d89535caba5c235885295545ecc685a0cfedbef5bffeec15ab205f7d00be97ff454b7875071d6af013aa8efa14c33da187e4b12441272cc1484e6f6923a0fadaab878bb1c182e8448700eb2acb301681bde998aab4e8233786695793dc4ac3bdfe73936de3f58f44b004a3392b15d550abb32a2dc98d1f82e5e7272e8d405c93c0f2efc2987822254d2022f6f5b692d2d7652edeb68cf4aaf117813706e29204053a476e518673827e13d20f59c4fb38e353a93878ff2e47e89f1798ee89d6fe612d1f9d0b8eea89cf3467594a7245b09ac7b69b1baecc05bf017cf5485f7308d2185b8de092a8ca5206677f70721b6f82a48ca238b6b95e21fb815a9b86852f0edbb0dffc2c8ce67325264771520445989fb5599ad1182187781aca19b3deed910c4f6ad607e56342f92326d684a349f626337713f1db79212ddc9b25705cb8348cf3d7a42e9c58ce55b0afc17ebf15e6dbfcbd8e0a5b37157572f39473bf7f1878114110bd029b7f7ade49955f09a1c86702ec1601439262bcad592bf6b50c9f081909510006ee4de0a0b52f81206d18e359757acb1205e09815eca89cb39ec8bdba38bfafff338b002e3b37545315e0ae20b0024825199c79e28299f41a43de1117a8917665d1e5f3a3274025a24ce74ba310d66ed2a43972e8283854351485ff0a0938cbd9a96fb9ff6c17179093be36ff6cd683d62aee8caefb05d9d08efb617299dc04658775d836b9fc0209abf4d2e1772e52d20660387082bd5ebb36ad19ea0fe372c323eb09cb77ca2159bcb56b7f0d08413181be048107e662b56a7c1d168ca9701a6cf4ad488da7f61050b3740889700ba8e8dd8bd1a1f9c2aba19f2c9242af4174815281f89726b0adc014a2ba94ecd676557fda6002903237b23bc3eef14d5e341448d01a02f7d72a2aa0f6155ddeb2aa5a83d3488aea5e06a460a0e35176d1a00f23c23e19695d6c63570bd120c30b48837e5f72a18438c8c661f83a34f048f788c9a861bbfd3ebdf3e2b106d1420b0359aba688313a908a83a44fb63e2598fa707391759ace116dc948dfb498bb41e51aa651897ac6a09377400ccfd33f3bf8ab22f7372282570f3d781f9102676698d9f1ee59fb8252d886acbd9b14294940eef263c888c42525fb2a5a633062eb58a5a8fa4726e9ca7202df94c8a304394d005dc398f1133d20334cd9ce73c643f5cdd561177997c2ea6b3d0c96691eee967f2a2edbda9bb53493f63ce9e3f8b13e189683e2d2021e79ca7456da6fc3a4faa67f43b2d6fd672201cb4b86e578d751b246cb283977677df12c0d91787eb589ca478393a8c0e861544b693b80eae747eadbefe80a0a2b121f848371bf2b4889105947ee4dbebf8995d9d30ee2b0b237e57517086edf941632f167e916b911149d1c5af3c405fe64a38934ae9f70da8ef6ca297af28d36255f410b825065b86e675ecb79a6a7d062ef055fafd4c50e0a3a5f43cd84e4d6a72f815ca1badafe370923c5e2ed8f7b612c139b8ce089b000a58bee0783914cf478d1578a6ea1d0e25f8d73855a0440c90ccd238684e327256c2664702a9a1e179e3bfcc8cefc8e3361ddfc91866e230f7fad0a2679e76133c19064bfed97ceeb50af7223e7f264a887cd761c89cfd3d2027a4fa89f8f49aacd55c2ee1087ff30adf76004b8734beb512afe4189b37f92382732a69e6257588f2d1029eef8b1e15c6d3bba9308c01b9a03dd454c9419cdf3dc24f0c88a1f09360be7313ff09a3040e6bdc4177f183b661c77fa9dcf7bb06909a263164ab33c8075b098ba6e1ca135560b37dc458ee07558e49c5e4175a74ac0902aa1ab6c8bca5392029288d151b2cbc34e996c01462622f2ce953ea5828f38ece978bb664620c2a57d0560cc153203802dbd157e5b25f71e153a3d955c9b45f51178d606507e5a1ce27783d92e63b2af5b0eb75ab3b63a8d08dc2d322c552fae26d55db2c67ca2e3b7d12290a74374b0f9e0ad77ad481e846eac63c4ed17c2c0cb98d662c1e0befdc447437088e5c1a83c183a82adc00317661b6e39d15a4a24fd010534f2528522dc4a65539de27f5e2584dd4e04b734ec2c12862b21a06927f65c21f551e693eaa4203a432d0f91358c21cd50d06c0f7dbc8a2af5e033cfbbdf375b22025d573be9f54c822c456f327949480b3c8f88edd29a41110c94b2f488cb83abf088da3f1e1287465264243cce840e3b52e76ca9600b344d21e78286aa6a7319b5c480b2ef327f8394cbc5416c40ac7c6c57f23ffc28ad01d3b151a0d969412cb075109684ba822e1e7f462038b50cbcbb97a1502ca4abfab9231cb4c502a35747598c094d45df41b81f369c52d2f89571d209a7f9440cd2c28198a8c6c45603edb133bce1a26d9da7e8c11ff121f2f18fa8738d21ff6806771b921a6bdb93f13e8fa6dc53121abb504145136d5d0d8164ec1ac7488ebfcbcb5afc31e3ac31ca761b48eb5d2a13eb4d25f19addc2bd4b73df90682b82be17c8c114a95325ae50657af626c4dc3ddf7c622ebbe5e8376441ca7965d56a4fbf9d45c9fe3ddfdaf34300bc56a7676e4e0efe70538ec7feca37ee7e8ec485f6a8b38a037ec4d6747fc6d459d0f5d04f6a4f0a27f983e19360567883450e8c33eefe3e8c5c62147019db0035683810e803d8f5eb31aefe4d5635eb29806babeefab61cac133e6e37b038d624df6443a041c27a4d9ec2926d8c75cde562a1cbe60fe3741db07ab52d3a9222b428037b97527c02e50e3bc4f796c06372eab17abfe116795e7669b85477ffa0aac31e82865f3a0ce1a3fc765b78c60c0c3ba28237f70c2a77c2292d3c70f5fb893db8cc2f5aa67e9322731b33336d6fa290204f0f9535a8fee5400d076bc5096d0da63338d782d22f6a4a14dc5140572e04b6623c32b1ba964868e0b299c0e8f8841e392a44224f9a9dae394abc059c0dfe15352920f984563d0057830f9921ffc8cd834f0479bc760b4004ff4f8d8523f5addbd42f798b750a532bb2085d04c53efabd4e8a024435fb98ff55728495394fba951b630ee0b0026ec3710951f52563f88e81c20934a32b4fb89f8388ae0f18c27663666af1f65d92b53944a70f3c0b985eca876963f7f109b8ca00fb825de47f993c6f8e3e03e8ad62b95cea7fcb33509df4759e28eeaa48c5bb9e7ec9abb08706a69b0c4e0b51632c9baace8b308b50975f5c268d2ff24518a7e81f5548e7da5d2ffa361dce1bf3b7c14b96072921c51fbc3794bcfc3a315ea4ba3b11f887ba43dca423494bd9db9cac09bb8a74832f2a6cdcc67afb836704ad115d0eeba56007059482048e9f71302e3924a599ac10776604e81e420740b0de29603c749a11c2b4d3c40eb223fc98330e439aa2db4b235a4c73263bd08c5fdda408adc274d471ac66507c048917883940e95f3604f344735beaa8a1b0ad76404fa15f4f969736f8e3012485c9d9e66a41801cf10a25988aead275533aa554d2e05041f2fb45129cbf3ccc538a937b7f2fcf4f55445dc1ce275bfa06c8ba979c3f96659dcb6adf90cd99fb135c8fb446ba10b5f742522eaac52c5c9478a86bd994070d9d41008b977cbd0e50496f86eef771c5f1c1a9cb1ef241d6f32fac44fcb51c226d4661e7c7f0f3629f36840cc5a5e75069d611985f188446ddbe759e41e4bc6daf5e9f0394b03db7c30822d00a4b8d44ef938de0588ecf56825515eae67ef240ee390511fb3ed97a3e128a2524946f7700ee336e575ec8c0892d49861949057b66b6c00ae72c97394932fa0d3625c6d1f4913ad4d9900a6e15366284dd4bbdd19af79d6521c1704d371226482e96dd2eaaaf8e950121f894f4c6d3029a0dbcb9bb388b165ad3e8c2943c51d9236e15def4589972d8ebac39fa795e702f702d7e0f0e880b171680031b0e80031b0e800b1587400b158340031583400b158f4590bde15fd437345fbd05cd13ff42bba0ffd8ae643bba2f9d0ade81eba2afa77d30311fb257a45fde072cdf6c8c6c4d1f9aca8b147c9cac6a86da5485f8e9a521aa117763b6c6f9c83ae907566ff2f988cb923d8f2f8a9b8d7905119b5b729bdd4fdb7e1275f3aa6090e3653812a562f657a2c60b4e761a52bb619e78ea6632a8c1a1376a7bacfe4516c71aab351184a70d2afd08acaa8ab3ffeee6f306c462540107d527195f57312bbbd55e6d37d24f3e4fb7bc98701d4a3c4c829bff0d2489a2e26e8babbce10d63c58b4623a250a8dbd4e3e3a99bd00d7f7d86ba14ead8cdd7a89074340487ca9067f3f6bb42ee5f01e18681ba3a5dc4aff33a5c952b260c70a068059295caefa17cd5ff8e09f8d56a51eb54f03ba69a4d4b4f2bf061621b260c160441c68de93928018000202477c7f365a93fad53e1ad06d49653941b0d4b22cb39f355e92f2bc67a007b07a7d718c945b093f339321380f06011164ac0f1ea369703e6a87ab3e8bf316fe993c6375199380dfc9515b273117ff2bdc322a63d470393ba21863abbd32ff6b79c434fd29bb4ec8b74d1b304db3e46eebb9994d8e02bbf43a02b12060cbbe9fbae5c834b4f167257ae7ce133d08129c35354a654d5034a2406d0b6de293216c0532af6d448d682851b473df87caa79e23e1c276bda47c0fb82946bb0f17eb84be621c74f88c5368da2455df31ae548089b70a8838cd4d39f661de1b6c1eae10d1827134effb3d4974453985856e0795c692ea7f86231666e2cc2242c377f71a9e649d4539d702f605dccc4ceb7f06c616b6c2453d0498a2c92eef6136c6c5db396b630f9cf9b4ec0f5bede273bdb842cce8b4f7253de6de0ba82200836bffb2676007b312f180519ec240bd8fad0d0103b9890be87e0d0eca0441ae568149be65c1ee9491ac03c3a93703423258bf937dac88c062456ddc4da57aeee35343f6a0643cf9944514038abe1b4e701445be53224222344c5428e672786ff89635a10fa986a98bab6162a0ef87782d12f28e5f68dd0ffa9b5d18260d781ad2aead97776356d0b10a1a3fbb218d52b968c09af444a5962f950778f910597d657bb5f7a0e667466f90790442bf3ba1c9d5bbacab9aa696ea4eaabd04d004f7aff61fd76a066d16713d385887de60fd5f23a5f7273e57f710de558d5bc0307b525cb889e7d062daed7106375f212e716c20ba3398a5f71bb73417ef3c19eba548a6afb54c10bb151760cd03209976b207618de25820c5813a2188e6f64e5fafdf7a9b9b56ba2faabece53b113f08c24be95ff23e4ef65b9bdf847c240570084951aeeff73e6db3461bace5c8d19b6d301f06d1bf339d582107147d41d36e8a51e01c5afe116dfb1ab35f7e3141c23eb5a737f9d3ea7934d31a61617fb3b6236b343af31074053478135ba2dfc6b39c86eb35b0249451e8089e4debe54b7594740917bec6cf2b2828f5a97628845bff531151373c09b6d35da6a16ac2540d3694c4b4f27c6b78db9d546834fca923b32160050d84c239ce968ce6adb620a2e92e00c5ca74e8133e66daad2422ec019759320c50889d0737f33f859ef9deafcb4d413eece92f24f48bfcfa9c6cfdd2710f320ba64d1523832b84029607c7c6eefe5653f97c4fbb92072a742823be40c21a4153544d496c8824925627e7fc5fcd365329f232e519c18ec2db9b0cae1cee02dd53a109d89ebc808b58d7ff9acac876af304218b80241dc31044a13992649b5f31fe6c83f27d9e251bb823d75074fec710840f1ecdb4a10af65a5c7a2f9cf5e3a000f8630445000883e8e79000408ff928bb4458ce40703fb9a1f77dff9b517e345b7e8b787d2a84202ce78d84409e09c60d05980115002992c5e107e74624cb1574f7b02b4129871e9130a24d2158233df256de689344f4d8c61a2807301f31821832683e4d2a4dff5d87a2335212e2c4c18572a1b6235e8636e9486803c86890462bd2572864477c57c144ebce2ef9c844ef3ab5127c140d262f6b72b47c8834f1c8c39d3a209e0432e667f046d1c08f862e79efd9f65ef25922db1760120e21804e384e1d79ef43e6058b3f99e750cd7ae8585f488024161287d0bdab14d8078dd3da9fcf4337672c7879ff6ea40aa30311c382a75898f8c387a758e18d7a86f1c2f701869ea73c090071e15ef4dc2862daf03f968efb19b28e4f51c062654b7556953cd3a8d4dc6b62f2309309d8b4ff3796ff77000ca4a28a87c468b18f4449401737fbf77dd6c210c3a13ca3134e51303a073ed5a7d320a2780f1dc85c6b311d2bb6e5c00fac505b0c061c6034e43afe12400a7fa594b7a1bcf066a37ad95a7ef9fadd654ce1189131856346c6168e8d8c59382632e6e45803f5d65bef62bd81ebc958efe30fbf33e68fe632e68f7619fb8f7619e38f7619e38f7619233ff6c57af0d6c3b25e1fae7c26d95244a1f14f936ae353055bad0fb02d76d804a805bae3baab2332ece14cde842afeeafc72f20444afac76e9ab9165d9046249a45b7e98e349f5c6e1733cd2337e618eb718b677eac8780065e34a3bdb17d99803eb7b505b9102d1346a2dda47e1cd08236fdec6c2d190a95a1bf2a22407c94a38f58609eb346a9c5aa2f143ca784dfd4f305e4bfd4e3986000366b1e6fd4d867ee34d2839dbb319a95723e7072f8381a9d9f846ef2dbe82f58beea016ee8a7565488de33021af729ab38fb14b281bfa023a9d08f54737b440e46cdbbce0288be95018c8a2b43b7b39c65811d2deab0a935b55b0f054731ad293894fb8f020886c9a809651fae3d4069e795e9978635642e6bd051d57d730dcfa893f5d959f05351e2e135ca2dae5a9e193300482455f478442e289190658e8d8dc139c268ca12c5b4e6354b2a0befe902885f31f7254835731b509a5243114c4be676866dc45b3b237d09f3251ac32c892b40398094f4577052ba06b2952340eea3ef71a47436c2eac4de6e5e86cbd4dc11c223fda5d5d8bf28e577c03ed17af29cae15d675cbcdee09acc8908b6a3629ef115032ccdfee3eabb3b2a2caa5420b97cdc8934ac89c76482ae76e2ad1eada3ef8bb865fd1038a6a219ef828ab8d5979d963d6c1b5cee84821c1f69e0c10d0b152682be681f0299390f4c58c410418c88c4884b8c984488258648229668f010cd6e17bff786eb9b4f3efaeedbd7be7decd3c7be7d2f5f4cf9ccbe12e2a7d51fa032c8201a233bef4ab95bf3a017de961b921f2f604411ae5d7e47a315edaed420b78941185014f3078028094eaff37eec9f78707789737ac1a6419bede2203d1ea38d8ae94d8cecea1c977c71d35a4a55201bac92cbccc29c7ce874a24a68a508c1787581b17a0773afab796b9b199ebf10f41db68cd7a554b15a756f2374d59467a5ba5c1f22ecc6544369b1ca5e46e8d634d55a71f93e22dacdb4aac2ea5a3d46d2a5699e552137a8dc222c7d8da48b4965b5d5eb7a8da04b13440bd525fb886837d3aa0aab6bf5184997a679568523f415c92e3339544e9d7aa4ba6c74be22da6d26544e5e7bace46515c511f63e14c4a081d7b431d2c625071574e0f48f63eebac6a251794a2b23736063498e644bb1074303291c3a0c545ee3b9d1cf207469544665b7b5b9bc13747fb221fbde466747b3d1d8d16cb476741bdd8ed646b3a3b5d1ece86df43bb75f1095c169f7e264d0aee4852c1acacb02bc80359467b2883ff130eedffe06633de5b932d1292b8607589df24cbb07474167681b2212eaf207224404a3b2e9e047cdde863269c5989222637cf02531af31c8cac55c9ca31578be9277e6ac2894258fb0dc69753ce50a7529c9117352999e7261093956493fdc6451cf464b7d481c2290208554d021d8de43d9a531e6dc0207e0c6c923d65336cd84150f8d1b9eac3f1e5e5cb6a8d94c35d9f70b98c543fe405eedb9c2fa29e7d60d066f00339f2cee324173f87cae8c90e19f30615e4d06f0267f0da91a0d7e423439ffe95e989311324edd4039d81ebc0301209e0e31f7739b2beee8a025dc0a13535f8ee712e0c72c80a7846f2fab694603370bac5057d734a16224dd7ec7ec1b1e67d63f576ec3ca6cf49fa47fc9c60dfdc986d68ae6015ecbf5117e2ed1aea176cb341fb58967217c15f5e3c07c60980bfd39e8a09bc9ccb8bbde50e2cc9de7f9af0d64d4e18f5dbe50ce883b0a5d36e996a8a730145d79230689c4b75a6aabf658a41779723d79c9d83ebe5012cd572f1b60c2ce5e40e5feee64c572c1791c7b26d49f7bde89ffc748abf064deac728b130f0af563e31255e1477442ccbab89a214e5fd0f582b652c4975fd6dd9874fee0215eed7a1b6704a7fcd3f577b57ef39d13b3d2af24edf55f519cad78e3bcbf5271de1ad36eedea07a8b1f4c8d8c8201e597c31336ad7b657e92296b31f29672f412cc48cef97d8fab31bf95dc6e19127c4659fc6efa5b851019e40ec3ceea9e2569cc6c67e6094d45cc96bb2e029ac0db28b50c98c6867b2afb7c0c48b6d3f7760cae3f721a654d34876189d789ab01cb928e087f8a7a330a82e8e1f224ce7eddd895935658e4bcd87ebd6c6d909e31315cc3e25697827a0b708380d18979519448d68357e5b38b619fc30f8d3c958e09053ed1f4055b6ed727dafa4d721212a51692b7a8b1b383c3a1fbe8e2b9634f662b0f04b84a7b628c0d08d4546a502f8ee15c591bb5abe97102d136259ffb0eebccfcbfe1d1155029149b34f6b3a346d9d2ccab17ee3a661efe9d2dc792677b7592068f4cfc27fce7767a09794a8385005e01f10310b5fab0fe412a2654424ea1b9694f7ef2610e85c7c131130ce5a57aed95dff3798183cc50459fdeee31cacb7046964eceb92d0729bad1dfe9a7f2a7d097b33a28605e8ddda6e5fec39af856304c6888f31720cac983ddc7ded5ed5e720e2ffcb95e20b6fe5c333b4cfea3e3e0f5b2fc89bae17123c34710da51a38a00e57e51ea110deb4c031b0ecba9b38acdecce40d3cc24301d4844b11ced61cc1de2cd11a2474f43ba162eb57d75eaba502ec5f17ba215d875c256b9d1d67eda2b25d3e4055f4b7bda276f4649e6ac1f0fdcfd19007726cee0c47f63e51f9ffde418213009e6e3744a5659ccf0221f8db9b6658b89e9ec0acd7b6bc5c582409f80cf37a7825a833225a987d28a79bff1fead648e78fbe366b879341a651613123d844fcb7d8d89baf24095b6471d126845001074491126289d0081c09adf652aaac9870810712443ee0b1370038fbb528f441b2bf81d1737f7f33fb6bfa6fe3a883cb650a51a6d1eb2092b656bd3c88898854bbb454243a44598890c3490d81263f57497ff99987c49050e06f50f92933fc5455305349566cb19e440ba4479a665d0d2be25f1034b0d708e42253eed9edd90353f3ac2323553f4aac83b0fbbf7117d0b22915ba98b90d808a0d121d6603592ecc527933488cfab5b1ab9d862e870a7fa77b598f53f8064a48ea5afacdd86cb702cff847399edb5fa8344388e7ff5b08cd5261e9db2230ab3e18d3493dbb9c58bdda20f6d17e9a50b97e6a8958da277f0d09669a9b38043398a9c43ac9022f7ad7d414bd290bc3e66acf997f3a7366a26233134f84441444e4730dcd271db06090e1128c188bcbd4672d87410ffc005bbb0c6a275496fd14945db621723b2d1e3489296ae4678962588fa68c1722314f5852001ee2e1d7f25320c289128666ea48668fdbc18d11f1ce34294f627223ad3db92d180b8ca10775a33d9f5e2b4e85e74cd8ed7c7b3ffdd78fb28b9e316076e05538679264e2e10288208ce23db945e74e3eabdb2f8383dfa67dd89878794a8c742bc5ea428afebe1981e022490d171e7037501a784f315f8e86b11f6eb911537bb60fe91f2488a1e2ca508c9fafb6385ac894ebb8692f3629d6ba8e61fe924a06880bd87415a52e33b1bba204ff306143e25307fcc65fdf33463acc4ed2e3a1d3b11a5af52a1d45dba0c29d7ae6b72f07882f73e5a590ad85130b9fe14fc39f48b552fda86b625649f01a4326053473fffbc850d5959ad5ad0863dadb32bbe0db19ac796b81ac5712b59579593272047281e619dd7ead4cc73e16180a78e4441efc615a0ec9c388cc70b3807fa2b72912f611873f4beefeec88fb26a6718a6ef6692055b831127420884fdecb016da25c6e5aa491fd60001891c71c90a62d8a26a964a7d710be5746f298c365590118e9790b615d8b7e63f61d99b9f83dcb7d0a7ec00172bf8ffb930b616a4dce08877e73f4b0246059ea078af3672344ce31a6198476f127e0b94f5e69c6fdc6aedc31febd34ba31e6d260cff4ebb3140cc7bfb5b6c646e6e3f5fc61d8a292b1ff60936dd9c2a6df5cc891e5c298630740497ab3a3c612bd3ab05687d6a8998b4818774d590df767a540d526899f924fa06a87090f6119128633ffec8c09908fd183db9ccc10b5118077082d850a598b0bd6708acca0d821cc08e6dc9b8eb4744485aa70bb268f05d3a0e3c1e8a1d2e305d050c0ceae0e8b23d00aa374e09e89d84abfdef0c1233eb40234aa5a306988e3f06388e0e8e3353e424d6952dcfb9261f79869b507e868771fc2b649dd1abf584e0f1fb71079ae41b453329352c8e1278bd99d1cf72d5c612f22a75d5eb32bfcc0577e2aaf5a73d6bdc87f4b0bde289667791e30d78a7b2c28ceb0bcdbf384c0022dd4ac417cec2c07ce982e7c9d28679438d947362cc2c9ca3dca9e8292780cf9239db0caf438bc193044a629a0d09fdc131a777885f6da2d00bdb7f548196f994b3154396087699140b6b56dbcd6344010049e89e501bd651590612b3ff23409d91c1cf76288e27b5877bff25232e0dda8842e01544da3001dee48004698a974aea21e341e3dcd3d7832b86c1806e4e319fbd130742661f758675e8244ece4550cf6e57986599576da09a7fb7c86eb010c03738d796bb7b8e02cee7a217878e798d6932dfcb06a22fbaf779a7d236d9198391c77012a899d9dbd4b523183ab55ef3292ad1a21eb90c5c889ffe4054efffd5e194fa06d54f18e7a1fa7951ec68c1659ca6e46bc5a93e005603d7a7f290a6616297e0339e2c2e6100a0c44a2ecf350cd5382073ebacc4f8dacfdc99ddde3e44712ba4a2442223269ea749cdcd2db41703a85a6769471772e6248bff3e0d41d8e7a9360c65b181f7bb0ff9d8f3595bc57d90d2172da3fb755f20acb3fc594bb3e65f9bc4577d988cc964d4b651dc283f3e4ce941d1153ea9233daf558c78aa9d611c53806217ab8f57e676a4fbb81d9f525c611e2f42b7c33d9856ce4dea374d56ae60c4667d727841580e712b73c5cc9fca6c48554a2c0e0e4f1a2a98264409d63838309e2d3295809d59ff214137e16edfdb6ab9f7c6ed0d0a78e1581bd664d4b2af821f8065c359eb6f08ba4da9c4d726be3ce566c876c6cb2d1dbad12cd22e7d7e9ab66c4010e2cf2a06d123f26f059352e25c727613325ba9ac092bc01ae76da6953b198c2d8157444813475c36510b32d587ba029d013b37d4c8079282d0a2e26a43292858c07a1a70062d213dbca8530f9e6eacba8504b4064abbb6818cef59b151e681a8123aae4fb20e634dd872692ea3378003d9e2290cd88ae9061160bc07ed8218d53fa173875fba2f6aca8ac23a966da8df6e1e73a30a4b18f1f315d3f0d436fd63d8f84eb42304effe1b487ba009807d74580060201a4a201d6f17123b5a525d1e6672dcf00948aa53bb852b17bc0e4e4184b476e2aa480594f9cbc1fb9f219a27919868bcf9d4a95691d0c60e5ab447d8f91f1b9384b04e4429e54c0af0652a200d4a6414d965fced59a60205809b3ad64113a7b83ec1f473c9cbdb95b0f3e556a24a6affeeb8aff7878960eeb3fcfc4ad1370bb0cca277af783d463d875ae11d12296dd129402ed3410f409e51c619a41cf8a56d4f178767938e56aac3084ac056dee0bd437059bc9131b48004dc1ed3b0d00cebfdde8b81f76b6797ea9732958e78768e782c0e43fb2e5c49448b62e1c91df5d1ef4e808161a716838563fa22d7651fe7b1cd3b12219702669af3b419034565ec2a21fd746c8ae9be4f9e30147cc221c0ba0ae2466a15fe65b73d391a3bdb7919c0326dd1cf7bc91b547063cebba25764cb0a79093e31d2e7927208314d5f5454db84310a369564da1897a8126622ba0596e262286d29c74a2db85be41e855f7feb7e23a879edfe715c3a1715a125827508931469019a345ec126e4b55da021afd4ba0d835dd76b3669c0b03a93c0188f49d3b1125b5d7f939b9377cbc5809db71b8414f9333b7c5355bd3a659290966d028c6049c7f983a5254b1cce27ee842a24bfe98d59e51ed35c20feec2487b730cbdf005c87d29212dc2b5e2e33dab26d6742f914267c2850c12260704391bf7bfd131dac87b770daad48e5ce91ba7378af526f2bb7d41e9762dccbe110f97651c6e2bb38f5ee4072c28b55e0e2650552660f524d8bd6a830088687b5bd21efa8dc112430b45d228b3c1c6358848c4a7a017c4015e97b839b324336a29cbfe4771587776d7e5ab6723e20d83e1a9154d6499fcd4b221b8cb280cd15bb067f5e527a23c019de3a40e227fea9c9fde6d8a8ad92a49b831c20934a0b2a7a839ed020274905cd25223eaa338dfe58850a1dafddbae18095bbb9400159fbc379ea643a7dda77a651c82af913dbcff48e584a03b05419f569654696bc02d40876eb3d161eb8b8700ab740d17ab50c87d90d3c0aa660afc7d1365913e899e544f9c35e37acaeedc16bfaba30aba17eb321590335b7ab66c571c2db21b967b374a10ccc2861778fb171605d5bb3be588fa0b62a098c2971545d07682cd7d4a116e6bf36bbb7be3f82d04650dcd4851b696f3ce8d3d1b0b1dea6be40870c5479e4cf27097925b52e8fbd0fdc512eb233be5c1516a0c9d97756cab99e3dad216b37d1d9fd3b689cb4b078531f56bd7b95a23e4a527c3900cfe955fb100b26369dc26bc4f82a0105cc4182bdf6e263e41ce9e127b6fa3e963a4e208487406b43ad06a06809bc60e28fec171c5bd0262236b0afd30841d7c73000ef1fc0d25040b3c49c4a93cb1db7fc8e86aa9e7ad238b2d21ace79dd221bcf29cc24ecb991152191373455613600db31a94fb7db0bccb2d3c71e394c2d87fcb990698f9290a4b52a829b0af799e8737b069145e1aa288ee81a0490f0382aa1fb07120bb68cc2fb5d4dcaa16f8f52590625bc828f2b8ab8106ca761128eb0baada1654274d40687b021d3d03f77e28978319594d1268313f3ddb246badf387a4cca56d157f14856a31e38fef3ac8f1ac7a8a7e732a5d1d9af0778928b3d899f33101066c092222e18c728d793f883076c7ecf109222c4c60c12d96d0b47626a02936fcdab001df26a236bedb708f483c1f030eb2a9b730d62970f8f714e91d1004370f2f2d68a3a5932d655604d0e2884414ca0389feded2b4e52fc23f03e2919443143f3c97ce99570610373c1a9756d4b290878efe4b09e612a906bd7c5902835a0b128d2103bafef229b22ba1586843158c2e4a2f7631f53c43060a336fc3fabaf2100ec45bf493ce9f3f122e3b71283939f44521fcfcec41af0ec17208e62565b669ccee36cc677f416e0dc9623248c815a505e3c9020a55025264c8db9dcf4b0b26abf17a8a0a918651e69d3f121f41ecba69936ed0c9c96d9bb8637c0d0e6d197795c64ac1ac47fb0610976946b7e7c3fc5899a47e24665970d48792a1a513152d62b8d8361cce1aa82685e69d9513161af1689e235f10debd9f1bec0c290ab1756b623b60b7681464903ee0361891f4c88ac576a6e2f9d84bca14d05c99ae0c0f1d51fcce7e9402f5b3927cdf21d22e86f0f56d81856b47781d436dd94373843d1fe861378dc8d36da44ef5d08ca77dadc74b9ec20fe9448b63e8ef67b1ee55fe1c68c99d61b91c97ae01a5f326363bff5f7404453314c86608d2d393d4075e27d9d96f038887dbc281d81a314b22431b4af5c88736f1824e6c3e4b7c3e38a3c9de503081236d98ba552839a7ecbb3cfc0f43ec4044cfb9b2f5a1569eb3b94adda1bd0056d9d11fc340d1d8ee97a22a312d360c40e296f0490d16ebc2ee64b1ea43953ce286dcac4e9c21e03872bfd4e284f71a9611b596af3f16504cb9fde8fb17fe40fcf1a5cdb00e979fc09cca1bd5e876e8dab2a1eeb0b2c1feb766bbe18266c44af867172ea92b729e856660ec5148687d61bdc869b2470d52f8f2aac663f9cd2ea32fd71dd0f15c74fc616ae572fbfd606b071f96329591e3aad2f55643eac01f392872c29c48665d515fa61b626b0b4e294ff7c2b9d0c6c561c0721c289b6bc0f831d8dba524414c4b5d2372dc53321bc0941e2644538f5b61a73162f8113a2cf3bfb222c4e63c639e9fa1f78fcdb6f0292e27056558e58a7d48e2c2ae717db3c82cd3fa93e48990ad9430a8ff9922f9e7f205e06ebe0e3d7713eda6a69002d86fe74cc4e8c9e5b2b934b565719cfc43ee28d4723ded801c16c5668c35494a155d982021e3c6568b930fcd8944af3f6f87f791166097a8b59abf371a05cd10f84097d10f76479cd68befffe3d940cc0510c81d791b94901026d9dcdfee8f4267390a331c292a10bef2c5ad641c8cc3456823461c1b91813a45eb65c8fd693882bde67c23ac98786ef4f2d00ad2737d8f0a7a6ffcb8d841f0324fcd4cc7dc7c4d002b4c471b7816d397cf53a5fbc6dd54f89c42a4769c4a946a8083f2e2b1c31124306ff7611ff4e9f9c9f61861d09793ac27559d35773a30ccfe763ad2e4899fc275cabf4d6bc995a38e8fa8592a7d3fb56345ec206ae719738b8ff3d3e938afae2c656ef36a3e6030d85f7cfc21311bac978eef1ce61e496d1b25dde87e9f509fc932d0dc582867f20b788eb3ebc2aaeaee51409dc105d5818da64eabd8ec6fb5779c08459de5671f02eadda91313bb7ef9038ab2e312484414a23d50079b064c9da048d0258d80e3751bfc82ddc33e244726ac0f2d36f71d505c337d442d4e7ab6484a4b47cf03eddc2634268ca402c6f8bb30ccb35745acd2c79a2483f646cda03d4d904897d14ba10b76f013506f448763d336042f765e3a495d0a6c980089566cf30da894f5f87379166a7928b50e4e527e78b13b10866acc9b46c96a5a2f92a61ad0fe08e8ae347c71324c1e5e15168c23f84002a2b74fe25e7a90ba03b702200aa26dadf7f6dfbe05bd6a22433c493d8d1fa11283e93579bc1e5c62a05d41b37f02f9f73c90214efa1615921264ae8bf1c72f8452d32eeadf6649e76bfcc612660d1c81b0f1269345ca7cd68afa719359518b53c72799c3068b61bbd82d457e60b58b3d66251334a89e5a781665bd51106f6950637a9f6da63a890700788616a521e9355da7785679a55a0154c22610e1b4d108659d204fbd06963dc05ed44c8012790f796e17fb329ad191b39f2b1feb449ce9388e14ae0f268b6d1af170e4e09776b147a55e452a9d99345629d928a1643f3dc1d3774b74eb3f5fa2137cf6d6f1a37d0dcc886281819d1673fc20ff5b1375476dd4ad446cd1252527da5620c967f5da24e97eb9ff3286bdf7c837a34f9632a4751971384c249de72d466a555a10970d0d44521816588010c80d4546fc8ac01d0f1a196a00634c13d8e4b7fb68d565c3b98ef8e77a07da922a686425e3d222aa91dbdf7e27365a4b3726a44c68ace407ef5e383c896763cf02352cac120ecfd1ca25ddaa6c4ca12d80cc401d8c91142a3eda4213067037bca9d1ff3ced0d5f11072b1ca8856d448225b944fe1cde8c1c263283982a764d0eccdc12cd05569b757948bdd757340f55554c36277607e4dcb8dd59ecafb39ade9d6947b5108ef4e119fed659ecd08813226ba8527b9cc5cebf319801e292543f4f798c2fe35a00791983f549320317a62b5add143c94202097d92981957710ac019c0124c629df7c1e9a40429f82fd9132bd038695fc7c5abd39254b48ce3b9547f20a2a9597c17114b1ebcfa1155209d214efaf82931a998e16fafb4aa8352ed1e60cac1a05d4e92aed94fa382aad80646c7c7a5326df626b7cfd1218931e8cd364bd7905ad551f3f17726c6c49d721b336a5a66278e423261d1aff3506898d47185ef3bb9b52ebf188e789984c2b33822733787c9429b9157d60656b5b05ee17fc5cc8fe23d2dcc4130ad86551061555922477fc8cb2bbb07a96dee9511983b08fabc9f76b912c936151a6e0ce509ac43ed7ee68a0b993923669b89494915868cefa88ddfa324b3ef913d64349e2320e8b46db1f1973e6d31c67c4ce7c80bc2774d9ff8041020944d040a73da6d6e4e5faba832e117a2aab92771e0f26331544a4755fe346ad1ab1531966cc9a31f4bc5a17ff358da83db3d408bfed5e430f397286c68a7631a815b16f4883d8c5b96cd2988d9705dce71e341810bcf4706419af45a61413c674ce622440353b437a577de17cc0bf86c9b9e0cb531a317c76023c70828a4c2f46a528fb0f03cf972eb1f119955620524abb09661fb80e490853444d44cb138d9e140455c4359c2ab5481857b0c0a1786d861d9b88fc0d9071bbf503182a13e1710b54a9604a0065395e5270f1ff584355fe441fed4cbf0554d70a342c1ebd55399c21736448220d14a0adacb85c01f1c22d9cd6f67ba63cd8fd90898cb15e3e9ba161cf793cd7565790570e7cfdf8028b747405f70b8524e93d5a77bf17024e73a126f5bf73fc70c7740efb8d95b08ed1ddab4deb669c6b87a478ce7e023348e6fc69e6d94b1f2f380d4ba986dc6e2d3b2a411f5a5ce386326ea2730142fddfe39a9542274f56859e53033320f454eb83f93e83b6c65c91029c347eff2ff7fa1ad5fc57fa8d5c68f9148418eaee7a1f8e510d3b7388d2fb4079253bcf2b0e405fb164b827545818c917f6e2b5bec82094a5498f4b1932cc703850f10b47311be2592357aaad30e136c6104f04ba496028866e16fdd47ad35155d352fdec2de82cbd170469979f4972b9bec064ea0b7ad609f53f77faa6470e2c9171d3bdc8beb1e02e5957e4d5b54442e33039db0211731f4e127a2bb0d2b01ffabb8e4074b9ddc366caef438276c98075802a537af338191917112d11293dc4374fcc88f7fcebcd0d15ab7ee89419b46b257d41b82b5277fa2d3c6bcfd79ad4b00711718e6378dde9e74b3150116ec82db605b7749eb37ebb444e5c22a1dca860ab7671020c35ee6ceec02b0ed157fa077c78af6a5cb822f23708327fc4ceab2207773a9c8a00c5d6cdf5cf8e17acc6c3d1f5c5489f4764868a361485007272f3e04eb6e697e7596c4d7818bd2c848425ed9d8eb805dbf8a3d284c726a7f29d8396fbea5d0e1719b83f278757ff4b5c9ab62af06aa98408ef4147214391a368750c4ccfe535c49d9f7b467e6892c73fbae0a88f9049f9ad2e8bec9c4bdb005481d9e33b0ab5f7b602708789959c68d756e8558a5aa6726ee0e8bd80cb580f1c4f3c56925c4904df8a386de24bf20f8212602a765943b92632afc6499e0ef98fc109cb8c7993c04bc3c1b0de727fcd93f543a4d203ab66a02c48fe342c83941282144ff32bd8a37bbf820b9388baf20bc85a84ffa69999f3a5da383324df4d70ad5d8d9e1aa882eebe5c6ec7f0c2c80368c43cef28ee955af45e096595f32d1d9fd7b8ef9b3650d6cf78d5f6fed95fb766c2487aa9fa4beaa123551f46dbe097fc80baf7c7aad1b775b4708e2cbeda3f7c4c9527296a020fb2ee35d87adad7db8ef5d71eb59b2cda6f99caf54aca93617e67b7dea88ccf4e39b559d8887a30f6a6201c694b469b67d0733b95867f7925e1050380b39228ce2ab481ca4dc57e61b073a025eec9af3dd552a523d93d62366a5a3d22aace6b34ec0c1a30aed82d774d6d7fafb7611e7625937aff9603c61d481abb23fe717552767fa5c140fd60ba24690c6fbe545536621ea3b7941355538f9ae2f515b88f8badea943ceac4a32bdc61e9c0e5456a0815fee161fded1b93960a77738fd2c5765141ae0daacb61e64ba41ab2b2353debe95ae3016199fe31e7459d58a0aaceaa2eac03180af4410a7ca72910ef038b8645050ef2899046f7b883a4abf562c0de72ccc4b039a300e84337d2ca35f19ea6e6480ab7fbfd4aadbd08b4dc63ff59fa1cffe1f25247c91f8d03684548bcc4871d49b3045c4e55902cc0a6003ab544c01a227e6f6f9d4d03f3f3dcc83a7048f23717bdd42c06e6bbf45d63b6520ccc4dde8469bc4fc31e7fd03509b3166344fecabf7053a93b9ad3a8f653fffd630f48b39678e31e4906e64b6cea983503f32cc509d7c77225ad12dfb6da9cca6a0c273b28e3954e947ae4fb2a8d748496246f550e233656a5f77850781b14cf8c808b5f13a9689848aeee3e1073be76de0338f8d99048c849d07b0a121d950b62c3f6065252b4bf09d28fd3022ec8ccebe91eef4c134ea29bf56ede7f009928c1e7203e86776711158f109a6764e41be4d3a1db47fd38b00daeb0ffd3ea4ef13818603f9e2cd502052c87828f0cac4ad5c53210a8d1782b0149c02ab8889a2fd27faf7eb2e45b6c6504dadc28314237529abf511c16e7e03c2ccac8fda30000183d0c76e86b9067537012b0dd497c340796c91cf174600f509ef9e5576d9a0e75cefd60a47734ee1737b70f16a9e0fc9a0d48383e942ce86077f20386eecb4b090e994cb63be40290c938622a6ce01296eb5b5141b1802db1d1d9c81994bddd169ab66a96e3829a9a205252ca4ac34a1ebc4d2eb8b3054b4e1c1cb8192f321e0b8983dc0bdce0b79f51f992e5a178ac2175b7adb0db7959b158e9da96fee18dd11f77483fb061a2ad805d7910c2885253bf46eee7d9e89ecb77aa68f318cfcbf939b427a283222d1d09f4fdd55d737df2afbb21ba4fa2c8cbb97583102b9583bf7d47947cf1921c969d08e5504a6a566af2c90088ab775ebecaec27f95c31d003858268fbf0f4743b096f53a90ad6dd2c8cc003200206f26467ed9c6473a7ec7f75fa7c95b879184ab9554ae50c3730ae51ebb65b013ae564346bb2d17a514f4e06344dac4e877537d846a279a85b95337207dc91d7da493d1a1858a91f289e740bc4dc8ae5e661d3cd858d408c4d39c7f1808f11baf3d9c1092eb4b6a1bf4c31a2575b3080fe8e1405d057e2aa32de40ef1d2e9bcc73af4c09c5cca957d4e16692da478d1e8cf81d575a0d7fa458e5b4bd5ce90e8d32ad994e4c9e83949d907724a9ae6a49b83f6768b40cda6587d49c3d723cc349514a5cc14688cf5b726e7fde91c17d5f967e64833d38808e093234b94c856b7c7b8fe913700872db1088b731544e99aa5e8a1001267a7aa79ab83e04abd091225f5f556d580ced66328d8487309c651830ac17625a5ac316aad68b61f9ce80f934590963105f42f26030bef15af7a0ca97b532164c813051c50b0d466caa174015057c1f454b034b72182735f4dcdd4bb0b8fc4f036a0d3d84c95054c632040dd3ecd25acaffc174b7c7832c6c7cb6cd1642ef49f636d18fbe955fb4171a9fcdf712d4fe605578630f761d317f1d7bc45a6ea3733d30091fd7abaa187816f5543c6a9238a4beefcb2ceac957692d40d3463a14b33f0c88e5aa9e7ce54afe04357f3057b3ac21ecfbad8d83add624dc29ad2a1c19cb06c30743399705229422f0e179ab463b2525e0f4e8e2694ffc049eac3dabcf0727004b54bbe51b48ce65f29404095bb3e9b2615d7141830c03ff75444c96f6882d2895788e2fbc0e2b1714691bc81dece9e9ec2c17915f7542980ac93af90f232fe4253a3a31a7e2999b1f157ae057bec815277fcd0974e79762799b92cba1b561fb2452beed21887dc0872160564510b395ee1bd7baf6784ae09abbe1dc8856e0e6a6faf1b622051d5e06ef73715f02a66bf38c7f9c150fefe49e8416c5a881c6a1a0a6fa3201b3ed002501167ba5dd4aa5036a04965ff24b6c3409672a6bbd0c52611f918e8abcd7e095d3f6207f718f7435b6e68aea6b702e56773acd800fe9bfc75a7358dd71e170b330d8dc57bd5de59b460645a894d671b7c02810f89230eed22c53ec55722d1a7b3c6223511ccf4f073687851885afb2c0e632a4b9f19fd2d4ad5eb98546127b60fb928ccc96c7bc8da937e5d2b27fc59ae96f5020892ab21f9a328b74bd41b76b46ee9400e0bad8ce9e22f02419a7571ccc7e8970c990b9a22339ecb1cabb07c220941747ab92db0ad6fd94ad9021cb782e4a4f2943602afba9a062ea38dc0b98509f130969687e1d0c35c7e20bb170bd1c50d96e033d847b47b2e781c83f7a6e817cfc24b9d49e522c05d88174548364a7a8b626d2a5291111d9d6bcf82318bd146dde5f5bacadf691383194a5785d7c3a817979aab2a0ac64fec7b2a42db78b2f4be64e7503acfc022a5112755a305a594d07b561824d6484300d6e3945b531313f219390c7ac8a2e9c4a7c107841dac0c81e9d4ad6021a40715d1ba9c70b08b270ce1d951fc80b60822a975d4a6f72855c77fcf35698702df5982c59696d6fb9b7dc5bca94a40cd9061c078f071c4ce79cba9fdf7df73592961751449c193d7d17a2ef1fe5b6fb8535ebe8e8b88e89ea88602783392611414db6b16394f7e792e944bf5c3557b373a8904ee7cc0fe70cca7192f324d31c89e303be9ec7b4baa15c0da9446e11938dce914c75563c8d0f6990b21abc0d360b49d6a13f60cf33312dc9f4a5924cdf0582ff2693ebe8d07c3fb902baf765e73a39df393a71a67bfa3d8438f3813813845c4454e92022a6bca73f57128623912ee84f247209eef5e4ca07efaf77efd5481846c234ed741ffbfb139feeb3327da739dd8f0730dfbf9f11d41b6921514f1d07fcd08fd4f0a1dbd0d0e2519f5cdd0f04ff6bf856abd5ca517786f461f6288fc22272310aaecc8e4ffea15ca1e0930cd22045f9cc3f1ec08c4247ef0388fb189d8fc99c6167752ac67c7fde9fd787fbf3e30c799c780033f7dc6f8e44c2a01f7d80904c0d2645a6d67b4da60f6b98f8be87654763fee4fbf580c9378ff028cb5924c6d41fe511160d89f208774cba3cc29c8bcb236c7d88cd23bc016d798435255a1ee1da35358f70f624cb238cbdb03cc21487e6119e9226c65439c2de23c6d48fd13b78fe60624cfd893b8fb0ccf1bb61e21731a69fc338e4fe4b2995b363fa316c575a62df1110624209dddddd36bcbbbd5d067557277d9ff42411b224abfe6104fac1e38c5e7d4de63ae76b404ef487067b34100d4e6992928a9cb8f2312131ce1c9126c8734a18e4193de8871fa6d7225913c32d5876f48b99676af04787f68329a6a8c2bdb7e3d9d101c1ba819a5bcf767273b3cf757fddfdceadba05411004c1296aadb556aed3ecc6759add3a6dc3b175afcdadba05c10b821dc88153d46dbb95ebb6d7baaf1ac775d65a6badb5d6da6aabadb65afb2fda5a6badb5d676b6b3b6b39df7dd771fb55f8eed756cd677783ca8513256e3a8d3f94e7dbed79087e6b5d65a6b0e1d4e27d775a10be5ecbd893813d4a939bff325dc7b226e93dc5f5ae95bfa987d4a5fdbb6c7366bad95dda72347c5e10db91f87fdec73e8b09dfc7be556dd823c20088253d45a6badf5be8f51be38662e871c431cb2e538efc57decb8c37d0e9dd29e8391b9c72172dd731cc7711cc771cf711ee7719cc779de87a2f73eec1f1c2cb86a6badd6fb745c1cdcf7a9a3b1fd7d2b808d07dcdcb7ede59bcff0ed2d06757678e887a3caf9e9c8e1db4fb556eb6aad5354add66a3beb71bf7d787fc3713f6efbbb611d97fb728406c05173744a47a755ebd8414d6bd92aa65510044110d4b4da794efb536badd1d6fad569ebabc37285810de3c8a103020eb0e075d50e9e1d1d107cddd7656934bd72df71777777f71771069ba194526db3580dfbfd902a4ef36fec1984a918265b91524a29a594524a29a594eae8546a6e2f3f0729a594524a492da594d2b718f6f68b5af7d6067dac4883ad067da60ccb7e957e7393456ad5ac7dc7b66ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66dc3366cdbb00d737777777777777777cbda3e3944a3c1f17abd5e2fcc6eaced0bbbef050782200882a317bde5eeb5f65aef1bc7ebe55d4b448c1922ba60d538ea6607fc5b25cb7258d2c8212f1c3126eb559cd96e72f63d449c8936cda2385e4042716855593fdbb6d9ed63d5b46f6eb6d6c97d92c67e72086bdb86d0e078bd5eafba4dec3bf349bd6a03645fb11032f3f099629007d479b17d5166a71a38d38f176062e78d7e0326cbef66292971651109c37f869147bab8619a73d2eab4b38c312d6bb860234ce83a39fb97968f6f85babbbbbbbbbbbbbbbbbbbbbbbbfb77c8fa43aba490a3b2971e8586e4cbf767f99537bd4a6ac5a794505ebe0d9d22bd7c994e792fef4b293bcebefc1e3ab5bdfc1b9dd25e7e0370b48ac5cbdfd1a969e3a59b6b21577d9fc90c08577d9fa4a9ff6534423973f97699df23f0c9a61c938c6027dbc955fcf213d76781ff2936c202fb0d6b6ff1f61cb6df75178fdec3f749d82b61944fc1a55f81535e05af781356f9156c7a16bcf28e59fe84fd5b30965f3e46bb6ecb87a6fc583a6954f6f2d2e52c3e9bdff285a6ec94f517d6137e7999594a4adcfa2d99cb209fb2efa14eb560995db00f204e2e2e2e2d3cb87c7f8c6e69f9eec982f24bbf248fab7ec8864ed805b7e0da60c534b7e019fd272c9d3438d460f62c583e6930fb159cbd297b15dc2b70a7e0a629e146c1ddea220d66efe1ae699bbee923b85d8d9365afe1d6c1ada477702f6914930633ed3d67566efed43e9459ab1b0fad524abf8d7067df12156ed8af9cbd6c1f2972f6dd43bdb341bffbd5d3a9c803a532baeb98ba5485cddeee1055269e1d395a45fa7803cf4ef623013987185101314565b60344183d73dfb48d7d1bb1ed6b915cbfabcf89b66db3b3c3f699368848d83e2bf725f3e65fa1efd038cb069896b0e44c1c04df64dabef0d60fa6729b8eae06c7cd69c3e7ba5ef1990fcd9cbd672188f201a24ff41bce3e91e37003abec1fc3379c891e7f2602c84aa7868615e40343ac648000888c0c8735339165894992213ad9234f8e3d34182900a66812d4a4495093264d82b290852ac4295b147f8831f4dbb9298bccd8a2f8bd28ec40a6314754a1c4b47049f11cba389f24c2b26249c2ede2baae5be29efb8cbc473485cb43491fca27f2b62397489e381379e0615f0899453fbfadd0548830ee8f0769154ace7d52aded6c58120945341467264da7ba6fd18f9abbb7530a71e6c7cd508830fce5ce8d33ddf53e4512e1ceb75d4d6002089ec5d28e76481e499483cb3591c961fb70c10240729783d71361bbbc5d3345763ad04cc6755d7e3d1957750e228afe0b8a27789ad819824e0f32fdf005126baa58e676b5cbb3f7b4eb469d8deebdeb3acf1c963fc418bc6362deed58d1fd567e54fd459ffdee8e4b2b64fa8db18f9e7f3f5cf47d4cee435371b436db142ffa8eef4c9a9c77996e2c9e61df7f4a24c826a20bef67a72687822590a3845c38f64826bd37b3444213d1055dd2bd8388a2df3ddd8476820920d8969cae062a8b5c37943b9e0dd9a1a1ebd2447043dff1913b3baf9d1d70c7df77620cfd13c7b98ee7b8ca8b348abecd914c713275154417f43bec173b91fe69201674132884bc0f610a43a6dfa28e06cda2af7da2dc19e2feead4ccf43583fc1393931bd43e5902d126d9b3e1bdcf9fc883f7cd06bdfaa2fe85b2d5eda06fb1cc1c96d9c33b1aac14a08142c84e18b8205b31867ab659d716851ec8f6eb9f18e323ba027d8f22aae490a7e5d05f71a67bd24aee9a307b7f39e43df5a8e7bd1d6a247eccecbd8f51f6bef8223cf3f4af0d7acfa37a1f8f9e4f2be2d23f798de36a57833b35f5e6ba4c39bfd32e97cbd5aeedaf4321a2e873eeba54a859b85498c890a90d39f41714fef29e18237aad882bfa52d977b24f7a7ab21f0f6026799fbf1acca1382e222cc8f445520599facb553ba24f75729f98827cf479c9f658a2221d0dd2ef2c95c499eda3a17d42c85c7f7ce6dca109ab4c672f7979df8e60ccddbdc6dddddddddddddddddddddddde58bd62e2d27961593ca8a94120ac9bb2351c7d9adbedb983eb1173cbfe26cc346589c308b0b6ef9c62eafe1e63a1a58adb789340d1a57b178fadd93b3ed9c86e78d74f2f2a1a4c21fdbd9f10184ed6cbcbcffd6f1f0f2fef2aebd68dacb238dd348329ded443a555ff06c916c17652f98e623ae06e9bb60d982e5094b2458ae9854566099822513f96ab047fa0005093979d2ab0ed2a821f2060b4de3347547833f7cd7962c3944deb05c3407f9a653d1156762a6dfac4d436fea777fed9ad59a615a966935c37cc5d22b60d0a40a4daa40a5fc62d34929362746a51238b8fd56fa7c14a62762133844954c0dcfa10758937377daafd852c8f44399a8ea1c478de026bbc83189085eb672d575262bce20a2277d44f9c8f2f1f4b1e523cc47978f29161273a9b54e592631e9524a293bc7b14cabd734c36575ea53b5788a65453aa54d1b6e7b2bf8e8e894c8fecb1db904c645da68ad1bcad50cdfafbcf6cdfa4d2997c499154ec49992cb9d4cbdbe0cd252ae56335b7d9eca9bde34f10c4fefd3efe9b792ee25d953ad7bead33fb663667134a516f5e69c7168cef0f7fbf9a54eadbca7b0d746cf7d0e3114d029949fbf43a7587e7e0f9d3afdfc1b9d6a81f9976ff1f377740aff9c1eed6c4c6f4e6f7a2b5779adf79a4ccfc22edf02778ea32876f9160cf327dcf22cf8f42898e54918e52d26bd08dbdfb0e839bcfd68f41aee3ec31af62bd82ffe2a9ee14db87e0bdc481ad52c9347f30b0b6ff1317a051b69f1cd72955c61d887b2fff0ca68f49bc5a22761fb9505a3fc09b3fc0cb87e0b3e3d0c6ef916d8851cf53dc67ff1ca0b66f133e0fb2ef8e561b0cb7fd85b50ef8786f9e6cff0857762bc83057ec12eb805df6870fe09f7d0e07c16bc4383f3513009c7c801bfb841c60610a6c139e705b114253af576cec58d64ae4c2a4f7fb2e2cc8aa73f85c499d2d397b28848444c995e4029bd2feff4e6f42ae679d8f4aa099f3c157cf27e3ef6d6c3278fbeef34a8644983dd7bdd7b45b09ba01aef7126ac253ac4acc92072e53df65809fb0002c33a1fbcefba1ade7772a57d9e4492d33d8833ded3ea79dfbd8647de63d87b6f9bf3bcc7de5927efb527c1fd31b5cf913062aed3867c2490e1267b3922810c44b2f73327cfef46d0baefdd9f4282324c8e49843cc9b672aea45560d04f0fea4367b204f46aa0018579dafb0022736f3d8cfd08cb2092e57d9ce179dff4f0c91be193e73d9847df7923efeb8af92bb0cc29388859d2de753cef3dcff3b0d14f67352884fcce87386b7af75226266751671920aa643c4064c24b6573b5f2cffbee57430ee5ca769cc80a37eac8fbf465901513fa31d207c2cd9187c9a15cb532a593e67e8c992b99679cd1a20e3ee23ac76bed1c4dc3220d771ae6346c35bc6958d370d570a6614cc354d37a68325bd20d3ddf84e804866559668018dfeb6858140e05c5c6997dee3599fe419036927f99785ad5ac9fab0dca7cc38b194ebf99698e3195319e574215fe09cec2064b08622d1124854f17a8f00206184eb860c40b45a67015f1f372a1228ec1880e7810e40b31306204533c13406090052ce0dc60080a1c90610a3c5450a208424354e002142c21850b6292259a50e288248a900212f844f1391940c10c7a3084263c31849e9682cb0b5e2f3a28c2850c404e30051dd0c0c6064a3042838ec213714827db11d7a91f33f3781d996e01120001105860851218010a43c8f04fc651920439464b28e4100432640b6078094902178c90420c839024987821e182127064e8e418ed0109a4bb5bc0220a2ea088214884fc771510535ce108125960020a353232382d542419c204257c694812920cd989a28d88d1aab90221b050f23f3865734e209ce03486960d5e77e698a4a5c4ea6e392669bd2087375fac75840854629d2e206889a00b77340624b4604449e772536031c4e59cd0d2e4de22b4f45c151c548144122c7a902881cb1297cb31091244bcfc5c9b6392140c3591022c244981cf08b63ce79c1fde92c9a507979484962e5c941f38b92b49bc80e0d61c93a460052d65b8a51c93a420042c4e70bb30bcbce0764153b82b9ce042849b92639223861074bb1c931c8193a74d72840bb22cc724472071841239bcaf2db1f5bb7ef5639e3feb739d0a658a47f6f5fbb152dddddd3f1f188b080cd73f7eecaa0f99b3b7d9176b267fe6fad9c7f02ab3cf07f619919fefc04698f073f330823df7337733e6c434e8dbb79c7dad6aef63abdf8f953cfa70bea66ddac87eccddcbdf5eb3cfd99aeb5bcad5ecbac21867e69d281f027165ee7ef4deda683b9c59f30d7bae9d6bdbdfb62fce689bf623202edcbd0ffa2106f2fc1867b6efc78a86678d9a7dd0b7000fedeff3d03e763d00999f8f52fd7cd0afdf7dd57ee29a4bdf771fa32bd63e1fdb67a444834b7af9140577df03882bdf27e1fb318b3e763ac8558e31667ef7230cc495bbb72c34f0e062df7d5833f65de54090588510a4b005308c810c65a84246e989903258990c10009151fa4efb1a58b5d1d5c82ae55429d8a7e02036e0e1bfcddffc354cc30feef6b28a1c6a6fb32aaaa8220cb97e7735b4af5fb87d613552faee8cb05e46e9f340e4916183538d996d7cc08d3a5678e5b00aaf961f130881e40a645908f721678d9ae777e2a1fefcea03c5be707ea4e6766064c3704b6945054e0ce34fa6df12b336d787a802e30e3562ca45a68fd11d0210552fc20264fa5e0c6e1877462db8a109fb4e35ec57dd3b4e33b255dcb17b871e9e74993e065c6a1ae02428d31fa04c5d5408c9b60b4dc8617c92a9c4c217981135b9730f4ab0e97cc08ca8c9f5adec6cc46fef683c86e18c1a617d9461c3097b99b10fa4822e0141142c433726194293b150084cd2549d2bccb732ff200872f50747cd380459079025488425bcb27c2b7110334fda8006a7cbc498b923c6ccd711744399f940f4e0f1428108a345ba983fff07937f3271c61bb4b165f67cc9f3693022a40862e652839811ae94373488bd8d7d630c06fec5ae0dd7c11d31067b1d3f37bc197b9f4ef5632f8960cfa3070857c57014f6d8f7a040849143bac01efb1fe24c7d0c08c3de562c63b82a072acbc776b2c4c218858c6552568e0537ce78cd14838d302195f2023a9b4e12b363ec8ff3d24db39bf6b560c921e1f6abac670f2257d837eeace14a893438e3023a9b4e129b0e6dd55a737b602e4aa441ff6891069da6417f5a436de80d116e486926b5697db4a6c129dc90baa88be2a09ae62929eb54f3fc9c3853b956d97728c26a93aba4d9ca9f78ea6444b0496c3a32033c214bcd8df3e9377129c62faa2a96d5ef363e0257cad3d664c7345bc9d520db961e7b8a56624c471d2770b2632dcb6e8629c6f47b4bdcd913095094c0e6091b1bfada6fefdbfb97c195fd6643cc9aa669efe3dafb5777b42ac6a0eceef4eb6cd04fd35cf35595186894fcec83695022c9e92091e3907d00d91f8c33314465ff8b4758d4841bfa4de637aeea55b5379ab05fad225dc50fe673e430c52fbcdb17a3ca6f3e9806fd2b35b83dfd5ae86ba006fde94a6adf568a1ccd9d2f6ab555963f7d629e3f930a71a67bf91df631c27058bb6fee70d84eb43c4bf0673b5510637676e8b7b3246b79bda66d6bb52b6947e23859ced77c899870e37c89fec655a2cfa54b972e258fc9d3a05cd2e04e6b67a509b9d56ffc6689a9e5969cf3230d063125922cbf672bc6481efdea14edf1e994e5e1bf752ad22171067bf9d48838c3bd7ceb3e4699fb88c3ee0be564f9e0bf7138f49fac06e596656f2dfeace12024d68a342869f01cf29906d1399c2d15c4991548155d515612e1a4c1d4018d5ead5c35a38828f95264b96ac28590b6e62bc6c817c5e0fac71cd252b6e954385979156782206990a5936ffce4eef22334b3358b6cff30a6965bda8edc38938731b594b8a006720feb267fae82c8ac35e4f2252804149a6ece4fafb6cd6967836edbcbbc7d3cfc94fdf6356fb2c877ca36993b88dce42667e86890fa2a87d3b36ffbec66d4669f06c618ff0674ea54a3e6fe1e59fee179fb6e1cb28a3ca375cf06ee7b258b341844489c914c34abc3df60ec3e6c189d235bf2a59238635fd66b7ad06fb60fab8c9b94b2255d484e6e3eda6e3411a2e7001019a2a738042261f8105d6c2f9590b7e7beb02dee01882bf3f8e1d98806441fb3df089fe1207470c978850e464a3a159a4639b58e72b81c97ac3971fb5d5ae84d9c41f12f7d2f11c14e6d2e911572c3e5d8124e79894bbfb93a05b32d11533544b0371f44315821299b0f72db0fad90949c18c37dc8e5e4ac008a31fd304c6ef7a39c58038c941f06400c2ba9847465f985f729e90b691052846f463941c8d087d87038733044ae5c0eed4159d6e42a7aa4513892de90726e483b2552ce6db05fa5242d0a5ebc7871a301386ef0b4a63d99462c10aeb21fe8f409fd7e1ca24a0e1151fdadbdc82fe24c47b53fc917b1722862e50c032b9fd99d2d262fdc1bf3e795a11fde6c0a7f44b1cf7e843bcc590ccb80495cec79c8ac90b1ffe42ac6487123cd366a3894a138bca1f5903162df8656d9c70d6854d70cbaaacae8ecbb21699ec8dcd0a0d47aa362d42c3fb8ae92328c682f9f5c05b44afbfe1846b4efa6d1651007178b021014bc906d733668dfedcd0120b48fedf5b0c40906786e34cfb5bfddc83cfc7b58bcf917cacca0c175bff6909961c3fcae70dfe77b72c35f61444e50b80f06223952912311a8409263d79fd0951ad88255811d8d4e70e3c79ef17bd54de4863753ca5dac66dd0cfbb6d6aa6933e6db6ddbacc5388ee3baae13c518e37d3b1a89de5e6eeb2a0a6ef4194914cbaab691628c315ad98946d75b1169326f58664b39ca5d6e84822bdf7a9d0f2b049f2ab2109107538e3a36324588fc901c3f08a54e727c1ac4f2f12910508eab56503ebeb75c65faf85ee32a95ef3144b0df0431d543434c1ee43e59c5efa056f9cae3f7ab7bdaa77f1ac87952be4479dc67461f7f6e1f715ce13865c4711b674b5ce51c933afa7384fa903eacde8757241377b695a3344d037ac5b8c2619a8625e6b84945482d8da5caecd8b74e3121d934d8cf22b72802c85577283707544b5886528c1b718e67a63437cb34addb31cd26ccf1a8b0fc0e4d83445a5c11ae099bf61a6f8d3ebc475837c5c7513f8dda3eea9355bb559cd1d7dd2ffb5557353dc9b1cd225c0a2a0939197a4243d221b51c95e3aab1211da9285cc5259fd24f29a8245472d2a87e22de22b5482ed20da9452a426a71399680504cda86659e72247346e11c77d5d53220d89476c591c156188bfa502021934f8ce1388ee37ea440d2931bd45e8e1a12f2f9096a31e59a51cd6edd9fa89bc1bda8d3415652cda66123286f9b4b4991f9c4391e65d3abc6e6864681f326b72f2f2fdcbd19cfd8302c894d67c6c739f6b2c55c4d83cdbde0383087da4bd32a90f6d25ed5a7de3478c4859313a44a112b123a4485a88f467d546cff0cedbfd361661f981b9de086d4470753108e662a35d856495ffffc88beae3f6efbb81b56901c521c0dd32354892cbf6b10aa24911dd582738cb2826dc0802883a56a4444505bb5d66a692d951a96a3fa45b8e2744e45527550dc8a5cbfea6ab9aa2a31cd4f6b612c3a84ad2810a63e1a90e6c372150dea2ae42a4dc851ab4e695144908898d27a22d84e72bf6bac97d0c532773474982866540002d0cda0cff2846b8300b88ab91a9b23987335d8388deaf7d5ca949538cd0ba56c587bf1b88a2725673368017bed05a384ce06630c4b8ccb1883f2c546316995e994ad0bce314bdeb01194a7d8080b0be569045c47c9708e5732c546507e7ed5515225db2cc338529106358da5e6621fc34935da8b08439dd230ee06ce71295b11477f465c0b1ca4749d0e356b9ff68a31fd2d72704352abe52a4fc851fdde505d5d96894413d44141721051476385eb86a416a9857d6165c134c751f2517074ab4221a5acc0811b99e08e4e60eab04fa586bef68a33dc8a13951aeda552a3bd546ab4974a8dca8a904a0d07f4d32a144c6b39aabfa6e7e6c71594e34427f7effc683c3548a58606692d577147381cad8643a2b538259a8b5ba2b538269a0ed7a3ed703f1a4f86b557832cdc4dcd4edd8cecd35e5935719b660bc089b0cc26b82d1df6915a284fb2edfbb597d6d32a949b23ad52a969951a9ccb2131714a96ece47efb851c93dc3d2a2fee47fba9715529a851fd252737a5273748ce10951a222a3b294578728a4dee0f555e4a7248b279e968a864ec2b01b5f0011630fa9afd56c9289854a451fd2cba19da47825172c31ed485d16cbf5b32e1c03916658a8d9c7e9e9e3269d58aef3f61951a5c7d70ad71f24286fa501f134e3d52937d56301542c174c8843915ccf938aaa90f10c71363fa2be65acd15e16a389b3168dc13d157eee4834c3fab55ff0aacbdb056c21a90a3fabfa361cadcc7dda03c0a1948addc1c50de380e0bda0a729308436b4917402a36ad42f97e951a14acf534aa7f866e06fdb4d78a19047142aeea1367705ac51d69547f4fcfcf4f50909327fd2c3a1a2979d67c5be63559727fb1d66a942cbfc581dd8cfa60a7c3ccdad3d0e99092edc7dd68e1366d01e57e0b76353a6b8fe12c6bf437dccad2d4aa18abd4b4cd520506e758b339708e6bc6f03c00e738cb58e6629c69b92311a9e3eca6b108c04502bca52733000214e00603b4885834205e5ba0062b2bb8e1c7f0c5a458a0eeee94624e29362526f3a4ee0f4e0c9b946219c3308c62189d18c5e8779ade848479cf5ccbff9429dacdc0e494fe4e1b463e58eada322dff206dfa8539487fe18fe1172f029b06a8940c179645ec10d10c00000000e314000030140885c462d17028d01355b40f14800d889a4670549c89b324c8611043c618420c20000000040000044686030038e9a79673ed379b84bc61c0f8b408651b0301048105e2e98fa46fb35fcd229a6cbf572c0a942f0da41d82a02b8b977dcc6c4ac665a0a0e342f4710835f1f6feb1deba0cd1e7c4d7333ab9bfe4da659599425615583da3d297d0107859f3ccdd33462f63a43f920fd59a871dc228963b67cef06ca60882e5f6438e09d0f218d1b6cbaa7a6ddfe176a8ea382cb91ac6332768c9675ed693dca76d2bac85f40f5e8d152609abda3c646e999cd5cd55a64bb39a10f04d987477af93c5c284e0b13a0149f01ea33431ecd1c72e3b2280396de6f2d4fbc8885fd4da71ceec13c560a4c9a793ea70875cf32011787fe8c41b6dec1343498d047307b7654908202d515e2349474bc9056e7568a3a51e3b036a9875ce2370b4447f4daaa7f0cf9a1068224e0b86a117eced5ab3611f76512ddb5fb1a05df81e55529b3e97804b889f58565d024939ee624b3a0854c2c1d533984f4914e020983a0bd4b83824b5b0019c90cccbd950a330e4b9b35fef45b5dd658add3796b73d1a732cb04fddfc76a541bb1254f162a2b90cdcbe05bfefda9b156ff5da35cd4e5186f8f7d9f10ccb2ea7fc205c055405b3291d38f6889a7e759426098a62d6dc8618f1b63c9e863fd660513d9e2b1cee7d43ba6c37d583b195435a1270ff4f390fea69d08f1a2eb599e746fc260f6701e6d5813279d40fed90a916cbd941b5904d1fd170b1378464b1fc955417cc61392b32bf5337d6281f66c1c99a052eac6794c26b600e536072eec653441ed7da7af221fb58b11c27c0be130d21128eb1eb11128fb3bbba8a7536ca879a81a5975ffb2fe59929d80c2df8c21dbda28fa47c5348493d53fa7b896a187fd937a820d2128d7a165143d03d3a294bf5bafce7539f0237074e224d4925a182a4f745212123d39789f6d3f3bde6a714bea6c22a2c5d4d3845de8d0831e1b89a3ca345730b2ed050a2c162282a269dd511d0342791f0d531c6a74addcad7c82739a81b132d2f0963e270b905cb0b132526932929a72e73811ff03469e4400ec988887902bb89d6c29b6cd228542d7fca18519000373d7a291e166e235fcc8e45295500b485cdd404b49f344cd701fe74e7711ad172dca022c78201f8cbf03c42894aa3d2d55e53692f3fa7fd316916060c912901675771bfab87b39e87e2c298ba4690b1e8de03544630a195135afee58208fd53a540a132614277802b0bb1d7aec791efd0ca9c7881c17096c68ef1b18445d64b92c4e8d332b580b9d2ba0c9c5e9f002a21a35c9209c5ca8458dca0222c0c5221bbe043a598dc438f594086352c6a19890314485ce13ded7382c36fc7fc4f7702a32d45a0056999e254f0bf11f2328fcdefea256a90b7d60f99debfa9663036bbedfea862cf523096dec6c30ba28ac3c7c942ac1c1f82e1530e7bdbc3cf9801f799a358afb10c740ef33d6cd90e2c7772d6e394a54d7814fba2c505ed1bffb1721eb2cd2c914e517202b109ac44062327886b5c1a0c971069092b55c3e06e13fd8458da4b65a1db8c927f3bafa29e3313cfbe2a3c065e4ab87fa6a7336058301b20544af9412945765ac08278aeb85ca165b401df0a11f650ead212e88a2b342c461307364246fdef3c557f689640e03a95c81d05044f28a04610d2ef654a083ddd12f43f835669703d68b511a0b99bcfbf0a3d032484096d8a584d104fa414eea72bf143191d1b46985a5db038da83fd3d26261b170605dbb95abe96950338558065bc983d4fcec77e934cbd09f098eeab1ac62fa56dd826eebf0b8b56859d6643fcaa88ea6b93caf52dfb1131a1443b610682dd5206715ae576f3b7c88c8cfbf9bc973dbb564b4ee534225e7321023e9b8b2a74d510d29eb565c2ac483d5c552dab413357add755d40a36e75512213c9ff6aee02bda61df985ea212a0e747299cf2a3ac76d596ba34bcf87971f17903b992015f573100c3d10ccaa733ebd22ecfcecef788d39a9367cbd9e44efec68272f2ca0ef2320c6f6ae1b97c4128b77c88520df9380d9d8f6b9a2966f4c73ab62719bcac06e55ce8bb8ceba8c38ce6fa5e93cf67a6fdea23e4c9a159c0a0257955599eae22516963a9c977efe99a6c6bde4656c218ebd72c83dfa1b5e0a50e9bda1297d6db5aab2bee449dc99476d88627b9e64e509d9813aa8b31558188ca75d58fcaa8a49f46e3167825a2c5f5ce48f638230172aa44b7e00e89f2427810c8db3752b9e49cc5bdad227d5f2a225475851be063542f042688dd487e3d2d51535fdb41d1368f41e74b8fc6974ab3282461329d46cf01291f49932c63fa9b970780cc41dd47bac971b775182385226e146c621c3e8dbcff37582e2aeb37854bfe93fe06a1ea8208096c76b56b03c5da15ab96e7c60f7ea3dfbfd50b5853fdcc5a726ca7aabe5c056ffd35768a731ea1068f9d9dc68125edae6c24a6cc0c2f0a7d863dd7e913da23d90174580ec89f00e9f668dd8bffca37c9372eeb859140f03e8145e2ab0f9039489e3c0450e424c078c793e621fab94c3e58288d954bf0362ef8979c2406d6a07cbc3b6f75c9192cbc21cffc4ef11328574256e1876967a083a1c5c60dafc55c1fd24ce861084f694d6e8ac78306f5e33fc0658077197ed76aaa25414911acc8119b4d23cca209840aba63294b4c5e2974ac5442394ed353d52ea13ccd3267bd681503782f47893250100d5837d8009604fbcaecf51a7c3c47fbe369c0c2bc7050b78c3807e8007aa889876b359e5b3c0cc69da72900980321d4ba2f231e7fcf5cb321870d04dde470697eb08c4233480197bee084ec70175aaf0de6bccf7b0e0c3147b2f81c1a96935c98c5e48811c2680a163a5f2b25b7abdc4acf8986b6db134b9d670a02bcfe203134cdfb3d361032b1930f999aa3fb762c46bcafaab95b50e1775cc5eb0dd54dcb60e13406fcfe0b7e062721a9441e503ae197fa3c1262ce751560e89f4d6f37789c90429101cc4c772ba1b563585d95ced93e9c10e04c06de5df2947b1c2d09d3f659337390d111977ff970d801d56cc515f22641651433330854bf36b65b4036a2005acd18aa348094cace169cf9a5d951282595fcab4bd59b2b08d232b6999fb5ca2d7be494fd1d04baddd76a33438fbc31c58c5056c6bc2b41faa7d262a28b615c606812edb3c215f4b56614143ef3ca195d2075678495eee6a50243055e7a20a417efa88cd9b4858e2068974b40824c910db676f83e9bd20f399a89444b1fd8d899a88440894c641ed64eeb8d564044107e6622c02ab17287dcf4a621840ae0f101108635413e3af4e24960a50560acd8af01515dd76ba3042c8c9bb2cd67161a9e88b7ddb227a693202ba0581ab2ec30df3812de1dcceecb9a0fb569a672960ba4305b10b4ceb0fc95b74f93a4f4b0b0c2b7420ddd96740cb311375f59ccd56e1d42249849b06cbe91267c18d4fce1b8ef65df413b8749a0f31a977266eeef9209316c5d6ec8904069421894a9e8ccb61acb04eca69d15cc64f8416633aefcb81a591eaeb1da932cfc057bd9022023144364d10f50a2c917a8df03ab7223d9a0e615d76f9d1d5059579dcf9f747009d64d4003985349a080f0fcf03fad20508c8b721e32cf33220aed002742b112341434d3e8551d594f9ed8c38b96df4daa0692a39e64d9b6c038c14e932ab1595a73ee8bcee998481d890c267bd160ac5d0ff3fb1306e7c5ba538dfbb12f462ad4bc8ad0cf8027f49cc5455fd1a96837eb773c0ea128d80c01689a32096fc546a4c0823d05320cd9b1a6ead069161583287c0cce3f21a24f72493036b4af4b979609d5040d7a9990e82d306cd06e3ae61a8ec7f60eca1f3412bbfbf9a73d8c487e06af8f0a6b2379cd9c25f7e57957132fde60ca00081ebd4f5d22971bd8b3c88cb90e09e03af136c24b91a5ac2fc5551b9f449897f0db084a023ea4f9847a90eb364d9739843643eb0604047b4ce949c14b0e909b026257ec9c485534fcf8b159a4876c143b50f7a2da2d45aa2bf2740163a60ac1a537bc8c84161007c35c0265da97029e165f319120b79a8df3e0134358f2e7d32e38e622df0dc0c0c1d26ec6536d9b5da9630916e59c91d37b671926c5f4f8238eeb1df7df67bcf1eb7100d0fff3ff4820f2424e4314a55e5130924f05f43dd9c074ce20b5520e6a53092b4423ed69d1ff9c4cd402d8ca4d7a9a481b3a501cfbb60ea9e9ac13ba32fae05b85861de080459c567d6733593f1035667c57325f4000cad5425e33338086d66083e62fd40c693bae982ef8fa7e2eef627e028156860ab24b169586a86be28a4fc2a899793bb474e2cadfcea584d379e8cd0ca46785184caaf2c65c6884ef91a7ceefd9e4fd1e207923b0267ab322e00d48373d4a02fe287cc53f2710c799ffe94ded9ad0eb26eabd60e8eea5e898610b7e087dee323415a864496b110b3c26bb05fbc755e28a2b62fe178381c224e20479982ad153022664e67e6447591896c86f07f784a03cb5c223c4a8117d99401f4e07cb5f08f5526a20b8c1230b2c534199d0ac1ab3285e12c2a9f425abcee01229ab1c4487b6bfd90b8b9f381241e449a0e29f6d0dd9343b5979b7d7a36d2ec5465559e53fac0a0247258f4c5603d0f3ba22e470b7dd19382767895aa1b40519030c53017247999bfb6c0776b534d1fb864c23938ef2ed57b1b57d43080c0991c39d406de262908cd7c966cba1b9eb56d9415d8f3cfbf2e975ac96b5ee1471129cb02d24635b5b8a86a95506e30cd1ef853a823ca5ac570d77c240dcfc09b29290244e097e3feaaf17f970fe873200447c821e554f5e508ea5712b7c526610f56bf63038d9a6a3be7d3ae7e828155c1a87fcb4878453f1ca54206d32950323b62daf67f9cd4de9366256d34abab7d7f35f03def1c4e11b8ce51bd25b24c7ca8dc6ffecc9e8444f44a763eb6d6e1d7332744e236f4183eec4a7c06b3d1ecb724bfedb96f028fa979783991acf093640ac03b2ee392b8bb753681e360a1aef6a3c5232365075836c2ee451d5bea3b5b2ca385d4306b9f165326769af274db0e03a8d2d5e0538596cd3d25f5ff5d8e69c2777259a8e279fe0d86b5b03e8452e2b944d6bde7eaa1d5927a983e26e1b409fe1e95a4be5c699e046deba86732174c6200977f7e119bf37bcbdbdd17bb7739ea213b87f68e29c6164961b471b05434ff2cbc2b2c28c1825a7c15734bfb61461e0fb6e6c6e04a8de4d4634cfde01f4ba7060643a443303fe90376cbaa7a13144990c2bced8d1992552946e17abea5f5706675ccc847a218c20d472178ab8e11bb89430ca640a62ffff536cc103abac2d3d93d1b499cfdd9a39056e094e674f4261c3fdf8719b8af0dae8a2808376fa9c750d7bd9e473c0f7ca88342b05d558eafeb55fdee40664ab8bf7e87f54b5d085c35a4ac367072da6d8a7778187420fe2d5f0a60fdaed08b13a5923ff2a7583bb23d41b49c92f85285259ba1c8ed77ea3a2a64faba1d5aeac2850bbc2a66a75ccff0a90746515e958c3d17bf364461f910f4821b2d1943a7dba9268ea8c973070a752aad3c49972d18e8477703de759687bbcc4805dc4f17efb6308420035c6a45aa5d50a84e84ec5e43db1af37b6b98970c19e5eaaedd5804cf6e8b0bbe3942e4827c9490d640d37b1ecaaaefbbba41b4ebce550c12e5986dab280798d4733c5fcc480694e7a201d3f4651f841091891a501bb1f2248f49ababb1e9a5c8b832d0153348f08d527b3181a3e98397211b9a33e8815906f8864704c951ea8c9ab105eb7748d193c6d12feb788a291b5493c8aaed6bc22fc346ea7aaee8c7c042a23458ca590782fe8e21c20ce1f6028cfb7999158411954cf7c65948fa71ca7a12876b10b44200ecfbe99045f8878599cc31f48627aa48d0e990329bf03abb867f6e2558560ec5ab8e03e8638b7067dbe5f01dd2a9fb6dc613ab878630ff2b21f5dfa98873c7c49728570e4397d56a445666659e002208014f13618f7f896e862bbb8a4a24cb0a6ed4d6e0ec4c1642fa0fd4ea0019d4f84931b26e7a9c32a668aa592091168477d3cc51417050c82f78d5954ba0ec5a3597f401820f6ee108b44fa15dc8c175b946f4723dc508511247af9d064c7b95dc43afab6109a2aec207b0ba1636e86d00389be2143f6ae443ab1e221217ec460971a3db63ce4ceea2053efa67b6e3eeedd416b0951b16deedec2f8935e81dc130d95cb8aec4d2ca9371d9d26cb1054c657662c4bf5d0706effa6e6047247a18107912370ac7320214c3e9cd479ef20b1fb566c692a021a855a400ffa6d0e4fa96622d6e1f993fa46860fc33deee2dbb14ef348a3e0e623e928b5456a057374234bc9c556df72f0edf7ca668f49a2b8e362bd9a5b95699e02e99be3f47b305e50d58371a4e72153d7225cfc77b959725f50b457eef4f56cd787a038c319430d8cecf9a003bd7d1784936a8516e08a4d01b567f65c7d7ba0a11123eff6a0035ddfce0efb92ef934619f9ffcbb135c0b703ea803772b3f382cae9578e6ede8ffef490a3f11e2db3b1cdbbfbf5b832e88b26a0e802b24f9f4729b209ec50fed01b70c0bfb9a70b30cafc3acd3f9a2eb524de4ac89621285584e0fc74cc7c775968dfcabc579b1888111d8b7962eff9d7bb4fd0122da3440749ac5913715773d6c51791fa06eb32ec5f3ac2146b67882ce28d66c90dfc1e0660da61821c07bd0d43dddd8ec6868c52df5856e5e36e0c82c4a1f126b02154b525d79bdce34fe453e90a73a111ad12fce58d15cb9598aa494eb022e5e170e267c4c978f0ec6e677a3e63fab0e10d2fb974b6cc0714029af82e7094c67cb7b76ebf48f0d6eaa09c457872a475beb92f0279a6f37d7a8baf90ea9f1557e8feaa4d2c9ec8fa28f958ddd1fdd331f69983ecd08ffec8bb263193d4e49d714e63d0bae76a52671964683d1b90c5d50087cd61437c8da518ea3fcb1d801533e52b90cad6169f461e075f337012a29ab3503725bbf69ca166e8ec5d9520bb97ceb229408cd932ea78e12be6db28d77602667ba8c45721d465dd82f9ba62f9078f0f56656a0bb3b42d794dfed90d9d4777f627f9a36fa8fa627182fb4096182a96ea5127ef9b6c892e1ef7dfecfe336dd1d589620019770918896b6f6bdcddb27c825dc0cc283a38c4cdd3abf26ae92a6088080d5a941558e9adf36ef5dd3616685c564e79892204b8f6a69c7593c221b6425f4d4a2090edabf0c1b8715ed9216f81b3598eb71fdf94597ce20e61030cd92198c3eefdcf44a2e814d09de2f37543c8a48e70d511b37f13cdefc27206a51b91c180575ed07ccdd2a2442a94624af340010431a56b20e9ea867d51ec32abcbf4289a09f548fb81c1fd0717aa13935573b9e53e70eee9c83c0a371ec359dd4bd69c6090c0abb4ee3224f53305d07a228c8b16fd429083d881b8a6166db5aeac48c5b5c268cfeb0e1a287136ec2b0dfa4855bd5406eca60ce627c0316a6bf327367da6f4e76d97da9fc5f37ba70def7ebab780364d0c776dcc6326a2003b4dbed9aba8220cdb5f4818e020de7b57bde3366de6ac174c8e15671de21fe2adabd76a1075663d495c43fc59042aa5034865ac1e9e3533106b6eccf8953f3b57d84906af652e2ee05b5c867028d7d660042789fbb96eaf035ef11a0b3174ab2359546a12bcf2eadd2987edac16e1e2546cf780c0624c456bf9a192d1c4565df5b0287c85b8e5723cc74eb9e001e89feb577625177e1b27aac14b2ee742ac942ffcb428e39b49e101e554e8ea2806c590e3b7f48705a7dc1eb69fdf0ccb1ca9520b323a7405bb8b0d5f7d673017d0599d09457c693cf76410991012550bb92915b6efd9767a793c4019a7502e08446fd3eb61726a7ee40f15bf6e30e523a3620b220c27cfb61fca4e8bb94cddb3689c1ca3c00d29779cc0d132c7b78dee3c09efbbd2bf867952cda347dae8004d2004eab166e84d80a795b01dd5b43a69181b15ad789b8341e02fcac350121731a8647d07948b8e61cd3c5015ff6a76315e392be50f00c9b3d32feb7cc9965a2cd76a6e685462c720a0c6f4a465154dcacaa4a7066189d0c4b1d49bfea698aac80fe4199f647cbc65be1eb12bb887da0fe2942d3b07cfd2d31ad06bc1045b0ac96e8b8d3659fbd026e6c2fe1514c2b901e22f63269cd9466ff5b2e412ef72a816f36a8d5103da8fa2538dee2b7b88be9a246cc538ae285fd0667e9428f501a6d034cc4b837102145d0ac2988e2afbaa3dc68d5d0dfd2c32cb5d0bf6c1c872ff73b5d1f35caa75c00dbc9d232eb3b788025017718b88fd7bd2145fa785fd3d25c15b54f0ba551b5b24ed28e209280fb418b006125bf4a5a46a09fdb1c9705b4e85b3e10508eba5b936a29d4510fb6178bdd9fea0590cf1e51ea1b86f9b24c0c4028ec7e3af435bbe94197ac0235f9c72e8bc760dda8967da1c734392e25a7aaadd9389c21104bf0179f9aebcc9645c47ac83c309a890764dfdeedf966329a380addd28b2be0c9c4a7e650a8ab138234d9376463dcbddba6c8f47be538c651a5a2ed0ad1832e01054558f5e1452af162907db2e624198b96bb645dec053a388cd1f3f6031dc67eb68a42934dca141ec5842810caef514dabb40102749a7d06786780d28acfedafe71ce9b95e5331806e5fe1aab79953d1a469511ba2fc36dd04bb5e02cb9353321775be0c179a0469a2fd78183292e9b30ea891180e8ae104a39081986c43db55b9edfac6096e4d5cc468f8555de1ac24018b0d053a2a957a2b212ce91ed73e0437963b55704ed1656a99767ea81ffcb0cc82f969d2476b5f574a45e6af6da19b26f9124ae3b9d090385d29b26d6ed920f35068e13c77925d6f7bb7c72821294f9412239fc6159503fffaa51d79038401918548e779d28fc403151803096a898ba0e43065c6a53e28cf590c679c18981ec64f3954693343e7623b2efaee2437f877416eb46530d0dc3caa0762efde181a7ffc671fad96fd84affd2a03658ca1553f7483004f9b891d875d1065b52d5e0394792cb3c94d39050714332323769f5962496fe01e7567d6b858826024446d0cfc6ba158807aaf303810b4a2ae70c91fdf489554c5bc9d1f654090ac233554540cdc41640bd882427756a01b78608b4cefb5930e075485e094cc3e4dbfd50967284bc10e65d933fa635a14593a8956a8e518e23cef9d8cc00af0dc8a63cc60e33ab641648888ac3b47dc875a058424a2d2fc9d5f8c303e7b3ce7f93b44c8fa7818ada0bd3e4818e8bc0731ab1331f99b7053e7de0f4ca8ad331516c6f584aaae903909964944b549230667aa9d43d4a3fafa00621d446c0c439e8d5abde3418ea00418aef90c3febda25da8622bcfb1667163e230f6782d82562582bcc22fe2bd75af44aa547b77115382159ae47bccf6e431042f9dab285a436de5974379f2e9f14dcff21ba905f9638056159e55fa34b4bdbd2b3c69630cf7a96ad50c0d090f9b038a5b5823c3d4ee1b39d8da34f6b65ea0b8a3d4cc4cddcfb3bff226c11c6a8cf76f69f3735cbff51e196271a12a004c85b7009044b4b2729453e083d88631f8bac560ab40e4368a4183ef41daac5d3a7b10c7a574801c8150d7d0ea55535cb1e8cf57a8c61c24b16a8d3ed40171da780bd7f7de0e2ffe610a5dea567e892cb90041f7629fb74eff4698dcf84484c64c403f42a39887fe8d58f1471c3882224c42dcfc9422d291ea140b02557f1e022612ead063d126042f20755e3cbc55a68cdff5ad516b32711a3559c962722f30f9e7aa2cea8be6e42765841975e426502945b7ff3608120a07340a0ffdeb1f4f69960ded5995e99c76922732cc1bd960786fa8cb36f7563dce1afd8f885ddc6c5b858dd22000d3689f4534b3bd4c287cfa45d05ade8cebb5859073760911452f26672236e7f38600e2604714a703e0104b88296b86480e04e2c2125be407282ecffb7e649df0ee2927d905b80da40addeee2450df177ccbfe1a2c32902dc9c7fb5096e58091dd224bc7b4adc4e62d776afae08b1a400ff9daec4ec536c9f19f9a1133bd023fb6cc4519036c763b2a399fe13151da841d410ddb3b5c275ec63e1c6bd7fd7797cb66138dc60784cafed4e35e3b71b8179898c1d55b782904a7697a6698f097ab4d74ea83246794c92c3c7f3a9a0c82f070e73b6e22a248d1daac76ef882674324b0a39ac0bf28e46928cd351fa9538fddf2228f37ce66b279cff44d6644882bddcbbec6c64076ae750db884bbe5f689937e93ec3e0de2d6c162609b25bfce9c1adaea2ff0029b7d4895787672fce844e58b63173cb62917884ef0aed70c2053fa147037749805ac7568fb61cfd0fe681460181eca998ad2ffa5ecc220d4e6e9644d4ee0f1f1641a178edb2a41bbb76ccb4101b775ed3140ef972147f10da015f4baa624c24aac4d4db50d7f1247bbcc1c5ff0f765192b122b451daf63bc75910d9995d6fb5614a18309accf3737c047f38a14c345626d83ce054b509628b5e546a26f000c061faf2d7f2b1000d5288e985dfea1e6ef8524321a984728b1db9890dcceacdd8e2a717d32faee62732bce3f4eafe7faa8f83e13a17d769c7061501e01aa4782720b9862cfc26bc70cbdd2e8dbc98be67da6046e13c384531076b479d2424454c62f79230836492317a4acec936768659bd75676ea0454460136cb4bf9dd0c55cb2cccba515bd283009376babb8ef42e478cb9091b60b4b900e2381a05197f8a49ff20c89a1ea40a3c5bf8d447641d81f6d4ddda510bdc100c95ff4f7bea0cd7e6bb193fd4f021bc7aead0ffb6e1baf93181fe2bc105c06dd3b9027a2e61304e821db621a42091ec7762b3160fc0d13bb73d8f299ccc861aeea3be3af95e49115827ed216ef012cf8dd400836d5e8064389595327a4b56a6cfd32633ae56bbb52b56cad0a9129d9ca4a1246087724b7c100ec763eff2e0780cc4015046f27d6bc38655658aa468c328dce25c1bdffab7db5a2175a209997fc9ca26b58fc42094590606baca81df9581a5fc07bd5ce248d66f2a0d17769cc0ce1e6815892d8f45b594fc67c9d1540444b9706918e76dc93192d0492e7069a5699454ddd80d33f9171bb02915b0ba4779f5c74b0345dec52963911e150c38c83f8f06aca7417b2fe4599db9892a2524734da2c1744ccacbd60f32130021372a173ad470b2cc0e1c4cdd784b659a6dbcab32e5a17674955e58439f1f5674dde43fee4611bdc12c47404f64aafea38d80817ce66fe374d660b72a6d11487166e2c9bbf9ee410424128f12a0beb1b271bbf3cc5f0d11b6853d059ac3517219cb25e574a36674951d3da9f84fd327048c5fb73b05b8b09af90b2290a461e462db7492b83322a93ce45cb944140bd7808244a49fffb36cad2d91e0fe4379bd1665365c68cf565df15ac03098f412aa8804d6d4503ed6186858676ed6b471b90fa1f68643104c68563ce5da9c1a19e2670f263972a20e84128870f143af6f0f9cdeb4c4e4c7c94e25835fbc2b5084402b283b47c9fd321e1fbddd727c6994b7b45e95971f5bc50f8bfc0d979f25818783de68a597b2ef7d8f5683320b7fb0dff82b9f1c0f95023fea048da2605a9a8110364138230edec69fba5083736b223a8a585b0e95e6a2f4125e1164d9ad32f9b2fd142f6d1ee3456c355dac7b11209996887c73015aa1fc9a50d3af32db695bf15b50a8468f863d26479cc19cd9853a8af4a8bba4371a26bd4aacec34df1021a00bef7fdab361ff91d0bf99e29988619a8676f074d29c61ed68ab7cd3ef20709e669cb4c1829561590b9c627b1c4dc443ad6f5094b3c719e191287aa993d99280e0376ea7cd151e3695f2995627cbbd800b344ac232e82197917d615d619c602587a0c17afaae900a218cdac7803b416f60792e8e95cdaeaf5f5b728d1c8c57f1810b27a312e849b6d1567d4dc11286106378e23e84eb016e5ea112ed1538efae327bf50fab5af8e8607d2b3329d6f7747221d14b11c6aa39694fc86444d700f75e0d06a58b5d85965d133c7ffd0a992d5100fa983510464220fbc1ee1a0983f9624f3837b4ad9c2078118a9debecb06969a91077912291fa1b6414f5e44f1ec4a338d153739a9fa1b1cc24173a4889ba50d08dc91684a4106c012ea8b22c12f3facba12e4161f9ab89383509341ff38118a0056d201de19743838362b0e5ac5738cedc6086427561b3216ac5b9a9b165565715387be7cd7cd888833ba95a7974e18ec7436e9b6705b76bc837cbb028f235795f95a4d9c84b8c68f74935cf3d4ba08837df46b2f41e6c917689fd93994202e180f6e70b5db4308d0604755936cd40e71196db472cbc45aa798962796749d89a22090403bb0d166386be5049fd7a38c3128e32c9a1a586bdfd42a44cd7fd86e490863b2e53a52d2a28663ceb7b1fc4aa21c63c7fa8ba7ad39c2214240335bd36b961ec8b7492f761cc8982a0a0541edcbd70de21818b1b5bc74823a1a472f98857e5178ad2acac56326c883aad6a6d29b06381ef3a9ed38b554eeacd9ddb1db5ed6891f4ccef11c02c6d379cf3e112762dff16c45e8db03183f8da5373f6ac638888092ada9a3e818db64e472f3546a54e10b32e021ba97a96f828719c5a8c3665f34fce5642e69ecd9f938c1e7f1c8b26428df3b5f8e3fe5da239863f6671a18e25edab43b3b3dc2720e5b6180ca47350b0928b3bd2426824313ce47ff40cd9c5f1b94ebca42bcc76679137c6c9646ca836808856007923c6ade6545cbf18207dd3b2dc1fee10b44400c9ee39685a88e1ed7054dc32934a1af1762c056296ea7f86a448c29b57f7124d13b746a2d56f27882c023ff2ceb61e723ad9ee68ae7c50e6a355306afc92853e448966f1470926592618f0233b135bb477fafbbd4f37369b630558be2021f372e76426aaa5b0c90fce7d21549d88fd9bde074d30c73de75822941ded0bfc36ffcb4e650a66c82944011177d991b358163b413b45e613c69f0a7309dcc6b81d1c97165ec8a8bb9327178f30463c9ad957bbf3bd32eda7dea60b951748e5aaba7f4760f9313c76ac1440aa45894f6d08d6d008a2a75ffe97b15fd594054e749074706f91238ef9e559341bd9d8ddfb2b62159fa9da6fdff286dedcd6a5a7aed2dd7ee306b9ddd2c7909c0546e91e108e4728ad2443b4e0ff45bdef538ede6f7c09043b1a75d7097a0ce50bf6139e4232bf9a6893b19e1687e0b8a0930c024868f37006c3af5123263ccbe2a311e4bc99350670bd5e89ef426f9152c2b200457f86b6246faa48d0049a4be4d36f46f717b67e8b3cfd2439848d43c9be362ac1f995f8deca7809be3443205e8fbca76920a015436c54d5845a7962e68f34a16af81f5ef9fb12eec1972fb61f2e8ba88593e07c04fa923cb04872db16eb1c13944cfb17ecc40c23a7f8999f874ba0700564d10ecc5f8dc5813c03029b2bd50952955e431fa15fc473729c85183369819e7d78c9e671d901b6cb6c76ea705cab81219dc0c565f19188c60265cc9ba3c4dd7832c2205610aa9213f777b61ebe37c8d292d0ca11a4778a4ff0c9e0bc0ff61f47573d557ac6bb91a738565714805e2f230325230dfba1c6c4aa6de8bc7f21ea163986a631d6051d0ceede56a97986524bbfb90d9d4c181073eac54ad7362b6853c22a90506ae15212ef81b9d346094bb2be711701e763c9a1f863aee03596488687cfcef9a8588637357c78a999cba878c4ca76b989b0bd799a33d345c886af0842113a0dd35d46511d42447a0e7302438cfba989b247395f8428a18ada2c6676ae4da77115e5350667252a1210efbc865f6f40a9aa785a2dfad070e8a4c13508958d2ff034e95f16e9f48a4ee02521ddc2e3119cbe9c04eb45d03937a0167a5ce3d1d78881d2e236c61562f9417d64e92a1acb2cefb838f63757cbc384bb8e520690748d58c1c059d3a16ccb521eae07b10564d01899bc1a2fc2772a0be0470b2a88b9b5a33b3c672ed29cd5597a2855869c00c490c9b510d18c9809ec0a10dfc244b3c233cda83557ca0edcd41561e71ed4eeb602dc1dabaa623aa395280c6e0c99fde914e97923d62367104e40212a3e22c7f2688d9d38f8a69f22144386421b4456d65aa144a4c26766f1c720382c8c03d40a4fe89d4b1b3e6aa60d76eed9c3eba074350f6bd612cf20a4f73c2443c13997583893e29999cbac8ad58d3553209aeb1bcca6f65732fd50dee96c84f48cc9648e0c4bf3777f17bcde68d0ab30d321b31c2e8a82cd8ce2fc15411603cd55abed82f1fa3bf7a6d79f2b10748a1c021d42b2b82431a3844ffd353f9974e8e80d42f7ed9d57cd4d283d1fa1305f75e6c29adc5dafd6e0f249e6a082110744b83e018003e215f7e6093c501bd898b0d65e64778967143057ed3c2281b471d76b1f29c4dac25796d00cf838b6e335cf842d5335ccb525ecef1941d56dc5cbeb5617a787c6302f8081a760cc772156de57deaa6ad40b66fb30ddbb06628822c6d9d38db08abc0a9424cc1ae35df26b92ec458217b7ec604869a0f985635c43ac91d59da86961a9ecf7e9dc60670b0c09e9269ae7036bf17d158d9e3b214a1778fab5f1ea4ecc4354136134c3e71a26670637ff73605b8daaa282e348f61eb62923ae333b11af1bd83f49cfa55588930a4c3e42cf62eb24571d0fe9f9b6631ba111a15815af9823df3be2e7ae75b1555a3b7a13092ac21ae0f554b459fa14ba382bc326a9a30d7aca4609f46da1c2ec19ab07adae77787cf7f0f89ec363b850a8512f174bbbba0f0dc92fe04cead05473e0449055b1d8b44b6f24deff6b66f175dcad1ec64a8ecb605dd1d672e040b676051e94828cb87ffd9fde05cf0b7025f882a8c4954a09976537f75b25c3606e6389e6570097af072cb63321db7207d8a3f559de99310c5ae317bbeeac7a42a51c13a84f4fa0e92ea566c8d92b4c1f227b46f5e4c172fc663ebc490cc195ca79f7aff6037bf7acfe4e21b34034ffbc59082fb6dc52249759b7ab57c4b3ca4580d519313a10ecb897f7ee4a69c9a8356b8d6003865c6df97c95c023acb76b17ceb42fbf1d74a72a7932b65ae38f9af0418bf0b975e1e73c08b4b1411607bd1c5d5f63b109a848b709105ecc233404352d788bacd76338d5b9329f2ea77b99c15d2ebc7c856677d8f907fea88728414f5e0da58edf5e76c733acd8aca27b8a0ab9ca42a4177e852b282d8ca63b69c14cd543b3fb703f913577432acfa710325f472797c1bd46a58734c55262030a6170b1aa7bf50218ccd9668346b11974eac4697a4eb7c9000e399d3f3a9703e89e314945320bcc6fdd096a2377c56e479cfafc6c0e56a8b4bd3f61156789362d614022f5d5c5ed80e5515525814512ad406e71ee94ff13cd10bcd5a6d618ec3acd82ac21e77df4e23b099ed1e7aeb1e242063de8b7fd4c184e9dbb81ebc7bf51c92b5c363c22d20407dc9b22839c741fc829d768661ec47a2a6516ca3e8aae0f603c595cbec16fb9a3c769a4556f3d8f0400ed3cde0fbb3e0d33456534831dfc5d454f15d42c3f23261d5c4c10a48b2839a9ce3a32f9db3b32dcb652211fa2052da6e2ce3de85785935b2611d63ae550e8ee7306dcda4b1c750e5e974800be02b0c79a2e3826a45af2975420a0a0248ca1375c302420f55593020b10b207b470fb06c2af079e14f9b8f566885b4f10fd1575674a9fa06daf73d1da8ea75933551a05b6c17c5e330958f1a7840ba4a50104e34099a4332d2abda671fbbb5ef7e76e6a617b7c4ac676465a9c3e388e6abb3f40cd36c080ff39a8691eda22bc6670d71eb9b20a09b61a39084d7cd444f784a3beb6d70055eb97ef582a51a578295aea812e1d4068d3677f0ef775227e33617ec10d56004a33655244b722562d0f961fc2c01b4287b59591670da87cd8f20b75f4ded484e86c8795fd609674d6c8d1a16b10425d63696b32e174921e9ea1522d1e83f1dcc886d2213a895df8b9fa41d8225f2caedd557f0fe069cd71e533decfd80f710fcc4af09c3240d925fd5b7fab066860b10cbde729f930ddc839f7d840371242bf04a32613f9535ffd5c7385824d0ac7514a298e577f7cac09bb08384ce3faab28d8dafc553a48347fc9412adda8fd379f261918494ef74a82e664eb9d42660b13b46206cf49d2fe6d58407683895c42f4444fb11d86b25208d191711da790a988a26684e22538c4119ed4032b4b02cbf4f72160364674e582996892ec0a7869241329f2eee441b3299dd30eb79c2150e7721567327b6afa498b4449d376c285dc9d9ff7cac07dcf905ec288193c946eb25c306923b09a756fd4922b85b341af4d7125dfce1589fbb48c89de6aaf9b866cdbe906f1b7be0bf9a1744d59a342bf609a0dd287ba4821080fb34021e608f4aaaf5c0540ac0fa95b8635fdc2b49cd3040c3a1d5457f84aa3860913149372124c3c54c424011772141abb8898fe3cfedb77f6dd42b9f425c3232d5f0199dcccd61a6ccf6a891b524bd31adadb08defce71b230c8ccfce8d10fa140a48c4da84a6f17ef2ff3d3559236aabfe177d374cd2a0edaff0c2b1009a70bab0481085b126b3e99167835509e8959923bc81a499cd0c377ae3029a13f34d695d5e531dcde1466610a939ba922bdca0acf4a9ed1ac5e276cebc8d7f654b826335def958dd535a261be6436215a265c768d4f83a97afe4f676a595e597e3304958f72757a69213f6769337adb5b2abb3e03a60fcbed9582634a02ec2327776ff672ed33056e08e9cf9de5927f38be0008828b0e94f0ebb1971b0c8cb1c34be4a0d958c4561aceb551a12941801b1fd95fe5fc313250fdae9e02a95cd4b8b3d3a4a8606170104ad5c74d4264a44980465fd08926b0e7d66d7249f8501af0f3eb377ec3e886166bfd71523230f6f8decc9542ad9b3e8906466886e6f1512af320e674985bf9749418b6ab3b0612cef40599bb26d3cb9cb93710da9d5bcd135939807b20d17be6e5281bce9659abd04539a3692a35ca410edd55d4c0fc357230318123de9e8deac393da9bef45a5f2bc962bb8017bd799f558e5378dc5f64dd2ee1d22d6c41ea0ee91ff7ca4eff98f5f7f3d20468370ce0f685fb05e8814ee84edfda347424069d3ed7ebe0dc28052026c6053a43aaa1849316c2ecbac2c52da94d6391ec381efcba06bd2e839a000bf111aa9b178add626b0db9608cf4e7f9e116857a8324ba6f4feae96958729a2e409b459f65f934d22df1f0f47d2c6f063f6212bbb4deefcb4dcea871c61c236808e52fb8c45be874ce41c44c2e8cc064c6cea1d9e50a0022bda98b07cdac81fa7a21728aa0450e7a19f33cf5417f6a7071c8145319b015b27a605754fd901897177b7ddeb6d1c04581f0ff782af542e28a5f1515d76891ce0f2c9b50b3cfd8e9e3e809d1a710df689b6f82281661b89752635ab916d088fb875433937df2fb40e5561d494b4c8ae2c4c481a7ccf2ed0c18bc8255c3b8c8c8722955aa287fb7a22a45804de2aac5455169dd77ed63b808c5635099c02181e74c30888ecf81c044e3264e0858638e5d85567a8807111138488b2fb0ea43d7884bea063aa55bfb082527096a6d7d49ce7de5b1b0fda8326eae959f45afa1a14beac348441b1cd29f0b03091c4daa45a071db67d60af7d355db038c61fb8383b24b98f0998e5fa32bd67dc37abe18f7dea06ac97c0a8219965a5894ebcfaac74547cb2607595e5bdb1fa82cb38dd2dd343b4988fe1a5f1e0f60471fa88b3f40d0cba185a0974312412f0719415f8e41825e0ead04bd1c92097a39c809fa720c14d4bf1fe291024e94a900590fd4ac0d3716e06b41bd184fc4c81a02af358c9cb40fe44bda279ba09ea770308f8f043cba460a39cdd9b2ca35cfcde84c9f78e21817b5852c83e38a186b31ec88effcde9832ed59cc85467498e45016266267872a3805d51a0e2b607342f2c56433810eafcdba1d2e62b2d76cb010ada22f3332c042e46e4e972ff064091597328fc613806aa8691f9db76d12308f9783be51ecea68a7fb7efa0372d6e5950af603e863e515a7c454962d026fb80669afaa9c2495ff94bfc6e3d56a43438aefca09ab233f3d07cf4cae0a381db091dcf81f25b3d37b0242787fb7dbb185c16a27ae4e342b0d0678f24285e98a13ec58589734e063d57844f35c54ad0a5f7c0880f61dd3844bcd70ebab80b07b0f136b092860d2f866116f027e10e9d481a0aec9eec3f4e59889de93860de6a888d6c99b0fd13710ad014b15f375be59e22bc1131d3e7cf8960ca91383bbecdb36a10c23794b4165f4d950ed6cfc0b9664d949d60307bf0da228b6c42718e6610215a97e5da5c7810d38fa21ba39f1e93a079f5c9400d1c6731f5ea2f4b711683f75cb184ed3b77b91feeb95aa2c11681a67d798c10a86218c2a34d878c37bea15200f5fc9ee734c74c6e98a45f4e2454d4297f4b5f2fe9983f0190d3d2ca08a4b816218048d4f0fe861f1aa89d1b675c0a0864a729dd34a988243b6f6293e133484b79e4fb10e31ec7a2f3e80679bbf9fede1b124bbb5a898624fdddc046bb8c09c3c90732cf95bdd39df2fda2234306053fa0274f73ccf36dbc26437ee888c6e8ea42e2988507cbc966015694458fbd7d4d6c8968a7afcb8758d86e033cccd83bdf8840dd63db9aa5b80e0173adb7de7530339a7de8f994508f81ff6f50d7537724c152a6bbda552a85337ac46394b0ee2ed6509dccc41763ad44b6d4b15ac31ec605e7235640df223f5403a3587ac86bcb93e3d14da5846702a76fd74ed078003e68fb9806653e2ebd7beb8f1f51aa8a492a2e1effb2ffe7463bf947429fbd79b74757f4c88039d08986089a995be3ed99321a7738f1f210bfd1a73ca095c4382b8f59cc107714efa4aeadff63d921ff3bd657ce928da66d7a1657502cbc4c742c5bf142b709f28208d1b4f7cd1dac5d77ecc632d320289e57e40988c8b74f258628ab5420fd83cd8111305a039f9803765a26d20b2bbe27125b5083336e3eae56c15c8ec220c82af0edbc1a2bbc09d39d59a3e4ca28104f10a35cb4eb79a9113e4691e1541bd7073e9a02e890928d34088cf4f6c8032dc2937a2f6d84cc412e2785121670d01c98bc3f0985b582411c86b8a1da4b939bc49a33e53be3b9118c61c30b03de3629a67c1d028f48f7abd07d4ab1f1510b4daa2c8b96d0802a3db122da988f969709ce8bc23c1aa19052373894040548868cd9b2f60d57813c59e9fc27c21645acc399078940efd0e10056dd0f264d1cd67bbd164c4e412cdf212c528e6b0f846af7e4a9c892be2e104aa1ff2ee0dbfcbceff06ca386854c5500d00246e8f85e6ef012705c54aebbc7d382684381db578b2674f285636deaf9f564fe39d3c2009ea7bb39bbda480f9f6eeab81c1bdb3800bf303fbd4d4d3210d77983ff9fb47e0441b4b525c9ced316036a71c1b4b07a7e83ce5085ddfa3281709d5786a3c1299f3fe01c9604df21ab58ac6387cbd7e89fe0ba16d809999a059dc23bc788028d27843f4dddc7fcd5ccaa7109bb84915367cf9af058e9d16d69faf92e7b51669990dfbbf50d1ea1516c2bd6c67069be40c03c58d85f938b92c1f0d21273963fa658531887f3a9858d1160b6be85a6b186b3a6c6720cf7fb64c04d87b42ee8c056a2c1b85cf9cc885f1cb9de021f1f0f24b467ca24e81ba80a875b11b0a4ea494b72aef81c87461702b839fb400bc70f13a3f3f305e16e5ff1094534d003be971397dc1c51c0e902dd10b100b392a7c977463dc3d062c4e2f799229476a48549ec2aacffee94875060f239fea8a447deaf452c72568b031b4e0d8d27e0e35976e6887c5019eba49968e68e85c48d607b68c7f63911ea3fb4c8d1f460fb3f57ea87761f03868ba808074d7f86d887213480fdf6c3e7ebed7c31c67137ca771b68373fdcd404503d4c7b870df3da23822e09077d2d4ea4a7dc82044c26bc2ffeffe83f73b4b20202f90ccf5131ec8c0063a60cbeaffefe1878c750b1c9c7351828a3c7f4007f4433fb41c4feebf5164261966ca012cdedbbac80a75fd19405c3d7d2e314ae5e3b4c1fc784b67fbbdd1407d0c4a531cb924c609cc90e4ee02748e692a7d5658a07fec5efda2243e381f1d87af8fc16aa67d184280593a5835932c92c88ee90bb860b28cda45027e055e1f94262142c0568f150e4eb98d166559ba317a4631a348c10d99549812abf19c6da15c1fcd3589dc9932579d224a1c251a6dc0057d483d1a3609d6d82f5815c90e5cfcccce15a372aa98f97d5e9a2522889d4e03f4c107eb881a78e567462e6eeb0ff9305329b12a05f2f1567169649045669d9bcd43c3310d1635473e360dcf4ab4b8f600cf7904f918863ea2dc1e41b8a559bdd44a68b7aac1bc84c0adbbd8f132d430bd2ad8d4e37b901b1450b28aeb638f1d5f2c15d30d77d184fe07f02cc5433ac0b32994da2fe7f08ee9d29d0fa13e8406a9150f7e60490dafb3aa5cbb1e97266c927a8ff349d35af109417fe6e61ed89ee1e42287bfe5fd812ed2d0a489354f37256ebef14d21693cc49224a08dc72beb62f3daf907e0ebe245351e24ac4bef91818fbcb5cf9bc11f59f9b8569516860ef7570c77ac675ffcc78a185116d1b60b4e797425750125f5ac6c7fed0d5fef0a7c23020c63591cca7e097fe663c1dc742cb640fc19cd0e361e03e562bc99df002f36ac3e1c493a6e5f732effd5f3a94ce20e9ac4d0de61409085d969a2200316ecc9a4ca341e5b09fe5898cd2a6a33d68dc74e319a447a62778db4a62b5284336d97bb55ca7f19879432ff501d8f9cddc505e56eb4548298806bbe3d1c0827c0805223b09493a04632dfb5780c90beb388daef81415fff5aca1bb4697efd52b8cccc9d9aecc6f2ee74a298cb54ca8fc7a89f3052810353a7c556ce1f8f5953f49fc0cb889bc1126ecdde3028c9032eef0d70d2d41c915bbbe814903c7a0d832e24e73ace3360481e125cfc278186e7d3fe2215b1b4dc9ea007457b8599cd5c7560644a64a6e0bae159deffc00e64a0838141e073e05e45556e923707ee66d8291809757a2530075ce7e8916ad50db67db015d4c4c0cad04dfbe1f153a28d334381b9550a38e96e3075a627708c5a193856c523b621befc37aecbe33e4063c22a16e211dfeec09154416877099e1f988671125d8f74d157eb24ca709cc5feaaef269817549a1b22551ee6113ef237410d57e4df06682771c409f82937fe018a81ff0f612bde9f56f85ed6ed078536f0da2e50f17b087dc19055815a6fd30a26fa20afc614dfbfa03b4a12d6247de469f637ad243ba4c641f346b878ee54bc81f951570d4cadebcb5ac1de1f3cb78259fb6e00fe850330e5c1d800d0d1812abaf6e44765c7672483489cd8f1c9d430142f3b88e643ca7f4aac10b83852ca13a45e548b366f0c442d1690521009a27523da83c2deead17f8ceef9d5f8a9681193d9a119887f44aa84181d3968462671431715b8543b211cc89b7948500d762c2bb70432179d66fe62576e314b5b379f737c32f4a826f095810af0abadc90b711bf484a1f22df22ed45becbe8e2f8aee4118df8c34d8de49985f058efeaf4746ea4a92318c038413ac9b4a9fb526f8c958377a874cd9ea9585ff00406ba72d9762f81d97373aaa9d9379ed559360bb57442a59c994523667a260e9c81886be8184031920a605ed2f810d5ce0176d8cf788847f58b204a9aab2591a8e902c06261618019528cd15aebf00966acaaaef117b131557824c63ac9b37a3fa80a804c100be66298d00d64f04424bbc2100220d6863eaf7d07f8cef90a0ef1abc23f29da64f16e4983c52cecc16e85fefec6786da2630be421e62633b9dc6ea06411c89af566b2f9afdeab46c454ba997182f48262834899b973e6c74148691cdcd41d4b0793da09ea54e446521c46ec81bf41bff1ab39191d556b03c06e42f7c95c42266a20baccf882d21358feaea24d92a32a857e689d8a997334fa52e003850f8027d240f006ea7fc42012665f146e780598a922ca83c59325e32e03c84998c7343cadf628736591d495b4f0800d64140dd77f397494ef1ce1c2ba31656c7d582148c1dbeb3de9bd4d37db4f1fedd665f79438db663077cb331805ec76a7c102f220e9372e5157ab2f2cb033a8e1dfb6fc9c6beb923497c50537e6b1fdb56899c25dee5aa5aa407de44bdc85e1b0fa588dfed23302b9506b724490abe68a2b0d36e75cc008190a4320c676cff3f2787e1b3fa210ac5aeace6d2d2a32a3becf643a43ea87a3628fe3bc4aebf1dd4b87f6408d26614a0e2ecf4c4d73a0416cf569391012f44647cd6fad4d8e73d37ac4f57f5a2922a25f24f359b96eca163eee780f7d6969a0b30e032eb29f5dfeaccc2374396ba3030b37e831a659d31ca62931c460934a45939515cafccca41be3f51b623e599a5c6f86d4583fa65bdb75577005a4f2ddf42c94401f24480d6dc40923d210225ada0443b05dd7eb04ebf4bb8cecde77d890705ad3566836e2f906217393384535c050842f2231b275e402701c6d1dd4f0c023b76b926bd9c8da4cd114cf034af5ae758968ff2bb3ca685397ac6648cd0a647c1cddd1c646781806bc23214fdcff271872e35d41025a1595759e94a9fdc370af85be8ce0ed725b8866ccbc45488c0e3e517352761076c2c4c5a63eee912290414cdad4b033ac6ab4351db7e87522f7009b3847f954cf6f21a50733d86ad5ac0a9698b446c59ae46ea58a528520b7f5e1313a49c58016e255a66edfb3dd3bfb2086fe954a3ff11f400f8d7083fb451e4dcd37ab99239113432c749cc580d6288c0732d22b4fbf24a7fa33a0c57eda754cc864eaa272af431bf40c00d94d9785d8759f32e812ddcc5977997a5129788d70247eb238fcef06851c7861184acbe922afee58acef37747d70f3b20588c62c182fe39dad5c1817d6ef72ee5267948d24f8dfffd829e852279de88ffe45803216210e93f37bfe0b16c93fffd2d9c0f5ac014ff552a79bf11b02b582989963732308741c04de4b4513599b98e2370e7259efe6e892556311a3efedbc2207a19f70357bd08af87226bdaf78553ebc00a81d5cdd4201e90bce4ac8f243c712b117bc331fcbd21cdca1c537384abf8508e336fa2e82c7033404974df6a38977fd344baf324e343960ca90be27d9ae08c642cc1a611912f0c41d76c4e5d8d34b3505588a065f49c8c8be85d951069d1acb8595220f66600b4ddb7d0455ab56fa1a73874c49db72bfeaa5310c91d06d7dccb52beb089c8d966a32e1ab6ecf5e8bcb131f8b467dd18160050200cc2c2c8560181ed393746267368607dc52832ce8ec4eee2ab5b3fae96ae8d9d7e9b9fb43e5c456ca3b95f5c882bcc4c0d0ee8f78c774f6f71138ac248df59d5c8b548c1621e86ef95fdc7157ed1fa581f3bd5e6850441610374e81dbf699ff937c776adf079d47fd97992c4a09d1a3c58ab1a6a5246ff3441cccde0994f20001c9c8143d79718a5241c36f8990f5bdda442ff90dd3c310f929574695c20b40d6eb47f07d77e59afe3c8c8b7084849a9c854cb0d0bcbf3b83d20e9a6d25583f533cca0eb7e1d66f4d2738ea309cf04cb6c1ca0a4747fe00001466648ab799aa1885ee8165ff98500f3c8a738aa5a0e9611c786acf2de7889234390735d3d9280f14c2bbd1bd3924a47556a621cb7a51f6a7b6607a7b62fdce79999c13e4f4ea87dc365860869a65be9ea40cb64d7cbd5e8a09dea27340d33eb5dd8e38591ebba2126169e3fc4df3c9e615494e584b1d8d8b565f66b87a80522304406f95e3fbc14084554e12fd0f110dfcc6994d0870f2433ba7351f4e5111d565302f205455833f978dbcd134d7e799608a585b6439d1ed003bc680f6e6152ba74794c3e8a084ec978ff6a7604bc76035a5f2e7c5e4052324e0759199a61682b25558c5c7af0b928b44d3801e16f75b3bb914ec09e7599334eb9257889e4f9620e9293aadb93cf4718dc05f637361c9846ca0129461418f5e0251e1d4ed310ba4d74903497db1c2159d476b7bdb794322599028107ef07aa0732f0255be865ba5c100a36c207e640c2b7bdfd91b018192b01c9a02012549f0fb9221df2c629e34c0dab4eee8ca32cc2c3234728c9977c9540e39bfd4797fbb62b5749e82ac92a498dcf246d88b4e836890ec94aad90ad4ff645491531a087a9c4c3afdf1a1f9883b5f98a5c910e75155da397bb9794dbdf5370ab652dbbe2b60c3f1916e0eb933514f00a68041a7d503e190b9f6c05134a2900d59fb9c2daf898f8643ce31668f43d715dced0602578a55b356c2abad6bd6ab7bf7bf5cd1137800e1b1f6885153f19730ee7ff64beea57bdf8dea243b2a1194a24530262b1582734ca643ae18684b1f60677477ad5dd4bc88a9fec75fb3f59df40e9169532a483fd865034a71919530c941d4500829b73cea69532db1f7f292533bb5ce9734e06b9be094f3bfae5ca3bdb671ef38d7f37c7c9cbb20a1920cbef0fbec072adc9dddc49ccf7fc29959fcfcacc5f6b153971ca198add9c4cd85e2a65ab98997d454391d2f749e9a4e1c943ae6913482945ee9bf472ca0871ce523f0ba1d8859c6c818419c28319c2f6fb9c93cef09b1208fdc311275ae6f3e8d9420b11fc9003268cc0810e3ea83332b05f096c3f6abadaf43ee050031345846ea0a4a6c5163920b1aa9ac97af2b388112caa85996110b8267810032d5050821b28d15a2401a3864aa5048b5906465ea0918d61beb530df3e969989978cc475151964b0032890004108a08052a4c5cb12e7bc65f2e774b4ea08cfdd391c0782d00e8e1022a449932037f880129fb46e9cc9a562f90eac1a3ba0eeeeb47aa54e3fea5e53a3d619daea465edbba60b5aa3fcb73b26a54356a983e8e6bd468154f989a1aaa9adb5d6bb0ac7ddb014cafe4374c0f2490eaaae6e7c24425a626afe87729140c7f29e60398ae28037775aee3a8a17ac148a5c3cd9e20746881152888662ecf72b01285c662b9bc7bb8c8349c157af48db7b6398e2e0f3902d410687b4a431680501cadea2de45868c1112b7379a60309aef85e85688a208b1f267280b7a6ff27c32eacc8c5d42cfb49863038b03fd3218bcb8fd38515718c8436aed2374969e107b7f083bbdbd4965bda2b49e0e363c50a3b0b94c8458985d9b6d0dafb5df1542c6943be48628982bc1c8a2e7c40b0240a3aa40df952d52a51e78afc53e41dfea580b8a7290e47afe67d6f4d16d75026a269b43d331017579f0620ae09c4c52515e43d29a0fef639f5b79a339f72685d3abadbbbb1b8341429322384fd1b455ab1fe2cf4cdfbcae97c196dbff814c757ec7267c1dcc0ce70904e53cccec3952bfedccfdd6f3fdb614732b37f77f76f2918dc6782a8514376cfcccc3e7d4e4ecdf7140cce753f6b789793ed3dae9442bfd7eedd6f5bc7b77d6cdf0901b364c992c5e5a59a3ec79ccffc0053c5d678a497e14efaf1e8af0ee432c967ca85ef76ef3eb855f472ea74807ee6f0c4839fb714df9a5ec9c7f22c43313333a9494430fdfa2df8cff0c77cff1afe7092f383afe0f7e929ae531f4a86620d18941d3712d755da711b976ad796aa1ff879c1897f3e10d789c39302e8fbe7bc873a4e38cc711d7f1a9e3804daaca9bbade0d9435252ddb8ce27370b2bb44a078eaf6a4a95fa94c95733453b6e4bb1aba6da4553f5033fdd2d8329785c6af6a5b0d13479abfe26bfbf070ba915ba140e4be52b293bd597949ae3e8ea52ec846cca6b7471ede2520a3d6c2ae7c81a19ba4c54e96cb7824acc4589fdfb2ec32b56bec79c8393e1fae077e1bbf3eff6dbeb68fa95df5df82617c2481b73c4feec55c51559ec49946072a404931958f3e533c05bfeed050c0e2c0e4f5c0e45ab6a95f4c28aaa2bbb7b4776dd1e8d1485d4dd7d6f241a8b446318cb759845a56a154da95ae5c4fad3bf514250bbaacb519828713d67a94073f9815c0e45b93d155281e67645322a8400c6e25296bc3527d84a29d4a0c08a31b691bf3df5ea274b3b686ec7a94073bb7012b18dfc1d3497db42b12b7d27bda780485fd32dd2cb7f528a146572245aabe453219a444953ab4c608d33c2072b329671649dbef11667ed2967becf909b4f43202e7ef9d65b2bc86c9b184787557239b44e7f9cf061e68915edcdc1882bb23e6be516de1029a91866fbe4d7c111f172fae68319929dd3373148cb52422b892611f92094cb55ba06eadb59932585d122be303f1c38aa1a55cdf4ee64c75b8b08c3a55e4905f09a6addb68de3ba8e440273b0b4cff4a14a251c8983033c623d10e4f66cc76d955a3badf5b6d2320cd49bd8c62349ee1653779decbaaeeb5e42b8cf5d0e450ab8f48b76046ba3b740a3918528324e581134028dbc253934ba52071f5c1124026b1294d997d7451230186bff1200c5592ccbe4d1141e2c1cbf344dce071e97e0a10ec0e1adfb71e0a464a02c1ff0a0c4913783c4724a05f4253e73162b984d268e63b50a07070e8b007dc321cb5bd225fb07320a4cca7ed665e1f4e36895ec968f43e6105661e29de4b3ab5bb20e56f8c02659c43b4996cc5c92347c25dc6651cee03119149a8c248b618232f6d8139a5bc2edf75437696c061a639b0bc85beac28a24a1a6b12b9264a41849e836ea8989142391c01a8d01c8353235abfbc3085d1bc25480f25c910347d787b52f72e0a804c58242ad028d00653042582904cafa86a5d3a0be09a25b26fac4f33870745dbac0a51da90a509eeb43056b7f06a6520a34864c99607c76ce486145fb3f313ee61cd35f2f2bc48a76c6769cd427a9ba205fb5773505e43d2925e485711c4797f75d90fbab063673671db2222a08753d1a8a10b8d353a7dba538999e91b36786ace8af2f56020ebcaec1f4cf0de1f2db17174b048aac96cb6f83d8aef54de9f24f2a24f5f95df7e484e97a76b7c90a1bc3bced35752927375197fb6060eee42c37396f4d4ae7ec50301e5a5bdf9dd6b9c35bd5d1975c959e970f443526a1948f9373fa4fc93633332b6ebf718e1c78dc9e5fe92e4709eae29aa23419a334795d93e48119a92ed7aa269e7cbeb39e710a2838e426010a0230a20a2a6450c4072080c0c58f7745bbb3822e824c41046589902a4ff8304509e797673b1cb9a27d26df083c8927f1249ec493789224d43d367b66cfec993db367f6502a2bed381a4c1c92c522008ed7f8a76af838f522be117424eef26f95eb1bef1eef93cf82bee1927b915acc4aac16b31233f530e79cd3a547f2d399eb734ef7e639e79cf374800981eba1c8ccfcfd203233b7cc3489cccc95c2d862b52ab5cb5f63607e902e732194122b7f86f0fd40ebf6b2d51ac4792497cffb26fda1a9d2f3046d4bc9bafb0c6a09ba5c612340f958f9b4ba7fdf0fd5fdfbc13d4e64667ee99cf3117ceecc4f6b0cd08a64b6c1ca9fef3f3f4577f7191b2c3333cf394ddff78305b9b0f22733b33723d3a7190ff571894351a78652c2fca7520afccd2894084cb4f064a007821ed1d090515111d366664e28130d0c1a1931200e64cc785a2bb5018c1e4c3221ad750912184368cc92999308ee5486c42041d1d4b85319128344841a77283148667c8a04a732c47d6290a442127c88fbc4201981045aeb929821146609092a1ed56afa083e73041f1fc1a7041aee3e34a6af7ce6cac7573e356c9cda80ca2c317916356c6cb0a80c8162820936583ec4a1d860d13abd2e3159107c7905214908e3edaf2d24fb5e32590c53ffc834ac878675ac837ae820999917cfcccc0965ea284ddb19471a18586864c4b014565e3132fa8a8888c0ef0504c18f26930dd57aa8bd18bd18d13ef0fb3e1db8d5f17c60d3684deb789ad6f1803410ec785e40238484c0ef05049fc462140682b0d70b0cfaf969dad75080df4ba7334269035e020d195af5020dbaad92504f5254c3c69bd6342c6c8f1568dfcbc606ab691d0e1d4f7f6703b75a48a8d3412603b500c1ef05146a235eaebc18bd18815040192833c18417231b2c2664336ebdbc804da3b09797ae48c7f37ddf1127000e0e08b6a9694d039b0676adf6a0629dd027dc903702c0df91ef05ca5e577eaba334ad69348431081ff77d9ccc4cddb6d34ffd39a16afd41a168644c2273462363c694193fe55361ddb64a03d39299534dddb69f1d4c446a8c1f140d00a49c4224c60e32668c20e51422317600c00852ce62ec900a4558cd2944664f8c1d44a8594d22b327c60eaa55ddb69f18442a8c9f55091e2881864f558fab7aa6aaa7868d943d363e69f4388d9e49a3c7041b7566514f3f3393c8041b27dc984264c6629d70631299b3136ed4cde7f6f326d3f77d5f105a69a54b5c423c29e7d4d171e1232905c1747727e203c66ef3d02294e7e3be8f8b8111c3446b910c11d1913432a272a836337342996860d0c888e16af40421c4683494321bc85a6a3d8ca30cfa913f8c442826633327115c50346d03477960486023570557e3a6e0568fdc15e338cee8f153245098f7f8e21932fac07c602d3524e3081b023bf2f279c1c6ad080909df065ef0d512a318c9332e19615cad079eb11bc16784114a1328f29c56f5f068cc6de5a3c347c6c20a3e341a04832191ad6f1c6145a8a458aa2526cfa20667638345613da4472a845b9487a76df8be6f04a98c634b8df2388f09269452e0a8685573b521a3106e7135f1b301b615cd0ce16acec1a08cb4ceb1a62cb7841bbd456173848d3f58bcc9c43ae1841b1f94ef4600680f14469370ebfba8083e28230a8c8555d41f2bdbe5f27649214f32dbf405e2320627d520836c759bdbe461e6daf975be7ab84de684c735e24becf67b1e9456b5e7d550312699194974fa9146b2a8e88492351a0a4523c3442363068c192fb1cc68f129af16ca8468201483b164e654f30383f10ea6183f41281a00b81c79b223220000c711bc2abc9a3705b73821ef8adb4292133a4a7142a1082b793439a117c8c37142f3f572a98950b39ae0c8d3f332dd49db41058e2b707c610231e408a304b4815509f2c8abc92c1e28814697d3433555356c5858a18766e392068d49a3a4a347268f6c5c797483d3cf0c4864e57b2678351b27dc9047dc119c9057ebe754c02d4fc6deecf6739b2e383a38c5a526f396b1bc5aabfa59a5143c2a5ad55eed841be01070048570cbab8136c8a3492352f3963c3af26ab78f50b313c0f1864f70f4553f387a4b1e1d1d81e38e19b7c71900d91b01e800e090029059e411affa81c02d70044721e038b3f245701400ca7d4689873f7d192fb194161eb394a1f3e460d902d6e5c98fced4895dedea6e611777c0f33eb65205d495547852c6e5d9deaacdea9fb91e5a98641b3a735197bf55f3ac1bf95faffafbe6747906f596935dc62d6b9fdd0b3462ebd691bcafd4025a97971060503127d3c7796ba683645200ea1fa198af7ae898ccccc779abca34ac5ffd9a9939a14cde45789ae714830646eff4d82a1931544a7741b0128b3c9262c5eee542deaa270821268d7cd5359445beba4216c560a07e4c325ff85273204993b4d72733c12072203924674e22b8a0ba174d8de520d088ade978e85ebceae74e076e754dec92dceeda501233ba963a725bbaf6e23e28ad92053127416825c10728a594a230a4053f50f2822f606004f7850db268623b293392c4000a4b2fcf80a8904921832cf1b9bf69063482fbf56ce13222e288222ae0220436f40fc67a440d36541074427666f084541e3810ac40080f4a3bf5881ebc1b86504a29ede206da11468678b111a94cc0e007540891105422540891ef22402be85aaec8505845d83004c7cd6ddb503a98e244074ba0aeb0a6cb331d0871c56f42074a663a0892a4efb66d43d84dc869a218697145fb33a32860ff4c8e38122ecf8488491ae3145599e28a0a809002218e9c78c1103704e18aaa1a224e2dcb1559c6506a5fd066b52a2e787956a305d9a1affa9aff1db8259428419013d40f504a29fd6192e00335d89660a28b10705c94ae58438a16273ffc1071220646296881cb674f843c21bae2b3bab1d25a290caccbe55913534c995df167d65e9e3551021a214d0415a0899f6de6030a2ebd3ca3c5aef82c25248d1ac5fae5191008beb07d79067444ce00a483cc8155818a5cf159404046fc881cb8785458d39a2c8cb5b583d86d0624455b18eef2ec082957b44f3f6052c091a3197ca218ca62892e78d8960ca9b5d60a441d42db074d8440d64ed040e4861070db131eae3f9bee112a38220a223f60b93cfba1c87de2f26c04485cea8158a45cfa3cfb22884b4319fa62c074c50d8ce39863c62d5dfe147d597295cf3b9f8a3414e7e5dbb4a1cc10e55ec05e18665cf9e20cb99091785c23cad895bf71bf558e4b999ed853cea663b25b785a00bb54be726decd2e911ea48d70a3f493220c62b9fe7ba663e58ae2877aefcd9a503f856bd7d7632123cf1d93428dd03045c0648140c206d380e16172108b12e7679962cd7df533ba40df99dda21af01a40df9f334af7c17fa86674d76ae2c5d29ab0c7dc3b3a02ba2dc118ddcb1091057943ef3693b03a038a06f445ee2ca974797a928b144d0e5185c56420928aed79f2cf1e81d12050f7f8612a755d2001205796faca382fe7c1ad267a7930a1598776a41476e1173d16fc049be09462e1d138c5cf5e7cb2bebeb0451767e7045bb6262c4f5f80196674c82b83c63e2736d2ecf7eac5cebc22d96104c0826921421c515d776ededa0b03cfbb1c175968fc735adaa61b4dd56f83353426482428bcd96747145ae7db5590f395c91a5542a7bb6a4e8d2ae587339db5e549e1625f647843b5fa4456e44603a03df9450efcb5bdccb13ee8cb93d764ef549d6bd277d0bde9348ffa37b8fb640e29e145ad78f4af3d57cef7fd4dacb4aa379eb94e37df75ec8be92b5e6ad530ee9b927855ccc57f3b954adb44aab51e6d7da51dfec740bf5aa541871e73395a23babd19d154b1dbaf3b74aabac6fc4f915caec525f6355c85bdb8bb6c09b73c2b0642a924816b13c32ba541249a2dec26f15db5cae72dc96a40824c9920cedb82c491757545d5112957c74b0dc567fc695db3cb830974b3ce68b16e66ea12c6a157d59e5fb98a8c5c7fa4b21f9248820aea65b383d834671e8b714a9de562b57b9a32a89bc91e32451df7413f50d6f44a2298827319b320171716fbd95e3dfa18e93ed75bef7907dc585a2a9f41e5a9e80749d17e4b7a46410bda28ff3511924832411edc539622efd4a548baa91b74a5f8fbed4c9062b7251f72cbbc195bbfda90b6545542e47857a3344b24adff0ec899d202c7d44725b8a852841dcf48d7c7227e0ad7e7f4a24894426ba12864a53d5a53f8373f0f6729949e676c2831962d251f247317d762b610e9778dc37ccadf932147da66eb7cc8164a7214b2e646ddadd2dbbbb5b8a77d281bbbb33cb18785cdf70b34d7777770dbc10ac384a8995927b07ef65bbec96227317f15c05e77ac70c2ddbf0733e9893092b290db9d66a35aa14cb945cac9ed049810eed57718b647dd537aa2ec571bb6c32db2a552b57b9b975bcaa4fc3d6ef5771b5ce8d86b26f0728e956ef1ef9cc2979484be7e4a620cd9073941d84e5e70aacca4b368fecd147ce51eb88b8cdcd100537a6b65aabfa83aac0783ab6a1b93d7a6ba3a257fd5278a8bcaadcdeae6c583a68ec180db79a8c193348e390ee2a650da6f7106f6318e4f5559b647498ddd2b2099f74d65a691b516b13f5a7c4c2b9bc2997da785840743980bd3ee6bbb8147b94abe4d9d20baf6337057f8fca2abd473e25c213c62e7b8b84025ef51bb9228db14863cde48a341674451a9b119284525726d36e99a1103b5862b6b34b1f22f732e4685e4b9ef53ee1b95c7573b1806517138f6be83863a17449d332e848e471eca703b40c5dd866ce97f36b6a8a16b8fdb2a72cc2ab73b6d390d9c6bb6748c1431314dbdfdf11f0b6f932c55d0fe4ee76e70532a50648d017089572885f498afa5a754f56ac54b54bff9afca49d91314d0a438a1a23c5e642de722199cc7f9c8386dac8bd828f743dd4376e8455f50abef123f5ce4461453f72e5bf684f0f1326b749933bbda7c78d4c3762e44a8779ab7fead652bf83fddcedd670bb9e77d43731b77f260aebd56b0daa413f93d65aa5c984624175e827282624abb45aab43d569f55a2ba55ea9f37024170eb2b01791bd3ff36561af4a89b8206af2aef229b7f90fdd269db0b96d7486b2e50b7a7d419c43fa4be9512addaa43beea6dfec673641b6b9123232c45b7bf12d5216f79587ad5bf4354a4e88891cf910fdb18040fe7af99286c7db10bf28e5ad55608c75c2eed8071290ca2be8971fb67866c8d716908fb781ce62bc9bd70537eadb2a4e7f7dc37a25cc1ec7a112860820856d1229e983ce49da18cb10d25028aebfff305dd58cd4bc91709f643d4a5e4482bad3edf01a3eb983222203fef7ffb67a843f67738784edfc42d29b539b817eb6f53c53694866103a11de8f61e78ab15ea6b4a39e404e279d7f7ecab5f710e29a50c814a217dc9e3c6a521d027fd6f5c0f53a4947de17a78049d94aa0007a7334cf0247679fee0014ae9cf1a8adc932597fe6cfa30434c6b2ac1152aa376fd59067cf90a461156941104f37a790b0626e8fa53f005c2ae03995c7d5a29a59492b859da41734db2201941f40df72e03f3d6f7fe323f323e90b0d81dfe304bd877eba5647c5ae55d1a1adbc880b18d4b6145ca0323c980f1dc26bd62d8baf7f90e925e52045f0b28c1a082bc402d9fe3ae961293c2e9a25ffabe5d32306fc99081c9c06460d7df931173193119b12e3ce5b8abf4f20295429d0eb85ef4080c1d182fad21e7ab5aa7051864c5bf27d7c9c39302e6d36f413e873fc21f4e72f8a5eb87fc199e1a0607f61be585154d77473ebfa4011145a0cc50420d3edc0b70170690881794f10a1380a7067709d95c4e2e3065e3381ee111e2fa915b80b51ea8b7b854a5dcd75047e5ab7e8e76b4ddd965fb6652a61232952b1bf52b74463543ece0b892a388aa1bf18399de9e8517820dac7caf4f4d7d2602281706b74278cd84305181338d6642c4aec844b3a0d715b9ca8d44a1af14ad7c7983a3a6068edd0294e31297262e4d5c9ab8347169e2d2c4a5894b1397262e4d5c9af4ef98370a0d4138e4d886d906c695cf4914980a0e9e92a51a29ace42b3f058b8575e6e11c538873c4d09712245850e5ca5c468205b54b1a2f1ba05b8657fd9c35b14dc704b12be868d53bca14859d21d02907cc92254b16d78f20f4ebeb6c5fc3530bf5b9e7c21f5b68432727ae55fdae2ef5a0213571a2cae44160d796b33d2984e101cb3d4924c972bce7421d55977248bf853aef2ba7cfd1afa18f19724ababcb9a5385f59d9935d5a440e5c56d7b8ee49298ef45ca8632235c0fb2ed4f114c02ece57fe3ada12b1cb05924c22607e0d7d70fdb3a50e9cc0fe6b28eb0b2cd7b304711d077db174a7573df4e1ef9779f475fad3a31efaf09f30ad6a8769d5e9075604c119785c23b908c808700eb772fb25c0adf9fdc4155d30c0ab7e4fc9c036400c5d5ee2f20c88d74d4db1a2a445e99b79f402103c2a11f8ce0fba1a72f05ad560842312048719a45443961b6048b17571438dd8a38a73f4953758d246ad51d9982996f4dd83312b7e0f93af2a0ff6d4a7e6be85efb9d2ffe8ff9e5d2d94be14965c5e6955adc236f54b2996afea7fa91824561e85d2a855f55f40605d94463d76bc904393afea03715f43ae862c5f9958b77a28fead5ca883d563b7c7c08b4106ee82447700a91cd20685f18415712e3b0ee7385dba318994c2611bfa325658fed2b79c1c0c755e900f129dfcc107e22aea3a30ac0db1af3e1c5fd1184c6cf3dd7fef2fe2b052cfde97e253e9fbbefb2fe49eb72c4118af0c616476056ffd041cc75b5e8bf4f45ff0d6f79c88138a2c1c95af803a66f18a1f8793795d68b986863ec0cd098f6bbc5771c9243303cbf7d46387fbed5daebcfc76e4aceadf5744acbcdf3f5f8658bede7fe1a987f7a705b0cb0bbf3fb5205ddf4bd717be9bd8a6be0b10b67b9dafc7ce0ee977baf7c21ebeaaaf53bbcf21755d17beafeaebb093424f7e26140ed6b2cd7ccec444b2e9109ead2ff695367a7477bb09065ad4ae3fb7c9f74acfdf7bdf02e94be18fd293de0b7f38c961d78fee49df02f75ef8e37bee49dcff20b16bbef7405c5deafbd2e77ca5d293c2d37cd20371e980ff85a7199e14d0fdf739dd7f3f6798c33d29d4f99e7b1dd2775c82619bfa3a4e9c8840df8fff851df811b27c554a9de6037181a9d30c63c8f91ee44c0b6057e9c15047c786405cff9542db12da1640fdef4b2fb288f842ae071761d9457a916ba61410172905c4355fa678f89523e9bd5047c701be015fc1b000767dbf007679a417e5486223ef3b257fe4eb07f6a3c353df9352a5e75202facaa76106240c2d65a5187274beff42590ad9570b60d78e14f295ff577a767d2518e47f2976b94829e9e2920c637c4b892e982efd1a9ee64bd7bcf367e8801a6d4e088fdd55ed5284ddeee41cad007af976d8a30adb4558ee8af3f2741d2daa72a8d8865b2b5ef99b4c2e2ed75788497203922b32117839cace16d7997c3fd78ce3f8e3ea672ec5fcb264a7b09f9c2d22907b85564a2b0590f8220b2244f0a303962f6842b010c2ad2ecf6236040551626a1f9a28828873420b1a90545b2f70a5861348b1d2822cb014f9c09d28fa41110b33050cc80a141ed7ef7123719965d707ffec663aa774666eb2ec4e1f33e40e5bd2254988f4b9adc403080b92b8536e554a29258f395bce23af2f12b7be286f122c602424cb700670c28a91b8f488ebcfb23bdf2f102a7d051e738ecf2331775cb7649badd2c95ceaeef7ee96cc51f13886619e734a77e77777663699388e12993e626068bb77dc163da4aee3a815b5d6984b7d5ca7ed9e842a99302ec0d076a79c4b8bc5e226a5f58915244143e52e9cdc908406168800044c1a082ea0a20725dd0e135b4b1135d0275e5cf1595c0e374c6bb230d60e79558ded891334be60c109aa00c2105f2c692fbca03b593c508b6e184229a5d40ae7c48e0fe690a1bfa8a44d9825a964886600000001e315000018100a888462916092a6691a6c1f14800a6b8c427660361c07234990e4208a8290310620438821c41042c8d0d01401fc7811664c3064a2f02d43d8cf11acd17ca051d2f09697e1f76a65a29007809ca5adc019da970e4683608457a0af4fec5d6c1188a43d024eb6b0fd1bb722edb1f6c4ea34c3952c0acaf9c7b0b6239bba5561d8031f8ed34d94d1872d3eb946aa6caaec022f2b3de9bca2c215025260ae93fcdd2d7b66d3f3a55d2780b8b4f2a9033a50b277a5dbfd7ea33c27e031c8029eb2ff691aa9f4f1b3042c7a08e2a7cd5660777df337d7879a96f8de7ad7fcbc08c979b7f8ca02a72eb2bd908dc700abc949ca0c4260f862aa20d3130c397006f554a0096dc9d563a37d0a100cd961b8f09d2e06aff42161c82ad7bb00ba211620a96b54b0fd8df2b8db6f1ed9cbe0590032ca621088f6120273712f1c77977a2272507e45cbb68607227b123d6cd76ac3f47ae5e3ae4a46854b81b50d6e5563a9b305fd70b622f5e36c4e2e2f6dc27e001c1b82f6f2b2540b08f053deb98924f0bb0dd164e2783a9ccad045b316df1a058798b8ab4889144f672de2e775ba64691c91f3b16c29923f8d8a2804a42a00fa9e3e73093de78c102b1758d5ff330e45b0a95bcd7867f7e146f4c7611b890e75fc3b98e7fdef0b25f7b1e642360f9744be1c4ba87ea1bcd162ec562e5a74fe572b2cfa41df494708a6db204ffe5ad5034e96c33d11abcc4292f07892c0619a81f954609d9b385b24d0e65d47379611dad61091f97d6f32285a2a5058790b69e1142640a8ddf463caaa6882e47a78614f870c7b79ebaadbe61e89b2cb0ca6cd0440a73827fbda0b0ca9543d4eb3f86e598407101a0d330a7b141733c2aab5a24ff15f51e9fc78a79b8670426228be84903a909fa2ebcebc89f8285b5c09a48f7d83d5e2c64e76bf69cc20bcd7618d71f69542d25565ae021a404b017b6ad0e2242d8dada0bc72ca89219ceea64cc98c0d96dc7a09011e1f3c78705c708c9e9b11c145a74aa96d872cbbd23cb480748a02310829c56982d4fe7cddd5c507658357c584cf425f6643e23849f7083f12ca6f94a1d454b956d92386bf9a078bc19ca1991496a985769aab6182e74a80ca8a94eb078bae623877844d4ea9aff937ab2d904b68209526185112b3692dad47a6fb0fcbd0a8c05011430d2dc2dd109c021a080980dec1b7a0bdba5e98a899e5e58f997bdc5ed6653903565185a9f7e65c1e9f8bdfeaacde628c2e9fe4a792804b3a3cada7df6ae21bc983c5859bf8a6ed77960bcca8004db117932db80a60be38bfb3ae3e8d7deaf66211e3bfcfd750e631225831fa8a9929da2628b202a569bed63d9c0444d965202c263795cb1ae1ef391a035c8361aa48e252383f52f56e847cadbf55b643726717be881bee8b2a1bd513cd0808666ed7d2aec6f408aa3ff21376cd0380799e69f36b747a6f79e83242fde08f3b92a2471911afb3f77fc131bf827236f4b5894e23df3f5201607641ec80457ffd236814b5cebb9dce2efe8247d168939507f91db5b5c0a366c60e0fb19cd14eb59160933c741aee6c0e78b61695d8a649be6a7bbaa0577cb6a3bd91a5c5136137fec1227a2c1a37a4f91787b10a666c42065a2764fcb3303a39495b66b57cc4f3ab47dc273a4acd7f819184d74579ab1a386031e04af585da9642865b0d3295a1ea840e0ff594d0c460b1c8b4b01b486ac162d50db0b3ba4bc6702bd99beec2f132276cf8c623aec82f3446ad787faafe04d01b67c9c1713e70796014096b0be84996e6124696263429ffb98cc831bb38fe763e4250f8a88eb4aef942b2b4909735a5423023324e8181cbf2a8e264cc5d8d6d175428492e1d69d5a600358e08f06748780dc7abc015890afc6357ee15896c18a682d1ca8ad70413701240b114698e66169aae5e9a04206f054106f54076587c9f6ca46a611b99f768a0e2a581124dbb13857770390509d72a20b266dae6a4850034f00c7d1fdca4eda8cd57da680d8ac1665f2b185a3fbe16051602828d0ea58cc1e9d79f209eb7057849a258564897f4967f0bd220e8e11cc40256cb34b39d23a5d4ad8a8d242d41d44d82648317d52318965f555a0e71101c40889fd352b1818c94427e6f91d8cd59deff9b2f377b4b929befc917ea9a365fd00e598443055718bcfe00a820611139916caa11ddf8332a509fbaa41bb611233dff25a83156a9b16d677f236d5c16a35cd9455d62a579c69af979de71ef321e5ddb53d3d52af062079dac91fb2a51fcac18ea52e16bfee4aff954d19295e72564ac8e6b8ae85c32bed3a568657f8fb2a4f3403acb00eca32208c335c4ba198565cc980949d37bf57e97b379110ba4e478e85c6c41117e7b9b098f217b2597bc8c261625b4fe15e6c1a05ca83aaee92338db6607d77a4ee8b4cb6f5b16ecbc154a647722b75e5a81b7e39ed444ae53b75fe2a44dd51477fb44b424e6c8d38f5abfb8c498ab77ca35b3a865a0d1caf73d2a06b61080e26d717189b172471b3c60edaa5c3841e03918465728321430475ba747c78ba2e4d295a8c7dbe8656847461f3277b8de83a2d5e81142465a3d7d1adf9f10a78ca5c738bf082bce602efb74368fe3ecdce911d6d78821d9064e53cf42adc829eef3049f4c9b92b9122812c5af9001d6116c111a981ec2eefe3d5bd15bd7c2f39f07de5b922bdd0e652c6806a0c7cbbf0ebbd717282103ab8afe500a34c9228f35de29ab0e82ff47d6e353281f6496e3ab9740746013bf28ec77e29ba8e568f708a0f3b03fdf7ab372ff2b9964048ac98d28b792444d411d7bc742334b12c4424748430bc5ddb8548ed349420b28adfb83d7de8701e38a72a853ff9869b435fd60cc44e56349b643bb5a685bfbc8bcdb49dcc60d215a23dea857ca57f0bffc4be71862783bf8fe036e5f02701cb81d390392f4cbcc440fa1184ac88794df75880fee0c3d76411f775ed02d6e0f4a1be46f830ffe8b73fcba06f4e254c3e5f2adb7a9ac8bd65da26f2da6dca447e0abd828003651289dd890003a51a640455655d46412563065b7f3bca288c8388ff9367fa4ca056822f07960594bf4db83cab3f935fa3d28011bc42680dfe643c1a55c7eb6882fcffc4a8160c7ea60661ddf824490c9ea6c21a6508b9984e683666a1f94cfbd615aac3c1ea42732b5ebc8535b4c3811381e1a585f75b48c2e38c50e88433821a9b995fa1efe16360614a1dd6e4269b5643d39ce247dd2cdfd41aab5bb34ce5d1b675ba7e10cdb3f3ebaf4a8b06e1eebac368842716943929e7da5dc8026623f4cf9256ee2a637dda30726a0b99158c91608a72ec9e01639c8038ceaf67d5267258465531691ab913c05b467e81c0ad18048c48bb887db30f149c101fa473ae36012c400b06d30962d38a955e78db80fff0427cc1940839b7ad9edd828ea52de15e9345bd3d3464c2b972a61c43032c72501844df54442ac26d5045bebabebe84dc53946a3cd6f6282fde8172e23b436e29cd321d75ec5ef54c616b6a1e19be98edd98d18113207b8817efe9c56250896ad06e11e1fe85e7f21231cc2192e569b3f8d55f0a1ed4f400e14bc4faead48c88fc5063dce49125edf1521270813875e16dec4fb92d18b5f81b8dcfc9314f59d80a4765ab1ef4b9dbc4a70f8e6664d7fb40eabdce5fb105c0ede0132376c0d4d7dcd8a172898bd017fcdb7951d54f322ed1fd70f5bc115536c0ea20ad27c0d21da0020b2363f666f757016598aa613c4e249ae949a1b7179d033153441179fa1d2c5bd0c09a3fd1af209d87625ed487074c918fedc48dc386073d886fdcfe65719e4a877be65004f0e50f4bb3075106ae8371fda145816dcd4aa91ef4151aed1306014ad515ca8549787837ee696982bccacdd9889087d4455f669ad58626f7d11d570cd8dda5b38dea32f071882c1f3d5d0df18de45bc7d540386590900f4eae74329849253533d8049d19a9a787a6e5ce27006d778badbc4024c31cc2504e19eae2da56c725ed976b0421b84de0dbad5516fa648461e4a9fa414f5619c1bdd72f0805619c6beaa8afa44bec0d28d10cfd0768e3d15cfb5c533f4dc2a9d7c897bfee970927e3e8c1ca513f43d69c75ff8224f0665b0abaf5f01904b6f8acbb0519f0040f68217e59834b14ee19ef06a5b5f61eab1b0973d43ae5eaa86fabd76e4822c51350a1accd96d693739ebf7d291a68d2814040f1422baa156b12ccb0d2c2418880d1a49ba2ecfb0c70e96fd4b3833e47348e066e93a2509f52cc3611285f1488e9db78131a00291ba407d0cd23a52a7d4457a6bd8eef329a94426a409006b36a434173072245b1af53e08b04545f0740f62160c23a30bf11574e57eb7657aa1bc9d96f9fbb4789a9ff9961f411af18cb9251e0ffb70d43ef6a4ac4088818427b715eea571e67acc7a1be97f1f0134b3fe0de99a66e02e60a690345116d88cdf51611792b4ab14239460087f423d1ce32134f65a4e35c94a06d10c74443505d89164d3c3a4c9eb3cec65f3719359c313a60b40956e342b50d35ab8f1239e252fdb20867793bd97c92b34169acecb50d82a1869dbdbd0ec9451215b5f563143a9d63fba136a5031bf081d718b7c6b96926a8933a8032b38b0656f4733ff97af074a0257d1663cf31758d906f3dba60918e94e9e9f3c67fcf45af54b23a8062d2d87c258acdd320c2206e5fc297363afc10857140e6ca23ac3cd5d2b6af880a6e73e4c921b7a90e515f019de310dc5e5e75702c7ce393241ec6cf8a9d51b1dce2fd16beaca0009105dfb60e3015ef7c437bcfdb7af5362b15bfcc2b15fa4466bff0335e7c6d75b6932a69b978aa42c4b9ddeae9f24d8007bc1c44544f1c8f557b2e3d385a24076793dab2913366f128001ef838f2fd620273e3c6039a8716cece0d094419e047244623176818def76f9c101c67e0850aa9b8ce30a0f962ed715ed2d49a09ba9422ad1bb3212be08d0536fe0d5287b98bc5a635b5d1c5aa4dad3d540923cbe2a21756ef050051bec9d29ecd8824f1fd6bfd43b605ee370c82fd6501683e3c8c91d73ae22e0cb609b250257e652d07183b3090ed0da6f548326e0115ee60641eac96855887e7058a44ab7072c1e15074dacb3b4f01a459ef45d2d3f1f6a786d85f119eb9f16fa532be0ace5494fc8a5ef3861d24bc78fa4033c717fe4832da54db94515f80d15a9b347fa79ab148031860da8e56ef07fabd686cc4bd5b67bc17fda2043345bc6596bdbaf99732cb6f3881c2615ff0c8b2d2b143efd1385655f0f1b02f29c27e42f6a0617aaaae8dfe9a4a0f1b7c2d434bef4bea75d5d5a9899045977abc62de8f847f8812c1b90b8662c36b589753233b72be27fd0f86b023602fd0511e8fd4552433a76a0225d94db14ec88bfb0617eafd51b4ac3206b23c394fc7f50765322aba5d0865e7158393c2d2a9e29b714f367fb867052ea368e77434b006222baa678b7e2576ccbe97a5d8701c212c80aec5a487ae006a5dd89f5f5e2a504325114574a24faff91a591054f58c4582edc2aa9470c5d3d0c21498e1094ab25112bab0c308c975d6348f240511d0bf0650a68c8a5bbbae8d54bc440d378c90f762a509d809c4d8c4efd792941af63b0950d026554e720333729a0cbd0f37236be5c653f7683a359ac447e8a9a2814bb5ae1211cf54cc321a1723c13a3ee074a6b6eb7fab240acfe92c40282826bed5e48358b2e0ebc3936dc407b52860487d0567c5676b843a097d8a27a8216e0129986dd942e61626809cff245e3f1d421356f0913fff70c5ce303844026550518d87bd47c34bd98ef4423fc8a904889d0390b7d0ec23372b0344156bf57b1e251b2694f7e69f38d3a0602d0cc8eaa7a95c6cb284edcdda4762b37eb0ccab3f604031a1d414fb468dd65a1adbea43f42af5d176f94adc6861e278236b5a43896240631f0f0d3e208c134005c67554b0b5190b43cbadb531c880e9e5b864e228eee23b24c0336c2541037517cbc93c33158bbe5143784643489e5c111b831041111aea82805ac5c2e07267caf10c3ca167c454c80aa729c77e2a160170a01a8b16133cd42f7900b32059350847722f0f21b6a97b84187589782ab4c67560974bc88fc4e3d7e8ee9750d7b616c636e6646e2850b3c8285da6de075de88a6b6ccd3d4848ee98a9a8ab3f5016d0ccafec08d8303833aa81b5e8ad66b2fee74cab7190a94daed29cfe605c80335d6e9b793cda0b389c418a4b26ce03c94abe6d96a1f7354016d5446a68268886a21f1081aada0067d958182aaed53da5cc86c445ff8b4a0721a1b2b857107242ff892eceb4705572cbe74c48953915269538d0b10568484a1db5fe2a0b9528252bbac613e42e5dec9e4ed8a047ff689f7a77115792c68b22d0bbeea8d937760c7296e83b81dc32220646a5de9aa4b4c4ed13ad17c1eab0671ad6a515b6c3314f10beadf7bb95ca12cc81a00dfd16d507782cc8645f2b598568012b665ccef2e022faff313934b059b77326977f7b8e538439002ec4e7bcecc51dd8c5d5a75c4e13034f32498004a2794956c466a9df144977a56cb7f7f9c8465f061c52d394452222ea2357e31d3ce3af6af6e3cd35a77ae0c9b626b6ed4400e2b2b386be2fa9d80878b0219b336f5768aad9df7a4f83223e2e4015d02d293fc367cb20b2bcd192d26a2bf00369ac147fc28b2942bd09e01230a6aacc5803553e2055efe81bcd09526174a8c985025e4854c0b0489ab96287460ea9c2cac4e6f13e1ef67869b8d5e333da5740128028bba301a2a10978a5678f9612b844cccfff5916e103cc84fae7aed28132589c4b328757e712a2a990122c12a607049870ce9a081e743fdd70ef8bc0cd82797f981696ded98b2825c7ad4492036528ddf9c08698c1846b70f88ae13f54303e86d803f60a2a3b7d2a5096a750ed094e0d3a3416fc4c1271fce6770adb9a1d6ba4728780a3e36881dda02904c9a03aa71490f0520a28b884022f4c1fdc2da4733c6dd4d5b1cd541618044e16013151be2b6e73883a5e7754324b101ed34785ca85ad78660ad721dd7a9af736ae4349d7092fbe09e0adf40d7d24d07823c84862ec77e8c06152b3b3ea6f6885fac31c39d073c705b26a53705f40aec646e3bb82792ee89f5a3afa0ed9dc5b809bac5277dfe41aaf59ffcb9dcd9ac88f02d252408268ce471549c3d5bae07757f95b34506d6073abc1e44e95b6f86256a5eaf79b961010c29ee7e552a01a9918f0cf78aaa465c730526da98486368bb25cff5941cc5f4040e5016d397c112d247c96603aad5252ffa07e03de5d0af04840043ac689223b3ad430f36bae4e416d520b18dd227fd8a06dc06002fbd0c907acc9cb76b298bcd1968ddcbbedc3ac61e678a3dc78db1da078543bf0dc19acb231e623600e31a8718363f8707459f409ac8e7505ea799c230c6f3f82cdf0886ec79c5bc90b48fa0b94a642132653d03831f40652c3023013caf6ee8e57e5430b83a6b849b8081952bbd07a0f54809a1aed8cb9680a63c93e9fe9dd01a82a80ca5d105bfe76c34e5558b608c9e0edcef5e96e21f16de8124cc93a6efc428047f6b9b875267d5ef23cdfe5f4a45c20aae9c3d09d451a7e1fc6cf6ca2dd32de46afd4810b4540d7e64f50ba8522a0e580819a40579477cf5eb23b4af1551ade2b49746fb35edd2ed735679a26b46ed7480926dd5e89568068d128014a2b82d8c21c0c5250bd37db202a5b3ea62480764b9ee17a0c45ad9d2e507c7e5670fc218901916fc80cddc0067181623798aa751e5cedd7c2f3f06f55fb85dd1f0c40b81c6b0087df2d0c49fa7a6d6e2b21e9def437c128f4dc2d7030130522a11413e4d10a983808ec56b6d264ea8287c4ccdf01569c6b2559c503c467354628a07da9b24ec159ef2cfadff5168125ca86805e2c82a4d9ff5857cfde1ad576f550feb9c0e1f66d39e4d063b82f73136ef193473b82ec4b03dba330a8625f8a2e1e51f3ef8ea91020674343e479d45a2e60fdacff7e0a069d58a05a0934a11a969085d227923598237e218c0256f77f976b7ad0639adda1d1dc421a750f7249fff30f3f2b1f74313664c316c204a336877e7aaa671739c6bf47a6533a9efda1942e99ab68f88575fdec5637ff09925e66faa61ef5830c9aabbf3b8b681ce0de910fa0729d4162e6b753e77ede27b62d09db00c9b780d2385e14eff1d9e4ef16ce5a8d571ba824eb56cf751e9c50f646f2dcee8ba603b16ca5c67509456f118e1149141020aa5ed918a44fd08505e94164ac77332764648d61e37839218dfcc5284ad73db22d9c6c418492af0ee83e3d93f01b34d7406f7c4eb944c63b51056c409a8297c13edb2fa84c21862682678661267ae69baf1e2bca9dc2b9414295e89004d8bcbc80a92406856081416054ac7cbfb0c9b85c3405d2bcab48786585148f937d21246e23a34af23fcce8030451dcc75ea89a7f678949a23793ce9842fdc4adef9c0358990a36a9a5aad34ef932d3653829f95c4cfd961dfc3858c770e26ba08499eef60161824dd718d97e7de2957ddb606c40b17387729b4831fea21316297c7f3dd0097b67abb586eae1709df17e0fdfb062a6468cd2ca21e696d0f7c5932b61484f5b6faa30f81a12e7f797504ec59c0f806101c0dfb009933fb5f19d489502fb0f447c2b40f1b59a7da40bd22c9dfdf5470165b0091b4411b0462d059fa53374ea039eb1456f78653c4e5c81b39a3043a1e0918e007c3b694ebf554d9840dddcd65ad044156ed9c0a3c9b8aa29d40c43fbd66f32f045b4a8350b6042d36cf577bf44bebc8f0d7a775a83393f9542ad5ab7d1c159cfeea65e2258360f9b4049c89a18d1e8c795ed7b320a66aa3142a7677792b078722e08504440ccffac1127a169937f54e3bc5a59f818076cbb209da243230795c32c67b146665218d4eede59389d22a88747a6b442392d2c1c18eb8928e21e2f4126e90a0fb9ea4705d1d9f799719dcfa13fa9159d318815690c32d80615b1720d4534c232a0e317e8ce3132747b42d7bcde511110e34cf1e499bfa8806d56fcccec20d8dba2a4863becce29a06d6befb857eb01c1407c8e8754d4503638ac12227b013d7a0c9152fd597d530cb5aaa0c64599b08bf08f94857a91b310a6dc12145d6f8f0de5b6132b21b0b080ceb09ff1e6061c1cf41aa8ca7dd8c1ba69ad275ca4bd76b1a3f6020085576a852303ecff9786f976a0950cae755e3498687ce3a5304e36baa71da4d5df3dceaa3da7e7814bd2035b91f8a04710fd617e29dbc1bc67078638367651c646f70e1595f3ee3ebd8c8910db939d091804432d993445c9f3c06e889f94c2a541afbf660f26af87cea13f088566ad967d78775754c2cac9f32a43aa478da603994ac51e9c54286f3e6aeaf01ceb2ed5daecce741759864a5804f660f4736399bbd80a8b61807647e0013a40aa1adf98585409d2f902dfb0c2f898e00014fc34d05e622290053929df36589c26c61b488a45e4cbf785b406a8afe47903ea4909811b0cb288e1d8658e84c3c4c3c1b6ac34c5d0bc6c20dcb8051a15cd67480333ab7c9a323a6f995e0ec5605afe818692217f3ab0bf50f32497ce512e70f0234add80b2c5c1d55b15adce3edb857815f530e9b56b6577424eeb57ece0f222036ed2d33827a1537b6ee767bbf40655150b7c9f4ec7d577728c55b8aae71b6fdc4f89f28dd0473e2e5697f2c81391227a6cae786b1dbcdb83442f9ec9ec063a277aeccc89e99084741c3036dcbb4da6a69df2346b1ca92554ad685e2df98e935c9d6ec813666a0571d749b5d8d357f51ca50ce3245d5f06711e93d49ec346170ff72ec7d5bd6a48ba2527c8c40221862bae82953755c7cfa2b05dace9ee53382ad0524fa6742ef68722d2e3f1a8f5880fc2da37cb708d642029055b37582e1023e9840b93f70734b066d4101b91491960645a51c6bda8406a876f1fab1a6c2d667a51a9c78e0d185aa719453007c3913526da71a749a3319a0e2fff9ec990a5028335f1a39646ce3ddb89a64e34f16c2675faf1baee5311525117d85ce0dfbe2213580961559f1a47626bd98b08b7377b91b2191c0747ffb85bdda5276edd5e896ab93aabc2d57cfd707821805cc2bc20edbc1d9a34a9c631c3c42ae66cffc53c8fba3ce6e7fdab84926ffe6810b93c756035d4e55ae6a2ab57a16b07cb169fa824998eb47799bdb32effc51dda935befff861d629ed4d454126fe1e8d65cdf12d7367ace3cf1703fde693364e86effb715d43ec8e4764488a6a5e90c707f3805d5a76b404d4d99a897ac6be4ed26ee201662f04ef28390e3c4c08194aee9f0816779be33f12b7254d9bd48b5c7c80643b2b320fe700bdfea9aaa2c89165f06afccdf802af2bf0871b4ea852657108ae85f30bff1c9051dbe1bdd50f9315861185c46f90196da1c6542d68d5923553759618bb342ce5715c74d7c59aba9d01dd8000727183322459d4befbf6aa43438b45fff627734ba4b7a88034f08067be23a006dff326c4ffad87cb437249fe524ef8a69a989299af0d563384b505b647296968c83fa91d8c679cde3c964300239bc70239502821b176ef147ea7a48ef3d87cf6b0caefe648a1bf94b3524ff82af66e822bfb6b8f1857a71e31a38b0ff3bce201d810b7b73f2193766e043efb390983b714a19b713ee6422f41642996e983dad08049a887f691e0483894927ef3526a272f816cd1793d70d3540e9281fb35bb90e91f0d64d70f9c935bb875a03fd40cacd4c044cd1a48e60d31f1e2dc2144ee3f68700fae350dcdb7e7d1c7f2331bd15fe536dc0f1fb2ce9ac20b86d057e56fb002b6e0b59c40089c502214cb65eedd05f5eda98a5b6c7eff51af5e07b9a997110dce3fa441f73bc6599a05e4c152853274d2afc1839c8c5cac4a40d775ccafa334556ecb06e604ee2d727022f97f03def48dcf14a25cd07b5e2f54a4d4658926089bd90095f950a1b65a1404a2a7854a0f4337e8f75065a0e91caff48828840a3fa473a112c6b7bd02358c480cd4eb033310553e10d553bb5305adfd1be278b85de2c1e21c35cd3d65cdc12f68bc2bd3b22cd9234ce71b0cac360e540d62025d7aaeb679f6b6b47be20bbd88e0fbe9dd7f86b004c7f8e61554791d03d16d8d2ba499cfd232df4650dc971c0fc40613de10accde1f726b26603bdbab1ada0ad5210ac70b0045214e867427640fd633d579729a86ca8953b4e6136bce1345559e75997c92257792c89be1a7d0d7f7981a16bf8ac69954b785f36ce1e510b04dff100a00621f597c8c32e179183f62b658f030dbcb9a8640b50bd4b8f9ca001e8f4d5da2b9fcf1e008ac9056c7aabba0787872c348faabf56d2e7df751283403c98619c7b17c4b83812101e2ff85829ba8e993185581e4f0b4691b843b92347181fa03f11b0e4927090d85aec0175e947986828ef0cdf28e17fd0465e0080dc273b835365f83eda58d10b4449bab03d139a9053a573260cf3102323f0f4e6d59109c9a82e6ef3fe9f4450e4ed126beaf317082aa750ce9f0104b2d705a482e4b83fa3fdef8367452313660711f456d2df5e3b73569cddb88669385a8522d5b89a4d8ebab47a90c0c0738512c0396d0f1c1e8d28045edb8d737aa821ff83f5b157543b25bed6ef6544a28f09336e203d3f653a97846e61196b3d88c588a984b33c25269307178b2ba55daa04c510cf4130dfdf63b2699b61067ff29782a12d0da68a2d69c8a379cde914d23007012f9891f716258e867cdd52c504d09de94de627d59a26f9e28ced4838a49b6e43941a49d99c3f502b9d6786710e5c271532e0ba3573c766e89de2c59aceb43c86eb8ce74c1678a83df900c1ce724fc189131279522f2875eb813de5cc0644ccbebb7a6e8a8df82b38363daedf42fa138022c503080419d45f305e02d6cc581791cb571f5884c4e67587eb4a49c58503253002808c6b6bfa3783099f6bfaa93202b2e26284f27dd4a31bdc00e49db053898762fb8458465ce65374319ec0760ceb16cd420eacee70835c1f7321a14f48045428f22c87d3f113e8f5cf3a976a804eeddb9658b3f2fb1be470bab7d230623e7e9ac6d56e859918c4b9ea20cbc6df8e6c533c2c585301d2e951511de4f3e9355b8cd6539999f52403e842e9c40544d2802b44af017aec90038cad169016d739404d2519e2a0863750f6463f08118ba46af7abff262fce378f7afad1afe2aa7fd9ec87aa23793fa818a1ceece1319bd8bf333d173ca5ebcca5047d6a19c135b0a14c893545a6687a13a532e44645022467297c812aa18087b6f2ad63079fa9491e253e803bcd30c71a69477d29028fe7298517da32fc584e11553468f1aeb72d93033ef668ccfb14eaaff1abb9c710c2c4b347ab4b83cb108d517159871abd1f1bf6846da868060afd2d3efdb740e8890ff8d7a88e89354fa011eb97a0d153f6d39773adfe194a61f1dd93f4fdbf32e2cb91106d909611a324a06db0b79b34e058a7a10b5f0bc6fb4c932fd96c888d62046e8d2b0f204526273f9faafb0cfdee3e576e34d4a1dd7e0a5fa3645d3349d56e2da5d741179434b2113c2cd19d169d3215628912b12f0f16f9f404e300a965acf0865846c7d0121dbf749744b1fe003adee414eca15867d82bb00eb63d4293e490f9278518a4a4bc3fa3d612d7c0ba50a8faac92babafc2bcac753cfc1bfedfb5bb6b78e6b2df241c150d92772aca1261aceb770fd0ead28894b0a8fc05c8966846146c44be9568429b16bc2e2b18d11898d8fd990200bcae21eb3e689c9deda6f0296a61a2eed01b8afe948fe6a6614cb91d9b43c0bf8732da7e208122f005211a50affeedc66189ee8c703de96ff58f3c5015ec22987c170643cc87745c34191a22a81d45a2f2698a5248e938af83d4527c926d89f6a112c9ce69d9602d259c7275a9977f0b6ff5f718cb820023f383b7b2f5a34d92228018ca7b98b8cc3af8e73425874a80914ce12a29a8a106e03c9b896c86aa243843ee075183aa988449410b3a8067e7031556262c6174eac251b09708cae2528eef7510e84eff9016e546e6bed13d5e9745b25e04669b32950323bbaf1184417020c8212117dcbc047e44aebc81c8b7d9bae4ae8671a20592630c4755069c72ab78f7f9f13c96c10cf25f3cf72ac4cc986509bbd569be75acc307e3577f52b996191d22ac502bf87f4394ce7a8486c83fbfc08f437fa6396240e3b121a32720e2f9547e43901eb00e5e335dec2c7e400ad000cd33acf568bb709243df6d6e350c10d801a27397377746f9f334de2afdda9d25ec341dc9d13acc92a766b88b1ebc7a4f4b6df90f77753e2bd231339dd6354ff62dd36c3c8d40c04e428b5ba7828b41138310273adba0cd6108ab01777c4aff9fb5bef3709e49bde5064827ec1850b1f6720c51c7927cd09ce405b6d2f65ae94d4c39951c1352df10dcf298ce5b4c87fa6644a9122dd4abff89b36ce41a3ff54c03be4563a9645a7f907fbe40c3ed41906281eab80751d6dc7e56b58fd1daff1a7d248f363aaf062e4db47ea61bafa06ed9f341d5201484134df12d45cc1a0297ec67706294f16de476645744d555ac503badeb9fe515e93592b18b658c4af5b93d3f8b4b0d7ef3ab19beeee27749503a51f69061259383605f27c2ce10564d9cf1513fb64bc1a27060f192f7ef11253b9e8e9de6bd26001bfee7837b36c12105995a5a832c60c0cd09b64227d8c87984317cf59f4843a7d2732e57493f44e89c1c54660fce68a649ccef3c02b89a2e5aa45dd9ad5967abde67da1a247961ad22f1bc7aab86a99203201ad47fb9258d4827979983210d96350bce2365efdd6b8895d4bc6878c1f1ffee2681591525f10a422ec4b1e07a6fa5d8f3594586eb8a8871962ee5e2df0e3d30f78b5f880cbd7c68235cf2facb0e8199d9c6d18ae0223b23198d8ac667aaa171f961db8b15b2a5e48877052aa57440493c0156154171364591c27ee2e0cb65d06f88e4fbff642de9871dd7f6900220e618bb56029fcd2be1f5af6768b21f2eae202465c2f78a383521a99487ff5b20873adc344a82a5fe6c6892a02e5e23246004b6f4b7163dc279f724f6439e57f31866d570e6e7d4d2b48ed991555b94fd98f0cfdbbd97c117052d4e714b434ca6f32e51d207bbec918eb1249832d71239553742efc5a07036743b030b40d9f12768a6272551a3b00a65e7e1195ebf80eaf2cdd1e6b85a5232223bb9550952376739eeaa0b55fd6d7245e8dc10c1df9de250673eca969ea2ecb3ed022fb836000fb10e949f007ff66e2dd5dbb5636b750e885688941c21974a00f1a7263acd741ba318f9c2773478fce2c4afb68a84ead0d59838002ea2d218be81055b71a360a35b860cbd67f85222a5a897a3eaa000283dbf5fe5e75f51efc5e947a2d447fa681d2a595ec4f8f34fe9024d81ee2de0725c12ca80f72f967fe3c6f7a0738b7702751ed2e53e76f8d001483c1ee00709e6818e6d56a6cf3b056bd08f37a15f81857cc9072cb5831875d557afc30cbf254e4741b06696d13db8b15979ddecd99d514babc3a7cdb4dd0f508540dcaeebb1f3d944ca0dfd8a9b755251415fe92cfeb5b2f3ef645f94969745e6ec4d4f080eebf9187f3419565ce845d558ce06dddbb480bc864db2faa0f997038466b4288069444f240dd0f2692c8dd22fd4fca0aeb37c7cc55e3dfb7cbfa7a4550cc6b718e069469b07f82459595036322c28ddadc2b2297e4bce0a3fa4508a6f83a1db45f15a625d33d4aba314b4078c9499be7c64d585ea2fbe1a197afb6c02008a7c8cc4c6d91799b4855f16fcf018bb0e9440eb3a7dc341acf33bca88bd22cd025cabd10088ac4d74b6d1f85af012e6f27b92d5d591cbeeffcd1aa2c6d17ec284f23ef0f7304611cbedb7fff1d1cbabb14fe8617f508793f1a91ca69ee97930ff408be84c373ff82a08db68a546d619858dd94070df7122f3b6b2b09d6da2b62bce33bfd4ebea9403b400cc171954466076e65a5253299076353fa985d4b8d282f7207c136c8d3271f335690263c8d99f60813061ea7e9fbd681709e2326bec50520fbff18c6e7469c57dfe56d7499ea502f0674e171b0e17e915067ef21b6ce24f871ffb19a953d9c36fb1ce45a153038211794201b4b237a402aa3b262f5ab3d320322b304b2d5062f5b37318737c0a93da4538704b7738c6e139d45923d7a403d47c206f181dd72435d6a987041852569fd199433fead1b15d9f8f2e5a6ed616cc388e0947cf036df1e89bdff2c7459415cd3496741995d86bfc70ffeda43e38d2e040916836d3b4536e02ef08d3a39d2c8d74b2e0c9b4945da13c2c4d93ec6794ea41a5013d474c39f26a699d716ca741a291788e803a1e606b30081fdf636b9cbe8584bdf20dd797aa734bdfaa864a855af60429ba7ec6043db099d2ed8aeb8e6815375c21ec5bf97a79adcc0647f45d0c0c4ab87a8bef3201f7539ac2c6ce921a42ef7683c9e9dd44bfcf5550a07977deb5a1685457427e56f29dc04fbec2d1ffed786279a31c3cb1c83e1f5731abcaed24f145ae150010c9d043aff97275e114d76d463619539e617a207d329289c04f83f222c89f8263e98a3d92ccd79bf349bc90f1e1791f11113f6128210052ee0d198317a98074d61094d68c1d797e95dc5b0ae632d40c199c2897641e83715d55fae714c47110655415e3741bd91023638a964f1542acc298f6a953799984f11e09ecf944ff334da633ac6d7795df926a642e4df006bd8091a8ba5ce1393682f027b4f665c1c6cc091be2a654f1f767ae1b89b85ad930db4582eb3458a61d27c69ca4bb1e953fde0db48bbb7d35bc4359c1b89e4a078b2133bc30eab535946e0b39f164ff778e37931e10010ac6632b4cb2906bfb7307b387fced692f5484a780a706946401699fd9c7ac0a1f47c93bf4a631c1e711d9f50404259e99bdfab73eeb246c02a50bedd292e324032a29502191ba1166a31ca7a89fe300365a5ef65a05af4255f2f00d06a223a0d15268054ed25d1a7e1ada5e2669b840c7fb07f16956c3aa89985727c3073eccef8b31da6033362a82d30178649d2c30b58a429da1cb424d614a0ca8ca7eaf417521a7d61df265a416668b01c6633fb27adf3678fdd6ff15654531bb4fb19fd9562a68234001e8bc264532debdf6457d14f055c220e4f8e46b11d806768eaa0dd4d835dd663212cf7220b230e3ab3abbac1e3ff0b549bfdcf30a0e5841da41b0fe1dc03a3a35fcaec477626f11681aec6aa7012d5e4a03e321f119730e74fac3ca1221d27b01743708e8b7267672d8342fd6c83e70bc321c7848dccf209acbf8d204f45ec0a5317b97d383845ae1a7707ffa08b67683be61d76305c1f3268329b30d2f4ca5462f7a629f6974e5206f613e4c522d59e4c410371e17cde3c051b7c134ea3a92f9ffa7a6e8bd301613b5fe799527a1fbe8342479e42c7267c9b645fdc247afd64827e51d798bc48fe53f8262194d062adde060723960bef922b8ce564a62cdb16bbe9345454127d966b2c6d1db60239bc8c09d2028754824cb10b4696b5ef93e1eb7173cb40bec08613e486e26b2fa7c5025081ae42d86a5ac59ed341411d9423cb290f7bb0cc948415fae55bdfeda9c2c0308ba966250db8bb9384e3e2650037ff899b1f5bc830f3a142d14c899a632c17850fa6a58e39d5951ba0b691132e365858b9aebc709be7da8c2b30b2293206a267d2484c707ee7a7398d561c867edac1e50e74deca1491d71e2931b3440590fd3017fed49404c5447cf2cfa87a00cdc91efe5b2df7dbb2b361ad7fb2a6aac9c91c8b01afdb3e7b23b292c2ca61ff11766a0d8b97072fee1c611798e1b4874cffe24e70a1361890aaaf0fc1fd54e327e37bdfb1f667f14b3b92650e38ece4a829f2081b283e1239e7e9e404dc60311d5d7125e2c482cc0cfb82dfef2c3e1ea0736f7297aef345dee3666dda6b7d44588d5d761a0de0a9937da6a2176473748aaac2d08f494864d1f0612e3039dffd1fdfeb6915c006809641210f56289b130528c4e379d28417122957e3cc55146997b38c6edb49634e40be989e4276d243aef9e4512a111b7085dc3ce0d8c8793ef85b7d72f1b1388c52462f0c57915bf880df1eed1b85a191713c847439e2f1b57120d8e8173091171f44480370754c05354c88423cc4f706872236af7dab3215e5a4b6c29b728f0d1fd55e2dcdfbab9bfae2b98a3479196af298c7b8e67cf0e48930df222134f74e6d461e407d1d27d6e393db54a5a2928f5970c43ffb99be942597bc4c16f7c0e681bad46a97b197cbfbaa5c310b1737c8965dc8e07252107d1baf740f3e2d4f0b9129f297d5b2468b2a99d06b418691db88fc063c539d0e90f2a9ad858e70590d528a0df8ab8cd75d1bc4803fbd0b1cae1e862701f83486ee2971ed4780393c6ec51de3e1668badaf7fd61c7dade0c5a8e5d8d4b2cc79b04a2cd327c39361aad615be43ed393b026b3431edb195c81c14a0c71e371916cbc1c371b4c23aa2b99ff7e56dbce0b2311d1dbe6dc4ec682495506fc0793f917e149c3171556179eb5fdcd205587a5a78ea5b7919a08f9b814fb5c86bb445ab4d4ff065d8a5bc11d9efaec4e80d2582e333d265e202272865fc02438f9da1f22b41bbcb3c8306b96a3f1de4e9d3b5a149eaaa8a8a082bab189d479a2f916c5aeb7de58d452d39e7427705ac4b887683a7547117f9650b1e900818864d584802e7372d5c8d341892959211dd8f79b0a4ee659d54b1d690c5737fc009d93e4ea4a642e5131434db6301fb2c889536758c6ee74aa5c25d0375c2f0e94e376031e6fea9b0987a98efbd34f978049558e4656316e5db694d12af21cc40ebb5e77a28615906f9ab802ca30a947084d593ba482d7adae07a53b3517ab724bc7ee48528264e3d56ba426acef0a260a9cd58fd3a82c1406a1cbb62c055e77d2b41414d84e004600d34ec5e9c1f781ce2cb98c32753368871ba9292d659a2d2004468a145426f0e431996714131a27c0a1d43adb28db5441d3dc0988289bfc267ba391ab5214b254bbc45509d4f8d4ae29b3ab78f2bb42af8922be774fca94bfe1bc76f448d9c737f220572e420f91b28eef9f8e1c0be092dd4113c358cdb5ff181d679cc779c996541950814953ea6b5af1aa292920d0209b89df6b99ee1aab5d8a8731e79d524a44559195817c748881419fbf28e4e75604f4e1d7e58fbe24cdc49e923d544797d6ed9d22b9de8517f90710e1b53e55a5fe1e40b9df9515e7d19597ebd294d5ba5f5150e3ac533d56cb81b02f8f3325df92bd2b665609ac6f5a1e1be56256e9e22b312c69508c22afa8a32333b7dff4efe2c1e0eff8a3a2345d15cc762911f84df23ea086a569074c96b07e09aa01a5e0db7a811ed7358b6e84f71b880bd09621e1797b445550a58a9389863c36c270826541d24c51793e01c64655ac6bad5368fbb550c47fbe397942268b4c86e67af496c71e31e2a2d390db526f2fa5837fd283e8d441ed0f2a1c91cfc6e0b4ff84c1a7922535f76b9266859693701f6e72c231262311956668947091814905b4caee7f0000003068e90210d26f4135178f90d0118b2dd76b51b1c691cf57331b983d19cdf32e23b55b4c91789c02e5dc8c0159d0f2909a4a0c99aaae23c4bbd2a4aab01d16a90d7b2e48403f9d1d61876f206b66426d75b89e30219425ba098bb73ea578d2b4cee6eac1134ce17fec397060ca46ad962cd0a125b492308d7a208a71a32ab9ba76e9e6370472cda85f6e932ea90739c977b2c8e4745192ae571eb4fa892eb82938c54ac44acdde4762ac91e8b184b120fb9d392a9b35c8ad7b561bb2748ae0acfdee6cb46b1fe64577d0527bed46d03f929a283094b0f5e77ac2906d45c09f5902f879320f4493220712168dbf72cf916f47f4fd8630a650ad099cbfa737caa41789234d621c18a93dfd54d4a4f429297ca9cac060c6cf3130b93563dcd64bf31583925045663bbf1e10e10c7623d4722242c22f7a2f4d5f7fa644d7536d30ee3c6181e41ec10cdb5d6a458084d77dac5391ee0d3453b1214b1d719647c391100e2c24ce1fab2a0eb69794acb2a09ae5bb4b148c22856f8fc5d7efa80e1294daa264b297db886eb02128e1c9b381cc697a3bac432f30e42d6435e4365975698976f7ef34a4108fd484effdab9a6ed739d3ee55432a799290f37cedb577bb0eb10de96ade83f691f583082dec415633d65a05d27afe4abb872ddee616faf0ce102cb983a734315b68a96e91d5620652567ea6929e79f5d1b6107a62908ec4c8182c31bb0a52f5553078467d95a0f9889ddf0a44e5aac18f40f622fca6f21e0a60682d9471d130148b348c42c6778d3cc316d0267fc8912f0d4d147f446e87f7b0210b6da76c452c5fec7e88f38630be720a449e2299921f06b3550172a4bbc646643935d27b5465043a7a7f66c1f408a9793bf6c036d8e45d2e6df11feea7e316318ee428f2db1124393b23db81526270f00744ba5080d468fdcc8cc7070932813fab6f812bec8c6e5cacfd86a18377547921a2d36ffc6ec641289a1c205f428e104f670723bd25d1f54e906dae55ac497c4f932f39b961d4d50be017e280b05155aa5a61663a6909d22f1e674ebd3febc94e3e36baf637ba1732a2dd0e0878658a33a07dc9aefa12e0bb09b558b19e26c753494779ce9c9bfd0ae52ff08e08c6039742365695e16c2aeae46450704d3e8466904323896feb84d9f91f92540fe064a8ac64d6c2d5e812a39bef1999e05d72f6b66f6addd491b7c8f9395ee55802ba98e242853f6d3ece93a5017cdac1d6f693275c12c73c6aabeec61f5e0dc72934433a1efba339646c67147861357fe5888fee67440533a5ce1584489064cdba33771598b7d23bcddc543ed98fd8cc342e575a1e8cfbe3d769b0e860b76e66380eeadf776af77fc356fff24460469aefebbe0298e9af446ab6fe08540b7c9f8ae369415f8c68e6e850ee3e91a3c4b5acec034c3d32f6325f9b5dc376373c235fa1557bd781ce60a3fce5154b41b9ef0434d9c6b8d082f1f64f162da094518ba25d91ebe515dd2aaa1b6debf115f511c0238896431e4116a08af29b06efc305b88326e3f7152e85ce670d474b7ec597c472e4c6cc9df81dabdf06c88f38e67ae2f9c49adaba80acb8b57ae8a700a3bc9bcdd05c6c8fa175de5b69a3ad2eb03635fde355669af49fa393f21084f89c0785a118ddb859484c5463ca1647d57383e9a0104d7e363100de9da43762f262e61a2830bb23b58987df8ce1a3bc80f6e84bc25db304136c73cb4408093983deea5eb0c2e3b44456cc6abe1f1d5d6e959860e2bd0c4e8147d80f5006a8624de28a301b3895d51720b0e0a880125075d2098e364144ad741f265b54c68c6b1f8d4badefc5f34d5b3b3a32970b651d20bdbceaac8d91bc7a28e3e1d1f4a224262ccf5e9800148702c26b9458a0106bb39dcc614e6a9fab8a644efced4a2d77e95a498e78b73a9ecef499121ca5cccb0ba0f8f1d928c3086d02b4430a75b2e38f84d591c78589f7aaaac2c5e0bf6280569d7075c182af7356f64f5101691b7c85d95c4346b021acb8b490da5a5e14a54824d13cec3193907593d0ff4e85694910738692075006298f61ae0d220f17f3f05bc2913ab76c443114715dce685505d6541d4e81252ea2a8f295d7485e0bed3bf624844693ec3139e32323b49a8b2de9e8289cb0ea619835d4ae47546ef410d4691370d8d524d7b94a8a44d34a143a00d41ddc6268eba5b29528c930c84c886374746659ed8313ed326203b3616c23ea7d1b0ae3438447f417da6d45ad6a54be3d8599caed75a5aa88e6577d520b72bf2e398c491318d3093823e25a70f326274ecbffa21975a88411a114be2a02018dcf96679f902cfa0b4de854c65c252f2f5c4f255176d3e9fd75f44280822338e661701cf21d55c7c3303b99d04337ca6875f1a20d4a57ac0b78fbd9889ba9263aff9883b35fb4868a1b9dac762a45db2de77b67b89737e15328bb543a3e8dbb99cdf217c5318c9503f91041c5c73a7e578cdceb1a94dd99f98950ba1976c195905975066846ce830f92847115d60dc93840be2b55c23dde2f2086520b6d548b217614f12c438166d5d3ac3ed6276c98fae79f6a8c5d5df30d2b1705cade2ccbad3285e9d016c1282bda8172cd940cd6b6eab06d3fad1d2010ed546a368ea199fba37b0a56ec99c1cbd31553009c15b157122cf7ae1fae3457336b067a7f4f997347b82bbe091edb0e2271464f91cbe7c343abfc8e1345b7d5da868c9409292559ba92e66752075d911eee20c30dc22bec5ea51ec1b55510ac238c546d0244fbfcfdef310444ead9710720762f10fece53ca804507f939d77486aca63a4e960f29fbbb6e4e9b5fe5e09486d19a9b75b7b3cbc19b3f5c24d411f2abe8d7f9c6e289c65755a20e9c52ea0f9c2a58f97ed9b65909c5e61a9e70a28c65230400522e328eed6dca99d4d66bffa9daa00700cb9dcb81a85981c1b58ae0cfd96679b8df2730691821c6353cd627ac1a1946e0cf29f756b7cd32a4c75a7c3ad8a7d50a6269c74479008904794d8e7bd08a85df387cfb9681f7d9a879714d1af8a6990c64120afd652fb56d49e545082ab1e94be549bb2584a90eee9324d42e7f0a1ed2306779d9a4f5e50ecb20af2ed82688a911dfbba0be004b822d291c48ac352a3850a848fd37dc13ae922c71aa3244113d02cc79bc051a60041e61b845e140042a165e9ef869df734b9e890f0d8f315dfe73dc3ca6ad9881d6358343a539baf1febd6b7130287283410dfaf378b2d6fca3c9b6aa367757ba7df4cf837e78cda9518f48fb940b8a9ec81a5c89ebdc7926e47e9eb33ec6b18e638f6659de5910cf7e65b00394f4cf3763e977ff2d73db7fa0eff26d9f68f4dbcb1fdae73095121c7e879a176433846c50a1e24d41d0913369c4bd7d9b7ce561e6745d79b5a848008aa2e09bf395fa9eb43e011b5cce88eaaa7a888924abfa4c3db02b6c2829f0f4c6d25c110f0d1818df4c36e820bc63e91845131c352bab1ac7ec8b1d52d2e37e5f351e124ec5cf1745b271ddff4f8fcc730310f0b28fc917d112c51b9ad1468ccdf2ae102e00810e429384f0c0af8aaabf3b6257a87dca68a7db0068d1d900845ea9cb715f60bae116fed931e2c1dc5ffe63d0f8b51338ddf103a8a879755acaf4d95301d72c638cd32f23cde0eed0c57288c84df583846e0d130f44723c74f770c53dc39072d35fd1b7bd00ef9f698512ce6fcd6208e237849b347e39644457cd467177b2a7c71b66d2aca2773809556b1dea658f6228f926b976f76cdf070df359273b4286bc0a990d5223dfdaf7aa312730c069f370e1d26ea1752cf33f7870e2464b24895b2a3a7152b25063fb60b428014b0725371068cd8edc9fcdaa5b13ce4bb47147a67f10ed4f9941536a0af8926e12cf36296d784107307cf6a0a01c02f093412b7892589512821287fb83ded79541827d85aeb8797ecc11c1238726e9435d6b07f20a0237b58e58900ddb744a1287af7424a53991a7d81f5eb84cddd07d0b586477e1eb142f5c44934d617850ca5c3a32900494c905271d17d5148d295a7f2c3e6779ed78b3616a445832d10d49711c6aaefe242e56ac3497de0bf987ec5014932ad15ecb84615ab4f5b2cd8c4996c360fc1f7e4eab4b2976d01430c0dc59871f4b872d8cf52e3f3c2d5959eb9472982d087ea8ffc3eca28097242bacc8506fa329285db2b3ff1226116a534c4dc981317b92e9ce76f6630f1e55bcb29ca368784a8f6d3ea670391f3742359bb77d904c96bc065a129450ed061366bb204b4b762e1dc877a61494bad02c18da91c62b1ad0c0f0441030ad6a5d2eaf9c19425084694564b3b1425c6be823c7fd9055efe173464a6546e5182a5237b11b51ef199e5fe3cb527c52abcb1e258b4eb291423aa7460308e72c26bcb207b39153b90a9cdeb0062090a0ddbe7deadbbbc083a0517e46c627bc37e2355bf52ecbffa00cfa0fbc6ed5e5acd87dadca8524163c9b80c4f7e19a6a90057368b1c633bdb7f5671f0bf61d9bc987228deb004d3bbf72f159933389abfb838dffb61e7249e47cd8331a5c2d7dc3860a46847dc06f34c2d7f74daee0cc48268a5f8c8c3fed6bd8b3dee0be0fe34a5ab30d9356f81743b7949e42d1e402407e68d2f1dc98f8c26ae2a123bb1312255345321c6271d67eeed4c8027bc1b4d06ba8c31c8b080b3098e6788da583e0ca7078553a506da93962b29fbd0a5091dc4acc9a43292a8aeb235f4cba45e460bb666d78d651bea705e242bdf95e8eb9e5195cf01458350d600a0a42ed5df2b13e7373202dc3162dab3a102696d572d0673dc11f0a558e4f95b06c59596e191d3ebb10d4393901ae1771c290e72d87d84eb7366283e8422affa3124255c435172ea19e1e9c4039ddf7f91e9681908987bfe29c512b512abfb46fa60874f4e4a7d98a3e7e2088d0d2b5dfb59bcb6bfc1648b580d00b17c7162780cdfee83bd148e8da0f30a65d7080952ae955a276cf67030ebe838e0eccc1812fd94cae03b0fc636c5b119d2477aac5a8fb87323f6ff03b590bf9853b185d1fea972345230df361f84d99338f1a741b4e619192a512c69201aa63a6a58cb6434a83451f11506df211eeab0d873f41513e21b3b1cb7f84cfce51c145d6e442711af22be8181d1d3b8ac2378f8ca9342cdb8f9dd8eeac352cc5a3e58ad7e8ebd7058e14d08b57a03c007f6dd792008d986f6fff7ee7b210a1f9028346924e3df67acc5bb37a8361619768531c8425bf95966cd646c8108f16dbe566243259efd007daea9cd0d67a3f701677e636a823689595f435fd23c5eedaae99e6ff715bfcdbe54af8e3bde2d40d4693fb85d6cb400749d7341a17959d885ce4f29ca04a37d5188c19c7d262c94144741d748288b9e7ac670094f5889b1e66072b639d9d3c61b2903870c09adffa6add7602f993031088ef5016e2643602c0aac8c0be068d93194ca928d19271916a77f20ece75091eb75240430e30bdd4c5670a92d8665a8bb75ef5fecedc5dd9406f682434ac98143a11ca1767a167dde955195781428d7b4c8e615a8b90483cc0b22ca23fab88682b50427e5c38263705723d3cd4fabb352a2213fda28f0f11d1c1a2e7f1179ff4d44a037b88e54bb1086e8090c8e4f9aff2972eda2388f259c52dd28b63a499cfa50884a3000c98011763893ecb757da09e2fb0c8590bbfbdf352e46ecb3432e4cf41e1bd78cb2a24c6c4d5a233163c2ebff2aa1e3acae6de74847084433dba5bcce7a04480491ed723fedc2f0f01cf6c898aa810c48d6781cfecee0c88df363785f1e098afe3b6c43f79b8a1a72781aff630135a59e82dafade8bfd00110cbb60f83cbffa025fcf18c39bf05afe3376c6d8a6ada37ec85cb5a2f65e6f5fbcea808a5ded399b2ebcaf1f8ba3366ab2f6496aec4ac717c7919ca80c90190d26a51c948603294a515af66ca0e4717450b318a38a3a472648dee46fd1bfcdba74c093c5d469898c9938bdc12976cfc9badcaf04160d9a84694b9e85a86bbb8a65b557f15104c8dbe7aa15e868b38416b584972a7868311bd992ef4699176f236f2ab4c631fdca8cb4c4c2738ab6f429f729fd80626da8036c7466e1e268b88d2fe2100a09ba350f86bdf85a393aa5d148e891f1dcb0aef5a1edc51551c1a15417e737c5c0f083ca83e09280e4bee8e74672011c3ddaf535628415fb9e49ea7e1a4fc1cf27ba755f46dc307376157f46db5964d77993993a257f55ac0ebbf923399ed1eb47e1f7ca93c008ecffcbe09112489f2b3a440c73e24c2e1b5d5f66169df2f45d33572ac26322ce374eb331757dab279c10c485a0b8ed8ae863044e29e0f18651516249d78154b3c7633febd02701676d5f2e1d0aaa6f4ec26fa54dcd009417ca7f04a56897976e0d8d462b5a4a83b2c057c114a7960786398dbeb14f9ef5aeb3378e946f4b1962f7457d40a557c7672a9dce69ffa067c5b7f94b4f1ea01b8fdbbbde6b15f9dcc50cb043e5919839f97d87e7083925a6892b44d405bf47d2a77a8b20778b9ea9a133945e37ca6d25f24baa2bb8cc3e48097dd90374e12cc93d4a4fd303be25bd564eb095458cbbc6722097c1f71c168d0f3db41af877fe4f2f5f904430975b01b35899bd339e28aa9ade898b8b0ab3ed04af33e0d6a66daa1f03d59e6d67319f83ce7104fda8d4ce9fa2acadc163c108fac3e4997eb899010a6316fd36ddf6beb667390a28e5083c6da9a38646fc81003a70ca107aaccfa668909c06438a901c4a8e493134121cfc6b91a4a9cc3cce1bf0a1483de9b9a981a471c509221b0bd426dd20d38c8d0e20bc1638dd5a47f0310963a1af98f87289e4568e0b6fa2c1e897092daf2466fe9d8448e9e83572a3e8552ae942f5db33f97fb8e860e569620428f04a5475c65b6e9a01392c6848724912f8b6b85c416b2261217847cc56d43a303428867f95c6b755a3cc4661606979e9227d0b9e90f6384c4adc21c9e3def3614c179f8cfa5575d4a8b17615f77ba776b3bbca98821ead51a967a20cff7f9464fc39ddb03035714b80103caa5713467f8084fdb9e6f2431a3a8a3340f450a2e64aef381a6cf0a1b4401c011763416c45b6250917b8a7a608963125199c697aa80c772df22e94d9ea2ef973be320e7d00de26065e4ba250f3e353844728a2f30c32d5aa02f7d182605d512fe48b75db1d9479524ea5975a007be9ed4fea2db136840b6264fb62a752e53c2abe3c5cf8c34f77608e6e87a9b2f548b5a0fff9cde301891c4a2e7561415565d1fbd37ac6cb7d6fbeab2806e5d02bbb43fb7d085d376c261c62432c0ba652549a7a44f0043854acf42e33aef26167e3d5b8c7bd55d3def6fea56fba4da61b11ae576cb2fa449dc214d1e89bc673aae39842e66d8f0487debff8245ae9567e57ac315f46e7309ec4d5862e063a462aa2948f6e326ab2d8a51688e55b14fa28bc59bd8a5d7f0253ee7eb92e6314371e8bef5c4efcd82dcd6605c50dc09f7d179304b436358dc954be6f4e2210bb238a9a80419a78712b22440479491f06d4095c251f4b8feb3e79b3c51c609ebe059fe1115ec6c00c15c5b0a6130fb9f3b37bb36c7dab8ccde9862be45f0d81f157eac24d26b193fbde1cc8e6b5372de4b52262df7520e58ebe1393f60c7d390e36fb603ec656e319b650c37d42261843c8b911cfcf435def9c731231c7da615cedd21f31f3f7b6a421753f18c0467ec3df29b6c1bd6e468ef704a07fac3227ef529faf92dfb2195f4577e52b211595a6f18e01c8d38e30101d0d6dc68ef093b70af13187c0f6c552a2c756ea580d66be899b48d3c43b2c54fa991f68e9c2987fa35d7153a8448a43ba957861a55dabb0231d2e45859d69941706027be40ac7df8d2f84367336ca3322f67b3fc07cc7a0535fe4f40a4f8edc8e9dd8d68bfc8cd353bc6a66261820c3a5dd92c1d02992f1bb510a803b434897a36e462c59e251dbfcb93d79d99f81d4b28f82784e0b8ed8e42466177b62b78b0dbeecc881d194e43b1980171c52cab65c7b16f9d407ecbc908f51a3dbc11c673570c7c1e1ed91edda02de734d92b708387099a55a06b56725821ca79e79abc506c0b1840ed2cebac11c613ad1d5a4f4b2cf7ed97a3340f2bec30d70e8e7f321be8b7bf31c7ea195f7788d8dcd4c9d3dd052c2e070fe6c85837707acd6d7893a05d81eb04811340e2f1559078dc3a86a60e3c40a5d8c3c69f3bc5bd2ebbb7b493950c82906dfc24ce7d2fbfcbe681d33ebd0058bb187cdb5a8468350e7e7b96647e2c45e234414656d1bf5ff1b7fa962b81605c215c37b4afab77500716d468d402b2532af63ac7efb00fb7d7f62265dc4fa3a111b1c02b8e4d898d2fc701d46a5e16bf6a7d79868f10038adeabf1d7b06c432e82f5cb229b1cbd9f14de97ebb8d30e3604e7949278399bc8918e1951b9e18764de38f671f8efed0b6c81791bba2ac2262aee6e0839b14772fb02fdedb46b9e337b9909cd19863e077be3a8c45c8707c15aff13d3df843c85af21ab5b7594b48f7bc04c02d0603e04257d32ad2c4863d162f3a522858861e5185c383326df0c60816c1425b3d153c1e2190b8190c42b5d2ee3abb876803740c088ed23519581e11c1fe8d4589713ce009b1a96a08eb9e7c0e2cc27c715bc7b54e64dd83847bc560d8905302ef8c1cb70ca55c7004f37be100ebb3400911aa2506f10167872afb6d95d6fa458605bc59ca4d29e0d2cf46062eabb14022cb315083c8fad7e076c1cbdfe0b14a760865bcede26536794c8caaa1d9d3176f383a5c84cf61b03d9f239b5e63012631fcc1fcf87b21ca26ffde3702d6ebc92dcd81a2ff0ce40d04ab1eca0001896e1ba14e67229fa6db73e88e0b09c19247deac4d20bd2377243899448dd4d05b0a274ed8a118f2d3456f761905b33a242a36b20d8e74d169ca6e56eefd1ec49baacf59f292cf2972994bee46a6bda47129e364d08222dff5c45313381cb79ec318e69758b26bc91fb262c4ca2ae9b0628fc2990ee45bfea9429962daa4a3e489f36a227a6e5128e209271419bd8ee87d3c30826a8985f6c5397eb7dea31440527b9e2de3495baca1179a2f2b06459ac3e260419e5284fe10c502a789c10f7677049aea0cde463793bb85b428a217e479d2205901e7ad229ea322b04fdd887fe1376cf07845dd6d6bd2a7225cd3ffba8a7077d56361fff54977428f59230593863d71fb48ac1d3bc5387aeee979dfe78e23eb51fbd21b17dc5495e6520cdc99a438ddb6c6651cb40893e2bf386a1bdb90f33cac9f18f3e2ddb4d874648d9411b3bd80e1674334004b652f51720affdcc7480789e845154566c79b6117a05d6afbdfa895bba610cc6389642a2076a38ae6a1a52ca72fb1e7c8891a87c27c4beda246898d4712302d09ade1574e4f8c604c967ea7147542b176a40b37c293d823aeb422e43fd20ea10b81131d67e46c373a8e3d8b438e5d2b1018434a45d3bbbac949ba66ed2402554ead9e2ee95a1bcc0646ee4649743e9ffe01006c2de1d0dd24d5687eee9a8b21c9c414e238fdc0ab25dbea8ce71e9b29598f535e00421bf2f28269169be85a760e659161cacfd1598539e5b282953953fcb3707d5aaa1340c80c3fa0ee991b0060cd6c9e9c89e820c64b94f8649a5519deadb1aa55b32d2200ca83110e71b28041c636fb8069ec01e5fc0280f47d29c5e6ba0618c5141eca3eca9d9ff6c14d651cc8c2090264231e541f1ef397d4a67ddf12a06d3273400b6b78f6a16a66db4789e0d45f4de600fb02ba7e691330d414dc26ce388c63647043a5e435b2af231f26c4f86863dac91a57e067c6f0fc02dad40e0400e4d9557e0d1f9a9787dca030a0f56db7e5689cc4394724bdabff1a0fbca00331751ff0172d46cadd21337f53d41e915904dc8821ba56f35d143dcf477786aac2a423cbf4757532a076bdc36d5d19172db5a49547c1755878078ffafcdd7cdaa19b6d262a0a9502359691f2092f7de82990c8c789ba12609d3bfd714d371ebb6625f5256cd495e3e1193e42fbcd8eda042778cc246c15d42fbf08ce2012dd2fa7b83f10411bca7abd6d8f7f97a491cdde0c7ccdd6e91812137cbc33e24af098cd5c2d69697cdbf19003259ffb41c60a74b5f720d5505ddbea48e910d71d00b9e462626a40cc5d1c569f50863ff758141be9141abfd57e03287196e313f863710fbcf82286fab9f533e23b0b145ebacff1c81bb0cd79b9ffb695319d1701d04a4c403cac906915e30d45d4d07059e0a56069fb8baa44e37042b61e30714764b37db7172d0619c393edba046911fed20c3fd5712578519b80160b0bb73789f0383849f9f6c2ef69720750d1d89ccc9a140fb22ddebd07bb23d9a68d2dade6d4d6e29a54c320501091109de0857365d253b6480cca0945d59e5e336c81e8eb88943e28aaa3c5569caf2a9741923f8b32ce2440d1ce843c6b8af66e785ca0718c64c159ca35d88905db60bf913070556f0307566cc5f81058f3b386628c3083c36930fa31e3a7c01a9f7cfb163ec2378765ec86817323acf967cc9d292023d22f0e86fc3e92f0303121ff2e4217f050a421cfe921e9331a5a75a931113d1528c486644f446a2d2bddf37a594dbc663f26071bfc65ff7e5df6fec61f3e5420af88dff16b6700061071923b2b6aa0e5a766f2b4b3d7e76f80b67972e97821072ae4683c1276b0261f92b4b1f9b4925cbf261f86cf5c3bbaeb420cb25f2c81a6b907ac8d30548565c70c5873cdfbd18ad4380a68b5194525ac409d494395cb2fb4c884929a5d45f36474ad403b65a5b9176982133a971d3dd6badb5d65a6bedd9b367d7ce526bddec0b5b374b6b0db71a4aa9cf39a727b9bb7b0baf3ee79cddbddff72c64cf462277bdc9ed28779a79c82e2d64b7f2c5ca94f455e96cf1554e578f1ace39a7cc4d941bc7c615ca02b4cf296ef26b5cecc726c0f22ffe39695805779d734e7ba9b7c5565b9b766ddb5bdf89da52b6d2ed69cf466bbe0f95a2b65ece836243a5e8b63dc594526aebe53a0f8a220e94d94ba59e762954ea296a7b8ae25277ab36cb92697fda4ae937a7a2b6666b9625aa28cf7977778f34777628da22ea395447bbdb35ea5a255bf37da88b4a79930685c2e952a28db7b5fbd75e8e4ba132aae336ceab2c4c53d6729d6ada958a6e296ea5da62c1da220a655b44a13890ed456d9676f7d2e7bace9b5465844e5493d7b98704d736a3179b3fe996f2b04c0ceba3a9bf76b335bb840a535629d792ac124ccdf7e1d58b8a9956d15fb7426902d5517dff8ca9e33d8d6823e2b85aecfcd4b1b58ba910fcb5ced6b8b0f6659817ccc2b8e3bcef4508de9e6645d7ded769af0af4ec920d5bcb0196466079419b042ad95a109b55eabc18371c250ad626b958b9cdd2f0ccc28a155e8cfab4ceef15dd2e5af05a056d9255b2b594104c2775d9d4a517e37c25c4a51f2582d3e2d52382a4936cca0e4cc8db5b2fc6d65ab245e205b9d629391831e5ab19c540a23cdae489a34451145268907225cf222372282afa7101d1c448ad01deeab6d166f0a818ad9ad0d01d21a47a92844362c9bb622465a4d4c94006d49418c724bb473f29284db20d091494a724d6287665f040adf2b345a1216900959f282838d214a5e848038e8ca224d1d0802943516a306b40152223a0975962736b2d1042e024041108112107224832c291087288c2d1af14e796a28318f27871cd27897841e2058917a317a397234858cab77a35ba0619f59cecf0ea6138d79cd7a6c2e41403284c515e681d1c3233b48c07fa03d98956b50cfa058df409a1b1149d83a973b3788da5e42f992dd17c419bd023cba95d4bbecf4aca97b544beffe283c7abeabeafe99c527a31661e3603b66ed89ff999b0a7936002b3616bbe8a5fc5e6cdfd1fb00d6cdd98e969cd300587d9df5e45a87a0a7ba4ec8813266001e5c10db026a8ff01e366d73d45861d9880f5b880da01d6e4e55d985128016b82ca01369b3a387eaa0c50ad696574e42fd5ffc8627a19d9c81a623de5fb5e0fb8c9616aea4508deb82e8fffe2358c7a316cb462543355b0cc0feb69eaecc808e5fb3240f9cad0f29589e52b23cb576696efb3945846b4cef624e4fbac27feeafe3e8bc85faabfcf92d22e2aab8989b5946f0d0b296f216b48260cc7550557b2ce59c1d037f7efeae7887c8fe41b13aa98ea8aa6b2011e554cf9fe13adb3a3626a1732df15907cc3114f91efa8da41be3f44b384b72625f9fef8416ccf229a37f7db53c13e6b88842078bc43f78e5f2304c7fc7865accbc2fdbd08c1236b0802dc8ff4fd57c07ca75ab8e872f17e671a5cc863aed801cb59be7f652e59917cff0508e654a18f8c2e8c815ce6a87fb3a0e4cb32d2610dd137f751de640d75ceddc29e2637b6b70f00209a1889dd97bfd58c62b08609995dbac09af88f3895e92572f13e5178875cbcf7bd1b76705c1870bc433eda45eaefe3983a2b9aea5eee05ec156deac87b871ec775ee95e2379788ceb93f9290fbc136e10ed105b8d2e50b105c2b754ab38f9fcb254ed40b102cb35bd6106b459b3a7768dedcff3e8c8768f9de97fb3e5c0cabe0b19f64cc47fdb03b8735d437f73feebbbf4fe3037c87fcd545444df9fe7de22fefef5f29d204ff2b25df57dd57dde9c9f0b217dea1ceb9ff12c3633f0de5fbaca1f9f77ef77ddca7d69251fe242c8142a0cc5e70f2c2961b4e4a2abd1904ee515b844300710f858b0253b3f31e20336833ea7e6d8f823c7017cf25444b3e5a5e289222b2645a69edbb9ad22756b08445e94d9f3fa97ba5efce02a5594e29a54351449390249f250810e2c485ec967576adb576ed4e706d5365b77d0d4e86115ed6886df288f3d20a302825a24495dadb4de995364db957c4d7b52d4184519e51f28ca9611b224220803c7fbb7ec28ebfe841b6430ab2ccc0c91398cf5876243e6477777721e8952b364350c943e0204f150c91671111b22c7e3f7ea8b2022c3e39e8e0e4d465b643103b79ec868aae64ff3b0492ec5ff19c2e3052f3e69cf3c64af6a2211c70923cbfc59c734e1f217b09aebfa60f8a4c4922e4640744e4e043485234e79c406029040d2c381cc8bb6d75db425c8381ae40c9622c43260d22596562068fd10e628b0db22fbdb5a98385105204631a2ca0e1af0859d1932554932540f0832b34204f94e6cd0e30c83477d19318f2f82e1c382e08cbe76cf4538f0fffec2fdeb4ce8c9a01b227203b1334b70e0d74ce068e3011439641b2fbe81ca7e18e1c2ec24071fd51df375710045111294268333f8c7852e09afbbd8331019652e77edb489d1bca23972f19ce1f5be4f92d75b6703e9602cfdc02cd1c951947e74c262c2911e0faee62adf7625c419f982cc8c5f94212c8c5580c47e7cc9740610b32c73a673a1307828962fee84112c85d2688c34081e78f32766f0bd4c636a5d18f8bf36568c0743e8c506c4aa1285cdb8c5c2dcf274108fe099bc0feb5192500e1b4454a902707a04574ce8bd61d17db7a0b17db27fd279dd353bee8d5ddb1dfd6a154deadbe5c929bcbbdb27696dc9472727c719de05a3466ee9f53ba7cd22edce5afed49bb28e25b2e699d15e68dff658283969c7c4a70111fa7d6927b1ef5f4bf681de0215aac1f3733543c19110731c8f3595e8cf9cd9e88f16250130e90270e684f4491e7c37c468678c1d21436fc4064682e910116c251703fb1a4052f2b2b1dab260a056467fae189ef213ca97283990c62b00315e43083d24b877375f1f762df8049f657ada848568d0d4e0b1b0c81e50648b23ffe5e74792b24b04de78201e411478b090ca065c909226c6024fb775e0c7735c5623122734200890e538a58d2b4c4086ce24003926fce3939a55696187c796c21031ae4a009239a58aac193ecefd262030d66d93fb5e3ee5e640a1a777747f51148e4f9d7dddd062cb9b37424cfb7eef386eceeeeee5ebb4a1e3b560b268658f261e2853c3f185e7a8e90c72e3b964c7d0642b2fb0c42907d06409081e83218f941a6294c36e5b106ca09b23391fd2f8ec12cfb7fb1ecff59a0c83e8028700a0b93224b86b0081d31c202a5480d1d80b0207da0068d8605073830c1c067c8b6b13e10cc40f0e3379dc78b611083921a1ff881810e496a2c891d8dd0c30f4c4982624f828e7c0e413d605142848b6c895c82491ee75c6249f69f7372c9fe4b6a7217bd8088ac0451ee2228b13ca298b27f4cee222582b21143598c184a32e286273704e5cf6dc4cf928c6acafeee524a29a59c96a39452c984c754528bc86a9d49a7ecb4494bae52e6531e82c75a6d9d4d2e6ea94dfeaaede1dca2949425d8b2b2b4620552130d8d95d8f729a95081b4343363652623835463b1acc8acf8f82b26e6090686e9e54586b1d06a65a45221d13cefa8a9eb621c8734944a1d3da150b37b7fb68de849e64102ca3c484c72f7c625b63d6d4d9bd3c665063c5e262e99e9d6aed2cdb271b94059965cf40aca70cb02f2905d362d2efa6f4bb61f95cff6b3290932db6445649b0f972dc675432e7a0fafa993356aaea175a21282c7cbf484925d262b7499ee167f550bc40515b3414e2efad3107c63fe925b93bf6e53d3e6b45da6eb93a56f71f13acdf0788f8e84f2adf96bbc4b49fe927f97fc356e4d598bbfba680927d97f6bf2d7589dfc35ce277f8d9769e3224de8ec7f9d50b476d14b1d9335acb72cfea236c83ab14ef6692ba2644bcabe29657f1e2b49fe9a3fb9730be4b1926445c98a92952cfe9a36e4ce2b401e2b599696b6f86bd6903bd3803c485b9a9ab8f86b7a2077fe401e242e566256624849fe9a4a726715200f521292129252167fcd24b9f30cc883946569a988bf2692dc5906e4b15264363bf2d73c923bb3401ea4a35acd8a8fbfe62c778e0179acf85891599139f96bd2903bc3803c484e4f4f5afc358de4ce2f200f921626261f7fcd1972670cf220f9c86441fe9a4572e715c8831424248424c55f5386dc5905f2204941324232425ae2af1943eeec813c484b68b4a32dfe9a30e4ce1dc873b4e5a8e9a8898bbfa62c77e6409e232e4831a498137f4d22b9730ae44172323474e4e4af39247746813c474e474f474f45fc355fc89d2fc88354643653e2afe9939194fcfc3cf1d714923b539007e90911110f12137fcd20b9b3057990982001b50bc98304d4397ec4913cd2f90d6121a7404253680a09b9386e7e05679db556bbd9ad6edb5b77262eba8bfef85f145dd989064cc3d195693205132bb1258f95c95f5da48496ec5fb7787daa5c32169f9c93bb4829897a28c89e2c59f8e87933c2904079d2ea4f70742249f3590d5d68ea8cf02ec2bb0db5b8ce08a1d5e222845629f4f119e7f755a6d6cf65faa62fcd947dcb74dac2e4220cf0389926d3d4c1f3e97354130c1ca8f7cf54c107903ab75dd2041ff2499deebb1a011e4511c75f7502c93dc54f0aa5747316dd04ed8692a6a2172c71ef6d3a40b2e0623b80ca2c9b6e4e374ae9cfe9356f83958ce812d73674fe669fe35aca2e9987ec72c3160df9f565c8af3a5ee41b8ef333207fe4bad55cd616fc5d73a4d4ac366d2a9f82439d23937bc608c6d94b908f1a6a17332959bbe8234952880411499249920d7122221b2126a5b437e5da79d1812e1ba24b04c13641d0f5a07e7de4f9b4fa44810b92a4a4c027b7141d6ec8fdbd6550c6b58dbc1d83e253f27b9adcb8ef4f7b9adc48853d3dfdef0fb3e1d3f3668637f47fc0646cde50fa148ad4a3bec98dfbdb6f614f93560adba7fec6f6a92d9ce18ded51610f8c7085b0611b95b3794365bb700fea1cea4e6470c03e14ba908b14a6063c7f742127aa2f80e24e4cac5e5773816cf6d7d83405d5998b49bffef46cdef8026ccb46e71bd88677ded09781b37b31e8873655863ac77f7e8dcd1aeb1ce954a873649d237fe652b4c068889321492ff9325a574beec01a17fdeb1ca7469de3b3fe748e0b81b89318f2b6269b2480dc9b8cf02d5694441677af4112ec8c595508e27fb4bb657777cbee22eefa4b4ad7ce8b3af52968dbca9efe77ba96d76ce93940beb714f2a673e4778b9d236d6ab29c4fab7f3fc633049c14ffc377bafbf4ec3fa5e064dc1b6e1bedbfb37bebfe2decb1c794d3a1d0c1a2b158dd26f4a5f4a72cc0bb4b2a0bd03f3ba7eb5b4ae78f2a9d063d74e8bad6162cfb7d4e9f734a064800b84f9fd3fd32a00160d3394de587cb7a846b1b9fb4b36917dcf7e7d8e163ea7817f582db0800b9c349852d48d4b6a55e76cfcd96aeba61cb1f2fb843806b3ef726b07a2f74a1a58284792f615e28eb60ab9751988ff7432e4cb601b6578112e62bef1db6bd45a9bcb0db00db66b9d447590114e9decd983b3ba3cb3be58e7a3b3d1a4dc4a5426b6dc43fe2679cdf2429257dcf31751cb663eab45d52a9944d9d15a810b5932550d836e079e377071bef25d0d49144a9500e155901e9c50c773a072321298dbbfea2f954cc5819560ccc0b5ea9bc8e4ba1ee662b9d2e6bbffcc7f9de0e58562be80a9afc6515f9829ff578c0e04c96c917bc1e0f291260f94e29a594524ae9051b4629a5331da594d250a2ae87c20fba59af0607327d16e810b72237cd9755588fc6e5b60dac7de361e06e974371298eeb268833d7712a55e654dcaa9b6097b9976e82a9ccc5741b2a73311c8b93e9266833a74205f771345d0d4d6b856a7280f6cc806b9b1e174c40420111acc9fcfaaeebf28155834785d29042b10318c460f3090e3f31189de1add586b25096df23264f70b167a5d4dd2ffd79c111e71b8e3ba64a07fc1ca5238f1d19c5e16cc371474685e38ecc82bfdd7edc916dd8c37bf898e18e1e2eca078f708b863f6dcd6f0ae62e9a0128cf13e0b19b720b348f2e1fddc8da21f1f7fd8ba2cbb5c3da826772171d8992034c3980a094fd75bc4d0b279b6fa66ea6cea1839602cea749db1cb968149332298d3a699425ad73803892b576e184e589ecde2b4c9d1d77778f43019631e903e2a0c00ef7dd4e154a2354b8d5af2d191911657f7914f351924a8117309a3a32768d56d8016d5aec17471c597e8b4d812c7d86dff72f62d7513ce07c9a489c55127814493882c50c04a0a6ac6eb1ff76e8220e0e3b405092fda910d3129e57b284806fc3ece0548856d999cda6e0a244c479b1443e7d0af6f7e1d9a14b942e2a451ce931f177d7628386e2bc5112030ee5cc459fb445f92e43798fd07e1a5b09c796ec4f012d4d245f336e80c756c2117e962ee79115c5acef419b8e38531eed5aa1023c62992b38a77b3c3fb20c5ba8b907cd787634d370cf3955e802db1e86e31d3129fbe360964d8b0ec76fc41e2feb7644db9429866cb21c3fcf8b43d145d70c71c40fc7756c60de428cf93586b43f8a99c78a17a325f003d672fb96f2b9ad9b8fe3372e6ee3775b3eab667be0648a9f9d9e2296fde9ce8b3eea2826aa7139a594dd1dae17e80ad1c0805bbc40fe0a923240e011d3306dea5c8c693bb2a9b39aa26ffc8164874176c42fd0eaa973260c355b71058fae0b94fd5d2d30d1f58136317e62d19838a9993ee0d1871a0808c883fcd5a1a4f913974c68093ec0a3036121d0855c7c0f72d19b748ecbb04317722019d439fe0e34856658ce24f625ee2f811ca80b1e6ff66e5627cefe9cddd2b505cbf0ce9a9922f0f8d5ec3c1d89382f6c7cc03b334411c75ffd3fbe60e1b83845203c7e5f0d66551f74d38e27a1ecdfe34e39e794735af976b39bddec66ad75df51827feb09cba400c794689b71a66945f312fc2bb802a28901b720b3ac2281fc257f4573710534ae682b2632bc15d0d398c80e5150651a6eb5ce81e13242f85aa6b9525a09f9eb27fb6f492bda8ac9cac99625fb6dda92b65ae7d021bb94c7158d69ab6597712fc0e39d69b2487c4f5f0dce9087d5124af05f21041eef259a495acd8696ec7f63269530e92c4dfae5cbd9cffcfe0ea59cc9ec72e62f257256bfd90c67b55928675d9b85b3daec7acc99c0a38ca9b224e105dc6296b52d0532f60323a9c41fc60952e625c46478bc31bf226d204312547200f3f71ae051de792fc6dff74d246373672793f0021e9b8888fef4cb504e29e594121c75d0ae5dbb76d823ec31011ebbe9e77f37fd60470fe953e79cac2aadb4d2ca9195e55759c39e1d3e7ab46ec0a35c5a925a5ce763feb7934dd7074ec2da46a604f867f41f192dfb143f8865ff6ed2f22895e42543c0fe1e6bc96fc107e329fb9f70bb1a170e6b8697e02f8304e609b2c56fda0907e1f2f97c1151898b47c812bfe99e4923c64cd99ff5362d9acc1ff2b0fa3cd0e7a0c0a17b139e477c7094fdb10f8a48b145c761acf9325882cd299fc993dcc333066846bdf058e1c2f15ca14d4e763c5768fe72a0ce3c4741fe6a284eb8e4ce3c576832cf90e70a2db78d3b916be38696ec527e964478c89c01d985055d8690db838008ce83ac49ffd6a54b17210c01263d080480015d863084dcc5045d8690a707011172879787cc19e8974b32f52070735b0f0233735b273d146abe2eba1783bae842be298aba9b4da526752a9b525a5f9c887d014bc801367b9a4a14279a80b940699801964bc80166615823484083213017680c4f60f56dd8d3546410020bcc051a640a58c5e1bcb3db6f3f602ad4dfbeda960af7b7bfa1adbf85b6c337d3ce9937eaec6928454152602e4c1dc0eaff809520921004d30f745eb7944cb38606659634efeef4349510ffc31d09765c9c94d639a773b9e99ca655ac73ce49e7db9a8945496968682c7202ba35078b1144a869ada0f954ccc8b062605ef04ae5755c0ae503295a82642041102f425e8e68974296bf9aa54c755af57fc0e6572c69296ebea37edeb0a7097e2ebc9c4f14d01cf10d5b36b4e17367b426f7b9bf719f0b7b9ad48bba5768de5c50cec4ccca9cf4a41fc92425afb908ee9837f449c6a4cc9778e0eaf7df0af3dd4fb09b313a140be43163a8c45c574f7fe6ab8a4e087b2b50b133777af0936f7217f1604be65e64c9e590322abea7ecce9995e67b72322a3e19159f7cfc3158da971729038e171c3f1c5074b1bec8feb2055e1044c22d9a3a431b33e64d7d1bf3a6c284a30e168ad54455117ef8bf4781df7b8e979797eff620f00cd852f1332ff32bb0a5e2572f33d781139ce901e79893a50ac71e35f770b1fe8ecafa56c5e4c028b13e0e1e1bfe9a9ffaeeec05c71ff982323f4116e64fd0fef5c2160defb997e13df73f7297023db05dec68be3d193af2387ffb9127a883263c802c00cd76c7c5fa81232ba35e05d8428cd5d798799970b2c06f258bb4403138ea6015f1204beea4dc453c9065cebbf92fc4a9c3e3a30bc7751a266303fe11e7ef088f72e8c3cfc5fa2ba4c063337d7d5964ceb013028fcd54bbeeb9cee3c2560dee532f83f33c25bd1853089cc3878e1dfedaecd7973129f3d70d5b9b8cc320716d01dc453b78cae32d723262819b94524ae588b3a50d05e33ce58c5965e763eaa47ea27e7e0a35cff933acb5da7ee9825bf551dfb5580cd6aaa10dee533f93486a8194186c8a33fc557f9b787ef366b29ac06f43239ae7cf9fd12ebe3c5f9c3a3492a8222c67be122a74d5349fe6ed5bdb37ac6bd9a7d97a3fd6b450620bee67ebc94b05e1319dd26d2c2e93e55806fc3eab7f26a24c702f4060fa5cea03f8bfde9ada01d770dca98113c610af78472cce19f0a47986e38e588560f92c6647022c5d738a734ed9dd8deb6c3aa977777376f7a6ba41f4a4f4c739e79c60e7f4d7bbc94c29a594527a27a594764eb04a082c7f86077f992b94526a53b06d38b86143011cc34c679dbe6d281cdc40034bcb414ab073ea8715b0d65a300726eaff48dab998df343c4f8c31a5ad02c7b48e346ac5208f582ac5286523179728a501f15d8925a6dfbf4bee502952970b072bf7e123614a536992410750f2fcf0eb5a0b72b0c18f0d3328019509022338bc3448001d24c9f3ff26f181868512511491e78fe0c5984e002451ac707717224802ccbf51d09083a13cbf06cf0004b6bc18b4cb730b972d3be4f92bb023792203c512793e9d4f73754822530d050de073779aa72962a86285a80734cc0f24a9c1dddd5578311c0a2101b0edc5a9d34a85ad14e8cf4f8537e6fc1eeee96ff466aeda2e05decc6d5d0569e62e9d6e656ff7aa78d87207360c860896d1d9863c649756eae9a3be956afd6da13ef53705fadda7c01bf46fcce742279a37db57cf873c806574ea022e3a49d139dbcbe08073b8b8fdb63d4c0dd8860ffd18145cd7409ba527e347bedf20dd68965e8cb631df82324fb0411ed0060d42587e2b85f9a85761fe8dd4166ee18dd4f7f8a7409aabad5d162cff7be6a79e8637baeff1efde82d3b6a8df811cf81355fb4749246bd37a976ba97e83b5ea5b9884b5d74a61b5e2be03f457a10adcabc20ecce7c2163bc74a991aa40f586113d65e8c560adc7baf02f79ef71da0cfbd0ade7760bee73029c5452b9f748efd0bca22fac1946864bfa603b77ddf18f0f8e97c351f0c0cd8bebcf7a2e6f54b3357ad6c5aab4a863b57cf2870cb9c7b17c444728a88cb3257bb0bdeccd9ee829748e6b6abda72db6c4ddcfdb619b758b6cfc5d8267af1d75a9100c5d82624324a848408d50c09133549a936f15c61c263014f2e562eb5d600d4923455f74ef6dd2840589f5cb45c5cb43132c0ea34420d079a94414f59da980f8e4a2eb4320f6d6cded8f721613bb27f4cbe2fc351ca48e51b0e651fb18c86f3b8b11891dcc36b7c990703b2879246cdfe15ac5cea53b7baed907d62892c9375721dd4cc4af959fab14356c92ed92dd628db6fe94e4f40b1d12a59c224a83a5d61f254b9f8abc664d67e4daa4a3a5244f66b167f59999afc850a4f98a02bac3517edcb10c7456b7fbc20e0b1d66a2ddbb73fd94a97e2af5a1b81360ee2b858bd56292314a454a5db882e1b11c7b57382bfeaa7a8e0160a22e4a6ec7291db401bb33e2e9230fb618bfc0bfad6851db0b4f0ce9beddb4b3d172ed99f7963bf8542dd9aad14234bb2f5711d4b8bc5624060f66f70196bf2d846b51e6c2724c5c80af2d85cb245dd6dc85fa32b96ed8ff6a7e6afb12ed927fe924148c27e8ff628db5a1e6beca7c0f92e1bb33ef6c7d2ead2bf7dfbd2b7187511a2706de333d5101b72dcc3c897e00b4c3852f932f3031e258d2699482019b4a3878f9e997715cffaf1eac8fcf62a401fe08e79e3bfc27dbf60a53f1506060606e6c76ec2f9a53e56adfe05c4e1d8e347963288a62e97e8ee8f6d77058ff74a59651569be306e9d8041ae2ccb0f1d6d6fdb567b03bbeb46e5bb4e185d8e53734b390112495c5bf9f625cddf5a3b24546d8a6bd93d553c384c820d4b7d36d9e5a2f5509c8101fbe760a2f7a9f4e8e3f88dc5b12ba0f8e8e1af5187ad651dbe80fbf673ecc0d12e6ab78fa9733b47bb00b3fd1d5367c2be99fd9e2b7e70fd16382e5afb2b6cc038aea3a31bc56cc11a1e5b0987bfecdba780eb489b442d9ea18cb8b619fb3e7727e72f60c3dca660bc8bba2814eaa65029ee2f57ab17830798e7543d68a6654ec6704b49c12c73bf7ace639c3003c64e7ee12f16ee735f6b707f514df0ad0f63f5a9af7f024763f5dc77e1b82377ff02f6bc3c067bf0e3898201825b2a386ea9205f5e7ec3eeaf50ad1aa98c7a54c8822aeccea19fca9d6f17f6a099fb1407935127c0802d5408a37e535f6604f886306082603b6b9cdb2db44c47f8be5fb75a7fbb9755698440431418f4391c7fd1dfee9db1f5acf001cb980f17e907c1313f6f680c88e3226585ae5ba5943dea2637d4c6a25d744fff3916b6b7976b18709419e6b99d1d9d56ad1fb8e3a2e1ef47978c16e18e8b9487d6b75767aa803d12f61b8cfeec47a552a99ea37d8fbf62bef516465fca6c9ebefc993a354f5fd2a68e084fbf6fc479b55abdeb65646454c8a8909199a1a1990169a6915e0b3436a618ed220d5f845701fc9a6fdd60f1007800843d2cfe078c35857a3b72cc6ea68a8f7601660aa3dfb3a78ef7321fa3e2e9539affbe82df576ba5a1f9efdb8bb1e2fbfea3f97e05c8a36968c29d185fe85e0c15a81e34cf3ccaabc1802e5dbae4996fcf01302a409a67c0560d140b308fface1ced58803d2c7e46cf086f63ded00e840182653ee6e9db842d15e60824d47c4dd80116ef8feadee54260d6d3efeb4160995678fda67ecce36781ed624c485dac35dc1ec684013bbf80e02897b4403d1083adfaada7b1180c045b35b421c2d73c006826308ac1a68782fded3b9045e7d07f50722efa3b74ee205f578d11409c019a6fc8428689e19145a62f025803b6401bf3863eab09297bfa94d27eca94c6fd50e1333c00919032832cb52465e614534285a41c71f29226c87c3424c89020f70042a6d80a9430a4070f860fccd061cbee96dd56f67c49dd1fcb981358ce6c4ccaa48fa44570dce7c846d5d4c8e0484d48132260f22925080210cc41f90210183785d881101d92830e866a3741f9020f503cc1383b3b5ada5c20850c42d7ca0fb52030176a8e40252d91c128d54db0a7938e3cf104cc85991e7c5366dd151b9888794088024815245bd5800351d73c5003a617214722800122c4034b2e04891630c11c6c262d3039040a2a46300777a8c0da0a0b74688239b8d304e366ba09ce2e410465618239286946cc605b12488cf8c10fcc417ff202198cfba2189d4088a69b60436972d40473b02606306e05cdf7f50a118238cd408339b8a305e6824b7a04b7bd70d16b5e4e79e9c5bd7950f0885f5880b924754088f7abc161c99fd9bf9af952d662a2c0e3f713d45185b2fc727f353aaa50c61d5e1292e0f1fbfafbaf4a6288f0d83de11c3334444c0c100632c2dd9391bf466bd43de5277f75111725d9bfe3e2833b4cd43d61a318a0eec9a87b1a6380b2c700c5047531444cdbc72c652fc135babc9ad20fd8f86597ff3f6046d95b296c4fffc6f6614f1319dea07f4321af6728fb6f1e50f6ed67def80c639462b0d830a61663042321081e5d314626688fe6afd1328dde8ff7e394fdbd251e9325f0e8c2b14bfe92de8fabbb3f5c028f962977485df47ebc9f3006d87ff47e688cd512842c57d2269ac4600d6b32df27168771e4b1196bc9805b3756983733eadbf0850e98763ae09d1b3b2f62338adaea69852030b171430b0de8095c33c3f232b94097d63929d4d080b10c86ce71bc7a9abee328990c4714d1ea8996574d2ecadac87f06cf8c26e17cea95e5007b8d9158cc5573d195574a3082108109bb4cf306081ec5d84bae9662489c9ae27497c849d274e4f81cff4c396034136b47bbf809e7e70aa1c8a8548d3656410cd18c0804000400d314000020100c88844291382c20d444457d14000c78a44278581a4ab3288951180519848c31c4100000010240666a68462000ebe665e0446cf23d2e6a1efaec586a783271d83f00dade271851b2f580454f01a83dbd6d525bf8e45cac0428285b2ab7f5817d5dee8ce122ea3da2235c7cc6f6bb3eeeb5359514573c6631503fe45d8cbd014f192ef29e619e5799cc06850e93611d4cc197d7e42ae7a825684d77d2fe9ea1b18d7f53a14d0f7389a2018c500690bea0100159fb08d2429f1752c87ee67c5d87ed6c9bf888f97b45e6757e722ddc0f48034ee98b8b9ab7b6c651c1b126784300ffb624bb942dd07a560beb42fbaf0feed19a21a984359725207d89aeaf6e539e2c1f16dd4377c0a554bce3678ef593c0bf72c2e687961804101d5f06818d215e89b0d989346880d8cfdf222270db68d64aea144d9b56df0a022da91f75b5551b87e70037fe2922528c2654d68e05e6f9f26fe414ad8e7452f4e85a5334e803fabab7849c811d952a28908c6f9fe690d1686c827078c66d3088089d86cc7357d20a8ec4d1bd2ea873e3feadcf63fb2d82d15247c97b20e3dbc317af574269ecc28a3d1fe448bd2793027a0f593ec8cf1cf8ddcb87cf3cb3d4585422258fb79437f92ed722401bde5bd5b89cce02d39d553241215a4f66ff2a17f200dbbadcea1147db25ac2987b12dd88356102682bd91e8845bc27a6cf946384c34acdab58decee3490052103d1486c0303a15532b2747f189e42c69c07395645ef81258fd4a485a57abd462dc8551c130e37f7a0505e28978d0047f28842d2b233aa933cdc19ddf3d929e212e21b67283f0fda623a3b327746df6ea2faf529c752d102fa0bdd2a5e0c518f895039daf762a686efd3ee2856ddcb9183d2f57b17eaa7a492627fdaa743fbdd88e825f776224baebd683047c313b4eae69e04b2f9a972818c203d46a1d424e958f8e05339ce076ba9ca4638e08208a1fa9ccf31f9493468080a0d38c0ac8f167e891c8434c954948582e5df27185d054c8e5d04149d40c8007eb62ae947d0c8aba090b1098233516d791c197ee91dd636ef5c099b29f74a999da9a11b5c2004160598035815c29b652d709f92a324776fca477ea1810c2cdff3cab309e8bdca1620b038df73c1ff7516ab9520a8636f4f9203256e5c8d06e1ef1966cf6dbcea227956f692ace43b01f64e48ddb99f12b86b021bce6dbb5d146daa7c54b74b68b9957f6008275b05df108102c879a6b3a276edefe8ae41d5ea1d011c0a59a1c05f5046ae9d9bee2a8abc4970747c502a8d753a3487acfa8225e11bca47040f1fd30401a2fc8c3cd3796c06119ce506b23303cdeaeb410ae3a8b533e0d6696e848fe26b07bb0549b19ed6b66546458abd1f80557ff19bef01e55e12f9564dbd7a0d711c4d2b60e9915cd613f0b6994cd865b79a779fce211cf22ce02505b87ee0d382b11dac45c29bf7cd35713db9a1e536807a6627435934b174e5181c800f86ebad433a18b2dc423a2198ddcc70d73d7db99e17cc9000e78b780c4cd45a4b75587db4765ba7071d8ab147cfb7edd3b7c38515cdd0f200198dc8e31c0cc0b60230b23ebf3687a48c4bdeec73569e083a95446926b8c34711e0792cacffcd32d7f9ec7465709d6130d46996bcb7d908ddd9fbb70248f3ca3a70cf35a80b05c004fdff769d803c509bb0f7c8348329c34b107d681800aff2721e9a29148a1766e93cbfce0f422c667a8dbf865747c8231bd04c903de87f2bb0872d2b0407fcba625d5d39beb55bc53cd1f084c8b9117dd6d961b613f0c5d93a24eee60de1caee5a0bb600ad13b592fe76495d5d1f927461e40a4049fe9512b538012c8c3d2224db4e49b655db12df9a1346208acab710d90dc1c64e4fe97516221ebb69de0948995684705fc978a95a4cd3e38d4d0f77460e96ebc6e4e68bc6d059f00bbcb100c4189dcd7e271974cc7eb38ce8232218a53b24bbe8cc71b63148255ab50cf403482642dfe4b9f5229a765ecbcbf639fc02de0b67b69742a40c918b1c60420475e5b313fc1b7ccf9cd206f30eecdfeae2112e63191b56dd30f80acb282284210835453926e0d40660ea3e2046bc5258288c9ac00158d7fa4237c6e32986b896d34ed52ebf39628c29829cdf3216e22c5b0a19d6f73294a3bcd0e284b4d0717749f7c8b7f2a115252d364b3647e212ae6875c75d2252e8040b0a90c1019ec7e31acaabae7391f0c1ddae55aac98c87f6e654875ee9cf9c416ca8470676eb8a1ce5b9a6a0f1360a5e016b9fe26e590cb2459f0b805bd32699771e20ebe96de32dbe41c1c649893278fe66ae129a8c38fa26436169859660077957d51d54b6118aed293aabe4afe11d5b19c114ba51ddfa22d350fc26d04e1bc49269f8225f8f3869242b9c4f9bd9d559118f3cd899face4fd1c5dc92e233461ec50a34a5eaed4c9e7c208815ddbc51706e2d59217f16572074c56c3fa1a2eaada005ca5961a019620f2895e2ab443f96ad59ff2a45f8141d16683cf3828ab256af1e1ba0dd01531fe3bdf8821b067b1dfb6d0741b8278fa8f1648690d0879b8b9af842864ce54deaa381eafde5f228674afaa3126162a0abab4e075e505095567c50b3cee74451f580e8779a5c59a3fa35102559bd927d183473f9cea4366158eb729b13966a11f0fc4e016bd3926fb31b02dac8554ba17d8879ffdb93ee9f1728a9a621506d9540d95bcf912091000489c70cd37f740dd23f12a3c70dff3f15521ab11464553d2a64cddbc8394976bbe47a4a1b39e3bb4981a7cb7dd7ae3e3bd90651d0453e80df5e61c4c22d6c70a679186914d3c8a272728a714cc0ac129b0b47297b032bc2cac3ca0ce4db12dbb985aa2f702dedb8fabde69bf6fff69bd0a825ed218a58e82a65c0b38e1726e0fb4ce9f6c8751a2822b843e2868c7e664732b57295ea289dcc366ac064c09c616ec240f6c3192f7d6bf7652b42d990dda274624e4c82e2fcc41b5b1c7736bd673de0e3d647229275234e26d757d3d6a347c25d4774b4b1035421a476bda84197a9df7bf97893d13ea5501ae7183e3817147c905641211174083b45a8c927032b6b63ff4f7ca38ef98ee7e431986fc33b3a5bff1f6550f3e26fbfe44bed82a61c35993e7d1eb5597ab201395ebbbd5723ab69d13513314e8455a4ff740712f18357100075417eb137b7f67ec7a93422b49ce18e0fdd0d6e393d8c4d7d5895123d769f1337bdbf8ca0f70e26eb0d625f8ea1387c8db2ebbe50928cbfdbe116718b81a25ce74d48e20e0dfcd6130a06d4c6886527ea3de5fb75a69c91d3255e49b6c770989abbb3bfaaaf6f18473493f7d61ed3a074c1245db5e937b774cec8b43cc3b3cd3bde6701e425eb7bff99e071bcf5818ad8b07bdaa55c28886617c1b7df2e36130c2bf9fdcf5b4ddee0727e06c73f2bf7003a13acd924fe835437b13b46189f7e7118ab3c911dae6415b20e6b9c6c16b093ae9b86882206ddea4f535b925b84d90a87a344d7bee49121358d3ae319e69aef4eae9e72769b1e7a32f2be4c94e6757d24b88dd6749fab3c8d18cf80109967e09a3436441c439aeb2f42273622a30270fed77c3d7316665a701f7ef69a89f521e2e2872d1972f3acc7d4ea2bb598e1e616a829ff84166b971b0911be397f7d91e480b8b14f02faa0aaa81f56acff0fac3e879d9d6bb1a0b4d259412c733f666445f84bd277156b212bca195ddc2f1bc4ba4cd093a32605a4753f8d388fd32fdeec75da9a76931917c960e0666aa8bec45aa4ac2df3461971d1e0d6b9825826582cd57e9295c8aadaab65a89a438b9a6f9b24726fd8bf9b04a7da986beda48d70d1ebd2803f299b904aa09e75fcf18b7d26d3b3d80a855bf35383bf195876018183d9667d49b76fe0d311ec33482b4c489af6907d5cc33d94b92987de2f3018f2272de368a489f0adf74a5cb8ea43c81fe849f2f973792dfcb741eef1e37edb0fe19fe2d45f3e0fdfc20b755a83d9238346ecd9f8937f9963b17a06ae164b3abcef8122e0dd853ce65548605d2605dd1f648378e2fc4438a27d49851ec38803e7d05ce3816f5380ca0c544137e20b0b0c2c7c8ba24c0c6fa5ea22a4ccc3d4582425580eea79a69850cd4bd629e1a208319e338ccb0fcad8a2cebd56889d615368705cffbef86cb900488c9492f1b9d9283e1f62747021db74e273005563c0c8544ee67b706368b4ea69bf6022f331096831ef985e304ac9b213a73167f54ca6a7c72d1f3b6e3157007ec9e4117560ff5ba75030db64247fd9c799ac43b19eaa7bfa31bac931712061d94c4edf4a9cf410b331fbcc218a1a7bf58e4052744d70ac416c2ee4322cc87277cf8644306c4c4a16318570d42f4a596e74b06c5ca9b74ed1febaa6c847b120c168408cd76ad615a781e14db87e3db94901985bb820b01e1fb90ad184cf0e415f430456f3be8abe37ce4659d412251c949a6ffd0a845f67ec5a872fd522b5f2667d00352692ef9a48d0a732129b4896fd67355d75fd918ecb57342407e2d4c3d694a7f61a00c11b4d1080bf9ce5399946e86f846ac7284e8c599e0371b09eb1eec3906efd5fdf80d5192568e2d26044df6e0601fe0f83a47ad505c9f1f30489f8523b38472fe2d33fc7be2f4524bb07dd542aa85a8481ff6e8e5a255f401e1be0ba7786f87d38a9fd9e8e4d5c22f24eddb129118a60fa07493ac5e01bc4ca8b511c5823937fb6a6dfe0834b338256f5fc9563d5bb73eb20670083189c744c130dd47ce53c122272833865990fa16e8819a166443b1093dae859164bee73d338c6b605fd1424722a632c37f59f62d04b30ca7436cd292e6f281d8dd0e2dd2d8bcb02e6de83b942097302239f0ba4b2e04e58e1196c60b2dbed24ba088dc073c59ffcac25821912fff94142767a916b48425f7e141132d11c129b481eec022b5124db73927986d13609a9f05b4424606f68ef911617cd17e181efee72f49eae511379ba3748ea918b9d971b0cb85e4c6f0b7bd1764c504d737aace1af9f153791a6afade8c4586d1e30ebcc797b628349400139a3fdadc43cc1b905245684df043c7b1f29cc8da21f03c4e11e5fc63745bd5d24b67c1f87b58b090205127adc78908c555a9f7cb72743d939ae9611c912a7e54795c971f39f903870a825f1c2997f5ae000988bb8a1f78882d5baddd7e6688111affb1ce759ae5d7db86d3519cb6920dad8ea66d41651ff44421ab76f6a8f97a43a739c792f009c89739c050489244b43e59e25620de7ce00d4ac6573d15779f283f8a36c18ae48184394e28d2271dbf27bb124776c07879dc7bf71a440cf75c7fb18a0ca24f3342daf994e973a2825375b732421e3879270e45a1aa9b5168eda945953ddab0e66989aa135345b9bdc53df7d0ea84ea798f92f57d007069a6df186bc24418dbed596d1b19483975a9ccfcb8f262b0cf519ec6611c5a411a2fa2a6e228c53e3443db6df4dbe7d79174ac7a6d519425ad7dffa8aff5982494339a75bd9c18d589ad24f1955ce1d67a29a388099a8f8b15929bff0d93cc31552cdca6bdee050972211b9d88c6631630c4cdd51171d46aa6b86ece4dbbeb48ab5a7876a7b2a5be28de1fd0cecf7b077f1d8fc9ce2c83d640a49212687ca89d9240551f235a6ece0848e5cb7229594b53791c47e5d73e686e219b2d6467a1ea85c02acef46c745d3402db354055ca95a13c511aa0e7ad8f517cdf8730db68df23cc991e27800041ce890351f0d5969f851f773676d582048235416769f0caf72353c65c1f58f793e7025a805ed531538cc98831c72c26835e2e810f20d836ce39f165b71aea137863d99c3e73ac81c7868104e84a88570514d98360edea3dc3b5246cf91a2faf0b328a2193b7dfc650949a378e50a2b809c5971cbb18acd79c8d98e77508cb3fe17dcde9d97aaa24267a62ea3060fc7ddcaa08c876c02c1c826ac42871d7193c122869431fc0b59e0edb911b58402f10351d396d14d739bffffa2a56b377419f95d41f280c330f5fb33d06bcfa9b58aab62806524dd217a4f243a97ad4ee9274e87d04fadc41c0c477e9f9ff780a40f563d297267ad58a6fb651fe721ae5df3b4f06b323a7e2528d4090fb6c187b4e6232b72dd5c1e1527f0e1bcdf73272b1f3d38af51302c7780a2f2481e8598182ecddeba1843f13a0f607311f73d2321d260672bef3fa1726eea02f22c7861bdebb4600904a57f6339cf602647481c2d7724a8faf59be7788f2bd54c903a70c1e2f30c07b72ea14a28e9e2b852b9a993e58651952448def33c4226d288f2d7c885be765ea857a145c705a9048822b8f8db8242766a3100f0593f07adc38212000e68cf2c4e17d6ace956c0e5f7e4fc6296ab1503471a6b455791b1dbd391b2541fe6c46ef2bf8b8448f62898792e55778d28fb66bf2128ae9cb07f5cc796f3bb370482117bce36381c5c18b11a8dbc12d5013602f8f4da83ac33365f0429dec6cc193e0bb24f9867a00b9a2fa8f83db099233d0b44a7eed09854eee5dffd73f15e7c5c6f64dc9dd4b798e355902edfb78529186ca447db98f543edfd1a3ce99057aab104e5f3d2b70512fa4345c706874325f04ae7427f758ef43eda92e33ec5925e52644fd9d110e34945fd02b14e9275a1640f0b215414598128552310eeaa903c7a099e8547acd728e16e457f7689cd3f5ea981cf5bed5e5035566c30f1d14d55ced075faa6bd25d44d878fb86f0614790926ab33060f331f95ca0b0fd51e88a78adfc6d5556fc6c8000dc56cfecaa6ef4256b745f610382f5edceaa5b668cc7310988e64f076d98a1d496b675885250c24afd9fa034117d6351006211910980180154efef8abdd971dddaf3245ec1cfbe59925f67ad7644919b71bc25a7dbb03f490dcb26d0f42c9d0ea5c39aaf5889b265ca45cbd5cb0d258ad26ec1766018e62d8ee30c48f62522fee4d008065e7498da07a5b14695b1e2e36fdaa254e8949cd900bd3fcf6fd64b5c00807f1db6e67eea235a553a10fcdbdf5ddff86cdb5b7ac7ac3da409aa4ff06f4f6cf2e2184d6fb4f7dc2429dbda0a9fea2aa921a16e475cad40276bb9b11f0a0d74de645bd8d37b74611143b99545717d5965c3857da467d7e59da7d1d8251061d21b70c006063623f494d017ebee2d6d2feaba2c24562a0856da5f2c5436e4332b4906acaa8731b42329d49c5a96997d582ad4776ab835e4192348c7a56a5287a54f4a98e8f08be228cd228626a7054af6d1a8fd98a4a81b7cf350e8103d2c7e9a191642b00748abddcd19e760bb54a17d7e9ec251ecd8f1e9b0f17ed1f15fb2840ab19b2c099b557e0afeee731cf5c779014a2f1b7065be32a0ae10e3bc51207d11ffcd3045d9fa7c83733a0176e288d0ed7c8f4040052450a9d953f0a56e6f5d394d9bedaf203d0c1b32d9d072799319dd51f69515667bb08057567a50b716c2c61c03f2c88ade2ad640fc080f11795dc8205b07ee33f33a94324a2b409e42848e774ef38110362dc6c40b0c9b2b0c8e451518c0be1f357127eae9e7ebff1e95a4e3828f6f0f569630c3a1c2ed746a3aebcf98cff4700bfb747d154d889689c38dc4f44939abdf096e2839ba633fb5b115abba4571bf08f630d02eede69e8fa0719a75cef2b640bf0d9fbfc2134b9e0a4a12b008140856693ffe5b40bc7400033e06cbe14a5cd917399a7a97ff17a2298c4acd888ee5eef178e8e98d0661939a2555ad1c84399386b126ba084b3680fa0b8f26308b339f46a26f288c13120c969beb3462dfd6a39203cabab92af58ad3ca75f071208409df02fde48ab905aede74db1130bb0b4900b315ce0120e2fa5d1b085c46473fb3b81fcb16fa8a73d5b68d18ebc6d1f8d38a065f14257ccdb73adac21e8874f32c0825f03d0257e38b75ea752d53606461552d80b6340be4ef2e27969d096193c6f8aa000866227da48ec95f822b214b41dee3fc0582060c50beb32d947831df6810e07d1f5e4fcaa42c4b6d2a345d219fd95de876f5a945d6f4f5811aa2440cd935a0947c020f26832c7cc64022ca1944bfd4e5ea7a9651885d1a08421eb45b84e44be5cdd3cec898f17bcd90593fa695e1af369d0919988ce79737d5b281139c00f3bf8cd65cd7ff598b1528d6c05b244af0e03782a05357a49e35c0d32befc22068df0e650809a4a341735144cacfb819a35417535dea380c79d37597531ae8aca9090830d289f0901be3860539484618ae5f7763ac42c9a79ccacb66c2ebc4413dd6b5567397712f2fb99bf4b8a584a3fc71e0151b6c91a7b9414e0969f8d2971c4590d53a294687ad5cf3ca77ba96d7bd8db31f99b4ca1f8cd99bed0d4504c0134b3ca2952f71d5b4bfffab342da901558fa497f4a7ffd8814f71193603a91d71b1e075b3db347f5d6e5f6829a0c871c6863dbd0582c1f4938b492621d87506b48bfc95041cc51f8a25173242a8421f932f2d5806bf451c766fdccf2541905427095154df74a53b8e714ce559a85203d597c90a074a10ad4845081731065777505a9e81ac536962bb3b3cc0b7b521c6196541ea8941930266e87d280b655d1a0334cc4a3013abd933b096b352dd36b20e84eedfc38d442326000e069a52b1d976ca32d6a9ea7ec2239be7fe7096ffbdc5c14018b39fdf4726225ca38e693d3ee306e1f770999363ca079f8450267c56890cdd8e75a322731ae240e59cf31a9e7b7144f25cf74381361cdcceed60643051c61cd0537e3d87a6cf229e6608662050a85bb0b49431eb8930960ea17446733ea643d0704611dfb165445751734909cdc81a565a465a47b396d576094bdfcc96329f5126203aaebf30503469a50e035595f7c9381211c482951aacf23abb93180c3ce55f075d3ae9ed4f52342128081a1c7a2e2b244a273116d300a13cb3b1ed1f47e748b3bc3282d38e3d4b80f55d3c4ba317840c2b910d51ff530fd5de9cc55e6227ff634d3d326c9494eab34b093efb1666e879d76796cbe2497793e44e08cf1e652df10aa37dc04764a5262b2b91da1d1b7df9294be9169779824678d7c8d6cd17bbf5d20c42b1ab95f5dbf2c9eb33eb1b505ed95fbd33ee6b150be589d340b9b447110cca5dae5966a00bf1d752bc85abc8a335aec4b748dded4bcd0bcc0af2b97b9b04f2df1fcfc4249613a28bfc8471e48cd3369aed4511654fa84c004408d9fb262d9f1a3e71035cf399d60f17f81e2235e68512009c60d6f57ac47f88d0bb1b7f14ead3723a810cd96418b7d7bdea590567f96e5242bda035ee18eaf68f3a6b2c35e126c1f0bcfb35e071456307a30413be1ad64970b120b9c95c7c0a6907f664a8d5758ec178e31aadd0a4c8f9658b2c2fe1ed0a85b61ce010bc8abc883d97e00da474d2c71f01dbf7def97bae95f13a34ed88dbc48e09b8afa0a677eb6d62a40dfcdbde44be1707878342b15b62ee26c3a5a9d3ebe0047a12362788d3120312c82017c5112ac3362b22ab94a52b107765560d53ca7d9f18798163b03c0863b414dce7ffa145062e7da7c8608c9c1e6784de269b07ec1bc05915502bce745205138d6050011e80d2e5eb6171289adc816fbbae6cc032dcfa629b31628e02b63eb64b12430262fc2fab0075d3616e59d9ddc84a785b2fa0d96ca65b55fff82617ac6d03e59dbbcf02c8243d1d6b24b5c65f53cc760272b02067bfae9c5139f8c633a7b8273821496af7f0f0e923831c2b7bdb047fe7fe78f9dfe498bdfd23e044fc2a717d15a0eeb1b661ea9a6b3ab589a642df510f26849edcae0a9dd5f1bc48ee4f7e719f98f19c3cc8eadf4e22bb8d8740a0ddeffc0c7f12da0371560b4fc67f767517f47c0f8d2bb94f1286990ac79996158260d8540e3cffdad4f733e5985dcb29fbdbd18c2c2f4945ba5efabd97db8a0eb1583bd78c8c0838ec243b4070a318dd777ea358b0fee0f01b4a633994c83fd1d4431919fd865763351f973028f2bebdd241a6521b7238e6bdbd1cedb054786d0c2d01f16061841507d8a04a9e86aa746e25f7d9a48e7ad77bee11c1e7c41b60a390d500a7777cd893f86a8dce5f9e64db120e921e7aa2490a90c5f0ff65fac594731eaa3caeada74d1580e83c738d233497f86a7ffb978e6f092e69dbaf5d2f4a7a384221b65bd10ccb13e738ffa628862ff307d0e7bd7ece788279a77153156122befbcb5ffa3500db565400a984a18d2517263a342caec24646c0e580415c57753ba6716b9d00c7ecff5e7ca99769bd0c6c026853c4770139c8f40a49afd931444299e3b5936bb9b2c05c917fe24c80278f83e3d02a3ed5c94e2b2907557b4464b7e038bad6566cb0e790c80ac9b8400682dc3f680d6a770837537a496dd2f2ce9126d7ed23240132a157c0704fd7e8b74a711a2861f84ada316e3ec8a4181aa3d13976651277b21881102f171aa9a26746fb3220f82a2c0bc0528e10201cae826f64efa1dc0a7d58d3357a087f410704292272af85d466742645624c55669138d682c702b7b85456b0ce0a580d920b84fe0350a5c7ae77b43b71260d5afbb3f0ecabb7f1182227cffb0faf60fe0fa0787916544502ec0080f3aa2efd6eb1c3d3d726937c978eb8ca2745622671ee3a7c62121cef44099a10482e046d30fbbf52bf748fdbe786163397573869bbce1264fa86846e3264d376ee2c0a0c966d02c3dc0a0aea7bdad16a5d51baf3238a5f794cde08eec5151fa7a5614f1c120aa27de60fb4469632bd1a607a142da4dbc0f6a5f5c577611c9d65db7a9f7e2ea1e5feaf20657b898430584f55ddce1ba343d0c369dbc45cf31be69db351535326843f8df0710de2d19c28d4ab51e8d565a4f1d38b4e1eb07901d5c936e572af46350b49f610b718ce3e073b35b044fc5cd5f32d908d6fe923deb2ff064b8a21e95badf95300ca461f356520439bd485c590b260ac78308a52a868a794f6ba1d19a56467347a4e7e2e49efb3065ecbd4158762560f8a78bd286a2cae589d7647165d55d7916b0da25ab56197b217da76fd66cd610a2c8fee56276b927018fd8feba8865a422e681bbc6e0bbaef6c58dbc10e580db40294d67e15f63960785c4b35c3f125906a69bf50fc6955266503f1783246ef697e7b7e9ca3a18ff5ef114d40683308f5529664c74c02c93e16a10494fb4e2ce3a63f05280efdea1843425d740e99a29ba560ad74ef97528bd06ca5c47f96b51f03aca5fff38193ac835d68a83e2076798ec6e3bef1db81caa3e2ef1c505fff0a05a0f1232efb09aac6d1c440c9adedc7dc044880e3a64e802e90aac199f986e857efccbca46d82e932ed85fa9f0b867aba4ce1598f0226733a1d3806f845c5d63ab3df0b3cb08b9a2369bb1c19805e9658a34e4be8c984fda307a59af33c2a36786d37f19f40d935e2ec27540103e992d5e8d4204402f7b56730cc0c5ad7cbdea75c5fb08f8bc5c01ba5dfa2543931dc0e7e518c93983945cadfdaeeedf5203cb50a700038df3728dd05329f16b8f67433c88b35ae8a1d1aa3df3f95e3d87bc4c1918af38c3abb1262f73cfc8117f81ca36fe8fd51d42a7f8eaec602c1be0f6abdbe8670661b3d2e53ac986b792c751026154f5b482143c3abd65126c7c4266e2f90d4835014b6d93394ff62d2ab2715c8acaf8f9e938e0e26cb587d0c64e3e52ac728a178eaeed04d7d351e16d325e8962cba01e7b5407b9e652e2667333f7972a704edb68062ce0d0f59af293936b74f6c21c1ff08795304dbb8e65725c734e94bda027b5b1f27e17658c3b7c544182193193cb6e5157f64120f6e104408e16708ce3c926ef8a1d2697f71a347bc0c17a7f6e1c80761285c77078b3b6f534cc37744d2cc49f7ef1726ec4bd277957b87682f0649559b196cdceee9c92a3252f2ee5b11d48a18e83fb096211629aa0fb1f413503f9686e9e09a5ecca4cfdc1904d22fad2630e94964b6e8d9a3a2ce41498d4bcb0aee3d2a5b75910700ce718b46c99284f3b1d27b88e4b08525916bab874025f17dca841c642a8175796b603b56bb4acc94148322de9f807818169d37451837639704a81b1bb3900678ef07d3aed355bb73d5fb4c48304e231a20613a2d5c13361afd2601cda023e88928443819d16f122b456f7274e578d71a51259f0a285bca56634842e18f4370a3f4c42e51034da59e2dacd48e7073f0d5c7e39b61b12a1da7d318cefed375af90ebb56f8396afa201410d5b84612e3facbae4d367a841c54b4381264bb0628df383d9024d46c9f621c5a305b1252c80657ad2bfc3d7936054e19c3d27fd1480360f89db7a940ebd170d0a90aba53469383596c3343fe2821188b6623331aedb625461d630d5800cfc7c01c570699e75e00fb784ebfe355a4c7c7ad2eed68f52880ea5fa1dcb1edadfec10a939da3d6f2d537b5a016b76a59ecac1c0d8e0c7885ce2120d400ef9e955048bede0bdfcf211898e65bee4956bcba2299e18d58d2a0a60e75bc435d779054fdc6a4e59f790144dce41de521552ac286f24cd3aa6f26babac4b7bea29383a294321e9682772c4731945bc78ee2e4e7b53cbe01830d8a508fe9306aea68a7a486be5b122dbb42726428239e30bb8c15694bb15f4330202ad7c15ef75780cc13da6a6c3db7340f2805ab4d0d3295cd245ddc606fe64f9ff10b223ad7b699054d0511e6eafb6325012b25e6f3d4fb6fc3c035619a701e9f4a7fed5b2842078e3a28e329848a930d36295e1a252c9b052cb029577e145cf9ccb62717dee7f0211b5dfe330037f399f04a06723c9ee927ab2d999fee278180e620d9de4ae1482f585df8ff57f375a3bd72c9a624fdfcb07aa58791b92ad49693163cbe4196b74021812582569e5317fe5528ae404f85fb0195a8868f56e0182eefe9f2007fc9075f80d2551d1c5c2846302710761163532022a94e9ca8c9fa3375c0bcccdabf62c6cb063333aabd23f087273748b54ed70b3a5859935b22334b9aad75d470d7d1659db552679bd407fb31191c7dfad636daf6498c3b200b2f024376d5f45f6f7cdf5289719cc686af69692a299eb5875326918f96aa62f82da6f6d5d029f990fde936e84512ba3a040a023592c5f0d1dfa8996dede511ef65516c6d086674c2b0def0ad48249567e4d969e88c6dff218562ce34da261f091ad2eb8fec4d733ca3c4a087224f60ca91b61eb0372686de3af2b3c13e8db39e98539c243a8f4456f57c4a580441e221b080d066c62face9b1667b8962d1362ab149d2f165cf83f9de1cd2928bbe5475afd8ca1d2d915a83d87cd40fba74a42104b07e38f749ad61df8c6fd611a092733f9999db20722a9f57c141e483800e3c9c565b92eafa56fbd78ccf406a62c0c2024e3a1fde7355cd720ca859e933178c89c58b017acafdeec82ba012800d1ecbf7013ac7e067c68f568c27a6950e005505976e989d9b893804951800076fdcfaa3f376c4e211bc8e7edea121cd6086e52be79f2c5445bcbb6cf75194057554ec5296adc26c4aba4a95758ebe0573719e4afdcf058583561e331f8f376b1c6fd5763678dfb612e6d77a4106426e818136106ff5057abd2e3c6ed5386b5bf2fb3255b01edd9f244da1cd2031db335c019fde1d4d393b13600b2c7af62f0f596d673168c17ad77820b3bfd83a7fcbd4022ab49405f122998b8d6b995b485837cf659d3b807a09230e6a874016a8d3b1ce0ce99d28e7e1a693d6e1dfe9e97b7db0948bae1843e0799365654ed549bfa1226c58eca0b95c0abb8b97ce54b76d6d5051d74a1d25b35b708ac9c37ceab3d4711a1150892f9799845e0893c7ef583ff586c1c62d1cee85dcd6e26f17300121931f20337c9e4130dbe5285a674a736249304c010220bdb69a8fd822aa739c7d5613874f50645275eef160f3ac9741fa1e1f5fa16478553efdca7b292a88225b661cb8cbb0afad2768ee536ed52e0b5a11cc57aaaa24e6313502ac4f6d395af1116e786d1f1e3ac00366ddbd696e217d7947018f9608b21ecaff57a3b93487b8bd4b94b696183215d3f0eee8833d1b525b04305cce926a55b352a489929641ac2eb73f74124683f291f20217523c3130d40b3e8b9ffec42a91e7f41cf42db71fcad196b3305db0bab15cf51f4d33e743179698b17b3fbee906defdbfa18141a70f269d560b683e39d18f17d1afeac060b63e1496632d353779b3fd69d4d2ae62ab66495aac995afbe604c515fc6b0d3d0bfeb63f070a0a74b02157272cb5169a636acde43a81a30931716d26932b856b56d8d69f6d21617294d1fcca760c7cc04202f723b1c70676dea9a8305f5394c8485172dca2fc340a2324b4e03a5a7c7573628f55197f73e6257ed4ff0a0485d5a6b86b0b2100ce18f8a2ebe6d9add354945307dbe3056473b8a3521208840e68c4373d2c1800014344b83bd61a4c204c5bd36241d314f1d8fffa7a07475ecc275a923f6003a28721252409e20719724be63dc270129ccc0c6f81b0f1254bbfa88acd7eee2d337fda7d6481b6a61150aa49e305d873dda2bf6ad9811bf266024ebbc58eca404e2649cc68d6124ef5ca95a3d1e03a1f0d76446dc114411a228c9eeae2a2f231d46394059891d0e9e2e468ac816f7bb1fbb665d3d416d382f1123a0affcd35c19ad7636fd748172013e5a219243562dc4a8f8e48d41a5e9be0e8c6211879a474c795aec39505d6e3171cdd21fbec8f0e32745987d26a96e3a550f5ff91c11dd85b44b0340b18a81e51518a27372436a84e93cd80bba2df37cc824be62f05cefdc94071405356bc2001f9d2b277f4155dc5019c029d7e78ff5f307cc7241a0cd658bb68a6ab7f961423696457d39cca7124f6fe435f43edf4b9b7a1637bad167799fa1b14064aca735717c4b4048e31e9c5a29be950f6f9fbd84bce80efe7c11e626cb9ec4408f0dd4d08ea3ea6ccfea17210bfb9ed62cca3ee66e48365bb92339ae22983be17f74c5ecff37950cfb3e1f3e0ad32fd5892a881013a640189c2f7efa7bde3ecaab7dc7e43e21de7f664bc9900cf99e158b79f7c84ca612a51a46e809029125bb7cba52186f2dcaffae2c0db1e85e65ebfb975c7085144fe1ba7117db36040586b351ca7a826ff905f207feabad8879f699afcfa4780769e566f11e5732b8fe7e525c43a3ea17d54e89707e2b16d2d5463b14e9d4805d393f2ed1a054ca7166452a45713f98b0283afbcf7321dd67325edfb78d1eb8cbc578a462c28081a6630493b000416f35491c8325e6e3b964231f785048cf8b19ee674c2994fcfe9289f65011b654913571ff25af79d145e8d37d2969a8059f5e8e9a97e8b9fcc9c7eeebcc00aab0586d4dc50d80d8d67d39c72c9fbe07c82af97aa068c6e57c84ee55d982e04d94e2f5f6204dc9b692a9c2b4bcd81c18c98ee44b4b67432252dd0dad6d3335fe6b985c3e171a2d485c13ca62adafc4bd0397f4abdecddafcfc640cc1aab69a6b9bdfeaab33254a012389564f36502cd68212c503e4849d5caf6ac72973b83b3fc9ba06be805cb72f3708367d9e80079c2ee591476a6d216b0518c4705b5f5201019edb77011ee16b4aa65335d3baa6283bd63e047aa78f77e41296aac56236bd4692d2f51f6b7a0a564790faedd74a44afeaf79fd9bd13de212cf910a6073fde9b2b73a3916b66473cc5e0264509b0809954363cfd552b5020bb0fd65b77b07afefe2dab30c03385a8e6b28136060006f646e4fa6ea2d113e084d7bc2003bf97815ad60715b3376572c437a34b32819a4d7d001645f07c56e6d61a00e529861cee6bfaf6f73c8ddadb1068cd8ec255d6eb94ed92acbf961059c6230d45b0699e9a085d18941db1b6d3c75c274cd56550eeb830ab9eef635acfed51b7ff1e29c47e0016d18032470b8e78f975b136f3b792b3df962a3c1996e28a601eb02c69b436393e5c3fb2807871cd3038c67400d5a976285ccc82cdcb0352b5e5e26722a4901cb931d3a9302e858bddee250518e6ec1d1440e8e9761968c5359a1e046a58e2d49d5b925f7c56a346d11ae9135a851bfd6ea2e09c69a1802a159aad09783dad565b8615b7f7e6d881b09c0a474836c7f56789a882440b6252f348d21e2a8c13c774e4c820e552080ae8b98d97bae180bccad8d11656192c52115c7900263a763362c81fb409a8fa2d89d504e0c254091632a5a42345466838958de497cfc4309177274f5e5a60438b1bd7f94434d709cdc21170df7d4ba0435b62285371c1ce7fd6835ba5043ed4a013a574d61eec8045542794fcbc079c165a739f1e5e47f9faae0691e8d7e9be4740a1366586221f7fb7beba61cb0fded34812d63d0efda82511c2ce2b3ddf8aea84a1969aa02d4c0cfa5caa0e66a8ee715fa51e866ab9c95abd83979b93665e3f22f13c708dcebaab03384d89e2dd574ac965951c7e1415bdbeb7ef85de9b7641ef0e0f351410666f703c1a51b54529f9e5f54ea7b4bd42ce2a7c80175d8b425487d0a5f28a28de25d70ebf268480319b12e92fbbb6f2fab9bf1ecdf0067341de42eb1407922bc6910f74d943b7d3735b551ae8bf2b83f07642da73d4247e266943c60064e60b8a21e4e94b9b022cf1d8596dc2387f5acf21d0c93c5985e0ec739f6d1c896f96de8abb6dc858d858e60cdb6bed0a9af35722fa030953fbeaa63f7351f7b5fe66878613c1d7cc9e0efebe56b5f24b5f6f51e472b4c077e08e42e4061a8f55b135a5b5fe75fb4b0c88dd48f0ecff8649d575d4b33e44b35f5b6fff408bc391f170827cbc92b185418ae796401840775b00de39bdb0280cb9857a848d73aa3a85d4799757bf6ff72291ff1fb9d1e29984f8454c5c396d5b465491ee32e74ae65e9f81570321d7e6687e291fecdf7af5b87f687870d940a28adc23af0e944c2cba54006bd15948746e2e1b770f2578a9501b9175509e23c6e37acd288e7c73f8d59e96a3e9e7804c7343892fabcf9ce97ee7423bf142ef2db4a14143a88d94dc873cf36470052a3575114b9d1bc372bf58f7af959f055291f5c4e07df9dd4226a57d24f7d7667defb8f562be1a1e17b14ea5102e2c082b2894f5c3699989270128bf2e0bfbec2d8a9aebd2ca9fccd0f682ceb7283d98497706d738eb3f8cfa5d4482bffd24e53961e579734c3b0863bfa959a81ea556ed176c1ebc5372ab6cc0ad1314e174780c87ce1348d8b33604e2864902771b06e771f76f3a01834c14673e951a56d2712c067a161cd9a1ed80b500aad41deb129a4afa19f2944700b1ee7d4c203a21c7526ad9abe307571d5b802e3960a455e9099152e167afa28b6edf9d2d18e04cea3c6d3b2648988134fe3fd4fb92581524648b410b5fff8c642c01e1ab5f8f5729c5d4c64bfbfd9b0215271c19ba86b3162afa018d4671cc4505ad23011f061d874596bdcf98bdedc3389887c9bbd44c0b01aa877f46c4ca860301fd717c2e8045014a8cf1ba555ad873d085bb11429ccb40add6580b7cb0ee8ac6a6e5303d42cac99a4ca9857c5774c209441ceea1a469b82beda36ccfcab7aeb8972bec34df4e6fa95081ec872d49f1024db5f7f3ba3585dfed2b0db2f11741e7dac86d564509952eb86c9cc230937be72793f3a3b4d404c3f9be632dd8da8a1a30f01977f9fd472f27192d0dca31ec0b0f31e539b0469782edf927e9e25d0088880c59f39388ea960b8ebb0264111c4eff602dd3860191562f93cc5677e380e3c0f03873fc9ce77d3c50571f76b675199d6b5713a97f9100decd588042be81be77969fe08e6b218f67a29b7ed1268b08c35a80c884e4e078d9b123abdf0fccd683dcc0950ccb21e3e25d36a97e7e2cc13476542cd7922076d239e694442d14fd5a87c9284194d74571477327b3711f7946f169e6a26a41eb4cd524166c73281d72ecb38166ecfd0bcf0ca485e30cb66a954fb081f7d36af4bbe1da022b31625543f73f58925ddb347a57ae7a2efdea877b5a083c404c6bceba6f676d76fa5dc008555e00ec0349abf1f90380453acc6f1e5658efedb8483a3aa184c50deb8276ce7ee02cb312b05ce863b6950ac7c398a41b5e609b11900666d1964b6ebef81bd1cf7b725ef9468a7a5364618f5fc99daf0982b8e5f93a1d90a3bc0a6f3ac57035aec70b97df1d81b6de19173b2a8aa715806d023c7f49cd8e135d90726a943b34951d336851f224b27c37ac0c6ed80182fe35f96c1ce5b0eb2c11f9910e1c88f29db44f61a7ff8a7c8a372f416bc100cb8f061e7650f0171458a85ed1a82d8469b08398322b916700dacab77abedd4ccd1a42518b062a53ec26c6c168d000218201b79ec24e6a2b33536910126417e11a1d299ffc692c40ca5572ab286e6af1ddc2d80a8350d94a4b44869fd681372f124adf8c3d9c6142d4a5964f082e3db8e8fc9ab81c65f5ea42aac52b6def9c1b92d45fc094dd87a00cf381af9eb7f0be6cfedb053bc515128a1b441770ec9e2ca908b0a99dfe70509abf9ef0f8084ee622c48445c7140cceb08472de10ec97cb1afe1b94c0c1d2e698939e2f3db372d0dcca67b0e723f0b0b4bd3b8be2e1e4bd25b97c4b2e39565b7c067b5a88dfa24b7080f4e26676f85f6dcdae2e1802f1f3347c42a113df0ae7812c1c811e888529245adeff6968b203d50086bef6f905afaf4070e23e91c21670bd47a25291886d16a7a918fab4e6821671149b153941ef252f9bc8391d3c6efe09c9e7c3b64a0e3e02325e07ff2f00e336ae0f613eae90111478943248ffdc7c9f467226827ed44b226ee99b47948c409bb370159a3a8aa2e7ce3587ee38ed1731cee9420eef1ebfe22d8be72603028e34a72572d01edb722f509e8cc81b2756aece90b0728604a5a3f6c1744761f28ba3301693c20cd2790a2cab6715817e86e763caf30e2fbd0db37378d61486a113989a441988244cc45d1eb8d5c34fa2096e4b95e31518370262b37069c62bc51e64955e4eaaaba4a373e2b8b0bb59c73428e56513356dd89734e525bbf05de22872594441be4deb7c955ae72cfb9c449f58b52a7673a9ce49dc50c2515bfcd33563da05e4e419786dad16a4bef85be143adf5e12f4b784c3ab8faf6db9d7be27c130c43bb8fa3bf6cd9f0c9fe25bca0128782555b17e481c63c816ebd4c4069b8c5dacda83fd41633c5facfe8cb14da760c56a0ecc67ef8809fa8a425ea9335b4bdaf407ec261801025105326f093faf8b63451dd4c4580616eba55bfd71476614ab3ace94260f1f74ab5fab42d87c1bba8ae0c23e4e1f5db3b0e2ec99fee82b80e64aa4fd37ba81bb916b53d90fad1a7c2637b3862f6b4010ae22cd9807af1ef9e4c76709d8f250e02bcd55cf5f234a7090dcbf0ed2361353bd9f5f3fe69f8293a9208f7d705efc626e8d369819e7bda417b175852b298d056d3b2927b1b36cb3c951462a1e648b32eeb2e998049ea680e8158a574f51accf1a2a4f2dc98879e0f998d429226ac533761d16e17f8b115d45ec80f7041eea81994480af2f0051065376c00306250509e482c74b9222144747fdeeb70ebf976961e90e252bb58ee958c8cb27c71a9a69bb0aab9952f1ccc88dd444a5a4a304ddc9e98aa596016c63d28e3f48518dfafc02963e5faabae96a1748659413f23c056f58d4eabfd1bff185f2efc4b54c61e0fb5d31b7e210ffa43b0b89f1584b1d5d0a88f23b4f26e14cea156a1912e5932c5472e9ad52ab224799f59ac4eca30396a05a5fadd2f1acb38171447f14ff2692c8d92a6a009f6948bdf0d8e6efe83be8a110103f70688e235eda15467165edb7e02c371768ee062d332a0c6be2a5342d10afb0f10f95a1f94feaa0e623ad81176ecb9ca18773e087a9922b4e6315b04f289268a2b2351c432aeedef0cf39298e8a977fa41b28eb36e610cfd4b48b9bc124119ee59805f02cd2a614a75025bbc1725e94e356ab2e04bab5c422f3968f9c80b50f6c05d5c36eafed867c78ca907af885371f1dff9d08e32136515d4207fd280c4247b37b1482855b77eadf3164e6f1a3570f4be45a5f0d71a914a53abcc0f7656e8eee3a87131a6bbac344b48135024b077bc5e4debd4224425fef7c26b26d86f54a3714f0a3e67088bf40ba6f9a41196a7295200e0574880b41ad5c553b019e8c8047b4a630760579322444ad0ef3114870f91d3d6feecdec8a5e861291ce0d1d3f1db4c2057d1cd8972a3357d16e9589ab3da5262242f20fbe60637a83fe663336758e620d28d3a1704266679ae275c32319ae239b0b3160bca3cba1aee69a9d5279d26e2bc40a423427519e174ceeeca138c7a5f0ab36254ac4085dd9fa85a8cc80aa3f3e573ac2e813501e67f490d1ce42d9af0065d92bb69c59a49e4fdc91fbf0216c9155554756ad9d5c216c80a3ca3214abfe6776bcb21186287e4640bde9a23e0121dbee14426ea3993ccc7dce0a2706be85b60f006028c754e08d815113cec64684b9455654e87171b82699de68144fea653b866a2a20ac3bb419cceede45a7fafc925badcbcf9231ab44dc7237df0f75dcad6017713cdcc1cc6b6d6a8005c39cb45a60e15fef1f7e7c515da62ca2911e30bb5243d97af976cbe0d5cdb00b0a5a1a783e94bd4e845ddf02ff7d2530315679af89d37d8919e801972c13de59d155c3e8c38b10e442e8930b7fabd2ebb5decb1b0d239511b126922fa33fdc988502f4244ea8ea4c1d723085c2d90e527d6973de756580f15d94c981be8e22ab204b3e4e2df4db55d195f4eeec8fd5b0517f140cdf2b47b4c3ff2ac969a063d5e3ad7b62543001bed6e520707a8c776844a9615ddb4df7c8d7f5a1e2e8a7f4227053768f867f4ca53c5f0c6bafa3537877ce26117a606480cc81e38b3284574e735717da83679053c951eee27f5761a486cd62620f598e7c6745f8549a885ec5a0eb5a492ccd7a490e2544e965f10dcab8e4ab666c66848a445b29baca0594802aa0048a865650259440d1d01aba4049a8024a4013680d454149a802ad13f4e4f606640025e6c445b147313bea6544137c4411847774b3b422ee5fdba9da2b9f126882fef22e7091accd39021652d122353c0cde39d12218c6a0e5d381f111af1cea589ff5b0c066e69c8d4c1fc7fd67ed25f3317725f0205143babed27bab0f5539be90e40ab0d4922bd1a5a1065ae1edc163130f1a3164ea094205dc74a91382e4838b69eb38cc035f002d2b9dec032c3afb67c1d47ccfd48d7e1f34d246debd6b9ca5b2ce48edd25b2c6b7fbe2e007935c83e337ec9f4c4c8e89cf85d0a23a70c0d7702ba19409ff3dd93736acd44479bd2678c18bf72cde0256476571eae39749b8468b76106ecbeca04fad4371c99691f1615f5147db831f1b9f06aeab2839955500723ba1ab117e6601db96ba18ca1906363d8244489edbc998728ce603c546b5c2096f72d3eb5c5ecfbf58c492898c9f6c92c6703381dbdf55aa8c566a952a329a8ebe11798a5a0d12bcfac263a2a7b0ec410674fde24ac760aaae2fe61ca47ed91a35fda2224fb5800d21186122c19e5fd67a5891fa757b3d0a138f1960cd466ef0cab614e139e70200d1eb06556fda3de0156536b21154961bac56f84df4fcf9a38162a8fa2c88c21f48ae8d51637063a352e84f62e71740bd284aca43a4d668998f379ae42da24e221152432e10edb57bb2c2feaae13314a81a73faceee30938c4b8df51e86bda29f431240624a2574cd54d1aac39f30b78e164668ffaa9e7c36fcef91003c2f4028c3c0b9f7ba6bff0e6c6c5bf44e1c5ed828bfb4d1fd8a26d2b0ccbd7292fa1c6f1886c8b2670520cb35d83130d1e67d25882533d396c11af05a9a96964db22f3ab2f208bc5050ecca13d014698e510aba2d7ff852876d8b58fc0ce5d4f59e5d00cfb6846282a632f440d41f5571d348d40e985a11ba5b88ee272032f0a87e663e943799d6bd4cffa199b7559bbec320cccdb018bd02e1171657f56855cd4e481cfbf543cfc0cc7b0b73311b166f350c8078044cabcb448a03c9e2185dcada02f9c1eda8738cf0299f9c1db47f98197ba6c14cbce275a40356dffc8d5726efe9fc20ec2aaf481eb2515530ff4c76fbaf5bb395de5e8c2167a566c906c1260fb0a9dfbd8ef95882b6446474fe04ab573ae716110461c8b46ab8a35a5469ce7f7403107887d2b80a98705c065ed88a36fec5e2154d2a888880b342f911ae45b75db99250868263148f487a81c53b601f957ca9d4f89854827bc95dae670683e79da787afb3f91a10ef4399ed0331fa56430ed03c2c60fc67bf3b3f3196011e62addf2ff9427d06643aef90d0689e13be221e9a723949566b7d74c9da0129e16de6d34f79f8b75c7b59c1961228214d9a29e85cb29ecd9bc524928581cc475db277c134dbe2a5a6b95db593db23f9d4701e30ae680f7a6dde8750fe002af7b030d74cf04124b500806f26ce0bf962551318b2aebfe1cd0d6b4a30942754a6101bfc47a58315f079dd48e5e1a2780042365717e933b95ebb7fff5e582a516861f005b3dd55a70df19e3df9c004c4cab0e9752352591bce830067dea94fff261e3f0189d0d66103cdd80e24f419a15b2240e6795f25bbbc57b0f68733556b586e433bdd4894b4a1fe238aa01627fa064f53c8fc4af4b2e5cf6bc9ac12b69f914cca65294b7071d0730529b81143793be3922c091295264f10b2814391ddd22ec26802cae596e1e729659b54b95c444c71adeb85131cf9a3919aadf36d8aa55802082d47749ceb53011bc0e807b967cf9a1a4e78112d8aeff6528f5b5da06c93a41e996f28b6fe68447466b6c0e7579f646e8b89f12cb194ed629381b4751b350a76653d28063a2141784ed2fd72799a6930503ccb2723ee12b7fcb1d4c14c78a160e6b0bc4b25f0264100da43d5414a6a6e105deb1aa666a4ad8dc190f018618143a9396566c723f1d76a180a29221470a0d890cbd2c2aa9234053fb88a40a77bee82c4c6a7706aeb4050f9b692043a8cf01bbb8d9eafaa805115557d7b13f8113b70f83b7918d2e1915900c5e6d61cfb4fb4ac3cffe7b9a336ab3c7ca22816bd2fd74afaf8586d9ee0890f8c1ae9bf1eccfa7ca9377293a1a738a92dc13772a4770c5f4a404ea9ea3fd09f1155cd858b76b0ceb0fb688b9b6c861bc20e7377e9752c1aebb5ec090a2e2bfef2d02ac58b75cfff436d73b6bd108dd0e2b2f718145370bc51541f980032c23ec39a44164cbd3ab41863dc44b4145ba7105e1030d85d650768f348c3dea3f66a78c6878c7446ce8184d9d0483f4b01ae4bf71089c7a5b7ce73be6c2bc05c62ad43710d1d17ccf46f0ffd0e68162d96fd5c9283ff2db8009295ce308b91d751b844b2a339f82cf893df86e338a572d1f86287adeaa764527eaa571905685961522fce4558805886e816bf3ef25412db1a309395a5268d49ccf24a4ef53221f7ca3e6cd0652116d55e51ef2135b2461a6efa70e84528450f08bab96d103b8c67182f489f765c806bc04234e04bbad163e80d42e0f1e5fc548cc58f5a109a97a744a145839e1db1cc6b5832b7048006d6142be208e623983d9aa28f83a566010c80f8b390bfe862a1e7ac520ef1c0387a8fa54d093b325dbfb15deeb0bd9ab4821bc009a674996815c1ab1f3a257f6ec79878001dec21627cb15da58b3606a7b239ce9070778a7b48269cbaf54b9ad87c7e809e6a686905e3d758947f79d1e3297ec836833ff9bd374bfc6629b0cc01b8c472bc0da8dc65ac06dcc7ba2f9c6bb7e6934c2704917e4fd211cda28b02c8bb70d2d5ecdeee422e1cbe18230f21758a4f3b3a430011896e9a57ed70adccb2be71742a7b04c68a6c5bee506bed12d99a478de0631aece731524d718749c35e8cab17130101dabafe7fe36b7e086f62a7f221cfa3bf68e4774cc9cd3a82f6e6b2eedd6027afc1c6915404eaaf714adf44239710ec36cc9ee25944ea0c112f1e73314c147d3b4253d477bff59a479103faac005e90a7b26dafd31064995ab3188ddfc65a407bb749164fff69a1fbc0aef4450d23970d40240abf40ec28cefbe81d0ce307399c699f847772f0f3cba9350dc84b0355cafefc111085058e299026835914830bc90685cdb7a8e8e540cb41171497fed3e40122f42e39ccd648df29d364870e5b1d41a81405fa320541a5d5a0ba0129ad3703c11bb20bcfc96c6cb17a855f2cfe46d5cd973c595140e48f1b64bda1820eb926736a75973ca7efe06e831f352ae8b56fa36b2d9e1fd23af5b0e0b5c1e6ae2270c6f050247b9fc1959513672b63b304ee92ca593c6a42613fd5ba328b6d50d7edba0704078d58c0ad23ea54870b7e72f26eabaa30eebf47de1564d99fba498b7a9673d995e5c172a033b76607642303828979796bc62569794aff5c2fab13f75af1851c56a5c429914be54028b177a390b630449b82874fb6871347d1ecc91594b9657be2c442b481287dc62d647ab65d63e16189f3afb109a8554f0c687566a9ab0296cc29bdd110a22698286216f83c7daa0da1abdb0c59fa4ae86db59ee07f79eea50ab313d730d64a040305480b057d02f771de9f038f0c074b4f2606ffac0465c7dd5d552cf174d046c9d322e2b08e3732604950e4ae20571818659becd83dbe61285e8dd7e97235da2dd928a98742c8ce35d2120d6b55423b38bf53abfc97ea38a9d582cd3c80fc668b18854bfbf7e5c946d6d7ec9f5ef713ff6ea935a0d9ea69624b7d37366b91e792a6a3d625ce8324d363e1fb5eb3ba77032d5006f868577fc3b5306fed19a8eb4657f3afd4ca8559f1383e6ce906c337f21ab65a7fc888da5e87353da86efa1af17dd49c3923ebad5530650195b96256d57c5960c6aed813be257a80155087ddcea8889175b8a409e59ca23b7840cc217db78bccd87eac116252ac3fac899583e3a23b95be0d9b5b72f10ab600235789e618f3c8dec83078a9985bb00f69591c23ec37f09de453c339c3dab4531495968604e253dec7860f6e7da8745ac90b46e35125b9f667134074872f425bb56e6c2d290b307d010de63f7ec916f349dc30ea3a91e0d8c82c9af4676dee700d6c303f438e09227768ef44ccab2cf4cdabe26953a5ca39d27afeb39623cf0f062350d05461afd90a3162ac91dd9ff1ddf0c00bdc68949613410787042f3be4b5fb478b0e7063f35b1762f1e6022d99f10ea2a56b881bf5662de01651deeef1ba20e34018d7afd966c9c6be7d25aeb076833fc3a193034b11d5d409032246e4d0710b82d4a659a149adea8ee1636dbee4f73b0549ac541a2350838eabbb203ffed2ef6a47261d468f316ce2f564f054fd221a7c6cc86a2c680358e49d6871c22bf206c72a64ecb1fb181aa70379c189a56c3161877a82d0568650833de9b99849278c9d44a4d8fa858acd13341a581385aad96998493baa8d92c4d43fa1f3e715c10a1b2e90108388a3e0ebc29e17a743ec5cad59cbc90c7382aabd60aec96a02a4b98ea8a54f147e3f57eab57031d295d819d815f75657d4a697ff3dc34d43f5a4379d646ab9176f148a1ed5ab260bb267a7b400a87e2ccb535cb1aa8c7edefc2453f0c7bb856b3564dc290e14035b0ac6f654edf62444c0234c90557879c753bd9c8b611196e84e9e12c0cac99467c761fa6ecddb72d1285e68672e822b7e5350b48c4f2e210ba91dbce47c478b0e8698bbe1be6369095dd16cdb3b86d5ece224dd10d11983a45b8cdb678566bdf002390c5731eaaf738ab668138326b56062143100e83e276bc0e37f469338eef36f326a996633bdd740472012f1b3d331a0b90cd2c7a0a6a79aef27bf530dc943e02ad74795f51376967708b1ee7b9c904a550d8ea9f6f2f1d836483bbe16436ef77aaa6d8eb056f826776d176aa5550c04f4774dad1aa555876f8bbe0d49d4e4560093fb73369a9a2170bf95d0d9f2a65c27e55e9f5855cb562d61430078c5f557d6e8b1ca5a0194ea13d5884edf783c1b144f00f2cd1f30a130703871265adf43370f594d28e554d2837613d910c202b7259e0022a6f6e632c78a4b2be3a00b574cd444eeddd0316792380173ba12c64e77957982e84ff890b7374294c4ad946fefae4c92b5e4f525951c9886f7f37ef8ba023b31acb4602232198101bbd9a3ff3569987c4a76eca9d32df3d29d3bc28351582a8606b1e320a7c5a09291302691e5dd3c3df403563e98d49eb8893f672d65d27d74877b7e80f815c69297a3724ae99596c2ca899954616288c021c37eb2ffa11e5a8979bb03cc02e63dfeb77971af4bf12b503e5d80ec845ef433962c37ab60c77cdece229350cd8bc8527b1123f389d57b751a7b1bc1f6c4c0f0f948bf6cbe13fed8f93a084316063a5c12d8b624e9a9bf6c1495112a69c7a66924c94de569a60e84838ca30270bbebb3b8a250909d6be9d22d15ef77bb12067490aea8e20b09100cfef1247747fbfa28c08c9c852410fa58371cf3576812b1b881d9c1bddd596054d24a3619a5059026411e52acc5251a4d67e1ccb228bac1792bc0642f81b8bf42ac95b7c8f32bf2a0dc39a500001269e45be5b760a63796fdc1a5c4c6b0a5e0400753a455f941e6c8c9e88803f06767362cef57e6af18995edbedc218cfe0c702fc061731cdc1862c3d61ffd7e5312e99bc740bb578fcde31a2cca655262be50d29606eae22342ba740f42e6d719efc385e3c64daa23304123538002217854a4ebef5ece36b7708b2e8fd929240c81f73953bc9755fb36fe5c4bbf7547193a6e47f1fc34348daa70dc9a9be35128fdda8c2db782d86833fd7749e8ad419e3ba30f566442d43c7941c7f050d312a7fb437c8621f07668c6c223972385e476fd3624c7507ad270cbe12e72b48f8ca1cf3ec7b502912184fcc715d032bb877827b17bcc48f1453fa3d9d0cbad39bf92d0930d0909507ba3096b347912b7667d7d958c7e807e596188d080c0f268f530053cdb5e5515392fbe940c01369803557eedce8f3afd2495658081bdf2a910e3c7709d9eb78a469b50dc576d41b09f291e49554d2223addb52c740b5ecd7539670b589fbac0924bf6b63aaa93042fbfb9f54c51d33573c4ac6607715ef5fd8038745298971b82c25d27b77ce634bcd16d3e614d6ca71df928292374510e184d3e674a619114b1dde5d00ebd100f181757800841b92e07a241c1f0013e11b16802d6371853a4d02bf4fef1f879f09b44c1494660d891385b54e8f18c171650b6f1ec3d0d10ae0e18893535df316aea0e738a433a4c69494a8215b9b08685f40472e2907442de74a4216810b753f2144be9eba9cb51307973655691dee5834e74ebae656f428845d12eb1ad37aff269e06eac3cb71e62e9bdbee7281600379bf87b0423d45ac288402147ef9907f791199e90cd8a9941c3145c8970aaba887d2d59abc9d61319f6e9a2fc8a7a92d5e5fab95ef2526736361b62225223cce531d441745932d755928cdae188c8550cca706dc47d92dbefa5a5ece458a51ac9672275a99cd8b0a60e75fd51669ed555237c7e6192d178331bd41f83d3919be0a149d4b22d4673a542831c0c7093b5690e7cc178a006cce97ad4e8f784633f877dd2de9f230a7d92213c7be4aba259cac1fa145e649ec30035c72839674c3e7b55f7e77669264615da84b051ec01476eaf1f26f3c1c76f9dd092c84ab31273507c3b2085558b6f4ba46bf7d9cf1983a700414bc019f20fd3e97fefab64f559c60a78abe4a48c46ba48569b433380d1dacbec79f9907f80bab8544834e270f6b3f5d2d4bde2d5be6e3596d7a2d4965fbe4c61322b982ec6f00a5e57485170d02793770a874c82322b956159095d8781e9cb28de785c99075905a39b69dc516734e27ad276fb8adaa35543b8b3efda7293ba8c3c7b91a91c2c595a2abb2eca73fa05c9c57ff259284dddcd00d2d53938522e8e2808d6be3dd35181c61945e72628b9cd69c4aa3f971a162d58809d324a055c5b516c1c7f832b17295fdfed5635f22ed1f9e19292566030662bc370f6084931bf74c253e450f7c5f82c7c93a5ed71c6b6e000da86f7bbcb9a011d785aa35e99cbcd0d8b8878d24489a8a9b4c1ea6154f41d7da8cbd2840b6bf7ef6a7c9ce3ad3ac06ed46e51dcce42cdf8e7c95cb3fc11e380d97e7fbe611c32e2652a87126c1e4816a97a602be2e698cb38f0facda0d86ea0c358a257927684303195497586000830ed6d5a85cf18aa27469d0a50a8bdb8eab7a1edec7d2130d0c8d0315e1dff5ca1bc3c1db7801c53c1d44f4bee1509b2c37bd58e661fc43ca83d5215003dfa85263bda401fda9d9cd08d9d2b076de6d37e519bef57586729f064cfb99f80a19a5cd4d12cb7cbc9559f82e0ce1c7345e68be8865be43df7ff8a32e776cb46806793c1dad1a53201f92470e446c6cd0a4e7402ae854cbb23c8e659268212429bd7b8584140cef6db939c0c00d71bd8fc7e9f52e14b152b55112823feea005427102c6ced5f812007e0cc1d00d3a100ca05dac1c6330b04a0f28b55623db7c89bc274ce58ed75b4bbf77af47501f21888b4c88757804cc82019cd5a3d463a4d70fcf15244941890797e1e9d14cc0216e4d2335b40fccc9656ab2ebe2545ea3312b032e4f109f9bffd51c946ed36a9e3288cf127b66993be5623eedde9f037405a8f60849266bff8446dba9cb159571b87809c9463b89c3d36b9ddeaae4766019b26d6c0b56061582f95d83408b550450fe9188dd984a65d4c83402f575bb26f9aaa12089c0f962d88d14245b441a3297d66b472ea8d007e3fc288e5be680482e5d64192945985afcb97abe97c02c6801d11e94080aec8c5b02f8305df5ffe3cd63184068adc1166c8d1a9a097ca7143a52336c20630c1da5842c0fdb820d9bd907e162513fcb6d242a6f86242aa5468910e30a37c49ddc534eb53aa43979c7b13af9abdd71ce16f964417c2802ff8deb7ecaa8b1bb7823a9e03dc9536a5ad7ce466989b352ee34c03d50c78919d8d331238301f8f49c0ac4f6b2bdbb36cdcd33b2ec48fbf6a8182fd2dadb28a0d97c8c3e069d932a000ff13aafda7f672826d56a83c360da8d862640033fde81b3e1d90dfb41807a1ff754a3881803c7a99011045133e2c4c64c6d75a973e5812a3772aa9d122a23ef79f74a69d1c95c9d5ffb0b59e3dc50a1cfc5f57cf3055b96c8f4f90ea40a19692ad2a745efa90a53a112fcc32ca55f2ca91dce2d1952134b533e693207cd4f1615395bac52982a4289873ec548d64f41d07963b4c8e0e1c588c813eb8e78e08dae3156f8ddc30d59915a5bd3a0ca3bfec6f62573564c4734194c67a4c2bc38546fe7db1917c4ca821a85d61b1d9bf098bf59989cac9d5c529b4fb0ae16fc89162e3a495eb74723e13ea34f0c415b7834ca2227d31ecc460aeebc0fd0f15aef198fc04a667b76d74ba2d7bd9c72b847840c7535aca20b6af5ee62e7dd54eb76f6ee6dc560971fc5f55846f11f85ecb71afdc14f65fe4635e0e6cc125cc6a279b35f80d7f6f28c79bbf1d50034b643a7619b7c013d1499ea05f8c050eb57db2d6d2b5028e3ca1ccc0c83b3f4abca58765274f6e7088969586c56f037071d23805250a794afd6c433078bc02c3ef0de9a77c65aed82d9a6186ccc2197944cc6be19f4f09c9f907d7a3b65772039fcd97949093832b5537f98d21f0a3bd598e1ca4686fce62923fae8a7b19caa7e0a77418e552462f06b0d78f1c778d47dcd67094bf4f1a5cd121a63a658eeae783d9c0e3ac870168681c7d213fc6783f979bb4a60659a964c874b759608005cfcaadab6cf7c3b4065e57c77e23caf1da1f0f31adfa12665099b14e576f4b8ac15ce94497db96e416c1e8de1746b689072ed3420d07dbd22df8f2454ed6f42c7ea04c094f609e2de9fbcbd3279633936f2e45ea666f7daa63c86532ca4996bfca8bed4dd3cc915b95b44f4b04490a817e2c72bc109e685dd200ee0f4dfadbcf20c9d323157e536919f3a0bf381da0a3d289f446164509c9cb1e3d4c5310baf9714973b5f0e822852f6c890ea077ef5c8659ba3ae540fbe7e068f440a0ed512531346e30a6236a49e1f29ba4afe265a5be4485920d63294f8ac9b1722131720676aa284f9589ed68709311e8a2ab7ae5890d04f6e927978646fa3d3640bc22207032b9357841a0599022d821828dc616d41551805118053fd2b184ae7e357575d3460fa04cf9aaf6b4d913465f92db14823d0eec3edb190bcffc2802069d08141a284816797e75af65b6a4b2547f720c7898ec9195c66faeb8d7e0c53c5ece7f24aebdf1f0f464302a55af8b0711ebf0af4dac801d0760a381706f1e03943fb2c43f0b67fd81278011a304adf607f54aa628e310f516d8d9273f77f3ace4c4ce811fc3a01953510ef605ccc599bcfa8181af52d73a4c3daf39d2d79b7a57eca27896871054e868def16f7f24634e01ada2ed9c8fa4a23478fa46907977476d08edc0f27509f02cfdf681c6f2b661a1ce3e7c31872e0131db416b8b1ecf111192e5498336c9c833fa5503528c935f95e3ce5cab34b8f0f0fadba8134880c9e82fab0ae829fc4c6768255b4f0035ad216935c305232520729af2d9cb2c8182327039c4df0ca5daa311c847114c2911a6aec97e5a623338e5e8540848964a342c56000d526220a8190425000fb1df5d0fd45d6db1a58ba0e90716cac7daf4ebf0af803d60461db265beebd654a4906c308e308fb0822156b7a53a7809071e34f8464cd2a8acc3ce95962cf8877e606cac6cf994ad9d894e5bf8927ae7311ed4b0d33ef33248b870d0e538a79d8cccfa96a989ffc67be97e810cc548d1992c55f5cff2641133325f30d8b89e23a508d0f7972bbcbcc17f3e43f4635038d0acc93ffcc27cee4c30fc14f8270fd6f8cec339a46c7325133e31ce958c71d6369ec310a6961dce9f2c888c4c3e01aff70743026ca5f343a15d3ad98a563314f8ce3415c939ae2fa7371c5e421981e6596de0491b9526208e33814aef1f71ee6a98af8841035a19aeb304b1c66e97fc46bb5d664fa2fcbb651a9aecc0fd6f68b84834981168a63667ae699bb3be9d2df5deef38cbbde33515ed21ca0594b7cde54d13fc5173d11f145a4122694842ff6e8c5174925a21f9134e0bcb1eb0ee4b0eb4efab84f90952ba6ca71e05a72d77ffa6de8769d5ec675e31a77dd5fd77de6ea7b7f97493155fd649e7c982ad6324ffe345a15554c295040a12cc7d1860d12c97dfa7afe5698c633dcf51779b0de7fbe02ca2bf9a52896bf71d23aa23c5f8347d08e524a29a58ce35b4729a5948273555eeaf4f98a2b268a3e8f30977beee8b4525ae335022c435cb6489745daf33acff33ccf871499d77b76594a820ca02f6c41230822838712cf756f85c0ba3cb7edfabb7cd7fc3467b556cb553324d3dc962e8b5bdd45add61291b0decce9d6cce798a65828b21f93f3e46a5db3e1859d393324cd4e0d36c7f4b37e5d9ba981b5f153d48932d357666cd8bad6a5f8b2de8b1f56113dafa691652d57d56a6db74a1b9fe9b2b972990b9d331369fa97dc4e40e9db5b73d564aaba09e84d260e06aa0e738501d3ed69d58cffcc387d0591ec2b5dd6baf437bd8daf85a6b7f133dd9a2877f9d36ab60676bc2ca6717b61edcbe24e048fbf2f641ea25137992a9679b7e64af4de372ae689a9985d93b9aabb98e168e455acd8d2aa9ef9c9e78a04db5cf1e8ce82eb14e83d8651d8be35ee3a8e9965f54cc6b84ef1f57e9f33917fd354e1569844cd5bfece3157f5953ef93b96eb1d13914d138565f10d1a2caba9b4fc3b51278462aff83b28fa9ed229999f287f364d94bff8637d14bead81dd71591d76c8ee02f335623e48db9f220b23cdef70af9566581b23d3c6ae31f2901d79cbc85946d238bb2c91ecc38c4c9362a6ba8b5906cd545f2991ac29bb2191ac1b9bf285649535990b9d334376098a6575abd52971e7e52a1ef3d48f825d3fe1b6006efbc700c1ba90372524eac8c64c54977862da06d0cba3d168b72cb8dbf7de0d77a3429b287f5b8697bb7971fd3f29e6f7647e3478399eb99a5f15d7ffcbc2e5595f17d7591f14ac4fcaf7f970fd6bc23b50cd8ea155a6209164d92ad5738f6495d599095f41bafc795eaec66bc4de09b488d9ac3b75f717524821851a5c2d36aa0389e752a740764b9734475a1cb37886bbb4b14c147d5d203b64db0e59be403c9bab112efd4b9f8598899965ca44d11f2c93952cbd2c9378cc80c92810da5f7fd69dbabbdbd8ccdc19047483cb7aaf26f0682c526159dd08442a6cc7d3a533f15a1e4f0c9065793c1e0f8e56752f98cbdcee055786be2c8f67041e8f47c344b905f88a39cb32793cf74d2ee8a07469badde3e813d9c1aef35cd5048241e0053501841a40d73fc7b7064d947f6ea2fc431e6a957f3d5dfabb8b097bdb322ce55c58c30acbb9ebcffa9cb8fe9f0fadb92aafffe7f349f99e5cff8648e37aad22d2d47d2d1b58be4998184226095f24dffb8744e357c70fcaf7038e9f6bfc5aa3cf88effc9cb8c1360fe74a3f96f5b538373b92644d2b7cc544f5d76a2d7f5fab06d047464908f72702be934a94885a897fcfee4463a7a8f79784e86eb06badf4bf71e670a5cf62999256c284f74a9c9fc5f7c821fdf55b647287890dfe2069437f25c5d96529876eff01f3e77d9ff77d1fd903f5843a57e7ea442fca433877d48eeb47f391342eb8d7774f4297e4f07bcdc1ba501ffc21f541320913de27a15f499acf2387809fc4c1b1569f826d94692cc435a6d59866b256ea649b0bacd43085106bd1a265562d25e58e320af841f1e552fa947d0721659cee6efeda22b0cecb1dcb80b093693468e4debdbaec5eb7dfdf858898a6eb35a036868090b8414a0de1dcd14cb265d0b800bef834dfbbbce86986b8bce85deadf50735c5e44d2f0933e7208f8e2832493d64d8ecbdf50734091a461fb44464f1abd1d914a46ff42e6b0ecc89661bbcba236a20b3487524a6f3afadfd84c268abe37b60d1345df6534c03412ce2c2efdc19a5b707129a594465da2d7fd18a75b8cf35dfbf90f968686e98b5e0933b9428bdad53e64d7eab2bb491fc5c9839d44444fe92b015f347388d057e24fc9cf139aa8efe9d8b9fcf4bda8963c60c191a6fb91a63e4d07824f534906d0179134f519e00f9234959c39251fecbcfd895e9dab811aa8639d9ab332c4e8baaedbe1a3794e3edaf5caf008703d1b156559efa9e77d0ff10476bd6711e07a0d047694c94479ecbd2dc3d2eb799ee7950152ef1b68a2bc1fedc0f34c3f352e7a5ecd5cf5f5dee42a1af0fd97e478a0e8e9832039247c27938c3efc24a2f7e16814c73446a06804862370ac371c8dc0b1e270437104ba782ed525caf4530ed863c1179134df8b9ee62387d01fe21f86230c7ef25e34967cb020595fa04ca80d130543912ebd9e791f8ddeba571d52a4f3b1d56403cbdf394921a896d6a673753ef576b57bd1269dad36a61482dc7479336a266d831023385bbb2c79c07a5d0b42e61eac87bfe379b0de83de0efe8d68a74438dcf9dd02effc5034a29daa38dcf9b527059f0bab46f5ae4df77d0f315144f0a9fbae97e8236ef7ac9bbba5cbee3dd765d9599ccd714e076699a8aee6d262eb0f314120d7ed5c7325deee3fb2814661d8fe7675aaa3f96ed70dc444755f47d29de247aed0b79d4cd49521c28ac89daff9e3ebdf3dcf0c28cffde89d2b9678f37de8e3f710e8d4e8fdf4fdfc1e72e3aa7e2205888a2bde50060496e54322d19689fa587659b363af89a23dea2753d550ccd3f71f0bbcfd1a69b1a4cba217021198ab7abfa7a3d562f9ca30ead75c89f7bb4c766ca2be1f91f1f5ab53ac6983758aa5badf7fdf53bc1f8f2cb2336daab96a1ad18f7e49fda487e8264dc31996e48429a524128944f221b9483eec506e5aac53caeece41909a30b53b23f6ef8fccf03dc3ca978d7df01b85f889eaec80640de0275992037e3f733eb2062541dc471adf01c959c9231f69c45a7bbbddf8e6d1e0eceeeeeeceeeeeee547c98ac250b0f7b889767f51329dda469e03370c05e80dcd331f8b7d059282f3f2e4e98c91c2259a5bdd24d2c9dbfc72f4ae2e9b47c34d429a42f7057482c54631acdb9935cac8a0f0571ac69a83c3d44fbd04d582827cb0d2961592897cb0129fdddbac99ff26b488adb67032d063f24bb9cb0f5bf37d2a9282597ab4ab5da6ffc1d1470119306734577cc959bb6b057700bb8044c5c17552553e9655e09ccc7bc92d2cb7cf74068b49d9db96a1ec24573b14b49ccc3bc12992fbd48d33f24e6617e88cc977e8a344dba10f3304f24864cc2c410982ffdf413111932889ffc4b64121e52b61cc1930343da99c34478d3a9907416e2faf398380cc4acf12f3dabc2f0ef9019d9354e3fc18c31949702fd61197e871fc6508a469b282cdf07d2a5cfeb3b37a4a3d67143236f328907e87f791e29d0ffc2d77ee82fa36561f4a327d118c0e5f959707926774c14e997dc2992e2b3e82591ac18ca6b97dcf07b9c97c579bb3e226797dc3357340cf9875d36ecb2b1a1d73d08f7d84a965ef7d22e4ebeb00161118bc5b674e9b906c3766731c5262ae6a21db2ddc58260bdff98f68deed3a5ab4bfadebc8d1bd55060594c4b51afd7854c327af12369c049537fe6b0805cffc2ffc8f2fb5ec559d038468b895fc8359269a52ad67ba60dd1844092a9d86aaa60571e5600a3f5a8b55617d202969b20a09e4bf2c0d21874ae53a45332f023a9570624f6721450cc1af71f21390264bd58ecc671376d6f9659086bd238c68492fa217d17ec12e0ce20217a2e8bac24fcfab43e91f02b1122a207c9242169f293bfd4c0894c889f7282dc8861060521a9586621852efd19098e981b7a6f8c40c5b266102147baf480f5489bd2092cbf80be76c84c2f7398cd9562aa984a9ffcab5411944517d7a9f833a75d5ec9e85f5cfec545f47d90eebeb3327ab1560bebdae758da6d0e833914351b6451ab8276a5f83c16a3035b49b6714dfc80fdface8912a1d80e6ac154317b21de84bc6573c5b75be6e2a44fe729e4da9ce18d906cefee3875425dd40776a98f88dcb129dfe6e90becf08583b933c8892fc8b094b28b5d2f34ffd88985f949e5d75c89ae3314ec044bb948baac24979eb09fd7793f2f9711164997ee22bb4eae99e39bd21f87a32c0bea9822e0914e31d7441ff2500bf9b9e99eb088ab4824924b5d640153c55dd466ad8808c57afd66710d86d11947fce433876b6c9a62a15cfffe7166d2cb888bb88af46eeb92d459124f3b56b6d56771fd232fe3113fc562a4dbaedad127ff1e3220b1b9e29a0b5d8546f44bae8864f1152580a9304f05668abb98e54cf19559facb98b9c613c71357c455ccc3368e31c73a555dc81c6ff1a13eb98a6dfee3a4069226cbb59105e2e5231a6dc7c789cfcdc7ba5c5d52ebf2f1f171756a861fa48fb891195c554d6f2a9f613b47e68ae6d2476212816da4734cd4746969f3652559a6ff4826ad93f5e6ed80a5a765d225b511e5650b6b5bb6655b3c5dd2795996e7d2b73cb6d5a9793f48c3e0aafaf48bb8ea33427d824c14e520f4c6a50f81227ef2294b96c965bd1a09dc764dd4568fbb3bb373c794d93f080876cdb8e2f38614fee9eeeeeeeeae3ea23ec08b772db77c9ccc70657ab094ac2fea83cff5916c5ee19af816c7b61c51843257dc03938e3253b4a9986dc56c31666a8c99aaa1488924e64ad4f13f3784f0bd83c6d25804310e12aef1efa1f6418771afd4e6020313e5df4ddcaadcda4fdcdb4d883642f6e262bf44829189a9619ab1f1568b6874f9707cf93a921e1c4bff8d31ef8d324f47d377e3ccb38c6f73c5e3fa336eae62b6dc78ff9b1b6b61905892c5e3da8779960843b26c58d5d23c8f3ac610c61c9f129f45b6c164923573b217172b2b91605c26a60616c6f19fcce230ae69c6c68b5c7315fbe418d9879dd08cecba31f20f141ba6991a32313025927d198f7489c49398285742871e98dc70fd7b07f324c48fa97d008e2dc62cc798564c2a66d9613496c6629e90304e0761608adb576a5c8460769459faeb184318bb8929c434e28a4ff0ed757bf1ed4504df82f0213911b2edf687a1949cee594b3c8ecdd5cc5cd15ac564b114b73f6cb1ab5d7edfe73ae08460f2b9d3b8738cabf4cbed6715f3c04141f0dce6da6dce72dba7b78b47b2de48975dd637c9f4609d04b24909e70e29fcc3cccccccc2deee1ae0ba202edf20be9d4cbe53f92a4551c848f93d9e5671b5cd6bc85b22027635cd6dc12d4c4e7b2e695cbdf351d09f113bf35d937b1cf38839ff8bf717ee312d397976f82c8f4609bac53c6afae3d2342afd56a31a06707f29803f91453e854ba641ce3d881ba8c395017b85a6daec49ad552ab716e94c58e7e76d9bd1021340700594b2e48b6e0fde1d7d92ec1a7a2ce101239e4c64ffecea3312c8b6bf54aa76a05c19f39e087f3e549fff24448ff422a616288fd975732fd442289bc3ce949a4124b563f89fe45f4568bf5cbe21a3b195d844413ad231b63c2f12e1b3568850e3e20ed99e8c16f17ee769b4d54e93f5d5631558d8379f2ef5bf3ecd6aa22429dea7f93151b74fdabcce027ffae0c56b16bae48b8e15d796f0a8145825d39b0ab03ecfac1f5ef3024594bd8efab8d373dabcefc4f54db204db5ce9064106f560ce3ed17c97eb98cfdd3566c7d9a2b5c3776638b2dc1dcc249e054e6eae5597ec5756f81a3e0fafb0f53c535770fb49669e3dfb8b91abd7b08ae3b12d7fd03e3b4deb73ea53d13fbd602cb37aed51892656172ae6a979f62ae9ccb555995c95bece5767b66fbe62e3f5d0cc976914ef66d849bdbe5be753bd64f5e848511f8d4ef3ca69cc5d9c0c485c522370053ca96e012814b587e9f281e13b5c24449a0e403d32519cedd0a8d67333d9bcd3c63992b1ca6eb303dca4c85603a15d3ad980e8687c138232ca6cae4a720c601c2354d4c87c23843b8c6df74c5c4456a8a5be589159189f2ef61aa7e84a8d55a93e9bf2c27eeba886c5f4830a59899175f34ba7c1d5ffe1bed7b63e9bb11e619a8677335c25cb95cff1632bdbf988435f9077547e665c8249cd3fdfc9b1e2e700e730b0896556a71cb1e36a4195c254396a4908a3aa4916f976658d6dc62334bff202ac494c6e9a7156c6e059284dd0ecced172c0ec896db7f23ab89ba0a1cb91e02745cefabb729dc29fdd43558180f66ecd1276a33f6e8fc4b2338b2a6956701b91d597679c54fe9b9979452276d8b326821a1379964617673095111266b5df5ff5dfe0e8fc5265b76d9e57fa9533fa66d1d77f4a9bb89c6e3c06689d0b37958bc9b67f36cd7bb756af21613a783c235fcfc3b2c6461dc41611cde82b9b8fc24b66cf260260f66f260260fc6ccece1b4082dc097bf5d8599909dd96c36eb9f968e57c30a2b6467ae7c7631bd9838e86498dffd8f8f9f7a6ced94b3d91c67edee16e2a7c67540e1dc31629f2edde7bacb7d769838315cd3299a15fac23ccd01e67c989275f9c9dd5df5f54c147f0ce3f80eb3867b6075b7d8f542b2aae94b8fb4b9945f8608eba4f5170cd3198d8ba1b6d57ace9169330f58aec9bc14fc303b3b3b7b0b463a12866e34d219e9729eda080c10b0c10001233da71018ba879425d75ae74df67db69b3730cb1a7d0ec73c6dd366c4080b862e7bda6030b253040c31c21105b676d9fc252d6cfb444138774c667ea6f133cbba6426f9a18421cc193067c09cd14f290efab2841996554550ba6c2a5d760ca8cb16055996084814eb14d0287a894a39b0f359a2978b044cc3035b7e290576b6371156c42a5de7753e0bd2de3a3809c2088e00c2944991f367d94f995be6259c0630520f8648e0440c8060693190a2f67dd43bafa333da7a05019d60cbe7b4d65a6bede87bb5abb73baf73197fb787d113ceb98427c8b4a9dca22c772699875bdeb7d75e7b1eb7bae461cadc6ae6619e1e5cf57e083db4433c43b61bf2401aa3321a446355680c77635a454663cd4404267621af794f3ff82cf099741a387aad1f7c0a80cfa0d73a5534364ffee087a2d16b4deb52a84b775abfd3da5b5dbad3689d6251d8754a6114b6438b653d0ee20d5dcc613e8586327f0ad429965ba1303a85ad748ac668b82dd6df5bde6a894c6cf79ec8c2e419e2dca4311aa3311aa331b761e23018b3c63f34828df8c09481276aae1acb4489c82037b3d70d8c6d3405966e9f3d46dfe8a74f504624f41df1c0facf0b86ed5a2f3e61bbef810d4b0b85b6e53c13e52372928dcbb19c0658973d0897e8601d50c3b0d8aedab0d9f047ef4063962e592d45cb7ec8b2f8369bcd46685816260bfc23b230a1b8a6e81b0683916042bc8a48d6b764ad9bf0c1256cc42a4ca205b500eb180ca89d682abc45d64233e9d2bfa343f7d52e86b95a5dfaa4350f0b591a066b29deb02cb021ce3251ed392dcc555fef1618c7b11830b620d17ed0dd3d7705c15a2b188eda48f84ac0b082fdd54f21f84ac22712823f7a22e08f461f86a37e96a99bf454509bdd5ef5befa8940af63dbc5209c3b58353e976cd2299689eab85d7f1db7b93a5d7f1db8b9a2402c9997daf5eeb502298ae53b4530916d8ba55a5896c964aaf11943cbe4617155cc296c5485266cf7d4394897330f62978dabbe770ea242f3cb63342c03312c16abd69a4cff6569138b31942efd77d8491a269ff97b3089e82be91f8b3114f3e4afa4fef73d914facff8948eb279f7d023b619f2e7dc75534ec4c1211fd68144e52059b209d9a3eb6a333a8661441d2468ac0e08007cc553fdd61637096dff2d01380c113736710d01964501c42e68a04d2a53fa453ddd7ffe8f7f5a3f4a64b1fcb3e03fb8273e21fdad8f4149b8bf05782821992cc152d01139c04103b8ce003e994bbcaf46daa619241f448ea750941ab8179f267720625315fe395c8bce9f987c47c8d1f22f3a69f220d932ec47c8d2712f335923031a4c6cfbc69a646932c0b2cb9a50f9f555e9887f990f668f46177256b6148249817981238228df685e6323265614d2c575427cc1b2013e52f3306b1f95ea4ae3790794d276191cbf55cd6bcb2a3bd1aa6aa1b639e727355ced5cbf5f768f03a30579eabebb24c56b7a547ab5ae8d35c75a43f0afe26ff4e95a0f7188d28cc9c3a362993b9c1e64c385ee6d5d520e373299ddd6d7671eb7073652fa5ed850fbe887aa3903a8a46ea4247bb1bed1d843ddc6fbe6f19d8fb4e257a242d1a63680a4c947f374e60d6dc6cdfd9fd4d0a2d2434f24a444f6b14117f3716195fe8932b197d387a713412a7c80860b2a2c076ff455e982bf0fd1b30530298a57f0aa3e8236dca1bee82b0f0bc04a10587fb737fd82a4c947f9886fdbe5bc85c8d606dd83a26e534a13e383269dde454f2869aa3ba5e9ec032e17aa40a5f6f614a229c8c23328e78e7bb6721059b20d046bc54f7be036bf258ad0c9ee75126349947798e98d27677c701e7eeee4e9b3b6f6e17af92ac8780110747939f2810172d1c33b33b3b7577671b765248902e4599654d59d008a85cc7c57e9899ddd93f77070224c8891d2ecb644607dcbb47bbd75aabbb7b3bcfb294ddf08df5a16f3d6416f1221db9d38d45baafd5da223b436893b9a35da46d6c96655364c8ec8599373501edc00c7c235afdbb59e3d400ded547526f3751de062a5849da71e0d2ef56a05d03a176de836820c0d348039ca568814bbf76487978fcc159334272313074e97ba20164001f49bde044f98b5818b8a24ec5405f50f4415a4456c02f48d689cac0fcee89af26a58dbb751903d4e1ba9b8dc1713d1c7d93ac49938105995b014e237c7921bd9c0ee86131516ea7b02c0f8bebcff2681ecd3d2c3c1e652ee802cbea6ee0cbdc297edd10ddad1bc23dd1bb8c32f0f562314c3a63ba741c345706084b4e6b6d9d0fada056b7bac7a75bdd6a9f4ecd701ccbc0811d3dab5bb732bad56ab5bad5fa7c44397093de2ce548975d77ddcdee481fe9ba27ed724224d9a856abe1b6e470b84985f84aab7e60589eb83a289c90d22e53d9ae2b1d6b2a3da561ed92220667d956ebb254c5962e7342b82d70381c0e4784895549b2135427dd50e385bb552ad588eae47be28be2e3e29bc1e7830f8d2a44fd4035412dc1e7aa51ea941a84ff97c6b7e5b3c127547b2a0e5f0fbe2c9f185f0caeffa7820ff659f99a28c3dd63aedfa8546a09aa09aa10f503d588eaa406f1a551a3d4291587daf3f9e043e39bc1c7c5d7836fcb67834fe88bc197e513e38be27be2737daf4f055f131fcccb79b98f0a2fe7e5bc1c11337027f9905c249f9b16da50b4145d45b91c93e6b1b11175bcb7a926fc00e22592cd66fb3cfa9acde8abfe8ce746b221f1d95e361bd799ab3a2667744e665dcf8c672897f3e1c3c70f20a2994c26bb52abfdf8f103c80920ecf5aa128b010102e48454e5168987c796fb6117bb4e38e18494ea23d188b994ca7ba1484d75d30dd9a1dda0405b10324361e543ac70b7320551a7fb14fe3d27b7ff4675e0e0883adde3a42000b6b9096de08c6bfa4ba46eec78e890cd551d9319e3d03372b76df446d1a0339a731d2974af1c2db2ce69e7947ae7b47ede480c459d531c3948364ab7a2348f9f4430985b8e1074f84b098ed1f1342693c90629c20ddced761bc791765e3f9bb73c3126c6c49818133b110a5148b422438a601241041176f098a9591a4d6607c9d2ce6d365b28ba72c55bb59a388ac5624242eda242a57e5e5f9645e19828bc128adc451cd54211f8d598508d7d3121af5d54aaeb6b1715191e23c850f7481075fc0400272690e8c7fb48286178e87c0f00ea5ef74999a866d88f28abd2a5e8c5e755da39105005f253b7bcd5b658cc5f0ef316e3f8136ff9147fe2556c4ec516034000ac8d00f478e98172419550420d16150808a8025512cc1a050bac80c16d9babfcf57ad9fc657b4d547f09a28ef725d41a137c90acbf68e7b6b6c5625cd36fdf5b9fd74f1ad9657d7cd847945589891daba4588528caaa78cb556290280a5911a37089b12a6c6c6c4cf021629edc0c39134c30c1c70fd0880d061a1f3e7cfc00526d1ec90aa940343f7efc0072c277c348d00039e184544aa5baf15b81a2dcae403731e63db3acc66ef35b93535314cb1fa4511075fc51a02b1cda39c9d6af02d9b8a6e7774586598639942e7d8a8d71fcc9aca9241095db3147c26b1b25fd55858e8fe6f33a642e34a1361004c391c8457c016970e488f91933ce79b247aa74f97263b11070501d4b9c4a970d04a4e31de63093c9648314e186fb78cb5b505e2ff04752041b15480a415668cc99388ff390229886729d1341041176f098b1d5bad6b52d381c8c8b5fec339f795a455f4c7c70e75f60e2502633244132572f6f679617786070e7bb3071280fd7f4db6c6e2b326467ae444332d9952197f9951ce201b854ca21ea903ed4318a509230e49ca8528944b2965cdb3252d4b1248dd231d185327111fbfd6514521e1791f2dc16916e1b729bdb865c76c5652ebb2244a3f11841860a50038dc02d228184fa8df8c34e3405464209d350fad52f1e3c441def4728551ca291ca28ad42d2d80d3168152a33d19818fde37291400209a5ad46169bb49a8ac6da898f0f000260bb47ec16f740b9f0500e5582b805d765ffac19c94c311af32cb79bd77d3bb3d67d0d6ae2b8adc75c7ddf72196d36ebbc978d712814afd7475baea2412e631c0a0515a22d6aa5e5322c369b8d8d8d093e444030182c482633c104137cfc002bfbc4b45a505e2f1f3e7cfc00528fdc8cd8f8f1e3079013be1a7304cc0690133c15829c90ea52b8b949a928101b1bd5bcf11e5d96fda26117b31275c05fd5236cadb57642402c0adbf128a020ea808fc20ac7c655323f77bcfcd420387168abca8c31229148445270a4325a85be5c4583688b71281434465fd40a7dd128688bc672882b6a2bc0d09c95ce29fe14754297ff4f64a1415138125d5e2ca904135363a6f37155e949394a24120d8e1c3c348efec9cea51c21d0d4a85163e6c71b3319cb64b59a0e1da20e7d1d6f63566deccd28271b8d723cca8d681d9d213a117b5e33981f4d30e3389222cc00c11806438ebcd7fc1a2f2fee6126a3f0c78c3bbf0213a77ffa56a4553d736171e72360e2b4ebc85cb9fc9cbfd3c25c819c73e2846b5bb6d08807e0cbfc34a28ed5318ea86880b6967cb1fff2a5915d2fead89717a3b0cfa87f4661ffdc7e771189ed1a853370c4b976c2b976b2a556db0223c20e991d319da25f27e24142c7fc20585f04d6eee331028c0822883adeef1895cc290b2e01bd7134d4a24e1d0d893c5878f0e031c29c2bd287ebd9ebf5cae57055c85e992db4aa6d7d9b314e7bf16deb2ced450fbd7acbed99f264674e00780140005c02d043ece14d363edc3cf567c654f5cbd1b8fdb5061d7eb85ddde6ddafb64d54bf8d1e3d449dee7b3c6ad4d566db5e2f20adea979f027039c735dd3de39ad7fdf733ccce7be55eafdc0addfdc8516ea2fa459ed1d0cc552293e61ae38cce9835fda213eb62cf088d89eae7da2857420925d4d8888cd860b0d5d4d4d89800eecc84d8d8d898e0a3ded8d8d830c104137cfcf8a298be9c0d1f3fbc1f403a2027d01352a3b6798b719b049b281e46b954d3fae56ef3cde846d4a97f23ea44694ad28e562fa752893af455220b3d51583990ab4a4f7ad387eb17d77c7796c027812008de1a4b979da54f2fc6692f664d7f6ff962a2ba6d968ab6e51057c8713f1f1e2c3fab6f72848083e6860d530e9739519452caa3802bd6da6aa24d9b3f6ba2d8237830128201891ac0cc216ef94069014c124fd460205e5c97bf5f0dbb72590dd4545cbaca7d795607f54c0928f7e55fde45d47949f9b82e2f8a3a2e7476c524aeb8e28f441d9135bae116374c82892028b51056f002b55cf0aba80356201778c19353ebe87e59dcefab1648d060a108918527c7f30670bb9f473ca1ea2d2c98018d871573eb932e9ed83067eabb5d4abb21ac8629bc251ee899c2678827b6c801085b60a1d5b480a5a4c4aeeb26195a7cf06304142018a28a50773b100401423ac2dae430dbdec1eda75d788395167387db7541467c71bb5711b713add81d2c84c2ed6ad7bdcd2d81e20a1a9a6431c60f4c8491d37dd92987c1eddea653dd03b95161a7aae036edaba2da6aaab4c64f1618283c323f3f68524a821653440d3d9094b0c114e1811e1b0557024193175aa5b8a4bc48364a1317265a16a8899884cf0b143b8c624c517c5cea14ad10099a58c40b442c7c711b1551d3018c02147ef1438df569caa6ccfa34afc854440f78f8a4e4402639784a541e6ce8a0d0be1f6e80021b020729b3570f0b64d8a634518227080425c861899935c1a2c82ecbfed721a8408b10b4681242931c408450023474d89e0812082df15a8490c65bc9b185871c523c151f031c58fc14386eaf448e30706002440e2e6c74d8d024cb657dab091affc47634b8c0926ed0b1c5ca08a1c30cfb32250421ac8d220426acd091021b7367500e34d021440e5a7484c0bad410021176e4430833b0b5881c3eb0a5246860454fc0ec480a2e2cbde26fb05e163b58172e72046161ee0cd2010c1d3fb6de19a443ae89a53cbf85a54c748cc0bab460967467504f10f781b0f6cea09e283e58f1cea09e2774dcece8cea09ea020ac7767500f4d8528b199254889313c6807105c7a679012627c4294b6a9cb57c1d4d4f4ff6fada983e0b0e110e64398b1d49130413f3e23985af303b4ebfddf9032a1b61e9fdbcf23dc7e864d15133188082fae6ac636837eb0dce69c19d444d4511b6858efbdc5efd2b7a906a5ffa8e46c173741ead08c00004010006315000028100a060482e17838d2d3c0d70714800f6ca644725618cb046a10c3384c1965083140000000000088ccccb601aa72704a7be73df01bcfa6356de84462478a11e119a7e9066a5caa7a5e3ee6eb4058900eb35831fe8fc1708a81bd2d302ec422cdb06495114692760254d93a7fabc0dbd7fd05277337c406f0e5935eb608bdbbd9ff6f635c7286539cd3ee5598fd284380df2309116a13cd93106991e2aade18b60e90c93c090fe2ea4a2dac1081b1ed0e897593c09c4d8458ea4a93ae8858075b6a5c22318b1b108be704c5fe125762914362a5fc19fddcc034910d28c5c6d9c79a78f076ed65abf9b10b34d6724122c7213767e180192309eb27f8ddb40680bcdd7c80933d91953c09f72f217484cd9b4be890af9527d8d9863929356d16ea045a1f9f3e6e0a481e3b9943573408b7c642215c78ee8e8816843abd00ce76f0683643cf339fa89bd8353bd6aa95aaf045a1a42b024f29eb2e55ee699867ce8aa7087c081be6c8c24d5866406d993cc4f974c3714a0f2a5b811204ef1c8112645fbfaf4f011791ae515254fac5472bf63b8410e9c09a06860ffd47812a376d3ab3c4c363086cd04f5871834affa7c5502c7586a614ca72b05584179268307cec07d5e742ec3bf81c85377f6d11f74a89b8f1763818b0e99430c4a8e0573a7a17ee2442319bcffa10ea049ae1946a016708fe78fa57366160da98a00e42f734c4c6b00a5b0711740b132adcc7d59b595e72045ce02c8572bb59674a14478369574a1155e8c8ea95a75774f44c76e5d4adba2ee3f486d7a6995961f73abe303a6225420bb119a3a2e1eaec84db3661646bc7df021285aab8d8fbb04978c9cd56f538a7862ce98cf8ebb900b0108055f3b6adfaeb85adbcdbf5bdabcfc87a2d518de55fbf8025a2af3e05a707868ddfa43b8f939e68f2e9f71ed930b16828961a5abcde44f736c4f33e85e34a86486c121db93306720431070ec8e2a51091d3f8698d9cc61e5f145509ef9e81d45b543aaec46e1cc512fb23b0b6df32209767bd9a82cedc74efb22b6e0601f9b059ac97fa28df0040edb05b9089c9c94368a48a06eb0c8b869873526fd58e182927f510ac32202a7b48e6f911902db39893b38659e41f16f7c3a8d4f076c15895dd4db32aa55c7ee35b7f28787d97acf7d34d36bd12701e2c577825b081b88fc53fde755294eb7f65f0ca1d11f5f2a8c44fc77b0f7c7af19475b637bdb219f4ce50e9f7466bbeb831a913185b28e426220c3a59ad4bd8d2beeec4c425a03c814595942c4a65732633d373094e3fd3b6646e1bde7d09cf3e858d7331b1488a124ea4be1ad6cd823f702d9314f5ec6e48f02d91c3cfd5c7385a82c880ba3ec4975101a0707c1787a1499eac4629f68fde0556fa5ed80a9cca1902bd5178ff8e0f89e59178a2568a27105d2c259b658a19e3b4dddff7c25df4b3518c9cbc145f744ee8bde8b6a4f86bb4c9f7cba8ea89f30aa2be4a142f157cb8dde2dd16da4e23ebab9a3c6926a0c7700696d0ab6fba901e617e653af689ac338e8a1bc6b80722b0d218087a5b4b8f2e541a06d91c1b356c0b30649b4562c645707bf1a5132d714259c2779b25d8fd22c8368bdd1dc4281a4f2e3644c5688a8fa5815a125716f12ddfa1cdd6b81fd5c36669dcca258e812738d08a6ea25bdfc8e42c5bd4beb3364b87b4412a5ebbe3e84f8cf0a053a70c29431f88a6958fd30f1a508b93b7be495fedd86fb3c8d7712ff35d280974a53618c8ce7880fc58ad247c6dffc8b1451d7e77993eacd15a8422a0b70747ece9a52276c667ff2c782a11c4336a1e3c7db6f5297371ba8dae77e1b19487e7da342f08742b20d52c0432602a752e8f9dad9988f83a73133bdc37d8d03f7805b3eed9d6d79d17f2d170e61e3324c4c8aa42f3b22fb1436b8ad535e2cb87479e2fc7ea39ab3ef4ae987b3691591bbaed06ec7b31a6d45f4aae0bfaa532f646c4abbc1be3d7724527fcc81b448f1d3eab3407015dfb15d06dc9b089f94a184176c7c9cc88ffce64e8113b7f834b99a52cb02fe40887359c1b4b472d367c6f78646b93a4c0b4faaa10d30946b48cb30852ac59e278c8e1fa5a88a6bc6f701925af2d0805f1182fb49faf9f74f010a6759620628733e1bd143d6f0cf94a2dc435f68e808f843d5313502a957e6f0fb544a0a5bda74cfda508352917132ddd3dd25fae90e21f0175ee167824072cdf453dd43df307c7820088e26700d568a88034dd3bd2978841a59404ba2bfeb76b09159d2cc5d5143f902244d0cd16d17083ef00b1e7deafc5cce7cf49b6c381033cf4814a79f8f548728017203c78f401f219ad18450852d0204bbc0481a07291745c726da4c26695df5516416003548a2980f8a9b3eb23e4750de15df96d8474c7eed05da1c58d066e2dc157e980b4d3717dc44764a0c70645b26b99db484a19c356cd56c9472ffd035ff3e893def732e9cc9f09b34e5f7d37216a17f959347a7891b79aebacde267f2f927eb318808bf2d5ee69a29782782272d0c0b3f0925e9f78bb119d9a62241445304cfb6ce2e8ba537be601001d105a10516b8824d53b7fe6351e3d30cec765de62e2a186bc3e4614a50f9037ad7604ecb94ad49bf95ee612d941f76b9128edd265e69ff40f3c092680dadcf4f6a9e6ea8bd1bda422c30d79e7cd5d516e11f6265851341cd33b0e3ba67c2f6c03622b745bfe1a394743d849fbd0ed63139df1ac02b160230814dcb2d57cacf5aa1e29c3d16013b25e28cec74c2d7829bf1652e20d1b9be6be28aa027b2af093e6eb3daa721ae19ea346cc325b32242696c2be6b6c9e632380cf9fe935ff358a58100a3be33e4d80e44d965f1bd0f0c3dcc3d1e0cebbec5f169e26bca753cb3a8c83ad0ebd2817b8a9debcf22e0327121437a70a80af910f3772c103796e5023f784802234581e5601576ef1fd8b80016b511478050a6947544953c6532b248a4e661cdededec4f54067f13e4e14496ed96978a514655743865ffa37617be256ddf6447bc3ab02d073a0d28f0f468514428aa9a761e669671c2d0a25cfb11f448344119f3a716a2f3ae863acbd68297c23aac0416f57291bea68895c7b66fcf8073d3047fef06cc58b6a62e88d91e6e9fe4d7e17f31359f20d366c7a066525412dcaf09b9968448aec0ebc367386077112eda944f509b16bb69a88611d5de0cb5ecb8fa06e2626ac1fdc5f1c51634c0b47c051fedc8a359371fefd8f5edd4b99628b033e63c16cd74b2e9c0e151b559e626d6b02d7125eb8453e7a4ca99c4838c98331abe0adb1c6d7dc3cd7d83997a0b0c6d1ea40abc3f0a670e7540f0b732e4071c3d20526b6938b83f21073110d8a9c985da628d5fe8203a7bddd2f25544904a0694b9855b004019d78d1cc61ef0977394850a6e98d84b199d67770b7c40bc028f781dc72f22242fb00ae655ccc192bfc4fa417681e09941b5a7a730d5b0a42c3dc83bb8d7439d6c85485082018355d61e6ce336ead30890a8cb5a8a5c62412a30db7eb8144c5ba2aadd3fd48bd5c2a07db1f6caf9cb91a846564a39e27c1887109bf050b92b367a92b1afdb04d589f9749c340666888a16731fe38fe2558d058ec54327e55928e8daffe40e07cb7239e7ea3619f8f39521905cd136151da11e000bebf88a0e0770a88632079d82cea422b1213eddac7b6efab0dd2c54934e2b16f085c60803633573bfd53a3c8d98e88cef89b539a9fc78cb946dd6acc3b5c7fa4153fd59ddcb9b8376180951e1fca00666557498e068c9e71677092c2938ef22821e0e569555dcb0bbf500de7f083f7a5f46a7af5a62c444b23744ed19386ef2b62899760688e3ea07be4312b523aec3e90242d7f442be2b208ca80c17a84a8b99e01d646276f0f1273deb634ba694e292153c77dfd9d7d4a3ee62d7d593e488de572119977782060d24e1b5f44400f23af008cc90cd9b465024baefe4401c74b1acf6d580216bd912c95b263cafd7aed579a13e5ae27e135432afc6b090108dbf5a68b7d4fa589b14968a5a18cae59d5ff42f646211f214a070a989d2c2a93f0051202aec5edb4d5c11a87d24cf0714d6a6b92e6c2b3e60286e494ee3f755a710291697cd2b51b5eb945b3fe6c2ca2907390fe67c9f4845c397f2f0dc429da0a2f12fe37817b581527f3b8b6d079d3ff98c89448ec595d93a43a1a8d6e52051891a3ad1796123251cfe51e5c884a4cc63dad95d7f31d80a2f891f0503b414341f6f02ae315e17418992afed80040b3826ee7bf3a41a54e95ca1abf4fa5a465c2664ec4d421ed1c9dad8419e4506cfb0fe351c1fe0e0574a52ab3956eed34185aab1af379a6f94c127a21a39f0e1dfe0067a9b2893f50e4d20726177df5c4d1d3feefc918ba273190e26ef73d28148f3a0d69c9538b79e8a64300be6835bb67bcb27f67fb396e7820e14c532faa46f515b50ca50d3ec26d82903106904d1d356af3f505ba9f360bfd09ab1c29619811921cb48294017c37ce72e756153402e20f35fe7707fc0be6960e812c19c2b819e66b8846ae9230ed90e4c02a762c2264f640b4be5a848e0ef9746bddc108bc4ed4dcfceab1c5230582c3efc98f97b625d49c31d028e1ad17c654d727df10c5e454c961fb1cd376c0757ea4bc37346d984ce2fa5094a7f1ffea4fc5a0748e0ddd21fe31bf23aa526b3a12e3071fc86de3e1d5ccf09ebbb8c559d8e7583f2b98ab7e2070ddc44b6516b68406673babded21b5163df4515d45852d6690611374e5a817491cf5df1389057cd82a134a34de18f1c609f657cc58b32778e5944ccded57b6c149371d1074c38de42dcedb3a83307862329daad345f9934110fc62b027b021af054a9dbab52ae8f526f9f6a6ea1515237416683b470f4b828d20eb75a73d1b33f4143b2f1ae458eea97de47418f9c2cfe366d8a214fbe43eb22bfde41e044b6854fe583e6f6efa76d2e3ba04dfb1aa9bf54c0364f0b8a89689232c9df4388c736bee0552d8d02b810e25b3a979f9b1ac4a1fa3952ff2f06191e0107d9ad0f7d258061690c3ff72aee53ed62be02322708ccd76b369bd6493aacc9e5b84c3195d8718a8b9b16d6645ac423982785d1c5ee94acd632321f4c7c9c1c1ad0eb3f27d2c16ac3a89100e28f3c353c740a655f25adca50271afba488873afe2ad0109d56b16297eef1e39765a56aeee9b53d28c6f6d3ffee594ea8ee300a5c61466e1029ad70d00b5ac90778838e42f215439d39f035079f2cd84bcedef4778aa1f1dda2daace70367564c463af545559e8046fa77486cceb2217df07e761435f3cacc5cc7bc99bfe8c6b92134a1fa88c09a1be6c92c07c2fe57a20fd7ab8142839d024612f1db5f9fbef46f01989649c3a15d3582d6bf8f4772e6120083e4cb5872c94d77ecee4f1be3b6cd77e0637c7864aedd20b2e6e203fb15c627c027cd7393e2f03072c5e0a93c5586e73299a2019d6652f3564e9eb343312c7ac2fcca460d0f8ddaa14c28d6593d19ed94f4e51d8ef6ca108a9f0775e6ff6518ef9dfa5e02e506e9b88c38278697b38ae02ef9bbd1d51fb4f9f59e3a3892c3f575ff6fc04ae44988e24f4ef4e377815713dad4fa8f897855a63c732bb32f6d525ced1f43ee04206e6b4985264b48ba6826fcd8812a836ccb2c38318be8c3a80e0f08b242a341c3d3bc30641d4ae042a491c919bf9aade8ccbca3e1a8bec4f0d3c495fd3302b2bab3ac1150d317e41e2a551dbd8dc8e824c6acf83a14fd90bf7b961375ef7346c06567369525e20d86dcb8ea1a259d086a5ebb50a46c3cc5a55299f5d8c10e8b5981773d976eba7feef35190e24e371bcbca94c9fbfd24dbc4f5f28ba1a4ed1068cf905bc543f907b39649d4c7c84acf64f8efc7b0b4ed776c4b3309b448e0c30c5830a07d8997127ca96687d8f1dd2bbaba9312fffd7183e9ff61cd2a059fc5bdf4cd08423cc7fb8c61f8395b84fdddaa82b2688e6cfcdf5b3118d9699d92ce66118db15828d1176eb63ea6ab3cab35652e1faa603e13a0d9c4617d7d37a87f3d66b351e76e734e3abc786a6a380cd0030d8e5c4c428a532ad44e3e78b7115289145e33f64ef77610fac3807bb2a89a16f7cbfc203e43eaf9d262c91526b7127fb81f57d862b4aef3b5dcf2b1ee7296cfb60fb5ed9fc2daf510a41c6324c301760479eda827f2e8140331f91758fb44d24741b3ebff3ee5265e8d4e85ace09b29b74f9cf4a40cb14ed60e1a729f886a92e79857957780a2d00d50bb984418413be3d711a527f230b1a4427c158076bbd1c7cbeff96d4c7d7911f4ccc192bf5bd9dd6b2cc5620f67866104edd63c7b221949ccca46ad929130645e06d5231b978a1917278829ab14781d04a2a94b131b623ed606b690aa2158316ab1480e744e6f34d1866246fb9884a4593ed0b37843e9a10f342748faf4c788361de7ec4822b53d60a77571567b439759d59bc8a97d2262638f2aa5e398d9d1548126442873f7fe3a43a4c733043d1a3e7d0345c04e1dabec300b2738847a998e82386f5e57b7a26965564e9ff0640c2fee4f31e125c2895a493de5ca4d2200846b00d5d127c40d00f379e7ff6302542daa803ab7dce132133833b300b9df186f05d5af10f9d9d70175879c14d1ac4b247a571fc62e2a208ea05dfe7037b187d56a0e660c047a69c3ea036c842a9ba0eb24a5433550769b8ae0808bf0a5354e01118ab54612d9cbb8050f6a4b5a84d53bcc06d147864497ed3582823a055a26ea6e58dda4590261590a20b77caa2b9d1e617d3f302a3749a642234e5288add5e94ad14f155e9e95c5a114def32a293205693ef0747260d7bc244538ae11ac2281c39225ef2e39f880d62bf5b965b5ac9294d428e6b15b34b1f888e72df463638dec7a11b5edf9762c95bcb780ce57e79d75040a08500ee96f3b3c4c31ac70ab19117e60e5a8c59d4c6b4704e420d26f2b6a83fca05c3fb91e0a6e3706fc46780421973198d6f9fe2b3e648aca499ad9de3926136289c3830ca0672a0923e0b6c8cd54c2ef125d48388c66faea2fd098dc287f89e14f33e945241c106a539a00ab22ec6266bd28852322b979e50f3d8b765d0fb0d460cbe8b1feb85477466b82f12ec251b733919bae22ec8af82566ae93a512e4d03ccc0aa7af77f24c14f9591d95ad66544b8138b3de9c2280a928bb8599de023fb377c9ac91089ab10cd4975e08389c00de5f1109f58567a8a63a75ab8802e4c157bf631f9859a532398ab5925d44c2813bbd193af9d051d5333810336766273a06d4dc2359a152c7201e6ed1560ca13178e7066a9144b446f7e91704d08d91aebffe12fd7ee02a5bee09ddda19c60b41ee2e4caec35b015b70d57c17808283515a80c02f5610ca10233f05527c812f86608abc5603351bb12f7bf4e8df47d2fce8957b97d9ac3586b2cec96b5457721df2206015c45aa700e5fa8a40b5a55ab488a0f538b55a4f6680b5aba0b92f3f1fc57e0903cdea88d1a4b72cd1f3f4612b11e0597a2b66c577d33464e246407746dfe606ac4ac8e834664ccaeb28ecc203b9b38b7ef8af1472cd5d1932d1843354b70a710a7a6f9c30a934275642982722561df2c392fc01a4f6887859609e4175f6daf58d05b9a86ae9353b5d8f6b7340f7c9dd3dea7a41420db2739405621c0eaa2c918191d6ba69632cc7f6cd770485d3b3619809af248b5e869fac7757d62e1970ed972d9d9f5fcce478c04b30ccbf2a1e337816694a01a9734b33c456852c5dfaaaa5161dc72cccb4e9b52604cf6d10669e888caeb51944dfb15d94f4b6dcccc95a9a68b1bc779a34ff2fb3e6138fac32bc35ddc6a06181dac9733465867f506427ae8b8442f88a2117181d4a2b5fa07cfbb18fa6569d6ab6c42b38dc79400dd23d01b6d19536d2fa62297666aa9fc15c1d9aa54db68b0d3ca0fbe8590b87cf684939ba02579cb6ee248991329fa1dc0017c631f2563ab38750535df86865b0575ae15ddeeea0bb3c4899485b6c49db5c369ea1fc6d9388d40fb564ff6f599777f4c097d33aae2f953abf979962cb4b9af8dce33d2ef4ee5f0e66a754f747a4d5130da69c3e85b6f421a56043fb7dd828393d7532f6b0c67d6611d962f2d12b490b3a0f01d0e5cda3e4e06b3babdd9aab19003c627f635bfb71080d919e24a198bf3dfc9bc03d33feab9e8efbc934306f1320bb26923d39e5320a23949c82816f1078481bf17b3973c8013f919c823018507fc0cca88cb08c7cee87a6af1ad0c884d6972b7fc2a014df3d120056165c0452c1ef76420fe7ea13581623221cce527e911deef5206e5de4e7268bbdb866b0995110a3a7eea2b8f9d0695db87ce09fad2b05360927bd53c53cf617dab87aea5898772d90b9ace349405a2c108774d699d38dcf4fc4179a16d4cb8358084f313afb5f5a62962fa8a7bfb188a87985119479dad34fb3c9381901bcd019cd7540f41269bae1840b99557f013c56e1a5906ccdb6127e215ccb07fc120080433961065ceaa84b04d93ce13154b1e9a8361fa18300326feee0bc9663fc1907582c937386fa2d5b55e514dc8774f2ccb16e6843cdee94df1a4a227e94e5b794b348112a400ae2fb5f600a60e6f362c714f047e788a022ef3f4747fefe7664cd7963134e8909147e6fc62aeb47a6d007213d3e385e3eb8feea1b0c5e3a3c90f44423e88386713bfb3159fa0285cf13eaf4889b666cef2b46466757af06a11c0b27d45ec52a81c4e6977772e4a515ec5854e8a560c141bcdb2d6c7172e0ac89a6dfba4bb128378fd41e7224bfe4da57b2e314cf8f5249ab263892931c042d13a127231e6f23423c48ea1a6244dcc18456cccb7874cba73e65e7eff957a82883b98ed4d69426c5508867305ec0412e21f78578a2eb108f5a9ff1cfff991715eacbd79913c2ac6bea13a35019bf6398854e65143cfd2b919245c704e2194246173481b7a1c57aeed024081e6aecb102e8648c21b91dfcf300eac91a6bcbebae4f3fff571e3514a761b270d2dbebf30803eef6407eb2d6f8840be90401f949b55f570792aa6b17c94325d0ba67c7442a31a6f99e8575cc5756ae3f9a1e996563dffc495eabd9aa28c9a815ae316adb49cd329eca9896d6b7c7a3ea40c5dc028cfa8810b31ae9d645953f3d60824590beb360c320630ba30c601101e700a67f8363b12ff09286e9cf01b6cf213b4898203c657e441f8240cbf4d2075e04d201ebf713b5fd58bbc86dd963bb336c7dfa946b7cb35dfb52196e142dac014611efdb1aacc4682c2cf76c855fc487cf8a851f0c61ff064099adfb166e4504f618d0a55f1a521c2b77686a5acd799e07f7623ff656659ef5e63e470c317ffb0f3000ac3905a8468d4124a08637bd187f41821749c7790f1f58f3d6c20026238e3886347cc69aedb6a1a08bf844158b31cf69d4900e449831cf17ffaf7f4424c298676559b1bb14b4c90dd9413ec2274f585656a52e31e5288b2e474b48c05081dac382cbbae7aca55e80bc01be1b63cdb08ac8306118f385d95f65d52d98a99d40928e757abd84724af74f2f058e06ff00faac44c72eff978a014aa9666cea8282a5044ddc459b981b2e4ce59aa15b2a92a58a4b757daf6851e77d8d35b085869aa7fe00d3426dab06f11c07e2146551f01da24a8b70fa10b0c860b9639bf46efc3cd1339ab78b58eb33df7ea7942e5219354708b49aa93191c349d226fdb0e350acc8d521603b8440bef2457e0351602923811a405c485608909ee31a4a16c8579b02238a08b85832db9db7fc92889192a42f54fe46b91b4d6bb03a02b8d3d806c2fdc12f57604b2dcfbb5a80c98242579e846506908137d19193cf0084c549c9341124be119389f23283fc2e6bc49688cd2bed9d46cb44fb45dd0acacce0067b6525aa00716351e0c74b9bad5349baaf82370e102234089d2f72ba9eb66171077c1c7eb7a6890cda91a29fcdef3a44304d5651db1c92a87f3428e271ce36699e0d5b82437a8f8e62b9d7481cd983161907d1dac2d55317407f1b63f80f59b4553fe2dd7355cfc2dd53c8028e5543983fc2dcafb0dbef58613823650d1e6b461da0fb5503463e955618492019c6208fe8d6fe201e4f89c0e1ce2ee69288f375569d016b947ba089a8af79f435d51bc5aa7b0dd1f5064869af2fd9510dea90442dd617d4bd2a4017a82138136da7b5e0652ee0887492a18d437fbd8a2a91a02ae0eafb783005a30ab81ebb04815de4650feeb023558046d2889aa1c5975bb3c943b301477fcf2fb0d4e85c335e8885ecc11611914d66e4eab8466a7a1937c2d8431555a9c0f9808c7b5ae879b743c3e28489a2515b74871ec1ed5e86d2f08b4e1275d4b13b8340db2d94ca532d8163a4a71657cf589ed10913ead31a4e5548553f23f432d9494deb5cf48dce1a778a8dd17c49345c898b355f03c7d4543c75e46c9eeaa812a995b7d8c74fca8f597be2cdc174d1cbb21e2e371eb0c091a0dd93eda8f4c999a65f487bd6da1ed2a2d0415b1b9008e97fe6a3ee0c6d79b06aa0aeaf678e2c095dcfe181ca381e4cea8cb93a974c97b2a50e2c4b2bdb7d76f744111bd193e598865a5fbdcf5b656d13a0b8f709727cba236e5a22d2399ace88311882c56a1472a2030c0914779f43624a917431072a2130416eeace4fac6adbb14a8775ced95fc69e0ad0e403f81599750962905fb4af55f33f2473cced57fb3ab14531e5e3f63ee4d8f1a951efe5925100b86b5fa4098d45834f59ce2d372fe4f6078958ef91fe09046b02c4fe57c04eb72af3fd0d8c0e155ab854c80ed5e1f83daef167e987c0fd35c309f639531ff6f3b40af1fd4d187ef52d3d3f861c7c77fb965864804e863395fd7baca1122e3a417a714b1abae7d82f3fea339894e19f8fd9598df3658267506012ccacd03ded5ce59ccca2da710091e136a6f44dc1aedb86d2730d746ef64e94da0ef435c870c3c59d11c1ebf86e5780fe735141e53d64a2d7f34ac2131e0ca3aa0dadcf33dbd4985021f2ca4948b768c647dc0eba1298a298a39407d110263a6a5689203b7913b7aae4a1a656757b3d38d7d46feee20069cca1623009a7c020ab667473514ac4c3c0d8faae0010dbcb2bcc6e1c051d0fadf8b758a120bee57b9556c0e99cb34df8447a3b16925e772e0a7cbe75f0b4a1557cd2d366b7136f5c2cf1c6f193466daacdf3dc94e38f7bfa53368ff4d22a7bc079c07a4322463c2838951b723c382c0df1382144d600e207139ab73ee8669dabe9a1df15d1a1cdca4c2fe544ca6fbf4017f3a2f34fc6a19a1d9b2850f29f70367c7cac1b7431d524f7908b52dfb344e9040b2a4f630a094dc72b51a521505771e8672b7003d6d777c1e0148caf9dbd6b1d7045ebbe373730b95a4ec4bf41d7cfb54ca1d4635260bcbda2aa18260957fa23db1a577157db79ca00aa75037bd8c56d093a4e127afc296c5675020385c9948bff70117da9af3a2b054f7fb6ad920637173bfb2a8af07ed05594555baa11021b86e664ec4ccc2d57e8d19d347ea2c5addd40973bc3aa84ee2d8f8fd804e42118221c7eba1f32ef2dd9123eae1db2ef9f2bcaa3377ae7a15604542d4fc3c9f7679bd46361e103867a1597b4d7e10edf880b8e3f00a6af0e8ca8a3fd8aeae4313afc64df21579eaa252b1c21a702702882f1e4983fbba96c565a90d0cd77f0595b5eb34144be271158868492db0e6cd1176ed216a60ed23883129042bdd69ca1cff09dd7ee9cfc676c2d55b836652b2ba906ea367f084d1bd5ccd9b4ec9a146affbbd31b5c8f685babb71a731caae771e2094cf994a8ee8f3e6512e374b647326c07648064d25e1197c67877bf0b4c26b3803100ef328099ae73f4f372511871a2d38c612be08386408830846f0122ce47dbdf40951e0b06c70d92f54a46b15d3eb7d789754d36ce06481caa2b131a3e1c4eccd81dde17812255501c372fd60b182d962eebd98951c47c514a6e922c7824c7c9ebb18d92383165e7f35dcd252e9aeb0691c856d35cfdcf00b61f16e808ad7b1a7fba0d94454a619e5c4609c311119a2bdce5eed864f0e110d5392bba9fc6312c4e4c5d81777a164f3fe61e56c8f1688016a445fe8e076c5c9203397dfa04fa81addfd3d7815dfd4531491bea0a6c6536e2af078bd6ac132d290f98e534c794c5feae5d237218149166a41ced646526c280cd9d6ad8f7c67e4df46b044c1006623b984f9d99d312c1a794ef18713c43bab531c3bf66a2e2914cfe028484529a9c6983e4ca477957e410878d2e4dc0160f85e745aa54f6817e1a3d98b4201c5a048e12079af968f66158f88652237187e5559e583156cc4235beb9487a671e987cc12cd3f61914604433a506edba4e91b43b4241bf9542ba7dd0f41d41d9ebc18712503c497ca7bddfc472da8fc3eb926a16539b57b0042d837662d8fac51052dcf98352836354edf83e6e15531f351f0f442242f1b063b471f56162eed1afe5c6385721f2291466ff06aec63c0fb942f5ce1a25bf23bbbf88ca0233a8bacc7905a9106276ecad26d026c034ea1547b5075cf28f456c3350385ad0c6356d7a8d1d816df228d1fb2e9ae49d76e5255a5742d75c5362047694838fae1084143c5d83493e4a5498af1511f5706cd230aae8bf3d337ff37815395c109e6e58f7a002a8a8efe13e83802b0a54a2d70a899aba18ccc30520eeee78fa766c20020fee69a5db67383986a77eb241aef039e56a6613d6d3ac3e36146ede7820562912bf41e8007f8e4c99544de9f425ae5966bc382efe896416cdc50004720e3d2e8e72bd5db343a87c12779cac7d919bd7ef9f6ae6c23ca112588345aa78fb843f51d58fdbfbb4b04d0415e1b4631a2f446f621d4e8b50cbf6fd84fac199389e785151e6b7a7c4b0ea909cf5113f3f4ec44f5947a69f17f92abc637d0bb1fd09ff688bed586320c5456dfd78b05d12b9f1811ca32caa0535e4d38f7aea48e32d3c9940998057c566431560fab8555f6942329a2b4c07debbc5b1cfbb41f4082ba362a5062dfdd5100a2fed117a37649b40abf3bfdd793ffdb6849897823dab86fd98e0c089c31394ad2882248e9aeca84244c42911a6909c2592bc0ea313cb1e5fc4c7c0b2fc31229ce0771d0080084e317e7ea53bb21401a1a5bfb044b7075fd4541d1c66d4e3393c7420458796a00ca22ae7c9c27d7134e91ea79020602cc4d59121235fa1266e47d6d45ef14f052682f83d64cc7098a8473202498f88530679cb864a0cbb940a3398b981c27a04a417049ab4dcab9577d5927a7c70a63859163768f28383265754d36141ba79be93b9f88f2053ccef8af3321c54338cbfb25f2cbb888f065e40c4fc4aeaea43553e721627512d91bb5b32e350c988f8da08c9b26e1b577c40794f84707dc31eba941606f903ae7055ad72568ceb84fb25b2728edb32d1829ab00562c305997674a588c9ca779fa9aeaf1b2853710bd061ded76cceaeaceb30d14297a03c33de6100e6368f7c8ffdf3c0661e7a75063da58bc180fe5282ab1349953dd2f4cc65e9101b7784c4a2d8bc4967ece49dac708b5bda88f0880273ca00388e6562ee01655d7eed760d32a9f647641d1b5434a2cc211d76d50bca64b13c54051bc70bcb173a0ede1a108373d43be01929d14c192488e7d11a0f1b88eabe3b29def7687f6834f39f7a7aee56ff31dd50937b7161f133893ffe3e98f8726ce6064b206b1e447000f790bec58aea556a22236b83e499774e105af1ea30aa1545288f1d3da16c3e1effe926851bc2fdf203acffd56c13c1e4e824fc4bd16ab98bf00ad424a0ca591005ac96ea9c0740302b8791c5d0e91e77916722c3c0b6ebe1d6c1ab48ca6860ee6cf65d400f6a3432b8f074e340a092e482828ac2ac0937197913ba0ad902a840ab90fa2bab91048a608d29a6672e5e91756a43ada69f90d02f3dc21c3d70f4495619cfedbf9336e715853461f0d298a4f9f8d1fc390596c22d8d2ae0e4650461b1e4ec377e4c583d88b144b45ead3976808bd7429ad01c4a3959d640b87ab6782c9a92b5ac09b9b330a73583126106a706678de101a331a5f225059cfcb6befd0956ede7c137ba832cf8d05f20ee35320869b8c257323f0f68861e28220eb467f183e479365b0dbe39a046d8ed54a59a2f1fef79205af2dbd92fb1e606dc53390102625c80d8080180c71ed31ba3487832cfc17cdab726114bde6c4930553ebb70b9d0b36af9b84fdf8f8ec283363319881903b1e6a1c3288e2c6cdb3fc7a837e8637f6095f90772a9128963abb5c782442b15032e3c80a431078e19e58c2e8295c67e073d9eb170627412601cbb096d20c4950ce669b28826bb4f693ec27e7da3d3e91f83a5cd8e4251a0c1ced026e23903312f8f64fb9ed2cad1555b44466e1dcd878b02b2787164b52854e806498a06894b7508a7998a2ac479cd156885c9b27155d41245ce4d671866812a120399fc2181634ecc8252a59324d1257311b0e90b9a51a09f1a6e95ff6221d119b398eebfaee0e5530a005549634c83f611ba2a6b32d3ed1fee452f55d1c34910a057605514fa3d9580555c8e22335e8b833b85a5942a34c55dcfe63b7a170f48c1de0c5e7e573dba7480d9df55ef1ef7284712a79ad8e4161731e51fc60f95acfbed2a634aeb224beb976f81a880af3b22a04e83669dba299a04b1e607fdd93438557c19ad12ca4df6718006ec2918b5f3e9d423ebd68952f48e047df77176dd752513c5181befb463420515e597cd1a032504017ad29c45cf24bc8f2039ae278ea6961805bf59293a009bf711d5416d4e53be382a7c01cb78c7c557a22817a49afc39647ba9ab1e1d098d50ea539b49953e4829c4b439b6964989033cf33ac29904b35fc33531e5683e7344c6da282d209b4cea3dc19ae82eba688beb51d4a2a3a45aecc499baf09baee71eb92d2125351e92243f4378d7ee4fc5bb2d862673a85a22130d2449492f558eb7b57e446730958789fc91ecc4c125216d66cc0c4651d5aab51338ae9dff5e895f2f886a58a334438a84a86e0e26c56b03481ec57aa88afdca6c802cb928b3ba3251a4151f8eabddd66d9b8512163ad392d53fd4c761e65a213471e0a4e888a32a25a3b1ca7ed61144fa5fe835b00d4013b9666063ad55cbe7f1842b3f0bb8aeb5fad70c442fe90498147fa225ebd5204c4238b3b7af9f6c3c4acf3ea023cc92898ce6db8c1a9a92b7693b800818a8b4d6c89ab603140729fdb6fd404090803934a077e333e549686b5a98bf84d51b42fa1c45b967bdf2c5a97eef2472855d11cd32d85eda65cea0bcecfbd221f5b7835f5dc5032558fa80dfb7c0ced44aa2c5eb74604ad277605ce7b71a2efbafd08cd90c5d0f745c175978cf135e5b7ee23aa90e3bb87435f08a4f76191087667ebb48f1fb602700908b0eec5aed19cc86bb95a77d512910c04c6560cf5f5803d70eda1a83a035300fdc628910c5db01c84f115ed57391a7f96c1af04a79316a6bbd18ad4e3d7342feeced5a61ab4642aa34888e373e1aab704166fe11f83cdcd1549da375ac98b7034b9b2c04993676206d4210c5c9cf07a0db30066e6289d8b95b458bdb2df87f5d15cbf2de026a57156b54c232a18ab40a733f2230f807725705061cc407c6aab867a4eaa3da4467942e21fe12f999390ca5717bf7b58d9c885b543cf3f091c530a9a2e15eb52330a77e600b93903e0e39dd49264c3361f9062803385cd4045c2190e0b87bd80576202e8f5cab23f6a5de1183bebe0c6a6e7dfae95963a62b614cf7b554b3ef7b222049721ac694cb50943f012c1e9d2131d65bf9368c4184a2f33e02c60d157d155c7a9e7d1a9de18c853a7d4cdc8f48f75232a6cd18a6f77ca9b1ef68f4429b613e2ea8168a20cbc06e039269c338a592e0fd1a0401d9524a7a64504ccb12cb693483764689c89e3a5b00ba32a7f40bc41d3e483d1adb06ce98f6be3258afeda74f7b166bcda9f97538d5ba656d1f154f89088a5e5de74bd0a375223a487c7adbbf1f7493b2ae27033019122375b5e1d01236c352e82031d7504d33b99ff19c4941298d92314f21ede3a43cf4e8bf947d0b4bf21638b996bb36b5a6b4f74a283aa9b1767ff531932dd0ebb7fed0562396e5581f20410297997b6e61a6b369c1d6955d937f5a44d8b280b068355a4df229819b739c2a793dfbe74e159d61b44124cb44365c8dcef37f0614090a3283c5f115b3a2750b194a868ce78c8dac53c16cc7823c4ffda7dd883ec15b84db907bb1d6b73b6850870b31a20b368d05bca606e7bc2c66e6c8b46faaa37e70fe98de24950e19bd6bdb4bf3ee1aed822d62b90d36683403ca95339c3538908bb82d706dfa6b33bee0391a9f3846dd57df39c5c87c15e9e3dbc87df31420bf0228ee3f3033db8d4023131e6495582ba8407f45a3c0d42aa5d19ebda2e110234b55134953bf20ad951c6e334be86ba72f504275b4d1391895ae357bd74cfffda96286cda4ef002a962183e3d3f860281a9b4c341f6cdb9eeb343439bb7c4d6f4cf0675d29c636e1d7853103dd2503bbedc586e373b6e5137a272cb341bd4f5c501d5a8f6739101a32587a4063020092109947228e1548232855f866e9f0ddd1e61e295ef2e55df753c33dacda56d0702f50e3b6bc6fa15e6c2d6c3bdbf244049ee51abe33346de630a53e1a05a8a76deefc97f95b4f9b4df377399a4439be42639562fdbe6474bd3f7a1a257dc6ef8fb9e7845fde71aa1bfee328c8deac69dfe8be18c6e78d5c900824ae93af95fe1654f775a87050d9716678d811e535593d7db17ac0b220a87a6d09d6397749c19091a341591007200bf2af4dca5b869dec8fd83c0bd8d26b68b238cc479cb6949cc2e0f04378db0ec763ed3d7cc07086afd25418ba8f0a861b198b575f53a46406607537b0579fa557bf17439932515fac444519c7b4770ad31e5b53a625c2cfab032538ac14e5e1f052e0e3e6448eb70a665fd66b7d8fffe04d314d034057e1966bf97c89db656513c3a8c35ad4d126f0322c6d919ae63d6579f62bc5cf1ea1455c4c6b014d32b099c7a975327a495ced53a6127fd6e049c6a1742e270fa7d78010b7b3c25977c998742528dd34b46009f75e3f6fab154fed8dafca05d74fbc9443584a156313e8cdf9391b1371383a21db22ef20eb6a7c3365a9b715b8fbd50390338ac2ad69bb02ab50c987583722b03f019831d9effb06a910655be2cacfcea83232d2bed421e3fca5aa99df350212000e46175d7440a872134c9c564a73bec0657d96bfa5e4b75b0afef5ddfc380a066d72f78155888d88fe6e1f16f408faa51996ae98364e0bc383cf6ff2521c6c77c283decd1ca6f3470c379241f6d8f24ec83b98ed8c0a0ebf3f3f6c8407a51defca174c5039e2cb8af3a31c3e5f8e163ba35536520a51c68d20640144464b3920b2002423bfffe7e3928414b2e8166b598147dd9b7881ff33c8e2c91f5e9696c870ef2ec2943097dec3b2300fe27259ee7d346816879c5a266a48478bfcb27dda4d65114ad205c4f92120f8e71dc71393a629df3674c733e8d8329f2a0261ac3687a75048059e2e8bab4b509dd39935c40200eb0cc7aa4f63e6773a074bbb85e010640146291c63e9b000630d48a9898eeb1093a1aa7d52846c7c0728b45b56e511b8dbe3f3eed2d1b29a3f8e709973d18c4abf3b0bbe590c70aba0c516a19fe3da9456fdd20c7d7cac258bde6e2d69f6d56c8befffa48f5187a521eec823b34865dd9530069d3a211c1cd56a905d105eabafaeeb0e536a201002491bb2054a7bc5f5d5ba92f8646d858bdafbbabce8c80a0ff04a0302bb301db914e4c3c6086700c891081d8a4546658c00b478f81772678ee370ec048b3eb20bf8cab7e25f3a11140b0f7a513bb89e2db36a0b0ddefb06801036a8687d8325869f9f3a1de40a640e106bf546df426722240defb1ef9a1f3a1a81e8c063f01b10c09edd405705024ce11b00f75264198f5bd6521c68ae6d32c1e3003417ab947e334507a35a12cef9e9003c84ebf7b9ea80270bb75679303ea1fc3f640283781bb2bd8ddedd466f791b101446f7a26fa5aa5b41a5e8ad94b0e2aa829301f94898df6f02d00b1a0cfa779e9530ff8dcb1eeaa48dff708de59c2f673c8710b33fffde679bbe8c25e13a963170128edd7562c6d5992977efe8b6e3fdaf721ac2e31e483d57b0468a6c0f32acb620c0f05f860d0105b08991982daa095961083d6ee2d0d8341ddaacb01511214d0513248c0a61fbbc5febc7055cae10064634ea20dc15b1aaf7ff169bf911648934e605ead6fe96e0f45b37cdfd4c8ede7757c3708b38c09deed513798e69316c61b24e41bdc08f734552f33d71175e96a964f90ba07321dc6934061476ae6b5ecc344ed0b629bd93ac7f24f1cf203acbbe59ce9a357e8a171913cbb34cfb01c0f0e7aba1f429854de1e46cf9a00f38ae14741372cbb6317035432e4471043ce6cfc083db11c97945629cfbec2cb9a84b64197526b141c2f57a776325590d1c75d228693cf226dcc5c6dec4161c7035b114c589694612fa15dfe670b2c7029eed947e22bb20752f10ed1d4fc6d54b9ee568adccbaabe58d6c4ba34a0a0f160aa5edd322a3c451b9ad9698feb7b8235ea953241c2f49c0e638e076724ee58d7d2723b347d9c85b941ef2c0114e7364d7c6269e4303d4c9714099b612515069957140fd3456c96888981998cf1ffaa2164e38da326821593cb13a689ddc71ce1bcea26683c949d38fc47deb548c34371c529443b0fd45889bb1e10524e8e65e58fad88e630ad04a92cba11b700dc5fe71acd875a5c18da0b0e7ffb7de7dd7ce93ab696166c57328c933a039abc7b5b37bfd7690d8bf841e578b2252b8046ba54034d725dc2c5145c2a547dbf6a791dfe367e553f06ad19d9318e7b6c14467b816def1e459e6d0bae391823097afe08fb786635cd64f2bc41f0301fce6e8a537247ee3ed73069c34e4cf50223ddd8e7cc0e8d105a0eff8ac1b8d42a7b47035785ddb26d9cd558298123cbd8500d79fce6122f35d4b9ebcafd21ed1be7cd43b7284ef361bcbc51f738356c0418d2f690a314185a11fafe20249599e9e2771034418146e26a425a1bcf9da8fb478fd2f3994ad4aca7e9f08a459960f333eeebd64d6f6f43f0c676eb4b1bd68562cc2061c14360d2cad6c341538267b63258df2eb293f2a509c91625735046e5c11eb822ff2dc67d90ad2bd07c8ef18f5297636f8c17b9fd6393214210207aab183df350555c2c63064c0399253708aa637001fe8316be0cd91a7db685e33fc973eba23af446d24f472b24e5a905a70dba6ac731a0acc5db067b322bb7874a3df1472bb3318bdfd44d62b31f09b1053f3ef0cc0dbd505a321f07d636def6bb59011c5800fab2ac069c40b6dc5940ec8e21f0fdc8fac759ca8fe22586e6d4f975592736d83a02c110837f82ac10109cba4c5a46c397627cdf7605fa93a56ec1ba6c840324f8d9a8859a000d92750160ffb3802955f2f38e7774005d9961b8b6d52ef80ec925910826d165a2437034b38268c965b05c1e6daf879fe4bfa93cd9f0c05a8c18c00812133b52c7613fc599ec7fdc210c0f2957f19b4d6138a2496e852ff25aa460314bb81bcc473187e47527aaea1b41be94e345205a81e444381007f5839446c32d0d80e631e7a787156891b8bc0a6497c897679c1b56ee40616fe08a942f3244a6e1ce09c27b8768df57715d9f0fd02b4076c4d8d5502b674aeceea43786e03bd2980e09090292c1f256e0f53dfb42471c93ca5705695683bdcb57ce65f2548214e854203bbd17f67056af2b8ac78d0768793c53a541869baa30e94c1261bc7ff292a8763cc1ddd385f74505b4bf16a3748540a7dfb1f99545855b49bc926b8b6df56b16ebac9b1c70a8ad26f26b81673059d73e781ba4df93eb211706d7e158b384c5570b8f942ea030c465214eaf3a8748e246f59a80a2c6fdaf63249cdfa3aba944584aa0a42c2d40c67b7e52c82f52a695da4ed534aad6695e4115b4e80cc00ace798178e0591c9c41db18dca67c6e35288626cff21f357228010fdc35a94f7ee6cb822f510d85d948fc006983ae34467f200fc1cc2fcb19142d41086335460440e9a35041cac6bf09d53baed841c5f69323f35ec054820633f1fdda41fa802db21a5e0d2b1ba1180b9f555ce06aae1d3a2a756ac2168d647387d35e211c372f50543e6aadcae49ec3dad59ce979f5f39b35e689b7a13a83eb8b2c6a9e08afc1bd06d434f4cbfc26f3af220bf2bdcc9b784f7d8ad994d5e0a771811e41613133de66cf96646243b25f40a9026cc6937d59a9105261c033c948a3f94712659de4a1544089ca9f900c6dcc6c5ddd38e5828339b0483a5a60fb46e7e09c6a90ef3a133d45fb5d82dba8773c2117a030d4bca1554d05da9b8f5e1671d809e6977c72586ccf000c7108f7f55eae8c5b3b626faea6534037e7ab956b47920f458d3e1256c48abe7899f7136d0901c01aff0331ad1c7027591ee7b1b441f2c4e148f62f836bb9a092b0327954eafce0900275772d011fca4105c2b84a3c8321d2fdd9d8bf21c2c465f1dac959a9f8d2f942670b2deeb555ec83eb3a74f1ccff3b76f09961bc03216b32a4bb3ac8caaddadd3f13d4d556c528d532a4e6c04cce386f3f7c019defe813ae3697557bac6451fae93db102a4b3093d141d07f17747464c0cd959782f31142293e72a84fda47f89a6e3ff14c81c8447489ab29f252b12617439ab466337e81ed3f4b04ebf0b5a11aa6ce4cced076f781e1cc4c263b5496160174beb2e05ae8e2c3e9e28428902221dbe2d174fe9f65367b6b6d78f9a72e4932c60b23481d29d6554902a9c5d9b606ddd71c4a5fdcce8591a12037a51a2c5834184c5c0d782ae459764b6ca755b472af8dfffde7e2559febb9601b5ef951ab0fb237794aac28f16403d6a3e469402f3e07c9938997354f5b55860ffdeef9a438cca205fae3aea09e6d733dbbb6c813b5b058abb56db6768ec135176aa6356003503153d17a15fc3a96e8cc54831a04b3ff3ee0d63fedbaab25727a44fae79ead5122a00275ff50b74f65a0b2af6d9062eb7f18955c4a7256360f4bcbb78c6d7375ef8bd1682d33958a1a098e6d54e40786132e5950015261c209a2def1cc7683e9608b4932b360c4e44354c0fb804f2222958f7db3a779c9109811afc3947dc28716b881edd9fb06424e85ed714c2ae539107255049509a9f2aa88f837af4205e920ea3fbc2301e3f1d3b20ab072ec25b2afd108092104c93dcf828f1b0c155f8501ab78bc2f0432d3b78e632b30971e58b291199402594477e792878f4254c797bfc92585b3098428e959a0d0b83e5569f77c998e7296b82a1b874d3be12327c91654b1ac86c2408a524058f09b90f472dbb0efab1754a5fda91abedb66a85a35d3654a1a9ae9bc14e4385daee5723f487fa2006069fd9b1bd9c954569a0943087d5c021fc4ef3a372a3f7df34d399a5dfddaa62a6554520433721fe47e14f05226da0ad55a8c05c076f9d1a28b4335151cbd7e44bd8611ea48aedfc33861b25efe46c28074caace7864c8018658643919a1abcf12a135c90f0a0486c50411b212a79303a1510e76cf10575e1599a1ba5d1ce29cfcba68fa106990a815f2ee5d3a634c743a1eb494f9bca641a8928f01d6f9e1f73396acad2cc19668ac7e1c8e00af6b28db21362e597d8cafacf7e8444ce86e7ca8666afc8a7e7ecbc22b311c239108c4c9348dbf96870a6224c51b8beecfa5b2f894badbf8329fba9ca8a6f67f53c70ee62e63c8b8403dc32058f4f8177821d9155de350806ed2efebb4a4f002121ae70be5309dba016a59beb1b7cb5f22f082df5283428337178bebf8d4c1708d45efe82acead7b4410a821dca93d99c2d58e18c39af3418c58fdce8333255e142178d2a0c9b48ed86392952ef348be6edae7dd2a3de999cd7f4e059eb068c8ac26a6b96b537fc6ddd82a4f621a154cb5dac85a5e5b8737a1d9ccddcc021cb978261f41e99fa886d7082a152a3fec008c1c568b5e6f1167379aa493a4eedd95f237dd4655412fbdcf97e272c06776c1e0a6dcc416bd39ab7fe04c49cd6544ed39a119b003b23100249aacb40e81e8ca11bb221a3020b01be13e7dfb71b040a7211c2a33ea036ad3ba1022d5dd2271f8780a564072141b13e602892e24b798160d462e6246019c096894eb3343c5985620952794aea9147d4ae302e93238a0161e8976c1b4726f1f7410972d0fb9958851881e9462d4342ddddbf06b8032dd6da9db51fd7caf37046fa27ce1ce3ee2b675b34fc5452fb843711bd38b64c043a0158e48311d8a1dc1cfd2443108faec1c6fc1518596765952457d61e17ef51e7b7442e54b4be66affbdf8721c32cc17ed38f794709210df2789b403ad91a6236382237b4ec126fadab4442eda70413baff178abac6a37b02bf88e3ab76623a027866eede827a20b7a860f843e48960fab2ffb17e77cf3f3820644c98786999cbac367693b3e0ee50894f3df5f872d19f50c96ba4c21f165aed6987875e66b35eb6f6f1b964aea139edf0c3a3124b90978dd6a37460c20d5d2b48a7c48635f8d80a1a012fb71a7fdb527456fb9c2a5dd7dc56c0cd15ceaefd74f402ca8cdd451ac7eedbc70cb5ee3c3612c5f231e3e5aeb1d73d50f921941bdb12679ab2f5d5c37dc9d4b441bb733b7be5d08623934df1ed0519e2de97f4d842aff68445edac99e825c77f1494576347b5271b0b449b50bf4be2562eb20d1711d1d4c4e180f99c3693de6d6bd30746de09860e27bf300fabdca836594270494771da9bdcbe8694d1eeb136b5e488f8cfeac2f272475e530fa43744ff43b295ca95aa05fb52c1612c0bde325785cd4948cfcc3947aee6b36ce8855f1ca34a1b0db8e4d16f409c1b5db58e0b50bb7cac1411abfbc90ed5e958800b2ea2c02f7fa382020afc4cd1292c40c59040796c13602333761314504f37e96c9277fafe2fde60b14aee76260b08002e4305d07ab0559d04412319206b802d5ccd20395c40c2f1e6f9b4b2919610633ecba8a3d0d69f0586e153494fc48828fec3dcec0fea770010fd638b249d0ada780c46288910bade0bf9a3ddf8d216e09b208cef15ecede08181e1e78058846a64adbf75d1b49791ce3f6b04ca5093cc69bcce8fbe3104d5a868a47f466a76368faa1941dffb875dfbd4b204aa49a2ece945b64cd4b859cc36a5d99c7aaedba10a6da992699691af8318421b0ba8271110b0835ccc84e54dcb4f3c94145c0848ce7d60108257133f1a8516f03d4cf59e4f6eb3172240cf072dbdc5bba7c2d1932116fa628cce0ad0c0a62f4e133c445a4012a905dccf23a0e744d4ab986e60ab272de57439c42759afdcc92ee80f128614df0d927f114d9250df34eb4bc385aabb4128be5a3e4cd591a7368b3bd471d4eeeddd0d35bf2358bde422f7638364e5ca314529e56590660863b7668479e649f03dde49e8f1b7b386ae081a274f8fd68f1d642ba45f38f38a8a3935b05c4d7357aafd13ec68d0aa1fdf494bd089688ac9af3bfef2e573179ba511162169f7badcf55a49b8b7dd200ab3f89a276fb95c5ffbc36ee755312187788046c6c9fde95850b14db205c01d128c1a99d69d4c30751eb5eae4864ab59552274c4463a3efa9a12fb7e73515e640a1620f44fea1a64f632a4e1a1c584f7461108fa98b529ba4a9cf5f3c812f8fded36dc6166ecf3226069a6b512e7bd9e08eeb2ee722f0099a23173117b66cc4596580c8139c852601d51fb281562c05f29ff9ce8c05e3ccef0d4418520761a5836b79a9749c59557e704954eeb0282ad9c1130dc2c4dea071354a6deaaf4b4ce54820eb2e76ce44393a398d2ddec646aa12cda8749efa5a40959cdde2eb66c07e0c608082e1b10e7b4b326213cf1bafca22a9111c0bc6d3a508464fa12b7da7cdcd545915be3899d0293b9007342467ccea04dd378e12c8844ebd8b695d95a94993f5b46b785160cf9b4e4c096594a5e3b8e873d0cfd39bc6c0450f781a5311e69eb147bbd12c856c8ade0d2851eb29f0f3ea10b6f7c040250bc2997b6d145611e0cc096b0dda413386e26878d33cb1e0354fb0346f029bcca415686944163d912bc256729fa0f2d748f6c27f383da3e6f0a30329bdefc9168981c8617670f23f0e5634813587aa82721cf1eb100937e79661597fafe6ef3a74910573a03594a0d7e9eb53ad474a98a48c488de1d70f224851688f49486ba2bf33aa14fe3f17f41392dbcdfa55cebdaa07967bc9062448b310c5a161e7aeda517b139be1b777941e5fe7a1e14be3e1fa18302f117e17ac5b13254681dd1862804d49f8670b3062e94c43728859f3f286f4e2e42e7965594ff4f6211d9fa21a0f138869a140e536fbf8d012d60e45c49ccd5cb7cf2e611aa0a670b4e0c4bbaa423305601414415455fe5a0b22b5ae9c3c14f9039c7809dc66a28bc5f26461eaa4796c1cf79ea7ed0e94e7e52a642db77734ef34984f894092ef7c5c2c598404743d962858b8beb3bfa289e56e6107c165a853f49f7634c1995fcd69c2395d6aa6fc4c57d106c5fb9b0d12eba4a22c09bc9b8237a07a5a0cd951947fdac37a4cc6e9a835292a6d489ae91a6ed0756dc2f8db297f144c97591cc8d70da84878dc4bc05794368c6d6c29b2055d5b54cbf7dd28a22942e9480110866664fb30343ceaba86357c26449f12ef38fb18620170003ac7e1809386f296d8b0482cad98c98aa56e98978b32ef4f5e7001bc304675594861a1183c71637a4f12efadd85267ab192197510bb86d49211e9475fb1b85edbf2ca886196f274edf29e9f1ce1a5df5e237905aeaa49e96810a8abf2ba5ae90cec73d8b82dfca35a7bfafedb2c736752390278318c26ef19ee870f7c26c704ec30251668e448496e53218416a22df41a2d5f7114a3b39f4ee4cdfaf8896f96cfcb396971fb20a0a5c5c0e57c3a8386f69b186394c4e5feb4447303293d09fa493be3c3f14802557869de543a7882f9a140c00aa63702edc20290d44a61281e5fbe2afaf4f646b51d79122d81ca2549599fb0b812f5ed5f297e9245b8b7883d24161a5c74e9f3bd04043d1b72d652a75335d191acbb2635ba4a521f8195f15f7ea7c80f73214bd45767129ddbcf0acffeca859f1e405848a0ca4995e9424a717323f1118e091e39d7efd1861238e9474e60fd292e3b77fe58b655f76ba12507de5c2132cafdc038677d8b15d8d97ccfd4ab07c0cb074c2ea195143b173ae002bba737ab11d9f1c597fb0f839ab66d5ab838c2d53d2ad89bf06bcbc15fea8a71c2cddc4a186e752b75a3674e388a564bde04f0816aa37d091957ac627117268b1b5870fc0b30ca90a648ed7f42cf8e8acca112c55cdd6e640c61f4e5141ea793e5d0fa0d0f6eaab5514c43c5080b2be12c1ed7b98f7a8536d8d812a97b3b53cf45349aca811bf7cc7db283e3d3cec71a42372cc3d6c223f56df84d60bccc8159164ae134c447a348f3a73ba95a2a960613e3284ec3dc8ac0408f3fbb906534339e913bd43626f10a0065605d2f667ef8f70a44fb3bcf590f0d08c3bd3b72178077b0502ddb0a06ddad3c3331eb59cbd7318ba6032b7d3fe87a60c53580e46490f6b3c971db0613922a03776fb39c2590f14fe988f4b58f8ac5711b574749095968ac24a9da6c3abb1ce8f3aada2493b3f3244f44ce9f4f94b9e1a104601056052aa028f1f3f80f0d096b74921ed8f4903bdf9bcd9dbc4ac555397939a9249f731544d9ce9647c42a540528095b0a16146e3f86c66e9a1f45f71e56402d811b610b116c16ad23631890eaae2bae501dbe5e3a9759caeb29b0201d5164053b7ec5f1d325efb9a6fffa9033e3c93e137d5a0a07f22b4d537051024beabc820c656ac45054fd0603541171459a776981881cd4bb65e725eca4bb6b2ac1134375c05c3423609b82c552cb4c3bee7109c7bb7deba1cba16fa0013a83206ab53407f13019be2224ad19486cc1f4f44da4ee7ea266be4d2f660f22e7ae2884eaf530c9e774562de72a796ae180b6bd0b585731b118a9320868139d2d119a5129b27bd0ac187f347079941c721846ec7a3e81f6a8e6828fb7b38d5aac2cb7c11cf263b035588f630bf4c0fe3a533176e7749050c2a63ccc62dda3810cbdbf059460f935640aa46fd53dc748ad0d8fadc81ac280d3aaa8f59c3c8a993fa18595e4e8929b29cb802b3c28fd3ce822a901e5917c1e391820105fcc315f341fc40766e4fe3a6de6340339f65f61ab219c638ccd38f3eb3d65c7013e8d08fed518a78cbb737996cadeb5ef288b2c257575d5676fdb010404ed1615672f32c705d6846224229f282c77e8a1545b0ecf9d5a8915698a332df5c50b4093068a4a34325bf66bd58c0919c88b61687299289872216201cf4702b40f6871877c13ce0bb8c909a6781bfdabd56f1604a4bf569ec6d4637942d046e66c0d3aaf0a89630ba00404f7225c121f54bf760205685d7864363cb8ea034891dc7c16ecf12acaf0ad9f81d9665b9023c68f4c400d3b7e4ab73b4fdbeab2618ea3b96eaa03bc56590ed868d784a88809a6aa18ec76b6f2170b8db4c0c5b09b1f22239ed0e8c035403fa0c89a20d4ef46c9e56b96eea83080ec9cb8853ac965d19ad170f8c37d78409041fe6a484e12278a6a54632caae72121d3b7a248ce52d684021c74ac44940c0deac1272dd50a805ce61730f670762380c078cff1f0fa0735306c8b50d9ebf8f65f5584d3e7096000f0fa4738ebc710bd4fe313f9134447bbf34ec052767cd9d62cf4d6475e74eacf59d616548284f7309b934c6457600354cb12e0b2779e527a38f22f503d920301d5a4ad78ca881a8bc8c3ad3c3d5325e80df0d5bec9e8d6c2413f64ba77b2e77cbf73ff90396a47d9099c988bbf5077fac8d783b74b768dbc8ef76e5b53e90123e351982b7065fb37263d0cebbf16a11b6fc1b87a05f87aa1ad8c3d39dcb26da8c8852ecd5983904eb164885b73fb9447191f71353424f51eb0ec8d67d98d303c8c7f5e26708c96c9cc19bfe8a4305e8bfb5464737331154b18858b9fad5fd350f816afb1103bc3a3b38e43374f679b8a20676335cff8fc4fb19dccf8fa479376d60b21d4b0511239223ff397f6f9cdc90244f758131c80197d72de5f516adef6ce36ba74ba5fa71a4abb4d3fffea74b6ca83fb0a36e6096faf053c6e7e6c290aa1baa271062b8afe6c0997fad6c38dba1bb87eb80d20ba7e39f383acfc0aaa74b404908244eced4ef9fd90a2d9553fe9d9e3625c13c1cd41e7bb7c05d400357e891438ab305fbf63ee36b0f777b29d04d875d5b434362ad224649cf17c3ce0ab3e452e01dbe6449d930e00d4006cbfddc767bac3c111625af4d400fe80c40968bfba1052e03d1e1be15caf58453a97e677a47d9884a228940961d53b81dc77030a396249618f5afac1fec111e2dd138522e184117d59bb1ed93e198c9017ec4c29ec3b4c5bf096e1cc2877d023a1538cf9746e9e42917ffc4e89a1306697d4d998dc2e4d53adfd466941920e6ee016755044eb897075c8aa18f24f5b4b7d080be06a6242086e79d17548f0056994192ce0c650e08ed63c9ac414a8c5fac611bd5039fc6e259746f1ae8c15034a3e3b97092d31d229d47141b2a1d5ae28109366a50f2e99ab5c6e995ce7091e46f73c11a15276670b3141d6f853657bd1f2ba5a90821d2796f26e52289097401ba9fd0b576c951ebe046150b7ca800bbd7801e2ec08a7f1b8a924e14901c53c83e17605747cd2fd55e407d4573da0583bda9bb7d436abcb8850605b68806817fda40709774b198279055301e5d98878b054b2417a26141b949128f380343ba4d1daa4e8365cffbd67e16b31c388de36cb950084c62f0108dd065132bfe6c5d6a5f4e908a0c1c5fadaec151b26215b503415e5e26672606603cca3026032a5667e76d3242a34f6cad004bd529deb288a639ce687b296a957e5457244cc1fec03e28f9802401ff4583ba8db6c77558a3613c9af058aa96aaa567656a7d04285b8940165581da79db9c8c0899189a7ba1a5c3223c73508463f48f804b49a7b37d876f0f2dce6e4433dbc5d4a161ece82e81c50877862a6cf555032884a4a18699c8c5687b6bee12202c351afc8f5fe9ae9717bb0867d6977691f01f74a5e02b1fb3af949d9b583caaffc6adabc0a558297b2636bf291644a1d048d02064004042afc0f7559b82c5da69acdda464496d86902982f93e46cb8ce838d76904b5aa163391031a099aa115fb08efc34df527b8e098fb412847fc21db350c8385453b6271bb7780aaffef2ac8cbacbd50a0bfab08122a4c09e24c0ce72e84f1707f8a73291807acabe94d5cf4364be9cdedc05fd858f6e7c0acdcbd9e302a0c3fed2a3732d6f1214abff34ec662614850f4f62a2bcc8425bbdcd630abe27d6d28ee733b67a980ccd683888bef1d910487a846c0f3f5eddcc2cbe9e926b0f6d8f52bc9f0e8d65ddc61517759970f28a2c7bbfb9ccf564275f83d66484924b695065270b12f00fd4d74a7bd3b0e6ca0240d73033816fda09f8687996f19215019982d4e202eee2645917eccc1d741f75e62bed956e1f6e65db8efc7222d0f248e2d064610076f6067ff07d8e6ea6cc450878a6032804b25445b99c9b6add9ace5ae2ee59785c45ca9020b1889bc6bd06a349e0a282452675189181718ed3e511d76fb9b11177b7ca1be2757de44e9ccc71f7b7bce1a66e16fe65776de32e03ec4f8d0a0e83cba611ce8bc5091e112157c1327c302c3617282586a82d709fcb846c6dd56fdb1066f8c4694454038dd04d3215966c5bc7d53482470aa868bf4999ec623586b38b7383c814b5d0eb06046851943b27ff80b03760c11646c4cbc259aeb997e403cc0d3c736ffedb58432d408e6d3e4adf158972be0dc8d374ad80f9d0677430886a1ee8001e0b7f0b380d713a6880f481c6b413fb254ad37c5be7701d8cc3d2623c1d0d892e962db36d4104790795b2ef7cef04de1700b63ae3c97462b5e11f504433ca8da5a81e4287caf14848b53cac98b5cd0249a1936441cd517ea62afe9dfbed7668527d779b1d2f42c08cf7b134b45e99d073d2b9f35f140ce1323c58af15d32588508cd81a535c52583fde722b78459e943e011c963ca5cfa56fd13826a8cccf31b7c6982bda74730a1d751002315593ed9e4c57333d4e8add10ba56f33d4ab55e2d4cdfb3583d8d6f028f46ab9140927e71aaa61fe66a8ed14420e45c911e832e60ada91488d35d1551bcc94d93d244ca1154bff0704cb1fa2eee43dd844c7f153ec8a64d1758d2583d27f187b3d6fbe0063a2342a9dc1a8105cd7696825ae251e88141c066df100cd25ab7b49398f0fc867c8d31badc960187599a439cfb5d5649cae72371ee1826c0fd3b23183bc728637c31783d8148b8103a4382d3589f091be8427a39088015f7920051d0c944004ce3106c8a6c181297409049ae16d793748dfd771438e8c7dbee63961d1f2e648973ad3d9e66109d8546dbe4228ae2b147bbed2bf2720403317992bd2f336ac75451e4f300ee27340e3b9c7e05fb33d9edade87f923fa1c449c1ee15b144384c77bdf2cb82f7c454650e2771ad4ddf5893fce4cf7f6ebee01b973d8aa4bf06500a646c97caa6ed17299f8652fef80b1ac762335433703a7dd4d158f145f4211f3056b6bded9cf937d6a2132dbb23b6b01975ed9a869ec6b0398a83df80051c5f443f3d7a6dc99748cfcc4eb8a07fc6d2262fb2cc84831ce312e8a51be9f171f05423a678319b05d85bd8bb5723b248e76a565720f3891f87334f9b98d866512377a0c4814d9f3e3e476170b9519bf86c688e12da032a5f1f706f5fd0b7a20448679727c8073d9884c83bbbd88ca7c5a4d52c6c772528c728208ad62adbf229813c5d4cdde92aefbff8baf0deb064e2b8dd599aca974d678655656019a6420866c49c0ddb527b43eac98f3bb509a0d010660fc4a591fd10fe129127da4b9d8036b186c9b9739f6836e3e64048ccc5dbfe7af766ef6b65ecce2d6ae9f5e076dff9a6157bd9fecb52bee42eaf5a51ed0b4b227537ab0db9b708e22237c418e0335c5001e5568259cfc110aae1b19acff55ae9379ca6d1c84fee79c0a261e5e031d29a137c3d8c06d868404f54fc0c160d540af90f75fca0f8951eacd1acff19e9b134fd2979a3c0dbbf307607147cbbfb71e1ee6914afa3d4f1212b6efd1c06a642fa26f72d1a9174fd848b0f21114d0c1ac030d8944524f5fe7e60524e1361d9f0ed3b169c217315eaf937908708bcdae190bf4205cf2e8bd9bc7e41974a68455746027fc550421417c50bea3a418115cb1aa5e8eee070f36ec735ee2213aa712f94a365c15d6e14c6308e0a3da310cd9c68bd8ab0557f06acbc0d192b1365a1c0570910624b54ef59395af53b4a51b4d454654a3ebbbd6acc6de4b9c1772ab93ea38f06f27b51af15c14e232bb509c38a40e9ba6886aaeeff1ebef2f86e4f37498a2784365fe5a4300b4860094520cd2db5a0699e64546064930da29262eab05e411ba8d018b23c22093dd210e6bd0e9ab5e8c349ed0149df6f127538ae06334a70cdabcbbe27458a1cf665c6e242459086da73925062236f6a8087df022e08d65b327ec83f6a210eb7ffa71bec4fabc040f0d28764a2d07afa10e1b02bfa419e247013605ba4bf4a29078806daabd024bd32bef35d22e8236d0243d75be5845d1283a1187653c238adeeabeb7ec7a9b17e58d49f6c878400354d4b407a83a841eeab1a29bbab25345c71cc62597505639a62177fbd63250d14e4e2a2132a44494a9bb5d05044a6d03e52a5e7d567625850a037e2700c4f3ae13d31e92d67ebd9004ad782038929b3cffff2082bb58cdc161bc79e6988479a8e73fce48a2321e577c18f54e5abe012fe28c9512ce3515c310e547b8e259791d74d3292727e7cd2373873e86d2696e8a214e753e11eb64fc9f6e8fc0d5362a593ab5058d080e07f2d393a42c9aa648e097bf1644ff789d7e0b8fb4ef79ca6f237f7cf693b503edd15bf1d6ac5e85c2e0ef644c1cab0cc341f300b38eb4810fc6e48dd58d23e3b255b3c3cdb3703ddebe19aae5796ea03a24135b96497b38679795a8e5614ee552a781de29b77cb65d7348e048913d5b9d92052baf05d6e26bba4e2674ee1721b83be5baf63d59b0e84ef4602ea8214d1822009f729527c22d402c80bbb9120cdeba216fd9c1390f06bee746c7abefdd74a6a564cc6bbb261bc7961ada4d1cdf7cb45cd6955c1f9c522a7018e6a651fc193955d451a49eef794289bb8ca54a26642ebf16488092a54999a1e866997628d7ae5d79f0222d7bbf727b5acb4db125c074d0138adebb9fcc1e53518a3de989f42801ac5ce29bf88b9d50e6a0280d02dd753168afd6589a49cf5a468dac63cb83e53a27469684b2dbc3c189af3591f9e4db5144318d9822dbeabf5f5d00b3cc42aa7509151b8ee323ea288ff54a2be6b58ba97234b810d36d90d1d2286ffc79bba8bf605153bb3c15768e8a3b0d77e5eb90770ee046890051f23c4f33e0b43fa2e72455c3777bd950151ead8e4017f81f9532a0b8d8c1059431c8813cae7c284b170f214b070b30b2f423cd884ec197e309e5f0ed352c781a323df0a6de68c8c71490e9323b02b318ac9ba61915204ef4f0239f1dec68730a7752d0203b28b3d03a6575f574b1ed3fb0cda517aebb57348a71601888f9280b5ac6dd5dcf0c2a0494a94aede69f51a5166e6d3db2a70c6e218f6be9c54ca6bfa4b4b3faf329bb8f718f6b2792cc1aae0fe3748dea4973fec949b2d15e5de15c8c5fd939cb8eacbe91d853514e9654f4e7f5c053eb11bc2586adc376608ee0d539e3991f1192be7cd09811a78a514d18284aa7737eb4c595fff08bf70e2e19b0d8d631d20c442eb5db81c5bdc034f55cf4fbd43436d2c8e45f2df201a6c1e6ada8efa5530dafdef48a223fa51171a58fdbff5dc4553fc28ff490b37601ddba33d05f8fc9a6b335824a363c6a93ec3f867231301af935d1c43842e2bccc19326900dc84a4434e80f82a4d2a0c804b98b9d90d8e2da34c8383228c67c7e6a95b6e075419325650dcb48cbe5d322d4923cbfcc6e949c9057916d3adad76b406116f1294700548a225506084185816eea6cf656cac7e8b6a411763dc50d1417a384e98f795f6cd3edc7c65ac2421cf232aa43dd5c28f9d1d76d2726eb71a5c3a5d73c3bde557a70704c45ff910cc6703ea71f67b5b3054324e079584438e31f5c54c9b8ddefccbfcf6b4ef4e30cbf35a89ce355b6284eb464cbd76b181ef3fc69defb10d3418da718ef54148c429278063f2c0d27900f614f43002e7ee8877612a3cd66aff0b6a7befbda59432a524658d079807200829071283bdd447ff0ec6e60face7bf20119f28e9dfc13066761d8042fc6486eb0a37f3a0c2c1af488d0ef9b8a952dbead69f566efd69c56c527fb2a08d77ebcf56697e71ebdce2d6a945498a5b27945b6713dbb74cad55b5039b8a39b1d6af34493ed6d74fc126f030e153fc5c404360803631a702ad18202a0a91f3e57c399f77914564b57d6153f5d6fa46d0af5fd32891f7957534971da1cb1e28ddb6176dad99e82d358aeb726ca3b62eeeb44cfd51105604f53246a9d02ee04cdda9cfbab5b266b8b41cea925b53a41b8f4099ca40d83455579b1b84019ca99f2aed68990ab78850200af2409b1c3dbcaab1d65a2b8e675ac1b4c314b30367eabfccc0a6224f4dad70ebd79a82af28f56b4d451844a0aeb3cefa77d68f30d800b4893c70a67efd201cf0bc2abbf8b54ce524ec55561777b62a36e5ddfaf57d34ca08cd7ead0f43a3e4d7dfd128fffa1adda4e626d8f5a5a93e0d7c8ad5c4aa8b55d5d5341a3c006db8d6971a3665a67530da77305a674308e29d00f72da227817cee27207adb91c05ff422b097be4855d328ae83615107c31dbf10fba2e7ee07d7f29cd662b2da33635cbf457b510783bd7cd1731dccf630d8bb3ff7fcdb6336b0ead287807cd163a61ff2455de5bad8d332f59b8b2f4058ade38f1a56b1c099fa9989c76881ad81bdfa58b701f66aadf25788e153fcfa1e9ff8ebc398da75c0066d3988d2a59b589ae08552ca8fa66a921c4d7e35eacd5289c4b287c160b20cf35a86d297cfb2a7af781d1c5da4f0030a56a070040a3ff80f871528248183091c597000218527fe87ef01052aaaa4808214986882c2141c3da099724e89420f96c6853e3350626806477866a0021c26b02e4b7814581bc68f6d09028e30ac8645ab881f1c4e2c76a14f114e1881c98d2014d102318ae0e1055663020a4d8e60c4ce30a018e20731ecb605163cc031c412170e2fac76a1cf92289a2c79228514587aa1cf129e252d560f1595e2e4ac66d44862a1a10bffa1b7bbfb084208a194f2c55da4b4724a29a57429a5acee3b2d4f9d25f22a07e16aab99066bbbe36476c3205cd52aa22dd376b784dded2da58b9452c297bf5e7ae4a240524a29dd9d2b7dd8552ae594d12b9f2084104629e527691ce08aaed6ce882477f70d0849975eea6ef9524a29a5f476f7197cd4104228a5f476f717fcc5dd45da6738e37e8324638cb25ba8bb7bbbbb8b2a876dd956356dabd9d6188427ffeeae22da32adbb7bbbbbcb36fd8d31babbbb3b8422a59cc15fbabbbdbb63babbbb6180aeee96524a29a5bbbfe04c72e8ee5276777b77c774f428eb5f08a1494a192f8cf105f61cd21015a08573a35b4a986e5fd9a87d42b7cb5992b2c6a441bbdb5d6259367366e4b8272b0963f751b641b8aadddda5fe6ee952eef4bb9015825136085b5c24eaeecd575cb51b84da0b75b90734c2487506951342295b3564c94fe8dbdd2424babbbbf209420853b27561dc34936cb54ccb581d48960f20090da6a494524a299b6df409de5e82700559a34e1a544ab929f149dfea004b492925b6424e2561dc2da5949dbdbc94b2835fd98b524a693b25a594bd59ae45246ae1646f104aab834d09af2f3a98d95c01ba4618a9cea09b920b2184d294a2a9214b7e42cb4d09cd85b5660f3b0835134dcbc0cf4c9b948ec20d181b27a080430c91c66d2ff0257eebb44eb7cee865f472e4e747e7e747e74767f4327a39f2a363472fdca689523001c0ebac7dfbce27fba317dbc1231bc76d3f9a88db7e7e7e74ba75b24a658c8dc1992cc31ecb1eeba0a442a9549d6e1d2a54747e7e7e747e74ba75ba757e747e74462fa397233f3f3aa397d1cb911f9dd1cbe8e5c88fcee865f472e44767f4327a39f2a3d3add3add3add3ade3d8e865d6d1cbac47eafcc174462fa3971fcb2f38412bad9556e933d3b88d56d28834ba89ddcd8df4491ad12a7d9246485c4a9f5fa5d2bf1b5a699dd2bf4aa7f4ef86565abf1b5a69258da674d20849a553fa77431abdd0ca6d5a267dbab8b4bc7410ceb8b8d82e35e4b67c7cd2a8a573241bc76d9f9671dbe752fafc2a95fedddc6815a3b25239a3369d83927a51312f6e6ea8ac4ee59c53522faa7b71237dd22a7d7e2ea5cfaf52e9df0dadb47e373737b4d2faddd04aeb77237d9246b44a9fa4111297d2e757a9f4ef86565a49a3299d344252e994feddd04a2b6934a59346482a9dd2bf1b5a69258da674d20849a553fa77432bada4d1944e1a21a9744aff6e6e6e6e6ea89346a49123a1feddd04a2b6934a59346954ee9df8d0b47a0051a261d93ee8d49a7b38ab2164cbab671a216ebf23222cda051a374820d181c9b7dc10eca32d443710a7b0c240490a993d2ae76d9b0612326870d1f413bdfa7daf9543cad562b68272868e7537d9f2acaf701e001e03139bad2204ccecf87862213162b264769686808eeece4c891438500d4e895aa555fe7a860fbc5252828c8c767d3beefebe9f11d11f744a5d279a2d249c19403be9898633825ccdae6b3693e97bf5b445ccf268b7c65f4ec3cd9d979a2d251a974b20c0d05c09b310508c82365746a3232b133134bc1dfa4525c320218f9f4f40480eb41df8bd0264aef89d1fa4d22b73c8bb4b18567912a1b5c0fef7ce8090f8f8c4c15c0cc003a28688705950f0d499c1c66262cd60a333609efcc0ca045272767002c888ce0b0405364b5a221804bd5507066beec69942c4294993fc990579abb53f5996aa5c2a89496affa5f40d4c401cacc9fd87c412fa67cf129255b7756e193c6efb59baa965b7d21fe5b57bbfead1bc2438359721a55abadf46f35b9c42970663e363f9bf3aba9528eb029bd2d1e3aa30793410d95bb8428d9233f590589c3a9b584c3ad4fb7a0403408daf4a54aa056e8172948966c613d8da24b6813f9511e59c515eb555ef68a75b962b507e2b4e756dac3a71405a29811f99a2c385353944597401485427b288bf650166589615394b2f894922cdae2534a3e419950277ce2afa54b7be04c4dd19e281271222e52b9950251200a448134dac92009c41e907ca2bbfad53b29d432558e82b0f4e73b2685d87bc9d7c48c60403c7bb53ed0ad4035545a185e6b4c0e88f2d132f53fd58266a58d70ed5bbf7378b815875ab300ce47a3b80986726bfd6eadaf59e17259b12edac7f04988c6423c74eb0be1135761afbee6c278268133150e6954ab4dd4af58e58136dcad0f5f70a6fe0e765d179f2aac82a5f029fbca2d80bb5ac72e38539f5d4248006d34ad6e6d822de3eedd0444cf3dd79120d364fdaad5f8d23e058726a7398133f51f006df88a7e088fee60344deb7ef48b3a21a2ef17c2bd8bb8f99aa669a2a9bdbf48fbc6beb3e7e843c05ff49ae987bfa87bfde8e73a16e2177bf527bf3493cb8557c3b4860d8850882771225441d8abf50587cbd1ae5cf9377dcba6e477a9fc52295cfa548a04923d7c4ab9f4e1537f9488258e01675cf2638fd67c29401bbef383c0d028f5e00c1236055d3ef8440370e9d37a01195ee013fd1d30f009ab948b700f7086c6730500daf0a5f437db28f99471a4d08f46a12efad5f0095eb1dd3c536e3ffd5c544aa55fdda92cfad18f7e4c6c8a7e3534845cce5d9f410c64852c03493a3b13bcd68a9d9fb239342aaf2a5d039f6aa77d35dd13826c73add6a69ac70703bd126c8a73aaa906f6868634d618a697db452c70a67ffae4c1a5c53839fefd4156417a06f87ac23c56e2eb7bc557fce217bfaf5141e20787e217593c156854d734a1a3681fe9d2aa48581778f2a6409b29854ff4db7f850b9f7c515cdfe7f1e96b54e411a90cdd0bf00973b694b907f064b5cc0df2d28730fef5e1d5a8a9ea58fa5d1be05fbbd490ebab4ecdd5eaa6e68a9588e6aa51737e73f5cd15ed522508380f5137875aa6ff876c4abebc96524a29e512be2ee04c3f35bd5cc851a1b247f2aca03f2579be19421a632a1aad2b57e2eb15bfd8821ae5bfbdee9c4f9b501f30bac2a81414ca9d953641974c315c9ae88a16a138748543576ca2aa39c45e7cc1192454358766960d673332b3549f1a45fda12bbaa2aa39278d1691972017e7e16902ca4c8ec7e7a75c6ef7401b9756c29d6da5bfb8b3b5b8b3ade819abccd7ab4ddf5df36831b029fb8a559a49b7f834bf597ce22210e0ccfc8f53a04d0301ea8854882084f8722c54a20e1fc3934085e7d53cad66b137e74fff97a0c9c44865a581a04d5077941e6a2c5da55f0dd4418e83ecdad540fdb5ab5d0dd4400dd440edea8f3d6f577fedeacf4914e676d23ca7f840a906ba2c28a26b08dd900f4b2c2b2e7ffda08d076db8460246ca0e06029a265f48f65af7431382bdd6c5a114276936d5179529ec55a0fa65f1c68b552b7086bff6b0d34ff1b87f0f9fbce35a6dfda00c9b6a0f0f7b4f2a8ffcca539ff44cae87579edac3b2eb167bdcacea73b902551f3e71407887bd4bc4cb8d2f68239fbf9334115047ece28927bc9707672215e95dfd6a08d51d2490150e08dae4880f94021a38b7657715f65ae8c55ecfd8518b694508da6cb76316d08653a1f59aafae75cad7ed170d1c145050411bfe1e3853864dd5da1b8cbb2ded70c00d12ecbfed51025908ef9cb2bbbbbb4bb7a7742734629624ac1c9d26234a793572791999e857643efd28b502da5c016da450203ebdfc7c3a8506512af4359f7192b098b49ae4e8ecf08a524ae995128de7e5e82461f10a67d6dbe44a93193bd851c7aa38c42b1d172c63d48c0175c4213292c04f6a300c1ba2fc84320eaf28fdee9cdf952b34c610bad30817e92355d897f7e7934b7fed82362d777e5bd157b494069adf414d854f2d3fbf5f5d45d5a8193f9f8df014fdfc39e773113ee1d0f8f95a84d0a65b0a2833bbabf133b89f0433be067fcac24060c6d7f81f33baaff1ddfd20cdb9914c71a865e6d730f156c30479cc300931e2c78ca7d10999d1c154f6a29f4fe223d0862bd7c35ab1a387af8db8bb3b3e62027243bdda6326204f12b22f9dc721295c4c6ef2510c03cecc1f995c2e4d185047c4820431e0e89f8c4c3262893aa40e9085f9f30477feb4417f2fa6fee0cc74e99a87bdf9a9fe449fb9159b5df826d1632291c845afcdcd8aba96ecb5c83dc000377bd1d3e7deb91e5c96752f4222de90b458b142801f7c8a3fdf833639ee7c1a081170e7d7397f07974a3fa74f122a5d4491328589934864860ac8e0c11075a29158240b962a9ee7b56b051a8f3dafa58806635224d79d75562bee7cd904a2e47cd98236a23ba514774a2877ca265aee943aeccdc984bdd9627d7d2b24598d8a3ce6cb1635c91d99237362924824fac428a00d8c4cb2ce08367f0646c99fffc2d7cc9fef41ce3261150deb52a5295907e1ec2094a95daad63a23eb4e16c925b5d0f8351a48c3365e8cb03035e472d7027b4c2c8420b872056a8d721e2db4606384838d1fb11b55f2e0eecd03f4e07bf3a6e4f9a3a8a31208219438f3760721843142a98233ed2dc77cb011e1eede44407777778fedee2e65c38e3c02393202ec329e52c9daa8ea25d8c85cb8943add55a44e64ed00556179d7dd192b08964e1162c9baa453c6a6a2c7db8ba6f81f495608dbf26c8895a7f52a02cf0a9e973de1c952c023448b9192f5960987fcf832cac601a58a7d5a5afd24f6628c5b7d00dfee6e1b2c1c5704f4b22e5fb6df803147b029b9135119938c0546c6ea8e4c45fce1b24d2cf400072a1eddc1fce8c91bd76ab78881e208202892bc2ecc8f7ef903450c543cfa87f030c28dff81a823def81e8dcf08aa01c6162a1e90eb614337a109971444450a06818a1decc52a420e06164e8440c4144c9cd800c70c6ccb853e4e9864e144e7c6090a7068a1c4124a344cd06674d9dd5ca2feeeef3d4beeeedddd34ee2e2784dd10e607e5419faf12da0921010fd832f0575aec6e686fd0288cee60de708607bfc04d503531a89c70bbbbbb3b084b04b10229404438e1d162dc9642662db0a9f7dbee8e85cbe5e78680107eb2c0c245173808c208b7bf85ee6e958d5054921851aac0326484d7111dc60f537477d7e802bbed20f1a978c4f7a212ad515541c99520c2802a38429c800160f07089255f5092c1cd914b2afd853e3e18a1d961480e2768ad6c3005055830a90287e0174fa02c09c2810fc018028c22533c64e20a1454b820079592a022089195485890be064c030ca24042900f389c0087fec227e31f7ee0018c5719485c542cd12f1da68c01c51346907165065e38b1f0a788ece4b3b7d861082c7618021052486922b0a8097cecb0f00b6ac2698a13229a1861c7ca0fba0b2e9204922a81a092c40b2adb2551564b7450224a1742c00195245840970c396289244c9080051026f8e1228913b4b04724b89c5c02c15c8c78481046f0f0426284942b5c3084204836c23e57dcfc800b1940c111daa2090f2a330b5e0364bc9ec82204248220450735a1a236616b65245868f28493900c29ae218e00c5ca0d8f109a30219997307c1de105173260c242c2154168c282162316fe34e9017bc630acab10562b3bc843d6d84d40c8420588a083650a1f2499c112405002a9073665b9099a6922b1178f38811358c8a8010dc228a2a7dc405e2572802c9f248090041537653f8abb0f5d3792c30d0db2900208238cf40f8650e3babb3bf763a1e56ebec2dd9d5960a18afe217c052c7d401b29a53c4205603cb9a2ca22c8071f3d824dd5aed0a67907070f095003113f3f0c60a58980931fb8e841c90fab1568214416f3ae27e0900f434be4e0a5a3e30350112e0953441aef22374711543db8e921c9122b78780fae3bbd3e0409091e458331b545099b62e1fab35092800663522dc298c42073593a9a8dcf725cc7b54e4c92459cd5cdb8c54e988783b80aa3e0163dfc6487a3b08b81980af7ec2479805fee3597d26c45cf9e1b07c3712a224764b999dfec230e3cb223b0dceca1cececd54990a0920b28873b3e762f3d0cd3e6b1edc6c323f6b75d34cf4b555cb641b6b184cff90db5dea0a9be2d6cecd3e3eb7fa33d1679a0986fb2197eb52918db097310e7bd9bbac92d5f31075bcea8c89b007afe47a70dcabd8cb9ea4848d5ac0eca39665d91640fcf267594099ec8170b32e6ef6358c9b7d86e566af75a9146e06bb28650614d973e69f65cf3c580f9f349f8fa3441df066affdf69ac6711ab741ae3feb3ad0aa162034e979313ee112e7b08c7c51575b464a1b4bb098c6ac31db28c3ee7801061ffef27fc0f8431b56b82d725b8c0feb128b7de4b6982432a922648487a6759565b12e1593dca8137772a0052c84e2050e142f5657fe0b8c8a3c20142a3b5742d900199390d82716890f7bb0d440d4065846fe0e96915f5db03ea5285fece9b6a2478a5addbb943da2e7eab8d0c788d5d5c1a6aaa5a1f1bce7ae31d5c0999e9b1376e353b709655ad4409b78bb9bdbbb695208e84db483f521832632c1bb8313992a7ba297bf03860eb2c775cede2683a4293d56ad163b6d4addec940f6f9683bd9c8fb5f83479a24677f4a8989999995905275276cb26a4d854e94b5e5d4a29da81fa4f7a834ff4fd4b73be53e9d3a794fe03d7f66f67e61fa2c11808eb1536552ad5d85b8d6807ebf149c25be213d7d2cb6e07b75c28465072211743cb1dd260cce8ad8d4f01f78f1189622cc3a6fac67597ab09cb7f833d1a178521c6e71e7c1481363d401dbc02237c03842b4a2fb282a843f670b30217103912e7c675f7a7269a76bd4c10d556b4ebe3b7eb69d7412df3a3023ed873bf21771c4b0dba2e12f996041b5fe39a8c742cbca9c7225723af264d2d17723e88c0b62de34b8033fed2e47277401bfafe310bb45902d4c14476b8ee40e29d2e8d10893a241090057f97415c3735109c7157d71f7bfe32e4b58c370fff68a18dbfd7a80ab840d48532ded27aeda4330eafd8f377a01ab443c5b972e50ab65af11057451aa757bdea557ffcf180f18b40415746e9f1f97a64cfaa87bfcf07dac828721075d4fa848707888a382b68339968b596b08eec0891e3c32af67c1fce87f3e17c381f0e6761cfc7a3c8c193db3c2b38d3cd434b8c25af23414200f9e052f1107b2f4352c61e2158fe54af561507c3696eceab0f50a62587c103dae0e434abbbf545717df18b2e76b107c49e4781365338155107fdb6ddc327ef7e8287a7496b09eb7677370e77d76a6da994e200dc5424c25e47f129e00c1591856e9f1ec68133ed034f129a2c791d091262a562af5fb2d8d4e6de38ee6282158c853f76f1107f71276f7167abe6ccb0396793566b4e6ecd39a598f38333b3359d70cb0ab79cf0c7dfc71f0462207ccce9b9084d31a3e7ce8a6593a74b039a3f8fcc9dd9e4ce2537003d53f6d46cca2f02de7500f6fc406ba50fbf524ae9c35b6b1696bfd66ca21d6cc9cbe86359cd9ed2c7b24a318ad54a13b3698f91d8c330ac7ef6a95a79603dcb32d10eb6e455acb297fdf615c3b60cd3302dcb600cd67580e609cbdd7d060eac7777373b19fe9918dec8e4dd8527ee2f5b2e69646141241c0ad2a8c862ed780db4f1e6621777d80bd2a83a34df2dd6c59d9c1ca7dd955a86eb11737a3ac1fac862d27ac2a79ef86598a9729db1238db55a3b592de33d1805d674636b5e9a5daaa412c29e072109616de58bcdce65978aab9ba201065dff56640d613fe772076be00c15cb9faa2e910f7876a46c677797dc524a29a594ef52be94524a296f60fda54bcf8b6850fb011422e85c98e56ab4bac02822396cbfc3f3d85e052796eb31956c38d9967dc6f5e08e94eb1169d4342d723db0af26786b85a9f56bf6d534c3064c34b00c65d87efa55eb60304cc4c1006fed60ead7fed917e07ad0ee077bde26f7f7c127e86307d3dfd1b5966ab46caba9a9e1b11bb8a9eeeecdf3c8a3e28d4d1fb9285ac2a6ac076933f009de1858acc6851d3bd88b2eb87f199dd7a8b21674655282ec79676262b78a8595ff36547052ba011d4e6fd9d21d07426f5a80ad815d007fcc41e396ce2f4d36c446b90d2a5d19379691953df9339e587fcddde3c69eacec49fff8ed31c6969dbb8f9cb0fceeeeee52d4c019efae420316fa30e1bad087891e9a0b7d98c0b95a0bb5ddc1ea51d3ba51088032feee3ea306b6bd183d57ca83412bad49f096d8ab1d1cf558faf3629faa358a75f6eac367ec2b1616eb207b35db0eb6742b7bdad7ec53376ecd3ef5b76618d6b116b7e6876f44839a0facb317611800a91c72a129c26f7f6a1a722334a522cf8db0bbf908e7d8532ac10541e8d8982358540b345bad43f40e181a55034d73df2d32954cb665ba87ec615f63966559d6f66299571a875c3afd77b4f4ee8e5f618f0368da924ad6e5bdc69a6ccb78f7a22e6edb6b730b7263ea0606c03bbbed2bd7daad42cb729b699a38532a7ecd1eeee02157cb6ab5d1c29f194b22da7b3425082124e9606d4c7f3961ed640ffa97cef931a63b720c80974aae47646e2663474ec965b921087bf231ccc5650994912d5deab56982f187773a94ef5c8f968f4be0a95fbe7caaa26002ec0e96d97efb9e8d884d41212d8884db026ee86a10c29e84ab2ec59fc1cb2213107953910524de20afd3b4d0a81a646b816300bc94bb1f72db943255155ad4b1d7fa7774168362a783ed74b0fe9a53f1b979110465b8901b18573e4b0a210d7bb2863d296b825aa49962eb097b7f99bd1814833a28764141b38370a6bbd8622f3e614f46155ab6c6ed875c0c356e74d2a381f8666a164b1b9161bbbbbb03c2d6b8d06709a0ebef2eb470dd9da56d250410e04299ea224db0e746ceb5f8caa1a1f1bc0f7ac2a6604f90cf123e57be03f8028c823ca85cf934352ab4ec3ba3462fdff28913f0f2a9155c3e45ba297b65ea061556a3461d6c99972fb9946ccbc8954d55dea971a3b14b19618c31aef0971d3aa1c11877f70eb83455f69899ff7ae73d285fa803c80dd5a1169f4d374420f161608cd786116cea69e2cf5fa1053ed95082cb3fe7c77832d026fbf933d006fbf9357c62930df10e513d717d967072e797ecd3c0e7ce8fa1997f834f28dcf97075278f29bafc40ecad1d4c0ca2cb2fbafc295c18ece1ed000cd6352001971fc88e5b3967af0bae814f740a892a0df3ad089b82421127fbb8d5906559f614429899da44678c1833ecb0a71fa5acd873c79199233316dfb12c7b2df3cc33cf9cf498c9868e5c8f28c48a42426a0812a9c4d70d3048063eb116b31b66f81142086117a3297bae2e61071942860c533490628c94d6c0a72cfe74eaa6e88a5280d88b1d35d1306760cfa50c0bef297e8481e6e253a9a6086c2c55e0b31466970b173e95532a155ba584e56649d1dc9a2e72f9532e3491183be9fac107a8234216a2f46f68336b5c6a6a950f9f5234e0f049ce9cba693b9e8506511ee3e37da3471063c5f2a7dc9a547062fb21978111194d2043e84776e0856b075e7cd73f466154e4e13b7082c8f5cf5f9b0c349674a2f04e0c932af63caaaefb53d30d41d8f306c8dbdf9d1028b441159c5858e572120dc6fc604f63933fb39b2c7b3075032764f977749b68d8e4ed6e13e570a38d91a52d0046512245bb308a922f8ea1e530410e1da816e37b2bd3604b0b8661a0628fc9fe5819cac88f22b8fdb132f4fa63cd328aa4d22e625d8aafa45a4691cc39e79419b8edb30caf51ac1aeb035c7e89033c15007a8c802eae7a76c0f3b1c737d80c82a75192e7e3d35ced009e660a78781a95790165f8598ccb3f57dd9faab5c2cc95ca64f8bbea686b702e06cbdb5cc58e73c5938bc16f9c371055671097bfd223754863b58fd8082302e60a853b578d2a95ac9dff2d385cfeccd5a2ca5cdc1176ae23341893d256d0c69f7ecd3ed629c3027a0c5fee342aebe1019e32177b9c8901a14cd6d3a80c287bb187053c6560402f036a94ac02f6e032d06d69a1ff49579499ad2fac360af2c85e7c923c19e73958e71103a2602a4bc2739b7bcf1e3e88d46e5ebac5a046c82256008e8bbde00c17c16a730a8410ba8416bae4e1c2a189a8140c1b2b7ac9d0d000000023160000180c0a864482812408c238dfe40314000d719c4664522c1a0883410ee32808c220640c30840002806106d0ccd4b0246e0d20652ba8f095c1d1a3ec35fc89e1759822a8b81ad8903a06f03b78734c089949820bdbd4deefacb93cb3da812aefe34c918a670ec0a46f9cf48545754dd2eaaec0fb4b17a00d0099ec8aa7af300b6758e93f868ebf541ba6ae9c661ded331dad80c4d80e50d23cda86ff3982c21fff96d1156d0b8823fbf7d89e5892c66089668cb1467ba5ebbcf9cafed965745d4da7e6dcbaaa4f4bf9adee8f73692161f0ccc7c4fd1efc0525060b7c2eb423dd38ce969411786a8fd6b0e470ecb9467c6d6ff63fcdde623196ef75dee4525c94fe27ceac182c3ffa17142e3d0779754a65157bf236c9cd5115af12bdcb7d95bdcdcb298ccf3cf0637d9e6357f5887176284ab6a4aac15ad1c2ac389fb30d97215231a145c21485e5ff41de83116d2b6cf39b1c76aee4dbb887ef610308a9ed76c70bda1f9008c8ed931e0b5b9c75cd26e123c35c6483b85e30d21b35e4ab875f91ef1ab923dfcba2ca28883df317e3833fdb4ebf877fa973b22718ea047da7ad73451d7e26ca78a152b3476d9f527f863dd669b458ca942cca94aece4212c377b051bebd183ae52ec3cfcc1d586e88c8218d4357a07405cf1909a500b1ea20835c3e1a6d49e51e984019f12809566990a2c9e6073fd7066fa4401111e80927887f857142a9fc9d8ae14a05401c67ee1ce4da9267f9d416c5fcb6950b600ef928e83ed3213ed3de9eb32410a73c31c635ba56b95518f5a4b4a6a2fdea973009c1c6fa8664a18d394c3357f58fcd8d693938f20f96688e8c5e421b8dd2cf15d879e918f390f8eaa4def9670139a9b0d0225c53b7973cc4fbea0d859c5629eb1c1e9de71831fab0b85d4d9b9cb60ed30323f4197981442a1eaa8a478cf6316a300edd917840c740830d21115360d557664f873c11850917a70dc455d267e8efe75aa00693d08f2853ccfdbb8353d503dc74b715ac46ece0eddb9c131a09c65ac3cf5728ab5d42047988c499bd9d7852f20d23c6f5b2bf7cbc26b26b43f9cdadde78fde7955b73c720b3afb1a6eede153726f25973baaff72aa7c4aa3047c1ec553294105f25ce489707555ab535d5ac9f8bf8bb9cceb950395358a70d30a2d374ca3212a787117ebc13b8b2330a3a51c8ec16c1e9670b18f6a84bcde71dcb4bf3db7eeac791d5f095be30ac6885a9052bc81cae397811fca7ba03b4047ff0741024424e3efa18230e626614d8cd07c8b68841a30f28f5e415d1fe265d96bc31b2a6860fe148d52efa7623e2255d2809dda28f801389cd1d626dc7803fb270279045fa24c70b7a3cde8a93a25be5b48fbe3251e96bd31196595c286e753157d023b2468d648656c454fc144dcbe8bf6e53da7b3e835d03b5ca568bdbda3702bc15c0b11d7ca3b189c51a4cd80bb505091070bb57800272b38d7b0d2073b0502aa62f3595643a0f3e6d3b753fd16d4aae3dcfa18554f9a9f8600170619b49bc27586bc5f1145aad4dceb6806a233488e94033b81ee5fd27f2db754bac394c32aac4eac06941337aa9667b63070adc674a931b6217bfff1033a0bc01982860640c547a4d360ad0f061025d07f79cf4563f0a8ced79547cb0e43701b3679e80fa5f077414713b710934fc2f8b3092de4a74df27d20676fd43b83060ce6d386b4629932544d209c8db3b7202012b89fbe9336cb02d1d696b9e3b3dc837cbea921d04ab7f72ad06ce152b0b826cabf8f25d9d1c0da2e3a367f169dcf86eccd057c6079bfd131df694509e6faf895287be9d1f3d574400acad06997b02bbb6148174236fc861568da06eb32bd5c6cad4e57624a3764a3d224557b1210e4a7db9cfb6c9f8bd8e8c95faefc67fbdb2bf557ad0b3e87633e11df661d7ed5ee28e3262ce1f754afe4f23659e6090403322a23b6d56da16b151d92f9755bc5d8cc85a011cdb30404a3ce2f9e0bb84a279d5ef3bb215ddf038a6b7d90e13a375c29953d2adefde4c2c967147a1559651de09b9ac0f34a5e284c999219ea1b7fe1793a56b05e4d2064472b787bdaf0580a0cf525dec59dae74a70632dcbc9bc65aaa582a402a2c72e45a6941bb236cc58138c184a9ccffae354e468893faa15ccc1f56159f743419c9794c229b799e64f5b1b4e7aa0353b3b273a3794f2dd0bd254838bc735650957568a64e6dedda5cac4fef9fc8ead274cb022d57303644be6aa8d3b85082733446334f6afe903c853df2e365bc3f83a20ab51b0bb50eb93af286e85d6e9a23bb5560d6ab4b31b3e75efdd9e31d80ede9299ec00c7085368a54e85760f64d901175814124640bdd2f20f88b97f60319307202d1735696ddb0f0b432a9ed22837d86b015a9e318413c68af13ff3c37d72c776a0511eab2a5a6a4e869163800ff3475f5f32945be249c5a1b88478d288448b403c2afd9274627a233443c54fca1f9c98b1c11c1199c69c72b407d87f691610d821ddd3b694b5a9968e2d72851fc959abbe7a820903f61c92b9bbf3ec051a1d0ddee799b26f9f9491d09c06aee1c0cdf1ea2a537463faaaa647184660b21d344b55b05043d2fa8ea53374b28aafc977d4688653c8351afaf8d66073b228496fceba61d5519d3271035af63a8958a8862a060ad323a6000f170d853ddfa127feb039d1f55b904728d7fdb31b5ec0514db3ed3d76ad780d761349eee99019c43f799045b54622d3ff798532547eab0e2acea29ddca09748b7ac6861d1a37539894071157275a229cd64d0cf0312d1900d0e7b9e73230c56a6354876579489f38095870d73f815e9a16349c0206883f6ac6b3c746755e6fb7f1d85952ceb559d2a1bbe4020c6f157c771eccfae293802c6202b62d24a19cf192721c46e21ee6783c4d565f242681627f8b7fe1e0fee8755918e9b0c72ca77f8b03f4d69831ef535d30ac5025912e885591ac5e68e0a67dd053f72fce759b99428cbe144a62e55d8b794d098c32cb54254001152c0ce10855dcdec7a4770dd32971efc35971d7bd4d8bc63ad4d79026c737b31011cfd5577b78241779b3f01418a4770c36befd3766f5acc217ff8b9adb0d5e9e01e8bd3cc2979a7036da02f6ba29e8512e4f802f82c04de0e4ca85b362324ad88228972fc8c015d796b9a89cef0e2743800605008bb3fc0cd186bf317b0dc1c31b2c20d2d1a04991b84fafe12b38cf84dd11998485686d457184a4430221886aaca718a3a5ffcd37ee18eed5b053348481ba1bed21800916a4450f8a80aebaa202056f5ec4d44a91b80270367425b91afb47d20fa37f3aa79f4087104c62a4f67917ed7a20b230402e9ba1d30c13bf968fcea4b9519692b0c8a2bc7e3faad8309380e031f4748c31a0e61ed2c3e4975517da771fe309a43c819f6681bafc0ec012940b8ee472b0eae2b8f88f9d39189912d749c31958636a9f4e9a45fede6aaf039090355a31e8a305b6055afcba3e755a5592d929648a969d6aa79d193ac16dc7c47bfd802b334525d254fbf4da5373a3656f6e5e1213de3deccc115bbf7fdf575d93e78f87c0d9dfe4288c76b7b21446103f203e638bb28926518716968647484edd651b97ab6a35a5724619d7f9b8530677c469f1c373adc2643f9e02d2ff74b2aaba5c7a8ac62d70a08a824ca0f376417d75720fad6f12f037e15c7f09ba1d923f5866f6afc1108adcf89e66adb75c589b35359d3e4c7262dab1681c8ae7d468dcf694b730ce99c1df7334e121471a2c5fd8f098d42ef3f3752b8e170f4c9daa82dd9d2cb22e7e4de0ecc064c69c3a5e752ec2c57c9baf2c036bcaea7e53aa6fd80e98dbc54103249c9c8823def52fb767f328ec083d58dff86c22521cdff813585fa2db70323105fb1e48a04e307bc9a8865ca514058146ba4c7e9ab4586c48899b3bb23415a347496f223f211ed47140632f25b2a0fdd9f1bd7c2c5b4c71a4baad1e5fafa4592271e21e30bb2860b2a3cf8546754ca0eda754ad08d2cdbcb55526a513907b04f582840f0d5e73cfa6f08faafa958470489c507711144acfa6651229ae8d4610a57cb6a3419de5d2bc52daa20fa08f42d87a86edf1961c83715f755b76085c75a1f159bf239d5be7b771ceaa8f8a6544a7ae65dd97adb34483c1c7380160af42853789557641960355edbca298161caa082bd9ca4b541e1cfbbf08db73c1c28cff51308f0c9ba56025494adafb008fb78e9b959cf639a06db03a8c83c8cd6bfbaf02809153929608b10fe128fab9586ff31c24be0c62cd84f5555cf2645856fdd5990ecd7fe63e63101922354ff844880d6385cc103cc3aed05d3c74c8f0e9c5515d4988904915f454de2cda5420a71ee8bd2d09da5534677f6be4fda7064ccfc101a7194970c9b3d55459e579e3415324909ab1b073e4a6206ef3809b795b4bb6672c6778c6491250e858e1c3410191a029a413c6f3a16093e0fd0296bedeeec4a720bd85d8ec6cda8d0c2b0d01d598b4d9679a175d0cb6415fcae4b20a760759b222f3699b43256c4e9b4e4592bd040605b8b2e3d3d957836674b7a63f5322aeac72014e6ab7eee11abca7feeb12b85f0ab0090cff285b5c0cd56d107b54982532193bd7d98c8afa3414ec222bb06d97d06b4d8e0699e46816dd802224ac4cef9e6920d3878f864d4f96c1d0ee2f7c8a814ee0919df363e493326e252006ae46c7561d1e43174b8a01df804e25e46ae4d4c9fc166ec854c652d0d7ef322572335e3cebc781fad0a9595b8fb970a35c5c1a3c79bcdf0e1fc1460ced2bb6a0283b19110be25070931a42fb6bd2c5e3ad43ca2024dfac5e4c24907d9b367c97787e73a6719ff52dd49b33f9c41bef8c4a67944de9419def43cac50c0447894d6af78b13c75a5c92f73b37562f4fc28b449769760489c231429103ca75d5af67560d0753dabce4669c9561a3c2e5737debc072e361610fbe000a5906a9e046047356cd8725433497991e037d52e41ffe103a8c970bc83083c261f35ee18482765402ca377d1da19d5c7f2b28014f6f8937ae59e3b14d09037b3774a22c706ac7fd993f784821f6a1ba202b925894baeccff7f22b31440d8cbd6aa2c734cd698483d94c583ae608bac5b50823d669574ac90a39ae346b674d3257e090c41b82f1d0f23fb8161f48807b4f2de6173d28d6241dacb47e2ef2622b707a1070fc80cb0d40703dc407096dc79d5e24803814b7643988a2536dc4af370cad372e45ab5292a50a791a1f0211ef29dabd3b411ce2e1ad8baafa17a2545d18aa46c615a3bcb2929d532b9f151897672fdb5a47d4bc83654963760c58ea5827ac6b64455ee07bb7ec15804cd0116d4bd04344b51c957be825a0e526de6d3cc2c33b41555c561b3f6d9c718356b747055a1051f8a6b8ddad6cc17841be88733fa3348fb19c944d14b37aa3fd4248dcd720cca2396822862fcf411092e6aa16329f40996054ce04009142eb6fb9c0fa2167e08ac99c19a0fa661ac1479ea020833f252508769c12fc08db31359863b831c8aac352a83c094d43938aec054a342e3d9fa8beed2799946178f06e1c631e84bcb24b136adaca8752ff1b70a8ba4cc89eb527194f13635091da168b4503359818999f2ed386e797ce48d723b0ffda9983cd5457aa7eba6919da211eeee64a2feb60fd1c9eb7903d198a47f62b8498d7c84836efe3d25a4262a8be342899088a6109c99f43394e383b84f242ebe09199ea861132177cb0fe5f70636b510cc25250542885399226d4662ee6d7010b274327772b3982702faadb8e60e1ab13c25b886948f851ddb35244f51e6eefc03fb6d62310e149747b504d8ecfa3e219edde48e140ede814e884f2fc0cb450265d28260b30b06088c4be5441891172d2455e04be6146643d1067f49d5760046ffb0f762fe10e6d7e04a82c0b0d2cafe0db6684ede95e849ef0aab1b9fec73f45cd5465785cc3ff6065c69efc2aca537ff6ad495a24e6495509b30d2268f0f136d5418c4edd6cd5ec19ad702b660729684cdb81097b6733c17a6df4073261b134e38436113d6979e738462f825e7172e4fbabb9403992321812a2e270e45a3c2f01e27d7227f039ccb18f745512fec8417f2c4fc48f44679b607a0e66ac8e5b766f7dea92f44546d42f63af7bce65080d2f4eaaa33a7fc8d73eceaa502ef4c44a61f2384f1225c909e7310436e1ffe06962ec2d23564db89bda2254b548b74fba279b0ea02f4e1a6a7059881669c19ffadb4b8881229271d3e41bea1741941203745a384821df442b0516902403c444a4422bf5d4aa23c4e69af363f21bcb9c1c5ede8d8e73477168871968239259369150b47b2e851db6d10d84a75488bb7047f9ea54663ac65a0ee8fa9a130da26f2de11b60c70bdfe5055fc992388120d3f45b4d4d6b0d9611c03d788339e24063ce2e669dba48c4df430941f3f05e47928c43d2dc95e231e05d28b54062f510759089a58c2fc387a87d56a9933b2a03e5d2efa18acc3271b65c41fe7fbfcc696a3d8dac67bfd6d5146d81fdaeb48e28b81a596a28d7dce43935448c1f1fcc9dde4f730b267dab1e8148b25681bef54f67dff7757be4976079cacaca5edfb6a7a897fbf8e4d6ca7a792c590d382cb75db213c4f1393c50a1b1b62da169d7367403a2d226347ef987b9ecd209d66e9603d1605591ed0bfa291651735034cdbc331d7cda059197a254edf47e8c4be922c5fb853194a47a73369714b66f82dc8ea536eef849256ae37314b8d21702c122dc61e00db0c32cf9e9e09af12dde2605254fda86fd079a060a57521f8de54ac21d479407ffd4eca1709118c998b0b98a5d993b87f4941325582c33bd4c72efa2b7bc07a885d2c3c60260f5b514a22f012e0093eb8dca067ccc9ee6250865633d931d7cbf8b3057913e0e76f546296924c6138df0c70fce37c376f60ec7d7781c04d1ee4c5bdba76b6d40968052ac3ffd0fd655db8567c93331ec89a659a01b35768773b1c5a99930a0ba3995209ac944c4869d60d82da85127de3fed53efa5002c0927bc6e24c4e2c61268312068af8c50f8c29f0a1d6693768a0ca9c1f8986faf23aa601b7c180773890809931206e5686f3a12a0d3182910413ccf2c7451e4b256a5880b9c7505bcce3ddaf079a80df2125d8f6887a1ee429e20de3d09c51aa992a00ca9adb5f611715652d732dba67b481d701b1850f13d3306e8114f8f4acff9734a25a4efd41340741570c02e39ff50f3979a92b40f3749611dd54f507cbcb5922c5af087d96780a6679da15f8ac0abbfe115706d6614bbc9d49d45099c4a6936b4427ef1369e6f841284be4c3653811a4a5d2c48a36e0168f40951834a1c240c3a09dcf0a039ef5be46c0d2a48710e7ed404f0c60fd9d59248491b2fb9341c4205f5244bcdff7abc80c3d5172974594b0ac8ba341b90b02fd4b524f433f5b18f7e76612fcd4d82d0294aaa36e4ebefdee6396f65b2062169a6c9240b7aa7df59d2796c9e21624e4301428aba44829cb4dd67a424af9c4f5784ae265ba79466a4c8fab508cb978251dc373effc3a79ef4a64f65a54ad7ba9ee4884277b0fb8863db415e135c54602d2bcc4593406e0c147dd565bfe74835893d652295fa1f301be604a2f3543a27aa479a1641ea699cb65d31b5c66eb32bb89ace7373591da38fa8e07e5a4d48b548b2df97bf2bc8e0f0bf397182f14386c59b28400ed3b431a465cd673880e3dc87db17d4fd4962b3e1433bb86ce212ce1b644e2a96e5c13aa0ba8f826343d0cb7f2eb1b5e71c63dbe9ab027412e31e2b8c61be798b200df993a31c4707ccac01cef07aad7febc0ed3c459c4df5178d9604f06b764204e6797483539454179977400c6871c1e19ca01ac643ef2d99c5851898c428a47e6122c95ebc4c2ed0c847b56f4991039cecdbe7435f0c3ebddcbe1e8a410c4b6d9574cf4c3f4731cdf706f3350414d5066c86ff082328c77263200e4213c1264a5bdb9d397dc4a002f8f1c562c7a18cc925818241f0e0a09f848a8ff537f1522807ac66e50b1e0a2d5666ae9220ee07d61c5e40fd30265222ee5bce4a14d54d564ad4edcb2304be909cba1db202c4c1b2ef876e70289f8b0c4567257b1567340ea7e2039e5e81c17f3876c2b98c99fd46201a54ac678c2fbf133c329d9eb91bea6317ce87e384c83b3663b712eaace87cbd2c10f169772ba49a024078785c9b1773ce67fd86243e91b619329b6e0da966a8304fec5c77787c22e5cd4401c5beec740f0e9da8a7ef17aa2844602801f52c7498d77cc8f234a8b5390ae0475b7c75baa882c08e1dbbb78edefcaae029bdc69409c8cf52f00c3842b2052c7b7903e8888e83ce8120d11fe6236a232acb9f2f860085a8aa647a3301fadbdec035d47382a919c293630020227abf2df75cd38f02a0541dd341136277b269bbf70718cbfa6e2a827db3bc6bc9ac3c310249401a8d0667b4578bc4a98db2a7bcf890c1536805e2aaf8787eb77bba45c15b6df3fb8c08fb439bca3657f8cab7cf182061cad8f8d89a502d7d43a7c53d2b5625dd6a8782ed85e3e31544507223f658d6c334c5df62c5369c38577b39616fd93ad17184714abc2616e3497e7a459041a2a747075ad01f508a2d18f480a363f07fc7eb3d76362720b271ea70db8d95b767e6fdf0a28dce69ceffdae8d41ecbc050e68ca2900e14a42b466bef941c8b1304bf709b12fedcae1c0fd311375bf00942f24aba9b4706d2bfa263f1fe1beffdfb53503afffb6a1258fc9137c412efa112a037692cc39ec1fd0f48f5978265a547309c0aef7e55c1a44047da451e9b27d4b521be3ce2c0ce575bf243e589412b19308d587f59dfdc700eb0a76ddb2d029a0820a4cbbdf31f71d952166fd575c0973a953bbd216e00428474fb960d6312bf6f2a8946e6ca823cc3eca31fb3d161f16cedb512136e9e43e5ae8c9883f396b5f8b10445ab628617e5f5b1aa23af1f5fdcc4bbb842773e3e469b3e26fd461a8e51a44218e93b1408ec4b1d14a0ab88df9c73db1b66b2ce4e0b4372d423d59316760e5d880230b099dffa45151a5b411e6608eec9aec038b07341ca67f5da6590a73971591aa25ecdf9f93d896fd3d56ee88b8fb9797c1b4cd3c772bfc4d8e148b9dfe25c30b950ac2837c652003348941dfa312fdc2453b68d129159cc7979cd8420e3b562dc14e28314bb9ae28e62003eea1892968ed2401b211d427decad48f00e72e8078844816869330e8c5b3306702aa16bde6e2baab80709ec2b8d2d45f97c2b8b2b815e7635fd5412ebf760f6e958585789da424b2e5339ed6956c62c7b7c582d7e0115f76c930d54ec9c1992c2e0a6605b79ffadfc85fe101beccdffcadac593de44889aeeec0f38edeb8d1838649820e6b481ea5d17c8394ffd78d2cb217f1ac8143b1877f379b0217d6c491473b35347fc8e9e1fcd7ad5e86d63f796083e99cea36bb88ba878e8fdb1d6b7a626474c356ef545d43c378b416fd4d0a7452de79fd90628219465b9597644da8c5d3212219c35bcef42d3bec974aa51e5da9d01ececb742dce019e41be78180334fbb63f2cbd729aebd67488ebbef95ef121bec1aa3ec76c21e23a053e10f3599e2ab4ce8360e31500dfe2c4f114099e902ab55fc81c990025a7f8cb98ac12b821e028c1abaf024d440e1021069d901017b9aea09dc2da159fb1aa1035f597a81bd313d201cea5443bdf38731a1e5de2681325c8de6681d93bd6ae01f751f5a45eae76ec6ce6bbcc33af52a634115ee4ed244b46c958120e09067172e18e708a95d48c433cf3f5afd09964b1fd770208226358c8d80d2e96cea0acf142879197361030e120d57b7195c0dcffe4591ba5cf09a914fc8e0b59c92540e7bfebd7bf8e3945bd34f9f038264314dd0e7402a1e1720788653b1dba7cbbaafcd96f0ce966ebb5b85937ca758fb28cb1cd2086d3c1cfddc1228816a5224e440ca53c680a9ca1b33a432f66ef7dda282c7baa678d3d44301912925eb57835eb3037da630898bcdfc5afffbcc32576cea87868dbf787ba335f4be2f21ffcc3dfba8608c9e22fc1c71c70846039322f54f09ec622e5f5d895020075d7ec04881c2af15b1059fa9604aa45a1a0cc48ff6576ce1099fcbcba394b722283f490c5967f092964ce1ad28d06a2a2436116d50bd25b4192eac568cbf21968f1a90a7c385bf353d5f5a63b4eb9c77a0f071c08dc4bf8f1ffebf77b3e83c442de1ac9422d71dfa5359e9f39b259540dbae871ff152f53b0a882d3465f40cc8fa94ca5a9bcf71784ffc4ec276806cb9c44027fa05a180900c682aecea3369ee27df1f5c5febb0c02ffb7dbfbeed825cb85c040e41bad7d92d53be038083df7270feefe14eec98118c615142b07f661c8255ae7531d0ebcc6ea172abdd41cbddf4c950739c2d7f959cb1d8cd6c8cb948051460b6de3fd2c4bc786827d6531654b1987fcb1449704cf954a739ee193e043616f472e657c789298d1441388114db6506f80132d80bccdde6b636249be29ea2b36e0b1119dfb3bc842f8e1e213ace37ac2fbf00b31fc097f9ef832716357de6759ee919886cf3bc2e70f15936e18c4c917b26fbc1d66cab8f7aaba25058542fabd1c278f404bf2c698da31d9958e626855c3b68ccb4dea9c958714fb3ff859939034ffa4f8711e4d1c02152beb49e2c98fd1187ca96bce8e1ff00229969670ea2b831087c26414e7405b43b9e4741d5f88ce12a62e41f89aa96d1a33d6194db75f3875e9416e336f2c657bc464999f39e545de247f7a055a783bfb525e3c90b4f4bf8577151ed49f3322f52228b109dc271ad865e3efbc8a83c438367f07092e545be64b62ad9e1f234c8a98ca5c48af69787955302f0d2222d4c367d9a03c746dc602244a3fc51d3366089cc8760b8539885ed1380fd7cb1a35f39ba010bf00ccf4e097e0eeca98e6313712826d2b1696777d0141b4842fe3567d3e5ac61b526b64d2073a5a866850bc1ab5903c1f839de92c88535ffc987bfeee7f2f964f5f41a1ee83ddd7a9d8dc85db6224863d412007d38fa151b3282b2a443595685a6dd4b9cf85b5268231f8c40ceedb048591267f26c138db016f55ee8adc00153f0889aa5b7dedbcdd4a928b4309b9d82b7c5cc75ae1282fbef4c1e505bd43bdaafa98086fddf49904a66bf47b44d711ec3436eaa75a738571ec703e561bed207580857451e77ee96ac895fc5bd317af37f44e54f7688ee5cc0533c9d3e57bd4b849f7c83b432d81382e7b505551e37df678f81a077f7aa17117880ec70fbc4318e6e83e0fd1bcc8e252d0142f2f6331907190c25c6e220e03c02fbb42cd1cd245d5b55b6db46d351eca715bcc81790e4f76cc246ae6529d45518a42d1d7854edbb12303db3100fb716f0487dbd7b6c443c9e8eb2054b7fab6d88fb091f1fe914445b83f158216704ab45d8012ab205c71f6c954684a6264da54e36edf8a657a60d956f084e04eb6ad89e2c036ae899a72afbde7e0ccc35ae97f128d2d0ba4f23ccc022092a28f0f1e1320491c5ac40adc1a7492c5d42c3a5c892b86b3740ffd90aaf56b944b9f11d5bb175b5c904450f08c3ee3f4967ebe65952c0cbdc4b62dd6a33b2396d70549abad1ea38d886b04e6430fef4b55fc780ced22afdff5a20ca6b48301ad063c622418c873fd0e0e3d497cf9a3c837ae3c20da7d3a1bea9cb4f4814c04c17ebe3611b1b9b4a6c2d6b77c260f0887385975e7e498a378930b4e2e1a8c94280313b5d88824c824d061be8232928f89575a16d9bf9f779fa7ab166caa198fa60184bfb26d761dfaccd3956d93ad36e5d659825080ab037faf16239e6886bc010f6b08831aa24076457a4123d703b5a4f70029e1d8c6ba21d0909287632ad2224c5d6be4bd2c8abd82d2b7a1fe0c37e00bca21e7c0d17ba0ac0e2a9d3471157fef448d4200809e0da8fdbed84f5f2df474abc8f728c0ce9cb576e6f9afe79abc3d63636e85bc696ac80c69579a55254d6a9d69f04b4b9d59b63e3aa7a308cfdbc2a481a6a390a5e93e9f83a83ba2da9a9c384ed5b9e818a123a2577344da43bf2640f4708f5cd3e0b7c90de4500371e714faaec4cc1f48bb97a2336eb06c0322f6798dce6a7d4d44ebb7d751015939941004c64562d30b8d26e11011462900a6b15cc4b0ac7cbc9cd2208a5a090bbadc09116040dd5a7ad3a000374f877f93e941c92d45b811d6d3cc853e4eeeb4bfad8c505a56c1f1e3f139e97f8c3b3dd5d9c241a45bdb4f9ff8a8347057b448d91a50e578c3e6a09f7f51c1b293aa2320dcb27b0716610eef4aa9afde72d4b3863329a0258db7416464548ae610187aa7adeafeb0d6b4a3e729cddae85f711e0d49dead3534af8330bc06b05c7b860729929454b5233ca20e0e2b1d4d239f7f5ed28ea2bbdeea7333d68dd9275c412b47a1adbc1ce3c88869997eacd0ac0e39af05ba834b4195515e93ce39343834e6a276bad33c814f8ddf0509a494bb37f549484260551274e0da5d50b11051c4afc6825884d5873a8b2dfdf4c51031ed60b5650d0fd0d7a1ee3b77fcd4538f6e3adc06b44b3a261dbe168f5cda18a05c2f27bfe154ad6c2eb39c54516a04ce8883b2e27d0810e165377d20257e8dc66ef566fec8b6b4d4a1718ddc7258451ebbf72a746751bb203a49a3e41ed1ff26cb009f80527f9efc4a29a9817615f942c44db5382c77210e7fbf3ae142acf039ed526470ef1b0907b63d3ed99b654c8b0baea0227cf37f5c42583d1cd9e404364401dc232d6ebfe3b9e8a114c8a60e987d38160ecdfe016ef27b94ed22c115414f413fa85a6bd8ba936080d121403ee49041db9182c62f32f618ed2c77ce71a8fb379ca1e9e4ec588cc6cecbc92f0f7b0d71d271c644a03b0d9af3279446b395dbd6a799af1f0b6d06ef3793c5d60945f45102558b2609b16edb5eaf42a2080126a10dbae659ded65aade0bc91c7dbbce66f45497add1182a5a60f4de12a8ece47e04521450e6e55aa63a33409bea80ce7272eeec43d01f9ceb9c80f607681fca02c4c2d9419fc171cfc855896b7acad0514f7c4f040d501197c48f497739526c0143792ed75562fe1055f5c22c9c07050874027b93316c494d046415d07d9cd94b8b0d8844901ac1f3aee811f0246568573b5b70031b57d92580841753d32abf0eb3467d282b40fd34ebc55188fc7e69a59e8bbd13162be4161e16445911b7641158725609bccf87786ae9a8a9166ab28778e3f5196246918d176377da8ad1c91f22dd6eebd12544df480c6c47b042c1ad433decdce55d34def568e3b3576466e619a9182db456c69156d042a0c53d9809e253a42409677fd159db82964d8254d77c7cb6348d51f7b99098f723d249483e4289d6785247d698a6038c5b9b93e8ce4ef534a62282625c65a69cb0425477f85913b0525f5075180829c4c014a8fdfb2fc097aa5a24aca2975c187d39a56682f27302cfbfaaba9fdd875c42962e4b4621703d2565ddbc455598e044eda4eabbfd90e2594764a455f5bbf1c6b958deeb85752dd6934a43e6d9de862d6195823c776b155db5ee9c7a5430b0825fc0cc4fa1181fcdbc5f9c270bd3b5a1fb9eb2ef01935ecac9bf48cac4fe01b173ea6719f3c1fc5761416c9f656cd184a5ec09676b3f302bcd138f410656b0674fe990b79076401e327ddd4909aa1aba2f50d0d28fe1b2a30ca16a38eed70fb9294351c38582ac2635ad29b1e8f4c7eb7cc5678444c2dc2c55660bbeb96f064a1947d90a18b24189acf21627fdfce475cb1f43456fd19e9e908dde5d9438ed5d38c2168e44773eb631d4a738410fbe40640a218b66368887899d1e26117c554c0a933ee21c2690593bfd1aa4f4ed7030d6e93ca312f6caa574da68aba9195a8a98b20825f05cfb5205c46775b2e7f4a2226f5a3cd1395f69b4ddd2c5b2f35ca2c7f3ce559608a9fa20d7698cdc48608617514443f643a7eaa4e071961dd715a85688666de804845af008ad30fbae730614a0e1935834a364d355b3dc87933e07c230f532d824a87547f10613819f20b23119698f3c618d07105cb64fe816db521937e3688d5ab3d066a11d58a3e4e8ba2a0a763fe4f1fc54fb9097e67f0cb0667a49b421fab2c4906c598374047603a67e490c01f87b872b4de8ccedc64286b702b9ba19dc6079c500357c0e6170feb6bfbc41d018337e2888084180391d422a0a339e3bd211d0c5ea54f054b7a2c034093880db8ccfd2e91b7bb4c4efd1ae63266bf32fb861a680d9e8beca82e18cd582a94a20fc0b9bd9e512ba34a16dee067b6ae468d6199ded0710dfe3a296eab2f4a755bee54fcc4631bd58c6d56a85e0ddb6ec404c68cea2059363ea8ab005e27dc3039ef00b0805a3656a92a41554b61a3dd6ee9aac27da34fb15d226534798fa2788912133772dea2744182cefa9ce4b125a712e1f7d747302b0a8c38667baca4037515be7a017ce2ed7bae2a817e8edcd8b877d934591d0f9fa98104b13836578ca73c0fdf60a127e7890cbdf98698ce7b9a086beeb5a6648de039aa0f41d5b4452ee0090e64f6b5b173a8f881a08c401287fef2d7aca09e8cf8a7df34ef6502bf656d89433216230eef1c7565c9e4a262137da708e61b715742d2f119611ba380edc00a22bc7066cb33af0d8ec1490ac8080048d55c2e604360c71c263d275df1f21116a23aed8071c099bc3e6a6294ccebae9141e7a4ffb9ea23e1434a506d4178532d5b9dc04316304468d321166a7f397a8363b0762684754996dca5e24a674b53812c4f0fb82902ba80d47ce940c2e82dea29bc11c56bfa58d47c47b4b64f100d7aa0830f413e0cd0786ffc648caf890f715f765601f465eac8224c978906569ee4b8c8fe059f99a4703561fd8504656029c9b04b7560a87cf7f6639501823cd3b2e6b284824790f121b8f781774095a4c00284e6752a9ee1c8853a28fc8994f58632a752ae322bbb8f6bb60918bd164cb81d643c6dad07d7ece59d631dd9355924129cf908bf60ff88000ca0f2628b83a9551845659752c755d93c9680824bef67e10a9c079930d14c283ad300fa3850301ca9906fb044156b1b06cadd6d5fe2e66d9fdd626f6b140881fb833c69eab35cf74e20c1a5915d03502661e03494b6485822ddc0bfc74518ab112385cee134edd81c601989a3b8c90b81668c91dff3d1c9508ef20faa91beae119b7d4d9cb192b601b9caffa1cd9aded04a15e28c75f28726b3812b87bcdf0235d5945c0c4753076c0c6b0f0f623838cbeea1328da4ee76ce2fa38b3764de771abb85bfc2fc32fafe8d116e20c5320f057b56487e198b957a071d4419e15771abca83b084f15cbe1c06462a3299720ecdd741085fda62872549905483a053746c2cc1b52a4636401321548cd1c6ae6989ffb6ab3448de565c8e0d7d4418d9d678223a57544404150f647604d315afc537cb652ef4ba6218a5166e053256087f1f7a33f696bff797252e8d9dbec7aaeb4a37b74547f19af46468f9da2cce5ef362e22715a48d4015f56c55dc0207edcc202f05bfa4eb67b44d9261d1b64fdbf929079d004ca05538bd612c0eeef0541404983b05f6460a48eab81240eda3ee2e1ba1cf29efcf5d55963265463cd9b47dd1002cfee2d99246ed3d2dec7b86770ba9ca427a912c27dbba26452b988103c22f0e2d314c16ea6425cfd53b3e7d89325362a7651cff2d8292c471360178b152d8165ba7bfa93360c5d35094e13c30e8c753a6ab51179ad65cf97197465f352ca94762e39fccb628246c193d6b079f1005abbb3880e8f5588548957e8435c71f98b5ef892ba70e02cbc1578ce60028e93c702bdece314a4bb2d00dd47f33adf62f655580ad1c0c4a2621f483cbcec34916b65f32bd0f63c7ff4c5465263b1711c90ab079c49fd6d24dad528e7caaf2465703ef74d43b1856e0aa4b80f57de504fcf05ebb8f38822141bf5eaa51cfbf73a2bb961126b96c2f9a5c1d8823449206487c5879ac517931e2c14121bc937d7b94d5fbbe7008e524f44a027cc60cdaf23b20f28d2440d2c37ea045bacc1517df436910ddcf459fc19c0cbd723640f801e43dd975cf31e35487f81e56716e64745588b2e0a71b3b57ce73c8204db206fdde0085d2db6eb3b02525e339583d759ca1298b74836169146eb0fd431319fdcb6eb8a93e060d41e20e43646640fc87078c760d07483cb050e72f881347611d258842679d853968443563b287c0ac8d86c69f715f229106e2acbbb4f61bb976282a7427b7caae2ffd17376df9c1069a48332e567b3211ebb6135bdb62f4beb2791a393bea981495de42f15c7dcd7e4d5b086094b309a8e361071579203bbaf3e8c369d90257ddea5b14bcd94e45d25bc5fce32d85092a4ae9364db839b96f468b0a5af0d6b95609faacb6899f6c5f2b0ff85934ccda97df4081b52728752a8c3069824329fadbb4abb91202c9db14c1c2c0211fda81f5c4d2345137c916b8e451585042d691a87efd2bcab98629e6b949d57f236a1b3f764a07384abc3f3b072807e15097a88a8da10291f9dd038d71e271f1253903d2a4045c997d94cea1e8f12ae653d21e6c78766c31fc6330e201b862b770e6d1afde718a8a32fd0652c2f5adcdfc342182bb619ea4a1305598e7598f2cacc46b169ef11ac117823659c1be87a0c3aa247723095ec3b10c102fdae4f31935a41b377b4b5d8211100cd2bc642c344e7b2ac362f830c6750c31311ae881495ce01892f6a1624f5c229b569e2f064b128eb31cb2368ef882719cef2c19fb87358bfe9dcad12a2b6aa5eb13e1a1b0b4aec3b13c6ac262198dc149585c413dc0604deda8a6f94f5e2a7c8c66fb9230dfeceaf3977042967525e0a08297fe10a74ee31f5d71c5704a911413d4ce30dcbf37d2e243263138154a75c073f1b1130b9ccf69df01e42c1808227079c58881173362f32616ef14954af8e8c5fb97fd951060757f285b2f3b21706424e70223a05f3cac17e6005b085c551ee8b17e9c3dbaa13820598fdff838504740957f0f67414cc52a82115caaa67025725a8884f0ac47e116dc0319057a23b4c58c1a72ab36681236872d57ebe0fbf6b3847e95fdba19a4036e7a03ca284a5f85089065e7c772be2799913ea03daeba46969b6867ef30e9359fd1fe490c55aa85c207b89d29ae752b9aa2ef23c6c648d0eaebba5e63823c9dc44783f30e74ba05c41d8880ecd8cc178685099c86aac1c564da8e970cd6f7e6fa8789b7d60c4bb05bbd439c151ba3851b93800f4520f24e61bc603f22967acf87b766b45b845ca6d5ce57ca6eeeab3c9854a3b3217e6e8ea543755073e5eeaf0687fd2fdd6323f8f86b917244d57842440cfc7974c3aca6aa7d2cc30d89fc972a08a2396b67d1a1458f884b0c44447ab2051723e43bec218ebceac017138dc9345db1a438764e2a5e48fa72daed1d9a08ba7da1060887c247df3bcfa79437df3a6754731ea91fde4b4586c794bc3b850005ccee128ebb55646119f85514e412e21331dac4e49ce01ccd8107c03ae23873c9990f45c8e1a0904bc73bbc4db00f16f618adfb4505e56dc8f004576de3679223a8dc56ddfc90513b3de9bf6af15d39d71c8ac9a816b30e44e9c92e6a7cf250348621285f7c5a45a264f39860006bfaa83a307606c61a88228c0ec08431c5f67491d41b1e3a25c67e2413a0888b10f3566dfad936b0e1761649d83c3cba6ba298c24416bcaada522c4340c37e543ce2a33a550872aa884e294767c6be3b494ef207f4f3f57e3e40d05854965ac3f2916a03d34074fb5524a9f5b18c30b29f83c23f2ea70f83b151560913dc9c2b465713df9c3d4d5a8239de421cdedd139f9a0c05906422965e6eecbb5f39a0d0b2b19503ee70c7a2ae871a65c4c1c4b90bf7fa50616792fde022add6b017b15590db154a95d19a5a4590e30589ff58008c5ed1cf3c808d4adcb530ca1f5c9f74fb68ad80cac278cd82c420e765b4280643ac79519b274ce820a33ee2bccc6ea73dc4fd1d27818372811d4affe988843a83636d0e87bc9a9c9fce4e8faf22d2b16e2c44fbc72d0bea2028aafa2dc42e98048e11d0e61150f6fa5c7b6a82a28023a649d98afbe07f3cd450bf96f36760b5f04ec9335a024428aa0d35fef0718d8778ba49093791693b70f4f76ab270cca840fd8b44d1bdbe97a35cab0a7591228320a0dea03c73af32551415b14e8c70c8ac82628d4543731358c9a312ffef468fecc320d10921fdf8176a96dcb4629dabbbe4ada5011312613fc43fe19d590adf31933996b9aa1b18516a93b5552cc983c7260529fee8c31784f867d4e65027e61c09e8fc53c4cc0ad0c26187509362f8103aad6026f6cdd2b9f01a13136b0b8cdac42b243325094aa54c15e4484a3aac3d00325352fdfba8e8c9220af078a5fce64f4fa089227414b63c6ff85dbfa7419885e05fdf171e1d3fffb2bb2994bc48a9784c48f7ff8e04878fe15699bdb533e71a7d88868c29f3746ec6bec332214aae3c00b1fc834952f2bc2279804f9d2889ad62637750e70f96e73a2984318bc1edbee232d79e486528f1725e856739c62a070ba8e233ae3091bb8c1df875467b3917357cc90623adb944ef11ef14e436468dfa6ed9cd19a2ed406d25817737b903ace3b96d2e5f5ae3eba278071778adfb356470cfd6faf449846b8037b1de1dfbf4d1b0e6d18623e0aff60a913656172d7f642a6ba01dfdd95e206c5157c24b49e854d0d9e6b2623e3f672272fa0c036e26ca8128e29db3593734ba0fbca696f5e58b01ffe6011305eb77d832d6a22cecc5a295c871886dcc959a666c69ee6e5bba4aa06ce81a7da3e6d14ca013afa751204769e2f71d86f3ff413b899aaa3e5427bf3397abd089b6f7eecec97a0bab972de67be00c5755469472fc8f9a2a092dab91a18c9e48058aab28c65277354878e8301c239701a910f59f1c119f085c2290dcc9c4e5ddc612e29c10a8e64a40aaaf0f7bec649e3a4fad6c9fdf7b069fbb9a6e46f5ad08be6ca39afe14430d1ac2d0c8b802317393cb257905bebce626579cd5fc7b9157d82df76af9fa88e4a81ba4d89253904dc435857884d6951cf47a21a5ff7c112a3edb03f7a5c9029752b8f6bdb859ebe1a44e595b6047a272ed43f49e938a70d8040741c0ab437b239193000778c46308604d533604964962848d91e16f0a26e5703d83ae5c273118273ceb46433fd34a86901b05b74c66373f47fe4c1bee67466b636bae057cfed1a7226b5ea3eac776c22fe49a97028fa2a21648d937715d9a02a063912e4bc8e92930e65cba0911239e2f3e4ff332cec00cbcb263b780c385d5bffbb3d82fa639f973d298f101dfbbbfabcac658e8b58ceba5d8c1c95645ec8490119229bbf2d032327c7809a0a27bf86dceb0255a866f09487fbaec9e80e048327103e09aea1bf67efecd9bad8188b96a0f9a3ecf7f8c9b5978712e6e1d32dc2c62907c3c68231a2bf433a0d9672df068e44c1406328cb70ec72c3c803b5683e59470101233cfa8f018c478677c3eb630d96f8f28e755b45a43b365e07735908094794fa41a7fbdd906995f4a7acff0268f649154cb022db18ca61602afdcac61575a91a8ed5c05604fc3a81864fd3c2353198108461d5ce0a6f483fe7a108b073765927e55d6989ad85b5089dc8952a6eec862c60f3c5d57a07d1c90521c4f85213e467b2ca24b0be271272a3c02f7b326948e157525e8d2b091f93a6d7b2123af043803620e674e395020e0399ca3c11d48e5808f5ba78c9e0308eecc4b7e32e26694c3438c9e191cd6e4279e27a04ac4f1d4858977a27470920b7070aa10e04726320d83b0de33626c9cfe6236a9588fb1694489d44be59e462191320c1a832e95e58a07366e616ed532daa6f40c16bdc5ccb0545d177dc7f2c9778bbc9988aa1f38feef58f6a9addfb15584b7a33b1a60f1daf68dfb80af1a595d3af92f8fa28ae954234b87283f536708ef2a42b5fce5ef8d4312119cc5cb27d91f19cef2b11dbe26a198fa8aa79d3c6f25f1c73ab606dbb5607868d3d9ec1b04079cc8705a2549faa790ab4d7406a84b85e29a9ccd6f91cb65a8f4d57c0312ee134dc301eff04099d45d431e195a5e59acb4984c0ccb82436625b6dc014e934ea473065f9e695b0e71256feeb5087d5638cf6b720f01becb7ba1792bd168c7012bdce04c68c3e83cae1f27790e5075fa9151dc2f3e81f2b5bc294e4dcbd5ce596c31bd8833472c9c9c6d556ac5906489779873bd317da0c298e01904d6c574925520fc3af2ea884784ebbc93735b54de8b69528389dcf4449df745ba93a1ce857ed46437b6a67a528cca0ca1d35755ba92c9b8c6443bd21aa6f4755f0e7ee4d33fb58d27960fe1eaed169ed4ff514047ee8699b3b39350945a2acd2f97d77ce8a14128f1b9875c09cf9104f7e129dedbe526a24da632c0dd47e3ece01a5f430b51c116612d6ff0569d8c8af1308c5c52353d9bb34e94e838494897e6d7dfbd3ff6d6f9f88facebf7894dc68afa11f247563ff789a190e641cd62f2968f5364ed0b898e3808b33a53306664c742f30844a4cdb7cbd9c46b4979060dcc94b090e2b3c8b4c45bf1cc20f93e23eaa1eeef8744f32f777a3d3fe8ac5c4e1766a474c5afec55bab39888fae0923b67791082621fe0fa61378db13a36365c3fceb4b15a9d17d4bacdc84273d084a2ec220f5f33c8cee0501b07399e1ea5732af6504363903dcffa6c598b66d478ce68559c6a525438fb825214b9cf53a42c1e0b2c7ef1280b2e9d082a7901ef67db7a88cf588af0d4c64b1e1c48a38c58ae82288f848e192e98029489923418e060a555ec3352c80133b5d3e9d69905fa6795e07ecdf2c5094a6e6c5e6c66974749ca9a0c6c4cb958b86bf8559e9b47b466040ca06d190b43e1344c3d65715864aab2410a8a7eb2e1706ffca217fc212bd870c8c1f5f10d1a78540f5a44c8ded5f6f9ff4e504b6ae13498bcf799a936fcb18a87cb78aedab8be2ce0de587fa89b2c9786216f6703d8f87c789499feca24eb4fe35194aff016919bbca91552d206b2d30d7d8b9f1df6c320a00eed4409025a7f9a5d8b805d549d583c91d56c21bc548f6361ce9d9a2069f5776c0420ee9146e305bb7b3a9c06de17e80fd16103140bee39a5e9a58128885b5b7e3ebf21d76f3e994544c886c97ef0123bc23e8d443f83fc6c61fc122aee40858ee44e75905029c602472e8122331709ebe539188f49e3ef28a32ca55853db68091fca0b672bca2c3c17c9842e278e9d272854ba12eef4427f50ecd36cb9b4acf1dae5713a1b784a249537c0b71dc1ec997ccb687c6d59002692a52cb3650ced79abbe250747c3bb3c4acbf4ec4767957da2266180c3d9786dc421ae403718e7fcf7568a014e74f16bcaf53d7cb5bdfe17b13d7060fcb72373512a17ce30a1d92e21823f89ef07f00a6c6e9283a813cfd9c3c9560eda0224d896af8102591295a39a86c06944c22f89eeedf78f5e391e03729786824a9e3ae1e19e4e1b08ef386a0d0e743a7f0bbe87092d2fff14d4f1896ad09248bd81ca662fdea35fe02f4c1c81de27ccf816cd39438bb60bcda3a0039411767d7f5484ef9445d9db7ebd186cb862a1e5ed34af359441488829ded939a058e5bbb52425d77ba6987a85a7a96898432ad063082d8e5da5ac79ec83fd84dea919ee396fa0258354f9b3e90c477e312e0b10f7baa0bacc21590caa0da65a54f8a269e43c48470d9e9c6b9989669abf32737ec12999005be003b59295dce0cb0694337fac5f49e0166717b8104360ce2a1d8bd8c8c630a134645a4586b5db2b80d34dc2bdb7d71ed6703b0bd8bec089c3a23d77c3ae5f72d35811880154362f0245386a08f2eb8fa320ea34303b21863e073ca46ab0c4390ad1b8d2a24bb335755da0cfb5c9ea121812294bdb0f675c6c295a815474f351d2e1ee7f1e441417c266e2dd918326fefe8acbec9034242c9bdc2ae202ad788dd964c51450395c06b63197b0d6be6590505883d397bc3692be83fac80edf09f94bb97933afc6ce5cabfa7df65f58f907dc42ad61f80235a540c3718b3544dbbf885365944569dce3163d546d72bffa681d04e55571c2f8c44450972b8e80315ad1f171cfd15708a5cd97c90d73803294916b1e28f43d442bad894708d1f075546fb631d599a3fc5d452344c38dcecef7873e24b4666966a6ef5d976134d7808f3582f0fb4eaa1d1522fb4b680a62df8b737d480e93cb8b1c36074502654b7d6a3ba55aa2358c60163a6e39d0cc32e89ae3404c1ba96bf759b4948cb31a7397fc3f7950f1552f63d154d406f10bc255b9f87bb97895a88c592fd71ee3c404340d2299c368a08c4d1275ab3ad05d6c4373bd470a5a725325ac483112e7d7d0c5b8862f92fa853dce53f32052c289b65da16722202a36e40c7a4bf69806244ec53d2da92d4d0904911eb8adf4494ce5674bfeec0193bcd18ddc7c6e175d73d3ff6060487ff2024b139ad5440fa1aaf3f85f9683dbc919167f2bfc1c67b31dce02ec2cf34d96276e8fadd9f01cbb02619b88c9fd031b68e43ea0fa3e09fb9848bbd9816ef59fc4c8ce76138038e43fec617bb3be6a08ba6ca0133ecb925b261ee4a3436aa813d609516424e5552c97344896bfceafe2f2765a574149c7ef5c456ca61e9eebd16998a4b23b513992aa21744503aa5fa3b8a7950d0165090549b3042421360895574268f595ab610dc6084b04240989340b3351b4da016155122c3cd0a3880a456bc4d48ab487d6ed9d870e23e8693e55ef22efeb9262af8849b58a7cae11d31da18807abc557d663b5be41d33d9b531a77015619af976ac41f3c8ccc596302acd36322bb683a2981f11cbf9a3a974d48958232277ac4340b136e3541b4929899564bf5d7e306df5efb2891c404f1fc18741234d850c970ceaf951d54dcd0781815b4fb79392416fc95937060b335f14472dbb8b825661bd417cb39f9b2afb12880dbb9da948c28bda2f2e8834562a2b7ff929854b3e12afedcf71d747b222d32a49899b74b2a0ebbb96d997481abde8b30a7a8e9958cd1feb90b809e86818065b7e68739af4c04506f3df8d18917f8818eb685b3b0411e9c84d936ed0cb5246ce37b58aab013dcc1323c8ee00d328238c8a4f1532b58efc734319ed39cd0aa451c3880923069e55d5259d640327a76edc72404e2ef91dc52242cdbcc523c1a091bf4b3d9ff51b94aaffe5c1ea75c7d83540d9569b751c4ddd292ce774c2ffddc9776f47ca734d2f77ed1622a90d0cc54200a1adfd4bdc12f99a2f5f9c9c1219c9acc745fa26538fbe75cc95d215180731c4c5a5d2e26c2df3ef15681ed8c985dba20a59b9c4aa1a9ea6e655751733f39b42a68f742152bc3302f92f82ed83545ce923d50e07427e811a2578ad51ac4d3c5d6a1c2aeff88f2d7d82997e1570f68fcaa6e282dd204ca8d48362027cf4d2aea62ef07848b26488318a33ad295c4417d19140588211ed372c4c779c450f47991f4f20209a6e02350d20f4b5d7bb7d8d87b07f9ab2f4f58faa2f5491c6a9417cb64930d99807f67d605bea332910e5d2b08569540169d68ec47d6a4a6a17d41d23210bd4efae5bc0b02b731a59765e1c60cd3765b582353731892ad175815592f3ec72edf1eec97d447c6b8ee4c4b9403d146b1ce2efd8437e9d3c598dea18e8dd02c5f69814fe830d24a0463609684ec4ff6856e9dd41719f2a6c6509cbf4b6822cceaceb2a004ea9f625c2f812ca851dd04ccd5a3be818dd47a0337b8075c90606aac27d7a3591ea282fd6ba95dd9275365f229fd9ac5fa8ff4e5fcd610a1df63597aa2207dea09dcdf36e6f00b9db54b3f6822417fd08b5d4504acf6893766d8c56e65f1caf5baea3e1893bee37ae56dd58b8a248a6e409ed18764664c5b50c67f3c342e91f21b1e12a90b5241c46757ea40ea6782c72fc76666032a9b48074f021bcb1d952c416a5d71347d83b3c88499ac4138308b36f29a4c5d8334e19fc06471a2267d02b788f88f24fa4b91b87b4626397ea46ac3d2ba84a2af57eadd592c431f4c63860bc161640150c94b1f108cb2eea5f9888fd463a22381ba9e736b641e23988a37ea6c89d45620cd3b5d7424d0ebc91f54c91c633815f694d99214adc0016f4ecf3e2517a8f8e55bb3dd13118641fe9f0ddc361c566c40ec8d4285e5e6866f3271c506a0e0966c9ea68a4280c78241b05487d6f21c5ffa2ad8391dab1f6933ccc21293f425210e991bae1df5ed7b7dbcd6e7934484f57111bcd28cf47d9f2d4483f6c384984fc23936c63576bd9ad8b80bb22a1213f375db55a711a3dacce3d5905c8c913880aebd3874e7d12a97b80c98a5fbad154c66c710e5f4d9bd6aa8f1e0d6baf2218e2cd1b1de061ceb55eadbfbafdff4d3d448f03743ac758a19f4326fc01db927d0f5466341334b8e2ccc6e92247a1290a85f847c6733bad9f46e15251a61ac25f9196074b057e4e53494f9bf463a3f8b07d18fa46c549a31359d8a3334ff4d13a63636193a2a96c714c1650b06709ccb24bec60793f9123b4ab24c4ab6fa996585c0e590e13c5bcba5f795af6383f6052dadc189f0a4b52cfd13c5453190166a0b6989cb39fb2fba247f71e9a09d5443be3083291ad594fd6fc17e0449e6880b35d8fe6784f1ef09a1b4577214666765c2a20df96932a48ae0f91d8fb23afddc64f9afdfc89172628e5deb8cffb7a0fa38da90ba1211d59185924317a9703b5b9fae55734ec3ea43978028fc3797033909178c46dc2b515aba7ed58b5e59ad53807df8943b2b95a269af958861ab6d286235a2348c41eb61bff222c9ebd37096cf98cc3c8528daea9948b32a94e04f74344a2f1b9940b8bb80cdea62bbc2acafff32c10f623b411a8396cd8bb800b02a141e9423103a0bd373d93dc1f44c4105890f84794da95afd2101a1b24aa550a31ffdcdd75febace2f52f1672b35b2fb724973fda309edfd4b1945c11c1401091e41f300a9f1d7fff98806e41dc98dfd9e3ab119205594e4d8faf7a28df80bc2825c457130144c750d24ba1e4f469206d2d17eab72e54d23b7937ae878464b0ba798b9429e71202c7fad4974eb83c7e2af034607113859320bc90cc50c617e35d2fa57461cd160e2935c86b9c520c9fa490e6b0d1e675f360522ab9cf2a1eeb26c4dcbcf5b8acf42e8c2ab8bf54afba878bdfd5881807a75f8845a89f97edea749d752e051ba7d2b1341425f1cf58c6857087f89acf6936a63d064983ce453417c39c5cea6ae6f7da0d39684d8f1a86fc44ffbfa9c210b27dda65208df4babdeb77169033d45a7222b1f4c3a9d5a6725cc0235ddd7bdf625028dc70667aace42d1bbb54689d19765d3ab39ac00a5fcbfb82769d250e88263c6a0b3b66a2bba751c662ed88e9a9a6c98dfaa27b7b23bcbc09dad93d5a918f88bd01d6aa32e934a1c727f4eb9dfa20cdee13aac7fa240fd1e7e6cfd56bd5f254d95400d453389622743db2d2a5a287d669534bada5b5c550e13d1176a6f30fd16ae69c56a562c5f9c39139f812d92451b0bf87d02aa874ed0ae1a6bc1ce80be0342bc811d3c0a916a19d933a4940e6b7a46fe1172b290a9fce3e92298a1a1db7736f7f457df5946f76cbd59e97687c1d5af4bfd2ca6f2dc1f345ffe580722996f966d952a9be8a4b5b1437125cb2cafcca5e05d19f4edcc05568bb82f46a453ac84ebcdce660706cec272e703626b202a30086c2ffa6809e5b00405b45701fff53629eca174b3d00142cc6091bcabad4a721192ea0ba541a7fd71063ba5da5bcdf9cfa78a939f3ae419e6554d715ca8dd9654d153ab460953213abd44e48505a36de95a0deeaa0746e8fc7103cc8150e018ade3e2affff0844e8158d0dcb6fa0a7301e06b3e945e599d2420d3b0148f253edc0018dbb641f04c145eb5439ab2a6acba7f5ce45f0f812b8fdd2ddd2cc5bf2466eb17ce1a36db634dfd201d2dd66a648033edd9ad2c3f0b8d8e149f89e4c3c4f0fc62ecf711b1ebec2f6804a3693a6ad850e00274b7bcf484a7a7f1e5c32bd44cabc666201bf57aa571666179e0a3736cecb2e59de55393ac62531ca7098f008404f01a20f6ca5997563647455fc1afc0ae5819aefddcdf4b02a92765331757c6ba074c3c286fa50530ed85dd0cd1cf16f0bdce8f8e00d8e76f151f0d2f009deac13dc0cbf06852850dc474d55bcc1615844119d5345b55c72577ac3168b00f48f1db94f0f3420a38adf95fe3e09ddad9986c3ef8477eb3855a55077831b3a6c05aa4bfb0523f20755607f5aca9a39bf0bb8a23839b77b0d99fc0731dfee7610a854ff4a22f8349506696f103ac4c5ad3b88222d93fd4ab0e8665e2aa46b8632a7a06e1d10a40f761770b3a3833739bc064ed71550dd077b713cbc9903a0db50b9359b037217bc9163b37c0bbc34f9056e76b01edf8a289d9fc01b1cede3a36061660a7bebd82c9f0a093d23a0e72f11fcbf84f2ff957a4fce84d7b298ccb7511fdf0c3f535534542af5cb4e3e4ef38130bf8057d2ecd26b734925ec6da35000dc0b52a9e859381a27318f18f388bf37067a173df29fc1892be4c990a49ec892bd3b537ea90097966e54ec09b21aa40212c2c7e6f179c14c5ac0c5e992ee6e43c40975e40b4e198da4776b647b57f2dc2463d636cafe5d5be5501b9bf3ff985fcaf0852f61d7c621a0afb5fd602973db1ebd8d645e74766a3b7e2b2b13ab7412d54e74cad977dacbe4f43238b88f17f472721796540a3ecce0af53a91c37264d45ad3d1476d51b52a6c2c44064b446b62f25efd63b159525e59b53e88b1c97b299f58dbb0fc432ee427b6a869b4a7d79988d2f4c445845239df8bce1070d8d05b900d94491d7bf3f3b6ab03c55b1abd8a1853cf1971f6bd23c79e999dc21b0ea602e44850e640cbba4932a38622ebb03e83e7428619598b7deb2daa8a89be0591ba3bc9db29eec7029db59d3f83d421dd43d330839f3e206dcc155427416fa079632f7dd27e852779941c899835b512d746a07ef8462d385c68c4ad017ac5fdb933d2e649f351abf67c3d619839fb8db963f1994d25f05a26d4543e9fd3a1b4e78f0e4de56fc7450d4e60a886c270ae5f7d3db6ae04327675be0bf06c3e7de11581dd252ea9ebead051e71746d1b3f5b7034f39e01aa587eed70016ee00a801b70d50935aebdd8a400faf09c2aaa26f2ae9bb293d781272d683a608d47c62e2ead6af169e7b9a602fe87067faa72db1b05a25b631aa5b3b12a64a8eeaf29c308ba2ab54b82b242fac25afb1821dee1acc989c0a4fc924a02f1ffca94f52bb99e64f520b717b9bbe4d28b588897b5e1e40183a017c077ea04c757a140f8d183822e0dc595a4bd6e81bf5e05882b5ff6850ee2a6bcf6f3a581abfa8a484e69cc7afbfdaa04b194975c38c65b70794181dfd5aa30ba6707c13c49c6aa36508cad563c9efa2ac153e99cacb52bed62a3db2d230825a9a5c9fd28db5ef082c6a9585040466143cbcfda992e570e6b6b3213d713b0814613d5770d4922cb8533d1b9c487798df312749cce6e440f0e54b1232f28655086f920f393374a12a546e14e648a7e3ad764a17ef4c147ffbf5ee60dd1730dbbe2a3048a5bbf7a68b886fa2fb3853c49305dfb2632044e45e4267b11072020698ff3935a16681f17cf5ccfe97a9b743162c5feb9bb229e6569fb1a92ea1f5765548e9b4b89237be48205e50a284923ad84ef94190bdac2fbcd69d0a0bc923f3fad3940d69e0947737286e23820202b8ab05dc7ca4934fa3e023ab02204de222c2a231cf7ae0c52920624a96033cb99cb55f9ada9d92c5ea31edae468fc2b722fa15b2e5cbc2ce342f50a7a70e5f7bb8c678e4d8332024b3c32a15b577d8578ad03150b9fc0c99628daa7c095afae2162e943646929a4743d1cced4066f6835ff23d85d3166b1846f0a6e461e8127fcefb676108766887aab02c4ccd882d5c6254b23b6928008e2ec9d1f81668ad8b09ee424c300ca075040214aa60d1f975a03a7fcbc0fc99944a505433895f23e88574bc06d34300d5f3fe7187cdf637a3b535d2568c5da32700a88175d8b7f2dcfe637239154680646e293feb5db7049184bf68112f2d891bff1ab8f79ee0f17c922501badc4ddd49aba38ea29c2daee186ecc06a73c4d26db02344e039a84b6865d301416710ec9796e67a89fa396782ec6d1fa3cdae7d1f6bc79703b52cd69fcb291967d173c35392f8775c4932f1d64d62cb6708fcf41fcb900a1d7079478108be53afc56330691d0d1894f36d0ef3efa59d35d51b4b2dd448894524a198d0851080a082252fc29e18c5144304aa887837e8a4b88a90881206212d5bc9fb68c64080b71421f75f5b252be7cc1817a76998aa55e90f08e85e462788576d51863b78c9ae024b84b5076cdefc3e4d8bfbbc59357ea904a96d2e374fe4221100882362c3739b3e0b0b8f6890857b3b95361db749debb6c4a1ebfb3f1abb05932ba7d393c81bd2bfcbb73c87fab9194a65a1a054f3855b2914aff413b565baa2aeb46cdff41495b59be8d24bbf34093e516bdc7721c126aa0af75b485048bd719f8504672058a3652e4e103c52572e5a57432a4159ad71ff658b3048bf614e6844e7a1082acdfdd0476541945a1a95663f8e4f8db98f82f2a3a37e2eabe4a39a62177575dfa502466d5663eea750dc128fd4d515b44ea4aeee8b42aa7987f45b2ff533852e49a14952e84c7622439c4c86e8358184b0104797a2eca5093f4199c7bdba7d4e02a9d7827c8274fb2b25164214033c929f0d1b210054b061e3c60dd0065b1b7abb2b8d4caaf35294e988ff8204d47322774fad61aae1956806ead496890bd36ddff6adc4394a949a94d444578866a81f1b37582cbc710f0bb4d1119f05eac832bdbe0c70972c282570700b8481b06dbb2c13ed6e215496698a6a9a6ab805ba4433dc02f5602e6a4b5483babae19628c52b30bd2cd3cf9541659940d7a9d24f535f296bfd86e559e2a2f2e4a2198b0bf5835bfb66516409872ca12597782373d0819ba628dd74da65953a504dd0855ba21675f50302d145bfef126935e63e488a3211d4835b6291baaac14c94e29628a4aea0883b3f97252e51cdfb57b47191a4a78b47fa05e19f7e86b25004a18f4ae3be890c53bcba5f22c3180e7f84e80db7c2170e61ac17257017264c5c6c97e561237e00c1373fa2e09b27519c3c61729d38e91775a6d07e3f855e1690d42bd5026dbc3285cea450d02695022285a680c85d4e01e18d40186ee57b82314c8254d898f47ad759dbbbae9e6b0da943fa1089c565029c1af3835b26199ef5fbe7899b8148dfbf8bee78a7b991e9fb0b287b016dc0212ad0268582b2149a42595ce60c14503881c5c51203da803188e06788e3eaf7c3d765e1fc08d110c8659d3c70da3528ab34b59ba92f047ab254e158ed1e566e06947c39f07c4dfe2129a7b035a49c4041417dc1e59c8340440e82308220dc8420c6450aee45696902ffba48ad80c91296f400094ba618b2048a16db9219fc58c282254208f132043e0aa34508bebd00724b25e12324a92614f981ef26504b6c31d8c29684b60d41821165b381a0c7e6431425605c16c7450872ceb945a684c9164aac5042fb6184ba42c9109a122556a024055828c1818283509af0951b1259609193c59127b2e0e1c942c89bff6266a1c3930510287f04f7bc4821818f94f8e0811452a0c000821230008209522c00c20f66e641e2e45f78b080d2bf5e7980e0817ebe8ccace6dc2e807114678c1143d6e508af8008a1246261768a002248c647103286e6a7412ba7e6eca359a874c3779239e266a3a6882d64ffeafd1ce6887e79c73d6dfa3cb0c549adbad0dd7bc37f0b866066ab8e6fdafe69c6bd0ba9491e25caf69830edb6b88826b1c41ee9b956707affe9755517d89a745b75d74307ae74cf0418105133d44b62062ab818228823ca149e1811eb798283c554049a180810d4d0109750746c47e6a68a2a8a9cf2d0bb5abf0ba0299acc79021af0fbc8137f006dec01b78bbf7de5a036fdc7ebdb7d65aa1c881b7107e7abdd65c73ada587c578734282fd516b504eec7c84e3d187203e1fddf72879e8d1c39354f41972d7755dd7755dd7755c596b8daecb39e79c73e672ce39e79cb9aeebbaaeeb3add755dd7755d97bbaeebbaaeeb9e870e775dd7755dd7658ee4776577119fdd6f537e787dfbf7455fffab5b93e7973f606bc20f6d6620953e907e871e6a48ff71200cfa8da4fca8f2e0d76b82e5f9d2f58fe0733faabc4af9e60896e635adc601f4677e0be0aea301d2cf402a7dd88187309e50c2c517509810460de97bf450432a7b8c3e2c4b1c40afbf0b9138d8306adabbf24547f42392075be3430da30687a54654fa207d0f1e7af4e0e527552aba7d12d983879827869004145c1c39a2a726fc1e74aef0c0125c50440b3ac0831a1289f4025cd61a432a7fe0b8e5a354d363071fa59a1e3c90c2300cc3300c476118866118fe07666ac2b2c7068a383104134130c209d29ad1af7c0f1e7a5ce04624481727b8010b9450133e0fa4b287e86f6e6e70666a48ef036e82ab06c7eca2b85c3538a61ad2fba0f2e372b96a70bc1ad2dfdcf8e0e2e3d590de07176ca61ad2dff850c398b95c1f306b48ff81991a52d923fc951f4190c256041122f0d4dc1f912b24af2bdb43ce8d0e6e8e30124508b59a3b8413558c20084292f8a8394d5de58a96a00dc119f2bae0f77925ad498fc5c901afa65784d71cdee196ad0991df83825c33f43807c6a1232bc93d8b410fdafcb2422c50c82bc26be73e07f672c876075f7d6102fde99587895b95a2571e2660dd82369e0ff8cd39e75a6bae357b1e13ae5e796ce063ff8ea0571e99ad7f15b4f17af8b5f62de84b41997879d6072b8f97fe71159c9282b1a9d794514a4a0ac93fe5b9a8d630ccfa2bb949153285445d937c31614819d2068e6b5aef096e7b1ecb171b5a4f0736e79c6df0ddefb9f75a7c7bbe165b7c6fb5768acf4be25dc16bc846b15178984c1b250a7effb44feadfb2076b2d91af7e597c40e0f943bf5005b25f86d740db86efbd1fbfddfbbf6ff3fddff7d90cdae472ccb9e4f706b43f33be3f83217004c107c7ec7df67ebff7a25d47d7bc5db6a4e83f4bd321294af9d3eb96669fd81f95d76f9f5c5656798b9ef64990cbc225ef3e4cda6e6f4eb48d0d72cdef73604e863e07562173c83d8534bb1a5ef84ccef3dea5794d0deed2a241f8697f5e08edbc627f3ffe8a4b9d15f0735c0e04962308f4a095726565eb9feddfe10520acffc324ea9ada944203defd69aac1bce62e9f06d6a2f534c16deb0b35b42cba4b348ab6a87d8be6bb51c0df02b140427a5bf4a222273eef073c94a53937c1f1e78c1f0401e7463b7efddea99aed9d8e8ba097a581bc2e0b96b36397955ed678fff7ce6579b22197c57359dea66d231bc9658db944cd3a8ae79a246a086ae79af83b122543a5a08eae933272b8eda76a76a252d485543773f49a13508089496165ce90118306ab35f3a25aa1a46101470b64005cc8d16bb2e63c50e8dba0cca79a75ac9a5d96eef8553c68c7afa25d96a9e357f9a8a2dc164d4d65e4b22a4f14381da35458d8191116f453f5f91089413f554f543aa352d4ce65e11f02c4f888ae4d828bafbea4fcb85adfbbbd5cb1c5f6d46b95a1027e7aaf6edfbb32e860bb282d4b0fd6ad2848e7e2848e2a8d8bbb01d9b40ffc550751ab5641aff5ac3c1d7765a85663f0115a4b053787ac69315ac0ef2825e17057080c1659a5c95f8d84c322048c4ec2f94a16598dc1cf5d1de3e72e8ec36b1cc928ad3425d04f8dc1e449d3340d06337c3ad6dfd2e2ba3204bb5ce99d9c33b99bd4717582414542b08e6f0a1545faa992034ba1c2e4aa31415654725662b4805f54ff2c0843aa97688dc11f23097e9a92cc78b1a4e5ed8a254c493a3e59525bce78bd18e1f7cf94ade34fd92acda8256dd9b18fbf44edf7b3bc75fce5cf2e6f2a173fbd8e576a1d7faacff0d145befa9261be2ec70b69c718f348f1ea1863de7176cd3a4c0a5bef388cbfe8f892b59ab67cb926d6f1fe71cb2b7905f94599c0ffd2c54909261c0380917961061af02fa0664f061ac38bdd2f7739cfca00829f1a064bf54c460f7e6a184d1bb92ccce5a43b323ce2fb4658d3df5069be3eeaaf4c32add2943a7e9d9970958b9f5cd7b85069829f2f3d95a645ef689cae87b46c53f43075cebd79dbf432849f2fddd46db63f53696ebfffc2dd5bcfe4d681c73dc04fd334b91f19af3462bf3f81faa4f3a8b51e85fba98d2070a5804583141f51942087d7c1ebf6c9fbf6ce60569a53d72f9303b7405adff2ab82ef0741ff95230e9523e6f487be1af04a7b1f19226b63ed19ebf734fcf77d8a4c68e633fff19fe626eb48c3fed2c32b1a42e7cc83bc5206afeaabdcab5fd0324381a0b4efa37d1faa25f5a30b687fa53fb198ba2ea6338ee2eb9adf78a285b01809566b0401853924888242ec9ead04848148dc9a808c807e9356e42382854084382031e79a222c8485b01016a62c5fcbd7b212964699993d0ec52b1cbc729d6e371d3869515730595a6426a4ae7696487244d6ef0b164bf10a8cf1580b19e69048241710a9d3d2f2c2ab103682be51e974e4543b15a1cd64e20b26c6c4244e3430166b496b0c0caf46b61acd8b93910ea2e12f24be64b1ef1bd3effb4a1017829d66a010977522b295882a00d1c614446b496b8df1556e1f5193130f0802f105dab09c7b238491d20f44ea9064f80a8b808538a108443ea22f766d57b17bb60f38048882b09eed03c64872dc4943225a44f82ad2ef8722381199c95217527c89b02fb8a882317501913a2e2e20da888e3835667c8d3f461fd5bc3fc24e444eb393ec34a4df3f89b3ea23cac4b48514632211e2eb8b9075898d631ac2be128c8140692ccc39228cc1f6298a9cc6be07913a2854f9e4ffeed93f808de9fe62fb887c445ec06025092a516bdc2749f005558524ea8dfb2309a678250367202dd602d1c421e28e18a473b1089188ca42886369549aefc7d7e8fa8111fdb24622aa39766044471fb65a8db93fa6b8755a52575ff4f423757559a722222f4e464e3f63dac5986a31a635f125c2ae99134b65331a5e9d68b1d847b3d1686108fafd96948d10001fedfb4eb431155fb44dab3544307088da0a810051dc0a717a7c7a7a897398482412d50006b3f1d1be3284bd700b4451f155bb2cd1ad8aca123511f9e0d6f681e116881602515b5bc9ee095f61112b221f1f910f882682d144b05204838daf1136a2a71f51931188e876596310db07b74e608c383b8cb103dbe7d453634eb4d3ec944449ad5fdc3a15a9ab224e3cfdfe894835efdf938f929316b4d3927e5fa41521125169884688303126a6a24c9ce195f86ab969718de9988ee9988ee998b6fc18d37e7f4cc79d5136ee8ce9988e3ba31263aa44eef2a804fafa40df07cab597b8be45d204227548659813c2742c16644cc1d8988e69088b85b0302726befa7d3116135fa211224ccc89b5a4f4b0727528c1a889eb7a180803734058bf6f433874413d211caa5fb197f6faa1186ed5b11ad1eb8788a82cd33561b875fafaa157a5117d254147200032d2eb838a54d60da05aa5e15f1f44ab34e1d7ea53515f5fcf706bfcdaf25527515b3943c93fd7bc4068970b8c5e3f77515bda85b27c7d0dc3add4d795af3e4adcf89224476a4bbf5afa89baba7f665aaf9f89d496969d7948af9f855496a65db3566952b2acd2e8afbad66f5ef0854eb3e780830e5ee74a5f7f06b74c0fa281c4afbbeb44d2a809eec12cd6616181a2b5b6250971aef981363a2c208b8b44b96a8d217775c96bc9141711848148986ea626a81f5418a21a88d2dabe81b0b8abef416f4d35d1ccc485e986436eb441e2166d1532d54dd81369e58b09585cb8e5a125d22c812a2aa08d0eeab44fb5d260528756429e7add1bf73d337b9c4bfcc12bd1a7477c227691a4ae44b3184c489a445de5741554b35684480c86835729291494a5488e22596eb8457d2412c94b6dd3ee1fa9834201a1040e5e81682c2e178bfd5ac0fe917bf4f510b981571f591a0b71d0578d096144d4d57dd026ffa77a588a3224ae476b0c8a57a3da4cc6cb531cd2bf245c8977c882324ef35e2edb05b40971bf474d706e97a01e2e042271438501ea49a1e4a3b506892ce1e0127485253884a83c653a0805726f843821ce0f3f595c28f4b2f287368f7dffa08dfdb74f823a72cf9814b9b8bce9b24cb76b9a9a54f3be687603042a0b05256fdb16c2be812e500784812978035da00c447349a1d7546142e8b1882cb99adc84a428999ec660214e885ec1c320ca3d7282a7489dd48752a09e10b74baebd761abf02ece35c7237daa66ddaa66db987bbe9f8ed65e9ac9f5c96e65cdccd6579383f3c232e2be7b2469bd36b722030dd28d84319077ac16bf7ba0f67a14cc6f3df7cea92bb15bd2f5d684f7ba10deaad41adf73662bfae89a26a46801ee881db886b5e12dc1b4575540d09aa078bcbd233ad7f03d940f6ab33a14c2fff1d957a1c28a72922e02b9fe2839fa5d8bd3704a14c25840ab5714d5c2b03d05a841deb7804dc7edec17534e01a279730bbc64f545a6952422a21d187ae6a8a8e3f8fa4e03b4219064d53ebe87ad638350695c184a8ac5ad1fb3826db4d70af949981cc0a796ecf074c735083bcd411c1b01cc1af5d87f46dd007daa0947204953aa32f0d214b3b6429c83549de08d536a55495826eab31f84bb1d90e5838bbac133afe90e7b6ca50a69a6d5435d359ab985c567773a27a7259dd4f94979bcb1a73b64106ca84d39c266db824dfe886ed3467bd63d4aa0ba881f6f202e4b2c65cc271cd991772c860013fb5cd9471043f55332e034efad19fa19edba81c1150e280287e0f35f6b9cf0ea4724400e94d6f2a39307ad2d79ad15b70c7080162c96f25ef19913eddfec7711dff715dd9cece10fee3ea3a6b43da26c313e160118a5ba357cd78ba15cd5efc8a66b46e5fdb6c221cbcb22f964468b72fa264276ad66740a578a5843321f5d53afff8403e512871a3f3dde865dd570cba679d4f3ec3adfadd7b2f3704c5a3b98c5548543da9ac44edcc88d496b6d54e08b595ebea7e938efffb4c54d7c58e043f51294a268255f05335d361560ae848f55ceaf0c07d59a201465ff1ca82363252ae9fe33c7963f00007187dd5585badb5bea894f302de35cdaf200574985d87d9f39792a2e2a936bc29f7c56b2d9943bea08e1ebacee8732932a29ae1d6a8a4a9a8d83d3ac277a0521cda409b92c93531560da142553ab514a54a553bb7c57317fd7ef6a934f9ef97622693fffd19dc1a7de923febda75f824adf86037e6a5bf97359311d7f19e5b654466efa25fd0edc1a952a9ebbc23fea0197a1ce3a5922b50daff0e8491f136b390fd4ce5d0d21519acc252a3dcdced2d6f1d5d7ec2a1ed54c45134936ce3f93898504da7839ae0ce1ab7c8e7b411d292f7afe211d5c46daddbbb9a51176b08230bcc59787a9dfab9a5dd334034e7ea2f0891007e10e422a62a1527a592974fca59ddb527dd7aa662223d7b4b670441277bdaceea5c64368a5114129f8a99a699c7eaa66232bf8667259a3000670cdfddab659a529755a57d56898c1ebdf688bb0a834a025ba1559166b6b12e2d6b1e75846482ac8721a4fe388fa50bbe216575331c6da36c3aa99adc53369349ec99a0b6a65954e520839eb24ade48252ec96189f3a765fb014a9b2037e6e14955ed618425f74909798e15369ee9b5aa14cb57b29f6eaa7a983d92bcf90b66fc9ed16971d32c2eddf7b2f396697ffc81da282db1fe5af55f06c5fe47875f3d7ba7dcd411b6e5ff60b68c35f472e3df04d5aa5d13544440da2fac8200c1d4eef4e97d3d4b9f6d133c903dfb44d33b966680a6e9feb9b595e548a4a513bd7ecb744a545386e898f3d5eaa78ca8dde8bdab92db154d1ee9091aa87a7f318f0d193fe0cf5747bc3add2e3d73613598ae156a9c4abcb5ac9e155db56a0706f446a195e95231e8036aa598dc1ffad7cae89574f463c8851fa5cb37c724dfc556076868ee077bf2565c8006df67fa609daec5f7d279ca09aa9662a9e14686c1919d026e5bfafd7078de020a757235244e92af46ae40647e837f60dd026456f4ed5a3aaa9681f685b500717e98fd3bad39dd65f17fabeaffbbe50a74272cd1ed58cfcc1adde20d0f3f406819eca6f10e889b8ca732a292c1668f3d50fb42ba803b4575801b4f9be9eba0563b860141cf43a4e8340afe374e8bd8ed3a2170fad435fab05da74ffa96c0bea08bd8c23785a0011ec9af64541c81f4859a5d9d9b4c39602c2d0f55dea1db8a57dd495be0fdef7ae7e1079baf4b3f69c9e908eff23cb53a7215087d973e9e3dce8066d442c8881037c72250f9d76efbd0de6e774373ab242080a6e9ff4e28f30a47aeea3d75f3146a5189562de6f05f3986732da866d24148d340af7e52e879aa16aa8192a45a5a81d940cc583a2a1765043503214cf3569a834f42f02ef8378e81bb150c38763d7cb32778c6afcdc6828c39ec7b203d5f7fb432a20f855a80a071dcf2a4da8ab665515456d953eb5f4f1409b4b727510f892049f8e4548b8066abdfb941ff75eee7236aa9a8d2355b56b62d50c54cdb08a87a642a2ea0149158fcac835552a24aa1e151904cf3c745afa943f9be4e4984b9f7b962cecd10e38efc1ad9186d18f08288df74b6ffad2d79a51c97b461a483ffa51b9c388801107c65beab0fce8c51f7dad2195f59627b775fb2289329128161295de1b585413f574fb2212b951728cc192a30cd6d56fb95ffd763e053f37bad1fd42a59e673295626f9aa598698f74c06fe5be5e96a8d7cbe2444644b56e4548ba35c24296d27b03bfa9143bc0ad29c5bce0229828e7b2f487b26dc46da950bcc2286e893948c7391dbfb6917e54a2642002465fd2b65369482767240f8a5ba5d85de1c7fcaad052cc00ba462c512313aeffdc68ed1155c14f6dbb8ff3630edde86ff46e58948ef1e7f413350445c99654166a89caa4b2503ba8740b3edeebe1a89f5b8021d567e46ef6faa71fb86ae6ca656f895f4725256a4b2cf70baff093780dfaa82c3dd1f19f409b3da282f78460338fd6edd7baed09c0ae3d1d2fe0a2d7c4badc418ac066d2cc1a6e914a31266acf328eeb24d2a4795f1f19e1e7463789346bb1507659b59af9f1874ce452ac635d9a341df0ee4fd56ce79a18bd2c1c027e6e74a33a5afa0c9f233c1ec71e365dcbb5f03caf6302c694a93497ab71dc0c34b87e2cc0c90e9f1f7b93f54bc60095a6fb6a2a42c4fc1ab5253303af9d95480f4ff4fb33d46ab5cc955f63c8ce00cf93a934b7ef21386c7830d8e49735f3525bb57e5dd91b44d70194c1ebb93c67d8d906f66e3c0414c6e4384e731a87b5dcefca915e35ab79f2e899ef9ebd9c778edd78e0636faccbc26a58dc169e05b1436c0cdb59c73aa3b6171023befda1afbf49af9a3837ff67646404b063065b7158b7bcfde850830d3eda04215b8846ab6111a29da19e90911092217a0e3697668d095dc1cfd02c340bf1d0e0900ddbccd98c4c97a92b3b53571677669fe93860c0864ccf250e3b7cc76102cff9effc4408074ac8e5c3049bd8c42636abcc016a8d8957a217e880e62b3c2f7b9ef674e6b6cdf64195c6d6017750c8021d2883a540bfb900b9e78a6fc529dee7b85e2e3dd005fa3561604c10888b2dd136ae2d8c0565e07e80755dde7b73ce45f09591b151030f1ca49891995a0f1352ecb24e8f945ed6499291829076424afa190aa29f3241f45acad8e121b4cf481e7e92601d3f0946ca412b8db74933ac84285ab2ce99cb9c2ecdef60ea0c466b6cc2983041c0981c6352a41fcf342ead0472c96dc793b0aa3497cb1ca7399d5729a480313ec1b24e6be678408de14e605655a430a6a9bb75dd0d3c5e393ad89b09581c8b763a83adfda78d0ed34ad12a5f55c805dc15be012c65c614f0b3d6301640bf8ff1cdab2bfb20b2d270432e390668d8618619408f9f861790697333ce49928545545b2bccb0a080c3b81d0bb038406c983da9e1c7ba74e672982f1899c171cb1c7885abc993001e7ed69ed939b38004c8e059809bc17171e49c6770cce0807120f0b8cc719ad3d9f3383799fe4d5366664600df13ee651e803c2e739ce6740679e58b4ed76d5cc54bcd3ae7ac5fd0a11170dd5d9cb5ce9a8a6c49dcafb5176fdb83cd15a7d8b2d6989c71ffc173a609b3c3061b3f3c3295566b150b0c5c966b077bf32bac50a3c66a55710c7585cd910baec365ddcf4240614c8ee334a7bdbaf23a0e18e8f7deee9ff3dfb88adcddaee33aee5a30062fdb6c54a5b93a6bad73e6405f1f5569b4364d180d734d1d50f9c25d41e159862baa316104ba7d8cf30a8dfb231eddc680b7691feb8b2d4ee13e97a7084602dd9af65a54a5b999db027dc93ae7ac37fe1c3e5cff69da6ada37c11872016c0cd9e5b29546dea1dfc79fb5abeb32075d63ecdf93877eb3056ed7250ed8be59a24a0faf701d650d04af86e338cd699006afc62b6b5dd5d8dba1bc8d3af1d59717ae69abc1061eb52837589705d2f1737a478c868ac567ad57f10aff4e013f2b0de352a77bfc03284d5c411b2fe912ec1334ac0c2a0f4fadd7af2b9f05475570ef3baf1c6148f5ae5d35a6d3ef7d9a04bdd779dd5790f79a4c758d5ed6a97152bd2b358a6df7dd7ddcbd462fcb2b61c8dfd9aeb49f039f79894c02418b7cf7eb4b1dd57f8d7d28b30f3e99fc19d723befa32fa00c73807e0ab2fa31b9e5f675df3d59aef2a7fad01ffea03d7b906c2d716e107ee95078b9f6eba6612be9ab30e18c05765f76a735005b75f5d35c6e21eb0a56164c5c88a9115232b46568cac185931b26264c5c80a11091109110911091109521252925113396c1a638c31c61ac735f5abf4cf8240a215b4c7438fbadcec4818cb711cc75d7e4d8d390e5f72cfaec9734d8d31d67a764dad675acfd47214df37e2814d7acdbad36b7cf66bb514f8eacb5923eddc635cebe79c73ce39679c31ce18e33a42f5af0cc54a8f77d5cb99047b0a4cdab93f374c906baac02451571c4c2cc5879f293485a6b84fbd702bf4dca7b84fe5dcd6f7dca7825c562ac835c954ce4dbdc8547acd9d6b72dfd2fb77537abd2c9c52b9650a9631f75fe5ca9fd2e79a5df0d2a77cc2cd80e5f7646672649f54e7f259794c1cb76adea4c6d1285ee50fe5239d8b752b3e4047527a3a17c3ad15db5d71cff1155be742b1cefdd7fde0a37326262bb61f3af71d49ce6a0c974299a4d06b721fa3053ca350ab31dcc748829744c8f4943ea0f294494244e738999e1ac3e53f67c03a37035669469dfb19b14a933bf733b89ff1e4b246d0572628d42a4da9730fba3030b2ce717fff848975ee0a725669bce7729aea35d29424be2fe5073f5f7a4a0878ad2b7b71d65ce78142df065354b6ada2501422a179a66b86a0b5365cf11cb062bae608cc398f484fbc28249124de6ebe2ec49258e264209392a964d24ea4f4c0c46262b94511d330ef9b82e5c472ba15b9c1bcd3781af1cd93dbedd6733bc297cc60ef3da2b47879266b5251515149b58cc26871697151d9e245c97f48465ad4b4ccf42e1f03c160f2430b24d974cd959595952771f20809b9492412892cb7787d45ca5d2a954aa5cadb4db0b0b0b0a85ebe188ce338bec488651b0e2163868c195bacd4669ecc9bb15d5c5c5c669833cf5c99ab25643056db7271c256a954aa13504061c7881123060a4e6c19a4c5a41093421164450822bbc55c5e0a3452a011d3020c0f5482860c0d990c44a6864c8deec56b00a006006e68a783444e0107800a00f854c84060fca6820d1554d0af2637dc13d33569d0a041c3860d2dbc1bbb468d1a356edcb83df1401efb0969cdb45a20da1612eaccac3033d32b4f135a5f81668515f692cd8f785acc9a40c1028586051a4bd6afa0ce35c6898f1314989ce0784d7e3e235ccf0b1670b0c0821638b8571e2d6e32901f29c1d1020e1c4560f2f5cad3040631ee49365914dc5e672248010eb98516401b5d636a4c1190f473f338e9c16c0b262123324e8638d9393580579e2d66ddeb95678b4a5369aa29009b0516586021000110957061b7d0420b2db8e002c90439b60b2eb8007201e7f01cc02bcf1619543dd3352b8d14626050386165ce90492980ebab2f678c4fc7d703c17defd59ae346dcf6401ef89ed51de777779cbef9e65247cc99bbdcbd57670fcc2497d18d2a6087d5598336609ed5a8a40c7638412ad5cc08000000b315000020140a06448201899ea58198dc0314000d74925074569849e44990e3288a428818400c21ca18024000684668c641004d45f237f4d674b779a477583b0691b499f681152a71519ce4f8e4b85b2e6a4743248b01adf5638698b39b6626e50aaf0e092c6c970dbd63109436d4f959881282f0ee6105a5b1bcbe41da753aaafd201e64195d2ccd483ea98529e25ca32edf87cb427480bd8efe55008a6f80ed8851c314a38aaec8b647d7076eef45dfc2f12aa59fb169373fe820fa612999167658e3a63b8e9c1303e6e7b7eec16443811b4378431c3494123f1320a87195dde569f2896bd3b04af380953731aeb41c6e63ffe6941fde280131581244f01722474a052d0bfcc4b22d5e8b0f7fec8a33f0db84c8a83646b4aa37da8a5cd3103e724f96512d00c45e20601d1b6aebfd5546e99987b2d88705a39d967d48bd8b5ff24dc5c0ef2ee322f680d9a64cf88f9c8c80e10aff397924b661f8711f1478d9f7173a9b3d0b78102c47ed2f6d202d54f9941ad4eec06960ea6f002be2f51cb14554be43dafc902e55cb6b2853707b9206d680016214aec191fa77c41f14db707afe17882ce89a09f3288c991090a6926099243ec50786aca515fb26518b2320e5228114c575b28ef9267f5862b92ba9a0c472a384cf0d84c96ee256a076b45f2f338691d04371264153e39209fb880c36a340a9ecdb831f702d65532aa655ba91e3212762478738b29e767b6dde970b084d6cf46b87945b8a171f687436a0fdcc6e4073b75a734870d974eb72d9005438e6a424a25f0fda0292f8782056499284791a78392b4c5561db6ef489360b94ceb29820d2bf87c4a2d2c557c71c810b23ee1fc9b37085eab5787330a40c71a126efba5492cc55ebe58918b0ea2535d50399a273582939f353408f258058378038933104051504b2002ba6d6411c562fe7e45130b9bed7c09611a1f1e0e81980d577ed6ef49d0c3431be5d382e7581df5c000c5c083dd74bb113de3a7eb62c0ee14e9e7d82517be26a3442d9049ad6b827a4b9a753554d25ee647743b02f914b3da1443adef98274ef47ec2d4970fb5e8fe2f616697b3fd2cc5e64740383683e593bdcb3976cfd259adb9509dfb4196f64d137b26d06ce80ce801ef20c1a43d0201c8d3103baa6dc50d2c71ab742a1c29d4f5f2203dc6b2f202a1c9a8c801077762b3a397f569910a94ea644fc260ad825c0ddc1eaded84d6205cbf645c7251c46b613fd132f1739b8f9d48ef70461aa03fd3e59d5a806b5d648ecebe997f428a0fc1c1fcecb636b0c05449140575963dd1d244f200555c0165cb69acb94dffcadb7cb6071699eeedb73a521a7c65a641f7836875d3a2ba9f43ecab5d053e690d1c3cdb2e5394895184b02d0a0f8e98416dfe6160ca1f462b4a102b44f5a648b3468ed860f2b982e4df3b79da81ca450c2368105d4aaa896c2994f06c6ac67e23c3094af15154341600d502736403da23bbae80e822880c1f626f193391651dcc5ff41c79a4fb94e1981166b7075ccbae73512ff558cc57d028d24945e76497fba0651d9225c1385c62e6033dad6ae7dc995d57978daa4e9091498811accb4fce4d8a3596783eca7ef52e1daf0133e212277d49dedbee304f65878229b011ad5e3afab3f4562c38049d353e84713f7dfc1ba0f882de3812c76036a24fa7e31116f9949b008c328baea9e8fba20c57658210ad66d53c03e8032044bfcf114bc321d248fa19916bca2ef6bc85f10baae0871e132753422d5d1cb109a2f2bb99a991c403d3ecc5892b9cad4852ccd324744612363fe3d1f44594befa9539a6a1ff3b888dcd65a6d3a2bdf5c6c57cb4fb64f1ef407635e398db879d23a34a7e0e4107ac43fb639a8f60b5303bfe58c1808931f3075c0985e23d51edc4e477ecfae08ab6ca591139c0be92fa2d54ed669a820b0700bd1db345b0fda74e47747541261ba62c4d84b519f18717c76051b10511a2be2d7e81b977f116cb9c78163a82df4c76f3a8cb8de50491baed2a6d540c22888424619987ffc51141c6cd423ee0eeaf60cead497d794a6f024998ff0cf8a4668205d8c134e305dde30a9e7c4c3e2931b3da4c1200ae2d0c15e137852e64393f927f5c8f62854822529c9630948f135a3043ac89dff58711a4686017abd872b453a36413ce23b3791730523d3f485a145f9a18ef1503df17b24f0c9e4d90af09df35dede149e0bc07df610cc378fc2efe23deb18f69bb77e0f03ec2b83c5779c2674185b95d138f296a7b7c0c2feef33e170449885773414c1a2c82d938177be1a324b94b46f73a2e99ae15fe20189ffc7ff8a7b63b7a42f94eb4248e44e6f4bc90184be45b992107fc61428848120a9cc8a80595e67c15eade9b1f36d0d8f72eeb4f00a9704114b346c5b030f90701bc519d1334f09ce48b0f411d22aae237a31038b8943ea8b6f656c86c4f765856e350c0e75b7345066a9f25cbbada174478a60b1b5469120a992fb6bc1d0670e7d3bf84c480ab7ce0e95080fa07f7db966230a512d87927d09e1ed29c39aa5985e1101eacf7dfa095d4a6dd126b1cc899e63c5f348ec5b2436ccbc0c009d3f9e0922d57342436ca2756e3be64cf8705e4ba4825f3ab2164d69b191988650e4bb078502fddb6be526eff1df04e356cb70799e9a9e32ea662257e9954ff92a183b689bfc9a3f440338d45049ef47da4e7451318abc01c48a16f0313a26042a4fc52f87b60f3ab57864cf0be7ec4bee0e3edbe425e7c9a2f2e1e7ae92dea4202575287df96bff5e854faea9c59ef0b16bf103e82f98593c65ce38d8e1de1574218f0d54bdd6b90cd82b55c9d2462f84bc2553717815d9aa27342f79c07a0e39518013bd24c4ee6d0f2988d2f7b7800f9e79cddf04bd85d7050a0ce599ca76c41898967f12347c3971180cc38ee69037636e6571e6d38ce1018e0a887019adcb69c75959b5942e8085a6fcc2b6e4c4f24747596309fab55ddf270aba4b705102ea3ce02ae3bff4bf1ba9421ebe75f91d1e617823adde607613be14531a2f7950fb19579e6c1d02431850ef4800b53916c6353b456c604f652a2bd8a61675559821e1ba54c223ebb80bb3a2cfd74f000c286b2462a73563506fa6561c0c3fca19503039a5b21d46a14748f2b4693f3258a0023b7da4845e314416b26a5ce6c9352aa3aeef62eff1f32905c564ea943f2005e9e5d21554a5c3f275cdea10e5494653dcd590c411673ed0ec04f99cce31dcaa38e1170f0a4b46f75302573c795447bd53de29a37a6e8231b6b64cc9f455e0fec5ea05db01fb5039db09286d35c2b425acd06a78fd2536ad421fb9ca79b1623604d208ea5408e766448b7ab4c650d15457adc54ff811436a01e44313da42bd72216897bbc0ace300c0286382f536816b3a41d839aa84c0565bfcb50e298ed88c5656359b2e3fbba2b447618558bf678de86620c7a89016758210c34e89c11034965004bb544ee911d40ce71d61dd82e5f42046e48ee8c18271ebd11b05ab4a563dc45230a9afc8d4f8c8c68c634d999b26397fe95db1eff5e3de9ccb656a85b2d7410324d66361dd6c40a933ba5ce40ac30dad4447ba1d573b6a7d7af4223695624d732cc7aacc4493487fce4511143cf8ae56cab2bc3342eaec230c3aefa4f1739e9045d9afe891c8c428076d6ed6a198e2d05f705128dcc602579e602c7f4646364e9fdf2dda4d82b9b2797e09e164bbbbd296f1b4626e4bd3b5dd48163ca309a908e0c703638bc241f9f3a32dcb61a5452696b0bb6858fc85290268bc6d7888ad71cfc17c2ba8f58f3db2e15c9db28927742f95f24e7485018b2dabc18f0a252d60a6df770711933cb9b319a216bc8d02dc68aed526f22e928032408b75ce4594df6acc4f59b0532a92a1507ed568dc9a82a069586cecdd950d194699d62a64e2d4b2fbadc5644a2b7901e8ac57a8207c1fbb6e10cc1c2a038978b7f5390fd2d66af54a136f290e605a92c0394b94bb6458dabe88d574e7f47ce01fa0936b62dfa600f84fcb6b79bcd5362252c610a18868a8900f846711054115e6780079bbb78a7e59ac70103798c3f8cdc58ab2623822a5a89997afd130eb3297aec3e02107cff6aa0defc74889524418d3cc1c4f965db33b16c616f0877a20feb8821b1c2e717be7fc74cd3bd166cbda09640297ea224b4efa24670b319135f4c264779000c958bf2df634fbb264810b843db0b0f5d993b8d45aee47c2e9ad34b17a59adb8320c664d3602e2a082993b3d84221e609fff0950610d6459b2a5d0656ee14bde3a58498728ce2dc20a1d34d75bd75b96737b9776300656fbe95d66112bab9697c490417a4c3234242a7d452802def9f0fd664447094aaa935f79e89b2791bf10ed6402baf146e968725e16b585833ff3ee7f92f68f482d53686f7acdc1e405187e00d12f21c19c91ee542084620e82a17c7d41ac31c0060159ab9ae4b577022b4f30227f36658a12c7b5fc779a38f30de425bbb26877446e1deae07b753610f57fe58eaa5e2211ccfad7770fc9d67e35a3f4777ce0f7a6e51570fda94b19d23cf5954016d0bbc05d801b44a017a2f228e6d9c047a2e51c7377626e8142a2fc638f51f34c781e969420c2257153b1570ed80b63824b09ec4d279102858bd56f3ffd111706a9b91fa2b0b07f02f0e37586112fbe47d35c8e68651b7be526a8164742b467d11def594c3b1ebad65e38b55025db90894f0d7e4ef1570910228c8772ca0a8ed5ed9b4a29ca28510d78fcf1751445f6b6ad9e8ba1c0ede7db1c7145c890dd2c420e66917ada17fa953856c308bba1b732fe8813da0d4716f9e1bae3aae8d158d95137212869af7f324e94a8ea93d69355a2663e8310fab0caa9dcda1c77731cee80c8b1235477de491beb850e49de4ab06208fadf7b0f8ccbe8187c63c61fc48b29194029e6c7ea972b397642ae88897314a1dfba4a1e901321d3689ece08960d870502683c32011c215cee909e263cc2125611a07ed50aa9d357e1446c120e895263c461ebe28d11870fe3f156b30300c299d7ea214719735222be949b8c233af2d14fb3340b244cea806e9c5beba133df42ca7ef587335b2005771b85026628bd499262c1abfb482f59f5005ef5bea154cf98160051b4566f2069be72684d9c9dedab219dabb704e4e69d47e4c308e2fbcaf48651656adebd3b447998ba624c01fa4349b6ca141b14440020f399d6f83d474485e96c18be6cac725374a534539d8bcb6f68d95df306ae9ab39d38b4990b91bc0d7f6c3dcb828421921e2218a15756cee8233803e5ede3c571894842ce979f3424e3d5bfc33dae7617e4e40302d24b3632fabb84dff62d4532e533c87873009e87d44ec75fafb2c4a76c2a280a75553f11bbdb42742a253302572b4c31ba65f3511474e280d63f7bc08d599f6e713f771e790967488c1998356d8bc9095b980e092d8aae020e2ab147e7f57163895561cecdf1506098e5402629a2c089fdf950af6c3c96d0f27050de279f67b3b4acc63798d493b485c321e4909c6215c263260598719dce18d2e766dbb942e334cf212e0049c5dd970fc487947a638a7bf39c055f4340cc9e8b603e841e9961682944ba26d2118df315970544ff3e277602d383d30a0a4f303c009c9877fd3a27139656b5258d7bf1e31a9656686110b002d44a0ddcc75400300b15d7397cc541029ed2004471e184dc66f01628eb8a24c3b909601e781549b3109f443773e6d4f9844491488ab110b82855c9e450200719efa05ffada5c47be8b32c5cdac1980ffbe1fecd811ebd4d69e2c8d8060da5cb8649a45e634b176f97ed1081e1673996231d87004b991c8362ffb6a7db95da8dc7b626e3acdec8222d92625b141ce2b0eba7d8bbf232a3e598fd29903f57ff57f8c93f2dfc88a1da5d8f81250b58b28e05cb58638d359658606d5845c42cb063083ca429fb8d4ffa1ec77215721a167a85871e81314c5af30387481580eade3efd08bc18d01ff355e909c07f23ac0ee962e13ac86348a7918cdfe2150b70046f244bbbc32a1c0c4ce7906bcc5986a2c1384bfc980cd442881426c0fb7f87141085c0636be2318263a2bcfbb10578fb03ea369cb19c9ad379fd014ab39ce3c47a8a3855f3d970cf5a82d07e95aceb5a8e85185c595f6da70aecc10b738797edd39d867d483e665ec5fa2a71eefea558f429f5f6ff5372219a72bd2953af7add9386e9ee7536bca752e16b95dd7fdb8e0a0133ded54b47c965c88e4c18204d875a13116bc70185bdafdcf2b3b9498d92c4f8b24f19ae980fd655a033c0e8bdb4c7c6f14472b806e84c5bd6a003ddb95943d0670b17b07adafcfd8203a43d2dcc77eef4652259a4acd7ae0904958cd6439ac90ce0a3cd36d286bdd1cd4f238ce020ff32f0175458e884c30402f60f80aa9910fcb2796cda51a1ff18995141eab871cfb12b86110090ffabb7d853026fad827888dc9e1b6f5e45bf07f88f3b1a6130607c7d60a991c35990c53c4e16ab74ba8751c08bfc355edca9178131e8eb6c1f1b76d45cd61d08904929b8807c9e5d353df92130fd7d2514da5b7970511d1be8b60d098aa10a0b33d804d3b27e6fdc52959e26f4cdbd62f43342b304448a1518e398e677a0511d408d59c1220145aa30a6083eef48ad8a04584b63cf4bae50f03f529f818d9695ee0f3945f6d52c27a5fce2bc8eabc78277e2597707e37ddb4e8c6fbd26673a72c85ca6217190cfbf924c807a7e3f270bdccc41420116eb67d348c85d9965a2a1c8fce6ebda96dcabe48eb630c1d941265f77100489ea8d415323bf78a1a923bba243c965fdda907ee1fbe978742ca380854d0d4c96e0be6ab9bb2b65f748524d26d8bdb7ea98f860ef9ef1d608233898af7004c164ba43865db47d6fdab31621bb48204649841050b34400d9de154d8ca83e023896fd7fa2f0a0c433f9acdf6280cc72cab8106e93b87a14ce1ab744260f90190db2106731855614701ea3c7168342fd0638936d70cfb8f67e72f67a091b8da47143df929e08a2260a3ba4bc9c4a1cc1cbdc498d69de77254909cce41219841379e78f7e2d30cbc21744d0cf7d257e5ebc95988c88fdc539c5d1ac24f16dfc477a2726344793340252e05dc8045c884a0f70c0041a4dd1921c00324384dc6c1f539203c36a6b2a0bd595239fb791c94807401b89646c7547c2d2ba36e99a1d17a4e0a0455141e77b041991471f4266d6abe8b7fb6dbbbb866165469bd56bd3ae358ec71273d3bab8113fa2627151d28b85bf5b484b98cc3fae651646345525a9c3a165ca63bb4351cb3e2750320dbedee971b6a13ace50f9c816bdac2c42d87a70a1fdbe8ba2fb37eb088936d0e01310fd352ebb7c17f10372e196b8d5c4268fa52d8818b4923b72a9f88b627a6fa4240031c93fdc314a4d6e2e4d38e0a518773fdaa0d4a411e9c0ed4ca97a0c1de009d9fca920085aa2811a3c606b7a47d9e1043776fb139d7ca08b20939d26706c8abac48911beb4ee136be89e98fc1646cfab8c815399ad5371b6910861b72bbf3d95f7ab24e47d885c1396234c53c55ba083a81a997054da4484929b62f32f12a9a5bc1b786d380e2d4bb4e1140eed9c4559411959ee6a3ce724c6776eddbd46154ad41036886e8ad19f503b85a69cf23f080016f93356f88b741e82034a21a798427fd8a87dd8d36f8d100eaa438d2ab05015c43b2e4ea015d78a4d5c48a59c3265c819a97b1923d74eff97322b1bf14c6b05c14a08f31f134567577fa6c88cb53a3c433f5ccf5d5aa703e10513fb44353640c37bdde9fa79d1a8020c811da411de2df16a3dcdd3736b3ff1f9ba5e4961b248bf71400bf9a91f187d6ea24f72604b2369afeb05b3af13adb02705e7afcdfa6fee38a102f5f5a2266ecaa4b9d80317ed1bb95ae8f47b539ea3a54144fc815bc68d49ea232641dc7650c24a2f93508bc48e9ccc5750d24a307ad694bcbd8992e5e4de97cc4eeef6ad5d020e051aab1f4cf268507ece609aa1035cb58a71c823193ed9010a1c86b6168f6c06f173d4d34fd9ca1f8608808b394578cdc3a256557c580bd7fcaf40c41b84c5a211f047e2c09110e6158af96b273acc6674656417dbbde1f4c79ef68e38a2ae7b184789b33f04da185b2802f1324b0bbe3eb16ee2c8d213953545d157c2773ffa75712cc16d0c9185002c14781f23609c8d2a27955c1e6536a7b1bf84054bd4a21d1c1d4127e109c81408add52892f5c7d57e6c9773564d30026202433fd762279c3e6c7ce47a618fe1cb47385300b65a1a75ab0963aa07c8ccc681c9f614efd2bf0858bdaee7858bfed316cc736b713f87b7f7008ab04850ac3b7275a3f79189abd4f1b9ba2a9d238e179f870ef03240ff893760b04d18fa4a420748e32046ec71a03bc4239c0b0c966c51d664e7834dcdce539014f7d0741e41c2d3e73bd69ce2132e01c0a949d6e1157bc0f5128013cbec60e1b6d9d171bdfca5a732b6918fb04768be422a83056847379cdf67e79c803b7f280f556709ff34ab5f4c7067a9771712523a8b1a14338ce1c00fd58f4ee25e568aa16ca9ccf58ad0944c53769c7c73a3aee6c0a1c29b7b303d8e652f5edbea40bb0ec74cf21751d73e66176ab959377983499d671a61107b6aec173be87e9a670d58e6db86b61d9f2841518748ce7e254b3019a4fae53b4eef04d9270cf127daad677482ac71a6cf974d9bd9497fe202dd7ed8480f93323b1812f942116171e80b76baa16bf4d009e1e1b8d6d8b7f47f98def075a3a24b476dbd01725b2c1a27420aad62612e2004266224fa73714acb038b1290f77724a14a0ca2c2a11c5ef556a413a484086ff5cb398d081e2e35224f0fc7b277d6a2e144ecd66b61031ae6d34f2bb63a0967ab89e89c590f36505b8d9873e1bd3066a869a5fb3016f0128f5385b238f019deda700d00268a161b38a021c8f7e33326566d485306128475c0cd40458e650807f01ad57615629bc7102139c302ee12e666981139cc646d37e363ff326b6eb4f22e5467ab1cf6af522aab6d5c0820bee3f33854d2d9ca47499b14bbc3aba5d61683e91281f585119ca0d120f082adec3bb625084b5eae7ea77c30973635043735a3871b04cba42951c44250b2d7d997f7efbc0127ac35a24439216de76d4cbdc09ea7cec9bceed29996c71e834f26eefb9c71e163e7853a3b1eb0bdf3f900f6f3c67f4e6e3561c6b4a95f32b7484f5ee02f0c4009bf53b132a599d0c59edad9d58c9978f37967cc3efda69846ae86f6beb459a5e55b8fcf262a86205243e10a838eecfb7d267c67c21964d640eadc0ed7fcff69dee506b0712dfc49f1b2228ca8da3d47b270e159c03fcd395bbfa6a0c98395825241f79fd53b5409e55866ac886ef5ebad40a9f56255d7093782ae92a7282ccb0742b911b9b0281c69a8b7c1ea1ea1c8437f1599c64992e42598911a7c7a2c45199cb8f065bc6de856ad963235172f4ea06c0a396a953a9ccab31fa07b8bb66fb8c9167f5818b41a2fe2eb2c66bf25e0d9f1d88670b713e7eb890c19034b3832edd9331fbd31ab26d46c04a43912516e3857785ffc053d1642109282c98d273e0e0cd2fdf0d7bcfb14b107fd2a2e23f521564a183e0904458a90efb9496f29537447ad51e677ae9a6bfe415e0046d8936da33477383530e8869d27974c560b70137ab304d51d8540e37ad0d54e9b66e21bdd5d054d267b12b56448883b63c4314248c945f86c4e98f2388f3740be1c33160deea59c59783bd01b7604965ea85a89ff121a1d0864a51c300ae7d7904abed21662d6dc0e82f1fb73b63fe1ad752052b9f356254ca360a0df11211617527d645a5c2982725dfda7e37fafd609c39488aeb100f292c5a91421dab3af5249c4cad932b831218cd999390f418df42e42fe13581816a9c87fabfd8db4d1f5b40c27c0d190fa67562557d43a6bb7a0006ce61b94b330c376b08b6298daec07c93dfbaa6ce4271011c8169399f905e3481d14a686500aa3d7f7d4905e3d2848bcf7d1b044ae79d8a35670fedf19552f760b29e00d49d17124dac6f898188a12ec4181ce849b27e3fe6c7c2a1991a902f46c294886d4ded80e1843d59ac312d1296cbc40b0c1fd2736547f2ee224d8ce40452d13f3bed42e25f845680c32413e6543039066b00dc5d044cec66588282f33394dde2342f086c653353d72d3a346476cba0a534681937c713f33c847e449efb60f0dd3b564187c223e8b1d59f04c514177485c11a62f1849488081010ea332312ca7813b60346f98e9fc53e90f89d4a60a605cc318969c930ddcedad297edb2ed2fc1054251bddf40e75aa6aced7110d69367fd2e378e4ff30b8879e8dd06e98cc6684703d5fc300dfd96bf67e0a66dc2a6ea22560b0e595cd5ae117d0d431ab2041eb7ae14d105b2455e0d8a1f3c06b4d96ab90ff84fd6b2e14e68a716564660fc278de1bb53e29026e9420d891a421eb7f21e4d3c86f6859c991ed9db012b297ec9344a7e8f3b0f6a30f53b17f2bf7192737e94ce492e17e5249ddc717e25df06e4e187111677f7d515a39b54bfd163f511c5d525316ce3e8b0a65b7a0f85909a853768d8c57bfb9d81f82114f319d988a68960961064247155414666dd0011290a98c185e09bdc74763995b5a22e9807e2420569b82520382c676798412e819cbf001cb68d96fd0a80ce7be682ceab669134d5687f23499c027323b1de5ff05e6026024d726424d8ba39b7387c6bc0e5a0430f94f6148040ed2e5a387ca8020ab6d1f2cb525ce1f17200b99e9707511b6fb9d93c0ec8686694cd544e9de53701bca65aa9c03e9ebc22853de7c56dc32f1befe1bba01fe83ae7a265100548c4da3cbfd84bdeb6e4f1a1223cd37325612ed4575dd299864841376fc00389fc6dcc625f276d33d531ac0462d8953a5ee9633aca1df73f61be89779f3ea286193aaa2a2f45f8ea320e0300e5106aa2343ed5d5f90125080760e5b261187e100f435250be8c6ad25462dbf58fa3a3782c57066aefd6388695c4bbf88db7e2c5772912037156cca2c7769da005d4d23d8a05cb121aceec87a705a7d87506266a880e2bbe39173d3bb2ebcd0490dc28f39fa4d6e5e147b177af174b0b7e7df31fe941dcfcefc336aa2bd7cbc51f2819cf4e60abf9f850a8cfea60c7e49091c15ff0c2842a7abe8acc150aa12751b957ab3585602813f6bbec11245aad8a47f5897316d3004c6fdfe3ac689d220e8f46d536b6072c33dc56fe3ce6e009b3b64bf08c71d48f191e8559b61fc8e308873f377d6c24a1582fc6c2866b073e51eb6fa02cdfe2959fd98af0a06635264da82ab07db987932f1384b3061f74b67cca7551eb9944a6d1ef8d35cef13763c28dcd7547aeeb78e6c2386e2093de9dfa5c0ca41772dd9b4a011b74d44d463149a8bee34007cf500a9f2bfd1aa040f2a11a999887c34ad9829824f29a8b3dc09112215f7d8777c91d8dada0bd0eff107561f82260d0afaa7e99503cb7ee31c9c6958f145c64d3770c650810d765a20f4fa788b6e00d7d1d364151535b2fae02a6f816fad99f4fe3af15a554c9b1d124daa6f7f3d1a0201deb1303e1866423943c4a5043458474ffa778d7fbf4da2528737bd23c67dcdf6c54a54eecbac8b8dd1304b74d16c2cf9640ad15261d5bae7056b2ea931fd716acf1efe94929c02f4fd2a2ab93040117da256f4d32985a07a31328adefc383267205d85460f3b63f3906f9703b79cf604eaeb6b4cb6a450a58fe25569eaad1fcbeb371749b7e5d7a1b200e38b0dfa769261a040aee5ebdf4d8c9621a4424f146d21fd408dbed4c6b75c698bc568ff0c2cbab2c36894dde47f29fa6c74a632223115a93144dd4566aec330ac421a544e488ed3ceb4ea6d4631f36207d05118ff3c3773d757ba90550c511386cf89f2b2d73b83032c0ce314e1cfddbd743cf325252da681f7d4d695c5309ef56b5109d32b1b2078c5e19e5084821850b824b44da426917a8d0564c04149501afd47a32f049ddee62ea4789e90774ed2c49047c147d805997e0e01fa224b13bf683c369d3ebfce966a5ae53bc2cd27900f39a736734a0506880660b36fbd788e2240efda1116080caa09d88159abd68e7a08362571086c1016bd547843f8feec085961d7d1666ee7a67842a4d10f9e6d259e2de3fe4a97ad8dcdedfacf482e682fb80d26b3a5479844a8f6d3d804ee01fbd3c5ca988b184873b7ee3e1ffde24b34744f721693c6f92776af46010cde4d423fa8714b7b5be8f2cf8c24c06b80c1912cf4b02a78100ad4e6089b064f57521da2a4fd5d36515de5d4d12e0d61cfb8c509eccf5081c38230ee8746e086529689c4c3c5aba6155de42489356592001d21fe9fd80449656f6162e9e9d8d5503bc298a51dce4677f731004ca9c7827f0c7aad57406daa1d3959d1f0ba12b08eb757b909d2de289f6885ea9da8f484e782eec7f177bf0676f4c5b62b693a98738a38db79a8b996dad707c79935f0b69fd6b8392a4ed761ce3c5a9e488da83b4d4193b03474807c05d7b1d40999ecbe968d254f30181a1190d4f47d3e1688c7a15cde2af29b055ca8c018e38c1b46109cdc914006ea85e819a9940f3292e59b094ba70889d86f82a3494a35b0d36946a8fe50a9d19390e05736d20bdf78e03931676bf167661fd334dcdd768c6d3d389eaea29a6c0c9ad7e3b22d814244c9be1da04c76f6b734d5801854c866608d565c27b532fe235324948ba665d7581a63ae4c74edbe239a4c299668634eb6f9b0debcfa1740364d05b8b6dfbbfb3ad59f90164a6c4e25d1079e218be9a8d90f4f975e11a40231ad31f0a84193adf9a586624ede8fde2f82930a034ef9bf78e5302465132301587cc224a1c933ba1fb15e3e06e64d354b80083a5aec0df16677cb308aed3ecb4127c8fbe2bbb75b6d74bc80595c843b44ef0cb97c1fbbe2021c2e0abcbbf6387bc26204f06f0b54b5f366da6894476a326899fb2f054fdb7322ae7acccc9f98519bd0ee26383a26494f72bd4c3a9c29df189fcbf22e970e0ff452e23eb14fd572066fc21d78306a6d7791ff970ff3439012ecb68a8172e4259929826a70aea7f481f67821cace8ac89d24bc674dfcb0eda7bb6719f65255199012b2a0fa261b43e6092aa89df42f50fd8719f80d65f8bf2d44fb9d255c0d675a8bb559a3e00fc239f6dfa1ae13fdd90254aeecd439a47f160c5f3ce4967f909769996f0a2f605642b3ceabbe8d4b5c35b304588cde22d8315e5a6c25513d55fc7badf020e44620c11885955597b75b322b38099ed792c45ad7a800974c83478639ed74817a829a6b3e3cbd93efb3369414890c21a2cfdd138d233c5c3c86819ac4946c183411815c800750012aa8975f21637f80c5ca9820c9031dbef41018ebe665d94989022d5dfc4cbb48cc5304a37b3d0346c2206a6923713a9b5861090ec766004b062763117437282185909c46607920d650369b2c6ea4a60176b84195b3b466b48ac0c0954a27aadfa43141ee41a623fbf7973fa7abc3fd39aeb0da67e375b560ad173a81c549642c38b60feca4f5af5a22d2c0c052d957b26a9114d603ac1d72949f16e22b5a7e50b1fa8f41b5e3335ff344a7ca275fe532bf69c59cc7088259dfc3c70b84447a1820631a44acec47c17475ca1d3e4f66c5040bcf20116ec46d6be440599ff79e595459c025144d068d7e99a99498eca2b3cb0fe1ebbd680441e38b012c9107c2a0a4f0986db80ca43dc29169cbf6ea652067996e0fb18a593e72fd946a31b5a96d6e13cbb289e9b0363e06d2787136a20934898bf9794754014809172f12c3b188849dcf440b2c71b86c74aa9b6cf422be1f4b2c5959e701861d0e0152384f8469cc383c767a27aeabf820251249ac1dee2bd776c4e76fc5c9f52d33e557b2a4d2014d75b0970c737e309cadb5328f2cdec42756402f26ea47e9442074bb7d1e68d1dc0130939bd5553ff3dd19d6a891795fd0ab5b450ec39c3f2e73e6c3598ce56aa5a70046b8f76313ceedae0f41c1138eb5d0b2262e84078465e65299098ad4572406314dd45d2398c0b1320fda86f30b05adebc45607328145457364660a6a9907a12789480a8e0d9904e5faa5f489be67d18493ad1993be93621cebebc13b5f1d1647ee2d276de5521e6a7ec6f51c645558e5d5b30cec18cc57a321c0ea8c4cf64295eac0bd343625cf27e48626ab847c61ef6fed9341490f20a6ebda6db61b0bdc57600b9a0292bdd4a116ecc1bce08c1a4f104a21f072ef00d17606449a2095d2c45a157573acd79ca63d812ef1eee0a1f946bb511cbd042c5bee9de85f765186dde72d07766f78d3d34f050597d1f0fd7bd1266d8212778f48ab5cc12f1db670f083d33d90b989fed56e9c7c90950d5f44b5439c0eae9729be93e96ba3158dc50d9b4165c13778c497defc7303d0168018ab4e5fcab976cf19b94395c3f782181ca596adf6c9224a71cd085102231fc997ad06d3a5a4cea30063baf0d2a62dad5ba7cfe73adced2d2c90155a18924c3b1092eac582421a705f7261d6834cbdd97932f04fa78d390a43053f33e41d56c1dbe387396300b368f428cc59a3e0c8b0ef87f992b537ad644c422e55c24f8dbd6ff740c2e15c655917d74b04bc85c0a83c56c6fb06fe208a445510beb4bdad52a40963b3d590be07a4c9485e4e5844c59ee5bbd09d326fe29dcad0d0e0ed05aee42da68e5092f3ee3ad436950a929bef4539bc8f5bf1610f7ce38d95d2b1a93a2cfff75a330a852922087345bbf615ab986a51688a6406addc0ed5f03c980b63a4afdb9357e08dc8eeb1696747a1148f779669adc202828e278703436bace20337e90717c110d4b61136ecf94c9717cb333ff5ece9ee08c37ac3c50278baccf622d7927ace33704064acc5fc66bc9c5fba9f35bc137306b46a63de0d28dd95e11f2d96956d492c39bad242644418775c16352869ca5f80bc2745694d6242af24c5cb9d038db97b310bca45cc20185d0242496d4c48565ea456203078a4fc0ec64e64a3ec548ab1de996825a0438f2129b59135e5c5186c1f002bdf6fa6d9f1363962552075e0abd7ee4fc5a587a83b4364dff816f0fe143b649a1d7df5605320ced0dc6b3d19347dfe18d7f0199835fece7d9c86cfbfb5f7b4b9c4016821f5d2aa24085c001ae0e8ab2d9f30e61c746ec71cea7027c16b12ee449d26ac6e52f310331d58f0a5ca36f0227f99089db0b30531a33373d4e6c405c26f75770fa29e1d321ba8638c98c5987e4626b8588ad13e5ef8e2e31bde34682c0aa34c2be8b60a15c3cca23db2154e73e5399c80621ba192b8ca80917044b094641cf43ca5f737664ff36f0189ba654eb00a8529418297bf49d361f9434c6131a5298a0fe5db4b67295400a56307315487f2b39f0f33b405c39012668c39ef7f6e9a43fa59cb280e4b866332adf087d14ae9b5773800d170c3eaa01f3d35e32f408aab31b58525d349eb0c0e8f4c97b62cc1d5932412d098e6947b452fff9ded6a1a56a0111ffa4812fff117d6d4ba210af2b773ddd26e721a61153c3de58082c1e00002844b749bb652a3a624bbdf4718a93395b7a6265190a110a8f4b7a8c1dae729e4a35b9587ddaab465b107d2338f780a8f7192e09c5e69289e0450653c24faa6617461fa75fc73fb1558be986fbd0a636128c85953b957bd8d9296300e0eb954ee36e72ed60ee1e43f8e6dd6963cfc8f6e32a616928489138758ed0f9611c0eb98a88e84285176331b4ab0e99989b42be257b1d19f2a4801a1e641cb795f29b421b8e9d081fa56c4bd3efae4b13ab0f1bea7b85a8d425a3ca95663a5ad7a52f37ab0af765e9a5093f55d3e019ffdfff0180f0d2818150f74399b2a2efd7b6432f5a4614646f380d689fddbf60ef914e0b44a6f7d7d528be244536e6b6d17d3bc87e8ae5119a203ed875021b7e7fca0ddeb01a52cd6e4a246907eb598d3232712e2211d77565d393dd469693ae8b7f6a5cf325a7deff5298f38b2c191b47ef5fc6351aef20a742f13dae7e8f7ab937eff480a1381f4d4554cbca35f4614b707c2e3310b5979ec03d0405bec3a295ed7bd8cf016e6946312a74bb253e8b47085470e24061ee27ac9f43b74c44a5303609fcd2b6be03dd0ba2b70752654016958df71bc344af3f31b803d40b7236c4526b5255b86c72a60572ad80a5f6998173c021f0e7333d66216929bcc0c246969ada4955aad321a31304acb11a81455d777aef47c514a0beaf061e24dc2ed94048794ec3c5a9bec9b8d6fe76ad78ae24044fd6fac84acd796ccf710a7abd956aca4cbd7a24a22151e914ecba2162dcb5b8d3442528ce94d4d5c7e1e89b67b1d546be1c74a0fc06a0c04498c8b67505d2aab21342c84a5cfe936224aa71a1bd51ea284257ce1104672ad584a17040e90263ce628c2a5376ce45d18f729576314cabb534494466fa2df5c7210bc707c703c22c78932b3f8340151f88ff9c028b7f67fd904f680d17f55c70868609a758e3fef4d07c93c2fbd112ce0fd5e4cd596ce464f610480d4f73cec00cdeb8fcc5c4ccf204c27118462473de3e85b89505cbf5d08a72c620935cfe7453399f75c45821b99b64d7acf9cb9ca4ffb3fc10fd88032937badde58d4218027658913aab49d79bc65f864f94a0977b0a34943bb36f432053b4825cc65df4a97545efeba569052a495ff2a4d4538d40bc56a9f0704c7b80c67dfa52b94a62cff5bd1d29d8695b21030f1f9fee98d8c4b5b93e1e42bd52630c80ec87f9f40729ba5cee27814e3b6075ff5d654f0b93f3767ee4bf6654ad529735bed904190bfc5e97d3f2652414763024a3cc136e92764a1589dc1ccde22c5975e0e5b20b8b740cf6853b806565d12b9520ffacb859c06e4426d1ded073e95561727c46d960131f4206cd75a2814725afcda1825fa61b144d181ea9f8d43c9de5ea121c8098dac626e061b9eb62be86260e5213dd172b35ac829855b59eb9b5dcf3025a1219f58b24e2035ea7f61d64014ac2536720fe90f9d98f9246ae4703bc97ce21b830df9e5cd0289ec126f4942a710c2c3072b19e29842a7dc60ed08436a6d33580c0f68c4e33e48ece2c354cb2a0134b7d685c8a24bbdf2e4422713b72b537073085a6fa7c7b7d0f872e894a3cf3b60fd61ff0be38d3b58bde1f55f3b7d9964db85c0a3e95c286f6899dbf88920deaf9d0a90093f566f923d19f0b58c815275dcd7a284b8f6f7784414025b548d726d4b3f05a67f9c09dd1c66d24331cbe8d738101287b9272622346f96956073d08c5df87696b16029fd93ece548de6c72b3fe295f429959568967b1f2225937b818cc20e8253ef9b405468cbfd23f25109a37db9663176fd03ffde17bbb80bbd5e2262949a7ecfdd308fab406673c2a52006d3d60e50cb72f4d0e5e46eb517d538f00b11e123edd34d181d4f44fe262734d021511dbc677522b0dea7bbd1b42878c62a3fdd17034b2d03d5a98ac1f68501ce9bcc81b470a8f6ba0abe137fdc5af29aa3f486510027896ab21baea3ac6134a4585d611d5c2b8372c42e697a1939a88eeeb494bf0690c592e90c234625e8ed72194739753792523d795be7e4733a08a7363c8d08ae7bf2d146a47828802e2dc769d9b07dd34d951456d1527cd241b865a2d400c1035c840f3d74259e5c6915c6fdd9fa6c9e6e1ccad9464a36fff0e6a5605a58b3b7660263c94e35b75e78f8af24422bbe3048bb13cf2a1a2a6bbecc4c1a7a8a8941b9c8720dce78deec0ac38556f1d5fab83f3b1a1a242f584255e23ee4c67c9a93ef0c1aba6cc596088c8e43e5414df768357c7367df5ffa3088d4a328a1011ded1246cbae7ad6894c8088393d09f942370566d2cc41563b65fa25114e40ea51442fffe473e89353c489a25a7d8a34a79c81799a7067652363b504177e7e54b62fac3e822fbf0ad35bddf71084a22e2be13a54eaa80f05acc97a9ff5d35e6a5c793603d4bd04aa5472127dcce509dcb9f9bf463cd91c1d4807fe9e5e989aedf7046c2381e028592474c51598cf149ee8eabba40b05a4316f8605fc8511c4d74eb645abdb918fc1b2aa54953a3d1251c0627a264815d4a82631e46c3bf66e074d84250ac5f1bd7fc6968b09692043ef573c1847f8c16f8d441e06b4aada16cd4a5b792ad3dc18c337dbfebe6b0c6e0bb64aa0f4b843aaf515638700d876b50b803f0be13036622a982a5e2001565f08359c2ee6215bccdb79f620e47b409c778cc1147fede915eb8842acf6545278938023b6006720b57f0802f9a504fd35819cfacde61c00a9ff2b718b5149f6f65434f0d4693474c42b528c49b5fc6552394070b6c858274e3a25010e109ab43b4a26dd22b70d6adff72344f5d55a9c3e73da594ad135f1d4edaa6a50fd5ef21d4f4a5c68c2cbf4b3bff5655087916f7783aae35a82c3bab2af7c8521f5ddcf412ba18852bb729a1356ca98dedb4bcb0fe71c805977cafba89e5da7e86b6dab5c1bdf68411ef17167cf3a719871402e4dc8f8f3444da7d746c66d3f6c3622f3dad72eb0d0b98f3cc090c54d00de6cdf2889680c940d841bfa21e4a8cc3a45efd3e6940206afba56456fd0216e63131efa281f7845d331b507d1ccb5a5701eb803afc545027b4d95c1f6564fc2b56e58b95a5904829ba1090bef28ecaf62eb3536a79d2b9e998b50cd7874020afec42203541518e969c61cad4c4c8665a25dc4bb4968edeb6512df8dcb85a15d1c4f9441c9d64514ac82102721f4cdaebf567a2fc7e37dddc628bb7334ea65e9f4cf792279aeaf6096d3b22385c5838e5f0ed219d85b1be6f4eef2fa959a75d2d4be9ae92400f8cc8c62b114ab36213429e02a0f1d07f181ac88de9b76f7011f16d0a9d8caf43574324239fe4491d86299faa1fb848e19ceae83dccf71fe7c3c6b7e5b0c3d4370f45049faf9403a8fa2aaef4402658e4bec62bbbaeb3e1b06392e67d9eea08dcbae4e9f64ad31197db66116c0bec7a595b288a9324db450825bbd12b684603a24117822d05afa2fbc39eb3382d58ee0c7452c43da3040da42269400758be391baf90861d3370a845c4730d8ae270f8e3bff94d60fecd961b75bb13e27097a59a082a958338d4c0792f5b93007c4d4f92400f0e83780731c983ec89bc0a4926346119e59e5f485fb4f7d567df0a63185f863501dd2a0b3be51a06a6321514ce4e7b682efeea68ce1a7dc27738deb3b2d8b90c7d567c824c21dd1bd9eeb2bbc528cbfb6b69546cbe93a14a7aa35fb4de91bac81c2ba3b92828aed16d1232220487c7c2ea1aa953a4fe469d6f3030ed17d1895f39ecc3f092390e43fbae83b43a7a5e1ab86a1ae2278d6091858e9bc754c9cc8dea319048a02adddc9345f2fda6a58a58ef64b78d2c7e24af3b791a9e2dcc8158c3c2dcb7e686ec73900e58d3ee95d2e34a30086d95be0f5eb4841809cf55689c096b31d6f4061c7ea13eec72706b332def3a47154f815198a2e0ba1e480de02839c343e4b79f7308b8d8681d86d7ef443772690bd7e9e7032b672e701a1b77a1f1c012814a8d4005f75dd937bafa0a26985eee3d3db041c22d2b8a0a436534f0f4c2c093c97e9f135850b66495e19c20bb8759c98236eddb2919099e9878419de9fc54a7b9907ebe81c0689007bf4ca459d1aa172628cbd32a1f1fed75f3506e979f0d45459595d379a2881819746a2336489d4bbfdbbd9ae03e1b18e10761c6052a2a56e284d304ba824b9892ddc021871b8776b841850381692935a850e5a10aec46f1f9dec850a14f02bfbf0aaec46dd4e6baad848215c2b2e15d83ca3898fcfa339e4cb37036b13e969cc012052b2ba1107e66cd596914515343b53b74a8d5de08f637b71178111c1b7e65aa17ebc5af6ef1271debb0c87454c91ecebb182fe33aa1530e56408c11fa37f5580f0c0b75952d7be916699d9d01185ed9ac7f36078d64f4d1eea2ce8e1878a8fc4ee5cd4eb702b92f7e860bd8f52760e116cbf6d9b568091c5b8c2e9602f266effc628b96dc3ab2dfeda7f6c326089c468f040853c3aa0f13d83d11a3d4e75053dbb152071515b9a283532158f64f4c652f374969e4436a4794ed8552865ad94dfa775352e2319e4b46d1d1e81fa9e14019ba50846d43b8922420e08c988aef30a081808e97ba41c6cc0c964ec048819f5eb37ed4d6b8994af54d514fa30294a14a039ad000b14e338d1014f6d873e91f060efa87016c9033a5f87cab3f3f9aa1cc57292ab995187a317454173fa1f9b9744fbfd2a40bb3e63947e791f2d51df44f1e0c00015080e3132a7d4791d1511fedf7f2936868398bce67b9bedd0228fa2ca34c116cc7bbe7552aa1b24240c8eae1cc2d9f85a7f27b7ef7ae9b316914326167e00cd18879021c8ea9dc0a6f8ae67b572377612a494e45075dc35865f069c24e8059be9a5c13e9af7c611b896847c1f03e819f413cf03272bcf79003473b6c0976f02a59e70a29140375967e9cdec2794f75874f2c006f9668cc93d864985b46262d1bc2d6b17002072c042e695c64ae6c2e58e8063368cbc3c20d313067b92986b83ab227e3d9068e3ae848e12a29b8eb248be304a5694fd2957f2837be2a28a6768ca68a8bd3ea9cd6d10b6bb0cbecf4d5d84a45aa3bbc956bb2ed23fef062a0b81fecdc27ed88e0437e8e6c73b4f295a700ef81251c9252640f06950500d43170be503554c960815499545217fcb7194a4bf8e4323df60e4cba08aea9d00034a2b5fa48c6ad1126ab0fb56eec15be0d1dce35c5b015887de28fcc32078044abcdc2fb231d6103761d5e58c9045b9a161865842cd0c54704d8743b7a4c9d90a02e696ffba520d016aa113d2ace435f7b380e5a25595b6800b7cee72a68bce49b9da8efeb729e36ec92e9e51397950a54100df63699bd805fe6decbf377acb324f3122d65e2af427509c93191d2b512779123fcba49d46f00873274375c66360419d7c5dd213ce0a6a714b8acb725784f78e1cae96efc77a520d0482042a6166b48bbeb59bd1b56bca418d8da9d0e8909f948b26ac966743eeba951325a4c24b8fa46425cf6432cb4bd3a9713503d5a19971225c1fdf99b2b7de7c96b2b84f1363005e394d0e14d61eaacb6592f128cff9413fbc0b979da2701b44400f2f95f32b815ec22b738a759113d5de81b40d3d055f00eea14f9f8f956291863907a10a4c9c722f03ac62d0ae340a5bf2e45a1a31ab193c5fd4c29c8d34215b2f8380506f698c905d9cd088204674443303b0610509a5d0ef6ae3fcfb051aab067a499a56f9652dd57cadbc336aa1a604f9bce98a11684e67f96c680418aa58cee1c35f226fdff9f4b2519481df1a0f004d363b3183ff34216c800bb432dc179cdec2a5df6c515fbdef0913a918360b40fd0830e4645e2735e912e2d5907a9b72086cc24e44d56fd4bfdb0354f22d954f4c7c287c08db5719ffb7c40bccfe553f8c0014489fce2a8624a4c569713765cb07480516d85eb4eb72f3663e85b533feff82b131503e092ee821220f7da0ba49d7564d45c265452193766df6032c1b6032dfb40a2b525951148f52950d02b3b4841102aa3c1acddfa889c1a8c91211a20892331c5ba92124691382bc62033c56148a88ac42e35a9ed48b738a760b664a79361e1984b0a2f53964dfc967bec26582f1c9b21fdb3efb5d3985971cc75d9520d005013c27586f2a6bf9281242f278a3eec18077c871f94db9b98588a60bdde584f475d110ceaf88687646cd71c25891184dbecbeb7601e9d9dcd03e020fc1aa61d1b85b5fa359b4bf1e925ea6ad73633d8c07a99c21674921a76c36d76c083fc961e704eb31431a425802dc9403a9d895117c4d2e0cd147b69e2c0a99a9a6e0248f07342ece195264261c33432cfe5a8127cfc091fdae626946d9d8f1cee116957638f4338148cf66e4372d8e157495405b6e0bca98a4d41df697f15c298daba333df5c23655197c7733ea9ea84145def363c79f9a7bc67ebe10de92a354f44b8de685271093a2dbb5e252928e0e16e4850198eb0a3553098d6de4fe1da9fa499f3eb0239416bf1e4f80e12a5fafa55380434d98444c62797bf7a444fa45948d666c3614328eab7f97f8fa9442a241db98e80318938341cb2a3c01a772c611d4ce35c167df9845871da8bff67cd3993824d44195b778e68476f621f3df3b2a9cbe7ceedde9da6cc1230d9dcf70660288434c153264dc6617562a8036515e3dcfa6e7b555162f91bf39b7f08eb1895eaebf96303ba5ffbeb0806f57ffb7f8f80afdb51a29e3d7afc61cf28ca3cc916ed3bdaa65eddc1350e71d5605571aac8560f51cecac153be656e214b95da89b1fa663e26f71d049d17a7a86d3bed7c85acbc34826f4d5a17bd54d90eb8d6e54d029518fe6772412130ac4e96e59ec8c45f95e26579602668fd0ca4c047cdddfa79f1129979e19910b698811f2165cb7636702ed1179cdb3f16feb0def9f245f20ba7c2ff3a51f4b7f4ee9232c10d37bbc46afd53d3862784ae5d68aa873491d35d2800a1d73d4419f619868e4961f92a6a699c125520f372bd0cca00f6966c6066b5e2763ded434a4730d2a8f76bf5900799af3f39e13c230d950e38eca3b01200f43ec84717daf35d432039860fd8971640e114191f1f4ae6edb292f65db8fab53dde48846b3cbe80fa77775e5957547eec46dc43b177f783a4e46ee98dd831a85f2b0a03ca209de8b1226168b7c4380db22ca4494f2444154dd7bd8c9e47345006642a686d40cb5f837c0eff347a8d2fe9f249119083a9c4e962531b15b3c951f5689902cb1b85f6a5dcf93c1a540ec5f00054c142216ffdd1236590249a0601ee2c0e2aee3724f14b268f0028d8523f04c727d2a8f84194d5b06ee3185d798892672b922d94952ff0ded5dea8a185d8f1b25a3e7ecc0502a5fe20afcf161009f37256f3df64762592685b123c4ab7b84b0ae271477d14922d11d5e326d62d9ba8ffdd0fae16c34696ea3dcf7b8f1daf01f606a6c2c88ae4a9979ce89826d669587d390db0e90c8cafb0990862493fb8eb54fa263e65f81836147140e73a77b231bac3756d8dd829d5a3059e94d4843108e1582717a6f12a618470ea7101dc686c0341e984b3bc7893d51fa9875b68c22760bb7c9db138ce43d00cc369e0bc571c6eb18872d3e211aefddf6c043770fd6c8b0aae0f65425e61e7115fba984c9d845167addc3642addb1450085984d4a31662d672b0bf66f5354dc3011ae6907e180ee36a31981255d6ec592249ec84a4586672ce94613fb9907eb26219881754ad51c23ce5932f1afd3a5c3c84b5e89a05cb5439fcdc1ae48cb957bd3c9bd5f85e06a6d5d1123ed20a3df4db57c45cd07f1af013aef87fb2bb958208b171648a2ac8fb2183b6b62cd3fb9e0dd3134d82df2df65e5fd3ab7cc77e881cb9df712062b3e5b1be243fa1147b1fbe1ced2df8aeae8979b2339cb0d70a0562c1c043db1d055b39ae475be5098aaabfe80997278844cad0a231aa6f3dab49f928f1d4d0a16988eeca011595c59a0ced3e1423b7450a38f9db997f296743c8d1b5def11d1613288b223255905068ecafed7aa00a1af062eba874a8348dadb9d32b06b43b4586eb64ef5e058ce6620eb6a17ddde60a76ea607380d543baaddc44311d6c7c80b963fa2aacfd614108f445b40d0c02b029734290db470341611566293d400f85eb8166f548cbb1dfbeaef88c068d324c15ab82bb7295cab13b024b041c2add193f8bfe61715d5f23002cde023760f080008376ca7de31ffefb8d8b6c1cf653d381248747215b9d3570051ccc3e674417ca20b486e2692f1ed70c9890d13480729c14570fe440de35219f699e594c051c7a11d625ab3c9b7e98e8818f58022fb61b15240ceba819ca65d2690c7c2bcebc003393a398bdc59396ef9a75cd5c1b121b4b4796de6b9e46a6f91d8b858d89aaec65dcaf3114bef330ce8c19846ad572026002a7f0dd36deec271f99709e8d1dcbf4b3040db181352469cc48d5c5d44894ac45a0296682e1dd030281c8d8e7412c4a6fde2db46699f7ff035cbd6c58a2d942b97fa1a0f257058d5fe71cf7bc4819e9cbdda692bad2c515b9f48fa3385464cd7ce0151157ba8e0a7e1ff44cfe15e5d8bc1eeb81edd7bebc598465a6bf690c1c0a31c4290afec3a9e09b0bcf87c99e44c6bece0d42bc05a381d72b9b2561f679f77e0c0ba6961d3de1649756b09b94f37e6c772e9ab8736ba41f01a8e7ecd8905c5fff0ceb7e59f3b01ccea3e426900eda33fcff4b02b4f076be4738f1b2211dec03e28c330f5e2d086f174fa01644902d20ba840a6b12aff28e1b1dca4d2d38946699026cefe8f1ece0536c450605d683e8a01bb1fbf74e066ec795f2a1c6661532b126d5b6403836c74cf35cff57d0ef9f02fe89aef94124892bd3fa0e9d87940b483b221b7c92ed2dde8595a1f93087ae7dc8419b5c440acbd4d8b45e3060010c8160c2d18064d1a5a91ae82a97486f35aedcb14fb5d89b1d9ca994909fe19ddb11644f4fcd8e47953fe6b00c35e8d99eb5b1dae21e7fe2f60349bf04d6beb6fc96770a064850411ea670470271ee2105641f14680b0caae2ece2f046ee682dcc9d63b62350b0ccdad4e7e86d0d7594894c56f5719e379ce28e644045633ccb4ba7dce6e2a6c9312e84a878b01efe2c5f6a9aeaf740fe32550a17912945b01703099d463aaa541d1e700a7a2e5c8d42a839637cc212123eef5421eff5406522135315499996991f49829aca33a1f517f6f5d33e42ec0d88a4cef113a1053fbc07a64035a6b28dd49e6aee4a5c110e68cd5b1c2ba565279fa6360130050cf8ceef7c8cea388bdc3b09a5ca20faed41997acc20ee9b8afaef603f4546f6bb672b9da21aef65d587a9cb5c3ad29703fd10e23ba9bac206ad0791b4529e5a5c975eaeac8635f3096f14f5db3687729bba2a97b28d5915a43ff3297c2fdbc13f276d7f76a1ed087608dfa547fbd73f66cdf59b0a0c969b01755fc548fe04b6a8915bb0c01175813df53bd3a13718577734dc23e0448c0a48c7aa0f9d8270c8375fa4e9e1603299fdbd43bc770517210ea6d13379415fcc439cfede53417d3eb2cd83842de20d02e6d3b1b61a7970e578e8d9eb31e43e92fbe28c43f9339616cc69af8c6065689aae160d713c93928cd09bb5606ae4234dc20cc64c64f576d240850e24749b262dedde3ad3b5295c31021da15c22d5dd293a3570ff6a4768a9ec808b6c932776e7ecec236bc0057b0ef0811f828acd8122eccec051e9638d6f562d408dd885effae47b873c2b0424ba616bb52b59244399892e05824ec2adfad668f52bd84c4976d30ff522997c22d3a28d5cb5cd1d6fb6c3c08a659ec20cf76a7e2cb5dc29c3a499626cc7668b119f739c1617425025336ff2dd64905e645b2e9c3e47ca3a3d864138700cbb272e8c3099d296181726c66b7c53638485b9c189dc469f3945acfa0e4d370c8abb4a99fea7c732d76084c89216b92719cac7092e7f6d2e0f9237cab4d0c88647a4a175bdbca639a23910dd628579fd0f985d8cb7bdc378438141c51b9116f2614b45a9d5a333f646dfa57b86405f3b352613d3fe30c87ef85a0abecad88ae8b2eb00aaf61f40fe6fc9acce73f155ddbe5a9e8f80414e5b70d1b2a17e834094d9edc6758d2d3bdad6b0c60794d4f9fae44d16e0b13e667faf0eaaa65894fd0ba53109f4182549c641049a1d7bae5ecad04bf57493bbfbdd00e4b33e46fc45b5e0a538a536ff4854779a29f168279f6f25fa8c666a0acd1a4e0a28f654719340f0b06375c2666c7df29e773baf0ae3e5977b45b071fa512b68838e83f8c0980bd7e12315bd11c8d27a5a31f506ffece67a6c82268dd1aa86c32d2fd581c492326605fb898a71711395910ba65c015e91a7bc3deea6ca4c05505d984eae5095893a170ee46358debbf06634bce0eeb479199df6ff9fff7d983d706cf4f8a47d53d8cb454912edd4b681215263fcab0394c73bc579563761222463155feb3e421cd4919752c24aafc5c0178b3d7d5c85cdf02eaddf1625d3010baa20e855c06a510f80a6463fb24c967cae0c76295a2d2ab246d0c7b8b5be492fee4785666b7daca9b81bc52f531d78677725ccb0f2010c6dde6e19da7e71a929082fecdfadc835873bb6bf5c3a10e4d4164efbdb7dc52ca24a50cda05de051c063deed8d93a1f260f1f3b25268e7d5414fa3e586380368b07ed4f9a4c4713c7de919c399abbacf756553b9ac33a9bbbece7ac6db46d3b9cbb8e2c2885ae0f4ea92debeeeeeede755dee7297bbae1b4d46a6db8c14daed9b8cdc74ab3a366bb226a36d6768289876ce39e79c13354eaf35c60bfdab1d748d154ddf3d00ce4f11a157b3b6a4847ac60a2da3517a25caa63120515450f47c102608fdee62815e0df1b76fc719ec106f86fbf9edb8f2b1f8ad8f2ddceedd1b5befadd70e8fbb68cc69adb76aab15f70170de9033f4f7f451059b6aff391354d29c114c1e74ec503071eafb36f5d040db840435d56688ea8c11123341ee12f74c159a34d54c3587cdcc986a33333c13e4304c7b781493870926ac2b62ba6eb4878f5d8de68cdfeb8503070d1aab958c0c2d85b43f390324dbe4cf19a05d9f9a9e90409b884653adb66a4c4eebdac24f6bab2bb51d49dd955b85c7cec85d479a9c31d5bad25403a598c165259eec8b15d4d66aabfdec673f6bad1d7968968e7658976082d0645d5a85e18ff8e11389c1b4e030705c99f41389d1af4b93476dc15df46158a30d346912599a48cf48a143d6bb8b06b9922e76d0d764822836858625367d1faa3a79d3ca32c09caed507a16189e99a7b8a2964ca04d1ae5f1ca8b68ad4a5da4a095172e26e3347daef93be678ffbb57ee6f971d3d5c70f09e48fc00680f6f79865e389f6ee7d5cc7494f419ed81284b4f8b21c06d4aeef301ba33770de90398752a16cb2e7819e97bdcf60ee7297bb9cf38fb44804e89126aa3431c5899913323a6fe4bc795fbd28a7204f14d5d6ac15d9de48beeae844d17622caa64f8bd09992940a34498180d012a080de990654cc1cfa643ec940cf2226aa6c321f55d9f4c97c33da92b7803624822a6a42c926f351134736fd6c4556aa3ab3c8c8cba65be60429a51bac5aa7305b24ad1bb451d360763185fca06f107c9e9a71d079f4becba2456a2b5709bf7cab3add3873458a08ddfd8c15da737eb14b77f320d0ddf603bcc70f7ade48ca50db9e69e7a39acea212100b1e13c717fc3d160cdd6de63c9d635120017a164d9951ba69d114d9a63446e9671a010a761eeb7b90feb0c36700a5ed7024ebd3fdc2fd8fe57977db3e522b6a8b3a8cd24085b648815235e88c1bf3516dd18f81429344e8ccb9982864a0c840690148eb6fafee95ce6a65763ffaa831ad22c81d511b250ada73e28e34119a084d73ed42f1cddec070b3551fb8f7de4b5bc248d1121cd09e942f3466a1bc3d8ba4e4a0080423bef068c018637c0407401a3017d8c8081a7cc83aa454b1f59e45529ac0369c328da2ea6ad7d18e56f72f89eebaaec35dd7e10e7739e36be7076eb5d65a291330e3cceefed626dfdcfad3551fdf7b6fbdf88e36875a6d54286dadb5d6ba7bb5f606fdc423e9c2fe5defb5eed616f1d562ecf866dbeb5eeedcf1686dadb5669dfaa4dd825fcf5c5b1a1760fe03f0a86bcbdd059a54a96c3ec76b4775c75c5bf3c61d04d4bfd606ccb2c5d65e7b31cb477561ef30d0b9a882db99737de2505a270e7d9f814e9c5be9c4d674c74989582b165b8c3bdcd94cff6619f0c519c0d7bb00ee3a8b475203dbfed89cede541fe9f38ffe15294a525b2cabed4223d2817249088b24577d124cde5bc44a9336c31bef8da0f7f9676f7def7dcdd6bf3b5b9ebba71def791ced6c5d8e2773c4a00578cbb3b7e5fcee1da7baf9d3757f97bcb5f3f6880d079e2d0bf4366c4ba714e17ad310b037badc516dfb0a6a9cc890ee203efa0ea581e8b81ac98e9a2ffb1367d87c0ae97e6d3ae5677568ab88412aa2f871c1d9a3c2a3dba1d5d39c2295971c5f6a0329839fef34587a6804e6892de6895a1d0d2d0258b4dd5a9dded3adce1eb0e2b02be0d1f9b7304efa79e3cbe9fa0c8a2d3853f64c5400008dddf789a38f8343f9276778e502fb0e9d36eb4207935b02908da549dbaef0dbaeaccaaabcefc80aa558d7ceaebfa9aaecaf32348d5b979b6eacf5a6d9b8c61536a3b8b032240faf3e6d27bedb5b44e4a85c0a3aebb1e312fbed75e8b5b3930c6e3161affdb1cf666f17410a4432b877ef0419374365352f31ae8f01d11033f63a6272951ccd3ff69e203fee97fbef781f998f1c7e7f430e30f3d1a7f7ea8d1d167f41d8114e64db55bb170c721ddd7d590fcf4f3e368c0c48b0e68404031d3c77c131ffdde7bde9bdec77b53ccf8e38db50cf29eac32af06b94c6949a6b4848962b0f00c2bf529d525f04997a1fe7f9af878afdf93092016f3a6f1a7898fe951e30fea4dafc79f263efa4de30f6aac46473f5566b481c662c6d3cfd8e9c94a741a57f889c4f0ed670c665c614c848ddc65b5e8195b0da131ef69ccfbd3eb6fe2636ab26201e64fef03f3277f9891c807e6f5f8e330a313b992d28c7d4f7ad0379644c46600c795cf87ff8dab21e2872f8e3f4d5642be37c08c7da3ff10bca483161290c0080828e646b21a037f0361f89ef8e1cf988d22f48c4df72337f22838ace08927ae0001c5660c1c67d8c008e48d2b214f3a0a74008ad8f6746293ae8489302a8cdc76ef80b67fa92dcff9526db9b32ccd5dfe9f3dda16b7ddce2c90bbdc0ea167ec8b518f21626b2ae975c6aa7ba9cd8ddb3bded029a5994ae7e59858dfd1636d3cde717a403bb1dff75d91cedf66b0f53736daad084403646be39f9f4ad58d64f71e88df7b107f4016b06f36fe7a3d56aea1bb8ff177f82b1eb5457938d0b604388c360d14466048a1a44a9659851525d00a17b21c2a5a2c41a5cb8bca143bc69e45546c9bfc575705a84fa0e9eb30d7cab2b5d28aa3153579d42edbcc9b990395b591d9ba3ffdad9eadfb1edab17bf990dd3171ec9cd3babbbb75777cbff53ee77cc01dc970eb10b3e69c1d189a7eed4293768bf806d57d4e95a6eeee292dd6babbeb52d3afd74e9a47b06920d137c370eef603a8aead1a7a13a7e62f56bfda04ace9efa2698efb9c3a70f69c9e635b8a63bbdfc0ae6ecee9445d5a4439cf399da80b0844eef6f3c2137be64fd1a6e9dbf84251a9c6cd26e79cd3babbd3b0ee2110211073cef994524a4320466bc2d6dd5d8736359c6aebcb192e639373ce9965623e40f5cc9f36a8d59c359b7477a78911a29c5c55ae3a73ce594333b392529342ed396dc2efa2e7d77040d35dc3017d8964ba68fa3694e182dd657ecb19324e3f43bf85195962cf7c77774b33cd3be79c2fc345bc41ee9936bb37bc09514e6ad800a1e572bfed89e306cac9c511a29cdc0c5c992ed6e608756d2765a48370326bab03a7e6e870777fb97b8ed8333f2c82babf740f29664a8aaeeefed994f0a9c0e431f72cfa768437f90a8d85cb6e2160de4b1d20e21e9b89534dac0f268e03aacb1f05ce1b3971f368bb603a429b94e83ee3234c6f986e71a72c924ea93a5482a8adeacc9cb5a6d1b9b3a90b0e9b73fa4c1e75c7c32bb0ec39b984aaaf427c2a80802a6277c7a30b2dd056937ad7908260d8aa04f584d600de07534968921629025a3c7640d77b04264e71cabeb4c8e8afd0f54711e8fa7446679645672bd0f48fd4d6fd99ed1ec42c3a9b5436a3b2aad37d47c11dbbe6b8b1096b157b6ac8a2453270245fafd677363aa3b6db5240d5b9ef000a54d88c7dd9f785d41f41eeb76e36fd4865a6b1a3494cd77d26f685625f2af6a5365b05ca151402ca0a72bb87feadca94ff1dfbdd75bf9cb4acd9ddddbdc87d95eac8e8febdf712b140df9fd5286b550bb4ac957c195af0fbf96290a446d44ddd63d6fd693471eedfd7be3717632fcb75b526ce7dfbeec20b0ef8a9b01913e2736be58cbfe5300664d1ca8117ad6e74016787a0b5d65aeb4d6d5d8bbf7abf8ac79bfb42aacebdeff365c350613376eb5fcbc274ddbff7bdbbf12864e2dc6f010502e36b6ee917b96333aaf95683d496fd99c354982e6bed93947683fd03b429c2e689b63fb7fd59ce5aab541f83c376a8bdf9a83a64e6711bce6bf3ed61b64f6ad7b6368f48c8d74ed5b1ffda61adb5498093daa0671115d9b6ff20ea89a69452eadb8ee4cf8ed39e37d869763bee78d5d638d3f67ff8a83a33063a50daee5f5bfe4e18b160e972c06a9546699d934392182ced00071f324aa33c2c9bd46fadc0691c3a64c1840996e8e24b962f6258ca1113608c313ee2e4868b45670489c89e208a0eff51cc222a76ce942540dc14a229b5ec82f9d93e805af9327bdcbae777341a7bbaa9d61ec01c1d3ca9244dabe3f8e0f1a2fd736c0e5b553cee0a62c5ae55ea8a8b6edd1c7c63c70be68327bbf6fc9c244c88be400229cb134474a0073d2460490f5ad04390ed8a2d78e0441059ec7b551a7543ce983f2fcea43fef0decea5af36b9e0da0d76b67e786068db4d9ef283a4e1e2c1026fcff1993870c59cc07ab191a598c99af4893a9d491aa43c200942a22ab3ad4c3cefefe8ac0f8c10ad828f9d95258683205e42ffb9d8fa3bd74d66696719aa16140166d36ce59c30608adee059b4c01ad9cd010caae2c1565cdfb39c28ff5f2fc3839e10c5baa0670deec6004587256e4a28096fa4f3bdb14d601d1221ac8b4de945e1c6d93ad4d8168f0c49e5e278c11da955eb5daee567164d791d4ce0a6b2bdbf0a5880aa34db36711153825b81cd9367b2ab1a5dc20cc4a88f744623edbf8c1afabdfadba6fb90bfff8fbf8a37a22b1df71177e22b16e948001664cff6452659fdcb9b119bbddb7e02edc8d1230c08c09197f7eb80b1389fdf74462f5bd11087cefebd80402b1d51c5709a8ffbd8f7ff8f3c3f1a7c91c7d6aacbe3f11bc52a17b5a4521e0b812f2fdcac7fbc6186561c6bc2e8f6e44571b10d0ae586e4e80b6c792c15dd8674dac3c7c3136d2212bddf213251d96a3ee688908f0d19110dc328c8d2d9e620b8c315e210b90c334c65364d918a3f0a84aaacdb83205144153d85453104961b449fda112336fb086b4dba087156fd0867e3778436b7d835d884514ec0627f8596b3d8badb5d62db6d65e6badb536e7b0d6dae90230034b2c306676772752539ab73d9d050616955860e81b840ec26f38eb642da1a9a52c302c6b495377f7da0171e3b62b7e2f3f055a3ac06c7dd511525bac1c7ef1153ac88f9cb3a784c355d13e6aabbef7e1705870123a45856edda0e9a77055c75ff45155a4e9832275f07680a903ce20834b4e342ed68d21d75eebf6c4a2192db6af0a6b3d2cfc3185ab295c0e04733dd18033006fee1da9fc85dafa4060725c0e772b4ae1dc15c30466ab7e0a078299a5dd95979c683e0be48dbde9ba3f8114ce61a799caa5943660026f30ce53c19b8dc1137b3c0687a5aee0be3ba6705de06eb8a25860bb5c036da352e52ff758ff7c061aa5ef05dd3df4241bda50f4248e93dc2ca2298b09a6f5f277ede38c9b942a66a93abaf658d7a77b63aefd02883b8cd8e3299b0e343dcd700183dfa8edfad92f06c3f946362f0c6da35299b2c924a66c5e15da66a2c9c1844668fb3a95b289480e7a8a0a7d2a21dc16cca9291aa5b2f91b0d024dbf869f08029db3d62ad5bf97caed68e5f81b958d4ed96aaaa8c284dcd8a46ce078036be4bc81352132d5b5fb7bfd6f06d640142ab7a96851b99db2cdf2a6e30c560c52396bad527d6da1f0ed946de2885750d5da50ecb193e545ea48a7c26dc1da789b916d2adb363ad2e45e8849ecb1421c962aaa64ca368f7c4cd9583bd0f4c9946d7b5fee70bdd6a9e7a108c4ff3e78a3968a32bfc6f62abf5a0c510d394a69d1dc556fa2d0e4cd926b69db78a251b94dde9b0d0697f304e545e7108d39b5da8a8942e7d476fd1a25688d2d89c9c3fe7db246093f598345f7644d15f9c99a28bc276b9ad8957ece23a97537d2f0483e854deaba236963d391fcb68d4d5e1a0b850fb5553db5c6b62b0d202334fd7c83264e8e0d89a12e20def3c6a9fba45680ae50ea93fa17cfae64c69c41eea6080edb90f0b2ad4f07e38b5a8d748b05a63abf3012032341543cd00418b1eac5aeb5d65a6b9146a5408733c021bbde324657c7abd62592ecfa5ac9aeaf52621745b1c5ae4f65aaa850d0b0ab942454af2bc6984aaea31246128c24c92541905d810ede3921028c316a627cc36d8c317eab2fc6f8c6656325318caf1477cf222b465f918a366104144146287d90115f3e78a2657210d23340f870c40b6458d1327b161d010513475ce9c0115c58b1b96beea992b16711125254d9b2c90fc20443556726e14394994409b6f83b0bdd7bbffa56761cf244ac1d7f88c860d4c20b3f8c94e6913dfdf7768421e7d2ca7aac951d7f087d709c017cfae30c24becdd8e977c4ef4fe38f6afc69893b99283ff89e8afe8c7d1f38ced0c407e6c19fe1f41fcc833f63e44e02c40f61582bbbb2e35ce18f65fa982712ab944846861a7f68194454ca68500c0b13612316498352e5ccab9e482c4659c41fc452a41d9919516185204a2c17fbca9243eb8185756befbd1863ff4e4cc17a1f188a26187d8a9141a56662a85eaf0f645857e216950319333e890ed9b3c6eb4e6b9b1251db8883c6cd8c468b9b1a331a2d6ad898d168610384198d1620b4bcd084a1bad27221b96ee0e8018e6bbdc31967a118589449adbc41b7ba353972e0b06837c0d1a1a423a704392f252f0018014008464210610797a7ed18816675acdf995f96d8638de8c076bcec90e085041e3d7250438b1e256451a3073b46304100d7ad97995f9b850e4c003cd7ad97995f9b05093c1be70e63715dd75cc346fcf2e8b163a6ebd6cbccafcd8287c7ee98bd8e677eb1cd62c77a103b218c137cb8e511721e21cb23d48382100a6e4f10f21384ec094229fcb0585e5c722c0e8514560062fd7af1a070565b2aa820f6f80ad6fa955126b5658da8b0421025968b7d65c90112a40619001d000126ca08015e228450c20f275bb8c384615c2844b8a43c2acbb2949144870ce370ae3cc2398c9625c6951857625c897125c695653883e105250ac49063522b4b26b5b2fc91a13c2a99d4ca124a599665e964c2c25989049430a874a26d44b5b274a18523213f8ccaa39249ad2c7566488022530b67e1ec1454629c937016ce68344210a184009cca12e330aec6a41c026351ee90c04367f48623134683a8128ca3326a4359d292da5096e10ce3caa3f24938ab31c138da839e60088c45599618e764c7f5c163cbd16aa1b4b651a96efe55315f38a3610e9486b3274a65ad2ccb12e3988441e12c9c695ba8240c0a67e551c9a45696e12c0c0a69e551592b6959ceca70562bf374520e3161e1ac887076b29540027bedb5d75e6b67560b24947ec802dd29a54872487248724872ee5cf0204bd2976bb1c54343f88abd7287acbd5fecbd82830ecc64322d3df460ed4562af6c69498b9c962fd77281c45e2dd77281c45e2d185f8b65f66ab9960b24f66ab9f65a8cc45eace50b0fec901d1a721fb243766828490b167bb94062afccda8bc45ed94c4b163dc864b2571837283458dd5a8b31c6d6fbc05034c1e8538c0c2a351343455363a33a7922adcb895076159dec3a652cd41895e56c42194974c8646090f52c1c74648d37461a2e1a37ad9b1a5040a8c962d4b06123e7aefa40af6afd6bcf9d3dbba032b1cbe5acb54af52f9b91656143a481e01282068de6fa46ec375b8f02b5d5138327cb9269b0eac60d1c2c9a0ac70eda7b9196e34f1e4d32612238aa666c92cae80eae2d96b5a597e0e84822432693a18fe8c8f920e765f302400861f8f3b7b05e70215c98b8ec3090d582101144d8a19ab163049d191d584a07b01d12a0c8d4449abb52b95ab763ca1c90c04388189a481369325968344210a184009c9eb8ed18210026e8276e3a624ff730136028d5e9bc176932a74ae9cc6eb1c3d31d75504c57fdaeca84759d155dae93e9d13d41732a95c93adcae54d6e59e20bb9b09a10962cf7d135ee73eaa4797032fbd452a9de12e477bd01b260e13a7db82ca64b2088047889046fb82e7b5af914d52195012a009c6f64ad8d9f111e484978ffcf211aa5437ff39b6d8027b50c8a08aca42169d798f10fa5b704414ecf63102348c0db3a4e9833e7a4215422a134b9126d2ba1069f426fe508ab414c49efb43e7e29f97e8ce6437a4f0e3b542de69111d61d199bb2a95fd168751994cc70c0b88a3c20a75052014489024bbca280e73d210ec2ad26e08f202c02be6e6d51e34903103e903241ba415520d120d920a2906d28c6721a9906220cd20a59050483248ac1149062906e984a4916090461a4830482624112944a27183142281481fd24d0da40fc943aa6103c943b201021208ad104c3da9e12e5bcf95d6551aaeb4eae1bc52494a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4addd0430f3df0e0075add5edc65ef0343d104a34f3132a8d44c0c154dcdca0647035ad5d0a862a43e9031c386766fb899144a26e634e357349f9d348ca9bef8ac1a3ac3393104316ba499e53eaf1b69a86a709bfe38dbf52ba5e5198de9d5441452db13b168faa47733b3678dd4b5a9229fc32e7eea0058a2af77839adac44d1c7ff75a9778e1c5b4afaa42c59015a129bae0c106422f770f8aa795d680bb8e8669eeb48bbd77895d62c4ec41d3aa040f3cf0c0030f3c0c0d0d0d0d0d4dfb378f3bb08ab327a3b410e00e0ce4b5f168535ba1d0f77abdb61d6f44215f02de0051addbd06d1d02f42177f7a16a021cb87f2018b239eeed62dddd77189ab89a15b2425ea928ed405b2b343fafb39dcde9d6f6b2d0e761215add6128ed4fdf473b6b9d94a8744dba042d642302000000029316000020100a0704e2902049b33059740714800b638436766442988683912086511404410c8310468c318418e30c324534550172c2fd4a2ca22a0e21502697386856d96c8e98174866b2c61a8b23d7afdf69d01f888788813b8cea6df0c2f3caf42055ca66e61a85fdcac03e4c0c81ca13ca2ff5d0dc811c723ac5a5c51753922699e8b76fd88c65901d099598256ad52db85ba062029bdc616f31581b2ae08a63f316a3e6e0bf5779e677188d450d27c7f834b8d1deb67b6ce969dd49349238337c2bb66860cebe099d000d36255ecc0bb10de39e5f2cf5fa974a22c4b86519c0a10b770e37c5ca610eb9bc43d358f1ea605ffa545f891f645b9ebe1cd26dede932215996876b8364593dbc0c7276fa48ae0afad053705b9f747fb3aedc6c52799aaa4dad93a98d9e277c172cd6caf720a14e1343713b33da7a28bc6ba25e7fe5e7bcbe0532b7b358ada9d0cc40beea7a57c35141a83a1e1ab540b5b8a52d67b9143af3d3ef2ca0ad753085e5c5129923b3d87c61a35034ba79d225ce3e0a7dd3d17748745421487567528f018dd83418335a068441686fd07377dd783942d95f06c283d236daebe08153261f915a1a30c827dc25aad18cda610bef52d5fdba0b2fdd6b50f3becf53358e945e3b9476bac8044e6f1258028575b2cb400c745a6c02d05f307e59f8488ef72157cc97c1dbc18e8784fceb784dd99042ffaa5f5ed6ad70b58e3a18abcb379c543c1e1612baa5ea803192d603154c3395674a60dee44000b9a899e66190e44127e398e52ac4e51737bf425071b4725c9fd4127662f30b530a199cf36046d82a99836f078b5e2578f9d09eaca5a566c80002beb6d2013c4c0404325ed97f6d48f5fe55e85902dfcbda25001b245f81aa566aae124052ad8e8a3fb80d5b8667f464124850e2f62011179c59778de3d9d3ad74998141a4661ed9e3c296946a3800421fa94176444c2f1ed04f4fa0c3171e78845f649ef47a124dda2b5adf6ee126793e1a88215c1582d1081795404523906e00cf50eb8579612cec06e69e08a3819a8c30e2ca1d2a4961013be0d77b15b14de9690adb577ae2607fc6c5af402f206731a9d46f773a7948c28b7918ccb3f5ed3d6140809dd197b3d8362686b2dde4014acf9cb0bb4bdbdc0471485815fa1c4778801933c2c190f0bfc8b87bdd61d941c21fee9e52924743aa2fe177cf50ac49e7a43fa186499928c27504875efffbb9e9497912ec9de58d3a45826df2f9eac392c9fb627d549217738d807d65607f9cd8839f51ff753711fb46f9260e2d8cdb1a164725f0b9b45fc23378d84555b9f6236630b03c3b913da6b425fc52f387e96a89813f2060ef0704ff63ea675cff48881db12608302ffe12fe61bed740ad6bae300c0e4d91108fa648073bbdcf41947936c241d8ad92c1047fb80e6fb3c416e5556425dc141e4ef72b7d43ccdeddeee89df47ab0e049c5b3dac60c33cf4d1b9a8ae104d2198a3f6538ad7454c2d5f3d17f59ec4597f26da2e80df6e4277ef7adafd543711fe51011f24cffbb77ef32b2502cb771668daa23bf75f9e9b31db12ee50ddc7828535d3bca4758c269742c20ccc88d77033f705e86bd8f8173d268fefa286835e1837b50578b6bc40780cf20ddf299bc3ce4394f90242fbf81cbb27c37090c6bb85a30ca43d37da6cf3c7c0f612b1ebf7799b88c6233d97b53dc22ca07d76eccb350a157d8a41d35bd7ff9d08c006515ec3060d83856809fb7575786b44f541c355f0705b69bb1bd3778efc8798b32e616b408de588b8b22a4f79e5517485b727851a0bcf7e88f1a8b3f4f221ad0097b8c51edc0aa5b1933cf34b563043f4109e1cdf0ad7f9ec1b0897e4f0ba5570151f3a7e381e289cd1ed316357b55a5287240e075834634552f0100790cc49f7228f5f58da4e431803c8934fdb5ab3e8d81254f03563301baaa015979dc0a2813cb5b809bda6d8ef0f365612c26f3a63ea97a6449cea192afb1149abfb395e4fac0aa61655c518a016b942a14fb055b41e3ab5d1dae3ad0cb04e49206a3583b8bcb041a621e309c97d357639b139b8891047e52e58c79bb01868017720b6977891831f6e3eb719b134394c9d650ea5fe72a7d5375360325a052364d8491abc85a11c782e94ed804ce620dd0104a1da15542d2c06a08fe2b8c4ac3af0c3d4c516ed9104b861263285178834963604a59b7c07b81f7c8566480a17d8dd18479280132d2be7a90636324f61415db7fb716c66fe533436df83218bb96984e4099e1c792867e9a685c106f04fe3a7752d70a0baa37e309fd248a289375b3915b2b14f2d5b22c9d8c9db460e3c9454eac7e5882d8723939f84657b3693776c27dd57c655fe63c63e9ef01212268e8c93d9d60d7f093b153737984bae72e57dd80ca5e2ae3d7dda231dc6688846fa6c6f83b6106e0e1c515b3951ce99c3484ad0fc728a1a905a40607c9626cca705eca1fe7ceda15c10b8064efa32f3ba0e8c23210ba65898010e48a87dedbd4ff69864c184b1a91c82dcc3007fe82890005ca72e6fb4412edee020171e8fce8fc9a5c81ee5f0413e4f03e79399cb35ee6ffcb5962306e8285e6d871df80db262e82448fc9066d221ddb961bce4b310c15ced45cdcdf248eafe89d5a690ae17eab52a3829edb0071ec611204c1a89fea42960a29b73fb1ce3acd06fd81f9a4ef6510c16d83e2e22c14aab904b9fdc6fb32d54d32d371fe28f45631037095ffd9549c4f0b040ed3d7f265eb7c9a2480b6f6fa1b5315f0789331e0026d4b09f4ede64e1b57325068b862d01b9a982a3cf95924f58cde02534d0bd0ecaaaecf6cf4cbf9ebb09899c910ba4d4411bb0f0a895893aa0f46554626e5470a0c0131df94e921756011dc0a6e2c10c3b9237e23006334e84a018d4c8f346a04817975d80d4401feac7f704315703c9b071ad2fa6d01762b002c91e56b355d383b20e8fd65612e0015248d408360ed078f97e3d392d53d8356050231a4469080bc8f4ca11fd4ef41372c862976b7d1a5f829d29c8ae12debc6cc8931abd388eb04213e020e749fffe476e4f936e79394da8322aabbce7a51448dd789aca23f3626c3eb96fb5b1c8cdd74fa60d7fd30a6745b5904c99fe1d77ee60bf28dd881495f38657693fbd748ec47d25fd797db092d30c00ac234a0bdcd60f97c5575848ac7766eafab6b1ba5a47c826361f547c1d07c12df8ed49fd4a4cf7482ca34f7e0ab0424f3bec931d1dae1ac18043877a595a52ca61d883278bdf008df6b9c74b8efaade3c7e298ed2f4cb4fc3b8baadc34ecf7096fba9f59e4e124fe84d33993d428bdb7d621667784886b20d9c41fe9e59af818a5ce70363a7dc6e2f19440e15ae428490a8b4981e411cb0d50cd0e872fd76eae34a886cd8ad2276d480b8d3b062a3f3aa75ed3e57ff19ad4cdae7ce0394076f9ad41201e039da056c1384e91cb6e8fa482db720cf99f59492774e3087393cc269aaa17fc6b711d12bae0a78265f96ec31c249ca4492b46728a7738fe072f94dfac5b6782eb87503ae6e03d77a38774013dac1f2734ebc7020fe13101a69581240154411542d46f09d770825e9171b577f3221cf427fed8c93d03fde7e7339de08f20385649354f9b38ed40226d2964c9cf57650c6902fc59806ad5ca2b04bd5ea0814e8e2c61353edf6bc05d7293802f243dad2b6a51bafcf471212a4ba9954323ad21721a4d070e547a6489e5ea1ef5b493bf4f83c61240c2d2b318d0c9ac20b4d58ff8e67022c70f480681fce29cee73d5cd56e34e1814dc712b6ec5cc5c9b8eb55de842e649b422141b71405ef701396e3a53d566e5f9be722140c99375e2b1cdabcb6cb7d346864b3a2b6421edde3af40e72eec4b99c914b56798d5eade2def73c0903b6d656227e446f30db76ce1cf2fa834a499fef24619c31b26fc479bed871031cd5326804d6fa07982120f983a20a17bc717804fb3f6487a44bb0ab886fd729736f6a2a3d835baeed0ec047be5a5f6fd8754cd8f8535643cd44c8fcd349a63c75ae51bb222a0fcc574260feae6985f46a79f0be1c2f9980b75bbdd42845ff82e45382e4031dea96aff3c1adbf65a0e882725e68efe619a516340eae949b3b682acc7099d85a18565c4c6bd809417e03e657f10cedf3d8f4233a8d294d72ff230bb277a814c7a2555fabd7abdfadd3a9aba9a7a9bba3b05cfe4cf41af55575faf5f5757bfb55e57e2f125d269afd7d4efacd3d4a929397ef6124d9dcd3a5dbd9a92c1372ed1d5dbacd32831fce117688883d3e938d028054804e28866725f5a01096c6e6d0f7d2f2b22909a98828ec13813690d9d325c3630e6a50eb3897bf1eda42817c5f535f251f673134a6bb19201918a64299e2513ca7d70a5d5fc993accc3073a90a777dacc8d0f5a771a9bf6bc691c53ab995eddf46aa6563fb9f211a3ce34327d1c08e3d8bad2d8d43159a9b4991b67964abb09e1d3581e31ed32e8e67c8f43a2e868a92ba45125eafc3a92d1c1edacaa8e3451543e045cca7d3f5df7f1769c4ef771bf1ce7e171dc2ee7717c108efdaf5ffc6fa2deaed073fdd8d60cd92bd15655bfc4a7edb737b5bcbe86988ad171a0ed057ae293177297741fb579594885b178a489092abd2401f0eb309b7ee7dac966def803c96997532201811940bc5066ea153524e788fd72554b989c9799391a0b06f485dda831cbe0334b6f5cd728420b6d7224eedfe16fc4c9fca62468babd54c338f2e7c546ca6c13a038fb2f2314644ad4583fa14eeb5f996826ca5e92db270d6647ffbe9cf5e8d972171c387750d7eb91ab38f93f33efad0930a280d563b24fbc5c4ef8fb84db36c0a56d593c748141ea46b0104ddb1be19b7c2e61e9064dcf0f762e921fb2d2e94c9a37146bca8d35db2e805ac23b9bd5c226299962e8db4f06a0bfe98666327d6e7606398698c6b83522b2693bb9a0b176813e045cbccf87520d2b06fc856542a8b033885f0106188b4e120687712dd2e3339f89fe0b5df5cafff6a1740a3a6bc1eef86acf8cb3549047cb3dceb0af58d218e164ccc4decab26dd1e8fdc05a1cd61f7163d7884a0afba5893130bc7ff3d112c2f0dc088925c5bf67c526a3949b462900fa378da5c1e683fdc6a14bec75c16b63fc3e414b422fbea838fe4412694b40a0f1ae45722e3af6862ece817d9f8d72227825eef38361f7cefbe42e88b6146fc942414f749dff87d15184bf2e1d10288e927595f201b466d5b809a9ca30f65665e8c492b34b48a35110677da953bd5080de42d9cb797230a9703652b86b9f6cd91cf21fe4c9f4ac9f6e3ba795e8ed92962019a2686cac10025ecc39049fd859daa272e3081a2c8bfdc53955c4a481e8f6d31a9e4f171c1a0ba8746fa726c10f4cfa486090fc2c4624806d8d40dde88416fd4a63ba744c0c4eab6cdc449b134718607f71b9312cb2350010364027999a3f3a0508553df011ce7c602a4d240c709b588dee672c57957ae47e845df6eca16ee5229b0de341f481ee486dadfa278e276dc5c964218b88f238091f6b9b2c46f84dc649b31dbd7a7a5c6e5d497e8b34dc93e0da313ef403f9f60bc945e83ebab20faf1abae3c095a3e70fb44f561f3f086a6121b9b297b0758f0392bba9de4f7cb39a3d2cad76eb07adce12779eb108c2402cb641815a2e1430eddb435d40ecc66a191c1b289f2e78b90310132e5c011695dba319bdc58d5954ccbc6a2a3f5cc788c583d19cc3a051cb8b725293bb00ce73412a29d0b459658670856f98e6e9e3705e517cb624e9c0208baef6a76281dbff4684333adeaef4408506bfa23f3a8ce5d4105dbb06d1297aa54a1c7efb6b78dd2c09497d4bb6a5498dc3f460ade6c2afc8f97065305e000802f6365746b8a7ff36da968889b7dcc36130143311da74a3e322d88c1c9938653338cf64a97f38f5760b00e1f63bb6020535c28f45a6214ba71542473a528d4fc0e8deecc208311bfd761d8f7caececfa497b85bbe5c07cfa674392f2eb1d90d45031dc9831fbd601a510cc908a7178bfe66dbacef88d0ba0bfef21b535174c5bdc8348dd6385a30b28b4aa61f214fd160900bd2045235206ecbbb85f7cec17a67f2205a5fd4bcb16e01f780b440e367785e4bc3a8773432aa2922d0d0a1fc0829283da41626dc289b67d6009816d5004cb33a76554ec946401855de4ebe458a16c2ab8acf42f4fe01c9de09b5cecf14ee2c9e83b90632ad4cea7d2c254fccf361239969c107e231252c9b1d1e05dc4d245e21d174bd9789d61b5cf4d62dfcf1ad977913bdcf598236b1650488ef24ad320e3ab7a515d70bdcf9fd2b6e28c352457fa60ef7073592473d1e38ad529921d8cfb7db116e2024d9d8cefd0ce6d66bc309fa72e255fdc23a539c83f44817aec1c03012eed2eed6cc0ca236949423b12552a1034639c7e8b2c3a535e2cfd6656bc611c0ae710b3522bad97f57ed5766a4649c7f0804e784cf85d1f2a298543c4a0db8a7fd08efe90d800b44a08831a8d3b371fd7b830285774d0f05afd86371ad0b220d397d284d6ce68d03527d1f24729f10207eac1094b806a7064fd28b6ec21d664500d800b29936c7f7a121caa28736d6a4c17f55e51d07a3617bc55f46ffc68cf202c74330b6004c647e22ad5448692492a81637688ce0d317f8f24674277e75969e7f330555624e06483faf711177d2a958ab62366909f6a579f5112e293c1743d2c957dc237f970fedf47fcdc7109b28450ccbd2ed52ed359c04cd87c2c022b8b01b07084156a2f35ca4965bed5fca6ef9aca36edb824aae5df3e1995cddf5ba5511311e82380f22ac727b0655c222a5290e0295531d2b481f85a3737bd976d3692ecb95fdc915de0147b36369208d7296786f340f0dd2abdceb57746fa2779e2dbe43a9f697bbc05c8705ffc0bce225dd1f5dd680b149738954a802b2c52ad7c48d5eee78854dc75ff9fb3c9340ba78680599e1d722731dc5e810ff7ff01bff97dc5dfd8fbe272e3ba8b63604848ac1dd0607169b30562c709192c23f275ecf40274baea715cc46c7ffcb6c1b32cc229be49515d07580f5b3391b898b6dc0cecd0533a6cb56eab9a37963c7c2e896775d2c6c0cb59c35a70b9c12827ace57c6ee381f7a3db819eb6763e6a7de3ef39749b870ad34af5b81edcfae5e47933db36cbb71925951825e7f3d49a082b19a04122eca3f6eb60450d08a161bd399f09d06ad8649a8dee8d14717ebcbb95f00c889ca0d8b70789072d9cd02cb9f8323db09a5a14abdf3ca0cf190edcfec75718831e213872326a3b4bebc010ad414bd61d5e3ad9afbc9d209fd605414a3b80f42d0eb666a8698c29816423f9437d5bb7a5f935305a95b3dde7378584eac78b9b30acb6d64a58d0d1e84ab44de4b883ab89da0c212fc326881e39970a04f19a3e354d610ddc08e0223b7d9fbd359d6336f00263295a9b60aeb401f20e1ecc79aaabc2c48a180b5bf223f31af9d2d4544cb04bb216f5c927c2361cefc97376f33ba9d5d41a0c8b4310d88ab4497323057d02ac96dd50009eee678f183536b7cad1467d927d2916e91f55d58cef959c0bcbcfe8557d5bbaf4da5b71d3061b98c9da641079d802a2160e6ea94503b8c75e0af28ba718ba802c91a33829919d63aa43244a9c241570da74595da1aff440a713b0b6beda2093304822b1a10f83a432b831a9934a9f94ab33ee5b6cb8524e9477a41796b15274cfe6d72bcd40108652a96b36cc72dc8b71f3a6e3f58693b456c7a20fe72273a40f1c3800e535fd399c739435b82dcfa673c08b978886810a887594e8fd5eafc232375d3292fa06b0b52df20d5047dee9cbbe27b19a9a8a81fad8924e53f212b15d9b72341ce662296f8f9b6515c7ad2f964ad7869910c4e0eb30f7c1568d2ab9a93833e95b8e9dd5db9e4d923de1a6d3019c40fcfdb3a65601c65a7514e4b46c4695a943a5f02bb4c36f9e550fbd18fec5803113bc958a6e3e8ec38dc3353dc94171d2822976025ebfed87ea0b5abe3b0afd45b00791df9ae3f1111de0e3ccb056e101d371b6d5fe1f2dc34df157ae6b979e072fc19ab9113dc17ea68faf0e27989b459cbfd098f225aab7b1f1b1665913490235d82fbff49923f6a360df8a8f92955dc610044e2ad33f1aac81ef83e384afc74a3c1da906fb548a0ada5a5aca40e4fe33c69ffc1578e34049b56282879a23bace25093fb38043614fbc85049c9abcae992fd944331153b9de53ea8ae6694acae6623cb9911a25d63cc23e5ece4f59b23c900c1c4c010120bad416b02b5674e4c75626b6be603afa138233a089c206fb654869c2ed9663dd90953d69261ec865a6ca025143576cdd018e8a06bbb15172ccd9aa94cb9ffab993ac462a6a2e276ae384ca2c4f1a0385cb8249de3dad629b6c326912f4fe21d2b517ea938d230504d3a10c8437873822bbf20ef9df0c75b0b57fe566c4a6ca1d6c39caa71f950b8e5467a575c9d87a2f619ceec2a5e84e708bef9f624268939b9a5d371bd48723b57fb31d668d93ca9f9b624edd3d9c4d0302b8d8ffd043ac95b23463e26c46dff0c607e8145d4ad31818d54d55171ae65eb2bda9f486ae42737b91408d4dac37ce566e664a22d6cd017bf5dcf41add106e08995f203ba94be6ed5c2b9ecb7dc25937474cc0fa388c28d0b2ad44574341168c42a53d1602a4f2d58e7dfb1689810d244d662af66ae06da52de3f12415cb74f900034b2c0f3afcbec728fe3d879c4cc8cdcf5605d5c157ab0fece8f5818b6989540dbe67f0ab934b1077c0b2bbf2f448f3aab217df6a7453bf8a54e956585597d55679b84298202f35442afa9c73c97ea76f0792c653e8a2b33942c7720a1cb7572757f85bbea2b7662ca1216a40480f6c795ef4f41be473af3893e97fb84a7f950ac999b0f2ebfa0a575663efe48256627d1d44f48c9f194e2f8345d40ba32f1c9062f4d422c04d6b0482aeb10881142cbc35c4430a4644296ba257c0d25ef3f1356f10367581113c7023bd66a1a6effcd396ca4514687c9fb88ac5ec136e3af67d3cb1f6c9d525fe373bf268e3587a7a6b9cc670c7a30e2bce60c5425802f4b6da4e29c663b4eb4bd32b28e505e090d14961a23f894c611b73a03bf6003c1efbed0fa1cd564cb0346330bbdd0a9b5aad5ded5d3766a7ffa870dad0ef9531e265f5141fca4d85fd418e537812b68ae8a6ab8d19faed6c81073a90645efeca52a4ad9fa6f8d9de1e8364f1ce429fd755f53aa46ab705f9b833d3bd0df88a78f635141e4d2c9254e1c129af94b7b6cc1b3579e65cf702dded87a756e7ab24f41749bf9a31833822e9b5151a4d7c4a5990d206abed2603b29a8d663cfee114a18f323fcc8c806d8360b3830a550e1165dfc627f835326467c8c957bb1f4c028fb5b22c276bb203016924a9290d35f82089826174e61ab7c19c8ad02a9b1b55bd8e0b8fabd3c6c64e7741174d9e886a910958574240ab0f7a61ccd088411f267243c09054c5d5fc7f619152ee210a1fc4e7a901d119a19f41883629692260e1b23d238f1e23173e1df89596ed1ea665c0030593db114c64afc936e9bd6e420eff78b8e5a4554640d508014f09c08d2b44862f4577904388c0a8dc2671eb4a1351052562a43ddd5abce40ccd62c9323d34f0dbf82b311b08f1ed3338685e868d6321cf0de1b0d9324f81c0cb65bd2eed7699d659a04eb140a08eac62d7d7a6ac2239e85f3ac764755f89deb9816b35755d8e688c8dea57607e452684df4006c559cda29dc0b63596fd19384b4efaa8b93172092e55a752f1c897612385ae335dd502e398a4e48b7088f660611c7f55ba3079c860357d566d29b382d673b900c560e049f4ead38d0c9af20b80081225a84c25b1ff9ac689861d52347e896547e2dc45f9d6a01c87763c2ae187ec8773c326aa6df6236be004bb425577b5e50d30d7f116bca8a7c6395b190cdc86a7769f84e4398eeffbaa4093f96163da6be71a7e8614ef4eab7430ed6cc7acfca808007a55cb626297896ba982fe358ab5fed468ce141293c0a32020abaa0f755bdf522235072b18a23ed3b0280517f566d4e6df4a8bfea51a051c59c78a9936c4d9dc68af8432a8ebaf732e0901544dd90d4fa45e8c2a4f2373d01a6163a69fa77210e10992d178ac1a5ecea303ef4e9a483131080725f8af31f4314bf81f0da48322c7ae332d6f62d6b63ac1e0deb71283cc28ed8082b9a5e09516cf577eb55f05142cd684427f7eb902e8461a41708f50733085d3ce72d75964b3669fddc57a8d233a36187630cb1c30bbfb48fb047d64f5102505b723e4ef78ebd8cc22ac82f580330edfc213f70bd6ddfa1e4b1c6b426c100aec1b4e6cd547bf3eda6628255f2eca1e446c5adbd32e20f19b05f0b617167c03f5645b31c2a00935517583159a5a5d213449a61456f949785974095d1d83a7301ca1da6fce46b8606d80119726cb5c14c237d7e22dcafdb286783a7dc63eec0fe25edcc201c8b59a16fc382afcbfe393bac11b0a944f8a11d5f399bfb2a9717d557016b2a8ea8401faceec0dd332b0aa30c60f6902e34b5fcef9dd348a9a825db14512fe4808a2f71fbc087b268ba4ce017061519b8be5860616816e89d05ba6a269b2c6b067f6ab1c3056e62bbee6e530544076c7b8d37470a238792b419e726c9d730585f4d819d86ed5691c3a31938d0ef90db2daa712ffd302b5b804d4dbc1e7f1d83fd9bdd05b25d067013163a87e8fd2cdd3c646fa813b8831b9550e074cd4b20d23bd99e516461fedaf0f7fd10f62ffab9df5cc3bdf615e87747b59a4eaabe555884e202d660a24ddfde20694ca78624274c429b4cee802306153f2189f6d1c66ec4f98bfc4751feb3641dd676e90362343b62749b5f726d5de1710dbb9088212171e5b2c0e84d4b54c7a4196d660eb6052c20046ad5a79af2a1c86d0b28aef33f018a057e7c04d5e9cfa0738998360a3e9f8231686c10e517cf6ba70dc749515a80e50b7260420648f8417eecd2e727ec72bbfa909d604917a044294933afa6db7dcf41740b75db5f78e23fb34af849f9bdaea7f94104945b09fbb62f788424c379b0e5c8054b9e2e78b801ac0287910f5d88c138a20d933df97a986486450c6d96154bcda77652e47395223fcee7248cda8cabe0edfc8c6e7232eb47b9c0953261378f31d37ccc569df1439f18fb045b464c09b873b0b772479d167306c97e3009e3e643fee1075cba6ac8b01289429e93aa5c15e961d59eac8118d3800beffb0a3d49f660450591d59244d0d3aa5578b4320098357378b3236ce2e1828766829a3940692af4f9eee666b9d7ca5015529ef3b064ad91ee892af915921b37b84c0dc853e06bb1065377a15ccdbac22b59d1c558d33b29adac69698a139bb51b88ce075672fc6d5718ef12b940e3751e006aaec91c709a5353333f09711fb995730394152f5f6b4d52c14a737ffc1461ef934d47b172dd557ca395dcec760887e51cc4f15533d75c3df69ad6fa26d6253104276e5e9e9e4032291e9b0ef608a257b70f773a5e3eaa4b66e45e92b9ae9c64495c93c36ea59192fabab56ea3d7961e7831316aa1bf31aa5aeadabb39f62396ba7e734aacab30cd9a29cdd08e043da6944186682b996cfd32223b8d51b1a02e1513dfb063a76460af6008f81ab9c69fdf75d9b6bac5baa8b9b55af95ac4d09d4827564dd7c38fa5ffa0077890bfc287fe74c15df09d2aeeed07649a67f6d313d45efbfe3e72ac44b49c591bcfe22373bacd474a1655dca47cb0c6f234ead603cf22ad8ef5ee99cfb7089dd0dc1d00c4746def1fdcf0c7e184ab8eb45633aee61a4554094b89c2180d90ac84d774f041d7aa57e3c5efaf295574233ac853d941e77a363a2f2274138e4c16cace41c05b92932984eabd8241f18fe3207b89e7fe3816c2cab3a2b0dd1fecb8ce7ed971f57598ce3ef58971d82fd821966f4fded78c5a60528e543d996639d73a320ff5ada00626b8a1df6da27186819f1691285d5e80b45e8b792adc8539d8452d7c6ad22b38d9e140120186859e6bd0bba8f2e1a37c87cb7d584cad5da5ebec68e53cab166af7565a0a57cb20bd97357ec58d7c4e351a6350cc01cb7719701f7f7f17a207cb7442bd9b76378288a7e5b067420b2ead8ab3d0619e213cb19dbba36ecf56126037a76d6cba0ad13fc0bd0dd7ea3f5ce5ddeca2a0d9d381f823b2aa9b1045d84d20f5dd40f905e2f2096e764b075087c516dbb186772fb9fcf0ccbdfca5fd4e6e32221e107ed7acdd660fe839fb10398077b96a9f2001b640d0b2589a940698c906e5aea7b1cb2b013e566cb2040f860a8e2680705dbc57c09f45dfb7f4978015c2199d6dcf00914b719fb6562e50dd017dbc72f2ad60c10baead4e32e3d201d983423b2977da75043e42badd4861cd3538f4e1918e82f37ecbd54f5a257f0f5695b159af6ed29fbfee520657d891e2b606cdf6c28ba879024f88ca9c5f0582a355a0a055bc5069c7a6f4aeb105c8e81b9708da2dcf047ed1177b40b2b2724e97df609e7d34877ebc6fae22a2e60708167a025ab00a8afcfc49ba4a6e416f670487c40e5209a5f7e659d1e2bb6fa097701a7aa82c92fc17eb2729a6009d115b53d1e2c1e4dcd61ce5276359d7039cf81bd8b4e16429c5a434e380f9ff19685753f211bda77f15c58a0b0f7dba575740530e0b1b9ac7adbd5cf8231b75d76dadc40469a4bd155aac33d3014dd565790e11f04ac79c1e3eb07bb5462cf11dd2cae49e82f11ce07e676d8cc725f2a98ca554b294de72cab5db53c88b1e8e8c57daeb5401abfe04b33ccab9702735e7c3a38e43db884fc13766973d673e69dc124ad41625644bae61aa8e3b7869e5069b628cb5a815601192bc3b57ea72f3b4c928df4edcff98f65a178a747d2a024fe1c5019776f9b2ad8ec41ca9aaafafdd5e56323541a1ffc6566c77f1698bb4be2be4dbefba154a6b534a39301b7980c0ee3b3b4a341c133cb1c774da5f54b3b9f15f59ec29bb437ff5b2cadbe47851be63fffdd7a332a5d3b9371be0f37cfd277a90b9f3399f2702f7c73b414e5ee85f4975e55e31b2eb1fb57fda46f521bbcc6936a3119cf9ad954e8d19c9b5be5670617820769ef73ca8bd4e89b4a29cf45bfdbb124e3a13001a23d6a73d29e81150e7e5c80069fd9115d8a282ea026da0d2112f11b6dc50c5c1d100b539afeb0d087422d19034a24fd06ecc9a02112522ac625cd204b81b5f02b01c679a3e31d94480ba66ae1e6c9cc2789ca04ad234729266dda3572e04f0c45f08c0751b60e5c5e14032e9e9a44ebbd695fae3321c8c08cad87aebbcadcdc2fe7b6d2fccbde760598c8c2c140e9a2a8d75d082a4940921c3fc3a7bc2d144a3e18cfff00e76f47068d91e41da436f656691c4a001392b09a22297641d7ae50a34abc334b477577f767e4690b6ae1b1037ef0305580d5959f4068aaa88fb32f962b220509a94ef172d54ded6f2da42cb756659b44ad5ccd45b751570e53122f74be2dffcfb7dd90aa29af2bd518865221b8321afcec2404d027d0cf85b84bf51dc3af8ba74c34b92d8dc107ca537b18523708ffddba1a7c7552df3a785241b8089397ae4fb161fc6bf333edb936f3efcc961e17c83fac7a88e016b2df03a3387c799f7f18de5377087786a8f1c05a9267393c42b5f9708a8b9e02762dc6eddc9b8740ad3121fa5509144d6894e4fba500e01b0dd16f424a88ac7fab34e7fa91f7a8e578b3c735542e4cc22820102b5dce51419c8051420a57cf887c174c3af2705119a9708cd9cd2aa805e5c913e239b121f4abd5c618e626ec275814ad149155359107f482d5c358f879c9cde29e7bada823cdb1057d76bd8cef07e572fdfc2ec255450bc18b3205c0169cbfdcf067dcf33e9c13e9aa31de92beaef3b1ffd1b77e03bb8339713a6e3be94f78d54e8f17291867d70df95869e10ad9b35ffef2a80031548aebf898d2aa855a44c18fad105e1ab697c287013f08324cae8a5cf948657468cc70d69a802e7f386b78b804c6410b3afc277824471a0a95d6ec8a8bf412184bb3e70d30334cd11081acd55e6f9c26dbb9c8daf6925a919c0cc704529c94fe0d75a51a662c854ee0e4e8ae9846861972ea0582180b14ec207b0c457feb1280014cd9f445eca13ce27aa113d6a3c9400409b50ee1ed3bd035cab3afb48f29fda9eee826bc8a35a3df38165e47e0eb5a8db050fa383ab3017ce50a1b933e4e8f58c16d36d61b75d4a5ad71ab171ee204b2cd211cdbacc90e899bbef76679a3916c02b2c3a934722722e9a76da05679bf3711ccd795cc2d954941e904c03feb6879dac997d6504aeb8a2419ba12318a554ade3827187c4df2c93e0b98e2434663e280578df60cb0dab8c850d7982ad8539724a241e6ba2cefe6cb610d6d62fc34011757cb64607c8c553f7205d1858e698515fe61a68a015e2fbe1420ecbd69e158d7ad21c67dac2279c7edc08f1e8978fd55398a1860d6d506b8111c10f61f95ba990ba22107a120c6dc7910fbf329b0543edf897c5e75004378aff88dd3ac81090f1f4dbe90a595d3d08e5f39d14d389a66fe02136af2470da733c9db154061966081c33de6e40d721c2a49e6ac4d57963b8d9255c3c82c81d3b66f08f2cf388947b8c381bfcc3624441bc525d6ae07a6035f5d59bd862697d382e4cec7a355d6ac26075c50eadb7587ae8cbb383b47f17a1a1b1e9782dd9682fa8dbb12c6af00bfcbfc91add166d9a6d14d0587442d5cfb1eb3a734c883b692d87d8645cbc03b071c5e36f957394758d1a78b81caf1171f169ede81bf21f931abea8d4abf490682a29bcbbe79ece730b8c5d933dd909af9068c3f52f7c1d74581af4f0897282f9ac0220640f06d0937045ff81acba10df1e7cb62b236e1336d6a50181e2112585fbf5475d9f5a0107031ccf4807cb7da007c41e6206a567fb3584031f31b76b0ac010228391006ea262e5ceafb1bd7e88f0e0b537b0aa55c952721a1d9bfed8b8ab19541a202b098b4654dce3d740ee29bdf893ed9d14a0641d19fc0d4569e1a321f9b392711a7f49e1bc6dc14f3eb72d8a0e494c839d0bc77244ebc19d70d9211d5ee621cac7b7968e2a7ad3bc67cbe1e1369597bafd3ae5651d99018a7b6cb84bfa72aed0123297975b2981aa1751aae92f6d4d5c210fd0eb98472ccc639aa5c464541efe2de4b503aebbeb7555fd2a84c2038f3bfe9a8bdd2ecfc1b338b6cfdc5aa8f2482b5e6262158ff5ac45dc839dddf36e76629bd7861e6080b0cdd1475933120dadc3d631563d158c05118f45d620d57ac34ab50d7261c8a11b7f720538a62470e0a4effc499bc095ddd3337c6a81f40365aeb4a2a98fbe3afd85387c8cd82afbee13067c4ab01b372ddf50cb4b8e87eef54f54dddf49376d0522c3ec93d6b90b59f667d71c365b79e89ed486e5f6fbf69e14cc467783055532dec9b173d69d59e7b41fb138336141b54f12db7cced2899c1784d6db97f67c0d18053c7a73ed7813c49b73388cf541ce6ff3058c4d67db5ed2647d0649b704886422d0105eb395e6c93bb84211000ef369f87754ffde307fee2c1392464ed84eb129494f78c8391e49508d7c89ebe9323ba630cb605478bb51c6657f99fbfd0e2f27add890d6ab858d229c0f39971371bdd3ec7dd505cb1858ad4d358f1154108233f7ee3ddbc2368d49da0ee4a77c142bed81e26310eb5de37bae23b16f4da3c336da0bd2d0118c10599cff46571014f887bb92b6d8d7d3891e94cf1e951fd7d44b45f8dff0ba1ce302abc28fb837933a3894296b01ddd1c61afea2fa38c903256d9fd2f4595ba9856243260fb622a9c0ea6940d115577616a0dc7876338caeefce9022705888292c7af70bd98dea6e0dad8a3a61f81ff471eb0614e340b97a0acfb05bf49f311344fac69c2477bb60760622b42025af8fc471c2e26050b106ea038d2708f393edd6c0c90fa6eb7c2166d00d9e527420e3a520a77f0e470087991d6065148c4ebf1b5ad9bf143f3ce470b28279548e504fb5be24fa4e00c5d803215047f83e53c300d0418ff0b51060fc4a9f038f93222b87447b12794cb98599e82c28e13aa959a961c7fbe2906699486890cf372be42a49de1b5203681fa2415151ab5ca3ef7f64ba94d2e847081cb232f1219055830f261ae55e57be6bdb283a5bd96c55400a84278927dc1f495f9957214b4e00841710e08712a4ba5a04109c7267e3f9c2aa77eb3de04fa7d510a008c882d95e54a57aa3da30d2ebf2aad3ce57405dc9164f3583f2db3d656788e9ee05345f99642a035db23a7d4d3bf6402baa2ea387f6a97f85d23dc72d0bc0971c4879ce2586c133c5e5ba373b302f56d44452e79eb4f7f4bc92bd3d741752786896bad114349412b72709da1d81361e43f1ea1baa1872214daa6ec7b5f87a5d8b2fd76ab5e5f35911ccb1ae7375075e0d0e8a3fbf34f667766427cbccd16c4ccd3a9c4b3773daedcc5ea8ca29325552304e23b706ca1a5866cadcd222eb042a54c09e2e43ebcb99af6b81625dc36b440eb1eb79cf84334322c52a44f52c6c3e3497dcac649c84001ae2da56bcb1be261257745dfd80e5f431e86bc92b58accc5a8c4e0c82b3044fea12535808bd798ba77377966614b1d65907b39d0a7b395c099425c1e842bdce3c8fcb5a2241136fdaf523d8b76c552d37185b4a60359d5184220a0d9b9f9c872532fc9e98bc7719f905c00bcca46e4d469a1b2aa04411bf6db32874a3a1a94aad6992add1d4ca85c72af36df9facf825d22209446583d977a30e07a5ad6824dbc9be6f7d66f89dfb4342efc8fd319123c9028c48248184faace8f9fd7ab4b6b1eef9dd32358b33b5e007260fd55b47c5323c207f64ea0588675d512563f7da5ce604325420ac3326c66c542d66336b5a296ab844c3d8cd0a918394e3f7f4d4a50be2514b3cd63d222982286c2b4a88e10136339fb08c72f867a9ca7c157a74ddb2282bed47e0dee18d8fd3048d0d856597bd19344c3e442a8c5d19e9a304b8a3a334f332a12c249a19521dfd3b017c84addcf77b9ec3c892659a404603df85dc3b91dbcb7483b24508c7eba2b2b92840757a2c7734d6a03d2b84f026f79f71e9f0c989147b0531319f842b46e860675067fa9b355ddda5baab43e992d0028d1ebbab48e58490ea7a6e59ad33f851be3b7e99d118b9d3ceece970f83bf9e66e4a5170e5b2870dace34b9bd07e1e7fcaaecd5af3098f00e5571e52c0d717c15c1e821942c7810b4a9d55a2c0a432f184b2a4701daa09e1ba069e4d9509ed5c83f713ca6d46975a0506cf05a90a2f46480e369edeb50abec4de23d6827cfefc72d37efdb688c67731e0c68561cc6d50727967086f48a1a00fe092a095a417b1b3299e76d7e49b26cd3f43cf42a23278c5487b708daa77a36ae36390a5e0af6d50b8b048b289723c24fdd4644609a4b4a7f9851568daa49656b0c641a0f7f11808c7626004e6928711a36f7ef5a8ff9efad5fa4a909cc4b345e82ecb01e9f5429bc935c6d4ca5cf3047530c454a5c9fa63dd636327325fcc4344a89db7c37f27d70adbe82eabcbcf365df45579cf95033c74fd094d3e631c85adced52cc375f6803264f8926939bc4bf8158275126346a63bc736869047447b6a91512be7263676b8474a120396268746789fe53158aa59a31c329818fdef31514513319694b5758e20005103bff8340d0365c3a6a1c5055b3839f365eb1129b8d0fe498676776cc0d6f3d8f9fbc43ab6f7092caf5142641f2f6b5d347549f482a8154cae9a9054ef98f9020a4373b6bed121bba388523a5690e28847c1ca709f090ad97594d9a2519b311b7a0cc6572486bebb71a79fdc1d42fd569b0ed7bd96253425ffa4f126cc4068f173c068c685f4cfd56b533d26a75ee1e60e408432c67591615c9775efa22ee12f5e9f1d80583a3c36bb66baecd1c515271bb70fca33c305a64c3d5a6c031ab7cd10ba448b3e88fde44d15722dd71d698444c36f8e81fe46a110b7da2c0be1631242db247a2c2243112222b161d21887e84a33116ee8a0952f96157fa847eb906c5855a870a9d4e832ab472039b4d079ad5a3c1d549895108a5f77699a42760ed680ce9eb61c730c08298c99764fa8b88823f84442634eef1d519f83d4b94ed78ab54c6353ce963a6c7a20ff8ed5fa0ecf620c4950c92fe89861e9877f63ed468567fe4fc79a64f47b36e6d891dd5a11a289d89fe805176a8c1a82c1622f60ca36aa08bf1322e95b92ee61fd3eb49c18a57706ee454c35ba28dc972148b16c454a6d3cd5950d5574ade5350657bbfd991bbcab31b310c5d1a0fcecbb768fdfa621a2cc074d4fe22c55b1c6b26c379efa74997ab986f8381f271a72474aea2b9fee5d356b71cbc98c62137ca0c5ded0b682dc017cd67c41b6478964dcf4803d0c55d0e412305809eaf4f1ca26847c7a452e482437f5193ad7d19f167777c955a4445e57ae3f1fead41657d639074a912c7449042b9fd68e94a29c0be37a8bb1de7ce13d59391cf7573b56cd819db1f504638027bf092a971721373f6e6e60271a2ff4feb7f7d50fc9275397af31a81215b3cf79321b277f99f93db4161429ff1953909c1dc8004b10a920110513703827545060cba8e3e97db8db87f12230b910bfc2ae39f7447ebdcdf8a2ee0b0eda1f847be7205ada5026846b140be8aeb914d3c47fba1ea6423388da1dff81144a983f0db8bf87c55dbacf84530cd4509c22eeb268525ea3882989a5356860673950254b0dfbdbc08c0a1e702396779e95b11c226eead67c92621a66ff7c7189ad03bf75901ba2528f0881ae900fcb8d37ab47f6fe06e3be9c43587236c2d642de14d99805f1c4e569064d242c96c5d370327c1c3e029270fc1e3d3e49e8cb7cda29939195e8c8b15709955c00094c1f04b129c0951a5ca975ac5d1aad0c9030d955e190cdfdbd970095d18c319c7d5ca35136aeb2a701e37e40b8166a3706f421e49b91f1781e7b8f1509934932ce72a8febc61f2107874eb55712b340628001cc7db577b820a91307bdc87c09d61f747c1ee8b28fe151ab1691eb414fe807505e721fc9fa21ba51ebf9f50ba9a55f7c6122038b8763745ad156414270304db2e4a35f95d13b0ac93517b4c3e2217022e8c65a54d9da44e4a91d2d2f0a72c54422983e8284a155ce0d182f7384a6ca28801382e484eeb344632c4a63abdea380961a65f4d2992f202b65704f368163d758804514993b96c3dfc9955e353d02b594e376887b1fe104b60547a5fa2799753312b6fb9d555eac772064df1a2b7a31e681f31dbaf05fa903fe3dd112e62e9fec93684a9368139cee6997f593f9c154a5c12b02373e6e301b2b3e56d4cda24b756c3de3402a9ca979b1493eae1733b570ee6cbf07a48f693a345c67314d05f6f39c40a703dcf14b866dee443f2f3aad0219aec4cac2b2372be619b69beb6a00ac1020804420ce8f9a63585eea40275b555f29e5af47d7a8101e6aac1184f050eb157a1430f75733113c7b3d4c5760206200c408b7174aef0e4c049278e8951bdcab8a15fe4953999c349caaf1eab00b04eb2afe96f0ee46d84b0eddc6f38d90f3c8ad2f20516f40ed784632be4470ebf753201bc293b22b1467160862ee3f11cf0b850fcf0f9152ebdf1fff8334a27d228888851364da3057819b62e0fec8969f9f63f85ccdbf2134811147fb0366114619b3ca98b1cfc191f4e185de21cd0794dcd6fe5d90f8c101a3e1d6134db68a4916616d4206ec77d4a1a3ccc761d0bf2aac83ae6694c29210b75df37b6f6b37442408db26b8d36a30556bb4d99d50469bdde0fe3a844857dbd8ac60ec8ab18d7b5a6c2a37e9d67c84fe4c7b0225aa3f3616692a25b1486f44a6d29a42229c3a385274e31ad92671f1b92f24b3c4bdd7189c2c518dac36df16e32976c3a77e4130c239edd18575a1c01fc5ce5a82c3027f00b8e77f03187b3821c51ead59cbe6957c44324a7548c17113f03d0223d16a822ca414f1b1ba2a9514091bf4de602a254b214fa30689e0dbd7b7b5ac0412cf7a2e42496b3474f8acf211a2046c10450593f1b0d7586d5644453f9257d7120d20c289c64d7d5d2496af930b7b8b4901d8a852d668f165d10c9b95fc566804e2561705e21551a5283f1faf73a6b08da582eaf656b9b3a27960ed392b1db68c173c8f002088f9a755556b467134e62481e45c695b56a1163c03c0fbf88c2b49e929e06368e4554b625e06b38146a4491a890e5fb374ec361ba93a1b15e3101ab7d336180779cb69c793c7eb58af40c062076b0b6e3500e98bf11d9a939ef8cb77503a7b61fbb57321976fbbee949e50c5952362650cf52ca246c193f7f8ccdfbc9e476014bd4321ff786d9d0b28bfd134c84f33607cb1510b668ff0c04d09350c0dbf21f3fd01c2d01df0b12ca6f74a8124c87c7bf20b53b06a146d7d2acc9ec88cfaab5711f4505c33cc80df76a58589f7f9849c8f723d116dee6d7d673f56fd559d5b76a448818c923612f77100a9b2071cf2b932b489aa00a28a227b61a0aee92b226f83940294036ee587bfa125885a1b51526418e26d32a2ed43a69a04054d0384f4616982a17470ab94415f6a509760fa613e4eb00987ffa1dead20f60b4a12984c68d369634a80ad2a3223ae9e86576c29e2477749256c1962ea15d024fdcca34acab32881cbe85daa7a129af5065942caa2b822c4075c2f2e3fe1f6e5ebee3b6bce6c57bdb521a4aae0f819c44277152de237f16d27d9753d80aa584e385ef3094752ba7a0e43456613aa18dd3552d9c56b386b789945c31de01341ea68f320d2281b4e8dda74d524431de4ec8dbf3dec38850b027ec596f8d4f926e357f5cd892326e522a902b2ece9c04ab6d21f06aaa38c8dc2df04f1bad9eaff4f137a826b96520c436cd49e6910312542afe80c29e696a58edc8988783cb6e6aaa168845b14b19de07e8363125c97a8abdca943b7ad546d7dddf387e8a7e89d00b1cd35ac97ce9c7c4777f139c1e4a18a1423b14838b892a96ddc2d8ba254aa9c16dee5eecd0e0ba8e820dbf1b80f0f5f3c868fefb5699ae49507097fdc97ace03293adfc5731343f4a680d8c3891de6b30e8eff323463a64fbbae26a0b18e1d63151b68f5c918703b26e9430f3ea5b6b26b8339106468829c81caaa7d4c7ef00662de87a958acb8de5c600a1bc43cb52e3b9429fbdc01f9a03584c7bce957902f564172571407b36c19a5a8ba5f379cdf2b8620f3eed6b2e37d862809438206c6dea62b8d06a9631ec638f90a8e0bac7ea7b6be05a9106873ae975f028f4f501ba3b90c5fa6e703140d668ca30d7acae65f6d61b90f719745b2f2f96e5bab340705d7a86acb9b0b93e5ca3fbab80419c992dc6ae784aa197fdc2119cfbc4570ce58520777628391c5012b68f053d24514d3bcd353590f90f00b07c12a71e7a8484412502fe806505449a6461459cb57c0c65737f17326aa1351f9d2dc79cc47d20e2ded2044f5616d85f72254ea3ad85a3afc346a610eeb3379c81b1174f923680a90da52be45a28a21bf87caf3695281716007d55f90115a894f623c210294ffc581e1a9f121f0ab62504d68f466c7e513b2509fe197163ee3b1e8037e0ed6a4592801fa928058502aa96cdbe8cb3bb49635c3593b8547092b7eea8d721cc21c770425f0bcc7b6d58d0fd7928c1e12156b045eca58d6454390002a8c81d3d51478e3d536202765785fb51b0a63f4a09bb6ccea31075471dd941fac98e750b3f0d745a4ecaed30680b364d0b85d2a38961bda81c879f132b3007aa1ef54a0bf24866512feac7076dffee45fe6cffd9b6079dc40e16645edd49f4d7aa3046fb97282b3aa7aa27d26b812fbf7b0894da7552ef0b7c5ddbf3c4f2ef4e6ff061aaff61b5873afe5b90829aa085d3f8a05fc351e8363a1c3d8b72e28b2ff4510d425c68972f33e0795a96ac208e2b7b688fe493c877e4208c66f83e117e152da6a5450a5ee5d704b84ab1a2c022c6b76e0b99d90011d55ba11daef8019da1a7ddc3bc4e6e87a9a6c5a60f66642e3263d383e13d5e4f259908fee96f164693d10089c55523f834a7dacfc69280b4daee4b4182603d3b81aeb62ad4a3452fd6ee6c11378ae2fb0b9184e31eb3b779ce4191ea6fbce50f2ebb7a83820e20a64d0b18063eade90642f9e518729c59b44a4c992482b644cbe7cf1833c462970dedffe40bf09225da904810a08b62f0f7fa422397f8a4faeea94d1cab261feb2cbba3479fc5dbac7d491fa7c6f87c64e73ba37818179d9ecf67f26b2a11d2f9ee8a831b33b2b1c514decd1dd84f4921f61c271c79aa319d62005be196c11e3e73d0114dda515208253c4bfd2b5a5224056bc62cb8792bf684f3d606a15018a851d1fd1abab9b5bb0bf03b8ffbb4260ec103b6f9a6c0d3c8ea3c45e47cc42ce0a095b34aa4a9a49d9c53667b8a00311e2adaa00e90000d40307df35ce2af54da7db0a2a587036d719c34df906c4d24ee88410a43ca074bf50cf90e27323093a7e3e9ed5e0cd7ece9f203082c0c53b52bdde8ae3a44bbebadec56d1eaf9584dca8c8db71012f8a98af2b1021d7c83ae05025d8d9340a8531919b31086c48ad4347f751c7beb82dd25027631188327384aa4d9bf81d53373a649b38c5facfd80392f9977f72f924611781f59bb00f0885beb756b158197a23c92007de7ed39e8557d1ecf49dd4d14b0ddc94e5bca66b86320bfba52932b9fe1ae755ce892378730d30d07315ba2392bc4e895b0611f589743ef50fd4ab1445cf9aa79ea728d0f1cc8ad3d3634c21a61b05fc0b86ec5fc14be31bb66c17f35d1367a5f62ef307645bb407b6483c9f35c300d8b485b24a5ceacbd73202884f9205d6a6511b1ae94ce2da64e1011a8b8dff57d35422c6b317272c40aefb414212ec0aa525051f6131eb443be03b9c3faa0d3c2026f413b307a9d56c035abca7e8d683d3ef29c76d39dd69b46a768a748a75a3b8f62bf99038683115c08a43c95317ca20976c3b403c2c26a621cec62a82e9c4c22262e715b6969bece155b1efb0e60fa7913c3c30ec57220b3758e0ce5b400862126317c25bf72dc9a520ea97610d9bb0818190d657c2ecdf5ffe10e0e642f4e92b73d781769864f381b0bf551199436997af111bfc52be47b542b9e1af05ff9a81349c705d419b49d255cb08b9bc14ea21c9397c2c593ba02c4db2e619c78efb828f6f9b2504d1f7bf6c19d66dc3f5c2e159c4b0fe14e1462b728dfc7dcaf355eea0c3acf8dcea7b51ef2116f8d893e9e302bd1651cdecf2550d3a9e5f5fbcb9aa0413877e817e671f05123008ccd680bf681fb03c15a7261e935ff1922d6479139d06bfcbc8e8400fef123760c0f6cab874c634d382b3854f103cd2d9c3fdcd6ed5662540f944cf92b11408be17e161356ff97fad0b0d072a310ac3301859428a686fa60feeb13c6126f7b4962b51f7b2090917f7b9b723c9046aad2ab907a2f9430762f7353ce3fe41a5348199cc65e0cbd4b8a011f3ab0d8a75d475359b60e82f4b2ec1f1db37f0055124a144eaa9feb3bb839674dd0043bad5dd8133c98b3309bdb396dafa7cce84982be34aad977dc24763522d78812c650c77a8096aba59b813baf8b3dfbaaf5315207c2b3babbf6f39cfb404a087d846b972cd8b2903b6d0f5d925e0be91a082ffab11f960a16b38c794f9001619b4f50f7430bdab5ad81e108d5de970279be24a02858582b17ea00ce0c1631bf121864b5a4a6b1c6a7072af5db140805ba08918a832b4cf530560d1d96fb20e24c35f3acd99a502d11e34b16222fc011e03818e69b3df611ae3ee6657d528d2ffc006aee6a7bef91524ec03e033b61f6a10271eb84d9a4e1c3d06319c40345a528e64444a251720cb68a18cf2fb9e7afebc20c2b5e5e271fbeb8d8e1f1d23e5d6b1830e0bd8414e5d89603f62a8d30066573d07ef38f235e89b110b8b24f048939385979c671b1f68708b6b68913b6a0e7b13811dda9ad33cd893fdc78f2daf96e3999bcf726774a58ad8a4b13b1d1811439dea2a45a1ddb73bb686156109905a81cdadfe62a9e50f8cb2713eba56b2a43e5b1441f24d0843e41a65483ad23e417288af689e2518f440f207184ff6210c97a9f0177cd34bc7715e9f6fab838ba4a086b911bf9e6ac38f5e91682442e1fde277b6d04c57700defbc94723c7acf8e94391aa621f279b64fd94fb21f7cfacc767aa8cb618e764321cf7497be9b37f72b1831904f4ef032a2273a35a20da15a903dabbe387d562b8beac141333e58d876ad49078a78feedf2bc8bce27c058301981390cf18a372cf0aa0583f00a046c5b113c0a170b076a052a1ef83922ca5fe299bebda12f08d8cd24a8b108bf6de7bcbbda59449ca6f0afa09f909b5ba153c70eb6a3b5dad89e9148b59a9e710dab7f3c89ece9ab7046d4d6842a1200521b77808e3a7eb2e7904633d7505ac0e42183b3a44a4342122b4671322b95dce2644707b3621f2b34bbb9dc80e8820a191a200410803053d8cd8e991ef9e36238c6c0a13ae4461c4a28438a16497a21348f66ce284ceb7671327687b367122b64bdb84159a80b2298a0503895ae7ac73c6105e2f3a7f8e0aa041f40cc3babf3a87c760dd4f5dfab02707ecbeaf3a00ddd997ac64e9438e0cebbec6ef257988b6258f7453d3d806123975a7a24911284d8a00edd9a488932deed9a4c8cf9e4d8af4ecd2362932830deed9a4c8914db18fd20e6b8e66a34274c708375fba0e690aaef025950823b8bf78bb71d5a9e82d7aa2d4c65fcb16bf4e5cd15b043d9145fbdbf24b62caa0fe561c3ddea3952c5219ddce60529104cdc29910415dc20a0da38455e5e9cf0f4bbab4e79c298d9e3edd99332c4f9f0acd9995a7f42b6dcee4a75f737346f4f47629d8fe1ebd62fb635fb2fd3f22b63f588d9052623b6987ae0f26104c9082ce09f40a4fc90f4484234d0ac315cdf26158c515cdf2a35ef9f0963469d4f96fe7e10f0c454f54d1f62910ead314ecfaf48a5ddf97ecfa95885dbf1a61d7b74aecfa35ded55f6d1fecfaaa6d825d5fafbc8a4e7d5f764ed8f555740bf8a5a764d7ff44158d0aeb8f34e955f449fc120761d7d7a63d7ef925b1eb87f8f51ce91b4d77a60caadd3665d017e94a9b32e8abe89a9b326849cb49c5a64994348b4d1fd3f774a7cbba844dffeab7df88864308e74f1836227c368dbd74992d507f96f5ddc9a69b06d9931ced4fcd016b8368cfd2beea00e0b6240fd19e3fabd831bb9c53b6a638d90b309f8e3c9e4611f6c8bea74f137c10ea090baba63d4092030be9123d3958e834c8d5a009d73252c0aa5e41a1568385578a1c58e82dc145102cf4c4aa9d0851fcc0c2af0645108183d12762708385601584606128564d77c044102c14c5aa7de88e48220856b50f33ac408921988a58f50048c0450c1666d1f50c9ac1aaa640b42a6cb0308b55d39e223458c822ba5e0501ab7a3a51842584e8124a088251253bf8818524b16a6a83190e1696c4aa5d053598c1681744f4c0429413414a100e16b6a862b070e574ae6811584499b2030b53b4064d703ca9154e086ce284c460a18bd3e9e2d38428b0aa1d0547b8800b1b2c5439142b7471c309f12846c066142362b0aa553158f8c2e97c6155ec1bc10843b0aae9135338b1b3030b575de848e107563f8a11b04aaeb27f9844758009a4daf463b0b06a991844f9410ce6c384c2065cd860d30b5092aceb891302ab9a2e718221dc6014ca100d16be10abf64210840716bee720091c1bc5a181a64038192217a30456495274ad9a0256f5ca4f096e301ffc044982602d88558731c4aa7d984f34a9edf8b0120256c9e905284518302d901a060606060606fcb0f70203030304e61a080c08f5c13d2c72ebbdd7af833bf4dedbdd7befbdf7de7befed94dc077beeb5b63953daf73eae2f0b4e286c1b7f385b395b67b78638ab35c369cd681b4b71166ec5364b8a6ce34471167e285b68e3ef71dfac2067e197c971edfa583919241bbf4c6d631610485bb6a01c2ba4899f202d5bc5ac987db264cd6cbe0fa7d52013b93cfdb860991cd5503522e39f7bfa343164df5dc2802b4f3f3bc85dee4d4a979c3d7d6c50367e189ef8cbc260c059d92e658c90762993d343874af51366b54a478ac5f31316ae82983e3503fcc860938167e31c197a367e1cb93963daf80f509b3373e31c191fcc978d2c260db539033a61052004a962e3e923c4c65fca2861db10524226c0507dd9c0116009d365238800390204d5571773d9b062b2f0970400dad88301d7bad9c0d5974c8e8d2077c180b361c574c170845026a7be5a3cb5d5ea71d611a6aba583c9fa3e9d7c5f26a7b64435c8a54cce8459994c8ecc8e030097dbf83d1db4f1b7788474309fb47abc081b3f0086367e9e9c5d92ad9e4dd636fe106605acdcc67354000f74b3868236fe281b1320b7442677367e56cd922a1d9df7dd4f98c7eaf961fffe84d9bf24ab23593bb585af2d4f3f2bb95ddea01ccec27f132375033afd585879c241d133d4c5ec0f1bff2c6f1429ab5dde200f4d3fd764f34893adb6f0d5a6a0dac2569b72a79deffbb43ee99c8c84ae4f3c27243567b1b07c7d50872723559f744e3b27232394e8f4233afdb470ceefba1564c3d94c3fad5c6de1b7a4e9a70607bfd4ca260592108c5de472fae46cfc612ba865ab2f1a6ab5853f8b1f523174af48ea6823539b76b491d909afb863e48e36323a5de779187f1f08e68432f8d8c8f013ca6013c571545111e988461b991d1d233248646a9367cac8466cf278c36e999d15911484bcf22ba38d4c6d86dc0c40cec22ca38d0c1267e10d94a915e40b8036191e67c9d4e68824b280e07fdff77d9f8c920bb046a34c8fe94392681a6d6496380bffa74b6f1a6d6494380b7feffb234b066c999ab370a964329d4ea38d4c2b575faa1cd692ad5ccb7bdb0202f348d498c3330d914b999c95530bea4f40a796969311eac28e8f4e0bea6403f7c45d36c8c67fba9d70b5854f3f4ac8e5f491d9a92dfc57cbf0d416feb0525ae78a4af9810dd2507317908d3fb4221d6da0a1565f3630e853bb630dac6d43e804793a21b4f193339256dbf8b14c0e0d357795d3099c0ddc9ca11bb76c30e064727ab069e009491a789c856336484309daf647d6380d46dc55c9ef7368d0a181c75d93b5f187d357c2d30988a53a9135f83e853eb22d06099da4c4c83901b4f19f70f56561f84ffa8626487060ce94a1e32e1bdb7117c9088fbb5490c8d4dc357afc324adce55224e0c659ba9451b2f1977429d3b3f1937469b26dfc235d9a721b3f8b2e4d421b3f95d1256a8fba875dd1256a67ddc36e912e4d46362677380bbf4988a79e7c72f5a4e32f0cc31fd2e68c69a8660adaf8cb9311a15dde2015b234dd72dbc4c3b3cbd3cfea565f7fca71d74949b6d5d784615cce6d5cae8270b78d7f362e0db96ce3136de332ec61119253af7a7f316894924b999c8d4f4d4eb79313999c50d4373b7c9848b5c47ca4c0b941f5f47383724277e842c137cac6578aa84ac1d8f3edc6787c8cf1e3788ce7ebb89ffa39eab0af226549c007e26149462da323b3436dc2efcbf9f40540db91d4d7381e4512d457e846c13239323acec29fd23273bcb941b5853f2b21fb9f7edc358174cbe62c0c9e7e367e15087e7ffac17f6a82ff74c37f1ac28f92e13f39296138016d7c0adaf824b4f149cac6b88d1ffc1c069cbbca960d8627f86bd9dc55cae47c323958266742616371034c01e7b6bb37cdf2b45f82b51db056c19daecbd913df9cbf3ab7ddfded48db35c2849edd5d255ddeddedd91d15ca75618c0958ecf236d9dd638fd6ba0795740f4ed925b804f4a9fbd2ac6d62f0f683bdf6da6bafb5348b05cd082123848c103242a8d62db200852748b9b6b39d8e4e0764812e12245b6c51abd5786a3c3c75869a6a8db0d58a196aa4d4ec2c6c98ad32e8581974aa0c3a3668a85587065bb78edd3a75ebd8d45081ac7082ddc2083bb3d6083bbb810301b46a458541460d2666b3d91f01c36c369bd1727670684ba0311cda4c071cdaac0a55a0cd68334f0b1cda8c46842734a3cdec86d95b3b5abd9d8dc2867501b331c5060dd5d610a935446c0d119b1a88d450ad0d22d506116b83c80d1c1626dee33e4d76fd54cb11b2ffcd0ed18be1b346cfd85bfe76bc11a9fc287e2803ff735b6ddd6a2be742ae82bda7d6a195dc75962becd418aa63abc181a2a1667328765bf972e2666e064da195096341d5179dd11ef535361f75127b6b68cebab67afaf157b4f8242d7a16adf25da9542a7d772265a35f79afb39dde56dfc068ab7e78bbbf5f9ae2071e5973bf9cb6eebdbfdd7b7eff9650f555535bd5e69a4749ac9933e3aea83fbd0e147922555807c6a7d711fe8904bf07130996f084759a3422555685bc2b1c3b76f071cbc56a6c1948e7a20a56dfa27a79a7808f587d8d3f31b65a0fa5e5ecf07cb61f1fb55577d02137c79b5492cea6b40f95cbcae67e484597d5ab7ed4aa544af5a95fa5542fd23dbc7ccb67dd43921c2fdff23dfca7eeabbe95caa525e52d644c8ac40740c0fc993f6156922d267194f38787ea236f56aaefe58139f4913ebef0b3e187eefae1ac9afa962f6be8969bd54af575f4de750f1c26ec922a7f1d2f3ef53a5cbee5717d69f2bfb79a87e32afea0342dbcfe1c2dbcfe6f819cb565c91cfa49f2c7cb6bf0b5cafe8b4ffd8b56f9ab2c99e3c5a754395cbee55dbee5433aaafc75a4fec5eb687997fb0b78917a173d431b438cb6539be5f0f4f818aac5501490cf4542a8fde1ae9a71ce28d4d7a0dc0e530895511f53efdf1aaafc1cab40e1846132ad48a4d50e1dea0bfc989dfc33680a8d58f228a6eeb8e4dfac24b1dd8edae27156fd2e5cf9f14f5a7c162dfa915679d393def4a0268521e9c33785a4fcdf7b2ba40cf5a79725b92f0302c380c8b0f065b31858f8406418e8d9556c288ed21d3908793f14c7a9c74c275210b4ebdf507f684c45a1dc9bdd77745461b0013b38603e613261c7c4ddef2ea901ba3d962e72f7dd8dc944ba0943f06bdcf5a35e36fad37b9a070ba9b22f928d489565790d00d976468d1e006dd50727254029c3ae3f6901ca994bc64c19f5c318d5024c3ffa1da46f793ade9476d0d868e6a51fe9953f699637e9f0491afc3266e5519f63e551a55f798fccc1f227f2c7e859c237bd8ef04d640f49728c1efc1e3c12d9c308fcb959b4caca8400c92f93895e367ef8329597890fea90011ad3a1ae2d91fe76e05680742eb2c14e11b28805b914e16cfb4567d3ff5d8a70ee9a2253114aba9f984e0cb16d17d9dca5aaa16efaa29f2af211dd3615f1e028602729e989fca2a04d45b8971d213021a2c99e48d893c91034d6eae7c7d89412d9bf8e3fe98de25afe444da41df54573413b9ca411732326e97234f51732bc2c60c24a1f52f145a6fa17aa1ff58b07021b1f0720b091fc0104f623498e173fbeea73fc60c11028416023f9e3070b864089c15ef4c061c24aa42cf52dbffab92263b28a8ca9e5194a6400f4458bd056fdcac202825b03b4c65d08f9fb8f870a7cf1313f2326c692944506f9e3e16f9f0549fe7e8e3dfc2da901ba6f0c3938abc6947c770fcbd245fefe9b42473b7a8fa3be5cc8596fea6b45ceda0a3f09bc72f9b265973467ceb4ccd9a238d855896d57fa6a1e4ee230613219ea532f3b7dcbe33061a50fad98c303e68b8715a06460caa89fa764dfaa5ffd8e17fff273bc71c171d134e745d3156d865ff6f22e3d54aa7ff117dfa8fed353b580d5ab5ec7ea55640f4972b8bcea7b7859bd0ed5bf207b7021e75f3db7a7ba51f9b9c75f699557e9f1533a7f8b163d4ab3fc49af3c498b6fd2a3f75af484a1f4849df48499f64d8994c984883f7a998ce565f9555eb6f232d18f36074d7734ad51db48d35bae30163d612bb031a77444d661474e2601f93c2711f11373ca807699270542f207306c5a8b1848aa0b780063077a8b168a686992e74f2f0c2d89d278cc942497a8ff477d0cac4aa3d6175f90593470bcb20fa1ed3ea0f808dafe3e6afb02de0df7ec505b9624511a7aab48d49efe24f13e326bcb0699beffd46207b29298ace5b927a5428bec64fb948104afb9097298b8797d203da8aafb3d6a767d207b8e365475491bb605ead739839d2ff75eeddb279db25d973e04d9fe934e3aabcab53f79d0297bd2ed5a557f6e0bccaf93b453b6fb9c32701155f797d278104a43afada61b64faf84becf9e5c6cbc6248f979b370f2ab431cd8dd555ba2b121370bf0775e9bbfe073ea6a37dac67d873b4e17ee97bbc31c3c6248f1976a5d6ab55d1806f0e741b93ef911d69c3252d893fda15a1864fbb6f35aa80317bf4650c9b65b268cc0ac3db795f48da31013f76891f072048d4826c618227920c410930ac755903fefc234d774dc57a6e92460565fa2a61386a5b431fa2067f862059fe4039cb49190b570a4c6411b4bb57d9d0fd7d51fd2f01dd7f259db2f194fd910ae81e6bd1f6ea0dd1b6240fd106e2518a03da767bc597ed30569d00a8b8815ff854fccdc31fe882d81a5900b8b7a7d27a44904603a4d517067768b0768c159616662331a3aa8651926b381a6d6e2555910d49e2ce8e9e8d36375b5b123136bdc0010e7ab60b6a458496946a857279617a29d9d7b49c4b7396bb4012dc217b5a5c692e02d06be04713ffee5c2566ed22e9e9401a38e422d9f4f28821485b58819163d0cef360c41c5125a3ba15d2a3482b8fc2a4ecd11d0260fc66e8bb6a8bfa664bf43a1db2441a6d16c6c000602fbfb50170a10a40ccfe02699746ab229779e697166367a4405a1656684fccba608c67b1694c0dc18b64831abf000386edefab9fdcc3ee558ebb6438a1bdb05dc75fd85d2e6c37b27db56f8e0fc00940adbf88904bbba70044b40244f1f104294f988207272dd07d4dc025add39c5c9f07cf936e7f7fea4ec21bb37e587ad0ae37e6a60f64d3727e9ddf4d20fb921d154047690e72a217f9ca398bd29ccf2797a22c12a9d0d702a1fb39501af0adcd6e4ae7d0149ab91c04280d4896f442c07a0ed42120b4adae5145dcfd6332c605d9ffa77a248e7a1fdfabc3b410b8b7dab67d81c7d3097551baa179ebb8e8b8e8a274421d17dd10cff6340574c03be470249734b633505f371337873aa18e8bea3a872a8517eef5c4f35eb90bdcd92b09af272fdc2b09afdcd5b9393d53c69015e4f2f64c7a67e5ad4dfdc2bd92505ddf21a0ad6ae14328d00e45b719a5b95f2b90f54a1e2a1da2a3435c4a67960e418186ecaa7f658de472dab2b74431fb1094866e6beb2d0739f5bdb794c6bee7594bce996dd7915de7d67a90cdd99e2639cb2d0d677d2cc879d650d63f6abe72575d53bb85fdc9d9a1883e7629a134187f6973c45ae98dfe509b77f8521bd88e2ab136b039eeaaffb3afae4163b4a93bd4d7bcce22a9ae4941de01574b96a85de2b0acdb2bcf16b581cdd9558b76a7e3c2884799ce9af6765f7ad02ee9771d59f27037b501dc94eca800e677e30defbb4a6be751dc457ffe35b2b1a63165d0fa596badcf69adb5411da5a17b46d973bca475d6ec41f5b1f86378f021dbee9207ef7af7e7244bd2253fb2a4dd680338ff8e37bcaff5ed0028cd103bda4c9f8d358d189c756b7c49e49b8af19c13cf18e600280dddd6569d63d0c4cc898f136e3e403edf4e8dc75d24ab4b3adb752681d2d05d6b98db73ac6446f9ed8e8052ea01a803c480085c603144912c540186c4a72683309358566238a142a6c6f4d93e9b5cc189ed506001070b339022440a11b67f0c8d23b6fb648109db7dbaa8c1f6cfb721365004c3154ab0ebab4831a70f62982fc4b03d88b5fd71a66d7fd45015dbffb1d0d9fe2c22b65b8108db7fa45123054ea0800452ac0087053087012d07eeee5e16be91d4d8f5c39f2ad056d8882bba26b898624f31c51b6d7c1405158a00b3fdbb112421df79a38210bbbe153d11050ae47601e054a1e2eeee360a0e761de1327dd21653f6cc76541026a5734e2a3292f1dd22365ffeb7d2d16f125036ff097b9ea7050ecea5795ae41c61711cd11d21419d5d2d72800227a17090e028c159e2ad89d2a28a22b488284548f1d6dc25ce3e5a6471c40f0e236e8ec23101a2273c010bae88bd302102172b6451e7c51272c8f3c4154b3471c50f70704510565272b7a7cf15391b206a4c84e4a6508489902725b0face6be79c77de17603c8a55b707a4e3513b72f2a8d66226e8d417406d7af19db5de492b9dddca74d6fd89c4efbd8f474a9b4821dbb3be1c45c93286d27a5fdb951ba45862dfd2969f9c63bfd6da5d5b3d2788ec7b1763dba74183bfbe18e9c9b6372300d98c00b48f20c42e6b6cdbd17900df549c5d37278929cdb7e7cb9c35f70d7653d25bac0ead5bb9410aec2edbc5f6485f421eed692bc215fbd26837249c13c8d3270b297bfa6401659761cea6ffc29e3e59d8c29d4d5f55dfeefaaafa1386bfc428d6fce968605ccebd1c17335f98e766beae0df3501e67619edaca0d522871d6e441e22cfab846621e67d167a122971198b2ed8760dbf79f98a7bec05b6dd1a7377dc3144a9c35774a28fbcf7d7fee7b7dbc652bc59c2c718fc5d5175662b6e8cf2087834d9f898dcbe184308f631e27f4419cbb449b7e1873d7dc94b640087ce2aee92384db94478495a0d0a6e0d0a6e06d535809e6367d5b9d07a746eb196223f253e48684eb6cfab4f42a367d4a6f0f367d5a7a50367d8a8fe02314095d42799a388961979407080a8df2825d52299b2ab14b976d140bc50241cc73a33d1e14777942de9027e409794378e609e11c1efab7073470d0f8e9725dd0b5d9aecc5d77766797766777e7ce6ead0a1f62304ceea8ad1ae409c3bf436dd19f40334807fe8f54dd7c73bcc1a46a3a0b088c67d357422e1d57e3a92dfaf7a6e3ae9dfac2b5ea3ca8fc28164feef9c13cd786c49c71af9554d99f56fbf85164ced4c7c00526900d9754753ff79d403bd5d18f592273c69f02ed23373f88a64ce92206c3642c89bf0f130896c4bf921667368436c4891124613b2d47c771dca5a23b469c451ff3609e4d6bb8f3c2493867fd8d53c5ae64994372ce230cc56c0f8a71d7dcc15d94f40cec30674e35f79869851c4488ed22d85ec52c6716db6f4c02ccd7cfd6fc2ab6ff9c4eb3639cd0147096ff9c4d44b4099a3f0f582cd92e739b0f164798420c764975b65f3b8132c9f26c3fb2cb1adb27490177cd7fcdb07dfa60e3b03267ec6f8c31ae2d18f599a7f1474ed2f31e7bb6e4a1ee5a4b4cc33b08e74acc5eed1d6d68d4c016060bb5ea481e0ae3bf21da78cae83c0f7f3622131572896fb86c711c6d52478051b2545249c84e96abcfa3cd5ca12b574575806efbfb76b264c09e7305a20d9e3274f982375d054f19f3cb0a6c4a8255989462209bee9a0c2809b8d8947c229c30e68442ae7b62304321fb68c384a267778fc36ca9be95b3ea875fd834cae98332f564c75334c1391dfcceae33ca59d5da7f5be33628ffef9a5b86f2574b96b94e18f51211ca92f4b80f333d104feb0b25a2c6b716978ba1316df367de6eadb5d6ee3dd0cba9175db78834ea55740b08b63cf828b0e5a63e3d65d4bf9aa402a84f4d1a86b7ea87f8ff512def3d8ffbfef6be6314aae5e7e84d4d621e97b4dd76ed6d9990d30b31bde779dd26b168c4dc6c8e790e4621b1accc55163905e83bfec1a1e27d60288e2adec571c371f31b46f845fafa2189e5f3d3b187ff68c432ca58e7d14f98bb7af4e001efbcc5d7477e9fad96c0044a5667493ab0e4a7a3caf17737230e600ee4ede40dcec2dfcdfb6161c95f47959337dfe33061df8fb42c7c9044cac417ad7cb5223b1a597b03a5f1d9ae0bf89ee593f8ff58c9813f933f4424bef15e657c317cf0bb0183a2ca178e1eccbbc1615d1cc14e76518dab548d0f7518b1984e3b9467c2c44e071dea6bc26a680cd1b0e0750a80d6512e2d1f5211a593ac5c3ed4abd58befb26a2165a737bd0ad7382c242d8d24b99bf1c00c7d7d600ed1984334bc1aef481f1e266f5c56dfd19833f8ebcbd0a081bdb79e7dec1e0d7751c059f5256473eee2b27a1fbbdf515faa4fbd7dfca18b7ef9957ef12addf2298d0a129b33de467d25b25da81058fc97cf21fecb47e608ff05f903f5a1f82b1dbe4a4f98068094fef432d29b9e87eaa6857cf1e7ce71d22a6fd2e39774067fa457e62e8df484ddc8565ec642817014ad882ad9661310cf555a60c593ca48841aabfde12efbf276e24c2f7ac2acb5d692b892d35616d9cae717b54c7cd18f7492ee9258f4af68fc59cb80c8b0d27b2fd2406458cb8b7a16032bbdb52f5ee55ff4f8a747bd3efd8b468922eac53f89a8f7ef05297379d3abc0132aac28d4c9abd653867dd7284dd296fdd0eb97e4cd09f5f704c3b62e84e43ff8350e7ea713aa92ab777999ea4daf0120bbfbb0454f988bce6fd2a25f699657a5f4e83dd58d8acaf821155d8ea0de437d8b9e65d8e94b5a06440cecf4b30c4399507ad6d68da9a427ccb26a6b45e3a82dfb59cfda12ed5b5b02823dc4b12e2e5fb26894dedffe0e1dea4b7ccb6243cb620aadb5957cd428c9fd6ea4abfdeefbfceef2299717756a1c533fbecb9862d1aafb2c3f61a156dd0f5fa47b4892a3e547df83ea4bafa27b68f9d17f5fe33e8e2e37bbb55eebadd55a6badb5d65a5bafb5d65a6b2d6a0a213b4d896effda1fedbb58bb63cab0cf2283ecd5eb7b9dbfbbb8a4c651fc1aafbac747aaf0eb407de9759c7ef45d4aabeaa71e08cc45ab2af9b5bef8a00a043f5b84675b6b51429688b5b626837d2bf2835d521cf0ab4576b6fd18ec78c3e5738cef42fe48f2fd8afca12255f5515ffaf13f3287f829f2470ba9c2e402505f7a552573a0c8d29f7ef4aa1ca71ffd899c2ffe3591aa5b2255f77ebecff232597e99ca8b2a3a03b5653fb3e851dba1908ab63602e2798ee5f2ade29f1598ad51531fa4af01d056cdf1631baa31fff19b078de8507dd5d850cc7b76dc78cfed8652bcc7eb877af6b0fce1498b3fd2e15b8dc384c9569ef46952066238047fa50b1848fe00c2c3c91bd9294875b34292c81bff0f5503d6405b1447a1a037fa03daaa6f3bef28fdb16f49a415efc3c19451bf06f71cd0567defb15f7acf7d5b037d511ed0160fe814b4559f369932a49832ea536ff7b03715970b82d1997d7be4f4561f9961d8e9a73b3942fa923eb9219156de8e2a8fbc01c1ef57f48d96b190f2e39de9cfcd898e5c88c2483299ca8ba1290a54a9a7a4a170a1e92a474a5fe327eda6211d1456227f0081d121edb4daaa3cac6c9a22fb120f73873728287bbbbc41612e08ec62dc0973ce124551e4f1ba9ff5be6559c2d0cce7f613a589bb4a9167bcb94b5424fb937290cb3077dbfe62933943eb4b7c7f518a49333a610a60a8be4472b4d5967f48962f8e180025007629f2a8609b6d88922398851c024d19fe653823c18f38db8138440f4422c42386300b6fdbc599494a2ec3dcf65f62f3d9fee11646824222520891d8564c1716d345c319a04224dbc3a0ed415e69983365e894e18e3834dae8681b6db5c228c93768d39110e8b98839afbbbbbbbbfbc52218e4faa1bbd3d1c61de3aecbb1b91ff171dcf890e5b8c17140a2f8a22f450ee02dfefc298a74ada2288aa2e80f5e71ce8c2a2a9a357e4e167dd9c572fb278e39435542402959de1d7444743f4c5710d375bbd84f37bfbb1e13fe5ae17f7320799b305ddfbbb0f8b9298efc3fac65de5d7c0fdf718ca318823a3b4b6c3e3920d1c31f84e69c373b623839e410a20ee5de1871229339e9ba229e14e1a48928488c8c38c426127230fc2a0ccbb5e5ab0e2594298571ffb77f507de1a7e40b9695e3070c4230ef314e94cd040747072769d4568c901b0d5d8ded67ee0667390e2d72397fae0ff2cdd9db4dd7e5088158c8f633aae6861569fafc0b76dc545f19ec2b833933b7925dbeb598521a1d2939dab4e07299adabe69cefbdb42135263336278dd119add1251487cae88e539d8fc668f87ddff77d3f9f6a31fcf44f19fe1806b9a4318b82ed1e041532693a1dead91df54563b5e594065301f8489698c43adb69ac62eae335b2e9975e5f5f5b355e7fce504be9a4b56b80fbb883c6f687698cc640f0cb9932747696d8ba9d500517e37fea187f47e99c9452ea2cb2c47f7f77d7de6ba242ee30a6189c227737e762ef3f5c53d2b3e486292e6255d65e77777777777ba9d3e96e434d42022aa6d427560c99a30332194b72df8724f4a9aae508f93e7d55f74076479674535a276b09cb5933b8ed183b871760f8156361d622af502bc6e68c6fa794524ac5184b159952b204a56cff300b53864b9933599834e30ec45809da7420c662e39229c37f1c7fc8d126c6dec6048662fb5f2a6e0f14b9fc9eebb2ff5d776dce7803228a98c7a811a319aa814313441365fb7f5455c3b8ebfb8b7ce919303df54592336a303d3851a050f74d83aba42847038546480454c5952379466dce8c9474924543a30741dcb84576d768e8e502a22167e5584fae16a0648ebdd8764de5ecfae2e7069336a35612a2142bcd7685e981e999ad1b7c901750ecfa3822bb3ecd116870336a0f8ec01109bcaad248352a8d46e4dc2228ce0a02b508e8558ab190b112a62755ab2d3791a91a481a8123d268f473b3984aa0092c81e0cf6d7affd28885049aa02cd9fe54c859db248af262db9fa81224832fca39cb06244540f5c5b64543222939e62e304614241adaf54bd16dd75284dbb53e0d13d30582b63933daf5534ad85ee354ec2a72727936f877a7be66d4c0d412e05f21a62b557b907c1024c15bdb336af5458383e971d612d305a3831934b8fa8aa971960ea6cbc664f97b578aed90d6c7f636675cea83b63dbd0fb2c1ff5e904bf06eaaa2303d304b606c30413051fc35819083f131a249aae6647b85e981c9c140d94e1ffc6aaa96ea7197bba64d297157c5c212a95a6ac90f0cc6f5697031722e04c1f4c0f414208612303d336a650c6e9622697035b329c34b10b7fd6970337a06101b80ac0034b84c83a3a8166230b4185b4081225485a21c91cb19b59a6dc60d46482e67d46638715709e270ee2a69704f3e253deea23eeeaa336eee5289607aa81601695190b3fc6b207197ea6190b63f8d7f0d9e39136eff9ad99c396d676def7116bd4b7658453ade089f92214c12a2dc4a95fa16d4e9fd4d51e4920677e4697073a6b5333e4c0f8c2cb2cad3e0ea2bc62cc60c0606b9a4c1cdb697fec507b9ab04c55d2fde9f46c85da6f7a7197297caf473bfbc3f8d1477b9c464eec271970bc903dd363477fdfbd7185243c75daad2cfedf2feaf672c31e9194af48c1e67f9bfd0336a253dc345cff08965d4b7bce87db429e9b95d4e339474bf72247f34b8efa3c1d13c71d7646204d0f6a7c9b9eb65fbd798d518e2af29858d06c85d9309cdb67d060d8e66e8875dd688754c8e4862973570b6e7afd2193dee2aefcde6aeb214fb71570933a336c3451aec92b483cca8c12cf1578c1c4c8fbb4aef3172a41841db617a6a60ed121923e79925466ebbea575fcea86dd5d3d12695577ea5639ee8985c4ac700e998201d0345c708e15aa6c865aab65fe45e707366fef77d9f9acd19d3cbaeb584130d9562de36917786a394fea84496191cdafe32d2385c991262fbb3685c997709d39323d2a5a802d3b3fdbf32552bbd5dcea86d7f910677812b0212e5be2f411c78776a8bfe1562b2280ea86f4d5f25ceea59e2f3435fd3e0687051c45e5c4647abb5ce3a67eaccd54cc5af7e1ffe70a55ea91ef74d75f7ed31dca5c45f5f4ce63fdc71a59ee8cde98d735e9ab33e8b71ad18e3a7fadbd35d38ac6e5d8576e38d71ca70daf27717b693dbf5915c7dd4d7f84ef57456472e60facf188fa136372d50641c77e1f7efab3747c7eab27634151c542f57db91a5b8ebdf7b6ffd7a5b96c8f567b5b6de6a1def4a53304a729d2109a81466f6177108bcf71ef4dcd22085596d09ecfd88703b229d57499948f6447cbfebc087f12705221f16434c43a256fbb492160970f665010924e8ad4f2d1690c0dd15c21a34669ed6637096bfc7d8db7d59e41758acdbdd22328cffceb32b9487ebd4c1d3d64c8c3fec89c0c375888cf7641204151bc724081f172e32b8279320729099042183673d0c160eda5008b2684f2641ec2891f39e4c82a0b182983d8cdc2e3f068839e77477d2ddab57afb5d6eab5563bdddde79c334791e9572a2ab960cee95e175e6f5491edbbf6eda5e0b262c4f0c2131a97556bad3cd04d81a0b02437bcfefc162972c9daf52e719765b1585d774fb0438182b6ef800225312730c1490c084687840461971e739f39cef6d2a9929eed305428c14591118400c8c90c76497facd825bd6ddfb79a0e5ab88abbbb046e5294c00841b8a2083aa8ed92e6fcd0c42ea9916dab2d1a535f4076206382125494c0c70945c0ac0c46b0cb690b3117410891c49212902006308fc1f6cf4fb6fbdb26bb5cedd52e616cfb8316dbdac759675b2632296cfb6f836d1f8f3696c6850dbe37da801bf583fd7ddf8d365f0d160c120545a696def0c3106f4d5a5f28f2f6436ce3c7efaed23eea5725de1f5241c806a31453ca240428d89e8df7b1ddb9cd6d412eec5b81c8a5df72a8582c16025b2c06a36f35ae2fcf798ea7a4048d1dc3125d90e9fbcd83dc555f14e63b422a3a617d8a8a9103c6aa09cb270a35279d9a56d1fdb654166acaa8bfc2835cde5a37c7969fcbe32ef7502814c6ee5d5b118476fdb9386edc1563d7f721cb51a750c5e1ae492775776a8ae2841222d3205f5bb57e2bfb47cfbceb4391cbf973714f708884bc1e9ce9bcd1b032a099a7105490032c4a60c52dc82cc911389516320810bb3e0a4701756289c22486825ddf9445a082122d0961ca0f767d920f72175418c2f61f61ba138bc586c839a2e2a8f4ac5576412d999a19400000004315002028140c078482d160982469b0990f14000c729c507c561c8983491203290a42c818648c21c4008091919111920600e3758b202a1e72c41db98935736a6ab3042b74b50864388f23b89f1cef87130ab440f987866fc0b81c16a5a7b15f9827464fe9397232f79a89aba806d058cb464e689f2d6a5c1afa8779fcf659668d6e8c11302245d84c1f7ca6055dda1f1253094126ea5d382b01b6acec852040193c054fd35d735f8d04f3701059b12ad698eb7bdc6ce190b639d1305506caf29770f3302e33f0271fcd4ad636ea9ad74f5f04fc0347585d7061798fcb488da1e9319297521b02d8a5471614e62bdc8470e6652b38cd95d28d0404ec0f6acf8588f1805560be229a670f67fa56b9b001a47a4909f6451b580acadbc5c21ff9066cac40338e7b209eaa51d81eef17d5aacc85ba7ae517685d148bc0592b645cfb3c63f5a8d12cc34d2ef8d9563d9980efd8ba0d422d5eba585fe643ab56346d51d85eeb5110726d904f6784043a10fdebee40d7ffe9a474d342df80aee9009b6659489cbf888df8a3dcb024991f2c824afc4b90b1d20d9ef977668f74c9a895e11ecb94a22f4ba13d2e27160eed64aa480c6c4a108af29a3ee69af3929cee885db8c380f05ce6cdf66a177ef195750a9158fc7b004d33d295f6511afa93eb2eb82a299eb14bf646e826562998fe70c9bf49c8ac2721de92391a27bdc1fc2dc5cfa90dc257ca1f047f6a99b1bfc5c8cea8886364d470cd0b8ed95b3ff93ceaaf09b25c91f115a2f3d780378ee16ff985bb273ff6c3cdc6ae663f7b3b380d9669bc3db5505cd0d7680308eddd52355cd00f799f9cfcc8966f4b3b232833cabf90a35060f76c59eb190b6bf652eb8a42b6c6cd1b29bc340e015557e4b0b5fcf784a010b427bf274f72b0dc296937c0a78a8b6c5ae41ebab2cdcc75b30be81e7e8d475109addeea09abedc93572652bc2f594f6f0588005a656ff451623b88ba977b3ba91d11d3ba3e95585c967a472d7d761f3b2ef711801223812eaee67c49353c15de4788f4514b30a5a00261b38a08eae8332b99ba0d73fb2994b564dacfe9d7be4123f45128cf216c3e1f0a6a32c0dec15dfac6d15750fc3d664da333bcf6b7e30583c288357a387be2857ab9f38b615bf76966d2cf073f457ece1eeb495ae2cbdd9c8c5177f6051789489a45f1d8990462189c1f7097e358318afd18cc4c1bdf506df3a7909a1d7051f93afba7a147651061f0ae9bc0c43dd61ae2adb73fa9e748f207b13ae924318af2ef83111e50d47870baf8e3948d995e843ecc8dbe19553444240b2d03f0f551d4ecd2bef8821cccd29a41f06daaf61fd69ba6c9c17efb695e95da7121e988e92dfbfa2081e4ff1155359e6c723064d90ee50a1469cdf2258362c554c3310829b9e18c1e6beb5a362f339069b1995e7125909f5b252cc2e26c1e7529e68cd134cbc9945178e19a75f654b6ca873bc05c9e20ff029c7e781c60d70ecfd386484b0d5f8659a5051b38a5a5483ff29bd09732d51cd19a4a830e8b6dfb60aa497532bffb9f5ae606b6a4f6292e6e5fa708f17ca35958072feede7fc7c0b5218673de0f83a8dcc294359b0dd9f63485085230deb8178f265fdcc57bd33746d2a863a15a14922cf1605a2283f412000f11f959cbd7c09373d8fb83afe02dc04b92b7c404c1225f42629606dec67de6b88720626ed139a2d72cb2554edf94fc2e38b348b8cdf0b53cc244440686c934ce12a56e82abee2ecaa45f5a66dd86f7c184e12c42026397644b80d5c9573083f8f29e26bf4bc0ae056d9b4c99a4ff36daed93b3b60061444f1a71c9080f4b1d28dee88aff081eb07998ad813b3234a38b52b371182043ddaba1a7c106cd61e5c8c71e6f7e73dfadef3a3d249a9a0d14001bd45b43d08638d65078da8e003f40f3d452db2a67574ee27794e75342b75b9bd5daf3bbc3522cedc10b6ca9a87c952cbadd9b046973c99913c0297cb90fac76439971ef00c116549668beae4db26367afd70d6a291b2b48f0fc06a998ec1111eeceb6d4cfda1a1cd165a3eb186dcac270f387238bfa720301d7682dc95b28f13304239ab055321323bdc54e940b117475bdc702605093b0c645c1881c0769c2631238a51b1a0a82a2faeac7272b981ae62ddd824dd76ce61a97551717e797e6c72b7e70048905b22ceee1229db752d05ce167002416a544f9e8100a1d42422428f3041558c6cfea05225007ca35248491f678df18fc0fa48200933dd817c692970cd6710bb6c22e18dde8844d43188211f4aa455d24577b2b9112ca0e74dc1be4636eb3bedb62337c3d4ad232916b9ae8e7a905b371fc61c89159e876a4f883754305e0cbb5c6280d35c1f350d9f82eea7e61452f0bcaa2988934237967b20835e1e06c47368a749e5b2134f348bbdb50e16e739eda7c0fa6882ded8dba7d4ed4150dd442b2734c52de149d344c86cd59a709a53ceaf008c4e9ac6624f9992f559b4001fbec11c0aeccb3f128cf9166d3b011c275d814d4601aea0f3ed156cdb638bdc9cabc3f61105bc1d2b98159778b6fd2747002a8f775f1048b1724c8149cfff1a1c6838d440194643932795aa1595bbf75bd56c8b63226e4ec92e64438ea0a07139d3893ef3e380312b33010d58f0894b3163032560c7d368b5a94209d0771bc00c6267389626ae4b54a313048c7ca42a6df5e86c652f09e11f6c7645201f4f9e1b38745238d8ddad3f9d9e7f4da780aadb41e9d72ad87fc7b889bc202243af4fb062de12b0946c0e2e05d286973f0f0a3ff4d4e1c12419c01b61037ab3234142a3fd27f7563fd2821151d7781757237838849b6265f2d513dbe6bfa5da50e7ee425bd3ca76c6dc7ccd6f9e6424b6206c3452cbafaeb4e53623e56f41613e1e3d107530348360cb45c9d48d13dabfc20ce3ee6653f1ed13f431c762a55c2b83130d7a92b67c9c8a2f163c04f937dd395cdde4daee943b2ebfe73cc4a6fb1066fc0b1b3e2fab42f58d2a58ba76deb620914fb2ab97c7efb215bc7fef6ba91b3e80661b2216a98baa786e99db0c562d4c865f84cd717819f21345435fff68e45039671dac4017da97dde48a8ce6cc56fbaab4ac9105bd7f7942fdbad9145d5efa2005f5e6eebbf674827054b788c55618638b1774ca16963d7178723b1bf98db2063e3797861fe081da9ad39c9e668d48e589efaf373ad5534b57e276e400ba3f4c37492900d20f0fc1a930bf387b728ab24b0131e3c0f5e0a525501382dc874a91d20c4172656bb5eb6cf86b3d3c8e402f5367156b522b48d852c3183e1e8a187d1687561bd1bb6e2db17f2dcedc13abfa6ab8fd17d385fa1e47a22e365e879a3946649b731c4415b8b7f37ae47947c8f27440c7fab87e99e02ab8cd06ef4c44dd70f62d52590d7e3527071ed162a48b04cb670787e1248f4d1bf93a1a050ad9d8a65c837e2cae5e2155d4a21610dde28e8ecd9ab6e8d4e71a185b5fc6a23b7661ea4b7d8047da4d54d7e12830eb27ce6df137cc76593963fe41979d2b0458bab5b83f051c6931006a33553d2fdd8570f3d1130165fdce89daee85df92c0ca858f1eb49c18ada93564206ea584b5dfd2572eaf48b6ff0ffdd73a0811f7162940d5111f0cacac09433af95496841cf4dece0b5b98419e4479f523be02128a5ea908898b79b608b6db3f0e1d87edb4959dc5dc898dc5282037a1ced9084a88f0ceaa39705964ea34a9598a69024f28a6e5088e1a1c347150d81910150933f85eb92f4170fc727a7d04b4553564f4edfd62b7c678859e978e02c37e56f7444be10b998f579167f743b44f203c6e32af55f3802c664b42dce381d2528ed306dd6b3cbf903a289c6dc99a5f918bfc4e097bc220f017c94ceb1059fcde4a26f19814e25a6080b740e4d741bb2ddb6d504cc434ce454117299cbdfd809e830b4f45be70d28c15b3844218c11568e9a286542ae7907c72cd6ebe684e5f5b968968279f4e1f2092ad8ce69136c162cf872cd3766dd968f952940f9ffbb8071907892abcb8c4c8fcda91de1f8744d9f9b444f856b8acd6797b23e1737ee54a6a2b38e6f11e468fd68ac82643ff0380976861327b70f58690094630009b76b3b52343800e13245c1601d6d525812e99c988ebe5d98654e91da79a6aee3e2e61ab83770c0acc3b3395f6f028eef91f4268b88b8b9b9abe30fa1ce680949a152f174552ca2c1a1534b5768d82f96c0d8d13cfe1de833cf0406f3856f04fe42f6a0600ac606f0a344a8150e97ea69272035cf84e4447a675196edea64d30506a5e4e50c913e0bbe6017b9fe34eb4db393cda67c276d7dbd0c229de47fb74807d1a73170bd442d94a42a58b5a8640723fa13a692a834a1cc88670e03b6014020d8eca83e48daa326ed16e5b7b2537d12c8ab287bf8096ca7ed7c405825371c4254b1e3b222b893dd1697223686863514cf99b047a58631a828ac94445c3928de4e23a81354d1533a11971676a9deabb636e4c6e145f93fad9a9f721ae4238a3074226d5c8159069e2c36d7edda427f8db01e3837c65f382145a42b8f8b956ef80d85af735e448081344a457d088f9f6dacbe97790ab25ceb10339020dde167dc1db66e27b5d43461d47c478601e2d105a86a6d126d351ed2f55e2981e39d16c5e55ae5893f3cf6e85aa93b88a907daea89470a10790af9764b482ef4d407b318aa0affb9536fc4e2ad0c7204710d96e2586cab8d10a2a50876b8dbc446e46ca85dfdfd7064aaf2a4c5b02c6d17d1265ab083336bc4db4e8c3d8c927c4b2c1708a09031ffcd677ef6922a885a971b61c3b3d549c017e6aaf8d02b7c651a9125338c267ab709f41f7ce236b581b5cd84be08db75e53543e7967a4485bfeee1886de3372f83573b0b6dbb0f40c57ff1d0c60b068b98875c7555a072dbd5ef46d402a2cd207de09639b88a0f0c0a5ebb70db2d076b0e1a9136e5b1e39cf7ddce79eb966e515b2681d0ee2bf8543cc7c7caadcacf96d415ab06d56fd86e20ceab0df146b5ba2d6a277ea2b25b2cb563bfb15257e196baf948c82ae1cf18417c8c6350838ab3dc84d05507b8e7813467ae10b7b07cb4be47d8f61aec70f68c254dbba40881eeb69805a493bcbde2ae322a8ccff8246f31425688a427473d4993ca6eeaf1b24b2f04f8caf72d452b9c2890bd0f61d33fa15929b21286e9b01d4c1a2afd2476b2d372f35a669bc0fc637f9e8be2415ab4fd59ec2e55a8bdd05979da7aabbb9b392139d7d1b090591142ef5c6eaca30c4a985434f8bb6c8e12079e802dc0e09f1c7f837c2fcde8a726d54053e13aac9ed357479f9616085aa58b4468197a7df1acdd7d359b18f1a98a08d1fdcd1ba30afb0cd92fe0e0e78717f96f1b0a26774c673ca7bb46f45c812c6f8bc485613332fe73dfb2bf272ec562b9e8087e7400fcbad266cfb7e90a141044f3848480928d18d52d6a87bed42ae852105d468711408689eae4ee4d7599c573188b17b50eecbe8a4346de7b1051a1b4493c0b958df40ff3928b86b491ae4a089f1e5b0cda2fe9844c190ee8cf9c3c36676f9f96a8cd3e97b0314582818cc80e8a03c2fa4d2d45bb8445454f1a787a6bca6a2669d289a3db793c20ceb5ad8bfd6c4b9414eb3007aff56c89dcf1956e3cd07c1aec35622e113065c5cb323585bdc02c5a9be18b79d73534eb8f9e26b4a8680fafeda0e09ed5691cd3273d56556377b8d697937e4518b2a09ede5457b04f058f01e6b07933a645ff93f1ad69c71fe82e495577882d222d573d5cc10630b066b40e7cda386a9685ceaaad60c5d8e2b206d976d3ec0c55e5bb921f4cca4ac5cdae0d5a94b1d7c2eca063f81f397bc552a2de1d1c7ddeee31fa085330321afbc68675e08fe8be2a5bb4dcfb708ff10f21947280e21b39692d01b72139c20980742318e2280a7a452e99697f8609f4a450b774d468c9541d3919eb1baa92d9d6de813b9df42517ab8981b7bcfe9608a2fba0fdfaff9c41f1b20e99be0d61de69bd21786b13ec22e0cdbc98982b584a6c33292adfe62350a8ed44775d9d92a14fe354c804b455e2afd506cacb44be78162c8c7d8da41908520d33b32a6a8d6e887e8698da069a3a9b3d42f0b9f510cd648a37187df8b7463a6c5bd91726cc744d149ee95823f3169355ff9530e87a8e988937488f44a3e2e53ee13729b6954afc3dd72b595f7fe4277dd6c301ee873fd2462d3b848433a26dc70e28fbbeaba6b44a6a350002b747924ff3e1f2d51c508c378b2509a3e9cde6561740b17e368b4aecef8dd09d5184cc63821d690700a6b85a03eb28f4c6013ff92f3156ee2ab71b2451d9b4231ab4d01f4df7df5fb4e3f83117a80793d120c58548464a2fae93b5370c78e772700eff7c27b5d5fe4d2c28ed0348e7fd1ec44787f78dc811bc6dd8351073e1e9691dfaf502f391bd8835fac95d4a91ba8cfbe73b8bc93e12cf7b6ea3751bf45d8613a26c3f6fc403cd5d2d9ee54505232f7267b1a00e9c93442f8b31cd15c14ce232d805bf743c7fafc722efe841339dd3bdb409de5a335380b8ed0b7dd30e1cb3439f055c1919b8025cf9a2ecd444821ea3592747403ed15b23e57b47185d6af28ae4683b32203e5f0670bff56091ba01975b91a757cd27f291f77552f6c8e6650e9ab76f2d30811a3632db0341da1422968ff103a8815d65012ad4ca3a37f0b64b8f6cb651e9e269e4b6f626dd8d817199c4e2bce4bdad160cb8b885b4cb286ddd95f57b2ad4b0d41638c4142640e7bb08482739d4852f8d00885e3b0b5b5bfce10aae1ea4deea25d626df82c8165f5deb3429dd484217ab8e453eaf13056c307b6fc4d11ac4d316b2c5d6b0b5c8b812ffd00712c0a3929f3a0a45db5f97710cbe403dfe16a5c9235b68bfd336791c50110fb3b42f7fdf98b3d8e58f7cb11dbb78456fd7e3e581b27cc140cacfe6a14edc03363a6470ce89e3b464e0b438bd98c92a52a15c54237ec4d457bce3a931b16f51356f14e9cc8bcc7aef7fc477a1cb68b8afa260378194773867dffa719a685085b839bccdfeef89962ca4a95868424a8a014b0582837491b93180c39886cca990992a6a4c0703c5a6577bc3f2a4951a73a93c8a5f18948b30243fa94fa4b5721e9192307ea7232f07e27024abe363a7ec8bad0993e80158d5c1a04ca15481122482534ca7937e3a964a5520984b3492c9dd946d9d2b1df096c05160721394da1528c8960f8e1b43d6857da62dc88fa8566dab87847d19526595725f3b2e0809a75e099a624338d5656b43e4dbb21767b9d1402367c060de9c882824fab390cb1e7abc118485f20406edf04080e48655234e61b2b5b98d5153b175cb63ae781b0c7f8c1c1aeb2c95c8181d8df860d5c5f78a58974423d6a904354e6f8fc82d28c061c17ac6083ba1385cd0384e14c6a8969ccbf1552ab3856b253e4c00f017610c554c03c104b6c9d3534eca49a0724a6035af34448dfac8721913e3018cd2d3b5630541c898773229884de3a89428327d50b73a54b81d49f084a0a4b0f26b71832d4a7874feef0a48b3572f93412674732101dada9c07550a87b9886b1b8adf2dbae83603408d99c7cd841d74122fc5e0ac84195dbe60b101a6449fd05881ba2d7adfc9dc7f9160c38c3029e8d69dc46a59312d0b91dd119093e80bf259b286ea442804f758791036e126172d03eb2f027c1b85ba419c0d3c82e5953832d870e6f1be630893108a82dba19227429a7d91581369ad699f227b9c6b18645821edd9799d7b5c1bd05ca15cf22b77962316d2e8afe4c00bad0f2c0e9734fe3f93baf4b074363ea37e81d6221912a81ca0c13d299610cfaa1af5bb6ba2e06314991bfb55798a50c4759a2b7f5956f75962dcf2fe6e854eb2b1a66f465fc5a03c374cf48b2514fa195c8ed37168b51d8b4a15616d6854c5c4cc70633955a1c21f6a665e42a26015cd5a12cae32241a84fb3ce4f6c9ba99446f90847fe213148b605dab1787175ef81d645f872e6523272a4a1a3fbfdc543b847f2788b5f5a734f2883a0d3dccb385040d3a0e8f69b43c48fc9d82d22bbb07c516310cc5332cf614f0439a4c48311866a37df6c565da45cf53e49a11ca1ca2990e5b34317988949ffe74fc6be1a2f14b1095071bafd37fbbbed6a2576a70015944bb314934a66745c4b3342d7af552dfd2223b056110225a00eb2e6d0e2dee84677748df189263ee71cef0cc67f05b1698104f9e79c03d92e92490725d04f38cc535195e9ce6eab8c72e2142f191f5a320c62fc9e8b8ff78e13aa463c4fe8f75a37aa01baa3dcb22b177cfce03c4e74990e01b544198f44974c4d6c4e149bd50b370520dc7a04d45f004d1343c7ca6ded5bfd2daf41ed62483ad23b18d385e630460b1de898c2ed7b18e3f90e8449296152b6298e706a8088752e5ad1d980ad326e6be2cdbad110505589c49d5d4451a9f4b76873bfb153c3044fbf2d5b21d11676ec1b57e7ed2d604cd98fe4f550fcc13369e13600074613f06f7fc33869f54bd2558162c151b1413dee90153a1400edf283fdfdb29503270f870a78ab6fdea4a5b68cb72a23828734c169f5a7f5d10dbe42ce246291125bf5b28b2ac51d6fbd2facab446e9ea542a81add497958790b044efc71838d24a207e202ef09a877432bb8eac20945fa325105be1ae97b637ed9dae97f436655a0c1f37113be9b0eed2854a0b437b6143cdeb4ef225861d015288d7edf685c89bd39b1c2fe3365d3e31e7b8c6ee0a425d3536127c09832d9e1f591ac9cc2e710c24519a0b3748948eb007d3a2766bb08281aedb60481bd5d68e1ad2bcaa68f06db274b638c988635d94e46ddf5f60e4f161826ee422adced021b1ea7495d978349ced9fa4f01ee3d040b06746c135e015c7b7828b9c711c32f642273b9e4b9ebd4ca3071d4f40d142ff925035794d6d29392299848770b58a71baf0048319c55bb8ca6eb03effd3c2569423322eb03fa68747c6d7b0ea3828b3d93869987158865ac9a220fb9ab7ed0f30583b654c5e505e8d3932e7c617004001bfe4cd3eaa4cb47eee0fed0a520a04eeb9193e8a6277212b68ed541b707682469b2cd5ed95afd7307ef165631b2efcf5c74a28122021b79b000e799736c0e58e68516d22aa9ca980f92e788efd822f2342410f163f4016a2453820df3737a6fbdbcc59b12e1199b1aa8ee0affe1365670b518c3b99972bcf63b4c810c0653fc02a39fde8182d7219cb323a14428afb22d88a35f6edfd879e6b0be47cb57f11f683a3be987cd86cc633ebca576e047e4c85127ff4c209521cf14c7ff9526bd4cf3532d440f657b58b1a3a2a23f0326724e7936f2d30eb2da1684b1a26f479e8081a75ed35eb1a3273364d31d20fe70ef6a627a9669891d3deafa9201610921eea3bc853a64064054fc969d6f6038c54a760f4d6b94932791f4eaa136ce3716f64de56d62242ae1a19238a6ade88bded3da87c68edb04bcc69d8ab20cb6fa85d1da95707f6f3ba7df8c3724bb6b85c90243df8080bb65a583e20026ca70d2801f2d3baf07073d78265f5f9fb9dd8bdd0adacc337d00274a26a13a2ef2145d9f1a80f4b6f40adcb55d422fb0d2faf001bea550c4901a482516700c7f58e11be55fe14d0b739c2d6cc6cee2bed74a0ab59bf05b61d0ae6bd835e8728db26cb0ff3734f37adccd8e4308816a61c28f3f9a298c8c7b3b9447aca707b280527b9bb58f7c220bf8cd30429767bff57c1698b10051549576909ffedc719f2ed4473d241c1900b0abca7652783a14d5521c602ab5b8f7d64ca2a279e62017a54d45c10845d9b2c40641d606b6f512e7a9bd2dc73cf948cd911564c5b1f404b81f779869f655af84a5935e3e221bb3a88c2ec3c8c5c3c460e6d2ca66d5df081641946e700fb66684a61475554b21db9895667465eaefbe315174d8a5ac3f8f54f0731d7e10ea433bdd0735a75f6723ca8259228c6d75dc637a181db9e4789b768c67f3e5309fcc9f9023188f97602d88a4874a2d09a4b2b894e7e42c7c52cc5f11fd22a14c04662eb6ed34bcf30a456ce82694fa62fdd45d108d509ef2c9fdce020c4d2d0bb1f56b71da11d867a0f43743d0002364726ebef05fcbb689f3d20a1eddc05653d017946fbccdf7f8c93d5929100a6d9fad6a8ed4414f826a3e3822092da74e6c1f8c5e0feeead6d626cc9b840d38fc6a7500b59f871a67d3460f9aace0c8df595d644159d91c7b4a03eaac5066f46e3bfacfcff0c44a196dfad3c70df2784e4f04eae3b8e151b1d98d3f28a9c01ac7d73dc355e4e41648bb032468699dff73296609c2604ff1e1159768dfabcf8e2c6395711340ad4a205dfd20a7e93de7fbc95542dff58b00b1b0e8079b83ba32843c69a266deba3600393fbf908c16c93fb5c56d3fb93d944b0629e8401074130be0549141d508403cf1580c4ac0295ab642c4405a0c871ecd2c8c3415cf2a3931dfe42e68db793187b05e7c5a23d045d9259cbc4684da1516c7c782725b6e4f9819022a1ea664a7af0e8ec56b5db3698cf03bd8c27af509a9fc63c591c33f7c848da54f5a1f0304001739c1756e5d62457a611e0e8350092b002ee7d940e665d905b5e12bee09bd8892fbad8c90c6bc1df9ae58887b9da4367f363af8a0ce588077184a7d9b6e41f8eff9ab4c9085451986aeb208ee607c778391c26d07768f92ea6b47d3e0b67b3665a0b54b0c1548dcf72cdc930cb44e5519bfbe217e8d360b0a68ce38b6c0f1819036d6ce664ce4b8468db54e6969033574496d8923a5e8b58c4efc44fb17e768e0e800a7432a16289e7826481f07e58fb59ceb75fc13c8285d331a90440b6fc7e362400226cec93b0a413a4350a85cc5509e9b5d009593aca6ecba5a1565a7b9f7c2c1e49c112e43cd73d20dc29c0795b0dadd3946bd61f64eb615a0b18dd1b1b297b73c3e485cb6fa2f62c3b6c3028b3f62a387e1b4e6304e678aac2d696e5a956b72d4199836cd0ae72030ea55016d4b884ee0ec1575ac349b409ec8718e5a98a02f3715f9f9f35b296e3c392f310f99823aa162200e331b7a4790e8d3fbc0c06a7692f9492919c037452f9ba98466e657c7baec16a474539d2a142f65f01cdab84691590420c1f4c2131702a35a660de8d6682899f9dc43868747041386b5c8c94674a964b77a0180c73fa68bdaa0092f316f1ad5c8587f80f56e4c70042453b0a1dc86f84459c9a78b30f588a8aacbbd42b1ae2174490d17d16138ed8348708bdaab59c82407a07b8624943279d95fe56529478eb58831863d52ae4be8b912a6b6e88da10fbe20ccb270fc7f5a4ebc5bb0692289d5af9c1528a533d1d940c6f148332152880eea6a038770af014272438ae9dc67bbb0ef289f2fb428aeafb45560d2c589f2d6a8428ca679fb42c735f739f786a048704e55b28e875db37c8b7295c4b033e5c105013230b5e85cea8e4998858e910a5263c6684e01ea0409159de118a2547eee4497e72461524fea81f6c966ef79983944d56f24e612884c646922ccc80c6a5e0df96e4b5b89f9e2e52657e2dc6a8e20683623875452177374b776f444c69a2acc5e4d30e371b99d621ae0c391044b260c9ccc500fbb25961999b83e66c0a3b9b389a1b117a33a1f550a4c5dbedd81f1df607b4d37e19dbcc40328a004df00164e436a8414bf15d1b6b55ab0a65a8461984ab553442a7cf1ca383699b07bb8463ad55d55b1f015a3bc5486be8d69278b243f4d04c80c0886d24841a4adf0ed670ae56f9ec22b0fce7935c5c5d8754dbd764ad12c08815569406f055d35fdefa33d2efdfc7b726c9af0192eb51ece1f6875282f436ed546cfa1b721f4e801c83b474ed7da24e260abfe1910e9df79fb67503f3ede6a404fa946ab5b1449ce5fab81d9c554ae85eeb7c4d1983c365822b79ab41bac6630b005053773801362cc7a8d64fe8e6a77dc1ebc1fcc70bf7878ea2974d84bd2a2fc07f855230b8f026a6726269b45f0475d3b21d0b2774dd4214b611d81ab08f0d88cb80bb68e99cae2cdc4e0d4e803810ca9d4b7826261c56497821d2566f8aca3f45885c7584c7d1d2a0854ca59994332cda981854cbfef4e8b316c506ef70e59aa591418f0b7cca38bd5e083746271f242dd8bec361bb898886582b19b21150fb22dd176cb1385184a5f01c3bdc291caca8814074e201b29b81335b57c4ea9d4bf06ab9001a71967d549cd03018627daab51f4cf776ef496c2f26732dcad6636f61eac10b37474521cb76fe1e8fc9d2f08577620df9570e3755907206f9476c7bf503a4c1898ffb2865e9d400375b108ee297fa6e47ac7205a27cf379dc002c20a1f6ba6a6374cedcf0825ae6fc3a3b369a1ca5b79e35a67a2e628a44fe00978b80280c44f813237851ead1dad0c064f21fa415437475fb84a27f6ea2d67dc4a1f635f3938a6561916c3148da8ecea4db9d4a4c86168f065f35ac9eb9666850059b316849440314a5f64b8eb80cf3b03f7b700a446e61d831964da97ccf90934e4bb268280070ddd708e9164f336cacd291a5ccd8b313984516a062ea0fd7533b63fbcc4539fe2b00670c159703e0b6b49e6d636bc3c2232046249359c158e934fdf9e3212a8ecd153cc991d98392dda2be7a5ef6803618a5c3409bf49de7eb1e3681c5fa31af3e24f90702ee330c8b01b2d7596df8f6d51c325c8e0851a24795b0af5feb5eee7b5d6ce4a32256bc79b04b597b9a23d5ea75953d29c20baf956ada9bccc3ff12f4d0f3de0f35a13f8e4817dda1dbd1077eb5ba4721148d4caa915f4fa1d6625f4f03d6b79638b59d6c66701de5b830210536590ebd5b586d1232e230dc12d458a4dcb690770e20c38506c2e2bc59ab2fb47446b8cc270466da69fd841b6029fa3866f0a99c4bbc20fd7c2a2c4504da5e23aeb0b63db5689593396da905736732204e6ef0bee161a3a4193302b3b43e2bb6f8ee43af0100051db7737092dbec401652a074da90c3297e6d29f26ed26312203bbeb1489ce99eab575fffc6aa45fe99d3cd23d7a6f3549ac17c7be5d914f091aca4447f5d0f144d74991d922e20c2be1b62f20437eae0867bce25897c38e1a785c66a21ebd62fc1c852bf5d3d8718f45e721db50b6dfe96838720e8bf0b168a9057dea970b858ff54e0b06dbdcdcb810bd08d4bee46594dbcfc2fc0da9dfacf0cdb51bf9b9e85d3b59c3eadb9689ed89811b295a79f3bca10192c7c78fa4cd06f536a41a059bc300bac32e7d38cce47f57a20b719cb3620f768b49138b8c39e52b14864aff7800235e010d411acf5838745e4bfc14051a3b62da5676f4baf24dc8967051e35963aa1a201a8e7d0a2d518cee27f918089aaa7c6a070185167a6b64348952cf8e581b444df81ccb5ec1c67f82ecd1429955b918bc2991d381e007f35188aefc306aacb2bc186a75e7385eef542ce8238ccf80604384634ab267a526a3ec892ba3c08da3819228909d45b21afd218f98ad369085d4c64e3de7d0e2e32a053aa2f94d847ad65aa51058ec3bceaa23b75b8717363f9609071e7acf45c48e93cba727bfe1fd2f76297c402fd094e7efd06f7c4e2b1432dda5b44ff93f7f84a28afcbcb6e08849348515064507317aa17bc95bf8cf452e13264e792e652aff02df3b593e0ee90113d8ade15e5fd383fa212cf3420ac15827d4a974c2f215ed38b83610c957b8ac24abffa47b0832e0f0d73dbf5ce5b919a7ea3068c54f88847a238455dc0bb3fe4cd312a4eb71951c03274e269263c7bfbd9ae346a555524a4dc60d52aff7f6dad4f452614c6eab787f25adb4f7ea2060404021dc21d4f73f1c653372d0f2a0288b396f9a5d679a13852c49ee9047ac70c8e36c751321d8984291293b684501eb06f2b8afac4ebb6c96158bad625efa71beb772606d25cbba0f26e7e89b25c1e89b7ef4487c7af9ae8a25c7cc78fce350735d9200b154d4c1be93dbd3e364ed8fa3cb07b41197543f346ec925a2e493b222da7eebf9ed786acc1a8ae4356d09fa6bcc0b24d258b6520881a2112b92bc150ea0cb777238b68d2b662b9946a69b4a4978d28cb892305e4aad590112572225d68e6bad87dca0ae91e3f53978fc62d9fb773549dcd808352930509aea00db9afbbf84c86972588ae2214481435152443e5ca24e73e9b4a1d5e3bd82b94d05d6ffaa64585517bfb257c6a915d8d5225496e561f74edd69d6cf0b006d74c21dc4953e34ead6eaa6f29050ef26884a58541b2e751929eae219cc43658e6cb36403267104b4a8179f5f2b889830b71891b213cf4ba957e8c80020c03e7094e15e31297b0f83eb3f77980c2b4803141f16929ffc0f22dcf63202189ff3cc94262c6466be6ad7a190f7fd646ecd51745d9ba33f44ececde0910b9bde7f1eb1dda25050ec64cd0befa6ffd71f4cfc06d4865c15015f693c2c52148b89d99c281632d91935727bc35aa94d05c08b645bc07f0e6a1179b050249212acc15ddd01b1417cfc45a9583f7a948ee6e08a8ab31ef7eddf72d9ec6567da69315f7922eeba98807c01b7a3f69dd2610cce35f2470894031ec2b5ae9c53aec2e4e9ec1217ed5c4e125f615c2e466c31fb4044f120c8f215d519924e6070abf6b88ece52fd93dbe33a148acbdbaf459860942c0a51047530c2aced2f492ae83ff38bfb49fc6512e292527d18dec7ea382baa3774a43a09c1fe0dc702db66c1b87254350049d6340533187958a42cda72366636329411a0747e29936ef2c93fe60b6bf81095872c0d8e1d277cd1949a45e58f95e6c72e58ad5fe9cecd87d0aacce0dda99a075629a808d63a5e252ee12e385d06b1ada0ac4b9115410e5e9ad1774f252502f7da091ee5e12b94903d9a9c7c2bb22fb31af832056bbd4e91525e1d607de21551515e254b01f11b5415b911092b52de0c3bc11f6cf72726de85d3708f81e24f332c3dd65646cf97a5571b1d5ef7814aa3c3588c924054f80331e3027095a19007c68e0b11a8e87b363a05ce5a8d7169b5f62e749fda115662bbfcd3b2d579309ad984ba0026630458e5685e9f523e30d3ce4b12f015293d08ee4ae6e49ea0854ffa80a8ba632520920336a09c6eed6e9691267e81b270f74112353e441f34b229e457486ddbd2907666c8c966880414f3a3a4adda0f0686d6a0143e16fd1a343d2f81270a381bf700893835838715c83dc97f116c68448086c3efa8aee42c2d6376aa43571b0417863140ae12d09ea3147a3476b79bcfd5e785cc87181adf00425a83a92bb2abe5fd087043c7f3452f72798e06118044dfbb8636e61c42564a3cfd1ad860ff00a0dabc24d24b5f1aa4c52579aa9d43fc83c5a0bbf1e53c346dadf2727b821774609f939315b0d4f55c9f44f10b20d50470fa6a73f8d55f3fe60425eb12d2ad34480296554ff78284b01850a3ef4a404993874e98792cc1cc1b361b5052b5ffd69c255805259be4e4ce3f620d0c69e10d76b310fb8e626b854afa8d90477c500bc5456c34da7e1b182a5edecd892bc1a8b9bb972687f450d459e3b234b9ec12c636bc4dae4d1eea6b88bcf2b72751ddd79c4c0993bfde2c7363421b477673e0d44008e6091bd311784a94c0df7397b9416de42e9057544f788b3d4df875b233c97c009c26cd5bbf482f519f00a4245fcb06dda9624eb2387ad06cb9b1bdcf944a457bd78c0c662e1115e1af68df9dcffd0e55a65ff68ba1bdc5dd3b4b9f960078162027a351034fdb23d103fa8cfa5278af4132a26d7419c4f13e2761590792ea1c2b15c4dd1efc2861af06846f46f80e0a2ac58b7236eb5117baedb8a1a1ed6de97e99c2a4674911e01fbb1629f0000b4c7cddb2c127880f9b6b6116e31a901b9bf676b36387e4eeab3be72c338455113327b255b744ecd2524273b02a3108dc62131fcb0917b3a2e0558ff1a91f502e8ebd630a19a14690e64639285d9873c0a2105208b65ad312bb4fa8ed8075d0aa91b6a6af9cef8a62026950ecc8173e1f49f7338ad52d6156672a83efe6bb74f13f6c34763668af4998b60278bd958f973832401005f364183948922185a954959088bdfa2b3ba250e1c3acc1b4024208baecb3c45147205370620daa5ba562ba24334a522920e7f929e54c02ea9ff4268bc8ed355f306cf45f223b0ddd30031fd9ee6b5be1c89952ecb0956380167c001f95ad5ce9373c00a4d8c315639515f329ae8382456a448c7f43bc095a8a87b34f628e9f2fd56af823b8df01d6f01ef64843121fd090b11057d76ab194d90e82bdeea95c2f60d292e4a208724332052c69edee0a2d20214317b06b35e930c97df8898b395f6b0785227a01b7b68043e417805abe12c11ede8ea688703a3b9882da994ca942f357768ce878cea2d103210ea2c8b128911432f971ad08db67991e403cbdb29098f0a3b62f92154882fcc3bf8f3e461d7e99e94073508cc4891c097b5a9455fb2082ed105483f54e122fd5234b420917c7e17c1d71a8b8bfc0aca7e1bee7168bac38e060ab14a4278c986316993c8cd2f7fec82135c028bb95d8abb6dbe6b607a4bca18a1d0fb733198944d4943443fa9cd83364aaf752c8331b7924dd3194d14f73872193e334b6c3f000c498e9b34f10f2d46270192191b5220a42ba68ff18027716ad64be27d808879decfca0794f66aae7689b5b4afa7d1249ee0affd6afb05cce505ff80081dc4357ac80ac1290b5fb784d74da73fc6e23a8916475a67e723a6c479b93cc04d669723dd4d28c2257b59c9a5a650101c985a033ef7b701741b61628b266438a173fa079041a203618e85520a8bb9d68b708bcc57f7027d54f47162dc28f7f676e095180a8efc2fab2ae7b26d1ef24a4bfdd561f2e3d0c28f1098ddb36dc613c1b3f8812d2043fe8e2f0498c8efd42d56699e485f132609a11c3b85fcabfbfc097a44835f173d6ff4b0eefe9eed3034f47a21722c87f267bff8531498de390145539e11711386bd81557e4acd9c3fada595c1420d14e9062c541239822bc05d22d6baa6fe5c03418a62dcc354ca992fb53ac3327e3487049953cfe5590075c319ebb0030ed1eaf7c6a70055b0d00425b8ddae47fe9f3a005ae30d32c8ca003a22d86690d2f401eda42668edb66c6f0e5455cd445c8400989a0fffa73993d4b546a480ab056bfd18599f1075b1f45e2d076dd44c1c3b8b9dc5ab285d69d1f3413961961f8d3f42d667b9177f30978faee6adaf53fcbea3d56568afdd97c4774cb3c4c1d5fbaa03afa84e4240b201ed527aa44cfc2b5aa57957ed404010410823915d05f739171a6c8be15659463918e2a840b5e2727c323d828185c4cd99e6600f4d435c274baf1d9c283fa4057cfdc849f0602189d07124868d816f11530b5f20f50bf08bc8ddccba14316876d1ae88f07e9dea45a8adad5e3b274d6ee0fdccb19af46ac882e621f14953e6f4e49161bf929128cf47964a157c4b1e50d676a908405856c2e78089beee565e42ff186220361c16f7533344e10886a514728b6baa1aaca50331a13229fbde87e5cbdb1098bd40ee1cf665919ac35ddb53d892b62ebed7e45b008c7332ab40ec37eec0998c1d078d344f84f5afcf1db5b7c3b0c8566c7e9a06c02f3fcc4ec018c849943bd4f6976bf463014f4b0a1101e5d9cd3f112158ca3315f1d115df69c97cd4588a06848538588c770a57be6eac7a79804dd2b415e8838b46be8aa42ec487a07161b0da100ca43122c6f6f06cc6e8c4c3714e98937ab90d43d0e2f402011d8948a067bc3b6030fc106481fe43857695e994b170e9e0c603d26b84afd81a71659cd279eb5bdacae0a506a426b31725f874398086867008bec47dba3cae298528248665869d6bd2e7fd6cde3596b9df4166f6f211e097f5b7c335c65cc53c58540b39488bf8b699d4f4d0815f40d70a0c9494f053c3d7f4165fd79901be0b090ee3c177a3fc355a900d837fda6a4e70702f10085bec0f53b4037811166c4831b90af93a1f9c256f04062fc78a06c80f7217300f6467df19c3ea9ee99852958e05180414a5f7b7478d017cc1374878ca1981ac8f0faeb19f5e75780d4303f2e93a32858c5385a23d577639ce39c42ccfe2a846ec048cea73252a12a2f80156b58d2f7e3af6131af816fe68d348ba680133df2983829006a313bc6307201d06819f77700e9de18a00b21082a751267f9a19bbfd67eecbdd31c6c926fabd1871b0eae7f684cb0b137dee6086c7a0d1399ed94f6c7084f5a1468057f9f82da858ec042bc3ff1a8c8efeb028bff81673cc1196c17158cadd03061c2b6992d48618c29fb9bffc318f83795e10664e5ebd96ab7cd0851840780b016648b59a09302ed8be1915f64d19370f002d1c8edb3792c7a4e0a52c857806073ed4a812762bb86a6c8982f4dd21a873d0abfb5f024d0bc53162f8fd779c40584e9c0291be95b50493b82751cb7c087f4811ffdc69415c8c7a91a6b4bef9413280a56f55660c142d89c9b3f118d46ff6a711754927c2aaaf059122e3c2a622998d825d66d24d64de0d634c1edec6db7e49a56b505eab9155a45ba18a6ea1d33f28c8340514d1138e2c40fe87f063716559b4ac0604acb3665b9a88ec3f52407a26a3d917fe507951d098df843496005aa0f43c1604098db357868cc190398d41b413c1fcdf27fff1895f4f7f32004c0b2b1ece9f6802d73a0c02fee2227f8b6476282419e59cb7d74611b95761ed76ab9e286b7abd2b8f794558a7c49acb9b014640cd7d3ecf7ccc8d580f37659b18d2c978a86f468c6aec042be808f1a749d2ade374e373d05c27b3ddbd332f6216e7c40e72f327147e4a48d4605ca40e987bf26486946cb87ff25266cff9547b8b2bbd4774431a0aa1f24725cb5baf1e62ee03879bd60370dbc9d8b6da8e1055b7c462dc62e06eb03bd74ed76fdd18a8daf085bb4d50cc2b0b0a0a52fe069f7720a86552a426778cfe750256fcdf016a329bd314a6d19451c5f1e320ac19e2feb8c89b09ed988edfa9808cb7e497cbf7d81c2101d387691a705e374ee8bfdaa03076072b07f1759d46038c1d9469bae46d51cd0510517dd0a977267f0c2ca162a899b0bcee1c229043906fd91de1de815ad1fa893c49295c4d2f4c27b581353d1920bebb4c673b1c752822071353d121e59e52ca65521cbd1b7daff218a8b39171b62baf930be198c27942449a25061be331f664610a4526d7baede5edd22d71c029b64379971e910af8305c20018df70cfa52a8250d605c1a87a728ecb5ac9e55642a90a63558024e4c9b9ea673fa3e3a046885a1fcd57950d72774a83a969b41c88d5d7b72b1d8558f2fafa3df90e414689a1cb4270e695d5fd30ebb049aa9061804010dc350863cab0f5240c0d351bdfd366299f4137ca44ea34b3ca6e012c4d424fe878db42da11e5ab507124a691f2624139668ae6602ee1d638a6b464a0a30d6023695e5ba5c203b6cbb98007229fb81bc8ea2d25563fc1e5f642d03bf4590d7bc099a1789ecc35851f308f933805d953c230b8dcd397a60950c27ebc319a7c56456580e51094ba12b1b08bdb708aed04b76e88b4b938e4b9a306b75d054a038d210754fe68c9a57679d2d33cdc62476fb654b067f71539d21406014cc69b799a2f97fa7c9bf9d58261286b356ea18e89a53b591a69b1ce27816f9108978ccc8901f5a23409580216f053a360492dfd4cc4567f7002f9ea26c72b6a33710232bf47c26314ccfea09f8ab1943ac0df70e1ca2202d9e854fc08bf0a993c38006f662a75a27f77248b6f70fd85ba4a2104ae0208b10542a94a586006ea86a154a0a141a315bf97fddb0223c2b5b507ba6f24d25ca2e8bdf824ea4d0f0f6ae51285b56a5731f11011539a824dcc24105f5c162744b5dbb6e5431f95aaf08174f9d02b1e9eefd513051f327d332821917324ceeddfd88a8e30b9cbd199a3a6d6619652d924b7c363c9dce01544452c85e2b1a137d0b802ce6c24d951c8808c892f04f26578d1cb182b741da8158270c803c44a5ee6995282813074e331c73dc7f59e118f55488cff258ed32e57eae0ed8cc82b0b8d8e6fa3a9bdba8669c0386cbdf4d51d3d9b6b6ee85b4d526e2cd8fa2acacdea13e1ffad1f5095e087af9a4f7c98a4e6d5d8efe4d15bb03023c3bdfa654ae25a7706dd8c554d1ebd85a5999a6745e766853886b239ba044895d0fbba9dad3a02524d5622d107599ec912abc3bef9ac278297b995a86216fa409d52eef9c9a2d8c9e9f91166f0091c00ecdfc53aa88264a0bdbea56bf5bdde72d4ea60384227a0d0f05e09a3807bb1d83d30e14c960effcd8e0ecd9722bed40c9976ddb7bf78cfc6f0c7cd32d503c2760b735e7178d3d5db8ad4eded23ebc892d9f9c95252bd809f9ad4fcb14e2b41d627270b69362fe8d4ac4e9add5c72210e25b6a03302b1490ad1b40cb4786acbde23ce079973f142dd1043adcceb18d84b5d290d1fb2caa8ac02ecce824a6e9b0b94c6ec950cdc68aae56b712131585e41f3bdb27b5136984c1c32aa2a3bb3a75012d2cf6dcde95e636fabc6423875349eae95b43a2090e31cd0f9f2e9cf7e700901e4597cc2336758fc0fbe2f4aef7e3ad0e790dccb8a94174f1391945603068a16a2558bb76e42e06b6f9104e06a3256ce6c13b44a2c34471f0cbe9e2ffb85a15d672864aff6ed8e6710391a8134369993281b9b7cdeeac43f23a54efb71a4449403716b2f26e69a9b4810173248071250b5131b56ce47cd41caaa3d50096fc5046d0785275906172b546b05747d0efabbcaa85c3db784c9908ea9215fc39f98b0ccd6e4c389436b70259f35ba155567457cef71478d65dc05db08485253967d62cc1f10e0c5aaf6d0982421fe6925d73a07dd474b476609d9fb63b9f6871ce092c7b04f0f6c013acd9e891874e5425b08e78beb0f12726c25217f6de1e0515c1874b1ba636b61b737f62fdc50a782470b0249147ffbf3a732291798c614cd3e47812c60501d6c8fbab24c07153d8c4ed5aef3e9f2a4f7379d2ff03b69fd1672f99a74ce00ca196b066113d07002ac43fc38c715080f8ff8b0c850a0ad5817be4c42c3d0e69668889bc5c1bf8ae2d1c377db27bf92f85e522ea64b1c26c3029de5bd1b063b7de6ed4a49035daeb1e2af8577eca4132658cc979d1a037e3cdbaae8596d417743d1ee1aa5f9569f01a460b55e895e2377a082be5eb89a14f4f96e70ee5e85f34643cafd7b42a683ec97e4c4254d59f0af8b52583a87866fc946559c4957eba08ee7ebe2ec51a560162c43d76ed2ece053d59246762e0041dfde9ce67a87fabae2cd95c4d414f4c91717bab4830c99c212c76ae2b9a801904b858b89657b086f5bce94d19d6dc673924fc1fa694cc4df2cd34e6b1fcd718d3c12a067b90f7f8181b6b3bc7a80821d7d7431f34d65d36a372ae3760c66401ef32a98a6803baa31195f72fe508d0426655c051784d3b456d2bfaa441352236436381b9d030b44a7e32b162174e93c743199e461e306af6610d9c7e4367b4f375281d14586df7c38f650fe8de4ea46b1048c99932075bca4e326b8c49645213582e732d01cfa4cbbaf571c32b1bf87a01419aa76053d5bf4502fd3994301beb2aef693459e55c8320a903276917818261a7ce47cdb9f6289e227c63d765b653f6c0487f42078c8bd800d065d8fa18e45dda633621019446ffe2763361f21df7f32ac205f9217066e8b5d60ace189c856787255fb064fe4c970ca03bd25be57053623c70a318ec92659f2fd29a0a0b43d3d4e591a4ea0578a2f5b95d2b9b962714240efdf0160fb97126a275dd020a4b804be0603551edcee12e8d110d013d3918e9530c58032b418ee513c11eff44bf93a883771ed4471825e9b43b34964d6cd25a5a196cb26fbbcd278b0758e573a4f96ea4480eeba59f9196e0a1ba3623fd5e4d745ad921ce291610572851a4495774e3c47923f9c06ebcb6d30046688260178540c75d5d3c884f5c753762fbfeab89d0ac1a5f1515344f23b347dfe65f969d46525b4a0fdaebdd9a0a22bc6e3a944155344f44eec16153e9a68ddcc312152a79a593aec6cdcfa42bc47ec93b05666f6a6a1be4c305cac67d0348caeb39c4ceefc01f5d08407c3c590724afa34418c6e97350e4ccd9ddb43ab41462ad8f59de0a671385b74590b816cdb27123142108f5f78ee98e3134b24118906f6b6e9837bae4a475f9f371463891c04d9837e4ff3ac8def19d6df74c00f2cb355a40df3b4da5348bea0edd68778e3c22c88757b416666f46647cdbfe7d4921ce356921aa1b19800259f3ec9f98c9830deaf665b317786eaddbcf12789e1e8768142013ef96b9cf590fc741700df791b97b5bf4462135e357a16d3992dd178bf8e1bb355961abbab0016062f78f489768559c234b2bcc563e8243b8d3d1ad4bf6e38b72ad23aefac0e49d4a6f38f8c45a2ecb0ad41c02beb385c27528766011f056e085ad01beeb776888ce964fd3101368a90d214be9ab65d0f9b92ec14f7998f1c9a7a945fc2c1a35ae33517be9cf11dbfd2674adba737e4b2f8eace698e26aaff596ea3899d8744b5f975982f38c4d5d0a6f352b9a19ed03a832691d006673402fb83879f707d9aec0025f304f04c90bbda1390f216db332e8fbc7db4bc24c77d73b73627f402245ca3e26536d02d4d623f815efc30965d96a8dca08eba91fcda7165e0e5d63342602293a38420dc45f8a6ef6a430adcc1ecc2ef7554c357d7038269372a44a4e97450b582c9e55dd9bb2820faee78f88db6ef92fd34b6f0110533d3708c075b1ac105c041f73c657c04c95a85809c8fa35d294ba28e656c7b30b55d60582df88bd1361cc42b93444908880a43048627d9cef367a64495693e099f0ea443608b2ededc8cbb695c3f9f23de8a5ec1d1f86281e16bc212495c72e1f0c83e17fa7f55d40f6056ef27bf95c4ec0401dfbef19264c9b686d859d0b07167735344b915e9e0da6b2e69288914007309344f173f4a0922a5478a2c505e5a65bd7cf984d9baf16f84f050be745450f509750a62ca22c683a23f7bb5cc7d2958a81c1a7a8c61aa30dbf6c8a94248c3f44230af700974191944dcd2a526c7dba97780871b5e750748eed0bd6bfd6351e21a2bb7418cc7411b0ddefb78a03c9ebed3855c447c91428974e2a103fd103597c771fa9ec39187068e9d97eab44ab98a3eee8c3c30f03ed7722b848580243643e3b063c4bf80b911c5db2722010cf8a8fb25cb575065f89d5aeaad453da78a6e4647dee83e23624cf0ee45700d016fb51a14d99305c42d3cfb92178ea53b8f2f52e1ebb0276dc0f1242a9a1a2024ddb391419777f94efa7cc75a371c241a3aea706991da73f6e9fc1ef0a872011ae12b643c49fd50f8f241cc9f37fa8a2931d10e86573a1e6d8e341fd64578b254cd1c15ecc26bb1a7df93d1b8377b1fc21a783241f5a004ff6280f93f666496a47eec1712c27747640614c8d04a8e77fae348cfa33f1153216dec503588d13f33ccb2c500dd1f8701f68099915a7f3345002f7ca53e3dd2fe01d4d1d68e24b7b2b044182242dbc51424c8a0b0acee71e1546840937e92b998324ba946caf6c1d076f16037d5835c42e7e43cd87f3deb7cab181000b3abda286899148977bd577a71e0251d27b6a905bf2e2ff6897e66b103b8af3aaa079803afa6153e61afcc61e73ae8d11f6f213db1ed2e118e49abe5868a67964e131f2fecc05c91c61261f70b0eaaebe90395953cabdb468a71652d310e68751abfcdd72c5fc4da6ffaafe5922a7f219d6f493ef251a9b09a6ee9912ac686565523853e542c48b9f833697c97497b7874f825de324f98213c1c2a0c80211d11dbdaa31cfaf4eb65ea78698103c9d99cb6df81e1892acfc44962800286f11073096108d4b46300328677ae324996464387fb270641f56f5ff91f4877276c0bc3bdd580c50a05985821f50d1d2d94df76d5598770ca269817088fcc4b5540538e0bcae23e26ed8e109ef6f529561b98de7503ba01daaf4dea755bcc46a6e1d71a78ec757955bf7fd3c2f108a5bfa486624d4811375be59977c2170d93c1f712fd1211ae6a7804d9a8a1a0108662060bb104cb6480e161518ef10fdcbf7a58dc1265a5c32f242587314145c5fdbf02d9d00d3126b8646f671d055d051f486757071a7cf0d7aa6c6e4e870b2778f97437215f97c182067bbe96646e2488da641e8150d896cc339cc4fa09dae5f113207969dded914689de0bcf9ab796e4a00befc21390d328888bf7b5116cac6fd08faa171126870d1964ebf2e9d2acd04d3cee8a070fe0581952b23e98d491ea7d7a48df8700d6aa5936a06f87f4c448281884b40b0b434dadce673cc8814a82657053b510972489b66cc9b65cae5c939e1271a5cd2c5529a090260e3e31b59106c406d541d347c36583d570d6dd449ede9fecf5c29bc579a6a8c047b92a216302047672484167522987d0032582060e0da1069a868bfecfda1023aabdbe91ad72132f29122a9a890124d9f3899c9cebca95755857e9764448e979f43de555952bd1712cc04108bc59356af1d43a731912045ed63cd355ba8e5917c78d3055de7f766181f08c6973d440288176e87b5257b0c560ac25962bdd40248863d6558e0d98cd624988aff9123148586dcc9a25e63fe3a05ea750177720afda58e0a7144bf4dc401fe6796cf268e95f070db5d48e6463d2f3d310e4ef8317949112768c65a43ee85684349f4284023fd2e31176afd9369c647676a78392cc3e32121188700450693f024f2c8f74d1ab23ec5bbe1c34fb1652c7a465d2420c8cb9fe838f2bfc4279082c411970854ee0e5ccfd6e78863f3e47ebeeb0bb12e9f4a00881a28fd33533d1d958c279ba339d227adaa40d798577526b92445b368bd5471745b14e13cbeed7e7642486c145ae0942e193905d63bf94141480c843588bc5e633c6b6841fc1354375395e1d6839d11c4e7a4823184a2bc3fb82fcdbc9516ae9c282f3e8473755151a475dc92cd3312f204947cd2eb8cde95c466e5856848934709ba9e30871fce6d782857e0a216a4eaf4b37719700222aea041e7aec4addfc99df3205aa9198832715fae4f84844df42c094820cb756d59afef235b95e641a22235b7c6cab7ce6171a6d5117a9ac308166eed3fd62722d54d96c34a62cececbf5abfcd2ec15e6d8a348b441f304b1c7ebb772fb794bc5b0820d32255349b33ce158734e02556aa3679b433c70411393a7e913346a50296f6832c74eb657c0521b860f89906114c5f36d5983ef70f900d9802d41a3a08c1df354f79636bff8c737eb5f0cbf3435a18e4405a54b30f6a4d604f9f5cb02dc31813dbd6e61e9374b4b8d1f5e2713563db4e83f166907d4745bcfaad845df906daf491b8695903f669a35e14b4c04acf36ea9ebbb7dad0ebc96d97f6a9e6d7aa68a8837c6f8365a42f25ad8d9b804266e342a82d809ca0042aa5fbb02f579b333890b754d1c46f0f1fb2e7592516afde79ecf3c0f20ab539f5e7c702162766863890209c36bd1e57ea9b2e72face499d785189d613338f2d3067f3ac8a5126b8ec7665bb7349c31b1b6b68f9cd0787fe7d5f7e295df8c7a945ce511a52fff156696bc01244150c8c18370a123cf977c3f38b497a9559fb12fd7e662240091bea39bbf33afcf87c4db4b99d98aa487fa3ac835309ff9116a8a9dfe6f781ec80c36cc5033bf2fca1174e43383a2b15c14b4dad40cbcb3c59bd09a6c5c14f0fb83db55bc176c2c27adf5a5ee5c1472278da43f635acec0ba4771fc3d1eb2c4be97a30fba3a8f64e2d7b70306ab2a63726c4efcaaae97af574f4750ae0b4ed33238926a124c10a2a0a88d9fc2e5c5c2c4aa93c4020bf181199edd2fe823991da472f27dae2f72151cf9c7dbf43910b37230806dc841e741f407566353bf875d82b14034824e8c1c15a45403e1a9c401ffd515287e0cfe1c8728a6e9da34b18a7c6109ace3ee0e4211c93523ac121b83edfd4e5c1f0586a00fac7e7d06221787053c1ef0ba0939b4f4f4078e08a5fbd06e33431bb96b0b6c6db3fcad6a7934be97aed1202550fb6fc014c4b1c355415306256c06d9cec3c623dd6fd41d7f55e68e67456a6ef48e195736ac51bbb852b4ef47bd734735e48f00fc8e1dc334e066460256597f2dada9ddfa82580c8a643d1b31beb2d3ad8df94bf79922755d18949fa633ae36b273c4a5984974df21977823dfd4cdede96bd125551dc7ed9ef674edbb75f9367e98405b344bc55d9442d68364ca925d66881e5c7f1a350aca2a487cbf8818c4e1071a82da83f1139fd1b5c5aa66b208a51f9c672c8eab0d94399a8b0c6e98957bd3ef5003b1d11c238c0800d4ca2916806053f2be3d20c686858263c122bc22bc886374c029406888c08ea58a6355e064fce599c95d5576d854b43c521a30f17bf0e6f0f01a95daf32c4f0fbcc9d1539f52cbc4cbd03e3a0376729de415755bfe54afd7ed2fc0a8cee3062465045f88d5fcea9ab96bdbad46108f8619ee92ba58a7490624a83931df7759a529740c00481d4850098185e6dfb8aaf2cfcdf21d04d8497ca029c11cc76e5e464f7f46f2023b78467170232410569885fdcc0950cc1371aae27b5e464097ccc9d677db051ddbc829f962ff39898030169d3fdeb38120a0b6c3ab540dde04d5aaba10e147f2f6c76ba2b717251043bef2d680974e3dddb287de49356c0f46411baef662d7d98ee361ac41221a5caa7c3f0ebe787263267f38500eeeb78b7f79f2668912a501f1db8c633a6ece19b2ca35b97f39655733a988dc9c050b1487f8942374d647ee81d56618b3aa048599b17232fdaf2655a91c3c9f32bd7525aa25dbb0077ff07eb47f645905306885e0e05a91b14709ce5b2cd4a880582bcb92896df1401446662714e98c1be2871c818e1e85453c1881e003111d4fb62d66bd0bc9601cc19382a74bd568536fc4a127131740bcee58042a9603de6282d6b0cccda7f5bb9090e81dd842faf2afd6a1f16663f57d9d18f676bc43f751bf4a1985a178664f86dac90d10e2d43cffd0873481e89abbf5c8e49fdb503092c1c0f27c4cd3e08d48fa6dc91c26d3a3994315f86e9c02573fa072397daa1d77d9d6dd689fc994c0e5373da8156191f97994cb6a17e67c4fcdbcf3587b79754a0914031547ecfda050b8925532bf14e1d5ad0e954de067ce7046251246f2225be55c7335f32dd0b43441edb1ee405d615abb14690fb5a76e395b6497c09b27a2ef04d44f432fa4806fa8b2e1d36584f0b92c6047123b9b056728e5a9b4e8b6a65dc4c512c261a963c760a549c02a0b4f36b587d0501b714dea7073402343ded9db5d4f32d86c659ea781170dfadbcc1440bd4d0d6f967558485b9062e0221a9afc828c794bafc8c362d5110f299a1e2cf986634f53b91da1b3897739ca2793f8b5b1a80bc1fc171e96900b5863acb9a10eb473cd89e8c4f0b77575f5a3c77f718ef3e8d8fe18a10349a41f7532898f5409d76f113b445f0cf9ea51cb66113ec9903e6a3229cba27f31fc8708db75a81b723ba5ed6ba27e78d7614dabbc4a839e4145e3978efdc018a796039092d587b2f60d68237516a493a4b6972441d4e2bc0a607521cb1d56cf6c104807f4c0c4040e2cb4bc0fbbd57496cd798a4ec77eb83947f2ce49f573f6af2f9469f16595afc7b84f70080d12a7cfcf5ecffa7d5165c342c90057dbe75c01cb8c831590526c9145dc4769afc5fcd61193722c5dfc89761cc07642f199088201c6da7aceac1a2987eeab2e5ca9d2b0e1ff6bc10098715ca9f3d776bcf4c9b5e1345736f65da0f17bb1d9c1cdde974f38f857fecd476e3d614074ca8a44160e63a6ff24464a3e6ee854a24fb2b03a7b16ae667856b04d55179d0da9758e0c370723854d8fb1e568fa0e2ef72f4b10e2134b7988bbfcb27a55857df25e3a605461e5eb2fd4245fbc6fc422d4a63346a03777e296f4134dc6c756170fc218dec5270e31bdac6e310fa3038d40347bbce9852da406ed6d88c7d514324b0854d47272b2984086a3db454a512be7ccf87111378372c257447d8965bf1597380de5300c41b569467cc21b019ebf34f0655c2e0e67146d8d6d0318ef4b2820f1e41c6ae1e1d5c852b724882deb5bac8f1c9c0779ad78d72aeb6341dda394392b330d20871a29aafee6e7b1aa85b1b9367d23f2e9414c41b2d848865d18a2bd5101611fb55a6b834047516a16cf978055a2e62c641ef023845dfc6afeb3ed72ceda20b64d2e79388b53b0d572478b84bceb1ef21860a13c7e190a5758302faee5e50684d5085187c90518489a91fc49b273efa58954f853638a6e92dc3b7be46ef93245a07dcefb0fa78f0008748ddce928d4b3a67b22c2f50e937683b02e48e99f7e122c04f2ce36b0d4ab1f9d205ab8de9bf664c9a75ca33992b541793cabeb7f44a1a0b4966d0e6c2de08ae4deb872e2869050d26f418082daa0bbbf0428082e46e4b6a9a0fc4ae89e9d6c812fcb8401833719c3e2b54bc0f2f02422c76d5d3e3477320fdc657d61498a46516ccd28e394a946acf7961e8aafb20a3832b2cb4938010413809105b135c2d668f998f564cd5d05fb7cea89391c298a6470810b5a174dae137fe4361880b883ccb21bc56fe771e84eb9bb06c9264e31286b89457bc21e3855b76bf177ebf5e26b8f7925c14f6a801570e3bf1956afdea2da4452fd05c399888354e2885eba554dcbf484390d447d7c532d3e71c200a5e1a2a915d48b0b0dd2855049c39ca15576c3b948cf78382cb0bfb053ee08eccf4dd8d12db3edaf317ebb34d673775d7f343079374e0ff7b7b73697443f13964c6b0215cbb2eb6fb1d0beb492817e3b81558cf6e0a4a1e0e0940e69e293d6f4b586d4fde8a7ec74cbf40a9c1473da8bbb610eea6f530837d939f06c3b087bb7db3e023d97142c3c360e3e7403869f9746c26f096a48046beff5310a29dc49ffb84b128241ca4902340d6698938d6098806d745344198379ba07780e5c1ab93e3f5df5ad05603c6a24f43f160ea687f1143afd44d3dc845870f40851dc8a25f2b1f8c80947c6922010dc7f4cb5ec074c9457b0b923b83210e1d879d56b11bf234f76cab29fbc426b3e6fc30cca5c91335802ec60ba620686e1e0abe574736f097bf4ed7d24a6fba444da1a124107720e1c995298932e572dbf50167d743e30b9636835e1862c85ce675be7296e407c76216f11c0be8c2c2e05fc51ac84f2b746174fa0bc8fc569fd114436cbe968f166f19176dca8a86ec8e214d976753fbaeea39f7c0578732e89e5ede113dab4b13cae5e675e1f98d32a68e8b80ed41c14a29e19adb0f3f63ffabb1973f238bd04453aaff69577180c9fc57a4eee89de0f4c51e45d3a7b273e35700f51a9ff605c98038fd58105ddc6af511354f6aa5050c28dddf28dc375ddb86a13901b03525ae22212bec2638986db8236de27ea66a1ca3c9ffbc2945e2083e4e05db08e38c27a79fa010372caebb76441ca49bf80e6252f9e00e9f895fc2f144f019dff9fd4fa6f128556f4afb7f35c989a2d9ad48e3b12e0c6931d761e7f8a6e8c4e1683ce6d8e5712a695ee64977cf7814ade927c3671b25da2174b3f366e875632be45ec179fc10110e574732ea1144e3f65ec1ad9f76d2b0afc6489e0c00e9c635544e8dedaa019e6dd03abf2a9f51c13d506b728e1005c54e23122f54af9d8014862f5d6c84772172a2262261c381a60036f926988c9c9199322c53cd61ab950bc67dc480ba836ee5e35b06411e98240cdf64e76ad5d52d394a3f5ca8e80a5e4df017bac917a1ca774398c3fd8072296ee09b27c6fb8ea0bdbc11af36634d987550482c5c75db8ddc6733e47f05981781a14dfe092da5343f18505a5c9d1dc52f5e6fb8e849d490acf0c0f558d1f389c0373531e0862602bcf189901b0fbd442db496f5066ea8b8a953bc09cc2acb512eebeb6d12792c1f62e189156d08a433ff82f43411ea6a93338c19026396c7d0190bbe15f149449d18718f003af32f35370c6e0028b5794d9d108cd4d70ee861864b175aa905955e39638400bd66ddf442945ac0ee05321c1fe4dc4446e343f70f6e882e950b6817414caf2d5a61aabe16288dd3331b60ccf15f62a7682b6008d2777ce3f0b5113c6fd517939beb2c5f5dcfe3beb3165923618927738773e05503f263da54890f626f29c79fd8d6f69fe8b05d29162d06bca5f17b2dee6d1aa16037995e0a8387b48b1d9cbc272905c102f78db8bcd46fba6c505c1a80f7df0e65cc6147babe222bbcd9d51ee5e9760f407bfedbce9b1ed4b2fc0d5003df49da2d3883eac91f76b34c95f767017e6322f094bdd524d0c0e5789f379c7856c1c0c8c80623b91b43cddc6c2df2c0dd4a2d323a625904e8bfa5290e997f67524c9ee40e612d08ec9ed1303e9dbe612b6d46e2ee6c8fdcac3b312d0aec9db507ba73525c8990f1d7021332bd6b3934c14321bd1b72e602bcf098ebfabcb50e43a33d6cdede4ceb89116f3894ec4d4ab8de58f483a8164dfb7d9d6c5e4dd17738daa138fde5917177568f0ec532fe4ce2e44ab51609874813d88cbbec72149d2168dcc1bbc90823eb7cb4c4785a73f100592a8dd3350b20c6e5bff04c657ff982545cadad14a26316d8184fbec64dd11f798455dcb5164dbfc013334d6c0ddc107cfa068edc5f79539d88f7c196a552a550ba660788f9ec57fe14f82a5ff02a2fade5ad368cf1aa0576d2081a593cb10a80c234cbe5603859b7978ae458fdd1c255f6514bc6b2ff7c002486efe42044ece791aa1ad2bd52748b03ba86f28258305e11a903c923d591a7f1273a5afbcf371e72cffb38501d6968ec494c76dcae7484786933e02e0b38dd81b785c9a985012af52c0560b914e3eb9bb718c009f4e8093714815b04bf451061443321646fb9b794526e99a40c08081e081708283c5ff3355f5d87bd6b695a359af60182d316fae2423c0875719a0f99b449732139cc65524580edef2ec451f3d268caf15b1749a4276150860dc51a2247a3bbcf1e8bf140d31b8fe1d214e226a72e8d1fb4b3dd65be43932c0eba007ccfe717b10915da05a00b3c0fca32e06141431f3e7f01c9f2093300be279c347a72190de7ca4d5f112d4e215e69dab6af86f04d270b1f8ad0159481270dec416e1b0ee11298f4f3fd4834053e184e5bcbf61bcd617ee3b149932a97c9141b9d3912e541e4a442a23c0a97adaca8a8f8a4511af599cc079a9a3a9827ffa75e28bef7a229fff7e28a0e66cac1ff421186e77328a23eef85e201b63857df85e200b63857d308533685b05dfb409b06f05139802774d416276d83210c1f5c464f7e237283f698c35c7643d161930a79f21757886c9f45b6bfcb668ec300dfffd21c655de6329781a50ae327999911237cf9cb5ffeb2135ab42df7c3d5b32ff4cbd5cb19e7fc7d39eb9864262d1397c9cc24c7a487896cfb9bd498c44c8498bc4c6e485a242d921649eba5bd4a274729e528891aacb5d6d24cd2922a6fb9c96bac0713b5442e37bd7404ec9e2149cd0e6d91a445d20ac08f00dc10d9fea1adf463fb976038fcc0c18744619854d56cb3bd8aed8f5293aaf9263eb6bfc9cb64874cd5953cf9cf1d6caf47c894a8454f1509929657057d228516452d1c7648d4470687965465d4e70738d4ec4f122633a9eafe8386163ba4ca7b500e9396e7830a93aaf95e19562b514b5423551546a61441a26a0ea4a9aeeaaa0926ad7b5bf28a66fcf303891225216a7d9210ddd020928072e017cd38aada902651121c8fcd89a29b48d27ae9ce31ca14ad16a25b8b964c11626e12131ba2cc8268b1ae4cfc8a7dbfaeba30857b5ca22d3285e3429e0fb64b2acae079301e8e1f928aba9229dec1648abf090e38d838aa9ab4b6bf89c9bd302c19936ce4070bd9d7fa665fced7a3e68b7d42bed777b3fda5bda3cbbce31d41ea52e518e34af204084653de3b28b6fd8e3cb6ffe7ffb9a46a24e7786fb0fd436cd0e2d7aab17a0343494fb6c67ea8352a39703e75895d971b03f9023771c71abb638ddd11df7c32dfccc772d3d7628d2d256ec8933f0e53cccd71c7af0502d938aa7edfbd2c641e17082023b5a46c39ca4933520ea90749468a9184905ea41bdad23692cd5a9b8d8e5e14d6e894c8810b8b785b5454ee78db9ea77f1f6b4133e8f931a8b5dfbd2d91c0baf608e6779f2d2d82b3518cb5269154dca0394a3f86969ebe49aafcb10d472516a127792258442516de973c112c996e9f9be4094d6a4955b8fd493522de9ef7802f8c412fdb359928f8026d385422f9d17ba51f445f12d62879d167921238c3e207d2933c29046758e02779d2979458fc60f2256f12822d7a2285608c9efc41193d390b1c5ac0594abe2404494210e6a24570a4c5517864046fde0f8137e869aae451a1f7ef4019232f1d35128a13c4122a7d4500955280b797df2b2939cbc84853a4711c19c7919134122a813469a4a93ce671f44a4a48ac8dd4b236f9c3daac6bfe884145b6545f973e2dfb604988f98136ed7d2309c82b0cc124763bef1defeb75f77a5e77492d94977d6d7f92ebbe8fde9fd4c351b3ae46258ea50817902ca2c7d5da9cc5da688a84b126d95c905ad444726dc7a39f5bc728acf1021651486add92dca4962580cc1211f297c930695bec685dd6c6bed8969db13decca5aa9724a7900b77d29495368ebe2a92c5536e48babc26f632ccc76fb92431955d9a2948db6a89233123225cbf8c9c150236999655b58dbda50b43788d831af328ca3aa18b678dad3dfdeb0a3cd61632c8eed5d685be8c9336bfbc7c8cc92aafb241e7ac716738eed17a82cde9c45b86db12ddd9e9ead1ccd79080e33ab95666998c6d1321a46bbf48d9ed135dc89edc446d327357a42853c9962fb4914dbeb09907d12abb31fdb7f14338249157ded118fd18e1ca79554ddf7cf631e479af268f4e48f692eafbaeacca37934cfe6ddbc2b2847294729a5943bb19dd8b8b07e0c3fb1d193634d02c0164f6cfef9b4fdde7b2fa594524a29c7716ad09ccbc4131bc8033eb1d1d428c7f6b7ed68a13cc63d7add60fb0b31f74aa6ccd21557b04ff0d9f3473132556f8c585235bfd5ca91232606070ebd1a9d418b75b6e278b87b8675bcf9c54dbe0af3e826bdca2b8ab1d6249269db6646348eb2bfd22bbdd22b8c57302c99191c664653188173ea1cad9c9da53be7042cc6321e7c4f40a6cc37808a297b5f060dc0c3c7a33f201d028d8c46df287bdd6884af1d8d46539e705c96b55a6bb54844eabacef3bc9cbf6fcef9a0c7436a211291340824d22323229168341a91909094949490482413939bd4393939119d8c9c9c9c9c844e4e4eb277d2e17b727262eb09474f7c0ea0560bcab8b89bd39b2ea41622520b11087a3c1e11a98588d44244128d8c88449fd168e4198d46a351e5a88fa634a9f65e8c3b3de77b3b7bbec8135b88e67c7065071d408fe7f3d1201d44245028343222128d462424df2c9590481f297b1d8944229148539edc8b31ee3a93e77939e7ef23d9d173ce39e7fc7ca8cf8ec39d0034e9d339abd0ae23d9d1397f2018e35f9d2ee67b79829e39f59c7382a69fcc681a84ad558b3e7ced4807821ecfe7f3d15a8340211d64acf0e05b8e8c8844a2d16884b1e7f3cd9f932b9d404ad9b669ad4119209fd42312b13f7ace0902cd506864442402658c46a00c2799170402658448369a64a3455f1e7d246ad094869f9b26c94917340da5ad1c2d11f9c4120472401a466c200c848131d0e6a81d1007ac6d7ff006a4813aa00c94de62b5756981ddd85499ca9a737a0b4dd51aaeb5f9b5566b34a51d7663135346e794d3ade70395a46bba465b9cab307f3eb0f419e1a2a9efcbd1840ccceb1b6d9e9b27146d9129bb7d2d9f8ba3c05bc4daecede2b837ae8d8f28b4b66fbce1ff8d52f5c911037af1c3f111793c1e8f67f42af345a542ba983fbf50633488f9a23a41baa0a94ae3a797f9a2c6481773be4c060539a4a62261b3e9f70769f9b948469cc8722bbbf22ce7e41eb926c7b290fcca37ce4dd05a6b2dcbfe94924469de215315264f2da9922abbc272144076fe917d6c4f79180a2d05a90b2f06cfab9d61fb7bdc18646a2cde13c1d285a2c661bea1f89e8d9e6a744aeaf148461d7a61857931e9855e8ce5281967c0678aedff999dac5c7bd36173c26c5a252868ee452fb69d880dbbdcdafefaa56d1c5573cef79ec83e48608cb387b327052daa6c1d262a2551f5a513d8012f6ca62452e5ec63d455519199b1a953708d735cbd6e605cb3356d266bd24cdb61dbe10747a92280547d5cd6a05746eb7315ced14de274992b99e22efc396e96a3ac8ca3ea388e302c19177ec265415012b4586760e0f7b7b6e27be9a9be5a60106330073007ec01cab63f5803c64021e00bbcd9feb8a5bd968b5481eeb55ab885334887e6403a4edb1f744995e52c7882d20ce1782df76adc73857648d4875a52353f00a19a4d80ed0f9a49d57db986edaf5b52d5bd0701571a0f8044551d9508b50d6b10ef8e20ecaf34558725827db1b641bc3b5a640a680a226cbda30dfbeed8370479adee8598fe5eeb4afb5d096c855e211b475db005b6c056d7b16466685ac83e979ae7679880453344107374f1d466787e96405aa67868098da29389ed439ab0a940021249c4e612ea8c698250ac4ec001388ab3088fb442a2e188fba24cc3f628c5d7e3f1783c9ef7807488e90956398b6ff6bec7b872977aec537cbbc7d9fbb68aac79dec15a6dc6e7e3819171d38c8d4c81ddd0726ad6a5c5e39f13b0183d98a0c6042040d39109c15a6b93499b74e53830f5994c94454cb4ac2d1f305138c4f1b745dcda60c515b7913acb8254f99e5a905d9028ee1d0c45bcf14c7251d00ee3da98a64c267737e5c02688435c066daa9c7d939996390a0771d485c162749dcc8ca5799e8d107a95950f04b1830e329903c309a984484621929111f448c5d9fef539fc16a6357f606ff4e0be7d1a5295278ccf17b7ed694528142b188a501b0d8562c5d9f35dcebedfc115e0797f047842b1f660cf7715a9028944a1587fece9381e8ab5b5e7571c478d84d24d3ac667193da5a15028140a918c4846d762afcb9f87342bacde388c918acac84868042a551999325f7f3ca50a932937a51acb912935370db1a34cb12f76656baccbf618715006d6da0edb0e8f28a1adb5d676b8c3b6d2b89725337eb0aecbb33ccbb3590fef3a52adaec9e2468bf9e6bae55bbe65d1ba6668caaeec6ac67e33df4cd7ddef4a419076cd3137651c2e68d3066b60acabbe7561ecb2ae3caba08cd216f3ed465396873cf9fb68f96181acb6685ddb6f56c64d725bd78b7926a9d3b0d6722ccb4a2648d0628ed518cdb26c03dfd8fee158e59b4c71c11c75e3285c7bec29e22d639f62cf63cdd0b46a5c1facf30f0c5652cacaaa1961ea8d46c5b7869c50092d2029a5297c71e5ac5d5d1900d1b981c28cef1c40372018900bd402d98058201ad2eb4d4bd3c6765b449ebc46f7f971a9067eafc5c54d9f17cf8e9b3e2d3e9cc4f40efdb8e8d86a9e1ccfcc2364fbe705d63638643b98030a0169321aa624bd125e42832f6e02632b3769104c8b3a160371a31c3aa66302f80f8d1365dc44820983fe2d3e687c98013893494f9ac22fdaa21e4faca0453cd293b7e8c94d2615157165f483776dff77398a091cab3d7500a9681c2d731410f40c77585911c1163dd21468d4a31e41a31ef5081af5081af5081a75cd02b66c024667eb180c4c4eb8651330b01cd812b0651339b0d93a4653201807ade615349a02bdf8c91f06cf5004c968100de943038d2d1f195d8416c199147ac880331c0cd9e08ca640e30cc6053361ecd72d992062db1997348e9fb00d75cc0a6ded101913b03dc4489ed0a0950862fd307d68e2089c2d9b38e2658ba02d9b38a26683469ad2b1d878dd6da881c8946feb984c711868119cc54023cb4d3ab6fd59dc9739bb091c086902072d8de3301eaf2d8246180f97ed0f7af1d46491b023e88064b63f4e0b0aabc9529292e5fbbe1a2d8258a01668b43a7608677324c771b6c6952ccd06d9fe336e721a4aa92d8146d00b68f5e98064404338ae4a5a7393f809d9feb9e526ff6ab68357e818fd6e4653208c9ffc65683435db1f082b82c733d08641dc19350ab030c1106ef2b78920d6f61a4f43dec097184d7d3abe13dbfee08ba63edbeb1342db9f0bbf348c15dbfe2db2d69eb98046ae25048da071fbdf1568048d94da640590b13c874d039379394eab078bd5834b0f3122448810f1f719ef2010b2428bceba994143da66cc1edc63205fd02b384b3a4bc69c4583f88baf463741c05133ecc159b6c65195c572560fa4949decf4a724c409e615fe7542807b08bcbdcfb9d6ea53db5280f70c69ae7ad32f5653bd49551199c29e0fbffed425e6131736999028eb8444d50bbbd50a5e49ee6b29f5dcaf45e4c9df138ad6c6f607c32f14ed0dd2b638485bc4d574c32693e775375ef305abe9dcd06c66436278b258ee5f27274e5695617fb2ae7467f98cb7b60a3dcd97540560fbcf1ba9026df79e34defc441f7c69aefb0bb2a13be0274ffde0a718f9c202d2051252850199e2effe201b1abf3859a08c01b8a44eb725c84397b70f41786a06710bc8d4f471236f76d841071d0c60000108608b52365faf29636971b258d22535598316e7eb66be982559fac14da0162dced78ba6b4b61c58019923c3b79c2e66a869ca0253e0388c9807b3b912b3a5b9492a313b739410477973ceefd3b6cf16623199df5c82e078cd777ce639566a97395934855958c47eefbd31a91ac0bed371ec4fd16fb6bfcbdce4b189e3b712e40b0a86c91282e6b88dc3fce531378154cd8fc914ff13c8941791274f39108f499884798c010c30994a25131310c86f32c5e79c99ac193373ec983c262b3613128bc5eeb57d4080e52c936517cf83b6879bbc4649b27472835d48e345e7d0beaceb095a54f141721c37ed5631d1b12bb9841259e0706091b03b90a89b3fdb243a7cbbec791db681ddc42c2cbbe48cafed7268a6450fe6d160de8da3841802ef0fe3386ace84b897e3d91c755f88213c5990dd79433c9dedefdddebbd15e2cf4606ef260f5e69c73d65eb6dfa754df53ebebd2fc0aca98f7fb94e4f6d40f63fcde835f411920f7a117eb0b7f8611fafde590cc4cabc6d5e3d5e2ddec68ad85b12fb6656d2ccbd26090520c7e18e4829edf18b498a9b0fd3314dbb93d658acb9ba3c41c1362c680a0428642aa648a7df114985f784648d5cd01c4375bc12bb39ce39672129926fb9819f30c5edd5618752d8ecfeeb0396ccc17da7126e790aa2efbc83cf28eeec318630c82618d17b07c3f8eded7611896cc0c0d188594d2931e0d8ef3c0cf033f2868faf845292bc29f060b5a40844459cf9b466cff0efb90af63ea75b71b8d9c6d6037ab6ec7002e94d1518ef242b941814171a1b4506c50582834db9f4a9bce2e33db68aaca64d926b3d59bed4fd39a61b9c9e73753282f7e721165a4b945a6c8a4caabacca505e5070c8d415c0f6475949d5a42863898da66e8832a2a08c25b6129491a64aa69027ff92da0eb69750b1bde4454f5e622b7995d8b67fb6855fd0146544b921535986324ad524e5c8349418190a0e2ecc346e022370370d516a504637b564caa88517998d2a146105296264305fa0d9fe55d6e2b2f39889c8937f2ef2dab01bd96543196ddb1f658ca1a0c4f634800b846265ed0c18cc390af2806360165068d1b6b4388a7ba73368ea7bfc5bc6a029903e8e2153fc6dbdb80b2bcf2d8e8c7dc15897bdb133b6c65bad28f53ccff3bc8fe713ce44bb3242a69e9e5ece22e60c66cb7cb1030c606c7f87f1540c7af27c8454759666bb95c9194b95b7e8d431fc69238a7369eeaf947748174490295e937fa821dbd8728f5a6e795e28da18d64ca6715467633c9687dd6173644f685776e59f3d9ee7799ef79ee77f0715138cd5ea7b7ff02b61983b6303bbc9b20f8eae93c964f4e422a329124946921293eebdd7e28fe1244b025b44b63f18854c819f2944aa425331050988fe41037d5c29b67fa80d5abcb25babfbcebaae6dfbcb68f8cae849091d72394afc68bb870d06458033008d90291539c2d1ea854f0c12653fda7c11c474e12f65341ac944a6ee7d91b6cef8235dcb510ffefb9229feb23adb9f4653a00d79f2bf41c361db600cd872c356a7c30346c1a34553a00bc80f1f1b6c812da08d1c03e2006f6c7fff7cc4ee2a41a6f87795205f7435b8b27aabb72b932939ec5eb2cbe8a61619d8025bf28b602b7b1ee7877431bfd5831c21419878b97e90f34dac45f65115d6da0edb0e3fa12597bd0e67afc3d65a6b3bdc615b59321726e7942b2b52d69032255a6a2c2f903f5c44602cf7e593e40ef8e51601b0e59b48182366c10d184d2c38acb172637981080a2c3714f902fc451477e9d96e2ac917df74e199a1aed1b7b6b54cf16d02448b2bd60d724e297138914813894a69b1219086952eb070b8a325c771b564b2cdb49ac0d1aab0267000e1ea740adcda3699696bad90a5001c95c31e02023bc8213256e974f9cf2f535c7c8ee37e659f369d35c42b5893e8ca4b95afc470565656aec85200a2e6585c0e33c8a49932f33571668b9b524a2a31a63080824524d5903a3a7ad0b0bcc002b9028bcd2153345a6ce7483e348f1d5bc61780692a86c4124b2cb1c4f903323267b6fc45f98519235df88b928a08b074a04579a329c9f2012ed4ee772fe808dab728691b007f78fb3274d8f0f35311e68eeef064b51cc551166bb26a85817d31a0bdbeeffbbeeffbbeefde5b83a5cafebd1c9df47b7d546cffef877cc1a4ca6effa688623b90ed47c08f3b99ba3b5a5debb6648affc7054d8323e41de73442a26e205114bc33b58584fdec49cfa9a7cefddb2530f78d814c0143ab9563efbd63eb8577f0f0f1e3b65ead188ec3c0afedafd7bd16d7191a5a12f0adf86241735e87afd7e16beb0a86f5aaa9d99a8d45a3b31a32ca5c97ee76a3e13269d0949438e47d39eabe650d60bbbc3bf6cd21ef0d796d74d6daaeebba4eef68af72474f1ab46c922d9bb8f1b239f00473db97dfef5e9433d1e6ec92ad599b9b721c255a7be3396cb7383ae8305d581fdba525834cad74ef6fc72055df4f61b758b13d0ddb47a9b23599e243a66e8c3cf9db2a6c4d02913f18c00093a9543231b1a3add9cb9229ee1f0002b4010d87f11cc817380642d81a7691297eed0dede8a6900ecdbd68c7eb2ba3af648d9029fe40d094b5b20c56d6aca4d9dab8bbf95d285a22dbdf768ec23888d4680ab4dd1bd7461130b4353bd63e2f478c68717860d7755de7d1c184dafeb59aa3fcbf0c7a24abeca29c6cb3367bbb33b7c6ee5c9dbbba34b7e5be783c1e8fc7e31e924cf1847668d917e425c3beb6ab85db8521b3ed59d8fe570aa99aff8d4b5c2940815fbc4cd8fe72a2ee8bf7092f6c6dba5842cdc7f6bf4a4815e8e7cdb1af1158dbe64c17fee2d5c1f6bf39902acfdf98db83ed1f841a6c5fadac8e547ddedfaa41bed0ef6fd92055e0536bb343e60b39128a60281c32555e4c40a5933823d14844225d6b3d76ac81a188770de44310b2ef33876ff7d34553d6464f39f3c5944d17fe20f88539a56982f962be4c176e6d362341c0f8be8c9729eeeeb1580fd6d2a2b044d7755feebe0cd3f7f37f567f3ca0fe78c0aeebbaeecb5feeb0e75d578f97cd973bbf56cc608cb387b337a3a54ce174e4bff6bdec610cc3ebe6cfd0b46ab010c59c2c99199a3bc45aeb92e737f222e94dd37bac47423645f212c6e845d2cd7692cd768cb5269148fe4526d39431bc081068a43f9eaeeb7467ade5f2f7596bb9ecfdbda49638b7bd80b38c7ea4c4e28711921f7922584225163f845e147a225844254ff225252f123d495432fad149f719e907813ebafb749d1ee98fa71b8d44a2fc41af98f7b4a1fa231e1dc92323a1919191d0883732121a19190959ab1d005898b1d676d87658095aca142aaa98e10edbbae2ad9737ec5c41f306096bb1bcc064b3e30d2d5dd775f9eb3a4a697ea9927d4a2e97aa17c850c540ebd2c0d97a3b1c8bf5e079588a524b8b8f8219628dd9b3a5ebbe8c311dd181025bbbee76b806eb9ee05cb8197372bb84721fe73164cad63a7ac69cb25693d993a3b5da8c5a615a6c781e0d0ffc3cf063424bce5e90fe782c203dcff33cf0033fafcb19a625535c6eeaf162d1d3fc0fd70008b3da9c73561a15df8a2f0f4d5f54a151c45a6ba5945285d6d095cfbee2bef27d0c5f890186351ae00263f13c112cd7ae7870d87e80fd797d3e3db4046584a0d0fe9ed29c69d5ba45398b80694f16141ad3d30b71bbaf3265027a72154893bff759059e3cf91b4153ce4d57c6c90eb410dcfcf64ccd1618e3b37ce1a4f1d34442a6c448172c9912ce19111b74e80c5ab2ec9cd373cfca65b3dd666a76583a2b1adb625faecd26efbdf786bca02f8b76578ea23ba48fe11f983b52755fdc1ad868369aedbecc5849769a21c08b4153f9060e4cf398c7d15665f4e4195f50d2ea4d4653b5489661d9b55d1d9e98221f8bc3deb036bc4f586d37f22855f84939b6ce312f661c9f7befbd1f9badfb60cf0c4d0bd4b1f5a0ae68c19355c10ab24a516bde17b6db5aaca5141c6d8bed3acee67a6df62abe9db5b5fb6ed775f6f36e386ed8f840f0fb56302c999921bc5ef3f57a590b6256a6cd80f8c00823820ebcd4a4ce0c88500e2d7a37589e652be427f21299083909f9959dc84cacd80c84ccca4764236c86414e22ebd8b9883c83ed368affff9fda6cb521b319109966869399eb3b87dc23bb783b99956bf298616eadd66a0d0dda7efee135db903eb6e71e39099a63f28d5a93b2de68cabfe25a23ed195e9917ef0ec42b83eda16877b627142d1bb68767d82eda30ccc008328821554121526c28a2609db04fd4d4578fb0656d6ac5b89329af8877f36ede4db2dc7c70c19b82f86dc95aafb5796b8c4e7c7115446a63d0e2eb51bf380cd0bbbd77f36ed7d2c0340d76a894e25bb91017b4586d76b63fccee48951be48bb985873dec658a4f21d565bc2d339c17c3fe0ee93ace03761e507a421f5a94b4c9d11569c5a53cd3867ce1bd2abb385f529eb01b7da5b9bddc815d873fd0fbbeeffbbceffb62b8c733dfe646056765b5caa0d7c1b06466685a1e1911ace65083549a0aabaf8a535bd2ba575695a93230d3823cd8626555d6b695c5ba17862523a2c1f7a9a87c1fcff7f130418bb6e57bb1c65a741c7555a68aca0cc53ac59e2a9c9bbaaf54631463ad29e9f7178af5f5f17c42cf9755c02f7b1d4d8dab4767b38415c6586299bd12b8fdc3b2ed4fc3185d7811053346204b20515ca692c6c0d72bcdddd9e01c524a4f06f1b4077e1ef8f940cb14eef10356542f1c2051f689ed997effde772b2b2b397b1d9699b181dd546c6382cbb22ccbcec482581a987d591cdbb23d6af5baea752349680d4c22ba0a49c55ea1a95a8bc06c6cf73aafab36cbaa36cbaa357f1000f101ac8954516bb5406501828579d81d74cee979618d17b0746f5996d5b93c31dc2dde96196e9458cd9c8e14c673a810bfd117afb98e7599d5686ada665235009a548de4dc6c1c2df4d9cfcf60d02d5fe37db7732f77e76ddeba699bb7a9739b3b6e722aa47bfa62a8c55162e865f6f2bd64f1eef319f07ca10bbe07bf074316ddeb2782c505e0b3d880e7f5fff079d0775df84326c2c74fc987169acaef1f9eb9e0f3d9f7997a3e1b69c8253486c4b9136ad969b1b1981c2823cfcea31db5d3e6288e4a4bd5e0332466b2998ecbcd3ecc86388c5c9b6d7f9ff9ecda1c5a5d223ad7448882c57294e3180e8269b00c7e611cdcc23d3a87d1144c3b4c13d1443411ad710efe21537e6d21d2143a8aed3020241f211dfdd5e60c7e61163d2d018b0e5be2c6997027fc099932396cbe8080c9612687e98b869c9b068962834459215322982e3c766d1a2453bec35bda5b5a7b2be7ca68ea16b141db376a36cc721866d19407b35a8eea5ec707ffd8fe380766e119ec03f3c03b30ebda885c1bbe816ddc221ead3d1e0499e23f43aa401aa9ca41902f3a5c6b0266d5302b48be89e5d061f8fbbc09023d88006632ddc462399885591f4f09ecf3910c388ee3569c32409efc7fc47c65f3608b7265ced20e32c5e5c95f04806c02488e8333c42b4fd02b56d0b456cecef24ca5c94a7394af64ea901cdb6261aaadeed44aa3e14aa3511a0aa2456fc751a5ec8354e1f79f43ac1b2a4d5669b24a932976c7dbc92ddecd4d54ae98d0920a4d7945b20deaddbc5ba5895256ab70ce7267d89eacd6644aad82bb218c9a6cb1d25a69d8b3e9388aab55d06ad6462d52896cff4af36e5e11ef9671e41bd986fdbe5c6995866f5ec1b03288c3169b31d9cd5d82e0ccdadc99b39993dd290542ce28e63e206633a942e5708673feb9d7543235a70b04774ce716854c01414f6eaba236a7a0992e204c1a4a9cd30ef1e986ed530ddb5768196242be36e8ee73d6ef1f022036c0010f48d4fdc8b0fd573e61499766d0d49c6940a22610d901899a3833b6b232f3198bc9a275a8640cf807b9756846000000b315000020100c870322a160405416a5b50714000d6e804a64543e1b88a35192c3380a43c618c40800c6000018209129ad0244c28338cd58698393effe591d967f640e121aae91d78a86dff55ef5974638076546ae99bd1de72826466c5cf3b2cd858240eac77e99ba23f809f9c4c37253eb1e3f5255291c22459d5e2790fce19786127a04dc2f1e398387747ca9ad0c333f4cdbc863cd62c4af09729acdbe5c9684997e6085dd5293103431a97d8d36596b48775cbfbd0024ef2b990fe6127608fc7a47a6a2e7958a9886aba0a93a961f26f834a935a00473a074bc8b4cf0bf575547b6544f03973abb172a6a6b7763bc59bb30d5679c3a5c12fa3a9b71706b1c3db9f8ecd247d666dd08b66f1b9ef45118b83126304cca9c4b8a5afea51cfad57265eae6f7b98e34383bf384cfc1f111bc7994b7d830635cbdeccaaadcd4bf2f57b0e8e8644579387634568c0810e7282c001c82b1f8939161ff7be9fb0e779ff089ed9b69ed0879fcf023a0d42b1e73ee98c2ae3c4840c34a297f53d63b4512e69ff1110d2e972b68ac53af758f919b46b697afffa9805615a11db1dac4c195995d090142167851ce8ae134ff5d9992289cfe048b47675fdcda41f4a53478ada3723bb67ed024232ac7b75e44515820e54826583ea898729a42b01b8de9180787d5661765491ce73e9441156391dc63ae4964393e1809eb137f28adc5be5c5e66ed36092c4480fe1db1160d2bffc7f87527263ce7c5983d545aa02d5031f5bca6c2b7724438225c9ff51ce76f0a15dc290044f64ca2fa65530630ced4374be44eadb5c23e25562302365d532ea814915281d9f4951b3259d58b627a6d8b7c01619b6c9e064af0b49321dc3eea364f9bd862430be7a998c933b541be230e5590b3ade4d51603b2cfc262d04cbc4fb6913109e7695de1691923416ed03252f1f4d461cb0f2fa5b1d3ed99691beeb66e2f4d483d4d06ed69f62a38afb3cd287a3e4dfaf3746b54ee09ffcc9f6fa855075bad0b3dec92193ba55a47052543e25322adce374ff108e0d38581091222f94384712d551be4a81660256550c62f04be7ce4b60f94b06821f946769e71c7114370ddc28fa463cc70f9ac14d8621abdee8b779760200d14b16dfd84e2e132d4baec5f1b6e73ebfe74b3e0e76e3e089e68b1ecd62a56765758c1b9199924fd8c60a415123a6defe7d67d7de7df257b6d5edfd06d2b3b61e28f7a0d1e476ae2a43df1a63f87148776e71469b42bd3e8d98813ec8b37f93a2e3d6f9f45411f2cd17962dec2794384132cf3164439213b4ad9841b85e084256ff6f4ecf184a26242ad6a3283e85b89c1c4c70e9a7398d982515e8ec9a71a8011ac2edd33a57a3f784f1cb29c6f429f4e5edac8e1daf9ee6f5c26949629b5801d7c0013340bcf9de7785ed153eeb40996a8789d21bb89306b31d7169af6e62c95f237a1ac5734aae697d054314d0c72bd330c5c7d05282f03d059d0be5d6d1cb4b15aba4b9fb3c5205db3cb378baf02817da588c3e1e25c71519ea7e8e6d07b82979666faf3f22ba57e027a64247e1fcd84f91a1d53c5a659e35babbc4e4cff8670e7563e33c1478ab0457b56e501d7c3992ab6e386533bf1bc0e59260a5c764b36d29ecbe7b29515ccd99ad75b74113cc7ed72110fd1f46098a9d416701443a06103847d4668c060fe13a94097b36874bde38114b655f8b3dafc7880f2ace606136441b5d7483693bcf83c7a6e72e30f9bdc9cf543aac886b8588d3d1bff84899c74c56c30b6e4c34fb6b89543feb1f28e3cc038e401cf72f8792889a36c98f53b3b10cf64567d477f4bf23cfad51090aba5e6ac1ce6965faa5e925f18a3a62895d01bc3a7c1522c9651a1920e88aa87ba0ca88acca4f280cf3e9ade16202a412d52b2bee06207f7244b64aee6edb78f5f63904d355b82fb3e430cc752335db88ff5d7962ab6ad9eb14682f37193e16154df2107eb8877281fc36dfe68a9ecd99c067c9f61af26790edad1aad164966a13a448b44b7de97830ba068eb1e831315fc438a2f07d03ea402706657cbf2071f551feb65fa2f05b906540feac2102dca6a89153fa522d947b77a9616b23478adaa059eb54bb3660a191564b3d4bfa214d716a3c257a2b9499061efcdff070549cd6cf1e94802d5c91b70ecaf38587c2f124bb7ffe0a2263339e17714260ff3894808a3360fb52870c43ea82e311678f8e912dc8d5091e13cca87484fa46b2ac40d009c18ee91b8422cb08041d316b6e4c708e3e0aa1576d4d28102175654ef3f00698f508981986ee5a74c0cc43df95e0779407035793e875133065e52324c435636481077680b0d98ff94573ff8c9197194beeb052c355588ea0c08a8eb0787a87ab5ea4c7e5cb975f501c2116dc559f09160e9bfe488c75dad4770d9e72f09e82ea6c118c0612a11e92c30931a877d2192e9c8962b5a868e397dceb4de90d73974f5b442bc3465b0944556ffe2761e6e6dfc44540e56421fedda3e3ca6b3a2f0c9abb3f361878cdfd6e16dcc265bd0f08b2967f755f902c0e5566e1c3ea43626a25d9bab8d94c553db44aff4130c0b860800a402781e127a1cb5ae11526c9b6a437ea045d254a3471c0140975d02503cbc888e94415ec5a8001ba078acf7f22b5287d4e01728cddd500a3ac7412d1d51587ed7f477c6b60bf9a3fe0f0ece99646b74359976bd9f0593a0c0e8680e6275fc45c51c198459228742627b2fd6d37a0225bd945f6a486a753ba29bcdd2b29f9aa4a07108a021dd43d41d1c04b14b144f042b733022d460c4a2ff53d46b1815fc1f82a2bf8d129a64337da57aa06262f35f3c2fdf6246b636ef53170c32569541c892a6d8207b05d6372bd453eaa9ac508dd64d6dab0b9ed584e244582be809c58bf11b77c1b3aa628828c821a9b22851273c92817e130061dc8c75af7a03901f766417632cecd091f54476c7a9d36fcbe6b7ef0f78a589b7036f36d1f1520624bc226662162cf34a3bb070b42861693d08b36d440941f002811cbb5bf5a1d1ed1b9788612a78e5784288a56108cc1d47af444ecf219166b17f221b8436c60e0346eb0ebeaf0cbbea7f3683c229e86d8937b4332b6021584bcc4bbe81547d53226cc7350665570059b1d646ac75c96875b7b892f3bdb82f85efa3b3e4452230c58979adb6b46a5cebd9d4cdb785b79a8e36e3d8df6df6bfc3554ab23a64bd9a9f145ae7d8dce35643bd4d6cb790087571de77930fd84667af185c7d21be08951b49f32b9e9497624ae7b1ee30e564a796b2a8d3719baff61f0a925c26703113e3d040b9789b0afd3699f8f8886959c8fe933fa58db0584d5cd6e5c609da723ab8ef60fc3c206caba6cc39dd4439b6b1a0d502846a470607c1c594098f8f3cd22a25397d92ed29e2dbd350915e4715656995697cf00dbd76d8c4d8d2d53eac08c794231cba59db7b557eb5c2f55b72fa4b7af60555ff8747befe42015deb612288bb1f2ddbe52fd421c52da26ecd7acb11c7cfcc2e17b5be18d9905584b6a5220e0820a1b24e1b655a988bf1a48233b4473d906b2446b3ef110413f52dcf5b902283e51fc178b47a87c0a36527492dac0d221844bb366349df1e4abc8909bf891784db3d688f16b5f87c17adf666723c2211b2c262876fa191232374dde5a13ef26ee7d6698fb75796d8f1e548d172c2ec7340434e278651beef17b511985f28daef089cd11affc449d2f3aa54e04b6cec53305c4b18eecd2f34788aeb6eb0f202fe2b6b7eb3b8df236a6f73b4a778f2871606bbe58152e204be1f1e7f5579034fb933a6d2e1380d2ee5f533e13452f4a8375ffa985573219c95646ebe4fd2fdd7a51801aebd790c696249466d83c2a1137a0798ab300708ec013f4844b4e00890bf5dd54f7e1c4a21216d08a3fc9bc564adae431127a056c7564cae523c752608ddc1e7ca1a4a000322b37e84a82a7679f3de268832ef50af19344fa5917cd64e53739083883954adf65c29af9eeb5294b80264ae8c4866ae3cdeaa62eea4292699aca48e7fc8ca277c96de99662bbba36f572ce2e496b67db7854e14bae3fee71856f8a2fd0034d26692a46891726d8206bc7bafa14e6a91e19403b0043f82d998651d24725f24a71a3206cbdea3e7d92dba00bdc5a10901194417c91958ee376d78ed693e9720f557d26fc12d820618ac1b1d5cb90b05743c1c7cb196d9d58c31013d1d499ab078dbb0c4e1993bb382be4dd300277c090250f2b38d0e65e0f3fd26608d55b51034088401b985282b095e3e6564f4cd31a013d5a1968a46b8d11f28251550926a8ca76c4a52c7420027c9b6a5c9a93484d2e6ac6c9d742f20ef3d74d39bdb1c00be9c4e26818049be35536065ba241c445685505d1c862c8e27f447f8881ced525a46da43365674ee30837396a36de4e79f23134c13544d3c3aef586c34bfd937c0e4986b2557cd164243b547e86f4483f17b895aea7f3c117cef6f536ac7ff4d374116cbdf9f9d1674b54afb297d623d8a8c5e9a67c62e10a3770d0acbfdd71239e78b6b3503c021c34ef4945ef1845fb14da7ef3a82402a6f9c26652165a7422070252d6b420e8969afc491f452bd19ffd2f13cbb5593c227aac6f47aa7c87f6effa5acd18873cff974474c60bf3483463fd847def0cff8404669c03e2c3d7ff53d41a23f6f45a66246414101a9211a4e8cca0c10599ee0ec37a5dd0730c6c6f9b2c406d1a1928b623e88d100f67d13ca3ec84165ad2b3652612e0a799b31f82de248530e35c6cbba0534d191a3e3ae82ac47a82765e48412741b14824e5b7811c6c658d550710a0e2bbc3061121fdbb34d619c6f87729d704035c4942381fb4c380a856cd14a601a7fa948f9b5a54f33c61c015536844bcc25431004ac7b3d0761ef207810150fb8e5ceff88f7a94e4651484e22b6905ca842f05e292f868981840412519918283b304423c788cb8dc27d7bf30128e08f8051409fa8507e974463c134b9ff85661484be44455187ea632d1a72a02ae6672daa93523a90c491deb57cbf806b8c5a4df806ca64d64b34831a9bd214d873a3c6ac009fd31e85ff062087db1bca9867fe752baa2d79757d31a5a77126f5d34ffd95208668241b721887b1fc622a2f0bc90645f7a50c9b22e3368cf662ce8ae18343f18c0574ff3133203da6ffe60896ea1733e6156c5d9500f7bfd7102d3162adf9b8361a129adc4ca037ad045e446748032cfb791c6d097f33599da2faecf09bc3b723b95fe02a7e19724d72f78e929ecbd300db8c1603ad6a0ee143a8f454838697825c9877d430b850a2681e783af50a435aec06893ba96441facdc388adb43351408eb19d0a51b128d8fdca4cc1cd61de3e00800fdcd09b746c1fcb0765134c4d4aa8f77f890e0cae327c5e43ab1baa80a3eced5e75cf3a60055925fff78aef7503897f57248bce38a3473b00b81ea4615dbf2ef1d8961e748c1460b1876885e23fef95040fd42b301a089630758257bb5c5bdddf5da4a2b68fd280c5dd17d26734807d95e35f4a508447a83811c149bd2aa6e8418889875d94360f133d8bdee58bcf77af15d543baf15576c524d387f798b64e1b5205cd9d0bb26c0bc3301be9c4c2e44d7fbac4ae3e42bba41c5f729e1d5e388fcef2501a66ac8878dd21360316d94c9843688c96cad499ea16c6560424cdabe3f082b2d6e0171597a2e3cdafc245776894054d644092b0f667c1d3e24f8aca00dce8b4c7257260d6dd3c49ec99ebc1bf15354913c5e8ad29591ae6d3c886f1a4b7e026274c501166f7f51b276f6e72c49ecb467e5dc366fa722039de2272c6f3b1f054573455496a8e78f294171d24bc42f0232e0ba47c0930a36e5b6a2f33f2c2b91e551e70ff04d457d5e7e3776c1f425e7cf4c01c2e79afe0660bbce7d156b6a4d342012d599404ab1fd1b5d38c9f1518385854b81ccd3435461273936df3500be3590a775eb1731344e21d90d0ffecd194342e2e92a36689941d30b8d938912b319e0e389c9c671e8d1c89d3e5004e6172455363f01a747a2112a26ed80612d8fcefe509a4d3eadfeb64554eb1608ddcfd5104a8df38d8ea8737d20d76505e5fd4fca923f5bb8f48dea5033cbdfa68503dd495b29541550e619d62187c2b00e51e26aca06e1c7cc8ffa6293c574caa4cf733c9a0b8f305f41feb3c238d3fae4713499401fc6a4bfd91ca41dc33878fc4b2819d70dcb4b4b44ec4580417ec8ddf724f9e8dfdea65dc89065794ca4f2f60c249f354b77bce96867451bd8b443c40e65527f4625108879313fb57540ed04cba855f8e682c439c527d416183dda2de0d223ac16508a29d58f228e936ba2390a358bf0c8431ec6228f70133614b042db7a980524bb32b44938a614d6c1f25df281f429c865d806c856fd86219e3337169801f438ec8604353485cc84dc291b6945b3b52ccb4c2ba5d86be9c03c1bd7f8c705b4bcd7ca36ba8d9fb8e7221e7f4187fc63fcd4cc33c95f9959e70359642280ff53101df41234cc5989e24638e392fc521eec0b531fb3cc1ac4689619bf19aa4e5a74e9b1d4d2917b31c10cfad644a615a57cf1f90be158f5ef1cc1498a6441f00b7b35d82dd73e112411e55d680ce0672bd29afc7d37898326c95e5837c9e471ee073852296fca2bef97c0d7d473ee4b0edfc2483a6e00ca733795f43008f373612f5ac12d4cd7e07e8782c1b03a993ef8e4e7e41f574f49ff87d9768b4cf7a7c7fb372136c416022b3ccaec0bb081d4bc8a37309a1c5911a85193e0e4b2f37e4e84f8bd8f5f3ef827e9aa8e3e7fac167f5cc5bfb878d2858f281a81feb45f42b5fa1ea2d4abea40391359ea0871abcc398d6419b7df1690fbf3973f9ec617b626ddcaf8233fd41480fb2a00b77962d50063b82a0081dcd2599a56b1e84312344d814793547f86f149fcd88e9558004275ae22d58a8086fbc4b22bd05bec06a6ac31683e5d0635e76312159bdf1fa50961b89cb21fdad53494671acc962bde8232e923828863fbb738fc7eb815755483a5418dbe6b0453d1baed1b4f788b196b378525710be67d63d9a199c3499d864e2edc47205335d179f79d3a2e2de4cec4bf3b935e95dc441f12dec531d3965207838e1a31ef2e2375dc677712ea969777e0cd13646c1eb0b4b27b9be7314f0ca44848b70ffa591a86103d024e2a265c91f52ab91996ca8eeb976343505defa53de920751ac8a7860033609046d6474eedfa9b28544ccde783642a2e83b462debd4852716be32e51200ce98eb450aea0033d1ce0e4b2ff404df2629e9b939618be46c5044db3be85525754591c47b3450f2dea4e8585cd20c11b2aa67a56e1501dc70efd75c3f2f474ff730783405aceb94d528f8ad99d8a264956d29eff53cdd7ad67d3b88ae9357d0231974bfa0b49fb11db8b27d3ca5ee5b1309aea1ba953973a75824bdfba1caf0fe9988a692ab0de77203190deb68198205c5b567259869e8fa45c5966b77083b38a8cee5aff48b3beb2d7d77023e5492a25e82b6744351521ed076ed549efb04856e4a390caa29ccf2b054a0c92301f77e2d646ddcad76ab2c60195712f9f4ac0173ccfad8050545d2218f4a4c0fd9d9c9197f7dc6c53a2311c58db49af573785c8a31ba5670afde1507f62421e2ec50854a920541e695b65158877303c23b0992dd06d6eabfedf15b020096f99f94caff0cf158fff9dfca9b494fa64a2691585aaa9730fda13da4fff2aa58442c54bb123d33ce641c89bc5e497df5176eb6f008f4ce8e16d3866ad837675fdbb82e28df03880af389f80671379af10e747e66f257be3fbcb86929d1f763eac21d5198f621e93cb06c3d0fa3bf6bc7da5e332f111ea320431fa7cd05dbdec79eb41d3d6d0f593381080ce203bb92dc08d75b4ec13930070b615ffadaec779d6f215454fafbb35c2f2bd61be9a71bf4bbc1edf1491008ac68bc6366313a44afab823f62cfc4c211fdc7e3b4cc559b17eb6f5c6aebca388c1e540d961034cf67bab3f32a8b241cb12c45b021b54f9c93ea6b6b702afc1d5479eb05472a1cf85281ae2f32a5f5664061eff8e0104b70295db75c02197d0e84c298f7de4659f7770908ae3684f9f1c267ddeae453aed25475ceb621fe9f3539350d6494f8365b4a3077fe09153126bafedfd2d3222ab0e4f20039323028f0b83a78470d77ea3f8c4efc8ab6cbe316fda3148a29772958b2ad786065d2c5bbb51e6e3ba48b885b36f9ec9480671b108783d5fa6708c0bcf45028b49052767cc9cac53f61eb335cddd23ab7dbd2923b5487044c530deca9d420d7631ef360cd2fbf495bf105517efc01fd6a3f756312a50c080672eb16f781a8b0cad685674038e6fea90a42203e66849d17e60f2e69b5c50ce0aa26fc0efd00ed519106bbb19760b1d9a821a461f41f15933f25f8dc4ff9ef48870bd78c15320aff6f5b6f6419537b183a7534a396ca7b75bf014cf9e9ff932d279fccc8f10cde89a89440cd425239f06972c8da4548430a330110be5164d386268cb98cf928fc7747c63af9a9287888d4c6b210d914d6f051cccf3567ecb613d4c364576f8984d9dc92327e2863addc59ebc0e96074830ec5f5f57cde3b8491eba89ad30f98c5ac97ae76ad9f04c14d56a87f8aa6c60c0f8306e655402025188948d25b4aee5793ed5f6f8b179b6ab5c5837cd7188a69a1704a6866cff105eae9f5d5ce77d21f2c58dabca4440f374fc9d1e94f2bb204afee2eba09a8c5bd4ea8f506139132accdf768090c00a8eb7ac3a984a92ae6cebc65d6796744240031312a4e4034a695b876af2d142bfa410ce6f6554cc7e65364d4e2224c64bbc3b78ce672add79546625efa5c1c4d2d8de6e3164d533c215e66b26262fd7465322745c12825ffbdc79c0e3303e72baf2b1365cc47cc9ef946fdb0bd3e199a390346daf01658ebbd3da2fd1e7c18dfd73ad2a20eaee2b6cf5f28502ecc90ee06dd2cd2a405fb71e9185eeaa6adabbf3681bc3958912920762ed18e5f1bfcd9c007895b4a09e1895d57e01109e54a6050170419da530b9b54aec5908dc3f61ce8e275babf0a06b83a5d6baf0bca2fd74343b5921ea61bcf4527781ca4024d3806d46160eba7cc51a8440e1b1d65aec3d0525ac9c7adb651932a14f00e5ee7af2a0b4f1e01988a4db1f3d3ecc78d0ff6a2d0bee6c79907cc5d466183a64701fa5bec105038116b85d756df849cfdaa4f4a54fee70caab6c0fdb70129a113297a8e01502ec25ee7ea23c592b8d5fb3dc9e4327276b9700496a64d9230dc5844a5ab1eb4f6838b496a5185648a25ba7c243006214096906254e72a4707827ba253b1dcd783f04f5f60fc583c82e02235ab84ebc19e55dbf2de7d8c1d555332a26c13e297233496fb353d0cc3c98ca6fd5b46857d227e2e150dd5b61771e4dfcb14afd43073fa2a009dda0d68eb21ec2b2082f4fa061ea6a83673a34788840cc6ac7f5589d29c8d7c9eb14a3d4aaa54056f85051c2fb7c40aa48c028e2594580a175a93b37f55ae8d55b28a90b0a791c6f103b34cf704e293da6a6377e10975bcbf433e8860eff5a2b730ba9bdf23f3286a5296ed119b8cb2b837bc9de2455857a895eed2e4e12481c2456232b09026b2324f089b04434ea2a1ea278f75ffdb970353c8b35ac8e4311032c50229f74105526f88d00892e633d2ce3fac4f8fd27f0f69ede5b9dc7458ae678c9b8ce647421aa09d88703bebc31ca836402c96b8ead9c24429140851f739f99b572a0bc5a0177f91f4106b7e6dbd85125955cf2dd1600e76a798c3743fdcae1970059e8ec742478495981d961002b8108d230509d847acd0742e03253873f4345b0861b943bd86749a194620ac86ffed42ba5d978eda8705a810c003b4c7bb9a80418c11ae3b9bff96028aa1898cdb8738998dffa450d69f49aee02ea70bca3e7a6be8b77b48ab8c2523ca88d991a14a728337d12bd39468bc1890d765003e705d41bbb260594f5d960005fff9b2c5128e7c7922a52bf569a66573d2b6d228a955e52f09c279af68327137aff33b88f13ea02faf77a1c0eff65d4c612fde828c65d941b066e3844b8ed5bf270d5312e33da911f4c8a1cbb35bf30e19a6f1c5b6af4b0a2645529d0aebc336217fa6fc1b8c9a7a651d44de67fe2f3b377f4b5e036f0bf13e64104375680ba7bb10fa75185d89c6a4522baaad700314d98f8bc3425c235dee57f9d475fca2681218b7477c8601bee1af0c1c47da8342aa03ae7a80fa463c8d9a472f36e7253231e88b0c4396cf6bc3ca1f07836fffb9a786ea3409f23a12cf1804d5e0673883c89c37ddd2f678090c7e9f16300011a9724a0c49da9e5787f0e3d8ac44940d6178237d96e678b15b3503e2bf781e1eccac302a740181d577acb427263fada9e8584048a57c667f380a890de2d6740c0b9095cbc9f6b1b68f9176f460042c7fd1351f6e5aa8c224632345cd2015f85d7b3378ae5c9ad7c6a86eacdcc3fc6a7add483ddd089fa7d836bc503191947a6ded76aa231403bd0a8a57ce7ec838e0bec635d01584c6756e94d78b346b51ace557c51455e93c16a0284b279dd2a19b26432ad92d15a652a8084fdef7a32d0de0b896499059850ef42bdc0e8b4719b9626853440912555a447021dee440e4dcea3028a2963d9ae4fc1aef74d04e0368850933717662bad4496e3ff58a715503a036214b5312a0bfbb45c974c954d679fbb68109068ddfcc74d3fc044e895f098c930d786caddfd4c3fba14573705c0dc9f7c2870a82c675c6d068499a4e9441311dbf15abf32fbb560c9cbacf1defdd0c38353719c01d4f65d12f804a72968067fb468349c9351eb358d87625b5ba7098fd3ad530187abd1ec60c064a9b8b54b051d0ac169e212a9a724e7e266a549ac6d80a4c148c756748291275117e49f8fd0551216bc6496b52020c708515012877b6e024a57a9f8945ebe8f3b50caffab79e2c7148d194f8fbba48803d0be0400a0ff604c75828b6951a729b2a6bee7057c68286648014aa705ba6fa3e8ce6a3c9e742383dc556b9404db31221a9494809e35b68e3c0ad67e55f00eba32ee2c2ca3668714406a0b8e3be11b51163e2c7b0d26b68f48fb89fb0b9f413a49f72bf9e07353559ee24921a20790a217bda8e1cba6eb4a884c532efb8f2adf1d9ddad6a01d8ba09628405adafd4fcfa897e342b8dd74b78e839712b39627be40aad215bbbce44d534456eff61db09435f41874b27d14ae0ac466776c6b7e91332a3a2c544aee55f982b337cbe68cab922a032d34143f0aeb56b96c20729049cbd27d2fca1528be365ea0e377f35b785327944a963a6ef9cdec86f32ff74dddca333564b6cc9c4099ec416b1d05780aebb0529a3b0dd531c5e7e94c302989f7ff11e465098307d3bda1d53f8018a74da8353a2d83fb9b70cca531fe38d9d24af474e0f9485ae83178d58854a008ed4534b2b2e82f136fd03c04eb71516ccd79529b1570344104ef557b55991ba656df93bc6db82d5556554d1a6865a327557fd551177a8ba6233657a8db1b841ac492fa269981a7f303d1dfd14635cfdd459e3eadf60a6c0c180e35829a89115aaca833b6540f855cbab605cc6b604acd599c810ce7fc821f8689c88957fab375409f2065c0d6488f21e5bf65641a627164f3f2e9d49741874c7af04ea7f2cf81b27d488fc2c6f2f5ae659245dc9a177d7b83072fe0c5c4606631c520675d92494a58b079458322666d81b2fb453afdb01852442d0740ce5a29f96a9e2a62d8b7263e7f3425e4ec5d2cf37832988127c11695d0b23b298f68df50e94f2676249796623c44ae3935b5ac8e9b5dd1f9b8c51787c40821ecf05f04afa545fe512149f2485c4414ae9191178fc94c5546becf82a14cc43637470c5b691be8006149a688b5ba19823bed4dbd178d61b1320fc9e02828eb2245b0a041278a930065aabd575de1610277d706ce573f68f292542d09a2d8050405064bb9da819e95d78b3e79cb584728192ccb274637104b7385baab6b6127763b0362fd51eccfcdc10b2a32c5369043f7323dba0c8c896158b8d749cd5b5eb8bb2c7be77c50471c790e256630b49945f0b339de9cdad1849c0b6a3988f7985dc5d534505895cd922f01592b4a94b0959bf5f6b418c48ae6f7df9c15ebd1e774dddbc62149568d5b8b4f3b9fa46282971c5c2da559c23ff1aa8fb5561c7cceaf97aa92a4140c6808a152347f0ff3cf9b7ecdee0d66bfb1327386431ea081e971d19c2d69af95b268cf3e4c180e058a1263fb8c1b646cb972892e007fa177b4ac95ca1576a2cf6f171a8a2e4d26d8de43090e5c302d8b82797466040b923be3e25224c52d9d798f0180293bf1e84fb0cb539e032967bd47b0da82e4661d0acbb25ce019717d43931ece3340765556e1c42b6c66f4b7af47fd018e38240074778943d43c113eab231615556531c886d658609408cb979ecf3ce5d3efed53bc598c4fa5307706e6be2affcd3c31cbc68eee1ae547ab75602606ac748d56107ff5fc629a4d5bc1ff8d6cad0386406e06d1c2f5519ff43dc47abebd2c2c6ba86b2ec26d3c2bb0fad90c81c3664e661cca8d89687b79ec05495e6db6f7c211244a246e0590270cbe019d141568f8fae227f2898fb6569cffd72e296a276dd26b8069fcc8c0c13b6f9a7b585158f9204e48d24faee2c84fd889bd2924445768b299402a0dd7a7d77a6c9bf6c8ffa3891b484ca950e8abec849b59cbbe05c7eebf48e0eb037d1b29c58a501ae5b6357dfe00aae2252419c2833881749f5b872d9350d461a4cca256efa63d362e9e83fada688532f0f46f7eaa42500bf6b6f0e44dd137d5e7df349bcbe3d9212c156bcc806f0901baab86f19405bac358bcb256c32d2c6857ab8bbe8223c3eab0b8cb2972250e0a36ed1f4dea4fd7a4acefd17c97518bbd18ef09670bf6d37ccea654391791640b4eddd0096722b621c966b916b0b9264042c1e0eedeff0815266f1f19bee3a808c463de1155b811d6457a1acf5b616f358141e68a5d1b004651a644b291481049ebbe66e3fcc9662d38cb622777e5a9ff9d08c5da97c096927bf7ded065af300cfcf9a65e2af3f181273ae682d72c4cbfe9bab093dc00f3c237055149148e79474f9d78a26a862742686d59c91955ce726f391a649acaa1f040cac2b73ccb6651e1461a28a2954b358903c463cf407ea97790c4bbb9c85dde276b54ebb9992985f0f1ca0dac08096e313bf765834ce993c94cb3303b3811d9ee758d9e948022f2a5ba8f9135acd2e8d81642af337b0c2cdc8a2d248aca10b22dec90a9a1db79cf033bf67092ceee8864d1f25dbf569cba550ee0b2670bf64d4c8d32231cf815f727daeefdbc5dd64587b47dbc6c51a3c517119c8e3043fd59b56fc2226b751afc6e0135a74ed227e8db7a60189c7c8120872484ab6e281d25d90c227d2cbc4d077a2d55aa6077d8a530c0fc9f8fef413469833ce002931a06e31a39c993825ddbad8e27956b423da208e9790913971d0ac1463bd890a3cd79e21655a638b6c2716abee84784d4262c5420eb92847a5d691451a6f0d615fedfeffd03d91bdb482181867f08842b347f8c59b061a7a8828bab157c8ed2d5069301484a9bb0e4b597707ae582f45e539c5291233fbf5b6af038f9004f677df96776b0ac4e3b49e8532b9e381696b147e0505c92c14710e2c9c7b8c05e03ad2fc5602a149c33b81bdef9cc9ed1ffbaa8e82fb356f4879c444cb8a7a1543cc8c1c21ac6f408f7f1e6f48b8c716970c21abe4c0ea9eb6d37c6538896b13a4789469c0e111bf20cb29aae64b8477bd61084133894dfa0ad979bc0f90d639ee8798c74e104758d4c2271e9b08b7503847a3d7846e6d34c0d7c8bc7fd3ca407bda5db87a70830ddbb7d8bef66119c63f046cc25818a040a2a2ffff49e7323ff33ca52f1d7aeba158e49108e105bc921acad37ae235ca2104f2651d9a67d8b81650b6b456c2112e99ab38c70a0b2cb703e311342888ce21aa97ba02591b0ce81c81f733e70883fafa236f41776260264e88d4ec72259832a73ec36b6e412b183465d4db618af418e2ce93fdc3d36f0a62758f4657332434f58f4e229b319a00e5fec7dad46e71e75d601ca4efddb659a7e3c5738e730906fa4369c30fff61050a06a3899c9c1b64d8d045cc7883955c46d3562a95dad410028db5ebe7dc739b7354614d97eb104e437bbde623addc071fbf8879cd689a73723277e1134eb46577084dc56e6887bc67e191dcc639ba1c8b6de9d7f5a08d17c4287067482933e75316648817190a7ae730b1bb8541fd6639e9a2e708cb54e87d78f3426fa9cd98f7109c1f8708950129572e4993a72bba79388174b2ece1275640a33e892a6e182d8e77be3daff03dc476d334542a820ee550327171d4c3f369e26403caf98c103a549b4225128d304c5f1d2c7b773c5ef1bd2eac4b1cbebbd0b5e6f97f49b154e61aaf575f84ef63219cd668f0dc18be688272421f6881859559db4db3e83938f11c8fb321051393d17dbd0b7b4e6bf31c0f9223c0bb154404a5dc4f010a1fbe35ab3b925b5c303ea11ff9aee8db6f550baf0efece811f9a9bba530f601e409037af2a380bd6e30ca8d581d3cb7df5b02c7309fddd6a113cbf0699351fc616a3c3144aecab2f6551e5aeb18f4d008c271f23cd27c8afd919adda98824892806c6a714792191d083c121e23cfee917fc2b4dd84e50bb8541b076dd3d6415d05b7c31ff85b841ea6e31536b5fefec6a36d9315185a1a03b8a0eca798d08b5df6ba9aca37502e4b1e87768a284aa56120065669f7f7c0867d4027ed9c87b66e8afd8183eb71a669caa103d369b77bc9989de669a8e0b54516677f8f255d3f817589628e28ce21edae0a25f311b11e41da0dca98d0755546943416e12ed992214ba291ccae3df36e011db25404ec37f45cee071d7c9643d14a176379b1fe31d1b9accab10ec0c555b9c45943c44e95925050b7b77e571c54f96dd89aed7fbfa1e6ee958d6502fd9d820e8ef960d4cad74716cac785c1e7b45780845c065052c45589475b81084426919104757ffad3a2392d086127e171c5ecbac7c3e348ea1bb9d108a310a7c83e2df7d4ae421d66504574be33ebd26c06e4a34ff41ff4de48e58cf824497d6e824417ca3613eb60fc3a8b4fa5a0b2ab3a1517686dc166ebbcfb9ab461ff03aa2ec960687a084a48e75cae9a6044f28ea070bfcb61f8249106737c147fc4efe5442f0f9c80452468b60bfbf04be263bc8311c6e350774cc5ca4c88d981d18d4064fc09b17aaa03b57b7731ec7dd6a5052584eda59ea59e6fc2573119ea04bd3a94d16fbeb1fc924a573e432ff84a96cc80c3ad0aad3dd6a53c2f8e8f34322ac2eb090566216f641432075b5df230329a8c4769b216a69001867f5931897abf78d19fbe8b8849c17b3645faf5761ffe3b841a5ab23ee002c04965423e8c2bf28d60202f014099910560f7135c3202bdb1fa091ba7729c3a3189fb20a5908142dafcc2c92cf5782b3fb10600d1da11f3071489119bbfcf9b262b8003da840efa69712f6fa9a2921ef2532854a244de86e869297e59cd1778da67dfc601b0e7524753f4274a15b70b5376abf96a27e68703a7a39133b035e8cc212134fa1177979f815a9b0329bdc5ac548aefc28cc3bd2ede93edbf6147e00111d6cbabce781c5f486d207c20032fc0273e54fbc2d553fa29a2f4d05e63a8e31232808ed18ba086ad02a501f6b4901747fff09abf5bb74cdb34dff7571294d8f2d147d22f656773bb696e9611e1b6fe808171111bb609165a699b33f2fe86145637b5fbad367864bb338eb77deb1f6b94a19b58e71480502c8281705d69b4601a2d9d61841d9b3b610297fbf292ef21c5ae0ed6c90bb1b6b8bfd60aac495c308ad2d04f4cd29e41a764ada98237381bce5c17fd2da22bc84d31ea861ff49204d50adc4bb70136bec5ba4b89de0f4db03c6d6168bec9008447f492ad0c6e271f6f8a84de624e1862c9a398184ef826be4337d93a163622f0a94bc96215c7db2d07c247dcbd6bdf0f7044a0becfbf856bf253c476019f4f4b4f4090f5796c5172ee209ddc624e5e2af5c1fc4cff4c837e4cad6449b6c270377760a0df4d742be9a45cf02a094b923a2876b7bf0c725e0fa9bc2cdfb504206a14d6d5574558a8625bb29791f7ecf4796051a0fa441129efcd60c8d203030282df1eba4f4eb2de2c36bf48b1670ca2208a846ad16ecb4448cdf01de9dccda13891204dfe495926405793113640fd5e6dd2def4febb6d14c6d7e33ad05c19543bd494651b0186707c18c43b7398bb5fece8bdfb9278c7f0b8a95799d8f60c83246fb85fb7ee869ca5e2db62ef4d59025fd14aea387612629dfe9001a827d135d82b18583a3912d81644d54323fca75985a3809f63fa2732c3b90ac59a5be61bcb08f78cc2c3a1a7ff06c6cc568d29bebcd13036c2652832ecfacacec434cee8c26e76a20fc3820b2cbb90124e11ff2c4e7b2cce2b0189bd522716d7f6ee31948c276951587eb4712de0835bd97eacc0b86523b7c4632a5704f167133327f74fd7e8e5f8740b5f7f478bbbf650358a03ef9ce1d02fba6f440a68796710d0e123e3e13705d2951d4f57ebb5329597a10b877bc189e6101de972cbcf4c1017b94860a5a3d56dd88eb454c31292dd75663550ae664d4ed083700a89db1f32b79f2ebad722edf937338d39b42d6894e72be82c3b093423202fd7a43c9057401567799a4b3583ee5650db3854acce8795d33aed8012ab67739badaf0627be9e02e9fb8f1c3421adb400649b585e3b91a5b423ec5ca44350d34c9b6c2182bda976eef6ca7661ed12fd1992b84cedea470360e61e1e2f506a5944346698e9517c77f29435955e995cb3064905db10d40ce867d44ee7cd302e3bc1b282107cb5c8c9e5c7a5cd9e248f3a2ae03ec16fb03c2d33cd3d0c4a4910f1485f351098f523e6335ae03e59d089d90386000fa2775e78c12040af3c3d20a8fe8ac5f455ed3d06a56df8693af848852ff08a2c1789359cd1419f4040b2bb5e623166629896f02f9dd8c3649e41e4bd04e9317fbb054817039a56d9b90a47d0da571b06074cbfe1ae3c004318a5d06e696b48d3816e4621196fb1382529c955b98bfb5e113df452055a502803bf43423d60bdabdb7982d4f6f370f203d28d468f47b0aeefa0ca4ee82a6f75038821012c413473291dfedd7f6289b2c957dc484d4bae147a873649c8a1043a85170396b64ab83ffcdda733da82ae9e6af4ee3c7e737ce61e3ff181c14912cf655fe8003483991433522a8c03f91a59c04f591497dcc4cc26710a60e99331d56857e9e2765f0a6f31b372bc779a7d6fd0a6170e3e99c06b5003103d45317402ede425bbcda89409dd40641e00ab7930e4e2840e3bb0ed051c17a94139e2c70a9a5fbb8cd018b740302161c4cb58bef807918f20310cf8fae73750a05b52a41c6ccabcd2219e69ea65855591e2d5c534b090c8ba8c49dc89b0dcd7e8970d65c3340fc278d957bd9cf99b6b86059259dfc05a5528a7185dbcbf8231f1ff02346a782301f8d970664394ff08c6f42bc45fc0c399f52e1f6a3cc3de404210b155c2e3b204ce73cdcd63dc3c8a1eb19100a9c331b965cc1efd56b5ea05a33245eba2bc826ca86abe0afad578f6a6f15991362fc4910b5b537ca8cd58ba92af8970c9d0c209f0e923acbffd7145e6fab3ed7b145ac73328637dcd8a921b583b89ddca72245d53ca863e830264c2ca7b8cd2de64aeb0049d9886b9bf5aa0591b2577ce24b2df26179a6ad9997725c94399f1638f8a77af85715bf1ab2391b19c1b2d4f1fb3881534a4c866275a9f02797b55394572c02b3e6f0c700bc9387aaf8ac47bcbcc6968cf919546d0562e15ded546558dc209950e5c4170914792b564eb9fdacdea8f2c210becb476e365d5b2aaa3465d9a395618ca4634f8f15a133ba78ba544942405f798c11995e9bc8e876766619d663d1789709f0eb8a9024f932fa77b57ee5a0cbc3820b7b0a2be4a5bda709c7b10f90683fad7a13b06d468a129dd2d06cf408d89f7add213cf3ec052f5bdcc992f1e00d5bf33feb59bdd834216e757c17a9d7d6879936b8744616a3b2c4db6aba54f69758131e472acb7c3c46dcceecf00d56caff800668c17633db7df8b1d4afdbac049bf3dade79f750e76acdad179bce4089b25d9f281edb4a64b99b77ca1ea7e7c4f367c8dd09141b77b6ab81d3ea7d8dac810907310196cb34d3d1758c8f666600f9511bfd08dc3537cc6151b141177e2ab3b254e1ed8f247b685c650d7e1d646735184d4b610a47f96e574fd8a761e74adfc28a15af52763b74160da84c3aa2b422c4dce4128fa378115ac662144eca1088394e164de93f75552ec11cb23077b3954a937243ec1552535ccae9f88b6c3a3d7f544f953d4d550e3be8252ef036952855469b2a0b1edf644b49222dc4222e1088d05bfcf20bcdb9a40825d889371a0432a01d96b2f89d358b1214a7aa0c9988aa7faa5b9312dfd24e38d751a4646a5282f197149820d3552f0bd3c30706dad2b073dcd738e2ead9525303986b775e5cb4adc434eac19b72b1789e07ddf241e5ec94de3bd5011db7700b7b765937835bdddc368470a09a15f6e8f77a12b278389fd16f58ba73f1b08fbe5222ab4a70b17b2dcd0fddffcd67886f92e06dfd62ca902e7fd6119f0e77b3ac3449052a1f6dc690084a3fcebe25b77af99cadd9de6c03695c36a6d5ace641faa2b26016e0ea3ed305aec20b0cca1e022588a41ea3f0c5969a3674d7dae4d70960ecc701322e643542ba35057535372b5fb3463e0ba229702b55d88c6c5a052eb62bee9a1b1f6af2ac30b049720411d0a4b6ca5fa09825e58fd66654bc28bd3bc7ab3303030acd3ad7e230b34ccf149a24f2bbf409896b84d38cc276025d827897c8eb50cde2a07f02bedde995f81f857bfac4e439f2fa0657cad1dbe4a4af17f24364f8cf9cb8cfe664f2242cb3f2648763fae3e62eed2ed0256f0bd2070ccecce8c2117b1988b0a54a9bb4d86568858b14b990cc768cc0f4b86479285abd7b0cb4f3152fb417751bd2a77ee3eca1d55892b0051e064a67755adbf5f5f7e2cd88de063619c8c3d7a16566a85560e380aeb15e20437b331710de08b25aef74f2e3802ce2c1fb23642b8e3730a98fa46d58c19f35cf07dd67e1b1c10a289407a7c11ca036bb65813eca27e6f89884ea0da62f409564ebe8a6b5c5b3f7c6c0320b962b9d5a8e0d4ddc56efb979202ac792c763183614b9ace2793d0eed25bcee0e927e93cb89956d9ec9f4d544e958893a523ae8f4b747c5945b91186f36e6dfa89ae7f806b515ae4c5ae3fad55bb21a601236d126701b8715bfe0635e23a399d4d194011a65897b8f8422a099514e13bec7364b958ba06c8b15abe9e250ce0b21433756890dba156908343d033fa136ee190a163b436cc9703c691b11927bc1749b3344c4acd1adaf7b423b5482eef64a5bcc97675a92d90bd891de87853e773e295827c273c06e012d0a80d97d71b752e5b170b09be51a370d5db5d89c4938ce1d8fff1c979a4bc037d0d42b86c0c6b305a7649b43b562ed66f166d767850a088293fd804df47a735e514a369260313f7ee00235ff8ea394a91be96c09d72900412d10c0f7e6595814c81623a3d0e76a76023879ec3cd8d468a0298409250f6b066a02927e2146f7a77ec8f39c06f45cc0a7f3f99a61176c93c4e84dd2e8d58367e8157db114631e39a4eeb7891ee23011fa987bca823c6a01b7d937878af909c6ff5139512d09fe42d3c21d1e51949688692101ee4df866a8829b881c846027c536fe4c060345222243d0820451fb6e6a2984bd2f6fa0de083b66824e0a8985c40f9091266b617bab6e07c27a09a1c4147396c9676d0bd21500c8635b200f18d8c1b1e88c1d383ae6901c671af3bef937a3a00b05a89a1502db5bc4c1cf1cea17b9f5b64387f9a8a3ae8a78a99f0da4cbee79434c00789977e6da599acadae7cb8ddc50d3aae8a55f98bab748f1ea23d7f5d64425fb30d30585c7e40216092ad92b205c396337b8669d3025b2a85c06a7365718a01604c74cdbb9dc56f9c5526051840027b1743c33b38bed5a427cde5fb8e898c1f7929d12b9efc1d341298ab35d998197ff4654e6ea7de01d5e5146c9e74ff113c776d5939b2981d4a6080780abd31cabcffd0dd93e0e17737e6f58276d0188a7a3ef075d1789a3ae08b9bc1d10751ff19e5baa9c3b11d580538f99b87507e4087b12ad5def4133dec4b3ee85e9e61a2a4f55b0dd87195077a284ec617101470b887ec825b51d060391c64521304150021220d0208059acea101fad2dda188abf18205e46375d7561d2384ad239049dd25dff4332382366df1620198f1acf61ae7e91cf01655350a0ae27808bc5f9a70d4e025cee8a1a84b8c5c52f685dc61cceaae889b9eadcab3ed984c3ff3885d58e675aa2aa89b390a415f28d80aec6383f8e3baa397a450be55c8b183544216e870a4c0b66c417e346dbf99fa3877c8ae71e3d58bb8cfcfb90af97e11b1a1f831524c1bc32b474e6c6ee748fb1bddcc4f9e1ab0c354c21cf1adfbb0a75044850b545bbef942e8f5180ae0c17588daf09f9c54aa9fa55ab2b8600055117ec8741444e08afca9a541d19152de4fc1b2c3f64b40b3118724152861dfcbb71683402b388f360bb935a860a021630514fd95fba47588ef6f3c3c8711fe8968362193c5f2a75e76c1d63fdf625d2fb3624e00b67ddf9a5184d9501393fbf22f8528d359326e89fd6079a0935fe2937a7266d9952cc426e3cb3961be42b36208682f84f34235166c39e50c82c476f65425437b88c4a1171e9f47764de28e9dd84745d4fc75d5eda811434aaf36e926c3aee5679d58af09d8fd78314a9310671330f2c1ea52b9f2df761e6438d61ada02ecad1ada455c4973c57ec04060e93e4083dd04002d09c53cb3fb9de55ff360479a8434cfbbc26427654a3e57e9e85f6b6dd1fcfb50fbbf95edd6f670706ce18097221b0048975b3463790a2c07eff27b24188c8da125d3a1931559950a5d63c14647dcee043a4f475c94e36d45e60d851166d4e15ee64f13508dd6bec62eefb62da78e5dfb78408cb7076f3546a0d7056a0f2a268755cf35dc3481d8e66555a4708803e7692977d3e19cf41db6edff485afcc23c7046401a802f437fe5fd8ef53616c651ccbfcac29b0ef8b64328f3537201e882a5bdeff5ffc8a2340a421956dd5030f6cc3114e299fe789cadd85a56baff185f9581e106e3353da30bc4a273f1b4c58e066b453b9777525e815e154cc5d3711cb22aa90d79ab4ceb4fc10c71cb723459040e1117e87971bbb4bc0b2057911cad8abe8889b68e1e9e93e1835fc2fd89a343cf05a26aafbefdea52f143958c9ca929a491db3d5a161c73a4f76a128fe98752b93c0855e94b61eba3aea94b2a9d25445601bcd8a83cbaf6fc913233aafd7bb80d94d00c1958c48018b66a85303f4d83bb17dce5a0c3f86b5be390d77dc621b136638b8566397485333f568f49bf29f851a67814b079ccd26bab565969aa6b2c742541406d308e2eed0580660705ed53232541a08f1824c31880caa88a8a2221b0a9ecc05fd52b1bcac05ecfe157b7ec9db1fbaf67096c66d47b4ac121b2f63c0245aef55cadc8a5811c04f5a9ad0e3ea98d9c8e87f2e6e91335b7643b4e04fe3b7d80d0df83fb6fadcf6387df9b3d5e5acadfcdfadba8ee0ad229f257f8126df2a0e60e38f47897ee6e8b55d0bae76cd91288df14ae1b877981c58908ba3dbbe77a963fc9ca629ba81c8f45524f7d597e09aece8f5bf4016be766576cdf50665c9b1af74abe246d38a7d2182476dfc7972ab50b06296f0015113fd35c3aca7fd9565e5e76bcebdaf1921bf1b8e3ddf3534a61ab46386ecf72d3b0816637ac010206e4d92843d0bf52c8725afa7ae3047b7e42a91424e25b9d52db7a3d4cd73e841949eaed6abe886da14601c12ca63763b385fc15e14b0361897cab831578e73be2ad9f6da43323f211cbdfefe4ef33cb4bd69737811c9fdf7136864de70be029683f4b8ad62d0426c2fc69b932e83227a8dd477bfe063d42820972e4a565576f3324d2be1119670458124e88a07b19df4e9ed04130f81ecf7593bffa4c5c7d00325c8bda710cb554b4081040a2ee8817ffa94f93f89e9a3511a48d654a9abc5c7d7bd60fa9b7bf16e6add11618abca062c29d8c3edc21af10c607ada659b4ea3e1a3361c220c4b8fe2b1b382877bde62db14c09fa17962dcf2593b444c704271cc7c18c46e4f332b382ffc11ee370d9c2cc07ac32ce765a3bb9b7e5ec281a25e7d6508ca4259255502570f262f95c0c9cc83c6b63a3967f89a3fa0b8dcc72e4d90c0f3216a712633f5ad3b0ab47376db5acc5e2ba612fb0925fb0ea7835ba257414538e64f67ecc4e113d8d9f4f3922ead2d7bef1e69642f801fd6cf8464463d6628a4c537be9985e8eb9d47ac8d38fa9e15f779e69f60be8ccbed97bf0778a6c6648adf087cd62f93a7b792999b573ef4622c97d33d2185c86d70ea7913156725e99a2f29d8c445cd34b468c60fa768a4f5cc0cb93d9cbee54cb976753b895b8db3be400f2c74360ad857a0225a86fe7780f5a4a531647f1b14840b63229a8d557ec51ad88754dc2db3a56de18b6cc7c2a8b8a30868b4e12bad67dd3ff36a68a6a73a72057244b670b73793548abbcee02c6f680a975692fb96bfde7b5097ec57f7a55fb45feb7f73afd6598b2bde1c97f3f0764edb4d559309bece42facbde9ae8f3e1ce7810bf87f6f42980de8f8ee240216f626ff06917cb6346091f18ee6e65e58055bf22eb481882262c5aeb8aa558d4ac014f2d798b7f4942d1d80b628394fa02b9aa8b4a8823206b0b96eaa1fbf648e4a854ea8261e0d13fd88cdbb8447517178163d4eb3a878e03639f76b64d7d0c91007528285d99234fbe01909c309716357a8c4ecb7dbd7abb3062e48404204572fbdb4b1605762c77b3d91549361138d9ae0104e82ed8f18fb7d11bcf5e65561a52d6be161e52d66e4126af90b8b8e4afa5b9a88f7ee7330f661f2fbaf6a9c8cd6356cd9d23909e358f95961d6b1e4df8dea0fba2c2fa1cda38132af5649be79d626cda65fc3659ae665b188983c60314a222348e4958e552e6840052b0a50c9aa1c3739ba269f5ed94c4384af43a1cbe1d6010996238a0bd50714fcd6c5d05c165184755a52d002b6862f8eda088b2eb4cebdd5ec9c36a53efa12af3c955cb6baea91730ac368d66090644962666a292dd3eabfa62b504343253cd30858b8753267f17a734bfbcfcea7055e9b443d92860cfc29d6339b048fceb6b10eaddc3e0c752b38d5f91cf49f04166e375de07d267c17625ccc2f84042843a8719972eb3017eeabd7a897ae1f0bd1b4324c5f32ea9956bed80cc5199be5ded8a1c45ca7b43de5b53b96370b087f0162b3af12dab1681049107ef6a617d85c215e46af2db2da3e1bbf9e576ed48be4642088b617ba9b78475536f5e874df1a23b789d18b99ab715ff2ac6f12f4dca6fd8c74c604bfc9de35db14865452e2b2693b85acdb2f6e27712b63210cccc4af89500a19543a64bd90e2559d2823d4b1c7753563c9c3e83b71245e105d736655f8adb0f722462a28c323544215d2f14e6d86ebe362fb3cd7699bd4c67cccdf2f38d10448e90e47297227a0a7cc0d7f5020a5f4a345236f248e290cbab77548a7e812d967d8c5d0750315ed2abd84b88ebbcec9654d70b48a35e5773e075ff689fd58d973cc143772e68aa4b472daffc0d178a49492fd49c5f2f4726f18ac49d9c8c0d8a0da908c28d3a356c659a6ac3c5f5e119f244b9dacfb86430418114484a4136a6dff2a8dc0cde3e0871b531bbc647c9fddfd3824832f34d18ec5c888d9ac49ea5eb3295a1e45063c18205230e47013ed8bbc7f0ddb7794ded10b8ad5d88ad0eab61cfeeb854b2c25a4b8862457fded58de78c0873d8cc6ccc09494c346d3670454eed8a68fe36e15d6118a48a3b7a9ddc95cac4ba7c94f4006c52d01c07e6bdd28f75f5daf085158f3b7b634136821e545e1e17e0b8952a68b6277696d7739c279b5937def6d71f43221c3b542348af3f753bd0e68d1f9f9143663aec33ed12ebe42833cbf1c9302d958b0e5335410555cd2b2993bba6a14265fabba40bef42e911209bef4737559ecf56ad21ee61c465903e921b38ae60b407b111cf448487f710b1eb8f5cfca670fa29716db1ccd05a30f2c50dde13a0dcbcbf212f58815c6e4e2b80308abd1c6c1f2725d555140df8a7c55f29a6f213d76f1bb8fcfdafab9eff2feaaec1aceac9cd4147ff20b701aeab54686af40b3822abd2b5c3ceb9a6e0752a1947198ad1da43f9842d8283c46833c7cd6b65668b39648a36b2640a8512e232f3dc81646119c22c6cd4bfbac4f340e62acb10ff0778e7aca57c030d70cf6fbe6d7ca04bcb16c813a4e7fc082e7a548f99092049d2e90602dca3d32a9e8dd1c5f4494c362290b0e0fd5c8e566c2b716917619d95c2c8457906d451a1a9964acab25bee4928749ceab103ad46f135a6d252344f132dded693eb002348745f84b05ec19e83d339a2099dcac302eae487d0248075e53acf6a1dcc30b4520c3060d96feb7e5a5499011935e7a3347db9bd075003c93258830df2ba3d3573025c45e5b5157827096af78aa5054c2c686c80f2dfc5b93a3097d674eb407271687aedd15357af786204c0c887892044691124afac8ef70eeb96a083b32106318ca189c42d3d28afaaa832be768eea68efc1c547066cf581b015d6f22d9c782178881d54c7085825acb6b6a73bfe8d5fc0746f89ea494bb802a0908a8fd0792120ef80ff636a42e8ba0a46816508d4e9dd339128c185a248a425c9e3c4c15026e6dcdc837f089ced4a0960b3cd87344b0e77d0704ce7b09f70fb640676374e5052820040ecf08fe498b96c09b01cbe9078580adfdc29bd09eb87a6b1b022480d75c5ac6370f99c9197f9e325f010d82e080415ad4f2f5cf1b952adb2680594df56615c355ad333a5e00fa3355869370526dd75122aa57a2c861f7abea783b35a078336b640e821e0164c12879e96286bb7ea6f97e53e4913d15ad67ef6631d9d3320569206753bdb20d276f8351cdd07ad5973c298216be156f188f381186b8402a545ee9a435699d309a3369024657f73109a146c4affc0774652ec384e775859478d751d375f1fca058dc8a61f427956a92c0a8fc159c85c7c0bd81c65557051b6142957f5860e36a084b0dfdbbe6dad6197df55ad55cc13bd581cec6f0dd9ef4daf735db05883ed7a399e914140fa41f2b1b8492203016d2ae281f1d97a510a5d46f618d5ecef9dd42e2953d3e3cd3199c1459adb2f214166c418e98b71a83c928ec0a7300a39efe5f9a9bb46a780da8d8a561aa7030ac449a6ff05f18d92c8da063d771760ca7776b0a15b2da2b31841fa9a6d35fb9a5fdc7e725bf9e2cca55d133632bb8d660e8e0748ee67245015a1843bf8cf711f3a52cb194189c4eb6ce760659f3e1c996e0119d3a9a30b74076cc19ba9afa13650d100f766da004752f2a9b76afa2b6e2e6e0ee23cad84bf7cac57ec1eb3a84db84b8d185d3cbd76779a85b7a4249082845a86f345966ee52d6814d91ddb8daa1fae5ba1b457854faaaa349868c097903f8a7f4efea3bdd66600e47031f17c1c7a868cbd20bc0ad9698f4f39165b148e3206eb445afdd6ad09c52eca39acb4e18671483402090d9eb4efd50041e74136ace3660f40a2e6feadfc62a10becc8c29c52af402386a10d5a0e06da445e698d0c4ba557d1225855b9cdf120b5164bc00937eff52b07116adf097e95b4b21cdbf426219870bb16a422cbec04395b0fe68360495745765699d7329661e852c6dad342589cd54d0eb807f6a450f2c31dc6f2c8a081552317dbfa0e32a8e24d0d3b0c1c0d158be389d7ac7cd51d365648fce65cb3f69487871da128288ea794eab5d87aeb3db4610c242114b5532ea2b15abbd63aa1d4db36a2fccd8dee5679f705aeccd89da0dc953421c85ae9231cc69d51f317b0d9c4c56055d6904e64cf1ad1c7164cbe5933c32916914b0188e2ffa0b8934e3d89758b15ce977bfe2572db97bee8b446d6e8fd45960235f267b8eb83e3f0ce68579908bf48f8e0b2ea86f1f49f9e8bbd6795e617042dd301f663ddb3b68c505e49606250b4adbc1a68a0a18391a940164cbc28102b539472bcf1102831df4689dee5809981d911a307e848ca58d07d599b15d03fd4d37d2275e2bb0dcb2373d5ea8a817a24b58a93f1e99161933a24bd96a9b2a9caa7d554bcd9a7899c4af38a03eae509f55bfc4cdaaa7bc1425c6bcf19d7f6700e491146c6a0a12270afcd43d5ff02f56ef09274a37a7eaf9ed00256802d8d62d7e2b6964c6c3dfe5bf3999c111b3f7a460a5a808b93ed6d656061250344647eb05d76b40e0758e2c40cdafdec86a9ef8c8aae2c510b7cf44eb9ba0e5850507ac9b8e924d29c65d0d7b97f1092f026d095da01604bfe27d2aaddcada7e36cf16943225f5f0ec35085c5d9a9502dd8b5e76e356dae14208c895889595ddb9a46030ca60c69a612194ec31a966e5421dfedecc228fe79971e7a787f9342dd28207b88c6137c03ecd3c2fbcea50cef43519bdc9ffa8fc4d79e4a839630234480710d87b50100d22597416e4e4828a18102b9f65b8caf13452ddce41c508bf13399ceb70c870229784935190df1b34163aebde26f0fd51bd9710676fb9f92f238a55d8a982bea674d2713961e1ee86672226febb68f0e4f1ead2fef3f5c236732dbec6aeeeeafd9e1e04490cb8e9c66617d388d52550fd95a24368a35b7cf254d65b9126c1cbd4ace8dd14476717b30459c636904f632bafbc70fb61e1af774be88223b13664cafbf07092d8ff0c8f6422338c6bb4db7402bf5d0aae9b7539d6065c7c7e86789f072148936432d187315080e0a3a6cccc5265792e793c188246ffc24ff23928b5c2036676220d1e426dfc1a0d2ea0d380e8497a34fd3afb6388377dcd33035ed4890ffa8a5b650514779a2a6f3b5938cd6c38d84b36b49d6922ee696882b520298b78b21e526af06921691613dafc203375511cff347bb7add3ac84d36a8842572a41ec41c8c25241fcc870e288e504ded9ab969c4643525f0e96d8b5dd7d7d8d637ab8c046927ce4a1e1eca7525fbd0ec52cd2aad96400e8e6b979ce53d184615e0d6f4d41faa4115b11e6bb35dc01b3df3e765a6c6d2d11e0ee25e34d2947b15e23325494cd86e2cc6349b781b3e870b928dc5988c041b0528f10de04f8bed58303bed17b2ce25953807486cdcc54e691defe45337408b45080b3e1c1fff00e749926989326a1868fdcefb64f7c34385a545b1e77cf06b4a199e71dc35919a7640b2c32c1b2c35c96bee60977e02c3e141a27340731369018e5f1bb10c9836f2317f92ff410c48201c195e9cb13fd4070e6ec56ea3bfd86600fc5b3e4b2c23a513e4c0955b78c6432bca7e8ec8d8518b9bc52f71cedca7a3001c3cada8895e61a2be2aad5098106a3b20da0d246ac47034e1a7980f862f28bddd21d5d572f45adba2a798cffd7ca5b0320bfd32af6d130f17a8d1b14b762ec95b38213de2c5efc088058b17de610bb4a7fc9c9003ad1a156110e7fb174f1d64b41779fa55141ebf245b4597f58ca6dc187baf035f05b1c2fefcf7469d09f6ed94010005d8bc01bb7e5188c1d7527fdfc3f4fbeb1577cfd7b305b7e70a8d9e68820b9378492b5d54cffb949acdf37d261af60f1d2604c175f99b6158d2bba594632a49fb3be159e45c5db318235a793b364bb8b5497b77a4e159fd9c6acaf9c33ce472f22dbedf0ffc83c059680abaf77ca1475012d621c58cde5c78fc0948d54f3217c9cf79ae8b57070e8ffd6c4bd69d912403a6fcf046a90c4a33ddf959b7ef88154171bb3e354a37badfdb88aa1a35d5cb31e2d0f7fb6d257533b631841de10ccb074381f354745fcc2c12579356bf16fd73de21c15467ae4781e4099c0aa2616d75ffd202271c42ee9a975cc8f4a3c9d0c912af7ee9e42f33ac1621b137a136cd12a3a2e71eaba276d9a07a0f3525bc505cc0a63edfff263f9c49c729bb6e9e82304f30e86c9ebbbb00995bd49547135f49991a721aae77bfbfdff834506673ff28367fccfe4278c8a50f7b19b9a2d1952c35baba20cc523da5270296230c27d1fbdd0a5beb1e77ab0c689d55f82763c844f937aeea3e336bba5d48d428b293bc48a1c41bb013c1116e17bf82396f040d05e418921e48c5abd2caf5ec2e471050bda1d0c14b656902b16038a7a680a7267be8aca8d2ca45fd17e5b14ff2284e4900dd770b8d404cc6464a4d15b22724f835ba6af02512b51341f31a25785f2c0587b8d4d5550ac7dc55d4ec3c15d5fa0fe360effb262cf1b487c5d1f96a523f837e4473ad309939b335fae54de8e979c7b1169151b3a830f21e14b3a7aa44171b6be3c1ebf9160fc1e8db3ee9857e5f20b610d6bec5a5df3efb642bbbe02199ea1f432075d856621f0ca1f8a613401279963cd46a5a7968e44e2b42b78e881603b938013270b789ad74d44a277e3b36dbc340336c6f581512a41cfd1196d5b1b896fa6cb27ea30b819d076d7c6f934c67d94d67be6357eaa55020b728d52752650b7a809f2dec77d4ca1223bc60bb5172e34ab566d1a03679aca4b8ea3039877a3e03eeb5eccbab4164e6d4d00cb04cb90c1f66d03b916358d297fcf6153c6be26a75852bda5372487f86775a5b159020110f53273a4a0ff5a0abd2dc8e122699292846f6e965fc5028302029a56da8f24c03aa473f84706b48485aab74d9394aaaa4b865d374fa159cb4ac22a4e57a757d0d023cfa001394c54cdfb8ea80144ea60e999abd097407484ed201799e1a00048cf6ca18fd534ec02239c4d58223b30c46398cb9b8a2a64716ac034fd626da882a15b8dffac7cffff0c22415762297a8d858308e85af1bb57cda415c56839ca3d1abc0a69d39bd8d1cda230f8381454392592005e47c0bc0afe278290c7fedca1ae7c68e139096bf69cf5d70bd3f392ab21676e2206793f7a700075cdb023ad961615c309a15a903692cd16a9a53e663bc734f48f3ca8d11207b23f26d75ae4c6df1d7a841cb2524025bc40a375bab50bb66f503306d1556c1180319dc94eb0086c963c3c347120e149137fdf04dc7f3aae03f9009215bcabda59472cb2465f205e505190622cc0bc691378934d4c2ef9d869df03b0d31d50acb891fe1770ab4b69984d2c5e8a2c6c510d3e5bb0b1b4c19efa2e5a245e322cc0be665edbb68c5608456851fa2221415b0a3153e0f548c4115be025430af7abb3ef3302f9817c6dd65b56aa613b41a701cc7cd6c68fd3f7f4eeb6447beab90fe6cad075c9c331b1be0662d362a6601320a88c14cd898183066f4339bd9e86ac69c9716eebddbddb67befbd35df2e667b3b21d36525ec6e6cc06b17bd9b9bd0e2f0e68eb76ddb70def0dd2cbef6a3dbb518bfd0247793d39a1ce54af34457ab6eadddbc1bb6de7aeb15110e0801b08470203c540fae86c0c52cc65095487984bfade0b873386b20d7fe2b2e7763ce4b35d35a4f3d799fa8ae83baee2f84939be0ffb66dff1dc8eacf1e79f0614ce033bc21f194394d1bd7dc5a3c310d2d113eb8e176608db8e974c7b9ce8c2b5d5d876feebaaeebba10a80b81621c642fabb67c8b8c02134c998b4365e08e236f1cb5e5a90c8d1aabe6180e0e1a79d66a791be17bf78cd1a8e1a819735e3a5747416b9df96577185e374a2765d164dbb66d1305e139fc3e5fd3a5095c7471137e7771633d1c6ceaf6999f7423a70bab05c3d533f3bc1b394fcff33c8f14041e7b85df41ddebe21def48a59ab9dd64fbf5c0ac9dee98b39c22d7759057ba40e64479fd47e3e8f5abaaa70eea2ca17855ac9b20421a376acbd730e39b9944f0f9a0ee37d070baa468cb276159429f4b646fdf0081bc0dba8890d3054475c971dce4266c868722c10bbac7f8d3e676497f1078f0fb8b30bf28124df07b0d776fa18a0915a1c3ebbbf4a18e2272d8ddd301ea2112ed39843aa8bf47a21f0e5edf5d07efd321f481be98928b31610c8dfded140906776f9e9956109eb02e44eb9c909890f356b79a73ced9f3c1454ba33d09926dd654534a6f06612ee38ed339630de2b4d619a439ad3507ca39e70cea405cc660ab66b401f1600da1dafa0e2c24b0b81a0efe4760ee5a8edbbacc5d8bafc55de6b85a0d07eb0a56148a7dfb6ddbc46eedddc27479234aec2479dd8b7772c6e1b5d6ffe79cf37fc17c1a846f83f0291576883151bf02ba053a45f8148a1b5cd3e53b4a11f07dfc899e66d41612ef33e49efc0f55c2c3c1ccaaa14f1c71efdbbe11e7dd77edbebce415ec149633e73846bf7da6661e3e0086b99368df6eff230fd28fc21e8c3d486306854d1a0f28d8410425a051156c0de8992edf6119c36ac61c8e84176edbb67960b67ce758b2f0daaab5d735c30c3332c8c4ec80e3329c0fdaedde568cb6e31acdfcffffff0fc28c728bd9c8c5e805128c69dac497d0e64bbf7deecfe2b8cb040db661b19ac703c991bdf7569231c63c36e79cf26ddb38876d9f799ab1334f606d814d14385128a31edcdbe104dcb00ddbb0d208d8444d315152a0329b0a2b09490fc84226be192204777a09dfe02161bf89bd49044548de24052ae37bf5f214d3e5b19210cf1ebc992eb22176c257827595e4bc48d886398002331e3025304ddb84e527c20763407cb361229e37e10ddbb00de3b81c122d94c4b6fd7fedb694811bf5abb17d312f327e80d9f27df432655bdf425b9a610d1ada12388e76cb53ca6c696756f6834f2190461802122413db9c9903d1b9dca948c28b91cbe8af68b81255f89df3f09ac8072ff94ff8bdd23949387c8acb1d8af639c3ea2595bb0823e4332a64e14cb178c2c54e27fcde9576d7a5aed7127f4d975ce2482040e44784df397f42070af3c4b99119a322fcae4b34454b4176cfa5554eed4f751f529a2bf1558994035f9168e77ad737efbe910ce9973f3e721fbf79d17a51f326dcc15858582cf78514f0fb75f7d67a2fb8523e08e34a660fca587acc53a864594d140db18458422c567091cb42b237cd2220a147f89d3fa132b77fe73ccf654cf80f95d1bd60f5a43f0e648e303e86607c8471d9187e5f29df44f9ec4d3c5c5955f2a68d86b9b4a56ae98619d69c59569f6259d51397c95632162e6391d5d39c93cb26977d5ca76110eecbd1432af71e5461b59f38825599acd631a42f46ce4355331b3758351fe8808a86369f703666cc4449c2c5397bfba4033728555d41b13085f0ab98560863ea69ca30a3fdcc66369ab0332b53029b917b0ebaf70e045faadd99a7395b6ef7b04d52b9dced2786c25d22e5e0edee799ea87d42da74e1f1a0cb2b7dcac26c2ce59548baef2f876da2051ea91eaa155245d8ee434ad3bd696cefd35af6820a38852d79e91e32898fcd7a70aff66ab3b48f2cb8b68f4d436568f8283a25949c077fee9566b146b1fad9322681a924a4284566cb77232d255076c2ef281b85dc377bb557b5529658f621bf480a22545996b396e9d9ea4d532be1d1b5ed4397fa27fc9e2f0ebdc11ee114df7befb513e5652638ad4e9400c295b0aca7ddbfc5ea684da3791e7c2dc3fd8e32cd7ab0f56279bdeba4db439dc4bdf68abf4bee0eea3ae81efa7474bd881cba4febef8ede773ff1de84af5da4ec4df335cf9fe62bb1304a65b9eb5233d13251bbb2ce91e571a270ce44d123a6cc6e221b65b2fd438474c9ee17447751bb6449e6e9aa56b6876597b4ac4f1e5244e552b9542e996bd43df3a459b5e5b7cc7de2de31ea19ab9e74cf5759987565acf2399337d91d3db27ada33273e4bb4cc3544bb260a2b61a408def7de7bbb6ebf6c071e004f25a6914f9441eb9cb7ed48aafc2b32d833ec6e9fd4aeaab6d46eabf6432a10fb846b65423002c9915ceeee8fd9f21d481632e4b6fad6fd454393cd564f4395f0d0dafba025fdc93cc4bf11094659f423754e2610efe37980ba48af5069ba64faf000fac40ff34bec634d7bb711eb47517018a1380ac1880a6125e1b9e34e4665dcc7b94cc6d58c39b48d85ccb251f9c85bcd39e7bcbdbcbd553d6d6f454949f0176d8686ddbf036f630393034ee64f9f79f557e74abac794f1830f204ca889d1ac66d649f8b5e95203a2c70e6d69b41e2b203fb89c73ceddde5478a9542b578e8f1b1e2311568d55a93a6e643bce769c135dbed8721d672fb69cb55be66ece397343840051a9f04b438134eedadebaa6bad944783c69e1ac3a1e20084ecd63af6cc2ddea3411fd64ab746df7eebeb4aba4c991cf07ad4c1381f3ee31b133f504824c6833253a383851543bd13550d78ad0e19170e03dd475a01ea87b24da53ba0f248ad2731075944e3b0a4a4ff972781da5e7e09ff7e580613402a64af14fd47d48bd2d7f46a599bab07a12d5134aef36749db47b4a9f295189b43f120e1ca5eb20ea299f0e501775944f47113980bae8bbaf50e71f0e9f804fc00c5ed9bd9e0d1a54d2215d863ae6035edbfa0b010df59cdaebc1570f6ebab5ca149a095cd4319ed7446d188a29e3d140d7eeeba56bdde7d1741a89ecd3b107f717c4eba28edd9b1d970442dfb19ef44e923bc6ee18bb634cd762f7b577cc9376d596c7bacb9fb881ec1dfb47adfb6adab512f978130724fcbe776cd5feb181d83db4aec576746ca2b45ea27b741259c7cdfe56fab584e27c753d7b66126e47d7746def6f218dfa475c1873e63a67dd65dd09a1a29466bbd5ef35a6642293d5f01961486c5e35e4911b22b6d5b21d673bce868bb7ffb843ae900ca4429173d65dd61d8c8b73f6bd0336eeb55bb6dbb5db9da9b89c951979c957f9a333bbb2cbe4c22e920d04419c07c1bb8589e2029581e2881874d48453618a11139a684d17141f2e5e32e72f69adf568b7db2d7249ceb7ef92300c9102674c8936f6f44548698f3151b8d3befdc9528a4b16a6f360ce76bb39f4be2ef9e06d5d32e70895a1fdbfd7ed9297a432ec2bc67b789646c2de74e44df592ad6ac750044392b496666506eab16ddcd6dcd63713c0000d0a54609a2c17e3f0b5f8086b6dc636631ff08ae30a5bada596d65eabaeb56a4e729d6be8955fd5ee5a6be62427310087b473e10ec54cbfd0ebc15808c83459d28e7608134422fc2e6e3be16ffdc220fc5ec789bafd88f0bbed721efc0e8909be1190d032b830d846f011b477391d12d50832306235dd3b46082b1019d4b11b09edf66db1d0ebf6d744d9adda5a7bbf97a6f5d22c18e29533e776bb39e79c75a7bb5d3b0a10b84855e2cd59d571a6cafd6ef4d2551078be76bbf7b6ea29e6b6ec76637c32c7d58c3977093d6388d3091818181818181818181818184bb52888adb5d66aedb6618ba9b5d65a51100ef3caa220d8878b2e5a3078de24ce2494b3378931a8623c707d043090c090853f755430606f0a2a2b2a2b2a2a2a4434cdd3c1c8dda7bacfd0f5d97531a9eee330f28b175f10bc061a109851c383e10202107e9f3334e665d6d85e4f3689ed91a9642fd5b50d98a933e69c3bd6da99ab9a7787eac705a2dedcd1b46a760fc771938b51d18670f1ae4ea32e66094fc49c708214b70710a31fe10359c57039a666cc795d57fe9991e4abfcd199e5c898c0909064599665599638f375ed5e15b8487b05c175e918f51c2bf021d5a2b3e82d587a8b79f294982f653d3d4b67c1d2c3e3d0f0dcdabd65296215edede65581dfeb62c0edbad3db75d7d386aa3b392ae5d0e1441536a91199c3c24577f1e56891bd097cca05cb8b1625afd5a2933eaf45ea7a8b4eea2f4aa41c00d05f74007cdef8e223759df4792f58f7e1f02917ddc5e7b93e8f7c81c788f5a6ce2b535629aa141fd72bbdb29e46addaf2ca51cb2b47ad11e7a3960a38e9b9caaf878b1e4a1f410e53fa77afac2717fdbbf7f32695bed2e79b563e917ea18bd29652296d172d7a4a890439445129edd2a746643da5a82a574911a5a86a8aaa86768a0ac545a76110175f8e1e522d3eae6cc970cb89ea64e866aa685422c12b882152bc54aa4ad193992076eda8b36029cd14f5f0375bc0b8cec642966449962449be58585890008095b017594f2c3a94858545c952d6d388244bb2244b92245f2c2c2c48f20e60967d507952ebf5210ce1b44774ecccfed8d89d31de846bcf2f84dc7de1f6d9ab1376f0040c58a91e6810228515a4782814fa72d01a68a189540f2c2e48f119c24e4665eeb73bd93633c383df2b95390002a60b7d91828bf6f53c6c30ae52619b20f382cac81dd445eb04fb040cd3eee20ba1d7f90b2a53002ae33b2fc08b0e0ab34bd23d418514e1f7de59a10054a6e3a1323a2bba280cd0c9ecab93bd58256248ec12254230db9364e7d5c9ba249dacf3e996743d21ce43f6655f39c4a269d5808ab8f7ce3be96a45e94b9db32304811376bdebb7bfbce078f0bf7fea05ada921da6de721508976aedbadd4f5ead9704b0de82a950ac7834fa92a04848bb3d6e927764e8493dcf5a02bfc9e79c77d7abbcf69ba32b577b4739d976c09002105c516d42b8939c45d892b890108f1276e38deb4a148c0754c99fb912087b57fcf3151b5571c4eaf01738a907e41407de66d5c8b31a8b66ddbddee0ccb7b28820fcb5bbed7621e587be3bdd096b87f0f81004216313de60e371ea4a6edc17a638757c5187bc0a756518ce8d6f60fec80f2e26207e9abf6a42c6fd0b458dc0f9e12f6ae3b04da215011b5d809706cca82d57e544f74eb765781199bb1df1a07eb877961735e18c2898476cbfbe4ca26cf95fe68b5ba4e57b78e4b5a3d3af7755b3f7899c4ab13fe44cdad05de75fa267a6306f3148369ba54a6062ac3053389f0c728b899bd1a2ed2d5aac5c4a4324b4c9407c513a113e1731b28beb9c3f2fd7185982eb766c816706a36b4166c058bedac62321e9f1d586c494f12586d818135c4bccc2460b05d33e6bc745668fc07a2ffffffffffff2993d5a89133e7a31108bebc4451a33484c2ea76618d7afa140b04790e2aa4c7c80fa91eee1136299ca3f65091eae10029dc59940564d62f12fe0f1112ae6a063e011ded79a79ede4cd49c289b0122e1531606a6cb94b1c094f14170f15bb023dffab19e701f851f6cf4ab7aca1f89e28f44bf9c9ab5a58abc2346efe1d314701b35aae0222d298c2e9927aa048595949694969465040f8205e0eab47f0ca0ddd61269eb7404167fa4cd09efe1df746ac3f6816004b60ef3f23733d89839c529abf12652032c9da1451068c2c686222dbf454b26ff4f3885c1645bfd96b53429549484e88067d8bd332010e133e7bcf3d6b85cbef8ced07218e6c197db73e7ac0db66a6f72b5b54513380de774a121d7c03619899cf39c74ce496b6c9df3b6c13cf8c22fc0819cb73b2fe52f251399ac460e0fca38dec4787060362f24ae9c71cc19e46ddd7119b46ddd71a214f05c5b70d7b207374b65c94f2379b0bed647545486b6e4f6cd4fe4335b64a667b35a33b240ded6dd0be8b3348f6637cd6a9aed6896a3d9ac91681d0d9b76b423796fc691d42cb1bd7a5267d3b8f6cdde74f21048771e0f8174e7691f5aa52d15ede01b8b27df5df534531335c3da7390a0e83c0f4a11bb13fe10ac131e0e9ff2fa9c2190062dcf39c43968340ad7e26bf1e674e63c8ecbdbebf6de9db7bbbd77e7711cc771209047e405071482069df3eac25f28905c968dcac7d533bb75facb99b4042eead5fbd0ac3755fbf3a7dd399f29838589ca3d78d8275be7abefed34d76305e4071059773b167a596be71ce96ac3176128663c9ad4b3efb59071bb8dee0dbdae129060cc7d3826f2a6bcc130432a6406b447379dd0c7919a292a03a232a01af183083f065d7c16883efae49bd30a2b84ff51297c0bfe872aa8781eea04aa848f2681f32fe12807094ce0c995f8bb816fc7a51b7ee0e259cee0500e856c5e201df0f1ce7be9a573ce3c7f6606d7712c9828859fd2fda90ac783b9b6905cff9e73ce20b9e3a0c43a6e07ffa80ac78339566a78c9afc5988f7a06d2dc8787cfe4c40079fd3b38430d345e60264a0c11f8f5077d443e9a976c0f89b4e6a519864a33d4200dca79134de929293637382e22a4e8bb1caeba3c6aa034e194dfb08573ba6c321f4c8f5e0f875c5b362fc8ed229d80c615638cf1fc0ca23c509491188a10d65ec6a2b0c4138ab084b0761115fe12e02b540927a80c6b9f79fa024e598d2aabb29f3299ac06c65c75e4d2a7c1382b1a5cc3813b0e771c29093834c7dbd61d8731c6b8e33a0edf9c5b35237e0cc5b85ab1481d1a978f9b9c96cd6a9571c64478ad19676cb71821e69cf76ed5de7befbd20ef823c22b8484371d42af263a78bf4c8eb84bb95133e06684cc0065b0dad14de56ed566dec65de160d5ebf187ba38d2b480c56ad55a57cd3afb4566c547e56582bab951a95da0a8ff07b9db57639a21d9fa8f84c955285c99b6cf85d853651f5767145a5e224fcdea9942f95f2550ad5a6cb7795b29e4256841f92f1e05fc9c590cc7e5d4e48d6912a2aa54aa952aa941f83859d90f3eb35536bce59a320c1c5598ab98495f7ce57ea01330bb8e15533e6743e1dcd5a1a36639bb1112eda725402a15cb2a38cd1e86863ab35c290d8bc6ac82337446eab853371afe12f32a4f642fe7fcc96e738fead2738d6c2ab805e41bbf0acff61449b34898941a2aa3dce3197866ca381b044ce39efdce9ac77eef4dedad3debd9dd7413980bab7bbee3a6c0df25a50b1f5fe6ab65bce1b0f0c36a366cce1beb38268463ef92a7f7472feff338da679d01d689c8eacdd369f1169ea798161a250464eb84ff84bf809e5878b4fd69fdaa46681cad849a3bd89f480dbb77efb2c32c989dabed5d3eb277e92d1ce1b7912e74de2cbbb3e0bb509edc9e952a41ca2a79584b5f324fdc171fdcd83f926fc3e01bbe57ced7643e0e4fb0ed3e5fb93f9c89b3049921c57338a66a026381a8f19138e86b7bd1a93d2084ec189a259c7623b0610010490314d56a6a5825ca7d1713150cd39412090888344fc085c9c37af21f880881908311a417710ae6eb84035afc0c2147e8881ee6e4ccc9c53773b86f6339bd93882850bafdef4a40ea6f1816f700e6e611b2e6f96dbae6cc83c5d5bc39e1246422094621ee177cc7af0f1ca946f14e15f1e2aa389d56be66f4f0dc289a2fd06f31211fe8fe9f2b78579a8cc9d793cab7af2462bcc59fcc3eedfb10b26aac3e30cc22f274ae3d574c1b8075ed912af6c895732c80082a5528b16a212d3abe496b3076b3499323907b255b6992e4c6c79677736b34ba68c2a669756e2952da70c038159addf356fca78c8c62c2c0403c17865cbbbc33ab13e1dc7711c870344e78548c78109aff04a8fd3c5e686858b8511d70e817608c4030cd0a040056619bb326071977559ae2144a218118b15118b951b9e42516c260abf485710510e6e6cd7690da511f9107591aa4054a389ca158f99ee35c8c8e0a1bc1eea200e059509c544223a321f44f944229148c46285c5d757fab598cbddd65e08c4fbcc77d1919898181414111f3d9daec43e5838ae7d850b63180c06c318634ce610818bba1c59b0b22ccbc7e3e3f1f1f8787c3c3e1ef1bdaa7a370b635cfac088cc818de338da3b8ee30863b1c20b64b67c2fa21acb51bc6385591e52bf82c0454dbe96f894a63b1e09458b83832f7783e3ca19df6f09098136818b57d56779034d43417941595141595149828b5d4c3379d3a673744cc33412fa8897d6d144348eb6d142e81f9aa5575aa5475dd3d576c7d339d1ed4c148877b1942e7656a4744d1e05a5530203429c9aa894ec9a8cad643300000000331600001808068442a16050341caceb900f14000e62785062523a1c89a3c13047610c84418c31ca186010008420406088a66c03d6ffd7ae16ab37ffc335f98e0b5642d2f738caed22a0bc783ca8b5335ff8f7a9afd6ec0b8fd4e91d63d0d9be1e9a323419ef4e3dcd95510a7faf87914c11470a611ffb06d6c6c81a6465ad44e61d573b06cfaade495bfe6f40d19be29ba962f9cc70d0fbb84e27446beda2f6e6aaa4bb9c176dbd325699958e8a3a210fd860c3dd3637528b1e8d865881b18b613b211153bf1bb73118519e8d25eb389da645f6f89d58e5a1173f7ee867a7a91d1ad9fbedab51fd3b2aa815e69b93eb856ac80891c9cded0e49fc623daab269bf34afa7411d7573c85345372ba4106e9e598fc4a76c0b48773ece975f9d8da31034f6dcb1e4f17bbc2a17afcbef5a80c5d1a5f385a14cb71114153115ae6df32f13525baf42be8a5bfde6fd6645986319306cbe4a6557ef929aa98314b1178171eef8f0e05b540eb8e881b7e4318101356da771ec6bcbc336c8a5b30f9a40da7b01079affb78053b42c3f036c5d19661b9fe2564a5c7b0a2050da0ffa2e1de5cd6e6cf36eea3f321278b0e945f36f2bec877fcf51778b7af1571c1b718d4f3bae0735dbecf4b7e28005c99c2edc497eaa9b07ceed62edf6bc29f635d70b0a1ff55e6bbec85e859b802f73a5924bd275298ee7ac6acc034960960d642e87ee21924e2e88cb643e168bde8225935881f31897fb245c1e47a6ad9f72c9eac9b7e6f53c1da24e459e2c21fbe70b5da100eca43b2821485424314abee5b161f00bbee2b9a39d35ba76059ecc669424949b672f2213e9d9bb46a304a29ef03adb4561f6772eac50692bf5e0809260ec84e95d280c7294f41f7bc68a5fece9a58f284914d1b3ea83d7111d15060539cd4dff194aee0e7b155e261679c108728490834a3a302bdc868d2189b964122e18b5030f73a1282ccb1c5714d28bdc4b51c3be04160041aa1f934d755018cf0c1eb0f06904db52a90d258392537313a3cccf459e72ce2125133f845725f2568786364d01a93b726bdd8ae290b735549aaf62ff8b91923481aa484d8e9488c6edae472a7eaf2ed245b413db2beffc60563b967d08cd0e2b64dc328ac27a2056f45c72cc366230a06e477f96ef63e727f699fdb4129bea3bbe04eb82f9d10da432092b22652e796237474c2bcf54931d9354d0c40b897a0eba0c61c02348825e2a107eb3c661a66d935aa64e4542a445e4211ec890d0e071154f659ee1fbb0361c3a2493202bed0a20ef05a78459351bc0be2eee55d1182a39c31b9f7268559610b694b3f71ccdd1fb5084fb36a41b7287a92a897a297a100242db8b1a7c1fba9939fa10626a9852093cecc187c6d11b94c1165f60144ca9ed51c59e7eef7520f38151b39be954b4232978d4c888fdc86294dea7ffce7844e363a22cc8dd3cc2cc6f7b87d7744414a48e365e52e127076a1ea9fe6106e85fcae118113b1fb79c1a3b45ce1d209073ac2f17ba04aa44a81558ae69f7a2416297326d532b1a19555eb00cf8463795dc8c23f125eee67db4ee1c21d80da46795708eefb929a8aff245cb693974d3314ff29cc078138f2d4654982e89f23a62b9a9c8bd7bbe971bc659c98950e4a89c252085abac54cfb69b9f9f88228ee294f172e6708385e116a66e9fc529f9b18931fde303e2d179d406fb3b81123892494788c18852eb87c6bd0ff6a132fbe9c4f7a8f51aec513db795a5d348a192e60c78d4b91cb745b939d639d0683b6f7ff656c91c6ad61a0a38a47778e2977014b88722505ac10aac96e1d1b343a0e2410300ae0db755a007328474884d4acf1ff0f202b3f0c78e2b084336b92c04ab94b18e2dce06269c2061e5e070de4428d8d131f4874a1ce25cb19801de0785f1082247b2c0745ffa7a61aba8723c59bc5e2529d9494b8628f9876d5a496c33e9e96a738880f6198c01c21ec8d0aa9e04d774ce55b801f6123baa7301562d524ec63b27bb9861b303b50db2b745313e78e262692d3a0ee65830dfe93b575b1f4c941a77a65fea14a51633ca37a1dcf7ce6bc3aa14de1f1e633e112b092bcb12b7806271792157022758bd383724e88cf4911e13377b9cc90965a3053c1f1b53baab8d197d531094dd4ee6b2bb4d593e103e576d51bc85edd4ad1803638220667fc988aacfe784714980698a9da44ea3381c5091f9864009ab6064b3b2505af6c7d8467414932866d6d0dd2335e5a68124abf7212062f4888481bfe479826108882da40c653acdb0cec3fa9c82ee0c616ee6d5c5fcf57ef1205cf2de2d0cd1ec67b4def165755928759aa2eddcf39c01860aa08199c9e78f410762e9acfddd4b2cac425c7ce8c26881e4e9f6af739455a92b148429d0b50cb6c17941660fc199f14cb75758b7264b90846cc5114079db10c88a617d06c6768219d71a22ffc7c27647a6df0d59c4822a9f32027eef64d200714f63112139e664ea0bdd011532157c766889029ce1e6f8e2b6c21d1493721a6e2b2868540045e48ae030fdfe66c6094bee92028b608d7eccbc3e6e65c0f9e38dc589c3c59191830834ee0ba4cba0e54143fcd3261c26c4b9d0990da635d8a9b7ab6ce2e315509380154ab395619ac514822e39767fe047498ac5603ccd6965015880c8a4f50816a96aae68e43d672ab8600b44ef8ca71b67fe1b9d2c2c0e941b4b59ba6d520c93e2003313c2528bae39ed3d589f2507fd7f1b27f81fe5c5986054993dde1b6ba8acc0ac0ff19561ab6ae276de03727b9b2035328168e9bbfd0b0dfb02f7d64445ca905ba1648d6d20979a465a7dd85b8d55077d312aa8652eb5eb1cfd5b8ac490c4fc737e370f5cbca72d2d350d8ba790f1dde5cac3167dc8dc420d5b2b0621c126e11d978c143262ef4a9508742838943033b9f56c7ba68c2a19d9a02d56eaf7921ea2a50e34e0e3295372c978638cde13112a1c08897fdd71bc5aab83fcebd1c9e4d47697fcc8082350424ca64b789c1336442234370c335d9751285c614a6c894ce768d350d68720c4261a4d6370d3da85896af4cc8ec34dadb6b41b229654e22283a1e450f55f5abd56e083814e693bff0117c0a0cd289057396ed021eacb17ffa6a9cd76dae7a3842c23b35f18dc1853add828f78a42d72045d1faf4e1dcf0c9b2e40da877c277baf0929442775b36c343baa6ce821bf1aa62329539b6070ec81824872b8b2896db16ec22f7cfc2744a0d5b6b66a49f7eff7454051da7905e65d48ce88c251050844798f36d58ff9bdc81fe62ec1e10a07dd5e17d87e2388e0e0a5632cbe3863f111cdd86a3ed3e4cdedf1d0f387c0a2a778b58bd454f4ff385046f8d2c50e4c9f5998167a4ba17d23ef328024660b75723da253b0d50b9cc8f41a3ec0f27084b46cb24ec14b0e1fbfccc75321262c84d309b01a303170f92356bfda59946992408971e755e741730b33cb2960209e48401b4d1cc95fcf558ca0b81e72339995aae0e1b02f32a91127027b4d17c2a135a8549044685f6d67c1bd3eed385eae1c65ce02f125942445ed3797258fe0bb0da29ee94ef0adba22f8340f6e00ec8cdfc48e8615122240bd00a8a24cbc978d63624105b81dd21d75876bdc0fb4354d09b9053badf4794a161c9a713eadc371ca232da6e96048e27e3bc8a8023dd6a866c465ee436b4d825b31c46c793dbd4701e59192cafce631fbac3a5cd61c4131198a138911e466affc48d19c531ddb2c9cac37dfc2f1f3d8cf4a0d561cbb5e2227d573c06239225b0da1a24d52e5701b926463cca7fc818c0555775e6a8e2e4a78cc1efc2dd4cea8a8ff32fb4f5360de9452083acc915981462b1d9ca18d42688d40c98b178c2a3525735832fb3cec453589874ef032973abd64091e0bdd4dfc8d869cf9ca3bc64b54280336fe2f6d21220980c29569102730aeff2225a30299804ffe34fbaceee1e204e807e77d6c38481491735ccfd268e81ea1b61dbbc4bd6fccda131a0c6ceb224a1a26f6a641d8aa17b01048bf8370519be6e021a54c9f34a2ae595591a0b619c9dab3f64aeba0e688555338ac924759012b61f00f94ae682bc9d9a2d1bdde1d55932448019bef4896f5fd3c210b1276233d1bd580a53ba07f8dfca1026ef06a96c9da20eb195e4c15dd78b8d9b93db1cd78490ad03ccd9d64cd526a858bf559a1a9d31ffb04145ab42b97447d11694ca4887a4fabb3313c2d9f36e3c3220c4ff36aa0277ddf766256f90cdff2686db40e1055f7f797e8bc7dbdce8e66cf781badadc3327c510378a0c3b6a83a6734cf3c2cab0fa26928b2cf336282b847c42a2820a3d7e2525eeaab947c9369dd69c7d6efb675183ac6717565392f74532e0d503f891362185006ec278007285804d48c6bc3682c8f2f869c072180a82792e13abb6429f96ac90af1de4dbc404e1c875e30ca7dc1ac1a5682e18a0877e0e0dbf5863fea31d226a0844a3cf9abf513c24996b42029640d0ac6c289932d168415b79585f078b0ea63420b78c4dc8e1d3f873d5fdf7cfa49e1fc96a7875049b7f0405b9798cfdfa1f12677bd784ec9c23c2309f174674da3e190819188603a48a76eae01346172c714946725d61e19c0cbdf544598ab93a3cbebcb84111f79fbe62dd065475fb6bc2c628fbeedef8fd714a38a2417b1545187e131d5b42e642dd0a7c4a388d72c1146e367932689e86aee750cf45217c256638e194135fb5374120309746c1a6b5120e7bd0634ed5272de40fdc73c2a5faa101b46e3c2579677f41292d397e9ae0df52e2a1aff2381fa1ecb781016fb2b03585916241820e24de56cb52ed21a2bb164347c1cdcdecb2509aa2d37403674586daf45b1ba3f6abac8cbc55fb092bfe23b8b3e471759dad260963faf23bea4b3701bf57632762ec47e892de7b84b927578b58d128245f6515d9f8c86d61d54755890802c23f13cf1ef75addd4ca46986ca1ced9723374928f18731293a26f2b53e1c11fa1eec2528b1f254b0e8a639b44d6af3d12be0e1e0f4ec2e594ec34526b59326065b386bb639579b819f4381aeed3f0ed93565df1b6bf4fab33d3e1ad471e7c0678397a9f341d32040bd133b958acd0b2393693091a9712a453d3434eed24d7359b6136891add8338cb83fb3085ed324b25f109e83f5022cefd32a254282291fd06604537e2dfbf79085aa5370bc0aa319ac280c0f4d288e0a4b75a5561b3383b70fd7826b125c078462aa8b32d9b100e555170be122333e7869938056c36dee3d889f4435569089c1d3d473be55a5c078f56c35c88c414ec92f45bd6995df98d1e1905dcbf5000eae3623e8e6aba9057c684dd11b716f1cb7ffa01de04b48794545b613c2d8020f8a4a8f699e73810d26da4135115b84f2a2e438453257ac99bec00d97da1985248da0fa7948e4251df67479aa6fd7c903fd6e0df16686258b6d340a41eacb22b6bf58254550c67251d9819c28ca7e6946800506d8af5a3abe19ea205614d2ee56f65d39e63e8984ee36c327f084ef467074ef433e7d8d05206452ad1424f9cf5efb3cc768b429a6b519596e604bfa52a4aea17926822de1d310c5dda93f6ccbb2d7268cd1e64322c1c7ebd1ded38dbcbb6e83b2619cdb7c460573fb52c5c4b3afdb0ec2d92d62ceefda9aa00e9fa04f10009fbabb72472f3f7130a1b2926caf6fb64d75bbf6da19232fca0f1a40af3173d4705c7978be41d720b12e1030b2b6ee5d7989ac11239d9d7cd496bf74c47f2ebdbf8555f9224db8954328620074191e4743e62f549e8be2f7f7a7dba09a007ef81cdea24781d69a5b9668c34c105a2a27891b13f26fb118597d28401d370a0a1249c89f03b79f3aba47f352b12ac5584ac14900d9491d8a55b94b81bf1bec02ebe595c0dc6a4384d28321e23d35fcd7ff6f6f3f2d56620588bf9ef13d44647add66b276e9db33fbdad0df179701cbffde15f05b494381375f7577b21969dfe5461a16d12c7616b02021747fe57cb06fc9da36dad8aa2bb276c3620ebf41a1118777b79528b1c32d8e368c7be830dae914f05da1b0f1932070af372a77d4991316d4ca77d60aa13940af4f718a60abb045d2794f3f87e3504ae919c76c09a60eecd8931ed4cc35f6bccc69b21c27e9a6d498e89d4038625a1bd7001aa9d092d359b81f89debea234e1bbf1298275e26fd2ad21e11da7284971b2ff5c01a0d852e030efc9b0981cfe9c0698e9f420447c99a6a2aa0e5e8ab39a820f72740f078547ac0202242ae8b39d74fe4c0407c4e3f168e4e69d7649324ac91cbddcb66a001b8e0a9269b1231b651d4e5e16fdd64136cca297493c268aa7e1982cd8d9a6c18d56f4375c6d7af08921ca5e8b926dbfc55d8734d3633a3822e0356a1c9bb265b79df35ac286ca7cabde89816856e4a751785197153106214f08d2d0217ce37e783986da0e263fbd51ca3031f36d202aba6a021bd265b700b2a55942d53521daa9aea50cc106e0f126a01b68e6a0ced42368bca56d4f5642e72efeb9b27dd6c67acd3b17ad5a34018a6acd3fd006475ba979ed4e4486c4b7aab981008c0e00810d17242496804a8a91059c3246bb34ac4bc3ef5b0acf666844bb96aea5c7e05a6b0b0b23165a8d49d320f6ab57ce50e95097c231ef246b2e59774ebbed1402f692cbf9a61e5d77a955fa9319921043e68a6fd169ffc62087d74935fab00c9932aa418cfe8add2fb4eb6372b2ed86b7d0a0e87d01437709af7965c2a8bded27e2b28f1ab6ffb057312fd105b704bc4fbc3956675ecc945cf0ef741e5cbd1ca494b63fb74a50d493ecc85b509841d37a239cf0c2936c5664f03aaae5c47da3b48c434e10d093c95e4a94782a6a4c614d448144be958f53a1a42e8da8859c7de824b3d8b3e668d11a1bd3392fd86aea64be0eadb8542bc3a52bbeb29d05c1cf60ea2710c9a7d9e6912a73931b8b3164148c4259282102c49e06688820cf9a8830f04de5e5ab61f8632ce33912ae765425859058fab168e96953c92a20356473a3239195874efc5c14ad45a871fde5cd8629ca8d18286e121fd79cd3e0e1c0b25122037024aea309f84d4ec85b4d9cd0c5c919750c0afd87fa29c10c21b60a14c0c6819380878bf8a20ca4c1415a40228a47f092bd110801f31d0c619d2a376a084dac02cf499192e437c29d39fcde476c65dd12ea93fbffff3831b9de16448598bb79e39cca616ea0dc0a6c4845f511c8eb479584310c5e1683f1c38b687749bb23bf872e3f10f6aa16968e89ffa8d996e349b50b40d86c6d590bc2707268621284a75a303522774301849c5e478d909af1af35c8b22232511b928a2f525bb17252246980e90d48792d73fc33d3ceec708bda71d7ca97956392dc99d93be17b1bd8bc87f785b1cb09d15566b0277e6015591a590314d74268afc72f2f1aa2b690a095084a04965b31c5648e21754f525f1b61c2662e1afc0db010e0ef8a57487663e99d86a6b11305cb78c3b78db6376e1582f93ba08f198f455f2808f33969be18ecbd7b45e1528d4753fa514b066d84e8fa0d83cbfe573e3eb4909dd004af78364fb5e66b379bb2a673745b7e076a67f06280370323da170f4a3dbb90c43f3763297914fe22426b8303dccb0ec9ff9d87f6ee17898e5e0e263478f44111bbf78bc7c65a2dc5743c5143214f4e3fbe04e5ee37233eb75b68cf6edf973e1cf26f9c2d28e65ddc8b41dcd3c2d8ece8d8536fa1b8650aeeb881138b96d5f8036db160c3c87a7a352931aa88b13c85399cc8c03ba15291affad2d79120a6b9b9d18fad2fb0e87b06c62268a22b1f34bf9139650512dee86507d08dc3f1593d9aa1008a11d9c1a79a84ebf98c38a8e67444f1d14999a32eb58a3657fa629955dc0bd57806e1b78b1c3b9592790631372cf9a41c63e4702a7dbb40a59ee48a0522164187179165e3122d68d5d8595b10029f67dc5839dfe15e5b93de27af68d46b14266b35ad6b8658cab832d6dea5efce7fd8a3e878336089c31f46712abc0cc5399702b3c10722ee3706a91e92a23322da436e9ccd0af60be08b3eefbdaf50ab2de3085104c5f0253bf18c75721d3c473131b64489f5d2e6a435310fe877c5e278f6b6cefa3ef195f17c3262fa10731ef8f6d5d85d66229210b7e3e39b708b4173ebde148c1d2c6d263e9604c5c1a05a25e0f5bd6b455285fa8c70dcee4079057a15c64aa803461508d255ffd97397700958347818ce07bbf277e8aa0bd32f24245ecd394a6333006698c66e25e40f86ef4e0569114efc0a5107283891b90a667decf2eb935c2b91390513b2a0ed242c34b0ab075e43b750207c957a7d11509ed0139679b6f452a4ec16debc94a228863d184a83f966918b9bcd395f51426c110a1523f37646e33305ed25a812fb8a9a20d145f86f5fff720336e0207803ca61345b94ed282f324dd27610698493d816050c10163721e269f842d107b794636e806799a6e8a9c78980a7768fed54a145f6e9256c691ac114d2c45ab3dbbf30ddee9741821abe6ae6a0d906f305939645698fdad4783fc390035953c818595524b6a4ed19ee2897b1338467508bcb6b81a0468262bb698571c06d1a104da77726515d977504c86c875001cca6f8cb394c82ac1480b03879f591881ab8ea65849662791f9122e0ea626825a8696de4a594ac72b940da366c3bc5488630fd56234faa15d6ce57f13756a6987a63a98e342d5bde3bfbe845eff28bfed58f3528b235c7732026f7aa7b5c717f3e25b98064182866bff501f4d5796b0f2f235a73713d822a3c2048947b010d30f7d2aa40dc57c4cd06d1a8cea90d694a1bec5d0fca3ee54cd78a5c992b03e5370c02c8fc32e99a4f7ae108c58c65a09976b2231062ab9b9c7d45a336aaf69889b96c15e9e6620deaa2b5a08555b58b3862f8eab2464ed822e3832c88e2893740642d6114149adfbbba1d5cfe535a4f05ce1253534f33a74f81f50faa9d130b30441c2e232d7186690a465aa22d50e05e8519c73a7cfc8ae4e362b259176cb6d7a3cf133fb2bd7c5649570c772c7b0b3ae6ce3a8c97097b635dc8f03e5fafba5352c7a6ae0754a86c62404f188eba2a2034b3791e40843be988995f9345978f3b4c44b2f10e11e028baedcf66053272a2f02496c1546446c6156f9d8871a39efd5c2a81fa487f70917ad51b4c659ef7dea315554a809ab6a4e3d0957636cd57496fea8dc1dd144482a60700720a422f6f516f2e190e256c3d7fe4194c302ef29823f2139cb623e70ff84a2c0bb0e4b904cee1c247c43c154a439427070932cb1a91dfd11e3490c570cc7237bb39fd6ca870a3ddb93f559255d84e459eb5f6f40bee22f62ebebe8388d2be3939946785e6d571103b8f2ae243d6ae707e814f1db3f9769e5095a7c328421cf541262e73bb61a061bcf9b2521ac558eab97065d18dcc2c0ccddccb8135fea5fb730e5e1b76db2c8bfa6a72b9e62c5e0897aac33acda84e48220fbf71bc0b53dd0a1c55a7b49f66c5cb9e1b0427eb09e6efb48d77ab2cec0044bcfbcbd11089fae49c9d4bb1d9f25852d3216fd3f26e437fef0ed9365e3adde23c3390f0ca44419dd7b6ff1c0a4f94edc8f889a85cc1d7240e882313ef6fc0b3ef29c1449900c41362e5ea25a01e19a461ff69142b4c72de7e4084653548f2aaad7b8f56b0908c6ff67948735cc7c077b3b8ce6358b48e1fd0416aab6bc59b3d01efb4bd5ad3f661eac3846a5afac1e750f1c96a44ecf480952d92734f274cc80306dc3a36e6c76c5b3af57727a7206a17c1a82697414bfde08eb95b0ed07eaaf73eaa22b631952b6b68cfd655c781bfc287c001dbded128f53845ee34a6c95091efc529160195f8745caff43e857ded195e5b5d3174007a5832baf0e9c328a0a519af13c0c00da3cf86c4727b5eab420d9d3d481f61d5cc09365f2456362370825f98cfe315179ec6fc4796a2464703982fed6d124bb080aa03350a3e24c5145f437c532b14abe12115001dd4bd9d4ceec20c1707850d4025acb6980c1a9a53803db4fbcae13e13927a2e2902090aed8e620c1cc748313e8b23e718ac555f2be3dd86c4036334c8efe73e8c0b79ecfd5ff15621bf7a7294ffbfa981ca76a11df5d781c6b102f8a8c33f16a922d700361e4334314d880dc8e742ee68c7a8ef16c4bc222fc0ce2fb0e8ab2f444a84a483314049565ae4797fe2457bb11c483313e4ac5f72dae8873c5876e2e8059479d910c70261720cc09a44a9b7ffbfc55ec010c4785e3b67080f280f997fd66dd30a9afd6294c262b7d6a10b95eb2a3343e062b3eb1b8b7760be4294472b2168ad49138c0723568a4ac2e823a08d082d9d741725864091a7319088604ab0de0ce78672579eb583a1a59cb487ea045067b5431ffd804bb7e4cc85541330a22ac634e528a0fd2fd14ac623ff528999b794688904c738e504efe2291e0ef349b64aa6d0ca28110883a91bc6ea7af310bd65be49cce4aa96d8c42c0cc1050f2470ed08927794151e744d860effd2ebae0812c8e5cbd3ef5500aaea402fd68607b4ea4f0fe68309b69ef7a66b75b190801b50c823867e7c389d17656a593066bfe083269e0978f4352dfe8b7f6fb2b3c250d9008d901cd14c3181e88af87e9cf776189b81078bc157e878242977ed4b84402d21736a4715622516d988e28f820d392aa07c129907e2f1090673d0a25f5d744fe85868ea0fddbe6c2a5686bce6f4c19dc30d90dfb2ec9e3a038a1c6deef61cd22a2f655b8773d187a4b5121af14035a00489df081ce7d1a3aae029714e95a5b1d9bc44ec7bd45e211f75a58ce0566f49961d4fce0427fa6b180de23608a8bedfc89a167bf50632bfd6a5186ac4fd551cdb074c8722722d776772f840ab88bf9a9be982791123ae753227100de4bb6682b5beb2427ec43aa7d1ad9ed7ba8e6d8acbfca9a73e3fd890fd33099e2ba9cea5bf679d2fe186213cd7a82d091ebb4eb0d3ffaa50a122027015c1481553a93a7e3015029c8123d389b814f614dcc1e7b8b5737ad746e82b2e156943b69052c54c607cc8e15092287a13dd5325a994ed88beb61eb71aa2168e3882c6d7521ad3adb2d906636e38ad1e22733b1362eab4e72c2898a3d0231f5731befd0697645e6bbd275782fbb0b1d81c9aefa01efb4367ae5c1d1b39fd65f106b47d3d6d4879ea90026d17aebda2613722f3fecc0185910103d5c4d79653bba188263937d615c0b508c0eca66dc1f354a9f3536403a875f1b9856823a73bef3197dcd4028f697a88f7bc31bdb84dc74165a2b4555c4d2feec549d73c1bd342c4664042bcad06842a86db9f895bb71ac95be506e4b0c9b990a900f5c5e9f0c4c03cdcc46ed755bcf691fe9c806bc420fb97380c4509d3ecece013e641e50dfb82251853ffcafaa9bb38e3497aaef6963e3aa4704bc65c12b6e2c955910c72d56698e8d0bf3f7b769c21f1689ce832f85963321b12383efd5c8b4b8a93f1cbd8fb7347a859a80ac27abdf05a710a4abeadf0b2ebfa2cfa3b27b55163678f4126eddee808d689792fecbde5164d48f1dcfb2ad736ad861338689b0bae2b2e0c1e5d1031606da77246f0753334177667de63aec199e86483883a1934d5d835925b8f11dc1015b657bf490838e78da163c4060ba62a3c5b21fdc4440edfaef56c905aabdf4a862e22f83df5bd8c050849df6db0c41dc88eff9ee4231ece26f6ff1adbb08a4197289b56203bf2ca30a8fbf38d01d9b5b70435f331844268c8754a388545f93dd1121c9e1efa245bdac4db358c22b9413c90de54b694990f2f23786a6d03042521c3ccc147fa3be6059c7ada049728a14cec7832fa1cfa4ec0780e9900a4aa782a4a07ec54f451377ae037711df04f8ba1905be09eeed2fc1583bb297011f458c5884f5e62dc92408f39c123eb27937de760c31aee60d7602cccecedc0b1c85f84c92e6211fcb45c97b61564c553252e7315eae820887b7e4f18c418a90e2599a0a180f26a13725a05a431e6976589bb09a9f32eca189a366a2a9266a1be686a3c0c386fdd59872e86904fbdb6004176152d2090b6fd49ca44467b28299805966358ad803432809c26ce0d1d4be382ae89ceb4f0ea58d82fc695f2e12a72397b5df37aba50adcc5f901c43fa8ecfd5652e934c02ebd338ac997a6d09664bed7b815c77ce96a342edbc33b0749ef8aac338de890e0dbc3e6d2b7134bfe1b0de9cbba6401a31a3faa612903a5c41f05ce0c300d48ec835dae8a564a5561821ffc56a4e1f56adb7ce9f8f38aff59463fc4989052181148507038726ccf382a8d9bf41e0bd332d7d7c325324f2c06776aad7872988c7db0de5d69ee3ad5a4a90a3ee6ad7a43c7534a73c93a7711b1ed1bd61b5a36a75a6d1fce5a7c1c90d9cd327b336b786cff0c61bddf146b234660db956699ced5f258cb60b7088d34af66610e9daca67e51083c91385f8b1d977065cb7b557f24d05a2859f665e23c25493aa19dee0f5f35f1a9011fae21808b116ddc15145ff1e6b5b7b70d1a19ca149b8c975009fab77e51704a32106cae0782d335188b61d1be98e73cdde16d87db1d819d745cf2cd8b0b2e3268f5aaf4ea2c7438df2a2ff25d93d2257f0960f96b2eeb96ddab1a06c52eacc907a8c5ff29134981b4fca4c1ac851b44d2df0e0bb7ccf390b531020ede3f3bc550d2a80f1b12d407ddce18879b50e950bf14695ffb322e3e3abad72d67613e52deefee0ce025516422ef8f50075182c72b4aa99115f86e1702a5ee42e15298e24849fb0d88bf9b7453f408135610bf200906c3200ad24282a19046253a2d0132d4cae1ca53d9953868cd79f87d07dff9cf3216c17de5c8b1c2f892d1e96b95bb0f7e7dc12709f9957590a5fa6f03dfc47db37744e98914854b1c6ef01132e601bf1d04d6f4d56f8e4360845d3f27424bbb7e75c4d46beada36e0d2b56facd436f0660d5ea6b523f1a0176cac9dcb2f8139ca77fe0031696466a6358d2950591da9dab21a2fee4cc38efa4c4570cd9cfdf4603161d3178cbd07d1e29822d5cf7f89f4902a598350e6e66c0d027d1a2c6c9db9292f827afa1d846adf6fa08f637ab1b70842bc265a4d4b894793e1f0ac7469648bad456fbb631974ce13931e4d08a19d69923a46082d54edd3d8d2455c2f7190d6bd96971f9c412c46153a0d7412b1f35760d4eaf480721e732a408f722b048f07c6b14399141f3492a516e5452d402b66955c8ea827400872dd168ed00a561b82e1fd6491e3558e948063e94f13ab65235db110da197c9a9a6a9ec271fdb8126ebdbe36041bbee664f411425d1b047524d697bc7c970795fa2e2ba8efe5043f77addb1856b2e4621c178696ff4e736ee8aa87592a0747a55cfe574388737d7100a589030391b4d08aa03fb419084bdb50601186cafa924e8ae958dd4b61aaa481a7f73d911c72a481d255d0ba1e1469a6c844f4403c5f7518095522d2ed72b8c3fdc55bc24b907b4da5a87fad5bfe18ab49fcfe362984194124c45cbb68a00bc8d6aae4225d8345865be100a91791b2087e54de7474b7238a83e36781c671a917dd91c81134760475f3c533ae4adeefc20dbf8ed9a54a80048ef2b930c0a634be2f4a81b8cb2e3a0db3b733c9978248f5c16d226039ff9e903a49cff01c4276f8c65589d085a6031b69e30a9144e9c0d8206dc548cb691726fc65deb726574cece81dfd58c30c8bae9d51b178fd164845d583aebf1f12bbab64297a32112ede275df69b78f91687a9ef3299604cf2b3a01a365cb4bcd74193dcfed438cf488d9b90491c33bcd60077126f1f6a5c3b05715baad7f07a1a774bead622251c08f17ee4701926e14227a1d07f25956d2f979934099b2926155d7efb7ce3f0a9ea0e5b386a8dfbb9f7b8407db6d6e8628f3eb135789d3bb375485df72b3e041ae70ae1ee84bee0a721b79466ec4e75d5d640f65d0638d7a144c9e52d6e0d595ce7f8ddf8f84ff15ecf38d9ecb08a1a53038b54cded2b4be81c889bc8bdd930a032a1abe95ee82fe239975376ac7543131978c6ab4e9e81571a876395338234ec4674b482f586162a5be8ad00f92d3f8cd71bd14c51e2a8aa9d177296f19f30c7f0a760a2bb722ee80b3fc8513a5596111a0f0ca389ce53b25ac1a9acb3b572d3fbb70838b5dc9cd87dbb3853072d208ce5700a9a743f673b0812fcb046ccd50f6432cb4d9d04804bddff1f8e9d7e2c9ad43eaec1205c0d685395fb810302a1eb2ed129fd0792b63a4ca97220f8993c05e6a06aa2846790635640fc1b5aa187b14bd382c4d91d64173c5b647089393be9b1e213fb4a020ccbbc2f7c050295c75f82de0896306b4ee33c1a9f1659507580a53580aed1fcbeb8939b80eb8b195008b4791111c65eb6ad0834ad139c8e034dd64c04c43ad81ce94eb80e27f57551bdef62d50a936c9aca2ec720887221c28eede9b268f039a5a319fa68725dc04486d85f90571923883a935fec4a711d703206d50ed76d6e6caf25d1dbacdaee8b1045971e66184227aeb0d197473a44b437779479798a201568606b520f513463a43356eb43a81e52b82c2bb7f7688b529047de4806aba87342e21be9edc5b73c64f7f5eae95da8ee315047010de3993152a55e377f920474656ab0b7180e4a2443f6a65b656ea8882f4ea77f0472b32db3264589e434b791aa99cd5f5e4780a0fe8fbbb24feed7d42e7ba49a1bf6d1e94cde8308826a9031b82d2be9135206125afb85d44b2100954050d9028c1285da359ea4d53ab43490a0121159ddd3d4d3940d632524a6fbf3956620f237f2448532a4018ca370b3f40aa816e10844ce5913f80d155a08c18080d6324086df0903e0784046dd4334980fae58276919ae2b6906250a4f9d07e312531a14d0abb572f4342d6edd418c3e82856c077fa981e7abee2df0301dfd4cd931196ebc68a69ace35d34f5fa0a969a5ca0d36c5449a240aa04c4f99a0ab00e321149f596a18b7df80323a33cbd97112d73f9b9d5960498f96fbd92d648179db6976937287917ac1138585de8c0ff8024269f51ecdec1202c4b93b100eaed543796617b83cc5d6619e0d552c2f2cfcf348fa3c2ea0c547c6f3be2e08d2dfe29fa335e6d2eb9622469c268660a6af17c0cd338b14c37a6ba3de0365f4ac13608b7d22ca4a937e1939e7ed5289d87766a9c6f48e910f0a7dde22a81cd19fe70cc7b8335f87d2ed98a74eee575119fc937d00b1c3056980dbbc26efb4ffff10930121e9d4e006607e92d9ea8cf82aa4edf3184e35be645e7c29dfb28885caa404daabb38c8ecddd26c61544c34944188f63066120cd01c70ad3b3170f0bc0079ecf6a615b0efe0fbd2c1098a4b2a8a3d88930bd5cd395c525fffe069dfc1722e3e7f63b92b8205f47cac3fd6ff5762b8caa3dfc7a6251fcc8e36aa9747d040ef2df0cb44b8dd96ca77d2e4eb071e2c653161de2c5684cab72d156bec59aaa1c315516d2e41c1832eb21067d129a5fb1cab3cbb951f05d0b2b7877315a799a2ccbd3742c5267e1a0b14057b5a2a7ff8a84f06679f08ffe70f91604f2a0272312572ccbf4e5ecc06e5ed4243962ed6a37ef0987bf0a59021156faa06464085a213f2f3e1fe0fc418257419918154b0a047e31b8bfe8f09cf61f2cc8bd9c143ce82430779963bde423dab1fbab10a9dfc05c0747ae671832bb96626c194e4c632acbcb2bd476dad822c55a02285c0006752675c0acfa6821bb3b208ea29073a60c975a9a9c1994eda4b0009938157184aee24205c3789f90d2ad9b306a08948d260a81469478a182cb184d426a4f0ffb6819e1bfbf71b88bdf049b423739411049e23b4ef9f4030c0a2a139e61121270b9671d1c56765fb74c94f300f91556429c640c1f29af0c363e7a309db79e272a3c6f14c75151c5c02adee39d2aa70cc03ce7ad746fb3a5bd00a86d7a0b7ae8122a542550ee006a2c776a0dad40f8d8eb8acaf285762319012604a61a26ec0321d1362634b3374a31adc5163676a0e85cbe91d8af663a68dfc7b0352dad2244f9f5837e4b27ee6a9764ab94228d9090b1003f837c012559edb9d8316ef4a6b5cdf9c230d43f2d057ff2e4c49336a6b026d8bf5b16f794751d30bcca622cbe6ee90b541afaa6c680ede7b63a068621162c6b6c22bcb16087a3801d4320428a6c917d79a92fb1eedeaca9345a1abd63c5d2101eb87c27c5019ddc57508108da3458c8bd748464b81a90ebe5c0023a0c085645aea88262a50c7520350ff9b1d8ccc442d814a4dce6fdd9458ac56ec5297762b76b6c58daa2cb427b8c0b79e5b696242cbac36f7c07c77419df26f83fb5c114374946d7797fc95c3cc1baa8ba0906819b8e538f32764f6032ba09261929d029d3d2763ae9f68c83edd50ccf0c4cca830ff2137b0b53c4280c16e7d98f48cc4d1ce9c2cd996a9fd3be7e262075fb5f021cbc8db4ba342134de8fe5028980b5075ac4958195bf95236e3124eeea9ab7738e93f2d68a6aebaeba78b943b1594f9d6f357f1683d32c0ddb0f51680bf5240638cd728f589db4ff0ca314219fb00509917d37075a3e076b7d133c03399839351c1c1c75a8840521c5fc04ee62281882304e0d58c88b7c205dd817abac678825bd7fdd62a3a2684532695ddbe8a5344802d308f344ed5e090c09de5c3930a6cd1cd014f8cc4ee78e9dd23fbe9d11ac2d49c7aeb1a758f38e8e0ebe1844d79abb73c03e876a2d31a2eef1a62c9c2779028a21a7178d2a85fa3623429187534dda35fd89630ee559d1f2bf8b5bde64a23292c65c3b53794582d6394b8342f93fd88934600456a127ce2ce2efd8b6fda8568059343c4e0ad60173a5617196130b89741a0f50311a6dc158e1915bf60e213d24deeec95572ac72b9317e7c12ec01e30a8b9969b2cc5c2491e314b42e5ce3783dce7792a0e1e875b9cd5398d00e44a0c7611e7c25c49bdb5c54665a1793c9d2365ef9e547c503a38347eb59647a7bcfbe8d55af84a38e065cb90d82be2899cdac68c752e2f859efe17afde6c7c20cff6884cb62f08a63cdffc1fd61043444f0fe70a7c00287e638b9327463869dcce083b66384365acb621831ead9753759895241deae9f67948df50082ee02097d4aeb7a1eb790178e2cf98fc33a6c8bba1a9acd16a63155b219d8a6a7c11d613879fded8e73d4d29d80035b91f9503ce17702d9cd77ba1abfef941684abdce170bcf50e545a8c1e325de55e98b15d946f53602c72087a4dc6ccc187ae0afa97c9f4e8d370cfbbdd4bee0402b4a05e2308228a5d2446f24c5a4a7a1699ebc09a2ef8d26ace1f87db139b00311b7c98e3347f3f7f17db0ff64f01f79cb0df4cbc48a69c19802678d0fea7af7a292a1284d2792b8a6032630dfb0c163608398f8267d39dd2824d23449c930089ca939cc2083db71b21f3fb1f33341a6d3583452b91b9e92a08d9acdcd3e996704b2a5f2a1271c05d64de7ceb78fb57ac061732b488095564ca64490fafc2bcaf68e2f49296ca3d1abde1061ea7112ad1cbfc4535e6f69d162735d7358b8745a9d0e5dd666491396184fe85cc4998f1cd903c4156a8dac0d19dc9c30ae8e9d429a61b1d1b4ce1364ffbc8786a672af24d80b72ed363d23782dcd0a6ae6987b32ade920ca976947437ced29caa7c6f0d63d74bc39d801348e221d3951229fc56c79069aacddd370e8738eb17607cb6f03cc474b49c0ab7079837e81c329e44b41865eaecf21a6eae9b261cf15aa0572c4f2683bc0fa0212d2979e8b9acee9ef22d6dcebdc3a09e78d82becda90418eaa707069b726256e164fba4a4a4b48d30c81f3953efa0ecbf7fd81dafc5094cecfd1aef8aa922fbb6a16c896fd15a1ee3bc0e31209036fc9c36e1bd2891e75001a76b4022d3bccf8c4b4c4523fa1d1f133022ed60d4ca37102ad848dceea9d5634c2353e4e3eb19aff13c2d63fd93579708030c0fa2922d08a03026328d4c559c1041a44cb334a3490a8f32b4474a129ab30b987cced8bfd32319e258012baede06234ac8bf5a206e9efff4372990133acff1940942da26eddbe4591696b3c3dce18df8d50ec0f5b4d35f96067cc225aaef84c49fb443b6b9067d390bb6f7d8275eddbc6092ac93983276c8f328a55530c14e371de6ba72cbde4cbcbfde35a07950f8fcd770ba4d805e4d3127a63ec193114668cb0c91c1bc5bec72c1860b34255780c66415627f4ae980c3784c8d13dc54bd6e2b2b76a1ce84d3132301b676af4275b3246ccd43895e8215fa756e1640a4aa3970cc54d404e391df4c0df7a1007cde18d5f9abbfd9a3aecb307e408317fee1f90d507f2151464ee3371717305e90fbfabc9b3a764c527a80d0c2435306a5916fa070dce34d9931bb27aa78888a2c2823ee59451909dc8b7e4305b8b2b78ec705667b07a3a9a7a2b136ce41b7e8354b74af61be25c4300a1854aad0e2163f603d1d8ed81d3ce7c277b6db832d236224868fb0fe670013bdf62b23b331bc2bc7731ce0aaa4454e3c9eafee87ff8a98add86913ea16080e37b98262fee06e91c8065830224ac0e9c4985c49060f1fa87a1653c34e52436c09ea54560534611a2e61339dec82b410543cd2ca386d0f5705164aafddcf765e2912c0bbbea40cd27e2e76331387d1fe7fa2c73961ca5b7bc70d4cae4c43de7429d5fd19f4751c875967fcc26a078e69a0f5f21a64a67993e0166b3ec9a9f7989edb7c2974a21536c6903301481bad1b032efc7b786da4aa21cd678796a3676bc92f5e4404bc43ff73b40cae8f278cc5d6c849688b67c6488a6910398df11aa80083b46c4587ab977b2ee8299d170529f5cd253af904267b5539383c35551d88e06e0123d335a691f1baa6d53641ac5868b665d38d230eb39f89d40abf306a07bbe2ec74346f817458106cf8e593c0cd3a9d1e6d7a681e67a6b74faccbb749de2e4b3ea439bf0d67a7252fc79ee9c7b6b1d6b91dd438bec690b2ec9b7e879274cc0c585478d18938bb48569296e62cbc55ab7b7e4c961e68a869ba62a0da38d6ac2843c40be15d0475f17233e44fac319a7832a16de387d8efcc548cf07a704012b826ac2dbddc43108666de60f935b5836f9dce1d118a3f52322c4ec111578db5f8319d671a1297b607f10176f16f9d2e7655d6e217aa83b401ce4a1e2206b38124f1c4686d3f9e07693c054aa79644cbfc3283e516b2faea1e56e4e10d7d57aebf0768af421959d7664066a9ebab0f1b89fbf0ed4b0bc8e8565ccab056df84b9332747d5ab2a1c62fc467d3473f0a52da451b64f2385bb66199e2a8dc00280fd4c7cb956d9ff36facbe374ab76812691054d268a0e12813d3676f5ccb2217c49592654951a1631433492fe4064350d2425022a949bb5ce5f97ced883ce63e46185329fcc886d9c85da35bd0792558487095211e3ff4e1b4a0b3bc05501dc9bbe4114a4480a457da012ef69fcfc381473f56a50d0f9589644eacce6352a0c3d3edf3400f0a1129aed098a2a66078451f7cfda869abfa72f4800196a75a8a8cbd5b5b270b008d1840d5f5bd152dc92432f2892d3840cf134e8bbe64b819d6c3dee11423ff68a05506d707b215176d8ce8cf1c6920e4837f7a814ab523703e3510b37089eaa764b443e769d58dfb9e92fa27314f492fb6328b1b5de241c531906c64410e75031f304744bf7bb75e91f782bc00f11edbc22dd06cea927e11da89770eb7e98c6a65b5726b2c7c50902336d8675e65e4ac2613a3d5924b102601e3e32a9d24b8c4ea00e6f46eceb50fef3d204d253f35a3c5d9a40e854c8f4c3ea7bbabb9d388f397acf26ece5243627a4315d9019b35832528591bf2b29f5f4dd250785d52da6059e64e127590fd68739104c5cc227e065abc3b09f3c26071516fe64d81d8d78ea9e5844b2b2e1df198062be120ed26ba39506bcb406b4103855a0e483bda8ae858123e6eb76cea6213cf6f2b2de49b24cf32cb5f4452dc79eaa48c2874192927d52cedabc2a54f373187cb35cc3d8960d289a7c4afea636edd9dfa904138e522d7fc3e490b3406f8074b9f85b455404462712704d7bc1ccc678bba1bab425612663ba4060441703863e5c6841dfb8ea653b76c4fb73b20778e726f9f7293cc1bd27a7e216e7fc03b2779050952331a99377f42b163e1fdc44fff845d62ecd47f2c39130f4bfdb72cebd948b05c168817de28fa8fe46165df1166522c8007a6ad72a95c6e8be6f619118f4c6b874d0ee3d1c006930da934697f11c6f3c9accb00fec80511eb5038a0507c0e954cb2f53d3b1d9cc9d42b7896e8b194a765d1a3d7bd67fd637dc49c50c860379dd322d750e409eadb82470664062faef63fd993f2c3cc948b1951b81e39b8e4e6cfcff8ef6bb0c0a24087f749f118bf0a83033893a10a7c348f819e684db23f43085800a7bb2181f18555e26b2bfc65993f55f69afc457a87a4bc9f9cfca85b37e0800b0b33983798b5c9410c0b44f6eb720a86695e0bc7e035ecba9347646b05b67dbd8559e9b90d0d0b04fc047d4d2f81af9dc0852b1d0ec1e9fa1106f932fb40f3535c9126432561ce5a134280aeb01ef7eb7ce7e300cc6cb7f4681ff6a425495100b4d989ca2a5350df1bf78256d571925ec7fdbd31a5a9279b2e7604ab5625091b5c72d2f7116d54d81f9ef5a6ab67ea891b39478f086eee6af5a0fa51d5688ad1063d739c149817bba1eb5af3ec6d541040366df555d16d9a1191c9689adef50f7dd6ed61eb5c5320946386d8ec43d2f4211a63f60950ac544f86a140bdaea384b6bb0a84729548ecec2b30ba2b7c1e58a6e69985373f3403b13586099a61c361409d0c8973b2e77f0ac3432d29f93b40db701d5a418a3fd4e5139f1ddc5bac72c3286a1e491b14702035433535bddde623ecdc0bcbcc095e82d43c6d1f4f4fabd71f31f3f57af7a0ed40c2c26b8de036f402c89c353989266aa44693dd9f3ac1e55febe405ba1eea7d7e66b9891aba3baa93e7e86b49295deca8c35f7e2ae21e50cf345ef322b08ec098b98a8e3de2d56f35bda8e61dadcdecdd70067105becd685cbb94658f302c12cb43278718952d245aa6226073c9a189889b6ddc7c372baedb298f08f3f3c25365986be05bd66fedab19f6ffb004c62c61258f08c2a6d7540646456db177aa05f1eabc68becf1a250ddce8f0d9f7614226e32e6a8b345248279f407b6f29f6a014744bdebee09a0a340464110569c36730abe1b7d7ffe2320424b1b8fa3a5e1521ba3effa008ba0a119ec007678357dd40e11531c7d55e78899be66fe1e1c7e0c37a30e7e6b100cfc70aef722617a82a29afe5aa03425565119df9e59f01c8b7fca39511cb10cc739a01a0d9776cd2612511c88e7d61267e8c71cd9276fd1a546102f1322c7cfa75501f9b0d46982ff1b6a3414f40725474cd8cc89498d049ea6f4e4b11f1ec62fa27eceb79d0ab9464bb0fc46c0660ddf3c19299aaf4039aa5c7d56780de3c411ee765c0168aa00ee2f5f92049038e0d93a7c53bf69e3f248bbe0982c61fec927041ec71597cde3a6d1dc9dc97b860e5f28e0f1ad8a233ea2329030e81b7f8d0f74299c80bab7730dc262b54ebb3c7e775adf3b4e27eeea6353f2f2c9a9e70f57f734d66689376f6baf040ee407d3024053409ce2abb827e798076251d1d72eda8f781653e9b5be618296c5b7ca6aacb38c50b11bed0fb891ae8476f7f8e96ea12376d9ff0ad6b03a381176b2b5bbce7898475551dedac931fc3ff63ceec567ec6c500ed80fe4b9214cef61fa9ce431c46b6c1fa77b7cb39d68732f0ea59faa5b3b210dd1cb6a05129fd07639a8f45440b029b056aafeb5f6abf75a9dd0820c7b169a55b63959d028763d699a6e9d8bbf3641374c1099972548bd7bc4960a32b1db6b154aaea0cc9638fc9e779a94a35583e14495814c3aeaa6cfd72f233fea7904ece4f6e4aba4ee3029f546b08ff05dafe4db7c774a3f844a28e61ef34d2370ea18b5752812e2fec2bba8303687cace6e240e27713f7b1e118185c66f384f7a3f7468a42e580d394bb9972747db4c4a85da690c06d1e39eaee043ba1dc0bf8d5505c9ed3ab08fd998f2ae06814973075785f9c371aededa3c12905f7a1adf2732f3c947e766f7b4e3e8d71a771d5a3bd20a228b2a3f5e03fbc48764e97b15895bbbbb9e6859ae8ec3e25297b78d9ac2d9a5a5bb97cab8dbad7321937e78834b71ff597b8e62f383140681ae4999e401d7383fe3778df708396352d2ceb22a52e5b3849ee42d55ed8cf3c4cf341a20a169978f3286b1720e94f6dff9109756791dca1407e1b95c37a26c2c14ce302e4f2b3139cb1513929be336a559e4efc0eb06e8bac991b71968d4c8f8ec5966e3153544ff1c8197a7e9a0ab5278e914840568feb8ec56c2790e106266c7da87f4bda31a71e6d9e04e3da051e1e55cf5e368fe770255ab976d44e65d27a2c8f65073d1bb36513bf237e587a47e330519c77c93caa360f33577cfd48e8e28c740b9ac445038bd4de8678a10eaea4f3ccab707373b494ca675af299b6f08d7cc40f1974c4084aa050eea4241e150a43535c30cb342bca1dcda02b43f85a79b35d2117f149843cb1b0ddd809c9684e0750a77a67a647a3350c018ebbc06f2b097880b2b446b0491790c562ec6800f91de0bcee536221dddc422f97979d16c78e0b230f0c117ad74b365689053a6a65505d791ded8cf0333c159b81397cedd457e5da7a707e1592a44341ecc8e2a990a9fc5b5bd7aeb3e79a139920290a7cbf2d25ce11697a72ce3dafb4c5e8a3775cd99e9052d856351f3d843ef5d0380b9acecfc6ff92a54ca7b19fe9252eacec2671ce376e4761a4ec5e58827cba4786df963da71434fd37ddc0778768ec1595341053eaab3e1fab9864b6b211458e7130e181f0406fe8c6e12257a0ca09c3c31f507860852b5d10f81f85e622d26a9bfeaa54b52551c1ac4d9a4dcf4b63b7f6285b7be91a22082f045ef9ddbdc155091c29564dfea111fc4c5cfe81cf3a027365e5ffe85ae8f92e7de1270ffbd4088abf1314c74c2abb0a2affd4b25cb3aa0652c74cd05c622c4bab665888087907f68b0a99850a36062dbee98a831730a8553f85f5790a91d12998569f5eae4f9f019082cdde3e6d7b0266ebc798a6e7df5b86100a9582a9f1f87b1352a870ab9516c87a89f96aba42b17a7a88b4a826250c4506d51f4383aaeea101bcc4bc3f8d916ea3b066c7ec61128d928c148d58ffb028722bbdaf854a7d5cbb4474a8a62cb4b851ccc274c04157864c0cbdb8437eb78049b13eaf6dfacdd816d4090ca618d921386af3534feabe97e1a6466a02b998028b63c50eb322c166055e748503accc11338109793197305ba5481f03fc9a52ac4aae8dd0a195691e23880ae992cff1dc1e4cbaee1004bbaea5c8a81f21d1054f9115b4c4806d81ec1a548d5f375314288753c35e50da67b855154b970c1038fb297a9d8ddb7b6c04a580a2b3adaf912b8aff9868dc360303f8585ad4bcda78b31235801148bb46ade5bc96a8d746346840801635e1c1c6484872c581a98b43988bd4ff475318dcf9a6c306bf8c789e6e1a3bf55e965f4960b57fb7306a211705baa7ca3c0124161ad2942bcd6d4fdd8a8f1608096cb80d3a378e5814fc4e40d59fb87358d99ffbe01271042a0b0d8a41d543c8ac7aaa846e7bbdc74b4d77014339e033eb64bfad28b5cbff2d8e32057e9d687e7b83a4fb151278bf87c97ea0eb72891d324849e43a8cb78eaf31a92c77a649d534795a6948e2f357e2c4a11e90192497d206421a2c75a9a7fc20e282344da39a7d4c0da392d95e394b89a0052bbc136bf86531658d78464da3f695d13d329e298c0d2b4db22983f7b1bf3d41eddd089e8e45ecf282344f708b6ea5b9298d1fd2c1f907e71c9817a1278f2a4d83bc2bdc388c91bb64d0baa1bcb2d44c34e82d743ed2dda345489f366f340f71e7aa61e0831907e9cb65ab303432f79c4815a413de97f63a3d31870d0df3a4827b47ab5a0690a2f901a858d57fac65ce3eaa314b6fcbb83e4ee8d88c00dd7eabd77e233e66a330c3daad1537b7974b4e780e208e3778e1ded45bb1871aab49c7e4d5bf3ed2afeb72e51fc732fe81503795c5f99d4ca65cba80dcd7586f90f32735f1f10887e2a3f6d5b2020dc26c8d293f6a8f01188912392cbf6a8816109ac3e75b467a9314052bb6e28a678e519a96370e2bb215725e5652319780b128c21c8111e9ca0e4119700d63c948cd3a05c119c64c0114ff03ea3457a25a3092324a8b0134ae13e1fb72561459a6accf8ae8f1105e11b721075c95808ee17528429008046fa1812fe0ce18617c993a2ee67a87c7345e630c16308697956e7cc491e900f29ada44fed62ffaadb5269288904d441289ecee1d0d0a3d09e10a9bead2d67530a9948ed11f0ceaa4bf54d6dfb76ddbf4d7b332795fcfcac8de6666beb76fba63d31d7b4bed2dff444d265df2366a644557db6664d373ab4dbf6ddbd6b3ea59adaa0f900f91910d6fb30274d0e265a5001db4f000060535db2dd18ed2efb42bc7e9af473bce845802777baca6c359aa1d7719ee547fa7cd6ddbed396d56439df229758b3ad519e7c7c0dce6d376ee18d59d6a5b0b236c11f86cedf56a7a3bfdb4fc2e6fbfdb6b8d18857aaa551de7a9ba6be7b6add3bfdfb84db59b394ef79cf6cdbf9a27bfd33c58f37a4e9bee39ed8c710a150373429d4edbb95feeb4719c76bf9d3b6d57e36cce1a11130bc9b676cecb5ef2be9e5d3299aec9fb4edc6947d761ed329fa64e7f399bea4dfaeb396997d3b843cc719ddebcafc76453faf2deb6edf532a6d2653cbb795ffef5ce6b21ee92a679386e9b6ed25fcf69739db6bd7ada4b5fd63d278de3ae71dbabf7f5ecfacd2381289ff3beb839ee1aa7914094edfe7a4e5fcf091f47668f80919e0d6f332c83091db4e839ed1662194ce830839a521077e29fbc0fc93e954e4f6929733aeb0fc9a6fae3be1d67defb9c5ffae52a9ff34e3cf891e67c8825e043697e29cb15d69fcfc687270fee2d1e1e7b9f8f840cc8b04808caa6d7e0d8f430f5d2dbf9f1c09cbeea4fe650a2a05ddac49c764f9dea0f75285754c3f0e4dfea5930c473686cee31616c7aed3bf7140be15ef51782bc5b5041995540e0c65a753a4fe9f8278d5fa2c799d5c3bfde579a40f7d3277ebee5d120a623d9a5478c423ed5aad355a7e37325d5490381f7a7fbd3091f7b1f12fa957e0189bbfbea4d9a842632be90ee58f3743f699e9873c791252f09dd9c4e4277a7694c9078ee11efc46828a393ccadbd9ed35f4a53944e3237cc2bcccdbfd9e33c2a5739736097e6865808dd14850fff4b32378cfe96584dc33eb2532173534ed3244f32f3e7b32337a5f8945e1341e56a36b9cfae05ee641b3f7bd9b9897d64bfa55369435fba953d74bcf45bf2bebceb04ba97b667933475f55d3d7dd5e7be791a3e8edcaec3536de7a12f7dd3f4a5534df549e7ec7163cd93fdee64f73893cb87bf28601b3ff3f221ce8721c6261916e520c5becf25cd79d5fb90a8be1dca1e3a5eaff2a0a439e9cf6e8ebb0e0f6796bc16e28679f53ed42fac797290b89381c464af5ee91dcc4d5ce9f5a5d7671acad5e9f8311e4f4953b93ac157bd9df34c87f1bae34c93f7a1b4bdde778f2fc414805b75faf6d331f79297bd0fc9ce3f79dcebb9d79bf0378f87a33bff28bd2b7970675f12da791f937d55dc79eaf3394d025156edd4e773cf9aca553dd650aeb84d7f4cf63dd40ee3ce3e9f7d7576fa0df98e7c48363cb4d2065e8b5a34fefcfc8400c94e3237de41edf81869f2a156ebb2744e7b599526511f7ab0e1445207f9fa0448afe0ec5189264dbccd71841176ec42347b7066409af834768c41d8f1f1403b725ed2b8f69721d52e301b13c955fc8ea10c238439687ee1e69c538321c41bdc4f05aa3f3bca53a26fbef04938241b120d29c3903f93e6079640230cf1fa9934f1f6b3a9efd7cf8ebff445b4e3efa6040c3be217768c55d01002da86605d3fb387b645d2c487014446101a3b4636768c55763cda5ff643f403b5a7d68ee42a5e87930c4fbd09e7f6f99cfbb98034a41d2da2c842dc449047006ac8f09f45a445273e74ce39a9e74686fff28e70d3b8c4858fd267df49b5fa43e529d0feeacf8e3f186a8e0cffd51f4ae98face1c2c319b8279487704eaf8d0c1f6f9057fe723a57054cdfdc4c9aa8834ffbbbb991dbc43a9c2a05ecf62ac09c2631581dc3a5e17d36c53a79d7182bd6e9525bac1ae48f6e5317f9b33aa7cda980c02daf03b7d43edb72c911ebc48deda9b1283168b36d1e47cacfebc8b8abfe46b0a986aa9d6cc72df537820d290dee8f38477d8c52d6cf3927a594ead45d5f6badb5621d6b4bcfb22ccbb2763d25a24448483a05837c9ad7a97a6aa9553bf3f23a53d71fedb371a40f8d9712576054ab88d4a8c5942772ea7d72326a9d661eb44112246d187d707e6a37f29753f3a9554d0ab2460507f983463713670531e80651aa00f2217f46f638499a7848638c29d041d472642e8adca920c37751f20e976635f35444323869a8a7b2f7c9819a56fdb0ae2a32afaa88ccbe24d24aebf5115bbd23b3eb2d4fb593daf5a98d2379b0577bcbfb92c88d69d54eb5ba05b979acc3221c9052dbf260110e3f1b496a6769452aef7ac94feb754ef929cf33eb65c913151d1caa229ed4c5914d91c1c8ca289de040c86270393237361bb20db42e3dc8de507d2103e58b19468eb7ae5b9752e632727cce564f6ad7ea04bcfea8944eae6c0925474a287bd00853c6c55eb58a48bd5eb5ec965611b16e2d9f1c28032b19910c9e900114488390ad3684fc595b5454c415a07cb244504cd124b5238dfca568004a0d5da1e1e7e80a0d53ac5c791274054a179f09c00bf9bb3664650bd10d70ac0ad046fe56f927890a1448402451b30020e4af06fbc1c1cb3dc2418e1b13bee4ef66c76bd7033269e23b39f207ada091c50a328660984759ae48c962050c8610e941ce3c2347264d7c1645fe60970e4c249326fe69e40f7aa94955e812589440891bf9835fd438e24aaa420d2a44550ca9c1a58a34befc3449ca5ffce16125882f80a0c491bf080406104604c60f05634ab56283a02fb604458941f428088cfdc91cd810922247fea250152b20da7eaac041cb4caa620933b8a12a9800aa228a0a86a89c91bf3854146586a8080a1dbae2091c74c51757c6a872c41bf98b55c290392ae2614b52110f6a2015e92045110f44103511896492440a22c922a2e2e40929027085fb9162fe5841e528490a1c560411b4e55552472b3b5e8956a2513c22237fd1287ab9f283263b3e26491b981d2f855248910d1fc081b46304929f0c9252768cf174cad8858cd845da448b884a035c5167adb1c63967aa099eb0d65aeb4ccd14bc200f1176d74587fca5523a409b4a456808299b62919f73ce69cd4a67a59615452833c292e1a4c1b022664dba1a0069dc9725a5ac434852638d379e847d721f61d7c46c10131cd275ddd8582ce5bfd77a5dd775d92d5df8b2af47efbb3a76c8f190a802d52994bf9b1b13e08d94211b84d015c0dd72a95c2bf789abe556b94d402b91a83a717faacc1ef70639b19f6448b333da48bb338225409d7591ab2b64e50a5da1392d2eec0f768461f1287681318cf82526c926110983f791eaefd84d07431017d062b9a34797f518643192086fa611188f4eb847474719667feed1159415cb42423ad2241e993d208c1b84237324ec981d2d21ccb38634124be86a828d2f3549ae2c2943969014992200c7d6a45b3f50019186200feb077b745942479690ad7204153f6843502841849095464d7af2420d8286b12624a40545119282b2b2b93184619462dac5b4eb255bab5e31c85ffc99bfb1b22d734e6b5a37b3f46a95b3552e3591bfabe56a912bccde2a570b0b518bb48933c80aa217b3d5ca6e66d92aadccb26a26a404113feca02a414454a1020a256c161445480afec2020c8b715e167b9131ab629366d76a580c5f92f975b720d5f24d9a07e65b8fb6376d676d4bddd36f3127d445a1e6d4baa9a1b0cfd97198a69d4b623a9536a89b6efaa52a93e99d76eae83b9d92ab9277333dca2b9d67be7b49cf779fbac326ad711b87619cb6936c5ac2999d95c160d92b6ddde93bed301e4f67c2dfbcd2515ece9edc25ef3be92bfbb2ee5a673abee99a573af5b62fc9e66ef24820ca9b76ecf1603ddace3e263be3360c35ef5ba1034968115c8ae8428b45702982cb8e9731d2d40288747ff810cd530a4b93cb3e442a4efff021f2218247f028a396e6f9cce90f1e71c799390eedee9c478ff3f175e8e0674c67c647794e1f2278e443048fa60f110c323c4ac1207f3df0081e31096282e443b4e1a195d1524613f048464b194c6c5961df5fee94f3cc0adb462badb4d27a32d53ad333d3338342a5626050cfc7b8442bf78a8574bf9fd974a31bdde846b74e43badde47df8f4fb99bd2fe7bc02973d15c618e31576fed5df0abb7ec32753cfcce99be639fdf6ccccec53a9b4c5c0a050a88ccadb763754deeeb6c2e676ec7ada55dbdf0c94ab1f94d20e08f70dcb545a42e9d38c69a667a66726afb06738ee74e2ea7742fdc4715cc984fae69d8ef2764ea6154c25dd33b342cf4ccf8c4f940d9f58e1260d1d18cddca4a1032c5038dcd296bf6f3c279dfac3e78e3331ee4ca652b7e19c655f920aa966b2a61814b439ad093f3bf62ac63f4d9a1deb2f04df2de8d8f8721ad34ffc5c3af799795f56677764e3b9659e6a3bf7ccda2f6f9cffa5b66ad340e0e6f0919d610b4766de97bdcb74925a4ba5739ae7744cbd2fd349ea2e697ac2a7187a0c83326926133665189b32fc8ba1b5d0528bb1877df3be237bb3ddd49f0f84f666cf2c81bc9d4b9d83f07438b3d6290f421afa0ce3438cf108230000002288b063874aa543878c4c2a1513030383429d4e2653a9d4753973dcb661ac69f76619b629b667a773be4ecfffea9e2f1d42eb64d29fb5e7b76b4fda79f48336f93893725e0b71678cf1e1f5be237b624d3fbdfb7c2ba6f8d3fb8e9040946f927abd2475d324d56768439c233b2138b2935008cc31c71c324240b22f47b75a856cd5bb34e773f9100bd970128a21b5d06a266873ced7a8fe5e85dce36f55ab38bd73357ec9fbae29e3d23312fceca9badf674ed5e91d8df596bd2fbf944dc79ac7f44ef368f792d09d93d06dca5a8e8139a14ea7edb4ddedb4dd4edba5d3f9d2bd3a6f7abb10eef7bad79bf4a497f1a8a4e128ed72f5bebc91eced5066ab1bcedcbc2f3b7df34c3f793b260de50a6fe73aef43b2f1abd7bdd4795f86bd6ff370e7ed601d773d57390f0fd9f0081218998fb467dbd7a35d8873cfa69bccddaba765dbd4be9ed3c974611f9829e6744b8731bdfbcd5ec5df5ebb4f2c847bf7ec7da79df32fa5dfeabb4d77dd044adf1e4b34dfa2f4f756ad06c9be51efeb4121bf76a7bf9d87bfe1cedb20fea1bdebd9d13a249beb7ed1d2b38985944e354fe99be6b9a7efbc9ed357d234ee5c0d298ce9a4bfb84d2653a6b9336dd807d66d987623dee93c8c72f547d69f4ff568ad9283f4d9bbcfb347eab9569310fc14c6b00bef60197cc59dc7a33d7bb7d59297737e7d2a25b3feb6c3979ef238ceeb39edee58fbec3a6fe3e0dee95e3ac4d533e9af077a3adcf38ea679d9314fd3dbb34eee9add76875827734c6acfe96aec11e8a914254f425c38f37b7f754cfdd1631fdc69c6cd99e96f63efeba940d9ac182b6dcf9b45a1f4a9b3f6d3a7fe4cd9e9b3e3dfcccbb2d2bcf4990822be33bbdab94f9ae1d7d9dd852f28e04fade23e9f9d7a2a15a75350e467cfbf99f7b930adacd3da33cda36515e8a35ae30c553a7d76326d25bd65dbcdac8cbb956dfa5bb277260a37e3329b33766dce8e5d8ebb3a727ec43ff48735ce827677ec71a79ed6e51414993e6b8d5dcd833dc37476b199659f94e2530f9fb6b0ddba9a673e7bc4415cf8b2bb77faeba9403e98de530f3f7b9cdcd8fb367d655f7611f84cef73c14b4191273d3ecec4be1e7bec7d4c5250e4afe763b2b343cd07c38e33b10f89971124b9f065c3db3cf7ce8c5274a502cd284556ec781f54e45ec0f63019cc32ec59ea59cc33d4b3532683c9a05027cd633f9f82c9322c068561319f8f41a1accf84f1be2ab5d0cff9d47c8c857dd8791973114e310aa94f6de98e4ffda98e854c1d637a774d5efdbddeb6957ea976bded269d38078edcbcefb43715a77f987ebfe5e3ebe42ccb32d7c18ed2b4a9c9e81deca863a83ba9c99b877802a7738738727e7b9df330f3a920d8bb578cc2867ae7a9e6b377a5926aea1fa897f4d7b34b9aa665ea656daa00701d1d97319926ea2600e81d1d97b90ed465327a11d0ee53c99ed741e94c7f3dbb9b150389d8290612f7e99df7d1639a8426f29c58c8e953f39cce691e98cf97bc8f9e36980923824e3277eaf4537f3b3455c5c41df38fc99ebfd7bbf36ef3c6ccd339efa63f3a2f9573dacf53fd25997b4e22f2c484643bbb84a97fa68d7a87429d4a9b98a36ea58dcc51d7a40dcc51cfb084d351bf57d3280fe3f9a9bf9e8d7faf56ddab76ba73d7e934e773cf5de79ebbcff638cf831dd67188633e371dbf5ab5138238c73e7dea4c7f3fb57693e6c99d1d476ad101f412f579192fa551d4ebe0014c9fa68dd2df29c6a372653a8ca7c995e9272f431ea6534a7faf4335843c34843c7c36c6d7d934d43e7bbe3bf5bef998d2218e31bd4b6dd3b992de81f93c8ce926adda397d5e04539b7ed259b5933faf63fad427b932d91d9c79ad16b1108b1dc341b09d1d66ded5c99e3f31eaa2f44ef6fc2c63cc651e76abb5e348ea6dc79918767b29e77da7cd1d4a9bed1d97371d61846f237850d2607dfaf58bfacc348f3d86d9633f502a7cd58ee9dd754c9df6c1efae83df75389bb2632fceb1212e956829d33bf8f4f876185f045867c8a3d35fcfc5d7876ae7ba536fbbde77d23da7ad13e7d83e38ade2ae53d210f2281d6b087998eec31d5f873bbecfa6fb729ec92be55355d63bdce9390de58a6aaddaa1e72e024e67c8e39eea0c79604eab76b6d3eb6c58d31f938d1d9eea9e938fa598b6d6eb31e2a34e3a4590de96cbfd7a9f917d7f6dfe571bad557f46b6fa8d7bdc4ca7ec54daeabb5a2bc7ad38985a310c2a1573fa7bb7eda87a9855bdfaeb59dd6fdbb66df7f5f7f574bbda767aebd5087dd5aad2ebb5739eaaf4eedcb67d6f25adbd7b77ed9b91cdd5bb6d9a275fd33ca86fc7919cee59edac296a7bc43b288de937fda534a530a753dd7e378fdbeaa6d5ad9ed6ca6db46ef5b4d65a6b35799f916d42fdcba71f5c8dbdc956024396104e74f082285eb0903842891886ce885fb608028d25a604d1052867eaa00738d8df6bea19359fa163d026506678e10235c901a80c0c08402778c3e80470c430c6d096a12d2a098e0883882744a4000756681b160d5931f444957b64d7df468b52ec9867310c420827c491503fc1116df800c830e79c73ce392777845c41ae8a5cc15318dc1823dc734e19f7ac357a71436913bd146a8e755d5a09931c4bc34943e7949933b82ce8401674588831859a1329a5147a50de421ed3935a09939ca8e1a499524a4129bd02094984c5a2a91d72562d6f1a638c96f73db10f59ad47a594b2c6e983cb524ae965adb5f6f2be69bdc53eec7549cd421c69299db3022591e5a3fca8b630ca5a449cd95c4a9b7bce393569f3e51dad6f0aab47f7c43e628c71462f4a2d11e9a2944e3aaf5b3cb10a89948428727c8d3546293ba8d4d66addd6d77ab82dab56ab561e8a34d5885456b960828a9c10b247452f90022315399184a4365fdeb2e54f72c7db94567ab82ba5946a242b74d041072952a440a83dc9d4aab56aa161d75a6ba5da5a6b6a012912d90ccb42828522578eb496b41eadaa772c6512657422b38324ba208496452febda0c5f4c3bc47066efbdd7db9906b67dc434b8809b9a1a2c4776ce19259dd56a8708f03419ad26d7d4cc7b8a9d66bf12c618e976358d2da0a686ca49b33a610af6c83462286f86d17da3830938379ab575095aff51086badb55a2e1ffdcc8673ce3977540fcaa4b68536f386a449e93c9dd45e36d3ea8c9f811990f9ff4070c8131802e464942d0da48b5c6990f98b26e1681541e79cb55ab43ac95020a5527d23a5bed24f092d03424b4e3aa9a5cdd8592fcb66e7ee17b0610136c7f5c89ca67275b369acddc86002ce8d091ece4d1cd9c255bda5748bef871f7cd0a489298cb8c841cd62c5942c6b6859e6a8214b0e4a63f890038c0f50d800ba41961eb02c42f480640a42147b5116c44d6b9c8fd6552d4d6dad54f26062b652a91e0657d694d7efb49087756c5b24884546c8c1b455022ad1b016270239f555cd8db59346083a212e32428ea521a4c9567260af47fdd5cbce6a59d34648e50eb05f979439ea0fbb757b1d041b3bf5be9b4d79b0cfe78a77f0ce754ccf9debf3caf1b9984439b15c0b616ad2705c8cc810dacc7f32c61919e3bc5cc1e823a5ecc185333060f5776a95d24cbb2c9dd6b10c07c19edd560cb3b49d564e41e6683a75039db5feba2e1d6115252e778b6587df306073380d6d096c84ab6a822d8bd866372fc20e0412efe49823f705a909a2a561a553ca4cd21764a1c16659015016636829b0b2f881bd020e19c0207a03cb0d7400258a264859166ee47080296958244c460683d596da4a4e9a2b564b18de6f866c5a7f63e99b9b239bd2d5ece143035fe4efc646e26820c8c9ce54cace23150548b2c000053b201114c404c5411643e02c9a684116376459489901aaf163032a8670a30c28a61003cb179bc5cf0d53b6f06001e005598b0f5738f1c14a12d4b21b16f9f0c4946300550c2186175cb0a1c40f39561c588850588aa018e9005301162eae111a2a40c550050cc8e0228320316e80654ac512040d3e44cc6089278080638a2ba28461340786050887ff079822c5cd07ad5c99e2449d628485491396700d007a1dbc3141f660927f7e7e5e98aff3d63ca4968473cacf5a35b387d573c89c9a26d120209149a171d94ee0443d819111aec18a9acf741b164d91617fafc1686054f3190ac6189719327cf5999f7e8119f9f0c4a84a7d02c6fe2ec445bfe8418e316e3802055b3871c3141c98f0c18a132092a00cc1839c69f499bbc6fe5e63fdd0830ee6849f3a4a2d238453c6d9c4674f2a3fa5256dd0d089c32d3dba2f0725dda4869047951edd3e35c6254a5c1861bdf1e43e81c5b559f350e9f94c2652462e96cfbe30c250393b7bcc43f948a9a638c80f9fafdbd6eba7273deb36080e02826dfd1292378f55e5961eccf1b1984819e730c27aa386a4141cb3a2c6b461d114217945cd0556c04697285ad0240e39640d56502c78b0c6b8cc9021c31e90b847c4b041193d0c6911522267da4088ab46dbb0a8871d5c4027cfd4390251684540ca77c3a21e6cd0f677a2790df9640419f2b56191143474f0240958e9161b92c8f5146ae1a488093852cf27127481c50dc818830d2138a15db08882f550434ee553ce4eb235673e3dd95fde3487cc064b830dd9c6d8244b065c77f21c78cd1e4269436fd56ba33de26dc397d24a69a514b392c6cb7265adb55a851deea927f681e91b4c35dd92e3219436d7fef2ecef757ddfd7758500c9ce6e3766ed2f4c7f482ecf524da58d2657d72fada2877b7e05a058da1bae8c92d21833894d4b35cb823ce0a494ded2f456cbaa17b5139359ac1966ef16ab94125b22628c90523b69e2cc8efa1963d4288c931291e40508724491618b1f6c109b54c0035388c1e5871abab061092f54907286005c71849417ae78128610b40b1520264616423120c3881fb441b9a88086941ddc30c58735ac70d952018d8827b8606963871cbca8c18e204881c30d6ac0831aa6d040837475ba9258186129e1a496619ffcd55bd57f9fa9af61502427fb0a2c3fc89c805234450beba668174941c22aeae1047bfe246db43d9faa990752dab0480a0ffbdab0680a16fb7b4d1b1908e38c278038aaf2060e5ca8f0861b2ef832842ab83042ca510f690cb10614353411832d54c8e1270b1b8040c31a66cc40b59028e0e203a436a8e0e2848838665081369468028e2f6004c9e08b960ab810148414429882872f4db2b4b046500f86a862040ebcc8428503a480448d2fae7c89430c242d5854807338d9122506416801079191126eb46028a9054ac481058b0acc1e1401a38925e628220d2f39b84205e8250c379618421c36986307b448081b90e0e1cb0fe27042d40413a221880082110f5ec470c50535354e3001855af1d9a3e78b116de4a0872c57e45053d3c4cc019458060872e04116379041143940c10314f845872b6780620920886cf0658d358e805dccb0bfd48e3fc470024b0e7280e1060d6815220862074140f1421449fcec20a154405291507470011047ec800551a061e413639472021b6b4c11c710a6c41005a542850b732c7c86159036527a32cad873ab088389a42c40a471643444d0507c62c31241354d5c01a307202d3e6872b4e309950b6393ebf6973ba4e7b1bf62d0c48e3beea2185cd959a64d90abb86335d497b49959ed902babe54af3c21e8b720beac19c5b3d2573072b30ca61727fe40f0d43394c4c40c10d5890534f3d25304b0e1378454ed548a8e27d76bc338a0cbce43039801b380441713841500e939a1750308395521281ce38ca61229372a06400b41bf401167254c9a1de9d41cef4e2a6749ed2d56ad24415ac3a3fe7e7fc6a356922a722627f5d057b6d7a799f0d55f5d655530455eb643ad996318e4b7fb9682dd5745a4be9752485514e2a836e84aa1d4a75e49e744626f7e6d3eeb4d27f2e246d55ab5dcdb56a4ddb348c4f59fbcd274cbbfa2c64d33abdb3d1ece5735bcdda2d8d7bd5aaa7da3ad5a6775c48722169d3eeca95d359f39472d0ce3d3548cb30266c3269266dd34cdadd345aab9755a00a4573dc23a7bf4d6f25aeca3e7dc3278fd35f0dda19a842912ba19df5d753836ed5ea21ae75cbdef69c6bd5b6e79e1aa4554df3b6a44dab415b4f0d1a41970d4780e442920d5a00d5201bb49061e619b49bbc2fc93675cf9fefcee92fc99efadb5e3acee4b8774fc915a749ee99cb9e6da77ad3dcd7a3691785a152f774ee976edbcdb4edb45399ced35dbb496befb467ded799368de34a5f92dd3d6214b84fad327d9e7ef354a66bffecee54260d046e153e4f76d37176d333d3352f9e9d34f2d90fd45e569d58502b6570b870e69341bb4e6bafebb2183614852c8661d9b558f40269643c638d2d8fb2136425f1a804d847fda59a5693615c029b58c34140202d224dc33eeab51a698c316669631f7190ebf619f661f5177f4c9da9858431de387a44d4014b13618c372d777308168f8e4cf808b02111d1be68a003a21622a3214062d29196e88068c651b44446d101d10cd924c34164c4492b5dee75d8c77c97ba09226b19fb98cf58476e7a9b991d1ec6a348140fe0d918841793e40a0db98a6c481a19cd904de2913e03f288445e32162f988c994a1d43d07a59d6621a86657902767a1b31cc8c20ec176a5986d9cbaa5c56903652a655afcb5e3b69e6b33cc17e7e5ed6ce69377004960034b1398fcd89659856e293e3d5cc0943832c277dc1e61893f6e5e0113c8cb7f139c6a776cc1ef127be86a80a4bc315c336b1a39568b4c414fb8b4731c63076bc518932f6cd8e611f19a684d0c6b00f6b055116fbba95001f568ffd69bb3e6057a21992d022533abbcc2e6600a68d097b1e71c69e4850b1670b74d8b30540d8f335b028891b8ee061cf39c338234ad2bed18a91ad98b804416f7aac0b31549bde6e61840b683635cd20cfc4c34f2b628a6d804d6f8b4063d35bd807cd330254dac8a7e42fbf92d796524ad951c9f143a095b94f9bce14203ad9df024c8057b4ec0f6e0963534ad928c0fe201c22ec2f36d93446a154c0d85f3c220b1ae10b9b9edee6d457454238d9f4373840860ca1824d6fe488159bbe034866b0e94bc0822f7eba0041091ac206211da4181aa28d2a55b010512922e2079b5aa9c14ace32d852fefcfcfc04514a6996df9c7e08a594c29c4902151db6a4118371e186d45a9e04aa0193e2e5e200c5f620940161c3d5c311a6c40c160f568a7d5d57ddd7755da736e8aa010d56906e908094278a805043051298e2258615f010b4430d12e021a909268638356a2abc2265c8dd0e6c646bc40e1ae4cc04005690392b461842d66880836c1243842a79dbb068873446704186d9b068873960904f1b16f1b0022684260ddc94da25b49c11b361110f6a2c619485c21e4d9c3ce77adc50833d0a40e5a9a77939ea212c7dcd9d55ca21692deb9a506e435838469656e2936364d268e9d136ad4b58ba442e4182887ca69764af9b9a11832e6b5ad6651d47da9435e59571916393ebd69b60cf8c580fc9a4a15e525da49c3509d31dd43b491aeb9727a4f2bdde959a94d7a5975c9fd7e797584b45630d8942f10920385148258f5552ab52a0bf5e64ce5f1a5a3248b952f1430e6009d5dad6758c2d2b465b3d08401ed6b12cd3f0e186e44b0a3267d3d663767f9043f896257411b1ad230ae536c405c4bab4129f1c9c49937947b0a32c1f85e25743276c0b02dbb2563527c012e2b60e015842b512259684756156b366248df51a7982bc653d4e695f883fb084a8435682ecc9cd70acec66d258433a3069ac5b982cf207bf2891369f2d81b4891f615b1600b66559d9fa080018aa305cebf75ac6964b56d5cb0de57d628c41f05e6bcedf287fab264fc84e437f79823c963d4e29ad98595b8363a4226d597520294a9ae55ac027a72665a1813de00a96b0ed28034b908154409819e4af668f3bc4e8c60bc90b523c4746e4d098c016546c2c2591e1907baf2aa2a32447865045a98c548076b7e387c0318e0a206d6051105f76e44cd8d10a348a36f19c1669436fd8f12cece44d3f876cc79173ebf190c855fcbc8ebd8975dba41ee20ac09c7a1cc969f1563796e619b935353ea746ee487346ff38a31d4f331f2ab1a17b7f37ebf28c72764dd3aa14ba0efb92fc4eabb477d84b2f615aa569c7c8d0b02e7ad11129de9b359c56db49730ff1101c59da21675f8204117afb259c56695f42b1ab344d643ba622526f5fb56ac8b6a98ae86a7feb82d0be08bdad5f829d6a153c3dcc295297d81f12bede8b61401ef7d5fba4b9a7f7a81d722ce190b35fecf651fe78ab4973bf7970d268faabd9f71a91333873354f4ca2725c38f3554a29a5358a3c06af0a3d28b3b19db1ee514a29a594424217a594526bb512263997168a32ca58d3b8f091524ae1a4796243017b54a14ac445152c3b0e51115fa3481bf9f8d1ec78ccc21252de8e325fb7a30521847456a1d9038334f01e8c431589a201e7bf6efe337df584f96f08ed963784c4b05f8ae5b15bdef4806ccebcf59a38b9c9991ac8e660f166f7224340b5f86476605a3a57d8d44a7c723e696e70260d85d922f7481b7afa0e481b95459f9a534af925988a058b051ff5432ce6c19cabd25bf457a5d691e44c4f65e9215facb7922bfa1944e5f5037aebf7d78b64b7f412ebd92fbd040922d6b32fc9510db9aeab8a5841e28e8f92888c71cf4b3cc43ec6214da684716ef3f254f3d7b117b16ef5126d7d09f6eb30a77240ae689e3414bb6e2c3b6968078ed82429291093ae105fd800ec01e111584295d963deb22ccbd22ceb9af52f12c978914d8f790cda4d8f013f46ab6a8c5655ad4a41bbe944b45a7d72ea7553758d19d93a9c34d79149637dd08b11192f32fee9d1872a054d331d7f89ea7a4c114c63b4e9f32a22a6cf9bb49d18c39eeabab07f35fbb76d08b3e161ec511337c4d7744dabb04bcab0efd4ac2777e65d5b621faa21d8f489ef956822f826ad641e3ff3ecb6715f48b0a7c28ee92130076543bea47561d7be98779dbb760c70d7b4ca5ebb4f0e0636d3b513315dd34a90b057d2dd74ab8994f24bf789d8470882c46daf02ccd17ef9b830cfda9876596c88c9543a8ec45444e8354d645e8569550a31d75e24e6da979c1ef322f83087d130e7f2eabe9aa762210441e2beae02cc81390b3eb00f81b96ac8e92a55915b18cfc88c54bc5845aeacdb98e3e348cd535da7c3bc8876945e82ba7618ed4b607e3accc1282d06e69483359369d2925e32afc17d599af56c59c79d750e685b9665a952c0d79058428fb5ea2aa27dc9bca655974ec99575eba733f2edfe609714604e13fafc63c98c446fa71919e6a886d4ab8858bff2fa8d5ab44eafab5e7703f0d83f1923b004994d8f6d6aad56e293939a34d49a1e10095383fcc12ed2269eea900123c7294fa39c3dc02c5d87e60c5cc9cf0a65cf3927016098f7f37a8c2f20c016b28e33af2f35802dbf2a14ad1db5c708c396b71eab1231462f220c5f5402f29855a85a1aeba03d8f21d52b282b9ae6b29cdd0c675996116ded8a1cb7a6e19bb9b8698cd1b87fc8470bb332eb5aaf4f608f3cabd5802d6fa1acec3c7b542b5836230752ac63b77db532ad95c18cebd56aee0be421b38e28cf7f595293c9052169cb5338c4458ba5a6e68ab53536d70bb275fa8199cd33af69f6f45fcfccb59e8d41be0eb7ac74c3d9835a212dc41d35d51f98d955ff9033dc3532dcf3baa4999527f27529e4d21fc8acc468c50865c348639458b66855101629269434a7bcec6f87bc54d775236deaae37e1caae04d8b58a50b5644165efd8dfcdaeb45ac10208750bd262a4824aeb2f6bd202a4a4cd577369f32d60d7571e9ccad8df8e5d77b0bf1dfbb3bb565ae56b7d4d01963b1948eb9c734ecbaa5ef8c460861942a88208a12d309011a43ba8246c986a8316226533000080008315002020100a878462b1683cccd234167e14800d7f9a46724e97cb834114a3288a410a114308318018038680cccc147100baa10f89f234fc6b5e17a5c9d3c451df25d19cb17c44a0c8cb2c3d7d608799a59e7acab7870af1cc7434c6449f2a908c8df6332ada62789ef5b619dd533746527f3a9a66793680790ca0ef8a2cd8ead99d4d607979d594c105870be9b6ac540b182229e02428adb26aa49ae0b59ea6bef23a3cea1d88c8fa13cfa321923250dad80b55596d7ee2f131aaf965cbb775a10d2dbf74f2915f96b9fc0aee1af51cee483a5303423ce675cc962c3f160e410635688c5182613be506f85df042c4504ea256021a5689453c96758ddb22e3dd142adfbc6cbbe5cca4dc0de5faaf6b290135159b848743fd97c4ef269a1be299b731a8acb93a8d68e7231ed4040255e08a66708c0d4d914063261f1d47e4bef09630d2b5bfcb3efd8934b56368107d22551f097dbe117de3575bba0b89b978c6ef0728994f08fc3782676922bc81e930d96db3b7d939833146de0a868773ecd286c7d2f164ab97ad7c2a1f88bab8a9da8414fe286016cd4f7df83305154c1ed6c8aa3ff182d33e844e1ee250b072ce09ed2cd51ca2af1585e140d2b88d5c98f5a0b8f68dfa8e33206e811b0e4d63f6be97ea585a07b5203448c214161fa46419caec7d73b829b8f478f4fe26234fcfa8e18a6c068fe5455574070cb2a506f106ac9e74112361038b01d6934e45bc7692e7f3ac3392c18de252250123043493b236bd35548913de19645e49b0c9bfb6643a2af7a2bda0507600e12224c808a0e4ce26e0a35e4e98b813e67b028dfe09efb4ae1eed3be03dfc16557c9d81458fd65209134f498b114ab1c1cf74e230b48edd91d64bcd128317243f91f09803f80b19134181a3afc75f14f6f85cc24dd243918e8a032cbe2787c323669bd830c64f345449b82c43a20b098e32aed6fe3cebdfbf9c6048e82d418db3227a3a1084dc83853a61160c491db40d24e50f9012214eb1898aa00c9456a98d95b1196a9e42b044bc464a53942e63e475644ee4b69755df71aab1093106c2c75915f7d614de2ba9867bbffaa3aa09dd358e81b6cb21bb42098de697b91dcc70470784a7535d3db838c0a67258ed8c90b40cfcf244ca004f5ad94376c418c69c7c33e11716890706c0388bd96ad5fcb3711389d6f9a6411f6bb884d45cc1ef4b0ad820e43d1e63ba60fede857e36e56b74af5a2259145cd899e92bfdf4a83d16e405d2c423c5b801d4f98933b96fef0fd05246df76ebe86e84b2663ee20292d459e94a33a681756040e8f81fbc47d2f50625e973ca4073aac978850f1b0e7b753dd3f9e2d32c9a0374d9a7a6e9038f99e307e6ad2a1af24b43ad477d1060d35607c2df4f68f9b4e79638c53d9b6b2b1887a2a631893396d69f4d023da4ad6b6f65f9f0bc6a28ce64e2f4da45e665264db3ec271e816457bfe18fa474af9ce288dc57ee2629cbc81c20de99fc41cb6be0ec3757febc28a6c6bc86a0aad57e2d7ffe357f4419aa6ff2b89e94f42d8f9718cd6949954c655ae649031c77153659d46929d8c1c3ed669109c2c9f4eb7f9803662a5f9470540576f6825a4057576c4fb243faf23a3ba8b8b814bb7ef650751a6e5b22605dc13880812c301c2b827ecc25c39f5571e5b2a44903381bbd6cfeffaf346a3e94dd0b83a647f8407ad623d9f38421fe3e4e0a9465a3afe9cc8c956ee0984816b09019b435953349fd33a3733a85706fcdc70905af3563b7bbe521412738f16e246c2db9ce912d20e833668819930ad0288508c3bb4356dd4d4214738c5b0ce79beb1c17af41d8c1093fc7bdad304374fdfc2355e9d28c7761432ee0ca28855bc7c1530182be5366997e2805c85e3a3959b2cf3f8f24c2efa0c7f5ea9256e2e562bd82400a720a3907e648de4e10a08f874919b7404b992278b506f6d30471cf957b4b5785a2c4295d043fc31b8da15bd54ac8cb7b11cded52dc1d55fcaaaab62c6b4dd16f979b88c87e4985bdf4d76e17390f48be7ee908672403832a3ea4ec95feb3ab51e32cd29ea8d56ed575cbe7f0d017d65a61f4e7d782346e0afdcd15fbebb5eb800bfa54e8994c5a68ce1b2ee415076738327c5fd5399fd96f5ecd460844899e09c2b32355b1d17ac9d6b7f5a44f9bf66b8e6482d6dc5f2f475641a9f4e3e91c633b6e984d6e96972a6be95c13adbadd88e6b5e1be2589430b3b08bde858a73981f97a59d7a471bf0ea75ab9d787fdf4be40c1a2d03e92673b3714f92cbd6605f1ea806374576bc64fd6424aa336184c93016be13463bb3e13bc725b5aeb7983f56ee8fe7ade29a7835e27756027d3d59d6a2104f3e92facaaa07a279522edbd2a910b0404d18d5359271da6ccc904e1d04fca8719b612bc089bd74acec490526183931d9451c18153150805fce57134425c76b240f1e094b1f1c90fd2311eaf8dd9606759a1f70392e06726231b0d530ed595fcf50b01a9224132221101662846709b411f18a7d62df648e666b3028c79519f5b62d034472677b7f91a2c45a73bf09e52e74dcaee675476c18479a369ff4a719156001ecc412634df24c622d55ed9f9f53e9ccb64da069e8178ec42a3a7c016a05b346b67fad6501c4440494db64c8cb71bdae65681ef1cebecec61998f6d31738a5cb63780e92167034d59d1c77137a7471765583f60a6d0133dfcba8016a500264ccfe6c5dc4d762d57db135fa4c501a667da8ed5c0fa0f2b2d943aee22b28afc10542890031a7d31a6a491d12af99869295a794850e4c91929169bfbb8919ad04d4dcbec7608a0f195311256dfaebb6f72447e8e51e445b31769dadd1090fb5a65fb1015f41f9e141de21c592155757bf440522d514c478a1afcdf11536dd0d9b9dabeec3c1d4dd04e9e7b96add719f0d8e138f577b1e3ed77ec8e9c8532893991b9aff5179ea75e0ef39aa2d3ab814e094e65829135527151d5686e7451c8febb286485e46a87345c88bcf98d8e5d738026ea98d43fe8c69fa40d185cc450b6cc44f7f588247c8fce4e501015d7dcbab1436c417e18f8ee1f8c282f249e910cf23ee7d1772f9f878a9e43755cda6573e21fe4280c837dae7f1f2ef9acf86ffedfca1e930e3e812161ab51efcb68ab22b70b93a8fe57bb01e67e88da5db79d68267f8c191c138b9300991c07b5df7a6551dc8644812577a2d32ba1c79eb04c131b840a020cb840dcd1488f2a6eb2f1b088859591630397f5753017f4c5776eae9f07b26c1a60e6ae278f71750de18308de9f357dbf8b1ac31f67a818a793b3579078cc2b4bd531639c7f854897e50a86c16e408b0781753d0827c3b05493cc70698212bffe5b29e3484d89b24d025bbb937b2de8fc04477ac85debf6e549e0a0c59fad351c55ef75c7e8ef8525c3f5a99e339eae6a1a27c2b3a18f2c2b7f206a7308db1d7b79fda1198a4b79b4e53c0761cbc2aa73884880c1948cc5360520f72d1f525d36fa615691703ffbc82f5acc9a4545a7cbb55989e9dc6fedbb091cfa73d163481cf6bb1b5714e40b49a18cbeca296a605d894914daab45056de0133a5ae3acdec821077b5a315d8244188c3740205664da121ba3a57089a6e2fb4a9e8797fe62f0b10555f0e92c94c4df68db6d11c49540e2cc55e38ac3fa8a222814742475e0ee78ac9cb56b88d689645a4665e286cb44a29c62203ff7a107ce94134ebd51670f49d677d3ae8e3a5ae161117739948cb455a81a07c3c08d113781b889720dc2305db37e3515b9157d418770f29722f8da68dd694826493319614431a789f3771d0e67c28cfb99e98e5cd3c22198c07bdd00f4d48c8039de33c4e587efccabdd4f11bdcbe3ce98c18706b3d98ef29c2895e2d18eda2435752ef889c9fa00411379ec80f36b0a84d5566e92b5c719d46b439860d4a00a517e2c5713449fcb9187e2e5988c812dbb95d5257ba3c5f7152ec5e31585858c4e82d2f944e3a60218bc1be25fb82a83fda22ca2b74c40257064b5bd3d3f9d5609f61c4f69224fabf0fcfd83d2fe5ae3ac661eb1443ea50923e907798be478a7d552868cb35ffa9626919526ea5bee411b4aa610067beaa2ee0e97a3abfcf98ef5a1062d5fc38325bae39d536f19585941eeb55a313a8d8b0dab2f0ed18502876bfe4f26c216e87187dd53a9e4b668c8f7451d7a0c888c8e19de94d171e59443fc54cd21aab3b4e73fc39c60ad97ca3878688d802ffdaf97d3980c8099fa30604bdd4e5acbeb9ae680a30358da257bdb7b9bcd7ddcf7582909dd39579982d4196f8e0fd0967a70c14cd17f7df4f3c56c5d5d2b33369a70c92717eb5c9dee9a2d69d9c111b570524e02ec3f7aa2963f28b23ea7d6a9dfcb6a0e69652ffc3aa0d44ffc3e75ee0cb789dab890d408b357075c16fdd2b1e266d6194b5b9bed9d4c565a2edcf2ce01baf6911aadd9be3cd3ccd9c91be662ad93c0f29e720152dd10a7bc8908082da46f0cb2f401d804d74a554e344f4eb9204a9fd0fe7adc5322678fd81382d03cfcb3d0f6453ee884ee1fa040c8d99c48b0653bad479a21b83bb73febc341948647dd2ff125b3bf92ba031e77d8b2213be1950831951b31c0192d8ae225031cfdcc51be359afb56760f2002b578e1c248233521671daecf26472aa679aecabdeb92c614b88b9b6fb6914bb63c4d45a5f9abc5067d5ce8e78a100f0d7b25642b3aeb46b70291ca2fd86e116227d936e7e60d4ef9be70e88504dfdf20135012a9f72a602f025733adc1628a402a7d4cbc08435742f816df2446dc2568aa7b4c51f36f83f59a68849d93b561aa35ac799a406e369d66baba66229e64cf532a75100c7101715c7496281b16613d16747b02c6831df5a90914e3a1747dbb382872a664997c59f0af3bcd6da080cf701bee5aff628d82ff5b8cdc492cac0d7b35a9007fa8e8bfd01a093b8f561b89499785463be683c8690013a0e3683d8f0c8106bd3219bc16dbb56110e12ed49204c6c9b4d6a4f903a6620262820ff0a44a19c5a6eb7164fa339789fc0d7e17bee90af46fd2b9d9f5f78b638cb32d48b84ae4a592708702a9e2b101c18cceab236cb8b24537f6a143a7b1730b12950a59d70258e8ed240874d5b24e2ba398960eed43ee3c9137e78c9c9fad7f8fd6b4880d0871eb438483add505bb54556833bf5b34d9ae5e5651c6acfabe32578801e85572ac07d73e276bbe12a5672e04a211b082159133b2e4851a0164d652ed47b5d7dd37250c9c26601bc852bcbdf66a2241990f68936434293787d6966f066ed58bd300b9206e8a94e8751fcb42091e3c7c4b3d56447d91ba3420e1d2a576ce5afc21f35efe49dcd8540a76d7c3a5f681ac59b7162f85c17fd0571a979ef6a0281beda6f97dcf164315c9d17426b774746c3fe649e451cf63a10c80ca498b662362a9e653f1a116e117a016fcdd49461f6f63836f9dab79c1e389aef4591a1602e8c7eebe3ef1a08e149d9ce9ec0d8778a3b1e59d368ec55482d29491b44e6d4323266e1cb52cad584a0871e0d432d63afe5a911880f8128e6bde5a8f1fa5e229baafc1a76c5948f695aa504cb834595eef0a67d34eb63f84cdc0b25975093eea163e6d0096987087881b28c6a1524727d8da006bfdb80eac56153126af5e8a7b0f3028482fcd8bf424b40cbc25d21abb7baaeda9f096600e0d2af33da52bd11697888022a01fb4e996c19b79e6f4a7db0ef74b4ce5345c0f9068daf348339c6460676a023a28cb6c9d925ff27318f6279d982f7fdeca049b8c9f3104c31a01d97edcaf3634e889955f8b1327acde3ffa3ecde6092da700955a29bb02a774cb95dc6458b30d6f0d42056cee3e1e7d538f74f71785850aa743ba80208b5483f1c8d0c09f5408b7d942dd811ac955f4efe8e265006d3e25035b0ed2020374e2b87d75ba017f7e249a19cb8dc3c320e9aaecb542e204b8a737419e685ed23598ed6bdec54bb64c2d5250038911abaee28cc296102eaa6d99f8c1899dea8c62f390318fdbfbbc8bca098b2176a02e505b6e5fb4bf789d660e7d7338425a560558f3b587c667216d3c82da68517813c25e6e7401024f9341e330071c2bef1b786d89f3546e8f7edb5019526a2617b78450eb41169b95ae1b4631df0fa59f8d1967680d059e1b15f82fb8afbe0d43d540771c6caf7eefa9a6f0222a39994e93f3ae1a8a752a8b01fd81d596a3267ed0cb33d9271abf3e73418f3dfd3d9f28b8f6334591f4af71ada3a5cce1da2d40c23b962b7887010bad51c5bbfc487dbacab6cb560203a1e68e9a63c744a8d951f19d501d7f628d93950c1039ff94cfd5e315e5e34bdc480bbe57509f9c81d4c61173cf017cca0193ebc224a06f901da69c62b78fdae4d7338a861d001fe99f24a6b186a609c93bcebca152521cc612c0ef0434f5c40424ce3a0517c2835d623451e833197471f10a115cd694d01b37054c909334252ed5bf9461639675dcf674badbcc30a0cd84b4bc0d8818ff0b569b67eb22c8bb6306e3d52136a35d3329f6eb5aae07d0381d2352d841b7d7966f2b8c816304ae3112f0fe6c792f2036385bee83a671ba3e93be28f8a8bc3a7e4ebbe46fb38a790634b665bb6e21d30fefd168bba28bd13b7dd5a1063fff4ef95f3eeb7954a05b8064891d2a860daf372af810288c5fca9ed96a4757aaeb66fa12955c0a963fc58125297c860f286241f5a90e8cc670b20b1408fdb3f12331212c9756edc9fe0680c069dd9dc9da1966fd7541b8d8445ce2a35c308589646d0733e9902e07055db53c0c9de5db050198ede41bac7e650e345ee18698ccc226d986373cc666aab86be4380e2a77319d85a4e30b4ad4ee898a4433296caecee931e834a7201b20b3a650f8bfe02c02839dec9ad281a822ed32fedeaf81896296f74677322235221d8741dbfbbb15ed9f420add80f0738566df5f708a7c6094e80dd01b6337a6313ae69892c7813ec46e7c24a7c8ef6a9c0000fb7609e750d8d17f0112a58d2c5fd01e82c59e10eb06d9520ca4a088c9f38ec2a511424318bbc51f88531c423bbc9589c87f19af3d899a03e8bed3b8f3ce2793463cc01225cbbbbbe3b762332590e0fd6aa2c6e31305a5e62a2df54454efadfe7ffa2f18f43be34106a581c0ec1cd78ca22e999d779400d466876d2b87df013b61c872247b3017002d94b05d64ed35962b20dca08ae15dd169644a73894b3bfa26b8c1b2f97574bbec781f6671a49ac8ddc7a0a993082beec2a5f831e2b3f6397a26e39c509e472bf46932efb1991266566b11d9f4b414cabaf13a92ed2ebd3512edee9041bc8d0abc31b3d12627221d6bb65b36bc04116f652703607918686f1619edbf340abc6eb63522d31ad3a8caebbdf65c3444fff43433af0790e347e61e3f9d7a1235f132fe16288831838e7cdeceb3e11e4a643a08d9f0826476dece4eec88e405b5c572e62a4fe71f20345329f68c3b69df219c09fe9b9e0f8e2ef25140609b8ddbc705ec73980c1893bf4f23281dac59c6c946039b9ca0a8df98d3a677e245721e49752d3c2b30ce751387d129422e2b7b64c7a322572b8c4681a0bcf6f7b63908a4a684c4666c79d619ffb3221269a4c39727d451db5d7893bdfe21ec87b78714120542f5313f83a208db0a15af1ac72906da8b21cd57c4319f5480290afa1c691be8acf4504a6f2004240cb84fdb34f4ffc7d5343ed4024fe5185a46c91e4d855ecc5d6164e300df803801c3ecc1de082b1d1bfedd88c20b03b8feec7dcc4b1ad45dc04bbc4847f8e553e14838962d1784712c1103cb6c39048a9acaadcbca4cb827044fd6b741287345059d4b2c9648220a149934ef5ee6dc4b270fd4e3afe38692dbf746eddcf11de113985553e49a0f822170ab0721eb837a22f27a967670cdc4a1858b9fba479e2fde65d8c99a08b9ac10aa55b2f049b08ed04135763ebc42bf05600b384144e91cd9f6df2107d1eec0d225f5d057a32ce8b1bca17920514bc4d6bc3f2eae663c0c5f5a985aa6b6a418ba35132c5e38908aee032a17e88dcb86b542238bbaab05e0a6849bccc7dd55a5bf1043f1733e7a0cd748572e466f55700313256d8ead1e2f0005edf23fe8bb317a21827e93fb239db4516184733bd59d89e549d2ac2a1e128134410fa0f4702d0bf81fad35082e4ca17aaa7bd917207b34ab781922496307f8857adab99d853a803b17f68c0c10ea837df5326d53f409553d52a39f5fa23287adc33419064a20758573024a74ffaf53bb22e07245dcd1f9986248e9a08c5ea70fe010b80ef82422bc618c59dbb389cf20682f54042fae6d4f8419c98f3a1f027db364ebfe3acaa0f3e443c8a1798623ba81a7264cf75f446d3cf53c007f7deff9821d02bd170ad99bdf0c0d57bbf00ce2634329fbaf65b17261a023289c6766d416e54a80a1e9ab40334b7d49ecf145a9c30910451ec23374ddc535c3fc64561bf69df0af5e70dc9cb67e5bb9b921d1426ff5f6ff7d0bc09b1989b9a795a375f3dc294f6c54c8fd57e313148813984f2079d9e53e362b9ca3c7095a5ee88c3eb39a70e7ecd4b3110d73e7e91acec5c4eeac72c670005f49a0c160c75322706c5b333c3ff8b92eecde823e9630ee7b3f119349eed0804b6f5551e5635d2bb7925a2b69e6d3205ea78e938fabe18ab049343bb4fd5fefc44c97657930e9af5b4bdcc866fccae9ef023e6759ef60970a715d0a3bfd81717a08ad449c2ee50760f7a207392b77ba5820736db7753918eeeb0d19172f200828148ee5d1a8590f8a26818f30d5c4b887e4ce9c47bfa4eccc9bd4dd5abca8df0629ba8bd9c5596b9abf48faa756dc93497740bc44b69189502267d5f5740e65432fe08dc157715748195bec0c489fc2469a5bf9aef2d5773da10143ec415fa5f69e7c249362b05243e673f503bd326f342b8d01138b7bb22097498f32c30f54c9a00bc5aaf06a8819cada25d89c234c40721818d3418ea52f96fe0f14ea1ca0289a06fcf64c1e9b92c96c25cb7bbe566a4c3680784fc0171f78cb2d8a08ebeec7848126bd703d12c39f640869cc59d4c83921225ad82535258305a5bc5061d49f51211a1cbbd2efe672b429bcce77d1f77b0cfc5087a6db6084e11d62c82c82fbc930157213313a1bda33d46bd6b1a6d99061201d6771a699f7fb3ae22938cb3fdcfc64514b3e17befa08b850de0c740dc700de22947bfef13082fea51ba28919ec0b8c7d0669055314d31090a88c629e1b4e6c7d8602e1fb8ef45156f95f336501c678a46de46e060da278a9b983926cdc0ab12ea0f72e20588dbdc628789b195053d6bc24e2a90c57fd39e838c4f35d142dda8d1292a02b0a453362582aad149d9cac255770e523df659eeaf54896f7b4a3754ca87827374bd86c1f17b737b6491e9f7e60e9bb31e204b75d586558df494a4d51dbb45af0030fd2ee3039f5b6ca66873e3327cc21a2afd9f387c6b1437240b271c8a25dcaab290f39135cecb69dd2e0a9a3f65f260c51cfe96319824b8660adde710c6026387d1f0d226c10be636c3fad946bba98c4aefe753867620403b4917001f2b4fff0cc478d0b4e74341e5562ba6d2651a47de90c20030a537c50c45f0ee6772a933d045e5f7d10d7ca659ac42b38dc4dbca9b771289d553a54809bfbefc9b53a60f52fc110b463b419d1641d841261950f21ee187a63bed2ce7a40c493df2d7fe73c0407fef5e3e895a720de098c59db6e14d97a53d5942178367122e5a43146c84c583d5db8500355142918fbf706790d0a390b61ced3a3b70a3b70157ca76859ac27cfa5f6cd8758df7e6cf2ac4e1b7d988316666f35b0c26bd0d48dd93ea34a490b9df2c513ba4bce3912a0b70b3405c65ad1d97b03ffc342c338b30a06b232cf28eb80763ac3d6a64c2b75bd34a72987e84c37166b125e5f1787351bc7dcbe010c350a47400763ae0699ae1a31e07fe1cc236a9c806eabf4636a827bc53838b43b9495d9a1ad3eb841c20ff3fb056445e09952e2b66473dc7a6e854b630104e9fac4bf7785b22767bc04c7655a31ef823f72b70922a9e7262f7be319364c9b3a02f06f030c88297147509b0eda19a46ceacc3c9b35085bb96156a1c9952d4e2514dab284e25d5961bc09b65ce9dbf2289bd8233aeb46b31e4aca5ce65eed72ee68fdbbf1d655495f1a55c4ee202a8695c9855d0e27d24c7e1818275b5acdae30f2df7af23dcc4f239162cc669ca2c965c1d0f7768f267aa2d592eb21e54687fb19f9fc3d9a6a4c36a5511970db2f161076ac62ba455412a833d21157022baaa0f8351df4d2d01e5705e4e76ad257706d81f0b4502f9bfc992c06382e668e7e6aa27b048982d670be175addbe1782c177b40e4af3c58e6667c5c58e16285c45612aba37a4042f3d8b8f1ba1051f00563f02b304dfd56688cdcf34d19575c25ca50adb356fb6850e8bff8636a3170aa44ac45ac5f095e1f3cf8795066be38439a53521096393efe778133d6c76e8b7891214f279a1f2fd38ee48cb7669181ed1a837c6799dfb948074a196c6856752eedace7a5082edbcf986c56dd2582917ee9f5e134e89d165a75a416c3abd22fc2b75378dfa0e0415d4357c1467dd3df66e2aa18ae4871eaab77fc6518062764375f6a3de93429ce59e290c04fb9339ba3d5bbefe308aec5d737f4d7e8820d0275f4f87c53852d5fbde31a811ca7dac1094638858ef1db2689a651e8444363e2dc8fd223a5b109d355a9cc0581259aebac593b6a5451966db142e277b325e2fc2d0f5777ba9afe387854d58e933ab755bb8b96cd5393705d31ce4232e057d41af5ab42c1b2cd2b5c378a2e63caeea392659ddf1460035f99b08d1b47db0746288cff65365c1a13f7ebff2ee1fef8efd87617d462aad06750495879b438ced6e0203976fa3479c077e60b3b7d74083d9cae65170eca3c84bb8a1f319e63a1fe4905fea6b96a9623e4f89634561c722d8fd47f51370b27b18883eff8a19ce352cdcf71c40f8eda60a2c19329f979646fcacba542a06d7cbbedb405aadc429bf4a62af4ccb2f3094aff87bf6870c2a0ace4ef18e3d1a94ff8045bebb8c9861a0a02c8f1313e75262e43eb1e0d336224423217fc3e8cfa08d9220550beeb7f05ffe2bf185982b87f63cd68ef79850e532ba7f67cdeff6e3be5e8aae67756602a58053e1e7322432323bce8076a62fb452bbd68f0f850aadc113e3aab1d2ec558ace5816f5b3ec2901b58fbc1b6f68090a997e8904f827d51fdfc12c5bd824ea82f19bf0e89fc5a6135dea9ceb07cb6e8c3b2d8dbdbda8d138b22f78ace24dd0450d8dc75049800b3303a57e51594eeb56a03b9673b801900e92ceb687a28fcc7eb1e06b35ac3a379e8e7aa9316cb3e3e86af37a433fd7a452d7a1597be7b3679112251e87d2c0547ee35e8340060d1863db24c827dc12b9697f5b99ab9d4d376cb81bb81c624427f1183e3cf2cd919f317b96e6e4114c67bd6a0636de5fd13b680482deca185e0c38cb331117a17dcf64f544a3251aec125db3ccd3f177d16411f959ef7915142f5247c6efd0d41cb5b4117943f0e0cae1d03e27ce11ec707c085cc67d05669c5d04f5273129127f27a544182a67a4f5c1f9cf9290a62d111c8cce70384bd663309888930d25c9bd1c466b8e3c91e718c242e752aaf3878aea6cfc1796cf824445dc72a8bff5ea916fe0831a7dc259da74e48b4f0a7b937146268d073efff88c16146941fe22578a3871ea3b2c8b0eff00e86f1c4347eb5bd20868d3c11d7620137027cab297ec0cbfbdc98ba8f811028c276a3628478062fea0c49cc8c18b2e925af06fc0a4c40f84b67917eb408896e96ba9b43990ca6ce1ac3a55839f44acd19b1da284d6c9d749a819d13746fbb5cb33f9b195936df5d345f9c90509956065740eb8facc7da0c13bfe7738a6ae9e59b24995d4b66b8800b1b04af99f3e1511696913ec92db68247a9a9d07b08082dc2fffdb6c18dff5288f0861b779873be24d7d5544f80ebbb2d155a987aa2378d7cb56a6c549e4bb2d080ccf84384f78ddb9baa083eade9eaa1681602aebb86adc56c46e5887fb0344705efe81a081849f3cc411e68091b1febb184d88b37cb2ad9bdd88a2b0317da26924ad1076b031f7b4a3d87b161aabb02879a7d3876fe748589fd106780d9e05c016125c64cbba498c20fc5a5373ed7e8982a3c251e8077498d10ed37391bf07c245f03aea32a2e3e162e013b8ef1f4c245d1e0cf941e16290912d9c8d3237aeba240b2d130ed4483e374ad5bbe064c405754052a9f0b4127f90277c921f672a22105cd040e6898844a39cde1c36af82541620a5bc77c83910cc58a2ea08d1840ee81d5539b2045e0d13615bc11e28406398609828de186f05969a40a32dcadf37310295a1e74cb1636bc0861b64cf9a119052243b689bcfcb424432f049d63d79ffd1b831ce697e02658a0e73b749c76ee72e92eba0bb38fdac61e90f0f4336ecf48fdbfe3f48eec508a67ad2634c9fa290ff39c2a01332c67891d33e7d7b211f6546c66e5c55cd0a659a409c73bb189fa366d9b19e637993e3aad5f1ec60bddc014266a06b2a9706038ab2b783c24b62a71480f7c516d1985e2d9d5fce36d9422ca70c0fb3342f866800b0e7a23e38ae6fabc7b74d162892746716ea9120add7e5a9c007411b98dde59080f280b3c71a0fb8cb95f3fcccc23ee30c9bfcdc3c80e8117cf86616ac22018725a07f18c56424d62866a26acc634219990f541e06b9984b5c3f78ddc93c049d27f48b0d8abe36f205c06a16265433a34a6e9ea1efe10d525c59b9518ec93b66d91c942b9441f65c11cdbdf01e45c66e6a43fb335c8d43f1b4a6f050984e100a160718526a8ab08d16482d922c03f9834f1f0762d3f8e42b466287c32d1ba7f9f97b950c4ce4a41a35ee76c14ac5f5fac2330528f17a06497d382be709b625206d52e1ee46062e5ac6bcd5945c8e37458f85ddd5900ce05459cd26ef123e636fc30b98003732c78e54b4b70b5bde2e37da60d7a2ad8c6a78313b0dc7d54e3f984eb0a17d606e6dae93b71a7bf15a50bb447243e919e07d5f28a699b638af4b0e4befc415aeb4a4a1fd3e4439529c17aeca55fd01729a574906825f43ef1561ee75f5723b212a82fc156379ead7c8b23e9ba56fde58a8b9e0179f1deedb53e107b400a23372f3339a1e5e4c7db6c27a5f00f1b39e677849a50ecd1cf326eba083f7972a58021abdeb2153d02e90d542fa09a996beb1b4bb98b8f3171d96df9db69826ed6c8ccf4256f5ebc8da0470ce598591f6d482d2a418d5aa1555aefe0990acd46412ef97b95edf8f14f56549e36f6780250ffd051ac7333dafcedc2a5db1891b053649236e8fb0fa13c90d44ecaf0c62682698f164ba57508d699343cbc24537448479ffcecabb91eb1b05c05d050bf06097b930686a5284a38e2d3c772d8ad7a95b250a9e7e919207c131b91ca953b185d687d18be38a8854dc27c3fe4763c18588a10bf5f6fb82aa586903a02471d3288305435c5e99980336be60e1f08b1c782e05a52d974424394a61a57c6ac658613ccdf96289125d427796e946e76a3a60c04843505506b6bf3968b84e89a332d165341b35947f3e0a7117aef753ca6ddd0e62f7602830391090399fabf03ccd6f5671ad2c2bcc983f35b118172ef4fd4d98cbff0119adaf668d209c5dac3e1e8dcf794cc114007db77e25da9a017f2457a1262f0c626f51d85aec750c99aed78b9c301eb8ad7ff9e7541b8746c20d5db2cb202556162fcce788fc6fa5a12d3f024f7a6f05b2a47ecb5844e0bd9e664dc70b67e1897aabe20f80c82b775f8a9e95d6d11ada40de925157b2f8733e0ba0f5c2658d268f30a378457048d87652cc48b6e486a34842cec3d3c332423d590d80d77289289c1e3c15a373a1f78df9553eaa0348237766b63158577474a0f9542a46a26ce3ce0f5a804a1c0b2c4261c11ff269e7a409280052492891159207c181034df0a8a943a177c6ddc2544144549ea7850e843e772b964cfddb250074e758f22c4d75390159bfac4f8ba4c7d55e676695254d31a46828c2bb132fef170cdd7930028a9afb3a3ff217b568517934be07353a72cfb526507fa5af8bbc92e41edec4aeb0dcb4b6c71ed7e02e398d11d13ddf001d495559df8bb5b9d241919e99bfc2a8a84df365687e57e86a91b279effa58909d078b1ad39a7c2007edb6e7de588d2851833b0045450522262fba1c4136ead1e4003c804eaf83b5634bfe5876cf39601e3b589c31b62f43a8c73b885f1003a565db70f964c6c995595692aebe3d0e7c8c8ba440826591a81909babf5952951c9bdaaa14deb0aca46715008220261dba444098767fbfb6d09b013c246c87b32ead89154e20b6241407b935435d57fb29eb505e975d31b6b6d384cc5213f55b251c3a34d4e88fae10892ae0130d3603c4125d280738cac91a26e09bf104b45f39859e63a601a9d5140ac09c6737b06461b3459ab6f23f39dc6668502d52bcf66135b192b60cdec205effa64acf7197b86062c0e8bbd10c8b1ee0f0e5a6a46e5161f5ca19beb0d260a72c2bdb4308ddbf589e1b5524ed5536a53275a6c9ea96dac9541f19ac4b79497a1758dee785849370f8edd20708987c1c18290de6e422e7cf950e3571ca7c0a4f2003ff575bf5274b6db289eb899e0170c5e39f4eaa27b277b204db6d59be3eed714b8a4822183382a194ca848f1d9aaa2a66f1e6a2baebd8e531e42737413ca557a4b0ef425ab66d0d2ead68a3a85af603735089da07991f486d4d6bb085923a81fb39e34306265d4b0fdff9e375515899bd2065404147f5a36d319342d9a4daa4fd7827ed54ea42475590185a4722c0f7cebfaffb0efe54d9f3e3c01fc073775f5195f0dc47e2935c8fa977cba749f17cd751cd86dda7b387a4c165ab8285aed3ce548e2bf2b40a192e5000a48208664d85484b267c584cac4a6dcfa94e4e1b9826dc06a921ec02da33605e36731405effc5d17f88ea0d210036b8294fe5cb38ea755bebb9076564aa0f3953081b657131465940dd4a4c7f9b839a1320aeffe2f627ee7fe1f697eee04f4a1e7430e68185ffe0304ba1fa6c01e9c52ea9acbeb9b232bfd56ab0a85b475558d4130bade0eb15afd45fe67c1963628d8dc439f9adc4ce060460693b7eed2814ede30a27377132ed8f0eaccdfcad24a0e57d42203712220ce0ff5625b3545f9beac14ea84047e6a9556ff62911e140d9fd7c4076304d520bf67e5cacb2ee949227b471682cd720ecb1942d4ad9d2e7995247e976bee3b0bdee6420ac06b7515857036c3e10abab036d847a14833674966858fac509db2c2546cefdac3be0062a2bca3dbc8206e8b0a464b7901533aece2fce0ad7c5492052871d18faa58bbeba258e408cc50bc4b39439c4768b4ba63221308eaad5283667594a479c8a049a43cc4216a61ee29aa0644c5bc282f5b456a472266743e70f2e517083b924ba831ae531811075282a277aaeed9a45b787392a3a1c2be45ce5cab6eb8aa5e8acf59c548f77700ce44ac77189cbc2ec9512b3a4ae0bb6733ccc0603be0425d02d46a33e4c626d30a1a0170a8dd83724bb0f167d829011d5e3bb129bcc814b17932104333d42423100429b3e51d0fe50baaec32e8b1931674f2ed175009728d92b9a11ba56e106312e51e0c344d905d5c41a8492361e723596bdb82a926a825802a60e3abcf51436de615b8fd8a20b8a80ab875e7ab71255c324564265c490dc1a6989d6609510ae445ac587b14ca8d430819544ad63d070c5c472b37d806b8e662d67af9ab4165606f5dbd8953a91468af5225918fb11b71546e63af3c1b8701f0f56b34ee342761dff8ece80763ef4e90db75f3598af7f841f310beee8701daf4ba02d5302022b8e7b0b3bb70e46877a410c67817a2593b0b9e2407b7f23ba253fae7f8f8e0b04dca5edc05eb04198e4d306a9f3fdaf2c0c7c6c2105023cc88cc063c20f6eade482469de186fe868a74034aaf5b23c149de51d491f7373d977251c0033a37e047aedaf323ff8a3b8599ec0a073e4cbcccad00c874c055feb0025c3af28d7d6cf232b9d023b6eb5a97275044182b5f75e058ff7e33226f21b878e14a545bdc42167d6faca889a55186e01afde367c8e83c7061ba90c00b249c71ecfba41a1a16c40e06ce61acc5c084949ae13a7b0defd521184479d76a3b194d52e8a35bc2d00e41fe448ac7e25dde549ebfa8fc88d3b0db25e0a775dd17fe4f4e311aab0e7af5df89ab1cbdec5f020c141fe1c1e8cd16e049a102228ebbb6f0841ba2611bf1c50d9e1ec830bdc5b5434bf0c95d46aa384d1482a33b5627f545aa0a64087d3dbb8465c6d3822c2868214fead9377da792244c5eb52b35adad7c2c5a8c290243f4dabff30b8d1c605c65dfc5421221c92a0ce1ca9a75319589202ed8834a4bcd004e3d2e81bf7c7a2d818d2df2d2e90e0b4f402c467a29d0218138ef452650471e49f1ece81345cff73f861155511712c6b3c38bed72d280d4c132d21fb6a085f8459069f1b71737769b0e8621e9948fc44c9237c0dce1cd39e026d3c7616462e1391e98348dffa8d495a5327fda63d7af3ce2e4fa3401a2b5d4a876b45d01e63e1afe0f5deb7df7279edc44916ce23b6a08e1e9b2e9a4ba346a95b98c2783aa895f0bd000c3bcdecce2fe253f5b75521c4ca8004f97ed65e8a25a875c0d1de041fb788b6fc5913b2977cac4871c2508150527bffa05f7ee9a918908c7c33a3ebefe144452afc53ad6de8c1667c0f6b3346ef6f1b3c16ba51d41c8db63c49a6b84f3a2ee3cc7221c3ec272c879d33a9ef8085c780c4ee91f327dadc6657f5a16b78c0b30c00cf180405b000eceb9cba2d7543f209ffc601fe3903c68c7596a7b165949b7dbe2404f498e109df2a14c86dd96a70832972dc2c26912eb9abac982be7b7146981d3a7312aee89639c28ac85074c3bd365d38cc7ad0919a1833242c3a5281cbc1854ace95a9fd10d770dffd2051c3d2f42747b9d785ff36ec4fde35c177f793014f83cfb0cf64e4e76b6b503859662a1ae2a73aad78ad25ba28e534968109204e919c82dcb06f571ba06d208b3a7c4249a141b384c624c555ebc5b12ce977dc7e6502094a3a42558745f92f1f83811be8b0ad367c1ae8597d1b2ba8e236645c03bb36ffc1a537c7c63cff8874a78c7ebd45a9973367e8b1502c8422c7aa910cf020c51e18c1de097dbbb2b2e4cbf41199b6c296050b9afff3b2181c80c2eac266f8e83159969300d06d81dba66d768b77c209d4ba5f94bb212f464a62f3b9760bcdf8edf8b181a42c4a3e1c1859c2cd7320fc5409bed82b05de6545666e2f5ecbd342eac51e817f12b2bb15b6095e7cd4634d0d5d2ae1eb0a718a5ef93d904c840dcc06c63022bccb316c7521dc0738060ac9ad4005350675b4b847fbfca04cd858fc5870ed55018531e0d1e11083f8af0f492700d52b8c82ea48a04bfd9c5a114a6d20d71746023e36661cf378b35a9477db120dd795e034ff758ef59e10eee4413480b4852ded13b6450770c56177532136991b6acd7e016c8e8d8c901386a041f365136d3a27241effd8124250482d0f21b6027c6d2f3d27f0295da937d6720504e1c84ca996a134e15140c0d84d26e8c4a33a35355565e7962b5678d6540a95524b2d019e44f50223bd508216d9691411dea74561dc4f0352120b6c64adb94ade106d009beb6a02473a6bb9d5267f6da2765de9705c3dc4aa5befdb335de8b22d00f08518d504180f236cfc04446617954e9d703652950f31b61e0796ad71cab9dd4346d5b873920caabbfcc0527839711743417b4a2f8ff801daebfc72c0f87a18032267d834d1aced639640689090665c41bf8d9a2c6cb9811a9971f89c56253ccddb30e46d42908ac36984974411485412cc07225d694522271e57373a261a4afd1f6288ed4efb2b205f7218d646e59c22ceab5cefa0bfb6e5b8c07d6f7cbedce33d621b1d75ae5c818c4cb43a6c0e200650098b4ffbb3c190a67c8a04ed8d4256b3e708aac7d548947c82b6794ecdc882a5df3f98495433ed3087944531bc30db6028b5c7927a55828d77af0bfaac203529da40b34dcca31dc63ccb8a132a26c108219ccaf0d18a613d9ef9d42100c4c4eac75e194301c40b9f632fc8677f9f043e5d0c74efd97187ced91555a9f595d8c9183c7e2033d7208e8f3076897dc8a8550caa46c536406025c0ea65035911b6e4d838139c2c46a6cd0ea461bdd6d90b613c1a82803c7a4cdf528feeaceac9d81ac8dc43693e0a98f11268b9b63c2a2827992299775ae711c44b912c01731e65bb4d3c4e12e6bea33b2c6e372bdef81d1994289333760495896f9441e523a2234d4beb452a233a9a6469be6bb5dd04351e62badc458a8ea068f098d2c77d7464cf7110dc5a4eaa498b53bfc6a6a0ea3ef6a44399d038a67b62ec8c43eb312cf57083885868514864b0a16fa4b73962dd5c425c5d70d49e566112a43b389a40432085d9ec3fbe2fe5d31ad3202680dec1ab2ee037ee24fa4e5a5236b2cdba4ca3e4f2baefbc30ba2c087bfeec6f3582d06d415d8d02b7b121d4bec1571ba3441dbae5af46b677598ae9ba5bcfabf1f2b4cff50e738841a22df87234ca7e5ad39392d755f4e68c6bf189fbf49a334ae42b05085e481fec6a08425aeb0933ee018bfe8832267d7d7928805130bf62dfc9bc1fb2f8abef44f612a76e7ac471e5de269ab60740f084f387a613f62faeacc22e4ad32d84a618b312df535053978d2179376f0bd8bdf73e59b47a543e6f68904f7db3185fcc7279deced1baa1f062e2b160d6a87d02332eff6dffb126a88c817152c06809ea21e61f9475f3568e1a63c19e3e33eb19b01ce8ddfae1c9bf6290f470c94886379f3a054fbe32d8a34696023442c296635f80c25d7079d6e46729442814ac583bcd35cd1fc676dba2e549b36cba2934f9ffd3cf826a2d47f4a373f9834d015e16d5e1f0f82d584f5d179b824d32ce2805dd28c14a1dc4f30dd5ae9572b1a8ce1d56b84e4b61940ff2fb8eacfa8956ce0e3598027926b6a404d52aead6c7e6c337bed17147aa449f8cbdb73419a50c3d52950c1aac806408920a41c83f3e4048d8592dab55241cbedf84c6140a1dc294c251f4af27599b40616467ae53f7a62fbe98eb94664fd07a4f3e463d1c9091e5c77aa4f3daebe083d1bfb3b922fbd45e0f30d3ff311773cbd3f35b381effee7e50e871ddb58545892bbf85da86308087f4cbe3ad860a7331312bd57326a51077caf02d1cc99c45163044fb4954b5e53219c1a25335bb0f320f0f1e60aba97b54cd03a830394fd5b38d6e81dc74df76fe8d39ce0033ac9189fabf9e8376dce56640194d25c51786276a8d3b12a7412614c5172195462117a0d8a1b499857c7fed8ceb0c6b7012c32c4dab304847c93ab5076327b473d05d9a60eb27ac8a7c0b917dda33975795ff2e1ff7bbd97ae9475d4f016b770d7a173206d139cb49398eec138448377a8e445e4664ca8f32f6c6927712ec3a65c85292f7561dd5f3c63a13724b6e9bfe5d48ea882f481909a3b0de246a9c6735e153f9be32efc0f1bb2360ab6e42fbd48f975a90939092977915a03b6521a647263aba1f897a8d30d08ce3256308bc2afea2da4c56b328f68b2cec2214566e27b81e0fdc8520d3232d3093fb980b5fd0f2e48aa0fca16e8a64078b95000f44164aad4c9a8b15b2f7838fc5f61290d436ceaae8d542e1d1ccad379308fd32c6f5f8fdba95c5d008e3ce3469a8115008f2694743fc2edd4995d4f09b3b081436c4c731147117518129202b5c549ea1eaf644bc3bb877af9a92d9616d9374a6103743c61735d605a759329d908ad1951142466287d9310421921d8ab1d1aa01f0177b3368839c7144f3c6b62ac15d66a44ad326c15fa17626f613ee6ea2fe43ee48e867c89d04fd87dc91d4cf90bb49fa0f7727a99f217726f51bee6ea2fe43ee48e867c89d1e040ae5ebef3b985c97efc400cc902ceba6f6cb68f5ea844b39d6509e3dbe1db89f9c1bf18a8409f40fdf835201d6e83d9d597623badaf91b703e0bf836d81135654907ab033d0d40199267114b19552c1d5b46f74f8138d911f0cd012e3680a24888d6d5d95d62f2c89ad7d5f8f9e779d2c1c827704fa4deed7ececc923f090766a9acfc91b3b27135fbd53f6b9006a0fbb2274007a8ec5f0218f19a48c8c1488970ced82a78fb1de04792fff8694c22d13179e101d09f7b30dc36e2960f348de7f24cbf2bc4fc30ca11a2db6b661a232c14b59e668abff48a352f927cc66fdf80017244939e0dfe6e0a57239bbda070aaa813d41e88e252173832227335aa5d1f6b23c86df53414e002a430a826c937186bd06fec0223348ff9894d6985e7ef7e6c41c9870528b157ca6fca6ab5bc61546b179ceffbff1bf049968358f3fc46081076b21448a5f0b629834228bb772bbf1b17b3cea1af80221a292020eb95052f084843ef4b97866a9e035f6eafff628d2932ba995ee27c86cb15234423125ecfcc22eff4f7679cc1f968fc6b8c06aeb42079681a5ac95fc9e7fe2dd676296159f397b14ba3f4b53bc1f8d750163d29917f019428ae1d594f00cecdddc880128023a08fb92443754b290ee31bc056044d65fea024eed10bb068b629a34bfa4e05ecbcb2023ed8e66173f3e58952aeb30270b9b520f959526b49350d07b9e569dc6955f5724e0822f641b618f5c734f2046bdb1c24fc20a179f16b7bca08f9d6a88695dde85a8582cce250efe84566e300ee565fd19aeefbfc1966b4b28e805eb5b6daeda8c8e45730cf467a14d105895e66ac82cc5aeb8ad7d4cd58aea136aa360f9bd8270821354b40bc874647074b61fbe6e312d8e11a05f53353eed06eb87e46a95197272316195c3a7fb632f972c00a58a90f9916873c2ce17c94f3019a8e4a0ad699cc8414a5ee82c7073c43c124bda31bfead21b13b85bb8ff97caa3872f9b5d660fadfcf7c9b68b1070c163ec9c9d52907e0112225c99c6ba7900ab1a4dba39c4d5ee52487c9f7b3e75f96241f7b57480ebbec503aae70a1dbe677686064ec626bf09573b1adf6e34c921743a0d89734bdeccbde53b689e047a56af676b1f51091d0164c0c2617e10ed26603ba34e682edbaa4fddf5292f8a2237987a1bd58068a3f866792abaf6f866ab5a3b4d1c1ba301283e9c90df8062d1b5ad70f1a3f9418e1ce1323d49e67372745d1dc5714e68dcd5919213cab3d8671646013a496042311860c4f5267d2bbddd69fc5d4190dc5ac968beaec7367202391a30bdff4c07d0804a2a60e210ef4d63af5e566d6115e5e73bbe86f6cc6d21e558a397b4ecaae195385409f299aa987654b6588daf9933cb234db3ae27a7735a6dc0b819dd1c455d218eee3eae75d86abcd5a565a3fca51f5262920759573e5605385a3a4d04b74e6135945c89a765c3a8462dcd43d6307dbf393a1c8269c358b9f3ab67a814f62121d79d6244bd58d1bee0c0ceac0151966c5e02fe84752dbb94486ccc50b35e0bba3aa121b18a542106706a5a45e1e1bbb4757a0f22c63df3aebe585e70a00d846265ec3294a870c03a23763ded81e1fc06c94479fae00b652af2e50ef0d685fda3f2788f873d0788e589146cd27bed58de0827db8d156544cfaa8844c149a83e487f3b3eed1eea7979e75dec0f8e53cc8d53383cfaa74f831cd24bd8f770ed24923c65f6fecdbdc72c3983dd34027c707f94b59439fa4641e44629da7a32604eb1f7da94ed05a2b4cdf11d0fe27f190daddc0b5a2831514ec8a6b6717f2214f7b1a060c4025a20babd889f2f2c39bf54462cc1a3a648f5594f487f82848182ece367a5bef7c04b867a6b36cefbd68dcd14c79561cfacf5a48c60f20ca5789f3216c39a61354c0b86cbecec77035e05dd0f321189b9f1742d6cfbfb2ffa66ed60364a4867bf03d1f05301ae327f5dd0011d3f24ec049dbf6f985d768b6b5d68f1068e0dd1294483e75719134a740dc89722bfe3a2cf2a13de7ef20382e647acf5f023b8216f527e854ad3f8ae45f5f1cc22b6ffaa8a5ac0c90007ebe46420a174e2b939ee6aa973450938ee916e072c1626ee10bb744ed4e9704259365364ab2bbcf92904871c4bff61a056b9a11cec45d48b0f5000ec455c5511489333d88bc596ded7baf5a5d4db13258b52b771c5f06830781b8b5cad33df090fa4dc1ac014038ffbaba5a2966ba562cc333aa8c80cf64de1185f9f4dc896ecf55a885bb9edd27d30bfa129dbab9c7f74079f323763b06e64a2372ef02af356ac2bab7060d8acff0b3df2fddb3d380fa50035f387ce18f0d85249b5756f9f4c30fd0045b59a9eb0eb6d802f0d1a504f6a13c6ff638089de3c605c53cca9441254a1f10ea2f9e3f14a3c1db6d8942248af5fa3a6823fa4833fc140594de92786d7e3cae926a9786cbae74e797ff144c45b18d73ed19a0f6dc8646b17ac6663be6d0190897e32dbb7a71af5fa467f9cea8313524cbee9ab927ca6fb082fa53695935cb4a17e95458677fbe3e7416d7c9ad09dd2d8cecc31934f2d8e0e217b9f35d12a943562dc1fa4994697f8653f36930cf126d818d89cbf29c7a5983bf5114e4ad9903cf38ae7c9ed5048b2abda7c46d72b4813b16a438725bf267f22b014896fb2c867bdd8817ceaefe92e4749cf9b87bef6033774b1bd3308ce252c1fdaf201d3c5875f758145a4f428e604d25b705823053863d1a1f90edd84d14c4f946a0a75cb858bd760be0c72f27162584afd48f7ac3548a42c62dda68918a9cc1fc6e9ffd0edc20c198a10e6f61aeae8276f09af04c6167ee4233f4a024a2cc2e7306b8a0c159436458a6c21877cd59cc9a81694d7830ab6c2a5f5fb15d14b455444d168048fb22a0863bb81dff9fe49f12d8525aa09b80231a789b9de5857e70e59da58b11dcf34a2ab68ccc8046b4ee5b73945b1a5282c419b9f51ad5ab9943f78d0553042e8eaae3a40fe1202dd4f585946ad25458b003e0c5b9535d191db4201153e9b71cbb70784a57568e76dc533be5e8a5ca267959df29490fa13b740037e3986e5922948f8b627589ef9a986bd3984fb9c2b64441910aa015b72f6655ed66213d242b2bd1c80f30802ff6cc68ca9c2fcb2647e95c326eb5d6aaad52da84f56fa309a570c804501ad160081a324c28efac0314bab57f4f1cab86be57d3ee272176749bd93442f690774463f2869cddd49c5127edc586460829ede0accce6db8959ea084cf589bf60ca16c2bb25b6b016f78b1851a95a965c710f048cb8f29bf6c8a0baf7aa46da2300c34384caae4d21176326acce6cac8fd8e82135556966616620154ba261d3806328393e764f40f17585a01965fc2d939260e1f084541382f57ee5ee0c5379fc8d09763008158e5c1ef5fa2dba09f19f0ef82a77cde3d654ebea2bde7d936adbcab0182f2d4b3922ed03312e7665dbdc481e96f72fea856690c6776d8a99da687d15f24194ab9ccf47ef173ad4580e368061e2e8f559186816bef0537e5a9fa891c27201751fc1fcaa69f2317f776da4701faa6b57eb24ab7f8ba9b61a4361039d990fd07d4cb7e3f3d8ee1cee0ed32efb85cca6018bcf288ddfb8eb7fcc074db2b70d9d6d2ccc707f3c7f380d7581a5358fc344fe1971498f3348787cef15e12d80ee9d75a3d763d6ff39c487ac6034d2460aecaf406219f5a38e01b5d8d522bad0693181dd21b4237aa0a9120a58b23764bc05baea09a81bb1438b4a48e335a51b3a3c7a471c7e266cb0d353a5fc7746795db908466f775f5dd49e18b9f7d81767859117451857a81ffa812021a1c6d80e04aa007d9102432f5cda60fa9ae4a41cc32731f862e63525a36818e658638f0fb0d79454d8066975b07b8a6ceb81b8327b438fb5e1c9377ad1fc6318c5093f216ecc92c2458dae3eb41151cb888652edea2da719181ebdf23a99b424ed36ead26f493ed61f3a283d1ab79c126732a301979486956a391a6e47a789eaef9c275b50fa369888ee881cb10697dd6349d837a7a58bc98596731aafce171e78d9c0680c6764eb8e2b3bd96c009a7d0cc1a9f81b9d15c86d8b66b391ab84e8bd93995caa0eda5e00defe3847cbb08382154dc6026f24121589c4b541d7845f78826b2dc2350089cd84aea4286ceb04ff472bfd8b72e366d2eb6d7d5083c2a25cd235de42fd7dad31b815872c8290ecc1366b2f94912bce9a8247e243da8f41d042183b5760aeee24be520b7436a78d11ee03390ea7a49911aa46ca4f60a1f805e6a2807914cf6a3842865c7933538a079423acfd5a40733024d4d539b07ff1e71e37ef6a5f2a4d2280db48295962a72cfc9205bcf4535f91657726a2445895055581a670c4044709bea6800bd9a671aa6119f10a36a0df70d918040dd2664244a146032b198e6152c39dc1527090bef695974e8276a5a052c5bd08521463b51865a70f8dae742951535777a6894586af3382cd65911c3b923cc00a2b64166af76ac8f9d08549cf35d5ba8fd8ed4982b8d8b48a34023792a4b55692445a95429312da3327bce1fd2cfb47ccd4491176642cba7da7388b36c98c0d650f1a78b7db26b880031a9dd6bc7972447c0aa7e1b87ef0190630548b4474c479ac86ec0123b17f09c4dd6fd9cd99d151a2ae29b9df7994dbbef9203a1cd1d0832101d929f532351e4bf1ff69faee9cc2844dbee1f965f3c5bb6a2ad2eec21a5841d64f85e70095ab535f90987e365e22a15598162244d9ad1828bde2ebe9057f0a5ec982a153d4237f5b56c3734a12848c22653f292466f567e9bbe3012630fefbd1948c63998c5567178c30314ce75e2c8b8aa204fe754eb6184a6abd6b52056f12350fc010c4f7f94ac6f329bb3ac20a764020d353ba05c1574354b097baded4a9704f01cb78a1bbfcdc9aeb76f6106a5ce095d376756e33cc9dafe7c2036cd2fcd90535d41c62fd0c4c2f7987e32d012602bc2c9c582e334c41a036ddbe948499aa142b8927414395e07e26f9f298919d550b8e40c4f482eb7766533d988d401dd6b07cebe50e99752148bff3abac8d01d76ad2245f2386adcbaa5e628ff7fd02522b89dbaf6ec300c5c239cc21c5e060c2726bcf677586047f0b3820e0b7db7a5854cf40ca5487cdff625d8418984d754180fa09f22b1a2bb238dc765a93ee4145ab884d33ea5e972f5bc6377ac6975111109ae09194ee857d5936fe4410ab5ba479537d1334a3d457b6c711e4d797cd83646ce826bbb60efabcd8a9ef965c86edd9848ab3e5731717c6cfbe17212a7f234737a3acf0f0b19a8e7c82c886341c30915a5f2e6d1a5ca0259f9c051e451a7f2e9e93c7327a70744fdd27d1c7f062b481a66ab6a0e1217084fbed28b3cbbda567a2253a9a6c89838eeaedf7105f6660e5838470225c1ec0dbc9f73a02df2b9eed172d75f28dc7212cba58b5cd4bf02d5b35a6ec4e520eaf42c4ebdb114ca31a7f973434021ec966334285a4097b31f2678faf87ec91fb0dcd79af7b3fc9154dc750e7004969434d875c3a8d639b01f26001cca378b46d7f206f7c1bac9f4006513c958d2902a54123badcd85a8bcb96dcabf63372f64f4b01686940f58ef10521b5fa5b3be111dd0d44b3037fa5661ac35becf38d260502ade33aa6c1cc7760941e1747004603514addc22c028355a29b87e1a38ca7f384062d750933562471008890f0a2b2c60037453fc8d65bb993fc16f1515c932b9fe3c48ba2460e02f5ccb523fa223e6d8dd14df214f8d793a77648d12a339a403fc2b9a9f286e78b9a32c840aa1d220179f2e539852b0e96061fb701def37220d66b5c2c7eccd31936106b3d8a95748785a03007d4249c5d8a2b5cd5432f4d4146090f3273b3bfa1b73ab871138a277800f51f8e85af69518980eafd6119309387a150915664bdfeb6802d7994388653fdbaa0725ba869c84a10785705bebe9eddfe41cf8118d8691b488d6d80550784b5babe66adcb5734efcb41755e12d3af1fbe46958c7e09e686f9b54a728f0044e39e6da4ef1d7e91af8b5632d3ecaca005e0a1a942b4f7355c3ab2afea6aee26dc7dcf42ec01aa87bde2283fe029aaacc9315ee5b622bf92d46c4c8f3adcf36b3cb09348710ad4dff0211dbef4ac2dc1e61fa376afdfd9db335b602deace9682be4e36a54daddaef542223690e6cdf244ec40345457788487a0b4285b2092bbd3e30b8d1d24ede41c184e6fc5572118d4481e3c94d3612515f1d896584f3de4e47c2c8faadb43d43c6ca63948654c32cdb3ad8e24e097219a91a728df6b55ad0162f5d285f226128775942c4e128b26dec4441c7b91bfd4051912d5f0bbd0a2b7507fae295e3c5cb4fdfff498e87bbb5679e0982788ddbdbab384c0d529fa475ebb49a604340c0aea894614b65ae8f89e38539b2a801bc0fb652723b59320a5aeae58e9ae92f9df5533ca3570f7d620c530644e5958e54e809f89bda6fb76924846736b635b550832580c754ec40636f0752954a9891f8d7db22f47f9e80d27b12ad177a8ffc83cc3188d0fabb0d86684560d6785102371285278a5d440308389c9c6e9d3ebad3970f51d6958be4f5d570af8e9de9198ad5094bbe9a2fd24aedf7e5f8105ac344c76d4765185044832dce2ca93577a8e0dff031ded8686b8e8a753a074c3b0c9c991fdea404057a578f0187997c3d0ca3bac8caacb6991d0b6c0f8d7fa4d557b633b0be189b35c3715677644a61e796646d34e8065268cf19f2c9a5a11f1deb67b535769c40c7b3cdf5408c48626ff935eb1519b4bead4e94570657338878d7aa722c11353f70f3b762e04d8ac03914af092e3f534af8df7f74e4700861500757356071dd7b8ecb952e88b940e46d8151f45a5c36d195a8b6001166187949415c5f264cadbdeea00f8bbd6a3e4a5e7d15742a340c4d16bbc9999be2ef35e16a5be18b82c924ac7a46b936ec7df6f7a5385284bbe8605562be462b0f575ef88606312229d0fb8efa10c560162903a98f4a03b985a0e726cb83a1a072382a0b01316d6e9a8361825443b9e90e2c83c266773f147fb434d43f54fa862f62d3720219969040ecc49a9b1545200e04c66c202f905918fbd0234e009643cebca92f98f5504ccdb47423559ed87d9871fcc050a79cbe3d958a94f52b491098ec9532c9f859735e4f3d3a783e6525927d5625ee47f2686bf640527f47159e7074a0dc0784d5fe8c87f3ad0c3989615eb43b497f01a0dfb2c82de1cbdd5f34e342d87d60666038395d04d4bc4e690152bb5105a8da52c44d02d8ae4f855ccfb0f562b53d8a821607aeb34a47df6bac1b09ca19715cbee8f2ea0b6d917070af41c0db339235866a4a6ca28b7becdb454a4982acf09aef94ec077b708d80ed22624082cac36aaa1d6877850ddab16894a98e41aab58b8d2219d2d99312039e2385e3e28a7469729579d72a0a035e1a6f7e451761050671880e3544d754bc58451585f2cdd206b815e944665a43db5983204dd16831650d6d39a2131a7eab227a5caf6df3907e44655f58a3db168ed4be6e15a2c1d5c3bc32b233c82434b188f8a47f6469de0c62d5d111fbbc802fae181e571f803dc816da3c2ad6bfc2af34dcf8825cdb0aab0583c154ff58c722e10a380577dfa0ae22792f46ac1dfd975fe2e77ebbe7c0e768b8f0e9a75327d9924d1735a3da6ab683b908e64636ba74048e183c5c4f0ee73986e2d48d540c6513aca02f9aed9582a9616906ef7a3a14b42c9d89165a614506ab65d92122e3066bbf50b5cca158ea80b989a0464a40ba6a148c60252a9dcaf5ee0f60e208ae83cfe74f8151c746c4c642e63d23864fbe94828e89d5585bcea4f437ca08b4a087d3d9679f100206270d80f77e142f4c1426a1f264c8918cc4636f21dc3c012d84d59855079446828cf2e23b0bbd7cf8c718cbe8e135cf302a78664d8e20b881a01980df1209417897de716ecaf387d42889af1e6744584573c374c955779bb871e9b131295eaa6e6356e774fe1c5c5161d082a046dca1194dfd30d662efaceb4c4e782609bf29b9e47cef4bf39f9f18c59dbe86a1622fd135ada457cbfdf4780aba1cb402191adf675f221577fe7e8f5b1b3efe54efd1a0f4c0b104bd214c1f34b6e11b42e982630d7a4b983e68ecc33741e90dc71ef426a61b3476f0ada174712c5555cdb27f2fb8bff61ce0a41ee7b73897d86def16161289681d973d1118106020ee11ca0723dc063da22f7b76b2080de807f34480619fa0ed581e77c53c6280f842c303fdcf8bd6e3c74cc624944bb7974c1d4d2c2de2888289b7b6dbefc265040d176896014ac54ebb2ca4956322c044d99f35ef130cc34613582708c061ad5187aed7a82b2057d806f69bd5df396462b98e4fa0bad8537e7be92c3a37341fd82a1f2c4d12090571d8723ba8f68050c498026477ee2849c4e7894f01779fb9f687436bbb5f15eab1b6282e920a2256c9a7083a1d45882ef5da5e1dc5194635cebd5869cb5ffd2706e18e033291656d1f4a79a97bc0227abab1472692acfdeb5d4926eab4dc810a0fe112019a72a4d817f161153017da23f7e13aa357d24facfce5ba354dd4012548a8bf8efb0d74f64884bdb24da3fb476437eb6e5c6da08e849124bd58958b84eedd05213f3b7a75b267974b87a4abab6e0be67233b13f5b02a2a959eb4902187f079f770f92a17337f857f1b7256e2c47becea686b1095db95b72f6d3054488a1563c4af7ccb86d414bb70b4d31758bfbc1d5d1edcf8367e8d72d7e5b3ca56c39cdf5edfedca0c34a3bbb1e8428e1c4fb8fd85dad95bbdf8c996002cd6fb895f6eb48f52bd1104b94dad48fc1fbd60c618f1da180b725a1dfe710043ecdcc46baf3dfa7d73ce7192172b016fdec8101635aa91237d4aa1bd6b2cd74b8dcb75aea19faef3f42d0411a72700cda56b828ceb6af8ddd2542049d65725907baa0ea70826835add125e864ef087bcc3023e60391db909f1d193a9a189f809adb27bb3b15e425e512d43220479cfa3b5885b78e2fd149439f89edd42db20c6bb54bde0100d66a82890e071841638281c39505513f8bac30fea393d2ac23e32b181ce2403ec3f16800e742ab3a11170a713ce451960a8c5ed409f3ae43e3e6edaebbde49ea2f2ed6ed9b6bc9d260c033ee14c0b85d430eddaea42db0daf8e876c506bc66aaaf5e59d11eeb87195fb62a7e68e5c8fc632d656201031d9f140ea361c2e11e6c1f86192000b1030e0f717651b8ed9a9c21fb23183505d765ff2952424bb9a0779d9172e848bcfba559f8c06ef14e38943241e6dd99c0a89fbf07e39058ea56b55a7d1aae72f724822cfc0e3a5f6795aa08c9ea8dba33f1916e3fd891a36355c659d01c8ec839b1e51ef75873919501f748f291cfa02ddf76b04dea4e2069380bc2eb68193ae22104b36ed3afbdb85362bd02ad21483b25a1f0e01eb744cdf481a1580ea7da32b556d8485f186b1dbacabab8f2ea65221e140b2484788bf2a96456ce9df5c4c174b906a5b0df801f20d5682b3592f1c971bd17a171de9aae25e31be92ac1bad2214947366d89b2c4f81c90749aa8b0fda87f735a8fbed918c7f1b96c631d1916682ebf86a478742428195c24a51d85372156b137608093db3e5d7a447da4ce4a3dda8976527aef943c045ea2e3b0382454d831f90ae864ff92e8b45bed8fe4cf159a5a36cc88ed1892d8d68c2dd96c552c1f6de06be8802d9b7dfe69bf5283530158d7f635e129eb4a02026b2171784934e46e015b8a09e52cb16766357b024d85382165b3bf4edffb1378b7f14bde3b141fd1c477392c3cc8da3d257167a1bf6cb3cb9cf257bf6b48cb2b60447cd46cca0d225b91d6b4f5b68a940e4a1ef7394a8c760bf2fa31641b94a70ee22921d1a7135b60153270a69b1175761a677c4ea563353e311d2d806e0ee50d5f734a8f03a2a553cfa9b15ba1dcfc6dc2ee4d9035802fb5974a67a10d8cb286b3b30c4f2b289d9dd5593a12d72103440f726ea186f3df08b11f493d83487acdd254654e93a75d2d4bc1a405bb9258edc574a1604558865e19e11098bfe2830ca768a5218cb942caa64257cd00177e812ad38e82c78f7808a441fba3d25ce2178cdca2a6cea161bf60ee009af673b5f2a44caea16f40784755f45273ade457a199577567d47b611812efcb1e0c097c41e5b89839782a845b2e3344c66374da87b62ae671f6722873a4b26254903e56ba86f925e6fce9da05671976a09b3f10641a3e5154c698bad0ee462a1191bd3a263a8802b07c481798f30f025eedd26b33a64abd0e3081d3909bc5fd4deb15a044ae985a1a8f9419f380ee23e71fa230102de25bf25c57a615252520e7cad843be453d71652e4c85c0fa56e600f70b2ae4f3257d99c1a47e67f9321ae704d6ed2aa0990569ad1c071640574da4ed630103a3ad3205f7bb34b6e53e18575e5830c52ac220fabf3be5792c25212c89511f7fd9638ef1fd4cd23fc2ff6c7deaf0be02edae1f3d6d02182300989aaa1fff2b59df019842c55754f61bcef0e8f468f34f5629f948b6c179d31bdcb8c4334aa51d84cee1f683a272cd760a1cd43e00c0528774291739eb87e55d0966995116e119273465bd7be3e79b766c1eb3c52e299e9b35f2416dc758dbcf62f31504fa7668dcd5309b29639f3a453da778966a2430e426c906a0250b4a094a41c1c20747080972c1cbdc72185b68ce74dd5e586e7f179b2f85e9536647ca35ae96b2d8008bb19c709db6998b2b6326d5857d65f72f036b923ab0fcd004a930026bb1e7e5f701f2ed295969864238943cd00e5f7a2238422d8403ef2fdd36c060344f043e3fbe336c0cb26ed78cc21e7f8576e7a6241616fa9401ce22cdeca27843cd9575248e408b052937f9eab33f8349c316397f6140ec607711a3091261e13fc5af6ca9ffc8ba5ac23426fa53d6e34c9f6818c6b2bb71016bb891677fe73b2f1b0168171c8ed50c4c7b41b6d1cff5161c1fd6efb7f76b5c7020400c5edaad3722ab30aa4f61a572827142e7ab15820b9a71567c7e0ad21daa2bbd532d1a7b882b0c131cc662b8cf868881a7dd6e9e3b4fa0407f83087c22fc4997ea3080150b5ffb7e762049e5797f2d3ea52f32de8b8c76b5316631616eaa421bf596c49b7a363627ef80fe46dfe2ba51fcc84c6ed66e7833d6ba761bb017299768216745a4b60936da821f0c7fd8366ef52ca672a7416a99dd1836935ab2e8d25de1a795cd7e324a71da4a60f611526bba1c1f0c9499341fd030d0e03735425e342093a6c62318038d123235427d11653b2813bc73c2a8c4ad9a0fde969041858f72814847e639342930f0a89c6a19b5745f6058a52e9a277e8115f7d4df53c73fd264743c2bd04bbe82c98044bba1de408398f6ea7c08eb17b5eb30f85cc3bddf24d39ee840866b63faf9578f9fe1ef24d689f146289c7753a1c0260703785d6d6bbc59e642aa3f0f8eb81f7a2f7da25f4e30ef3a1eaf3eb86d8df04ec99915c41da85a1856cbef0e3293c88e65009b1d14473efa09d4a318e418034af4c55e5a50d815a503c51db83d42ba5da4ffbec1a971d05a2098ec8073f205c4463230fb0db4299697f010b25a8fa93ebe6a731606807e13d1efc8cabe2cee50b53fe94038f499cdd0a3978ba0df066280df1249c8de7b6f29a54c292519dd05be05e0053cc4262a38536b9b21de830ea55f3733aae04c29e52597885486982a382a433895219c8a1073d7b99f1816036fa049f42a437c583e7fe383f560114d1008e542d9dddddd14d04c4949494999014fd5516d069eaa91b82931d8be5260a385922aaef75a50456eafb0001083b99f07e38b07630bef3d185c783735cfd160c28beaa86822499da13a2a96d8a2ce9b10ccaa87ead443eddfec73722d3d7c6a5c4b75a30713a6250d60a67daea8fd311bb23dd47e95eed7bec61180c97c35f43d73957d6701bb6939e810362f2c52976ec9f207a3a464a6b9389d256d3b7c738a326a2e4e6749bbedf08d3f1821b38bd359d2b6031fb38bd359d2e6cf7596c46284e6a2c3ccecc262c4dc676666e79c9218331e94d5e59919f5c669c769c769c769c769c769c769c759c13c13251483be80d38ed38ed38ed38ed38ed38e93128a2152a12f78e3b4e3b4e3b4e3b4e3b4e38866acd0a02fe0b4e3b4e3b4e3b4e3b0d0b0415ff0c669c769c769c7b1f114a71da7bdc2c9a8374e7b85435fc07921012e70ffc0dc47a2bbbbbbbbbb542a954aa552a9542a954aa552a9542a954aa552a9542a954aa552a9542a95bc4072c4723776b79e420895df4f53448fb179e3d6fddc6fa27b10945354f3524e8250476ab1a3c5e5d7d5bc974e55992e2653547e6cf330993e67f34ed307c441547e14128fb86b7aec665a23a9fca123301c068ce685213d2b3fa8babf75a956a43ada35a6d6216d49a6e63b3164b443a9c237bc844dbc046f9cb3f3a4060969cb3935d169534de3e09a9a9a9a1a6ea2e364279a4c2693c9545353535353634d945068e7490d929b6d734e4d74b66ddbb68d44229148a4d631f18dc96432f112d312f079b7a94d6d6a539b40a118a2946d73b6898e936ddbb68d44229148a4d6d9d1d17952c34898f4b66ddbb68d44229148a4d6699dd6691d9d1832665c2895246d6e76c99a826cdbb66ddb46229148a4cd3935d1e96ddbb66d23914824124965e5a2717d060a62b9275de33b5be3d8b66ddbda666f76499b9677dbb66da3315de4cb9673bad909321afd8b443bd2c9ec77da9bb8437de7f98efb4efb8e8d4b25c98481c9e49bc6c17f1ad5afcc2f2ec79d9eaca8366ffec64f12e381d5c752c4c923e59c22d1ff68e44e9c03ab0e015ab02c3ec1c064767ff31af1e10412fce0a40b2054218b033c28d4f7d3299145f7c03900aaa73a2a9498c117163b0358b30bd3fdbeb3c107d7d00dc3a3e50e58cee5d8d7fad9db3923bc1ab09cd311dbb9fd767db1937b6a6ff71eed8e90e525dadf97324b278ff83dc77459a4f6cb6bb6f39327e373105e904a0e58cea7f6a318aa0b742c228f30bef7e45153123e08ffc9ec4534720fe9a7c09a5d98fd3674fbe09173553c6fd7dd576b51ba6e0704ab9db6d5bdf7febafb27a300c5fbdd9df7c6cac0e2a82c014a85612e1500fefb6b65d820cc9cc46486ebe89018d529cdab6de3dc39e7bea98847fdddedce6da152ad4f6bccfc422b03e4d1323f6691484a48e5acfccdfc4ea5f64319258d317a74c7efe38391c2f7e07b4afa06eecf5dc617f9f9d5c5ca08dfa5444a86017cf3450c087cea657f942d7f34f508e17b7ef5152f8216d86862c21b1b535022055f8412247802881a257a90c2a49e8cc00a34a898708b1a55ccd8842e90c0850a5a50a30713c507349090820b266a0c61564878e1021a3b366001136c606303d263b21633a3809867b7fcd4ee6dd7ddd86531734531b38cd5b5500d3e66f6f6d1efee4ebbfdbd97b93b2f737777f332a77860a082dce8ce39473587001b55a63b7b77f75d0adfbbbb76cdbb9bdd29be7d9d692fb2d7a226458428a38c123e2fc9089fe74419257c5e92113ecfd9811046096584cf73a28cd257c69c28a384cf4b32c2e739cf6194cf61c9df739823e3f39c28a32c9564cc89324af8bc24237c9ed3ff8364e676eac6c6c6669bbcf7832dddaa227c5685e332c2d7553c481d81c813295bbaf556b532c2f8b125d5d277775d7615523655b574ab8af059158ecb085f5781c5bc9791b2ae966e55f045abc27910be58854bf8ba8aaaa55b157661d785c5f85f8d6355382ebb0aab2faca55b5584cf0a42eafbf74fd609818778d547359aea2ef00acb72943a0558ee9d3a2f753c7693c0bb163131b7babf8a7ae92204db626a31642b0d12e01cd18a10d654d9838b10d7c28f6d3149bddccbbddccb4bb2ac8d59e09a1d916814030d1f5b36d5565f8db5a7617d221ed127e6d6c7e2c2395e5c3497cb6dda5391fa308fbc214bc31e198958aa598d6ae5522beda5565c53e54af1c2840b0d462f98bbb297fa30b53d8da4e4d1f4a22ef8fcdca1aad82aeb506d4e7eac92985ab5b9444935184b98523ad722290c4c263b74baed17a61a987a306da61ad366b2c2d4019a6a603299b628a60643cd66aa31ad6ca39b19d024c50c500f3a545b1253831bdc1c015caa8d46dd1eaa8da52e9561722da4a9d19061aa34742ac3addae026c334bab9a1c56059d412736be51b9d3b4780a54095b1307580a72ad3c4d4aad3e95467a85b4551e56b209ce75297aee8b89627a606c38d4e85e1a6b2d4ea54df0fd9aaade8547e7ac5b0d3c5cd6e4c7989147927e656bf91b349b0988aae90eea7a89309d3ee523bd336881a5ce4df8037d547eb10e264d94921464c253286c964e19164b995badbbfabdfdf8a8aae65faf95bfa8f152032ce4977f168995acee2bdbcb8b88c46522492b2b1bd20f499fa1ece852199452a6e9678c170061c71d7ed3fe75ccb567b60a0421896ef85253987ccfbd6668c3c519f3b197f9fa2bab61ba4570a315e8626e217659affbeec86cb617d95b255ca0d1e715d2c2612a9bcb1cd4ecedebc130f446aacda06eaf2102ba49a91fa249490aae87b2e478a01cc50df6b38eafb94ff3cf656ca6b5fdf8cfa5e03d5f71a55dfafd321faf74234856a3660bc44a1325e4465d00f8d419dc3e1c1a8366ba416d54435f2b0812a79d82aa966a4aee321d40914ae7eb0de474739e7a4a4da5b3aab4b759cc4e59c73104208ddb7745d586e9da31afcf8bbae9dfbe8ff5ac4cb8585ceb97ddfbbbb8fce5c40bcf7de7b0e1f7c0fbef581db7c5f627436442229e93ff768ff3e5fef6511bc20ee3de7dc730efa09dcddfdaabbbb8314c07787104218a1433ac4eba35738e7dc8c20d6bdbb7b7eb098fd1a8b680996a4debdf5645754bbc79711c648e55583d92f8520b26287c3e17a0877174d3ab9e0dc7bfe9e7bcfdf73f439103eddb9bb3be7dcdd331f8ce48b6805cfddbd5f3f7cef457fef02a67fb03a70f48da4cb0bedf7de6bd992d2c0eaeff0c1ead48ae9627777776bf0fde37734121d9db96a47611385119be6a880628bca9486ab0544954654d9c8ee10ebd77aee877ba632eef9e153cff1d0337e5dbfef189db9b4bfeaf5fcee2f3ab3d9ca8fbdf62beaa15319f77efd775f547e05a945514d6e8dd80fe61cf3a2daf594e5ae2bd3f8aff7eb5deb78ebe3ff3d5bc7a40df9379ba1a9b2996b5ad9cc55292ae3ded5755486df3d9569d2f3fb9675659ad7cbb1f74bbb280d577f6ae6aa32d6bb4ab522d5d266bdde5d1faf97fb63997cdf0ad35526c34391eaef2a956992ced054d68cf0d0332d835975c5e90f7ef73f445573cfeff6c69ac2c2803c99a7797f81addcae3aff102a437ce62efb100fbabb3bbbbbfbe7727777be86983c41eefefd9e3f738a0c2cc796e584982c1ba02284b5a56a5b96374060f680fb2c6fb33774ce86e732ca7d0c2e2359515675599807fb78e6073451b29450e80a5915150ac9180a85d6a978d87ad2ee0fc628e58ecab929ac1dd785611ecfe71312cd898292120ac1801123060d8fdccb7057a8ce79d1ed1f18a57bcb398aaa2acbbd15125dd7f47c44313314039aef41389292a2aaca39e79c7318e61ede98bd0215ac60aac0a9a0602a992a44cc8b1d888a89a9248f2c0bc33c9ecfc73910080565c6a4b80b8bf1fc3099d967ef7adc87b7d72fba1f9ea20942c1623e9f6b82b018a6cccccc1c7359cc444cedba769f991f1217a379b10ffc3cef0feff480eaa732d5225097ee30e2e34a580cb73f185bdab40caa5be25adc57565f8ec97675ba16cf27c78ad920942b05eb7633a3ba2c9906e3e22c1482016361ec37a08a52421f18312e2cacb9eb3a8818076b8f564879dbc354dbef9fa1953adff127f85b9f1c1f8e83cbe883880d3ce21859ea66a1d92487d6f14d7cd880c383efdece3090177b10b2fcffff57c145ca97ea8849c308bea889b975e30e1eedac0e8ba16134ea6be98e2366884727235c5c3c1c982fbb2e417ad0c01575a3569926aa6e9fd2f75896e0428bbacf372acb125c3061c4230e4c4d5621474cad3727424841d42148c952fa16a5c8deeba0de5ddb2cc438fa1d836cb0e20a149da12da0eccc950edddff2e8110bf87d55f88d050c2a7c20fcef1f95d9e7af43b07c30912275a987170c95c1443135b7830ea22a433d111aac470d3c7a9207111ebd8a068727a1c3154881b8c38147fccd52ab0c879eee747a779a35700e8aa228aaa9a62b409c6ed6df54d3415465fa3773e1113f0df25ac79ca3dfa5c4d4664d1502ebb57d9a3357e41ba94e34a933aaa3c2899d951ff2b1c7a886bd7c23d5c276f07faf837fb564b6dbbb8bbdd58f619487ab4e663d5cc530aaf5705566c675f07f31905e6dc14ccb86ac6b814fe32f93d97f7f839a3a85123bd52dc562dec539ba5d7b60f8cae25b9566c4aa32ae17e7d036d003d7d45dd977cfc3d6aa5ad9d72ae5d1b5a56ba12eccb5ec43d15e1b39608301968d03f0bba43f246f60190776dd4020507dab91de3da2fa41882710e18e881f50555052754088e70efbb1cf95ec748188de7d0863165ff426e203330a0ec65a76f7a52ee3a15909cb39e780e450640737c4df65fe261e2d93ca2cd7e47ef7f75e76ecbabfc7b2bbbb0b618c514a8aaaaa252e795d18160316a345f7f7dcdd9d6a3dbac1a3bddcddddbddd5ff676af4f5f7d4de999168394d5a93681da94e63302eb1ddd8b6368275dcb3e8ecafde2bb2a55cc1e3b5c53b7dbfbfb034775cf0a8f579d36b34768af77c0da75cedd39e71cd576616039e79c734e52c037980ab797171717917c54f6fcf9732c263e5c5f9f35353534f0f3c5175f6ccf802504fb5febbea9af359219df3629af351029af3508ea825eeb23336ec4cbb235766569ac8dfd5f199a844f35296d504dd29835289d91d5f8194fb32347b0fcca7be00f70048d6f137f6f34608581c964f994af5183e5254b0d16fa66d448d25bf71295cc2b8dcccda0b5cd0a0bc9af95ae2c99732d336a50c7a395cc55ae35b2240d0219b406e29ab195065d61a12ea03446cf8c956cc6ab64aec64a3b6b9bba33421f2383513fe5456f3d2c4614daeab0508a947352a2acb7aca7c83a27832aa799f2d587f8a6ee5b9fb293a16c196849d9ac2aa7f9f918d9a7aa9ce6fc1899a7aa9ce67ee8650d51ad0235f43132acc2d80f513e613128fdeeaae052b5de9c0e09d35a6badb5deaeaaf5c642a85a6f33bd8c041313749b6a23c1a4041be8c6025bdd8731947059e8e0876a7359e8c003d5e622e5b5bc4972584baabb547ec50258e2c12cfa90b816f9327bef33cffbb79bdd06db506dac7aadfaa51ad65585c2623c54521e0a5ba6867caeeaadeab2b6565686bd1c7279de7aeaab6c08cdac580c65613650d72283b65b65f63cceb5acc46262c78cbf43bbfc1eaaec04239e85267e4a86cdb4511dd5e8798da5f68dda6dc57f8f653760606259d4a4bedb6b15231f3e7f1c551e55e68bbb099b67da8eea28afe14859cdeb5e75866a887ecaa33c88b2b623dbfa2f25d5fce56ba2f932e7d77b58a9edf7197507d71f20245314b10384648a927b4e134e6a7fff3bd430709a4766ccf66f067fdf61be37a717d48626207b735a52db616de2c26ecbec9322ca9f87a2211eed432a2f21a6ab4d9f066a2c529dca1a906abfb5cea93ec093ea2e249e1c01f6fde3154e477cd238fc3d16a1ba472854973694d2a99adb9136b349e3e88fe749ed8f4f607c5a101521a60622d5fe5c8147fdd9712d3dbf3efad191f1112dc1626a32b6da75e9ea700efefe8f161f279c636d908290039a4b8b2433d71182bc176f873ab7a0f11e95f5f27af892e4d01e10f5c9d1a7b81c0270a3feb64423064200effbfdabacedfb3e8665f0a9bfb29967aacfed60be87ef64a076d5d5fc29d9580c4dd71f0b851451f870f54121051455caea30f9304c73c917352346d4f72ed3a40c8c2c51030e0fa82f100a8162c2ff28e91ab13790aecd0c8fdafb89b48e9184d345a2ffd1c845c8cc0e34fec6e84f7ebbfc96fe5e9daefc8032902e0087ead4078fdaab4704a22653029ca35f9c0d9c6301fc007ed98b7e88f9fafd53e34f1b5cc09f7b22169f8cf9bfefcb094662b3a7eb09e4c03ee88f54d07b86c4c4bfdeb23cefbc8da8284f5ce1092daadb9ae9a8c0411035e5f9dd563f6f7932d0877ad011e4578098ef178502d907bd85fd487910d53c293462318e7da8663e4c0dd08aaf12e47989c57864ca07267ec462a284ef6a64eaa23daec7bec7ac9e9869f13f1a0dd7948720aacd5450a8070ffb454d79e75a34ec237603ca0794c978e88cae3a06643f7e4ae679989ed94c6825521ed7630e4208210c6532fcd83706e4ba641713be0c7fca6bfc28dfa0f73c7c944f01655f617f3e9eecabe7410fb11bde7bde7de6b9daf38e47120ad3f34d2596f18780f063efc11ef416289b6f392c26e5419996427938d7f279e75a3c0f42c9e683f6024dbaf2e3f3d77bfef2d078d12c0bedc3cf5e425cb5dfbd5d85e1f744c05bb53eee8753b4aaa82ae6568a7a18ee2cc6d6a672ab2e8b1e31eac61e30e29a7a0b194588b790524a29aa548c31ca2d9438f7c54df530d31e920a7faad476237e23b58d98aa7ccd0812b55f25035fd4a61ae4c1dc7a6d0544adc9a07c48513795ba42f6a05125a54e10de4cd12ed5e4576f398f10767d7f5fd4f1e85d376c571e1d7cd9b1faf854456fd848519092d4297920f8f835453aaeaa89dd2b930f3f46195d3af8fc7386bcaaaaf8f0a5942b65c5ce0367bd6772771d13eebe5afc1e2b5951d5acaa8d102e101b75e166b1b89f21989d11bdbc2ca009bfe8407bd9c13bc5e66c3bececbc8f9c1c8fd992f6a671d010e1d1d41cbf8f16659712e69cd105ee1186d8a3c3189d00a13716acbe30f901c977a02cf350ed0155522d3e7462db735955b7b4a86eab0a52a5745654ccf9620bfe0dce111d0ccfbee89493aa3237920070c0526ff14bf81be4e10a48f592a26eaa51dedd71b455c76a542fe7524eaa2dda906a37aa7c4085bf12888d1adf799545aab5af887eef1b245373db15842dc618df488dee326edbb609a1bea24e2819d7e49b695785ce4910b6f6766f47a2b285a9ed66053fc138f8a790564c211cc1d43aacdf96738a44a691897f9364616e51b5dd5c10b6bef142fe36678b88d458c5483598cdad5d451e21e7117b55bd3f3b6d51a5a47c29e50629f564343120f0657c2a6f702dba7d78f11bec3a11d51a2c6abc274c00b080c1104401c0020658f80073646e0d929a0b8bad699287746ce0114c0d2fdaccb3a9918ab935c230d6c3c1b0a5e4c6b2a99e58364faa27413e0934e8a0be57a75a4bf693a0003f80b544e340c1a584a9b9ad7a4284263b30db36b2965437ae853f09539324d810685985ffd2dd0fb7d92e346475b3212bc4b59ca47c4376d7860f3b51d45e3a93d5b6e1c30e14b543709aa48785e55ca6f56909236795926ad54b261e42f8707e8dce49bda4a4e890ad9aa83625618410be574885f0e8d1c9a3194ec70ce3e8ef17328386e30adddddd310ee4404e492a70a76a4929bfb34953bd4875a75acc2414f27a5c4cf89ae8abcc64fa5ddb7ae9b0acec06e3e0af963e17f3ba81bdb0dc0dcddaa9fb8c833425e1468f6a13788f6a3b541652f955b40ce854a65afbd8c1d4a0121e1159e1d1157e111baa1c1f9e2493d2e0d6fb6be2fd4d42fef6de3caa088f36871c78b49a75aa4e95ca34e745b57860e9d47d151f36d4259b638b70a3e5bde111e4e74c67dca9f65cfdd95d9995734a32f96ff048f67565d6a9f773681d960e0fdc16e17238b7dfd4ca712d3f982c6fdcd86ac7b5ec5f57cc7d0d6e6cedd89ceae2a0e4c6884502e126d9239ad3c9617d1d0728e68abf91ea56f8e658c139bc83f068658f2421adcddef06817c0a12578b4afd2dbce8ff9bd325de13752d9b456d4e57f1eada96a2b37565e2ee05566dfeef7287bcccceedfe3e7a8cc7ba7970ce60ae1d1ce00c1d2fdd36584233b348e7e229c433a8bb32a3fbe9565d40edbdc2a5f733b95666b6a7aebd6ec91de81a63752953fc62375039fc3a8342e598c18aa1901000200b315400020100c088542a148948591aaf71e14800b7588406e5c3e9807b32489611c06316390318410420080088c8ccc900101ecc02562137ae00aa73c7103a00a5467cd4da7eb64ca3bede0adf3e1b58cb66f55d9a1b9d00710c69421c0ab99ce89f00d55c099e4f696a18ce0380e7e7a38587b2b55d120bd6865cc968e3cc4584f5a424bb58e4588392d5f3906be32e8fab3a6a9b4f868083e3ce9aa944d32c42b78fa526ae0be3326d629bf8dfac14fa580050ab8033c07fda6fd5474056bc64bb0ed79607e7323499d62dc0204766d7ed20930e8e73a40f99bd81278a1f3b934f43a68be3d313e8710e27839f6f5c57beb83c1f46129af0e31a6486f5cdcfdc86bd50c25089a6d3ded862d37cdb9c35e7d215ee4891656a30cdcc596f156f2194a309365e1963c4dbd7a79893a3b050349b00e3dafff629eaf5208a92940addafb4d17c40d5b1c13b82772fec1ed198863d2815d0b9618a43708ff23c2e98a4105d98061b24819c0f8ecd2847c43c221f1faabd98169aa0dc176f9ad460c84ab08e4b7ae35bb484d67030054a333b29848b45624f6c38bfd046ad8dd3135fc04780ea6c855dff682f4cc3b8b283c933a49196f44850c25e178bebd0f2ec387c338942e6456a6c89ffddcfe11ccb1dcc175f553a2c37573c90e22290941b08ae8399080d6cf6c9168e8d7864ff93766eb47b23adfa8d60950c0748539c664d29da68d4692628ccb65f60d05db7fdcca77836fd0f75622e58fe837704738dc5ecdc55caae58d120eff9ae172b50ebabd4d60ada6a809cfd8c1d878937a4131e52a3489642bdefb272a3ecae270e01817605be5f39a5e4b09c114f19c9c61b73dc9c6aed456f55ae191d84cf1395a37bb66655f92f4f14707690b9cbd9e9530f74a2361180e480e45dd1632f4616166b2a250f2344d254eeea81cb1cfdd5f27606a2bb4b1df3e062bf1ee0a5bf34f1b56c30d322b0b21d9f48442428962c26216394345d936fe851f27200f12025cda125ad467ea3fdd7f449980054003a0d645df662149e096f12916ebfecb6154be8cc26d0bfae1fe6cc7b95bd0eea4835181ad646842e593dc3c3b61f00939cecc840b0b248590e38a08c6944350b484362bfe66e7cc18c197e50ff157412c6f8ac3d52ce2337eaf2ba087acf4868ff2873f6cb8498babe7ddb6442add86e269e4063bb81ccdbd92a74b2ab26f32522e3b8cb50f9bed254900e5f6aab6aa71a2a1431042698799e3ce0502292be5f76555a4c97c74e6bb37ea20f2b0f070fc2f9cf13425255ed877821a83890397cf839783bbf0042c2ba49b64b36644a71727a3227a4dacc2ab2e21aedeb02b3afc2c2a24e7a28a71ddeb470e4a09e8d8d95ea510897107c9d56163b16074d6640efdbc1c57aae2f70c70f858d1cd1839e37766c58fbe92fd0810dd794829fdb26002af37a585217c92c04860b68b0458ec1ff5699f0d381d22fa6c49c888fe6b9603547683775bfaee1aa0fd812dbfdf732cf8671c4a4d2a98feda940bd0ebd635bb43bd829d421873edfe883669b804c8115ac524fdab58749df327cef7db71b0a1acdce90a5ac67957395fe8b27f42f09312c6063ff2ed0c2334ddcb5f04d85c0708fd9e08340ed2ccbdc4a5b25ff48b8799e34c07bbf04c0b2ed3fddde5c722ffcf8e525b583fc9502fe249472b65a142c7cfc5ec130a18cc5229f8e34c440dbc9e341ab4e11cb40e5c62c42a625bb18e05987ecff8124715199683a908aaa6c158bd77cc6fc618dc00464911b5e6645cd03bfdaa41a2f370982367a88596f1b7faa919176149f891b57b3de5f8965c886446c29202911c01b391b041acb8750a8ab97dc82ba518aca3981d0dd5e60c270675d4f77b3c28bc1637ac8d49a5e818c0eb5e2028982642cb5255d4d831fba44184d43dd1a19c2d52e619280d510b412a0f68c141759ed2ba590378a309b0829de9a8881b99fa7e351ae2359ee82517071844b4614b4ea5bea04c0df7a1740113ed021384322c524bfa25f202b47a99b59ce81a6d4ef3e04ee71d4eda7cf7153dcd3fe9d76cc42395a545f65595a919561377fb9c8a5ab342da16ceea4fae99da635ad76a7dd3276ef1c76bc57efacdb9f67364cdc6b4a898568c572bd760ed2b0171b1af8a09b2ce9aa9cca8d93125c958f5ba7cab572c9810a6b483a0214a41a83ddb5396ee60208d3ba7376b506bab9160e9b02d6b72e62ab99fc7324a167ca4d620ea5c7166ff8e5707190f07350884a5f335b6b048b13993b516395833ed832e18c46de0c87ce0682ac582591afb5396c2c13ffeaad83d3fc06fb6ab55783754f1dbab42c3fa43bf9c63055f258a17e6eaa0909d2714e6d64c30b8066e8c4b9ff66f44d19cf54a41f1719915b8c5fd1b2dc6bad0c420ae9b4bc9dbe5e29ea19d710ebed9b6cdd058ff41252542c6e76c23b037e021c7dac1b4025544feef064130a818fbd3d9d9e18962df020fcd20481d06e88dbd916b7093ee13ab453dfa20f49023640384f5d6c76ab3ebff07671d928be101f1d82be47a8d91f7b713d6bb74f7e30f7e9a850030b3ca1d14f896f470c465c0565311e6210ac8aa76614fbfbf455b9bc8d1ed3ed24118675ad4b871d5e7ba30827be70754adfacf3fc3faa41fb9624082d3ce3364eb650e1fdf1d2af71f5c971c38f329baa0cadfaedcaee7d92bfc755612dfc0e093f5d662ee80ed95d1a57467afb6e3d27265d37845ae8aed8fc63d907a72f6f4277af06cf29149d9dc690ebeb23499e184494f732521a7ad6d146b91729593ececadb717aaa00fc7a7ac9a50df92b2087695334397c8b0ea88a69bc6f47ddee2889ef4c8943e76b2809fd1401e4dd87a640cddd8cb0f8ee675b21c97b5386038b79d53ad1b21a0db85f4c90df75e883805e566dd0230b9ace0feb5f5b2679a10701410359ac97519bb13fe01b3cf056cbb9d73fa1d703235ce3de7b46486c074ab49b17e3844536eaefd4d196a888d07c66120c989b7ac981f65a7dc2f26929c3e428c918e92d7d023b6d713cf9df1aa40bf91f8941294007014abc944b4b420d93e9ff1e82bf34efc58a438e52aa0e6e0364d925e8373bdba4c044898bf49b854b6cfd820138f7c35b3e5a99136a9521e954cc59b5184d11740b78b0a19ceb25de891e0605be27612113a5db6a80c177d8b0389fb70d3153f1763640c6eb107e04a10619d43349fbf7ece42d3adfa7d3622118630fa30db9642acb7115d3cc64fab7639be5db92c6050a788ec048720947d570035c8cff3b19473106c136ac09f4070877486248002c7cfcd022e154c4386bc604cb32974f74e6f35a25c7ee66bc2a64a5637f73b3e7484f881abfce9897284661577f6e4f8c262a70510382838c3dca3458ff4761f776499d23f3e577fb256d7b3e37b4661d88bfcf7a1872863dbb630fb1c4ef0fa63fd5dbfbc60d4ae93a0cca8bcd796b429a056766140bd324be8ea8b22b25f858e01d2c4a2d5584a37f595bc746ad777231be2e1c26f280f4a06c0c7041ca538ed959e7ea06aa8fe67eb9ce588c2bc987dcd530ca348ce00114a77a870ee7314bc7b9921b8d396e688be69c0838d6695998b0af4a4d4388fe04c58b2368ea39ed4aad47793514381625a07e678d1f938f3401c3de4474ebf8742059d247b9ceb13c616bce8685e1d91e773354a4d9b8ae0127c44bdad813af92035830287fa27e41810d62227eeaf17b51410f0b881c7909eebd8c73d1788dc234c3ddd0ef46d55baa72fe0c88b146ed01062e6b16cb737cdbe8c6c84d008e0686698ea2be0660dc99d3db8a48f8fa657833db16a3e10d649418ea04b44160869a5209faacf014a68c22fe52c05964b16185166529463afdde998a2a53ca89edbb84051b711d9013984e56bc3d4ced3065b0ccad6337f1ec1262c2e7f4fe1fc22bef5fe718aa38d4ca672dd01e0c340562721bd0aab60260a451b8e80f01814a1e5c8327f412dc984f2408bdb360e49ec2fee956bfc6e16541ca7cf31d5cb4478675c54721bca0a0d42635886a4b112feb2ccd69ccb7646a2528d59ac543670ed0a04cc65faa16edcb199692b409dc8341d15361502aac036acaf443c620800505abd86a5487193f8e869444a5d9e3b549623558e7fab91ab18778afd79da1124480952299a2979a5c7f211f93b383f78798a7a6814b12719d8f44814d01c3924886b7e707e092200605d608569624c6939021c5e72190c9de7faa173588aa6a5dac0a38fd72f4a9d70dc003a42c9b66cc509786635670e0283420fc7bb2670c888bf467e113231ea8992e51dcc4d02d8ac58c0190631bdf45e2c7b90832d4fe26626786ee16a62af4b603c79067d813914dd8b81b5d55fe13cbb454882acab894085afa31dbf3fae0d5ebdf01c6a8e472e86184e5e8108391abf7f168f0f60aa40b719717e682c3c730aa4ef636808d54462350187b96a402f25602a442ccb6aab2cb03ddadafe5ccd7aca9aacbe551c8a24a2bbb5f5fcbaee2a8644e85fa0b7222d117cef5702ffb687b28633e9428be32aeee1caba6dc3c5b84e767fa5fc7f5ed7d245910ec4e1f4e9388c647055b288d122a3da8f7ff6a787649d0783770198be05be26fd0a6927f9f6fde2272b8aaa3d439bff9a191e777617de38d61623fd0d656e311ced655fa3c74c46da2bea12f20bf3831fae325f15a44dee59ffcbe3b723e0a6b2f09631b78fdaf1e9d86e4ace8734892afd4347fffd5218dc11cea009c6aed8fdbeae7a35755cf4ee13f1709650f3260a8245be2156db2f900efcd7b993292a0278b617a2c68c01a94a5852cec6b6b4e0c34c8d9fcb96fecaeb933b355673768b8db64ed7f5070cb654755266d9bb6fef9803c333ea9d4fb81fb42ba398a3a51a3890c2c48fce1f881f67c2117f993af76b4b2ad1105d4eaf39eaa92f1c12c79e0c1d12001e72b18a8dff185636514f5cdb9e815fbdd880b1c0f5192a1d65958469ed9a71602761b9ffb55a0f1e2d516c8c9c62cf82bab2fe0d478afdbb25bacf0bc208700ad0f5065f26e94618f4ce1b03c1e0bb6118591afff99b8de57b0a42dfbba7bbc8912f7c3311f15b5a50230348e92c0e1d028aa30e25c4a35d93baa0a3a4ee3857b7303eeb6c02882e3ee0fa0f5ccad7e0e2aac38198de7eca47b5741e578fc86801b9e8eb05e70d626992cceb79215be1e6672ebc6efbb53d37e9408120c824e50711cfbfc15ae0e91b9989b7f884aef5e1769383f6e67c06d8f5ea84738c0a3f36f2680313fca0a8311bbb048ef884ee417494e3b546efe30c0286494ed8b5d4c14a881e6600a3235c5d6c9485fb8e280a16fe710ddabad6096b43c3064b141cb8f64e40bd036196c27e1e6673e943f84da832ec601eefbaba8bb66b25aad22f41b1a834a27a0c82311f814504558511d48b9ad4ee70c296e091161c9d09fc177a75f3a7350d70690a7d17d95241328779e1198261a1199a34939558457bfba8caa2bc8631e1801468df229de11147b41ebcdb2921ec34372efd857d319eb7c4aba086ea98d46028ae460c5b6c8e5cfda471b8d352c089fe546a2e85d12b57e22c58e33a6ea6e61849cb3f82c6c91bcdc965bdfb5b1ff7e5ed3f5b07403163d10e8c03876e94755cb4c9472b27b4a0f611fdb5dd7c20e3c0ed0f285919728ab02fb68ad3eb7db454b897412957cc2ecede7cb35daec49bed8ca272608c9fd0c102a15c803bf909fc3e3e50828e73c1d3bf9ac9b0f6e889210434f279115c45720c00c1b424634f80e2f2f41db23b7a67d13cdcca8d073bd353efa612a9480588f6f03439172770737cff03a1e726cb32a62563e2e91f69bf0f8d550574390bb708e6ff3135c25baf44e7289e09ec954e3b0d750f3531064cccea243f9036976983f27347e80e44780f9421f6d69a5267d90397837c6e011c023def795e93798041d50f9b3b9c58f097dace323a92a08c6ea45d6a7e465750110056f85ee15915b7e938d879a6f0b00e28ad1de887ef72b10530b0d6259d2f06a95ec633aea9439385c51dae195d6112558c91769196875c700a65066a83f5e8a0dc800661b64ae0150e357d72ba9edcd9cfe802d3e385a209490d36f77fa45d21922475b1e1fb348fad0dee24998aa1e6a248980675d7d7428fc98c686b672926a751085b24aa0c44e46334b708d074237a1487568ca9822006dbf61d12884a4805e103f6781eac94c68784e6e4d125b02802e056aed55581d2cf58a264f5a4c4fe849b36301a2485dfe9386854afce4f037a4746bbfd12272ac8a01eeddce31cb3830748155dcb2cfe87ca0b01b86ec189e7452152e1b73b404ddfa21c150ba3531b412f1b74cb16c1c10e1a6e0d9c06c910d90dfb3bb7ccc6f4aceca2235e80557fad85bc619c985914a39d20122036ec28e6e7fcd6bd48733668ed96ecb19f040debf87c6050c591b43c426383afcf6a4b006afdc7922fe0b80f7fa50bfed6edafe9488c809e8aad8a34d0e8609bac246acb1b8e2a34af35dbc6fad598f968e584aad4296c6c1f3e6fcb7494e1c8f252eabf20f765c3aa7f0adf328768249714ca6b6a0c4767a48543d9c6b8491f088eefea43d4134c26cd6c03cfac3e22f242dd5676401182d703e25e31411e0216b9bae4a0cdbf3eb48a824f3f2a759b2e43f7293d647651488fef6e545d74d8290b6d7ead49726be493fbe138a5021b2f9dbc225ff9d585ba12555b8ee19f4c06599adb91d61fa995d7a1c8abb0849a8ea8b60aab1962ef673c86595d37dc8390131639e982d06b700259eaf65d91c4f8c8587d90dedeca54e1755942a6cae8a567ba605e2407ad06e5b59a23d7766d6a3b25b2701dfaeb775d57ff4c554a1708d76dbb906fc04414e424b9170951e7146e5423933e62eb89e84561680c9f6142deb504180af499791ddb5a7ee6ca1915413934f2a8b1eed42443af266bf670fe681344c1331857a88d5944c38af7faa42236c62aff0772a0c131c94592c4ef86f6977834ab78311c6e2bd6c67ac5a1ab5cf84b002d49678faeb605e32f1269462d58f7f6b5f92a86507b151801c463e71a9e508f17108e7c654c55c8ac754be86a784a0c25650ac492be48a23d860062d4c1f2a25291f342834c6f5808df8a3e8f46fdf5e629682d09b97c7d3724d08e8fb2c8dfab39c441dc611016e33970f1ac9b09ccf180ba1047d4d30fd4610105fe77700f5f980085ec584d810269ee3d06fce7f81095ef26622b7ef65d0961fd1ff998639365a09a2595862e69a2fb1b62374e662619f5092dcd02abc83d1dcbb86d0f0f99a0853261bb3b7baf435016f146f7e07bf9a3f35d605b409cf2cf711e2f6a1096fd5a0dab2b72e74fc1d5afce511b35c98c2c8da9a12970977e6d4a8e7d8fc5f22a691b89306e130ee1c2e517804909244d675c7acd4abe02f781d5eaaa9fa9c729b82e061bd513b04b806bbdcac1bbc856d39e205499502f7bf28d7977310a9a4c9aeab286dab83db8771e324d38b705e656d0ca04c51187731c3e590dad3eb95af4699076994575c9b4bd175c76b12738b47a66c9cdb4c3f9a66ac7ea721edfaed5bdfdd4df474e36d006c6ce3d4367b3098d9a2240829845a6a9aa54ec8f64cbc59891df66269da409faacc52b02f626da69bf1ec4e344b644428b9400e972e6e85806c3130a61208dfa5095540be8888c6f3fc4b892c4594386413b410e59383ebd173d543a2e98db82bc2ce4f2acf610aad507f7c6f92b0dd2313e342bbad8dfb71b3a4827d892ff740eacbae81f1ad01535d1ccddaf960bf6d1488a3ec18ad22b45f9fe8327212e024eed4207fa0f5cea64718fb191070598f562dc060085db967740cb0d5a26d4bd6ea370a243c974ea22ea6439dea0465141e3f05688f0e048a5b6059787fc11465553c4151720745901fd11fbf4f7599e952c2a2b73a7cffd119b3a0c283c00365d58b1c56d147b2482de5124f3f4d9152fe60da4c7aed7ad0e516bb3d65e176182a69b51da12694e9c4f6d4b16b26bdf0d56ba67f92f9760835230b862901d22bc1df129c9d10ee48978bab8c64bb5791458240b9ac5e830fc74d476d6ef98c8b9cd5f02a1aed497e7b53743ad97b73fce323b7aa4f592a3dd49767e9234fa39bbb676a80ac2257e7439c3f21eb0104e84fed2d563b6b391bce9c513a185860a6101f9d4afd2d3a8ae1bca79ebcbc0486d3673f11f369e403b1e8bb324ae41213a5350cfebc844273a1102f7a3e7161563d53f6153df035af3e47603cc759d8bd167f4823bf63a8c7aac14f82d5838f2454afb29b6ca2459e07afd1581d247397b6b00f0eb339c1499a131c87df070e78887b7a72f35f1889def40b1d1fc861c84294f5a9420b49bf659654979c86aa97332845fdf3e83da8053e5645ebe328da9f0bb5922f1b382171297ccac865c8c20fd9a220ba5a78f1e43235f6b277a47f4b0f6045b88079e5e28626ad716231e204566755ca46716582a61f0bdf9115b0a318d9e6d762f372ee2f5cdb23829740376a5417bcd22c01d3258dee163bf3702fe75fc42e27878f48a6d477fbb0f3fb64c249e4aaee9f317eed6655089fff965b842bb499b230008ef56bd62f1b2b31a0873a15ed3c1e04be31ee0c14e6227ed3b1facc0b4c2bc0317698623c75cc46acf457aed3a3400b840776460d346ffc47ea07a45e0a6b3ec49062187da726d29566ccfbf518d0894a0b3cdd99c0064b6b5aca8b2942f71fe9b87d56024f0ed1df1d73ff348b86f768dadd6de4991bd07768239d48cf4145d46fc29071553d47b2433a961d776e123d6a8b344560d7c695c1fb1bb79195a1611052c7910b1e1fc3ed10cb2728ae5199ae1a04452cab1d6af8e76b1a6cf960d842875712f33cdeb76a18ba594228e2701ad64930315437d1543e1a5701a00a6679060f3578cb567d6aa6154990348887d6ebe608a62435e82132253ce6e1e196c9b59e2612a4884415bc5d3157a96c97ad30636a89894382a7d64d1cd5ea171c4359987f3713552f49245ce5ca16e042fa50fc7db6a4e81c34f060a5e5afe8e12103b6a503f8a3a2dfb14b647ed829e16d51c14146c3782af4dbe19ac58b21b02a875d054e6b9db184657f02de52e2c00e08185690705e5c4649accb9e228001d690b22ddb4019cc65b851b509e58ed3c22ae2e34937e720e055b899bf2673f80cd10b699b785d3db75d0207cc30e244d557ae17afb29a5646f12568f73c57d2702453016262a2616b861e4aa26e5be516d99b51e41cd9e0e5d83a0d2b78fb44379b150b6d608e5f0b7b1f1aca11075170bb68aa22cebcadf9b654ec11df20e710ccd1b4340f1221045be055ab3aca9d4b54a6e11a5f6e0409a922d036c673dfba7b5e428e8af1dddfc475ded69dc133a83ef96e5d018f9f5eef16747f2a08fa81fdb088cd109fce616d09efa8182a1527436fecfdf58b932f7e2a10f7bfaa59c29c438c35af956d08e9e1b4b12cab0d959aea008f314c2f4a59ba2622b5eb43a33bd4855c14271e1274bbea44cff9e0ad0038ec2f28ecf48c083a5e94b33f21f6878192c94dc740271a8aeeaae9e86990b47906d3131076d90ff9334f02e6f6da8434f17d207de58ba14b0857eabbc31baf1a7550df646aaa780790317bd0237b16ed14ef774d0846a8031ce18cfd6fdf3126732be6d61fc53c0f4b521923a4a9ca5685cc17252c4fd35afb73ee41533fe41888bedd8e81262e7a85ca87862f5084271813f91359d005e35a86c68f4f61191e3e999cfe70932c638d74c0c2d0d27f5ce6e80f6ae455c6f96ef29d070ab22aa5c6a39cbf80a8441ab8b649d3a20045e3e1f4c0d998aa0c2f5e4430bd6212a34b19737987529e3557613a666d2300f2ccfb872030a178635b69d146bb5081298e2d273d99c726137d31ccd1104505cae38e14112432e937ae2efa8c962a6d8d28ceb61b931dd143283729509e61c8698fa63c86da10f6f72d3b065286004b9f8db5c57e4d9a3395931e91fbed26143934e5dac0730fb4379e47cec2424603fc7fe23d68d3770c1ed214ad192580059c875e143ca3bd9f53e26a42e4c2cd389424ab12ada83b25196c80c4e1e41a5f72744df4921e8de9e264df9e2fcf04ba7205d24e1d73af5d11f67d86ce86d865c8a70a2824656222b438a303e3c117ceac86e985080d45b46c4939ee6b8bbb4e21400d5299731c1f3020cce55dded194e7c3d086b0820d1819e4f501c561538de13c28052a95527caf8a5feeea7dd3f9a721c4b1d2cb5f8cad46d110b4ccf2b74d69c0a5dc18cd54802c102f921b65fc1efb1478988b26bc300abc69324dc87d148f6c842d46b3eb3e866a9f102188b74cf140219eb8b4db4021e5a5188cf272e30fccc2037a0237737f3bb23941a2255b1d1ce2b46e50539c284bb2906fb47786a4a9496946febfee6dd5dc8f77a1a004f4394891dc74e591361cfaddc17eb9b7a2a37d6ee62d530754887e902535510f5d24cc518b15ef6e47ed4942b3d6add868ab5954f5ac892a22147f0005b87c50690ab03138c718474ede6567d12a983a00ad0a56fad72c78481c80fa1a95030e398bfb8ee05881a4708e6549c21cf2ec96c50916f46faaa7a3c9e20a0c2d9a9e84dca4a8697fbc10ddef46899385ec65e22ba408c28b86d7b514c58ee833a233e53b84f305b44a8dbf737546295b27dd93fcf92ab8fe276c0f3a18a345625e11e0e1aa142a7fa86742c245aba0663025c9f18658fa179263400d4987a3a347f6a7a0c20aa94aa2f620e5c6075adfd2cadfff74f5e4c9bdffe66c81c4842d8af93c2242aceb1fa93c5f9a760af24222eaf32e05825ca12764df9cc87fc5ed12e2e1f08630e4b4c182bee26041be70a8fe0f7d4018086b9e257c6ca9dd9bfac67932400afe2f83dccbea5cf45b5578909b3fa15c82acedfcd0dfc647799b10974c68db6846c0613e7e15b4bc81af719da52b2e2c46913b3a608781b75bfa4a3c5dd1885e773ff1c5c32daf3fcfe52fd2fc415ef7a13b7b5529e726674389a874e768d43afff369887ca1bd144156b9a7f85f8c3b0e032ee42ef3fac80d7552d373243637651ed555708971bacebd5289947e789c0be0dfdc3ef9332052259a028eea9febb3d11ef5a0c19fd179771f57d700e1b9e820ce5494f86a298c2d1555034bc5337e8e5ee468b756f6032e6f8223b8f2eddcc2ecdca926320df4217baa8393eab2101fdff69102ea0324d01f245cc1b5327ac58c4592aa2c0dcc2dadc2220a5c9d25f5a21e8b182221cbdc05446496940a85d48ef7de27159dbfd08c454904d6fe510876b307ab976561e628b0266242690312101e197d9b3c079529b8d005ff072d8bf86ef063c3fafdbcc2c9f5f1466789a1031fa2b45716b5ba788f242718f5f37d5541b0b00b51bf0b1d84450a3b45d996a90ef923c6b430548a60a2a19d3f7b811e9bb830e4a2db4cc73b73f8626b4ca8d9a7fcd3386a87475df5cfed805185a56cdb7cb0b19cb56de9aaaada4e737016d8ade766ec04ea83f9d60644f9d0423cea0433c7415b1ec893241e80326c0f54fb87349b0b434e23d5b38ab4a40f61883c5b3a553bf4a2d2ad68739c31e0483aa1d15f13934b45cad20e050d7682879ec28062d82b58c8fad8f05fd11147685de03ab36b633ba7d12a6931aea847b6200010d890832a6b9804795a686b7e5035192c9f3b6adc68895b835b8f34448da50f143bdbb2e3500072a6240e386599ff8eac15165c9a179f5d91dc1dc1e90721a0c0d5603735489bbb2508709535291f56a378a40ad3195906fc7d29d667c610cc80629f1c95981c393fe8e496aa34b2151b66913e5ef0b165041790590c18dc704e926c4553c50bd90f378cef8613d4056f264139db532f1c71cc2f89a755a9c71eb0de71d26386fc6be746d2f65f14553fd8491f0791e0e26c1f0c714d3331b645ec4140e3c2b7a102517ccc26ea2562f11514d0d1a26c9cba23fbf870949b67a298f8c4e66bbd8c09c755fd110410b5fb303adc2f567ca28f4df631be02279ff6d42c1f88d7954cbe57067c180f7817ea2cb2c60b269b52bebba8ab5cf83e7a022786e09710af82f2ccf197ca86fe84df0301f307e1f9df56c193a91a6e5199d2df61d61dc652cff078690112b4c9e7f51bf03785b3247f60d429eee5e710c51b6100096881e30684a0546bd45673f7425387ac67086205e73af388f4f85843bb1363ccac34188b20cb8e7495cbf626d575325c6879efe5086063d68ce29b5f85d0a9b744a511e7497a24fcada529371bbd1307336877586582e7a064f01613fb4e56d66eda2e8d55d4691fa163dc3d8f286915ebd0bddb238897c84a2aefe602b11660b46369fd408cff16f8025f62e33ffbe02a1d7bab423821d443bce4c83da3e9699936a1271a86724828fa670a0a8e763c825a6011bc7bfba3a05c26fc4553be6912e524bc7b7aacf6d47dcebfc0fbc31b806c1c16035fd1a85eaac051455a1d47bcef7ae1cdfd7e9ec9d8340124c603a527e4ab8a7d96ad982ed8ca4076759acbac57497a73a220ba78cb68a5079f03d9dc9aee0d9ab333dba3b5548688d24fc2f1d476c83dda66bc640e6a50e71dfe7588d62412531e3d905550da321c2eb7702a019778866015857528915b8b6a682e9bfb1a0b5c8c4c9f96fe4f7ffc3eafe718590e3fe82b0fee058f0cfb3bb2606bd919f05b5aacb754a591504f8471919d6e6dc84a22539dc89fc5bc706fccfbddbbb2de5ee39f7a766ef363287874d58e993332fe20338e3b29197e03d5e0129bb78f6e5ddf4ad08d6ea83fd222fe793c94ecdcd6b377da70850995b92d97efc631620c1803150e08ca1e3203d8b3afc1c18a4d959a95216e9cf3725800188dda5ba9826768a6041dda8e45893801411a091485a39252082acfe2ec344f4aa86b619877e75463150c52a2c06d609037dc0286a3da74bc30cb2ea4311a2217f19770771f5260d9a4a4aea03775ce3810432b7ce032a3205ca902d7ba11012a15abe70089cd3dc8e60ef0a3a087e41ab1f1747151616998955fdef5aa940b0e847d7621819b01a6e8fd338b45676fe15d593d3f533dead3df18ab91b162663cd2061542837a5ff8595428aaac4ed804a92b2f4a926590f4b144f3d01ef54e3c6b7fb95c806a843b6f2401f0ce3362ed040b9ac5ba23355bc5ef745480d6158942e5ca3c6997f97695eb37aa6c6c0a7cf743a64c1809a5cf49a3ca9693ad903f1c59f41965a171319232e26938b9881ab0de9972d8965860b5fb6d3b24b7964dc751f2c07b85876ccdbe316814e2eb8124870994c2d70a2f9c602f06c098d2e86bc6a555e336c2d2645bfc72d3862e3dad1748d1043418552a24cf685841e1f99a149b121cbe4627c27800a921840163e606d68802174b31aeaf2598cf302a1ac007e42591583ca3a52670054097ce20b9e6cc7865c789d6498dceb80b786fe5a25b4215572557cfea369cb276854fa20f47ee8bf566d8653f07d6c9b3d2891db9ec155c6135886865e580a34f5881ff6a19c5875f8cf3418fc2775639060b5472bd3c1f07d923a83e396407229c3d561948dba2c3e94a0b7f0c14885bebad52e7c10151474a4e2d0b157906ca1c4d2108c8f76ad9002b876a8c1ee3fffb0b902daaa4ab68357fcbcd1c840b4c67403a78d07f400171b548411da2e8e36bb23c93c06066089b214c3aa3e5797d9545c0036b8a6293c1c5fa29934bc661b692f8c6353a1e524835e87a4e2d834015bd24e62a7e50a5c05d14693873dc1f890c361287876f6bc936561c2e29b192c956f3d72af1d32d6d38504df1a2dbb3391b59f23500b2603c869cec10b74e50276caf948e3c3666201e8f4ce29be5807b86754c76e6674ef284ddcd1a393a402284fd3f8e844d4bd2af7f64ab899cf1c2ab7b58df854abf849dc2ec118b8ada1859bc5a5a0e28d636b83eb8f5a7cf46af5b74139b269189f3200e029605fc7071153cd88c7b20463e96ca1efe2480fbf0375a8595ad604dde6413de5077d37470b8e969c327a5759132681f12890e158a8bfdbffd42bc2d2a637d36b20083b9aef43a8d9acf5ce8f78dcdebdd07483428af52d9cfe90027ca6948ecde4f2ca4dae4b8bcc5c52032f59e2cba58c24403a4437ca57865098ab7c9a064ed831ce7e4c5ac33b5131ddaab44e3d8e6715f23a6d601c8db17164deb8a7d9e809f8216caf139f7aabc5facc6b08f2d568b44f84d5f1d1130699b37de5397d493823ddc1c8b7550d27fabf3a480e42f4a625098148d973c9b6bbcf350274b10188c396563e0caaf3c5eba6d44b0a892120f679b88400b923de027a77f9f7372b758a5cc309d8fd5e58909f49b8d2870cfb93d358a71f12c96a48427a6fe8c085e64d04b6d207fd54b7413c039075a8bb7dd78fa9b8c3312e0fe7158d29229761e58a103061eec6df5e07c7f1a25d3c7815bd0ad4e14746b201e60a02ed656bce9433b49f0eb7a0c18edad64213f5d12b9fe5b580417707a637b2a806dd2161f4d77b3db2d7ae9f6df970a8fd8a78dd84fa9afb0f312c65dc95ef0eac7e526156998c4f1ef4f4f6625ba38fe0dd6a603c63f07cff632cb6a29f693a741c19d969c76984d0a2120d0097e4817bf3230eef13b300dff81db9f23a78c41e52f97cab4506ec4be38d2a47130a2163b4639b716c836926c84d6588e5110d0b28a597c784480a0f5e0f946f647fe2cf1d250412675202e13b01f0f813a6a23cde528ed50133290a99b81601078c0d1d454e6f560b5c95344d4ec95be51b60547e184c84de36d08bef6d63ef31a18a579b0be45e5fbc524fcfa6130dd173c218d9b59e8f097e1dbdd81efe5741dbfdc2329b3d78e2798cad84c07be3199c89b06e04c6ec27b79d979f5ffcff3e3eaa5790de05f71351b3402a7fb01462cc1616515339a7b1a61b6c4b79dd14dedbba4f8d16bd08ffeb732d3c0d3eebade3f5d25afba43710379991906d391a910b598dd0fb8ae545a64804565ca02e2473fce61089c6baf1ed5b1ffe70b2fc270d9cd7d37fd38939e6c351e48102bfab3b395d141ef5a63c8587b4b979d8ce4bbd06ca0a81575bbf5a52b7f0cc3f105bbaa84c25671e0fd43e50456a1b3991e2eeda12bbe7e746487f6d32616558f0d892b344687a5db3d600dd1b8050b3c5aba9b28b9c6ebb5656e9004bc0cd9dee81f56b14559102d62fce0c7f25fd6e41df05996c465ec00f4e4eed35beec27e3e8a1a18f218621db9b08c6fbd7933d1986e1cb97e1c50a98327b90d08fbdd1afc7cec764a34ec866121a08483bd178af11c747c37348e61d1f6d8502b4fbf8280fac00e72c2211b6067b8c5fad3dbee8eb73d66fdeb30b8ad9388e787a842cd903019b25bc406a382ff1b86424a845ce438e66d77f37a0be80021a1c22b2ffae60ba80f106381f057ceea72b7b058ffab2c05169144d9594f04c2f0ad6cb1b9f5ef463d37fa88be00f21ac1926b67d4014a6111a0561008ebf4dc8e7767c159dfd7c4fc6bc0971e85eba605d362ab21585adf24b462c4917f689bedf4954ceb05bb858174d5295597e0bad44c879a2e706a080fe0fb2c25fce7af2d5efcb0216f103fd4d71911b1ac10babc1774c7b4d326e5dc8ef3e8722d4a8856956f407cd283cb8c2b4c590310eef4e01901e4723e97e623c6d84ca00760a002f9b3c08406ce566cabb7fd602e1fb091ab75718ddece22481f8b9680483b61a1a518d89d5dad2be51add4c979c0be31d93d3370612bb959a1a081e95e06ee263c86cfaddbf8a38d4ce989c64132e588e953b240f2c455920a5aa1406a1851dec42402dbaaf7b45ea3b3c954ef018c6f1fe54e580cef31d6e9f93f72b4b00918ea688d9cc93f6bd42f70ecbbb49663917d411779a07b71034202a113372ba8cb4d2c0b80a94569a3fad08fc6dbeecb17ed7a8201eae762ab83d9b7ebe7895f4f40137f80af8f00af3ad61eb4ac277e2002bcecb30eeda5abb0444400ed6317d552e0fec61a82205b6353f01a2b71c8ed4b6bd8ace186ed97c745173afcaea1f27657b28e894b89e73b6b67337efc61e54d0e9d9a3cce46166b35453a704c9fa55bee46d0e0dc2b571709a98a7ba6000c80e62b5281b78391b2082e37e3905d82b06c026f72c25e8d9874731bb301b2dff60310b29401dcfb81012a9e30c233f1943814b90cf5fc50adbe4f7f3e36b49f0eed79d105728008ace0d2a700a4a06a7e0ced6d8d12c3899b2e25485bfac89062b5e0c034bb76814915c53a3f5c9dc17221e5566ab047d512374e34f47ff6640d07921940126f0d68fa0549b59ea8d3641f7f1ecba06fa9ac0d920b1e76e175e50f8e5f4486f28a35fb1d91303df43bb045cfe1ce5d6377478769b5b01adaea042e1c2e54804196c418794f0767453cc0aceb4b63538c1da1754a82cfa015d73b99ddb256254a518fada7223499950f244c718200ccd6fc1d23c396b0c975f7bd6fa975df9759a9c02cdaae39eb3b9d1d5e40e0227a8cd2272e1541abb871f5d50f906ba9a48dfee2641b08d3b0f244212255d0bc6e137b911874ce28f27fa4c17c3b851112701b7f32522078ed4471dc27e92f35601d677b89fa4ea8e9c06f5193e1830c4c0ffb43a83511268ec3f2be51717a9e0e383c93a824fcb63091fc92f0601c4af6a3239fe7b30152988a86befb0b909ec173da97bdb6a8cf27e212dccf80c7415190513ea7a00da4d7aa575222badbcd2bdb199612c24b351b78be7492a4bcef17f84ba5be83a94373bc2f59f865e390039268fed1e0ba0c50d65bf3ace85b005202906da79c2aeac29022c8ec2f935e054892a6a91d2acf39c0bed8f476af8f87bfa674af517b345dd7b8ecfe727048867e944f68f5c9fa8443a850f93ca079eac0ddf13df11382a368a3fa5fb2da4486cef15f16f045fc0cd9c22984a33103ce56fbea729efc6679eeb7e85bc9dc25f48de58ff24057b319ffa2db6efb930da5e28582edb3a771e99df66325cbe90fd00bd5ca12789cfc1d8e8c400ebf2b2af74879960347f46791a72490acca44ad119af99a47bc4b02ae86a878de0bf036b17a69936ed77a47ba4aa5ddec477fff46da332041ff1d02a315b368db90bab1c235a4bf513d562d0f1627d318f4ab3c62f808da58a558840376ede86df34aedff1fd081ff980556b71c14edac6e5bca560978e5ae16d9bc7ce7042336356aa71b824c5ee2ca106b9868abb1b9e3f39b9e796ec05a27a3ad377ee04942cd64a2abdf7b8e33b5cfccb2419833c6290c6df3bf2452aa4e426c87e9bc11e389ddec676bf1c98d26679eea2ace7d3c74dd33cc7fb36e81579d9563e2262d7e14d4cc60818d564eda381521e55dc70981cc8d7a5b981c6385d2dd5c59869658984d368f4095befe6088f489b5a7a82b4002ce750a507fe758353e958680e5975c901434509b813c336774bd220740196f7a65ba5bc759397b1871c6f547286968915eb90e2ddba3d0ec6519305b942e4e16bd925eb8a2398d8f00b5d602a5bfec6d2a1b50303092263e23fbe3f0d2c0ad9c39426239f60ca27610e9b0aca2460ef842ee72f4371e96f7574a338d54bbede4b8bcb97f2b9cd737b0a4b39bf2197fe4e897c4a80a4aa9d814074ce3d419a2620fbce2c8810166011d1264523fd4b618630e60fc4d84b96f5071816f17316596dbbd949615cfbb0c142d4bc32b4525dcf62d632b62e044b492a77e7c5541e22593d5ce7154361c5602488a519ed15fd700eb3e8ed0156878774bd27530095fc98e46b226e1778a2f28aaf430cc149a44bbf374bed30bae81d7e0bf349403aafe58c22008ce067e4b2714d10f95027662e9b1932fc972dea43e5142e64d5d2b94b034a919645a18c4d0348369c3efe7086a76866d227cd7382ccadc08aa9f5d86a251296d92a4241a2812f011c919c9497094e42f960c0dba5cf0f6817f37d88afe9cd6e7aa350b3cf9b3a099cefe88d37434d875cfa87c2ec86f4fe145b0c9a470a5ff735a4ec865f2b580f178f51dfd054d430b680aa11f9c5a11dacaa07ef971c114f42dad8e9639348f70a88994d4fa2507847ffea030088cab89e6d8f5f31835ec40b781b84d61a819a846cb641791328abc980dd5d4fcb9f687cb0df91a9e3b01819164694f40ea02f6cb023989b6de30cfc9c63cbacc4dae4f5ad4ef289a6c8632736c0b3a82c49ab87eb6129b47654311bb06161b3fe53c12d679b07811f98a7d984e7fba0f20f8bced8d5d964f4aa804e6f43f7a8f29b33520f55cf3fba1a61751303adfade4ef51917d701d7070f65a90beca38be32393beb90ce71f0cda466603d601f0d143e1eacc8770705cc4b84b4134713e6b1d48174871a9ad384b9a0ac0fc52d0361bd19a3ac3b1453534eedcd26b974d1bbcba94ecc5b1d0051619ec5e4d62b325aec532d18ccb22e689acf21852c12898c45ff306cd91881f5b3fd56cb9db26df7f872f890e4c928eb5077a0bd0469e339f50578dcd1b771118369dce3efad948eb4174d93b2d3f603a3016aeb3cff69a0152beec80260cde047b3bc522c6dffb4583ea3fbbb9cc42f859db67fea8d7a0dcc4b7f83d21de6dc4c322a7e276ba7eda8eb6053eb37a974dabec406191bf0a8612d8c8e10a6dbf7ea929d1e5d10b0d356fb9ff5a68aa9a901a75fbd467cdf2640496d81da8c6b2d0196b8f20156deafdb2238b0702b1285d91a062ff7577c61e717fbb3e04ff72ee3517cf22bb4dd9238b3f09364e7323576b7270ca4cc10bd40352778b9adea81d4be041f8ffecba5d3cbbcc85593baa98eb2306cd81d1e30a6b7d8f5898e3aed429debb3d6f90118b8d758af58db3b2affb08d0dcd01541f3601527e1c8539e66256a74de71c408f764e83903ae001733fd935116867252bccb39e7dd1d2ce8dc0d9ea51f209dc560ffa70dd2a6653dc1c54372838f292da3f1651b635faa9d467527323b7b7e0de097ba02a6ad8982af8495c11407f03a58d5f4a5eddc88ea798f736ceb17d3d5de4fdaddacbc13fad717ccd5da1935a6f99441d24ef38c73c3d12fdf45c871cf6c68f8532101f3ac77419ea3e004226bcde5ff05604606dc7c1cd81c2a8392bd055f60aecde4fb6134b678b0b08a10a36099f4a03d50e5e243e8b07ad409f439d571e287b67b94183c8d105df7acf18766849e021bedd89382255fa2541f67b636db517ec05bdd1d1a47af627f4d4622fb18adfc88cbbc835557253663542f295e006788e0db77c7e95888fc07d9c58f63da098f70029c8085fb7c02b118568af9f5c69c9e96e1d66044a7c31627ead3796621c8e9555f4725eed13c924ed17196c43b5232078e5b4808f15c9dc78d13051671f70e6d88350ee015550ba93ff7648f2efc36ef500160920bf009b12655f6051a5061645a1f8ae4d7e6915177ea4d143e452c3d3d485889707a0a86452be417a5fe447f4f404b0b4248ec2912738df13b5d52a452d0801a88cd3d3c0c309f836dc22edc2145289ba31055d6fc2ee8324fcc4864c45ebfa3fde42484d665501533db52fc48bfcd5b3ccbb424fae030e2c12e73ba1803de8be5b1a9cb9bdd6f8de2a4f937432e6b45942530e8ebb84073b4392b90e5c1429d75640259e7aaa9d3bdbac5bc419a050ca72d9d0e05ae4826966c535f76208c2153c70db828037ccf720f756a2a76fd40fe68c7f55a45b03cd4e128261e60bf6f7203521e5a651d937d215a275d7de733975934024800be91d05cc4f3313593c2db3e33149774118e0cecd8ee7fd0c432b9c2ab76849e56d7729f3b63d5ae384284225eb03e6da906b53c605004a97e983a28c781718b35b4ca9a53577a92cc0d2bb42601196b76ee4ffe45a16c7724964d0700410af9d8f838b2c0ad2cb52190586cf1619cf5855047903bbd519ea985b855bfa8dcce444f71c8e6fa24764010202b9c5cf3432457d1475cf124af8f635c10be4152ccf9171af2463da00816e0ee261a1ef2f511b8955f39b808f8a9eabe60e9164a12045949a8e865dc8a8fadb40542ef685dc10126450c9702009195c42eaa43434f78356d7a61f91cc6f9e53b8d45e2c3a77a9b58b067aa69018486ed0f9dede21ad221a342b1c64969fc67124d600bbcffe8c8a1b66b113ddd173ec9444d3cb11ea85793c435d7daf3acdbf135f34d51bed882814c29a538f82ed5ffff063834007eb045f231c83a2543e1d2c70e23924d5c234126891c959e1335b53ba80cba50afbff22d2ff0c628333bdb15729f23daf6793b2bfa8011542fc4da4a2944ff5bd6b12762ad2a3c9b998a0f628cb4c595e387c2c06291daf15eab5eca9403479218664d74b2d78099b79dc329d0e32cd76396404f238c235501113dba1eec009ff4bc5914e2b06e65e6d6c306442b1ea8849522e70e0f4ed0151b8524614eb0cb3b3ade4bc5f7a8fa1184aecde17752ce97e9955f206e2f30628298edf8f69b692e8054c14b34d2e759365e0e058c6e51d96716e3edf9b6c3b295bf203d4e1a9e01cd9a7dffc119bb02a21400a082fef9ac72b2fb129549e043113594ee5353c0149c10008854018f13cec1ad25f0d5e5e3a1082c93a916dc827cc42f165c45ad6745b072d9b255b06e1da63df4ad2867e59609315b9321857a97d2bd2b25d80326f356022c6b81291c65b26688fc0206b3ab43145c4db7c3bb210028d2904a0a1b9f90fd8b086987d91cce4c6ef7ee04183b66cce213ff575512dc47fc3a571b56f531fe95753221359dc1b580bb83368fa40002a2b3559b54cf9e5fd36e3d0f9affd99afb21b8d9236b4dd6e47f6ab2781837697ee4023fd7e8be6fcb788b02338cfeffbc2ce914e263239a1224704ec4b46293905a748cc3f21057f83d95dccfe908e0a4cc06398854b2c5f0a1fa478b5b3d7c05b6d7ba4235eff7313f38a37970a6c65917ce82ecdef423d29fb88e90ba660cfe056d42d38666a057131d4109ce1eff00c3442329a9018f73125cc21d5a9370aa8d7852a075215a7da371569e1430d18df42e8efa755df7db1822dc19cc369210eda084e0ea6a0cc8bc1e4350ab5feeb0cc751e797f874544e6d09c17ca8ab2e15b241ceefdf7599a1760c9f26bfaf399cef1b902495eb8ccfe1929b5019170f165720410d6834d369ccd3455e746ffe74a2e6b2cf229c161f3663241ecb90bfda2d904ec43ccbf6e91519ac463d4bef157b0020a42470168ef99f77762906bc41c7c6778cd2279f442a1b7315516eaac5082b5afa6b567ddc50ba53427f815dba12db643851058d617dd649d6391294ad822a7be7606620289426f412edcc6c9eb928afd4d8f2c175bf9cde8fd6c9d0c504c29e39c6d087d95c81bb46917a35169d3505d129f6c0f4d94474418678718bc7c1b49209f3b93ed18a6a7237ae7cae6f74a298dc8c8b605160ba0e6f5bd394279a7e8b533c87a6954cc8c7f58d2e2a93bb71f3697da289cae46e3cebcc5de222b00f2a56aa0cf4da44388342b4718acfd1582913f9dcbed08a62b86b3cbacc6e3131e8875ad2bfb8c5e3605ac984f95c9f684535b91b9778c8342d66a94b6c18fbda44b84141bc71c5cdd15899893881a9350c67dfc6297eac067a99a94970a953231a7a7c88a234ccafabdb42d4c1e7aea07d553f844e20754e577822a66cccf396df93c41fd39eb53c3e2e952521480c95e955ca234160d00f94007a179a113d816e899e4083a267a0bfa17c15fa333410ba197a10ba191a0c3d60a77f72818a7d72838a3db9b88afd1bfdaae8267450f4273456f496a2cfcc05127be6aa12fb77fb2af4676828f468b6ffe50215734547d57d80bac373bf53ec4d327761b3296ff68be2e6040fc8e781c63d9d19585787dee711adef8c345f793ff163b38d32942d8256f13202601c65488e39235ab8feea708c13ff18bda5fb3e3fc1e2e3cbfd9c096767de664b644a2270a40c47d8b9490c4568837cd7cfa388509e1ac2ea149a005dd4cd1ddc194f0c1675129eb9d8f9d2226c08f8a50511261139688a5310fb353938a5d8e0adbed8c484747848d15b0a3ea48399b0908c4e6cb1879c5664c0eef81db240db17235b4b7e09c8c7fbbaa6383d4a681938662df7eeffa29eb6b7224c7bbc8ffbb1261da07840d269a91ab5e7f9b9f753d0822d8c7fb1a4c0b5af30495eadcf246fc800ca1b81855560f12ab078a9a9acb53f6598ad25e13080fee638a5600da006a081af0b0834bf6d11dbf4350ba8d6e99562a104dc9243e0ab16fea1343d9fa48ae0d90800b7f1188a4c08a46090402db3cd3b70fe897e38d83fdfd1080ff26cbe83607a610d6a31a229c638d1585520a68c059a19ed84f573f39f371a8379e2ee6e1666682025000c2aceb5f5bfa0962129c8f4010ec16b873457620989d23002cfab59fc1cac70c6c905631fc748704cd8b6ff92f542155833dbc039475d42b3da8333b3bf1d70e1e10c90cdf5b7ddebe099514ac28746de433d19cd61a9b4fe7a1bd2b1fe5598f1f7b5a200b82b3009374503a13f21dac07b8ae6c139a26a4fa62013796c9fed56e9283a1022596efd983bd0da47ec525d42d7dbefbca03479dec2e1730e9fddea9a4f6f5eba5ca4b42df521b51f0c3354cd7e889864cf1a5b0ed9b1567b421d0b7c06edc4bc6ac116cce0fcd68448dbcbc5648fe3ee95999635534543d2fc67993df8c0f0ca3c2df43d998c9f72989f9fc453169e5ccbf37f53e4a011acb13290a68c039a09b042458c08311100daf8185da0c386e935d88789ac5ce8bb3ba0b0ca4ea7886494d618c14bbc4e2c5513b95f51878fe0e49586a11f60b9bb013bf5014c67c87a652fead9be89279997faec80adfafb1e0aab13236b474ece92a8245084c3084a1e32999041f3121b7e50fd80b3b93a56ca472b3f7bd8a2866b375ba58663175d0adbe549e6f5c27aaae24488890e2b0263c218a0bd29b7e0b767f7d229d5e7ef64ed5a4d1c52a123ee138af40553d92dbd2d5270c37d0faecc0dbc29f59a1f483fb38f2be97e22e4f9928fb0e264a116b4550d6f7e6e9f7cebffa921ddac839d9979d52a643401636e8a98ae178f63c1c1c116ef0cb615b73775fdcca99211cd757fc312ad5b5dc6d29e988bc71f4e6ccf6fe0a6184f50c848c37a7c9e7ace20c16d03bb9319fc7690885a0b2d1d8323d815568611a4bd79a35354b850f5e0590fec429aa305a6465ba059e9bb46321b05b207a806f1e94d48b6f2d6b868f2f06f9e7601cd750fa225d954b9983c8df417dd9f795bcb6d03c5133a5797331a021e6892c6e462a972ec420a6420442d55ee0c8349c17b0b531607e5e734eb968b591bf21d0e43a41f9c54d1960a73629cacd636f89ea2837c667664622c0f36d22a023294ead2ea31281d901910e1dbfb04530b586324a944da76cbc37c4fb53f65dea70508bb7b29e8527df04f3cb35a48a45a8c7c706407ae1b2591c1a4182a2a07dd43672841c4864fe6204684858a78eded04581d46dc1a6baa43bcfcbff78e1dba680eee92e6e5d23b04cc4964d792675f700ce2f44d30d8226e638112fc3187cf725da723687bd5187f124642f02462b09f2ca4ff3c5024aaa2f16cdc998a4c58347f991b06ecc7cc4587c73abdda61e88c6bd14622b5c302c7f3bd1de205ac7488f2497ad16a22dfb448f04a82c2579aa970112f5437d7bfc0ed5ba9e278d962b1a6f61edcd260302a538a1b8e72a9395cbf476ee5a84e040b7db16ea98ae60b6bcaef9620d5f085f48c3975efd6de58fef1796193f96f47ff3efdfe38bcacc7140ac7ccb689ef329b0896d19fe3e2d3bcfd530b8228582626a8d240ed9eeb43e880727be9163286da3520193a5af63de148023d2817b34d9b012bf5ecf8b69982250fd4986f955a255f9112be105943227fc3be5f69826be1651c20dcd5ef5f4140a14b8346d22d26af6edf882fcc5336e32572544cc5716720f90a67f7dd1ad50c34a09c1b88cb31d0d77202ca84285fa72440e58413dc7cf61fd7638416863c30549a171aa2b9dca94dd88563abb430140d5427854fb91e6d8848292097a071b4afdb33bffa17563e5860b9ba3e37ccfb91756f964e04474fb7ae48fc27b8c889ae5808a415db7a631b7a5c62b876830f4d15e618f45c092c5c30a3ee46056433f12144a5c39eb346c4b64905a1d0d491d4d491cb8aa45878f08d65561851d3eb4d2ced0eb39a007b8d3375ab7adf47c1868560a9d44137463c29df81784e1567a22e0ef09cd25c2c1451098717364be39fdc60a3124a8778fd700105d578a0b6bace3a2a61dc3986e8303a22841a8191a6f261d8d8f53a9a75286aef24a6bbc851ea8624f6ba320e54dc024087097ceda58577d6e6ad3fff03992d4f1099ba9af27843e2a055b90bc1976740e9c42ff17312d522e1c48121ba2315a8151aaf1c0e723ed68461c021efc403095e91a66b424e25780e817b9f81424f8447bed092775117dc7a9d616006000742431e645d7f95fd65e24103956d8f9f932c552d7977afb90b596c477709a3ed124727785bad0b0cea5966518ee1d3ade9eee8e8a463a540258c29d32147a603c2923485ce3e9689b3fb358fa9d980484955952602e96269bcbb8bebdf126fd33ef027eb0ed9f0a0076d4cd5df110b228bf9f1e9f4c0521507ea3db8c0bc224f885b68d889cd2d1a0d0c9db89aa29b697845f422ca287479808a91f76d281f19d3b851bfba0bae218566b1cd145173228b35cd0d31718a1d6e1e56d037da563870c97c24b9231bc1e8b896864a5efb9d6e05ca11e3782b58ed79b8df1910cc350382cce51b7f946818521348a807c42d1bffa4cd8f5f75081d3aa5cb57a9dcfa295cbd0855cfd65189e50d343d1bfb092dd5df2ff4ebcddec8143ca5386cf83f46b18b25450c49c81db0b634dc07ba003de5d6d022489ab6a24cb8ff81c5cca886885db569863b6208207b3ffbc16a96ea3a50d84f003e09be5c28ae60d632b2691ab0f8c59a3a2c7a609afbc7dc261afca8b6f12d94274def3b3f3347218517d818c68197c4770758f4b3924bb8c5904526606c23af07eb0ce3d170a380966659abad4834b3deb72f26c6812c8d60339597160544ccd5f3183bf6489eec28abfbf23bdc2792cda4c0bc05767c00286039d33a3e1f03a17472368f3c202134eb07b59c4c5f414baa704106f797760d2581314ffd26f1c7dda8525c0339d240632b07cac804df4d3edd3ffd0f6ab85025f39697abdbcaf94cc577bb383ec5b6c43c527e762f22017d14120f14a714326b10b48dc03c34797aa70e8bf84fcf62f91232f202b4f23d374b3f710b0c378929d562a8ecc5433e55ff3a0d22f4e07ad1269b643d29659b734b4737bc52ee626faa574a130ae0521b0dcfe1e48d5df97772723ff57ce240318a0fb8ec695a32819410f53603cd5a6e348c021d81e3161958fb31e4c54268f87051c9c9c19871c917bd8348ea0e8ea41981c56ffebc8730f141ba1bd56c778aa5880737c83b0792f48e238635ce160c04df56151bd5e4f27a641c11d0b37ffa422e3c789cf2ff56eed24e13f579f91fba2ea7fe6a2ac79154daade768714de98050cfe87d5e0214f417a83edb1927e8e1964a575ec1273705595e66119ee5e92bd217bfd8cb21e2751131f25680f5d03d5a23a89000bed80db91bee64711e92c53414452cb89bfa026cc44d4df3f82491bdba5cc762ed3da1e4ea10a75c2328fc138abb6115a2b3a6135cdc287e14357eebe6c71114ca69cd1e3180ebd67fbc92c9bfb7f2a7d69e34d969bb768c2e889bcee403e48623607e4deb24c907de231a2f9df6e01f6b1138fd8d2b8d755b1f23ee0a4376e37b98c093624e3992ebc9528007b2396680c09c537e245a4086d58a8d85d05f46607ab00add5ff859eca2e4004a3f94f12097248065bdc3fcf5216a93f160b3edcbfe0206a15522d59512f3e2b1d7e2d632d7023256cfae88d7b46d7b55b2ac5bd3dcf4788bb6e015bafdc5fd03c6886dd859468adccda38afa03b04a09fdcb11ce882163a99a66e491979f0033a1a0f8009767af81b8bab5f703901fb7077a2671d36daaacb5d4f8251387c5ac5676545d1bfdbfdbbfe94b136c4e88c34f7f71970ab3161d4354fb67eff85c452f08a9e9c73f88ce46aa3b1ead4ad09993416ebd9773b5ac42893b62dfffd1250b49afb189c482a1a7bfe84d7f4694342b687eebe1f9895144416a303716881645e1fc2620f53e2518a2600ac111c3ab7eedd12438eae424e850ff186bb0a8196c849eb5a526e923a310e5713b202e448ad20f4ba64ae1c12d168e8e52b1de92e1e6d35e6526f96f9f88c040c4c73f04564b57702c986bd394ddb6e718eb980bc78c891be607b93ae07b2dcf1842d5714628d6615b698509dabff82cd9a6e69e4dc2f26a893619593f6df3ee2aa4a2f203488b6e8242d7edbbd3cb171f2f3a9c2a8d852d7c5b16d4c88fd430771b2357850ca30c913360f5ab887fa24b1736fe7c2b61a219a97cd11eeacf57ee61d30db956870131f2e03186547417f9eafd69f51c88e882240183aff28838e318c46157dd0b9e4cc7ec07f359e3ff36a33ea3a3964326b357ffc39cb3a5c80b4465d93938dd2cb0f61d045045c025bc00f5e089522da5835b513887631167ebaa50f826c93b3c9b1a51da2c2123adc24d8d39a2419d0b18b2026e8c7f6d47ae809b1a73a4aefaa4ad60a41efa20380eac6c8be0566d39c56ceb4d652471ffb13386919b9b75a94ce190ab436528bc018148aaf2e09683f831343e7ef4bf1fbb22bba019a60e2e469fb6df50531a0d39d1a01a34abcd42a23391a606274ba88b88e2e9b76575113187971530c51a5418b1424716f0406f05b01538b17db599c9ab43ca155b13eb6049ef61aeafdcef6db6fc2d805e81f58cb0c9c655dd2839222e8949b9f175e08c9cc0f76fac92a146173e75b831f58ef5e0e664848a6174b9f90c9b85cb0ff5ab8db549beede657421485cd4a4a0a85822ac45ad5f185536be9c3a94121346b3eaff78aa62091b242754de579a805ca4a5e4d9c212d4e2294916e68c50130e7d00976b8b04ecf3256ff03ace4c5603e06e9da2ac1349d629764822a09c932a14440fa3af8b57207ce57221adcb52eb137fdd84793216ea582c45cf4080d812e74a3a0986c7bc9acd1d7344f08b52fe28b4a01cd8d9da4e154c864406c6fa9587a26e7f4b9ceb82fca064ac807112b0422a278f66db9ba44c4c13a9c3630f07b1f411dea5817f5001d0880b26f2ff33f911d04de8b3a200a18701a5079ffa56481abdd943d3071e54b2397c88176836f8f8460d60aa8f25c87dd4214f7039aec26985a20d03a4e075517ab8d3043898c5e74ec103e3ff36a3b3098ff4d9851f5bece81d227038fac7a38b3b5b90000dc121a75df84127104b15c875146c4832e1d4e160863b446c258ec75173f792575f493ca6f7b77db724b295392326d0a3409ef08755699434374c690cc201a044d119a42484a93090a1308854e8603c070a36186a40320e0c66e88f1820b08f8a1b5d65aab035898400bdecf7791a46024498b4729a400fad84f418504d05a779c251289427c803c4354f854007decab90802494d66b395b53d808aed45dc751a976fc27a1b55628a89824491205acd09e021400faf82b80aeb0005afd72b6da5a93c0f365ddb6c8125521aaad6aa93d382453bcd4ade245f5de2c81c769cb402df6dff0028f0e74bb92f781a6197c42d1a46a6ad850d9dcb8c1c19193e357a18e1d1ff0e8c1f201c2f7010f09a48788edf8204e1d7edea0d281946664f91290ae69933db20c235d56c44e14f1c93751750d98cccfd80b92eec558a5fa17c54e1a3bc9a6099d4f6469c96124d90a205da62bb294ff05b8a2a2935af682e62203f249777d76cee0acf6114e984ce22c0a9eaf8f9d1d2288f049d9a32353d5cebb0bfcee6507ae58d0d1018978e6421786c032e8c070b33a4eab8e0e0002f08d20160a215d2d9b46f8dc1481bd00aceee7ee3f77c02338fba53b200abe338200de79469c1d469cd50fae4018b203f671590268e570f719e3f118ab95e372f17c2b2139c8e90da0e746e847cf8d20413e21dff785338684ebfc10224408014850899ee7d952c7d11f201080041b0ec2f541822c6176279b5569e7a6042dbbcef90089d011583b3b6209250cc92f07b4ab934c3082e427090b920a550f0640cef24369eba44efa218aa28d472c663f4b99254f5bf6dbe26457741443c20ac54a108bc562319b188fff0f328dd712409f12c013269d4baccf2d0144c14b78a10a5000134ede8387092698e04304e3a8785c1f49c4044ea488890811225d9123060069cb649d64b34d5bd713ca8c11b159a893669019a565427452386324b8754e813283cc28f38aad8a0d8ac524a01f1839f249d933637fb1151f1cf92e36c0376f2019a074c2091d122428dcd88d3144140e50855243313fc027dd7f7a08f89c22000108f849a116355119b91b705f5f28258be3b89b3999d8e3e707f4b9ff53779efb71a665ce6af0933d2b8a73b0a58ea3b5c74de153e1f329c566053cc02334531554502101493a015f02401ffa0948f229a065b671c6a6908d09845a94d2e2b4cd20338a0472a5281309618bf1e8e1452b24790e82322da658803fbb87685830f0f4a41775d37f49e656abb1856e354e1e327c97e45ec950f30d1f3081d0e756a3ac8d3464bad33fe6528cab90ace5fc588370fc58a154890318a491fb53dd3a7973316aaf6bb7eddec2886eaefb76c740873c4ea13925cfa12a25efe6e28836757fcec50cda6bb04d4221e8db33dd6367396a1a2d4b1ebb06e6f463db1a29778fe92420182d298f1e947bf49894dc3e45e6445dac208f5e94dbb3d4fce8b32db99d8bd3dce6450d79f49b277999dc2365020b8a59c92315ca9d87aae4a6572851eea74d675eec208ff428956f7ea4b4dcb40bce8f148c531bcf174fa0e4b0822066607b75993cd2a42f9ac89549ee9c70bc5f3cc9389e766b47c6f137a00f0e55b7c6eb8551c6c1f91ba00fcecddb803e3737bc80e51baf027d6e4c228200ca366f03f4b1517191555f03f45195b28daf017d6c0820d7e802875ce353a04f8d9c5cf334a04f4d8af6829c7a14e8934a659a3f813e343e328a1644463d067d50a79f017d4eb88c8cdf04fa605994671e047d667e6413174c64d37fa08fa9d5403ee097bfefef0df267148311e4eff3bcef401f6f8b5cda2205b9d475cf813e1d0cf23dcaf783298292107a71dc57d087b35c60b5d62218a8c087ba7b0be52539b32605b331c6345f7006832934b9ffd4ddd90719401193f2a50645e820cb1247a0b8f46b3e9e307002c6400b1a98308622c33ef4cbbe25bb164b647f998518d9ff037ddc05595ce1d3dd9a2a972a7a456ae92209303aa8cd6008297240851724efe22231c0a398e7f4a159053995dd0b43089cdc7f412cb2c0a126f77334483772bf055b50c12cc4a3a0118eb2e47e0a6eb1038b1078810552ee77af7693c7ef0545e47eda462f4823776bc1c51758fc61e402297237be37530feebd3b215851450840382348c78a2d78f051a3fdb0228c282c2f3710ac28f3440453f331db81871e628058332174e031064c9c9560870f6810eb316382871d299597201e332c4d748cf1c1ac053c8465c876cc683dacc2c074ccc880fd18b6705603207204adb47cc0490e12d16b79c2030ea45b0e2d23d801c706cbd152a5879b1a0e2d2b8072e38c221c2d5834b10123bbd142cb414506ec86962f4f6c1cd918694123a846112f45aa1b74a82902c6060f3aa452aaaf5f23ca139a30866a92e80155835baa054e049d6e319a166889612418aa055e42305303a2530bd008c254060cef000524433653440e5f1935930972f0cc90814de4500ac3f64db143378323ef8a2d3adc2f42a52bb880c28921d45d61c6132b66e85e5126875a240c8ddb82430eb448cd6e09f222b65add32821dbac8ed886ea9f2647661912fb32d5138c12a1974899570c337ce972964624ca105870d53d0b07c01620a1a90608aa421f005c2410377b08fe2494e962372f45024050e11609b2c8d8ab4c8712b32e3a5c0a62c8db0d840064bd0103847964658a4bc152c2bc8e3bf008b1659b078c9b10406b334c232e6478053355c8171b234a2e2071c55a8582247188cb334a2a22806541cdda82043092a763084511fc8918267449073064e9520070b30ea891c5e70b785097cb7c83992418e14b8e4250c4ccdb852450df2f853f097a5511538b8218b0d59ec12f4051546497c11451e7fa723c108381c419b10e5f958a57a0093395f082e3369f6c3a00b438a943ea97fa527bc94346e128c1040b2c806a715d70a9e409f7b0a1ea9cf85a3bf053ef046f980d7d452af4fb99498449132d5d1b07f29bbd36aad0d5276767676a6a494562a9c49650244a9fdee55365ccbf27f5c3b12cbd0ac8e617afb9147d8861320908b7e47ee481f2fed9c62f7ae4056cb952fe4fa08983807ec2fa4f2fc98a0d56c3fddf7deeeeebb33bf2370bf37bde3e54269ebb464543ffec8d2260bac7220591a85016414450c287b335fb7925ab0fddc21cea234dc71d6f4a1695032497914d3c8230f0ef2286737c8a3a48dc96860800c2b4851965842288dd7b4417e7202296cd0e549920ca8bc4e595c61fb410c9a172a5e1df8418a1c90a901d0131c5ef387b86b09131044c1841969a4f1c414af15c6fc0074a4a50c1164bce6b7e0ae0bfc30451a48559ec0c189d77c18dce537c092c30eb2b8408889d7f4a662a898244f74d16207464e8c31469431b52c74008418b01a40406f2fa0c00c3e70a484182db280426b20c9342181110d0a31585c11a2da5a435c650ce8199302118931a2882aba3062c78a184550a8ec80091939d0000723242a280c8cbec0628444825ccad208298603e3b245092b88d07204450a420a5996464f24651e591a45c125db7fe126fbca40e9c52fc8395f95abbb872d84495a9c48602d80f9140a1e67963cb39caeccb2844e6e8b5f8b68e0fef76af2651113245fc9a21b8a8e7ca8ef7f73b3825da102b5386df63a6b3e7696b3b8e0bfdd6e0d34a5cc200d345d046052f4ea9f41f205412f79a3827c3191ce9a37373ffe2ac897872bf853f130f78f308334d08bd40c2c5f2c20be0401f492af25398431abcdb0101a27ac85fc0bcdee389ca5f55a8ea32bce6768d16f9a78261e5e90200c0134e14842abc69fc231041b8f25ed7190af3c23498e61231479d408c79d106a5a29a19f99e4acfb437a22b578e7cdd6e2bda97cefa74e5f7b30ce0a89e1dea178cc5d58f6f4c8dcf74df6d4c8dc93cc711cf77865c4597d6b917b1ab6ad45eeb91f67b5cd1d7ce9269b1a4e3deac779ab79f9e25ebaeb86d2fc0aa7af093d80fa132afc7116f7a7701e398b7b0fa442e92c1cde09d29f48f3e794996b20268b7beee20662e2138aa651600a475597a089c81c0d0f5408275bcb49b039abb95a8bdc77d8b416b9471981b9f7e6099fc2190bb9e7fe95d0cc12a50e074ac113d636c9e32cce94bdfeb8ef20242d723ff373037d4d98ec699bd489b913d96aa4d944b63a8c14b9e764110fcfce8e28feab5053b89e5dc664f9ec306dbb21e2b8afdfafcad2679e52c74be6de8b019c79d3908e80744d98b7207be873ffe32ec9b1f2288fc4b41952877bd3aac34820dc83abb6491deebf5523b5c8fd8439f75cdf9cc53d3794392a45649547c91ceb8630558723768e730273fd5dba55e3de042348e40e2eb400aaeb13144fee185d267dac9cf19071b97134da99b382f0984ad130b067574a8ac8fa3e3ddb826b2d8269b18243e05126cd50c4ae9470a0b3129a3ba9537b966bf5afdfb376993bf1e48e1125cbdd7b5b8abaf7271d91bbc0efdebbf720d953d375b57b70f5e32c77d262e7410e6b119504a64febde7b028f0ebb7990bb4cdfa964f7331cbd88eb482c9147772277f8d663ba0c100beebaa17f6ac15d3734c90cc67f0a69303d7e1ac09ff925af8e3ecdcbd7ccea868637299c50bf027e9ad003338f1f15e2900567c997e79fe5fac8651f39387fda789901df03333ed39b4856f7920567753f539b365e30f8a6bf6f53c3f867fe5efa71e07cfc1e308dd7e7867e12fca677f0868629e0077f8599308639e94f95cdd8c530bdc9bd0ebb0eeb21d983c4edf0a97b6875bf431ac0507456f7dd6db17b997e6ec02ff0bdeb6a90aceebb9a0aa2a89e3459c9177dc9d02d195a2c022475baf796c0487e1c8ac78898aceefd47fc7312315d1e649ac1a79fb1998289024cc37196e024f2c061085d6bba4c2109799cb51f87398b76dd53941178945b86244a5653386f33e9769de72854969ec364758fc1c03360609392e94ea44ef774e53b489dee67cd5dcec36475df3911b9fb0ec779cbdd4b772072f71e8ed396bbc760e09a1c03553233073a5319e22c2a3cceea7a88756beca11eea56129f9f1be80beca44eed1c2675baef3ce6acee3b77e24da4d87d57248b7878767644f17fd672f70da6d02f0f9ad2b1d0adae89d4e968e830a2ee9174abbffb9f247247175a0025a3ad029353e5cb19389ac035683872e0824701068bcaf8d2448e2cbea0208717b894a5d197299fe50b9437c117980ef8860638cec0388e7248806b3cf150e01b591a7911c241c238b234f27243147c638b02631a0e19e06f0b0a6a584a5043c10d6a2310aae18043033139c4601b5ee48802d718e1d0f0e9ca53c1780a951944119ac10e4718ef0047104e85c1d1034e6d210446559183258a9c22c0b0720230aa84018611385de02f4b2330448003033082c080c920c6839492babbbb7fe01e848b3589d37e90b4289f524a4dd0e1ee4edddddddddda95f4a57ee94525a69adb55213de524aa9a434fc7196a465ea4e3d07e734071fe1eebe537174eeee52de784e6fcc6c6103aa4c22a5366624ade16e1384bb5321eed4ddab3b3db9bb4b49290d8dbbd3ebeed4dddd5dbabbbbbb3b75a7eeee7ebb25e58c0d777777779fd9628bbb1f818fc047e0f9d6dd7d02ee93de94dca5ecdc714c4a6f0e75f71ceeee4eed7b5d19a126d050a4eeeeeebdc3dde90e9fee7495e574771ea615ac50010f29a5fc1c180b1b2b23ad22cb518291f8db21a50eaa9af28d94526a3f09ca703492a50d123c2f579462777fee4e13504a29f59ac346ca1c29dd717c14874a79236f502aa58dfbf591524abf9d16e5bbdbc0354e744aaf9959a2c0f451a5261c21676196343ee79c7336f53967db0481a7dd4129a5b27dbaa36abe13a51fb6f1cd6c4129a53654b09b289512bc316fb7a494f2a9d32aa5a4725249c122354b6bed47a9941ea5b384a3cbf9a4bc39bedbe2f45086cef22d409b93d27e525659ddfda33a3e7c02ea3b593aa51fad734e7a29a51f4c1b2a97ebc1a3c7911a2ced2d29d8a284e5a80e49e029031dc8f9a83e7676886069e9411774904107864e8aa00674748258597cea24cb86029ddda13c04c162b5725c42b3fee8b9112488909eb3a711aef383b3b472f676f4077884824000126c3808d547a597b3542e674760edec58ae52cbdd1fa2287a0f1070951a312795d9b367cf9e22e0f1f55aae2475bc0d64ff594227756a8903b9fa74b68417aa000530e1e49ca5f57256c8723e447cce2a4ea488c92f572f67290fdcbd1c0f3cd40fcc3040e984133a241e12147cce1b5f44140ec0513f80a59ca595b3f7de1b52cb899ca5f5729608cb95ba5ba5587bd83c2127adb45ece56a9f47276462bad97b3552abd9c9dd59ffe7304672bad1cc7892c9d1ee28e08b6d65a6bad3d64358c74d9244e56c44e14f1c9db262a9ba4a4aec662b11a45d658b5d505d526b9ab565b6db5d5565b6db5f54a05e2030400d424db586305905656069e6fad7581b555b44b5811a8574b69b5dcff83da6879cced56862655264040d5c90fb0c22a0c003be89600d0199d512ef4881ea9ba3569e52e750283d1a07e2054e8071a44abd0213a744526838288280b2da22308a0d62a8474595bad1056006efb6eb91f002015af95866a653c090020a52d17adb5d65a7da85ee4b3dcafe3442be7e2477ed485e6b97868adb5d61a7399cb78727cca005cc885aa0c0da087d2954bf1588b22f09e201fc57128411e1444c88d903b718709b9d16508d0499de44c1cc8094002b5f56608242060db6c63fa460209a08f7d124a18529b4b1fd90471d5565b6fed1202d8341a985aad8421363a4b17156df901f6cc1b92a9577b395b6bf5f00748845e01412683a2899a2867d0877b15c0044bbd5aee5a6badadb5569b4d9ae24348a84a0fd91e88d70bf4e10a60824fa55e2d77adb5d6569bbb6aad434811aa44c4643299acc6a23414d1c30eeaa096d2b18e553af3aa990039e98699801ae373bb95494a22426bad15ccacd5906c458c605aafe56c9d5be66c729947475d68b42246666c92c8525464e488090a22222346401fee8d1c3901895b6b2f676dadd5d65a69adb556eedaa419a45ecbd96aab0fd5561b9419a446b9526ba561f5615e91c94ea0b5d6b1d37bd7e3b37fe0bd5704339216ddfd4f356f0a41f10b6feaaf5c61ea62b2834dc3b0ef795fa6d65afb4dab618a0f756bbeb539b27df73ecbd96b3b697f48df60c1d67d7697162dad33bd912518d8665a87e18e4d6bf02685faf457a84f4326309e17f838dc579391ed8b91ed7bc9f657a06f0a99344d8a601f87fb02c3ee42b2c2489625635f72e1a882a591adfd991233c110484b7396cdd2b3b08b5ab46f51409d1537c8e0d4e9e7cf90668fa1fb19d64802cb8cf31e4ea9ba75fafedf71fa06c7e45186cea7067d4c8f42a150a84779fd9d56338f97e42943cd33a19d404053aa5b32b7b5f282e1b7caf7a5ce0d39bb7a6195e7fbf457f5a1311838ccb2cef69133cf5075c3e3979b0beee6e786f9aa7f837dd54763e6687ae5f9218a3bb2c766fa23f0744bd5224dfdf07f773199affaf47d25b6283fd3ce27a983c517678eba0c2f47465581478a5f1ec38c610e40f2f0e013b6f1d42cb359d4227d5412781e59e15d4e488b1cf7813e5efd2ae853ad955287fbd2e56069c43033f72357e30a0c9b41b20727df9f501e77e920d808f0f8e58bc2c1054f8039d8bda5eeaaf084cd2177dd9faa22e9a570c2a4ce7d1b2fd8c24adf95ee77e184b578b18c7341966ecd769c55335d22532470545ea7143a85a44c8074d9508666d1afa16c160d29052275a0fc008660478a10c5a1df4445060614111e670cc8946e8d1f9b52e69003913d3fb586b407150c2a5c349b33c352ec029f93d2a27d70156ba75356cbf6c73b6dd97e0e787e5f116d3decf794b69d77956cdf5738dfa295b9e02ed3db8741fa9b5673e62cfbd349bb2652b364dedb2c42848c30c20fecad3a4673570f396b8b6cb594a945b63a8a14ed5b59248b787876764407c38692ed43f9b9a15f18a402f6805de8d6678b81c9b26f2fb61898ae1672967d2b6d7fc371e2c003ec16f39809c1146bd9d94fc79c657fe264db43f6cc9506665642b38d4aead88fcd264e6c4e9cefdaf781197739e72b392275fca792d98318e6a781c79bea1d64ff1db2a7fa8dba23def048b3ff11088e327dd364011e7b0809296db20b2fc0d02defe9f7bcdd49da783fa477b448a5bc2fbf456a7bce396b902e1176c609d42bf1932e1baeaed236567da55926903af447474202a10f82a08ff56805c06faa5c984542b9569b453d8b6abec1748be66983a83ffd0c2383a0420e5c47c954a0822973d7f432a734378f2617dc6ade2617d34b0d7c70d5df200d0d083e87aa09511c2a9cb55943d998616a3c453dada91152b1f1a79f3629c4468d8d1f4aad7080fc5e8c4bf49b06b4810a658b334f7d9a91e973ddaccd23ee4bab18661e7732c5e1ccccd377400bdd9a095f34853b4698e611c7bb749c251b74dec6cc5b0f7569892199df32a5ff7d69c5c23c423581674dd6adbed2ad51da8864cfec2c5a0b188e8d25d32f1cdb892f07f8880517dc6543de5b0a4bdf2b1b44ce42428a5586868686bec352b82375680fe1643ad4fdd7620f35d150f7c8f229a51f70a16865b9abacae64fa43dc8f3dd44bc82a4c64dab2c9ad58d9ae62e080d8df2175fabbd5b7d8a3ca26f7ffd3d3d15c42d32a2982b2f71ee8e1f8bc55f64aef7d2775996ed1af2e247bbaf7de837edcf144af746309dc5ffab99a493fb8936e797873a474d32f9764ef86486725d97b07aaefbdc3dabbefbde7795e0ab7e87d8806f6a8fcc860fbdeb737927bdfde0e9bf7a3f57e7aef9ef7ee799f3d8f882080bcc7f1fe5141f68b9e1dc99e30d36f2edde2fe3ead60ba657320d963ddd5d1bf1cd7b31fa898be5b3101bb2f75a5f7fe7b6fd6740d2e39f8e919771a734a3add66a71b02dad4b3a6f5ccae9ad6b5b6ad7a2645a0ff751699bed75b64faa5e6a2b5481618077fc0edb5eb170a969e7e9716bd52cf702bd1a694fa946e0d790dd295a459ed45ba66ad87eca151467f429b8c983098892630150c4ee9dc557afab294a493348bb664f595603295485074696f0adc5dba69dd1a25974c2badb7f451cf9abe5f67d1da0ca9f99a3567519ad4a14f6ddda5bb90227dcab34369d36a5d731655d13a4b6a9bb516297d941128a033ba39d45dd4458d45b23a55ee76b27b9158e677f7bba74ebad53d85d2ad21d943898a648f8edc3d0dbb2e729745eeb4d0d2eaaebeb9ba2dbdfb22a9d3fd0d163c3a51cf2bcdea1e8adc755f0abbaeab9ec5e13018b8c6f77df23927920196953b7d0d2dd8ab60112aa7ff5aec5e1651327bc03df81fb0ff31c1df83dfe3705f4c70f817131cf00b7c93068a749122b7c58e870cd3ee7be5472d767f5a39cd59dde395134911ba9f712cb97bd34aca008c6e94bb07adc8dd7f2b7742b2ba9fcea5c5ee3bcb13dcb32e027fe8229954c5653ed4fd888772f75d943b2f889cd57d07d462f71eced062f7a313e5eecb5df729dc5ef3fe5060670dcfba357e5bba6553cda51b4c237dd87ec9b64cb6d6afb468894297b568ed7f64f0f8c98a68803265d22dfaee45ba54cea2c99e1e99bed764cf0e5ac30e191743dfb6edf75419dc011efb96696a90ac2e2cd1840e74a3025cf333a7de893e1aa2a7f9bef7da49b75041b287e6f414ffccf7131904871cb899c669f090124a0cc55a94d222ed9f398bbe6321dbb180f9cc5ddd44b2e8539ad38f7d5ad17c83a8f7e6c7239f62d863b946184a0f870de3a1ea273208076eb6f1f4db4943b1115251fdcc779014a27afa35549f5a61a7c91ecf14049d46933dd3699e34a9a40e9d422edd9d0675a2b87bee69fb40ff362c7b1e7d96e98afb2e530ab95b8fa92e64fbdd9cf5244ef651c3c01d9d45c8124804cc1d4e21a943ff260adc30a7d16e95c2300aabc1046ed0071e32fcd3e340981254491d4a69c33215fa719ab3e837931629cc815aa4a94cdf81f209f7698b94524aa958caf6559225e5d734403438603070cd0483ebd72eba58f55988b4904844ceaa3ea5c5fa43ba2607ec432ed462adff91c1475dc3f78cde9512488392cef7158f3c57313490c6926778a54ecdf397f89c27a17c855abc50f4bddfb2fb5dc3946e0d75959974bf81644f4dbeb74c8bf7febccfc22af544df3bef4d3960022edc114668f176cda77e943416eedf7bef1791ef774d0894efa7c2d9e4a392fa213d93dc3565aaef3d119c29bd7497d00c6791d4b9ef2d8193ba956ab14cb71ca672d70326ebfec5a61f55a71f45d48f3b343ff2e407ccfba90f79f459b621571283ebd386c11ad6b0ef6deaed3b16d9a251a448e4ae54e8b7f155a670fc0186311bf7d68437fb85f47341db2e563d2ddfac5093f26a561eb0a1e66b3c0d35c29b1aa1076ad050f335420cfed484777e35ab252f09fafc9ce0e6fa2de5b76cf1d01c9a43736886a42423d7c32555f7978bd9b71797c211ab5e25da9bc7fc964481288c06d198ec99415240597e17816d3121fb3e64df65f69dc84d4d738498bd196badb5f7de8b461554ce48e5f9dc1d6365082fcfb7773e9e36dc4a797ec541154336d03b1f5bc1e5cbf33d8905a63cbfefc5d87bb56af1ce39a9e8a29639bf2f8899b32115351caf925b0e8208024a901369bca60dd9c351953b1c7f47b750f94e530b3c9cc7f1e4a494d2d9d46950a6efc91eeaa1212b3e87845de65dc8166d224598bb4aa1d7de3e775ffe9081f77e187b2ad54d183c36ad83700e9b41b8d06b13480eb2c786fe45ead00f294cead09218ecdf34c959f4efc558a5faa4d96c369bcd66b17b5595e7525b79eec5aabf62e690f0e83559cd764ba24014267b644b5ef4bdc85df405f4a2ef47f413e3f98b33888747c213a44effe823f711109016a2972c8d907f8cdf171e91a5d01af18255dc1579ba678001950596799437ea030ceef2171199676478147fa4b3c6478900dff873ce71dc75ee4be18d87255aa5489d5abf5bd504512b0a084c77847e98dc90b649d5622582b990362cdd307dff3ca42b7696ccbf4c19840358a440ca36596291e228072965f97206913af6c7c6138875577757d859f668867978b254b94b74964dc15f5df8780bdb4f5aaa01e6c26f0baeaf6a71ca9fee87b4e5acb39c355f82a52270373503a99dd8ffe6501e55398c61869159c9ec812d5ad10485b9d1158839b3e6c1288c2cd73ceaa8756bcc9911756bbc379b2bdd721d744b1ad980b2bf0e30a38e313650647faf8227d874d4dc255ffedefd86b49719a106a314bb0923281c738e8254a8af9c99b36e6ab87fd451138364cf7cff1b35ee6344dc4a07cd596270dc6c8ac69c990e20a99333cb9939cb75d4747891ae4ef64c1d64b2eb1093fd471d61b277a18eab52e9a88535283524945d7c12d3511bca2e06499d2af0a8a3967d6523ab39b35ae9e7cc72b6e41ce524e560d2ae2a94c30509a6a3960326c79673cb299343cbfe954e1db5225bb7461b990e303a90e6aa3fa748f6ccec8fc38f64b2a77bc49bb757c63a918f25fb8fd4892c6bfe5db6657f69c59c81ad818becfe58649f734ec999461ab2921cb9dbc08cc1864d63312942331ba02964d8ae10156d19b2dd90c6d8108491358ac8100262c758c331b07cf9376ad08cd4f1f799fd6b1559b76a7037ff7aa55bd2480c2ed96bcd22460e32583446cc2b51f6d935d6ad71a64a711ee4b14ec97e83c7153c237bfaab119edc21e77bbdf2a48e7f2781f8749f23140808082877d849d12748573e32f78a03cd358882d79e6eea6fff7189cfcc32d37066ffb4c0b1826305c70a8e151c2b385670acd8a0a0221c8e151c2b19c70a9e2faf3723b30489d070b440b667a484aa117386637d92fbe5d71a64ab0a55a0b9aac9f4e92a06223eeeee1d6dfaccafd1041e4db5dc3f57375df0d87d8f265aee57327b30a4e7a5d23d950bd2ae8bdc5fe7ac2035d53a5aa864f6a03bba49e2afd2cf4c251ca33be2bebf06c91e8bc3bfba5b9fc896c9d6627f9bcc1829962377d1dc8b314d776433998185494ceeee56232b516badb5873cce1409d1fcab54183b8d981490ec11d28891fd299aa37b31a639721f2aa54fe2af1225aab02a001a8edd915c5da9333f258481f044809212c4e996e9ec2ca4cb8466d1377509c3c50bbd21c3fe93ba001eb9ab3e15c2e32c5a9429750fc73e3a7297f474a35738cefb2b4ee4509426d02c8ee3b83199dbf12d72e384f9aa6d30ee39aff11b32987b1976970c2e7dfdf17e9bac89852b25ede688b5bf2483aa1b2e59d0c79b73ce69ed6a49f6aff45d62a1452e3c92cab2c57973ee5ea798c34103d38a671c870d34d3b009f30e9ab00663e33a8ee3be6fb7061a23e676bbdd6e37dadb6c76dff13bbae5a62366c029d8b6970ae62aa66b7faddfabae7d645446422fea6652a733c263cfc020e5fab2d3d2bd4f9d2f4125271491b9d6ef2f1ad8a3de146c1f8381bb0bfefdd26a3ae8e399c21b23329bbe88cca65f927b357e6e21bb694557df4b105c2dc9be92a1e62f7cc00472bf5734e45bffaeb82d7256dddbef164599aea611157d8e95a955cd14f55055f0fc0912a91fb6512961ad4a05b8c85780ea5aabd35a6badb5d65a6bad95565bb97a6badb5d65a6badb5d65a6bfd4a69fd5bb95adfd6ea4d81bb4bd71b25f04fadb5d65a6b9da17eadf56bfd5abfd65a6badb5d65a6badb5d65a6b450169d1b2d934fe79fe852a2fbc97a6ac60fa23cd7f9b566a2fbdb4a3253a6bde9782beef82c0bf41a6e782acaa62224c220f5a08dff56294d249e9dfd7a5d3f6c9d6ecd2c9d6e4e1d9d911e99d77d6e4118db68c073f6123423e4f282f449f41ce9a41b267d2a40e9427b235bb90a23fcf8f3bee2d7400fce2cd3ef2e4034c7c0a81c78fd1b904237b0cb28b3c503b219c785a74d29ce5535af4a1b085fc3d1944eb9681cd25e1d13e65d40915de64f05d669cc914e9e6ee9a42b7642bfef9dc1c63b2e873df851e188e6f0a477126149a791c4ea131a6ab6524bc291c5b5f38e60ce1e08c9c3c4a1a6d862d49eba1e930071abaa859439d9e8ed02e1a9a45658f7d2ab3081132c20838d7691b923a4de40006c85623216955750a5153f00c9d2cd0f620b03d209e887e679935142d524ae4b1805fbee8c4337f0a5938bd69fad8d4f0293c999ec90db307af139cfe8517a8c0e6eb8626a2827b88d6dcb5436dcc1a7d7a7187b4e94741da45cea28f7a301c49f868d39af76889769496a046e491da09216ccd9104f0a70a157b68d672ec4c293da66f2d52a943ffb46a272416232b726b912c5b0fbc64faf46752e70cece1148a813a60ffee955844aea40e9df9a50e7d22518ab5527f4a805330f7f7e2c8f75dc6ddbfdcbdf7fe5ce1b46802dc3b909dd46564cf7c158e3c7612de215fff40beefcdd59c92c571f7729fe440b22727df7726382dae544d78b5c6d24b1d285f8fbb96b60d3b28bcb7afd75d0e9452475461bee8537c1fec01cf58033590b33a49ea045dd9ea3252bcad2623c5fb976747bc49aa69b3c9fba3e5e92467dddb7d77d1bff7beb88312ea56e0c91d52ceccfccc8f63a7b5c3096506a657a8740c196c72d229220100004000e315000030100c088562a12c0f933cd36a0f14800d769e4a6a5899ccc35110e420649021861800000080004360866a4a1b00a52bfc0ae355071c08c03c4842e2fd3f6fd982c04150d95de1f8e592df0ffad3948cdc69483c300d979698a56e6ee7fd1107f4ab03ae709fdaaca246dcaa45287290b536f41b64b9fe91f076e9f7017e4badbc7dc9bc0d5cb1d8ed1740eeca15b96624ecf8c11b3275662fef299a218efda80d821fea72b34ce201a446428803cc01e39361ba00912cdbe168e48c4ec4210fb4d6109a2c3dd699aa0af569f6d33258106e5b8fe042ca4bd3c008cba0876942d63129c14c15a9f8bec41de2ab4e60c8d9a41c4faa4de03ac58e546486fe57fc4b815ecc408e1d6887c2769326cbf8b85880ce599e25c247046c6c6a91be89b99bc87468b2e6d711223583fc43975c6117c1de222245720a3ffd695cf0841110a2ab2a41732de6a1fa995529ea8991f821c598ef9530c36a8f569aa0fc335b38362806259c5adcd4220da58ecd53d6c03f804b9792faa896bef1e9cbae2dc021bdfe668260c16a7ea63a362c4e8821570eaf7aaf3967810092e9e185ae3b2c9eb2b9c0ddb918a8405b569870a9d361b73990f892e3b93369e3b5744569977342db9737f1437fa82b8d89423861d0459e5f2ce19cc56848fd0561f58560c39233be6ed4dc1b37ddd1fde5d072c4da4b5baee039395658ad53897ab31e9b7bc70147332647bbb9794610261e5ff68666ba77c0e0269da6d3698f4d7d35d128d5e395b11629a087e624ae0c59d8ca085b40f6b83c6cbc6f41ce27715925508a840e23051b1e95aa584a9442cedaa3b0b597e115c3b2f73384a2e4ee24bf08afb748da54e98cc551933bc478049a5edc4af108ba4f6540ee8d460f1cdcb28c589c65c4a6419e57d2452e2bb93dee17344ddf4f1358574109a18622c98ee2f52d0acf85e3ea0efbc291128f20267647228d3e55d8b56e9307afa7f81680a49e61b3880dd0c8702b129fa099feee548590a78dc21289f75e5fc3b47902ef21c158f222cbcb19e12f302e0ae071d0390e80da9247261fbea3a78ac31268228cdeeadb9e482b68467e8853c8a4b3ac57eb3893533fb2574dfd3d4f8ae8dd07a0abafa946ddcfa45bd2f2f567605974b016d0a9556d2aa75e3f96cdbf663ca8e9532e59d09ad11bfb849df3f73cb6b7ef8a86d3b0824b4ab9ca19c6c1a7fa9e1de2f16ad8d415baddac043b224d38f2f0a715f2ef24172d499770d3ef6a8dc46be9d5638ae2a3909d4da40d09d8621d54ebdb46cf1bda1049041064fba4a184c6cc0af35071d32a88f5c94e7086ea1bcbabf740d22dc3d9799df2d0b6ffb32cf590d32d3c1d6a5312e05b5d9468000ea6a3553d49b3ca5dabc46c55e87a33432d8b150f94b904083eb8e90275aeaa350edd53e28635cc9ae6522c26eac8c00b82e1784af10f64cf36f9b1ecaeaf842100b81aae3b4c88438792e2136ab014db3e8b602de6be58ca1e795ff1faed8b887933300b34f0d729bd6e550382dbf729367a8619751ff5f92b3b45b3823b832097d4d9ac015e92db6295550041b080c90be8f2bea6a85f6abc13190e217352be390d4b5ba4260fa9b54734ea9964830e75b3aca1ffae2b297563b208307a04c5a94222dc60403c233662bd721929ed55134524de9825b6c36789f5f227c4ebcf3cb51fde524f3100a9006967db472915ffd8f01172369450b021695557dcc019ec36a5bd51a840c0d492db2b9f692a6eb3a6d522ac79bf5b0cd34af1ee05f5f86df80405bdd93a05b39d951ea5272a8015e2a235020ec004783e8b4549d72f902b410db6eaf4eea3942d15aa6c88aece9bf956019f32b386a56faf7d5ec203443cbd9a41d89d0e1f6af9f2b7b64b020caddc610cf03c68d4ee91b7e10517cda527129660e3931fa9321283abd32998409c4a54420db8c2f9ae9893b46f85ccdbc459f7b0e4d5d187fad310f30544bfde6c33fd9c829340041e9d2da287296f65eb39128208e6625aac81ada5d8255721eaaff717e8d28f63068469cf0b37180dd1b593093b5f80458e010f46a5cd7c204a10c7b06e10b33029f5ef462b5fd2162e3a3cfd4b2990a140fd443795183f90b16e89773efdb29d4817a841c848e553ab9f69c3d2a070b88b32068978301cc3f3cff577520a20c056d028d096689d2fa84d51c46a8636f9bc5959805c490a0f4b997159796c7f0a1b6f9a4bd1ad9b0b54813eb2d1071cf7d8221ffcac029c4e0398e003e821229b82aaa31719e6b20e114c5bc470470fc7f7ff03441320578b8103f50f0c208d1f52fbb26de78c395318e0a40039ec86cc89ff71b2a20e3f47a91bc92d08f9431afe628c43ed108eeff4e31aa5373e9ade4416cd86784a71cad902bf02c8fa86abd810183aae3a420b690342588d4aed63dd244cb97177b173891e1c23fc009fd5f8ffd979ce770e79c43360f0eccdc1a69cce084fab330c5f1ffab9b614dd36a91356484dadb3acf1bae74f09761a5de6711c5ca43ec219c58f0268056559a298a607091ed5108243163ff5cf95d6902996e64b173bb0d1aca8fa5869f60eda80f2d8945630473975d9eeb960261d543097c0ed2f3bd5e9b363e4487f8a4d4bfb889e349cfcd72019cae1be4721cbde682e2be2ec73b5f6530bcb2a84ecd26ab0b36b3175bd3bd4e441a0321fb3429b7a6d868978f0f439134a5f551566cd6ee0e885d9b331aa0d2bc4a95a78f9ef2a233e3644d80cdfe6633f1e4c45bb5b18f505225c4328f3959674ae87aba2c94cc963d2f69c40ee9ee1265ecf5114652a18eb4731540eeaa2133712c1a0112532d52c4f10f42491c6182c20c68bbe7642cdae63c56f2b9a8326235d353dd7c363c18c2dae6f048837eb107f471098aae0e1b8fa41d95ef64a82bbc4a9a1828f1df2bad12c606c6acbf76b0940fec98e309b74b866b662f07d0a0e203a4a97b4d9ca16fc492e52a4c67a415d6d07b2a09dcfca4279983be60d9186ba2890d8080974732bd52e5f909283c254e7c88033d9a351b238ac7c1b00b85696920c5513fc7112806df0623b53fcfe5e77a717119b34f51cca28b15e03a62d14bd5e9aecd2f16efb912f119905f23f57ccac73815e3015bd678d750460ef461cc647bd7c9141890ab81ed6ef1a6dbd20db4b6d8b09762a73b3d7bc3c13189881b700942589cdd083642d233873abe8602de47d379206380c0703b81e715787f3798e102466503c7d083c976059360b04d864cd9543caa0f7d44a97a95bc5ad95543c005d1104d5357b725b6aa3688b00b532b75faefbe8c60f15510aa570f078a50d0598081f7df01e11a0eaac89860295836e31d82c21159edbb60d0c95de6bf34d1ac53d2fba50b5bc845bde77120eff0698aef6503ebf1014c936d5af8cb0c29a151cb33ca8956bf77e59f0567748a2c42fc9aed9902ad2d2c04b672ae9d0ce90ad17dd10c8bae5ea07988a5faed64f4fb61d5af633fc070ec65c01b2572d11b27f4168b9fd6b22c0a4c83802d0c84b62033b419069840721f1a7da5fa8220d3c94540733be6be6426ef15d43ee69487632d9880bac970dda8e85dfba7f1b358dfa0bb5a23c2cc71b2a3347868d8f49b7cbed6ea8b5a8f0517003514b6ab1b8ffd4432db23584e83fb8122a287175acc4413c97f5a812e4500bf3e555a5625f5e05518b834ab4a98d1d10b2fd3948d9f6638ac429d9fb6819413612cd35b9fb8b15d3c14a3bc0ef835e17f0ae142d5232e4a85cb6dbe21f539dfb40d85a9980f75f0cf13a240aa67a2124e329ca6abeee694a51dc1324f3ea04739b82040c7ea6293bfef85d0bd9cc198127d399720d9be02f491837cf34c29f5d721d29e98ee48a50dbdae0cd81d7a7b3904f2671735b5d408b2b7871dfd7e162796e8e1c37c1933a28d4475be4362e0d01f6c20f5380d50edebd1a792c852aa7883514b41813ae6768fd1efe3fd86fe4cdc2351710302abf7131e47df2c382e00412e8c60b3cf2f50ba13adb862eff581a5b2fcb10b7f6beb5715c53dc9fddf352d2f3f12792bb97341fd324894c71808350c0ef0f44c0b732e788c7b2eb98e7dcc9778cd2df7e3ba06fbc2fd7de7790366118a2789c607b300c99169111d0816a2629e5655914add96420c04c412fc45a9df983b60a8ac93770fd9036d62f0ca9dc0ed7db11dffdfca6b9bfb5538612fcec6f4143cd5a0d814c3f5fc81abc551ca5119bc432d38622201270e1b438f4754bad179e4b53afca5388c7dd60b5934358ae0107636a6b544c7ef83f9cdddeb512020b0bd646b96f413fc7e0e9cb0604b93e2d0be3a8c6186cbe6be23d1553554100d52f96464828ce8a05de011b2b663e58603be0920d89ba98c4c90b9b596b571fb4d159ca828595bac85d163aaf53cbec5cca8b35ef8a40efb658d0cb5c621fe5f2fd9d14fac52699415744212aa2a0b27900c3a0c179dcfcd1c1532166629c85c3c81798bdc92173e8a1210365e55a3892f9636e34cd103d157efb8a0ef5a2b7f2f6a71dc78c6297000c877f391406241cdb133bcb28b24dce2fc943655b80233ea6c2feb54517dc5a011c720c47df296e538a699705e045f2167f06ce6a722859058264f65924409bb213a24681dc52893c4f55393ebc1799d0bea605000c1f33cf000223cf8f06c8057ccc81ffe388f7f58e68ab96b8d915c61fb7ed638612dda35c597a9119f4833124c97392cbeacdfbeffd241b2568fe2f055da0fac6902f829d37b75c2a6083b7dd4414b74c5deee1bbd2e145199da48023573aa3254956cf28b0037e5c761c7ec35201d7382f559c38e6977e291f27dd04712f4f7789a4a199442b44c6250839eb6112fb8226c10ff9adfe828badc9d6a0f75aa688a5e32d80ba869fd4f8073d3e89459d6d129165090d5251c7f610fec597786b6b38dcb4f44e238983e922a4fc9bb5ee2ca70a205bdcdddd6a691f4399d40d263e5479503840f7d7b0255794c2e612ee9fe76910ce4936157dbabcd6440220c0f138a6237d2c300e6243690fbb573c8bb331b8fe10cecd448993653a4760248b2b5642e388c0df70aec192c7fb893f89a3d2fdb4f73af2e2b1ba38660bd8cd1b5cff4103b4061a262e407ef5c6865673a5eb05dd2791c1560a8ceb02c8398b75b6884460bed17ff4d35edd9e3ffd756f5960c4caaf8b6da984e7db487d9d1544ed560a75d81eae0ff2e7bef25c0305c98dca2f3904c4271f555e1585c28e937b9899c93ff9c4a9d5a5b882982ec7015e35ccf07f1c5d27ecf172f53e8d6479dc76a97ca73d036627854fa1f558c5c455acb99d7500d99e86f757f89c0bb0db5ca9e59128c63be3d464d5c51023453b3ad2f7da8d508f8143a6285d09633ac105836d2dac1c4df8625ddc9a01738bbb8e376cef29901c0102c59587bff8ebe467262480e12ef9d8f5b3ec22ebc5351663c07d1434a950bbaf439d246390d87b18d4764e3f68328cf1301e024005ca9511d6a39cb3bb44ce87a4f066b67432259d0b423fb1fa4eb4f8103130acd928857fef6587d7be5d3495c5be9f511764dc0ee05e3609802aadab439596dca91a9714d867c5db9cd249c5bb6108091809212bde8d5189cdbd6e33711358608b6b6d9edb06c93add419d8e8500d2b06eec9a7452af6832c9110c0935d2a097ab6e540abb2a3f2526be0089173c57ef19ac6061941aed50e0b344e4fd8a5e02a63f3ffa466913b46b0da0c9dfcafee80deee0b0706f9a64046f136f6ae8ead5e461770a330a31ac027f8c7e0e470d41e95598ec3a60390eb30d5ded207b59286318150b3fceb6808d0dd96e2b59ed92b441d3d5a19d198ebbacf4c5883a44c498c6cc752cbf47b6f0186ad466ae45fe0ecfa1b5a5a2205b2808c1d248c512a4b7ed91f188e0e6984e43e70189821041b4f986c13cf1236cc0175052bde824649e51e35eba04761a6437ddfc649d805a1fd82c019ea32dce89b29cebe4493ff100a89933608f723fede9821e05e39ce3962519fa07db26659f4faba6c5d5b46954b092c80775d690fe87a8f12469dd26d2e890cbf589b8fb4b4f63ac1ac61979fd9245a66173d038b834c708949a82480459b41e2994f6c972eefd80df54524c8c56fe2bff9cbfe49b9e949ac717d8fe9db55dbd1d074be059ddea17a9c9bcdbaf3bf237eb0f6e4205eb0c6f276e0d6554ba116a2fd207f0cc1120a1657573b7832899aaba2ff60c309c9a187ff41f27039b9af101168277c9b04278a79472e4658b228af00330870aa5180ee21f6d9cc2740ea76efb7178ae7fcc12ac0a935eaf1675d5c8d51eb7ca5518d6702d3e06a9084928f33e0d4b8be3146db3e6e84e9771a87d159442bd4baa60aec273bb16b6052cb9301840a2af72c32257d21a83ef7b0c710878640ea055dd541ce024e560bace0b6d0e4bc42c96340db2f1054b729b7d8caa7af40240956cf027ab9ef928b8641274111a7959ecd82654560c6c738ec677b4e266d658c1fa7dbc0a9546065214e53f23251b3e52b5288f11a4e33007233d9d39c0323ac3ec7046f7ee1716646e1344f468729b474bc009a94e675858e9d2fc16ac605261dad752eaea4910be660d080401c0d31f134b0361958afff753b27213fb586a86959fdac5462b0924117eda2437c34d9ca363d9676333f93ee66ec272b25eabf078c0f49c551960d46886c108662621eef82bf83b2cd15cf804fb6ede030cfeb59cce9b79d77cc0cd8abf545ede19a21f4623ef712c489339d60b556a5cbb3279c6de0b6448090c4820f02cda870d90b9b83bd7e56eea806c62e754db5cb3886c5c649f0748fd786d2a6b770cec71535960f4a5a0379b62332650a239d14cbf2b8342fdaf7707cda71aa432a72d6a95c931a5694b7570a62043b25c42bc607258b453fa02e2ad939575e2fdccd68f75337f602522d93833c224dc20f7c418c9827a98601befe02ae0ba3d30d2d62e2136c295854df7d59c377a5c6df367fdf2e7309597495f7e8385ac065cac7ec6504a6e950734e0982be3ddb76e7d3c987387704bc8cbb712192c4f6fe39d910d4b6812e200fc56128eddb777a137c3a789ff67009a8fa7a29b48d09e1ac4e0d54829f3665e43fd5cb8adff3471777d11ea4a8daa142166093701aec84b53630956a139c2cdd6c8d749b0db9ad58639b47a3cd512b871f4f731cb33d3f5f20b0751964a1ad33d05ecdd61bae4b3a4e0d0f9343a3723cdf1201b74f9821376dec3451429ed4ea6f71d3866f71042154fd818cb12f87b9a375661a1446eb33a87eb805be5557530fd5a2ac7b449905feb02ab44f6f8295ff85e8b091aa6b9bff99ecc284efbd7c404d66f504f7a140d7581806e866c59288bd6649c3672356026c94d0b2ead4eca5d35ee8ce7eddc5193cd43b359150089ec11da1f82d373ee9c987d6ce2381bdddce6910792c4c6e1edf5832d147a06d956f59d3face9a98d27657e508624cbd37813d7c07c4f2a0f90df3ab570062c05f414a981787efba6b973c62f0623dc493be0de5e8780b01ed76ca99506a21cea933f65a9ff5a86cf7a7d04c6a395710dc25b1d91df6179c2e6f84b4ef1ea49b81f387d26ea3be84dc433e0116461aa73fe25557ef495bc4026805f744e0dbe35b8b39048744fd1445f58e3df835dff2d47163fd6e963cd50ee73933986387329f9a83abafd0cdf66d4a40f65bc73675c0a8c1b68d5ce251885423b19740443a1dab4ff18d9081d63f40f7699124b42bbac703db99dd0deae645a009bb7f3deecb6448ca2b1df80e4b2b988d10f944a99221edc9f7adc481d2346052834e21d373f612a0258ac4cbb27198354dbbb0057251cc0751ceda4be21209d67b8caf22d1f181c71af882877ad4dbb0a927033df36e1cbbf1c9c1cacc0c2067abe79df1bf4ca98712f5bd36e090ee8d58534597996cadf4558a5d47b28a6149444c7f62917a755a4504d9eebfe1b144e71d95b08baecd65cafecae7fb041f6ad236ecb50e90c6465ae1ad11958eec3e832218c2fedf9940b4ad3d2a91787051858ed07bef8cefed98e9e3186f2ee28804d3cc1e17c1aa059227fa7a5595b6b39a212f51e05823fd8eb913b774be448bc1cfe660db6641140948fee413fce040c9a3659308ee50e250b76592e48c2173c7345173923985b5b691cd419d2890e28fd3bae34f17a35d902aed7416078fe988ca6dcb17dba28033a03494d9477a1117225c4d5f13852e4ced69f4e4f7989ac8301319f412e3becf85fbc666e65c882f6865e75e0e8fc4e0642c806159f37f05b206e8a156801f8aa0aef7e3264dfcb1111f9195c8a0e02a54e9c2237feecd9c9bfa5a38a8869fe03c4e61bf1120d5c76923b67f6cd212bf8072f8f6023377bc351505a41ce2f6838af168c3b24b0b2267cc0144069c00559934eeb4f162e05ef4d103d6961684e8e98cdf2b98dccaba7880be8c48f817640e2f5b937039c3cd7235e5bf319a57bcb362b07965a633b57d6be522665b47c4835cb2f74eacc4a9530dee024bd09649ad6468655dfee60780b7ba2703b1ab3a3a9aa02d16845b8bc390fb5ba1fec9754283d8477d5286d793daa00c72234856b10936c3b0a439416fa6afd94c4c88e466f1d60d3250c82b1f408deb93e833c3355847ae850d675c877fb4c4355aa027546cdc2e731a767e8d30cbe039286de58d6ba35fc1cd30e49d9e158961b34d678c5929e0a28354a71ad751f815ef2e1c75ee8e3b9aee5e66c8de1f85581789dc1a8b71180e8a4bc51e00a7838f445d13035958b8bde06e04b90432a05b638c64702b0b05b0d0492492de4b9e1ae3a506b7d5df2834e79eb337f15d041138e6c4f2743de43f48116ccd4105e738781658a40996183a581a50220c8514b05bc96a9216bf94e01c068084c3a8d71523724a789f516afeeac78e3496cd548a7e6c09576058a5eda5078647e4e3c1795631a64ad3ac8131a5eed70eafe9922cd782af7bcce3e24cc1c5289a0984cc189db655a313939156e00362802e84827e8b9ce6a04dd3841cc54e7e812670a8f7229cd4a52b2e02ba83eef1659f6e23392c7954ca743006f824f38ecda34cebbc4b88931ea2e9fca5ed5aa35fdcab5642904cb9d6c1fe442b438f492ce0c2339b1858742f6d0ea8072f1153e482d95756c5161b865cb751090d2e6961a739cb669425292b36944f2c07bb54334878aefa5b04da4dfac1ac5f5fe2e0fdc8ccf29db445faadec65c1dca39a9e4048e837c387201b73b9c570548c5cbef378f91c60b221cc441c7f840908fb0e870c6d30e8fc559e4a2371ee46f091e594cb9175da78f8fc99c0197092206be0ed56fdf175062d9a118674035298eae9905847935c161c4b42c11a44749c311f07037fe8f8ea6bd8a7626604719ea8371153f5b923d46fadc7285b433a3eb3d32eccad6041cec7490e684f102cfa797fb7fa9ba1136d3a20f3caed1fc0c0b7e2e95cca9b24ed518a64c42963dd9cde47daf7080b94ea5db0f16138855a675db0391a8cc6a078e8597cfb7421c78ebae845b3eabdcf33a5aec0f75c19271527e005845aeba3a42e5b53c2cd94d190b48deb9a396f81a293e899c91cbe7920817a2c5a2e87106cded6c718eb3a6b1cf7c96c7bbea6eed50fdd12627bc99c1ee6bcf2e9e70c0ec5aa3323b217816b4b67fbbff5cbe1f803082c71f764aa44b45dd01db33847a1070484b326593a53fc4ef4fe267e8b348b25cd0c96ce943ec46574a988271692577cce9fa9704bcd17bf7ec8c20b07308fb9847e581cecc437489817f7b87dfc6afb87fea244c206133e9120e0f951a7bdb7b8e5e700bc9c1bf9e01ce2113772739108d089db15dfd8f252f39eeddd50ebc94b243992563a16ebc4677a833cb733ad43534c1b7356553d9bcd5fe519fedeacdf8fd68a06e6a790a2cb0ab81231d066f20609fd9fe755b54a68bfcc355933d83e910c69f4b6108844d287a0ee3b47b3b4a64470f3c4bc64874c7671472e1a18ffe9563d135f3db9ad708e7195f6c81bcc50621e01c44f310e21689aa3168afb349e6e4d58128a23dfc86e8d4c45854268818be651c85c0ad9608be965f65dfe1521a471d740d43b0cfcec86e7009ed047cb6a4e8fc937a5fa15d4a46ad6d53d274bb3d944a121eda95734d4045513361dfe82819aa21aeec79dd34331ecd5cc80c34ac8b31c14338f9464f539c5d29ef545d5f4341bc0800a80ea4cc5f340bab19d9fa16901f140de7b8aab587da944e428e7718658e886387008606c63477d36e95370a8a8d7e6190763f290aaba94f47caa4e5aa83e5e9b5da84232ab11a92b7429e386c30a304f71ce66a89ba8131061b826f687f292093a78dc128d40bfdca2e7da179d6084a56794251492b194cde427c4548cf20ec8118ae8ad863f8a0bc0e76099c23121d47771ba66e06580345bf144d08163327230386b9b913270220c01cb5c3fd08e95d0e9de564940bba0be7c1d1e6318e6dc088b9fe7285ebd8dbc8c9e8cbf58a9e4165fab59c5ac30cf5e637091930acf1103e173e911117d037ac52c1d3895eec6b5f44f87ad855a14a2d384b1036a72a98e34b63379cebb9585776cdbe8c22801b80a0d3e6b1dac9e30532bc2f294ee80e709368e55d269b96044244a50ee9e597e6e46bfe6b92ce7bf2a827b54c64553a5a1dfb00756859ab8be975969e8940d2a65abdb0eed843b9d4e6cf3ebc7c3a4c78283618f367137e15e69e1c8ef10ec6b3ed0a86c487e8ecf026b1470d2b7a72c6171d969aba18f94969873f59f63caf3f77a7fd05e2cf6c91fe4ca590e4da9855182371e63d709429c07601944eaf645132f62d9212184543b03a1c2a01526ad9a63f81a4a712e4c7815a47d003ed4524d28f6ded2baa7ca4fb179dd1dffb1e9a0574e3d0bb9aecdd713a0bd4e2d75b7508d2bcdeca5d32850a0b531f29c21afab9c7105eabf3b3b4392962eb7312fce4e7ba7e36584da3907e5ef296f7038a055108625f2c4dfc0f538b9559599a968615a794f74497b3d27defc87d71bed7afc55a1bbf28edc24dbf48d81dfbebda4d893831e5727f421b92dcff287f7ef7f8d54b301853c0d1c07e8c981799538286341546571d73951359988ea21abc15578df307514fc4523e5cd15c736079f652a7df9a57b8208a793dc8699e7e993fb15fb7f492dd7f763c6ecf7dc71ec8b490be011eb8e32bb66c2c29883f5cd492cfc3b51451690db76be1e4cff3dc8c0afeada78559d1fd8d5afb689ad0ba290e7e1598f521c0e079090955328bbbfdb8b3e4526b806ff7af71e47d5c6492005218591fe160842740d856b79688c4903047da39ee51bb70935a8a73a91d780e097444d0fd993bb580488e07cbfc24fa705bd7ab21554147a040fc5f8f2c4476e5950cd41af368b9274eea04d57149f5c3de4cacda7a841460a0230a4bb4c97855baddc0a06058fa4d5b1a8af64056910b62851d16b14fd6a98326e434ef7074e62ea301e1af5f7faf6dd5b6e100cbedefbb435bb39e41cf7894fd0740de3c5e67a94b827df00e47d83c522fe6b86f9ba22969ad974c0316ba5eefa2ad354ce6a66ffb3e5e955093570daf21ef0f7a778cd6f0365746405534b99374834ad7b9efaf1ec5173eecced9d594371a5746027b08e9a794a34cf3c80bc753905f495446dad60345545c7e14d9ac12a5e25b6810dd5949021bb41da961251d3bb6d8e5775105c73b646d2cc294e4530cb98f26a3d32a6b4a596987a31336a208b0105dbda625d8c1fb33fc58c195fb0ded6aedf0d40b04c068c348a625b8695f70326cbc6519ffa0570c6dfc10c3ebbbc774f865190ee32d2c75c829ae87c2ff9b87441c2ec21c28d95d3bfa4bbd57830755a3c81c5735b0c8328752e48cadabd2912b6c3b1a7700021e5a2fe5ef1b581814c01c58cb15267c8a7b7ebf66acb0a3458329889181ad9339d32699dabd8599255e030040c02dbf0fa3e55680551d331682d15fd90220969b0962f996f397776b753b85be03bb461f35462e13bd3ab4d2dcc828e5144b1465a31db1de0a9063799c24658e2eaa97200d9a3c9354f4073c58b812bddace8fa4f8084ff9b711c93888f8c09be6e80f477bf8806dc9a8a2604c865ed73c72b46a87f49875eb2e8a59517d5fe2e3ac22ff6dd71907b14241e8234dd85ffa403c719b2b475fc4c1ac1026cec67ede7da4da2f59208e168be5332d8ea6b8e5decbd1badfd7db125adab2c1144b25b3dea12072b4a620d2c0de18137aea2f47a3773530a787fae25e777c373707969c92a140ecf90e6c19464c392c6cde8460d9f51bcef88b2de509b49d6b1d4e77efac54ff445f97922a77ae5d02d7fd817ec9dc85df3f186f23aeafec1450bd1eb945f311fd48138beb0b567bcbee282cd01503132a28b4018defef2d35e1df021805fd044162072bc0d4c8ff025b7116948578de5400602edc5bbb42e3624237991589abb84fe82d3dd1ce16b1578e0804ca9af87496b0990c70fb8276cfd4fbcc1948a9d06f61aa56772a6320555beaea9d5b0d36049ae3f505aaa0075a816120655ba81349cca4e0ae011ebc2c2607050b780efdb2d79937f30ac9855dd9b198cd9fd7b5e12dd1d3caa3f00dda80f86a628046a20fb40dbaff538f87281c2df02d242a2687c2f0baafd8210a2559d476acbb2950270d456ff147ba38caa43f552ac5f790ff2dafaa08fc2021de3de38af3122f66f2c8cc086addd71aa461863dc574cf8a89bfc8d692d8f4ab27778311d5a018d27b1792cc26f5f3aafe4bfa8636349e872b9c9a349e07610fcc3d088e60078d17982bdb1ba398d372e5a4a3f1793663bbe9299a700e904da34696f9948695cebe78a6da0985e5d709d97c6adf6001effbb9d2a8a2ebf6591887afac2a9fcfc4a84ffcb2bf2c8b66dfe1f4177a02ffd6632765af503eb897ae4d12787885b41ef94277c1e2b7a8315a556257f3a74a4e109a078a429c306dc83e763156c9f2b5bf7d2ef848898a2f5a50c7b6a1dd476dc8a46c0e9f48ddf4d80b28c8194a174f58623f94e092f185763c3e7dcec7f2679ddbec964e9aea558c591748d1ec00540d09e269b85d6aa36d323e32c3a46f047589373b102d4cd78a01c0361799fe3e0ec051a58fa7ce386224da25037f38a9d99753840754e29b4975b38474c1570e7b2fed984fb3d6b81bef4e90a616aac20fddc746ba452dfd379e8c9cce141a9539e55ca6c47b4e995a13494716c0c2b817f258bf23626e18965645abab432148e305ca34406acfda7320cc5d192fefb5a547fa492c4b04e15789bd5e8432939ab3b1842f1b96681ea8a10b40b2ecf6353eb4f0a3b5835dfef670a73ad46a65ceaa6e8246e48814507b58bfc41fce6393e6ecd0fb5dfd2083376cc7bdcc4b5c45a0234690ffb5e71322cba6ebc8611ec15af7f3f7eb0dc9c5e180d379f107c31d05835ea1c4338c404a8c407bce2b526d015c3d2c0569f3b0ede542d49a74acecdf2ee05018f1b75f014993f0917a7b7577dd17cdcc83b98d4c99f03f4e1c4c848416935df0ee9046a74d7f2e95d2ae4f62754ece0651ea2925bc5ec008e124cd16eb21dfc061b9d15fd06e77163d18abddaafc2c7af154bbd4df10be6a6139c1b12349287ab66001cb39c9c502a41cc903d0164588b89fb53d88626f4d7ff748029a6c3f72b3af12ba5e05ad6c2ec3865eb42da966887ad2ca0c0e0cee63696159e6a6e99d84863932d48edf1960566959be0b14915c8cbef075bd427e7ce6ebbf9ee060081ac327f80ebb403039fd9ebfd2ba27d54a3047dfb5b83c4f54f3a3f330387ecb536e7db6f5e8cce9aa3af8e288fbb7cec215cbc05774f1597ea6b76ed08355f5676305904022f4d5ce4cc45f0390c413714757257baea325829b51041b349b101ecc9b615c8ce5cbe864df5fd4ec5c50ef10cac34f92eabbc1e227854e11486f19730f3d02dffbe1e7c6d1c0c8a4b6230e2efe03f6bd2aa8c925c6fb5595aff341960fce379b546882275c2210d53f2faac91e794f857f9489ca644c8227815c4d6194a1a4440740608d5b754dcbb82a82918e07d0243d8ff05e3c2020a1a4c03492f4b8f770bb8cd4311d7a0803f449391c1c608bd977ffec4d3dbe4012d7ec9d45b0e790d377c3d698da676137d206c5469dbf29f14cc3d22a0c9887f1383edc716c4c488f917ee8f5e02050f10c3fba63d961f7954bb47b3574cba08b93c471c9a8ba6c963410e341c8bd34b5689f38cadc174cbd51a78376848c3d4ac0e465f234172a9d607d8837c82b77c5248189b1d47a6e214c00a3d5a6a7432d14c05b4d2c9d6c27b8c8e32eb474660bdd3e54656ea08076409f52110105097075ade3e1a03dbf54b026947fceb9fa29c70931d3933b0c06cde335897e413644ebda78b85fb0d1d2b173c440198de487e05b6b1900d842b0dff04f878713556392da5db50757766f1179b3c52c3ec5f57c4620303871f48b258fd04796feb6979c2abd0b25ebb0854c24991b090fed836b0721da7eba9fb9eb47880eeca3e0de456eb180d7f645a8f826c50fbcff9b9af36556802ce11c24d49cd5bd0743d077147aa115d60b0f30a7396fd437fe85804b0b6292f89a0fb27e13b295007dc523dc052684983d99c836259533991fd44532cf5ce62e6e3fdb4094275ddde2fef0a3f332bba670022203af6307327b8ddd9f2058b0dee2638fa83d70b8189c7a2f4aae49b1d2af4697edf996f6b248b673c1d7dab6f31aabb2d455aac52fbe7589f62c1c7bd86258da1d59a774dc42396f0a1267e0e1e3add7221b22b9bbc5beae020da2634078a8dee030d98c79f04f0c36a2274f7bd551d05547bd54a4b5946eea1f8eb3a03c464e3a64b02f22ba83e3c49a196585da5b7a1e93cc5ef40a416ab5ac1511cbd1898819438346c92d9bf1acc49d8cf7c97cd72d7f3e00eba34459291c4151daea98a4bb7631c1d5cc5ef28283f788793cc14bc433c6789204c5dd7ccccd777682fa9573563fb2c60d49b45ed5e83bdd9598af15761bff4d2a68918cefeacdb02a023ff87502a524762152a50811f0f25a485d90eb13b967cc82315aeb855f33d71a6832d370c4f985c1ea30fc0a907caead3e85a4c5ec28e5df35e00b498fe8922f151540985da429f4c0f102bb838b08b44493b547a5dbe0a730a2836e675bf38c2d6ebe2105fcc2579fd8e58c9bd1e75d26af8c8678f39edc68733e23a81823df44c5a918f36aff7689a56984d0f93b45ab36b8505c7a6a0c0d606760dbc2490d1ff864877b09943c76d6472e9ac0e8c82dc32a46a208c040d9ad110062a051f3d65043103cae2d13dd813340cf768c7ac952400dcd88182905947cc64be1612c6d674bc6d012d86fc8ca891aa0e5007f378391fba8cde6b65573bc489f47db4389776027a8b413800fd9c4039671834cc5120c1277b9d16a4fe2c71aa0b100c1da7e665173d555803b390f9124bf63548503e83b47217152b8674d39edf46199a7b12425c249eac3a005c0bc9f94c9f3ca807fdb7991710f4f0a83362200bb978a174147e8f47d23a12d7fc0772f88a939141b49f31580a1026038ecb280c0f4973e04ab4eb4533820c4ee778c404f040a98b4cf77260f4df4f0c51179168a2366d411421066cc4e2838724afb8862b6d37ce9297a9975f725ee565e36fc2886ecee78e6b4397f7891d42f34769f87e015a960ba2916bb17f684685d6c88ffb65f509ab7098d5191c60158c574d7224a7e217a514e444cb5362393f8b080f3a7c468b752e688c008bbc37e68e3e010aade8ce8636ddbc91b91068cbe38f4422b9d751bcd4ddbe28aa3a45fb72b0f029e223c766504ac8bb825c14596d758f62aeb2b9389d8d8dc5e11844f07f330f338a1c0d596e03a84ed28fe1078defeb511ac680fd4fd08d4882d809b2045fcdfe7e4ddc03b50026edda6515a96c10dc5670b01f9b05b7ccde935277a9fcea4bdc8f8e76282e20e71fc444eadc24bafdc5e039970624e6dd6e077ca9ff5995ee17040718e017dff744d231b025a5723af1aecf2b1dcc2fb46282f9c213883a895ae508c733cb08f67f4737c9d02f1d14d53dc6211937d26ffca1b2bb5d5cf8b5cb9228afad203f746ee8576ac298059bee5d6707e8b3e9999101ea49dcd3e2a113d49d49125884f0c52b39d00ee2dd994fabed2a18562ccdde7474747f1beb66c975abe903d19306eb598caa7a262d66bd151a606b37aa708da5458e469c6f39643732e87ae2302087edbb751073819454ccbd05f186474d81542ff8c2395d90f1d4a440b08a1c2991a40d11fb7b983e045fb07997642f868b2cb7f6085b5fd18ff130bafd8e984afd1cdc67b29952a43dedae21873151ce98104fb386909cdbedc1413c103bef74db6dba90205f24877e9a6c2fce0ba84dd403b10d48662f887f080a06179aff63c1d46a2f79d9fbb759bd9701660cede427f772abebd749d02cc0cc2a78143f638551f7d98dd3698c8b0c3b072a3f88e1e4dfd4d5faa9969fe583cd40ab85df62781f53c7571535a1a66cc3148235615e7d786153c58124dbf62fee05a235974f660fdb4fade1e2ff356371ee7a78dd5fba508e7a6548d14081d86a9bb0f80914973655f430897e5ae28b076f13722e507d5ca4ddd5f41676f7fb897066ee10a655f3350e3cec1e9cd22f84ca4ea046e113527e42aeda1e68a6c02a2a12cc8e8b52142a6678f4a61d6109f29031a6a5e3451f8a8b0b27bbde4a46d308ed23c51d3b389ec7706d6044958d64366c69082dcd35114d79f6ffc495f0411e152b790a1a145964af4fa5e390aeba2c129185981d9b42f4ff10283cfbcce81feb8127ef5e1e7b83c6adce51fa15de670581fe038ae01d241386c15adfbf9a9e23a1ca8ba69735426761328ff3207974122eadeace310c355fc7baf994188035db1b6cc42e83c475506b6be892d73683957007ade48150c070f717191c9399756f8554055e698ee273f0859fdd971cd5c58bdb5e36b4aafeb589ac6dbab54cebfbb7a0d9b5de110e106ab8711e04ba8e12ea0f30f5860c0fc870fa0d75f8ba7480808c45e0d0ccbf769e503a48cb628510062de7b50c7a94e0b7049e0c3d2b96f78f2310a91302ed3346e02108db90b4822a5f7f0426f7fc41b6fef1eb8cfa3800090059bb987dc3bffdb05791503fe65f00b3b8d2d27d5bea88021698ace612313d3c0d5f8f4e48db89405838fbdc1e61d922f3bb62e01bf92aff38184b1687f15119b17c930854d0292097066edc168d270038f4b5015335c88a40cc3a3fbb1d6bf9671090789f3f838a450ad6474c70611face23555baa4f97563dc87594f1934a15718e98dda6978bf2b0dab28e24844e19a92426e1562e3214c0f3d265e645c5d289b065f0e3f4b0048f520fb7537abb8c6e7093299cf6186071ab966d5b5914437d921a91e3de28d648f535607d71d35972cc5f17b2e332a0eb54a1acd74d128cc110e777731d851ae8ed510334a8872237a11fd685873dd54c2d9c762204520ca07a82bcfc328d15ed52a33714bd49378fe68baf1c4f79b1e89fa7820c7e1c265dca61b051033149e29a578033bd5c6f27f266d640e4c488fa5ef9a21f894425091596e9a0afc0c36bed2c0af624404174d5151b39fb388e0ced719b0100e9aabd76a62e8231070993090f05448deeb84d80d75396d3b9265faf34c3404d91fab1e635108afb34c26da36ab78920228b7210db3d0bd0c055a76b78299e3f4ea0f1147d77318242146015feeb140c3f0beb2e7064e24eb823b19c0ec07cd16643e2c9848e837c3e33baf5483fe21e0631427a526c10ef097eeea51bf073416329797167f2a038ae0c7c56e505f02112af0e8dac1ae39ab5313a07ab221354c0837beec0a7bc0eda80cb1f738bc56eaa4a9085434ca9a1ac633fe3d197109f48c675ffc4bb67d33212e8ff8c462affdc3d99fc1d9662bbc24d28797473104fa43f8fb8538dbabb19bb34f186db8674fe8d5e22f3d294f86111d379a359c40d617556f65834160f29cfb40034c0dcd67e51f903c4ae9268271a4811c608010734aeb2e838b4f87c4b47fa3205cf42078ef44e7e04ce0d765a1382ebfb7ce70b7dbc180821493c702a8fb269661f389c5da3bde6baa388f54729629fa38bc1f34428dd602a8fb9ee0229d12e260648e3b2324db39634d63f166cfb0aab70c90da0a52efa04584fdf91514e7dfe02b3a3247ceafabb7fcee81dfa19747184f5906d7303ee1676dd510b86f2aaa2299c79a71a065be96e7f83ce47964279f7d6acbe6b75278e4a57f675e7047d4d963c6926012e903717bd96150d4249a05d4c53daa4b3b35aed190cd68e80fd175f89c2956bcbf5d9a3d0d48695d44f8f438257d499d2d9517fb3e114a50e0f1b1c263887e841db585afa5e54505fd047484606a88c29dfecb126d45070e9a7830e2e7f90197b3e6b699e976e84cef068f44a111c097e4d39b9f46f452f3d8a55c80745ec5a03bfdb920669b706b9045abcae5cfedfa6091a8a2ef943391aeedb7343d2edd6f06f11d7ed145f0cc8edd7846b746a4494219b44660301371a76aba6ad51e4ca4499f8153d0346c94ee00717a4e467c49ecf02ccb12db5aee73329ea5980d759906c64a5dd9f71f98859a34a42547914e0fd271954f69a87fd0f34396d69fd12190da93d4c7226f3cdb1f54713bdd76a729458857cc79d406c1aa37264b5908617ea451005203d844471cc8d616d66a01d5d15bb5bd2b34c3de855f2dd86764b2a50c863bb9e479a18b709b5c264fb0288b1fb75fce50750f908d245eb03687197612fefc44294b2808ea53e55856379d87d48342662db5a495127c0172d061f4d863647038d4247e8240d2ede8b76924639d505ab1effda2a6fcf35e79d2f17dc947e0c5b83dcec1a03a4d09ba41e6de9938dc03666c827c486b3cdf610718a503c3c69de30b6f904491565c4a153dfbd37416414032592bfb8cf72b03ab5c7fd6703beecfc71f8280e3f8c9e95e501cac31b44392021d1086a14a94c783ced48eeacf8ed4ce5b9dc059b6748169b6f44dbb042e4bfa31b384101fc460b30a83d8004dd8bac54902e27b101ad317b160ae080ac44b8159ba00c326335634f5dadede2b8a542e2ff455802a79001b083fdff75ce93481f4052eebf9de38bfa35390406312d8bae1059de42e86e811d70b9057039c863ddf722d7878115261ee19a7e724259064e25827602c9b60e48c831ee388eaf7b34a33b843309bca779a292454b1157c0414074ee8e0fe6b6b11deec05c0afe9c6c8e72f8c85c72061d7e9a5927c0c7e3e569e59ed3ae0a00a6887a9f321b03ec2baf15e0d33c8cf9766cb13c15151fc791e533a7414b2cdd36f7b340878a7f8139a49cdee5c1e87f626a10636aebba67dc8452207ed9a800136654f26b0417153cfcff0bc60e70cefb9b7e74c3917f01f9ed08efc26e1a1e65ac5cdf404f7428532121ae5923a9b3015d5c685810cf90fffd1c89e7a66820078ce0d036434e87df3436331cb431352c614a9b470d92873b27cf837f6e7404b1d7502566f59eac97098d125f06c7120b20b2a3321e06f8917b345cf9e93f2d943e5db40eac70ce08dab437df7fad2eacc99b8af4c1a4c6c17ded64f599d368f984637c54de441266d4ee799bbefc2c9abcc35c61e6f1ea60281768364a9d4775efc569cfc2ec840f6e688e5ad495811fb183f1b98c1fb8e8c49a6db6ed20e5c5a7bd9a8832e04b0233c924c92c9edc045adb7e9abd4d6e681dd4316ec234f2fd4a721d586e65d1ef5033a73197cc672493d60c71a335915b089c239c5b81f591cd1488dad9bca900bcdbc0ac80e55d08b618c0af0f99481d7865ed24d94f3b22e06438694c5dd82a8b6533bc7df41092702922c1cbb04db9f21b429ae98cf9b924410e0d39bf89052efaa3e703770fd3d179fdcc6884f576a15ee943512abefcbf110b01f86052e2c02c36b3d7d779df238199365fc8b94ed6eef4264817fc2dbd53c7364c17353572934b1966c3198d321ce308d8ef149b6abfc2c3c709df57900f4239d944c8fa1254aea0d916278099609c309eb640a8ddefb3435aea3cfe317b1d5c1da517f40974793017b2a3cf301a746e2344fe4db9941d83e191b05dcce3161b5b1775fa7fc5a78f50ac526243ea070bb4cfc1b4702188ff9c6fea30caf76a895be0a8de1c6eba260e98d5db451ab882ba4c2556259d7154b5410d7debea227040269ccd162c22b51682887f71ae6d6cdc6441b3cb1e89958a87489b3749bb540faecf753dbfb2c13460df39924deb439789747d5b9d2e1f46c35fdd92017751721f5e2fb2e0770ca5ebd0525d4722f9888329fa5d5cebda5e85953943063a1921fb05e0ee35fd1e735caae937045c801bbaba05a11a0de8f9903b72322567994427e59540b61c520117dd3012a06fe7b9212484136948527c2bbe587e4ba2e496687af85fbbd04dd63e513c0ef4677af26750f58d2b112bd1b7b8c05104313aea0d8dc01d87221473b9a97727777103af59607d2b162c6ea29d9759e7f60d0fb70cdaa787de324a0e46371fc583c7a5e9633c7688fd785cb2149d74b965170595f29bcf5db083fd6e504b4ee57ea0ba0bf336a82dd0c9f8c08ab446a9d1ea1377d35503206e657915b88e1ad3dfec0845a55c2faa520804f4477d6ffbe52bd37ad8499624e19076703ee6844cc9d09b16f2e3d68a5d584449a7d94ab2e232b397571cde83ca032f017d976692549c16366c99604c77904de8af4b0f20430bf31481f0d17216a090f0b5480f7e885e887a283ef8102692fa2a90a9916dc3b3a29e2c8e502b5b7d7c13799569d0c1a8194d5ff6e9e5864ad385915fe2da5ee46714006b34a22a9b833eb494793219e3f19371df2ca35c33ed77d4eefde1539c596340f6b3be7fb41c15ee4052a7b7d6b297a4a418cb0cce557b6f11de1cd578c7482e3241504e60adf1bd4c197188feffc64250c7577c58d760eb12caac0468646d0cbf0bd6a14e6ca47c839cb65d13987c419d5442609ad7fb076a00661cd291e863b669c76f05e9b96813af911c1936bbbeb79b69d0c121aaaadf6081883c1e6ec7df9fb7f66ff0b09e923f10c14a9ba3dda62927c718f3fa0162f66cf9469e9805135f2f2777dacfe331d852c4584e6227db841fecd19fa5f751260096659f2deeda435d961b8a24a90a0a809fdfe5e311ad277a0ff85a41cb6c30087088cab3651c0532fac3b976ec9cecf3ed2ba57c3bea23fe58873cc43396f3c348fa8f32b7070eba3697e0ec777ef0c5e20cb17361fc865d2746252f2cb1892ef90691d7a172c516b3c0b6e27b8729b03366b75dd818e50ed7105aff7f06ec13d9c17a72412cf7f06f32198f6522fc1bef69a66b6021df9a42a59253b8b13b3cfb9f0bd0edf438ba7ff280fd154790652ed64c0fbd29ccac1c21c76dcd5cefd11690c3c8f31fe3cff5ada3d48a51fb7d4d58b630941c92cee564b6e3135f23926a3f14bb0f85cda60f612a3939d1818c1fa28a279c7ca4cb5ebdfbb4ffe85b0940c70e71caefa5900f6fa2bcccfe1b95566f7ac8da85c9ced9abddcedf93667eb9d5d42a0bffab88d3e3c64d0e6c4523744083810f8fef2473a4e7a70784994a137c5851f667a2fe9f53e09d7b5740f8def97105ede83b662cd1623bc6cfdc2991e9bbeeef6a342adba27e9cc8eaf841035a10a3465a450696a48e02cad4c9192808f8852f5e99cfae23cfe0b87b948cce611f9ef57587e80c0c23a30ef5cc36271a4b12645a408cfaf9b1fca1c3c266e68fad27eed897b1bb33b0c5c1af441d53caf86e0f738c2d8cf019cb4527c614a68f9c9d91654711cae70d698ab00cbdc2a40f986f53504fbcf8608409b1c18d1ede25622b39dbe389515d67ea887c6eff91b1bfc3be7d9bbb356ee6ea311d78d04bfc8bdf52f0e9203c4844d67563fc50002d251bd7aacc38798c498a1338b1fe4b4ae747440f6c5a0f8c25380fee02a886dc938fa3a847db6364fa0ea4667d14d67af27e3d8de8671343f5d37c564c9acfe6c2fbe9de7f250c8a7b86139f4c7fdd064e71258af8947438755858cd1da4c9938846de12abf8073a21153f8fc6874b731dd9e3d67cbcddf622edc701ece5c6d80784ca94c2712938d1c369839a1f53f2f10a1a89b388b50a73c6b3006c28cc8a1284836581cdd4c482ffe0e41292a66b199d7aba0348e9398db5fd0f0fda528e234f8b644ba0f1770aeda6586af18f6f3c2dc829b3f092938b67f884bfc40d9e3941c26c235adf4e3db32a2a16a53b50003f27c88798e51c8a813bd56eb2bf53ef0ef4e2b3d6a6c980e72674f21d0bdf477f302d38cd22840e24c6e18d0c98cc6a710c8cc2a633a7b59a90bd893a073ea1b87bf15830e1e384749b25679f3ce10e0f9ab26010404a8df627f4db4205dbc7edd13620740e93e700f20975447a18b1454428311383ed5cd4eb6fdb520b08b842e2c419d786a2a92136eba3081763795cee17d4a5746406de0c599266c50455c71bbc3878b3399119a68f65c00f4a3f00b3c149c9c6111972e88123fa56d54ced86cdf807a5525e46f2178fda42d813b48161bfb40e499cd429fe502add5f80511aab5ff998e0a242a1a476014bab4805e337c3146397d366dc2a82206163d44061443f2e8b2cf125029e0454e58aa28b4c9018617409688acefdd067b7312c36a2755ab0b75828d9f692017c722c8d2cdc2bb313d68a4e4a9b03212d82f74f85169300749c63028f0f29bceeff7a542a90a272fbb5fe1f091f70b88aefbdfe8304a47c18b8fe40b25cb549f3c4b84871e521a2fa2cd5b2e026d70c474a1f3dd288c4ceda3022130e10c6c79f6f6f671ce6dd47e8c21c8d1d166919307bc63977a0c672f73f52df0527d9c079a9486cf295ad2a44527e321ecc03e9d4cd3f4ea1970e12fe18fed685358124512fdf5165bd3c5cc0e3e6373a49e299fb1a4e604c100e981a19428bd4e117966214b60d5ef0340de153e69c6b135e512f19c5565458a0dee4d1d4cebd55e8721c7020071c8c63d784f06abd5ee6ef12d0e9e89ce07cb732f954c41b53b8e829de4eff77ee45134103a2fd7a2dfb42c1a2e533e25c093e9abcc1d6c6204dc6e059614c0ea6cffe8e718acbe9c1e937751df9fcdb7b7865653c377a8bd604dbd336054636bef8e5af6e03a70e6c4d8577c084c8a6123fd88c75cc013301366e430e8892c5f86edea7b7a38da7a3dbe12b38952fd54652c940e5dc38d7eaabc4e6e621bf78bf72bf8eed930d0ba08bc613eef4a1bada0c4100fe64ea4b1197f0fefe4a10928b0ebbd1f846922d9da780383bb0dacb1c228d8fd722de3a63a51ad69a69c93f26832f4502981a3c53f58cb3bd77240d73c2324a3e6357b17c198cad87bab4c9879c8e8ff3774d706dc003c7df622522c60ebe14e077ee41c34027f2d907ab9899cf5451cf98e9a92a71aa649da2c0711b3a2117d77982e51284ed203358308ae2c0825e77916290eff356c84ba308dc48fd395e06d39f4382b70e3577f7330816e14a9966545ff4d8eb0e91373f0451c509a068e4070aeb92eeca09a7fb508d8ba284d396400e18f5d76719b3e455ba8c6b4d8880baff76b08e782df31e179ad65b283d89796bcec24aec2d4264f4092a85b3e3b990e8b6b807c9e66faf050f1d21d45908a6dd38ef9a6f0bf875404adf35d09429ef82bf7e118eaae455cdf97fa88870c4dbcfe8c8880fe0707e0a5b0283bd3e5071b6b0428dc854ac7c8f24432ba2e8aa6e8c2489b7428d7c682e44b858ed3ed53bf796c16a8c974e793eeb6f75441eb1a8b171d7bf97b09afa00e0d48a0fa76a9b9c63c287cb3ef58f0036f4922bbe019683ed3c610f013a5f6ef60533f422184053652ba13478373993ff65fa4aca60625267642ad658084a7aa2c6e0972aab8af7055e9681738c624666617f60db09cef296e390bd0ceb6bb10ce24ac1dc5a1728aa2a9dc9e77513618fdbddbe623d80356947d7f4c76c67ec0905e9aede5538014a8f704c23981b5562f6c9e80b1c5cef57c097a46039c19327bc77c93cc494e6eb936fbb21ec8f4c400368294ca5d5e01c25b7c62775b4943e5861c1539e1dfc4cce1e860f4f936023bee9eb77bc921bbef14dd92811dc93de775a4d2c46ce90667db3c758a296f754a8ef31e3a56496b0d53f3970ee25e482c1f53a1af8c1b45184d9f7dcaa70ead4afeae48a3fdac81192d6f927d4818d13a17ae8693af4c3640f082446b9e16d7f9287223ca562ab430b8533102f2479dab407524501f32cdba74c7c0f68c61a4db77c2dd87e52363bc66864ebefc5d6ab824d67bb68e2b0064e9e7c54405bc2eca8c3d1ef714691c17fddb493e6ed744d8dc69d9676566f22b087c9c5f5274435115357509f7cb6afe05f901491e15c5c2b829fa2281a6aed613f8778c94964632e15eeef1a37f2b704432946cdf3fde9521143ba9159f237d8471e644471a313ccbbfb12eae2d304ffcbe73d21976aef9b81869e8a2a4eccbfcb0495c08a40d4aa5800276252a1d4fc45555aa8f5d2c4fa7558d5aba3aab0629aa584b00d56c907dc1aae336b70d3f72175de393c5cfa92c431c30b01a1303186d23d359ce414c36eedb037a6409d778c29228b66eae808d10a797157611dab50af4b2dab47ab4b917436fce3d5eb408ba4b15469b39a74b108d6cd05a201713140a9d31b143df3836c3f07ceedd69b7b4023224f6fa327a15059ebdda5deeda5600254bbe9a5ad29365748e3b82ec2a35d1f43af408940a361907f0575b640b3e36a8215a12e71a692f31d191d8459e31679c6227a7ed2b66a00f58a100437d436661b280f137adc99ee5d57747627bd7e5630fcc4652525a57343ce543c077e08db61c931ff18e5f41f4c0acba9eaf119d1380324376fa2e71a121e75fe456004ee9580eab093d0d35550ffd9080929055654721f9f56a26eb8137549d6d8ad1ce97349be734f74bd342b3fa362554e5e874d83ee0b536ac989e267fa0f68716a962279c9edfa37778815763530601eadac2b7f06f4b7c0d2b681c39bbe4c91af0fea4f7a31a50927cc8c2fed0c24064e1b1db581b851c6c1461368738d032ea654e29ced460d4c2b831b15184be0b33351143ef0a5b6587f264fd44bf577ea4825a9fe8cbdfcf562b2c43135aa084329ea720d21b8da30dab28474d7b473418a60d5f0a5bb8690ca76d01ba6fd01e0d1f0e30ec298e76b0719f2a8d07d9498591f07d22dceb31f7f3190b03ef1182155cc8d83f8360bac32e9e1ae9f2dd52f75dfe41e8301804ddec2216a35578eed29c9c0b927f0edf5b647a63ab775e01a151e9edf14185a1d96fb74ac8d0c2d8031f6c13138087ed194159ecdac3cfe0a3aa8e4346dc4a396283f0401bd8017b5654d7c365bf701cfb03ec77691beb2b089b50afc2430d07ad18ab7516f415d24e00c61b5d99d4f83a9282afdb90caefc48f07d2908149eb31177dbd7b86bd99d0d7ff52e4ea8df5f3e0b1b34e2752787acc6edfb8d0c97b0ebe5f1ddd0733bb0d6056769901435ec1f1c0896201ad9c03094ac2f5335b814427764c05fd679595eee07bb6cc84fd732eadcf9ef091d28fafe1e26227ae22048b01dcd406c0235140237064e130b6d657ad083673801d81bd506a525069dfa59e6587183d92822821c0e5e2344808a53be34eb17dd31d05ef80750ca6ea47790033514874732fb76076eb799df4305d7914460e2993b082510ecb7ea4c1a08b2a2f5ac3bf6d0a226e0d203cfa1f4aa54d96d9920a972c6557fae0f3b0f5a25284d49db82a08b7082af9768b34283e899ae6c77ab6134bbcc9ed6de6f5029b03f2c2e4833cce8685812fa07d9417aaa7f8ca8dea12096827492d3786f60aa0c560110db958bed3a3227d8e5261201a7e21c3c757f376e9008851372108a2562f5e4e735546b58dfa7463d2492c32d1b025b56b9f16a85d96efe5a9adf5397d7ba3295020401fd79d17d97d4aee5a215cd56dd87f877b3ae4312be155dabdd2d885b5a03d4c83a23056398e1142796eb32bbfdf83f3907f6093ca4d916656a2bd6c30db1bbb68e525be582468c490aa05b0677340952fad1e32001f3c89f8ee0a559de7ec2d7ed84c1df48186f19ac6862797203f562cf05438802b4d6c039f1e68263c727e4b51ce7c581f0c6f39362e6f580e022568fd0d4604930bd2ba0110da9b63831c63b9ddb4ed4f103405d218f36e50e73dc42459143c0538fcd625907eae5a16c698c8ceaad09b49231c0242cd64f823b5b2a10f012b9106c3c2495b10315afe9ec54df939b2c9714372b3a13a8bbfe5384cc9d7c0f0d6977a408e290d1a00f3d1b25f723d2a7f5804477e959d03cd4ceaf536bcf4bd1be892ce7dd2c99cfe6d44bdd18aa09a5fd04898a89f58f14faaddf8e7c4a57d54998fea27f9fd34f51cf641ee226fc892795f1cf1c4b3705e91db17fb9282cf12a1c79401a50bd21a7afe3532be80119a6ffe9b3740f472972969836e811c8f62f8eb21884e12965528bd611b0437d02b614103e360b82d9cab46ccea08327b035c1c777ffc15c9a00d61eaea7831283742f907ac5364b6091c6e0f7f3b436f7c40d9c9444dbd82e4ffa0a62906cfe132acd30dec5fc59b2bf17474ee6e33ee3b3c8322b53c1aede611c98e458f24c0171fbc3677208e4230d81533ae6667be5c277e1a6224a64bfdae64ad9967baa81bc75ac8eab99c667547a288c727d5838ba85c1d70afc4f5370f59c009eacf6ddaa79981e57a97ecd98665a38019c065d54101ebdd3ac5983c3855863c37d962f29db13748b2ab83c61007af56881e784c397d21b04a4660ddaafe8aaa0ba5bbc924362efd94cd8f63b0e0660a2f3c121ee131efdf2d0535d809c1aa3820ab0a1273c68da6de38c346557d2cb11f8f73f1d5eb701f46e6518e0e9538c89f166f36eca21f827d044bde8c22ebd3c080ca6aeaa9dd57d07703f36e01dbac76897b0ef5049834a5abb7205f63a36e028f9a19500b5ad0ca1a35d80cbe873a9965615529053973e15e13ec56c172fe57ceb5da71ed459dd01a4ace6c40ae99a824b5d5098ce207d8c8893436fa3cfc0f712c2aa42a9eeabf373d3a7fa4e0773d41e6db1a3b1f0f98df490302a46c54988e8ea402aeaecd049a0021e01398e027620321acdd1cdc044d8e5551124fca4d71604e777d247d1d01d12980ca84993d689d0d554dbc4bdf26652c989c1c5da1af17dc70aa68b09b4648493c8eac4ace1f1fd6b5a51091c7dd265b5d118a8da19b86d5d55daf92f1c235736a9e5f8914b0ec6072a6e7e05027b220143c6968b1607fe7e9740d71a51e513f69c5254cb1f0044117705856e42c75423cd5e814b504b8ef46bd7d2a406081cf23a3b06918366aed92310210305670649693dbd3a9682ca2e40f26b77eb718673d92fea1093650b733f93da8ce4283af72d33ea56744bcbea60d11c311876417b0ef56e717f7445ab57166b9d8198865a8a459d58deacb26b35690da39b4aabd5d9441bccad4933833936d839c251301820ddcf02d44855779dd992c28a9514d1dea1a9946d3771fbfdf395623bcb4a6f4fe0ca8390dd1e9c91d6e7d6746c33ff7d59006b861511142d922f5f1c9abd9a106344e47e73bfe626d2da14c8741c1fac06d394eca297c72c48f21fb0cee319b1a0c4446790f9bb78a0ab59f0c22fc973e90b84bc9ea0c5095ef3387455511bb47c66f1d570ce12970e1b4849d04d7430dbe25c6d79dc59b2bd63e9cba0cb6c489e9af450cce5d0b432453a404fbcd6dc65483d58b4620c323020fe3a821ce2650927b5b6e20c2a2aafccc35bc9c70af75758fef44316fca58478a80d03ced20671e75bf7c8884b39d0edcf09060be19ba59dbabb8564aa685d216c39a05de58dac23cc28e4346f9e9e040701e8786d108cea77e06fcdb08b3ad798895404be66f115217e22c711bc6e7b27598da40c9c65e438fda1ef578af854ac61a8f8eefbbcc7591c821010298f9d87d9c7583bd8d3f8d994e0403bf688c1d65bded64059c60f91558d84e7e97d90749c9eb386eae605d8ff36952bb0ea883f712c9085c5567027f060311857640c74283e332c6e8d3ff85bb14f95e4bc12b3c84ffc1707599b4e24a646a30f3a4e7f38ef34abc7dedfd4f3def6df8bfd9078bca138c7119a82d29db078cebdf9bab3885b5931a09bde445b2953027db98e177ca336b1d9413a37cb44d7582490fee544040beb909ea39ab6b6e13b734975a901a6988fb3c80a27b5ced598584649aae9c889696f0db1293bbb4fa3bfd41bed2eb159a2e2baa103667439a7cf4e6ed0c2e6db5ff06379b8b526e99a771dbd6b42c7dba2e48c8f3b8738d7588e7d9dac2b3eb75b2687785fcf75558e738d46ddc6f1297933565bb067570b0c8671831b5acab52dc1150738681358a9e85c260fe9b78e130ac13f8c9564577fc81b9c66a35e53fa6dd1d8dff4b47327950d390af8a09c1cd6b89144ea67b4d21ac8f576d0c357c4d8383c5fdc32b5fbc1af03979012bc6806b4c01c84727c261dc0997d1ed825fc350d3d0121d9b29e06e5fb2e7c98f6dfa9d7af09c8884c088160e31cf1e8a2cf1372c75d3c8944e31370076292ba0186304a7cdcf8ebd1ce0db9a7fb0a9ca4f3991d7a7c949996e8cfa5260722fe2b2168e2b7839dd219deab839ffb24f97cff5fc3ce566862529376d784630243d4264be771f25b7c6f138e7e60e9d77e9d5ea6c6de3b56cccbf7a9540b366ade53dee86a7c03bc7ee41a35480d88ecddb5e2f29cbe173dec0214a34fed76157839b7b120f149be3c1e240b41818bc0ba9d82f153ed8fa0366d92848a7709fdd47bd02cac48ffcee3f790b4ea1b84edd73e85446748a03d9f0c9653ae5df24dab83c306699e401976c539b871a215b8e2be99ee252782f0cc406a9880d0ed2f7503412e15ec76439d997c61a7ca365613cfc9353635ee5f03f1ef83b48cf7bdaa8ebf3358c5f8a26f9f6d16471f7407ec88df5da0dc6d4dd0894dbdcab8e328ae6a35c63b89bd8c468439ea6a1729bf4d333a665b5798ec7a822b7ac5b477dc52320f163e819442813dfafd70b4c7a22995d8a157ccef7ee962a562b453f1cb51e8d023b81442959876ad6d693a127e84d32ed73cf6cd95981d5d65b00bde120a3de408bd16a00b187639c525bd607c57f54b2928583f86955a059116997730c83276624fc8940469b0d50631f0c8553dd8ef6ae4e7862ce250b7cfd29c5f2e77a7bdd922b1a95518ef38b5abbace6027b19651886147fdfa22edb16946456c7bc5f0ce972e978aa11d452f59c8d0a3797169b92c4dd2896d2f32ef62842b5a85f1ae442fab91218eb4d265caddd2bc4ec146a6a06b2ed811f27231ddb56d3a24a6d35692f6bd5d5cf7ad4d1282b1731b2574cb0bcbd161c8938ddd0ac69a16af0c7196a8d8c56fe4ff683afa87d6e749e326804f20030ac4e8573ea0d2704f7959d3e19f123965404c30593b1cc8505ff18063920a3b191c600eca286e85f1b5277067d20417b8a7213fc32243a6d934c8282e95dc20ee254d478ca11d0327589d60f84bd6c104b018d67be947a8aa6beabca0bac1ddb3a8d9f5f018798c93a2edf7e44ef8805107be6e72406540eb3b88efc2caa534a0a9ba8994ebf0daab5948583a0f17c1fb91c861031da061c799ab6fd9ffc5910bf9c48880f87982c284eab7f019b70e81b8be7a6fbe851a22a37ad7d679e36350cc1a26ca28fb3e5d5965c6e7464ef495f608fe3b3863150fab9f432c6c4f8f6c171ae161945e18f6bc4c0f6290263e87f5e7ba533e1788329ad786caabb2e2e94763da0bf61b6dcf11023476171cd4bc17d67aba0d43270aa79e2166289dd78943a242e351bfc5a663e1f629131f2f9d4b30cabaacfe7d3a745cbd85d7341d2cfe3efc370a4a3b0aa6a4a7ed4a0ecae9f5047d6d626cf6c091914dda3b05a408758c654161e0ac562c2bfcd35cdf127467e5913fc64d318425e098777fdbab400161fd2fb9b993432d6b1ec0622ee703694e5128ce0d17f8105e549c7c12ab00b0742fc289bcbb87dc3745a3cd02f7bc840b9bbff1618d5a5a341d57f1cd541ed9d092cdc7779a9ef6a8f76674110808b8fee5cf6ffcf6ff13a2bb700d8e43c10d872e020717ebb24179d1fad4a0e414344cbedd59a4ec38dbae1c1d3efe851695a818455f8819d53d250418ce94b6bee8c10444f586131a76f94018c8292d456aab5c8d03e90398de1a4e373c778d3599c3e083205881c2977b3c55807cbcf4aec2d9b556b63df0431b047af3e8250e41a4a38470dae56e304b408ec0b2f39a4c4bde4dba1b43bfee86000ea8160b04031e12c9ef871484b7264c47bff06bc0f8950b64b2f78885297133df0db98023d10a590c894359ba4d2a35ee6f6f4b52cd7722a2eb79661000b99e925bae7a530792be15a7d156dccbc8b61717fe387062676aeeac0186d14cdcc874d6784bb7bf35910e22b4c3e35e47c2c5e985cc0efe99bd68dffe515d8c287f1b79ba2c561ea37909b75e0a2c177fa31f730ccbf138f44b0f7c64f25812092300aaba5a136f26707c927b3f0e665f278b0d009146b7b08e2ac2a6f911b7d2b88bb74c5260faa55217f91eceeae6447519d57706e07bc47c1890ab9981709380362251214b6cb532f7919722b9e60e3890b7c0d8b05835c842b357b25a0b1ca55ee0cfacdc49b0dab64fafc792a693f3bb31eb8b889bf640593cd55902f2b0bc9dbe08f1e3a4b4d933dc9ed2565b5337b600b833b99b0c45d10bd3c6c9ebc4e5ae9d9ebc7aaab62a73d13cd8ebdd229d56aed99fc1879e5d5078eb7233eca87595f3135c68e7aaebfa26107ca4c1a2330b83a71e7d9b95b7843066829fe0bfaf4033c0d614f7777199831090a6071e8db4532f33fb3074c3ddbccdf2465345b449639ba43608818e3a1b4a46cfd2147b1c4da9327cdc563d70f6714d84b14bcdd119fb20c953feeca2716895901e502b6e1c3cd9b6178be049f08747857444495a53e790cfd8410d9cf690102024a03d2c784f5e5e517164c4a8848d0b2f4982770937ece3d0efc3a0bf7acbcac1cc70bc2ad0c03b03d2cb7c501a719584100aa9b24dc03da0145eb76dd4422d0212ef2ecd6bcc8770dda5954960b1276adf0d3a26c1d7bc3f7c1d8d6a05843ebcdf9b6f30e8eadeafa2d7280ab0970a4754fb09000b0c81c120fce087ec88a1dce137b769e4df5588e074212bb372e1a83af89e21caa72a1cfa3d61e7a2135d542aa8d4aacbe07dca3b0114eea270f2d3396c8ca7f91aba1499ab4dfa9577a09d79b9067dc8e089ba4b4312c37ab0eac6687889d8b94a70ef3ddf40d6a9e986b3cdf9688a5971bdeb8b08080ab87c7207ef46b44162d5cddc9f7f389b68dd5580a7c7fa75aee9bad66ab515a4bb6b58ba032f98c155b8cd1064e7e1215dedd29322966da2d90bf98be7a06c9ce6ad593a1cd2bc2829d0c5b2d469bdb2c63881c043a0c840f575c59ca1a64e0fcd6a77f798192471b03b101240628a1200550dcb148981ff337d871924d019215761a742e3cdcf204f8ca5e4654fe68f754ad9ed8eeeef9a81d971a7d8a87af00da91a64388bd3e4ff8e223b8bd445121e4d9da23439baa9ea4294e08b942eff676d6f2b31c91f67d3e6d9aca85055013f4b2b1341326840f2251396511322ffeac542b0f2db43a1e2740db5073dad65d2ffa649685cf93b4dc19d38080ba43d4a2ab75cad74cb469e0f61cc878c77f9400a119fb158eb298bb2876c560bbaab2195ef5a235fd5faf03091b75629193d16556c66e2c3a4241654ee39be5db4f87836558fe50242bb644768dcf8299d91eed188ff3ecf631fa0c28d68ac90c87b07532d4f7f75b2801b5fdea9f3204df3bdd98d5a62f543c5cf616168ac5d31e9404296f7f6c122a463619a19c1b20735bae2c78352b57ce17c7543b61f61a1c102603be3f7b2a6e82aefb9326288035b8ee075af3c7d588ed3ece43154bb70d1ab822d1a2013ed730be416a3ea03b71ce29b5fbf58688b7686c44ff78480b7694bd1b3308cf5f2e53856d8012bbcd08e217fb499fc0c0698be195b50ac71ccb313cc1d502743ed5fa37f51b1c790238eae19cedba209f466023ad179625b30cc59261d2bad05368361dab818c517f402d2f3c930b7af0b2f80a17d3aa3b425900765b82365a4f8aa22ae5206e9a788560a3fdcdcafafc3c06f4ebd99245999c79374d2731ce888f712ac933ed656a491bdf796524a29a54c01e305c005df0594527a29a593673e6d633a9f8af8c1991315b25cf486e441366cda8ec09db56cdc663f07266d6bb9a8d0d5d931361e6487709b7d3bc7ab1bb343d87bb42d9de950dae53ff6af6871f01ffb74e2113bf4e4daf673b2ed7f5604b9afce87a8a65051cdf096706dc89bd5d52e906d3fa7b3c7dcd5b1ad5dba3affb10fce6027a5d33ecf9c57778d8e66d0ceae9a42d6eb2645e56ed3b6d4426d3b7577dbfea4390c61064d273604a5c22be0ce7a813a2a64a9e8cd041e64b7beaf2f0c3cdad5cb07f68d79592e4b86ca7a5932f66b1ad599aeb21c5b65a95051edb46fdf4b5e543adfeecbcb6bc987ec0e435cc8968123b566091046bc422f9dafece565e6ebee9aa6508a1dd8d4579cabbb475729ec831994cb610862e00ed4d124478593b661560c970f795121efabadd57eed5a835e4c0778500c2fcb7811a37a598ed96790b7ad0466549633c37fea6391025bbed5428692f62d01ff9940995d3f27fa9870da3ad8faa96f65cce840ea52ec2c30a5c1c2c6a491d794b49e1c5edd47051480ffd4ffc940ca39e7bcc9299ed820b52d65dbcd7f84005065c800361b904dbecd2600a00e32c89ee55b5987b432a0fbe4be2d2ad464bed3360d1b94e10e1b9ce1144950a1d9200d65952860b65789e2b4a7c823c727c00eda3a8a09ac0d5ee997e678806efc35245d1bb4e17c13e65863521e98ed0fe41276186483399ce2881436e88552e4b1ef55a638e1b1ff8553fcb0b6fd19ba2cc74ef94ffd28b22d45af3285cbb6a254d589fd82899dcb41be6506239b1d3144e7f73ee7c41bf3a1f9d5bd6c960fd9af56cbfe70219b6507a831bb566b43d60cba4e5bad3fbb2c67192d4129fa16bb65c7462fc70a2269bb973563bf6c988d63b9a6f420573350df730ee6f5dbca4fbcea3ed2f26d7bdcfe22adeb6d35fd36fd1ffefbd86458600a556025000dacb26e9033e7425a4f0ebd7a0067100f8c6bdbdff61f9143fbbe8f3f33da6758ba61e780bced33a80ad896626d83d2684bd1c40667c8b4a568c106abcd2980ffd4cfdb8abe658617b6144f6410cc7fea9720a26d0fb40257a0691d0b88abbd2374c2abbe0675ccce39971510635b4e9bcf0c9243ecea64e704b06b5805037d04bb41985fdfd69f5fbffe08768f5328a56676ad31a55c536b9d21584011382f783db95a12e446172d2328c18d94a41e84d05a6284658b0844745e666e40d101ab086b8bfb90a60710465bbab06ed8a08106223ea0646141081f6c809470856a0a15246fa2986c29b13aad2d33097483c68b4b0e2276d8a1082d5ac0ec586fa4f6072a128cafee9edddddddddddddddd7fbebbbb4b6d55b5ab0fbf2aa373ee9a8ae24e7bf5e974ea506badb5567c3b10779a7e08b66da70f6b75eded09b35a9d529a75d538ca62ae50e9fdecfe757477ac6bf7de5bed64c240aa9829a8ae3e17d8f18af8b3055e051f05604fc8234391736256d326dcddddb350c6945212341a89122376744ce8daec440e0a194fd80352f055e059901f8b77b417541f9a62aa90ee186060cbf169f32f910348f1d7d8f6e2ecd9bed682a87d6028cb99699d11246825764cd448c89e320a9e1e0a2ab0e0c5f1029f142a30b0d90dde00100001d87e6c3995baa8b1ed6331675d8c40f49fbe7f1f9a140b81ef379a809d4517d2303450b69452a09cb86dbd72c45ead70a6decb9d536c9fede17c666797d8388f2e8212768329e8063108f3dce0cd66cba1a3535b89aa9d77cb36858cf217615d1d7d6fb7273ace86c106a98a0dd6141bb4e1bd608378dc601637e87d166c10ac608321051b94f56c3087678333141bd42736a853dbe008131b24b1b3415a58c5123b8b4534bcd1faadc48325681bdc21b14113233658d3d9e009bd4114b30df2e46cb047b6410a2a003768c1b7c1f73628e60d8e788317dc0dfad40da6a01b5431378841e881370ce8946eb3e5d4b1fa8f7d6c9f5aa2eb64f6257c82e2ae803aa50823d4fda4e3a980c4d997383b1e8538fb9adefc79e86cd39fed2712d831d39b6b7902a77d1f94936abe30659654c01c69982440161f92235de33ff7ef833694af3ffc4f72ddcf59eb5a8dea8b0ad3ac1163ce281b5fa633ea9166cdc670a459b5f8880f7dfe28a539d4b41068b499e7e1cfde1a4ed922a4945eec01683bc0e28c2983ca96a4339492295c02e5a4b55a5c29978c991a10c4a2b74119622c628cb1483798432cce5d450d56acc03767f6e5e011537b71a6de2863b492218396d9b3bd11ef037d6839d4b4106820187ab45a8f974399082de85b99b5b6d27baddc549c8165c8b025bb63ce096b538e7b855bb5e6e4c85ad8cf91855388058b695bb3c10bd92ccd6c0681ce22063d82c483b51a899016561fda3e4287462b11fad0fc6b0ef50e09590c1068b4113a25be844c8416f5b3d3a04fd3a74c6f4ebebf11b21694849485393217764726428beff19b086d9f3faf767f76ad483728438be516c50dc417d33659c4a0a59dd26bcdb1a3dc39321375066d7fb016fa0c92dd10944d815ef6f20f3e9b613c5e8cbf3ab0e99d7eb33f43b0e99d6eb33fefb624d8f4e67ca47af9881c5204d26baf3573bf28957cf995b3d62eeb97a66165307416c195c1c8b3c2a8db5a23749cd71a914cf80c76da530251ddce0e95a37b71dbf4a74e54e85a51240fba5bd3366f9a6b45852e17bd6dcda0fb356d5e3ce89e71dbfcc99546863d7fbedf607021bca3b78997acecb9db6177b9a810e5e261b8f6ac7ed6a65b337775be5f54885e4abf6610c55433c8f74de342f8be799957473bf1ce62963d39bd55e933c7c5cefc13a8b01326e39f346d57babb57ebb58e3997b252a4edd433a6fe95cef049e6e8ce6fade193bbd3dd763ad20f0c14e991952876f88fbf3f8be96267fb96d373aed4c4b6e3dc9a36ff6c8f80f6e9b495524a29a5f268a73ed524a594524a2ddd795d2925d3cea618534a919aeaa7a1d75e0e6ad43164a23876aca462b2d2ac6b606226761dadd7b45933f33693808c6c36ce8bd5c0f4ce6a2ad19d975c925f43f4ce9e24d449ef153760b1b3da294da1dbb414e42e81b486e7c475eb6e07bf278b4165302cc6584eab9b66e66d7e5797999dd3136ce7b48e334e2f143af0ddb97647842735b7e8910fd5973f6f9842df653303043647990b139b8e366cdaacd84d9265759556dc2420db9e54d68226c1618f109da34e364c47dff956e66267e7b11334785dce9c9dede2e22c4dcc4841a573d4893acda02453e846cddbbc50df0e6ad7f6b5d5936a01cc33c85d5b59e79cd3070a4a6863c2a6d5c6ed5857a880704081113b418dce2d9d4e67024e7ead07ec59c0cef978c940834d77ceb75c404bec9c1780d2a44da5926cda94be1fed8c26874d5fd7d09860d3ff34559bbe2d0d9c4d1f286ccba6bf82859aa64dff05934d5f060c9b7e071ee4b0e97fa086cda64f43caa62f822fba066c5336569b8a09d9542ccda63f652d28d4761fb255082123845c033b80c8c043520d43c0e4e031c4a69f01a5b456939452231c5a5d740cb16b6187a316c7a6417ad8f43358b1c114aa38fa3284e6671772dc9c5f4cc85af434c11ead49b8346b0d6993263fab239f55ae3ae9106ead9a564308bd86b331b39e28dd13a57361a87b8484a09c3e6d64b41c7d5642d602850e7a8e0dd0d9cfb9552da454948a5a51219e19393f3f679c5404af989b5e3b83b326e65eee4291e77e0d1ce532a259f496bdfcf58c769cec3ba0b7fc21fd6f246a32e37b59feef1b8956d091c836d22c9be3be689e77fc60daf2d3a0379a15a487117b8a5227f2ecd039b9e399417f3e1d2917bde5e799f13dfdef471d748e66ed6cc36e12e83e3d3b5d1f74f9410d1cf8f789bef7be062e57b3323c9167fec733efcff0fefefc3b123599e38cfbdf48f4f4469fc829b5a3f61f9ac73ccebfd4717e810d5dc79c74dad4ca92fead3131a0fb541946f8b3f438fe433f8b2ca89e364a3f8b95c506f45fd0209a423c1c706ddae5f47548699bfa535df5b0bb83e3411e4c9bbd3a0cb8cd5a19548806f560067d3031e04120d0f8008499cea4934e3aa907fcc74e3a0208260e6b41030790042a4016900cb50f7940c7854418c36a73cc491bdbd231e7426cab698860daec7b3a8bd6be87d1184104d249461101c924b9934b3289d401ad60319d6870759dc0b0f0851394527a29bd01634aa0464c385cd1828298314e8e854c9b4fcc0b3f7c74f1afb73a1ed973cff2c6ccc6ef5f7b00429284578414c1a16b9aa635aeab881827149a8031e130d0c636cb199d0f1cf40595a32936a98702c4d43cb169e2412a87992c202a7c595274496f7b9f7f2a595e121530de03dd2cbff342c7a56949a8681c6810a26b9aa6b50d269aa4b5fe22ecf551d43c848ede9cc5ac5892f096dbf5250917b163a67c78570d2303d2bf381ed1b7d61fbf62ac8566630c866be3efc1185f5b6b7d1f814c001249296064a68cc04993021a220a4313274a38123c8b8517640f615c6c4b2f3c6cc8c24d51c650020ea45dbcaec670535c800b38b0761103859ba202d0104d24aa1f7024c8326fd4c8100419299cebb0848d176e8acea506076622822c39e140af0b9026bbef06252a38100ca9e8696ea06117ee827c45e140594845e934a604859ba28765e1c09c908a928513dce8705394494470e04c3675c5b08423810145bcbebc082da08900f9a07693850a3745bfe2c2813a3e806de1a6e86ab61881a1f2859be210160e241152513e61caf2859ba2e4510407d298d28495b8fad2466a07aba80a146e8aa20f38d0840d287885e1a6b8002a1c580ba9e85686388dc9243680e87053e4c10107a208a938954c51dae1a6e85a9870204f48455966f7c216111cd8b3b56445aa051c48011329a70a422a7a97283aa8705314ca81032d08a948f43786ec7024781521ba4069000f1c09288ea8385b0813e44bac061c288654a441a42859e1a6e86ebc70e0f8b4e5eb82908ad3879a1d47b829ca5d130ef409a9487f8af3870a39763850850a0c841ca9a1c24d11031670244c89991ee0b9d65aabe73fee0159c4761f9df6e5e1b401ed586f761b0752d36e480a2aa0229fe7d2a5e73eb69b17d16a2780e8ee9b159c5e69fd4010fc467cadd504e3d10f87a6357b5982d38634235d6bf67ce8becd811f826118829f87af957080f4bf8fede643f6bf3157208ae3f17f3fe5d736f3bd9f6e75f4b4b9f74e6945a1622ae7fd377e3f65303c1530e839652dbe31e78db3ce771f7a8356fabdbfd72cc35cb4868bd6f7b7b216129c61252145e7f47eb076c287ec5714b9efc151c594fe3efff714a4e098fb3ccf7fe8a34993668dd8ec7bf0fb3e279af52ffe5a67ce4f5136caae69fbe6cfc4a2d9eb5a67ef7af673cec6d957d1ceeadffb33f933d1a56eda3ea99b1f1d89a6eddb60dabe5a3598b6efeff8bd98b6effb72d0e81cd0fee6d797543e543583bc51823d28d9e8f0afbdf6da6ba9a52fe728bffce7fb9c36dafbf9b97aa008f5fbaefd3d188a326adabeaf61f9be7e0f7e8f828dfe5e7ed9ff28100733e8fbef67cca0effb0e7c48eeef6592dcc9ef3398428efb9e6806f9fec6ef5fcca05c53b3bf31e72668f3b53fefbfcf697fdff7eedfe75cccfe3e69b4bf2abffe4b7eddffb2d6e3f79ffb7c230853234183864d1bd0e71515a7df74045160d1f741f9579cbd7f7fc59cf76fbca38c02e18904dfc235b1f7b558a45fe736e664e8771d91a8064eff157335b60c3deaa722ae62ae04ba5d9f059d80e3b47d2ddea881c3a28ccb4196c5a05fafa848ed0bb65f4db1d9341c7346d1a5f3e7a4d1b956d53973ee57d9910f25cda06f9455728d0e1f63f9b9591f4b1775df5152f94ffe1c31fdfdfdfc55d6e2826f4511e4ec2847ccc9a83dc28722d1b4e5af61d1740451a4d13929a9eab4ff395b7e5a8f8f0fc99ddf0233c8777e0f3ec8cfc10cf202ecfcb519946b79cc25c00d15fe9c5f6ce74c75db998a55fe44e95b52651936b372f75a6d3bdb3cc1dd6b1d73eeeeb5d6a8127d686aadadee4380c21443a81415e8d5829e9dfbe4e0c8f5482392ca3ab5bc8effccd07fa876ae07ab0d948e0f49b9b3be3a9a4ba00f7df72217f2c610a6e7ee8e2de54f6faef89cbd70e4801aaafbc07fe8e784e9dc0a0ea650a62fa8ad85894d1bb56336ecfb6ca3e0c8c1bc3a2af48dd4a3ff22cc86511be6747d5477e75163a5adf90c33139b8569f935bb534a297d7277a772c87faaa5694adb94c40681862ad4a933a64ebde6eeb3365a7ea884bad327da5e0f3927a6968db5d65a6b2dc8145291495aed9592ca0d40748cde3cdb241f46c2b463915215969574c4a48ac81b2cc0d975d1214477e589540a62395a58beb4cec0406baa9f7d9a449ae6cd8924c9fa7de2581d6f7b9fffaaf4e52a01618cb194d5bc79967521b565ad97654d51a09e484511abb24eb05250721e588e8c9a762401001493cb4385222c97124fa09e483da132400d24b0ab1a56e0e2c50528a929399c59e1799ee7756d9937bfa1cb0c98988bae3877c53e005496162e618ae001c6b610264ee5d23d3979126eb8b2064b8e9040e9ca61aa8aeaa4e82a3dbe364739bcfb63449795f58a38d2e562b39ce218f18adef63eff3e6e8fa2a41c5288602294d6587c31c61863ad1a741b63245dd334ad7ddcd0598c31c6ae75e5b431ceb6880f1b048ea08a28fdd811840b2c4861023311468bf3957ff8e0b9b266e70d71ba352d1b5e362b59a5209689889f967e603739bee049846bdedceb2b2c069131dd510ca69da661c98c744dd3b4c6a2e5471590a31c00c86087940957b6a2bcf1d2242a86a6ad1dbf4246e9de7b85e0901fd0c62a93b13233da22337adbfbfc2369e13759158916f1eea5030f1e55647c0023248a89d5911230a02c55cd74e89aa669d71863fc40158ad5bc79d6960b285cb5b7f1b6f7f9c7506ce48052b3d65a1c57f3e638b6641c37e81a97f66214668cd501858f060f40440909020b0727a61f5b0ff2f0bca61d61de629e2092e6cd959a92444d55e117415c617cc4cd67f47d41d2e4214bf3e64e160243a06a7188446525fd35859f5a5a4b25b8aadcc802431a2c489609122663430f5dd334ad3f1c3b9604d1b48b12a40a9215942cd961a8284b5d647a107372b3640b36c18b0998690c906f19cb5e0d4e474e7252961aa0bc2f49497caef42055770042f686c9dbdee73f0731a10c4859237c94f190b4ccf0198c31fe116563fc98d25f6ea8670162ea4b4a7a2283c011aeac43d7344d6b9aa4bfda00aab55a6bad8d529a376fca4974546dea5f4455d980260ecbc3f322c0444816141baeac6081da216d28fdbdf75ea4174854f3e6de67e918bb57e2e4302a41c4e4e6c997383c56c87a822a01894aa8ed59aa6ef0c104efca95af231ec4785765867c1fce7295f5e3862c584c3c174e5034efbd3b8cd060481ddd7baf8e1b812c2b1b665ae802e6c8aae08293aa05ca4910cf53e2b583c3f37ef8508483871f3eb471c12a79c20134e181ea9bda638cb115a7797328ebc28a540d63bc83078f0b045f85271409a18b3c6f099b22385c523a9d6e870557e238716d16335bb7c106295467a288140162938401a213064953c5218728426ac8bb1b330c8155250b1a1b73499634454c6e8c2352f8da7befbd5f9e57e3684c51952a306db40cb148b8423657c25c08acd1656103b653c4a4470f5654a6603172440b143360ba88ae06e788de3c67248c31f661410562ace810e60211657c30c2c215901e1d13beeacdfa6295030c3283a4f45c0d679ff6d65a5b24356f4e95ad74d6bcf98e51b4356ff5cdbd5e2f307594020e343eb674715a62d484e4de7b3bc02a226a86252e70caa4a0e5498913bef6de7befbdf7de1b6669df7baf0f329ee7795ed2bcb952539228173ca62ed02db22f907cc504302624402ab03871d92e2e37e9fb84bea0c1c14b13912629203d79524adf7763f7638c19145ab0a2b328d41d9856a0a0c694ad1a8cb470f9a1288626aca41d2892c4218143d7344deb163f880db1438fad2728262555592cc797a738cb179c242d48407c18630c638a8bde3c475953e4186bb9b06463121b177d6351bcb256942f1d0f648d54282f290b90e6b1c68512deec8081ca064b44c9148a4525c7dbdee7bf015345762b8831b911c60e1183af5c0dc1c3f36a349d99ba1186499a333a964ee0e16a51b2b51bc3d7de7befbd75df7bef8d81c48bde7c7b5ed69284892dfa228fe64ea7d3fd90aee55575b459cc6cb5ac948446f6e55896a625e99aa669adbf96a065457cd9071411a67352121297c517638c31c6185b0cdc9861e90a140f3ff0c06121523b3cef8521d600c1a5858620c0f81b2d558b0a5c74e8b79382990d4135440521883821081962a0e25eb26240c1c31262c9c8bd684d4129c95154f5a6c7152c562ad796229e25773a9d0e497a160b619ed523090bd7552642c9427901855edbb576b8c24a60e05825314794fc016fc2ac0d1d4ef28164e6e56adb7baf99e6799ee735cd9b27c9517aaa56f557b62d401b023d6e9837dfb915f712b12fcd42248b1579f7c5e97bc37defbd421577efbdf7e66a731f7e8062c5c8045858900177ed09fb6a5961e9291b994e3a9d0e4896bbed5a4dbb0f88e190d28586861e7078c6d9f882e978dbfbfcd7e09134e6684988274084cd1c642e182294309e5a47660862978c0185e9743a1f25c6b65fe1c235b659cc6c7b049778a864cc9a696ae810d5400008080043170000200c08060442499285699ad8f414800a569c4c664c3a9dc602a13092a32008a22086611884611004180380634a59a42600471c1dfb6c713b4833f5afd5912d46e3ecb860aa1994b4fe701c9d03d9fbf7d0f1aaf73a3721fc2c5a6dc720f353cadc10a689976f5ed4584f67f911df245bbb1c05828bf6b97e6bc5963b97596571e304aec4c9294da166ea9ee60bef8b7885ee827b15cbb93555268b85b72e1caa921e1edf676c29e119f616a3a1945edb7f5081f2ff974522aee0ab99a9d4d8c9f8ffa94b3c3f7e4c659042abdb51ca1d874290c96ab8c40ba6e2a6f0dd14dc06f2a5c50768f9cc9ffdf0c3c7bac5e93fe3725863a2eb7818d9274e005abd0b3ce1fea8afa200bfc00b31f07805e5f1e29a5c9fbe48a9ebb33cde5fbfd058007437b277aa99f06f481137cdecb6d262fdeb5ca9bbd0d67d76276825bf8b9ec1d81f0f70afb52233375b43f2186e50c74d044f4713b362cfd90700ef57e9298e5fcb0424b4bce990869c108893ea9a5159aa918ce4d0c091d2638fa6510c2de739e139f2b8cf70edeae09aecb9cf6cc3fea8a95de1877dd7c0ae4c59ac0628a53e811745dab7ce274101630fddeb4243adbb33ec0615c86396f65c293b94a2097613c4370161030c82534a4832c1285aa61a0f40317e0172ff07f5a117304965e4be0af44fad6e4174ec5878ceff6a91040acc32965da42149ebf0f5313bb7917ccd6b24d21070a83e993859a63226cde89d2c864a7f3046d2720c90a727133b04a8df2ac9855bd09d9af0b249645e7d4c44f1348df61b9976fc94098e2a60f98d70920c8c156ae7b7256b57b0d6be0e274557fdf9f4b6aa6005c05140e3f55f8a9f2fd34b13c4194cbbed49effa94a341c8ea83fa3835cb5e300a73530847414b118af9efeb1c7336990a035b7b752980012ccd1aaf4beefc9a9e66e51d7133005dabbc6ade25152400b98a547afc7bb23c905f095733484bd4c57fa2dcf5823984656bb92e3ab95489cd9ae7020e0aaab0213833eb1fcac026d2bde04bca15fe2ddd0e450020a6f4e7c515356504cf82adad16c46aa31bd4f2819b73749ec6c8c186189311fac0059878d5a883375820c044fabd2b1e8ad871927bf9009e956522b221115aee7128d3e4f271288e88851882528bc26689f4bfd973ee0ad85a415adc9f73eba60d7a32cf1177111bd8ac9a2cb12f4445f5aa6f0c131bb5259697b83ee38ec5f0587f430690d31b5577e036006882a0be1a8e2292d57a0f49a3ca8a4021ba4027d3b02d15645a8bf3d243fffd3aafa720ad4daef9edc0e21ddb0c746b687d2c27247c4b320eb311b2362c2efdd6a0a9086eeda9cbf093ac9a0586100213b39a8cd0b65c8abfd67c7c7bcc6011388f79955701dfc4431b7d1204947646795663e0ec270d2a094d2e19475d1b700b99323e1562f2c2b566f60ef85a605a99e32c26001925f3db3005801bc1ca13bfe31f67c725f9bf1eb25c3ac32660d77e1a51f2d5aea622c85def1cb7884876c33e2d1ebd98340c78b497af6b745c11741c6bf22cf1f73cf54f82985f10dc632b04b82d4d105ecf2590749c93beed9ae70473847c6c029c5969e2a2406c801091198ea62bc83fa7b3965980c1c6a0e11c76cd9922d3d9bd663012cd07847a6a89df6c71eeb3a06969eaa3844991878e208493693a522013a0db8284db2c5a3162025d6282275e4d071d33c92b4f482d0328f977c60c36090abcfa438d9556580e9e20a969688f49937ca30200487425996c1d22983e8923c8281649b92a306f7eac156a3e8db56be50d88706693103f07bbb90e84f3734fcb23406b13d4a9acc9110c2c584ba11f57e28f8d327916c537021f7430176235a7e9b6a56485c1609f0904b09744be1f67a15340a10f292f81fd156ca054f0a371cbc5d195f4121fb4aff0d867b9be71a0940b5e3c29155986869060fdaa46e163a52c8ec056d391d5624539b13d3a56beb599dd0be33e3abf164d0899cee373113b3ca03df7b7c0aef5c1976ad5175295db9d98ee1cc3343e6822d75f2d147279eeec2c9dfab351bb605d559a89e7bac37c541ce31ab1486bfdaf7607ba3cc7a74a54420319fa5e858b9c651b435dd7da2aa9ad273e6932fa9cb5c88c0335a6fdd432d45fa67bb875d152de836e3cd9fef83b0fcf5b9e3e080c25d0b6de72661d65ca3dcc8abf3597480bc84d4171f3c076dfe23abc3ae6bea51e9e2b5940a742de4b2e9122dc20df62b789a6ef5508c9353d5a82e33321c50bba337eb3a73188087e10ee0422a21c48ad2c7a623f3860131d0d82b75bb830e43bb37e9b8f33656600f510e4bf143cad6e5ed720467ec96b655358571a6bb800f6f7badc895193673270a93f00bbbcb5c5574eec031f3508bc6b7e03138ea2c061485b00f82e349c50eee18440538d0c534cc4ca6e9fc91582f22b8515d15b9994a0837c26459419411948273e4fb51a158db2ea04146a02f9c2c6ea54ae5897c0d25982f51ab6d00cb020fabe82d5b2f51ebcfbbbcf84822c21b6667d23023cad66afd36e830301dfb206b937b24823709d87af0fe19b9a2e1d34760a964bf8f30e474de9c5863b16ae96ac9f65cf589dd229d11daef39593458838e99a8c51fe6fd31b23ed088a02254ac8098bf12b3fca11d670598d854a6a5cd80d7bfccd057c4eca37df63c3cb463f56d3fb012f8be9c21097857d8fb525b7284545f5a7ad7c2119a9e34c2f008617ef482529816a16bafc1da9460cb4da84329846ba2bdf8f2c0d7ff55e78b5ba1a4f55b76b3a2feef87a9b62e1071be1e45358c3f66f9208b5ee537471fad28a9e72af61cb688ae5e86a95e7e6be2dcd7d00017c3c90527c1ded244b242ef72c173dfef41b115583369148cf9f467263408e41c8f505c2d9a0a034afabc25cecb577b4b727530f966e492106194608934af22e163a4509d2882f95db647115d1ee9d6ae62622d788861ee3389d8421afe16aba249cf323d53e26bdbfd87200e726fcdcc72848af3599c5c148f52639a3e07ca7ffb773be20225f873e5e5ab18652fbc04a8dc5ac6df0fbca395d3b89ed6c1f7ed42406d7b8d757768070aeb0708f81b6ee49006170b6893aa4a2e6838feea2ec529f319ec69068220902affc8e31179219acca0a4ce326881fd36dc71f36e34ed53915cf41e8045dba11feb100027e1b240ff972dce3bebeae0fec5ef2ff9cc686a26be631987fe4b1eb4c0bcda74dc532386b3758a9441b0e482a6b35f825f74e33c9bec95a55a0d6acfa32cff7690c1e5f566b8a21169c2dd7e59f7e1baf7c0ce5bf5204340a66402c880dc90d061c4c270088995a3017a0b17c815cfe6ff1a14039200260cdd8ff2afe40dbffbc5fe441235c826499862c074407e47b2ddf50743db9cbbf64bb6fa9f1f70eb420be43edc7d2bb884131db127ea1c510109d5253eff6ed53034733e79173ec89bf1a89a4884f4541e8a93b22c5351eb1f1ce6aacf46c8f4a22d7135e5169e035b092363e8f244fa552319b0c7493d919b8f691ec95c845030fe9de2bdb27225f293f85ee0a48294ec539648c0a907e3990f81ed1391f9d97753d7039c82901c5a8fc16d9203da4003d0f181210c0b23a2bbb71b588a804e9f3b37ec4969c7dcd7aafc4e88105ce08dfd9f0deab5cf27f9ba06685dda5144928e15c7e40b085bf72706f737db9f45b7335d325f3daea1196895a82bee3786e72d8f6ceaac63d2e7f734c352502293e897e245edcd49b6711eca07befa78cb3fb51c95b4f1bd184ed88b67d00a71a04f7f49a053402e408e6da6e0a47a39c8344d210e877fbb60ca7ae7da440c2e5c45dbf488f32a782499d42acab4f53778200952009ba20b3447793eca761277ed1b316a67d6a802a2096e0ff7d3cd9f2f07855485f4d79f42f55ab5a56547600dbf8ba237e10f003d0518e5c2ac37e08969bd5f68ba78d65c941dae5bf034c54637a9be30661204d1dfc7cc2305c6519cbd63b5ce23b97b865fb9f599f717262fe23534e17985bc19ee1e2cb9a447327a8af3a59eeab73a773e99d14445d89ebfd7d631873d38cb457e42e8581a864626c2d62028b0f8925b8124a9a36e9502228abd2255924cabb8e0b84c9d6500fef1a8009fe341560e11c6c89527f3d44aeb1a5ff3e9f53a2c67afaba56f597555146e28eca44cb6145ed1695efb7e65a4d410710b728ebb5099114a5ea2d41b5a870c745842e09ee9ba773c164a43faf39b6aafce8cb9a17ca262d60c98afbbf2c12990848a28a0f731ad86538f40468b96e33322945e33e4b14b86e7bdb902cc1eedc9f13d1d81fdaae501849a15d880bf2099f4923f768fca0bdcd498a53e49fa7fdf8eed6ab71f917af3e53d53f4c445baa1b2d2dc9d1b3e68646302b2743a619f62a4601d3ebf94830b3e8af599cef76709afdde78662d228e79005feda6d4533e5344c113db28c8b9097dc79f9dd0711209465952ba5d08167ca43a6825b19d002facd977455e918aacdba2c610554d44b11f7deef5d46405749dfa014b9991b162942b0476410c3d671fb73b597bcdd6a1d9ecf2e23e7db095a7761bab791656f24b78190f002500acf835e652ab38ee9636a6bbbb2b67970c524bebd167e3ba26bdb6aa6991c8d15e73e8ab78813fcec3d3feadbfbec6a9c76533f55a0b970147298061f896529e2bc39461af8b5035c5f55c37decd4715d6f8040ea3610cf6effbc9cb5fedc8e77f05c5c39ee0d7205d4ee382ce4154f1d6ed03eb9e0a4ba12469444ef299a066a5c33d50a750c88b4c89f16a20b179eb5d558baf1156dc875b3221d1f27d6d3efcf290b391e5763920a40afc78fc019dd0c701421479c6f9ece52288a0e64769ad6290bd25cdb1744f47beb7302b346dd71fc1732e45bd31333492ecb5619b0f0a3009409941fb6ee63280f90b26e217cd10c74e155d2a9c4d70bc6ee86e5888cb8daa1156189e3cd405198c76a8ea2c443cd056d7b25144509fa84d04ffd2664c1704708c78cb0220ed7893f49cb6e12c081646ef33caa0c42f9460837a97c3c71d783262e951c7fb9416ce875b08e0c4faac0188952dca0a87626deb84b104d3206e77cff636a980910dd7b6cd6ba3698837d6812e74a4d3333fe7c9f46f4528cbe6161cadbe90d528ebcd21f197f2bb4bf9cb380adb9ad6d030cdce4efc15413cbd4718c133118b6ab67e85ffa0ded70073cc340cbd88002cb1e37a9a9283ad829a9666ef0c08ec7644bd0b80e14b08d5e4ae40d3bc6ff43b0c425c647f3876ed15eed1cb8699c6fc40c1001fe296bd5c8390ac2556ce7da5213dabcf3be6e70ebb8ebc29b09bfef9c0e40388d397745af58f5cab4ff0b6337dacc6a9b2d7997395976349cca6c2e167a362977da96e13429595ddebb87e4ebda71a971714ef888eb75fa5c991840430438851440fb103da1236fe9f892da0be624e4be6ba09210c2afd29adc1221bc5ef78790e1f9f6cdae868792894029c5eb326f20b33f6c4ea14259e76156bdeb1238de0ff5d67d9606a14fdc73a4cf90240331b02164470f770990a2fd84f58c50bc1de0b5410ab1c16a50d4e9bf9b114dc86ee09483c27f60983cb9249e5e1821db420cfdf28622a934afd29cd768ab9b99432ce22ab5f508bed267014026009f1764f02413ac3b8c743bd4b61a6f5c0bee3386791c9eeb3a0de7554bea7ca1fe4f48623557c32adb297e8dd724d8a5f77a4d6f1d79093adefa4065702137d27fe780a44e6f788b76a84744827b516d13f3d3d4144e82ea4323c8ffd44ddcba278c4e940d5d089e1ab74cde3e090aa8168f4b171d40ba76543a70071cf27cdc2fb6a38928709ccd477f5efa7b745aa8c27862f881889f0dea65926695666e6eed4de0c032c31bcc3bc9c16556a69da796a421af91a0c89e18d7bb272d7feba646a367fd8dd26e27240a38bc2edfddc76d750b9f397304706fb54dd7753a327d56590b77e203061e412764f61f0bfa1ea056f9d8982ba8fd12962f4df05333bbcd94df9eff502b422a29eeeb7ef2657652d168860d19debdc75fb6834102eea0aad801a9a2d2242d1c84b7ead563eb271cb0cb17ff5131c117c1f779c23ebc257c7307350e30bb02c04219521e0a110b25f371bab309803dfcf651ca459ce5e0ed053631e26b3c2890857a594f6fd1813670032ce09d380d72b6a0bbcb923879f9886f360fbd1225fab8e74052974bfe5f72e32e4eee0bc1c2d128548b2f5e91698c87f082ae3d7c7ce462cdce8ede566db87bf3642efc4b3d1444bc9ab50ec9f0d8b7fe3cb04f16bce0eeb437f94fcc49279ec5d2edac2ca803eb2de7ac370982654afb83002302d6c17bdf31e3b46b9f951baa8671efab1eb5d7c4b5cbd788602444f9c8b92fed06794c301c155a2a558cfe875eb992e90091d63a253f1637ee9b01531da027eedb23ed237d9641d5fca6a827f06c30c9fa2f77880dfbd536722da87001ff8fd8198c625911af8639532acf93936d204bff3fb5d07bf1320f347a3a077ebf67efa4aa6e835fb03ada2967b33581f80a0b008f628b5fbfb3b08b18fa1c89fc4f54ca84f184130dac132f7a9243f6f68fd1b9891f4cff0c38bf7d49466d64785cb5e8271194be27a69dbbd06ca1224a8096b831257c66dddf05a9ab7618f993e4d7112f98d578d5e28e7f4d7c00438499c9c41dff52ef98f4c2439cb183532b762fcd3d930ab44e8c03e42868456b7bf6d27264f62f1c9ecd28821dfc8baf3803ef1c30748c1964a6cbaa5b20211052018500b0af182f3ebe2ff9e7ca3d0623ebd287bd07d8bc86ef4ec4d5a18c882e8875a3489e50b244f29829e926a2d628529e19e5fa212145e73d302c1664d180a4c5222ddf7e2ab6acd6f455056ccf0c6b62f106449c52cd099ae1ba4f889320e23380c637d9a1b955e1300d598d00db29f7531736c29c338314fcb1eb4f5651085c93b94c212e2addbc071b6386fd362008bae80e27fb32f22c9055d4472ab30d2f0c98f683420c5c1d3bd2b184557afe27b85ef0c4daf65c50b1efb7141b69bc6110c062893ceb7a512e10e3aade9752a4b5285577c1d70f7a1983ba5f31ea3b2e38ef47c0c3be1bfeec445a8c16b1a88ea66f7ca4ed98710b3783850e8541e1dbae144273e210c2da46201684a97288db1320c757c72d6d13d1d811c08f40bb5f12174cda92b9235c877c37b82c50f2ea75dba91a1c305f5f32b3b379c0a831c23d5d0cc0789b5674c2dae67130a0e554cf7c9c54c66688df795307c641d0cda102ea0b33a22d32301773c8a9d909852f563355cf27384e588e253413a102ee405418f681ac4ca363e38cb151f7380c2366fdb6b19435f597a05034be190011139c83aea17264b1a41f70063c6a04578194750ddc3421f7eb1548262ca314d936f1b90f8d5592d2e026d190d676cf50371be4c072c29bf6f24a5a106e881f6043623c3e8327f8e2c5d1d1071fb72e0c39ad3ee39714cd0dfac749e11d17f2749240b1d3ec8fbe279c85272d8f2241e34d753b0dd0a0d363d0b5616d0283ea49ea7f2ca8b62400b2f1bbe6567bd86dcc9c4b17c21241440c68a3caa501c724aba2be35f5939a7ab66eb7234a0449e6802d6e7993d1d7b306182834a8592a3c401c84c6a417c3483f9d74ca465b3fd79c14ebb0b9d78d3f4b80a9325676c6389e879d7e4ec789b99d4d7dc113c9168392af8653b197e0449f09b888e576252573f290d7653174c18ec16fe05a0b4832575b7af6d688b732d84cb74f19583705a8721991eccfe636be928f4dabfdb6ade4578cdbb44f5bdb37b1c493d2629c6678d85c0e7ea7be1dec89d43ba622bbfb12e613711e990d7928f2586175f8d9ad81032cc3d72e1b9b3a97b6328b4500cfbeb105a2a1f13610da26de3bb1c52481f8a85666ffff0efc7a2a40ecdd75ecba1bf409d7323c9ca9e4a5cf565f644166aa224fa7b79032842e4928c840710abbca10f04b21723c6f4f84583b55651f73ad29af38f252bc2bc148426d6988d05e4b8526d196e9db1b9d0ebcfcf175df45d35c6c0fbdd8c58c028faeeef6fd6921bba1b49705a7a2a3e86177dcfaa963f8750cc05798317e21915a9171814706f71b88d8f79c06be8a2a318eb9ac2520f8ba084b90b8646a81f972d0f9f343b7d9c5a1fcd1ecd0059b8abef65f7931b9101400386d970150382002621f1300b88061678613ba7b6ada24781ceb6fd1c52373adc14d184b8243b4568e50c1decb24c74adc328ee4e0bf7dbcad5bca7cca540d942248f19b07e0f19abbdd1ed7f265e62e6e370b1237df4d6dbf9fc08fd1e80ee232b9913857014e0af865b144c971e976c3935a2ab54a27431023e140bbc83f8b7af9c2c793c9c8a49d9a69b4d03e720773a982f3d5932c1535fafa08ffc3c8b600e13c7fd746c52af1c201bd4fc76843fca4e14a4fe632945c34c8ea93586e2c94401a36835ccf9228c6966dc10d7eba05a1fca8dc3c76e879ce872592dc1837427a712a897705d3fcd7712281b3de6474e0901b11eed200993179105274e16dd26e47408df914378966090540cdaa55011b1b325ecdad300ffdccc10133404cb6e6fa4ba775f637e770407bc605220466f29357004931bde4dbba14ccc19344da0ef9a6e41604d2ca250e7445ee9ab723ba9715554e814aa9bae8c7a8448eb971830c780638321358f44aaa47e43d13989c2703434380f65f195e33fd3971e60f75ebd3ee19eca82d964d2c314c3977ffff2f791008744da08cfac2404e189a9fdb9269f7e5ca3ffe09cf7cd6271924b5abe546e4b0e47a0c5b5783b2e022e742cb0c2ff15b0d7474659f65b83183e90e8b50e47d5a1051870ca683ac8180a69ded554e82640546c8c0d277ce3dbe3f9ec75d6fa9c81a7a351d4ad01729cba9188065ee802b434bd22620af82932cb9ce691f9e516a7469d5afb8aff9b18492c5307c366c227be8805ea79dfd6ed2bb36470393d7aa4d575e42a6ae51b335516c4fe49c9199b0cf5806b9bcf0a395260fe6058f174b4f5030b4a163ad93d37db4f66f025dc1ce87d4580623cb2d38cc44ecf51c52951e61a3f363806e9f64ae7530475e0682f0de2668df18d67b99b37b60f9189a2a8c32412ff818d579ff39d2a96226d27b7fb2f278502c6e37f5c953d25a8ca0152569f9d3980c9d2ed9cac4a45bb5d3ae7a09137c935cd019ade00ae40068b9e03dcc948c02d9dbb02ae4bc37635cfe2437bfcfca3ef19c2ce402f7c4bc0553121c10d3a64e1c931cce76c749590b6650e6f4f706271a336cac87b96ebf08956ff8c98a40d32dbf180eedd41d0ec2ab91acc2bf3a6f6f761ce8f3fa4f219a198331f37cb537c070c91039b90cf31dd5f1b6d6d726ea87745ee650396f76883391d9e137ee4c89a501f1fd6d169b66d9a4814ed181ca9aa0ce67d5faa749bcef29b26b8f615327813645d22b8391113d34d7e02246afea88d7f17662855fbdca68d15d747761792437cac5aad9b02681d996541f317320595a956fdaa94150dbf71a24e88b7d2a2d032fd85f48f62c60a9af59689cb7acbaea4ff51ea32d1330aaec27b832fa668a57314bfbfcc257f54d36c5feb55d0f516799c6b0629d519198e67e83b4546ccbb9409516d54d5d9cda8900a621862372a9bef74ff711b5c58728b472a0dba9876a8cc1360b485a0801fc2b6935b3f9574c47131124fc74a9c403676306506019b3b662db263fab7236291a74ea5ecae1fd74443544e01c3521bef682baf139dfeae781ca00fdc0a945b29109209ed8a2eb4c5a014dcc457f4144bb3eafd71c634b0b5bb985649871ab937df4a172ca8bfc4b6cc2306c96f9241273218cdb1d16266b124174cd7f455de18624ae6ecac7b731d3b099cbac5241ab9ab1caa528151679c5a65f921089ead80e797e2a93eda25875968bcbe3e61c95a2c62ab6b6ca01e026d8ded065411f7b18dee1a3ae23f414621e34dcec2464d239747ecdb6777e7c702e2d33609f29033668c11b290d9f1cf93dea21508304d437e2590428b9dafd2a5c823c4de1512113426268d4da7d7a10bdeb4bd78f442dd8801902d74e56da915464c9578a41bf2e2e84c9329cdc3d8e03e018f22d160a5fa71d4d12b8b20238d8cd8509324e0bf996b1cc1866eed5b82f1c1debade42a4137ffd240410dedf0c15b2327cd39b7780f345f27656ed99967bcd2512e6acae9bd88678d87bfdd821240fdec5ae8678ec86cfb1bfae72947fae7f9b9829a2e2de6e183c1fe253acdffec941f3a1507beab1426789e7d11f7ec08f0d2bf33fbeb1acd461d14901eebc06c161fbfb90c282d923e85c89d9a05d5b5e3802472d5d3239444c378d0ec22f49257906b5cacef6a5c0b3ef0890e0727986da17d0b21b955dc07c92367c30b62da2b345840fe3d9a63a1efc3e861d1a8483ba6ae2152f6fcf313d6310f7f8cc33e27bc62408abd3cdd7c4efa03f0e4c24f7c38e9a2478ec83e8a2e6d05c0b934ab160f6c45d03c59deac03f94d99666311ffc848fd0f9cabdb2c31c86997f7fa6abf7b8a3fce27aa72bf9a57e95988fbb4d19d0154baa372d54f6565ba5375fd001845cfedc5fee5bf68dd4ca9bcd6f2fc20e53a242b2437834e9ac64ed72c40dca23e62ad36a02f10b64497e3d9c5596d1bd4188d365a75e26e88bf07ddb7496e759a99b3c01d1aaedbbc4d0787e2aae2c0e6356e6616796f9b63319cc5c29d66242f9a4ff4a75861be0878e84e6f2303d8742ddf15be0828f4e03aa0a838c5c621c6efb1b88514b8b92c017e2dd6506648af637ebc2c3c23cff827bf1a4ef8fb02cd0852f3a12311134add6a01750c27592e6183c00826251bf7effaa800a1cdd54838f8ca0039fe09dd108fc9ef60f83f2d5caba096c425371e5765b0d0495b872f5ae0ba4edbfbe4bba3ae8b6fffb1dbcf222e0b17b06941ce99657c72c0dcc75fcfb459ec391211a9002e36fed0807ba160a9595d98c940432f50de4da88ad4abaf6af3bf2bbf530e761a5b52e618db167a0f06a16e76cf07a06b349e35391e9f8a179fe31e8b97b72b58b1cfa6dc7558440ea558b2f070e1a7928a4d852e389b9f2522c7be971393c2e4a8440a5d0c793f84ec048b496c23ac5527480208616626292058196f5cadc124491319965caef163012fdd9e4315dde12a0bfa7fc9969005bb635520efd5bb469e54d92d60b3f7eac6c98bcc98c9e7d47a9f69dcca8e2cd5bf067c4ed8e53b65ed813d13718c16100c6459c26eb4c9ef58a44729c27645a9e24417a162f69efec1b2dc85363fae22803c72bebeaaeff4f8fc8c2d2667c16108cd886ffd89d23d9f0b37ad9bd16eb63867a76f5f374ab37743abf043dd594341637029e59df7d452344df405cc66d64b0b8f70e7d956da00c73952419171d0ac2aa94c986cdb102fa910a9aa7a0a9ab8266f07b0cf0737879dea738e1a74caaf3c0e5a0020a3b6318924f4e0929846cd9e93a01a933bef3bfefa196e3db2bb2a14a8e5f5d2b672680e4d561637b00d6b3e154c3675df146fbb2238f05744bf11a3fd6a8ee134810538be47d1a6b2adc598c4e7b42cf5a723ba92c0522dac961a5e1065972c967bc4c338538297e648b468584f023220c5ae89b819432588b07ac6fb925397c8bc29a627ca3618363e63c09aed2de3b78c9739eeb10b4e1b65c8ab3b4822beee0831318742afc358254cab720f6db7301644a68951ed39fde288b4e333a7c5533c6d31b8496d4bc06bef6c823560f8436d88fe1a404d719939c30d8cf3f3d82bacad5cfe05e5777cf07b6e1655607e01d747693ea2e0b6fa8e954636c976d5a86d0d8199fc48ad6cbbb92b22ff4b0d7f9a4eac471d5500cedace429c7c8f445601eb6f3803c97de6bef0e005e78793a0a2f5b1bcf347cac45a7ff0f582c75a5ebb5bedf53bb7aa8f0b99f9788ce011192b66ad55fc7be25820d6e4201f306005bd15c1abad68abf5faba4d030ed77faea8946be0f9d2663537f634e224519d543038d8c4e1a19f5279ac5a855aa7900452726107567d37155810954cf89da0f15070517e4d5654b3121d73795383d5054d7111b0bf7b7c3c3657821924011c391225bb39e59f5399161fbbfa68abf1bb5e1a0f605fa759171ebecbfa9be20addf42be75c1ee4ebfc5091da8fcc5c3a9acad05538cd13dfb380b521b869a215b47d269cb70c88870d02e460ec43b84cce70d5da74157c892a26baa0cc93a7a16bc4369f915c9e5bf5d6cf5d5c8bca9b41e3235853428547d4431331f4e4726e88efd8aeec2cf1d29a01b2a4132ca9756c858c84ef7bbfb3b382a5f96b1b5666ba0d04b54ae1b643644feeb883c6d2cce846c7e5966f52e39821b4858e368185256bc9fa864a4b03bf69edfe671008487010343996a6172ab7452eb0fd0f7cd543b0ab90ffec8b3126702759192608a6cfde1b629a41805ef0324f59ff38197d2f062f3aca7a433debb979d3fc314d8870aa16b57cf0676b32b5842502cf7c85d76611a32feb0ada1f4bbdb8392c78865d00c9dae9dcd2840468b2a450059a18411982292ac907a5eeb7a2116b05cb0ac18c5cfac2b94f503b2e482dc3bb502fb4272260e2d6e56c6040f747efe7aa01b87c69784948d64edd178617e50d9383725c3c14b5ea01f7265e2e2c8c49bb4dac227ed2a13e3fe0402b511e5b0fd6e29283073f264179939f0dd0ae1fb0caa3c732b80e574fd29e4c0afb87902a959ef45a5bccde426698263a6414252595dfc42585cb484673515e0301b5669c1f5b2744a1808a26209463bd4a0f523f37c29dadc121e4c610f88d47df364332ef386a261da7ceed7e06371423b25a17ea97f3d181162edbdcc66524167f05d7f7eb802f56d64852e773e5ed39bff1b684e18f3763b076b8c51503890f2a535695921075032e87a02c6ca42d6b1784d8a3e11ad8373f3bf01dae7b6892874c757cd2ac5932e8578946032fe0215a4b0fc54b5bba54ee33038b9d26901771812088cc44c168103b69164dcdd3c112726259192846b07867ccb87a432f873c63b82b725071b5eaa1228c9e9652dd5a63eb8beab10f5b1f53d6ed52d89de3b7901fbf5077f992a0adbc17bf149a70ea4879c87e592293b46b02bf0724db3deea840938d62eef3dc696390688c114a83fa48188446fa7800e480560d1347fee5d654bd0f9265028fc8c4a8d3300f73ede70ef57b5f72b481479be1848f3808e50f3f68087c8cadf4660f1cb3326a23c1543e7f466b4fe4ef467667671b10567cf52d7c760ca904dc629eac28a3a4c8ce84e4a0e010696eeb5c1522e6413fabfca2634916ad2298c0146ece20e896a4cf6af137fd595bf8c40d9738c17cf05c85d41c6401930a895bdb7b1bf52327a4372be7274fb217916ed76b6ecd4e3e6acc9e1763de4a86663d09e428614aae7383fdef23e9e26cd22f05385e2546e5e4b57cb3149f46fda81e73c17017e297ddf96c6ec447402ab1e109bbe5f73fc4b88e11cb81c7a9f68f2267ad9417dc1d9be687d11268b91d939cceb2cc55266336ab17fd905664f90ee5057d437602035c721fbdab6ebc094384903ee4aa959ecd80b6525504d9420d8883f199f9c9b62bdba451219817634774ca2c284a1e29884cb5d8a237c8a680c8c6795ae7a9f10cb10281e68e54ed9892ecffd0788714ea12a5d2f546bb8fe3fe8a83a80b5a345c5a3ab6317820527f94750c94baa135ec458ab58c242a71b31a24eb13e5ab0ae2094d117204127cb8f8945bfb498d6a234b2c6344298fd3c72db72559158b1b6d963b248539ec0bf0bcbc3ed296239c1651335e70b33357a43e28c1bc54580e3c3f8a746c229c2c9a00b8ac574f52b6aef015d7eb8154b1daade382d3a8de8c4e8aa5d14224177122e80332d24a4c81f4bc382e10ff697a46a44d99b5b64360939ddbf2ca120c9b782fe030a24e54cc7335ef069e61b558b79e02f0891b72932d60dee2a171a08d1a2e45c1435c5a8ebf2121fea39265c0717e460d049e28c9aa992524c002242340d56fad69428afe3dfda5943bcb870225ea958600fddd3125f0faad281f81e0024169b53f6bfb955ab203fc14993cf274ea0a97afb5f4f7f6f5d4f5c7e8fd1dca09b81112d334625770e42a82c98a3e6bfea9624a14d740cf0797102b4c8800a94867b5211918fc54c3da4052049ba62d5fcfa484ca35e1723203073d4ecfc375e77a5eb42059e84f3b1235532888f83cb32c2102f8096e958c90b7495032fa349700ab65d1f6aa37701c5cb71a909bf9425f9a5c65d71b0ef82ac4c94dd42af1f7bac407ee4c98ee527838185e817dcb9485a6fa6faf5f1d60ff37998dda26ef6733b0129efee9ba0750067d3193ea38bf2168e437e7b0445fb6785ed3686196002273948e9ff380d806fc09f8047ae011a3d0a05b6019ce9617f28f2913de3956d5fb7f5823b463e9e6172323b38dcfaf7ba2816296adbba8fbca152b19ef5f9d354c24bc5a85a6813a371890619a224d65dfd849965d2a4056ba028644a021cd043502db2cb01876c1cdfd1873a110b2c51c03ec6470160cc390225f545e63252ad2393623646520593dcd54c0d4264430528e0052599e79a25d4b3a57f7c365992f7b55ac6cf65e94ff90d47caf8f6889db5ee643d466b6deef53f2db025151c9e11709cb55d847d762d1b38a573851ae2b799f72d99b9607724dfa74ec817b8324fd0da436c397758a27aa855304716dd524397d0047297e47118b86e371242a5200291b08f931b34d75587edee7b6c5589b1867e130eb11b1b5f9ed6b73691fd4a74c823a262dfc23d75087f65f1a9470409b742ddf52019617d19488de98a5c4e40e4e3a1b50f33a73f5eeb473e8e1c61c04deb7f1801e012ec1781ec2f475f49ca830956adfb2de0bed1d309a3ab1acdbbab0adf5baf5037390894dea3f9f115768da7ff461906f342e912c90843e8ddc64f9a560c06cc8a5cef53c96832bab865de6d88092f2b676d0f14979b586882b12a0837fbea286b507394ab28106340fc23ac4109dec96402954e708f64d015cc5af19cf334857e62a459e1e96a99aa3cff924efc56674fccc3fd7accbee5fd4ea7421b6b8f646b1824823131a9f9caffd39a97d4062828ee5cb20d042844310c4434664cbae92d9385f548d8277132ebc1d00d20c65b30e1bd4b47734a24d8ea2057298db0d65cb36153d7ccfd713b75e73f986097932426731a3bc89b24a8b1bcaff214e2f202e431f243cdab43a07eb69163ca3d8bfe77b583cb931034b456adabb7c48f21bf6875de86a250aa6bc6619c56b3887ce24dee72a5e4615174bd271fa1f8fa3f46d02f8e4479227712a48d16b3c9607345f1613f74bf8d68272c2d432bc554401755e36d354e5caa4de4baced335bee1e48523a2dd904d4327b69a00f8829379f741c3628d855c5c26880a7c9ac1625830cef88506dbf9f9ad696539740503e66868ad783326bdd4b00364639e160d2eb996af184d565c2f54bd767db5ef0b8ab229fea668f9eb816f1d016e5b4086ebc8e4617a49ed0a5eed57906394eef4657d47f78546f4f909187c2d09b6bd15ce89ef05c83f00bd4dd3e8743fecf1f04383809153bb9133c06db4d593fe43b143ffeb3d2941da839e7cfd8851ad7b929c2bda63d3807e6d40077b9fd914adce4a0e3537e7eb37287b32d20f4141f2f83cac54d3fe37a06f9b7d6601c79cdfe9f53d6dbd234b7f5a1f3f4dd50773acf7d4da674c195fab9b441db9fb99d8715c3481268dfb84128e0c5fde2bc51cf3cef82a0c45a95c72f848be81691ec7d028fe432acef4d4ea773ebf67c2553b83d787501da04c21b4222e9c4588e3ffbf572dfc7b178af2508fba62d32642a2468328058e55cd64106a6083da52cee1d56905d1f236c0ea1b040474b1391ea2ecc8d8f2fd813505e7e5a6d27b425f4c4cae0d5eeb073172a24bf10fa6e0b3fb3e19370bed5a4c4c3c75ff0cceb158795186cc52ca24b6c8dbe18687a043eef33bdb85dc8f1cc526b1e712da88e074a02dad9b871632200e8eb5477d851ff657ac6949cfd26fcc7737e739f1be159f81772a1ee4e86e749a72d772ebac49c1919362dbbb860ecdf5ffd57dc62f9e22cab3a26f197f60f4631c8f51168a717b6c243c637af2eefe56975b1cb4027587e9eca645e415c773e5fac0f05685e009b1f4fe88dcd8a5a7f846ff7484b2fef85e022f3cb8e630606e238d4947760f1e78cc9747d081144d0e9ec89f62843b1874ab6e8031f08f379d5b96db45373c0e920da382fdf06d1aee668336e17534104736584428b97532b16adfd63ecb34b2e8d55ba39a6438a0cf26a7e6f84f341b98dba88670ed1c1f8b1abc8b611150429c385300cdf6e5441c3684dc6697855aa1520a519cac596a198bb9b9229a3cbc5945c88f587f381b0ef93702e4d4c6421398ac655a703461527358f1714e2fff1410929683d89391bb12c664a310f20ac2d336935b5e9821f1a9c3d02a2b3dfed295207a78da4c7e4801806d2feefc2e468e58615c6accc039bf199f5bddafb48f36d85a1f191911681a67e935267dde6b440c431c15568aad2e42a2fe44ca1acbb3c39618e286824596ae1f48c99471472663df7b3ed4e2376adb46a510f1b134090d05b9c4bec0f9a2f2cfd0b75a8c014d1ccdc30970a03b0290a3527ca6e682df96c5c194dcc8a40024858ec67389be72fa58128d413493cf9823c18f8b6b943579da2dc30e0c82ba5ad830403a05720ca98afc9718caa0d737052af561488a85ac590f88c5890d15de7daa45f88b744ba4ccb540cd699c799ff3f7d18cc625bc699d76600d3cc1f69f73e6f7624434ef07246c42d6e93367f60fd0c87acc0fb62020ed31fab165854eb1df1261a6a91b9f68f4b358417401d390a0d4ca4e147427ad9d49f4d2486256ba45bb2ea9d2999951096c425202a49c64ad6c6aa5d3f0a5d1d3404ae942d8892bdb3628d56385fcbcb18e457d94ffafd26cfb0f3161a0341a46398d5e77e06d5acd30a44c74a431f822cf304cf2f9e2e51ce6b38292fa96fc82abfd223779772d9d9d13d3ccd7a883342d038fe3fcc7326d67894143b9a06d0760e9ede01eae9afa71a863b742229a01750cdb602108775009a0d29e31af6e337b7e06b983fec99caf0ef258e5246695c4a2acc98f0cea2cc4c9b6a701dbad47075cd8672d98081edb24be31123edf4b5ab40b9814c2351aef153bb49b742e7a764cc0235c545a1e0e40477bad985aee064e10a5b60a8c02c64972df61a1343826e4034ae24e810a8a944c6941fc5f98ca2b819c191aacc13dfecb8ebe3e9f096972191d3ce4f5acb422386e7839fbef79ad23642be4eb28dd15754171391d83e3c2ff15247de8418b17c910bedd399c5c0adf99261385d78818ac721ced57543791fcd466b2b99443d84f76cdbd8bcfc45ec1f82440aa4739eacd847a4f8faf4e3eed530dc1d86797fd31acc0eff874b76d5ac2aea0ce51b06c021881e36a9ac388dd76ff7132976fba26e57044817a2b42a0f917e5398dc0fb1c695ed729cd2b4b476401c83b2bf0b3feb774a01928cf74339f7c9ae65c08bfb679978b4f9b9c0be5d734c765f16992ebb2bcdae6fbae501c83a201e083d6fcd6400ffa180041fa216a6b34b7ac6e48fd93d77848caa88217e14d93bccbf0d736f7223c69927319fedae65f84276d722e845fdbdc8be1a749dee5f06b9b7b21fcb616855e323b4829c4f900af399e0c26a4ecddf14c7bb8150f49fcb6bfd7a91e8f3d51406e6e250e2834e5760b6267fb7d5175e988bedee3833c1a2083271337a1dd263b403aace7f2705b0b583ec00959c5b5e9b30170c7e64de8d3c01b5017fbd83e0cc61ca2e18687dbc96219d2fe3f451ca290f6631d7438caa5b407c244a256e0592173d99e840877218bb1b7534904b041df26e7a9095b970cf5b5380998c451eba1da8dcedd4f63f24e0fedd884a5357351009789200cdb52132b1118419424d8a4aa1657ac8b18b4650af6907fa6084ca92ddfe7e15a0074dc9d5d5b3272c14b8f42748c7704cd07fbc142438aac554c311764f1e4bce270892e31a9206a60318c358d2d28b800ad1418b8f9835df8a2e6cb181568e5e2227c08e42221b5f0da11cd771a47a79bf1e3fa0af163a2541f73cf1dc42a2d69db33b2657f37c0abfa5deed7c3339ea2aa6478a41340aa839c668ec0ff9385a33e97705b077dd847a4c681175531433ad6d2a8ce2d210514bb31e2fa510098611c2a25a733d2222a388833d8179ca81cadaf03958918c36471f7c9ab51de33c7fa9f89bac4deb64c0b5aa261b8623d5c9296fe1b517466aef4b8b600ea0d5df1b7d0648b89aeceeaedf015b679b0543d20288f794261d48c4801b7d2aef6ca49e654d46d65020205544e193b82d401c52fd4d74e974ba51aca57c6bee1d634a4f6f98971f55d39a2f147a3a1ae982aa931316ab40793f7f06fa49466738fff0ead92764fe9af5cb0c7df0a058a760a4fa364da08afd09326c31bb66f1a647fc497284eb5f9635e5fa1ecb3162e1f5d938623baffa77d709200204b2d1f09ab92a229324287cf541cfc7629989df02879574c002e6448caae00716803a765d564c4489c8968093962087bbce5d2effbcdb46ca81c0b29dab2e149dbb9b2aae4526bb0d8d08015ec85a70df005f88e6dfac1718190b2a7982649f4d290f1fe6e3f24aa1f982991499a61372538c62b354afa181e2401c3309ee4c74a7418b06a0f9cab433af2d78585b2d77a670c1359b83959de82c054ab8b7bcda019a49788667256b24ebe139b5031b3181ad996b4175b9ba9b173654025a117ebbfa10dae93bbef49b7678492c7307237c644f97638c3fb2c7cce41c5f11cc806c1bdfe033431226a2b02ec0f014aaccc2658808c65753ee887794ca694e8423f9cfbd4c4ece32e98109d849767c208f57f7a20b2a36a27aaabb7ddde7efbbf5bca2de516bbdd8cb2a6522314aaa3e295ceb100fca62b420ec5ff127c3b8a0c0f72a1bff79649eeedbf0351025b025a026f6268e5ffff9fd781e8ca396eadfb7bef4d14aab6945834be0622f2df6ddbb6d53d6d5176461884a907199617f6c3fcf803dfe083fb8f6fae32e7d9ecb4a21f56a8a6011b36ae54a4483ae3134edc6afec0b6b38b4ec20b50cb55d4193c61e2b922808cb297cbc11471385c3085294995584498120cd4562b51d4c4e25eebb4de75705d01c3fb69e50e442f0cb9df9e4935d132a6c90e273e58453eba7afae8db4b18c41d9f7119e7575ac877a9b5843becca06eb040703882bd1f31423a2c4b4b35fffff7fcb8e6a4b838e1a5a8edcff7f991412e66a4bdbb6d73aad772dc76ef1ff0753e27153c4d6f0fa8f10aeb6746f99cfb88cf393deae4247d5961e09317121a759e7320fa2a044386760a1e8888478bb98ff7f1bdc6a4bdb9b931b971658064b0f04efb34940c3071c9878c2460a1fc6148814b034a958b105335d7591e05992a8c0841cd630ab08a786cb9009e0566dfdc1546d6993eae99afc80baee2fa938731c5e103c59d213970719e1cbd26b9dd6fb8ff6bb6aa1a2d4b259fa709e67ee30b29e999e9b9e1d18b11753b688ae73907716bee530eabd68cac06112eab96e07284a515e721017940e5a62e318600070062526af99dbafffffffffffdfa486e7ba2f67e011cdf5b48551cfbf9deb3e957be1e7466ff8d9e19dd66b9dd63b8d98dca6bdd6697defd86b55fede9459ccb5700103059100ca6151435bc128bc69adb5a681546da992d9a432a1f1dc3709801cd11419a97cc410ab8eedb2ea88f7beadcc8a9b48acd73aadf79d372722fc8f5ba068b760e432cd675cc679ac87ab7c03a087d7691fb772fa729c0601084b52e59197306e83395a6bfd83516da99058346af821f78dcb6262219625bb45e1d034d3b0936a4b9d26b0d58ccbe27516ee0d423899558e730acc8ce45c9725967363bb5eebb4de87acebea1c52d5b92131df2c0c8b43e3103954b63a5e78bbbdb78f02e514d15496964d09adb474a4d911b31122c0a30a6aa5a39903e5548a18c6d81c60d4c2c0f3feff350002755d9479ff60e8a68eddbd2c807205a18e63f31cb45980e38172ea80e2702369dc61f0b0fcea529ac12569f5b071956630fda40618316505e3d4a1e10cf18bc90de335286629bf303969a4a59fa0560dbb65fcb5b296f78efb4d8750913ab12179681e9ad18dd4ec5e0f16f8ff1f958472b4f1f5933ad2cd2870486184ce0d0f0b1c9d19379528ecb48fbb5419c8ca393ee332cef76e010d76307dad7327d422de17e587cc2302dffc684664176ed148cc69bf7e4db5dad236e40554c71607282b142b4d2f58f2c27b5261c0e0bcaf91e40e93cb663ee332cef70ec26b715789dcf052b644dc2aa3e8fd7befbd37ce8ab473dd991e0be2098311f2977967595ee54baed73aadf7949c0d01cfff9f6abcb1501c0e8703538d25e9d208d869f3abd26b9dd67b8d950dd58a0e6937e4a255fc18418a6ddc5c2fa4a6f96b041a4b44950b6417120a9ed110d251cb4bef3d01a843f3c73414b6a4581a71926355b2c1a56778efbd7f38af0cc115756f7c7cc6659cb32c8ca4faff0f3210343397e0e2645e35873a2d645614574b4e6f5f8ffce81ffda37ffa6e2375f95c7867657a6a7213e5b2650e2e24e6332ee31cba6514b5d73e2999709284fce1a69a343ddb17215250848f19a2108589625e3e4098402c7e9a39421f36da6e59cb1f37bdba2097693ee3329ef2bdf78e4ff413ae092b2149e3622328086a14032a0bb04107af8210b64a00b942c842404f3d27c3744974a4aae1332ee39c050099f8847f280fe7f602219aa4891d3d3b4b1a06f149b224aea0f27efcbe5daafcbeac1c350231874ffc349491733a20083cec114154bd40f142e72bc7c615aeb153ec8cad621c5cec6d356a715f9594a815e2ed9ea61b904c71c3df1cacc94874478eb66e86b8c4f65cb709a861565bdaca328b02de7befdf7efdffff41764afe4150c10bc22b925163a85de8707aa94ba9898924415560ea9184d4cce05b0c875a846c5286e804d525732f452715882a96154bf9cc011085322fcb12ec9764787c3a179cbc9e1636408bffffbfff7f15e06a4b5b20b90f5690a0534f9ca593098720ff6339834df3d9beae0865772a4f5165ffff9137aa2ddd713520835cf7b142923940dc0631a3d39ff9c9be8c2b452a1c0ee782a158922a9dcca20757a78bc57bd6f33beb60aceac6c0d691d6608b897b50e00b8785881d2eae5e340b2517c5850dcb6a82a30964249dc155e07569467eccb573f658128452cf6a524ee96321b7c205ab434ce67d6560ad55d47a11d3e22135215844763ed72d89a67084b5e3464a57d30fc0fa1076a9b2ccfecbeb9b4bbf5e8c50d95281830905cf75776b34b9f5de7bc76ef1ff3f7e934a45aa2d35c4a1ff0627ba65e11b002cc41694f725f1efcbee6eba75f71a43a8da5262d1883489a1b48a12b66ddbb6ad08461c1bad1db20f9d5ef25d585ae510beebb3867059c307d62aecd7ffffff52b26a4be77686dfcc4ca4767a6d1606f386aeb6b4dda95573d54cd54dd54e55d0ff3fbc329766184c4275cb90f47c42b920953f3d29563415bc8135edabbbfe5a3511d52770fa044f9f00ea1330b51d9f7119e7efd3d97394c9c2a9c9332bd60bb1da5d500699d1bbbdf70e2a1015ea20236222a088242cbf9a101b24fde606613578b158a761474c9ac731b9d5f10a6cacf9ff1f6cb53ef28ec023cc5178247e4d456baddf57abb429ca8e47012f214a2947ebcb35f331fbfbf5a74e3a27d9fd7fde6e65f9e5782bfd786b0b6dfeffd61c7c8b423752d4474f0652d44d4a4b8a8ace1488109f184827514957caa99c9e3143078debea5cfe432f40b964ea581c5469c034531c5451f6878aef30c378ab2d6dd3a76156530b0e0b292e2d234b242ac34e589816942c337c24391706d86b9dd6bbafcf3a0c9ff83d3d071f789c163a58125cbc9073c1a66749509cfe0dffff8338555b6a8583cb2488976ad512198990444a2226519376d4d2b66ddbb63e239296e6d76f5ab8795c3c2f58a759f62be8ebf875f4c5f4e5844107d8371597c532ab8c225234c3d46e12e7c41c617657f7be28ef7efb6de3d016582006385da240275891a59c7064ac8e1f9c3c3e3271972ab3f2fbee1db33a56703a51e6d5512ac913fec7b7904ecbed7aa8306d8a52b5a5cc26150729cfdaabac41d6e8babaa7e26b6097ee16d78db7036956cfcb0a072e65b127cac87523dda004f2ff7f8524d76fba86e3ec5c77fb1eb8f7de39af92bd5b3dcf3f075fbc0ac0f4ff1f9e2919455f7aadd33abca2b957a8ae070b9bda990a9a3a4e1d4d314d3941591d459458f88ccb388759c050d0e9028cf2a8640d1fc7840018131a380411c37024ce03c59d00140005096c7c8468880c1d30201285432231200c120980a0301800048202819010000a8d87624a567700de405ed00989eb67c6d2ea2842ab21b96e279de6e208fbd681e1491b1283f0a0e78ab5d7a4c142d37e4db17b50a9c8cd5d317fe94d8173835d8cac71ebd41b70e04658a392565e7a102cc4446ea152db9990e95825fe96b29a6ad34bde4e88d8fa6d943759375627ab07da67dbf585ce37a1d7edb396aef40a9d2090df600f722dbd2b9d3c6106d3bd020956a7469e2284d7994a5e6f7754e97c825405a236fa176542470057311b4554a3c8bb54b502eaa7ec14e64fa651302abfa8d5f9c9806252e6bd7034ed440b78c6fa816ea728c64c6858c95b266973402da9790df6960a440a1267d30e0f0d69f3cb0eda3832ec8129586818e6fe9dbafb1028382f46b37867ad8439e1fc911f546fa111ad4ab2be679c2426bcf77ff43f2fd0ac0242659361b53584f84720796d46b3e78ad8391fca101eec8823f983651ec9cd39c3b5b046b31e1a53afe985ad46646521545f49af9e196a65bc02007b0eadf8942b6107e5184d64efee89f3316a3505d7c7a679bdc0ec180d9d5a239aa9feaa5c2c946c5f4095d797cef48d412942655cbc2fc0228c4e3e35ae699875e37dee2fb9149dc007c2eea6db15e2603f5de371622ab9c86a1dc24f4879f09e3da16eed8c73aea34d7e6e23e672d7ed34cd1b8d493412a9a2f5e35d8891372c8ef3c14bf81a19513daaf3b73e18af59268c9da82bdf4e57894c28805947ceb54bccc61ad638e1a51b1898db1886e603d95b9061e2fce17050dd53ad6b07d482e3f06f487ee343c3ce2792ade3f5420cf72e5e32c2812aaec9fad357021667fc2ba3f19abcff02b52dc6eff12a83594e9b225b42796f0d644c8c6164953b5bee5e68c6fdd635cb5ff862f088729455d66410db812eaf0c994a5d6e999369835e57570c849c291179b9169c1a011830e0a45e5bf4664ba8524706097ad018633550bb77c030f3f17db693af4ea69af7d2831c90b9fc3c10188f183f7e53eea659d84d2bd2583bca602d5c8a5e43605f4014ec0934bacaa3746746fe38ac3ea214719d123f5f01e73a1d95780a1405409fbca8fc57b7adb082f0e4cd89d0c165e2d2df49b8f1586a79a23ca051695fc4db68b4746aaaacbdc0d94aabcf512d2e677b552583290e05c0c31cfde3a0506164ba69e70da224966d66892a5aac313bbdd880c46773dbbf128a88d8d69cea105ec38ee965779d16cd0f14fcf6ce0a3e38d4f2934a95e86bb04b874a510cf4bd43ee227f1c4f42b17d2312ee0d15369c4f1ba9852ca6e1b2e1b77e39c0d70dce3d78daddd218d0217c302dfc7da58b159464dddc9065519dfcf15530205202b649201dc270d7119189c3f84dfe6199b8fea0b92b480887dbcd063f90a71ec460f84e173cc83f78283ba7c1a073ac7528f1363e822a2d508c07ca7be4971be715acc5517aa3408c72e3c8e8e64afe242537ac53a00edb96672e70c43a92321504557c2f1900bc3d8faad334b5c3611c2d473f0301b6910b95c1046f9444e02d02f4346ccfa8e480a5c5779c88d016696649f2ec4b059b8aa954b550208ead3381cedf55d7ddfb7dad484ab59885039df3dc19a9107f96f8a668fbcda7d5c15d8355462df6e8f49a3ecf45c5bec972e8d22864269fd82b2e8dea1a90c4c147c89f3452169ff150f43d2eca540bc19c3251266ce9ec86ed488cf6b072d52a4d84b576df849abf702476c373ac30bb0d5070677f76a410836ea9ee5586eedb817f4d3682909571163027fdeadff328fa0f760696841e2d3c84c01d84c2e45da7b341f4fa8f4505134b56355f93543535b48075310f07517854327058d7cd2a330670aeb0abeb9075ebb8c6df4043175ef473f25e6de0e17a2165d68694a950f37482b4bfe1d9f579dae009548e1e743e2a42ab96c790a8e5a01731c5a36bbc1a0f667639d90e724e0d08eab5d4dd1fc8c904295f49ebafc4a2d61471e7304166498bb072bad3d098e0f67cad1d0a11a8b465f7d89d4a86d9682214ea24c9d65cb6cf07b1d07a8a76e50adc6267099f7a99a82b25971f580c0f8d90fe6551b36dcc12b2f57b87586e6603dc2444e2ed99073de25df5683db10ac4cda2d04aa6a6fe91248df88e39a2208a6fa4ceac4c6243ea9fbfd004256a163b7d026d31d52846ae7a2e60a0a813bb214e909b3b6ce75279a6827f63e2830f64e92bf0d9663f947938058984dab5968baec7e58e0c2e724590892c92073aaf3eb135b2670e9aed3225da1847118957075f019587a5daa7eb40db8f09abd790c7e6f4aba1566aa97b167c4794232d4603bc0087a284a939adca2ebd56fb6fc0742d64950402b6907f031170ecfe8b4b086e4e5a65e17fe9a084dd29a809f247f654efba224772f4cd3f40efc0f2fd53ef2157189ae46d9b146168972e82218a5a373fc5e353c6102792ab690c597478b338a6da6c5a3142aba27304a8eee7a88fa327289596ff4bba9c2fd6dee19e6206880701e526ebc988b470daec9070907a1b59ac7d41831a82378cd3f120e339ac9e03c1819e46379f9158ef2cde50b9ed414ccae55ea11f1c5fd3950fd4fefd359c3f20013610d407e8974ec7d354d40cc446c28be25b7d7d63e380aec27014379316d6919869457fa88c5c21102d5939e9b02aa1c5d9aa07d61135d8184179e954b83b08121d23341e06202a78ec7e07528bafe260660a130f5f14d1cbf948865c91fc80b228ebc9a8899146dcd28efa643a2a19400707307f048a579158978d167a5b60490c29c96acbcd0e9393f6c82bc9a47ab906d7687836ec1a93c8bc89c03ad453bd7250fa1bc6428425286318a71468458e67302e7cadad0d6e75768f96d4653af59a5d685d65a28fa9f3ab7bed20786b1561527193d640f9026ed086525c480fa0d2948ea25250dd9963634df8a58a30d02de3284da6ecaf42c41fa29da42a3f9ff74430c10e5a5fd2d6543f17a6dc9cfa37f59769b6305c0d3e80a104796a57b44e30088da86bba2328cd44346d96aad6f5fb5f54f00240858cc354da859c5c60505c1baf465111b210db46f8160919d4729f1320bd4abb99a6166f02348fbc2765e9c7e5063c24d5ba0fa9b88e14a6610160ac5fd80031abf52f7d9dd2d050814d203be5dd22704b0c979610790518c95e2ff2e938d60f2dc24b9c007e8b61c150d24305735a5119ca8835540ca7b0b81d072aa9fa87a3e8e86232148c6d351e12874d5297d4aca2b86fc5e1622381c99d8d7c17b3269430f89137356dfa78dc4edf7e95bab567feca12b7c21818bb922dfa954e45e3351908b7267e28a176076666eacdb4111fff32b4b75bc39f8cc8f844175e2601b27840edde16d95c129a577af48bcbc5bbed7aba8813f49375e04329438a494d177d906e88c51fdec94bd1b411c53c75e68eec171f4c5dba501f7adb7e0d111ae7ca3da1309162cf3c8915a80050f889b6522035aa0d1faed71ca81bffad9d6509423520b49e7afc1851e9eba3e981f0c167f9f7465ec56fafe2c25bd985a8e033eaad744527f4598ad10a8362689ba2f385ac4d9e88057f5d3d75bfc69a39ef828415b8f1e835f66365da1d8fa64c9c6df9f5fa5ad8be17578315a4d45b7120d1550c9e62ab3f894e8e50640f07b526467042bf278707bdf39de444ab42934e81eb857edd12fc3b8868c9ef17a807992754909607a55718f9c0fcf793c9de6bb83cf905fa08af2004cdbced7e22f3296b17b397e75871feccea1b87d5095e3171f4b1f44fb568e285449cb3fbacb8b64b1699fd9534a6e9c0820083003c104fdd2c2d0022f49dc05a9a2f7ac447aca443265a9fd3f5559a5334e7d7b5d5eab4c2aaa3de249d57c281f5d9a3a5afaf24946e29571a648851ab34eca33ea4ccf49a790fa2ae7bcf9f88c2e1c674d8a9323541e9e68ed664c8fec4bcc673e85b52ddfb2df1d52eee651098bcab94a20c163a99ec71f67aae087503703f9c81ac857b353945dac4d1fa4f63d9a2450b939df0bfacb130941dad04ad328ebc68dd60afbb6ea85e125b1b761ceb4842891c0bfbf611bd5639709035e7c9565f671c45126cf36f74503d1033373dd70065c3cdc1c542c8d47f750fefa195dd0c471206224787518af8a2bcee6e7015b9b766cae3918627d1b40b040ca33fc360c08b3eb13b630f8d5a21501c1f04f437c43e6de73b7ac6d2044ffb94a2548a447dfe5fa96242bd1860f587b1c6dc02777c1c0885aff8851517fd8add58031c6b118d09f727f1538d7439988fa326854f18ad1b9a0411366c929d00ac22db05230b2d5baf3d7a3887ddad0746a500411889e8269255170915d84c6a8fa60314a038d36bcee7e20bb5e75554943b1b44588143cfeadea2a7dbb775284d28337bf2c0a02c1b2c863f4152eb405d308fe5d6bee1633379ac593bba0696b4e7c2e9a23cdf9caea5a5035d6d0192520ca0be81bcf8e4797b7e68bc78579419010838c79f83160eed060f5c38813c42f03f67df79ed1a3605db3431ddf50149fbbb808bb068f03b6d779f2aba9893d644b5dc1c1fd986a55c7387bdcb779d770742daf41a1083bbce9f93d6f2c13366b6997b16579f995dcdbe0cd7ecc5e90a7696bf1a163d237aafb239d7cab188b554ecd043ef7a3c1644fc5a384a85b620943430f3545b6c383dc35d836bf65b03c5fc1608aea4341be432ae44753c3dbc13c26ff2cfb434332477329fac79c74ccd6ec43f94a74fa76b2ec068728ecf80c2e856aad44397402289cf55fed0108c1bf2d41e574d9e762e2c681a409ffa38edb4bec7d4944463468b7aea323979953419e862791762cd89550b69b437fe55a4496708a56ea8abeb532dea2d7a53ef642f05b1ea2f05c879da9a36c64c100cf1103b3abba288c9cdb5fd30617c24775665980df9212312044f4002ebbb534ecdf2aaffb83ab2d054a2fa378f951702247a9b8e3fb177dcebd0f505a4556efe08e347ca64ac818fc802ffada1a416caf2b5954690a93069c75c67b1831d61589acbba8b61f445a3ac4490b683450508a14f1390ef7512d45d12415df945f54d75790add3e5166776281487447b4f8789e90744009e04e0dd983eb9bac1f32a0dfced3f31401765babe40b54c5fac0489f4167a99534d76034a32946c99f5c00372a5828c790ebe928003e866de2e6d15b2db6873f08d2bb32d2d2e33e88a52c2c8c304d7b2110a4647d711e66202a573242942f4e2eb741f9f5490f8b54c7b7a14a8b0ed57b4e8fa0c65b5f5b3c883df1caae254729cf72d27a0eb865750c8386a5a06569088d2343445b37f112e0b25cb7e0424282faedaa3240c9dca238fab23de9835879ceea31c208e967f4a37f301a78ef5a10ccb86d4288971d2fd39b7a6e5f14139a6817d327871955edc1156501b725ebb42a0f6d5f4644fa77335254c7408dffe03ab0bf7fb4e04518c04b626c112ff0cf0674b52892781ac9d8f827cb912bed12ce0d52c330f60f40cef7ac170b6c4f5a52e447c3a74662b05fc89d8e2f54f7e981d6abe54370ca33352a6118047200966b6864b45ed559e2bd720e9cbfb4a44eedd1e02540ef99d7d495f1ef46e66ef40a3b79c6a2ee433eb3aeb74463ab1c336806a2a2a497ee348417f83bc22d33b22e6b97978daae38d50e8c0fd65a0c35df22a58e60541d2444669f59ae7d9dcd2cb17c5e3bab78d68a8848ac1e462ab02697125687db058f756b64c54e9dee53feb45f8d0d27d09e5cb34de81c83f4b3477dc7bb8d2e05d1fab5ac7e12f917acd7abbc46465742a14c128a89295db240f335b4b5039bd28e9644ecbc58e848c310f84cd51bf98a1945b73e8f2d2b2b78b5e791886aeb968bd238bad4422f87c7e6ccdbf58ccaf715e134325c6791165d56b1b0e1323fa38438a44ed4274c4c783a2f844750ee81e88484e7e590f23c9c43824a390919d765ce929488d10692f1c98d88c88d088967f6c499d2947934f20bdb2316158054213949a07c1f1ff10b698193ec488f1e98312ff00ced33bbaba8c25c01d9574e03d94b4e04fa5b4f9ee67cba7abc0e484acc87422846ec0f2ed25acc0bf6f9157d874ce6e0039758bae889a6b8126eb0222eedffb2ac1c44d6815ce313ffe5c1b7df752927bebf167547e88fbbacc0b1de8dd7a38364e334a88285674f032ac23b9a1e1d518d8a234f50688d97fa4ed31856bcf0834c04f375103a0da95b00e607555a93b43561b4c98988c6adb5bff4ae8bf9e11862ee3705378afd36ae39817ac16e215ffd4d23d4af8f65e053d30abf6db3f55cb36c70364a3b1ee89a679fd9bd83bcaee02b256fcb0296f67f325d1127f104ff1756f45720a8fd3ce982961721503ce8cfa6c3cec9614d5ad1eea3fc0248e3ed972cf46786a2ba31cb12f4cc0c4c03d121b56c4ccd3480628093de0c251903331d7a2a51e4a37eb025a28e75a877aca661604a9ddf4daf63880938d56e40e1718ba216e1e509dd36255fd663081541d303ae64bb8af5b3fa55305fb6f82ae3f282b7b255db14fd4caffb2c6f8a73c368d9d8aa7de6f115abe4a72b4ec0641e0cc91cd22a5fc78556502bf34218e64c3658ca7041a2bebdc3d5de810d10d510f9d5b1214931668b05946098c63e8625b93683dc116a6131f2b8053792b565390db09c7dc4519402ea9ebc139c24205276d547e1982eee1a68a3718e20816c97025046500c2d373f68f2c4e40f8ee73f60cb6e995067f3611f436544e379e766b41ae90b283d1d182c8cb4bcede5d0f3005b6deea57fd58ba99d39e91b58ac83a1385a2877ff4498f1e7a66cd2dc753569c39d4e3adf4832efbb682bb52101a91787a2ad043b8f4d48412fabf75dc01908e892356f059dd887cd0c74d871958d69fb08730afb4f5742ea9eba479bee060a35ec23d1d329fcb50f220eb081d356700a83fd43a4e9a5098ebb7d931b77e27086cb5ef340a4030eacb9ca5b2cc82dc1a8fcf926c76cfe12937f4ee6561381499bf3e8cd9d82635bc80990975c027ab36a4934b76a31ea6f5bcb7c353aaa16f926722bc84508747718fc13be86ac70f08d8978d78d7bb5662e4815768fab6252b58f49333a28491d79caf2ba06c61b0648be6dfa8125cad15312b07cfcdc6119f55e86305024c4f402abf3a43202baef2442c947eda790638f7c384b4283ed70ec2d6849c71a57b9c25918b92d0b48770a3fb0a73fa4e8cc49620452010ca25ff21a29a09af4ad2b9c7cb1f9ccf40f12354d6d448714a304b0c4b09c5e4fe68a06d58d7d393fae229fb3226481922b4da99c065579711a844b948dcb160d002bf46dae8cdfa7d151916077f15878976dee762bc0638593b1dfc5644762e94fbb19a3ea240db4e94683e020b92591e8c3c09fbd9a253ac2823f539d98a342b43f184ea54896093693f326649d6cf48c07242186b16c27037f2df293f10304d0fcd626b726b8014485af3963df57f835ffa67c6e18d32412502cafb9dd55adc1e51807b349cd9bee8506dea226f43aeae6082241283478e47de6711080e640c6f217cf2dc7a1f96c9551c5e36bfd22da871a6d14dd3f0593b0b62ef40507f51b3791b193460eb2bf64bec73074f63c2dc1cbc37addf8394841e423fb8667cf9447b3638d1d41dba9483808249a398ee10c88a9461289c915c2846db8e6210d684ddb50509910a176e8e147687246cdaf0a53182c5a8094533b20a5abf0d5c62034ba84190b15a01f90c463258ee7870882776e3de26394ee7814ef3560668bca09063cfbfb7a2d57dd408a7395f084b0ab744ed4c9158edb31148839895f0a331b67d855c5bfe55ce27067c3fb1b3865764063faa2707b8e59fa5b26647ce9cc7674912218ed076541178e9296b2c5bc56b0e6b8ba41176d13de9b1e7718428e9819dc18a510c89346407615f7e0ec00b6430dda91decea95af344217229c123976b51f018bc5ae032f6f30fe0560167f4df234504880f834b6ec90712363b4496c0024b3ef66787f5883e9e84d89e70ffae75b72fc204dd6426e0d796b91ac5051e6bc6587c96fe88c3e342ab3c5d1ee0620cc3cd403b9eb5ccafe8e6829160565fd8bcc5f38c21a4c9df3bcd5786576332f6dca7250a428f61f7be908c270ed347dcc21f78769535c7acb8698c411eab7325d41046be9e3568601d2fb19ec6a0c4e120c4d4c0681b86e403d0cd8fc125a834c70bbe93b82a111dff334abe3803fe894108d82dfb96dab6992315dfb8327b8255af3c0234e0d0d8b52db062c9e1c588c8aacb4bf6d432bf07abda2f503b0174c82a7761b4c92694b2e52bb072193845a491152eee9ceced0c0970e097bd61e4c6e7a5b3c4dedce93950f5214b97aa471c9fc9c3f0b0f455aa2a35d033aa90503b2921c09358f21821a42edf7ac6909c7403c94a77c0c6d68fc2e168e4301e265ab1efdd1117c92b5f847645d7c5c5307ba178024140273e9a52cdd35cd4618693324775665cea6fc6023401213d484c409a2dc1d30044ccf34e5a98e5feac85c3b3471e33ea4f28830bdb5d358fabaca478d4163b13d40fe8933b8ca62b5cdfe6000e2bcb22d6df44151ad2c78e6523d7a45426cf5df0780649b5f1d669f4a5df830876d604d042b9b1707eb5904ac47045e82cbc27b2a18ed2554461f779df813e5010f616bee010b0db2f31f224ddc8fc0f14c8c0a015f6dd007a11a79be93ea5129da8910ac624451ad5875cf912d41c2ecd5d71e80c4f1f8174c4f01ead1d02e57608eb2eaca40188c6674dde8c189c9b5f649d9a064266acee0de5cc835f6fda7e3f10a733d70060b8f77e049e4e26f42330f5f729334b8cd992ac929d8285ad38ea68612c71794281feed3cdde22bbf79652cabde59601eb02e402ca022ac8f8629f404f0d878f263bbf2d67a0dcb62021757006f951bc01f5428f212c5944944fa6b23d93b7dbed289b4a17a3c084fc99daf3b49ffd0be9d29dd80f3ab4b564527140fc420fe79cf756adb53693946b7889a9e929859929de3aed1ea42d95854907f1a48a45d9cbeab4ad41b877ef03547279c44053909414a4765a882dfc8e5e45b6b8f78e43d1af77e2185a01d5a31e36c6c2589602e04e1530484dc21cbd5c6b71393f32ccd98c1176fe02d2a0cb525e7565a0b213765c87ea41fee01d1d6b5191325604ba513304a87b3beab813a43585ba1bfd3ced67ff1be81c825bee0c85b931c28f4f0f2f3a0b0b595e2c8039e25dd738099d864e44a7a293b10b69adb5d66580700e6827f9104e4319750fd75aeb1f64ae61ab649652c5cf92b71a059abe0c4540a3e69b73cef98e5c9aa23b44bed683132c0fd21e013dcc44946b3878824918db4ba200bb98bead15be2e5cccb7130609c491e1c713724b91d2d1ebd9495d3f703f783f80db4b968f5e772c8e47fe6cef04e5e8bc309b97adbce69b73ce399732a1c9e5fc07092ec7bf73540a894fce6cc242d1a45505b6c37bdacffe4f9e094f280a4e92452a589d2a4cf1d97befadcb35ec230aa6127409d3b4584375c3b76a34ca968a285d381ed0488594a48db37791910d4c7cbfdd0a3dca37e5d3e311a1dbe1eb80809b15949e959298ab28ac63084894984b1297259673ce77fc542e487e45e773ceb9ee2ad73056d76251e8b6628fc4777e80ec9fa51d22ba4632d7709672458de50a1bb7bdf7de3d810593ac326cdd7c7a1d1c20d55f678cce24b1bcfe84c7f3a911b2b0cdc65818cb9e3c25600ab574a581c270cdeb913373c45c3167cc217356b92492d7aef7dd7906e8e5731413a276a105dc538e2b30424e5882d8180b63d95e4e039f90004439496a1e4541f5f7b49fbdda0313c202ad1dd7121f19e2912b4cc82fab4c07427078e4a5e9042b41be08318d7eb2f41dc4be61ca6a0710ff847befcd4bae9d03550d7cdacffe81df6e23e6b27ce9deadc68c4292d4033ba18e6d3bdf5c9b4170b98679fa4410604c29f7b49ffd033ded670fd4818531543aac9c80a4844148969016940b71b81af9f494682cc8883133258b0b45a6313ba11aa92a9efea9563dbef5de7bef2bc35cc3c4a23155710599a63584b670a530c6084f8a182c530060a9b274fa20da3bc773bb6f3917a2bddb7bef0ddc38a8ce2b2182bcdd4ee619f210798a3cc65c0124088daf0d3e3698cb500814141c54ca00d9128188170f506694592186449cd350c1a2f2d05918cb64888db130963d795a6bfd03e61a36fa25b1bfa5d88fe97f4db4df538bb3dbec46815768191f52153488986bb868445aad084aa629be28d110807a0793a07941bc1b1336a6388db0b542074fc21c09614b46fe7828af434fd74de4de7b6fe130d730b168445a7114c224e729093aa8f7de7be73ded67df81661ed55a6b9e30d7f090476479c5d867c98caf953dca47563ffa50fed8180b63d9b507f532d4f0b49ffd7f617b9c73ee33409201037890c9e5b0611caad22f1c4b90d23a75c1de854ffbd97fe769ded37ef6efc339e73d2e245eb113ee1f06c426bb2d2a2aab0fc01051e2a1416908c55893ecb51ba0889a06a74f18ea5ec38e73ce777cebbdf7de7befbdf77188e8ee9d54820943316cd9c1f36ea5bb5122a12b2b9aa7a7a727fd6c8c85b12c50d14e4e25ab925749aca456720b7b1972e4c92a2325a58c0b3f001c1df5a23037dd7fd68397badecf765c1a47526badc72c730ddf743ea214300491543502aee2728985e848085f7049296e88d02eb3302e1bf9274d67f0b8c59023c9d0132c5e92f4a81ffb6715360e312101811c0e483213a5868f1a545c5a42920431953e1b8c670d779e8df34d32bd14c7c65818cb5ae5c8d9014468d74b724276391661ead2f7872646e30249567e600509ea7222c245ca0827d2521207a6a2820a6f8bd8180b63591db9e906640295e6f178ac085dadeefb95a4986bd888b43a9124b9693f7d07e4d4f9e69c735ea42ad7f015963e5144eb7f5415887d94c7efbd37d732d7f0adebd828cdefefab4ce9a5511c54d2fdfb7afd4275d7a9aca319f3ae15cf7edbe8e02a4570b75787c690fcfbcff588c5341364187664b9bdee676092c81c5cbc24a8e5ead37c01b48e9147e0e3328f505fc11979043e76b9b96671d9c3b5993e1d5c42996fc34b8d9e7fd692ac6294c5370b904fd7d4ea900e8f51bcac25d7868d68f118c5c4b5e1aa984922bb267a76e020460793c4f94bd786bbbe755d3cc58395b1dcd4355f0903475c48d0f32c55145cf3d97cbbf1a2ff03c12012127efbe9007a849aefeb084602a6f9acf9dfb6467e8ce02a058848d040d4a14fcf5854bbf4882cc36263fb631ee1b68f16aa3c8f482c2bb82f171f91471e1988693e7b8110a4293c7e49576ae8a52de66ed9ab35eadb9e55d3547d1433373fad021722da74ab3110da54dfb6b70cab5afdada4fb777d6cf6d94ed5dfaa0ccf3a7f2d45d9cf1a164c8c5e99966bd217a06b9bf6c1322dc5e56452929bcd0e458dccb897930d37bfbeca0e98356ef8a804e8e6ef3a3d935c6cc5cd47a72c377dbd5ff8acd140ff2bd3cc145a865f63045e277ee55a4e5f3fa64991be32adbb26cc354d94cb9a41098b33ad6635c5e40852ac0abeabf9f4b344bed906c4cebb662430ca78f5aee7561159e564caa7733a639777dd4d8c0c4d1e255a6e6fb6c189e15d3bb00a95652af441d113a67755b770a460f5aeeb91222f29a61b8d50d5cd8ab106d19b6d1c58beab06285b49272e27ba77ada1b29123da9045a9a8839508a9acc6b4c063a6e3a501fb01c33286e8335bf8f5d0bdd906078953962ec137db6015aae8a8514763885e1d1a24ac10e55efd3a34485c81d1f2a24f037ff911258accab4303b6c2ab7568c0b4bca88c06ecc77bca7a6ec0fc179043ba77f5f0012f97b4846fb60171e4a5819a6894ad416a4a51a7b8178e033570033550558f74be79aa39e71c8cf0ace11e46d97b74ef09bff229b0c3147bbedd50e8cfc8434d1e8aa28fa228aaa54a30ba285adcedd634218a3c1c82583d068d75d6da9cf26d381e97b1f65d746a98c13755756a33a3a8e6726d22509a9b0690737edda4cd4c24e6114d266e41e0d8f3b51ea2a8d6362fae39df4d1c6a94c7aca518f268d41a4af9b8368cc5b7d64d2f10336d935f37f5b48687a206a0a7251e4ed01004e5b49bdc9a8b8f479f8b31c678ef5775bbd5d7fbf1062f57033527d37c4e56caf039194dd3343d4f143d7bcf20c3b9d65599ed35cd89f3d7ef1df7c7af41874980a229e734329c65b698bb7bfa2bb751f1fe736f592cd3d8d6f6b6d6362593a15926eb4a58f3b39a53178ec79b6be3e2a3d4968ba2e8dea7aceb8ca25c86660950f33c914260a54a3a7e26c26471c14d8bd3e2058c1825724adcb43738376ddbde80621881e2d530eba9684c2ab7baa63432558943a6a64ec408e862513fe9a29ed2c51e74cb80f802468c3e6b5f9da5e19430018bc15ebd78c3af793312e26ebae090a989e7338d3266e87c98ddda57676938254cc062371cecd56f37deed85040edbd301f4c5491d1d8c1719dc7c40a318322e10b6a8c005b23d6b4955bf88819343ad15cf72d6b455d755cd2c5ae09448d39ade332ebac8236c3d031339285ec898a1c3a2457fb13091d33bac9b406122a72fc9a66f298fb0993722d0241a169fa64ca360aaeaf2089b6985337938d36828eedfde9478b1234ed377ee2419f4ff51cc9e96d3a2021722da58160b03d17b26b28f62f6b49c1615b810d1b230117bf6ec06c1ce68b3fc678a3818828cb1d28693eb6a12cd90c1417e93cc251d93f802460c8c83953414f76f6f4abc5819335e1bc53274b0d22c7ec1ed9abebaae6a5e53959645f44c5ba1fefaaa4c8015bb3028f67b64dc6d6b5d5bbfe0a6c569f102468c1239aa7e95e0379d77f506e7c6c6b6ed2d3d120267adaea9baae2a1e13340ad7548dda72d757f57f08e1a2c35e13ebaf81faebdbd47f695507b57e1a8a3b98efcdd6f068c47253550775528e22a8246dddf7cccd80008a069318000005000c8371200bb32c09553d1400072b8688bc8470a030168843c1b0400c0604836130280c00026050208a83180aa439ae92bd6ea36dfea7ca910ad69099128a9eea697280d3369cc7d579791f71ef55d0c6b560827f971dd2462687598d7298dea9323796e3e83c9814d46edc8ec3e4929af372adf00565c893047b1cc78596fea4576a8352b68c51da07d49b481bc0a7a78b99be6f301cd1c4f6d05bfb62718ac0fd4d9666091befe28d53e58708686b5c710508fcafb438b3dd3eb3da8646a8a4b0cbb14e2167680eaa6b92c6ee086c210ba929fc25b238a188b74aa3cd7d19353b09be55bd2146fa56a7e07e4de14ae54522265e6d27bbc1f28916c5ff4e2d911dabdea70697b8b14b75a9be4b465a37ce12532d0d64362d408338227cff42a274c17d24e205827426c2e94eb5319d411cd3c96db116ea00cbaafc56cddee200404266af65472fedecb59dbd981dbd0c3b7a69a7d776d8def494b0777ffe17806c62448f9f02e7289daab67c9273c4a19c8187600ea86cf0b53633a08506d30df202739bf169c70e4b23995116517cbf6b8933e1b2b4f3981bfd8be85ecd6961e7fbfdcd6cfc4db4a59e290d3b1c6e9ccecb684b572de819f8bef3bc6177b423d15d1c4fe8f07dc803d111419af74d097d2d39e82dbf2a6f5880fabd490844e38b53e89e6a0bdb0fbda8dfa315410012441ebbd25bd4c4d1c0541318382ac92c0f9f84ce51afe6bb69d556b619499acd6fe0d17a963ba4b79a616826caf4a2a1a8b64a1d505decfe8bcc0e44cd44e160f9021529161835052c84a5f4c50d750ae49ffd3dadd073284c1ce93816c968a22896b6a35dbc97d5886027f094d60563eb3633461fb2bdd44ffbd2b0ccf0f411848342de1675c124996ae65be0fe0e6d0a94ae10a02469b847de205f5c18a9cc665ea142e8cc01e78430241778d75faec6b8d2941655729e604a8169515c8855dac1c4f5e5832d0c3b1ad886ab3627429dffeaf5b2b2e36ffcfeaa955daa0b74357db697328e3950bbb577d986d693984f71d684d7a817af0968bf59ebc963dad1d2158460a0736798caad6c96fc93a75cf1b3b7527f3c4f8bb9ca02298ead92f848c350ffdc0344a30795d87dad3bd37aa52808c33fe24a391f73df55278e13c43f87d31054e8e69b705f62a57d488009e649a85f1f08e5034c1909fbcac256049d1000e9ecf7e31250e5bfa38eb4fe6f4e2cd204a1598736d2e2c084d4bd30a9538710ffb0f4a738e8fc687c63a144572803ead2b35725a1706112fc63287900199431f616941579dc3cbfc8cd8d9bce4522c4e741654cd11d89756ecee993009e840746541d2850c95eff7e3f417a3ea90ae79ad4eece06240256d40a108d3039fae799f06d9ce2d1852dc00f5312124e92b88b70ac125813e3e5b237c2d7880ae63431ad99338cdb3de9ce2a90fd02847238cb20d1d489a5b7a73f87425649b89b9e62cdea00fa47c4fe6fb1409b166a676b63926e8933959fde0f50f34b96ea0166dcab78faac84431960a24840a53e0f34a4e0e81534678924ae950fa8b0899eaea000b74364c79e95adc32a94da6c00206a663759f718189939bf2e15697ce4170783c03b58c413e7406f1d807d07dd3ddd2d0f8e027d0f01368d64f78122c9436ff86603bb0fb4a396d3f581dab3ef93883005e6e1ffc422049bad0da16e043a9ac6aad6d717afe8e991d05b0d21daeeca8f844b620747399d53127b1729a47ce41b7eb782755b15ab1b8032da95656093e11a0830dd3109e91b04f856b52f7b1e17d1bc4c67da521c3ad3f123c043a453e068d2e606273252a0cbd024074823e03e6bbab4ab43e343074061121c0e2cba1202b4e668b02d7fe4ff37335ed10592f9ae63e2ff51656a8f83a8350644ef8d19f1e465f05fd8ba6ff7fd4b2adf512f8d26af41034873bc4d91e2bfb17f29409508ef2f30b94268b67f87056bd54311f62286bd1695f12582e27ebf3dd7a7e40af98e830b0bb3f8febf834c6f4c84371f9b9cf323cb116ea539b96f34f6433ab8c3c18615eb303548cc521683f3df056c5a43ad2ce89e0caedd2c751193937ec4fb7501d3d7aa0b24da444dd76002240eb309d9166c36169fa042ab484b0810d88a283ac198838136eecf79e7ea0818568681d84925387f90cc25a0f3393aeddba0fa7909ed160fbd551f1fdbbd1dd2ab6cd7a8c0858c3747d171e812f770288d3e88b9a6da15eb0eb5cf73e702b4c923c885b8a5359d2ee0b256fc40d32e62ba02274262d88d0dd152269ab6fada873155aaade5e68b401cb8ad7284e7976bcaf2ad49d7f458847db902048b7cc20ef64c24b3d23cf829238685422231213752d263865aaea29c6359e219c11ef58bf34fefde3ee0c69ef6e6fd7bcc800bfba5d85ee3b4d5cafb8db7cb61aa7c3cc89e54db1b15df298e671c0b2289b3ecb0dfc8820c2936385247bfc543321f5ad5f588e1335fa9dafac46e618ee905108440ef77a344cd0518d991428761e8047a0174054b190a14595dbc660137a7eee5c20c786b6b8be85796f6e700020f5470641a6e9eb1040557a38d41053449696911e23c7a1bd5f6c689358f9141a5af0dd5879c0198950a8a7e8cf22c516e40a17ea6775966643d36558a6dd9b7d15b52195e09bd8a2e6db57c39dc01ed84963d478bf30a430132cb734fc617df2ff3c91f2edb705f124ce80beae5872043c2f112223cbc0b1fc82159fecfe51c1d09254e8bf6973d814e3ad05f00e274267c9d6cf404c46ca534c090c2a07720249523523beae83dd4f2e0a37e380d1a54fd37591a0f76267e638e9de983781b7e5a9994fe316f48cdd2d4170e053fa14b5642b2422ee997f1061b87028abaef048465a9b33dee6ce35e2af4495e568dbd72f253b6b9959983a7b0dfe3759f0a3fb8617e679ce3b2fefdebb901e7f8f3835182e8928245a83a5be341c4985c1ff21546b5315752fc036dcf2871cae8b483aa0efb1927796e3ac3a9fb95e5165401a554231db328bddc73c7a29e3ebac09e75e33e55bd1184a45f304ec842cf26c8855432656976e945f29fe52d7ea20638305be88ac2fb8a2d87e06e6cca74c32eeac50382a63ed5e844bb0cb08f6b15a76703abb0c6e3a06eaab7641596f0ce291e68069f01ef1cec81a19243c61f338c6a6e59759594464adfbdba88a27477a7f6c38b92c2c5fa149602280be7ce294aee9f95869af4b80ea09bedd5a51b5628dc9da948cdc56be659ada762513d5c6c5d1bcf67cd60ca9ea045a05f35a51cf1da3068e9612e40b7e6d1e9363b3a636bef7fea125caf4117705594a31321fc0f34df1732520028c3f6818abfc1633b37e35066d857dc1c526d5e98def9ed33422dda035f41d860882fd607c90985897f5013118ce6f3c491e127c195b7911aff3a35c78add701e3a809172939e15a544c35bdb51def1b1b0c6955a85e562560812193f015085607088145614a74a60e062613223c503e6cd70319e03578ca5cfc7eab195373441302f5462846b31d85049d7195c0119d07b165e7cf027df9c04a8fec6c656272d224620c539fd9b77152390ef09e3fd4b6bef748abe67583de884d5d54127ec0406a5b4b49caf4284b27eb721e62abf3e90898bf832e72142cd42cd0c97ca221f25e4a8278d48e00c3658d209954095f4ade710aac907ccf3d6490c7a58ab5a1a1554db744f6e245ff69e15a5c5a35982b69581cad2bbc59207e963d293ee52fe72792509780052f21724b717d51fda47bfb998ef5e87d60c9dd9a74a7b14388d6a0389f8570a017afb0e0e6866147077ebdbad190b52808ecbf1dd6830eaeabce7cdeaf8b66de7f403846f288f10e0dbb29d10cdf07941274e796c6ea6b4cd82d8e5f10b2b0cc084b79ad8c549ee5b8b1f2150784244d1b421f0d185295997ca64abd0c56eb092e6c14f446618e43a1cce6dcc02172dc34747a9a80602d1606b9e79594be42c19c82315673232467fa971093c27bbe8a8995e64efbb177b028c216daccdb5c6cad7bf77ce24c561bf5aaa92a1fb47fdce82a6d03940cbfab6b3739f6ae19542b0ce43622cc94e0da1c885fb9359d02686fbe4ff3f1a74c1472506855efbb623a854a9d23d255a634c381137cb37d2a454f3a07d490b20f0abfdb7437c0eb5a919c839c02330a099fe87d8e4b490ef595976a50ff7c4392e199804558393b401adfcbfa95c60198e43d45583de4624c5afb208d9dc4caa993644057a582faca0810228f5699d0817e0ed035212ae30e56df041c7501a939ba9e94ba9719886e4c53c910a4e31eeb0504495f304001e9c3130a280a824998545bd67dadf41d189a70bfbc92934c04a362892769374b2ec06cdb56808cc88bc8507efa1a5d80573052ee230ae820f67e2ed66884f0dda1fa52954439aab931d47aab90cc423090c2d0cd531ee105881a20fa5007e33490d31d70a0e41c78a70d810876682265602c98ac7fe45ca2c04376e455bca3a44f1b1530cea4d700987153a75a8ac183adebe9b04e3a645f7c684aedd2a8690f708efac70ac00ed16e70a2a2ee4d9397e5971aaff3c91a0f9d7cb8a2a5fb60a901b652b64e98d5351f75f58416d3b60b942650333ba221625de755064eea799772e0346e26c2612c66eecabd5100da898327305aee267e4099165d8fccd4d882967be7f3a2f44450b4a3f9af1a18c1651bb10d44ed7d3097b3fb47e8a0efadf4dc3bd97963a8c2e939259800220a08adeda6a0d2f59d474f6705c4461badebbace76bbbbb00669f136f42d3acfac5264251759381f4cfe1fc70373cc3396eefe11b596cb6874c4d5938d032425546c0226958dc299f978b5146e633da1f73456cb62ac3eb1c2eb95bbeaec5c898d072680851711896d1190496e3fcef6015902672ec665d10703df6cb190c62affc8d01be4893200cb52174a5372bd428102119dba72bf454b1282b6b5261daadaaaed2bbe902a003c537d87779ed255ac50019f78c3a708815ad2a22e522a57e00bd53d342ba7c1f8db7a091ae2962bc3e12027725dc64af9e0306c2809078f6c961c6b13e0022125c9f0edb1842ec020da06592ec229641b925cf3e183282f2a583e6466ead46047480d3121b4e556e907235ba0c08c14b32c87acb81f601de6b17ffd223c2e2167e0a8f5970443d4e63e384f9f919c2ed2d6ba640de1e11b6f8b1d7548ada3a541e87211197c2a4913665903c10fc191669341a3c74bb57cb74f6223828912b52c439ce175432170e1f25e5ad4c02fc27eb06b5f8860422d942f8927e70f348601d7b6565264cf2453d992c00911c10910f8ea424cf6025ec0ddfc6dd6129f7cccf667f625d7b103574742df314de24bbd176d88ab25097d6112ff2893123726b4f7d3705672f965335dc0a998d6de4687a4d2f9a15fab9b1152a238b15e8f7c91cb2b0a653cc503ac778394a461933efb3019413416b3ad57bf601872b9ea80a75b3b9e7d6cb683fea97c481b0a5c6856cf5d651296b1117e30a5b81f73804febf3ba50f147419f01493bc66745e1b0c13e0b933d2607c6fdf80cf546028144dc07bd8dffd495972b0d076d800116c5344cdf920033430785d038b1a936abee2c681635c7f1a3e8463beca640a60a4f57f96a4852fa2ad1e4df432598627674724fb36ebbbf6d798c3eeca8f711ba4fffd779ab5adb5b4c59e7a3663a67ffe4142c3faa071a6d161e20282203b7d9a285f70928b36df9e8f054a97e84ef0779431af3f2342adade2f59f78aa5ee4967515d96e7d1a8d54aadb6958b47f3bb7aeba2418f06541652ea3eeea60bf58cbba46197aa75b77599a3760104e5cf2d34703fe2b1e2b146622b1f3ed6f98e6416c9a16de0bf47f31c58fa9898393b69753bf2f33dcfdd52c2c0ae0f33562f6f8f4292b2f399248201fb9926dc4e25773a12d89e3157d3319efd10d2267cfc344f6bf2ca0b50c8b18939fb33ede20528245fd18bf9c0265d1d153e471e926355bfe4cc8e23c8f070d0e5da37800f71bb1ddbbe842f6816814c72968a07cf3eec1d037284bd766b94b7f85362c8b66004e5463253616526a8e24b5295218818a409c51d0b16c8436c3eb0a26201829ee3f82769088ecd249910220230d6e60042a81d18a7c4a361041d02badccf7c833868205e2d9ee7daaa00d33b3c66d10ee16370f83f946c60bc25ed75963e512670722843bdb3d82004c92823612e533fc882f015842ba8cdb0cb07fdbd1980642fff1a3dd19419fb0206d1ee3f3bf47bcb22706388499e00a17945cc6e4271615a61d5e130fcb446dcace0bc752c9ea64f28bded7f91e2729f9d0f89aba1c5371d5c0261f3ec6c62fde30c2ba20f27d68d4879337fa5cefdca18a0685e228a55e07ad153f82ca7e92b1a344df1c659951bcb88ae909cfa5e21ecf8e7980476ead867ee60b0547d45569bb68f2088c3adaeb66b44dbca690e90194c6cc988d01ad172f2188e753b40025677a504f0c83ad83010e4bc18c4cc15086f66bcb37d7eb96abe5e9fb785e8c855e41d1c2ec0874129efee5d65f475c63a75915287682ee3c9d5c11164f4a261f7a6b96218e3524fbf8c1aec9b88a71fc0a248f58986a7b9dcf7d563bb1dc1481e26a0bd4521e8005f7ed60a89b1ccc7e7381b87d6c761a09edbaa83bef7fba5765428cdd57ee67a4b0016f71eb9c063a905350b615fdbe913234142c72bbbd2e6eca704d9e88b7a49963d8deec571e2697a888d48b0e34740b7b7bc139dec2828f201952497cea9ec2944a9861ab523e9019cd0dd10c7f8d22142c9a6937a2e660327cccd627bbb7f5b0051f6877ab24e6d0f1f8e34814b2c2ee70b1c86865f6010da6f2331d72b2cda7ba515c5f67be1f01cb26da7ab21dbc10607571271df67f4be584e1629e13dfc1f790dc4414e06830f272314ffdb5f02f58b510702945ea858faeed48b60f547c2074a001be5864adee422bbb17a9c6c12beb80191e1f77b4cb002cbf3f81598b7eac088917e9e6fc74b9a019b35db889831e6d8bd08c5320626ee97d31b4be69a3b5d6bbed6582b0cca977b565408d02ac90322979e4904d8654924aeb91e3e56ab2e8e454eb77d26ec31479d78d5f906f415eae7278dd8acb5fd42c9aa8138ecf01ed168ab33c0e2abaeb37ae10b6226a2ed78c8ac0a6a85eaeccc0eb26e8c4212afcf5698a13798745f6b63f4ca3f015287fe88f813032244205582f327b86eea2def4fdca92d5a4b9893736413b4a82b72e61434eb8013c5af8098a66ab01fad2f5e285929c889d9480d9fff47aade0e8ff891e41480c08db4a95d0eb8a98a569582182009cd4415cd39bcbff694c71e990866142a655a443804a78f2bb66188ccc5f7ac6f397b5be82b125c0aa0a3fccb278bfb8619cf8067f5e1c8f72552f09026a3a0a995ba7b619afe16694479f2357ad5a3ee0f455b74be85244999b39ab9c89bbbc0f10af3b6b971d0bd29b053a5b413ca3a5d6408c39c628eafecb5068e71dc4bd7760157673797d467b00160714fe14dc98193affef14efef4391dfd256b9d608ccb2e7e5528746aaf9effdc9a0ff1f3fbc777ec313afdcfae993d7203cceb1f218479364b3ef76599c78bfbae2e0f4e9639988b11f09e44a9a90023848473015eda18f8b8bb3daee50bcce9f85042c4286dddc57450ede9a7d47045735f20ed5221e4e5b81ec10317e1133b91c6ce8c504a06e3eae32906021c4ed4372632c2382211c89cd1d2f8cf611e2ddb823d3be645a76ee0a26c3b3d9203572bf18fb14f8fbee520fae971a96fec166944e0625135e6ffb2841dc981258df4e1281f317e1342193505f4cb5239331841ddf3109920094197047602004abc9c68a3316893b0208be9cb99735761c3077aec546e54f1aa9702aa2bec89bdabad420472cae32e42210c2f63ee28089e6298b07414cda6cdb0c9d3f95349b7392cf41dc1574427b17efa6658f65f1a9a0d8a5dabae09a70c0f6cc174487c08262600b4935cebe5c38b8baf61453a6a0cdd4ae758bc7bb7b0d0aaff77cc7a297b7d11d2afa99dc8c3ee894e8ebe3b75d6579562668a4814a0a78e5c500137e2f6f2a9022db569d5afb669d5e5a6fb9916bf5a05322f74242e599e9b762bed43ca4eba8caf4b167614fb80553cc4dc4552a1c15c1e77daecff408bda84134f81b0282b83a422e7b3722c94d59f2a49fc81c1ce82f78ade449a06c81621d38c011fd6dcabe15b84bc997ad83807254c3b842b5ef23506c19c4170a3e0d271cd67ec76f661152e33c7544d22d09260c77f1d2e47804129bc7e81a93a0580370dbd86ae4a3ab1a11ee888b0075981185eeff98e9c109d563ec86ecb23400841b1b882a6241401e6bc57d205c296f76555311170da33d320f6108059174820b5cd40dfd7c0dcd9fbea16a1fe18f5e1a709af34bdba5153149351c0c4bc290f322ca868b053a71ce5824cb3587879db50adcf4bc3d54468f938a986fbebe598d7a286d989bac9eac87768f508888ab9a76a49fd0f742ac9f54b7502a058a436f68d6d31c7fac65b49b8b16c5680990e0698647bb3d3ef363de5a84a212af04b05d8a639d196a033ec2e3353e0c965bf3dd93254c78f8488138777cf337603ef60dd8acd5884f87426bbfa765fd7365bbc48f49014d073bae018722352545f561ad1b12b6b96c66aea0e13cdd5fa5f5ac0d47b10e52fb63e85f4788250d2c6a2215c8cf59f29d5391072b1dee6585c65e5bdcaa5aa86cc1202bc0b7d8f02ba7f505aed67c77cd079a7ede91ea5e5aeb45a69f6111285c3d8886e238ba03b509686ec44c71d2c035a3da9999acc6a34f8cac2cdea9a72e22ce0d5329bd2c545a1a52821730bfea50bd060d3ce5dcf607f9f222778b8600dd900af418678feb2c9d123b68ca810154c68ad56960c4ad44c4eea430eada43361952c82f1f399206a4754e512383190abc704f2955e19baab069ddc18859d73955014dd4fcb4bdbca35c04dea7a7baf092cc8ff9035db4bce7e40c1b05d336e319e661e95ef9a8d10ff408692d31a6bff0e22a14858f8d68fb13006028abc6e3e8cf4f7521d1a05546ca480347453c5115e121cf4649507bac901ea237ae8952c1469e5dd91a37f560f382e2f365fb3c8d8b886df7a13db768deec34938196da69bfc4c52c493478236766352b7af3677006a70e465d902139a42d23155c664de962c13a17bc3ed4195b60517a0c71763ac9750b386d71a8229f6e3b40f6831087db5605d78f2f829dfe3a140afbb5067bc035303a9f5e4ef139f36f097034e9895d1d964277070950533ddc265d1ac0fabafb6aa9542eee8602b9034e09f013343d0445786a146fe073be3f710de9ce819fd4a0c8b48d1fa147753b833cae663106563424bab2afe4d4260a689a059de14b3b39940a12868c136e1c1247c15b78700ac1159506932d4049d103dd1d7a9e832a61fca8f98bfc28b9a9471c85e6bb16f2dbb3ae01287e7be53146eb4854662a0e1a181259666318692353c212ae73c86168e05b81bc56ed86859c4f39b0fee0272dc9f5aa895250fd6c12bfe6244f57c33379e8868274a1d767d64ad456fcd5a1d860b637c6c5ecb484a596509ae8683740f45a9d630da16602d40e270dd5f09527b355c7659b10a11c0cb4130a17ebba69e1a9e561fb44a1348c559449e00bf9247e994a671dad93b4a4d4fb49f8af22ed463e34676796fae72827bfa2925f8bcbd855d0c35e17f1ceaa6ee693adabe523147c9785d7b3fd09b4015c7fdd4dff6f268e05226f4924c8205d69a4cae112c20253048cb34b04fccdbdd2a074a7542c304505b4e5937be5d596e595d81638aeda4ddc55af7be50184c6a473f49379581959e2ab16864bb75d7d3db01f7139293b9ce9053118b9b76355e4322358e10fcaf68617b025f949b587f656a314b05d19161b27ba7d59bea4cd9061eb796035e9f6e68dd5fe8d1a56d541f19ec6dabf015622202867eb10245eefe4d4dfbafad193566cff07dca12aaafec0531642904bcc03e5e0046bdf293b9db6132735f02ac515161c212790a9994dfba5669e4525859de2243486480915cf42cd353150274a4e5434f6a3840ecd1e53aa0dd7e949623d44ef49264b91ab510221c9f2f5733593944406e28c2bf322595e39af025454ba417291bd53d557750be8db5fc1559cc8b4a9524204b185707f0ef93013efa38d3c6e0a78a95c03838e4e93696ec2adc56eb97f1014470448d81a7f1a5c4b0d8b81e35f406290a8b59fc3ed093990dbad45ae638781d110ebf47b23de83a026bf4303c8283ee75b88478b66ee04a44cbde13cf06089cdc6983a7c1bf8b7922846cb03f03c94bcbaa427275b4097279abbc5434a96b2e24f0913964e95e323db6b872f471b736331f6e0e6d72502a8ffdbd45df689c8a4ff9e1ab47ce8a01115730a06a8296f7abe38eb34ed2e0282d9896a5d97098dffd7c8f6dccbfa4181ceb482a62252fd40c8e65155943d63d6a10701ee98a54dd76b076a5208b5c7f777078771b2d53cfa97da15c376146ea4d01c8f637b091e2f5c938188328a8ab7a0316a6a20318f292fe0f9c16d0f1ccf29f4d1e4acd23b2962ecc6f2cbf77264a356b64a13fd4945e58dc6d3662f628d5ce6aaa3846c7eb52c331b581e5e9736bb8b264471e7640839f95b411a772a3df226e4f6b4ff4a90f7d018df138b961305491574ae1ccdbd830bdce15d73366251e9d7aa3e1f49358adec034c16cee19e4ebad5716c234c233f5be7d6142ab2f87f74cf4e268299cba865dc60b682b2d51acaac2564e8c5701430b0d9966326ae61bb282829d9c25bf7332b0d432855b9abb9d65810c6f715932ebc91aeb51f76a540f6ef6891244a3fae460b7af2856d3cd832e6009a8738522782580df41acec7703aaacc28b5b20103a9b3689b8cf54ebc9c335d70e2eaa0d5511c8f7b67830ce31d5f6535f0997ab2e17f451cc542a63bd0e09fedc9480066a0184458517e2044214133295e56be268b5c4c0988f15572acfd743f118b1a559420811a195dddd3bd508aa088c08353d39ac3db24836a956ddfce788a28fb546f54bada2e27725f6f2f1d9416b54b5cd1dfab34645faddae5fa2eacc1a511c3a75d42dc65a55a72c942a55bba83697d6b1635a5bab55b5aa56d5aa5a05e61d34b5d6c4ac128bd6c8c1c517d360189824d8da0be38ae1c11503c3ff310d7f8580011160586cb91b9be11ac1da6b6d0f18c32c5672c1b4a6c130f662181fb0d2b518b09fbdf8fb5e7e5c62c182055f0b232187857980d1e4e8ac2e161a6c2fc68205df991a3b609ea9363c1bdd8d5cc2b4365b0d0e13f0c55213334176310f316c2f86e10f0617db8bf1adc262a3c9e1336f369bcd66b3f6add454d681592fa671b9762771661470758757a833d9f5305ec2d8e28e852c509a8b7fc5a1af71d0301afe0a21849b18b1d78d0830441061860cc7e119ae114600470881ee8b3136cf226a42a0aa038ec1603cb8605a733fdeb85eba5e2e3e6dd3b6642be2366d3c80532bd1d0336956995534a8bbcc28453b3d5b3e2573a787480c81562f7e34c01c0db2fe87d48179981903d3fa3e50cf2ae05759ab10fc88f07891122f3d445b2e8c841c16d6008c264767759d68727272747678ee4c4a4a8a2a9a3bf499f4f4d86c5051b6dbb95c3475cc2172129744848ceaf8b431e9993ae6107c9c5bb4c82a227a666aec80333b3c1f8f0dcf4677e3462ea1041cdccd66b3d5e030015fa79a9809b28b351083c966e0cde50405c2ae14418fa8f67b6a0e1f7a137331993bf42bc771401cb77bdea8584452a5730822a40e7d5965bb34397ce6cd66b3d96c361a1a188d140cb5b144ad25d1cae1abc116d3e808e90929e6e4d2c56a52093127386738e7d49cab3959734281c39c7a4e2f36a0eaad6fbd0e815afa592190163bebd659bf0a81587db50a9f6c40430fc359eb20f8bdfdb83cf52f8bfe7d9ee7754f4f40e95d9773e69e6ae043e7388cf185a2df6bad7d72a2865affa1ad09f290a542c0c560616247a7ef7542f45a3f8740f593e2062eea29ca08c768d9c10d9568220538c22668607b6a82034f33c0f244031078c104155239ca1502d1a456cc0e1a86042c9b5b29b06145a5b17420fc614512ab06482055d26a07337c532195e7043ec9808bee33ca3f7840e5692087199476b0c08f2e4e3e5069c28054d20ab03c6d898a22df2e9fa2a8d1f040bf643ca1b3cd264a77b71a1664b141bbba7c72c114312af44c974f2e00420e3ac6066c88d02b1f42a0c12a4fb71a0ce8b0cba7db125f831b979a1f6e56d4c840eb2e9f6e5f625b7486124546169ae583cd0cba25448d14dac2a046078d9190a185eea03e043a3bf14a682b45cd94b6389841735d3e59d1410d34d8e59315aa1568dce593952f41505441f1c31314542e8c2e9fae0cd16d974f504ce9a3ee5d3e79d3a42d93b6aba5c961f667983426d87d22fc34fa3f4e2e67613e7e22274c9613c9e7dec97cfc4e982a27e2b813cc67b6e89747c50b07424495e0881c9880026abe94ff16f8bebee7795ef5ba9f735a99f34b2ba50e19c057f2f6a70ecb83cc9dfbf7b9f4995c48eadce7bae851420529aab327b4fb46ea1124499430f92165998af7f30e2af3fefe4f0a3e3e29de5f6d49052d7c1bbe94f25e1624cefd7943a0908641a4ce7d2d033d2565a5b5d3aec6279d4b9f1f6edb4149a3610f195d2a39926423498011f9a55f0848f1fe85622275605398f360d672133fe247fc02fc02db164acd299e23b01ce831b4d1a6d08602cf91ac5f36e91376fbd35667e0f728292599ca72ca72504a3c073a1c42cec2a48a63933ef35f4ce82ca1956ec3a43acb394c6aeaf673d6faf57a9183ca7516660a6e1c74e641ead81fbda93477ec7f53c70372cf3f66e9935f7e0d79e818e8517291d18b2a2d730f0b5d9f86b63a53e2a0ea6cda2a8e7daa1e0e4aec799e97a839a84af3f884e1e0453167ad5fafffccc73fa2f52b64496184952b2fa815c78e7a3956d65e2b0c7fec1efcef0bbf0fffeb42a030fc3cafc525f56ea8387958ef25f73cafeb3afd79acd5117a3e058ba0f983fdf80e015a3e7961d4e5d3172f9d842e9fbe38f50c46f5eff2c90b5bff2c58c37be9e6de0e40f6fb7700b2d38e9b4070fa9db0cc9fd24eff010a189ff4fe43432058863fe8c380390cff7d8ec3300f12fb7db15f2eb57c4f5e50e9ad2e9fbc88d2394edad925cce20db32f1f6639fb536bc8837efeb9474b7fc23891c3b8173b7ecb25d705891d7379f94fb93ba0619822efbd38731c4841282a93faf2969dd2db95e9b4c5d55da1d27e1de7cdd5c779db50b4c551673bb4c82b9f162e776e815222a1860aaa366fd672f846ecf7ef9813eb1c06383e9da613152f9f372ac632b0c3d54110ef980e821bc6e10de3780c3508b5cee1ad3b18a3831edeaf0e62fc20f6fec378831dc41ba683e0867ddfe30dfbded53bbcbd0e82fb7bbcb9dd43c53b4349cb60b1549d4d259c45ca6613549dada6fabd38ea366d544481944d22a478ff2add6c94864904fe8bef8ae39ca8e7eee857deea8a86abfce0b797c2f7bacdf10efb0c816666a0650f7ff51fc428fcd5cfda9c809c4d2366ed8b18913ed3ca01e46c1a91e74d2a06fd4e24c279933af78ba8787f853fc4f876ff525d8c1f6f222ade79a3b22b542643f585ce6248538b54bf2fb95c2f50fd7ee6f0955ca6d18432a3e03d9fa8a8c8df8832597cf425156fa48ea414a094524a29a5391407dd712371e453ca5fc6133a6bd24355d500436a5a18564e289afed437d0f56dcd566bebc33096562606aaee000ad2f78a035f8b7474a1dc2857875ef7fc7b74a150d95402824e4aef124d1cb64452a577822e2efa28b7d468d0b3368f6ceca8eb96e9de5befadf72af5fbf48554bff5de4b61f4de692475eccf5bc3e06b869296c1f6f165a4e2d0206549f01146b2b60152b43f6228dd5a23964903a42c02566ad69844c0dadbc36a42eae1ac51d1ce4e7bfe0974399b14983744718089898159db316bb3866bb7a967d6a691cc2c82ce682694a6dbd4612a51a932917a76a4a0e5418d9b5823b8457752259cf64d89a65a2f9dafd84b2dda4e396728a262ec02393729d8409762673169235b747edb237d5ac8216704e801d40eb2482b4546681cddbe4815e75e1915758f7864545111996e7f1ebd9a12a083a31e2f55b386ef8863c7fcd8957a4a626acb402501057503277c4d0b426ef8929c7be4d4c484d8c47ee4f0255a88007ad8f0255ce7a3cdc7843c6b2fe1fa920b3543b811d3e863a4f133f8929c33a326068b9543838f393f832fd14368c4f80883fbf13184dcbc00f8922c24068c8f0989c5584262ac3762fac1c725213f63c88d908f3164c6cd1b31c1e063ec61b03ec6d7f0255c5f6a41c647ee4b4372f2901eae971962c34799afe14bc0ce2d60c46463c4e4838f4b43be470b397c5c3a7ada90a7c197e86e0123a61c1a6fc4e4e2a3cdbb64f858f332ff537900a510005aff01000a27ecd0e1b345f81c3b84efd933d93661d208f038b6085fc20ee16f6c1b3c7b87d208f035b608afb343f89c4d028d8dd3a4d208c08d98449ea5a85847e0990a2ad69fb145e04b2dfccd0ea1059ef986fdde34ff7be665ecd7db6cd8d76c9a8fed19d8879be6c13df3df7ebdb7613cdf280dcf57f60ccf4e3c6ba162fda23df444b6d00fd9432f640b3dd11ee239680bf11c5571eac76cafc7d8d8890920e1a5c3d8dcd3510d4ed06576bd61024e5774d7c619b04285831e53b987d93c9b8be17bef49ef6cd783f6cfbbb65bcbf52bbbc73d8981ebdc8e617e612d1734b9cf362acb372abb929db2960c4565e0d7cf5b721495adbe7ee64265acaf9fa5ea54af5febb20f21c5faddc62c90d88a14eb7f1b1f21c5fae0c659a4583fdc380929d65f6dcc8414ebb3369e8114ab4d7e5b766fcbde6d190a702190fd56d8f85671ea0f6063201b43559cfa413696aa38f559d8780a5751b1b2b06dad460bb22512b728906a34209bd206b0256d858d6b50a2c9102808b6b9aaf44b28157e68d096341710a248e9564603fa2d1fb153afd82613e3890f0ac4824fdeec265ddf042e9ffc4991cc2927940a37a9a507879883a3cf8f5171f2a07ba48f91bc6ea712c9ebb1a43ce876798fc8c11756875930326858f7abf77e76dfb765cf5bda9d73bea25bdfa3c5e3be12da5b998ff96908b47a4bc51687e54c45bd67ff747e90ce2c2df3f1fbf6fcd6962f31c65540e9f24b974f5448f59c41993f24afdec22150ce79cbcf2fe77361506ebdfca1dfb7f60d81705f7cf185173dff6d75cf91bc7aebbf1fb8e75f81fbe28b2fbe74c997623e871278d198bcef421edeb75e863f70f782bc6ffd0314f0a473610f98f7f92597ff75606bcbd71be67dc7a3c561dedf9e5f7adfc1bcd54f6e61b611cccff03418ce949fa6f52c1665b558ac47f2fa02a9cba72024f174f9e4c55467cd607e3cf282b57e056bf1a04bc3eff3666d58fed9f3c36c58e63f54c6082d66fd7118cb52b183b15adfb71b307bc8c5a8a48ff991805dff97392c3feb3587651e747bfecc412acadf30a8e82f833a7bf931c6e30b747d8f3f4665acff3e29d67f0f6aadbf87ec9ac35a0f83fefc3a7fabdb23d85b7a8fb977e1cb70f54359fb495fed0f8ab9f37df7537f28b8f1e7ed1955baee6dee32e7304857469440a4e3057586f4b9f3866ea70df6a907f4087e379dadb5a07d2b85764207f1418fb28a4913a5d7eb17a864afd0aed86b17a5b55a1b02d94aefbc925b8d31748c451ff38ca939f93cf2418fb6e7095231e7f778a6627e1c642675cfdaa4628af4c19f9ff6207d64cf2385d2338dd27306bd1f67979ebbcbbff43985caa3fb2f2eddd45204ad4281b86f952c27e59337a3de415e99ffeaf927d45b790dd173ce398725d0e34c9a20903326152753990ed267fe4b08cd943393599b47337a9ee9f9e7149bb3075ef6cf561a0af1f46761434165c922ade4c8218a7bcfccb45af9bb93d4c934d480ced2ea97be7519c29339b91ba0faa5d4de67c20bad32819cd1da8c7e9f365deac42b8bb6521ba2dfafdbd2a6c929150f80353aa5dfa753a86ca44a7d532ab4898a4ab52a2dce59eb170b3405a2e9bb69209a28ad512a773a61af0de71654e64f277e90330a0595258bb492238728ee3d33d3a2971ed15aa8015aeb41eadccfd2a707a9a31e2177eedf298552b90f04156fd3bd0fb3a4ef7d6929f580306b341dc73cbec1061a408cf1c37cd1ad2f5fb4d68f5b8f297e2a054a1abc97b27b2927973da34ea0432034b54d9bcc3ae4e3cbb56585d9b4d65f1f0b6e9a7291a232ac33c8f510889a60737d4a51244a6d98524ca929c53f4ef79ee9784a4da94937ed610d7a9c5c3ae6c29a85d4c1df0198a51945655ca62d67ad6d78d630aecd98982f32675d6162608c4849aa29298a3d30738ff4a1344aeae0cfd1c9191454962cd24a8e1ca2b8f7cc4cab953bae183fc5d84a5b9f80144649e61812da9b818d12373842460e6e300411371041ec49d374f974031a6474d17a8b9a240d5ab1c31432a44c2103124ce12483298288d140cb74f934050e2b9882862a22a6b6b08192144ca4c0ca91142690010412d4604b0e36567ad8417f5d3e4981c39314519ea4c88013525fc840aa4a8a4aaae60587e79c416ab59546ca5aabee81c3a27dd51816c695c1f5bab85aabb43119a6abb6a5bf7a2d0b5c85368570656bb51564596badbdf7de04d84f4a2965ad55ca4eca18970c8c18af191a2b613a2af06996b6b6d66aeb586badd3565a6dbd2f6a635418564a995b6fad55ca186ed65aabb5d6c26469efb5b5d65aeb4d4ccee05eedd55aad4642cfcf99c5e10f5c8136d44820818405b3adb56a243e962765d7aab556cbc5d85aab46c25e7b6b95b11546b560adb7d65a6dad55d35aef9db54a296ba5a94145776d772d87f306659db5248da25f132e51a89a1a1ec24d2b46de3accb6a1c2014aa5863a51a79b17d541043a852a51251166c4a051a8386304285414abed6e6769ad26ba70707042a0ee69c4dcda4da03ac5668a060935503112708e4e8d9dd6b7bafb6af15a55abea975a45655ea1229d37fd819b16274a6b9497ea05fcb6d42d4935a9ca4e5555945a65eed0aa2896060d1a39356c805f0f0d7a01bfca5a85750bf8d9ea96a42a494955a2d02a5a55a34c9b4da706abc6c66aecac749849539466deb171a3d6a8280ed7249badda2acea555b656d52a5bab495347dd22aa26d96c944ea1d8661424241e1be084326d586a6755091d121c47a69424c1918b9860c4041957ab554441a064337cb3a42023fcb9a9e0433acd7a2eee000e9b9027d65657104feaac4fd46b33369a9576b5a3d90b2a5caf1742781c06bdce1a1d4d8f725d5399e78541f93d8cc320fce3e4611fe38c45c73d7e3b4e1edf9297cf28558f46a9d3ff26ada087247db2cb49fa749555449fcf1a42caaaf59056532c9bf4b9ef59e9f4c7950efafc90e67b36c8d9459ade0d7d7a3bf4f9439f770252e64991fef4fa7ceeca2267accc8476a24b29ba64c91bcb8905c58a624531bbccf59cb36785e2efbb48f49341a76cdb22d5d9cc161287fe04926850aa36d0240571c49ae23ac767aaa40efd981eb8aa9c333b5075ca67945c543ab3ebe19337578cf84153cfd1a5113fb8f528166889eba692dbef532034175fa2a2032fe8d60b5d27de14387a1a4734b3d05f717ee4fef198d4c5c3165a4a29a19aaed0b2494a293d20a5acf726d97fa9239f7316db6b2d37a70c0cbc0a81a68ea17a7d56174dbf42f5f9b94b3dedd39f96523a47db44adf8a41c5937e972c2b5d63ab26c9eadc75ca1ebbc4795f390ba2b085dbfda7d6b52c74ad1d65a4b29f049798f5c41dc5a6bad7452b124f1b592d2b7750acba66b6c6e6dcecfeb3247b7d4a4cffdf9148a4b1004c1a736e9537fcc330ea91ed9a79ced0bd7e485674e69cc9d4b1a73a7d40a073ab5a15324a963fb422569ae6e5287be4c171e524de790ea0cec92047a51eaf44b68d4290da1d83a45ba75fad47290d659656521c9e072cba938f3a55472bce26987f8a4ecb3cf266aaaaaf84246942aac9001a50aa94b4301292c5fac076b2de5b1d64eb90423a234fa94467913271298d426715d958602d38baf7ed0cb8ac45a1890b83ac72da9f02d2930b8ba8e8f489074efed38bb37bbdda30b86fb5fa5e1e5a37d0c03f7b31f012177b4f75d0b4abfffa3e33d541cda429ec81ef26405972ebbecdfece9e1a1624ff7230f0e2a52fa437e8642f385fc1c7aa197319fefb7127af64eea7cffcd3028bff744bb09d10b7dd06e12f443ff854f3323055a49ed5e6cdbb4f03f14db3c251dbe77a96cf446c5ef1e4709f93f59d1a587cf9f8b2a7da6cb272ea0baf717bc51970b90aeafdedbf757fb7211fa27b4a10d231a7a17de05de64e89fd0be896d365da7d717be167917a8f876d02f742fe34665e115fef5ebacc9a4bdc061ab97b4173e53b1fe0b5bc6023564a9a2f9af3ec67f39048ac1df15aaf0a4f397e10a371ffe0f8db167c76150f89cc382c2e7cf390c37fc72a162fd6818c455d163547c096d1891d0bff02ff02642ff84e6824175b542185f3dde525d548471ce83761e1ad52e3ac8a72e3ae01b8eaab349eb3293514df80a207a7decc457213e82529798825467978a2a2c2a7a175e8643887c919fe11016de859fe190205f84dfaa8a53df858d6b15a74e79576391bd847632f445ef44e889fcad530a4a4751b17afb3e6c8810212f69451b3664c89097b48f0387c2cbc5c3b61b1a55c71bd5ebffd48f036f54fdcb2583340cc317a272d6550cb5affe72a1b290d7bf515466f3f5439b0df37e76ef5bd8308f631a8e4271eabbbad0f88647fc012d8377ab6da54eecbf70684b9ad096b4cd63df3719c00fe14d88bcd00bd9432fc321f39bacf043bcc9d0d70af04e867e0aed5a01de09111624ad002f4320dc5b16e81379217bfe904d7f288606cceebdfe300c3f535152d98a7f4e54ac5a3af7e0a7c556d161bccc635b9d09d5d4d4d4fc931545f4ee3152f8f5f51a75ffdcffdf86efedd92595ad3c6fb5e279255b714f48d284fc0c87509a10de440234a10fb7b72f14bd5b58b83421fe53712a6c0a4db14dfa7cbd7b0c84a340ba10080e8906f0b02134e8e94b1a0e895678d890f9422fc49d10fd7c49fba977684b1a91b1eba31c71fa58b3c74e668f38bf6598ddb5475d80dd64d280fcc72da9f05f0689b6a40575bce75bbb6595d0ee3d98bf21900be47eebed4ec2bdfd6c8ba13de6476ceb3d541099224445d014215c8ab8212e5436ea4065e396a8222c45246948e8edd72522292a1b97823e89cac6a5217f7986a2a212958d4b42def21c65e3f1f57111405f1fdf5208c0d7c7fcebe3fdf5f17f7d2ce3ebe3292a1bc5eff1f571172a1b47781f545436fa7c9ef24265638ecf5da85845fbfaf84baffd1801e0c883d221ddafcf45a1b2f1392a5436cebee7eb68c2cb9a705429c1864211277c7deeb6e3eb7357747c7dce69f5f5392de1d7e7a0c0afcf6da1b2f1c54d51d968e36f7cdd799eafcf51d5f8fa9c179dafcf5551d9e8fafadc172a1b738dca46185f3f1b51d9988fa86c84bdcdd7cf50a86ca4f99aaf9f91a86c9cf9d8d7cf51a86c7c3d152a1b49f89caf9f81a0b211e7697cfddc4465a3083fe3ebe72a543686f0374d2c8d005fdfc7eeb1fb27c3183e3ee931ff9b1fe0eff60cf0e05e15e063db662fe15cf8bd9fc8fe1c444422cf03888f97c86af311e89f488e8ff1259a88185b190189bc8c03ec226ec494c7d5fb8c90021fff7700648cc097685de4f3464ccfc71138203e06e07d88e4c844c411fe89fcf391bf0c222fe38d9802c0c72522ef539483c88b453e39de8869043efea7c0f928e303f0db48d12a17c540ef79d1e64bc022be978a3e666455f4a01171843762f2f8c89fc7e5e37ea0ef762754c218fb1ebe240badde888926d4f9d8fd15f2ae8d10ed574225f4d57b2f747cf4fe0abd354037c48d98c69e97f12579b4791c387e0060adec71f0257ae88d982e1f7bdef225dcb53f8f832fc9343ee2c84200e8f92b742f1fc1b742d7be11d30f1f97841e00433884be04be44e328e1fe8f7d007cf70228e363cff7219b3c248bad86bc21af7b23a61e3e2e0dbdec059ba18fbdd0237b23a6151fbda7817cecbe7fb8c3206ec434f61875fe065f92836cde88098513f818aec01b413a7cf4f136413a27d8fcaa48c8c7d58341ff152024e2464c230a234fbeb1838fe0873abeef061f6f7c0d9e376202f9a883ebf135f892ec838f3572d00e1d0f0681e017047e6fc4d4838f4b41bf83a846d0eb10d5d07923261d7c047fc7f73a3e2c82c29f407423139d40b4e263ec43a255f8464c28f07189e84f287283e8798adce07923261b3eae1e053e861f4de4d0c520875a6b51cc30e70c6e98d3bbc00d2608f1add3cc1b6f62cb70e34fe811a4a202bdd0b7e12619d682d0d42fd7ef05ecd316557ad8e5d3164b4f7491c30e6ed0e1061e50fa50f03d0cfca4413e958f9be84ceb053c9d795ff74c576f17a138b588fec8d32907730e7ae4e9b5b35d77bb6b654837dd6312ba8174fb45515195663ead9df2625f86db2d0943a756b7badd2303ba8ee9f5e7519404d42f5235a14c7951ea6c377c5f18638c2fbe574e4d7ac48684caf26c925f6ab30a68829c2fe6479ed0e011206e482273994a17df7be57c65f1e2a95473d63d2f45245e40263d932039d271a1c1a9a63995ec6cea330817d36c928d33082abb1f44d63a499228febf8e28c1b1a0aad0f393820f2acb16df231587ce7cc4a2441e52694eca9bbfebba2e772f770c32cfa3398f66eda85f2654a4f49b50f0fb3698430e3b2f8a761de9a949a54759b544ead07791408ff368945acca3b9a4dbb1d78d14f3fba02f16f5fbd130a876eee751c8419e8bf49bf744a222bd1c0a7d024e2f03534a2ba7e530596953be932b39ac7216ece327b24e2c9df6ef9c734e7b61d2eefb44973761929ce83ee64dee37794293586259ef4bbee8abe2c849b32f2d975095561a50f025b7716cf41c5dbe0f593da82a89f32371260f79eb31189974f6e0e93124199374fab35b6aed8cd1449536457da9515f7aad950aa41fbc76611c729164a45fdaa5f505b47ec9e8f2698ba3feeaf2a98a8a867995a65091d63ae794f35edcdd2899e69b7491eeede674b5cc8e5b5a39bd405cd8911294258194321d30c6b5be72cd1547dffcb552ea1ee87d580f6aad557c69dd43ad55722bd80faea84b2ae60ae4596bd53ed45aab94574a7b6bad1573f395c7273d37618e9382a5608e9382a5608e9382a5608cb96b6deebc8fcb1ccefdefb3f46ac115e47256e294c070c5e5acc429ad585cce4a9c124b63d64ab7ee2a94360461625c32b953ca1cf8859f4ce7e58eab326977c48814932454b4daeca3e2d09fa220f6e2efe1e1c11873d7525d67f8a5e4308ecd4cbaf047fe642d032f2caa6670420b2f3615d002072ad0820b951631f880164ac8e0420b2c1998fd792cd0d55aab0d5556600864271410b4e8741ef1d490a5d65aabb7fb08028106597ce9f4bb2b2a8c3ec6bc3461a5054659246591031b2cc001171dec6002287ae0850d515fab073d7e9de11a9468f3e707a9b3a7cf5a8d368a7d7c2f1ce8f42d8d396207bd7e7d9a3f206207a8664a29a5f2053f4851c1e40310566044a35f3aa5309d524a29a55b6237bdbe1c73b080a2d7267aa512a207343cf5200a219e7cb045af5fc32d5824f5faf5a9d3ff931faac0814b0e5774e8810b5aeda25729adefb25adafc41a76f5744bc68950d5039bd806a6a063360f10414aada155540c1022a8a3ceaced13c6533c595256caa74accba72b543528b72344f08315c040bfba7c3a62895815475451938123ba4802064850bc76974f5880609a33533a73753ad07494407ffc309ef41c42ce588f6d7ad26ccacf44168b5fac33169f4797cb8a7324f33ef4fee3f6e43c5bd2c02d69af8f83dcce38dcf3a8cea6d8bdcdf36d968cb1c57e37b88d264ddac00fce12f0676a027ecda0ef54d43cca1c1715a1b3b9da9f8c9138f8c10d9338f8e70ea1739b46af5b86d93da9833fcf1dfc9fb411823481fb1c8a833f9a4345cc7134497a3ec779268d153dcea328dca3e01d4da423d247569cfa1faceb8e261295cd747c85ce641254d88a2ae2389b3ac6f825ee3297452ac338ac50b70b74fd31eb26cf0acf7cc5d4f4c8faa80fc723f74bd7ca43651247776225c759fcd5bd94ff5adbf5f3b0a40f169befef260dd37aeff1f6fec31f66b1581eeb5b5e4b8af72ccdda7abb90d02f2adeef46497b8f7f9c50f07e918af7c30fe8d57b7f04c9d7c5d6f1b75aacff58fc07cd7a182ab2f808f6d6687b8b8f31afffa13304b295d0d2c7f5d3f534fcc13de62eee8710e87b560af45399d74d59571dc6a3cffe43c13dce0e7ee0ffd4ef827877add66be9237b8bebd67e8d40b77ed4bd25adf4091f7f320cc2df7ad686015b16ccb3382c88f5def770f13166c4ff80d959af6b6bb3feb5fafeebe49c751572706f4fc3f017e9dc86611ea4a8cb4fbbe80c09c5b97febccd2303f42c5cbd3af9e9e9197a41e42972ff801959e5b20a099a2614002d68781fbc961e0fe52259dfb8aedd8753ee60a83fdfbbcceb9333ae9f5e5dc19232083943af549af3f763d3fe6fee356af5930cd79d0aec3d73d3332857924158b92435f87acad821620289e3aa60ed9c11e86e1cbcd9574c8c3672dc1bcddb055ddb015e7d1bfc9b296c567af2ba017d75494303ad0e07f307cf5df86b17e76d67f1bc67275a1bfd0065d1ffc51f7186a0cf58b2fbafd3ecb3aeb4ab64ea0412e3b3bf2c02fb9fc5e127a941daff62737f153fcb01ff361964b9aed32515e4b853cf8e40dedb22e71f3e5bb6237a95ea429f42c7f85cdbf7d520965e98cd2e47f34e471bb05b1ad53fa987fbe9db7c9222541822e6f49f4d0c7aee33e1d20672e7944fad8f49c4e5f89f4997ccca9a1d31b0b743ac6be497da840c56f81407f9f414024496cd0f9b95d548406fc717ed1dc46ba9f1dff995f848ad48891d14851119e3a6652877ede78eecc9f61d8c4cd3a7dda43651864e7cefcccf1dce9f2a5dfe8f2a98b537e1f54a441a86cf22054443277e81f9105e124d09e7704c9d4213bfd514997bb4302f6ef3f6f2f85ffed5781cec20f77118a436df75167b68ba2919af076ce1f8d921677ac303f052a9b1c3481e67ecc3d3f3f3d3d477e7e7e7a7e6639cb6c2167ceac2b9060146857922463922347b86b593cb090eebd5f9fe64f8a765db121aa4b27babcb2011488935fa12db5b61e759d940a4d3cb0a18f720a890755fa28bd40d94ac516e8711ec9db7c2ace2bf4f8485a7cf266a418638ee3400eeb3cefcb9734f943b897efe40e999f3f7327f227875dce632aa145ccda3c6292848a98f3a21a2448c5a90f8343ab46a1c7dc6f95562bbe982ba8a575ce0f9f68a874ec986a7a5422443402000000c315000020100e88044271388e645108d17b14000f7c9040705419cac24912c4208a821432c6180200000818205343334312042af0a1262a12fb6497af725470e5d9185a1eb61a02e46a01e8cfa01fb0e90d0470ef7f7336fdcbcb7f24aa596e3950070100d3f5d70685865e660afe0109cbd5eb10885366611685226fbc3b1ca0224fb9a6ab2f850dbd3c8d1a57d786fe525b750118379c008f87f05499eccf1b40318da3a8410a2494836b708875e2c3e65a88a51c6c801f61f21f68a1d35dcbee2f0a80a1a7070e994bc5c128d776026d670609a29e13a37ffd1320f89c0706ff57c8431e53199490cc204d0b00eee3cabcf304c84ca0b32fd1f484db498c42b1454f5da3fbd0ffeb64337769f14eee1aa25940db0892ae49a9c63c0b47cca6964ba9c723b30153aad247e6103c400b702f58eedec60b3eb72f3b0bc96ed017d2cec7748fa043c376a477eb495349c5a6b256f2140f2a14ca447b48bdc2d73cb24a449ace270575d984d201b14e5bb45594e50b3fadc144ef42f4c9288c89de6393700a5355d49532dceedf92cb7203f515ce89ff321852e8e0ea7e91fad10f0a729d38f929ee1c0b8c160c112fec9bb9fd3ca8cf35d3a76880166731852f5313e406aed98b2b0f82c05afc585b5cdf408e8e2a1a87239e1714787fd4d6c7a42da8ac5bee122d6583d30e7f04bfaf8e8a023fe3c62a85bf8be9698ffee5946349ad90cc180ce43be28b7a6dd4604c2fcd2aa05b3d9a8cacc3a7d4a8310479f970c8a42c7d9581ce6a44129bbf5c9d9b520cb23e13ca346c61f470becf842b5f80304930d889fbabf8b139cb0be8c7c1913188d002c1223c75ea1c9cfacd6df3c035ab9cf1abdb8c15dcf79a9e546674314c0175a957f2571848754de8808d5f646946d38627b9750d08423f29e198ac6a56271d205db33d72c7fbd151b9c73260d4081b2850d89f853cda0bf22ce065f242d92dadd97adbe7dcba6e22b54b8f41f9f8326e2cdcc5a92fbef6df11bca25061d4cad102c704b43094d707f93c6c4f6c7391046e286e15870a19d1eb2c900d00e23e426024051f00af9a104a20e5d79f9760b728cd1d5385d0fb0c49146a2beb114b736b9e27b0996da5c090d91cfb7b37db361b424033757afda4df4788b801e33f512111aa3c7a3af1cac5daaef5dcc3857448a4b58c9e1ee502a7c17e4b8100fb427e2206603c9c391f8e6974358bb3301ce58b21046c3922b7233719afe85789a778a4610bae94476585320b15877192fb33db606b429600b0583f5215f53b4b4bc85919df3784964e2fe6f113fa5ca94068fc17b30a5d95c2d4a5764acc29c87a4a0847fdc59783a5fd08d0a06ff3b523143c733144d094ed92046f76a900db2ea04b3ed9d086648d6b1ab092232eac0cd6c8d3365c1127486cf73af4d2c645209dac4d37e377c26ec0a2f5ad16fc0e22aa92c87d519f96d59b905d17992f57533284367aa5b50d6439b9e91c4302beb447a4f529b1045b506478edd5afc9d3f0e63385348ed1a75a302c8a07bd8e6447eceadc5556b7b3bbb9cb4d6190d0ad4a913b3dcb1121dc12cca1efb41a2a1417af324f38d7ff78cd1855e4403af4f9641e004d2b285a3e4e38daeda64a1398e202c02a9ae4565a6ae2c37d7ab82b85aa80e9443a05c8f77147cf29fdf53020124d7071c0848cabe11a27340a2d57391f48b6c6d578a0092d1ab99b74530ea8901911dec678b7c4f850d02120dc5efbf18fde33fe8e7ecf1e90875ff365a70a6a332aafe9c252a761e169734f0cb94b14a4d57b7b70240aa212b8e91c51d0497b3e9d3ba0ca199267641d8a20412c9a8d63948e82a2c45d9eef02a2121a02be591f310fc9c5f2d5bc88251c9da0c175b7a25637fa26b29d1ec7375cc64c5167aeb05746fb54262be22db84fe2c17fd4ed58772ad7147cd763965b62253ec014484462f26b931f7513d1e0b5d14d0c1263e0c8093e96747a679b585dec64fdb72b8445c2fa40e9776ceca693c8e9e37bb6b38da6f775ff1d4d92643038ebb675c86578aa4d1c9006ff215a53fd5571ccd2a1792bf0c5a501546f4be6d325002c56780dbb0309ff47e09daefc730ef66cac1f36b1bbf6a86fa739a42d4f5c3cf344c0cf22631e0c8a020b76bdade0c2c92ab895a179e69f1920fb063105503a1f2f5dc1c705ae424127426201ca259ccc8d7fdc68b1807e0afe31a9b1806ec207b0d1887cb1e280e7a5a86f8a5b3cb42f9fc8bd925314c7b37c9f84f79b1adfc739fd5bdc09ab66c3ac1509c9a45f7778b8991935fb2a3501da98e63ea665e6df0fbe27f72339e63ed254a72602aeac252c9cbe5c03e5a69e43ce0d7f1e8af5a5ad7c4a374c9a7643fb1f97aa354a67cd199554a0e45b187668830ad869f3cf8e212ecab0c611dc91af265a256783593e0b7cfb0879de8e8ccb28e3c7c1d7ec90a00423a1ef1a5d6347e2d67cf5cc96cd34f805a2c7152ad168fe8bf316d0e05062cbd45592689e1da1b52b491fdf1a13d57a3e197990b5efe6dfcb560437e5b3542cab7e902f523f7857650cf4c6b0e16b60199f4384974541ce9daf1327ce8b088af9c16f53d9ecf3646b219c3c0cb17b8ebe00fadbdbb4e986fb28fa3fc599c8fddf4268f132aa4f7d64e11a3b41e98256f2039edb65374b4ecc0f7221307c6a853ea143d3ca020a610258b39bd3dfb92a631f80bd00ca6e663a26d771c35895f40b4659c4823aeba29377cfa763fbc1d7019daaf8b93f79486a87a6c59d9d3a5b80d8fff58e25618fdc36f00895e341e0f89fc7e839dafe60d739c7affa6014a085b3fffb04f4b8da0f5b20267f5142b5ae2f0c92ad584e006a101b60c146046f96ed79e961f0106a04514f00612597ee300bf2430e0e0868fa8687eb6bdc47ec9e2f3f5a8f518042b1f7e7bbba7f6c949cfa100e49355bccc868fe71c0f1e80307cd81dd56f157315fe6e8b9f52a9e41f833612221cbf8f17c6fadda3261e5d07cbc01fb312c771eef811bbea7048a518fee7bfde6f2cfbd1e5761ccf4faaf392cb4fbe0ed17319707d933a6e4966b3081493ade0c822ce9a1f22cd92b45572eafb74abde7d1c7d232a549e483599c75410b9afdc431a0e95b2741499c1df2f7e1a4cbabd07081f9e1706951b9eab497d37c8d4c52bb31e4e739bc5bac084cc4a0c242b917527dca4409f8799a51bf2d7528e237cfbd327bb12a8a73bcf10594c78b0f01a296668cc3b0a7b932e3bac945e91e524a0b65c6e7152c58d4ad9e7453e85728175bf6e3928adb79f4abf69e00d9f95f04ac3e999dcb961217e7e844b5b08cd4eb298d511811dd0f10378f80d8c17e72109744121fb5705370238c38dc918a60fa0c4b68bb8e680b4b867823b74b486ba3939ba663f00bbc2508be3dd2cb348205a2f38bdc28b5ab24917f7fc3095add02775d302dda9c1dfa15336d1c7f4cb4f7c9d8b88cb85ab9a4684787cede17d926da7b7ac16aa6b2e813fff233c6781570a49ff02b09af111dc97bf5a1bcc69f1ac7a522ed21a76448632f93ef1e27001062b77b800f765837ee371bfeb1cd70548777e9c283ad5d5b5c5bc28eb1bdd60681b79daceef3a5c3f5b6db8da49592ebee77b3a0891e2b0e5e3e66b53e73e3e88b6aa38a12deabc7393a24363cbe00e54e9afa90232614c2a2257312d2ecee41f140b4d66c3a72f91228c10efa08c3826fc6b9e873ff6ea9316cc00cbd808320fabc51215a4ca59e735abbaac028e3e3609c9a2262ff92b4997dcd103327c90d0f9309f3fac783dfbe3eee0dbca1c3886b088bca75694cdd597be69b98f9011237a8594914ca47a37387de65906f12335dbbcc322feb14163a3d7acbd0d58e7684b71557345f3ef8a03d3ef249e57b35ecb2edfb1070e956441bb3b01c4940d2a9aa5c3fca80471ce3dc50f9e2735cabeef1901038484b0c9281781406f81e65f7037962691ee7038568da1bb98f83ed7528119da91b0502c83b1393d1af165a00604720807b446f8062488e4b6f3c7abdb498d7fb9f9f3daf9a71afab09e4f9f25ea81de07fbb5a77f0ebb92cef59eb6e709a3af46738bc8637b78fc75cac372f3883425781b3e77cca1ff4d567a503c5c1c4b7a5e98a41fb92b8719892b403e4f9731fd67e3c83b34bf5f0858e8695b36caf243f09242a86d64662f738c4e0aa1cb5ea38734f12946df7e6ef2b86f3a2c8365c4c09aad3ed5e1d54f6f51c8a15d6fa342d723141d62a699f83d2729942b092ae7c18af13dba40ab382079f8c5c8f5fbc993550a8725abae055a8bd07e3b29f7f031d21dba2ba45e8e07411048ef11d64cb9ff7ade4d0cd5f630f689140ddb79267bd741dbd2f244d15cfb6b0afb476a830ead6d46a7957cc0c2f11c50f159ba9d147ee84d149af63133e8ea1ce84ce9e5661c6a3f7637a862db7125207662cd23721fcdf3e58677cb0c5120acfaac3012129fc404ed82b002370751ece72941dbee95e298e95e007a5bfacc2cac6e3dfe91880e22a7646b08c38bef8142763eb6719135c9b5d2ced50b83ef05a7d30c7744dc140cadb6a3b82c26717399498a061c60ff96580209b76a7ee1b4da6efe801f3800d31668c9ebb37a9ba76e63bdc17f632cefaf8263566e7dc98498fd1ee674df485c72647bf963d60322ec9bf34c994e608f8032b1176f3770c26696f772d56be28a74b1e30106832f5b3438477a1496ee1a7f066b35a2c03d6e63a4c475ca494412a8a95faffcfddc6084b7ddb16e820027c337a2dd3825c40deb43f2668cd69930eca1718dc92efcf9fda1791def389dd3029973351e92de808ba514d6a9094f349a6ac2a970514c676fb3084d491cc45bb95a6e1a0c15264142476883204f6d3b9456a30a386e2e9c68f321f50357ce54cfd9aa8a30689ed4296cbda1efc44b1237455af5b6f32c3a12094eb06d9e2cfb97a0518529e89b769570f7224add5194cf0cce3ab66a3f11692e9ba593ab7488c8c7a3710f746734202ebd86250f58a42192d2d30067451eaba90f7503801c784fe6e3f07a6d5e6eb58130cca2182e77a65a41be8681c2c9f09d17e9114e820c75b74b022bf536c30552b3cb79298da81e27b194398b46f4bd9aef3bcee77e7cca7d044f417216174bf42b0e47a5aa92b48902e6e9d5c8ae2a85919568e28e104f66aea1d3e0e0440222ca651432866e033ebea0e1af90bf128c97cf045ff57ae7f8192c0189978c561d452091a0f111480688c311ec2dfa2d790ec3ed0bd0b3fce2f2c93d96a963255ed54313d37f486f7bf6be3e84382f157981610e07e827048863d028788178baf150b4182593117d7986a7db0f222805aaa775932805c8cfbe685802776053ffcfad892a6f157a84a7a5d9d87da08f8a81d351e523c9acc996441c1d708dc218cd5d944e0b05c5d77ae0d790f66e51556c8ce2141ea6a1d11c0eeb29efe4b7ce1aa4f238ece789f473c7ea424f4428bc9f1946f6524485543cc4d645d3452374151c87513f21235c63bef1c3d6f3764bffac6c3ba045a5ad5824ff69a478d6067896d9ce8c2ccf096c376ba99f374f48a5c5016c85afbb239baed006c00b478b8afcf1a376e7bb1da20320577e9b5d94305ea10be9c124196c66c964aae95ce51d343d0e6c20036b03a939f161ff70d5561901acf81578a3bde143ce9223c0785b5046a3233647e2cc45fac8d7684ec9fe2cc4a3fab0dd5efce575c77e56c47bcc142f9e45bad683364dce83a110595f68d1605a4fd594e3764d85489c360603f1e91c2ab0222561671a8550620ff9504e8cfa813abbb1088260a4f83c55bdccb191431424b21c146f18e5e81672080474ae987adc2f485d16bf4de53d10ec00ad3a5f7c52d9370d6d8b23fbbc3c0f43505d42c470a27e37f6476e60687dac9a3f7eef53fa086f172d76734c79bc305fec14a55d09167a25873c676582d24c906d741e2f04c02557646b4f31dc54d8d9924d0991638d890800323d3ee5f2fb5dbf1aea94e340f07584e0f9ca50753f95312771f8a07a23c4ef26f807e42f90d78bf47bc2f30f4e49f17873deee999698f77ad48cbc2a5d313d12c6580cfdb71bd04573d9c0d6bced810e28024d7f60b3635194aee7a77f94a5805393039da139009c8c5e3b409bce5e4e94f86ee76b3eb557af54e5eb60507e01d78baecf2f902fc6a2b71e0bed3b064de28fb1efd30d6ae1f3b0fb84f18302d74ccb4f1855fa1667f501ed04d049cfbd4862850e082addd7ae2423418aafaee42ab30c07f089e70a332fea6a9ee94af330b079177c115f199cd6ad32c1c85c08c9607a00093def4f521d31ec69a9093be5a37291b1a4a98962c12042603a1a713c2475ab2202ca0009f69f3d16a1319f3e58c77fb98656de0fc9c2e18ff5d327e97212d756a2405944a87f93907048d18f15db062f1131a1a69fbcfa65ec23a909dadd0fecf619b0bb018f3318371da8f8e202366e8b67ec70faae942690f0119ddd241d670f57a7040b91ea1b86653d03106905d91e247d9673b60dc95d497c29ab74659880105891dec3fee5e9cbdbd209d5f81c89e7522a14d6a16c2b4e3a91f0d01999832b7924969d91ae46957d7eda25cb0da11644b45b0b09df5c0be7f3ddc474c2a7b120f7ddf94dec093fa87ce23ffa45c7b5a9391b511223f0573572828ee9d388559ddece78d7c6a96a991fe9f5be165b3df9942fd79936965b57fdbafa2ce65e16bec33d4781ea7a69ba7105b6f45eddc23778cc2567a167dddd1921027712835e74d09199be85a3a96cc0d7029a31f6fc6f7828a3e5d8496eea512c6fdec2da5b755d371ad76508e19165c36ac0b82c3d550acf9b4825b7c4bfd784964d4e1e2f3a908e087b4366accba11eb8baa916554b1e862c389da88a2f353645d0f99a7a149f2ee7b24af2137740fbc81a8a2dfb9f9b87a2a135f4bc6a9f74a9200b7a3a5350cfdd8f584b5b2a0c1c0243fbc45e9a092b0dc985876633374ac368a0137b50053d859c5fa5c6b7540e87056647d580c798bbbae7ea07821b453c694e82729d133bbb7b0da9da5a0b679950cbdf8431c4e162a55c7f3a45f7ea3b58a1d639b3826d11796ac41d7c83584f25fdcaa2af933c4cb00aab2e682b74c6fd8d5ce914416740442aecaf09184d1db1f10a1715c849a52ad08c1f4be9e0dda5529c66c01211c5f7bc1c9cc3776974f1b81a2901bd4f6bf94ed2ca17e0103d1ed5610185b40b5f49ceddc569e0af6736fa550f6b643b6be79a78e208a309aa34072a1056dbde9393c2ae3adda0899e78e983bb3ee7694b15c404ca77adfde709daa5f0e5dd0e003cfc9dca1229c72cbb11647d1f9c84abc77625a32951143a27e28a796f1489df776dbc8bf23dd8544d9a320f4d2dd62ac6e9df6568476371e85a7e55345ca0371e187172afd1c091a71325ae72ff9e0f2d87417b86f637d423cb1004be6016bf860babdf3990648c04914d0842ff7c6eac7c2b7338d30734225cda8852f20f58e03cae39b982e98255082e75ba3312341d41360ef56b92413f792b164d3a1770622934a26c8e1723e98045a5e0f0cc2ccc41d5c9d69693d126a823f54beea87db18273cc2819774522a4dc150852c6b855eb5212cae530d0999c18a5131925c2f54426f1bcf60baa99a4e93d020cc55921e2651ced4f7b3fb783492cd25f2ca47ab199288cfc17d7131367d64ba03023f13e978cec380ff087eaade0279538d91afc5a7acbc0eb57c08e135d4d66a9f32da24353c5443f7a5b2b3d32be6db96f053e34014cde144a71aa4d5b02b20bfcf14a409151fb89a21d953633582c1c617f09393f5a6b22a60c64b360b28fdd78997fdee6355e841ee518ec9245efb6715b8070596982ae0a6ac1650607a00b12a20af58027e2b993e4b12801b13aa6151d4251e2772cb3ba0445c1709282221ff01bcc93a47fff2fbd25c00b04e210bc2965f5864a146a757ed91bf28f21e06affc3a03c60e276ac3a79da44efb1ec94cf5e52bf6202707d46220dcebb816401366abe92ebdbb21048ccfcd210f4f073e3c6e189fe521a1fe4f5e13a3e0696df30ff6ab01324aa58a7072026d3662cb5615ed732b9225b18dbabe52fdd2a3ccd880ba97b51b53b1049eadbf93e186a5ec19f7a77dac125f31d6f6f975384c38159babd998ad81e183f3bf0e4327f0f670244e0534bed9cc049b93d5e9345e8389fa87b488c00ceee5b994007cf0374e5a1206a8ecc6aab0c677c69ce3d9420838e06ba74b536e90df4a6b4cf3a74c2844bb6707b1a46595e89d9913d1a8b1591777d5939a944c9d35a51e116e4dc652578ea5613a3d56c1b8b5ca58dfdadb64ac92bac91043004f73875668c111a69b7e13ba16ca79437cecc280ce663e23549b5a373dda7633a84afbc94483ac5b632a1fd7bbfad81a7a824ce57a2c5f930d28dd5d128a3eba35a510e0ba16cf3cf6681820e761a9eda484b4b0cd68b295a399b4b23ef593cd710ce363410915e8e1d908b8004221b2b941248bc4709ee0f9a84357ccb022906aa64e32b1b274ab53a61661ad0d706ea2b329b06641ff4d7c2a59075cb081e6bfd79b146039039f9166d981c8cd160cbb00ee0a9c6bc79be670dce06a4a90bfe2cc70b88a3ec73b44d30d5029a17755503c0ad221a1ba10d1803c89da4ee8a354b94dcd0cd0c7a80630a9dbd61e96bb7b0bee217cf62482b9384657f6f35de5bb848f5c985cdff94eaf285bf638e148c2991ee2b07af2305a580b15c816a7f6c42a85085243ba2ef503315581bdf4ac195957c09bd27a1b0a52abaf61ec4d77d4a2428056377927be78ad1ad9df219d340df2d6cdf8cea2f6bc210fbd108fe5a0eae6ae7e0c8014dc146faf5fc8acb102b96fe0a29386ff3030f5e4b747db4f70ac6f6da779e5142048ec227f67310e7cf661c5ce1800dc76d6f9d8274ba787fd6920f68bba706b24afd754ba5e784c759d4a2c8c90e85a5866901e4d050ae6c66e91004f635be94a7c650b7fcca6f47e2fa2726608f7c52398ab47a9cf06ace126a2475e81ebf9ce0f3d2a780878813d34b2f0494c33802a529871451ffa55ad4229cdab7b06b8655b73e2fe873b6026fd3310892e7583c4a3dbe5f9c61810234bca70fa6d0cb809b99eae5074d828a0726cce2675d47e081b6ed59effbac43abe735f224175ff9da91fae73dfe946cdfef4a459802b744d8a20dc3b04fa3fa98feed179cef58c83050ddd1bbfcbecf4e6aa28b2cb8a944592296b2dfd7d490d6d28a2c311eded8b64de57438c06abffaa9ae48f767e9dce942a9579e844bd51adae441788199934f887f9fd646233111f12d529f842f4540b9244ea4fb09559d845e05c3dcc8f9c69e81c792218ddda7acde6f47a549522175f8f5c444caa4a6cc68edb40fc362a34a508bd42c1671bba8f76a74a78e7a751a6d93239ad9c0a8db1be7f766d99c6e028e8cf9ec99bcb725a520649d7f87b32a125343af25502d1a2790b0379e78f624c9f2815a0af17f5cbc19043da0e0686140f116da8a74ae23ade2f5544a5ddcca893ba0c8fe7d94cb083128c68962fef707d609f158c5e5f0d1cba3442c5c31cdd489799d43f4507458d3e8d8d9d25044d229dae1648b7e24e133ee729aa2233bc13e53a17b4b416572c4a269f188fb0186cfb629446bdcd1bb4d5c25e92bb833c2f535e3eabc82bb54c3ffc8d7918f501b984004582df1594ce3a7d8944cfd320803d0ef5626110a06fa9d0c0d412ed24926f82a60025da1b2443682396253780f86a6efccb8c8d6260d3d0a937c6841f69d94e401ee68ca2f600b8e3833c86c0ec5407ccda19b8ec0080155a95d1680295ca47dd0f225ffa62c21cedf87c44222a2c387a03960f85e11e41d3ed5973757c5787c3f61f82ec9a79aae6d2d9b41911d569fd6ecc2caea79709585cf44b9a67263c067babcaa18e40bb3f909667ef3ad96e4e3a5bfc295ddba11eaf0e6cd7e067cf63c62a846fb12a95fe6e5521553f24ade8860bc1646358ccc345405c10a83f28420e7641694e752fb2924d9b4fcb16ef3447a740e462c39e17ede09d381b9accaf9ac192f3296da86a1db7fad4139dd41d8d97522c1ac328ef1f88e9e18555a8838adff08d89e8681c1e54fbc750c0e950a0a1724be114d7834c31047399ea5f2e5b306ce720ed6ba2be4eeb393e4fc7facb2558c317ed25c4d7f68e121927bdc2c723192875d416be4b373734fd463a122e3d32d1d4c3f6498bb2003f2a28c5ee4d9799ab1782c56bd81eed589c89a7f03d002613c8a9f50d7412434d08c200ebdaf5df1059786e493c281d0d9c9197949cd29d7d12d1daeec160ed3c171fed5b92acb586ea06955bb666ca7aa0f4178d7607b7a61f557e567efa5fbe04d933bb8fe7ea1e62b4a3f79a32faced7026b0477b818084af97ba082d94bd867f407efc0838887e5fbe78df84f440d79175187dd8eb6f759abdb633a4d0f4a91229914fc3c4ed16fac1cf2244f43e5e91aabfb78e69fc56c865c91cf2ac2e0a3dc6fdcd10b464b87b2d772aa5308df4b94d07bb50aadba8fe8b7fc78b2dcfc537334d1846eae0e6f6e39a987da076bc145012b7942812fadde6d509848b8387d0b0ccdbdb594c3f0ed122f6c61e86272679df6d6f7c25ffe4799f158e1cde0c862996235cc338f40ecf0be683373321705679c846bc4a2a875da499f8c9ca57f4afb89d5a89343aabb4805d24c72e510453f2f7ed3ffd64468d03b83ecafcbee0298fce55215ca0fc1eb01d3c754613c8b06541e49dbb18e6aa15fa98b215284278c5be07b753f266c1670bef8a47b464ad0a6e3cb14e12d96652213976802a852b0ec2ae7cbe3950a12e03907503e3d732e4834c5e13044dc4f309891a919f94ff825085b03f57358d423a6ff7325cdf1360aa60186baf2abb0c14e3bd57f875d16b50dad1adba30fd40a8293873ce29ecc7450d28e5634b935745ea59421389c0966ea21e8ad401cf03a48ce76c429faacb1b0f1b1b6a3edce9d9783951510d0037fec728f6d4a667cc8aa8db31bb6fc2e747f2d6e2a95b513c4fa86b0789eb830fb7ff30d54681e5cb04e694eace024e18d69e31b0d1872bcad68f8b41d387c3644a9dd8c5de433521bdf40739f591a895513d1e2c9447d1165e28c62afedc414b206bc12eb635fa7f0a8148d4fa1d06be64ab88f4b06b7ce0abec27738d30dfea8682002548584dcdad8869a4400f053249de6e844054e3c9f914b115bf2831bae4a44c09f87ed9d492e561f0aca33a15c4d629a0fa41e4616ee5724d1d0061e06b3ee3afd025574ffd11bd7b1307221d0c2409d12a3751b4abb41bd6bf8196b6e5c9f42c80055b7737b0b7de2a74b660af66fe592fcf01cb322ca6fefb0511d280e7e3d6347a44caa3c334a849b822bbfbb60ce6aa328fac02daba5a2dc0fb284095115549bf468e8769b570bdf0d43ddffc3ddbb05d2cf24b0a1d5b5765aa4525444e672e17c64d616bd9d8efae34bd407a405247ca2501492bc53b84058b875d9074b76870318ff936e4953764b738ed4554e16ec35aea1fe09880fd8100e2f824743ce8d86208142e06355a3fcc2e0f4479d7d936aecc8394aeac5864cb88650b2f5ba1041bef504182ea96594a792a8582e04248934bbd16082716ac9d039689e39574886e87f4744addfaf0ad3046a880828a46e42ebd7829568243b88ac75ae70c10d39815551aed74ef3350d62171deefc0d4367ca33173a9976cb7ef15647c8b023106847d8cbf8f645790bedeff1cbe6617b4ec0c0a2bbc736a35773a176193e306daf09e78603567a4ee5c319fe87123e8aee5346b31838677022437e50c48813968f416d52404aaad52b81e0e2afcdb811face271db0a2bb18d821a191e95d20b418c8a4221860947f399377ebe2c2f4e6c316fa2dc503b61c2f382421bd273356cc4523803a0c29e7976de3b0d5bc18a25f727b2f88a89de19494c11cb1dd241d343b64ca338816ee7fe8a8700077a8db4b6cdc4c01e6fd3353a3c470cb9865c11da74928b1a06184b4d2da8d8be651278e22a8b369eaf5e1d38bc9fef45d16dd324a8a58d52cb466d8c55802427bc003540478e727939ef0fce2e3a25bcdd06849919929385b50e558b41f5d2bc73d9f0ec3c692e4edb1bc5d382858d242f5258cd676ebbb36d801796dca8d36b75de0c0b661076adddc075f9c9468878daaa9574dbfc838a401ac7f6e618ddf03323745e68ec73343eef68e90bdfedfd2b06a2f105f0ed9d9306f1f2ab9389f9e29eacae056a7246a099a6f71b9e30bf4108727644bb44a8315e019dfd8570f92614157a6a03ebea0d649380d94678cdf2192089822b26c3d09446747d3f262b3d4e65892df2ce1d01525de31639d7db0ee221029c120b47e36d3d10ec4a99d5b17f3e543cce4acb54090740d0a8d5d79bc4cd5b49f853c4727dc4a734e466d4764d88e68a347b4b5695136505ae40b20124bfe8910b5394aa66b6482f86fd2ee3fe2f7b19aab8d5a58549a1c6162d8910729c3ab8a0f3018ba061831af82484709e0f932234718239842a085325fbdf14a0a80bdf749ee8a34ffb06696362b5ad1feeb378f2d84f3f6e1ffd8685e2073846c639d333c9fca1ff91ccf84ca0614b0afea1558d214e07296c2a2325ec91ae3b2e0fad4af9733900206104c022fc78d989404fac17c54fc2b5d36947e29038f6e511a4aaa7a2c0356e3a6008fec8a561f4297596f712fb54ac94b7a216bb956086cb9731f87d23efcbd40ca1e04fa6a637fc550a4910d1e2fb1fb218a332425812cb3c8a5515b3418770a04473cc1dc16c38df2bc94e7f2e78ba8017983db6db417142a48c7c27c0a5f82ac34a5190e69a76af47e0d54dfaf46ef7a71d144219e14255551b3552e0b83b310ae59877f2a8fc1348a9ddf165ddf2063b726a8b9a73299e400db166cf32e2d2e12a7a57973f2addef80b5d3225ea0a75b26e5b103353d54505a881a312a264d0cc36f4d8eb6d0c90ba8a91cc7e335748db4450d350a46f2544cad33078c45c73934b5a6973d6307b239e6fd36cb7d9c55846a5ff2051d79bf3922dfb26c44a43a4105b3aed619a6fcbad0c84d65c806d0be5ed7bb90d29d0458e026cf25d0e08b6205584fca0db106b50ea4015fa811240ef7d3778c7c5f3b3f7074405e258827554ae388558060b0f92590bcf6cacd693fab6a382dcf085f7b19d52694af26506a64d63e9bf815bb4a07198a9b8a3944d2a2307d4dc8e425699216d99355376982fdc1e316ebbaf422d8baa359d0bb775d56d5f520e8241dae140176972b28486e041f708ca0cdb0aaa3405125b8374b62e5d073152d63e73e82f014de5825cb2e618ac4051e662a0dbd5cc5bce702c3105593337bd4d13671a54f194349560552c85655adc34b9261b7ea3052b63100c0773b738fa42e8a9a61e9ac4db9b4e187c081fdd949ae1834f62d54cfadceac2df2aa70476bf095c907c4e7175f8d65d1513a96a428db70c4097dfa950941b9d3a338d423ee07fba031842b9cae426926b5562d72221ea0ec30da23f08ee83fe45a686b8ada3e0ced43dfe0901d2d16be082d0327226c8bc09febe9551f98ec7d091a06584484dcca2b8c44ad78b53d25d9c11d8f8e2e6c802942aee57c16de3e42c47ab24f4883a26ec214bc35f44fb32e570f4f706151303c3dc51b45433854d82db5a382c2dde05afab9db33182b0d84c342dc2ea9798427d34ef24f4228d79b2e1f3a2abbb81efe530bd934f562f118c842166d9bc9bb5acfa2998c30369f3a0ab07a29d40f3d059b6b863ab70b61cef7cd98dca1523d79ddc4ee6aaab302de008908ab0f42aad165ea81cfb43f7aba00850dbd8b2d1899ac310dbcd070d2181996f64a3b321183f95b2c862befe5e6aaf96f401f5b4835e30196a93522bc9dbb9aa084d4bf782d5f26648d6ffce53983599fa61394b24f06869a92777b08200a966f249243c23adc70104f26f6d54b3d364871185ab181a12e6fd43082ad0d89e24d790f7b417199d71695b310aaa7a13e67c7382415f93c17f5bc4c081f49dfb3a022fa52f233470105ef91aaae4007d2d65c42c79ccc79279c51411a8cc24934e9563bb6032a68401791299c9d8334581e5a1d1df0520e0301dd7bce819379249ee60b05604b9b20552af0271d4461c03ef46c6216b69ae9ef8aacaa009d2065ebc96397527947c1b9d8293ad4c35b22a78d552f8f7be200c04069e81761609cdcec4b03179795ab372d6aec0189887cf0100b17159e689dda9416ecaeb2d145fbeb125068c2024ce15ec9917a7ed88e4eaf89f021b4e42287e92d45d00ab3b166249396f8220e75c75473f1884ef818e6e9dba02303212e6daeacd5e459392aada4939d51345761e6827868b8ba1453c4c2e0cd593417ade4113195bb575dcae6363f2f1633086f64cfa498a86c21ef571a440322d319ab4805189cdb532071c601d9fbfc8e9ba86f4a1c9efad2f81a411f09e4a3e48548bc43c789d9e009c6b28998a1027ec5c4717843066c9afe18bebbe20b232af6a655eca1d984ead217d94f9884f03563eaaac2367ef80cbfe033b3a72eeb2737b2ea84d9f7560899c49e0ef18ac9c210c7851e08ffa175ee49d111d0c41670a32e169352989ee7f74c995db970206a09baf746bc3e495d5579da31bff7bc4fe27b61bd7ad08966cee6827b717a0a35f63efa3a7ea0e99053cc253d50d359160973ef869569eb8d68259539981242f401386c08908f997fe78e63ef0c69ef69d3c9a923a4886680d8d7947a7432d88ce4c6a6dc06012e9604d02853ee8670ccb4a84c901521693f5ac5db38c1b2843270862499e632b6f6b1cecdd5860a27d6cdb882522dcbc227d9510b27206b26fe12755077e58a102ef47a1e1d0aece2a0f5e6cb8c8042225aaa2435c84fe9800bbfb73bbcf15a412f5c36ad1d6d17a0206c7fdef589a4a0acb953f4b2d5eaf3a14fe03713ca4903f544cf0864a4dfb938b855511199769795a28a91038374938589bc13f575b4e41184dafc6ea97a817641a778017039485e8194410abb1d07bc4e61175d0628234520abcbd4389139653ce8bf12a91b674261915c62d504d1de5a2511d42cf7c17271ed6cc5e50854fbabdfba8739f3632eb6c0502081eb4634bc0930967c682472162139bdb324cda906ee086ecce1bd2cbfc6be0b424657b73df3c9c301c83ba747be15c46b2154faf06ec3c90f2dea7959266dc5adbb17942ea1131010178318d6dd53a6cacf531eb9139c5042178b289a3100c0e6f89a80e456ea0975fa4caa36d4ecfb353f9c7fe355a844001a253325f40f2dac2fd5715951a235d482f32673e913e4ac3dd4d34885bc8d87f1ca74d9f605aafbddfe1063826f04c72b6a36677844d6f3c9a8474e178bf5f79259d412690c0a602343ecff9408a1a6c9274042418d17960a81639db93bd6ecd7843d69029ac2091ce5510759e2ce83c7b1a665295979d77b1b4732eaef89c4a4d233cf61c7c554c533dca53acb0f8b7b478c95beffc8c93ebeadc708cb686fd807cfe1cc1b1ff0260bc38c0ac00b8fa80ef391881a6d50bf5e3c684bc754282a4bac192043930fc782461a6731e1baace6564b4bf0909b577bed704ae8453c1c64ac63d3e0f0de8a901b14743ec432961340c9d35139d070c422822ed48e776e4bf418a191fcb9cbc7043b6e5407cb9623ec3cd4a02e4562025917aca22f219662c7d78a40290495149e2435f02133a4f211117fd3ff554e60875f4866bca6471e38479d4117fec0eaca34c40b587d8b6c4937c468d2a10003aaa2db1bc86d87652725bf98260f6c2ab31c3ff6d7cf3ce6e60170ef6c2f30c313ca5155c3cdfaceb62833f65bf3a62fc251b0e69bb2d0e92ce127f57afe8195eaf185a46272667d37a78ca73bfa69992c2e06e83b15e5a528866fc35882f3f4e1a8f0e3b2e7e54c51e6f5a9d6b882965efb648c1ee9967f1c66067ab5348c6f0ec98d4a147b29df04583710ae1848b58bd56c260cc1303b5da88373c3835ad7182babf503b3be2c8a86e97d385d667e6d3e154eebb740e2d27c37d338f36bf09f305bbfca1d0fa7f07458d1ce2d8ed5063402cbeb6197c55b694fc6b41133720cd7f978f71602a15b426cc091e2cd38a8e22c594c4390e0e5cd3bc16a6d55ed3f0741b5e0e21f09232b8a4615386df5f8eb0f6c7fc731f30d030c12c8fcac26cbf65b30a5762a5456549a98104be0a8dc511c6f98bbd93e9e3b2a40463256c842ed6e513d274d9616b1ec080a8a75881bce47376f4545bc796bc96e725e4556879756f63c31ce887b4e7c76b5ef692c01621e84fbfa8dd95e4f7c5938eef0097db918e030a533752e49383e10c1d2a3591a7dbcdbbce0afbbf26ffaa31ed0b326712f241297b45167792c8dc980965733c70e83fa02c744d03c0a9eacb955bbe6cf33e850e3c952db95c2ba44bb439570513f583f72139816f4ff8d07e3023b1fd229143424bae8073501e44ab983f5c916d1307a77b11fba9415a1a6bf15580cea121fb56cfdcb8acdb357dddf8631f8d7ebda3201a25ebfc1a56d8301482107412d970abb769d3e8f2eadbbd071f0eff081fa1127facb4a54dc68a28abcc2288560d009e386f8447b4ad1089955d44f3e0ea70358cf008b2d2a5b343e180dc960bbe7e497f889724c5b82e10013d5726b42abd8cc2557acad595c1bdf690b87717d0359dd85665f04217c5139b8f71dfd69bec10cb9ac86d8e297231103f4c4f4e31f312a1e0de815113fbae4746a5b25c6c305eac3b4870b91b07c15a86acb15a30d2cac563a2c1c1f54dca6e884de1eed927de68268a779b58bc3cf18b8a66dba5c69ec384f9ea7a566b5623c30e812e6cd75a2c8cb0c37def8590420d90179650c1448b4f4067c9fd7406906ac49277f59fc88e02e2f28f184bdc0cf83fca18b451153bb8d0b0697999f6b7e180b301234dc16d9c2041310974bf44c0b81ff0881ce8d2aecf5ce0134230557ce5e69833e6ae261bdbeab32aa21fe7f61be62c19fd5f9df7ef41a2eb5f3c28201f8ed72ed22f1122565139d85d187993b2f478ce7b37cedf046aaa2fe84c9167801036ee541527663b3f1a44d711b62c99e9922e339798914f68e5e9266e78d5c6bf1dc0c73309e771fcc8b4ab0097d772ad99a2ac6d6fcd7903bee0c3a3ffaf95bf95bbb29bd49afcc95e2a4477e67835e25ee1866b4f012e93e7db4637c409b7399cc60a70a9be1408de5c79ef17c45d4d587fc3b8c397b1bf03d717e0e0dbc72bccd98f3201b891079abceb4cd0d37d27255b96eb4360b277213ae554cddd84ed855e826d5fa473d467ddfaf5921a951fb18f9b300aff7ee24f9c8bf561269b9c119406db8ee7a6e3d874aed39480e156d150c50651599b27af1a89807708df0cc55393606b5884936899c2f7354112c721bf5e664d6ed43c8568cc6baa0dacb9bbb1e8349e199c56f5b805fd363ca17c1f90b8f23dc4c11973f419f02569769cd73c36bbd8a1c709e6c92a36328cd869ec8aa3d7d17f4e0a5d75bf44fc55d72e85559a67af37734a7a03178aa662c61d19e061646d4f4af7a9537db0f45af900618e7511bafb8805d5eb6095757f27e0bc3d9b3889bb2f73ef3116eba3e6408a9866dcabcfd783147a4b64909ef7f4c9998c5eb7e127fadb0fa331cee479351addd90e74039aa0c2c7aa3f1380a0e0dbdb73f4044af13673602a50b8e76b6792f559178499d6062aa63f40cb053a5b0c377bb9752c8a6db3725086dc8492a316d70279a5ace0ca9ab8c972a252a2e3fabb4110a797a823107bd7ca284ac354379da4ebaf5fa85f74712f1a4aa1a4271deb4bd3ead701e3e5bb3fdabc469d25fcc6cfcacb85023979f490713d3e4d9b34e1bcf0e18fb493ac33dae0f626873e844387f39697d8274a06d51374a39cf9c7967fce7cbcee76f7b272cdfe6c53c976a2aa7568c5d9dfe726ede8a8ee0cd2254e89da1b45f00136a79ce7bcb5ef0c023688bfb442e37c52cbd9177630cee6dcd12a596209a2d3b38c11861fc18d142c57fe191fffc2b8b8f5def62951d4192ab1be53545389f98accf8c7732cdfe6e55b11326964b403932e8b90732afad8217650bcc352cfb1110db635a1f4d1c6adeabef6df71cdfe18b60dd4cbf8c0784fe58d9ba7e78344fef5eaad4a3f4f935018db5e047ee1feacc56ff9d50fcf041a847735c321986ef2113bfa4596ba5e566bdf283ee55e2da2a013818b39f43cd13cae068de677c559502f96274bcd889c0692624d027500b3e8633a978263d78d5fecff8119354ec82c2d84369c3ae8351764b90edcea004e7bd7867ca1fded9a6a9377b50e6fad89726982fc75e5834caf8818cd6e6c6493b3fd61b27e142b326e0ac54cbf0a472f20147da4e24f1485390db21a622b871f62f1903f030aaccc320543a7c0d0a5542376dcd771ede090c433de52a0856871c0de7d438d6b636dc83bc4ca7d100709bf7655b7d48b47594912ada7bf1f755940f22f1ef6f362a7eab8576493b66afd276ad53d84c09b0cde0826caaf1663fea860ef437dec0b996f35c138799d3ce8f63851b1a97c5f4f978036ec0ac3fd15d15beebab8d7b5dcb975071650dda8d4db24756730dc4f1f52a13f5ce343e679dd835669153a28f60ad6eac20acab01062f4d210b12ba2cfe668822410d2c7fda5db8bfd707861bd9c389cd0df047cece78f6564bf671d59e978469465dab77a54a7c76e2d3509cb012571992192c0dfcb8b001906e14f21bcc60c9be6356801e6a7933afea78e24b0efed309a14047a5c80d656359f5a00d6faa0f33b29244ead69da8f09f0b4f323b697262d545dad86d6029965e8a47a8b617e7341d253566f12d6c304edc28a06310dcb6a4e75c37f3f05d94937bf318c46bfd833d0029408d323f431c73535f6f54e1ec50d0d88c1e61ddd5ca4f4c751faf21122ab0d9c81fc0e4c095fa8763371ce40fef52bcda44a77cfde35c0fdc8a42dc600e918b425385872e76b5e28bb7ff09c41a50abbe3fd4ef858c908779998f144e0ccaebbcc6644df1dae4803ae099a072d1928a481713a7a21c639924311e4a551376c11fc956152f0645df25d0530507959c6702a6cdf912c2f75f22ff80954f22cba126169eb5e216bfcca861008bb6673af8e7288c7605602799e8d7b08f8aa6a0f9bebf9ed8243ae268b0a599500116b98855f52b197f6abf6b097c59deffca4b5c64f0b7b99f77da971977dd54b8e8c348c658300e448af9799107a59a85a62d4df27ee98cd9b3da879d9e9d52d9ac878d2c3f2d2029226be0e03668f73a64f970e73fbc4f82e8b4474084d53abc53896e4bae2c0d68a8301f2b2b708bfdbc2a2eb9b2505e94c32c7e156cf0beb36a3089d7a840914411defeeda756d992980e9ae22f4f78d1e293d55f693acbcc04401775bb18293c36bbdcfac32582c5f8f814c349dc9d0bfcc9d4b88778179ffabba0cbb78429da2622d5683c75789d283692d27e9665bf103d12013545534bf891802290d74830aa2c95bf01651ad0f38e9be58a8e242d33049c1349e8fbf182a4b9e421581f841b3186ee876519f793c54155bb76c953418f3232e5af5cd7494d1ad3f4cb15808962040d0c5abae4c1009771319cb9b4cb406f62ac5024d21d83d5892d3abc59f723597825e258786d780c68a17eb66a3321fa78c0a36cde90597ae16281bc9b48568b6b5bb80c050ef91a7037c473a10b80a4249571734e2bc001650531baed096ea2d77bf865da08966886e8274a8d80b0cc9f5053a96e5490d6801d5178c9ce6c78b325d78caefd81b4974c1b056425eaa9ef3f01101a4897c361273a6eb11510a705eb7022e8f802f252fb0592d61e160b524782f5889d06753a582514b5eb38235615d508fc3ad09f4cd00e0bc3524736c37de3e8da10c8433452ad5f075e798893a772910f16f09968f64a093f66af05bac0d48bb908103937e77285214ecb054e9a26346f508bb155a154179c7a9435b998b48fb5803862ea2408f9ee3a333ea480c2e048c81eca8fecc9a3fc5e4334b202aa47b92962fdb5d3dc6ca24e9762328625e03298c296094a5bf37532480899f4ef3f6a719a52b23fff63896e991bf9989a82d757e51d03f0f77e1703076266c20d23307a71e33a8b0c32ae9c92316696f23f1c4b645281c6292c3600f917090297200053b953d3eec61d3bfeb30be9839004bd2ae6aadaea1d0cd18496722a027569fbfd522f2907c67bf8f4178b8326786aa10b40b3c2bf5268eec1be1688866a5395b894c9c835dae04659df393d4325c1653a7a578da667a3215654e894b7f9759b887a9c2e6f3d76531a5864672a52db0db479420bb298f09253ef8306d3ecd30319a4d3c5d24d55de273f03db52989332b09e619725c24288f483b4777a1977cf0724142a30a13b75615459287ecf730a4f6c2623be90b8bb5e9f7581d036adcea44213a2280bc7f34e813cc7e72770330169a34d62fb8ea1c8801dab4becd4ceb9344ebbe58d251189978fba25f291d802721c36011daa43786fde60bec710858b648f581e8d7c198a74e8d5b500112fce097baebc3d47da770f2a9525654732648132989ffae00bba14ca680d80462322527582e8370d8102e3b935f2806f20d5bee2b01527f00a92715bd2e838107e82c39d0b1379f270bf2a8446e105568c057bb940ac40d3c51b8249b58dfc181af2904e1f86c9d6e40b56f09f9dd22398853b13c52924aeb90f4919c723b7f0afb603de6ae98114ce1bda8b7e77954c9e690b02245617040ccb6821bf7dd55d2039d417f20b4eedb62d738ecafb4db07e264b73760066aa4c3adc342db3dcaf669d5a8eabb58ef3c83cd7894124cd16d64a63971f8a405f4db88bc089eb747611d2d1ca03f5170a8b617b81f81050ea6a45a20c79f331c0f9006b240ff055ec03d748fe44ee3a81603e38defb130a9ab0d60306b7f5a8782b36d74356c4f1e42fdbcfb367664d42481602b1fd42eb5582e213495080306c1910c1df6d32168a6fe694ead6602c33121ed34defcf4cd94347dae995a52d5fbd031b02c7aec9245b09e458250558b1e240b060a3e4731f3f0850183557d96f5ced26ceb3797d82f836e40893d86dd55e90e375fe39e0943e6991b92377c276e83d36cb7eb9042992536f43302516e74602901bf0c16a5a501e5d90ea53eb3b21ee9f9e93c5d6ec6383ec174eaca569fe2b1d216d169035ed3ea0dc4bb59ef967fb1cb200fd09fff5cac9f45bcdd6c8d39099fb7b8111d4a4007854d1b45e3c41b3b4eca661c49e517dd13f392feb4512b29bc1df0dc250f07feef190ab8c0c3824a67ebb5a78c562962493e3c48a40be441a2a3ec17b7d4d8f08fea55627773be1da0b9e34c8712d5a35d1765a58cd259656c05befbf992346f623b66bb326f00be1b466fe148cf8f3a6978dea56ff6d6d7ff5ddd7c57be8389b3132ccc6cfe3f109b590316fda33b43fcba26c0658a892b5a92a813949d25e03b260527712e822912fac50aa42508edfd83a707724012db8f7013608c6030a10082aa61354a4134316c28aa36c2ab361ef33932a2d0e9540e5c1b600c3a1cd07acb9205dcd234a59fbeb95244ac2a9e1484b29f6ec6a73d0f633ad79c718fe19ef6f7a560c974077374dc7390a969eee2facb4b70570007d6a9d351adb56dc775a40d88713fe72590d596c9f4c9b72d7754d5151b872bde656c6a24142e75344fd7c7f1aea87284fa562a717f64ba04cd3fa5ea25388bac8f2df2bdd5cbd7506214ac3b2a56804f53e7d4103e0b3382cdf06cb2276f409b0533626d3488f09ab09029029b53a27c5e42a4de7dd1ff5e299fa1878fed0914458707b28cf264a05f0948a9d420f47f5b85a40dc0ff220e10681129362006483d06ff8b01477102e71458e90f59de2f05e284afd530f8b6a0f1af60e40b376d0b86267c100e3603acd14753fae27e23a6f49013aa891b46a3234598622a7b139d5f8b3f29c9c37412e60a5039b102c0e0c157e15b35c15a08dbfbd82b9a9fa60ed06b7307cc969e1acdb921cddbc3db63a222393f045cca2a84a8e36febf9b35a398b51d340f31611c888e9e765ecee18a1e853434b07653aeaed55e40fa456068ea8b49060c58e54e5543ecac54e8af74dcb33d3976215765d0df36839c31d8ad4d377d3c13e2ffad2329f4ef56103b0570004ad665a5f05063b6f321997d8366ee7a15357785a2c83d832cad27b02ef0554967144e87a21e7de1dad60015b2634335fdf05eaade6d54e4c1abcc61f822a200d912192bf313c08b0ade96881aa1ede7a0d9d8a2c211fada0f5047cd44a3a4f6299f38ff4a67065e814d0cd64ecdbf359811f313656fac8d099852da807912ecd0838fd880d1560e7472b08b1ac00e73f6b7bab80f3b77b03a39b0b1a23c25e1023241f74e8893341940c4d2eff73eab6e9c90b708ea9f5af34c3b21f7a01a7230c2e4bb7d52aed1bfae71b2a483a79c15927293e4749cf2df92fc093176016c34d195ea8c9cda6b48d5896dd0b1415b80be3ee74bf1760fe9da5dc7b5f3df394b2d46a2396660e64ca1564706fdf352212090c6440d6884c5bf82a0184378e564d997dd46ce6beca59e2ab58c7ff3536ed0d7ec980ecd034d5d608da6470ceb1f3210381214b853bd5ae82d6eec4693faf7f6ba543d5347f8d253fc05ed01c8a36347c8a4a429e5227af9ac29b5d174138aa3f549df6d481199cbe9d6d44271fddec54f0ba9212aaec097069b6c3a6e4242319dc2c0e68af46415ce59dd77820dda8d309d25229fdf8d621fc5c9a8855bf77dd9279e1393c9d0e91a0180ce14cb2ac55263d7be4a7ce25673a614d2af4216e820ad64245f18324296ce249b624f7a7b0135559044fcf11acaca15e8d22c216b76f1c25a57dc5e2155b8e3daf54bc9500a796e1b1d7874501526e18ac3379cefd73cb0042489b930e4abfad460006feb435e1c7a60e0ab2e31746d82a8625a4fbf3509370657c243757f0ccd75dea4ad70f0bedd9dc29a103fa04bdf98d8bde41734db5233f4158c0b13d19a6ee733430c1794f84d3ca494b3334350af3d0520d695a5b6b480fc91ebbcb772e3d8accbf95caee91fe43d9ce0ca5f2c6ad101bfa6b77e74e88442ba533f4091ac1ad3d90ade0f34f7f5f16a3d6a4f850a21f285d93e2a8a6291fb5be4fb3f5c81c5718e5ba1f0dd3410fcd5099658f972fd0507b0545a15b32fb424c8d80264971775916d0aa5bda2d1a5280fad9b136307ffe31a154f99fad9bbbb54826c18387f863fcc2fb0e750efa63b692d4c5b5d52d8042249cd1b58fdaecfa80bf9aca9ce369f0c3053a0c7e6421967cbaa9319260f5196d0e7de4a95773474dab2e34606ea33343732c4a2e1680a5092ed2d058b5a84049f4183c712f23b9c1314419496d5fcecf33acc208efae126661d1a6f9841543705d6c529cee12f241cb629edb46512cd1da837000b9a3eb2b85dd9a1210da140db2181331b46edb48ccdcebf2abafcf416a6ad367ad59c1dd77cfd79717d3bbcc76890b1d2c6c6dbc746a15a1b07cefd007df485b78a8379158153329824d2f46fb44e9f181e4f7eea45551f84d3f5646c368ab781441a760436c7b289cd66c9ad0c70514f1a83710ee6313abb3cea2243741b2dbb29e58ee2e76d97fa5a5d7569eff6566dc240e675a1aea46811ff8f6a8bfb1eff7a8d7db994f0007e3daeb97434dfabf2620453936e7724178d05f2cad4ef92207b31dbab866ddcd3698c067d19f1e19ae8cd62b8bf0583cea436b2f01b4889594ab56b8deb9e4941de64ed534c03621e9a5986a10c6cbb5b3ed2ee6958b50ca21d2a9d8ff34cb439cb8f044805f9af98bf9e3a585e970f3f63e05c45a81025ecb06173932ab8283ecf090ec0a7da51eab7fa34dc856dd4851c2b963ab94387da396fa569964448c7229132c403a5d1a472e6ea64b479bed90aee8a1f01ccd4e84444508133406c3c8133e59d80b2ed17872528a915b0f2f5d1d474504db6c3fada3c2dff9b4888e861e05433be1a596cff975bdda27e35feeb05c0d126ea17a2a817a89c7a6141f25cd6bc3e1a4d69d6e85d19fc680c81a38569f9b3b73bddd200796de29163b1410767030185d7220a9d983e600b6f9743902a0e82a12f3f3cd202639e6f4e1ca2ed1c6c911fa6375997ea1b7c3095c0344554ad1c1856ab05719ab31149ccf02903a4f307236533500fe4bcc6ed1f7d214e7dc55f5203cd797be0086ac0ec52897e130374e3e95f150932821397d9b33ffa88c14cba14a05f858bc720421ded0b9a9e2ea2b9a1651accd584927b3446d2597d9d8a15ff89f0aaf8d92940298510f67e23a554a90b4fc2557e54d40888a19191a5fc1842c83961519928ad0aa78544ce320be04c0d85381c4474079cb25ce8582db1e8641c5245468d8e9f5dc228c5505a733e62fe38c8d452a3c4a9170029ea1b330fe3a8a9f8ef1af63c337c824f350362a921b6506607acb2a1b5b26324ee902e64314caa45f3062895a0c6e20a3699da1f1a9445e9743bf4de04dbe060469ca413cfe6300c22117fc6308be7c142833a9fd0124585d6f694fbdaed1d6c7aef5219445e5d01c618a55d2b6286be5ff575faa1b3440fe895f08825d488d1e55722fff0324c95dced0742b9568a88d30abe72df6d88c90632f9019ee198355439eb924e3ccfce2b0097a763461d2940062259a21677ebe475778909d25b7b35617d574b3f56859bedc9d7f2a60e33db4830ef299f8e6344b45b1d9d5f1d6b01092b7c6ec0aa283e04a229f0c924c108f6af39cc06692b3d34998fd56084adc06c380a2f34e095c859380af8e3164a4c807d0706f6d9cb23b77ffc102aa0abbda9e444b65aae5f29fb376899039b26c33c9b1f2d0d1a633da13a84a880e90c89ecac063f74e51e613fac99059375a3b4eded1740b02b8bfa9ed6941be0cbadfca50c6e0d99ab1051dcf403a0598ccea88ae2794ef67b457fb48a6fa89660441e00aa1505c7efd2ff80df18aae74fdb2377b7cc87774549e5da6a0b7097fe9914a31786f7ecf649c859058b4f9a7863d7161afe3d3b6394ef26d4b0f6c42a9c77b7363ef5750f8ddb92e3f4fc41f05d0c7a81591a1761cb72ad728f23d474bf44bfe942c43c785860261dcd453c2f26048b13f42ef2e4dd6232336128b983e74f90227a85a8e652f8840bdfd4e792e477e78ea0aac726cb7a91bad58c8f9477884c4c7dec9396f1982930db3dc1e8f2d50338edb077eab93ca31616957af57a547e6a56c23f4ed4449fdfc6e901873bf33ed17f3136fd51a13ff953749d1884545afc8b7cb142565b9506e245184bae1ef0a07b147348baa8b02f54a04cc137475d19ac00049ce257aa973e4f4e32171641218f152e765890695a3e9f1f4a05edc8f7e44f3941182bef20259e963153852975c131a751d3af1cc0cadb8716521a1871d82509978b3a19dba4906af799b219fd08ad474fea0406e2b9a70eb99f5eb10eb9255d264c86a3a8406c9aa044c409bf3ba4f334094571b9328f28bdcfcb147982b99499a7acbbd687e63c2d5f98bc61bc948375bcc00761c775728266360533aa15a7e9cfb68638b7c67cc312546db731441f4143bb0acb57db8d84aa113813c0f9061e8d78b852658ecee864d8c84ed06604cdc54d322afb80c308982837edaa98e6af499702842d55b30d815c0e5a03b0d77574189e444a3b7795167062c9c507d586868b41daaf41ece6a5bb2fc1bf9a66d3a21911c2d3764413ef12b75fb1e16a5f057eaa097fa1f6dfeb87f9e1431ab1b011f1da09bd85d0dc544efa354172e42ddd193ef53e8581ccd59607808414a74993406f65619e73ad17e643ad89779cedb54ccea1e9be596b20ee73c1728bc86bb3610b60868ee311177d5237559544455cec268011de4729d331ca02d39c949ebbf98a916ee931dd50d855f56874592736d862be473ee746bd15073a205591a9a684fc3a6ee823d856905800f485690fa3db7268e3480afde1802203523b6f98e5ae3d701a8ef1d3695c357ba7e4486cb6ceafbd6f30090e19697a3a45faa5f4f6a749c8650d53a17aec20e0aeb99279a195de641db3add20fc269bc6704866d11e2c390f006267da30c68e92c3ad1167c8174a47e20b97dca223058a434068059486054f2d598e9b784a9199d749a007f0542377966e3a0aa2f15fd1aa55274bb557ffd5465d27353ab084c587bbf8380eb4305b464269c09729e3f183a55fb945ac2fc752ad3d3272e787871803f51077545c335eea836113c04237a8823dae686ad5dc00b25a893184ea182b4a34b25d1248ff39af3a7dc05f625d0f6f13c5ffd1b50d3ece36bb849abfe4b02660c24c113c85746c21fb572b8f35f5ec28400ab7263ad36946abd93463e63986c23b8e66954a32aabba2a9dc7cd64a66b6da270e2a6a4bbdf9c05fee0a5222e0460f3af0c2279d2d8e7ccca5592a2de4cee24e9f829e5663e7c8071c2b81ebe43119e3cc183e7706e4a34a89da94d188dab4f343c16966a136e135193561bd92cebc24cf842a341c26ed3d3f1c9096240013baf2eba5293703c30d8b8ae8a48fd5cd1a2e38bc079317590d9e7f91408ba4e4bc7fa246a4509746ceb92c17cd46a334878200ef59d002b060325980d72f50e4d8c5dd0cd7d20665b6a6621de1f860ad1da66945058fd4af003fef6460c3ad853592c9b4718aae85e1e37a6da1270be8422f4806cda9fd6cf952a6e3e0ed7729293a43ee2a5c0fae6979024a30aaf25264d63addf98a946acea00437148a3cdca987ea174e991e399bc44dccc42db96ca1250194409feb36d1e62ab073f90770fd12a4b404529b9c9c62e3ae93166efb58ef996a394e513b000c0b1e9e84573b7c6493127bba0901b7a0a4a1a0f22d69e5db30f649b5e71ed9a28a5d2bdf085ef966dd588d1f7448d5d3c14d0cf9514a4cd0abcc348f5e68391be931fde05ed2dd905d0c3b66ad8dc6449a8621bb8c30fc86abc242807036f92ad3f151a2041654650dbfed54d3f885ffc4457c670de4f601995d6d6c8294285199a9705474af554529a7cfa9880695f9e7b7ed28ee32999be085443f1f22a757e2228f1ebea1d889ad2f4c3f619d362ae34c7e138d73654ebbb0244b606ee0f033b29892e887486c5acf0b354fafd6d91942a58ab93a421ac2dd6a12af4786fe9551a34b86da1f22ac942e7ae6dcedcc109388d03b4b6f8124686c04a1b4ff8775ae87988e5ce156bedf88c8414648be2a29123a612eb0367ad4df40455110eac384d04057b0a27cfdf30e9bc6328e5624d185c2abbc018efd732b069f00779291a4f68963a8d0c048b697352cc01049d1b16be2e242987199ae9039de480a2f12939ea69afd8cc247b42ec63e42bc5c0d4acad54d34c842a11c972072f1317594e72d3434d0efe2357b0242d6d92e0e5abe5ff29a337e4d8141ce8c992424392f909fca97fae8d5fc92644ef2eb6d0f048502faceed615e300e429b0e29475929dc458bac5a66863f1f0237dfa460ed2c2b5d8f30e220b8b174993652acb67253b20623b8d1ac1d4b173a6680542008c2c050081cb4a186f24725968eaa82f1f61a9ab06948d07d97c14842dadeddf6de5bca94a40cc905db052306f9737e66055cf3015c2479103332c0cc5001469b007e6e4072b432fb1263f560c49e5e44b1a22442458b2916fdd752c5e40a2670cd4f29a5af718791d268330fec5bfb52704bd35d4f9317375c1ec949d816c9486020825f0c812f9017b6a10c5ce8e9f104c3e8018f543074588caa5902835778e097a6bb1d5090c02f78b0a8e98e474c0648b04bd31d4f12f907eb6c8335dde5b8418f9aee7ae8d03be3d82fffff5ff37ff59ecdfed5caffffff57ef7df5deffffffd7284b9cd08eebe1a8c0f112be97f0bdfcffff57effdffff57183be7131b312effffd5b443fcffff7f752fffff5ff37fb5f1f2e18fcbffffd7fc5f675efeffffab0ff1ffffffd5b9fcffff7ff5ef7d7dafffffff5f3d02f5bdbc2e7c9d0960e811e0a855985c3c7a2882c90b8f3cc20617785e3efc6d396d0c433ea717b0a7c81790e7c5c6da40e9745e6e52c0f0dcbcb0cceb6483cc4276a13100901d181aa8ff2e1fcf85261c3e5ee5017b57b7ca6303766dc0a03eddad5e6633eb33c287c25181244b121c287b4960bb7c603f80a24fa3151c70f4c9cb0c19a3292ccc80ba1502f96eb1716d61fd218622f4b579a1f9abc5791e61392470fdd9b5df5a5f56bb8c9fc72773ad1a18f30e69f80f929e6b59b39756665f3b14237d1d0eb4f050b35f9e31420aedcb3754703d918176b30a2d28b248d16cfe644afeca94b89529a9996615beae606c31b9a038169ad8670c7065cac1d39745405680d17578947c9869ae525427ac676d538a73a51462fcb39cc193d2a82ea03a945aa2efb33eb6487f198ce5ecb31a9f55f049f7584d7a5b9eef855f2d6dedddf6321760b679567545ca1e85aac91d917e39c7b1649fa6797c2bb1f6e783a4257df6fde9369bedc311b4e158cb1a2195616b29cac4bcffc4f92ffcce9a00317ff99ba0343e7ff99da01fc6c502860ef386a1f79ffacbae18486fc2cc8a3d9d85598c51ca41985b2c74f8b1308f2c328c3d065914adf85e7e0df37f08598b65085e5e097475047392d297984568f1810d63dade742086355717610d897de65e33ecb3b154ae6c57381d5a3d18f7297c2cfc1b9e53fbdff56631a5517309312fa12af11030a5030ae546caeaf3ee803ecdf80f438cad50406f3efb7c8e21fdb9ca52a0d1813d6f1ba47e1ad556b6615f915414569a0231e64a634d81188344cb0906c10122780efd6aca988d85317a4551146bb2de168706c18f92637d28630af86f5e27fc9bfa65fa378f06e860e3a0f400347f372280b5cb7608581914431f16b41d2ae41d37c0cc927db21d21d01b022fed57466907557ba6b45a46a06acf31179f6d4c05fa7424c50ea7a82c2f5042bb61f3cddff77ddff7e59e19b44fa6020e235a748c491204498e2bb47befbdf7e69e19b42bdb61654a5ce6e265119869cf2a8038dc4b216c2e7dfcb2baf74049bff9af18c48007bc61b3cf6f8365deec0bb8f9332c8394460d7a53f3786f094c961903cbb4e457adf518cdd6a97bbe4d29b8334bfcff0dfaffff9452ea84d4f6ffff711892345aa39021e6424c69a4b9d1eefe2fa33816c7d7fa63eab38cd2980dafe9771efdb54a6aaaaa07c573826a62525a62d2af43f2d9d3ec79cc6910dc3f8795c8679452d9b53424255dd81cca50b01ac401c62a91cffe5ee94d7343a208e8e0a6056bbc314d37ed853200e8728ecba88c4ff6590c41a032be56d3635fafdb25211439fd34273de327348de5a04abb975529a34b53285060d1b09ab18e80372ccf96b4e71a28434eda758551d1387587d6f8cd670a0b71fdecf7dd3a53019b9af7e0abafe3c4f1e003fb27d89861c5136c50de0c07880002ed39ed9d6063c607335c42d8dddddd4f16171790f1d6e8ffffff7fb5f9f0c256716b75556675d53ac9a8f37abcf8be9a9d81a7699e59e2ea6e7ccd0b5fff93583f406e409ee7ff9ff5a3e8ffdfa7e869acd5c6092570823cc50982c5466980fadaa88c19262a193e0092f03baf06065cf3b5bdd76f6d1c5757a118f262851febe40b0c24d83505420c11a39820ec6b0a84182c9ad414083152fcc7071d6f8a37d2a7b4529a6bf8fe9f6485ea89177c16a789d0dbe674dab75592d1db2eadc0ee90c2f2782bffa95ad53fe9ea5250f4fe23edfb75da061c4c9e78b0c1c013e5c911b4fd660ebd9fa77dbfd307fe052a21cff3404ea4dd6a7fcde4f5a4b657d57026348aae4efb22edd988561994032a322aef46d1bb3e5131e2ae49daeb927626ed3abf60b4e70dd004dc5d71da6b7ad72d4b3ebd2157454b9668f749df252778be80eb509abb9b25b8677c732544e5606514054918249a4fa13de378a0020bde5769c6865dcc80b75582b7b5d235c77235c784e06d79bae6991e785b9cae79c6076f7ba46b1e75e0fda7ae79cc81b7ade99a5f3cf0feded75c5ef91e787042ff517833aea2efcd36b37b73cde5414a11fd5990e82f6bdc1922302b4bcd312c78fb895bb89ab1185c76c1bb5ae97a73a10b08bcc4c30f4ffa66eb392df4bdb97b54b342cd3543b8c511b8851157697fa0efcdd8733ae8016889f1752ced5eaaf755d251daf41ca5d70dbf84f04b09af00835744f98ca9af4e6ea4369b0510f37caa2cb0c08d4dee03d7a4d3fd504d87c3d99148e6f699e165492949f1be2225291e0f642901c3610b302cb29ee0b058e48284e3e802aee975c823a526728512d764d50a5fc052f82459e4849aac7e5f9cc624b9058f2cc07bdced8034cdaf9ae76c162c903c67b3c8b130c2d57769cb42f71a33529b3df2f457b7d6d2d21d0cfe78aaecde7c4665f8bdb21b2ad0e8dff8c8f32123cd12bc432870cd940c857718e6077e5d2171385850f5029248612ec0e80b8ff63112888b0a7cb6d9fe48ddbdd6fa4febc7286d7451ea02236c371a9d42082872342a1a8d1928b903dea1866db939b1b14042f31755008f094a398569c19f8e926adc7103eb0ab803caeb4829143669f5f4007fc8ece80202b951224fd4c723468401dcb1c50ee9087b8a589e245c475a345b3af3ffffefee4ee9ffd731310024e1eb82eaeefe5f799274cdb3b0ea9d7610bcc9e81d8cae80d0ada5ff45fa2da551b30dffc7f0f08cae7b1bff1fc794bf7fad4e8c95f736366c8ab07d4100dd5d0feb43cf87393886d1d7fd587badb5bb1d25dafe8e5118c4486df667ea7aa4a91975a5a9f905a9a7a9191544db1ba551a51cf73752e06d6f2ec5d2ad56abd572d070be3aa5d769f970e5879bf93a6ed69a2eef756abe4f6fc2df3cd9deebb0aaa408b3937a9d7ad66ab42a6696bd2d57f62a856b36e175ece992b5649b9eba5652b66b4dd7517673b826abd493babd8e0cef47548d56b3c5555acd9fedcb7d4a36774f6b525962d23ed9be365def6ddb9bae17f7b27d6b2edbd6cabc58468a3f7b6db536e32adada232cdad640b3d6caf6e74072aab184134470112440420e2557ec802bd5b1b21906ae03de333eb8418093a602014e9acdbecb51a4e9aee6a40ba0e9ae96d3b0cfef544b97c06c031b23a114c173603f5fc7e8b57c80385af57a682f4768f1416d8cfe72049a90765033cce7cc01e5682f74fa2b4720ff4b4d83e508b8ea51243d666bb5267a68b11c0186aeb290caa899bc695639c258a76a7aac1946184d9623b87495d5b104bf5b5adbb223ab258a9fab6cb5beb54fa05d9aca7606f0f56ae963b055eb3731529b2d56023c1979522104fddccccfe7dedeb0814ee74267af9001e070b118ee5769a046ead1cced03b1dcf078230f30d432e319e1f508c500605c3918ab885487ea8c998eb1d9dfc09071c4ef3b0108ca58238933bbc9310df2cbf657d1fe5234eb346726cbcb48a98ed8c24694c19ee8589659a631c24c754a593dcdd14527bff13f455efddce7744b560ed1728189f58cf45a24e1c2620c8798fc1c359f0895f17242697cee739f7b2354a79ee1d3a0ff06ed673d574ce1970c76b5ca1739b25ee22b045fdf7dd9d7bfbceab42e0ed0552821d3227b9209211342b645b645f644294d61a4fec28229e9334a1ff00f0800a5b556233228ec25fd7affff9621546dcb10be52ffd6be61cb105e56a98ccff7af577a652e42b8d19f6f62147cd1f8acca348eaf5e34d25ceb7fad31190f531a96caf02355535bf34854b33bae44489447c23f8de528f0ff94470295f19656ff166d829f661753f8b30c08fc9986b4b766161550b8062890e0ca3676c2dfb8e732569b647478d3261d6a3a7a89e5abffe3501949f8c90b6880d2b059bb0015376c420951388791da904f25a4406530c198a65aac33cd1236c81399f33256594b692c043eb3945a7abfeffba815c1534a22c1b059e821a635d9719ea84310e0e9c9d4629e54c64cd0db1b36d0e95c2cb157c800703122333e3e10cb0dcf09ded803865a66bc303c5b2806c06c8109339baa14aae35667ad52b59c7efecd4c1138b39b1cd3e085fcc879667231413f6d40f385cc20cc4a75aa496f3c83995533f6d482577ba03a5ef5ae261097e7a958fc0a882f9af93906972d99d9335d6062bd303d130913046966fcc4ee04367330c1d34f0f4375aab97d0aedcf43bbb984a98286a60b17211b8600edf009c39027b25842442126086dada2180e11865a44d648822139b2c450e405b1d6da0fb439a01dc398d39b9b4596be1f735bf51bf98de7cf8927a621837c5a89308c851487300ca956b8500300e3053e2f7052221bf9584de99d71a046773abbdfb555edf5575043ad89ba8ff9afa0e262375bedd66eedd630a03fd7cfca5835d7b52499f4372c2128a678634c6520094127466ab3c7ea63a533ab025df3df27595b70cd7e5f34406a6bfd16683f3bda777badadb1d6d532c698e09a3f3f0d0db85afbffaf2209f55cb51197dd50e16d2fa864d0f4420a0613d996c10a30a6c85430d82383293a32f081599aee7658f978d184c5a8ccc3e0957cc32f28353cdc3a0283062cf3a3c90ba79a23484d773ebd9f5145979c19a203b860e241ac88016658284038ee08e00574335e99d999a8586c4966110123b6e58557ea5e16a1627401594334b6a062bd74a82b92c035bbcbbc66c8284cff414d773e5b6a171bce982098690fdc34c52234254b6f879d245f68429a666c358e1ca0fca8cdc043844412b42fd797fd3565572909bcaf92670f72e86532a3c36532f364c6680669262947ef99a5ab3473c369cf3339ed59e6c933dd323ded5b466acb9c54cc0bb51c74508810c3046dcb58f9d07ba6a63def19db0cd00d92c9728364b4c834c9f0b4cb3869bf2ddd98b467ba658eb4cbe8b4631925edf9754ed1fb65a57dcbd4b68c8dc5142754744f48edae685be6268393c9c9181941650413af9e94077abfa6a45e4e522f28ab9769f56ab2b279a6fb75d3be5fb8fdca25b96281d2d58e1c9a3ca1edd75190de2f9df6fd527a2de9f0daa1c38b87d5ab6600bd5f3ce69459c5c34f3cdce3e59ad01b1f69df188cde58a92908264ca298212a88b63190deb849fbc63cec645bc02fd816b0107cc3b802e88d8b2c0ef3581c063a3a8fac3cbb943cd3ed32b56f57d376f15600c40d5047dc74539aa06dd7d3cf76e9ed9aa27231a172dde03a72e9b46f57d2e92afad348bbcb666baedb76f13cd3ddea69257ab7a4b48381cad1c4bcb2048eb65b670fbd5b56daf376e9ede2a1d2a242a54acb6c35dde8dddaf1925a4bad23eda59467bacb53fb2ead76abd6b473950416a6225298a0ed96cd47efd60d57a43def56ae65e4a434c2099872a7bde4954f654ffb2ea396caa5255df32e99b4e7b23683dea54dfb2e77f9822251a07c9e8c1802036d97399bdea54e07a4ed32e905295e98828c9aaad24e5e7d8ee43df5b4671257b5c9a34dea4c98e2e4850627b6082d41dba4120fbd49537bde6413b97b1fb285f7217f5c913c2490f64d123dd038f54063d5a8a47d8f2692de639305a05822e68724228240a1ed91d7a3f7d8d361d1f61845352aa11a691889c6a2d148fb88748eb573b43545f9663d69cf74b37a364ad49468ea892248146db3a476e8cd3aa95857595851b2b0a4b0905849ac25ed9bc5e44f2c1c2b57c59e67ba4529ed5b3cb1e82d5a35c1638715232493882ab4cdaaf1e8cdb269dfac1b8b4807b18b0ee2102293b8139db48b50a6a8b445b326deb67824221579a24898102a8c9ad21e561d854d4721efe80977b443537bc8e44161900785425557218ff610c8c19e83527e3b63d01b54daa039c587eec70f51568ac6d036d8b4e37983504f80459e008d8040201158a41d34b2faacacc09a950f7a7fbc29bdbfa72f082938d49084071145d0f6d7d3b1bff3a5f75765c4a78311df93cfe843fa92b47f4b55aa77cfafe6fbbb7d44566e966ae56ab94b97e9eeb45727bff15d9d92f6b5b56845a86a11266c4f4abb9d3ab24e47164ac99a4d4abe03a3a6bb1698d0309fe5b2ac41cb5204b62c6b88a04ea1cbf20195d6b0321a1807489a1618266aba6b610c4959ac12bc988c31aaaa0c92fa6272fa4f9f68cfbf146619ab08ac8cd25c6b942495e1f9631bc074b78295de567b94ae347b2e414d772c74d1b0abe98e052434cc67e010638c263059b1062dc420024c0c2248c0a004172a304c00710194133190c0aee9eef644c6b73096da889aee6e51ec8c9a7ebb1e56bb1e411a10d9eb14540b836c3e2fa3f4d220c4e8b0790abfddcdca8de9ad3e77f2284da7aa9c8783e4494b384c383b4db3e76a95eb9870d5731e07ea3991179d9e2332b94ebddd7637a71bd42daa4e699aab59cd5a548d90cc249a35e0555715c804cff9325476a24c1f68ba41b681d5786ef45cabf3fccd5ea524265329090443d16429259199927924499224cbd69faeffa8977dbddeebeb8e4446b96d714418bf888cb46799dc0cce125d511463db3631c5a0c2db9e33344c2a2c6f4593f6cc82096661b0f70abb35f4d05e7bef7bbda30a52c58a7d79b467163ef33284aa6fac85adc2c06280c17bc560455983454b3b0fea29aae7525e61b1163af2cb102a5963fb1ad19a5b53f35e6bc2960b08682a73e95a6badb5c6ae18829f8d41e522779e589fe7379e67d5799ee779bf3083300cc33034dd64999f189e67d5799ee7699a4ca6699a66288e250c18e4c764b4288aa228428537b024ffac3acf93759ea2693299a6695eb274bd6666f469b2582c168b6542886121f967d5499e7f9ea7c96492e69ba639f67a51bd5eafd78b412543b30286410c1c28ed34fa4a0ccff33c4f93c9344df3b3377bbbdd7abda85eafd7ebe974493a9d4ea76341c6a0c2300c6a5e640d70707070706ece841eed9db477bbdd6e37f3669aa6699eb556190a568721c6ee39d273cef359ce3018c6ce7be51de2bcf1e7fc56c77add1cbe39d7cdb56eae64f54ecf11abf226f3a6a9b1d940343d277459b365cdd29abfc106a19bf66689cabcc9cf9ba6c6668307ebd9f39c2faaec7d8fd5137be147ea6a4ee73963924ea7d3659ee7f0b8ce254951b6696864bba6c6c66603bdef06fada44c1febd7821b64acddb00500c3d3e3ce0809aa8ed74ece0e3cbb36c93f3d137c46b4872753b3e59ac98a29f21b65ac3ced10f86611a8cc33ccb3639db320eebcb832008eee459b6c95906a1223cc2d80e7011c24d5124498ec9a783508f0c35e9d6e30a21f9c83986611a8c876cf7b6c4866f621b02002f8a143d2ef86043936fa932016689861e8c4071ca4108870e2152404bbc00b1c0080e3c6c80f4805887fe743b4856b019e73ee13c9c5a49d28412ef05636ac0c189cc288b12ab188a107345a92706223622df0d2bd4effb6c391b4adc30c4d5144142a67c4d7ea4e0916342f41d01f1c88179966d729e659b9cf32cdbe41d84a48608d04304470b2f60f181942b8244e94bfa8182c7901a64a4bf9d247f9d0841013911fb030f2a27461ca181059f0d403cb61c4f0cbfc4866f326408c4f964618103a44748ccd010af21c9d5ed0cdd98080dd9a9281c710aa104084989f7e97c7c2192a3bd16dc6e48a85ce9314403e8a3c7100e1b8240e1e1618673c7cf0cb9a00f7bf420c6071262aa7cc8a2e42709fbb3842d140a73f5234a101c6c481ed5a30267e783a2f8b83c42be8850949f6f0886611a4cf1ce47901d74542d4a7638979092434fee4ba2848f2161c66171b14877c728e8c88e960fc98fc923a4ee2046688c113e74099a72042817548f8080785c2041446e9e659b9cc19f230ff265e7082d59b0984a9f91a01a88ee67cbcd18f2a18a101a78d082830c40490f644568849a15241e588e5c1d2932c48194f0b06211c1304c8371d014a9dbf9efe60314a3fc902dcfb24d7e7a299f2142766018a6c118480fce0fc5d22076d031e413a9288633f817246407b56221e833faf1b15373399cb73351b4e1503266a9881f9618d97c10d9050dd9328ee5428b807a3f310c712236518c91e4cb55940b3f00d1f40d19c2f9f8dd09f180867c1e186ac14ed0962748a0bedc0f53ce46cbd1ee268390cdd3ad9ef3c05614c04c024b699021996d6860184cc1af77a81dd378d680e6808002984910ba8d76cf7b56834536600274e6d9495256ab602173154ac3cd586d1a6392758530a49424b3286656256bad51c95dcf9118093e9b00e560042ac3f327012ac3630fa01acca0acc515d3da806993d2709c19396813095278af3c9406a5203e81de80322aa5537eaefadea84e2b5f5d988aa8842c992d6aa988488010001401d316000020100a89c401491044811a95f20314800b5d844c625632980683c16018864114454118c230100208008600828c6312b50a02c3349451836eed19893a2c0be67858668b6b0c5f1594df6f88ff8c45d493c890f8557907fb9705a4ded3a59ff0b6f9977825033c90d419d3720d800860b8cc7a31a1484e1d1847ea5fb7a74a686e280d1b7ffba561248a6f1eb11a6bfc5551808b72dce55e18b63f55c089f4baaa3c78fa8859b44ef946f9bd34223524c05d6c0b72d5c1a11e5a06f848b9523823663d346c848581e9b4ddfb517a07937f3701cccc666dd260a06a8bddec4e19984620a3ebdf612df6a828020b3adc2f55694dda412843316c413c2ced5adcb7b1e068610b159b03a4911b8daf1cf41fe3fdc1369e972471b849ac1754da72800939dfdd6efce3bc9f2fb25082bd3fe916a8a3759ba9b4e9c972df5c3e08a2632315704be1db82b1bb6da7323e9976a0e9c946503f88aa46c1adf709c0ef76994b010b8f2194b835c9e25662076990d21db1cdca5d707f1990236c9f41a7f9c1e6a58258733dfe9b8126e998a376802683fa206142187278dc7d725eca7745042e4f7d216814ff0754dab25853aabccbe8d335e54ffe8cb8f1bbfacfd1ae8cd0c530724b4c70b56c0d67d7a47425915f428b6e3de9381b6305e0bff575a3d8988bad53a0810b3b437e85d736ba11c9ef2ec2932dd0313fa51aabc7dfa2e66d88159a1f93befe55df7202d8acbb974847220a211d6605a65f2121d6e47c2c2a58ad09f630e66f349e2e3586deadf86c5fe51fc06128fd888ca856672e7a4f22cb4cbdabca13757b3317bd7f3540cc526fda71cbf726ada207538749bded6bfddcdb48afeff3d173698dac96b2606ab5b03e3310d7067d35d2973f63e7e3ca1d208e0456718e089a876f2e9bd93cc98a69f8c10e0a07455ddb1fb39573cf853a074f08989b1ba3a824c8a63fbf7dd2b3a393ddaad47b10d9ebbc7e8613e169d515ac5bc7102e1dd80d3c959e13e782962a43980d1a0c6db6f12fb4cccf11dabf9252096f00b0e8fdfaf9fbb26e36f3a5f8bbb416e01b6044d2fbeb7993de2d998275ac5634e30646480223d8d3d234d66be97e1a4fdc2647070367cd1b7af9b8d42e66db4ef227648bfecc33ed8e97bf84109f6d2d951bd1a862e3d77b77d8487280240557e417b3cf7de75ebc3285b73916a81c59a09c645017f91c974601cadd72d0fd33ca0adaf1d0404b660f1e2299f7f8d83fccfa5e7279284e39a77ea93ed65bbf453137284f5039d425949f55372a8853d2344e6ac2368191411237360cd06fb5f82c82ae7ecfd0f30135c5daa5b046f18ee9a4e35dc4844622d179769e043b9564cb8cc86107fedcc269d50ce377869cf8692236606481b3e81dd37fe5cbea577dcc04478a4120db2796e6bda1cd71b47a84bdb677b1985e6695950d4ae0d8551afe1a6c782f93637d8f48c6b5faabf9837561f25a6d6453ecdf9b522cef3fbc309334861a74bbcac24406322903840a71b25e90f6f41d65f4c92db3f98e8cb26cc73efd29c1074dbaa26351396d8dd3ef6d15d511c18e2fa6acb64fb3464378989e7360baab0e05cb725c5a8aa884989f9b77c46bccb696d68f133930982f389db3e0a398af22a591867d57b2714d22366e362c2b480247b7573139e8a3b6f1de8b7d92379ea58f2df234468190b5fed1ebbd5903cccf8813687a45be4e77f8a8d718917aec3563d7ffacffad61e35a5b3d4a74806520eb74f944a86ad73bc99c14a21a816aec6cf32119c1223ca6b6695c1a8003d6140653d290b4a0a2745392309b3024d856f9e39b91d0aa7818daaa37ab0e0d0efddbefc93916ef8f0f56d760430e8908405a84018aabb47d9cb6b6314a52db766ddcc2ff868e554d4415f5a8eb81eed2693d650f677784eaaec5120297e7da09ec50180cfb0769fde758585a93a8d91b749316b75b0fe111fb07eb7dd11673fe20932b5fdad80f0323253c0d9b3949bb7f4b3854b9c90fc4a5299da5de610c7a5817d0658c703319134648a6e984d6602a22f9e85c67cc0f8dfd70efc32a32f166d9931d1904372c9b4e59b3e9539f216e1fcd9f046a1dc7bb756d5b1f48343018f3dbce7fe81816189b9075860110640dc1a7ed4b7d62b0201e75484069fe5103b1d5a1986ac0c4d0bfc2b912397428b195a40aeabc763ea9796075430f8472d54a94c6e914302b1970930a049599f353c3cc234142c4934721d4931638268d81d31e1b8e1c0222bf23bcfd31890731a3d6e552512c479d20f2bd758870ff4fd54360075d0876b00e23219ff1ce5a17eff26fe1ff1a31afadfdb1890720b321c5c9f721854861d85f2a9a0b04934341e2ae0ddd0c2c51b558e777768e77675938f69acf9f3985a426641e2e6104f6aeeec06b23a0d488029290c8dee37b2e990703898edfdd18b07aa2f4540f91823d0f24ce08761266af9bc8194dae972b1402c3e49aeb9390f8b2e7e2261bea498c68b9ad4ca6fe44c1930edbb5c571a3657e7171b9ca4c106197dc3129971c8f511ea43755de583cf940b00ad2cb1185804a0ae4f8154ea9bc0d413552da3498fb3667889e00039bbeeeaae183673bd7e80b246917063a863877b0f0edfbc31e6cb864177a0df5470a16924d00454bc2d6f9306ac2e74c3f992df552bafaf274852d24d64ee48299dffda6fecdfaa3d71ffb69eb2d52e3a58c4b19a95b3884ae520ecdd66cb0ffdf14d7c1f0954c714daacf8924e6e9b05e54cade511208b8787b474a3fb7bf55b3e15b65c808d14fbb6a6a16bbc340aa0cd9006ec3648cfb4292e1883f0a365a28b5eec649b94294159e15c54e82d32622556d01bbd5748496aa8a69da9284bc531a9d8351ce9c9d3e139e3f03b39727c8f91bf9db71e9dfbc6e41484f6c7ea175ccfe5866baca90fdb8dd79e133629fe07bbc87d685cc882dc73e76f40705abab07a0f991c39fbe938907fb691f367737c0571daafbb420fde3aa1e7037b34ba0e72b8662ed310582f42d273fe9934ec0200454fce6b877037d9d069400caa7616963d0926974742ec9b712e644af78b9cd85a7414b408db8a2d13319ec84a8beff9619d583f169ed3efc42fb30d0b8db6a6a5cab12af6c8a071490e6a5578dafc694b2f45e46bbd5ab0321b3cee45ded1a7031a1b4cdbc40e5e28abe5bde571c2cb6b7d04a28541d4f9855a9a0da51863ea72f79cf658f71bd93e1632bd4d15fa20c336255cc0a0fda5c2e82a5f942bee49637f47db4ece5ae91d05be1e85e32e4acf4e061015a382a59105264b0e307468b4c73b04fc1d2a6b9352b1318ab29b729d6d33bab916bba856afe115deefd268b875d1370999f816b58d576929da87b9cab937e00b93908bb9cee3e1ced4db407ed2951f2c7a0bd5f810beec2dded5d32d08c3b153290a24bcd491f18f4dae0de3dc96b182e91c8ef95c878fd69ba3383fe6620120ac97d50f1a51974df1da7c08855cf2d702568a68192b9e44db79394e96941ede9e9d5db0233d92e8998d8f86bda81c8a2f7513a87858c21e8f45ec4701ecbbbb8609d6887fef61b767b3f06cd6e5f60858b6024aca375c5be28b7d30b5cdd5a8946f7f57d0b9154a4d55f1a39a534546bfdb71e4bad7ef75337a637d5e8d5c8aa006c5da21c77526f07a21c8c4da21dd2856eaf6088b51a66eb35e47dcd7e736e1e5420d263637faf60758d5354b01f58b2beae672ab88b82d35135dc7f49a9330ad7d68d5cb7ee3b0eb41b7b5e3203fc8d7d93133fde2ecf50fddbfa3b90da05f3ea817bab36bc619dc1df2422ba775037c1c01162ec5e0a304b024f81afc636ee565e54ae6cf476c06c5b67958279fa883b7b3aaa191ff6c7e648efbee047a07da19987fc0218bfd4b8fc096b4f30f2831ff7b60e9cac3b3cb1eae726eb94607737ad6a22a503cddb492f37c5b4ce468450d54ba638d5d96b0bb876265604b9aa56db0bac9a5a016ec6a987d4e13799519c80696468a2b988464a3d2c1adf1a6cf0d355dd5594a85a5d0116a3aec6a130e79efd8880ea8b3d10dc0d3728ae61d5acac778103fc3f3faa7afc6704d83cb33bb46c1b99b2312c3512adb87a8673feb6bb73f275c99c90dd6676d0ab50ca50167f75fb10a33880e21a72f26e67eee3699f77bb3fe0c7c4e59ed97abc5d9e1b32f670bf59ae38a3eff3ca130cd387a4a590be7d08a437d6f1c55bfe244f5e3904cd30cd665e1609751bc828ae1d194c27e01977ff911034f891247892acbdd8c1646fd0f5c3b252a8e55197cd5d62d1234dd7bb07f128974132a035b7be232a057e37cdc328bb0b42678dffd06caf39ce6e3cbd4497c523409022a18419b76e198ec05c141ec1327aac3f8cd42b160fcc3c49cffead9bca4ac6b68cbb8690740696826fad374ebc151d03fc002178ada4b2e9bdc2d007e9e5c253f65e253f36ab40ac4d0e55d348bde6cdcd7d5d4e2d7be34a94a4087ea6833866a4b452fab2dc93313ea1e6528077249dbb6ca58677daf25cb4bf41ddaceaba23d00f34f62e9f723efe9dca1bc1f181e2c75a2677f555d787d4228a275b3ff2e1a675f42b6f51aa24e207b8bd2199b06c1b8ebd99b925caa3ee2e3a2414af979e338e3ab92f06ba2e2fe01133800a3a05fdd211fc6da35b1cfdf80224eac36b8053febb6e911faa31832e18f4566e8d09f79e1b0e1a7a105f5b82cf7b38eef9cc749a2d03cd2244fb8199433677cb26887985996872fa37e551a4b9cbc06f578e3bce64d858945d1b47b8b7837e652c251d96f77bd9a0a5d837d3783349d4457c34c98b150dfa0dc47ca5d478cf6e073a3c434a71169fcae4dc4ac3d8afa55956e4782908676da3611e10a821c99a52cef53c025a9b66c22661c837208e542178151cbc008d064402cbb960592d40424bc96d88bd55c4415cbc7599840d641afa1c187cf96c2a6fb1b0e34ae5536eb5137b0baee31aafe4025c381c57313b183c235ca0d3daae5d9bf1f4befc4944ec0023c22d9f95bbf46dc974449aec58ae0ccae9b44f25b201a1436cd0b9e8a85c602d812a77c31b88df89f1a71fd3a51d865271971180b785c846e68f7c63560981f6223bd9b8b9cd8831a018c2dec1b8400c21b47ea7de2d19a034cd3de485605a43ac28b2835016cec8daaf5214bad23e37e0d1080ac29519bf2f5d0e317f30ddce5b42340cd29dbd622c941ac4d83c4ad2be64ee121275039c4955a3eda6aa1070aceeae8cf46877f02daf1c4975d03d997bc1d8120cb62e24e1220558344a7a9b90637f3f85f60164eec076af1f1afec1f1130a604085b183494b360d8fff1a6e47629eb10c4128eb4a79c3bf65f414744132a2c76a77f1fb7771b5f3c2a4153ed3bb7efe656b30f16d96924e5a8809c01d6e7d08e6a0631b90f5b05db0396a3e692bf73724bd6618d0bbfe746bcdc93af2e99c096a5f0ec421d232c4bd9571740cf4d0cc162e6463f42e6d03483ce9be436c05ededa23a7513708c93b68b148bf3a1e635ce7115f29676821dc0631a16c8c8ce84d4d7f852520f2a6862e770c67479235ffee4fba5c354e214cf59579cd32433fc56fc389494ac9ad718d9ca0db83cbc05e28e17292bc4dde4f5cf9bfd42e9d055e2aefb8b0a4a1cf5d183c351b2459ca3a06e0bf3f25351bcc1bc1f5f442bc43f63bbb5d81bdac9c8caf0abb54368dabdd84b53ba85079a54ad7a0c98557de5373813366ab1750f2a378c5c4a99e567d983eebca9febd6db1f4295fa461d6a014787bdf00a6ca26e9209116d02e0b9a1f9407be395c99256e889baf53b16faab17714037aeffbb430fcf7303c764e74c8399b2dfb9dc34608b41b8b01bd17b4f82b271a5602c3b830795911e1967d95823f80443fd1ccbab2168e0282094231cf0f7694bcdc36cd6e00aca1058bacc5b84b349a7017ada20747e4e06874c95db3d845bb6bd25c60cd250e6f383c4a98f08fdb7d5ac5de0aa8543fe1cef0a407bd776ee77446117b182977912f71466184238d75c1a6d0817e5f3f94d53886f425f13167e8974a6b7177d53fb94b016e51d49bf4a3977ff86679fefaaea41b99d78d91039f011fc82e2893cb19e975ca1091bf0aec590ace7ebe1d6435c240fec16196ce27cd860122642d301ff71e1a1857e0d4f70238431800f20621a590bd371e67186f58085ac4e59e92d2de84613c92dc4a2fe0f1e42b7f009c0af7257cf473e89045f41b1748c7022e348623e9d601b233061d9525554497141fcc9284abf168a87473248f01f0d2ef9f0739cd6aafa1b10d507158a27e5336f48c74ff1e73a88b64309183f51804cbfd0bb0b823d0cb42cd9d8b1cc2bdec81d437d33816300f166c5876d7918021af476ed90e4f27be6cecf3e932daecb3398ed3c341a4aee0b708e6524434a183e9be830abf3f4564b8a3a1a4e1f7ffc46f6b2ec78379d8dceb0f60005fd16a6c3b6483a5d0d467ccedd9ee112c327d93f99a29481cbd0aa52d1a124553a674536d03aacb317f249310ce2f524b4a32a73d744bc924aca3d30eca62486647acb9e32cb87cd136033abc5783b1c8565de29138d9d31659b4d4ff04659253df11c02b35a29c777b92631f499047b682889695a5f4dad02f38410295e33277382cb81f33faac2a73511daff6ad10e48ba8ebc309040db14d26b68b9a3d917cae1fc9a7f859c244b85f080d38cd47f7212642cd3dba4ad0095ea732c6df95a9ecee978b5b0e922382da9879c13630b8d0982adef16a817a843ac495798a7c1061c5c84b6f367c443072ebab0927b106580b33cc9e6495baf19e9fef816d519677822c060a344a004f02de2c686ee489261a03a8c47a06508772eeb2ae81a1121c8b5322f998856214e9d929cfeb30ff274e5ba1bf898ee91064fb877cfc594655f024f2390cba73f2374bdc4012d602344e1faa4331364ee28a7940dc457807656ca857c54937faa295e3545a855952685e28622670d23fa16fc3f4706e813c9d6a06072b987747acecf7fe634f6a5ec0b1ec80d2f8838ed829e50f1ca2cc60fea0fd0611d4b114b349cf0ece5b13937babd0ca4650d3e40ed8cd30978c97e3942d2340708b4a55da0890f0fe13c0aff5f1a995e8f497d665642cdc9e85f34872037dabcb3448866f44730f22bc9537ede030168eac0ce04893a56976e21c0f6487c3d7a3816437ce8356a982274ef2375fd53e6320026bc25c31846af9607b7b80a270844052021f760c559f6a8e903f434ce3e91ff72eb1e61d7e88de92df508c7f31a23bb8eecb6cafc91e8428c7f756f6c63b39f3a9ce24c5055c5e16f50e3ef20c9c3a9b9eae6fea2de4277c87bdd67610dce0be9deb6241b86095a1320d9995788b42f11d94d5b5c81b2dbf10cd95e5e5c4d7b0dbf18698b32d9b91ba99165335b7385a2b690755fa9bb691d9d2aa359fabcb995226e49c1b51a2b9adfbb40ae12efd03ccfebaeebeb7eb9f9487cf2487ac07d382b0757d16d884140e4358fee3acf53255bb46ef06c46ca01ed9312e21d08fbeb3fe6cbc7d875fb0ebe05baed8f22ae1d5b5e9ce1d71bf0d244cdd296748412ebb2fa9150afa88a6267ae24419458c21bdc76f101002ac8e5bd61e65167c127e5e383e081258ab3f9a2cd8fe60d976f521834458286091977ece2c0fb0e9039f14373452b0ef9b0a6ae03a80f92cbab288638911c88ec2a4aef02c2bad8d93b536e057443c021ad89b54b83b208b54736c4590865de512ea07a0360648aff8f6a0c691185a7615aa424491edd2674e16d44f2d4154856050bcb158454b01edafa4f6a9aacb9ceb6d7fa15abb349040e6d3b653cd943ac2ea8146a3407d8079374769c07a0814a32aa731ce5051f7b53fc16a17a269c932f840d6b6803212a24d270dea4cb7e501fd6bf0adbc17d646166d81cb04840121b25f4d4f4b142eda5575e0131a4057a13e562f0938e832fd3d90fcc3c1b2a4c5f3812666ab85d6b751d011ad2aa9b8a3c19736b2fee7895bd28392c1e0dfea492238632f95dcaef865fa94fb6de90a6e28378dbd35fb6392b2f4b0c4c8f712f8d9fee6eb8e9e62803532b1e3657876216431085c1c770d8bcea4947c250e174b3c79a3d301581148ca0028a2d144030b02af612f8a948645af8f3f33fa84a839dbbc6e6952627a6acaf2cbcbd343830c798de567defb8cf3bcfe1687e25c6ab1dba06d02c135fb39707138e9082ea4a3bed42b619f431a3c312fdcef512308c80ed94046efa73dce90af5a30284e8f1a668c6bd1329e8fcbf61127802835301da9013aa17ea777626793455f7d528b10d83e892666960cfec3e52117ff6e892e1e325a45b0bf8b4c7e367131f1e72f6bb826d48158c97c7b8bdce70de0820df2908a9aeb66b71c2c6dd37e060806a4dadb02359df8d319c6dfe8a47cc8bf6b9702868508b038de50c3c881c495debb094b18d446975b5b7089fbe310b6df3bc1f099bc7ec360a97a5ebc089a33585dd333c6f86046252963cfc043c60aaa023d366ed5c26095f71c522cea1b646c8014638c4d443dd59f22c0859b0ca66b6af1a7e01086982b9156a709c5d4141d39bcef8c2baa08b5cd8fbb8bf3ea145166f5ebc0ed6c63b94d49ff12d4c2aa1c5a6861b185a91a9567a29c41efa848545d342ddd1311f7088d8517a101c3d678cb71424f295640682b8eb7fab4e265380ba978d1994506343d0c8df009555020d696a650ebaa3997259aec868bb467bc18ac68562cd3e1ba17effc0f99a2e38347b146717a8946b531f0ff7411df150eaccf319a882aa7ebc1b917224e1f885b7cf893c6aba45c5df2074463dc198361ba36ea0e1c0606dd8144cd3b239dac36a16e299397d65358468bcb1b8d190c20f31a3ca3d3aa87f07a7ea601c06026583a3dee90a23146b5aa7efcc64b0b93785622a2081be6ab7ad7518ab4dcfbfe7e772707ece05eef02ba2f748ec4cc25a60ab303b9c3699deae6112376bd445ab132c9338e77ddb3baf0ccdbd11266f6e3fa889460da0245f288b6208b1ced27d0ebe8f9ccb404b9e41f0ef85388af9661527ded158123eee172b1c034193637c9e0547e0c6e73bcfe211a152ac9a76f292e946478210357b5a0ebf3f588020b7e1ddaae81bc93986d05fd1520b226ff944a524a3cca7656e2b24467e30e3ead0f51c0c764810eef92ae9e8c3ed2b54e2bee7d1d10d2dfb70a4d365c13937b0cadfe8a5660fd1caaec8f1eacac84e3097a6946a70d8cb806cccf2cc02de675751c95d16559db5997067d38ddcf0f26ccdc94a0ce74bfc681851b0a58841cb3f423d4858012465181845174d6276e6fa0adf3daefc7d2e93ed65620601099950737781ede99cb9d6d3f6925c330899cdaed24a358c981888839ffd85075d85f438e3eb0bf77a942cc98600084d8f03fb2660806df93afedc70fc3e4bab353529b166bf06c349c64d8df73b07639145058f383b4e9af80e039fd98b45bfc10ca52d47cca199723d82e5ca213f67fabbd00e94a0e4023cdae5a604d41dfcb20419d6b8ede07cc78ad9b6ceffd6001c57687221c9ea0c2d6dbe574c050348ee2fa1b36c49cbee166725eddb28087b3a39a34c5eefc1ae2416b1f8a12c4c846edc68302f605a643ec9f379b502b74577ef678be40b64f37c61e31037a8196294301e296550d6210513ba3708081e58e7bd2f6d2c0a2599b587f4f3aed2bc7e1ec3f6efb2e9fdf1e4b79a7b8eff7078c6d5fcbd161d608424193ab81e7a9208c796253da1aa3acb23c958137de601f012d08b476f5d0cbbfda95dfcfb94afd0b0bff6d0ebcc851796794fa851f47b5a850ea2b4e0afca738de0d1acfa650a5a3807b49e5f986e18972b4a8fdada576c2b90089e770e5ab5e1d68a89ab2480f59439acff4465fc7a358eb69284cf242a78a381c329cb3abae41bd6afc49dbf7c91db1e2dbc05ea94fc020e900516c4da7c51186680b82171eda79d57a8f0a9a4bc0a59bf5d3671a6cdd5c3118cd16346aaf11ff11c61605ecbd09e77047f61f2322ed3b8d4993f0cd28d7d3cf56bb9603170b3494b9406da9fa72c444126941dfd9b7a6b0afbb9c59c1d78344414f303d1b431854f302e8036723a223e7c6088e42b08e20ea7da89d364d102546aaf73103d95d536496f1b0b1a89198c508df6e3d32cbec08fc18de0192a55bdf9bf25c2d24e5970caee1571bc5ab6154b2afc591516556aac0de50ab9b1a8f4720297b3703451f71460106b4b3da4924b34fc1d4cc40a2d24984c914656e98dffe3b7a0a13e6a8e1dcdc1ce65ef850dae700814bdb788a66d0ab7b4ce9a6be0092135811704c50dd13b03231a11e3cf1b1ebf042f04f1178c3bf5106b4f752e92c803003b76d70aa1e3ff24155ccd4e69cffbf60392ad0069ac24002328022297553ad70fc6d1e7174fe5b1692230fe9eb7bf3acc5e5197ee97ba871865871d2ad97f4e2283b1eee01d614fe4d0fad60e091d4472a55d7face026d5c84ece16e8a692af620a106211e90a1c28dc847b4409cdbb13eb199cf5a5a6b66c9537205f233dedf91ec6d39cbac1a34f4c2dbdad5b9231b61524eb77c48ccf3ab34fdf5b4356be85927cdde0fd21a1fe574165436accead951c5c3d67bfea7865a00dfd5c05205e3b2752a9ec1caca7a2fc7b0578017edddd99d0ae7fc93f0f574eadc280b03f947884960877f24a966835d158518aa1357cbd74c1b2c6a0a5835545389f9b02bc99d9a8cc9e512ac7fd9c670c92d0cb899420582780f78dd2733e61b438d5f264bd1acce4598886664dd798febec45e472be096c55a1e7bfa3b037ef08e5a0f4f99594ff67dca5f2718533f39c12259f2e97c45e511ce7ae502502ce1bf82daf5b46d9b8af8f9240b9b3fd7e603294cfc5d6e35837bd079bf76a2fef6f879d32747ab00bf81e43c3c674c06095a3aab91f4200d963f6526f04d578fed69b28613c51336bea5aea9595ea108dbff09733051be31951c2f401c2c048ddb8a910c785eec9f9bc57b230e820df2ec6d875c734a290155516490148fa1511af34ae78671239375f2b5603e1f1b0ab513449fc508fdf7927e109477b00c29be176c5750030c9c02d808798235089450b3ddbcd67ac10acd4f0aa9cd26a2fe6f6e4b046aeb4b16e16d4639b1149f76e8e731f7968b478ae316c052fa2e5b896d5edbbd379b8c19d5db874ddb9c36912568c2d8bf60df30b459837613f42e91ace8262f1c59488bfd002ec0abd92d5a14eda2ca33e23a7d333197108c8fecd12244a7a0c8dfe5369e6b4bdc302e33e14c14b4b7b9be4724ed1d234abd90d99850f118d2ca5deb162d294894ae1be46ee74792b05fd77583ad1a4630b29b8dbd20770deac3f634fe78b9b3ec28cdbec6bc1e82a0c911191514e4d84425516efe4ae3d1002454aa68b867874431ab5da682ab9071ec80c192af3dd4b3bf1747981c580ed8af72332ffbe8e2e9970bbdccee2863a70286d2e441511528fa90b2431d6f533e38d772d8bd1a71bdb9146f1f0fa75f607d5f145db23c2793fc83d54c14e99f757eaaa3297442ef8ce5b35934b73bfd2e0b84e5a161fa02778faa5de0db8ab5f530471c100dcd644682ecd81aeee818e571fc5871e27e26eef1c06617210c8eb2d6b760ae2d3dd5ac7ee40636644502e5674af9f498fd7abc512b866bed73951d696e27f135055ae184dabbfe0f9d46f407fb96a26123bfb88270e697dd4f5427a5e7de61841303faac5f8e008bec7c59120116695ebc775eb2fc3de294ca07d5c258076f4da7e03d42ff5da78cf9331728b15401803a755e3d3beeda1ba555687ff57f9303a9203bcabc6a4c0319b9b0707c23bfed342be61f6504a9baf18c684fcd320f651692ca8e5e82da2ac440c5360ea006c241764049bd6f940f120676fab899dab95d3db1ad4872bc5a8a88b0f4c08deca8e948ebe8428bd3f439d3991d862e6d15c5c0f2636a3a429afe8745b1e0ddb4a19bcf3b3df1bdf2c29bc65e95e39a4c4d50d13407f4434b269ec6ffbeff459632814004e9ac72ff18fe3b3ccbd28ce8e7dd3a9e7afa0ffb7690ac865b3a800311bb1689f6110465862546b6c77f1ac87a54a70bc3f9476947780c7220b34e0ae15a3aa9d708083a99537ffb813704b567f6be9004b6c6e9cec88284873c070f61995b15a4faece932332df44d59f752ad9dd794787e29cacc82fc4fa9227820c4dd2297033a9e65d905462afbc62fdb03dbd3cf455fd8a529e7c5e8d425e825326d2f33e292211f2aab073f30563c36110c220e8144cf3f257cb05e214ffb45d383ce157ffd234782ca849a02446060a3e76860a35f0f14aedf904ae766aaa17d1891e47e8f20a9d199e789ad29a2e3e8edc83fbbbd3ea0b3a55ecfbcf86000fcd69627dfeaaa6f17d718adb8ed3754945df32ba3d83abcaa3d400b01a3c5ee143d905b1700b90661e98486fcc1a8f1115336252274fe7ed262532028aaae0eb7f87519f0756606401a329bdf46b384dbd5f0b1425970e13743795d1bea76bb8ac7724cf86af8000515c2f335cd968a6f94ba7e5127de38b11cfc4851b02e66c38f284d7ef58ca91ab6cd9e3c86498cf812d721cc0c358eea004c698289687b8926f22e2c9f8f9c7f941a8be3b467508080e9cff1246e8c749bcb94cf5c70be4db212f0b6c1772bc6ce85ad852a4fff6a9fb6b87c04b6bb1eb614a6fa749fdc6a3731016a87adf4f0f75089be8e940e6ef65f01bd71d0a5cdd26ea15880d8abea6abdc29c8769d5b5d9bc021f650f2047c517ea7ad68cddd16500cbf70b03887f71e1eed2eb696b9d80b63456b03ceac39ca1d2a2211f5e10fa9c6f71ee249846c0ddf143a000d200969b7aac24704bbf8d1fe110651b36bf2be1fba00ef84a38fe7706fc37a551798501f66ef1d32e8075d26812097b10339935452c3d3caaa7e159a30ad6b331cc8dbe0f230aba7821d2f1a12c1883f0e180e464065bff9cb2b07ee1df3271c23b01be5347efaf8af1e1ad5821503e6862ec4f48c987ce73278cc7629a4639f5736a52a20c2816a0efe84a3787735b0021decb2e2eedb35d1c017f124c04f949cc8eedd75bce3155e5102beb9ff50ced2586da5c6caee0a25b138f9a76b98ecd9700c419269824874fe2e7661ed3c173bcfd5bc9fa70da3c190c059af66db5994e2d47bade08aae4ac0dfa84c72fdb9b61a3b2be1d648bf112e60fc53ab0c7086f5f97ae0ceec8516bcdfac4b39b35d9c3ec7b005f7c311f287177e568749124f8f47a1f504800799d8538fc36637d6f556a6680b4b52b2a4e744258e9e79f0c0a1d84c46a1c42589cccbc82dd155851553523f19ff497bca5b717b5647084055e6eb9b880cad296d02fea2a3b0e99ae3a6cd71314c0862e146f4172c4480c11cddf607e82347d61aa54503ffff41e5d5614173169a2e773c13a3068e43fc45479a26d7f73339283b884e0aca4553118e88242b647f1150d8733ca4f95648bab6b357387baa900c88ad9fae01244f5556192c46479ada8f994e21760baf47f9f6265e5c3d23439c894e49dc793104c0afcfc2977d12cf81fde6cc6358ff411180412d2a40cd4fe35142088234085cfc5feaace2569f5777d4b83b1eea210d9b1a27206f631b9852b01e6d7d1bf7f5ce3e321e62e8d7e12e67cc7a4f4fd26b5bd123a127d793c1170e0d93daafb31886563609ecbff42be8660c0fd079b7f60b3fe1a5162a1c5f95a16a7abbe3c2a396895d17f3e2e45548bef714eb22ffd071092b86b87c2b6cf21aafcfa87f7e28ab2907b0bf9eec7f7eaf928828f5e95ad01ba6e3ef0c87a72a0877d3853614b1688b3ddabcefcdf804fe36b6fc4fa2852b1617757b1e8cfdd7ddccc5f5986fda111cacc3e6a8099038fa5a1800274bafa6b358b8c7aa52f2804d01d57fa9f9bdaac7f257aee3ae6d7727d7ad0f23429e628793ad9bb241c56fba7d4de7c9a70cdc026aa4b0fabbdfd1f8f61a5c149b7ab84f47b6da0e22656310c794194b16ea2270bc29c43f6f6e8827cca280115d0e9ce58c9acb3fdba48131602e01a1e762ba84a93dd501bb8b94d49d23e9374199d1c9e5511c6a4b699ecb3bc2a33a2a461df05d1c7dd6134bb6e8586fde4f0c935ef3284fc09f8eb72e0cda9e6597b16f76fd79d8ff7fa8d8f5f9ac37dcd9d36f472f0e78a2092bdd4390cb4bc899759e771a14c3b64aeceefbddba821d3b946449a9bbc120b0e1281d01661088eda44275ddf72a352977fb2594f694d14f63ba0579dd0ea660a37055b605b8f572514c04c8b343c5f3555ff7becd765e0a97a7bd988aec35ae819d78d837a1dfd539bbd3464008d733241d790d4a1783418d1d9e30021f15cc458fa462216bc34357920a0b2bdbbfbf6399b1e6454456a54a38b7a358db0841726e9a4be5bd0f60e70b6fd54fc6c44f00e02bb129fd01e9f30915a87f673d898b620f82ecbe264291e1751dc4feb18d16570d75ce734fa6832972f4230650dd837f7932d5a525151d024fa69341f4449a833b34cc59859d813eaf9019fdd0c632700bc050fa0f32d18ead7fecb65419fa4d0aa5fd5fcb02ca3e03a8fb544424a47cfb387eb2ccd9ec4a1d1ddf752039159e8ba2521864720f839509534103d0107a02c9880f4408e74ae529a500f4b905e10d21192876703fa024a484fe2484bd77290eee15e9a9f76f5c6860073174d63bce2e1108cb697d462c9a0c522055dc97f47f5e4b7d533ac8cae6842376e51b4bf118200eecfec1f7eedeb372d47f8f9814db777128009c21df2a2b4a57654cf3471b47c764bf4b162ed192da70cd27fb9b9227002e8cbf93b47b23a2c73fa1e9c9ff4523a318ed0a5f76e6df6a4298b1f89c93a5ac87e4ec86b32dfbceac35ea8eb5e2654e10d45c17902c22265346b4a901a5154262e38849de34ef88b1dea14ba3adcbe8c3619c27f36e07803083633f962c49ce178f75aae8267c2b6c53d61f7ff6d924ef98e2b4b4538cbd01a04c29497e42ef47beae549cee07830eadd29a16dc715ae1657713651cf102036f85f958128f326c5dcc7ab05c3259e71a2450f256740cdb6a9ce46ed2042fb80af0cabd346d735503d964c334cb470b52de8c070be69b9cf6edad648d93ad80be56a4dba8ebc921a6de7bcf6995b88a9a981c34273a05a5cd36f2c64e5c1f1ec2261d206f963ed2bc66d41071ccfd926e72c2b19c22c6c46594799989450ad0669372e68b3f064c882e850ff648df8758fac28f4ea1ca47edb8a26ded8816e6d373ef03d40e788d5d021e5a23d88f997acde13553a57d49509db31783cf0ae1e42f74ce54dd47ed1d56cfa38a49192e10a04b1d4a3874ea0a491b3e1c6be3908ee70f8aff7cb0595451d13bf962c1953c8ab7633381696627aa7a92fa4d67abd95765a918f3145fca80b3b5b04b961cbd37efe7af2800d866d7d0d1930811cf3f1b8f26cd4dc56f420698346f4a1fb98a150cf9aa52aab16cb2c6ed240193a9ef7f0c571594034dcba31cf4c05655192a9ab4d44b89a14efc7edd8a870fbb4855e8ed2d1629910070267d61ec89193449fcf02dcceb4e912b40570d4d327b6008027938e9209875a68da2efe0237a6891b3ecd6419461b0bfa215685d4ccdf6fea3795ec0975c53d299ec402bbcb14adfdb5848152e8569529cddaf1cad0a1afd70939dc93de95e1a5d451131641e04994bb26a77385879a0d25b57e3b1f7d4183f8dc201a66de3b2d9635432c194d0f1d9b1012282699bfa3bc26b48abd93b943fdf04dbb7d031571544bc23b6c299de3b621f1394ed6559b9928bd4c71f487e54c498b292661db85e0fd6b4c344d5ab6a74ca19b220f8936100e7ded218e8d316c8b05b406d7c0f07520a98cb1bfec224a4a60415178af614f871dcbf970b733f5119da9f938c79b222325a807c50f0f5e510b956708b1e9fa6ae1fc1676565d4696934a5f2569186f7caeded7d0a365217ab151da9b051e741521af2f9267404ea1013e7885885c4d41b7e12369cbde1246e195cbbe4c539ab30baf81dd7ff841e161276c74f50079718ce31a5de22dea7cc469455b68ecf21fb61c8a64dedeeb3ce482158906c3aed038e5ac52d27732f87516ecb92fdba4ca50fe34bca92c3c7d2c37657142d8d769b56f4b96eb2b0ff4f5a55667d8d7ec4ee6c58f8f6a50aa1f0f902c97ee139b7ad89ca5ab9223d703f8bfc38624e960111c21790e8482eb80c2b5af83984d88ed4aa9b46dc543e7300ced0b7fa9273ab27f131f1ddcd5e5d8b6dd73f6bb6e86e1e02d489269d8a24d3ec8136c10e1066406492153ecb7d3502026076f7b975a78d7c2b42828e335c6cc4593dfdb4217dafd2da92cbeae1d5dc8e5008adb3e50c057409f6bc7167fd2519833456a9e1c09f63129aa942ea4b90150a39eccd7b2256cfc0e4de6bd5196c8f8a2ab1cf9a17728fb1f91549e8018cd8854581585c5c65da845c415136e421610ae191136ef0b15e083dc386e8e0bb11971c1fc4553efc74102a1c2670e6ac2203838e19d2c1a7d3f92ffc89892fa81194a863496a694a6997cd1ada9ba7434bfd5cba489d106a1f05a792cb62319052c35d371ad0df4242883cc63a0ee8b34c0e2b750ee1ab014876dffa13614e532cbc209f099000011e6e1291e643fef1675581c250929d27283d3db3b08f2d092d892127fe50b5c78e4c5b2a9d6d2b5bf98aedc2ca445e00384bc405a1636516f0caad3282ae6ab2512584c3716922b33171cfca4d311bae8ac541417c8543e763035fb025ea7f1e09017153c7281b76f7860d6c3098bf0e91c31ac6ae71e6cc6d8c0ae7761d70eb5300c44e824b8f53528ede13a0977afa46e3fbe9e266ca021848426038a88b0aef4063538d2c93148b2307f235fbdf3fefde9c2391ad011c9563762072310231a53370803c57db8b382c5669003d74b2d3f4253c9f4620806a605b240094601fa15eccab436de080509ac49a66b93934210fe8860d1aaae26f11996036b60dcac96cd81868835eccbc441cf1b39247be80caac0195bd88b33cedbb21faf470f2583a4ed6e2e599f3d41d99760511baf5eb6507c032bd4be82c73e8ea5011a03fba0638e20951837c720a894b6e38c9cab2ab11dfc96c1660172b9833a9171e5193fdc9698945add84aaa2b94b45a7ca0fc32d1b15eb2273f550abe842e8a682a82f1685dd3ad83796dd48569c60d21895bfdf25aea3d94b35647c8989946bff3b9c714de2a08c51ec614bc0e403b32287e4bd9723ef220797c65f89a72b80e81ff2f23b0fef9459d028491183d20b51019a284d0a2c5ca1c2e96662b9abdb53a694d96f21f40b64805620dd0a3c1d4ba28eac1dcf79e896eb13f005f21330306f432d119e19b28c23843fe6ae5d38448413509907e2ad7ffcefa83d7c59afd1b381d7cdc12033c3d3abc2b3b7588a914a2c600c7c670b81a0478f6c70780a9b24b2a440f190ec339229686d7723a332649df8445867a8c9c053b93427519c23b3ee59577d89fd3936741c9fe17b16c6966b6a9f8b7cd0bfa45a7456c133d3695fb44ffff849251533a074c3e18e617ac96434a1835c2d89a0c1657f4f101ac8292ab8d685b888725d74edd28317d2741481b99dc5c96014b69576f5076fdcd09f59e4a9cfc65c36f84a56d67b1c8c669b7778a65d3672c8a938440b419a6c80b8dcc28e3ab680b1449772f9053afa8a45915c11b448438404616a732c6decb44876f998e0a545ed2d3a230e380e9a9750a8467579e8134837080302a39e42adc4b22c1d7250b49a6cf3197c2dc27f784e89833e1a061109c2be853fec61a9ea214d6988f01ef3a9c3b061fa6079124d2088aa594f7f74fe96b52b16314c05df81340e255d5e21cc744ac3b524a0462539eb165d654b25856a70150a13cc352e8d06876c282a0fc95ebc540b768848d873a72e2a4702959f4e98e60e4ae4ba29f68fd1b0f5562024d7defa62363a021b7448eed2ef3ba910195724d94287660d1cd5485769a306858822ee2e65ee113892b94d0276fa14c78437bfd83f8e9d6b04b78f43072bbbf1ba580baab5d61ff1ef6eec34d965143db33e4e61799ae14ff62e31d112f1af049ea052d68929fe45d9fc5c946a0f312bc4de0944f320df28fa1da159b11927caa309f7fc56a57a08cc1389d8818ca0629f5af383520e5a238b0a08de933f63165fa391f54f4448686897a108fcd34ef1688abaa0c9d4f7887f04b3ae90ea0ba1a70bb6e655175a01c3c828dac93ec44370ed58ed9ee752a337369bcb8217eaf7868ef28c1ce2f415202073fe884e91df8a434273d7799fe918d2be2962c553b314ac76755f3276149af7183f22e77a8b0f15ae2bf6938ac3a2fa50e46bdb6e3f00328e9e350f236986f3f4c379384f3ede6ef0f5e3cf23f811e1ce61e1606b430bfc278446cab0cc019a44cc200ea7b6d825d76e5895bc03f0b8c1f404cf7b2d81d7c5b94fab15c9885438f1fbe8da1a014ca9436a35bf0b125341091344a073f95ab1d03f0ea5959f7042c6fdb8258e72ee6bea0718eea2d4b07f9125d63f538160a40e036c98bc3fce2e634d618a91c72a2e29fd396a68523072f9fba981084a56aa8ceff89ee9460ad91d3e5bbd1195531aa1d09911294bf6785a1a22163c1fe010eaaf698a7280b2fe3734d90d08aac92e3cc6736f23e13d7433329385779974126afd50d77332c57dd0e127724591fba8108fe1e2eb007d786c300c73c910aac5f3ee3b8ac5201f2cad85c170aa0563be0c7f5a7793fd585566f452bd0932d71cdd3c66647e9308f1bafb37e7596bf71f7693765a97142038c28f29ebb43f70c6a50385652ff077bb4b8b79248b0e5b979519a302b8f13578af2993106b0a3c2184e9b031fcd260fb9feeca493d0392e40ea43b9d7fab565b5f70c7f0265047afee665a6cf156efab6e4a617cee1ac4ca2861cfe0e429fd5bbca2ebd7f257562f4ffa4ea7fbf1a742d841c49ec69ede027d4491a9172dc9a35a45e02b29bf4db6f54a15be922526d58bad60b43823cdb2ca941743f3dceefd8efad45db2185a9607c7c8ec30fd0ff01674a07a33db9590b14279643a93275aec577f62f68b2623a1fed0c5b889b54642473edae81ce8c848dc646b5b640845e3020014c53af686a6927b2bcc7e64dce5eccf1bc26f328e8c7f011577a2716ddd15a573bcc30cd3fc811cf4ba67db42d65a49b8cfd5b1e55d5f4740c6f1b44e7f89ac2a9e372f4a90a70e2119ca58b4fd7847fd0a00f208cfa3e8e558070753ef994ccd66e4543929d84c2b576334931d67b46cdcf6bccb8f51a50e4f7378e2c5dcd9ba4f764de30e56dfe0ceb63d8854857a305d8970d15d7bb3c747ea8b5e6d5364a67e246d1b06104ab71902a8826be1923b69c0955ee7ab3318fbd27d79411a8024370bb5e47bc013103d82f041ff2fe7e62da841a0bccbf71cc6ab908f99fe522e43d490b75828b52171daadfad2dd2d47e56831bbc31e43daac0cf9f0869f82ec622ea88f8bc8b575a339eac1ba88af31d40291f490d5265de4066cbfd7ff08951d5d84e8052d2c05a029f4828955b2b94836ad2ba1c45c6412fad544f76b0d64882e1b0a23bdc760fb50e4b25a825c04422daf50ccefb8084f248cf7c2178169ade76a9f9cef60bf31e4b97b4e2653552480a62a104f310f88e67be202629de2720b01db14d8e2224206212f39f642cd0e82621ff6101127cff4e2f71004efb011495b636056538d9b31e5006f0f78adf7c09b1fbf73c7fbca016b8710894ddaaa03816ca2618a7079864c0b8e0b403cd9645cb4b3494c6a9ebd8656189fc02b839fc5aed3820a77646e004daf33bdd80d932beef0d7cba15da04ea6e1bba5021724ab02172f58810bfc5b810b9957e0828b97ad64b1742216e1b018859b567f16b8106388f844be62ed6603d70f62e6b6de263940393d4c12a7cdceec9bde0f036cb353502ea6ecd4f18eb5b617076a52caa203dae432166ad4934e75c551b558c01c8c6be6122a4a6786f15d8389266f9883c5b93e05ba9d371ada481e03524746dce5a273f7a613944afd8e00cc5aa9fb35dca909d2e69c893bbb7a3522f98cf264a3c0081cbf298a15429f7d755c7a05c5ec34cc2e60931643fbdf50690ba3f6893dbd64403d09ace2999eec30ecfdb8ab433f131e637d6561500421034b09a79ca9731b811405fa2852f293f2400a015e0ed6b53a203df6e6b4b9d396217b4e57f9d02b0846aa8e43f09c56e0cc2f0f5cdd05c29f5a24fead758c961015ade012d87c0f7707aa5887a7e5a89e7b563a22f8adfbbac353c61f95278a7bc0323e910114a3027aba795a5e4096594d07a163515486d4818ec1b4abb0d5ca6c2ba051306e1f9a7b446f0b9cedd3bde0123a620336ca0f6c9c0170e5f8f39d3dbb6c4780bd98009a413244175800dbfac60534ac10471b7be90c876bd1269a6c7befbdb79452ca24a50cea05910537053f4d5e7ea04075a7d3e98442fdc47439643e9f191d3441223d1031221253d1cdcccccca850416489b0654b18112516a81a542b38d024a1e9549e0c954ae5b1401a4f8613497a01c03b62657252a3192100a82d61384c21806ec7005e84d8a49410c08315a09ba3122a3d38d50e4b563bd3d59094d4853041c3243ac28df0e16606775a6b95104283b556488d6dadad752e11a282226e902203215078002064a9478b212d1a6892180264cb253d5818a1236ca2061008397e08126eac1822a50615366ccce4d02173038e181d7a6060d0e3059623f5d34487cb0f14b5d65a6b0fba6e09fb9122478e930f4b506ba5071d2a463974ac061391256e7c44947e7835f8e870e0c125d9b1bd7c6847d8909decc03818e1060e53f81877e41885041995f88c301b46257692a8778402363e0100210a30b4ff0528fa861e3792a8370cd9e2df282188941b72cc1b7a5021ebc486b5d6da2436ec20dbc0031aaaddf1830d417818d284ee81e68c5ea0611a0a1d64d409027483eca3c427a89b7352ea13f331aab55a0b640996618c73f689e203846ddb382e482bc84e1764d49e30d231d03e3c347c5bc27c7c70fc08c096df690f63d03e77ed7357523a69cb37c14edf016cf9ab3d3d164c475758dad09f75ce8ebaa8d9326646d5c0f6300b71dcbdb5863bd46548dbbf36d9fe1669d70ba4c4458130c618638c31c6f8047bbb5af3bd57944f58dab67555e3bada755df57c4ca6eec864ea6a57419595bab2e274ae9838ade2b156569458ba0a9e5686b63f77f25827aa643b05dafe9c8b8743815ee8cb4bf5f912454989ea2829cd8e8c90624646afe93381c6d9337b664b3e519ac9249984c209d3c39146db41f184ed6f4fd8f5fedcb78612c4dab679acd9799be7399d9ed771def799be4e6f269595159695eeb4b1a05a5a5c5abad4cbf6f2e274beb8bcc0c0c4c0743233dbcc8cd3391333a342c50a15dd831b083a9d6056d1d0b468d1551000abba5a399dabb07b0fa7822c3cd6dc5f4195c762d1d178acb9c5d9826bd1c2e96c91bf9819bbd2e42f26ef1b8a2fecfb379c2f253500120a8408734e4a8148f1c9517d786415c2c95808f4dd39260a3af512f600ca00ea4294494101285017f24fb02dcac8a8b698812cd9e2093a37293d8077a2feea556f95a8a103a9c6105cfdbaa2c90e4190868044521131ac0e1daa0ea2f155efe1576bad8e1a4d3210288030b1c5bfd16ce028cd3ba834c01140ac5031231303f3927269419d585654eed20d0e5fe268e8f974952a3a9a3f9a544d669a23a809cebdb2aca868d3e775dca6657c6d750a43f292306490ae4dd024afa931783a296532e42d38a2e95921f110119ae2871a45178663e7470f291d17920ea620a2093b9708d88f17c07ef8f88123c995c0bd116fe44dca6aaf3e5914d13ee89ea31bb8055a89c6a16709dde52003fd835ea27fd42062812ed23ad0e20ba17b0461867f558e628b7fe32ea83c58506badb5faa023db8006cc638707120e1e3db8c1e3c98d5a6b652144b3b881ee31ea2471b784f500daa27eed09e9d53d94b9eae0d0dceb0e1c19093d97d8b841d890d9bd1ed1a05396c788ed32bb9d0ee339c2dd4f29163ae898be3c886c1f337052e52fe491a7fc5db81082bcabfd9e7adda76602380d94db82727b15da49c5f0fe03b5d003a54400f6aecabe9905a035e2023675316be81f60e24c5904f640fe7ff166674fc60577de40fc1e68231503497eceba8753b706da29ad7f96cb9d90f7817a735b674d3b8355dacc6f67191c9572d2fad9ed446b64b667adf4ea87ff6d8c0ddcb683deeec0ec351a286b8c4cb36fbabd7ef7b7ed81da6be67720b72b3882bc4d19ffdc37747065c7b28b9394ea38cf8e9d429d72c837d82541f01c19c173844f94e429c2e6f1c109c5d384a669dac6e5c042702ca2cc9959603f1f2eb601ceb0cc21693dd829ba9a3c9e608c73e661846dceedb7ae7370bef4648fd68e77b3c77871702ca4a0f54e4f4ad9b303eb2105ee295202448b683e13ec39da29588fd2d640eee101ac87897c5a6badd6fad0e9898231c639f7247d3db3f301f3a32e078f071f42f898901feac423656f09eb29b235a025cc47918f156c7d4249183278c4231e4391c6f038f317741c9ab96645b3d7ec6836675172765aafd6fca31c6bcd26b725bbad27b3357d46d91c92b96445b227ae992b8a6be6b8e60e0af9a58f52b19c9616ed43ed6f9e20e7ba340e9c0821e90814484f72d04eb4f84568a019d01290127d8e9b1d65a2eb5b193502ae2242fb4ec1bd86754e202427fda78dc178a0be7d4fc544df5711a1ebd3e7a694726a29f81693993114c65f6aea94b3fdbefc84f9f9732661c4fc949130667eaaf8997fea8debbccf14b4b75701ba4c05587f063ce924c8ec19b0be0c887251318a59e9b1fd65c0fa31604b0f18efc51527db3f06acefb2e2e4e5fb1730f5302c4952a67fe146976c632d3be5d202ce99b4998f02e94bdacc3f81de42a9bc6865d862d2c6575e9c5b8eb45119a1456e64c1dcc8b2028a35890a286e4cc49a44944cc459644f23889406ceb3e76fa0e84f546b63dc688a4918991bdd9e642697039be3b2974b4e369b60cb766aa5acfe84cba4cd7c994dc1987d3b0ac2ec979dda2e9ba3a1d4725831b39fb7f5de03ab2b02f55b98533a8fd997e173499b2d2786daa61c550eb4783ac5240c0fb43033bbb2616630a3b4c9d9226adc38d022cc6cdc2b4618943ff7a71c09c33e6a77a0f4df40b126f1d74011a6c8f6cfa79858936c71cb41d915b3da92b171cee8ab25ba6c068a4efa892650716df54fa4b182dbb45c3ff104afa24d9f673f110560caa5057562b99f9882f057a898918981c19f08038b9053cdaf4755c6678ccc298cfb8bd7d4cc8e217585d285dab2ba6e8c9394934fa594524ad90d0a293440050bc09098a8ae93ee729d7352eaa4dea0904203548021c94e7aacb503d5da0c70e008859bbc012d5f3a5a5bfc0c7400031c48ca9dde3425a31ac0096f0414f3d64977192c41ea04057dc9a44e2009524f663229098e404608739491a008a0705c8db4a1cf79acce9b2f3b700514dba294a96a7e2561c080a2c080049a6d5f02a73aca7455250b26467420b5111eb8bbfb3092b48d44a18d4c4103a09d27403d408072580284a402c900a80787810e12ed456ddf7be9795ab57557b0db1ea8235f48b4e7a60e928a1f49cd21d875daf434cea774ff720e85bab0520f637e6bb266fe965760a6b63d10d440a12eccdbc1296de6ffddae9433f40e9c35be398dd364d63e1186bb3d1005afe164473f11867d9febee0d5dcdefe691fd44b06fc8edbf49b8945fe57ba514d3f8684c97d4a1f9321b610233128319d9c1c88fb611c6c8a7610b978e99315ffe0cc318e68c0144cf694b98911b5be29795c6072d6aed96d2b7f7d200a12cb8eb83809c6091620643b4c8565a2e5cea48fc53db5e43aefc459741ba704919521aa1f884159cd9b6876dc964b4f358a87ddff668512b9359d90e75e9c2b6ac8cd6902e300e99435de01dbada71ff9cb54ea51ec3703bd48595415a9f2e835c4d4a690be3fcadedef7fc10ee3f078e44ae9a1a492f4865b9b7f2a24ada26232994ca69cdfcafc854a12adf1672227692829a55f6d7faa22a32b7f16516823be978481df5ddc78eafb7749f485a45eabf3faa2d7b35dca2ba5a0d08dc670e8392ec356b540cfbf2ae8afb4740d36f851a40639cca0c7054556002bf2dada95a32a5215a98a5445aa225591aa4855a42a5215a98a544ab40cf764b45af6f55229512999cd5e2f95123aef0f17db273a8e2cb3bdf2da2ab3d5d25266baf9b67c950b62d48597f1cd2f6c1aa3abf91569b270328bca49dd9fb2249d7308479fa3200e67d22e013f9d6464b43eed6ac2a926157466a899219b64d4a97a813ed5e9e14899b5cf3e8e8715cab49d5377afe149a39bd220b40b75536ec3b450e25eddab7b75afaea27908457478111ed5b6285d6bedbdb9481147c2fee8714cc2919e5284861d384987e490387ce69c53c29c071eb62da58d140245c8d29096bb7bad4378e85896789f11dfd6846908107437c1214850192265e5da60f9729cb81b4e5bc27e7480fdc0c0e507960af75740955044ddf06d0a3f503481a27575a0689a89a851a411db2c58ec76948f9f3e0becf8b5d0b6640dfe16504a8c3fd3ecd02e5d86a35c27cad560057ffde9e9d851da1cd2b6e48beead4f02e6872e8440effbf6458b63cb37b564a6960b2dcc29ee1b989fe36ad76d2dd1478e7b1943cbdf7519dc32f49dce8787cae69c4e3b0cce6c2b6be8e7fcf7c5ad55eb579e4dbf574b2549452665d249279da6d6a66f6a997a4c3ed4457dfa92c63c67a5a5d11c8371577c447b499bfa34445adc7c44dbfaaa0dc56f885d7d76fd2369535f05d374f3318d3d9837d4e7f4ba5ad115b4ecfe4e59dbf66dfbe6b1a646bdea2b78c5bb3e95d65a6b27cd5f7d4af369ab98a0c56ecf1dc3969e0cbbe50c4b9f3e95362c336c583d968af6d97a790bb3f5587ac60c6738a5cdf5582614b246bd066c9c4757c0f7cbde0a54f3aaf4eacdcda6de04b4cf17d042c9c2b2d65aabc7aa1f818abd1758dbaecfdaf6145a2998a58d6762cd97af859ac7e2f686ef96e5c6b22116c1f670caf6629cac59cb67c92d437b63cf4f8ce167b430bdef7e06eeb79f5e8c04bcef7e052ff4d9f257e0c267dd5d9f75e7f55a9853fc394a433929ddd9dd77aeb5d69ddf5a6bedce7fefbd177bacbbf373b8b320c618638c777e151164b2d92c5411414f079c367e6ecbdfbd21fe1c7226edf30c2db439c49991f102ceccc63f7371789f8217c4b95376956f77eee6a454ca557066abe02f6c8eaca1d97362effea2cd2a3417f59c1d9c5489efbfe59680e9c2633debdbe7b0d61e832dc090a3813206bebf8135e69418db73f75efcb580b74f5980f915bc7b03b5771dea93a310ca91c2c5848901d25d166db1765e6a89932fe46ca1a43d5f4f39b433e423a57419ce29f2a58792536826242454c4e690253824646d702a1fd43bbdfaec98735277e1a15aabbd47afdc618c71d67490e9d084ebb66ddb384e071d66413b571302a77a82253b4fbecc19b42428c99c93521da204c1a413310c15a167c9ce1344600214316cd2b69ec3071cae53faa4b5b5d6d2d024c38288080171c2277d9913aa239cb851a98ec8d9d1e969e5ece8d89c698deccbba6c9155625d947baee5d30334b67c7a5a4746f775676e396badbd168fa07499c4135b6e419bca2c9fbea891bc2fbd47912d7f8eee84c9e69e6ad5ca2eb633cd266d75db9ccecdcaac87d3f2e9b940776cf9f4b464e5ee6c1c0252e21a878046dc755dd7755dd7dd9b2fd2a6afddd83dba467445595807e7dcd98d7295ee8c85f2c6212025ae710868ec388ee3382b62ccfddc1ccee306a464d3e75c1dd057bfcfe9fc46cdc3e9923a5917eb9a74485d8c9338266bad5ce52438374ec2321cc34d30129643405949768d434054d474726a0d3397679ecea6ffe5987294ac94672cd4d69e93da8bba16d38e36d58ce88a6aafae73d94638991b7726bff8d493519b0bb545dc7cb69eedb4b536d926db649b6c936db24e7b790dfdbab552fb7f6b6d2dbdb55e9b832fd4287567333c9b567b69afacbdb497f6e266cb6736ce8666ae59d12c168bc562139c3038d7d9cc5f60ad53a9ffd56acf6b5d961bb7fcd271a76d9ec73ce6318f63cc6c06ad4c166a678c33c6b8e23ce6d15f743a959a79f48bb5a7f0ccd75eeda1a8532bed95e79c8e63b1582c26b3e546cd0a4a79e79c33e358c6b1996fd8eb76bdde0edb2d7bbd99735bb98cfd5a4e2693c964a88e73c92464415b6b26417b51a3598f3cba93d4cb04e7e640c9cd3d77983d166a7badb5569f77eb625dac8b7d5dcceb625d287ee85d6cd3dff2e775e166da5a15d38cfff33a1c6e7e6fe7f986d8cb336cad775bb621cee3a63fb91bbee41e737fb9d0865ad8b198428b9ccbc5b9b40df11830242d9a1f9a1f9a1f9a1f9a1f9a1f9a1f9a1f9a1fd76abbb57588178a2556e7dfbfff85e2dcdfeb4d65dbb66dfbdef340b9849efbfbfa5da7a2efbba993fe8173bbcb169996e786d47e865ec76d2b9ea882e80fb86b7bc0fe7d0bded7a47d49fb92f6355f5c615f5315418b7a9b6d8ab4ede75f1935abc74d9f459bc27b6d53ce6e0ab55631a1e73659d004ce7d67b93b6d6dbef679afed4695e5b267fe3bab234d0f5adcc6210963daf7ec77d6fe665fdbc66dc5155bfb0ee440518b796ba16822123523d3c4407364a252f1a05695aa09c72191a0c025620256897c4d01fbe268c46aadef218c072540301e7a603ce8d088c598429c58cce572b954482829d188599775b1b47ecff35e7b1c8580b6b94f81fafc0db643d2dededf148a316c93f734623462d64523a682625dd625b2605d20988960a411a34b2a688275d125152cd957d29126ec30c30e3aee112486b4a9091fe80be5083a521c017284e8c80f47823832c491228884202a828809221f100d81480aa22947768ed870848748c8861321d9a13710bde0871a44406c513fd290520d1f52c20da29c1651cb46251a897ebc38571a5202510e516bce20a59425d8f245bd7d82323a3931f64f8461db0f80f55a984d60c2941d82fa73a740e7ac41ddebb77ef6af8a7d41793f7b3801d82b60f64a4a232d6abfd7b77dba6d285660d730c76ca9a216881d82f6d14349434345434843b56e296d94b8322d4407e921a11aaf1a3af259ca257690c413b11e7a10820c62d41fecfa3a881d42475bd42f44941fe81cc51029845a3628098e19044ba2ea93694b58500fb6a8df8d2c0c7410da220111d235d07828077a077a053a68e75f15c4435bb256ce3427a59e33a55402390c1757eb9b23d98b5dcecf7352ea5eab755cf3c6558e733a39abdd0d73b9ab98cb9de66d9fe66d9f9ecf993aeda97c2b2c9585c5e964e954563c9648812438a95214a599d1911152cce8c8e835a9d24c26d5271838814ef1041668a787377252a75eedbd4ee7edaabd177739e3ace170dce6e1705de7e178ddf7799fc9e4e1a8a8b4782c169794c742752f2d2b9d8a8703b278acb9358b8773ea50f51d942da89616a7b325dbe99fe801b05ace8262ad75bb751c974d631b7e0d638c378c2bc618632de3fa1ab5b586b97acb05129b4abbe3dae29f6fa9b6042bc37624dfc199fd45f19a6ff6cdbe99b31459c2a6f59b790d651967b34dbfc588b6bca88bdb0285b624d11626680b5228b6c0a46cfa2c2c2c51a80bbf16d7ad811b8cd6d0ed88aee8b6c1e88ac6a40dfd6f66caa12e44fbdfec53faa27c52622c977dc33cbb40b4bc7636ed618b72894ddfb644ae852738b3b796bff85edfabf57ddfebde17bfd735020d36fd5e7365c6f2ea69695117cef26279b1bc585e2c2f16162396a38c2be83bd266b66c0f698cae5aec0daebe5459fb72be9c2efd3bba12fbf46f0816accb34ee5049514da15a822a089c4986a2756debe1dc5db445ab82a2ef37406eefa747a9e7eeded75aab075a6badf5eebdf75eef39dc75a04a496f8fb5962cbfbc35db789cc5e628de1cdc852a257d9f72de6bcff2bacfbbe35ed33e6f2d7f0c7b46f7dab3bacd25a58d16d3bdf731dac7b0bdf0852ec499d95c286a38333b87629df46737b52038d5929eef4b4b4e97967c892e2df9d2125d5af2a5a525bab4e44b4b4b4b7469c99796969696e8d2d212751acad8d3c37959439c994d43cc0214576dcb5fb8fc7cd1e608e79c93db3697d7d0570de44af3b72e2957f75a9105ebda4a6817b005945e73f4744559dc90ab09a06ec81afa262564cd7dd37d930c15746668f919502ecfd949bd4a14eaa50b18e32d608c8731126879975fa1e55ddedf254401120f5770f954888297777914a4bee56388f102a25aa72368fbe216bbdb1f15b630a7a076240c67792957545e4a7bc3822d9468ef4d4d4c327fe1d2e997d285495c6d31bb730245948fb862079b0ea884ae3517a9543402000034007316000020140e898462491084619cc9d80714000a65763e665c349b0885b124ca61180652902186184310008600830c8d0d19100079122883d1cc168113c1f2811d0aba3ca64d0a05d64e12bc6b1ff05d48a12af4ac3770637c94ca5f409c4f7e1e0d0f4ddab2f34ab934ece8daaf123991633243107a24e8d5103283d70615934a050d19ef7f1456024007870fee05c32abf4c92b30efa65baa3f7d24d78d0c483914c5b05a20d9d6bea3034b3309a0a96635c228dc522f7d097db05aa2d41349c30efa2cf27306c4655fd445ab220278c44ac44f0960f3b1b8a0e4ab6047129b00a227897aea47b25ad63b0281c16039ecf01cc8e42be403be0b075b31b580486ac8e2c2b272458067f7e56dbd5bfa08b236cbaf8c0c978d085580085b7477884c1fb9979ed8496e092d0b5ab60076b8a3f80a0b4ec30203d9c104f1d83d4bc1491b5ab0d905f289600f4c0a108e8e4fd18514720a7ef2941175c31729ff5f3673205b62bff41b7fe66fbb040818ab0045415067c14a09497146ead9a9d7849389db470ebe777402946760ecfe34840d134b5ac10e90ac4a1cf20e14677afec6061ea76787220737f2d2a1881e10e8837292153746a354fca49b421b9580410c0fbff8acc5f1103c1be9040f0abfb0dc3efea84fe523c8c918081de5e384f2701cde25c2b8039e50b04ac2070800961bf8ad5e9af83193adb5f20187a08de04153316b2284e6c250df28542a35debb0ffa248fca4f3a4d66f563049679fc18f2fd590c6558bfcbffb9ad38fb41e08513515126931a87ed5e7b640fc9e87735efb0bed388ca7de9540ba26fd47fb58bf5cef9916e9bc5f83bb9950c555943db92feb1230901e542f96bbcafc452377d25cdc74181b5e283e447a432826794932db45f564bed49319c1b8701d7a4b951925c50cb180466353dd4b0e25cbcac861b8f1fa211cbb74bd4b6f81021dc1d783b5a1dd16a1da081e1d076c6bd38a547a35acd5d5d62d35ada5ab01b60a5f53c6824e4175f874c0184076b5394823f5316b0513a887785e3058c2bf02f570d836c779ef01fe4aa1c2c24f43db54dc66580b817a30c2c78737ae310a8496a66a5e9093a5257d6644531d2009212beda2a83d162f073805eaa16ddfe404b555bacf83f50f02f540c989912fce49da0497e9c63abd26414263e4e7ed15c615a281188f41e77622fbb342cf5ca509d4c3adb7714025118dd138e711a887c22b4cfe4e8f9a3e0cde7ee17e21c7eabfc065458f280aba249c0cf030f7aeb598941d8a2818a9b7b883e71512d317724c869f5b74bb54564b402313cdc8d1b509d4435f6f82d98ecdd5a422e5c479c2640880b294832e9138c9ffcf1ee532e2b0d86abbf77b6cbb632f7dead12f500ff355cbf49f99d6e1ac2c902b730509da54c13abcb605c335f5e0169cf273175bba17dc51421b52c3b8e10ceece527dd74ce6a4a0dfeef91e9db57bdf8a2aeeff2bafdc62f20533b51c9eb33c14b9f8695454413ef514cac1554e2602fa16f3abc4c74532a39327815db9370f0d513ec6c532ef994edb55a5e8d0c2c4acefc8dcb7d7271ba47d3c53dc3c7336a51cc1e93aef2997947826008920af80f375720f866621fc1a338365ef22b2bd8b57c132f6643ca2efa64caf4d228970f63f676ad533fb5575e475f35e41a5bd1402c4704a31b2acdd84b52b31a2d9b3209c656f5fac7724f5b53c4fb0a35bccf531be0907fe882df53c6ed401e2d5e9a9b0e6b2c58390f0a04b98a18b5891abb35aa9bd3a2f7ac60a71abd6013baf52505c200819855daad35b7ecdab02fb46b64e725395ef9d56eed1592e89620e314206036957a6476891aa004de53f4dae8ac4439c4dab7526057fc06fd594717d41d466426c7ae9282a8423c1dc4952ebc34acce79383c34476ad2ae8d632aa931d013cb5db4b8268850d834b629c5dfd508adc129374c33067c150efa1548eb94924d89178b2ab223ddee67d66ea12301e9fda1e8adbab9b7dc09aa50d9d1c95588bd464725d7e38c2e0d07e6459456b1f88973d6c04b416def7d58386547f77d45d2e6591ba34a27297ce5daefcd09db7f32e62eb30ad39ed0ff53bd4a0ee1fed6c6e6008263c0be1effd0eb8da475190a07c3fe7412ce87c5b8882fbc8678442e5e22d5fcadbe6804177c0c1a3744d230489bb69e194d10f8633c3a50ad2b9a728da893bf5b701442a877f8ad459bf82e0d8916d4d0b3f781739052362412be06a040bc00cd7991e815e5a96394cae9553a65df7986a1615a4b371d92b508419b2bc8ae4e0f61dd5d34e775dc7289ae5ddd781236423593df887a7dbf9c685bcbacd6ebca822dda49db834d5a3b2d902a58750c4eb4a87533bbe9c85ff01303782d44d8f1976bcdddd406f9c71bc6218b833828058a7dfeaf96bbffd0b986c8012107603c0d53e070c1885c98b6520d5df0ffbd707bd324a3f6ab01131aa856eeb80b14be36518d9f0736578c0d1ac2c6809370c14948f056f4a865fcd75947aaa0fdc4b7c6bcbd258bf081f3ace41898e1a36ac532385498315033b7a01e6a951ed754b7296192e4abfc1ba81f4aa669c20c419326ea19130798c46183e98178ec57e1240c2afcbab2024dfc60ab52bae68e3f131b4996b8928606ad8091045cbbcc3e059e135dc704c3daf1631014825476071aa5dbde1dd4c267857ae112e5a8888095c26b3736d242963e4e082ab91e3a9c387730b4df08cbebddb0ef9f8acb00f77348d2217dd0caba0fa5ab7acfd6fd39674e26fb526550e55616beef0df75c6902a98ea6c9541ef3f9857002492e8a5ec61b1a286bc2914329cb0b97c3f219ea885271378fca8217f4eac9ee0587092560e3dd30b4f8abab175671a6e5c52d22a5eba90b92f2f77d1f00107c15a7ca57a8c344268c76346c8c27388637eb5d4f5a7f28401b38c29d3dddafa5efe603b2e7fc1ad38c6e866f185c485546f1489b0b0b5036cb47777b37e9bf0a3f068b21bfa12c4c4a1a6905370241f7538afe58ba19f406160bfbb81709d66270158c9c11cbec9b85494dd155a911ce0438a3200b614f49162ffbe1f6233f97557a0e1ec6e2470ae2c668719aad0505971501327a4603a7a09d14c379f8f28eab0b9b83731390b6e8fce4ffb3370da806e03704635b4ce59dcb9a89da783547609773e8c404f783f8e6da54b7be422176e2cbbb6f9341eaa2374262dd5d9fdf7c7a771a81be0fa1fcf409edbcc0774e4a8b3d8a0cbd6e6549c58d772c7c759db953e6b00206c6484b2bea2b4a2a10b73a8dcc8f7527588d5ef6f8d62d823dc5cd69e42fedaee4f843d4329374dafe114ba4969959ba8dc6b9e4cba00d15dd071c8aff4cb451da339720e07d10dcb4630b355a8400d97bd95706486b763684829d6d79ca2acb8e50dd8b5eea4e58f08eaa33105d29df31ce0523b10b0ad7cf619f034e16460daf0a140c93cb9cb64edf7880786651b1edbd96f628eb9d9a482d3bd5b5fa010e2d8819e965b8a5b5cc0523cbdd8d8d8b0e04a5a2ae7efcc97c685df1610276c6ddc9e81407f4743dd0b3936206ba1fc9050f1afc2a28b2515aceba4059840c8a5496f615fe0a63d4a9a4ca4e92cf7e36552b61e43e6dce4d68fb4f5f840adaaa8c644d281fbabd42df6b76629d56ae6f6f0b773032dc63cbbc625dd47c8086afbd45d9c981cc9ce4da22570ecbd9354b973ead1177a8d5a391015aa3837c78285b0afd583250ff1c5922ed42996f90ec6b2cc7233a16596bcd65a6600c81cc3c88558eb3b3a4527f060593439408316a4ceeef0a8eaa7db6468f33f7bc9b4987b38fa6cd560fd200b4f68a1f2334130a888690e7ae32e663f7ee6277bf1922c65a1546a6d9d331b0501edd13f4ce7c1f751d500b91f69c10cc4f96cc89d5a549b4d2ac8e17bec99a622aa2e73db4f623627224d89b79b1f57fc55e81953c888d6789a9cd6d10ca4737f56bdecd1628ae6b381105878d125a5003e936efda6141376b4636df0694a97b03850bf31579d74d86c6662d8990eab7172da27da981af067533a4023d0e9c8e8b106cc7d7795aa8c8b3f0f1b018ce429cc23745439a62950ef55dce8c078b8ad1f074f0c346cd529abe44cf2a7630c8c4711c48ab8699a21266882cd5f88b6dcd1287e49d3966619235ddc084b9a0114d49148b31575eb76ba175b4cd37b78728d3aa501e182e202313aa96ea2399abc95bde7b5578a9bdc8b7db2309021b50dbd8ac099303849af2fae2df8b2e4f7d0a6f886ae88e8015042fa31221d4a5108b02bf6b565987234b608222907292c1bc241bf36265046e0f246406eb3e153eb0c732e0a0545ff58d11f14a5145470eeb8f89ae67127696308ebc183120a2670afa8e839a1bf6f2d24306038917acbc8fd26b720810121b11b84f66d921b26c44f6056afb02ffc2f0c732f52801627709f56269e1c7b574976bbcaaf026012d8e2217d2124a14a6640df3fec18d91b782dfff85d862cef10c1ec478a60f9fae3e1aed6537ad67014743342369f229ef68fef76e9360c8c08e7585f9cdcc0e90570b828f4023d66d7492667408052a06ade0ca2b732745004b3511406f22d57410a478ed89b92c1eb1ca388e0d514236328b1dcc75aa4aa0657805b413c21d31a10d4cf7e42d785dbe26112bcac0b6cc641257a5f89041ee6c705a62ad9cd122f859768b77574a31cc4b106aefba3993777af58e41f1af8d0787741b82f4d90f1c691a2970bb6c1c21c446fb4d5f741a769ce116ca4c0cba72dab87a70e2cb3dc8f21c064b3f06c5a059a0e4281aeef37ab607e351e3116e42bbbbc0ca783e860dc740e54b7b62c8db156f1b54d9f92d85a6d0b4e8c7d1669ef9bc0d8a0c82da2b969a113d6c2cc32394b9772b6bba986c43382a8244823a5dfac2dd2a5f0bba1f7273214b33c1f145120da2cac0677c395401bc4a4eb3ef225ce624d61f3cc679e497a55b96aef78ecaa1688632ed599e06b5845ae786c05bbaa61ec164c75d438a59538fd74d5ca5fd057b1b2cb00632584a569e14da59df4c2ded0e968e5b95bb4746916a359b99dcc815a981081fade4aa9729b97474bee941c18c34f153a4b889d5e3080900f02dc63d511e4cb1f4f16f74d7a5a61d03f8caf48903ff8fde1b21239f4f24c797ef421961f505c2ffa0fd6094cb9837dcc474fb568c68d118cc845303009229858702f5c9d2f88881d40f8e130c070ddf47a485195c1cfb786c115bfef6da4742d22267386cada57f0226904c184886ff7d6b0db32f4f4567ff4dc9d29e34e256bc8733236f473f391ff8e3d7b7adbbf24b07407dc7f88727f20719e0fd909faa08fe829f0e9c68a98943a3e3bc3f3a2310f0aa4ea08f9c92ac406d71a5cc57b35d7fc3c6894ed34f791fe0bdfe8d8dc4b67f92903aea9956548bae2ec8045262dfb1514ff59e9467a2d14d389b01491bedc1230f10adc801efc6f836122f501f30d7751c588ffe6c063358206436792d92e3b1cb478441a743cfc0d9aea891de00c341d0a8cf1efb47568e1b03688aa8b78c480169f27fafd940f655da01d0ceaf4436308209a2b6bff9f19686d5324e274c64ab8462916a0b26396b322d57e9b1853874ebafb85c5229bdad85a7d5efd92b8eb441a39cc8ea3c35341c501890cf349a93e8014aa5bbc4d130a7531cc5fdc922a175e6861b765bf77a59e5a638d97f0bdf1dffe14a5c68c18b1f01ab27dd66ae405acbf2d08b345ab58bdb23a61fc66120b0efe3156b297d04e9f4f940468db25a8d3f9dce088e6a22740e32050ea0598ac12ac074f0db0f7d34709fb85a58791b4dd682d9ab38e1e4cba52e60e9d2057892e0995f08bc3504281c5435daf794bfcf0102040de620c1ed220aea8272644f977080438bc8cf1fee272ec5d3c63fd19242cf5d3eaef2d921846a0518c870490b6802e6bdd8a665dc34b5e4601e54fb8d859084dd04b2a85db284aec0695847ddd321ebed3aa5017641b721c908653a138f6f82585b984faff85b04cea61c340ae632d0bb6bafdc5f3d9f5200fc0b10810e496c46c41b1f3e36331426b3727151a91ed440755d320b46aadd203aac70d82bb7853f0f9ce5d8cc04b93bc70e3b9683bffd35662f63ab6f9bf6be45c1852e68e5d14828d9b135ec3c293768e80f3dc0cd7b04cf91d00d0eb313436cc17dd84ab513fb84bb471da971dd8d725f93eb26a7188be1601f0b47348f2e55da74cfb87a4223d11e8ae3685faf39dfc82f318e4719e7a25ae9f1317fcceb6da6794daa5bcf58070349207c7a9b0dc09c06814a7fa1db962a00b344b8e287d0880c8a953b472bdb2f354126348c31674f20ed0d29a5c9a194c0c10f7eff17964132016fc23619ee2743736d029b72cd116241bf6fb16323e00e2c84a2ac8a5d4815de67040c587d3031183cb046ecb554341c65004889545184a1cbceedf3b00e9c312948b518e640658553a05e9afb3029de76aea2ff1104782c6d47667876a4a1fb053f8f4424b8960f7c332424eb52f724d849c00c88bc9350c817ac0298246ebc44511033e62b3e15b6a1d862dc7365bb4e08af6d7b82f0154b09e9f48326a7b880edce4c12937174fffcf3d880ea80ad6c8b51d25896831fc830e347bbebda15f1cb2195f24d2ae8487c7f65516228c0842f8774e43ebca5f8300e0d111c701e140198191eba88414b8fec58470180b1a3a06a7af406807657d2b0c8b4c0a86c58652e930d1015830688993fe6835d669475008961faeb1fdaa477f70a285d6c2d20dcc571c7823a240cd4bd0a0a0ad9c57c480be56bcc0e7304ec08d37c28a0723bd3d5dfec6b81105261f68d2ca7690867303b091a92cef1a9989109f5466e5dea227e5cc68b5787d31cf9708caf9f1f27793359ca58cd2de18a61c39b9224ebece540b7a91d1883308f5160f66031a6a9595c0176c5bc27a445584fc0f87015e1b8c43ec87aefc1f30e7dc9b4a64b63782e48b241c4bef5598bc21c448095ef6a95e6c921c3da4c3a28c1dd452d5feeb48d284dd8456b183de0ad9238bd55af2fa3424618bd2591a41dde6d1c7449fb215ceb41221d73fe423ecef61805692599d77624c98ffe1a9ac13b16f3251702995126ec46ef907817816e22a1d855cfca565acc3e6b42a925edab29328a34903fb36c26d5bbb8b509825dcad9fea6916993dca15bda8145c4cb94a9d98c7ca0e04ac4377ac31d86835a74b50465ded0835e8bd595128fc0571b0d88a562bb99e8f0cc6f5279d4aa205e54595c73050ddaf29000385b24e646e06e8b95828b3fb8ed201ff164295e32f0b71a66d0454964632fa324092c6120544a5817aaa194a2fbe9328a0287131d6d1d4a5e77f527865264f87067b8b9e7af83e69bdd078c321d21d262a22893e46f1d403f0e8f47c136d9c68ba2d24844e74443a65580e82634e5aa435012a9959488c06d08d6b7d2104ff6ae41d8b58513eff46c5dfad314389bd6b1db9132b48ce2dfb43001d9d227c236d541f567066d21b8b049b91660cb83691bf76f79845e5d092742e92a902a79136bc87419e505e2e0d167d26a05ef4f4f7081c0b68730774a6ba758ea0212ecfb90c597797ea63b5abefb489fe9f12289f44d7bd50872304485099b352ee1edf1d5016d7867fd4e4ef5116f41825404908706b8ba63e121ad3379e1c4ad648295d6eba316f16ed7689943f052436dbeea661af23768b2052ecd042a2d3dd52fc2279fbbb883905f580262f882e714785e3ab174e8bd0c03266ecad24fc8bf881f4717f77968041426b349ab739e2e18ea09e74c666d26257468bc931ea000160d35578539558e62745356ab6ed4b9d41157fc1cfcc07ea72542940b528e5c93cbd38722ebf1a2d1e797828b49f75b04da903a9fa0a6ac01c9a4dd699bd71a418e55d1de86baede78741928155fd52a0e69b4d9bc904419531e56ce2038d60f145da63a80cde2fd222bbe8ba80193316f5d06b976466408ee4c5c4bfb7cbbcd59f0e1a15088dc29453b16754369c558f55a84e30c159c1221d3a5aa79db93c74535ba301bd666177b3db92a62d148e7049499dec56279497e4f81d9f3e0ca72f84e3c6294ee258e86e6b6009eac09050148310af583b26e1a2a3f8b2a276bbb0448359e4df1c2eee57e8348fe09bc33780ee0003291c6c45a7b68a941763a1acf014d00523ef4c76fc9d970327782fab25c81e9e1db91922721da59f7c23bb841fff8b96684391f5c43cdef3675f359151ddc2b633a74fce09b58ab78ada28d0ff216d8a0a76ee9ea7d84e9b177612a52dc79a82131b5619e712b54bc6fa8f7018c8b232ad9194cf3799a8350ad5e6a9bfdc2fa4e17656dc2d697bc631de0021afb9626c764aee77eea14ccfc53669200dc1b1b478a0e5df9b62be09999635db2d464cd8a6c661e697cb3a920973ae7e146f295a27e31863021f3ff140d961a7d3d2c7c2e27808f70c85002c39bdbfe91b385d9abd43550fb221fa9b75f87dce7931c35dc2a9f49c66d42050a2bb3ed324644ab9dcb7f48420223c4197260e7c0dc1b8c1f3ca92d001dc0e3f41463a6ed1ed62ded950bd6a65b1dc72ddc46a7696378bc7572346bc7797b13970993982d27af32fd9bf613a9cb5ddb644990f678ac0d58ec6d7504f435deb96da8f75419681753baa5fc238f8a91f1dc402688ba687b2f890e890a00e1e8e9a2c02f30409ca3090b1e9ea2d0b46a1c07f4d6183b24b62b6edba7b6c0e93806f8e689782260eeb31d803864f566905a5a1d2c64b6ec6811a00ccafbc5aa328c8a367125a237dcff751b9dee74a7e50b2cf183e617d8c77525d3d9817abcda5a38ee8101f04406bc7a67ef792b8541ad5d7405d12398a697c25e34ae591a5924b6f19061bcde1adef1e4ac04d6ac0b3d509cdefed8c72bad082422ce72a78417be58a9bdebb28d77711bd9a1c652a9d3d98b5085b4afe8b20e0754aa85d6a4042a83fa1cd27e2547d2d337b28d8038856ef60c83395b2b96e0fc9a9d39265de270b2ce592259855e49e3fbde7d46f6c87963b836181079aa6d9a2e8c0e9ff36e691c71090fb6e3dbd9874ac00c1bf89c19b7c0d6c0143550452d9debf4c30dd9220cbfe41fdb15b1de5eabb9b5f43e4155dd9931020128d2bceffb47ad6d600e0771f858878266709c24cf858b346e5c33c3d59b27d5b66c9d2cda994ca408be239b9726a08208c177b24b49651325aeb87d279690d8abc4c79325a37c1d095216ecf9855f971e8e60a0b5097d2ce80300cfd8b5e08550f45434ad86c42a702e423b30a511508c14200df59fffd042f0cab565379af6d62a45ad8d754533c3aa82fb83d654aa17a653c34fe576cb1252ef4d9a2ba2facb5064d8adbebc8d53f60b33b52e25d89616d270d78b76b7ac7a3f58bd8e85b7d43286c13021199431cb613871d10381820511f59ef8b888ca3ef58ea4ae203daf52f2017caace1a57e2a7f358cee6ad03d96fe4641952db775a8216c4ac5d10abba8650ec0316c7a273ef73fb1fe0e069eef434a8f0d3b9163dd35d88437d48ac32b0e1c161c663c2e297ec1890f8ef1f863263ddf73db0c13b4a79d3bcf59fb7efc97b5a1d9cff03fd7dfb822f71b07095a20a8fb762c3108f0743e982dc01248b99de12dd2b31ff3b9d14f6c8e8b3ae5222ab3bb27b80c306a8eaf506c6c14cf11fd4e8a7ccb1e017f3a62802d543b0e379758559381feb7e1f4b6ad9c13350a958f2fa62115e1a1c50393c308bf8f7e1c32c7b28681dff98cd3f2941dffb802b2e6081265bc99cfa8664a1ec89e615725b00f5175f2d38b036c48be76ae4c4c2692d216b839b3e30c3a3236f337030c3146cfa9f6df2208f3bfd7c50f34d2d80e53a35e373bdf112df7565f1cd84097f167c00bce68faed596259d94acf968f6d8e117afb3257416d3cfb004f1dcccdd19300431c94e2ced40d52cd72a88ba71595690f8e38a64bcb919ddd7ef9e1eacd3663f4bbecc9a63b51fed30f61235556a24360917c72e05dea403347e15654d875eeb2cc3c188259efe51e302fe17b981a1a8f53f62bd386ef01ff3e190594289ef279b1b27926bb4eb6d87e001d39adb5899196e5b242354035cc69ef7b44234d767e354ce87c85e5763bc4eeff354848231221fa3b735b2f3422d1ce59dddc4cd007aa416964bcdd29c2360702d1f295d4bb4294d61b99650f0e551e7521ba4677e19e916ac1de401b7ce1af09b490428a5c0977fb406d06e0d05282aeb64b12f6ba94318e2c23eaebdb0c5547084b4a3f9c672d0b135904b08960c8594eefb3335d425545ebe24e5194108a97ba7d248cbc1ceea856a687e66c5ded328c201925a78343b1f1a2b6745772fc66c14e6b64dc309d1f5f49b394202c65e39e04e5cdd45655cf4c577090cb8fe7985233eebeda51e36807bfe44ad14dbe1f147eb5997bf7b61a74589916f76ab193ad689345b8f73922da6f9108249d4b096431f1adf635619f8107b30a5d40cbafa9e6770c24e3d08407b8867c5c063e58816bbbfbfff9dba50a8c6b4a44c7303dcd8836b3dc7ec020f4ec0b3f5b58d2e159be854f3c2c80e6a651e92b2b9dfb2b6ebeaa29270d6a0978e6aa20c4ebbc4cb62c05e36080718229c3ad4b8031edf41d20da4a29a6e94d8a4a1ce0817c4f1d153a003886400fb1f07ee2d486849399e0bfb707920d6138066d68ec85a25b9d6d4544e33d87ba223ce194b955061c2bf17d44b1e7523e9d12fa123d6643af406ad6446a6aae1fdb72b02d7cb9ec9f66ae23cdb36c7ed9a043080e2941336acf7a4fe0de267fd09ef80b26792e58702f4b28052cfc877e4b943634a169f7332efe433da934b10019f21a6a54f3420093fb80fc1620d87e75ef654e0285d8d6ceea5b032fde76b93a9e0e32afdc18e48669f7057241c40c3d625752a2f111a13ef67eac8d78bb8f6840dcd49d7950a24a9f78dc11ac5b514f0539cd72fd045ac6c0303aff8b2cdb74592a8440e457c6852cae90d3cef7e119523233b4f6d27a8d76522294b38536946b72c895752231125e2fedff8aa578e8fd10df527fb45e68db5f1983818621cc9c244b4514bb40d5ca2d40826766632b1874d6e83929109f6b3a10452ed9280b112121c667a840259cfa2755af1218b39acadd63a2d7cc8662e502668cbb3ed42a06b2a269ad0108c7cefd415299b944e94f1d403db3431e11a89b08324738c6043dec604e0af294a652264bf88905b4909c6d941afb17df8cb799f11573ca743beeda8963f2ebad8fde45f967dc61fa334f8c71faadb8346c9d2a5819c45e0c51a1edb4e1e1422e7262c138504afe0bdf27c9a3866989bd38858c933d8989c81585b78beee04ce064f8b1b82bfc0d930da5f93acf3ebed80195b7af40493827232f6216beaaa7e31090fc0988134308611198c317024bd5cb07a53180cb0612a2b020bcb656a198a2b3b6f8bd27d76baddc49a24177660f7ba899dc878a08351bb7bc114a397cc50b6e8468197193312655c82e20137720e8ece66a5a1ed48eea02f22e5814ed74b5ec3a9578cade144e309fc48be7f8a60270d5818e9b4f793952c3d89df0c3967741b8dd4ec2dad1c71a41dd16f097ac6bdece69e40471ccc2738c1866ca8eaba74d8fd46e9dd7141682dc382df739528f2e2c3386d9f25643731a206a07e91e3281682c41a77175e8788fb831fa7cc5bcc8fe258b36c91dc3b9ec85a376ae0a93a0d4181dce148b6b63a184f6046153e451b711718b1cdbd37c2b805ba97a463dc023cf8151a7f818e66e87ee515d7c3679662dc3ae37e781b6bee97a3befe2026bd1336d3460e36bd72f240874493c747929358bfd5baad29b43da9e75aa30453e286b2d31cc136f01e4b5cb68d525cd053a14f8ad7ed56848521fe9bb6f5a1af8035f5a1a1d21beefec1cf797352355620d6faf64fa3babe1fcc09251b6a4404e2791ed9f09501fc10b56dd6e61c78c42d165f81be4861296bf36d2379438ae3f7a6645ffd1013df0a9abab41011b26ead993527e31e352a1d3ae018d2dca24505b508f407f35f5eae3f58c9ca3b08d235dc4229863bc94d57e1ea0719cb293da61cabe3d71878b2a21425ee18ffeb9aaacb918951188a754cd5549f636886c449948931b7e9d66870244906460ebde6a9ba8ca8a029119a0a0bbd2d9ec6452345a75eb70be1de31d0b5377c7286254770883eb54c0b8e3f48c4574806c9cdd370564822206282995f51923b39ca6a62a553782fbf8284e07c6e582c4da358f5e9c07da6405b9946a6f294d537c3fd9b5889a18a48a44f3e173974c25184d5e5114944dbf64819f178cb644c2239acc17f5c4fbb15c28a0e3090016974da97eb6a87ad26d4a63c52b8ade782d667b15c8fa288274deea9b9f6287f3dee429ee5a45e2a694456727c2148b508add1b68639bb477428193c9672b290084354d305b91a5953af8c4b9a5e4f74907869505274db9bb80e4440d1a2d032a3c33b060d1016ed2264791a5bc8417d7ac2c36a6956b25934911c4c9a1d0576d8f841805a41478b1652d326501190479b3cdbecaf35d7f19a3620feecd378a6caff1dc7bef0383fd543afe095e857d6f097bacfad6d0e874048a1c448ab9e9c551e44281e70e5e02baa2a81f0bd4245c700eb04325c2769db68c6218ead59611c927deea7988a0c51adc8cd4543dfa08b107da372c13ebc17ad535d9c8942ef0f47515467c69a1bbc229bffbbfacfaa62ed61659374fcdb2949b7f791dc39ebbe45916885bdaffc4e4ac9984643e589ba06cf9e26d39aa5f457b9cc5bc1c081b6ce8ecbb4785311c59d2b17c80cf04d5ca93d0781810e79b24c2499951f309353b091b4c6478486654a1df52e83e6283f1fe0014c42c8bb12e8a517f82d71d7fe3e977135f5bb9010e0fd831be2036924a2894d8c8fd2f05d8008d2619a27fa1ee738cf7c999cdfb8e3d26ff4232fdced97d73023038ca9f711363048a19d7a73d4e279dbbc7fca890ffd3f38abf0ea827e2ecc158f552ef80f83ec116d74432975a2de61746e93fdf0ff6703128eaf10964aded6ce293320142b342da80c43e0ffc25505ebf6ec17562aa0ce83773649b4a0d0856dcc6742a8d5ca11db971b7a6f16ff0c56ddfb6b276bbca3974c7cd54b019ed3449c42cc0a894ac40a5ef01263c6d35cf92545664d6cc33c5479a42a62c1a67ce1819850cfcf10c7fcd049372f62c15274c1b5cb700d2756b1664541b40b839fab1725152165eab9de38dbf0a6421eb95f87f48f4034f8c7fe444d9c42536e1873b40d68dab43858e5cba2c02cf34db63a77a04b92415475af6e60822539e59a9308ad31b64b689b2c2465f1bdf1cdc49a25e9f09df815f436a5263f3b27cdfd43ecfb5daa1ce2d58a890fea8ecb0a03f4a4d75e003a8e29877f3e3d02a743d6d78241534da51c481f98355288517a1e35495753b0d59201431cf300a626467c3ebdc5e7eb657cbeffc7e7f38f7cbe921fc77f10f84e04c476a390de029854d186ea137fddb46e5e89f43bf80dede165a9be999463c78d7aa6a924956c9b18b46ae387177030eccfa5ca9fcc299ed834f180b92c246f03864b932f15cdfc54a27110e41baa7e092f82dea80d3d3e24f561bcc2bae3cc2538220c89489ce0e94783749d30c95276ec3d34620087ad15dae4211137ccef9d3b935a8f6ac3833c1b0564fd1481047813faf2d932389f2d54c0a8461c8a69457bc499aaff45657cd802f196ef8376d7a400655f341b81e92aa8b44316e4c1987721eefd259028c812de0146216012adfdecd697e260865445de6732cd147bfe372f9e313a1ae9bd938d28af7d1071a3d543fff29eac9a57d8bd999d6d6573fc765a03279692a2f00ee68cdb84d26a177c204325e5dff61f5c46615ca99e01dc6e406ce9be3ea1fb2dc11c758499edd4b8cd7324363c7e98a37c8a17408b9f01749f04d24424d658f2d96fe4de3f2ea91df980ea0642060b939002572aa0a5caff89198db8eee6567d5169f6aae46db10395fca5fef067345ae7028c64c8f515cd621f251bd6ca9c27228f82f42b7cff42495269cf20b426445a9005b1483ae66ce14bfe0e61528e905cfa15d1e4548701d96e1829aafa52f886864aaba6dd1311ad59a52192f2c73bbed903af12647f85173ce0829aec12d035617daa256df4b9217f62c1afc038560c53679a01516923c6e5e9e3d2de372a43186876f1460c937236c6e171b4775de3ad5397f73fdc42fbd2e08ccae92f0bcc7356baa1074a8e27e18c03cdfa8d79460397cc8bbbb1da93f21662a91d8f810f6a69877c88bf3fc6fd5a73d73687eb585463e970bb9ed25bf06e53cb2fc81b3a981a27db3b005216b77671ee702819d0d7c80bf2bcb4989868a80b746cb274b0213ce7d33955f13419aca05be1adfd05df12e84a792bcd5f6c38476030f9c2c0384ea5a49f17ace0d3c3a8b69a88f7a44f1aeaf72050957d3542379ed589c90eabf897820f60ae46c66ada9c79828f1be9dd25fc41c4db9a04a0a562fbbdecb2f15ba7bff9c0c80b1a4f9f7c8ddc2edc670a93065c45ad9b8a78a2dc2d9939e995c9a1a2165ac733c4605d55acf9eba2937ca2cb0722c3eb3cda3b5d3302db3fe015895112b273d95d98e01fc09f96b8be8e4de60e830a02d352baef6fd9b9262dbc6e63fa3e22fd376d1d9622b59e0615e6833454c417f4c4627bea9663544df69b8562e818052dc66742d8a93d18ed436cf86d46f6fe234e88fde268dcd86121add067a6cbb6f6d691442b329fa69e0cc4d4b6090a57dfca59f3437d9cea51824f38e3c628beb2634d69b8b13814ddc0b77f00a344cdda0c93492dce092dc1b7d5bd5859a37d13a9865bfd83506f9a1efbfe7813c4f036baab5a4942c124e6788855e70022556e13b71c5dec3ef2c3f7525b4ade310f952467ab18e8ae43a1769a3543c384f05846eafde047f411f4d70764d8c99c2b44ad8a83ed622a704932e18c1f862ac56e8b445e74547e545b6f8f041582a01ef1edd8b2fe708bd4250309d0c04a85745b91e15c6e6b388e759b84f252cdfe3224579d38b7013178a712ac1eb1c89f54757cc429477d93f59a5e2136b75900b0a5d2a327edadab4385985d7d7efa2fac34dd6d2236e501edd8c74a6f2d816adbadf48b2b2a94dc7d3072613dd8a4c379c834fb6f1f9fe9882e79440eb34c89967b238dd81f3bb2a9f57e6427e243498ad78191d9843e559a0e9e373b949f09ce10a43e99cea442099a853e4d4496133545cd596d527e580b74ab352f60301e330dd5150a1b74f6d5e2cc736d4544ce6a160dedbf18c2dcdaf54695f14ecb3edca5ff9f19929d6278b90d2c3471a3a5baa80e72bf8aea2022c53a578da60df5355b00989a52ecc18ce246c203561c561e5a969f7e35e809a403713deb9a9cd25b6cf6892eccc4d662645ef96d6c3935aa063afa3d11675bba0db21d82166dac8e62e38b3cd8c4ca45a9eab41e58c367f5a1c1568473db72a66071c2d30a82092802c509bc4cba46cb2b2b26f0ef23d6e043e0e61e5e2b33ddad4bdef5d12308fae346ac7013cf17ad5dcc5dd563959bb73b37e88019f2b9a260e1baa4611bd3d8af1616edbc2988697c6a94866da33af0fea44b2591b50c0b5df42fa3d9c2b603423b37e62572135c16b3011724003cb7ebf70e6abb7f7a654f71096754b7ddbf15e60e1c9df406808e9b652787466edf5270b5a7d67205040a89fb4abd567d8fe1f04c36b00c6dba9a9fc430f2051c177f0eaa558c732ce8f8b7b4a81bc6508ccc6ec324fa29356fe4cfed07ed2b2569c9bc7f98141013d1a89f01d0136065de8f1a6a6ceb53a102a4bc8841239c70d7de4fa1d1a6ed2395d821fbc0bd39db7e1f155c2fed0a364403728e2c46750c404cf019fb418f6944c7bbcab90c38c7227202fb13cf87bd1a78acf019c09ed7de0a2da3579f1f735073e938aed5164ee202656c191679a7f37d205f594869d84989b2ffeb0791143f56c3b358df2d904eef892fc10ba63204ac63e44ab241b3bdd4163a3ddd5e9f24e24c7ad0170a2e490bfe6a4f6a80adc00115ee0f0d7bd7156e5a2562e32c7ebdd40d77a6ac133464949076e74793c4b8e6d7f7f151e02746e87b12554cc032d1eb4025826db91044070a13fb2e2f33cad45581383e4bd2bc423126ab28ed9126958dc5fda4940d5541bf506f0328ae9a0991822d0d20148ddecb7ded3f165a858c94461975469760bb5db6f7ddae16dea70ccb5532cb64583d604a2511ecf193ca669f3d22dfb859a81b171ee78e80c24db39e3d7967b3d4ffe8d1dfb1e84feeac7fe43c86bd0498ae05210bb39dd3bcf5b9c821c2864ea6532849b5b2ad47c46b0d693d9fc0302fbb7795d02e61421cd8ebdead1004c7b2841ab9fcad826e3527f1cb505ef44a81547eda38a94f1e0e78d848130e1bcca3d7ef8742be65a06e80eeeb659903a209906e1c5ab3389a95eade620f34a516f799ebb0a717c6db0b118a55585f4a1eed1fbead2d5221b1f425619ec048570fe02131211a678d37eb674148ce566244dbc389698754e50970f78a41a000bacfc536e6b6877a132546675e6c38f61f5d74d346b39d709efe48da4f9ed32d9d4618e6332fab4914c90896caf1628a4fdbcbb8cd47e04d8be9eb157495f319e6df1903602edc0cdadaaede08af2a61c65a6a5174c32d56c3d193e7d4868bd3f3608804d22a928c0fcfa9e9d98bafd8e7255ea942f033426c2e1575cf36d8ce57e922cf14fc95fd57e6997ed6b682a81c22347b02ab131592944a075510a09dae20e53fb59d8592add93b656642bf1552219fb22af19e363dd8e4516d5bab8942bac7c3e1459ba8a170d674a4830a8cb2a04342e144b244680a1177e6f49cd61f84d2a44a3e61766cf09ceddcf94ffee655779096935f290ccde5727ab4dcc36f38aa09317231cb1b86be47aca77c2fd7a647236bb5875f2461eae9daab5513e646d9737d985d696becafc86bda5d7d4272b33eb0b209d0c00b36bbdb87aedd92bc230d8da6ecb29d438913542b950f4dd7c1a0b499e98309405e62ecd7ae02268f6a5ec5cdd9e6fe19a9a1fcd9ae7f2ec1095dfbaf86f15378392ff084758a7e50a6273ea6d9228c82309e9b76d2235fda40f58c32dd3c47f057880931ecdd0c4d202758ff1fc2413ca5cb78ce0f5cc06cec8872fa605dcb04e3eb46197f7965d0072e016ee1378f20f3e366da74e21550378d132b197a822fd8d49d40392540db25e9749969b20076072149f3b95d97811b708606e7d838ad8a3db3653465fc8ac672fdd5ca403f3c9add82ba6d6b017aec5538ddd464e744e06baa3b63a6538c532fc74247368ed59d14d904643019f6a58ffa22366d151e61e57619cd70325505adba968124c1f71a00e44d17eb529f4033f085415c6c9f344b2e15d7958aba575d69f00103edf68bd8ac7b9f402e007981a730f016a04f3fcea6a615d5047dd089554acb70e3a233303378f400d68da91373c0f52d33e70171192c00cc8a0eec790183db6405b75f5cf32d5ef079c902ab2cad3a507618e8233a8462dc5b87ae66083bb50e0e7f87d8d0c9e8d58325c3f6c3dbfb2f4b1251ed801cac0e1a434a5bffe345a01ca8a90b1f54a6d25eb0147373437749cca8d942bbd0f265d4f51fc44378b13e999304647e4decb4319e38ac6cfc9c36f7f7a5847c284e6ec556486ef3caec93ed1bef6654c1e4b187bce26a17e55ac136b709442660bcd52ae6ef767acd66610de1d5b2b151c24b6ebf4512b99abf49a2bf244eca89db10e80f89c1d5c9db04c153c25432bf99a09f4ac88509b729da83a4b58735360d5565440bd09c32b136333596cc09da36b0bb72618e4a04826647643da3eef72e8b8053c07880a2e8169a5091b2641681fc35a87c4d49f052f81bd6a42ab92878b5e142048415cc6f2fd05f257617736e4db4078978657ed3887c92c854276e0de80f897975fe467d9c7a812ab94f5b27aa06ccbdb375aa92fbb475509d7bb3779c6a2e35f7c1d671d56029e72cf5549dcdcbd92242d4041c5bf995c4bc8dec1ad80d6eb15cc6b4ec34a7efa2f78d8a73b1e27a52ff22af9cd7e6f919a67f2df63238e91664faba8be786fd9d9b4ead36163778d550b346235fdf296c63af2bf673aaa910ad0551ee4f374f596a2876e85684ab0d5bb3db47287a848c23d91fc0f34252878e9948e5a6d054149cfa0e96dc2c33fadc0f6f55604ba2591aa79cd4d61eb1f1cf58e559592e5454d41a80d3f0726a9eece4ae0dd4db6d6ec8926de7004ba1a85a449c348c55803eb5cc43f52af94fccde6cc069f56bbb7b4c26795c47f2c5091d96698ab9e2aaf1932c47f39f9398136fddb179c3926e55b8a44ca472b676c639c2bb4e6b2ac5d98ac40a3d840002acfb0b206387ca1ed10b3d40c87667870040296383aa6e8110932140cc727f29b2a2e9636aae781f88d8871e509f0552cf8babfccdb11f8220e067542509e4aa3c914982057f9f358d516b60ad442152469cf007b5b763c6df1eeb0bec24ba215dbee21179a9094a39433d7f524eaf89932772a9ad30eecee03283e63a2cf63da91ee409fb5b9a3a3f189d13ad8c4a892be08f979f20308d1071693ce1de8ce6c338cd91384bea30c80ce7d2fa8edac974e1059248b424482cb508ab36505643ee31bc102f2a7b625b8ec07c0d9c9230a1b2e006ab15b9c66c179eada7779384b56198428457227c74cad6e804e26b1ce193bc6ed68346025fd4ab99ef00584fd865f580c55b94304c4a34775e0b4573a0f0b60a03fbf7226bfc358d4e38db73ba58319bfa011e7525f2f821107ce8551f0f1d36f47f9000f30f49502f8df888c059eecebd80b7dc0be8867b006ec03e8f7ef3ee93a6a39e4b618f78cc4ec4993f4fc669c8d349775dc6746bdbef86a71f8b0eeb62350f2fc41a89a428ebae8baf010b506b2b432905d2ec1b7c9076f37674c565845bcee05cf208129228982ff26edd2804b41b5201132cf3943dacc1530d21df85de4b4bc6a1c95f83ad21e1a210505381a86c63252a70c5659b0116e70e46bfd9ec0d73986c5a3eb911283d8741823cc68e30387bd3510f931d52ce4208c7954075ec953ba09d772809a427d5e6415fd3a6df1a6814540918ac7ab8383bf9e1233f5ac00257c7817d548d53c041223e73dd24de9a5fa902508d7698cac321c1831adb3e5568caf5fe997dfb299c74804b9a3837150068cf232d6aa2393a8ccef88750f76d187fc8fb96bf9eb990b98949c50af03f8c030243afbd50052efc8f0c9a33fd5071c31345d268696561775224453d041e14793b5501cf18aa9d9eaa596e276dc5900d70b4e69a2e069f1797583f81a96238937e3187641602a50c29aed0d831dfa25838ca6551c32f5e00b379cd03296ab27c59c0568eb35ae596a09a5df82e8da7c019b3127aeb6585fb19d6599777072b6ed0870e22597b80be5fc8c6659b53821d120847a17a916e98ee4ada24067919d30f4675ccf1e20eb7ae7a7ce8bea5e8819b584ef504f87512163849391ea146c19f4c4a8f1a12cc865937d67608492d597e32b6d7422ff77a8d1a07c3ead294ae0f1eb13d4cd24e80163cb0cc914acd25c601260625981690ed07e46d01f130447fddaeebcf4a092c4ba8f051407d9f334fb9ceeecd52e2ef9cab00c868e2da5dcf855208f213f60a259909b83b29e1502b8874d0def0f42292465119e40d45dab18f86de583311f559631c299320bb85c8cad47357893d913aca57ca7782eac5da0d1e0019214ef1797205132b885211c94c94c324af325f66c2dd679d28a68358bbaa974fbb6726b54f019527089e71e15464e3a3f5fc23ba201a01fc0a03b725e5e0a12046db51af0e8d2acaa03fd07386e808f8ae7217ce332589d1ca7018d2f499a91fff4fd49740f4d2920998dd73468ed29826a0d628741960187ae858e56b5888d53480d7c8dd7f2ddff61602aeed014544c495863ea52b472cf80f29b1171e4df383a04ba8e3d3d531607738eb9f93adbebc380401b4a94cee025fb43babbab4e06271b0e87da621c21e186dfbead9372455586c2bba8c0f25742a5769ff95feb042f84fa82f2e27e4fec9c2a22467d72d2b348e6fc1f429dd3e5219a7ad6edd520b2db76a458bad5a6fb1f556dcaa675b6e5eb2ecaf0f373f101a250ca55ea54a7b432bfd6a17e47faa18b40b7552de0aa158ed9ea909b9ba3782f5838d456e5190ad2125bf8b88202bbf02c677ac84f7b7b73facb58a33511403bdc2bd60b3cb1c3a66770bdf598b8be592cea7aaf9793645cbadb56aade5165b6ba1e556ad68d95acbadb68efb56439f4267e7902c861584d4345d957fbd3c931d7f416757052c34a79f5ab9e1e2d6f1dbfe6da1f0f4b8b41781046d308c899f601664f4de887579cfeb2ce43737c73a16c85962c18644d16345d18aaf32a11dc0d2530a571a1f51ca4567f97fbf0c3b90587544293c938830b36832c86d3bc6f89bf2be71d524d3368a4ad006b0ce08766ec25851759dde5b21e9a69b70682fa9bb203ea9f2553aa77a60da9b2210b51136a3cecadef40ac066d0a4b8d9425b6f2e47bc495bb77ff0e3abb2f19e079853208e311fb7ed9004afdcd47bfc1b683a69bb4da6c03c56da75972c1f729a1ecb6cbc2ab25f1747d64c05386136c26e4d8bec2dcf76ada7f5ba385e4edce23c9dbcb39c11e46a6d4ab02c834b7a3d566ea9063e2995b0af924e67f8fcf2bcd5fa4eea6e8d27772a9d06771796bf7121ce932cc958852f5e74f90ee28baa08608be28b90f81bbc997a1c9906c3ea87750c6934d7faf58c0cec4120f4c1c664104e9dd496d41c4ff572f709eecb3a0101436e88ac8debc10483b83ac0ecfe35ca9776c0c83c5d8ba36bcf3daa61981ead577dd47d27b4dc3d94be8a700096c787587453e67429cbfdf79ae5c77fe888d61708f0a0a31411982263b30029732d071caa8b6f12dc8e8db264530acc9eb95a32341e15e7fb1ebf73165f2e33dad7545d2edce1f9344a0b1b4377b7d75cc908307384df542bf661f9ffa3428d556bce07cffe68290332314907d718f913d1cc250131b72842e943725e40a613a78fc8af63bb9867260136e12c5d0649f14dbaf618d9ca18107502f7cb41195960bd6d18854a50500970acb473131d3a5af04334978725e3fcc9e286a5d43de660870a21de8a2747ec65ad911b417c274a63376b535c51788bd77e38d1724b121304f8ec06ead6f86c64298970309f237b4682ff3bba507c0e8986821d5fa20111d75b2da69816043500a4ec94fa365bf55a33ddba2411c61fd15623c97fd3ffcf4c13e91b0ddee61ea1e208f66378e8ff2d0c85f7549caf3d1420bb388b884cb48bba61827306e52dfcb4887bcce15e08e7f863f8f332e9ed58295712fd2d8763f015a6270450f51d5576ba3226b4219aeb8cdc45fa56739a0c67cfed2dc194689e6839faf773bead594feab4d8edda0cd70493e7fca424dd6c5506e5c6a1e97e4b48390989cbeeb63d7122860b380c7b482e8f03e35905fff0feace9e22e7051451581afa4f31d094339ac6dd7aff5515fc8db3c970c0d070e861bc34cb60769034c72503a4d730c8103b0c7840582e926da86d23d71fb8b1c5d3b73432fbd5c3967ee673e8094f62ad854c691ed30ea9003198d6095b9430a363939b5ebd45b1533620de9d0a9f38e67d5b671921d24c5ef0f118eefefa5bf6735c8329b4f283f96f2b6067b32dadc64524da82eb080a166eaa8cb7439ea207b3ea6a01d98b44f179cd7c75f5d1cbd50903bc0f44e2c9d9f64dd78ea4c7cb0010de26bde9eae3e7b4d48812afcbc35760caeca3a4b348710113d2fa6cff90883ec82ad8a0a68c2f365593213fba0d9880e6e603ac9c93292f5342a6d2e8ae9dbd219ad916235b9c265fff424f88ed6a417361eac25153160e8db3647db285fe141344735f005eeba12050fb87ec0d39ac8c5b0cfa451db483d977319cb0c14a5f9d1a0b193d36f16dfad9704d225f23ae4f879d1a43817457811f7b09b4e9a25967c9b3bdec9a4af3450e1c7e40757a07b00eb46fd1ec38b3a710dc8b47dbfdbcfc150d57752dcf3b7051b1e5cbc2a69d096bfb7ae4a4c87aa656e854690b39f265c51d0f51249753b311810d00f59425a9a4c7dca77796ac713dc816df98368d4f259310a85bda719595baea8ab70a3f0d45adf6f7761a0ed19d9d614e51d473f12e998c95193faa035d9bbd62a101524c478b304b2ed8c92f0dd7d5dbb02733462a4c85fe73cecb269581c17947e996a9ca06a2859558a354a8d5ee7f5d0a2e0939eb6e0705e64c90301dc57040e24c4ff0d581050e76c739b4e17df07c77d7d078e9d16f17b98d61d28a2ca35a72beea0b734daed3ebdd9a9cf8e04ee9a688d1b3df4c3d41e762a6e2f1afaa599d383570d366e3fc40b7a99efc8224dbf1a9009b25e5a5ab2a594524a19540d6d0d6e0e4e7b8461e25bdce59bcea5a0bd854198ce7bfdfbf5f9c333b9e771ee3de726883dcf83e9f845f7c39c728bbf678a786fbc373f7764f486546aaa287b8f42f12b39fb36f70ecd9f09e57c9be5d3c704558739ad4a979717977205d3c19cc074b0273027d57ba6ea5c99604f60ba0b7b027382e9604fb2a7624d699cfd9835a55da294c6d9ff79ac29d81398cec64a15d614ec098c26961618399813eb0a16163096b0a6604f74e5f836bafdd74587bfecb940bdbc28a55d32516b59fe3a234ac964c9a1fbe5b4897273769476395719e5b9ceac64a2543251e896792d3767bf8b4efc2dbe77ae32f2b9ceace5c52e3a74b3a6a0d0fd354b285953e816a7bc12d0fde1ca9ab229628a1355a088020a74fda4a4604eb027505154acecb7b1525ef90f5c813e7aab30110515af984725a70f8d964c4258287fde7fdfdfc5c53b7f0879e76762fcdf87f1b7437b66f97de2cdad57ee5eb4ffab7f1dbddad5e2a5affe3b6d9828f70e158b10ea8f674ca17f05fa02bd5546700515f158d38232e97bce511a2bb9f53454726b3fcd95dcead9099a2ab905054d546ee1df364094e3db00b1ffaeca245c72ab5804ba8e4d33a45418fc35a68919b99bb3c35f657432a8689cfd2fe6d538a4f86b99448ca99bb3695c72e8fe1a2cca9546abcc754616743f69ae32aca0fb47731597a0fbc5281957d6193fa0fbc525f963223a41b7380494293b07ed174525e7f59898ae93dee59ef9195363133679962bf2572bf2572bf2572bf257258957ab8d921e093e69ae2f272bcc5fcaf2952bffc5a47c71294bf25f56ff5296bf23bb942fff42f25f5f5c5cbeb5ddfff2302fdfc2bc1ae7e55c5b92ef5c99a3c9e55e4d5e4d5e4d5e4db68785098a83406f95295aeefa9b2449d2db66520e9d2437f8de477670dda837924fbafc6aef15b97f875e91dfe624d5dfd5cb6a45becbea5dc8fda252fd6abbac5c48af3c5796e9cb15f93b7cef5526399aa30b221a9e6b7ff059f93fefe4bbce04adb55e71923ff96ab222c9578efc171372a522c9d5bf98ac9c14d12a72f5ab73a57279d5ea57ab736db7eb4a2e97cbd1e472af26af26af26af263a0fca08a8a029a0b7ca088640c5d59da1b5d649abf79bbf77729ef9e73d7adfd3d848f15e831f8e6388c7108f211ec3f177e8d15c6150c6579949e2df711cc3578daf0abfcd4534bae68026913fc41f7f47a6b161a21c4f5548ee98d6f149733ccfeff3077b8277c2f33b576f469f1d4aec1aa6833981418139810d017332bf1eeec4f03c5726cf833d99299130beee5ad45a9f5ed75d77ddf5f7edec79f95f394fffc639e7df5ac83b8b6814ef294f680f7aab84a08a2959bc2957a6ec3c6f6db7f7ddb461a2a4b191b28aaaefbf26e573c5603b8aa2289e6bab3ac1ef4f92a3b80acf76a3e0e9cd941f1329d4833971cd614e20cc69f55eef7661e881a0863d813d813d91da4ad0a1e2d6df66149bf8eaacb5d666521125fc8b68349f77a36d5ef1d9eaaf02f5416f15104085208a104841454de60f01141405f456010116a4507e4c8e66f9f95cbd07c573157fe5927239cf91e75095ea5cc7732d5736524aef6c37ea8de06bd3464a299e349e43bdfe2b535278d2d8305182a0deaf357f7d7a2e88bfb58ecc5d47fe7ebe9f4bf17e83bff7fdde61544498132a86abbf5feffc7be7dfe62418b9f78f17d1e2e3fcfcdb6cae1985f19debaebbeebdc38001c37e18bf23635314fa1ef39d0cccdf9792ef549beff6b93fc3743027fb330c0abab6593493c2cf01dd1746ce3011bfb8c28610619c49e28730604f6836eb078c9375ae1915614e68dfcf8958b9ccf31f58bfcf50fcf057ec42d84f56f8dff3177f7cd5cffc0b1ffb613a98130cdbdff7de6b9b579813fa0df5df67d27e181ff6cf45ccccb778f12efbf97f3d0ff54fda3f46b47fa97b31a31af7b97e1e155414391aee2d9adbdc3b84b1236f9813b4bff7dcdc2c937c1a537cfc9ef9fd0b7334759e7961ba7cc34317e7fac99ceb1773c29c6bd9e27c39d71997b3d4a238f21ce79d73bcfa4c0e75544abb4addfacd8452620e5d25ed73ffc0ab93e5bdf8fc75fabfaf1ccfb5d4a1ba84caf5e72f9a3b547cfee09326cfdd1cf193c61f232afea002214a28543fec0986890f3e4c975b17e6847ee6f5f6defbbd7ec29ee01dfec5d0601744904247f4e5a3401005bd81de2a4d4c5569220abd416f95269cd026aca0228affa543c515577d7fee945be1f7e743f0270e8547e1b91e9ae0878f3dfe8316f14966fcaf1cfefcfd0efdb5993493c6cf01c5e6aad16f955fe4f764cf2b9757130ccbdf93c81fbfc72f3f5cce35a3e3bf9c6b46c97fe9d0fe18e75f71c718e73389fcf1fb6b74ede7bab1c7a42407fcad38a0fdbf737d0d81aedeb739297f0e683799d06fdda8f721fef07c394157af8846578c923fd68ce69517e68ce2ffbe737de950efefa75dfabfffbeef57efd3dff7e5c72e308cfdc9c7663f59e387ef8541f01f7ce0ef3ff0f7f0bf7f31c1b0fe5fee3652ca36a37d6df34b87862539f4f2fd4c7a09fb4bf885e18bea3b574c7ebfae84f8be3e46a122c93b3a9a172549b36f94343bdaf3fa62827edfc921fce07b2ed87bd00c5f9b2d7e9b30af5cbef1568cf2f263f02c28e78f31f8abcb9505cd9804c95f5f4d30ccfbf25c5d575c52683fd776d3d83051aeed0e1f277d8861f8dea3e1c7a40f7cb2c01fdffb7e450894afa6b27cef6d14ffea57d7159446bef7e2c3982dccf15c9dabeb0a8a3f893c57ec913f461493af2273eb6a0ae5ff6a8261f85c5926f4fbf1c57fe572eba2e27752487ce9d0d0bc1c05cd8b76144c82f6150128df2828a2dff96a8277bcd3fc609e62b488351c30b1de576bfdf4b4f7d69c73ae7befbd6bec799e5793022da575396a6a0a3fe99a14945afcf83e316fba39fac4fa6a8df914a97752f9a98a9426a89aaa78a0021adf75f724a0190941d4ec9084140c1421a5082949f42b528040a538910281242ce18225ae486962da9d402a0328d8a1033a3c1d6189231c518a2e30c11551ca882598e0c01223582205bd054a00015562084a1c41092c96d86189dc120cf89fb1c4138d031e8c31c63a9c22ac22bca20cb1981197bcc8e486c74a283ae9d0ac45bc55f70909510709cff3d3381906a62cb9d6d8d34c2c19c2079fe87befc393bfb8d249fbf5633d14a2fc4530d47b46fbf7df3fd4b9d6fcf333f5be7f489fdd876d8a7cbce450efff799e287a9ee7f5ff6aa494eb875eb9354a94df63d58bbf3a6d76a5ea8442bd176fbe8cb599f34a6bad32f7de7bf59c73ce57bdf7defbea456f5499354d94e38356f2137f9a9a8a8af27ebd52ab96f254674d13e5f71ef96228ae5efca195ea3f5475ae2c4abe78b27c7ca8f81f2a9e2b8b86cf845e5225fe90eadc79cfa43a59aa5f3d4b7c267475b2aa93470c4a9ebd09f55e3cb39555eff6efd07d556205a569f149fe194e9cf7ce58c5a8bd77bec0bdd9da29bbba4ee3eca77ca3cb2e10898b737174fa29b77a947e81dbea1cd82ff22717077e7f8fb21f478fa25d3aa771f647c92d8dc4e6bf00ef5c35da4f0dc47581bf571eba791419677f14514c314515555cb1f73ef5c9afd4eccab547c9e56a42a0ffce58358a5fbc49a898c71a24caf523f9ebcd398f8ae23a5def5d9b3d2acaf33c4f9bdff77ddaacf1c05dbf1a0fdcf543a7a6a2a2b00eebae94d4ca835eb0c603a48f4f9bf859219b5d897f4837e5489647cc95dcda6f258af543bf6f9fd94a888598ff0eed20190606f7c71806467b68d737e7e67c687f1e5832881f3e911882e707f29a292528cd21fefb41d2071735278746b4bfb82acb12f8e8af3f7f1b7fd7f34ecc63a0b5d65a6badb5d62cadf15facb5d69ee7791ecbfba4fef9bdfef97bce5aefbd4dce39efbdf7ee799ee77956ac4c4d4545d52c316a1e31319ea7bd310871b766f2cda77f983f1a27f7c8acdb72e1de883fda35a371f073feeb6aeca1730fed7a8d93bfc78feb4261c5bbaa8887e295877a49572a05e1949ea922434103cdf8b71983ae5c3c93f4ffa3fa5c85f6f3a95b042b8c70c5119090042d4a580213b068c2169e45930e3b2460090f3df890fb41010b60c2809b9385f0d99bdc9cfc42f8ec40dc1c3e353535c59dc838f9ab7c50cc549169e42bb2967cc3013084cca7388fa90f07170b114e6e9073810a72a0c3150be01b83e0cab7c405960882123ebc311ffa9a26000325a040cf95099df14419365da0f173268736c92fa9bad138fb6f340e46552fe6d12383a854e4ab48d5efc89b3c874254f5a371f6dfee795fe736bb12e692e487c92079f5e4afc8df91fbeafc6ece470aedcf1b4172bf7773f6df9bf3bdf88d61280ec1e49bfb2339748373b8c773cc3743f7def07b73ff924364480eed7d73449024c11fc92038a148ca508e771cc771c4f953914f861787a16af5e5796fcefe15f91825cf1587aa4ef23f941ccf242d722b3c2b88deff5104eb933785a1111d98426f9523acd4406f9523a4c2150cca272f9ce18232e6e902795f8ad9333908963121cbc7daff47f6de8b4157566e4e085ef043cf95eb80661fb9095dfbb35cf0fefbef4cfadefb8bae2c17be071f3c933c960fd44373ce53bd09e2cd57ebab84134a3481aa59092594c0006f42cbff6074aea123d1e480174838c087992824228084110e408204489c00892790c80112424082093b1f7641ec22b0dbed50b0abc1ce073b2624914b0202493c452009100491440b929822092c94f84189274a04f1a2069d08d709225ca0a5092b1ce0440d22e00acf9ac134aca77922d6d3d03c0b06eb61641898bf2e7ed5684ccc11a58c8fc17222b7d62bc58262b1a272ebc56f2968a45ca06476b935fe7e192932503253722bfc7d450a746be9cf7aa6a0dc4f960c34cf7a229a67c9789a9388e659cf3a95c4789a9329a89fabeb8a7ef935e60a8dd4cdd92d7ebd5a647632524c19a87cb33f3465a6e49bad319025323ec68b364cca3d009a3fe107c07a18381c4114e3613c0e324e56ff139ee64ff88c438c139ee659fd243ae1a461113deb619c20089afd2f396aad7f95d9f1dc9a1f7c32bc68488243f2cd064f19167737673fcb14f502cab547117717084abb58bf3ddd9571f1ba8be2d61e2944e66a7050f65f7b941bb4803a8faf4d2634ab40f10a78b36abd01cd34e6ea5d98ab4665ce1e853fa131e6ca7f803165766bf962ae3452a0fb5dbe5cbe3157666fca374df9662fc937fb9bb838fb830842082188c8f9bc1728716fd2aebdc79c168e65762c285024557faf4bbca271f6e72857b4ea5cbf4f6719579ae8270b6a49b95e2d489434e5724b6726acfcfdb509630384cd0f25cdb3f6af48f9667fcb3259501a673f8d79350e2b0add4f23659380fdabcc0edd2f5ec9ad1c85546e65c9ad5c456e4d895a340cdfaff9fcce9bef87f156f5119565d70dda778ad59964f2e740bc33744e4f9726ff7d315d4cf1924361ff8db340ef5028b6782297f36a1cd513bd9c5ca77154e3487e2785c8b22c3f9b353828cdf5062ddb5ccdd55f12ff369950bc1a5f65ae2e2ae828fe6882b47dae375841f77b1ec9775194f9fbd0af054c0bf3858952af3b34cf8dbd3ca5402d4794e28fbf8a3926a52eb79cac769c0953107e51fc9129c8c3a2677eafcd8e3f3cd75512abddcdc9e0b98a3ab1495c22e64426f9e68657b4eb6a9ca7ce55e7effb7e15f1677226949faf26a358eec0ccabd8e8caf552592f95f552c158dbe46c7c28c7ecd954a0dc2ef0c327023f3c951ce1bd122225e0c9146443019b1d4a183b0c00a9c70b3d80dc28d0ac7972eb732b6774c6df5c1b68f632478298c129fea452c2bd404dda057e0699a02b38b572a7ff9e29e85472c47d259f4b8a280ccf1b74f111e5f7a04a8b76dda0fcab25aaa7702ab7ae00b5805828013f9dab6a2b41f574733238054e8157805a402cf24d06a7d00c964fdb02227f8c73d65aefbd37e79cf3de7bef5d4aca46025252566c24e0020ca87fa8f3d73fc435f7606023848d103642d8086123848d103642d8086123848d103642d844c0c6c96602364364189b1fcafe5ea6f1ba548b2c1768b7f8012f6205d59b5a64b9397a0585ea35b47273f4f39c8e422f007c805f54c088e60fa1565121540815428550df4e8b76f19cc6c1afd33939d1ec6a3650aa6a8a287ff0e229ea05113f8029a350fce2e9e6e0100ac53b34419aa7fd01299b2038b70902a683e9604e3aeb6e8a38ebdeb5ee5ae3ab55fbbbe9bd36b3eefa5ff83929a4ff72fe39f7ce79e77de45c777def3631d79a6bddb5dee7bdf876a81df889abd5f771fc63b532f3efd575e5d58404c1578de35786abd5eac599affc56ab57eed564c59fa0a84c1ea50927f37373755d71e29142a0598edf4b9749a1ef1b9b942f5d4fc107650a9407bd553e20050551708ffc21a21863f207ec09dfa157bedb917d88ebf763a87afc34d5b99227ec0939c44921cca39ca0a27f2df3c39c843ef3db186f069430274c5e2c9f78c4500c54144fa10dc5751ac743f57b2f662c86413c2acc7ac96a889f302721193727ffe5516c50ae32a4647c1dad89a2c4b7a37746c65388fa3b50864d48e5969725ac2284ba40f8330cd0bc0234f3a71d8fee99e11553d8ece815211651c2a928ca704abbc226aec880c1cc0a4aa0055dcb1b3ee5895f80ab6e108dcd11e57efd6153b824cc854cc0281728ff7aaf6c2d1b0bdec497e4d68783f2eb9cc35799e7da6eb19be087a6f8993c57b00830cacde13cf7997c49bee1ef81e6d6e29143380b5414c5f07bf099d0dcfbd612ea9a70f1770c054c077bf2f940bc33f0f8f7310e9641b97fbd41797efae7c791ff7bfe798320a8dbf7e61c3491434df456819a0215f178fbcdd1384acd0dca75856afd25cce51e0eca511ccd357cfe3db8238b2613dacf7c458641836de502c9809a12e584d3146511ba49a465980ee60473426154603a241df711883c97f3df954e9bfdb3b9b3de599379f71c7ae79a5bd17c6ffe9befad7f9bfa59f95fbaaccd570eefbd8b7868fe8dafceada9ddd45d5d57d0b5dde8c73fce75d7dfcf8b77f6cecf5fb99cbf3b2175022750318fd98583d0bcb3f9d2690fd54cf8732b7fbef1f05e4244effd3b23eec7f8e646e35ca2ce77ce7f4354ebd7e79500d65aebef3dcda33f4febad794d13b6d69aefd75bf3efbdefbd27fabc10e4dce324d177fe78e7cf9745c9f9de9d9ff7e6e0f776b0bbeb6e02c5fb3d4f67a71820959b9363682727cf09a704942b7ff2bc5d135bb66cd9b265cb962d5bb66cd9b265cb962d5bb66cd9b265cb962d5bb66cd9b265cb962d5bb66cd9421384104210d1317eaebb40b96ef2cfff8917efaceae6f8e2f3103ef89964c930bef84427d189f5cefb1bd29a975eefdcfb6de2a04bd630a1cc21d4cdb15952ae3c0a9b253973a69eb90bc49bc51a0e98c0043230410dd01922c49b6b76e052353328571868f6d0fc18669bbb07a57e7c5e9b0d943c4f3183030860315a6305b622632bb0d6d80aac37b6026b8eadc0ba632bb0d656606d05d656606d05b6a2c68aa9a91a2bbe2cb475fe1af04096f080b3f2303ed7691c91c7bd3737a20cf7e2904236bbb21c397966b24608e578b2787494ff28eef1bbd65af31f553fbe388ee3388a7f55e373b3a31c2ab7303a8abd8be7688acf773abcfac33b2d7e163f141f14ff13dfcb1d25c5f15cf78b2787ca37f8c53377813c73b7f21f780ebf3649dc9ba0f301162c58b060c182050b162c58b060c182050b162c58b060c182050b162c58b060c182050b162c58b0883f8e3c68ce959749e2ef95dff1ea707f8bfd2b5cee87a1c5efb3243fe6f7b9c2fc582589cf45d1eca2f82ef6b728c5219c05ba5ffc17c82f5f7373e52ff257a9f8bb98eb0d5954bf4f56f9fbc5e726abfcf139ab3c7988ff2e3f8a7b14c91727f2c53389fcf24c72f1fb4599b3d5688be72f9e6bccb94ffe2dced51e4755f99df4b16158553fbe78ee7cb3b63874d287866154ed9dcbd2a6017baf3764110a5fa471e1b1dfa52cbf3ccb972f4dfe62babc4aa57a1795d3e6e6dee2efe7e63ac34dd5f3d234825197f7f68be28b3764415ffee5344b73c5a8cbabf8f8dc54bd8b5996664655e6ba3a69dc6967a9a1a2dcfbb9a9121f7c95b9e64045293a85e61214ceca0d59b45600153e40b19356001554ec563588b2b2dbedac48e9dd6e777795124451e5bbdffd76c01ffe14347c992a253002d5ffa24ffd9cfcb1592c9e3552ab1a299b060c51822156ab526ab7db5ba846aadc7b550226bb5d7983959d959d951aa9ddae464ab5a5e60735596a82500304fee313891f3e0f78e7fc658ccb8df1efbbbfffd03e79eee4398cb3de9cf3ee8123376b7850e6d73c08f70f7d99e427cbc7f7fc873e7eb2be079fc59f09054ff63b799c3727eb1733292454230599372954238546f7de526c7473cea5e0e8eebdf7fe799ee779520c79e8ae91a2ccafa5a4a6a6a428b18f4f3cd7a4a113d07e02ba776b99831a1d443da12aa0b70a0996a0e2eaf2c77b7f7b7b68ff1cd0ccdfd3fdb7c9bf0b71eee1cc44cef8a2e38be369ae7c278ea6cbab9e87f257af49960c2eaf7a2297f0554f549e3a11ef9df9a77bb93db2c74547af3fffd1c7caffd6e0a00ccf15fcfc5a1c023ffc1d9ac677f977e87365aaf80fa8fe1c365398350fc43db4aad981de9b73ce7befddf33ccffbbeeffbbe9a1d8064cd0e4afc57dfe7bf33cea48fcef7d63b676f6ae78477dee1bf742fdd05f7f63e97cbe55e4c5eb9571328519e425de884f233772577259763a2fb767a87f7238139e51b0ff5604ef9c6f33013fc14f1ce6773853941f777d813bcb3a740f9e7300b1a4ae51b7eeef4a34c7a3fe64c97a90665be28bff9e6e342380df194d32e952ed702d18a0ea142a8a710ea29847a724108154e09a3422772ab875572ab8b57ae44f1cabd9abc7246440977518c087731788af204258a113b2950e1aea605207fe962f014e5094a14237652a05cf0f404258a113b2950af262f27215494a7904a9427283b2342a870ca5308f56af274828f69ce5c2ef7f46a128679f46187eafd5c8a87cf19f4c513a62cab8c2fe23193f81ccd9a274af1fb4a4d5e149b17ad794199043eff1d596ce972ebe6561f427cca2df17bfd6214318a68c42786fd7361a3e0f93b74c7678845bed1926fc4262d2116b829b7565c5305e4f8ec353028bdbfa1969a17949853e155b8150e05cf2dfc1a5d31bac29ee01d7c0e81f6afa1bc55a0a008b538e5d61a6ae154aaa0fa7995ecc241fa432c72eb0454df2a504c564e05d5ab98c33b2bcc49119d4516334c2bbbe2a7c11e952beec4299e378e991cc7f177e4574ea57afe959f7ebe5a9d364c741a57fd2802559d9ea99b684dbeca24cf95e9d5647ce5f44bc73dbdc751c348f1176b5a5026e132b7beffb7be747b84f93eee3404da7f47c6e8eb237d88a83e5dcc1594c6c13c62a0dfaba46e8e508d104afc6bb8ab4111280e7778b75a018a4f11cb6a05687f6de218e82587b0d405fabebf67da30515e2c287eb08bd80c3bdea19d3ba1df7328277fca3732d0ef65a0fdbfbe8a0a775ac9c9f9f3efbdf7ce4f1829328f4778b1a64a99e43d477b1e31e983a3de7758a2d079800c34855f8f18460b68e2bae4b8cbd0430eeb29e8081271200a5f8f1e7a2fc816c0193b06d0050c0c1b1a99143e155c0862a343f8e9783ea48fcb07106fdc1e0d07d22d70dc28fc0e0ff4c2bdc35dd83f3b30676993cbf0805f4cbf8460335f2803ba74602106d1c677b367ec197d034d29a8760b5c051e039cb161ac6ae8137668d2d9c19a88ced99a23e03c80034660ab21d1012b513b24360045014c3820619388a8b12107b0a18601cc200021405e08400fa01f9f1b9b1a00fc4e010ab22042155856d05173d2bcf09e5c5045031990416805a0136246b13b3105044744d100940b10f1340101dca0b0a32381081009c9028b2b8c6a20c2104850647369001033223a60a11725089d0bb4c19522a200bd5a2e60c4f0f360b03ec0840daaec470c01f0c14388024d7ef0e145ea860890e8c006986898c1d59a81d930b062841974e468815e4fa20710286181270e5800022a40010a00f123061d301446cc04bd84ac654ffd1072968d254bc1a5f00d720d320dc0198c506418e427f2949c84462227ed15cfc032be13380cf245769163308c4b0bfe925d70b95559e46106bf2f7b1ecf1b6b8cfb25579aa547d0b419d0b45fe8293cf68ea043998126ce47cfe6063a6c226a6c1723140e740534ed17409910c64c029a4002ec1a740cb1ede3c7e3fbe9a186cd84bc054d1f54138c0d3a802e3d012bc61d34f12c9af8484383a6b13d47d004cea8b207e33bdc492e467c01811d3693a695b932758ecbce37e392c50e5c85efc7c3a1571ac707a549c35063a787dd88262d769a4d43aca1f11e378d900757c12340a7e9615f479811069c01653c02e826ecd069f46e074ea5a966dd4a5f5473c2061a57dd65639da359d84046173d7b34782c2f061ea3c25dd47c8f5aebac3dfd693084b1b52700ef65052474f1ba87b5b76dec9d8dbf179aa503288373a04965840e2e7db3be1676d83bd8e12cfa9bc5162c36b802032b2ba0823b1212844e2868851c2e5b8d0843d841a15c7d5e016af81143162f8022063280e07182c08e08eca0064fa880041f4042033b3a364f702d10eced0845988a620650a0c0034c391cc0861a06f02386211e3262b89099120226243a90030e4527c0b0d564a876a083224a4d596c4902126c28800b19971aa820052520c10798480288263f141d800034fc88c1c7132f48810b44e0d40307377842052820c108320065013c24a008533598010946304589254daacc87900b3945c5143b58010a420002294504c100555674001b0a40830f2118108e0bf9c40b5630010904b144951d20003e7808c18054e4be544cf1c40b5890821080404a124534a0c9120304c0470e1890cb460d9c17977db3a0620a1db0600529404108409044110d08a2090396f0a0c80036acb0a38305135e7061a5ca010c1d93050c80061e0b30600149901c0166a1b7e4267026e4257c1cd81bc845640de09e5c43f3601d3147aba05f46468681591e8d7e8167b60b2c03b67879d1e55e8daa92a4195f88304217d0fbba977be75ce34b1396a0967b2f7bd3516946695b9bdd967654992d36abe14eb8177b60957e4b900225f8a4049770fee65f95cdd8daecd9d9b7b38725b9a5b71429f6e9ec411d205009d2b877c81fd5662ad291c7f91c38fe26078eafcd8e54a30f8bf22bc2bd439e88edd9d9b7498c0ca0a3a353001f3d3a0dcfbd1c767214eebd326ef9d12e7bd3a9ddd4b64d97766ead0ab7fc703e98daac6555d97f6bab3dabda648f73a3c66e381b5b7a48f0a4aeaca6b6e9b3a51725aba9a5d7c453f248ed514da53d11dbffcd46e496b6ecab47b5f486e4bdc0de746e4831255569478dcd68ec057788712f88807b559696125171b7f6de7d7e3b7d8a8b73efbdb9f7a270622aeeabc6ebf8db0d174be1f5fa79b95eadd78d978d97cfabe755e3c5f37afdfcfcb87e5a3f377e6cfcf8fcf4fcd4f8e1f979b97e5c2e57cb75c365c3e5e3ea71d570f1b85ead9f96abd56add68d968f9b47a5a355a3cadd78d9f1bae1bad1b376ed8b8e173a3e7468d1b3c375e367e6cb86cb46cdcb061c3868f8d1e1b356cf0d878f9fcf8b87c5a3e377c6cf8f8f8f4f8d4f0e1f179f5fcf4b87a5a3d377a6cf4f8f4f4f4d4e8e1e979d5f8a9e1aad1aa71a3868d1a3e357a6ad4a8c153e3c5f3c3e3e269f1dce0b1c1e3c3d3c35383878707098f7aef65dd9247b9ec4d470957db699588a8375bcc00050869dc8437383c0e8f807f1a218d1001ffff7fefb5b9f7d6dc7b678c5cf6407cc7104e53721516f7de17b7dc3d1832e481421a43863c504b44a5fd8743705aa4a3d9edbf25a2d26e863c904ca5cd52226c9a23470e935591626daa0480b3dc4718f1345040e1dbf4656a526c564b9796dc0bb4d372a3f7d9598b33fa67672a52dbe260b8d96e37a123b6dbcd16b461dcaba3b3f3bdd7bba56682958bf338707c8d9b576bb618867ccd366b977e3871ef3da1eead1971efede9ded6c9bd3c36dc7bf5fd7bef45ba17890da5eea189eebd3694bac7bda7117bef1572cb4c057bd3319ad1da9bcd28bdf7c6dcdbe2de3be3deedb2f5bd2a8e961af921b79bd090afa55fb3fd109c9ba5f476a48603026a6938560d32e4652a8d96c6eebde5bd7775ef55ddf1de2be221eebd02b825b652e22ab9e6defb40acac86b3193d11dbb738dbdbda22f6a6c3a36343a7460eabb6498c767c7a7e62eacb48f67af5b46231b5468e922da66384436a6747496e2d3b3b526f3bb6a35b1adbb9a932da0c67b4a32ad990763e078ebff786f7fb7e626e899b6e3ba32db13fa44d87c8dcf256b9f7bedc1213dd4b4b8dd894d4dbbd97bc9763a007f75e7ccbcb837baf8b5be216f75e985bde2ceecd814495d5663795adcdd89b8ecca6b413538de03c3bbbf99a4a7b1caf1abd8c86b321b18f73ef05efcdb27befcc2d2f14f6a6d32a11c9f766bbddd4afa52f536b375cabdebe669bb5695b539554da92d2ecdb597a6bffa8366bdb54e96623f269eef75e18b7bc33f75e975b5e1bf7de23361a919b6ac4f649da592c5d4a63ec4d27a61a293a92de766237762766a3d1d29d24b7f46634a3dd7bb9def7de3c566122dea8a626f4eaf10927722eb724b7cf1e6c9a48e2bf733eaf0be2cd6611cd69df921d74c89ff7bd401a7b60efbdf9638c93f02a84cf224a56fc0160eabf9518c9cf5bfdfcfbefd05c7325016042bff7ce6d26f1b38812143c933aff7dea353ff6cca44fc9de5aebadf7de5bebc7ff831721de35fffa3df8d92ca22f3f716b247f7fbecd223368523e5b8df6bf68781651d203a31f3e8b2841bd33697ffefefab57993ee5fa60f6711883650dfb5f9808d1238c43be392667e11e32012871cf27f6753fe3d8e0f7ae1fe9034f392fde3631307f07106c96df6b04290fed05c92c341fa7b582168ef279f488fa249921f8a9fc341e192f145f02f29c30d02c7271f9b38848fc113872388c2c78f83f8b93449160fef93bc530349f2f7530349f4af80969f7983447389f8b90f0aff339790dfc307858fc9b3e95c82430efac3b3492fc97d10f99aaca1d44f9a38e4a05f3c9bf493329044407238287cd0cce1207085a0f07338487c319be463737c4d0ab164d84f7e0dfac7ffcc25200d4c41e489c31144e2e3c7217c1307510c4db03f0eb241a22c72b340fb99a48124f9359064ff739225c3fefc441a080964ff2581e8ffde84f86997122539e4d79f03fefd4a8e20f2bebf9225b90fe2dfc3071d41043eff1cbe0fff9e4a7af8207e2ac9a17f78e63e483f78e6f09db90fc2cfdf3b73e8f8f9a9ff7e0e1dd7441b107cda1594cd1e700f7b49ee88222ba0f87bd8e711455640f5e72218d3207e9f077b72613ad813981377c2e45154f8141694f8f21d86bad2faf41429a2ae0558b08008e88a3f08829feff7ef5f4c9abc74a0c98dc026cfa4503f77bed97c976ff4d7aca0cc8f3594be7ff77fae2bda85ffe67bdee7cf99448937b7f42e7b6c1c60a3b301c206089b04e83eff8ece3dfe719023e19c932f92e68f8d037e702001af96ef6af1ae967eb53c2d79406944dfed96b8e88cc82d150edebfbec364df7db7ceecbe43b3d493eecb65169b0794ab8b4ec6a9873daee0af9ffc5c65a0ba7e05bbe844a953c669091299a8510130523f3b3c6fa5fd3829313a8423dfe47ebb138cf64ba53bb955f4d0aeeffbbeef07c76e5f25a019887cae6c9e94eb4a0a34c687d2f8beeffbaa5001142a3881155554206595751f08835bb87c7e202fbf5e2b65b9962dbe9798181ec363788c39eacab5d4953b3397badccab453564241751905d5bad4e99e722ba3facb1d1e195066291454b1b3470ac59ca36e77738bbfda55ea97dc7350bafcfa4d69170ee238e6e43c8649bc42d47239be5a254ee972cb1b6d9a942e2f6644117f6658962fe726c111bb982dccd25ccb75e6d17eae5c9c128ae13cf3cc31d7dfc91fdeafb47ca3dfe68717e6d538dc46bed1dc46877215a7a6722bbffeb1e98adcd2322e1973b9a5cb2d5687c1fb357fccd93fdfade23f34f32f64f0cc8b9f79a2173f33f32f5efc8b53c98b52873a5da0ac5d2fc6f1ef8a5c3d39f3e26762a038c50c11f3945b2ebe1301137581f2772da5ae74925b31afbf742a8540b516a750adf9bfe0274b86997ff14433ffe25472047f252f7e869f44343ff333a79299a739734cfe55140226eae668f15771aa8c314b27a5d310f9268f2e347e61b2f2cf982c19f249f4e2699e88f533cffa99f75e9833a614ef5d5c644cfe97748989898931d7fcdebf742634bf0c20df68cf3b6de02f66cc3b553a95ba28caf54ef12f677efdd0722dd172062d75f92cf3ceb8b8fc7aa75e62fee55b982b13dae23d52a8c58bba08ce62ed660d0e4afe2e9ef7313131264c0ccccb7b2fbf43b778717139bf95e756aa1f9e4c1d4c0950fde0a7b577ee9bf356f1df913b5f471d93652cb9f997f030feea79205f85c311dfe330feea3bf9aaef7f574f34feea24225f755e16c3d82ca0e451f4064551acc141197efeef2bf7832613fa79fbfb26c79d7fb3a4742d6dc9068828192b3b671a17ebef972c73cc3834435e8cb912c3471917286fbd5a5ca05ca05ca05ca05ca0c42b32315762aec4441123159325a68a98a9182dfa6ab954647651680f2526856e4317fddefb9b93733791767d888c133254d0fd1e29944fd9d849a1f245f15c459a2ce020a97befe7c0f10f34e4a836abcd5225f536e45b365d7a35564b8dc86a692ca6ca6c2c282001413d502aa80f57478f0f8ebda92cee26536f6aed88eda7a706aeade16c4ae90e17e4eef5a10795add996583588526c0032da0f590d6589da9a7abbe15848c2ded41991596b8b41a60a51d5a3b4157234834176b3c5d4202acd687654c3f178416d6d4a6a90db4d28e98710118bca541e4a6aeba39dd1c082dad67cace0dd24ed8c366b71371d506c7ae469b434f63a90866ea9632645faf46b3325b59dbd4a3baacdd847cb1c52f75e1db6c519f5dc328705eebdb69aacc70757e6507394578746538fb0e9d3a8a94666477f549b3d11dbbf4e121a0d47e4d556a52d29d962b299ec59f589d8827040b9b822b31a4e496d6754c0906ecf0c35b4c60c6d4d554a922e15a96caad2d42047d4184aa01d0882a14e1223100443f57176d81c968dd96439b559aab4639be9e8b0319b6c6707885d6a5ba41b03b435d5885a8057918840100cdb9a6a44480850d0bd37865bbeaeb8f7eab4ac7a748463bf453a9a29bdba94a44d55a496868ad4d65425dcd12d7d15a955656a4d5d5a4a919e4d91be669bb1accafe113586d70f2acd16842d8aa5291bebf1c1b14a6a3bfbf1e12ac9d09b7a83aade78dc541ccbfa5069b620ec527a44087b23e20144496d7dd05223b6dbd2124a5363b59405a2761624536f335c1135483b636d4b6a0b49684b4b38214852b5c519cd6e6d8bc220539362365a4d00ac92dafa50694629912a8405d5c84c8da941643555c86dd6821a9bdd8010a9b16aad9dd17053891405b5ead12d6d8b64ea111b8d96eac0a1aa448a5e98b5341c0c311bada8a5833d4a693cd25b8b4488464363ec0c0655e56154b3b1a91ae4a7a706ee06939bc4766b59b5859a7a83edc64335327b81559792a82c9b16b52acba23672d07feebd416e69a3bcf7aa375c9baa447e085bc31519491a22536946694d7dd5e85b16a7f4b25a3abbe5245193aeab868e0d1d9e9c231b0d77ef6d4b9f2cb71a7c94f4e8f8d4d0e1c9a91db1dd8c90d814c967003d6ee9f3828d48cd47cba62fccda59cdc70d335a5a9bf1104a425b5a4255364502420404f4820e99bab434bbb52c90129b1ec121a5c694dbc6d4dbcd16fbd65663ff89ccd42524eab3b35791881e36a4e8662b1a225391d8596d46e483d89b0edbce946a4a5755b209516bb618888e6ab35b2b2448d112ab2aa5351680808cf4f8e074dc54206cd14d5d42922add68696b6b8b90a44a3a702049956233b636bbb548d422954d8d74f4f8e070dc54204852a59a8d488907766bb6197b5367b8f7ce64aa919bcaaa4154f5c623c6ce948080d89a9a646b8180585c9bb642d4582da5a5413622351f496eb63695d5521868e94d5dfa414b956eb618d82429105625802d0636490a44558914b149d225964d692cb06c4a8be950218b21b129520da72acdda1aeea6a3d21973b4dc210f34e4dbf4d5366d9770b79b8d05f6a6434b7768295099737574745068551a8d36556f47dfda62781df669694ac3c56869ec91dad9913faacd64351c104d35a2d28a80d89b0e129b22ed20b1291210507bb32d2de1d8225529bd11a1f1c2bdedcd86c4becdd602a9ec4d35a2debee6e3de4b12dd52852cec4d4756535b24eacb016ea90292abb6ac5a2bbaa9edac48c9762352145397d89d986a44051b5208020eabde6cb1da2c4562618654f381c4a63d74e8b8f5c0414bdb9aed86d9cd86a4a348260408489ddddaa225d688ec961ea91db1b570c42ae9f8c1e188a9330001c554234040b2962d5a62d33695b569515b53d9b44d5b90a56a0f59aa22c5d0a3a8a8c70767c4b65484245542e18ba1870d23404045ad4d9626d16c4b699bb650b3cd6690a92d3b636f2a90195c584a6150d51b51108d86e3618be1681664b311dd54a4f6074b9ba5adda824c1592f4a36849298624491a536545b7a2225509c7aab725db4dd6e38393d16c48ac7a436a8b64357589c5b129ad550357d4b2b719119acd66d4b233246a114b4b6b4733b6c5fd686bea6c6916536f3a7078a84b4ab1013c8d189262365a50daa6b2b455958a54a59a8f9b2aaba9ec8cdd3daacd6846332140404a3124b5d9d1ecf603004af001004d1877ef1d724b0090b7b6a6ee2ca54aaa0e1c997a249bdd8cd0a721f391de5220373862ea0c6cac6886d3e3837b1a3d3eb81bf586195b244362d31e4040b254452a8a15512a2aa224bb4196d2cc72d9db03cc23bf837b89d86c3720aacc16535b686b2a6b8422c5da54899dc1d0da6cb2540d92e4d6d68468a92c5583b4369a1059aa22a1b6a19a6da6065165e9ed684900b254ed112b024311a516d41b6642549c8a930129d6a64becac859a8f25a55b0f96556f47424b4ab71ef7de17ae0b01b897a6d66a29120b4b4ab71e6c91daa62d5b9424a5a93720a0a2d686c42ea535216a9bb6428e6ab3a3946555188480811651cc60050bfc808a1544b1018e02c474a6cf03a18b079e08e2437043ae198109183cf400aa66c5c30b42983ab2e70113902225c78a9927332f9841112de410032205e4d041bff7f210a219a52d4e49e8e602c0c5bde92d6330b949d4a4225a1ab371e06620466943a7864e8d1c9502d8898baf9ce09627c0dc24b514894d55224466b24f5f35922221c56648479e46929b4d49557a5b5b9bc9521b4da6b631dcad656bb6183bd3499f880d89c57d12a397a9af1ad5d2d8bd2cdc7bf78e1772919bd4b6e9a76d2a7b36497a8496be4c656748edcb6ae923b129d2ab325b2d1da2e268e90c404032b576e4872a3baacd906269ecd55bfa2a0df7dea07b2fbae3ee9862099764b3d158bb7b1f4895d962afd28c52a458aade6ab897a90f549be14072ef855971af11eebd04b825ebaf4c9ddd5a578f0e4f8e912c479ddd5aa0974c35fa24b254bdd1d27bafcebdf7862213b8f712b9250dd2bdec4da74895d96ab35bbbc3aa4666ed4c69765bdaf171a9448c8c5c3b357a521e974fcc86ccc7c7c60f8feaca51dbf4d6b2463876876d71b6a4988a14db69674798878e4c6ddb9992922d86519da4586a64bbbd6af4b534f66d8ad47ecad6d25bfb3575c9f6b6189a80c22d5fec5a9bedf6ea517ab3b5ac12cc5bbe6897b4bcd871ef45a1c6aa36d9cb5215e9db9a7ae3f7b670cb992ad89bce4e91daa6b2192d35a2d3aaecac9dd1d2da4d6d91a84f4bdb9aaa54b3dd66466a424aca1926f72aa92d9b84cd59da6177eebd30dc72c6c6bdd764d35b0ae46baa7aa4558d5e6553f516abcd3e09ad8dcd6e2dfbb6f65999aad6fea61a29e16a9f14b3d18cbe364b91629f24a5a941eebd3faed60d1b3ef7de9bc3fcc2bdb7e8962e8adaf4686624d5463cd93374285a56f639723cce4d4da5e9c46eec027cb8f716e0963158eeadf13944682afb39723c928e7a2453d99b8e2ab3d57654f5d61aedb035dbac8da9349a2db6d332528918bd7a541b3c2d1f998db487a727c693a632998ba7461a4b7f5e3d2ed5c7868be7b5136b59dc6d8700443e584081a02c3c8e47229ba56dda827a940251712aae48d19292aca85569b474a988c5d1704ab858daa62d1ccd88d46c2c1ce0de9be396313b2e6bbba937fc516d968456b3bd2c55696f6b9f6d6746dfd6d4596c4684088e7d99da129921b117d370ef95dd12268abde9b4b17449c916f3c989a9325b2c27493bbba54bf7ded62d619aee95a9451ec6e373809eafa548edb3ac4a4b524b9f55956e298bb3cd8ca83720485459cdc78fb686b3c58a8edc542336594dbda96d0d6763413d4a81c8541657d4221dcd541a68294e36333293d566454852a512dc7b6bb8650b14d066b81b3a3d393622b5598e4a4b63ac4cad1dcd5816a94d81dc7b7368a8503b9ad152f58619dbe26c49d21756b877aa69c8f594bcbbefce81aff78465dc057b7785b90c56e1af67bbae76b26378f9fbf8d6185f1759e7e68b65d82be0ebc1f85d75ce6d649d7b60ece18cb1ee37649cb197658260dcb17733267574eefd267cbc73de3abc31ce213e42c6e0e57867ad62bc668cf32780ccca38f79d711032c63d6319b00b8c75fe4ec0b5a7c23218c77c1a4f0067dc33c65917e1bc43d0461eb18e0ff3193a07f672de35749833c659b53002760d91870692efbe190378eb9bcd33d6973f00f300df7ccbb2bcf8e28d331e02fe720a18679eb3c6d807efe00f8f39e7dc59c028e4bc33e6d13ae28bcf06932f34190298e71778638d6f06923fc4fbe659bf3f0433405ff304f8ee4841d6b8e3ed61126ba79c3f208d7f34ce797f1ec11bcc31f678760d193c3887fdcb1863ec610c8473c681ee18cc5b1bc167c661ac73302866fc7938efc831728c7c82befae215ee396372977ae7173967dcf12682c11958e41973ac72618c3166651f9e08666cf48d5ec632e00f639df1c63df009f9956164317b99638d33ce62f636d65c63ecc21adf60108f1863188e9143ede16fe38c3110c65d26c3e09d31dff89545ac730cac75ce3b6ffcca19d3e41083f8db19630c844fc8a1a7f1fe36c6d8f57826850f875681638d41bc67300802890560c5c000d838e79e3d1597313c620f8c33ce18e39cebdda55b03139e22c010ee1380882c5608604e51050d290f3a200029a42e9179832b840818a842d2276e1022a64c208869fb210311eb1490d4c68e17888861841e290c9e1711333baf54c5019714e40c9719e29614b809ad215950a0f55baece1026c4c8191225d390ca808dc6f3a4a0c2130d22c818f2a38c77f1729f5a18e00ee141dcdf09b857df7befbd37051df75e1acad514f6a693b6a9aca82f096b5ad0e4811eb8555fcb03f50923910e319cdf4d0fbf9f3daef01e7bd13ea249edb2af2420024d5d953d70d3204ff8e9405338c328db34543a641b2c2e808d3b0e1334e578a1059704e41dee177092d0e4831d52b0a203e6402e3276e890378dce59c2a60eaec20c9a7a97016449cc2a88073da4830f4d2f401a469811e6061e314b1f1b22a109470b0e0d6940e9819ae449fb07263a7c5bd54fc02b1f1db058004c81064b3e2d10506591ef70f15edb05b500bb1d5af09b904768d36dd82978af1d9a206268870d002153f22c21632f28c089261147440209207b567806181ae6bd68e9107aaf1e7e2aedf231a44f087920a129f47c9042be1e2c211e21d8631422c3d144938b4da31576a016640f03a1e6a8546db396454c3285d0c8884000010000b3140020301810090583a168384d1529a80f14800a98a04e62461888d224c97114424612600c1000000002202023230e089f67623713793497e2bbe53982023967b82d36261b1da63305dde7cd7072c48d67a1397d5568d4d74c70cc5ce9a5d25f2325e5d4a6908dffd9dd6c3df8fd09b9ed28e0dbbcf929787ad5070e1957204ab6783f41a70e507991959e26bb8a162df61deae7f3e00eccb943a36784177553a66e5eb54e2c9a00731c1cb4d8684e86eb5001c5f86662ed2ddce41addb4804ee3a098086d716b6e2b6f8fa0d69d63f12571ff36e5b13dfdb2236db4fece97fe5de3e7de00cb7c090568237012c2d34b005d766e1f07f806d858012e9799024b4a857535ca4a1deb8441207d1ee7a53e16862fec7a2c13af0a3f3cd8a7d84d45346efff1f14968a2ec73f7be0412acd27d9ca86455e07f5731012e0de3219e241eb53530cca6aa3872fcf476b4e589e1620a9634afd557c0a399464ac79efae1be9b8c4013299615e2f5bd5588e93c605d9b7e398cd8c32730dc608f0a44cf17b34b2aff17922d1b2e0980cac09af8eb807735a9e01d5231da3c9c9d2ade280e2b1ad6c524b50d4620e9afcba56e241f22b7ddf94a3f01e36c98f69cf3b983e108e3ad8e0c7c4cffe9c196e844d69974706b607f83f40acc0d750a69f2d10a6fc9252c7c15a25742c15101fc2c02b4cd7aef2a18ad5917fc4ac83ca33ef502dee79972fe1bf3f4945b4da44f63f9816b406092f5db67ae9d4da30e0d5acb0312a647cfcf2eed910149a383d0a76b2e0da2145f137714a7934ad100b6e3b97e948b7dc1cea6ce2e178b42df0f326f3b5c51bfca512e9079bdd2e863e850b993b651f76a42cf9f51e853a46ea7d3676ef326d349b49fcc2626330724afcd664d9434fcdcaf946bae21f308167561f88859043f0c46d94f4a50eda988fcb8eed1d2050a64934dfaca3d658b9f175f29fdaa42d89abfd80cb3349d37758d3d20a6ceb0878b9c361a2648906b65f9f1ed59f8e0d30e5f78a68a3d7da27274476d013c2c24612b82d9158b925a2fd05e83160984bb8813a6cb3dfbe9a65b01150d8b152aaaea4615de18167837be30aef95e0b6f19781a21cc3920f4e358b72a9bf8c0df07fe00e345bc89fc7f8f624569e5fd2b139dee0c34e72a511923d409639738ffcb62ede80a1bd9be8eed857d5c5441d2b114303334b154f2de15fc2408f22ee1e24ea1e89e33eba61705aedf0dbcb485fad42dd2d9bc055a27d38322bf4511fe3d1ca67344eddc0866328adb65b7efcc9a459f90a32f5fda84f6886c827c55a470d99508436f59d0474422b9e307a6286665be82506567b1f311d475d03da1e3dd504b5057d618f9fd44e15b0ea1aae5267a138b1ae7885b5a37680438c6621ee7eba949897c41a016a811f9f8c14557b4adca69f2f1b30ec6db1b9103e38dcc4cf64b2cd5526133ed57285565e1ad78d746fbcc12ad5577c21f039bf11f21e21505bece1ada2f1470dfdbb8682938064599f1a13c4cfaf6dff3b3cb228bd9b9b58f625d7912de7a4286443b155c5dabb6d898560f13fe335c6963bda8415940b03a1ab924cf55ac8c342a80e000272494e412a63664bcfc03812143751e4c54ccf8ee961baa8cf1ed364d721a480b9deea05cdfca84f59f1c32143df9db10ac288363f2e2f443d9c2305057b8d3281016ecaeedccdda433574ea104046543bf7775b568950d051bdb80d431c3e64277a0b5f9ae0e8066f12003a6881dbb51c58f6903714c9453e3aaa94deb4f8e348ab5e98f06549ace03e4c9051278fb51f5d09c6d494eca12d5817b7d7dfd7af376c7506243418f1f4d77b3481d959a9f6ef4c6acded51d4f816fe6b185c9ed280a0d6527ac1a589ee71c25fd014ff4f0ce5ecdbd83a17d08c90364095a8df7bc6184fe671fdc3c428486f4f86a1f4400c2cf2f9cd52b35357a8fbe758d1bc0416ce4d08baabaf29ec470d4be8c447f73500a61038014247ccb7c005889ab510afccb561144dc550003564e3753ea63f994cdb35b43540933563444bf436d1b13b7e7db599a60af4b9909cc6af76d819fefa31c3f939b58777b27d10d89d8ae6d9bee458da733dc1d8f52179a33fff9e67152e9b7c175d906bafde3887cc2bd3fcd5df140f0d598ac4896cef31d53cb4f59e6eb719b2df121dd5fea8ffe2d74f00b750c5badb6097e0f4bae37fe44e33771637a44c573580ee20058dbffd3fe30f810b34f0c591e71ede887f25d3712fd8f814b974df515857fdc2ed11e9799740b647d99c0fc2257607d00279104679c5d2940d74a7143740a7cf0013e27bc7c8848de6f9d30f359157ea4e553646fbbc9d96c5d3fd375d9907644b686b19b3dcffae4a59f930ec014dbca0f087e57d64fa615cc6ec1a34bb3b9c74a56f9be050c4e517762206496a7bddbe8ac1be9fc65552698a837f624506bfd33f35178fc0093b72ff0b87c24bfc5e7d077af4ea258acd31d525cd929b540af8de1f2bf8ad676e40db13f4a245de320ea43528afe59e77f0f1ba55e2ebb1165a51f6ef9872ace60fbcdf62ed02fa1efaff5c19f50eabd41e9afebdadc773086bd2f45fdf7c83ebbbcca792b79965fd3b17370dc3d9137996fa67e2148edf53981ebb1ea8caa27e69b7e62c2fa2bbaa5eb6c8a319fca6fd5a732788289c9b095e13f7ec7fcbdf48f1e380a9afcf2c49dd8ba51ae004e2c17b2bcc1376ae85d67aa755b7475d309f35832a6dfabacc5943ae4fab8714147f62aa608f4571ae3e600c7cf7d5f1bde63457cfcae04964b8717022766485b285c38c47fcc73ac5138b645e3f6cb1ce5a61a8ddf5b31b2f40f000d391a30bb0f523e45ec3a63a3434ed1b3759dd13743340bbdfc901d30a23f9473b43c0bc637c786ed01ab94d237e2be7d2dff39e39dc8987dae28788bd936003882041ee80e346f1a14ceebcc7185d51109ec9f5768792ecb87c3797942e0b0e9e7cbff891e325dfcf7061e61567342b4132439ccd8797e71c09dbdd1f3fbfd97f63975ee7ccc77eb993cc7de3101b37b329f0ff8fedf592fbfbe97618b3f38fda1317f650f3c18926f8a119e580d284e2027a2a32d7bc4be226e8b30df087cdf00ffa31d8cc35a8ffa9d03c5edbf9f90fb0f3a5f8cb7f465a4551f3e9c6e58086f7c4fd4f223e537d3dc75c510dca8732cfbd8eadd566bc2c4f98da58c984722fd9fe5ccb30a75010ed7c3c0fb12778030098c8c1c7bceec1f27bcc6fa124cee6b006917ad546464352cc3f23fbc1b8715b8640cb4f31adcfba1f6f55b96c35b851c4690d236e637c7ea35e26cbf63da7da5c4f8aec2e6fb2a5f0005667b0e90800462e2617c4a68bae2b08eefb94452795a24dcf779b863f7994b06bbbd931c85311e04c4190664413fb0b63ac01e4b4f3d395071d057ffac75f8d6044086477f760cc5f59f6e233d247cd4d1497316c71cf52b6de16f2857d6aa91999d4323c490eadd734a8bf7c089357ef5d905ff4bc9ab9004de340ca40f2787b1b7f4e61d73d861e033788c6197c2fad2419ddac0fce72bd4e4562a88010aed5c7a681f772159207d94910972e7fdbd7fcf09eafea49e0f25947eee94074488ad2a19703f58b4e5eff9eeb249130195cf7b84223ffebd207253bfa01ea975df8ac9579a7e98d939c1a274f7c816ccf56474eccf063a24c3e678ba80b8e47a42c6ea6033601de0963651668dd7e03a9c66634f97c165ff4b60e1ba32b875d9577a0328a515b179df36dae947ffa6c5e86ae54a9ba6d3cc86d0a46c44164a71df3a7b4fcf31832e5e5677a3a9bbd6d2128955635559ac92ef0432de07909d84152c5a334fa298a0a1fac95e29d66d80ca185dbe3a39336dceade3f0822f9765be4b05f8e7533af613fb99f99c486d6c577c417d6952c44ca8de6976d110ca72564ca71aeccc1e09037781edb3b32abb1044e7e399796f37ec5f8520b2da364b81bec6f42dd426824afa0b382156d91a7720e1b94d96aa7b19fc9c1604f7a6b84df7504483900227ef7d39f4b7fd93ab1f7e6c28cb719dcc0d1192e3ac662bcb5a8babd0f8e9373dd7fbe702f72355c6e22947c4d9873a235d76f08c593ee9571eecd1b693f7cffe00b9359151bb0100a73608d3451e798f81b0db652bfa866b8c2c00596971c1d12fd511cdab99ee50f3c8ba0f471d11e9aa3ab8fd430cf68e83fc6754cc748240227a55f657b359f91b4f759ebe05334e23d26148f42158bdefe3e5673fbbc71550b011c8c519cb611fc2799b4033bea2e65491bfa8a04191e9905adbc8eaa8ee70b3f1d310196627767bc942d05af95b35551c510a489cfcc3fa8a1360cc5da1107136dbf1013c7dfbf24c91d0763f92e05c21aa2101d4a9d8f141d0e9db2d1e0e4c88c6cad6a2c30b4ea00504341b89c5174dbebfa49b53a56913e5a90557fcdda4180cc8469c4c461198799e2c76ef00b4b7c8c3f4982644f92ffd18b97ab3db30e7cc2b3723602bf3b6b7b162170132c5e64011432725dbe9332f24ffad91c3bde7d874bfe00a752defcbe47d620c0c54dd2cd5407094ddc74a302923b57aa5112c084d1c750c45dfb3472f08cb1af00e8a23c70db48abfc037a25499fb482ee7ec1428e652628df4281d8ef171f248ea68b63a6334aae62ca1f7d97aa6350b65e98b8f84c517bfb042e96a235f3c5fab344821ca654eef1f368d6d66fded23ea8f4bc344911bf883fab6123b78a567b4dc6ccc79646c8e58e0b0b5130bb79632605869e9261726a9c860f99016d9d96fc680281676b9e3e2000892fb05882d91fd1526e2b41cee437be61929712a268459ee04bcba6e5f8906dcb92aeebb3c3799475dd81642305db095074c70dd3873007df8c27f28cf069216897111584c90522680d1fd006a484a920de2bd4ef97b4ea2e4f1c2d58cbcef1c5890318eccdeb2e501b544365fa31b3b79d0f7fa6c6f86648f3a31b6ec39a5dafb77f4bd49440496aa5a532384919feef8fe7213aa7e0aa20f65ad9aee3005cdb07e9ab268cf82b5b1ef07a0a412696fd69e6bbd313be5abb1dab9e2c05f8f001c43afb6b40146753ffbb1bf0d897d43214610e281a70773abf7c82e7dfb04e28891f1148734d68a727c712f23bb20d4467d8204d727343fc0bb1ad905baa862ced9cc859cdfa90f5bf867ea3c179e073829d274af32a924a694ad81db3d4a6d91bce6731a0ec21eabace1cf29da2cbce612b4d7bc65ed73ac17409a52be14c841770395928678496688a96a436cc885db3eaaefded83a2fe60730bdcf298240c87972ef8589128a66c36bae2db6220039372cb72ae4ba033a62745aa57cfa1093c4cacb244748fae23bfd16538c3583999d9b7dbbade3312a29d50709f5bb4c649c3493a63ae46a61fba9ad6e15cd6d42ea84fc034a16a166742a2f46b949ea0474601ae7fd1f828881680760060868e2e74f4bf50816e855091a1e2781fd3ff2dca576c33a0488c22698ac505f26e9044600b39123640cf6981315c442488cdb76adab1fa83961cffcb31bbd56b63ecfd2384d87dc2ed52fe602d1f96916c6ee2bb7ac4d589eec5f39ccec9596c31b99bc4709982887397cf23b5459721fc088db5b0a52363605984afbf681eca7de5d6673ef0e00b320bfcf422f2a79d84271bfe5e20e1f46518d90e39b872f0adc2377e24dd1648600368cd37c19d280e6925d9bf151b0160043d79214dfd2272c160dfd653028cfa6c9c54abf8e5aa5c6d2bbd1b6ee03c0cf3a12a3aa871c43a4dd291b83c8ead8de9045af787efcb58c16d7b6f89e6d468bfe8537655174a55540e589c5c00d2ca6b21134dce02687d3625b118d1e7680156eb33d19b1c756f147d050b54acbbf1a8996e3c3b8838c16e49aad2ac00d39dec46e9d8778fb4460d5832fc83b0fad8680330039d529fd6a7039d6d0b082b9b98ad55ae095de3b020677f2b9190e05b87920a9c94eaa5701c6dc2d3c00f23f73607d851164a39759efec9832516af27d30bb11a8deb0cef654ab0d9b9fb8434f8a20e69598d5a30aa0a41b2fc5abc68830673a498931c1fb2ed638a110e05b5e08218409dc71546c9129be81ada190f10dac09a75bff27657109209ad395add87501977d384b26322403efc9b7b472ba28ac8889a7123fdd670478007fda1b799eeb846f50a7bb07d953d33e9227a2fb411165c5beb98f0946b4b7f57c4f3eb23ae443c44e74eaaddc71ef7250b3e9319af58831491c164444b06b5aa0d5cc660a128d7caf66f68cf8f0f90de4c978c9769bf912df6a67d5ef61d806faa8109978af05edf62a6bac948f6293bcccce0ebdc6cd77a378334d15950765d5f8abf94529c16f72583b2f4bf4dc9535ecaa325e66c276a5f3a0d67e36d07eeac2b8b828ce20e8bc5d48cca36e1b0a96f69979881bdf166f9ff7ea6ea7583439a25d2ed368fa87336aec13f105afad55e49b94d0f5c66809edd92f5a6c97271008bdaff2a2c167711a8d336624f0ac24b49fd9b62f5a67cdae07849601dfcaee4d2f92183756f466e7cc3315ebd4fd43b7268a287a17c3f9e05e1f08a66b71b471b4880e24572a614337f6d72a991b6a27973f5c93a8407bf5f7e8d98329ccef83d31bdbefa3dcb0274ef713d9b4a8c4acd3cc0efb10b13a1b973d9b06b8208da3058b6bcee1ed9c9a301851ed89549fa9be6fd5369ef9ae5d0ad7dde4a0e52d858729ce27654b4a8e60897d1e2a36b819ad686359e87191f396fec83e6d484a26e293b5440422c92501c9b81102eb597455489739859288ac44b09f6b3f2f0d6d9b0fcc5a50eaf8d5945c9f44f8ba2dd5d1fd0d50c1c44c42a8b01482d9e2bec8d216b3a94a3c4a7dfa6b85c812257c6c249795c4e0ef4edd92e4579bdbac217bcd2c20791295c83e04de561cfbc0b9c6a6e637b0f66a9b11e5b684b3d19acda4998ae2603a93aeca1a1af599743a09f56fa472ccda8bfe663ef37fa27a8e38a8b89cea9b95a8c921b1533fd92cb2922c876f31eb4f53b51a3f3445e0b83111e804c477943fac87466450b4b2fa10b1ec6ca19f8f341cde15419bcc7c2372bd45ccbbd113a1c2de53af8da8293dd59bc8c40cbb73717fa287fc703675751c6ebe5e63e2c254a58259d49db489c678a5a00d60d4c0323e14067b0197f79972af604a0784407de0326e13a390b785b7e7d6cf38f8e2248613c4f099c46aecb6976f1376d0d75c5621a0866ac1f7ae0f0d93904dae64a120203434515c45b9b097f26e220a98cd056dff90c612a196d83dc8ebcd915c555a3d479fa9e3e1583891e6983b0b8b12bc42dc3df7e6c99db5cc13571e9d601252e960af11799f35b54c44995043408a55e894e5d926b6d0f46a5a60d03b7a94f91509e20d37575ca0a0e9f34e7e9c9caea5b93d493ed3c44c97b9d8fef67df1acc8e0a01c8baf1c48745aed1a22868d43d68385c5af250c39a9cbe0e903c60df2764205691da0019fff90cf04fc210bbe20371fcb5b93f1dbc6b4b59802192a32871ca766d904fa57aec1506e41495b4b9108c8f181d414d5cffd53066062ba32b56e6bd5913e20effe0a135d59d2f95f10d6defbc38fbf1ebe89ee6d0a4954f2a40d1761dc2f7b9dab75335427eb98cbf065d17130a32b5f2e05fbdf51a40075c98b19898970fd5087e4689ff1c000327d14a8d503ff6fdf08d70f6b71f7f28b1da49d611b45815d21e608bfb3fc23e28d5676ee36ef016637636077a17f243e6555d5be70eed64b116c9c9842b840217a4a13e9d31c93a6ad66cc7f524ce93ee0bb6d458e0c31ce8462fad25b3816f2e32df3c5b60c7c2fadeb13c061c170e90e5526a4e62d2729961074b905c4738b35eee92bd54381dd52ab3f152a49f81c02d83eda7a85ec9f0597735522dab9836c6b03eeb9c058f0fa3123718b7a57948429b00bad4caebd9d4c9a808e5f0ef69e35f6f73752986d75ca86920f687a71f28e943b7242f7deafdf269582ef9edacc2af96a4a980914a20653089962c95d4cbdad20263819601c27cef7f2f11b0187889640bd24a5a5e142c93d6f83b56ad39ae245444741f12550a692cc3c15f1ca29dfa87c92abfd9270ebc39849b652a0cfe6d21450406f231f93bd4ca3c280714525e567b0ec8ef94d2fc6e146165748ac93b7c670d4e4696ebf12f88dd5033034a45fead4fa90a809cf706aeb832deb13ea75e0e66e5f00cd68ffcd8615dc65f368dfa7cf6d033601facbe05998a0a0079c65427f8ed5310c614cb5cca792c2de153de21e42ee0a6f0d88865d703dcb9e1679b739288ae8cfd9db91a4e64d5d6af8426fb2d1651c01496821e8ee45008a30f8285c77ce46bc1b77679802471fc4eb08c5a04e09892b453ace9e313a60346ad9bd9d11f64ac4fc5911be7dd824ba15156074106f39721fc218b6aca5430168b96ef586b85dde09ffb8fc42c4d2ccac6358c5b74a03f62beb894bdf1a7413d836069b1ba7a3b089e140d2067a508a6059ed9123e9959f793f8a0ff5d3131348533897e1856930049c2de0f77adb1ef883aa94b16b20b83ca7407969a56a45e0c09cd84c18d083831f29b097b324d761fe0efb97390e56fe16919cb9d2403c77db0bfd288ebe980ab5ca87c0612138aa6b72d1b9f0b52228df4e7f13ffed56dc8b09a3956550405c4e93ab9967e76c10fd4a155fec028c678e7379597d793eddcdd4061e8cd9adb2e2281c304ca39b0ab9377f00756ef9f03bff354e3d2ef82b7481a749cdac486bad5f0e77a48c84a7b54fbe5102ca62b311667195a10216009f1587499c4b57e5f15b8d061d9e7aa8387b5b2ef87fb03184247231b03f946f1a0cfdf7020b52762ca540f63790cf1727b9d1bf33a2c4d78e2c6e550d6c250bd81c96a1c9c634d66836164cba4c1d6173608fb02a0a2540a65c77bf7969a850505bc8c5abf32be531e28b73d083393e2b3ab0ae7d660a202dfb84f034bd741830fc07a9deffae2de51a78b2990cfc6a63e9dbfc9d5c2799592d06e3fa8cb440c21ba7c6f7e852b47e4b6fa70734d2b694a50ef07e71d5b0b48b9a8e91cdc561f9268b6651cb8bd650363072e3c5e2448ada57d0a44f1702a94fcb2217477404512009f88e18de86d0471d92a264b9dab16bd4e55e83ce309d68c3f6054fc87927e44d71daadcf466728c3a43e964570028ac655c894c49083af87b0fd82ce2e2bd384627cc2977d4908799047f3b5a424308086db1a635edaa695554644645740f71b4f6cc492d1ea7f91793311d4de3ae32dea9d58b8f426c42ba25ebab5c20a097fa4fed09a548838c471f3a33ecf52790f6506ce11d7249c0ce0829ada4fe50abec9118d13e3c38d650456201f01997f788ab21782b9f4455b068157300039a31f05ab1ab44f0133a1686fbdeb0d051a22aa609baa06bc4ab0b18cc0a50fa3e2e7621d999cc87fb0feed2dad488f033aefc281e243faf6805d505f24c4d809830f2891041d49ad2c9859688ece82358a08b3ef534de6b3c42409d3774aaec4ca59d72cff007fc0a8960fd4202f5da8157905cda899608cd75cbeb2e19dd523509502f6a6df388b60b6db0ca90bb83c8ad4cdf75fed1556d9645f143689d16ccf186955a505e853f811e27f715bdf2019f2063fe0a2d4ccd98ef8e2626f27c70b4d661ff822721a545716807e5ca6fb333b699880b58a7373111d3116b7c3d80b349b85593129946bcbe7452766847ee56b6291eaec397895ad4f0444616bfa6e117cc83a615c420b4ecb3864240ce55e12d9c0bb6a59976a18a6f6f7140a26bcd9b2941b9e4555897bd2122b78705e9c45cd076cb29053e1aca9d477064f9dac7a1e28bbff3b620acea153f9f4bb976ed980978d121c0e7f0d710db18eb44e932efd262c5e5fc5fa668767adc8650a135e144b67aca90e4a04c64e2658cf424463d1631020c29682c87b59efdddb370f17fa83b121caabf8f0c597f94521878ec4623429c9a61fce1cbaacddd438220a611b893045885671191b5cccf2b677ad81ff618c1e5449d50d9cd8af9510aa4c7340660f38abf8a02704fc7560561a8a0fd76627003fd2b235dbde039f97f754ffb0134011b7272ec865fc6df38986a4db5b692e2e2ef0b1e70611860011e038e15978ec5e241dbe22dce3e93ee336b3ce1f4ec1a590244045c3e5381badd604908f30849168a8cf4008abce53488020c4141c85c46c6ac5b9c91bcebcf50ae215db5ef9f80de8d5aa04bd51fc3dd35f440f512afd2b50fa5131ef5e1edaa7a76f3b764395ef47e0c4330b7b4e899a9b381ca0110acdb0125ae8cde968b0e176e353877e0f142e8779bbdcab087f3e6eb27d9458c2f6d8d786fba402654761c2586c7e481c01d121a204c02e3cee234e416ccf8802100d69e2d820f86d46569a1c665ee238afb91ddb6a20cbb7b14ae9e3ec5c484e0aba9ecd86b7ec57ba4d09931f9090c895f14fdd3b2e086c003a028a68458a6d45740f7f698adbc04b15c91b319849aa99edef9f50a708b33814a9357b75989d73d878f5c9748f077b265cc9f01a1b2b21345b80d371ad6092e0b2e49d86ddc4a1e18c97e0e1c4f03a77b30caf4d69db0f4b1df8303cb91fc7154a790ce9b736e662b5219fc629a48daaa19bd8e90f5ab24dab83bf34879956489a8fbf0c2b363a1ed067a568568a7be63f4807cd3a0444085d3dd013fcaa009df2f999ac65d78932e6c3282458821bdb8b22f76311a558bcaf1a381d3eb2e3f32df8fd567c815e3738cffd39d00a3db30d204e2c6a44c1a3ac82554eaf00e8f76c90738fc180d43b9fc67d0d81cf9030227dc1aa68e1b31f03529d84223a6e2162a8d1230927227d1293cfe37bafaec6dabd8976a373a10fc5d6cf2f13499c7c1b84c0d062183e794efc89915ee5e4094139e6e6eed89e4d5a0e0f02238ac6d46bb82200e07058214896228ce347f80fbd28fd6769e978f0fcc5bff1e6de217044c71f3ee2c5a9e7b8f1b3c86df9ff7db776ce1e084a0de0a2e1cc85a2e3a4e871a9b402949929ad3252480dea5f7356f23d1e69d62376f505bf72ad0b9f128eeca04b4ff299c66a9805af2bf459e590f949ecf30c607eb0b56f5f8355b0ed2dd78a2b70b2eb645d386d912494593219fe041f0ab44164ef4b817d829ca3c2bd3c1e9d98626b6c7bbae80171a059a0cc5d27a5caa03ec92e4bdbb4016da37de6e0fe9412bb5c65a79cc4b9184719b398884f12236ffe684e0fb5529b4b28f1b3548bccbcd972512e1cbc994779280094b45630e99eeb16ebf6b41c252a6835e885a7085fae636c1e561b0c2df3a24f78faf72c41a9a704c818682aefc44bb32f8d76e09f4cc9d5ee8aaffdf038b229c448cbee708385c7eaf06f32b8946742c769ae101132859571dcb36124022432a7842d6408f1ce53171dc581a86489f1da53b7c91e85d8e9b1ef82ab73f7c39f43a2330ed8634a3b8a6c51045858bf44d42098c0284cd6079c0490ed0bdb5cb234a0b650e46ce65b3c001310c10b7d47128743658872aaa1d1b22224592d4f955019b4445d990dd7416932a294a1145db1b6d449955eec199285bd4fd390b8d176b74e70370b75d5019e2671c1e6ffcd4d76243f3ea77a30d274cb9dca8cbdc7cbd05b09830c2c2b1b4952f631031b96c923bf2703b9499cd0fdc53672ba4074b3ea05927175d8b32d3531af104a5c710be576bc45ba8e6b3fdee0af0ca4904a7983dc867ff44c7036e275b2a615fd6e6ba39de373099d39a6cc9dc11d3797e8668e8c19ecde16bc412ff8b587e31dcbb8c052fa9461e64b352161cc6a0a71b7293be6588f30f6ab5f897270e16b30a6e8c9e592b34fff5de04fdd189de205b79adf8bfa0de063402c1aca10c2a472a9b78c59cdc85b7c1cc1ac88184fcf7b54c3eeba8ad0fa59478d686923c8b90d9a9cdfa31abf3e3cd89e4a552a68a9bb01d5ce7a34e113fa9a2371415d6b0b7201ddc08ecb7f982f8eb401e6b161d1891ab0858c1c81e56304d0520a14749e9453578fd586ca6eb9d75b6f6809f632bd51674fe164c07e1864b3ab4ce0ab8af15e4901b7ae852594087ea80d47892a2edb69f0b6be67adadc6ba444b604ad2bc346b8cdfa08ba7dfb0040923d6378b36b4849b4a8e0fd6bdc619849c16b4d44c9aec4d48766229870f6deeead2a9cc20359df2bde5d78ca7e5d981febbf118e85cc2946294dee9b59110256dd6dc204a16c8effb850798a78328a514884e153f4253b322c319e791754a567fd0db297b0939808d0c66c1b23565a4611657590b31d9e44a65aac0837cec4d13393eb9961e8327dbe821d5a9a631321303cb71a4d1e3fba03c2294a0ca5d2e2dcc9675605a2ac9057651aa29500002af747943e13b91a05ae4ba886e32945e894e4b669d4b0be80d366c3b7a6c1a8515fb693fcf35ac8af1da31ac626062a71b4e4761f1abb1e1546fb0396a8ee298289ca16006dd7eb9e719c285284cf6447ca5936881ad8c4038a9b7b8162d7d1083d85265ed4b0bb907e9c9898f0f1200366407ab2833f4b0e7f1ad993119c3b11134b5549f0aa4437315c3e8f806c1334e8aec4fca2238de660d07582442f67f8f022f9c87edf94b66feffef9e140934c6c907471e73c2542e2d643bf0ac06e3e714884640dc2ab278a111ff26502d56026a32989bbbb463db82878d6e0be58a8a83f1144f86c00c1850db820b7f654c79e82417b3552884153d733e8811f9c86222c03cc6b9e3b8e08a950ee76595add7ecc83941b9b91e07b4e4d57e0c333bbec30cd1779878ccf4921fbfdd63adb25d0c516016b561f87e934bd63708c2e7d09c7bf01157a24f6d335d458e2ae61f250f62e4ae3917ea17606b67b470571393cd0364e875b8f04aaef5c0e63bdc7dae6195bc26dbbf9e5762c6fd684b572f4deed37b208eb856b266e66aa4a37dd6d4ef875fe14e2291776be110241778b41619a6b821b137ec3897bc5e5288c0836ebf6ff8370c394598d3e4745881780a475b9dbf6604b98cd9851dfde27ccb3c9cfc6e6b758f0f65e56ac345d5152d8245d80c798489bca97d0df00674e3375eef38871b1b61dfdbd2feeab3b863c7fb894a8b8a4290461a1e44f35337c6ef47680a7b48cb5cc925c6a7d25da3a17fbef69caa35b7b8e1f7b1a2e6dab1a32422ab401c9886f5f6db7b5bfe87aeb9c0a5da46a5599cf1e3daed0a047ce32babe31c84b1745c65e1f36689e31b4276b8fcbe6ded5d3d468d04e33a3b4dc3db9e19af1362f6ebe64251e16dff8d10d7dd4233f6ba6020bbfa2e44d267bf929162ed3ba75f4fc55bca820dfba054abca50c4b3f7a7118d24cf9b7b5a5559a153027cd4b0cfddf80c4b624055c7193d5c0035fae1311d458d181131f088d73fd5d9a096d91e08045914256fe0e015682ebed0cdc788edc01eda3498c03994db3f86b29ed51a6a854159209c52b413644e588a646d9976702dad852f65cd528ab97dafb0373e23efa5476f8b43f48330125adfbbeb57e200f0ad92d60b7fc0679d2484792cf3000422bb60ddc191ab3723f5efc1538a78f8594317faa5ff01332466f2f240df05c31f48f93928a42343f9c9d05b07b671e94a0e627a6989393f0f8538eb4210647480e53ab9dd30e9f4256feda5e8f1265cb5ce1fb87ed589f8f9a33bb48dc415de1efed63f5f6d9764c4294b7fc4ede543ca365cbec4216c000c1612d1a403d538de3a7d5b5ef50e6465355576b82300c9e5eee2228a0314711884246b31658fa9ccfbd6a97f9f673f4e07f36b6fcdb885fbe90f71fe06bb08e7e1f09b42fb730efad4a90830132b7336f2a811f76259b6bda2b8258e90b19a4b63876c176bb618bdc37228413563feed32d15f62e720ceb15e75cd780b83766e96b4726390b7fde561a6c82da78029dafc383c06b005743cdd5c8f47740b14454386d1b81c30d3f7363f75f81f3d704218e0546764b2a50f01882e91f517830cad89dd332dc90f78bc7ef1473d9778c88b7bfb8be7e14e473da6c489b2fd4562cb11182098bb2eab4f802bda198ab654e05a0c9d521a49274ebaadfa6f972a7bb63fcb0b8482e50e5e2b4f2e0206f0563556f9262db6db07dbb0bab38e53cbf32ad450a2d1b5497378e6266cd3282396504e8e109ad80e8230a4216a58bb08f394904011234ac00dbf2256f7ed4f8ac13a33f598d45527b31a35d4c1505b3db5d66054436d75ebd5c0a4965a75d5a9b31e06756aabb5a69e7aa873751db78b071ea21bba9d57bcfe3d7078a93309b450fbd50d77602cd9ac6349b34846deb26884eaeeda2d03b0080e1ed5be202aa40c27dee90f17abe2ec44e6b482a947a9f5a2ec7de69825c426ff65cd1968602465ee123199f833c07f581ace0647716297106adc4370f2a175f80ecd3541e3fdf2601bfe78805e678b5b389595e0f731d788ea8bcd37b9082d6acfc5822402c862ba82ea3eeeab71301711ca7901084604f23bee5d7d85f04c8989e2c13b56f83526a1d5f292e36d36260308a7bc0e6f60ce3e241830920532452ad4aaac28b4b57d4139113dfeec896bd5c9ff388ea7919ac8306ab1f16cbdd18b70ba66266d41b3b5e9215e151bc289ba58b26d7c314a89dee5b82ea54459f243f441f7c31333e95dccfab9433b9a79b5daf1c69e9ee0f3710686ffc23986245738f496295f13d242e741ae018655f737751d972452e5fff0b138315a67cad699ec1d4ede93ffca677ba94604681bff8c0dd9f619a50bc4c17b21095c2d065b55db596b85fc882418e7d45a6ecb8f62e79f32c0b52a19a7575e43906ea58f75ec09586cd9a0773ae3461d603df909cf68cc0ba00f8c80fc46306aee38c9aa172a9358a39e33908584622bd55d02e6cfa42c1da0ce858151e0459197c58120d035afb31ee261384e4762a509b2d2342751bc8c776b822e282417c1a197a017a990376dbe85b7fe39d54edf6ed4bfefdc88d1af486287a13a17573608df439c4e729d791b094121696e37705f84467ea0b8dddba294a9311789a8313957865474c58da724b54968a7748094e96575ea2aba9267a031882a44aa4e57127a5502e38f40d1fbb12d3632b5a10612c0bb64179b8a9bdd4fa2dcb35e8648cccfa1b571edbb49d211e229217e994ae0c1c1eb032f93bb40964dce7e4e8eac17bce16bde35d82d56771543079d998186f517903e3ff50ee09fbc8988c1964d06140c380f204e601b652cfdb23aac457dbb1821da4da23a943f554e2702f18efb254ed05833e042cf00b72cfcd84afb802d6faf4e7592f58b44f0507836874b8eb36fd390e1f44bb6edfd62c16e2feaa2df05381e5da4f20b16f989233e8545b7ea25f42bcc061bb516d18339d696e9fdb61a6cb8feef4a65abf61c040a19f7231bb687224f0fd4f710847bd6c482e31511f9dae75593fb039becb302107bc25242e4c365b6e03e6ebe766c6ee47cbaa52be56fab996b67a6ce1a26eaa9a38eb97aead65187995a35d6a9510b33b56ad552632d75ccd454b7562d75d4b9ae6b81d35ae50af32ac7ddcb1732d9c4a942e2db9b184e69f306de9f1958c048ccf7f4b797d2c339282b9249d05301c761365d2b1803c42240de45aa106791f609b4f754a497c95bed88804c62ab9dd6b5493d566acc6e1a17b63568001c0df5f9317ec06a13ff8b365d286b13c3492ff05636bde693bb17ae1d6eba50b0492ceb7e3489d44666dc3efd8ad5ab9b25d737d7f8f187e593eba3265285d02a8ee7c53ef1dd6ad31f1d3c3dbd7734f91087b30262aac017bc99e0ccf4fe66692427eeeb1d824a9739046f6932ea29a2eb33948faa8f602b4d1542cf853a9f94e7de090ffeed80e37ffc0b4887bde44c8419c818ca5df07dca2cf05d6cd15393a2371105cc08d41961da82fa0382d225b949265a937e25d639aa85ee31bda734b40601849e6fc924d24482f097740b594ba28be010572bad4254d5c0310d74a16fd3c0055c2087d75bfe9ab0c78babfe7cd2dc64cb5212f2445e05e17c1fdc3e2f1568bc5af6c73f075c423a70c66347c4ec12977e8c0422c93761d57e0bdce3a44fa270b54dae1062b904c2c200bcbd040402ea80ee49d8a855c8fad85ccfa55a85b88e132707028fe620860655c1ca07275fed18f749b89f0b7c7eec19ff83d126729bc246c64cc268257cb01e12a58870d16baea59208ec039e7ba18d574095d73a0e483033dfef4dab90a6b0a60c85aa17c81fabf50bcf7121b7b93746bd014164e78cc061b58f4bd602d0969b82df435d8307217136c737ab1f436454ec13ddfa09875811b854172e411706a3343a91ca506a6ba18bdefc5877ef0a3f6bf0d1f80ead8c053f3b521d2c69242642d955cb2d3dfe604f9538bbfc97c636d3ffd5152b2df9a3ec832841453a46874182d13858d820ad760a569d2450b1f58ecac9575ba3b3b6452ab8092bf47990aec48e09a5aa49651835419b1a620a5172e157605788921036b1cca5020d66bfbf4591aacbd9988af9d4203180ff1a84469e58f37f0a4d2422852841661e6b65a1a19e4abd5c95c05bfadf53fa8aeaf594d67fb2a51e9a73b697ee78fb90a791c5d183da584f45ce0121a6ea201545c7f7070e1e14a187bf1fc561dfec25a64fbcefe10a30a78c89d04f8de0f089871ab0a750a25da5346c2546c489a67d8d57cdea4787ce33cc53e05fb30381a48929dd2372776d3e4a4f1df14ae17800a310605bf446c89b14c5e0f99bc7767d22f42e39f2c1a5938b916bc36e8c10fb44062c05ad452c44ac754ca471917149c5e2e33ea17906cd197a3eff32357a1c0400b2701ed42722801fa26b3bb0c13928eb4bb58cf74b4a0b23025e95bada488f55f0660199e750f35abcc7967e3a4675978e1110fd44cbaefca081be00a1944196138add8daf76afb8de2c4ef7759f46be08430e3af3e9eac7d08ed1ca248c65a9d2f6fdd5ebf4cd21c30ab08d18bbee3aad5cb2898a3df3a2b2ff3a51d4305674f2078605afc2fba60b78d70d9e6d980c10e302292a081782f41651d693dc13e97fe4231016183d9cc1d84d1fea6fdc4801b29075d8295ac89e074ad94b5b7a753005fa4775315c9264a7d0815129fbc1927035e9c4e1fe29370e1faec226f7defe52fdb1d07a276c180e26be09f793c1e4d311a6a28e23c89faf72a1184e87e83b1b371f255d7a20ff913d1d13c078b55d59d0eda540c9fcd545852a6e508315f48e14bc4cc517164efc235cd23ee69fb08df006a6f794fd08c81d628d4f285d986a22a6eca459ebf0a8e6293a58c0fbadbf943efe7b6548621f2db20f0548876899efbea5e29ac807e611ed72e5f8170f80a80ac8cf3d2e9bb5616a887b4cb67663224bc2d532d3986c75a6273a5d0ec65e8345e89fdaee968cc21ca6f04e7d225e180ca4682869363f5a2f82039ed3828df5b09a82e3dd7060c3464179877c69cfce187d82b5b87ac04ac6de361dc64a5f619cbbe92799149ca251a42ad89a86172e26024ccacf948a83976823f65562cc237d6ccc51493e8c83a9f60ecce53be2dd3bdeea1e4a85efdddce0f30484f4c1772fd82a33a8911ac63de7f8a5310b8a0f96fdf84ed21078493beed8b38251bef3ead477053fbef8e1af3d2d46b6d0f0052723183d0d4721b1a5d76288b67a7cd077a29c052c554d788b8ca00cde60da3c31fc97b5a045b04d16ca2e6ee0b34191c7bcca79c2cbd477077ffe768eb6b2fb0e63682eb1cf8130d9a03d86adfb04ed894d57aa0eb207d3fe7404c86e20762a567aeba8ae8b8a9f78d07dd4e9e7e4384e88e230a85a4488038e67833d4c6de207635e10fdd3ea88f988e2cc84266bf94c85153578169366673aa56879361febca13dcadf36db372e0a711567c9c61d99bae0a39e4190cec2617e2e7e14065e9720a4191b212d15b8da02d0ea4d0cb966c0a0cf9fd11d787e44306c8ef6ad0b62a4f9a1f4992294cdf7e75c998699ad6745e1b9a84e0447374634937fa2b890ec80091998958dbcc2eca7583b0ce0c04017171d32189eb66caddd47f98ad4dfb423071ba74d6b77f3b64d7faad54fb2dda5afe80aeee092e38517cb9a326b5023f417b35a3de27873ff5cb1cd0b52c2dd60d493f1ac68f0f0b4384519891369bad00656aca293574fc178a60d1d182dabb7886aa20f96752b53fa4f69a3362c24f0091d864a8956128cf8b641373631b6008ab0cd648e858cbb390742f296d71b2089d462bc201a769f9f8d0531e9c9f21fafad055d317b671cbcb385914b8f975e8069376ccf233f047f631550007795bed80a866a0e3da34e719d056d499557339e981448a84d8a280d080b6355c09d8808c6a42c89096df446d38785e64013866c8c926ecfe6780bf800ce39605721a28132c29739d6fbc5108c221643a624a3f6740508127d0437ac20797393dff5274699d8320fc513846fb53417bd10b0424178998520fca4f7c7d59914f850e107d9d2324d4f0ec7066ba8e43af8ae63b339bbd0d3ccef8c89e9bd59f4cd7b12758c1dc1725f398540e994c8381ea0576813e084d4e7363b70f753dc9c544719d1d7a0916b4f27d248c89aa1ee828344c423f30df879c9569ee79972f6604085caced40aa23f9acc9189cedc2bb66011a30b602efd032525e75d4cbe5e2e3054c1499b3ec5cadad6c12ac846245a39bf112e9a3aa46e677b5e49aab45ef5c691295c98ef74285560ec1a28aa34cfc4ed0f6944beea6211bccc83118cc6be9d047e5d0bcc548e277942abaf4e5ed28c1079bf99cc13d049c544131f5b64a235c189badadfe3cf16f5e3e9d1ab3ea022c0af8f522500aa1057eacfb341d19700916e159360b4e8f9a1a234b0d529a3f2f5f229090cbaa6599ae7cd03c12f8a379d96b342ce46b3de455a0f9fcaf48be7515da41c63b9776c377129b858c2091ba44909a3775efad42fafadbe597a967c8faf161ac7942abd6cf1dc063abd6b99b39cf226f6363326904cf1d173d53ec866ce3c9dc6cfd416b5bf9043e10eaa3910bec3ce1391126bf60438654764946412541f0ca5c9c0ab9b0de6b38d2a494b0e86eefbecad423c56a65b2b2c4432adbdb8aaf0bfacc662d2717ed0df817cea22d01ea8a2ecf013ebf947cc9a5fcbf3af9ee4cd56e55c0bf7232cd13f3d38a1d096c4d2ee95cb078201fb99c51fc3a34c5648a442af7914c0495204f7f84a79196324354ceb20ef8a89c5c14431a51c2b92fb7c5e5d36e5c5904e6bf6349faf8e6de015251b1cbbfa15e804d2d4ccfdc7f3db71ebcbc372fde9f3a7404c8c85e4e452e1492f67de9ab8ad133518b0106dc9ea55b15dce75cb434521600cd4758b978e00c3fa52bed77c8ee558d02baa88f032ad19a03701becb37ba099fd1e5f0db2b025dd4977d24e58f19286a1b984913166e12266d74418b14ace04e45408d13b6603ba246721e3550b1f88996c1674659b67d77a70678e69d754a3a541412dbb43fcfa811f99e4962dfc348f34c6637c5cbc94d2921642d0512da29a68f45b1021b8763eb21c62e2a8de492b0cfb72d8740abfc1a820419235e70f7387a50893e5e82c955f343a9a6776e2c0c9d5279975debce474fbf3b6e7ec333cdb68f78cb2f82855e1ed03d6691aae6a869b84e5b3c47f4cc1d7852874a9c35003fa26e660c24e26a6be5bd88d06fcaf0cbf24bc9d0aaa2771c5956473e197e8a26825573982d28400485cd749ac13e87d6d7c7c150e16c0a51690f71bad11c37cbf4e744b28f143916521e2c2400d85ef6a3cd71a6680ecbdc5efc0144d4918b14cf562424273152134924bc39994b7b1e14a7eec91176233cba523b1854d64a86bcd3e8cd522d132b630a031212400e62b21e69f022b71b91cc50d1bc411c550fc5f6b6c877c246afffcdcb9f8a57f785186b2ef6f8451ff812cf40e1d682e389794e1f08f09aba9d65471c6e9881c42f4295cb030c58257865b5a61eec91f535831d11f53b2898aa3d0e58dea0fb8c1bc91c49c847020ba4e693a866ad28b2ecc66ad42370e37e0e3a2022916c9c948c54a60b9fbbf52956e35be0aabb155d2b4481763cc871dc79195818924fdcd0edfe5927a65968d327d8057d13c8ab3733309361e943a3dd7d21fb061655163886126a9c39a26e69cf231be6d5c713d51b5a56e191e65bcc5d0a8719c470b6b8298a4e4bd540c0d683811b1fd9a2a71b39509ff6ff14e0c89786e699c0011230c72e7aaee2dd550456b5d5db276ab391199c6b6c5eabf2fa5f0a8597290582ffc0cfb2a786dd001aba2bc200f2942f0dfbe7d0b371071f1f1afad2233bdae5c1023ddc5b59e4dc50cfb071b54ccdcdc27f7a2c557bfc67d82a3a128bcc0918fa265007ebaa17a13fdaeb903ed2c5ec23e164084da3a7ee30b7302c71b689c1f08ec622007d1ad486873648340910d8ddd83becd6f17aec1d411675ab60aaaa3b2ace0e216d088a42bc91af4ca92a419ceef6aa1b58e2fd4ef18c7c63b8c7c44bbdb60e02d0e85a58ce3686147fccb71560b69f4f875662eba1f992e753298c54f3267695e17e748c8e085dc6d4da6efe5b96e79251e22718af46b86c62533acfbdb7c7f1e084736f1a4e1e096aec034e741b612a51c971cbf8b251834daae753bd6d6e6c4f6015ec00b1ce4cdf6d7fb38160fcd53dfbcf8dd23834f4f6e95b5f84d4f02c9aa1ac6763bde1ffee2044d48182f163202f52de1070bc1154ab882b440dd2d8f4bd0e2eeecd029b185e0b574626563ae65a48b79814b9b1e3a80ba54ec70f7964dbe6a68f5b675735d622caace0dbb5e996b43478ddd36924f20516dc79f1b456b7a0e90e7ca906cae562fc551db637b8a16cfa7826d1548e2148281eaa3f2b97f1d423e1c585dfc8b143f1f9f9d6c2b3a60d37c986f42149946485114c2684435bdaaf1daa416a5002229c4b84f9a0aba95d44d0bbdd9dfd919ddf5093247a38b6c340d6ae5cb03080cf80c003a3e8a88ecb8c5c3262868f51043e4961098aba130298c5d38bf6aea977177fe3bc4879f171bb0a0205163577020a2f177921e912f5bb705227ca7796d2013695489514024e8e3a17385944e5552dad58cf41a87430f9d3e4bab595272610afaa9e53ecc8f52c13a64c77e66d29def0c15493bf269b7443554f6834479573e63df576ab22e1da7b50e14142151e05d083e357bdf808355d0fd2cca1fb69ab3b33ae34272f337c430134e305a0172e755dd08c0068ee629cb65ec1531312ce8ae7e66f6a8b0bd4ebd86f4c65ac8e2638eedb1d68a16835b572c77940a2378db4c13f667f079cc513c285ef4205d129e6f252afc705831a4851f2739265894a732cf7f4099c91a1e8bcdbe05a69a3c47d6251e6da7857ab71c625091f108f13f2cbdea78395358773084700c0b609267ef38d655cbb9c461c85b6ca4c4091b71b60ce8681493c0c059908ab49002c68cc138d4701c7eb1ee812e45b11499653cb08b85b5eb1fe26f0575114eb18a26c4feb8068053a184af94bee83adcc1e352f14f8d246435dc86e8448f6ec20205a7bfb1a9930f7fee86c8dddf09bb0ed911411cdb9cf6d6999dd4a2bf34420f60125315203cc8d886dc5bf591e544c7a40b59fe4e7f92dcf98cd35925a2c1599d8b1eb891f64f5736868d9cd9996ca4de46959231a5240c1c502bce94ef9983bd7f33efc2305e129e9a40dcbafc0b44f210abecf9f224688df093c2777a80b6c2e8d442affcb85f6df018648ba7e68d1ba662104d10895b9a5257d28f80598e2e6e2ad90f06915fa1937f8d77954655b0b7a213a259f061ee6a0c1b890d76d17c1df4a21129f435e83e08ec0c7d923f0d4babb6da391161f41c689882ae8abda020442e98cbaa64767afdec92c6b0d133be8729c4cb1285604d1dd65d25dc20e3ae0b331e00e11f0036cbd776c19aebe3db783d8abb06e3967fda441e5dd59e02557279b9f519ae30310d5161215c507392bb599e68b847ed19b67f62918b49f7b271d12c45cdb0302e80064709c49684e412756f86cdafeb48cc206fd8930720682d96671fd76e14f202294c5685a26a5832db7c93ee709407a089027541d5f545c2fe594cd4901ec66fc1f5597a2373c971d15637facc7d21b249c687be922ea5589ce2312581d41330243ea651e3314b1c269375858dce37de6db3c256a05371ba6044d12179f726d183973a1177deb1379d1a1131b53755d37bdf95c801d2c8e260807eddaae79832ac043335ad2025b0303a5fb3ae0a7fc99d1f00f2e7292dbb7d81f18c754919edf9d93a92f11f80825183d06186dc423729c2a8c9d029bfa8c0b2c3d4579ecc0eac43f207a34338aed0a60dc933aaabb172777f046ca7ac306be940106166f083c96b76f5e3b6711098f7378f1ba19217f90d90fba10031b6a9eb69ab12b98fc2a711dd0985328dbc5a9b813c688f128a365e8d661a7c7e8bd76503f21a606cb3355ccea6dd31ee78f7f763564968c50e4f52b4c094a58dceb5e66878e75e4fefac98fcabe191b4dadef6b55d966045b4bd90219ba83bf5d0a21a555a02ccb0ec77221152da1b8d72f5946ad74f8188cb49733910ad2692c24a67d1535d9926c18ce2c1b0ac03655f908123a64c309b9a8cd1539442772da3eb5ec54c6818f4b82a2028c19ffa5177aeb7bd9825369ba985d80b0e5674d062d32c25e7ea795cd298924d92c69bba40a4ef995df1541f1f9f33471e696fb14c82bf890accecb59c9962dfc83874e37b360cbf0f63aaa9db693bb3d02132c7180a45964b7f2600e55e649257c0d1e6ccc58d647840b6cd9ac8728faee957e756c35ab17b1caedef86fc0a096bf662d1d6317c0986fbbd120eeadff740a04a442ec795c29c4e3af9a713d4fe21d69b017eb9efc6cfec15b07f5b213163a5968087205fd3bd78d4fda38441aca9cf0d404ca8118f1fd5836a8185f2e1d2290c3635729093748b9f8678a27211c187c6f07a738840b6b781a97d35f98e08eb6c639c90d4b6cb614b1675e8cc07522a4235f26eefca5a26ad58f1c9b3320fa9fde3d738912244160f6eaab2735e8d80cbe5e831f4202eac4dca05a78327e0c2e9476d22e79525b9f4323f2455ff5c7a9fd44e917257397a168e0a1f125a1bf954daab5b2da8b5eeb2ddd1026f073d57874942766df53c565d4869f3bfec9a244d86346289d84f85cf57f28e46697fd39c765a36d197055b8100ba965aaaa2746dc1863feddaac34596e5aa150bcc66f3e1668daf689e21d9c5650f17917a07631fda4de5358966ed90168c4499e8ae8289b52407c4a4b0188381b9ac49b94d8ca0892689e6bc701cc7001de8d70d56c6d083bd81c681e1d4644072790abf79e39e14196bda893569f22bd5fdbac9fdfa5d0b7b9dbce91f5bac4f69d78466dc9261c94e427d1991864a0b5a5105bab1087ec50356c3dcc54a5af6d97e41f159bd468219f9e813e6c407f5ddc543db6792f2c8391d8dcffc6f2324621273f759d8b5e80420eb18f8380b497fd78f71e0fc8b93162ffc892d8ab404818dd2b20f1188a998c3e2eff2d8afd500313de03d90a08fa3fc4ff26f5c6fd1e5c4165ff32e56ba0ec2ac4740ea78ee69194303d2948543ce5182ed442ce9c7c5bf39c90313c326f719c117c2a9f394c2ac0aa20b08643321f599489b99f3ec7b9c862bcf2c99ab569b47fd4e2b2e730cdefd83d66d86a866407da707aa40e6bcd6d8862c59d9caf5053769cd6eb7ffa27d8c07dcb916ba871ea877f9d615d987dba7ba5bd17e7fbe7062b08a430872d2d378c8c18228aec1ef4f6708e0538a82dd2a2cbe6bc90a500412e2d6dedb8a825a26834355c6dd6e72ce7602b54412f8c16d3a2ad197bde9790300039e7084afc78653733b5c838b844ea2193fb79b1ebbcef9b5c9d879d3fd9b245385c4a5c20c15602883c04adcb06f911b2478b19e0dd3089190d61131dafdc1707cb00d49740d427a62dc00f9ef9bd6ef37af3e6077be80e2e8f2ba40e087a9d7cc6c4eacbac22374d58c9e7c081944b65f9813f6d30a4bc90305e676c0b8347a2d2f3ae1557ad13afac18c5c00572cce9e070cabeb5907291a37882209f701337f2992006991550bdc730e65641893bb29d606136cf431727a8f13abc84fed9c0d77331fb203fdb12cb437290c24c830cc28116662aeec98910d53e23f6213dc813a4470d96296a56a2a94e5ee5cdb916d133307375ff1a3ea28e3b243bd518e252b5fbbc76ed65cbeaade086ccfc2902f298e7d77ef6dcbd50c2cd20558481b16ff879e0e37fd896f7df6d0e132b433478635db323f166ac224e432a6846a785daa59cd8f5c6b42533c71338267ae1a107a6449e7e28984e132de773b2d569da5fd5b1158e331b34c8a5579d18c8bed0d664ec5108a002ff8fcba71301663fca6d81b98c4d015e3ba17c9cd80b54e6cfccb51286c581a4d86ad9c3651b532a5a3abd2e20a7a4553f0cc0a21ea866fa76363bc02e630dc06be8a8ca3e5886445f7933d2769790bac88db6a10983ce096314e48fb0204cde030adabcabccc9961637c93942c1aa62d4f4c1d27a6707e2f11dd88481aeaac29d055ed8b57ae9abba08bbc269b703a0b79910b085ae6bf6283d079bd013e2b283b4207d8b5bffb9118c2c93ff6c350b6e9e111b49cb7350cb07bd2fef4ad2c449c04a5734c73d3c370368c2b4ae0e89680eded5191877670333063260a040d2f2af3444b8082e2131343eba108f590164c19b7de9c11d4992419e6afb785851f49fa424f23a8ca98285b27bef961e2a8ccace4be7dc00ef279ce8ac8db28db8e889b9ef3d67131754f437b829b8068dda1be47efbf48ae0d3a2886fbd70af764a70d0f2923a0f1ae213b84ad29bc912f8a4639bb854e525e43550d3f85e7a112d547ed5339821d441f3573b1e56ce8777f27a37d0865cea01466cdcc582302dfcc280ec306b5d69970a8ed8cf46e44c1fc19caeebd0d070159237f8f53d328a851722f1d45b65900bea1b4e34e0b4182c1bc87634bbd8846d87ec9ff95183b5a0d49bc6c814e51cfc26b5257589bf422d9988362b88438c1e07fbeff3ea98d06ab04e1a776c4142668b106272e57d39cded24872335936d04cb708a10ef4b18a8b48c6c66c81309688e3d4345f6db685c7c17a127299e56c1ed1962e950c74569d73dceb47ac9d5c7ae18a3dc68429f7aa95a0333bd256ebe51da845a7ff444218f18ec9c5f7c8205b021ac1dfe67679d360490dd35a69634b6ae608ccd2cbe4ebe0a1d7c6787f62c2d3879eac59c8178a226fbac919232789746fc145c9a7bc3a65f33901c1620361afcbfd2a9c0b94266290fd9ad65387d07c53f55a611f9bfda39d7bf6ee57d7bfe8ff8d38a5063910c1bb8278b06f1d94f38d6f655fc38c9f190afd057c70a1fb1fb75730e61e64f2f98b01f4bd2bcfcfbd0644d40d8d92d454e0fcf3b928f5549bdf61ebdea0bcde831cd46943987fa4e0baba79f9bf57096d6d60f4410ee2403e228d3e45a4f02915e587a4e063f2a77cb873c1d6ad6a883ad05bb0d14b93af05677a8a57b4d2d27f84b98bceead841325233ebd7f19b21107141c403be7dc595d84e3e23dd7131d20e951c9072b2bdedb07a94850ce6f6bf6f4f7249a99ba9cef6d14f66c1b54666a028ebe57e8278309c9cdb8beba43ce16bac3f989413af5c899d1ccf434c40cfcc87df120a9367bf62dc9779dc82616114612180cbeecff2519d12371f19c24c21b0b3e6e74ef331e55b486f655f641bcf9b993951221cdbab01af9e95355252bb78dcd2b5d9e910add88a9e1cb183b3947230e7de8af4fabd39c97aa9e64c963b582129cd3c34ae04d07536581f567f3031a9f0189c039f604a6eaae398032ec2cad6bbbf97c9d77748ba8f6cc0e672c60d5492e44ac7e518c592d114bd51a240ef901c9ce595824ad0c2f0103a607078dc1a113e9b77c3ab9ad6dc4682344322cecf4e79b1e4642e4f4126247748a0a340f56e540d4a864a118a279002e3a1c30d7cfac23178a0cf4a23cdd748b7c7e4ac4e23bfc75c58fdb0b665847686638a7df4be1103d25423770817d387ba01aeb35123c606110df988fd09b203216ca430073d054a946bc9aeac1edf448d39b557ade501aa313d7bee8d21fb54add20f96494174b06bbb2048bb9e97797c8e2135ce89a713823423d500b9e4dc796bccd725a7a387c5c19ee585c8c9e1324f85e2c983041c7613a1d955c5e0b054dce21267c8d34078b2740ddc2d9100c92f2cb0479f42cc7ce6c12978ef63556ba7997f3e371abeb7f6b1e0d8fb53a5eae758baa250e4241fdf1b6365f7e1471e7a5c5fe70259152f7a209edacf58ce840ce7e7dcd09b36e6c4cd7cbc84c1980f5789643096b50c043f24a4df7fcd161ad0ce1714bc2240f066c0ec0ef556de623bb9b136615851c9ba33499c6efae755674d74e78b8d59715e74551ce45925db73d38916b116b3926a78a2e121fb44cdc5bd8423823c413b99e25bffbde78d9d06b5d19c9aa4c576b8bbdf73b41fb4c03c7467391b1cce7f014cc73dc4684f796176cb3f3160eaaa8c1917f42af057a42cd0e8831807b84e594d6745c226add56976d3b2bfdfdea5efbbccb8e266376b76be430fafde4d69844eceae99d7f4c97272f9e59061d23e7f67374fadfab38f847b76ecb23f335c4f8b67b728460798f5e1f9483d00e2c42410ca0c9bb9cbcac6262a1a84a8bdd123ab5c581d5f4ed509b57bec6fb8b6b8add5cd631cd016ba76dbd27b7fb4e5d2b6a5caeb213207f0fd5e6382fe5dfbb0ebefb39b195c9d76d38f9c47bfea43302e28b8d459c81ce05cff306d97938653671c506a90c19f2ab440965a8ff39ca30350f2a4a376d9477d1f3e662a9e14ec918d17e373286466a6e23a613ac5bce4ac62e4349d80356733b1e60ed5ffbab6e75172cd190a02029834a7e6b9e64e72b2928756985b73e26023e92da74c4e04e90a2ac774db3d22079f586b89ce9c8be07d3463a2754962c0d0f733111ac3ab1411cf0b30cb55a2637736d4a5a9a44b32139a358e00286ec22a69d5ec06843e9cb332903b20b9b52d8335548e5e0dd8d3f64674534db0ad6b466cc8788516cc631a4ae0245b3405662e63602e0b4bb24f7e8217e6c0d924f99f2fba905e78e128b7e3a3cb8993f93c411fdce0c765202d5a56cbc7ef1ea3b9e4b724c537f95ffcacbf44cf8926c0b18aed805d009b922e1dc7db956481a245a44fbbcd9cd3a678134dfafe677e3dde024ef90c34ae863dfc4ec56ceb560be330a6d068edb2f62fd926ea78d282774fc9ab190442c7b8cbbebf7eb85c39b68705c0261ee4b808620327ebfa854fea9537e1edfc989db2e4e8083610241083ea0ca0969e2843750c67ea9124b2ee9061a833fb9da81360685de870914a9e645f4bc4dc2c41303624b486d87ea24de4440efd4a9435a0f30434391b03cf20044826948a78bb25024560d25b00b98c0761d90a6f1046eefa03308e08040032accc778336248b2e752f440a8d291584f660e62b5aebcaf258db2e722663102c80998b167094ecb7ce0aed3e583a33752dd66a6d870262866a6a95fc99ecbda3268d2d11bf3ba255f9c94879e7d6a1a9f126f5192460802003af9d3a41b4778177e4052e8a6627debfc0e7a7d99108c1c0ef8481f5b866a7ce26e052085f4195dbb1ac9e425c31bb2e9983f9a6df8967844a44bcc8b3d5eac4e03ab89ab49638c97d103a6dc076eaefe0c9e8a49283676c83084f6172247b1755a6a43004585e2aaf627eb7700c2ec2cbbb63b0e4b366888389e391e398cba2106bd23071cc56d0835bed3f0ce0747d26584f9c1a652dedf594aa9b0e650a3d8638e186fd65ff16c534d539798e2d14a1ff75e3a0f20190401918c4c09e5df0d48a630c0d859768d26d4aaaaaf6668ca728295d980faf58da76cf27b30203a74df8cff3f1900a99c0b0ce516591d247a2639112f0c0557e0a1c455d95140aeda31c93bc79de9692c1ca409e8d931965701e56ae3c5416a9b224f57c39bcdd3fae71f6c7590c747448d61897557d2dc634acc249d95492f5292a5b02c57d83a807b4a46ca670faca8e9b29a4da116f4239245adaacc429a6541883b45d28f8578232933e8d885acf49b7b3cf228301594b998c3d7b7de19ddc5d0c6dcae961bc86ba75eae2d135fd53eeab36268644b60d293134a3420fed541bb63ac414bc24720848fb692fa80c5fff3f4a511959ecff92762a8f2582355bc82871f1211de99114bec99262cc2a7bfba298c2fa0a70291c2bb6f74bcec2c54fd55ffea77695c10177d7e3a4c2f8efa2707971cc67d8bd89c33c337c3633d9cdcecc957f705f4a27e8ccb3b0c824840280f4cea1d51a6f4e88c85be4b9e6452611ceeb930ef476f1bc5ef6dfe6b5a4c914f3f965ebd385d9a82791bc559c3a533f191fe7588c34b635554f57c814c3a1582622539bf82b3d055147caadceb6fe1fa1002a445944196fc72c08c7901803d72f91675e4720e631207767687cc114be9b2c1a9bf59a18afdc0971f3c659126f4bb31dda0f5a01494454bf912c352b24f2586e26ce767cd2019945edb161cf0e0f98206e258d3021b0fb7a8d3a2d1acacee835735efb57084d1f1fc8757f4b369dda12fe9ba900c10a08ae48c10a08a008018509a4882129244061022c244831022812582182c4e2d4d90db1a749c42511d3cee5e9206c074783e0a5523a9f0f62bbdd50386a6c7fddfc0955e9534a7461ce924f1377e3809bf2edb7fe13ca5953413e238ff3ae4eccdc5ddf9485184ccd7fed4c13e375ef423e259444f7bf8b2829c0d343b09dd863bc3f55ed70d2b2facf055cb682867c40afa178d0a06c886a3c0c2d6b2134b8f0fcfd7b420825c795adc8a4692e41fbdf62155a2e6ff95c7be14f14fc48c08c0bc50920226555c135b53454e5c32eafb1b80b0744db47bc0ea63c4bb3a3b907fbe6331bcc283ed12a5bcfd624a3ac1cd6dbd4ad686570febafb83e7956f40160f6dea1c1e67923c3f4f8c3d764d269a466295e2e57ab1bbbb06c05bf609d18a07c15567efa7a15daeb15d8b8af1f8d082287489a8e90486f4875be19a6574ca696a4714a95659ea847bfdf5a8cde4349c4d33d031a07c94968cc66d958992c3f298189ed6c05beb65622c2f2ec840c26b0931d20f545aa2ef3214c6de1456e3a58f8beb712358090834284e111731af09aaccb367008629eae4d1d8da697600884772704f72c436f05eda10759e58f1243e09d4e22c6b22d3ab3de2eb79a97a530aa4e489883bf580229effc6409987120c0f8887fb97b7b1558187a6d40204375e9623c6ff34b360d7cf92993c6ace11677a42b0959599bdac811c30b653e9802724bcd99204f40a2fbd7320ebd8d981a74ab5b551a3d65965fac80e72d543988f213d398e0ec3f7e11728ec806cbd806bca068cb2eb988f6d1508b5852d2921d2eadc4db6d310b75fc1a19779d6d9f444c79f0c8dba696b0a39baec5b26eb12e2209c371174864cf3a78b594caa93128081e94e578da6e52c41bbba03e37a8aa060009076bac55eb5b4951f2ff636f8b769f263fc3b75840ce1934ecf70500e108acd0225836f04768cbe24e974c19fa7d33590590e4e17b85816c8e6f892e95ad1e987a0e2d8198401d4120de8781b1d100636000e942667621ab26ca271290cdba7f71c743d04d9f44f291f24911230986164d3b929ba76d1f73bb2bf78687c8a9211091b5a3d2592b8457aa109b0d5749f9990041be0933021b0df2ee98ce9ce53b17aa05c8f42062fdccae108ea638132625a4d67f9e448a29672a4908f22400c4dfed8170a848c7372417ae1bc335417bdc701fecb348af464e70c38a190bf43d77c05f3d10473021a0a9418b192db8d879b4fb87b35769b9098184084a296ad7c82cbf6ff547a5db6b62ca47c6e9f4207dc2a05cbbf3f2d2c80f06aec4f4dd036b530af44a27be5b60f1d95587f37aad1f38945f5b61fbaaad87f6e54a3eb1b8bea561fba2a31d831dd123641ff37b62a370ae815c30711c721daff272673d0ab4be724453ba71fa404daf9dace0f931a1cf9045ad877c8531040cd381dac76f33cd5b18fccb3b6ac35244df293f11ee7f9ba9a6029098b41e2e8b965b35b2ac396724726d44892dd64e5d2162319007ff1ecc1af1e38b326ff81ffd876b3a98f0188f8e99166fccafb2405200c4c0c210c5d97f1fbe44f3aff522cb338e7d5529bf9a831d32a82a0f56a1c85696fc5da9f6c87f0645d2ba967315ed9a1f946d90e69891161f9fdc48842496be1fb14cb288e11fd678d624431220c7e063fb36244312231624c628070550d8e72558c083f96f45b7d12df4fa2f5e87229939893147cb80ae5aa18d10ab3babb8336492187498a2cba5bf02cdd31f8184b2dc280a28c29a208832fe7c4aba954d582a0f55ef83ebd57c24c1c80b5cff75a96b9deabb6be364071fdc4a44a5ff931fefcca8f67b5b0d7e8e5c758beee674c65b9d2d727c9ffdeab76c8fbb08529fefa3120795f7369c1f23d4e5d9be5f759ef87044431048a2c50882905297dc0061c1a98e18931cd796e878aa0e152adf737f46967f9411f61250bd01c29cda1812f5425149cd8c3e4c41add754ec36881852c2bd710110d2c23a22bee4529897777277434f40a8a61d56a0509bd808c565b8a8e5e5c56429d0f59d1c416f33f2a638e5d56a94ac5441335a5154a28dc4965d1bf959a8beb3e26fac3b4d8a218bd9fe436cfcf5e4ab39486fd69ddd357b59d57eaf573a6d7ffca4a5d3afea4fb2d0f2538fffb8b711197576e54bd6ca1ddfdb58909236ced2ea9d05d4aa1bb839450e82e05a0bb7442c984384c1798a3913c097bbd383cdf3afe3e296122063fb3c0cfac229a29f2e65ad04a9c5f8f96582048b71881072cc1e58423598ab49506e8411384c687013994fef8c18d369a7335e7cf5fb16806f53ce9ba6e86e595ce515de755ae389b27381e0dc75c43bdeb5c7e7646fbeab286fe30b7797eea419a8b6a5288c34f63a008d29a0463d6a6866be2393a1d92ccddb5d99356ae88c2a2f8c9d4db6c41ef3e1c5d9dc76cc6958a5fe8393af37ef85856e1bcb3a989d9bf81e2ee068a9dce796d0c536ef462164b98cdf848353efe2c4bd0e7f7b3fbb3fc329b1df530ff2b3f4bd97f3f1dcb5928859c7b9cedd07f30598f2878bfc80ecd7a34d6ec2d8a7ddfb243afd7eb3586dfb4607ea954b85a8a55aa218ca0311551c655fe0310455020862290e84e92c43ff9d8a591b8aae59493542dbf72ed53f28a32d7fe25af287f262fcd444417a1bc34b759fa570adecf3fe783547e3f5816e17f2bf5683492b9250395567e5cf891a4ebf1fdf9749cb21ee5faadd5106874f71059ba63960811224570971f2244881479cdafe2f802bd1a7e7d9f677d2c040bdc08bb1f7aae925e99847f5cdf277fc629df4856e9ac5f34de1765f83cffafc4ce8d557a8c3aea653263c74b32bd96ded025161dd3f9f5b14bcff27e97a32376feb38ccad2a35f28023947b31509897e4f732e661dfc59f6f94f735cdffbeb195bd0b174d475eca0bdb13affeff7ad9cc3572639cd5624ff2cd6c3764673fcb3eca818cbb99815fae1e236bb7336df2733f518751ccf58aca146de4cf183347bccfe2c3b77a90cdda6c39dcf47d53ce1c9f19c47921f6c7c2ccee1f031c64f65955a59b5e37dd106f78c3fcb8feb9274734a49c3553ee8e3f749118b9e6456d1c6dc0e1dfd153f2aadccfaadf9f7f347ff83c97af41f8ce6fb499595ed10d0984330e8da9957e51b615965ca2a76683e8c5e0fc3c416cd66c1489ecd5265f4ef27d956b643f985b21dc2ff566615ad956c87f05165815eed35eb633056c0c0e0285765fc1300afcfb602e1c555423f5c661ee8d59c8c219d61270de0e9b6d51570c41c5e2de504f46a4e7460373a39de74d2f339f2779e6742c989f5e8dceca4523f3cc0f44368fac1c90f35dd75ee394ef2ae4b5522e76c3e8bded7de9930a9710ce2bff2f37eba83c922064a143187cf83b2fa944fa25926d1fbf385e5f7c91796332f4bd8ebbd6b63b34c694e18c06a578d73ca113fadd91bfe15c3d7f751a45c1fccded7f077be57455bab7c8d357b431f884a301edce081890494a9d25d001f0c90809ad20b4a30e8520a4a5c94ba40c00e4152c080ee66b509012274d7710f01f470002e4d6408d00e70e0cb6511bcc10e33310c9043dba08424890a44e9ee239c74770f0a50c0f5c95448007fbf56ea5272fc05af52ec374e2589d7ff784c3a1cd1a5559756257497363da8d8408a0db050812a1468d2dd61bac17451b71601a060ca8184ff60300a039af2efce1311079c93f452a970887158c184c38643dbb8ca51fe8afdad71d1ec4f887b44efb5601aae300da5acd26b6759945f08d21b4a42dd30c8da34cee4df8cafa8f1d11d43fbd46ce39fb6be9ede4c33d3411a34d9f872f7338d597bc31776e56fa9a64a955481561531c40b6495fac043c986bff3a3332fc994f5683c5c107ec6489efdb0c76236b4aeca5a7d4864879ecb8fffcaf191fcda9857c518a6339773f971aed6739ca9a388102142a44895fed7c6be8a8f8fe463be74facfb2749bfd33bd345f8560c25e384ce505dae28aee29babbc80e6dba47e0cbe577825f36df3aa69eb1fc8814a9d6e230b47756eb19cbff3c4beaa3fcee2711795d7549853e6c6042824b6861430bdd611d9f28630fd4dd2fe0e00204c29e571f60871c70b04074cb8cc194190e78f0f1507a2fdad7124fc94acddd7dc7901b1c0e27e57dd2f74910bb0092d15dc369139843f77d4c6d777f69d3a786094cf5143faff657ce2af1e566de958f87d0be7670a443f5824251f1a1e236b5496b72ecb3a8547926a3469728c73e8bbb7bf58283d7267dbd6cfe0bc993373b0dc408a3832a78a0e213fbef353fdb4102a84cf1265eff7bbde667856edc2748094c544cffb4d5c71c824f7368400f3437ccc67478f4c96fdda229efab542a95bc413c3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3b3a3a3a3a3a3a3a3a3a3a3939393939393939393938383838383838383838373937393739373937393739373937393739373951a2448912254a942851a2f4f4f4f4f4f4f4f4f4f4408102050a142850a04081f2e4c993274f9e3c79f2e4090f0f0f0f0f0f0f0f0fcfcecececececececece8e8e8e8e8e8e8e8e8e8e4e4e4e4e4e4e4e4e4e4e0e0e0e0e0e0e0e0ececdcdcdcdcdcdcdcdcd0d4e942851a2448912254a94283d3d3d3d3d3d3d3d3d3d50a0408102050a142850a03c79f2e4c993274f9e3c79c2c3c3c3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3b3a3a3a3a3a3a3a3a3a3a393939393939393939393838383838383838383738313a507ca139e1d9d1c1ca14fe66b4190d5dda8ee1ada440032f2be138fdeaf2f1bbf4a048a20ad548866bfaf528fe667affcfc0793b37e580bca6d64219a2c302ab24378ac52c8dad1dd7574371d41d5d223a34c419ae5fe28b3d8a1ee9ea3bbe5e86e32124f2f28e8c7fa945540f71f7c411157ebe11c8af2a3324956d1ffca87e55945c714bf688357eaaefa37d81bcb22ccad8c30c7f3b333afd6effc2cff7afe307f3af14fc7f42b4d86d8eb3f0c8f55826eadebfe283fffe48a0622ba3b8eee564177c3d1261ac237de66afd47f96afad62a531cfb6fe549aad48f5c886663f774ec67d8de3ff983f68b18f32749b7dccf4a33059597fc52cc62af5efa3b4f959ce2c4a98cff2a530975ea56339036dc6a2c7519f2f1f263f6e62298ea963fc77ce2aa3f893a20defd95614b8d1dd6d7cb5430fda56779fa0bbd9e8ee354ce0fa124403abf5020a0222fa190a7a1911191905bd82b21c011109bdb8ac8454aafa48d6f59f38ad508f0ff89995f1cb2890cc7e93f595ca6ad08f16216c6ababbe5f87dc9892326f0e546d80b63197a3296a2628a5c65a520bd1ea6d341b13efe4993ec0d1dc49286ed1559a5f95ad8ca4539abbd1fa14a38635a0baa4077237184acd269bd077aa34f6669a5bb8d28a25be2e9fdad32da9aff6587e7c9ce1993f5e1e81a1f8bcde1783d952a4767b256935a2d7f71b61fcef66bed3c115b435e6128552a7945f9c222ae8fabf55e21f6e807138b54aa28383c3c39171bc1d0ab237b9f86b14a5d5dd1f32ccdeb8a2651a09e1c9ca3f9356a650812457874ab54af10cb300391eeb04d4540f87299c27a72705ef87ef8fdac270747246283e8a7bb251081eeefde20cf7edf8aa885a11eda0126a13c846ce396ac528ca9507743000c8b872c7c64b9d29f05087cb95996a193318701537f52cf5834729ceb83b2c73ecb32f4ef5ba954dd2ddc202824e980880e80e86ea23661b1028b0e8b8b2f393a2ea547ecc2b279c29373654890d7153157c474d76e1660ba72d3b9521bbee6d768cca3f9c159a6573ab962a4bb47987f087ece32062762e88901855c1fff2d1233b63b492a95cf0ccb2b323ccaef67a5d9f959ebbdc43b9b55be40b1ce2b69a15893604d82393a62d007ad230225a05b30fb559941a0c765beac9fa557edcf64a9542bc0daf4b3c30b3f2574370da175e5fa4941f872b9f6af0ff1d39b49286a575d3aa3390d926d0431bacbd6de6efac3ced83cf0d94698f259cc744bc5e16e0028a3db5595662b169afdbe9f9677ad159a9cb940f9aef97fe7fcd94408da4200c11602c8727d9fb43070c47ead3f282bf749dad73c56b95f24da9113ede8363b379f4ad0a318fcccc28f244121e7baae26859cbb7496b1a380584e6739e36ef4542a57d7728e3a873d1aa3de87d8abd31e1fd529abd8fb34542a24abcc7af41408dba17ac52af313ed96249ae59368966c87bccaca7608c993b0ef933fdb9127ddfde2674309aa541b0864ba7b7c24d567f9b5181b08f25a2745415b90309b911a74588f32f5e817d68296f466a427685259f47dd26709ad8b02db0722e87ed0b65e325ac35f9772e33e2e656e1f5ce96efcc190de8a8cd1f767732b62ea16e75fafd4a3329b6523a20668af15fc65ba558eb10cbb1b490bbd0db1621b72b721ae6d4808db1023aff768d27d8c2f87f126c48c10383621676c42aa6857bd6a125f54a6f3efcf2349d7260409e3f9ca9582f58ab04fa29f7c85ff482f9a9ce5184dce72486718949f5ff9f195ff799f9c334fb4b952f0951f6f41bc70104488090f26da84879836e101469bf040a34d789869d30b82b4e90545daf40215ba7b84108c14314d61469ba620d3a629f668131545da44850a6da282499ba850b5890aa0365181a54d547419e2c1c9c81e3b7ddae3a74f7b1cf5698f2e7dda43d6dd48908c00c2b6248fde9604d25bcd91de6a52e8ad86a7b71a2cbdd574bdd580201cd9909051a24d6436d026325cb4894c1a6d22a382369129d3263279b4890c1f6d92e38336c9e16d9223a74d72fcb4490e579be4f0bafbc807206c320ce94d86117a938185de64c0e94d8629bdc9d0ea4d86ae37196e6f3228a037198a28128210103a2872ba83883eddb1449fee80a24f775cd1a73bc6e8d31d29e8d31d66fa74471e7dbac38f3e9909d2273347fa6426007d3293a44f6694f4c9cc4d9fcca8fa6426863e99e16242228484378adaf4c60c6d7ae3d5a637be36bd91002444402812421121dd46b6235f6f476ee8ed48027a3b1281de8e54a0b7234df476444c6f473cd0db9110f4766404bd1d71a3b72374f476c406bd2109d21b9223bd2109a1372426f486c47b4382ea0d094f08a7cd84c4c807438ca8a14d46886d326287361901449b8c10a24d4628d126239c68931162badb4808276c518cf416a584dea22ce92d8aaab7285b7a8b6243779f4c38726488112444b60f361308221441b26d9e18bd796cf4e6d1d19b77a6b709426f3300bd4d25bd4d9ddee6aab73980de2697dea6acb7697b9b0ae86d42a0b799446f338adee616bdcd347a9b6ef436e7e86dd2a0b7e7a0b73fd2db9bd0db33e9eda3f4f6acde5ef6f6b8b777406fbf446f0f456fdf81defe8bdefe8cdebe8ddede05bdfd99de62427a8b997a8badd05b0cd55b8c4a777750c48891234540e8e0f4810d6d0293d426300868131820da042689368181a24d603ad0263060b4094c09da04864c9bc0b8a04d606ad0a630346d0a83a44d615268531816da1446d5a63059da14c6d5a630af29fdba010add2640ce0052c7f7498f0c40a2e8ffd8cbe6172061ba7bbefe6380e4d00d4877e5061014ba1b9020af337a74f71938bafbbd6aeb98334a74371e1fe9b30f7d1fe95adacb74e673357e9de9697cc64d6786f4f885a1fc038feec67fa8d159befe18f387137f84e9ee3fc2fec34be3a7cd81923f44e86e0a66fc343fce74f77c65fb61931f54f831816e6ecc21188b89307c3f3f68ab4a0c2bf529aba4310234ce1041c98c253e7081a90f115099e293825850a1e257f46a363548f6852967833d079b97a3708d921c1c856bc6477a8db0d78be3e04baedf9a577a9802adac702ad5b79ceb3a8f59bc72eeabbdef02b23f5ca8ec6795222dffe8d9d6efa319fb5f79e47f9f86258de6454246579c0b9d93550a5aaf529f9e633a3f49629f554afb4d98f55ba9cc30eb58d264c67626afc7acbb748b3f994709a26495a22ea6f763d2f3e39fd19947ef833f0bef2cdf4b41978e29fdfca5ebba3c685b1d9ed7d22acb39ec7856eb75e59cdb5c2dbdbfc5b9a73763eab3de0fc7af267de9ba2efe6edd568f519a7c8f594f7db9be688347f363779573a157eb3dc6ef7d1ffd120444f4a352bdbeef695dbe743fada7eaba7c1f457d3265658e3f89ca56cecda75f184a7fd8b4787e4f6328c72eb1f4bcb21ccb2a0dc3a2b1cafc4c6174666f91cc126895edd027ce9f79ad6c87ecd0ac4772745d77a98cee1219dda531ba4b21e82e81a0244677298cee1218dda531dda52fba4b5e7497ba286d51f24077298bee1216a50e94aae82e51d15d9aa2bb24a6bb2445778903dda50d7497a2e82e41d15d7aa2bba4815213dda50c749730b0c475410ecdc92a9d2e28ea1996af2e66da6833aeee4e22800ebff2cbbe4f7a34664ac0b9a30eafc6c31d52a44270dec1a53f09b35f0c40f80dd31d225c0e670996a940991df25fafda1f6b05b35a5ab084127ca954aff96ab4208f24e3fda4f74a3df487d13051c4b9578ba96867a68e92575cf9b5569c7b90e6fad82545ba9676bfbeccbd3eb633cfaf6867700e6331cb2bc350ba38ed0d9d09932b695fc5393c2dbd1f3a28004cf3cb9c0239a78103acb25421c68e0ff47bd5da11841d353b5c8d73fd6a43d01e491b0a3c20076903f3bf3a78e8d0a2268538ec995691b3998e72544d4dcc36f19dcff2bbd61f7bfdd0bae60f613b345629342f518c288b44f8e9b599d5ba5f6944d90ec9aabd5b1ea42e9acd62ad6b4b4d0acdbf3677219728b5c8c82c81569385e4d92c5a7a84e8a18f58417777c9007184e98ee33b288e23dcf8484dbceb1e49e2ee556d77e57f9ee3fc32cf2cf7f1ca4a617246a35d8b3d878fe47dd27c8fe2f191f092873dc6bee5da49670f7359a51e056db5def8517ff1ba9c730ec7acbd45e36399787c2c766848c91b41ba3bdb4b63240513e8f9f3671563f3fb2badf7eaaf4c25106281920daa20811311684c508308125de386125deac38d1d7c3918e85d9c02bddafd17e8d5da98d924da3872823c4e20c5099c50728229affcf865a788e52b7bb2fe176b98ba6dae35542aaf0cbd5a283a3a38148fd8c40650f784d91bb22182898d0fba1b851d5f0ee7584d52a9708ed535a8690d9557eb4edcab685454735d39fbb9c9c9d1a149a3c9aafdac128c0950e86e262e7003c5b01b28c61d16a5f730bf32744cbde895b2b14a5f24c0407e2ca318e7f592e64f045f58bee69825bc4a1620327e37a1cc18a4797ca4f1915e2fbb6238823c46f0c60872709202bd1aedda17e8d54c686c818614ae7a85b57f599b1d852fe7610a3af130054d6738815fc4983a99799f3dcbeba14b997f5f26a337ff153f89e2fab19f2ecf28ea2eddfec386b04bf628ac85abfcadfbdfacf2cab7b0f885576c198d62952b99550b02baf2bf1f59a5d87e3f397ccf5bd50f89683f1482f7cabb68f2ce59a65baaa5acef933949a0c74588195c329659262bd77e28cb7dfa85ff5672ed8780f087e0bdf293defb494421e871a99f05e3f0085ffbc124a640f7ff0be975c9a4ffde28f646393a47d635e5077a5c3e265bf8317ecfdea210f4b8205d09decf724b08de2b332cafd0ec10c6e36399e3fd24eb610a74ad95697ff2b72605aa967e325bd727b304c22ff3aa4ca2d99395f5d3feccbfe1a7fda1fdd06a94dfcfa34981022975e92ed1d05d9a81a64a0ebae0f0e7ff7eb2846460494ac8164e56eda579ae3fcb0fda96731826e29957e5ad22240c0d63f3b128a48b0a1c9c3a981d181184083d82d0c0a6e68569120e0209721424280894d296211e6419abf42cda99a95beca88cc5017497b474978e4aaeee52ab341485490c54378cf64349849147a55fa5fe30bf9ff45a98c220a244022fa5d504c4b823553f8ac55976299fa43757eb3050bc9e6331749b2b7dcdfa1efe595ae2f307a318dbd0c7af62f04aff240b93723eeba71daa02f4f4ba7c56a952c140f1fe34807e54c62cf62e8b60f7b14f5af2b4710c1036cbfb999664280d7d9143a9a8bb443434851745ed755107eb4a175e17417aa8bb278b0b1dba7b5631f6d70917266c9187ab4611a652dd9ff2ba32bed4ca234997f7dac28aee9601681c4c8b20b89755af3296bcf28a520b026831a5dbabe2cd8eca987eb9f695c70367bacbf66b756e3fec2128337e90ba304da2d3e52c0df1c051cb2b4a0f8020cb02882c4c2d2c98e89e7959824e74929c78170b8105162d5c714437075eae0002bd9a04c10e98e0fbe4cfb4b1caaab6cae80dd19ef64356bcc00a1bacb8d2fe49162c7da03400d62771a6b22b3ff79654e9b987127cc592e8f87d96743e08521f7f96bdeb7c8b73d21fb49ecdce7de96a5248a50a47bd41804c777ff1e228d4a4dec5cbcf32950e012bba2150838077739e0c913cf9b9c4a19cdd2f72ce867b8ed6f816c68da6b30ba344c39684a985a9e16c6e587b5a03a6cc186e0333e3aa9d98d27cb4f93e59e39566c1c07025185373b107b8c1e5077cb13d60d6ddf67accdaea1285256dfe7d400958a4983a60ccdb2c69a693ca706e0e987577eaefe600d3587fe6da8070a337207ce0e6e7bf61e702e2a6fb3e16e7bbb606b88083ad7c7cbe4ffef88c2dc7d25d53a6801e9729355bc507fbf864fcd3c7c7a6660ae871a9d92a3f68f9610887478984570cb881012b30a0880f56f830830f443d9ce9818ceee6983c683193ffc66f6249fb9149b69509a69f185b713f2568abf7b36c9f7676881bbf4f5eebe327c360b7e2e6a33e29f4c3a587560f29f47064015c3487bfce53dccf98bdb3fc7d36d7821620bb9ba8b7059c1640b329404c734ff3f1f36cf6ce539cd00f171feb95b2bf8ec5fbb26a3fcfd28807301b0f2e6c0920334a211fbdef6e197a43c00d106003042881802bcd3d9633ef8aa1ffc73c95e2b2a433996843d1510700e300f500ddb643203bb060db81871d380388c000073080ab0067badbd55b019c284002b602dc703eca5095e2fe6332ac9fe27274fc615dcd57aeea3a4f7135385cb2dbd8705c0daa26874be70ff3efa35b9cc3aa2e25ab1eb3a1f4b0fc3e899a0f7ea61feb3a4cfddf0a27f4c3455e8bfd939e0e596c3a24a14312dd9dea4d87dbdd5a7ad301856eae3a4ae8874bd7798acbd61aa9bace539c4df89ecc72561393b8f5590bfb17a63ef7e9057d6c7a4447f58835531e364af06629da07696e53979b367589b5a98b03dad4a589367519a34d5dc8b4c90b095e7adad4a583ee7e7282099d04004d9a30b1a959a2e405175a602189afd04d23044d1012e8a68940370d04c2e0e0020e2578e0c31030dd340fe8a67140370d10db0d6b6c379041f343370d037c3072bb24021fba5aca61fa85a0ecbaf958c23c0445988fb6e55c17d28cefd399149b262ec5997813ff8fbdaaa5b8c6a97848f32bff27c526a4b9c6fba4f7a2e29eff73e2c4ffda2aa3a88cc5db759d73d44798a36c429a6b3c357e577e14a4d8a557f1a672b5b7eb5ce26aa92ca4d9a5f4ff58d78d9d5ffa33cb4248b37f52cc9726b1e09f459bbb4f76997e22e8ff315f795219cfa24dfa40771fd1dd493c6c4961bbcace68495c927e50af2a3db10e49b5e0cbddcfa19cf285639ba84454416c5781f6c6e48c96cfe8ee1e23ffe416b2095fce894a95ef27d5913d2ca67374300f3d8457f872d57a2a15e8d5ae19574cb7ab66a5488e0ac5165e793494a880b0465766002410951ea745506aa153502a75ab154a5a2bd76f85d4956732d156013fb3dea32c22dc32cadf6ae1ef5b55246a0aa018dd1cc64b6216fbf7c99ffb45dcf749af2685383cdd43eaaa49216ed6c7f7b1e8d2714833fe2439c3dcf858b8f191ae751658a0efa82aaf8dd1b073dd6439f899e52be7ea879fbdee97be579fcecff23e28d6a7592923e7d6671e458d554e56b543d90ecd073fb3f0f749b065ad2b94b4d67fb27569368bb5ae49abccbf34ac05014d3bcb2f94eb675c5976c81acd1a942b15925902d5a450b6432fa10d360446cb95825784fdc5df47913cfb613a7e3509be5eeffd6b146138a4f935ca309434fc02a9fcbc5c29885f7fc317ce8f5733a4d1a61960d0261a9c5859410909514c5c4e6de262d3262eb64d5c9e68139732dac485066d9ac18436cdd0d3a61988da3443529b669022080a08e0870a4b9460259bd258ea9a4b6f0420d2dd3d38dc4d2a5b7bb72f3e740d5f5018425303921724f0e55a43442cd7cfd0d1005e5c5642f4dab07949a13bc9bc2e7c391e0e87bb49a5b62e0be802e3f05fffd07ad7e696f7492f24e9bd1624f95be6cf4a3dfc46b952a1af75b30cdfa81ecd8c6d968c45a0f9343b6b100d2fcc80811984704d0a499fd5a450ad49210ea419bfc7beda4c674d9231971efb1e03b9ea2bfd1cb439db2aced9fb3750bfa2a54972f6b05182f8bdbf2eda916b5920bfb60609baf450f4faa15521617abdd26c966eb1db793fe969f68acf7ffd15bdc795765ea587627299c4589e84c82888061f5a5d71b951906b1564148307557157eb48c839e7aa75500e3927ffc67c56946c7d3ed69573588e5fb55d87453c5e7973b5de4f8a65d57ed2bbcef1e358ccfe94f7c12ace75f893a29dc1bbae731ddc7d7ca6c82c811c35552a47f94c51a962dfb7a68074d60aa5d0cb241577771cdcffa321683d5bbbae0bf2169cfb4250d6deab24d1ecc99f32e3cc399681063238d1dddc9225ae9af485c2b1bf7e6d752c67f76f48d559a678cc7452d1068a2b8be6fa322db24ae503e12f0b17ddaad76b8888d5320ad2f20a8a616544e46abd5e2a5790515190ee8d2805ddf8bf3046747f7ef6ca7d11e35ca950ae542894b4d657fa4df940b952a1bf4593057e6685d4057e66e54a85402abf1feb9a8f8f26eb7ea54d96278766996e11f26296e5cfd7dff0e7ebfbe42bcb946e2c69a1bdf59164fe095225c894203d4e926c583a1a187a04d04d13250a0d952952545168a03ce9a6e169a2c444a549544b81682cbdb1e0e8f92b1b83a32a753bd4835b594987d20a343b5daa8175b35da901506abbd275572a65d27115bf4a71b98fd72478e5c9159b2b226c31b0e12a9baf8d01278610ace0d1ed2a57b9eaf57904dd43bd59e1a11b7f92fa4c196167581121080e0e56100426f11c72f0f94ec5c773c8c16d6aecfd1b5e9872ff37c64119ddfda537200b7c4b6ec049e2d3e4d44344f363f47d140bed87a6fd69fd7cff5069055591a38b7a785a2dae662409febcd62bf51a8d65d1c2a47363be1fda1b3a0b3d3c18b3e0c92ffceb3d3cad96cfcf935b1283738f64932ac536e34bc51b7ac63f3f5be3e13f92db2ce41c87677525eedc2cf49f757f6dac669b1a7ffc492bce75b4ceb98cede70fabd87a348fd1ce06a552b96a3afcf773cc82985639f31e7459a5f893fe4be5998c7e8e65f5a77b3463fa7dab2931fb492e6d5c7f6bdca3a1cd2e33a69f773eae291df8f26fb50f5a4c633625f36c6bcd5671ee6fa03855a94b2db608539ae7997533f899aa825c4443ce61e7eea342e7baaee59c8dcc72daec78090e2558e31c0ea5102a8be1276d2aadc9d63920d6959fe851f97d622c943f533d2887bc0f5b2cebc2f37f287bd69529ae76c8dea7c1caf7c99f0e2e14aebb25cdce0c94188b6ef3b443aeadc707a7c7490f0a5b0f929e211b9440a0988152064a0bba43eb362fd4b813b779c1512fb82ab48e7ac16bdc5528ec5fba6c50ae80724477bbcaa6a6fb090b90ae94c9f015ca9f4debbd68351afbfb42ba125452820d342fb8f07dd2cbd882af2c5f342d548af4ba368f57528fc6be3eec93b94ada2751190bf83f961fc9a31997d428a131294d121a5fe171a5af1aaef7a0acdd2511d0a850caf103f46add342974d3a050c5ee124ea60900ed95690ddd3427f41f26d0944043c208746c397974778e33999f0da54773b3e5d8a6018008219c36130d129b1a7cb9da0ba7c6551e0dad47433bad10feb792bff5489ecd92afbdf2bf9fa02b56d149dda03b95b3a548e89c24b779564baf8ddd0fc58c6596369674d39c604311a0e79c2b3c9b0831418349b831d169d4fc3bebb7706ab3f1c3260dd0ab39ea0be87151a95c5df0ed6c2af51a97a0c42216956ab290aea5e51f729f94e3c0c42b445c25e382bceb06e093c2a0734c9864d13a13267e6dcce7635c93429f2df84923eb2aa2114273448b6d8991ae8ff1bf4eb029b182735592f9559430e12a25a3928a6b52488f35b617d6e86ee9d77ae98ced05256658ceb0bd3039d896ed05176470a10e20960b39896e2ef4747373c88c138cd956d0a3bfdaa1f955465bd95a239a34da580862636187eeeeba6940a0f98086486f2c4cd958b8a1f18046080ba676951191915190274992644b228224662411b32571a2672f941357cd77e2a8d7968407a742854a77a90034bd25094a72d3aeea2ed5cda9d83c039b03d1dd957a3547da9ccbe6425e63f3faa4ab72ed51ae5a61896e49c37fcd29b1a49f7c8953ba5af04d851eece4451b7ebebc4f1a4333c68b0ebc604b14344f341edddc63fb795ecb18bfe789f8c55ce59532f7d0da2d448a70f8f3fc7a96568814e1b0cc72baa45d09cebcfab01af34f8ed2a6c441a968ca082f480109427497f4a041a51f95b1a0d20cba4b32e8ee2079246d279cb19d3044f7d39baf8d79fef4e69a149aad1a942578ad15d6cb6468071c4944adfc387febd682c6c782e767af608ce4652cb3bc572511c6f2c3489ecd1223c2f24a8ce8f3689ec55856a9a20a982a5ebab9ae8a32e9ab1519ddbd42c10c18c410e0048391eee630f77d02b07212408e4f0b82f059f98440a50f2a719ca888e96e0d0dd12a769e1abf599fded883b27e8efd37658f29664cb9537ea4b8408a17526652a41424523c5059d1376667959d8f307969aeff7f6d3ea94e51bc8872459457140f4e3d25e831408f0d141340e100141fa0003de9e3c919ddcdc56c2a635be452329d4832cfbf5984d1f1c3fe77fc669523cc75455bed937f7284470f9e0f788ce5aaae5e11f6a2d9efb319ab343af1c0c0edecb0c344e78f93ce057468d0f1b6c2fddf59e5fc9c03cb31410c9c2970bae0707974634c479b2ba5e37743c38d9193404e4ea03839718013282727cd7131c74b26cd7e1f0a771d77e246e088a4b44851917aa58aa0b0407928119a80716a324413dbc44a13cf9d93f9d586ff37d859a6d92b4bd9c7df5f0b14e455d678771f14c59b3b9b9de59966693f98cd9da71e87d413275ec2f28c4570ac327f2b26484c54367bd86471b2a9c1e60af7528a5c857f5a218ce577aa2973aa09c1a946889a2b35a6e6bafa37d86b674b92382df9d2dd47fab48448091f642821a2bb679e685d8f24a5ac387c52a2e4853e4e2fa8717a414cf7e985009c5c70c1077d72818c0b4bb8405d48d2c21939b35decca25b5127debd4020f2d44690185e626ed4e2c6c39b1b042123492fc9024764a729424c8c963e0489cfcca0a3538adf0447f9f84b3271d533ac25c3529486dd879a5d9076dbed643d133c6b1f08a61d29de56b69dee1bf22ae4155be30f59912542d960fcafac2147f12a4decf4fba5ed77ea84298eeee930a590501a830a4b93cc256a714c858a510a6b9f15ba530aba24da126579b51df7742a10cadd9160a447c969ff4f083288428388797c810b4a700a8c1e153009ee89acf1f9f9aadf27dd29b6179254a9f4ea0e174c20e9993096a98309af0c2c9040f4ab0e25442772a21a804212464d114d53d1633166b10ede87f978876ac47ced9740f4ad48ad6fe8b7d52c6f693ffa2cca5579a0c5bdddd419f484022a1841150308210a71180648ac300e80200e009003074b5d363d4678e1a61df27c21622d8e09ab1de10c20a4218e214c2d7dcc4ddad47270d9cbc53924d055b10a78dcac974a69bc3f7b1acd2751eb392fb6abb90ce46f971297c3205f1714202c5098944a2c48d233f9c8efc1124a7232710c49c40f0a1ebac749ee264e7a99baec8392c4329ca280c9498769eb27996af1876288e56ca840997b1fd983071550c43ac21a3d5166eac5d31f41308424e46ca3819d9c148cde98333ddcde5d1a606d32c6d6658f381014e1f2cf92000a72264cc2ac75aed5f5df77dd2eb4e45724e44627022c20511312722349c88f834d77df26f17b31efbaafde4eb25dd8538e99f9c61399bcdb0bca252b96658ce6cacb26cacd2d769889850fe5d9d86ecd0dded0de9ba1b853e0de9e9eeae44434c1eac4e1e10e1270fb8cc3c0802ca9310322721617e9e840079a93aa947c5d6a90333a70ea4e0babff6a70be27e7c2c3ee363191fc9e7b14cf15ecbce13f1a7f54e1d70a72065bafbc7ce539031a720439c82e490e24e418a9c38e0e2c441adbbafe87930115b6b74fdb73813264c9870d23f2c270e60c814fc479242270e4af8d689468d6e2e943f73bc04063ec62bd86f39d1c04e3447279a29ddddabcaaed5751eb3dd4fe72d6fe23f35449da7c64c6b3fde420bdc4feb39ed6bd063392bf2c118637983542ad7fdef7b6cf3fd57ae93be72ed2b686f8cf6f5da53abd17d6a3192864fdd9dba860b848fee40bc08a4c6e12c5781a0ba0fc4c80dc674372747587783909b37f01b00e2864c5640c63417d20c880fddcd0132833996344060709de1a387ce647126cc99a2ee96a3cd673cf5071fddfd871a7f38d12dbd87fdb1c3fda3eb6e4ed2feb8f9a3577e9021e44798f6236c3f7a84fc1842fba13ed6f0acccf1f7adfa80a20f2e5ce7bfeae3a65d7c94e1643436e56cc58798eee6f8f0a1393eb874371fdecd01b1f6e0e3fb567b8ce9ce4baadd838866ed71a5bbb9af362fd9a304ce06656ca0840d6acd511bc030c26c60ea1a94e16a9045770d66dd5c0d7aba399b1a9f8f690d4cdda3ad7a90e9d6238c1eb3fc343d4cdddc5f0b8e55521a64d1ddb53408d3dd1c8b066177d3e0a6bbdb69606a6efeac3f9b811bae1938d13d83d99533e8e1647506260a93411e2d1988b1433228eaaf264119d4743707c4e2a3b93cc8e8fba1e7c73f7910813d8f8e93558a252d0fef8fc6c08dee1880e9ee9eee23b39c3ea822289fc9c2de6bc13e36e74a856496f35a2be067ea333e92cf64b97c7c7ca6f8f4882d2295cae58343ea42f9b01ce59a12230a290e69f651a95c2f95cae5f3fa3ed319cdc7352566274ba57299c9c38c1a668830a3831925664ccd75b03b4228d3461930ca7c65885ad0470b9e68410d2d80a1dba6fec735f1f1916a62d625c5622c6929298f3b4f4919bf1aa3d3822955fe160b8e60c10e2c80b120880529b00000769461870f76c86007143b4cdd9df43a4f751c15073b4fa9feda486fe29e3771d0c3d5b25cd5cd1bce63b68e21ea20aa83053a6c40c7982a661156633f3ba3613a4a98638c39c239beccd1811c61c871013992e45042068f59ce4baaa571e357edc762d2bbceb1aa43c2644032362b58630547c4b1471c248803873852710851810a5440850abeeeae732cca686c94e0fd24eb18836faba3baae1e7170e49182a30738aac0819af91b42bc71f48691147491021ebaede7558b023a50c001140481022b28a0e2c61d6e44c08dae25cde6ab1d6b3c533ba3390aa3326d038e36aa68a3d6c66c633bc19913dcb0a102368060a3870d13e72fb799f455d339a6b95250f589d7bb6e5c238735b2ace126988109ae3001122660994004aea37d75da57f9d9a6c6667b4798ab3edba15925ea96608912e012542981cb68ac92800a12742450410d39d418d35c37d2beaac1a551260d2cd2a0401a4169843002322378c0088ab611981ecf3cd939777fc9cf5cd9fb1a60d6bb2b7e3457d151e0679afa1bb6d038001a5bd058e18c3dcef8e20c26ce103ac388d7d9fcca95d2beaec059bed66d76ebe54a854440452064c61e66a0c00c1eccc029838e32882863a80c131962907144a6f7bd5e31b3b8f772a678090afc59c6f26fecaf634b63706e4a195e7b6d2cf6a98c1fe6d2f15ff193499f7a23326ec8a019438de6ba6aeb156398028da1658c202178230461bae75cad407003105c018219089c80e0041004f9801b8d6d4a4af7810f1c11830b3192c4a012061e61d811d45b184a704ee493aeea42e7c67c65989a97cec63002b0815107185f808100306a603401631b338689be2bce064bafc6a5a454f76799fe2759a1313b638a7c41822f10f005973b5ff61f4da237aca2a7bc58c38b0978519ba38b26baa8a18b255c04b27101022e94e8eefcad5184b9aabb3f8a196359e4298e8bde228e2d80d862478b3db4e0428b0868a1450b0e3cf0010f844f04c35aee2b577912958ddf8ab3c98f3d466b1c779dcf0f250d77ddb531afb25654162fc882892c7668f95894c1620358cc8045962b6a70851b15ca155b07c674a0031df8a1032b2bfcb0228beee63a507ec64b1efc283865e7295511d775dcf8852e63d68a2755dca00a36aa20a20a5715a6f959ceba1587f1129b7dfca8004385162a50db1466a618639b0288295a537420268f6a65d2c78ff652c1f295e1084349c7a1a46131338811224518522421054c0a231cf8830319e0000fdd753ec25c66393b4f7163953657dad5ff3826dea453c94f04af8d79f2ca98f562d6fd6779e380120e6c1bf8a30e2edf2b5dd5e190e6ce531c0e69de40ddc09640a280220a22a280018a33449f745b7d52c754de2e46bbf06df2e31acfdf82e20505ea09173c81c413f5891834d0c7a60138341005d7e56f3dfea4aac328cae2521a00d2c0113a9cc8c2891b9c28eae6301e65f89a345749f354e7ff491c8a8e978ce2d6c4184d70a009209a906d4db890813532c044068ac8c04f0648e8eece06a786a87315d7cdef1c670a068d8f343e52e729cef11255a7baf23fc74bc0cfb4f314779760ea635d713fcb4430bfd6bb8ebbd67d260bfc4c7daa8f8da37c6aa678afe5abd667b21ce5289f1c1df14ed667eae333e5c331fbccfa1ef6f171d4fc7293336b3feb971c9dcd02372052a97c7a44d0e3829ff0e45c6159e08905842c9105e8d12c419b4559972852a2054a484089295fbc384a89139228d39d34ab4c62892428abb7243c7fa24dbd1656e39972b9d65505dea8c0022a70d35c16adcf6b1d355d66ea28293724c040c207247a8ef0a4a33e11bf6c8ab09ce727da237e38c21e7182111fe8fee205556d284a9b51571ae142776f45c0d1cd15d144113d1471632b92cc1d92cce394451421d35fbc649102435000060ecb19058c1031a6bf7841c9d0bb78e16c3ad0e332ebb7bacf315b5325feb742840288e0d2b4af44b830013dbabf784175f1d2da26f0c30476305d7199563b9b0d71660826be78f18cade7a82e5e388c45c7a1cdd887f819628a102c10420242fc04914710477cf1e2933aaa7e52bd9f6465fe49b14a21e7462cdafbb22aa7cd3ee5df1aae5209fa741b8282786d415cf12e5eb85c25e0870498904051739d8da326ab4b9233714ae9be4ffe74b38a2e812d023388c0115fbca08058a82e5eac9d72b6da12019eee0eab06a804f248f7a116328468460000000000031100204024180dc72362e178d26e011400015fb06cae529e0bc43cca29640c31c41000000000200020902106004b093cc84a4d5305c4eb7f5bf2761535e8d142836ed9241c418f1022738aeee60b399142a60806fef7d45aa6fa74880e72a4dfd5981eee60e1f679eb765b8811e367899f57c798c1265921fa3b00e369afe61a3b1a4e7bc49a1373601c8824a4a140c1a5a5e67e22a64f6240550f933d83796496623c29e71224986b68ede731d149e60b8706de8ade46f0cf418899d3312ab01d380ddc7baa431598ca46729aa93d3e421e6b9cdefa65bd9bf05e1b6f8535e96924f73050d8bc49d28551af05c87d2e7462a15f117b53d99aed6b469c54b7f30641d62deda01ff2078c2ca384af8a03eab29c71a981ef090dd627e14ee6ec633f8fd2c27e34329aef33fd4e20063169ec8b1d3e0e5a97b0d8b16eb5728972b33e8e67d45c7197232c9bde9b05011cc649651690249e6fdf2847a3af53de1d955aaff3103b1c6723995a99fc51f47cefb36f7fffa9f7bd67e82e55f5a459f2298f5f1f3455d17327f74d3895fe88ea24b47158d5e7feb918778f6f93353599499ebb038adb55069f7677642d9a24ca462df1a275209883fde0c87012b2a4603785ec1feebdd4de9f46e04c0dcb04ec90c97140ca89edb27f514d5c69b65dbc233a1780623848a81ed775b89d938bf0db2384d9f246c60243b06122518f2c3912674a1e29b9e0e5f671f5e4e5462ba476c0bdb09c93786d935178b44621969e07733ad6b8cd29d373dcc6c5be4b0157dd88ebf9ac22ff54e77ece4f05a9bfc7327db8b84f9d92fd1e40a01e72143d7f039c3c13ab4fe255ffa052295d4aabeb15247e23f0a4e318e95eb99d26cb4fb67d46aee1e0bf512f539840eace4b992bdc76e5c7dd1dc9ab1954d7e35e6e814200090d87c92928e46cdcc56f4cb16c010a276852714196dd9a31bea9b5c88a85a4fc9fd3637abe78b581261189d31c8c19ad650109dc804f0c4676867d2cae003edaf0d95e1eeed007b29d9c7d9e6d48d55515f31b2aa752ab1e8d518bb04cbf06a44ac3ba9ac7266d59b3286fed795dc3e303a3d84e1d2ec56ce2ea8a3ddadfcec68e670f6efd0fff715b8e9d4dcae6dcc2b5241b6728c50b2289ae59597b49090c1e72485a94249eed9a466ef121f06c24fdb5f60d258bc61d5d72b1eb208dce3f221d8fc421cff1511ec84c7cfe345f9b69f4d67c9669c4c583e289ac15ed56e7c7516f5cf9f9dad9e05d1895c3199bf3e37459aae5fc98a7868a363f8b2f185ca078b5b0b4106063cd7cb35ba6d8196802d95ba09ec91e542034d0eef765cbfc55aa178b1df31f7a67a0f4702a342f698b5ff7785cf438ce952542f20818389b2aa71562b3731ac5f22527887dca6962373239e9d14025959303e1cdecc79cf6bce7c9d02664df0259a30d7d7c6ed07c2f33bbaacc64eb654458c8082ffdab92a08c609025b5dc0f22e4df647e5a06027c396c8e3d9ccab65bc27a132998f0692fdadddffeb1df14e21e7161a49292545bc5fbdb9e6b19155ed9117a67710e9757ec07d703efeb3b1f57a541570a33e8a88b1a7550848d50a6384f0d22a910a9ed918fa50d9946df651a1befcef0daad831b66e560262e10bb33cebc3d2eb30ff746907231d6667f1252dc9e420c7a1ba16bb45556d9b01b4477614e3972064fb113aa4303693471d6faf5a0bea858fdcfe641419433670487a792c6e1d24da4be4703370c6eaa0109456c15c1f2fcc3a058a3147d6338b54ab48a0a60747f37f4577308dd83030466ddc48a66d45bea01ab06a70105f026448a64e7800dabde3ef0402ff3e9dae8032a851c2ef3f3c06ad4e3447196a6220bdeb0755841939b6002c4dbcab7091293247bd067a911d8a1947d4833b3dd1624e19d065227e9a11a3021aac8153839dd53ddb672307b9e989aaaf8c6cf59ce98f829464e9b64ffb41a4812570ba365d942fa5b0ae29b7a90d4419cf2360e50ef81b3bdeedaabe922578d42eaba2f5b8365d0fcea4ace17ee40ffe41ae4699707397ddda1800f6ae2e8a5b71b3a40516bedea0928c71d2dd8e588e92b7f665c7e0a52a903558275768d0065ac7ad781133d09e1309edd6102fa3b9d36a7bbca884fcaa36eef511de5120e48cce5de66dbf7269e99e94b24a1701f5bae359acedfe8e85b7dbaaa87e0308a138fe7c54a9e0cc9dd4f0b9e996b5b2f38277e72439f44bce86819897a26996858b1ae23ef34287a05c5aa838a8422d1f1ada4ac668d0c81dd2922857fc62637e3493c14f6e6ff638654d0f0e03b74128b392c7c699eb354670601bcae08cb52cb643481e38ebeab200e2c7c7d00c8f2df2f6773a5c84610777bb74e42f23fb6f4269593828a9d69516ada90cb1b84496ac305a42f40c3f5cf122ec9d87f8b64d4d903ac0cd2310bfe0ad392e68efbcf74cfcb4fa6ad5777ce7caf927bb2d5103f0629befaff56e9dc41641fdf3f254b73f8805bb907edc7608045930089da7e4fedbcb555fdffbb061434d1689d59fa9119fb373b30384d0c11dfba2c7e2691d1d6f8297c9e00b3027045447e4ec6cb3c512f29661c8be26af53053682a60d2104e3ffa631e8143e8462a3811ddd3934b432761c4ab04fc3cadf0f1f8ee0a437583f013065f0b107a7c77d07d1abd278335071e7eeb790e06fd239f5f8d357ec6f41a91214e07316767a1af8b1c7dafa15917f52330e1e0edb2c8cb75e5e611c7b84e367c9715114e53de4d6aa61bf7d7ef66bc7f83622fc9aea305e1f281646c8d0b4f452b20fcd85222ee7ea34ebf7a1912c78c0b701c2ee818363b59e94a351b2ab67840f735ab6db715dd5b7448bbc5e568be25195e6d05ae1060fc95b63e7aa3417ccd3e2edd6ef0a307f6d083861e986267ca2eec8a7750a530f91b01a0a3ea91bb87b0c6a10872b44a10b5d7b3100582e6d534a20aa14648850bb48a8e8c2ac299ea3f50c56f413ab266ca6a140e92339f0acb5bd77b0863c16bf065f7f3cf85f139b7322b0d9401b33fccddf50bc7193011c2b2e0b09a7cfd496b86ea9a6df066f6707e945d5811667f28cd8eedef2fcdd8fbb0e67aa0ff6190e019769fcd745c0dccd5b402dade0e0644e509076f49084034dbea0d1c7fb18600cc4d755b566ccc10f4e888b6c99b20e2e3ef0447b087c0b2894c2cb74953e8ea0798c11f50e0e22a2e325a7ac5e1776ee261384547f31665efd2306b638e847ec78499bca069d32e6aa633031efa6942f1425f6f5464080682a0051f374784525d5679aaf73c5260e1fbf2f8892e9d884c0530a0c3ebade73e4912d7066a4fa1269e05d3145218b8beece749f0a114a78d122d60ecad11922c62a01d7e73e9310f30c27b13472c217474870a43d5271bcfb89a8a279b665e643c3e781b415471337627fbe5206ac49c9b154e60bb7e1d50644d769216d8386d0fb55865bb127dc9b20bb7db9bfbdda6be7dc4d1f077e6e46fffe3cd62df49f2e38807c65bcf7b8d0798f2afb0edcc1bf9e98f17548f3f125ef329fb69e169ceb99ed6e60789846c8b6bd1a6a9e374fab3db8375a01c3f8a4b489fafeb615faf4e9ce918f71cac85481126d6f3a10ba773dd4d89231264fc3b093d317cbe1a2a929fbeab87c8d977088244e6e491e8a469a1f4d0e3a37a8dbf7e0711189552b264b3d771f518f896c1e1b256691b2989a654550a28131d7b2040fb5583ec939775252569ce152b625327a4d71934cf91fdb6c0e203725df98fae79a29c1933d3f5487cfdedb8ffa04de3168c220efe529f68421ebebd29ada2c1f6b0ae8d08e48fe60f1cf285aedbf3d0b2d0215a563af5ce19d16eb1d11e82d6802fc854d146bedb3d209bbeb81ee62fe53e51f7839d005504e6c8f5441a4d0eccdfbad86f46765d8003d863095c5ecebfd71c86cde0b996da39c071808141c6e00aedcb6569e01f64f6902808c2c216a594db3778a82e58e95ea9900bdd46da1920c2ed027ca536a2f101dec65870cdafa3ace67429716af6b9c9712306bd7b44d033846c96946c8dd138f7d6402f2466dc66c5868e432b28cfb291a1f75d4604dc89f40c3e12d8e2ce5e4b86b52334ef12fc03b076d015543371b37573142fa102a8e21e445d9a2eb1a7c03c3bd9e3dd739ed769d8aaf41b425aca53e1688874ecd474516fa801e0611170e3930389cb88ce60fd0a85f8094b7a10f943f87417d150a3ff123d8c18d0f5a0c08888183f5c286729e7b4e6c33d1fa617c2e3967b7698989c744a6e19ad78ce4c4d91041389809e90e82fd5d491c91965768b54f9fe5101e9f9c52addacb2473e072cd937cb07c9b7c7ca6ea1bdd187f0a031eab9b119db98ce09d6f873ac32f3fcd101f2360b67544ff7fa4e11b07f3be27f4891feb0af038a88bb9f75d47b2523587d48a3097439f5358fb40a33c57590fb7464a92216e40b8e287b94530b5b0cac06743590f9f6c0a3c841dbe747da2ce27498656a5cd1ac0d4cadc9a9a5296c66ff5b56c592bfcd4cbb2fe54792fc131380fab8398d1ad73415e65f8ffb826fe0b07b18c8e305c8500feac0ec3186c92faa3e12c3d36692b59fd1390f1f5522c2131d25770e9d736d1c359b906e7161f06d8cf25562ef4950508a278ab07298c410e86bb2a8855a645b26e7d938b0f3f008aab8a3a58ce5911a24a150209b527daa1f55c3456587399a3f8f4b3f46bd07883c1538c0c3e4c664bbe897b836563e13d7f0dd3f2a92cda270b56663b8370c30fec0adc98009ee5675d38b148a0123956ec007082c4e5f9f8d4325a9ab433bdc49e5d6db32ed30b6156e24001d8d3f7256b571f530386a1a83717dc6e08a571eea97d6ef0972ec477b0d5872b082724ddab3252230cb6f4f225ad63a62e78f8a15429aa4f46df0e8bbc8df04c9033b36d2e55c36e418ca75b65dcc3c5c534f6d4fcb0f52369e1eb265401915e509ca583c7b20873d1c3831c858d4e7d689f9cf357f5b4ddeb6008f4a973a23766d259677652a9c7b773c99b46f52a2ca2281baa0c3f87209f9c6ddbea8fdc20f76c4517dac3cf313fb66eaf5e59f74920ce840588d0cafd22a154955876071290b20e8d0beaac7e898563295204f8bed979a3b756b7749574328aab9068b057aad6878fb076b51ffb9675a7649e93b02552d32d1028517128923e367b6e50dbd9335904ce9a4756700b2f89bc8c6684f5083684446f4e8a16432165ee831f9aae129dc4506cc7dbb922ae552cde870b849fd2617298002a8968b7ac267b199e8d78a1db64defb581a3056fe7360ce32215bd3e8c5b908e79cf53cd06af2f0cc30be6bb147655922799d1135cfefb81accca01acf41e0fdb9436fd0597a7ee5e86d4d40a7c1dc914bac0da0b5c51e8cdb1b90287a2aec8e8ffcb7336aeae6443c818ba6558a2ef6c57acad5d5247cfd9edf23ffa54bc69f99a7093594e85a521305e28174e438716c6729e24328909f500d41f4988f60ec23d22f5da93ed2552dcd26893e6900c57377cd965b2276c62c132ec3b4999c2206c17a80805c1c9617bfdd98208abcd2b0e1ca5776cef532e87c66252dcd789950cd1f12d3ddc4cc51e57e3a96fb139b3d4fd9fb7385a94309182937bf2eca2b31dcfc0e6a1314678f1045bc6ffe050dbea06809ca84b04fa0f758702280fccdd165f44de9304edcdf8173253688ec695fa42f844223ed500580cb490243093729d75c5fa6ec330b7d966cd1a78f1df3ec1c4f481e72371b079021dff544f771a66df32b63cce95676139dda6a32f87a96832535663fd20566ad7eeecb646fd9d36ef75e0280e2defca5fd7d0fc79252646b4782642c18fff7d0935f96d39d2dd1283078cde019f4d996426c82d580edc62c21618380c8a2c0cd3138cfea3d869a1702dddc2cb6fc88465bd0c4a659bfb1589a1d8f0efd92e937aab34a9de170c5a36be78f22ee1fafd09a974ddd8c71475749bbd1b8a9a406a463c45b19a4cfdcd9c49663ae7ca5c113f0fe49359eed863dcd5afe10c0bdf7f8ef87637ea43815cbfa4272b7604a488827556c2bf1f644accd1c21d19269e088c1e41f0e3377ebec0ea3f0b8deab4bce9e8d174acce76f56ecd0f7c742b0a3fe8c134b2e38d4c98985ec2fc7404f83403e73fe1526b46be24f2dc28bc948779c4f61557b98e4dba195dc732906d04a3e43bb98c266f81d890fba3a7c13e692b1ec410e397c5a15b38b089ba49dcb6831370d00db2d8dd64cd10ae20b09ba017f07f19acbecea3a0faab5e8727c43d626a522c6651fdb546481968b7dd05c8b55b437a749f39e186edf5db98b2503e22e8fe25bc03d11ecb07061a51b652f41840821345e73cc4745c133e7e2647d7d43bf3da9d8dd0a50fb74df9353e573566b880625d515e1b54f8b6b52b588eb59638f2f3158431ba4d5a46b51dbbd8199f53a5946a902a17297af803d050be3512f273f6eeefcbc8f52228c52ad8c2dccc9835e3d8fcedcfba1292e05259cca1e7f7918fedbad2d178f7a470a26be5b429e024833917bf451a7f2f572c4bb8ddd7e9020ce16562563836e887486fa753cdb00f53843e534f38ebb55ffc36f4e7ecedf44de69deec85b265994ae9a687ed38273f79f919e880f007fa1ec01c2dcb29da457151f6d0c5873514202a49486455bce9727793a833a53fa8805dc18cbf8dbb57f9e7ad6971b82e593d7d42e7c074a67c170b14491e2c499c7539906f36d1c080ca2714b404c7e0725f578c0adff004885e76635b83686f1c9d1ac5388caef07ea917a4357a5ed832d009120611b7052002bb367f6350eefce6fe36ad9c99f75d4d485721c9206054d45a61080915d577b9d5d73f91a9f12af2a98106a9abe813cc714892abdcbc659197cc23f8fcb33b5ec18252d2d58850114dd39111e33f220ccb82cd0e4439ff41787e4e4ae31846a3a75ba76639330a0d688d7403ae995e3ed18914492ae654f1a0fb8b0cd25388a3a5c877e3b35ef8d52ace82f8463edaa202c054b3557da1c86324ccd35765a855bd50596351ac3d1ac50f332e4c7a144e4c402a31ad27cdcfa183489914b5c2b4dbdcb056a222ecc08d38765bc22619007e4b44332be469b0c39b5c1b88474e42d69cee538d35ebb1c55227553c5e1ef6dcc75bde097bfdb84051be94c1e01f33686e53a4cd43b7c03510cba28d86274316b023695e5b18428656f1ec9da82a0d2209b0210610426a83b30ab85cf79401e717ad99e80b051305f8cc8dbd3b2cf8350283b8c872fc2eafac94ef05809b2050a07e15036aca3fab318773859d0cbe0f5ecbcbf789494f33b4fbf59164f81a55aac9acda674df8b5e71c4f18c823acda94838cac02e387929165a70661e1bca068cdd52e98039b678b0031d581c4c73d94b2e869126b77c8b8603755df79f697c36b76978e7f9a8070a543fdafef17b7fdecabd5fec046447213e0a14d2ad8678c750ae3dcd30f3f96ea8cf7e46fcefef7f6e5dab75038326c01dd0dc792f56b2ffbf9a267796820de3c5817964e2b16c267c4b94ff5be2d8e93b0188a8058b7ed60d3cec0233d80ea20b76a12ae9f3feedd49e834ea0028ad477fe8e4829b6f9cdc802401aa26e6d591a5e4b199ba2e8f00cccac08836721afc722a7852d12752f784989d3f3c8265667f845a3a7f14ababab043949a13e0196a55c9f25876bdce66be91bcf4842a33fc084bb24f2d939dca004021652f6da963d7a125138ff6bfbc2608087e82b15891d3de6389a7b812e13573ee23327b3a56d3197483e4d5ab72cb6d5d3040bf34c68c1871923b596540460e58c41a869dc52dbdca252a4a44a295e5ad6a2996cdc41c0ba0b1e04c870767173c53a8033e49bc33239e4ffd9250ae43560b7118c1d7dc1a246e2a5b9bb72d6a8eef501e46d68153c7282f4a7e11d66505a8314de7d38418fefb9a3f432af722019c3fe5c6f20565100e287546ec0fb290f3da4d038bc179a58f74f233585d26305e79dbf0b912042c5be2f899dd171486e7095dc6f35e12520d36b3204889235bd1bb7f6f899fc554da76c80f7670b256270b6eed34b9e9163ccdd1e2c263e7a80d5612094e885f70cc67a16b2a4fbb63f15849ea79ddf48690d4117d27458136f4df68b647813cede0ffe00a303d4e68d94c2170baa1c6d0e1fb4ac204b3f2238e3cc7899902440a99bfe95ec0852918c4f21f94d1f5d40351c2195500ffdbf54717de0cde06ac26dbe84a3e037724e32f65d4820e7ccecbe830fa87885358704fd00a740007222f4876cdd4f8ce8999fc9e5ed5b3f5c8046f688ae127e3679d27eddfbae1fc1b9c191c17473bf94d75e23e091cfd89fc80ed1d9893548a2da97401021ed10d80f813a418140c905e82bd0f37fd364047741151b0b6d1443c8e6d145895e6b3d51ccbfe98f97e20a67ffadc44c9870e55fe9d50b6afce10dc52932c8a17345de65a312c5edafb15541c6bfeae19feaa62e6f074a7acf2d37c374f0b87cd7859a03e0f08cc57591c732acb3e5214853ccee98ac0d8f4ebb9eaef5d2cae862e2c5813a25e73f63567b1007b7b2d321901c91113b51d333ef692092d7c22748dc9f827788fda69d5751a5ebfa68e0588d940a7b447b5701c6c8a48f903e2f14fb1d4873ee1c02cdc27adb4ae913f27998e25c22e04d39210aba1e8d5694a5eff159c9d5348a62e4b10106fc0ed188bb4aaba01c5591a7dc15c6d98cec77b80e1bed1786747d60715627793f9e0a80de2e505dd0b000b2cd451c1594ae8f6182d82caa0234aa1f0c66737dfa25b2016b7df91057b045f69180ce393a670cf20897d5e5d9dc014b700dfa7bdf07a344e2cef9d6867818afd7aa636b413545c8339f31ca5d19fe6b2e8ecc0145b4a5444fd77dd564f963f4f1e0f45c088fa6473dfdeba653447b22c577cee686e5d4df8297754d65795b1fb14b5f4b1000a80bce936502aabb1ffb5ebdad814e028e8d8461668e0fb96aec5d3336d280f099da47dc7c29a4b9c371cc401da8c23d5462ffd1e3cdb224a6d45c9381dca8082b7751eba1c503eb1621baa739cbc779852d9d73b5b9be7aff4a11e95038231b1f29ef81a3499ff5160a1e49c23fb88e1f8f5a89184d9a087c0c5ebab5e252e6daee2a57bc660d739740cb7b755a80993cf3ba056e85f1e608b697a55a9fd0370f9ed6eb7759266a2dbef56148e9ae5f262797492a126ec613cb2a922cb20dc3e383539fbb140751aec327ebaed3a670f4344b5002371c3f108b0df75ac2cff5a1f3a84eb04f9d1ee7393eee23c9e7c2fd5914a657c19f0e55c87c81bca5f0e492328af84da73400aa2e331188509d75035ee8279d15ab5b3085241b8397151d8186e8d78a6a35c2068c5966cb14b2227fe7d3230382c77373067d8a7b9722b96daaaa8bdfec74086da764ed14e3cca927d49cea740740ce2be1d8272d9df328e54852b2fa739a8ea0043989fee207f9d137581422b9cc0dd94d0c6b42227e0919e6177e47720c9dffe6bb5f449daec1f1083d10e79efb2b76f97c558b3a2a949d7c988155bbd81c6e95d7ad0c0a58dabf18d551ddad942884b482f98c443b97ca976f236afd03a1bed1231daceafc9c4255ba20a0fcec057da803c8dce48f89d4e70c474786774c413bda8092c8659d45ff613e07417de6449076e5cbae3f7f17ddc4be5a64d7017592f915905da6d858addc4ea27660f5afeb33b57958a05384dcb52200c787caf2981f52d0ab2b5b014b9e73a0d635fe467c02d8b88af43d2636cb40c59de05be1026dfd97f8b180b4bd2cf7f3246daaf68fbf8725ca605c6b55e1b4ff4de7b08b34b3773fdbc40e36893d7dfc43dfa9033a3f26981365d1a2806f7365b4867d83208b16d2e8aecaf6251b9c988a31fc09e8f93970ce909c4dae8bc1bff691a7b9656e73d42fa270731d562c3189560ed8b3f5a07a47ca3c5421d60425960265b92514d530264bd9be60cf0df55a69aab6b7112c93b9885e8fca6f08b9806fe324c8e30615d4ed7908d0d369394ff0757f23f49742e6a8c80f8cbc12eac380eb7b539925ff830d0d59a384e0de84ba346f4f5bf6533f8bbd4a57a2124cd03d3a5e22cbf8f0943521d25f106f72ed826a1a21e6e7eda46cae5e3d0851e08bfdf92e50afb9b038a8577f87345211c1d9fb6c5b32760569489e1a09856263aad81f4e2ebd97394c8fc4c498c7eab877378e5438c02216e3c845e76617bf54df33d854265347ddb643766f6323e5a7bb38693c2d9ac0aa9aded9be4cbcf5e7637af211675236ff646528f3ff45df438f643a5d4fcea14a050807e92b3ef8188e0e282445b851eaf69cc4ab98ab0befb0252451b330792ab2fde9f726f2401e0fb9fd20fb4bec345b5abb5f0571830eed1b5f9cd8a9130d73e50550fc2d62e1f17067db0ca54eab3f957614989561b8b6f93f87476ee9f2b4bd70b0856f45f0f30c3e2bcb0dd9ef1ffdec2633c20a47e1c7ebdfbc8e07f74f8f7f44e9b050aee642ac2fc24ca85cb5f3aba58d3f8641226cbc2e65ae12ccb070644b6b9ca6be7d9ac6eb523ec37d4dc78191b146a76a51a7808138851a3189eb07baee639a8a817feee3651db51d4cc472963bf4db6a4eb4689c056f9e348a7612d2cfa1a8a04f33faa962849992e868874c65c07eed2ef4075820ea47bee9850c9f2c134aff4e22adfc0c62617f684ef9b2ac74fe8d61f8e75215f13ba7ab5d6a8d6014f255f259a048faec9408654a0c65dc9ab8fce48f5c9ddfbccc3713ec270e63f5a0079f0e9bd65a6ede7f9caf20e42ff65ffe2fc77e47b17f173492331f44302829f31e827db0aec189db8d2973c6eb532b4d3833595f0561a761641aba4f08ae477b71ca447e7cd7d7a58c1e4429760034f96fbc2199af32cb1a8cf9f8bba627c27844c74bddcc58366cb23fd906746172b2bfcf164f39c384593364d5455500ba1841550c05b7e922133f2e1c22ea80bb9b7a579337c74f6d08104b247bab82f74598f0551aff0b831f7f0c67cfc2c0e812428e92b5585d67db0ecb6166c79966d7fc1fdb780577f70cb127b4b2deccdc37e3246bcbf3d893caaa52c260b13c12428971b7dd452311ae10a5fc403814750759008a9d263244c9f5f403b09ca1e840596ace9c59206161f1eb65c0936b877ec56bee6c6d107aef566fdbd2d877cb07b2db269d9d0abc45332569b9a558c00fa18bfe8b8ca1ff4579c5c88ee5c929feb0b4e734fe3d7b93227f39584ef4733632d734687ec043281ed0fa4a96a895b7125e7a08f834f271e25e8fe4e824551dacfdd1a9785af97518d320ccbacb5fff125428bbda03a1f6a17d2729c1fab6939a490e458cda6202028e1dfdcfb827bc3da93506e59163aa09a4d675687852a14704f915e90b76dfbb7993e6ed0e4b391d624808b6c6e273d896e78155989c20dbc20e03e2255a58cb3995091009e02664e410f6ef4c85620018482881b1239416752d1c16e9572c701c7cbdafde0154b37d6edc268c875df382efeeba240f0ebf0b9a3d6990ed3a2efda2ee67d465b35fa7016117cdeb0eed18d73f466f3db6efcc2cdaa79c4f3c7c48099d563430e091c490d4c26827faa3496f5755cfaf54d546191753aa8baf91129e95eb36f555ffa285fe633a57845bcbcae9211a5a61308860a9a0d86a5a8e8cc209e84a546e79c8c163c47431aad5429e229c827393f99699e3eb74e36be71cc24e1b38cd65ed5a1a5d1e7f65979145cf766debb384fd2beeceb35af998d340ba4a6f70a6c60870b78c930be382830ec09866ed442a880c586d3b2926619db208fa07b30ab32622d8b2dd402952b8b58c15c9c568b938ecc65751d3cc947bfa97706c1742ac5a01d5f8af7237b2e143aea861a3ea4cfe042c6777b40eea3658556d241d4aa1bd5e1ebce6c11dedbf58691ab32abb330ab0cba88d322c6c7da346b4dba9ba9caade8cc4c02bd43f08ba89f65128841d8bb8ea19b04fd919e56706d0d746797cc9e78e035b0e0b98ed44f0fa92d979b0bd2944c76119cbf2b013d57513cf55917bdd6729ccce4bf899c36d4a8eb2dfd63f94dd00bd70c271b6d666a0b34fbcc901df0bbae4b45b5cb237d9ebcf370c2b30df3f1ac9f85ca78e1cfd9297b6bf889c5066c0c3d57e00149a97d742c7bac261bd32712cc66137889f963ec70a318c1f8a41c3a64797fe598cc0eec5ebd2246de12add8e20149324c030a14611ce8dfc395e60d8b8f978974d04d81e7e0b5d50d1bfdeae3c2fff0e650b0de3ebe4dfa912c26f5a326949abcd318e6a80d4060e5188c6abbe57f0d6752bcac0af44c03d255c57ec726ca0b1068d61bc2eb3f819c6bba78ed8adce5a99b4095fd263ac26dc727c2c2227c94c4ed3e3bd532eeb617fb747bf7d64356fbcd29ce1aec243a36a27f89f206fdda20e0932e5f2526c08fe50120a6741db5c266dd03e53a58157960368d2fb9584c4590aa8635e02a784b18e6c8cd6f4be3d0a809ba9577e213c6111e3f8550c7d569439fe4d8cd8ca2ffa3948ccca0c9b19e5311b78b4dc6a46a8a9c9edb3744c0f08ae23d4f5941753809e1c3d5b6fdc03cf4a50dac4f953fc55718ead7c59055234ce67014ea2cc5ba158d6c4e6ad451f2f4c5271c36011caf04bd030fde81a11ae53116a9a426ea0191677cb282a1834ca9dd22e32a28bf04ef2bc6b584aab1db55a039d2adecda25a718b10fde7f744ac856e56eaa3eaf465fd88e71a9f41c26f35ac32ece64fcb239653e01b945bc1e5b0fa6ca0c53cfe686b606a010f92a4ea65872bbaef2a47cf9b794895e31f91489bb3124ad69383b66491e60a33abe19a92d9378c7f3d688748e848bf728efdd18effd42ae41dfcffc59f98c66c759e208975658ba49137c238948576154d8968db1402e160331cc765576eb8f6dd5784833970e069fb452966dcdb7aad3b98e5f8c730be8a2311e807534ed0d508987addea7d8b51d6e1329bd7976896f2eee1c54722db8bfc635a9f07a637d40cc81185cd25fa695a08462ef55429e08ad21f033a638eacbab7b77edb2a177c0d746576c40e22f41cfe0cfe4fdcc9f4021b06c340767cd4abd882fc9d26d6e895e431ffd5987906a7342eed68359d4175cc6536b1f2864e3e131be49f50e8dd3107b9a65703960b5579bd1eff61671bbb0e72380dda9dfe18874f1d20d119d70ec570deb3879575f7bbdca6bda3d85534c964b95f94aa7e638778d1b9132883725921a1584790c8a621ea98dfd7a73f5fc21d1df52e07968f9a3db703264a3b778d537855cca1533738416478768b679403b2e80b9c6669c6a962392392ede68b7d49bef2bb2b9c8552ca55d5ef9f8f3482fc0e54746d0efef18f4ad7e05df6798e886e239bc657c16ecd848e11caa4eb799bd35d3dea8d68a3cdb8de17ff805765b366b9c67617978c6f9f9c53441c3643c48fd5f081524f04945587eb57c2ae6ae17aee1e1ef213ffc8349a383f421989b04e4bcffca87e62b18839cff6342b5df1c486aded58253130d9355851755e1059420db76c0d24c89ede19757b27d0e4abd1bb9902ad9d4605479029bd25e7fc55edd3280e8596cd8f3164e1376fdeaf6f4bb1766f87b502ff58856b13d2ae0a3a384c899183f611b82a81d3f48439ae02087d1eaed1657c6b595287419280ad87a4231e144709deb39b5b2bee1f163f6c7cc72202e076cee2dfd958b2c5f4b52a00161e3b802ff48064176a0cafa1e1cc247b01ec6d753635a45373314380e5689265b02653a2d0e131c209d6bb124a06e64fd0dc8aabba4292259c4bff87c2efd6c051e29cb963de012d449f563adfbdd7bdd534f5a64668e95455aabf7094a0646bfa27e6f4d8f28616dfc04a38166cdb302b74ac1331beb73a0340e4492577e638f5b883b1f1f5dd6b98a5ca56c1bb92ea93a5fab08ac82b8679d83aebc2e6e2136730a51bd3dddec7877be936637c74b182656e5879db81d96eddace4f06b47f01b416a2e7d8d813105fb1afcd8b595e312d67237c9ea874cafab63fb07ec4b226d2ca460be0ff2cf25cdf63b4fc1b66d6c16b7122305f51dea1d8324851a925127d0253c4ec89ff4c999d7533df9cbf4135e45ed3386ca73fcb95617dea49851e26ccf1a549d9f81b13209ba45a6e6f6f0b2deb6cd47e6499469bf47eeb7dc7d61dcddafc9e13e61aab0f20c8fd870f0f658809c5a4ee49b24b81c3d4d6120fde71449a80551b18fa6cfc5a3375b6420848a9f1f049912ef215f48afaa97944a9a4e2cbb84e41f58d406b783e6d4907f33f10528412d768d8885e89bca05adede6a422aa2e08291f4500ac5158044c5c57a1ea9d8bf323249a58523c17105accad3ebdde949b4b89c19f3f7df7733e333e94bc161793fa44aa2289b60d1a3ff98d9886f7d1536417bb528463cb55de19a23946ef0c7f8994d54a38c7509fc9a7695102c59e52ff1bd86ec4ac3b1702ee1f935b38d2dc7d0a1db53594be9b5372da8b5a137d8af4cd369babaf6a638b277c3dde0331cdb7934ce96b9f1ca1a8302a024826512c726bf4a8e73b926e1bc855460b7754c47387c3a2a467f896b47301e80b743d1bbb5beb38de7c823f45db3aeeb7e1c379973985464814b52ab4b3a8775b24f3596d7dee7085d586ce267cdf985410733bb7a356ce34b069e6535e08d04ca696e01cfcb3b86d33a7f70fc8d548592448fac239ea83ce4de6fea04fa23d8d38ba98cfe366a1ae3fe7f200322d719653a8e7d4dddd6938e1ce4a66402febcd03dcba10c48c423f82d4ec9a2c737962e5be2bef7a0899715005652fcdcf529aea8bbf5238b9ee4e1c53556507f0643a1a9f01c3d510ee215d3518c302eee940e5e1a0ef351efca9e9c4439836c954e3b723b36859062249e66d64e96ed8ed316b64d3aef3f4e2d934ad114e680ea1d1091815d5673ad3e23d0f3781b4f69a7cf56438c2e9f93a29d01844689884729021300b8883604a2d97860779c6f125443d11a55f1a6bff9ce73baef69481f08f66398dcf41cea1c2d2aec7402d490f71af427d15862738c95bd8c9edde36d3301d54f33419e44fdfa9301c54e23f846f5d5a94d03814e12d1c74d1a85b61844bddf28c60986a87e3eb0775e288025665e30d952e8b100e1c3b3e54ef1c6acf20955b861fa5baedb3582d80c488636e309e6dbe95f334da443d83bc8d42c610a733f91a3cf1784f090b44155c39656f1d15039f74cf8c1e257b6dd89884baffc563c7fce8ae7df4b315885f3adb92d5af4eacf336ba275420824e26462e6af57d8c55698e9f1ea0492e4f5852025fa69fbb9011bd652f2554fd0d5801188303b9c4919f98701e8d99342226f9f1993d78202719546810a2ca87e3a4e7f57f88ac4371bc22e270b6df3b99f57abfddcd028125548410e9260e5a3e33d12c21b0184a65953bf4de6befa0a039bdb1a078bc0036c547558af6420a79e57530643cf70638cfdc5c36f6f7a5ac33da473b6c8f66aea5f2fdde353b1e7a95104e07ecdf35d1a66b96ca53e7f050c3e187684a03d2f754b29c07f6ded5ad9451f81c41d32fde6c9e01e5c51363ced97157cd58ddf92578bdc8d43f214e6e7fbaa1bccd579730b39ab7db66b1baae902935ceae1e60940efaae2855caf0d446d70d75a58ae803019af90dc0839a1ba6f4cf75d0d8adb1e1fdca4121fd5789e75c2f4d1133374d3a24c202b11d6666b4d2f089c6a24fe9585a8db899691b68c7cafcfb7171c402840d548ac42d14ab9d26c6533266a17035ef9d41d356bd678d1174af5e66c9985fd895a3cdf89d52b063c95b2218bc50a8db47f557ce13f70188c7241e15eecabedeef31e500c217d51c0636d87909dd34e79afd171ed44d462592ba106510c2dd79d3cdba83878872418d77ae930da0ff42df47f5a1cce59d454b4129161de201d292d09181fd18be010de18bd09e97da1cef6480480d1d6343db36e86fea147fe6013c2b5d5ce2a2afb8c3c5316386785999cb51fb95797e7c4bd83b92ce9d42750dedb10b3eb2b07207578ff15aeb6fd1f6c51b15cf22a5409a221b2194ce103198ece25b8ccd2113b4f16fdbd9fd9a06c14920f89fc624b78c6348ba8f2ea5e50c5d106e56719ebb6061c4b8696bd94698c15e902f7520e7000d279acf2e8ed93dc4062217fd0e0e4f512114c7efea979bd35edd61613ccca0e205961fed996403cc319e0e294f8126fa61181093aa4c208f58493cc2a42f88eff72a880a62cb903600ac6744be7fae2b726e03ee8c5beac5397c4de1d9dec5fa4f22e25e8574029c56cbb2083f7937d458b9515fe2e39bae91acab8283e12ff09c6a42d4300ccdecd679c575f35e4fa35ee5b5765e8a2f6dc71af8ccdda356dfc6fb73fa558308f4452fb3f916f7dde063e119c76a30f492eeb3ffe81aa6a622e26bbff866443089cd4302ea76872a67779a1135eb1d91c206c5b51e987d1d528ea2a70cacbea4423edd65847c80c69bbabd3f61c8af1028bb518e2cdacdd4bca54817b86936be09d91e5fa4013387a77e308ad339c88936c6487caed3c5f830738386be792fca62586ea2cd7d65feaf67d5b787e19010840945fad40498bcdb6cd44f7180648ef7c521de73f87fde30a6a36a5659a982ead4dd91cfd70d419723cf9d2878e4f9e7b649dd97c15ccbc4895f3e49ceead1b52761d417ab73712bcdafeea2bbe1fa5c446a7d2599b003ca57d374b337771c9c5bb0f66e518b6b9c7cc673475d2b72626cf4cefc3b325bfc725d0df99345852b890e47840828b5dbdfd14aabeb1c52b3c15715b85eab49c92c8e07f96a4d2766916af152c3cc3d669cc4cacc09bc57dc189c1a19d12e381d9845fea123268cea73ab62bdf6ceabb51284ac84ef41a315b8797767f3dc588a640e0eb5527a75a11fda51fd32c38747e945203c873f8de57086cfc327bbe99bc4fa015c7ce3e3ab0530932b93238be7c75b1e77a3319cc85908e47172ed69148f52423346f10c4c2ccce90504b9c6927c3622ae608da329f51aa3ea540b464c74258945f6d6ccce628c77594f7e44ff07498e29d8fd54bd86f77af9093531f85462c6ea6f04027ea985ae1ae0db02327a1f0bf0e0f16d6e456d3bef5270f6ff0390253dc7fb4530764761c1751358c14f85fd322b6194aa67fd12ab2068d8efaa8d0ee458e5f7772f365c88f78e4926601318947096449250350e224c402af2cba5854c692dae3b93a7dc80f887e1e5169a3882c5e5daaa8e05d092c601627cc349bd10f6e4e617e221cf5e2026544b962d821b69c47d139f75a65e1af89f309d52a8a3b0ff2e39a25247bc6566dd2589d3b9669003c2aaae9041ea7f19a1bb92cd42c1e3d1c753806c0bc2e367bcf13108629a4412352f1fd2a28e5e6191de0d14f19f5729f8496ab67d9b066cb69b782818cbadebd60aca3a26440d17f35631bb581ff272053965e4148dfc6e15811bae26584272a755cca23bba1377a20bd35ae07de440c46351f55fe06818668a30059bed40d20ea2aec936bfea950a1de2fdbfbee5d1bd8ec833f97621abbb890761a65361da83de09efcf14bb07ea352ce18ffe4b50323d61e08758c7863ad6ef00bdc71c91d63c6bafd99f921538beac9662332d3cc7e766b3c470b1d31980027a115b8ee4ab0930fb73da71d75ad9d9918a83e2ccd35c0a9f7600878277af59c5e52ba80852c80f2d920fde46f412ea4ff3dfb0cc6e8a5008a7dd5dc4124e2731aa06030436f16d7fe7abf6c3f6fac57ebc7297f96c3251f9810fbd94ae7674d551ee5018afc32f53d7b4314d376eec62d6d49323c20c2a4391c8ffbd02d9879130402d70d51e0aca0a3a086f929a265a89e8b091929505057c8664cb755e4da926839cc2149c6dbb6d412bed24cabc72f9371f632b6eb26653442df3808db19811b2d60003121460ffa0412c878c03d40f21135ae46027e5bc889fe537de8d5b5332d8a40535e8ce16a8d59e839da2ddba1f888803141676b86a0c899702c087d7b5a7b28dd3e2a5d4f98da5eba1a64854ead33ef745aede5cbab6a45f7aced600f44d0a80044d949932d9af53432e54ba33e66db11aed9be41ff1d7dfd9db4dcb79531a845c6635666f1f740a837f2876f07d098f413420a87e6788fa97046e8507620bdafd534e0e345ddc95a92c0b2b01a717930292f3e1ba1eb9e2576f0157919fb1706d781db2205b3c059e9888e799c81224c5526e31cd2885d6c25866c76d0c5a3f9bfdc1e7f78169ea15f38928d37415c26970253676de8eb33ebb62dc44336680392d1078daeff8c85ff0537f9ed7f4edeb8e8ed8d70c447ef5477c643e79bae8407d1fe45585827888b67064c2a74dc40359a35c40c7c660d97def845cf0c122316c3aad26aa13f912066cbab369c865430d4db8ebb23af30ef8ba1287aa881899cbb13e040731b22f3ff3a27850680641e2d96bb9ccc4bc5dc5ec6e08255c1becb6025d81158d4c487df3827dd28a4c18033a5acc42ebecdc8c35c157edf9d8e040b932cf563fef43425f9816e7702deaff1703382d869e554f67d6a0eb6eba17dcd2d630ae60a7599a0e8f86855873753f0e9289a812098559fe08b69db10361f3704b8e3c5f2cf21a7042d98cb04fb71b56bd465382cd840cc72ea1752ce37d16773ae340a1ee8e5503eec633c5dfbe98521b84cd32a10af8ad0c486505fb5db3028e77eb5033e26ab1d35c6b3902055d5bd5908921464f37ff61d0431f97742cdf8e584e58fd9c6a0f05368d26eae4f5c21a638e5119ababac981c42ebb675e71914d591b150a4583a7f90595faa0f9ee1f1570c0b855cf008df821a3bfadc328a10e986ac68c9d7d99bac9b23f09c9338bfb042cbf503c6568128b7ceb4e1a41c7a6427830eed07787a58c5f9bd5031ce205d44c3fe0e0f3ed1551cc0bec648cf10a6cc25644817e98aec4a5f37a965ad58d40a35ebeb38119e4b08705b42160c00cc6e8ecb0051e8d81d2c8d8897aa09f3101a17553bd4637007b000d78947fe94043b2e81c994bef07a4d9d302325fcbd34fd1a9e64eba91b4a630dd6e4272f3a8f792d3b8a3953fee9b000b38565d7d5b33ef523c0d8f2dbdda9c368af819adf2601149d33d48c1690e754b85cea41ae8c33b410a769d7c5d98d75d2a7d75280f6ad2bbba5a5b8f8bc0335ef0c6da524feaeba26b18fc453b71e6e0d1851391d7cbcb0a4736dd55246ff8a52cc8fc0a4b022b4982c945d5133a461e600d82b98bb8565383335befd078cdcff6aac0f4ebbda71057c3a1dd43e05d134b5ea3f82dbc479d5a10d2bb2b37510d26cbd20f73b6484640ee14bce97ba92ea02e570195506d9b34604319d23cc1224dfb0964af155b03ff750cfe834b4aa84c164b6d36939c2f37f1b32d0f845ac420ce20e9869fbeb900c9f9fb65fd5d8b68f6d0a8fd9d6d4f675603a8be170a4d0ed12889597467f5e346aae25165d002b55b55b5c7ab0e44c9bdd1fd699556c10daab12b046ae74934193f1eddfd39257a1449a131108f8bf41e0a0c587795cb2c170a59d9618501f2168e395a7ef093138dc510c6ef7b73fd6e61a2bf70025b02dbc24b5ae857cc696d6469edaeb9f026e84bf73677053437240992bfc21d238c29178c10232f7e87352c553ee38e46a419cfcce0f5f2a9a08f0096f2c54678f6e4a074f6bc3ddb44e6e58f9bfa2e4ededcdce47b6be8d397dbc52886d73ddb17c67226f269d41e961929b2cca200afcd29ff3b0dba1233e931288513685e2ecf0bb71be333cd876a877ed2b65be0780a5d54117d1838a566a223adc7f76c53caa9877021e67d9be47f14824464ee76ff43e7082033abf0f81c917cd529df8c4c16863d64b194006aab7d1a0360771dd4b7d02a5ac177af6d2fadbea4d80cb6742bb5d7ccd887c0f41733b74fd2140fef0f8c563e238fb2cc01ce663a45764c83dfe658f4761c2ea535e714b2ef00c86a97227ca07a73bd5a802987ede6d9b96ab761ee4d71b312d87b0a8323ce8935bd9538424484b0c685cb254220f21a44705a969ae557bffd1b65ce5db52774c376acdebcd7e2f8e17884c1d63a55b74e39488d5b2a4321395c70dc514544b3834fd141b06793179f2d54834b52d4ee16a65f8dc5d9dba3fb4c83f9efed1320e5f44192bd6e74b261cb6f7c735c0e3ecf0be617b399196013e37e204d73a20721d768ea39004968b32cfbcc2448fad48595181767e7644d04629b61a16ca7026ff06e15b18279f83c5fd8ef5f49cbe75ea62d65331faac9c58cb9c615d3e3fc491bbecb9770371e4f61d19861efa900b73fd25883841724c0e9d6294bebfb8d94621b8f5db3cd36c2f3ece89cf7ca5063226756fb52c637fcbec0e63ca19a21821459dfe073096e6fec5ccf130243a67a7dc2226f8ccf5989c25f18f1216e06654db6648ef0719436a81864270a7ee3e3487ca03a5e0a1845c17af7831d6de86858931e870a470a7fecc48d3e456694dfd09466012c52c448d139b5d54a67c61cb8f84242e8362d921122862b0da21d0e0fe12f5e0f76976f57d52ef32a2b3806fe196aac320d5b2f3310be299f6d20b68a98dff84e4e1d70358b0031d5311520693fed02e7820c014fa39bd3a40a3b3f4d55d306114f7310293502fcf72020700d54f6aecfd23108016a4146268febc3ba88aef8ae2fc8cc7116cb0df1b29febe787c3acd1480f9d14e14409f37bdc1254d3c61044eb57d2c5be7a607bd561a07cda5bc21e06f358c9046c375ae85694a9249a45dc4dc4c82d7a3eee044f9c1c78b90e375b98a22a9ef20717f826b5f78183e92ed252c243306f8afb5fe72dc1db2f3b284e52cf54d496a4ff4f3f5297c95851896fcc3ff7b003cb0a3a449c036275c2b9cad78877655e8a8a600b15b30e0185c4c1897b51fadc0577d2ba0ea4aa976f50853062c226e614c747cb5da4b414b88e32488b6be38638305721050a0fe4ce5672952f65eb960b4b5f190e510609d6acfe3e6091b5db150edfdb2085392d6b152a83e4cf0e8e44a957deb483c7544b6c682dd6330e6ad03731c717406291776982d900dab8596a3738244ca5f84e54c06bc6dedaf02e2decd0fd74c5beac14d3c751e0e7c87558fb72a6ff3b7aa9bcc27f69670b0b623524119eb718c527b205cf59f7f32fb0e4fea969ee7a6dde04c7964bcce49ad23c74fc546d5216f2ba9ddcf483e9f734f09c27f3c4bf07b3042397420366fcef0981246c71c1edf6f7096db51fa8529e95a337cf4d3e1ef4e1cdfef0f8ebbeac37042a4a296cf2eecbf01c3ae85ad66481f98ada53c8fc8832be6329357665faeb2e0f5a45d28f8c4f93c3d548afb1b0b2d855fb3a2f3e6759b36c41c6382880117f00f7be7efe00c06bc6d15984f972ed8641c8e7414b8266626c476d92a6b22962a71f4d200279b21f37246dbcde03d3b4810853cc765dafa6b28356cf06e4a144210397e6a3ec1fee9bc6e8c187f577c7e9e0ad1fad93bfd53e31f75ceb8dc77c33a5f6e86401fc0e5adf636bf64c84430b3a13446e39d8440b451c75c9dfa007b12c6eecf0e1e3f2c084e9c6a009b073506684c7be61b1245b2e48fa00fe21889652076d6112043455f8cd3e4b6d953496ec3e256d91f737baf2aff3f936c7b20e4a22a4f0a01be6239b48a5d813257a705ba97a2cadeea8683803c0f2b7d36c015a702cba604c83ec89147ba4521a78395ffd98b48631af434626b3b53cd15c7fc382085549825e822f8c45b39dc08d438792725e0e05edc1d1c910b73ca5ed49d8ec0eb7cdc3ec6b8b2b36778548d4afb59d7036b8ebd0e4e753c918d4ccd24987cee44a0a4cd55433765d11aaecb409bb74f56542fb18181e3488073889740b875628608d45984a511935d849e2be742fbb2abec5c104fe3ebbbbfe650b2a02ba1cbe950a69da179fa32265c6a3d5d8181a3d719c4a56291573576660de44237a2ce71acd743d9a7115fa836edb65c8735244433d816cff91105b3c709dc283f28d36ef32030528666d6a023df1c21d696cd0b1eaa27ca62b12b0c58c00b156d859e83ff73c2fec03f6e86c28922a385432d8e43c1a90e8e213f7ec58a51f02b000edff34864931aca89b663c759830bc6925d4b4b97fec86cd88815b917caa727acbec2b27781ccd67ed02c40f2168f15d3c6920690e0800bd04222b0158a7751c133d659908ba588bb76ea7c891864be83581c941e0a48d03546fd0fa30ce4376057ac090a0ff922aa354df387847b4780db24229a3a5e2030de986ad2624ce2e12a2b10021e10e5111787d852bb9a0d869443c32e4f04534131df816be1f372040ebe3515c26ce6aaf5beaabd7eb754c2eb8e949b0c00633462db451699e365bc5f78d41e80becace37d89ea68c347c7ec7fb1c3462f6927259c569b16322f963b5713ab846df642f18bc09c327b4704debcfd770e7c8de20a972242d8c4723ea5ce8ba2a22d7c75fbeea8ddf922a602accaa02c1efc16ee26f7c697d3291d51e4044eca9e33c2dbe58b405c07eec0cc792a09ff70dd3e9be5c4a647267da50c11a062941a0313b48489a9e8c5a370124ca1f8809ccd020f30366f8c0c2fd34b61a76c9e73850edc236212280f50e2d83c8836180a5c5b1486d269e41cb6cd9994a029013d41d174c1e845b143daf516e19819951617d6c2158ac9f02c06330077371e11c105d6ac4d0d70986319407529c751562404d0a96329f54e40caf3828e835782a3d0abfdda192f4c034620703b0061da52547ee55c8d92805a4246e4414886824c5542c5ce4e4085ee4a63dd552207c0e08ed08c53dc011c8fd528968453c08f7764308bb3a4674d07c1fbfded93443e8f6f437d609842970e85870dc29dcef3d86535f97a807eb4061e3a9b04c0c58a27c002d87c00e0bffd42d5d1cbbae1de7f30382794d96df6cdfef0c7cf367e214be0ae9f91b4555d0033926c5d16c38021fbf78777440773251007f38e65f33eed6a4b989214c177f0f5a3753fcfb8271af3a26b8e3336c959101844ebf9e93e2cb64594767fd009820d6b4ad3137f8d345fa17f76952fc7cd2dc6345b9328132747bd8c22bdd9f94208f9e87bc7c8ba3b6070f871dda3fc1377f9cc73bdafa01ce36ddef82d5d8fd450048f85ea4d67b332c0817857c9a42a1452f50c4bfdaae5f28741f7dbc2a86afa45decd5fdddb00cb6eb8f2ba112eb8dad23905b3b7a6eb09cbca5bbdf5787af74c7eb8135f4b5f4ccdd1c1f16e65c2d8213fa41c23522b12fba916d245ede2485a6f663426f39d0bb05ce89effbecc1550aa03c634150bc8ccf467717518fa6d0895f04f1ada8e14eff58900ae76bf727b341f2e570ea9243f04688f9e542d52e51bfabb51eac7c1a3349bd0b133aa242e30d76fc075d04f38cc32210412adda87fa5777217d3c708cef973d034ddf406e4a1c87f36c60a1dbf97cfbfdac593865fdd76d550292b040d527872b40e609cf42d12e7b0d3305b8234a9a1c85848b81ff97e8bc0c365bdbcf98a5c451b6c5c7c7a9a6f2b5c2f92c0d4bb630ee50be669a04b3fa499d0dd15c798641639b17ba4ea90992415ee71381fcb3b0a0211d2125b244366d459d963af1b4125a3a3289a6484eb6088525943cc524cd75c50e47b1e0079dbc37c62ae18f868201d5393fe3cf5d2b80a17337a4b86a47ee14c3b7adfab9bfe3599d5c83c4125116737ae8447962410fbbf986290525589ede667cfce59d5c04003bbd378c01246fc41cb139649ad0b21f72f4483d6bbc0070c05a0b4c6e47d3fcef7f25e38b472c32d633ad16c48574960e1873ded5f56df13bddbfc0dfd94e8fdf1a37863028eae2c4716dd6a06ae7072cfabcf11ee94d0ab55a12ff1b451e93a3887ef8fb75f934a06ea15ef00531ba3d642576521836ef85f3a86762fe1e8eda103bc488145bba7e0efae376790a22cd4489391d546c06c3b44513aa97a390e1abb624860572e72f284676ee478cd52b0679063b8a0de77f84c32110e66ba915e227e80a99c6bd4e01f7f83c93aef7133087aaf86f6a7d9164e59502d836f4eefc62f9b7283d2e071cdedecf9087580f521682f17a7bce844c0daced391b92f33ba7508169fdf3db0ac8200c9d7be72c9925888260e2890bbdd3e6008501f5850f28020cf2f13ca7ba0b12512baa4bc400941aca070c90214a640e82d1256f3cc7abeb6aa87a1630cfebff9390f655a6aa408b376abe96fbf63e03f287b74620ccbdf6a0b0c164f790fc307c7af488949aad0d51095f27ecb85d96223d8c0885b16060dfa8aebd7e553f0a5da20b2d423d5833b277422b5b1b37d300e9bf43019d66d5a362b4e9a1ac8578089281a0634d52816f2807ece223dc4548d851c2ed251fe8ea93315b0e79b0b2f738a01bc8c112ec24dd5e82a78dee13e3de273571d361df70cbf9e9bae30a5e56164bee36a76225b235235a5357aaa840727c621f39f2bb9bdafa799ac707224e91cf8aa1344e335388038cf6436b856d74cbccba69f1e6e6e270e36156a051c9e5c5285fcf76ff0240b7eddba9dc993087579c363cf762902cd165800129687413c3412223e6ebf27f4ed9cce709cdc65e87680be5ca7db498a3d2735c5406dcc466f18e61dbd269949932203908985ab8b1923fa5652197c49e63ae7fa75ee23106bc479c23014466c7625b036d3621083661a6b0ebc6a9c4e0ff4939a534642ab03a4f45931057de1c8a0b7c80c1bcf4f5c60c1c5c4faf2c076717623dfa9e104d7365b202ad05e04570ed2d5c6011e5b617f723607830ae0e173f6a04f2ae3b063a6e22d3787632ca659a9de8494c57c533ad572a9faeb395ea0322c46c55e039807d4dccac349cee6f0042a793fa5ad5145541c3f2550e4793db2366108655a0707dd1bb4a95720acf28470f3a4e8b389f8206039f297e3583ea3cfadb3c4c8ece3c151fb60ca05236377682b7c12153849612adf9cee155899073083d1507d5f6f444b247e2718a5eb072d51ce4221bbd88aad13e2fc735ae85fc9d3b95ad2cadebea20a1be42767405a134b7bafed124eb912fdfa283359c2f292c7e20d9217f2543503ddf88188bb3f54144c4ebc1d4668cfb96e512af10cc8dbab11f075059435c0847732bec5c37ce4d3df5d91417892c34d4888d68257c247dc761e454223302a805fc16a6192059ed4a3741678a90d9de2cca785d6a94e7ef2fc0a5169439312c130f8737559453e5f9bb1ef8934240758d9a7d824d81aff770d00e80b75b4a6cc1d1349330ca8e8eeed591475cae7b528eea0eafca5a5d8142f4ff5b49e2f02cb5b97b1153b2e7e994f5699c7236ff7d74c87cc2ee990ff91cbc6ef6e5ff8601f640e9191634cf7cfc34900dd1f326b710e6fb05c80e81089718dc4672bf6db5a615e705247d2d14d2bff19711b28a2ca61359cba62911f8a75547fc95f2ef0596f0516b5425bd364c616ef5c022e845f5722cadd93160901c77c1066ea2f163aa537ad7d52aceaa89ce4610cf533aa0f0aa7becd9fdad6994f75a23d9dee921737da9a5ff6bf907b3d44f956cb015661ac3a3cb1b3d34d00772c434ceb7fa8e0ec64f67669f3fc0154b3d1bbcb513d696c03f05e7fa30fe1d5d8a98d0e18f269ccc43809dbd3d462bb16f5f63963ddd8ca970ddbc533908a2cdaef42795e97d8e3d949bc67971a022edd7a742c292791419de353da1dc0fb3a46ee0538126b4ea82730c9b858a36a81ab858bbecba76ea9bfcddc6fef940f5d8b37c1416c8b49fa84dc009d3c94c35a7a420fabe6da89bb0a1745af9fd3194ae65e394f3c4afd4754e6947603d134e3604b8fd693eeba6b727e46ae7f071fcb4a839164fa7bcdaff20166fdc9809ba870a40c0ac8a101cbd83b04b554de2149714d2ecf9d64a9c4dd7e0d6ce3be3875551548042c286edc9ef3eb6ba0a7bac6c08bf7cd52ebd9ebfa9e72a333026b081321846bd292ec471e56e8effd7773b553cc78423ccf543da2b19032c341a73091024d9e50964da077827f45cb17cd58b7079980f1abbd193eeb28e5f3920375730df98320b315c33326b0e68f4d8aa2bb0a1963a0f0d2aac42301a862fb0cbcc07749e698323b4ffec5b4cf688c3345ec74a81d4ef81fc8d7f26e762fd1fc73968636b50fe28c8e2ba22e042e921204ba9e4c26e7718f99fa55bcc37ea5459e09fbfc08e80f6435c63e743dd46a3f26747ef68c232efb00d71efa8a787aa42dc1ad21b08b445dee75e46472fd127d0fbeafa4c1f64a2b5c889e73b0b7ad43cb1d114c3841b2b44e60eebbb586f5552c4dd8ab043a7f9419379ef7a7dd4a4982dce79b38da5a9e9d64152ccb83e2391cd8e349106be97849a03e16587d84471fad128156bd9c27d1951e3acf14cb595cafe654236d97e6262374dae523f48c95a3c48c53f902fe616618c1c121273e49b9662d97afa23ca2d4caebcc5beef74f41d2bc9db5baad601248473b453bcd1cd3dd9f4c457b7a106aac8787a3beb80807574b4766faeda986502874469f9e669a40afe1764a77de64f0de5a4d6c9cab10c5965226a47f79a0aedff1f9a6c839664d85e3359833de2500b443c921e18143162a9af2d2079abfec5e6bd6db1ecd8ac49fae1e2749511f70b68e999797c5da87e63971e4219f319b8ddf96029c7f25caf6946d96b90b72f28be5711ce13228cda7fb1a8af486d3127ba1eaa9ad7be8026be0f76dc24d856f21388f73221e7565eb757a7be29db50273c0b0790fe015f41bf967f6f3e5784b6a161c96af1dc817fb31e7a875d8fba2e2bfc617bdb5c9ccf124c6a73bd87429de4de58f470f6a8789049f03abf0cc882524fcfcf6d497024c8cea894bda0f7106a4b16b26ea5f7b367c77e647aff23ee66e7e88f64e2dfe42072acfdb3e869932601bd7647f92e7d30b8338d38f9f8f8000dcf09de07eb60ff4f6446baed4d92edaa858c8cfd0f7bb18736c6fbce6742fdaffa04bfe816b38fa7b91cdd3fb4ee392f8ecb978f812e0908a7720296218322f87a193f7eb45f5adefe1d91648dea2cc0f41ba9be15bfed6730a6b0de39653bb3fdcc5a51559fdde955f5cbe4a5b410f22fd0b723f0abb604580b93fcff5a176bfa23b2354dae5433353f93df8899d1df2bdf8ead0dce347f01697dd7993a7620d5d99f681c1021f572aa6d201b28f51a5db9addbc3261b75157b11028a0a4eba2eb39ea1e4ee66beb202382fc273f08a9c036d213342ce9cc7e4cb6e45b180518abd7bb648da8136dcaa32cc385f01ab19738aefc2e650810fe85ccfb96718394c2ba6b477620d09481793a72d0a25cdfd7d7a221b445a3df37a16231fa28a53496d8199209173b704ac435a95cbea8fbd8af137ad44fe97cb2dcc64f53102e779aa55dc50c31c5e137ea1b143fc69668fdb0cff5f58df2898590a63255625c35a0087e77cafa4b985fa57cc1ecb31a28efc4cecec891887daef5b87030ca2b98bcde30f1033be0b742f0c5dbce5aed555f96f7f73e588ff99e0687e0011ad4e19ff03995e1de0fdf5d6a8125b72f3725e23b0d74d0791e6577f170e2b766fc0b1c21af2cef24496afdb6161693a0beec234c167e387b413039496bd6ca4eb639249b388b8dc1e9643b2528269a5a72dab96789183642b85021331f8ca2e5dbea8a0408403ff21867010f2adc5861dd3748d9ec6b0dc12c936637cb76467c88b3096320bdee1103127e62c174d2b3e278e64add0b224a57f282b710ad5cb3751b12d97a0fd63fd2e946c3b0283033f667ce58dbcba251dc09d852b779ec11982f65623ec2c292f94e1215467a0ec5a9ce43a47856f87f4687f7ece0fda833fe9675bbe0aaf6d29c27154cad730e65d143f04a094b1b8a49857ab476f47c21bec2665e97b6ae1b3705840358c561c5d957ffadb25178b7ca445bb8908fedb571bb7a68b3cba8bbaaf6dd296ab97a8d360f1e488c3370594dab8cf44bf140253479265da518c2fa3edb68226ff00ecc47e0b09f8f9a4f3c9114f81842dede81b35713b0e4017d5e25aa78f3877dbccca97e7dc782cf5461a6501af7d642dcd80e5a137bccab2cf856c2806dbf92843c9802a34787c40750c17e3c0ad566d762684208abeeae429ac1798bb7ddccc58625508410e32d52996d9077193dca99f971314557d83048080bd8985b8451f872efd90520d47dd51065457881e464b5e46aa38d66158d29d6330609fd809504800ae3a6e2758d37ee91bda0e5c4392b39c8df97b0103db71bb6736301a3f99cd7ca0fa80eee1ab45518096da39488b3d6effc8cde55e1c1283dd602397ca8c47c6363c1025f62439b48e528399101f3919114331a44cba1f2d28994d5e938bb33b1a0c7c7af51aee39db374fc813bf125c5eb28e449c3c1a7ba69e8791055151f067327ae98dc9ee619ecce4ee0e61937564061dd3d828ef233c31206ebfe627145d717a14040e46527de106bc0d1aba04f652b4bdcd0815b2813bd0a4bec65c37c0fe95877a140b5377c606fd1c01ed1944785879e4b52d4a0cbc0fc2f94ddef750636eb4d95c56db9723c2091196dd4c729777c15bf2cf3b210846972f351539dc58c79b69e0828b6c87a742824be34025ae6fc3991a5daf4b50c5faceb4685711cf1a837de51e290ed08ecf77bedf9926ae14b223b95958cb597dbf6b3952dff1c5bbfd1e38daf13cd28e28e049f241de18fd01dfb919a4e603109e0c1e50418e02ceea386d0976e946043432edda3d961433027a9f4e6fcf64e42c99bea8e6b8b8757f23652f2a28a74ddf0d38ae285a9eb9af738cc153397af6db375dbc5de685a81eceec17e63233d24f62c2f025ee421e7351076170eedeb199579597d652030980f1b63bca8fe3c7b6d4e04584e9789868cb0734e41ada04709c661f02e26fa07c3a6d18d4ac4a567faddc60cc2d036f31e4d8f78f18267cb472c48d1181adbb12c21e9da08e41090cdf1abe5c6f1aed92149e72bdf2a961f0af983506cf8796a48b44a3924b65904cc8a43394b0cbab42490eedebbd22675d04c4cdf028ef6ddbfddf2085a573b9e92d82c017fa2eb5f2961b647a41b25fb572a2eae99fd2c686465bfb2078e5f04ca7349623153df895962242981e5a4f63fa4311f4448b71be1f14f1a43dfe415a4c704228915d2c647a6556ca80fb0154e2e34ae1bef641fbe7d426e2ebb9543c5edab5234f9453fc92ed4a5cd058215095b0f8813e1d49c00d262e9e76defc4fca94891e165c3737b96b0db224891b3d4e859253885506084bc35b98a6bca59ea3b979ebd575c436df440f628e03df713c5034877f389c55963e9ae60edb2b0a887abb4462f65fc4db6820dbd681657b48d0d521731d95c212dbe32c2a6b8a2e9a0220dacd3c2a901b3375d55f729a78a3194d8e9fc5273975f86937043e14cc629e8b92aad274811d7daf87674627f8b6212186e564099c9b798b9c45bfe5a4190baa3c9fc5dfae9124fc6d55f79e17b47a8a5c256da4d40508683394174c9ef8b85571c92e4fcdf5efe38e8a4ce45cdfa2a39746e195a8073225d89a893bba3e86801d9b1b7d8eea626862081e3c868fa0e6a1001d6c9e58fc3669aeda4b3b6c289f6ef590d69fc29bdf7ffa3dd2688fc4b78caa091709c22fc487d8d98fec64df00e1207581f946398c5a020fa28b001056a050b247014811847ce93a201caecb3e96c803901106bb3fdd2a44b71b33e330f1bf82217b2fdb7fcbfc0700270e4aca008aaa3371801207ca0812b04f9a2b901b67d336355e8a5d65a46799daac8d3fe497e769a8d2c12f81efe3b29c30cb9cad73c85172f45f90f925aed04521483a62382bccc1f002ad2bfd407ebe08107bd661e86fa7e609b011c82878607b1d345f98b213d2898149442ad76a14923370f9b946a24c0ff67203b03def18f0b19069227d0a2eb31a173eb5fc5b2b868fd536d2648fc525e9fbe660d287ab3de9782ab7e3cd3a517735bdd99bed287a733724192eef29af00ad8e8382ec99f8e8bd03f364d71f9322525249bc810d646871315c1fe9522826749d77d3f220252394fd86eee78da263a74f368be03ceb2efd63bd38e40d137c4eb8b2ab7b161e494777fa6f5d38b79f244232722fa616aa1a57b6f6765a93d93d21e63219c3028b013e6958c5b2356d53992b62f0ef84d9e978142623b4df6b5d3960671e2ed05f564645b77c75bfca019c7debe6e918caab8e8d5c7ba622b8c6d659071199dd9dc553adeeb44294508514315a2448278ab20c48235469110153f3f18ebb5a57a5d655d452917ee4f0d66c0e154da88799361dc8f2ed0c2ec67f0166b8b2928214b946a7b109b375519f4b568c1309a3089aff17c13a46540eb38e77ab1b0c23496308744f1331560fe7de1030975b47f7841a94ee1e45fc5fd3a0ba6c6f4f5accbff57e11d7cfeff5cc0f66a37be75cf5888919233dbf8598f269801d99ec10669359a8500bf750def74387287855f58049b7cdf492fa772abff48041d21d04886ce1ec52d3629b10cd13d3835e85d5238ea16079b67990f1f9b45601f46a28b1bf4af6b6e816fa4bb34d254dc3f50efc0ca1a44a80815ba3613e91d7861f674d19e36cdea646e7eeed5a81b608ae5d9d00bb4a86519e218ba78914ab1d1264a8d8aa0732ec014c64c2b2e690d1ddeb1f84f0d99b6914165dfd5ed421367a588cf0d9b86a59b16cf39e63df9b081a1ceac0c4f0b2247b7483baf33e33a7afc802122219578b02041524cfcbae4b51b835d43f8c74ff1522f67aa5dded3a2b16e01987c4d13d40a7eb4fd893eca59b199b7d07f447e4a35b830a57afc50fd7070ae389b5ab17e5f85510d4250ac7c1fe9cf9cc717c47da41f33ff8319c5ea74d6a4995f05109f7d36972803ff052986a5758cf69626adfde776ee5b7cd408e148f198696f452fc84324f0d422c077f2d3a8270e384a6392b584dd331a679a00d826e84739ce1c23341bbd81deeb131182002856db86a713d56cc3d2dccda0b1681a9efa70e4c5ec8c4e6be8a3e2ca5c9b1758540c1461dbca7546d4571eca2ac1ca6bf9472ad376bc1028302d3ca8502592291b7e56471a46562f6313057ac5d88cb1c3143c373e89dabbe99297f0d5c83fc72c0226a97b0abec6f606b872b150d9c72d92e10cdd1c32e132c02d73c9e131d3999da88ef1b5b60b3a52a04ddee9ef624ba1114538a4b62d68f47099951033089a8444fcdc762a44851428bd4cb4ed0ed53378ded9965073f6cd3041263642e6352591b8240511d69f7b36912d25f97d8383f05881284ee7ec3f0ce56736800eab9ba1f7a6734b8bc9947eaee0279784be1cf9a0dbdab2f4a1bb3ae3317c74cf2983cdd1b317d4828ca93bd8e8a1e98a942f49c7910438df933720e9146f217022ccd238f7ac41e65779f6d4b943aee613b1c80f220194d622e03f201d71cb63b19de0c3ffdbd8ea5b32d24b80b88a93f871982fc4d793c35211d69191367f46184d48693b4be13eda1704a9232acddf70e1fd2847722b6f70358517d27d544a6ec6c59a8bc1ba2bad7d9b99287de6adfe51dc98171be2ce880efd5a2b6b98e36ff2d29c07b22bdafe4df303557a00f233f7e52170cba83a9dafb95f8438b6f66b3140fab3e4fef3c7ab7c49c9cfafca28dd675d7f6796042196981b0d3dad98a512c8d6e2c7b212dc98276389719c1ce6110917f82fe1d9bf9d8dbcc0b6984f1a19d64d4e7b6fe48fa04925335bbf093e97e046e77bbd3546584a336f99ff870246f77c1965b893fd166d55c874a9746e46f78f6f4b42f67b10fabaa4979962bef8f42936ace06687df3a69272ca14b528023b422960cf3a4eda3fc9c0ad0ab33fa99c06730f1a67dc59d8f27ef70c1dc7fe4eb8b1db08df75c639f767d592b83db4c74fdec1b5471d225ba76d1a304c8a89aec907a260ad77ca16b7675600eb4397b1e624fca070b8f4c9d314f0013be2349c27e9b6421e3982dbc5a3a616f1a5b492fd91193ffe421ba935044ff007a67c7bc37521c7c9ee6c5f1fd48aa08ae892ab8287c93429f2453587f22e99eea19df7663eee8bd8990a08f1b33c320e3988f0c7dc6424f7e44b68cc8a726312492bfd3e5e03b5bb2eb7961ea698dc04e2ade31e54ddf344e7c1708a79fe8ca087f261b30875b60ddc398187c18649a21c00f9d8be02c041068080272411fdbace58b7f7f147e05c62f52af80171be1177b8e5a23f685e5b9e60b63a51dfc7aa3e9568ac02ea959e5f437b8b7f1cb63c0b8b16bc93c6b599ae576a2db1e52c043fc59002e20d665de33f46cb87b76c526a9d1f2940480b747b681a7ff0038fb3f1ee75a41e868eaddfa3a7edb4a7ab0ed22afc714e156cb083620742d2639bc911ff6949943bf7b33334e82d89daedd1c4733ddd97bc527f8c435d451fa9161e5cdf0f6d0e2f55fc5a63e1fee85d17a42b3fc4601f0e333a3fd4e6644a9f080e103fd4c5cbbc7f98a366d3a37afe724fb051f0b4974e63ed46667e8d18756c566dcf059201e575a103119dbce2c0827e64df1c4af4d98d0e63627ec4afcb3c455555d93d499237ebd228ab37fd66afba9eaec7950b9f8e2aac030107edfc0dd58bb02151034002d611f5441beb63c856d00433358781cabdf0cf02820adcb3974c01da78a9abe558fba05d1bafa70ca80c9ddb9894636a48503ac4ba48f03f4902ba1a3d290d8233e8f0b2c1b0d0053cb8b6642d59d05abb41f291f67e5958c6698ab2a9b34247f3441be1b3af925d8e43cf8eb960c656f2151c5aeb30c0f0f8bb56b40c3d9d6987568600c7406a28bb5c33a27e463011ba7a121bea49b6f82658cd41f257cc5ba4f3a2868d577419e560c824c34f817f9dfcab906a2378b2f22824e349fb3d76efb369b92838afd315783621b339af912725e710ed73f8701c0f4fef18846a5bde34a883d87424e38f7d8173b2007bb3a3e05170e39306d70283cf1d1c4816e76a1b4739ab466dcdde55a3c162ef658024fc6de82aae6632b9113981f5f16d370c759ba3c258de418bb18dfb82a92d1592bf50c9e8f1c5c1e9b1a6ffd7493ec8c564c9730a364c1b95d2041646872d576a6c438cf9b3f16e3b6fcf9f9b535fa84549e5ad14b4f1727e5e728491d3a014142dd6e0bb5806f27616fc4a149348654debae9d66d1f929f8001c2e3ac1f2e4876ff39a27a6073eabd7ea3f12959cd1513aa655deb76e30349df57c04e0e31beb41e5db6bc5b9e2ddc1c7a88944555f4d1b2182ef6f01764d62deed553ce1daa2a2081e0e403c270b08e5ec2f751461301c6ef902dc287db06b5bfcfdcd11422fd28037f06f6abcb95eb57439264f90350d1a3c5bfc7ce087a5a463e9c7b4c06e76443806f6e4eea81764813a2575712a06d9400ab9cf9c40fcd38680c2e2708d3099dab4f38dc02ef92f3bafd7399e81cd37f312d0fea5e2cbb8c7b372c19ff832179f7e4e82cb1809315183557f6416bdd00c260446d0788e3a476e6c9f3fa0987ed7f422db88dd641129c7ab3d346bdd1e6578e3bd2c6919e3f8ea8ee9804fe7498ddb2c219604c6f84423bf44414845f6e3291e7ec07a9bce420b2c599b6581ee478781e05999ade154fba600b09ac56ac58f02bf557b031d96c80399e385fe4a20faf1097bdf11ddda2af2a03c05c79da881967638b86fa726db9d45c6de52641fb23af8f5e8e03835307569120aab6ec20dbc0502b4e888dfe82bb1603760d3abdac1b880c87401274b2ea3d07ba9d5d1090a6518190e6ff8268ea6544f6170268e712b76be086a1cac3781765b699a37609e7c38764af313678c6336a9b679c1f9e30b47d55464698901663e5dbc8c07e748d785c9eff94ce2f7ce41822af2b543637bb7b8852a09a10df03e72a6fe051fcf92328d2885fffe27158014b8f61a3b445a2ae11fa6af93afae05314cbf1c0bc8eb3bd417a7c5ed4ac6318ef9f6f6db0e0d2efff4cb1e6bc47b4a501eb2b6b589f264bd7a29c21fdb937a01d4631f00e6d26f83506df79101eea636a766fd2e689b3a86b53a6744cb6d3548f827a9096189a4e18a9a6603d4dcf4f5912f8681425f4952b15a44eb6465936a1aa539bd90039177f41b7b62a94f0c527a3a5663916a125aff21c812a29c5dddcce1baa05b52fb5d0b74127fd54887dc3c91eb99401a248f085bd2dcd226ab5f633835213cb880a092ca9011ad362d0c33e6fd3f62a138f3cc10ccd2bbec89113d52313c4135fcab39e7cfd805ae0cd8336cd0ee669cba85c71e9b2dcf4cedac9ae8464e4f79210d197be61c2b0f8596cf98e74ef57468fba320143937844f529f59f29530600ce8abaaee23c945ac486c22a72b2879609763ebc2fd183ac8b442c0c3416cb7d3727a87acc68581814abf771f1407e3e80a03bdea9731375d93bc0b08f00f0f248819b3e9003fb800ff09cf6e7644f6f70c026109d7e2b77cb0dcaf43b4c25f32284ba817a5b96d8e96368675ab3d37184bdc828036de67572f43ba3dac6be6234405ee27d5983ea5df0ced3f0b36bdd90de3202284bfe879ad04e3acab738a7c117af7a50ee130fe31238937f46277014c03f9352797850bc86147c91861b40e7b3d42050b45a1157445835273092b532b8edf49e14af2f6bb0e8b376cbdd4ba684c38dc7248334c1c028145f4068c2aafdf632ef2bf3c224fe06937cb40d7b472d221576636b707aa4ff33f718f7e7c4a1f8c26d685728274cdd483be3d3700df2f3150db086823d7206d153abd7ffd269a8ce7ddd397ec9894d64f98f9a6b287be30eabda0f0cd57dfba01690ddf63c4b77da309f9137611870c870c1105cb5be92b595e4987cc3c7201be160a38070e7fb6afba4484585916e6219a33b06d6adf4b2febbd1b9388e30c638f9531598b522d649b724182bd1eb27f49a3a8ab14cfe923a6ee2862180ca0b9366bb52b4bcd27b64b26410e117e07e60d8ecc9a3c4f9d614a3995a3cda077aa8e89dd00bbd85db9a116ae9cf94bfd769d5e99773c342780cfe6fbee3e51a02cf846406e5c7f60363b7716d3cae2549614a22429e2f1762d8111a913a2494b4bd677c1f102e731b9874d8599366d61f5c819e4a5e03063ab1f0b1fa092068e70d9a5317e6f6485dd15649af64f0d8ddd2f0f0a3db95819e2fe341d487885aea735d97b70070c30250399a4925b80cc090dcb3ee5b9e636a8ab27379f99be53efccaa30dc9164a34e154ed936c44ccb3611284a817dc49239c0d56a794e3d0165b374f274445235c388a479ffe75c722a8971e549e136152ab59174d8c3ecc6a95ddc6e23c5a2683f2a0f170831da296761f2845aa480dd0b23b7115c8eafe73f5d6d3e321f491214b20942d5a6288bc03f5d850948a6bf6b2b526c3dd792dc6594d2bbd8d7780aa55e18ff40f1dcd7ed92473b126bd38b91a44ad27bcfff583395cc402013467c0d168f04b5d8e54c94cd4532b432f56f61489a71c6e7220ff0a2bd588be016f26fecaa769dbdddd02ef666170a348b4fea3bccd29e06393e8dfa8b79abb881b0e59a4c7c99c8764b0fec8b1031146306257b81deeed0e1e04380631c7fba3532bd4a2c5d081156ae6d9e0e433e27f6b08284af9aec7b435fd5d28f661c7bdb52fc8ed1bf5b5af878272a0bcddd14344fea5a766a79268e5de0eabf7777491ab62503f9edd3d1d1a06b0df0dc3cc5ba2bc4a71ebb8ff8121961dd3288db10c418ac463f1cac558cee93d4ea8ed74a5de4d7a58850df6db02b08e1be68f9b6dfe0be2b8bb37a659c223a7d43097c2b79d959469c2951931dc036964fe797824f41878b98873e41af884171efcf9ee08b7fc7a201b4f8c2e836372b0b7de1ceef1eb19ed65c9d9b44e6a07ac4fd532971a1b0a9f2cf6adcc84f7ea3a61e3ff87daf6bf4818c1d080371a579e953dbd79246ed20d18f16b911c7b64764d77894dd7528cedbe0a7daf22fe57d001fe7faf93cec17c35370ecd51c026a822f6a9f70d3538b4e37225b3b2cc092db2b515a357d433f69f7998f967cd13152af1338028c6d6eddaf6b0f864894c50f8b734e7fade6710e37bcf16082a389fde6d4053ff1f32bca773f9e920f883bfd8c5eb9dcbcb6780fb1762ff699c2bee2094e2c12b349e2f796ec954e76eaee1de567b318c98146ffe4ee55361a80371448e16cdb355737cbc78786d97b2a6294e5070baf9f9610b2e739f02de75de943d34920072deec09ee430fe6dc8f388ab8fad05bcee1e97684c1a1ff9808b775e5b03fdef5c851ef0e4e397f81a7ae7cfb5b122e78a68f211fbf7ae3513f7bab37fb226cb4a66403932f2a72f874551f325a94625a495fa46bdcac13e0c3b96a616f6ea7dc33ba8a6acd5a3f9ba804c01a2155a88d30d104fafa35b6f5c520c5836e9b8d33bb6b90276ff734eb8bbc0b09cafe70b17f1c5aa6122c86ca5112139ca4280b49fe74e4ab47ca8b84cc6eb2d37172cd3a6a0ff40bdc34ad073e941d8f3e6a94688cc9d78cd6fc7a78e2056f5515bce46eeb1cf12d33b68285e304361156785f98175133b6fb61711d758504936157d07d96330e532e9efc2bf060de61a074b88f71c5a70ce3e9ba5d50f91d8de77ead800f95f99e0f5864fa73ff7b9b549652c01d4e4b9a618095d5043929be7c5c5e15629096e7f171fc69a1e25ad75b0077ade8a30092817ceae90b1270160b6bdaaddd45d263a020a7c3fb3035da5d757f5245788ab8f02776352e9d152e4beddf5e5915a2383326ee658d026cf55544cecee60ad87fbdf649a75d873948409d41e8720cf4d7dd8e7c5c508e86195c4635c867a8febc37c87558148f9af3d4a30d3367c617e0d67b0c8ae075829d086f9ef19eabf57ee27f5e7a7fc7a182953d851861cd933f19caaefb56d25f556c582bec3328d489b8e24b4cac0471d5e700bc49fc7dcc869d0df631f2415e4d45339c5530a6c3eb0385eaccf67c82c67c2c333a8b41f1a9c6839b4b1b9da52375971d7f1d6b3a6b518511b27e88e04ef208a3e58fc0d8bb9f6a414e6fb2bb83da90f55c3749f446f9781cfba4313a2e505d565cdf023f64e8ff285fb18cb4cbe4bc1d709671b5ad58d15380616d7fb02695d1992efdcb4f5c5a4ec7685e5ac20a994761b392bad8834450f6886dba6c97ada29ff627407812e8fa54496c559a69c259bd64bcf4da29420b643587b67c30bb15dc1f77dc95fcabbc01fea8b525b23954217710b8cc97ee6e09c4e4a542626ad31278fec81bfbdf6d7e18ceb2fd24bdddbcf92fdd6ce7b3b06480ef51c0b790f8f7bcca1baab27dbb630c0dcc407d328538dbfb4f752b5ecb642e67bd587d81b92df18dcdb0d20b927f134ee419fe5cff8e53a098d9ac17ab23a0de9b68b81ecab0f7dc40837d72411cdea15d8a7d0f97a239f19a3150ff5eb751b3d34804b29d71387f6a7d6233b32686187245b7c6286709956279d386807f39c0a3c792081957a4e1030d8a284c5c91860f3428a23071451a3ed0a088c2c41569f84083220a1357a4e1030d8a284c5c91860f3428a23071451a7ebcf10a4e2c71c41dcc18c78b3f5c5ca1c38d1e258668f1068d3c6cbc88c2041434f2b0f1220a1350d0c8c3c68b284c4041230f1b2fa23001058d3c6cbc88c2041434f2b0f1220a1350d0c8c3c68b284c4041230f1b2fa23001058d3c6cbc88c2041434f2b0f1220a1350d0c8c3c68b284c40c10e834b5d2d5fc2cdcd36c6b42dcca639b6a7aff9b7c651b555d414dd9184ea043b1411e2082fb491c61f6e6811c5135668238f375ca822892bdcd0471a2facd044146ff8a18d145738a18a34fe70438b289eb0421b79bce14215495ce1863ed278618526a278c30f6da4b8c20955a4f1871b5a44f18415dac8e30d17aa48e20a77c27a69d2083f68ac076b9c891afc32e32f581d86091f23406441e38d1ee0aad100697bf743d40c5aaa128535d29f4c54dfd7e8e56517a1911e29bb291342bd872b3a8a9280ce131df26410a6aa3c00fd23a0bd9686404fe455bf80b19fc189c27604f477531943ff4ef06b64a1da79b98c3d29b5afbd8cfba4a83fdf4e947c95e19ab40c751080adcf441b9eb28c0217eb4cc4fbe602f0c65fca92149cee96e75a58b95587f06b74165442f9ac4d315cfbdf6c61c7e598daa6dbe0eccb8999c45d5da912b33593b0892e984bc1fd3ed4d8e8c51bd7c985c25dd679fa0e5fa7e77aa2654b5f073c925aa35e5f65621148132b3d0a165dd9323a88d6bf9884289d634cb126d9a8b6d5f32f9877b146d49cf3f26b694b3a82b7d05d9bd3a882ba60cbb34cfa010900ee46a4be3f5bc074a816a092561b6e7b9cd3df0299e552c95b08cd9bfd4dff88190dbe4b839d709ab37ae3d985e0f32606dc3db2f507c73e77fa68640085ba4c0f01e4463183560abd9349419a4dadc8f80e6a7298231afa6ee07919f10a40293c97ddfbc382198270120cd1936e167c70771b12ade1312f546fe6ad2e65eac1455c77882fb82f186f92d09d3f583fe0349cd68d46203b8c267453c2faf5365b597a84f3cac759ce73af4b0d915eb8ec364f9357f72bf113cde8341ae1f78e689f39798e3293dae07ca993539c1b59a2b277fefce4e9a3aa7d6d50904764584c5387b06bca49e46a18a66319969698ddb94b7d40549388c38311c9d3a3e43b64cf23620ea9a31cc5841aeb3ba64df8a405861c9cbbd64a6d24a6d750b77a0188fadb81eb8deabd0713a7eb8d0844605ba07f817b9fd1cfbf0360a0aaac6129af08af6cc4e7942e8bcae849d7cb28f47f502652216fad42699feb53d5a8fb45ced9e21666afd154a3d7752aa46e0e27aaa36fcf69f44d9b4d256fba9289cd564b520fb966d55a18d8b0b25bf88d3c5c72b3fa52bfe8feb74cb82e7c2521230c66c3ef36291544aa758cfaa34953079fecfebfda3ffe77d4b941aed9a2b089101d83f80cdb7f71587965f21fe3784858abc72d8097f90226875aa657590b3eaa3c1539fa8a599ff0eeea66876c42fe315df53e31d13e36d4de56ff86c8901340ebbf171f0cb8922ce1d11750017e92f6efcedebe3a1be14aa70cca437994f50eacfd71de50dc21d1ed1775f8e3b638e09c4de4fa89c417659cc6f70fed7b2baa4464880ad19aa2d252eb517bad1879eeafc980d48d46d3dea00c1475659f7f31c0a41b6e12c15c7e7dc73d69231c742b89e14d96af67b1b2a79840c5fec05b7e4ae53e58946cf0a10381b542b3b3b1e2ccdb989132cd55683fd586f577e67d9b97bd53d0dbd0952ce69177d6f36649cd67e752acfc904f6be5ca2a73b87d97af61bd6c78a164d2c4231c3c59a26c2113b64b1d043969b3c555e786a2714f360fa163176261c34c8c05c2678f936565124ca583568913c8c3a1b37a4b6825be468b2e5798fe63ac337d20933140254d6002c2811e9123803c348e41607189bdf4758b0b2e22bc2a4f5fd09bcb34a502e3fcf193ac123f1b957badbfe88c171294037beeec91ecf489cc96a7d7a1c412e95adcd965a05da47d37ecb2bed66dffac99ede7b0a5a07003fb002e021c3804aeedc4b00e441098459336401606060606060aa13fcf61bc32e134f553e603b56d6d66a95964baade4caee7b7418cf42ca274ae15805307399dc5cd93b4b315d089e3df47241a7ffff77d512680d270d3ff28498ac1f79425b48d248e64ad287a62a9a5bb0aca66119d1c870967349c2d962878c9352efbf96f2ea156bc799e5a50a1648c312c4080bcb0b11160ec8e31d1acdb00f4db5b924e17c40878826c4e1446003ccd9800620908108b0408181085c80021680c0e6890ae8a0c005262081279e90804c0498f3a1cb094da3f9040438cff57b4223281ed01870007332d08096992ae7431c0e2b79ae67ea8b9c01359cc8ceb57d0e67d4e431b0006e6f02399a4c4c46145879ae370a68e6f42b991fca8901793aa2d9d924e0020880774433810330e73b1c4e1579649e304093295381023067fa7062409e9898e90301026c06700101b0e584680301e8d13401c0ce0fe9c0c14365f4311b4861111428cd5927cc014d9c5193dfa134a0050c40a112272f33412a188882139422804a031bd2e89182026506550645063506250615060506f505e505d505c505b505a5059505850575056505e586aa82a2829a8292828a826a4339413541b1a198a096a094a092a0d6504850475046506a106108692480454225a0d94f3983003932d00395030381c891812732b08049e46049420e1618e460194329a550625aa0943a192307060aa0948a018da608c4118524518a176d2895d2821c17f06208392cc0881416a4ac80a382142855d344bd6ca860c9510127e47802e6784225870484fcb04342528e08e488400fa544764837f8b8199d3e3ed89296961491ea99197ac9e1000554114ae96226470380a81c0ce081a806d4c3becc7469998a848b1f79409e0bfa78b6f87c7ad8f7a1548a8c7d2c3e9f1e4aa57c444aa52801d15ca5524e603d4946b58752291e90e7be15a2944af9d13a0ac63c562aa5c755aa59cd0c857a88944aa9541d79110dc9910045b41632b5891dc2b4f80f9d7534b980863c280b50169f0f4d900f9da3c9c57e26218f02cf11a552e62834cd4c215016329ef9995e44ff412617394d2f4aa5503133432f2ffacc201ffba199b668223ff3814c2e4aea446404ea3f748e682617224aea1c1d1149a55292f48001c251293e4ca0944a2981522aa54747343b292468f2a32c944a19410a1529493e1f1a17a552b810a520014d1ea552b6d022258b7f99ee4b8a8f2361b840054a2902a81c04e822070164945238540e0274548a1111289552040ba5525cae50475eea2f69668778da4b0941ca90d5356f45552a45c81c4dab544a15356f85fd783a3f52828c427346a914693d3523cf6786683e45944a01f2d5336921ba221464249a4f64d4a30868765e3e345d61877840592895f223f44a3ca0e90aa5527cc8cfac1e50a7fecb47e48979229fe9a91e4b1382075213a211cd0fbdd4f8502aa58752129a82d4f8b0a01f897e0b23539deab485522954804029191af9a1a9239a3f409ea98e3c3e441f7a218ca49130240d8943f290381288a4a5a5978844dd6c494b4bb3252d2dbd842d9962baa021a2e982864c017a229026344d017a222f335d9acc2be668da293c1c9067869e0e696999406f6526904824b21fcf165348515f0b10cdb525780c4bd046a159a4c64e3e46a1396387d4d8c9c714533493a25916231168fab1e10169a480cd3490a6e695b447633dcd68a6c8818302d633696124241a5d17998ae4c7a8899c219a1e3344d3a30768f2882aa0540a0526a054ca134aa54820254704944a8180130f502ac5010d502a85014a299594052825aa01f5c8a1dac0e28652ea24872203f57c60720420707008a5d4c9247ca0c4471a6edc600f4aa9142e668c2c410e0bd8042494522a6b102109457260a10cae943a29f208186ca10019e8810c94522720b8c108b028430640c88252ea640c445460470887144b504aa110912010c4181b1883124aa9140690d18645ec008d2cbc412905801ee4e08d9338b468820aa5144a202461860a30b10340b8524ac5073001e041070d27e08352ea240eb4c540821a74a10725aa792b54cc904359a552722865a70236a163850f532965084d2829a441a93ef86125133468d18452eaa4109810c4ccc4b4682d38a2903402fd91e9e3e39941a4d104bae2071249643793e8d36323896060ecd7cc4cd1775e364da64cf5c84c508dad53241a792693d13451298ae9f1c0e64a8e39f7984ca1988d4886668b8d0423a281014dd1d789c9683359d09d3a1db043423f43474342313173b339225391d8e9631298500aa5092a654a9129402671c50f245114b9e2079222285ca020c07e17a2c9c403298090841e941403f2cc7c3ca28d8482491492945253ad48e2899548bc11892e2834945291184542044aa99548b0ac4402012b90f8c113a8a0d45879441e4820b5984cf0c0c649017a00b12b8dc0b1c2083056180185525267feadd848452697d1df2ec800420d6a10274a0d4840610d4aa912a8153ea880a6fb403aa22984106f10a2116f509c1542c480109e4d0b21344008012835a2999c502b831083524a14925a5a04a18815410042106968598152aae6ad8809f5b04229b5a294a81541304014926a5e49072cf0a0491492020195529bd1dcd4bc9240a828a5763c4dcd2bd1a1529aa0d40a20c400c4095600b1031017500a3479381d58f1031b56fc50861f9aa89a57a292e20714c5647646d7250b511640642a9219998ae4c8911f1b1e47a4084d413e9ff9e3337fb4b40cc07a262d5a5a5a5a38a0b7d6d3e17866266746246a1e32c5147566a6c7f4d2d2c2b211cdcd0cd1f490628668e60834997ca64c4562a71fb4665e315dd090e9823c1c1ecda610fddfd0e411bd92ea19813c56fc124f8c4c6d62259029d812998aa4a5a59758191a10a70b19eb8634dcd044f5700394dca0949236239a3674a10d6228a55468a2338a9736e8982b6c68031bd2504aadb0c1056c5812882624f2d0e943376b68845292677259431a4aa209ad810a251aadc10925d957c3153e9e0e934d0f35443518600a6930c14a1a90a441493c8e782c688a36303f776e6060ec7b602634e8000d2e6888c019de38c31a671043492f33d5191adbe30c1b38c300cc100533d498610365788392be33ead49f01d13c0fd00e1d9b8f99329e8e6876ca8003253599321634d195321060850c4c20434729a96632097d8cfdcd68f69036224f8ca7230a8946a309a463f3d633863e8c01086310a2241890e70a989929049a36bf64fee7818881116258430c4894924430309b69049a7cfc80f9d01d54c72f999d1d53872486b5094324940a4d3a36d6c3b1a08986a10c150618a5800cd984810aa5442f617880da80c0f08652223080c10d30a00186264a94051838ea993ce80b8d50138f2fb080046ae50b17f0421d9492607e636ba6cc2481bc0086179e179c50eac78866ea00a84a458c74418d2e7456baa002e3919199a6cc688eb8a0038f5ae142145bc803f7d848a0c93312cd1b7a26d367d343ead43923da02932d944029f56a450b6f2805c3440b67ac6881052b5a78805230d368633fd3a8533d9d983993052c64210859200223cdd10402c3e441d36464661a89369e1016b680852a6081060b2c5750c315c670852faee072050a5821134a29098b4d0b8c24a281d944317d642a126966cac0c0b4c04c1fd29481811185a650e847738a21561043a21ed106d4c30a3392158a58c10a4e28a5944c6eb421b9b1e2869d3edc70a248151aa1a42ad461faa8c213ec8b68387688870a6f485470433499ac50a1a3946441930a0f5053e882524a7d56a640869a021647a4200929084149a01acae3ff6e60a6b043663ca39168aa9c8e680a91a9483aa2d991c292421476d80ffd12cfe7edcb44410c1505181b05a844d3f3991b26ef12051728f4c10714dc508ac9bbfc5fce14524c214593c907678a29ded27c3e34c399461cd11390b4d108a5148ca80d15c088be8dd746069c90871618980d1435af0466c786870e988d13de09185869421cfeef86039b4d88c6da1e2b6c4c820d44b02105a5a060c3b2c1030a181111db432925865a61c26785090258421b6076ec586bf10e1d3a1ac986477b6436a20d6c5aa050129a986c441ed1280a29667a96ccc02c218c25185902039840f14c5aa0906038164624648a9729b4d8f0b0420a2580a18417252c40d1242110d2153f908c402f335dd1d2f2c264022581034990c01a605883c91a3ed66824fc01096c200109127e4830a31771361b2573d4d2a26323e9d8d0c8f0268a1d303b9882b0d0b181818181a9a17f6d0cc81303f21ca1115d5047e8718422343147d82849b4e343938c8e4d0d681aa10c1b8fc86261843494149a36fd62842246d8484528c44a11d298403d8a00a3a4cf5750119ce8a1861b4ad1cd6803200f6cd468b2a206154a7d98fc5d51c30922486132e1b1995ca6480312692c218d1da401930601843005214c42d011044c7ca268922409169fcfdc7060c3c3a3040b2c36120cc36c6a3e211e59040106c10140a804108800842e80f08464375180a60d8f1890e707a21fc81f60c00780f0c1193ea04229f523c986039b243ab090fe33977868ba3032e311d1443137437ae0040fa6c08324f040090f1e0f02800615d08841a3a1c1811dec61074bd8c1921d5c400769d0c11374307550801c6c22076de4e073b08133dea014149b3bfdf57c36a291a7258a1f316f5ab0e8c0904d14d28e2331d3470716303a36a3d0df49039bcf9c666a689536473aa3896e78f00061b189428a02346571c4634173a363e31179866c463b746c44517842134c10110c0f984d8b04c381e14d14d763e79c420ad0779884a60d0fce193ad4194ee0c00d25ed004d1e100e6070f08452d28e69740338dcc08b999a91e86f204429c98c4658fa9687c78c36a41df6ea30a30c3360cc28831265aca1a6cf8866e2b129a389928a2855437b94a1431d21a30fd28e90c823b3094df7081965bc4787888c193284281f643cc0a3a40336a8832766c9a6870dd2504a82b1c18c82b101154a493030a21abc51833228c9d3a9818f1a8c60fe872e87c9836ae0040dda504a493c60447336912b34f828698c338c311ac3023340c30cd49881680675062b32c0820caa0c0c20461a144d68a463f34b3c9b69b4d9a163d60d0fd1ec6c3e1f0e68fe679a24a98e7ec383470d68d6d0990fcd78946c66886e78ec80d980fe958838ffa17323868b181788c11e6260460c48a0a4d0c443e4f90f058189014a185008a30461c4309e08438142349dfaf17c3cdf192d99bf858ecde2cf67fedfc9c48271061849c0f00183444818f8c88c3a92b469a1996a3210e3041acd128c60c2c00d18d040492f5860616453437f485f290cda178590449d116892d930194f93285e66f280f962f485902f0cf0023794827951161b0b00912f3d7e4823452410988dc833459e39442412c5cccffd0fdd17086132454bcb8b144d261f1cd1acaf850b4ae082014cc10b8f170b9060603677c8e679d887f9ce0834373c5e36924886a6863c75f3f94c2558c06c5ab0c54a0b10c082386c2c680ab160c4821d2b784329a9e63ba397563082156c54a00615b440053a528089149021056bd01f3b7eec8079d1f4d1d232676aa847e4e9bce8d8483534459e3b44daa1630303b3691981e668339a409f59e38111d1144c2998220505504adac13ab0d844217146d364e4f3594141cc0a0a84d444a2e6073556d43cd1840c4dca9060288d88637fc234435164fe4814e311c1ec108d26509d1e11cd34dad04c35348acf674e90f4a129d580264bf3a2998dfd234d609a40268550aa0c4cd060d2844991256fa8256948a2d99996ccac2c29a2d4121d4ad1bc41c306a594045aa171c0e810a3348c9a8c88280966878e9799441c91471a6dbe33a2d9c0c0ac88b620a2c18a480a991fbdac844c1082d7481771e8628d2e46d0058b9a20310d623a636253004065983262c8b47c06f1c9820e3ea08f90cf0494bc21f3a3d0743d4a9e3841203cb00323adc47821468c98224aa90a9af5c6e1fe60fa41fc10be7e0260ec083447a1694666c85318eb02db01c16ca26891d9706003aa57a8b762800682a6a19464ef047ac933dba0d448b432210c0b604c00c31e1071fe43975387c985b3860232c4852386c481a97925f63909504a12d5bc92cd3492f1bc15268082094860021d25f04309d650a2a7a317896a5ec94a092c40823790800d127041029411b0318215ac8cc00a25e910590fa7e69558d16323895e09c8b3d93112e9d8d47c36a169b3e333473a800cd93c1dd5bff7434c1e643fc411b991448c243049742895840bebe1c06cb848a3c70a1748242e7470a194928e20a98392824242032454483523d1648224015b9441a92dc4e8b105922d12a0b47063870e2dcad0a288524a6551892c8e908588c9df074d51dccf8ba2004d20d1069445162f6d7859432969e5c5e58868762a8d6823fa2c36927d110d68ae1c7143d54ca1d112cfe7081a4a3ad239b2568e38a18cd4611a414329c90812a5404696521268c588134a29490475504aa98d08bc58e219899684685a5a5eec90969617eb298286222a90608a14008b252b5840e102079734e6157990ae98572c267385c80e88fc20b2002bb460c55ba180101c220467483c42208010a80c69c2102e86e8504a59d0ec6c6024212f42282084031b9909d2b181a20a19841241d45809c24510968f58915b905dc88d92a2780f9599926c46a26966a381cd878a6c3c00337598f8b8626e3a95466623ba93911520330022c50f41acfc08c3e7c70224514bcb4f29c1c0d44ca30d10092606e411493b5a5a5e746c9a4c4f6fed7a7010d23fb64bbd4e8e70dee68e7367e461d29ce3670a391184a4cc369cf3ffb1cb56e48e73206464b690bd565eeecdf92020e433eaa637a597724378ff20a3b3d8a275eb5606dde32b4efc2067fce8d6b783975ae62f9ef4413ebb6b1f27337f773e88c9c4b173fa48d2e8632489e5a50a0ecb0fd642922489dae73e38e183e4e891d2e9ba31ee76eb876844d3a772607f26283443c351324d1f6f96972a5844c0f2838508cb0b1196b807d9bf784e46ab7d74dea807e938be775b2f771bd7a21d6716eb342c329e8e485ac2429d8665b524277990eeb9fb966d7cb3365090e6133cc8bafad1d775316bf5417607f9e885d39bf575cdffdd0e927a5c3c5fbbb5e875e69c9f787152075999417aa18dd33d76b3759c41ffe1d45f12d9417649c20972428718db6206bdb5061bbff6e7e082d4215b9741b7d6aadfa9f3c77b422f9f59272292c4f2a3e6ade888a6901f2ccff5c4e4e595cc8f0f49927c00b9c239962284933948d6757a9d3d217bdbe06c434ee420dd7a476ff3f3dfa6efc641ceea9effec576bf5081f1c24a396f9d64aeb648e55fa0649e98b7445eb1ebfe7d65a619cb8415e5ff4b5c7d031576b7b21d2832e49384b4eda20db8cad7f35dafed5cb5cc7f951e0610ebd9ddf617636c8c5d6edbbdcd042d7e87b4fe8a5e93859838c94def80ffadb4be3ad1f510cf568aea01a516b3951037cf0bd86afb1836fb116b9c576b77e65cc4c297bafa541b6bef3a98d6f5d7ff3d5b72e7131b5ee928423c5091a6475ededb69f718d6df90c72326c2dc2efbf7da3d38e33eb2e4ecc20a77363d4dfb3d6bac52e5bc14919e4b767f0bd377aaf6590c9201f745e77b6e7eeeaa5750c92bd729dd1bddf37793531489f4c6d74acd67ff6e3890b4ec22021e51a9d9b0fadd75b1b052760908f6f73bcb6459eb13a157d412e1a9d8b96455bd98bd17a41b67b08d9639d8f638b5d90cd5dd9b2ed192de39f1d67ae39e18274eb51cadc73d31febfa3ace2c05275b906bf2a3d3fa727addaab4e3bc5893132d48389bb79d956765f35edb71e69e4eb220fd369c73c1e56dc59eb6e38c0539e7db58df7bde1a593fd7a2932b48c79a75cb6b356abb8ea6c90ab29d2ba3931f5bb3d9ee8e33643724b38f6fb5e9f8759ca3cc4915a4af3819bfe59c6b73ef182ac876ddb6daac45b60e998ade094ea620af6d6a5f37e630dee8ea874453c759c98914a4a3cebedd5dd0b9fbb63bceddb8313397249cd04914e48cb132cf7e6f59f7ce4241d248fddd6823d34b6bab4f90ec6074fce2738d52365b1baf99f1748ef3ce3a9b3b01fabdadae05bd1775f19b209b9b73ef459eed7975c706730d3a6bab3973fc60847ddbf9b28cb649ddad183bfe5c0b912411cb0b114962ffe28409b25f5b48d76de8f0d7ea8eb33bb6fab14b12cee7640972bae8cf0bb2cb1d6778851325c80ae15a9351cbff1d7b769c471fc384f800d2114d2192442da7239a9d1f2c3f5884b0fc6039c2f2428445fa784fe84592a4299c244136ed5fede67c2c42381f4b11232c3e587eb0f460294284c52d7649c2a1395943c6e5b545e6d6b18670d28e73939119d2100e274890df569c31b66eb7f6f4d971fe0f8d38cc9f090a61f133352f8ddf891c4e8e20d97291b56b67cd55d7abe31c03f2884234f5431cfe182be713d182e5078b152c3f584634574852e89584a6ccdbc94592de4e204992a4c5fd760235c78cf2831323480aebacb76bbd6f75645d0469e7b3d6b1ca3fdbb17d6ac8c5ead79fab17ac11c64604692f9bd6396763ed8e9487201f8c94f2b3f3d26e783d0de97dbf4e67fb6e841cbbe30cf2707e3459088bc5d66d3d6ecdb5860d1b5b147665bff47ea416da20c8d9dc2e3f6bb13afbd6aec8831320c8d837de4b2bf438f936ef384f17244990c3ad5d92704e7470f203195b746fb953389d7d1a7122cb4b152c9254dfde372249a32a49955333f284e64c5b3438f1817cbb28b3df9a37ae6c6b0fe4e4d51ae4db68fcdbcff1a0f93d66e7d690db7bed71639f91f9f3cbd869eb38c30e8d66b6384143425e1d2db4b5c2fe8ed30834e407cb8bcc140a527fe68824bd307649c23961e3640712de4523b765ebb566d64f74205df57eeea6dd1a437b7310996baf2de7625c8b316cccd95b5cf36f37f6fb904fce90d6c5382347d8166def210ee4dfc8dcf87d7cbedeb5edb924e194e0e40694beebda9aff9cd5fb9a19b09b3ec65e5b6e5ba57c528684ec9b56e8fcadc6c8d60919b231767e7135c8bed17fed9dd840b2dfc8d8b5cb96b3ff8ee7490df841f606239b2ec27a3bceedf98c8c879dd04046c86cbd062d74beee84d0d3e485849ec944a44d703286a4cdfd0dfb19beb7d8cb42024d204992a1013d97241c7a3203c91ceb75b94156dd5b2eba76220339176dedce77d39fadc5f1440ce960abf5b50a9da73bbb1d67260f62a13f121a1d61f6d048c4e2490c64b75b477bd5c69a3665190ee83fce353353756c1d0dcb0b0b75342cb1ff240cc9aeb2f9bd96bb46bd46f7e3040c69bd2933fd660dd97f6c2a4e6020dfbebb9a32d8d3c5585d9e7c21295b18e973ff4d19a3cdc7c90ba453d66c17ff826e31d73acea310279eb8403a5fafffc1c6a87db71ff41f4e3b20275ec8b7a6fbaab3c54bef9d8d096981a4ced275ad7b3fdfabce678705921f7decae8deddacde81de7c7a1916805d2d98e8cd278bbc5c77c3bce9ee6a802d9cdba75f773b91a6fd38ef3e759ce0cc5386ef610a4404eea204fcbdef573573934122d8902f9e2ac5caf63ec5ffcca9f69a2426ae45d0d67e34927adeffa5d13e9ad46e72e6bfbb6d9638d89fccbeaf3e81efeb3ffcb9c5c221b6d6f3d686d6b0a63affecf0ccdc8491ac9fa41eef6cb99abddae6824ed2f372fe5065f8cf363c799c338af45b2d57eb131d7feba6baf779ced9c615248526eadbd5b1fd9ac5ebde3dc41ba901032ca9cc58ffc9e3fef388b26dbd88546a23723afebe64c9fc6b938b6cd629210c60a699df71fbc2fdeb50548366bef72ad42b6d4deb664e4fa66e357fee6fb66cf8f643fe37b71cd587db979b75022a38dd731daedf55cfc99a1183b679a433f8a2399cbe00922bdabbec3c758a3ff3aceb099863392b9b00a4fcf67b72a74ff33d6be1de7f668382f0acd99c6323420e6d259eef9653f5d7bd63eff8eb30b3d0834248b18e920730e3a4767bb33ebfe3343f3b2ae6cdcdaabebdba22e32b51d67fb79cf754798e148e632295eb23b1bd2b72ae5f6fc388d439d2af332cf452be9b4cd8db57eb16b5dac879d452449147af85c927062aaa4ebdfeb59db82fc7d1de3f7a1a93695b523d74ad9a5eb5e38a1283a97241c2493c5be4de1a3fdec7b65dd5c9270268c9cb77eadb42d7b5f8c8e46f600c75dfe3ae76cabddc1898509a48d7e6ff5b6566bd659d871e688209740da79ddb27cdd574be75b1de79af961ec4820ff466b7fc55bbdc2756fc7d9f11881ac8bbeb3b13eaff16d27610bfbce089bb79fae2b17b2df5666fa7f6d7bacf68c8c875124b2a363962e6eeb6cdbb52d646476e3f4fb60d7766f1ba2857cb33aa7d6b676273b5b35f33323f24c1734a47a40b38824b94b12cecd42365cebb6162d3357abd7189047495c2f92af7dfab13dbbff9429e3d74724f3f456abf31bfb1b6bf68c4806977bcedfdb366dff94a4cfe7eb7a2e49385c8840be7eefda6cf628658c7dc7d97a383f53337296972a586a403d24a906345f1e7349c2914564b345bd61ed76f860b38eb31bc95c0d60211f5ccf9c3dfa8c3e6a5fb3450217d97841c668bbcb556e7c9bd8211f039a4624e96340332449a29989c3ee928463af80bebac1f53a3a8e0d1946c6bca66bec4606d9ed6763c739f4a3213f24c9be6766aa922492a4d08f3c418cb0f4e2d068e6440d22b2596e949bfbbdddffdd71fe0a9ae2571016a0200f8a66a6162b64a4155ae6ec8b6bad05ffbe117f3134f4ad1bc95c361302f9ddde735b2f630b5ad61d670c0ca9bcda5db5adeb58b594798890b8fad9bbe6f77fe3ea38332baa80cc9f9bed0e79b5f5607308824c6fabb2fdd78dbef8338a40b6eb145ebf96d99bb3b4e36ca72340287b6cb2db9a36c3ca6ded64e4c7f5e7f9ea6d7f962b5b467cc8eaccfe2c8cb5196cdc1de7cf8c8c87633f343b22e83d7cbdca7d9fe37a6fa4b0fb850a19a77b4effba635fefad8ec8f98120b2d9627f84b5fd649527992934c37d640a79993bf7f8587b36461a3bce3353c8f2fa80a4ab32ebbd5a377bd9731d9f468ca590ecd8cdb8d8bd78a37bef383f68fa544ef58ce648d4cc1e9093dd55d85e37da3f9d3bce3253e83649aabf84e585088bcc140a22493e2429ba24e1a0a045c6c92e6d6ef3398cacb58e33f411bf3e66eb9ab1ade6fdace3922449cc85463333d02d77ab2d171b328efd166392cc92cf4792d65e87b6576c5adb6486028f882634dd181afa403e33329e763234203692b94c5c4ea7b1b647465bec4ad7bd5324f224f5efd8cf9d9bdf3be6a46333425f8daee8d68bb7e3cc61fc1ed75f32a449ea5eb3efea2f1b29dfd871e634bf91cc658fc94c132726c4e138d1d221d369e9a3ee689d2f769c47214805cbeb5673bbbec6bf6cd1da71e6385cf2418ecf9ba30c5e68d7ed3843f60ec858afe367bfcc3d99c78e33932914c32d496eb71c5ed7dc5757d9eb388746a22864bf7596c24899b3af671de791cc8d59f090adb2a3add616a3f3e9b7e36c419333596ee78203f23547d7d7eb342edacd759c3b229a194e68ba9cd783e507cb0bcb0b1196fa5a30994622bb7349c22102857cae32bbfd5a635ea3731de7d0ec544e6990143a766dbdfbd1c15ad120ab6dee083fb6e55d193c836cecedf4b6e5de3308971964c7c5b1ad48275d91322f03e48ecc9e2f66d65a7408dd7a8cdd466f7eedda8264906f5e5ae92f6ae3f36a2f49afd14819835c8dab5be8a8f5e7d8a1182474ec3e3a6153e668bc2c0ccddc7b31f81adec7d6e1a5bd268460906e2dd8fc1a64dfef5ef805799deb79edfcb726e3d8ab7941c60539dee791cdf76acf2ec8fbec9a8db35ae69ad5c605e9dd6afb823d79f9b4bf05f9288cdf17b2f876797b2d48db266c0efedf7ecc3666c17db16befc1d66df9e3d6b83e6799ed6bdf2fdbbc6241dad766bd6c6737ea7feb152473d3ed4ff7ef45e81ab382ac96d67a23b3492785cd53dc900fd2a71f3fbabb4b12ce94520549ffd9becadf11527ef63319a1825ccc4d6eb063ab70b275a09429c8cafac6ebaefbd9db738d06295290d4d1c9dc5c1552bed72394414a14a475facfb66b9bb7e9b13f2950900ec6c8209d0e4ec7263b7c829ccebc9832a53c579d5408296d48a7ec5ab66eb208dd8b35ba13645d6f2db4f55d9b347a8465a43441b286f4b1667e775eea8e0de98c1da3ee2e64963aa717e4b806294c90b636a45eaf6d06dbedb8041959bbd59dd95fb42b3f25c869d77b3db93bb68fd425417e6b375256a98df52dca6bf8f3d5cc3d326c112e87ab17acacf1746ccd67612341be777f1965c8f3d93193a4ee33528e205b8dcf9141dbb5e75c168c1423485ae372dc984f665f3f9b520449ef7f3bb3ba6e6d6d5e35a4c3ba68fd56db9aec412782e4ca1f5f9bf3f99df3dd21c8c7de471af93aa4f799a621dfbb3be163ce416e3fa710a4753aefbbf53acbe26b2f08ecde8bd11d5c8ff9b53bd89c7ea55e6f7ba79602417263a6d62163b4b2d9d69991f203f962cfe7265cedabbb6fb5f3817c343a56ed7a577b20e785de1ef36f0b36ebcd03e9627318ebf772ec359d68c809ed3bf64fbd3d3fb603e9a675d775de161d63d77420ef6c75c2e5f86ffd383907f12de871bdd70e32a47ddf7d91becb1e73ef0c495b64ed1abbe6a8abcc1c4bc1816c7432f6ac83b031fb6e536e20dd3fdb9eaf5f17df55e622c50cd9f8565edee037f8cbb19432628b8d9bafe69c63cc1a3a072b8dd751366bbb2dc6994286bcb59b63fe8bb9e618753fc3e1b824e1482936904ea7ffa2d4c5dbb3727fae0b414a0d64b48b36af2fc2d6718e993e2c850692d90779dad98badc7ed3acef66326224452c660ee0fb26b2b7ae4e80b3ee6ac27b3a3969f36ff989889c8cce4224931d3c7b1941948a6efb5763c5dab919d9d418a0ce4ba15ded6e8ff7a77f5730ed7a48821a18bb3d6e8f056daaadb8ef3874686bd1061f9d05c21499d9127342549f4f978a88813fa19497249c209a5c440def9a0b7db77adfad559c7f9b1bc5481c4a584213b3684175276dcbebe088674d15eebd4b9fffafe5fe8d54881819ccfddc8965dd39d6bb45f48caf04ebeefae1a27ff4b79819c8b2e6691f5830e69ab4d468a0be47bb0d6bf0c699cd636853748f14232c820b37b1fa32e49381d944dc836bbfdbdb33c9ff76590736203144d48ae8f1b755fed3bd866776226a437d8b3ade8e2fabf5f3bce50d4fc1a132c325f1e975bacc5151bb3c60fb2cbec8a773657173bce924892502e21a9bb914e5ad964d339fa5eb484e477df8dcfab2164f85f09c91ab43cddbaff766b636fc8c87d17f36759338dcf9490dfecaee6eaff24a47b963ecad67cdfaed1270969db9dfd73bfd970dd180979ed37d7d442c614d2cb90f0b63ac6e6ed203fefe61ab4d6467ffb1de36aeb1192c2d9de74d54268e3726ed02509a70b1447c8b8983f7bdad8ed499d3f9e02a51192ba9dd5e3e3172f6b334a92c4e12af2c8b4f718219d3268e35f7b2f6c8dd545487eac753b6f9659369d2b42bef6d5f6645ed5369e2f1192cd661f63fcf4bdb5672f445824e921425676567bb9edf5dc3a070f21dbbf7fc6188c17c26b6bd4018a21e4b313c6e7f15ed7b81d8a6a66114992240e4356039442c8e57045e7f65b5d94b6450809dddd4f9eb4d2679dd58ef3c803f270388f480f1f5810a9e2715e109441c878fb715dfdda51b7d1fe4ccd0b84038a2064b476bde6607c8eff286b6c4009849caee9ed6b9bfd45a77b2343244992a416f270984fd480028815ddf3e58cb9c8fdda7aebdd4869bb4febbbcbea6247f983f4fb7e56775e5dbcf1d98e7368e28c3ea631647e902f5eafff6abc91aee608631f64a51ca7b38ebea6fd6ef241d2d978ce7ef83e42eeba07d9d6537ef66aa43d29ed217a90b0be8bd559fbef8f17e6413e785fa4ee7d32b76e737890ddcdd8d1f6188dd6fabd83ec7facb1e69ab2c5b59b1de46aaef321eceb2a9cb4eb202d5c5cd9cf4be9c358e920b9d5f6ae515b1d4ec77c0e30b26bbcb841d7a28baedd6cf7fdd9e91e738f9543802207d9e873b5996bcfe7a4ef0c250ef24df8ea3f8e76d93bfbe1205bc78f71cdd56cd1bbea1be48bcecdd87cf78d5138dd20db7b4dab4feafcd6dab18181d20659997b0c6bdb3ba97b2f4e23d010fb45c84061838cf5e16a6adfe3d5ef9a19286b90b7c1e8f5758cd471acee676a5ed4202ba42cbae80d5e6a1b52c76990d6d2b7ea5b6f46fae0ba6c08286890f5fdbdd86cbee55ccfb0d0544492ce20db74cc39f7b6afbd8cc1c65afc403183acb49f2d8cefd9be313a5c6590cff1b3b7bd8f8ca1c3d123835cebc2b678f1635a595dee8d41726bb73cdf3bc6af7de32592d478096b4c401183e405ddcec573ad67f3ce114a1824f3b70d238ceb417b2d0383747e6f9bad78bb755cbcdc2509c7a27c41c2db5cece675416bdfaaf6f39e2092c461d1ab19351150bc20d94e58eb64fbcc51e898244952cd6831a766d44740e98274b6d9693fcf069d73970bf2fd75f63bce151f9b75a26c41b2d5cfb6f7e8f7f367a70509b927756f2e4ecaef19250b727dcf9fb77dc6c5902ba4020a1624fbfb9a747deb37fd5a9617222ccc1c6e0d05942b485ebe9e8bff957953487db9816205d960642cb2391f63ebaa243173d8b380e286a46d19c3da93cd65aded2a48763821658bb5adece3eb38337e549096c6bae8fdd7b12ff33905396b5b7eb99f4e9fedbd45285290ccaeeadeb4cbadbbae7317961f92d49648123387db928edc049428c8faf3bec38e6e41a72b42415a57ef7ab052b766bbf8160701e509b2d9652183b659182ff5da865cd42d06996bb6383a779d20dfad86df8eb57de7da04b9da4d08fb6d331b324e385d77b3491ba3fc4c90ac4e1b5d2fb816f49ebf84179bdba13be7cc357ecdb15d70317d97b266eb3e455182a4d6b29b1edf5f9eb739020125097255473d4ea7cdee5b3817af215f5cdeae75d02ba4f0b2a8060a12e45cacb265ff2dfafcabb30f508e205d63ee5217e7827cfdb124b91ea018415e372dadcdfadef5586345908bb15b71397d57392e5743badbc9ae279bd01b32874204b9acf5a7ccbe6a9d6dacbd4019826c0e276d35faaa73b28d6948ae0bfeaaf5cef6d5b185201b3fcbf5eb73f0d2493f08b2ddf53a7a741c19af07027cd01b8b8cc5d5cb19b218e98d35aee82cbdcc8bc21f48d87345487bbad616b32849920439fc646a139803141fc866ae2bdbfb5c7597b9ee382ff117222c222d9c81a0f440429f153ec7bb2863b641141e486f9eef5af4d5de51eab84041433238b9f99c0f5ac66ea53b90f1798df0e3ff85ddd65174d0627774081bbbe55c6cf6d8b37c6fb7f5b531e88d59819203193bbe561fb2e83f21db09c4f8ede4d220ca19d23dfb90b5d73e5fa36d4e23b7b8070a0e24e59fcc3a8fd331b618eb784d68049223d0ac530bd833538671506ec09219f22d8feb9b5a77ede9e58e3fa68c864592409367441dcd626c049432a4a3d3729b37ba8636b22a449220b39343a09021292fda4ed9bdc78cb226e448102b587eb008691c05141b48eab336effe7ead2d8f3590fccf3ec28ecc9adfe51d6796972a5866a61010499a9942b6c70b1196185d804203e98f7da40dc6e7fce9729431e4fcfaefbd9e5f5f6437a2cc40c6d93e3e56e18d96315765c01e14316475d59dff57cb7cf1accd3f283190b09d7bfb189bfefd36a7cba9098d20cb4b152c5210961f2c43e615923433657ac900250cb97e42a673bee89284c3021430e45d8eba8594199ded623b224992049b39ac071418c8fbcd9fb7d80ce1b7b3ccc040f942bee7d7d3b947eb3ff86ac799c3996238b05f80f202d96baeea1c5d76faeaca1f1108c505f2d9e4e52c7dd710c2b71de7d92c0c142f64336f93616df3b5a597759c39b1bf38d9845c76fe5dd6d108d937dbd59d134dc84979b1bbd66d65313a9809f9b5bd2edad7fddbd7dae2d598903ddd8470dab75e6dfd65874892489258821861892e349ab98464d7deda5a5badced91e5a42d65eb645ead739fbee7f6ba1d1cc4925642fb6e66568dbaab0fa7d435ef8dc3fb42f3eb86a5b4ac84a9bb6770abf4d7e5f27216be4b6d8e30899abcfab2464bf355d7c0b19840c5e1a0919995b0a693ba7966fa390908d7eb7ebb771a417fe23e4c7866e3517595dd07d3a42b2caf5525b619b8d90d74ef7d5baf8ea72ae272364b5d551eada83edb3ab2f423a363d3a3a2955848c94bd5dbb48dda18b9e08e9f0fb19af66eff5ca0e11925d0b2b65f79da5d5c64348c6cc62ec1badc7399b1942ba59ffd26adb7bcc9b7721e4e535d7857de7bccb5542487fcd3d2e3703765ddbd352cfbd9c214182485273d60e7054815e57a9d7d8ae2f2ac8fbecc7161bd2762fbf29480b637396f9d1eb9c3566714841d2e52d76a3f3fa9b90b28e732b82230a92f2bdf6f18c96cedbbafa101c50909752e8d69a73b9e63cc227c80997aff6eaa48edf7baedb90d6f9d177ebd72f6f8bdd091236377b35cbacb5cc51d77176dc04e9d5bd77abff75cfefb0e31c021c6cc8e6d5bdff4166fbf6d18e731482830932d3e4048e2558908709888603f2c43c55000e25f08bfefea6fdc6d45fc7f9459fe78426cecc94617623c09104872fb6c51ebddbe38e91df75bb1c3a86fe8b5f345f5c43c6d67adae9eabc6bb1879f8fc70a49428264b05147ab7f6bce2873c7395a81e308d27fd9461d8b6cb169efebb8c46104d9af3ee7cdbdae57e02882bc975db76ab4d6d235bf311638d490ce965977e3bc31be9bed514204f9987b554a19afea6cbda221486ff7ed75bb1f2773d869c849699cb5ce6717218b2d04b9a25bef5d27ffb76527e402471064ed6ebf8f1985acb5fb0b0e20c8d86c73afc966afc870be1fc8b61e6b9fd4353b1fb3db6c021c3e90b5d278a3bdf636e735f7e4e1cc885c0fe4b5d0c277ad6d961d6b93079223b4ecbd7b6edb37753464bdf4c2689ba3b55cab15e1d8816cd3cef8eeaf182fbba5387420e9f55a7fd55a6b6cd0298e1cc8badc6b73510a6f53eb0c04c719b25e4ae37a16dae51cf38b66440f070ea4abed39aeb0eb8a165207210d6c4670dc405e77b0216b103a7baf5333646dfc7ed5d6fa351abbcb90fc60a55f7b5efff9621538c870cd7964ddd8b5f3b81833efdb97bdc5efed391c3690f7b5a5af19cef64bf9fdcc54458c3f088e1a4857233ffae6659ef641e7b06b010e1a48c8ab72fb638f3aaccdc790d7defaea7293b266d9332f70cc40fa65dbd65f66d9bef9fe882449d2879cc3fe050e1948efe5a085af32f8b5290e3164656c5156e16cd04d475d0ca463eddcdf7dee2a6bb7b181230c399f8decbfb5e5fc5e6b38c090cb9d597b909f2d43f83090977e64d89752c89637fd42b6b3bfec9b5dcb3dd7e60be46d3ba17fd3e538b2d570b840d29ef5c21b9fb7afb361cd5be12ec0e185bcf632b7cb5d073f320825691acdd484eafbd0172a9b90f332640bb6fbd662ffea99b490a40f71f843539551d184b4eea29d93e15ab0276d9990efe05cedd9399973cc8d09795bf35db12973d61d7d97889063e3c6bc9fc3069dad35d76c66d53e66efb3e696908db54bfbd5ebe66ad3d6cf1149e2b068347d4410062a9590f6da181df515d9ff6cf10d597fb6f6ef356c46d96294906ec217dba4f0abad96b9492ce898c1e81e37477676feeed87de6c9ac4b42d2079d1f84b6cdc5ecb5922449921439ccecee04954848e86f457e0ed256e1b70609f96e84cbcd66b71d738e3e423a5c8fdd666dd7ff4659c511d05d6cdd1a3be366d618b76fd4d56861658c5f3742ce3563b3f1e983eeff5d4648b6feae6336ba46b9b65f84ecfeba228cffee397d56921421bda757d7f8b2d56ef9cd429224492511727265faeba16dd5bd7e1de7251ecb8044d81c1b22e4b3ee55babeeffcd5361e423a56ff31a6eedd3dd36808b9e2570a1d743b0b21fd1db7fa6e3f5bcd3512c21debc762c3c62ef6acb435e73cdb46689d0721db45e894bd8b6e19ed2908e97abdd8ce608b0cd97d13a80442be76d6badb5176f656db69a10208f91ca4d5ceb83a2edaac87260e28f4cea1872e2a7fe8181dfb8bcd6b797b1c5b83b3d91aab7d66af4c680a224932a1e97a68343303153f48c7edadba7bd19fc2d754fa20a38babc2051bdf45df6d153e48f7cbc1b6dcba70b26fbf07f9dc837d237cbf4e299c7a90ce3147da9cf955c983bcd1b2b62033b34f8faf8207c9f755b7f65dab0cdbaa77900ceb65cbadbbf781fc4ccd0b743bc8f7d83baf0bf2656c7e1d24bb565785f7eb65eefaa383ec053bbad66c74f445dae6e020b3b6627c0c32079d2d176d5dd475a3973264f6769c8b783e3347422391192a7290b59bae7effd132f4fb71906df5b7ca989baeab850e07e9b05258fbabbb6b395e8ee5a50a167f836ceb7a646eaecb9be9abb841bac9dead153a5c96b5d86d90adb9daa8ebe7d6779bb1331a4d1536785cabc6ea3c52da7176a8b206e9abda17db85b746b6d03ba359a4754673a6d58cbaa9a841fa64f555d766a5feb3276766cab8109534487bdd6cac7e7476ad42d871665c1382100d92edba0ee9a28d32b8e8ec3807314285ca19243b3b199caccd5727fb93a43154cc2023ebbf0edab97c39ff2803953248f7cb2cbed8da63f6602583ec8feebefd5e7736dbe018245bfcb4b9c9dddc7ccbb11421c2126754c4201942760e9da3f15506390cb2d6eb166b8bc66e90c50606c9d74ee69abec617b649bf205b333a5764daf4ad376e4a54bc20afbffb4be1bc31c2afec042a5d90d6d2091d5f3b79d558e1679aa8137d66643c1f152ec8c86a9d6eb139f9d2afb0e32cf240167d3c239a0fa86c41425b5b75ce4256d969752dc81b615b5c59f3d5d3dbcb82848e1b43a7ce5f73db1a0b9259871e978514ce55d715e49ad5b9bff5d1b9b5efad20bd79bbe86bcda6ef3dbb21df2f7e2ed2c9f3b18d4ba50a10727baebbe36bdc96af6e786f9bb4c1becbdba3825bcf9b5bfff8e2146443cafdec5ed8be61431529c8c790e35bcc5e7595f98540250a72367daf354ef8a0b5ed1c0af2173fdb1ccff62efceb4f90ae7283bd1a9dacef7aab0d692dafbeab5f575bbfa313daeae8966bf1b963ae35c8cebae5627dcb586bf6b99b2021f5d7de62dcdaa2743ae7300b54d8f0e51dbdc57e6b41b6185bed167dd63ab665ce3dea5498202fe4e78eb3dbed4f6b9720db62b77d3b757fe76d3d0279a6228f9919474509d25e766db5b7f576b95a559220295337eb5badaed8b6790d59ede3e9dcd7e2a6cf409e0ee751e01145d0f45141829cde2e842e3a4fd6f7baca11a4730c5f6b2c366861abd108b2b13fcbf8ffadd80e571164edff17d963e57be7e3e6031535e4edc78f3d84af5badbfe20f548820db62f3b145e9bbda189c2a4390304e7b69acaf3a3b19d63464a48b39f3db5a431b5dab0841ae8fb39d7bd54527ac3708f25e66e75f56a12fb6fe802027bb17464b1f7dfa685c3f90afa375cacd97bd6bc5f781bced8f218bef71b4f05b0f585baff97a8db93973915e6b9fc17bfda1733c90ef5ca34eed7b7c56bbd190eeb275cddfa5af59575782ca0e64ecc7ee5346efd7677f3d50d181bced23f4cb9c756eda6e8b611a2a3990efde9acccb327bd88e55ce90eed59e2c5e165f8becaf8203f9d8bf638e3ddaf13eb32a3790bfd8b3edcde8f64e7aa78a1992556723f3cab875b7c52a65c8e516b635a357e69eb577027154c8906ddd6c303a8eb4aee94ec50692aee5cfddfd9acebeca2a35903c273be78e369c767d54a181bcb7316fb6cbdad7ae479531a25bdc16739199c7c8e06ac88c36b75eacbc7e4216556620a79d0c6fe3052f636d592e5464b020838f35e8e28b11b2e8ab39beceae8373b2c65c6b2a6248eba89bd03a6b17bdfd680ca45776ae36e7eb9885d5a98421ebc7c85ae566b1fd7d300c153024848ebd185d5c96be4aada40203b95af407e1f2f74a995da399010d1185687eb824e1d4a87cd11ee4f55883d15773872c2e6ea2f20249ebff3bead6ddd81e8d2a2e9070dafad6e3691bfc67a98a17f22ee8ff58a3d03a086f370b523621a9bf055d77cff5968d6c149a455690a209592d63ebd86cb5d7a32e3a270529999097c1d9adae1999bd90be48248a9890b0de066f9df19f9db132ee21a45c42dad6fa5efeb76cbbbe299690b7d9756743c613365742daff65278cfc8db668f90d0969bf5b2774763573b7a55042d6dbdebd0f3e7abb2ecbacd92464752e7af509ef6cd4e9654c12d23d748b5f5dee0c1b3a9748c8b59ce13b37ad3376db8584f4ca9a7d961da4cdced1474876695ccdb5ca95c6f6a223645bef9eed5ef4f8bc7923e4adf0c57add7165e69332424eeace1a4e7a2fc3f8386511d2ddf75a758fae5ffb154190a208b9666535fefd189b459b9208b9a86358eb6daedb617b44c8e7677a2985af56f69a4d3944eb08d98bce7d3107df63d8f445bbb8d2ebb1b6eb264c3184e4d7ba3a56178bd43ef742c8c69aab7d1de3eadede284992d44b602f6999da841142d6e7d65babb2dbdcd2f6ee83904cd75d74adb598eda637668e8e80264f0b42c2c87639a78ef9e5e62cf2202510b269d3192d57f88fefb3e84793480c520021a7b50c59f5c56f9fceef38ffa886723a229a9938a29919d160e123e50fd21ba5f735f728eb7e97fa41ce38a1a5f521bf685ff33e48379fbbf5d6f5d12e1a5f0a1fe48495fff25b7f99ba56f7207bc2ca6ed3e59ec256a11ea46d3ae1b2fde88d1dafe741b6b390ab8d5e9b3ed68d07b9f851d6bc36abdcd5ae3bb4f8b8ade55a63d89c2fd6e8f3f61e7c94e1e26aed2027acddb6dfdedaff3da5d4ea2027b3ef6ef7620cd2c5970eb2ced833d2861ead75bbe6202fad8ffd753bab8bcf5a39c8ca62846fe17cbb3cc21907f9e87cee4dea6dbaeaa083838c0d328eeefaac8ed6d7de20b9d785cf6f9d0ebefa981b24d3e77eede0f50a2fd33648af5f178db6c1ca06d9f1c59e71fdfbeacd710d10aee7fe60e4e7eedaa3cbded5eebb5ce7a46a90f3f5cf48e784f4d9f51cc4414a9c28a8647206777910a32006010000c3648c08148311002028201c8d862322b178346cf300140003528c428c503c160924b2589283388a822806420c20c018638c21464106c70a02440da2a4f70b9e19cef7efb60051e3f3f1d3662c78862bb247f1cdf2e14d18f7dab2172ec655d1e0546dc723454d0d56b1014950080ff3fc6fe122ed4db9000adc2d28e33c176df1f800b3a663d4623dde81271dc1cf73986bd7dd0a5c88b507008728168277e40a49a8caba000b083a310e11f66faf48ebe15576ad18e68adcdf6fc521bd59eb16391659714c0b1e8ce20ec86315af63392550619f817636be187a460c22ac8898a8164f0b8693a65b7f083472d15137197239e7f91780ff8f6d1448d9aa45aa55605a4d0c238848a75321bac4ae2d8a70dc66c14d7a7ecc930af8b1d4d4f309a2caba4849f629dea1aae7c4722ffdc1ec2bba014855b50b2de54a44d45e4596c8cbb69cc2805c0db3e2b9ee974233789a557ca6dbb99dd08842948c15d09f53fd9f88d68af596d347ba730facead91e94304e0b51aac95309179c4fc470a963ed41798b677b0f217e3800959d8e830df4eb02b82ce4889ef4d4b4d856ee86056ea1ae9de56864d7b8678cfef6ff33038a8ad12be36ae0eef4cf0b717c68017aa0574e7a86b3d6db0f5b48f14fc177018684bdb501a6027b6c83727c9885fb51ec26972ea9d5070a6bc6d91a4770c78bbad0615fc6f20670dbbb857902296c4b54dcfb16cb04a3aa8615a7b76b53004e309cc641da72e9274d5737e428553005282e07c8b242bdfd32bc306a25b1871e3408965985f59eb1154e43e6e2d07a538117434d482d55cc8c698c5787059d3182dc70110b817ef06e1ed247cf576076fd2a0d41d9087ae36db1bb9ca81dc2141f4efab115285507ac492e84da308e0e56b044814a50739f5ada4df24693fb0b5c0e0967c142ce97f49c7bea535b004855a57ca86a0cf28ee230f3c299db1d582c816198a61a04b56b66c04ca1b4a40d54e13664bb54dc333282e84289758c06ef2730b1ae25713abd0d0dea044eabf5eeaa973b0eff034e289e38809c00e1d0e2967056fafd2e64df599fcc7efcb8411fa25b681951878c0f8244153dc0ab3b4c3ad24c3f06f728468c319c8dca50bbc5c1dbb792eb1362e73d9d92ef15167e9baf03145cccfc547d4e529b24fa8b7ce604f4119b6b75e70712a76835d7ce9b292b6a87ab2242955efda7e97b37fbef8c0a9e3e2d6865bfe3d4a7fbc18f5a18e24bfe85bb7fbfe23efd08981f58f85ce06985c90d2768cceb1c3b17a6ca583f9017322d3f47b06701d913d074a61b2273f7f49d9e8e2e037d986640212fa368b452e869a2226491f97c76ed03e934f47f19ec3a2c12b493bab067b95d530469e52eda4dfb47640dbbbe735349d5344da30e684e52008f9708ce852ae98732e632f4a71a88ce6ff5120d9b04ad59a286c05166277f7f651f5456a847c839d40c94980668e9b821e33f980d61104fee8e7a747b30b441fb543891f36a90dce8318e62572f7ad0e0e235e584f5142db2655e16871fd0fb4d9991a398be36cab405db739439ee7aa9812a7db9ddfae7a8651552ea346b89b0ca14216099f7fc92ff9252a16f9a0153183c3570b4eadcde1a1c63dbf85e34be3bffadf6be1d5249280be54d3213e972e04745cc5086ac348a52d08f558f50c3e3c16a71c4e04ca914cc6133833c62cce318b879cf248f67a69d799cea7e58283f205c492e05317172d2904273c4155d348f071409d1bf87155922156e47decd440e33a0f7e99761556739490838f971ead6c4c18ce5564f899eca59801a0b5b35f7fce0384bd55b18b9fe544163f67eca15594b5ea5f66e0da2fcacd3c0c5c8b2b9d87de31065bd760701f790dc0440086483455d40563046157bb339950a43acb842a080a556e526082f48d293219039be76af9f5eb9e62dcde8d2bafc08b64d7d60a724e9d4d4523e210e3d98aeaa56a6c5ede45b91bce1d180165965f02e4a7584442412f6edbc7c4a22c71a444427a5ca306e3566f6887b4bda23aefec0474f8fb727ecfa74bed2676e162b8743cb1be3f8e0fa42f73d8fb430fce5e84a669efba3cff2f5764b8125be20340f617a5435517c11daca4080a5ec79c6b5e65db0c53263e3ae9d1d8b88141f988b924ae121b4772069b872ff29f79d65eed8d55892e800895a28a1b06177129d999151b82a8e898a8dba7df3d107b835d27b554737c23e3e147ca1649de9da16a89be1bad97dfca96178e2a336d0367b49140a80d2d1eb6187e3881120f70926e0c84485d759fe1a070d22717ab1fa4d7a7363fadb9998e8753c50e46ada312394bdd61b28dc6947e66acb6dcbe48df974a0503e9503d77e0d6c7f23255c7c2b0a5ac99cdf63a24d571b5b4ddae718fa4733dea5902483e94b0ba0c6d9bc434c76dd5720858686a7ada734cd7e6c1ba1b0424c3c922af253784e6c5f447156f0c71d867aa83f6e21d27e016b365c9552955d814daa90949f0d797aa5a4f655b94d27e361e87cbc4998ebd2b58304034e8a4007306aa08fa086195d3eb558f0e7f2915ff4c27814bce4f510259f163be87eee91261416069c4954f82c2bd30e06508d112abac51ce20fe299a835a119fb99165d79c6b23ada15e9d3523dc50326143f82a3d753c659ca947cd939010ac28cf1c9d75e1b53876ae9ec5eeb30aaf33e19b1b46f0bc754ea08f1fe8709a7eb8db860215f5962d138c58ed30ab3133993cb0447ca32590eb4b822345abddaf4fed20b3b81b402c31476e32b1df7b9874833428d5ee220dcd1b060ef4d74ca3abf2c4440a82720ab41fce6121522bef622914245fc48721881eab526179a673d210d87e4da64955d6b0032a6b2c9a42e04d80d204aa8e267ac5a4187c977344357c6ac375b8d43dad2891a316a8e58f38e04d2a19ff12b3e03cf5ab0209dd331a4e9af084a512713618b7168d0c3385590bebb4f0a68ed3e9dce61f30cbbf902f1b1ca2278cf3c19105c8dd76e54b58a1e1ee2df7f659bc2fadb5fd4069b4260a19ced4bbcf55e4496f4e2403f77d159452d1b056e49c91a10d1b777b996dc9f565581a40ecc1c9a9225ed0cf2380e0acda1f345c24ca9fbf6442e547ad2d9bb7d603fcbdb91947ff674bac7080e703d38cc871632896db340235628395141e84e41539cd42e05dfe2615505fbde921873447199790fa524e13dae15b221e5d2e37b8cf74545993ace22f5844410547f5f226817850fdb99e87584202c608090308bc03af70a87bfbab9812e8108925d0920984db94585436bcf706c257acb8129e30dbd93ed37a841a161c1734608e77cb246d8e6ad6ff4325072a47653458197423f76f164de23cdc219103ad638dbbfc549807bb2675ee4149343563e4a26c2a5b9334678c966e4cbaa161e5594e0d754715471dd1b968a63e66ecd7d4934806b6adddb48e642b320a8c3ef74b39597cb67af5dde7cbdd7c2a1257afd32efb64208d5570984fe3c09fb0bc442f788a6b028070dbdf0a39196559f489d31caa33422a9d93983dd7c4d506a34b682e7600239fcb2ac71eb19ea314591e98f2f5492dcbf0e801b884a84039165fbd11e8c4538b4af077000dfddee4b4bbbead33892fa00c3d8294aca8b1d440c2ae8febf9acc16c66b16233e4ecdca51a9f6e68ebd2285cfd9cb7c99a1ce181622ae66356d361510e6e7c1a145cf70d4417df93e813274c49f22a92a4de887cb60be898ee033fc684111f772bb12535f5b2418833ac7712d6e13fe2c9b3adeb837e84dc1c947e4e53a398b21ec311e2850a1ad062e239ca8a3ae48c127963e5287aab37eecb504541764c75a87ad6fad3cb5f13a2798434b47ebcec7bd833acb0a39d0f420f1115ce15597ea7cbd033e41ff7b7b246fc54efe5108dfd51af856361119dc4d016a4fb2ea410d6bf9997895129ca081a958db024c87abf78d37ac69e1014c82aa2fd37e7ca7eff82251b5738bec2dcf9dc56ff01f19ce4521d2be3e9e522808ab7168eac51b8d2af44b33dc3433ca08ac274e1e27b8072c3cde2b4fb711676fc9c6388089c0be2f51407d4fa31bdf0a5604f0404eb5e911e41eaf0fa40f6a2f34f350d47993d6d2e8797ca3326556fb7937a7f042765940dc915997acb011268ef0ae2c111d28c17d31dadf4d9b46f9f0645d3365875e625b344930a64a03e16dd8e4b30094c6e4c3066a98c677a0f0838bc4832ac64c1c97221463fd4821825a0a1b5208a1423b1e6ea20220056c57894f15726d081616570e9325fbaf469438180b1a05d2777255872c2a10dd4f6a4dc7db2738d922174e89c342090e32f5e5f6e01371d5a5d401b5d2c2f7ea9cce8c305fc95acf1db2a715071822c1127ee1590d116a0b12fa9834d5401bedad8e9202bf37a91c5dea915576df161066d7da54352a9c98c15c5e4f582831006b4fdc1e1a053a9d4dbd4cb98a29e3aafd289cfaa3ad9246c937336cfc65eb874b9a990fdf368d5b6768573c0462b402f26182511ad5998178ba565803d955b5e86edaa9c52eda435359c8f40ec4ca89ac556b33e2f61557824e5d252f5501465b311822720b42103c8a06c2e20eb9887a13c610c954242777532577dffc023f10c5aaae0f5deb7c1388dae8661a53a4eb6562f684b1d6852c87feead9508fce5ebe158a5372542e18d92843be413bdea91de77e86b64b361da5b2998126ce81ddeced2d52ff23e834b09ac9392a6f3658531f096899b50759ea8da00c847551e7854313bdc54939decfbd45eebb26fdfa6fb9c2243cae5c4fcac2a9dc10b934d4e6f2895a57f83164a83f860268f09331c8ef89d1d3033add4219c3e519c623aaa27079924173217f7c143e82a030c496184fd416f6ef50738abf01b82b610a2f4748c67346c3fbe945fe07a48e0c4443314bfea4a8eb7ca63528f8b80bf33e8306e684aca529e6c1c12ae0895052bc908ca827ab2caf4a456f0ff3fb37250dd5576e761b13452da459dba44d00e3c9be4ae16eee81f45c4b45d8cde64fb66e6792d0501d2cad35bed328a9b1268791b5c2c1103586144821639fd03568b6b5a1559017a69c717362782b06e776a7805fd6b73ce8a91e78d76bd63e9f7d348ca795779ec32137a10070694f38def13ec0a55e8acdac266a8b3de044c1647d9869bc17746f12408a8441e096aa68993616be7591a793fbd9c62ce0b9289ed26120f3c112984192d7ad2364d8afef08ee9b415321b99ba0cf683bc873d7c00325bcfabbaec060f72435eda68570a4212b1f4005ec82f6df63511b7920d763dfc42d87bea017dcfa66e903a7c97fcb30d0833f9f98a5d3e38cb72899d685620c7e6c02784b661c10830fe5ba48ec9f49253d8cf694db93f14db4ea69ea92d66b1b7de714ef1959b6cd6d6246b73ab16202e386dfa94103e5f8356842b426b2dccd4062ada9f085d173a3f16b007211ba266d6eb0d1e605dd5e4a7e3256363abcb25967027083f0ea1cb4a3e20b59db4108ee760f44d8b84f16a74537f555453628ef977cf9f0362538421338e0d29eff1dcafd850d0364bb748b7a422397e826eddd386e45499f868072f305fad8969ac9cd2a446ab3e6312467c93b8c363625266485290cca1de49bb9c5ae61e19f5e295c253518b451a6c347337822624915f77fc21d08097f949281391bd1b3fef19b7119c15fdd0efc1ec132a4f76ec7a1618f6fa0f70efc62bd99261e450ee772e43049977dc1462d3065e1c6025ffd48a1d70eede0913faade1a253b789921026979f0340aa19c92517979c28a48d248705d683714396f7862e093f61291c4079da26985fab4310ed9c91d4c41d21410e976b86c5cafa74e050a1750ab76ef46d3098d810853d994c4dcef638acdd5e275cab405a30b73a1b80c841e9806f0aaa3cd9d0ee813e548ede3c946659f9f089c104dbde37a788d2a750622fcd4d0409d5e564494bc23f626e17699729d7c89180a22a0195d79253411b4b250241d2f60c36cee476f169acdae264a3d257ef2bb6d59557ec5ada9d62e4993aad6e0b9a6745838dc5e81640b1131e889603909ecb1f9908c42bcc44ae356325ffe6d34b2864ca60165565646e3bc804cb5225e139d718cbe08e819310b99ab331752bdd801ab1534e2565b060741d468efd75fb1d7422bbe3eb27192a6506090a54a20f3f72cd93024dbd29851e4168a5c5e1e0dff01086c634ed494159c5aa852cb74340723d43f206614a44108430aee7bbd0e6deb09ac913d2e8328c74a69c123f7fbbad8291297606cc6dac980dd7aa5f42349607ea62bd0d0d9eaa96d148cfb7ee6f2f5ba29d52d28d9d7674c7d4056f578a8962aff774330cfcd52269a6ce17756e610bc8b17b099666bb6e1affd0f2a1b5b2c15069e6d26b20c2dac326bec8b65e51d073090e0008cb991304b67dd879d5ede77716ac5b69b8df4bbbf3b939f51214218b36be3bd2ff396a706805f7091230367f6d4dfb1375baa69bc05efb580776e4aab243a4d8729ed3d688f96df24fb1e09329c6e42ed4811d8f9e474f1088833172e758627cb9990c05498e86a20067f389795d04c963a14badb5b55c3ff86c6b1536c18c79e6591c20ab37dd30454b9c6a4b94f79ec6343f4aa6a9dcb79aef52cece01e2155d1a1a34ff55e45f6c50c634c8d12d193c4301d1be4510d0268561edbb549712da9645d12600a620d32a392590602d8c9fec027a864665475d5c867d080d5e2afda3bbd33d542777c808390d476672545f2cd18005e75338b70925d9a0497f55ddae19471f97ce4bb68b0ab7dbfd66806150eb70d47e1f46453cf0024d9a6d54e74105fad01e1b45f0a02b441e3b9b10cfcad4b05178bd5ece31e5ee566211c17f287d9d072211e3699696dc36b3cd8655730f1cb43461c9ee7c9d7e66a52e2d71ea8e26e285bea9a19aa116f6c94a1267a8a7164d04123fb9344590e5b421746341eba46d142060f37d7a2b0d97f5ad3f202c866ee0813bf5dbc1e87c68ed88265f2f380f267ee5211b55309c6c8329279a6d9e6d8ceaba21bb4a7997c3151bad855bfafc0b0814f65b98569628acecde3c2c72d2aadb7c31acc8b51e6096ceb05fbebc446400c0b5710fe2d3d3acb916070f8e9c07eeca57177eea0a2d4cdc59f78c2ffb7c4781ba4275681c101bd44f72ad8f77757f50c31097880ae3baa78827654dd2d90b704d400a0f21593a5e2e7233237aca0f7a8554f935812ad8f791d3a998b1263a90bd605c783a05ac46925871d708e8a348cb438bb15330cd6acb17f62ba1aaa8a2dead927e3822a1218c55b8c34598ec4da94b27555f39aa60d3ca4c9a6016731a94304d078ad5ce2f3a5779f7d119eb64284b06eb3fe05de564244ee2176ed24e24e9af8a54d99d665fe56edfe3c86ddc281d012abbeadd32c1b233320c314aa418684b41e8a0f4d58dd4be22e8d40a9c6a2860a4158e30f0982fc981b43bee101973262c837bcf66e0a7913bcd6971eefaceb07f7ad39fe66310182a1a9d46639c801642b9c2e85ef7694baafc0572de63086d49754ba939aea5fc7a2be5368e59152505fe25ff262b41b7e314b7b9b618493e35b9b73da3b22e1af2f2700c8927d25c837ed818a71454fd19f7dcfb5b00db3bd53850f34125507926cf70076413ad12cae56766b4bbb4f6502bd430dbaf74421b56ea631807fea2c0b2911f353b03de506d32f10288b24f8dd888a0a2a9885dbdfc6533d7fa3471aa3e2424614792e01d01224153551c035c87cdb09ac3a8d4e14eaa83c264712f8cadb022d0770c62df7300a24957ffef3c044ba735d72e114f4d2bead037fa33d168371ebc38204c97c341334413244ab3f0616ec8a20eb74677c5d1ccac6ea9a2fe2bb6b1c504451a4ba127429b74eea73528708420bc9866f72aa8c816d83cc482694def5cfcdfc301659f4735a3197907e5bf554e3e470adf1f721c6474a94fad09b9915f326cc6bac5cc2ea9b9621d7250757c6cf6c738050c9e4c2bd26993db7ef61e4cfe9defec0826ae444a600f051b67eaeae0cdd399a9b6ad36f67ecb1a0c6f3475324b9ac4d3419ffa9a94d8b01db0ec465f06b23f64ea4fe90ed80025067767394690a61d39b9f6d7f21d15dec51f6af3fb7af387a0b0dbf7be627c89238a0dc6d626c918bcf544e4a8e3b473be16d99ed2cdb03688c141ba5c3831adb5164f21b83695b7cf01049417c0865da4ae2c6ada4dc2290525a7edf957b8ae74a7139b5986cf6b574c5bf6e707c1451d15e53d8b7b45d35064fc8f0dcd2680bc2ec407b411f66bbe2e798be7786dae3e42ad13bb71eaebb933aaf76aebabe8adce8f314ff14aeb9ecb935cffa36afd3368f8c3f39ae0614e782ba7e651701f00b1b52c9a1c3b4d648612ad9b9be67668c4a5c747b2e6042962c7ae64ff571df58af5bcbf067b7f60ad4b32c70dbd3b1a960c48d6e6fcd975e65083f236bb8797488220a0d05a73dce40612898f42b69312c3128a08874a1469ab29d86c3c7fc2e2440e30cd8df7293fc457e14a05a8065325a3d08b9531285e8270ba8f2de788060f9720c610eb70318801c643c9f30be040a5fe29cf85871fc8e6a799cafe47aeb6a9a79015cef1692d2014c3ca12938b1e68299aafba391fd35c0b9eb0454373c577fef572aded0077804dda415bf9d5e0c0b3c27c0ba790fc3f879f562292f6d1227a3bcf70b1a2150fbec3ba090cc721984869d14cb4c65e2438d5573313f9cd866e6870bbaeb8229f2c39603791a8ba29afe707930f35e584972d16a312ae56dc771f2bf6302451bcb798f6708aa599fd7302b19667e3b1e3fa35d5753a99c53d6791c1e361cd2968c61a2d22fc8dca0b72aa88b9dc43b1375bfb006bd663998f70315e00abb9db9c076549835d913de06b0b9abd8f17baaad842c4931b56acdde6c6bd3e1c2e0118c25805a1d08db4a4b4cbf31d5f6097351a86894ad2401faa0f67cafd4ff2a4c9fce4b741f0ff73890dffa5a9440c16ad8a505976f31c14413c39a970167c16be2fc7171c0997eebeecd7c3c51208af77f052620c6e6c1b1301071202434e3b1467e6f843fd3c6048b2ff69c4ed9891c7f03c3bab6b35ca79738f3738c10265a334d54f41f753708c782161591f0b7d2215bf368421e2499c584f9b4f957685a25d6a77fd4e796e06dacad9ae857b523eac25a6840cc6c85feb319fe05550c6a88ad6b62acb13577cf3951766395e9d41a2ba8b582298060644a4e7ff06ebf05bbbae67fc73e7aa47c501c72567637c034ebf1a96bbc52261b5c74f8e69558d0f7fa9d1dfcdc4f0790dd8d3238799993e0f83db8bca0403b4a99ee4d2c4b19170c236d3f7e75b61a15b2e8d4157c9a3b241e1e9d7e365c761b017d0a57a781e938eeeec2559e42a60032eaca1a815627316f50ab7a7a83d1831a590998fa6141621a539fa9905f43cdcfce23ed8e6b5bc7a50de95bf2336b45510f2278543a6f4a4fabd133d7c2004b56ca802eb8664f431896f743596995c6679afe967b180162ee2c9f708c43d6e71412a36dcba3efb3230100cd10a5c6e88049472fcc72c0ba10dd0e54b00b4e2e975244571f71c5e9f3d3e8405a8024da8003f360a7440049e3f32046260fb654e26e5d6ca0d83674216bc970b9440033342588fa0216b5806e3f89001b165a29c3a4403be20c4f34c4afdda8a633b38ba9fff54dcbd6ec40a24e6ae73b695c8b8563c200727a6221c0b9c79c22153618937204c0c0df438f77e34bb2802a28dc58e13d0891f5475e32a102394ff3137db84b451daf0e6b197bf59da9e9c32244808b60b350b04f7b2aacbc86c7d7aeddb60baf17014c72404b930eec8e36614432abde32b3080e7f5297e6145eb5507d413911cd8b705f03998550c4a1c8b0882421839efb634e70f17ab2988fee722c4806466a645026c6471d302e13809b1c0a989951ad0ccbc1e048870cca08f88e03c13332b0c60f4c39b42347ac542ee73d48237938ce0538592ff3b55ce6c59dbb46738b0885a3b32422839b7fd0ebebc1ef60ead57e2f271d8a37d461fce56996743823fe8fe2e59db0ce0714e1bec3fa1daa79e54a70b4ae30452d76f171034e81c75030e0fb72a3393b24925c6008f0fb31cf2e083d3c20497e2a9aba88649d0f3e77b04cb347029d69a2ccc039358e9c0ca709826e7c8863a3d8cf6a681c18e4ee332c37d2c17751fe4616acab0177d3f9adaebb07fc00531f88763a869f75fdc141bf5ffde925c3491735a69878e9dfb6bef3d8a709ecc0efa4ab4c2e859f206d21eb14ae895f15422ae67f8aaff64d303bcae891dd8f0287fdd024f1697719bec613726b25d351fb0107315c5bb2998fda07684184e2eea9b36d7e5f4ca0af643219cb0b413bd5603cf9b1819a902b9fb14b4adfb02ebf4f08f04cb91864243ccc8be22953ab36b9f549fada160fa19b14f6887da4a63bf6478d323746216d07e800f920e0157af8aa82d432df0226eefd4757fbe962175b2cb9ba1124a2a96f8a5c0835da510993dd5e18c48e6972447328435ec1d9f538c765168ad0b1b802c53a43c62015d19b4917513297686e9840b6244d4e6adce9da3dce657843076d13749eb0ba95dcf741d73808b481bc59f8188693ac282f3f51e09c47d998ec5eb3dee2deabc3905583739afaf381f1d8b001da100e6e784a9ac343ad0e31b7875428813040d67e6aaad1f9e94979c2a1e265a0687f26f62766dbd8b1c77911ae3541fba8344b179e13d7fb9b04667c553fe261fc15281df8f7e2eb18cfd1f8113763e1e317713f50719a09b913d14a02fd9317dbe3b1827e0eb452b008899434f40f88b285ad670c8869b710a839650e3800bcb8319c63345fb0858a343883c01ffd7fd734927cf0c1bacda046c09d0130edf7f3ce3d96a3aa77b0b9b5e494e9b9710f1f6e21ce68a44b28f141ecfa41135c7e329e224032f2c6beb239552e430ee5c7a827ac33675504767466a3d9e76d5930f8244fa235c350bc358b975bad63a4ed0792f5a6f7d2e2db29bd2a4de31317c9d77e460c8e478cae9b9ab9350c0ef61f31e4066eac43a3c6866c004c3cf8d6caeea5696e265a63296d5a43007a46403b0d35f55643674de797f1cd799572796c4c786a136423d576d23b049495965abd92cf85c0b46fda352a08cd90a92f9b045add70cc530b8e487ab8abc21e12f3a9d05eb3b35491e57599f7564bc91035cc05b27a7eb4edf7e3186ef6539783d70ec862e267b1a919199e0beb3e993a0576f827af38498ea3aaebef7cbfce376253274d5082c60daffa0c9187df960598712271e14c6f8e2058b34e8e1646e4ad020b969beba0637d3bce46c6f616182ae6469acbe652eabdee536baadef846afe7b8ad1bb4b342ce9b8e1128772987ae31fefaca05e176921fd1f59dd25d6ba5ee3dbc42b484fe46c82600f7057ef79a0a5ac3b7818ca2f1cb2e6789211c3473bf37e0b9f2a3d73ffd306d05fd815a2f336b2d7d1f26ba7e12dab9e42f5804c35a9623ee5f34fcf7f02c9eba6fe7687f861b1ef667d6f279d15c20217c28048f623418ec671d31725019e423ded0c65f00a46190d46a78b854b2c7e643c279ba103340baa1fd9ea7144736981cca418b0ce75d18fb9fdb2ed673b47fdba5f3046425c72dcc9566f3e4c1ebbc2efa31a6d0c7a53ef8dae60122040e9788386d92af3fb45e13587edfb6420afe61d731dab902bcbeb94b7dedfd3c6e750d74afaf3c28a083338cc802de384ee69662756ec9d84b41a48c9643db85d39650793c5f3334297b6a99be301d30e1c5b40cad2d37fa50c396936e9ce0498b9018a8968d91ab02e82a27d51b3e793b6a7080fcc3e115e21423478e85d073ceac69d03e20086685820d664672b5604dc8bdb89750b42b34acc1a2228bccd66f0951963dc6608da410eeaebe4cd284fb8571b80220f4edea0d802dcd7311acfc4642a5301c58f499aa63a2452cbb473e7cc45ed925d8548c8b209ca0a5118d882d1d2ff869219c328010d6d411dd6c2ceaabc5c52d921a182e6d88133553505c7323430f9360e67a88a8e2234a9dfd1485dacc11c93b6842b009edc8ba1a2118e7ceb90d0a58b2b895b3bb05a25ff10d75ab6e5cac9bac2ec3c25108fc4736afbf691753d89a5f07148758b63e0893ee44cec42c31d5573e5b6c59337b7156c177e097d5cefcfa2cd2f020ce2942f6915df1ae7b76f283ebc22380dacaca756cf912045971f9a61d1385029c21a8254fb144fdfbb55d531f6d7cfcd18fda9de87fde75daf8a7a60d9b1c68418747e5159d65498175f700a82f90c3eb4d62fa98e48f55404d0b48c623c14abd2cff6ef83283b8ba1a144b1abc67282c9c01c5b90eaabad75604db4259ffc9b232c78416b6a0d8f040753a9156e394ef2783b0bb72c3f7d94189bbc80523075a9e0361d216937759d9c84225886a799c6bc3a729da1adfee9f0417eac0b2e5ef51584d0d2effd3fbb67e0723df4e9df4e01f8ab6827b14d74ce3a18df160c82279ec4d1cf9c7c545b9e8d5d41974002a6613874a993d7154ab4fa20ba5f7417ed07f893997de7d68a535f31aecd3827d38d696b396b522e549e5cce3ef21571e6a577806c7716891ae67587e503fbd208618b74ea9ad4c02fd576ade122c0c5f5179c36f551be0a69d92dd7b871b0cea6b5833063714f3980a6c03c3b58566fc2b2a7e2c3ea6339fbb7f0e3a70424065deb08fbab6fa112abcfcbaeca2758b60a000279f1d9a32faeaa78b2305d697d3472794e6b5471aed1f3f142c960c35b8dbf2c175470a98601d3aaa039a4576f7cc3e687c3f747f181747027b7c7daaecad8e92b8571a1348a482a9d134c5d99b93d3f27cfb0e71af5fba6dec46a40089a6e62cf9c62ca60bf971afc5dbe16b0c1c55b34c4ba80e891ae4ecabfeec0b8b2ec9806aff9367ef2d1230a802d932de1b226cbfc2d9b223410242b11ac21c98281a9bfa7c6de9affca34cba2ece2627f16c6a25e8b2669a43112b3f616e5ed808683dcdb127b2618f59fac7098399c922566fff2090759c661cc6d2821f893b19050bfbc6e1268c7606ecf27d26b6ac360993ffe201f889d5abb7f7ce0b0c8a2068a2c6b20022d9850f2afb5a085a58905f54ed56e8ef080160c213afc1f876eb3fe062e9ca8ee3f8dd77fe5fae55cf4b7c6533d43fea6ca65039fdfa37e06986f7eaed27750973e8c1771323dfa4d3228bb6ef3bf40a1d759e15d2d97b5095a91f59b8fa6525bed74d351614e7b1e99b91baf96bb4bcc4f70d7ea2804b47d6f46e64c0f771c56291a933be2f20c39a748b6cf25de9cc40bbcb5e4bd3f6636d2f54e851ea683e0c9b3806840fb75508c824eaaf84ab92fbbbf10beb380bbd3444f069e9477e5806ec8173a4e335d5f39c7a685f1374da4639df4b730f5960eb8b75ec672fc655f4a83df7cd73efeb31cfbe4e3d14f31a0d01f2a131760650ad4c377b3d3793d1c2e58700fd586f20cfbfa79c55daaf226a74814623cbb89616f016783922018b4c84d46a0383188beb93c54bd5248115e2e20f9d0f4b246280913cadccb1ef1b407e5578a62dea353a1bede2f0d9f8d019f73d78f502e3c9fe17d60271b355592c6184d0ca2bbb2d542e1de9f1caba01b540953a265b30f8d50e240a6ff45a5924bcd124e073b3f3b640f9923dc32c45f3df20c90307c3616e1bc51783d792528b10a60eb63f0f4251b41086866e339322d4ad4318692442664d4aa83a9340030125ec929950fd9319da3200184d36af9ec09831363198a8f8cadd7c92a0d8310a4dc19914f93a96f494819fa7c01a742166b2acc09b3906ba856356359f11c0da74b51cb91d59b7d80aa12dbc01fc127c667d8bff39d066f3195f99249bfbef58a7b6b1f13b1c5bdf51e535521966b0140e73a366a865a2595dc2a9b14fd26da7b4e69d2cae2485dc66d35929093d03bf4e1518f2608801a6cdc8df96a07b06d7b4c8b6e8e88a38116ffda8018f1683c5c70d4386532d1688fdbd4d9775cad4bf2ec1b88d5b4b5e4dd498b17bdf432c1711456b019ba299c5a70a8655f34771f18fa8e432b5a311bb4abaec1ac1e8e2b4130a9ac14a0a99b755fb9115156fa6ae89e018a5f6d73472c16b5b734a2763ae4be6b897b0a688e91db047bb7e65dbfb91cdecf9bb4b0becdc66d3da65eb7beb4f3ec4fc2083a78e724b3f9640c96daf0c4625c2ecec763f40e8e41965fd883184ace9de3b38aaf95e0bd51f22b8621562f17bfd6d3582e92cb1badde3e04a29fff5e0a7d9b95771360d32b7fe465d6dee34c7ba7b8eaaa51737e0044ca231d2ad9989ffbd60a1b7c2c6e11a07e9b44f4bbf9fd10ffe54f26b877ba76b6e522d355e89bf85b4a40a2e5c786abbd67ea98d7a17ad5b0751408ce2fe9ac0d33f35820ef15eff1284fd49b0fbe7e4e0e15408ca6e29d099087b1a5894697297a6d16ae813d182705c4e404a44faf52a0e82ad55f126f5f00d295cc8ffe894687bd47ef9c56001ee52c195c9f1439c63028feb624630dd958ad807b73bfee03d50fe0c2f1579ec85d42793305bc0f1f9d5008af230145c58743b95cbd8a2dbed372111cf21a353ef1e753806c6510f7968402a36122b41a81b70a6b56c44d85f2f27b30e8f09953ba66c045ca6da07ef71b9b5c7de3f3f9a6ee712d14809c690b8191e52801fc343c2e3576671c203a87e9f57e88f61721b7edba8fe5a3da8b054095d964954339da9a8bfa16c8d5ccb6865c0e6d22b66827f02a591030a8cddd164f4cd67fba8680cae2f4675613327378eff0a984d01373ef0d692866be074edafe48dfaa72ef23007fa0bdff04659cef1ed6f373c1129c527e528bd352e60cde7961b09b5471b2ef91c8c593e49e63b3ba13fd5839f814f655845fca7de51dd1b87db8a8cc5ac526cab904a4436ec35e4c815113a575b2f008092844cbc93dc1a6e4fbc3a6de47151cac5605567280e4f8aa5f317b639cd165abb1418e68cd1d9879d4aefce65a4523c963c5727795f3a9332fb658ba2c6a9f273ce2733b24eccfdf8267944a8cf5d7d21567ae1735a0a78733f0e5f85f24fcb71849ecd0186f9a71e006b5d6d256610bd0bd0fc1c1e4714130e29c7e6b3b49a328cf511773b224edd436a6106df077799acfa4eab59db91eee4020609726b33b2010932b199b9b6fa3d25fd84642eb28acf1714f1b9df633e6e5d8fafb2407421832453eba4f02420eb563e1eb0352164217c699b3281b807e2cc1f6426fb07a41c537a73693f5c6e1ff34122a8728c5f362a14ea3d907a431191d6b4617ecef46ef183e0e18d67b1fb466051bb3d77175e0842bf5f2f2693b0b58c0aaf62868246380d984a54f03176912aa8f2f80d2188d7d7fbba4c2f0f18a749661ee84ce12c980d21de88b5d02d27320d542d662875f3ac5e18c01e3a2884a9db8d77d1d33d1eb00117aa898b6fc2b5f3091e4dbde06160be29e9b8e0f5ad70f216b35163da855d23879d60ddd8e670b2eddcb736662e6bdf94e54b914c69ff4928536e5ecb037fd65a43eb28516dc43fa3b819d2dfce58f422b74c983ed1abe969465c8f88b2961b97dd4b0f3d2244c518ed961ad43c62c3c44496faa1151f35d9c80961165b10b8cbd57c32a0a75b4816a244cb416be126151a5fd363b900791612f18396f53b0acad1588a5fee610cd8f390fe190ecac63d2a1c28c03d5c443405c9bb6eb1c303e637054e819eb605a44737dccd8951265b3a6f731aba5d4c47286fb99104d8334823034684d66d5c826393391b53748e70f761c8093e7721d59d398bf454da97d616e7921b384dabec6189b95980fec7b62c7c0234c0cc6f70bec41c5da955f9415112751d10aee7584dcef68218070c2dc5ee191f986842679dcdfee9cb2ef9f0eccf1be3ef6008d4bc1bf72b670a1605a39bd7c6a0adc8e922707d2f9478c67fec03998e567ac6151a9a617e165adb74c8214876ffa47316e7ebd059280c80aecd07f2bf754a8db7e71844d319d6a263849923b788618f4e116b19e169c6fc221b34714d0c7aa27048a555242e9a8afbfb05e68ed0f9215495468645cad197497d1001c41502e3a5c28995f2a47d06d0e28b3b28460ef01382e13f3b8ce5aeba01ffe56264a7d4d5e8bce3f41bf2da1fa66c10b9469f98648248a0842cd5f91451a94873511479de6b82ea95502c20ced1362a2f49fc3cde8b66f832f87ea52e63b9a5cdeb028537a4feae13f0c2b6a357ecb098883fe574651f49cb3f1ffdfa1ba82a6cfc5eb57828f5440eb55677d5ea28ccec6522fcbb27b819604855860303cadfe41dcb59148e73120a539f7daad6a261cdb4ed5e3626f49bdde102824dabda94ba184b226260941ad45981cd5140ebdbe0116bf16b1e0052020a28938c7063d3626407164b6b960f6a350e70ebd6efa0451e76046732c2527a674e1ba1763f3de8bbc2e0d04dfbf1253cc79c8f5d6dff0c8c51a6bbfd5f53010e7ef8f09034206dcd94663ed9bbd8685c33c6438af44948a325076a97f347d1323da01cf6bafdb3a171107eddc370e31799daf3e45421d66d385fed31665db2a4f6b9f5be4f12e7bdb8d5ec04e2b546bef45d177bfcf069db01162a3487af95a45b395988b107289b65232cf3713d7139dc2117a2a76e09dfd87de394cb501d4926b349721ea771436fcf532df287b02cb9ad7b2f5cc4d4420ff89e67bbba692c9e14ecceabbd6e36ed6f7ee60133af7422ce5583c85f933b09a1cb0f35cdc70966c2f7ad6137c00c68fe207173bfdc896193ad4f410528ee004f630192d664a658febbf3beec57b6d21e9ab31ba9149b76395397b3801e9d02a58774acb6a7ede35873181a220f2f071d6374811de26b52b50d5a2225c6ddca70fc78a1d40f97131488746f99a00c6cc21f599f30c699b7c051ef3d38a6d833f38e3179ba6c1626b7802cb8111c3385c2574c20d9937c53d18fc1956d693ace7c0aff8e2230f166b97f353c0a18b1c721bc01927f2e44a69f532db3255c3189defc4f059232cdd4ef73daf1a13e164c98dbd9b4457cfea8d605e881ed2c9b5b2e7495e711c41109f5a039a9e813d72b4dc519a2830d31c8817c0a695cccd42d513b5e7b94118bfc6f28683ff0f362d77507800c3cf89bf9e0aaf33f48b923ecc2502c30ef4275c17de9b43bf23c6c627ceadcc95bdfa583f999641cdf3e2f22d4eca9d6e591ea89bc1091961d2215468513bd9ae35349c8a71f6a6a562e202e87f21846c92c6b159a1341e34e79d15f3004f0a13a1d5cd3d603146c561ef1862088912ced3513ff5abac5ac360ed2b18472ee48935f881c9b32a158545326138219bdac2cbe442ce775b82b61aa484a350c707dd81215c99ca6977dd8e0de7238eade6122b556ef22cb352c5dbb3f7761a12ba303b50958ee058dbe4662192f2a1e51d7e21e22fbd76462587d675ff62127b47102867b5f6527cd0e1b06e7ac9dc6320e3f4f51c591e2e7b08197450b97ca55f18b4552dcfa29bba9e37f9ef79a113370f02de97258e90aee61d0b8b283401e483aa69b4bca20f3a6ae3d6d881de3cb628cfdab8f56eedae474c73286860a6444307ec41d9c91395b552970048d1108eb8829f2136c044e44419c0811abafb6cfad431825294ed3dd3123b9189627e3f5dab29bd85e49dc7089d9f6b1d8c1750df71b9a717720c83acde99de25422cb76b2c518edc3ed15e5bec8b5c7cc8a8060cc32967330fcd08bd9a7e2e086ab49d1a15b64fa93647903910fc4bcdc2821c5b0be9de26baf4996aa377cbcea4b140275ea8d2d63093ec4f224e8c85982cd66dc3b2624b67df5366fb535b1886d15b7e9e01f7f15029d41e19c8dab409b4dcc1bb7961775c3a15d8ff93bbea34ae36c70161208c440ec98483729597a62cf7ed48eb439012d06e1323542c1db53c5c39873d9402624390dfd82063b158e7b070e09629b8bd028e5f2d2cfbdf906524e1b11b22af095596b355f5e748c00a30193bd94555b6cfcfe1bfc1fb358babbd3e31a548d728901169bad75720ad94960eb7b331c17c000979a5886f3ff010052c5ed8c97af1f525251280ddf640ea55228cb43f69cc6590a4539e17e28705e13cdb8ae3dbaaccb1e27ed09aecbe95078acc45d451fac87359241d10a0445179acb2c3410a659c39cfbd6f64ae40dde9f1759da7e1f853c57add373ea33199b75c2436aa18685bc0b4b7367367bc85ca684757d258440b6a9d65df143cf2dc988fa97bb83f550f459f1bf36c4f897ebe579f50d200c3284c30643cd8a92c2e89accbcaf662e70f92dbd275416ba556ed664887973cf3c7ca5fff184b88005941c92a4b6bc6904ea63020e9f2e695a369284e2d0389fc30d7017f8b2b5def77507948d14e99a352cc6ba24e811344cea3153643e8e088db3524eb9659897c02597f3c32e1da0c5b4ce3e05d051b73bdb1123284e2755fd35ddd3df46888bda925aa55a2a3c63beca96474b1ead63a24fcf2cd836817c0e2eb2ba86e51f8159698272cd19144171c41f5da0bb486f32fc26eb27e8dcdff6686427b4f9aca9a318387c05bca2d27ab113a2068a3ddb04c50e8989b9610a2b8878a5100cfe29014480e2cbe7cec1dd63029095f02fdb8efb0a7bad5caa328ab3256b436505d47bdfa6e0a478ce115c7b7d71eddb4dbf1e7361a29395c45e1db3123481cc3598ae45f9786aac7406b5876a904e5e0df244c5640cc684cd141f90f86b4b7fd88c844061c98e19c6e6208cd273f831fd2b6e56c3e36fe09c32f26355c202fb403d99f7ed04e7e425c1d28321b72a78f90e3fdafad4c1d670ca678f4ad2f402cc1ad8b73e60dd6f39078aae4eb5be1a32d4747306afee451b702b3b3c0a85b9b62c3b59aa712b2353710073f733f664cc043539aea642dff09311d420371ff7f21474e9c185ebd6890288586c9f8b6c4bbabeb9b2172f1eeaa1a08c376581e91e4ba5502fddb51463982f4299eba7c4d2c1aa27cc3cfd77b9046b248f2cf8c9c6a400c67cafac30bb33fd965dd0e7dafa084bc28b4827edca10e3aa93fb09bb434e4afaf7b4e088b62c7cf27ffe96eb54905eed6a6b6b82ce7008e486f67fb1afe96e2a93253ca966345b2aac7ec06225a58dd55b725a0f137b499aadddb1ed5d477817ace0339c5105c890e7090c13bde6c8736c763755ee37aa3fdcd3f8bd9f4a12c847cf3cf64f642c0b1579ce65f916a0f492aef7147178514b1f51252d7fbe6ed27bd1556ebb80a6aff955469f8ae3b4bfe3727e45ac9d7515e641a654c82c06d64ad134674ef5c8753c75b0f6540f7ec45e57dbba8b31b7e170dd5e4070979197418568bf7f2d739f6266df30932a52281646f2bef538c7c8e005cc5eb5d6ac7ab0d2be3224d8e98f99ea412e167050824e0e4dbed100bee8939e30ded7110e3d83b1f46a51847cddfa0b40345563d1c1f981d6b73828d7ea50e914277307f04ca3cab7573a8981c6247d093dade60c70b7be5f381ce13dfc2274e8669d1011f3cc904166ede3a07b81b3d11d3b84d4fe264e3998b69a33cf628c1b1aa6b63960c5cbe78f0b5bbf780919b8f8959098e43b93e8e5f25f8e824fa1337190fcece7ab9dfe9528d4d386d81d02dd61fba81acaefbee496992bf42d23449fac7f473b2089f24b007ad0231790370cbf4310bfbb497f3c78326cf9e7ace0676b20dbd6023d67039f87406859bbfac0ada3b71c4e0300e6ce6158769a5e29844feb6e1b7c964baee78e6a04ff741cde18e1b1333cbc2280883d0ce76cc4cbf6b7be6786584ea276fba65637dd87bdecf52b28c2cdd094c3967dd058e735d83ea9cf88f57748fbb96b7596eff1fb6b2f83b22be7593afa62bb9fb9dfd1deae3043c04af2e25ec92dd67141d7d0ddebe5fa1facd9653293cd9f769df986f4fe2af37442aa6f042ddc79875818db7d36263507a66670e6eaf8a0a53387723dd105ffbb7a82e90cb9185e64628ee9e9f75b1eb12effd7c58a4687ea44fb93b6a2a27a0ea6dd9f266975fc7d0b061cbd33e256029aec33b91cfdc1d5b8fc29c4b2db5a5c936c307004fa21a97243a694334184e941c800b6ea17d1a84d550d0067ed81880475c58e331ee0b721c1fb22f26aae9727e805799efe98e672829fdf1e522e93ddcab80275de3dd3c95ae7b9cb8d70a003731b0dc0c0c08b64fdfde45e79cc71c17f35032cf9f4c30fc4189b7b2b2eeeeab61754001e66ea8fcfc96ad713e87bd5395fc615d3a543801625d02ef015a5d491d9717e8774ba094a80713b30711ab99f2c274bd584dd5a1958cde853c12f482e9c9a358e5bded3824203d4ab3bfcbbd42d986da56593c79608c4381f32f747d06e50baf0d8a576b5b54cb62eb5da00cacfc901b43443616cb745b3124b31689393c2ca8ef112abe5f4efc2fec92cf9d558ed467747c06a999f104a3c75279048e5da1678849cdefa3f3e07a0b2bc38c5016da47c06f4965e376019425558196ef4f103a4522613ce787be1aaa589c6909e26ba4b73a3f2648c6a8fefbd237bfc488c3a979e830d41f4ba892230639a1f57dee183e5b22f93cd5b48d81ac8d7a702918020efca0fce7953367b291f25e6e2001965f4b067d30a6824907f71ba86f51d8aa19571155a0f32a79449db9fd63398a7ff39522555e52555e014a0088e3be54858b534127c6f6ac415bf16b0aba31ae0eb66756451e63173a70d49b31fc4a7158d92da82cd3e1dabd5720a852da492a500371d4885bae4c38d1ebd26baa843f17395584c837b4111035283ebbd668b3cbeaebe64abc96e68b901a0e15866728b9df4ea8b27bd58462faaaa9bc3f50448ddf7fc9e00a5b88087e40c03a1cbe6c256e4bfb2c5d58451f3fcc14f25955b4f43dbd6bdeefe24b333b4c13fd5b1baaef3b644d4fe879dcd817c736d44cd118c384e3a2719ce9f3cf32eceaebf47ed2bad8ebe17401bb85c2e602c6cba5ff40aab8e821b0078f35649cc413de28dd8f9452d26c7318f08ab61df171374d4a376e8d011d846259d7dcdf63d285a52704d90022574364b02a3d202ab81ea58652fed4a890f8f440810208451ab45c260667c8de6c47c222378a7bd71addeeb38e7d6c58e6a5f2b387089d25f2c96b6d9c9df34dc514be6e02c6e7be6d30a18d92f16f92e30047beae18f4e9a50e81de1c5b6def6927dcdec0e82463e348ee4bf43ef1adc8bcd3cbc60622eda6bdec5b72874b169c78678ce710388aa7209f8e740b4541c7c686ba36f6f9dfee90ee4ff655c172b7e41ae8161b0a6d82ddc3a315d2620782a8d7ee0869656c3b77dd20c5a0e1b24bd97d4e8d5f3d5fedab3e101f2e8aa0180af33262a611ae149de1f689f4705c696cf7cf9ba55c08e350ddd084daa3b17b0bfb1892eb72a930dd5a9e2edd12bd1d15a53cd2877aed93c8f95db55e7a326f8d421750cf50535a4ecc9bb178b2903a4f432df0645faf1eecd771b025f1e994ddae1ccf3ddc29211d64c44620c8fad9a0326fb7e0f2bdd5bee20bab8fb8688e1e9f03a22c884a1eab9c91ac1c1bd30ae1d1fcdc9a273945aff44b800a659b9a5e2fa260aa494d1943a95b79bd787de3b43d88e8be51664baaafc6ccb626d70d1c1d8c662ebac8568dfe6fe7bea79f63bd3562e43aefcd46b2dff4bfd0fc561a7c2cd5faeb4130adc88f3ae14e2c2d2b2db868d73e07c83c0e37c8e3c96c4df06cb783ea0279a8838addab349ea567463917fc42442718a5114016a0f20374c7073ea601185774b2b91518e980097df2c2505c0535314a44097ef8da693148ccf291c7934068b515019b7f0a532503703c49913eb18de65c03af04f6f4362429bae49c4405cc44474c8a3e80cf2d5feca40eff94998a7dbbc7a61c11f8c9c99ce35dfb9b061351e8c368ffdc429abdcf42c0d0ff2d17f89f00d89533a4a937768ff5cf0597d45340906f3d035006d285c1e310dcb6c1c913288a1c480defb0797aa9e556ceee40c2064cd830d64ad9d43744d5889f17efea0db53c35afd17a45c1e9c667ec9a53f51218b3f3ae3d29d8a2865d98aedc6c98826099acfd4866c256d7408d8ec013acbdf798c4d5ecc168ec61bde6349d00f54ca9c38ea00e20e1e7562becba1f31e5daac8748480c21448ef49fb05487a0c069b06e6da7e3de571d58d8cbacf5843cae7487c8b3545a44128b90e1c39f1db1b890ac87f68e9d8509d9418a96741071ce39a751f207d6ccd52fc47ba05936a6d99c2da1ffa952427cd769d48d370aca701717351bc43f2cd6335b7ea16351411c3c3067f2b2b5cf55397ea79c2408b58809e1cc35f882f7181030e3e91e26197aba87dc1b3b44e2f180a91c4448be1e672ffee2382974e870ef2149a144fa30fdf57985c0f5c530183fde8b11275062f23c7096ed6acb1b1ae7800fe2facbb6dd2faef0038f85c07b06ce1ad53fcfcc8b3b9b02d1f623cd5d8fd59bfef11925b5a7d583764df5cfc309bbe9311a482ea9622707ddbc8947d5fcffec64f512ad425369e211dde0c185ec18d19974ddb9ca25522a491626304324d5f056d6ee95c248d4a886ff009b94d501cdc17848b05afcc2220f4ca7395f38e015a224482676d9d24e4665bcd138dede9320874016945f1d27f31b13e1a651d72625a47a6a9a1fcc7e4a995a1d92b51064f3920511ef9ed4f9bf3b90e9a9c4c23c48b59eb27b9f99ddbde7eff7a1b94ea782b05266eef06b9ac4962141b4f4f0b78c73fdba402e7e31df0063a02f0d42de87d0d601ffafac396a1a82a25732828084f43a12b16c18502f372c3652347028929c6afd9c2d186ff7383ee2fd60cf32db3c6dc7f35c46952636200075667a23e6ca78bf903c7fb9493e97aa07e14df9484fc369a72c9f148577e12d959c2a9e746b8aad966fcdb4f7a3317cb4ea13e4f4c9f5518b168e290bc7e19faa845463ecd44ace77f54db4168dd5423aa4f9a7465008454570c0601830e09b46d5a82e769e31cdd18a56189edfcefa87ef18c2985f17acf7379d11fafc497c0346ece405e068cbc726d606ff0c8c00cb030719871c1f3f35e8e4544833a949d63473132e71774b6dad1b5d91adfa56ae5f811bbb3355aec277c42bb7a1b800b17bf7f6c951b6f62b92db5c532b651674ee05f60a8179cb2dea37efad57b0b21d296882715d3847bf7411d65500d0e23b518a780c19494b9a0ebf71c3c1e6af998ccd32878f760ce5434e798e92179e28ea325bc1cc207ef8d42633eff953574df9885679c8c0ae31b52fece07617d159ac662ec3c715881f6706a520ec713fa93f7795ecf999ff5e63a8e1ccd9c5fb7e39f1a52cf5bb8da6b7d01b817e91c94681a63d7b38a660c6d69d8e8ef3f9f54afe3c60a2f1ba9e148aa5f168867a2dfbf1aca7ace9df57e470233595737d87277ca7d532d14624426455bebb241a2d755f46a79b0f2a2d81899177a2249ac2d7ea10313e27e72b91d47776381abbffc4c9590baa048152cba48d52b6155b621a305207e92e358938058e5d87aa517695d57402f0b937fcbac352a92e0473b55e0efbb57f707646240f1112695eca023cefe620bb76a1d70cb707731fb1b1e4eaccdf81a5eaa0959751e696bd3b37e324c01265fde017afad64c9bc42f7eb77e99407b535bbaf21c21cff8d038b43955c0b7fabed275ccbf33ac4738299449d9c1a881540110ab7d2e817f980cc4efdf22fb5d2281d864bfdff83014bf70c69b914ac39b5bf2affeef5c06f0cc2087a451383cc70883dcbc21f1188e901e87f6b7e6df232475c3e46110ca1b0c7d3175e7e8200d81c1dd507f1d97feaa2418e5570316a517266e9a10bccd0e32b7a83e998614ee2fa4bb477a90ee28587ddabc465c5372ec6addd79fd816bcfe533d035257046315d97a3debd0484cf0afe33b30e03fb1ab19c2c557301ec29992eb003319849b044f27952d5f9b4e7c3a9382d3b94e197f235fac6401cacba9f7cc6bb44a8ea533bba70318ee55cebee9a84ea67eb15117fd77e604bc1623e7b97246ac4433573bc14246ce9ea9b83d3eba7c142d6cfaa7e67a2cdcbd15e0b83c0d410d7e8bc8f369c071846128b5c4fcc7c7399b2e3101bfbde2f4b4f89de7585abde094f28291c80da70ba7fdf8de7611374b30dd714622f6fffb88bd11a163b9bbb924e4d5dd214239c43ee627939f762a22557a6d370b4428cfac06f86fecc0c91666a90bcba0bc6ed31e649790140bb3b69841e2f9752cae101d453e7ed5900d1eae47c473e8579b312da783ef40c621c6d5155abc04854c7d4be7fe450c01aedb1ddd27326f6ad4abfc0587f67e921004bf56d2b219a6ad06ff7ee80eb6234237fa6522726da7ef2df432ffe6989cdf663a25b09caea80482bf61674b81570237c9f4968d70607002990cdc3bc6225eb10d60d692998cc945196afff6322c81f9d8f6bc57cde676160e152c3bd8e603fcb7b2dc3d34224f04248fab139e99bd3065f76f795c25f245d8e484c7ae349eff55b270236c26277cf26db07700d0e00ddf03d8612c62f79462e40490fdb373e5684c96ad75753c2728916f4bf6b9e2de5a5b0a420a3a519c497f5bd0f6638ac0e0dd41ffac81bb5c61bdcafac49767695e87cb6c1e489d66ef64903827124ab30d41f0e36e5e04ec1bcfc0e7f538a4dd4696997afd3fc64e5cc7ee254bdcade7cf5f1f0909654678b18a027faf9f5268ffc1016f4353f12cae18b988c571e819bff3b0ffeff638a46a651765d12a1426dffdd97c07fac6cf286179e2297564159de4d619937c44ad2fb38fd80c5a58e882e8920413f4b02d07c2cb92ebe768b53a774991664fcec2c4a77b82edfeca914061ae2237dd23e8c67b5c2081dfd6659714fe8e8a322d5c1c0ccb7a290b6f10e15eeef431c54abe20289e9ea10715f55c9d2c2b14340ffda887f3c8020347ebc7c412b973b8dcbc439fdcb6accd3cf159690f79a5af551782454165b8359a015fc3e5703b430743157634b64e7be00a47a89178f265b26663c31857af0ae37f0197b61a0b3a707bbacfcf56c2aa038c8bfe9b676dc8a0c7f42fcc442adf1ac5b07fbd76579ae0424d96e6fe0242d470502be1a607b35b984e7aece20df618f4dc639eff295b5e50e57f186b288d67e8622f42935c44522e8535ff01b681ec61cc1d88dbf6d6a777acfe862e9fc433db47f0933afa88378a3ff940ee1afd9e82e4473c3a3824e1a0d59c7a71ef9109b32a4b9c5fa8dd81d6f6e150a940ed23e9f09c30010bcd3de813b845e1af6cbc368d5fbcf08aeee1185f781d94fa0bf017247e069177603e512f468770655af75ab52b37ca4572f969457099fbb0c99cb4055fc862547988889409a0946b1d27ef50c0b006985dfc431c419a93978c66a9bf60373983b9580506b79df86240f6d9c00d4e7af162d52845a09b1260206bde72314db6583b7bc0458697191f872e921c7def7a786d3013bd0df28321b2f69b0b103abf4280e5d50c3023c59fd823722090423b4e8e56759eb463dbd2fb22ef63ee72cbbdecac5d60774ca81b6e899606b793786286e56731499dfa77123bdb7cdcd1022e3bc06092b9d8aa9f75e601883efa8ffeb008cab7574fc67d5c801b31ee5702203e07b51b932fd94cc4dce77cca260dbd316565215233c5cb87ead4969a8a45b62b26e7472f63fcd43439e8ee5dc8a5877d6fb17900cc6377543b178c557a276ec704c90d2a423ccbf0c95759cd0d6d218ee2887f6b87cd6b82077a1bac195b9cfaa8b320edf045a91a62907fd97c8024b1a1846bf880074dff74108ba97b17878437aad6e0067eebe94a32bab9a12d0addac2bed6403abad03189c8e9d7c177c2b6a8eb8566772f78339edbc86e4fc14da607032cff78fccc4ae6b04a9623282308a6c8806f75c5053f0b8979d27ae1d0cf58efa6dcf4699410e8d9c9112ee45db32afddded1e1a6d914a73a649ae877351771535f55e1ee0a9d58eec55bae8044f07e96850f9b8eedcaecc936f55944c1acc6e33eb87961eee5df903c82ed92b5bb32bbb9b5a0e32cb76b729b716c5b84b9c516b64f1cbf7ddfee68dc7b2c28f8d7d19f0ef31081b92b92752eca9b44463d83b89fe7efe8eb6c76b8f07107f082a925f5123847e2fe9b5b8d88a3263dfbd8b34e0a55308ddc228e696acc68afdcfec64d790032ad8fea50a11eac48827c5528a89e82ca2d56087b03bbc1af6b785cb185f6ca60a39951c89509ab0157218a3f0991e8f1e1cc60ad0fa993091b476c8a4368de8369e58a5320a541a2828f7995eccf1cc492e5c65faef49f56d18b845a649b235c114bdf9e887974f59320a39a13540e8b5b0a0a572ce8f986125233e0dc81bc990a2396b4f56b32adbdfc530bb036cf3b013a643f89625d5a96436c731f13f74c9646a095271ce4c498a9452b865f0e46c7e92a1bd0641e190f7140ec27369c20f5f5cfa35f1a0ebc000ffc60443a5aa57caa8cac4bb4203ef1b4d33500f4086dd75e2083cc3ca2f75f5d879643c6340af8c0d2c02418205e87d5d0caadbb35e62208d900e360c943a6c2eef282a5b91f9b61fa1756f9463ec5d7df8642da513d9b046c37fdf16b1a685709215c8c13dec98170fdf851b782b7b44e61e968ebe1e8bffe77321a237d80826b6e28374b2764f78ef3b945bd21e6b9bdfb66fe508a3415800d2874115c817d0ef3ffe74991c7e9aa8ba0c879e27a0d01aea5f2bc59935de65b5bb9bd6b8bdf9c7c9d7adbc222945ade05d6fddff032546551a5f5b7f9cbf469566d0573dcf34557df86cec77d499f7b5a6b6a03963aeeb2742f07312da237d55d0f721c466e1fc4b4aa85877e2edeb528b24b61ae54fc13c54d60f5fb8bbc81de0eb0cbcc6b0f45ea4455fd5096478e7c8d8c288f12ec2bca1379baac813320dcc126a9eb9f115d40fc75c106cda56aff3cdfcfdc90668b66a1b8441c1529681ad4ce7487889a9bd3414fe4e4a138350c598fa47f480f473113472c1e6179c85c866ab8ebe4782bd379e8df9f2b975d423461efb0b17cff805ac84916ccd1ce87f34e4cae2ada5a057a82338d463078c891f64a5335f48698d526adea70c63ba454ebc6a279092e3333ed47c01af9e8d873399b80af16f2fde3b3e9bbb6863c12d0371dcd0d0c890e7da3fce8f111b227fae0bd095913a5ce3f25df18faf014cc9266be79865c8ff61f3cef47179a6d0ebbfd1e413f4842ba01079c24a9e89f22e33aa3581edb379e27f6137baa635ddd67ed61488f7ddd4daf6c011c5f62118c9afd46a2d3765b3adf0b8096a9e5d99168a4d18d036fb55227fae97069a9c691d24c641b4a63cd668b3f698ee0a4caccd46b6bc2a9de3a26d7da7d9ea3b9f12c1f9db3e7d755a6bb6f26c1608caf78b1176a05e7c1a63e55c431c507f141daddf14ae79141cd90c91e5139ce748d13f3fc35de6bff8e7ee4f7952854e865d32d688b5c7449b1e7daf056761db3ca96860ea19f7fcec534e78a39d1997535972de97e1812bd8c9472ef820f2216733f64f7e29be6e9c797727cc04a5f6371d92eb46901b62eb756d6be9bacc72ca540a309ff7b134c7c0be1e6c4af32cdac3632729dca5d07462d40d30ef5d70c97a2723c2493028ef484860c3560f0547230333333333333333333d36c6a6eb81c6d37c99545847b53f1a132536e4a2a55e2d3b9d386bf481bfe22fbb7ddec731a04e90ba60cc00c4984d1ec7144512ea743ffa83bc47a1a51700f21b98799b0791e461426b5e31c7c904514d3448d7daa5aa6e428a21c342c57773ed67d2711e5f4a0d2a3cc7e9cee20a21051cd55ebd63d6be71005eff0234f1d7c64751d4394cc3ffcdac47ed77821ca23493a46c9b6a23a21ca1e9864cb8d49fd7c10c5701f45b876928e1941143b7ef02073596ade8128a7bdb348e21fb6654094c3d29c9366f30f65f7e938c7c1478f6afaa1905f5a5422a6b90ce943c97f73fe40c2d5da840f654fe69ad371545991df4321e87fe60f227a28069bd0b8fed14b7b9f8742ccfa72eb1b5725f77828a5a4ab0ee26f78aefe0ec590657b7307f770d3dba13493a17b26b1e2a4af43714fbd537de9871c870e058919bf6eeb3994dafae358f26dade6e550f83856b266cf49cd8f4329bc7e7278b2e6928743e9ed4534dde630c9bfa12021fd9fb1774339cad4c455547ac6db507c0ff197f716d7391b0ae6c9d63269ec59660d85c96c665dfa31075b0dc5e49ff193aceb764e4351620e3ab80dfd60c38e8692fb5fdc6609922bfb190a16e593e1f383bfd6cd50daea204e3c2ecf5a2f4339daf0a95b09d5137532147220539af63b52437c0ce513897b92471743214f566a0e0fa929f43094ef443642f7a26c733014d5f374359af544cdbf500a8f63bfc8185c22732f9423f268895e9a644e174ad6f16154e4aab90f17cab1f5c7183c3de6fa6ca114be5e1e8c86d3f6182d1442c86778d7594d1d9385729c0357fbcc394afb0d164a9ef122bbe557287dc72719c6af73b431562875989931f5b7468e3455285fae4a7e8ede3b27860ae5db2bf338dafecb1033855247d2e01171ce4b34522868be8e7b3b62d0f4992894343f6fbc9dbb4ccf40a1183c4476fca48feccc13ca91fe2e25670f2397714231c78e39398cf3419c4c138aaaef2f1ef8760e25c38462860d3d9ac13743be84424c47bf74f8d5f9e35042a13e861c43bb459ef49184728449fe943112cad1c38a4919723b478f500e25ccbbc79063276a8472e7b8d2cb25dd4b528472d8abe181aa67448e08858e3ac8f598d47a6d08a5f5c06d752507f5a94308c524639a1ed5d5be7504a198838e63e6f0c3f3b83a805058d320271f678d9af383a2c585048dd391a5c70785d20d9f91ee41395a49b9ba0e2bcbe541b954b2078d0c9be3558b1d143bbbc715fed61da35ae8a0e86d113ba7aefd38548b1c1462c65fbfb7d597a816382846f0f03918d591345b8b1b14dcdc423f468d3196ad850dca39baba6efb241d86ad450dca151d556e5adffe6a2d68507c9f78e629f3eb573d8bb2c8c96a8fe5e4a85a160549f9e8c3bfdcb053c7a258d6eba1a45d94861a16e550621e721e668f3eaf28ffe6942cd53c5d1a57143cdcf06114cfe8f4684521ab07adaa7152869a1585f55d7309999e245e45218a7dcc6fcf70ebb22a8a72b6deb13b1d049753512875d75e8fcb3c321915250f226f089153ee977c8ac2bff984ed8e4d5178e9f8b6113984fcb814e5b6f7f03e68e4468f4951b69ee898f9d1c5453c8af2abb9484489dadb114559e7f43b0edc3fe50f45b1b3739c2b826dfb0e8a8286a0e93d08f39bfa27ca7947e3c6ccac270aa2e1e33c51d783c9b19d2854079a714a2cd4d3cb89420e956ddbb0efa1de4d14524d79b81e8b87215e4d144b52460c49de4c94254fa45f5bbbf3208b89835b7cdc74e025ca41ee309fd7fd5c9d254ad1816b44e4b8ef5925ca517aa4b1bba67b19250af22bf9d77eabfcda4994c7f63c73aad8c8d74aa26459fb219ebe2bae8d4439d88d78a692eb632d240abe11d2c38fdaa183f611059933effd5024f2ee88b2c781a49330e95cf5461437536aeea435b9378c28e86a687b1c5446dc2ca23ce1411e8fe2d73c0e4514ec36797e8ccc33e144948366bee5871073be882869c8d31d73daba871ea224aa2fba1d4b4ea31aa2949b23424d0ee2c664210a75f1615de29f7e2421ca29e6390892a34d6d0ea2601d4ce7c72107fa114194e338fbe4489a636318888287dfdf9373de9ca102a2b491fb1ee7a3c817ea1f0afbdad962dd618c9efaa190e2d3a9591e7964691f4a561de610a6a6ee26e543394c7273b8d57a7ef65018ffbcdd1f5bcee3e8a1e8d991ab4f43c78e9387827708fd295f44b3070f8510a96a543cc81d8a214e4bf89bf830ea6987927cb41a3e54f19836598782984d98707ff7fd9174288739487e9d4731b923e7504e9eac5dc4964321e968981a49377dc6a13c92b7c3daec2952040e258fcf52b2e647ceee0d25adfaa0a67cece3e686626e482a6dd35a6e651bcaadb29d3737fa934a3694fb43d209d72a9ef23594c3cc1faaca739b6fd450baacd30d59437c3fd250f4fcb9a349aff38e030dc597ae3799fc7b1f3e43c953c60871e632af6b86a2567fdc1cc7d1f34aca50de4d1d5f8fd1fe236428649188d4fe64a21d1943c1ab3fa7fd2d698f2262287a74f4f499e3fc38270c858f8ef020f36028bbc669e84f552dff0ba5fc3839ce1ce385b2baac24f7badf0fd385625b249fec12731e860ba508297164f6ffce630b45cbf99c9f9bf07116b550ca5cd65b2f711f52cc42399fb487fbe183cd12b1504c1d27fb103f885213bc42d1c343e89c09a371442b14d2e33ba4e679202356a11c87e704e97866a38854285a59f8c739eaf7693885f277b68efd8197840ca5508aec9511531885f24f0e39fadf1c470d2114ca7973f4b17dca84e8794279a4465ebe93eeee8462459b5fc7f9dad326947368f9aa5e1d7f920965f97615890e92de2fa17431efed9b99437b2514d35fa6cecee1666e9250fe507395d00fe6c320a194217b7dd81172708f23143cc63831b91aa174b9d1ca62223c368b502cf1dc7f298f08a5d07c61291dcb72724328a66ff6d81dbb5489370021943d45f8ab77f06184378020142cd5ac345fe6b5bb0100a1b09f29226f4e8ff719c00f8af996621f82a4c467003e2884e8a1c7bcf192dfd8007a50acf0a859b9edcd616c003c2847cf089247dfc46a1bc00e0a5599ebb3464c9bb20d4007e5706bd345f4eb07131b400eca692a33e72ecf66a20d0007e58df4b0537b42ecd0067083620e395ada6e75b09f0dc006e5aa8f3bf2edf87acf065083d25ada567a3c19d5b301d0a050e53937722a8fded92c4a96513fb9837efecd64518ec2235b787f7b98b25894279a4695fcef51cb60515c7df5c9da817749f68a82ec9aa8bca5df785c51ea3f0f45ef5e7cf76a45f9d5a3334dffd368172b8a99367c301fae551453bf27d4e67844df5245399799857d30bf6b2b15a53b4f1f457dfb785aa8289b4fa79cb03bde91758ac29fa708766b3b6952a628e6f8dbe3e31c79ccb14a51b498ef2026794eaa1529caa15855a40fdae3d46a146515d18ece218731ad124539f0283f316b6d8a5485a2b4de61e319246e46058a72b8fee94b4eaa24aa4f14b76f3dfccbafd5c713c58fd6103ce9cd69be13a5159309eb784e14724e57771d62b26fa2301d67329afe4588268a3b9d52ff7ef3789e89d207dd59653ca67e0d268a311ee6f19a0b0f3f2e51f2b7e9dc816f52dd1265310b1397541d845d89b275840d0ddbf0e552a2941fddc4bd6ac87712c515b38f5a73b8ee114914e36d865d9e969d9128d85766be76b2fe0e1245f938eec34f0edbf388721cc1d37b7c5a0d3ba2984ad3f6356b44396d7fdbd78c285ae4c03a9c0f1f8544025844b1ece3cb666a6d894711c5ff384d6ef45509994d4431c3a3c8ca0eabdc534414ffe3703e994ce86c92000e51ec510d6ae9c1cdff638882c74aff648e2f4439ffef47d16cb631c70980108c695b4a8c90626432db6a34fa4c3f96539d0ca2f869b37cd863d6317544a0035fb0400082286e7ca0fac977f5ce134020ca39f238fa98a30b55f3b01c080010856899e4a259baac7201fca1982d82da5d64e785afe58762c8f371bee3f41ab1838c3e943d7ef851c6d59475980fa58f0e8bac0f3bd66cc9f650888edacc39c89c6e4a3d1464d326d718593a72270f058bf52c6f8d9273bb7828648f1219d1bec376fc1d0a5665fa9a77ef5673ac1d8aff39a40dd57c4909d5a178abea7651d3a118f2111de9781c55633987d2977cb471f3c3944e90722845b6c4ec13d66f2b3cc87128deb4c71cd5f6519e3d1863948170288412fba8b5c483952880375c9b3bcea56fde36461928b80d08c00d7c5c5b842b8bd0d4da18f38a3ca32102684339e79c34eff5cc56522c2f2eb00013a0e0040d70c101c820a327e06220c0860d1b25003614253f4701f987c27af2709d2cefc5c30fa50c4dee31ddcf6fab3e9473189f99ec4c3dcbb4051f8a3998f7384fa567ef7f0fe51c65cc61cc933dbbc57a28fc64a7db6cb94d855be4a1187d32db8732d31d613e620b3c14f2cbadea7a2a73b7ef50700db13ac733fa786b8772a4a95a23c7ed5d626e5187d268d428c944bbe3965bd0a1e021848d0fbd3387424d8ee3b84ce57adecba1e0516aff9d6d42be3c0ee5d71ce6d3c912d6240387a24608393e0e1de24ff78662d6976eabdf88a3714341c395cc77dc59776b43e935ad65b5254245c5aad8820de590446462f489eb195f4321648fb5d5860851226a284d94cf25e7712431c53414eb36468dcdb6404339af94f586b2cf9afd19cae1296a34b684281b33943d9d5a555ac94c865b94a1743fa1f512fd83d6ab045b90a13caa395ed1ccbecef4184adfa7a93f7ee924411643d1839acf16d97379370c65bddde8d9de63a52179b6004339fa5af3a06c3e3cf52f14435f87f412eb3cc8f142a137c711536e4fcce9b145174aa393939947635f625b70a11ce7cb103e62d88f740b258ff5e34c269ecf63af8592e638656dbccc4ea7c94229f263a6f6523d2e1b0be5f6fa2b9dda9b8ceb150a2e36bd3b9f5229122b9473e326ed94940829ac42d937fc6cee4c591f795428f54832ef48f5f29c2994e3ccfac8d4b633ceb48514caa93639ba9973b54c060ac4f882b68842d9a303d94d3d7edac930da041378172302fb2e4618785b40a13415d7e1bcd3eede7cc34c182e2003b77842e123e62fb777f5255b388151b1c8c8329349933226848c7a9a42c1164d289566fee861def1386daa38b76042f9c38f3996d0d418428880093cc00b0554c00b5286d8620965978a4a8898d4325fe1417a87b173e0b659b35b28a1e8a6390e2dd443e658dc2209e51cac84ddd41ddce46a0b249456d26de4f90b0fa34dd07634d8e208c5a8f61fc97af2482118658cd128c00a630b2314ae4389cc1eaf35e4fc2d8a50ceb15b0f3cea77faeb8616196590d12628126c4184c27df4eb92e4e7ee7914dcb1c5100ae121de47c91fbef94334bf02365b08a1187eb222e43a67f54f60cec790800db60842a1b3667cdd53f0663e032928266830ca40c1094cd516402898dc8eb58d7c8a30193f28e6483a775278630b1f14bccd2fb35cea3baaa9c4d8a20745910e365a8ec6ee98048c2d7850d8e8c1e7bff32ad573636cb18352b876c7fe61f89e1c7243eb8b32b6d04161af63c6f49258eb4e0c0b44c0860dacda2207c5ff1c76982d254bfa67b0050eca759bac3fd3d548e76c71838298ca6b4b68c94cf1163628468cf410bd6f5183727785aded798e51e32d68904b494610f78eb950b9b28f24b3fd5317e9318b627b07752793ab738a298bd2cfb706cf3ecfd9cb860dfec18c5814d4b2c3245233b3160bc440800d1bb0a8e378ac32ef75d840c12b0ab719de83bdf8c8dd7445613c46644f7a462b0a2b6faa6fb62621115614ddc307abee711c27dbab287ce0d16373bb3d54ac8a729ab0954ab3f1ce4152b1c9a490b052261dc27d6ccc9bf6d3e35051b60e195a33b91b5aa7df04658821c618163841ff18659011810e7c818219a728ed77646e1dfda76607334c51d45ceb81e488f8724f186694a2b8be39f4a0b63149966790a22039d336e36a1c8f232d4751f438b732685c45510ef2965a1d2222263714250ff3d6c7955c196f81329e0c27cc0045397a583f659ff93a0a3f51bcc8f039a86a88fd314f14edb6dc73940c0db9ef44b935d6d5addaadc71927d88a999052b2c887c7b76887e2b1e68ff64d147eec3b3e08112e398e2654ad8a99d81eebf16df2f2fb61e59b199928777c51ad5982788ed30d2d177c09bef02713e384d100264c1442d393a6a45fb5c42e614ceeb0cc8ec91115c1325c7ef81cf7857e382d5188397efcc8c8a944c167e33e280fac3fce295152df48a23ae38c4914734e9e9ea371d755fa620c315012c56ad9089b324ee7a8138952aec741c8933aba6a98ca4230031285f8d197ad86e6a6b39e8c4714a277c895fd1ec457f18696234a12b2686ccb749b9c51cbcb27a2684a7c0623ca9aa4438d1f8b840dc94594c36f0eaa1ea6bc247b1551f418929f4924a6570d27a214b2fa81e57ebbde7943ebc9d09e8188524ab2cc9c1b3aa77ce48b198728ceaece6f764a8fbbded0aa324459c7f43fcc476197ff0dad2b10cc2844f1bdce63ab74ec3ffa8656162198b58822e125661e61be254adc3ff4204b841983285ed5f607cd4ed7cf7183198228e5799f878ba64788dbb061c3860d1b848319812866f08c1327a4b39304108516530de5d9e3df8337b4be18a3df04a610e04551bd61c61f36bd8f6e2f31ce45c40f4c673b9518a36dc31b5a4f861826305aa40fe5b79b1051cf7e4ae37c28e47029ddbed933e2227b20b94449089134a544dbc35abbe330630a0d9db11e8a1d1b32c99e8d69a91c98918742c450269a425d32473cfcd5918aebd67687b323ba8504eb4c09d559b99ebdd3d677d4c9d80c3b14ae63048f6d9b4256ab43d1c6d5afe3b9d2d8331d4aab1dda3d0e52e7fcec8c391455472752bddec676861c94d02a1d2e9f122376d9ae4dda34dfd05201195f5c1c0a9f6442f2e00389917f389463cae67b54232f9ddfd07a43314bdc9ca3b283d53cbba19024cfdff9ab6fc841da50f038e5f584381e72123114f4092e0333d8500abbb4dbd4bad61c6b28ade424d6efd9b1c3a386428e534de327526acf23cc4843b9ace38732e14143713e881223c3730755a9000228e813540961c6198ae937b89a9867d83c11c10c3394c4e57d474a5279fc366ccc284349e2677798591142386790a110e26d48ded6910ef10dad309a051fc6176388f145ddd518d088b7b2aaaef852b1b2ec58fec3c88fed380c8ba1a8d1a53f3ad17ebd1a6db5c25008a22317a9c3d758f1aa8c16fc21600618ca71387162b286dcf776c6170ae1b9eb73c6ffffca5e28d9c76a19c774dd33da85728ae0666a1d7eaf8798195ce0cc6423479ca984d421318f78f8f26da3d611e38c2d143d87491b23cec545e686960bc628a34a6b23d0812f0a30430b852bf78f9023edac91bea1e5617c190d28366c1820037733b250e89c83901c277e18db344167c01031030ba5f9587e743d07def9c2ca8b0b2ca06031e30ac5f00fc28497fcb36ba300052ab04261b3a493de6029e1c36e687de1618071ccc030a30ac5df88ab1da4888c69a6422927673f88f53fe54ea11c7744879447191fa9195228a8e9c6f01fc207727e14ca33fb1e35e581482b28c1e6236640a11c9dc7be7f47fbf39e2794ff25d54b66ae9c8a6e68ada0045f9c7e137c81e500628198e1849247edb989c49b507a5d9fb5f0f98afb6e68fd08fc8918339850ae37d58dd38a10575b4261b24faa95f459d57443eb8b2f7e0463fc09ca4a30430965cffa3ec9a21f9e7824a1983262fcb8f6c608c01823cf0c2414a773501da66c34a4f308e59af1a036a7c26313f49741c6172530810a0e525e5c60010818411860f0cf304239478a95cec1e75da608659d78af2991e38f93264239d94afef073f89d1f3c8462ca0fed8d5408c5e85b9a35c7991941287ee8a8d2377f9fc7f41795aa84194028dedc6a4eee1dfe4ae88696095a0529f82faa5a05292825307f0301f483724e3b1955b783d065faa0f02bf13de7f9d859562079460fcaf6bb9b355f27e3470086d30c1e14ee6cf2ec65eb8790e9318a0d1b3dc6dbb001c68c1d943a3b630e99e338fcd05e30430785e8712764af091e491883193928dda4e9fcd0ecedebcb0874e00b0fccc041c1834ab9d68c5fb50c0036987183b2872a9f273a9060ee8130986183f2bace768b87e620476530a306336850ba881cf9c6c7cea2e01ad92cd5fa43ba9445d15522ab4a0c1d5387d12216852c31dd22beb9631216c5cd3c1e27b9e99499e315c54c11e284ce397498d515a5aaf88fdaf71dcf865ab4a274aa13de93cb848e5618b4604521976ac4a01eaabd2357519e0e2746b9655dc7122d5451b2d89c83782669918a72ac39353d95993cc21148828a427ea8779def3ad4dc4e418e98646259a91221b15a643e824b6daa97a085290adf9d4e53c4d9adf55a94a2fc21b377ae64cec8f36b418a92c6f63053a665b41845f1efdf83dabde5e76f045a880251338fd8a21226458ee337e69498c0f2055a84a26c256a3e16390c76ef171f0618c709c4305661880146960bca00838c035a8082f808592389879774e4ebe547cecf91ab5a7ca2943d25d57592655422c3868d0874e00b1568e1893a264fbb939e5bde4e10434f6f0cd609b4e0c4f6299abbb792d28aa1c52632d59649a2222ed3e5f2314343ea7dcd6cb76ba1896287b3d71ee65c6694d8002d32510c9ab6f7db7446631e13e50ecd7ac52bd375ce9728bcd8479b35a325caa3f9c344fb20249d498b4a14acea53ea367b6c871285b049272547f1a8fb4994e3fe98ae3186c66824518c9b7153e68e73b88e138972bcafa13e869ea0f78144d12af23b68d1dc613e8f282695d1f20427bd97f51c6fb6fafc390b43021890801708f002015e5ce001e40f350e51fa1c6d62c43cf933f34da86188d2e6149b3f46ff89e4284431d87f10bfee39257610a2986722fa871d34aeef419453840fd1fe39a8970a05514e99a53dac6cbd570727a146200a6a9e1e6d13b2c7fc160935005154edbc3a714c53677fef0e35fe504ccd27137b77dbfe5938d4f0435123c6f3e06190b4a1461f8a9b32b2511fd799c47881800b3c400d35f85088f2e261f9f65e20c00b1380d165d41ecae6efb93468cc88eeeba1ac23a711dee11e3ecc43e1b575e39465aee49c146ae0a1f4398eba2672d9ef9466a8718782c4cc87a162837d60ded03a3fc6175587b8861a76287b98f5cbaac315f5ca0b047881002f10e0850d1b78861a7528af849ffc8ae438884b87d28a47f8758c8e5aea1c4a327fb17fe6cba11c9c759c4e79a651f338143ff0d8f3412af73a0c1ccae136ab9a66f2c8acdf504e2b33af1a9a66abfc410d37947b6a53e26d4a3b76db50928cbf3569747d36c286f249c40c9bf9484ab3867268d6c8fafd213e5d822e037b50430da510793d9199fcac7d1aca3f2b7b9f2c533e8e4443c9c34f6eb2fadf0f256728bfc4e0d1529ded223443a9dac169a7d77850a30ca5c8c99ab0b23976da6428fd884c0eaa3ed7c7c817ba841a6328061dd118315649cceb8e50430cc69be0b1c7d7a6c250eae99839c29fb79a0e06629f89dc95bd58ff8582c4fd31ab8d6f611d5e2825c7b09ad3ba7b74c138f521547cf8980b750e365d7abcd1322d50630ba52b8fe37c481d9bfa06468fe15a287e8792a2b9f3cc639c85c27de46215d1a31d9f19a106168a6d3e5a1792c3bced71854279474ec9412487ff850bc6288322a028e813848144a861855246bd0f726cf6331aaf42a9373df2de8f1f468e59a106150a7976f951e5a9dbeb53286a78daa8c9dd83688cdf163404bc4080176148c00b0478e16180e1002f10e00502bc70c13bc00b047831c6df20a319e00502bcd845419fe00bffaa2185927c77f021fb3ed9f551287fb49bbe43f397351d0aa59d9d90b1f2e309a7fa1cc77d993c9c50ce1cad3e733caed975138a6935d6e46b7eb45a33a1202f6316317fe6bcf312ca3985cdc57e9cf7e35809c5d412268a7cfee0f126a170331ef6f3a64828dddd86e98df15b62c8110a5975f5a1fe87d2b6118aaed9b1a6bd0d1f26b108658f422ad2798ea773234271367dd0a95f7ac71f42f125abc884a8f3a4114239c8c6ac0e37f5071ad508423970f73fcd71e0a259a7ab1a402879fcee7b9c659a63f0078556b9ef2499aae18372dc68ce133a65ef2428502f2ef0002f4e603640ecd4e84139af4c8be768a3071112851a3c288ec6c96934be837267cca669f65935291d94356fce1e72327486899d502307a5087bfa35672e119238289874dee4da7965fe4da1c60d4a5a1e27ced7741c44866da0860d4a571db39e7bdcc9892450a306e520711b52a75777de5a841a3428c688ee4156be7c1c7d16e578aea9e338f20d9eb33b021ab228be795a7fcadf4f537971810510a3118bd2d49cdc64576f6895a15e5c600165d8b042801754de042850c105bc98c003bc8045b966c7c56bd289fd1f46960d1b870d1bf68aa2fbda46ce797645c145638a3c29619b3406081aad284726d6414aee099eedb23ed0604551628ef9231d750eee59453173988e3a45a3b98e65b4e0bfe025d0504539b8baf11c65977e60af1e68a4a2a82e3a731e93eda64af3400315e540cce39a9e9d65cc9da29c269cea8725e181862930d9fc68377ae471f485185e30a0518ad2f544c941d8777f24b9a128b83046008605a24083147d487f1ee7c8346b5e37b46e98327a0c147c218693229b018d51942a5e3bc2c92b8a72dc69a5bb29dddb330230c8403bd00845f1f36d8ad84d5e1d6880a21c44f32c9fe44d4da29f28668fd34b2d637acc1a0d4f1442ceef1231443b519a90a37643d5474e9d1345cfb1738ee3a89e038d4d1473fe14961e3992ff38a2a18972a8a9224662ae3b7b3351da0c216669deac97ea0dad2fc428230c30ceb81213c5df989b3f4b76fc9dc3860d1b36acc8a04b94b46a6265ad7c43cb50d06368c0128518fce4d6aea3578738814625cadefdf21f798edd5037810625cae532b949cd56d43c93286ef4f587692389629ff467ea8d6603543ba011894208293cae3589891af2e2020ff0a205621cc0860dff1640a2bc9e9b3a6f5eb87ca812683ce23888f193f7a43aa2a01e3ef030b337a6480d8146230a1b666b37721c3945e757c088a2ef7856c6c5c67b0c7181c6220a39becfd829e7db2a29d050443965071f6ce7186ff16f68651108d04844e136746d4644fc720f12041a8828e674b11ac4cd32d57e88b2ebe7e42ea9ddd05a43c310a5192f0f22ba737ac46f6891f145ae8046210a31878ff058bec3c99b30548065d02044f1523ba4e35e35437d43cb05078c32c408e30bb331680ca2281242bfe45f0f6d911b5a5f88e15b5e5c6001240c1a8228f6e5768ee0d20dad4014a79345c7c9ca2003c7b02cd00044d93445fc94f30d2d23c3860dc3028d3f14a3467a73cea7922e1f4118606c1768f8a1ac1aba7122635668a7821ca30c14d0e843e9cb3f064f1a253e7af6f890b506cf8e1832931b5a648c32507034f650f64c97a2ed1e4bff74433540430fe51cd48efe869c32cc6420a19187628428b7f1d09531e71768e0a1105e3cfcb06c4de2d7641c34ee503a8fb3e7687535376188618762d6602f96b9547f03b41568d4a1f0916dd2faa8792a73171c5f988581018e030d3a94dbda03ff49ed1b5a43630ea5cc4164afb6520fb90181861c8a2ab91257836c544b8c130618c48b0b2cc00b03f880461c8ad5d3b29d03150ee5306fb2cdcb3cab8521c68621011b36c828811862dc40028d3710634ff2ce1ca223cf0426680424951668b8a1fce95327bd2f8f11761bcaf9273a7cde830d45cd9c340d496247fc6b28261f2da9907defb81acab18b076715da6b214943b13393741c860f0d859c6e68b8efb813be33101b1227ea1e2d29d5aa3ca6e3283e4c9abca134cc508e65ed364712d12843d1b443fb106ae6b62634c850109118c43b73dc1d3d34c6508e39a8f77b3fef8e430ca51a8b1c078b84a1d4d7e1be1b3d7d42d5031a6028c69ac858d3f29a3ffc42393f420ac99a77f3280734bc50fe55c938cd2e611a5d288a072bc17d5b3232cd8552865dc5c726e9d3b1db011a5b287a7c1e62cfbe67dd1c985a6025fc6cecb250eef7ec92f35774cee12c1528076860a1fc9d437bf8c8e369f25ca114b61e964689b5b6150aa2193bd098d77d6155a11ce9749abf1f0dfed11734a850d28e1eb91539ece4dc140abeefdb2529bd3677001a52287e78f4dfb599e7355a008d2814a379a70e3f25e60d8d188006140a7a1131c47b4cd5e63ea1bc5bf211390e6a1e3e27147c77734cfbf864c2d684720739e2f7b4dadc739850cee1586f079a3f7b7e9650fafd398dfc1b653e8e124a9ae3d44c9a3e4928bf69548d5b726ee7414239e99a6ce6e0b3473c4728e68f27e3641af3ea8d50cc41a73648520f91a308a54ae9e416b661354c8482a6b7f93c9137843c8462d27b3bcdd1be1d7c211452c7c8dc9833c7d33e08a5c80d5134aa6b4ed103a19c6e2b1ef9897dd47f500ee6a61fc609d1f6f04131fe53238790e379d77b50d08fa1033dd930893a0f0a9d4e5e3c254e75e83b287c1cbac4fd5487fd3a28985dcbc7d6b0e37d0e8ababa29592e5a913138287bb679fb30bd31c2e60645bf96a8a79b37e6cbd0b04139f86968e67876be73d0a841517672f37f3be7fc418306e58e2071facb743c3c8be2aea71db3b9655134ef1cfb89f6e8c78e45a923f1546afdeb2e362c0ec7cde5038f7945a93f360913219cd8ec8a72b835ef9ab8b135db8a9254568d8478e6a7322b4a3ffb9ae1a3cce6ce2aca79c722b5e4248bbc2a8a1e857df0336f2a0ab1b2c565d3454579c3a38f3727cfb1fd7a8a528c89d76f87ff9ad514e5388e1b23b27baee32845612667a7635576e8418a7294e0162185c494758ea27013f73ff648cbcb4c5194f32339e930c95094d7f34b87aa217310495094e389fc1d65bd8738e6270a39717243dc90931ec413a57fd14dd1e167cd59d289c43da44f848470a22c9a7dbb9a45f6d3b889f2c4788f6ddedddc464d94ef67325a46cffe2266a2e021b4e5a5b44a8821264a9a44e4367e9c3d115ea2741ecf844c85962869761c39aa68cdef57a21c64f4cf078b92fba044e1ae523b4ca923f224cad11173e049d55a424e12a5fbabcfe998931ab9489443e34e7210c3f47c40a29c23f9fe4b0e7b4431679f0631093b363147144ab5ec32781a510e6fd9718e3e37e930a29c437c0fb3461651d00e7aa2d9d88721528a28e698f65f27db686a958862c81ec4e48107220a79bbb44524de437d8862368f9aa9e30c51de98f6cb2348648d9102b62844d13f904cfffc4dfbd01684288668f370fdcf41e8390eb56a639ba611e8c01732d84210a5eacffcc993a671f90251fc38b23c373787aa0c106984b6046b9768cf1692b4cc3f8e83039b284c36491f874e2e1d74347104b13e134549a71d7bc88689525c66de6a26f7b08315842e2e514815631d355e47d9cdb5a10b4b9483fda4a93acb6e33ce862e2a51ba7eb7ea901f94285587f7d0e38469e862128573efb2f53fd3da90240a6ea6a9171d79790c3fd04524caf5a1a9e1496d2486c0d00524cac96de2778e3d22bdc40b5d3ca29c3b7af930379daf992ea313400600c8d085238ae5ff415d2ca53df81b510839043f59db30a2e43152aecc886ace8e451493a5676ef538e4fd684514e39f7cce9cc23bcf89285d646d330fb76223882885bdba871dfef9667c8882cc7ef4bb2fa131c5862859479b7c721c5988725c591f1fa6c4aa4d12a21cfa43dbda6f8328de7fd4301daf5ae788200af1b5b37baa8f942c06a29c436d9f91ef370f03a2ecbe39bb3ece5f9dbc3f14b7e5bd222684d6ed87427d6bd85849fa61b53e14a4eb56fd460a8010bae04321e549a6c9ebf469740f05499f1ff6471ff5e6d1851e0abd31ff98844cee5c322abac843f15fccad935f782844f0588b0eebf4d420c0860d1b366e056380d11340c0710205d8b041a5e8e20ee5e8fd52368796e9eb638792065789f1e3e88c76755187c2768e7ba3319b1b63baa04321a6a8f7e878bb9843217aaa482e1dbab5585dc8a1d4e1a19c989d5978ab5dc4a16c501770286eb987f68f8c2f58f061548da0c368082421ba7803795424adc498d76e286a871f62c760d11aff36143ecce1fc6d74ffbc391bca693ec8192261db53afa19cd3fd7c6a784779550d450d3b199348d250deebecc06ff3467e201a4a9f34e694072bb5d1339423a996f69de4b5493343413d244e4a99ca500e63ab4b18dd8cb10d19ca9a3347699fcb741e3e8672cca123eb1c5bf5c388a19c33db7aa4e8084339b3db36fa6b301436471abedfa2b3ed5f287b68d939bc93bd50c8214c6bfeec314ec65dd075ff62b2783821174aa99ab5e3badc822962a4c38fa331a95a28659a4ca5db7b4a0e9285f255d7f6f9d46478d60516ca13f7e315c6e8c20a85b5d08917f3a1a18b2a14f7c1c6b439248f0ae410e97d750a85cfede1adf53eee183743175228f427b90fb7a5b43ba250eef073b6ef687d5bf250e83a25781cebedff84e38fcc277e1ce6a64e386cb325a7d9040fcb26942c36c723a71126d3cc04f683659def48f41c59822bbd21dfc945ab84438fc97a2aa27b104ac2bfd5daf9038f3979474239a60f5fb374e47154720443b6588d1adf6346602a5bdc67638ea4638a50ac0cf2316796c72d1a118a5efaff9ad3c7949b1a42f725f91b33ea070ba16497ae913ece25375119ba0842517ab2947af05196dd40d83e73d0f6f96a3ae4f841f9e31cfa83920e2587491f7c1fe849a7499922377485d0450fca61a7eef6341375aed1a10b1e944a4259e7207e6a67cc0e8afe935b3de6101cbad0812a9d246afcfe103a9e83427547f591a448f5fbc101ee79f5fbb30721f4bf41a1fef40315c9eff83ebab0c17963744c88e4ce6e17aac711425febd95dd0a0a092a38ff7d78899b3b3586cd445726451560f1e6feaf4518488c6e29eebd6f4e83ed86e6051cc3aa221226aa476c92b0a1f43c7a33fb0ed30eb8a3f871c8dcea64dd5722bbc702b0f1ef2e7509515a5ccc8fa1c7b27f9385b85711f7393e5ee795451b68bd5ff707318a265dec0452a4c8d1c878dca0afd282a1ea9da9bcc9c22138b0f74d5d4a57343ebcb006301e68037c141082e4c717866b3c152a26fac52f8d2b91ebfb121e4b8a1c5a428caef9bc72182ce848f5194c3f051d3594896cfb628ca915e44bad75a99c9a1e032b64fd984ac11a2a0288711cddbce2b4e42f813e5aeeee439637dbe2c3dd1476af4fc1c3eb69a3b510ce1ab634db0ec60279c28a64a0776daa13eda4d14b284471f868ce123829a284f7f660df9f3de3dce44c1b535a64c97fa108389a2daa7c9b1ad511aac4b14c2b463bbceb12769c485250ed732ce548992c47bdefd538f03ef28a1bc07ede1878edb9f44614c738ba8e71cbe749228761ccf85688cd0fd238184a65b6c8c94c86b9a86986b16ee21879a63820e038cf3c51a43a29872f2e626b6efd16556955428e8137c416de0e21185a8d6f318bc3ea6268e28475c4ae7499146943c8ceefe0e255ca58611a4d0be4eeaff594421f787a8ce1d62ce2ba2bc1fc4ec419de71cf713512c515373d9f4f4104494639578521d3fa87ae61065d71c4fe43a0e325287218a2ac93c6895bf901c2e44c9e3dcd1be35a4ad9310a5df2e9d10b5bb1f64104ec8f3f87ad5d611b810c471549ae137211788527d04eb7febb439612b4094ec6387ff9f733b66ea0fc51c1ec34b65c881e64ce5062efc50cc13592dfea551aa5b4170d18782b706f510e5716add17f0451d1db8e04339e6cf71078f50d7667f512d2823f9c0c51ecaf1ffe394d936d691d643b13d9a66f97bca8e340f45d3f650114befaf030fc5bdd80bd1ed099e2a4e0217772886ea1c0766dbd933fa5cd8a1ec1d6c346fca915911c0451d0a66df7b95994387625c4feff16b8608d348818b3994477dd33fde905cf76427702187626aa6869043c9aa31270ee51c67c779fe9169580f877284999eb4d23561e26f28e4b8e2b93dee77ec0f3714463fae62bfd37f1cdc8662d8a879ba573f492b1b4cc9fd14b39f24c305ef803514acfc3f5ea37c7262d450fc385c86b8217af0aab848835962c4ff1c648c8672dc1fc72ab31eade4bc09438c08bc09438c306a0f5c9ca1d89e1225668d8f23586a86b2eb4cda99d89a31836528b947a9079a6b7316990cc5bfd0fc25b9d6abf9188af1f19757fb770eeb134339e618fcbdeda322d261284afb473f396623f904864278145ff3b8ffae922f14dc63e9b58bdbd891e6c20b65eda8c3b0f9e3a8f4f8093cc00b31cae82b3070d18582e76464bd88fa4ba45c28e750571f4287e7d14f82c0c5168aa799d424c7e221fb69a1b8795e63daf3e0220be5c82607e9ace3f8b7d5175f8618615421c00b03e010b8c042c922d56a88ecf17de4af50eeb03a6543cc29ceb35628871f69dce8791929b31d705185a28a7d640d0bc91d422a14f3c318d446bd6a6b9b4251b2c24d42dceaf6490ae53eef183c8e5fa7ae230a05f5bbf530ee840ca1864231ec841892ace2e209054fa969254f08ea93b61c70e184d2f594d668e647e7d001174d285c69a7cb5af3a831b342e08209c5ccb08f3f48fd786d0c042e965088292dd33449dc3bf147a055c62f80b950427147722469537f0e639c84727c125367fb206b914442517e73a8137396b03971718442ebea948788e13f3e4628f707f33af910a77753846207132a9ecc53fa3e88504c13db6d09de933bf3808b219472539348ca1c49b20b6fc085104a192d7d2e6972508fbb61c2e8310e1a701184f20739d2cdae3da541e5020885d78928d5e122c9a61f14e6a523c9904232e38ecb850f4adb2737124a22dbe6700cb8e8416144fd83ae0ec77f3e7850ce1ed33cb3f62ab18b51c6171ae06207c5dc5b699bc6e37fe675012e7450120f356a72eed9fc7095c0741916182301f9012e7250ee99d395fffca943a32a5ce0003b7509113c288c36010a9ac0c50dcab5966b191f4e75f488800b1b143acc614886bfd5ed1c37b44c6082be51060ade5bc0450d0a6962cc1b7122b47c9a0b1a14438ecb4432f665d0c82cca4147a36ba73d0cb1d59045596de36d9e1bf1400c4ca0462c0a1d3bc7be0ed12f393947500316e43e6df1483e53578d5714824d78a0db217fa4f78616d11aaea0d443ff4dbb8e1bba8c831aad287ec7883e93436d345107355851922ccff49c2bbbaec62a0a223944b4c4bf862a4ad21966efd7a537c335525108d26213322e19728af545a1a234b9a221dd8cda5ee7043dc60bc038011865a04002366c7c81891aa728a55f798e99bcbb29b3851aa6285adfdf7526fdd06e3b4b438d52145b3ace8f4bcd53749a9c45a0035f74a006298a59b4a25eab4362d88ca2d47136d69b77948fb7612316354451f07f4ffb7a7f539f138ae269fabab1fac8d23a40518ae4c1a564bc43f1b84f143c44bbee09517165bf021a60e589c279af84cee7965d45801a9d2807b39ef05976ade773a2202a92ed3fda9b2827ff124d95b98626b050af9f2c21234b32510ef13de3c3a3efc31f13c590830e7764643b8afd1285f70e6432a37e869cb64431e74da9b92341a7939528cd4ef4f0e769f1b39b000c151c616ca20625ca91d4c68efdb64e9173432bbdb8c002522b021df8428c1a9328ee4ff9f96584d1232076811a922884099f97daa621c7ed17653c193f8619056a44a2f0f944efc30ea2a9e0e8036a40a2e03ba332771e3d79d00dad2fbc7f8c74408d4794c3189183aa7a5851a9961a8e28aea68d1fcb90265f65216a34a2d471d2630e7360ff1979718107d46044c12652d6e4c8fcb299c0c1f8220c31d4868d0bbc09bc01596311e52023a60f377fdcd04a4514337958d9816ecefe9a88721c075125d95da406224a8b99a8b4ad050392401c0c0643a150280c08a52faf0063140000000c1610c782c1589ce96add011480033e3a2a3c32321e2018121818160e8843a1400010088801413018100685c2807030481107a1f601f86d3a39e541463532ba8bb6a3e72641b0b98152acd00c66a24ec6016502ffed1c54180fde1f235ab140a8038b2651468200682213de4fcbcb5f63641b6600d3ca95a3e77d333578f26375471941e2a1e678d5ba52b6b51f181e914b478459643fb716e4d4f115a9548ff0364186934461a84b1485184bb6b8ecadf5543f838ca1ab96b84a87237bf3d37cc603cf0715fd999d22eac809f6b6a9e8e43d91704fb4131de66650fd9cf6749493366c4424c1fb0e35892ba70666cfc1a0f7a0dc903259fb81c7528e8ef59feb0361bf613f1cff41fad998fd13aad74a39ea5b9af503c75bcc04d78fd157fd7e37bfbafd86fc072afd8ab4a86c46e3932a848604495123044047c87c5044e238dc756c3655d29a2a7dc7135f827a0380ca81a41dff3e205e9c2198353576184658db8c9128f831885ee14f027a20240d37dd36f74ccfa3a0af0782da824e308b740cbd2c3312327c8ee2b68ceccc343aeedfd751447d480a8a03c6de2eafcaa55561a00b5f60da87227a5128106e4509bafed3d43512f91566e0945465fa1659c5350fd6af3afdf966b42ae08b3c996427c02f0f93c8f3ea01a779f633d33ce39cca40d0f9206bc597a28d06e1da0231f2f3074ce8f7d705727cdf5a9e683756d9d4efb026129a2ce71ced86ef5dae1d2b0d6a6419451fca26f3b12cb318cbf359b91617c4aa1107236b2338a1d1b884d5dc7e309aef0845a43cc56867e5442fae65a467d2acc82b0341a429f4519a84b49b5a0762e846a3b9086f4bcb3c62f6483b547e6a3747a3a7258efc8616577e2b341e4d9559f78c02a33ef490d2b89da5119093fbf340aa257e815a2c8c6f04f3320997c53ac79c3873bed680d6ca8d26eac309b9bde90eb220dc287e5229946dc98cc734542220adf727605f7c2d84a3682037e9a61f95b592e70418aa383ea3abd233204810c0fca045a3a22394c05eb247871b8a4c2b893c606d001788db49ef13e41f693d2a76134bc6bf6401e1700852024a0bad0b157733c8aa48088cc7278ec6407fe9a30b463abdb2483dc19f0ea29f875b8889c7fe1387c918580dcb6ebac5dcbb7f0d182df61a8eaa628dbb860c224eb52a541ac70629ab9457b9b3794db823a7ca306f3760555023eaf1b49ce1e25419586dac85b21a35dc21b0119a7895b95ff08251c1315e6a50b1935f6f0d876ae2982ade5750357eb0926a2e795415f442f9b09c2047e8ea883381200906ef5a42f5497075af2b27e2c0b03c2ec6b75280779686753fa75fa56b217464d79303263a6254858141f37f82f512dbb4db1ecc22ee6aad3aed825a14e897299e44c31fc966a0033cfbfd6d6d2572a0125343a504c5218b0c1c50a7867ac0f402d9002665d4c0490185099501ace9f8483a4fe4a4fece067047341a120fd14911088026f725465e0d43ca46d42580337e61e01a0fe05614906d4b49910f99018e5815fc064c0fd94e377e7855a283f75ccda980368c1a2350c52170b4f806d4481aeddc29b4a4135acbca545356beb43bcfe29e997c7943a1adc30a771392393d1905784252e97399b954e003cb360e83da444406bac3a9ee7d6253dab3d84e78cfb67d436061af2682a0113416c4331024c84c706541ea0ef830d0b4be55a15cdbadf852b01d1222f16dbf38dee789da0121ce3586b0d3215891b35b6c40dd29d7322e2b1b66febc70763aa15191b01c9de0ce519218cef8be3e0fec0100a946d9f75d39a086cc063b1783002897bc63a04455c7d65ae990a8594628b0321e2d705285dd0e254c922bd5a4f171921d1dc0eec6c99013df6306954d7deed7e3d44f330ec50f66ea067bdf0ee02945797d520f4fb7f902d38346e4e1fc43eb082b80a0fbe44c86906902940a9c55c2af033cabd8bc9369e7315387c5548700e4392ed0acd57b1f02ed8abd0a39c1730d05295ebef3521397d6abe825dcfb1a805e03e7f43bd0df299783cb96a77f4c5fa3445604c26ed50f734993120e706776ebe7268c70388bd62d7bb45111f4ec2f6d9abea4bfa6cba17962490177c84099635744987ee7022dd9f27cd90a1ade456498fab077ac5955b843bd0e9a3c35ed9a261f824ebf2b3d1674ef2ca4a9fb6dde781593ee922390c06a8de314111e7c9941561b1c14f88b3a3f172cb20d1e216caa69f7d3069ca80d0e2e9f249ec1882e7488d26666b7b0a5b1bf367bd7719322d09ccc7e5197435f529881ccb1b95eeedac8caf717893aebcae798244f674af95fabd7acab288f826828ca4fdccfdf81dd06ab89d7c9d0a6aaa138edd415632929e9e572142daa6f5794465905707552dd8fcb3fca1d344219ea7f6567d19185b59cada36644c75ad2598a1faa04153b9528744bd8bcc101d520ad8d38362b1f2f9e85ecdb191c1790de4607794a21dd48192aef75374c4a479bd48a31d567361a8fae3e1181cd9a0b3a3c955b67054ed2546f38da3b493e477ba40fe04cc2efd1d90e034989ceda213f63ebe2308960a35a02630d62e40138f3edd04152b7c33538f711d357ef1795c5d8e1b0111c90c2ff8759670618ac1aa8857bb2b94c2e6bbdacbed4c74eac5d35c2f0110f0300d75cb9dc95b0ed7b44e3f122b0e182d99b547fd5c35bc9a36dfe6125fef1a76d11e0deb7198e4837d19af565b1e62e9d6a44ecb0d6f154b9fb21b2b728966901cbaa67f9981458e0e5ae33801b63cd77074e7c1d7f990fd23b576262cc81214baff5a2587d20502f196bddbcec3bdc5a46efdd273cc20fbb5cecdb52f20162d7cc0449ec8f1c181c7c2239cda307b3ed448f2e0613ef3b3f5ef33059c8289f6887c14e79124ce9f9c4a6c98180f72adbab6d94a6e18ba6c2a40d19dcd7a5ebf2f49d33d618faaf6157d58287820d31f2501cb2d7e54e514e7f765caaad17404f8d57a58d851adddb059a7cb62797c699ebd1875b1d3eb492b9bd4c9df74e3049d8bad4e23dc037114ef932295144639c041457a7fb9a65e040f4f60e3caed3ddccd5bfb2f9f6cbb8aa7ed25e7f2a2c450968906ff0e66a091bb5a64749ac41802683d0db0534ea3cba515b3aa930a198f41086b9b3ceb52bba5b8e8d561727a2392e4ed03556952d402a9ba0a13286145336936a7b346b31a14ac0c7ac128eb717e3a184269f5a624c16863b17578a8faadbf9753d8239f3f8c6d032e4ed35658872a76457f217ef04dfb0f20fd017eef35e144925bc643cb34a767fea10d0adfc881a76a75ca04a671330dfeeee46a52152479bad4cff43656e9b1c41691c156b5a0f04d0b28cd7561f3a12935beece4b27025d2fcb793b64f1919f32e3185188a16fdfb891d8461e1e204bf6d1a61c32c661de261ea1eab7451c96a16c062e30ec75a328a5ed5e67c07f1ad67bb3e85995b0f2660d9756f7ef379aa4b2cd9ac6330e3bd2d23b67afaccfd57aee00fc88c2a2482c6845825f622254c653896ccc95d1dd6be3f26608b5c102b12d10796814445b9bc04f5ac005b83c92a8db2c29495ce91b3e67bfd2f310db34fd2292a2b95d8b1373a9f66232228a7d444cb23cbb2040c1fb257a5bf902dc8f5ef2b8ebcd9479e5ab4cb3764da496800728a48f729ddbfc422278e956b9ef48dc8abbc80121289d17c855be4045b20e4570aac1af7ab69439e4992343514836b60b5704cb130027afc566b845f606cb2b01e075d85992958cfc847c4bf5f5009340f78a2d1bd9d7212320f5c697c81385a1c22ca2a8903abbd9a8e7698f2c5e95a794c85a8199ee2fb8bc928e20c22d4ff52159a82c1aa5807882771bae0e2f8eb74be11bcc91a28414ce751cfe9f74c7ac08e353d7a2d3c749eee2a3c50e092861948d79172f63379c91e0374f98ab0e1d8e9232bd6c59c67bc39638d19260f805470c1c2687aecb2f245b20d95282d6f7b7d7b5bcc6da001c068f0988e8cd3720becc2693913d24d03eaedea678fc289a8deea1732ad75c954a52f5020407f89dc97892de1bc759dbaf0788e064aaa9c2c881dec8811f1a91fc827a644f3328cf079afc75828e7ea7e49127c7f01829685504d1cd9b674e93986ab176fe3a7272875470f011c04d862e7832d0455082465c1a2557587b7794b256734c7445ed8db6d76d5fb27267a3a8b9bb15113fa7f872e9fe10f3231d7f6844b287c77ebefdfdfc912bcd10e424e048cf0c6cf629a9cc772427e86889819274bf9c69ceb5eb19d1f7468635fefbf6e186b464e0735fba197ff0c322d0461a34ed883e6e5a227c7e75c45581b919478619f293124535ceceddc23de2dab2fbf85ed1f14822c7cab98a3378743e83e3789e46f19e57699451c62eb59eb47d2c04250cc802608ac050e52ec11578f557c0fc53d348e10af0dd68b290f0cb64ede247f7c3dc21352f93a59c7f13fccd7f544f324120635292d8d14508b7f1acef5fdee63f0507b77f26527b82639f9282f03557006045526d625e182838683c36b4ac9ca88839638367bc9e90bb99061309772909faa4aee4b33183d8bbd23fe3feedb0f4b393384c0353fabdc66569ff90256186e51566ba66f0d58e47f70669bb987aea0d9fb19ded6ce64e962543304ddd647886d8706732ceee3d1deed0ef410b8cb5b209a892af292201eced2542b5da18edb83fe75422eb69ff7a3b6a92ed16c6762a4d894c171f383c9e397d53ba496cb2e260a7cbe8d838681af09079c28bf9d97c5bc01429e62d11727e249bd6a36ae57ea46cd1f1922c387103c66f0a7dd41112e8d04591be7915840991f6e56b68128aa286f0df50e256eedced61347adc552597b48314a9e17eb5fed4a36a20288d2e85a69f4c7ba6ec056025f8abaf0f43b1d2eced9fdc1535da63c428c43bb89b8932e85e37f4c2c27681286bf1e77cbf1a23f35c6a99278e15a76f4a628e0e52c4845a7084f9b532e7abba9300be8dc8c03da18ff48bd385102ec84995ba34f00505933433e84f47a42979e34fa052ce0fd2213f09c9c8070bc47c70d0ade0ba8f5f6646152514167a8e2dd9887e4ad8299879510c0d59904cb57861bb8281e54ee8a780fc429dfd1898db919e8cc1d35ac6233806bf993c9df8fc5f9aab4f677e21166786ada5772ccd9bce9a97e04f2c164a7c8d41e8b341bc75b6fcedcb973186cd2f17e1bf9a841f4e4d24281b655a89829b7c9f878ae7c00faf0698a12346bf5fd04bef85b30fc24084edc1981c9dc3e8b022cc6bd965309a9ebc9d54ebb4a7b8378dafcb732185a820990bb101cb077d11f6b5631b0f75917c3e4d9f3a54bab5286b7e4c30b922852d1147f3da02040dd914769786f732aef158ecf45c0a3973bfcc04441320bab5532232410925364811cf297357f530b3d65ba0e9c4aa246822741b371d9409100d998f02906b2ab5a6ff97d559886bd1beb22332009e0cccae749454b48dad3ea96bf80bf096cd13122b7a4268377bd4cfae8a88e395d2ce6226d883c99e93e69fe717fc2cbb7b8cf09213926055e2282f5191981e684fbb3c9ad896f99eb26e2b3b7d39352d8c453e485f57aedfff4fb506e5507c5571332c5c16e21ec3cc42cc8d23a5b80488e72c3207b5a52ee4eeb3eb92e6e2caa4ad3d52937341f4153ae7b22f643129501a204da658b90b87b88e948743cc3eb1ab39e1309b18b3a3744cc15731eb1d6cbc787b3c08b88d1ad38db1303e30a4b72d6dfbc1c035cfc0c65bc43c417e89c67efc18be5c311e535cfa45718e7d856a9799cbe03234d18cb7948916515f246a4139a0424ddf24112e6710e5ce73b9e0f7a85e6ae255e172b281b53397e8f02e51343fd4c4494ae956c55a8d1217c0dd99b4a57312000de1b78aabf8fdcd2cb92692e65b592c3fbb6bacf4cb3dac99b96cde6c669894f583b511367e756f1d243c640c74cb084b4bdf48873ef15d768b9ef967c17a0a1ab569b98fa7a64ae0b403de70fcded52c1709e95dd6a392186bbaed36372fb5cca17316a614b12364b2b40d3c5c1d6c66886dcde43648eb4dd00f5713603fdabb04a30412c0b45d986f4100843c024406229640b8005b25a4bb1ad3bc005e757fd231c25b7cdc792ebe9ec3a6847ed74aabdff365684e274d7489cbf527d5735e3e0766437a0c72806e9c5c2ed79e116c813b8bc8c534e4a2ae68518221ecec0169b8f7c811d0f5f85b97a507cd5a2acde2664dfc2e43569d8fce6b60a7bb96a74b57b36af6992f306bea15b9c4795940bc7a8cc7db436050c709d4bf5459748fa0bf93096bd03815dcce3ed350c410b975f2c9436d8230dc7b0ef1f4dc7270015e233d6bd843c0e6dd957712e1da53a1695569c2300f00b5490f89a3fc855910ddf25281b071488260f2af2be39d796c78f68b99149d142f75e39bf9a3fc2c26620e9521d7f21b6247bd613ae492c951b929d350e141424c59bf8fab7969bfd66c78b411cf17a381001ff894194a0e8262063d46be7726420c73bedd6ed41ab4b27264c1230bb45b8c46c0cfd897202cda8418598a1dd45543e518c439c5e034148a620eafeb34f3c6105429bc8ae1b077d5c7d72dab263004160add9a30be16a2d1e207cf5efe2753040c6b392fc77986f9fb556bd7c06a2e315cd47242268f5b285c9d1852da0666c5a5d1aafc5989384eba7ca39a66ca2d1a4186a486a44a019321703e4acaa4e00f655c11c92c8452bbaad432d93ad06de3dfc4e801486c673ae8f1b94102b51c3673e69c62e1bb71c9e86bf624bb0fc47c60c0109f87447436040ebaaf65357b0845074e0927f075a439bfb8e5f3d773260c7e150dd654f931a64b27d4808e571196d439903621cbdd9f0b2de9739a2d5b6db129f324f7b8209f3a52110d1b73fe3b41059b1c82548318ebdbbc2d30687f4268a1ae0cb5fc8b6119ad23050fb3d4f22b8b30d892947dd320a291e63016238d596f1c97aa3668a35f3037504a5e44ac7609f55376967158e6defc7ac5280a088414068c55cc990096550c8c8b8483a2938582712c4118e1cd5c87de7e0ca21d75108c2f34de51802eb32f3e29ab45c3d861b6bcb9ad48dba344038b172b92a36266efcabe7a4dce9b9ce9383e342b6c88d68e60f1fb320e0a9ae4a73255ce6f7606140937016a8d158aa8612127120f59db5e9c13ab7f539d813a69039a1f469acf3586cc957a59b822d227882cdeb2c664bc9d4c2e62be5126878b318bf6854669301a4521557c87400f9322092172e53984f06e9c92f5a024460f93a64b82ae0182d692c7f7c216f1d01e78a8e8f9ce7a6c474c5309ce44ffca052b672b535746ed3297cd5131f77f72c81b335afbe6042e78d65194d8a963bdcaee9306ea4feb470a5d5715782173955da3bd3d89248d558b8a85349425ea1a423d0a2c8855fe91e90d5679f1c7c2a1d59345491398277530d8c3023d5bd8c3023d5bb887857ab7400f2e400f0c23defb0a1045753b36304a24d2a6a176db7e361a756d749eb43a50c75be5b2151dd937c6a2461283eec830862dab501e4111b282f76b8186891f73f3b6601d900e9a0517c88cd0e6e7b5548df7591e19f256788cd4c15855fc81793b8aabc3112891061121d6be96e3787fe9d122b703838f414a6988aae75d1adf120326a3d1e5c1bac90419969fb395ad2012a4b89d2020174d908ed95ab72a5ed29cf4e56099ef59bbf31c5a89185cfdd7a5bff4044c8ac62356af448b88eee05538220e3b756734c2ccb3b05a1da6f889c14fe9d898c8e2faf35fc10259f156ab4935418a774ebe7b435aaf3bac8c2b12abaaad8d0cc8469e7af7196e2773e36656f034041af41ac00131cd3ab1aeb6dd7d5c8bebe9bc04b8ddc0ef65f2e64e14e350571867c78c00fae8b7de4d6e4c47bada5036a0e2f9891885da83e33dc7fcae03fff49ed5c0fe65dfadfc6c437f1bcd28a6f83ba85929847adb8833dcf1f26ab1cdcd3e37849481b9c43bceaff837a6f1ddf95aa4d98e29905f61bfc8e6863e5b1d936cdcaff4d1e4c8180258714116d28f54e304761a154f58231229a98171cd5836f169130161a2c31796fb4a22069986a4b3328d9ea17513aa23efecdc37d43847fb99fc1f08fe56c208739306835589fb88fbd8c109c5e7d88b15ae881b1c569ae6ca0215209ca228a161b20cec5dbaeaeb2e72a11ce530411f9f6e1fa69248d0bc9fe369ee7b27d471f367624a31e2255befd68c2b4bd4e437ee7dfd9a51a302df6ec9c5dd4da00ecda15a5f947074f24a4044158fecca5879f87e31b49bfc4630e808a0cb0926ffe518d35cca95e1b8e5f35f316d5fa286870788364de120e0b2862c72b0f226e77b8ed7d8dd4a7ab7fb89ee9bdc57306db6ba4ffa324ac40f6040c801ed7b2bafee2adadc02dd872bdc120c9284288260c8c60cd89d8e0d2ee4b5d07255dcbc4a0c024ffb010bc535f1f951bd8b5576aa54101dda999053a7cb14bb48f7d2783cd5dcea665e9a55f03dc3f4ea79974138cb399b8c9cbb72ce53aa1f2ae13c14bf2963925539abc4b763592fa2a066280e8710d74faedfdb4ac561a6dd4b16c0accd6c4c749860adc9eb2ac599779195bc7632074dfd2c69093b2d4419ae8fc92b388d0b19a8b0a6c6e007c5494f72019b0a8401d78ab384b2217ab0ff7da50a6b12e1fdd3ee5c52bc3237407bd115209a8915f3f2b9de48c5507121cfc29879302a2bf891dd83e678fda8004cecdc1eeb520bfd6a0e875094f90a2f4ed2c21e041448ee88b72a39b4bc00a92d3cbb38bcfddbbdabe03cf79affb1d4205655c2ff585d0487afdb68bbe254251380b153531c6d4651b66d792ec0cbeb42a3628e32a7529e190eaee817a82a218d2aa50a9952915d3a700b29302be2851096b0a7e14f86d46b83b241d45cef5f20fc310561bc63a63652606ed94e7e96452c403369a89170ee48cd6bb2ad9242e2b9c88f7483b4204a8f44962230df1a03bc4e74f9ce3458c244847ac2c15205be09240d96785d49398d065e1e418115b7e5ad7f4dc8bfc9b509e17ce760c08188180e777e0e8087d0152d6ea4078873da5f6810f1d848a65757d206294ea0118f0555503fba8b5775587214bfef929c30f2e5e5443603965c014954af90c350a5d7135f9dc659b83fd2f934a364d7b0d4317dac008267e659c47c28bd4b5365ad5ba21267e5feee4e415462927cf43c1f41b14b5a8b9023b6b6ab5669b7dfe8884cd1f7abdd36edcca9331482ebe249a5ccb72620713ec61105890174926fd172ac78e2101f5ba363806157c95dedeae13419d1d415e579b5b49f64e82304de60f5caf1d7407b001555630666874885d5fa73185b5550555c0bbb3fbb5c32253ac99535a4387ad76378180942780e83667484a4a0b154624b8675da9e147f7c6e209f14c7a6e7135326d11956e1cd2a133fc92543406be65063a595c8ab2dd6dab3dba99439d97b8e94f95f3491763519004dc977194aaa057bb7c8ca03004b4dba80fcee1f4603b8d085ad5368d69a3cdbed209dc47b18b34227e1a7a2daf72c2d501fd6f26156078c2e99edef7fd4d3a7bae88ddf86b6366760b8d09ab13f8255f0995ca54db47c45aecc435632574a4a6268132c7ed795880c789585768c2ef0b1c44598ad5fd276db1113719c392c3c733c54a03fc25c184352a14eb01739ce2bfa04e6176890f0755c5214352859a062a715b85025deba8e8fbce8acbee0eca2199b34897553cca3bb8e557d3eb1e7e19562d0482a06b16414c7ac1134f1561c39223a8baee0362580d381df23fc45d30b0cdcb22037ecc2c8dc48c01c72cd30c27f6501f215fc3ce212033d4b0fb9d19f0a7608592804a97d72a71024b49c307dbea98b7009930ced10dc0d6102904c570e5e268302facdf9da44ca4cb7f49e13fae80d0bb4efbe32eac21cf5420fba06b6b3908b04341bf3dd7a4671f16a6d5abc78a3157c5cf525f8c2493697818b1424d10d404e750720d55cbb5878bc914e98fd863f7f66626b868e550e4a8f153e34f6ff7e5f45530c0b24b773c2694592386c97d8cd7907885205061770f4b8132b10e028c2855772be81458242353a58dd9d123ba9dfaa44b594bc9389a8d6b2b4094f515add11e59ad06d1929e42ae2b6995f66b90c2866444d15de69c0a1b1de24afccc0a6b48277207d5c232999ea5d6e9bdeda802b6e8a3985929259e6acd30d3d22eb51d5c7a223be78d88a4279d5a47d4db54880ab680fb3c42d69a57c2d33e7dbd4424c51e9a6466520501e08dbd28add8e3a1dc941d7bef90c3ef51e971cd91c84666a8212ca6d9027855bdc31726447940bf65899a5d0be6394b5a0b06029b1236a00064c63587d8622d22482fc5b319758ae6ef73d9820152d320eb753cfe41442bcbaf0ca76d3e434bee90386a6f91ac733c460236c934eb91d2b716689815cd7b26e0bbcd3b52c2cfb7599565ab8961dcbde8cbdf20a6699d8f042afc5ac5bc0682c50ae998c68b533d43922198e7b694e05a58d9740fc68a8cd0b5e870647fc3ac62428c2d14e2c3ac0ea2a548ff1919da897189a25bd5570860b7acb57ebd86f6605f9ebae7ea767f9a6966d454f5e6e9db19b0ac9451dcdabb46f66145ec2f3c9be6ff777467c57cd4a7798ff42e7fc365ee6b18c82005a10871694bb0551bc7b4a931892200d1596d585bfc5aa094f805d2cd4982ed68a5909481be2d38aea844f0031d04f44756945627241d42d8c88f1d53fcf73b93ea75a8d4e29a9581e3818d63b200118bca383a648ecb4adfc30877220b31317ed54afb01fe0b1bccfd1ec4ac5842118473bc678a3ff504f2ee4a74aa772a5ee586796a5c4b336bad19b5c64372a2e4ea025740859f06f219abfd2237c5fe36996f244263605f09aa3739b2fead81fcfa3dcce91beb56b7718c7321a8a084c370a5ed047ef005e909919963caa899f0aba268b5a01559ecb41f4b11c98db3e6bcb11ae9adfc7ab1c9d237c58777f38fb53cd62c4450a6cab6570ad8c79bec9fa0bceeb3f0997319d1a344039511216417427fa42102f13eebf92da4a9bf1e1025acde293bafef770497c91392cbcc070486fd899097e0e04a1f219c65b426456feacd33ed4010ecdfc839afbe40f400cc4a791a8d4d9b2c453b1d0ed143e2f7a8895fa68120d1d29434cc2258e9604ae062f37190b43e36db16840e83afb9df06aec4c261f2bc39f9cb64930368db9968a43fe1ea7a729047e8fd8a8789ddfa586d3a8336cee6b69e314fa4e925a4159c0524612ad3d65c1822cc3fcf7b1cd3f9100aaa66b5d6523360d3e3ee7ddd4bad148ef5ee9f4cc9b322657982be58e5a5000d23bb64356edc1574558814349509a0e22cdf352c810255d4519e62b0d758362081695665eefee64228408e72b6312443312079ef5ba8432decd329553759bd07cc529a5eae6481b2c20f69fb3b665fdeaec2bc36a7400508ee6889ca9cb1726cad9f82594eb5913a78eb768e72aa5dfe998886bcc8c520d8bd8a88bf0d8d20c25dc5a733ace52d5505afdf676318716cd44459bb0ef3a70b061bc46cdeb79bd01e0d9866f0b39281b5cbcad795d94514f5ca228c948f70db02bfe9c67b81b95f240a8825c51b5df42a2d36d223ba3f9eafed274a536f49d024e54443d0b3640b0a6d0013c0c3c0c3c0c3c0c3c8cdfda7db3334b424a724b0ecf352e2371f45c4a49a62453ea01e7d8dfc65d23f6139dd87750d105120b4d0b3b0b979f58dc9432a23434714c62ea793f3739a491895390b376496e92826cee6860e2a0929ee79a762929c9d9e280c6258e7f2aaf9b99f989ad29cb6147c312c7682bf236c99c32b312072b2989419b98d24eec2971363fb1cbf4f5bdc8486312278b414933ee61f2844912e712c37cbe2889c4f146bffe49d950332f240e26ae4663f8082d571f713eb1945c89160d471cde04e9252f591adb984623dcd4e55e9aa667c431c68492bbaef4d87c110769d59b478813451cab7466840593449cc643a5709659ee2242c4415474eb89365e7ff621b858bf979ae4d81027cd948491637b5fa22bc4498610c2a452ca849bac123408718ef1ba744998123d4c833899586a6c5cb4823866af959b462daf7c204eda366ce92d1310c7f4d7ba5779e2768f79a0f187538ca14ac64942658a314fa0e187d389d1944cc2af82b07c1f8e6b26e575b58a679bc28763860aa129477b38596bfadde87de2f689c13ca0a187835e74cf19b50c17f23c1c4f0cf69fa5b615b6e3e12077835cb2cd1d8e7f59bf7362e869cded7036e9368d8a4993682ed5e12435b364f0f65f9d133a1cb4e952e2f958dee77012453643d706c9e16c7179c3791c879318ad2e6fd01bb4b586c349beed9fc9fd460fbde118449d92dacab42425bbe1f45bd29affbf88d56c1b4ee2bbeec93fa37f31ca86a39d98477e943a21d3b986b3d86a75a6b9a8e160a3fd44ec2a6938496ef516636349e6a3e168725eae5f49f90fcf7092f7cdd39643d85a678653eadca6d8e695e4caca70d4a0c56be42a329c4ed0fc975df6319ce44c4a2835cda2254562382949933ed5379d29bb1cd008c341ee9c64725a1e0c27e99f7382924a4c17fe2f1c6c83aefeeede0ba710427ec591ab0e3fe9c2612eef588ae11566e5c2e90479f94e3e4972db942d9ccdb73ef3c347f4bf32c8d0c2299560925ea8f1b752928553599718424923c46966031a58386df48faf245738e6d7ecdb125182f4da0ac760322b76255872135385e3570972b25e655e0b8ea851e1389671e4da9794dbf5b21b616820d30734a660383a40430a27d91d19639bbd9b5665018d289ca412dd922cbaef4db95038870ca5e468067172deecf1830c1a4f388e127facffa432193c16030d271cf62a64ee6c968a7ad28463ceb649b265ba0ff531e1fc1a3349c24bc7efaae440e211060e1c377019a481c6124ee276fa2f2a5352df2be1b8499d7fdfea08697b120e4ace2446a833932c9320e1a831e9aa135350ba24f511ce257d26d164c83eb1c44638e5e649b316939e49e1221c2dcc4932c42d98184e229cafe54491196ade34398463fe36cb266713c2e97236def2e95b511284b387f8d094a374a70a8483d0d46725c88911df0f0e3e9724953ba3e183d309ea3bfdc47c559a62e006183b98d18bbc34c59d686ef1e218b27ef3a2b1c410c1b801c60d66ece23442647c89b1f717e21066e8e2585a752fe6d5b8a736337271aa133bdd52b198470517e7b50a5a830c3d262d734498718b53d0173c33c5d509d1dbe2287ff94dce74e7dea3f7400ba3065d02bb1a748fc22158418e0fcca885f2b33a3a67333976f040333d98418b73bbd58976bf5a424fb338e5107929a7a5ae2596c5712b777f668d17e3f4589c64cc26dd246b499292e43a7a8481b0389c3ea9c49ce65fd6e82b4eea4ed7e5a9f8889a9d8f19ae388ca83357bde07341dd8ac3e912f584f293e52565c70c569cae42c90e0bf67f5959c549ce944a926933236aaa388c7cb14d82b852a6a6e2ac15b337bc7f8a9d507130c16393f8cbab1cfd290eeb15bd8412839292f403678a534a16c32e979284df121be8782ec57943739aeaa928f73d294ef1e545b7b5bae41c85b12fc98bf93413c5e14a5ccbbf74cbb47e284e42086b9337285d721e3974043340c1fa499374c95675750eee199f38c8f42deb660f1e3b74886086278ca752ac2495954cea44698390e1f7199c386df586d2e8a58493db91199bb82b8612c4590a8b6b2251b521c4a82d1307f950213575d3659838dab95e36cd277659b9c43188d7fd7dd11035269638698dfba2bf412e2e5f89c37d493fd394dc24497044cdc7ba6006254eb3bf974eb62b59d2a86c1247ad0d9e3198269d412389937cb92e9bbe93fc4a1889c3b5a674217a29ea0924cea15164967ec9be6df8889394344c9756e8d3e43be2fc268467be0d6ac4d92f337626691d23ce1a73c53b938b385ac6bd8d15fd5afd55c42543a9c5aa3549c46983cf6faed589e984117114bfb7112bf1d486cc214ee944bee532dd0a726488938ac9d4d2955988e3fa98ee55ddc6dfde83470edfa1438c1d3a9010873729175e94580671be132a2374f6eeb762411ccf94c50813be84f92810c74b62570c3ad46b6e538e1d3ef223c7cd00c4d9b469aeb4db8ea8d5a07b1433c18c3f1cbf922484f01f3dcbc0dd07fae198b29968fe1b9fe9ef0dcce8c3514ef49eaa1294d49234a296a306bee36ad025305782197c388aaa60b28c3ec1b265c61ece2952bc7545ce46fc33f47012524bc593dd16efe433f2701293be9d86ae2c1bf5197838a705ddfb3f1a4de5b747ce3830e30e871d1573995ff74e663e00831976389ae6de3d79a2da359f518773891ad7e46a5e9ea6eb6e84a1816fff40961f33e870329d79e49cb4952a26cd98c35183f44c616230d1c2b5a3cc90c329a824f2f64ed2d6a3b21e3c7eecf6f01f3a300333e270cdd8b7b670389912574388cd1a66bce1dcefdb59394ed2e3a53d61861bce176cdfb3726511178da855d98c369c2c7f946a57362defd970cebeb4f944680d8663861a4e326ab615eb509683e13a7a84813687196938c9a541c5cd5a29ab190d27e525e6fbc7a4f605679ce118e4555349f9554bccce30c349098bd14dad9fe612ce2843e95ab4284177f74f06e3ad578468d124fba1638c20cc188369b32a975b0c8b8a8173dfcd07560733c270aa929bfeede563c2196038891e4de65d172f5a72c617ce27a950a559e385e3574939494ed6d3c38c2e9cb2426c08f37ecf1b6770e1a0514e50497783aa351951cb513c31630be78c31a937ff6644ad52f70823ed28e3bd0437c0b801c68d1eee19b0195a38584ce2e51325c9b0d5ccc8c249b869851f1d9a195838254b622e9afc47d4c6e0d103a51c37e81186084a8003c75916665ce1784209e659629edc57ba47191d8219563878e5c93ef91b1d5667156654e16852927f62ab269a34e2c0910685195470a38c16214fe9499a423a546e2a8dffad91023af3af5850d69b3e2bcc8882617eb38896fbda2843a1df20fab5549c493561c613acd2703188a5149a5953a8de69b6962588194e3849929f8ca741dd2309339a70f82f25f8a85132e1a8ad279cb293da5ab7259cc5c4944eafd204a1d2cebfc47d5ff624092769f46dd2994ecdc68c84c325e937c54b594908a1231cf496983fb615ae1b34c2c1c49d5f0d2ff1e4ae453828b14c4fc3888970cc12afdfcdd4284d0fe1943a4ab42427936b2684a35dd8b13bd3a5ad0ec2498c3a352a5c09ff6006100e8d29fb6ab696e0a31fac1eb2f19a61860f2a4986e56ed40c05e8c549c985d7ea2b7b5126bc389d987459ecb4d899510176713a3156aaf04bf927bdba38c6ccc8865993a6f4a963a406397ef8d8a1c32e51010870d4a000b938f569baebad5efec87171f0be8c9e5d792c8bd22dce2347a9b3bc0b6dc26a8b830891fd55b2415fb702d4e228226b4e53d25af7cd0a0a400b3ced62524af212cd32dbe307195228c02c4edb6f29289da5a465e934060590c5262c66a8646eb39758a8ab23b533bb8776444d4d0f0a000bc3c1a300af3829e9b5620c931e1b972b0c0713a100ad28002b8e2987c566523ddb1eade2b4716d4d2e2e89ddb12a4ef1e226399ba3e4089531c61ea100a93866d70a977f458e3231a8389a50533954b632cdc929ce973bb64c9062ed5f39a100a638b7bb68c915e36a4aca48978408e2247f5bc5e6eb1d3a60d0a3471203c60e0edc00c33d033cc2b801c60d1f1670cf009610ac2087009410128853484d7296b4101773e41942007112b3dc9c85df60716232e40f87d731ddae3f77ba8446d472e428a50c217e38a694dc2ff76817f3b410d287832ab952e4e94deb22b6e3c3d9b5bd534b4596b43ac71e0e72662ddbfc2f88103d1c73ce4c729bab9246cfc36984f6bff892848793be7c714426794734bdf687903b1c94fdf6695c7ac68d1d4ed9a1beef8230a40e079992f0a75dfbf575a943081db6202c36aeb6b8a63715217338756508575306090f319085c8e16cf2fc6430b9c4768590389cf5cc524aa325e56317110287935ca34de593c3b2c738a266831ee3876bc0072242de703c61f51b5c93d01fb78709c01b42dc7092d56d2c653e493c31d68683166917fe64f7d831b94184b0e16ca3f6e29da0adb91d00cc10b286d3fa9c8ceecdcdf6550944881ace275c4c5a4d7e3d258f1f068c1b1cc081a3478b5146084400061837788491af0849c349badd11fbd39524137d313c4b081a0c070a39c3a95d2e2cf8260bd973f8701f3e72240ef80fef91fcf01c56a4106286938cf7a5c41c27ee3df621a40cc7ecfc8edd8b3bf94c0ee0c08103478e588490e118fc44ad8ec6bd30a5319c629de8a2f6f5ffee440c07b129fb82b818bf360ac3d98431ad1bd54f545843c070f6ffcd3b69c5f594d0178ea3e5449384fb5971f9bc70ce603a43cc25bb70ca50e29b98294e5bfd215c38a8db9482923516b285a3c5d25a954aaafc6f8521440b0721cb84fccf88e675b370caa0c4495a5a26aa5823f04208164e55569684d332f7357121e40ac7d42688d8dfd60ac768a94b8c6ab40a274ff72f59dde493926495e00618378a043746f0811b629031860e63215438a6e68cd9215f52844ce19cfd269a65b84011228583ca21644afea6c22a33a26696810b89c28540e1b0592658653ae924429e7090717467bf690b1b5b10e284732949df429c24138434e1b82a9a4a7a0b9dd9472b8430e1b849845566bc24f534860859c2495295f425e9f4c4a8b7e4184294707ecb4aaaefdd6a991292849324b37d5a3619241c568445519996239cb2ce850affde17428c70b6602364bad8b020a40827f9db4c924ada5421840827a1c4ca262d49c28413154286700a27871a1b6d4adbe9236abc234408214138d70927a364654d551523040807bd7137282b9919c2726f08f9c179e6c5c48a59c94e0a317910e283533e53e7e3276df9264dce01a417e7cf7f0d2adaab415b9217a72a251f328a7a2f2905db00b28b93ccacbc2ba784c9751c407471f612fd33a924a1de3a482e0e7a3f9a549e982cc606828bb36c9fc88fd8b09c1709406e7196d1385719d4d9670c882d8e6be5a9765a07a9c5296f854da29a7d93201d51cbc123951980d022310899e2040fe935cee21c4abeb86f72eb5b09fe304b1e20b2384932a878446f84a10131c468306e84f181911f3a5200128b93a8311b4cca098b639f893f66c13b1ff60690579c45ac64a94a21636d8c2b8e9ac34dd5494997c9122b80b4e2a084baa765bb9c60312b4ec2377f6564688c6f0f026415c7db978d76416cd42f2d7f0051c5b136e8deec88555271944d767625a395829211b56230e86181911f3a52b043070c74f41001af00041527156b64c6744d5aba8fe8097688e02ced28630c1d1c0390539c54e55035c944173b494c7158b5ab91b1f4864a651a404a712c49657ca816cdb6162505bff143e50b130d183c7e0707ba00328ae36b4e8b5352a8cfbb9c0144143a0009c5292cb8c5906695f48907028af389498bd698aaf7892499d51a26698962a91f3e76e828f703104f1477a2a2e5a12bb8236a3f748ca1430c3510dc00230537c2f8c08d528c8174e2e4256d595e1bad0184134755b91835172753af5e00d9c4295cca7a7a6397d476324d9cbd72fbe8c96413403271520d63e2090d260e3298c9aaa3bc33d606728973a6d09542cba8c918648973665e3a517493142aacc4955258b2d8943ae6cd434bfe36f165553e54cef86a004289f365cbdf716f2a9bc429cd5aecdd51aa9298d3c0f146181ad031068f1d371083c708dc33b0e7230320923849f2c49b58b934cf71a043bf001289e3ca5d12db6e47b4c5769cc07f74074020714a59369d25495b4619e5b07cc4d194fa2cd9d64c501a88234e9f392b9e9a3709208d38a5aeb10fa94950529a11c73e498bcc9976110771319d1a97d308208a389dbaaf919333efde8938bdba8b18134c0ad53f214010712cfbaccbf7df2a227488a37ea5de5ce63d635a431c4b54cbba71a62475152c810029c4492e799794521f21ce9a9a4fb8112a437316a369c0a30719641435800ce294562d2cc58908e5b7208e9ba133a2977ec7440371da6b8b333742fccdc6069e769cdd010410a760328556db3517d383c17a00f9c349b09c7117b2b44ae47600f1c3d173a4965d9e3c4a2999e006183870e0c07196438d0f01d287e3f965b4244fc6acc83870a08f1feec347973a80f0e19c21665183b8a0641161a0c3475601640f273f498834a924d92d623d1cde74c55872e96a13d3121c1d40f270de4dd7123345450c207838f67be8f82d490cd3271840ee70d6d9a4a2572e9b30b69501c40ee70d966d2da874fae44f05903a1c2e366a65e5cdfacb8da8e5c82780d0e1e8154cc9a4f5659210871340e67092044bb9d08d8da8d140c71140e470fe362549312396bb4f4b0089c3299e94cb73e9672b1240e0704a5a339d4a92fc1b4e61d46bd7bac363476e38cca971ff525b1356d486f34835414e5f98177db3e13c1bc65daec33493bd8673e5d5a5384956c3e1376bd2d0db27a846d370aa9355b7d35a56e4060d472f55756a41e68c3acf709271a28b491bcd7050139e65cafb329494e1e0a271ef44d1797f92e1a4bccda42acba7db311cbfe48cabf29db6b1a30188184e96e7d5ae73b35be6c370f8caa65b326949ff1d0c271d26b309c24ef469fc0ba7542dfa7aae57fde25e38fc96e8eac68b652675e194d794b4997da3ae252e1cb4c589f15a42c9f8c916ce9b525af432afee4ca285d3bfd9c8b552926c1c65e1f0756dcae384b0709e134aca994992e53b7485c325157ed3b292f8695be198b4e7e4cb262ec8acab70b4dcfe37df91e1aba6c249c8ec8e6bcafe25e9148e3e52d36a848a495251291cb36b53926bd3289c949e55f3711f179942e1682974896661a455de3ce1a06e9485a668bd76a6134e2a65f253e363134e95c45d7ff9ad561a99703c75269c6ecbc91bc4251c4459d0352555ab9d14251c93f80e55b97ca7c59284c3aaa6a0728957d738124ec9bafacced231ccd4b1052b3093ab3c508678d233c46f684915b114e3267cc8f2ab55c2923c2a95438d96eb3408670d2fb53fa4ba6a494aa8c01228493ec6c3bd19a830148104e3a476c63ec53265534a20d0284a38990ad416ec818def4838376b90deacfa4ec1381f8e0a46c55833c396f4e6869f4e2b866b71beb6450371e0d5e1c3ff4f66a7d75ee24dda0471829a0b18b2c9c5965aa7bacb6a9c566404317870f214d9d4ca67efc948ba3a6da2fd512c6c5f94b8cac5cf919748c346e71d0a3d48c8d88aa1e255b9c4bd693ac2451b97227b538d90922a3496baa2f515a9c82094a94bfb057fe178d591cbbed629dfa8a6b5a16c7e033aa4ee566aa6a62719e93fa2c73f86e8f098b93587b1aafe3bc55c5571c6dfeb269cc6ae9bb42c31567df686eb56efed79a561cc5ba439332bbfceab2e2941feab4c59fd9bc8aade234dac48af797bc638252a0a18aa3a9b1d798e4e465a9a4e2e4e773e25f0926dc8c8a837e91d14564e8f5499ee2acf9a21a2a8eb7756f8a9352e96a5a3d830aaa2fc531d3bbee9536bbe41529cea1e69a54adc995ee8ee29ce92d73a15772f5258a8312134753a8249b7472a138d6d8b949e99294428b40713eef2b99d64433d1449f3897feb656d9ef2b95d5010d4f1c6453fafea89243df6c041a9d38a5b025a951b17f42c938712e614aafc8743293a83771ccbeba09a9e6edabc87912e74aab623d324a29314be2f49ba792f5fba9d27124ce967262d21b935082742171da1d4be2eb9aba5bf611e77d6b97396d5ad3461d71d89a9197c433d327d4469ce43025a86bb6d6b01b461cc5e413ec4b83594a6216710a9644f88d65147150f1daf256626e93c9441cac2437f13443fbc524228ec1525f10b1ec8ad1fa18448d439ce4b2af5c552936c91067d1f551393119370b71ee102a9f4979d2ff9884385bc888cc2afddb3d0ee2202f575dac25b1510ae2e0e255a2ae64184d2981385cbecc68e13b56d400c429732c5e6cd396b9b1d6f8c3793ca4c5ad94b2cafb236a39f28cfdb0819715d4f0c3b943b78ffa7635fa70ee3929689162a142cfa3c7b5a0061f8e27c336ec98ead7ba20a8b187631284e54969c34e09170fd4d0c369db4e124d95ce64328b066ae4e178e9d428d962497a4e6944ade0e1a45d4e8abe1da63f7bb11a7738accdc6132f95cb34281135ec70d66c799358d91a753885ffbfb2ca1153d1840ee75f8b17b61a459fca8e329e6d50630ee7924bd8aa144c72388c9ed871b741edf72c83470d6ac4e118cdc26a1a5dc2e12493e472925552eadb1d3ac678c34197e9cd08559933ff6e38c5b8da9755cf67d335a2b6a3471928078fa434a8d18693c52e6562dc68a2b565439d39b332b2526b384951297d983e37d5ac1a8ec1b64b34316f92ad13428d349cdf2dad8932351acea5d93793b0164d9ee1a4ff37d4697eeb9d6e869398aa35dafa5acc8c1951cbc12371106a94e1945f6d94523bfdb241644057988de638078fa43fa83186f30925aafa92789eb9440c27fd5695e4a93a499b65442d078f1486ceaa3559dc7032a8c0908fc85841563458e30bc718da9d73e1bc440d79e160f1cd92e59f6c92d4236a5d38c86c93f5d4496a35642e9cbd92924a4d670955d5885a0e1e297b50630b67ddaf3c7a37a9b44d1951d3410d2d1ce3854bf1f963164e27a5afdc5c57b7760e6a60e1a02e9f50d22d778593768bc95e7194d49d8e647c50c30a07619b67c7ba1b51cb4186f720a37050a30a47d3b651132e448593852c4989269bc249f64a347529164a2e49e1b431059d9f73b7d6658ea14614ceaf6175bebaf4c68fa0705059d31a5434a9b2e8c450e30927fdd5e5be49cce65772c2c937f454aa7f089371130efa37428be8bb2499cb84c385fc56bc5439ceca259c74fba594f2a478a156c2d1c4726f893125e15c2c359270d02674c3f4e4cca2922ea88184939b7f2559724e4f291de164a743c6d126e5306a18e124be79dd8b10b727adab428d229c524d4c47e992d67f27c2d14444a970c1c4f593b11a4338d89c1e9b2bf100193d46a9218463a9adb2350d67b5e11a4148ec5ab12bcd2540386cc56ff9af669297fec1c1eb368414939fe1dc1a3e38650e9984cc6ad937687a71cc7015194446ba8cc68be3aca8c99c73f2c478ede25256a2bb7bd797ba38e90aea4dbf94942ff6b938c64bd16252169a6f755c9c749b50f25527b740d3c2523c0b6d9f235b9c2fdd8965a2788813bb166793e5b4fd69ada512438b73aa8f96bb70334d29b338c93521d79915277593450ebe88c541a85829546a3a2127068bc3d58f0aba9b4df6b45e918812ea62860ca57d7345257c6d2593fb3209ca56a4f67475c887669a15e8924ae782b2789372abe0b32dc8e8134f945561c5984c351ba6928ab734495d4a2a2b11159524a5ca1593a4ea1455dd9694265a668d294e426bf5dd52558a539a3f7fb564fff73a299075763d323a8a649e5071fe4b5414c54992c28fd8de84e2a0a1534f6a95676c0914e736252a68d77cd247f489a39b8951975eb24c903d718a5196a24b2cbd7fb29d380933b7ebdf52b696e5c4f182126e35bde4d7db4d1cc4e43596c628a91ad5c417974c9538eadfcac449f89c6c396f76f127260e6e7258d5e9f112a7ef0d1d1b2ffaf3522c71301551736a2cf867a51227a9da323a739bbe3c4a64da624ac7c618fb491c752ea39dd85dddd02471b098c72bb65b086b8b0b5f4402a573b3cd0789746cc59c4d23db233fe298ebf26405cd2d6a57471cf544153de1ecce47e50a5f34c214a7d154d7653c694664e26c64b42f16718c593746eee9aed35f11a78a37d24f584961e54fc449ae8bbc2c3e22dead2db5551de2282e27962ce233d47b439cf4a4d69cfebea54fbc1087f99c911bf93ee226c4f9c3f624715256690771aa3b3d57d3a197a28238daca58de132fe6aa1488c3893f5994204d554c9200e25cf5df3da296362bc91f6e3d2fa5de27ae2ce487f349b2963e312b9949faa0ae9598f6663e1ce498a07ab9234509427bd04ac98c17849be0ebe114fc04dd1a6f4d4911e5e1a0824a524f9ccd1356c1c3c9e337c8935bdfe178fa15542ead14b5b5039b2bd6af4e9694bdafc331578b899f4950b62b7f41878352e26d53933c879398b09bb9733bb6633914e6f409224b1451e37034d13d37b668e170aa72132c35ba6ff852bf84954cf25609b9e16849ca19279914dda1b6e1244c9d6cbd49f3051bce9b16f4db74ec7857c5b0f0c51a4e4affea884bf15ad7fe420d673f1d993339a59468d270c5d12498a445ec97341a4e82878f1825ac9b6cc9331c2eb9888b41bcc80fa1190c61e95edfbb416538cbaa785c2c0b194e6b314993b7b4f9a6319cb2626b8effba7dcd9bf085180c6d498edc8d26a5240cc70b2156dc525e4f76603888c67ca9499029c9da7de1f2dd93ab3754b495bd7032499338512fe5d1b375e1342a96b75ccd5c3809e15e32eea6b233d942627b267a2869e1207696ed628ae1fe962d193d46f9220b0791f1f22b98e82577341083870fefa169b1700ead69196d2dc9d9e80aa74c1be553d72f0595f263878e31ac70364b2273cda9fed574154a2563668450a1c231bc4628f95d392b86a670b0b496417ba752386ed21fa14ac62c6aad11b51c62f40f1e5b852fa2f0cfdf5b1295d79437f8020ac74c514c767d450ca4232923790ffdc1174f385838edfe118b79591951dbd1a30cc429f8c2095fae6dd3156dc27959c39e1c21365e3321794f869d91abe32ea1de1c26e6bf9f14957036e997b48b52a1996512ae2c25f7cdd98504334c5013db44b9bd18bb1186062e006df8e2086fbe4c934992ae67f7889a11d898efad2ca593f5f61e89a71c3b3af1c154f8a208274d6a829a8dbd6517044651f00511cef95f4aeeb194a16733a28666021c5f0ce1bc2af36bd2f6236a4987183c7cece0014617be10c271e35b123749503d2abd2f82704a82bebebb4d621e19b72b39be00c2f9945712fed54cbbf7236a37e8f1835ff0c50f4e258e12bff05b329e94e165cc80055ff8e05c61f44952e63451213482013284f4e278bd6b166a46d789bb0143082f4e31cdeefce98a6550efe214cfc47449c9b8112286e842d750cb4eb39439b3773dd4fd6fa8890e1f3b7424244272713a93afa1617c5c1cb34f0a172cc6c42bbd272bb738882e93e42825ca51d783c70f135ce0870d4ed9e224a35988e550a24df4b53809db9a36349992d6a4a010420b1e3ac08840c82c1c10228b584820041615f20a31c468305cd10a0884b0e20121abe0f13b72e820830121aa4840482a50718a901053f4304048297af88e1af8f8f1212840082972e820c3029eece0a18090512c2044143e6ee03268404828749061811e2e831e3c52d08010502487010c3a8c08847cc287277c3020a4130808e1448e04846cc2c70142349180904c848460c246c825be08b10401422ae1c375fca81142891c3ac8b040f728c3023642267192f3f449a5ae6652b6e4700d8448e29ce157b157424817f5b709227138c1eddc04d9909e494d08818499444b05a5df0c79c42928515a5b2cf44c923be2a439964f851d8534e2b4692d9a4f967872e58c38be661d37e9a4a994c4459c3ddb2f5664869bd11471cc75628497102156de449c2d66895e0b15f3531d2304111772880b3184e128049bc475d150b29de4885a08215239393d4a4ed54e46d43250821b7b2a0819c4416c5a5bbc38997e8c388c9da871599b378ecc0e361671cc64a249375af389ce28e21445da9daf8929cb96d0602311c7104b254533316d2ad1da40c479f48d12251adb38c4499cb678a2643ef1eb2bc586214ef2651121e245c58f4921ce3b96c4d45f4c09360871eccd67996166846c4503360671bcb0d911fda92283cc86208e962788106bed18360271ded4d061299f302966c4b0010846d8f8c3e1ae3685504b0736fc700c32a87f496ef7e1a0b5dd64c9371f0eef5ea53f5efd43a6d70636f6704aeac5046de9f461430f474bb2b75cc89e8763ec934606252831bd573d6ce0618b765afd95541eabcc1cc1c61d8e414fe69d89f2954c9a7318156cd8e114bd66ac4da62418e8f091c3f260a30ee751b24a92a6b2873e250e1c5fb04187737de974958da284ebce60630ea7aed392df24664ce5700ae17fe2a799ecfa31661c4eaa4992f642fe6a3ed9061c50392b6fdc282a2c031e3c46c4483aee0461e30da7bc76a61b0e4a7ec92445cb9a62fe236a660c36da700c71f9c345058c911f3a5220861816b801c68d1b60dcf86103ff71b02c6db0e1a05d94fefa4b82acc86cace1bc2bfaada4bee660430da75793c2d6e8be932ffe061b6938a96821faaa44532e97b1c1061a4e42e4d644681fa1b5bac1c6194e97e3677596d6a04b7062b06186c3c9ae6962a89db20c2ac341882ccdedffbab153186c90e1e4262c54f20afb4c63056c8ce1146477652879e33fbf180e62416bdca5d91b6987e1a0a5672efa520996c460387c965bd2a1c4ed49ef178e15e7d6dc4f975c4dbd70bedcb0be219ac208958d2e9c6c376f6e91f976e5c285e39ccc0b55252897932d9cfb4325ab4cc94d685e0bc7643ff657c2cfc2495daa2429f977f2081f0b07bb186cbd466c25bd5fe1242d84d66d3eb1c2413688d1d2ae4b219454e124b5da9aa8a1840ac716b92e76f2c3db640ae7ce946294116b92c2c952e3c7943ef597465138c8396d628d1a0ae78cfbef0ff5134eca4a5d52a26925df76c2498e0df72b41998e8e9b703afd264ebc8a19e363269c941493e34adf8ee6770907d5247d6788a897bd4a386fc9258637bf8aa2d9249c4ff8d47491e14767160907592293d0131ee1709adda3fd442b25b746385eb8f46df395b52ec2415c9297c4e2466511114e9954464bf2af7b1ac2595d7ded5d2fb9c74238ee5b3a559a47f4448370b0d8f5515fcae49c4038956e9d96d899b13ffdc1495025d637bba63caf0d1f9cccbcc226d1ecc539c734c689ba3711f2e294c4a44c459129cd5d767152c25a6a9697a42c4517870d8ba92fc86fd1cee4e29846d6dba48a312931838b83f9a5097397ebfd32b738697606edb72783db698bb37db8e5a573dd18cb5a9cff24b74ab2093d494b5a9c6b735b8ce9620997348b53ceaa46313954d819657138493a69779fb94e0c63710efdd9142d85b03805d1ec1d2a2353f45f7110cd62624f3583dab8e2b061b6a413d38ac399e5f9a6aab86461c5a92b7b9d7649a7eb2bab38f9f7655092d2a9e26cb1fb3ebf74a938ee28db1437e6f6660e1507934eb444469b6ded4e71d02a3a7231dbe44d628a63bca4497ba388cf6ba538fe6b98785dcd925a23c549e62f51d3368a63d6e952b1ffd27d2f8ad3853b15beb2f7c892509cec459d247d56a0386cb8dbe833ad749616f9c4b9c432e9ae37f8efd8229e386b4cff4d7e72892e428c8348274ee76f7ea94aef5728e5f0d1653e2c50da20c2895309a595b4a2a56f4639600232ca488e0132ca489e74132771cc4c543579a2a58906114d9cc63609215e34c693fd1944327152b13108a1e1ffeb3f260e5ef94a5692cf041d3233885ce22896c5b3e4e6d13c6f8f8343c412e72c157bfd130f4304229538267191a9f7e8e1a7042c4289499c543c292c2209c3110391488840e2441e71161fdfd465ca54d88afbc023e28863e5ba88a705cd273870300f441a7130b1d346d52875b308238e9574e38fcc380f327c780fdd80c8220efadce4d092414f7c8031802b8828e2246dae50620c26e5104a228938679fcb4932f4799738a2c6271041c4d94ecac6294934d49a50193e700422873888bf32f1aaaa1aad8fa85d08440c71d81857a9647dd7a8231d3c7e14e254fa6b2ce9498893f7d7ae8885d256ea0f880ce23866db5f493a11c4d137b4892d7fa1fbe2409c92d25a313aeb4ba60788538c3bf6793a664ea9f60f0731339b318da87e2b89f8e1984f972c1ec2ee83e1e0c389ece1647f297bb8864d440fc7b372cb241afa1a6f913c1cadeed384d91ce284d188daf3c831e2a337802a8287f3b98931954d9bec93bdc3492a5d72a53ca49f9df440c40ea7fbd7ce2cb1ea707ad7f495c424d992b0a1c349d95e36f537933775581099835136a3566a05d79896c238c6a0811862c8e168e9d2be9bbf011f37700fc4e1949d9552aed6cd90aba982081c0e6e29e54a3abbb69a7ec3593ec6b2896174c3513d44c7975e4bcb8a481bce99df62ced718af8a89b0e12408eb16d57b5ac3b162dedd3a29ac8663da4925941edd1b53af0591341c356979eca59c2af193418fc1131041c3b164daedb7653e419a233e7a037b64f4181d1039c349db645072e84ba5af34a26686f3895768b5e9de3291a1a30628528683b88ed025efaa81ef702b531021c331595752490c71b3de160a22633898e09afc3f93c60922623896bc66c1ac92c270d253825eee250b13180e266f5fe946bb19113741e40b278bc154126374f589122f1c947585fcd9ec9420d285f38dd21fbd244d64a8b8700ca7753299ac41fa7f754810d9c2317ea9be9a50f11144b470d8e82698f8b61c0b276288a143065938f6089949dc0a9555d458389a459dddab1246db39a2a603912b9ce2afea553875eac46d8563bc544a6534b19a665d238854e16ce2ea58ae8a51e1ac96dbe7478e7cec4f0491291c6f6b468653adce3c91c2495d9924ea496a49ce140a41240a470f296795d6dbc3741f50386869f6e656cd39568fa8390c9e702c31336553192689b28938e1e4966e94e46a526e4c36e1a4649b5b5c90266e4c62c2d94c7d36469df8891808224b385f9f94f9749d5896f4818812ce59b9f9cf828673209284c369ead167654ad46518092755ba466345b30df511ce7b996452a5a3ecb26c84a3eb9dac58e262dc707d14e1b027353d344f8d5022c241e993b444ce87ba4c1a511bc3b506224338a8e97b134e8a5db2288908e12826364be6b4a0747961f81009c2f92b2ca68d975485460111209c64ef8cff9cb064527644ad0c911f9caccee204add388dacd40c407a77c23bf24d37c2f4e266bf4b33b99c2cb548317c7fcd1a5279ce977d8bb388d8a8a7b69837e06378c92821aba38a8afb15dd924698f528d5c9c0c6ae0a2478d5bcca0862d4ef228f1532a0521d7e223d6e3071988a8518b53d8df684a30ab8ca225a1062dce662136b2e29aee9db11338ba4183a0ee878e1d3a769491c3d10d7a8cb3317c8c1ab338b66834a9aa6d2784892cce96abfaf554ea9b4c6371b638fe1d4a75c364ab018b538beb6831517fc53149566283ccb2385a75c541a80c7361764d6d50a9d18a831675e1d27465b62449561c83922565fe8be145a85ca8b18ab35acccb5ccc772a34559c7654ca55eedaaf50928a930575cabcb65276555071922df5242989e5250695531cc3dc49e16c839f0c15d730c5f9a4594679ddd83fb2b292851aa5386b8c7ca93f19a2f48651508314a73799776b6575dab4511c537be88fa6cdb6297e75a8218ad3269942653b31e692261d6a84e22c1aa6164bd2fea21b501c77d34952a9b6fc1aed11b51a9f38f87de55c8a3b32cb7b861a9e38e8adf8d6fb6c861a9d38eb9624eb9f60d51b4f9c38a5b497442ce9b6b6a04d9cbf7d4c8aa9356b6734711c9fd760e278795c3e13c7a094709dc124499fe531716e3fa99494943c3fd65fe2e0ab6be94556895a274b9c529998b86adf9f49528963ed8efd498bdb6e25943849d269038d6aa8849085325924120883a15020068370c4b90063130820182c2a0cc622e18038d3b73d1480035030284c38341c242012161889c4035120180a0402824028100605c2c02028440ec5909ed303fcb3fde05fb915d990e90c854c6d3639e84c3e53604bad21586865d61b0ac94842f207c8a5d85fcc8db819e4cc40c8c57574fb27442ae4f040f11ab540490cb1eb0e21691e7c24cddb1820dd678333a0012468fd1a9e45c9b217a1b5ab5da525d3f6f48b6b8d49ea58fc92244aaf973df999e661e09fb23942227075e8081cfebc502622ce6d6b5f034b8fa70e7b6a7a940b7fd2f96d3e77f4307c7a0ff4f7295fc2b129a9dac89b8533acbabff895e1d6b0fd085b2ca05568fce9a956524b2b9ee47db431fc6263c03cc733a4449463d9bd45c75bbdc441150df471896b59d672c6f980a03f38214cb679137c4e72e053937f0820dc9bbcede520d8d379e8b32a73d008321780877d01c9ba950acd41c6593b0c30271acb6a87723d982c42e7ab614d3532370f461776ceca258a90b43789a90680766a7fb93e99e64c636a3e57ca75e356979f118f0ecb96194ab49f840dca1bbffa4ae641aefe95bb2804a4242705844c6f5aaee6c54bdafe88627b162c42e31656066b80d2d10193f5a0636abb8aae096ac1fbea2404bd12efe1682b3e62aecb2e4c4ad157bf654c8b02d9622c9f6271df21c70edde6855f033d3131de0b08dde11b48ab6ca10ed03b51a601b4936ff0cae6f612fc0ac47f0362f8dc2fca7b97b8f5847abf66d996cc0feabc5f33a673dace3d89be5645cd2a119656f3dc9a366693c31b2d5a8dfc7e7d23a335b08121fbe5315f7304cc986887f3cba500c360a7201d71377559a084fe21f22de555a6b752944bc0c011f6318a977e445c4cc268bec3885d463e505525fbb2be1fc7b491487044a902882fb5dc6a9d42c4e7323924c33cf33b605b62a410534122390a268ffc6d3fe82aa79bab3d227798d74d5878bde0dba2bd651c095ef6231e6137b6758ab7b727eb99959c3c62bca8c7a1606049360dabf9e6209b1cf5455c8d29f004ae8a29f08458eecdc6a7936e731584305f5824c737bf9ed992453259cd5781b23eeaec98aa1a45a412cc66881c912fb2d50c91ad9147725599aeb7a03dda3ae054a028ba3db769203bdc3bcd3cb1024e5c1f93aaea0eaceefc7c25be8ad38d4770c59853afb0ee89973805d511b406cbfd007083da70a9e6d9fd2cb3980d9bf9d4ca7e41dbfd526de18989d3c0d3756b5418a681f985d46bd9051a3bfd4b649a7ddd213d9f1e41409f06cc2f80c246562345f5075058036603d63749fd1128f0a448497d16b0b2e4d8bcb778dd615abc72bcb040830b061658e5cc6c03f7618554bc2c65a4a5e0edc24ca69751c97a5014d63328e0b5fb9e67ab0da8faa5335585c80a49b0079ec6b1274b286e4eab0af77cd008b80d6e83e2b54794549dfca7222006de33b45f11ab79bf92fe5b5e04bf64174378261035e89f7355cf7b338e805371705c69efe89e4b1254528234663c315b34fa32a46e972c0625921299ad8c4acb1596d5e1b47eabfbd76432a0146dcb8b037c322528249476401255344d63459e22ea4153242cec96751ac7780491a5ebeb5e1fe194c898b5c4ca7a9a9736b858ba3775d51a58751be1ae19e379174514e622af2cdeb98865724280a897c48ba2ce50cf6e4a54930cc2d78ca8fb3ccdf09ebc1b025500be596860d0e124a5cab2235e7a5ce95a9762ffbc2da9265f0637efa11269625feb7a1948741949d4a15b6cf5523bede8e8616dcda18d25d1c72c37800dbc26420ad56fd23b3ec01f7052e21bad9d5361a158451d072aa5e7f8846aa631404ab4c92d65d051d1226690e3a3bc463d20ac21e80bcd2e4a5193ac513ea3f101f95e2296240fd362a79e0b7b9eb280358b913d59b04cf60d7990f2cbd04672d39308ec2cf4029637936cc2518c63c90d3f9801474416be20c38ca22416a205aa484d790e2af39ce392937a065ccbc74187384e55723a16f515be7a7fbd33fbc2d25c1d98da906f191291be8b48621571dad05fd5d00459e03c24c84762f10a4a4d882f5c3e1eaf735f1cfff786abebf8e436e1e6dfc77883db337006478756ad203dc1d0e2b95905eda40ed447237d2e08d30d44a014119dd24a804b2264fbc073749601905682280d636a0a9e753bf45cb3cefce43d52cc3fc6751317f897773588a24e7e573ccc573d4e44ca3188e617351ce3dcb4660c7504e88249cb87b25dfb22ee8f96d4e12653a8d329973ae0dc402d9d2600a7a40ed2b8bb1034aee8c3d96d6f3c622d462da7193c8716a3da915fe97b159f8d420701780ab0157d0bad98b605e472d649001a439932e1e3576fdd20b4c586a4b75e441d4285644344bd047c9a406b1e91a402bf6c624a2f99fea1eb94684071a07c887246496d08af41c6cbe299c43e94a4059cfc2fbec46e37ba68d773937d4bc6fb66c23b5eb1f73d65b93915d78f9c61352acb45d5a9475838bbd8c5b7f909fb3d7571bcc5844e7664791f4b6145c718be32b22cfd87efe01f6b217889f05e63e59072084cfddb7809a2e0dbf32afe4100cd071948e2c1637a40bcbffbdc3b1c5ca033dda5d600891ae6808f157b41efcf3bcbfc0d5c86008fcaf515ec118d52a31625837e51e0927591a584898580cf31c4f2e4f4a1ff710a2a14af5015582e8e34eb84e260598d3419a2aeae8335b854a2d516db15b03d88481087b2efe3ffe064df949875bf53a50662dbe693d49cf21b1038d3cca9e6b0a964a5abadb2be84a2d41ec2eba2dba8872ce69ef13e8eebca34f26aeb4099e6da891d1ec35e905f853973afe7b4d83c903a1e21f4db2c3e454a8f248bfa9aaaf917eb8a930c3838d33aaaf9a356e50d4e9c3a6650750791cfa646effc29a8ad6fb840e8170f6550ca38350aaf950d1e073f55cec2f8a05e5c6e6caeea1e0fd763b3c69f60f0296e61ec82deb0bcd87ed2e243fce21859eff9a32e99fc0b76e032a1771b3ad09297c73d6a05d3ca8b7e104bd6c201a7e9415221889a07b3d3f438ff9178e0acdc091e5c8fb63a170c657c260a1edf76651555416b0369451dd20c522b7ad2e065834dceaa39ef49d8c319ef3178c70fb0e0d8c3cb2422aea5a2037279c4526f568f809558068339402402904621140054286e246f7e6c7388b56ac8a1a957762915934586dcd80163c7c1079778cbb795bd84cf949cc0dace8ae12079955a8daba162f2c353df3c7be4fb80be1f9655b4ce977c761a79175c35b9f2a8774f353865b180584c9625eb3011c9f47249232f9071bee772047240862ddd8a153c049c3918dbeeafb275081ac1cae0d0f3ce1b3cce8e49b48c7cb4b218cf57be23dba4dcbbb944f0089517357dd114efe5635043a2a881a13199729ab000c9ab6add337571eba2aa5ba72edbba5c7521ea164e97aa2e99baf874abaa8b5317f7ea66dba3695d69d6c5a34b3674f1bc8349edff48395dea38ca31044ce530f1fe527b406dcf9dc3fb2d12ef4d5de2daa5cf5b5a77ebb3f5e2ab98bf84484cd082088ec599b30366a4c0b4b2a3bc7d585f81e52b0760c55bdb230c925aa33dc8c129fb5d73eaf6b7b6b887008c0af527f0984f408e0536e7b15e94250b98a179084fad1b8536b9c71fd1469403821860c7d93c3e309ef87e28013b1339caa526587916231756d87d3329db624bab9216c27648e1b748eeced10621ddd0df1a741345270f7e2c46f05f0239a2253297d2f95a450777f0dd543eeab1471f85c5b923ac048eb7bad835b3d8e4daa6d9b8d130ad0d51b58cc6ef34d3172d12d080abb87ee0f5333fadf43e0a64a591438326eff881b26bba8aa7ee76fd4123b271863e5921c2b7f44e443d2cecc54f88428b0dad1d6d36c50d7db2c2ebcde6c24e17b355c642753a934105a9c75cfb5ee12efb083a2643610b8b383f874a059fded699c7909b86880a82f631b6b3b1b6d9992c89c91bfc1d8c3567ac1c9d712116523ea1219f2534fe85b33aa523b30045ef0705d897fa3fa7e3adb0472e2155aa230ab8238b1afd59c08201e18ccc46760ab8d814f67b5820a617a2c2ca981220b07d8785bad44961963dcaa58eb38054c40634d7f806a88fb006dbef94cab85f15f7d0bd63b6193cec6f1d853a6af5889e10165c058565339580d1fd9c668d71e041a264b27a05ac2a92fff669a255a1837006691784a3214d1141311c92262c77ef7953145e57baf98bb378e0eacbd48f8bec435198b307e736c771d110e0573eebce11d6874272b4d19128267ad95bc40025952e33c46a844287771f99eae04c613d538a62037806bcb94089dbd74cad9d1a76dbfca9427467ab22def00990d798c6f566fe9f74f42276ec2a5d6ba3fc0b5719fd83aa97d13790a1153cb000dd003405da2690e15dd8440cafc27b500713be0e1249409404320f0d4cb66fa0826f53c6ba3e2904324947600876019707f86deba972459237dd92830432cbe4ffdcc3c9e6a029700dd160a0ab4050ac9208f74d0180f5de0904219a4fab8c30f438c5d1bd5757428f3e4e4f00339cdda29beebf458401503b6fdac5c00f928e3c9ba9d1c60f16db8032ff282d21df7fae488bfffdd893788a3570bc57a3549b1a4becac015523480d6a1acd6de06285cf1a6e5fa7ce11344a2940e092031a25d5d52883a686b6061701daebe3e0eb61626994cc3d14783b2d66cc191988a252cedfa66c67fabf2543508d03d121402480259aae947bccc33d1c7b8ddeac8fcc06d52b349aa75a0bb5a76505df16b4130f4089592a4f86a767032f6065be94cf86b85157753585447aa469f49cc1953674cb811d237a771885c113e0196aad3094990aee46a2830611358500e264ac3cf50c35247f68d26cc1d422336cd2f4f627a5f92b25374073f07140230da5aad1092ca917fae079cfd0d50091cc017733f9bb16ad7473847d1a6af5b7e765da30ebb27dda269236781854cd406f0364374f370c5ccbf8ba033d841e879a8114fea902924b07dacc4e960dcc6c941875a319be324032373fc9d1845d3ff618b6dc96060f7e3ab43a1f106450ad05dbc557c8504b9e21ee69fbf4852a15030044f87f6ef90e0f133b6838686e2a89625f7a0d3376b6ce8d9cff3e260b635d88650121623bfc732219fd253f7a7a92430726c9b47785f283e790eda8817f80e35d2e6b4d3104f33cdea8dc595e7952e39d65aca8d58a2adc84aae3d5353b3195cd508c70ab5866d0dd1a6abc931230a19ef526a9a2bcedd7cec070471ae96f6b9b637002fe442042d03f5fdaafbdb3051d4269e02e63b921d91721590e2ae0c33e4cb6c46d7a101aae9f580b6b26b5d5c89d2d9fb55353c6bafef38264b6c76533599d22598b997803af9f9ec8589673adf533d36cd8c1c0085df39def9f8a540f748cdbb39b8c2ae2fddb296e4b7b012c0119e3c82dddec1ce279c690d7a3b1ccd9af136896672e2c817df61f2783c841ffa73ff10767ea642f801e676e9b27b72389e63154933b87eaaa0ee35f17f74ebce7f1a3fbf978a6108478459fbaa4c0790e08ae791efacd77f91c169e2e3d0ce87cb3b360f012cef74adf32226df9d5b6257cd7c601df06912cd5bfaffd36746e2376d80e2a36c285c046435d77a70abdfdc2e934ba1040e098ee64debebb35941bee3a154e9ac23f48aa28aa48959d920a779d8c28cbeeda52228ea789becc28ecbe9971508d0f44a4214ba3f0ba255003603e70883ee40096044a8771be677c1f408f7fc34205ba3bbe9545c9597a8b67c1ac4cd84063a80fb165f28e5a706144b4ba5e762366c5731e27c6b3ddc49123340c7220ea6ac679e64d72d669888af729ec6394fa202855335b8966b7f391106924d800016ff9d1084a09b14c7323479959eed6acf5445b5824ce2b3e047b0b67bc5dbb6c722e700ac7e5ed2eeabd40c61b989068ddfbb87e499bb4c3699f5039b82c89466e49b58dd879d388bc92f41e231eee416a4df634dd05843606ccbabec96dc538293ae6f25539376ca64a4641acc93715bdc00717055f85824aba56aa551d50d2c54a9b7a2a61072f9a22189849f1d04543699ce782a294340ca6f87fb027d19fa48c8def2cbef297d8b4b44542b2ce433c28eeb8b9283d82aca9a30cb95c91d47b7762ea88a94b7377eb0639b8122350bc51f23fd6eb664ab68390f946c7a999479ea388cf1af6473da4ace638f240d35b192810c4c5a0e4e218a1129cd7380033a926672a7d674a9afb662b8f10db9fe42248ef4b09757671a1c6f089b4b3e7b7537ce440417419f939f57e429cd66312787f20404a4904dc3a73a22899c138375de426d97160eee8bea49608f69117a726d4ac5ecb712b83f119b110fdb3a66532f7a490849856e9c6746db4f61a7a71197a0b38ff8d78cb6162d0ebcbb4815e1d0f7e1313c0e1deef67de598db894563312c5c9dd616f29cd665df604eb3279596432455c03e61600795c82d8d9e5a00915384097dde15f20023ffebe9cb980bcb10bc72f207d85717afb04a238c480c0c9026291d2d43a3ccd6705f82d236ec327107ec2a87d06dc7e6f0e939f6955064dbbc53662a01b3ef286aba5a98152a4888c1fa2236c3a56aaa4ac314019c249c7e87024c39687e3759142a665d0e3701c1a15dc435049939f12652a204ed041459039d33b2c5841934b65acc451f6a0f6cb8c87e06b0804eb76952b10aca1d4dc8ce6990987645f386ae5ad683f00f64927d4103c712c6542435fb9512e2c918361e651e97aa2d6c108ed4aa25920a681c8b4dd149856c2496159316463962667045900bafa21039c184451d363e2a74a957d39b067ec42ce5406433edea60b3cc190e6046411ec7d9fcb5a92c9c8748ea87ab8ab92088946cebe03d1301bb8f8c0967a60d853669772ef2dc281998690f543fe4d800fc8c19593d06b9242157d41a424f964055dd15c391f871beaae121493b9a5888b61739daa6bc5ecba227123246d5f368172597cce72b526ca9769e0753e35da3ad8e48fb2eb274144d7df8d1cfb003a4fa7523262789f9aa88eba74668875d2d9b99487d28c6979e980b6a4d87c95471553f85501a8278de71f8192db85f00241246e34ed69939ff15310f98a5381c5e60b2061126218fe7d8ac08d593ef0545689cc97d17e4751bbd99cd2486d4a923bdfc70e019bc9740f41b51ed21e3a184e416b10d1160ab7f188e5ef59ef646a84e5b67dd87a89787cb0c7761410dccc08cccaaa7205cabaa07500c8c2ba9678d080089dfbfd360828012958a6001dd0e591ca44cc0698589532d1b1baf9d4f8918a48faa45a122705b75ab6a008cfac6a3e7da3c2cc2a56076d61a40eae50b8a22692f5fb69c25465ade76827611354c00977d0f941e40d7d985b6749aa402a5ac198c4037fcfe0e54f96ef2fdba1e8f14abac1f8e68349ad1d909a61d72c9bd1b9cc09ebd301201f952799d3a506e071c25f6f246b921180b172c0fd40f5129eef1ded59d4dd62fe7aae54960701fd33d72edd39f5ed604884cdd1386e190e1076352afcf5ae627c4f52a7b545f50ca404e47d82947f76717e4dfe6af60769ff1d09ac518e6247658403117c58f544ec8031fb0b3a08574b01f38982045154b802f2f81a495d1708d906cb53237c7700a1c4931d35046608a5fa80e532ca618478ab68a708ba8debc763f9ec69164d4a12b96d9b952f4be541c960bb1a874bf738cd8f433977785e9f758e1094894a5352ea32a0dc274291b1e56053290a51adc9db529242542baa62608a8d6d988b95a5bd8f0af7f3a7e983f572076b93c2d4c3b1707c9a2e14686b49fd1f2511038df269c585b8ea38d8b25fb435529bf775cd61c072507520dd03b6344675ce38ae69d3efb39216c43808bf6efaef6ae08e4b58f2bc2988217a837f2621b008accc145709788527ce055621d23e0fd9272d09953b3f4d488dbc86e9ae91c55c0ee01e2da904510ada9cdf00ea507893544fd2b93185653544e4ae38b5ba43a12d29b8c145c3149daff54abe87c25937bb384992222d4019a220a8338b80421240501e0a5f318fbbb728d2281745078ac3f56914c8a797d9b06c09e95dc63050bd4f52d5a61988cb45e76f73c5cb0c833f51a55c5b84897a65ccc1a667507df53b320472dfd3be17168b6599bbe7d1de90193a490811949d6060f13d666ed09adcb8bae4109e6e36b024002e77204105da85397a98560aa71c78619df08634ce982c23fdd6682ef97c20256c83e371a1ab6b3bb92f1181bfb65630e10808810bf4f064480f231aeac752aa87704686134699c6f22e19450a05687df431d970b35d36987d4e4622455f560747e2507d2fc3095c1c123c0125b8655206078a719e82d6143121b737eaf897c702f075e99ce7647fb01e4e1b413548b1c09bc923b09ed305faedec0f2e4c950d8be87900b0de453fb723ddc0427814ce7e5ff1ff60a1127154f01f252527fe1b21c3b47c23230b61861b1657d0484a09f57ee03d8e69246f98d587e02d99ea52b3e432eca13a9ff0b09ae1d66ba4d38a9e8736628a201bc4d9cec3250939fe4908353e01f283fed7ff6450abd24af4d500e1c606a9dceb95c0eb530ae79cc2322e0e9148f2a4204005903e66659a33cbd839d040fbb15babe49e5dc8e705372a29c6b49bbd2323c9ac258a6cdfed2c520e61577791788d71a2102cdac66296076625f41e9e13ed8831b4aec4aa6128a396ee55b951239952e0cc51f6eaf987575cc92f93b06b125a8afae0327cff19a8ccadcb7991e3bf0194b379723aa7bb4516026971e4fc840843c691905294a41360200a8428a9441c69d0c6b9232e4e8053dd40d0867635dc770fd922f6a82827b259f8b42b889765e786823e59f20e7477dca51c572e2703d4701a38d5a75c4cd40208e7e4e03fff3c2b609545141a84111c363d8c526a0765c9f7ebc11b4e2d0e4f822e667ed2353821db9739ebf3a978d9dbbbcfb0de9b8bb85cb44024351f08274338783f93b2833e575aa0b33c63f93dc6d2a30432e03d772c1915d89b567080994e28b3d3c24463a42344951a6bc320cf38c2ba080e6b69bb294ab0def48f4a7a69111099802922e82aa27896b667671625e6a11cefb73dbc2b4f7a5d31cc7ebe0da60eb4d00e568674d41c7be70b65b034eb9d4212830aef623ee48a91fb290ff44a195f9f573221c52de620282981205fa54acd1a1a0c393c0a96bedd57567017d94deb93152c19afccd645e33390a8a3714125c36ad49cb64439a1a2f601bbecc5d9f2f3df167ebc79c4d27615440953d05dd0e1a2a1924884924e18a6c98aa50f69596caf6fea21920145be58adcda58daff27d392f562fe1f04d58eba216385dce20aa1850f6412d27b1abeceab241d54236cc7b3ea3d1c40ec43b4a813bcc8c92812cf295028aead996958a959e311bcee8c425bb4fb2dcbce58c9b1c28184dbb0821b699e680adf1b0310618a162eee1b59275dc7188a7fd48f30047eaa05c625ce6ae2c6102b12dd71f68cc3f76d751d1705efdbd39a6e659298ddfaa1d6fdb1055a66a307119aaf1e321c75f80dd7d78b10a660275f73fc6040244f56b2547cdeaa22346a634adb7e3b3898903857e1143c78f3e543b8a35de70a4ef7ea473b25a2653ee9d5be2bfe628dcb5ce52f1078baf223d8dfb92780de9f516bf216259551459266fd9cfddce6eef0209d5f999bf34414045fb3b8a0c367adbff0595a46a19b5262b479d48605effff4cc4b391f2f0e53c39bdc837e3c396b136175ddaf5c441d88117c716f2c08e6cce5a94c9153a930badcc7074cce2e8db95011c4752a2438d99f9e3afe9ac8d97e9ff58120e7050fa2e19429fabe7a2eef4aae1d75110ea21a0c8657dd30a0cdc1d9e25e31bc5b35e5de6347409b8131cacd7e0e828b3089b650d222e59c3a1dd36a76827ca36450494888a0203abd104f5a62dd2028152fc857d6ff459a48ef84e2cf1a045da416955b4cb0c802130ca41210c62f0bd851e18280c6145b77a1184a8a8ca362b7fb8a9100547120d7b37cfc70a9c315d70a56cc6f859b28230814c2043844de83e5b38115edcb9c95ec2fea4cf0df88d4349bc1ea1b5b80f9581e3094a9cc214b6e9d1f9e8d32362ec796dd8aee1d66231bcf0e8204313624c27225095be05f0f55238b1255e63828474cfaede3c38fc4bce2a3978d5faa94adf8bb388fabb3ca4502a4057aa58993248e5fe1e2ab3b0985798f76208d41005392338cb4a5822475b60226187779125af8ff5ceea4162589a455824721c0520914802b1db03326747f12944c1c15e12e6669da067a9bb3631a46decc245c44823321176485a8428999c3fa1f45b71845b0425826ae45cc1fa8e3298e41d0b9a14a266f8e52c3fd8849d5a802bb014d00955092405720220a11e157abe0f9dc0aaa0843044e02e6023d42fa0a5856da6f6fc36840e023642f01f8e45e6627b0de193f051a09db0eb01cd29b6757eadddadf840912d41fc14f2694027d9c75c70e6b29f2d1cc899924e097ab422386a158000b01d9c36fcabc11aa489badcdea9625df3f27f999d50523f6a30457c64130de2b9b4c205341d78d83ffc0d5e862f11d2c8e2cf4abe0565dce793f1acf8e2979795af0b9e6688f70e5294751ab7a8135103180d03c436dd3d3748db6bd3a9b28ca41a2a9351c30847557f925c41894e1e30e308d6596dd07aad92e664b135c85b10a092517388174b18d93b5b321492f79c49ac4e5137a386de350ceb3451838d1006b26cd2d5c9f8b62ae3d1840cecddc9db6afe65f843df9025408247b7aad603a8c91280478b3dca920b110a33005019e2ca674f4f84d35a693f67aadb270e7ffaa49e4828cfc95ac35bef42d40ac3558c87aaee0039728e85e65a1cbeb362d7579ac0b309820323015d67798103471a301909d8166126f5d8b0c8b1c6b06d3b0004eb2620d3126bbbb288996d347573a94b3bb342d2e914b7cd25276a2e26a50b16bb583e230ec35ca4ba25cdae923c9dd0654248a92811926876ba1d885a87b5d297e9c1d7c878e7327a4f377a18b46ea4b4c32e025033a2c8487cc6dc0f097f642a465264f4ce77dcc1a13c43bfccf8d92e4cbdc858196430adcc8ad7d65a2549db022d5560f3559c227a15977625dc610eb1eaa984ede63fd13d045a59e75269580780c282325eeb5c15055810146456fe12292748118e0154e8779f1a0834e41e361130c2742bf1964323741acde202a0c06ff54e121e96f2f4bb15a1b6a8d3249212e0487d4816a0632bb7a570a9ae65d1ed0bb5e6896aca84445a0a20d15929404f50cc84ca9407c95932f098893a2a34143989534f81428977388819805ef017fa5e62ac48944981258ca915c49defc24c8768e5eae501d2d5782e8172995bb8d5653edcd0d6d1cf60ba4c006130feac5292e7236d69b61dd68de0776f6cfa0e7488138e270e66742e0549ef50388d38feab386ca96b0a265d62999f7a9e43ea7d1222dd162db9858207e1809f610774448060989563fad04a0b10b184b054a4a555c6c258203e96e53823bcca574da09bc56a11b5449751921fb1a521fc6b36a9421669976dc461b4bed1cc4423cb7613253c9e29aa5c7a64405c89e8c5762539d3174297b5324c2a831d6d1a358b02443ba3d8a275a26b3b3dd170d9616d3afdd68a8d2bf9ab32d8eafca099fe493204c32a83f9c44845c0100168912745286c0511027a940ee692df9473d76c450bc3de12bbf3c833e272289e7a0ab552b17a2d6c40a3a8368c7c5f8ca58242a7652b3f874a899a98d70cf1434982762c09c90e3ebe1de6056ea6e45a4353a4a434aecae2c9579c3c4803953ae4a5acc2a7085a5210232f0d3d511114366656c246b37a90c9b85dc6e7905272095d29769536ff99c7deaecb2adc4bd3122cdd2bb9d224221fc68d64350895cd0c5f0eea5d10a76c0e8257a077d656ed577cd64bc1e4b31b0bb831a647bad1c5be294f415c9fc56dc29594998235733e35d56e53a1b27572dfdfb7bb4270a32d89f75c721dd572f471414f047799e25eb3eb56a2cf5f112865cce57c7439ee6d4834b5fcd667dd8e5bd8871f9230cf876fe999845269696550d25ce5f9a53f5265088f01abb2967c33ae4736609673dc31f03835f7ebe83d1faf0a3d78400b7450214f846c7590e35712067b9a9479b05472b2b6dc63f3aa00225f9a772399d50c51254cf5dda54575b28306ce431d79ed70ac5addec386a70ca204a1b7c08b76b35167c716bf7da45bab520ea2fc684e4c8154482745b2c9ece650255763dc2d1d86db03a00236543ce9fc2964d26b8e9443f389c23405ea74e5a2156820f2f41d698d4bfdc39e82e2bb5075080b7cade99a75142b61f003f9bc02784faedd63042d51ff24b691c0d5206ed422f3a4918cc562b96b9f12b6a279d9c5141adf85d3cd941988c5aedc74d1c6786682ec7f8cb01fe36b5894c5ae03707267f20c0db9444ddabc297c0ac6a56c614d5005833254470a7869920108c49260ef314b95ec9ba1ced05d6335727398f85397e4007ba67e8c099f16e23d2634c1c7d2b5abc0e2bd16e932011d1fb5345147503ede590569e1c132c1e4c2b3d636897ed3ebb93ca54e8db08246603c31d273ed2bb90299bbbc3a4f081c60c8633456eab3e9b607abee5e4552b3d98b992674d39020cec3122fa3eab04df0e3217726e2d1ed075c893843824447202a28ffed809897f2f5eeb90429717a3584a76a00ec3697f43c1808fa547b2b8105b6135a82b562c819ca93b08c368fce0fa0c2ec54ee6174325d43df8e241dc48fe04a4e780303e27eb11553429110ca5556a6b4a3c7ca83b2a075f05d2fb45e1c10d7adbe0b9ce7671da5f29d473a3e160c01dcad54f71201cf1c7fc78a247722dea65d786d947472b9fe6b8587b7062de8f8857468549a1d7cf998013fc0f700c0e7c2cf0023a691a47f1eb501a27dc4060ee4f58da27cbf4115e01a408e0c0ffdc3fd08b9b767e81d376bf3cc9d26fd069aeab9a2da5775d85f7cd045d11e37b56c3b4f41b0f11a00f8d196997c2919b7d6ceee06a5c18e76994393180681e1a2db85d850afd6c1ef976431287a545afa2375d1cdf8d07fdb7aecf339c15086ff8691473983b125d2c84fd77e93a60be4f90a6577849f321467dd4275ab317924feec822d3268e7918ae58c410913ca64cb23bec3dc4dd277b231f7ef38689adf3d2c10016e09fbd0b68bff00733a4514e87c1ae154c1f6ae0fe35ba82f3301b7fb42aab967cdf6b2b524c1c90cf60e014906901342bec4e72f7660324fc305571c2a2fd23ba1f914adfcac8186b0817faca2aa5bc1f396e577841d08a5ca775d0aea640a77de9aa8e00b951747a45cf29edc3a09e32fae9ef5d8affe8046eba353b3332f48c20d8622ed18b6a4910d043ae1c33773436e75c6cba48322a8adcd29bc6a20a374f4159d9706ac133850a70beb434c4cf17a5ebd19665c2d40fda212d42b0d8bf6b15a6c4ce8bbe05f51ecf6689e1986e9a8970b4cda5660062025b01a581c0126c107220a78de642c55527429893dad01c31bef9aa2c1011439f235b7f410f6a84a0be0d3f071364dd5f6c818898cb1ce42e9a1718ac4bc895ec27a247b8c1d037b76c0742c126f45287184e71a8f4b45572f614c84efeee1bddc038d129182d770491b24e087e1f91bf933fac43996d8c5805a613dd451c220b87384dbe615abfb2c697bced58ce9a86fd9ba6a27701e2207addfaf49743280204da36b282ba6156980274d41a6230fcf800876f3db1e6e42ab06260d92b9983c58b2a6a3485483e00d8b6434b5a4faca3722a4deb5604d2c36040efcd19288bb80513e642218405da47d0f880e4813841804012603562ac64df3c45837d0ba7ec1832542d41f39aadcdf9034174ab27762638557e7905ff54e2b8c00b16a0b9c985b60a011b449ea25001a61fbe1d3d45fc2b4cdf4384a19dde61f721c481027ec7c4fa0b3160b25472ff000000000000000000046370df7e93105baf4813110e36252525d96e263ec8cc5e6ccdb6259d0e3e9d19bf20e90bff0b420ca5cbafde1d4f92982ba9602a8760daf1f3e4f02aa860f24b8516afdb6bab9c8289a75d9762554cc1599cbedce213eaa6520a362489793a4d8e762385147cccabcc1d7a1c0d7d14dce5d042ae9cfa971fa2e073149a1d4393a5a943c18d85eab837344ba6808213abae38216fe9a89fe07ee2e5903b5bf62e3dc1f644771072f8962ab89de0734d7d68ed793159cb09d6034b2fdad15e63ed2618fdfbd0153c695a5c134c0a3147c93d875b926782f7cc9d9327ec3a3c30c1486d894b7f48e19b4bb029452c6974dda03196e044226e5fd27a3f4d259890fa7274d89aa36e29c1e74eea41ca97122c3a09deaf42720965c9722709367d3fdec544828b993a079ef2a74f1b2498343169b65efdc8da2338936ca9233815bd48964356314b1ba1c7714a31542246f0bf95d1b5de52aa882e824b1e63baf193d0eb2b82eb387c435b697647223811f598e31fd19603117cf07812f3e3e475cd21b8fb0f1d274f8ed16308feee2387a4ec1e954270516a92c4a810820f731a0dae0f82abedb1cc7ead398e1404275125f87d8dc7a90c04235afb2963ce9fa20404535692e3ac1f9d5afc037fde9e35e2463bebfcc0adc715cb73ecb99d3eb0a963d3f29827bece075672e8e8d3b9072e73f81dd671eca1a4d403fb9f62ce13dc2d6b9479f83c7d2358f2e0816bfbe8771b3ea13beec07414711d4fedc044fdb3b6b0646a963af09af62cb96ebae8b174e0dad63f7a5ca92c45e7c0674844de7f88e9a71c58b74baf606b9b2b8871e0d6e2e670cb21644f100e8cc7514821f737f01944524eb7a71c7edcc059b4a8e6f6b481ffa8537a65dccbc961031f296977d68df6c95f03ebee295da6cba981ef298f34fb274e0ebf34306696fad3c4deca1f87064eaa2d7890c9638b39ee0c5ce6ccd0e1ebc731749c19b8d033ffc97721d9e6cac0e43c16c77defedd623031fab793869699289e4c6c0c6943c342b72626073cee8b6aeea38e8c3c054ea9e9c9e8e2ef460e02dd98414524537a7bfc0068fbaca33ec746b2fb0214786f2c8569b2c5de0eaf2e6c9b1c4b16db9c0e6b7f484f886ca6f0b4c9acc789d43aa18322d702185a97f6bb2ba290b7c5ce95e9b65e351322c7093bd230d39885d810b614937c72b46cb5b81bd16b3a9bc5781f73859a2e546c9be53818f2746bd983c1caf7c0adcfa9696a64cd2da2105c683444f1d34d2831c51602b6f640b968602d323551e68a60d19f3043ecca9c310d5d99346cb096cec71e9ec673581ff1c7b85609b12751613f8a06bfdd364b50b622d814b7b4bdd7a5eddb152029b3be4efcdded17227094c9494bcf63912d8641a52e4d0bee3cd8fc0a69cf08cca5ef51f46e04eb23424d3f00a1e45e0f3e728a6d289c07e943d86ac94afd36608bc84e42972d4fb1ca708810fd7bc12553343cf20b093f3fa6ff47a4b0884ffdf743ae5fc80eb8a7a59cc3b8eb6a301f880b71ca6ce2eeaffaa512ff81c1d776765d1f310e2051ff7e50b493cbb98a10b464d4342e6c8fdd4e782fdde1ad590596b37b860379996ba78a5d4985b3049630ead3c8a2db80b79371a254fceb5169c697a2c92435ae02c78efd6c9d17f103e9e2c78f70c6953a8cc7190c582cdff49eadda15dbe080e4c0b56033360c18620d963ab174b41ef57b03927f3bd9ca4ff1e734329a905c6cc156c55478de539da7bc86d61462bd860b6fbff515670a61efee48fad55f06925f65a14cbb020c1c20c55f09ddc3d456a4e2af86cd1abf42fe9c20c54b059bdd582479bdab11098710a36d35a8648c5362b1f30c3147ceb4f9ada95ca0b3f04334ac10739ec63d0efd2dbea033348c19e48508df62f5775146cca963b7a742c7ba744c15e9a46e6e42031a4f486d259604628b8af5e4ba79f4b2de46680821b4dc13d3bc7d1558813667c8217cd15628a2126695160862738df10a39b86dcdf4de90497a6bb2ab9449c6034d57b4e37f90da54dc18c4d18e57b21b222e7861219a789cb047b317e0e1dc5147c52850956a432e4477868a7df0d25f4c08c4b70abbfee361d4f08f5df5022c312dc7d7488fb95bafbe60da5a24af09342588f27f592d00a66508257c914d4bc364d7af424d894c8ba09b963f2cc4a828fb077498dda49a98d04af3ef991866f8eec15127cdcd53d82cdb1c2831c0fb225441dc15555879c36fcdc6f049bee3ccf2a454cc9638ce0cb7763688bd47d478be08269f41cb784e63854115b7b8b748798bb6b4a9cf26b8fa637aae306ef2fa8c11356136624820bd6eb714ed162c8d31500dc300311fcb75fccb14990900ebba13446192ff819cc80060739c38c43705f6f9ad2a3a3d01f6f08b603fd0cfa1ea5c398a3106cae4f1d8adf8b754c1382d58f982ffb843c557b107c47312415095a1ffb46105c87751d07c9624e936b20f88b94e3ad8fe300c1badec7815ec721fc627f6037e670b53d781cfe931f986ce9ad92c82de51b9dd107ce33ef87dcad7faa61ea60061ff828ad62679a9c57a27403dec18c3d30e6f127cdd13d6378f450ccd3360fa71fc28c3cf0b6933b8e7c73747b7130030f5c7a54c922645f91dc67dc81f7acefd0993d240633ecc0855ccf9c25adc3cc56995107bee394b4b1d355851ce5cca0036f2b3a39838aee757203628871cc600e468978b559df2dddf73490c13fe0f80f501515cc9003d7713c65eae9c3b2f3184055c693d1811971e0bb3d2b228d67fc28724309073328e305450cc000b11b33e0c0e69852634a12227e79dec05ac7fb1a6ad4add2ed0636071e46d210fbca1da50dec84f947d00fb2818d597c2f6392d6c048b290b73fc8c8219a1af8eef8b79a37c52e8f66a481519d5cf9236d0e62796e289512cc40839639476b3fee68396e30e30cfc7f143b9a8c3c29c79a19666024f5d5e3205207590c018e604619b88891e9c65a634c77cf20036b6e1a34baeba487df8c31b01f37847cd3ce10838981309811862ecc00c3169d93e6bf26e60d252235ccf8022f39faa58df956c7bdc0bee65069213a4e1e7aba5066ac7ecf53361778cfa194ae44d97bb4d90293db3f7f90f51f479ea28573bc989d267dc8e156b2c07a1cb5c4ab52bf0815b7726a677848f70abcb6b7e6ba8edc535eadc075f2d4da0fbad93e5641dffc4093c7c14fa402bb7e49345dce1418c9955d5277fe384e9f14f88e1d9bb44b150557439a140a664081b18fd22159129fc079ea3421a5a871f2e3042ef8975f0a510d9591194de052ba97d2958ace60021fd3c6f4bd694a5263dc208306312044465982e10c25cc4802b71963efbe5a688efd8f400b66208133b798bc25d847607310b929ff83724dee0da592c00c2310da43ee798eeb39c8985184fd2576e67cb9e0b801910b8e103c49c00c2230495b3fc4b052cbf50c814b9f0e5f2a7746c96184c08b7be59090bea36f4a10d8e8172564fab032c7a140e06adc7c3d57f207dcfdbea9f774ff87551e61860fd82469bab683fed8e3da0b26880721e7a05f2f39ce0bae2eb548468e79457c177c1cdcc47e4f3d3334ba60cfa3bc1ab96f3ce71427082017fca69cccfb8d229157120401e082b59d4aa97a39b7e0627d98e2648df51de56dc15b4a3efff8f18bc6bc167c547e4ae9f503cd69d3820ded1b3dabe628e70fce82fb4a2ac93b7b14da9f2cf8cf22d67a1d4da3b3fd810062c1fb78fe20b349070b2e42b64e25192e11be82bd9c5294b290f23b775cc14ab4a6fff1fb5ac1bddbe4c6cd9d156c2a73dd8b49dc8368ad8253f5eaf8b5c12644c44c00aae04d2a841c9a5aac07024805df493c9967fb1cf999a8603b2d734ec15b4a13bb26e42049b098821f0d6e21ba2b3be59482a9172db38f3cf47fac0f04400ac6ce3bc4f1e439b23e2d07021805db356d9ea38fcab3a645c18f6faafa8f261b0820146cc4d3241ee5b02e84d00a144c49e42887f0c0836c767d82cbff8165e9304a35280b08c013ace7cbdb88f9a3645b192a98010c543083188c51c6a7a0c82c5d009d6025c74a1529e528965538c167abc47cbd34a138086013eca847213dfe701080269894fb73070fb399e0268f68471d295d0e4c98002e416208c012ec7a90215da784d8966a83002ac15ece2e091d66da135b009460f3a94e90bed36c394e5e6004638c400093e0d346879b9ec70dfd32ae8c0024c1c6af08d9f26a7f8840009160234ed478392144d28f4282c934a96c9d293fec1fc1c741e26592b8bf2e410784200c077cc0012e28830c1aacc019f00f017f52af108023b894f5438ae5901ef1d208ce5a276fa5048db9bf0fbedf490a1500860fd84a39e4affeb6b6f4e905db9a43a954f3f082ad5d750f2c638c31f55df01e079a36dba40b3ecae1a78e75baa38e5c30ed1b2219dbb6266b70c159b88510dcce934ef6166c47cd39bcace9c34faa2d98cc71c7d051d73ca7b316ecc4aca91a9a2ed887b46027f5e430c6eff02a78b3e03287cc151d86081d8468c8826d8fc493073147afd8890537fea15b94ec1bcd745830112b273543fa0a36c7fa1bad7b2febd8345cc19ee565aef87b1596d756f0f541c8123f2c68facf0ace2a357ad86f5f1b7f5c05133ac61433d05005bb295e8e60a7b7a9752af8492675ede6916746a8e07284270b11346e69088d5370a939b8588a6f1e7352766898825bed09b93999a84bc8a0818fb1d5344ac1bae730a6e87b19830629f8fddeb78f8c9999e90fd01805e31ee5204aca2c7f0c698882cbfad4f57149c8aeed42c1849ad0538b1dc7131550f0314fb37ff4654a018015687c82cdedce9b1aeb402f58a0e1097e3f5cfb9f8a549ff41b4a63946162f03328df82363598c1d718687482c9fa28b4472e6a2327d8ead0937550d1b69b0a028d4df0414e1ba1e33d6f283d818626d89bfc912162a86482edeaf0d861c70ed3a22f80810a1c2081301c10c6053040031375ed491437c92fc184741c7894a886e6571ca061092e52f3f965f7a7b1a49914685482bfcf2429f7c7d714685082df0e4a55825bd6033426c1bdc71ce425296f28a5e0052aa0315a100317a8c008004aa02109563f4233a4e04682951ee9ec5972ec5ca132041a90e0d26e945d871a935afc114c8f65d647d1fb729d8ee0f55325cd39881ac1c77c7fa98e634f738f1b4a465724a0c1085e3cb7bc4573241adb19c0608c42008d4570a51e69ae8fedc30ab512682882b3dc610829da27826b0db30d1632c58f7a0ea3031508430c31b04003117cde8a9da1dc3c2f723b1a87e036969ec7bec91de6704370df52962b357ea5595e4054340ac149088b794c2f74749c109c564ea929ea791ccc63105cd0a950a6111a3a2e083ed3955587ec696f2d10ac65fa7bd4dc552e2940f0917b3dc596fec0faa7fc96a9631ed564d340c30f7c08e95eacf2e747fff4810971738a78eab9a3c6f0819b8ec53b078fcc32e5ae40630f4ce5b44cc9e1a1f3327ae04282f87be45144c87179e02fd43f07ab5481061e584939c4d4f83bba1f5981c61d58090997aceba142e8143b7011c98358879f42791849d0a8039763e4eba9a7d7ca381db86076f934accc2bca39707ae911b12a8fa02107bea3a4177375c4051a71602b85def3abf65cd963040d3830399214f3a3b3dfc047d6aa0e16573ca7981bb85037ad9e383a9ea34b048d36b0d191b6f35675ceefdb8003aa081a6c60534a0f558f9151081a6be043fff49d5163bba71c6a60ec638ca5810ff9c972e41c64c8c92a41d04003f71d7de8adebe4641242d038031369effd245be78e4a3a3370e9f34f4bc851206894810b0d5172903f27227a32709d3cfc9ee0711cbb8d63e0aaefd22efdf3030d31707f11c9ba3e870a2cf033c880fd814618f86c1fd77d7a8ddabbe9810618388f13b284cc41d6d6040f34bec0c6f78ebf3f1d739c1e59a0e105462562d0168d9efa4a078451c60b5460013ad0e802976b931b22753f083a7281d71c6bc71fc7b499b2a3e240630bbcd5ede5455bcbd9ad16b8a82f9d9bf2e7bc8fe040230baca5bbc8fb96cbee3d37d0c002ffddf519b463dae8ff0a57dc0ed1c2232d56e07a42355a5eb09b0c4b15f838565bd1e07160eff7b6810615f8d83de1252999027fd92dd4475aa4555d030d2930f173749dec1e561525a18146149868179d1cf14ef2268fe640030a4c7e9c5557dd5278ee9c81c613f8e49ebac3f441ced35771027b41828d64f6b1f3550c349ac05ae81ceb64d8c71c7a1708030d2630395965f1fe3eda8ef3028d25b03b9ab3c7d5fcacada34043097cf411f432789015638707ca302d80801862bce0cb28c3b4e0d04802e3d12bad86da8a791f3828430580a081043652c5d7cc99c390f912430c1c94a1023cd03802377e3972b38d516d3302eba1a318c273f67cc91781b5fa96accf715cb73a11f8b571f588cecf761d43e0a29f4e07a996257e84c07be0fdff1bd563fa08029ba277d22c16f37dd440603c96d0d3fd2ad19c1fb07597f5274325ed4ec3074c6994c70ca5e9051f6a9ac75e7e2e1e04c370800bbe8c09dce10b5e705143caceddb9eba2d9056b211d4535778f27d7051f63a835174cced1ad564278ce61c105b7259993dab3b760ed3ea8d8f9f9879cb505af3147c15a30fe7f79620af338cc2e2df8fbb4f3c8983c0e8dce82c929ada7477f360f4359f01f3d3a5b08dbfc10c682cb3313bff50f21f383c516d2e5c8b51cafe06e2a5a86ce925cdc7305bf97f63cdc9ed0742bb818ddc373f1d528bab18295e8d0e187d329665fabe026071e72de73b5181255301e858eafea3c52c1ae7706b5ac57314d1a157cce1e6f5aae7979879e820f3cc7518a5ae96f7b5a1c7c610a4e23a9599e4eb0f294820f838bd6646fa9a79c147c6c93424d779b46cb310a265ee5f45f3956144ce5a78fd3ce0d05173aee77f97ad6b8495030b5a956d15f73906dfb0413ffaa6d237f1ce7c87fe10936b6c58e18d3a4134c7ac4ea15bd39c1c450eeb6e2ef179be032628e6356ab9aa0fe7e3f7a1c72647c91896f27471d52321c10430c31c420e3085f60a2f4f8dfd973d091c2179760f34ff434e9225f5882ffcbe0ef6f13257c51094e274348bb8e2e2265a4055f5082b5fc3925cdc9241dd9f4c524b80ed6e1e4a02a92e032ddb23f47ea305850055f44821be98e427690a865794830b919835bf2d69ddc3f822b4b393b76ec1dc167e7dcd21cc999fa46b03173e5e8418598a3723082e91cafdd320fbe9163115cb21cea56f49c1b2cab0826e4f3942ff3da78a02682cd651d4c53e44afe8808b6ca4c9387123b04e3b17658aafa3885b40cc1c7cc0829b45ff0a013189041e80f15b4800c3cc01785e0a3f4ce347d1b73bc1382cb2d4fa331453da6ce20b80ea23f66573d8de60b82f5482594c594b60e1308f603ad8afe51fa4110104ce8a01e85d04cf6a1f2076ef7e3e8ee3c46f128e9072eef46f7c0ddc3cb8ffac0c5081a214a5aebb37ce0b234a4742947bed8c37da1872ff2c06aba6ffae8abcbe3e418442cf8020f8c4f5ecc71fcd47ea532686081174060bfb8036b1f47eed891cc33331340267c6107ae7224b1cb336491f0451df878c5e3a92431e5075fd0813defed583b42bffa44a48967458a8b2fe6c0e6ee642d31338b4a565545444444446535f14c85f0851cb8df5649a9b3a5836ff28b38b02f9dbd5fafd7d68703db5171624bc7bf21be810dc1dfa2ed468a6f6ee0a38e7437aa473d5d6de05daa35c7219a9a5a6ce026bb3d5748759da635709da30821ac420e6f3a6ae06f424a79bb59734f4e1a78bd8b9756220799f26860b7f5c3d114ad39e567604a2c640be2d152889b818be25ee31632b88729036771723ce9b5103d0a19383bcba1246d7a9bca18f830c6ebc8d1affb5831f01e591d74c68899cb30b06983f5b4f948f0110c6ccc1bcc3df6cadab9ff029b5cf42bf6876731e4bdc0d98ae618f32fbded029bc646344876aee55c6023c498fb2ced0712df0213b2e438f6e03a95365a60275d43538cf61e6a1698b452d915f3051d110b5c9e1cb24bfe10b1b42b306d3976cc1b722b7099b7aa77a3794bc4abc0ed6f8e57c7538151dd0ea2633947f353e02c789c7c6dc1bd7c29b09ff2d8c7f13da5cf11052e5f64c971fc010536c6e812d29656c71358cb243da9c427f576029f922a62214731e634810916b1ec3cf460021f5f42646aba0436bb4d8eaac7228712b8bde492979db6444c02771e4fb35fb6ad1443025f1ed55f8ebe6b5b4760ecd2f253b53c8e268711d8e8d8d378daab857c119810f5d13cce93d77222705b151d9a495dd487c07648a93df2fa384e0e21b0a1cd0e02bf1727b7c7162070aad3515beeca55fd80cdb8f53aed173ee053a6cb5cb1272fb2bde0437fba1c5fb556649b175cec3513dbac77c124f5fe280d213bae5a17ec7444dbbfd8f5489d0b6e4bfd3f050f51f76370c1bf67b8dae6dc82a98b24390e7a752fb7059f42dc831cfe69d3a3165cecae9354936f3a4d0bbe36777e94762b2f330bae634eaa21455295909105973c32d29e85d94f4c2c98dc9e1c4775f5907358b05b3eb1a7a30f7378bf82ddb5f4d8394ecb61bb829f8c5696bb3678c6b4828b90e37db81556b09edc935be6ad5cd155f015e37d94392daae066bdc404369ee0021b4e287448a89c1251a2755495b1dc31fecf185f060d62e002181c28061b4de072794cc9bdc3e2c79f1b4a6983094c88b9c394a3d42559646163097caa34d92d47da719d1b0b1b4a603c0eadfecb9724f0fb9a7d39aa70cbd2ca0612b824f5415c1f09a1c63e021f52d40e543c9cca5d0f17368cc0e5a012a4d5b42d864711f89bf441274446fa58379408ec7a640e18d82d6c0c81bfcc1ed279316d2f86182978810ac270401864d8c2861058ed0c217348aed5096c04818bc1de36fb6eb69ae80f3680c006fbd318123f9aaae7077c64aebeb776d98d6fc3075cbbbfe6f07d3dc58ae9059f2b86eb94a4bc602f772febffb80b268596ec28847c518f2ef89cbf4df4349248b072c1abe455665fcad131840b6e354b6a4e2e7e16f15bb07b51ee22e56187d9b605631f335c3a900e2b2db5e02a640d8b13a20aa8418b3ffe7966c18ea4e6405c6279c4b42c78f7a8edc3aa634ad3c5824d953ed9c69c64591158f06d9be249dba66ba8c62bf8c8b5b25727c6155c5caf3c29872934456d051bd3936af28929f99bace0377d9021620c39cc8ed558051fef7e5ff9e5fa49d40161100c84e18030c805c2704018c4026138200c5281301c1006d9430d5570ffeed1bd749a1e4bdf504a05379e2ea45e6d8f2b846e2879a2062ad8582a398eff9782e4e886528d53b07193f5c5e8b76717baa13430051f478c1af61ea68ce942458d5230b1a2c38fbc2a7fea6a908289103763a8a45b992432ca50816619458d51701beee9697fde715c51b0d933e9ee583089135c50d408051b8389774e53279a1a0656133540c17dce2c4b8d94ab43a599a8f109bea3cdafc896c5724c0794612e40060370605af01bb0440d4f70e751b49798fab24d8e6627d8ac1e43c813aaf3e300190cc840c143d4e00417fd3ba88f2ddd2823052ff80a941a9b28316a6882fdc8f2c5bff330b4d41a99e0a67537771ce858a881092e95d8e5b0b28698347943894c0a352ec1d46647e5a1e696e072e36d5c4be50da55209c624bec4f5f8410e2d093528c16fdecbdb10bc1b4a93e0cc7bd2e5ae9cf99704b7ea512c63f2b8e9b38d0497ae93fdf8f9c71a9290603aa456ba1191d8315948a8f108de62f0d698e358d555c10c62503ea89105dd18826578941b4a8de053f61cef886e9c4e0b5af0648c81031a3423785d4b265e21b4523d460c68008319a4a06a116cdf98a4103ac49ceda08622588fe021493bf286d21830704b0417f9fad15254886073734a8a915cb266c10183161c106a1c82b3ab0889f2ce285adf50ba32041ff73e56cd607bf79542304962aaa62eeb0f5d23d420043ff93fe559d08e320383198c8183ae16c060063808ce24e63052d2da9e985bc6b7e07d05c178f8e7babd1f44b21e0826c4747bf943ed30ffe3885003107ccc9c18f25c3c75d4831a7f60b3ae3ee438523fdcce0d2517a8c0d8a10217c0a00c437e603bd3fbbabe52ca1c7d43c9ae50a30f5ce794b2cf834d6ec9583ef0b1a3727091ceca7fb9a1944fa8b1076ed56ff2d2e38c1d74500f7c4eb79c53e40e72fbb262428d3cb01fa5497845cd7156aa6ae081fde8317310347743690c18f81db8ce51cfb81ee4dd98dd501a830c156c0d3bf095e6df41eff2febb1b4a64103a5b841a7560436bb4595c4dc990a203d3efa1c70f2bbd624bf6851a73e023f56439da29c50ed25ea821075622ffa7ee3257879e38f071276acbfc730a518503fb17530e25920e704018653420f406763d0e733bbacaf1476c0b35dcc09b5977dc1de4c89bd20da53670b6fd71de1c3347be1a12d46003fb51aaefca49b3811a6be06b243286e4ba8fc1afa1062e87ff41534388532108851a69609258e420f3a97b649e26d4400363c1c3cfe1497d64963f035f1d96a7df38a237793370d12c7328fa52997d978117eb4f97ca9182743419f8c820995b3a66ddd18c813731f53d49193170d92d4b26f14e2fc112066e7fb26bc851eb3dc782815defd58b4ab9adfefc0237eaa17a9e2647fbbdc0571e8fdb253ffac8a30b5c4eae261dc7c92da5b9c0455a8dfd51accc926a0b9c8798235bc22af6aa053e3a0f92c5ec965d22ca0217bcfd424decdba039a10616d8eda053e57852e520735c813b49b97356678ec8991b64180984d1810a8821c61849871a56605dd368d61c2cac51054e3a95e78f37256f4f161994a84105d62b3744e5541ed8f81108c3016140c0013b05be430f11a1366a45bba5c04e0e7b3aac8e7bf3e651a8c2f33e8e5ea1c045bc1cc7d173d41223fd04f623861c9ad27202df99632fb54c7a49cc267096ed4378909edcc3d87e0d480e3598c07db08eb672a5ded0713596c08203062de800d4500213d3a526c6addc50320aa3031100c1ff11438c1a49e02c5dce71e0afa139a6379460f08231aa4a0d245c96a3a6c761991994412e3082310c50e308bc86afdddb471bef9425d430021f471a2c6dbb799ace29029be1e739d64b9dca1f22f0b1a43c144987c09d69cc6331e5c98a8e10f82e8f24a706d38b3e08ac4e9654d9a15aca3610d8fed32c8da75993f91f30595ee22966bdac8cd7f00113725b99e7b8da3da7174c5b471eff5e36b3d0c10b26794509d1b4a76369175c65da8bae90fefc2b37b9e0c633e4586c93e4f471c1ee4607b99537784cf7166c56648ee320affea56a0b3ecc414efcbacfa2762db816f1dcb1b36fbc4ad2824dfe511c17cd6c5bc15930214da268a46e09a92cd8dcde7ce5d1a284d4c582d75ae90e1e794adf010bc634f98bf8792c9eda2bb8881052a3460a0c57f0414fe3dfc6cb6e293204305ac146d48d07fa153058c19e7734a162049fa89655f0b5163d22ab5405dfc1273c57d705462ab80a0faa7932dbfba79600062a18e938ed871bb92c76fc29b834f992fb96746df6300563397eaa55f12805eb81657aba0b91c1d2a460cf2ae45d4ef038d2370a3e3c891b37c71c42494c147c0e2749ec5cd17aa450f0d939479d1f42a06043d3d8d9d4762425d14ff039ac90d7d367d3247982f768d327adde0e3ef14e30b5717a4452cab62c9c60e3ef7fb43f7aba9ddd049fabb375eee86322a526f8f8f32f42b398092e73457f895a4132476182e9b053ea146a5d8289af1ba5c3cefbd4174bb09ede2f845c31727c6725384d8fcb538a581e3d5282d3c81343d7470b7dd924f89a1c684841752a3c24c16ad7dee46de4eb084582efd471d4dbae8f1d4182b51e0901e3112446e5018623981edbbe9cde413dcce50dc06804d7f6913c65fbde473e23d8d8a697a7438f45b062a79d62e6e8d773ad08467cdfc35add93ba35115cddf98687db8281083e72d9c79f3e128c43b01d47973779c2c3b4794330fd71e5383cdc8e3d8e2e04dbd16dce41a67775c91382fdbd98badd3774faf82098982ef6498eccc9122308cea64cfa329b7f90974070a31edd2a254d1e270404df1d9a29d93430fec0f5c616d71c727e8e3330fcc09d6e88f6e4ae5396f7810dbaa9af8310bd223df8c0b7e458d874925053b9a1f4021598327e0660ec814d0f5a39423a891600861e18dd4bbe7123d5aeff3c305ee29179877e34e9c6037b1e7b948387664e46c0b80313390a4f71246b0776d2c358299d11f2ae75e0dfb378468e3ad150d2810d0fa2e668de1cf8f3208867c8fc561901430eec45851c78f889031f7675cedc6ecd963e70604caa52ef428ed637e70dac6ffa1cadeafe4479dcc0c49822bf8f6f44efdd06d6fb6d3f160f36f0d1e7903eda6bd3cb7a0d6cec98eba1b55fe2460d4cecdfe9307d5093b14d031fada52153670f3d93686023c7d1e34535cb693c03f7f6bd9dad3ab2c8998189e641074619f8f81ea656cbeade412a043806186460fabdd57ea2671badc01803bfd1a7c4fc73dcd0316650460c080c66906088810d1edac7993b8af2fc5906c0088381a1f604607ca11bff4f2194a93400c30b5d60528588f8c71d648871e3c289813000630b8c6632b7e82e185a28168091859b0118587801185738a52da8984a749924538fccee26460ca1424e91008615b8c99f357d9c22b3728501605481c438021854e0abbbb752e8f81962ce14d85193be1c1ebf3bf4a5c0757ff42629ef47da61a2c0a4ecd4f17b103ce48f02054e3b0617cbf102e3097cc762dada9d72471fef0426e59f546d6bcf314d13f838d773664c60229ee7e40e5f2d6a57810dcc2600c61298ac4be3c9f3c4df1f0c25302e390e738e7673b23cb5004612b8241d7ae0c9a348605c2d791c5ced7fd53f02971ab2e598584afc14237051153442f4acf60e2f02abf67120a931c729912957008308dce68fe3de0d4dd7be72018c21f055bf51fbd155d20e4a40802104b6434549cb92536fccc10802df7953c6a09b0308f6eb66338f42f903de72b47a62836073b2febe781dd68582e05390c89763cef7ef02c105ed38c72871f5a31820b8dca2924f2aa528da1ff8dce9db41a71492333f9c2e0593a4397d603487ea460f63d09d0ffc5f568a740c1e52b5076e7d637b054b0f5cab699d455ef3c0fff779f01c925908513cf091458f1afdb903d37f9fd3d573fe1cc70e7c0ebf63c7f9af03eb9bdde7913c7df0e9c0a86436bb3c3ae9e373e0c49374981f7dafe572e02ba59a1c72448ec38f3870621bb2bf0e073e5ea49f776d0e62dec04b7a6787ccd107ede106c623fef9e6c4f8ead1066e837fdc7f9dee410e36b0dab591a5f6b57eaf818f228668c72e313f450d5cdeff4a8be419bb34309a21ee45c793975a34f0b14d7d4ebd1bb2da33f0317e0e53ca93e868cdc08ee61c6f927755a96560d3bf3b8a7b1fe5a564e092e78fc394628af6730c6cc7f9aa3785ea78a7189812fd28a5eb5092ca30b061932fa45fca1c0703bf962b72c5feca397e810f435f8dd8e805ce2d74554551a910bbc07810d10fbf24e87ec9056ea3c7ff2fb7052ecdfe73e8cffbb86981edfd0fa3721c7143cb029fa3ce919249d0945258e0c6d3b348ce61dc4a5d8197bc132f657a2c9dacc0a7eb3389163c658cb20a9cfbd4644f1c0f36492a30492ce57037e5503153e03c543bf82b052ee767d6bc5d6d46a14ee39b7de981029bf7d2fd47fe098c5fc7da69bdbe7177021fb3e3b8ae52a5b2de045e3c8e2ba60d13b8c81d47a631e97f97c0849468e9a343097cdeec99636692c05afe4c2d4bc1022081bf0c2ad1999b3e2c588023f0932d2a7987943c65b1004620dc8394325d5b04eec7f6a2f344e03dff9b7bf687971d021773a81963fd441285c0977560f69a726d14042ed86fb0dca14f0b044ec326c43a5847447fc0a7501fd9ea967a9402f880c9e157112cff52b4177c54fcde2cbb9558f282b12a158dff9382bbe03a3d481f724e175c84851c62f472c147bd16a4cc92e7c0c3053b693de4f1289aa2760b7eda3f4e53d59662660bb64ce266fae530f5aa051b8377b449bf532c8b166cbe7854c1727de669165c5aab0e21757a782159f09eaf34a94e98552816fcc70bda99c3942d42080bf66a6f638a7ff539045fc16b65ed38aafa784582aee0d65eaa2fc5cc20116c0577e7e679b27e381182ace0738e589fb4b30ace3bb22cf1fa62c8bd2af8380e3ab458c8d2da53c1c6d7ed28799cb6428e0a26e8e5ac11cddac4e3147c78971b661da6e03a94587b9183fc38a5603c6809f1f385144cc7ee5929a68c821d9d249973f471f3150593c1ef2e5f4377e886820be12d28d8a8e3e9715ca123aa9f60efd2443ada9c424e7a828f93aa7dfed8092643a8cbfc5873e59013ec48e4283fcc951fc34d3052bb316b7bc48f424d70ea15ed73f4d498bd4c70a19364cb90539cec30c1fb54bc1cc790af357609c65677bfcf35278d9d25b8f01c64d6e822b5da5582fdb8626ecd5795923a4a20c9d6fc833cdd24f8f20939a6525f4fd149828f4bc783cafeb50f7291e034a34e6615fd387320c1a9a7a81c2b7ae0d779041321fa974c2bed3d1cc1edd54799b9a3117c18a35d247fae440e46309d2f739c73d89b3b16c149e7ec211545b07ea51da40d21789b08f6453b47b446043b21636fc7610e414cde95bf1a43b039cc25a93efa95650ac1dd74a839b014bcf642087e5c5af36e0e4ba73208a62af3e5384ddb478a20f82a3d4d679625424a20b8d431ab532ae5ff0b08fe23654d195375d2fd0367a925a634bdb935ea072edde71c7e4c1e249a7d606af523a7d8b1a2997c603c468f791bfd72977be0ad5483af957a60bc34a57a550fc4cf03a379bd37b35cdd82786037686ebbb2123bf10e6ca9744d8bc59446b403d39d4ea5d5ab03b7d1227fb2774d0f3a702153eaea184952720e8c9dafea5dcce4c0c4f61c9264e3c0767c7a2167eae54038f01a29c450163569546f6072dc1d7f7db4e4c70d4cddd4469deec5b40deca5bbe74817a3e5880d5cd250125912633689ad81e9de6829efb857871a98ae9c69d35d2bba69e045356ddc5c6a2e7934309ed473fc62a926e9676075744434a47c361d6660d3b34d3ba79a98ee3270da1173bd84bee864602a99a98694bdd43206265245aa25a5889d18984ee983848e52c894090397463d59927c75e90206a6f256ddf8fb052e5864bdc0f48876debca1834c17f88f7cc3d3947b642617b8c91042e8cac99fe41698e81bcc2ed846fbd002d757517c5d5a43c52cb09bbf22425a2b4288055eaa4d7f2a7f32dbaec0447e0f6c93b9477959a1f0345215b8f7ce8eb4d73dde890a7ce09e1b9da39a021bd5fdbd77d19e839414d88f3f9a8958a58f211505dec30f69c173a0c078e86d133ffcbdfb3c8173d3880ef59f983e4e60d36a8acc21a7095c72ebe83d5aa3ea6702978246be91aef0f597c0df8478e8214bd4f595c06f57a53c9aa24cfc24302d314858cc0bd97b24703988695b9d276bce7104aec3a2aa648dc0c6cd0f215dc74cd751043eeeea38979d25d31081d35497d26fc463b487c087648b88f614ff324260f2583cd7ca418020b09d3fa19d3bf4c6420020b07124e7fb4731067d09f0033e969cc27a34be472e017cc05ffadb47befa385e7bc17b65a7fc0e7e1ea3bc606c6248a943e90d95ee82b7d01a4143db063b75c15ddcd0fdd9c2db82b9e0538b550e3dd2f788b8603fece89edd5a72fcdd828bd289a57d3992fcd982f1d8a343a7091ee5d58291fc122fed3ee575440b269b95e66e8d939da259f09a35a26f8fc6c88a64c17adf7ed6e8fff14a28165cf69f87155cdced030b3e3755c2b2e7159cb8557a7b4a65d371051772581dc7f4b9fe7f2bd8ea1c996bbd5e2e9f15dc7ff6ab607f7b93e9864696bc2ad8f48fc183ec11557b2ab81789d5573974aa382ad8aff6f455f7f13dc729d8abf330c433514d394cc127778f3d664bfb93a314dc7bea4bd2f1a4602565f6c6318bc1a347c196048f1f5958a44b8b82cffc496269219fd5a16023e78cca29d8861a14ec7887e3e1e96651f309d6a3c7d6287a828d1e639b4b0ebf9ce904939fedbe597a62c47082bf7bb3fcbfb516b309be3645f75234c1771cf94359a499b499e0d24b3afa1f13ac7d4cf9f1a997e0ae376e4f260f54534bb0e92a59094e738e7c2292e5b19460a723dbb68ec81b9d049363092d0ba224f8d441524b1f16093687b667453cd3fb20c1458f8fe0ef265e6cb7ac12cd116ccab86649ccf76a045b2ffa31248b1dd48ce03d43c899838e39de22b8eacd91df9dda56041f58584445bfd02c117ca56c77f51ecc24426c208255bfef899acccce2c5c621b871df4d6e1d593f87f5c08621b8d0d93bbeffb2c997d2818d42709df3a5f871b6ad98b1fb810d42b0ef597573070f3ca47b105cdc9073d0992fb7871a41f45e1dc3235d2f105c084931dbaed546bf00c18dc45cd9b1849c5a2a7fe0e39452e8bedacf76ae0d3ff0b9a232bc36c7f187ba0fec6877248f434a2b666df08153f7b0e3eb45dba54f07f6d0978e6f0e82e7fcea81ff8f2faae7bcafeef50636f2c08418bfcd73b87125ef36f0a0bbdea7a9abdedb8007d8b803636ad143b6903506a9edc09b4e760d76d571c89a3a309e13343c4dde182ba503d7593a7bb493755142a8d2c61cf89c935d78669ed871b41c98f81d33ff85b60b4fb9a1348629a305ef486cc4a1da061cf81c071e87ee59dec0c7ffb03daeccda7003173aaf3ceb6aa30d4cca5779a9f7d2fd48850736d88036d68036d4e0eec609c99ca543bba1fe8789010770f01450ba4dd848830d34d41958b18eb610f4e37de430035b27adde967b228bc446195837efb0fe754d447b316c90810f724ffa527f6c126c8c81f716354fbe1127d81003933fa5a47d1fe4f829250c7cc8902da92347fb1f30701b747bfa43626e56bb041b5f60a3e4e68e1f6f0ef3dc0bfc4f4b7ea9575536bac064ae9cdd827ab4de41aa6c70818d1e564a77e9a38d1d5265630b6c59aa7674316d6841c961be59e042a744defdcb96dec102d7e579ec2d5dda7ecf15b80a6e2a5a1a4b35e956301b55e082c468952b6d06cf49950d2a70dee953d33e07c9828d2970d6ebf1e307f95a1f471e6c4881b1507ef93e654e694f36d8880263693446a7e4aa95bb63030a7ce5cb6d8b8aa8b4adc5e2b1481c1087c30151200c06ba331fd3140800182c268d8502411427a2b03e14800351221c3a242620201a12141a0e0e140e8502815028140c04c1c0201810080683a1a08bac52c60f693771eb0f7c761f762dd2ba4e513bbb8529e25ae5eecb45fed7f3854cf14f117b026c4be28ca599a5dc1dc0c0013ad5d11cdb6701a58d773b5c5c9ea737e85837754f4c9cc607e8b00e73ef2ff53ccfd5b6f2190dfbad99da6277613d41820a29bd227d2f8e7a9fd7f5ac4c4b6fe9a519f9475677a107eea48a018d6968e3615f8f1279d529bab95c24a712b61bc39382ca81e23d4fe4995293df6f1af514e9ef453933c61350c08535841b479fea59538be9f88dfe42cd856bac8e943f75f5a18a35759d22f3caabc5eaa0e5889ed0d59cfede5ea2f33a97ddd22dd94895d89bccf4795744747ceeeb6c0e3d8be8f82cc56a6303a93c429a625e18f6944e6d8c9c961b8d61d38a6097bc392aff695d315f329f4e1673f4e8cff4066fe8b9cec668892d99aad6fb99f68b482404aaed9b7c9c6c58c9b0c302f62418d46044c7f34b7b64715279bd207f62f4947296f1c05cbb5a82117ebf7d0af7bd4e4775ab7373c8c77d4e7775d57c66b3b29c17adb5a7ec1dba871cf69c05dfca0d03656f2a350dfefbbe29207c3d5b4ac48fd4b768b4ce45eb467ec8c8f97b7de4f36c3c8883887c7b55ed147b9a4c940d27a82a3f587dcea736cc726eae8521e7fffc1cdd61b108ac6b31bea17de473756bd8ee3d0dc999ee69fd13d66a4321625ded250da36fbf3916c2dfc1851ff0bdcff2b16633d31990e56cb2a9343906c394da1bed23d2a60be2d0aa3b7db21338fc833eeb5b7c3de62136cdd2fb62e9fbf08353abdddfc19ddfa019bd9518cc63e6c3ecafcc895dc6e1efe955da6b9d209886eaa99ce73e6c3799ed7df86c347e78d6dd0313dc8c04b579d96179124ff98043b0a7c719b9580937ddb1fab0bcf097cdc836609996c7a40d2a454f3b81a95b24c602b2eacd72f41c38b1bac783799cb66a4d4d178904276a1154d81cdab24ec495c4d88fa1d89e018e89678946ab7e802514cee592a7d1c47c1840f8a223dadf85a1a5a65d511921bc10aa7b5342c4e9353c15f621013f21233cbcbfcd95931f2ef531855eeaed730965257c52b380d3e508df63daf70dde5e7e89b80ef1a857c8a9170e62b8feaaa3309e43bd6fc075377c0bb2fc40dcc3f1a8fcef7fa761e76098300c6a2c4a6bced9a087d0ee931d3791eb89edad1cff5f5a84de0eec40e78ed02412e6dd2cc1111aa84be12f80346e2414d264542e117da0400fe81c090157c30bf9b06674e97b619d6205143cc30fcbab63f0775f18ec8eb7a71508b313e352506dd041bf3de7de8bdbe8b2bbe9076589b021e3dadfd1955a2c2c84875a6173227724d2515e36183e61b793499a70e46044e4c29e76248166d80603e9c48ad0d5027f1135a128685351a2550d1670051400d24ec54b00dcf22e7d86fc904011a4fd2c0a20d41dca1c129edbc3948878bba4b95834d83d5ebb2e08afee5913f69ed843b1e568b4e12891241eca75e2538d8413021705383a13bd6bdb265a8d5b80b0c90dee0e7045722b08ae45b7b4c8edf2a0e914c3f9b434bad1f5a2e047c06f0a7608aab15e73a925abb7d70e36ecf8cc0af9f67a43b7163d7ff0d6cfce8318c61a0896df32dcac802808ff16711600c936ac483070fc0f2ba4cc241694520081a4b81102a0d02b301b3a04522ce02708f9e8cc4732a00f58db0dd94092fe0b49a20d443b860f9afedeba6cdbe4036bf296fa20990c509777a830b34b25ef19fb6f045ab4b5a482027d953fd7dacea712d40ce35d531ad051abf55504607a37672b0500bb2fd91c93d32d7b635538dc0e7f14765583c467463b937992cf1a9def480dbbe6b4a3815ea8398c9c10e42bbb34103db7748cc554a8ef470ad2be2631a73b345f2e7ae651c8ed519da1a2546edc3f130a57fca122b037e50a6442805a3bd7d6086ca64197ced77140e90076504cebaea691b06a960d0d30251329f5a226eb850500a27c5c2580652e6619de2d4093ea3fd4f9e8e27147244503a14131a27224440589fe40fb588c8cd0a05421070afcca09aa2e901cd139cb4bd06633e045d6956957d79d9233ba1201ef8a368e4d2c5966f3c8d5901a1a04d1caf751189aee788709a732c7648c9e50725fbff751e60dad3b752548b0d33bff5f33520c151bc11b8d5804448c768aab198c1fcf68c5f6743d05776433d23418edb2c56877d3902a4e530c56401dddc8837fe9f130b0157d92a1dfaade9bbfd29ba2b036455e78db6fcb45a41abea30f10f323b5241e75b0f34e6ff68b52624eedde8dd5be4fe18ca28e2944eb38c1c4ffe07d5e29c9bf018e8f22eebc7c5fb6374f85770c846a4d3c0f1616b0fd1decb28f0941ee9212be3d83d499c6106952ed67c8185b42558c1fcacb98fd7bd6d5986399dba68dedac2d4a71ecb70c3e8752b7ee14f71b7832766f86bdd42d7a7fbe6efa5444faf4042c9361ef512d8126924bf9322171b484ded19e5274cdda5807a7aae0fc51d364eb529711368734bd947558ec9418ea579be4184895d030936144fe93697e774b777ac81a701f75e6ba471777d31bff7e1b84f228784d3a9dce208b022f4f329359397ac93203a16b69376b1d988d11ce5091aeb66ada142141234fd93661b5a2178f6646dddde07a46af10649844b097e5865cd5dab2fc680b5df777fb486848b38b2670e242daf32420e91f6c1a6388159e4952a2dcc41f46a19c950e281124fb3952df1a4add9e09af345001a00b9c31c1725ff1f844964100d5c292e2f43d1966263276b9d20b0bcb15949739b4170b7a55aad1cefd38aad681f8bd50402247fcc88ad3ab52440872c236b87140ea3857e28be36dc85b9302168d219b4f45321aab5aeab7955b9fa812511e9925a74f291586a8d25ae0b44cd7765ec1e87848a06377b02f0116392f326604e8a95cba36391552c1658d02d4d9874da46e6093c71a0fc3a3f4d4098fbe167ead54b576253b08845d2483f0a824eac4a8a02a880249ab082708b51dc7927a0de308ad21500c355a12980f6c9c062e1c508320ab7038d5ff7a19619c61ed9946942c6b42419533682462b2f3decd3796039c960728c85aae134e65af3d61ed10839a4fe822366266b105d4a085adcac859ed144b8be8cb86b1ba230f34b0fcf7dd4a8c8391a7f5776891fcb2e2486f0fdc6bef6b48462aef285678700ad4598c9883cd7aa9c5ad8552750a39f39f502917b169b42430a5ace7431b952c9e410d941eaece4a17ee213848b9355ad57afe593abc9c4fea348ac2d10ea7452009a5343a13c184c6dc102f1ea8511dc4125a089066937b4d6cef512da9d512f417fd38cf422675110c519c04dd2d6adf3080cb87e3989f6ca5ce9ba80116064a404bbdc3b68007d2ab1c2d9f7ff25cf3da98bf4cdee10cf02ebba3de4482509bda1154c601a1fd1ed79da392d1c93bd0af2749dd0eab8ded4d7005dc739c4e344e17b2cbda28094d1d5f26963472d032a2d9c9a646bb03952105b70e133dbd996b5d48d3ba79443c87cc5321a3480c92a49a7103160b690a50383c5124f8aaa8306b7cca237446fe3d7fafc559bc611572b1c38d5c0b4d0b6874693610e8519129f9bec37afdd856cf7872cd194ff3c345062319c22012e637d636454fb58f2f04f63585d4990884a559808e2c0f5327e4962130343b0c2b7b7588af0c16a9a057830a350aa19605d52a10bfc5f8fd12c98cee5006f041588caf1e8acaa74f924fcd2fe85bafbf1cd2d912c4c0bffabdfbf0ffa004c8093803a0ccdf7e0058fe8ba6a96a36c9d2029f7050cdc54ce84fe8a90ca3fdbdecbd375eafd4639cfb59ffb9beef6334805e1a4800fe15208284a75f29284ffa60a872d69a3e9cc0c148b996588899c58a102498c5ee8f3ab1244ea954acf69fac11560d4deddf871b029641e65be06250b95c724740d090642f332b866295b4f530b34c9b9f210295cc63b76445246e6c2232b0494e86eacc71c7ba78880431320c706c799915c3672154c3bb8eb665e03271f942f669f798d126d70397303ab331d3348afb4ef945add708b5648af7057a34e79e9048dd9d7646c31888717b69c1c06120cdc5c2d893de72735811d06470bb213d45b471600b7c28f0deeb4d1c68fc0f6bfc7c84de4a847269a41a4108081b4f86e3bcf01f2c90a196cf9d097c4d256b120aab11252f5bb84291685c9a1882f5eb30cabbd39c052790ea557b9ece5a21a977d1754d9e2a75ca58a88caf638dbe44cd635694b1212bc845c8d4bd0a13b59812a1e2446dd31fd498a9c82a7aa40cd59d0b37fe93e30e48c177e0eecd984d57f24e8a6825000ec43a744f25a0dbf4c99d6cc28ec58f502cea64b45e1815699f012e48b6022326ee5e59a178a65c0f6fb7349f022f0267d64e4565189e701bf369328efa001de136981a9c7a8c9adc2a6b5a24f03955364c732ec8d4f96de735c91ae66e1d42d09bc69d994bc84fc84f3ab7a24a04af28c31b51ff0c245bbb693a552a404b504ba025b865a885b05d80f606aa490f892f371fc9ce5ef7aae04c42c531cc85607065ee93e1b6e04fbbe91af5e353f4137c71b6824c3e5d5bc4aaf2c42679a28c0a1127c49775268de8d06073f3462898606f53dd46bdda814ce06dff56d209d9babebbfa682902103ec403b7b3a67eda646593954db636b6c566a6f6b6d99b35193d3a7e7bd414578a0f9ebf6380ffc6c75134265d07d9eddf9f0715012ded40ef389323e21ba89409c5054188d963d9274eed6aa843aa89ab93c70b0fb5c1aafbcb9acdffd5c00624c0636449084677d14e9a4f9fe077ac608e377bdcd09ee77fb4695fef5f262f0f5a739d8b1186505968df2a709ac8b6fe6f9648ebb912d3148dce2df166fcbcff5fb8906b6e55e76c16440fa2dc46ba4b07dcbdf053671181b14b2867730ded89352f6762d2131897373e5c1d2d7bd17311540319179def5269eff093968da077ae2b3ed8ca8caae1e7cae7a7f2deaa2b4e71e61b6414d3a69fab1318c0e66e87b2f12a57ba9218f72be4682939e883f6eba3c084b8927078b24775ec1c439e8003c8f2f345592a2112d355234281e3f4f1102311f0f72a88dbfc3cfdec5049b074607b95bcf1567d1bb1d6d366dcc96ab88e4319e1cefcc0d01f2b4e3437a1c18a62b17e8417ef1750d1327d132a97f2f4906a535b9024e0cfa1ca426357ba51c2fe7e2b98a37f44ccd907720877eef03ae6d17464e110991647f5bb9a6fec32f3bb62625b6bf4055ffe483885ec0f27fdecc1c423b2c98104a42020be21e269c80b01bc1a42e7fe51cd9155ec1990923da1083112d481a5c15083853c152603bbca66fd55e490038d8431f95b27d32913c2b14b2b97091d4e121a224d4056954a8b4fd7ce060786c19d864bc5374de2b0d95518b3b636353b3e7a81c91efa6b3ab0ec149d7f63805ceae2aad12b7da114b59736fca562438d149c648548d7f152625388ee3231a8c2ba1e607efcf044f41813d5c35238ac945594250fed10506be73716612c949c3961355029f1925adf7b612e55e41f091b1d148560c96896e67adaa1a63abf2ea2ac871a4213b23680ed1242990214562a717b9a0db90af49430775dd16bf014d87e72d2164933db038a8e5fae4c038cb34e2bd2896624a1d5d14a93fb4b8584764579f9a855d8c619904aab0857e3b01d906eb6e4a3a1d9c4c384e162cb4eedc9af57205978ce37df4ae5ae90ab408c2c4b87dff06e99e1711baecc5cb3076237b71e4adcd93e7e59100328b45e873c3a8e6f55728f6d1d22e3c87837dcf5c0c7516361ad67492b9ca8a1a08a4e2e810755a849ffbdf9d46dda6c50ae75635490e1167b043c323ece0a7d8caa7fc0903178ef9eb52bb89bee3db2042d1f62807131cdc9841f78280d50f542b382378ba84fd4b9f5cc3891c6d64aa86dc0c953d65402a32858facb6645f2fb6a7ebfbed22063544059636fa1bf80896031a21a7f81598c22964620823af91f0b090b7312a0555f96441199481bdc5847c30d57991a8e12230923c0c89b9b0e7b1ed11fedfc66e435a98e921c4381de0dd271e98e6413feb35ad233651db414678078d3f895ef03bc76a8e9955a588cc3b0596befeaf8d07620f9335c122477eda59962649a91354d21069a1b657bdce1a05587550d0bcea2bda8b4c3a3460b0daa9b92793800606031cdb544a267292f1f2143a5b47f2039febd2f004b36a08c382f438c14f4891d475c768174785d0402c9380e2d4a25708aa57c0f859a040e6a56f8007daacada936749dda53ca450157d142d6934763b6d98a48375820cbd0ddabc250ac69923a8c9c32de27a2fc292db36a5fd46f0bcc3d5e81fbd1214b864a4e0625de6ea405f8c83e49060575cbbb98c71430691cd0317a6682a1dcab873bdb8c60dadfc3393b0702365b6f62ad2c68081870974b858d42c4a70d933682e154c02110253837f7ed03c6ecdb0f98ef169aa09cbb0fde0a6622013bbce2498ec9f57641406a16322c49a6e3488d42c6aa3380d44482353d1833ccc271ad9c5001c64cb186f4689fc49b648b63ba67c834811081088109827a7a3dcd6a963c3fdfa11f30c459ec1a368c0f022373af45e496a279d20e2708b2988ec00cd3feeaa7015376c553193e329a329beab58c5391797066842eb1cf7c66175c94840a22210bb04fe043d40bdb0cf98ac77428bd8ee27c1940ce19d0e95c7804bfdcc66dd845408cca814390920d50cc832aae55947429feda4863a740343275d70ff280a5b14fe2276eb0027763441275266fceac95c0a8d50204a992690fa476a817de7042f07a559465ce92370944fd4af0119f1f19c4facd912b97c04dc282f3163b66cb872cadcfb04b4d074a9268294e7f251911e8a52fec35dc6c214eeb28645b01a917965eb92ea72378c11570fc94b17d1c6cbf2f73a3a9343d96760cc3e6b1a51250512d84f6705a38366c852b7d745c3dc8d0f7724997d2c91b40a0103d6509501ad6d01527087dab45ddf12090e9db0b0e8848e9396601947a40bbf8422b2280a5babf82922655129eeb7fc370f432400e801a9e8f2bc172549b9423dee89cfa8610f4d230888a122a91b3ae9d0ac1994e761358ec4ff8e0dcde94c36ec9acf3328e580435c58c1bdd2e1021dc771a86ddabff4447c30f7e5b9c11f129f2a5b767302c4b50ede857929d40a6f2441ca29e37205b19293660411e6e6b6867e9221792fcafad3cc23a5e50c66f177e66e5f69d8dcf2cd006ba2b4b670366b9c70eb6372e3ad8e255fb017d18ce5a8bed9feaf199318daa919a41fcfbdd94350b29ecade1ec2d9b28158f5068bb513b692c0daf74a2c40d4f8ed8d913ec4e8e69723718ca38fb624851a9c516ab8bf26916f54efa9bb55b8dc77842985fa18f7a8c9b9ec1e92f1963a8e2bcc67cfa2fadc6b874d519981714c42383e87b7cb806c7cd8c1a94ac9a21d327822caabf8c3e281160e14b9e71e4cb82af363fda44241d806d7437a3235f6c6e5f8b4042ce9a7b34b7e4f344dbadaec390d9ffe5af9a9ca96709ac331c1a326ad5af87a12af109f4c73b278e99bcc4a823808fadbd8e0a67836f6fb02430327fd5ea3446e70812044aaae5546ffcf18fdf76067304016ef91f7dd871a1a08d2fed01cdae33b282149dc73ee21d34eeda0ade565985dc774ca80df218fb9c323dceaf4c2fb248e1973d06b115ba80b181465eedde309e8aae127b7820bfa082ac6fdf13de1bd44bf05e5a6a5f4a0772a781b375282f4612724f498795d7aa8dea15eedbe4b420f95f4018feee98aae3d3c7b07cde0fb0da1608511edf4ebc8aaf337ea5f9b4b1be6178814e3d471ec253b416bf5c71c155c1b391b26d4d8df35fea25bb7f300e926b1bdf1238e0a976faa3dc4234af696ac104e6980026220171a72994c05ed54bf1a38cf5d6befda0b9d0d750499877ec99ed607a52f89dcda1938a7da65eb506a2b31e5fe3adaa7c247ce11ac93c46fdaf464fc6a8e03db77594bf609fdd3266420da26402a00f1d89a42495c03c0592f1b5d1d61cdc76910287abf9e1f22d0db65adb5a922dab45972aa763df2b50907f39b94194021c9c0deb811bf99f91e48a0aeb9bc424dc22dc4ed1855d24931a936b04d8006554fa9971e957e1edddc8f90708e4684813756aaefa33575150db8761c2a2d9e76192f853eed6951afd4f11b9db4183fb7f402300926a3a6d6e8466496150805b3ccb7d6a9c0e908daa0bd9b0c0043382f8248e694a19461b8cbf9d59a773e193e8d8f32e470820ede8c5ae5b32e604d2bdd294e6d62f6f374740ee7e31abdc508255a46453f6358a90afe9a1221eef95d0c6c15d612837eea073748919fa67c271a04bd921306b93744cdfe2dfde40912e20a995ec1da3581d28085e6f4c7c3a9943741177812fca5ac282c3f624e481436d7950f5f86e709d6633c01c65f55f422ac533f179ba7bcbde3eeed755a7f9f9c97502d288f0591d57eadda9954434379a9138836e15bdae9f2df166b600521300209fa207501e38047763572d1deb63522ebf7eb98e2fb99050d0ff40a17428f77886481f2d32922e6c69023180ed1c173780155dceeaf14ce84f9001a2403ae8150c084ffa01557cbe9e3635834e08280001fd01bb042ece2114c7b8df6a9399f568a15de84ad603382a90c8cdf3bd1c292920116b1b8581428c4a8ed4b8b759b88f5c1a5e00904421ae8f7580323d03a760a70f5671198cebe093a12560e57e36e0315515cc3ad4db450fb2b90a13ea5c3eb8ec4f2f49d881f21ff7348b88460857483550813ece04aafeb2d04d930293c08ac2eb05753112a209832b6f7259bbc2eb7cc53097eee0a6b825ae3a1f8480f1d24055e8125a887a5202a77cc8dfc73181a6c82ae4024c007deb050284af037e2f9023eea874c0a2b20b548dd88d803f82ea83e8a10b7648208fc795ef066ec0b02405e306fe2aac418aa6f091885a7392a010776081d20879ea00440088426fe206102bc4a10e500fb9580115f4c0fe33ff044c1ce0462081adc04ae7004ccea14dd57e2c4bf016f8422f04fd241056fc292501583510e4f7a8f1d7d5fc409b9028e8823e426b67f76024f8bfc7f21980fd201384c0e7b824890087c012ef52bb8b6aa5cc4334b70da1a040d9306ef4169880c061dfe9aa0ee4d1f0e80c8e64fe5f157385eb7c82f72a57f2ac15452f463831c0204d7004ee0f412be070030768523613ec0375c7a545f85a3e20aab037304ebbd1b5b2d4c88508f0cb9e98fa4d016eba2cc82f840365889f1c2b9e00e42042e78834c52b14012d8075366a9c049b006cae6d21c5d5620c68b224b4d479423fdda86203db8146866f60049759ce9c0bc153c7d2e33e4a4fa688cbc4e286bc68eaf57b0d567896af7438c1b8aded1849a348e479c1689507c341cfe2511ca051a237b91279dec54c67dae6347ef1d679818cb39118060bc98d541826eb2a7e48aac198f09d3e2936fde877a9b15efe1da2e41748eb2b2684ae8579ced6395a899f05c3cc7ab048f103c6bc52747feb00d06e43a99a03f8bd47a3d7d3a5a9f610979429ff4715a9f5bd52cb8118a731301ba2ad4662a91dd485beecbbaeb9d44d29beb1c497837447a7d981032d6326ac9dd6cdc4450b31757f7b878b03ed2273464032bbf5e425dc5e6f8ca9f731f5bafee3ebf31dcc1ad4ae867a4b7eafbc39eb72f6638fdf9c3516dca752f42706d21076f75e561ed79e8f83cd9217a8bd36514b6e3f34bb35f9926ca2106c59092cdbdab7dd65e43be26c14984edb8501045df750f700902b872ae4f46949f15aab521d1d861e8d185912ed1ca720c84779d8c521fdee6c3de7c48bdf938907b6efb643d12141640e8c147370aa1171fada71f377e6b10e5e977ecd1dc9d3bb6d3a9cfa0744497de57ca5b043ab8da683634e1142cf5b6fed2e9c4066e277e129824181509532e1ed12ba48a51580d59db4107ca4f4cb2eea965f9ee38ffa3a8c6c039b0727feeb97c05d3618eab14b73ecba0f25053b7148483a880405912c0366af146fe2e642c2d8fac883d2570890c2213912b119a682188955778690e838820668898e87d89ea27314e8e1a770a508cea39f347c6abd5a2429685be5e0088d1605a8811d43ae663568011081f27f78dade4f31a4990292416ea18c83ee5276f58f243efc3dbafb33246ac316cbb3a6c0736e8098fed1ed4143d84cf192cf9e35d0c46417cc6713e998e176208777aa4814d6678209a1193e92f7b9683b5141f1b8462c2b1a3240880540ba94e4f845e0c601e3559371b370b1c382613171e3d6ff9e90b58c63e0bd98d21c8b493a9347c8446e9b588d849239f5b3116aa2054932ca153ef8bd07ef19e610c12ce8e4af538aee9dfa7fcfc5b527fc6fc199949a35f5ff818eb70b0bfda904eebbafc963ce0dc461f0392cd68e908ea1248861929c36b55e5cd7ddf08ae6975c132d15149deb13c95d5f2362b3bc6b3e4d89e132799bf7b5e588c6b45e64f2daf01d3e08bee2c72b54635ff122bfce32f2ee82b7d03e449a909f97c953b9eaf5ede9cf1cf7c811e010ff1f3a8b659c2ff47b8e14a1da72710fc6b9fee2ac918a88ed2760a254963f5f24881552f0ff4a93cef25ca312ee50db3b06d9e151e2887ab7f2adad5a4e0d24a2dcb9e7ed32ed8e52d361b0b3730151225deb29a049951171828a2880643684ceb96daf1d9553d1c286109c907b9d1b0bb9b9754a29f1495949cb41239b53018ecc168c7b1e2cf0db1b1b13e8fe6658a65d7dad03d52112bec9b0280ad7e624b9f6339e88599da5240d84bd0a74cdde824f74c8a8d8111a5f6a69b0699949016e88bc1ec2e5fa753ff3c44d7da185071c0e3e1ddb02cc410bb5952129fc390d844d9a631787a22bdd2f96d4858feae911e213a0241efa29070020d6f48813a74b671f6f16d17bb255a1d9ad0da7b539bedb28182d741d1e00eb03620c23d8a97f202e81941cd394c397c295eb4873caa16080ff160d3904e1a301a8bbbd6004c599bbc88b24ec33e24487b2493294196ba7c30a52d2590df5560a590d61d49dde8bd6d46daabcc9df6349cee79a0586b01cf6e124628dc570081d03dfa8ba543081d7fe8422208d56840c1504401caa2ecbf2552880e65158771ee3e632573526870a597c1c665865c11755cb61c08901f4b23383e42624152bd171e7ff41ed7fc7686dadf7f3e5581cc6343e939e409c9eec06b64229c9f566750ea0e84d5bc43273439c71b60f519585645ff2956d548c797388b4fce850d80eccb5872674a41d14b73b70d5304fffdf1d87e51906baf9c2099f09542bd9e7813af7f1d179f9ee78acf27437656814e9888ac7121f945245e0e01b32188364a2c4474649888b7f17424fda91410543c0830a0f7f8717b6f9d05ce493df29d96b2a0df4cd4fb078e8fe5144ee53796e23e67b61b4ad91c432e17347f30abd7b18ef2b82aff8d6e83d93ddffbddc5db210f4cad1a529bd1b58e9c2e942daac23fb0c9c543f4447d40ec939b2bcf6c6d81be864912a48a5adf7aab529a4bece41b419becc040bdecb6afbc6d9b49973ba9a74b17c76b018558027e2e84b10a3440b2a4d001ffffffffffffffbf4ebd655bdbb5cd3203da97526e69011492984b29c994524aea17e608218d106cfbc2f6b31bbc04330ad90a850a9aa913ce652d1955314d16214e7873295a75e89b0ba14df8a7a4249432419af004bb2073ac9d4c33ca84ffe225a2359a4a3984092fca499babc4d09c4a2ee187bbe79439c4949558c297912966372be17a77753e0be79f32947074ca65c9ac840af7241c214bfe4defc9362c09c7d2c42c25fb4878325909234b90f04449d1d6848b1b3d3fc20b3209224b562e96ec08d7cc34644c677f1d37c235b93dac8bd889f965845bc2781242447d70cb22bc777feda0a38a70dd3f663231f8bfa989f0433e965ad6f0d929229ccd5497b493877074884eb23ca6a8e519c253825ab414657ea715c2ab110d0b57c266aa1221bcd41de4c3c4ad579506e1e6d774316d1284f35e9224a609f5244c8170b4a6d0594b8070edef73498207994f7ff05b94d81827730cd50f7ed812ddcc9347114f1ffa94bb930c3a091a830f7e0a95e63d59cab424688c3df43147b7943d7a7036dd9470f62699d6e4c12f5f9393a8223a28f1e09760620e7626aedddfc13755bfb299243171dbc13599494e66413609aa835722f3952455be74990e9e24fcc6953057979d83b3496a7abf4d5e522b074f5d859edbfa33370edec9e1b249651bf47070fb64273bbde16f7c8327b8492699aab8c15f3fe1e4f9ab0d5ebe8de2b3f229a4c9064fba2e2999983b0829aec12b557fba04e52969a8064ff2183589258489280d9e798c6e4aa4978a1d1a3ca1447bd26b31a6ebcee055e5b4d983d9ac6d66f0a28938d9c4142a83dbdb236a7d36cc58880cdee953728c2234064f96fe9483f2123c460c5e4e954cd9785c304f189c4b972f9885b42f0f187ccd2948cd64d2c4a0e40b9e9cffb593d41c2ded057f8332150bd973ca2e78969596e73d973a9f0b9e4e299b50c1df827fc27470bfec6e522df8b32925995d67c151c265e7fc3316fc8cd9bc640cab70ba82df29b7d6bb67a58c159c8dd92adb08ad4a962a3832a81813b6ddb3880a5e4a8292846cc779760a7e0eeba4477aecb2540a7eca966e3165143c55e1777229a1e00715d7498ca67349e7139c515e962d78c868824e70ece3e234668bb162131c8fddbe9a66c3db638223469cdaaa35c9547796e046134d76910bd24d76d2f0f2e9ad12ad29c113a299c4aa609da19d24389fe173c5122b9a641d24f8a7c36bc57497e49c7404ffc772650fea2f83690c2338fa2a89366a4529256314c12d496888cc6818ae5d30b1a414240c57e6be748e3941a8c1f0c4543a3e338949c5030cdf4a2841a9365525fa85efb1eb4c83cacc24bef0356906ddafae79d30bb724f9cbc25829994c78e16b5f88c987b85777e1e8cf4e1f224d50daebc2ab4ed27a9998498572e15beacc953165499ec685a331b7874b2774e5dcc2f35c39297daf2dfcb48f31cdd6c24b19eaa364cbd27469e1db7b0e2a484bd2e667e128f9b88ea954d62eb2704da62409bfc4422d25366a4e85859f5cc62aa5f9156e6ae5d195dbec4eae706389316a92b173feb215be9885880b9e148315ae95da2093ec156307133156e1b6f76950ff9d5dba440c5560a5e45c2a5ccd15834c5d366264092a7c4dda7395a0738c6bc9291c25c518643e33dfde14ae9aec2393d8d9424da570acba52fa0b6e279848e1770e756a55e44b7a46e186becb1beb52949a88c293c436dd1674c52026a1f0733af97afc34df0714ce5a07ab14a3a689e6136e6629dbd0bc6d9af1841f54badc21ed4ef87177f2c65449cb0973c24f52948d6f36e17c5736b12f65b58c35e179a7a86e82b05333e1a8c77c90a1196407139e74de3978d6eca5da4bb89d338ad4a8a50425967035a5f9f63541e5be125e5be64ba984094a4a29671a25a8bb4cc299b9a492ee7c2927497876820ee2445a345b23e1fddf5dbb6d063541c23917df5a4b3aa6271fe1793ee5b33d2ab64347784a86ef0d67c1abd4084fb68c2f1965c43f6abed286b6084798694f7248119e1c93ba3f71b3a192084f6cd26ebfbe4184a72b47ad987308c7629aec6792aaa4254338769a19b2a4f9a510be7aba1eb751254d84f0d2834ca26c4a39e507e1f7a88e66c24810fe49e263a5cde3994281f094a48250da6349383980703d9558635f2a68fefcc1f512356b7312934c397ef073a59c3be59c66eca40f8e6be8ec2dd97262c2076f3685109e354927c9ecc1ebecd0fa908d59367a70448652795dd4dbcf831b16f3a551e3c1af784da9cc4464eee08789dbf69578f4de0e6eb6646e26a65f8c3a38a6d6265a2c0bb2ed17830e7ea5a98fd9ad366bfd62ccc1b34c2928c1945225c32f861caa7c29944ca21c0747e96ead36f10da18583a772eca4a4a437b8966bd6832ee1834e718363ca332bc6943638f25146c64d52ba2d6cf0748d9c201a624ad53538aa2e25fb3bd5e08aea92fb6419f39a064f6706a1bd349c8d8806bfd73cd7b87ccad2197c13623bb9e62ff5318323837bcae8af0c6e0a256325f92783df266decb7caf1e78dc1d3a3e77a63b68f1d138357952aaa2d56416c1f06b79208334bedc1e0da064b3176fbcc67ff05dfba36556352d288ebbde099f429a9bf183c675217bc9cda4a92432e78e3e127ca4b8991a52d78f9744e7d92ac900dd282db416a725192054fb4f0a144c9c1826b711a73ff27db3c57f024cbe53985cc0a9e2ca2a29bbc65094aaae0acaaa57747934b9750c119cfb3e9c65328a99982676d1acb45d7c592143c4912a1442dad29fa1f053ff797892e792878c1eedf92ecf594c34f705468cf615927f8397b4e1532de5ddb2678720639499bfc965398e0774ea2ac76094a46cb12dc4e5f7239a534a22a69f8196e54eebc2ac11f3567929804b153ab1849f094ec71e9528e4168550c2478399f8eb96b333d45c53882f329a59829ff099352318ce028b9356538955489528c22f8bed95e164d6a50e6303cd173d06292123a9d290c4f635013cddce3c50c86339a949c994db6c912186e275d2a7b3899dbe47ee16af24f51daf9c20b67d14ae5d43d5aeb85274c3061654e3da579e16b4a194ab4945b2ebbf0aac4dea8f5f619d5851b4e8e954e562d815cf8a7d2880ded69de4f2c015cf826a7923f73e8168ea65149c97da90f1f5bf8f29e3dfb492dfc7462284b62c96535a185b3a5f47a2c99851766729fbcb93d648c2c3c69724c5e426c508262e1561016e4bbe5f305165e78699d8f1093a2ff0a2f86ba9c63ada90eb9c2cfa08488d7dd0a47a70be9612bac70cbd4c5f6d32abcd7ac12930ea56aa3aa70947a7cf9e5646e662adc4a1b9d2425a8f0d4682a29fd770aeff3988d7b8550da99c24d42b6560595c259f70ea7f348f50b29dcba92c48c16e351f829f666a70a1334662c0a4f305162327571283c71153c27e1ccad4ca0f0b46c66bb3ce227973ed1673bcd9441e3093736945213a34e38b275aa624a4d7f8813fe8cc98775499f6268136e2865f2b78a34e19bbc59664b27a13e27139e785fcc667a4dde8309e78499fa311984303197f093d69ed395419ba8b184a7dc2d9a8b327993a6128e8b6c4c25bf554a794af81ec4af67e8684a924978490861d274cab1e19684ff96e96fe548f856b263569324412e0d0937f72541c767f7af1ff195981446cd8e7094ae249c5fbc126884574989a91a7412f36557028c70ebec83952acb4e979540f2c75451849fe9315c90616412a46604fac6201fc2f734aa4d8e495fd2e21ac2cd59548cb1f94ba64b0be128157d534b9884f02a36a3c5668ce12f3808ff3297a64fc2d5c74e108e0c4a4c499c52fe1ebb403827bdc807a9314038327f4a685bf6074fa7cd14bde9dafac40fde6813fcd4f57d70d5469c2449ca3e25990fce7b769d9694e46055cd00640f6eecb56462ac44b992a407c7635a134c45797073ca96941219840764ec641bcd4e74074ffd8ac574da9bb972ece09df8d59dac491d3c59aad478d02a6a64123af8612edbbb0425da5a660e7ea866f43ed1eccb7f39b826a594b22de5e3e0774e6b8f6ea1c9a20f072fc87f50c289312699fb0d8e8ab94b53c7da0d9ecce3a3949828ea326df0bcebfb533eefa0ff65839b6257578965ba73d7e0c9dded5183e31996efb22c69703df7860dd75e524c1534b8bd6a3f4a5bcc90969ec1519b4f0c972976cb976670c4c56eafd3b00c8eba583906cfe1e97b32782183c86c9faf82cc8ec1dfa86de25a46655c4e0c9e567509156a3b8c4a183c79fd4343068373f2f9cbc8d240bee05df4d818e35d97d602f1829b3d35abf29d1cdb49902e14acda6a6444d45be32d798f4aff188370c18b9916d4c4f419c4a57bc005902d70a5ce54bedf44455f00d102972984eeecb4ab0dcab1c3055700c982eb76628bfe12362de6981c6490f1c98c1c5501102c7852c78507798fdab071054f9d12e26ef36cd967a0b1031d3ac8b8b143c78d1efc0e2bfc9f438334bf2c8ea4074a666ae5032055f06a438651e2540e2364260042054f9237f668a68a6af5c1b17643478fb3a443c70e1578e048003205afc49483492728c97d0c714108508e1d2e501029788212bc4dfe2a49b8d3a60d902878d9b13d4851bf6216c2b166860e1d3dcc18410281829b041953986c213e53480390273897b4da289951ed31c5099e546e8134a18030a1a401b2844d9034ac24e98ca19992b8ce6c478f33ea00a20447b5dab3db647e024812dcac50eaec52121024b826a9d81cddba52c936470072044f69d5e0f2be22a6e4418ce078bce731d93363e98314c1cd1821deb4a4c3f04e0625c774b17d08c393821653626aed2cf1110cd773ce9d4cc78a10d91fe29d599515e203185ea813726616369d2c4ac2c72ffc933c49d945d7dbc5ce175ea9646e9793a4c2d332c2472fbc6c4174060f9daa9eed237cf0c22f51dd27766576e1dabce6243ba7e60a7a5db86292ccc154a658d6f823176e0cafbed9b7a9eac2b8f043fd05e57de92d9cf18b8f1a4aae1915e1c316cec88a3de6315b0b3f4d129364626d52f8a085dfd7f561b39b86cf69168eec51525292b6c79f46168e67534273ee8d353f167e67d216252aa77cb28685dfe2dd693e557ba9f02bfc998fa136a497ceb2ddade0c3159e9463b6f28f492d876d0b1fadf04d658931560a4ac57358e1fd28d9c46493abf03644e73575f2a71e57157e6bd87ccd784eb29b0abfb2cf656730e1b15250e1f927a154954cd3ca487c80a77044db4eb3c969aacf8438803f7c98c2db9cb1a2ad9af29847a5f0749dbd6cf9068d4187148e38ffec5e2a69666b1715f1310a3ffdb6758a5a27c9413902c4831c3d5e070805366c84b820042cb061c3860d162010e0e8912306643cfa400ee34314de56e774375525b557189161100b0e171fa1f0ab74c718ecc48e2dbd0d1ba0f0a492cbb0ec58be1efd847fd144f66635514ace74b0830325e0f122b833f340d50e8483c7abc0042188021adc10c0293e3ce1bd8675fa203b7c9a0ff6e1a3139e687ffac4ee5c7a5ce4c30727bcb4f0d14ffee826fc3731d9dd434d7852f986a8992a138e380bad3f9382096f6d7d4cb6eb745362838f4bb8fe553d9fb297927c04870f4b782a284d9b212c95acc14ab8692634a72ccb991df441093f6a34138eb54978d1ea447f366582cc8d6306e261a6840f49b8196a4e9d2445114a1254c7c3860d497c44c20f2f76bac23e37d00ebec75d0b121930e20312defa26f570317c4e613dc23bcda13e9979cd28318ef04f7d93b2bb4cd2e824eaf164dcd0d1e3060a3e1ae1678d26f5c5497d6a1d843c58d023c7413b3cc0821d243ac8e8800e17f0c02d7c30c2f1d0edaa2527a93cb4086fb68275584c49640c29c295d53e113b4f223ce1a430517b3ce33f46849f498caaa828d17b3bdba183073b9019668c20c4033b74e0e8810607cad0b1030466e400012c3e0ee18f4a4a7d3e7b4ee55a031f867046a792c3a9e814155121fcec1ead644ac933460f211c1b390b7eb21e84f79d2e092b23355a6705e19c64d278cefaad0fe34720dc3ceb318c7af558f50108d77492948a4952f5f39a3ff8d92f17e353f40925f583e39943589b0c1be5c33e7cf0c16d4b1dbd4e4a0debcac71edc92c4a87ce8c119e99abbb2736c868f3cb831aaf6670ee517333c860f3cf8b18493655af3f9a6e70e9e9429cf78a960191ff4860f3b38a3e92cf5ff7a096e0ac3471d3cf5d755dd314247190a1f747083922bd7683a392e5b7b898f39b8be5a659eb2a652bb85c4871cbca0954b69860e3e2756b6838f38f86141a7cbb1c2c834baa3a38cd279f880835f32879d503993428a3ef87843c1b2b3e6d25cb2697cb8c14f9b2fdf694f0de1a30ddeba9f98b7f213222638d6d8e0ca5892bf2fad6f92f226848f3538b2e131bb7d85ca68aac1cf50afb1de744a1afce0a5347cf6cef02dd1e02741893d3255ea2d6fcfe06a12d7577217aea46a06e7547ab7ab3229da7567f828831b8370ffcc61c1c10719fc7f91df4a49306144e7051f6370c62429a8bf9283e764f91083179aae9498b29669e1c247189cef2487f4d19a938efd07183c4f1e4b264bf132968371f8f8c287175cefd4ec6e0da6550eb8e1a30b9cb418272e38ea2dbcf2956492e67a0b9e6675bf242bac054fc99be24688daf759b2e0a59424f94bca549b073fb0e0092227fb7cd07f5cc14b3a093b7df7224de93eace0657977aabb4f4a663faae0a6e497bee76bf39d0e203ea8e0ba2813ae930a16de3f1e3ea6e067253179104a8dd0e1a40c1f52f05693eea0927b7638930090e1230a7e124a3aababd31a3ea0e08f8e4950a93d5449fb9fe089afd9f01ee44795886af8708297b46bcae4a2d7347c34c1199334df876d18e526267851a4e6924cda2cc1cfa153ce5a56417fe7d3f0ed735f76a694d2824509aec9b2aa129fe4f7124c82bfd173bc9c1494ecd943829b4a26d1d446578f123b829f5cfb4cce18c14d51353d6ab24b9fae3b317c14215957cfd1e4a0d23a7aec618c61f8d79acd544ef2765d1843189eab9b4962c8f8a44c29186eee93fc5db47d6f1a18aef6b88997d459a7a15f3822fc2e0977196ed3892fbc4afab6c6af62ded3bdf0ed64cd9e6154e62ef1c29199b34d8c7225279b77e169a99049ca319f42b3baf0ca8216916977d25632178e8cf6ff397887ab18c4859fc65c4b63da6ee1a75cc1bf3ea69613ab0cc402335210e201b38573339ad32afcdcdc7486316ae1473fe12da9fbf9ab092ddc14a1bcbf676dd326b1c218b3f0ccebb2879eef9853c3853164e1c9336fb6b10493c4fe6251a6b459f3c8060b37c9d5264db43a1edce84116c678853ff225c9418f27b1de76852383b074aa495a775863b4c2ef94426953a28415be6576d3a92fe7fbba57e1fcc69c3d46735b8caa2abcf2ba20c59494100f84e4302347421f98c318a970fc4b8cee2c17156e0af1bf13eab5bf621b366ed81ac63885d73998f06ec289a6968c610a3f8427a541c7602a87394629dc382997545e496390c26f194d16b49cf8f56514feff95e5bae06b92b8e007a8046388a28bdd3116c6b3b44eee33eed48f565b7f804a30863142e19f7f78c969adc600859b3a93a6c8de123ee9136ede3a9d2d494f954f9e702b5dd2ee2525a9d65fde20743079143208d7bc664eb6f12c2934c4830707b4478820bc241a52b3d2a970312924108e5853255b9b88af753a0801842bdb569a95e1b72af607573ce5ac4566d60f4108e903229b103e14930efe623a5b65101142f670217af046ed4bfcfde6380fae9f70632aa36892ee5e081e2ee40eb89adc659a8ecf983c7532b93c1c6b1962072f7410cd1a3dc70621757045093f29f58df84c2f840e6ee93225beaa846c97819039b841cc6387d614775b9283e7fdb1c47b13073fe8a4ae9d55a632cb70703c89362a991c0a7983db62f549b67f0a9d5337785292848e17150bcf671b3cf541491354de32f98f0df9fc474d529235683fade152f944c6104ac757480d85d7c9b9e4f24d2a2b869034b8d95b2667acaac8592168d8b292ba95a5d7ca6a6227badc8832adb5d96629849cc155add2544f9331055d88193ca1a5ccd365cbb9b7331e2165f072f41cc38bd081410819dccaaa26dea43c064fbb6913a36752ed139e1122064759a6de58f299a76f43c2e05da6ec260f42c0e088e8f0532ac542bee09cb0d52aaa15ab6ca4215e303c68eda0426751e2d81f8474217bcd639704c71a32810ffe048c06085810e286102e1cd755d6a97121227ee2ee2c14b285668327cd9a162d781617935f095a5469cddeb01d2159f0d4e4d8fc29b7b66f88633f040baec69769cfe104f353052157707390354a1eb1b9b7dc8e1e676020c40a6623072155f0c3eebc52da890a7e7609cb67494db40c9a827397fc93a9d50c6a355270e304f1d5274933db7714fcbff093c28d160afeb8e78b7556266b2a4f70940cba35492ad8a993e2042f877b8ef38e1f2b65131c33416de62b77fa2699e067a6cd256ce7aafa7109de565c2e95849786331f63d75cc792699b125c932f971cfbdfc4d61592043fa975d159b935cff748f0ca2e5cf4ab20af261572044f1294e8963f4c428ce0274b3f49f44aaac269438ae08928f5281aedf2a5576318ae299d820eedb030fce021a47889f5bed9e31ac170e4337f12d4f698ce39a506309c73f7d3fc15c524d4f88527c8e0294a8ad0da3d1e146af8221d835a12c497985783357ae16fe512343d798aa95583179e58963309176376e1c5562531a627d97febd5a0862e8e13934c1a935afcb9f042a3896ad255ceee142edc4ea33953a74a11678d5b38dff26a5faaac610bd4d252d53cc6523ebe374929949c6d3ded1ab570c3c8eed3b1e7844c0d4ea1062d3ca9d15a4349f206dd658d5938a624e92d9af4b11ab2704eec6cb484881aa51ab170649bb8943c5f98a49260e15772ed93b47798bba4573863154b9556932bbc24956ce752526c38532bfc7cd299c99a84f6a4c40ad734a891ebd09bdeb40ae7e2db7365af0d995785b31ee33dc4b69ab053e18ce7f6b97ca5aa2e0815cea893abfb4cc807119dc22ff93ef7669329fc1c7397f624052b53520a7f54ca1a27a6a706293c19a3ab9f761ed3f2d41885a7740ee5e15ab49fa786281c193c574a3108efced4088517a3fd491b99eddaad018af2e7203376ed27fc8d226318b5fa32a527fcb693a4d0e17527bcf2fc229360c209bf242d6ae9275594ba093705eb92c474a5de649af083d8f632a994269d3299703526d9efa231891613a9f956369d82bec6257c19a1b93c98df75f66b58c2f5eb1c224bf4876abf46253c95440bca934f094f95e7d3d698fb363a09c7be93e8fca731d32809379d4cf25dcc45a20e9dda3d4dea21e169cca8a8f03cc2111393a798fc7365ba1de19ca7cf5482fd67efba117ee73221c6ce92682746389f744aed5db2b7a88bf093d860490a4a9570a222dc54229b7e6249878c13e16dc93ab13144bb578c082fcfc77d921683aae8107e8e133c542d9ab8134378995e72d0496558490be1e852d26494bb95a0238457ab3177855857ea06e16679eb7bd904e16d50cb59b172095f6d205c53965aeb3f80c0e7939c248bf9836f7762cc24d39431fbc13b25b37cdeb4f421e57f62e1839772694fa3376992abb2072f43ac9f9f1c353c470f8e7b2ee93f4919c476340f8e1aede459d4a92e59c5835f6249a89693b47cab77702d78fea4c4143bf81b5e9b654cf28c8bd6c1ab7abff9381b659bd2c1eb542fca34837a14cdc153cf92e54b64cc3629074fd2317cd05faf99c4320e6e8cb4ac3f1d38b8296d3b3da98ff6e70d7e494274f5c9bbc1db204aac147c94acba0d8e18d3a933792a2b61367892ce29f8895057425d83b7224e9368a11a1c55e7a2d4ba3478f2894dea3e06254e0cd1e06fbfe958159ec113b495aaea246706cffdac6c947c19bc4ad34eaa6458fafec8e069b32498649e9450d21b83ffa56e179e93925c3b31f8395beaa06310174d1406573b780795b764d3ad81c115352526a95436576b5f70546b589149b5fb52cc0bfe59b68fcd5092d6a02e6c822e496df411173c314e35a75611a2926cc193d3adc499242995365af0c48ef92cf839b7fac9b294b1bfaa81052f5d4cc9546318c75a8e336c7301b6a8710547c64411954ffb75f975b3a861054f94a7f74b22c4b1d606031e3d921939cef81bbb8053d4a882bf1ed38926a54d810119093a860a80440d2a787974cb6c545b11969f829b71b24d366d1fbd53ac420d29382a664a26e524cd53d328f84135cba938eb8a4d0205df359c4a9935f304578430355127f59cdd56a8e104bf929c6d9427ab09bee934234657123e1e66420d2678c9724aef27d59ce85766a1c612bcb70f9d4f32318da5a620a1461a8fb8f67c2895378538d636063594e0cd6b8d099e3ac69c1a4970649204316e33e2d81a4870d475081d63eb1a47f054f455b86d92e43219c1134e565dd0a31a45f036b5d34a5e0f2b771a86a366ccb744cdd5410609c3cfdeb85ce2130cbf5492564b101f6078176335a7b9f42ffc5297d926dfa619d5f185efb5694e79280f0d26bdf03b668b6d397607f131bcf037e8fb8e4136bb7073a89cff4be7a03b47175e9c45f1de2498a532177edeb0a135658d0b3fc368ebe499bd85a745aaa5c5a5a5ad8b2d1cd5996d32f7c90aa3ac85a39efb2dafe834b19316aea651525a979d7432b370d409cb7565f2f5f6b2d867f409c2843b167e34613d97ca1883c51016bafc976726e92b9cd336a16e35bc24a132de8c0402882bbca03fa724e6f094a40ba202482bfc543a989884509926a78c0920ac784dd9cdc917c82a3cb198045f93257231d33a5880a8a2aa9439f9d779123ba9382bcf5dcca620372abc284a926278ebade0f7297c4ff2765e9a63b978a67074f0527a46e9ecac1a8eb552b8499019cb394eac7c6a258090c2919a934af2d9aea6d31f808c42cf529220bba445e19c50cb12735072d2aa79c0033f304307395af08394630782c1091460089050b8266809af29568fc98f63cd860db4338080c20d774a7fa853326f7caa00f289f5c47622198413209b607e2ec890a24fb461a36f478f3304800a104dfc692b9306158eb53274ec48366c9041c67b200b29403251b6e94c3741e32a83897ed6e6f35fb9c95fc274a59498ee4c92f2692d913693e6e4645d9f5209ad2fb99a0e3a96ec9c2fcbf13b5c0008104a9c4f48ef52f94b290f90495c96b3267d722294dc0a40247158caca97d96d8938b1a38538a951048984df9e1a640a52030209e7ab2ea9ac8658007984339629be42a6d15ab205c411ce89164e9ae9d4f0556a845f16d3090b1931c23769fdf55bf6156db308ef3af3a56c49d252a65684f76325e61c3e9d08c7f2a64a7acd92c8204684d77f3526671f1428b3809505f0eac698c01866a3141b1681a4070f34761420003f301638880100e8c1030d1610200048078f860ecc78321020001e3b12700200000000000040031c903c181080360080070f63830002f81e7f6c142000dfe38f8e1d360e0080014020013bd00e92efc1e3c60d070020000f3800e6e8b103070f3200d103f1f883a7b35cda54a1254941ea074f76b0f8d9e83d26667df08210dda333a5b78cf0c18bbb3ba9e424d9270fe3582b65a03274640f88073aca2803957180113d608e1e3cc871f2f08333f080397a1c1d65a071e3460246eee0ff5c29250899c3b17c86c6591968c70d1ee438374e8e049dc1a3ad871939908719397aec4064e438a30c1dc90a46ec80376e2060a40e6794919481ca30c0081d30470f3270f0f81b37123032077d4e58ae0e79e5e06ccc966eb3d6913878b29da04987abc820038d54e62370703b5b8926e7134db4a037781e266cae5c991137f82656bac7a49949187ba40dfea6888ea5a249c86e47d8802cb7bcc14bd2c81a8e5a23ae327047d4e02555f297bab524530c1a3c6c03236948d014ad53e3743682066fbb2f79d0a0ef64f8f5032367f0525ceebe4ed9cb548f636d2f306206ef55c3e23a2ea6fe348fc2c3032365f0b235437848cb4c2ae9841132944e7b3e694aaabf6a640c411811038e84c16ce81801038e7c0147bcc06c46ba80235c68d3e53b2cb5ddd59855a6ec4a82ac93c4890ec71adac1bbe08c912d782ff7266282aa05cf7a849674266ebd732359f0c4746ad0a7a247ac8605ff4657261393365c5d2357f05cfc820a3a4647acd0d5d869bda554d1b2adb24997be3ed748155cb1fe7a732f51c151d154614912b5644a6d8491297c9d3d44c9999d4fa4c09dd858c77c5a9c9dba944e6a240aae9647cd3146050a8e52a7f306dbeebf1c3ec193cb1c3dbd5a57ca718233eaacb53bc42fd5db044705cbd94c32d99b3f650242e5ec9f434339b2046fee248deea6bbc1481abe5d7d7609db297c14e130a204cf746534fd75d66b9f91243826c5e612ba0499c7d711243826dc9755b83872042fbf9aba4a52beec9f1b3182e7d6eaabb9c5926807478ae049721af5be1e2c839a458681c2c860f8ee276d4d8a689b8ac0f8857f99b67bbdf201324375b0039d006f07ea91c3178da5abaf9b8bfbac54963d6374b5a42f498af4c2ef7c16d761ae1e6de585ff9bc3ffc489fea9c3bb703c98ca77a5492c10d185f7a2f557a6745c524a72e1c6972c4f92d4ff1b1a2e1cb1a426e749a56eb1b6f03d861293d128b23a5b0b63ab57543b51a48523ccc6848e258df8ed66e19aece1572dd876c84c16d596b859ccd59d875ac86aac0f3317b3665c9f51468222b1705318a193dda5d461926c1081c589bcc273f1e0171e6acb6458c415d78a1361c5ad025521920abf3a8926e9d2a2c2f9326131575092880f9de2444c514ae1891ec5b49494e5d16344486136467122a2f0dca4f9594b9d434dec1222a170eeda63d558470414b956675d5da98b55dcc58ecd944294f040e4137e6837ad14cae4094f8e9afed7a429d209ef73526ac4278f08277c0daa4426b56a726e130144426413ce68b4f429d4ff29318926bcffa825894958d29e2a2299f0d53d26e5625a4c38dba33ecbbd0625d60ed423c7a52172093757ac7faf882de1af7528d9fb531d2295f04e1263ccb499bfb3654a38ea7fb3f5077712cea8c7a80da619b37f24e1c628316dd53466cdfd8e924422e1e5cf1af449efab588220a198669273346123f2087fd4eabc334511174d8ef0c263ee7c625552926b9146784273bf7f0a97968497117e1ea1c4093626a9fa908d165984a222fc79d14a7e29633a2713e19c1232b52631846dd2df21c24f231e9fe7447ab6cbdd219cada4ea4dbf7a6708ef4b8ed99238b710ce79ce575a25c3a8247f4708dfdc04bb3341733708dfe45ce1bf32a396ecde8908c279f3981593e8196d0a84a773c858c272a628a0c18d158800c26cc840e40f5f5d7a0aa33378227ef02a690e1dd3db81481fbcb0399f7c9d63dbd927c2074fe6a0739a5ce9e63e06f7e0c6e8a54b32ff51440f66e344f2e089a6e65cdaca3f2e8c638d470e3d3c38a3477a75bf8f2a4f9ec81d3cfdd7bd72ae881dfc55cd49a78b8cef31c113a983e396ec73e5e0be5d693c113a58b6766a39e334e6bd535082ce492c3207b75249f27627084f66de89c8c1df10dbe43e423488c4c1334925d1d2213e9f5b441138b8c13c2c7f524a30cbf937b832dae3b73fe90f1d22e38c3272f06017718327a894fa4dc8b5e38c2381481bf0d0b2682f372e1b1fe7c640840d9e49ba4663ae18f3d1640d9e8e661f43079d1abc12176c5465280dcea5b510ea27a6f2b9d1e0da98baa4677426a5b367f04db0f69c3d9beca44d3378a54ac7dc5c922acc2852063f9d8a91ae41e54e840cde6fd0d817f6a9fe9268888ca194e7e647dca6c5802261f0c3fa4f7f776a44c0e0da6b124df0e0f9821f84ff28ed1e2c27f18a78c1fffc314931e3f29c94d205376d28595c94bc696352840b6dbcc875857d67d85968225bf03e0923c63e5c98f24a440bce69c624e98f75b97354240bde5c677efb64b162c2388860c1cfa4d29625133c2ce65cc11b1d3de4ae7e2b78a9566387f6dc4949b10a5eda7a964a57279e3a112af89ac4565b26494e5a4e44a6e09fdc08db7cf29d8814fc1c63a6a376e9f858120547f9d69694363d1128b87e974b8e0b13b4fa2e66883cc1f1e097c5fab36b947837ec7870224e7054d2ca36ab8ccbcc399126785abe6f35fcf4faaa66e428c18930c1fbdc1fd7596d3c9125787139c7aaf0c15d3da682481a8e925d42f75889abcfa8031125f8e997bb44395bf90f169124f8a7cadd2e954604096ed7e6afd4ec27854a8a1cc133612a626db3c9304a96206204b7840a139476f12ba51529825faadd3d43936ca3510c1986274bde60dad714228c0c86d90086d9c01b84fcc26cec20c41766c38c905ef871c1b39a9a92bb537a8d7208e18517976427e194a0c2ac786408d9856361a207cd18ef563a3084e8c297f99c73c838132ade40482ecc062edcaecb8a3b215ea283ce8d63c697a16347e2b35bf855258ab79b106fd374b185b75d97aaefcdc4905a78fd665f9ece3708ad68e1e7ce96549b204d45ed67e1e5faa8d96252c951ab0e84c82224165e79b6a9d431f66c92c2c24b258931fea285f50dee2b9cedad5ca592f8b7fb425ce176e53bb53f93ec3b15d20a4e561373acb63c261c8bacf0c3924a259fc5ecb0cc2afcbf58e5eadaabc28f9e93f2183e93688553e1052d4265f42851e1a9bf7d095b5f1f4df7146a65134484923453f89e325c9f7621a5f04c5d5b7c168d145c2a8fafe066c20a198527a63abb8d4ee6561d46278488c2b55c41869728cd3a49a17054ac650e93724c1a43a0f063844713f2999459cc2736694dd46d5838c4139e86133227f1944e38d2653429b73984137e30952b99124c4999cb904d3817ec52eb5dd49d52856802f1b220a79666ecd3e6347a674ad7ee92d33a24138ede4a25d3c3c7e5394330e169cb49dcbc09254e8ca6117209bf4dfe31c1336b644b2010422ae155fee8aa6a62fae8f80619680725fc607723b5efa44d2dedc8914c032193f0bf62c9e6717d1e2b8f634d0737ccc881a3478e1e4842334b692cbbc96a5b5ab9a79989cf7539e57a98a16347ba71d046c2114ff2ffa84eeadec8811c0881846bd1a3bae6ec0b61bad39047f855ff224598609bfae4f164dce891838c33788438c26b8d4fdebea2be01010b42421ae146abadca781393a91fc2084f3b9a3ce22e54abe82cc291725f9b42e7ef4214e16f2879ab11a5e562851792086e4e74cf10b2c7b176e3073b7a98f18620c2ebfe98d76e2d0625ad9043783ac6128d696f88213ca56e93d41c5eb17336a4106e8bdc86932f2e688410c2038b41a824ae6582d25820188a43027128100a2cdc1e00031408001834268d0482e17014a6d2f6011480044d2212362e2420261416141a1818180c0642a16028140683826150180c0a0442a1f0759c237b013bd977dee17e4116ba8898ceee02846ec6cfb5912d1c5793b6e8b2e934af00b9d57830d9b6bc2eee70fa4906a4170f9321dc06fc002dee56d2c3c83b83c481b5fdc4ddb1bd978205f1357a0a6e3695266b7b4ae15595c79955ab534bd1f800b81046f8b726249377978efed9e71478c071abfedb9de803c542d383f7f74cee3c3de4d65506971860aff08484f3254eee1fa4c751ca2b4ba48dd8d02c0cade7905209e9546419c6193fa3a944bb6bc0a47e3871ed4f12229561893b939ce6eb22d141093556b3fe772625f249934a0e6cdc8300b84d96f86323305118c1c0b7f4a13145523dd1b9340ad03e9e593c0d8a3c9fc7c7900f9dbe79d689374de94fcb529678d5c7235b16c40920dbc075b5142448f630004e8806fc62b0e4a90764a13c9d4fdc60356c5ff0444957eea424332eb56faaf8518cba5f4e4eb06f13a0c88ac47ebcf7df3d2713d73721b92883205cc62545c4303ee26b5e89f74a5a664f1e98e390a9e92ddd04438ef4abb557aceeab9968fad88dc0484dcdec6ba6a0c0f71cf8db5424bfaff1c078211f88b812f5a5446cf68a2f01b6245a2141bb01b4b1fe1a15582527620c2d366cb3683bbc3bb4cd4a4b5c8a737eab65b9c26fc13183737aab25f7c04f4863b5c499262ae488aff9d7f2d21acdb27e23869fc5fa627068f293e81b5b1c022a163efe7d558c65fb727fadc93ece3024312239907f0790224cc51d3ccee47a463793cde24c9e111e1df5e4b25bc4cf0b73ebe08101da595e5d5454f8178c8fd92f6be94a19247484f39f3835c03009d7e6c98b97c64aeb3d8472d0b75d632f4b6ae6901943e89dba02879e7f732f33710412ffb045ff8342ba51c5492570e259fe60cb8b7a0ea3750eb8a89fb09e574a8a9b7ce4c762530e42c5ce9e1a25a774d4e681a9d413c4de1e8941f49706380a372c342849008e39ec05a517cac6ea61daa47b45c0e2e326b0eefeab565e263bb5f6b776e21eea2fc793eef39a7dd3d80ad4b3d5eb8573a852d0e129088de7c08809c9c04304a72394961a99474f08e525a211c748aa3608d3c0d965217a2536c25dac75d50e0d016fb150971b050246442579cedffd17dded06467ebabe5a4d8306fd21a82bc9f50a6f5a44c4dc7341d3f0cad7917ea9ad1f101feca68b96c27b4314118aa5c1c0bcbeb0aa24563a44d368af25d106bb06c91f917c477be39016d95e1be895fbc8c8e4bc636bd0bc58abad37c4d27384523b4f5b43c7c59277987567caf9c34d28bbe60f60772b23954ae29ca4ca57c8bf9ee29a9a752d1191bbcb297bbcf3becd7ffa320efa6b8b4ba1ecf196ec087bbc11b1d3d2c6ad78af8e69b5b356cac400f15aca9c35a06661e47aaed0e72c90141301602f45fe1144dea918863b057e1518dc0e15b2a06922c522f6af434d081662a09786eab66909a0c04449ccb3d08460a693de38a725fdc7c82ea3c6aa07fcd397fbc3c32301dd0d74897f462e97e8ae808e3ba888b6f1c6a3a1c8db5a6f4ec4de343bc08c24585a7da30597d70717adb705767e6abb507890d6aee044138275183941b6eab1373a9bc6323b3d428c95e6121318bf63e9c1c49c7b432cd623012f4ad77a867cb5cc4b20a5da4cde299dc93194dbaa0101e5c239e0f3265ac74c8395d641710be3fdc4dbae8e84ee64e34861aa52a0eb4e3cd6a6098c8b9ad61b41a15d799564ec809ad5d337fc522cf78a7e4feab2c6a102c1d11a029a60341a274984a0118b4e7278d3f2becc0e6faf3284e13a2c24e9522a19ac521532eec5f5c9aa2006555079764b874b50f1bfe5401ad542ada8d82d026bd3010054995b09b1f2d727a78b59e1351f23ae9e50d694f103c50bb1fd8bb59375a854e601d4b35b6720399c29b7cc4253d5cdd2aae9878376724cad20b7d886410e81665ac80bb22c82ef182358a4b066c4086bf30253fcba87526e0453d908b85a3302b12706bafce02a963f09701268c195bb70dd9afc2dbe0b4d17ff0ed1c1d6ee35413d26bd45b3ae3446e85ed8a7d55b16596433231491e6fbc8b83daf55599359c8cd76dbe5cdec4a0c20990c16df8112b9a813f310e21d525567e82a134693ac5ce24f3dda1e15cd59d3c0e0632d5b82df283cad60af80786f2a3b65ffe04b6a0818ef667524f444ca5b0b6b8916248708483a38c09ecbc322eda60d6326bdad9372e2b7d6aa2b9e0ebfdbb2b5ca90e340d201693ce3bb9a0507c7daa96821a655afeab3a0a00513acb424b3828bc20983a40fa2c84d96664c2f9fb8f94de1a2810f0e35a56b5b724a80e8df9b5c38b0f27c7bd6f0f5972e6f7b3a4179ca444422b6e25b8941bdddc951ccf51fa1d242d494e375204f77d50c65cc44d4e7a00c5dadc0dc428e561d7733e2f8737ecb1ab864aff07e55ca80c7c44cebfb1548b440bcc6004cdbb8308d9b17d41305c5a569f1ebab1d6dc96e2778a7b1e0562f88deeac8688fc66953f759526850853a2995d96b825739bd374bc3a36a1901462ebb9f4e5c417a3d2dbf410f8ecc8eb5aa2966aa2f09ea153b1a0675b5ec822813bcb3ce5a4abfe0c5262bc3eb3c481afa3efad45699651ba2614ec77fd7bef6dd0d0944f8599e9df295b5f9e27c2f10113c8b71213f78bb8e37e02ab1138580a4088f2a208f63de936a65f9ffdccc4c466acff12ee716a10690113ecfe226770effa95677ad257e9b6e6bb4e69cc961133a6d5f06b368998468b4b5e8f446801e8944210589c020a5b25899a078abe3d364f970a1bd868b416fe2d02ef91d58ba3b509265c39e2e23b68c539c34ba8aa5423ecb1424f68307036f9bd8bee3e6262400cec4f40c918013702a76fac06294ec0e0107dd41fdba4c36a87a2d18cdf196b914604bb694e94e486a65d673c34b75b9176da71047b3426c1bd9979ea9029a25360aff8e89208ab2e29ac5dd74e4d2f499eb5d0ca45e2c7400352877e6bc57cbf89a9a314716bdbd23543afff4f45faaa5d5f2c27595248d05d3feb8b10b6462a192886321074b49b67e58e0fa29972e4b225a95627f0bacf65014058c9d4a8e21711f975e2887da63da874df70ea8595262d96d8630908e2432f60d8c621d6af8cdbb28a91e282a422886d3e4a7085af0ede094131a85f59168a26828ea08d8d4be7e39d9ef84d1545d8810da19a9a26e06384432ed0c6c7c322f39a33e5fb49342c5ee679283d99bf41ec9de908d665d29f87c9a93a0f7a8fb90f10d19c9c44ed978f07b50f49154ca0c2a85ba6ce2f31cbe26f2454db300cbc99a18b3831c0c3e2506f62b3bad0afbba0de61591b38e1fd9dab642ab632c222a130ecb33b543ae9405a8f5e570da7338b9dff40a29321afff8ff74966115216104e80eae833cc9bb31173d13ac1108f9cf125af7929362685f39974017a90a3708bc19035c2bc657b779e06f33895035c2e211f3b77f15a88a0be7752105ef2415ec95ae429fb3ccd6c448fceea02f833cf6ed5084550a026bc2acfdb907fe20335fd2e23d1b771171a4e4ae723d4c10be06d8c45c0759a0f2f05f72669aa668d83f56fc380737629dfd47c76782230e9a130810a45397e34506bfafc59f7d1c9e153ad615aada186eb50198cc2f9ced546674c34f853228181db2f1f804c61db4ce547cabc9367b1e1fce6c940df630c2fbf7958deea405dfa1b1448b93c419b4c315043c183462f651034c68488b6300d46aee8a57902bccd09f124144712f1a3ea11714b70b9b0a8321b21349c00427d4f9be67279c05c6446a6016c05db2129012c6a319d1b87c82b597d2777a25d4ebaf546e2d883a3663b367e89a8ae3fe5435b4615a466438e7d126aeb268494d480093e7e05fd7bb71da26c7c51556dceed88cb8cbe150480bfb107351bf6b3e3e71b5222b2108c7b41c936032097d73ee4aee705cea528290e617b3c3af14d6fe647a264eac3c089bc8846d70199d455cb20dc57247861236a324f0323659e36e48587df280d4297c80ea9a04a26be252034c26b7a5de16e25aa9a2124b6b738c69822fd8953ebf84869810f9c4b2dcfd1e492c51d5ffc4928d51c273114013f840f43b3057f6bb6a7323f42a455e89299992a54676d980de6051d57b2b0f8ff00f05981488107e40aa43209ac5ae69a39571572098dfb1ff8a86b3372dc762350d75a1500a7023b29841deb2411b9db3a2a7c3c84d52f92bb2a5023b42b7e9b76e3f85fd2b18493ad4b8d8b1a8f78d09cd170c67b8b6ec88abe92ef0393883673d8e98b3fc3569fad858606bb930667d69dc99f07ae93fbb005403046f4c825713b9cbf047709d5167b8caa4a2703e22a8057202326c40955a5b9a6afded762814cd7a541a6dd7ac46a95ea0d2bc04d2dd68e3b774579c8d764745512b699b84e747f9c0a283675e38b4f52f100b6872729b2822ed3cca21d807dd8a2d6052a6ac10f3402a04f8014b57caf353214a7ff9c5c9444bb60b59acba61176d167a40e4eed5776b5064b85841bf0a4bec4c40765d457f284a770ba36b55f37e518937f3e23c048b333859000734dba9776698356a7ad5d65aa2747d04fd505ccdf0b26501389aa59b77adfc9b5cf41c22828df7c1dfb61d26f61a7721f973f10a5bfcabaa775f391bf82cfd1c2ec21aaa3f98d0a58e1e887f6f2378697c8387c9393d821e2b2d06cd7987a6830c88a052219f98cc0851cc27a02c827aa777045629d1e19babc7e1c9ec6ecbb401ccf41b7783560e7b5be467160dff8a2a2d2a9e55118a8e5c92d357e896d5df3e1991f6ab01e68292fca17680dd090cd544ac3e1777b56bb7422a534538897441fb5e3e65134e60c19dae28a98a12291a2dd0e91e0336c567f036bd321b677dc35677756c834f68504966646df6349871e4fdadefff4e1f059952cb80abde39c5482950c1652fcf0ad78f306d48740a78108f659c045274877e984375585ce4c95fe1c7a0a4141b9e4348ba88f162045f96ad7c8acf339bca55ad03dd517d569b14cb042d7b8ba7482acf6ac51f478d46b341df9ab0053ab78a74817956ef8a741feec35ae07c470776d57a4ab1e836f399450a53d549d1e65b298560793fea1443183501d75582d45c90a3181569c857194c42b2495a6e1fd611b0feba2e768b6d0ab1495ec8f39c4ca42904209e44a07d40dfee04ad448db4006c5b94faee13553d185df5221bf82e3fbd89013e9038ba7b2c960390a76d935e0fd46a87530f574e4ed01eb24f6b804adf3d541901ae74af3514a3de6422a0a848b8045655f6d33156a0aa7f0bff95eab9cf17ad56201290a5862fb86c6092a1a3cb4598d934575bcd7a0c11d82f7b9f3c18808ddd07a8bd29aae596cc74eafef37b198d087b6ed00113f50c006094d87661f6b6ff4194f9a365c21460dcfd1955471d0b64486274eee10964d6fadf11d104ff16db068ee5983e7110a042f23b2853814b7ef0b83b52cb6f997c9cd4374cf7474e5441d0c8f750a55b1b21107582a6ad0a2762309bebc882151882d367d85d17265e3038d9eeaba7776c4079028e3367b27ed7ebfd62bc35e62e040259ed89fdc3c087f963d6e29b3c57d80ee2a3e2722ac938fe8c6c77cdcd6bd116e17d728e87083963c5731ae91de7d128de4ec9b751d1466c02a867ac4e9c914ad04d0d01be9a1103ab9c00261c052356e81eb97f42301064502c50b48bf4ac32cc2861d18f13e09b00889a0e44f9258a072e950bd380b88782840a8b5234d5ccd4e74dfce72948ef0501fe347001154880cb7abb9ecffeac24a90c4da9f87eba5ddbbf3e7de77ad1e98f74fc9978fa4da072004a86d5d03440901e392dc235a6208ea12bfc559d1915a9cfa07c1a2e4d4815046a67144b9e6bf20fac496d9a44d6e981546d3614a956d686790d6d1c53e1dd4d12df2ee2638f6dc65b983b39f731136e949d43b0662fc9106e2b9e28e1c92461102aaab8a68c4e8dddfb83599803537636fb88830f8afe7ac070e72f6091e6ceff24958ed68d14baebe77d772b4e783f49212c67f36cfc8a139336b3b4c7476b8e70cfeca9ee8918c639d2052163a9229ce971711bf3356a9a840b689a9cfbeddf399298d16fd286594d0ea38fdfe8623382450aeb05fdc87604be970b9263527cd870e4f949c1be6513162095d4549b561e32b2a023d745f04530a6de46849d3cace3f69b724ca2100e317f8029fcebf088484a2804911317762d52f50365f0d5e61183133081ca0833e5ba8ed4602bb843bd494c30887e7823aa10730a6b877bf4b1b34bb9bf29daff39765befaa78443d45406a44775c87cedbd653c835b55852df95ea33d2f1317b730424680cb5b82bb4856fd92fb04535ef835d81a2237a28b6b1969e00129ae1bf76278ba58dce33e4a06ea7428d422154b09475764716ad5c402bcc75c049a437ad5d907b5c99dc0d74fa428acbadb1125cfa78368f115ca76bd7c278da14dac10c55baa4bd94ea7b610335c17d5cf2ed9559021ea9f13adb900ca43a5b9863fd330a8d2f74b1e0dbcd1c9b1c22ef9cdd585c8e6b5562a95d5927c009c7d25945189284f06ae58ef2ebc1728f5e718833b24621b65a26b9d3e0f415cd78ade3a558c40641539d592f9c7fb81de672fab05ae47311e68c70487ef352f654e43be87ca9a508440b5d29c64b299236b6b874c53b6db50792f5b150a65ec599e4365bed0143ae4def1d7ab94aad2392e4b3197c3b88b714cfc16fef25d356337dbfc68cceaf48e0d09b8c4286c88594aee5e26ed40f33dd9cba7142d60c422538bc279bd6ed595293a339742bc31fc02cf25a3e7cfe24b6d3ff5d8c061a81487508b8f8df7bae153ba355395a3b675d992eedfce65a48de3b931d655f4bb0c2d47d2565be9c83db159fcf9b595f5dc25d32129d35bc11b8e02735e8ee8f83281b9bb2b924d98203cec4ad496904a0e7722b0476b3d2380f99b7e9874934468fd023443842cf053fc1952218a4c7d187a29ff1828c3087cabb4db22ee549cffac7b9e715490e5b3f80a5acb32ee4439a8d4e027294134c1aeed4270f764b4295079e645ec1cca1845230a5091f360dceb35376de3788e2e95401342effbec69eb685cca9bbaf0694fdb13061c007edd14b9c8b9b88704a1b9afa41b7eca47e5ad7535437439e64890f4b32289abba684584800440c420271dd92111f89cb2dad53df522229e18b422455fcb6535f1de17d77b65b1bc5c2faea194595a9a702103cf0c2503dd967a47f20d557e98813fc5dfe8736dccf65bb032408a30cbe42958457b8039541c7f8ac0aa9648620ba29c8214b30552f3f70b695105b4f0031f0d8ace482118813f259213d212010dc99f7bf2cc0fe01cc9a244cdb7eb281032a045b74b1e2624a3396345a9422389a5ad30597e2a3bda8dc92eec34aa43d6b4d5b6482f7c6f0a39199eb5085dbd0878f6cb7048b682a5de6a72477b7d246cb1e65ed04d9c32c1616cf01b5b0b4901c5c896901aa5822f16e0d8e93c9168375e62ca529a1732ef82391a09d75c547180004fa200564b4a9e094dd976eace41e6f574a39c70530c8231c898b41ea8f721655529b162793a4f6e300262bceb9c43324bc80d814c8cad726ee023ebf282517f02f32c8289edc49511ae1858b7ef84abb214ba4c3e3ba79cc482228267324c9a052b2d503406737b58aa8724b4b90761c883b89591b31a83af023b580f75466cfc349e929a820e24229c599c829ded27302d5040a365e15985fbe35eca6a22204843b7ae9971eb3b431a715f4b8f13cf610dc5443258692277c082d5528917cd7c2225edc2958a442eb0ac94f803e53ecdec10595411ffd0939e066b66d34b2893efd03924eca4de8e1df2b586f7488588e7cde4f748033a2694fea20ed04d292a8b4e6b0edd3aabe03951a22104a0b0c2566b6b895ce710895983dd98949f890881f96dba48f1ed934e63ac7ea2296183913c9d911499df76cf218d731bdb530e16ed5fedc8fc7f3a1a75c47a39aac3a3bdcc5d14a2e2679fa63142f3da2aa2338817870ce50adfea935b9a455a7b5d94ca34165e2a72f58a3d42c62e3b46483ea64e2b9071aafb555f8800bc24cfc5e4b986a3b787e57287e4e59959c2fdd1811a8eb0413b60fe1d912d557125ce67a978eaa1ae2434923947f61310989f1f8bc94aae4448bbbf7272e3f256e7fd8fc65431bd70731c5a8a0368aa57332b2f879c7f227b2919f97492f999d2710464a2db5244cce58fb6ac499410178073052b7ba596a71afaaf0d1909a2c21150f7c274be256bab3ade5c3cb05eda2cd9f5072d0c0ee7948c9d17dfe8ad36b34d42435e081e03981fdc1fc4ce29d705e09725212cf2ad30b764dd982fd34d433cb622076bcbe52a5c133ff443867a3a2fe82e5bbb6b3a68777091b3376e8749228602abb599a230df5e0989c377981dbb1614292354322b0fe185178add5a62b84134ad0f009fab6d435b868128c5d0402e5e8e27dddf6e720e9b422005d962dd36951f29eb987b1314e0244cf6ecb48abfd6e690378768215adfa5414e10c99787c04daf0fb683f823009594148391b63f4c67f38d269f14dabd278bec22a024ca3964b6ef5d81b51eb029d8ffcf5f954f47e25eab680aac43e2d10f5db67706bcb6b056a95cf629d17306c4a866beae02d90d01214308ca8fd28e4d44f6e379893846a214f0fde2822896550da03fa1269073a0cfbba498c3f2983cebaab9e20d4b23d505078d306fc458eed3bacea0cf7024e49f0e892a37526076c0b3a1e348d8e05a3eb047c4b572a7d2e423382f09d7331f60ddfaf9292c564b9aaccc469ce3afa3d392382437e40f143a23803607498fcf4ee0d2ce630b19d4ebf15a7a594f45048f5274816cbeba0aba043429c155a9bc254f08bac7547a8c4cfa013b369301df73972649c68c2328bc4e72e77a326aef8e6a69ce74f8855a49f47571309220e73b81d5c44987671851745987ff08fc0ea4bbdb81047aa6a2da8cd07315b49f3699d35669546495f3b2cbdf79a7b28003cbc206121ffe29e92a8e6bb992602b586aaaafdae64fdd8c183b4759a58d4a4672c6268e2d72e4964c2ddc1432fe12f4895bba04e36806487e61edfb8a97d4a10edb253622fbf973ffe86917a181dac9d73a863e711cb98482b50f5996f758e4e308e9abc990d31bbe47eeef37814b2f22a062b0f0c1c05aeae7b93f271db21af9dae3877a1a7e1d45711d825fb8b913e7ecb3177b816ba701fdc8235f978fadd1424269ecb45b0f8bcb7eddcf6cc42ce9f61c687f86986ddc343271968cd317a39f4e6361cc10afbec1ae21cd50fd750133c28a9830389c5da090ce8dc9a00bc85c80080914ee0a1713186aeb3e2280a56c2d32d198919a546d972c2ff1d74fe60e04f852d10066bb63c1381f71784907de66fe37c40411c350e24fb2551c5a2e1dafc0fd81e360b50fcc74b1134e1f5f2a938daa8edb0a0bbd142bbdf9a1f56bba8162385333754d645c0df20fde083ac91ea5583126b8bb39811863b34a32ef2a8f14298a91beecba28d28c377f35b1164544685deac936f413ea2ff9af459102b6fe4dc82c4c39d78a9f45782e8983cf6ac7d0ba7590ad84bc744b05d60132a25a4908a1097d992505f0740a4ba25ee446759e59bbb5dc5aa4bd4d962dc39c3d6070969ea98954eade9ce6347babfdb2cc4ef5d6b865a21571bc95ec7a124ff6637adf5d972b2c387434cc8a30ed1d06266a63b00adb108c49e4f15769057246978bbec8ba3b0ffedb31ec4b6b10eea7997ead82044869e9daf4a49ee622c69e727d42b4f50a8d45d7585145a6555aef25f041b5ba902009495d5f1fd9ab73aace59cecb0f8aff17cec0f0ea0a661718db36a6ca9318dc6a36ea364d7c7694a211c2517fb77a56662c6d3509d12685892be99dbf4213d89051b16800ba2d77d9ca35ef2fd69083ec50fd67509e495b36c47fc290acb486b402ffe38ccbee1304bf41c4651aceff14ebd27945d217b08d122843221370aad45e813a11284c80c8103839d066a2502e7ddcfba03661df67fedb6ec95052bd6110658d838d00d1feef8c5e35fe90b29f64b095e921f90b735ca773009802b09a98385cf1f36d7a982f278ef2e2ac5632f66f8724425e35da70c8eaea5702ceaf8c5bea8f2d6a387d54b41e951bd3c195ab704ce8b32061dc74fe038f16f7b52c7e77534a50aa6e96b2a18a0f3bc7e5f58c9c1453633923b30949d9fb76426464399c16339431a34f6ba1a2e051be4b8a9c9a510a4500eb780a0fd53b832b852f212c5a4202ed285d58793c140854a701826a0946d360b8c4027ad1ea5f098d18a8158cf3238c95eb3fdd9fdaf268a833c99e4aa874df4637b71930715a5d907fc60550b2500f9d9130e4573ad0d74c03cb728c1127652ccc04f620388e5b4faef09620a8191264ae54ce118d7b55d8411f512952215c1834cc85be92078aa6af80ddeca1365cc4935ea8169707b0923c2fc3d7994b374059724336b68fceae3077d31177461b80f03f5bc859dc016033b8b8acd8fc9c8366b9d809cf5c027fcf55df4d55c480e0b0f4708c56ff81d61665c419c67e6f68e6cc009298a7c01163a75e2651f1bfc738f49a38d9d0cd2143166116234e6b95fc64d2763ec27dd9dcff4a7cf6ca7c69444b5eacaa8184d5b831cb51d66ceb4eb5487ff48c63b214773bc73910dc21778a655423b902a4e6a06a3cbbaebbf9cd5c64c56498bf0e7e4bb6d037c2a23d3aac0158d1e6b935e5e6d093964d2326229cbed1423d0c3895aa397185c0af1084ef20aafcb8a14156456873e95e9cd473bea75d7ead48bdc22737206de55c21cb810d56867a3c5613e6665997565b895b2b145011569e3bf54e83e467badd9495e8c76e2684a9a3550b80215ea17cd84af86bd9606c865aafc7ea78444c4fca11edb9317eebed2775f320d4425d494e59775c517e262563f53d7da8b4c4e72d79da700b763d64ccfd2c4e6596950465b0de69c794b2fbe49ec06cbfa96cbec4bb18c4485f0adf6e1a54262dac6e87591ebca7c53e058a16251fc2fdc20b3213783893599b4be1a8951a622f1555e6c9cfaeddcd5fddcf41ddedbdddb5a25768386a5e6117d93620a91285e8e98d4584d9e44e1feaaee09f022fa67b7063a717fd840d6047d71babbbf99317776825a63a562ebc0e0a222d2e19aa06b1ffc8f49541f872b5a42eaa3acbac9d7232e55aab8864d4bdacc0624b80b668a14d2a3035846ee2a8d4879b3c15a21351e935cfd739db6d9a43b5fc49be476c54b0833fba957e0c56ed93032edba7f91494989f6bc1da10889e8099268c7e9288ff2f03b336635a2f6bb6519d887d14f4530975fda8f5f5960008bcd9179115cfb5750ebf9e95ac76f786496178c87f01367e836950c9f2a53df2984c13d0cff610faf6345cebf43da94547984ef988cc0a7ccbbf1bcce22969525218a53cd08bf5cec0b74effa801752e303f5ee68cbff6a2bb1c4b98592fdf0af5e9d2012f84a96dde7505fe3c55dfe655959c6f5b0a050bba29e43e3442a76dd22640b2a0d88aa4b3873039b029fc1e1148411d2137878bf8604", - "0x3a65787472696e7369635f696e646578": "0x00000000", - "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x1002d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d6318141ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f9657ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66", - "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", - "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x1002d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d6318141ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f9657ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66", - "0x5e8a19e3cd1b7c148b33880c479c02814e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x682a59d51ab9e48a8c8cc418ff9708d24e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0200", - "0x7c215a8bceb5e4dcd92f78e36a5fd0ff4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x000064a7b3b6e00d0000000000000000", - "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb363fc5191ef4dec4f7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66": "0x7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3c21badc8f9053b1f1ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965": "0x1ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3ca2c608870c60a0ccaa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814": "0xcaa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d823137badc90c9202d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534": "0x02d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534", - "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950039f7057bdcc4bc16175726180caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814": "0xcaa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19503b5fa0e23c4a4cae617572618002d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534": "0x02d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19508d1b0ce8b7d45af961757261807ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66": "0x7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950e6240f6cb493659d61757261801ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965": "0x1ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965", - "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x1002d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d6318141ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f9657ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66", - "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x1002d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce53402d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d6318141ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f9651ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f9657ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b667ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66", - "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x03000000", - "0xe8d49389c2e23e152fdd6364daadd2cc4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000" - }, - "childrenDefault": {} - } - } -} \ No newline at end of file diff --git a/cumulus/parachains/chain-specs/bridge-hub-kusama.json b/cumulus/parachains/chain-specs/bridge-hub-kusama.json index 0ef81806cc5c08621f3c0a308425c72f836c3ef4..90b70b05016d52aeecaea4870f438bf980f7263b 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-kusama.json +++ b/cumulus/parachains/chain-specs/bridge-hub-kusama.json @@ -25,7 +25,9 @@ "/dns/bridgehub-kusama-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWQMWofXj8v3RroDNnrhv1iURqm8vnaG98AdGnCn2YoDcW", "/dns/kbr13.rotko.net/tcp/33553/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66", "/dns/kbr13.rotko.net/tcp/34553/ws/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66", - "/dns/kbr13.rotko.net/tcp/35553/wss/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66" + "/dns/kbr13.rotko.net/tcp/35553/wss/p2p/12D3KooWAmBp54mUEYtvsk2kxNEsDbAvdUMcaghxKXgUQxmPEQ66", + "/dns/bridge-hub-kusama.bootnodes.polkadotters.com/tcp/30520/p2p/12D3KooWH3pucezRRS5esoYyzZsUkKWcPSByQxEvmM819QL1HPLV", + "/dns/bridge-hub-kusama.bootnodes.polkadotters.com/tcp/30522/wss/p2p/12D3KooWH3pucezRRS5esoYyzZsUkKWcPSByQxEvmM819QL1HPLV" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/bridge-hub-polkadot.json b/cumulus/parachains/chain-specs/bridge-hub-polkadot.json index 130bdf31ef211ba6d353fa97944b2bf80320997d..a9444b89e1e2cce9c79a367c4c561e910f1f28f7 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-polkadot.json +++ b/cumulus/parachains/chain-specs/bridge-hub-polkadot.json @@ -21,7 +21,9 @@ "/dns/bridgehub-polkadot-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWPNZm78tWUmKbta3SXdkqTPsquRc8ekEbJjZsGGi7YiRi", "/dns/pbr13.rotko.net/tcp/33543/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw", "/dns/pbr13.rotko.net/tcp/34543/ws/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw", - "/dns/pbr13.rotko.net/tcp/35543/wss/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw" + "/dns/pbr13.rotko.net/tcp/35543/wss/p2p/12D3KooWMxZY7tDc2Rh454VaJJ7RexKAXVS6xSBEvTnXSGCnuGDw", + "/dns/bridge-hub-polkadot.bootnodes.polkadotters.com/tcp/30517/p2p/12D3KooWLUNE3LHPDa1WrrZaYT7ArK66CLM1bPv7kKz74UcLnQRB", + "/dns/bridge-hub-polkadot.bootnodes.polkadotters.com/tcp/30519/wss/p2p/12D3KooWLUNE3LHPDa1WrrZaYT7ArK66CLM1bPv7kKz74UcLnQRB" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/bridge-hub-westend.json b/cumulus/parachains/chain-specs/bridge-hub-westend.json index 018ab0ee6fd9810595c841237dd253b9463aed79..447207a58107a95bcd5fca173d9d5476a0ce9cab 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-westend.json +++ b/cumulus/parachains/chain-specs/bridge-hub-westend.json @@ -19,7 +19,9 @@ "/dns/bridgehub-westend-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWBsBArCMxmQyo3feCEqMWuwyhb2LTRK8hmCCJxgrNeMke", "/dns/wbr13.rotko.net/tcp/33563/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD", "/dns/wbr13.rotko.net/tcp/34563/ws/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD", - "/dns/wbr13.rotko.net/tcp/35563/wss/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD" + "/dns/wbr13.rotko.net/tcp/35563/wss/p2p/12D3KooWJyeRHpxZZbfBCNEgeUFzmRC5AMSAs2tJhjJS1k5hULkD", + "/dns/bridge-hub-westend.bootnodes.polkadotters.com/tcp/30523/p2p/12D3KooWPkwgJofp4GeeRwNgXqkp2aFwdLkCWv3qodpBJLwK43Jj", + "/dns/bridge-hub-westend.bootnodes.polkadotters.com/tcp/30525/wss/p2p/12D3KooWPkwgJofp4GeeRwNgXqkp2aFwdLkCWv3qodpBJLwK43Jj" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/bridge-hub-wococo.json b/cumulus/parachains/chain-specs/bridge-hub-wococo.json deleted file mode 100644 index 7024789b8ccacd22eb2f6171d911c3ea62876551..0000000000000000000000000000000000000000 --- a/cumulus/parachains/chain-specs/bridge-hub-wococo.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "name": "Wococo BridgeHub", - "id": "bridge-hub-wococo", - "chainType": "Live", - "bootNodes": [ - "/dns/wococo-bridge-hub-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWCNomXYZWuhwHsWhZpmrFmswEG8W89UY9NjEGExM38yCr", - "/dns/wococo-bridge-hub-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKSq37RLqP3Ws3FtJDYB1xsjoBeJmehVYDZcCDRNLBXas", - "/dns/wococo-bridge-hub-collator-node-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWDkSQzQYC7VwpJKF8VJtJZMG8bcvWXm1UEJSKk8UE2iv5", - "/dns/wococo-bridge-hub-collator-node-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWQoUFxyPbpotTdUpfnsxQfQ4uyxz1beW5Z39LGM8JPhLi" - ], - "telemetryEndpoints": null, - "protocolId": null, - "properties": { - "ss58Format": 42, - "tokenDecimals": 12, - "tokenSymbol": "WOOK" - }, - "relay_chain": "wococo", - "para_id": 1014, - "codeSubstitutes": {}, - "genesis": { - "raw": { - "top": { - "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xf6030000", - "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", - "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", - "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x0a000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000000", - "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9050f9ffb4503e7865bae8a399c89a5da52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649": "0x0000000000000000010000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b4dbfc3b7761206de75b3a8d70fc3d44a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b908aa810c364ce8c3bd964ff3d424cc926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f52c4b3c3fd1c798e3843e21a38f1421b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ff9bdc7d7afef8c14d5b253d4e25b33db0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x5191446272696467652d6875622d726f636f636f", - "0x2b46c0ae62c8114b3eda55630f11ff3a0f4cf0917788d791142ff6c1f216e7b3": "0x0000", - "0x2b46c0ae62c8114b3eda55630f11ff3a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x365c9cdbf82b9bda69e4bbdf1b38a7834e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x38653611363acac183fe5c86aa85f77b0f4cf0917788d791142ff6c1f216e7b3": "0x0000", - "0x38653611363acac183fe5c86aa85f77b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3a63": "0x", - "0x3a636f6465": "0x52bc537646db8e0528b52ffd00584402045e8e84760e521040ed361d686da611213512500714bb39cad5cf5641bd06bd0abd9638f539f67d2082d63fc6761fdaff8a1fa0ce30dde74873a8d1951f13478143baf1c7fc501ed2ffc5ee26424bdade7b6f29a59432c914d40f5b0d260deeca2b0c7fdbbaa337c730fdf8653b3fc62f4b4cffc7b7f86506e0f85b77f5661a70fdf8e55ec2f1e797db5d343c81010803387e6e77651670fc3c57c0f17fe2fc1ad70f4a8c13270e1c3c3f7e39052c7f835cfce4cfaf7177482a8ef48352f1bc19e278a1c03871e2c4c13986e3cbafdd05af7417fc176e116214ca295bf7a04ee0f5920d5fb075582fd9e004eee7dc070ee96f39ac978ce0e0fe1ddcdf3cfdb3b5013cf8fd9d6d5b3bac2186dfbf9dda612d1de6dfbfc14d72edb0c6d9e26ed40a6eb47157e3283fbe7fec88f86d885ac10d37ee6a0c3f7e2d2fec885877c5ef6e85c6b023e2ffe3d08cadb1fb43f7fbe331081db0c1871faa04de92d11a9c25f6b7f0adede8d3de933d0c3f7b5842d84284d161fc1f8731c618e1c708dddf62bf3a47fce53b3402df6ff68f0e219aad79f263ffad4ff0b636c1eb8fdf1d917ed8a940839191910db86fbb2b5becbf39d70e83efb07697ff8fc74fbacb1f8827bfe5b0c69ec3da5dfdfe2b87c5f76f390c62ffee8844f9cd10029612b318bec5f0ea1c812f1fbebc4410c7db1d117833c4f07ffcc79bc7afd0e3162116213c636bdc3cd9c3fe9b7558ced931e3c1cf1cf6cff1db5d3f3cf8f1fbbb43025f285eebae2c71ff8f6ff08b7806ba06cfc09936c8c14f62ae518802dd602815cf90ea1a8528d0084351009e81c3f0fd4d8d42146884a12800cf40317cff53a310051a61280ac0f0fdbb33228d8c30fc1fdf9ceb46017e8edfeeeab70ecb10c7efdfe267ddd55f002f7e7f7745a4bbfae3b5eeeafff1cd39f839e61a85287009cf40317cffae5188028d30140560f8feb043e21961f89cc31afb7767041a61f83ffee33ffee3fdb5bbfa9bc7af90875b8498439f636bdfe0b7c3723fbcedaefefe1884acad978cb8e0ae5bb3c2f0e0f7b7b6a3043c020773683b5576c4ec74d9c9610769e76847079d3874ded07942878d1c38726a908384ce520e169d2d397ae4c0d19922e74d0e0d726c90a32527881c1ce878d1b99223454e1c395ce4bc91932647889c31395ee41491a32687889c2372aac8a122078a9c2c3957e4a420874b4e1b3a5fe4dc20c78d9c2472b6e848a143458e113943e428a1d325870d1d2e74d4c84943670b1d2f7484d049810e179d2c72b4d0494207899c3572b2c809a303448e1a3968e434a1e3440e133a6374c4e860d1c9a2a3854e173a6de8ac9173860f2e7c64e123091f5a7c6cf131840f217c28f948f2b143eb8ed619ad33ad315a575a43b4886815d112c283382d1e5a46b4bab47a6821b572681db5acb4aab47c6829b56e68d9d09ad2326a2dd1e2d2628255048b4b6b8716102d245640b0ceb0cc6095c1228365863506ab042c3158655869b0d2b0ba608d6171c1da82a5054b0c8b0d6b05ac395872b0d6b0e260a9800507eb0d961bac14b0da60a96165c10ac3c28205867505cb0a161a161aac33582660b1c15a83a5068b0e9617d6142c28584eb0a2601dc1328285042b0b0b0b4b0b2b08d696d51eab1bace658ddb18a63058355192b33565aac985829b1c261556565a4aa4345878a8d6a052d2aaa34546da8d4a8d2a8e050a140a502951915192a32aa12a8965460a8c25029a97e50f1a0caa2baa20242d5838a08d5169515d50daa2a2b285438a46e908a418a8e1497541da932a92e526c52665263a44c90222355460a4e8a885411a92da9a55517a91e523ba4aaa47040d581a203c506b502d41ca814a0c64085818a831203f5065506c5060a0dea0c94095060a0be407981ea227584fa821a83e202b5054a0b5416282c50605057a0ac40516153d091e9baa0289042684dd0353a2f5039ac86a03fa47e48f950bf785ca4122b703c3820a2916dc1ed50814d0cd561ee21e730d9918dc9bcc8bae0c4c8b8f08ee40a3c2b340be5c1c8c94a648185c246e1ad31b36c65e60f560913189c0b4e649cde7049a73cb830ab32528e0e88037020fb20c2c7122a36542950c9a19a830271aa82334ad9b07a0206931ea637a624930ea61898dc98f030bd60278b1c2f2617703a7467a85ac0b540fb82b2e1a9c1e261cbc1760396145d182b30a6195d178a0487c56a4c0a063e7ee0ca9c84e07ce07ae0624071a049b49a50b5e1baa850d03d4c30e8c298eed870903385244382915b482da419e9854423d19067c832e419690269861c23976416b24c848104436221c39063c82ea418928be826de116310ed9062e417328c24234b10c988654433dc0df4065c95d416292d52625259a4c2a4b04881495d91b2225545ea4b8a8ad41429295251a4a04879493d917222d525d5448a89d412d40a45a247ad24e80e9b1b5b0ab63128961614ad28343ab4309a1cda15da1c9a149a155a15da190d0b6d4c7cc1c441eb08af8aea0eee87d40d3eb8acc2b06db82c03180165c6c68122c3aa20270ad50c58535253ac98ac053488eecad60595c36e5911416db09a816a881a05eb07153a2db638740eaac6b687d462dbe8beb06f745cd036ba2d681a140e548dc604aa8a75a31bb319d1899143d013d034e81b54087a443481c988728966c4335447aa1ce810f14c95629b82cb8252d9a8f0a66c52c835f4064e0b0f87f9c644639e99269867a4ee9868660db41fa21afbc589052735d68b531da736bc1a5829362eb30a8e8e4dcd1cc2844337c4e90a560e53ccd4428554a9a044d429e8163a03539b2e0b1947a605a724dde89672bad00943c708f946ca60e2a02ae35169c1a065470bcd0a07120ae9c55485e9cb76c336451a21a790544829e4171985b44256616ac2d4c5c484c909d312262fa627e411b4074e0c35826aa145705e5857621b561756132a213a21ac18272d34e944c7ca8b131b3487139b79c5a6c4f6e544c4690d5b666323a6a0dbc285c1ba617b43a5474b89235d0f2b1b7870565e72b0d88e905954656c557838886c7060e45c3155a15b54303965d831b50a8ecd14827b01ad62b2721a22e78ca6a55e31d558346c173a6298a6542c5462e488d15193842a715bd02bdc52ce163309eac3fc62a9c8c9e364850e13d58a1a4662c901c274838e1b394b262adc175b18394f14a1535ee8404e9b9a05e7c55687ce11dc981c27748858d58005848e12261b221aca854e191d2d1c9906705ca0d4acdef84022031aa0c04a8c150d567016b0d24388498aca8b84120880428312b141dc904b22f3182445c907923c5112044392241b8c2d34094a9244434d210129241005910c489c50e08582e2052552a404c5d04489035a682283930f3431128393282a9480f27281273e20c30a412fb4c024ca901e9413177854976e81484ad007923079824340a48293254e963891c0a39a5892c4890b4d8c0c0d395900d192a0a121270b18008a899632c4440951500b4982825c10006a8976e24213264942102548062849983cc1444d8262088112161ea544072d69e2a449122549983cc1414b9a3881005113199c2c59823f4025d1829325412d848042a2a58420482889142921083ad2c4091329528226f0a1b83408983cc1518284a238b900920f75442709922148860850a085a0175e50428101a08c6829424b82605852444b216a1214250886264c1eb5a5091317745044f41226529a30894244435092100c3d79a2244994a0168698c83044688993a1213ba8213a86244a8894b4a08428e801289410226032a484091193264c5828199604d110944205d1519c2c8043656929414c889844710203910c4a54282cddc40913222551646821099193212949983cc14aa2c880599c96165ae264e80349828894b4c06448c90ca68641431f5042d444061792102d098ac2a485264c9224f1018235aa6eccc06107eed8d64b464618c6a20f4ab7d02df009ac52e775a6ce9c3a73ce391f3620cae8ee31d2e88336a554c618a38fd8b4db2775eab46937a5317a8cde9446a7d4bda98cddd1146974d92e3dca0e423a21b410c23651e9d1caa6749e28b5d646774925758fdd1dad8d32524a29ede8dd4edb3bc6e9557ad562a4d57aa4399a4c596beaa47b4bf76a8a31336516b6a44d9d76b791e8923645511aa36d1929a5d4463982a49c779c515277b7564ab7ed544ada945211a2b74f2b9bdaa61d2195725249e18c905219a38c56f300d476f796b77b6c2aadec4c566a295b6b8dd1bad328b5f6485b4ed931ca6ea7b63dba5c817e965a08671115887c1f03e689523a29e559c090384f725a4abb1829ed6052dad46b94526a324a199b6e51f3683b464a1d77f4e15ab7f49316393a42bb7bd4dbfb27c7a9d3e850e5dd0e21ed1c053c37bb63c7d63414ca5a77872dbbe794d2d2ee283308a3f57992d1ba161b00b43d84107a7ab6cd7d76d65acd4ad9d15ae724c72500019ca7c0c52929a5ddddb6b5c8797bc71829a5d23d5a6a6d8c1da537d70ea5f40821f5d853ce9eddb337dad1bd63bb5c11721db5d8b1a91480bb006208d14485a01065cbd8b1a39c514a2935adbbdd5a6b6db4527ad7c9ae93d235cddd5a2badb4314a6b350f8075b74ebdbb4ed3bc45698c07a0b2a9bb76d2da6ab6a7b5d6cae8ae494d4a193d7a8c515208a1bb078992cae63877cedbbafb50d8cdba1bd2da5edbeb3640ccbadb3bc208238c1dd0e89436ede8edde1ddbdba97b778cd129a5d4673bed760a29841f50e800701f1edd23ddf16edfe1a2945abb6b10da181b15a375eb313a75db326aa713baa7d3e8de2d216ceb6dad49762d29adcb38dd6d942977f729a395537ae081071ea452ed31524a6137f47677299da7dd9b8bd1bddda3bbb70928947487314619a5bbc728829452caa64ddbbd3bbaecee3c46975d53f70622658cd15d528feed5e5a4d16943e83b4aec301155ed2188e22486a01868092d0445096a218a932430030009621285680401a87c749023b444c90786869c8cf0ca010104107cac2ad11225529a288942948488490c313871b1aea7a36a12f4e4891210ba84189810359121caf55c2092e18320189210356112e54992255298c4b07302ec031a9c80800804980180acba8420404a4802244848c9d00792bcd084090c5294c0f0260c31117afd1040011a089113272e12ba04254151584142498868086a4200d60b02b85d821422294c68081a81000d8406262e044579f25205bda0248a132229495a186202c39224444332287121a8859e4a14450991140100116100444aa20435097a218a14006c0da48520295282a23c89c1c9922451826670b224284a1225525a581294a404219801803c591224c52783610991920f2481410a93264c5c707d1002179844099a21c9014a30800180b41094c409d193104459224236143443d050121a94c410c2ce0b4e8692bcd084c90b2f3819f2016300a18382a2b890248a1322254b9c0c0d29d951110549f9199a0445af4b48d2c409132951826648124549d0d092243404b5d0844992264c94c0b0c405179c2421a221e8ced024288ed081e13158543ba0188cc158762a92b1c8813a5654818a8a6214a808c6602c56648162b0a8a828a635501715c58a62d1818a60aca8a8481645a0182c8ab92c2a8ac536a02e2aea585151512c36818a605151516c027551cc81baa8289601754c032a82310854048b8a1c28068b2050171559a02e2a8240311883b10ad44545b198042aea18058ac18e4da06ea0188cc52450c762b108d4b19803c5600c02750c021d9171c0ee86314851d201dd3e26d376a9e9cb161a451fb8147be49f3e07c9afecfa56f6b2cf16c325f941b89b5fdacd2e9ce15277f36f173a5cc2d98574fb6cfad3676f83eb764d9768629335f59f6ef63698be1b994c6f7a3732dddc5da2995bf2299d66a85ff85d507e05b2c1dbce3b2c7b8ffab0e8d775f4f14ebf456bad8d9f50d5ac8dd16a9a971df5dacd8eeba36e76ff3264f3f22f435cfd7afcfa6e791c66caa63fa50ffd297483f3f4a6344b7c14fdcbb00dce93422ffb496f866cf07c1a9f3e7c4a23fd1c25a6921ef1b7f0a3f1157d7a7bed01f542783dbebdd48b3ecffdc9fddde9c32328c49f5a36900dce4c5d11d4fb15f2b7dfa132ed39a7afa29fe228ea37fa92aa6eca1fa5754432bf82b7f835768f2e71fcdcadcfcffbb25f7dacc6dd473b9fab97d78b3eabfbdd73f7244dd07ef6b0bdb65e21ffeeed678b2de471987f47e7b0df3cc46dfe7215b1bd42fef4b3b7bf41fa995b9fd5dbee427ffb65a2881d5e9dfad9cfffe8f382485088fd99450c3f1345dcfd963dd57ebeec73573eebebeec94411dbdfee43fbda67d9cb8e4806d960ea5f63fb4d5ced4771fd1abbbbfddc25aef44224f863faeaebeef15ef25ca526c6325bbc62e4799577320f8696002f87d5e71c7efef1f3dfb22ccbe06f9fd0c966d9b6e93cccec15dafe64a18fb845cd6a5bbc029453d0ba3fcb13fe96fd26b9131cc6f270ac8619bf5f3372c1997efd84b208e1c7f88242e2d7a80546988fcff10a10fbf914af60fafedd6d6d7b2d78a3859f4fad335d26857c028559fc32f40bc469f6db6139f0b79fdf06dff428558c2f986559275ec980c01fd56a9fe2de9b11fe073a7787eb8ac417823abf7548381c1f083e77b3d52ebc1fd8bb1375b48e488d65f0b69db6ed94bde2ccf9baf7f1d5ff8911fac71ef9a66fdd6c511f7d7e628ffc8c75557f518fbd6bbad96676d37ecb367bfa4dbbf637e7809ce0b0f9f1e7cd27e099029e5e193c5fb6cecc394fd836cfb6ed6167e47481c01fd9cfdfbe6c6f9638bb3af627fa00c51ef96b96d62cad59c2f2814021d9c4db67a2894f6ffa7c02aedbf64227ed678784c3db039dde5a0f9f7e4d0b9f6e269a387bd3cd2760fa6bb0fceecb8040215bf6da05027fc88e488d5730ccb22c0362845b84580ef4e00a0887b3589a81b6bdfe56ff0bb78fef0f92c20f031ca6bdd7bf6dfefef0a38ffa6a071eb452315603c7bdbcc2c934f1d4715cfcece5954cf591eb4edd47d469d59d7e25cde0985d78e6c733b7f09c34cc79b92f437ccaf1f3898bb7fefcb287278dd5101f4e1a1fca3442dc17e1383378730c4a2579e50cce1466dca3eae9b33f84374b3398bef60f4f97c871063ffb25723cb30b43086dcec323f1b9f9c12f6b97c871a461eb56acef8679a7a8ba5cbc3aa7af4f55dfc1cdb683f7e066cf836fb95a9fbaf9e7c79b8d3cec90cc8f2f951c561fbebc0285c487dcffc41f8f5f63ee8b3925cde0faeef0e3158acfcd796516f8a3b2be75f3afde839b5bdd7770b757ddf8f42bac39755533f51d9c2fbf8573c399dfdd199ffe165f05e3a7383878cb263e7d8d293ec518638c27eeca2c515ec1f0695764be3ffde2ffc49ccf07fce8fb973dea3bbdf7997efb3c5ea9e4ca767ef6eae7567e1b7d7c48ff0a0eb312f0213ede157d58e01ebedffcad7a736bdeece1ecc2d18525b45f61e5be7e96b8fee69ccc42adf5bc562b3fb6f1b30bdbf876e885b3cf43af4c1f3ed70de332987fe5aecc52bf2cb59c9e3e0bd18786d823ffbfd5f2bc4683e5cb2ca76fc78c37f4c2dce9e96b9fc39dbefb2cb560ed7d7c1d7b20f74227ee69883e36f6c077fdb75a32de3cf4824658bee9eb7675374b2d98becc028570328b5492666416c885e539344883399cfa31406cc106c0bdf4031bbc350a055e2ffd0006bb702ffd9084b746d1c0eba51fcae00dc3dfda044f622e094b2e58fe068d3cdc6ffb570ef3fef4fdf08ac3baef875a1ca67d46bfb72dfbbaefbe7e343e0dd1c78d624ffcd46f11b56d99bb7bf609d9dedcb3acfa767fbc3b24dcc3ff710e090af1af799a9ebbd9b17dd3cd8eb3fff1f9652e09d739e7cdef5f86d8fae739ef9c7e85b2b73f7b8b5c774522cef36a0fbfec6148633514e1f9f5c29a498031fc9f08bfcc69c199bef609f917e1f9e588e17b6724e2f85b0cd7875f8659e2a9c1d76ebb6b664e096722ef88d40b256221ef9064c7be51cd1dc3aff5662e09d3b7f5a5c34c5fbf7eaeb542a8c4d56f87993ec3244ceb7cd7be0c6b4e8cee4343ec890fb0f5b39fd9fbcd12d718df8da24f3c5a7dca8d707c00fc79b311f8f3e7c30e09fcf9ed30edeb734750c8fceaff13b17f3b663c22c7331339ae4f3f734958fbeecb1077ef7545523fafd07cff53eacb10432dc6f851f5ab38592b53bda9ef6aacf5b2a296fab1de95eac6f9296e0cdeb2aff19c59f6cddf20373f893324134f6ff8237347b83eed8ac0ef9e7ef37fa2e93b3df779bf7dda43faddd7cddbeea2f1934f43f48947b0e1b41ce6b81f6e71d82ce2f8243f1e459fa825f6c897f01e61f9f0511fb4fed98fb0dfaeb3dd670e6fce7148504886181ae1f89366f3e16b0fc5f086389c5da1f9f5b5cf7e83f001f5b59b21fcb2cd3bfff9ddc3ce08fcf9dd97258657c7bef634bb12961fb5449f8e3d52462df52951c5f03351c5dd9b3ec323ac0d71187e1ee270f7dc6789b5b75d11f842ddcd10d7dfbe7648380c1b0ece16779fa31358bef7b5bbba3f7dd25dddcd4415d737dd0c8f30fdee5e76370f71b83e77b3c4f239242804de2c2586eb6faf7d6b4f3b221a9784b36f6e90ab1fc5f66b3cbf1c02e86eca23c0b37ef3bb3332f1fc1faf9fc4f683b83b231bae9743823fe8955c708b104b410fa8b830e59c3226441336c84116187ce14517a91ddc7c3cf840e50331268eac30429aa202373940edf0c60e2ea8f28115eea51eaaf080871e7304c92865c442ca6f2b65b6584af90696522a91a93222183c70b1c399243982bc18bf79c002c797ee038da2121ea0c01de05ee2010b99d7ab056b5f3c3c4ba4e47901f100bd8078fa045f90b1c38b249c80c10d1e0f49a634ca9cac58b166e8f04687a40dc829e594f2bd242a584a292fe0d91ddc4b4954608a7b29e9090f236988d70b03d6be3c661270dc6fa38f1425e850420b1bb8a0c2e814010bbc2c20010ea98c0e521c539034194725b0f2e50b0ad0e061426ac204c6e25ed2814a0e6b8ee048c0091c20cc88630b2b2fc8a343c29221197184a4288d3bc8a0e00d366cacc1e5e006090725241dee25241a20b1e0dfc5c2f3b8dec5c2bb3ccff53c2c3ccfbbdee5792d345e0420000108cc53f3e45ec255b6fb78b1a77e5b2c3fcfe95fe7afdc55e7ebd53df5e7cdf43d7755fad93b72c242b4e5307aeb77fb74f7648bbb7dbe7beac39be9b7dc551f56577d04781eaebfc1eec809cf5f790e830fe9d7d83b07c4f08c3e7048addfddbcf9b15febaefa3f2e3f7e7e6133ee6a95175e7ebd1c96ff84faf583386c7efd141c96e9cfafdf11b5821b62ea374fbd42bd14f348a38339e79c55bc60557982e5b599585e2def795c047877c58fa70a96957cd7f3fcac62c5245f6e31684591f42f3f97fcfc3cf27ffce5c7f75eaa91f3ffcfe3c10970fe17dd02057133c18c22e968290f7ad4c4105176f4f1e6d10eeeb562c32b8f6c48c29e4603530ba64b38681399f385f3bcde2afa0c79c38836b81421851d446c5d661449f10ebdf07c6f7ead55fdf81d7deaf5620f7cf9d96288a588347888820b135ec46878be37049ebf7258c513093cab0c9973ca2bc46b9c3d6c87c0ef4bb5383c363dbe81656e7e26c0520a5d06e27e09bfdf7582fbd821fd5ba3caf07a890a1c8cc2bd44a507dc9dff8f33c0fbf8f0771c9679708e778807c3975c2f5929018e2f8518127fe53e7248fc6f14208e31c6f8e34d32f0bde82387c087ef9fe72ef8f35b790ffcd51a6fc3bd64250c66e15eb282046e1e580463d25d0e2b3fbc5e09b0f6f5fd5bce8e18f16bc28385e273916b87651b7bfc3bd7b7eff4e5e7211e6c3ffbcd7ed65dfed9a700af7e63fff93f9e7d1277f49143fcadfd1ac30ec90c10c35b6f131e4caf90fcc961f8dda9206fbb2b5e0b1d696bf781464646464643f20b77903683a34f4c0b2b92f8d2069c3cf2886248bfe7b00c77bfebc77de490fecec0fcad3b220ff03cdc4b555a80fde1f75b1ff9396ef1b3dfdd918a85e24b77cdff71ebb078b3c4f3bb61f1b375d7fc76d7fc15164fe2ee76453864ce6f9e79853cd845ea8d191ca0c00b1a274e60c560e1b2c9cf96de0c71bb8b3ebdd962e83d8d42e3fcb0fa99cb10cfcf1ea6dfeed343720bd3a7f6e9af1c2631bd798807d3dfe867dd053f058f3efcee8ad06bed573fe9ae580f539091c70f698800c28621f4adc32aa6efe17084e9efb84f2f191919fd30843e8fc31ad377459f1e426f1e7ae179adbbe4b7bbe40bf1a28f1d22e5378fbc42de848e044f8c3146f83142777f19e14081473a36b223060f363c28cc97432f6ce74714b111961735a0c0f0e537f49218be9410ba0163fc2d7eb03bf83f3ee7c78f9b73f1abfeb083ef5d740867b0f54471ee6a77e51d0cff847b69ca1adc7217fcde998206678bdddfdd5d0c767777ffe6f127feef3097c3b26c392c3ecf94231c9f739f382412c071dec1f1e34b87f1c42bf48ae1856ac3eba51bb6e05eb2e10dce2eec6f02ee251b96707744bc1ff73cce61fd3e1ff76ff36b1c3f88b736c18398466e7ef4614724d2efce01314c37d819994f6f9e3f7ff3ce8887e77727dd257f935cfc20de2247bff9b0ebff1321073e0cb82b7efd8030c001208755e830eee313d9a7f3e61807821c448d0c1e0c9282c39e012b38c061dbc767c161d6088733c499c21fd6edc0f07d30830187e5200ecbad26c8128e9fc20a0e8bcf0187a9707c181cd64b3e2c81e30fb90f1cd241f00ae76e43e10f7a279289a1c438715ad12748cbd4ea5a78c26b7f0bf25a902c08aed7ba0b06c15109b708b10e6e74d4c0aab5d6974a9a34335f43623ffa52495e914a328b34c37aed63492579258b34e3306aa954aa55667987754f9fc761dcd377390c3e7d02386c7b6a29bd7966a9850599e564aa52693e0bd107ae549b177d42f82f38c2cfce37a49a98c6d1400cef194ce195664cd943d4c3cfa6bf0fff666ffbecafddfbcdcff66f7b081e7c6e7de0e3f38370db7b74be7f27a7d501f71d7fe7eadceccab93e6efed6cd2d0c3bb8d607c2b86ed7bcdbc36741de6c3dafa5baf9fb66577cae9f0ebd3097bdf59b539dbd465f62ee0a791c7debbd455193a705db7985bce73e7bef8e70d9f6de15a2d44289bb1f77c52e8363e672170182e0f9b076b08310baa028800e05c15485f9dad3af49102c34b5df36c881f085f0f6044c5f96d26bbbce7357d7754f3f280af0becb07bf4dce469f0f24e04342b836f6d0fff10fbe8e3d947eccb22c9b9c8d3e1ff890bb82bbbe2e213c343a456af4eea2df7d2d77d1e7be1d77d1df3e1e77d1ef2e02be85f081f0d9d8639f7e2b382cbe772a686fef0a12884340f81f0fe1fbe0e99739c05df141f8bcd813ff83cfc69ef83f31fb629bc1320bfc426339dea2c206890d92890d365d93e908898d0989cd11121ba40e29226994523894bea1d40da57650da86d20bd9983e335def3bcd27f579fb93473def7aba9b25ee6ee670173d1ae5f7b763c6d32ed77ffa22d6b297f54f57fb367dffe93351c4a63fdd0c93ba2b647a4dab37c74c57c87690fbee886cf6e9cf8feebc0bcf393f0fadb07d6add45b9bcc2747e34666fbf262b2c443ffb2d72da977daddeb65def4b7df7d5efe2fd8fcb4e02aa47d9d4c7794f4d7d5ef614f5b5f7d8f7aaf6f5b3eed25658e3b096bda679dfd2723bee82de0357dec3860d1b36486c903af7c2d8627372915ba216bf02313c921726b96bd2979ffdfa98dede6c3ffb97fcd8f56b6ffada7bb4cb7dfd1c87b52bedddb2faf26d36f1f4ee88bd102976856a9d56be8431c62ab9d9c273c6fa4939bfcc6159ffc7a1c4da84c342f2cecbb92bdad8acf4abfcccf4bd624fa417c92bfafc98a431f59b471529c961f5e71c03916a4dea3a1bcb8749f09b5f4bdc52fa4cee157d4c12ca3bef676278f1636cd580e7cbeffe44f92521c11ff2e5cf9f38e5e62636e8d7b806faf4c620a4104ed4dbf99e87ba48a018d8c67e504cec88d0d3a3be130c632d14032d180ac5388cc236d852086118995fd8e61360180ac36c5b63cf61b47e997dd5bfe67c3bcf4833b18c2f416cef184cc1d80bc578eebebecdfef6662826f5306f8fead7defbe893fa08dfde1cc447f5f0e617b6a7b75ee6bdfd8e3eaadbde937dbcd9950f87388c92d7f45aee2e916bf025465da1eda27e7bd3cd2fee127976ed663931457d7def8ea0ea29fbed0a41786198ccdad969763e7cfb410bad9d75da0967855c772ad0d77e8b5cea533dfd4cbf6d4fbfed539feaeb977549fd8f6f6f9f7e76d5a2147eeeaaeea21f1128104a7739fa21a9fff1d815b16ffad811c9ae74177dabfaacf7d0dfbeec619a7d9ebbe2abbeef8a684fffc74d5fbbcb7bfec737fb6d9f7dd25ddbd32f7b78fbed61a782f6dbf6f0b35ff6a0404ce947c46ba97d5ceba3cf8ee7307ba19830274f7d71a536f8712ff5b0056fbd39d79d4a057da854aaff71d81151a1fe0585b01ec5faf940a010d645b235273b22ac9b6398f5a8cfda47a1ac45bdfdd811b13f3176447c3cebf371730cab50a8871d11d4d60e53b5fd06f2f18ac32c9189fa545f8658f571ab9c4dd914b03d614a200f3a68a988cc8f3e3f1e74d09a1f6d837de05e0ae20cde22379d904b442ebe05e279b5603aff0585349e375e7117d53c78f8f33bf8f959fb393b1520fee07774726e46bdeadb61accfde77ff137d3eb83b3af7b63a039ab775739f69f54d01cf6bedb3be76576645cba57e655ff5ed98f15097686255eae613ba6fc78ce75d22c7aacf1ed82242f625565d21d45565a8cf3ab8dd67ed7693bb42a8573d7cef8ea820eb1239f7a82b647fe2d82199d87240e08f39bfba9dbe3b22a6ff71f8b02b5221ee4cdbbe7eed2efa75cf15e95e65b9875fdd320fa60f147da4f7d0e7ec0ad31def54e88a38e63e9e3ed3273fce6b790ffc17862e0ce3d780e9cfeffec49ae31520f0c7fcf9f427d28f612a6f9651c63c705516230c7dc0bd84c50eb9d5dc38fb7eafebbaae7a9f50a66fbf88769de7656fbbd597d1c97511f52bcf5b79deea7fe2aa7bd427645a9d3cef572bd4ea511dcaf3fec74fa837997ee5a1565d664955eac4aa1eea4d2bd47b52255312e5dd93d4bebea9e33eae3bb261ed66fad9ebf8db4be10f3ae1d73182188347c0bd148412de4e6efa84727df945b3a2503f6737bf43559b259e0f8094c21fda6bd1f4289486d2501a4a43fd4f44696ffa843614874299506fd24cddff38f7e5222c64faedbb43b23dea7fbc7b22c726a811cd6e0861caf41beaba77b3c57ebad9c36ebada7337fef651f843cbeccd1805eb94b8c5d8555921ee6a841271ed4e8518b6bfc9fa650fc76a80dfd50e7e57ff27d6f8da2734bf31acddcd31dc41a8d5d7dc8eee4289183e4076dfdf1d962b1808eea52b6bb0bcd16139836589aed3b8d73e772fbb2242f566b8755d966559d775dd675dd7ddccd1ace5d92c3697fdc5dfe0b7755704f6973de76c840d31fd84a0d480ed37eece48847842fa9d25fe0c6fc75f834dc0bd04441457d05c71026fb1ab7f450b1600ee2520dce0cd39da11a90f3bce5dfdf55bb9ab6bb6a6cf2d5c6fe63efbf67bbaa6dbb1a77ef431bd9fbed69b1f57efb78a7afba9afbbc7deec61fbdda3beee1eefebee397db6a7bbd9e2daeecabe1d331e778926ae377bd915c93121ed7e16af10fcfab45e0a7f1cd95e5e0a7fc4db33c63915c304c74098b4ede4faed94d2f9442ee41ca59372574876fd244cb2493246da5466f4b32dbcb254d636c56a707f78e45a8438b841682d54b26e9f3bf926adf45e9e3cd3cdd6badbe73ecb23dc854856722f4dca9792bac3a45a4dfe9ce9244f9be9b57882485996c13178b39cb596f30b91e4f665ff1337c941a41d99c98afa187d50f73b2985e4dbaf553e8448f047f71277dc8ddc6f96abdc476e8b34494a29a5ec25937b79442d9ab7d9b8ef7f5908219c270d426b2794fedc2731fd1a3b8542e84f2badf53c28fff4fec18ffb38dc1e3d7a845a849ff6fe69dfde495e2e56837c6b5fcab7fe86936eca124bee2514e2cfdd9f28bfec7670d67485b8afaa77fb9093ab97aad5cdee6dace79d6eb7bcdc5779e9fd710981a2cf4f6acba47c29e54329edcfd7baae087c7985e49f9e8314fea059447912f5de6bd0bd9b89a685f2bdac2342b5e04d72527e12739f94107b6764c32769bb22fedacf4f3efce69b1cfe2639fa82f4a1b556fb1ac3ee6dd4628cdfbddcb49b3d7bb395b25e4be18feea54e7dfa337e6bbbb99569375b4c35ee37ed85b84ce33e7291fbc8dd4c346d7c0a8568d9a5f007ed88506f83a194524a49e18f29a1d12a0bd7123b7e74bd4e089282c3b6d73ed3779f7dff4c0fbfba52a1221116583256830646934bc84f88d36e8e61eda1a42fe1cb2d52cbc240215a279f4b653725dd2f97297577d7ae907cee35e95ff670ac06ea95ca2570ae4fe442da1761eaefd225ae607e22fdb2dc827395da77557afd970f6f8d13c609e384127e866f1d963d8410d2d7b4f74f5aeb79dddc8561fc93afd1abc3bdfdba42dddcf2a7371bf1a74f1f7648be38cc3e7c08060aa10f35fa35d6e86769dff4795d11d4d32b445ffb13caaaeeeaa62efdfad6614e596bea8a78e773355533753314e3f42dd711d9363929e6bec692d24f62f951ca691482f137fdc4eee3deeb9b3eedb3eff4f16399e8855fdc55e7d3cfd673ab615e0f61991eb65a4e27ec60f6d05a6bbb2b44df64c34021193a4cbef71d7de27b57e28d7ed5beb6b7b75f21faf6a17d8d6e374331d65a4b4d500c8657277beee7b75af6eb739f94d8f4f5b7ee331483b7677da7cf12496cbfbe355dee2b50b4b6bb198ac1f5c370bf3dfcb23ded886c520bd668967d8d29a5946661e08f4aa191e73ebec009679a712ead77bd3ef81c64e7732fe97c6e33dbf773def6f1dbbe3f04104c1fecbc4ee6eede69ee59063dfbce2914721f7eb18b9a77af4d3370c9dbb81b87739af76e9ffde7fd79b3ef7e7e16e1b2e0b01c29a59c69aecb79c6effd3e4f33140af138cde0094d6d6ad3c39bebc4596c45230f8ebb8137d7a7f0faad7049e3fef4ddffd976089fbd06a1bbd4efcd1d4ac5eac08356101fd27d9a996670fd6dbb14fea8ddca5eb7087fe2785e9b4fe30300de1c2fd1c4fe9c6b3f5eea01b07a378af23bf2dcd38d37fb14e1423ae1e02debbaaf719665ddbd546e1f7cfbe5df3e04106e6efd0737bbf0cecd2f9d9b8360f93fd1071a512824cbf1717390eee620ad9b5ff6e6970737bb5c1ddcfcac9b5b4baa9bbaa89b699fedfbe7ee3d33994c266d7bd3677b290bdec6dd389c68947d363dcc2ed1c410c2cd5a9b3d6cf2cf2d6cfa954e4a29a5b4034c29a5d487971ae4c0e535a9a559ed3b6ee03d4d25e9ca968a63b044add9eb0439b881892a698ca0038c375878f060adb5d622808b1a3dbc80d3822890c8c4d1c8b8c921084de332441859b880066c30a1c3901a8328c8c4608b2462d0438040d364b600c3e5861af490858a0222183fe0e1c31ad2891e7a4a40e72b3ecf0968b821e68c13fc70050509f8a2bec14229a519d5ae54e967e228cbac0cf132adf1668d1bb041858da4f93972dcb0871352bc71811804241db5154ddb810a6e06aa2972072b1ee89004a5948201e33d3da64cf603e6cc534a2911714ace0c5455ec10565c76361a2323231c68adb5b21185f7349831652866e30ca5aee74169395da932e79cda9c9c1a3d4c9a161303e6cc164188d9c386255250d26176c1a2696dc8a1d3c60d7cf857c541a5c3136ac82c9b42060730e894aa06d73a258fc65f3891c54887232fd258c11c6608f9e28b46e68b2d785803255f3e240b1710c018820b0906103b24552cd424d912e07c3105ba400ccf0bd4682f98230710d2c820a90c2058000334be40218b35ee48434d1a6bd26843421a38b22519fc7065eeb186524a7b50e31b8f399096b43053850c3d0e30459824b8181919191929b911ee2526b41809a169796c44685a1a49043563ba000e8a8c1d3e5ca9b29179810d3c326d4ed08351abb502ba83d7f2569ea7431a9e0956def53cff409ec0527778d55a2b162aded3491363b9d28a4604a005061456ec0e5d6489811e53bc50c38a206f661b4869a295278e40010f44989182295990f1a9c28c172871f4721fb223dc4b561c1de10078d1c2488a3b54f0e608d811774587773dcf3f5784694a00e210038b25ae00d3c50b0c40a908f1aee7f9cf6323e25dcff31a921bba54f1018d227ab0e204306a88f857754197c7a6e466ac31a38d196e2a1044fcf0464a1f3b44ce39271aef69356bdab89143b4c091d2ab9862584cd9d28551162c4a5698b0451c7b58c9010ece70a20d901d58256841144234b1460a3a4a78c20c0d0b28a67c41d891c41674f84046122fa57f5122bc373232baa1d65ac15862821185d7840c5cd0050923f4b0e28a1f612c6185a70ea5a3b8078551888666709329e20c9c2328618238766061b4e6a800532c4d244dcbb952c5948419f7a1b71a234d1c2c50230c376c9004095ed8c1439691f1c20654bc8f4bb3d25a6badb5d65a6b9a3770ad72881df08064d070d380339618038e334865d610204d13199934544cc121b1c7111b61dac44187990178c1e20397d6cbc8c8680aa594aa31f29e3ecab01aa52c6b41a1b2061c3adc28c388140ca12360ba83894c1b53b09851323232ca41f6120ec698c1b407d31c2861fa84153746a0f4bd36984ef1022a5e2a9d34c394524ac56893650ec6107270c1c20734643084ba302553c69a24cc9833e032adec68d0464586067b1c35a00c30ce88e9f2c51e360800ce948c0c9ca42a31ce7681317030838e2ccc40410b02200796d7ec3546464655e69c6f5773ce39a938e33d9dc6bee1b169d9e13d950a17aef525b930726c61ec4002001664ec5218347e30c349102c28038e2fb808420b1e7aba0308315baad840658b0857a49713000146075382e88114270c1a7508a1c7f582b481f144172449040f1e8cbcc04153c798af0e248e8e785b6bad6f784fb3b176783f6fbcf19ec6b9f553c8518498247a48620c23fe8852b9591902b543ec072b6cac00872aa840410f21d02094f4b0628e90bcc1e20436bc80871a80010219206440660a31901acd192536be443144970fba8854689054e9a4b0f5d1470e518a3a4b584ca1e5c00453b63459d491650c1c1510b143c5b261e268027770e1522689277c8043c78b1dc24a1e8d066f27c8c5c8c80889524a698606f7d21d4798d24683b57861250f2bb2d481c699213998be970653374b573c7f63d65aebac3f3f9e338eae08b325a90d1940f800c3892e910d0cacd450868c25c250e30641f4d0029346aee0cd0eef7a9e2cbbaf471a2d6364a0061e6178b0042607948d0d3afcffeb4ddf600dca4d1694cd952a40c6c8228b29b05843055c7480038e0c0e7a30b2b8d65a6badbfe2813a78f52b66b3147b1acf69f18f679ffd56ee6a373150c1094ce0461a3e28c172c2b6f9e283a4523ed5ae47a9ac2b5cc9b031836b65411a5c6bad370fbd3c77c9cf84f01a7b2d6dd8fab34387fed0ef02e4cb2c7f932da55c05e1b57b77f37818dbe9d309a149e88fe1c6dd81f0c18e4e8e8f96071db056aa14ca3b993a6ed3325be994d19b84feee6093d025fe8f5bdbf176f714c198bbb3b935f87eefeeee7edc5d3ffef11883900431c6fe7e7873fffcd8b37ff60bcd18638cd25dfd664a895ae10d35ee6a2c5b4a29a5942fe99c7442d966c96f5e618932c6e81f1fd64133a594da34fafe36e1cff717729392e79f39188cdfb0f9197ebb4bc2772c045fba4bb60ab42f22703395b0fc8d76f140c0dd2464f9652a61f78caa02cb975fa612119e7c69e340c92f384f2e21e07933eecde422abc07972511a83055394b0d4be94406a1b673d544ab5629d4c5d16c4cbda95ea85f2ec13ef64eab84d6b363f99dbe7b1945e0955b02601ddc8b623f71b3cfb06787e0bfd46a7e933b9cba44ec09300789aba27904a10af2de562ddfce26e0e82bd27dc77803a996e77eb85548a6e80eb0342c0a8875f5837077961faab9b5ff4555ffabbd9a06e8e6df1be6fd7fcd919e925cc3d548a3ef0cbe99a6e7779f0e46ee6c0972f5f80707d592195bae6063d402a2b98c1218509d0582dab9c4b0bf7c280c5f55fafd76b02fdb42e3499cb34e89c70287e99d4271e148afaa5a20064848e9b33dfedf307a3f6734421fbf9b0853800fee8323bbca2cf130740213009fe88ef799e6d712fec38f719ac3dfd64bbe63f893e16264121dd3fecbf1b376e50e826213bce30095a0b95a28f92126e30625e3cd64cdbfd9a84f957bbd903583efcf04c82db3502dc1999b0dd94dd6e12a6a55de664250c396497c13266821605734e5a6bad75428974cee974cd6c69ddc19873c239e79c904e7a051cd3b3cc68835ab39f6f1b05faf3b346c1723c50a9f21a62db84f954b2aff24334ad88b609d31639b677360993e209e7ec36c0ac41d25969ad53721ef5a88775d3d25f27bcfcd5b109c0836526b797109b46dfe5fd5ce1c1cfbdd44bd2bedc4c392c9d73cee9940d8d8c680329d0fc363f59b7a9cd3929a5744a0e64d9b7c36a9854d2496b9d934e77c92b7d684a27849452af66997d3a9f7eadbf550e89fcf9c5da199197bb4df3901d11ffe9ad5a63a07c7fe8922b1ae3483c89b472a330bfaeb6bc8cf0b8cf2febae88a79423c053ca4a219c52d4392dcd68d71eb4cc023d9c614641e9500b533a29a555d096562cb58401cf97de500bcf8fb6f38ac392b68070cf0546e2ec6a8da0861735640bcb5803d928d0b8c411c7f4ca26814e4c63f5eb26226b9044314e9c38716688785034e484b235c4cdcce109a77f5022843ed66b496965a535674a39a5a453ba7c971c1d99a5da4b88ed73754f66e9bb68b0d5e594083f3ebcbd058c1d116bab0f5e2bf6c4df7aab9cfffc7c3e9e9948d28e08bc2defcab4020ffa6ff3017242a8855c22411b0c9fc2e165a8c5b56029653709b2634fee36585e1f722d30449ffaf2c225d792049d702796221087d41b95624f7c0d8b3b91dd09d712d3f080c6b5c82579c59cd3d239e72442bf9896f3ac98d4a7153aa5cfe145eee7dbe8b3bd10dce6c25a665f4fbc17cb8b1a5e5e585a8f2e786f35e184d07dfaf53cafd592d67d32197d88ad6fceb7f3eb7c3b6b4744ced9199997f3aec3c38354e61af09cdeaa2589249eefdd1105c4891347622971f344eaf3b583e94aba0ead56ad40ae4a6b903c3b62f058014f5443dd58eee6f8252661199570354231948ae1ed2621c7a32e9efcee5460613458deec4f7f287ef9f22a0d96ef68da0d8e9f87e21725dec0f3f30a473438c725371d607aa9929bb8e469708e4bd1a900f3838d5fa86403ddd04fb61b4ca1bbfbd06a367cfff94189eeb93569bb1982f3ba0dae9f5bd0880a180fbee5bc7693fdb00419b2ddb8a159a637efd3eff9591ebc69db45394cbb874a8b290db6d639ef1087a91a5ed4f08269a54d38cf0d47ddf0629c710f7a048c74465a65d233b65b9db98210d1ad7e5d84ea5dae17f75a76f3b3107d543775339bd9ecedb51f113dc5891307ce40b1bc338ead79b2e6e6ad73f29371d6cfd38989049653082ca70f58fecc611ec9973f78526636044cbfce239ce7128e9f83543caf66468d073fcf258d8cf9057a41c60b3bbf60e92ecd0d171b94327a97f6e0514a29a5b7a5c33210a63fb9d919794caf7744fceb4b7a25d13c12429c387130a5ef4fef0c13e20cc637fed23fcedb5bcece179e7f6329dffd8df8c71b8b106ae13e072901cb9ecf47fb78ad46817e90149a0812420e01670f6f5a86a63aa617862621befde2c1ef16a4803a2a93844c014b0dca2b3837214043bf78a15fe8172e948a1d3c2f5d9253e8125da25f2017344ed02f9c1b19dd153120b387ed37e4c2d2b39ee7aee8eff9b4a4f5ba789ce7ae18c45df15bd127f39a82bbe2d3a52096f31c4ad71af902ca5292216b4d7872485bf28c1ad29694bec0f4330affc0f36a689a84f8b40c0f7ed6d0686870fc293787dad29196238c2a565b62491881983e459e9191910d43a4f06ff95448d2dbf2e3d36fe70b4f7ec30e89bcf1db04af9b3fbb151acbdb1d8147e2851225846f54585b58fbf91d875cb8562d5eadffe3daf7dd33dffbcca32e3c69b0587e4b202cffe96cbc794784aed0b8de21179e0fbb233eced471059e167737e7fbcfdbc485e595afea51cdd62778f48bf8cbbbd36a08615ee1a6d9c2ee964edf7b7e919bdb6ce8519aba49f0bcc2ee9193d4d60c85f659c52d9efc22f25afa1eacdb954f987ebdf98463a7c2c4f3ba9a84f85d1c1efcecc2310ac4d985e35b0993d3bb151ad33bb4c2f2bd3b62821067eac0b23b79758cb8bc4d5658c2b9471ee260124c82470d91c40871050f00f7521070f0766a87e5a1f905c79f2d2d0d98befcac102dee92b508bb102d38cf9fdf9d4721f136fc41e997855cc1f25d8892bbe44f2159e00f7965ac42cfc23fe8380eb6bf75a702c4f6eaacd0d87e91c6f6ee94c07b9c6d0cd72f3faef55dfefcf11ac3f3239afe3f3136215c636b1efb0dbd69f036eeaadec65dd5dbc0eeb3cafde93375dfcbed3bfab4fae6c7f266a289a4e10f7ff94148e10f79fd9af97157bc96049e7ffcfc3886730df727f6a4f087944598e6189424f0fc9fc76153fe45835b84a218d39c9e606b9e0c9570b4edc25bab1b05487fdb9c839dbd2bcf23e25ed33ece3b0bbf19bc20d10786d813dfb6e95a064f88fed4607058f6f1879e1039ac068719394c931f1f1ec123982435fbf688b401a219a8646a598a4335ca9843333390000034005316003038180e080524b2aa6af20114001179b2525e4c948aa328c95114848c3184104208010400600818438463039b800a8633fff4469cb02112bba7b18c6889720983c7847f223b9fa0c8e05b82de07071d25ccf677f412f1965aa818d4491dbc287da19482b109b0a6ee1f9984e00db19ce74b3d9a168fa6ea485682252495d29dd0dbd223fcfe7e96f2d297f6c51639505a73da552aa6b995b76f5751949906b5ac454006d2f0a35901f92005c4a22a21b958db58153d758a38548447420459157a234b34bc3c86970a262a4a44a8fe835c3accc41504cde1f8769e5be53f14a268e726ba2fc37129f22cca15cc173a186afef1908ff09ee0262b6e6ef8cd027f270ab50cefffec365b712098ff0a89dd72516d13da3d44e0b19ce4d71b5f069b80a06ca562909fc8ecd3306976f35444ccc32ff084054f5e04ee5135c25d3580150d04de271e15270695536ed00df8a051bd03821b5ef8ec63b2d0f17496427d9799f8feecaf0ce2ab300f10df58e96203ebda71c902d61a2b61cc9433be973f0bf658f3bec0366c79fbaf34b140ed1e42b29eeb28353c537326a83b6db7216d17ca438291e60f1bdaa41a71a43a807ad708a4a96813cb910c0234324ea62702d044e8412fd27edb3c1347431fa23b71940b02128c79cd05968172ec84427229f2409574359c3468a7273a58fe88953bbbb53aecac0d75bb1d8fcd8ab60f730cbba170612e09b1c71d89571ebc64a7884c59c8161d34c41d20d99ff5523722e068722461bc8a4610988c2103c7708faa59b4d821e14efab164ea248a5c58ea3a92e110bd89c68df5bbdca69ea2a0480a84ea4eb3e2c28de6bb457e82818697e0618727696e59f781831dba51f572b63488b5930a725acb7086a47029f909463814b038a913756edefe8c955d3c857c7d755bac9ea6272be3080bf970bfa5efc42130ac8f8e4be28819617ed4686eeda6b67d4d505d014ba3775e383f0a5324e08df6d96c6d454c863d31a1901982c2ad2c3f02512bf94ffb81b02bc62084f6a007fce6a3a79e51f83639ede732bffbc70fbb95ec2d409e7ec36d5fb607c227353f873ca7660fd652b2c04b6975a806f4f526e28aafdcf92f91ebf23a3baa2ebbd54900b0f170c399abf58d814679d71d7b5d1ef9fbabef86fa4c56951a4df4f3d8feb9933c2341225c61e865bdc2f20d0dcbb3facb10ed0e5850ba175d0e061839214e01bd04a5383df04a775b2c3f3ff7749e35b84919db2c0baff729d72d0e1c36164865d75081d2eae90bbd05bd4636ea8dbdb72e01f635ff9e59dca97445abcdce6842103cb0b32083aa9866e33e50ba4d9921273a2d17613ddcb2f78bfaa9682e888510aa701967a509f30749b2ecf16608a4d9db59d1592c54c2d25f7e6d076a76953a1ef347e1b7dbf3f73b8ad8352842ddda9041f9b5e94a8802d553bc62568fc68ae7a3276f1279565b4f2f8767e8f4f37d00ca8e27c7735db6c6082024e73e7027903b65457d791f697ab6600c366f74511a4b85b0d8b5a7d21bd41c151057d11f9205702982159e2e513d19a076f054d53792d5d0b8b139315bc4afd1dac03c9731971a22abc93ab5a4d314b65c10530e27ca1ceefcf793be863d480d617b506b9e292debf6ec1be6d87f9ed8999ae77e0a8a821a10f88d22a4346324376942b484b10ba9de14d1e3d7468235b74e089e4be7cd4cac038ff60190558201e13cee9d765b18569dde3d5c9a68c70f2bfc3802ea584546d5ca781c99926a12e5a3bb8293726f0b7c365ae1ba511515ea2656d04c043529cbbff9ed72183fb7abdf6a4c43dfcec55cd4bdb0692b5cb5853cecb78fa384c78d61e4c461320fd53047e7997a5681b8d5ba9c49d2093a6a656a13a0f2c2acab52b0ed1c2678373231ec0369eb9af014883062dfbbfbbffa0627860309337088060b465e25593980f1f63245db4d2dbafab0b26ab0200e7f4b7d813185acb8688fc62782b90036bf6bfa83598f76ea6a812a039a5917496731e5105a0c8a4e27298743c02757f31b929baf6d5961ae7222251b62ccb4e20ad5f62a90a351e2cf877f34029e42ab0aabe7918178a8c8364f4292bfcef84111020a1b2ae040594dea6ef73d83d618cbe23adf4424accfcf206468cd7faee4000884af6a4f82cb7199d4b8732e937846a4ce0b7cf3890fa26e7df5ef61aa2ddd943c1eefd0bc64728d0d612855f115a773ac4f46e2db3ca201eed4a6ed6758e9686a54c665b24bf7d3d0aaba98c5a3a14df6885735abb64506ff81f657d34ac1897f31f658d20f643fcfa1f70f68a8ea511df4bf8ca7dcf89347ca81561694bbccf53dad64847253a68b1660bed6a141754bb6fea99c75e95665721c3502fdcd72ee05eb3b0d88aab0e1092e2b2e9e27481492e39ffb7d5c882c88b6bf3d4241f417ad9b7cf46e09c8e4383100c4f16e3e64b6d25b1096b4e6d19bc6c8c660e19feb36595cd08cfdea2b7ce1868bedccc720d954ed6189a3e13f8c9bf644c20f96e706e5ff68423cad062a3af527cb0c8bea85ecfa0c6b4d0e864c6b3246fbf537ac68802d6e6f7d120035a1851c61cc7bdbf4cd88edef952799ebd24a4d05c7cf82beae23c9933acc09d67fd851ae6553309d46a160576c1174ae9351b8798854f2712983b80f75b1fc971dd8d50701eb2ced7f31262dabc006d6284e0a640d4cdf9d2f2f14b81ef7dc2e430838da441be003525f17d0b326bb844877e457fa0ac7479d52822ba84d1eed93181468e5a5a671e8620459c36dc4e7f26df061f55e800282e43970be4772046fa0963c0c7a6d80ae040a02bb891e765dac1ed7641a5b45202cfca6039317c099b6e542b823b1d1711ba8796f724995e1ce3521f0584ba3b5b194234af17cd4561df5c52b29b12f8a0374b356e71d7dcb974b051146697a438f2f531f3b80907ebf298f3312a04f6633dae17bccd8b4902da9d624a70888230f2947c48c7ae139c4d9295770b05a6b3efb6366f7d20af279d9fb84df379b5e4b3dde917fd6514bfcb570f4b47e56af5ba3e9d27896cf3a23e9d89774f1b7e89b61de876a106fdf3c936ed9b1c520c696cba71f98934360420ea32bd33e7e217590471a5d65975891a0443b83e2f310b0edf9d0aabacda842bb82b7756460b4d7ef8f035a2b21868d0e3bd4a66cfc6cc7e553b3b616b5d7fb48d7b80cad7a6767bfb40676fc7ce62545203b42225e2f93d7bf41414b7f0bf051549a92f946ef1d2b184d650289cd0673093ebef15cc088830f99553b9481f649bccbdd06953ff311f0159b7c845fb4e9a4c97a0e5de19a62f62e43b81636032ef22e7adbebb64798d1a5e5a921aca0ed9189350a01e8dec10a70630197bdeb8288f256ee9394093cbc2fe5604240434e82c85adf0d31e882d64be51770e82708a48d465dc4f792f63aa385280e53571b5e0ec57f1f9d8bf153337e1652dd7a11e4ce9412206650cb185058fb6efb53dd1c4c3b7e33940b0cb00782d36eaa25a7d4ee3035640b2d4ee07be3f88e4b126d2f8c8eb17c486851da2c9bc045906dade52a85614a2bfebdb0e7a181d2d374622f65d5cd396f85e6bb252500234dc5d5f50e93ee12da74a9c849e88e15bb81de159a60d20bba68cb8c30d1806955505d1557c08e162ba002707263cce400a1a879b6237f33339c05b565251ae712ba49c4cd0a1fa82a582e3919722c04a402236b9fad914b846beda7475325f9a6f14a6e218b709db346fd56e35766bd5876829dfe06ca938d3fd9d48313906ed0b4d1d36fe5e6e10d0e550323b98504f8763bb4c7799671c626272895b9e6611d4657b36acb88549ee1b5e9ce29420f82638528ffe4c378158fbb67bcffee197f1e4d3cce5b2d57470fb2856ef3e683d5bc446df6ffb905041af320e8a9acdc06b0b045ffab110bd9140bc8e356508e606d7f52bfab673edf130631e2a1b2c84b0eabb8970b66b533bfff4b44483e442e90147f3778361bc6704c9f6c3e8ba2932c466de7da05c6ad8b62b354d575d1f28e2e3817d2ad8284a6a87386c0aa37f5aec395a7a0c6a2a4a4b085a9800c204efe084aad04ba95773c587915f8b3e1c8d855c8dec7e16a75df04d3e42424417fb1390892892f1a3adff7ae09e0c7bb8938c98ff077f74ec63c9dd409050e58033e34019be50a9a035c2ff8447ea304d318aa23a7deda022e6283dfcbed6431b7849bb96d7aa62293043ebddb1df1116720dd3361b17c419247cb652f1e915443fc6be8932d642ee94881899139e128f6ebc1614311ad4e10cb524c224ce0ac23dd81a3b476e9f840bfdcd8c30a2993701b2548f50b9c504a7a629a322bb22b6c469e807fae416d7a6ca486a78b89f6067eb45a01e235da8191b53a72325e63b6c06c58ada2cd3d1cd5a1f03feced9f56cc4f6a9dfc540dcb031bad8f42d94b1817f2b99e74dfb2e0ab98d1cb5aa92bbaf35fac643979475f33a2a44303edd108e9070d643291f9e487472034f87dfd605f841afe6152c33f63c92aacc9592ba1682ed3a3fb3a0a80892e1d618271e60411910201456847e935165f320ee402423ce913091b8e2f8f9e4f77f498759973a426d671df83cfcff8d078e3f38a67e2bc691c57f56599802af9dbc6e70a1365e2be85cc61045a346239f050fd3cf15c010e5a0de10655c9fff039f56a6c8b881b49c489e2e08f24fd80823301b3c4b76d362b2520e66b838f19cd360ac6334919e456ba4d2de24dedb140f3cf367eb4b4941f9a0770c2a10f4d408e7475210de2d4e8d9a80748afe78b3b5f5163622eb45a0cb3d31531b4a0fbcab933b278eac661d5046a6ee06296a3f77fadb5fdfd647b1be9cae0eff5fcfc6876cde50ade9655edb34e626e01f8dbec4a4300148f7139738314c9f78c61ddef08a415c4beec71487b279808d8d04f118d824f5844e03988cceaac091528395ab7e88fa5cf2414e2e95affb4dc6b21bd8ecd3682da314b11ff9d62fcb0cf97f309d9b84cb0dd36bfabd24fb3ac5eff10fefda1f833b9a25564747d0deb94aa7282ad0296efbb52ca5bea7bf23e1fb1e8170c4a3e1fd4f60719c6b5b44cb0c1bf6374d159e3528014170874cb5884864d80bdd7616354afa9f6a3b7d9bb4c82b83af079e780aa45a4e56bd23248c08531e629f86c64b94b0a1b1c7176e1eb689fc2073805b1f8c45a8b7ec3f562c3d5ddf422f939fafa85c70c6152de4663692d7e4e3d0a8802a695ed9926470982615ad63b43913611b51ced522895669479bda82a1314a6c66290d4e887764cae247782b2fa9a04c86216ac5aab45b5b95e5798210e1128772d2f5065ef6780302b4a1828ef2fe8fa14197bf8e57c784790fc2db41509497feff9fcd949f459a09560380f7e90eb1e3b57cc6dbad2890b08fc4a40c1f23ffcff494361854d66e5648b8a69f6af222987df781981205e5c289a70c549df74f76efdf82b22549c06e7bc6c1904a79dcf4f411caac79e5c93c451a82226aed8440fb8272abfe9190a7cc48ed99f738a67319ce9e3da0cab966372ac6c02826f1cc67acbbda5d76278f5f1571638f6ee172d493ac996b2bcc11b6591cb6948228d2a989bf606ee7e6ffe49209fb752b8603f4c2a64128adffb6608b5f09b94722420fbb1a98116d4f254c93784ad71ccf398ba669851ba948430af0456da252358419d5674ee9b61487042ccd6e58adb58beee8368ba55d5efefe7fac675233878d337cabe0986c78a84e2e71e10760090af95b5fb8160aced8f345a47d7b94ee300bf62e3bfe92769c0d8adb2a48d18e2b51c7c164127dffffe3df979d8a292ff60f095fdc157c420797c857ba7ab3c5ed766528cf354a3bb10684bd08566ebb00f2212da607d8a704a68d820c9083f9c0c3cb1bf4065ac91cfb19ed9c74da4eb90c7ca395d5f7dc5f811b2d15e0d177a40f86b8f7eb621c0fe2002ec000736d80e542759b32dc1ada6687a60f1a50164435fd9c7a30d94696e36859ef54aff538e22fee6de883213b92605a128c614ae533f831687b5e75bec927f63dfcbb7e95c3570920e5c3e47e2515a4cae43181248570f0f9d3da5220254e8ea3f457579485cbb7ae5ede7a09c44596ad4456ae04002e7688e2bd6b6514046476982ec80728c5a5de7b6159d9175a1309c77d618f7be11b8728a019501c571909a496a0df1e268b2d8788821f5b80a93febd912a71530d6832193e4e0a09dd65b1e521748efd83c5128ded6d21d5a1ff3bfea6ca3bf4685cee29ee0f5fc183a39f0df78f71c401bb039b38a21ffcbbd506ea311c118156488d5e00a9f711805f11415999b88dc3e731f7aa3cb6dde35171b95e93bbae1e1160080ded4e0b76f174b6ee6826447a440ba31b79774bc2700ac930068c76d5f3d0090e19ece53c76023cbb8c976e63b0e1407044ecd76a0f666132c6b2cae15867e8392bb633df67c805d72acfb530d21af5668fa70e0ceb025e3743cedd844339c2f7d649aa37edae9c0cae71cd35938f2cc468a9ef09338afdfaa377ce7f64ac7d2bb310031446f7d9497f13a3596844fcee309adb4ff0c8a6527769797ef52b769d4aadce3b1da8ec676be77e41149e19f173ca9fecd8d306fe797d5b6aa93d8daec63f077861e13427d039cc3aa1b1f3c82307292c1b1930b719fe35535b316f4194606a877d95a78862596f8ac5e06c62b0a7ba2ce8c42c37b203422c8d90eaf98bd7ed0a2e8ea1108c4f05b0c4bdf9cb40a1ec40aa26273c40550da1671e825224ff4897b27f7919e92ed11299eda8710837162751c1f96ee03908d8e1509a8cb53b6fd5fd56c2b77fcf7e00a546867c0338945ccbf3d5508648fa7cd18446820d283ec768f03f57dbcb7c26a0e0d7ab2881022c6263fdbdea9e021b152ae6ba4aeb757ef1b60caa95ff9af37b9661ccd370d20485ae3a61a94e0ceb3ee04e8592374b8c90e221b4fd83417e0d87d0b90dbacf99224324e19a5b637a61d89f83808300119069116ad2e19c78d26c83205c2e06c17795ad453ee6d47955c9fb5253f3b46a5b07ddf8d1641ed5da6912c1f85c3bb23436c16645637b6b79841d85247ef80cc317b0f40550ee25950868d38efc17df52a01913f59587c84fa089c2b63447067806c3da0b9090c5bc33d0d8d1421aad66ecd9c706a2b809ee4193c6b71a3a384fbaaaf5f47c2c4f0a41b148dfbd3805c86b1a73425da8524a81a7b9a1dc322142d7cc6b7da25fa106b6de887170d85cf5b04b915b78dce04d02dd1950dd68e61055bbf5db1b5af1fabca8c8e00cc6dfcc120cdb970c50e12c1d73701f00f4d0dcf2a8cd91bd2889af3d4879d8caf497e485b2a6c44d8454d2cb02bc97880bbac71b099124999a23444658ad58eb6248c97e9c598faa5f10ea52b9300abc4ca06f5d21a1a586a45c516a0c585256c3a410a4db066666c444cfbb89bb26ff5123555490a7e47cebaf49a117f5e81f1a9b8c57209be03a788f451f52536c446f2aba792417bd1d8b74a3ea6d009b602bac73c2f01458f0da65440628acd0af53bc29f0e721dd04abe2426d0ce470ed2a42312b78cc486a8a0992ad3f59aa4fa165f40fc1a7151967dfe3144795b05514447f024e5127f11afd990ca4b1ad1429174a91d685889042acddd1020edd07dab346643594ca8d717cc27bc82fa8d018a6d04061067294def003ddd5350b25e93130c91b35a482bffc792195706d13676f8082f134b34eeeceb78db8bf31f77b68580b667f2c8d86d2fa62c59dfe9f6136667a11caebdea53b84a21f88494a26103b393ed394c520ef25712ddf82aa57b470976fabe3e629df2d53a259b8b4630edb37a22160095197f51655ca14d5e6d64d48539391d2be9fdfbccfd01104bf37ba949288d8254377cb43172c2f6389dc8768f231a37e8f4165cafa67cd0a2f3f5c08a3b370e73f302d0c10ca063ea46cc3970582bd5d2ea781120c1d20014c20c332a321e4d5151e3db8903a14a7233c62635cf6c0e02c0434164ab8ee26b4e627d369eef0bc4b3eb3f2ba91767e6be0a84bb70fcef720f5366f63df229b6331db067753126473e078ac9b1cc848786b24b0c21bd8d1ed2e8cb1bcf8795cd58be6c19441c8a41161d730aa2cb65d15ab1f5c9d57a8dcceebbc0f22560c8d87055c2166385d3e33f88a18022cfced79da7a60d939ca36a3870f1ee9d4a9cbb5212ae29feeab8cc0197404440b38c6eac85519de69016b5f1ad0ef1f4dad51522d3486d983812765a42aade8f859c953152da41df9141bbc4f7ae59281b4db14f24acff0b39235f5e8b8a55e8f3ac90b0c237b7b97bf1a2c419918da08f69c91a5e6973339d434b3b4f3ece1d9dabd8931644a9277ced48658b04e1eb11b984e7b6d5f734932208561c9069e90ca0e615ef82d9dc12f7aa0c16fc7e9512d1a86a772a08454bee0066c7eca3e7c90ce619cce1be8038faacaf434b752532bb0ca45141ebe9fcdc72a48ca6485680467a4213ddb7f756d47953230291d678bb71f0c18ef01b71130d0a71e8161ab80f247d529371db418f3c1727f557d4e1269741a0e6a1f7317102eb5c9270bc20fc0cec4550006b1c13892b203e62557a361e198717f02541b35e532cb364c7d8940d20abbce6d03fce80e1c28bf0ade4dbc5763834a927bb6a424e002b2f9e014b401dec941a07afc43bf9cd25c4933f1572787eab436485c8a8da581f513262666a6354f5c2f3f044e419dd3ad031df63b8aeb154ee008b69d8f101bac28ef4181b7ff60a404d2bdc9be14446daf489d197eb6b341c2723c77c668f0864ed0fdc337b77761d8a7a5da6c5b3cf4af779379e704c08373f66b7188b381a28b4db9e96ea4d5dcecf0f8d4a64fa60eaf33445051d183f7d203326cb1eb01920dd68b1fc45a59c574d87b9f08b8c59a54467d9beee232027cb1b2dc34fb6648d47eb05552d26f7326e338c8d9a6ebd6eca51c9e1ea1c9c15c6e02a91d89dc7425975d20b5c592753efe1b9efb31cb6e840723e2f8ecad092353292bd57e107c3147da1e47810204ee1b9138b04112de9711a7310548000629dc0c58e68417c193359a0cf0f06fa0d17a317a52a4a5a4ec7b5945cecb8e337f8056140a5993928eca2efa063520be19888e7eea349218e33c05e71346894bb087d0595e2618120d63bc2002cc5954818e5d9632b9dc13345d15f336ba6ddb9d0b2de6f51a482b5c365d80223f957e7aa6420458f504ec77ca3a223223bea6d96420c374fe4d1312c636fb3e638b084661171c45b01d566c99579922ca319b9494aaacba9017160cb878d868b7a3f99d38ca5c1869c6c64347aa1b5a0a06e7c3bb4e932bdd0df529d61144f61f80667853c62cbadc6c4da073c7ebdd9b29733a2b6cd838d3ba4080be80126170253c5050e336ac9a8ad10d74d9d7f86321538c9f6a30bf5ae9500964c54612919f04e9979ae6520f961a4f6d8fde0e677f4998e9dee44f94441915c56d942c5037110a5678456b575b26aefd36975b5277a958cc2f425acafd650a663093e1077623d9ad3a4664f73c26191cb5def7edbbd4eb6701721a51f299088774e70a91301e70066de0f80983cb6ea865284e6bec2e65d68b271ef09fc7fa317962ba68266a9def9876b85865fbd4bdbcc75960b41fb091ccb8e98750b545de3786ea2bdd46dee9847df67272817a25760bad9ac75d08c816cc7e2110102a04edc67b2aa5d695cb616bfd31f9818775173b2f4ce3717dff84c486ba32d4ac8d7b370a86ee7d1207a661c5cc097897e7f71f2428d7bdfd5cf26ca57b4fb1ed52362c44c7cfaf57d643154f8cccb8124a4d974208dfafe70f0ec6cd1b3c003e361019a651de2180636b74c31004b700990841c3e72492c8d83e9786d763cf455ce6092272119ab98d7c7881253e68d77a1e6a3ce8aefa4652f5fb6de6e530b1a9b69e6f320805444e7c6885158828d2919e2fb14a5e99bf54501c548579129e5746eaf9f9f52e8c29fcc912f6e3482c004280e77eb5c4c04d99b4829fa8b3080772150f91556d815daf58068b78b4af1d78e6b9a8f19f55cb022eeef016d0c7df2038fda1b7c059ecc6b7d6514fa967e546218731481ebedd5c58d76a33018f16b8193fe6aabfc2634e3401fd85a5e3bbc2ac7b2c38bee2100749a13babb11ba999f4095c361caf4a0d7e9af829059e0c418e8865db30f957c674af04875664319f5479d5f2164cd73434dc44b6c4902c54a88d962b187c0a4026756307f6d644e2882b6afe25729ae3bbb23fd2d2e1830f22f34540409a20b4a11f344f9adc7bbb22fc2475536f5be2f640eca3d01132bf8930c856abeb40f323ddc8ad0081230908fae7718a3750c3f05be05be56aa8b05a4e05d8432c2d454dde0e82d267801371ab42b2d38f56c801cf580c60b2646cce9123e5d2cc558533aac8eeb3ba4a2d9a338a4c92314b52ff92e56a8e9459f004b20bc49aeffa612e1653b8366609167994c866644300f094718a4e92ecda647a34f1f274a0084b33f6a67eea0fba32bc6c6a1d0da56c85d45b0075bb6d9959b57df62a667f0e915909a086168baa9e8e7b221e70f2f70812461531d203929e2ef06a751357099ac460482ab07e052506ae67d7537c75433197c19a0cb4f685eeb1c67d9774fad1d532502610aee0aebdbbbaa5e6e5dcbc9ce0f65329d7ba51075e82804cc03b57aba04d7107a67373bef46e974f0e86eb771473238b2422997a9eda3184ac4e7d7772910e9cbb5c9661cdf20fe478786b492d86b8d18097dc236a70beb3cb0120c9b5b7425be8dd958861ff9083824a4ab0fd2ba1fd8bf464c7a61f4c254c9a6cbd9225f6b59c0362ebfee9878b2c1b89324004a57dd68ca0ea6ae6a33c8e5602c1182f5d602e203395ee60c186e38391c3be5459ac0322a083ab8f5f265021b6a0265060164aec1a484f8bf2d55f794aa0796386027b6433fe0b1c6b7b960be8ba4504ab0fe57e7a240c05f0e2fdc98f3038e7943e59484de333887290ed6daad0ff062dc261bb90a9dcd16450ae902715ac3f3379de529735bc20d903b7b85ad15eaeb74ff9c15b1f026a07b53d95f34238204a73493c9de1130e47b80a9ab1314fd13f701f1094cfee038eb9f9630f441aa0c9d55028a12bd8e2253e0634b510ac2031faa190e6aaa4af991ba40f942cc90bb938801d64f71e20a33e1f4a2f7fe899ccec1803e83f4489ae3bf3464ee1bd7ec37ef735eac1d07fce9bea11020e03643aaf8a4e13f7f7a307d577a44a5257ec1d12b337047285e7ce803b45d32840f8a571304bd31733ae74642af71e3bce1c7efcb42acad1c22b20727b8541a99b90f6e2b5a25485483e3261a08c1da1cf4913a4b5fbede2cc0ddbcdd2edbcecd667625cd6f14a539313defe3165ca6a088a7098cdb2ac8b0974489a0948b76973663ef85359ed65c8cc1015281fd7186c370b919a2be6351b19bc00766ddcdc87ec6db23aca7a3344640bf4ce44a2ee3c0478b30d3c6e0a59c71bc313598b31fda2d39e303f669ae57d9380367ea2b0d8664573b45c2455194598427d8c144d8dff15b40bb3d684401a8036867e0bc4a2a5e89a29736d1edc791f0083de1cfa03bf0a0e2cbfd84497cd40df1a64cabc840f00bd7e3f5f5a01a5d1d64a012544062f12f89f1a130325029a17e8dfb8c5a6451e97190f803d735765b1ab7072fce7cbfbc62c37a4dafb5a6e3e3844b2c2c186039b4c66a8e18858eb2a38f0a9a2f6f383a950e749a6da415a1eb10e83e0cc1af9efd5001daa3f859e05c32781def749982e18631858c1e845cf8de6c43f3f7128ea510d149e92d3088122cf9e897da8c46b6b1856278930558297b5e213d0510b731d25294457208ee3b98eaf224ff6fa242b3a338f9beb5031eb150d191d0c16fb7064e133c4125cbfa5fa9e10ed42018b4d49810579aad5a7d678613943b5c64dd54da1a9cc54016077159bb9cbcb81a2802aa1ef0eb5a05551c7360daf51f5bfc40919e2b7f2501b89a3a92f9b219ef53fc35b75040ab4ff6eb88a6b83baef6c11730438658d05318a81811a96ac324608bd23c07d92578281fe0938268f9f7c219423f4336883d528c511965e0e162f75045730d1507f0c4ed0624a4818e9943d53792dea0fdeae805957b4de45d511c0b630dad14d39e319389ae8fa807a450a2cfb0594a868236dff5c57d50394874ced3523327074fd105ba4d3d41fa1ca28989dfa73e4c42893503c37283aa7345fd3be29f35b5ddd2d5411a998684184650d2e976a4db40099ea53bad423fa5bfbd96e0459a57367cef5a3c26a70e6e00f2114e9944787f6e688ce46d5fac0b51b35c28eafa722a08fe618a7828663d430391c386e66ac92830418d8542e54e89d62cb5b9d38a33fd07225e233e4038266a4452559980a702a241e674e433770818940a77836eb2aa35df7447dee2e2a456ac82674065d7207eb50dffa23c549940da1b2bef74c196e5cd3d696a2fe6951d824e69386c0904b9755803ad3c0a9d71688b27edd12236e92d9f58291431600b5a96ec58a064811e76fc81c8bbcd71ad9b26fea607abd7cd44accb4cc9689a84216354df9ec9a255f3aef8f2047aa3d37df42bee02e043675056ccf5854f09697da6fba3d581cf4a10346fb4611a7a92b18a761ccd51bccc338b544de02dee76cd7a443c34f7d9bd7774172ad8e5271755cbe88d0fa6cf196de06d42a9f18fc086924856ae8c5072763001e4e21f6245bb9196fa23200e20ba106d544842a26c6c3ea9af7bd895cb611d8c92d3975d10c39bebd093089728bb0e48de6334132c37a83227e2e0cba7b2f6d2e2a5bdbe652942a11d181cea5c21b06d0208ab3e7aa356f625b498851588093e252b8884ace894e048be21870c9d24af21cea1155f26b27d60be8e5c12db8b6a59a2d8d4390691af42c2bec341d2afd081299b6d9fb73b2d3a976906124339ea950f4e9e41dff8e469263b27990ad9c247a6a70c720b07fc037a0a62ad642f562c48914ffbdb94ca6ad409cf795eba607a51156082d79267922c11881b9552d15ee30503d4c48673e1e8b98e1ad06adf6e28f1a4a70dbd44ab6f02d8e115fd1b442d725b8db08163f75b21eb4f103bcb96050eb5cedf7977bba3d35bdb3095f870c64985a6cd0f63df567610914b7b19ef625a6a7d919632092e23e2c64f6e4e2c9e24ef25b72124a47a6675b353762d34ebbc0c9381bf75628690250d3a4189b258a4efcb1750bb6f7a5c74626ea8b035e87a57fc757c4a90aee841606c2235972dfafec93f348a1f462bc8311f525b85639739f97d455739051a8769df433022016484cdc0445a2451017eebfdb2a8c197f485e72270755320b71cf10fa2277f7629e5f1b72db735168699890e210c558fb07a342fbae09b65f820d340a6472729679678d1e13982d3e603fa97bea183fe1339728fb7e6d9f21ee6a01a5c8b296dd8e6fe6d2bec3419a21df90196a8a9a5052ae594cfe156a06ae52bf0060569cb3380d4c71eb7c65bf83648d054d6c6cbbc6b50ca3e178aab633a4653746a868b1b9178008dcfb0344f18ab1d7e63205a03d525aef820c700641dbb3009e100eb386ade95834962d938901295785a4af3c95a2bf16840cca84f54642719b4b23463407922e3b05a302c3b5373e11e8d14a6c762a6a8d90c5d7a52d495f7fda1d6dcabafeef0f88cefc6fc51e8d96dc14d09549ca53728013ed058d88ba56e2dc9790cb4a256982b9a52d2a04dac22376e9422365a2bcf6b77f91244e92aecf58386ac4e823642fb31c91adbb79561fd6cee60a5c6a1130ceb62e6d5ad611ee6a169386da93458d00b64e22d4637308527af4ec82d7f1b4115c82ed32315e99ae409e39a0a16e8f3d9ee608f8bc7136eea94e86067e8f7cc49244948db8381e36b2c68c5898c18e6a5c3f529541c3963071deb19dc3aee04e213749e7a7145d770b66eabe54133302b502bf64f411c42c9c9f848bbb93b0f0849ade1b5c1d2cad3643d54328acc77bd0addf4c133d566ef829e58be06115e0efc18edd21bfcb0b12eb6390648ab7e4c1e08297a21fcb352c99405c89bd18d3b2f857237808c1128115af6ab7ec5e32af85e8162aa70a572f09a01003c121b6e96d4893dc1d5745dd3eff84b23a50a872ab51fcd48713676361c227ad4a31e41105e8579605a016526ed4f12f265d3653e5eb2027aa85124072cd8a0acd01401a48112aecb305c62c5be6fa97c535a78b95e46d44011b723b08fda34292b75286416a918c7163cc5c3bda8d4bf7370b8067f266e5569ca4b065b2c99851060e80b91f18762897748a17075816eff00de761d1c26f8e2d32fb0b294885a47164b2986c8f2a919de41d110987468f543e7052606b3280090d0736ae857facab96674eda3f13d2382ee279a792cc868253c0c90c84245ff8c4d468b72b6656066e5e2f2fd2f49ab32a0fba95b2197fa05a0070b686201fadbef8fb3ab8d4684498a32a5284cd752cef61c0f011969c451d36175fa420cb8e52dbce24d143fa6865e992970b63c0174b05d1460571105a22272898f0c089c52510c3891dfc40f68b8d881189f2b7613054444f3db1aa537b8b3a7b220eb7c750ebb19c19e08344fb8648c3a57accc9af689c9fa3878e1386119b81872c3ce9dd1e1dd22e618ab479c8768774d2c44bdd2eca43c7ea0046e6d3415632f51742a974c093f34a35d2046ccb170a6214c0c95e2328c120da8134488eca20c67f1ab28f5fde74852da2c2b8d0a247bee1356917bba66acedd6cf5a852045c87c96592044c86063f923c9cee41ae43696d8365f833bc17a0049803c2ac6d1e1533083377a21a2ffb81da5b356a4dcc8333297e3386fca65c90b365f503d484566d3e1d809be15d5e96cb0c38df8a863e5fcb541663090343561c4b3c977edf5a9fc579e6042ee75c2c60e0d495dd656d4c415c155fd6c4d868c448fca9dd3c9db4d64d4470ab9c54b1e53f8ceb9096ea04a8706fe94675911cb5e698a0b2df0f08cae791b53f7b6bdc78882dfdb28bc563aa9d690e245a54fe7cd6a7021b0795d22d67e3a7599d9d945950591318eaaed440cdac3a7e3850359358d6582d46fa4582c8ac6938e4ec0f6f21cb2e43be1179ad729163267c633203fdfd1e4200128cc8403ebc0b03a756f270548e88ea893dcdc23b25817b646a3ed0312ef497c37511b56dfc314fa8de95e90685a1e8ac2b2609694187d4664e711c2de6719c15b8ee64e53ee1fd366151dc6d4f728d12a9ca38325e5296dc828db4759610cf5d74abd0b24209abbc5be88f90ce450719ae2fe92c30e35e28d38c1f001405025cbfac6dfa44ccc7a1a8bd1c9714d8a748ec65e3ba60679c59051c7cc9e15e2e044aa5250a040faddcec45105a6ed0509c211fde6f746eafaafbf1f2db046b98160132d10af77fceeeea1282d32bd49ac3878e6023956ad4a9354e0104291a34a014ab919d708dcf24f18f715600a53294b2a271cf4d4f2c3969f30cc80562c859113d83638300bbc21b686e582501c158b482981bb148045729badc8179838f43b0dedc3e89e3839faf1d895215d240d0c4fb0371ec17d220ff7cf21f0e463042723e929a5b785987f51cf559445622eb59c40521f4b8d7d9fe0441f9cbb3228adfa6d5994be466d53e50a041283e204c1f900b782526b0edeb83c07f932659e4ff011b189c401d3e16a9de9809cbfc80f46cc60b08a22425976f003fcb2bf27581fb91f00e827d472104230e99630e1353220abeb3344b7659ca3882931a8cd715fc3b827d201af53596b8b0316e1243ee4e661067b5922706338ae231674880f7852bca167f473dc4df0bb7cffacb32cab4631cca535ab5c33fddf7d0eacd25690628074a63d8b13de6f95c378d00104304c27c95447c1ba68e51e1218be0125ca33b841cc0472cfd8c33118fe26e295814e65d3cf8fe284c7088ffda0568fe8507510f426e34439729eb5783827813229420104051bff2411e9973e3e383f92d6efc726ff16b3e2f859450994d361252439e3416c4856c8ef6d15b0cb652a02c05edd9993aa39fb7220f6c8534967e3f4abd3f803bc371b7cdaf4c9ed22ca5b67753bb6855a321e251b55e136afb5e8837453a738e25a5ae437bf19a25281c2b1dba55768e5d20ce96f6b9e4234d20a87969588156f61868e24e143b9326c199a73fb75629b29f93c0b9798f8a1160790a322778723aa24b83273ca8988141e2b26dad560ed59db88424366d9e8424fc04f8fca0caa44ce5d5e1841f30f1ce3840c454524fa48eab769da6d9e125aa7a5663e6a5cc7256e1f88ab09b321c202c52fd3cd6abf64e0151b3d2ce5095683c8867feedbe21c8462c419cf04982eb24ec379df46dfc6542067ab208e3c0d7926ef283d6437017d860cae421bb5841b114526f368dbbbfcdcd094189c670a105e56d52dc9c47e76ab697612ec2ad9082b1263d7dd0b56b206c00dee7aefc0a7610d091c06a34a93f323374467aac15526e0c6c007f47add3ca1c38c46b2eab23deeca15171e4fc2f73d2fa21f98ddd3659b5ea2a60453a48dcb4c497268bfd012dabd00330657bea37ac5e10770d26201e26c788146108c8ed6bc3b90f94266929f6f775f8c0fa6ab82d901332421a4274b81150a8b8728ad7c75ef421a1886bc8c5cfddf6440696b4a0442286fe6e1c7e84dcb107c5206b8614fa264e011bb6e572e4cdff6bd8544e7121d2484bb806124fc9543e5c8fd77f00084decf21a9d3bd13fa5a42455e74e6fe5c2bdc7d9ab61b4acea6dd885e31c51a6f9d0ad363d74c88d9f056570cd6f30ea70d7787551029ce1dde8fca9c1a8b1cc3c4e3a84d23f23103fc4201bedd68775b40ea522b4730b119ca786a571808ce60b69ccf5d89332247660ac9650d7dee483c1d13e5c40052c1d5a30c86b8b7939ca12c6d887627c6062e83640dcb4fd0593343502d26104691474b9caf8e00dd5e4544803ebc6d6bd01fdca27389483cf97b5b2ec2ea552657903a578144177687b3761a4a21e9fe89268160d0466a168058aca24d49e16997e5ff6edddd72851c8cc698004b739a130a42ddc0985c6513a1d4303f8e488f534086d223d2443b9c2861ecfe2543c3a1a059bf52180d53a5ab5bdacb9fcf8464f0bc38e6cdccf9c1f25a80de669ceffc260e2ffc22e225f4389a5de085ddbb888f7a1a17244b62c32474d04e545b05702f028d254f44817d0f47de0596baa6db4f995af179aa90d601dd571a1111f6afac42fca8bd93a303085f73d9335f90cd988796702e9c10f3ba4916eb80114041b45a0eaf6957c0efe2a2373c9bcb87d1f9935827299f85fc6843782c4999941ccaa08b70bd3bc939d49fa306d406acf753911ab3e1ae3057a984b19e807f8e261e0afd9f6e7b8527fe8d30c19d0dd85a4fb0f8537620805e2f3ee4c61dea3f6cd58f52f99b2e28e3d8cea610c90a019adb847bfad4f6e933bdc3769cfec627d8e60c8e415b22d1500daacbbc8b007c41600e6f5b2e648cbaad9073589185c42b76dd739cd420198380c856d00305700e30cb10db99c17afe5e452c03dfe27e7cab2fe0f0ae5cedf11a663c69b486325503d631f21a720f4f25ed7c00b0f28179cb8361fcaebd12680b32a29cc191b0b4274b5815a711deeba0e0697ec1074684d87edce403871ae5a0e9df1013711a1bbce6910917901cf2b3d674e8427f477a9f446028878d026da5d812d78ce851951455aea96bbc581e18b69524ea4ae391da385c22b74d8d4eaf16c569365482907fa9388e5b2550e46ab17e8b6794f9b0978c56d51d77f27885294ccdb7da0f0aae27463e38552c9226a3344d5a0d4dde860aadfd38e4a54423c8a467e7c90953e96ce7de969cb32fdba4b251aeb0fc39f5012d50076ac527db2a9a5a804308e9f94071a06652573d0930d08d5fba0fc597ab6f4133a0e92461c0601186b27e300d4bf4897deda915e85edae1035ff8127e210540e2d11d5d6e1a66d1cef6a95b17281ac71bbc3d45b227bd1ea5515dfdcf43c61d36cb42ec686177ebb61129230c9a49814d35e04530808850dc5be1f39501d7c31551c0c98f2db5affe2ce07b84634a99c5f00f33d4f6f47db05802276fefa8d80625523fce3be2ddc6eb2c207dc1e0580c54bc475c952dc7d4f45fd6255ec878e3825055464c5d5f542bb1bd46db9a99aecba7f4bfa289375f74e4e41e6167c4ee04b4ba6c9d3b0b50bb6abae4311044fe88a5514a7334e8f1b94bf18ce0abcf1eda78ba7669bdb9b1ea1690629c164cd0b3f6b1c9faf4726df5c9a7f8ab7e2b46ce1e264e4bee05a0d6c76c4979d328c97262d7df052e06eddc373f48eb3c94e3ea7d5f55cb4c6794ec5762acdfb6d3c981893335b147b18783f1fa2d0728a6cbac0a55b15eb189d50eab223e0ba3ffec60f0ea296483cdf8f03dad3eb1ef28d69bd14d939d02ce9d36e324f8d396be870b13b349644f1c05e9affebd40aa656a18d17b8359697dc4c79c432b2b862261b743701551ed88c5a37f91bb82e5494bc2d5aae14fb6186085dd206a0d1ed3ef6a0c7956dabd7006d293b5f2097b72611458d956022a163170c34196cb253985586332c91cb10933289db00269a9606b71d4f5cb2be3de048779b83a6d32c0c0b11d202d25066a285215d59d92c90e40d0f19448fa50a143a3f006a28c89931aeaa637bb43c6524b2b854e92d0fd51f3915c5dc41ff5c84b777615ceacec8a8d6bfd9416698714dc4824a3dce93db181637f34faccf3464e91275e045fdfc23f17d8ca845d3a605f33684afe2bdfe62cd0a8f98e84a3fe3e1b067695f6a042e19753af1a89c1b8e2866b87be775fb87c5b5a1191c3b04de32f4f23ec33730058e259eca350c3e45c5186f8a23641bf08d65c2106f3a2c2d926f99f7326cef97aa29d43dc70036a28d44ec7e74fb2c41c498f041acf52546ccb4b67d7403a430c9b07b399ff314c106ff4a90629fa4bc59612f1e2e837e0fe465f2ef61cb75751df5dbf812640c945f953e862f5c44a167f6c287e0b4f5e87c0b2482a57ab141ac9dc925bb96e1032a4559b96ea217a904c6f71c57c5867b77c65dc898db2d4980a3fb10bfc515aca7728bbde27900f819985fe34476c141da5e28781e5ca07da77e0fe40521fbb658b2dc0be222c59bac7cc6ab669845c01e3818979f1d5d58871f6bd9af6c0338bb6f256131549a7bdf0888047624ae948159f78604526cefe093b98a4db4de044f580511bebdb89579dbe49f88a55e1137a1ae680adfea2b7a5941743df7159f89f64c926bfc38912bd00494c4f9e9fbbb64af38dc260efa0529199dd0748774944f9a4cde52a61325e4a76565f11293aaeb0fcd78dd0a85fcc5bc649914183bb77d2d7812cc33c0444fd598a49cc7393d55b211934c1b775301d360a6177770932e2cce529a33490cb0874d83743830f899493a1add68edc654de0ca0326cdb0e92457f84471588b6ac07af09128aa300ddd02793007e0e1b2cfc5b041403a104e38ea327809811deb89a087a15ec8f28303ad6a837d7684df5ce9a0c99d88e5bcb0a4b076a92eaff44453e9821e2479c0d1ea44d1a4e6aca78991ce1db06276a86958c7d74875e082de81ec64212dca90df24794a2086b17353ef40f442ad1779d117a6c97c01e3542e32f0edc2864c6874f886afed1c19953e167b90a5f99bba7b5ef9817b641ea2f17e5726a6c4a588704fb48eafebe6590f2da24c5659bc9789df17bd99402316ed35b389cb64f26fb4b99c9ee6612aaa09d7d2c9a17795411076e35910707e0578dbe47ac086a803c123523ab0d61cb48a5c6f9219bf2496abf4f259662341ba72ce84eb955d670d55a6c583eae5ef947578112e23cf125e12b5e4d9ff62454faa27d8efdc93ac33bfbe8dc9a7a0da102e2fec79fdc53ee918dae56a9706506b5605f6e2a695bf6ef7989d3de94cd9fcb54c9597dfdbcbd652596bd9383146994f0dc685e1943ff89fbf2e84b36caaa42a56337ce3366aef26969e9c4d57859dc6f776af984feb503055532479a3c5965ea5f98a871a14f8b434155de025815fce907af4990ebf8a7947f012b6b5bf9f26ab8522bc4512e940a93ee405a21b3fe426f0ecddea96dabd2a24e5724c29eb5d8a6778f3e28008f08f9f30c995e2df14121599faec216b2ea8186a6e590046ee929519ff715a044dada1dcaa11e0d64cfdbde1ee7043f9ef0e31155e5f9943da319ece37ec4ea8f5a6aa257df035ae571ccb846ab7c3da5cc83db384df0db2c52c42872ad01cf8a341cde0a85fe84df1e2447940012b0aaaba045fba7f7b43f1ba6f991c47f2dbad4efc4aad980086871db16527d3527f999c40c03a4201c7501121917b19ce419f84e4464083eca618007626e07938ada6a54121cb5ce8e6a7c4495db2fa13636ae84ea0ffc66cd674c56a921a8157e9880e8a112b8e2463931b0b6fd8bcb7c4ce5c41630ea60661346a1a4f5b77ba7c5a78ba216f2658f366917a29938e3f912f6f19d52f83cac79d5c08c6e11781635ec2e10966d34bc091c6a28eb98a30aa1aeec99e7da40a98fc00d7dd23193c5e0d1b38437291563442ac43a4797b1be5e06493da831f59ff81cb643b88ae5c2a543f1f38a1e16be90130d67e81668456b5011f41070fc476c31cd02e1259c6617576e06cd9a4ca27a3331b885dbc6a60106694646583a1a872ab51b2d4b1c1e34b4e712c5420692651e1f523d34643867e4be56059b238bdcd20e21c0871d67af2554bb1eb50fa9805bb4e8c27abc2c48c6a4ba12204c45d394d0616abfd291c94682dcea560daa8347c1802b2c47e884b5fc6c1638f68b76a3e1dfb2b2ec5ba4ab319289d1ad92bbd2f48baf8ef609b8398659857c31128a4819c5aa66f448064e75ced79084f72f0e6cf1fedfc0d9b230e2130e6af7ea6e7f20aaa61091ea7bdc4d13ff9d07a94f0fa86e8b2c12af7b772dd182b25bb26f409d6d8ffcc34d3533e035c0ddcd649fe2e96d56c1db1a028de26e1aedc384833ddb7ce6e8e6acacf8b081d436a68cf6be761d31b11550e10d57cd9346299eba58c7393f82179d40619d9017c32749bc6169a6be2c96679c9df0a3dfe7b0b860ce749f80ba094a72824a84309e99fae29b11562114b13235ed32fb9f4ef189ec5406e26ae08a8be363eb464d3aee5b25428a887ce36f014230213cd8799dadc63b81dfc214e6edd830c425d427d393662c09976746b2981c265d0a16a84ddc28c4ba3b7b308ed440e39ccd29b92db94833fe4bacb3cf8f124fcb56defa4e8ec0689b255abe8685b424b11c57b52887ef8a75ad312bd399d640fbcf18a6b2b734cb30137a0497beada06c0620cda06729060ab15c90d600fa0243a33e418f5c77f1e9b73cbd73798463cdac7399b27bfeb1ea9347b979dde900e3a1d0bde76e6ff58bd0ee093b41bee6b9f3cbe478693d8568db14699622d9a52d395435b6da18d95bc520dd154d4f6cbc3c2934bbff7efa5152c41009f45962b88a9d6a93cb4271f94826ca03bc7fb5e11d6653cb20b3fed44de84606657356f0f89a8b2befbf4e20b34f2581006c07ce0a75bb794e07ca99638a390c897b33a82678feeb88af5eb7409ad1accc594d4f91b315456e217e9d9c54c8a385c6baa22df614da9de1c7c1535fc48a70c47263d5bc388fec29e4b51b3805dc2a09c031c51884be2afba06386ef4130fd9fcfa88bd8d86e5008bfb8ea2d4a26a2559041f988465dfe14d020e04a123223ba232f85760d2308eebf04346b411119b6e64124a64ddc0645cd8acd6b99d70c09587b2d445873256ac92ca3eb5434d8eb8fd0bfefff72919f9aea6b869b9ef9f16b8a991c5b60af64667df7976eeedf0fb57d1abf4cbed64cfbfe3c2f78c1549c909349fd83b9b06d19b46429040d2ffcfd260c559716adcdf7b9c4c2fa777e4e0b18484c417e611e482eca2b32349e7a4092e037a84ba1b2557f5d29515ecfe8036386aa4c0c5048c5c7b9d36d79598b98441a8cbcab320a3286cfbe7707f80fd6783a25294a43151054d387df22aa89a4a9c075b90fea908750e3503450dd123fc69a4fc1a5c4a880e9bface41267c88cff61a0e3dbace4126728e31f3b04747978b4f01f0cdd978b6bf489f1ec58fce26f497c97c9436b25c2108a2c4da12cfe4634c7d60a8ca1165b244de5f1aba902e8af5249a9086fcf4d1eca2f27d5300e12c9083ad7b62029d237090d0994e8dc1098726b80cb72a8fb114b8cfbac298380d63a41ef9e40356262f3df97cd0337bed56a2e71868cf7ca98f0bed4fe4aea065bf2734dfea85a9eba435c20465c638f1114bdd00bac93d543f929db09a24bb954a86fab7260451b69659f04025009f8b022d220ea07b09e24a37d14f616a9a4f12a1c5049c2fb5cfccf4f212cb6491c41c4859b010127c0380a165b0308df13421ac4f200230c7c04d7050ea830a1287597eee14ee8bd59d95a2f0f97efad560c6cd2a26661efcdf9a1e28f8a5894a07e92608821c7e7496f05ca14e06c7c843261b23c732314e20e79f98842ed3bded9b419ed381bf8c4b976f3842c48fe75b6142ba8c5c0fce33d3b428b18660b1ff828af4cc288605d7986e68d15134fe85eeabc4c3d09be579e0eed24beef8f8602168fe35f53d104f13558758cc17782425ccec7f8e7e9dec73210274ff4b7a888c348fdc5e107e4bf415b70f1e54d3e6d4e1c0dc8bb6f6dc6923ef3c40e2efd694e46bd75899a7e42873cb04b4c7cffffa9d090f3c9d7b118fdf3f61df569dd93ec4cd00238bed1855278ea154acfbd96f8776803399e4d1b1088c6a7d023500e58fceab1261fc34665effa240046bca4e77d9116445e161b2b8e007a9ed1455d5e51e7fc0c35496ccf1575d2587e837b3622f3a7c7f0ff674aa3e5c54d969bb3253a03f1c33a9b0b3b95f6a8c02559810ac394c4b0dc5f1dcdc5c9a7b2b5f3e57b432258e5b50d4f4c465676b3e7a151b8119fbfdf76b3068830d027b1b530bcb247b2f8f4a206e14bfb54a9c3e28b992a3c50c419f245723975c39f5011bf460965317bdacfa28966a3c4ace9e1f3820782114a7b5e9ea202e56fc55685cbb7acce3960081ac7c35414989a18b148bdadfca98f8b1f986a3c95675c549a17d5cc2252c0324444629334aa05e80f5c4ee641ebf95b814eb1460c500c34682d4ad279c1cec1f80fc5f94e91a45824729557519049ec21cf9c014fcfada74a843b637b38bfbd05a16b221af969f10724eea4844c6c246d33c089c3570905c99b9e7e93299ccaae4ccb9e53a94e238c813e20903acac47a6a097e36d67b18a5be85212c446d2edfe2662d80490c63c0f070cdaa752ea0b3285015fb245e04be38146f2720f506e3b0419f37a213490fd325b9b1fe81d2c7edb9e076ded7f6243415ecb10bd9d5fe5dfd3c956feb6b74b9e1f5e8802b0a08ac012a1ed241a72bec9b8ae2f6298b592adcc002c11436f2c56bc102985a07988859706b90ca5b6342511f8fcb296ec54e1907c8ff3b6b8c1981e39902ea520d9b9aa07edc428f13a3380ca7afa609fdae2b4a14dc760a49a68c2bfc965d156dd65727dad6826d72de8eeb486884296af63ec0925150fbe0cdc95e2162e5218c057a8fa5408c12313f66e292593f7cfff7866e7ead98bd2909eb2364de462be74b6c830bff4021c3f80aea93eb00438d033f969d5aba51432b13023e188a98fb2a519911c15f0e192ecad3882f8bc06217e3957d191b0e1b4eb578b9ae4453dbc7001d95c4244dc249e4ee5dde30ab2e1a0abf4492bf0868f1eaa8426aedc52902fdd9252d576a88a0fbed37efe23d3f23735ff61fcb9dbf3956efa443af262d8abafd5032780fed2fbfbd6c9eea8dd470d9ddf1e7b3bf6bbe0a646dbb7342d264a5332e05e56d61db969b12fed1431cb50afc68cad171485cec25c4cd84116c3cfc3ba7f9752e3f9850fb86d0333a16c54ce8a8ae2d4d08daac026787fedf473f431c2f3f476b83759b5c54141970416cb9728a9283387db216e390b3427c26e25351c13304f41adee1bf7d7f9a5b8d99ab27dacd113acc19d411000b88c50c61fd94283b80ed67cdf2df249366451d8afc7987c4b611c75a1c43ceca1dd49f3c6f37904a582d3c8cf03be63843df5297ba4c79cc38d0136da4bc427cec54f300838b841b8ead5fc07403276b34d1a31cc481558fd2a0a201d30572eac84c2916070bd8a85478fa86d4d08e8e2ec94b16d7c8023c013d2e576f3c3b1a685151528bb8d11080da02c0372d15cfbbc65459bcc20f59a093c96d823cfa93ec5d3320f61ddcd9fe8b9f3bb1ef2239caca74dc3c1bdd4e7655108e9b9a5ec6398ca81e80abeb354644068ea99a43004c73857236621f27e7000e4e545f0815103ffbf9654c7d90a69e1f437bfa4c6682493dde7a5ebf793c94a5a1ef896dc647e3d842c75f482f717abbd71fcb13b90f32569534d8f282f3a12d32d70bf6828c9b4f836e2158524e52c49ec3b6324b61a4db12c2dae8cab14fa73ce37997d07fbc28f55e23a675fc436882b92dd8e7226ed512d42fe8ad912e19f1004ff484d1209d3dc013dfc6b0466c35ef7df98e84f4d760292cf40525155e75eb6d72610afd30e618169c2671ec698d036d6a49d38aa3293854f638fb0af6f08aa2df5350fe14cf9396aa4917fce0207f38a840592d3ee8899527032ba1d147452f5879cfb0652601eccf8fa212f57bd8572e9cc04548e3e78f9feba71b259259302842a0fe28343cf927afb82fbaa1db7fad65d3e09b16c1ce8f62443dabb7b8fe8e5fbddf53e673a6e09eaa9460c97586bd1b4630fb42d3172624e3c0fe47019c8bc26cd03fdbb867c688b60e87b820b4eac6427a70ae8551cf2115451f886002a6877844cc101cb5867cfeb25787c1be75ef0da87f1109851de3d7749448f9d233b70b079a9d956693ba1539dd884907a52bbf6c18af0855d8cfa555a7df1eda1251932a31ef42595f6148db63000e5900c7cfc504dbdd95bbc280a286993b156ee66197b0aa2ddbb665188d3877b0be2b6c7dc391ec3962518da3c404b68e4e993a421288862564aaedb335a4e0d4de238b0d0d021d66a9fa516328d41618a4f41aa406148f59735dd7a42890f61ef5fbab91c1ae217dda01ad441510989cfc7adc6b4915453a74357123bc7c35b5d182253840192ced6c711c8c15e7b56a840e5d40c468ab4114473b38c1d1aadc9a4802b1f81837e3440e5672decabc19a17df22135317f9f8c09e3f6a4ff0a921028a643697cddba4d748211e8c96592c38916062379bd7b5322e495dc762400a8a8d818d7a2bbfb52aead4e3535a7760a55998aae4421cfd5497c62032ff545c5aa8d8aba612985f406ebdd8a31543e05ce8a8e630e4057a03186e5b2a89017f4eabd4f5e848aa83c312eae838c962a1e7e0434d9bc43e1708930f2a5686505a8ea0ff56ec3d87309782bf507cc57025a0bf66d5e30d0399b36fbc90598330a2813eda17b2cd61aedeb3f85387031d39af7b59f5d80732cd449536f24fbfb11f2389c7870904af58123461707880200203410e4604827b3e230b3d865be46f17ac0d028f2d4b977ab43ac9125462bbbbbbb779e07e1066906403748b202e6b886ba349473ce46d52673ce19a8d5ca25b888720a504495199884f0b090e080e5a6438f0eba70214aa18a0abb1f2596d0d50084d5c383188f196c489204103a254a14d1c1698ba089aa2b2a5d50a071c4eac895160e20a003054c7634e520c921861b1841e281e24ac81e81f4fadacffcdfe8995c88a21821830b3e5926247154431116d208227a00b19b1284d00e48b8b01b7a0f78eca9671d3f1246961bef041884aec0b024e4065725e9f8869dc739e71c0413b5c927a9a90b23881eb86327c82804211e947e24211464c7952147824c189a3a0c501ce71bf5eae64a81c40ec6187ff8c3b320164259b48f910f928f92cf924f13a8d47375c601cbe9e6c4e324e484a3a1b5d65ad3d859425103151d3e544574d06075c4902074c4f4d01fa0911232be58e3aa4dea9d1cbd63a726e79c791c519bbce191a479932a792469017ded67be7e8d1da236495474f43b46a6121a39b8f028a9728a322247dd41d11bb29aa0216946124ba87c48e2724281cf788c735b0b3ea67e100114c5c34d2ee2ee762acca81c989002d0f7e5686a01551f8acc9b1684f60f5e8cee47161d49b87a75a8728d9c1dac33cedfcd39e71894aa4d6aecc3673cc67905ba596dd9b163484806266e30114373891b4f2bc7508dc6f5c118635eb5496d430e893d9210494108a8188690210909fc44eeea972784b0d1a27aba375aad6d948b08b55a32871f285d74f8c03aa2a367a3e9efbdf7eec0519bcc69237eb30369f6540248b1f3b59ff91f40902e3abe0f4915a21c2d3da0f840e2260be1020ad9051bf4b08801a8060da52251928eac283159f5a04a461688c20061e9a173c2b2854a95254335f54ad3d557aebc2411ffdacf7c8fccd945e188ba5a5243851a5c7835828435900049d119e79b73ce50d526f5073e3defb45a39e7dcf338becf06c38ec49a56b7dbedc6944b2c2849ac09001c7f33ce39e79cab9e7ace97e39c735e8107861250b86c71f2c4d2122a4bfc008d94d0035df293fb98a7a5a7a627a83b93cdf8e1331ee31c07f5fcbcbdc105251e8355695dd1d9d139e79c7394a228475192a22489c2846b3ff33264e35e6b5bf2daa08131c6187fbd213476ec7c4dc642b728618cf18d0e7812b013f47d3f5f63b58810602203103f82340d614310447646176b8c31c6988810111c911c11232248449472cdccf5d15a6baded10143c84c4bdf7de9c546e2ad7cb55e5ae72597ae8c9074bcd71257b29c88255c291a826865801081b9830a247c617638c7196aa2fcbd5f7618cb18d5eadce10a23689cbcd309acd40fa5a8e50ee76bbe5d0425d2e19e5845268b5b0803c781c717908f1c0f1c8f130e28144021a563c1c783edc2069ea080e2f477ec86102e8410e273c61850769e787293a6868031ebe1a3b3b33fcd372408d1b4d9e7041a28cf8210987440e638cb1134cd4269fa4a63e184ef4be8fcb50c77807064518cd70840db8d4b0e506fbd003cbc118e30fd7087d1f0282e88a9f7cb176d292e46786291c4b64d91dc1a2e3fb14c063a733cedfcd3967aa6a931a5cc2e9169104908d5b0a999cda9cf7de7bbb0c519b242a3ab2bd4b92b54ce8fb32c5680ad214a5294b539ab48e7befbd3748ee04fdf0af28aa28ab282ca9dbfd1d3ee331cea54839a1621e0f5a28826aa714839193a01e9298d08465e2566d52efa31c1b2bc823a0824ce1312a62091e358878e8d85ae15600e2003b4534210429080a90149223aa72195f3c75f4a84deaa8d23aaef86c2be560a92f40559bd4435a723044647d5aad0a54936f909e9df37d2ff09cefcbc9dffdbeeffbbeeffbbecfc80e1be1612354d888153682855fb8615cc3232643079523965878c2470a5751476adc80ca2e280421c4d4a39b4149141884243185c87dd045880d4f3b90f0d8a107a585a7b9e4a7b5424ad44e091f4ea258411d55ad40afaca84df62b2ccdb172a8903c36885804fc1080c8f50100c6f8678510942055c448cf951c946e3094a31e4477fac0436d52e8c2f00177efa75320b18331c6180705d55c104aef42a9820185cf7428328431c6180785bd1e3a2e5e5f7cad6eb7dbcebdf7de7befbd513c514251b8a85c9451145294524dcfd9050e3a564ec0eaa18a0eba275156e10d514f04e1331ee39c85272d310861ca3144e5498a889754165c1056bcdbed5682b4eaf2869e9d55b7edb45a992a0917ae70e1e1049f1cb8e858219dab73ce5988276a9352533d184254fd68ad754bb7b44f2be71ca4e4de58bad17403eac6ee066fbb901383815c09619d0d82080af2ca2e00d1f9dacffccb84f47e58803288a4a8b440044815131d27dd901f2119638c31c63a45d4268f92927c373a4cdf877b723c4ca8831f3dec19070f1240010b152522a92641418cec0842e473f30307e486d60141010768435eb9f5d07060460e271e5c8448c20307255c9aa099052d4047434f922b2780e434f94c2c4520b54043951b98ecdc60271deb00a5d89139e8fb58cc9005dedd3c91c659ce39efba5c1a82eb39ef3ad852ea81e14410ba9da919a0704d8e5465242b20d5e49c3392aa8be48aafe0a187881454d8098a729399e8604a52365af86b3ff3af8444815eb05f83a5c87e244e09c9d6b1ca4076165433b758fdccd77ee6ff01365c209e562be79c6fe4661e2043cf52104c6c6c61bac14c2edce0a95b76ffba24204287179c78a2c7101a6ef212a03ce387cf788c7313acd0077456d728a35b97ebf5aa4fc54aab38ce3aa5e2b8ebc3a054e49be71d4e69e37e7981c1606062b1100794f8da4a63668c768d32fa8be3bc40beeb14075f0cd0e79c94ce0cb394d2b4a4998fb3fe5a20df3c74d9cae9e42e3d03c0032867460ad5e983548738f86d1883f6fa77cfc47aadda287e2975e812c4f3d6676869a6d66271eec429456bf46bd5664e511b7dba83fc803e9d559dfee8a2d3d9d35192867ea54d284953a3e8bce70d7e9682bf7439a1a48d7eeef26794b4c9c0fc7162995de14274fa52ce299dfef7b1d669cf80a4a10f83d6287c3e98779de27aeb6aa35fbec45dfeec13b9ae36fae19516650864566d577cd6b0b57a4bf94277f9e0b7adbda3ed35873855c58a262eb3fa0a6c1f271414082fddd22614add1bf527cfe38a1baa52d6ab5745a0af5f1a04c1461441320a434d93581fa195e1fbf26072813c0679f7d2ec941a5cbb70ee8b6a9db97da3ea72d00d0edd7d01672091226baedf65d4c2003f28594bb136ab7b485d8e94b143aa547af65c517e3185ef243c77726adb0f86c77bbd43189a14acd5c48268a2f9637909a3009b2e3315dea980cd11798087d8d098fa42d2653927c83906ebbd43129fde851823e38317d89d19a8c50af32970682931b59ddfe8cb6a09dfe8a24b9d30f6103d40605d2ed769b714375508e14d0e9e842c7e674d841c72574bbdd6e3743a40eca923ecaa74e6fae1802103d31841faec4107ec0a235cea53a9d353aed210041158a2076203c7f83692ba88d4edacc4adecc88b7c87ab3de8a33d369fee6c5a87ab59ae9f3c78bd5675ae3afb56a53ab30aa4d5daa9826ab8dde7c52f8dd5d5ca73f5f76e02e388c24bdfe0432c0d461960cf5fa16943214b0ddbeede30cbaa54949336f94eea958973c0663679d23d439a7a58dd6ced9edb6e27729a5fd499bdfeb4f1673f6fa521ce7ecb58212ffc839e3aff6ebbd3bb0e49c33fe6abf5e8c69f256ca02285f5c1b3342acdb4b6bad2fd2463f266d6aaa8d3e1d57f41a5aa33f7ba8b6734cd1e5377ea0d77fbca57c71b1ad745e6a55a8a999616e4998e95a3ef1faf847f82705065ebbd4e9a4faf842fc76a9d341a92c70da6b9208dd0d06e815003aa55eebcf6ac5b32e75ba5cdf5dea74434f6c4b70a9a16308914ecbcdd491bc49803ea7b479190069c33be813095b8c69e2cc0c13a8d01e8d4bc398f6347e106286b6fa7a50632a480c5a80e80194a344c4a774831b9fc2eee317f5b1d6278d3eba70d2421fe5d20cdd933eca9d9cea3574e2a3d78fcd6408f96124078a0d1d4d96b8a91f42b5915568d8e0e1874f4e0c4a6e6acd4e4ce8af2e754e74728f71f6c2f6399f4f3de78e1a28113d6188ab217afaac832fd9a2fee32217881c3234cb932732708b838eb7bad43db1413e3962adac5b6badad369e08a938f8ab4bdd131e98f6a484a018c5f1840655c2b7962bce89a0e232283a3a2a4f45a615bebbd43df998591cec52f784a34c4f3f7dea9e6edd5a6b676c466b5a382c89a90487994284a7d055225ccf40a1f0d88f1f294c273900f5c05174a973d24283f0982e754e55a8140fbbd43949f531d69d94f45a61d09cbbd439c5d0c12e754e387c308055ca179af9596bed6b1ab3d65a7b6fe8746b97605680ac0c5921b252f4f33ae39c73ce5aaaa84d5e5d185ab22e0c26747e1fc939add6117e216708c6187360020da0bab40588b733e531c6f887476df2874aff58f11facd9833d3c5e97371451c5a382badd6e4152ea76a0a8c09e1b357893a2a59aab6fe30c638cb18f2a1f573eb28a748a808a868a888a8aa82de7a50e837e3c6cadb5d61e10b981f669b586805008499d0a581ed0110109c9d1094961deac3ab6818e2f89e7938166f7de7b6900519ba431a46910f1d94eee927649764c1ff4960c1d11eb81a887a21e8e643f3d72c9d012a22118293d295552ae3e6729f1d8134c96cf788cf37bc3b6b3c294ed7a0cd1e307158e1fbabc80e4cab68050e4b09443530e503a35a210dd2b07638c3ffce12430e7bb1f0ca0929d03e46425cd63e23df1a47853bc1e04ae74eccc1c51603934df0d3f08ed5ca901882c2e4844386d2e069081e484548e23237c74663c8184d3015365e9581e9e1adcc4d324d48483d1949b7dce79c7c8e79cf3932b6a934fb2b40e3a5c07a0d9fb04a5a0ebe9aa787ec8f8628c31bea1065ec7584609af2a11a2875e21559bd44db81e03ec5478d25962d4a387129c50a2b365033c9484b050020d3a94d882c3b6b003e27373aa4e0ea8079b6412b404ee96038f9eae89197a605d2c4b3270408121ca094582f458016b07971582a414205c624713346e44546cc0040d296ce14036f57a8e0839f11375440b96203db5c850bd29426221a348871d26e8c822b4044b1622443b72f4421c4330c6d869ea73ea7d4e55338cf18e11b3044590b8da2129ca67882b1b94248829e829482a686abb90132364b1300e57eaee680b5811add167d16554a09a59758ad5e9a68a8935f78ab6f39c431c9dbf8f52985dc5db67e28692a5f826215a1b0af7c6e1c79aabb8d7110fdfc57ad4467f86382ddae5650db674d28b4629dd81348da479aecfd71fcccbae22d85d62ac2a6675859525045b3282e1aa4d092957043bd2b1adc5182fd68b55c5ac6257314338180e86043382e160447d84e17697312a98d9070260b7e616677f89b3871314658fed2a861ae9d6d4d46affb3d9a58d3aa906df30cfe55d2aab194fd2ecbd779524539dd6546059f17a555662ccbad6bd7cc156cfd22a8d06ba5aef0a71f6bbc218d5ca04352c065018490846eaad97214e6b05109807e98ed91f13e2749738fb8a5dc5579f899b576dbb5c91d1fd6abf1dc882058bcd79a7d1429c16c62fe5e31ffc3efe76155ffa1581a87850dc39153bcb85debbd6c76ff77d9aeadb7d3bcdd346fa4823e924ad7479ba8a4e72b1aed5e5adc0f6f1f254883290fe8738df7f18c37615cc2e1dbfb8ab15be228c31d33d9d9b6539dcd230da0b6da4bd9e8b16be6895b65bb46dc4a3d3c77b74739266fe67748b6e8feba3cfcfb451c642a72f23d4e7ab1065b581b61d8030068aae691a09576d3413b5dd1f5fffc8c1b7b40f87792e16be5d2af1f23eaaefe3a936b78a6aab6af35955db77a32d3e1fbe1cd9e29b92b57bd5e9d78fa7d36f67c5c5d9c30178c045d96d1dec0e429cfbb9c7001e6c2dee5cd111add1dfe246a2359a446df4a5eaad8ab3ce5485de2aa6257e3ba86a73a14088e959450efcdf4ec50e775b5abb429c664a9af957040243bb52d466566d1eec2aca74f0835d4515ad0b455bc468ad66573145bf221098b7345d6df3c103ec2aca3a98336eb5421c1811add1ef7b8c1f0be1f8fe2792809160b92fc4c14360d1165f4bbe909dfe072a605731d641172fc4b9bdd79669c2e5c3a0611843f78ff6ed240d7d992618aedabcbc60e91be799c184aa68d555a6559d3e7ebbaa03020a277c84e1f08f301c52c7b4512b55db7c185156db7897fa7cd006f6128ba9c2ab15598869b2dcfd6f275fec145757b21c8a1084988e6929ac2eeff6a80d0cfe70d5463f3765d443aa52ba5afa9a7a1f94ae92e56ed587ab36fb68277db99de4c36da60f693f7d38a9a54ea79a76ef83fa7ab96f476b29b691cedf948a1027d3be5dd6faeed6072260bbc4d9ad287badc265424c83e1b410b9f5f4bfdd7f53d406c6681f511b598eca72497cdc49669b69d3c6fdd4b4a52e6ddc53b2ddee7dbd4e1f94e5aa4dac8adae8c7ae8c62595c470948961b9235c51041f598a24e7f94ed98fa082b8201200441d6312d462573057fe0535987116144d4469f8310e7a3c1703bf0eff1e3bf1da4714933bfac19b0abe8ea570422f356e6af14b541f1f46544594e85a84151e7aa8d2ea236fa4b572e6a2e4f5fde2ca9d3239debe3d34f215eab6aa30f235eac6aa33f13b74cdc54d5e6f228d5869234f4b5b871b446df256e225aa39fc59da335faa1b88b9cf876148bb6d83bc87aab575f35eb63bdd57abb19f571d6e9a310bf5db5d18f113f5eb5d18f891f55b5d167118230ebbb2502e1f91b98d79baab7f6d3df5757be1ddd08d9800f373fa02f5f7c4f5f56446d609ebe2c07237eb410e7d2be1dee6d2ada423ffd5d455b804f7f5bd116f7e96b24cd932fead3d749d4661f511bfda9a95eafaa8a5e5dd12c9ad5e973108210d36d4e6ce5d7a2cdf5f9a0f85cd2cccfe2165b1fee05e6c36d028438fa0910c6b0fd7b018431623a48db46b4465f450e3e7ebb4e2f8eaad30709108a40fbf758ecdffd962825cdd8c2f579eb230cd7e97fe215b528ca72e3d7e753b04f59aed3071bb0abf839605751830fd855cc1d8b56efbea57cb182bf5cb07083ad2f85cef85a1a0f2250e97cf9a0e6000848800256b85a5dc527e385b2200782f415eef8fcf972f1f0f9f4a7fc1a23ec52d2485a6b13d789cf9fa135f9b546f1f955be94d2865b461e6e994d2b9db8758d327aad342969e4c3a0b40e593b27c5593bec90b5ce92b3d08b679d2f37ab56cd829c7376bdadcd7b089ec2a9fe5a1c9c2e586a2dfd682d8d31801ab1104e49c04896d6eee5b11027f770bead706a91248dd419efcfda7bf1bd17e3efcb3bcf40adb30ea7b45136f53975cee195f64d4941ed423b40daeda22e853166b792663ef8ed6fca39c73df7ae1bb4a0c64fdf8ad4524b69b4525a69a5b4660d6a3dc6ec1e49905616dc3b0c37155da074bd5e9c8a2ff28e327a18baf63ee2a394eaf3c11068ed8952dd026b3597122a64c24707f4a9f7ebb579af7ff951f74f04dc796881dca54b97acdea2018179575883d9cd10fd148714d0ff7af0e5f5126f7f79e997d1c110873fd5ff22ce1084dbf5bf6e07ad7e4b6d60afb97e7912669d87382fb4eff9e6a27ec1fbe52582a09f3fc8451026eac0dec6dfc06843f4abe0ef7a300c715cdbe5e2b413f4913f09b3ee7a189487a22e755159355deaa29e3a58371765bfe1188a805faf459728a37f76fd305146d3f60d8ec2377f71d7a74516cc1e3ef13eba9e84dd350eff57e812ad4bbabaa481e3df9c8638faf5c6af1f631d8fb3d80adae2f5bfc55146df210ee734991064e71f43764e9379f911682b9a701911f86bfefa5fbef532fa27bac4b105ddf5af17918ba3ee2f317c19ba5ea28cee1249d83da46d9ff901f89f38da0e6e51d2dae3972148b3adcfff7dfad3b48c6923d46cfa3aa2524c75cef90329bd2048c54d0bc57bf7f7fdfec0dcca9fcef5e9df5aeb1050ceccb0f091f7dfcf6b0dc59cf05d7b87ae300c7f8b2d5ab877185a9d979d3240903653c24a10c817b286d6ee7d70042035a8b919f25bccd91f3e9882115e503b1019ec971d7c1834146720e366ff278e32fa97814ddb6fc5164d04c59106b70eb65e861a90371b14f36f3147c66d2927c4a34363c6eda6868c9b9638ea9e3f033cad5d1cda0c715634e1af6e69b2a986135d8622bcfa47ab1115093e72f99c7efd0e08c004a82b2a5540f8f06143065c4b06167ae019b8880a555a6675390269939452b9a435e7b3d97fad56835dc8a58e23d047c0339d4629a5985e6c71c518637c29add4526a7390b4b106595deaf4faf5083ebf923075baaca26d91bf56ad6fcef8bbb4b471d6abb4c9af592fe330d2466a993e33d20a2e834f28030a332c9ff118e7980b2843cf0cce79eceb52e7e4471ff95f1f506ac9a5dd3222e0b7faaf2e0a5597ba28bbde4197ba28475def24fdbbd4451175d0ee1cde9d3716ed0c2f20bbbd17901dd3b00232f45bf942f63bc318f82da64fefeb797fa6cb88403bfd17986094d169c64f47c00f5a1104d0af38d3e54cb52580ba28a0fed2a52e0a56072568f70bfda37d2cedec327f8fa59ce95794561440cfb4da12c7ab1fb4e27d1b82407f62e0beb5bb4bf956c49fc5f15260a6e399d72f2d8d083ec28f9acf3029fd39e79cd58a24d4fa9c3ac4a1d64e7b5dd20a986df0491b6393d25aadbd17e39cb5feb49ca2fc96fc560b04f70e2595526e9c977cf0c55fe69c72665f603018f9b1584ccc8cb92e0ec2c4260e288bc9643218984cf6c26532d94be60a65b20db63e9d65f8da017c59548af6bafccaa3523ba949ebb517676db5ae746a89b3fcf4d702b7ddbbd2b95be00e5dd6e5aa74baacdc3294af5bb35daea42eff8554d4a514476ad4e5bfd0229885c12a9d309aa335f958575487deaeb0aeac9a98a4a0989a989666516e0e4d217ad5e557abdaa3eaf26f0f040aa0e2289914e0a286d6e44f4a6bb5f6628b71a5134f5aabb5f7e26c73ae74667b31ce797e2ddb6a553a5b597f5fab05823bb46158e90ce79ee19c2f6e39af74f2d0f57a71fe12c69813066361602a9d30b02d652c06058a2d65333b9b553a672f7b8aa385911f0b63ccce696d8a31618cd9e7833128a4ccca64954e9976c950a0f6f9b38f33e89396718c76c95060f7f9ba8f2ce893e6410b20285fd50a970fbea8b55fe79c73ce491bedd3f954565a5de3089d42d1da9df406ca99518504a7fe1ea4b8c7ad7df0f21be3a0c4398c61bb0e71c007b5c6693df87d21ced76ab540704e2e69429cfde0b67d8738af07c310277cd0e56a2d615788c31f7cb91ee4bb15ea075f2495d54ad885813374bdf84b0a97383b98c5d9412dce0e82e2ec604b9c1db4e2ece01667075fe2ec6028ce0e82776f7176d9c1bb5fe2ec7c5721f87cf02554016803af4b6f81fcd96548815997b4a21a223ca4b1c6fa66517dd5e5df2a6aa3b151b551f1f22f956caa4db52275f9d5a8da502aa5ba546d605ece5e7ecd551bd94b27648dcaaf38640b8a456bf425d0aceaf02cf9b48adad4db5721f9e27bf97447a7288ff628156db15f56239d21f76dfccd10fbb2dac46c2e54b54df9e2f5b2f52e51a21063aa08a3bf1c59fb6ef4c38d1a877b598ea7262925596a6dd706f96e89b283303b667fbb8ab090dfded1e5c17ed42f3f7297ae197553eb566ddff751812a24b8ab28556cfced5dc5f9cd8ec35d450a2382e13e193dfb156577ed2a5618110d971aa894cb9aec61d221743302000000c31700002810088724511ea969c87c14800c5078505e4a34508c486271300e83208642100461180641108481208002294792bb0150f8ea20842f70778afa1dfc6306e1226b6871b0b545448aff8f8bee024bba1249cc327f3c2d28f6cb4c368208f26d5bc7a3ac3e1669607f6ce913a3291fce8ebe4e087a9db5405a665132ae1c885cc826c5175bbd2249647682bf0e2b9998ce0c8a1dc63aad6d1bb4dcd39c28e8706b9f99812f6b2029738240d74bfeb815d68fd3b58db14ca91b193215951df2dc38e8aeccf88ae32eb50871736591fe1dc3f3c919be4aadcfafcd03b3b0bb6c0225fd9434481b2e345baa98405bedc09ebdcfe63cad8378cdbd0b6370698f1b698accd0bc50971c0f80346aa5a683b3ff0cebf0d2478d6db2cdfa9fa8b4ed68c5c8f78dfe3453e32773ed0b6318540a114f01e5d4349d8032f9cbe92f747b5465b454debc7ff49a65b90ef2f2fda2cb24b7f1768fd3c7a4396ec890d4afa0f444f877ec86c5e5ad172afbcc0fa3c4846931bbee2da38903e985726384ede914c08026166c4caf63980123373106155e3b71f38f5e6ca61a7e38347336a49eca9c000982702ee352659bd95beb3020938b5014070e9e291f5d51f1d8ab74ac5a0486d70388f2924e03f843c985e69502eafa99ec8fd7bd12a312b83a73024ff7cf77dca966ba179989c44e1f562784de86d60bc6cb78d6ba3afbc879bbd805f4953b6da158415c200545237ea84065b224141a2332cb40092d37220f20ee004f77b2610ab86649cc74628fa65304d6ed67179d39b6f74a54b37de36b5eec2cdde1704dfd4a604409b655eadf0625fb39f83edcee00a25ea5cb9fa3802c831bb478dc8561828fecc0f95acd69543c71be9dcb2be096e3559c1be195204ddce4be6e8780b36424d36ebfdd180e1a9739cc0f3d6d9f6b09173c69b9d2ccf4ce6c4fc41aa44c0480881149e3a2ef523f835d88b3ce54948c822329ac147ef49eed35e04478be88641b20f18a7bf9c4a4034a8a7985a8f3c0ec5a734ea423839ed389e79c4e2a40bebd62638da5737a53620bbcb2ecb22a750ecda138303e0fda3552b558879551dc65d3a25723459a678e30d7b22b754e510b378bc87b1a4715c0ce14f5c7587303669b6792335001e58bb5fb3f111a97c341c521b4699dc8102e28a650423f6d0d8d8fb0265e7509219192bd903019008d01c5ced7f468ab3c32e21d527d51109d0966d04cc31352ae097e93f6986ac09349b14fa474f4d458f581d099428a3aee32c51e43643e086df5dea5ff23cea41acd5cf92cc4917619e2e59ca25c76804b0de95ea127ca518be8d5c9d8b9d8317815d894a2397c827147ca5aa0f7d7f0a5001c2dc5e870ba4401b58095cb05106794aaa1b37dc0512a2326896930f6cc7d95725bb815cd25b727b0d2036ae5c135e2ef4389082bb77e481682ffc384613d34c123ea9a9e32aa26091b56829573b8cc68f1e7c8e042593befe42dc5a28b243c5236390b6d514936cae1f8a4255afb9f07d9e65927d7e516e57f0247666dfcea6dc703989e498b29e0509f7ec2e53e3eb5a043a919f5550a81b5793f785e2c7c93871dfe89f673d97d16f20293e60d00a1920a278e7dc9add61869234c3f38b555741d494229555d50fe73c9591fa4feb398f7066f349a5c686e3415b01d6de006e6621208f36de4a46cc3c0f780b980393efb792f5d9da957ffab0cba9001c35b8998344c74906298849311800559daedebb8e2f5be996779db89dd3b17a32f8d400ad7e27c43a3b79d4c100ac834f27e39d0a907bb891f618ac8f30856d95b3386e06de7ae442cacfe0b1dcc31b3d884d49f1bca167955c019a025c09b432fac04f6c77a0e0ba90c32590375fba06140359a68bcf33d79fdfac02de0445a5f4c491d7c001e3292334eaad03311eb4dc514023284437f32a1b85e11c7cee8fefa2ad85b6e09519ac719371b9c81def078e1ae4a26eed5ab0cdf24c2d2652c3672d2e7a2d789081ff9d386ad20ccb4737768c6049a2198eb913d7a6fce4ee583f06faa609f239de1746a5e7b6eaf38f231de498d5b12ff4f2a6066af4a7cc5dc7a1222ba221b7b90d1be79ec658ec616840ae164c3941865fbf41e54515b7cd289db3618ccdd3f9d4c987ced1c929f630ebb743e05011da61801ad561663c19d9fc9a9866b2f1d7f2acfa046dda5ddcefd1aaded7c83481620581be9decf72e79ba6e15516237ca9ece7075595f32dc6ec4c170dfeb6671f212d6d760cbb61f3448db11b99c232bb26df7ba2f6e9904ab8d5e4eb89874db3b246e51f89f76c9cf99f2c700ecd24fb183016c639d2287ed2b146244f6c47e5c601604be0b4e3210ccc90d7338f08eda9aa9baead251e8c598a65dc3a582d025b5451bc326316da97fda3b284db028f8bbec37a78e67ae947341fc963ec3f2228e43d2bf5a4dd77ceae29e0cc664a3dfb8acf04c2982622200eb07aa3c0affb1fd5584fd45a7206124be295b3fa10be883a87a96ac085497a5559c388ab6c4a9841213407d91a62bc5a8147b69a77b96c778b654f6f826c8c8c8548e683c16e991daf40df615cade8582ac73ec6393530a1932a5bac6a50d78a636ad9707f201d678d4815eb3f62c6c4107505328cb23bbbff5f4b0ba6c805f093780dd10b62b2449a45d99cc222eb0ab7245bd3561ed0ea56f1ef099897aa69c0f86dad6f639ed0d7a56240be5e1f60eb8d73ab1c8e2ce4a06c30dba9c3dfae37b1026364c51833addb02ee160acdcd05cb122ce7611f051624470694d91f1958e2c2e80fff37059da683888d5a21e29c058473ced5138b40fa8a3c20e1574b288e44adf685ea801c50f66fc9ecab049f84484015fd08614365551235793e600b61bf44a88d70aeed83d9948341a02fab3cbfec6bfb7780bb2a7941985e101929309f19008a1146a97bea0b90954a3c8d7234daec45712ed7ff1dc72e3f5eff65d8d2260994b5f6a7f48bf7d0dc4522604e7d623ce71f4b316a932e19291371967b7e01e4cff36126ea8a9f3e1999c5f24d5433f35c69b51be2dea26e68650950eb2b8d583ecf22e017659f287b8e7b53b6f4785aa3d56c03d80809c506c2684ff3d80a324f4ac3edfb7f14e47511aacc878be3a82962cd027b8e4058713204a0a1989ab3c4250fe91e645ef2819c9322a0087ad02d0b87db631f3c94ae6805102c495ea90298527b121688d1c58d50bd45791de8b4631ecc314ff162a8e6ee59bf1f4deb899f2f78a4c843bf8598ac002160ecf1b7ccb584159564268b8ee327dfa941c40b670e71c46e4e955a415508550dcec4da093aac9e2318c5d0f6a70809e383a51c8bf34ee8b9961b50c7325813f9e72c7cbd6c804f0e5c8e2ae9369216966492f0e2c793557848293f573a2fd1c008a2d8d7fc1193fbc6f49b74447586eb3c893b48f589caf9447e9382ac7ce7be0ee7e95f7c0d1f04c18e60662bf71db1cf9d39bf2c21c41987b207029ac93959c387fad74588efdd19ec577825004502768f487c88359a2cc2e19a46ae3c05de5da1e21ed9f4861406fdf0b5e9ee51b35dac7f3a5f9abe4d2f384365228df5b02e9b076fceacfe9ee1ef4729f9aa58b90f41bab64a60fdd16a17db699e3c3a7f88c2319738994a7ba73861af5423a48d197925a3c4b6709d9607f3fb2b1c853151b5defaf00b446588af5a1b15abb9c403ddc67efff8da71b872e47491afca0811e2812649536138db7390cd0a6e57ec79127ab7c0f5b4bae986d78153027b42eb4ba55260583a1ec6e7f02cfa548d488700b3a9598b72fa23215d3a95ecdc4dd712e30d9d4b65042b8eca4695853e82ba5336567df99e972e5cb6cebfb31010631d83c816b4d65f595baa2dc4a585db5ac8ce9d7ae0b668e79b50c8b50467f7df09ad3a72a63a1443a3549f3afd69f25794829b496a77a6611fa7a664e89f6812dcf17bb95e66b0546e454c07debe05a0495502e16e7e60b71dff36ab4ff7b5fd8ef1b1962fd5b7f0e6c42371e73a7693c7851c3c8301035b3c80856011813f434f9619f7f013658cfdcaddaa7070ef0b34654cdb63510eb2401437d23e49bd1766b533a8ac02d330419c847eb3a6d9610d1e77d84cbd16167663a3508e3fed8990f55f0a9873da59884d55c8ae5081ed411f93e4dbb000c257603389bce453907d5eb0631acd4fb0d297f538352e13396de6a4fcf8d1e393dccae2c32f27fc5df243bf386bf70cd8471efe8e7fd20aa5dbce51185ab8fe20924e548492275ece4c9e70265432f42dbd776a534a42fabded539ad629d079b43876fe5af177109609d50c78ea637217a90795bf6e03845ccf71325c154ac06032863130a50d7dc30262e25bc595eeb22bb5680a5308a891eb7bf06cbfc9289c130607be5ce967ff76830636f2ac3ca1352b7be7e177c311d009cda32cce9f51d4fb8ec33ed5ef2f0f7ce1d323074363eb35cf53f69973d4abaf00eca4d31e14134d86c0e97c8558e3bbe0b87699b7a68d87f20eb499abc9216ed4cea1170d267bc2112e84ace5465c6c47ad1f41025c6cd58200bba03b92902708a1101b116b6265567002bcb7bd7773435445545a7a0b1cf2b7fe1f332c78c08f876ba8a3f5e60499d21d43ab947a389c4bf44a04cd7c7f0bafe6e1edab371a3378a488030e4f4eec72b0aa40b9dfa192a0b0cfc88ec061ded11b0dc6b354f075875e9a79a99c501175f2a79ab2e15b4cdf0713c1e24d2149dea9e2f169cbefebce83b94e0f15fc4da34597f9f618ce038b9bd49273bbc43e861b91274c11139650465a427c5c6045d0dc85a46637880badda3e333b32affed1a9f276f7c4faba6d863b5a3de055e3b0a09a3023033d72e5e482886bba82a2b1484bd854a67a3fcf914e210f10b9824c820f6b27ed2911fc06f12e9449f6d6ccde4763f6e7c8da67db9e68fc2c6dd141edfcbc94a1e276ae68c830dac5d80d113965019b0a9a234cdd785ed6991c02931369d2adcc217f5190e57afe2c515a76db4b09f9906f4af046fcedde2cbaa338c3159239da2657962ee5ee3ef171a65dc62dc032b8b6cbc85475745f771e7a93e5a9bb00d48ccaa7c3b85fd6ec408f3a392634aac844d2c96eae6a1dc1adcab1314beba2f756462b26e794e8ba8d7638b1e636a781e6a006687c59c6b03f994ce68e6c64f5e9d1fd8bf5681601142e64215d0f684b253c1b350499ac6c63a38bdabb9635644d134151a609fb0dce3900a339ebc0c448d15ee3ad989369e630a7ebed34d4d1f5663ecf12a86f9cc74bbc44fb48050a848e09e0108cce411cdac9c04aee1f2442e68ae771a145a568126061ac968928ab014e77138017b4ce9a9c866bbd0328f0625e0923fc4cbd0e5d56cd90842c539610fdd286ecc4a01b073aaa39ee673ceea178fb19dcae2c6e29058b568891875418b8e2a8cce45ada98785022f9e1c994b83bc0a41893555b7eeb72e6b2d448700f1aa8e1ac3e1d87217087903bc4fb8b3f62f3855113eb6f21938c987e0330f603e53cae8311335af9712221e1fbf63b7362eda61b31927dc6e31913bd712ef5e27fdf18c57515c5aa153450a6cf0535da87f81951cf6c85d094065ae8bcb2f53e3f61983b6192abf07a80854ffd20c177dea386c64b04e637743a0ff31268d597fea63d051b579cdaed4ad1b1399d5d046d418041bdef92a99fe92becef8ae68e9aff7926d5c9d6fa9b4b8dada5613193dd71468645482b1ca9affae07a24af17f7f1bc69f6c2c189daa184686c34a5861c3b127063e00aef58a8f531388ea217465483b6c64c13f4fa2aa913ed3d0683b416f8c1274cb3620fab86ed3c771afc1ebc5b0e5472fed6b0b331101a4282fc904d6199b32e8461f084c8c619ecbdf0c00125c9a83e7e1de0de84e04ea1ed12dd56f84e95acc0c6dc56a80970de8131e40e840086e008c8a30479a0a5531428d240eb306619385984d2da7ce16e9b362dfb811efc1e800650c4f239339ace6685e4b99864c6ed6d1f0b9e439168c46ddaeadbc6f880721aefd806b5abfa06fc0e2a3fdac99cef0911b7fbba110059dda3d330d5338d0bdb52ede4ec85efbaec72976215526dafd420cc79c2f44302e33ac3a2e2bd1a13ea9c3e598a87d92b9d4245b6ec0bd6d74fba29292ca5bce83fd8556ffce1c48c324f88460e2a663de5938105c7470a27db0a5326acb13f2393893e6adc344169a7c7ba212d8eac1c9ad866bb0dbbbb1912791cb7512611770ddab0e7375c1834c0fab95e1a866fdaca018d65ebada3303663e3307e6da60f1b5f3aa79107e5f28ef3aaffb461c045e4edc4652ee546a122611b79921e5127986a5b0f1939c0d2569346de1a0637d7c2963271d739aa7c4806cb48739127dbae2dc04d885dbefddab5c4a156c16a76020e6825db92e6b3f8c6125fe8088b82b3049995d3ed5cd665a8b8d71aa42f2c096dd9319e006d129185fad6ef8a1b84f80e6d497a371013303a04bf19dbc705cdeed16953f876c5a6709416446ae42c5aa0063687135436930511c0c266bf363e74b9ac31090b54e83ab8a851f4488c19f0cf626fe767d5a8f71479b2304666f7f2afc1339d5f0f8a50c9d1bb55f991dc6510bae5760a670eb126c51e69e1b065a44631f1631288c1dbb6155e6c286f7fc4960929e4ad78a462ee8c63536a811ad8559c8ae3d223843dc009f7ea160ca9d944dc54685c580c20afbd9d7e950eb4310b69b99b5e05b92a847070138fc860d4266c9380079acb7f836e7475491992b6937ed1ba8944aaa2c3195fc75f4a3b05edc022c75ed41953b026045385c7e264920b657fadf37ae19395b20663921e5a01345f3ca00410fb8d2b8dc85974458a99160067cb0584863702aafd0bb8840101faf5e7ec430430cbae8ba2b0450db665db8e366930b577dfd7dd50dd96ef6317a18155bc2c9b9865bb14756fb3155ace4b26731d35cbd2c1d0c574751255645970c83e8b0f3fc55c53c37a39e32950130ab5b7236151f9cee457e9e49655d398b16070691b864215e87516d682cafbaf547dba0e7b4d4cc7caa053eb4b11ddc5b209059c85eb2640decabfc48f1c6a6529469597afbac30476815bb7c2f39d6acf8cc9df3a2c21f2cac0a4c68b00a66b42207edfcc050928374820a8b81b3683b811bf96cb84e7c98a13aeb548c8e19a0fb4097065c2ab613cf7b2e701397115b2c893d4770325ee0a32d43bc62a8481200fb18cbebc21d0f00f5cc5888e0cf5f8bd5e96ccaf3f376da62102435650e01653b6b380dc0fa042730acd00f2264cbe367cae190b35637aca81db547dd3478674a615de81c52e423347a158a21d0ad652ad4ff9a465ff42255989d2898e80d9dc6fb35526e0833f25b0642017c3d8e1c1c7cb1c9e07d7a9736e4adc83b612fd5de2d92d9c78c8ce4c4e8f0f4392561b2299920f313af1011d655906fba9f27e767668c69e1f0acd1d9936524f5c9a269a466b797b8701cfc63e9fe7e9c4bf4b74f7ebe544ec6f6defe1e04a10f68b87550d4b95e299f7a11e592e17262e448fbe48aa0aba3099e33c9996e1d973f594f368c267b8a524d12b221758076977c5815b9d5f780abd6e664f8c8c85b6201c42ac4e664fe5a92e49b85c3309deb6b04f1e6576598f2cfeee4e0cf1ce45127af4fe7df3f23cc92a1eaef047a994e3fc96bc5655e239779fb54a9f72e680174fb2824e90857d1a23e20f320b414986f30b1ca0d3db1b2dc47569fe6f321578ee91027c037c68d08f76d291c36bb01c3041029c72d51435a7504524a773d3bda7c2fda1990107416d08aec14f6189c700f882e77c44e7c3d55dd91167d4430e0ef05edccefa4e79aff988da656335d17c58adb9dbfa288a8a8f1a0e914a7a890c714057cfe0cd8289ea87dcbee9176ac40b974cff6695f06c7927a0d604b8b3db307ac26f1f24cb750f4dec0283d3ce15827eda7e8781bf7a4a248e7773ae025898d2083aa8919acf0481f3782411c482a7502c3a3d9aa102d8cef5ff3f7342329f944a43f8bb0790bf28c813f12fc4a8d7277e1a043b8082292c90673fb478558ec8c07f9df84ff72131b69306af87651c2ce08b1dd127f20cedc0f8fffacc45b57464ed6870cc230848be3e848ac05e7e66fe238a236dc6316fcc99096494956b09617a2d6f7a3486b5387b67a3155aa1a77612355e172d05fb19b7b3233a3eef7c6dba8308e2f2d190e9c6e50b7051d43478d94d89eccbc9324226988ce5023cddc1e98cc0a144196f5f414ea09256425aaf84726a0997e08cce1c3731435237b6f7328558714fdb6c34cae11594053797647e1071dfe409ac722e17748b67f6e4101d187f88202a023cf71ace27002dc19510d819926452db70f703a154a5ae322a493d1fbed43cc599c57126b88d9a7b89957739bbb63d788698b120e8583151688644b308f957f33740b31320a630b87495400753ef0f778c9e0a8913ce4107f73d49dece1c1ed8e28122b950f946871cb943afdc9cfcf9bb8022702c366553995a69bfbe5ed5a33fce6ce43df6351f64848d38994b668a965065835d19c45890ab7f15d62ef22f8bbc34b6ddff184b49bfe73fb829f8650a890935a4e8c85d4843022cc54017df00794839f1ce8d5b91a38262f02fccddd130b9290001f0426f756c6698ba5588dded127d9ba5296acce409eeae2e8402abe458f5404bef9f565dad4dfa77039cb6492bf6ea0936ee7d3360449c6a2dd5702ad3e9d69ed58b4d07f10aa8a3229a9b10722ea5347412f633ca46199965248d1e7a30ae053c255ea7f8b21c82962b3ca060b2bd28c412ad3b2eff727ee282d2332d7061bdbfa1ba36e18e3fc883b42158999bd1353783e03c337ed2638a0772d05e0d1f4c4a78704039145139097a22f9c046c0098ef80d16372c7f98ac5586096934bd3855a792a5ca97be72fc4ef4a966c8aaa5dcd62c6a06e17ed472431419ed8bae4a0c75e93978d32a7789e3cc42d8e0ac55bf09ff61ae685fec59bdbbb796e29a3db1a3f07bdd4d8dd3dbcc0de2657a97e5eb465b6e1f603405a0698e1045166bddc58ce1645196670c641099ac1806dd4ddaf28c31e14eef0a73e05c30672e2c45daa7c3b0f0d57da74b5ccecb96c3bb6c7b65be37e11417c108292599482429ee29f093762f700f3a1a9965b32a89a2cfa873d2a208d78437eded875d809d0708c2cab6f6bb018e085175b3ddfd000e5591228fbcbea9d3b5813448603ca9d5c66a1327cc3502ea10432cf09d8c06f3428e55decd4de6be2d0b17ae022abcde2858dc18d7ae6eb6c5d4a6c5659ddac128f891209eeceac101ed5b4fbfd563f03cfc67d49186a9dc36414a71f9d190acc21bf2faceb027dca918e987071aa485f2fa393d0de23b44980a32ad6d6cce2e34e92ec419a89485eafbb3b23bad433d2f0889d561397fb1e562d46fed8d095ee65295a79ac3319fd70f0a11ccfc3b7c2a5fb905c93df2e094acc6c61fb3097d17311e9ebce6c653748d8d64c0e3bf7fceec4124b09b59056e33febf27dc52883e72519df1d5fd817fa8d883a7d7c570e2d21368471082aa86fad72d847a61f0e639099e4136c92af29974a5674e561e040330ac32b8eeafa736476cd6f1be888dba909b804b920131f8bd34a2a4bd57d67c6f4deb09a60ef1c65bae80ba44943f8d98b00c2d842bfbea2d7cd1325200e61028164f4e4a8ca4866f712563caefc76cc705c042f4810d61a9e5a17e94813ef904859f42dc75ef017bcff1a79fe82715ed66ca00d56f4581014a5d8ebcd54cc9cb043cc8d80416cc3257d2aa6283d52e5f39c4a81b283b75eccf943d272c39b07e402c8f2d5281b1e7d73791cf545543edc9085485e9ad37775e2bfb10fc22f587442de1e664059f2a8610dad9517e31c730fdfac5d22ed77f309d8707b68f2050de9626c541fa48e58f3c928cdedc3ec67e9f947cd58fa170ca9809c97251265a4059592ff4c50ae07d58a7e8ccf0b75e428ae55a016d262c1948c7029643a25ace5604d09d174f0caf047dbdadb8a9de9c305e6c7efddb7518489c32243c0b092a743fc81e65f9ab1891b7e2752e4c1200b36a8fe8b9e7ce6673d69c4c95967d67deaf04a2b73e1997c16fc7d0cea2aa72ff2a5b3212ea1acfd1dabdfd2bc7af0e978c6481fa35fb2d0d4a46d6d65d77a8129de8f65403c3f6e347f04e5bdf46979828322eab7068642fe78deb72db93c4b306bf7b127d8d475aaadd84653f7416908424104781781470528474483f82ae05234511b58757dd5e409d6f8a6a02282b4c41cd5671531d3bec6bf09bb6539c6104b1138cc1e24e21b3e93d0b384d24db191448fe2d278e2ca9807d90a7a20636b982dd50f3e8de414e0a9074599551b680529441e6c8e2ca6d60e8f7251689f0744f3d038d9f504961243f4112602017bbbe62c72192538262f3afb0d893b43426a27f53ffd910388072e603ab51082d5f76552e886a5e36709273c60256cd1c63ce1001e0002dcf2c3693fba193aad6b6e179a4100c902c7c86632b0117c44deef804a02e367d2aabb57f108b7fa45772670e2432b9ba5cb9cbc79db8e8239a81b069c6c8d9a3995b720995f6012bbc410abb17e8be30ead0e9b2117d88ce95434022047f67c11e0275a3e68f8ff41f7dabbd163a2bb4121d2d376f5217d0a61c168bc60136fbe6ebc1b93ee023b668702b8910c11d6ba36f521e2ad3de591273b7e588e4b4cad9d3a4042d95fda0af74b2f3af5fe964a3805670cd8ab7e295378617eae22de52195f54481d7637e0cb3abea34cf0e3142d0da865972892f50d726cdf1690420b8e96426d8ecea1c335516f99ae09ed413389258615d74b40753c640c35048387820959f28a0635b7caf396af4c31fe6441eff48954b8629270754cfe91446f5b55c44509f6ab9746254813fee5fcfe869b1b119c7a6e1965147db07217c42103afc0625509fa3d8cc38516be3882884394d8d311cc3c6944407e55129d9bc959ddf67341d3f18b5651529ffd68649535b059a5d4e925c21140f0800ee628bd45cffc352239d9119c24127faa0d6383022de75924ee4f51428416d59877265c7f0e7497c487df269c7300b7ed74b5cb22f6f9200c7497e270a9814d10939b20fed85b41d02ec246af47eec8463a5d7002a8f2201d695221480ce59e0951054f07dff1566274a587aad0ac6e0aaa682c7b18f131c35019e269026dda3e1570dc502a6c79109981d27d28092512cfc91e9a07de5f0f3a9aed1bde9d7697dcf4252097d40b84d8b58630008d583df1c803b2fd222d5a8ab5d4014517415489791fb953048fc78bf69df412e9df1db46e8949092a443b132fb8df02499c1c963ba3be4c3591e14e31140dbfa00a59ba5703ee82f9750caedf0a9a96ddf68c3494b219d916ae2661f39017e3ad9985dacf958e6c1b3abc43c6a41aada35c4450606701d23c53a3a416f07309440063a7647a148b78e74967a5c44fade62ebe8c0f87a83bd69348fb330b01b11f78689025b0ad1b944085172121405c47e929a7a618a695439a38fb2ba040f30cbfa3e38a42334cf1a595327b4d7d2bf9c3482989e1f02aa44c0eeb05e54eb456001936528cec2a1d62580e967523ac02aaa015679e971b3e815ea7733ca2399236fbfb96c7d1e386487be13da649c7a9b6636b674858791ecdcd89b112ed064d7f27ac7ddc6b011ba68fec654a84145dbe369b187cd4481ce8c73cbfbf1dfe98f5803b3a6c8bcaf35fd6e0370dc0030b9b80d96aef8f2363dc14141aa3d7458dae32a617ce1a005fe0a9d7f5862f4e1b9a060a5e10a520b9a4cc2fd0c245c4d83c06f453a51d6d5518887e7a847c9b1b9702ffc4717990a708c6a0736b9bfa3a76398b3a5d5c720d03e89e96692466cb26596014fa0c55402276ae7c77f175d0f715efd033938acaa450570337af4a585031cb2b361ba7bce226f97445c6424887767d5dba11e90b46c47a09c49db2b6dc6df83f52df32b1afaedd94384a28f5d8552ea45c3f8d2c6aab3f9027b4843a111f5b7b2cb9d5287a32d2e64be9e1a84a9bc538ca6c186bc41512dc55213778a728cdb875d291e788412a128c3e361d2ca33457a3a376b0b0d43d90c1efe8ac2718ad45691bbe6a45a0b11384582878aba01f1a77f6d654face0c73a8c2c1a5e8bb25465fa2baa691869e52024981504cbf621a0397a527ef8d77c01c93eef2cc68d639a476fe462047958261ab7f516f1932996cd6c4b9aaa86ca71dffd162842e2486ca097f08a6f8ae9143595af047552ee44cda3fab8da37423aa88cca77332a86fe5b570cb3c9c0b214444a0738987801c6d006aa1a019c611726d6560c4a6c3225cbf89394197a28c10953ea08c789b772c6f24829644efadfe79f176ae550a6d788a60b391c12de55f1a874cc768649ed59d6348c026ab484e2a76b84e3ffa2b22df23deb15805e2887a56478546eafe4097512680471063bffa84a93fa84117fc3c771da2687d24a5c5c418c931a9ccc4584d1394f24107c3556f1cf6a6200c91435a8b7c64003795df4be374c3434746d2e83666eba3a4279182d761f6e2578326dc384cd50686ca05482988e22150f9c70696a982877974d4733eb2e6afeb381814f46a6e3362fb7d7f58758c755ccd3c449af1cfeeb491e626140d260786f2e409a9c25dc6549352d0afe5a03548aaad1750c3d4863f66affee4ba769ec7e5600e38d3df4d2a215c72e6fb542ffca1a09c6954b888cb7186da89fee09d4757413e4da3f16e5fdc172083c2df2408462033fd30f512f9860f594110eca2eef250318501d55a8ce3d2ed90df1d819d1a5f4276398ddb4ce42ea380d26dcf9cafb8010f428c974a619f815612a42379a846941ecc110cda2f2a6aafcb8d1207661717f836811402071cbf724297e05205117976f0aecdf873b716c1a929a02645a2a973a313613c4b5c9e18f8c06ed56e10a353a275400b2b99fad201c13ac189f0848c5132565103ae51a1a86485035a483dbfe7807d1d6a479a64e68cdfdf989486c56532e549840a17a9420a809dd98d31a9c0aafdc3445ed93ede8cd6a4a2bb7c0775d037ffadb4e6537943e4867dc0373a97c0f486284df999255405c792210a9c6165c760bbd8060f7d94881e436e4f4ff024e507b46916df220a764914bd235fc458d4fe28faa840d615df844a79c8612e95b5c78b31a00852afba02135361a7c50c49db998cc601091400aba870cb432af43f58a5f2ee46e65d1c6f19007909b8f321357c9dafe12b8d64d3785a8c3b5c0e129542450dd125d44ecfc1a3b177ac196141a262e9564a19d23bb1eb501b97174c99f7cc2acf8a273e6727da0421707ffcb05f7dfc2dd70769715921803f885a6023517b85bf368de4a2e2b521dd86b10203e4fa42715372870abecb6e2b09c342bde00ad006eb701ac2697422f44c51a0880ae5e0ba2401d15882050453438718dba559e8233144f5176f0d0d88c1e032afe2769930d82bdaa6185532fcfa8326f9f2afdab4b81b0cb5e44983a9e078f6c4b4a25e74751d51c0abe4d6068a55ea4205f776ced60884251b04c5e8e3cb8fd878c58998a3ea7f58bc62e5e33895d1045640edcd1c05fc7fc116355f9f964910f21041b19f6e0361722c54b3e775325a6a7f57ab7731f81cedefc961eacb560151ef75347a3730e0c7ce603888ec7b5797025850d96162706a32d9c6b7c3acc02f7f542d966ac5c0400bad07c349fcd7a5ce10074b6b7805dcc9001f80c3cbf5b1a140fb5fb24abd2e60beaca17d435c17b5add0264ff215cc4943ae244f0bfcff6e0d29837ad2fa216af18c8f0a138f235b512ee3083de0a0a4be0922e801c10395a401ebccddf687e072680e934bd1cebb8f3b9122cc918758848ce319d20a1581325c39d507ba053c9849173b8496944c8c63ab772df63cd6f3051d506de9dcdb33ee0dac02a8ea7ad1c7213093046608e1e1275d5c3315afd1e98385e94f5182d1374ca5fe4f7a8ac83c615c84468cc5c3717723a26263eb5d9979a2ab930c43fb60146787ab872220f9eb9c4a50198d3b63efa0d2c51903534b365bb950ad2c610afbf8e33f1d6c1f227845471a0e2eb50bb035ed6e85efe4f672381de324dba2492d674facf93c43ad47351fb143ea5efb2b9cdecf592652e048297f38387350401517e198d8114716468a37d08f794944e4b4724a59be76376eeab653295e89644279736e8b5137ceba261e2668230a18253583625b49dadcbfc3977057b0d9ca40f828fcf284fa8ecfe80d1c0378433ce0efe9bcc181b3e7ec503ea94cf42e83269804ddfe104b0c12ce3d989ddd356e8b0109dcde3cd182cb3dc0164bfcd3b423e2bdd0c8aeca54b68f95f592f56fac84d0179fd073524bc1dc0c84ae738c92c6a8f021cdb872d052dc9ef0910a7f60bc262c2e05596f3a2509b16da81063d81a6c03ec7954dafa63283b40d374b52dfe96dbb4d70bbd6b3b514d8b3d6c0fb5a2b4ae86c62103b81676bb3a22be92654e7c2e2ea0a7f60a1097c3c1d66ad1295250d200e2f2e991d01488c9561191d9a5be9b547e3179d8049f3b044f0220e1acf54fe4f22cc2ee7c9a9ef3c94008617210c502cde9d014722b530c6fadec11082530807a05f191cc2ec51475ce084192033acafdab90ad20ef5aa0dfdf82c1cea2d850aca8bc46bbb69e1d00608e6b15b03406a9a75c4eeb66d2213a23de790a5ac57e9778d22fc8f6d82d334dab5954fd1d88a4db7cf5a9273a6606b942dae9ea0609b28ad442e7c82ba088cbe7cc5813dbfa4d85246ef4b2f8184254b9644d04ce000fd0c4e00f92aa806c70279482402fc7494cfc50ac3c2524b6545a2065d5a5a59abdddbcf90cc842105c9ada87716db1d5fc80e6eac533bec6c2aafa058776fa92744de5f2673d8fc2bdde0f05893df16097afb80b9d045f37e18fe02d0708079102f9a4e9f09b0fa4f17c2350b1333b5bd17c77533e117faad4907be1f11a301d0b4502fc545e748584e7acfe9c5b1a5ecb06457d16da0b02878d8f446be04d880dcfe466eea4d7479d384fa0252480f0f2bda6a6543ac410dbd44bbf3fbda02e1a83ce69a3538cfc331f3f90fab0e18438a650e84eeca4bb20cf9652f9fcc92c995436826d03fac9899d119f9afb2cdc92b6db97534b8699c80375296b2a54c79f81b03508a8f4ddf5a354b0e2905ba05131a82fa0f447696b223b6d0fbb5316837e2ef3fe33001994a1ffc05d0b2317d9739118f443be334adfc7061ad83c4e4a253c98387957dd6b76c88bf7a35e43e804aa2a3794bb587a8248f3b8571e78386817b4d5520ab7f2815cece3c8a1b2d3214b9255c768646446f8ae595d006d739d853b840817d9bfb429f193d7f2d41d3161c4e9448889941fc043ed77ed48a3413be6eb516cc1d5da9b8a0f7d09f2dc41952337918895925257405498812d653cd60a17aa274702591fe9f9424499c16ed1b8489feeec063008791d18175aa1ff20cba01e8002860dd4fe23b4ebd31514f485142bf339c4206b16628c7d02e02fc9185c308669460970c96bae8f051617e2da07a2e4e00ece8092c042110ab343624aab027be788cd0cd694e3008a4797cf16026b3acc55a7fc9f209833dfdba97726d9bb0053f5e77390936321695334cbadac6889eca00fdf1015c6996bf4d0eef600359ee53dc8202b236623d787d426109f09970a828b1e926f843a3154cd1c36e61205f8c55b38caba4a78fee34071511ae360972867b7d003d82ce3831c180288e85641c6b8ec64447bbc6e2961399abfa073354de98761e205faca20ac91af5ebfc6aa6b51c07c5133b7bb4735075b7512222ea05c7910e4753db69c92f34f6a49a7950f462bd40cd2947221ac1d4620163a61688ab149f123bc9b0695756880c668356ee1059550807549948ee54e8c453c24419f35fd01cd2d803fd69be278dd5ba251665a493ef9bf10e0f928754987774e03b7f350eb33a91e4a9d08951cca4e04f9e90361942fc3912807b650857f1d78f9abfac1ac3b7f37d1d75fdc30a15554b45a9021b3d1e014177516adc022872f4c45cffdae9b4f3e582238202cf8255b421772ff3f60256113a0ab459587b3f901dd456737bc4d67b27c2e9db0ad6b0bebc925fd21d0d79195828cfb4ee0f84e17ec038ecd74d4651e285ef4b5ec2197f0387134470610c2cc373d9320a67778cfc65abc7b845fb1f6b00694c3063646adc52082b5b6303d24fc2e7739c7fd634b50fc648ba63d1cb2ee2bcb0841d6b8609001810dfd6ed85dfaf5aeb6e1164e70d306d1b23e78546432efa540341d0e9ca8bbbcd13381b355442a8174969afc2bd6aa086c32dffe2af9c6382484c76cfc124a17564499c5042745c508da35d222183eda7cda26fc0b803cfc140055f218a9654d75ba33c767e87d5070039ca2f8578fd13657c193c961bcfb1d9206fbdf3f5513ff4902b7d4f40437ca8bb6e8c28263bf5f8902db69a5b28b1c7150cb53d3cf51ba9bdacda6bdb19097e5f646b68b860ade8453da30daeff7300a8cfa179a76605fd5375ab8c90ea5bf2e1e6aabbdc86d2e28ea3649f81cce2ba7dc0d11b5a4a4bc800293decd4d28a1bcc27a85e8cfe43b5936b706452f2868a71831f416f02cc5e4aeb7804285d1ef9a846b79f4fe93783927021c7beef6e0fd30921d9288b47033a54d3dc425c8e0668285b5ba9cb8210fe77436133656dc5539d9660cc30725fffa7b571ea644de8145387e4ef064ad32e7dcc7d5de838fc90fc92a6c81f21c0809efecabd38d74b79ceabfe54b74e5d8097058e224ff750197f924a8b1164f1b334c512bd32549ddbe6773b2f0e0375dfe2748abd848b3b184667d90b834b7e6c99b460d040c77c8ad00c81bbc29c8f3b123ed4c2a1e780b8b1b3dd83575bcadc1117fbcb1182c7f70b01cf7d8083285902c9c54c301eaebeec38efb54b99f9aa38442ef4221ec5d44aa0ba33cb1489a8dd22c9d1ea5e9fafca3f3f33426211b22f289986ab9a7813ab430cf10f49d338365740c4f883068e84d3fe73833e134433dacd3b14b2935514200bd980bbcb10bcfdec9af3e5d3b4d8c0900252fff8268936711183a0499c0a882776626567bc04946c8e0d9823c5c5f86bb185a0fc5288118bb001e92cf51301accb036a766ba857309c11a539cd9a8c737e6e97cac1f8bbd715015cbc48c1b99c23cba7d67173dc720c0dc1d590dace5df8d8d5cf180680400804f2c45da40a0b404b778d8ddc9c173b8bdbf4eb1b9a0542d65adbad7523dd88f59295bd03f50a200ae609d43e6cdd1b293b5d0bbcb4956b816a0812040e7190810cc62023481038c461075ba7ced57090c5bab32c8b5e52beeacf66d41f75dd7d63b4c9ca2ed8a2977c8f980d1f57e3dda26e43d126ebd6e91ba35e6d92af7e12c9353ce8ddc6712dd62d322af5971ba7928f1e64917899f599d661e38c81ad4f9a459bac22d8bab44eefe3699375100d1e8fd5b5b61586f5c6e9534e9fac37147d7246aab083adb7f5d6e95375eb1da485f4093467add0b2ee56ac4bd33b202ec840066390b1923bc755d8750c526765d1c3ab2da9a053ed208b69cf6c3c9c1d64afbbecbe2ca82b06a9f3b47a8f8b17bb3388417f435c8d1ef429bb8f8bd30787aa5d76631b1183eef3712df3561c6a76d0e7f3f3d1ee6c5366ab3f8bc4c32c1231da2178feb2f96581e769f582b4693e5aa1e6c6c1f3f03e216d9aafeed369d33c48489d7f3e13d60a9f4f5b61d4fc7cf0fcfcf3795cf4c919e9f9c1f30f499ffaf3af8b3ecd3f2fdecfcabc349d0352820a5d28230c508c80085e9060e70b9d261c0e8ed00114479430822a70a290814fb8802eceb03332852ab2a0ceb1380fc0172a5e9e09d01580806ce3dc02dc02f28f7eefd4614054dcd732311f41f24cc42ba9792b647967c239269c63c93d7076ef83ee26af7308706fecc69391d6c93c0c8807c4c4fd43fe09920c13f1d47d62ca309184f3194206135a74818538f8408609797dd08fd739d7370969d32dc3e0ee208455555537cb43096704452ac6282384a00761105fc7186d8db5535d4be30084fbf1041f6b2822f8815c8d3550d7d4f57bb03b42186177144b8e106109d65c8f8c0fec2fed12f2d4d481738deb9177583e4679e59f8517b284e006f725bc8ec68178478a808377c0ee481074801f2604fcbbee7d9f756de201c4447dd712a3ba4318ecb815e75c942fa78160d838188e81e15be2c5cf922e9068e183240b1e2a76201188b409baf7e99302c321183af8749c83328eae2863bcbca368b41796f1be9c94abc2531f14b83ba70ab424457d1775d8450a81544ed41c8dc28ea709425ccbc3e9e1d48225dc04c1810ec0288243a23857494949ed3801195730210963a8c20c32d2092cb5208337217646a040049ceb6b00a1ab01a5947f125e31420863c326a08412c2e08b9e028c1da3c94463655e2a57cf27a68b8d457c500c312226c6732850a2448912254ade53222191c8c37371f10106a6e10afc699862ad2a6d224529dfd5a62fa4a328098b403de99c73ce41e81c26c66c0921a42ee79c032581f00b28a18430f802070f52a336814465a8ef0e3b233d44c08d3309374c7681f1810752cb74ec136882a0901294402767d6c04108a1a4c14b69d82357022205c337ce18c0110b9a9d730e7bc31bb273ced5ec9c7330a2384751f20444616f787087424275f8c12290250df53d8ab037480a140251d49049adeda2579442aab4d861cc1042986b865508218c43b221d8513a62502feabbc420bc2808af5c2b9dd939e7429568424c0c509c97fb8bd81b9e6ba1503e40c527282227c567082194422e512eaa86fa0ee35d4b8c3146d21549e09c73d2e66aa22933ba94f21c55b97ef44022189ea601129971772a4e25db81064e2861010b5e4a69872d898f167ab41fedc727894f8f4f4f95c3058f114a92f8e8a1e487f2a161d8626707469c1520300e315fec0481e30b1e3944470706efe5bab9d181a20b2d4419656c839349c299e49d847f91a2823cd722df7b2f084adc0e752bb9dee0fe1454efec54393b3b14a887b49016d2459a481b9ac88cd6e99cce494929a9901a8ac6699c1c4e1a48df00c04472d1cf89fac6784bde922a873a8288c8fba1ee4bc244122d47c9cece0d81dec3c3434a82c4e7a59476d854562a7557e5482e86b02189cf4ee5822aa75a817be91ec852c560a7e7dd1ee91345129f1e9f9e9c79750f1b768eec28d9d17292ececf8b0c1878a224a8af8eccc21497c0670817eed80ec13a263f2880c69c3109f2ae70b36d8a91e909b1b28701ececb79cffef0409bfa4b709f1640828ae0e16a8c5092c4470f253fa4d7837b07c812bbbbf7e29e7bbf17e9f32417decbb5f32c9ca54fe32b0bd732862174f4000131bccfb5c84b123613ab1c1f2df4482e70bfafe05e7aa777b6c0fd3a7af7da0141a1e5f44e1fd1c0d07eb49febc8b513c4b58383c700d8cc1494f673c54e15ee45fb71da4f147ce610ca06ed4703a35fa8db5a744f955345017f88d9a1407207644d6fc377e08bf4d9f1d989402a8c8b8b0b025a8544424002dea8d61aceb9972a0a57e55c550eee5f3b7d3ad22f920be91304e9a372137b071e9cc0398803c3509d06a85b91a7de32b3e4645a0603a3ba6e99fa96a0a51c113bec05c27b13fcf51b01a597ab970a7e5ae8e09ce9e1f70e23d6a14913bef0d03ea460785da40c4cee750a5639c197a85033093749a55f68b42905d5b93ee92061bfb8031b6a44709f4a9c364121713004030443a540b3e7d25c8d12894a490259a6784890e05ef233e34ab9267c9133b0bbb4cfe7f5a4a494544825941c4e720080491c01002ee76a8cb08b4ad43c7be41144441e0f646979df4ebc793b444e7e4833ae1b7a4320cb13f284e45c37a4339c1481533c228f484a69874d2587ab62e7248ed401435d995a983d1a769f57702f7d33b7c07dddf48d0e7d33802870963a59df0cc9c1997d134447e70cf6c29cfb7b51b429e7e5e490ce2080eba6c685a10e653dd0c3fd983c1c38c5bb1980e9824e3c4c6f6870e3d660ba9cab6155440ce153040f243502c08c484280025c3bdaa4e3023deb034cb43fc4ec04c182e170342ef81c1b212c75da2406171309b311312ca3be531a170c436db9a0e89e3d4170bf97702f12c7e19480d40de390f5481c8863006c666ad1a69e3d0770b99cb86eae25dccbec81ee2a01c6c149dad4a75fcc1ef8227574660f6e9d8b05f4001724a3ba48ea8e74dd4096aef3ba812f52ea489d8a53301b3a6009c7d0399c82e482804640025e025414206930a58e8c817bb96eae9b1be09b8b0537b84f1570a9e4504a99414239311989425bd5324c452302fbb2e0e1d87d899850f37167d28a09f6bc0a5f53e2f3fb6c538587a779b75eafb54d599e3a95b6ba1f183ef6e9f5e16542bedd354d1718ca3a29a5a4e2bcd90785df1d45494aca8b44c43e280c9740c518e19b079677363f9328e74eddfc0eba2ebe471d40178988e5f5219740679f5c84b54d3d1bd3b4cd4be2f5b342852cd53384cf411607af9450562c638c1144cdee17af6b5327f16c402d85502cbba8aa5956d5c2a0bec87e5d7f325776818e6517e894750abb40a08ab26e84096f5a8781717131997e12c97aae79e2ec9c802bd0a9492beb52d47556b01a1406daa7ef7a587c6333f1d008f8d23235e08aab61dddd08f872ddbd077cc1eeee01f842effe6e1e0e7cd1eef2cbc1ee3900d44503770fce1d80ba483c9c8f3bb353606ea84f0523b5a94fc22a809dde244a147832f44f865e9c4b063b4e940142dfc494790e01f3977d328ded8832d86d62094d86be655c9f3055391987cde8a0d73904cccfeaa5a8f054e7045c7d52540ee784d77de18030302e2e26d34f22d50a9d80a74d133095235ad7b996f998ae4c6dfacba91ac405284881a11004f7af3ea969bc8b3eb0275467f3083ff8787d3c3c135f8a77d7dd5b781b0944344ae9bb99d4f0ee1d3e683530dc92f6023fc70592a7858ec7e37a7c80a1e10283dfcadbf46cf49db3f9eece479dbbdad47d0a2ffca27d87afaf1c03921e8ba28c608104bb31b033724511f0cc46e05c5199c390a8d71ed3206ca63aa8aae66d13490c55fb9cd76cc8671fad693eb076b3f61a129e9fd88cf689cdcc536d3e6dfeea9728336fde4e37abdd7ce1aa3a5c9d5af45d9b757aebf4dbeb29365381a0db2b4c085019659441069eaf303b45ad4838a2d06758d72cb91ef4cd76e0e1d07569340d8b2ebab96684b53beddb65730dd66e7669af872e9bebad195d9af64bcb2e6dae51b1a24ea9fdd22e152beac4b966946b46d8b2fec31035d87d56478621bc600a3523dc81478621bc400af0a44338a0239b7be05128865eebcd3d70bc597bf61848e96b7de86f53bd66473458ac35c6cb1beb43b54d6e641dce309b6d5a3e9eba972138feda4207c79f9cce580fc74f987b601f10d39ce41910b81e511eb7931e6f36b91322de139a1ac6bbb6050ed68e72ea362daa33c28525d45179392c077dc484d02e6f3ea2e20e94f0066c3076f96a73c5b59646a7091dfbe8620fc58a9dda1c0a62fbe810bbf5d5462044ef81437f1804eae32d8d5e1a1dbbeb5b1a5d1c8e7e443f3abd23ecd86dee1173e8201c0f6ff2a21b85d88e5d9aeda34b8372cd47c4daf511f176518078d7feb01c28d7cdb83e20be2eafddec6e34b93e203eb9bc6e7ef8e4c4e62470d434fbe4558bc02188230432f6ec03e2939b4540e7756133d6a36dec3021208eda8d6daaa19be57d405c6ff601b1c79b7d4029922471392c8cbd5aeb1ac466acd1430f7df64bca43a7a120b687b25b452ddabec9af5c7b66afd0b3a7d807d8e052769af8d0b31b1f7a0cddd1c3d8a5b14e0fb5fa770a81aade61750be39d9eba4d8cea8c70c107d3d7d0cd363874cd4a9b7b949eddf54bcae5e825eb9a6574f3c49af6141b0343b6030f9f5cda6c72107669ea698e7afa98c681625ddad0b7938bb4d0e54397b72e76144b13bab14d237779b36b56f413bbd9c6229b4deeb4f4997e033638748c9646cf3e3a664597567bc8e68743af36f7d0e445d8334ba35d1ba16f21eb2a7df5cde624302d69a791afd7e4abf68add9b93c0f4ee3e22beee1eb6e8b5fe987560f769338fdc033b1948592ae27777440fece399a0b4a8060ebe9476c4cbcbfba3072908614726de4666254f8c659c9005c2977877576373351c8c6b71438d6de20210dca701238235a8220840b0865d1540d8021545ae788317b800d85d2146cf16b228d2180fb9061901cf123dc08e47092af061b0e3514209fcb0195292ea70742199a13ed2914a5dc50378deb2d43a0284e7a84d8f349f835975e9d3acfeaccaaaacfb8254f6e9d827042399a13a2c1da6aa14f4452eb6d003ec8c6c21887b0276467cb4c03e477c88809d111f29defb04d819c94214983a6c8614466d9218b5678cb15655552f0ad50aab5393f522bb84e82308457723d16b155dd1a10d5d91752e2412d9d03641950ae9aa37bbcf9a150bbeb2f015a9aaaaaab2f498cdd4081a0add0b428bddd91a92116d7ad5d2b02e968736bd63964711353d7c78e0479b6edaf4569e509f0d860ff7ed77691e91505b465e8c31c624505218fdde83955da2ba35677587ab4b59559f96ba950581a80abea2a07cf1c17e4d84929690dc509dd5401a08ae7f63d806d2267752186a3fbf31e4730275d306b30635d246822576815e14fd78a8590042df31db54f44e9be42ddb476c17e9534be04bebe4b9a4a990ad93d10b9bd94eb199581d04c2b0776c591476a3f1bc5dc4b5c81f83148220688222764bd3fab4fe6ebd7afc0b5d988dbfec461d75d784a61ed4ea9af23c90bdda7405a9daf3bcaac7d33ab2be563aaf07f3d6fa5c6793cecabe24cf8b36c936cd99d783f9987e5df4c9bd2eb0cd5ef911c1f2d522117116483cdc05ad288f1fe81fe8a66b6a93c440f7e5c4a0fe8736c9c750e8eece9d82b09909df14a1190a9b99f3ba669977d87ff4e9bd9b79f30332a93c18959d56a54d3ffac4a34f0e7449f499847950539b30d07d90fa7ee8d37b45da244df25d041403a1e3c1f254def4038f1f34fae5e55c389be27d39f0a58b4016f99c1c2a64f360f972f0cbe9d3c3f22f8a3ee9bc207d6a2cff2c88d484aaf3a26893fc65a7652fec30907d3951a8f15da48bc017539e5517298265147d023da64df0e5a5c0b1c8bf00cb1a60f97480e5a704dd97935951fb8d04eb511828e90da12e62cca2c2c7088f03d2b26b57c52ed03550051d848140200df301040a02479fb483fe114905cb2c098cdad7aa3927bd75bdda25ea374dab775bfd75d55baf59ec569b658f819ac56a66ad57eb9a745ad5044951fdaadfee3e72995d6953f5cdbe4dd5277c095d1364a95e5d2ad40c83ab78d24a4c9fb2e9873ee55110d571f4697b75d3ca4724953e51afaed96a496dd8427689d045db16128542a16fdb43db160aad5497c6f50cd9021731c668851a218410768443fc8b943cb54bd05fced13b4c2f25bdf4cebedbfd685beaf45979a3cf2bf28ac81e299f045d242595a74f0ed34a47474747eae8c097c7a3a3a3f37876a828425d5c8dc6f13442ecddb7f38ae8c017f730ea6fa74f11ca59ddb753a44d31bf2213042bf5768ac0972713ff5ca650e1f3e3b1fe78b2e8f141f292f4a97abc65a3b314bb7a5210d473cecaf378200b36d35b14d5e2c743a2b563a5ee13d2a6189d50e78d8d2bea4e9b9d0fce35ae8bf8c7d3a6785213ea9f4e9b3adebe3c5ccb0c20b51fdf8f86052c6821258c1d636c28a59452ca28e5a594524a299f6551e365942687c404d44d1ad4154a0faafeae20038c2be0b004bf0059422ba38c32ca90b9dec465aec7c3f4e91d24abea4e711db12c6c86dec266bab2d84c05aa287dd88c35b119ebd38216a4af6c1146d4b4293e623cda149f45a1becfbbec9060d61b736d82af2a58c10cc3e1b0754bf034efa0bf212c116d8ad7b0997977b4295636c2e8e893d301ab09133f274cc3e0186164506132e835245c1d6233a0431008545dd75537f5e9651a38b6293ef38089d057dbead4ce3fecdd790531f5c9d1227e30a98a6afde1781f6262daf4ee439be24136c66751c0d49577b635054a9242adf092a19f7d824a6acc7587c4866a1d9e94325fbd3e4a658c71526acd496d45e92deb3190da8afa659dcd93c657708c43c04214b7375c6377ab6b81cf786aff86c34068bff7deb37d698f78f13d2b50d72fb1022161f9a4bc9a05cba010ffa87cf1a7c643d32b23be273b6647628c110c38de0c4ac498e1e15ae2edd1020c086f063987ddacf14d47273521f1708699f085018e25de09150b1cab00311b8ddf85712df1194f35457869b4c9445a83c9217962feea5375eddbddf334b569be5a1a6d9a2e90653ebb34bba256a7ef550fc298958080b81ee2fc03d7eb5c4b95f554f78419f4e0ea315d8407fa94cfc3881e7da24e03cf673786cb8d0a9ecf2bf36ece39a763a99e64e70757f30e2ec1d5bc0b0d93ca572aab59d219ead5a679f8ae27b1a1d63ba9655957f6cd2eb13d5455db5d687b966d777b65b5bb55aff531b0b2da56edf52d4f7c65d663fa6db2e8cd2bd8a2956d152b2a6cf8724028a06ecea0c63695b4bbe7e2ee44d07a365610ae9858dc8834c305e658a73735bbe0e8925ddac567514aadca7577162a252a7c9c95ab61639b64846da06f7959575c97a0cb5799febad52f7a5d17bdb14d204a3e315ce65319d57fc5c462c532aceb029be8573f74f17233ae4df0107e240fe3a1bbef29d47375eb5a6f2c47f5b65e75633ff6dd0a6c03cf13526078eb27f7a2bbc4665e8cfd306b05f4f1a33ecd7035feaeab0e7ab64eef3c7d6339ac43d8d80c0df9616c52939a14858166409b0559cbce472c740a8878044d065c300df5eab23a6633b665ec1676299b5db00e1cfad5a7bbbb3e3d0a4481a8570abe80443763b7feb099d0053d06d6ebf2c4d975b14d57bc9cd7d150b7c605cbcb08b707ca29829b5ef0b683153ad78f81135ed4ddcd2ed8e16cc2f9b89d9b591ece79c1f88701d18f44e0e9eb1ee7836930b2c51652088694d6c7ed3183e027d3e2cd526eb24a79af2bbb306c54afd33ac2e2265f85c8aeede6c862b5f4ee10ca7a5ddae52b65d296b2ad94dd1c20f801c10f8edfe841772ea6b5abc253eba509d5c81c5c43378eeae8c444848944d7eb69155db566d7abe8aa597573ba19a5b496c491505c4dfb93d9cd42da103c7a66b757ed6699832b8e84a24da15b2d1d5c6fae9139979452ca2c7bb5d96b0eae525e3657f65a23732e795d19043fd925737070ae91391cf0e1001210fc40144c0147e640144cc10969f2aa097e6239cb7a24dd42b965726b64cda86698988c2ecd3cb1ac0ac5a4aa500e8f626272b3cd89cdb2939050286c66a2e4405229a188be519195a74de9a539b68b6e88463883b0a45d1ba2d367b556cbaa337ea3bac975c16bc6cd51dde495098511de61418cae6d303b842420aa6f971804b2d35b82a737f9664bf0d6b750a804af0d93876eaec1a1ebba6ab5b00480df48f90c912842938b007073a47cc6534c3ec3da40dc0e7fe01b2637d7606add5c83b7db600cc7ab1eb76a240f31214687976674edd29c1c3e64473007d2e3a14a09e56194c7340ab5145248512086cdcc4398d16955425894f42cc226df5c0fd73b40dfc4e4b14f2837b9c9af3e9ddce4d5d518dde4d735b1d8cd35183b6d4a6f89be9463bbf61bdb351b7aed37e835cdba90ddb094a31cda2ce5f496721cf1cac0a3c39be3e961bc35231b7cd9c6d7eb21be4d0e4d22d2cd8df385db24dacd1d40746845232cbae814c5c636897e622f5b5d8be831c6d3df889866c77e23bbd8b5c1d0529be1e15142779816d32856742d74739c1cfe4474288ab7946374f80dc0152cfae8d67af81b15de519b442f470ca4f37a9810f3669bea4054d8bab32ccd0debf51033c136939bc37abd55314c9bd65dd96360557dd2ede69a451d7478a6838d81a053685d9af9aa9aaf6c9894b0977288befd8668bbceb5d4db60df7e03fbb66155d49875ccbe32b0c342a1cdba39b0c76374c36e29473cf60d60b7ba96ede61a8a519b785db3f426767bb419b57974a932f0292d69bf11baceb5848e5de75a44b7c1da2de5d08efd8676ec36385a6a31ab59910dd91c31c4ea2956aa3707f678a97637a0ddea5ae8e3adae05bb0d7698766f64d8757312f8d68c6c66acee9cf6d50c1161cda16d16a8c8661f58b4d507b11d5e58bbd94396b59dd426ed99155d7fc7b457cd3a4df6ec6adfdc85b57b89acc3219b6bae4b7643780a6f52c186118300fdc921bc79d4b0b5d368e5c9254476cde64b2b894eb3fdbae85edf2ea83d06467bddb279135d57bbd42cf40101edf09644992d897e3ddbac5b125d1c0e97b0d358171dbb2a56886eddf9ebf1db2f9b7dc0edd415e38597bf2e4dbde8d29c1cda1c6fcd85eb8d2727903ec251a42626105e1bbc4ce0637508af1ba1161b42086f2545856fd1639f4c2efaecd3551289be91eacd57858f305eec249b3d74d578131b3a0dfcf610fc76b85d384765b9aab2ac0b678f36fbc071625966f268f3e8d45bb34eaa6a43d9f6f8edf1d5bd7e626936914dc49ed9d04d6cadb671c8e6d10559d6f66b131dbbe8970d3ddaec9bcd17d9eb98a5a972cd556d4e22d37eb90b4f9b89a06cf681dd61dc0116769005786baeb7032cec000bf8bd515a44443b25e54a829151461965943832f94cfbcb44230b65869cb24a92bc34dd4d4a29350d463b9172c6890909453bbd26e54de44fb24b4c080aa35d461965945146a93d4a6ab376a965f4d2661c5866b7147af6ebd49642df4ee7ac9544fab3094bedd905cb5b0addebdbb75fc781e9af18282595269730583e83c1f546931c263348379e8c4652cbe42533f928a59645994529a5945264330e2c2a997084c1dacee4a93d241f930b8e37946d39c4d01d24a5a8a8a4904eb2d056b32c7bbc19066737d7c0886cae81891607ceee6096f21928d648a472127cc9e1a519249c652a37bbe0eca6904e6efe4d2e2e3893e273b00eb2441c301126cb70e078cdaa585163966559ae818938d7c0c0c8247c727060f75957b050410e6056b050010e527ac60c88c0f4541b8d6aad2bd9cd2b389bb3de4c048e376738a28c32ca28459a9477a566a566c5c484141a1de5c4047b3df6788a65d94352bbc484d84e8f3ddabc826316b398c52c66dba5f7f231bbc8666c747a983d9eaa44c4cb5bd2aa2d611876ecf938c3b69b89c0f5f46622b0c4b29168f4ecd28c4e6b567076b98247a12c44ca504e4c4c4ceab3d3cca46619cd4c6a463322544651ae886e83c6e401471f703c6d3836ab3dabd1e4e61a6f5e11adb4a966a512b1729c1d4b152c557036ba9a3431f9c8ba36896eae99a6854426cfece82636c748448428746b5688a859a959b189c2a60811304ac4c0b302a3440c5798e8f4c0548b999499b434d67aadde614264988f88b938ddbc446ed65a7f851eb5fa4c087aec992c693707bdd843365351c542af3db057faaa95b69b835eec66b4da5c1faaa2639746f4edd25cd4fa88b8fa885854af8a723232198db25146b35146b7edc2a1c77e3964810fc51c5abc35bbd963e0ed7679d10c1b9b25c6add6dc036777998c8199cdd6e899157d647360d9b5cde61e18bbb4db4356d36ce3cd662bb3d866736037d324c6a3c749572b5cb732eab05a8385c8ba6e97f6b23287379b6b46d6ab910884cd542294110dfd44b45ddb2c755aadc49e5d6e87981036d67a1ae333f9cde6118e37b26fd9dde2319bb72042cffee2eb633c959766b3572b81b09e5db3d9a3cd3510d872dd2cf60cdb6ce6301bd7b7c774b6ddbc1d141f311c0f876e09117abc34a16797863e7eb335a3d08d11259e8846373f2c1289b66c1365d84cb565d5451f9663b35525aadae36dd49b6da48d527667a1574b5fe10ba94a291202bbc36eb455752b4765b9cb6ab334d7ad5bd6b76b368fb00cd94a2275bd39bb7be824abdd3d066ab66684b763b6030f5f879675cb6ad76633ed8de5d81eaa56bbb4a29b6b9cbda1bd3eb3f9e5d0eaad57d7cd6e6d8da5f5b939ec46ddaebbbcb4a2d6ad2afb7ea831e9d1109ad150dce208474dbbb906c79be5af07d0ec4f74074377d33e437bbc798435f79a51362f93511d913e4d1e6fc5504e3eeb28bbf6ecf234c34cb6c72bbb4e25bd6ef6f8ecf1f49bcd57fd76a8d99c9d5e29e5f678bafd0a85b64b7ffd3a7da69dda5c83e345aba6dd9a11a6f5f4d2d46f9706bb6ccd28bb35235c318c6482723232198db26ba7da28d3b48b3ebb36cae8a5cd5aaf592f212c7c5db3d64336d7e0d0e1c4a2bb7e119d8a6c1e69526553b1a26e36f4cc62332e7a76d1b73bcaaa4874f308cb9b6b4617d52eed0ed3b45c333a894e55645d9b4237579c6597331ed6f2712defd3d6206d1252a5681319d5c55b757890b527f0bb86e36af45fb67cf01b6181dfb34910f07bca88c7b5bc5bf5e406bf50121bd4ac25d1c1cfb9162d499ff2d3bce853b6c116cef7e96a48fcaec3d5b8f0fb0ed27c414d2142abe8cb1cf852bd2f756e758e051ef1c3c6c4477800471019478e8856b0c2c37bd2b21c6c18d35ddaa4c38ac6d95981e16dbc1efab027bb0cc1b3c385089e223c38a09125398097455871e101c6c5c58507d31ae68448be080327320192de26d8a436ddddabf880e17da06c6dc39c334e41e79a389348f0a59f4918f2b89623520b0872ef6a13fc73ceb9e7e9fe2e854c2785b83aaf5961051f943e9247eec81ed98321cc320b2dfa942b177dca328b2ca490474222933c9f48466d1aa0bf4f9065814034468bc648ab49279d745aa7a716ad267dd47ad57c0f142184b18706234904ab401aa636c18751b309d3e897918c8be9674f8243854f1249124537d5ddb161773f1f2d26cece478b1e2d30fc8e1d238aa8f129e23344083b234b94a8579d3f69d079407ac3c145ce14f43ac862a71b6acb0841efbe1bfb70406fd069fad4df21f5dedf71dc73f0efe6f90ac73e206d82b09aefa64f0f3704dd2170c4e8685317d103e44161048f1f15cc0478f3838304489f72863c3e7a55e9539f4a431a702a139ee003e4094ee0f9e1e1bdf7de7bfdf76aadafbe275f7cefc1b8146144cd73edfa21e1d127238ae8534dcc869d91307e2ef845cd13ff18a14fed4c71081842bcd0011dc1d10113c8a879de00006e1db0c3812ff333167135faf21247aa20224182e1092cdf5cf40b2807e2b81a3e7da24045201022630c012383847801cae92e3227a80008386329000396e35ae4295b648c213a00c07db31dd7226f8100804139f0e5eaa259e4bdd00143c835c6100c08112c31288a60994117121b54f96ce56cae2584df6506705f76565dcbcb1ac055c62ec5f89e08cbc3c8ffd02ff5477ed405964de4f202cb9bb0a444ae50a20c9c7dc0d25da1041538beb638b8b064064717b41c10163a2aaec6c36e08ee6e25745c0f4543c618a939e78c261bb344ed43087fe81384f03cd429cc2851d09287f8c3abca3a8aeabeeb6975dfb2acda17046fdd5cd909840eb86f3d768e135317c77589d9d10349c55de4dd21ae8e3689e250454ca87dca0e49764930cc5007ac14098ed0c0072700bb233360014f0c64e93705765789d88c6c4903c73b0804ca6e078370f409741b34a763a12c8e981f8af0809f8130e6071cf29a98d03038fa9479f821883ee58833cc47d855d1c40e7026e1fea93d23a0d28a3a038508a17272346945cd91087ecf9108122418e6c09cdee99d9f9f27044b2beabb931468660e3bccc63bbc25204c72c67d2311d7d28f92a20a21e26a0000bb3b2427e84cfd2c81e077a9843b3979e36ac4bf0af77b24e26ac0bf7b02217c4ccb246d9a8116091b33114288d9a03997479bfacdbc6761d45c75d250f39c8dddb8110f3118638c0e8b11da0a678a939dcf7b9ff0aebe1b34c7b5f4dfe5e15afad4bd1b54bc535097e2c012059e8cfcc339090cef14f20e0159fa5bd42c7fe48fabe17a09ee4337066e4b0ceca880b3a5831e827b09ee678a236524176abf678a83fb55888505752b940445cdaee7e9c09dbe8333c98bfe6edee545f53fe04ed499e4330d9c5d4fe5518411357db22edfa34ff4f23e3ce06a80aa6907d1cd34707e37373979bdb9a25c0759b27b72dd5cabc97590851a5d07595a5a966559d082cec54f37e785e78cddefbaa2e16572353abeb1bb8ed743bcef4602aec6bb6f04ae455edebc1e5a092c0fed93e2e950fb84bc202f8a97f37a80e5a50edc1be25ae4b7a0ee4df5c2fb21cb7b44b4ae98a054d39c6bedf201b5457d315eefbdf762bde8033d51ed45467dcf454949498148da73cf39e7faf57330d80c7487ddceb9ca39372d6ce6bdfbfabdd7efc57e8ca018f328b6fbc1efd0a2f4c29c735896695a7597e65c16afdff411e3acb0db5137a9d3dcb1acdf7bb1e17b365ea8819ca8efe6ab6e214de4466fe4b2ec974c42cd3442319baec55da3da8545a8f5ab19e8ed5003ea56f2dcc1f1f4bd39ab7b4284bc5bb750b3dbf18138f0257b3ccc812f35c31807f408999514e36a6038be7de04b0fc8123f67ad2452fbb81f1ff8f2864096f83a9f10c812c3a8b97d7ce41635bb1dec625c8d77d2166a15707458e087a710f882535e7c0ff8f2bee058e2cb30d750c3c0f1e79591d34e7cc6bee2192a7d3df4783dc0db3e48b81af336175c4beca10313cfe70a3bf7e96a4c1b97155547cbcc5b8354f87e5f702f31af0c38be35e0f8c27865e0e88410c8127f980359e235a8f3d23ed7cda66fb76e26cd7943b56e2452fd6f6e9f08b44c76210e6489af3eb97d70fc133267ad24d2cdc753481582233677aad68c80830714903c8b043c385e7fd1314660e5051f182b30a26c425250ec9c2205b272409723d588cf0ae8a19f1180ddff6849ae057e88d743df19ee0090bab3c4052c2f8fccacb8a0130912243c201213121e104e242624538c9a9764c78321fc10f0451e1e0784a0ed083850388104cfc0ae0a277eaa788310ac825d1562508121114c352ee9b86c64dc557ac5e46a3c5229fef43fb0cf4bc0c990200bf5797f60f7363277812cc7d4e7eb25e064eacd557b7691324e263bf55e295e0764a190908093c1717fc44016ea978dcc690fa6ae82457532d82dedb83e7fdd1becd7e7f54106644a7d4b1598a7df51d5fbc3077d77ccd3fbe3c9ccec7630f592fcc3980eec9674805eda711df4eba03f19180127739d464b9f730f664740fc44486aefdcdce01e430f106ef0659f0e64a12eb750f3c38143cd6e49e40159a86796b24e51b780abd181cae3f5d0778a7aea17702f409a30c960c721c9d453efc60138243460eacef9800323833dbb3560ead4b24fe6b22f073e9c3651d5e737dccf0f47071020d7b303a1d75e8aafb791c12c56ef0fedbe9dffc82e0fa67edd4686527b5922a578ec3632454af19622709d3ec6bb831ebb425e0e64a1de104cfd7a3a98ba1e0e64a1acf7cdb5c45717b2c4832e2482dd0e495836afab26281247bc32b0bb8f99b51edb7fb603131f012a230e37b8735075c09769723552b0bc0b7cb15eaa4b88e4ef3b6550af63d52d4148ddec92c067f7070fc8d27f8d29b525f7aca7e49e39fa1dd6e9dde9fde1037777d067f7870b6491ff21a909b2c8f71b0e2341722dfdcdb558a787d86529b5aeb4e33afd75ef2cedb8d695c09371d0edc0691b83ae0b64715acfb4e9963ea0dd327d83ab7b71ae5fdd05beccdf1d06be4cfb642c5bd9ac4875b8a40348ff7dc30f97e47f5cb73e3fef0ffaea3632109bd1010488f5eb02a94e6f8c655d4af2f436323025794b11b05e7d8755dd1f3e907747757a7f38c852dd0a5940ffe1208b751d64b191b1917132d5842ca0c7b4cc8c42adae457e732dd24196d9f31a96e1260bf575a9217ef7a14fce48178a9c4ef0bb113efaa5ba7946c0a023939f038622383b24f8c51e306c01f74faf87c68fd4271ef0fb3ba9bcf3f07a805fc12f0738d3984dd4e30c63829d912ea4c03c38c36057450a7e80dfcd0cae3ec9964656600228e6745f0421b998825d154d780193b033e28523b5ab60822dbac84113c468a20c49641e0ef0042e1d1e4a46f9b6a08626ec400d443035e420e6a0861d50178646d410030ce1492e8e67c3ce881a489086318e604fa75f5f0f0f3b200a2e96c8d077b575000970702353dad18787f7470380ecc8b48d0c75ddfdde83d05559b6c1ee005a3072239344bfac05d636f070e8b34ff3d6318c52ecb4af8d3af80923176070d105de013b235b14b1c113bb01a25ea854cb988a8b1a650cd188000000011315002030140e8904a3e18020a87aef011480108dac4c62449889b32087510a19630c31841002400446006664061b00c0497ae8d130665179b0dbc158114d3f880b3119da4a60013541de03a980b8448eeb53b3221a5cecd288ace50bc4426564df2241db17894c82231cae20f6ce9b98499589cc461726626d13613c960a7161fb1314c8a5984640648b3c3cd45cfcbbbc60ca865fc6f521360718b53b3a072495148d4f1ec6349ee1a9e557944c254bcd103e3b12d8004dd42d2ba37a614c0168e45a06a7bfcb3fee76e9bd98c51323fd3c296e675dc04d4c9f378f33010d4a4bb87346463c3ecc6673274632181df1dcd3d1445d1dc8fa7e894550b4d08cbcd6d98a579567906320914371a27ba96756c1da306e581e2fd8f7fd0636b673163f43bb5bee9b82b1c1eb893f474d4e5de7a2712d55a1d9f6f2476ef9dddac23d4d61a29403931e7f2e104d14f0c866254198ed9ce2b2ee0d86074a288b8b42eabe42fb7b07b11ea6de256a3f46d38793be08f241ee45ac353e0c733cecec1d7b70635de0ca7157f3dd63f007647bb51b243e87126ee61debc93b7cf64630b68894359601f076878f869772b3522f9b46bc23a0fb1c44d523ad8ac56b771aca25fce7b5d34096e53f03c1aba7bffd205201232c6ba193e0763a885cecb17f0553640a00b88a3492953af654fc2193fb940cf9d07f7ec64b4ef02909485b4020d56ec57862369a9fe4f4145c6f80da2df8daf49392435e674e16c33d11fe5a978cdc727dd4285df4d4689cd8352e1e8b70cd424eb6bda7bafac3e2b8cc651f6c9890e59a1eb792b4cb4b000283819793392f54932300bd241249f9b125045e53a1cc46852a71cb73c944a32fbbc0c5474ff16ea5bc34e75cc8a8dcddaff36591cfd2161bb7c017f4a9378c4aaf8fc707f2dc887df517cf8ea7e0d5971d5245f501e324b4808b5f070f4f650bfbb0c0b5690ba19486ec5057dc4dbfafc8f61661e536c18a68009d901622e640f31a15aaafcb9a0159d4385e4970c52885409f9ccd5119cf2bd6870289b2726ebb2ef334374caaa2c8dbc8bc0040ec50a27e098c83891d522de11123a45a3725f0fbf73ed89bfbe51e75acb9b0a5e5dd6ef65f0f557fc87da5bbbebf1404e9455d446e0ed7cd468fb575925f16d11e683e36265e2b98792d091348bba1f1069f68695d4a5282b2d0a9a0b80da8258ae8683e003d9834ae693570c10475b6b839cf9556b4ab6a1bff06bbc1505e40d397e06cabc9b5fabdffd7f970c1df37c64694be2335ee2308e13308b4d2eee6f2362fbce8fb078a2afe217d3b68b83a4724476696db93e0660804675ad03cee5ebc2b44278347b043ccf7b0e1cf3a6cc156759c00b8f143981d0c9867b266be8d44216ed33dabc147482711b1e81308efca5e3a35af2ec284fc348cf6f3df5df0d0df0b2573717f7b3d5463c37f27240d0d05a5b4cad07fb75f14139d5b207df7e56d2bc92acc0595bee1178672eb6ab5cc2f8c61edd5b02aba7525bcbd6c9b8c955e57d3e0f5ea0e2ddccef88dad8b23d0770fdb5d896c6aa731267855bdae10e5d6e03d8b6a38e8ccc5d87809cfef0eb63bc3261fa2fc9edcbbd65f97933bf55792d1bb9c1ecd6179b6bac937a620f7262337f35d7e079b813cae3e84bb6fcce2d56065efff64ba2df3fcc48f09ff91c7ab0b52a018fedecab16b6b0782c3cf6b8d42bd8111affcf9f2e482c9c6b814855a674b0e173d5b62806b33b8043522286fe0b2afb84032d890843fdeb5602d42f9d361e74514abab73124fe0eb8fb5ff65339c9ab94df8a370795016696dd2783e9254addaae7d3c2a79a47a4b934e4706e4e64cc6450004a65845383795017cfb1173dcde89710f3c7cd66935720954726fd02cfe991823e903e44b7a2bb7e08d383b17a5e2355c4d5726a584ad5457c730cbd893068844a45f89514b2597fc12a5940be4bb6188a1fda31ab3f3dc340e91b8c062865179817726893b2829ece032b45be6a8f39d3e4b7854abdac4ad533414a7c0c1126b7951135e94e5ceb0c34ee482b3635ceb2dbf1a697044650159965018b7ac603f8f61a0a392940b2328c876001f28a0f4bb0ef1fa4ba2cd69a9d313348e9b8dbbc29868c9b7a4f29d61a808e5caf27d80d128238f83d1cd48b7812c0f454988d61dd1f0e42508432fe2811112dd78e0c89eec1d3b1695d3ca2a4b2f026acef294a1aa5c728805a0432e1f7ee4575de49aa0df078b1cc52b10a510a26bb0eed096f396e0c32bf17e00f6800f17420effc6e2802a00e444e872c9b18c97b05883ed67b48f081075408c293b72a2129b11d0321c08a574043532e58985929c25ee04e5ab2e189225913112841e48c4cd172a3871d8721891dee9d4c7b97ab4517f57419d126f2818c5fa6105f4c3e006e912e2b0632dbbddbda20f6d0b5199f4736dc29232be202b3de04248df8526c78c5032dc2abc3dfc58da17c5b6dd03ad19890ad6fa3ecf5d4f6c7a16956dd004213ac62c185b8e1579d9ddf8b26021517f6883665125c117d347124c88f1d48abe935eb1244f9666951e3e7430b03a0068a2c3c2e14851029e48377310b310f3c0e54d68a8851ede94571c1e1dbd0c73f3d3f0e6c40f007db86d04872b0b10d892c5bcd3cd3fffaf26d7438e2a85fe63b25d70474fe56b6126f91025e07f844f76e5b8cb1c8c2afb88b677ee1286e1a03089cca87f766a05bf7a0bd43a062e33abd0cf5f3a57a55a5d9559dfb391ec7fba02d99cd80ee32371dc019113e7295ab130547024030280b9397a5e3505f91b7f82674b362094c59803adf15133f5ec38fbddca832a00dd148129b8f37d3e31a078a7a80ff63e52d68c9f67e048152330650031c3ce028ce9ee4663c4040f66b1e1ac25a6b867fe2f1b00bfb90ddb5ee18aba0b3e6b0c12cd0d66b30c7a35dcd0163c3a90ca4b26ea7c946179b17bc938e7194bfe9e4462c3ced0f871c4c1558f2a6d31f37378d144e068ce16e5b3ac15022b1aec41553bfb24cd78e7a21b4eea7006d0fbff2982d0a1f53ceb8577433cd807e03001fb20a3930329034947f77070ee6482356fab16894b4a1fe1c2049785b044419e6552083395d1aecb4f03d13ac6345f94abe08558081db56d3668cf05cf8262f4ae0fb9a03d357eeea31d74ba8bc949e1a57f5120201c797c80b6ef1b8e19faa70b48adf2fe1a24d673d490451eb0f5c76ced64d86558a81b345fe920e575440fcc9ce83ae26cc7cbb86fcb54939aa0b03f4aeeabe4fbcf29710119c5bf612458855a131017fed814f138047b9f962381e4153f783f6894210f4c47ad279888904f447708036d3d03046104d9bfefdf3d71c0fb9140e6bb8c2ff6f79ee91085d8240189cb0ee8e23a02cd18cbadc3ebf715f523e609317bd01fd20385450ab4ef8a4590314004b35fba937dbf5fda2219601520cca5ddc8e409a52b4cc58b37c6a2fb786b127a09b36f45189e6f93a802224127415a7feb0c5d13301bef9a6b9705a79391da7adc07964aa7babe043c54ed8b90d0f59735981564aa1400cd79bd6a0240888e45c83d1383fe1aef9a613bee70c973e191341575f8ff438e58310797a787c778e05a543e8d8f98f201b1ab7a8419f9b58d8c78a167237afcf7ef20faba0c7849a6818d4aff4a42395a061450804a444561d8fe8ecf924fa1ae14f4ecc632e4fc03658b0a7a4bf228ad5a3e67c2adb3bd1f90bdcefee815a05039ac7662ee338ec9c4454415ab78121617f63cf8f40c49cb675628c534a062e1da7875928fed1135fde6bb25d41a38105a6750ab6c12521a06c593d0e185d0bb4a9ed42ce69802661d2ab34fa633d2a7ac0336aba04de75333ac6c34d4629e034f3435e8221ba549ba5dafbee73dfb8a9e1367754d727da5d21816c5c0854c0b6a37d24021f5c251ad66bf65db493e06d6bc69436a2ea50efc6c3cd5d487dd06ca10aa2e995f56b7779f74fa89156c8603c20dd50d9e1545746f796b07a356c4b1bcf00d2962c9b22dedb0ca54b94ea744e3f9ff916dbc33e1ab3411adbf710afe1a599ae1606fcc59586934584d7fc1bcf56a8a0f5885540e32076c2f8ee237880571905c890274467523fa38d06413aa2f7ea077ae37f49e80b89bad7ec78d9d4f458621b6a2d1480f2d97ca2536155b2a25fee08aba43e3ccfb2a194e1206058472dd48f2cbfa3c8c14491a1a9a91df516b49e3aab01e50a7eb9517adac7e36cf6dae9461b22d8e42176a8da1605c25ac686b40fd382b8b08ed149a3a2d8860f0c1d447a6c7c6df7bdda7726a3c85c3b80ec7da1f7eb20d09cef796d2a611ff2fdd9f690967cd49759e02c69c18b50f930737c6060e71c7bfdca3be0514452271a81970003d85bacf4221267af2aa1f3694267769f20c201c4b062a1d428feaa2392dd65300c123fe47e776bd8c387a66c79725d5a5d0d99a06380a2e670574bf2c70ff0227169fba0242b843a180e237d4320d8e1030f8641858b84e082217581050c137c4dd1e14df9bee0431f1e7b68b48047e8d8eb56382680f49be5974a6ee293d06149ce1162fb5e7797df80115e0934b05f4154fd7e19f31d6d5530554488b2d32ec8b6835c4ea5e688151a04210d444c30d9067f5549761f961cfdb085478ff6278c6f3089c52d1661bc7ab30810a604cb676e2984826d6decadafc3050e17cee1ada3eeb82d362c0b1f2faf08718cfcf43dd3f31a94a6895cb4c91b268a6887a1b1b26cd309159312a184b2b59e9d8992a69dff37fff1317359bfbf66cb24453a8f067d677483da615c1f1911432df916d2165355100660d9fb96139b0504bd0eab6c6c06a4d38e34fdd31bb50bffa4a8f7c36b619edf594a7b56b95beb480aa994470eb6006f8499c36791e1f07a363fa903192457836a423fbd91d43bfc0e31388ce0ca9f864812d501a414aedf028300a0630c42d8f28453c43d443ec4be332cf3b722642d851b57753a2f5753ffe717420a4b4359bd87fb90533f5421075ecb38c584106b2f5e555410a766814d5d9794a55970ca8414196881795dd4099ce2c6d994a2247fd33b521eb1fbd7446ddc2bf8b7fbc2f6853918ff14ee21c99a2875f3c36e43e9b06e68c89180b705ec113c9c7390509c693aca7006a9d1384a5853f74ff90322c49a39053dd3f04d367392f31c7fda3132e1cc2a440ee62887f248c36adf8554dada05f4161318b42773a54da85b6649d296ecba8a29652444903dcba55870070e2c160d3ede1292d1ab00e96f55787f2ed346394a571435427e744454afc5ad52326f760ab7ad7b66d7ef05e355c6d7360e62c0cb0dd1e2137968da6bbdcee02d030216f4c0634d1890a5cfbfbf7df5c629b449c62e2897d7c8f1bac103d76ffda860dd724fa854ef27eea95610b9493aaacd0353b1b58eff09e6d1cb9815ed638d9d43dd5de846f9832426432bb45229cffb958e8b074ecb1d8da2ce4861c097c0b2e1b2c3b0f29f6e5ca9545c5e8d72ec4ed882f1226ce6b1053701ba13eaa9f015b77f8868d03bd8f5644873b896ed68a0ed8b79075b03f7c0b7f7170f890deb5957f3cdc8b58dd4787d86b74b50c3484bf87507dfacea4aa971fd05bd54a931afad858459c0a13aa83bea32de28283fb2fd2f0494b575764525d8c862da3f4cc0ec5289daddf86c40c5b0eed84924ca3019d5474e8dc436cbe73d3537bfe59dc59ae8d9256611864a41daf02b84bc85ccda5d1e17fbd58187cc50df0a7d1d89119969076f98f71d7a93b4e4fe9f6aebcb536567897c200f913a54bcae2ee1e853ef984547f303c6b9e52b116a75c4d49bf657ae04784bffeea3f62626085159ba3bf0a222503334f86ba4b8abbb7e422ac575cf16847637c30b248230ed84c44a15b0305cbdf97046ccd8252b88f21d56de3d69e725c6091aed8a99957d7b030d14385d123e17a9112be6b4c52a784f9e2d5923f6ebfd56fe84140e8cb2c17bfd32c88decf39f16ee519cbc3afe7daf4f2ecfcf5449022e1eeee8635abbbcf7e1e832eeefebb81ebba71ee46f5ca1add181be547fd4066d3320d21d6f10159c963c76699e009e8442c8a06a057884715a3aa1047aba357cec0280fde3ef03e7ac5629528da36ffa7c25698233b4d3336b3e49dc93e7c0da3cca12cb8f29f641a8c36123eb7047a0796ad44a7d1686d6b778b591ce30908d42a21dc05452b86bd4d9305c54cb18ebc5fd281dff02e30ce1b34f57333f257f121930cd95a9db867694690c8001d7f23b5703c93e89a1b402e932234458eb50a00e27bcc298a7c01f6007a59ef226e22faef83df382313f76330b636e68697522fa2d8f23ed1b63713631f917d17a2cf9cf728e514c450030bb3fc57251ad3ffb3cbb0a57c6b59d4230d3b7297d9dd473b157c1a5bfef957469b9e796a4a427e0f3f84f4bbec90dfa89186b787411e0cddf55b1455051b09366029157c198ac0ee36eb8085bd50fd43e18afe50cd42ee6cbf381d330f85e043b2eaf6409503fe85157bad870ccfa3008a7bb0d4a6a4581d40b1f7de3eede566252ac5c003bb466191215395d175d0ceffb6c9abd695963081e9028532a084414a707712f9b271f1c02c805c97374ff632a55a9d4a98463687cc6613b2bbbcb85197ce7f3b83ee067ea3d3fd38a9a7b7d074a55a89ef419a6af27e5027810b6b2f2563a2593b49c86758f3118dba894048feaf461b25a9f1de895dce1e40cb6527efbd2544ef0aa15bd04e3e686b96cccf6169724febb49842d8398ce15f0f4077cc03a5d6b5a9ac443d68bf7a40391edd2f225a70b35243da3d4e95b3940c3fa07390c341a5080d7edf153fe99a85aa400a94bf412e540eea40fe7bda3e472d698409bb5620dc4da54eddffffa95a2f46523755af9678e9fd6521f30c7d490a0293ac7a9799135da628ba09360ecf531796c1236c71b2694398251924720f00f2e07a358d6a8bdcc8beeed18c323cc4c5b19a1a3c325b8ed2680f780fbb0abcf0d33131d39e6d9635b2ad02d69106e0ba6605e8904f0fc0a57a990d80d4d1b902d60030529eaa42c25ae2567f8df1c7d284fb806afc01c816ef12a1b880af78a98a0b5894076c6daca15e00a33071010d8df93b0a74634f241d7a572224821b50135c9850fc2f5d20b8cd0fa798eb3c211b67fe46cb8609f5c90f10c060592bccfb0f89e0ff0f0907b49f3feaee7b1cac39e4d5d8cc6350e43988a447fb1d68c47d1c1186c3a2ba6e39e6b1248da2266a5f8bca81dcc1f43e6393fc8f6d6c6d2ced1f417321f57210190282b0a2ccf8d017f228cf15d52d60dc22224e040fb85580b7a80d34115ff4f2d9b984c20ae5b912f5aaf73a102cb14934703581d6d9721ac61d47b8473efc557ec877830a50f21c700bffef71c39d01d4dd452933cd4464f7fe2b5350009cf0781710ffd648121d5ba11dfe3f376fa0487878fd7b5f63df9865c0557be934303bfa39b2d4f40985470e9e0c2202e588cca83fe35d6114403cf4d1e5c9a71783c02fac03b37da7e44229b4d7b7c0bb07687dcd54d6213173dd205e2844a14491bee714a64f4d1d521f760deaf41782db6440ea5494167484f179aeb95a130d090d4f286522b7be7daa84b6f7c2426b6867c426051e38862047b0564ef2fcafd5dd49496315f2bb7765b1662b923b437e375a0a091f1e77f8c3fbab05e2df0f9efce7c523f70942e6955c939428d86ec838d80d2fc10b67b2bdb679689ff20e74a276c88ad01a8d3d480fbd377b7b97dfa0fc3f9bf752380e73a768525359a3e620ee8c85e7daf74b35bb2dec80dfb469dd939c7e9e385a1b89477fd3a064e9f8898b53c366bd5052f5de2025893ab770c4208ad99479953aa1cfc9374932d3840c1c589284304c3829359e31695b19fa8bfaed2d035de4dd86533a397dd995637351e4982d9066bb1222bce73f950e08c68bb974ad632ef75cbb106c9fae00372517e49521e6c367bcb9d4e0061606f39c11e117a226222891e3030b51139f55537bb2adb236b9acb58a690d8a13b3fe73b0e61a3eef9ad5fdc89e9d60bc529d68d08aa48f246e480e836e1b494a37969b57af45072bd044c6ad0e1d3f13bc532a47937a69a55635dafb31edcd371c6a29aca592b27dbfc83e059df98512667a9df9bb1da300da3589c25c12b6fef11ae31fb8a07c049154be6cc54368c041c2c3651c06206285a1bf672295d1501f7fa216aac29aad726a6c94a90f482b59bcf3559d22431378b374cad93c26247e2831024efd6e892d8610cecc48df3274766ef73696360cc046b3941c2d7ae6dd78b733c241d1b8526e5b3779824f3475820d37d12b50f50b1b49111dc14fa2afd6bd07618aba4657f85f6ec65b8eb6a723c75f246a813e8e6e526039c575ad31c41e5a39674d60864ddbe348628135360b7fed190e88190d6c94f8916e881115daa86f7eff89b3086bdbfb98e1930216351d0d02f1184d87f7514099c21bf66113fbf198b4cf18ab0ca8a93da2344619c73edee1d7b058182904086965e89a81761d67f8b7d4a88572872b018159055fb9be7e2b7783fa5014897afedef9ab9552bac0b89246f20f69523c0a54c291d304e67f1ff93ce519440b77f20af2990335e16ac9b59964bea1b693a4d6702946e1639ed933720acc9768579705ce628edb375e157608621b92a82527eac3852abcc92d162f40c7b1de7f7b09c48e336177004960fd3c8752a15e30ec21866178126f6e447b20665a792759ea097a8c153210a26468c2688469098b55660fb81ade35c258be700310055df1009a663d89feb1f222edb1b711adc4fefc9ff439980872c7a3731d06752ba6b8569feb47bc03c49a705b6dfc499ccc3da11210c49ffc6ad4c1fef5e156c3d4239359fbe9155710575084a6763f9cbd926498f508fb7f6e726b4b65a305ef20779e076b6bbc398c0c947cedef089919d6bc695f8f226a55cc88d3737a4a97631378fa15a570572b04a00268f1a5f40a99577cf45ad381671605d3e9d4832dbee7c30c5eb4d0230458f6f40aeb32300042c101b90d03a9f7867936d32c2a8e41dc9f759961ce0c3dbbf96f7760c9678f14a6af5667715f70d3ba998f011fc5ee7a66b24ccc0fe8a07d336235a97d0512c8c8880d1d0102d205d131cace11c0e1d80e14520bef477b4a2b0d14f8bc6763f9461c6945958971bd4ac3d1814a300e2896169d58d7fc90a1904e74230e18e138ebec78a4191d1a738f92fcb20be8a10a3e062f13f9a996a0c3fb8f71f128c9f00ea6d2c0ea243e73673baa12f78314b4cf6f1480929c52c0f53be4b20f76288f69e3dafcf796d844aeb699383e4f0896a9a34b3925a83025e52b599da432b5674eb8dd68d0ecb312eec47a5fba4cf841b47208ce2dc44aed4512e39eecd2ad2198c985501341e1a3f3f0b49342bcd2468e9222a297191a56e427858bbdbb388331990fd236101c52e3c8ee94ccfd7235ba83b08b0f0156243b8de1fd63950ffa8db714cf69128558b693d18cd754fcd46e539609e186c640d412c789b528f24310d1068c419ccf92838ddad3a776a351e6b8ef36a41e2dbf36611dd1b5414b2b2ab0408041c3d22aa87324c51594631f3ae99b8e9d31427c653f4606867539328c8aca2518255f332701f1ca07037636d765c8e20468c5f90b63fad6ae5c4026987f84eacb532a33c67d0fb5ec473e6c93ea07cf1e0923cee427ef3bb0c893c61c26179696921730920e691a34ad451da011944b88a0929bb2d356dcfc0600912bd9f2978a7028ddeca7359d3c03e6f51641f02013efb89906509618b1156f9202b725dad37b189e6d0929a422f68351967bf15c3eed8d628c4e42c33f90aa9436eb90a337f86807b52ddceb117f9792bbd1909cba949140ec476c356d1e9ec412b6712535d86841b33261ea270548e3f5488275357f290e7b73dce2a39d616986ce2ebcd2d1a3805ee1b58fd3cb7d096edca7a7b0f585a0896fa481b4e566d662d006cd0c33bb9a2e2533294d006609962e769211a70debbaa80d63518e331590dd100941a08726711019406479274b1f1e03a7300de04a4020d8355a55aa4b33cc9b76356b9e9c88728d83eea7a026c93a1474804bdf3af2b78786373209424418fd2d1129f8031f84727cb14d4d718383ae76b2bb39c9b4a43c23fb312957bf424622f5c39bc2bb2cc0e58bc2ddd4179ec1dd6a6e33a52fb69915d40c9f399ce134378040d172299aa0d1e69e1709ac6e273f94bba512907ef1a24f443bd7cb916f06ccaf0e845b32ca10350cb06a1dcf31a0c56125d33183334bc931700ae46a3813eab8d3f322d702b9216a135abfd1b86d1a3e2bc01241c6bfd732014940a0ad16b214c2bd39584be9cac0e0945c2661238bcbcf92b30c51ad565c394aac5fd474b0a2cf6dea34292578933bdd37d592227559e5252029b28b05d179faf187b315a1b5a403973910952be4071be5469989f1d84d8c3255adf09c46339cb415d69d38a059e2b997a097eff749ca34811129c4913074701cdc1e5adc047b3c9108cf911b503ecea378cc34217de5faea035ce00da395517a2aa92ba745a9558ef9269f46b0ee7cd12182f8c306ccd29e0c7385bb5c90bbef53213371391a969b0bd3c4d7955223809820d063bb1bde3e2a1b453cb816d550794776a4f7ed929bf8ab453d67431db54862791a728c9b598dbf14460cb215b20a3f3ba85a879a9d35591fea6a9532aa0e9949cb95ebc3ea55aaca54c8657578d1402b6e013dc3b5d46de350f9295506d9ddfae5af68abbf50a667dd3548195a070fc86047157637156bf0ea3edb9bd1effd3b6b61bb4d77d224e238553949bc0019ec26a049c87bbfb4906cc55d305e83815f08d4f1b13de82cb06fe7146313d799e607cd7dc61f3508b876aa5681d96f44557cb89038a00f6c12d0e4ca5902af993aa251aaec16046a1c34447c4f45c87fa473dc961485e0e2c53e42065b31d65a2047fa0dcbce23abfd1276becbc88402d0882e6d1eca20edbb00c6c517b536f7f0566f0d50542191b054abd3179010bbcdef0997154ea15d63aafe9917a3d64a04a166df94effa807e752c1bb84f4fe6b20802b2b6f982553a82b04e8d4c2907b42a608804d661b813c650aa6208330b7e7a6321e77cc7418aa5f518a6034fe930368290e2c613af4a054fb425b13fb81708ed969311b835bf84e36b8a11f8312e14d72df746206dc9508c2e9ada95a091b087d09decd2889f06bc052ba6a46f3d3494ee9686426b660d1c6f7879e331f8e4da5bfa826a4246f23241550c99df7e3b40f6e182d7a3fc759a0bac5af61ecc21b3266efe30d2d45a6725d1cf0694c35928ecb27a6a3e874c4b9fc5ab6056e85467edc49ee96aeec720b11f25ca0695ad11c880832e50601635558b527c0230496b704ded2fb225ba3125bf46da3127c1f0e2188737933cbde7f8b7986187fc3c969121253fe49be7b6c4b6e74a2de78789ae7eb32f9c91e9ff51c0dae1703a5e2660ce9564c90f816c63c84d35090c7b5384607859426990a6b8d5d523b6f828ebfabac25a09a37d25a0a379944bbf6783e31cebd410054adf287be71d4e4b31d67e07443644d43c7353104b1498b73f9981296cc1ea92433000755d1e0e060c9dcb9eb3e860761c557cabf613cc1530c02af32a832dfd81d2b04a909952bdbab4eedb9e7d20d3d94c0faa9a9165a587fd1fe3adfa4b6c9d231c92cf7d42b3a08a8297ef497563076c1d679aed511cf4f663793c53d88409df3744a9295b86a9008ec87371a4b64574a4393d293e17e7a23473c324a10c50ca9880e779e4d5c10b21a62b0fbd799c398762245c4542b736a3f2650f7c91032bc6e852afc097e79e479bd046d3a2ee1afe0781a33c61eb8820989458bcc5c0f30a6b5c3b13d5fd8e14ae40868b6a1867c3437ae75eb98d183a5815f71596722a41f9acc28c7f924abc0d74ba76a8a8cb8a81d270cd1bcb653a94bbc2d27846a61500c0ebb712831d4367370c5f601e26072a9ef33ac93c42d9beabaae38a11424acc19866eb056c19cee297a5628d7948592bcad59accfaabca67055da5879edcb659d317b246b69e071c066385254435ea0e996a14606e85c29b4e15d6f609c077134621f8dfe7e4616d6fc30e21ed79f482e28206394202fabe82a70abff9fea2f93ffa26d4f416336958f1f36ccc594e0cc16d4867f3d2bed57cc7f360931558bf9afe6a260ddcb341a7e34a872e9fbc0e378b86d0514ebca02bf1c760909825a07f888bf5828b4d1a8ef61085989907fc81d69067b284284ad2ba84fe3449a8275c36a006bddd14cafec9a6693909be0eac1e1e9307fe232895fcd4f11dda651e52f04606c42f10041e23e78b90f16ba9359697e898c178466bac2dc631bc1a42b61ab6720f77b76bacfb124135374e3ef9e711b4d023a5f4e351f2c574792a47bd74d8561d66f3bff15236bd15e07d28e769f46c21be3f2aaf9d8ddb0195732a80939266dd69c1d56eaef8137ad87f473ef6e4c9970334be96a6665ac3a11e050265588b09df816edb734abf11c83c19181a1091090f7a9ca628ecf3bfe304a1dd83841fac1e02b1165aeec6fd8a237e41bcd782ebe7201aae08f67d12b4f363e0204bc6b028ebfa6dfd3a34688be9168f03574a5cefc11f17eda18ac7c9795d5ac4a152947cb407bc02c54b97729b11634b56fc7d126accc8c2b7e405d2e3acb11d85d3605c62ad07e85790ba6e6b3611f8b204008871871858c598c0a9d8f96404c45d0971aa433507613102dc55f01c13775da35cb20c82b5dfb207c54922ceda2f266213949d3885272577a3428705698fc5311de87664a4350c82ddfc08621574c5a39f3b6ce8b8a3592d4da206d0698d7b6b653a845331a2bdc44843be90f5d301489db89166fe815c1e7068b81f3f57bc81e802323d787df5f2df906ccf7530e13496886b3f3100f132ee27af3cac7d262b03610731287d7e434e5658351229501d72821c41997e2ebf075552451e8ba04bfa6b020f927530ce630f46d3023ba087c7e5642a028d47f70e48284a33a8513c3dd6fb1696df05b67282ca925ce3ce4436a0c616af995d6e295d4e5b337208778fac05e70856821b8cac38ebde6ffb4c02029b83a9200d176a3b85571568484b8c0ad7dfe2324ab318f48640112d73dbb31d6a5c1a0b88d0314a51503f954dd0a2b113583aa060ca85196163388aaa57615763f5d82d33b6b51c9848cfabdb397c382a7369262e25fd5481b096c5c67ca6e3f8bf1aa097ce10df2b4d596673c09c2a07242acb91f769e40f1d5e8b0c3b70c2f5152d358f4ea61fd6a22379c9aec507f35d49c88a2f89aaf36d18f105ec8a18118bf6106a33fb09ff511e829c3867c91afa4775d9ad6e9a12139159449365bd94d42422a32853c4c5262af636a266378e1fa46ab8906889d6173b3cc184e8288514593038bd9e90e5841162e014ee85d247a6f0280886480688dc2908ae9307b006f6fe9d7feddb73d8e19c6c7cbcffb59e0bd0a6ed74c0c3fa0b1e60317fc42c5aa532b910e34331ec6b1450a2ab9f2a8694d69175765de2eec9303b20ba9305dd0d665ef4d773e1a2958b2971a45c1c762059685be6a91a3966c68f2fb4f1890842c3fc956a1d274d48edb3d8e4712996aa000ed2865ced4cc7d0d4f5979b1dd219ea74fd927b8e5c49b81fa9b13f53d075151e8b865b017810c51512c20a6ce53d15fbdffc411b6502051a880ac4c3701bd8f2e47858984ebcd519c900950d25e52e20dcd3a0207c3f1fdb30adfe70231f14cfe6daa05877cb2992959f4864cb0bc4eac32a4d5ddc1421f7b9c42f9da7e96a98cef79be54f39fd9513dfaf1432c16ee1e6e79acc5a3ec17545ee1445ca247c73462bb13c89bc6dbe1dee037c014a3f79c011646cce02e74a4a3612501c986a98f8a865418809dd5a461382b9e1832f637d0192931411b33001bc36ab9da3b156b05def7bbc04fb6297685b1b86b4b2db98aa962eaebc0045851c077d882ddcdc79e55d8887cb941d29436bfb458830d0e40d7fca3acd7eef4d3c82b9fdc2f7707c3a0272f0119f5f4afb94ae46903769ead6a06a238ea9495f2bcea20e5ed3f353a37c64b1192463f1d163a95e5bc53cb12b5327d9c602d2533baee979b38491886e626e4c58665129720f2f5cbf7b7feddb3deeff6dfb30092220bb632cb475dfe1e9f4250612fef277f0d5a36a99dece4879092f845f97775058c8af580528ab5cd8bf0a120c8275d70c9861f0a6fe02784cec84c1203745fc87f5e57e34650d0d1acbc4186c3e9f1dbf1ea1a171174d3cceac37d6023bbf5b75806709ce9ca4aa89f3074468b6e6b55c8a587ef9394395853cb003f4867fb7c1d107a0189209079b2e3465bf5ccf75c5cba13840cab4f8eeff9f6b646327e7e227bbfbc165519df4058eef162a78abd15ff874e0d392b7c7d35e078b83c04c878529584392e54c8bc9902bc312162d704c5bb2809174ebfab77cbd00a7f6ae59e9f00a7b794eee43defefb7a67b04a5b165e8c4eb406f651843b6e2c8cb97c18a2449a1a734c6613fc2104705111a98f9bfcc1d5ef52e2286dfe72563dce9ba0cccaf6ab4e35bd9c1a2031160b262fd609619766e5e00a462fcf67b77511504468699f80c3502e6e8540bbabea241b30293301f10f533f0dda50f347db910f5d66920457a057203fd84c7f583da1fd75205c3d24042417a17734c104518f4dfb3a2758c61e830b9c53c987a9cd782d40f64d53b128dc49772d559dd91ae6ee54fb43f4122b8c12118cc02062b8a880ccec7802dfa045fbe850ec04186809b86b07e928198ca3f84934c7db938cee12e79dd1bd65ac3efd4c0fff9ea69738f54cc915cb69f5dd222d70671df26cb59059b5bdceed523b38d366ff64d59ea01245c824f6a9517164967fde2ee433a2e2a9a864442c7a9cdffbc42f6b9e0fcbd267a39fa7e8bb991541aadbc3f5b8d85e90c7d453ad107e4d525dcd66c7cbf88eab323c9891e78eebf291d0016419a7e4e7449ffbca3c4e6e6e1bdd7c96226b3ec3726c4d46424c2ce72225e1d2d4aebcde48f327eab4a0df7ce7353d4a4b9903abc196dfe033c5b133b150c5f2174b005d2b60a304cba07e8762097309181b9ae3413882b85c4e7988beb3f9f4b9f7e947260fdc1e2b894d206d18b7d89926518b989f115268402f1da14ecd71c70d8748a5cce375a97c8be5b10adb784e460baf14f064f33153ac85982ab2abb14698083bef339abee8f3e251f4c53f0c23ee9a0a0c771756940802a0a367f4ea83ef000e924af6f241178adaced8d405071b01d5053da16db08382589375373e9a21d17086300cb118e3d359215c9d3a92ecee0280b24854d964288fae1e9894b6e7752ed9ba085e89a1934091b8d11b2f8121acbddb9e480a03e8abe62e8fd1c3ccecc3b3f40631e3e942351de038cbde8780036a5509ace3efa7b91dc8a4592e98ec9ac39386393f1d83facab83923516dfa605de82bef59a28dfa0fbb1a7c6cb1e36c017948720f6f655de99a14a81e47b0281936ea41428220056046abf0390c13bf8153bd963eaf750e4b874ae87173300c854e79f94d23370a60a527e8695458e0e714ce71bf1678470093af4d6b67f4955484474627e24c3f6cd1c3ac3876c28365684e5acdeae005a87d237835ee886cde76f8face187bf190de7eed02b396093560add81a5c9bd15d3efa1032b20731d296ce64d3e814fdc11051af6512792799d46fb605164b886ccdb3e242c652005fa74f05e5c879c00da0076ce6b39ae79a358e2dd30e890f10232f7af6878409069b3d1d19ea7c3b8ac59345df2c69120e67b3f650ebcfd49cf1da70283e51519a910578df54206e1a09d5a682b806404bacbeea0aea27abd1a3da9b46d799d59dc0c1b7a40eef932c141dc9c25e95abd8eba6cae46329a2dbc68eca6b088b08e3a4445b7f1c74ccf27c8bb0633a3bcef32211b1c0ec1bfbeb3c5a710071f37f334d5d1db3c6383318e1aea39acac88469ac18639bec385e61f1268262b969b0692e0c418011f0fe3f3e334d6da211b9079c207b08a71a8011a339e59f1ec6d704da11a1930d995b6a3be1e9fb79022fb67fcf274dcb4fbf1bd0b34d252b02aea7a5ea5e7eb3c134241635459e9ee26ef2fa4dde8043000bac960c2798d44b9b07415103ca903f11eb7283bbe17e71e2e270b9109ceebbc45a54f3a30714b57cd0c42ed089a5633860f8eca630488d8d5571e2708d8e6118240f5a96764e4ce5c8422a80d5e0cf43210b85bc5fc39d10fb077ba9d472679911220152ad9b6c20a94c5d6bd657318eee3fc886eb0c07e0cf066cfd8782700416dd03ccddb90a8fc186754e8056cc523bf758dcc3cbad14ab068a0ee5406cbce7598d55501a414826b879565865f481fd6a477c839b6185c92cf07b0941efe70e72fe07ea27f1f8992ec499d958a4e1b4c92d0d9a8da1c4e1815fdb38b72f8c99e9c7e0c8ae67301b0d7351afb68ffc6e0529f96adf20f8a6787dfd407f63644e8617cfa210ba55ea80c5c9fdd43b344af800af92a3a680e1a185c1da4328448e2b9d0bb39f251038e46220850a4b37e7b034bf11517818a3185893a6f6d509707baf57c4bc3b9ece6f2e57157a062cf8d1f7c23df248a4b14f9f94e33d4b0c631a0978f014e08ebfd9528bcefc4a2184c0eb716c1ebbea3ba6a0e1a0f2b3edeb34cdc4d79f33096168796c34be75e1d718a302212157ceb5f3754fe0022f8c58d32806018572242fa7efebd52fb3fb73bbcde4063108c3e1269a3274d842ea880f04e56192128e669c688deb307380179ec25a9802ea78ce53088706c40dce1228e98c8e6faa2ca2d679cba48f3807b22242aba8937f165511d6929d5858705961796e97c185032213f3957db4e0337ea72f9f9228e4f507ee667d6b7d6631f38d188535e19d925ccb8eb20ed2ec45b410c6f6d0399bab323bbf9588a968d6eb8e0bc03e135c63d564372f5217dad2aabb1230e5ad6c93fe90a9ecd1714afcc580da5ea29100ced67d1c55e0aeb33efb1599869ad86560abca3c0a9228612a4dc8dad2ae31dd079ac34bc1cc0d4a5986981b2e87ea3ad27edd6196ac72de8a022db0ac64c4b871c9c4123376f188fdf32ac0951a6b6407b59100ea6255d4020e8162fb5122fc2bd0fb4d5a95ed5e0482c6600c52376ca0680fadcf70358a332f643556dac3b31d048e934184407f3202a9ec7d5adb0a55e2d044de040b5575628f898361746c5368c1f18c3aaa901af6e3a2fa33ce87b3e980e699cc0991f1c12d1b0b0f204fad75ec2de54e4d32f209984aa068f25a0c3df95a004cdc7297cfd1fea1d6d02f985273ad2e8133ad8ad37bd1c817790259fff3e4ba8d7bed6f35adc18d4eefb96f2b4f716d869e1c01602f32276da30e680b1216290aece63a77d682680414967206437dca760d0a01d5144fcf5270fb95b20568d4ab034380540b9f8d160f8857b599125c2686246004fe25fd2617dbad3be5960752c9ff2d5dc280a00fc139fe23077cc6d6023f34d266406dd52f3f7a8718da2b6e424d4ffeee954249d4e56d414cd2ea334490031fd3b5f192eb13a78f149526ea7b04f31df046e062118a0838e73fbadfa15de560e350c1edd891a49cbef7e25b6430107c06c1780988981dffe0b9ab92d961c5683f560eaccdf819f80ea53c27d6afd6e82f78a06ed24b220f25c51123dcbafd27534732fe6ac87f01e2868049f873f38e4d4715510211ca2a96eda54fe446b97c07adad887a3c9c95b6a2288afca599f672dcf9ea03ad4f0bb40280bba768a0f694b806b20df86bd59058eb9ae62af5d4657de09a41918d8900c20e37d35eaa70867394635dc318cdace713a71651382ef128c0000369e74677e95c01cf60c0c3adcaf2bcc74eb100c4db0760e81ba00a0522e725f8079fc777ff280e6f70f3ace6c474477f593f3eab0502987e0e64c4dd2a5920c126ebf4338354c7c09275511d947127702492daadfbaaa71f5e9f0b55c297e8452a126fde37d501aef5d8c97125ebc8f7436c619501ab2ad50b4d188de6a83f611ac6faa37cb97a7c94883639d8c59ea58568c5f4db5252dc514d92066c9fa0783ba82f85d094d546cd999d92e737c963d6a8dec5c1d5a4b590afb7aaaf815aac8060ab346b827c1e48023e7518ed1328c9fedf6ba216cdce652b445e705f92f662a0cdca3dfcf69639ba0b37c7ee6ec6856b8791f111fba6a2523920d6e7f21c86d4b5287362c4e55775e43ffe77244f7a54d9214e3d7b7c225989e536c81e8b2edde3d08b2f8159b7e404468f73b5ffc634981026974b92c1a92d749b86b0edae1cca881c95c8542a786193f014b3bc870982efbf8dde1e10b7107f3bf53c8b070048e6492c566b9f78d38c0ae0e122346c5b264fef61da237c11dbfff89ae988da0245267d3ff2fbf80702f81126a1ed7439810c26c292000b7c68b81245e08fe0edcc597199e05dfbabe2e603488ac613d2ee93a1535d5e562ccc14da4b7c8452427c2f814311c59dd627b43d88821e429be44988b45cda01e6a6ba14288f01b6d657c7e6569d86404eb5c5f2bf479c91bf4b6c70cb11ed1d4940cde3a21923b955c647903a778620f50d1e1c6c753a8877a29406b6a0a76dfbdea6b192fa87dc314d50afd14d2488147a75b30374fd0bc41b100ada13cd01f645d0ec1564a8231527e9539e696ab2450d817586fcc12007d1d6277ef10ad5aa51621f6236e4a0e10206d5b934e3a26c26a7d7713215a369c355eb032f178617d98cb9c25cd011dc840e149650ace3704c377679f895580c376128a4d26b9a80bfaa69c3d49610a6bb05c3894160f550e5a6d33dcc12808052b521fee1d5cc774b5f0ca1e93d4bd0bb841dc317f77297c92e82b53c321aec9e0463ca35409e71deb668dd776e139246aa0de2ea46027593cc52276b4c0cc6abe70ad7f2c3bf0d6dd0dbda036a1e2cb777e16dff3651bd4076936912c2c1e54b5e4db0b88c025f6b0dd771ae9f25536f9d3b8e2c10fc34feb9b78740ee101c4117af9513f8b8e5f130d3f3de6944de645191d94af4a0caea3d88b972fb6318556dd2ebac5511564e00eb8911f06f14efbf4bf631683399bb51a6cde836c451e6314aa880546a55953ff66ac19c27c3a176df6b064cbb140348c97869be3c2893c04176644348dcc3b0e2d19e9bc667646dbe77aa84e27c300c915544baaa8108ec252c1189f1a5d0148d0b1847068d39650bc6351d131cbee20e0924521dfb92b710efc940b831e2b4dc459bd83dc011cee13f7577d016bc8eb7e3e445d91ce55c9dd586a7e129dae6f6449754bc3a1709249710aa13f21f1420e266761f3513e0bf99309654a569382f24f41ea64ee8a50fd7d8f30e79ef5d5514c9d13ec114e6eab688d2194c2de98bb885dead08a53067f94832dc6d82eaa36f9a0582c1c3ee18760da99a914c7d765286eb67f1ed3060b8a97fd3c890e31f60b51554db0385c7c10202e7d0a55326d917d34c119abf390cb7351c37f918be8b2addc15ce4c7c9aeeefd439e1677ea17d51d6a7bf42fbd3433123e3f760704f4e61b587e2315f58ad245b252c6b441bf441d1e337bfb1715aecebdf2868d6a5693176766c16a876ea4b388c7d2ec4677ebc3d99d1470ddcc3af141a2b695daadaf71ebc3de7b9730bb7eb79d49934160ac6350fcc70a466c2aa44e408e2cfb8d217380aca995548f62be452c4bef58284eb10d7183006cb52d514ceb9b2d93595e47e5d69cf750d61263251138d3be04a0d37359320fa1b6d93b5ca4dc68dc379df8cb28044c7cc3cde42f0f03678917e33b61b3981521e83c5721795e4bd1c8d87c2d9a87eb3335a4c7e434fb2d29f64dd6a90b349a4bcbedebd17b986944c936da8649fa640580d88dd08e99a3c95571120978a64354bcaa0e152330f3a40af99b54ed07d04e1835e6c13bdd26cbb7f18717a97573509a37b26f9fe0570050fa5c7703458960bc67cacf81deabbfcdf4f7c2dc2324f4e11422ced34d727d5e1bf5c60d43c86dce606f3c6970f2bf61ebd48fb8bd2d7ffc41b6ffbf3c0d1e6f32ba2436601e93df9ec02fc800bf54aa8b620a0dd0a40ab90e8cde2c1e7a333503590c79c9f1e5280390180908ee0a301739c044cb3ffe4412cd0103b605663fe29bc78a9c24f9409ef65eb7c188d28c288e6c5d0f17818b70fb73bd7fe57170b3923c85d4c022567435e697140c525a3133a28a6ef0775101765f4ce70b58e7716fa3477ba543f924475e8f2ab08d053754fbd78dec0eacc6739e64f9d5b10ab1653fb3cece3c4c420047ae4b8dba48ba42b50035baad037531e89de359b75e4637d743329437906d56b42f6331166dd6510a1818b51552b6b6b770089f301c2640e2cc937889b5683a9e01c5e53aa88a73a6cba95c44f9fefbf97da93355fa39bfc30064b8d5276b3cd5452cd7b6baa4ae258778261e786644ac31964edc1322195b38a9f74b74785a54759bf591618c3799abdd7d78d0754666fc4b2192d19bbf7ebd6321597269d8127efa4604800fcd3130a3ef69b8aa71dcf52d08587500c2fbc52142b047d24edd98dc68da2de097b15417c0dfde2bd27ee3542fcdaf48bf44e482f8bbf7f5cb10945f8a463f50ea84e18ce538adfbf4f6e1d372d61ee735b39b4b18fe83cb93a99af594068d142079c26ee03f95610aea51e854b5369b8ee22efb52de3993a85769d21f59e28bea9e14b0701fc7b9b0944bb1513dcce3bd5cbc561efd30406096a5ccf59372790b5decf085a16680dac16b6ec0192299b31eb5118a071724d0c9db93180feb0c3b3da0c6e3461cec5610aaad138887ef5d06ace81048ba0be9a5cf76002b86ceebc6d33be3a58b0cb01d7012eb5a561047c168464b4811c424d58861d047aff97baa7baf20109e3eafeab330c0d567e6586005105bbe4449dd45e3f7b207172403dfae469388945454fb897c1f76a7786733cc600b3f9052c5a73d8bdef08158e5466af39413df3145c776a9e3586b50105b960174c2cef114b1a762130cd0a1a15be621e8049c8ff448212b6a70ab6b3d6dd3d1da30c1a9e5a5a1e0bf166036be53846831434e4d7460177d5667536fbc8a536675a1040d101cf7e93a931fa182460f2da3811c99d1d24efe4e212aadf44d74c480b13634f2188c498d2dac0d2aea0e1180d279b21f35e15d1368986deff6ccf089608cebe704d78910a5ac478f442bac79a2bd6851c3ecdc5ca364b02281e44047b10ebb59878b08821427a65c1cf9b8e001fffe98b040f19ee9310d53328a50359bb0f7d497a9d548dda248bd047fcab1be8fce58a936c90e7e88fbb4f434459e47bba1e3e6347560407f5ed9cfc36915e003428040b249bec7f5ae5b5ab345a2a0e1d50e798e730a7872eee6e488c42e9241b526a3cce7a59d84fa6dac636bebc61a3ba425e4e68d8a8c23a6e0dca86b118945e37c49905802cb0aa4f1ca5c38a1610ef9c199ff94604c793e7fe4889e8b83f13b700d08f687433e48f2fd3609a858b7c8b096dfd3d9bac653e50f5da61013f3018303d62a4e470366e71c245b2951a42339238e141abc1c3b5f0be73cefc5f67501308623dc1308d83aa2dda762dac0cb50054344eeb36061fb3664bead35e8a193e86ecf62fdd6e9134eafc700da10edf7d8f1e2ae468d357a0a507a73d890efc81b93963ea8db641f680f8dfe63e25013c7b3a38ea171740a086a8d0ff392aca79175c20b826b331d484b72fc90ae9df044fe714177df48467aefb3be47baf99d6df926ea4629887d52cc27ad31a0146645471aab2da813168bac09a1d2aab234d1ea6dfedbee0aa6fa3ca15677d9b678ec559baeabbe38e37ff24b89f34d883f4b7b6fd8a87e39b89e6212caf331e70953f780f762c399583437c95ca571310ee7138615bbec210f0d7710304244b3cd920a74e537eca85d4d34640e1da77c0b57614de382d18249f4c445f1181fe9c2122310deb0f0b2d0391976e16aca4c1d3e3137d6ae88442d01e5422f2a164dec9be2a7bbc763b250740d98487c4613e5f16da09b9d8bedded209bde1de9d43c6edcc2e9f85be3e1683cc4c64188e2a4642f248139353685c26d1eb0b291779962e2d6d3d53a1adf1b745743835e1af588495586733a916d48cb880daa017266aad39045c31e27691f79b5bb6d6bda723d9fd60e04f095bcc83668cab06c0755ab3066e684c443720939d8e8522a025ede6ba613d6b05d5379d6a78759e9803edb29b32435947919d68b1c724149216e4a10e38571c4a825e590135a19f316fee463c6d361f554fb0cb352b83b8744dc6539fdfc73e31c65458b00fd47310d08c41f861748baed1afb3f1b1770aa6ef1d69b29bd854c7c6ca5f00309de64022d07d82a6751646ced3b744f4e2fe340db245cc736275bb8f6401655852c5861e5216a5ecdbcc3d306e66f8a54266c3742de60a984093e7855584b20bca97202e7ac7885c0e60a86b247ea3e78acac0041ad604e374ba0d718e3a861ae9679d271742826ad820a5c39de7e65f80acdb7100eb4a53a26575e30fe3d53aa5469cdfd3961972db4ca151ef905b4a55398888f0ccf094041fe3d5bb33121958f5efc3b6464a088cc01ee1ec70b3f3406dc72f3537c9d2fa174029815660af4159d17eb9702bf94f22f18d933f320b29afffca3d940317e6aed25bd99801feb588a7b244e2575644f7e29267476723facb3cf2d558145ec050ae81bb885b63fcbdadcc6d98fceea04e3bfee46b434065e0e8faf48d9d2e8ce322a718f8c9f18a9f19dcaebdf16857ec63a4f069d1ed0a251e9c2eb80a8b59c286d5469a42ec4cb9bd6864a42514ca2cbdecc0669e13ba23ff607e780725793b4fd165087dd4853e61c22237f42ae7801b95d082efd4967a16f247eb107926c56725e4e6a48b1c334208f8551d93020f4e13e43d257132a1523299ba810819872a2354a8b92358b3516291f7402c53ab74c2606c01676bb17a7671e30e23d55d58920f2e52308f6668014f18ffd6688441cea2a8f5e8b6f3910d5ef85968163b86b19a36523777cec55ad10750b5f42dea88b901f7faa18f0cb92470f379b0dd010f09c234121b2344f0b305822850d0ed96328c2fffae843755d432f287a44fc07409765910900039a1b673cf1944658ccbf3381f08181c209e9ef004dce543cf0db09810f317a374bfc20ab5d2c20f118095ce1678ce6f382dddc4898f711c7094e8a75c9cc8d41617eefe288053f57f4a07b70e882694e791c319cf705cca51b371a2cc370f935d33bce6da6a1ab2ffe807acdae95a424f06445012b52ea7a7523aba04a7d35e7900ed4b087a1af6b2c68746bef8cf21cd1136277b85cebe87329a83a8561980e7c1a3e2a7c380c811e7c6246cff78f5269f55a2ded67265680fb5e5400835929b49a651d5c9731be456bb5feda4929b57ce72e4d7ce956fff1eb47b80b1207b18130b93c1f74ce4cf5020e70e3fba597f80432a2741cd223ddf81b0219b6ec0f4c76744d52ada2a3798ad469f3c59f1a9a1b1861fa6762e2eb993fdbdc6c82c7525f89c6ddcc7ea5447b14eb762073dd44dfb1a8262c9724dcb26593e95ec742d1aa5e0904f05526cc8797c06b5a5d325b062483d77f4b208e7cbfc669efa20c66abd025c29617693061db0920f28c4d0448a69e649718f7515c9077a6c18b115794e7a931737e214b5a6a106f70021162f425c9810ff1f759109abf450639224e84c642ea38df3595a45cc5086e1576c91d5a022f90f22ed1e3c3074a648d7985147f13981e5bcd06fcbbcdf15b8ce574e0ffd5f0618466c672be487da6b6ff08cdd3135d17f50fd78547657e3e5e8bd819a465020d750018174efe37583d2276f0f21140fa1188619239e51e74c36e6851cc35cfb5d73f7d30b3cc80df11e8cccaf361751e991bf03b9e64f0c1d5e6a409bc7310d6f2612b0993e0964b59bbc1eb0dfa1bad2db7329a6966b54a4bc40da31cc78f5c4c763de0493231957aee3e314f69eb9120391558dac7df33878e98f6c65fbef0eb711bc4a64b8ec57279fba51aeed2386e741fd4973af8c57f5a3fa8a3c56c23003db6a30456283d7e15c8ddb74995d8fb6f5f3fda6b3859c54bdc11a38e79859f9bf23a8e12102633970550c31f0d2b1ffa4601bd4591dc198393fdedcfbfc4ab86737d47dac411270a6877b7f43fcced4059788a98419034efa0935545f0d3710bce0052618d8a3247a974df6205bf6a62219e5e1304296e6f78f61c981b33d1204cc765db3394f9b120300312fda7f7256822a5f22b2737333500371c4f0d398ccedd0a37e73779fd6168c179a375b75af6f3e87bd1a0810be5146754aa34770bcfb9a69d77fe729ee67c0bca84d4482954b199c286909ec0db935660a74005afe1cb339aea226d524bc2e3601ae86a4b8fc62da7bdb7fa8865bbab955832764d7957acfae9390b8a8efde92be5067850fbdd4803ecb81ac3eb0c02f0749ef0bf9a76073f8f4f74cb0a0081eea7e745064869052ad5ef162045f76fa57d807da839ee551cc90d1a31c5bc1fefc26a6f68efacb445d8c53e83656d0395192f2fae66ea3f7428569a43757e5965f0d0db0d5f3a464035bd68575aeb5e86ef2b5dfedaac28bcef4bee48a7c0e968136c50d1bee09c0cd00e3873e682816f62ff009cf0b4d5e775b2d3ca145000b6be6072379980340d21a57574fdb275dc02be0d68e2765c74a2933719fc3bb7acd6481f894c44694b15087c7cbfb38edb248c1430b1bc6c31bc9e0149387fbeab14fe04f58ffb08980cea3e8963ddfbfaa12c39a4531274c53519990f85c3c6ddb9bdded671fc8a2b3226d8e9b631e670660090ee8c1b7ee778808046735a3172bc4f84a909e8f99f8b20a638dddfe715cbecd63526ecab86b0be8e2315ffa04795967457ecb31f3cc79bc853db6f57b1b893e348097d048e6fcea3212f7939be3fd2bf729b6b9b4d03291d5440f6aa4b9f3800e3ae71a912c936e21045fabb3ac012daee51c287d862fe6e9bea068a8282e4298c5dd5d700f1d186d66d265083802e3833d22bc4c8bffaa7e5a13fdf0d4d8cb62f3bcc2d082d0d80686bad0b22bfecb6d372d9a23b3558cb21204bbb71b0391bff29eaefd7d3f9ab6c246ce90670f946ae04cc7818404670e479602162168a82f3b5633df361d60e08631efd97914eb0c6c4a3dab269be6cc1c254e706033ef55d074318aeeace4d0d05098914136e8b39db318688642d515a41f5a5e9926316b8e883301c49013692aa450bb725e5607385a4d296cd540ad7cfe8537d5bc407471e6247efa5571c79aa403ff88fd0c4871f579ada4a7fcf2b97c8902a93cc914e7b9a9278d53980effaf8dc8d352675b24059f16e887d9695f4b6dde500bf4e4c9ad42d9ab94e464f5f0233008d74dd500077669486db05246f0940938c7c7300384baff0b080131542f040481be06d70fea2cca91558cbd6d2b3518848d016bc9090b79a5db829c61b555c4f82828b3ee22345b7ba32a5aacb5d34e7b6d19d0ac09155cec81296498843fd988e3249721570b9d01d051f8e1eaed0822b9dd9e333fdd79e650abab159cc4c5ec9a5d2be567feffe728747480d616babc7a47f3a8ca3a532fe30e9864bfd7282e87644854999c1dbaf169f7e5db37b4495c2ee1e5ba3df90a170a1a7944d34c8e473c3e4903adb0da13327991b63b2a599b9a8d0ccf685c8ec10f77f694e7cad8255e91c389056ad3d459caf7669937cd6144bb273f004b95b6cf01a4da2dec2190248c272995bb7bb2b44c096be66a1da0facba20b676116d610d5dc44ea690cb29723b039b293cdda211f844ba9d350aff9db605abde98cee9e43c83f33e8b057559e813f1e5cb0b0b02a4c600c7b72e0bf3ccc687e702b2839fbac06127e74a0bea944ea69ec244856fb7757fb2ae4435f130144ba96ffa46e1439250c6641076c7a8ba27b4043bad1a8ae37a99499ed0ddeb7cc5574fbd85ef411ba2a3b461428439fd05f9d39308fc2e76f12d65dc01530912002f8094a9f5bae6af3cff7f485ad64221e6a514432ff02c95b9298fd89eb552d08ad34aec8e150a0b5b4804a92e95f7bce0724bca9c18c493baf9f176bbdd6b82e02d06cb14bc622ca5f1e298106d19966d7bd6854b12d3fc9f10ad91944754a3ebd6c1a3c8d4f2cc8d398257d09b866060b787abca334d11a78058df4392e8ceacade9673b403120314c2afe32628e4f3234c0157b8969fb4170f63c608c79a3256b8421a395911137d285022be812c00a571713333f53d52d45401d2d0e78d828d05c93733868ba43528643bf39021b8a1126de0646be2405c7118c5819e378c1ce121ea3fc8b07674d6d586596ebb3c350d1fad2ac466212c0d834798cf8e2487e3bc1d83dba2a090afc820e7c6c88f9bae35a06ddb85267349c797d672e321bd77797a3116a173d27741514bbb06dbeadc32f2b4599b0da5f07a4510e617c6324550a308aedfaa2d0c49631a8a6c54f7aec30c284815c40ca9886267c106ec449c1cf6ff622441023309e6b7b66a259a33a6afabe1c9de07eee3f5872be760d4b91c7dea23d92b283d54338393910ae5b2556aaf96ae80aafb1aa86a7feadba2470b29fb3217cf40b100350a4dc7a085fb712e4891206c59d3321bdd2e9e041f2313935fca051610473d816839878111be37988d9cfdde1ee4b93247a919ab3139c5861a675bd9ca3702ee14d9b762ed233890c116c26c8df3706eaa2fadc2502172ccc8df5b94851864426558dc859970852676132bd8abe2a695b35a8434a656dae4043c42a17eb307acba4f1d761d2362dda7f4d1ff21fba4b9c3dde04862225182dfba3046a687d49d423fb1f0941b22f0895fc07d26f9855e1345447082f3f5c9140fad1077e6c5be0359879bd0d972cbbcda537fe765f6f8dfb620b4cb99624dbefc1c302f68633c3b5c0351fd13ed3e3e84f910789dcf9c049efd5e9192e090fa57f54a8bb6a661a96372997625ed9d2093fba2de43eb694d4729c200e41035384371ace39df221a8cad89ad94fd145b0a01ec760a0cf6088b3f70d1ceaa1a5085c1ad6ae91a4a69a2c4a94983a9b6b33169b909ac54a4fb2626062bac4abac27d786db7016188e82dfd582d5bb68a9bd93c5eeefae1b8a9755193023e43a143e74dc938372dd6999a49a9fd08322d84f7a26f8dab819c4661520f3e4b08148e4a293ae20a78ed4a02c59936d5494f61ce825218a72428ae8302bf988a603f0b670c9ca58faa2705ccd5f07ce2464b9b1716ec8547ca0516bec2208fda681dde93505e8741665b9090a92f1ad52891a73a12608548f3cd9272a22a058b5f173eb8f803c3bac30f8fd7d31b119a3b4ca60a00027d1b5d977dc77f64508789b2ec3bd40594da4d219b06948960ca8ff3bbdbb178412c6048e50bce58f40e64109f72510d24d87b130cd2f3749959a10a116caa1491bea492e48ad8354d13446368650b3cf81bfb2a8a637d1437bf4f16cdf202abb081b112e04087a2e6824d75e9507092a3023c7416e12a3d01c298904370375f3b2b0b75c4b5b76abd75bdf1309447cd0e5ffcc3f399111eeda97089a9722be42bd461df015a7872975506291d15e483471605c24538fb3fff3bc63fd325089264d7b968f72f19daacd4b8256f9fb808da3c4a7e26b7685699afbd9c480a0b87afdf0f9085a54824dfb7dfe768c0112f20deb9dd803f98d37cc8c8754ff9016a5782c1e906088feef34d4488ac25a25886be8c03c60bf8147ea628ffdb4a0169e509df98f2e03312630205c82234c7520d8d6799c368936d47ff3814a5b84fc86b950b4dd007cce908dfb8034b3a81f8c44c063d75f34f604f39deaec7dd9d40de58df1d78a258afcf87b884225fa6b5ba5e12efe77cbbd005513c4453fde385067548b32c2cb635e2e2b68d96c19c54a4c33fbaf33ccb7f4eecbb0b770ab64b89e9d6239952d0928ff93de10c5151b9849886f97851e97bc10c5d941c693c478fcb95d61192142d408b58f0f4b4935d8d96ed108372c077e70b350ac0b1e931c251378224d80a4d81e1b4130fc151e4bb55b80bf35aa1916aa716b488fe0f0b046ed39d90abe74f30cc7aa816d38c1ac554f823c7e2a97c211ec7cd47251407534ded7d5a9befc03ec1593a66b3de56f9fc9276c4797df7b2a39484ac08a17e21e1a3ad27f9fb7ffa6c2c3185647fb5b0c8a21614e9be4f94e94555bc22d8682b82a9caac376e6dfec5ffe6f05c583651760252ddab743c9cb8debb0b44c8afab794936f7b32b400e30ae7e8f1bdf090751b533d9386ffd092c45ed003e42f1545c913f28773e4d9a768fa5fd253a38688cac7c5c5e9eb70261ae22266266a025ad990c7d338380ac780e2a9b4a61bfb8a8dd4ba046cd0a0cb8325a028e4f8e6655886fb66cf817fb04c8636df220e59096bcc965ba24c2971840385ae74acb2f7da5377fa19b69e8c8c13ed42d8985c7077988815b32438700eb792f698e9599a8813ebeb301cb1e57b82f7066044a8198e97fb9c8b17ba2db9d2050ed5ab3376b3f1cfe58775de5abaaa7e106e0fb005edd5b926ff0dfb80890a20a858822baa4c1983a04086be6afe55b40e6a349b2f885f48af3b1880ffaa3a55ae2bc796b41ec4b042e1684680d5920923252073580551cb8131241d84541088f2e0a3a53521923719a9bc3b8c6b07e59e32995be763e8fa5396bb145323e07838922c2469c35d984740ee89cdf158b4b899dd27fbf37bc831f32ce8a9446f6de7befbdb74c49ca570dd20cf70c9f459cdb9c19d451c2a54a38f1b4b1511ca791511c7f72cbcdcd589f6f8d071cc29f6c34ef20cdc946731acd538ee2f88be6cc8ce2f8098cf153299146669e7a954a9d431aa7c657cd5451648454b65b167f95169514152d258e34b21ccd0c0c9a2734517c467572e4c0a71cd521b1ff6892f8d46526770283ea34d5e9f3851346fd3760449dbdf2b3e461c0a790f491efa72f5bf234e05348eaeffcce498391af4f5f411c34294662d2170b267dcd981904c1cc208d19430dc1cc26eb94c3fe3ccc187a87994d1c66b649e4f0cdac7a02fbabccbcc30aecffc198315411cc6cf630b34d02c7cc3702664f7d2de0bf574e1a31e9eb48d881eec327fdf7a00c8dcb27bd2d87745ffa67950a0b92f7d9c162c4f4b1d22bb7c282ea4b112bddca5fba159e66955eb9100b2a1b12d55111d88e50b7f294236feb569e62e4a85b79ca0aff9d66cd84752b4fe1f9f034856644de5f1565c8fbab8084bcbf4a0a02de5f4533df5f45e5df5f5553bdbfaa8acdfbab6eddca017813de5f95a55b1900af43d4ad5cc29b372dddca3fdecca2c21de0fd555b5eefaf2ac2efafe212e4fd55395517f0fd55462a2f61ac5bb9f5ae9f6e651f4f826c842704981920ca0940059002843643055513abd8f7ff5bb772ce8bf0fe9fa55b7900dfe3fd9f08e7fd5f0b8ff77f5cb7f27dffdfd2ad6c7aff2fea568679ffe7d2ad8ce3fd3fd7ad9ce375bcff77e95696f99af77fa36ee5984fbdff7ba1797f33d6ad2c800fe1fdcd9f6ee5d5b3dedf94752bdf3c08ef6f3ee9563e7f478f7d39e0fdcd99ca345bf8cf1c27f0a23913814f993a4c25df03fecd47f2ff038909a7125045841c72e60fc9cc9f79c8ab9098f0a953c945f22335430111c9dbb4f02c9c3e4159c7eb9c4ac03cf3259420e4ccaa7f04d894702ab9978512de274875669d6fc89911f0259c4a4013ce5c0288c4049d572151a9ce6cbe0d1295cdfb0421e0cc5390bc092c9480e47fb050c28ff709d239b3ea859867b67904fc5380851990859438bec9c2f77f2a2159307f0a0b9fa2c00c0b2f52c0049df7091acf6c3e91efccff431e34c11d02e4d4bb4e25e0ceccfb041d60e77566f0bf9df1d3b173809fd921c06be6470980671effdbf970026091d32728bb9e8453099875fc08237c0f060943127e8453c92df23e41df995d1f9e4abe2fc43fc2a9043cc09947007782b8fedbf9be338b1fee7ce1fb04e1334fd9f9204546d879029c4aee0804f81e871fe4410988249cd9f5af223ac02224a4668a804546f07d825c679e52e44990808e229f92808b84f7099a39f3f80710cf0cfeeb5126cac8e9139451c83c5e8453096844c7fb0419e08433a36644118cf038730aafc3088f1374fc0c0450679e79d1c8db08a08e9c3e41d900b907284201ce2c3e0a88b5229c5984c7e9f13e41e299817c283ccea9044ce1cc38a091020079d188289e39f5d6c869834f90e81384c299a718f9021cc131f23c8ee0f0789f20206716bf00f6813c0a0206f8138e88001e39e1c8cc99538f3a32837a9f20039c79ca913f0102221cf91e1010a1c7fb04e938f3cc1be0cca83fe16b92f0ec389580200a21d4a47874a41210c2a9e4f2a880828e53c987cface3533c4f632af9f09407d4ac70fa04e51432eb413895802180508383862604d6994378104e25770556eacc305f021e07cf8e57e15402f2e080f9144f2a45c393a2799fa0049c790acf83b0c20e9ec7b102083bde2708e6cca987a1791c5f732af9f09424a614403c658510c01550b86f5a41c7994d5f732a21f169834f908e9af7094ae1cc53567814928470e629b17fadf0ac53c9c536f80485c07a9fa07b661d7f4d67ae21324408020ef0c2410c70420180986006e0754c007c09e6cdff30cf77992d124c1ff5e5801fc1bc790298e78b60e6f43007d02f073c8e79f33cccf3433005c032573dfe72c0e9131480d3b4f5ca01709a587ae50f8279734e49f23bccf39c12fb5792d3bc39cbdf3473fc9b32af3263dec63cbd0e33c7d798329f32639ec63cfd8c99e351a6cc8b66cc5bf3f4a399e334817ae50f9a32a729a557fe9f19739ab4d36952e9953f0b66914762eefc0a6691e73177fe8859e4346b3ba729e42cff9229621ca687051329bc6018f39b22968512d8643a1426b732f0353d1d9e30e201a6650ad4864ba739eb95ff204d18ec8f441a8bff9da1991f7f9fc9f07bfc7daef93efc20fec13003ee3e2fc11d06df474eb7421b9ae0d3f203bbfbceeecd59b74c2077edbcbf49735791f7376bee32f2fea690bb8ebcbf69cbe60dfbc330c5474c1a9a1193a6884933db3169645cdc4c235311f5cabf01a66ae62c7f06982a2067f92fc054d19ce5af0053252445b7e55af3abc1cdaa197612a64342349c5534ecaad9006296a4d04968a3753388ebdf24d8bedd99ddddbd3731edee52773748bd3a8e38dc553f08d626797ad5e009762008762058bbdad5ae56b05a13f47610fcceb305d0a38d2d3808daf7b301e07f592e4882eb7567925e9d3b9f15974559fd2294d04d7174b88f332d6a3368985e3163868730bd528616bcea16e87d1a975e29030bb6dd42afba855ef55be97eb8a00b9616edb4e0aefaea6e411738ffc8ab0690d83b4133bfe0df5bc11e160cd67f8162c11d78839b4118090523bdc98fe910135670bf87e9101334dc67beb88a980e3961c26404eab4b859a489455017118dead4dc13454e884974594383fb38868ab348c3eedfa51191c859fe37ecfd41da9a1e1ea9c98a4b87ba88e12cceba95c52da36c76d4ad3cc244a36e65f1688cd22d58b7e8101755b0ffe8d3e217b1a85b74880820ec2f7269577d8d30714bb7baf71974c0dd0942b91d6e22256edc67071412e1d92c0ad06d0626858c2e22ad8a902d0b4da48935918a586504f28c18e94dee8cb8b8ddd756e784aefc73d7a47b82fdadd013f69a680fa738fe79c494c430d437725c8ae3368ae35fcd4e46714a1dada37554ba5a879375559e74b30e4b77eb88ba2d9d503703ea562e75513a2954a7df28053730ecdd8b823646365eb8601cd85f05a4a2d22e5491cd1731b4cc08a97e54320b13630d9c5533a03182c059254505c3feb6f68c50b7f20cced6ad0cce5899c152aa70aa6d6163e42efa1ae2ed449cc96e3f4e76e6992223dd6b06c7459ec11561df6144715c9c9d6aa72a27a153d1a94bbb646c272b5da294804e594e4427dc89cbc956022ad1ba95c559494a89ca0e20aa53dfc519d5e9f7103bca0b5411c5f1534de6aeef297d2b24ceb0dbe49ce55fcb22ddcbb34ee03c83b391511cdc0cce596e636473067585ef6f6344752af859d5e4fbac6282bdcf366c60ff19217785a78d91b3fc4114eab3319a11f26c8c6684b238cb01ce3b8690612f01617f7aee00a2385bae794282e2f8cfe0b07faa9bc175abc3fe335da860ff99a26ec9607f9a58b79eb4ab03f29fe1d22d7a258d1b769b192f47d8bda8cb159c6960d8cb2e4538d3fc609fc961ffce561ba3da51b7f209091b2fddaa2cc06e04ce365f1660310a5b9c6ad8ff8404d5b9817993de4803b7461a4ef12dc57c03ce7a653b0ab6f5fb473429ae74ac4be0d1f481c70e4d7a458d30302d09803bd62b1047ad20683299683099faa79f604b021e3f3234c758af6c772199f566d62c95c859f649b36ee995adb82737f7d197549f35cb9534bae01bc5b1dfe4b651e77ab5806bb13f89a90d871bd4182247754a6fbf7feed730a8ab028122593ee969ee9957b79aa338350c4a3af3cb00db66229eb9b20154cf006770fb69e7b56bad5d9d7ae79d77de75d5ebbe69fffcdcdcb95be7faedcd82200aceb20f82f7f6bf5afd98627a232fbdb28f82bbea91b3ecd77e340d5b7acb3577cbb9e56e8fb3dd59bb8c2db835d72d297d42ec5fa6b36691d22b1c6ea59d368dfba9517320786fcdb92f291b47199ee7d3abba02d1466bf56a9dcf0fa61dac57d607975628c6f2884b3f773022b09771e9d7bca46b858da2ecf6f7090e81614f88e278252fbdf228156d25a35e79a5a35e791fa479f8708944cef2be8a44eea2215a7d77e6475591c8d5d528cb7b94941be509d6a1cb8186087b1f0373178d2dc7c4c092c9cd34361a2ced2a19659a2cd87bef881ff07dfa04f0bdbfb06ec5c06aa6ec77d85ad1864727b24daef717467546ec3d4d91eb8558322ebdc28508978cba65dffbd251b72af6bee4a55bd5fbfc037b9f738ec0de671c5dcc59de5b77816fdf444de8a8d41ededb07a94ecc0db6a20dd7ba98bbba9ab3a85413686cb587f728d4bdb9bdae86bda7b15513bad853aaf3d1d8868c40344531ec3d68ca7eb0f7344533ecbdf75d2ca609f6be868aa1dd0e284607ecbded6231343bb86ebd52eb02612ffad5fdad696b14c733484406c7c0288e949b4522ec7dbe30ec61ef4522aa1383bdb74960fbd926a192519dfa9e149c496c698ab0f73445345cda05bef75f7db05612e9f0c3ca303ccfeb9ede649a1aaeeff85b81686b211ff86e1b8f4e76a6c51fbe3fb171766354405ba5a28e018a914275bab403baaa3140b896ba509cfaa51dd016a5395c2aea56898cce750b2c19957245dc7c67b959b7c06e7d343cb83149784310e1fa370515e80e4175c4af7fa3d09678055d41a12d91a857f5633a356e4657722e2852021bd175a6876b3964c430d43788c46018ea1b43643aec47385fa23b8479ef8ce2d49b82caba82b66e1491a85b170aa5f5a6c6152317e49428c2b5b2f23582268c9b455cbf11be88a33add33c1f54f372462a8f8cea84efddc31c9de1048e0f08ca1e2ec0d81eb77015d0dd7c666e8e9cedc7dfd7e1af07385755f777ad503cb75865eb5574da825f882f8d54a85f54a347bf00c675cef51e855e7f1bd91c4a23985fc9e7691df3f43b76cfd69565bb9dfe3192aacc670f73b3b7062a0869b97d41ef5eb9357d8b8c286143c365993f4aa056ccf16407ce617c8cf6c210909eef799128940af5a063cd6af0fbe40e2ef6cc18bab42f8de995f00323ad9959e0bf52d083e117ff004f281ba04775f8574ffa457668dd22b201910b7cce1e61f3ffa4b2617656f4d6c1215782c5910db47c0009ecc60044070a922c3e2659308810a377239a04931dbe2556b46264ca9097604d86bffb0461a389c83a94d46c34d6838044c876251b07d7a93abf7bde77d0f82f7a250be72f0b39d9db9761688daa3bea556466bf5afefa590ea990a400103ee229d07eeef6e70bff79daeb35a0337d31ca6380c64b0669a4b42bf69adeea09dd1d6ed15bd6137813e2723bdc96e841dd411dfa6b2dca6d61f603faa2cd0ac63d3d3e2b14917cdc6a327d42bb7829d8a0903d3d676d32a4deb551bb5b64680fd69ba2937bbcd76732277e17857ccfb7b51b7b878ce853c4abbe8cb7f3409b144c01c1ff52443774f0f9a3018e045d3c3e069cae94d667dc59897b2c0c7618294053e4cad270c33e05a4f1fa429752c26759653f11aad5725a6b61bd8c06e7aecc79256343b3cc68004e64d4fc600be0cb22a045df9c7d096cfee89ea8c5f69a7c77ae56f327b70bce949f301d574d90fb7fe8fac57ee58288ebf0b517a7f9ff5fbe330837860def4b41422d623cff5a81e75eb4bb7c41864b8b87ef8f58b63f19b67e95514b4877f35c7ae2b85d42fbd72583dead5eb9abebf1e99dedf6bdd3a39269d3174a5997d8a98ff74ab7e9b20f698bbc097bfcbec8fb66d3b4ee155e7141c6f4d1083a591a07e1c676a07c7e3387bea8b06d3c338a6258fe961fe7e53e926d455893a46754aef60c9ec1891bbba0965f9bb19d887c0de50b0b713ec26c5e50c6ec55d575b6bb5b576b72debd9547eeeca1d7410bcbf02cfbccac939385ad64f7ae53f83bb9ae8c72de7c72ae76347df3fbd72b7d91c0b8eb36a6996bf1355c782c3f59bdb88f0a706ce6eaba1ac5aab8f0be576cc5d57063aabbec93c750b48afaa1b91da4668a437200882407a453f7fdff799a3936eedf7f6ab3dc1cf49b2ef86878f1a2fea3c6b3bdb759d0d8188b6f3c20acef07de0f9bdfd4eaf938286b5d652ef8430dda9b5d6026181d947f7e985fb35d4755d342d6581240665c0deafc01ffb52153efc819f9ff6878f5e754e941c106c7ab1b760d7c8e9ac6799107fe86823cbf83933e881417443d7755d37a44689a5061eb0f480038b0a9c98702ed7e82200985e594308ebc0f4ca1a42f8000a212bdda25008cdb0bfc7a53da36e51289af8c19e974f865345be0f3f253ef862ca9e48be25f6c5fae3d9d3b364865b120c90148742ee96a3386b7af1edc9d3533ac29eb2a199b267aa48f8e223a93f9e348c5fff4c591ab277f432fd0f52fc9e209ef24d670fca59ce437e79f6acc81f28b0b40257d843547d6a91883f9e3404f1947e7c1a4c2f3e92f1cb2f4f1a4a3f3e7de51f0f20bf2c99296b3253f6a4f74593f430bfe4d5550847ec74f654da4c881683b9a4eecf7516439379d42f79e578fa227dae42305f8f9ce5ff3d413ce39bbe27487ce7293dccd953794c5f3a7baae93c7b723df55671e24dc48db51e617f92d966aafb2f55e4fbfe547722f9be5348c0222da878510308067b911ee68378ee871f864f7a9ef0493187397b3a76366d167eee58d8339ac77247b15c1fa1cc1ca68c196352677962ae8fc6a7aff1b3c74edfdf13c413fefd9e201e1c1f7e8f7fcc002f98279d3d413ca43f9d3da727f1dc279d3da7b32faea785ce1bea0ba66fa6a72fd3e716329da9ee97bcbca35be9e9ab74a6ba73c7873acb2b7296ff9297971b87fd63985cfa4a21a9aff0eb2b0ce2217de94b674f50aa48e94dcf537ad3d91364bf873aab644f9ed2dfb3c767ee424e2b72d1ccd197f8d967e2494310cff8e2d357761a0de399e2f9f2c5338584fcf2c9b32728b523be11fa124f24de11106398c0043718ece53db17e912f82f1cb33c513be985d08fbd760b9f4857396bb5bd161892aaae00283bde88b3c6910c178c24aa3201cb8e0053218ec157ef8d971d8fff4c42d808f0dd32970f61caef9e086294c89b08d617fd0f423d38f2e7d89af8ab31f7d329825b64f68dc8ebabcb6edd62b65854a407cea16f5eeebda76e419ee7965032cee4edbb786fb75358c3a3315e53226882975755d77f2707c30a029bc62692491654992c69278431308f3e1f04e5d8c95f11c3d43d219140d09ec157d1fb9b8d759efeef6bc73ebdecdc5d82d7274b3d28c4eba89c239bc320570fc3eb3a3acd203b84c01044937718ce2c52339924878248d25d24d128f7834916e8a78c4417e267db919e211c7781a63c61c638e1135ce8c3533291a92870037a542bc07f7d7efdab178274c44182b64b53d6eb8dfdddd99a0b9bbbbdb19dc48e0fe8a806eed509d8a7fd0d91141324023056b0c61c4cb91c0de822e3cecee5e832b0d777b3780e1f77d679d41ccb3e5402a9e7432c87d5efcd827709e11193f5eeb8cd8c0dff79d50354bef0df8ab4fc8fc0c20f0477b856db38bf2724f68187d3da2e1755e1a5a8881d1c86d51051a339c3f478c22dc34c7c78f9cee078a2507b7cdf951695e37f9fd0796fe9874c575f0ddacf85ad0adbb5b775b12c51f45b1646d78c3ff36408db00be17ff75e4a73a02e98d3a3b49f544a603a69b3b24be98174c5754cff065ebcb3b5fb4692ac2591efc31fa3f4aa814eb7eed6ad7b674b77bcd7cca4b5b5bb4f30d4eeac513a2925917fcaab74ebd42b777721775bbfdfba95a5dfdd5f2debd9ad63fdd34ffa3b4a7fd9673411d5a958a47ddfb2ccd0dca439ab49575c0fc76071f619f6a7659d59fb6ee6d19b866929a42b7d9d8d7fadb8b96f7deb565e12962a94ce54c92a71af694b2625edbb1837835fb67fbf4893e3a6f59e64d230483651be586c21fea1e9f9d995441ae7509cfeaece6a942eb83fdc5f6f554bbb4c597f95d22d8a05072db8ab08e1f61a96181a38571b6e124b1312e05cb3e0eeac3b50b7728ed3ba959b08cfba45b1c4b8e07e8fd2ad6ac31138bb14dc41383b15dcdfe5f8f826ea16bd628610ee6f2dddaa3bac56f6cdd50ed745ece7eabbc1cd9fbaf804c57d5c650ace9586bba657fdde2fc6cd282fb2211ae94d1e800dbb91234fff05fc7da60b2f9cb048b2715d101ff5a3090affec67cf9c5f78e1c75ac3174e2e7c8f7a5a0e417daf9cc877a23e34f309873f36d98343d1ac988e1fea5cf5ea3f3672b8fb2e851189c9f32c982be4f0f8a81b1e1fd5060e534cf18f1fa8cfe9d50f14dc91888f21e0aff0b534529fe76b6984be7c76bec87747dc3a1a317b8c7c913f62f61cf99d6f1d9f92c1b18e332fc1366f73d652888eb3ff83b48e244f55339b368ef0f8363f7a576570c557cd5440bd127ff4a48c1e59cbb1b4829b5533aaa303e340c05d0f78aac2d2ad940aa857ae9272ea02f670b36a769e724e5c08bc2af5e3dbd8bcff29e7ae9efa82c099129fbe20f02204ccfb38ab664ab88171cc9adbb579b114629a4a052cc136a659b12d5518dfe64ca930becddb9c2edcd393f2148f95b4b1b1b1b9cbc765412d7ae5e8a8ce607f15c5b921644305fbd7530e8477103e9704c93fe06b894401ff8013d3cc7ae545cc144f91d92f79ed98299e9d7fc03fe0eca101fa2534326775485c1a769e85a7a1c823a9e590c66211d75f343da05ef98e495f45524878789ebe583053485658e1e96bec91969e945e8de3a9aa4d6e4daf50417c6cd203ea16bdb2031fecef49e9161d7fecba1c7fc7a4afb1b6946e8da7bf07d42d1def3fda3c2d85a8a2e83053e3573c7e1233359e34b217cd9366f97fb8abb23955b35e753e3775dacfaa99a53829a1268b98f4b5933a73cd8be73f2a09189ec1144b12328ca4bf8701bfc2d9c3c2ef7c2d91d4ef69c0f39c3d481e1681a701c9d7df316111781a5828425f11f8209d4259a49f85e731ebaf603e9255af3c50b1f8a449f13561dee633a82325a69e762b358ea9d409f66afc5aa6be5ba9332fc137b41b5ab752ef7f43a54ff0a757b0bfea7710fdab66eeda09c3302c8b742fd50c85ea685e0a11532991c4a9077b957a5a8a9ffad44eca9c528486c747fd581b856a638647f264a375341a9989864ab7f28c91c94423eb564733a3a9d108d9b0f8d364a1894203649a31ea5656cd668eba95bbdb8c97992f5eb900ab9aedb85db3c79ba6084fa9e99a3025dcad99aef9a34716316f68ce727aa542c1fe1d0d04efed68453a279868e6b348e4a76a36a392d9d8ca12ee948bf1e956beb553eed4e56414138b91c5cc62a2c448f1723a12c75bbb42ddca25dcad72ad4481fd71bc8d8dea88bfe3696454674ce9b0116b25dcad459145afe6ae93cd59fe62cd1477549f6f6d0711c5f99dd15401519cf0dde6d471a6ce0cd2c8529f73601e17c43c9e52d70c8fa791d138a13ae3fbcf18a5ce9cc3e6cc57c78c512a07ce25dcc9e6ae120e756d70ec0859c023c23662a67c4cc4264622ac131d4c3d301123cba27402060e9915af872eac932ba2cc0c981ea6383989f2f5c0c3c98a5076a5e4440b0e1918600f44e0a8d5dd6bedee76977db9b741cf2875b2eeeef6fab3fa3b24eba7419a09196ecff3aa15c25d8c07166ec1fbff51a81b005a93013d5101c0b9abddae06329cbb9ae9e1b149ea1685bad71a79de574a47c7fe6d3f9b70776f8d1c04fb658ddcd5c99cd50f82f71ae17e2fd6addc71e96cddca5ecc83752b77b5eea85bd983793f5dadbfabd29e4f7f97ebfaaae1ee72b8e5b6f313847231ad955ad2141089bfc0b34704af2f64612772d704d7ef64f54dd40cae3213ae9e6c06365c29143bd0308542872d459842f173c35646756aae67387070b395b50facc86030188cc9eb7e57eb84dc45a2810da490d9dcd536d8cd5df5e55668b10116d8ab7e87f3169c85b345ee12bdf8608d2c3977552318ec5541d17adeb9e668ee598d72991a155518ae63ed80aa0cd73377b43ac3355b19b64245f5bb9ccc5d3044417081bdeadb9991bb6acc13b2409d91b5b98bbe4ca04209dbcb3b7317ebcedcc950385b5937f33377b03eb33df26c9d96d1a365cd76d6764ec086c6d8f205183b58f1b256b0fddb05db2b2c304a62688717e011d3a11d6a385fdc658cf4867e4daf6c7033bda190a45b8d1d0505ec243933dd42967e6e8ababa9306c7096efe01c4ffc7d00e40980efd64c104c074e8678651a03a8efd53a03afdb4cb5640b7285eb92b4768760414422c61032f2f7f1e0ff8203da5eb1980d14b0403307a51ca5361dd3900a3572ba0578e42af604e701f531a7c21e556db5619e94df66a5aaca400e883c11111407c08a2efe8e86886198efe6835831fdd4e7402f404c8a3c56a2e4bad4e9034234a9104eeb2edcdf05892a41d404cba40d5843e4db4877ff652d061149eac5bd9f3682592dca27eada79fe052f76aa5da486ff2076483eeae95ec55f7f7703d9c1f531cdcdcb1ff665f141c700e9d78efdf32d0339d7a557bd530a0cec2eb6dd7a2b697dc926c576ed1795fc8a323100aef23e98aacb1dddc7810f9a0404a461fa84524222a6d017a7d0f95e0faeed525b8e6bf0131fd9b1b9c1fd7dc1348a250ffa31471fcba25c66e793d5bd17203353886669db90b8572103c2b10ea51a87bbb4ce13db2ecc62d4a7bf3b27b227129cab688e290645002dd153ea57d4da62df8298e094c303f6abe59ae9eb9c4cf2f82e09d959ab8f573c390f447138ca9bc2512399a4453089a3eaf33599377b500c96539aee86c40da6bc5bde28a2b4c2d6c706da45d31b5433b68712eb8dc70cee051ea2284db00ce1040a90bd6475e37cc91a60243b263c28604436624472e6296c8a8d112d488d213c92682651bd79d2c51506dc9895b9f9a485dc922aa0d7bfd6e2d6cf96ec9914c286c11eb15dd69efa626905981b0ff0cee2a4259fe58607f0f03fb9b3853222f7728ce96a35ee51cec0dc3dd095a9344a386e9df2eaed3efa4b8fed405bedc56db778391960ba00da027dd1961c879090ecf9c2db7737ad54f63b788c1e256fe3003c1392720393e8672f0d1416e7cf1ff7b5bbf2008c0e1be90d36fcb06e425b83ef8b65c40f8f53be53dc51fb880f74bf0dfac787ce1f8b414c7f0ad18be0d4fef2bf63e13c430b870da25d8cf4ecbd83f9686d41e72b27909267b5557b9c37488ed55cdc9b1b576b5ae9c557fe5ac223f7e74de29ff283fb29645bc7bff20ddbdbf100f1019714764c47e82432ac5a95f777c6cb83680cea23f9672fdca211587e59051a4b5497909b6562cfdc792903a9646a8b34a2215e391a4b549fe20c5a1751449f68774dd69cf940aa30c4b6c056932b467fd65682f38044c879adc30cd2fbc9073bae03de89d2ed8eea44464e86cb770b8fe2fe4600be29afbfb5fcb5013211d7cd9814f26bd14e2b68b31e2062cb067d70f5c20a542adbf04e3ba45774339ec1eb551d7dadd3ef34426e9c0cd555654655556651d7014d84217750ee5b07b041a758eba2ef56acdd342c4c52f09ba77d5bd562e9ff935a0b3ec0d076ea6b6ae77d5f66dfda1aedbac9a8313cdbc7ef4a4f6f44eafb35ee73ef3747ff4aac660dba9b5a8d20a82d4eb346aa55dfdc11eb5d9aeabb6eb6a0ef6bf9a5e9da85e81191c2b79921eb8f9c70f77d1936c16fd5a2991c5a2fb917e18ca6067f466dd82e099bdca04f85ead526b95093b4340909cdea9e7ca598e020c442db853234a77ba15d1dfaa10adb5ae9b61eb5d0b46f073aa0961f6279662c9404e9d3fa5ef11e0fb1caf0470f06b201bb89912d976baadcb36e903db4aa90f02b8796b56fce94dee225cbbefbb1f4cbf6ec1f565f8b0fd6c71ae44f4f44a15446ce9772511efbfb6247df2a714423f9346a940bdaa4ff0572937d71bae5f6f55c82b5869954aad75eb64ab8bbe9c967ede36512d84187766a43ddab794077c52d396b53e357171290d685c9a62a13a5487521ab8800418e5525877073df0039d65ddfef7cadddd3bb7d6bd3d140b6e7deaeef76b438b5b9f82a4940a00c0fe156707603f6d8357f4e311ac9fabf3ba6dd7edd6adc5eddddd6d564cbbc16ef0037b90c4182a09413054f2815e57b2b6ad974ad75d2165d9a7260ee0fe6a4b153eec5609eef70f45f0f32c15af32e37261301dbae1864ac62dad01bba62f6cb821a643366cd1565c11d3211baec0e15a4c876ca841c5723f4c876cb81dc006db90cf15387f8e1a15bc0fe6d823421821522551229f1a1a1c9570a3f4b24f9ab61fd401ee1a6242fa9e51ab90c1b906fb0eb0bff507af14ec8f1a410fb0bff8678c0b2f72a012458a279ca812349638ec00f4aab57e107751296c5dc860affa3b5e3f49b76a85c15e7985999c00f7dbeec1556385fd41700914990e6954c1031b3479436805a70f0adcef75773716475ccc60073f08caa186573781bb6170777777570ae94c9e720d169c6f70e681fdc5c0fed6aff8e0891ffc80fd076360bff2c613d887603660cfc1d65d089801151ea8a0600747bc9c09ecfffeab6eb17cc0d17d3ddd13e35613c4117c9182fb6d7fffd336dcd0a10a0f9610a20624961082c2e42efbaad88d3740a0453027f8dcb0f1c086901b35e8810d9c1b5edcd0c6cc0d307e8e8cdc40e3a70d58f8a4dde062cbecd6607a850b1f5c8182f3b709baa8f5d005140d45175b84ee09d32b5d78711714c5dc766130bd5264026c627ac50ba11c467a9363567099d056925e7910a02beff3e783bdf778405d15463d1f7869602f7b6460ef3d21aaa3c3bd9bbb4aefbd47e42ed27befe1dc55bef79ef79e171402d9132249b7bc195d79ef7942fe79ee5df39ea369ab09924822ec9de8cb9aa433e787b3c08a49a1539325d39e39267d914cfa02cdd20dc0ab347deaeb06f0e573c393a808de937504efc126c17b4fc85da76679efddb0e7adc2b8f4653fe6cddc95c48bb90b8518f684cc199ce5bd584531b40274c2666044ece2ec095daf46359cbd22710646c8400c2bd08dd6ad4c8fa274abb2a00ace24cede0c7b2dc0d9a39282ed6a542707f6bec2a88e8f07a33a157b3f83f749bc072f4871d87b1414d0aef1bd1f290ecaf2be345394e53d68eea0389e67cd1dafd1f4b93018ac88d70dab97cf0df7255211bc07eb085ff56996577d7ae57ddfbb99bda0ebffb937a338de07698fcaf56e1e96f1bf3f6114b27933aa439de5e1f7d913c2dea7c2107c4fc8b3756b067befe5bcb48b5251e4bd87a55bf44a1b32ec554f0bf69e66af087bef59c161ef69f6ba60cfbb61ef43f0f366ddf2b2577dba5595588097b0eb9fc198a2ae49438cb6ead5be26ea567173895e12dc130dd4f3ecb57ea24a54a7bead5b30d21b6b76415c5b8231bdf8a129a26a447334fd282434b3587a93f8a54e344be6354f585cd4771f665a149abff2b9a4173f3f26c727387c981f2b69329d590687f9b1144ab9a5a7bd2a9da4e9cc38500d38e2522cfec5a73f7d97297ca7ef5ba314323ec8e4bb2586953a291ce2383c53e1539c1a5271f88d2b0ed2a4399aa119c441109b4ca6bf54876293e94793898a724d5df7a6f02dd5298584e358325d08dff463085382299d29154a2f3e91d2892387ff818a4b7f5f2c7d787bd53daa57ddf863486b755269de1d93a7572fc5fdb65c7ad3dd0a6a310356c60efd5e4e67cae1bad07dfdfc02d99dd905fffaf574e1fbd0a4639319ec64f84cd299c117c8217d86b9d6da237bb8fb1828c5c921ee406fa4340ced0dca2f954ae43f0afdab20eea2affe1d9efe08b4abc678186120fd25929442db6a4205414c92987cf249d2a44e2e09924ffa205d9a53ee7b66aa7cef29ae66aa3c8754ea79a4b32cbfe97dc1b5b5548174bf7cd14c91be62d28fa4f3be4ecdeaff707775e65b7ffcfcd813df3c9928ead549b70ce2521cd3ab3dba2e9343ec9ba3b537b8987a4fcbcec3b6b3b5fef97961547a19bbd5185370caf7a76ee6acdac9a85cfb2ef09d55bfc3b55222da34debd25dbf5bdbf6776318ae30f0a259144267157fdc9486ff2ad614a032f68604a032f64e054f721f670c595ba50aecf264c4b2bb83f50178a5659b778e0fefa84fcac23467574e066f5997382ea594d80fb81f4aaed0aacb5160bee1fad8024bd8a40af4837ee0f25f1039ca48508f4aad69f5e59b901ab7ea31be66a60614e05a322f41371963c984ed29973c860c26aca18796912648ec05326b7c3cd30b81814e8e4a58d27799642a6c4fcb9634729c47f0779f10d79530a21ffc654530363eb158c09870117684b460b181a6944a24157fde451392b9b286b4457986eb79a1a58afbc2240717c98219bc94cc99ec8e46068a41f41203ddacd35b01e6ec2e8d0f129d327f5af7a9b30e7625763a68c523992992aa23829548502e71414dc9f4ddcff995cc0c09b32665011fa55a60c1974847e1b53260c4a423fcacc117316cc593232b90090a953264745f80973e4298ea78ed386268cdbe5303d9ab3eac904408daaf4a6dcb31ccff276438e6300483769400fcd29a713f4bcce8a625892652904664b075384233d15782df2ab270c1f1e71ccccfcc9f439bdcdebf81a199c8c163530130e095a392b85c8396eb531cb1a5011fa759825157484fe1ab38c82925012a5cc12872b8bbcc8f73a9de58d8ad04ff3862bcc39ab494663509d7e9930a809a65bed4175c6cf397e70e7f0c12df305e7284ebf8c165d6470dd4a854157fdb71a10c5e9af81b92b150565a5924082865343d06ea5a0d0951051b01535302c8a707f5803b3e19213b319532667ca18f5cacb91b372c064722a6ccfd42c05bbb9bc81f5c7fa612e0aaeee04d72a228c0aaeefa74b711f707dd3994d52e07e1ad351361286f4117c904082b71cebf4b0c1dfe9e69498a70923247354041820120dda8221823c2a676513261c79e426f2a864ba996ee447049009673203e60a98263e195ccbf5e347ea8dd41ba937489174205c7f1c8124a7a0500f9a31d4277d07eb5669ab81b6ca21130e86e6ac0e0aea828102c606d4ab2b680b8608baeac7f13947939f4bdf853877c9e040940923530a194f185c6cfc405bb76088ba1ca22d982be8ca84735798a3b98b3c7228a8cb54036a7397e9e62ca06e95b55ec110415b650b72dc1b9a70259cc9a312ce30b412164f181a0c0ee7ae726692c199a9544e0610c720c385b9820b0c51b7525e843977a59ea0acfe28707fce0a5cf846ca4671fa4b668a4671fa4b3335a338328ad34f1eb92b550465d500c87473570a0a65b520f5a4ac752be584aefa3b2584432a09dc7f04c339874c660c9930688f7e193347cc94e9d2abfe1853a69cb94be60856ce707f09e5108beb2993d3e1da68e16243d4adf2a3f8cf13f7f19f2fee836bae8175ad81d55c03cb365b6a70ad81e1fe1a588d4f4dac5b5d8c59e3d3ac7e9d528877d6c04ca831a11452e3635403238dac672dd5a11497273802e966e8c3b42484a08749b3529cfa3126ca59f547d386e694d3499b659a7f3dd4dfca1b004a216516104a2135b0f25603ab81c560700260e1e0f0c82171a510982dbdeaef510a296f14a73fa5c4cd3238dc2f73d4ad5cde70e7bac818c978e9567ec16bb8bc511d4a758c62689e3b6e0a1042575aa392954b605a0910e92975f9dc26c1fb1bfe459b84af59dff8f57d742bfc4a96e514d28b6696c192ced0cc3280d676d676967e24e9f3beef3b53e053ec6729043c7f58f0cce0e360357eef991feab10743077098c2c5200c178367ee000e4f17be074f7a7ed7f3ce947d8add330f4c36180327ba34510316144dc10419621c7155d1a1081c505053220c2332e8a34a0508444127a00d7d11a392bb38e0c2061a54a9a24311180638820a0f6ce002329e48f18591112481866d8d1c9400c8091e2c89a1a00539f4e0498e56c405ec958fe096cee8e18bd9961c7834d105b55c71c728ea1b563a7749980ecd8e50c18c88fac5ac043a98ed70c32dfda01bc3871d59dc530e90b8e414adc51d6d5ec312b1208a9012c409da4a103fe0206ae82145415bd940630808336a184340f0408bae610808222843403cd10d015105db13b6d65a5bd70082160410425429eec57408081d78b839bc94e09660f4172dbdc3d00f371c0cfd00834a34f4436de807150cfde0c3d00fb0a127604cf1a40b9c6f9dc193296231f8c1d093246030f444881c869edc50dd18f2810d3be48318433eecc08732ca26d2d04287378c9c206272dbc1117ae50532f8320217b0c007247470c4eb41133ce0404a1858047961642cf213c6951738f101076bf4507484ae808720218ecaf0c10db52e8eb4e0031054dc8084131bd0e0c2c816479230d2046602273e7c19ba4194d8161540e1018a0d3a88c16c8a16475865119d1bb040cb5015143421250b2325a85de1c61644ec10430116472a00c40329b92cac2041159c112b4a14d1440d293da80087235e340747c822a41116cbfbfa5d8cea78d4057ead1d0ed7ff5c45b87a8cea7caeae09653571e2040a9421bad84963a39f5d8ce2d433bfd0d53058afb8f906df78494485bd18b627798322c6b89f1af547dadf8ff5a496526a2975fbeeeeeed46d3df56a54e2661024ad3943af6a4f0b6eae76f9ce9e66d5197a5512e37e69e04c8f8c9418e94d077a55bdd833d390c3f6bd2d6eae7dd20fd26d76a057b58f6cd36e4a9bb6f5ac673d6bdbb6694f4b7d80393f7c802b10f5f9bbcbd251b40f43fcf4404415214c707180c183f6e28e4d54336e78442771bf28dee49a74a07245264f5c100c16dcd00a252a97510d2c40604e9843cba1e584b41a48abd1b6a079c92d89085794cbc9312327a79643abd1b8a0e5d47268351a0d68e489085794cbc9312327a79643abd1b8a08994d53d6dcaeaacf801ecd6e27272c01c5c43a250ffe314710c73c09c90560369355a151a4da8e6029f56a1b92b27879653e3515be5589b63736cce89bbaf13c0feb552ec0ac1cfecb063d06ccaeab288bbeeab5fecfde8d66cbaeadecb20c01efa68567f17746fbaf2dff30e37ee5c3e8eb83528544e2d8756a3c58006769f9373821c989c9c1c1fed6a5a9803e684b41a48abd184684039be18ad5befa10b67c11445f4c084eb704f980ef5c083ef7063dae82a2e98a576b9e21507e28e4d78096e78041b4ea2bce14487867261cca05d10d3211918ee23bb42c4f5301d9259b921c5c0f9736c94d1bbb8f51b878705a31840a90702b05fcc237e2c42d535614365bac72e6240a291f4845802ddfa4d29a54cdcfa4d29a55d22a55490c1fd1fce19c02d0e317e38df94bb7f980ef5b083ca850908d72f74b0420a325ef47118716b0d3018860b8c88e20656d8808817edc1184ca0418215106184e555dfc375ab56d185ea58bc811576c1bf3f7b395c3d9fbc5ff97b66eece1756b87bf7707e7a449e962dae57eb56956ee5c03970f5a23041f1a2304531fd98b26946744464829962b8ffdafa69be37dcf9e6ab03124cc1650926b498418257be54e07c73b8f335ba5e3c28d7090f8a15dc57c803ba543ca05ba5e6acda05aad1707fa9288ace25a31fe05c3ada52650b2953b8608a26af7cb3e07c63b83f5fd97d1246098b304a5b78e05cc21995b21895b478b09290072bd93cd80df79760fd349762b873299766626c3104ce0560cc9af8bc72690a9c4b34dc9f4bb5521527d870e20b6ed291ad8bcd0b110947544494c3fd24a01ee04ca29152509443174048e0c00667bc32490a9c4937dc9f4944242d5d131294ae0929ca007026cd3a19e947467ad2011d0191601dc9d6e50d772e73898b99a249edcb153088d0e195cb28702e73b83f9746279c4b2f58944e60515a1100cea5505149a5a8ac6254ce8c4aa09286fbc9a2353299c936bc98628d14bc21a50b28af5cde702e63b83f97b2189ccb27b608120b5b04b985853389b335328bad69b13652c8666d3736702663240762a0032e455f82c061f6ca2414389334dc9fc91a6e9cc92ad6c9c88675327e19bb8c5e56388f4776366eb1332e34a11beecf23118e571eb59c314239638c32fe8c4f4c9cc7d991787434c28e445b3fcde20d771689b2881bc1113e4c51c596d918af2cd200673187fbb368247ac14274020bd18a4845acf2388b42456214514a0e770e8d70875960c1124b630530c8f9e09545d80c701663b83f8b32f195c5274f84583c116e09b3845a70fbad59f5736805778805f787301de430873e9818c30a932f98784119af1c62c139a4e1fe1cd65ce32b8755866063882f5dbce0068f68208e5644cbe17e1048076790863b836ae00c2e11420a327ae8a283152cd1c52b83433883379008d4e24c4028ce048c02fe804f7083338f7d4720cc63b11b2e87fbf367f479f1e273c28bcfca47a50aee4fa8bffefaa3e58ebe1811fdf97bf28487c513de162f0b6e2db83ddccd13ba79b67e0fd6efc570674f0c9cbd067c1102075f48e141114bbcb27705678f86fbb3d743743d44d775e98e681d8e560464847347c39dbb1a1e03e74ee80b2a5284f1451559d0b8bc72677b02e7ee86fb734784bbd3d24c3a28cd244ad7ddac63471dccdafa69b637dc8b0a2ec0b0e9200a32a2bcb22db2e16c73b83f5b2f565827acb082fbad10ce52c1d92a393bcb5920dbd9a5c0d98f6c0cf7672b83c1d93e19c2b118620b6ec7e1a679169a162117725b158265d1d97b94a1040db254e1021918bdb203c9a03f7b95da848ddae44b77f182fb08b7ac28476b222d5974ee2d0e6828148b28b87bd682b37e9e74cca855025dd5cfd5095c8215dc5f6dfd34d71bae3f572fb85fb952c1b50a6e5a1067d577006d857455bfbd737d82fb6380738dd584213b238b1f8c4883c98bb6d0af8024dd7fbb062434c1043786ac8b202e5e1506b83fa7df876371b377e5660fe8899bbd5a6794bbd98375595c70e882b65b2ae3665b84eb97c2b8d91ae1faa52d252d6eb6365cffb671b31fe1fa9d12f5cb1f2e89874b7e7173637173d370edba2b68c470d7fdcdaa7b544d570208389fce58038fd003237a4069ab13a23e58457d908a4b82c12559b91e91ad1908d8fded98e802779d7d75bf434471f68870e582ddc3758b8824a33ed9e59642dcd212b754e5e6c7b54d40d833ca31c2abd65a6bedca9e9dac817e466bc5275442c1cdb6b4c31232886bb948d9598d0ca1575311cb01a874a9c08116dbab79a8e246d1cba50515dbabe7079627845e22b03e00bd68936069e78a81098e5e63e9a8541382bdda44750ae063d3696a4b5f6e578b25a359a8f44a95b7a922868ca1190104000000031500303018108ac56291481669c2e8071400108da44a704e9449b32887410a2163880100002100000003338341020091ba00e1af897c8868da0161a2a3721fbc1e43d59be171e5c48b796715a9e083234ef6ed7007d2021a96b30c75e21e15cafef8b6bd4868c7e1fd4c1be1f7793b083ca846b9ed8d54305cfb96c8710d494950d85a3a467ed944225eed0ff357bf14ff5fad032f0abddfa2eac0f8e76c2258c8c533435250b30a9999f246ae42edc1f44235b8a03d4e80524e910c520cad50c87fbf8ad44c52f72543b295d32fe0655c1de103732047f5c784fbcd3ba8df6894133316d8ccf34253b2b13cd9e57eb3505884cd63f9ce31d88c17d05e81e151c60645d39383fed87f341c131e9ff83fac3b84f1e05714636400101705d81a047fa79b8b893e644fe8c246ce288612504fa4ec0491cd82e179831f94c3fab115795e01a06501b7cc7cb5959c05bbdfeeb64c6ade83ed157e0fad3fb9fec16cad04ff531956bf7ec3a67e03f4f718adeaa322ef14d0f5eabd322c0628d200a0a02e5041f28425acf2f5420235f894c2beb18093a6f5b8f2ccd3448e0e93b593d08f4b0e8e0d2aac571db0c7ea549e56f741067096e38199c6d108e8ecef3c1df417898191121d659c53a977886ddb0c09980da71f30bb9fa6cc4699ca770fb3107353254a7e46d83a4da29d751e1c734744b12d5af3de8005e6125d0787e0aa3166f6be49f4f8ed11cd66caf6cfa0b04dad88f9ebeb01148087518c46090e2b8c60801112327ddb529f9f08978a9137681545d33c1c741851144ade48a6059f9fe057123769e46efc3b240e74241e9ecb3cbb99373c043ee2fbd6e416646bf54d34c4986ddb4b700e7aff514bfb6e28a5ac3a6d97b25dadd3fa09e27e05da86b0e5d7882c24b38d26a49781c7f08b7b870192f58bc3397c120bcb1b8585745b2490d79980ab129ecd9b0222facb5ae54b54f56366699ea61cd4d12bc9a99baae20444a884b14b8c2fba50a202fc9efc5ce6744714cb9459466cb74c0afbdcaa6436631dad13af992ec9ea894b7eb4d974725bd876d519e877c749e445dc40fbc2343caaad7de59cb1e7eb1d1ff2c10ff3f85d60df1d98f1602891330902f238ac32d5333a99f4d5bc39e9b27f28c0002a1b655cbd9db8ab1e6f685be0da4457490af6f1f065922fa1636d1723eb2790eb89c5672698bc35392042b640b3a12624a7d0482fb91712fbda1aabca374af90292f4631734ccc793d97928b6023786bc649f98e57ecdde0eb8d1c699480c3537e9d1e545737a5390f83ec4c9c32f8121b6c0c6ff0fc996554ca4676fb3acbb4753574788a7b8fd5cbafacbc9936fe8f41a786ced52f34d5daf8260d42c61d5de6b91ee7de9f5408444b6c6839e01193876960d935173c4ce4fc21190d1292076ff974081ede61f6184c803f963783eac4ac0927ee888c0f57fb53f256b59e2713cc0849fab62769cac7ce2e68561e69b7eda0527d5dac3c3d61c18dc27294714a9a40fa3ae9f403b5c287bdca2d1f84e218b87d6f5dc1c2361cb7ae48d333a1d47ac10254e419fa52ba6855f96ba3f83da77f215e6acbe87b542a679e3956d1afa59e53b0f81311b8fd8e34e7a47add97643ebdf9cab0749c34c608eb8c910d5df0b16a7ef0deb84f350e2adc9743c77962cd073e7047732c3a4766df1c1f464be895082215e228c58ae4d73cabbd0edf455cb1b2fadf5a0828f9b0e8ee889cdc9fc3626c4dc54b20ad76dcabf17a159e10946c47d6ab130973c80837576d758a6160f05476c3472eb9a24d5548650734c0a6deb5672c8a0a490ca97699cd790c88f8276529e1916fc9893398a01723e8b9ee707dea852f39c37273f07ac88909072d180c210ea2104f28642963725cddb7fdb98f88a18e5cb9f490fd69a38ea515d1eefa9e27f039356466dc19e0b79ca5cad7e8175406f76d14a571482197807037e0df4cdfe96809989ef8bee482e99c2ad7bf28b18ca2a8e485c2dda76ad538a9fdc744a8cd486e0c3760f3ee4ae1494aba143d30c2827006d5fbd8687c3eabcab468dc8e53f664658169900b66e70fb291825c8da7d1b71424044b898f0a552cfeac6fbaa645b3964c047fe1d0a5ab4ac7a5b8196aec6eb93ddc8416e5cfffd2ca85f0fcc38c4627f90f385c5c54133b74214419210342da896d5ff1568c39ba8006305c745c6ea385582bee6a6c1635db72d60e1eb71306a6d68c0370a50927aee4700bf48de2b1997d158b27d77329bcb5e43d0733ef644b11f2f2ce33e920702e41fb2d377f785a0e916abe58b00ff033bf63c734a433d61817e3057e409136a37eb2f2dfcf1048ec6a56ad2d738fd4355fa751b38b88e95f256cb7a0cc799cea6b62aac94c46b2c0755d88cddbd10e1b2751740045e10b3375e1c0186388babdf605780d207eab21620cbef960e01c925dc7d274b7aa1391d2c8936e605a27c55364540d47ed1f16d8cb3ddd7bc1c115efecc2c4bacc7367efccdf96cd3002b5539e6bc48fade49f994aeadde8a83b173b2131773b0a36d65f7ef5085f68c990b3f76aa09d24e5e4e078bd0782a0d4bede01b3131f4e38f8fd1877cc95dd86bc3ba30c7d3f177762be25f69aac00defcbba2aec2f0471cbf4ef446c56d46c8580fb28ffd6d9389e280ec8bc328e01c42c4510d64ab7cefc1e0649aef6793d98811c4074f13182b0389afee6c1167baa480707ac2435bac3e7753101ec2cb8fad64f132ce8d3b85a168a25415bd6462ed0f69bab46507d9b67298e92f6b237826d797e2aa5ac59b4510c8eae4335811f6f371dafe32c3396fd2e24665dcb4b73f7b0db0afa6ea36ac3222905c41e2672ac188ce68d8b6649af3cfab762abd971e894fbdb01ef31348f5ce251602834fae6e8615be6ee81ec47f7c0c85708c62605f004a55c9e2b0ee03d0aa966ff4f5d50ee1ebc369c693e1e07f970a82d84aef09aa68222fc784dda0f4a149e4a83148ba2298bf445c164eb6f9f12723c94290b3c2e1d0a43b9663fa80a2b2c7e9ad6ab4550229b9a5b41d10201701e72f7f15cc97911919a2b7ccd67104812231fa72dc8262f5fa3877e48afe26ac73958b6c2ed789aff8d1032fe60b52a641cb9489e68480ad7d421951845c743710b8fe78edbaef8534b825ed84cd096416ce1bb52f9d9860ea0e36eb648cc56fd3d92cca604255168f640905d183670703f02b7c74291a8f97230fe0b0e6fe4db9841352bdd399fe28bc4db8a759753f2ddf4f0856588d12e54f42314455f6ad8e7f14133890b3e8fd73f2b495057d66a655f458b767720be3fa8218c719d13322e094b1c9b2333a37cd9573109c23bd2bc2c94afe029062c779d3f95ab1a6e585ea3d6e3c96a514f8cfed4089ba25bb31d55b0f16f08303d5a671124b7855dca37312391e51c20c0c60e3edded1cb627a14f2aa5753d6e8b6838bf194978cb0942f6b7afb92f49d9fa975ecf8860a8974462053110641c1a9a5ef7f63f791a8011dc05fdcb21ff02a3e4ce2a55e64b3bb3e8ac21ff078bf067220c03068c5794a5e6921d33b5d2d554264ddd5e1c1b3dd1fec895abf418c7841ec4f88fe2c0710ccb275ae2c4bc0604cd1550a80dc0d0bfb012a9c3829aa8ee011c6214202a027941ec0292f56618a3610774da08ebe163504eec39288f1e46bef980db61e6e3fe4a60e8521cb78cb5122e1b53c578fa32de9d6e9e9f68c8da6e5d7f8f450537c7a3d478d20ebc0c20b30780e109ed30e703e85839e041337f00ac9deb0a549d08cf884aded21442129c341183ba9d441c0f23a720a0f08e38687a625b0c76bf72f83ac6943e3e7fe5a78293624d87dd9a51d574610085118816a53b280c09810026e3e011bdf1e5b0bf0996125ee137617688f8306cd250d1faa2cc87a06fd2d3b15a85cc9700b13f8d1caac6251eca9c4497be1643a9187ae0d3997defb69f60a873dc3af66c4f21e3982784398469102ec159140a8c859489834ea0dc497acb45214de60afa0e13852492e241257d89f3319a737535874f6a1a30c434434baf8b6a0343ce34d0fe1211d49b3910c85005ccffcea7a21878fcd27e33d199bb22be18933fbbf9d9ecafcd878ddfd2b92f046dc24dda1ccced6c293e125046dffe17823f89306e0085b87287d910bffaac58ef0b796b6c67f786418d6a3a163a35a507c985be103381c0cff0d6169e2fc7650674faa039c056cc0dd977c37a4e97c12f0076ec7335a4f3a154fb0e545a01ac3c8fa807156ed11434970c8181e070cefe5851030382ba9c2cef5b4632694010506f2c56aaf8873465ab2191e9d61f10686af9a8367bad0499c180a8a519072adff284134f348b8afb4fdef88316e0496c3af6ada93488a3def40336af431c0e7dd2029dedae6000424001d529b0c8f48646cbd926e902012520a220e249cc1f44f55a9388bfc0889c60210428b40acec04e086371c62968c31df6af70c06d6a519978ddf73c86ab986cc7e70368410e18e01687d4f74738d0f70a287b5cb060c3ea57bcd6164969185df07128874a1a506fa49657bbfc27ea3db184ca4f6cc9108e9575688f97334e382ca28169e3630708135d6544a0bfcd6e41fc6166207eb8c5886df1bb0ae9098db8cc738b113f143400396365820997a6c46fa715c1f12bfa306ebef2200bb76a37b63c9c3f83cfd1c0452c039362cf8131cfc54ef876357bc747bfbf1b5a5cd955ad770d3c5caac2f74af0cb1374b2ae5fd25c39a206efecfb06844c1d026a0784870c2d428e96f38af6527d6516970829fae12561c61ab33b3786d847534544cca4303d4aa7e00e2981eaabaa5e6047491716d3c8b60ef0ce83b6fc1f246c0fbc6234c220993db362c484ee75b5c7b18abc979a10ce1c0962cc16488b2fa0c2e38ff57e6f690cccf96fc27db208426de2de77989cd569e970bddd07cf81807ad7ab09af8f4493946999a68d69e19b3737fb8e7f5c042de40070cc5ad426409cb256b9a6385fbeef912ad049c9b4f1f27a04823095810708987503c4fb98b164062169c2321bf2a63bff16fa66a6c98386127989eb2250ad05319260ceae1758cf0bd3de6cd68f04987d1582ba630cb60ac994ea63dadd5d50c1202fe381262a8627ec7a34a9b2ea2ec7685db50d67f087436056122bb0a189a73719d04566be2dac12bcf27b64b94d13263583c7ef9a85d8b05cc8cd8e6a407d0f2865912f191c8abe4b8cee6d8c9579c212fb62a57cca81b90bd3aa7b93e4c4b00a6817bb43c7590431968e0a04e3e6629904364b663b10e1dc5fb6472a71f17b45239574687b431554ca84085b5984447d18bfa1be4a605c2a6057003e4642da633f1bef9b1aef9288440ff18be960b985ae507e5439a1505ffe7bcaa17678a64bac0699bff04bcc77fb63d1e051aae4d1962b98ffbdfc765cf362787f0ab18c9e602b233567475f21913df11112f511d0c777ad7d85218268b0180d15f516def5a3dc79d6a7d36bed62a2226dc006385ba6b15b23cc3d7dd8b4b34461d1cd93829a7610b60b8216f072b9f7dd39c8af3d65bd1acc3b991d5187f3667beebe0093222a0093bbd071638da7b661e40a88977ea9901dafe3bf32ba90439ebd36718c22a91bcd28704b952437da8461fa2a3117f1d5e5c6cbd784fa1cd1dd662be1c0dc3116fcf9f0bd23fd3847306a310c1530bc64017f84d3b19592ed5f83c13bb30c1d5ff3f3f66f1924a21fd3bfe152084e567ff537e449475abe5fe549f4fc5a17c9eb7bebdbdd5c8dd558e0d51f2eb0c0a009512b01aab87c9a34031c98b8b5eed81d3b4e316f7d19e0ecf5f2dc230f8db5d8f1caa4a9e9df1dccd1f9bb44685e3ee1f273fd0933a052a21aa20edc3e0b55dc3e5547c1d597d3355ed6246b7be89fe6721a73c716dec18a44452b6188800a1c3dfba0f3d57cdd32bd2b00168a726e6b60763c0a6d7d2e9da1be5bb5e79a31966a191f66ff7d7a132297c0e6f78b1181db901a6d98b797b1bea8a7980154ef45d7676525da317c95f25fdc6ce0634a02ad2a3d9f604816d2946b9418e3f6146ba3cb1285707c67288998e04b6e494540648e21d928b668f2f210534a1cb36092237ca661a46f70cd003457548cb9ea384421ce192da4e8e67d8c900893b48075d7ff1f126c74a7dac9b75046f3bee7d639583e72c8a39a4ca327dba3cffcc885463cb6bd6a66b07af472a1e6d98666fc490165cf630e94c52c96a64cdf58ba5a7e98557739aa12302e0395613824ddb81598f200e94214b52819c6b28c8a102225113ab23a9f4a5c9e248804044374f4cca3b23f6f754a3855da68cc6cddc42b9f13af09319c427293f56e35c90905f78ba2045833cf21be0216bddb83149b6e4a6fc7f656cda630bca98593312668c10e6318632c00e5f21a7082760c079108e1d24567f6cb1c4d2a4b8923347789b99a89b7e8d4894b83d1a7e029ce17a8659695c4dfd0f697cb06f3beba25177c71cc5897dc431691c36b78ab65bfd203f8cb0ad7392e367981dfc6134f5dabc8d87364643848eb87db282e323cb5b001b6f89cf1bbc26d8ec304491e3ecb854d929db5054797e3097091014afd66e7fad8ddb1321021c9d7aa32c3ccb5550bbc5d94e989f1830ebf4608121005c287dde0be90a18d55094b9a5b764f200f624494dbb406f0c601d2be25c023fdd53f457aff057008d90ad901a0e25e1989b8a51a3aab89a9d00c7151c79dcafd0267e34a2f0a6f49b3ff7444b369690146604c74dd2f5ec52f3aca132b24e5d174bee1e7263869f11de6b1da933c1621f06c99a15ff3d86973dcbba64c2f9f99076086485850f585603498693331548e9dcbf338ff2b115e3d98b57189deafd000aaa810ce1480ed464e0bfe6a5980c0fb83c0d5546f4a82d608ecaa24a822bac712f74e21dc122ab25f283320ff400801a4efb806c41dfdf83616227e69f5d939cb8f13364fcc319825ac19040f5c207d99225a3dca9c255544389550f10dbfc82e6450c7e0f7a9dd84e0c67e9f5b64f79307aada47de985a33c85ab12be9df433197df22618e6283717164283e8d9623c94c43478326628349a3d0319179e058d8486c646a1eb20ac3c99a85796e9e68e1ea1b8e4d51eb06bf877ba14c64a11d8fd6d69ea7a0de7b0dc5444451f2debd5e5a8b708d4f55e28ff1a6b6fde01fdf9619625b0eb06584b4c7f7596b8de0d0deef73d461c33b7b401f95f835f3cdba4514b27664160f49d874bd7aca20456ada553ce46cd24317fc03eaf4c668db312bad06ae0df229460d27efedc3384cb41fc88e9570d16f8cbafa8e745552888c94e3ce4aea0e8c1a0119310dd11644fa03d337f019640a43ba5e22442617ce4325e2e63834102a65cdb6370e14e3dbdf2ee0130c187f223f41182a71336e4484f6f488729bb63c38bad8bacf8e5da313e46de77e964eab814c29a44a519f9d269bb2b7b1c84a49bd367bddbe614d17d08c855b0ff9194bd6fc6f6cfd57bf00508505b61d7fdd8b05303c04df26f13980a5403031f8b43e128d4ff6c36fb34a5c2ccff8ab583b4a99a9a5589dd36b3a7940702756d493db4552937a1af6da0cc8586eda11843ca2455203c13ade753c21add945741097036629801535007d8e0f921b1648bd43c61378ef07c8030ef7ffe3833c653b23a48a0fc2d5765b7b589c673c080bdf7715db61f5e383482123c726116972653bce560943c1c5e921b0652d958ce22000786810d70951f102d6b397ed25ec7764a640b1ef5c8ad32178ee27c973219fff7f763cde0653894a85850bdd8e6ee1740c66788f63ef3d43b2957aa0a29ae1ed36e380ccdcc0f098b5bf43173611b58500ab7a5d5e4ffdba9a56ce6b6c73bd9114609e3d6f8180ff0379461001f06b918e724d66414fc01a2f0c072b33dab2f4ce669cb5ba786a2160f4f839048dd9f8fb7043fbbd4cbbc4fae30f9596ce18fc8579a6fb34a3ba1466a73f848171eb1e48213da25407a7aa42fb4bd310244d9a8c6769381e825c7399e8d9a8b16b9879db2ed09b03e523359cb5cb21f8f0c97e3e718c2039f8a950ff4f726dd15c8a2c24e0a6a9833bc9e403add9f709f00d3c3d6c7502e9336a358acd57c6967b8e6233f65dcf436357e9f10a604f56e4a813a332b8c5a85893942cbb657db7375117d8a621f75174ac278c38a77c2e72a8f324d53d19ca6f213a8103c5bba6220e38d2362367fee22b0c3be96b3f3bd4c52566d7877e45bb0144b0228b5d6c2a198c0379a1527c553582b31296901d2d85fe4ccec347049c263e276815d305df5ffce69051d4b31f0b2d36b2df68ac4cd841febea129c015d7f963be8cdf226ccfef3b75d29d0e1d7b15c067916032b858862e0db949de242381cf8c1929f79fdbe06231b0a8b02b5414b459ea21b31f831a5f3527d12f3903a84dfa3f79a4c4903ac7426af8f9da13fd9dc338b1f988f39f91d83efd6dfa33c78870e4c4669b4dc96c81a2032ab5e4390f53723d634eb05676729a06a61e0da70168e28eb3a560c11ca7afc383c287ecbcd2492864091e3cacc4558606fec8647099d22ffd26cf5a750f5d4adf7ca717c73e0cb44e8f277fbb50c0dea50960f614c17fb003c305f4bfbe43d1337c7b65ddb003d52309a177f0307ea8cc0163f122892fcc516f9a0426cea1735f49b51ba02539a6276ed27f320fb5821f070ccf78679f2bb05f97efd89b459e50d44f9881a7d2037f9014f8998deb70d9b814fa095e0e821a9cf2762820035169692a9c02ac036ea94ef06c11d555d87ebd9b77218115469ac3dc1b311bb42310110febad50c49493c120aaa1dded3b7dd46d820392b5f70ccc5a6da26e903b406837d9f54bdeafc2df3d2a331ba5571a85165541c37083ff0717a849201944c8b54acc0655fd177e2683ff0fda135d14e3fdb8b86710f7a5522f2bff24856ff3210b1bb76176181887cbc90fe29ef9d127f5865c55cedd49cebe501a3befda9ac6be77a2083cea79ff7b3767ae1911ea1d35428d7fa36902481896e728eab4deb2294a10dba28add573a0842432f2c49f258b67fe52eb666630200721f283fb08f4ca5eaa43e694ffc37be0c3404fcb5ddbb817dfe2c2519d4791c3c1d01e31b7f8c677c1c097f3e902949dcaa2b5f052d691aabdb57ba5fcda9dd27a0ff73ec7916c2a6aee42c55c9653219754ac3749e0d08e3967363e6d25bfa7d2bfb9ddc7316328b54179ec7e2d9c42286f4b6d9533630e75a60f699adacc9c74c321382eff0cbe22fa65b5b3363c9cb55390de574abdf7cc2e8f878268a86818d3c0009374088de2462b3d5a5f27c780dd5e68e78d28f218e93d4d44e860c5d7dc8ff7fa0efbde108395a7dbe8833f75d69e5a15641e5755cbc15c7aa8a856017bd05e79729a891466e2e0d7ba97b530895dc430282396bb3dad8a500c465dda7a3b20009e8ba2a259901cf31c6c58f758fb3807800a33fa2e3d30b88a4acb5793fa423f964252bf9a9b9e2f413bf86a5c3fdc65ea12cb4cb2448afe6d83f3a0fdb73b7caaf7c7b1c3145417391ff898699b25658f0b2c33ea0fd4f650a513087446794049dd2a68d8b8ee8cd5adc6dda0716eaa9f5e4af6a06f3319ce70574a33aefb3d3d0b86b942a9024ede561891e54af8237a93890fa87f920f675c23560b4130d5194be82c8a2710784a39137209a4e1765d7811b223399c07301fa687b16784af5be038bc8af47289925b83cc392796893ca31086c366fb02d624d9d4110a3b6bb608791ee05d31c1cf59e09835e0982bee0e71f5221bc392eb4e93940309637a9f7ca61ebe5edfcfd7409abd7febf4fadeb07b7dc84bf7a138e3118609bfc8f8118126b8687b0e37d2cefe2d6dc67c3b78c98145eafd42d88ad39f6aaa13ddddf9b28c3ec8ff56ac33624481065cf10a2c7964f31c86329e50aa793b59e783bb884e5f611eb1607181c2a6242309cc7db4accdd860ab6416cb83ac0e01cb160b711232ca1bbde897b9db60662e9fc54bd905e4cccf003374b0c15ac7a1f090c9f19f66112a09930596897e0ee6773842938a01cdac0834b5e244a6740ca7bd98bb5d6ff175b2bd8a357f8240a653064599f93655d82c3b99b55121fea523362a43ccc5d92c842419114d63dc31d441e0554c698ba74d475a8243fefa75b6b33fdd638b14a8cb09fafbc6c192d6c5441a4865eaca44127f1fa061ee8ed7a42cac0ce29e4221c5c7f9528f105c1d0b6f58daa11d3d996c7e7784df60642870157d9e3f5dda68157b8a7b7f233dcb1d10958bf49af0fc2f4043ec18e57b3a67cde79f75ee8cadb44230a8d9608edd8763abfcfdb617ca3f6919c4ffa3d65dd10fccdc793f61bd468a1e9d32d36d4ef185886fa16ed9cf27ba8e96d1b3761bff477515d75691c3e7a3f659a225391c6aff71ea93b38978986a4aa8866a4f1d7bc841e5cc3e6b815ff726e44d9316a3821850f31d385b6152306e6b4f631b804c97db65d04f2bca2a268f2b6f30f49ed08151cd59ed28c1e3b5658bc982326d3b58f2f94c1da6d1281a00075214ef824288c97837fc0e543605b1206df29c4eb88dd842504f9cc14acc12bf5c42878ca7df623bcb09dada2eb428d821cb4233bf371b3286c170d42471d465b01e1d38f24fd078356460298ac15a019082febcd9b06f2f9f7fa67526d39e1427d3576fcd7c3a91e3f1e229eefa62f5487edcfdb33f40f7a986bbe047b3155a9ad2fab240ccf32cb47c68161934a28bd2bf5a1ee1f241acb2032116c6f40546458db16937e01aa15e2114dc79f9e86f4fdc33915d13dd57354faeb44d4b5c51a482ab4e9385a174cc7c528a4ee862b77e1f08ca8fe2e71582198506f2206ef9873f4a3efb6c28ad198f4cfdaefbb397e83198ca03acaa418d5ac15b5c62e78d9fff60b2b6ec2fda258e71c1c20a9249a77176250fa6c78ee97a1ce7693ac42bb4b12cd9cea5bdb4e6a2e41b77ffd903a2ad87c52e4eee4f5b232079037b1c8809928ce8d84a4ea58c2b88c0fd8702173241bd9cfedd3f1276fcc67ae8264272ba8cec27b58f549349bbd3b2a3e8ad5dd3adf004dfd6a7b1b4137eaccaf59757f34fcba67dcc8ab68a24f3be135e6c9d0f825c68e219c79d4827f6ce529acddf6234f780d44eb3a4bf702e12c25581767878a58e3855f9975bd890189b03f3241cc010aed0b64411bd25563dbebef3687c092865ff7ce2368e2ec450b26fb341699557487a69e1fe17b29b3b3468fa463286b5c0566e0accf43b065fe86fb3b0ab5042931d2573b864ce5cb35942496380601897a1b37d7a6520cdf51739ba91b2e79240fab3fe8548f15b13708939b82a39ecf412aebcceadc0d90347bfb2758dbd0980e8ca0cfa2d898424f9b827db0abd09af8b2f004e3125d411e5a283454aade5d0ea59a90697140c1e22f82b0cbf2d0bce6eebe515ac967a39a56832806d09aa14101eac20324f79fa784cea82b7c1e08411c8ca469310742b18120ae5cb52882449ce9b7de7cb149f522c8eea1b35577e55744f8e4558de9fb4ec0848d22e5e23ee78dfb141b9a0c1aee6d5f141f8dc1ee38cf0d97da2a51d28fe045cdf7a6cec2530d317a600223c5fae21e4bd0cc89951f25011e3377c4638c5d073a5eb790cf1e1011f86244314064541b6f4cea38c324837b59d5900e32c57ff1af8a9a27edaa22b4c7c60e26ca83f063d4460f9c45e7861efe858aa8cceed35c0b8ba48866dc62ae64fab4c39f8f5e84aa31f9184aafe2a54fb752b9ef8023ef2162ea39993cccd2a06292b7929c97da8291b0bff31598989a8345266348b37c145d1ee2919f767485ab1423ce079d6daa0e536c34bb4d56d1fa002885190fc541f86a2415c4b330f9ead968a5f07ee9bf8c99795a9515a608f5cdb75609118ebac0deb910ab6f994c86c2d03ae271c13efb4deadbce6bc2a00b6af4029c2879201436338d842ffbcae7334a5b06a3d2bb4f2d21b70c3f1d2b0a178a49ff3c3878841a88500f17c5c9bc8e25a0471b2a38514b1f094b994fa096a7ca7c03322992e0d9334af11657baa55a6d68c6c0448015d4ece5707d50fde23105df68f7e285440020c6f3b7f12955b087874545dac456cf2187bc6e68ef9f5cb5e4caac49d379e9576463b4dc2c7c1f5d889dc0d3f70f61151d444986226362622989b3da664a70875cf06484afa6acf383fd38780f1ac3b0d0081f2b84a318cff72d3195448c0cc8cf93fa8444941ef8d7a6823eb8adfdb6e2feffe4ac956e82319704c0dda2347907583208bbe2cac85422a616a4c79a29901d971a7358950b80d8744b7dca42bf655ce4f159e5e9f45cd1af70d2f2c8fe62c071dcedf6a1a1e14a16ae92f7b8a09760aca1554abb6d6e6db5ea24d6d3e2b80baabc09a405fe2d125aefbc47aa92204d528f69cd6d3cf18d9590c21d0230d28e2976785605a4f83fa33682bd19ed4926a498b574b93bbfbeb359e22d451c65aa8aff4945775c1a2d28b4372bc038a827e464906565e02ccfa4e5adb2f578e26869e59137ccbcc6f88bf256e28c954da9b56172ab6154e43ec79b4631cf91911f557e691e28eedeb9bf2d9fd6dfa110dd0f239a6997a0bf9904defb7dbf6c62d518b5b8d6d9a60d73c96a3743e981ca8287d5d32f3f5c96faa476cd1b92bcf38a85f2d6c0084268e8b328d6ed75216d73e4ddd7cef1e4389b845419f11aa1520137978657bd6435e2d5a6f47af4dc874ce648c9d656ea4334b8469a13f9d8076c52e23139c627babfe4f68f7821cf1e4859f240a8de8c2b7e2793cbd45f526dc41c15628db3ee3500e719240f96fdbd69d4c43fdffc7c53709cea9698b8a130c8fd76f89f767c3424f7373d7ade3c2da15efd31780c9944ce0390b033c5a59b12eb43190e548ccc5fc5e18ff45aec5cf77fd3d5503d4d04c451ca82b464e1528eca51758daa79ee749a449b38485a14892e69d44930c6d08008533f5aba29ba15b9822787e15473093ddea58df33ca576047a9ccc201be09e286c251d47c6730482072c483e3f0f9f5671d40275881242082977d6ea32e8d812747c42a0a52bcb5efa236dcfba3c33f01f6f7dbc6d8962712b75ac12f21c6e55aa66d157150df19920d6db52350b049bff4c30d502a7ab3ebd466be3439d40ba64c282147cc56fced1d3cd5c4d66687630270223370f8b24379208a5ea89f7574e239f0243ece8f34c451c59e20e4bf46fde1a28d9bd0e36a9bcb86c4187b5633f5c468aab6cdf76b5f71218a90a80e66083bd5c3df3fc05e8d1324c2cb6b8695af2e531183e6b9058b6d33748c7910906aa54f72108cdc9b8c19dd5d411d42b28c974028be120cc76601bfdf9cef13ce648fdfdce3ee5bcff2634886d68481104d2d645157a98d4db9a067cd2110d04a641b1af5bd3c63c61a89fe71853aaf16c47f7ecee9a4f7a3bfcae974c73a0d00931c02533ec8aae8a8bd0553375a1aa747bdc9054238259df4ab86eee91c4dd89af28b184acdfec52d6dfe07fafffcbfcb700d8cfb31d983097c0f9441938d169ed9bc3f14622bb84dec143aac0bfa085526988eb961d53d1d54efd414fb71cd316334f518e8d08c94fe39b88f4cc26bc88a4b844579eae8b3e89efa58b5e06de47078265f49eaa3a5bbee28a889865d46c053470fdb0786d258270fbcb13edea2685080ad61c789663954581486270152c1a6776cc4157ad12d8584c4fe7268ef133fd994d900409be37118bd40ad46777f972f49ac4389aff5c13145cc251e5793b416e4e3edc0f8dff0af919a9cecda15a54626d301faa0ecd49d1d6494c311819ad535bea3c086fbacef24e976bb7631c2115b9bcc5ed8cb798e1aee24b1f47369cad9763ec5ba14192b6faa9be782a1bb8b1169325adba47d081de48ac40e8bf92888cfbbdccad3da9cc284434def24ac318c629f070cb7fb9e3d529981b9916f1067f527add497e76f978e773734b64c6249b6969a1a48ca3aa6d54c79864d5aaab05a1c61be466cc0a1063af4db274b6810a56e69a19c6d61aea9e37a9634dc4d3d1951d719d6df6a5e253323f13bd5edd7d267d22ba563c2ecfc0d72f50f359f951ef9018eea573a91492f59425d6934ce899bf10be318c39fe3bb7bd2a95b1a828b5f81e0fcab72c3be60acf7b23e81c1ddf436950b445e591bd0b8151c7c65ed59593eb38d499c007250fe89043154b9ecd2d006e6f71b46644e61c479a87d196dc3ab3dcadeecd4b3521f827e40e2a12d5d2bc2c7de02d155023b451eea9ba7bb0632486b59408f24c3a509c071b753b6ea9e177f260ddf0824dec6a2b0d79c396eff603e0ad6b6d30d023cda254434b7784b74d89584cb78985bab8fd5d1389e31c046e7d214bbb06cc79c439772c5a21aaada9a3fa621857260fa201220dd63ffa4c9e1693c534e16b869ac9b17a749735ec74a5f4e956f6d1875f86cf74d6c8d90748ae6fa87ebb724e5685e3c937b37e1f11d920faed22876ccbe050afbd64a5afb6fc0ed26b9cecd78d7ee36cbf40f410e7fb65a3cfa64fbdd2cd4bbc55afe07b70425aff1ecd5b8f1fb3329fbe57bec7a26386c622f4830ac747f9d3f922e57e67cdc0ce2fc905a60f35e6cbbfc5d542bf26bd8c5aaccf2004c47ae9a5b10e1f01e75b7648fee500682abfbcf6aa60d1f81743dbce42b2de0035dd50bdd5f79e5e8d6559b504e2e3f0514d3c5af255a9e9759c3a78fcaa648259f0d93a7374294bbddbf4f972fd5edcddf0a0e97d9d260835ac5406d7c48d0564164c165586e6234c3315476e45f27f8af3c7865cff7e4bda05276fa3524ae7cf8624093629eca8979d5f22485cb12216ea7528bd6953549c70659ad721d6a7ab8e6bfba8662d4b9340c8cbc753f19feb05acc50c4f0c8d641f4aa767724f1dbe188d5ddb04665d8e24b69a8ccba927c5354f377fc3cac94c28a19b383aa354f68d1033cbb292e08a17c97f4d781a3b84001a9e5c912028506dc7ab12dcbf1969672ff666e7545d94c982c5ca495cb816d4971dff6cd4091fe30fa31fcff367549d517ae31a3f8888836dfed5b493c87879a1d4f3fa765567451bc87fb7925de607a8462c3935dd17812ac4e9639e6bec4919f3e60f17a246dea1d8e72d2c7c4a3039fd0343cdcb77d8efa9744455411e8e35e1aca7adf9eb280f370d82db602fa5b2c24d7d3ffe1cdf17125adb8b6b3d2e4cadbc37c2bae0716ed1574b680b07c5927794e48f9945682a11876a504c10941e5144e5525d89868c65cc3a0b16350e4148337b63da12b1a2508e6128d6368c08b4f4923cc0799db0a5032b1aa7d28e34da0ff1ed6d4ed785a2abe6f7b3bacc86577bde57bd5a5d86b77488a0ff3b7ff1543f8cab1b40971df651dfcca50c354370d358f11da1b9573478902c072f596c373e0d66e10cd9178a1d3926bad22a77e16d2c034f83b330fa2b05469ed0ac8bafa6b9a8e6e4a10a7f7ea7a409ebd9f772a615475347f27a4a9f8f0d37ffd23e734a38684ad2ac353fcf7956b0d8d0f86ce1866320eec8bc6b0b7a52cefab7f3eb136ef723b48d320d6b159dd9d8bdb25a20b46c9da5368d5d0786212de1e9d50b9be0c41bf86518300474aa657c8cc45a9e66c7eb47f6acb28d967fec5fad0f190fbae7a46a124028ec49f3abc6a6733127661f1b73da5bfd74fe1161baf0cfff7b4a5092e963a3036d24499de29d5e7d862c1a9552bfdc8523bc0a3c7278d8a1397fbdb272f318659e1316e9f14dbd28fdc9f21a848985408c7e21195ebca01d9a93593eaa837054f45820d1683e8a82239f4aded32f27d08e67a8f318a00f8168a68f9d259affa9d9d043428174975b85934ab73a1062d27e94cbf414d32849ce775d9faa4a9ee4f18ce83830411dc28a89dff94079cd631fc265ad7db8fc8a21d39676b85fbb9ebf68c7ac0aef50afcc9c27628641f9c9cac710c2cae4cf09be2f69b1c336744d81f321f38c2e8f2294087b294877e0f74b88accd59e8fee5cae6fa85ab27d31824cbc83e4f7f2b0c35bcde5a85c14a7e578e1450ffbc4ed45f95cb4577fc3619339d29ed893d5519a87470d490242530d27112db17add0b511a6500a199c336ffacc67a64226844169946c0fffdeaad7d65e8b0cdd5baba0b5ba8ea8bb40b7f1a79575b5968d60d96c2daee2af6749513df9338e641b3a5c5150a1f10d7281742d8ba0894726daf355619bfb06eb1654ab8e9da9fba534b9c8d25218bc8021d18bb60c9761e0037b7cfd57f5cb2cf09489c1142c7315d63b961936ddc7a886743363c2e3ce14d83de2ea66d487983e1c760956153664efc66044d16e1478bb00fab4df9a3595ae76de5a08235a39dced20cad43256adddfab97ad7ab91bb0d0e3a4f4cb60667bee643e8616cd08047e929c085f69e66bcf6b00576d8caf942a919c3441c6318cb05c36e6cecf52bec474f04690eed5ce486463d8a12101ba2ed604df67d3dac48b94e3da759038fe09d4b2b5a82a5f28d8ecc09e82ba47c0e9b8bf21f7d77b4c2a495ae26929d836a34bcde31669dc10d4c7467f6db6dc2a837fbf982104fdd6226dee6c2db56dc0e5099a49135add5a6643a075d573f2ee155f238f5b2ec9041ecde098d1f2af148ffd8e2fdb5cfc22db34b823f172418082f6fbeb2b783512121a3747c10c2fc8236d5696de903632cfca4a0801a0b130d959185f4402e749c9dd74123c46790c86e3424f6b38d5635ffaf0cbfbcbf15219a71a600543329b9d993cb533347862ed13ebf73cbc3a2db9df604173c850168bda7c20fcc4a683ca7d1a4ab00d3e797d10e0785a206a1bd151fb68b76cc94753dd630ea9584a630a7908a04eb3a3602f4e3b78e1a75e5f99c233dfa6086d89636f8bd213031c0c02cf0074ec1ac5b984da85fd97aca5a6241066d299b9aaa77f7bf58248007ac0d095cf21ececea89f8c590c3d2b6774798e59f5f9a28665b9ba18628745b45179a0798a174303667169e8e24922d8a45ade8a71320a434620b72d5bc9dae2e70a692e4ea0cf7469dc680077395f608e2f12323eda107a427e303baea6c80c895ea436c1622eb4636e310ade5d2b123a8fd58e43fb2841b591049cc44743f1390eb148ac983830453952f67d9bdbda95bbdb8f48b58195552bd018acb3ae71514bfd5daa56848e6d9e4c68ecf6dac6e6a18a871403c08442fb788b22c2189771e5683aeeb325163ae11351112e27b4787be6b2674e347d9aaa70afe4f742df4491f2f9bd7c4c98d86338ec4da7e079c1e01b7bcb70f89f9aba08bcad03b16d92f474bc1b2361a8552aa671d0e14b62d9b35c6618c8c0e8b6900b9700be980094f18c9e53fbd5a99a4c534f692e665faebb05024edef1cd1413263715143b2a21d4e12b592530bd31ebf460a41262c113b4cd90999e290f355b412550aea887f18c109e53798b5778f26e3b88f8a3de89b4c628e2ecdd0f4f485af3a8c84c02a9370a4c92412855ab3e9753914bc3a459d1a5def39a941c1008fd4418d6eb396c17b6e268dda9cd6a3c52b51f2eb5f612269076c6b10062b227599b7419ca9a04fc4724e47424f70a00942fce4675881c18ab5a91333e498cc691780292e5686115694f72779019d74b0de2351ac90e75c97f5d9533dc8ec89411582d24c1b60804860b6eab643fb216f82bb9eb799f1b57d241e8f1b51e1d81b5c5d02660d19469c0a6a373f80e28880dddf07ef043cc5bec0602546c3ff2b153c58e038e265ba0a57a93cdab83e29ad7e733dcb4eca548788123d25d64cfd5a9ad9760cb1156da08a7377266ecb667fb05a782ac3163b154419d0bdb6dd12add44a20d2e19fd842b00650cf0c361063f72e113757097722eedb6926fca9983dbf882634635b76d8541520e5c38b25f7b61abe573f429ca164d82b00daacb4500d1849a4f696bead7f2c017685d75b3f0f5dbba4d7480d57ce09cbfa9f60acbe1ef3462e2cac905f06553b959bfd01d82e0aed05163932d3a27f73d2d68cab73f83b09a16b4ca2fe0ef462db4328b2d009004103de589b30731a8579106359231f670fdbe06d9c487a3813d2f33f6022a747ae9c25ae8c3295dc43a13c43637c4261ee8499071ac91818387acc5a624d98b8e5af5f7da00812371b553b5643bf340672b0006ef78a190557dd7316ad1c6354774883bc815c87915484071a6f2e474dedcb1cd680030d9809ddd0c58a96bd08dd80ef472c80b38f68885e856071940ddbee19a934c439a384b74838b70e6a1e9d672463ca9648aa9a8169f21337ce4e455fa0b550d6e57cca734619c1d73d0e92fff2e60b05b073d2860ae7f92c17b529a136fc6b31875554c02baa5c0a06eb28b7491ffd5c324c780ec3a93b407c27172ff40b365844e5a0ac5b9e48c139f72f8e0d2bad067453ae14174b3cc303c52dfe16573a8eff4e055073941ba863046767f27024cb2c2db0c3506d824f86d3cb0fbe1dfc94ac302093b5c201250f33d97233867c086be8a42a1476be19a1ca9c36ab9b2d05a5eeb1b6f26a04c62a5299730167f5d1f2246474125442b9777c8420934a8441e68aac1d167b0d2cedc46af49eee5e5a7deb8b95f8abd13879ba647c65d0e70428fb25388995ccfe497c3c18473164dbc3a64f35ff1f7a958c06eca3b4b71aa8c7b13c3d5ab21d12f47e554a3a2a8c5a39aee1f745a73ea18a452699bb09b5957352dd11a461947e39c1fef3ecdb1f555ca8f770e3b07639b7b49594ac16e59cd8f51963aa19fee57af39e2ef69791757ebe0dfc98fadd7f24e06abfae6c6ba745c9bdf5a0fa16d8566ac0b04502b3e60d83a01de5c8d5545a8c1fccacbf54c3cf449ce1a965687e09b8b6eca09ead113687b27894c9d845450e8c2d94998d9ee9b4e8cfbee3d5640eff915dcb1a951b3a19a9d209a2df5f86a069a9927f072cdad707443fb111bc6c84b848d85159d135d2708ebe9675012b79eb0ecce1f33be2f9016d3a1908f828939c365c6b3de39d1f4f2a9d9fc13dec475471067a4ddc57b4b8290a74ab4dcb7f911b1087c67274928e7ac4fb70925159da048208021426add74f3e3ee236af5be62a95d9cae2b8e618d4864ca7c8871086bc78464d5fa18e561fe4db8e8e4954b4949af877ff9ca62a4b44740179d7424ad674f89efbf2b843a25e36a691c76c82b1f64a717cda6bfe3c795cc2a3ae968a5343e901910e2d4275a748274ee876aee7bb00a585ac20371006da720474a34b90e080f0253c8dd40e0531455922c2bd509534ed870c31262ed07e0027b225091b2d01dfdfd5ebb8e33c7eb68adb783114250a26e0f7a47358503f06d3ac9c54d82a328195e038024f4e9f96ee4d86fbd497ea9fe175f9fc804cf02eb2b674f692daad64f8717a70c750b83bce77949893fa70bdf9688055ff558b2feb12e4c83cdc3199e3bcd4aca219ab509b14ebd5ebaa9c9c1f3fadc67f949619217e795b14b7e238aa17da3c026d511c7aa013c07b50ca9570cafafc8ddaa1c0950f428dfbf4e6b3fbb6c72cd3c4f97c9e0a315bbea275c2e426ba04aee08351ba8ce9843599039c665c686367670daebe6672351bb5fa609e940a2733cb745621643558c47a01dfb2fbca5c1d16836c0a1c0433fe978ba2488edb18ece5fabbfb808df610868cca18d73a94a524ea7d9fa2cbf0f949b72aa2c5b3f214134faed526249fb9e8ecca9a9d33056d368fb1d549dce0a3bc596094a66eb3a92d3d4bec1113e08be8c0ea30312a8b817d0d5727e3fb9e0a9146975ddb0d4f1602e2aa5393351a73d312714ec386b8a4d6af69fa35e2f7e451abfcbf63927f36e0ea18cf65f5d2f56f5ca637e3892882a3da11e8cc4ce867b9aaa6571de806038245a13981fa759b4a6d0b51481fca45eff2674983eb4b96c5d476e02fcce12cb57a221317048a651628c0f2c260b22706dc7cca6bc6b83d4996eb2505ec7142ecf2cf185594c636c15a8491a5bbe7d19b09b516831150198eaad5a83ee257756409d331fa8000b091da8dc4b11e0741c4cb8becf4e115dae06378413c2d7569b4a2b2bd14f47f5e2f829f1549b1343dcb79c22535d7aa7406b9bd5584b1fd8d8ef0c98bb31690b35639b1352691c8c941932cc797e36793367c34095c6d1b6c03e8ba0b589058a85ea55290a8c96ff31c3ca8f7c55c5222ed1081efb4477b4dae99c188af7752d982d758ef8baa0b9823239dccd86736e306aaa659984fa97d6ce045004d4622fa52b5448f19776fd3a1e292a7ef620c4a186b650252cd4c80719d08486bd1c50b06cb46fdcf9790599eb6014504c4dfea7f8d6b445bb3e0a633a45682e29aac6ecf2ed2d169ce3a1931fa77bfda7346df8e14b07a0847e701a4b37928b2d09a07f608dc24dc225a59e94d0f47ba3cbe4a407a03b90712906525b375b9f5b6a112b33d015a2985c63ea80ea07fc0687ca6307faef69da2e2e669cf9b0860552f2e678a2d89909d85178ffe78e82bd95db64dc19437d18dce5262227547d2700d1cdcb89967a0deb43f8619b7b5db9518c80716ea533c080e067ab512540464d67db2246752f8b5ec0469144baab9795d19d9f11922555d59fe595b68cfc188eef632bc4aa135b043083248c12aee51f776bf504481a817d80d36ffe6d9a25039071260e6666beb93d93ccb783ddfedfe58e2e3a7a8d68bd3dcd5ad329fd73ea94f7d27838f149d882b7a4e8cc41f54e89925737305372c483b42b10b604f4411cb5b318801e4bf72ae4e0bd3560b4b207ef1089d02d3ddf9f4a73b1e2443e5cc2d96ae33b7b18e182fa325916ddafd231242b7c3eceb311fc59000f01e5b63d92ed2024a4831e9f63e599ab5b9321a4a38ad650be39f4c804ff2abe90e123a68d6409c8b4bc082b7573f0efd4414925b6b4fea116cb0a2470ab0b023a07050eacaa643b2b55ccc954095555cce7086807f26a45379692936abe7b735ff12f0c5c805dcb05e849672a027b4eeffe5170abd1577be274567d152bdaa808f6d899697e63cc3f3892d0f23e5cb8c82277679f270abed587541a04811ffe8321c8688f2f46fcc2e02f463d31e00f43bf18e88d815f187cc5403f067c61e42b06bd31f209039f18e8c5885f187c7c84cede2874cfe48ce4a41c56321e4ae6602fe895cf6d4dce372313ac334eb524bd5c0e577d5fdb2659a7b468e82b769dde9fdfddf49cd42c7999f17f333a3c6041a499e77c0bc2421a42e2cd67ebd60e07774fb780477f9735100e8f9b0356dc8af756363759ded51c89d1f710719ac57cd40f94d27e1edddef41752cf55616b2d381dcca0d88d92f92c2bd9e1d56c9fc646cb10864b9c98393a0dc395155cdfbca3cfec44d7b66443b9facb35be8ff1336ffff35d30d097c4f04229ced104229f115f75c68ddfcc036a5589430c3e41f7dbff47df70f7b4356882f4dd776c706d7dd5ee2995848ef5a41c806fc08fbc9813385d8f7987888400591fc9bc9650d5479dd1fed10e613f5f6f876fff759f69c2e1159f8924050b40c6f224d951f421065871b9b117382916fda600195f607295bb36277d118670848827877f563bf2fe9fc2cc8680aad045ca40a8c8fc587a4746110d18136e7cb5541044302a0e299a43fe22b76c9288f8774f077bd9d56dd42a8af85ca731b3c1ed678741faa22002605f009bb417bb6aa1217ac604220df4652c438f6ba7895fd4196f0106e373e13383d5f8a84be589adf4d22b7a37bdf791ad046ab63cdacd664214b35c27daef2b9a985aa9cf41a23ec1bea61cff264de2792a6e4aed3ddd824433a0d009ac53725934dfd210b724a2220aa7c9707dd6ce35dcfc6cf198c2f2db2a4d426e8d87ea99e40145751f73cf86525b330f8c8db803499bcbd3c2f67fe21038ce970ad3a78982928813f7c48745e863a35cbc49dc189958989f484f6070156462f1accb3d85709a7b62b23d54ce01b7381f48236b0813cdcd7f66da5f28da8fa32120d0ded830f8b5db23948415c2edb40b8d401e1207b6cdb360b9004434b8d80b553bec0b58e2632261781601545d48688cc1b430d27a99e955180c289517fa2bc961640081f3a22eb85cde4adc8dc5af55da6e457eef5245426d8a73073e1a740cc00981c717cd085ce5a72398f25eb2c5cdc2376d1bf428ed2523a2e5f83320a35b48627d7d98fbaa618d3793b98cfb0b75a883ad5a8d37d766e5e9aaa13c5c444075b4b1fbe02eaa7349f7add57e9a0ba8569416f06801d8b45dae29867a2ef2a626365bf70502931b41b9b46b04de5b9cf3c50c22af971cc196799b6d0158a7a8e90954c7e1e0a861adea3a288239edcbbf081e55ba8b6838bf19c989cebf9624aa0a08146d699d6b5ec43737229e0d97836b37561d14766b33f55f65c4007997a9b427eac8da8600cba2282ffbbf6d998123923732449e398494aafcdb2d39507eff9323a4ec6492568a8ad15da68fc51352eacdbf23286b27a20f6c490c4c0a69f4103f953af7f080db1fa43004d31362cb751c5ea1d132fc97735eebfb17c29826cf0132ddc200fe7039489774f53bbc829ab88cd460bbc2f080586530921b6fee46b2ecf409be32d7ecf71e83e261d48540c33369f8144048e23a7c3a840931b15f5d4c6a7c81b5ee21a4299d1a67872ba8c683573ce8eef53199245eedd5dfffac6db0aa3e33044e77d235bea52329a429381c56d8d19db41948ce80e330c14fc43286a4ca041c5b501aa6e4a9e8c5546352baa5ba7d5110576e628cd119a82a37a720550ad1b4615713264470c4a18f2e83e51a4e32aad3f65584d9e584188cbd29b751514eb53a95e9ceeb312cff12a7419fe3d54799905c690037d2cb6b6f300ef0f8935ddc9c574ec46247cabda17582b9b750e92ff084da56a001b05b085b878da0ae3c0d0a6161077c73936bff7e5f36f829026a210aa771678713d8f3d679e5353b48d34b4962fd1c05721ba3f2302b752f3af24d63fc3b91803c5ccc67d6fd16372627ceb52ecbd52deae5cf0b9506b9fca4c3cca2a5f3ee8e1fc67e5f537d8835b517e6b78c1c272c2c40a6eb12870960b2e1a44e618142617ce9b338d9d3539b53aa48e517c2641997497d44b5b46e8ea4c116e023a923c5c6d274eb5d6201e90a5c00f96321e0a4a076edc88dde9de193c50de00dbd0e15a4b6e23e9a7f70312e75a744cec7e17ee414f0b52c37c5af1333e4a1858d3d9b99f9b440a3b5ed7f39424a8ee940fb320dd97accbed5ea28f997f5addcfc81cee1d1a45256df9ce0729d1239925f3c9e1e37a9651203457b76c2dcfd0db3de19d43f422a1cc6fa4ac64d9ca6665c4d41b3252b04f40771345fc5aaae65871b66b04cd7e3888e47aa261511b406b1d61afa5abb446802d45421538654cbbad77792436ef972b324dfc31c184c4093dafaaffbf00db71e5e39735e37452571c6a682f939af10272349abe6bac14e37509a1c18222ad78702161534753b292a7ab947c817ea8e668a960fbb484d64b0110f4817f6385a58a5ae29d3489a982c4c556d34629879268e85bbd64a9535feaa4dfee7a6401d7bfb82d1b6154a25194af8e1a010b0435bf322a515a68cf75a7cbb02f5d3264b47bf05118469ddb540a7c2c754e067ec410c01634ab02eb30634860a6fda269488f0d826d49d74da29c95b34c23eba455e27046546228af960bf1fb275865f9ff5661911f1592d8676f83d129f4ad8574efff5cf40e08274cc14afaf6ae8991e026960b942db0177804a9250f010d912db1aac8dde61b3c3191c07a4557fdc4e42be5ebb9127f1de733fc8c8aa27bfe0754dffac6bc4029d5c3c9bac743220b192dd338493528439843361ad91f355e54a5e7a0fd9f06759d88fd955b9aaa6647f408ea67e930a6bed848092c2b896c24bb5171fd8c80aeb340377a132df212b43f1b93dbb6f3e1404aee3581c6fde992806abf441ee5d6f43c32d000a20c1fd384f4f7dbc4bb49ce205e97588075fa0a35dc055b650d09f24e52bd74b5c439f23baeb01241070c58f5ca295d34653b79ec871145f8ca6981ad0f1c81440b80f62ed8c010eb7f1a77c5244809db8e47ab8090c50cdc1e7101e33dd937a253d269ce9d60cc9ca1fca6ce221d4d3d57f0f8f70cc453c7bcdf407f57d877e9d67be21847504b0a2e36d8cfc4e01d842df1c96815211b71ed102a2660110f1a14fe61820b1b6bd6975c7663ecd0e54f2bc69c830ff4a938ce89090ed987ae5d1157170cc074f8fbd5605f8775c4242adfb57a197ab637a427a0f3d8a6df7e0fb5f0e67df18643832a13cf197a6f587b8324b9855a424ca4e2db5d16e6e61aeae9e19d38500403b9e36690df0a9a59e33a4850c7ec5cc9f0143dcb945fd637518076b586cbc5dabc53aaa4ae147c731cf45411ef9bab8d825a6b9e632ab65f999199bb4ca49154ceab73a6761c6718fd89f2e7a1a8ae1211d7bc3ede6957d385934a7192968c44b3066c48cc7a1a357c6213aa064c6f02b90155b711e477736208886c9ecb4e8b0ef5abe21c98471b2058f256f0a3521bd31c5426959922ea5e2380a71ccfa54d2ed5f2111f0fe4f30dd4c582a5242e2309d6a3a9cdad816366dccab7a4379053e5fc1395892657a29478b2d501dc2961108f9dbba494d48bce0df7f14e0e9ff3036c5e31901b53dff87212a0187e43568d7fae7577c963050703628e33b2ab58378731d15e92f48f2e24440fbe22b518bb5f59ca743a0c8f856247c803e199eb1f184881aadc985afd8449f8431fbefd92b979b82a686c963becd15f6f6c1f07435d5ab60f5c3f195af6eb3e90dacf9fd791577a7faa9c753530e2f7bbdfcf0239a3f62dd4f91cd5377befe6cf949f56b9560b79005af45fd4ba2ad4cdac86370801d99b120d4e932d37d185bf31127255f3a8db31c4589e763c4093cdc6a1abb11690e52fc8f4715df831660f5f7a1cf21a4022881d7cd8fd8a93fcb61f483f9279a00b71ac0a3143dbbfd111afc1f967ea6b2227cd625121b03566bd90f0738909e9af5c1a3453c72101d38ad51198bf3381a5506263b89e9268ae8e1cd54f01acda2c2ae0e8a31055cadf3e41a8ee42012d08618ce2ebe329976f7170ac118ae1dae29d4c6a2ece6c99793b2ecc11c6322190fd220090c7771784e43a1e5c59e5199a8b3d2aa7e15937279cb5d931061c986f1cf43181b7bfb599c956afb7139a39745af32174a13b1f3621405042b9f8c764699b6833f8016a886141a136bdbdae32427df52d4acc8cd14d068c0dc11195581b06f9f2b0bd167e5d42df622045b8656971fc12402655c7faced0d9ac8d55f25e6cfbb8d9c91b13a83e91fe3ea06b9ef6111b5148797ddc41015158373a6325bf6fca08f39637c61fa5e092f4625d40a257ce6eb11c05f3dbdd15ddda1806e0a5ed2869e0105f4597b86eff75fa4db07ca60e1f63894cdb2816089223884af53b4337f60f9f5ba544674b8b9037acad6847d5b8c7214b0dcc1979b5d042c90d915dbee0cc9a0b0fc39aa6a9934a27990599a697dfe456d0d95797c8d22fa57d1783ca6cc2990b11d082bd564b8b09ce54024687569c2ae12711b2dfbe0c9aed8a0ae4870b02421db8a3a1eda489e34cfb9b07ed117860e52429bbc0eb0ad088dd9d39314a003c7fa02fef521db6eb1970d18b8d465e8cc4fde7a23db9a8a5d515b5ef922f9b9b01d540636645a7e35cc3402db6d7dd44d54b6212bdd96458585aa8eb468508871c272960391602dd2e4588531ece35cc56dfa4bd207db5664d06e65390824480ec56c18ab5e9a3c6d0c5dd37f605f1a2e915bf6766777f3fe17a8d2889ce0880c3afaab9a281543091fa338c366de34c51bdb9657ec1a9ae341ad83b316fabf427edbd316c7dc6085663c87a101976a37e718094087a6d8aaf3c1613b4c338a821c8427ba82f28a9668f8588cca463d56ced75649477e100ec5005149dcb7e4d5c9b805f0771d985274b7411fb1954b596321c4eee682b92863704af48b1acdba0591994c3198437127c05044290d2f4f3ffbaef0d0fb8692773584e1f1b59553e4270af83b0169a8d26e684033aaa832284785b54b744458e5a496c63bda1a5e986478a803c846122a629dd86ca5390cb37c58310a0410a611998c8df877fca1b5ee9d9630331928fba5071479852ba094e82c1b5cd756688463094a90ea685169c6a606d8530be195d01298375fcbf3ffd35f42fcfa96cd80fc894d59a815490039fbbc882d37336864c7ab2a658029f1a3821fc890d2d90ae8672cd118d67cd269ef20e5e4511b160e554dcf61b1b0fec58b0d08739eaf5cf9e46f0404db0373d0c2467835a12bc42691d69b9e63a708ddde6c45db9387d3cd472550a1a8685b699f1fca7037f57419b16aa6f5f1911da3e880d51026be9a0c4ae2c91f4846697a1e630b64c9f923a6f893f6b8d2d5814409032f9fc3f9cd2edf02ce6731fef91f1f9d1c74af0481e45327918f5126025f38a88abd316993bd16111c02576ca976f4ed0e9eaacc35ad338f9497910812e9c56054ed5f7523e059294d16174f919741a632a089359e7fb1fb6a4f545c3ac5e4d345a44579899f88d91a64344a2df8cdd9750d7940c4a9c6289510160a597a37304e4ae264e7c42280ae3c434439a593badb31c04c6164889e7e69e89bd301ef39919c8a48a49a7c2efb10b7041c4a8099ec915aaee5c3bad2f54bea98cc31b3e079663c11a016ce6629f1cb47a9f90f33da5b291891acb7876109bd95c970fcd850c8487cafe4197978a2fdd5d8070b22b190586c42c1ed4a6b205dc97b6d40fc1a1e3501e5b07a5ab45c4a907d74bb8a8ed415d3f32205ae5c90db1b2f8e8b924e940797fab1560ac7f4a1341565a6657d4976902abdff1f48cfe4cb3382f2d4733a1fb33f4b54c255488b5085eb1e101bc699ea18944e50f9ceb2830d0ec7c20f3629776213a7cbe76fe49c772f42f0442788e4c55efe2098ece66c10da7d69b999cf33c4fb6a9b001da108e1359052ff227b85236708ed4b2d71d48cc4a28161158cc8bcb8e3131b30b87f2fe360e4053a804b3982ce2034352d2902fd41811cf32578003b6121b327d0e491a5c851a59353e65a5a3d357b8e13c0228bfc669566cbe68860b915f27fde7dad6f73ac39f17ad0ceb0acf109a008526561fe4a9e3d3d84d98be8bf2c08ac670d8b9b7011f06bf5aecd6a5ca0fa7b342d9aca18982878b45f67b851b937125f1403a50b41956ed3e411b9e384a283508a0017b06ba933ad39b3786764bc6cd37e5ed088ce9b7fe1e57fbe1669675fead7fba5e6c94b75f485d3374f68a6632a1e1e3d034c6309b69294c063e7bc531b55532c0ae0ff6d0c12fda5071ca06559e0e1ea1fb1a3722c5e264447e0db418816582a720acc93ce8c3e70c9f4d911e46b9dc9054fa0548689f74d866ee4103a2754764ccc1695aece592f47a80c0b4dcae5c284197fdbb63716424f729a13169fe272c1ec70c3eeb5ec554d0d34c897c4fc501792282280d2edd9460363410e74716ad2991d6664e5c497eec7f3067e83e30f0fd3e9eef1546e521247fa0d23490021d06dcbcde5e0e6c6edc868eeaf5632a327717f643a1a534506b3948698a79ac0ad6fb5359031c3f42284ad106a01970730e0efed716296af748da3a0d50dde46831fef496af2f942598daecc14b8b905a37ac183ace0c5ae1085aceea4bb119042ce8a5e46bb996476b8ea7629e1ce07dbcc31f330bf669a95599bfb4fa9f357b93f01fe017e394f50cd7059d9745e9fca8fc35bde26948788baa2f18e1289ff1e12e27e37781ef4ec503d2c6c910ac514d83ec860c403eba82e8e6a8bb0e43f23d77e6fc1815c41b6b3c4e8b2427ebe678fe2b4243b1ea2fcbdf8904c7c72a5cde9802e1c927db06681dcd1ba8173d172963581fb78ba4c69cc9589110aa80eedd6ecf7e2baac1eb8277038963759042fc7cc64bde6967ff1143263c926660dd41568f7c805f993bf16b0c4332141c9d12db6af88a118e447e3b860f7420af1a5d10ab008b862b878f9b5adb5d9757e4accc09c384a860d52e4a431603bdc76e259f22a01b417ba5ffc126c544dbf89a3e56fdcc8e85f56d82cd162b7e761c08133f3094c8cbeb4fbb0014a24cb444f29360f78972a8c95cebe9ea1b2b4a2b15a4824c1f3aaf28d27922a3966d89ed1ddcb74235185d14b5e6c9749bcba747f329bf26fd6d8c0bde1b7148a8d83a821072eab17b9650dbc3a65fc3c8b96ffd278eab6c0428cc738193272fc00145b4eaface5922abcc326d71629a056913df85b454f2613332af046c68d6cb297e521c310c74719e1534b989accf3714a6815c1de53a61097f2166ef43da424844d9600d992cee97f1ecd48874c29bc7fee64d0c30b9c4138b5c8f40fb3fd0ad94ce20240a715124f744b3a915c64136ccf1c613ee53b842e8f2527f8b60d0941cc08deceff1ba411829fb20400e14f740f11765f0343d3c904980da5a2e935d48493c189f0c16f09d54f31789733e169746e3705d130fa7891a4730779e132539d57e151a6a678b47d49549ad6c6e5a0b1c3076b3355e3faf906cb4711c9dc052c62bb62f557ddbdc527b87819f5e0f7c7a34c5307cf9fa766e6b71280f0ce9f14a3197d66349812deaeaae8d7c55c3263fee22cf8bf0dc44837e69c33e4ee67f2c62e5199b1f5d1b291e2319a154619201683c0dce007b3366574ed3074b82a4c967a214c7873076167293b0be438635aa08c4e28a2cdfd6724a15b6e3158afbfdb7110b9542d1490229f39dbd70de0cc7115c0578d54fc28898d388828ed264234be07001aabfdd49b1109efcf6d145af1f13fb0c06d170842ceac02cfa58f874a289ab3a34616175f3997d1984e1de7a0dddd0cfa91951269cadd1e0fa1ec10aaf54bce51cba3cda5c495138c98337236433936e535f8015c9ebad54929dafc918843d811690d0353159ac37a9be7f6351af277e242a24bd7f364c25866e91541a2a6ec81f58550399d4848857914e7b16d35604461fd938bab0d6d5d58ca37f9df3cf85bafa0372e272afeda182d1edb546ebfdf074ee3fce1ee0c7d7f19158f025b5b0accdf9ec5f135561b5abcba40d6c264f852a9774084db29967e3ad0be81d9997fbeab384393f4a6ccf392052482f9b2eb02e5ff1dac09fa46c6d7304cd9cdba7dd237c8975d2bc678c35408353ba04dd73f229ee41afbbbc569a7283d0c46f2861177fe54a80fc47da0b5a5d522ed852c638eaa4f5b13fdb1141a16aa1370a1e81b3208ddc45b6d4114aaa8640f5d38cbc46c5c0e0843cca32739dcc36dde7f1f10d73f175b1207df9eb8b901c15a4ae5f67a41412200efae7039d74e1c6823a4076de35375e68eac81377309f9c72b32d5c2f8bdcb38115b187e605aca98c7059d2032bd3a2d7575177b9b7922894b50851988f78cd853384143e95dbdd2de15c6f8ef251c3485162ce8a38be706abab55d799b2ad7e41a139326fca26fdb7fcdd15bbbe26d71f912378ccd4706135d5f188411a9b29fda558a493f6fd0b12d54496f34280a9cfdaff7924d743af6a4199749b9cdc7114c532881e82db40031fb8d2e58ec1f2b38e05af7d81f0b4bc150024513eb74373f3f3d8c8412e7267b1921dd0aef55e2def1717336d360091864138a3264d2e23d9e3e755fc140e03316093c82590a6f5bb891985e50fd51f7082cc217760349650e87a2a7ba352fb0d808c0d469970b92b496a3d22ab7476c6c71498be97a21eff5c491625377b0798beaac618aa597344f298f177a5b1a60a4d958cc46b2587a8b0389f79f789746d53b6701aa087911beab2ce9751e17a8d67c02032434722f58346c6456d7226c0c7f59cf31d1c15cfd27cda0e2214b24cca28cfc68ab0cdf3fd2025fec065070c41b81382190a1c3e4a33f2c530e39e9fe550ce9c98301d613200f5d1a94563b2426f09092adfdc717d4169c92e9dd01f10ff42bde0cbf8ba9133711bec28ca869e4a623a7dcaa7f300329bb38a8658185c168567c0222001ca053881d640c2f40745b929163858ec03106243ca041ef1b6c19698c8fa0aa1c83f6f802561b098d007a93cec2c1dc11765f88dfaa9a4837063dbfc880164e48b5045d82b28fd52f5d787035ee3c833b0e33c14ba3bfd993b8b759ba3097617b97afb7efb85c307121086cf558418e94dd11a9fab2717727d130dcb6455c656f6a74ca296e70c973a7a86869373a17cf0a406d049c3ff364a04dacfac15c546d2dae56567e92846c8992fbff7cea21c6b3efbfb3f5f2ff3b1306797d4062f4a4acf2a84844b0b2ef46380bd866e1855e7f566693d2f201c9987f2e617b54acb34f12133585014babcd3f64b5b674396c0e996ab3e021ce801ba50ad456040d05d6d0e451dc31c668c7f22da9fc5fdc4e01246c8b93e5da52e1b5fcac9d8a6e0a0879e306b80a72718ae488b7d06f847dae7ebed0d704f0e1abbd5b5031ff0a1e18076de053a36e3cd1f812c8ffbd5d66ccdccd6eccaee955b4a29a54c013004340494042914089e3e10044d60c9034190869e0fd0d0f301ad35ad35ad5b584e26546b0586ae522764097ed5b3e8a65d7446dbc09ef0b1d8b3fa1bae4a2c1d8221086aadc75514f4454196d62bbdaa1cca345f21583994895572ad34188261088a3dabc7624fabb46a551157462c1d30ec4416267edd559369d50a5da813d895aa096b568bd62ca9ef964c166d793614eab46ab94c2d21b89550a754c7d248bd42ad5aae53edc41e17178ec5457409d196473ba1562dd7e9c5e5e55da21097f8ff302e5cc59aa5e56442890fb36ab94a465ec4c246bcb884a0d61a049704b1240813d69dd8c3f258ec69790e14858030fff22eaf126350a105e6c525748126ac2b872ab1585a5ef43571758592d8a42b876aad5a4e21c8994a26164bc963f1b892a769134bcb29043953c9c46229792c1e57f234bd89b3f5daaa657a9538c3ef58751caaa575c7a142d0642a9958ab92b7f24c254feb4eeb4eec0141a32e035fb9323aed95d169a764958665545f9495f2d9f4cbf5a5127d470906149d800f5d67fae6a4b6d6a9c14db4bdeef45c8bb3d51a56f2955fce3a2b09b112edd977993ec6e4f570edef645139854a9dbe542a954ab55241e09e3929add5da7b31d6b49cb76de3b8aed3daf34aa592c9f4dd9c3c1454a0299014954aa954e04ab55aad52a8d5eaf4ad562bd3aae4ad562bee89ce634a7f52b4e977164668d3a9af881f46c86d5c9b1be9d77523f7437c73731c2734356f222b6fe034916dfa37705c1b4608468813e70671d8dbcfd64175d09245904dbf0471d042be0860d906a27dc0b2cf6d5c9b763e5eaf476ccffc258b20f7f690d9738cfd382cc95772b19f4d3f8751598efd6cfadaa8ad3f29c242741e53c2e86caacde96eadbbbb8fe5fc6ee6397bbecf79eecaf96ccf2d4e527f8e079f0929c9f1704a2a8debfdb91db729bd8b431bc8a682edbf41f1991f9414721bedfdb720b739b94fe8964a0cb7069fb9384f38cdcd39dabe93b48fe8dc9d4ac37affcb53695adeef1751b47d8921a7b94a9ef88b7e6983b6bf6de234f7478a93d4df1ae133578892466ef3bdbf15729b1fb7f1defd12d56c4eaedeff2bcd46a47337f5ca68db5660e7f27adc9d39ed7937f6ab68f726f6b0587f63b8384da00c11f98440e7e61851722b95bc9b1e960712b90c15d017a5559c7e9a290b6a8da53ffd892fbb8a75c3944c8222aa345f7ea39548865d29ff1453f0bc1bdc852eefa627fc95aee2d5275074e5d01af43199334b6c29aa2fa2fa129a3224cbc1e141a7067f09d9419ca41fc46ab2e9e9448464d32f8d382b23239317d4227e412e63be742ad5dd5165187d738a5819d5974511e1b0c4ba535fb37b8d7b3e51f46ef28b2c9bc8c24469243f80bc4ececa88ca090706c76154ea8b65b7b16ce9616b6d2c593bb5e3ae17f411e59d6ea5dd9b93fbd93de8dd74dfa950a142663ecda5fca8df1e14852c993fc3ef003d994704ea0705e4eda078503e6c51d057376e4694a4cf551dddf8a3be649069fb9495a0a93982dad1d1450e1bda1971504c86cd8dd30c106ee0f868681ade8df63570cc99393367b6d74534a5c948a0396f799c5728a90d314393432684593497984c38cd349a4e68b299dafe27bf31939832f48d2acd9ccde66c26514ea2211cfa717837f771783decae6f53bfcd6f68b22943f197dda44236a538f38a6cde98493d184b6d88591efa72847044efece0a01000a0e70c670a1cae08003ab6039420d400148870780ed8323b08b043a3111371d4d48800004fc74f006444e868ce9ccdd9b432697451d19c45219230afe761783cecaee30edffc6e4700740f7dc32ee74c936d238a9b683a56b413da8892453737decde561abfb8c7dc74a1c66e5bb4271287953c72cc50d0fad8d349592dc353c6036562245cf9cbf6fce601e0fbce7ac045f07a3870fea638439828c00e61473e62ffab3074ea38bbc88c6a08b2829459100646232289d8dda2852a50a121a0387ab681f8bd9c36f1be2a1b20971e0ab95e3f9c193271e3ee93c263eff720c7798fb9c738e2fa67d77f1c2a4e6ee5e7d9268e193b3bb7bf54982854fad96dbb43d5560c52e6badb5facf29484ca762c4b5569755fdf698fadd2cf2ec606b756ab5b6de9f02f3b9bb7b459d5c52efae548bbb8a056cadbe6d4316cbbd15ae58906881b5b2a8dc5bb46765de1f9ffaf2d7417312a98cab7d7139c1bc7c0eb3e27b37fe5e6bad96a7c4b3fdd6e75c208ebabc3a5a4597dd576c2cb497f4cd16572375cd2efcc9cb5e179af3e19b5fafb5d6da17da63c2b3f2f3af7fe7eeb78cd56c180c1a03e6ddf84f9918323160305eb878912d58ac8851318a0ff3e2e292a136a05c6b91f868a574da91ce2122907a8e8a20c9a0e1cd660de3edbaa873567ddcdf4de4462721d72d8f9692d7bb41b2f8f3a89180ffbec519e318fb4835e8b2fd187c5573133525adbdc9aa6dcbf966fbaed1ce39371b7374648fc47c12b561fd6c4da23f0c411a8254eb0c4878de9b825a6bb5010933d14d9be98cf95953697cd05aaae3a4d5fa0e9f0adbefc755535fd4477d1d8032b179b4d8752c014a508b4d2fc6d35b72be1a1c9486003e80f87276ace152110ec2afcc9014c407215058a08928a4d8e490020d98408826acb060c78502882736e0b66e07caa1e86b41477374d4bd5a6a2979290af804e4f69fb5d6ea4df9e65f0a08e5246cadb5628d0a77af7cf3a324cd3e5f993f3a53e66f6e0efbac29cd47eba63cf6be5b6bbf7ea97dfe4e13b3e803a8fb3e16fdb5ca82cedf5a6b5557fffd7b6f7681b5d5da5a3fffac8f7f6ab77a23d4d15aa7b8e26bedb576d4f68822b9d36cdcbb0b74e6cd330c3286691562e28bc73bef9c2f6b3df0f79bf3f3d7f9f6eb7d6dcef71b6d4e6d4eaa7d25e1deb73fc21d7511b118131b84b036c686aed3327cda0ff1694f82864720770442e70d5d67f20e4c5aed5da22da1396f5ca7a9e7b0187cf34b1e8fd3d4e133d14f15baa0420baae9ba49a554aaa942a554a00a04c1a6efc70c1f37116bd239edbe39bde75e8c31c6d8628c31dec9796b8c31c63fb5fdbed3c9a232c67f350dbffd7dad8fa9259dcf25554c81560c8324419ebb4f1e987071f2e008cf11d997a1e8b0122c3ddf09079f07fd702508081db607fd7065973908881bb60729b9b24b1ba4e4689fb6072929daf3e64ca04a0fdf93567bb19637aed35e698a23c0c10e4fdf29c94e0ef5c99b739e504c26c784d529cf5a9b521da10255e090291d147005ae708e5782156bc5ba172809c10a5921cea1f3216c852d223c0ca192b7212d9616cb11447c367d4d440911241f8bd65a6b96162246d9f483b903142e6d32994c2e97994a0997179717530a627450f01d1395e5c93a37474779817981e921490f4c6cf703a353a9540ae67b30ea44f01a0441f0c5ac8160b1582c716cb55aad51c5dc21869815312b523c39277f482bb48b8b8bcb0a16d9ea80450b162d8cc044581bb4d073033020b5288a22f952a142858a970b27b2a626d363f202c60b18437278b8e23364665190b512183018304cc4ac8725b24704160316c34ef962c8c4e86434a24f2626132b2a72831ac8e0063119b1989dc2f34d913143868c2247e4191a0683c166cc20924d20232323030208448ec89e6c763fd498a95163cbd145925039ccd0cccc6c073292b353db81348043534343a391e86f3b90111cb28e910ab46e5063a3c6eb6ce829fabb771d66c409234d582341467c8c2cd9f435cd32b06163c30611a2ba1d88c8143be555516c6ed8d830d9a576a02239c044d813dcd0738649b72341021a76386fdcf06eaec31c362489b61de8c8066ab8ed40476e8819b962a40acb8989cf818ae8ec523b50919c9d6b8843dbb061c3060e1c2b22376edcb89123470eefe6861d523287b782dd393687d1cfe1f5f0fc670822cb29069f03f5302bede636bedd662c0015ee8789cf1604d97b8f8ea2dc28f6e85e1b746d1421295248b1831dece05e6be55a29808064506228c8de2656ae1db2b789956b87ec95e2da217b9b58b976c85e7bad5c3b14c4817b748f8ee63cba47f7e88806323070e26466af946b9d24d92be5da6b93ec9502058945b24848b52259248b14459369e7db39e79c4854ecb5d75e7bed104ed29c73ce59e79c4944494449444944b53a4112c30907cab516096905760517c9da0bc5de15b4606836ab52a58ab537c9ded91147e010498172ad93247ba55ceb24c95e29d7ceec9572ad93247ba55c7b6d92bd52a020b1481609a956248b6491907e0220234237675d0dec759264efccda9b64ef6ca88a09aacc66b39a295c50a10595f5efc694281d157c37d6de7b31c69aa67579e33aed954cdf099552812b56d8d22dda3f27849941a7f9535ff4ad0c2d2e1880e5b8f8772f302fb6eda7edd813b58861dec59ee38b2f712447152d54c414c14296737db6af6256acd06432ede743f2637bce70569a4c9369324d362b3559b7e74cc80aed6e23c312b3dabae89b82057964dcd979f18fd93e084a4e254a26134aca349f51324a0e6709a60f25e9bf5e2e7e8785a3cc5cbcd0e16cb6c14c4143f725c5403716915b1b51d2137a0103032f3939309e14183017580c578c16994e4f9d984c4ed79a3812f0990c1933c2caa2b1a251036c52c33bfdfe45a4642a045fdea24ddf84059ce0425fb1db5aec61c0833e2c2d4ef47a50c0613140cc2898f0752ff415370c435f3177307d3325f173a28ea13dd135aef002ce76bd16b950ec31e15bf7bbef48efa6fb191c371d86df04311465f415ef0ec5909274fcea8b62293eba00d15d01a2fbe5794a7a620f0a7f82a8b3f1b388ad133e147b4e7896d1c6bf0adaf841ebb3f1df9c2764158a12feb3f11bb1f1df9c9e1b64e39ca49ca31c5bf46489cf0e6dfc338c82f8d517fe135462ca00a2d7170244afaf04885e4db0bb8be993d88380ffc4ef1020a2c403884e5f25d94207ed6464cd726bf87c87d341a8a5d22843e3a4af5481a055356ae092377e94c46f0031cfd057a4579c34b4d863c277e227e2d907abc1f110b3fd5ca6c0fd50ef66be7b2dd43d82cf0088866d1fdf9402602881ce1c4610850ee6cf03bed6dbe125dfdcd6ded80f870571828549e3017446eb348cf18c41c05ff48324f951ec870ef29553c78d6aad5f4e9daed378c0f3f3e4cc3e24b0a70b7b8ee5fd07e29bdbfaa8343f7250fd3ace7dc5d8ae623975102196631f4333a5f1c1b37d549a1f46dff6a01b2899ebf843874d3f8830490f373f7c2e9f4c69a6f947e300ceee4c140625e948e35fabb96eb5abba7ab5545bc0fb7e1853f2cd58692c6b6231fd5ba65a8ccecc9c29e4b230378de9f45d1973b1cd95353a7076a63335f88bfe6c5293c73216824de9634ae9dbb1fc2a0e738ee5cf1c87d13fd1f0955348484888471df5d8637ad33869f951d99ee4fa6e6b6a623192fcb7b97aee309bc2be4fc237379e40ecd7b70f9483d02e730e4984a86a6cc464902f71cfd69cf33d1ba0e06006db9d74096c6ccb9c7f50ab7b9e34e0af9ef5e51752a7250cbbb43f637ca67b2e35b9544c49de683ffb52099a3c57e875548c5fd8620903b96fc61987372f6ceeafe841dec4868c7dbf7cedfbe4a7def4de77a9d6be5f9ef6fde93461f8f55d3596305bfbd458c2809fc732448de5771ac39135babfbe7135babf4ade088ee1c6da0a2fecbc892bcce9b59457a83b8faec128e706840ba128474309db727f630ec4b96dbae7b1bcaf5d7c471df5c62811dd8f6fe3fc3e96f8b5affe6dfcb36a594b21dc3e7dd08d25a93d9e9af637bfa6b9fb5f4a5622f35ad7c074f759cb9bb7cf394b4b6bb8f7de7bdddd4671f78944be9aad40ecdf7bef6de19674be5a6b75bb2d92bb5b77776badbbbb23d1d9aad55a6bad5673fcfedad75a35ed8e628dd2794c49f2b4b02c88a909d941bf1544862f02dc8f91afc43f6e23c3fdb860109cb81fd79e234be6b0a2af7429926d97a24d9ffe70f88725237990ac07b0cc06d5eb61bd5afa332db6bf3700ffd973cc30d4395110c5ecc0572ba5d8035f7dcc029ac792528c29a5948ee5fdfadb8a29543a038ec10bbeafe82b3f2d830ff7d88befc55a38e99c934e15b6d75e7badb5b86e9dfba40193e7a8046c5ed8d839190100083317000018100887854241160449a8ba0f14000f5a76465c5232170824b1401c464110c53010c44010c640084341ca40e50c53a802007c613884add2807b38610ff173cba2eeaab0beb1edf2e2ccb9c512f343ff8cf7561241b144afd16e1765e115ddeb9b439cee38284ce03f8c5b712632bdb616f1fa1ffefb53101126afc9a753d7684ab8e9d45ad26bfc0dee3463ff1592a4948eec27f6e4baa2b826d5952dc24cd541ca9eef6508be27c835dfb3b47fd220704fa64d1ae63410cc30809b7f33a1448b3a65d9336f569813d7112d7b272ee81c6579efad80e40d75246410bfe4402de463827973307d79fa3345054f0f81bd8263ccdb9deb33ee731f911db2a54a71fd6a86d1b7908848a4b260cd3c8c26a04014eafb03215d59d69046824b6bc696327a69d8c2842449495ac443966e7bdcd9c88cc77bfabfd26b205718b869e9ed12c4d595004dc3f2ec02b45d5c895ff1d1a8515e4522ffee05f32cbc93710bee3cf653ac4978b8883a8bff1193c202848e088e05257a1b46483193c0dd0457d15c5d737c1e9181424db2f64b039e52017862ee852cb97f271ccf0b29e0d91f0d11f973d284f92629dd479b963364b9a3a42a35a79eb86043387c02c143ebc9562804a8c33652bb800d0d7ed939eeaf6622cbf5afb058631c91c712151ce1f69b195e232ea2d925fbfc57f2c6a7106cbebe1fb20a1aa69e2dd457612531a21ff5e2cb50b150c6e87ab2be26e27d6d9547c486b3e0601f47ce6b35f193d59c03b72f4fc32688f1aab3f7f42efe1a0de0f581f003089e20bf01295a4e52882f8ba857ceffa5e8e0c531125515c704c7252415796162b3cc89fd86c4a79913c3fc65349a391dc7a66148ca96f98d0e07faa7269b4782b807e0a6634fe7699f2a825b4c3477c8680755c323c6eebbf4900066a90d4584a334c0e4b260eb708def9f0a4f81957bce9d71b5fa8ec64960c03adc21daf466ae11f895328d8e4f1410a4e6de3d1a83796e502314aa42c2957493246ee0cb9d5b2a93df6e4e22ec8be3a2668c89b69ed83393785c85f4ec233d82c74b945b5479acef8ad5afc850e3e6a386f8b42bc1e5c92b3f5e919a08b615227e79ecdd9b8ee9313d489203ed9a7630490e6b9814b50410068d5390b826af5d07e317ad4ff6f1129b0201956e9817c829a7e259140ad4679d02462706345696dd8df065d5632e7dd843fe8c05848a86bac6c34368d1f5f1c593b0286aec1b90d563660a827cb00c72713d736249f473fc58d91823f83b643973ac9f97811598e2a7dd8c231e672ca80c6b4cb2a5945f1eda3c8d957e1f5621a062f6cc4c058b8b354d4a0c63281c6aac216e928fe808d315e5ba73bd737b6ec56edcae19cd813c8fff5c097d12136626432a78d9e6b9d5d256140e7b29685208bd5ced0aab760b25ff7dba3e67fbfb25041e305456817383f97ccf236456e04d8303943945727e231aade5943b87f0569cc506e3c790fef6abe4670d350b12741f8872c1696941b7d9c5b8136b107d7fa77243bda1061a3153abd6f00c927ca7d5aa7501733a1c04de310e80b55e274feec0dd0cd1af642de5a17398df5e16e50766d7cff94c70379a8a1e358c91326c180d6ffcc676279a2ae7d1dd347fd110fa8119c25be313b7f749cd56da7d42115215c287fb68f2a11664e4ed69ed13ff2268fc5ffed8d451cde55f59049d6c379040d17ecd2e164c87cfdd08d8533b6755a250babfc194defa0a65eaaa23c468d332a0d82fe57494b4764b771ce0021187ff25ff280d161567591ece86deea96d291bdce95aa3e8b482c974b5b2930f6d5a060ffa28f511a8eac5aaadcdc5cc78483b2e86ca88fc162870f477d120033e20349d74fa11eefc1f0644b177ad2bb1f8c0b682b20b7c3933717e22bf7a165585af93c5da5180c983f4667e1ba1663447440ea09fc4480ba715358a4c8580b2a3c8305ba43392a2075ee0482f9108615c33bcf070249bcd606a04811a594f710affd78439070c72c023e5ca6d417a7b85dc64fd299344d355bebd542a240cbc848d4f5eb53adf72a0207041289bc72762c34bac91004a063ea544217e542b5afa80f625d558784a8caae1215eb526a89da7cc8301e55ff03ab4d9f5357c8f7953ce17bad5e7f387300fbbd7af3d891172e992a68165334c855988b9c43055177ccc153c4ca17aeb949b9498a033d57951f26d222412f533c59624bf70877720cd66f6b3a2e1cf162ce9333a3eebbb488c6fdc5089a2ce411c706c557153ce2b1d9f4f10742690b9a83983f84eea2a0631d88fec071831e80554ae7689657dd29c07e31046d164fd5af8446f5d72aa67e87b3cc21f999a54d10372bc649d84a67c0f316de509b990142d1c308df1ef2ab93852649c1ad309541346482a49c07fe34c4a8c089661911f950476fe4681aed4d7447ef5865f11aa8e859e42a951e652ed375ba36d12572ead9402205b8e10676e1e4485c93874197e0d1b408b87fbb854037c3c6fd5e45480b41bca44b5a080a9f2a087f7283873ea6f1615a68eb1df4ba33bb1a7427f306535fc3448f487cb33496e368028b65c8accb2012a6bafd85ca92a43f59a23f921aca2905c79dabbecbb3a410e1dc6a4567d2e353d3448f76ea1e67b9794ad8f3ef0de559747536b54452f2dbfe70403151e6a58531958160d138ace928117fe3644d9fe58a6eef8b0cfd57f4c20861bd62d1985349070ef2308d382e423e00b6811e8771262149a0ea87084acd42095c7539f26fa07b6efacda734cdce650563c3a6e352fc32d748b35a182d7347ebb241d4df5a469847839494f3efcd0faa1dd2d8853ab07f907d3d8ea30f260ffae2d42c1af8d7e89bbbfa294be274d40e801a24eb658e309adc6cde649c710a07d778796be1829e78def075ec665bcea621f8ba8918830dcb565dba8929c3fe91610a26f1a0a33b3f4d6cc6ea549d699a257751d0ba56f37abc38f59bcc56c85447ce104032350ae0d75cbffc2a2adfecc3073a9918134d489888ae2d991737a0a7adc2b9e767070169f4b4a0c96914955297298f70ded567d3c56c2c76d2bd60c147a0c7897dcc5b590cdf6241ff8446a8f625c56f6d75e34dd756506155d8be889c85de0639a5b07c2bbaa7ed132317ea8947731b1274e29ba516a2530b971d061165bcd93cb12c55f78263ec5b1f44e704b232d8ecf688cf2bd603e4eedaf1478594fca9b21899c8c321fdfb291c96ffe184f3d9f4b0bd7ee3e9309a2545bcd490636102c6a0412316e7c8638c7ef3b8d4926996ca312641e9fb14fcd84a3e17ec44d9a303b3bc5473e40b129307ff1e4015f26d662974654c85b6e062a209d3969488e3a4f7bd935847e7242d0d04bd662d65bfc7fcaac08ed1d94a158762cdd48a7a668b06f36394853da44eb83cb2983860808742582dec2d4f55645966916b92c231525b443ce254f608ec1ad71bdbc6abd571bd4ab4cc4f41c1c3b7f2b2577579ed67a3972c383afa4c1df281d2b9cfdafb965785d439cfd16bed7da1e82fbadfed116ae9f75d7ba72b532349c0898587e51773fe3d1c2862edfd6ed5a01453b0a7a8da393c4ce27fb5b757680ec8a47faabdd6e85abcf1bc53057ee10eaf0ee7b134818c1607f193d87e934ebf6306c5dcc6a0dea91beb2441f765b574dbb4d4db2d7fe489699cdd9ff864acb9503dad6ff3dabbb4f861535bff87045f8fde5fffe5e344ba9a384aa3d3b2c18591c46a9c2479266d5c1f01d7a40a0f118550e294b240ffe789ae9b5eafdb3bcaee80c9eb429aa21d53ae1a230f1f846abf55267e5aa92a05de4297db929719bdcfa46c989f66e107f13908c34a61c5f63d95b18aa41771aace37b22b5231d137c3fae76e7a4fa154dbe9379075cb993f646568362bc418b63045d047e2c5a76b1edeaa6395fe87fcc7f3c51f8864444a263483862274473345e05e0ffedfe3d76d85d89dd7d999be108da30c223b84eecccdd11a5183f2a82513a2b15ec00ac819d0db73e1bedf2a88e1fab20cc74e7dfe6e67107a235a10896c5c49b71750d9bd70b8d8d35b885cbc8a04c042c987b039b1b85a02c1dd924e2c8af9f4fab756510a1078f936cf16ada4f585f597804efa67a4f9f1c17c85fd1531a1f3216f7890407d6140c8d40ba7f882aae427979f4ea89d33938a0a874339526943f31483656685f0ef034806843cc054209171426748449c06e5960a7e00c063885367c2f7382067e20e933ed3b531b8393f8095f84e7fa5570447f0a071a2e93c81cde10d2a697086489c3474b9e5d00ca3fa839d3385c595bc66b54b018bbea8b3c39b9d0e47db1e1e5f5d94986bfa3a31b9a595908e69652957b86748ee03643add9e1b52c3dfe71db5a0d3f99e8e16849ace8eb018453c59a00e4d6020b12aa5e712403da615459f62baacd83c17b9148e1a19239b64694f5ec4cd9d18d0168c8a240b95be51d4bff68c32fd0019a62b5c82620e1bb386493ac9787d98191067b99391f65635081097b33ba7f122a92f6058fd2530dd67ba5effd8173889124672895ab1f8a1a66873892e0ab0e04e8a5e86860c42818c5fc9d7933e5de89f02356d356beeb75523a0bb1ee1ad9d4f70daee08dd4b2771903ff409bbc5954c7c672a22a55c520004dd3df2560a2ad59042fe563a1ff163229e8add3a81a939e0606465e0bf6cc3493609526581213810de48df73343f1f80daa037f6e94f9d6522372c34fd06a30be5efc0669072c28ab6c8d79d2cbfd0a325d3fc97931cfe2107bc4098515be68b3b6e8ea7746c8d796f4a7df63c09656ef2b17f339a21fa15b5d2551c9a7b3a68eed74d774f37fa7d4770de8e1f4d54ca614b3a9b7ebf7f5bfe074ba7f91dc8bf7076322c64c0f26d5eb4f37322fba75cc034a17fab1f26c14cb094667df32f00c67abbaf59f39080d7c8339819a1556902df448c0788ca0847c236658c77f9c4060b316cdd85958e1a0e988b11b129e5c074b913c4ad2a762e0b692bbdb35785966f89bed2bb5b75d40f9f6e9136a57aef5247e40deac29e43e0bcb042e92d054c752f8ec5bbf4133be08e400c5f443f9ffa0f58175f61071bf1b8b11f56c5b4f88746e680b9d2e88c611f6b7873f247e8e68d9180f1879fdb559b95d584583f054d07abf3c22c2d71da0a2a18f5dc044f52e326d4347d25810a19fab600877edf2f0d52a3490607dba25e143d325f762a1f1cc692afd47bb2615db940f999b913eeea4546a2bf6023ae0cd2d427ab7a7365f705eab99fd3255845338df5547ee69e275abf7a1ec8a3559ada64587042ffdc40961e44cad35e78df8b44402707dcc8aaeabac6680b8bee1c537fddd784345ec8e9323c252c3cfd8b7939797ab637e22a9867db495ab702c90659610f0252f181402edc85b164bb6a2365fb00eb3b17e99a704f3795f0a96e0bf9294bc37e73acb04c4303ea39b48a5d4480312d5f46a3defedf546bbb855113521df7eac27b9cfd88762de1d3fb17e9905346c45e9c9843ca0dcde4c4c65437a1a54898a142e169497ba1f88f88be11daef61764908540d1aaaf14f3058d4ad50085a67aa7f99eb9f61c00c14f55ee0ae0da5d7c9841003f9c93dfb7f65029faa7b39fa9776d8cf2edfc193d86b5fa62850163470898e3df29efb5c6a32ca6bde2367d6dc0999f4ec91913bc533641d0051b659f16e868b8dec11c3e06c827091512a7ac907f9579e1043b9efe70d059637e9d53767221f64d7f97b54175bb47a0c2345ee36fcd7830666e6e07158a7bbc00216343db8d713da29d616687a63fa8e2b67e7c6dab15e96064eb59b115526c8919d723720d66125837a6cf273e61e5a66a47180a48f2d025f736415c20d4581ca834281f0ccf822f619c9ceb8fd6cdce8a93b8c0d15937a27a11c6d290f3a4883004d7752a3a59ebb9ab360496c047618365c5f2178a3c85140c0b3271c10ef5711fa456e3329d146243981b38e62224e46c2375b8f96674ba6195f7653ec3d35e720ecc13243fc956f89a628aa7c808e0ea1c0b02cc34fca863e013c32b8dd8cf5182008aef9fb24941c50f8a929cbaef69e17771140607d74a64f84c6746e16ba6567cab56402ae0165f609c3276557b25ad499ef20d05eec2212838698a59285a7c2eb8eec7b5474ae606c3deae0a5e32472e599cf53a64f95c8751c9856c2f6573edf7e1d04d7e050bdccd0f0d012cad98565a1b27a03aca3f2c86d7c1810e89671ce6e2c1bf5f63b585e25cf01ee660c3b6f280d4e123f6c863d7f280d6e12372c82b9796a462756909f734d0c7675346ce6c739a6647b695399c8c73926d00a94742420e05ef0b3669892948f12f190181c9dd3810323c64b1c345ef183b79cb4548eeea641f50ff3013ab8cdbf7eb1f8c9669c795339b859fc62fdcdf583e640a8f865031c6022020b2e3afda2f69d27d17d9610d69795b7aa1df81bf5dd484ddd835d13ef60d05f68035dd76da254f276ba0d2df7c742b55e3e5590b29d681b585baa3c6ee35d8e4541421720f47818024e9f7d23a857204bda300b3b3df6e819a85cc83567e1b84e028199ce0da63e0098e2514330781ec15d0a7a901a6a3bf0e72dd0fa16607d0b05f07453aeb6859bdf51b56eebeba0b88e8b7a999db607963127172e2bd5ca1db00ee591dbf8dc9041b18439bb7059a99acf60794a9f03dc37671cf943293849dc6231aebca11c9c44dc62132ee78f86271dcaa025c13e1c636230aba3b1197fce3521b0494f27e25e8e310356a0f4313061f7823b356248aa7fb8b4c23330b73d46a22581c01231f0d38210b4f2e8a393d8f126080459df8ad34840d603233f28778988e54a28ed28660e9f6196b9728c9bf7dfb9a0ac7cccb24b0278e66cae40dc1bd57dfecee298e19052cf1b3a0145191fe4f7f2fee4d8e05d76a9aec225135b7ad20f6df866ab9ffa873768afba0ac851512604b91aefc1571951661018967041434b846bba9dd0ecf5541f0f1df7524833ef8fdc5ec2f2497df75a2f6ef3fac2bb8eb6fa08f9114252aff5fce602b3acb60ad4fae9da4bb522922303a7e85ee7c5cf2f4583bbbe40a2aa5db8ed3a5e243d1369cbe47e90a27f22784593516ca4fce7c8722a1337a1e0c5b1f059b41091ca6ef7fa4aeefe23551d36f825e900e6017b39b2ee2447aace2bdf21f43f1bc775a9d2d71d1411e4a4e126a8e17d86195fecce3113d192a4b32b3262052fb68c79eaed77617ae5afd2e4499609154cbf13eb97fb37388ec7999d97aabce60352091bf877d5059f1207f4501df2b49cc44ebf11b63505189a5c9c75ccdcbfc52f5ef1458ec5d05a0e4ca9f4d41754f1999c69e597a6b13a39cfd33a39c59337362e16a5218554aaada5bcbafc3c667d6123360aa0d2fc028bc1cee5f29ebe792a787afd6b8db2d44de721c6396847e85faa8d887897a58d16aae96e40873bb9836c921654478aae956f38b680288a0d14a4c331646c4dfc7e029986a243a7e73014169fe4b0504595aa8a1bce85a9ff9dac76e52ed1a1df7112a2dc48dc8957d12a28013d184f225868b3eaf92f32ea6b38cd2583c0abdc7ee4c2dd10580226ed32112d6fcb86bd10c32001088bbdb138e0ef03f98a18282e6f1130e8c19b672c1eb0c16f3094ca04a6a0cec14b6062d91884787d61eec43b088d2436e2c2a74da5842f5c9752c3d2ab90b553a876f776766552ddc8e67c121a07055f70871a0764aa160ae80d78c0ce5a2ad079a0754bf98c33c5045a924d1a410f843ae7e086dadaa1049c9c2be03962f701de91a6e0cb1062c9419b8fdae641ae7f52261cecf652fe32e327cc4198ede52750c9c47289c7879526e91e9fe0d8256bd708492df9ddb6de8a2109adb1cf83847c950121b49d84d10df32b87af8b5468ec0b81fbb9aa1202a6573a5a6870ecec520fa740602dedb6d157c02fe929104a5f5667225c4f4862424d1961582d8d2689850b6815d7c2909b1c40d633ee5ce4420db336e55db8376c46fd3862ba581e01a5201d91be33ddfde6ed9d3750f3be4bf90b99c0ea44ffc41fafcd454b925c6ebbeeb3d1c3b7866717486e6db56104f4a68bfadb5db07b4f4e82ad580e9957abcb5769f744f12b11cc5e12eb09dafa2e07f4b9f30f7db0fc7735076b2a704058d95ddb9aeb61f10482d101e5823188c78019b071c6679085099f5c2602b2f491c52dc31c71f4839b2d10b9efca791cbb8fbbab629b3382fd29b908c72dc407bf17b72a9570cde5cd2e76b644caa40181f800b8a3a00c9fa5d420ecea36325d6728cf9ac72d0479c22721c1e99e05a2b969ae59fdc1fa663c8a8191cdd08fb92e1cc4dadb0651ed0e19f375f7071e660379d313500b871ff50e934b8a94df1e8b2bd4384864b38141b741b2228c89aac85b484a074abd91d25f2bc4dc4766824f42d4252dd48e28be011d42916fa255f41b276049d734e002aa8deeb35e176c1c69568f5f0146e37a08824476ae1abce4c720bd5f19caa4733b9755a2b83a9077a8066b1e89bdac9e0ee20d7452710dbecb073d8270459d2a2eb4a7a693e045f57f50be79278fa2d1f073325264ba2989c233df229471ba57b13501488b55e3504b5b9443c41022fc3283582ce5d03278b9fab6f9e148e8400e16d88be4fb370eb6b88a51237233194ef63c91fe15c1710300f3247e48855711761104d7cd9dc1736c20faf150cf0a8e49030c1de082cae9b9120c75cf5476440ced2df3c22b965505f33708710d0870c348568b7bd8008f06a534b88c2b46dc28997d640d1111680b5a3362de099ba548496a66ed5bb780b1acea2bf00d2061e6a47f89ba0ecd588f20e64b8146a98ec799ddff6dc647a6afa8f1d066dc006d2b4865d80270404a39322bc8236943272d5e451dde4258edc942a2cb01f5fa237d8b55f77c894190fa96aaba5ec1de8f5a9451461eb1a8fd484afba1821718501914aba2e5f2d955f2233c489477da2601eba8d4899c85902278f8081aca926836cae881b023119fce2008dd9d5af5a4dc10029ca0d38df700a329e0259389be7d4da18780f779ad24ab3ac80c7aa54f30aa1a9330616daf41d0383730b3402cf4b2e8faeb9bd35f4d7aa0b1b29bed5aa64bbdbad93dbd1fb99ef353d3f6f7063be03193d6300fb1e7e425b25717c55386a507dcb3477b7cd858f022ecbcdc4330c528c84fb8ef512b9d8b58f430f572c50c150805d775f7df091a7b2860bbb5737ac4f20b5c0495c06a4a2d03849a51640f6b68f2b03d07361fc21233add22ba2e5c0ff9133b890367455b84d53d0650274efca89df068442a33010121140f90a524119e596b17e1a56a2e8f2d5454f2a49ecaa469936badace88de4b77a6bc98f0711ce411e4d144627a2b34e476fa4584b9b10c632f664df1cf45707617e5e16173c011266f2b0361d486914dccd58dae157ed311fed4582a4f7fe8c94442c8d0baa8e3ce3e49eb0619d2fd4b6bc4b0d6d59130be6d8e42932d59db8cb9c2d2ec8e6d7373b7f1d7e482440dcd0264649a42af2cf2b89d42a520b54d887ba6519bb04310c4b845bd27f040c69b50000df0d409ed821904a877800d117bec6a388f9f5bb86f60fc5a21360f0d5cd72c7661b108100b23b43d986786b0da81475a79252c977e343b0827b02a62823a6487432cb58e0ec4a1fae1c8726df41f9f80c2015cd0060b2a03fa3be04e1fa4e3634c96a1fca267645eb8d541831dde5199173ca1769928119d9ae703d8188cb1d23d1257e5b0dddc286c35fe85a10b836d39eba544a63b8c6bbc9c4a04a749ec9c6a564cc06bdc1e04d269a4b21251208b6a02d3fc1458319a1ac8933dd05e8980cea4910f4cdaf853a709b68ff95a22508df64f908ac0698652953aa5f0174d9fabe8ec79fcf81e8405f43ee27d78da1639720f33b09373baa84d42334494d6100830d924cc2b64144258699e2fc219ba79f42e0e427c9d5cf08d612cd16953f0538534fb8a39936f1dd2f0004592d9be638c485c08a26afa54f81af670114a517ef1743da55ebaed0a20e463451d38c660ef97d67d15f4bf296718fea62d966f6d07ca4b0cbcabfde44b496d4a9c5dc15ca8e13db4674a30707b0e462604d38a54ec20d8df670f21740f2b4bd0c070b9143d220800b82b6036be403e1c56cbf9e6491492fb0c8bd43f56412f803cc6c284ab0b075a3acf083143a41e40538702a0a30aa578e9360e496168a803b9fb08a48cd198666204aa9c0ad5d8e7f33958dca227ea6673f4c2703f52951aa4e350fd16b78f04d017e3b3277eed4856f5e201badc3b1d29c64fd86302e551c1da105486a13f57ee8a044840983a37e52b81a4faa9e17f9e40dcc3b992bf1f370960d984a0345f295f65521dd052755129390223f9b912950b93806724d47c109c3767fe5ddd9079cdbd52f20a664062590e46e0cd68b365ae3f04e204826dfae3833623517ec91b27182d26f35ab6532fa7962bc18ab10de18aadddf77d1ac64bdc3301ecd8ab0160ba5b3608f1e287c002a1928d1eaae1a74cebf91986f9788981bc8bcb72cb145888120f863494c7b460af2863f0eea072af6dc8ff2d3c5005b2c09772793113270b7b2d8abcc1bee3c743a6b8bc71b8ba512abb402a02c142ac0ef53455531c7d2046c6b67e581dea0d558c9554e0446c276178a1d6f07ed32419fb3cfbb39a8d10538bd69611808572c98d7f98580323b416b326539efc1a650341a1db25715c8ac7dd6b1a4b5d03b380d7f76f4a880d0f4753de9de4881aa0c2d0947239e0143847872d793c815ef9176c3508950e9b241649f7aabe641000e09a568158792ac27b5ff6aabcf597c0387ce79510a300af08e72ab985d3b240feee14fa182f57de5a97325b0cbde035da716784e6710b41e59bf039cffe161b8b507f2af1195873bb057954c0ea213f6e41e4cffff1736643dc934d78e05a22587d419b2fb08357290bac83be9f4cd313f9611f4988ab50ff45ef3103ebfd43b02bba0ee4282fef49ed54a78b8f0a11d238ca579dd00c5e8976d85760301fde070e82e46ce3480ee4edce85a8f1f057d20d432365bae69f8399eea1facb541daf99138be8af7f8d26f602bc617f73ec8e736540357b820bad69effe052c2df3110cec6b92f3418f4d2197e748b605022c5a5dc5739b0a0af05436f100ed1310a8d56a41c401a39e6b29a99e3def8b7e6b47bab423719e14b44bed6870c17a1553ca52b17dd32704a8cd1c9474abb6fd1b9c9e17d933f825d75628bba8086a00988819365d064176732d8a29fd7334d28f182cf030ba0347187bcc6ae52c688c7da074ff5f51b81bf78dbde34fdc02ad3964ef5084bd52d69e3f70cf25da7b741fcea2506284f84963691bcc46888aa6350e213219f00bba9878f159eddf05cb63e1d333bc340728c409b9116247f4e02d6b5fdc917086cc58f656b942eff4267f6e9b02046db02d7911e2c538d26dc7ce0cb33fabbebeb7044db3e7d0a8678a7d62004d8babe04d92685890bcb84ef8a9f49fe8f884daf3d3974104e54e5f478a67f29f1ff0b6dcdf66c4ce54224790da0382fb84e01e51ed068f647407aec2304125c348650e6810eac5c6cf606fa8843e4669875510b6d8bfe4e1563fa20475ce7a2daf54a1cb50b4d2421220220dbd6ef3556efc120a1a880d884d990e02e1e808f30873e16c1a2fb327af0e818d8d18938f7ec7cde01c9f171ee07cbdb2f83ac197c7f78e91397a2b1159f759df5bf8d7a79a6d3e513fe39f7c380d4abea036b4f277b8f9d6761f582150d5899d25df210326a573aeb7a33180b37276882240eb302c3ebe2af03277a3527a2f4e86f9dc1c925fe3bd4909566353938d32961dc738725dab6c1e8c2bee084e870eab0ae6bb9a351d9d27861f732f9caf8fba59da33e789bf290c4201b21e877fb46749b368e7273bc364aa2573cb47249fa9f41182846f2006644a5fd6f4fec95fbf8644c6c7eb161f59ab8e8fdbc4dbf596dc425e82f16acf3bfa8afca2477ee5780425abaaa04c7a594ecf7a9561c1a091faccc0dccb1e26cbf553a47312c194064018619df033e9745812843300a43569a59b0199be32d845a4f65c28d3a0ed3a8a62433a3b248ab9264070b7131b41a1e044d103dec353ecaaf17472e799beaca62ff812d99f43d80fcafa909ccba2e47824ea55217f4a777a0b131d2f7ecceb024118b5d00a71abc225de6faddf10af730fae80823ba6f7b5a5dd3436b8da645210d95cd8c1c5ced4ab321dca0e56775ba9a1e6eeecc2c9a05ac8292aab50d7ee6f4aaa54a441d233e04a3a386fdc907b6596427636b3a4481ef779f9addff72d08caa7e9202dc11d46cc81a8e8270617885cd7aabf027d21fe1b7e96a8ddc41b04f6e3d701d9c5399937ece21fc21f3655e0f4e9132aa3e2a33bfc8632a010dc097b947a8c8f7f0880b18c8c12ed87651246d11116e09a5e529750a93a546cb152d76bc593213ee80da0a484220cc229f1a99bdf0fa82556d4b7c95c1b63bd4df2964332e1432c5640a05afb6505178b84cb5ccf4a7aa0b8cdf34b6fc73cf0024581c6f1771250a5252444853ee86a027bae57c20dad1474013d2733945cd7dbb02be22b4f57b6d92cb8c2646765451650d4ec9990699e29a88070699a88e9920f80a273803a5cd220d98bb07122b5d8ad07a9211bdd9f8e0d08f84faedd2439b938ba793dcf4f7c33a37f9f28fc85fa13e56ea467b26ab6d08736937a365ac16da7f3d35964e0adf1146b172208de6170f07838be79f6f95d1efe2d12a888ef504cdea3e4b377e1ed2892487929547aac32d1b063338728d300637afd8e3e6143c90a0c6a864a38a7a1861815079fab1288f29869ababf8b43fdcb6062a60ec89a602007fd48b4b454e8ba701e8613a7178de66634f11ba4e3b3b8555ce272d926b6fc80777104f425cc0ceba725b1056a92b9bfde33192ef341098810f195829a1e762d47044e7c6112cb843bee698d3f186968ad80292874ac756038e4c28917e1036532d614337f3083ba4a7004deb98f241e7897a415857d42c837387ca2be22a4faf1b05381f5934c7d3b5edc4a7a28268b91c84b8a28df977e0534443796686c458ff713094323cb7467084567d2f5d94133d77e6f10242f38819ac3c13adbd197c1cedcedf40381e72db32093814691c12488e56aeab4abc03d407504df67d2c7ba8daaa376a9a7aad28ee525199579e18fc36853ed4c6956839598800bbdd29ebaf5d726a62ed20ca2265912d956a9a96b1e163bd4686c7bdae16619861fc57e7861aac2ed78a3353aa6d11c9f010ab848657037261c984b8301a10c52db4c8ad9106e00820f6c51112528ac19bb15c5c32216ecc8689f2165aa4d658037014a8d820b3d3b864ee9d23f698506f6d058f543f7c37cda4c1fbd6111960ac572b04bde41081beb9fbef8d75008f8ab477ae062be6d50951e5bafcf1ac10bf9cef43d8a8ca648093eb6dfdc06a710a887339f537d23c377bc16622f4ccc5bf066f3ab7446438f9c7f2bdf600a1fb4038fdc4a093c21c911149cd8800d627ed4e82523abf2d8d2d1f9ecacb0f0783c5eeeb276b67b885ca13dc0c5073d943b0f3024c520a7c8970e0f333f13dedc905a5351e7ff3f29bdf082ae1f75ddcb3a66b345287271896f3274309bf5aaec1277797b3d4656255cb04da0d6105003b66c98f9ab5dde2592f96f083a761a734c124f3c4541936de3e2d48702d436e8d5c11d3ee096c48932687c157123c885630e4680a50c723d20d7377092c55350bcb8ad35e1ca4806652d01f3cf613d8c3dfcd14998be3ea9bc6c6dc58724054289f6021e0c7686e0a8621847fb0fb7b0fde6d244a73e75b24dd5a88145d5ede993dc8c84c6006072b40619dd2306cb985207c9d28ba448d3743e67b7248c5d012469cb3a458a3ebd5cee0af66101e236b0fd6f0cbf167f390297ff0def7644e8ec0385f4978f80dcd58fd0caecff58636348cc1ad315556b349a08207d61b506b02a204ebfda62e621401ecf2abac3bc388e74d7c00b0c05b0a7672c34eaf0504763a75d20982adab81ca09fa044d5778512515a968f96405185a450f91fe25cd777e5de3758503ac50a2349568e9190fe5a95960e305bf8c9d5113f204d1a1fce77ceebda52a148116b4b2f3f0d2c5d265b926d3be283ae6b2530eac4e0fe29fd246b7ad81ba6baba0f731186dd346b3ff20fa364008b06c37ff51c99d7da61912707dcdfccdc96c4f3d73a86a429102c0ff129632b0e098b1ae5953334ee5b0115c797f5ba7eae38dfc7e136cebe29adb0603665f5f52abdacf9de2f4bde5d1c3df41819061931e2d5dba4fcba5b50ec1055e2eca5ff6ccbe985b1208a952c4813a6d35288016fbeb9251060a160da18b85cfe8a12213955f4658dd6a452297c11daf6110db57048da85e7f32ce7f821a0aa09d5b8fe84cfb40d40f7baaeb1f8ab02afb9e71b4597851498ceb7faaa61f94112151370d8e5550f84649004e60b80de46f00291c92bb97ed9a005a63d1ce1e410b2e50e15636be6c2970a0aaf2054646b1b9bbc2218a0045aa44f4ba42b10932e070bc46bdd72c4f3b7e7895c97c15470bf9cffb3a02f01f9226eb5f72865158070249479854a050dcf48b9fdcf8ae9dc28822f009f7315024d9a03966ee192d2c8d032f3e3bc141899570af0310b493fc628d92ad90371a590c27954ec47a19c84a27b2f30b81291ddc19fd397f8c145c5f6e59ae283605719105b490d4c8d99ccbbfeed4747bda4977824d6740b876e523fbf78f0ea8efafdaa3a3ce35f225580ab9cb09aebc76646c4f1370a87401faf4622d1a316c2397a682a02c37f5afc817831f6fb0a10869ab08d70e348a97c7abed699ca20d290633a53b3f184138f79a33ccbe2b084526168639c8d38047b97fcfb7d09c9f8f86ab702fe86a7916593508ddd6950f2b32f0f04b142dd4bc2421f27231ad103001a831721656fa609f96cbbf2b1b1c0baf62c34092c73d223e8719b73367e28df23b914d7ee443065723a9d40669aea9c8ca1ce167375c3632d999440a771c9aafd8571eedb48c337dd8b60c8c7b3a1b4d5104f346ccb6981bdeeea7bbb6b2976413919560358129e3a29f369487543c3279e5b5de44a859b8af3ffccdd62fbe13ce908a2d760a7049b5f5473d4940062a401a6bfe1a6a8e4112149a7deff7d94e067c0e37a99dcf39fb96dd18ac14e4dfac2afbb25b90c695fc19e53fd6dd5fa9830810bd91122b337b8e92fc0ce5eca00d54d64f432b6bc65ff80d19c13f28c54e87bea14fd56b20bc7b77147a1620879f20d1d07a262194e134fa08a53c8a02219d868e2ab8495b3efcc52ba18e8db138b987c78362fb17d07de995f9eb223c7f9c09116bdb376b87ede49848858b69fbbd61d09eb189f23baa391659f36cd58c8e190b6f96fb6ac559b7594594980886c85beab2f1a824368055560ff43fe357cdfd778358ec7bd11a96263f04c372757a6e91eeea93a6671e5a3ad2fad38312ed68e0839430522fae9b430aa49aa42db0a3abbcaa31f91d7cd751c50e1a7a9157d261bbde52d8900b1b5dad7e50890b33b22b122fca018ad3cd5b8ad95e03352e8b9f7e8b5859f95058534cc2e41de815477ad32ad06bacb04dcb339075b35a9e0d94c8c2717b12e2dddc85ff3aa527b41bf99692187201115b2306a42d94e348ddbbdd7c8d551d2958cdae5303de3e095fa615644bf2cdcf68bfc970955edeea75f8a408cca2039aaf1e8852106878940e6848968671ac85bb11849b17e89e4fb3b910a6c3007cea63eca3e4630e7013e1c79989369bad0d8224ed80cb20ad905004c700b4d4c70d5f16be2c961694d0e1206964257bef4de49652ca9452ee0822086f0836d5cf57d3b82fc636d5cfd71679f7bd6956ad6e8cd32f51ceb573fdadf55584119d04fbf7105c1062654e44c4ca9e182d5606351c4da0fc7cf9a80469dcee2324c8dceeae9d451925561635af8f082e40ac4c9e01852fac47860c235636ab90c114ceb032d30a945819a6797d430c21c4ca32cd2b961b33384290d65058828c11101ac4a0c5ca4e9ad72e23093e28c3ca6a8441626554f3ca3a98c2119258998d1b9ad767840cceb0321c9a57d782128a58198de6150e29a1c5ca72685ea10b90708695e9d0bcca15e4000644ac6c078f21850f8f21854f5791048533a89804c5119668b1b05c2df05912e2716405134c21c4c2d2592043894c8dd75411789971523bffc438434b8be5160b7f7106168268bcf6146600218895d5685e1f154818b13200685edf08b420c4ca5ed0bc624ee082102b83c1bf10210421af58fa082964a939904082112bb39a572c0f055b00f28a85bf50420a8a58ec09e8b400d8d8fa3636363630d8bc6063636313e1f3b6e961c92dd9eb7ea2c8b9aee185b9b5a64f3a5ad3eeefbd07618c170d2fdcfe6c729c8fdb92f9ba989939cade6c9cc3b086f099999999db8868a81adfb6dee2893ad759062156350d6ada73ad35d671d23422db840f3a84b02164ecd459b9cc96c7b332a0dc7ece50b5fb99a570934afb5cfeac35f7f15800d79f0e548aed27208c53e30cedcfe0468390db9f4516a3fdf4b622ef3eabc338fd409c6b0a4fec5cdb549178b99f89f82c72337333379452c668e110e7323e85acbb1d42d80ddbad88ff3a8ec97ddf1d9df52bbf1be9ff3ac2097ba0fb15af1f7b4769583b2117daf8ee0037bebbc1f15da5f98ec7edde5abb942608bd613dd68cd6beff56f4e953dbc19fdfa1226b3536e421450894d0c64f18271e845e98b63769b7bfc3be6db73d5366da889ce6a9dd9451a9a1619f61a6dab786965dec6959dd7e52d14e9fbde9b14a8587def9d86733db6ae8b9db33ce0f53ed3bebedee3dbc93a65b8625c434410acda9e514ddaf6273be8f8f610f6b4737225843bede55fd1f0f6c1ed8636a4f02f46cc895463a3ab75fa3ba89550a637cf83cb2613d425bad167c2c05181de6c8df5b068e6beeb3413d0a2f9b1abf69d3bfcf3293c984d2fe3df84cd779886182178e87182654717df2e69a3f263c9c9e63fa391f42211c1168fb9a2d68fcb993c3c1390560ce7f433dce8f2800e764b9fd44a78db3f93ddf5b11f8d7fb59e3439750c2d642073980efb13c73b8c4471c40d721d20edf1f8378d5c3f747215ef1f0fd9188573f7c7f2ce2950fffe4fba3d15b61e07b28fedc168a43fc747751d44540b8fdd00b5ec5a2260216bc8a493bbe1f2271101ddf0fbf70901cdfb1359f28361efdcd4152dfefbd00087cd3e0f87eeead947cbfeaad74f825dfb693f25eca0c47bdf14aeace3fa92d83a7a935be473dbd8e8ac3a3aa8de751e92fa9cde573d424bf43ed27be87da34781e6a13fd0fb587de87da33f858dfd5943c519969d0443d54996750996580434dc2a5eabc9b1d6474a8aa53a534474b88b9f0210f9621bbb41ef5868f1506279cf82c877a436d27361c15c92ba9cdc4eb501b064f536bf81d3502bfa4f60b3e47b5e177a81d83efa176138f19a08584e2520c9a88800d6398944e464215f56ee4d780840918bc40875552b52a2b0ec5b4587ddc2cd3622d002601ad0e49c032042af38fceafec2015669c29585a97e6334d8b35869b6d5b5cf0b1deb4608b0b5af043ed25de87da41af440e94bb4b04295183052bf85809a085052bd0f2436d15bc0fb553f0b10e2007149cb95d44ca4105294041ac026082aa433007d40d4dab43126092839d434c2caa0ee5606d6c6e5c4a2273630dc0cd68b4582dcda16d157b54e5f1b1d66be31c8f8fdfe3b31b1b911e3c783c6f4478d444ed331b1b118dc75683ed513b663ab61c72f86ba3c70e0d472df24a6a9be075a89de5692a0dafa31ef91dd5c82fa95d82d7ea5003fd56873a8947d5a13ec16340df2389e7d12738111599944c639c888a8a8a8e18391161455898c660a3531727a213d18908eb02a8ce24fa043414691374962e410efbc2cd505aac0088b526d6d4cd525aac3dae568798603d688d162b0f003cd48d75c7cd60d062d58146ac39d08895e66656fba13612ef43ed233e561cf7498d5fded093c7400f1f9ec70f8f6d75c88747d5a11f7e870e9f1d7e9efce0037d42481cf153990fd0452564e8e20a12d04514e020593b58e6033ca94dc463a0b6cfc77ae36a7508038faa434f1ec3c0f378f23c541f1edea788c9224177811505912fe25951c422aca849507db05dc4ff50db081eea6b8d87ea2c1e6ab3883471252473bb88644417d14418f1a0f9509b04cf4385acf843e412854c4a263430255a2dac08034f687028d161898d86a33ae095d49ed7a162799a3ae477d407fc0eb547f03dd41ee263b571e30c5803aa0ecb7a2b7589bdf20ca839ec7d25b55b330c71400f962b3d821e628607b06eb78638a007cb9506fc926ae57354060840c35157afa456791dea079ea6eafc8eba80dfa18ae07ba821784c08bd31a7eab041de03758915e21350735805bc920a02118440c802822800043aab2a1f1042042110b280200a0081ceaaca0784c0727e49f5c0e7a809780c8b03d070d41fafa44e791d6a104f5355bfa3caf03b549eefa176e063ad014407ec3b5a7558043c107589e5c0e3d41cf600afa452e1e90010191070002aaa1f5382e0004f0780c88080035051fd981204070cf04b2a109fa3e21040c35109f04a6af73a540d3c4de57e472dc0ef5037f03d54291febe96a75488747d521259f55ac08f3b2f9906850ec3bac082edd2e22f150a3d0bb1940d56163f80cd42516ca0ba0e6b037afa446e108d069004a0f9a0d48f161e3ca2f400c37513802741a8032805f5233f039aa007ad0d0ca69382a0cafa456e075a814789afabfa306e077a817f858b7bbd521258faa434ebe47e551b54a253ecfb4c15aefc631a177e3d8100c4a58ab158b5a4550e97611a907f93cd41f2a3ff93e3cf972e563407ef642d561ed37a94bec041e003587adafa45a002b6abd555c127aabf8830df12a26c137fe30c0804251baa385b5ba13d1f5cf2e6053e507c056ac6829891fec0758111a4b4b1670f2305480021380a7f8435c477de197d4269fa30200ab4aaa76a3c5aabd1e3cea56877a900146904066bb6e3bdad6af51125f7c41e4cf36ddd5bacdccb6d32ec720e77abe9bce7ab3d11f9be055447a371db1e038c56d241adc7ed9c539f9457e9160fa19f5cb30b248f64b23296584458d3427a548b7d9caa1eba5d05b5d567627816e9391593ce76f7bf972bb78e6762473fb8a41718be7c026dceee6f2773717f5eceff5c64f59cd937204ba5a46c4fb5e1b83baef75563a65f848088cfb6cee6b21a1346ea78a494d14713b21f74528f472bc081bbceb76fd3d887fd71f852251e412893ca7899cf348e41cf7a2d08b449ec3f7688a2ef39a01f5db1707f17ff0898cdfc674ce8773fdef25fc94b445ba48228d1b5f4748d1333aea42745b0506bdcd4a3eb065a49539eab22423ac468c70a69d885bdfe5cf98718af47dedec43d77f4dd13f0fcaa0b104b594b9f223120a92aee92f08a1e96166d29ebd2ef9d9dc8848db3733d52941dc877f6d36644eb623f1a5455a8113b753fa0ad1f7bdcf2e47728e0715b4f3251fe3395d94f9bee44ae020f2a7d4908ffdac59ed3b9150b0744d2fe59bf8ce1f21fa4ef93cb6dff7f07657eeb30e8673fe3cb4a0ec5c47732f264d38c823c08ddf1120c29dc7542295783b567a166e69d07e2f830145d125639b6bfa79bdf76c67aac17bd7bb30ec9bbb1ac36c62534e9e40973e6b92364d2e2430dccb46275e3522a3959ad190211d07e11c10a81bc5f4552acf46c30cd9358b5471eec278925cd77c29a87bfd109f0f67bde28c97859ca4e662b56333940e99c1c8bb9853471a08d48d027f2302e103b95e859ef3ee83f0c1f71ed4e85b6596d2eb4d292603d89a5a1533c99d2f471aa95243ec9782eaee8635b75989c9fb4951ff26d36b26386b0f91b5b7d8f0a5a06e438824deb635a82ae8ff5f46bc32a8bac309937bd94e3e977adf739f93d732ac696e648de437fe3438e0b7e97ab64913a26783beeb5f1256badd48627cd048a767e3d1d3e9f4ff5639fe44f4d9e99dde1342d4481bd25be9f88df3f8cc5293ed503a7fa37dded6065a1e96df0dca68eeb0ed3ef0410e1ee8031cae6d91345fa51dcf85efafbe4d83f6ce5141ffcea95dd76bd7958455d4bb792ad2780fb91b38381f2acfd9be6f547e37d049dbf8d6b991bb82c6e864da2548bfc633d56ab0d64040a70d687b57bdbe8ac4de460dbaf05ff7663be8543b1d9335994caff21c21e28d72d1b7aa32dfeac69bbe4df57d6ff0df0d2e5ea800c90d2e5ea4a0ca5b99beb6cd7bdbdbb66dfb57a738e09d3b0df18d485bf8dbbff7de7b5beda638b935feb4a91eb41d9cf277b39d935bc3764e6e14f8eaa97372b7bf3a743385a1efe435aef77a7d0359935f55c28fdc0d3b44c80fa87fa773230b3060c080815af610be776998f69e76cd0b4e613e8649d3cb961d3722b3e714a017b38939e7bce075cd26aa206517ba99d3b092a4c71b7982a6e2f7dcd411dbf596982af22e7c26ce5971ae63d1e5ad48c74add6fc473def7d710dddda69040b729ff9eebb6eb9b3ad2f55c6e2150b79d8d4dbf16060d570f41fb916492ac0103a19ffdaca8ceca46a1e868119aad6ad73f4debebb3eb3d0c5e4f427a339ab711c1a0e75ccfd85f6fc2ace986be4c52661e4b30df1ff267a64ae337ef256f3ca916f05e90c378220c28aefc773dff1412fff8d146e9f7e71fc2e4eb27aadb0ada17c221f9e0b5047b9364481b107f46ca36db880c691608f6d8fb33795b0ed89bb0cf78cb21b3d836a459207ee30fad342b9bc104d408cd82769c94e4adb2f767d203865a825993e58d324effd2e5570aced00204209bfe1a48ecb499e69f34ed7bd332db413617ab405a800014e7a4f43fdad47c99c212cea0ddbc50a840bb0622d3405ac8a0ee19cfe240415a8000b4f564814a0505dab1914f4b078aca890a0814321333454cca6f938cb67fa60f1bc1a7e3d75f17075d40bb6eb596076f16cf0192d15ef7d9fe699f1e74cb814c7de9e019ad386e2ba3f29ce97e3d574453f07b2e13e7381f4e726438de6dbcb7aa12f43a6c8eea4bd5c570ce2f0eeb555067dab5df361b7f82366e34511084df44d1c60d18df86edbc0cf6439e445d5a8982b6dab918d75feb1a890aac5460ed34ef742eeb3c9dbe6e3bfa3d670c613c3104540c613c11c63bb795a1433b2e1568fe045fe9c89adb3919277a2b27f17dc93997d6bf38e7605c0c5fedb05e83a2e9fccbab02ed9ccc9730d7cfb48f77ecd142dbd1380a45a973b134b7f3202fe34f48a2feec6e7432f29794648b1097eca7b3b44f7b61f2ef49dbf9279982b618e75e526c24319e23c53871eea5da274baa835a888b737dbdd25cde88f4fb1e6fc7638c1e5d7a8bb9afff9deba2944d4ae70a016f14d7714eb677b784efd25d46f1ae6edfcb08a58c32466755ff90f764082be8047d2fe6f6d32b409e3049504bd314547ebfb4a923f165b4edd3ad2a8c54430761df45edc573be349836ea308dd462ac68d64ab7b4cd34bb65dbc7b969bafa0bcdc52a2bbdd59c94a250ff1ca75219a9c18b641184fdee6d53fd3df7c8fbfe17055a20aa2cb38b7e2893264fa0480982c7870f950d596e3f74d2040cca4a3e7d7a925606a526945f523edb8e5641fbf68641be18754d35ca7bd835a5c9f9e9abec8d6e566dfcc6b74b055473973242e79e1532064287bcb975a819c59f6c87da344986ccdfab52e94be79bd364a9f4fd9697943685e44a9b92df73a5edfa42711bd1521ebc92e237dd01eef3f7dd0d9bb9efbb7a1fc53678880df802edbb7ed3179a9adf73a7109090d0cd5c9b357564fef69bedb60f027e7c20e0c78f1b919b75ddafbbe76fefb7cda68af0e529dd051af86e0fb7227e37fb68e01b450afc090491386978579b9fc9daf5cbd72a151e183fcceddb778b28bc5cbe7c6dea08f48d88e639effa67a7ebb1ae27c3aa5fcd733a2c43782b0b5d4f85350a7cffebf263f5bd8ee74ad1aee965757f4f1df1bfde2ddb14922b0a3b27f99a329bba9e59367ed3a9ebe1bdec0b81762ad585628d26615eb41041cb5f219e937adff7fb0c3a6b6c8082dbb6f34153432f3716322ad0df4411d18e938c763c47a3516adff11c2a4dc5b093273c220ca0db46612eb33333d75c7fa66e83f2f76850171568879aa85963d211d2e900b1d9885c45501b8ef361f3342877a7f19a9a47d1568da4db4f5b6f55a34c8b8ea11d2d738516b7abb15423a98611053275e982887f8811317ecca01dbd5f946ea77a6a86164237bd9304215c82323ef169870e638cd76503286ef7ee656c0163c718db2f29ed55c66d3bada0dddd0d217cb19b4a43f86243f8623784f1bdf9a65c010cc8d45c9a3242aeff0bc142a9e6b29d6528ff99ef0249427fc8b61372f96bf480762a0cc3302137fe157fc21c137c3a9ed36cbbd386695886613a2e1f5591058699e625ab469da341d538c7a79650384306aeaf7febb80a883f19de04136498bb3b0465b42bd058a544a01c978fb060c2edbe8c11848aa230c6fb8e99d9ea38d74f428176aafb638c408ba277e2f6ba2edbf947ab724ec7a547772ad06ec2f9942002c707c2c59faa195fc773a27bfc6955cee900b96c072333ac538bc17fc3d9855b5bbafbaf93fbd751284aa30e101d9e785955759cfb212ec4368b2f16a01032d325ab2199d5dff1c518e16316f38db79453e4400a51c8ba0af415b3f84c15cfb142e4c5878c9656905097db7f4109c5733a951351e5ada09039a349413b269301c6a901edf88c5213aa58c980e442494a4905101946170cb85cc1841959b22863832cca70c20c201b5d9819c2bbfcbae425fd3207bb1ec8330ccdf9a6e0c196197411f4450b1962a4fce70b40480aa416cb6d92eb4d0f5b2d9e9f9f2c5fc2686971f969a2223039edc36a96a9d26e2bf457b122050a1332cce2ed896a67bb7c540512b763231f2aaee36e1214c2739df8fddceb7eeef58b3e1ec7236e628a269334c95875b4f95650c8bc17827f402befcef74e453ecfbc4e9e3cebe4df7603bc52087fceef647a939369bb9e77d9662e3e22f83c08bff90784f2231509dfdf252f699971607c9b433b84d91d3b6443e9784ebf10cff9ebc58b3ba3e0f0a34befe747507fccc567124170e1f52e0c8ec0186ffcc73f640862d4dc5fbaa5e2cf33ef639cf8504acdddad334efc7725f2e5952f938b2593af79c253941d21bcbc618174cb2831460bd48aa6c0833180ccb905bea3960d8410ca187b4888dff8730c68e4fa9deb77ce47bf5bce47cd44d1c08804284518601c99e13338e74ddcdddddddd7fc7279d1d2319b197e580bccd125aab7ed96110ddddddddddddfd2f09a48646830df9a69f3b2bf73de7b904a42e35a6f4d3e0b7dfdf2f3333333333f30e338eff0ccecfcccccca8cb47566481b9b985e0115947d19b0dad45eb69bd1d81afd58230ca8f5398b9dd6b74966183ac684e1613e79a08635cf30dc9759b386782262eb44324a042e88c2bc8094b17019777b00432ee9c94a250ff1c333fbf8a935cdf598cc4f496d9a58bbe84991a9d28fa0dd40d74d906da58405be9fa76db3e970da265458ae7fc8de276ac6466e95222cc164961b2c8a2c4e4ddf4a42c7e5f03137756725672567a76062aaa20830b2131170197b9100acaaeac9934f9d1f22489cd3839d3adce92d3713b3def942ad7a758b93e8373fd2a959190397d7829733b149c56a042de62e67b5d97bc24ac7c55401e64e63a5a9ba169e95603db89ae24cef94e124e4ac2c47378ce0692d20e6141bc15f63a1c44bebf0278d543744be7aa789cf3e939a9fed3cfed4e3ff75509a25bfeb355836a416be39cf727813049da392945a1fee575556e097a35a1afb4dd3a6141e3cd78836f92b54b72e510bf2c274575a355a94c5f7cfc9dec929fba6c11bf1714291dd45c78da2788fe69a0de72fb2fe9c4733ad593a1e98469a5f84d43d142e3c72a84dbf0e33d9326feef4221fc4e795d48e388b7ee94d78ce358ae7f2379b6997bcfc4df3bc929ef46cba4490f13cf7976687e7f13cf715b8391b79a56e7dd20a98014c92fce304b218926d26b05d2bdbee3a5eb310ab49b971d9a3f2dc7653a1c64bbfe4288f44fb76c403b5e72b2c4a71bd0f9a66febf1afbf7c23e2c37574fcb7ab530695b7d34972a3ed925c55cb24578a4ac56d413b4e12656c8303fa3eb7fb2d7eb65c045cde6288a2cb9b4b817633045f74f141d1303472b4ccfc3624f1e5f551dad705ed26172a9e8dd3f7de7befbdf7de7bef9f8e11aa8576dc7d76e8faf73e3ce7b2f3d138f75a08d9cfe5a31f98b9978f7e50847b048599cb47508cb93cb87c0445d05b51ae79c20d89c6ecdb0dd70d9ed3c36d5d3c2e1ed1eb7628ab938347a5a0ef5c435f4d4ba5e577933d213acecdd805dd505d885bc07c4c64a4c0854d9664e25065080be1beefb0333d840bb4e333dd44f21cf8cfc7179d77d3ff52d6eb39153d33df8db3301280414649262d2129193da317c45bc97f3fe4aa50decd5382e7743ce6090769c6799784b2bacc90469009b4e33352e83e225ed26a506343023fca2845628494bc147d495a1af3043046446784b874112a2af2525404a648b66299eb11fb5416c1006159867d44e2d2d77518c5a4084614c3575c85a5d885cbcb4c9816cf728f6650e6668fcda26903353f0ae636faa74c359ee3f1b9b7f2f1a5212fd79f755cff5ff9683338014fa1cc9099eb4f4d582cc1f587507b5b0de3c89f991dd21ec31cd36c14925d86fc6abf793e82398d5f9ce34b733b96b9d208c297271a14dea60ff56ee81528bf1b28efa6dfc6b97678ea7a6edfae41960ad38307526c4599143dc4c42d70af525581049417786995410618ad312ea04d507ebefc88db3f7500a60b582832230a455bb2383203c5135820018cffe490441233609bedbed47b39494d88b95dd35f2218cf71236e7fdf9e1ee431c8df74dd460cf294733fecdbab1590f684bf0dc49b3073bb9ad79b0d7e03a688c1ec69bef549e31a0dabe07fcc143e0821843e02c833f25086e2208ee2202e73e6330d8a79b00268b8ef5bdb2c8a7122b49d81fb68988ba8f73452eed36810f74d9ec851c68ef28f0a4900c3c4edfec9008c09ae00d33a83839fdbfd4d41963d3127a5d0761042f810c27eefcdd41267f9fbedaedc21b7fc6e1c4b3becc1cfc852bc07e0a0c54737c812032d6e60c41347a0503e5b391981beb7c2834412462cff1f44d8418b9559a18c110b5e5d40a8ba10fe0e9fc1c58c11cb59b232cba808f01aa16174f403337ab0050aaed042fa0fadb9fe5690e5cf431cf532b882098c934ac085153a38f3458923a8a8e2f4848cc92891c6991f2f9060a208405408e160b9bb9dee41d99c8106505041128ca43084c47a4938e1c1282fd4bcece3aeabaeeb5c67a3242def67d8a6d0f27ccacbbab4dda490cd3033263eb11cdf4f5e50f89d14d4956fc673dec7f7249f14e54456221914cf71a307453a81a202fae4592862e8db3849a65ca47fc2ac8d17ff8909e3ab664537923aa03c7ce8fbce8d6ebcbef32425c6893f69fc530f2864a5db5f6e7c4fca7cc9c76c1fdfcb780ea655d3cbcad7a1731de6c625a87d41e7a414d5bd93b782acf850b2405f119605184561f84646c55b5755f9ced637eaade43ff6995f5aa03ea6f93eefafa0de4ada14fff56f056d8ae7ef986c8a5fbedd79368583fcf929b63bf2676a27fef5f1af1e16f6b44b0bf46d3934ebfae7fd4e297e9ebe29ed77de9b7efbbeb3a6f8af4491fd15d3bfbf12c56677e0cfbff2def4574db14d691607d3bfdf31b132bb331ffe1568536c51ce61ffacf9272b688a95caae5f62facb5e8962e7fdfc2bf04dbf64fef597bdf27e3eb3b0eb87388651d5eb22e3e7e2065d6eb7562b1b4aa051f8bb753b7e0773fbf6103d97ebcf64c98f75c75b2c2c9d10f0c64f1d69a11bedb31d96bb4359b28b9f3a82dd68a7d47826c255ddaea7d5ad2b6fb4687fdfdea6fc14ba9435ecf61e42ed10328ebccc38f2af4bc616e3c8d79cebb1f48410376b7ee65a347a2bd3cfef6dc764e7ec1866ed626841188c60307a395a8c737d8ed6bd1a3b75425ccc88712ecc68b65a465c9e39a973712e3d41e6f6cbea574c8fed981eb36c837a37f1676a4e239e136ffc1ac0dcf8493c67bbf1ab788e8fafe28ee7f011972d373e47462273a32771f1e2765ce6c6169724dc8ed3b891754955fc187322156ef413dc0ec88d26b8cd85dbd5bca93e1ad45bbd8f6f03b49901c3e77d056228be141e7387780849978fc01873972e1f8581843b842caedfa31f6cf941d1ed8d49f7e02d16db167cfeee022bda16b4a9252c68ad449103b3ae60e1312cdbe231acfe4ec263e81586004633f75ebb37433b8373fc52ba1d7379b3628c276c614543af20d0ee55dacf9641472157dab38e535f716dc5b99e1d34e7162430b878b9dd42fd53c6d63e73148ad2a09ff9cd5c9ca67fda9c0ff90734cd392d15ecfa6e4d13966576cabbd70ff16e75cb3993d5aae637fdf087be9ff115e0fadff00f1f3b977ccf33ef0de3c4b0e4ba61ce1fe7ba92ebbdf72cc7387e7377b8f099f36e3189f59bd8c0874241b7431ad4b6d7d1d188c8e6559955a39b691555b8f3a2c1c5996b9a7eabc9b20ccb30efad52e6e06b958639f85945710d73f0bd3e73f04d55e3c99039083b29f73d2b379e4e3694620b9e8e773b11bd3ac608af772a578c505e3142795dd76563845c85e7eed3dd83e69c33cea899349ecb84097199b60e5c73ce0f5c87b30a34fd7b1f2698447777777777774fe90b3308fdc8d9773c4763cedf9f6f7c8891f8cc9c3f73cc79174411f6242c89e37c30790204e9bea77a4f8cfb30314f890b2d9735413bcc0833c2c23c882569f1de0d433fcfba9ea72f10ef5e5588dfbc18df7b526809a5bc2e1510210efbbafd917fcc2721a4029f67dec838fd9cbf84363e09218416324e3f17217cf3dffb98308e8151c6cab761901fbcf7de7befbdf7de7bcfddfd6d98732f6273a758b9dd704e34fcbdf7de7b1df4de7b1921e783bb7cd50eaa0da4e5a7b340e85bf0f7aeb762bee9369dba2a774a156f8d0b86114209a310c7227bff8391f4e5f95cf085991e978fac38413b8124c6154920a39de8d17197d3b840bbd9aceb278764ea5809ab906ff826feac347c13ffaa1adfa06ec773e78517da29407c7044ac7c74b804ab98c5af06e7bcc9fbb37c13854869872e192d10d55b5dd688d339ef9213fcd1b99b17f042ffe85e405f46d113d04703cd02be7ff437ca99991b8c9ef73bef3db456acf42835d0922bbce40a3712638c9134274571567e7eb27c09a385cb0f1051111825a09f9f9f9fffc2fd645ad0ae5b3f2d1f9e20deeab24e749a30928e7ce208fa378deb4eb843f0c29b5b1962641a51149dd10afc6859aaf7fe2db450fb3322c529b6291ce2cb5ff21eda2b57a2d881ff6c8a9f59f2afbc8f36c576e87a66c567967b4555b6ccf079119860c2e48e240673c7904cef1786a4c29886ce2fb14cfcf19cbef05d401f920d5a97bb8ce4e5889b3deda2027fcfe5eb9988f14b1118ae890ce3f46346f18b0dbc841b55f2bd767d7b70f15af722d2038976aabbd1105bf7fab81579c144c30bd764e112e3f41779e14e0bb17f75087becb18a89f19baef34e0ce98a02ede217a488d4ff1c756b2415656c337dbabbbbbbbbbbfb7cdeee5af48783127051c5ef67f256d7f712ede6ed614d49440fabf6b05238c8bf7e47fe65ad44f1de4aca9f593bf0a3b562536e290bf52e2cd04e75051aaa25da71ae84c7dd4d1a54f3dc28fc52b40bff8a5de4624639f7fc06194b4229e39bf39a9a13325c3ad4f4fc5b2c1d61f1a5afebbab84b3281fef571bdcd50763dd7eb626677beaeefa4bcc33a3fab3eec9f3984d7e5261b85df947d766d4498afc953da47627a7ed904fad78a1ad03819f406d307a168a8c4cdf644390a159a110000000000b314000020100a05c462b16844a289c1ba0314000d80984a76589a88b324c861148590320410000030004004648666241b05e8867416985005c033236dabf73ca0a07a7bb54425016e1689120c3026fe0fa0437bd3d85637ac4d6e83b15ab798c154050517a230f2e98bc988aad370b0e202b1df70fadc7cc48e5bbdc465473b614cdfb3b5da0023bbabd424b42931e3e4aa4258aec408c1831b12606db331755ec562af72c7532e4da4b93a786b0994f627aa17cc493107d25c826ac31683d65b24aff119e4c8adf9c1a73afa2d0fcdf007d5435bf4a3a8dc78c26b6f614e9929fa574557476ab9c7c6325063a02898c8ad506fdc4d2f619671e7c4e179de0f38491a773ee09fcdae1de8fbf8d3e3cc070a4de3bf8269b4a7e17b7f45d5eb95c9c5c6a17c1004645feba845c95094daff16fda8127f8e5f715436144ae9072448f1f4680cb568dc785e49ac30ebe87300784959583331cff52b593e06c812bb85633e3e4ea8c576252bde7a335d38e6cb9b5c21f91ac8c8fb5ef4a22b1fb65dd131541f9a5daf0cd7c350c1f0d28569cd0408952ad08fd913fcf67ea82cb367d847e251e99b1181084cf3e3cfaf685ad56ab3afa97a43c87ffc8540f25c315194578491c18b9606e4e61c67034d89fd338096e5930a6b87cc95a38d319704904d122fb268e3c79b0487c8180e602e9d5c2dc9533943643eff54bab0e4de1bc563cf7df25f1c88c9aea7e33dc101bc42eca22e6b7537b80786ab08d127046979db23ec707a6954a5143f9460db75e8c4c37cc45aac805e99c7068e7317d689bd454fe80b36738a58b94fa11985dd86a3b4bfc36e894907d259ad1e88b5d1dc9e49c8425b21ac10ce2d9777ae12d86bebde8703cd0f612d1bf6acdca32414d423afe6a602390fc8ee2fd0284db09a70406eb32e62acc8c761ccf1542484822df0512987aa1118222dd6f40c878dd68b3626f2642f4324f1d0814c914d6d6e66a83911a396dbd5dc0275005b042e843d32589c7224134d904c54137f606fec202f5c41dca22403ba3a18aae405efdd1090aa4e6f50662e61460b2b5a4a18f6f8830d80dd53c44539cfd0d8b0b4dfa39eb06ffe3f0d1592b5ce2c3ca5039684a890a224077ab584f01ce1b70a4939adff9b0420d1adb17643c2488fd9e93022c12a9248a9e3b171ea2835a70eb28d046de133efd5881875799896111225cbb2c2c4118feb9bfd7551059d8cf7789a104e519fe259c7a1d422519516e1d0f2b87a64f1ecf523cb5dbb8c0f4eea1f85a1cc93a6db89663983df937852ff26746030f55a7f3d07d77c7fc6ddd8b1edb6d495f29b0351571fd71a580a1b95ede6d07aa4c109d2bf43669b6a7b8046158e840281cfd95ba959b77c06b1337421f975687d8ec30247d59d4e9e9024387d74d81f07ba8ec31542fc0c173fdefbe87d944ccc0de723e10d90febcb0e6b8100baad93d3cb8471d9d5c1f0274aa316b35a7ef12accd4b3027bdf38fce4ff5d7aa962e2032c5d0efcfd14f78b55e0b51cefba0071c35fcb0d41f04f2bfe801b8baa43d12da85af3cd08f4d117eeb5d03a77dd27a0cf742d95fcc6051db958b175a151bae3729421c0448a125a005e593a80c61f47c8eba74b46864ba06eedf384003cf188da071165a590c54499b1543ed72cd1d687c85086604b662bef71044307403e9f06db4499a063275d1928cc50b4c6f5b841c4b24a3a01285879a37bf4b367babd3bef680133778d5c77b8131bd020975ef8909e1d35381814db475a89703d725c936cc73523fbac45c62b1bc9508f8a005957021d0ba86861747f11e287b7d70b00c85e6442c2c717a46668f227ae6f95e643087e1f6e938d60ef1ef855ce4c09e01da5016f7819ecb57f347c8bc2cc1ce19a401c281d3b686d4d039905cace59d5fbe54284a6fd6a1de33bd455690e06056146cc2965a7d70b9ed25d43c191528a04032907d74313c031cc8834d2c9d27909ff1de55582395683cbfd315ddb02b0d9f1b716b2e62bf7ed479b30f5e8501f306c802108ddc042993d0d2e69913e2d35f636851c1dbabb2dcc466a0435df2855ab28674d1368661a728762c7ae79ffa2bf72f5784af456fff3d0310bfaf3ca19fc82645f8ffbe5201bf7b07cfad46b5143e0ea1b6e25a426d2800345a7ceb6fa60c77fcf46863665a120b8075c44eef53a2ccbf7b0040ae8390e365edc94d20cf2d7e5148ba834efc0685bca90f7262771bec30b22e63d066be2bf1b13b6d4e142896b453bc457140e70660366176dd0a0033ac160ad5018cb33483c79d39725c4758cce6f4b0a167bee77b97a7be7fb17d93c4344c0aafae3b01b54998b36b04e385e97a72054905a2112f0d880aeecc02a6e5b0ebf0b4ee2444c051d6174def3c9aca53b3e021e064535d363dc99b011d9086a19f36b2bba42ddaddd4215a1e0b0f3f3dfa6127719c3cac0e7d6f0b114b1f09682184ae9a9c9fe935fb984287fb6b00a59946ca56e0e0438ffa9ecc091601377113916b9c7e1926e011bbce5a9c23c0730a286b1001112f139dbe6f8f0c09624abd12202553b7e5fd79f4a126ca84b827e733d730a8adcc29e032b0ff00492f59764bcea85bc00b9006ecd9d6c8c47752b454f0b11b450fa47cff4d0084f3a87c8a94f55e8cb1d97e1713f56b879bd3aafe5d5e9cf02eed16e81aa439342a750c8dec957cca3798ed5dfc642fdf80289ecd41ab078b13dfd68132d0534e8b661ebfc7e8a9bd72b4899cdfeba45441e544b98cfff1884e04fef696b1eed24aa19118fd2ba2f715b1161f10a039dc8021c98327fe51501011fbb6e4de45d7a66fbad0127e8c6a5f412221628bc3da4947814b5946a427f17d00053bfb47f37d8efa1e8a8bfffc3bb0e6cb1e93fe02dd4f1b71869c9181efd3840f8469ec0e6ec367f19b487803beac13cd58ce9d9bcd7dec3e84bbd6a934771c632d740dbb80514e6a5e736c7f5cef532678c10a23548df4c6579780d548f049a11d6682b0cec442bf57ccc255f7502444e793135de809d4b8489e3a8b927fc0f52f6d18f05d9c7cab9102a8944c10ef44d32e21b8a9a1f90b93bb27ccb672e9ec46ef359750c0f43b72c9b630b902ac401357ae528b1830d91dc08a83c875e51c3352f274c8397718809022c8c862dba1978d0a84d3cffcf6740596a2bd63248fae800dcf316302b928dcd5eae7a25a7e945bbbc76cb90e0caee490219033be32c623f791b52dc8bf61f520100a3062bb9c49f8e31f1054ad8c5d11a7131a15973ca14a61de261e11cb21e59c23b236842741ab54eab5d283aa6a17da3e022710724ef5d8666ba2d38715ec135aa475299d70b3c5aa59fff4c2b4b9d6cbe7f5ffffe8f4320d83fbbdffaf47a18a452828b01f5e30551f08a72746c445a94e0a9dea689ec8bb7a1ff7be0829adf32784ae170b64e97951a58c00dd46227de0b52fc02128dd36978e959964c4072cc239c409d0b88a449c63d8eef0196a9440debf0a4b7278411783c0151941a1b6336eb055913b30ad11c615692cb65a00d504a93f0174d3a6081e1d1011d68f562c50f2ed784679ab27821b9f3e95bbee3bda84c2ff0cd9cf85e783b3964c688aa6f9c88731ff489489d078fd3051ab4d9f74bb005a3630929e69082892de9f280b15cd1352d9038e280372dc8bd0d4cf13588fc45c0f351cb4e1a0f7579b9e9beff91b507de83efebdc33238592cfe2996bc95134c20da309154e47d06eeee4706283835990e4dceaa434110a3995e213b22026092b240515fe1c1344b960600c0e92aa2deac1ea15172019a4e39c481bb6f98d2ba1d584bed44e914c7f2e8dc80bc50fe2dad03c33063ae6eee4545a90f1cc090cc49eeb129d7a15dd1703de8c704485abf139afc9cf7022974f476f1e80e22c519a182d343ec2f2becb97db6e8066c8a067a1d96e446931461c216717a81a17a256715375b2017ef998bba1b44e7ac7b7587b074e0751edc2b734d9b5f27a9744e0644f843cf34e20c2e92dccb637cd1d513dd43f8563814995e9efd824cda59bdf4b3a3860aa8d0dd212f9d3883b1c3291ac98fcfbb1ab4139cae062a0f5273c34d457e7f539f221226143c358c89984cd34443ba23aef2ea176db71174a0d7cf641e2692132d017415c310884dcd850cf248e042f6659cf964cac2cbcb3ddee85dbeeb6b4fd418a8feb6088a5382b90df466a181df2bf46a4040e685c793611da392e803f5fae7cf32e3b4b5a82d29ebe6c45ae57234d3f455bfc177e8e343421e68fdc06fcafb296683710936d932b47d2b77253890f06005504860874394104bd9448ec8e824adc51f92c20ee05721025ebc2e81e93bd7fb4702d94c5440a7fdb6830ca6d1634b6c2540a2a7c14ca548eb16910039508cab07d0752bb6edecd613565747b40bd5abdceae3e5d6d2377fbc016136109b39c6a5e78edd7c53dc2f242425d784c78206cf2ffedd0ed36f08f02381e15eef6fded6339e5fbd680d6d6a4864469edfcb16920e7ef81c3aa430d2aac8e35fd3d0fd6356cad56ba7e0032117ae536ac2b2eb010db3a100913615ff089cecb103a399116584396361e15360a5c836c177c837bdbe7dfa9e272fd627c0790c8708f6e90f82669a6c136d4186ac319aef60118a70522a45f4ed46d3c81208e521a5ad94ce5c451cf0a0b22a79edb63259470ec01a62ee41c9288953efa4438722cb39db32485e88f78b4f2467e5b843d2d0edf506d2c49209ce365f0ad27e62735e0fa09147f991f6570a8453d993f74b16285a17838a38baa58b26c076dfa121e2c0206dd99d755f80b63c775c0de2bfb0b873504fa9a6766ee773258d5ce5a0070b037511c4d713dda0696858eb6e6d0f2d88791040fa284f12bbfb0d213212ed1093bb96f294552e03de0bc73da9b5ebabca496db889f47c700891c18eba3468d9637782a9ac20944e4456055a745518bc1f71fa2283b12c950619c42d9bbe1c20304c08312c8f6269d3d9ece930d232e7c4a3811881a99ffc42bdcd6f1dd547c85bb7662cfc9c8e1ab907461a71c15e9ee5547c8bc52f917f9b60bd1d2976c71f1084c70fcef7509e2f8d35640380f08939e5d32c281f74ece1aa68ed3c293e4bf86bc0624195bb405ed14bed8cec3f9a1a04695f31ba86387343adc4494283868f54ae9aeffa9fd0225cc711cd19900e622cac7a80827ca8d60e7f45392eddd6f2040dcc39871830109f07c0c3a281f4840b8c4fc5e1a4f9392a208932f87f73b7dc2a863d96f32fe01450c0641ad6f4c991d0e4756e9da75c50cc1cbb784783766ca31d1fcc7463f407d9ccb4bdbfa4e5ebd8fa67fb45736bcfd799d33995ca9eac62a0701f2805edfa9e127da21ad30d59a530858f74e02661ecbb2cb4f1f41a50c4ba2dae80bb8990dff363938bead282acc07bcc5c5009908caa989fb32c8ecdc11d00dfbfb6e5fd49fcf5d251242af0ded9c69c99a6fabcfcee884f10b2dfcf9babbe82df42ec50256c6c7b44bbac19e909cea38c14cc7275f8d3270c3ad6f09b1721232e4a2a46664b0afb195f9cae4557914d842a620aeb282feda0cf2636ff60a89c9fada8b87f2aa7f2a1f840024ab4467977ec258f70416a4456a04a58d1e0b4492b8b36c6f9e4b9ba4942e0513e2b598cb41732bdb4224f88488987dd9dc1c8d8899319dd1ec358847631b3cf115d5604c80cb551393e19e4712b5d635774f946532f96625c114d504d55e42db373ed4a6f4a7a0fc220fd67888c91b064eef26b65b7cb8a4e0c0d0f8d642439f7f456171e568ee943765baf16b094bd8ae5142be3f1daa5c48ea75cad5daa585040160427151097baa84eec994859aa652c3d15a7e53fd74b4d243db6cdff35e58f53de8c1faa09ce4d750e78b46c6bb7b7076dc6b6819e17345bb5046ed052eacee65aef4b95b84110ab84d9bf92f4da05a4ba71bd7fa3c2424f3d486cf265ad8c4d124839abe5afe0a260ba6022f6653ff57489893dc8a64db22d6f190df8a910fd35087fb6ba6e36302e0ac1b616b3737f3e7b487fe16b8f8d2d97962dc741367f698530fdb7d427312b2052e80dfea9d69c00b8fbfb238b233e772019ed0790318ffe085956204c56153a341d80b61bdddbfb557ad9d0504927f90c591d9a5539041f3f65722d58b334c9c0925c848491d203b2b85136431733612d5260108319f0c2bdaa3af054398cf79c6b05bec515d5a67b145b865337cef85e7441bc59abe9bd000f4d8b3471eff0f44e9a5f9baaa9a3c8e3e233fef7c0ea621833f97100e9d33668df9754ec993d455f44e4f4aa2593db9dc27409ce7a606c082cba9904d5c2efe0471d436f2a9ca51b3c9cde55254bec0b9b02ddcf4431c12f7aa8280c0e0417fc6230d1d1c338efce28e11371c6c61f531bc8bd267b6addcbdab4126c2e6de06fddf2b43fb503676d0b55f9c2184b40a69b673f2b4cd3e974530c4f8d0078920a8c441cca7f474cd3dc6f81c29dffa776625331c30725861b64d7d21bfaf5fc480e54ca8da4eb12fdeefd0b2fd577d74c06876bc237a80e10359b264b5d11e420dee79871da6eba25d37d34154d8e1ba9d2536bfe5acd968a4a980e22cd6af1e545907c65c5b7ad5bcd2e7bd7cfb98b9d1cff5d6a8741f80f122ab0f960a959db23148b15906fc6e1a465c4fdc4bf3626e8e0448367721ab45e8fcbbebc0a8a248ce0906565688fd758a91b5fa984cc9f9f692a5b4a8afd5c4c25af7962583cfe795dea7e915de4d407505aae8a75280cc6ca64bfe7acc2d6eec3f55ed2e9314a06470d6716335332913d4af82772f2ebb5d1ca9c713573ae7bb9afc376ef7d638e5473843d34dbadbfb5be016cdbaa4eaaacb51ac0b04aa2cd16c0880710ec7a1922fde87dcffa284ec6c1c579f6ba629f8d36401eed63cda37c73809e9c8de47330a2f58bca93b91708c21090dbc3acbada6aed5d36a3d2190cda54ea93a2b91fe5d3c1c21187204f753e3c5604470362da365732fd86d8d99e629f72e081d27c7ba1c0d030dfb296b3cc69c3dbd3fcfdd3b5e69863eecfa878a5d01822b239c6920cfd512c278990ec20b944cfae0edb968b6608992ecfc062c5697b1aa97996b751c118fc9f2e9beda28f877d7e1030e3ff3f111c332328262abc4575e0063c6b18c6889ea456c916146eb5ed534b029b8234a5ddf4d2d60d8a9c7470bdc32d735182c250879d4389410be40fe2f5f06881d64c74dbcf57de9572f5765f4d3f0f02781588a6878f3f7efc110fee43cc88269afd8ccfb252d305dc80cdd45f08b435c77e4d5cb5ef07bf7f3a3a0a14d3d79801b16915e3da2d1f5da420c379accb963455744206e86a5aa3ea7899a10b76e4438b7d9da0f860237d57beeff03e48dfda004b86acbf75c6ce8899be6351dd2a2de9ba6b28160c7726c6c94a6e8920e63d22ae7c27c452d7e0f6c667dcc6681ebfbdbdfb8232cd17dd1b7c45a69027db0277184eaa1b979b9d86e755fc94edaa46d2106bb0cb0c918e83755fb7fd524e7813e56bebc2a890008519d0d2c08832a4c30d22e1a4f449dce30538102f991221aa0020d857ec1bea481f78db163c912697fe0948d2ea30bf413264269c1cadff14a06d5c42eb51d4e1c4c7f90c69a020ae9c26e7e0b5c4f172b206fa9af09164fa2d15746bcbdcceb358797a5893cda83ddfee2e05ff79732ae5308b92adedcc595f4704860e50a6cdeb51a23230499d633765f1cf8af87c5d941e7f2b4188f2c6203d15658e34fbd4d0f4d1809a76bbeb3647596246c27d43b47f36524ff079e51abc13e6eca46fd1a5bb7228e1812b97467ec1df479bfd9717707323b49ce7b30049f301e03a6a96a28cda54195fa369af3080553f3ad86020e642c8ca6ee360be0650da6c5d638e44979fc292b149b9319e9917b8ee0488998e4d29b5088832ae0e5111452d0b47174852ea76d50d9a7618ab411e8eb3b06f32f91bca72eaaf318335c5d4082bbbb3be3eb2d67c502d7e6693f6e53e6b58581f5145337080a529cb75c8a7d9cdbbb6813de505442c0d4c1ed6940c5cfbcabed7a172f825a6e9886cb6279709c60f4ee93ac49f8a65414afddc21498621ed0e0f904879528bad87cd0534aed430819ee74a4e42e4f22a889137b4d17546d14205881c6d4cf89e535f8e1969d5457a61ef49b1b1934e49e9a1405451207dec7169fcd672b183182027cd4e53d2022db34141bc8aac109ede0a3012f371984be9d6041f028a9712b834c8a6fab3628e8764b576774bd8cdbbe86c0d58f18f242c8afa2ae0c44076b4680202102d879006ffc88d7269271f28fc6d40cc4b1e6a55de0a5456d32b0a430d7451f8c4c2ab445c41330764b63675d4b1954ba7fee9347c2c8de8fad8fbdff87c9ca843615e1809a9a9067d0e51836b7b5e2935a3f68942aeb4f6ace2b9a63ce346424cca3a357dd7dc98b28a35453ee2abc0534cf878aae0daf491fe2a072fa2d5cb8ecc73c24e07bf48623e1523b292b8f8125a246bb42ed29d693802dd8268f63b9b7384735841521f00c81596a582aea9d2c3949710f948ac62b13323342e8b2049110e83c51a71d2adde8de951988b5e46da21a0051ce681da32a614344c6a8214585e872185dd9ce8aad29c156fdf18421c8295ecdc3143e426f8a9578f9da5804cfe38fa8c3fca86409dcefb45d0418bcd146582546aa91c30b0194902204eb87245a0028ec60da189c9ccbc2817b58d0267b60a523b66e6c8ac12ed445f3878537289903d355e49718bae48de76c3fe6d0797de22da34d0347f6a14f7b67a0deccb3bd225472292b8310990d09ceeb6aeb62640619a23a00146b8690df2bd1d081bb4eab32311bcec9d8811a668c32de122e9cdddd08e3c409ab0588d52bfc7f33190c15a70521b4e983c674a9f6d0dae66341022cde477f8712fc0a74ff0120384fa0ca5ae9cb0ef947f423e7e8cd0f0ac5590224d3794dc6705990e8e0c9336d7f8de1981b86c39918bd0da1222cbcc84f576b3511cf01c6a048f21349024c91966d94bc2cae99187a0f55611e7d0398ccffc6b23925a0b401860561fdbc3125fa854c0b6ba6bc830d86ae7dc593e5cabda3893e5d974efac2d3c73f44414a4a097f56d84ac1141eaa10825e97679242e90a8cdd347cb943efce8e57edfbb73c6903a11fab8843912e272707dfbd103628e15a0f8af8108202e430bcaab31618097ba80b11b5a7e33fd66c353f984af64d78d590d9dd3d57141ac5b499c2d68f6354aaad41b5f401218ab39a42911d035e388769c94d18b037945227c69845ed1b0e3650212d3718bbfc615625805afaf98694573b075521e70ac64bb77b8c45370c3d4d6e28b9f54f5d9ee94bc618ec795c0f54ca2af64e6a1acd745b8b0b3b426354fcbe72c755ec48d015f70e2c8ca1d4250369dab1845f8523f5cd1562569c7be8027d33ec87b83a88a586773079dc66a884dd210039cbe2c3adbbd814b20e175a5b40019c053f9bbf7d87ed084f4a962b630e2df43a64c3f2fc24234c22cf647b9381c3d5468c9b7da45601cd82da9b2308fbaf4817238691783286134c018652eebce203c501acb8c6d66b6e82a56a1ab163ff905f5e9c9e5baf3cea986064ee9eb94eba2e5922ac90f8f6f512cbd8577e3732dd2622da44767d31086150ed75ddbb5a9696f4b997c7ba2f22daf45d962032415918b9e353cf1b87f60ecd290c7a30c0416dd13931cccb485095059024b431778a4e4d1be0f6cc93e2d3b4c8350a57b85f799aeb70ef54acb30678655b1494a534b712064952a5eb10c738920f33f2c1b1b10218133c54167461dcb5d47d33e86352ee2c41c016c4e186a7855d9df102b52c3875fc0417c7865277b26474f48a9434494b84220e1b4c9d5de6989fd9c81f185e35517c451601aebc0380677fc1f0f2a600a9bffa39ef1e332136ae2f036ec45381cf3fcd4074f106b930be5e5e4ba8505494b30eddf8837b70fecdf8c9d9101862a8ce7c261054194c6dbe0d8a4430257fb1e94441f68dc03f1955b543b70eb8c106a78d60911488c662f2973c00512d271a0b4f7dfdc13f501efe586ae53da4ae841e9f25228dc46b5cca15a85157cc7a905b741ad11aa3153a06743006e42da52009fa707915b6834ca15522f289737ab984e0c20ecb6bf63688d4c9866f65f8fd75d53b7ed23e3be5b8a0bd66c2dec204f4735a25eee4a2a56245bd1d7659298e4d1611a9347824af786fc10fec74373a8278ba5d69c13ced920c82bd6fc8a61663261ef5725ab860d63638b6fd420fcf0a45632bf78b88c10d434eabbfc12d8b82eabda832ee8819165573e2c286652b4a1504cb1672de03b50ccfd09629ef88df08ce0d8fc83506dcb113be11c0f61d38a6955b55165f978e098ac145776332fb2f9fbe4155656c02465171a55bb09287226bce5beb2f731ffbd430c18dcf2327e30b4ccdba8730b012249aacbe01a10973febb2f68bc78845bc006e031cfffa0595f137fa5a515f3a1d57b5f71076ba0d3b02d6ba65fb0b3e9764a7a43984770245fcd74f086e70c0a0b97380f72b4cd05e4f7f6a43a080324b4f2720b61cd927b7c2bc98607f4a377453e5d13fdd9daa23bfd8cacd15ddbc0018abf5b539a5e1d325f3ee5f4e1adab3a684e88ca1a0f179be11d28ce2284b119425b12f93a84dc65e1b265ce3f96d644fd56ab466373724869813f6d4cf326242d5886842c07d1a4ba8291ea1bb5fd77cb72db7dc158dcec7e20c9724ac0118883eaaed96a42906ddebdf790a48a1ba0120c4199d6dd829c244cd57335cbbb3fc85c901a797c69bac7c220232d56fa77e02504686ebdae49a580340bfc27febbf125e19c4ea0a51a9fab1232ebd86c23e55d370de42244dc78c8af53624adc708724183a66041c1fd278b99726ba81e21ae403bb4957018246e5641b1435229e4fac5df9661f4c93aa435d28e9c7ab609de4ff856700e81b56adbd358a70214b2baa0d9b24f0a8588824afa992356fdaefe5c473dd347604d32cbb96a90acb717bc51059cb99b94578d89100826b4d130861eecf72f0beb5a6d4bb970dba4072d2240468854f266f11dce48c19485f8841f62a86575eed6cd92d4fad74d2fe024f501175ddc43a0071bd78dfe8d1b38734ae61b069d8da69335a7317037f7f403127054d7e9bd2758dc17fd5942149aa5c0692f01231dc1dc240aded34f1f8b39432fd3fa55effd1426eb9c4d8584973ccedf648fe4645a64890b9a2de285ce11e428d00321c9774765c059bc41e7ecad8522f4e34e8d91e9e06a857f7e10265ddb2003ced4f98b430e0565784623bcc719331d4a245b62260ca8045b0ec6601791ce9efa4f46a0fdf74024264ba809c8db343b8b5909911c2b68480102bbd88aadf80772b434bf5f81e963b7f7b555e8da08efaccd0f64861d544722edfc7e9172731372840c5915fc28bc9bcbe59a521d909a77549114aecce3f6bb7d57d27b1d508689929dd343bb1fb359f1aadf193d4cc11c376b650c61ebcd7833ed5865238381224d18068c655d0ea6a27077a3acb4ae030e5cbe3724131a9308bdc036c99dd4183375cafd29548db74f9f5075096255d381ad88879950f8c79a616612d216660d0b4f0d623081f6fa742bd543873d2282c80c6cc38875ac4989cad2b07ae09f7a9f139b67a565f69ba218ecc44a03c5fa2915e38a203a1152283fb2a6f19b31b3227922a363e2c2643434a6db43a9b86ef3aed04cdfa543ffd63862d9725cde6e80eab5a901fed002ce104bcb9ca6b3ec9394eec45f34790821c213e58d97017ea840b2a688046a3e35971f05d3171892990266189141eb0338ee5ac658815f8640382f51875c51b540a5299a181d0df71b7a524496f87ea0e2b9c08e5a0cba89894d7783c49f3dc2aee478f0f3d40944b8e670ce4e5d98579ebece0de47020de3d11fb7bf13ce8089cf378355378b9091d24a19547edc83c87b421fc00b6828a40f7ba22914e2d10fc12840bfa340a504a258aa57a48ee62d38f35b367234e0e60a6e83f76ecfd1a128cceeabccfc0bc6773c0a1e80b306142669eb1704d0bca753da2755ae737a1c7a41475396df63dc15848d965e74a48e362490b1e283f20560c949a4f0133cc9f48ba9ef6f1c827863911d616a3cb22a8dc11c5ab64ee6ac818cae732e43b12a75e8d206c23c73cdf0317b1d6ceede8c208af574fed8150b7ff0ecc1da48d88475aaaa3b188915f4e0ea5f3e6f1cabf8e11b175ab1560106649e5658472f90ef756acff5eb0bbf6c08994db3b15db6db28f460254c4d8801e01329a8641328cdbb8871e81319d4a3c106f9b5903bb3ab3bfaa2bbb980d06b78c25b21e4374d38cee6f49c3dc2999770d8b9c4499e9ba77010bbf2ec204c9622c9c7386299b3a73dca6056ed636b20aec4a5a9db13ce464c90e7099e3d11136710d78e6176153d416e465376fc4d7ab715a81bd43d910d9941c1fd6d4bba63023f699fe0ab5c42b6d0d61bbab61e9651d7dc3f7d17395891a7f25207ac4b7fda0684490195f110d2a82004166fb5106ad7e87b0835cdaf4a42d80ee68fcae3dc25a72e0d71e0a277a7937528edd29a2c1a4a6d7a3d088d36d1aea99e98f5f0e20e864d7b49b1681ad22d3ebb114e0808b00c2fdf8447c0980c1983023bcf18415d79c6ddf5e2cfa9238e5ecdbd92e976008be7d399e9aa65e0136a80052e0b3a77c9cba16b810c7a8fc0762d18a1c21382c18cfd83a1510aa04ee21a3887288425702d210a664b090a25d5fa3b4019e1d18c18ee6a62ab752a3c0c3b2ff9f6c0e096b44c7254e6525661d30192087973299a4936413a71e07c157082f154977bfab9dad1994121d6a01c769ef2f2dc94619619cded3746815222e168e8cd48ed8bb024a8d66e036a38399b03140536b4fffab11a1135ffc6fb9437b185b6472cdde623317638de28abcd96365301d3e35a27b54704f32b26d42e1e417ffe9207e7d6cab12f1d7de26d2d662fd51b2badcb10c32619381b18131fa3a4b6befa3615086b7a799d94a6c8b19c330cd073b1adcb7f3181cfca98adf5d6f47b83fd3815f9b0acd08772c97e169299c879c9b1ebfc4547c5c49e217716073af074cac9c9ac7ad8ee5dd9d8dcd99c5b4930b6d97b086095e89d4240bcc2dea70eb064fd83da0a65c1bcbe8debedea59ac3f23800d3c26323c1118d403e4d182da2e5760d9942bd881315383d8b3ccc8214be757882886149f0c3760f330c4868106fc796e55f6d21583bec1008792b3027648e9fd1e6c5d9450f2dce39adbea572c0a4af86bf2575432b808d629101a32e48829dce32550778295449c4823e4a6c522473320534c044aa00d455651187e068ab17eb088ac14288c6060827aa382ff1c98e1d1011914734bbc2b6feb62b0224d1717f8340973cbe1f50ebca49815ee2804ef71552a9e48c940f65519f8f79214c3877e96d5639f7619deb6d3c53c15db6dfe1fe5f8c282f3fe79ae93b16d9ce9f190e7c1ec36608d99a5457f0e8b4844ba96d9ac8152fa484c11e70107a9598ec1daf0c7e88bdc6d83f721a34aabdb92a9565089a03332ba19c46ee48a9ab7849685f3a086d6aa70ed01930f7ab759773884c20bac95ab2d6127807325a513d26d282b9d5530629f1e2d105a06f085df0d17a84521bd6c02280d9b68002e9e6a8f79bd72cd08f1cd1d441203461cbfd7a358e4defe8482da8d9b124a6ecb1b34947437286459a9252d881b3c6a3f36cbb997555407e505991e249cf1d77d1d88ef4a6dfa2baec72b3ce5bc28a0b9e0f8b6bc2da725f685545159404b7d73ca89ada28deb495b36de3ff13c34bd3747b99c24f9e39661c1d6514969df1220beec5bc5b74a1a1a010a8d685b6d30d0822721cb0adfe802adc15b96557360a57a5e4b58e60e6fb96e3a3c7624c89d569ca3c19ae808cce68753e7ad6e46a8f0b2bfb7a87368c7f86b6aba62d170c213af80a2792290942681431e8de6118b2b560a6ba5b667b46293dfcf6b28bc1ad5d09053a32944cde41918ffcc1653c3074cd33143d62def39ed03bf5917c7474d146e332d1e4f2a2b6b088cd0913251d26449e86785795bab348d469164f92768da6f4a7633c19eac2409e63f8e7f18131f9954bf6ce0cd87ba89f8abf8a675adc292e18287fa86000d5af219e4c2eb7909ce762d786e02f3ef2ee46937ac9ffc839f9c686c448cc310d438a4381d8442f2985df348e00cff531b9510a5d3d1856456c3a54d4e2124bd95b4dd89c554d377e61bd4eb688c5596d19983df035f03af821cda534fbe9eb8f950598e2b7df20192c358459ac4cc9e4d043e87b7806c8034712604b2261fbdc8b6ab8a028ab07f5d2e07fda59589e13cf6c00eeceb66c5858de1eed0a54605cada8cf745b814a30985c5d1fee7878c4fd9a03548f1bbef52a136a84ff5753693af590f5771dc582d1635ca5b44d4990ee2556e20ebd9e40b56301c62bb4c93f13fc41746897e3cd8c7df039742256274ba3bfd3a3b4c5510c906840a0596643b31bf9eea2fddeb9a3dc3d497638cb51855217fc430e20866d08036ca321ba044891aa4899031d327d063218bef21862e63135bc5c4ed421a060ad221db0905ba2f473cbd200fac2a40bd57e4950113275d48f734f923b10d1b281f2293802a5cb6100b9830b01b6a8bcda46910d4b7d7d068f29a9311e628e8a264161312e8d01cd2017646e4a4c77747baaf13f5209acba0d01f10526bb5f5b179f1713353082a5accf8c3cea7b1e2a4fc95b4990cea6fb20b1b58648dfad71bab72e8c8f1046110abfac7e520137dcc881f6d4d117862eca8c88eb43388266620a0732ac3f0e71c47f444f4d1edf579c9b1146807aee577362dcb36e00b6bf5d4de9c600efd13646e5bce707a63a2aaac507acc5baf6373ca2a0bc2a7d14cab67a3ac4df004d3ff565c7f81b69a60e9d132469a6440b4e2195ebc46d18a0d36f5f3e87204a349c44b0fa099c9045e9ba940b2adb09b8b3e4f9e2ac96e1b8b94f0e46739aad49f144af71ef365ad426db0e98aeeb507720ea2a06c9ea43e50a2a7d33ade6da15a04235a8452c2ce8f869beb9cf541e4958a9df854462aaf3049633025c69bd1c9e751faaa590008ba476dfb516f24602780130c11240e701377dfc0fcdb41a15aea75cd7e88720c844640365bab4b585f08a45bb2f9e99c862918a90cb0a2f2c9bcb16469f55c89f6c6b13410708f0ed175e2b0d22527bba5983e2b63f03c923a581ba75d08ba1a45a08d9242e1b39a6aefee23987edfa435b6bb915ad7906ff2fbbb1447c4b55031950442cc26b3842030687f1691a0fcf441b3a4e844c28d1d181907dd4f20de091ac5e45e2b2acdaaf07ce36ef71b1b0d01fdbc8b50df75a606a4724be20d669a696909a83da617f09e2140e605a4a8f750698043fad26eac354acb0e89afd6baf1ab5938fa3ab1b291c062d30691b183eb7633a1a1862e904668cf97b291672516cb6a2ff5a9d4f9e7d7547971573ae70b4676dc83eaa16f0eeb9368a2661461087fa9de8c35daae769bc0c9932c4e430f843d18a9e861b044c3e6eb6224be78de803da9558d6d86a45320a65fd64e9a96cfb0e44ca50b8ad729e35561bec4adc44054f605fed468587f97d403cf312de9095219797e2ef86b5e9de74bbf3d926ee6624b19fda24423a0b5182182b9bcb0a85b8ef7ae9d040b8518dceaf63ab02dc796bb0fa74093fdb61aa97282a9f55cd354cd18f51d17077585987c9148d3a7deb79342f3de5ea13aa55ecfb62052711ee0cd2fd0c5212ed52a5feec62c0c634805616dc0bfea15d91e87696ceb526e7fc89949f17fed3dcda95a1c22caa92a2d9156d6228abbade1b84bb063ae0269b8b9f6ec7b9c6d040074ed812a6b5c021f16d7564e8ad218431cf7f3e02e4aad384bed13bbb83d65eed12cd84139dfe9e19c487407460508d62fb343f596681818f227b3492620ba90adebbfb7e055a340e80e5b0ceee717db03c00a4bdb1551d0b53392f06e119d34771f867bd475c9e26d782ce3eac17a8fb999051e4591b956294f0fc5c02e9ec0830cce0736afa2e3806274ff0080f1a594e21dc03769c5df4ba51773232f76eb4fbdcb1d62cb23f4bae1c9ae56fc3b85e0cd416d07607daeb7f72301a4431c8bbe607d2eedbe0233052953b6d57f4c29b535b92f52ab8ff4e8fafd29349793f756780121e6f8f8f0ab9f4c969e23e0c5b37448abfc3a512345a5256ff041f2214f398b25ae08a8a5b4058b2803470054b204525e61b12b7f8586bffdb4326c75198784daa293db213527578086ad82c590be697305fca1fa44dfb65ce0af219714dd332517fe4f17ba759b5f0d770893151da5fe63fd7262d1d2bc15f2da873699ddf8262a06b580a214709adc9725f142a7ac358ec1deb590808495d3a22697e95eadd4a9b9b1a64ce269783af9c0139eaf6209c9c321c175d0de312a2c6ee9457c649890b5b5da8db8b2292ee90263f667058bb5544b5747d080605a973a3a095b5899c6ab96c503af4ffae60eba8dc37afa4d1b48f4e2ab7760b5facfc71dbfdf1fa83142de407f6dbf17ed02f761768fcbc9c5d643a1f1c47a0776698de76be5a59512ea32b631291dc4dd9e8047d1fd1701ef33010f9b79ebe05f5dbf750f0fbd89471c3a1c9de9a18238ae33aab2b1da7a01a1c7e41c798fc673a7f612a5a79add5da8d24d625f573cb08958c946cf38743b69a63281573fca6e119844c54ba3263e2ee6bdb6eedbe7f2897dcbda3e8af72d4d7750decd20bf2a6cc470e50e5bf1e6d14d6a7f0268313458dee8d9e2ad7d1c64bdf2cb0e2f816454877139c68d2bfce08a8e38de782aa432deae6f0166914edd1db2cf4bfa150cf2eec02cf4c3af254c1c28cdd7464b98aa7498f7cbed71682b7b8029b1755c6eb035495577d3d65184cb503592ecf9f91c053f909c49145b1aef95b1ca01f322562df8d68e793c0e7c19adc7a8c76398d953e9960bc2efcad21760619ba44315b3c3a61e16d01bd64919ed0ee110a66d2753b7b29b2177b9db16515592b444cd70205f6f858fe4db5ca238b4ab3776759a74638e60875288fbda843ed5d52ed43a7c8de94b4f20e0ad9dcd047ac9ac379c911df8a67d4566dc23e90182890dbbab6dc27d0e4eb0214c6e920165d2425d13ca86b901461d2b725add3e67f0e880ea55b231c808d9795d06c3a9052da02e2c82bc21ef2a4117b04c6ca98ffe07f53308a9b325f14fc821d0ec09da4d561a382711bb8c7dfa026e15e47693f930cd71b37fb1d6f3f838febdfa6dbc1e0657b38238806fd04b70dcc590c20ddf6ae76b0441d0c46c362db9cbca227c0067215605e6cc64a56bf11d3a9de9a2b801cb79b72aed9638405a1404b48ed0c4c682af7841c9ea3a421baf8fb0f3d955ef5e704d1a20429a4ffe019d345d932e7b04ea287cb38ba80100004a04338f395acc8b5d4c5e6f6e8f9ad8cac6c47b25568f6305ebfa3d36bfc75a28edb5cb4ebc577094fc96c9b46358061acd4323776dd0f40133e3ca1ad1d34fda716a4dcc0b518c8a9951ed1c9804766749d5c90ab818a507436468c3a6fd13e967811baa13758923944436ade3c27681483c6c1c8ddc9cd9c4da1a36599d43e60ac63a5ad5b3a9e57579951aad6f28ebf559f8770f8758ef5d1a7cb17e52e77b570287fe6580467ab19fb70e8d75a81621b876800b8e031fe81c84892f45c268c7e2ebb3155778dfceb2450ef7c41a4c302060bb38edb8c6472388fd2187493244c2536945bcd1d84b351f4afd8ea08a5def1d049b0b603a098b266bb9cc8f8d5b51c92d7cb98ab51c0f593d62d13a54d031eb2067886f8a0468bcc7a094236ccd917de6cee1508512d85045c0d03861ffe46aae66ea7c240b1ff2166d9f57b406b18026cc4916362cc5d3ccee61f6e8929e0d3810d6e23bb992051d3516197d13ab529aa2017e9a2eb80c7a30a0f0ee8db65608cf94a07df01755c0736a81866c8d40ee88dcbb63d30c72c26a498ae3e7628c118b3294b63b44ea6222d725639f76037fbd9ebf9481c9d26287c618249a1d0a8fda8e68b258d256250a9d15029d17ec2ea346043c49642b68d8732156c5f4124fe97599cb9482e043ac172d568d1a0009a675a32795a0ac171c2374ae066405d24a1cc27b5d431350ca8a3625c416c5972525f46112e4ba08da698640768ef4da4fb6bb3e13169a22162642f3040d8dc027a0f0f019e8a3696ef3a885ac20243be1d4ec12285dd746d677d712264d338c690226fec0dd57bf8e86af1546e7837a90dcffe7ea81c24e1b9810e1d9f880f4c48e3a9eb3b760833dc08be0d8f20074feeebab2af678ccdab4fa0c82b57dce32f9d5a40bce5e968600a57e7c2378a8f0b4d40974fd4124d27b24f449102a0d2728347d33ad3ba41db7d9b2a066fa00e99aa0b84cd42e73ffaa32a12029e05da7fe04cf286a2129fcd7c461a6bbd28f9a28c7a11ba1316a3d8a0246ccb1333426d0c6c71318fdd3d12f1c5fda933cf50dc81c0ca7df0c239a2e0b8664969e615a3c7b60b7ef45dfa0d9f1570f3b9f01be833552e770dce8261c357d63d49eebc1e6a9b6ada83761688c04b83e2e6d791dc4cd6341d3a4c4d24399783b1271570932097a4b891204319af8a383eb4ac4a435b7da783f87e83ebd838802806129587aa7e0caa00872c9752739f79ce74e0224c0d3353ac38d0101c852871a588bd7893a570ae137447658fc59ad059ed3a18e71bd364a2222ac9e90bb2285fa5a2463e66cc39f651519c47a2253c6ea772afcad7266dfa88a9c9eb33cf4e9597f0a9ee72ab9a8eef1644cf818023add36e95004026a9d74fffba9a0c23929ee531c7779da58a3fe0954828d55672504cf56551ac42b7e1cd4759aa29ca1bd9d9113b162000bd09949b3a1920f148d40d553a4483239b14d13f97f973cb195752a8fe556d4f55f22d864e8d163ad1688ab932b8890d27ea23489c69c32972f83077653c1b768963636c04dfc6691904894fa8c2993e665ac2e664faf9e9cdc930c3ae4c06f7391eb42d09dbbd9f7cea6fadc4e354d6197be8b92f6e5c96ebc5506ac223965e9ac2d3b11e7b63dcf762f6e952206eaab2bc51f856e28f6bc36e109c18b4fc531ba0e2b4a999c2d61ccfdf4df42f7abc8be4df96e27cd86ad8013cc596e3d23bfcb9458c942a3b399507f3cf8cbf75f2d532b52bc3328d07fcd405b2ce2f67205be3871da2c6cc38d009ea0ee626a4d0f65b8be84df5079a451c05925a9b8b23230a195ea0031bf776cd075642469d3ea7985826f4a8edb68979927b9388081cb9d8106b0d003998638d4099704d46543239130dd818a407e771dc0171158140882d7741681f063d3d1910e5803d6716e41a3465ccd317915fd8046babe80afd2b5bf041cd2602cdeaae17636472d855e103e0b0b4da807bcdc7dcf1f8f0a4acae13410a41c02a1cf81365c9bf19ed20e2cec7fc88f965cb4a137309c716a55266e835c534e2416d05e3d15f3a74874b5758a3f78fd351e8c0286b23e6859821c3ce52dc393cf26bdf2797176857866de93dfa565b693a8b9b22df70da3938afa05ca07a977769609b8e96ba8228dca8a85881593506edb1d23e5b2f8ee06686a9b3041367b26b76868c76e748a9049fd6ffe7fee8a17293d83a94cb903c1b08990d2f59c01a7834203d5c230ec8830aaada4cc0628735cfd649af645f3d7858d046b91b54685b23a7361e78e575709f0094f9e78f9073de018b04c35fb1bd7c4288e2135746ff1b1d10ee74ba531bb14989880883020cfd9c45634c1648992f412ba8fcd2486382c7fb4f1b3f4aafb1e83b00462bdad391b8fa112884ff4063593d486141d3d89e894ed2706afaa2dce31093e0e8a3011cfce346cc54a10963d9dc8924bcdd19cf16205c48b28f13ec394e2dd7db534af3c2cf03d18e1a753be5a3b581c4f1ed364f82752d3d6909297313add7542793a8ac743480c85b4e889d723912fa3b722919685d929e1c2a6f897c9db6391d4534c30b346e64f4c6cbc6590405eed8055fb7fffd5edd418c459cb52e01e7d4f2a14d9a2a6bc008421cdceea9c0836b4e0918aefd2a1542135c847d060fd5acdca3460aaadfb890288b079cab49c5194e4f95843a07337733536e60507858c8b33167884f0e2aa3ccc5b6433a9f61a1633f24f6b0b96a4899710a20992d6d1482e3a742523d650e5b7c5e355321890cf71e0338524695dfdc172cf30240cd2b452353325500d076c164319196f86e763eba9c57c4087b473a811f3b5e099856f24344e5547cc126a66d3a45a555b385a8be9de87afaa42ed76ff97653c1d45272ca9cfc44b28952648801a06e853c3f849347182084614e213a0100d18d24fe8c6805e877c23c1b7208b2f840260da6b52472ed2c0d46e8d42b3168437049dd95040beb401ed2c9858353a980523fea835418c9129ef87b40381ddc7c7a2ce4ac4068077b9121aa7967cc39d0e93b1dea247e166c20de15a2dc5fc0703c28c3a89d90e0364de47f4b5ddcfa3713d54555c79fc87f27da0ec3f4d55834aef2e0abc892334f61b4995df1cc5308b9321546af77f2a3d431a4359912a1e58bdf1b22e0974f57ef5d207ce10ddb57faaa16f46f5d09aca0609b41dd548123fa430a1f64bb8d683c8a9f5704d81f7542f40fa79f1531f5ffcfc9bd1c781f5ba1500b61b286baf56b8c16084e4d13e5324ac127379321cb987836416ce2a56405b4c9be98739c2aa4aec5af69c91be498c89e0584aad4f6f9b9570b8aa8df1710035648c6c7b4ae86ab6c9fb60bb2e7a3854a6547f636ba1c6d8f4fdd03f26833b9dfdcc9f6460198ee335d9b88c49958f1614db6befd361878f80224733e396634fb1f2d18e02905bbb37c8758a4ad6d8bade58c6e818130a2a73d96a696ee4859a9f1d3962bfa29f06774338e12d3df7d338e73c1994ce1a6e237e725e8989a09e46d0220161496d2b55f4048b44165786ef0bf101dd9c9ce43df4aa34644be25faba32c5dd8f710aefb000f811a03f910a82110e14b17ab35a2ed781d5d00569180b4222ef7c918e80918f610316a0d521252aeb6449bb134ceb2658ca28f6255de98e6b55f83e2f9e7490a17308c6608cbc70ffd9ca08cbf346f277a143e430412248cdbeb83f4a08ea020402a0f59e6229d08490be73127232b636a30f54d89254d8336b2191b0a49d177062be61f605d764f2e0ba2ecb5af2b57d48d7d06fadc838004e17b71532cbb382675b75095a2bf5a2ea8443678da86a279af6c32365613a8fbb54e17b6b2d9567a763fbab2d03cc240ec04292d7524edb4078e41e2aed91caaca614eb7758578a7e46f11fed13642160f2d682e081c59ada62b679666e14941c745c5d4fd0facea75bb724efc3914219b23a3ccc06a9a2712759ef9263c5d8b7868a0d8562e7cf04068ff579840964c12036b6594396d293c0cc01655f59585355c6010e023d5eb76c31f72d86cb6c140ad401689a86fc225ebfa18898025f29502a9bd41be3d0abd37a757075c090fe5908ca36b1839d8918fbb3d1e1af3eeba9522d84f0f02b31e07719f62514af6584272b63664cda6a4dd6e97ce7012ebb191a8a9bba122ab495f5992ef2175de6c5ebcd5ba10311d2890a96f40208ae0abbdbcdc6307c426d675bf830aea8454cf531f38be148032d897ab1621bf12f174d96d9748615eb805d43abfb1aa459f0f3a0a0e191fab80d76a2802f56eb4ba02b717330bbf9a03b4376cca4b6acf213619542199c692e427ac4b202171217aa15c777c27735210c68963ca9e8f625187c9d60b773df5b0c5cd8dc73dae3f660701c6ac3fc9a38c4aac8ea37f10ed7a6b73219e3da427a5ea2ad762bf51d202595f08ed00523789fb14305ee7fe1ec7904cb89c1ed07fa230988072d5ffb91e80982c0fe82da9dbf635212a3527a437b7fb70cc9b6dfaff624e61eca726506c5a8b7e0f73f9326eb56055ff71253c1fc6922ebb93986c0a6280f2e38929cb6d871d5d9428f187e32d3652ed807adb266baa2cff5d03d03e84715c790361cf50c8e14da2f33716efb9d9036b0b5883d981a2f313989620a9f915a5143a162b2d02009432c9b92392747b96c3d5dd9f01cb9c0d35f7ece90fe5fa975b6ba8abef42258da2782fb19cbd6c65d593c862b5acf5ca934f012192bd978f3e396ed7114792105fe5e9d07c10a1b66c8a2e2a6a14f31528067c5268f86b5a0aead28e4b8fe7b758a18b668c00fd078705e897900f0971ea1d7c96f9cc720feb7f1d23b71c2ecf3d6161ec91377697aeaf061951e80f51789883599d4e314781c0e94a73b39855f419984eacc569204742557bb4c7c9f82c6a59afca9903a76ddb6950c2cc96971011b73ba67c239bcbdc5eeaa37be0a418816415547486e4882e2440e50fa1844fec8de6cdb04a1e086363a4450e22d4735523a2681c4b2458c164918d6fe0cf3a3394e5f92f609a07423855f005cf401dcb393b71c8ee8561af9398b32066fcc852f075879488b008dd1ccae3b07a92a7c871776caf4f8601c2ba5f2505002ba9e5274c2701509c3b5ce5ab777cd389d735c2a1d977eaf4d3cdfaed65b8e0595bb040e4bc3af48e138ae2dac26ee5c3afee165b705c5e2ae3f82ea5c24dd9bb712c57bc0a9cbec7a905cc2be949f76bfb9d24ac80c7d23381f46f6c9b17419ce15231ced5341ba041d31405c46811c4e92edfa285a342e28b9f21c3d8e9ce11c90b5ecaf7e9b9066e0c89b0d451bb724f4690fe67322d19f1b0e9cda82b19267b719ab5d3def7e9419d16026bba65f7115da32057e6ea1023995169974ea617f15ea4261c6ed105e01caedf60d05c408d5bb7284b987f7331bfb43bdd12f84bc2561c778d5b3f0a3fa0611bf004eae58c349f39a19c007d8084a140cfdd395a24293b7512fb12122ae8fa2196345532aefc8f6e3161efd6d4b7b8034e2ffcda3a37ffda6c09c7a15dc5f4aeb9497918e5347ca29a3ecd04faf2056b6d6f737540f690bd051bb0e3aa29c192578aefeceaa58284d3a1904a300552c9702d8338d2640f15ed6900a3aa2e356bf14cd752044b5be41c06f88801fad3aa603e509e2a0dd35fcc4f45e782f09dd67c73bf904f2c20800e9433ec04607ef83318da50ec85be74a561d535bb0a6f08c319d798313e0087ae1e660bcf7b8fd74146c16e200022216350692e7725e7cd0783e5e8db4ac8ace04da5180f335d74d398aa7c21ab33be38e012113e71ab0bcc61c959cf1c7a9e6bce82606e2e33a816173e1badaef571148c2d1a30e4d9c36d1acfd5150996464eadd450e593d9bccccf31ddc2bf524bb596323620dfe7f4fbe903ce8fe5d9d1decb4dc1bf59e728add4ce2f85823ee1969de4c83abce0affbac4a4823e1a8e1933753a98a2d9c30fa8d95979148110ca4616dd8ef68fd56c6a0ea3a478e2f74a3df592cee7087d14a00c9e071d4710677cae044f5ec92b13046a4b0a2249fba300428e87ec1ae6abe8f0ab7aa459343cc8404031478859788db5377bdf652c7bff0d554606a09bd5fdf6fb60ba52f49c1de0eab3311c031f03f547cd5dafd6bf8815ed648ee68cf77aefa4ac9ddfd13dc8c569f8f3bbaaa656e4420c5a702df66da1cfe3f4bcfeab9c9a9a418fa636ce7058d5b70fa745d0b5f4c503960ca10431a75339d01ca23c0452e9fb2f37df32d5d16c56da9dcca1ea5af775099c42dd970ce4cbe3cc9bc4e818be2cee97ca3e2dcd0901dfc84091023f3b02d3ada64e7e099a6a3bef77d4656ab59974be56086c210147c65fc3d9a44200b69bb0da90c53499f5aaa1641e6a4ec38e78424b988ef134c57a627b8f537298a4068f9458c83657ef6952760790f6bdf7386387026b6fcd25474131baa84147a17fc2cb73394e6a462d9021a88b83685c91f9191b4bee341692acc5e827ee1130f31fdf90dd4ec293f953bcbe936415330b7c32ddf358bfbce14484870797bde1c99f24029240c18f18f929dc3b9160f984da0c713e1bad7e5cac5edc4a5d3735358729b85278b785befdbdfc5a208fc838c5519c01f6870594bea17dd59cf6947f1e026df9b2d4537efecc3edc1824a980133f30136ad3b1b6fef61f7050f395a13e1d112cadfeebfcedf2917f4f78747772a51333a305d1b78e0461c752b3ef1b2034f554840a67c39a34a7810bcfed22cb14520301e6d3364bb14e202710adec22826a61d2cc58785d70dfe80e2c87ed0f26b22dc74c57828358c2aa61f86ea85a0d11cbeb38fc8557ebe584089d3b5a9e68692f7c7ab9c89958dbf6a92adb18e9a5b443157ab3ee577ae5e9660490d1ae56af341146510e54f5a9efc646610ceef22bf161759bf11805906b460351b15e720dd9247b167ccea9ff44c52745867f2d1dec9e0cac36706d19d5ba07140aafc9fc99760cb5714e57eda3e71f910f0c5a814fdeb5d4e833abfac7e82dad32713f7941820bbd86e6b5b9de115dd44e78fbe126225ff0ee6fc37d6185ed304dd3f0e2e482db513de060f264f6ec9a23a6df63a0f51c8bda8cbf9ef4ed3c64d5afe1f6d40d6ee06753cbe09d892cdf66e29f76a47188d652ef6fd481c14540ee7c3f8137c2054d23e3994006127efc7f3a95614df42608322b0163cbb5a06e28bfd32aa302a2f451f84347960b96ce828d20671e759439a4e8b9dddc826672e8e86c96a3cfb97449b63396839606e61f116226f02fe4debd51a14891115dc8e9ae15dff5d13e54d7ed97aa46f911e5b58c3b358877aec5f2e7fa64ef05efc10e3568a9932d9f8b24783f55ee6fff46658c549efd4dfceb0cd0961e530a81f52c7d2a4cced13145a0956a4702245a05b1f9b7348c7cff48ff5e2699f3bfcf9878808cdeab75e2c95cd5ae793c690398a2d1b78245f6b4f14b62ab43637aa26fbf5deffb1779d8755f524c912607df6179ef9314f8f5cccc1623917213e6e2c4780c8f8882605802932716006c1e159d25bfbbc872c23918e08f2c8d98e8840431964e77d2b0ce23b465b4f939ab5a24807958ad2c8459fe04784577de5dabdf85ee05ae869f5f52ff67822a3a29dda914a4547b9e3bfdef366218628298c49d41cece9fefdfd87299b89510e4af2d6db94d610570bf7ac9d67dbf6f8cae4f3ab1468aa74448b7ab71be08c53b0607914d3c3e1faa6608e1a61d435700805d3f4b91a837e4a8f14b95fcfee6b0c1ceda9c256eb0d9ecb17ee83e4279c080f9b5b241c652fae0d98d8fab03e4816d7b8da1feb0f27b8702c595319b448b4dcb17e2814d84fe739efc6131d7764e01239dd9ca4bc4d1d787af64eef8ef9e86b486e1f8f1cdcfc28083143c6d74773edc8153760d18376873a3d897fee276261fdfe4d703673d93123d3f2dc6b3717bdcf9edd67e24b5234731b1850b4e980c7d4b13142669efecd3da8112e0cb9ae951f4026e0c21bbc76a6cec2383ca236614596db1d1f42f3f9109f631afe001575e6e1ed99054c8a145a3fe2c186469d9aaa9ed53cba04ac9f15920db2cc381f4fcdc6754ee5022c4149f9499a7828a0872823987d4979ae5fb09f8a1565f9ce6965c791fc926285b995638414e532df416918b73207312427f11c90f6b0c4ab689aff7887d7045579bf4b64add0d1d44f7b836276005dafd6d97bdc5b23a54e89ad3f9fd9f3a3d1645d317a546f595ceff6bd2a6933b9555ce08d8627cfa904d10f4c7ac00e6a329a66cd10aab4a0e3b7999811773da3b38f4f84a82c77af8856d5dac77d7dec0abf161f21d3258ee4fa2d05d16141274664794babedcb391752531bcad77f88274e34cc07d8bd0a8e01ff196bc8cb361f0088150d51e2e336491e9b3529bdd9176f013fdf104c1ce801d6fbd19031c9f94a28b81bce041d99eac1fed4481ccd6e900ab7d157e9e921c4f4f99c0b9cc9e547276f4879f91f073599cdbc2b431d8f1b7124f949ca888cd0c54c5fb7b42af5af74df5e2de0917d7d18e26d0f5af51dea6eb2e2464f295be92ae820c9a0e53586109be5337d0f3767c86f6cbc220c256b802aaaacc757ff4aa3314ed57ce73425707c1c0e3723d27884c0fb4a023478d9fff0bf0840c838aadd09d115a35da765575402b9d039a8abe0ba75931c775f659291bb893b562f670dc48bc8921727dbd4767ca55a262046765503694cac94c8c62bd432ad1d7d6aaea0d9e8b32d3931250968e782e7728bca045b6a81dbbd18a39ac4f2a747384f817c9a9ba975ae5817a76a028b79d9fe7453d74b883233906dd01fb67dfcea4c0215fa28fb14cfbfd49f365b144779c74aed037146f45289eff7211a6af05d0feb024c4ab31bc84bd347f9b05a65b5cc37c7a31421077e9997c97c7f7f3e5c4575d6b4a05c6a5cf54bd1f55012e91d1e7242e2d430a54670473547dad97ac5a1aba67806b1c42f869d605220730ad8518e2ac3809bee5bb80c104b3e6dc2ab8c780ea59f9cec4bd3169944e9ddbed0994de700c24198793ea21287faa5b097d28c563853f53163307119453b99e5ea8460679942f7b0ceafa6f2986ba1abc87ac1c94ef961ee8d883e47fce6408393ebe67ea4d57462b9e6de2688f23c0c113f5c1e610ad7bde17b4b694bdc12b3c4270013b2ac60d260ac1a32d50675d5d4c1ba3813b95c26ed8ad3cf0201882109901448eb1e7e6b20ad2783cbe53ab0f91783b146fb0bc5f7995ea5a51b7a2ee205ac027cad6246ef5be805075a759e039e45e31fef16266edcb9cc270edc7f941f8c5e3fb480129464ad7ad63ec97ae83d11177d7f77f1bc360ade296dce432e5dcc15be68244fcdd664e2d3fdab7f95569a1050fa11e218bd17171e25cfcb4635a66b6ebe8281f311f621271ecabed36f5515886a8bdcb63dd3c7a3d2490fab301bbe8c5ad886ebd88c1fa99cbd474cb02ff86583d4cdd355e712311370b3fa29b80869239b043e98e00e53c27de4969f9794b56250fa456575b2b3d73109f1ad6da02803aeead351c8a009c3d26aefd0d8ab9f72de95fb8afa2c329ce3b49e29e9b3116f1675055b60ac525a1988069e12a00644456755f39bc672d99bf8c860bc3a65dc94bd7a16fac1db1e25b6a0b480a6d0b535df9fd27da8054c23dfa66e1b6a14f9a0c3989db42c759554bc30d63d65172f02e67dd9cf1e73ebe6e8024a85cfa317489d0844a619339a95d4e964ac191c4664890bd85be143a879f471b93ef93b24dba954bafd4420e62c9d432227d8100805611bc2094faf1e866c8aa88a8bdf73f2fc90171a252c2115a015b2f5df1929ca217f4bab3ba5c5e6e3b580749c5211240a8b5a3e079e8247f5e51bb59d76ab7ce9600a00aea0e1525ae8f11293908a174a656d34eddf40740043a2394b1c7b60a9834dbec832238036ae826d174cfe523021304e6590651faa3540b4b8b5dda1016aab4ab8774cff498cb0399e3abd132f86b0028dcead61d7340b9071366d0623b12152cdefe1f50d1cf2a8bfd0ad81b24195c382f7025f8a73fc46b227b29887e4b81625bfc3f1907b7656ef662f8763e579da99438cc8c2edab6e47954072428370dafd190523fd74d444c3d85f87d7a1ccfd1934c618ab9f78bab29afc4312c8c490a3364ab055f846a17d28b68961adc68b32272d964e04ce89fc2ae57b0425fd9e07e21818fb78ce70c2023f2d1e8e2c6c92934cb4472d6ae775935dbded7804fd6a6045b3642745208c459dded34f1de1305dd50c4aea9b1d2bf52a9ed1a1580b8b01e1e2b2a945b935693157d0e2f31207f36ac19a17f2ae391149af16874a990b6d492d7a40832642c3a12992b520e61b8a071029d18d1523621eef447bfbba33a86b47f27742c0d6d1b0b588dd9e8832ad7d94cae37de1a6b4a6662fc9aa65e1482b2e6ac75c76c05c8083e0021f17e44f88a77021c60cd8dfae30a699f9d4b950b53461502186e5dfa340b4160dc4396770844436d396043e5bad90fa8f86cd44c8296094f55b0281d3331a8544f341062a81802eb5aea56e37720a38de6b29ce7f89517099b9291012d0067c8f4f8936e71469cfd282796d203649e3f4207000261aab48b6df1a4356e8545e3098824b39cf97460acf0adecdcbda82ddad0a5f1373fd118770c7a64f21f3232a8407ebb48abe7f728f4117340a1ac15b8db92b84441a2bac138a30a881c0a84d78675fe9e4408c16b8d8c3a3d177726fa06d409d2d2e92a170fb578d16d20361b0e010009b8616377570b674bb3fe7af98c38bfb0e9012960a89fd3c3cd4f031554d6b6521a4b99907db4ecc0f0801b89fcc6e22dd3a36c5e4de1717bd4009d9ff515f49617b7a9e14f80a7980c3860c5909186eafa90e2afef4aa035917eae81e10a6b35bd2e8eaed22d0a3ae328fc97b90977f6beadb030554f25a2be64d9e62d3fdd8ad9ccadea33f3d5a7878ff2385fcd60c41cfd100d091e3e05c34a2d8619c5614f49ccc91e94191b2082af6cf5c0afbbf7d73831e6f8a77b450b0b92e36c56c5650b09ecba289e566a432fd332cf60e2e6c8405b60e10ff8e69f878b0c21a66056b99a10e18c4282dea0dec6a3e0138f91d8138c9b1d8c76c094ac4f9051b1f48e132428087f94589dd8c3b058b49085d65b22e70aeae8ba92e54374cc68e25dc229b243fb4502feefd2788b833f8120fd5cc34ae85c6142941057f8f7a3307a5e0c29245733049f8d5495e62db3c16de3ef3a02e650bc8304a7a465cfff1c87da88fb035c755a1233c48763b62dbbb8f246dea673acd3009f0feaa39d1cdc630136319cb508370ae23a5cce045e011301f81b7712d7594c320994670804a6b51ea9a10c2291efabda6994bf96ca551efc84db8685f0990eb1a25c6f9c9b426cdfec465a8c3ef2cb32184268f62d8cbe11dd952df7c6a8b40c3219f4b4f7265b491425ebc2eeac0f3064b788adbdc9a50479acf0bcf2a034fb9bed3747133b6375f42cd50d065168bc00d0e8fd59e2d58937301ea52dbf8fd0f94c075b065dd5297d25b5a890d20970781b010267043210bccc05e22c51b1f4ff1c293a4d7adefa3325787253a4aaa68e13aafcfb7571ec426ad70f3231d0f728d07ea52ef6048fbdddc63afc2b1e67307fdb0c4c8ca8f23e25a764804940af9960fc9d70cd78f6d6716ade4660ff0eeba41cf14a3d29ff8ccb6ce62b2a14669af9395be229ec418132eaf93613f30853fa73831ca67f1d0bc3985e83a36032bfcb5d789c1ce334de9c4ac9f19269029d9134770e05683b9f46be21bc43601891d138fced33c9f18c58f4b9c58eab378505e66927e6ce86ed6078799b7f0c8a39ce284363afec0a9cd199f5336e8c0f125112bf1cad2a92937ebae467286fd3548effd4de1c928a801ed72eb2ed05d41afed2c386d051405bbf8d14331c861a7e550f0f787aca824a2816dfb02cd5ed129c022ee3b5827e1f3024351534cc4f9e248add0ef6b5a0a240b12d842aceedf19511b007747faafe556443f89504578090e3acfed6f68456bcb9940300e89859636477d7f341b929db802fbfb9a7611e3a3ccfef223d1efcef67bb0028ef4e39ab62a375cdc469a36c19120257798c30b28970049004d863700cd47422677268a649410d05d5aca290952a322474c158ab484d78d694be0369204cf6b46b9668d35405eaf5a1ab620afb4c64e603297b1c86c94f41a4ff501d0ccdfe2611a7e4f9df251d0695e965a62fbccbcf26a047fff86e87ca90bdb4eefd36140a1064bbc5ddffc9d169589bd353d3dcdced3c9ee881a5badb08709b468033a8afe7e5a301856a503753c781c664c75ff01cb20d1ba45e6af75bab148d8a3ec4a8a2ff2a615efcbb8b4ff264e82868e87b06ee4dc8fc8e9e1c6fc42ee780cdc7bd95c2c681f8e06235bd88b937568c44608919becbdc9965b4a99524a0109089107df0741fed324073eebbbbbee04dd3702db6ce24dc5b8bb72f74201a276f717a46e1b87b1d999bda9248850a0f8cab27118fe8975dfa43465d9546e1b7eacd404092a6b4054fe2f42d16cb86ce78b9c9b128f0c40b56c7c95456918fea19b29389599392b63a732c9c6e2f12e0b9201ddff4836bc0e77b3b1866c0ea177a73541b7c256353b11d4aa948c0fe195f154525ff4941eaafb1da53b284ac3f40fcd29f48b14958f65c8217288daffc9f81030784be52e06b9c815ec4cbf2c6273f0b6c011beef8c6ca916e7431aa6dd46e2d4fecf8d149139b2838ee918a1bf06140a45b0ef5202daffc1769e4ad319701b6c4d64d015829228f5faaf17890421694a7bcd9034a58b9a280cfab5511bc59db9f1d5caf0f5ba1ed37440926601213519ccfec216cb5645dac51efed474c0d674cc275d1756bab6ebbaaeeb9a521a5139320243c861fa49c3185139320223e338c7614e6ea30d994f9605cae38ba9cccc3a0ce1692c8e95f63a65ca822ed01353ea97d1d899ada99de127846a8aa980b9b8c0490c867079814f7797efefd79437a420f7ff362605b9e3e0cc4c3dd0b307a8cd309be71403b2acd76c31a2159558f9fe70ab41bcd919ebf9230e2fa02660bd860c90249f9d4aaa8244026a8f93442c4cc3f4f82af6380c50abe44d7b55ac4aa260929ab02429d4fed873fdb79182e6cd356f7cd58b79266f5ad591885d91bea50b6a3f495201b364c9eaae1f2c5931525b9101f2260055dedcc89be632b00151bb65b7d9908216c20d9a40d8dd857199c2ed392ddf87de64f03a934179b1d9aadd5df668b338f663985712c680d5b531683f8410328410465e8710420821c39501927a98996d9c3bc8c37501630c234689614481ddbabba3200c27d86c06383601630779b868d34d369e14069999a704ba7ae73576746260a196841647ff2402edb8393ab4918a3a9557a424a41dd81e6967a3736f3c5644e80e53eeeeee717677775f777737135254ac78a2abfbb3f3829dcb8a256aaa76107fca06d68022bcf0ed1301d46fa3ec1417eab754aad721d5fdf7e322d585fc3f3ee284a7fa11aaff042a41f5a74aa4808aa8fe0f34a5fa7b532851fd63a6a052fd65689a54ff1d45a8fe434517d5bf070e25a8fe39c0a0fa7ba055fb538516d5bf079b9b22d59f715ab53f4f8a5ab53f4388a2fafe58e1829dda3d063d7510e815f420dc100189138ad8e00761b420074b28b90c4ac8c1902934410c2b889a053908a37fae1bc19ac24fa27df9224e7c1925375caffd7e8f3da971eafcaf118bf919e7225f480d8973915de430f385d4d8402e94d8d46c8d12fef930f395dcd0419404a145314a6eb882f45f9d920fc9f515e028e8800a9bebfaabfb21fe28c1448d0f3fd874fff5f3270b681f39ccfc88c58c383009d8becc27143ec639388c4b632f3f08e9af17c2e3e2482fa426884bcbbfe1924fea82bca766153476a69aad99dddcdf997d9a43a402859db7ff60ccdcdb7d1ba046d8a8fbce334cc8ebedefc22b305a8411941f6a5530824b50fe2821f4ceb6e3e9ae09517e676fdeb8c54ab9eeee2bd9a173f0594a68c918533990b8e9816bbab4208e434beec0a5058fcc39a57487ded5b2a49412f27bb424ecc1bac718638c11faeadad12b6c955a18ebf7d7bb619b140e8b713435e5f35fd3ea61d2c000819e92338413e54911336bed7cc9c13dc03953524310e8550f3230c4548fc4d1703c64782023990757437b37344cf752c9f9223565479cc1ecb9d282ec3e60cd2b6766500da0dd19990e0396ecb86b7468f50644cc8f9324ba064e88f081139b1ff0643197a862db42e6a745a85caafce2c5c8484144addf36a9f05ba105faf649dda222a042205c3b00b99d7a80ba2a49c614415cd426aa8e44155180428a57ea522a423841006a7ffc620a016a2f81aefc3489c2293540ff0bdddd34b5d21f7d52bf959f292a344085f06918152ee9c126bbd795fabd20a07e376a4bed9f4a002106b53f0546ed9f20fcd4fe8101104a50bb2d2ae8a470ce981839a965594aa059dd1f288e78fa4b20d1688aaea488708872360ac59315645bf094a0d0e2084c09411a1447489074761a662b6dc11157163e180e8ccc241891aa286249b9a2d0e208c905d18ce2e80591c8322206904848e200091fc2894f8750c47b2f9d67035f4a114e82ff8fcf148afc4be1992c5ec4804159a8fbb30414474b6451bf37c21261d0808814473f5228b9111380ba3f48caa8b0ee8f144c3421dac1309e227e416dae00156b20d4e6b8ba5234c1456d2e01d505fefa0c21415d9f20a4509b73819d565115db9525171aea2704874be14491da5c4be54c0bba92fa56c50e08571dfddeefd21dc79106b4dd69903be9a448c1420a0d9a90228512b172ee34f8cbf10069d820ec7e6c75bfad523f776e6b2f6008d8be7c33702afcdddd088fb41724c60b9660138b76e66567c05895fb2a7e92f63806390c7c5413287c62881a6351905192a32540adea22157eec69d5fe3019aa2baf81df4add2f52a97005425ebf8f41be8a5d4988f214c187b06849abbc8b44d20942f108414f08090911050d4d8912d4da0efda24e4f9238d4e3ab6854e453a1b481d167e117390cbca191a83df8de45a1f620f501ed8f3cadf2aedf6b4161e558d4aa58e1c72fe047a356a52a7c99d3aa23bdda284636363640d4c08f54a0b8a97088bca990880995fa499c0a23cf0c76ea278d54f8dc9ea4fa0855f8d0880a7fbfd8a442a1566d93214451e1c7a156b96043dcd16498deb9b875846e85f0639256e9c0af3103471b82c9f8db7d701742f88b835fab025d2db0d9aaddf7e8ec871d842d847fe86e1460bb2be8bfdd36e4b8a903bacf8c045e5ed63a730c92e225a7654dd901bf32a0a4df658ed4dcdc40b043df19afd7b4e0bce637fff23199bbe6357dcc79d1581fe7b4ba7b798d4e29801042082184d0bd1b322fd3dd0714847143bf5fbd1093f104030929a594314218a594524a29a50c425a2fa594524a29a594525a5206017e319577b79bbb6c65677a4e4a5329b8db90959d99b12b0a2c0c7c08340c7c08030c0b58000e1c0318000000f065372c0ef834edc12f41a16c03a1c0f6e58b41bd1aa2c4e7ae8ceaf2d17e7d53f7378b290215ef7e79b1fe732337da197630b6555ba58f512504a24a284495df0d8b7b79f96fcd122222c121553ef64ed41d51953646befe2ed6c3f7ae8e8a9751e593b2ce8dda93bf033fe1da55dfbc6c9d2abbaf736427fde8b28134fc1560b9fcbef1951b398cfc54ca8d6eaa57fd8233f3979a0ed774ccabbbfe73a318f8d28d38c7f5f27d0a3be32fdfc358176357db6bff6d5f0b0b75fec782fc8db316663e89f3f5660bf34b2d130746ca2657e3afcbfdf29eef1ec3d7cb3d066938d8be14614aaa4d597bf3bfd40e1a2918162e5ce8f35d422697b507e1d2581373d6dec74f416805141d1c2bb02fc6e8233e8451beec7cc468452bc6ef193a426eeeee9d39abb9d8b23fb698fb699c3ff956bfe497dc26448552c686d6126c4f26d3b69d7edbb66ddbde74dab69369dbb66d336d26d366da28b7f21bc7d5643299b66ddbb66ddb4e9bc9743a6da793c9643a9d4ca69369336da68d9b6f42bd693b9db6a7ef6fc346e9e944b73f3d464f277a426da7cdb46dade9d860b6c27d5da9fb6ebfc2b13caae5b46262316d6599f1acb1cc303d663a6ddb76da36d3b6a1b8b6ac709d33638669db9ea64ddbb6994edb763299b66ddbb6b7341ddb763235caf4a895d3cac9d4f9d8def4a8eedbfea4cdb0bde9b16d333d4d9fb868dab687db531d915be9962a7894202857ae94503a0d2768f7b78fe92f1f7236b8bb5b7066346e4059a8632cf51262943e3ea9f1b5a1fa55fe4c6bbf34943e6a557bcce27898f587f7a8d737b74303614c7e2f7fcaf7313baecaee7d33d5f8a4f8754a29255cf9d52bd78d6b1dbcba6dd897b0cffe2bfd72d897b0f7512a71259c5267596f75d2fae52e19504dd3ba6d6612097273426f20489492a878a9fb53049d8a53b76e4502931e17a0c104f57730284dab5cfaabd7f8d6fc786192fb845409dbc3b2e69e6b0fe5ea5a0c5cd74d08baff2fa4aefb60e581bd4a15ccdad9a54f488519d78f715fb7293e480f84fb4a3e4a3f7fb3e29c5fe2e2fc1f5c4941fc91748432eaf73c9e4af57fe9989f19e8c48c26eb8f9c0ff940b8c6eba18febe3dfd899f9fe3876c67a7fcce27ec0bfb86983f531be65bd84b3bda9e988eb3d79630942a215237494ba55a855ec46acb8c8216f8ec6e116fafd978738d6747051e3700ede16fa9b368eb393daeeeece3e0bd3efce3fbb825915f587ca28bb8c138242ad82dd76f65f572c488dbf9b83a7700bfe58f785a007f679ea6f65cb39b66e93d666f0f1d5364f21bdb74f8d7d329e523db60b3ad25e6d4de32ccccecc1a57b05fc3a0fa631d17fdd0c6718edb25b9035f5cc4596d0f762cc418e4752f7d3f706f7c45b1189f9e545450457d4e54513c5451a8d3f3101ba138eee88f3da5bc276210994ea747991e55049b3ab399f97f2bf2a6ba8a65158d3c52c1b0d5744c2850d314a0d37b8a0635edbc50dd179f4800d5c5285d04a2c1437d00e14a8387fa383d10aea8a781033f722a190d1c645293213efc14ea5145280a25053742a1bac8a43d9f5a0c16aafb7460a38a7a2a646a3a505dca098d455d246acfffb388e214ab68674e278eabe585bf0d26d3e96411b5caf4fe25cc743a994ea7ae862850d47f91a8ba51dd191437494ee8ca9f229556a1de3f1eed8ce9fd3113c7358eb1333fd808f533dedf06d30ceea4cd807a5317350d9b61e26eb4e7a6199c7cd3c7a1f6fc511c0f5c4d5d0ded9db8ad510885e222188bc36871408112c1a86e453d9665344c15755514ea79575b837ad674a07ee3beaedba93d54d74f378ee521477f35d4d374145a79ffe813af8b248de8178dba3a0b2c548b76ce5a44ad6a797f6b4aab4eef2c276e6b0b072bcbb3d6c2d2e9e0d5d363278e56cc94c156a17ebf8d50a847f9a31e73e6768fa595953f715c53d8cfe03aa73d7fd650d148daf86a6ba6a9fb527fea3ecfaa2b9d4f69cf9f0614f4f42bef46ad9af14ee430fea41ada67535a6555b7886674d1a83d7f8ba8f46e3d819e3a1fa83ffd4af7a1def4a78f812bc619ea043ba845a36fa57ede3946353ea9feb373a2d10e71ff656768ca326a7182f9631e5d69b781697869cfc68ca954730f511bc231b027adb04fb55149b465f82abf6797755e7b730af47b1e7e6c951fc3b8cc6360d871030e3af43004c7c8ce129f2742256e8892b6587e775f57493f6d78696fc5a353a01e7f0719b6ce97812bec9a919e7f9090a4b60dfdd8b746830f95d4dd4b7b3c482418c1e0dab03dff60cf85c5e19ae53bc375dbdb3996e04bee85f67ca81076318b63bba1dfd717a30b09e6d2e282b07eee5abfd57a082deb97eb8ee37e9ab6e23707bb5f77a12e03ea3edb30511103b670018bba308bd303ce6120ec143828b130fed0d7a8ba3f7756d783eace1deb6c36b4ffd321abd810ba55479e20c8680a2836b4eecf155390a88040ef80dac38770a59630928665dc56d2757dff0bf74121db9ee5223bdade4ab148cc59f13901b62fcbbbd09b37923821b2c4870816f5db285f4c6d2743982c64cc21dcc99ce362ef12af65daf65c62d0ea041a02aa0b34a453a755039ab330fc3ddc4b2cd38ea1bf428d1dca0945d575328449a5699ac6894522518c82d99082c69f32c4a71d6c18a7bde21ad2ecd63f3557da6324d7c7ab830d136756630cfdf4b3a183ee60cd42500827fc8e12c299419733ebd93f5c9451b9ba3f5ca0a07631441540dd9f2d8a6abf106c865406d415828d4e17424149845af5357701d4358d0473f145f5a028ed44adda1f2e802a4fa9fc30a8f21605fdfceba020bef92890fffa354fd1624ed08ff2a8b643229f1f0b6a8fa3b4470225fbf25d68e6c0f4e2a83aacac14f1333333ffc73a3753e040a9cc58e854d6591c53a03b13b9e722bd92cff0a3340ce77490070539d13af4710faa7b543d08064d1d6c5f3c1e75dfff47fcaf87ba3cd078c896655996d5fd901fbb6fef0282fee0aedbee47fc8fe7c3f8fced4eee7ec80ec9fcd8c118dabbf308b49e856be1ad126b4d876ce1826061618131fe1504faad516dbaa261b2f1fd2bdfcf636750df5f4bc57e17c3fe7d95ea66f7d196da52bf67e15256e65afcd67420a161faf95cea22b075763fe67fa5544b8a1341cbcfe8be59677ce9a1a6834709b8a6c6d76869e18208f243959a962722a4a6a523e2f235ba5d98afd4fd98cf7f7a1a9ff11c929647b59c3ae690d87817ee8720b64acd0d8d1a1b9d10a9af91aab14452359e48ea6d3c1197aee589d8e8520eb3eff2342e5d8d69e26e6453a3dd6f89bb8f476df9ee4ba51e6be158525c102c1f44480d0b1744cb071152d3f22d2dcf1ca9fba624fd673e0ba5f6b3c897175763d9446b2c9bb2f6b883b0bd09db9b93a6776441347f7f8286d4a963da54e66e471644ed6d7db9d11e5b42563abb42e51cd2a6ee0ba9f995d4ae8af4dffc0b9b5c363f9b19336733fb2bcbb2ec4951d39149807faaf8bfebb18c8315bbb8ebadeb693a55b3cccabaefea30eb5ac11aebb2b419e6b32579f0a893089a4387437bf2e912b4ff5ba2b8026c07f6a161dffaae5e315efab3467a35e3734eabbe87afe6a43495fa174a59bca4bda8d35e3cd23011872493ce14fd94f731de0ac5a46a4cd5eac5d4e853d3c361e21341b9a7639ef6e2d32528ffc73cf108296880ba3f5374b045090aa0829a5501c21b757fa66851a9138e6871c1670a51dccc2929a84e570650f7a78ba24aaafb4344543ffa31c000dd5fe9cc5064a68ce71c23c5dddd9da116eeeeeedc5cfb99c8ddddddb9a5a49cd7076af1123f2ac1dddd9bc8dddd5b0b7777776e227777e777e796e2eeeececdae04cf8301061b2f5318b1840e3686803810cfc21b074b56e032329247278809c9230548b74d67017b485289c5ca0a0e3b69c29088731971840a23293133623c6f8887584465e0f09015c2a0f8204748ca101b41191421041b4949c5d450e4999167a55540e0fac02c483e913f0a9058231659ccb2d8267033c10f17dba47366141540a9fc52da9b1d461d5e869c3bdb8526601853fb617785bb10e60d60fd5873bd9a3710776682da40a061fa9f0c6d55680e329d8c8c8ccc94faedcd8eeee721fa65fa61e01cfa0757a948e077ebeeee5e3c827c8ce510f202bd926918f92b9550bf05aa511a46befc6f9bf8ca7af92048a24a7641954c44956c822ab70a69ce8bd2994a753bbc73b3434ae92c7d81a2c897e11ee9822a7f87164faafc1a606815aa55fba345922a9f93f48a6b24b6cd5dfa465099887b2a01e3b94750ac3959331aef6b90b2a4f2bfd02bae41adb8268bd5093374d820161696de75c185468d16ee13c22384e7f49f0e1b248447080f0b0b0bcbb3bcf5980d1b7e351e00789a66e158de46044057c3ba807a966e1d263e9401f5158b0d1bcfc26223f52c2e3452445a9e488d1a2d1f9fc657b8ef37e8c47d3a6c904b0d2e4524d52284878565069c33380ab3dda02a3ffe42ba7209a16d12c2f6653ae0e66b300760bf9f0d5132fd598bcf31a331b31105bd9eabd4cf7ad2f78eabfb313fab4382fda004be92869ccbacb04a2921f709a91db02a10aff085d41f5ca54a952a6f8445ec870fed43f28392f94a305ecdd274740cd63b841042e85f2291fe47d6f140a3525246ca1e3e89bacc68a9917a2234fa59fc377bd28ef52f7d3c6aa99b97f694fb7854aa3d3627a5a9d437aaebc7b84a353d46ea3e5849f033ce2bec0adb7fce2ecb485ffafeacfb484f7990381e75b518b84a25659d411cb0fbfab1c6fe87f55f0f55eb78a0513309ccfac3fa0ff820bdf63e48cfeddef8bafb61fd077fc6633c60f7c37af8d8d70f9b9b7577613ed263a4ee47d62111c2e55d66b87442a46a88b4fc8c1a349522c2727a1aa80f0839692f03dbd45d1ca6eed3ba7e0cfb1a1c0d2ee530fe298e484b17849484123a3882055154a153b31de53e1e15fb391ffbc5d65741866c9528b49082186458e10a556a667c105c7363d51011520331d27f6b3a48dd0cf37b3ae46cb5f21a97717485d4ad70930414be0b0da4ef2775fdb34e1b78e07a752e33cc6abd0e56e5c1ebec2ca12fa2d04d8d31b604a4ec7e6bda04e3875c10f023fcadf09921e7dd2e84990fe6f29304b457496a502a954aa552a964838dbe20131f3371597bd99f38da5e06dbcbb40fd6ad2b69d90909eb999f44a2c187ca2f3b48205c9df3c7be1fc219d34fb3ede6b39bcf6e3e3095a2b4bbf935f2fc1b7371c4273141ade79ac5e4c0819d6161ceebe3c797d13e6b4f7b1347dbd3607b5ae9276c595beec0759b57d5bacccaaa9401eb207ffcddcdeca39fdffa56b5c541c8cc1bfeb3c5dd3983ded16835e382ce198dec1439c4680111ff06a59a14f48bda44299a4afd7b5e4c8c8c4505fde24e44224229258e058316c4a5344553838d956df23720a510e2a0461c741bb1c6dda4d2d429a531f20821c6b8a9973963b828b91f1c353965ceee93ccbbcccc5146beaea0f1af2ba8114afaae59994aa5aeeb88e58322ae2b9ef48429764e8c9da30314e5888f919e273bbcbb393939399cc3394073523a314f88faef720ed7ccd8e518d9192576657deba898b0baced92ae41cb6591c40e020a942d47e1c1c86d36a3c02d1b6123a4c34c1c3399073f688856967258038a773765a1593b40ae6e4e44869d28eeceedcb98f957c2023d4aa9473ced91e94cf75e508a197646a5aa9eb8a385c2c92138de830e19155c418659447f108c8f3de2dcbb22c8f4632c628b985070a431d65a9a755328925301ef9eabf7864e47107059beab555a59e129356b58e515d35114a3d13ca21918c6b8ccd515ac22d1c5957a04b38c76684afd21208af9f2830a98ce1f4b4aa757680f5db74b69d281ca9fd5b92de76e490aa63d3d98e4c3944dac43238239c2346321647bf6f466a3315276c2a56467b2d6de211152780a8dc6155d031dc697686b198420b82b24fdaeb2ca8f65b41d01d60853433718cc5d1f20444a8fd2b494045ed5f5ec52a304cffb74188475c50d247a33ed22a77da4644bf524f8d47a51e2ff9f84ad37a4a3d380ed33f27a55438ad270afdb49eda40edf5507b5c6222042df9947a30a1da4f7be8970101f92a96118f32a07814817c85613c47d887f160516aff50abb6623c56603c4d24c1fd48a042e330f323f758f7e7094be4ce925ecdd89f2f96d4ce80726a7bd013aaa89fd4a9adf384a28e9c013d6955270161943262ebeede41663935f92f533ac2fab44603c4f1553c6a98fe39319c8e98131446a32a81f5997db4c234038a4737d81047ddc4a1316a41a5910bf5b0ac1c232f40603a73521908ac6430a45ee6b4a040617b1e638c91351d14093aa5d429da58407d244bc9ccccccccccd2a7539f9ec7c4380634e77f0f32822ae949f2594a29e5f498181fac87f67cd5355be3e3b2335b630e1b6915e9fbab979c3fcadfb8127759eccc92f417d7437bcda4c8364594e912da437998bea5a9e463bf9bc39f44395e18fec82d002abfa451e234ee9bd5fa52d546b5fee2be00d4097b10b2333c501f3687ec7808b233cef93367d52d3d4dfbe3b0335d8fd08f6f64e49b1bcb2a7151c7617cb22e8d61e2d9ee69d24586e89d66d239bd3b76cc09f49fd3cc3a3b47a4fab1ce0e1ba020dc848d0d51cd0fd9098c6aac4f7511db383be3be9adf4cd46e258e703ef2e123afa117d08f5d17f3a53137e7eccc7603ede7ed0c0d33c831c3b6d05f636749417b7a98b4eae3a37ad329aff39e35d4db85bb31e88bf5df35a080f2d8d14be8c742dcb338fab9070bd39f4a994c423c6b0fd15ef73c6bb28b813f461c17d0af6f6c70f11bed7996931dd4af735e883131323fa0dcd1f40c0bd3efd3b345edc7d1df37564eab8cb4cafba66f622c62e239a9406ed1e6e001203af04df72fcc01c25ead420e83907ffa8a03b3b9eb826e515d987e9f09a5359ac5d14f93a0df16c1325ae5454539c4286d24cfc927c6e8d11fbbaea01e638c5106ad57d7efe6e3abab9bddd6454cab42b12ad4e5aa59fde6ce68b50095bbcda73d18d0a8c4aa624f7bf107476a7f14b51f06ed0c7ca902257646bef544ed7f1293f67eb02a5214ebf517a3c08c50c19701e109eb25c74f62d2aa08c47a51ec10ff0f33c282f2535d89daff6146b8cee2d881ebf740f8c511e405fdb62a556e3eb57ff3e920cfbacde2f825ff7ff2211535f4627a6ce0851b40433290d7f376777727105f95806b94c8d095bb7d2a03fa79b18789e77945601576373ef15e88a994fb6e2aa0bb38fc8be4448662d06bafab47ab7a001758debd3020ecb32f719be37a1e0450f7b1151e66c5ba0bb80dd53499d6eb5f21affd1ad855e9b56f1a3d768686b76378515b6747c6f990957efb96d1baaecbbb4ad9f6a5acf45bf6da57207badab40e9b76ec7b4f1617bcdf4f4fb314b7657ac338b0c1f94dc40dff43e68bf7d7f0f5ff940dff42efc376caffd0da6a7cf9a0b7713d85efb0a6c5d054c1ded64b2a31e3f90c94adf8f65dcd6c8d094b8adc9385f6d9a0b8f204a6ec8bef441b6aede50ea8268dda698fb05c0c2aae27ff1c8242d89bd64fd0ad5a2565daf9abb3d5a45baa66544a73d263e36a4d64c36b5dfb2588889faa655bb9a445378888f7a082eb2fedf573d1ca67f4e4affa5a0f2b3848ac1ec7dd531319e979adc0959d66b9551d57a2c1ea9fcd65babf2b772ecb610bb2f1e515725a5b4acc9fc9ce3f28f3db866655147ec2663b1ab1fc0036abf500a0cea8500761e802aa0768edafbb33328a0bf03d78f79aceea3951641bf3deaa18707305e762fd99002670bcf8bda60d416a35a292fe897e216a61f867118b0f33409fab1902c925fb44ab2100bcd197162919cc56144c764b369f1844f504c8c0c0e3ac0f042904f8c89e1d72a33c7743b81c8094238adc56efbe7739759736a37044180518df5d38ba3fac5785184a69b609d7298b6a8e0757f7640e5e57a69da31b4c30323348c5dd0cfabdd95394d078d01fd627c9ac4c4c4c4a04bc9e6135331eea77ec70d2f3664e2c65475a9fdbf335dfb27b58ce818638caf73c35aecb6bf357a847e29984aa5a4c42992b3f9c82b567c36e6b4aa23503c127d624f7c1277e2128901b5949de9b4b7757fa3019d316767b48d468ad48e455a25634e7b3a54a014094ada212569558c3931c7b2224e9f5ca8142d7775450d11cd00000000c314002028100c07842291503c9ee8baf20314800c879846724e9949e324c9711432c618428c21c010024004406088860800b17ccc0f9967cba0b8be26a7e1c0c86b0fd9938b95c77bf289c01d1f623c61996d1ef4fea49764f0c2b049a02c78c15122aa34838ea6fb615429d635e75ed5753030ad8a59a4522b24a1fc86b48a399eaa3259231cf1db1c037c5c098829761b2745f8b61669be1b81f9ce0d544cc9e14f5d5b951e3b710f0377732bf956fd6d112bab45646c179e17ce52a5430a3a7de3841c2611ee1522d009c0392dc4a1455c9182403be85fdadb3feeb9984c549b26d5936a64a49d66fb547d5bac0cde18864044ab5dc0f2258cf8904ba549b2c13aaa2144bde86bb6ad78e66be379cea874e7a047bd9d8a43f0c8c9e519ac44135c0027dfea9142627dba5acf79b5e18b799febb6c8b00605e2a14a11e3f405dd2af6b6609de284bec7c70a72c4ee1dd0f7d860e59527b06900eb8ecd3f20f861088d0403c989203be1d83c85002544600e2282e2cf2bf4599e08e52dd7ace1e439b111b626ce1aa66ec44d4cb26b08aabc113b09709cf6fd771ba079477abca084b366c60e30d088cbd37519797e135874099aca06606bc1981535ce5f51fa48cc1e3f54012338cdb386b26b40f39258bf1144a2858f610aaf72ccc9bfa6554bca2085132280ea71dc89270da279cf75e80449a3df6acc2490a3aea297685ca49e02591bd55858a925ad400c494066d90aeace08eb5ee1340bed2466250de91e0de74e31e16b1f9467b96f546403bf022885fcf45489a23d9064264666db57839b06a8a8454567f251bcb9350915f729dda1ba650e64326fa9a3b300a53f98cba9c7fbf4b66d5bf35fc8ce6e19897e1f56fe21a04ff62cce5ae1e479c8a7fa9b414eec877caa37b39e8b9eff3b94babeea8bd5cf31d3777be35f059ba0dc24bf162ee526580046374471fe4598d4d27ab380795a483a2b8ba28ed21f96c2cc1a55e31a2566e9ff039519c85fd74e42cff227758992ea2666a51085dce7f1ca77eac16eb6380fde72f0d2f9e691420ddc45d044370c9ca8d340169e243fedd3b3acee9ebfbeffa4cefd9e3cb5990f222d0c39b5bbcf7f9ca87fe6f46c322097562792396e65acbdbc8e2242dcca334d8f898be7a46d20e507ef68c2929764d131420910f6c39a49b7863815f751eca0a4d0ac89112d0b17af00a13209abe222b52081e0a2ef6188a4dbd248ea4e709dadd62fa4c94409bb40ca18bcc4c4f0249566d533082ec88d350de8a0b911a28198cf0f20ba2e92251d43dcaee39509ee1564d3d3d7bd8bad806308b75caecab81ca765c31302d0c1a46b5120f834356bc20f39c566da9d60c952f5b46855fefce142063de20b3dcdc0e11809807e580c08a6089f413f49e37b1c1f7bcd2a13285bcc66899605e9387fb122c7da28d00daaf81a12397c3e8548f19a99bb688d2639b7bc4f041523529b67f51193dfd3cca7aa3cf4f691efa6035273a27eee200755a4f836e293297925f15a3bcce3c587bca7bb0d650d30f01a7b42285181ca1add2dca15a84613b725820b38e55b9deda862d133f3f21f8b4a9556d9d2d044b231a11c0975c950cb282205223eb9e5dff27ccf4092effd8939f3efc8d8fca201c0fbe737fea62f1b7508f9fb24ba5a35c10325d3a33e94ce1047055881d124e5346a9ec4f6401dd7d1d403dc263cf4216bd062ec6c884eccf37edc5dfc86146067c428c238abf8a95773a9a1eee70feb69c9ee0204efa4c530ea1b75f082c02d35de47378dc6d749eadfb2525b8924bff3f26b21b892a1914fd58a6a0b9ea50a176b99d4a8fe98b34659819fcf0717104a09b9471b79da8ff47cf62071f22fb9e0d48ee3ed3384c6706f3ab80a9ed9743bdeecb79c35dc3fcc3eb68cf30ebdf6b0f77fdf412ec373328faceab7c57372f979471a2b547f4d8dbd354285f19a107f861ac3f95cf34f5adf871dc007d7a0997d7003a16d089b1a13da9542da07e4bb60ee95949310c912408a61a6c7a04a86f83f8ba5fdc0e82cae08bc3aacb7003db90565b3f73dc7d0e4d3eb58c030434dbf779a495f76f11054e1a36ddfce5e47eb422530f6f42bf0220717305c463cf0996255f804c0ca8347c01ffde4ad09c390d9808388a013147b6d3602b010b9fa36fde7178d53d402ce629b5917fca351d5b31e7e781a4c93948bdd9e16c0967a760cbec54b493e592e9238d0516f36085f1c7a88c254034a9cf7690f392b806ad9d6fc2d1e128ebd841a6f561514aadafe45ef2bc2400b9b660deecbc9aa3d0878c35cb7fda47af1a45e1a0e2ec9c58445bb11caa2c3d4851ed6538ea0bc7cf7238ac087ea1b9a40cbac12f236e974a4dc206ce913d6e2cf9901a79342a923c6ae6d2e062a313f911005055d6a8ba66c445c2e481cc2375219a27af6204a57018ec54b261e8aa7f1fa2ae02a62c0fe02fac7cf4d52205e3dc2f07be122cd970129ee21d2e8077152355343494965df6f41d8036403afbb1967458427cbe18a1746508958886ef51f0ae89de02580e9099a0fb1e9fe407a8c843dcc9369f904a9fb6472e8ca3656094997161db81acc5e66b6927ba19151ed81248416bd0b5fd2fb599a8f14ca338205ee1f740e37605128ee92de9086b683d9fd58873188a7de7fdd3e383f45fb1a0aa8a33be72bad2dee9d35207d2ecd3e106efb5a5acf19e2f4297ca435e90c80eff09e5b7586b81ec1e522e6b99094ed5e1502d25cef9e7ba82cef57119b695f1f08fd5abb7146cd4b109d1aba77484478228af9f14675066c806adb3056c483b2453b1bf237b85abf5f2c3110acee2862bd587ca3af1b9a59c6e3cfec2b8d084c2bf2817b4e660ec9e1cbd3966352e6102bb307ca019bd57c41d772026a4ef90698eae68626419788ca2d2844858415ba6f299bd41128f3245b12b336607b8bd33c76bca2e42528096515df5d19508ad0fa08ba9e4b016539e8fb9c43a1905f3f7e12aac3c76b11814120181f910405a5c42830050aa69830854da9a78da4e017684ee3867a671d65a64b12d435ce64bae0438291fa2979396aa20e25403eaed0db19296cea109943a216623ad490120a1c15dfc5654d06236968dd61c249d5e5b4e0a873ab7e82f9f870cab78d1bb9d53efc1b5619209ad7b93067864c9364b40cf39befbf90cbbc00824d840a56d6a4aefcc0ada183a8e0ca05646f5c8eaf080c0f0da7f3c8a6af7124f7994b8556cb0850c10275aa985987c5ced5a8b74f96e6bfdf2450602de0666496885da3e680d5750a10f74bbee1693c7f716044c205f199a72443299f066114d7f62c830204b398f4c03da6046a822eca590588c31db72722364c570421bd24105b3ed499f9c0d8d69292c9452c1704479d4baa2876197baff7d21b64e4ad00487da538965519a52ca802e88176146d263a5438643ccff0a80a2f0cf615db68e4e21933189bcf55e11c83ed288bc251790471db734fe1f731de1c8a72fcf3c924a62b0b9fb2a23a53dfcabedaf75a3526ad81ab0ea922e5452462053a3450a61ac0823b838a1c4365edca803ec5f8b6dd649b8ef01fbb6d5656461ddcd5e2125686921a7659aef58650f775854bdb160c2dee40ba213cf9cffdf14df2291b6f0c2a975e5ad5cfd82f5d73a4cbc24f5f41c74a107a189e47d27a7d642e80f4c5ac95e132e18c4e1250468d62870538e4ac45407177c5a41623a5fa286e47406f39496e758557ecd947195d65df20e38e6733d3a3245dd2edda78c21189fb29011ce98a341be0cf96353c578f163e7cecb88ff410cd1ee203dd1501a878c8220ebcaa0f878ccf2d4cd3f4a9163a6aa1924bba8c159106e7551ea60b4f5e140ccfb0e3c9edd651fecc938b958e56593e75d5c51c0dc1a518e4260c8209d06d36cfec897aaba997c73e7591892d47f5e860ef2610d8db9e17a51203949a53665eaf41d0ddf7e9061ff6056826c2d0368164ce49e5d67f72740ee19f5a466ca92cccab9372b8c226d129d4e25dcc8dbec0d4f4d5880345858de2850c3a730ca38062e0a61c1bb460c9cbaaf911ee77c4cec6f2c53d5d8707ae8d91f187150bd53156f4050f83096f9c059edc76d00866a5c744224b325eda14414a984c93bc9d2c1613f42ff221b9e6b3860135afe8090bf0729b4ab13b098ba7158ce125d115fa3218a6052b032ef019dbec1453fc2b9dd043e068e3ede390c4d73bff797e7dcdecedd68a4729e0fe0e177348b058622f01a98a8cc83881c34e3d869185cb30fa2e92d35c94a2426209fe9a0a5b1a445094a47e9ec316dfe7cfdb79e72a29ac9263eb30b9d1d77bdf2b5cbd422a33149eca1a7c0a8202022ebd9b643916632e02eee5b1aa6327b9655f556742a7857ee0d7af5943fa182751e1302ac968a9e8724e1ce8a647fd6c15b343b3a1611705e608f98757bd3789e28a375a31aeb0b3b0ed8ad77e3962274928d6f024f53b8c5df2b86613f9fdbad45297b11dfdf446f74a9715bc0e4825162cad1a10460978ad8f10d967fa0bc252ce823b27159c6558495ca83b5a77de993d5ecc65dd4dbd9c148ce59928c77fadcddf20245e928dc18ad601f3acc2d09f3d09f44970784fa57fd384016c4c724cc2c11f5f17fc09fa9f5e3dc7ab5a7bff27c2133d0403f9a61285273a479b17cdaf777da764cc13d3449827292410a662b1e1d9b414021c9376ef9482b44ab7638c31f8570b1e7cabf5ffc333046030452bef14ee37b64208ecc74ba4caf225d1fad206b527622943aaf9a04f2def4ad4846ccc812cf63ca12a9f0270b25bd7d17d853004cf3a1081c3523432845071e391290a0394c4987c74c9111084f6bbdaf3726a21d6df44b08dda93c1fca8be79677284148a84b8a00999c6d0e90ec244f71e795f6d48458feb83b10fd4630cd5e35d24c9199868602ece493eee1d543e91f3d8c9d2eb9e758f744e644fce3c1a4de0d8a2109ff037460d1deb4145a519f2c208bd8098a5030626c2d81126e5fc2b723fcc7d3a76673992e67b59a13e987a48ee0c06180416b74110f4e6e5278315f105c5f333ddc77ca79b365903ca7cf37292da84ae82d293838b21f88a8292ee968142714f408d9aa322c5ad56a8132ecb70e0ddbe642a402e927934dc5f0613482e30b9d305ba8b7d8bab7800504d03a119469977ea098f7e7816592d1849ed7266be4f2b6142348bdef6769252f4d9019e7f506a6527d0ca013597b690b4002626169fed8b821207efe7b0a0c5103406ca89e0c278e8a6b82f349ee544bfe4cb63b261cf1d62ae02dab1c320d2eda3536a1491bb7ab824a2eb03ca83ae7272fb48c11240f47401febf019d9ebf072a2705ee336146bba59037c42e6949d36a513c826cf17e24379dc914696353b83066dcdd73e773e3b7b80aea870124ebf24282802d57aa72d621a7b5726510ceb4e5da00eecd41071e673fe67b97acf2441c3cd76da925565886864737221a835187150f9345ceda9ccab5b07b72527d3697866253556f5399f94eda646362c8aa73df4cf37db1ba5007eda2627ac7bc5b8befdf1009c4245094b58883f9f7536d2d2d3f705ec6eafb764aa685324a401be1e601ba42a419d5501fb08604eea6126aa8c163d794a783866362e4d11da94a10dd8927cf403443c64b44809fef3ca0bdd9405aa60bb30a24249d07ccfa472d7ba1f7b494cab8ae534e2c1a3b53feed70415243811b08898dcffb6874f4333f6e6a6a0b641221a130ad8df904496b1e428614041d33771d3781113125dc88ad01c2559c8c16d294364187dbed67776fcdcaed54053425e00bfffb081b209453bb53ed843034755fd54deccc71d548d6b95d751fbe1fa62de5a38b6701314a51c92aea30a687e972fb39fc33296235db329018e4b6042b98e4cedc574d6bd22da47cbdb765d694238eef90e8ad219afa0aa7054994b49bd7fb1986b22e781c6381c3faf094c99e657a807b097f9a49798b9f59613f9698cdfbe9202f960827971fc20c799e4ca5961ce9b4314f8014cfb1cb27bd7b9affcb9a63cf62dcf432599856ee02ccd17e3701b684f9bcbb586557598a98cc7e0893ac4020ea799850dc92ccf60fe477b1b0c1cc8be9e3ac04f49bc227e3dc89a39d2350911bbe41bf44c6fd814299f480ac425e24553db356e3eccaaf0fa10aa159d1e24abf090a443753ec2122dd3cd9d616ac70c719e961f821ebcd711c50eec740c721aa3f8d87d87d6db366e177ab35b510f1c5b306ebaa5dce93dd33cf95c084dac60463198a434bec8f4a3c4a23dea6737aed603b34a6e2bca036166fde038d7a5d3231dc7774fb81827c53a5dbdfda11487717797e05898614a36bb80eef56f659bbf879d56cae529b384f132170dcf1a0534cb4585364b3f1ef795690eaa1e911792b258a13990463e95d2b22a420723cfd29bc748d605cecff4457d67c522cf1d7f059a88148b61358624413a20008ab739aaf9572eb91d495497d26a1bfa583bbd278baf98cac2ac7535022998847274160131c6557817e96fa5e52fe2f35952fd9de1269625e67f9009e6f86950a76e4ed9d1a3b7d603d0496d05824d2527cbe0b69fb2077bd58d4021875711eb0a0a33dda7cfd53ec294ab282054cd8a1251a96298b2e0fc1b643c48d64022fe03c263f0c16b8094fd17ad6ae4abbbf4ff2693f21fe77df2b72bd8f2d7f30b5281574ef7b12e64fd7b6da2a258cc1c362075507407c5f21c1573b6a5c0a82503ac792b808f1b2d3adb0dde3fcc0b267de96a8d047ec87cbc9b7ed657c404706c0f6de1b57d8c33c69bdd663a4dc09d443bd98d0c7904f28301cfde4d6995c21815840aae1525afa77016501a952b0eb2f941012a1db281e70a63316237e10e97423da274395607e5cfa520c0ff04ae212fab54ffbd760b22896d32966a77b6d474732e9be3eef0189e590e4a78473ce594a8b4bcfaed672b86d32e7f8ed6fc963f388d6e6bdabc203af47651b93ccd50cc1c5802bea15948bd406eab1f8e94fac32c62befa6bad9bb012c8efd2f0f9f10fcc1960e59a275c28309c14a39cfe44a3a79f997b84269d773c5a0674ae5b51213e1a42dd877ecef1bed3bdd946001ead852c199b29da16928ba7a960576a5552d2099a141c65075ed3445ded0cb270b5c0feac302121b89d47b110077654fe3f2a2fd3cb92cc9f8629914d6af4b0c0251504097ac601fdc97aa1e4ac02f0788c70bf5f8af32c0c55e89383ad3e6e7f9d942c43275e0fb92e8ca2c4e180d981c66b02e15df431ea1f93b1113d7701e5463641e5a79281d1e22873107159539d52209020c1f7b24151685440513d60a9e1983a2e408c391832f6a44830471bc5927c1c55c587407235c0f0da71f269b96a7c6ba682b52383f510c8fe062f67caa8d5c7bd769785a829fc12baf50a4f4a9f821e5acbb6a1da07001d58af82a05961419f630f9b3c1a2dd5885f15253a4ec7d39187fc78d5f303a76aa266cd406295b07ea469ebab34ea2d0f07427f05250e6d1d1ec9d7b56756b4dc3228fb5f481e4cb6c4efe526c32f4b724ef5693211340aceda38b162b36fcaefe05ed459d9a167940ac846d7ae16f2a5460c6c774853361f15d8732295ce78f4e41fbd8a6f5847c06f2ed53de8d5c7251f341505dd29f5c029664eb3c166316573d3703f49dadaeac2d3a532e7f194b804201361c82f305760b532f1f9ef26811c9af5331fed375f3e4c8cfad9199841fff44c283185a92435bc605b8960f114c8351059d1bc83956b9e3c324d60b524d790a24ce6eb7f8a36bc6d9906038d0cbb418fa83edef296971b4c4dd46912e93c6517af20ee257b08fda16747cc7449a9a8bea6174172e97fcb7b4053551e4ac22a59d865b6e112f99f9caa9deb4332b6360a354b0ca5aa9985b567050bfc718bd3fe166308919ae5004d89cdc73e102c4635dc0681dfb8ff0c5c816a2eafb1a923fc127d837b681d81a4fc793dc6baeb940451111025b17b926acba316e62991ee8a12aafd8d225658243f30c33bb84ff9ff64c3805fe0da3dff9e3b79ecfe40b70ca0c2ae209ab21bdf7214c686c899f8e8cefa37a9c0235f2aa411c0048bec4562807a8916fa2be1b1196dc95fc74e0b261ad9b38f509e909694e5652b509daff710f6d6349961955a983eae79cec951042e36c555dfa4d73f6433d0df31c5ed09a2a9a141a9c5347abbf374be0936fb1f169873ef3ee6637052002a4938bf33a11fe0d91b7c8c7daf5c883c82c814e569d4aeb78a73975f62a74a7c1249b3d3daa282bee803177756a53cec1fcf9182a29fd34969e6331e0cf485771f22ec7139815c4194355f574da0e354e8bd9d6541e17831bb1e11dff88b3ff6ef8eab99633d17d24dde4865368947a558188875beb9988ff8a6af5dd05d6f520ffcfedd91424cc58d961f344342e8a48a20744938898a4072481af1ba524dbd5d679a3adc242d69177f766f5f02108ef1c3dac87e0dab76f329ad90b87e71c9933d716da22d6a339fce8eb6c1f16262160695ed2496f40ff7000e2f6ceb25596c86c3fee678e25e6daf2b7de055653490313172c3c0717f557046b018975bc7486c912a10c61dd304d69d53dc48da231e4b88e977d0e79fccff97faebb251ed103c6a92224ddf6899d6a2dc0e05295633981abced6825aed486f3097809c670582a161413d5866eb683472e5c5da90e75c91b9667b760285228588ca7abd24df50a6821039454a0ef2eab1af8747892a120363653a97c02819f65b5950b76beb962313d632ea365b7c0e7f65f25d9861584b5a291c1fe671a8d4d0d76ba0e2032dfeb3c0b38b7278e41d1478160529385f0176050d5de23b852a9cc1a3fbfb8d5380c6fc75f3c148ae38c472795124369527263c1c3e6df7e6dd8bb8527974a4fb312b1440c800a4eea9dd3d0c3672a987d8a3bd54dd82bbc5c74cffd2971a12892f69ee9cbe4698503fdca3e26733e6458422af15996d04462afde56f0f0af1f9841aec59e0d1f9d9b9e30cda1d5bc014b0deb27ade65ccdfd30269fbb4e721e19db4fc33c0a7b5469b7c7e339feba4d3f3c51a78e8323cf8d997243164df14d5625c75fc43645c59ca1127511930b880a4f293c2f832c6d177d62a926441bf1dbef7242bc0b34814acc12798da92b9aee9ce1da3505942683177e188deb43d89e65e9f587489b4dcac1b1b0c9185f4599a1b05a5f70fdfb335e58c4dd2a5765fae76c32851aefd9710d9e824e382341a8a9c430f8cc6a388ca9fb75ee9567388af2032d28600e104ff2665311ce50ebe244d59a3573c54c11dbd1c9de04883c4ae297eae32fd18ea73c9c59d4af1a0305eeb0fcc160d97162a76e4d4dc8092669a68b107858bfba5d36f90953b0d6b10ce8946c7ea1be7b3c2398b2c0b8bfb8f49bd51eb8afa5c75d97d1a8087da1c78d2dc6e30b421ecedab340b106a836d7d3c1067da3e2dc7b750388e5434c5132d56ee84c0b94a95d4375ce2e3a13381acfb6f8825e3cd23dc0d82759cfd178f4d843dae67856927a83fd0e2c407baef24c182d4647f02ed006911aa2bcdd6a321412a96407ee4a86336bae35f0373dc370a180b808f8c99cc02e5a496d1c2cd4365e39afcfd531837eb02951e990eeeaabda4c3c52a5e6e0c6c370c302ee0cd33a5955b43fcf77147066350a24d914c49f9fc9f872162986074d1d86ace84754bc0fc24840f91bf9d41df303024be5f4d32d6a9e02581f78ef44663ef1a6355d952ab85b6e1465c7b63f260859ae64cab8180b7915560870a0867ae8b96a2110c68ac81fa2439788b028361d29e66dfb419eda4131ac43fd593acb446141ebbbd47115f1c7f89e6784f4ac0de895ef1065a027730eae6345499df691883ded22f2734ec5c959bfd3b82071f63840b350e5a0462dc80a2bd0276ae6c068a11e89d8336e241424975b2768162f0eb780a5f94570efdf0cdc5a0988959fffcd91ac55156880790e49a39c842593116fd78d791a7c3af75bf8c01ad218afa532d4201a1a201cbdeb0befd71f694091ae2bf805179c3d8b22e36b683b17567037de6b7ad2add1164bd8298ae215a464c13ee298cf394f294583e6439b01eecdff897e2439662a52032ed17cc6af3877e1eb94aa2483c5e47723a1d318213c18cabe76bcbb0007042385105b7e3f10d9322710c1c39861c8a02a854ba734de937edb258dfd0034c6aadbe738b343b6a091b56a482bb2c74fd088bc80e0b724f4e1330d3936886d8c07d9c43660ece87e3c868c7dcbfbff0a454308e62aed81e584a65267d7440d1d1f739b10445612541c4ab5eefed763e7af49b7c2962caafe9154af66ff9315af944b24ba9ad515717c18497aef37774cd7c64bead74b71207990f0ac2c9d926e3946609f4a15fd9cfba2f379e015a07f353e0496976c74b2827bc30fcdbe2dade037ec4f62545e00e097a6da6c701bd929d348c479b89125e042a162b63e8b2c05adbcc4f1302a5675efcd096ded89541937dab2b2b9bdf231033ce128a546e53a7d6998fecd39aad764ed7657cd84e49286db8afbc200c2bb90c924ecbc9709a029b9ef49ae9c67f766db5f5541035640d9cdc35d859df802e9fce09d06bcd0d959ef13f85892d64b5de631515f5383c8f9269599eea585bc285eaf60a573ec8d4b46ea4dfdb05a2a90db3a29d3ec5352c9bb83853ccc3a4ad0b1ac07799ad28f44ed09b80007bdd12a5a2510c6f43a770db4c09a1d39bbcc454090e9aea45b380d0396592ac302ffa896fcd2b02e7dac5d1438bc6e3db333911f56ea470593cd544ade9f4ac498c05f794daf6ffa608d355a3e67bd99e14fddd306a4d137de15518e310d2fefa5e8539c1d855c2732e5263f5ea9ad3909e708513ce371c63942831b1d2f4dd7c7e16ac624b63cace52e80950a1019c35b546252d0a634fc0abd8e6d41a8a2a69f2ca64f0bc24528c06ae5fee21dea282210eb304a22f6de292dc02b877e76e4d3b34661dbd3473778cedc47c52433ba6de899fdeeb9a25a88b911cec169d6d39bad65438d18f758c614d1f088f42b841095a525dae3f3528ff183bbd03aacf7437621b5e10f2a926b7aa464c3295a6b2843094ab02055efd3a71659989efae1f7650dbf3503d1dcf6590eb764b3d5f02ea2e98acc180deb4410c697734f202e21740ef69d70b370bc016746039b81bdb92c6e06a5c81e52493b32aa58e56aa779a54134ef3eb17a3c899778006660d1737d43cb3d34764120fbe60104f04ec1195f8362902bea9c6151230cc8886a4ee46a2c62487d79e80b653a3115b465471d3b4f8440a1180907d13f4bd3886f8afa57612f94eab263e672580066a1c4fabdcb6aebe1ebd4524e8d970020c5339eac7f7efd15bfbf73ddfe450f8abf0a58aeceada566466bd33b818a8f53a5fd8752a431022a82b9072bd95271abecf484b92a915ce743df17932aa4e5fcdbff88d9f0638737fb1611be62094b449057c733c881d8a07da35c73a934eefbb253670a50965889266a63493c140880ad04a80b5c9302ee713a9d1b111ef08868d1d91ccf74170b9c8ce67fdd6f3f979850da128bc82702e207ea58dd95f868eff3739bb89cf042770184f0523d5eabd62569e79891e1f5bdf48ecb41abc8aa7448b79fa9cdb0905b0a9c3b11a75d0a53b90006939c0951d977d369e1f24b9fe167a32e3b3c995378dba8295597660b918eb7d2211ca144b52bbe707a377f642c865541f9087275604c984c5e51a9ec1588245a444f5cef04ea9783b4eae87f94b997618b798e66e1f4274cfdf001f92d67bb3e657597658794c8146604b5a344c66b8c9083510c407d9f176ddbe118e393b5aa030b7188e86dd670cd03fa281f46894aa7bf18e58d7764f66be224e620f805dfe60829d1aa40337961d71639e30a42c14dcb38e0ae0a8525b111d893a42cafa7635804f8f58ac18fec6c27805b57064c814d6049f131edcc53218f2d9c849021eba9937de3310a559d1f699d422f73f0c307a9e456eca1e6cc6b178689464973244980032746dfd3be597676a23e87959b9ca4bc63f1862bcad00cfedccd0303f905aeddff1a852867abd56a87bfd0098d24ffdc53f429576502579cfa64c02dfb462a3b4cac8d09e846d2247eb420868273d1db010789f2cfb8d5a60b45dc375e8263a743c01dd4ce07a89e2a932f62167e598caae4e7cc2a4af7fdf5a046cd862c370e0e45a34150eddf866e952242154049a157a236a81117e96ff1bb934fd844f476aea63fd489ac6368aac61386a32bf1b93b677747297de39d8a8a0064c37bd1d20a0e5b12e636b907200054f770f79c893928d87e8d8f5b0b5e638e3daeb4d32e154724c6bb6c0f51b011490304cb3605d36f09d99c687c8314d89425a9b49e0d4aa9bf9598608dc46836741d14288f3578635573dec86578e5b9c53cd51b6f6863efda32ffaa8e502cb50e53533fa52ecb7680ac8cf05197c5058011b1070ddfab89f6050b9d347fbb0d2ef3b8ffa5b15df77bbd28838fd58cc06a8eb48568c8a1ce4d36ae4a18a2b8903cf2b0539da51af7fcafe43ae24e51380eb23671c7f3da2c20bdff8c5cab43037670127b2276b9f5a40e20a436d2cf689fcbe101a446ea0758b5d34bc6c171d48c3461b93079dacf1d1b3239c31ac3a8c498e509ebe8292c46fcbe65f854173aefcf9f80bb396d003fab070f84ed8f77d961899d253bec0bf5b2417ad18b53650504f65caa22bcc8689d684e91776fca21e2c75bcae304163b122d7e1db28be63d3a1926f1adc9c33c4f9f2c6b0704ab392260597962eb4f7ed55cece468128eb7cf3986173cd3e26f2d73e90e86b54e2dba47f745d90e81cab13d9e618a3f6b971245624a65c5ff76a85a48863ae08f5ae1786625363820b64ee965f401f6283fdfe79171d4022c0ec79c9fc361b36d74203ac3eb1e8f29d06cfb1bcf0f7becdb1207948aa345d440813038a78aa542fcf2a732b2ff15f52ae0bdf86f9d076112f034295f6c8569e5aec8351d7a3241897486e2f8b508788f456acf97f11599b1b0c8d0447e62a511b9beecbb0257c3141d5c09ec03de390f91288d76facf4f070b717c410b80f2307bfa0da23bdf93bf0a9b130851f8821ad9f7441adb16201e091538ffd4bb513e588dd0161562e3b7db52973152632ef09589b583a0893cbd31e800376cea51e4af14304033b8ddfb7230f9444b97d783b36bfdcc0c4750098fe214081d7242617c1dad4c9acf92a5f1f3c36ebf8439dabd8aa20d58f17881f47c2be7e65c4fb47942315c41539a2a4006a9f8a3692f0c5db9a7a242f2e9afbfb45e8f608f910e4bbb8740182d439a9998f6d29bf88061a8d1aa7c943c327723b81a92c845eb99c44e839b59a42dc996bad9aaef3d57de0f99a34f456626cd2ba11a1113763cfcd83ffce717b3fee6b8f5a09e4c8351b7a0e4714065ea79599a4f061ee8ecd921e8efb10b26fc0d45c7cd6aba18986416cd1b2c2c2961a0ede91c36bc596da3641ceba8a29a45012632011fe0978934c42dbe978f7392ec0c6c3d919fc405fe3352e52d7622445a5b452c76d18e5c5e8b6906f103c6feb89b2045d3cdaf754654e3f70bda90cc998c5f0d9c752046603ab815a6f26348aae07f33b206ee133a036c62d7b58157b3ece579c90347c4098d47f8a47ae16f06a3c0048dd9a628e05bfb9fb8887478cf59f80b980c518011460de2f8d002a82014bbf0a039e1ffc290c2b40ea03f5ec70ae72109662b587c9061776b794ad294eca26e36231811aa3c2ff4b9f9f38aad3f085cce1fe6084987c969422c45f8b62aa9795de5868c0bb7387848b49ca687b47270880932d92eb5b4ab3b3b0dc885313f8888ecb84270b2281ef972582274c607223dbd36ded39077e8825a87964f1c03d748fe86568b20e6b541f279a4aaf120ffd63f4a6062ace4ecb2d53566d307e02d29544252a2260a59ed904a075019a3cb4f2dbb9d278bf0925625e02bbb5d6dfb9d2a6070893c1fe147285a12c577233215852e261124fcbecbee148e068cf72d119a928c199ec5cad3267b6e6cb7d75f383099f7134c32bb144618b0ca42d45232297a00670cad44a28eb54a2b03a8bcc545f7c606ffb867fcbbb88025cc40ea4194510e51490107aa370b880008e9e10379634e5383afd0f95aa6b39a669edbd9ced6de2ffd8028503a7bf655416f1dfd5a1991179a86c9ed0819769d9c8461407ff30cde9e8d355a72edd56472a99211a3b3e7fe5da6c47541c59fbe45061115b8b852a344cb67013ca01c77d3930c48ecd7f7d07d843407e3d848ba0bfa6fb90c024f5a174a3a997cd100c825116d48ea261c9fb5060c3869659939df3c38805dde198881907fc93ac04350a561f85f67899b6e678d7b9351e2939d0d583410bb137d4cc6cd8676aef4b571b610c3d5e694c2d3840911e4f4579952905af2d7343e35a971be2629f7b624fd7106431b1f435cad4ab243cb286f9f11fdd15c0a484c86782d8e7cc3bf124a9d091cb9d6ffcbdc469edf47adc628f9e5045ba98bd36eea77f37e57248afca48cf29944fb507f05ab4619ed015ee08a099ab673f295dd2c20784d2873318a400d94a01b90e8b3df2efb5b976e762099a7dc718508a6886f8dd0cdb11d877bed7e3bba9e5f6ee30f7b8834502165b77c7aabe3a1d816e507ebd5d1e0e6a59044d6d3f5448d1bbae19f589c7629382b557bb69a1ea3e33f61f90c0a3f4ed3009c29aca9c0f6f2fe9e2b4cb3cc49354b4731127b876015671861ffa7aa75c5c6a8a95822381f7595c44284c67d5f33008b56740ac2e9495440edde5a131ea943d11f0579bc7cade23134201731673a67e5c74bcf7b8bddeedcaf17a6d54b2c5ce689db3f6b422158c7bd1389fcc429c8296b7e50b11860b7fbaea0c62d963c46b2b1899868d6d1090cf2ac6fe695556283362ff6e089452f51aaea286dc5890d6182ab339263a137ef7e3e32db2c57ef1aea83b5d28207137245f8046b4871f79cf8df7c8e9be15567021d1db792513d6353fb23c57229b410254e4fe38a2dc6b701c77069530f4e1191c9788e69fb0a1df9e6817dc799db85e260c82468ae9da8489e46e98bc09a24063bc5c8b37c67ec1f335c4895b8ae8d7212952294a5ff48b0cf1ba51d8200dfb8896c1f64fbd27339a5f0f7a8660fc387f6cc05d5d762b7a2f7233d98593daf08e727dc6a4f516286d77b50010e166b6b0801cbe6e5b7457b0bdac1dc561f74b843aed6c5cde3e8dc94076a875e99466437205d7352ead681b214894f908709fed3c0e3ffb22f00c846e7420a7bda94ab89f1ea044df8e05ae36a1fb44513534660d4c031fbeedbbcff06c8126b641fa229d0d0271f000b3880515c33f60b6092f007127ac5d256fa630a40b0161a147b111ebcfba9b5bc3afc972b7d0cd9a5e6cabdb8a3357285a57ec1ab7a84de505bcc43f30a552ce5810231b7aded1705292434267eebe9e85b30da8ab801e97859a7261a0e472465e71212fddbeeba1c041e085827d508eb9cfb9fa429cbfcd84cb433bbe7810e4215836d8318447ecc7afe27ebc24fe8ee276358049c023db93dc601d1cba57069c64c067c0a28a42d776235254224e42b2a53128b2a8bf7313deb96bd69e739d1841d4968451e9f3dddae05f53dd3f9a0aa1deb7f87e8571e971c1e8e98e0410de1f8c9132d972d17cc0cf6ae08d682ce89a5832e9e9036191a94f1754c3c12d59b3c68439305849aba37ee9420bd97ccc11c23b1f18b83b6aab05697b160e4e21b847f59378e49bb1b7c1e4be265bb922f967d1288548a907754d8c89cf1064ad2338049fd001e19285a33a879a8d745e5497555fa4d1622af4aed22e688dff218903371b7810a0ba8bfa1f2d2717a400a6b757ca728a2851773a13797944aaf0031f8997ec42033bc4b780dd0c0581b27b52a659326ad720cdebe04242b27ce08334a43c94f7f4ff067205c8222fb3799ae27faba382ae9fe289d5775a4486ab00921ceb5a53e39b19d9631f5a7de5ac7f045f84c100b85ee3d5b0a7635d7941b26bcaa85509af3fa3886aa3dc69bb1eb88c970c1c94a28c07934aaa7557740907876f57151e48cab9c9b64b6b7512c38edc205e453940d33d25b3f62906007bee8cb36aa510de30be8783a8127e324b6f5e66d56cd63f73806cb374e7bb877c89739d0e5360a1e3ee8bb171d607c769d61b467c9f5536448927b41eccf2018dbd9c8ef86bb7b2333d94d47b7d6f8f9951128cc872d68d8c7e5c1a6a4b676b915991fedb9081d7485ac1126314ccf62f823e13b3a4c900d10a4dc201a39c946ff0e7132d70ea2736cecdf74b03591e7aa008520df9baf5d4120e74b1104955b269aeb4130c8343e4bed22ec0382fb131b6a37031ead37750743aaadd01c6d79dd7299b461f3e1a6b923e08ee07d64f719cc9b1b192a2167427041a5aa52c211ae43f4b45048b851bbfe18f03d9c4544803e32a7dd2769da07d8b0d90555be6c911632fd1ea6c6873a65099e964593fe6b00a371c6aeab5c94c9cdf609bf2289f03667aee9aabead8ff6e1fe8a66dfa840f01b8cb964e5676dfd95369edd327b2e805da6aceb5812772a029e25d4c7a290d32c6b5e8d8ef5651d826497ab3acc6cbde8cb78738d10821902007a505d98de595f0b55fe4cf485aa8ea2486b2e51c828a479a569ed4b12a20705f2962d3d099892cd114033c1fb915a1740f3361190a34ee0be065e64d3a784a3b15b9b59ed2383bdf8394bcc9d6eab2130d7a76d3152ca65dd90530b6c04afbbe558a88e923ee5fbb4f51e288021bb9cfe5c53af138e2bb27240e122c40405612212a070b2314ff742b76a8e0f647425230d3ac6cc51d8d82c52a323361df1053a3e80c068aeb00acea9a671123967fd0110a560dd2c5de5aed0d667096bd5faa37587f242704b174e7b662fda19cd56dbe88127c2e04c6a221223cca8942773245ae8f342d784625c9069c89e925a5829cfba4a4b557d2d756ef141f005a1da1e98be23fb1e391c6d960c43c5464ad33504dae59e79496a9a4039ac0b23dc9113e1d0b5ca089f5f06563da99a2f9e6d3bef8d75e08770ded20c286de10b2a1950dba9ec0e0ad41bd653da208cd2aa3b5f501a064d6a02c07183b31fd264886601f5a536bc82ee844ee548f926a139dbd7adac6860213b4b7bbbd6ba908f5b6bc6c4542d8eadafe05b54078a7079c289b9987ba89501b4fd5cfe96bbc2d5dec73672c95ac13ea47593695e6139451658817ecf44cdac817e7ce3a31889d3d1cc2b125f8b51c68c8125be1a691ac72ce2ab5d5699f99887e877af6a57f7a2fd5adc3f881a630d889d5b30fb89223fc23234f3477f7ccee0e3adcc4f773b433a607a8e8c5d003b3f72d6d1dc54681c19e2aa5c5081b4b804d75a9a1eac1e7db0b4567df0f167e5e31539f318ec8de689d5dc691621eb4ae5f9564713b12c0e24de8d70a40de0380e26295240d9fe22ae2d06a41df61a07e9e4367fe0ac55e41b79dbe9e8a63189fc4547b7426277ed2cfc75a46c07a299e110bdeb427301e204890a1b0897865e111c6c7e2ba1409044ebf7fee8a71fe45b77739e8cab61ad21090f9c15778d296bd45e98f56b923e3e675ac2570e2a2722a163232041105f4a78ccea93408b6a25cdb6aef06a9f88a4694ae8b64212e94bb94099268d4b8ad9e1e153b8946f766b899808f5bbc1f59abf7c5072707418262985e20f31cefb9ee014814c488344fec60b0c30fd2c4566939c329b5b6eeb8a82b1f4d5f114976967a332a0385cccac9991d3aa7fd6952e397696151f1a750cb6f57ef216f663873c8a2db156b9486638dd635600cd28d94afce246df662bd21fb5c25a01ed3e71bb1171e6b7114a4852c5fc25090b0c006ea9c085187937ca5f7abf6d3cee441db4407815ed05f7a916a2cdbb485e4d054919c5bc9a0988c629676d82ccfc4ee6722607f2270019f652fec3fa24e54c8f90869199473f0c1d5a2eb518ea85fdcefdb168da1a0849701907d0e0719b3de1144f93eb05973d307fec839f1027d8d84fb9bbfbe919231042a4407ab3025e3c61049398d3ec5a52b5bf2f01f2de47aadbf81291d3f4f4cde23d25036aeb7f83f501d4a8dee04ed3405c0da50ad6ddfafd2f89c9e601b860eeeb5f967eb135db03ac72385c65700141c15a35daadf7230c2a6afa1f95ca2d1b4c5b4a541d14f3913453c6440a268c0fc90b1a4fe8bbce02c511be5e6c7dbfbd131611d0c54b1527d22eca9a7055f1f2bcea27b0f95c2f48ebbb8a7796b4c40fafaab464e7859159d9f677860cff17fea32bfe0685357cebfb1dc841bd3b26797394b9ed3b6f0512a00c00f7b4f8352259cb1ada34d1727ce3d84db4f9b459d7cb601ed363929703d4dc3b7c5c78162e02db6523003e514d8d4784054a8bc78a5e0efe68f76bd48df97d20e93d44f05ff450355fa77093fd6c6f477e82df5817e16958317a57a62db32299042f1a97c77b455e5a0086f8888c0eb67713ce7e5db41dedc7eb57087bbb16539db5da0099d64ad426b0ff426b441f63bb896472b92a439d033da7a5122f73f8cb839d772c200bbdee6bd17540d1ac3dbc9e04c9e95e2747ce7eedea4011aa14b7bcf419d8a61895f519485ab5da3abc5019656df58a329e0bf12fbf7ee52fbf1b21c16c9dbd5e073ecb797986b7b8d7369b60a1b78e0216097995fcc487e9db10323f614a0b9333248af3bdf04e11a28a3ba536dd0c92f681947c5a1be2a8886d8905eab3f0fdf0763798e4da943e8b21e57ba9fb0f8100d5280dc0b5b060a9bf0a7c2565266729821b2b98f0c0be1b2ffb8d915ba0dce7534760d9160ddd741fc60dcb4a2db7da283a1b3847feabe321d81bd21d8696bb64f4d9d46b88ba9bc35a0f02e2fdaa36e5a4be00e82a46e14af8df92057a54d64b02244ab5cdd279847026c5a51fdaf879ba46124b8c88ae7b1cd61b098a0c1d60dae71dbf099a17baed38258d30f4ec699fcbfe2c884f274cbf7954a62629cf47f158b468d3b1c8e5a1741e580ab47977490141d184127354622e06874647256c3bc163c6ea784f26097c16c4137410872c04b864bb0e8a16d976f56b087a56261bbf71f077aace11fd99186ff899326eb55b627d856994986f1f9bcc6b0ecbdd8918bbe05a141725684d7325a14e00fdb711f5fb76898eff4944b100dc24c6a0e8146a16b7ce193a16da5ed9fb3a860ce019a754668364aff6c1d3ebe5b80a4f268e8020e560f991e7c127d9263da67335b42df3d30cf704f8993245efccfb5984f1487d62a6ca1ea7f7849ef021a598dcf0c332781377707dcb4c93613f01cad4dead88541302cea278624dee64ec7f9e62c7681ee94d944daf84a40e9ac4c101c22990507e91fd8c55cc213863bbdc194ec3d9faeddfd86c747d0e4c69dba52c916642031d2557ba2762c74c75d501c119fb6b6cdca601df9442f68057f3dff54bc7027f4bd8bdbfb8830af6f1a33777e172df7f2f3ef584e83b63052e48522d2f88f47139acbd861456f3dc35ff2116f9b68af3e2eafc42e7b40c6257f64fe88e8c0e2a58f88a1785d8809757eee2809f20fec9bd1b03772f1f83beaa41d6b71c21e858c309edcb0ae6090998ac54ba3201c223b08c3e8b486fe53fa048805ae0c3081dce8e54f14405c09af9e37987956c7ee213ca79f8119b7e40be65daf9eeb082780fbb8b2f88195d615d5cea189bfb185e7512a67d41994602ba82b26d7c68e977e58901b244fe328a0ce3c6819aad34ecff9b79e9fa4674a4a65889d0300f192b83cbf0c56f11a5519717210f3156b3c02848131f95eab089305393272a1f241b68fe2388b9157bd764c8ed02d850c196cd346f4136e5a0f48bed71f571a9a6be42b7cbed7f12f3b3321ef341d36cdd967e7f154afc492b9a28b5d5c62c92e7f62b08786153dae17e89f168828d2439b497ad429335b90b7e4a511aa097b48c4e04223e4d13f46945bf1ec999baade5819031d8bb5668462431a78ca0d984a3e2fbf8845246e660d0abd257ce781e41e607970e2a494e268c74bfd54ca2b624418886947c475682ef6bec919d888f736fa00d801c96cd02ed5ca958d641c6c65973563723b59499446e529109517a10b9e5fd8314e65a2655042c561e99bb7d648747c71bec9a203c2e4c6b2cf23a7874952009cf1c5bb2f2e28ee533ad538c93b3802b44e893999a04907fee0f92d2434e129ba3ade199471583584475016e16ddabff99e8fb060fee9d4d170b23dec43ba6dbeec87b9320d345d452ac2819da708172474ae3a155c680827e5b0790f9216deb0879ba987815b2fa71382a2a4dbf7beaeb75dba7c47c2a21c3a38e0362473e9f329a633bfcd323a137658c4f15fe0f12ca7522b508d723ef5ce2269b5a8befc4259560e598be33823c0892dac76de57ce98643c704d972b2db600297c01a977ce19282add97f5e1bc17a7342feddefdda36d77a20d04d04b03bfcee96929d42b608e559f84ce62c5e9331089765ae20807fe4b3d62ab0bf51afab9597e6a06c249a1ce0e41e50221ab1d4536400259ec2b4829f6568640d84726892ce659ba4c49ebd0f49be29fc35b278a58e1ac38081266e7359968704a46cbeae109f0d650b4ab84f45492bdf5ca2bb2e65e5f9b43095e27d660e5be88eb440710cd57ef3eb8197b9772b86e7cb784a1a5a68ebb4a51cc2093893321895c01105f045e3764a69b4696d60dac6882025d361e0a5e5ea30889dc4e0af4e5772b35a8790678e2e0794fea5664a944d3fa1286ca410d10b52cfa2e8db93e4882312194a9c163974addf1d6ed2e48b0f1efffe84e8bc4030f3c4e5e4c585c12ac4399b6d07234f1c5606fe7490160f92ca8b855ffa30764cd6998673507dde658db6413813592bdc8099b5ec770c16ad7ef74f4b00cce4f86b0c4b099bd572dda07a33b295e75f9f02de820d193112952cd1f4c62598b35473cdc78baaec8cc711a32676ec3221e6af9201d604268772173c0c625b0ee0a25f5e2632102400e0245057eabd4324c2f3971a273ce1843db922c2c85901c1de2fa58ebf946e52070278df1b3e42f02d44fa5fafef9aaabab05478cae829cd9fb7f54634a7d0ec770a813ee4a1bc28ad5e62532de7c4b83be82de8ce4ba46b26a54e47ad09afe9f1a06aa7c1f10a4d1cff0a6418e4eb13678ecc70c709b89f90bb2c6e27cd2dcf64c8e12ef45c4b04984740d2e80870af83e5f4376d77f571a440256c58ba6aecabac39b952736f6c85282376625e363de9cea4a533c0eec5947339876408ac4d8f690e431f9b5e8a580b5753b89e18d0d6b4ee8e3495d7a9e96a7f1ef1e3a6e9dac08ed0bd6705d819ef06a5dae0fee289e803c81e5703f4ca08cc924c22e5594a3a8bcd75abe18987a37b27fd97e27884342411e70ce484a616de42130060c6009fb76eb36d7b6d0a59d02d806bd92bd7a67372418aa3a917805a9317b92f2ded97af972cfc6a589b5f93d7c6571dbff36a2de5beba357730880c0ad6c6832e3573f092cdd88a4f6e2a226783fcd62b7332c6c9d2b0e8211b5671d9f62b96b11a997abba67d927ebf623f75b1042196e11db611a54a52b880d4b481ee4ee518a8c800b3b44412521bf1e49f3282495ecd07a46a078d9ac8b4a21bb362db64431ab05c08db50c958117d25e8437c0155fd5d8d3a14f32ac0499cdf6c33702c6474c992f423f9a087ba651cf6f98314a96ae9425fa09478215e8c5e24afbc90cbe243028e3604c07891561a3fd6c27e75098e7e35ec4804ff7b063e91b97a2392f627a35c5693142975b25049dffdc7e80a27a231376992c688e95863c78c3d01adafe1c69195b67c025b7e3c9abd5dbdc117f183be1aa34f6568e1552c9301871155105f91042c6fc2f0aa5e38862b2ab5fad8cc732d383cfae3829a7adb0ab24975502e41987777d446e911f7041747ec0a453a1040f23314fb688010cdebf511e9903630421121f62615829a3ec29a69c5d66bcd8d86aa6a834d6c3808912c2743004e72a0610148bcc376647178693a6c14470e9b25654d81601a8fcaef384720bb4cbfc704f009e3eab7560823d4f9506b003fd413cc154c71d645326e95f0402d1676ad3a9c645b8476e1c4e4b5d85c239cc31de41022f6ae103d46a6ad46506f997d7f0ff48f56cbb6bfec8cfe16ce8be2a60a5ee37b3b6e35df95f9129aeec1865241c13f82416d7b39c3339ffab11c94d9a242c6039dd518e426b5d913a190d07018e82618e44a6131fd8d63ad05e0adb51475e0bac8e5cbe291e20282a9f6e94b331868f938153017144fdef0e4195148f1543fb52c4a660a9a963b35200fa6c65fd4e8ffebc53ed7da8c5288bc300d1fe53c7a4405e1821272c4917ddefbc3162e982d0ebe2fd682cf3ad29be479e82a604e88d76c8f4ffd74706ac7a4a32700b6ede0d20e0e7e22f66c1ed362dbdb61b9aa9193faa8038c0c47318447f2ec51ca11fdf48b2fc9390b4c66b7eb3d06cdf0ae6bdbb820e14e6b84fa66626be7e0af283aa213aab32deef1805d3b2371a1f821364739714a31a5a32d9603818d3d48cf69da03fc466dd950a2cf1497cd733039c1e57315d4d699fde4012b56fbda27f7f9ea7dd23b7149f12df1d42ea0eaf4f877463809e9db0a066308f73a72e953321c076522569ad40e1d2f1af8be49b3dfc144d0dac1c51ae1c5bd8dc44311134f4953a95b63c013bc80658e98e254e460dec36b00f33c08cde55a43558939fce7eef859b65b21ea37a89c0e269ec5fbf2e87dbfb5b6c6ded68dca787761ace2fb26070c457d4bf6c9aa8963adbeb8c7b243c812cf4a7404b8b8d72457f664059ff84038f5e4dade6dfd267d9b405581098643f06e5dfb510f30f04a6309bf0f1891219b4b0ede52204e837fbc60c3ffc2abdeb5b38606a480630e598d0765363a26c6558948c1b38e9b8906b4e3d2cc248a596be25c8fd2f018eb38b2b27dc0cca1f700a62b2456d65520c6dafbebca29e0d14476f1f5b18149abd96b6635ac8bfca5e432078dffe29654822e5ffa96319bfb63b614a499e38c3178cbe1bccc2a4b18c3711addae90e25aba8f78117af000d92c0626c6b90aabe201e84c3ab74e91d2ead099c18b298264011c66942ad1a4ae3b3be93cbde472042d359c176d50ccae3d50c0b5f0bec2382496cb92de3d7865c75447d80e95b216456b6c3cc0422310c50370927b007d61a2b9e9b6aeee2dd15a87ba89970f2418e27fd5b311c94cc01b686c729206ed3c8a505df915ebc75d48a296a03a811ba80e3147d6bb4e9267da5deccbf3aa5f51d03bc594863361e8f9ce657f74bc614ee6b407cdd9cb5b2bc7b17353c95a8d678e47ec357005ac8e0bc69a9940de226d2ad3292059459558caeaee0ca6367574b594725a39c3f865317076b874c7e409d66a174b65f501b31a6cc76579ba4546b329710c0bda932e98f69709eb447ea8fe81bab1b3e426fff088c09a2df7f520255bd865852a6cc295f316bbd5c06493381bf74896b4456870b82edb706fe6e2c0b1aae7135d4e9caef83be5521020709138905dcded85e626acae32a55192bca69394a002bede0167784687afcc4368a4ba4a2d92b2ba80aff1b90ccd494a521a1c33fdc183a1ed5b5abf4195e5b080dcd01800995caba400751a70aac271121ab3b8f46b8efc8336b933c26fbc790c5431954295c44f3c8ef5d123929667f94aeb81150c7f5eb8c9a1245e37d951de5c5c5a4616009dcbb33c2da3b97268447f5a383b870b42db94b59e0212d5852c22a96bbced81d01e28e97ae4876d279c8cb0daf4a72c4c41ba0cdfe7a9f18483a4d156ffbd72e7436b3e0bd51c4fbc0962ee9c967ebd1349b581d5193bbd7e577847404ff62cceb2c2c97f88a7fa9b899c988778ea6f669e8bdeff3b94babeea8c559f63d357bbe22f2f2013f2b110f1d5687f4c72ad6a0e9018238ca5e1df69ffc1274a220cce87b56636485a0f04cb1183ff0063670b95a1329ad781670a8b79438fb2cf97f33ff87165f6e1b231caaa758c2a71f4896236ea40d46bb605eaa86d6a428641191a4364fdde0cdd92b220885e1ec60632f4d65f3e24a37cee3440e68443b5825b4ed775fc5d55a84cb489ed4f19ecb6bd38d5cde25c9cca6b70de027cf9a9807d6d7c3d3a2ebd81fe86c8ad2760a545b379d65c36a2b76407883bb96adca93217ea40ab7f14da70eed36b46aa8c5b093205f9eb2b4d4ace365cae28eb1f24962773cd9e930360d0dd7aece338a452b52073a24a77b5bd2db94531951201a66c7d8c77b565671461b36d9b1ae96d0a3c8ba46d7a8da49089718d0d3d7c874b371e2e0eefc301ed7ca444486cce42e56e23b81584a0907bc8a46c76462fe4ad96fc3ccd6a4b39304138894638e01e78c81800843b053aadb22ddd3c19570b409201a822fd2c100a8d350d41f51491a3b194d0c316f1b848d03a5c5e8198731092f1c82f186846506b4c39904083647ab5ccd6ea94b99ddc0b11ee987d9db126d16ee371146554ceefae8466a882b1f1de7fdda19c126e668ef2ad1336435b48197614dfc14eff163436b37c49af453befbeb44e9a7fd9468dd5aaf11536111d1b46e1926aae942884e1b35faa425d942ec92869b8222da44f91b3bb73381a49b4da501f7dec344e8d1eb597bffe1e72f8dde246a93783a41ede408ef08ab951bf13f05a52a990492f1efe883d19c89ef08aeafbd6d335fed1f5fc7edcc6a3a1fc2796f726ae40da0039d62898056cef920b93c74efc95d38863626da853a4cea66b08df7f8ed8b66e6b77053781ee03e256a5c29391e33343b8cfa7cc07b41dc3b8df5ceb7c82efb68e75b9c67c8f96d5093b0702f353b08559bd51be695ec67a071c77c6cf9235de9c39e5405a7edb97916b66860d9e0f9fbbb2ab2fc411f1b05bc2adf7593630eba25b5a2af414f164723902f501d2bea332c537f91680c6caeb795751d2f2d28163f45985b831133bcc7a6f2f92227d8853242e3985780977c527e70ef4df4030d5d268705e97d47a6eb0828990df05c829916939c0bd6ca354593396801ca419f4a83e53123d7c547143910b7fc80294a407345c58178bc0e91425dd0d44442712b7923e19b5f8b0aad4385b39e8175bcec0855b6ab26b99f46adf21007ae6ad8792385ff1c0b4cd72c2d04e243a40e61a2d38bc4ce3f924d42ed7c4cd108adbd8e3b8fb881a9de57ca7040e756c24d91a658c9503da92e93f63827810dad8121ab8ce0af46a9022d4ed4f14bb849e0ee85d260f444ab580777e3d7f444a5279f021bf4888dbdb160dea3541cbb721a18dc2466933dc614ed41a96c72fdca3ad1d143b3892bc034713c938b1e3061f9d75f7b646a32fe8eadc150000012bc6bf7897f25bc2c89c13ca12880214854807b7b0d12a8938ec5ab5d675c57a2ade33ea25c49646a48fa82ba6500c93d21b11675a1176d83a5574060fe89ecd4625340945d1187f3b09fc0ae827fcaa79bee429cc647858675cce4324cecb731a144a8ec411ee604cc8838620a94f2f9008bece558658b542064e0cdd5fd723f0ba45395fedcab11e6db556172de3c4e30242b4f8e0f7ffecdfcaf09fda1d6ec309a83680ef985c4a5800a1d2fc3f64ef662da80aa5127343c9512309e23d77add5018c4ae34bcce408d36d84b2e53e511f037abc46d2c0cafe651514cd097776912d6528e81a0dd982c004fdabec5bf00f1b2506309e46bf086e06275eaf77edbafe9569a1c38d7d78ab841a1eca1adae80d7f2f4f13e2d75af367a0b42fc389c3f3039654441e581942a10768a2a734fcbd21a3fdc7c3df0e6cc8dd9892329d3f474138576485dae14cad49f4b85b16e1cfaf95e13488a69673b1f822bb8d5d6132b184a6e105381d635c347661455f8dcb631869e7f65a06f1bd52327bd40942e15309f68d84befa937bf564278fc37572d32b326de17cdead88392d2764aeb44fdb3ace15129b23c0a74a7708dcf644cf55e6bd505f863b034008b8952a89b19be79e875b76e8b9f838fb9ba81e7a48904d321433bc7b1e689b9e0fa6f688d01808bb3f2d10dc450515e4ef4586c0813f5589ca22acebb42509953f05e6123c512446a59ac5a619065e277862a86d89f2fadf4bca34608271e1d45b16406207f9bd67556b4a682af0710df9bdfa98e7f872be15490a956ca465a142570073ee82c20def557b4c7b3e4838d2e41f36e30c382600e83f0c534da690dc4976150f620e8b615527c47b7776cd41183eb6c8310f55944cdf6ece7eaee5004dae560d948ef452b0608c93233b47e76b946716ca8896612d957e077a68d5d789e6ef9dc07cd5aaedb1696a6a7cfac6060f0d3ba1cd6db17772e5b5c25618c4c9639f668d50aa60f674d720b45b6b9e20eeb84d349c1c5f261e47d5c268f884f86d474d6f35a9a3b0f6207c368dc6252fbb65b3642bfae10789da99e4e853b2f65ff373ba0ed0ab872e5c5dc5486f41c4cdf85aba02ed744ff238ae360b57d09fdf02f4d4ee47866372644127f6e9c7c276764fd238ac164b57d4971f0a5a3b5f8d5a75496ce5d136d0561fb3441f08ba63c459756626a319c2ac769eb0e8e7d39eda085053d0436166c4cf278689704bf0218f244c03cc2443c563c6bed6638a890fc28b5787de0bd3ac2e437c367e2e70a82382cf7c67c5db7072f917183a5b8e38b6e71c29817fb30447022890973aa2a15f26d2570c0d424bf4cdbf3c4c1ce880a4dfe6ffd536de8499d4a0a8fc13676d321028099914ca51c2d03b05b712337aa885258361c01bd868b58c9394a8b9b7c0358ecc8bcea2c73870f38cd4a9ff37f04eb25a9d1505b7fc85fec78c3aef616521501804bf77813960f4db413ce2e391c4cb17632870017450ef6ad5b46e0d0213b1f3992d73a8a52e6d244334f434a38d6ea5b828a761c31b04591c5eb6f5b44dbc27a6e23fbb594d24ba515f1e3d68cabf72231e8c95a1f2458c5eb1d70f518576a3bab31538833b11288cb51e00d244cb4da4da151f684552759c36398da9fecd5933caad53e46fd0b2a33b406dbbbb39228a9d48ffb03852c2f964a69f65b816bf2cc29e2e04e7b26dae414f447849c21c2a5b5a18ef24e3958f4013e3b049af077ddd807e3a6a6599370258608b507b9467904c9665e291caf202cb491c68df26f44754a0d91d72350b6e3d7df039b7106f83bc82f026a022c8856700e0a3dd409171a47d472be6f85d521d0d27815b3d42201352dd23c60dea15b095dfd01392883ee388d6c6ad6f22e7d413d344131d41da6f599945be9103119d3342538c2da5480f53e00f2c46edaff94fac624b97b864d7c61280a0f6d5f3759cb763d28d2d9a4b64fc4fca0fc6d50097d308cfcfeaa4a5c9d8bc3ffb40686425f029174f0c5d3a433e5574a14393f6f1939d605352f4a93909821d67c69a78b129b1e6fbcdbfa355b65524a3971b51e08f4cf0dd259bd8a889401cefdc0aa7dee89a74cab788df677f36b50eb1f9d514998e42b5c2a652561f2bc023ecc0aae7be052d2a4ea550ebdbcf307691123ab9ac64399049a8de5c3d21839dccd6bc0a792e11b71174370236493f0df41bab0acc13991286e3c69c6284792c799d9b3258bf7189204c6b702fad6ce96d7543648f3870a3897cd586c52e4761731e2bdb8683019db49c0ec7af625414be599cf34425663a068b7fa7e227168c051a995502d3de8c70e4a3f944e01c369070e7e3e5d6b936d3c4dbfbfd411ec1e72b178c30b9e6c1829cf6c7a03ff316c4e12481e2d3d55a65d1c7b4ef5fd5da45c9840eab368db9a1d62c10e2d13f1cb9ee006a77889d925e80ca411953b0b2eb5da479fe9ea031af428ad7980fdc641e88a31b111d7f8788f76cf8045b623bf69bbf68da0ee4240bd771d9b8cc958e12bb5d37805b86d005a88661b500ac61402d00eb485ceb0901343a032a5bca765b30dc11a703190b57b53a0ce6fa53e5e5bee68c02ab3e7b2512719a52cfe280be93b59273545a72986599c080d4eb39c380705f9c739e77f8dea5117ec10c42bcba8a5375bd33fed81ba80295d603fa51486fb2f7965b4a29534a0197076e0729071703ecc30f7c0136c9af95b7c0b14b35f30149fd22c64823ad957e371741f4c7c67eaf426c582b9d5936461528179be816658388784431d18fa511c14471f46c6ff4fa76cf80417f2817dc794684fee4419e11a2b73e4aba1f07bd5b72bd833938411de8e70481402050cff5d929b9dec11c24b212a9ac576160bb1b91ca419137ea88d878ea6ca20b2aca1f09c1fe0d5d502e2897171da14f211feeb7afc2280af609edc384eb9f298927d0a75056f4134f22102c95ba20d813256a30b9fe3598b82974c1d9c2951f8eee0f045d685c6a4458b0ef829a33d0920bca4d21c8a7566b47a37f102c9544a9984f012e2789dd5c918b65e8f51719e166c1c8002e2789c52e8f8b7a54229428d8b063857c321f2de473fd41a21d4f9484543c8994d030fea231ae4fe1fa17d7455ab8f70e91cbc1276c18f209f98494380242d18ec8e5a0d3dcb0636551b022253847cce6bb9c040b4c4429142536ec5856e08cf1ea58abdbb1e249e472b1b0a0040b53c4e326d0bc9c13861697037922210e039a9f28656b903136e86089a2b02c33bafea2d70db27043114c8789859acb49c668b95c4e3286cf15b9e249948a302e7265de312452827380ae08250c2b72f9a3806143518a95640c55922bf4e45441c905c0e51c2aa42ee72091103ff14c153c3757e46272fd453b2e82b989ab68821151cff56f2109c28f06a23b5487b6a88b22b9feb426487777a31cf47a3baf67e20cc8880d413cae9debdd55ca087239c8afeba09c9b72d0855c0dd43da163c51388491021ddab3661bbf93c4c1f2e96074020b3c2be6af0841b8a6013a03d2c09c9c0b698f0badd4d3c8178bce7e6fa7736f1d4d9dc1c075de77a0ba4aa367685ba01e1a4ae3f4be4ba410b442e207c70d0d9c7b6c08622973f8d895c2257f7cf6d171b185c3029a594523e318d1e4f2486f1af60e7841d5161259d5956595180711557a41bd28bd4433a42725d7f9211528bc443c221e55cff2857562b2b5f59adac5c6e375c59cd8e42c618a38c1a16563eb7b262195fa51871d033c0da58516cac94554a13cbd2df97e5067afb5b317251eaca8a653a1b3fb0c1b291f38971fdbf1fef07d7df63d5f8410d1db0a9b26a20b94e038ceb4fc38765fa493ab8fe241c92147c9a29d28a89eb13057c4a594598c9829516d894550d29d8241ae235562cc3895e50c3c8154141e3c532dab7ac5866fb1327ae93562c33df3717682c96e9dfa4703d6595628465a610ce31059b660a18f44f59cdd44cb914a455465a8986b829c5c6c117b029058a94151429b12adc9595135efd141bf9fe2950c8552c5ce99a58ed862babeb95c61256b7458a9458cb6a65455a51eda3f1ca7ea65866ceaf5e44710e29515c709db990c296e21c42b61fcce0a62e758d2595d890b4bafe35706a2071d37cd28ab422adb24c3504dc5ac68a8b514697f2c54728bf516b2ca3ddcd5c2a954aa5582a81e068646d977c4aa20f47a5d2c997e14418cf5811c6a7beb8e05306639817cb7c2cd33783c2f5cfbae053e6ca5c61f88fcb4d61d6bafe61a6e3e3a630c3b96e0ab3d5f5f7277e6ee44ccaf5276e587b52ae1337ac4b629cb4e947e3679e100900a3f70ececaa3e3a0534a698b725dc332eef429bd71507429cd329a514a29a59f640648ee002e27a1a2757b037547a58ecf061d7abc28102db73f7339f8ca5cd777b2d72a7ea1ad2baede7c2c0e10fdc99f7c1d6089cf3306e0c2cf8ce879c68e5ef4e4e92bc2f8d39583f52605967e5857b71aa9373be8673a3e9cc38f60c35a5b3a2e4780cdbafaeaba162283e74f91cd554510255056154196b88983b003d5f5efeadc71e2b1cd5adc4b2fece112316fd7237eaa33fe15c9ca4db42b0b74f29c57e4e4b9a65346592b11079d7e585f4b622aa6a182c010e4fa579f28e9ca4d6125723dac2d236eaa47dce4426aab48ddb1617dd55a635f8797dcc43e88b8897ef2c18e013e633fae89a7af267e15e6a0eb8835d72b0f1636cc60d7bb27fb71535337c5506e7aedb8c9e5266f205aeecb154b10b9add37213cf1013d2c26637ec1a1f505ad8ec9565f55579a830e4fa57989b46d75fabd184f8e905567bc44865b0baa4c660543872430d75438523dc500be273fdb3b0ae6ecfaab403cb0ed2fa8a27caaaad97ebf5aa3118caa7e6facff5cf70e249f4fe192b5bc593e80bd9312c75d4ed584d3c098061fceb0f6aaea829d557e7d32971d3cc62f665446c585fd507477d5d7fb087eef8691e711887e9968e9b5ca6f9b3a7878767674747c77372261224376c1b9bfaaaaf5a6f1cec5f1919a1401e031659206149165974c1c510701046165984114626856c959436ad1d22ac21383aab66958a59c7037a60198fa592118d8412a640cd1ca1b1e03543db0966b972a4034b03296e019c7976cc212facdf9061bdf2664117d79b6019fcbd55397687d9363fe249f34f5569c44d51a552cdc92de12e7671bb5ea8203c1ddbf9a18129b9fe5c5dae964da90482239fb131220efa4ae56096c92fbb19fdfaedd5aeddcd953b7be6715d90ec766833ebdaddb53bab2f29a5557ee609116b880b2af241bc0ddb5ac9679bb061bb6e6ebcda051bbf6fa0df2e07bdba833b9ee32d6739f81feb4d91edb9d809d13565dbd552d832222b371971537c97abdb611ad4e2ae1512742be0eecc0afe5e90e0c6d85d7b576ba5747e911569c26ecffd48d3a66ae5a0919b2399103e32cad404f26eb19092a6ac40e5112bc448a594727e943be04020909be229fb92d7284deea7d20ee6e1010e96ec0d2cc8e0c4c183fc521c8dc007dcd83f224cc60000b76958226597b021019d526c91c4064f743183264accd0d51232a03f588206597c1f616a40109df0c50a6e1cfd4b0d2637c5e7865faa71d5e544dd3f343bcb961f597090a879e5e50e8746a12e0be42621587fce6e61fd434ec2fa2381c65533aeba1e9b9834a3d964c9214626199d4be6fca61858b4c32c73cc5541a4a185b40cfeb1e3113b06f88cf69cf68a33f3597290dfdfdf0ff23aacd5f5eb56abaaca8eda7a4cd3d0392ec4f319ae827c88a738c3ce6ab95e7746ef5c7739a8d3ca691afa0aee2154213d54647bee1cd033db67c41133dae737f10a2d0500b919185d22b8c98d1f0502a25908c11fd22f22de22b58d0032937df5369085f3f32dcbb24702c84cf621a1fdf67d42f0fbf471a3967d49648f44ad59f61589ac3e12ccfdf5b31186f91e2181cc1790cbe8201d80ccef5bf26f1362a38c35e8cc8f273953e486af84286e18c3841b324b83c10dd9e5451737e4d79ce10516377eca287e299e803ca14410929005124ca184d44cfc1ef1548117094c5084248a58c1c44cccc0cb49bc2872512e27f122053ce06e6e4a29a52198cd8360f7d05ea32cf3f372afbd3f6704ea87831c43fd883025b08dfa118da841ac3581a5f34796d950e7aae0146d4a9972ffa8d5fd6390717e1dc5130396f0b018eae76387f6db871cdbc08f6e41838039c0a6387fae4870354fc65903cb9c837ecf0086f1f711c411a03dbd309cc38608ad3860c518b3988d4623e67a90849060bdb9d8597767dd31fb58b9bb6528c6af66a010889981a6ea6604f25a332cc028860ded88657c87edc6166474341ad51abb1e4c9eb36fd49792d64e734c0cf32cd58059e11c0cfa976274d810b3e26e4ec13233f7f4c0572ac912186cba925cc103b33b1cfc763b643eb76374dc242915c12a12aab5a3d13f08964a42aa1dbd7d50f4335b73f6f0f18386071dd867728f4adb677ab0cc0b778e92e0f6c08bd1a41b728b5bdec7b7e1bfcf97ad1ea21f284437b3565114f68461ae7b61ed1aed8d5f77d73ea4d62aa25518d2df846429fba5d603d0b066f82d4b3f5fbfcfd29fcff40cff540da9b5dac898165a7c5a68a1058f1625e879d9c20848d8620b1e7c91451649086389192c66e6549c0dd7e2763822dc100e87d3e156dc91ebcfb44529a5b4a77866e69ae0d364a99ea3c1754e8aeb9c1377f3d97c6c16d7371fee33627b2033da17dafa85f5b71f6ee618da7c1cf499101876fbed9b2c9fadc55b2b6e2dd636597ddade00575c7f518b65e8778a53cd1c8719754dec3627eb091b72aac9a93895ec50568aa1712acba92ea7e2545936831552c1139ef0841ee0c00ad11150df5f8bed5ddddddddddddcd6ddab55f76fdcf7f700084c20b485dd425bd8d00d7b35819605f58ab342ab5557c70a384b64d9b66ddbe6f5cd7860fd7b05ead58a655e9a066cda9a154fdd87be7bd406daba1dd973bf7d7bdbc6711bb77942446edbb6eae0f6dc757f680b2bba61afb62af08038fc4b55d0f1462264f3012446cf9d3ef800e12627725d0c9eeba06e0709c65a432250680bcbddb0b34a433f9c6041df3dd7a5dcb47d3a38e80d60e9ef3e1f11c61b30453f4798ee3d161f3dd301d18778107d2cc4f6f5b90f7d35c28440946ef6dc4ce609513f0472c3fe84f861730129d2fd04dc549ffbd083be05b04c117607a6a1df5f0796d93ef4fde83e1f9dd01656bb61afb855afdac88d9b46af5301504796a4c0cee956bb9a676b9e62fa70d31044d4db2f4437b91bf7dcc70ef68f5511f56e1f4b731e4b7f2dc595b1e21c26b061a756ecf2c1839bb2e7981bb4148661fc3987ff8ae250246ed27ad520d09711b18d03fab05717f4855dad40209b9410d5901c075b3a2e075d9c8f4ec0d2ef8d0aab815fb2392686b963bbc4930118c6ff07ad1b5871436ec98e5e0d9ce3000ce3af738350854f7fb5c6b48ccf39a0b0f1fbc398989839fbc5edc3d6cc3c79e278f911e872944ac984ab5d2b57eeee089ab4d2ba39ad5ba51288005fce4277d997801f6f6aab6ae6484423a094d266e65a9dc1582ac556c7c4989898181da1efbefb8c38000d6b26f44066421b13a30ad1b801b8221c19b6aa6d3c9794524ae7f5d1aa19d82132edebe4531011c65f030c7a1ad8de3f88789a0ce3df6363a5347b970ed626d821e4f5ef1ee2a0eb52d81a140de86b220ee3350b3847132c838a7354256cb53ab00f88f1a1a273c8e69d169ae05a31f6e05139c8f7eb5a2ba54d388e638efb39778ee3388e7b8eb5b90486712ee60ddde5bef9eafb381f00be0731b049b624cb8e21fa7de70e9268f54d1937713819379c38ad71dbff8340de4c4d2153c53986d838888473b072600eb6f6c3444cb682b9b2d9e804a2149d1cf31b1566846a36ff60b62927283254be94910c10fcb736bbfc732915d913149452e90404ed176331fe30e285d5dbd08461a47d3614d79ff6efd811e7f8800d4121c74e34d5cdac183ffdb839d19b08e3fdb4890df96098f925fe31df6704bbd3b9c5ac1a93b1fca68c855dc3de9d080536e41a198bfda0acce455dc0a0d6ca959f65b8a585c420773cdcd9b57da11c580efe1b15b66fc86e186fe211d7de27111528129944a611ed9b11e7c369138f38e8d626c6f9f4c378b30aa714577b99ad588673d05eca901d94d912b8ad06dc7663fd39f7e852327fdc31c6edc67677b711bd018e5fc2108c356f7c99e32e9dd70e4cf2dca8f95c8f1f8b6bd56aede871f868546bfc19e7ffdd428f0a5817871b57be1cd49868b119a02746232dc6c3baadc586b0b69e1a232e6e890a09cb38cd4aa7fb09096119df7460c311d70f554b83c3afc55a11c6bfd56ab55a7de54b867078e412a944f6f0f8978dd2c68f654e07f9b26e38aaade9b0cc12ad3503f4021b6aad988cc91ee9e3a6506b5d3785201e9d0b6b8d6ae88166a5d33f42709c0745a41595a458e608cb7869869f5aad1d8d5c63e2a0bbb490001b4a004c83960596c19fdfb558cbca979b785886c64d7ce52b9eb45fd9585595af987cc99716932fd9e23e838f3efbc2e36facffa41ed8f9451047a2c2b214216ff642886ef6d10fb5d8f5cfbe22fab2e4d0777edff9fd107fca2fc684889b18247fdce4517af30c85602b3720b0b90c84cb391fe0b9dc7663fb55e58f9b96f8a94a2692090d4d9020b7854c21f7e7cec0ded41bbf6ae32015761be2a6a60faaae5584d1b961bfbcbf4824acaebae3a7ed26c2b84982e2cd76134f31debce2291e4172fde5176f6a18b63f6635f145cff671fbb841c1f667d996bd6759c671918bed5e4310c7f6d9735bf6dcb6719fc5df9ee3366edb70e8104936b0344302852eb877ee9d7b7fee0b5babb5dedac925ddd8f9f4c178aa957ee1e8725b37d7931707b58d7e8965fae9b3002efdd9ed3041b4c3c19ffb401cd8e1e0debccd7145f4e53e1cd2fd4936d8e24533c510cddefc32d4a55fd857b2e460af7c7b04ea1251a5c737eca13441dbcafeec76943847f65d1336cdf528959329a5a1f0882b6538c36d196ed7e4f0b901a3a172db4777fb5956d01702e1694ed8fec99190581e0d844aaf6b3a96fe6e6820b7dbab7193f4e1ea6f6f8e3ec4bc1e3fe61297b8e4d525378bb9d94b2f2c952aa8697cc4185def923a393b244b3653a734f4241ad86ce51271c4ed4ffe38987d5f1dce91a8b0fd38dc84c341f90efebba22b7a6169730e3a7378b9f3eb9e48c244e76eff57e37edb364dfbf0448b8930da77c7c48e73d0d6cfc5c4b08c5fed21e08301f85435ed354dd342eeb99af6ed951cd44007b5b086e0d53ec65fd3be46ed396a1a173b1e7fb7adbfb7edeb0f11acd0c11633b440b1cccae5f8912347fe81fdf4ed176e444d0f5ca6fc09d14d424983c8c5ed8f30ced1345aee8c7e9619373ee9c6d740ee8dee8c8ef3b95a433c51da104e4e9a55ad6a363fce8a2998bc91936635ab363fce8aa90893936634b3f971564c4d3aa9cd8fb3e4946df323a3bb8dbbb3cb03b6dddd99b942619bacc0b0695185666b8f72d2ac6a1b07ea42227b8222234565e5851169064b0b0a668392372c3348a3175668d470a1115336d3055b51499181325dd7ffc4e55b9c651353275614eade6349d19f0ec46dde3723f5b3d5cf06a99f7f46ff5753365e40b01b6a37b01b56ae767382d26e644bcbf28d15186a6c289524387e899379d28f1c63d4989f3a617677f7373f7bae524a50944110f1c38846f96923c2613c08e9447c4118696b848341fc20c2060d9b52e86134b2967d0797fbc324627a08e67c9fee737e3f383361fba3d349635f4166313a3333c8ccfca204cb1fd282e5ff19ff59267ba60ad48dcfc9e77eb96a1ae447552d430c58a63fba02ced115e56aa16c0caa54ab457df5e20dca531126fe7fea7aaf6ebcc2869e6ac59b78aa2f57371187cad58def1d037ca66aa9b9d252551561e26ba9f85a6a0879c3dabadacb5255d3a029c132ac80653414708ef85a8a7330c1c6afaaf885eca3baf1e3fb122ce01308056c9a1fdf9960934c81427bd9e7a6a12f4f4f32106ef872639c41693be0b2e797d0bb2eace8b92e0ad663e1a7575e1e5e9e6b502765d7850d4b5e77b78343f9139f9ddbb1cb49beb0b9dcecbab0a3efa2c05282a5c4eb721928f3fcce8e43273d78c0974bfa170f868f63d8f0afbcf1b2bf77dac527f05f3eac57e5994f31a8a8905e3cd27bc7c3cbc3f0f275e5253f4b7d50bd2facbcf02bcf3528b4f785e77e017c18bec6733cc5f0345a1efc183c183c66987f8e6b90e7b5ee7bff792190fb7dd6edf838ee6ee8cb71cf832ff7b5ca0bbffee6715ed801d6e5b7f1dc01b9d523625e1b1f6d19bc672ff4d6f55e47fc3c1d0e7a9f0defd577791da47cf771d0050b1e7838c2868d85a31cf532e51b0b96e12b9fe5bfc717887579fe7470f03dbe2e2eefe2f970b055f1241986f4a04bfa1a5f58532e89f42d9f0cd96bbdbeec20295eeeba21d8b05ddaab3c6b39f00ffcf539f6bce3d14d2eb77d2c3df86ecf83eff6b55461ff6a1fb92fe42fa2eff6321ec88ddf3497f41ae93b791df1c5e3991b1ebfe0857e57bcd055bc146f0829a3692e3b78e260089ae12398e2271d35ca94cb5fc83f42b0edeaaeb061e35cd9aa28e50ff9b57ef2b91a587e1db13edbd048fcc5e399ea79ffaecceb4bdbf52f107603d9871dbbf45132583c31674fe313e427a36f3b1d805cfb2899cd44f124c61e50beb06fb01f0e07b3ffe1ebb20f81687d4a43317e30a761ee3e7afd41c041fa7d45ef5da5a1c681350e0f123785fcc3ca490981c5531023213a62ca107d3be5261e314c7c0aa236f6401f022ebfe9bc86158de21a646d1d6ddaf76019bfdc03f1c34df1b9c7813302b884cb3df389e3985cae724bb8a0e4b292cb49b880ba5c77f5fb657b61894a6fcab4d64ffe70a07a92e2207d4ec244c90d632ef7f363dc14da1a38933ff73d38e6513c4950e54431ee9d7b3bfacad9f3f73d3a7bee53dcf4c2d11dd468159ac5d3ab8c5e5216109b543e74940fe9e5d06594cb2b33b8d8b17882e19fc6733cdd78d2734c79186e781a871435bb1d241594969f2e3f293f7fc6288ad2701ecbb28e3513076716737066a4277dc79a899b687ccb13ebe0bfbc783453c80fc1cfbc3c47c353798e515ad8e531a60666710ebbe20c25cd888ffcc06d6b9f1c54cdcd5ee5391068ce96ecc9aa8e74c91df9923c12267b6a2b87d57532cb5454a4fcc0ec7b699ff6c93ed92476551ee56916bb1d19334a8b8b8c14f9328548afc88d7ff16a7ee6868eee4c9a547efef4824d28dff2e104c3e5c329853b4d291fce2bdc49a26122fd7c1b545050b7bfc5a526c54bddfe1b9e90ecb3a4a7e1c52ee95b725bfe843c04e2a54545652e40e551be22fab67c381c0455be170727084492819d1b102c2769e273394913d8e53a504bd93b644739994ad91ff6759119b7949f757056503db1f19c83524d6b4384b58f0f3791a2cb4d2e2e1e5f1b5e8a83ff32f8dadcf934beb092be58caf7127561cd60f6289e64b0bfc5ab0cf6bb782006fb657896c1fe142f85c1fe176fc430fd5c37d31862ce91fd9d272007ffe33bcf13eba090222f8f9a2e51d68e46ff20d8ddcf51af6e42394d5884992f5b5e3ea8bb1d35fe3f96e99f6f834d2e3f57acccca33afbc8b0cf233ef86f7e2c916afa565a70acd6ef88ec51c376557b67861cbd36f1bff3cd7fbbf5c68c23a36ba615d2ef91d932e99aa91f23b4a103038a508244abec0539209af91277094fc80d3d4e4824f121561e6d7809112e34e55162eadb9930691a86c3472d0fec00e3e0da988fd2facb70b415064b85c94ba28a594ba6cb88e406dd878fa2fc3fb859f4a96502fdaf022cbc1e949eafdf03f445f172e30b9f233e9dd80e1c379f9252a9e60f822ec1bddf8502f1f8ca76560f0fb088ba78c613e6619be9fb1effb220f8fbf5eec2d3038bf09f3340d1d6b19e64f266227b873fadca905ce313f8c5adc2989b8e9467a805149d42871a7a79ab8473eef4369c4fbef698a8a444994a49135b1a5b40e4ecfbe7fc8beffd8411befb2432a6233216efa9f9fa9e2e9e5479fb95cdec617d6f92f5e44e230f33f2fb61c9cef795107457637d02715b1a1e5f1c3c8c1f944f44df981f638088ba7f69121effcd9333dca664affbd88e3e0247dc82c0fe2359ed338385de66b5fcc71704a548471949497f111a6f2f3994e1acf92cd7f1a1cf7e1844d189d347e3e0d7232c3ce98f1cc336a7892c693350e4edac1e2e9eb84d1f824cac1f9e18485120546bfcf7b7f6cd8ece9b07e4b8bc797c5e33bc31ba2495e389a3ff242d00b5e385af186c8ee1032c50b536e3861370545a2eeb45f5bd9d914f664bd3263b9388f2347ba9febd515657555573cf18ccb555dae39e4ca8eac8833e4469c78ea72aa0ba7ba62abbaa2eb0a914aaaab63551715366c588dbd03ebf153c3e284c176dc945ddfe1895f75b9ba411d23df4cc6f81cbbe55f19bb9fda85cca93a3b353033ca86ccbadf63c6c713d7ec1bc91d2d5558d9378b4fa9bc33b27e742468858d2f4b978709620ec3d74f80aa1eb091b3ef306657826cd61c4355d2b734bec724ba4702d481ea779fc4f648689ba67df749708fc4c66ddb779f44e891e042dd73a00e34c57c695d7e7086cb49b600bb610d9cb7797d625f7adc9f9473ce29a79492a9d72b38e79717909bb4831d17dbf902f1ee23703ec4d3fc7e57f1cf0de517a5f6b2471c8c3837de708e185511a65b80bcf2930ebaea86b2c5803833bf7913615260ddc64d44dcb472138559a16a24295b7637fc8ddd4f27e97eb5ddf165741bf43bb716985c7f8903cc75e5ffe5442d290138e1681922670c720c83e35d45068e97a41e58f9fdfe5d72a941a385650669f4c28a4a8a0c94132b0a75206ed332156474faa0da70043759071fac38c2128841a79a81b8b099672cc38e85f6b200c7ecd231b2923592a607c642128744556cc98eebc68807f11a9f9e1c56dbf41026b19077ee0ca5fe6efee6ef16c5980d4b3cfa3adfe026684a1965e49a72f46cf29ff8702c6de41026b1d7ce919b988a4194f8b47288b88da398f0bc708eb4904efd00c13a3db6176edb8195cd1d43fd99141cc72f0e52806b90e3b8eca16651941d0f6666e61a98bb9999a315dc88b95300902b9f3b098e5f628cd96539f252b86bc92507834814571cf1aca98d83f3a65faf3af9c125504afc5cede512a9ab916e9fdcd62e4b7c79e76d9b08139d87a8444d56563d0b991a110000202000b314000030140a874302a150302215d5cd0714800c848c4a74541ec9d320885114648c31c818431000c41000818119c11407043406eded9961672cdbc2f55f085e63545d2677c4b4e5a3e9a743afef616495b0219b15ab2d20c50063a02b6b4d831545b1a2a6fc6a6e3dbb26d570b39b8d022faed469e2c2f949a9dad15042f8dd539423b18a12f23833acf499fc3fac2878e82a5088823e37c95bcac7c5b012887a512660a03cef89558e98f16a03cf1f8d37302f174133088b509f30bef2e038610b28efcb369affb1716e4974ccb50ea980a8e740567dcde06691be5757ffa41f27af3117684896fcdee53295a2222e7fe593cd3dc06442f45ebe1ea1728c37b7a10716c65e0501984e4ab0769aa4b81840be9b07ab4aa8bafd426bde0e743c1121254e2992f4f510c2a856a04002dc936955081dec5dca0f5f50383ce438edb031749f3314662df49bd8250481334aebda532401861044b66149fb3c3357d802357e14bb057f1168cb8c748fd41331f46e4bf182034fee7b78c6fdecae67bcac568385551da1cfdbfa8f439b3c0c50b4e0ab9737144ccbf6a8d124ab50305f3a008dd99d36f06b75c9d93e96dc44306058a5a15411069095ea599836fa31f26625885ce3ae62add7e85a3987504c3269c60ec2915373a84003af09ba4e7082b097ed7677669fc43354af58408a18096ca91f35adfe8b77e0001004d20d9a4753a58a6409b0c5b8a62334a551347081c74ce7e8fec120157ee755046e2bbe916ab91df40bdf47a7c2b4726b82d8283d8dc238301ac2d2b2e507aae19cc283134484aba03415a0441ff010ec56fe89e07e955e26abdec507acd1d31e88c80d90d686ad62caff3f0bd49d5e5d19c18076bee4d98e7ee29201ffbdacf5c6e99d938267961d5c56739c064ce5babc8c10663f5502b2691cb9586001aab51d913df7cf0dc74646a7edf5d412deda11e65c05d6d806185ef54fc32ae358adeeccc6fff642f5056e1774c58e0251ba52a8c8d1c874daeda0135534d199817fa6422252884cc489e863b25e38c9a8d343abf613d462deaffdaad07ca4a2aea204c5009ef94fb6324a916bcf469d1318c88788b653088a9c030c581ea2397ca2fb67ef2cc6d2ece8fcea8a5a700332a14ef4311232b8c0f19c1ac111f157fb9f7b053880f52cbbdb7426929a8fbebd3f80a4550559db31343d6b1b89ccb95868aeea10df8360f02b69492c608a866ef491940c9d8bb5a1205893758d26059a2b54f46e43ec62277cf72d0e5ac143588424c76413d63ef2e2b37bd60052f1346b083ac067fe294d8877c42e4e589780febfa4385268d2d3ae0d6e5d53ef1ed009815a3f0687a282135c0216d9a0435840118683e38b9d46b058f6bdde4c746e4c433b9a1c4a78dc952648f24939773c8bb8afa0ba2e9346d987d2738fc8e6a5c5663e1a004916e338c52a27b0e075ede1d6c3d4361b6643b6c2326180a2810f845681381e08dd7f67d99ab92b8b2100739f3afd40c13185350fe2fcd73e2521b152ab805a98076e3ec99d096d86dfe698dbf306612af032778b98a48137c1ac85b87326033d6d659ed3f5a046515dbb40c92e8565989c88402a77e10c8005a5f9d47560dfb821b34620175089fbb9ba1f09db1161cd0b935571160badb421a67728050a1448b5c563aac4b95f9ad29622989724cc27f71388a07bc2c3ee717232298810fb88ab36dcca94d580b62ddbe2464af22938933c4ba49ffb7867fd4b82358a32b4237b47002389f72d50561e0edcb2aafae1e9bd0e835aa9fcdb513eef959b42f84646d882122ec944d1f75e2aadf38b32dfbe453ca48023902d901eec5f39ed6f5fea7d9578855597b68bd109f2112b011aac5a32556e9938eee1b4a9dc7e47b0fbafcad0fe077a9da08a0ef56f3c73aa5f0cb2bd8efafdfdd49846ebd9620e9cd02ab1248d033e8e7eb95632c45384f584633bfd56ff83be35aba0b2e5d40391881e4a64b5c67e98a07bd6a25f92e69c52584aa72c11f86b3fd698232c5269256f91c4c99990893bca5bbd6a698238027a8319e68817cca9b2bfdc974143118800206fc91055c4033f08398b4a8e0793d6476d8bc163ade125cd74ec40f4d4e1856eb47e1054373883e3055aeb714ef0226a904620fd2832148b01f33fe047065c42e96ff14214ee85036acba0aa2f1490d2f94e3da8d20b0b505236d7042dd64dbb3890c6ca832af6395d54aec14331cfa84ba5168151bdf40b42c94a38282b41ed2786868211e8ccd244be4fc65ac440ab8aaf2c983ad2f511fd23e0814f1a46d2c9021d5e450fa982547bf84f2e3417c46766ae816e8eb6f100a2e761670a50e976d8b81df84f6adf87bc817e8cfcbcb58cf514cec07b27f98dace801bb694c6ac53b3503a0150c21ada1091ee98b7f10325a241dd20942a070fa5a85133281f7a0dec5ba3fd1aa7fac323b9b1624783d42862ccfbdc8ee3c18e7506c4521f313ba1a1a154242d363a42d321724dc62dc661f284385ae7aa85b70660bba0449cb7f2cd2e0a0d21a27a598a95053e9a1bdf9cec38214503bc347026d5c515391f95d27ef4460da500df7858fe9db6a5c13f50ae618cd655d40fa6068ee21c6b71b5cb9505e3d384dfc1c9b333e5770a62ac6f43a0d7d31e6193b1bb316ab1d509dfa3493fbc92b389c38344a8670e0fb9d6140ee021a052fa4185925835b8fbfc9d9cd6b9b4488e6b3a648032e74a7ca34896d61bce8ebbbb21ccc322df7b659fe21867c0c796c46e4e2c91f08b7a85c017f139dd20515a5de51b27351e08eb1eaeb4d7d670c62afbc3c95ee24531e57fadc6871a01b77febf1444d66050ae3203a2471b296a91df344d37759e4140d0e526d1c9546943d54c79b248390e0de13d361c221e60e2b8acf4399caea85f820c5522f6809ed4e98c105e89a1f582809e16a600a2a3aa179ac9d51cc31daea19a50563a08fbd14b56cc9e1702ba813a01e3dd5cd596cd529a5b386220049b640894109695744359e8871b42dddd21d75d0159f56e2469d7294c00993b7ce212d944877f3d987077580e5788fdce91e82afe0e48a998f3b9e648efeb4f20b0a1e4a071a38791ae4ea620983c8627cfb11f40d87cf2ac421d04afca866fbed1dd302748c453af3f92ec062d2a7fe7496921285af26d931df04aa586704a49d43817a857e44ea091e9c20445e46b4c2c4608564082df79ea00e49b1acca3d5d792893c379db0d8a8cddefbf57a7ff4b8c915ec45be261c1859f02ed960ce815a24ca0e32cba3db626420c7e9edd4f9819241be94ce6385f8658c62874e2619bbf7efd8b782ef1e94ce3c2ef84f429ce524489d15b9efce82fb8772e08acef51098a4fe4917baba3fddece788146e6f4f6eceed19dd6fae615f33a5558667a51c426d5bf358e754b1dd690ab52ea43c10b497ca9c3dc2f22e66978070aaecc9e21c310710b200d152936565f4b3e383716957b391e76a27c0eeee8587b9a64c7b23a73c678aa51367e43082bcca6d444b2b66ef33d040477d1f517b1664818e1de3e3673e6c5ba63b947271f4ed8399ba43fa78cb6beb2b150afbcf5629afa4232cc09045ab236a21a89fc1ecc5a95ac94c28f0ce8d5158bb7b964b62a9ebc93947f47cb6a862c4718db8d827c432fe9b0f7e46c70fd96e5090aabb5fef6949c599c7f320a10719b4613cc4ac33872e1bade10145f235ab4c90dec183fe9c4e0246ea8b60d4fb0f82cfc0028071a6c95c4b619203dc8c2b7907ce34f3421c0131ce7ab81699a7eb20d1e5862da882df14442772a4318c375d5012f5a11e7b68b5a06081b359ebb0917789ba3146cddcc608edc183cfd10bd5804fdb1394205b0e7b2e0add3a9f089e6a2829a6ea736becfb4717e5bab3c3a27b93f713ac72356ea25aac3fda5e3a43d4e8da4755ab17d250f1abae9764c3e01a6b115eb14437a6153c2a2b6130d8ddd40f982a89d59eee7b171c885dbad877cd4c8766b98c0b1d17de41c51296869f2119520ac80c4f4a19613a8683f017bd578773f7c24064704a935cd2633e7edc2820890f14f087002f03c091e1dd925d8de8622823974b2af2694f8e2b4772220e76c2bb2ccfb651f9e7d0345e4466162cdf60072fd04e5567c8fd40f91faf4b1bd00f08b85507f7597aea321b66c93bd895bdc8e22edce8dc473eeefc049a7734037a5c2f532a81997a1db7a00e5c6cdc5d03139c72f6936bcdcf3030e4c88ac2f348a2f080268f2dda1d0c6b9e8e223acd64a4bd8c737f8ddc3c391932d2099999f59a6091ee76f5d0a2a807cd1b70e47970e8d018b4f135da877aa6490b86a70ebd0ed012e19beaea67898401367369222c918b528b24d004ea0898a5a8e210dc9ab76a6cafa3dd7481f484412fcedad294cfae11b972c8fcb6e5743311d4c3031e2486e9b99756ab432efa4c0aea99d5eaf88bc3b204f7c7aa43988f4d2903b85369091e0969d2c9c6b6d9566c83bc02b7c0257c5ffd857b50c411a38a09f90209c668ec6c129ec4eb09294f5986e158ef05897089e8210b55574744fc6d7988076862707431a2ba274f67332673f312d03e838a23063abfdb3c122c7a392eaae6cf4c52c6779b023b8aa1735807581c00c3771b1d1ba49390d465d4c2b74fcfcb3b30567aa8b7d47a42562181f329c1b04ad123cee07ee7bc54376ce94b5463279960f829f0c05656b38f5319bce7ea440c1494b1ddb674986eccff0350b759794cd86d2a1db87f50ef5c1128bd09d6ab804e8d471393f3afdfce61321685a130bc0d903e2137abb01e2216d34ecce53a5b5647b24b1282df09cfa1d914504f82cc744f4c5c0d26db347d038ad610eeaf0332a1114e3d36c98922eeda6f7672a31a7eb8f8edb04b5c1544f555ee55631b199fe049c76fe40266a24b2c9b7bab33bf1b34944ec51d1543ea49e0ee038c824580526dcc4b08ac500dfcce75d85431b26c4e36539bfe5d64865d704cc99cf1c3631dbd7993f59e104354846143d0326ba2ac0ce993f3b409438ac9017e588676bba9f982717a0e77e842c53f691255a2ac12a9671afcf38282b397d6a9e6aeed4454936b98bb9ee68c896a2d89cb1b6ccb29dea1d4870810eaefbed44f5e739ed6d69cb96761e15ce23264143c11a6d5a2ea702eff80c54545b02475069cd6016c9b49635748b700acdc44b4a2704b354c96af1902f7b3da2c4748e1d498a14a8c24e432cb02422c2c0c698819d24792c171ccd8baca2b70e3f452ab8abac2d900ec83fc7b2f056aa993bddbb408dd0c34cd765d0aae89c2eb51721d145c87a2eba1f45a285c0f45d74b01dbf9d6b3b82e289daaca912564f681a1bba91f722752770a853da66c20876af994349becc6e97a166fd11bf3144049d98870d1c8a45ea44e6dea4ab9807909a84d87a43512287220030c0d4393d255791c144f7b318aec65f466c545188dbf41d5c633715439209692a9ec4969682e0b95508b04215e51595ba664e04cd8e35fb2f7443542bfe7bac503150699c7a7637a61b6092f8cfca165beba7496d202a995151d563da8e0aad65525cb96b1425dacd7672084ffc2a72cb4a120474d601d9f6255f7c5247cc00a3eb0faf68509fa80153e602d1fb22afbc3647cc22a9fb12af6a7497c14580d3e19e7a3dbf585c8dc9a89d0951f0a0891e101b0a7c0e773c7a756187c2b99c7ed083368e01e727af7a3d341c43fa5f5f94cd651b3a069b87de1e98799eafbc88a3229bd4a938cf98c588e3aadb8cf20f4cd09fc4f42ead66544c1ea7c8419a66f266c4af26648212e47cb440c07465c07218e43e1139689780e82980e8218960f9f3948621c0031c8a1fce4a398654d2c5f66cf276db915d95c43085dc60142b07c67d75822c2038de3217fb9750c2d41dce4eea1549e641511433d33bc231fb72617758a82ec6b35053deb5abfac69b710261513e97f61820bb5a7517f4863180125e67bf32e135541e60739d5daf3e2693dca0ef4ab694328703845550a0b667d82c49fa056a00644bd9396edf3e313b3963bc391a22a1815ddbacb28e01e86af3b47291373e555a0209bb396d843bae22b37a1e90a7789f7729a513c2c9b4d3110b4f7a1bf92519e32cb44619d58dbb2f806938009ae3af9bed6e38402fae0035f63c467f1f36b5ce2b2b8d37aeac54335acbf1f954614c957fcaafa59077c58f7efd50a94cff75343188e490e49b151c15d9f9531b7f391982ac45d4190026230d6ced54c44afe2b7f276512bf969990be5f4495cc17ba465d50749a08266a24d69d6e3bb7ab5b9b9bdf4ac6c4947c7eac6b7a8b194a1a11ef92ee65c05906824f765e00a91d0ff46f6d0e56ef31d290dfc2405e8378fcdd901a20723060fe0b7dccbd93e4d982e42ea724b385aa83537f409b95a379ea4c8a6b9361e0882ddd6dd61aa844ee6049e8f2f1a3c78aab632890830671d8b628cc0f1bd532a395885f44ed570f8956fbdc90c391e2e4598281fd47c6b533770de563968ef12010f001d6bfea28fddc41f48781888cd17f94655b94dc1ad7191449275928350c82fd7f81988f9e9c9f689fe6125bdfe858b8ca264f743d9947ea93a5403cfb461625c07a74186463734c33990c17fc9606d3400c39874187cb67232a4bcfe33d177c3951d803fbecb6062b2381f7d50261691e4bf30973da2299ab8c8a113cf80f85c0e5d1a32687d5445b40891154b37ecab18d1a5df67a76e9b21d14e746140245b23274cc0d4916cfe76a49b296902f238101acaa8120c96e02fc8e9c7c61e3a37f103830c40082e6ac58c3406de8477c42394c10db95c731454cb8b834cfa27b6d9258c95bb5ad14cbafee3246fb7fa33e923ecfd31aaaacff9b6b57f3e33f94f77b2a251d17805d19eb52d3470a36097aaaf482650ed88888b1b580f77edbedbc177affe0e6264e2f918983e46c758adf55348f6e342eaacc0bf9f0110aafe80f1e4b752df267578d157f8aa06c820c381634c53966a08ac33f1c43a38827c88cc347c29aed4e140efc21d2834e8d53d49c97b34efaff39e0c7a0ff4bc833c5ea267af50ea6b638168e47fb6844c302bddca395cf43f9fa460f8f969be427c68889ca63d7f085cf0e49d8cc00a52c01ed7cb4a5d2c00462a0ca8bc616c5af01a60c9e20a2240cc01cb60b6ae1a2a59199f302298431de04076a49a687834f304cd4ba520eec844318fdb22a91aef4900a3b5e68ca9c9518d3a3d3581b2068a563c7b6f1cbe525f7c4f55a909dc1b865db0020dd1bfa0020dadb57fee08c215a1880a63b328344f643d3e7cee862af51a501a8ab64a14e292ef24f68670fdb790fc3ae020e63965c1dcd42cdbc7e9be2b687dfdd5b3d2abf8b8c0118d7262aa1dce064cde835dee2c4cfc28343df22af75c6cc27655cbe7c6e46ade0a5c6a567aaa60a72f68563676e63ad9ea47b8f10983ab55fe34468f48022ff2f1cee306320424e3e9db16402d218ad97eb55797dc880b74b67b1c3c7715c9309e4bd4b13a1dc3ce3f8b58056c45f7efddcd00090681b57a14f70c446eba84a8d6876e202bcbfe152b6caee48f2922977ad616a29e176cb0df4209af4b3492be23eaa258f72d7d4d9f300063e419622272565e24b7c71ca9718a92fdf419dfa554fb643da6ac730c24b327e671ea400f6ce1c04f6d25d4914cc559e476ca0285f368d7b4e9bcda0690ef67a0baba5e0931e8fa70ca6dc2535f3c23e6b373587bc5e6683e9e835218a7efac062a99ebe123d698d542d0865b031a3bce8d1cbb8beb1c6b67768a86183adb1429e610b16ae631b55dfb2752eb5edd3147accd3c1cf363fd9c0e2ef2c149f7a92c60a79b9a042dc85386837a2e132c33b76742652ef2a0806a597b6dd72878d025b0a8685482b01910d5ddf83d1c41dda9a5a224c585da56adf36f78827000400bd3b89051a2169f7904f8cc9657a9547a640270682550c14642c2f16ab0308a4b8ff002a0b0fdf47ac4b2fadde43f584935567878b845028885db97f5e687a57329972a989cc1f8003a31dd5f590aa283558e2ccd9c52e8a18379443044285a65d86befed087131d82a328c49b7e835f2b9b99c73249f0ff3012bf91c7cb84b269f775059be46c528edbf9a4abd99227e82deb63cca54b112b2280eb95dc3ccfca1a05cea561132713b707704dc48811e62d6d0445e21d86c2d1b0244e0f47ad330d8da19498ecf4666879808456cc6a9839be2f5879117041df885da9835d2e8a06a36b2b7349866e6ef28a5bc398083dbd441ca76fcf6488a34200969bd5a73ba0d5d5cfa1b61023388f31ae9993283ee39b99f764a3b807cb41eb4222d2e17a72173038ae4508bccde1674d2f3e1d8efce301c723375fe1d2de96005cb470c08713aa8b419fb6b4417ea0f117b317f90bd3b26a5ccec22f4e2c1c48a734124e345b8590a756262f8c8bd2c00b24abbf626869a6691948288295e5e068b4e22a310fa163fbc96101b5370c040a4d89ab44e69ffcad926463c62d9c386f4deef6c71ceb016e779faaad7c4b31c50fd02f10d2f2c2e36a164b6f7d169758ac02116b3ee22e75ad409afe745b5d769a19e64aefeab0ec60bc1742a55245e33ae056dea7ca7f723935c8c080b50ca11ef8ce9a63cd9e04932749125a19f7094efbd7807ee76d4c4a176da52f10ffb881e832983e2b8613ae4c330adf3928e10881f8fc3d024806d863f45251622a948b9a0369fe445d075c50dcfc73fb4cbc85255dead047b645fd0b7422d08a93f145ae6fea5ff0238792fd4f5f0af189788c46477f8532531929528b89ceb0d95c57a08dcf915d0566913ac61ba30a3193d8206e119ea838e496efb63908c1e714254117fc7a1295c6c22e58b3a7718db101d8bae13287acca5dc130b1fabe5ee198c144693320f108cc67c73031352037ee836c9e402f8a0b887d7d83ec0261050691c350998b2ba8eca2185f4efc784b105225c5f0294aaae322b5beadf26d2f62130c9f48db8aa4d007c1a9eeca79fda43d2228e33d9aa4cd351be9681d90053534fb14797e4bf6e83de58373c829ae0e25b77f100d44ca14aea6ec0cb30224ca9622def1244629f609e4f8693cf0129d6fa79e85d9b199121cf44452c87cb26c099ed107bcc79d3875b873dddc309e145480e36a88f2eb6ff256bf823b28cfcc7616679652d5bc5d8df627485618774b9c5787963c5282472ae2270185477af513e20056e390a0fd9087ade41a36268ecfacb8ebb0a42c0eca9e486483e66275f42c12cbf4ef64617c3be5c57b4ff2c34c4bbf767809c79b850b2906e28ac5c69b0ddef10ec974c6ce693f03c3455df8289b53119f5e25a25e1c60aa40c24e2fe8c3b2f258b8b5fc02b077b031053c015697ec239fe71b5e0c53527d6f49b30224f21b0cd30fc480f9d1eb332847c1eee69156ade0e71ccbe1845447012538b5114b01b890511f65b8b54ada51825620cd166c8d0d6a953764ffe2c3fc6f063b01feb700aab4ad629af7dd1da503580f403254b44d18cd13193fe446138666d2f3f2d56da2275983914e42a815b32d65cd339ecc7e991301ecc24edf821e2b34ce6d9059c6384380ec54ef388225507a82de2dca0f7b76872fdf144d08722f70cc40e3dce24a4baef1ec55bda29b054e68d7dec5888f2ecb69ed7c76c85244a2e812b8b5b577c09308ef84f6ddeaeae3de210054db6b9933d99961d2b0f8573292a92e771abba36209bc6623acf224062b8997ca75c7fd75d9232047ce2ef56cdcf30742bea482c98912324f3e11d84a653fa88514a7aa2c18ab29f4f5faa7b07336d273518f02601ace2fb2c4672b0d5db343d55ee31eed006b78ce746e72ff76b82fd4f2ede743af6cacef6fc0fe5b12f68e946567e8e069b015c34537eea9f83209527742da4318ad97dd067598c0ba72f0dbfef1cf8cec90a7c6768f9f314f1ee82d7705d3a0f11b41ec90b454029cc52b327023347f1e124007908ed1ae852395e0e86dbbc6f8752e1f6ebd087ac9f0b9ab65852a5907ed890bc62b928f308b53c424ecf1994427f4f149ae83195f0c034f0b86c4fdc9cc6491ed27bea761b7a128272b4f2e0d34913a24074becc6276ef5cde841488f682a2433a483012854a43a296453ef2cb9d1f0249eb88931228ee367a1af1854df937dc44d85a790f884da557fb247d0c7118d839ca250469ee66de5730abbac480e2b3d4f60d6ab3b450d050c702bd39df38c5b494e71db9f15e86e653cce548a79241d8a9d69eda6e02e4d83e8330cd208ee7dc20fd1618ae6ec76fa56c6d15e0c0314aae362ac1617ce6c890dbd5b0227ace4e5f0103875124b5ab002ee25823e9eeb307474403ebd42b60e01989a4565a33c0333b3c8ff62f96041caac0c567549099b5068854d251fc44d342e9da41c5047f742c3d5e315fc5e95ec502afbb811ea3318561d2074a6de5605089da17781460170afbe8a390dffe56855813f1587e8e7266b6d0269ce6f8ed4d490e143328d0a8f220a8861a415d2280622921946500316cdf8cbf35872dd37c1999dcf8ab634cfb396c3cae9783f79a6d1bcd013b700e980f785c3bb0eec51ce0ba0d48c21b5243d991005ca6bf4a48db60f1db299d03f780f0c0393b90b87f886117808d149a5b8d2f2788c890d328b503340706fbc04a53973c661bf9fe8df6f43d341f769b2efb01dc6c7442db6876c0241fa0442284c73693efefa31a1733f9199a094d7dfce5a5d14f9aff56d89922fc2d3fea72d6b9bf35ac8313f408885a80361ecffd20c7ef554efb796613d8265058e4f108b570e926f96dbf7d6d690e0d7460d037a2d88ca4dc4625a8e06c8eef49902b102d09fe1b3eb4a441811bb25b2094059719017c4c4027caa03e02ae3b6abb0205c611e72acc19b990d967b0a04d31109084bb8cc9ca9fb9d1992d04713bf0df81e7fad12f3f26091a642da78c86eb29d07f053811e07be51f139b3f88f0124d0a4ca846c449d59cff323aea622f2ef354743ad8a38f7305458e7f7181072c34f6720f33230a650d2d4beb29dc589208bef31322f3b07ebe12d22ea0107ea2bd78ccd0ecf4a9e5f8b8071dc3a3cd00527ef520491baffdbc0c868628d9692f59266ad189c40a488bb871801c5ac8a0ea341f4f0279df3bf03ffaa26e2ea516965d9827af798e1493340c86cc424d39893b970bc4fe822a6a1a5892e32018b1b6180c00bf7efe062361fcdde47a854ada3c9a90be19cbf605db185b8d33bfd0db47fbef971b936a8b2fd64fb526a2742381347e996a300bb8789fa6780d993297618937fa73a86a5d248f4a913357fbdeb2540aaa48e8958a8fe5473f01c90a99cedad3cb152117311d9f430b41d09d4df9854cb93b790eed3adeff3367c2d4cdfed6ec78ccb8027a09ff287ebeddfee34c6e2a5020ad58f4c317c95d2077d34b834defb3addd0c240ba2396001d126f51ada88b0c8d763cb2f3c3c2b4dbec4d5066ba8c788a028c772a48cc30fd76a8f6acea4fe684a8e38a05060cc1431e51fc9652972d6d6a0f0aca281a7d784ec81e95d3743a6f0bdbd068c11a9fad21205b4094bd541df29d3d0bdb75e1c51100cdc3180c5bcd0a6b5f42754f70298d3d7abe5e3e927e556a812ddab86941add5444ef3e98ec046349d391b690714f0c8b9888ad7095293236ab07f264be3845a68bc84c0cf52815ebdad7a74d4a6c3d000a6f8623f677bd724c1ed828a8994a106670bc8353bb45c71d053dfc8e521f554df76a1602d5065ec5401f19708afbd838015c6b037da48c8cc31139b5c689b26722210d29db79651bb3418d3ad4d0c5e23c8e8042313628ae15d2523b54740950424150db2722962f19f9cc8447d304fa9813a3a410024744f8e4a389c5d7548bd2af5b3ad045efaa59c1875bb13aef87d6e87a5c126c850e411d6e89e1521afc184df93c533998c5ddce1337c802a8824ef9dc2ff20b83c2f21bf350e0d6dd8b4124eb5c9ebb1c62cac843d04a21408cf97dcfb12d004bf63031e8c89363fa28a4981bba9fc3db2557d611df56085cebe6762a32364ec81ee1b1e9db234076b5c556e4f2666e2681f7848d0778bc8b90f9b9fab49354373ced78241a44ca3fe6247f88c3b19587434370d985bb91ecbc73f4eb0aa10052c945ae471e3a47d9a71cd43a58f250f781f0554a09f501779cc264d17a8c19bd829fd3e324bdaa23419a29c648f0c71185d0e5cdfa319e652922826269a18053feb514d061f60e8a43ffeee0923e79a080a2664f20b12e4341afabca931b1e90c0ca6a743c8b282a6afef2dda4429e9de3a833772a68398dbf26e1102ac611e5516084526c6292ab2aa7ede942bfeeaf336b1a0c6369d29d7fa274eeb15bf848c1993ab481e7c0ce3e7f814401286644dae84a65170a425de57713301b42374dedb63abadd3609c9f5b0004a58303175acd150a8f5a1e522e7646de2b8736cadee0ee86cb192ee558e6873ad0b8f30c05db6b05800caf9b126c914a74177350de2eaaa295362d9a6691f28c4438bff691c49df3c0d89ce0b321f23a280f7e9c6941e9903ad1c4649c362bbc9943bcacdb440f2a81393c6b1318196df5a20dcfe69d069165afa652074348352a2118b7a84ebc083fe5ca7113730036879cba6a92283f01b94c9af1831df12269e637a5391121f39d4ebd3de91acd1bc0fc6a870281c4fd4fe2174890b4c8cb4e6d4ab365f5512eeda3873e9005f95f959812d6c1c1a75dd5aa88c4144e3b0923b9ff29cd48e5895ea886d027f2fc3a11bc23f67fe6d32f61caa40530680c4e2e37f5256b58222595dca0e9be778dcead2feb1ca04dad5356fec78bd28b86c17c969d362858ead55920967542ebb1ddcf240ab43d26180b0e4efcd9bbf772a83df0cfb8499161f2128e978de819c1d91371605a6506a4e181e069e441397c699eecc93da51dd1ab6c9177d3961cd4107f07f651138e87e043a27240c87531976edb63452b81dd938a8acaecefe243e3f36e4085a554d53e57e173a56a22c9bcb7c0b9bb567b587a48cf2fe226a01e7e0e5ce891c5ad5a1ff523d993eb9cd3c95d6b830a91df04c68e9f4bcf52cf81f5b311199cecd79518eea2d06de4d8fe29e93830209e28dcaa98adaabcb190e226cf65b09e05de57be350ed4455df41773e479e01189bd19b378aef19ddf0eec47827b7a9795b853b905c83ba662840f2a16ac62e95b617ea6590cd3624da14858016d67527fb63bb4fa535aa6e075d1a41843fdf71528657a24ae98a917cea2659f1785067f933c2514856591298cc3ba3f48ff27509fc0ace38940fa7317e90c89eed9c3448f95813d45e6b4fab87d45ce92fa7772964ec411aa5282d24350d52db51054c9fdd5386262c646e09228db9c30f62229a6032f5f8d8d97bc0c787ba845fc8fe6157a47c1b3e205b969b3e57f517e4dcadba64d36e3f6be507eb70bd800055dcbab57b10f1386b41b43db52024961ad9cbdb684403db851872386a252078cd2e24573e360fd3baab65731eee0e581ae7351d9c5116ad5d7665cead4a93348c23efde181ece76827b0a39ea620f1481e7cf8d1dc0f6ac0597d20bc23b168e4b391413bb146a34cc49315cec649598f09533b257dfac60e5ecc236210b103471c6e4815baeb93f8bf2b8a0fb80cbc5eb1a362d215ff48baa1c9487c589bb501cd511929fbe1c9c0c6e2b73fe79956233ad3da3ca0627c56a9d92305644017f0d8fc1b31382aae58a57c016ba01a1e38cf2c4503a6b2e942705a8ea397e4991b400f45065bccda6485b1c9c43f15436fbfa12fde40c80fcae03369b935e3392a6a6eb99068f46b60cef8a253ac213f9c081ea8e1ed120710fc2a065471ad58a53c5cf63c80de0ce66e39af5ba3e3b8f41742e083ab5109e9032021cfc60a1d784f0f4960e02c4a0395dba75a4d45509cc30e93fb78e522340b8675de7cdf9e3283a79081696b9a9661219f0c6fa4d291084c60889bb0ce224fd59dbc2abf9ae926b5c2472284652116233ffa0b2a0d0f80caae9b957db145a1be45a4be9fd1b6d4421d4143a1bd262c809e3a6a484fce97d63cd3c968e9edfa3c49df2813e7a52002be118d2c53a08f2018522085d158d7f9c585631d5a6fd60d0249664b058e0e393a980adbd06a625e05188473ab32498423ba8b07777b61dae512be9465f6b635d863b968d115eebf45b2447399f401598ec555d2960b16feff0aab494805c980064e6f7c22eb3346c8d641738ba0f5f6b280ef38b1be910c59421d167584d952ebe5d4d4827663668386d6f2022ca7a6311e9028e3dfa0877380036e0ee76b29e265aca2c8197a0d2980c9fd2735f0a84cab17f320775fa35e06aeb066ac9534a7e75ec5ecbd0fec1b6ef22f0b39b851b7bd92bdc61259e67b39598e5b5a0edcefc4a90ae2d1391b574e5ffd26ccb6cd86926ad84c82e6cbf3a620467dd993149f06056ea2595c0e37bda73dfa9d7066f9164bd06f41bc1cdcf917d9b72bc4945a707d216204e70bcb75ebaa2064edee54c36a28bd61aec02fbe6444e11061da6062b9a94ceb24f6fc5adbb7748116ad86c732dbfe046a507394d80c82aefd037b92dc01706ede09e1f8524c217335798c2e11d6072aa8e21d1d5e8668c52c6cfc920f38cd98da6c3a8f1293e762170d6829daa05c30b624f0ddd3cbbfc08bbcaa54f9bcd88c51fded6e38f48460c9d311c7854a24cfbc96b718b5d92461f658a7cdf0e8d6da11a0e3bd9462a9b2a375736cbfb3aa55476428727c154d88705e3e07f3750e0a9a84d97ea61388ce6808c2179305c72657a285b206c77b15abe6174c9e52860c42efa94db13c6517e63bda2f245957d98aae8f55f75c0842ada47a9ac35005de343134472f1d9cf03cbe496db3ab23e9852e2b62ce13ebeeacd830428457cb6151eb58920a7e2f5b762d82fb17f842953ba26180015b005158f18dd4e5668da762f37a7c319a32dab24efbf1a4132a40edc5ec221548d0908332253d22410df5bb66f2fc5dc7638c18227e787f05e34063e90d1b6f5b652a522de042a98b2cfe776f9df05c8a6100ee4be76273df0d288c1375375960ddd9e74da6ddddea9acdffaa2dca88ac475b483f1df152510b9e5e8d282423d4c96a1fab17a75df0d80f45b616683a547f315d159867e4e956c2e54ed1f4213bfb135a6eccd6303947f12582a5e04ffe1552137d642e0cbc4026457aadb9796bae02153ac11c7d0894ce3a73bb3521c2b2e8e576e6418eb7b231656a2ae3f39b869e4ee1d625d3594607f875cc498e52c9997b712bcc5a24d08dc6b16472a3b04ee38c4225fcc9604d7824fca462c248d0cd4d8402c14c582c3604035f33c18ad40594ae98e08046ba0b5aff2c062854c3be0ea7cc7d922c49baecc4ff903dceeb0d244dd031b8bbd6784037fa4d4d031f636eaa07492389506cb916c419e4036704d792fbfa415774c6078cdaa71ba51eaa8b55060eeca590fe7895b333eebd1ce1362c643e0f1818b1f0b2517068f150cbf19829b9f703444c5483f1aec9d5ce108baca591510be3ab3f79b1f05eed842121eb6bd06d0b6051661b299e6a7837cd22788904c87d409e893dac5de06d23cc6b013a3740505a70d558925a11c2f59a39ee615637984b7c13e1f7ec31b098767391fd1158a1945206f7ca76da0132e31e5338bb318e8be548325a19aa17960d32ffc02e3575ce87a22a8d98f0a548e2ca9139925f945ca01029e16d7fa5e6bee4787400ea4059925a2920c428c18025cf879a93824c69409a425929619584d2fa0c65e44859fb6552b2caf8ff864f20ac9175a4bf06c6f3cc9efb2d4679f3a479de955990d726104c648605833e3048a203ada80ce96b144fc63b6890c833e077822b5f4b9da4f29f25a27b07745d9c4464390fb0a7f3e24a678e089bc20ff12005079879e8a412645b55a5b0e06581a0a1b194b4f9599960e4ac642f15ad43cd2f2ddf4edecbffbae0b0392f14e1eb4e4be5a1cf73ef88480413e15be62fabb22aad8ab5de45858d9b78b5dcda1bae2b9e6e02bc3d944b1ce4cbf800a083e24dbfdeb6893c6db7b0f43ead8e016506b0ff938420d3f025b3404631753ef8f7785aca97182d9e3c8120372c4e022b8b10388e59f8eed5bc4eb1ae660abd61bdba4c97e53a640f8d8468b60856105c9b12a75ab26244a24503a1b47c41d56d901428bede51dc3559eacc01a3a069bf905c233a15f4867308092c5b253d32b68f62b7da215fa0fb0cff44409bb002b79702da9a7940b79cdf817c73170a959bc286238a23850e0d53774e4945484b3abdad9c9f1390a8d548e393bff4d1e12fbae63bcec088b96888144a38f219540b03b30153d5ac9709ce08db48cb0528f1de879a9dcd0977acb6dffada844f8aedea104526957daafd0b4a15ab4e13b5a2dd51fd06cd87fb0c22709ec3f44993304bab543f364dc571cef177f2ecebd709bc1c725133e584c8f85514f70b2356eaab89782bfd81b8fd8285d0da63b197495c45ec637ada7aedb28eb1d98de6eebd5afdedac4eebb0cdc6c8bfedd34e95832d6c3d27a235a65411a7fec3b175e0e045d835559880968b3e19ce485b0163b9686129ed4ab1072beef84ed332eace744942112831dc214dd565e027312196921050b17f872a703101672b981680e6e96e865841c05b5c9e3f267da68c8b3a5e72f93423fc35d81807755c7ed76b4c18705b78e7bd36bef93e58132eca1ce0c99d3bfaf7790c170db63c077871e39f2a7a0afa20331927ca31f24511b3364682a5aa126e3498ba9049dfaf166ecc30447b5310e09023fcbf9efb3722e4e8b413117ef530513eaae00a1ba96d80fb5540b0e2515873ec7a51c2eb81b3d6c22eb32247082036ac905315bc2a12b81410e6ec4e8d1c7406d0b905fdc24f741f512029bc8dde3061575c395c0c4fcd739c47e0ca7aa8e604d422c5af2963145e46337075590cfda9b6c00a2b98abbf7a7faa504183fea80a0ba43da26c1b66bfe3bd96a889817f472421bd7db85ff8f0d3f44d01cd005178a73823a5163cc6c4e8e39d504859d493c9f418125671b25302c76635112a8a5fa9a46e80b63124374b2d30a0a0335a9927e12bdbd098815b7aee0dfd04d56f4ca9f85ae1e4b9643b02ebd5546377698e5974c4478d8bd900fb766a8247dbc859f304ab978942a4f36524924d243b2fa9493eb5ef7818a477a0a7646df242f7b6158056e8d100cebd42e4cc9622005dda609cc29dc94aca96867859bedfdb76514bacd5625738d5f9cf13f5301fe66229dc2ffa22a02b800590eb1c7671749615c96f2c28514f24b78081a11657c791345a160e2b87f0f32af7104a70279763b1dc5ad8390380c2a0f5b0cb5351ca4cf577c73611bc32ba0e1d7d16f8b1cd457964c5c02badbde8400423dd4464473519da425bf27154bf4054ba7331b7c4a790e2ea90e85540b9b24cc2c2d1a3873433170573d46ce4373de099550e5851d7610fbb2cd16be886f7c258529e005f48cca9c497cfde7a788a99fd19c612154ab9943e4a6c9a6a2cffc4a25528b9bd6c78add660c70c043bff1851bb6d96fe21784b5bcb536939cf2ed3936df74d896a5a932fb1983e6626851545720c4a947e6c4efec3904eeb871a7dd82e2e97e979a29ff0ca793c067f6fa3d1a2e9e4fb92082ce694cb02c429e463301b64584e631fa17012be34f991564acefb19abfb6e88ae5377a572e4cb8ff7db4b2d0d84c90173e183f846bf8a427e539d56c1753e184cdad5da72e75e30d37617bc80fad890322a534d6a57b5d97ebfb0d517f0a56a9be18ee32e1cf21329cb513040bd5f7ec04e554ef26106ac2fe0dcfffd8150212d55becc089d28b6fa7e5ed3575568aa4be8656fc91bf3d74d0c907011970d5a1b5bb7b0d98236fbcced3ae38edadce2c833e1edfa04a6b1a5486d49380e6fc1a86e44935a971e3f51ac63a86fffd1f90666a69dcf7eb5f630bb78513f32c8937c74d58f593f20b0cda9bd538eeb70c2f6ba14a8a805352c493c300eb0ad4d7acccac382fa0652fc77820f294e1d1bebd97fb79d45f34fb8ec9b0d4bfdddc36a08f7146fdf8425b9b543093718db31ee7635fc4220ac939cdf781621bbee2554ab7eb2a802679cfe22cdfd3ffd85a2a427422e42930ba9ddb1b588fb989efb182a3876df1661a408a84f848fd561efd0129aeddd17856e674a5a2642206e27658653b43b27000814484869ad7acc3d6bc33bcb42ca53d2cd9a528c52f5a10aea254d90aa4eaaa8ab2004322b3a9250365e6101d64855e9d736d887a3dbe9f4ddde0195f5d63557cfc78a2330b4cb4231bb56ce44e236a47f693dd054c2bf36c4631e192120c24ef37948938a64863fd4324699a7553fd765a5fd31e0212106ddf3f5475230bd21b842762fbdb96d0159477e42cf4a6cfd759a374ca7e93fdf74698bbe266f46f47974d3404380044355064b4e859564e634b5300d8b70cfb3b8150b41fed501a6d5773910edb59fdf19cbd0229682cdb9a71ebaffdee7cfdc3255d4b374c348bec202afdc0239e77af85726ea76475c2b1745a50289010f459af825b0e739df0ba241345c2827166226abf9ef2b6dad2f27c47e2a46839a55b9b73791538609949fe7c14166c93d416a126c1026e5249a5ddbb9089362e7f6d965a349f655a264bde3f1c79ef6946ca36cbd7ce3cd739b39c84374beb3d3c005cc491571d2289c93603437600b2e18a6a7cce67db8a24b7560a05244dfe3f44f450f2f897f776081e493bed5c188994944a800ae436f725450cc099e294924a489a0c61071759b3a3b2fe422e2cd74b45d3581dc7cd7fdeb5e229eb74579a234b896449593fb2c8dcd9160e498368d0b810183272d2980a610c6c8b758f983a2c01a3e5508010b39f02b4bbc5f14a8b70035cffd7978377f4145b20b85f262742d047fdf6adba13e62d56135d652aa764c595809f9c54411ff6f5355802893d5f0592a21497687a2720dd69468a81f2413d4de71547017226f99c403961b60c4b6054547fda8982e9027ff49a2d0312e875c2dcfa53d41fca604cea230c17308559e81cc8e99c294200b56789c4dd08130fb8489a391e047f66692231b1237aaae16101278f8f87ec4058bd5df7c8dd4c9556cb56f9185ed3a7492d138cc5b4b1a7d4477be304a8ac7c3045e9ab8c09d478a1108fcb953aa209f8212b6d33968422ea6ff03408a68e0520f0e59b8f86016c94df4c97e0d067213c2b90e8b15ffec62f6b0d49f53a588478aa82e3a670825f1614426d80f72b4507b2e5410a7a309787c39460b195f9a91a0c8bb9445d48f0c827ba0624681df8bc14dda459937bd4a2e373ff27840f09d84ad7b08a78af5ca66345ffc268e01fc951e29bd3aa9c21a9bb7fef23c8f0affaa467475ceaf2831082a267157137cffbb1f6806a391b05489fe4825eef04e1fbad24f213b83f3c531158745a714eae6c20e3369aad620aaa0a88ec96e88a50046b969d09277b923988fa4458a4d326dd628113ac818fc02ef581fb07440b57f898aae5ea8e7c04240e8b83ac3808fa38fafdc9107822d579167604e3e83154ac2ef9f1ab741dcd91e2d026ec74d4932b69f7daf4d9b84762cfcb5274769cc42785d829c4425834af50fa241eb3c530ccdbe13337e0001ce46ebbcc3e1fab620e1692bd917bf102ac553b9bee40a67db008ff8ab32a87beec0738a176d8012a90a3ca6ca019452c6f84aba30efad4c38eeef5b5a2dcc6e8020e371b8c4461575645d9d0088138b88e7636e6bded09d395ea6f01c67b766b9303607e76668fba11288edbb64464bba9f1b976e701cfcd42bdd648057f0dcaea07c5e5b7058c90556467d6f154302cc3debc190387eaf02f1c9d198f582d5bf7272232423c55b5844387c0ce44e6a14438204092bc8143f7ed477fe16349b54dec254c2337113177f3aff5a0a9330adaf4b1df4df7e063b1efcc293fdeab8d267f6e08afbe94a32dc5983592cc7263ecdc39ff9a772d5daf802bc3f6e6108cb244c5d7048eafa81944a84bf53ca5d80aa3c868dc5280914ed5d7cee13fe9b218aaac06deca61437516d16aafbc5960524e4306e3c63ebb2da76da381b1045ef48aa0f8ce85c9ecdf8dc5c762f53d03fce05bd4cd57c6a07fd76ab8a3c0cb222f4a37c41a43978f72c3e520404325c90e37d49d0010e0dafab3a2203a94ee56013d5d3e26e9c0bf10a72ed3e662b78e52d2b26e16f83316e3d4e3b7a8a246c0d65c1063a6e5455feaa9ed558c787fb877f9f8028875255f1b61faf1aa9a2368a1a30b265438c1fe4f94486173e8bdee9203f3a1145a6e8918b2ca75f73bf94db4fb7044adf74b354ca0cffc2dd0400f71b2ca687e4777245304ab80ee2eeb073b8ee100a69fcd0c0e14b107c1180bba3fdb6972365c058fd9d4d6ccd69623368407d4278b6102866bce8ab1dc91dcb885629349c1c3c69ddb1742ba15d77ad2b51a1de5f564c8b84a1a4d12b1f5725f05e010aa222d792258c92a4cf9c4fa077804b61ee7cb82824d2de91f8cd7fa35f3f3073a0f165967002fec21fe72370a271f6854634b220610fb17c4808258ae24c438216aa6dc3a9a87cdb07f29edc443cfac95d220749363b8c7d3cae7e86a1b622fc7ece8f546d1e9fa30095b48de9b9ffd09bd8913e76d7233def6a859de0982ef28b487a68e3d62b2ad05ec336962d90847b612f94cfb938495b3ca8d89b575d5b4a35a7349bd6d1ab26a162281f1480cdfbdefe07d636b7c6ac6f6f4fa8cc3f5ce111680637ed5f3d16338012d183cab5ef85437f40d1ff5eb7be8433587e1a710940b86dd19a5808840870460bed53266adaf48d112967eefb72c0640bf74f61baca8e0ff5c70539d52ef29e40e15ecf9793314103080bf18b7a6f6fbdc22689ed58d52b4226fdfcdc222f9efad3ceb381c24cce8b292b5e7811974dafc2c2beb05c81202d2252b7b70bd6ad061d3149dc9d7327e318aa2a8cbae8a63613293d16d5ab62965975a500683174ff33115739f0b135797906d8384b71c07a041eee92894dc21f2b8230412112d263411bf5f1aaaa1f47bb875df650c6bea08af4fba42ec97945d2dc118b896cd2d3ff6cd881c392d31adb4e873e6d988b2772d7f446bc9ad3f7dcb79d5eb21f4a034e11566dd1ae39838890bf786f32d8c518182a8d353e1d014f1149def4fafff4733850df5dcd87020beae4f8b5c92504259f6ce552516dd10fe60b137308ecc8d44680a04ec9b01848129302322d9cd23e7e656cb31eaa3742dcca25c9d71250636e589ff9b278ea22792da2ec92c449a035f389c909e21beb2c4c835ad656b3bbd339eff6601485fceba7a44e1ab3acb29029cf9d5e901db656265af9c83240a1d4b03bc18a43d565bf1b672c807929a599e26759ed806ecd28310afcf01633d35668cf7f300fcf6494d220e9200fd95fc0a8487405e59b80fcad19ec37fc98997cefcfe9a295b0f8453d46cbb7dfb4e725e663ade9927838179a9e517f46f5ff52cc45108063430c8742da13175ae10dcc48309433441d6724ce66392e98640a7d4334ebc0a34df7232679c5458be06904c59d4f4c455194000ae36f428d18f2fe740f0ae65117312190b7ec7de98feff874e5aeb741baf59fd4e03749137234ad993d5abb39030ce4289bedb5f0e575a1b185efd56a170e54d88d3d4528aabfdee628995e2c98d6e890d1303c4f70245cdbd4058bd341675aeddbd376424e3b562b62af62750945a0a51a74ec7a39e5c4af0e126f304a93c166c4803f04bfa858091589f817453dc496d84294346bee7b8f20f0fe6052dd401b157db1cfb8031d3e6053197fa98e77a0c7a83a7282e8da149fa002e43d6f735e62b70f925ffe56894bc27e813915ca1e9f5fffa7a6f001874c0e2731d50868b42dbc544e0834015cb4db968a428b047de33947bd7456978a5d0eb5ee72c35c31c1e7d834098b4a0d299351332a09a3abb5e9a332726772e01b2a157f4c990e664e13168a235df7957215d278d18d4aae9626d032e065d9b69945279e89583b3fc4a7aeee4c515c16c6f33bcfc27e541890e60e7e4a07864ee4459c153e5e0ee521b8922429b6ee318659a6822d6c15ab93647ef6528b2d39426e69192809ed4990596f4543d683d060ff6bafc5943566b58e588b49e9f997180b4f34d98fab8157cc56b6b5d602466d03a1d20174df8652c18dde6e23f8b5f85eaa6232e1456770d822f48a154b9245e46af4c135877f0a308806b92ce8318d43d846fb112dace76a4fce1af98082478ee2f37fa7e8fd5d2979323307fab7f73e362d6bbc38079313f61aa29f95b136d290619b2f838264d1a9c7f2a37252466031a36f9564dc353ebd6ad0e02cf2904bf649ba4458e0b8c7665e318a40e37f6bfaa4c2ba4e97a3dec6c6a63f9e2dea3e2fe6ffef885899fa48d5ded5880316b5c680011a19f0905e848d3c1a2f7cb580e53e0ef76ca998c392474f9b884cfe06a575a5b0b82b41fb23e05aa6bf7ecfa90ddfb1e386da4516a2c492558a1e500e7d3273c2f89cd9c580bae340168fc3d2bb0db0421cdf430fd747b7af7a28a896519e96f83009d664191987122205e226f4f01e8fb4791d02826ebbaf8d7d52034e5c06f2b44636bcfa2ce1ebca0ecb0958b1f873a7ab4fb16b03315e5409303e02c0159f09b9992e2e9eb49f92cfb9bd69624f4d7242ca9a55445236704507d2488056d0baffb48e02251460f8f3fe9310eb1ee449a0fb88f0acbea44957e20984f0db5b3196a14753188972884932dbd0d9ca818d0d5cd97c49589a3866bebb1922cc3432b2e0ef38700a55ca270c6a7faa568a62cb3a86fbc24c435368cc7fe03ce09f246c6c3404fdff5b63ff1e684ff8fa25da28ea6492dd0f40361aef671df2b1cbfab9a5c88ee81d864b9da0536cb54796d57cfa19c3a2c97f87bc1548874137fe17f02f4ed82eec2aa30be330157ca52494ae9f842fcc0b8bdac94899071593ed2ef63790805465c285a80a371439a4d1cf5ae5f7b6ffd3f195dc7c1320f0838a501f9d24f0d3b6eeed28b28b49b50597290f6f9becff35d0815882a98bcfedb688ec02029a60bc56f1fbedc0e53df529a85f85b96a8a511f1f0fd58fa9b5ca771056b7608dec0b73866169cb07ae1961b325fbda6ec6c53a0a611f7adf3fc77f0d6664b1c346919437f59c59186b6be84ec9880c66e4425f8edead9e10a2cc61f10fe042b2ecae62de33e2d9169cd74700e3664a8c09e3e73a841de9cc40853f8b4a63c5c76035708c508ab1a472188f1fe9b0eca79763290e081af4d9183fd10825f80a3bb64f264fbef105ad356edc13107ca3338302e33de192e514420102bea1f98a23014783e6cca524409065a104cf1a7c1b4c356322aea69263fe751967273968758815821c9f00da61abd3afd100a44a147cd976a4f75eeb16b0b18ffe230424d92c00151babca5807f1b5db812e049bcbeb99b9f9e6d1ac44655b2aab55b26b059e68aefa4854e75e74e6c2144199aca45243824a4e79e570c1d96ae6385c5bcf99e17d8b8767b1dbb47d100651d282cd8433e6f4c318496ceebbd43da27ad4df30ecb619b08341a579637a003a4f8a375eec4fa00dba6356365306356c2bc2bcf3187be9c2fa9bfb3d1971d4ee0f06d234e9855150623a868268cd25f1ce12321aba050388818891faacccc47d338222e87c34180adf0ae3f870b54fff0f07d3159540d800b119ea2d35ddfbf22c1f4cad28cf3a361cfa035e108248cfaea9bbe387be5ce7f5f6d371b47491300a1adabdb3666e613e8a9938238d61fbfd3f18b13b8e9904c50cde4f2db8e2a811a214026acb34b28596377a811b79f48f610b77410f341e654dae3da2865d0bb46a56967e309a30573f9bc0ce3fa727fabf04f407cf4cb11baf5d6fb1d230ab40e1536d3583257d9c0234db6d75d0fbd1576d6d3e0ef8e190346b0d8951a91fd444a60cd5c02ee090c98a9632d08120cf443a62ef7147d4023cababfc69c28d9cbda0d4925eadffb28c2a066c1832919c1189fd72ee8192387a855e5b72eca4e1b190403b1c87893204a7d302d54bc81e66cf7171dd116ecb181d540c2eec0a0fc6c2c222dd804c0a70c4b0656db3d5d114c499081158d28aa52c9eae7cc560c22796dcff491afaa0b80a501e928b6cdb19e0886db36f682bcc2e6135710732a9ad8138c9975fa70d84e743a6be3fe2bbd46f9cd550c11257444db2e2aeb8910e0a9be913a909681cb6c54e7e9fe67eb12143678c98ea158ff10d81f0e639e2a93a03449392e0d466df8729f82eba816f72b1759723c633f6cac141b963a88b0683623326f85dfc7baacccf70e179f5465f8625c68d85b1cba54621a39d1af33908e2435553d5b1254f82a7bd6db3061b74c59e8a10d705e5a4dffbd125a837e3f9b5fc488c683f0800a3120f8f219ebc1097b524baf58523bf33c7813dae5186df7c25211b35440e238cf4ef69227a36995251d83cb955eb5370393ea05716dcf41f2f0b6824c240a6f7ed9deade66142276e352ce12cab40cf289ea50e0b40cab5db7aaf289da799b5e9c8ef0f852709e5c73cf1369dfe6ee7d96b4a0f97ed85c6fae88d5f5976ea1f936415818abeab1f1e2ba84370e2af95d29d97c52934a488d0c9c27b58773afa4bac7a9238ae52303a37f68d6ed3286b34700cf6a49610cd6a176ecf1fcfd2a9379dcad741fc898b3f30b8a33ec9db6f1654ab4aac8b40b75c1714b98c64d0ea7189f965d654be73c97e0bab99bf8e95a6664e92c9f93c07fb602d4ada04cc1d4c768a597b3c3d3e0262fb514b6b69ec3fd78f1fe44a98d26c6c21d531da2681e79f0c7d8b2b397f4af3ecd74d7e8b1b809c819aef7d47ffe37033c5be6fb4f767e8f6b1ce4672af0969982eaa3902cf3c2387ba236a2bb9cbaca95371be3c2aac1d527060dcb00427f644b66e55425dcb023f7051f49205c3cbacafd422b1e28fc483586a6ad10a7540149d63aea3baa3dd13996ec707b0c176f0f18a01efee82ab51d5a92691ad4e88412c30af400cdd2a8771b58d91f18423d518e4828ba11d2f133b60bb97a9515ef285516554e140cb250108f7c8410d36106fa6f054f3808cc0c2936ebfd4a54d232573a2090b8f7c1f24c0f6d129fb0f856c628878aaeb1f428395fc14e3afe67f2f52edcfbdcef4d11f261a1f63c9c1070edea5d4c19aaa3fce6824172aedc428b0e4f37d7d4ed8c289b75da8bf6e36e6a3e8a25a9f1cf0f1c363e7851f463d88810ae819b0252632c229f8d3698c1873bdb5d8ab4469e5c724e8057c4696d5ca46b1236ec87013959e2c25255a8ae32995187e4cb9a855048d036fcca157ec3aa4228b244f06ad68a109268dbad6cf590e6631d77f313de72d51dea2877f8ef7e6e389fed2839223a69c332bbe548be0b39668bf1d08dcdd891532ba185f5570b5b97376354e61a5f57468a8ba1e27e5aab5f567e77677b8c370a4218f2d22b364af7e1c74a1597aff31088a51f6b11b65562db8d4e6a9a307680aab503541499c8ff3a3df41352c126709675fcf6705cc9d69351b738602b64ae957235182c637b02036a63cb2b68fe702a3a45d0f922819983cdb657b6d93bd76f97160bf5fcc7171895ed07b47e05a352729d8104f282e85cb980682eeaef1a45205361646a78c4f638adc230939417f4b88998187ea3bd6331fbe2694a606c63c4cb46d748c51d372b81b81a7272e39e41f223b4f29c993875967711d96ac91be8eb57a83e6f3e9af28e13f5c94387a2ac79244cb54123d9293dc695f4c1729cb811932512c8de440103f6ee575b4ceda7c3934b9452c2a827585627452f4e951351e7c393c55345dc7c3b329af5e393b273dce71ca37703d8a3d9919534520935a4210f989ed89703ee5e2cf13f95c33752270f55904d0b586ab18e28f910006c1cbbbe9f71cd9e62355f86076257225822925d1265247d894814f0d6c6fdd4df4976a36b4d71414b2192327a63e6e8121d98eb3886ab85ce948c629f803e3df9fa0085346c623289121ad29704a8c1c63b9bc207100aa25df6825282a95993c9da0d6a8b6c6d1fde90409b79c5d4e71f47d4803d60315aa6ec6addccea5ab6d26c87a66cf012096ca51419d09258aee663907248f206acdb80bbcfe6052e41ffc580f7a8bb8b4be3319886a79c98061d128974cac21321e7e8e96822fbdc262823bc044aec29d60420c1a68f865a6bb02f81414f7bd145ca8ae5c13f0a799b690af9bd36470a45217f8b2234e967c5a3d1f1b3144f41668774717fc915dd18e107c4fec67a066483719a806fa3ee04c67c8d578294d70a50d76a02fa283d3e28d83141ec2354ec5ff8805cb5235290a10e6dbd6637729aa52d234ce33536fed6f873eb07b54ed55e191b8833088c20ad0c1d7d1867f082bdb486b9ab9c2be406b777addd93ce208143ee872aad4d539299c0ed7a259888ba4229997987308228010381f37262b8f71938a7134d865ff5e16cd9e218922e461d8b16bb6d1da2df672a2a237fab6de253f31d34f2edc2c7b4f5c2ed86e9d66466c13a056af2db12888029f7c61ea0c012c1b6d25aa9ee4ce02ba485a715b501c55a503583340223e78272d28e7f34ef624a4202331006e0be003dd3281ae505e08028536f0ef4e41701cb54087d203290b94497fa6a045e4f16b3613f58dec2951717effa1ba97533d3bb5a3932f09915bee2db794322529030c074807d10628ee73c441c51f2820d8aebcfd888217e34566f81c5580edc71e0f4686b71f65fa145371261551a95853a44d2b94783111e786cf0e86a107a342489f628780cf3ea4132282edbb4f9b52bc7d0ff262224f8ccfae046cdf7b1eccbf7dcfe9535ce1cfaee32aab6283171385607cf61ba41e0c8ab7ef3cfa245107f8ec3d7ca070b07d47b5e9e4edfb075e8ca431c0670701b6ef340f2665d527992ac0e71e9249f98c509b50a8f06224ce8bcf6d85a0076382a74fb28bf9dc3d9d10fbad6a93c9db6f245e8ce481f9dc41c0f67be7c194de7edff449da6073ec89b7df2b783152e8e573b700db6f9b07437afbbda34f1375fadc34a8e661bf65da340ac38b9934a6cf7089220fa604a84f3355c36718945aa58684b48964092f66e2b8b0f3edec08f019ee743c5d0fb67ec48b993c03f80c7180edc31f0f66e4edc39a3ecd95003e439b1b5bdf3e0cc18b99422e9fe108b07d683f883e59a8169f5f1114dc61ff87365d6fdf022fc6a2812de407e0f3cb02b6efc383b96f7f873e592d9f9f0f108bcf8f08d87e8d1763e1b0e8930580cf2e5ed8d7a14dd8dba7f160e2dbe7da247a1a3ecf80ed3b0bcbe78bedf6d9b1fd152fc6e2597930fdd67e6c137dfbf0c55842b0857c6b7f8595617570a45a4cc590c2813195e7069cce4f3585802ed5a53a1e18537162f060592b8e35d61885604cad30a21096d8ea00a8156a85a2813198900168b0ec0a90ea525d0a07c6603c2f70b04cc574a92ed5f1c0180c0766cb8d6136484c4a211883d1bc48217cad4ea8156a85a2813197908926e5a7abab21d5a5ba140e8cb9785ce0747eba5204e8525daae3813117ce0078b0fcb9f2d38512c05ca1e64afe148231178dcb1442f989ae5aa056a8158a06c650a100d0a4fc44bb9654aa4be1c018cac302a7f3134d01a04b7529f9160f8ca13890061e2cdf5af989a29c453ecb96e9b556b54708c6501ad802e545364b08af44d416515c44cdb079112cbf6518ad80aacab312c3b66dda06557545852845ad2a6a8a5a71b0fc18deaa5454a551b1a1526ca84a83e56f28b29395107682590cd3522a0a86a1c04e30dbf160f96f023349e17c2e5d0c07cb57295d273014466302439960288ba1301a2c5f05898e5642a4955d8d5642587e8a128ba4e32175f6ea3a1e2c3f654e4fe1a452291c2c1f6544e21c652ed44553572579a12e1a2c1fc5112e5d09ad562b212cbf5e282eb43d58f634273b9ecebb8e07cbafd8499456d59ca4299c52eada70b0fcab35aea2525b20144569600cb6822de4974a2814a634d802a93d3ed80ac6d423bc16f2e5aad4d59c9a84967f02eb6ef49345e1d7a66c5371a6b5c61102d95995a4da64fd49c6cbb740ac8edd71771da7a938bfd6ae39f923adf6549f949faa9066912f8465aa0ec13255bbd722b1eede8aca155579d41aa87a2aec6daa0de0ae533b67915f2add6b43e9bb0e965f479a48b31ed7dda2bf765d9b80657794663bb8d4a6da643723663e868c977f72c4cd46c878ebad871b10d1fe68d3932c999c3615db6dca59a41465dbd71fd8d2a04a8606799aa5a735e8038540ab93f213544115b429df32e79bf3e041ed7afae43ace52759c0754f9c9fe6816f9120896726ed68e50301a76804877ade8eaa6f42b9ab2226d9b5c6dda0c48ffb648229148d7f5b1360ef659ae563c6afa86d2be691cfa51eb1bfaf4baae97df385a78a71f9ca4f50f7eead48d24c27d7f7a828f85f4f2913ecb22a4cf254dfa3c69e4fc975ad05eb64d9bfaa954e21281a5f6797a00fb98353db0e4f11cc4017e9c017f5a1d86c017d4e8b6d21c095aa4cf484372258914e1623240d092b627a18bfebffde5ef70319148442f6c45daa1010e78a66b3389c1849a98fc4563d84818bdb7b46a27d2260baf2c2d5b71471667a24c94993313356b64914933794c99b903d3a06155aea631ba23daa6dd153541e3eafaa7711ba7395296c2f20633e2a634275fe551cc3fc5f0f2638d4d9b505e7ebca16993ed81e5e7e8034b14963ce64854a1f461ac28a51b8d1baeede51009847db4497bf9d68d9fb697bfa24486fafa1834af2395a743e386c6e91b3f750d8c4486529a53d1340f6a4ea8594a9fdf0a484eff684ef68dfcef1a227d43e525b14ba66819e214b95279b9e251d3e3e4e4e4df3bf9a8a534a7b2959a93f2e95da1f82c57284a58224322d1aa4d96c890287b0b85969dc8d56a6561abd50e7204c36a1a9557e66097a2b92fd4b7923b85e7573af2a38f0f816aef6893e7e079ef0673ee43adadfb36c3fffedddc7ad7624e9b2c20100af34b54782d4025f0fc67c5ccb5872deb47da1bd1e27c4aefadf5fd905513ff12124b83401e6984f4b96f4a7f4ba41117356e4d6bcec7465c300cc77af71e6aad10e57fbf5a1626e310154fec8942868c543b3a2211bc378328540ff7e14fa7352369104873f35de67fa97427cc696e3e46846bbda49f7ba6712e0cf2f9243fbf79748ff6f1a387406dba3f1f4e149eef736eb16ff07c126d072067995fb71a6c9b235b07198d92cfcfe7afbff160e693c027adc0f047eee8b1c0177d49c3cceb936879062c7a9296bf84457fb57cb1080828c36ea514cd8717fcb677e1e64fece6663893e3f2aa737bd79499f1b60c61387d7ff17b7f0fe6fcc032e3a8ee0fb6fbfc0177b4e901cd49eebf5492c2cdfef067dae4d0ebfff39cd7427e6eb9e12cf2a2b4f4299f3304cc915a8ec31967914f6974aa510d0849370eceb4ec99e922c3a5089eef13f2c898d3ce683ce79cd3e7a4f4de52a9e36b89315ec98e067ee0cce1c7dd90d747be0dcadc9236b9ce852427c8000637c618638c52c61a39e79c724a29a57c2a295f4a29a59c539ea0c36f870815aea3b7f2fa716c538c1af081df8e067ae0b6f0bd53c630246188c2c559ae18bfb7a7c26270637631651adfe56347af59c2cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf129c3a848f05aeb650df6887081ef8ed1041836b6725213a3aa74bc75976e20bfce8166282e8ca06fc60e2a5e3274a5730e6aad1b974746ae88ad698dc135c44563cb8a464cbf106c71d788844dab20361d514807c3a20f8de2d7b0d268245a22d77876f82a09393c2c1d90cf6f1c22a086808c630d8035b404b0cad8228cd5142a7d299b2c605bb74ca094ae455035bb89faea01a68e1f8b2e2f85750bcb8807de268751802cf8f53c67f38c71dec510705eff4e280cfd1050df81c67c080cff1881d9af4fdaec43be1b0f13912b180cfd1030af81c43a043930479273893c3676f42023ebb146a7cf62bd0f8ec447a1eccb5c13bc1140e9f3d07333ebb0f647c7621dcf023dfef1f7827e84382189f1d052e80d1222abc53cbb4160cf0b99570225ef4088977ea9b22c07cee204d78c9be3b18983eb70d3a072eda86e19d7aa88b18c0e7fe4093c0a5b1770885007c86546861f195fb0c7b601168f80c91803bc0fdf088777295b3f8ca8330c070053004efe42bfa7dc5008b7861c0fdb880fb2df03ce5f30b4ad15de39da28e0f0f66bef5289fdf4e0170d7d0823bffc9c88915ef14571ecc3b5d41fde2d4015ba60dd818f072e9d8d8325dc096af0274d8b20abe6a72d8324dc0966f8d99ab06fbd3d8b28ad3150e5ba633b67c65a4fc6fd8b20aa62a046c99c6d8f2c5d007c696559cda1c60cbd4005bbe0590f17fb165156b2866cb1466cbd7869b21ecffd2f9c9ea4e5ba6a62ddf1abaf377b16515ff1ef2937543802dd3016cf90aa0876eb0bfcb9655dc6ab1651a802ddf161b7f165b56993e00d832a561cb9745e583fd3957d119b64caf0c5bbe2b56b6ac126bfc34679c45c7fd63d8b24a69db72e96a5bbe5f4355b64c6f8a2d97fc53b6ace232c7af203f49942dab94506cb9744fb67c73b07f29d2135ba6d7c496afc9e65fb265954a9ae14b37f6883d482e9deb0997cfd55d2a1ff22112ad31e95e35170eaed47573d9788ff78cac2e105c3397cc502af55a6063d514807c3aa7719ad702a94d0f7252374141af053616698d6b36444b40517486caececbc962e0b41403e6dd336af0536aed84d10747252458a544bc652815563a1ac191f1fad31a635aed4c70bab20a01f3fae9411543a3910f55ab49e73661a316d66cd4409f9f05a2ca025865641345e0b6c3c7394d0a974f20a5e792d50bb825e8b53019ba90146c48d12d85f0bd8319fdbdf38be9dd7f392a5748ddd6d6dc4ad1b2ae1f65f412f380b00c37f1d26e33a2bec04c0f0f3f50397d742b3924ff7f2e2fac1c9afa094190c1573e9c016feb906fcde4590b3f8e9c51ce0b1e8c01837c11e040c33681330c4c32739410c803886d0938424924822892492482289249248a2a7a7a7a727489020418204091224489020417a7a7a7a7a80e0e08bdbb3e69cd2f67facbbaf39dadc0edeee5b7479fd4218c1860dbbdb4b34e08e1c6c11fd71d6e3e0472933d7011fd83348298425b810461610421c5c941912eee0a08e3c04ee8de2cc02d2c01923c487abb88a6e5b4efff6f6e601dba39c16bdb09a59d1e8461d5c49242526264e944e50a0a4a450a1f275cb2c901a2bb06d13fc95ecc591e8df977c6e47ef2db98db3bc15e4811676b5a7552a55112258de2a04f3c1cd35956ad38f3ecda8dd9c73c5cf883369f839e764f9c933ffe79c53e5a7d0dc7ece39b59f9266a6fc9c73a2fcc4992a7ece3953fce499a59f73ce133f85268a9f73ce939f93669af839e72cf989334d7e4e28846792fc9c735e3f1f1e311ffe9873f64f8b2688392d30477efe0f3e3c18ece79cf36bcc39a90b3c75a0f160eecfe7da447fce49ef149af6e714fda434b3fe7c122b63662ac3e7cba57c15c3f3744242945ed4aae2a056a815aad2e0a4ba549712e2e9525daae311c270502bd40a85d1e0a4ba549712e2e9525daae311ba702c1ad40ab5425d3438a92ed5a584788456509c88533bd47f090d4860597133c8b0625bd99af42af5ab4aad239bd56a7d959b8204941494918a9aa213ea523a944e543ac1934aa52c8a139cd289cc04124ae66ab5aa2656ec844ab60e2399578aa7ae521744c285bafa1a01025aad562b12c530ac07eb84ae6e76b1eb919175533ca9d44c453a2d90e624ce08355151d6fe6181fcc899217d703b3f79778a013ab8dfbfcac0c5dc40b9817203c9b8f3c49bf7c9ae42f90c8b9512eff16cdb36baadac68a08a23db566c8d195862ac5f6d9b78cb872249be26b61e62b2358bddb24d867ea658b5f65bf4d8c5304c84fd08135d1856c230ec5f300cc330158661d80a6b03cb300c1b619f6536db36ec57ac0dcc6e99bec665aec5af9a74b9dc82171011286b99190f68dfc2229b9aef34673da964bda7721c05af85f552731f6ed35c8fd762fda5794d0fe92015604b88070d1120f7eec138befc725739be9ca54df5af872fc655b085f5d7257a1d238ceb76d917f8651bb0b523fbd78e46cfa7b30fa3edbf88c1f66d78f65f1e4cf69ccb73a5b7d65a3bfacddab0236bed6da03659a3cff747234dc5d6b87894c17bf568eba11b37eeeeeeeeee14220d4773d68e14a601bf9d1d3b706dc9a0d6d5dd5bd27e6bc3bb5b3e20da323dc42573c9a00c974f371c2ab606dc7a78411d0528642e734b5700596c53b7d64380825c1577747dabd4e3f28834d87a0f03f6d67b910793fd09764bb4a98788c0959065ddcfded36f9f5a0f7bee96290e15fd76460e020011212482adef7f449ab3dedb0880690f765d775df52fecaa29fe22824d19d49cd2928a7b15150b88e276dfdeabb036e2b7a7b0394420efba96b9b7e42d033bd85d5b7dc97c4a805282b0f5796585adcf2c86b0f5d9854be7f1eaebba2836e3c2e8735c749159efc2d65881614f733ab82eb2a9c19986321a8ed7627d9440047131055e8bf513782daca7253ff9d0400d94527ab841d4095bc33758037974479f188ce1a14df11ba8814a1d84ad066a16eb9d62b481eaf67a4890095b636e60083e65d87c92f662b8e638d22cbb5e1ecc8b7938db707277ef5cd5f7453ee9b30b49c9e73739f1fe36bc98873d06fb9fd05cd52cd69bd05eb39868af4bb4d724da6b967b9d581bd70bd5e87aff2ccaaeb23eb3ef9b095b43b40901f168cba2cffefa92b571bddd8480d8ca0dd49dbc806ed796657fd9172262fa3a4698feccb62dfbe0da5582eb3fe48558369ccb73a5bf256abdebb8676f626dbc601d3a2fb4151a3ea07ef8c022363b1d4493eaf1a1d8836465cc2fb136e8666b6ca08d7f9eef323b50cd593f626b4cd18b8b5bcf592aac0a373710ac5fa1f530c0246b837edb401ff06644e4f65e204e833c604de62a958a7ef6b93fdb20ca6e225ba33efd5a3558a3411e1a8684f5385e8b8cd53b66b217d65b169411f2febd7f3971c1cd1015a3f3bc16eb5d7315156e4301e8c1d0b7fe4f4e464630eda71ad66d656aa36e596e686b60dfcfe2806d39aab5411e0d51f4ad9a4a9b52d883c34f2faacee781e80e1d6c6bd8ef4d083883bee8dbf6534da40501b1dd2a606d9ab35cbc96feaa615aae01f70b6b63b50c61e043cb2f3630959203adac657a47cfc86073a0df3f73bddea2b7f40de45a0f1a862171dd7a5751eba9c521c7cd9ab338f496a36e3d34677d1010635b059ab39ee26813f6d657a08736c1ae39eb5d50b8046cc11e68e3271774e7f2942ceb61e7a7d856a79456defa6aca30f9aec527693388df9a105407c4f45dfcd4439ac57acb22a263fd73eb69b065f5f710cbfaab866f77cb9ab3626a0e513b889c278481094c880113567244e1f8f01f7eff0fec8fe1e0c6187d6896f8cfce90985ef49ff4d15c3f0ce8f63b90ee1a21a59ecbe3f11d070f26057ec48be91eb045a43f7aa8843390814747fa730f79ce83f18f5f3dbeb50ab0a2e7a9ccd6803fa7c43e37c8a239df62ebf0dec9538f257ece155d8563f7e0e6e87ffc706e9bad6f5e6f140c77c86ddae4a34fa236cd7fa6d6e91cec6345e2c1643a4676cb360cd70cbf836c0df8edbe6aa11eea22cdd243d60a366c070252e2e61ec2407f12bd03f99066f1d47391dfeb25ffe71efdc2cdd140cd4521cdb54fdcd1a6dc3e38c69907f3e2eb08a988c0e0e6f669216d7a40fdfa6d9d133ba7c473771c0806d2dc0adc5f61cc71078edf714773d1815e4b7c2ddc1cfe41c0e8369d83e3bb4c9b72f4c38f45dab4e3c1581fdf8b7830d8d7c7a696eb9661b5727f07d91add58136eee148e36387e074520ed3ad973d09bff682efa909eff2ba594cc7f747417f22ba594d83ee2b74d740b95dab13488baa2ff782dd18174f780317ec46be142bea7c48363dcc131e2e0b5c4cf9d021c1d08b729fbcf8fef400f060a0fa63f7a50ff7c9f5601fd107e77f398f08a150211718d1de18b52045142521e3f72ffa5d2bd310ee98e55d4dd16ca4420627cb738484c9b8b31fa77e0765140bfbf9874cc8781c4b572e8c8e111f54bd42d36d78197977effbfd868683a6834248adc66c8194360199d469bdec97b97054f0144abc6af1076d6d15dca2d12c131ca2c6e2e566c683debbdb8b381d4fcf93466493ea5f7cea759d765bc975da0816a25c6faa86b367b15c7b8b9d4803ebc60bfc00bce01ff6db9befd5a3d4f250299cbe630f271e4e343ab80918f4ffa112d3617738ed10701314a0e2bd8be7decddc64fd481c41b2cff3d18205bf66105dfa76f227a97f994de2bdfcc33e0fa511b799296e3d3217864cb2b303a5e6d8461ecf15ae88ff0d54443227c35db63f1d5b25486af569da6e2ab614118bedab573e1abd1b6a1f86a56110b5f6dfa5c4dfe90f86a115fcd7d782df461340dc7f7da565ecbd5e0c32f36873b476c03eebf2c2cee9725ee8e706b10c67af2664c7d4b2028094ae86eef97ed36600b51227d8ba0768a90c10f085fd0c119ce7c21855dd2173443697c23f4af87330f0666cdd1871e78384a14297df8a1b9eeee255cf93cf0e00ca421b826701a0ce10cf4ad873892d29c8c0f67e2d4749823a5e6766008652e9f1e2285e77baf037ec6009eff1e4cb6e9784b6023489f678eecb9e69eb5d65abbc9b7810bf17b59a6e9c8b44dea3004ceb6f758e4b965f266737ea6e5f9d9c3f079b76cd3613dbc4071cb14da06bc18a2a79848241a899c625f479a68341a8946196d2b63e44764ec70b70ddccfc01031eed76967e8804be022e0ec7ff2e0ece3f5a2c72ed18f445a965834dabeb92c3fceb22d870ef8d9fe0e77bb5fc41031ee5604c98f6c9b0ef8d94bcc88fb24a46de624c6dd3c70fffe94f56a1b804bc49881ffbd5a0fced21fb7f75aee86436b02ee8f2cb8f901fdc8bd3f52c41df9228ab85fc41db9a216fd58c10a58b8f75a924ea5c37114e32eaf9ec55a3fab6f5fe49efd8884c54c52f955be95f233ecdd62b5d60a6fc4a8578ee3388eb302a594529ad9ab6299bd2a7661d876c3d68851af1cc77196cb382b9ca01849898909ec04356192955812ecaf2cb3b5d65a6badb5565aebbf5affa56badb5d65aabadb6d66a6d96bddd6eac581b75e361cbd8e3c0208e304257752ec4711cc7715784115ae932be4b7f9b437cf71cf529a594d2cf1c0f385c467bd5af97afb0f52e0417b224f7428c7ae53a8ee3382b504a29a5f6298655d90bfcb20d38cb46d993b2d1c827fbd7d9c3e8d3eb176dba3e8bc1d9dbf032fb5996655996655946cab22c23dd8c748374335a29a515c70dacc364948e452cfa38e632fcfbabc6e9a83ab9dda011070f8e09d15a2f4b29b502bd28a5d55652f61846cab0ea9a73f4a2f47a8cce8bbae698e3388ee3aeab52ad717c3856214a297d95d28dc64a7dac06653080693778c011010854eefaa9029d4a87e37caecf9db950342de3ffdcbed8f0425a79f9e7fad3d64d08783df6f5945dfead39e656a8b5d282274c9894904029258e3641d8e379787930d8c37f41024ba00934014fc0d24308bf74c284490909941794524a91fcfa39fb6aebf632517d297bc01a200e7c8c194497998dca2d623d281522e2fa3a46b8bedb796113c3300c7bc1300cc330cc5ed8b418865deeeeeeeeeeeeeeee5e67b00dd264a81b3e3e3e3e3eb36233d87cc938ee390eda642251ada24afabe312289ea065e0b11afc58c8db3dc3467717f697d29373833cabe7ecd348882343e375e8bd5320f06eb1b6c7d13f1609e8fd672cfbc1b3e4132744608c3b08afda3d7b545ac521ab30da220cd4c9b301a148661377c7c7c7c28966518866938b28d87cc5e5aa5194633eca29816ff9a3687c79246ecda78d84e8cb8d6dfe00107ad18ad188d5a7c9296e91aaca75a1010e7f0586a9c9f7a48c7e9bc3cb63d68a1d4d0b0fcac312004e612fcf693154b383e8e57b8f1dfbf9f76802dfca95e8c3b3ce0c13ceaa5d79221ce436029b7f75662741d6c7da534e59fca53aa421fa32a3df409ae9cc57ae86f35832d08bf519c944e9830297912d2c377b90fe1bf8071030d1d7668d394f1f40edbda8094d217cdc1959f6280f0073fc5b041d4b65167a11b44a97814158f12bf033c7009bf1d0e14c13566293495778dfe6bda47cd887fa9c59f9aacdaf559f622cdfe7d9236f2241ae94b349237d14ade8466f22734135fd24efc89567a14dac9b786e251b4fe941913abf809a6527c2e614d83449ac5527998f27febe1509bfc6fca962f45d1541e62ec0a17a6f8dc40b0f5b97780533e370ac5035b5f536810abd074fc80f2291fad0d158ff2aee251be76962207158ff2303a058a2604c4291e12696b07b68268501e46aba00011e3f760f5a7682f36d75c0a4de2144d067d140d12b11e85068b682dd39cf525eb4f683da3354d73d69b68ad3589d635dd43ebabb58fbe691cad53cd599fe53490e6ac9f5aebf44e73aad79aa7bb6783abf79ceb83adf8348b56d4717d86356a8f62d785c58b4a29bf876e7c307d4bb27073fb5c202860eb5b88f4f611d2a69783842b8743de33ae73d22545ea5bf94ac37a15e0a14fa47f1d8b95b62c210e805fc078373c937429c08be9c78d53638555bfd639bbc3d85bec33ec4594da1fdd7e87f645337ec7ef48bf075f6faf17ed60faee9ffd0cc18a7ea3029b3da804ec980a831eaa9911000000001316000030100e868442b144cdc248ef3914001078884a7058194964711044214c19631421840003000000406040a638009f001e7779b6919068760b89c4dd60303ee562dbc6093aa7dd54965ee0ec2dad11e4b572a4634f12e481fd32b06c42d0110aa56923aa49ce425b1a360f1f748f03cdc1d5635171ed21831fab1b219117c1a23348af743e5fdb67096c026b9244030d3b0c428b4afb3a95f6dace7268a7907af10fdbd3616386b0cb44c45606012d58f356fffee4ca1b5ebd9e7fb18a8a4fc19f1fb135fb22ed424955f4b009078d1c5b226978322162abe3978c96d69cc9aaa896d399c9e13134f293b7f82684ac46cf1c7155ebf051f6501a8de34f1eeba8696beb81bc929f69d106813a62af72800a4cf45ee4fd195126196d68f97f6674e74f98bfe787877a5798b8bb5761188b591e99c90571b43fb9572569f770c74bc9080747bbd4e1bdac42d8fd2939e217f5446651faf5d6fbbd617543dd70562cdb99b88a1b3fe9b1c791c0f80779ccd8f47a3c900b2424c2dfc3a929704e082419e80a595ad4015d494c486b64fa090cb57ee66d2179e5da0e45ad412c7016ac1ad7ed12d0d71e828d46f2cc87c66a4e6a523fc93fdcb1b5183a2f58581ae28d20eaae7e0c129848c6479c099c3e6b79a13f34862bef1866bc7a6d996bf2ecbeee32d7eeb15109fc49e4a13223503e2f9657921d00fc57d0dc7e2af0dd14c816fee2bceb7e708a26802ca8405707ce54698bb5e20285621835091d20465b005f3983e076da3b22a09943940069ecf39a3bc8ed5bbe0158768c6650e10e625a30e52626fe1494fd0eab6c12ad0a6f25ba6d86c63dbfc80a01d7c229a62d140f9404b0fa183fc22ae30410ea0d3c94f33a6d6b315c8982ea06eba377b1b6aa18a549c08bdc77cba1398551364287dd691af1e217412d65dce38425a41f6296f8e6473dfb4d313b5198adec8dc4a126a24251dc6a86b7b3784cc019614d3b1104dffa5ecf23e18c1a3c12b2d16b9c6e82ee04bf0b804da0b58653423dd304bdd3641ee027e7a4a11d387eb1f2cabd87de910c06bb0398f984837924432e2251b99fd739339f4acd7b27c9a53e66bc7724d38ea74481f03e3be3e6432a7bc6d003310baca8b7be7c500d22a19b0ecd6830db88c76d00aea0f3991a32201af16893a4571f1cd54b324e1175322263a230b471263a119432ae3018776b4b6673f7324286a65caba10d895a42a27a85b6422c0dab7cb67bbdcb055425095aaebc495d80a4975f6a615da9cbb150691230619884d57040385766fc0043952559e6855b18d465f369a669062885ce38ec0cbb131dc9d8ec02fc6dc0be11315e5427aecea4788925b3eb5615144914577912d9272a25ad64af6421620fda91e0b0f3efb6d58c7fb62e90df3814cc7db95d0850e977a90128910d372b71733d0d4895957053ab06eefb2df8017a21ce2186dcd22c02539caaf6bc4c03290e4a509aaef7a615b1b8b1015b1d4a3d2a4c6a47ada6d2c5eacc612a4124f3f40862c22731e84a2275603566249be8108d3e2fac9eb77a7daceb6d213c94bf25addf8b333555e234ce73b3244bfdc9e3ef421233d6757f11465c1dcbd64c920726618c4f4465b88ea7464eb8e8a110c9fa524ae510bde5c5cd24dc6d3f5e9b9924abbafbc1dff74ce29ec3879de1ee0060c8efca8eae33492859697d33d34134502a0c52289ed1312a70c4b38655acb24566a3e1a96297bab78862de26116bfb15988115671b9b8e7631964c4216c911d316075d17a5b050700350338511022304d0665a02ab45ce8c024d4a89de896dbdc41f6f8b2f7aa2c24c1213024df6473852afdf8fa4e3f0ac6ceb4e144711e7a828f10e43ff3dcdb6ca6b93ac526308d4265ca04c0495b8f7a316b9f2b9cc4b28b08c4334297a47e334ddb498c0b443884c0ee8af02259ed9a32cdb241f21a99a0eef999a62cca044e9c02659dec0c5d6a1c7be1b902b8507592e4938a4e9697385aecb09a434f9cd3aee8a09a028992df8b1df882523ea3a93748f841274c6be916e2654b48f5192442fd5f040e602814901ff8b2687629c7248b32311497697b76306e6916a047b77ad766b28fa7a1904e18043c0b118f0dec409da122c90b9847142900f15aa5ccfd9c421740a6625c8608820f638cc42c788d4f57acb729ebc40e27852e9b0447a2811a0fd05428f9075073f4221203ce8c46e1520c31413019d1ad1d3ada807298c7f47fecc0e6878dff6a48da3205c9d665eb106b1fe753474e00b7f18e9a2527790e4265a94bc74e081c32814388905dd81a28eb8cafe9c8b5becf05d4e44a26dbe21254fa99d4549e9b7ec2846dafc9b2e106761d45b8851ce0582858ae5f087e7a8681809008a8aeca15540814e981aeca796a1d8b575cf31028ba9a1069e5cb71311694de42d268ac06107be198781e013cb10c4918a9573e211c07d31f5faefed6c534a8547c375a4220a990029194829e7daf7e00e49d4903ccb1763312396ea09d939f5ae5612cf17e806a507acdbb129d324d63d363cab063290556aa175b3b669a9f0af493519885b9873d7836a5e9bb0e3a68f3821e9cdeaf3848f773edc4748fba495f222ff7f5ad39e7686e710141f48efa8a2ffa2ef21340dabfffa8dc69a8203dc7c7b141ecbbe6b6d20840826285488568f8986d263e5e7ab9ad9d53e18d22a03d1dd389b339078ac071ba2845e12421d6c58d33eab8669a2e2d24cf366516f69ab78f074268593ee09aedb984a83710b00f1f8b0cc9573e09abae54493c69482b6e37d64f737c89d3b83e695788a60d757c176c4a7daf01477697350b24bc9133af198ab23ea4e50366f0de01a58d140ea78174d87bf49c02195d2325265251b9b0375072e1640ed4f3ebab1d3a7d1c16f1fd0a1e977aaca32e20443ede465d50278c7f43611c57eb29d67ae03e3104e26a06a7220353790fc6c88ee00a2b484ac8e07df0f08c36d8e5427619933815217287b5f31ce5be36a430e9837babb540f0e46b8380a8d0f24cd82d229237d91e94d95a9977411a937abce30da41dfab9d4da3c9bc1b0b86a06db1dafcdf9bfeaf4b071fc7f0b892dda0d4f8e98f44ad8d0f8a50e10ed0d2aeac6c536cd32743b0aab1b617482d03567b4a24730dbe0d787cdf030137c786418b3714f85952806e11dd581328afce996b84de489f4be3765aee380aca3cef0f08653537715bb439a5ce67cfe7b3409ab3fab374d6f5a2b20b5c883a7478ea6424f37beff476a53684bc7276ae11915060c2d4529ff5552cc23ca3e9663d4a212958f0ae277b28ec6b755e42b532f57e19b949c5fbd671e9e9a959637187cc14455310c93eba01276f54b6162d76ca14899701cf67168af327bc80cdee2a1bc8d1bc271ecd6592021db940845289748ba8f589ec958ad22adfc6220c31d8ca1a410d4a16251915966b15b5c9b92c9169d1c0471bd7bc28bc4f229eb647d72091979e16d8959884f9611161a5ca7aa45337952359410617e6d86b58da14e6d9c8388d1c87d3d89ffc8a3475b52a6f5598cc93576983c6325004d9042c43147782daedc5456d2a9ef211e234827afd69fa21847d3b6b7e075977cb4039eddb5ae62036decbc025e5e631f964478e3cead867f232a2e0802b7f8b28d7b2f2e3e811767397c3e183973732506bd3cf5f80c478c839bcbb27cf94b75f44a9c5663107a752133c1cfcc786b3d6cdc37bd36f914433e1471952e9085b1a59c10242be80aa69ee47f7388e1fd6ec62382855d91ccc6dd70a937dc76261c1219efdf46b19f17963978eb57351cb248b70b0d8464513e43e8ebaa22f12889c9140e0cf45886ca82ccd07c356c426309a36f3d86f101708f7ef3c0a61d82bcc4983caf65ef982cb8ea58c49e3b63295eadd753fde333c609ed2b2769602b15fad3c8dbed15b088a09d1af10d5a14ad5ec52e007fc79fde4bef2cf3ca5669d92ba6db267a7aade21ab7e8114bd1408b03770e34cc1cea04988b579928b86f927f1802243a3fe4a634f2d1fe9947c33367cac2dd7e879d490e7fdb5f37d4df58bbf45cfcd11fb674ae31ceea07276f29178cc50aed8445c89227a839c173d6287c2b58d9d08cb7b4c6992305679bb4af83fd82a2eaf2a908ee010403d72fb7e44f31f6ad651f45a420db449f5dea8460392164d4e09020a24aa3ffc0841a745b905e8d723838849378c4b9842fc225950731fd090941007fc42a1bc84ac0527c5a1fea24ec8b786d004ea687335c7aaefe590a78df6dca954616e699c0b2277d2eab72aa65b4163bd5c420034f32fde5c1e3691ecc48e78e5d9134aa1230266d6c026174d8811918ea1deb82c7b128ebb8e85339238ecbb05d45963ac2cce94f964746c1f2361cf9dab184d5576fce728a34b74406540b9fd272e49536f0f7e35d119830afd00b618a2f2a8b1fb6b5c78d616c0bee5b6cb3c10c81b68c83aad9f6293cc2a57f479c24d5ce41e6d99ff08a558ebe344877238cb106353ff5cf5f88f8f0afee216edfe296170fdc7614bb1abcc28c9fd9e24334ab3ec159a203d1651bd695f76749e2b2a9e8039e21b6e2d87680b02dc6ea545c17400fd6f08a68dd3e1edbd283197d9aa12d264499cf4e9447f5018529a41a7d0b49dc1aeed5830ba5c4a9a701351aebac2436657ebea5e0c43bf58dd43a1072455df10fb43162b1d3084891c521fb02862536aa0b6901370ffdee1cf0c870b29837dd99bc9ae555b960a42c8817e280f8a3c7a0509cb3cffca51761f152fbc95c97a1218551620a2f346c0c8ed7d213220da75c935ec3e2e767ba8611ee685878ebfcdffdb37731c5ec3e991c7f9d5f84e2222926433e53c3d6ccd7148fd389999651c1764c58d66a82fd48928626ac1c28b22a58a22ef15e08495cefd6f84aebf4b18eecb81bcf92c05954ed4208d8e4ab950995d82daf41876d864db25a8959386ae1a21ba7e9c55bd27f6126c874bbba0e95516c261b70cc0a9d990c5265dce956972d396531a39934669fbf25ab2f99c3948330b100f9e87daa33b4dfa373982211083c8f98df4e59ff9513af110fc8098c7d76ed7b9f8e09da7be93b64258b7e3f4b3e757e2117853b17c10f06b14d73fa0acbfd8f5a0d31dd8b523b1410f1c3b16f9273f80ef071dc0fe9024bb0124a606b656d9e78fc87d22a20a3e5236fd951f591cb2b45c9935557b94ff7ad5e004a8c3dc88906ad5b6b8a9db48515a330e114cd8b8ccf5123d81956f0b2e6d65f93c20d24fbcad5a07dafeae9fab102ce62a8d91ab60132acfb52a00bca64b4d1637cb6d82ad962eaa5fe2b52055f04aa95dbf381f219886d4f484db23f35876d07fb4d39f8d54dbc0ce18986f6a3e87baac3b4a6aa4f55b81407f613a9876e791d0aae5d1b5ffda3b011d48d25e26744ef7d1ee2cba70f77b1d1241e407371de76c18a844661a66119658b46fdc65c1af15f5fcc93a80d1da5a210601353e9b77d67871082b094a971dfdbebf6a533cca4c184bf4f2a8613dfb56ed8bf2523c43af3f0e327615e0f085dd01134d2203967699919511621203302979f39458e8842459a95f85fe784a9fdc124d9573ebe38132d3d1784c90d9f4624cf7b95136661fb96befbbae33f8957e8cdd3798cb6e426101d50f5a41d95a7716d1e8238a00a7d85aad9de8a861b109973a09939e27025c13e5b68a8914a52691e56c404a3d895921aa18a1c7ca69826bb03d84547735513c05c2a33eaa0ada3fa117042a103d14cb5324fa2cb724ba442057305c81e823df9940dcd764bdd056b90a569c487074c7415ca9c2b946fb06b70302fc1c0452c8f2d3257e06dd0c1ca0e19413c3e828d341d02c9e641fc61cd0f096fb5431847ed286e5b42188349b53c3f1f191def77a0fa2fafec2dbdebed89303558466d92ec0a9c810f07186b507e0c7659c1d3ddbe95363c4db0237db98357483bd955f26b3b212083cfca87d59692f8663ecabba17e392ad0175af8fcf17e3113b5f1e06d0698fe0030b6eb360d58bcfe669a9d91f966825d3b9415801cfb5034a4dcb0e67409f08e0e039d22cbfe400a1bf912c8a84f2ca03590d7f45ff9371e1a0b564839936891a5bc3d7c8ae810e5123eb556e2c9e4f2745b801363901982cb412952ff2abd0b8e0efa52e7251a2d0a438efe0ce798cc37d267d63f1ca541e456a3cec4bea4524e234e36155615c2393cd80ed8500f29750c5e5ca1c716eff16e2db4f300512c013ec3b67d494133c02edf0f16467313d9671a7467c7380ce87ceb0c0cf0a27c8b2108fb0322a08328b6f90e2f33b5afb50fbce78f76f1a130065eb70b598988a3cf0bd25594ed8938411c9741eda477ef5b4df0d2f9c6338d58c070775a65f6ea4f6d686e3a0df4d83915e437e9f9f1cd3710e8d00f81d55880f6d2d0ed703dff28b601dc48e5b47c94a3085794666cb41dd1f4ef036516d188af4df0614c91edd2014e290a0624b1849660520631b2c0d42b38c33a9497bc473b56bcac1efc98d2cc5bd198513e9bf75b6df4fec5955376801b7f1b6da1eceb2492ab0f593ada5eafa58c753d807dde5ab6b23aa0892eb0f46e5a6a04ace2b9a37088a123dfc0f287a2fd26f48574c64dc020d24aa72cdc35d3bb84ed270527b6e3355d5408df9b99d195a30e985000e786814fab750005cc3e6437ea45b8f4602191db702374a201d02b3b13036a0a6e274c0b1ef0ac8224ec644649cd36ace598cd72c3710909105cbc0622c5be05be57c023376ebac087dd4f7647ec70b7b9e0580590baf160366263c35a7ea638bf6479f9729da9c2f485a00aa7add3d3d18372469a27116f1c0cd3d17c8b5a3d9387dafc47b30f4d88db68a9e238b6981182cc5f87c7bebfae586d93e529476ad76fe0a297ab080659bc2c04acbdbf1c5a7594f0703645e472082c8f95380701c8211c08006c0989ec32f5e00cf022b7fb55c5b7bf399161760986843a161dc70bf51d50f65bee384c652bdf5a16c9a168c16746aeb5fc952913f827bbd5d441d044e466219a82be6508426472753128d961b0f8fccbce9421d1bea7cfa5f9c0dd496c06905fc7027e040354a7143df8340a658c151a7704ef97559aff683c3d4a30c5ec395722c119a7a66b594ee1f696aa97436b1c4e16e614e1e197a0aad08564b740b2d36887a82d0fc353dcccd0504fad718c8e4bc8581d66d48cb5bcd2cd09a0544b1028a8cc9ec4e59f5091897301651b10d95d6cc164f9f45d683507217baae842ece7d82e1a1a00d1b16187340937e81ae4a008b992ee9a7b1b84a2b9a6bf7431e367c0464094ee91b2cec116ba91bbd6cf00030e9055f5e70a70b4ef882dfcefae3de15d1ee63ff2371822643284b1944832080196437f03aa35b466b14bc6b51a80784d3c2c4cfc0b92a03539e12ba8566278d0bc20ee786328fa90544c7056e19114087114056ce1e7b0aa87c6056cf3736944ad9f545d5932e199443d42bc3aee1480ab438f00a936a4c3e90a1fdd42e0e182162c44b69e423c0facc86906c81234183503ca541ab3d7b7a6238ae29f1ac0116e80967ee51ba79b89c516c5638ec307f3cf110e004ef4d2f6913e8045e78e9bb23154a80cedf74f8382f1fab8496c227af1d3f4b788eb056e4385cc9a9c326df106420d5daefc1d28468439fc1cb5329f38a49691726f0b4e45f582116444ca580a154b3f921fe91fa06f062b5b947a3b8656e9715f8e6583207fa98e3b2f52684b36e78fc55610619b7b5ca008ebc2dc3169141c26b50d369117077c667d46e421c21554515e2a3ce7ce34435d847dcad15c01682eb4010d2ac13a43e9436f8ce18a8a53910e705decd952caffa74a6fa91c367f45a7c7512df4494fcaa87c9e2a66dd496962b19447d4b32ead53393a2a499cba3e1dcc3ec8d31cf4758e82df5107447ab7670fa284b62335150a9146608344daaeb9ac8c41c0817e823059c87cd43356cdf515da544db467506a39f951ce9dd44842183c4c143e99c1df01cbc67f1731e207b661fbb0f46facc8559b68836ff4125f338174b2846fcd99fd55b09248d57a968ddb8a6f6058fd4da0f0547075521ef0798649d78c7ac400ec341f48fbd8db9b13ff6d73dc712f23f0754b148d91bb18122776e7da549e0b82c92fee4c3e67bdae0310029d9d539a3f2204d35519c13171cd5359eca0ce8e00b15a3e650816914b1003bd0d07d228bbe3c26eb2694000259872734306b707ef4128cb6355bf339b43e630ca3f7bb5da5b6b9e296ddc59961744b521b90f4bc3aea6c91d004182955fe8de4928e81d51572649f4754866592837429bc7173034f24329861723c15704e0a6ad47f69762fdcc1d214889b29e540dd0b1d457448e56c16d2781feb88c78e60528a803b7fb34dc13cc158cd51afcead52f254fa4dd32965d7dc25fef135234a908e91d03fa17ed28e6c56ed2da44d3ef247ac7cf4a38369e18a39c4f23c5fcaadc780d6ba81d8739c13556a910288bf991f6abfe51c0616a7880fc4701f00c40efe5193e5412e10dd474a0635d7f0f3b4a7b207b77e1258d1592af8a28c25588325ff88ad082091a7dc1555afe2035046c956781aa00157fcfe5bfbe3c5f01e88f7ee923877b23ae2215d8c51f1960a13553f2508705d9680f73a52c975b35284ddb1732efc4e9e211737fce91b9a5e010cba0685390292d5979f7f0618e62da8a0b280b00cbb611f1a167c2f18afe28aa8de8c48872fd6e5ce4ef426587bdf54445742075627728864142c796904b27940dd11f8380732ee52a1c9a91bc4c38ab6b94c748d6d661571f657b25e7c854b73a43f2407f376ab492e9966a066ed30f3c54e0fddcd3ee0b5f17f07c972f9111dcdbb063e6b14c2b474473622042bd9b1499d75b6fe4d65ab4a89c2c404dc824731e44afa32b22e8ec3aee9fc2339140e4b5657979920aaa98b0c4b04b37d0d80dc415d282fc4339471e24c5414038d840db3d23f70a5f60047af83689ed369b00cb2c5ce05e28f6adaf1c353af53ef338e3e2b5a68acef55803106df961aa649dd7b3b41d8a4dd058da12f6a38c43df69117ac06ee54bdf23afba8fe2e357f25b16f8c4d5791fd895db7b1ff98d9092aa1575d68b5be753c24a10174befecde8189b786c776467373a9712a73149b4f4eecb9a35e7cad75c5ddc7a495c930406780d83cada9015c659c58659bf0590440d614fe15aa91008ad9111bce8757c050060e1b289ff6a4f95774987cb2f82266da31d1bead31d1fc3e800346aaf6b35adbac33d07c535dc9cce6c061e5d63a9ed8ff6a08bce73c79ef4a99a41c87cce1fe2ef48ee3dd5b35d583e7c559c625af5bea820ceea5bc27dd98bb33a4e351f727521f23a23f2e2112a5406743c5ddd147f397aa06054a932baf618eb9e772aa22d461f4a4b6c6ccfe0d37bb19e8ebbaef6228029e79ca45c7d5ba43dd95ef69c1e443d09e9117aa3b7a3af9e8c108e5464be19cf04a7a582149a035a55b5b078a1ba9fbacde9e495491432bf22d8cbe4f444a86f3a3d51b954ae9223c853ca55f403d241b98a6e204444a27d042774b2ff8c65018df64ba2d1c4988b87cb09ce13e8de3e85dc97a807c6aa955e8a69b124f860a5cbb9611e49676ab5a4d2cbd04dc9df5cbb8e8ee9a9287802d78e374d644ac49330f7891248c342708e7f557b30cead8147844349c83d18d2b417d313e23f83aee135787120349003987a19d6309bd6400afb4c11710d305c3c00341cf9eaac371018a911412a39cc113b221b3f8735fb3e72526748a2eb41b10def097c3ca6f86b4d20d2b4518983020284dfa781da6eac4405a664a638455dc2186127be19e726192b7d111180f10b8a34507b81789b934a302c3394dbd1638c362c4540a238cd86289583ad48febda3ab87c2cd4c004110cd4bc5baeb6d418dbdfa4958507a1f90dbf0aac7dd30c415fa6ac52823abe3a6c5fdbc2c91dba2f9a8b18799cfb12e11262c86b2bace48ded5c6ba43348ebca23aa0ebf07c56c6a8652a366bcee4831ab154b8bde9a0a7575c193dfa8748c8f07b22c73479c57eae956b148f5b8d73a23f6dd5c86e713e3eb5e4771b50fb3083c5053a939d5fe2e19f095bdbe17b476e91ac0145870b2b56d1ae256fc9115c8bda8a900822438452ab5a17f8d508a4804655651fcb653dd8f76ad0284541ef95c7cb4809b976870834bce643683c0a60936ed975a53a53389b1dfb51ff1c22909ffccd7b4684416d550c5311f1b2c32fce0daf36dab0c058b4e4b08418df82704bd3862a5fb1af5b102883e8310b5e4d34749bdf939fd5db1e29a2d3d1d5a2e370e6654da3d2f64f272c96429299c0879415b2d4093de1849cfdd4511db0401d7e26856fec38125dabf21327393fd00570611b454324a6e8316a29297b889079cb313122e4acc2578a94d33f906c93afc367a3eac94ea8bf6a87f61ea4a7d345fc3ddde6cd9fd619b60c3ca303919c2a261474c6e1b5b20833a94c2b97bec2a684e6c7cd4420eb773ab5e666368981086d3f62c82bdc8b0092c0001860d7d7adca50f2d985e95e8ddfcc1b657c810160dd8df55f7171b26778fe5b77e712898e065834d6901708246cbedfa9ea0c89cc201b42ffc14e0f18604039da3a557f6384fd7532299902d399625a691e862d51da3d5c1be6487dfc5811fd83da53b80047e0dedc5d9f5dcb8f34205b56f01d4262ad49cc2eb5e318cdad8b36cb2e9bfe196f677c1df75cd224d2453086e47b09ebe736430bc8387d11552865fbc1bf16c2f02d7d3b175890b64bbf09421f3f842ea0dd0f1138671b60d6254dfb35e16d763fb4a44f6fd13437b4dec0b32ec0f340b847f10585091c87a63ef2a5f3bf083cc6fdf97bbe60773838c136060c1e2ba81507775c2d95436433c630aeee3b8936c3219ff2163db6ad9499969357ef54300c1bbce780a40790cf890cdc6508ee046cee004f810c7db82487f59b3d3fa99b4ea7a036bea1f453fa80f9fff1a5b59910e8fb72d0182b6fe4b69e36ab4c43f681a3af5a3017a881aa70550d01532c9f1febbe87febd0969891772667cec26ff8ae2bb7b68fa28c5a23033e434ee808b8410a1ed8f9b9f81e3b4c712395e69f33afc2691e5d13511513715dbfbf85aaf52182fc57ee6f37a05b08baf4c17ca56de90d566468275d048d8e5bd7d417b70316598f09be841bdca4876e17ec00485191def3230cf13b76896af4b863105559fd262db46034f3db35746906319324ba91f4930de76c570559fe73d28f6c371358466bb248bd8da057140acb8e1811e8b2f8b7f82c805f3171cca692fedb330a4ca7a9167b1613f08028d09f04d1086492a1a99a0aaa2b2ec13112a645cb9096b823e208b81dc3ecf2251e4ed15800a66c7477951c4e3043607a3e90aca6f57f225cdd93cf626867a47a812ef4873133cf340d39b2534f93459bca290df3a4687b2677acc07667b16691ef25ede4d7aabd137dab36fe99d02b675aea3440691d68e4251ee1b152c5bc4c63aac182a6b5e1b59faf834c009b50df64ddf58563c8c5e18642cd1cf154d5e6b2c732b15ca4504b65dfb34dfd8a836aa6c3c04cc1a7141bbd62068de00287b9b6569e5a42a03fcb7a0a7823dad3f712fbe6e721363f3d97f384605fef15158585bafe8412b085f9e0e26c282a696e25ad211825c9395c46c4434176ea264698a03fb1adc32836834a7eba82acda8a28d25c61536d85056b3e0c8626ed2eb3f5f2bf5ae1f7bec4076c7ce1bb326f0b23f3d129976a8adff28ef2e94b477373dd25946a8fc78014fb045e45e71830f372502121cd27c487c7e26c4124d45329a832a059847d58661e79e273a4c504ebbc8a51aa481da5d26eb79be0fdba71457a049a63671faced14adf3edd05bfadbde67792cb6cd609e5d2f6e9e6294c6dcbabb11f21852abce48e665baa8618fd3a3b1a051dbe04373ae72a27012285aa6efaef9f98c8231b20de203ecaa0c114a34a2703e0bff59f2d9d35b55a1cc2f3cd077acf269b17917431c26f4cc2edf8700e5ab8950f9d812f161a1cd7dd1bd5b81cff03ffd46bc298b68993f5e8e3e9142fdffd081e844f65847b0b0ee4f714b52e9b6578b13629e243c53f047e4fa583ad13637e1bb76d0e6cbfa72e3648d30ee2fe932c9ded005fd401d26c2a0e7c09b5654830356e73ec674c376d70b3c5c2a7c895d7e3aa2f3a5f404c02c5a1688a94d9948956c1b32fd23d9e374c8b3f5a1761341785ad90aae0528d666724e70f000670da5389c647d2208957fd4c4e712e9f633f921d6ac04bce603ef27b2a3ae3908a7d2aaaee696cd0a3700f738262170f9a84aad426cea2f8d27c6f53776393bdfc4115d2013601d0d34614a51c67f9c49f8ad48872b6b029da3f4d83ed97094684bee123a13ae20b92c589302692378c137308986734a7ae1ca23275aa48ab8297fe99cc6ef4e8482dc05886df39efc7f5cdd24d1bc2f6a96b1985e6ec1c6275a06baa237da6d9ad2ab63009fd8e792c6c480ff3b8bd4e4198a8107a53a33816c37e2a4a3f2bdef42ff7342890a3f88a85f266a87ce429e4f50c3327559db4a6256d59fc598572980a46545fbf0abafe820abf1ff2a36e4468c33ef94386bfcca5eba1b1a0093fa5e0e919f6a0f184c4763a8d2da8679b79e8be05a7701f3a24cb1b5a69a0134eb03cd1b3762e8fffce8681ea5d30e308862245219def0b077c23d53a8acaee56a5f69bdb3bd74fb82aa7702731e591198b6d2232c13ff929e48d5d88343d9c2f1142c5e526de647a9119d3ce63eaf9b107cd7a6ee2b8b3d913620efe2aa409569c683f5d6ee85a43573af4d5d8949a5e89351112a88678df99118f25dd9ed4c417d952bc0a7d0316efd2fdc05e734e1e1e1de02de606203d5e0228068e0ee036ce1d900e571a3006430fe806ee268a93bfd17c6837b41fed0ded477fa1fdd06ff43fb41bfd436f43f3d16ae8bea7de758663a35fc4e17ccb55b440d76d04e9424e7114ecf621b753ba172136ee5cb0df94c99928442e6844fbc8225416a24e6bdd8001b7db59ef544cfed2c2c75f03d6ef3a3c17f47d84bed0d27f4ec0db563e3a770c6bd29aa4c61d9b7b1bb08fe611196ad49d8e2fe2e9398057eefa6db2fc5d0b3c21a8b8800032c561536b43b7b42b3fa809260e66d22287878352ce0f37891a4f395b5c786b9ddaea5a81c5d9dc66abd2744578aa79b7d5d3b3951214a12d33d7d60181ebb0526c5bd134b6db2e1257abc8d7ef4aad6a116dee078ad6482dc48dfd7880442d9c0443ca00034e2b620edf89303f6bff50b2a655b8f9328034a3b4961bfe57c992561a9ab301438fd604893d7e9b7cdafe53baa2b5e450185885d82b8c8b2bb4b073f99e84b959ff49a9052d149a0d0891f428f7f8ace2c6fe3e54c02e52b3e924798f0700b54cb9cd3a3c733f4fdbde3f2604532feeb4c19cb8602466e1f063a29e9d7565a5960981b6c95d7b8ca8d828a25a03aaf1dac558834239446db87f6049b86f6375b7941b5ebcbe860da63a338a629f07b9aa8a02c407429804b5dd52eb19dcc9392e5b426a95b4e762aca6f1ba260987228485b4af606a455f2b17a8c74f2cbefda54651c0df1e6e4669864d895ac87113cda3eb2c2ccedf1448c2c5162357f89b3d386d8898ca842b672db23920c83b74dc8b6265b7e690e0585cd33e0b2db534bff9366d3f0ba0b0c52b01ec6d93fc458ced37d3f7a754ff0929edd71a4a17dc8464850e027d63d1614fa474517fbc809885b7598b6cbfafac2e747d708e172ce98bb4c95fd317094def3b0cbd1f8ce330b05f60685f23bd5a9e3c064ab624cb8cfb1f220b8c4b0bc3d97ca92434c49570dd6a74dc136961d4f989011c8f1a31a11a668ed96884b1217af3abed13d0d9712e8e31061d907a201dc97ee5d7927084bef1d4b532bcb2b9dce691508e9469daef28e6b12bacdbf296243c311876e1621b5185b0c378822b4c12f6e3fa21257e892dd00122fc92509b87c1724e69e46aba997276ca540bf45401b875bbd0e7e1af87127edca9cc00e443e6f79dce13039e842be49e06b36e61c4dca41e4247e62df3fb0d0d4cb6840b86296fcd104389224881862ec123093186e3d80de0bcc6a92fd670be10b5ef9d41978576406b7a7c34ebd052a8f0158b69258a7afadc41f520d629d8e73e9ee19af1e0a5abe499bcef2f9a03506e8ebafca8e626b30aab9b5e31ccc486e88aec9bed75f8f64dd2d9d1153814617588677eedb1721e2ef5d8a1ef5fba49a11bcf58dbcaeb078db2e723ba8bcb06c9a5f7b75c2b6da09e5d202117f4431a23b0f1c41730ec49a6b745178cd7347c47bab7179dbb3847468da9e375cb789e3de86cb0e9e19eaf4443fb38a433bf21b889346e127585acf594c0310eaf90614fd2be3d7ed75a9bbc5420613146057141f7d72de41ca0d456f44acf55e73f15d387666a58d763434e4f61edab621d3ba79b6d8334ea13417dbff1ee86116b2d90b66cce9f90658272cbe596348b494d8ea95310879c97883654cb44b264ea2ad50d8e5a873c9589a4a7c43d0248f130165e1ae71a9893d307c7dcea74cbc6b60459c4a607d232e3b80dee182338ecdae8698dd4029cac73a7d9ff327ef4d88faaf730a88d647083dea37a8cdcc4ec54f73f864cde87574192ea8dfbc924c1f27fc87ab843d9d08274f36770958fd4bb3c366fd326fd5892af52a676e46d3b64401b3acac5f8435be72d15619eedb9ec1f377b522ce93d1762e2a0c6a032af1bc834f73817763699999fb7957d67d53066c1e54dc3d0711e57f8792497fbe70078f0d8cbd7703c4f9fcc0f1bec06ac203a4e37da4491c3c7561fcba6f757509e12657a9f0fe353d0172db7f221cc192afe11aa2baea6fff3101ee73771e7e87c8c1054cf27bddb1a68f4e4f214287bc60506bb6f25796171d7c40b6afc5f8167910ffc362faab91c68bd729227ce7ec4be3d536c4aa7ebe0bb14c531b2893a80ca59de840770abe97562da09fd0b60cc9d419b2dd9cb137afcec605da9c7dc0e32cc6ab595ca18c543f8cdb11c5647f83c5264a738232ab26d70251f856d2747585e156aa2986ffba9a30177c2460eeac447bd493f4251a042752d76e160944dd7886498e773787dc760dd53e6bcce37cf5fd11c14bddd4935ff4d4e7931fbd0e07b2a30cfe452452af6f28faf2f1373ef729c149978445414ce3270f9bb27daa7804debadf19af0c822cfc6f94bda5906894de42ea9bf81259769a17a2812b688b913197709b1338dcb3c104a2ced452b4904540c4db5282582f49f6f1f943981c220c38c4367e1c4ef4280272abb89ced7502de2f2f441979846db045ad9255143d2606b10fb04bd2649abbe2000559e961ae726e9bef62b1b85d8463768d67fdebea7f0b23c7530480dc2bd7749098244e75a988f042e258c7e3ee0e64834afcf625610fb28bdb9a6ff69899fe9a6906ea4d8853723e80ac58ebc51883e32f6c40d0af56edb9bb35ee0f6b2d48be59377f3e6e7fe92ee2bbd14fb4777e9f6777f41f72bb98c95dfcdbfbd12ea1559af400e7d5a3151ce41f5c709d5f71531a3a79e2b416d9e325708fa22096725df84c244c527f9be2a466cdd2e43980afcaf944d46c02e819d21ac3ff0c89c08047f46658cce3c2ec578411ba32a74352029fa3bbdb894c4cc6ddb852e16f1d2803eaf239d781fa13a979bdaf38ea165d3fcc419489252cf4e0712db4ca744071f912b1ea7ae1654f112364f1721ca007fd377d605a3b6b58cf0ceb1dccc245a19452c7efe0ae5d85837d9fb0e18d41458cb4ca004e110e7c82a83546a743b01a9e1daf1f1a8a9131dc7b9f88a97ff00797807cdc238120f9376460a53cabc56d3b284f66d6459c09662ed0361de5d5a4c66efcbbc44a19f4f86d512549df93e61ca36606bab9e9fd6f2eef15366e6b4e26a7fea145264cf7e0166ef3832c151f32bfae30c49c11f5dff0a92e818842cec4b64b90b3b892554962c9beedeb39d55fa5a7953b482ad9f2406ccc7a26f81cc873f5eaa5964d66f86eddc3514ada30f1bbfc84618b996ce048b3cf049e048d5bbd755978531fc2f097fa2309f2865526e52248266f0f1d2c53140c0684445af8fa9ab39516d0f9efe43329840e9e9b92ac470b2b0fa20f1d967df3ef3e9cb77be7df2d997cfe6a3c6fdd925b21cc6d8f8f6d557dfbef2e9c3573e7df3917d98f521079f99171920a9180ed195918e3b6ce60399442b899bd10a058d9006d06f091b149ee184599bbd8f866ccce77d630123de2855b6bcf6420609bc3e4ea0815a94532fcdc0f25e1d74ce7cc63e533a0c2910347c63a728c4c36e6c60fb6bebe5d14334202c3aa49e06a80ffb95089c060ce06ec006fb37fccc52fbad88669c196244023c1d2aba4d5ae20b0d45e80208c1776eec78e8b6250ab4c282263b1462c991dbc5aab1ebcc32c82ab1df232b4d58bc4c16797580b074c33cf44afffde9b698b8faaa5b847ed67f16d59015b7807ee17f92e8b50a9017137d4e323f2630c321a5794ec63feadaf828d19c21c6ce86f6fbca72dfc651ce6ef99f39397f4933765c6f4eda3f5e942593f23527891fcbc8de2c9af1e1d0ac517fa849a6251ac99cac3f1e1fc3d76866c30ab346fda100991634ee72b2fc786e0c5fd0100843cb1af587ca3119d1d0cac9fae3ca1db1973562cc6acaacf9f7604ca23cf9649afc44276699d1382886c986fefb42713bc111c96ef59f7d38cf8b2287896630a3e287069cb9f160d88a5c88bf3f88673ff64176f6fff8c27d85babe998fa952673a33590ca3451debe76a33aee3c2daae98cc44a121881b6b861b9862e61cfb9bc56edad38c4145ab486d86f52ae98ac65a0de0864c522970792e73d9fbb708ea6c64e790014417839bc5572ee70c3b69b083018d330e6b8987eef3b4ddf15c39f113043aecde811d651aeb29698e4ffb504c0fe3d13d8c7da6b044d5818d73bdc45fe3a94547f4faae7b7ee13641dd7c85720dbd2fa98fd11a015b5b8e3a2dafc8d9000f05f1423fc4cb7f2915050178bc23b788d72a446dbd502c1fdbc6b2410b55f96fcdc36c88ddd3e9b716b3d54ee221e37cba3972cc2fdc4f923f4dc3262ee6bfef93bbcc71780f1f80b72645b1ce733eeedc677ec1fd3809184d52c5d148572c9eb16434cdb2778ffb82b76ace90b298c8f3be368faf590eee114038aae12e9619ced791bbcd2fdc8fdf05a3c9c2b8ce9f3e37c737c7e11e0e1f7e4d2e636b7c675ca6712eb3dd8f268c5287340e6c6bf13ddbe71c737b1096b9b5b08de12d2262cf5ee727db4fde4aa486711c8d39c762dfb3dcb2a4268522dd01a2b88e6bec7e83f94174c72f922d29f2f6fae9c6cee71934b9640347c04672b3272d741c8b3cd08b8ad913e5b5637656ef16428444d39238778b8f3f40aca36a8f6f1ad999bdb5305c7e12610be62704cdcfbd5db2ac992ef6401aac1ffeaef0818fde1fbf2f34201edbd3bd45ab10b20f5fe5184abf2f43e9d085d2c69b24fc9a9e593d6120fe0178eced4984508c6830257cf47c27c8637470902cc77e468e09c6fb816ff53afcfe300178ec4ff7d2c8f8c3974506bbb847837285e8a87a5c0ac981c320c3361e2fd9e9a0b56be214a2540355718fb4cff6a95ebc4e8ca8387e32090193b4a31a56b7f107b040c6ab9c05c03443afff1a2f5bdc59b731c4de379653cb79e3fe4e3cfded7866c5c45fccb7e45659c5f3f4121883a7c8b03759a45e396087bd58cce182c693006ea9a8481a7bf498ed12f1f58c1ad95b3fb16363aa662bd799ade697d669edaedee348a1c8fd60dd83392be97789fb9e3fc2200cbbdbb7f22487fe98a2b30a1e4cddba769153a515dd45f190c3db9d4ab30be6e436ad5c78e287f616e3df225081aeedd1bd38fcd9bd78d501caa2550f1fce6b6ddc50ca612d7302ad1714f003b189c20a4cddc3d702b910a8d361f56a9891a2dcf8c1086aa41795857be95a90c1b83f27216a7826fd0078107f52893e9a1cb334f0c8ed2228c4a86aac9698d1bfe203353daaf487fe6aa02d4498973ecf8acbc4c6351f8ce5716362c0a2f0f4937668ffa672a5af257f80d99194e7c1f578bfb398c995a906cceebbc1c707b838d994995b66503616193d0d560f6d8d45169e9398b8dde22d50c98ce897243ae4f4b99c6586242f76cb484a29b54f4cc797d703035fe1801bcc40a676f1fb21996edc1dfd2946f122ed9dacf6b88a7778e2551237fd378fbbe848a4dc3b414cd09d7df2f2ef83067d32617f66683b4d1c002ffae112a1eb4d5ada90cea364bed8a2f4a0d0312476c07b0e7fa5fb4b37acd6fee77398c8ccb10273f41d6b53b489f785c046a3ef33106bcb0ebdc8419feb4aec7d9fb96102a614284667011854bc971f98ffc1bd42809afec8f7d738ca562c9be6f7c702b9850d178c3d972cc080e6ee02d4f5dff71ba3505d3720f43c521953d4742a38ee3fdea7180572d441d27127074c5cfa479731b0a75b56e31518a68172991ff30e5da09b92ff4e0e5010227f241bc1f24637b15abb3fee6135125ee133dfd1ac2e58ef667d296482bd76608807b21b0c8c9e8b2ca28c92be0f9fe5f129385ed21fee2986da8b1e2260da6ae5269c13208423ec3507472842464766c844c521bd95e9f7c1f5e77db5f395a0f1b289bde39a785e5e967deef61f35bddcabc0df4a28a10fd43a153590bf88e12fef76a882aba99b76091478d6b2c2028e1fa00ffa389f9a85577d9da592d41c956812d729ef482496a6bd24f5bd1b8826e2ee109dd1ffd367beb2e7a4cfdbe9647b57defb5dceea91d2ed9b7c7fd0786c24486c29450794e9e75d5f4490d3d6d52052e81034d509e2c1bd0cc95ad88c879918af0bc632966449a16c33b2113fb426ba09bb5f393cd406052041bb68476943cec2b0498aad8ef4e36518b59d59b2e63d716dc6cfb0a6c52479d8dcba39fad75be86196c3f987fc2082ca3e96a4ad63f65a0950fe6cc03dc6d0bd5fb12946ccb9e3d1a432df3999acc9cbac6b04ff421ed920af19db8e1aa940701d8740a69303af968f16a23cffe6680cf48b5e3ce2038fc1a25a8e748b4b542a87ce41ba932a247b2ad87aa343a05820fec59f00fdd1dbdd525108045aae03630f5120442d2fc8621bbe20162ba8da4a0836d644bab88b2bc0f213e57e21d1afb45752fe601a89ed744e04060c9945a3be2c53d66679f46f3cba8a9963ee234da598f3ac8e0294639545efa629330f9dba68a6fae01ac373fee03c83975e6530c2a7baa42375a34716583da9ddf556b89050eb3284e6d033136bedc51d809027d059c6c057ae89598d608a36d7c5653e8b14fc078cc4c23caae4ff6378b392613024d50056d77c817a7d8c5a080f8d7002ba60634c4573028012b6f70af1459051d1f34d698f3650285863e38801776cb3890e8d34013792265fa1ccb14f7f42d1f2ef1825563d2348bb85c682aba6539f59c3fe50350eb8edd5eacca5d2ebf2cc8e09ddd6b4742b329fde4f1b2a1fdd0ace22d5c6f3de88c335cad0bb726b75af123870576eee0b354331229e2fcdc41ee9833545a3c959df5a20ae48c947b576b3bf95b6563e00c834103cdc535ed89060e38409976f34a28271329a9f3ecb9c836a8c542bd71b578064777fab8fb49d81ec33066ff5edcad1a76ddcbbe1311bc591b28cdce4c5a7b32c85f3f9a3ed8b080557faa28798eb60cb88a72b8c6a96a7f226e52479852d22288cbfcf5c6825bfc30ed3865dc82bbd990a20d57ab689039c243dae8cd834450560ac703593e41c8711f810ca9f3d2a92853878386b3bba7c32bdc72ef3bd01312dcb794b388fa0a8afcab83c49cd9079fa3ca15bd9661bb3977841cf97b0d2680591a1f6faa6c0776c36a23a2a36a40385c2535751c9d2801d469432fbe565956ec044519152d6b29e7e42e319c335cf0a5c04d120ebcc99f3f465e4e5b5fe885195759c3a626307482b7da5c9fb40c3593f60e20da1c750dc1265c63c4507b72195e8eb45d498067644eb1f015520ddbb9fa46ce8e8cfa08eac0657aba4ecf244d535f2ac748c838652e95b5b91c2facbb172e22a365dbc4e2889b2a8b1cb550eb7c9253d6c2b6aa36f750a3ab8ba2861f1cd28b58edb7c0b0f5d8ba3e9de887537c7f09b6be83ea03cc3775a7634957076b43c1aea6cc62bc7961ba2c1c032559976f401f365492a3dfcec1011cfbc73aaa77a935dc334d89386a22ff3d15967c6325c68711469cc061c798a4064a78d7cd7cea28231d499ee6934a86615826d07a391d3d51f1a508e7823e411bbd9d420d87d09e79bb15f830980600152cadc78d509eae715454e32c045b1984171b51c5db95acd27d44613cc4412fa57ff5c140414296b29e449a20134885c382e9289cccbd3ee92e247dcc45747d80c733cefcf5d4a7615e86ce7566bcde6b6e794b35fe00f350c1c32a3174474e9072239374b077864a8cf879f436b5a282748af15e83b2263535a9d20adb349c878507f299510673ad3dc43e0c201cc66e4203171d0383b32ada5f98e7c88759aa0bd8d5bf503d5265d421bfbff41615f435134da99c4b2a254af0cb1df276e98f7c494dbcfcacaf888508ecf18cd5e717da850b2695ed679b6ee56fbf2ac55d5039c4c3297797a31a32373d2562e77c0d7e616179ddc2bf378d84b152ff5bb3310c877ec2310b4b2328364df4fed3788c443071acd527601159926d79329f9e37166446537719b9f4ab7363bda3f1a57afb09e6204586aa0dc8f581dc0457bba53df2f11a13735d7b198603b95ec0cb9d578ca0350ac209e2b551fc06f8415fa87d65a77145ccdd63864bd8697f22a4c89e096ebc43e092b2af51dfadf8d08783d7b5cda812a6ac7f7cce0aa6af3993d7520e2275ab52b4c201a1146425c64b349da2ed54462f0234f2d8482be59fa9448e82d9294d444518f0397dadf3a34a3261deb93deb6645f758e709b6333cbdfbf318c45120fdce8802132fbcfa65dda8390f09e93ed4c49927c950a1e0cb16be16657c1cbed9e0ed74cc144126d5f81189556edd95d4a21476dd9da9f6ee944b0f548d5ce1d6cfb25e31821738ee8e08344e7d3b3eb7780d28e39685665d144666b586a915733789fb305b2895c870cc8633004a08fd319a663800e0d4e177608f35f989e246395b3cdacba121883f1d81102a82b0657ffea965f2c0aa80a9354d6a2f162b05d31217d6ecd6796e06d123eebca7a1858672f126c2931c16331edf12f6b49c12d17c1dea1d884ff2ade1d48991735f82f85898021b8f157c26c0f03820e5cc06362030404bccf752d189b1192b21eb8ed730377e01a18eae0a129812126febcd8c60de9ac4c2d03d6d8185e332583701aaa7182415d091d53201670e42ef011ad3872bb9c329a2bb296f0925ccf771b0e2273c5d11ce79e9752354b949a17e53bdde5f2b21e8a4709c0b7cc8f4dbe39e6a6a815a7bf8d04b7142c1866f10fa7deed2b9aeb24f5280060adde25a481016cc395ece64a0b7ccd342dbf1d83ee7425e4058146d0fdbbe7449abc745b7eb3e4b3084d3f9ed4573e574a5745a04fbfaa9268ad3d7ae34988b96ab9ce81e2d27a1262f4089391f99a320ba669068facc48d120da6498d25459c742e745afa6eb6ced413f47c84b4c045ca98e523f5295d4b933d40a463970f03f7e8cd3c60df57c55d17c2cec4c1d65de4e224d68478945a77f42e2ac3ee16877ca3cce6061a1ddc4cc8a033a43e2f0546990d9ff1c1106b618fdf8b717f5d2b5f1b0073b3643bfa2e63e4857e59ce9b096f976594b898eb3b677c14d6572942dcf7d56849870f95666af433a23798f29d2612b17d730ee86006ad0c70f41b7ec5443a1449c444233322a13dfbc6396012438f036ada636f9b3df36bb66a05bf9904f0baa2483e1677ba07ca6e405f618d45baed874a512fad775199596329140b82814fb31595622bee5841667362727217c2e243414fbe5e5166b524558131d973b52257551dbd9b496c7db00bdceee61063874289fb052bec43450f629d2657d707352a0c1b1c932682d7151035caf50c4bb0ce679b411fbe46393cf5b495351f176f1a71e2c3034e8981acaac8eff9e9d1882dcc7db16c490b584d1984c90c8ba30b3f959fcbc774366382993185caf5f39ffb0ecd00daf98564a6f3108ebbc8ee90d63dcc40d40783245d3053157f6dc63ade921c5a74ba03e40d034a47a2735c4016d6a8033927a77fd91b8f09160a88508166d7b40a61891e23a042050f0b2bfa7959312c91d5218f9137770b1582d6266df1869b3b7427d01d198b89e07df400497cf8ce815dbfd55b72d3bf9ebaef5b229becbd9bc8bda54c49ca030704073307b90b7b897c91714ea8ef57c80fc86312c6f32ab957a3366509fc0ae67c610ef85d983376083c2028019ac1df01663094ff619ae987c327a45e03f8387cb060e8a03b08b691b9b2acdb12fd0b06b6c4ff70671077cb887ca9b0860206eb9bb662072224b9be0c7c7099df841e6b53d726f06bb71cf3f8bc835d4b916b1b993018ac63d74f8e852cd5efdbba5a58265c2f727d97428f20c4fc46bed4af1f8eef5588dcfa85dfccf3c9cc093dd6a2f452f78e7980efdf81becfb3c8f513fa4d8b75ba82b819fc6e5523ad164d905cdfa712f2458794c5aa7c1203926b4afb9429d5d379d648182ec4d109839879b37866a72f4fd7e09ddd392f82b5906e4de72ad7110d675999a122921123258402fa5cd0f3791dd7b13f7bf66823678bbb8389fc73b8f31de658f0ca3b66791ad8bd0f87965beccc4cd374a9b67cf779d4bac53b80d8def3efdca9bb7b34700bc78454c58276ab1a999906de0144f7e140e3e973349e6a349e6ab50ec9b548ae2d4d69da64c79634ac67b67bb6d76aa6b8330d3c8bb4c882e70a9eae7964be4478d6c888816792693395e0196bf1a645d9c573094e93399b4e66ce7cd228dcd6f3164e9693eb6f3b95d34e74b4d8434e8bdd5edbcdda4deb6c5bc76e5a9d4026ab552dab4d2d09236db0b653d57ef7dbb69bb59bd6d9b68edd3447ad740c1174b145175b8073d6b0dfb483fe485df3a1fe8ce2f677f34610647f85a80a59a728803cd9f86af0dcf511ea614a24d71f6d4898ede57788b8c92e64594304106b7b889016eb8d18505e865ece78b9f292f492e5a569e44d8ac2cdc44c51144551140a7fda54e34f2edd6a23edea23fdea9a09a4456e76671eefad6d02615e0dfd3895ccd8ca8ff386c4328fe4f0557eb42af8f361ab6cf8b3e5ff2e9b97c7f871dea4fc38b190f1a21759215d047f6a7c7d0aa44f2e346dd284d0fbb589fb176eb409f42a6f1562b1b468834296a79485be88b2e4a028fed408559e3ecb93e8b3d0a6fbf46dbc404dded347409b509e3e0d6d0a3dfd1bda3463e5e9e7d026d2d3d7a14d349efe0eada34dff9482445f3f1c2888522a4a3dcbbb608af21209b3fc0a26fd0cbcf2213ce35170e83d8cf21de630f81fe6fedeef60cf6fdbab60ed43acf23570f81f5cc362ec82698f2ed56f954c06aa99a2462d5f3fa6434cc4e55530111efc34532959a761cfc1da77f0f635b0ca7b70e72ff6fc87ef73f87b1073df61d062d0a360ef43180563fb2b78c693f0cabb609f7909ff289aa91adf82ff3f58e547b8e569e0d163fc79164ce34998e56b6077c1eea44b15d443d708c31087a3883e084baca345fa2d988716e98ff00e2dd2a7c182736891feca0c7c438bf4439806148c8098185aa40fe21b2fd86041243dcb07abdc2ed1a79f7602f397d77c28885290d540208d826c8f56895ec6579894af3f919821984948d367b260129924c8f5e787822805590d04d228c86e22bc027aed412f03af801ee4b05cdf7a1290bfec7b8d84017d7d47224f14d526fb9504b9aa1c0bb722fb0e727dcf833c0f02bd8d816790b9f35daab608fa4843d14e9cc0aa52f00e20b2fdfa298047fd6a00695f0da0f74c219e07fdfc70b0d6d30977cc6c3daa5642c2d8af2f413ff66a07983ddfc1d73e0864ed8364ee19ee2f40613b9100cd3be62c7754d2a4f54a521ca60d1ff49488265966213211493eaf7d9efa58655296353e5ab9b35e0c2c33c808099c509d20d71741ae1286c70e30777e071099fb11f4f7bb6b412f6500812ae8fb6bbfb7f72365d7e2825efb18786610963905074173e7bd06f4db670308047ada898010b967f683e88cf569e8281ab693ed7b8100b94f33c8f57b562b01e469a60c2091803e9baa7e0b49f90c670a147e56e1c3d17d1e37bb328f3deb5945224f53ce9a49935aa282c5b422d7a7b5ce219f9756f1a01dcff6a047516ead5354a7833d1dfc75b0d7c15de63ab8d3c1b683b70ed662b58369eefcd0b14f67ee402fdcbc15a21d482459be9406c872dbb68d0052f6e7c3d1a17828289d94a9e6ecd1e2c75ed6bb215967eea40301018db4081aa254a4b8a3cfc6de620b0973ede9d65c92915cbf87b09a89aa6a1328c4fb5a6bcf66b32b12a17c3cc96296353e94e44eb694937a38265c8a42a92ecce605ab3fb616b9829e4a275daa3f9fd81e17c8a4e9d24cc47999e80ab1436e11d1ec48ae3162bc0ef9f2f90f9e34b024335b9de4fa9a4de692793395bc44f15fd4b3fc09678a8bfbf97a86db4913eeac3d3c5f9f162161fcebf7122408225513063fe2ae285a2cc8d559443e36520ad0fea37db6cf67eba06ca10f316c614caf7c7e5bf93c7dd077dd07e3d4e751330e2e19f4910f902715be3e0f3a24cce76b1d69e4943c4ad99747299b34f2a53e08cf940ae1fc802cd507400aab57e34c4591ab8441f911f49eefa80bae87843bf30cfe3ceea37ce738f409c729048443ffc1e34cf1a8799ca95038532d92320ade014406bd6b713fbffd3853342f9f993f5866d7a1e4064b48220c19fb0ed8be135ec0891cec6fe18a7480b58009689aa0646cb822c31c9db71d3c67bc94c2d6031788b08716ef09ee6823cf39e78c2ccd9ffebd62438b0f72a586cffe9f3dd4028ad80a9004a982083632a30c7568717e8739720e39da244dda118e200213b29c5a6f51e8a4ed3c8fd25e71fd7da43ddd93a195ee4210a84075dd89a2286b8c0092e7dbd041479fb656cd8f26b6a604799436ddaac6082379943795fa040000a6470ab1885198ba3c3d0c3f15e1bd8fceeb3aaff33e6cc7769ed779dce73dd779dd73f45ff07087398c377777af2f9d6216d8752280b427f556d2cd4f80cc5e8b93a3b9eedfa1c275f01310fe0290a7eefddf4fa06fef4ebd002e1e777fea2e04f7dcec3c58039eb78010329eef30053c6327a4de7b1e0ff6bc0f21643c2111cf6f9e9f5a2502fe07864574efc3f3baeebdce47f7e0fbf83cef03fc3cfeb9e772be6d0d4827a9b8397610803c7d4ffffdf479fa220b36e858001759a24fe9d319ba898536cdf7bed364f805ccac693f7a2f01cf44be0f53e07aef9ef75c0cbe27d47efe46e47ee7e6c85bf879f01316f1bde7f3f8f8a4e77d7cff791fe07d1f1ff0df4f3794526a6fa345f99a27e924c9e7ecee04482bbce89ace1ebc44a764326589be0e3350651e3453a924d3af39686e43bed09653877ca13c38e181f093fcc107fa109030b04c17da431b12c633fdaf92854e4a4775a6340e3421f1a039d4a29673e70b2d5ef9a23deda6276a52bbb93a786072c79bb54fa97cd0fe0708481876982e4818fb9a48d3aea66978c6c87698305c91e7e7c47a6a43c8f3474984ac6d73aac8e0d6a7dacfd5105ca716b1bd0f6dd3b4e7ecfbd8ecd679cbbded74fc393ba5f5523b95f343dbddfde5d7dedd7313827685816c0a456e8a14d9de9f7bedb9cdb76eabdb7b5b751d6d9b64cdabd770fbeec3c1c36df3cd6de46813d5c35cb5d84a24172829cef9a760142c25578439b3de95732665920b7310896438de2be773610ef6436a91fb1a762c6c9b16fda96c868e6fe8a4e7c89727d755bd366d38f4d5d548980f8984f180efe9f0087a42cafa64a62ce9d15611fcd9f32f66e6b83087cbb9a2173d27cb3cc74fd4e5e288705d6d82f92914ce09f3e9036db5388bb448599e4359394f664eba499eaf82824b59b4086d110925e953287c922ff11b9f4a92b4e8dfb93f19a9903cbf3d47be4c1fb476b5e9087db5c9f3817b6f93a44d281324cce7f90f8f9ef00b3f057023f7eee270e7cded09f6d791cf9547e7d173be903033cf7730e689cabc849301fa832291274ae6272a852ccd9f62cef2ac82e650970aaebba86c7eedd1a6d15d5946970859e599c97d4c73ae361dd15c89c75a9c5e459e9ee4499fe67b4e9bb67f92882527274fd90e73f224114ba83d25f3dbc6c75eb991398f4c2078be44d9c764ab1e92a44dd2c96faf719cf63bc0ac85d539ee67e6421ffcb9700299b3e944e2cdab6f3e7da25aa49c02b8d79cd35cf3ea79e118830ef2c5bfb9ef6601d316e70c2353c7f239dc5916e6ece76c3a993913861738cffb98e6f0f61fce816f6851aa90b7cdeb7efefce99953040a477b45cf85e3cb09c3fd8eec8108bbc812f757ee20732f03731fc30099fb140270df2b3fc5f8094e2e06f6f0b7c87d0ae62a97c32b49be70e128442bf432bb06426f012164429f2314869ec35a18c28ec70aa0b27f8c7358c3282f3f064819143cce16e4f99bd56490f1a7f8a5ac295007000c4a679e3479d3e44f940572e06ec8a384c99a18b27c1b5206f48056bc9ecffbb89f0b7a1e9cef09455c5c26927e5f484339e99cd46b71865a1c6ff6df6ebaf0a28b2ebce8a28b2ebc78c213b678e5fa5d7b64f7205f2a8e99e9cba6b23df2635a8654cef68c17c8f274a5489a402f7f07794279617c71a191e2317cb2769e84f9f8cbef3e3c2d9ef983bb255fea7fd833a5d4dd41f01d047d89df4c58cba64dfdfc38abc8dc8703f8143693b4c9e66947bb4e426b2161b8dea34f28f7c38dc256520f08e441c9913092a6dae4f9fe781feeec5802367b5e067236f51441c248e03a161206bf12c673c1effed37dc0e762703dded8b22fb8f0781110227b9e8855d1adb15b63b768cafc667098cb6cbcf879e91d9e4fc4aac0c11452cc3a514ab772b73cbbc7037e3878de6df0783c9e3d3c7b00b10eaab907d67df73fa18af6200db4390ce6de851fd80dc799845cbfdd434d821eb6fcd48397eabb5216cbeec334c7f0dc1db6a229d7b744acbbcab7895dd26119baf7a75307f20507d20528c0a76df250f0c441220359fe3c48f3d4010ea40b497a8908890cfa480d2e1de7bae384f9963373726eb754527063577498c3e44bfd199ee7d48574a9fe9022b91a7125a40bf53dd855f2c57b094e90cc4496ea7bca87f8a23d1f8e9abfb06572c23cd6269a6b8cf6f8272227dd6ad29c121aa7823f36930c86a00f07f05d4697481bc0903e68631eb67a74234f478b1602b3882172fdfa75aae44bfd98f660cfddd6b63d2306021c1883b93fbfaae409e5ab95309f9f29a8dc6978ca74a523b5fec0bd4aafcdbedbbf4bdcb1e4caf64bae2396a341e9863b52ba22cf522ccf9ad24c9ea30a57e4a9422c4f2aae689d46385e620d41d2e4199b71c40cd78c579edff292305f6e81e5efc79624dc8fa4d88cd40cd58c559e2945e4f9dc4bc270797e0a4ac2a0c07c7e4c99c9738b128e2cd99f2f4b4b902792aae4846c4baa15507984e3a7124e29d62692aa3404692205a9a3128e9f546024559b54624d0a224d2a4390a22de1681fd32b449454f97bfb28b8468e97ac0acc4fa4192fc5da54830ba9320458ae1b187df0687e38b67cc31a33f9627f45033704bb6f2b2b8f251c92ca85991848d17e0927975c2d70916d0b39d956554752f969e5ea224a31b0ac15a8aa72bd619d912f355cb1a1fef7328f3fd456aeff7d8f0b621e3b661036134119c1296cfe40335606f41d08f77002fc464dba34c3ce1be17c328c9ab468b358694129a74d384dda04befdd2acf4fd7cfb2a5950eb1d691a70355d1279f4a8c8f63bcf73779ffe9d7bb8dacd2ea41cf6beb63af6b927a4e9aab8d64b69ba41648fda1a7d08db9230dcf398b9fbabeab77f83a846952cba1694c8f6c7102c5b169864fb2349c5028e7cb125952cb2fd894b38f2c57e7b610b33985cee79cc91f542daea274ad9da4e5843255fec8f239c6c4b38d97e01525cb768abecbf49a90179ea843f74c94da366c49c2f65e74922f71d2f84802cf93bca51201877bc71439fac68e6a445af39dd71f679d8418716fd453f75e10dcd85379474421a5af428dc3a9b692b0acebcfc79b257279574f38059855c33704709cb34f8a9c1262e0d120694ebdf90c34f2ca8ee78e3460c7dbad2faa95ffbeb1a90d1de869ffcb591b6e51b369c838616eb2c896eda169d0129a84cbf9bb3bb9b36716f00c9f4eb17f25491c82b648d499818992ac91449a694522aca022625a53366c670cc8d16db33c3151a13dee81bc5155d17d1eedf6a8d698ddeb4486fb448e9e7823b6f8cb386312dd2bfd1226d19d94d28eacd8a0b6e7d4b6fccd0c1b5b11afbc4a6ec8ab25aceedaf610acaeba2d6efeeeeeedaed92a5d6ae9b477aabfb64086202372412b9bc5adc2ef59e066ad41a721c2ae4da55cf5abdd32bfd62f3a89673479a679d52f318e1fdf07e783fbc1fde0fef87f7c3fbe1fdf07e783fbc1fe04bd2f882cd8e1946a7baba2e75471b1bcd1d6db8ec493ddb9c5c4d8b352d72b0914bc2d5b4a871af16b56e6eb6d32571c71112b7ded7dee5e50273b1f19394d11ec452e6835d5ad503f10a11f0efd3ef34528e97b477418184e9522061b8073129a709a03d7541650d0c49b30a853bbab046d22c6b2db1d0ac6271471796cbcf01f46fe8c2f292f66368766deefc512c34fbe4512c8a3b8e5823d6a8357a62e482656dc4ca1a67f31ad9b47deee525ed391796bfc5e090ebe2f2534bcc85e5d2b2b92eb096d811b7f3c01d555679c4c468c8871bcc428ae012f0882f0b8b41997ce96fae25e6e127932f5aea8e9c0c4cc917ed8330802fb2f603c87101f6c152a673d1dea5489bfabdc585459f703d8f2ef7754717960b6bc2d043b4b236cadec24cc28cb21d6529dfdddd2790fd66fb12ff2066f73e19bcabbdf7c9e0b5a8d99769ef682bbcac07aefc5165355a6591b51f695491b5a761f344d67e6461226bcfb29230dbabacc026ee0a2e3fcdd79ec7cc2e2c09735b8a90b5ef3a5e104631daa2a11988e3c2fa3a0fdc11c4c95a45c99796a13fc4449a48da8770248c35919c2045edc750933c8a2991b50fa9248c7f68c6246b0fc29130df8fa07dd2aca52039e148878995c243c346c2f86b3f5aa9ac326d693e7af1eeb43796aec8da8fa358d6441464edc7d04c64c99751658563943d2cc5e48bf633985c54fe7e6c8965cddf5a97e7c3d1923dc4c95a87b5e7b0c6c9b2f6dccb4ffeda1ec7cc5b28ca17ed7178b62111f0c15024d2dca5488bdac7b4edc07569b9b05ad4feb6eefcd185f5820d8d994ecaf8ffc0fbc1f43c2b244e9b3a779336d17799a5d316c126aebf56843c552b24135971244c977dc9a81d91ddfda5ac5cc4e44934562b72a845779cb9e26d37b835cc41438b1ec51d2b0e6cde10a7498bfec13813dcb17a0deb0c67acf22b4e9d856ef01522447ea2c14b6e330b0cee58712a4e9b9ab449dbdee7a6bdaf6c2f33b542da5457da10ad880c3c6a68436d9229a930a66cce9e5886db9c586e9bd4b46fd165f345c39c2e6d704729e3a16532dab425dfbb36a148515ad36d91caaf4da21651a46985245fe8af882e52a44fe9f44f0bbf453abb767f1096569f2ffd696def9794c8cccb6c2f3e0ba4189b1aeb7e90baee47ad5da26fbbb81e9a654a4999be683a49ea6274a022363bb298b981f186b6bdd3771e383dc2a437602aedae6a377032d3bc582ca6830eb1584c87582cc60d318bf1936f5608b693736fdce8dc1c9f1889e2838efb3a4ab85e6b273b76d32ae7497587a4d89edc51e2cc740f1ca40026337fa6be0709d34b7e45c3914171c73923eb61866a37b846886425dc1ee6f7cc94b1df8388037ce7a5cccc945991e18a03fc26f47e9615f42d5fa67ca19acbdddd3dd47949ba778c6f8daadc53b24b81942e6305d5ece4d1dd1d0cfb1dd3eed4f6a605e1cef75a434a4ff673c2b796525e17dcf9deb7823bdf4271e753771eac6aadb55677afdae649cef3649b64a59d5aabd72bea63036fa5b5d65e51296a5ab51f90dd90704354a300dd42295e5112a6bef760f65d5dcdc6409712a3d65a434a81a8fd5c0a3a104a8a8a772710cda8b47f26e1cedae944934e20e68c09c44a051609b753595dd60c9b884e92bafb3891bd16ec783ceffdd411751da5b2012d6343adfbcc3ab9fbb1d41de4a17859ab00953394563a1d85a8a81ed42e34bf1e548e098a9bb8e3d3f9543a45c10724c3516484bcee0437a5b3d33aad1677524abf8be1f547679d32bc09a8cc8f4ec04fb54865cdaf48bf7be8ff7d32782766e44e9127bb0ed71a25f156909815d7dab8ae28cb9a202e87e2c6c8b2264809dc06413ad0515c4f963541504e8325bce0aaf8604ae186b2ac596206dec412447a043725cb9a25906062091f34f7cb99b12b230b27eead028c0ba28113b92845da07f70a69275c9b658d12345307f7cbb246890da0545e9440c5854b713b59d6a87054b210cc4a30e7ab4649eea7362f584d8bfd72af74455333cc4c693ab4d3f19e1d9ad234995dd2997d33b48ce35a9e919adcffbd3cae1966831e88faf483785094508f149a92e23d531cd57259eae5c51d698a0ca7d6d3d2ab6675ab5dfd9abd60363199b7506d8342a150a88ec9706639b97f6bb55aad560b8542a1502895cebba0506896d3a8994ebd5eafd7ebf54aa552a954ca65ad5eb55aad568bd52dd7ab2b756fb5bce52d6f79eb138a2142794dd8b49931d94cbd5eafd72b954aa5522997cd721ad5339df2d7ebf57abd52a9542a957299cb62c800e59455cd158b07fa7abd5ea9f94aa552a9d4844d9b1993517fbd5eafd72b954aa552a9192b343cfc59e1c899bd715f22588be2bf4874ef54b166abe7ebf57af54b1a23efdab037562b1e44d1e2dc99e851d746a3b33aa374e674d6e2755e5c30b3c06eafbce4a72a6eb7bb91fb6dfc546daa4d157d15b387a3f8e1f872b579c2a764e9b1f65e91487edd13eefcb669d397e577b623bba900267492348656b9692ea7f21227a44fd239559baa11ae0837c44f154897fa698468b98675463b92a59fea8c28f7ad40faf4b19971e115f2b1a9342df913eb71678f72f52c3b30dd27a81a3d38c8fd1e9884a9332572bfd7a44f9f954b186f13fdc0685e405c5f8fd693dc1f96e7e463d34e31ac898b3547f9b3f253dfb096b49ab872bf723ff9d0b06d60eccb6f0943e753594e9f3375ebd36f8691caf3d9e03c82b0b9e58bf6da942f1b063ab3db6367ad4f5d3da8dbd7d91312c6bf7e6c72addf4afa04ae60b95670e5c4d5a4952bc8ca355475521461936736981317cbb5812b3f6d375dea5fc26ad272927b7bd223779dc9dd5ab8c5ea4cfb055ad29e1e5a81b4b0d7de1b435a11892a7ca0b8542948000108480a57080923fcf844f14241a24912178444184040197c9230e287272472cdaefd00906f06319089233c18cc785ad081eea5fa5e46c4a0151303d7d5c18fce9199ce0a7ba4c5198103db14389d2d3a20ab49420a3e5a1950c5cc606609246260f02a321312d160c64bb21a31624a3b5f7e33d029864c51e466800646d8c45011eb92bca210e0d42c6b40b0c58f66e61140106b1bdc962c6b40208513f7b3ac01811260dc5114905c1523f4e07eaa0072b71ccc255c192e68215c30cb9a0f0c89c1a559d67c6085800fa86a6878f024509635469420d32c6b687290c7b731826ea5062da84c7f82ab16291872dec8fcd43d7d10076c02ce4027601337c4449ab81f3284c3814c297744a6b41fbc429e40952cc5244c4aa62408224f200c66821cbbd172ee08c2626012f0d5227d70e525fa1c068b7430683168047481475aa43504592dd25781425aa4609216a9bb833620ac45fa200c8481b04cb5154d03e9833735997e99fe9db9347d9a4cd8804bda246b903092a92ad31f412739994e0e1c91c78bcaf402c9740471327dad9a469026d3559b4610060369daf47476912914f2080e510077c4a46fc1587f378305c9fd817b65ee955b765a70678c6011327d59c381551e350d5b501d1494beb02ca3592bcd636934575dfbcb39e71452552d82b9736f7695f5dfd6f51f6bac43b9bfde743d525db09964ce9c14940be924695491a55ef2a5672d3271edfce5ae769db2d2ba792aabc57e3077d4bbaf5b3b9079741e39d7846105b97f56ce4531f7029bb82e0983929b7ed7245deaef0a6bb17b065dc9a1bd165a60cad4b0be10329c1451c834dc3277847b71aed9f53c722e1559f5412749630152d93ef89d7b5deaf6fb0ca3dc79f7eebbd16b95ef7d4c733305955740e5cebd0ecb4c3b5f18d772ac164b455ab4cf6a955425212dda9752cad2ab45cb795f1877fbc2b85a09a7c4449e463fae3093ed0a4364fb297191ed8fb6b050b8a330b20d5b3690ed7b985cedb58fb964c9bebd313f955825562c5b708a6cbf059584e9bcbd2a0d8986af65d92fb1da54c47e4b966bad25ae8683e5fac9b55443a150280402815754963ca9c0bc1e215d5e5f1c179bb8a8c4cd1476410c95a21900000000b314000028140c8844a391581c8f035d991f14800d7ea23a745a9d8ac31cca611443c8106308000000006000464646a2008e230e90ce6020521e1d441c93c903feaad4344104290cef06c190a646f319881fd14c2dc2e9caa4b93db06fae30edab91a898486329efdec880476fe1125c0087021f9dc874e9e6f8ab7ae148bcdd9c8c6b18dc06c4b898744767b21ab7def8dc6e6bf3618889e137e17dcf78fd55e8179e05ab6fdb249a4a5cfa3eadb4e130697f00e3c4473339f8fc7f9312a3da34539a40a112c4375360dff72369a28b69db7e4bf814423b5479409f066dfb9752506029770c63270d95336882337a18bff87cca26838d4a841a4fadb7cf76b1f748aaf220e060ceea770735d64cc904850d1481f4f93765786a993125ca9d5b1776a6a31f4a1f31a6370ee79a50269377a6a5d8fbc89d2e08a3e5f238accca0d4cf074741c58a433f05082c90f2f2e50b32b9b511799f650549acc4f710de5c247c904d6ef30e6f47575b20476ba5240ff82c1445a83ba451a47931a94978a757107bb3a56addcdf61d7e0182010dd010d2b3f4f463850cd24a5c4a66284e3843d17808109cac53a9efb6333528f499fde171936494727be68cb41539f76b5b1b70108daa7b8d9d93a3b9c4a04a12393b4ededdd73a898aaf5b264067a8710a36f33e33806a5f385fc4464c97d02440b60ce6091dd05bbaed214cc364665351e4ceeda7efa411e5aeab06661826c1fa1c205e428518580b60527d451ec8427692f330eaaf03fce2099824d559caadb8f3e03895f27a30441f869b970880dc6ef3afa37a7fce7417a1132f866602b06327079b91f84fd9a7bdd6e26f382acbe7ca588917a986047d7ae81494d358e39c4ab84eabbbd27f8cfd516dc7aeba0322c409d1192dd59d54b1d2a70ea22ca2fbfc40eb853d38c91d12604a66d5ab0e527efdf9462ab44a1f74c9fa3f4ebee12ee8be985e4dc1beaec038c93a8098e8855d64b76d194a81189d4ab90a4ecf97ec1584b9765c677b4f940cdcd79416801f509680f1ad0112c050331567f6d49128b7c1295f8bca227946a0916b9453d0f14071a323fca47b936deb7db13418b902e804f2aadc9c63b4a02ca3107d738daab30586d21e7238d929c8f1cf2db205d2d4d30ffd60658262223c35046679887f49a0ed565b708a100c1936d75ef27940e097981fe5a51a037b8cbb2c4a89713f0685f583ac79fa824d70a00eb604e448cf8cffdda095c4c65f237648162809504cc43212068617975a88fc4a449be0f44898acce7f9580fa472fb7152b7b3318005965c87c4098009c2c57f08dfd02dd6ed7aec128a425f040b00a0615f5f9328989017de7675b99e07977fc12c4240971f90af7c54ae228cb0f0f7eb516442ad8fed156d5fda77164b5e7cfb55d1058602f4e111f365a72d7abc78b19c1489f1992e8bd2d87aa739cc5b0a573f9aaa3849fdaf465ee455e7c6247c1270dff67c57295958f4ec227b822de5ffa531c8a8d336cd93f6e745a23274bc6e00f20823fd192e334c4930da59ca17d3ab3b93a772d59c1a36a700f568854d83378c3d4b24336ac446eb3fe43a7575ae5a8e5e53b8abf3181548245d5240949c9559f36afd0c8c0b6e9e706e23fd99833453b6222a98b1d40d3a79a02d52830d804d11963cc148f20b46419506ff4b80c963c2d2fcb490734b2eb0b5a8d632245d02d11a033855593b4c2112c3302b5b53ef9d610ebd5b4d1792f096cb3e29c4ec5b9d447a0d225b793e88e6f54533843dff004cc0a2e7a0f280bffd0e4fd1688c419e632c64a87f636bddef489aac92439d3abf3a72d60c5d384817ea119341441f6ab11915af458a693a8a5cd0ff08302e786f7700283de518bee06c819faadf450d93839f2b3bbd45202263414d795757195afe0d82d66497f2f3ae801d90922722fa17534b6789424a31278e89e6a566c44cc3ebf8a7a1b70b9117606449f416b1dbcc6a1231a347ec8667b7630352160f5657aec34028a22b54eb20d3aed0efa515c24003b6f47509d123c42423f6a28cff1c3672ec83429fdc6b00bf6355558f1bc48c482456f69f22a29c88e26b9f3f11aa722b4283b5c06f605b076ff80b937f6c7190c460a6c4403101f552eac0e310f251b1e49bb0b70f1492093c739e0bc7163112440422f70eed93aa429e7b32b0b334c244cf3dc791d3df4c170bcbc029144b9222f38db9a1edaa9508713de03b4ad9a72701125722656a290a57260cbc19a4caf3e7321d8fa078061ff5cbc32ddae6cbbf69a01f224d6daf02e504ed9c5622572a547a39791a72142805e68ad2af350c2a8288681af8e7e5e74276ba6f344db09b99dd0eb8b046f938d3ddf9da64b3770cdabeaa8d4c3c6434047eefe5323711ca184a14e29e26a82a8957d6dfe0292fe0c0cb65a9d815e1f729c1aee13a6e7d019641073b3a62531f923cd31d456fe33fd8a8ae7a1829222a624006af15e837873abc8fb4186f85de4d48533ae0fa508012814448d0ec819fcc734d6240d03739a43df2082e81c22226a462f007c89092127c0c4163084e654330d4a500830042a0bf827d1edf30c3a95e32d0d8ee9e46a84a143472f509cbcd8028a41ee44951c8a61a40e82a5f746eb43c529388f71b869a4fa55825228814adb09b87d82388a87b06944ea1abdc91e75c441bc9041ffa6c43f6898401a595a64f662b67960f8e12b29a606ecd695875f19124eb691aff3cd7a1a98925116813185a5341944e1054af1302607731fbff6dab30c4a491a5b377557d09ce368d2c04d3a2451eb4c55158630f42d241b11e7b06112905b3a98c6067da399630fbcc7f779019dc5d027f111456e53c17660ece88e8d48a8cd7e42f4839141638f88812ad00a94099467ed7bb5683898886b8b1dd06f68abfd2ee7e4ae6b7204a08ab116703c7792737d6805050081c5f64ac125624d63c1695eb98a9c0c524dad006349830998bc3592151c852555528b26b53e02cc5fc48248c875dc9687317f9347104521d02100fc24c524fbdfe889354ef01aa51f4aa544a5289546f143ed7433c05da5e885715ba953ac10f18693a71877993c26ce9d7000ce4e99e87c8dbed825d694286269c62194c373db7e88460af1555d6071cbb9a90813f76131a622097ac9800c070b6f0808424ec0c5bc8864477ce162c4c8d5389d9597f8739542d00e7ab30777813c96bbb3f093bc4dc3f1745d1b81a421c7a5c55d407872f8ca9d4f8d902e6c2ddc25269973e38dd40fb5d3cc829b8da41fa5511225aa9cdbcbed1983e9c4b6bfd9dfcb2086178ab038a5432877a8c9211942a112f99d41b85b066cc39f7cf9344b4fe83c6c0d8fbd7d000b9b34fc78c27c4e4674dee4ef4d2bd20460a9707dc7f92963880024b1022f1fe965ee6bc7b816156a45b769e1bcfa182f3e80c66db3b92463da73eeb1252e882da7813f715d19a9eeb6a8cc4eafb0a38ab40313c3f62330275f8e4e1f945229982045769f417a12a5661bc4814c00d655f88a5aa5fa9c5778fbf7ce0e863b98f53500bb925238af8be885f809e71d7c0c1d9872d02d3c7072394dedc4adac939f957e8327f2b9531ef230c1c261e43f45926390c8fe5818321ee30bfc1827006744eafcd46f929f6e6385709a4956934737030036e51988884c0ffe1e0d0a8a502bc109d01444c0967da791c5649aa9baaec639c2994d0e0dbda74ea8c64264415057458b0cc9c5a8b51ee5c134172f17a1fe56b24e1895f12122c0bf6661c864f7c0d84d985a6c280f38b7efb6d3b87acb4e513449ed4434344c67b236d184480bdd122481a2c81915389d6314282eb2a59a0e24d0b11d52a05e1ac45eb5d83b551467a51774137a430a46e4bfd4266832f9961218e02a2d120a11b040928126e82826bfe69eaa02b61fef94270e8524aa66a39738e5108c92c327ae188250fe1f1cad5a49c60b0ba61d465c100cf44eacc187a740ce7ed9481753b1a1a653e6cc6de1bdcb5502e48db947479b7dcc85c17f02bbbca4c69b59bf4e3b9341e0f0752880951c1827cd66195e9fb72c978eb5f2e24b05eb9c6efb313551f23d5b4d382c396e33514e023973380fe00ddfa0c91510038a1e0321e0c3efbe9a59b72099d0890397b27f1dce72416586e1171f697b6a8b9aaaa0f0c3cfe0a7acd95b75feb4a81b74fde2fef17b0acd7c92a5d7ac6fd4626bd70d01677cee8c2258e408dd038225ea477c163d2dbf5abbd9264b7e0a51b1570de4e376d2b26c2167cf2321494b9b4da83a65b9facf032283fefe8085df5a19f686ef8af4ac04d08d667515f10cee084bd7a9ae9fa44a581318f18de98bf123eebecef3935bac1bf57eb3b3407ebae625b392f798bfbe361511f671c735c1355b0b690e65744e98d7455954f7c976bc2a998b25c426b4c7f6751bff1fadd42545452e60d5af31d961e6ec9195c429cf7557cceaec3baf028ccf36778d97f06ae447649bfa3087631e4ead15ae67b4e322619135caa87b84fe37014852952f98818c2e0779091dea0dc8646dc249fdc36adc66931b05ed02254743e8015c7972f30fbf2ed4ab972561b67938f8b2d8a170497169d3baf8fa18bbae56b105522c207a7720f0dd38bf3f05e6e8bde71b40bbb66a0e704a7e81925dec0b2f3f84a34e1110b3cdd0199c63788e229315651bb419ce7b47b95612cb3b2e3b4507873aefeeb4e880a16111c12e69b4d0b45aeb415fdba13c30bfc937550ac00ba59ded28d8ddbda3e66dc60667706ef7af59c3987d9d28ce174d7bf694baa8a85306c69f908694d6a17ccb25ba56065a46f161658dbacaa43cc40b2888cb840b1c1a9a4c8177b6ac57ba6e1a58dd589828e9e99a344ca7037586abb98e4e3259fe612d8ed4beae3846c589fee0414b6461b7d332f193dc8860bf9962ec2d7f7431ab7a031d6dd649602a516f5da061c71ea15a89e23aa1b8465b10d575863a8ab22cd23564e07c5c41b846a3e1bc898272e134528fa491053fe1d538bc7c1c250f79c45a11e30a42b8b6ad88754d438e88b59451ddf227c62454ca9addf1fb27c6e7054351729423626572267d49285d2f6b115ce38839aad3b35255be4b197c99fe30908bdab84815ec980fa8cabd786fe9c00f9dcc5dbcacb49195aad6fecdeaa2ea16bfda62f20e6a0a9aea463adee5f3ac49d874c3d9effdfe5c44b56f0f3bfb1586730f99ec661baeb7d8d330bca494e396ad5bb982357d0ffd2e38bae872d13844230dad4057eecbe9dead92a6538ef391ad8359a3d457852521e8f71bf1b8a8f1e311ae5b75a871db94f3c9731e08d18a37323e8208e0cc5558e0c31cb11256a021666f728daf56e30a4e6b8404ff6656806f39a1f0cfb175d031d61f9945490e3e6305b2a649a551789e2602251e11935bb6f2079ce1afbc8bd36dc58982d4a6a403bd5c6fe37f89ce19a220c98e83c0795c6a2fa1abb386250935c0c9e9f7059e274f14dc88426ed0ceee526fd2c001cf225b341b5470e4add53c10b64710dd36154293034693d582a26a5bef609ab43c73249126e98253e9d68d3c0259946dca76d06722582a8950bb2b45daddacf44862482c05ce45050eed91172038488aa9089a3a0d7f72c3e8e02ecab4ad6a50ec9a48a8269ffa90e187f2cfd600e8baf272d8a49a27b211f8ca31168901aedf9af40f7defc54a5f4aeab6e9508071e3624050d016ad6528c1e36d38ab2bd3dc4a0c81d9bb16411c62625fb78bc25d0398c7aa9bd83582fd2846adea736077a31844d283c25ba703d94c95d9ad08a8b5873540dbd1597aae301c846cc4f48087e3d6923a60e8f2a68e129a3dd4e23318fc58104f260176b1c970d8d62a4932d8de76c7dbf09f146ef382a8572e0df2d586869a29d39ef8538a0d529b4b6856c59e80f64c0f8d6ffe981e950927313fa2f799052d8ee7c8485f4683d00e8dac7cc93a0b020ac1cf3cfef3b04ace0ae434bbaa5023d8c2718dc7b54c25060325dc2664d07b9f4ac23e06d234a4e99db4be8b420dc1cadf969a7b9910befd36e957a1be84fc30858500fd9d933584a82a0f91a2284b78001cb8c0f7364fe1775766029ed08b6e5c49389b5663e79a96479f3b2ccb53f3242850202e3ff0bbd7b7e02dbf672167bbbf62ffbc3f9b6acdc75abd7084167cb2ee581fc9553b5cadd91e0ffed827e9ad0b8755d1caa0e11b7f1d069b6c9a96af168cd7d2051c03e4df00444f96151b1eae20450d3e9cae26add55672ce0ba12e6d2714787d951cab24422210148daa88e491bb6abb1bc04d728404e26c790f24a9e137193c975d4328f81abe3718a2f17da21b108195c3e3906c28cba8e4bb3674639074746f0bcd7d65342eaa0db1bce5c6ed6e24a75b4ef4971b7d857e02f30b6cef97468b86887489fc21290a5f4a998435a6ade4c7a19c77d058c3d4cbbb9d0edcff524f783617291acd6d516fb5def11f5c62d62508d3614dbcd3d7fe4eba3b78041b0142198a1c1231f67139477a23feae85468a60255ee1d618dcc5c6999094b5d44c3521d86bd9ac5070e09ee24be58c7ade5fa667902382dc19dc401fdbcb2b05f3aade384838f5b962226af4c4524a886cf6b4ba04475e5de13685ca35cf8287f98145d5acc4ff290ab64d7295fabef27843a91e2868abe8c1d02d9053f14c42442319fcc7395a082b8b5f58b950b1483b2e221e1a39956fe1d87757ae5bdd65cd4ca5963b36770e78ce001f2f00842767f562d158d00fc7e240a649c4e134c389d609884c97a7b74493cdb272ac9d6c2abf464d479ab8caa1f487d27f686d1de0d55de6751b0ec59cf47df6e07ab0d8fb6bc958360b80e885e523c6a4a4c11993b8456c67e41e2180f6e7cb3bda9bfe1aa2d3baed49edd1e00c146ec8e43eb266cd0148f65d01fa833d8c40b5dc3ee751e722aea563fa9a7b852708b4a0e6be0ecdef6d82dc71dfee344a19b892d2cda8038d3861b29259ed6d7aa03b3c05902c3f10de60439c2142c80615fc5593b0134a06a0d3cdcdf2fed9b2885579aa12760ce58f24243c1825062600685a262e48b5c19156ec82378cd2eba9c8bca64397b5fac263ae24ca60cddf5c7b4521c353ac0d69efc9835b4b47296fb592a0b94449ac6c3f495a560a1b830c4b9fad6690b856d859e613b22a708359d58b5703ea2a2cf58997e86bdf703ef434c3ea71ad111a41600b339f8a4751f1a16e7d5085e80a448dddeb3b62fbcd9d00bf1671c7a408c7a3aadbb1bf169b54bc0f6008b088c92ce58f346992c00827d9436fae1f61906f2c8e54cb076a1ce1766f5382a8127a621a6acb888972d316ecc3dde466f64fe93fe1aa9fafe35eab6d8ecf47720124627f37788e31e858b3f173224e720a6daee8f98a70bac8ba30f734bc7b1a444af0ab5f513ee14395535cb5215338e8999d68068cfac4fbd55496048c8eeffdfe19d687eddf53aa1ebab027971ee86b5c018ac5058449d90178c0a05722497321b2cc23d5bd238fb5dd0187813218ebf5cc5671d5f90f6318a61996f456246741a9f9096995c562835bd24a8da832bce38b7807ca0fbcce10cc481114714b28a228152587dc77011abf1289f4513e92428a4c74ef8c33b0ffd86f42016f9ec21eeb56c0ce6d6682c16ab57422fb92e9f08e5b687160d95002b790983c59ce506b9109505c7addb8e66c6f98f8b3cd3021bb12a6a33a03236d18dee2294c79497ee576dc1d1809b3c0229590309c33c59c2157511744a015918e9bac307fcd09f66e298fdca410f6987a3813fed3ee3b0e12886e86f647e06530a737a4dd40ef3014cd80ed6024a55bd54ebf4682ace04d87c3bbc1c86495744431bfe85da491b40f7aa63f6923e5c398f085a2661d533b520549639c35a9744428972e57b822c3c7b05c734b7dbf078d6971121a91b4fbfabf5d99ebcc7ba455526b61874c4a58e0a750dbe57a05366d0a0dd0980c9727bb475b020bb39224e66f58560b7fb2b79bfce3afcd9a358c93e31f01dbbc8844c8a0c32ae97a879ecef5b8d62b3bff266b2d2fe2c44384e0edbc1fc57a3aded5d1de12ed7363157af34b332b531e9c22a9bedc3ebc52f870af45e999f588e207db08f3bb12fdabb210fc6162658649845d8b7173dab6b41c4d050514c8443d1a239284ca53c8be57a1f23e6ae66c20a2c262a0fa38e5edece3c8b4d1918920b40f39d02615752c8051a378437c12be4257018fc9c1a6edc63fc6cc73f00ebdc416f8c2b9d4eb552412b658923286f37c422153fc121de577d20789225b6a1689ef2b677d6284213ae1d0ecff991552a68c484e6c9ba7cb80193f8ce8ae6b9c7dd5cc8195287a311e2931c17f535ff247cdf3a012ff9919003148454af80186e5aa791917053d363c83f83db6d73ed23047daff3b01d6532c38c26919aa7df85c40bc6bf03c0e9c769e6ba9c4c5d4196e2697181653861a204f076b5e2a465b52c41d131c8d5d48183bc61dd0ade2d51d272f8536146d1a381b5ca665622a4be173793d3d716162489b507bd5a200bbb8182e2937bf1517d4d3c5e8056db3e40585e71d19a6ee0d5b5d40649c0f0ac107559d5f0a75ffff2fa410738e86a8f24118f0a81cd73e95f84989854bd8c1bbf090ad77946885423352ce235c6254ef03a6865d6587f45c6ca746da201a7a5394fdc35c13bedc33bad1c1adc20af3b2dc49f24d848ca47599f332ee470fc87ca261f3c065a669b77c9db2a6147ece1ea2c7fd45d6eecea5a238711bf907b1d4c59ad480fda2080b91f85950a88b6c8cc3fd78e4a4aadf1e8047c40f4c60e12ee71ef040299e7c1238c185a23cb4d33d36dbf769da539225bff3406d52f63c28efec221f2a24f68950aa046f88b27795c52f68a5b7617d4c922f9ba467be435caad5dac023742d8090003e5b34dc13949d31f1224e95978a412757cd8c62d2acb78181cdad58a69a8395f18a30d80337d259bbbd3cbca6b35233360b7af55acbba99afbb1e422a8ab277d78f99cd1238804506b27811eab3f54dabc40d120bc934278c76ea5dbb9f857ea473c897413d8d06031c1792c10f80965898198d42019b8ba9363c1709e6e46c2c971f99bf37f890381c2dda2a2a3084bc1548dfdb10f19434896fb4687e7823eb78e73a7b0dbd064bd86ed6001f7fc4b592026460dc54ec27a262d581668e1e6bc4f61ea0d633fa7d5b18388645ef179078fcf5fc626058295c7389754d0330cde02af825d604420f5ac8ca70cc9ee37d129f72cac7a6de9928e281aefd075a3b05fb24d34d4daaae49d49b5b0907f4c989f1f9f35d547e7fb80108bd272c84e1cc4ba05a4f36bd5c8e761457cfc9919df30f07fc33e3e97a67080f17c3ed7791f72ad96dc27dbbab85d8645794e5cbcedaf66fe57045d2c11ae768939f1db9c93f6c762a69e9fa445a86da198a1504bd555bf9e929b88b33079dd4d1f78189eca1cf9895f84d4384af44b088ae6b30bc1229227918846ea86f6faf06aa55d5c1c0bf4972f9152b256f039c64046a9802f4fa6791453cade72c5f04fb960ccff958955c9a2b212b41027462998efb9cd58c1db31aa2b35c801d89a871f43a5ca64a182b168a19870586f650c97d947e62024d9692df0b0d08278e87b6a2e56608f7e5f241b27ef0ff7f29b3e620fe8b7432abfef3ab7d39e90eacfb8340a483da27a772daff7e802cf7eb8e836b49b7cef93fa4ad19d7d6a80cd2a955d5059926c4b6ca7c9a8a40d0dfa3f3f6f96cfefd9445941ad75184aa15f0c83b441a916bbf10725d5ff8f7d7a1793787bea983c4bdd87f6975b0d29453e71c581a3fbe367f47cd3ef1dadea01117b5cffd0409abf01bdb50b320eaab51e812367fa7ad4c9d36eb3d081c613f115cdaf9161b63b6ad745b23365db5d7db4f351ee948991ab88fe7b5be007f014d3887d24f26360cd2efb526adc5f93e39f2b38430e71badf8d03ba4bbf455e780300fe59b48b300124ddcda6af5607baa5c41d305c5d10a8311ed83790a21d77a04422fa4506408c9c39dd5df6cd20fbeed885b995c05e37d720c013a0c045bdab2367d18749d876575218a56bd4f25a0344f367a9ab3a2f6bfce068486be507da59448be9ad1d96c84656062f0c10865ab5c8963d67d485251142448003bdb1ad945b0522b4a94325087e26e2ac2fc9b040b5fdf088938d7cb7a10e8cb218a66a1a60a2ceb0229f26147e97fd5260ddbc14a408665f19f9204a1e2cf1391726ecd19a9d201e623a371dad33a8a8cf4708b4965a6858dd115c37d9742caf80df68dab0a696024967a0f87e6f0450f54cdaa43327453b0404ac6ec0f37e35fb9ed195e3f56da36aab131b1c5208325816f8a80056f33b4be158030f897b2d58915f152fd651eac9683cd8f009724df13237ef13b8e82691bb7adfd30107cbc610a9413e59f985020cd6e5da27768d17a9d2e1b43a9a8fae41cfee410541a6d05caa5c2974e246978a6d686019906c1827cb404bc5605ef1c6b5cf3a231424d1c0154e1f51eed506608dc4e2d889b912a06abd97c07e0c1de5185b26b389e18af081d137dc2387f933e52c23ed933e51dcbd0cc0e4ebe0ab1b47a051f5838fab25d122077f2697f77991b0d60eaa0cd6cd3ae0b04d9989ee89a1bb1d98b6c803224fc21a01e9e582ebc509fccd1299b131938623311965f3daaa159b188bc1537497b1468bd9ce74eddc7c94d286473781a43deca05ec5c7b0e7453aec7d114d2e01746870a3616c56d3968b676b5ebb01aac684e30c7815d726aa0e5cbbafc3de7e40ee6d27c6493e0bf5b7936ab606cb6869fc307b74aa950eb5664cfbad2f66f7fcbc79844b85f360ef66a10bd21526cdef17e2d172cf4d1d1afb3e984b95b63db7e9b44f52f8822a15ee3a693d4e5ef304855f04e1731bf11bd1a30b7c106e11af65524e8b246c9a0e7cc680b97b8fd2618ea63e218bc597d114c041d244c1d753ed8f35204f5668a35f7b48da16ceb5423ec72138c6d421a1cd2837d62a08a0950017198111e7b5d29d1d4cca4f325b719453c9e0d545c5d7eb1f87a0ce303b883922722bb75125f714570b0377bd11c2db27df21182cd04151fbf7597b9f373c17b2b745fe3b0013d1c73c2ebc139ff6d8f1c09a8798e1c57687395454fffa46cbc3509e5d874b7b2cae3dbe5f7426510c60c1cb09ecf1fcb438baa535d0c29521d80d2265ece2bbc61b1bba0d46a95ce6a16bd0ecc17626d7416f531ecbae816311257b18c83877da3a774d29fca11eaad8733b9925145b0fcf44f489d2c4d7e18347972884fe1ad6bdca69205490dc9474d77fb287a4feafd1c5562e3679c2a8f8df537557188a13b55ef2739558aaf66a4921206a355bb5a7a484c1cccce531426db314bfd0f6f76df1328d46af63af546cca640554d0cd888b2c686e15333df5b9b34ff69f0bd8377ab64deb26e39ba62b4ccba07b29c295adb4ff1c09f534886f994541ea0564b48aa4abdf142ea5cf2301f231cc55bcab621bb3454bd9bb54f7fadb5049ba678de3a17f5dd7bf575c6cd2f1b83bba569f289d678a20fbd3963b7bf9ec7f8840cffcedc136c9272075027d0aeffcdce4ad52707680e31d594aedd1dd8f215075879289ba2a5c5b11a07062a65531ac05f9598f3248bc81b9214380cb2c1a9fde6f8e1454dd73e06c2fb4fdc6f6b9c3db26192bb794d3bc9446f26ae01702ce8fd518b2bce03c0fa2ace70a659cd12f8c76e7eeb8bf13c14cb39cad95e0053b06dd06e6aaf44185a307294f5f29e6de5dd3dfcd6f8020a4ec637f1f5661102bb9d58925e01e3b819b6fcff59ec242c35b28d777f21643a2be1d0f6a7e9b6a3253fd2d12d86c4b1c8c672d981629bf3f994aaaa9e8162014ad1fa486efcc599c199a4c32385f37cd008abaf06e08548141f84fd34d9fc746b36da67e5fe7cfb6748eccd60413fe72cffcd313252cafbe9e152f5e06fe15edff3150a09ebfd41f7d6ed53091a5b8daed35a477b4b9fdaf9f8e33810849e9840a7356a434c8714b3e5e9eaaa5c862e16b8a3020a01dcb005531e6545042e55347fa7c6ba89e650c4540057c76b44b52ceaa5e9b8f9c01110ac5143a967936e43bc31dbea1992147d47bc14ead730422778c327667408d88dfe716270b297a65fa0d20289d851657bf20f0e950293e26e63592d65040ed5d25c4a6419c1c1e047c192e470d0f5374dc7a466c53ce2108e35cc2d4a76dfa0f58934be86e6ac1cadac65428d7e00820bec1580f747955cd4128a461f63c101636a5fb90ffcd2e84b7fa8b6db2ed847ab07f38b69c79042db78a9fb748a1f468c421f3767dc6dc2e24761eb54c465d6f61b03153584adda01a4410b4271d195ba95ca308490747a8af3f0b65a140ee4c680b55313c4da4614142c3972774e3e9a2ee7a7454346283c99a9bdc462830f089d24a06fb0ec2bb682673b790503ad55c1f8567af4644a90ab8c7c01c2331886dc8e855f447846dc2a42dc0af90b4ce32e34dc58b46548738143196c9ea1989bd41eb871a6a3f6f0acf5c20e5429d4a21d78148e4ed797b4087b5ae5aa95cf984a441849db6a202c5c44b4d867d4a34ae786a04cceb87eedf57fecf008ae9b7e2a6cf0711a21dcb7dceca51f825b41d357d53e77a9e82cf00b1f8f09adc5a7a1fcda6ab059a36e5016f2f2c0838f7d5feaeafa0070e1370edc97dacf6d37388e5db6352d6a674c6f986c2a2776c921ef7f0cf0e361c467c99343491d2987289ae3aac6592d357ba3960863b2b8e4214dc83d4a90708a5b522e4bf62c32ea51f58c0779151059281a736c223c82bf14034c96b06b186cbb7c0774d74aab4e701fe5ed3738a7566013ad07f2ee2b71a80c7ce611629120b52534137817c816f8c862e11dcbf2a636732b972db591eed4d48e65f9cb80627cdbce9a74996db5c0d1fca86cb514f04392a2d06f239a131fcb7289e894e0f63e9f2a35935f66356711a4af9a62b7cd6dae2341e2906454c944ea6c94eadd16c1b38e8c0b81278661163896908792b4b8e4732a7c375c67c92769c2704f9c3106f4a31b16f3a49045057bf4f9c6d7cbc313c0449f13df05d0ffc33a62d7544df4b085859570cdc488803bd6cbe4ab705025fb15a3cfabd1d3317c18ecd7bb30819b4949bd498a688ce29fa0c554ece8772f1f0fcc62fa4e50df6b698f141b64ddfe42f3c700a50a7689ded93111f299456ddf00c0e09f8df1a038c9172821b9c8acc3bad85e609e1e1049205592fc4f349e3440925a005be4ae1383d870ab09dc360973908d895f1c17ea394088a2c8631ac73493a93d891792f6bf8dec7396ff8a2acfac5090249d1641208212f2790a4d7637d9f410d0246c2cfa798181a32d145883ba888737e70dd12000c2eec936ddc4d2162742e2f3fe0ed32bab9a4a2201c631f39c116845d366344496db8f3486ddfb61d5264e9cb15142460b52db613ba538d0c642eef8f6677438dcf6d20f5d72eb7d203ad406160fa5c1eb899a9a02c135f7c58caf8a13516dab27e3217c15a334acea9672e986fd04c1170283e03f9c4b0ecd4e574cafc02b1514b2cd979988b71e9e7d19e11f226ce9ca5d0f982feb57d980948f37feb05be71f800d87a6fc69d950a18de7caef42455ad4bd0d8de0117bf5b1d0a8db06b942324a845236b478577b47d9ddd5a6c11e7e43f1ab825d9287d36e0de1a99d8f1f5a5346067a942e9b961fcee3dded8277ac71a80dfa58216f80cfd5d04a50cf878c6cbafc1af8f954a1d304c6d5d06ecda07de0bb7c8b58d8d1a65f162a043eaa0a61ff595a225420fa04dc328375151ab45d54eae6f7ababb56723e4e272c2eae3332c4cfbe86e11ee02adce34aa903a418aa2e23c78366f283ee6c85579efb9e170c6fcbcdb81fe8fb86cfaa6900cd2145fbb77523e8f32d47a23809c701ac1eb32354c98ed48e53b2c80109cda3a35a9f0a6d249d84ff9e8a6d2aac6a77c57f2538e529da9971e895da8b6fb268065ff3b3e0fc92cafc2b886f7d9ed0f05179910fcb603aa0274df2cce2b492c1f4a1c0aa41d44ffb74c100197a9af77073f0d59cbd3d4f0805f65c5749302273a7940036d50b023debcc316f8d2d800df7402763610fc1381b618c4c8de65d8583d4ea9ea590e9d0153d44b4404a3f0ae96c14389d0d48cc428bc921a0223763e858537baabfade6a1b38c5e351043aef2cb7f9f751423b6e224d77ffd78bdd20c2b458a4f5c849fe59e4515cb530ac96b461f327ca247d5deee7987f9077d9bf9bcc0ecdafb15e7c073506e13c721e69c4b3207f4545e67e608a867acc31bc2b9c938a69bde9c9c158a343c5e30222f8def07a8d33a4e0be742c5486d315f702f2347ec200af763949c6089f87b4baaa9ea83332e44d7dc6b599625d3a652fa62d4d180e64652948be4205f4a281ff487936a8246a11699b2ae68e6ed1d253f85933240f8a7053a5d4051bbe2138d68d0eabb7338a2e0abfde78c64d75e67470825bb27551c655d87d722c342b332de28f00a37094c78b17bb1a8f0fdc7f78d9b5e7d582419a242245a974e26153cff53fde3424ff6e1056343abcde689f3b53b34536a525a2da5a76477fd9b919cfe330e561c6d92d66e66dac5231c959921d460f96dab3ffdf0a5145af066124976f5628a91fb049c7bfdadb85eb5b6422f5f146470127f1991c56b10391ff69f29edc5a861110bae08f635205ce835dbe1ce711ee01332dafe9e63f9f493b93cc910412b7b611365a00000788ce2ec126ec4d9bb7232fc00252ef25f0b9a3686fb22171c00c38f2aa7c907abc8a9a77794fa0d756499923a25ab38f90148401369f4935a8b17d96578b310db4294674d12f02facaeb053a3dcbf4eed7d0478bff6ad9dff0fe545cbbfe8e88d622a1f9ddab81f76e39bcc449d58f3aa6951e49a98b289319eb19131de88dac9b3cd1ed9ce744129e071a32b2642cce090226a3d9650fd4b815511687f7511b3156d9ca20d118f534248aa64eb7c645032685f1339deef5d7fe7463247b576caf4e4d8f6021849545b643308ba10262cc78e5b393fada3ab3fd0e5a4986f79b362c28b9e8374150ef87ff05c3a6932da5660f06670bd79aa219880a181b02b29628a59d1037d5d206036f3573efe682407ff66dac20f7e1b4c9f2981b1ded0564ca58f78bdcd8b1ea42c189b281e98dcb35517829bd034493c2d06ffa59c38ea5d7364f04bc398d31ae6ff21c3da31d47ab643168b814209e6e9f94c9c14cc5136d1c621777e4c21acf51c78c205e80d34f53d22f63790c8ce9b952cf9ae617a01502bd31a275fcfcd35dc65692b0fcc67b450b6190e5f7bf05d7551c5a2ecef26e32e73a401a2f8673cb33084d379f450f554ec58ed5415fd699ab73425095234684d46e0646280089194a896e5c3a70923c231cca511f3b58316c98f490674af4029eb82f07f57cbc85e889a363b8794e3941c172a4e1e37b494ece6c6854364fc89ab96cf38d6481ef0cea14dcf115800e48dbb755ce3af2a128f87c1b7cb9435191ee6b8809604b88b026747c362f1552e6b0f070f3184e2e7ae34326f07ca72826b2ac22a719da5058c88692b8522069c5c51e3a4e9fe54c0b0c3c18edd6b7f39b204936dd9358bf36e5d74bffd27bdcb487732695cedcc340a2edb57c17c9af639b217e9857afc2e0abf7d49efd1665c4c5f0ca7b24d730d521e81f490edd71d8731d100b4bb1b8b7b3ba25703cfd1e31fb7992fc9e792cebe7292b9731273812ab35746f65b9080fe46426b744d648700568a5df7cfb056ef323e042f3cbf60b9718cf7f1895ecac3bcf902eeadce5674c1ae72c991a1874af63d7a781a2b11c9f452b1eb2868c8bae02a54a66fc439ab74888adf1e1c6fbdbed2498b84530cd020be646c0f5c61807f1305ce3d3c143340148cb992062720f14a6882e9cb9823ca44b673818069dbf2d318cab1860c71c853a374b723fce54413cb4fcb6ed07a292550c5eba930631453bbe87c499cbd2ef9419e75a92b1b71b31eb1ba742ca7a03934e187feb064b40657b839aa5a24a38e34d82c9012a9578a5c91d4fcb3821d2582ad63e00b2cd7fa5c1ae00971984d7e5db2491df72532fd7686950010fa219ee429b9451ad89d14524751873585697753d552d3673476305584ca4e3f9124b42db1fdc561926d24b21f8b42f06be15d338e73bcde73743ee7dae52e5e8342bd710dfcfc4112937c4abb9868330836b20a8118eb10542a7c67e8f1cbe7cdf9ead8619d110bb12a1482bc82afc28bd5622c3cc7b2b9a8a0410d7b0b7d6cd9870b9e0e834e69d4d68871c1d0bd203cef68d60402f9d74db945889da956a8e774cab9fc077bb93a2f3a35f01d4694c862145f401bb6fe3d67da2531cb1b92ae7009a69873c13d8bb48b4394539ab35976f0e75c00adc2e926174640c8de713760de911442f55ec88019152e55fefd49ef26a3acb099906c945a7a6dadd85e95be029a886c8c20138f663bb1eccb458022e9866e87bc1e20b0cc4a420e8fda6f45049a77ca5be0794c635d89c191da40ca1bbab6cfb1e2e81a8dbb235def862f5bdb86d2da249f9f687668fdf7066a6c59b4f2279cbebb58579e199324f92cb5bef3eb69b33008b6d00d776431a924551451e34bbb3d5a61c64937d7b9342d2cf612f2ac39b207e3728a655a05eec22880d9f7c135ed214cc7813d763435764e0d53549f9a831122088b64fc240c94988e61911dd6c84866fd94bde36e0ab6b4811ef5b432a7e28e9ab018ae9b51564ba0451403ce1a5bccccd9d27c84ae3f539f2577a9b5d376a2ca1a5528d9a76fba0edfc2b52462161c825788ffe4a5cb0e9993acfeae5abb43319be9ac2cc04ae0d2b409512503b1011e8c59eefb775937ba3176e09dc48441160e49a0f125a303cccbf237f0b3a6715a72cad3e207d9092ea15256146fc8bf05203b1c2ec27a39731a549069c4570eee188cebc60e9ac62caa52ab1040698d6e96ae1595c3ae6c8262d7540ad7cbe180a3e71cbf655a0c6dc924bf6ecf70dea7bb9c1f03a6de2ba53a34f0cd1bfbbfba19fa5985cd09db1d5eddddfcc8900919f419086944e99485cbd97223c4553c1159c560509002e273b7506c6dccc8369a313e5cdbf97917d317acb913915db2c5707406d44c9e8f0dee39e07bc544a9524d2e853bc8daf51272b59de194079a92d9bac510626c5d6090e42478c32c11f84c26cfaa74ad2db88649ff76c4f7dcb3debc544442694a59c54247c3c231aa1254a5a213ea50b32349de4c79810e84e86cecdccccb215f2962363a4f056cdb5cc42cbbded5fb24468c6b5fddedb89eb7fb489855976283420a16b5d48a6d5999d56e593970b8c44a7b1e10348967116297a1d1f13e5a42f13056d4a1c769cf882262ae872f8beab98f9f3d90ba5e8ab46fa4af5aa32f4838bf96041df86e873efe4c3a1976ca2574cea67a38c7f7224c079018ff2c68494587abf71a64909fc52824fe630874b699a9db4590431cc70a3e6537a3a9853059e46cc48712998b65f56a01b28b58c944668403952bf1d0183792820f96e08745a95d0455544514e74e17742ae0afabe767d690d3f878f55f436857dbd24f62d85b14ffaec047d779e427de71e1f5109a17fa9c26a4d17bcbc6ad08f786e9c46673293032feccd282ed1c4df4004e57983d495e8c870426185c368483810b4483901852089f3f49c6efba0de5716c99c6b52bbac87c449f100c0a6ad99fcca34b5c517c67f8be356cc92698b53b0f21729c4c0f56e9eef7c762315b83a940b6ffa0404729b4d5eea3c1632b4e44b945b6682ee733d60a676879770384905d409efad35958ad5ff4471d1b0a8707d57975b3a405ad6d3ffcfc96f6264b018d9857413de0594ea87f4ebd66daf22365184fe0912fd81f7073638c143a9fcb4797c8eb55cdd471ddda561725135bbde313415d93f692501427b178755eba6dda8580c9838f51b61c95a92d08f28275f4b5777890af53ad99330c43094f7348695391be43c32bf5a931231a5f59ccee8aa9c3a71758bda41996f119e968d2188b077ede61a8426ca0ba182ddee62d1c1e46317550598e626e2d3b6dea31bb9a4ed744101ec2b876ed12190b126762d8097668e9d0fda490d00a0120b417966d1a4f33bfc33a87ebf7b47ac1509614aa8221101ff50b41db0c5d27b9f5360fee143f97b0317f9912083512e919b5f186581a2a49c2ab06cb40b45d9f3f5c15a18b8545be7b6722aa2b3d06a2fd0d3264389f86e178dbe99af929b8a2bd7e9bb130220e5dc557439b42435b0cf3c7adb635581a154db92050ceb981239d5669da3cb211de265a5ae41fac769f7fb337328e17b6bff0b2fb7b69131c97fce76ab745b3da7581464800c8b696b036865fb90325c77acca8f1424bbf58f3c444ba9e5095d222be25e634b5ad81025fd061d9d5280308f2b112cf68fd160fa8dc1fd25d619967ec8c608902703ceb52c72266f4072c4715389409e63ce9652dcffd0acb625c290edf817e2debd0c9280d9b28297501656b2a149a5485c17320a85c077b9e1102c0730bb4587b0599b252ea9851ce5e22dcdb83e33241062110c8db5e5aa902b4a883f56ef316d67fee4d43e17562ba806843b18a4db8d00f1b6576b3e42988e5c6946b8e237b433a7a1676b9882dec4682459bba5e651283bbf5126e0d3b7cab5f3660efbe9e9e04e7f875f54849c4386976e51e481370ffdd1c65489ae1f91caac187b728ae638e29d792b807825d618ae06dc3115c05e479a1c56242186597635f634e20afc3a52965bba2eb1ef713c4a710ce29eedbd1112ba14ff3a5761673a6ec5ac3830126dfe3330a0e55b41171f38fdc7f7b676eb3446b77ad6cd5bd318c184ca40eba1c912a3a5a0ba7dc3053c54773f3d12bbe771f955ff039211fa089621c928e97d4255dffd8d41b067f72cc8131e1e29510cff514c5a804904cff5d38fc97054726ebb3d4db9cbf786a4af3508a1293348cd88dc07e60b8ca4a6b84bf797e749d765271535b0314a9fcb24c675afebfe0b780a7d21c289e780614cc5b27a6ee2cbfc458dd2656a89710f239bebff8640a803c5ed8d1d4cd593cbc42560d307762f29fa8b73e8b9d9399cf225dd89b8b12002c8ca74b91f854893b5895310a64db4a329c42ee7dd5a8511d270f53bd6cf609def4417a458854c4ef4be94015f1d3a6833fae48486d2199cffa30dac850700cdaa8562094c105205bb12682509e4032f86bbfb28517e4189f592f69b1159c8b7626236e18204de631b5f972003a26935ea96d5c1846ae26ac7be0ffc1e1ae5da913fc5ed21c087dd1ec4627ea060a014f37e945dd14758007e7c80574c2709cfa6a680b05eeb483737fb9bd22e205da26d981b0315204eab4ac6d778065ab6b6b51c9db9ae18edfce56ac9a49e75f87dd68b6aa5662142df3fb40b5a0f694ec09063fe33f0bad444f839af9b8bf40c64d77fc8eed11f3fdb1dc4d5a62bdd4a739da9da60267485baee1f7955721d3f22e04c75c6da9fd5057150cb3c302c382d0d5c3225c723fd06f1c6a0520fe65af175cc105b7d3a04a926c001e4c514d7a645c08e17ba81271cd11293501470870cdac8b99c61248fb4bab51a3aec6bf146a8ad9446b4bf8133afb55ec4c94636cf003abbbe068c0b48d7f978be878574d980db794d81b63c4b9b8f51aca96aa083c681fc3310e88556d0c9cd92707df9919a952ad664703f519b355065c289b567efa774443674da995ec9227525ce341f8eb2ab450c0d573298372ce82ffdade06650db26bb6c19850f2c4a3d92eef09a24b2a2a35225082890613582b2efb6508e69018ffec0451468d88e0ec62833369477f6046bb7ff7d961f746264a7896559412165122fea73ba2a004adba7da7a15ee622c0062fee00a2cd3964146ecd9a936a5f4c1a7d0d417958ff3c1e718e8d89fbd585a725121315dfa451fc6c47ad322851684c38282d1e3411c01acc95aa6373c8e9625480049952cecaea5614d1c9fb5ee125d1801f39f1c56b0336b0ce33d124811eff14b4b8446b58729bcab57b4c04bc5d87b7b5d4ddd150886f498f88348ec235c91c6f0a54860cee2fbdcb7c9563bae00ebe4c817560423ba57ed5a9885ef349a4870f98cbf609788f55d342c6db085422d9d6e1ad0a3e40834e1d9d1b108d20108eadfa93367d0c656510fb6c45b063d43622de85acefac3871f08cb1b78d230c307689bc0f8de5141082312ae82492f59d1dd346085b3cead51c62aadf4f8fe6173be0e997f4e7558b5ae6ed17bff30eed57362c4452916bda7075251ca29f682abd8d6ea471063776ca24817abb741f4a5f08b3e1aaaa851a1dcbd52cae896c3d772fd7d92815ce28b54d9c424e0a4ac731db9fed2f9cb46301cdc84bdb624827b8ac59142428f3807e9c1d3460c2a58bc6114f1cd066c451068386e2c1eaac181877e28dbc167a761a1c622f1e41a7d99df94651c518e19c53eb47960dbf292eafcba9914701b3f93e2b0a2493ff81a2091c75b2bf04f7273c94b701287495f71f1c4f00265345309fb9dc4b05023da5ca5828760c44b6e30e850f0ccbf0db252d68a950258acb18c01605ed79cb43ab05214a484c4d39de770c7fbf3f1fff186ffbf9bfe7f2ef39c32e539a3833c1258aa9186686fd00050a829b2c18cc2518e2db6b6ea8808054f00aa83d6ab882d614c8e38f1c4087c6e89881b7935ff49e45aa63bae9759eb2c2e64dccfb6f272627e025f21d10a2e014d4dcafe8657bcca45b0f9cb1145968c8b574f0a38cf1b8f5310c0a134ea499281d04ac849a8b02fa727e710439c84518b7b3294748c248717169cb5d30f81a02857e6a6c959f1deb18a72d8d5500d766cca7c2e19a149ead1a15a82e0066490df6209aad8418f09212d614e855c8565c545b61d10d3b5f47f8cc000432a7bc07208460619c7060407540c4fdcbf7f7d39770b1e07da21e64730e4a30cf91d98974f059990250dabf90bd86f39fc8813ebc9ca774c089a74ff8513aed3f790a347c0c6da6d5c5048d30aef05dcf9fa47ccc8ee88564c7ae8d3f9e96b6f26a4377f9794f2fdc922de9b9ea1af8f551a89351550dfee11cef17de002a1a574d76b49bba1f4940ae5063a552bf47935f9ca8086347045a783a63c6e6e8d2261faf6c22545e269de44ca522456692c4f9875c3de7cc607764871e141238ce5ed82c4b0c5419893e4ab99488c59a8a80ee644a994cd767d3c6499e0b7b58acc244cf59b5b1d7ee4e0730b35609e725a8902619731c0d14b03a75a899dedbd0a99eed306a40237b7398444abf976a568f3df8acb2bec31fe2ea52d90f5b2f23c45329f80e35c21a0340ce340b8c2a9faa798161753a826c6285083399d5aef2a097e1c20e7c80d6fd0f987eb734040f99995e00bcc94e234fdbceaf2e179eb37c144c51006374535a5de9ff15b6daf162b345242b972029d38de8dc5c4ea3f4ca50671db2cbad34337e9b4debe9836d1f37b3a617c336f17151270ec9f1a5d754c009f0f7a59041eeaa17352d8bc123316ac448c34157f0634a5b21a3a5ee8a4da4541cca2b0860fb14f630d792b26fe64c0375a5a7844b0ebc6685fe9e6c8026a9e67e990cbe980cddc73c0312edf4997b610c02f872c02fcb92e01dac7accc78a5242e09144db70fc8811d83eb9aa416d6ff63b323969c6c136c64a63576aeb557169d76478c72536393fabd7436811bcf8011b25bfc74924c2acd324625a4248319b8fafc10deea41e0d08c1c5ea3afd13c23816587c04995d3f12827637712f6db675ef613fc90c2fc9e48fa94841b3ddc00e83159c104b1fa7b587609fd92ada2ea8711bcd71037ac81caefd7bce0ea8300a0602023117c669e16c1af67196cb0d66037bdfeef1d67f077f4a0235d3d79f89f64e530fbc9d90bb49571df5fce58535819405bc91ad8147310c053fb92f1dbcec6e409afb14c2d0140f5e6ef0094f9ad5f9ae4a96fd6d4d103033706715c5d7791992ee78020a6f99d6bc0e483e8ea7b8b6822ee59e9b995c838de362b43fd3070e560266ec9e588eee1c58627647b1fd1c3031ab8700adb70cfcddf693c84ab22b35acb0071f80cd60455dd500b8d5226ec822ba3fe2a4ae08193eb250bfd91ca9b88a3832a8b73c653547ae5c09d0989ab23f57c00562f83ebd1055d7aec008733ad2dddc342d5ec4c345f6b45b9277852777b98e7a8088854f6d4c6e78012d9b77042b2884dd25c6cdb2e8ba36f3352ce9cdec6dbd307c18c7b3cad973f086a883e6b2d498b4d08cff18615d70cea070f42d56a33a15db2029348dcd558f1c790262bdd65e1536c07e07731a99c4ccb9af0655c2421964b7ba7963d6ef4305d4a2240161128d9ae9a1bee44284534f95bedfd16c6daeec4e14156b25a338a345a4026458379c46e8f1443ffb61ea579d3c3bc33e03a69218ba91eca20bcc0058f79f92569697ab89121ece12bd5cbf4cebd25dd019765773a73c5fdcca0a83c402c2a20833843efc9371b2ca11be868e1120a86f848816fa5ccaf1291a4afa5de8fe98ec79a07e090382eac1fce011c518def1bd2b84b719966105db38c36e5159174b0ad2b183f133df8de4174c764915ca409ede38f73a3329fc301cb60dc3dead68224bfa3136ee13cb8a700c9c9bb558f7f1fa92a19ec019a2d9d363f4d684954a4a1b862953b4220304cd0b81db9e5d4458ee6cbe914b9730b54164a0caa0c20d55c9f5b67a999b27b711ed60610fadd74bd0e2bd20fd32d5e30a0708cf3861e96eb3e2e415aec7eaaa7fe973066cd59f32da6769b2ad9e7905f9236d74bc8686aa37906a19385ec7087ffbac65ff70976ba2fc28b425c3b22c864b8a97b1c715fc92fb22cb6b24dde4654e750d373e05f71b4ad4071bd09f9f4eea704ba5c4cb8a8e907838b004bf44145248383041678794d129c70243cce5310f9df0e3dc8672b6f46e7b9c0669d9a6ef1f352d79c965a681fc18c55c44689d5e70879e065c9e19888b57404c7621847a016a0b1fe112b5386b1cd5ab807d1d129c5bb2d21b5e83ecff03f648dd00ed2063b47b53a29380c096d983c0c46e4ca8c2ef4799fdf3b2f451255712c35f255eaee43dbd2668e3ec860e05983490f881d33b001506656614946b18f5a2e21800e309b5ae3654bc521951209c64a7be81d8c38b97dfd9a96f7ab3b77a429a5ed873b0bf768ed9f9df8385ec0ccc8b3315e3057cc5659350fb3f991640c6f3b932299bcf553005c69d40fbd89080176414beca79521231e51c61221d4c9d0de5797e1fccdc34403cd308d4451716d8bb500ac626bd4e51723212cc53ca34832935d2a84c38fc808803127f29b298c10621160c25324b0f71dc01b4f40da0c390ef80ae8b86be10b9d3d9185b5d35cc39b0d6ffcf75d540490de466c25841a149a27c3eb351e3615cb87f6d145c62fbc9d4963282bedc73c6d99179b7c19299c558ac12f86749fffb38725500a35990492a138ee781763ff0294775af3ca3d6db4486110e6c03e85ee266475861b5505a215506a11176fa955d5bcbaac060e6451cfc0d97c6850ec3869ebf473b116410726670f95227ce8d199f421a4f8fca0e675dffbc3d570a804de4781042c49205877004134e81a8378b25108213128300fdb661d5918e2002566777708ca30780bd07412a1d67c14bb3d95687d183586e4ed9df979d0035d9530d21172b87933f4d95120319fb0feb19060d48009c79b2ff9ad6a091ce4a8672099ac445b0d704cd94ec9aa1c87b90b14b9233615641f450d25831647a9bcc63adb3bd9c2ea21b895ba59b4ce466b25097b4aff3616bd741aef7179b463610db8ce36fb13aa44e1a784ee845ca957c1aa22ff1432d9d26ea80e324a15416e1fe340fe46513938e70e4aa588db3366dfa99fba3fbe0160b1b1ab4d629dd284852434cc1ffd012f6686f359f996fe62c80b4ce903d359f6ec2e2350e3d068591fe3f1247c293497321a5158b98f24588c6ae35c9f59491c0f0e1f671d6ff5482f1d1fca1e1cfe8821827f5c06a67f64f1ae88c210b1bee1230bf6c8338b32431d3b5fe2a2677db0002281ebbdde8817323fa0a5e121ce6c0776f15b0e9fa52106b0a117aa8a3bb97ef5110ed63b612ba2b341700aaca8ccda5cf5825e12f4e33a2fd7405bf858eb36f9259e6eafcb5920a0e296fb5a453be8ad4ac32bd12c86a0478bf62926802b86bc6de03d3c2291abdebec840c6be41b211ab4cc33e520489a6056e1f74103d17dcaa077a321b5670e2f03f3e9423803c61b2cd4614c1f17da4100c3cbf047b573eda641f395e09ff3d3d76d880d628e95dde4d8f6a83059beed8205435ac8200700ab3e9389b841567bae15ad88c41ee5e3c815a3d49498d57a44a5d0d7ef8eceeb94ef4db5264ec5a6a86978331739a935b20b036cf35768f03ed32e216e90191eb409c92340eeaff77b4d2518dbe447621d5f4efdff99171be145a5c22968583868f6b69f96461eeef29dae47c62be7f6f4e7093eeffb8e4306831bbc35b4d05628cca458d041fad7d7499cb4de37632e10556d55f397342e07a7b1f3dfd06c323eca9d134be5cce55ddeb2530c2456eff3885de0d4161ca9422f0545a6247dbc55b01e59c44195fd1ecf6fce7ef120365dacd70a5b2b876594b9d14e028294d947c729614e469d8fc20c9ca74afe5332487e556a268898dfdd30260d2bcdbbcd9c8c3a92e7ddc58c26c0ff6fa676417035cd9b6a5d2d031a67bbe14ab599e44909b83b40a464b728fb7f93d2a1bf6bd26c887317cc77b27ff4ba5ed563c4fcf152a649a3b3261da9cfb464fd1507fff897d2acb5f097be5d10a66decd554e9436fcb43065cac339fe35a378cdc2e1a99b07642703da721280562058524d07c9096756d83c8a4239e266992a28648caf17263f8179061c09819eb659003201a9eab328f0a358e3664f94e6feffffd05836cb54fb8a3ad390bfb1892ed0e2564c7b4c70088a5ddfeeddd6f6de52a62453080831089607402e8edf1fd65cccc0f787422e16f1fde1908b18f8feb0c8c50b7cffccca450b7cff4c8f8b15f8fe19968b14f8fe191f1727f0fd332d1725f0fd332e1789f8fe99978b11f8fe19988b1070f101df3f33737188ef9fa1b9e880ef9f01725188ef9fa9cd08cd0ccd149d56a79ed3c9e7d43ab94e2f177bf8fe13ec143bcd4e27a053ed24741a3a15d1ac687a68687c685a6178e47b273a55f8fd342f1a184d8c664643a301a2a9d108d10cd114b9287e7fcdaaa6a78655e353d3aa71d5bc6a6035b19a590dad06c8451b762afbfd3535175df8fe1a211701f0fd35432eda7c7f4d918ba8efffb172f1c7f7ffe871b1e6fb7fb05ca4f9fe1f3e2e9ebeff47cbc599efffe172d1c7b7f778779977cfe1c5c2f8f7f822aa169f97d90f2fdf2364c2736328a67a794fbef7592d90f80dd8d015be4258180b67212d040a6ba150381416cdac667a6658333e33ad19d7cc6b0636139b99cdd08498a9cd08cd0ccd14ad7a583e2dd70b169bd1806a424345342b9a1e1a168d0f4dcb6d685e34309a18cd8c8646034453a311a219a229aa59d5f4d4b06a7c6a5a35ae9a570dac265633aba1d5d46a846a866a8a7eac7ef4fc60fdf0f9d1b2dd33c730fa1a24f00cfd3eec1121be0911ac543e3eb43d9d0a8990c6550344699446815c7ce1fbe61bc87790c7f986e103f0fd1bcbc518be65f8feade5e20cdfbfb95c4c7d8b42be69f8cef9aee1db86efdf6a2e0ae0fb37211707f04d80efdf8a5c1cf2fddccac5fbfd5c8f8baaef027cdff0fd5ccb459d6f1cbe73f89d988b3a7c3f3773d1002eeee0220f35170fe02202be7b7091888b09f8f6e1fb3b968b0af8fecec7c51fbebf6bb9b880efef5c2e02f1fdddcb45067c7f07733188efef622e36e0fbbb998b34343e342c9a1e9ad5a9e83474123ad54e4027da69768a9d60a7d7c9756a9d7c4eac53cf6935533433342334539b01729b19dacc6c263633f39a71cdb4667c6658333d33ab301c0a85c25a0814d2c259180bc357c8a30c21acc98761d8806d79db72846dd1806de1b12d46d896d1b664c0b614615b30605b2e605b2c605b2a605b28605d26605d24605d88b02e11b02e10b02eae075897d710d605e600eb1213c2bacc1a605d68415817200658971a10d6456801d665e807eb52a400bb63e583ddd19300bb8345c4eef0e9c1ee6821c0ee701dc0ee78f16077c076b03b6206b03b663ad81d3b76470e76070e76878edd7183dd51006b5259d3b5a6cf9a7caca945006b720dc09a5e02b026980dd614abc19a6639d644833509b126d19a52d6348335c9605f62b02f01b02f30d8171cfb12c4be00b12f37f6e505fbe2827d01807db1b12f35b7e947d9971ff6a5c6be14b94d3f8de571b23c662c0f1e21e99963f0de5405cfd06fa7181cc338c1b82f3ee019faf965c637fd3d2c95b134369aae184d588c138c3b86ae2946e8fac1d9a08e322e99260d93692203ab42f53bcab8a895b2c5f5728af917fb626b08c217b111c7c6c6c6684aedf1e3e53674b25179b61257a3fc5fe6c7ce7bbc4cd7d52aaf9296969f4609c1e35f42981f5ebec7f778f9222a6fc651fde2505a8d870faae3dcb63f5efe6259d5e00f24154c0d10df7035a8638b4fe8e3a2ccfbf8b1ebd1e2d3e273fb2bcb459930f4d1753dc2d0f512b26eff6ca3f21d431749b2abf8c7d8ce590563d96d66e85a9154210c09750c5dfe625965b2ace28c0003048ab8f4025c1ebbc8958b428505536c4c361a9080524a2901a0b821d4441b48e830a880a950e1248aed091544d890e0846ca5d14ac2cd7159d69a715d4c93cb8e63ef9e7bd975bfb1cbae3399b8706ee1f4e98151e5773e83fe4ca48d1b6d9c74d2a8e3b62fa4d7a47694d9398e63661e72d1dbb66fd9f10de98734736fe29cee2bc1d17b9649a3705ce2724b803a74970ba7128dea264036261274e83250ec324cbee4aaa60450092fabe0f169e41abb4837aeebf89f77dca687044144b62c32cd4646803c232c43d8ce3052baee58c06ebd2cc3c2558239e79c2c7cf320163a2c74328ce9c9f7da7a1cc49f411d5cfaebe83057ab11a35c823a7ce7ebd096aed2df5347de194e26b1ec04c5ee103c3e3569d4f63120c11ab5915e8d22411d49afbb35e9457f9bdbf3889955df7cf3cd49b12be813e419b54733173d2745d1295aeb54d7559f2f4408ca4455a321d451084d0aa174e8c382575550f99973c38d933fab3a17e7835ab0548144b50273ce90e39ffdcc1d27654d883929b770c6b2eb36d9b68da00e11a1390e0e0e8eb731e79c4fc453322653691222c2a269c62c84bbe6c66111a776dc0513a1d1da87bcbbfbb773ad2c5733f8ce8ea5dbbdd7a514b4e9e734c9a0763ca52b555840352122cd5dd7ddddc5702478858dce0d0b1dce95729374523adb0ad52fcb7e842085da97653f4f380d2030144aa612136a196e0201e0c8000a12444105294d4ca0dac0a86d5070826edbb66d67acbc4848e85318d4b0821320149c7850b001926ef25334bf1f2b6c2a4fb4c182328ef0a095c69f602ecb7e8e70b7cbb29f33eef8381d1c422e4b2356947c904145151c15452598d554b7bab9cf1377fc2db270b9c7f5976161c6757f93976457105df0b26c8b2ad0adbbe56f3fae1eb49f9629081546877b92e30b56823a1c16eaf8d7e9f53fa971fd519da2b92eca262b2452aebba05cdfa88cb94b3ca653d251a87fc3b4e9c063cea9a13975cf84f6657cf48881e1f162dae1c2ecded25dd29183a5aea8809f471a75dc46a7b76466fea1a2230eafb96817e78fdddddd75774b7627b44bcb16efd274968e4a272b470583aafc4999a9cf486ab9b36c9503c1f0b85001dd3d0aeaf07fde07d280446177da11e7f20375bcebd40c3d77d2080ca2ddcae4aa07ddd18dfe142f9430545568b2d8922d96a4a0d6cbb22d7ee846e956a48a234150ecc0c80ae268828adac0d110420773254780367c9ca8261ae8a0967a5c96d1e00597745946039f3bd67f524d2c973b1f624c01aae66f9506c40586cc5582db4f4d473e58182c7ce72c75fd758bdb5a20a96e4ce1daebdf5d1117bc208b214c110512de70a20661cc489e1975acdd18320d1c9881c5097290c6cfaa61347881b28adb3f3b35737b3445d77d4ae050c12c8b2856d0ca420915435988918496051090a8a688657104964f165180428a57166c94408582a22c8a5852c5f2711bee62d023854a8a21b7e1eeeb4ad6ea7759f633c50957f4b471c515fdbaa28b367245196e03329a00440613b6f0fa4b8489886b442869c1955f44a748578a5e8c47ae624a5c77943d2f254fee28930c4d69a28ed275a50965227ac5c182d665245c76628513497bc00318ac5426598374a34e79e5cb30bbf285746a74e513c1d3e268c53218c4aea4bdceb83020ba234be1919f5cf9d3dbe43623a89d13b554ab6361073938799f952a8e8ceafe09f1bd27610417c2e0b03df7498c927b1088efd993e09efe68f4feaad19cbfcd0d0402040286f4de832153064921077446a14e172661049d8ed3b56fb1832de48036b803f723520fa41f718ecaf3c69151d96366e6da4f7afe2e1cbfff3a9544f7db7ba3cfd6466d25591dbbeb3dd7b9c88454756e437ad2875aa82ea4533f412038b652c100317fe557c21054153b57d77b151fe633215530e0affce681210774be50c70b93308214761d17abf299366a9a8295952b6a4fe59773520a521ac2788883ca1a52324bc9524ac9cc2cc3dec16496cccccccccccc2c99437e29a5dc014ab2e4154c6f834928aa3f75771ab694393eddd91d8c52fdc1245407c1e8c24eb2f39d51e0b9fe6c47fed2ed920c6a7fbe02451dbffa7d3521228d239f70594a139a0997bb5bce12ccda0fda8f1a9ad3ccf5b6cf6d68edf5bacfed5f99eb913cef73dbe37adfe736e67ae0e716e67a2a9f5b1ed75ba99f5bd3f55872945a4a2d2e3b4c2f3030313d7cfc7fe1e776a3d73b7d6ee7f5686a6c6a503fd0283d4ef7382a21cb20643a89e3e7be5c76b2855608723ccbb3242047b87215134586747d724b97652f805d09ea106179ae05553edfd029be2a2afde104c3ce298bd5f11e0f8a83ca37c709d5a9fa2ddf2fc4459767d9dec5a2de6d4a61732f70dd98cbb2174091e3658e90891ccf6e23c311fc117bfc8dacca8f9e64c7f955abf20cfacacacae7c0772584dd952f72a51d5fc58e72943fb2de7f2ab6c8052d12572a41af17ca306490d10ff1d137f703cbeb0899c8f12c447284ef365d67fd59feca7716cb2ada4a7351be98ddd18ffca54c064297653200bab4cb321990e006e1e2c73d0e4309c245264420b108fa4248c5647b11481d14c18a958a8911dc6fdf4fc40fa3ffe8e58f4ad0911582c3a4fa8f7ec451826af1fdb45a804380b6e7551fa52b3fe78a65b6b1016de78d5ecad0a911e9fd19943e7adf41268e38e2b8231c221ad541a471b95088dddca8230315411b19caed97ce8333a451fd3f744aa5e776d795ea5877e4275dd2e97600433217f8dcc62162c9ae20925d41f45786e4042d9f4e4fc198e46d31fa05d91a89e1f1698481769b4ba386b5532bdfef499dfa2070ef9e70574223bce7ebf5b00c87154f58c9ebcd1f56d43174fd7071c1a87ef0995332aefa35409cf3b5f8d070ef96f432ae3622c34573d6473474f5f7adae7f0edad1ef168ed4081d8d2018ba3c9e35c25f5ed3f7b03309758c7175ef992ced2e835d486921ec52200aa551ed44fd1edf1ed4237ce1fbd18fdeef94ba72afac52cc7dc8bc8ae7c31ef1b1e33d1f3e7cecb0324b7c5815958c3d22f3929171f9b02b2a9cdb03c7e5dba5e565663ee6b749b9128395db593a823a863d7c8463748a9483dff93c7c38e7e30103bfdc33a778b456fe65c639cee30a261fced9ded552fa91c7153ca685070c9c470bd4f1238f2d74b024cb8f3cb6b8397eb3342ebe89711c19d7adaccfea60f14d97627c430222d52c49c892d46854d348ab95211a6d686885b6a206e7c8db3fab50473faed05cec2833bbfd2d76acb76447d701561607640e96d0357a8b0f3572c7169fb193711997cc129537aa4c93462961ffa51553c218d22b31a863485add91d2c2259df2bb813f42033c468c1928020317b0400528300109101101083c60080708d180201800c4027e50800f0920d203020ec0c30e06d06127071c746e2880ea864308300001d850430e0d42c4d40c32c4100018708200b979c1ca20e102006c7c5aae17ec47ccc50e7cff8f998b1ef8fed0c539fe3df2b8c26d1e57e0203476e64fd6c7ffb0345f634f6f637f3ccad6bc0bd6e60160518f8405c0cb5817deadcaed803d22e3c9581faa4fe5f52763415595b12a2a0fd823d5a767e8f74065ada8bc982531af185716ea18e37a7b4489e7c06fc026f147d80d3c8f3de23560ff47cbf346580d7c1176fc0c5823fe02b688c780cdc057c05ee02d6031f013b015780a580b3c1176022f014b81878025e2236025f0435808fc036c045e083bc43bc03ee083b0427c03ac031e081bc433c036e07bd899ffc102f10bb00c781fec0faf00bb8027627df80458053c022c91efc126e079b008f803d81ede009687dfc11ee077ac01be03d6c7eb6077781ceccee76075f81b2c0eaf63737895bde10b60753eb4aabfb6004f001bfe107b5f0096003f003be46bb002781bec009e065bc3e7581b5eb434bc109bf33358f15356c8c7606778196cea61b0317c00ac0c1fc4c2f03836007f63833c108bf3d6defc0b16c82361edcbd8172616ea18e38a71b9d8e3fb635e31b04e7985f50cfd49d8fa729b7ec5b8625c314b42978b1bf8fef015c25c3ce2fbc3988b1af8fe70e622cff78734178df8fe305d12db21e98ef474ac46f9c05ea655b72a2da9a3f77a3501572f3402a2f0f8348235afb277b22b49b4018c24289c74e60673a02fe5b6f546aaa6da712412c916517992eb48a4f7b6cf1651c9e724d77deddb4ea37e43fdb66d6347db98773a2587d016e7702f651552f64cbecb2331744b69e1a5b4e87995f793c4513a7b4257b4412dbcf7b6cfb345540c6eafeae813ea2b4a39aefbb4207d678ba83cf0235998f9ac2aa2f22c8cf49ef8c02aefa525e23dc9c2f893be884a5a9c9ea1dfed0fde9384f830ad83f273bddb3f5a21697154fb49721b045263e8b61677ec8a6ed79cfcd8e08ea3233e2508d4a91184020675aa9bb83d41289d8a92ea02b0943b8409574a2923cdae2cd5ae64669e49714bac2bc71c4cb83247ed4ac915805355703eaed71f68e40c8fd6812be99b9dcdf7e86c56464837ba6dde795e371a798ecfb3303f78fffd1732e1859ddbc82fa2f2be6f7377777003c139e7749fd3dde7f469bfcfedecda664a3048766556bf19a8439f346ad6d166970625a8679cc31d5047f38ee6eef66b99ded2d2d2d2925b4824b016840973ce39e21b9eb3e31b0e856e14ad2640dda97ba468c30c2fd8f0f83d8e0195edde6d4c28c731e9ba6e34a294524a3f8f524a2908aaa8544a29a5254a698c49878e5249967474b2a5fb799a14bb4674112a4116e8d08315b03194852b8a15391823084734a9c2118af022128202073945e0020a0f9a60218692243c09a316049bd10381278c14cc645f14ad618331808a68d083042f07befd2225900b01d49152d62e354af685901bc199516f727f7f3703e76b9b86f9c261526743a37ae84d51fd3f97350ccf9399fef5c64db7f249f249a3f97da4cffbc0397e3a2b2aa0bbfbec6a8e39e79c639d3a3eb72550c7ed686ae17671912e54a5e6d0e1f29540b9a3861423c5482cf05cca9eddcdcfdddf3d6479cba4db06eafcb80fc7aa946e73ebe6d7baf239ae54aaa65aedbaeedb5c3131a6171a71f75e7f0dea74f2495ef749e9ee2e59524a77777777e9d2634929dddddddda54b29dddddddda5cb5a59587248777777f79fd265c83fc4db8e3cdbebbecc1bde1452cab652498b34c49448228f5c675d7f9e1f2a89e8d40daebc3b51845cecde9f871d3847bebf0e5cc3f6fe3b9cd3ee2fc4c52e44e20659c3166680834c23f826092e827360ae3f129c339f301216c137b22a7fbbcdd31689327c87b9f1a544c21d2ec245efbdbbb00b67655d9ee1a4c031dd46cda210125d65fa2acb11700f0bdf34c7f16c2bdfa871c7189917417734c5f0c7d255cee16b622b3c1e3880e1415d19e22c67244a299d2fda4265e8c3c7ab7559e6c513182ecbba605d0e48da1992a4a41a6969714ebf735fa2c9df6c8338b8dc91646485c637de8fd4c8ca8a6f483c6a95b3251aad45a2ad9c71bdb6ae7fd7d2ba2e8d5c971746871e617668b9f3bb3b8e45ee6a93e138bdb8f2473b449d9852d41354f9b363e6be0dccc21d9999b9fb3999d9755c237cbafb646666eefb974952e5b304afc0ee6117854927654d8a444a23bc3129a593521ace39c5a493ba31e91493524a279d534c15d2a80a8f9959b664fb9cf3a373a2208a2adf03bbae0b87786c8817a173b17b1f4da3c9393af449dec7dd574b3ca5cee9bcc1ee6c96878ffec7a8af1b7d226e68d4a4027490692330d6a8928f784bcb8a73fc9be6ab2273d48d3a20f0ca0ac76d1cb76d5bd7bd3143d9978a60d4f99c6934e1f169e45ab7719ce5e2f632dc66446014bfb75cdc62fe04a736da1a0cc7eddf622e6e406ed39b50a33622706a9b02506c0dd6c16d87b1df8045ae391f1edc666d4260dd0672d1cd38c26d2087b914ea6ae5d7e1af9c5d67bd530af526d43db73558a41b1ca016266fb4712a1eb6ecac16dcd2613d0ee35adc14888888888888888888888888888888888888888888888888888888888888888888888888e65bc1eb46dd88c335c98a0d758be6c18133724db26243ddf2e0c019b92659b1210f0e0f0e0f0e9cd8a4936b92359d6b707477370e1cddddddc1ecf0ddfeee8e1db8fde3eea12369d426c37777ca73d6ba32fd89cba23fc1365dc7f9138cea2c702af49986ee3f7dba74779f2418b5dec337fc9e53ca734e76fa1c52ca048d0a5532abb0ba1dd7b9a909ac68c3dddddde51a2e4bb677777777a75b28a7505dae739cbb4bd1c61ad2c5c5e9aaa5f4586b1d2b0dbbe6c2722b464deb540fe740912dbfd5b8520a91e18e3b435a4bb221c1d98ea0a6bc1ab7db9334aa4767b55c5a0375e8ebbef9745e1a55b274b9bee386b68131a3599ea4c5ed67f3f162f319b58086d029a750ead8ac6675924fae58ae3f77ab09189b4bb5705dfa25195c4affd429bea6196861064377acbfc5136c7c9f154f58d1460ec9f24477446c649228ea281c54bf5e2c7963cadf12ff9874a3d1649e22aaa85fd7c6ab6bb08cdac508eac842b28b2290e8827366a9f2340a091f2128212fc42424844e7665481e5a44a5461b86746af4fc37eccc8df4425adc897941b63c94cca312f3763f1a3dd73b1a3ddb24f85bd21f171e8a8daec1910a6a49b6641975942d2c8cd4918b24163c9258700ec734ca746254f73b2e2e81ba5d105e833bf8e57e091e7de1c845435cda51269678a216fd27472990cccc93528962979452bee1a6e0e54c917bff8ec5ed3dacee9b6df7144b672a2553db4d77774abdf09a3223e195e526a1a85fa3022698a529d7f4a7ed8d3b3e8e0ebe2b793700fc7c6635d552ad4b682648e14eb66061e1fc0d5abc1107157c88a0021e389164065c12304a6085ada8045310630953e288020b1f38093a49eb04a5ebeee4064e5870bdcb322741f848204e55c90955fe28c3e5cee5103a56741105106600451a2b3fd24fc0dc90022382a9319348648f8442229982e71b95e0be9e0f8a0f4918a3087479680f0d2c1a90d0d043c3096860d1c0f24625dabb0e7647298334b9ed61c8204d65f7352d226587138ceef3a2aa14d539594e5b6a147f5ed4d1c4301c5e5761993ae5612c0aefa52d7259f2e479e41ba6df935c975d96419afd79037598d26e4a7f486f019041d8caf7248d92b48827613ae5b3bc736ac193d496e03975008c5047067241ad0a3e313333bb337d670e479e4f864619a18e1dca24637e4aa742c2aeb73269a951fd813ac6dcbe25194608bbd3ce225414d4f1a53cf594cd85c792467d660961a4f724cf76dd245aab7537f539e766e553ba49e948c625da0430403021a5f052794f7a27a48a8910df935eaa421820432a26800ca93aee8dface3ac363961f880113c375592686cfbdb92c0b43e85616ee5a8e46a32f7297b89d1d658c932a0ba376b922b7ad127eb9504a3b4a24e3127732d7ae02291b24a34ac9b5a7c6ccc548efc713ea8e56a42907fa75a9c97576ee807367ccac8e9ef4dc8f401d8e0379287247ef791f773beffb1adc01673e377a1eddd3397f8775e19bf9d2966490f99be5f992c32f87914e174ea7e1178ee444e2832a654f6629e7c6ffb951e9cae4e74aa55aedbaaf73c5c4f09bdce7f2e74695eff5d7dff3af7c6eac803534b951c76feeb827aa0c2b4b8ed0db9b25cb8fa1d26536f4b9e06bb5bed6f67c1a597a9f0beab66ddb36b76fcb43a3dcf37e5aef6bdfc9440d2719c47f86e30736961cffb52a372a071bb55b6ff75eb76c57c9f13247c87cc3729235dc80eaee4ad881271007e7c14db5763c5b6514c14d826ffc49b33a7fe4d9c7b38de704b343bddcd7cbf9f644a9d4f249eb02eab07c199248d0e291ed9d78d51632a1cf743aa5d4bbdb89253fd785cbb2357ed29ecb4ddb1541d2a8d95df7fc908c4a7093fef8f55c6f9f3d7c50c820fef47300e783de811cf8ce3007be1e72d26fc0bd13afe242269bcb22ee489fc7d41bddbe9f2a7f88d7d5386ff395ee9cbb8be04e6959f7199c52064172e54f97cf600f7d024b879eb42855fc2de145588d924970b79f9fe7141e9f2cc783735d722fbf7bb747e05c2e1c712ef76cf983f93eef512ffc0e912086d8624e9020b9e1f324f8de370ad529ee7bf4a0ad6dc3bd643be2426a3ba652f4e067cae51efcb0725cee010ba67051147359c6c508ee929fcb322e5a3a2ecb96f45cd992594cc6647547efc5b22545d78b7932273050e3b2131848b9a274d282315230e5fe65274986aeb77284097d275e05e4f93482abdbff43a37a76f5070e9ade9c3b9e0d406cd7d333f4975c75f5ea1f29a594524aea79d39bdef4e6ec1f6f25b0466d636caeadc9e66a5407cdcca021250cc7b0f6c9bf9256a7646496f8f011d4a3c74f4c0c141818190f1e519abcbc2831999eecd821c5c5658c961624a5d28f0e1d5072e448c2c2b2a4d6262b2b414077274876411df087f4b7a48e1c1127c4a9714ba69f9d1273769513626ee8965151efb91ad54b622419e9456ad24d6ac2a48ede6bc4f9d4b8ede352c324a50675e741b93d7a4f6e8f9eeb76d79eecb6e7ead4e8f92c19492c25a426a4216f8c298dea55a3fe84d37d3ca6cbdd67e2d114493a3285e6688c3982098d6052eaf7601a75bee7f17c4a5c96df6528b7a350697eda71e4536b547b526ebf47f3a000b14d00f920409e8300010204083310205ed06d8fe641a1b16317bbfd2a4bea2c09532e2949a7466e8cdb2e543872413e9d621913daed5fdd71ebf15e36c81a522ccee11e56758a2fabf8927a3887893eda48c99dd2e2541d19fd78e1d83d91dd5192d17b3189dd9bcb3226acebe0f750eee947efd51de914ede91445d229173af5c237b7e7d13cb7486275349009f5e689348abfe625a873136e5ba83fe4c72da16013eacd0be15c3a37e10b2e58120b00e168b209598efacfdad8489b90396442823e340f28dbffc38e1cabb163f7e4f6d3d8b16b72fb4f763596ac4fa3ba05d4a92ec2e53b6377d0000a0a42a3d6a98672f98676078d1a1a52d09002eb5413e1f2b576270a6c8c31669dea215cbe6f77a2cc9e3c6975aacfb87c65ec4e94961225ae4e35edf2f56177a2b8962c01ea549b71f9f6b03b518082827c3ad542b87c63ec4e149f9f1f5aa73a08972f8cdd8942830225d6a97e72f9f2b03b516232599457a71a0897ef8bdd89f28ad2244a9356a7fa0797afc9ee04b594289975aa7d70f9eeb03b41b3274f6a9dead9e5eb6277826a52a4c03ad53db87c5bec4e106c8c317a3ad54e2edf92dd09ea4182c4a7535dc6e5abc3ee04f9fcfcd03ad5b2cb3787dd09a24181c2ea54f3e0f265b13b41ac24495c9d6a322edf6a77825c4b96bc3ad53bb87c57ec4ed0ab4913a04e75ecf255b13b414041413b41b14eb50e2e5fd0ee04c582649c03ee04c9f886ef902ec3044deec8f56c435b11b7bad4e54d661ecf7b69c7ef7adecfc96a5423a95197924e0a958663cf7a5c5fd4958b5e54d46aefe58523cff57e5acf8349c92f7be0b0eba1b65bcd1f3b0f66794ab26b424340d264a2af693dd8fc26b51fd6ad3a357a0fecfbe62ff4602f93c7ee12d6299e3d2e7e4b9a67cfedafe85bd26237a5bf968b9d3c5274254bb286bce78eddea08a9352aa22bc9069c40a884ac37763dc914a2111900080008007315000020100a0704628140208e5435d73e14000b789a4a7048198b634110e43008a218c620040c008018600c02c8304353260022f6bc1cbff12752177aa43623b73d4270595ecae83cd1726bf84757828de109fae3cd6b64b4f95bdac172afeef25a32829c0c5660bdad01bb2a82753351f29433d2196e0acc97285067e04d56d79d358452ca357e921c1bf52701c33d250b7ebbee6ba84760dd9ccefdd799c053be78f8108124fd1246e8d9ebac0463dd9914edf63a4cfb6d0868f5a99c8ffe6fe20b153b23b1c72c772edafc68af040c644fe9ab5682d6d71dda2bea4f1b378e9786910c6957070fa51a896abf0edef4bb4a6b5d5b8d236631177dbdb255d394fbce2d424420d5d50849aa5afbccd911219b6aa5fcc899790834e96f1099ac025558cfe7c12a50b54e1f0dc37ae6437783549f0925231ba7105b8ddf061f07e68f3ca88e1617bbd78af13f8afae3d21f5420727cf96d302d8dbd8af00ca2928bd82816dfa18d1b306e3a1ac421622ef673c8a2e20b5e80181d6ade18633d58c8be2edcd01583b1aea96d4bc317e933cddab1ecf03b0404227488ed2f7711980e43c3505cf212c86e17f95c3ba12c285f4fe4ea56bfe44eb8e00567cba913d7a83f2309de4b948f0e0bfdf30775f7665131d9c4c0d521a4512c7c4010d11532d9185325c7a4ae6aabb3c20f7b62614a833014a9c29e93b6b20ad27e8370e85441e74082f7fb48a2d0f044cebf86497b22121702f4cd10157d87452eb4cad97f7d3405643c0a36ea7250e23582f41dee93a82272a0dabe473cec2378c2a143f9ad40afdeba9fcb91ec29c81923acc3c701b06059e4f107e333e61a42fd45fc33ef9d16bcffe5fdff2ecc0b78d3e31f6d5a4e47e286cbcde953fde50099a1d2ccfa0b2be6c8fd5124fe5c43c8e70596c68dc9fdc9b284654e8a30eb7694863881246342d20c1ef8e4f1ee8c5d202f6d4c8f009e936f6eb1e2c73491225e68d4b091710448103109c6a1f12d0b9fafdd84e51f5040052134f4b1e9ab0836cb58e83a14fe193f963a55169b646f94ec20cf7f004cbe874ee8dfc3cf7894da5c1b69995c8234f98899b907a357266c510341570ab910f07441672fe20a6247bc7f93ec3d5581fde8d14a8d005c419962fbd0c42a6b6f3e7c46746dc4cd55c2553fd5b2d472e596981d58be2120f82320e3087d1cf6fe5cdcb4b13d7adce06784cc29aebfceb933945a304ed5f3fd2a16f727e90763bcfe9b6e002acc370743892c5929a8f7a4b07a26326d09a7cdde92dd66deeb099259d98c6fd2aa3974fe43d1263ddf080c0240442acf8a8c68b77fbed294769f09219dd1291f1be5a2f9c56d6ad2ad5650cae1631fdde452949c79faa5235abde114381d71bc85be059a4420ed1e92957045f284ec1f8dbc61aeb26125b8326756ed2f5619a9551144db7bd5d19ba3103af16e6658ab42153963de07d1ec18ff3300d465fe025c013465f04ff084379f07a16bc04ca1d3881d3590954b0a3b4dea638fb185dbae30c8f0f0d839e71a263f989154c8a162754675a670c650c485d9cb0a3de376b77e2639b07be46cd4976d9530bed94b7818362522a9bc8d4425eba791214a121e83e24c82874bdc1a6c4904806225434ca8dae69046e2d617c94ef4515511cc9af2b8d4fe6fe06364c1eb7c38a04f7a2432354cb99433842d4bfbbb333d8788bd5bfbf3779154529b99171dd3dc0d9b3c72ea00d1925dae2d857b5e826451fb18a83a458c60f3b5205717d0a013d40bb996257dad2ab0ea1ee5b8c8ca35f0452d390ea7fff8af1ee2cdb7a5fbd7a370babe98189b36bc5df8bb5a73a42ec0c58ce1cb0c07cceb3a7d6208315558a569731e076ea261e333d5f3799ab3311337f1539abdec72f47d0a748030c78359a00c8da1536d1ea099a9ab54017faaa8292820c97306ec63bdd6c470c9b632639d039d2c9e02900fc476e02e854046d466b4f36f62ded599afbe74161a64c261467364f93ea8582ce45b9383a82b8670c0bcbd1c4b91d3efb22ef7f46a088a4a4b77101f51a1a01917306bbaafb8060002e50323a041964e97e415cbbbfb7312405ed281ceb15678d23e8f964f0a92be1caf2428dcf9421e526fa7b07ee8d1bbcb4141e7d16cfed5c79975a737dd16306929af90a8043f49d4f3f21858964eb0b1696b36ee96019b11ac7900c956533c2a3aed143c8886e2f44512b640f70bdc27715c827e939d9d10d3c1c15e7c290c14f8301e2c3d50641dfc5312f9ac0e7d3d4f713c1f0695cd67c11f00eb07c6ad06702fc34d78199daff51c5f13716f6e715d640bd2d460c0f7f46a7b8ca6393428e0aa6abd1c6293ec5fadd9fcdf32a9a0d0fed8c3c927671ab0b51bdfed4dccd7ce263a01c02a6b3ab167771eec099081fdb2b133f40e3bf33f7f8bb7efbd788e5e59ac7f61894a54edca4afb0aee95aa0031c73139534bc132e151c0e47a862f661d6297accf4eba38f82bf554e5839d9e84fc9b9f3235d5ca1e7c6291e8c2ee2bf1c88122aef5471b6df94672d865ec7a609c514b9ad4520653aabdea8e929e689a6f5d0a31f2f92ea0e69bf17e03da7c1cef5782ca17e3fc36287c6bdc2f839a8fc6f84bd0f0cdd8bf01353ee4b1e0ef05b508a32c5a40bfbea82844e4358aa03022d72b12e5885e5b112923c27545a70851e91aaf2814235e88da2fd6817df3974c9f6ab74499ba40d4ef4b7b3d7a32451d8dcacd34d6c7075c184a77c5eebacad4d4a5e461a13d1a06e0ec079e3db4be8ac235fccc5871baf8051cbae41d3d1df388dc8239c3d83233c7841b1111b1f5f65734c3110e66b0c85f9d557f1b761def57347488f227d8cccdbe4d26b256e711ecd3b4a89008ffa7244cd49c9e9213c056fb46d4d156c49bca491e793660d626856c76ec98bb3b28a96fc5cd8b8ca4dbc2a75ca03f1189bee5c221da6331cf05b6cfdefcfc1a69063a081192f0d2a837f442a5490ffd944ad65b453a8925435259be9b19903e8a6b9f8873672389a02508fa16f62c57a4354ec22c64d7dcc072e5aaebff9623c9bf2b60ff2790f21ecd2eb08f92c1937678052d0e9803bcef244a893696aadeb0b10105db9b47e63e9cee2a1fec2d81461009f77703d99ce3166dc4cb92d5b0fed068bc873ec12cf7e428aa7760f6536e154244b2ea2d1376aaf65fcefcdf51f897453dbff65fe868a675288a19e1ccbc7181821036a49ad0b1e573cb04a75bde6dc11a2e15eb5df3c191c0a16c544d2081b81e3d19d4264b9bfeff91691e1ce5a85d9da9d514e7a0892b75d3942a74c8605c763f4d5e62b006d851b3d33d3ad55608ab97f013adbec43ae1a3f44df0a2afabf2f735ef7f15a484d79d67b7e118ce1100077fcab3be464bed6bfd2f04216c3f0099a3e5bed805aae64937f4f08e47d05037d134cfb1c33d7cc50659f698a7bef4efa61b5ad33a8b89996b215216ee666deeae40db14f61b439bfddb9db4100f12685a18d8848ad09f76f87eecb55727bd800dd9b6c9cf5b402e8744843ddcd3a68e96ed6c66844803a50e0193c526afceabcc14c040d5934e5cb3553b878aab114c0416be450fa3b7f17c72a98f226909c06af8eb1393b34385be6568ebce082cf34ddeb5158c5e1a56c2128bf04d870da76e7d0280816f08b9d2427ec6174b6bc3d2b24fe391a548c67fea7964b521757f15a3005e0ab2ff7aacf477b91ae9205300468d061bc1f137970855faae974a9f301edcfd12ec0d093cc046715a3af5df6fb37f215d52477170d954e801f1ec63ee612c80749ab45b55c11e60186b04b9810402417db14481e552111992be1b8716f6adbd170dfe53c5f18fe91714b78a680c742d5fb517cb7247a71f9282cefcf75cc8fb5a29de817b0f5d0efe2cf95061a980c29f4ebdcb3d024e4df0b6b08d5b28019e289634a99bf85d87fbe6a252371d320a6d7d6889a529b4c9d2240c6daed9876a02c1d4e1b19b15afd0c680677950889d4d074c001536188229a5ffb868b2bc5f3836521270db06a507dbc9c31e78bdf1daeb3a6f847b9dfe81106bd632315ad30e0ce089f22e088d2c91ab3fdb0def6941fa1f87f87b87f77b57a9c41d873b0f03af7c7d3039d56c64a037bc91241e5b5422dd4279e2b19e6ccb7dccbe7a60ab361a871b97879fcde4dcf0c6b4131613c5b5930c5e982d47a8e2320168cad7cec052964b270bf756f90ef6785febebe584d4e0fb840c6d1e58fb6cd879128213332c2ce5b532acf08ab7d6204155089ec740bb4b6bdda4b614e24ecd6a4ff05e320d3adbd650d6e25d1a1276282e8923207d928528c89b8c611cbcf0b2fd9bbe8a1660e4968b4479cffbf594e7cf44c4bc29a06513f62a3d0396057586ea011ccd4294277536139528aca53f18ad770b5553697000a4cfe2f944c15fbc16dffd181ee3f21f5f50176be663c045385a13c07a7e8e06806e9d0b011de229286072eb477b01b9fc191b60323caf07f00e8e2302ddc68925003a3d4f0ae06ececb02adcd1930603a3fef0c606fce73038dc1893a30b2f78c826a5352f5e203cac93cfb19c442f75c667bc6c49aa004e381338c7eb01ec08590b0483aa2c5361a9de330e685412a3f31391d0ae843d44680cb1ff5033649ae07f3e92972fc00bd1d6de4e0ad4eaccc530b4b3249e7bf142af67e1dc361ecddc54886ca196e514e00219b11e6822ab5044e4f538883330147fa5d061796ed66338df7093e62755ea2b400a04897b14868e0b3e250d63845e4a58e9ae7ee6e7cf3e955630932209295f0f1c75f18c20beeafd307fa623a397d90c457e47302164d32cbbceff81ddb93111095ba7a93da9b2874ff725ddfaf44350fdf4c962c88eca40919aef636cc17fc756e291ccdc067005a0140d4148715345d56fb62fe3f6d1f5f26d4396999bc5301e90f38cdd4c983b8c58dcf95fc0e54930325f69a2d5f3201010d370c5226cbd8f1ffa8fd862d1765f2ac53ae6160452a8579b4173732106572efd027f0ac6d86c5c04874a3b92d8549a35285e2399192b424440f2db7564f9d4d8eebc6ce7880cd22a4fd81d22aaf5bfcdfc6bb2a806d76ea8803ef1407d125ee082c81faa1d32dd9f5935b03e55cfe5e1eb136b7c0bf40f45aa96e1ceacd6b658ccc1e9b3ba7d70f3fe28ef168f722d84835b75f4edb38039d1f9fa00bbc4e8b4885b2b26f9c3a65110f5243a7b5add2cb1edc2769f7e170605ee9d0599d50966030121da76c77e31d81e5ba18c41a8b8d74ab85a5b41195018efab65a43af449e0e4398e3988a50894128d5f07781fc1297b84cae009631236cb3dbf95fcf5379028671ba7f283ea3df8cb2745b8d0929865a9e91c5e0ce2816e40a468637ecca686cb91b0005e60cdb58c5be6040ebe497b90a9a8b56b506140f3ab8bc7d0b3ada7d508eb40b6853c365db80dc2d4e40d3a847e48e811ec2533d20d264f0bc4aa40e4515d22b6bc57041cb9a5b089ba9789f7887792f825653dd97b9a6413273314927421230cf0c8cfa56c7fa138e618d3b25fa34778d44247133f30db95f4bf933620d67869d322306f88c78c39920e7ccc4003f33d2f0cc7fa8ea07eeee4ca916334c60cf881d3b73d6c28c33c033e2c7ce94b4317306f4ccc8b13327adc44ce5f4210669324298556e44486dbf7e411439c0f38db806da858a4ef0d6f060b35e66674ee4dd99edfa403842b3b4a134cce22b5cf4531165024beaed6806a6ce850d83e010c476d04ebf0b530f00adafef4d7f7fec591da811ff1807c4953590233882da7a73cdc386dd34e2fc5b9092fdabd860ace2800c169a73130a9457b184b9f905f98f06a20112822f745900032d959a1d14bed30d6608712a3cf87be6542b2e8a5684da70800ac2c18a09c21159e80b7a5c5579b2985c2f6f0d759b436cada8856d6ee76299b3215b7f9e3c11988e06192a62364c46e4ba314e81afd7372167748f95a2905a8f0bb100209e0725935dcc9f9ebd7e532c32f9048b00dd6c63a906352ba1b77c9c42c1852c1304fb7513e83e2d4c7348a019f3854b80f4b8df30c58aa7f4a0bce59ceebbbc5f423b240973edaeca54781f6c9620a58cd3042fb8acc5c2e2dddfe5a009b0e8a5a6218302e37e841a5cd036621d6f1128e024e0123aab3ccbe0ced9c74a0ff3db9258f34c77e4b8885a206294321c3de8b70f646a95c15204df203f3263f2ecddd7a0ecd29ed746e503f67fadff19a7c0d5746259f4c83070bb0bc85fad63b9c09b95d3acbea17f01d7ac416f0936a1d5a18f9e8a7d6e4047780ef047b708b3a472087a1dfda407e8237f0a9de28a6bcfa18e9b28c1dc29b27108defc47beb024b84ba02cffcf7ed0c4091abe657d30edeb378d42369dd1c40ea48ce45250d06cd93eb5ffd64467f0843cc76573137f6ceff412d4ce8116dae0e926b02e42c58caf3ee6b9f0c9dca32ed1d768ffe5a450f3668f4f55fd24995294d887af7a21a8b153c8bc66d59040105c3a78cf1db2c15cb87a1635ae865c8d4efc85ab3cd7c7c402bf3fef49e76a5cdeaaff1e019dc739d4b4571d26bb2712437622cc9442fbc74341c12d48f6eaf5be831ab3c810ac13ba5bef1b3a2eaf6017f456906857f747cac2683752c493bf0ebad19116a0c736fd475c039b513f043865369c6d38d42229341cc19356a0fd8638825ee6eba4ebf183127f1be82bf8e757f1af293f3595330940401f79c7880819b6d819f61ec23df8d5a1a5af5a784b1c388ec9f8e1a98e5e2268b33fe4f7f6f7eddb155c246bd5bb0bcd82705014ecc762570624861a742d1f5c06dd0b47457fa69da826804bad756ebe54459c453c965c365fea70d75366920b912b191263c5b0014dfa22d6ea03e8ec26f8a522af4f9832825a3124c9435a4d57ff4a41d168ae0492fff52a3ba497c0cf861fad8c2df24e93a4bd0d46038c0ac9ce595eccaa8367f1f8270a7d2a2fe410b427859e2a33d544f7f0b6a2f7f5cf94beefb8aa93d5f386792ab2ee71965c27df5f9a7b6e5a1580ee4c146611fa9f37127c7e610cada7b5139435e787d7d4cfbcd0f1d65f15babf5f7fde82e4b6e78a0ba37eb501941942efba8261fd075a67d74fd8617704d34699ae88b28070743634c7d50194f838e3c691ef51644b16d61c7a27fcfc279436e409e9894f7fee65192cad5e6109d1f1ab34b686ba826758171b5403273f5a6d358885c056ee418b65c8f82a3d1d3293d061c05a6f386b51bc2c0c2f32f4b976e26c316e6f4a3d6f03a9f03da7633a93deaa906259ed45bc5541bfcec4e77e9f41cb2333386403e8f0a08690e77072d5b33f52212db7239c7a98e0cfbec652f20f76ba1a2b0caf5b6568f85edb7c1a18a4ed6597b15e2308e32012654eda6006b280521d581869695f4185376e3af3fc19af75d36bd28ffef27a716f6c7a3e69842821ea4bb8c94ae1f5761186536339cecddb87d55ebbd7eede410cf3276ad19fb319ee24c664438ec53ac5ff7ea535d2ec042eafe783a4a0327a6524de5abfa5b21d42791a7de65d7c7d853b61e935771bf3903f679f295b98aafc598ecde32bb330877c308e2215176d9d6fca5f9a8b6c0041f540cc7ec925ff761854511cebda42178e07c8477fcccadd3234aa72397bf468ba78c3991c93e248dfff503e018297e5ab9a81e3722a4afa6c5897484364c87e85d29b00189bf4e9980b80bcc7e866060e5debb5f4d414da75c75470d9784d272431ed008d0d19441087ac4de0408360f546df71c59b7c14ebae2b47bea32d87099a9a9c3d84d876eb8a4bcf330a593b966008df0305f91af33918812c5b47d09f8b0c4b2cf5ac897c7723f394642d108e48e4641c3c7cdc12fb2762d8b361fc0ccc5c8690a77b52c0ca804820c9fee06305d8dbb7aa34f55a3423ef02a27f3c5a8021ce3c29e26c6bd0bf7930e861a93d3504f5f828661e3b20b51ae5be06853bab2f1f77d4b668e88fd6dac7f17f5492b309fac76b6de658ebd394dd862fc7384371c606d9747e655cd4db1775a6626c0d27d9dea0fa68e46edcbdde1474f381ee77aedd899bd657430ce2af23146dddcc3fdca4b0c11fda79bea81543cf219fa635bd305fe9f3d9038e3b96ad39f07b0883ba7e0a632c34bd846ae9ff8c576944956d76dc8af4e948d3f20379a3950fc8e16a1aa9e686c12035bd765167b29a07d2b9b03bd45799140425f5fcba401d34ab7078a8363db655aa4d12a455dd2691307bb2f5b8492746554dbe73eb2feb6ece1b1bb06439992beaf2b6bca8c2dd3b58b0921e847d2cf3f953c7874588c0c16fd97b061dc83ce40c93064bb64fbe3346fdd1f73838d2367ba0e928c4d4a44ee09959bf200d86f98c61cd85326011bf4fabf69409d568eb59acf05c7c9294f9588b18568813624e9c98f19ba15af6f8fa9c7be3ef9cbb6e57d14745c68a4d731e772b161cd8163fa97dc2dc0aec15ced96b06670a701d0ef7e129807ac0058e792def87b23582a58bdbc77749d97d2c3eb9714f957be0904dbab6d632146c26e1a1b519d5861956e5b4d1f08d5eba89967bcf72623855f1c9be1c29ac01176804899359d5b608000518c5556ab74e6f8ce8a1c93e9ee781f05632914fd0be2ed4797176f7f66b41b0eac32cd5550d34609544b36255ee015353daca301e4d70e859aaaed7c0a4c6da3c1ca8f290baa17a15730f33705098727e834d27ccb40bd7b8a0e1ac7ee9596672fb7321fe16b4cc0fe71db39ad7b27aa3a049eba7e0974bca1f3476e5950a155624c4049b788310333b4b52ecaa00043af655f7c4af1c08eea3fd785c04a95acba2c9975dd4d0a56eb29a6cbb18d88117232cdab75f7e6dabc7d2140bb352e51cde421dc9200228af321555748ddd9b03f0facd297e3abeff79c7a5f0c651ee99c2d762a3a40b219a8c77518c5070879c17444736eb824d55b8a3b06bacde020d062f2a6021fa2909c566806beda3492394723b258151a907aad0b5f1990f7313918a7867b299ddeca9d21d9558788cf9e5ca9e390661fea57813417067ce44b18d9cca36274ed859e27596fa43f89cc72ea0de0b2c13d8d4c41e9c355476519b556d27c27d1e4b772b3b03c06083b3202efa8eca58c81e53a3060631a6a21a132de43c8a5c044d44fd6aa47e7cab2a4892e2ab7ae66dd12962d1e70976763ac6af0063cd6a5508c39395d2f21907be538e5bb5af2a1a4d20a0ddf0a6376b189f0f51e81f590d52f553017cebbe1ea379a225fb912380753e63d4f3bec1f82226bd90ad6a02a1def25617f40dc07062eb0b0b567228374ac5078c3587a92d83f247497a83e437b77b28087756cce2ceadeda5e9b8bf52c5c93d8f524be5ab71c0ffba40cd19d9925acc8d6d45ccf5ac34b0ac19487eb14c465bf8bca7b1b501d47b445a2360e786eb465da47775b19bbe9009135e03f7bae64ee31ab215435dc2546c52a2885e480a998a3553425441778d20f11d8a223dfb40c72730a14f6f3760c5dce231e08a0fd4139730763ff16e91fd24f72e5361a31f4d43487c0f27086124288bbe2fd00a0323002062acf4db0036a5d7c0974c5f75d340abc0dbeab28cf4547551c5a0f12fcda0243977eb49b540ea4b3558775844bb52c973a3f98aa790c58880c6dfd46e1831e7679280aee66f79205826851bbcef33c049bd1df762415eb60b00b290615a6a0be09eb78f1d46817fc316ad261ed8029dd4241750ddf2b18aca20d6802e4470cd30a26a4845db23dbaed97531174ea124db070c71aa9bb86d9545353e79041c19bc699b14d2e6e3abaf534e7ed2fd75efd0687791b8f118dd730d88219fc46cd278472df487ac3dd09a39615f23ea996fc0175963f33637f8a491480be639653c86322106da8dc79a62847425d2978cfd6b73bda90395a067adfa28cb039d34a6ba1b6dcdac30046a9d57582e5c2ac238e42bee69237b09666c64f980e69106f582163beefdeb481fa604209a032d24add941f08113872975e507a1c2afe7765662b036da193261527ec65f228955c23ea2ab0a59c5b579d063c8c3e96499267d04c27e06874ef8b9cdd17e6a48766ea1f9d98a3dbeee6fbea4d71f54747763016a6980d0b544b0adf660435646e502a5ca13e886c20a3eceaf540b29a2bd592150f5100ffcc93d0c0f209c2d7fa703e28db03940148318124327b56a7d1a1afeb9f9243badf66e94ce035acfc7f0cd5acecac50baea030ab869cf0f11860a972ff0635731b6f5489f82467f9ec114acdad506b4cab2d3a2955f305b5c94d6a15ecaffeb4911f5ad18e811018931146972480c008648e233c69a779e2be12950bdfe1444de150475fa8f1a7805c791fdd9f61ed605fe2e99fe07fa3a176fc2ce8333f1f2002f274f560d948199085a738903b753846c86856e23fa9981f3302246d0a0598ad82b589b9e45d08cd5284cceb3028eb290aceb8077a019262f1d99611cb553fd93e358e6f5f06fbe8630245694a8515478349e0223a27ae6d096148dc86d98ce9a4ba8b3fa34188124fdac55858e98fa186a07c23c9301317cbdaa261249e631e2ba572e6de1ae5a700dc21b6b7e90422ac37e04318ac9b616d9dd42548482f6e770f940faa2145126236b8a2ad690299ed6257f5f894fce70389432f15b1a60c68676772d496b7238e16d875d505a3685805ca2db5c4471613b25361dcc74426bdc73f5279fa8f23a7fa94c04724f0d95653991e04de23ee3ffd217e0f1b86f60b06aa1420a67349647834ab3fa6fafe676d50aabd13fa7e3904e82855d8894e235c850f95987c89643e297e7136145d4cd741d3e936073e77dc80abfe54b059ff994479cbbf07aabf3aa6f147a62a61f2191a63242d40734c7000ccc8cbb952c46c5c3343dd0a17f579673bd669aebd77ddf1293617a4bd743f884f6c3578d9fa805c745a7555c4a38988229689d140705946c5788854d9b70166cd0bca1403ce17000b5adb70ef97c486711dca4ae77e868c9ba7eb30b847c34d08e4f938cb2f50f2619a5b78fd1042e0351353616b1c71213a035e3dafe768057399ce7705e1f6ac57f1e1a8d90084283a2c23c1b977c199b07787e458871b79a589fd8d450547dbe17f4b6dc6a1259b6aa9bfea798042211c6793a77571f1432037c4bc061a07a8e42b7fcd8dc517bef25ce2ac005c6f0960fb9653b2ae3edc92846975f9d3c01b6788067411647925e92fbdff7a9a845e3037ff85e34e1f0e483601813ee91aeeef8ebc1dc1a33282e12ee1503107b74966720d18a9adb7573f4aaa1c11534bd8e375b243ec8b6aebafd89f76bc1258c576a2f747bd31dd9ee0863a4839d55b045412915584a330c4c6ac0a2808b48308c8ce88c181d98ced1a863af89b88f38a18d1be86af29e72b29d012794d66ba27c83304d7d5c30739d636d77fadeea3fb2471d39267885dc90f996e9e67885db8ca4013bb2ceb2b6227c4e709789191b08061b344a95be2a13004870b97fced707686b86159003884beeaea168da566c3cf5411eab9326ac56e4f7571b5f1e7dd688a51bf07ebe26e2c4873e2bebaf8927a2491aa3a745a039a57b18686ae78ef47addc24eeb8e9847bca685dabd89aa6328b8175cf9d25daba005fd415d7a045ac5dbf548bc94f3b46c8b2a8e473755f4b0a942e005c02e313b69fd132468ed59d11ba8812f3eec0eb4015fcf22f72a1e9f6bb5cfbc9d205b110caed0d6635eb55eef15dbf636e982503be9851fa965b81a12ef037ddabf2d4ef1e0207bdce909c9d6ffd36e2a5420a318a412ccc02f4038992cc3689c53e5fc3d28270ba99f75ccc7c052f704d74c3136a87f017bad377767851f7b2c7f0b4161b839aba7ae4d689bc1a9f8e5c505275c41af6d045ff7bcd2b3bcfdd1914b3eec3aa4f14e1d90e9aeaf244f1c0511ba8d7456616fc1db84753758343d02c1d124287b7283ff82ae8cc2ba0dc4fa5a06fa712b06e842def1c0d94ee6c05a4db5ccac50ddfe4574d37ad02e53e95667d43a9f9337420e9e7d52adcf8d653f5cd548aeac69a7aefab405dd762a9a41be4c6d8b3cc46fa6afe25b46fad8b886729266875a869c68053afb2d107e6044709d6e74b866e0fb98930a55b223eeb5041ad9f38f22898aa2bdda2309822b97c042ee0072ce99c2ddd42082a0c89c49c5fdea096e4c5cc6e906f41bd0f6bf5e253d82598d8cc26b437cab08d4c1601f4dcf3e8b28f28b73014ebc95c98694d5e8e8a603a09a72a7d6c662b722911c9084a82580243fa0188827f9e6569baa54731c7fe370d2eca0808a2d04690b29694438d86ef8497624897fa28101ea6be960d05712a23bc489f275ddcd9a46e3b417656b6c1f44a9a9dbdf243ae82426069038b23e65192ffd9d3b5cb1d4629a4cba912060898db5b1c02bd7bd20c28b8c37c368621c0327095df2dd67b459a564c84bd83a0f898406d4770c9182b4d4aa17e4396c14682b1b552f449fa474515a50b4b164030caff0a4d67324fe8528cf802b0101c526e8596641d1389e409c002dbd3153eb905c9198a05e1a5fceedbaaa27375a93f78b58c0983217ae2533208e865badecb3f3f6290ac5ad11ea9676084ae1cdc6e7be26c6b43f0d2df602ec880218c769f5954f4093f215dab94d16627a0e20e682e9c9a572090eb8e09330bc2e01651e521c06f4910602dcf04070593dc45812d14581e0e0394c819efe01096ab6d80fbf2eb05e44731e60763b07b6dbea07c0e026fef353879c1696adc8e251779c87501747d17c9f228f19ffbe791c42956025ddb8d83491439f633debf1084ac0515510008e8d9fecb00c0ea6981286b890d43bc9b020c585641ca58542c210a3267e04149e6bc13e3e0046a79f679613823d6220d6ba69ca3855ca78998098943318444c25f53801dbed9f8a343bd59c5b55924f2a6fdf75e9855f4d40763e50a2b29669417734a52a03d1fe5bd80bb721d79bc290fa2b52d227e4aac9c0576e905fc9c428cccf6965f6a15ac2ff4810e274f30a6423241e4e31b5f6d88f06d135a909189fd3a45ea22b3a614a15345d6b552842e26c2e3f2d14d649f2a83e5f8b729c33c9580b05bff20340b84dc4dfea311db926089a4a15e279435889f117f112d21022be8053095c95e3380b72119fdb5571c2216e9d8142a7c212b78fe35adeb724959de4e2821a9dd4baea9015784837f68828a1e65635f2305d042fc3fb0014071a501b91f497e232a9035888a4b1c7fab4113a8fdb57855af676255caa0f0e220c26ec84bbbdea5495399d4ebbfcb1bf9b0f846eb3d255bd29310a3c63c31222b70f15514f1f1f7bfc08259d5542270017a0dce17496658d9245d5fa19227f0c04872467e5e1d6c4ae4203f134a1044036569222772640175318369c12ad0a5c8dc5700b42cc16a45fe0c6b9c2d4fa00a5de2451cd88a1f881451951f85f4820abfa21d252a8661a234038891dc3401a454efe40c3a475c8824a7ba93f19d9c2bcd36b1ee27142024f71dfd107fa42c9ffedcb869bcf4126a3fc9ae02019ec288a07e43b23904fb19e481fab5dcc818daae2566d31b28a876958c411111a58fdd256695631223a77fea95f45f216a431e3915603fccf8fd7700e1a233dcfd10dbfa60b0b65d20a08c075fc9f5eb8f90a3e3fc4990a92cb7633fb15f8c1a11b683f7b346ef877cbdf0172c31571650af0d6cf204b2bf142ce0fc3518c3f172e364209d6ff49d5b4b699540114480040d631f9d2e8930f89b26ae1b83223d9adf8930fc06136d1434e358d0c0f239ff77012ce3d035b46c0c7ffcc06dd08e130a61e72240150c6fc8bc9dd28b1b1fa9837b3072af7f3304fa4f0ad3bdfd622efdb5b505f583ef48bb8fdac04febf59539e43ac2398d7f615109e57581b877b5a4ea802ba87d5bd58412a4a8b7c0c7256b57079412ffeb240d2859a795f70681d6dc56f610203be5eba6faa867262e1a9b61df613a5b4bb031d3b7a4758fca17375a8e7918a4a9f127a00801ed78df98f82492ac9cfe80509517fa1c72cfda252b18264f1a63084ed062deaccb83ef5a6faf2be4bcb75b6695dbf86fb3ead978c035cf1db25dab44cd2b58a8780e501235897f00292b65f7d49f3c9ac7c63a9d4ee5c006daf9d9f97e080f25f011612f9f03637edd50e7e1b26e441f6ac4fd3360d94caae4eb540029f1561bd88ace2187a00e862ab50461cba9c8a11d3d8ea4d09883ddcc708c7720da403ed8f38826a515a4a7dfaf2bd3c413f63e7a56d9aa4e2543b57cba29761329054c9aefe0890178ab19d284df2dfc7b021aa4ace363cbfb0024d0144c160875e8d2492464f49b419558d144d51be529f3d6a3146dc1ae9ece67d914e5f3998d8f447d0462d56a7c7a75b858397fdc1124fb675ae48d910aa453692d0ae15d686dc53cd842dfb8a1858011e23039a9d2a6913bcfc562f69d877d63cd432af40d1a0eef1b5cd9929cfe94d1c12d2f019d6c3d20bb6e40de4c28ba40e8bdce77964036a38c5b4f2f50de82b59484b30577d9865904b884d640af5f40163c05d65f071c4334cc13ccd925f00322f9c268eca046760f3c32c2ccd7a64679b8ffb028675690ab0ed05a72d907a1db81e752ee76a4082611761aa6a3a490700659588d96ecb9e6bd7d5a6838c01afc8e42c19635707df8080bb36c9c3eab64225bd954e5eba9f97d6deeec58810dcc3db3b364bdec25df1e0837bcdcf3445a31961b676783e82706797375cb12b4477fad0be564cca013da8b9f5d0edc3f9a65658070af85e23b82e0efe03ffc04414f53ddaec5caad7484024cb1d620a2bce3132ec44d0dca77e39e81f727e6b7ab3943bc6bb1a6b51b1ecd9359348934b9d67c16b428f0e126fb24bd6854d7da093ca62b1d0636309710a93ad5376046a8c16c1dd1417e10b8e9d0d5fc72171985fe7af915eede448582a97e8368d574907b022c1eccc582e54496d669b3324556ed2935181bd5cc52b2a243724a533d04293f7da46fe367d1fe03b2a0750e5b01a7d4700eecc3df0f1f05d0afda7c8b1cc29a16adc87c94653d6aad7f084bf1145bf1324cf937b2abd997793ea891fc41a588d6d219f2261d3b0991f6954709a6c5dae97ca4c3a28cdeb8ef2d18b4c553b8249bcc1a08fde0f2b0ec5e0e30967b6656ba549c0bd37db0ddd6defaf3c539d6eb838b788732eefb00e9eee9275095619a4de9786f3ef80e9718aa0bde583b5aff4e2b537e5533360365a895403907116049bc6e8d76ef146d728073f232ae402e905f616300d8c80eb60593f7513cd478a062619f6296da3f03623b5907e2624841f3d6f0b8ea5f9efed2f04ef480fcc3a1e306de81b08c3d8d5c07bf195020011c75d00354edb02d24062b823c3a9bc7fe689625b65ca456c3c7b69854d0e585bcd6c25a2d2774dd0a82d0ec0bd5bcac4cbefcb5cd67d96a8081d4f4c07f35abb61b83b08483aba54551578f7a077da027151608c24429063f82054c512314ba2bfa210074c1693cb5e114f1e35ba9a593b78a2377ac4f01f9070e1fe5b5e69c122bfc95208756e415b7579ec04cf82919f2abe8b6f02931476897224415abc8d0b98da5f53bcf6f2f834a27099fd03780185a84c7bfab8611706f75bf69af1144f35b8d86d7d4e248fe9431fdad51ea65e35e57c7b3949f323ebabe8e80c2b3125b20c74e42956ef7d856cb089a64013a019bb41bdada94bcb77cdb702a7158a91d81ed090fb0e0b23ace38778a8b856500598c23918ef83b98ef5c15e0e08e8e687e1c1c30bcbfe8867caff21fc25701e1287d3faef7a13d0071b014ff5a2472b02b2c022ecc9386c09b87a5a1640ac261906cf93e36ed13bab56398a913287553c62d375f97083cde3b87394e2d915e7d99c7e5239b9974075da2905689595965bb04cc8561074d506cfeaa2d90ad112497bccc332488bf9a07f42c3709a0daa838a61002ed92a56014b2bee5b0c6c1a64c9cab73ad661927a77c87bcd6733960377a7995d68c4152657b75029c912dd57971c74a1b5a66d41ddb9a034076fd840878aa113d06eaad84cc0782afe477ea2b0b64a3c07a72e7acbd89b1ac3a3272c013b318a7f72bc1385f4c000b1c419a8e4300493ddaba8c55b372338d1423368227f6145b516bbd7cdfac218977c620197b98c47a0f39e116056678ac5741cb13d342be114c34cdeab99d4ee74fcc7b7c3de6765b93e93c2ad99688eedfa4abe232d954fe340cfdd8c116b8a9470ca327e53f1a77a2dd2dc74da7ecf47654752d2a2a77018c4759606ebbdf574cc8db3f98ae6bb7c45b88337408c91ca6246cfacd3aecd625a98886f39f1a06c7851c6ee6af881ba7b7fae98c220cfae2934c45de8a5d216675d3ca17a88eb81eae19585cd5e46c8fc358060d322bf028b40c8baf6bdca14e4ac2f6cca0afe52ec262083705e28d2ff4844ab5ff5e8f0d8249ad9ec58f22f1f55cb44fd938e424c31e470e0096265aeede9da89677c4d30267e0c3a7da45e0a6b5dc5d927a1acb462ab576e9bc2ec6e5c1eca7c73c70861b279ba0f648f25ac65358de0cb4baa139e68e93a4e2998a056041a7a2d3c572b4dbcd0e4b900734f2046c6de7be9a912ebfccd5591f77cb46022d10908c3308c4b810e8c0e11abc0c8dcc93637d657e3ce21dc75e3bf1e89b8fc187686d001a2519f571f67c958065411ad4c713043fabd26eda780803d2764c1c4e629573f95aeea8ac8dcf67d9c9c3ad0fd7af529c6bb9b09f7f9aa5962bfd8768b97f91baa5754d4b1a6d3310ad6070a75d14492a2dc5f599578eeda03eac1f76b437f4168e508cebcdd7a7e295bb7c634e1372b21e976bcc77c468226b65e2731ca96de4e50128ed7233439093886ba5c02ad2a53dc96b9752ad5e8f191cb63d28c644e5809e008db48360b93bb687d79502ff9278b36943a1b713de376419a9a7132099f420587b4653c984c22a4388a66f7578fd070836c235360c8a04da04d2b3f3402707a5681de38f7362580e03c1635f4de17a7ff4ade1bec9af650672110b0fc6507787ab5a3614499fbd80b4b8021dca110f649c01e792a6f2f18cfcfffa8b2b343fc70ac4ad1320e51adbd8feb442f061ae11e2ec7caa5b9da296cbfc91fba1c306b280e0c7826975aeecc4d6eb401564dfe5ee7bf2967f530bc597736421603019ecd29616d711bb78b6463dd9c5439bfcc1a6c65362b9aedd623c65a3e45b5e3c25acd7ff76a9584b53ff99be1fd7d9cfc3978aaa761f475ae969bda21fe811fc9c5d308da4625ba6b5b711ab980abb665fbca0dc665210bccfafe3664bf97408779c2fd399cbe4ebe5d7f9154958380b417dd18c1bb659b1f1e4cc2f6df9527a6ddb60f9fbe49b8cb30896647a66c1edf731bfe822f445b0f2bea267b39df70a466fa8378dcc30a69209bbe481e24e83d0a6b30178e20bb9a4785024480635d0237a393075b7cf890b60526e20d9fb29e9b56c5ba7c89ccb1506ac5ab072dec806c1325a700c0c3d1db1c70ecd1eb7786a8c3ecaf92107a48e148b01e0c68fd1dee68a839ae73846363db00fe058f68d73412e4a996e6b03d91efe58e156ae75df195cb752c5e8005e66ae0de5c0a5eb955eba1555f0086e81604dc2516c6be45541df938c44b0f9075ca35899edd226a10edf2ba08c2f32ccfc67884c447bd77b9caa9cf6f37997328401138ad0f990e887ba8d485deda71a88e1bd378ed82bba32758b593de82c36d5931363cb6162b03c950ae76da9facbd236146c3664d54f00dbf80373b6e17a36a9956dc79f8d4b8366395948e00f21c70450b77e5a5ea2bc22b1d45583eca444eeccc78934c8f582604047fe7666aa9b836a47e11812e0b19e874afd900dfd6011665f7271033a03fb58ec03f73c80433f508dc2b9d2d9834314d5a40891f1a0cf063b2fc6d4ac82ef7527335de033e61b516341775279850e7214c6084ff70e9268140a19e9f71a13dd98f804726149e716592078ab6256e2a8eb1e1cf4dfca9116983e0c708f16e83f5053be8a9c847789ec16c66ffdd01a918930b8cd59f5a0a2f822e194d017dbd6d8235933e4854716a28e9e7a1c16d9074a801017748c90fe289ebf9fa31c70327074a4a569909e4e8ea9c706f39d789efb2bdcbcb2dde2c0e774e64113fe4d4e8e8d69207ce790a3556ba90d4e1afb71a4cc105c35d38412453f34de3df659284fe497d3e4dfadb6c52517e01fbb3d99f685470495f85e904d6bf3aa11c86b2cdb7b4da9f26a00ad64d1f31ee3e7e64372d53ce7fe5610b2880a3b93f1ff52862baea3faedac5810f7d567a0602c56fc3d0c21ad5ef673a711ec6339bee4af4da2cfa5b817a090e24c3076318f48fe6f13207f7b5a9639944aba054e14afa44117cf390c54ce367154748c0cd94eae6c809b34f86b6c49645fda49a3b0f4e96c020693b21f21f907159cb0d505e4bb5a2cd607a967269c61458959b1a39e7979805af402c47cd7e441b03fcac68d22e19dfb15d9c379710df255204d33b173d3c5ce40b303510920976f57431d826ec21efcdcb71d1075dbff8b1ee5f0215dae3fe7484a47e221a62b73a5e40fc4c98cf793bca4bbe615f5230379e018df8d6b4815fc3928126d6372a86c62518caf39c7dd40fd656a5a91f359da96a4bc72d7c506879ea3b008618ac08fdf7f700165727a2350e69644e8da265413e698e4d81ec1db4cb8a3feeb4d823b0bcc51a4b372b5899308596220a659b0d5c484671d7f525ddadf94e3504ae042bf207ad8e1f099f46a7f222b65a67b81c35cca4207738e1bb3b23dcc14c253d47f723e7b176f86400663fd096ca41e57f531e77671e1e0f42b34d308b1d24e5775b821712a28f16eadfc711f300635fc088da54ae24d3c31c6873a5589507a7876d159ed6eb356e8d41f0f624c3fdd12515d0409ecd00f2f2187a64a8f63d1becc232ab15df552c8a4870e26e8fcc7a8594a7ea71cfc8b147f5164973436f3e03153ffb6266b32a98e45833403550e0bd181dc18bae03326d876c36a899e329621ba5d293b26795764d9e92a200c64388c1647aea48d452de1f37b7c9809c2eda58244e7d73a2123d8971461be1ee220b6869082c6f1889b44dcb07fc1bf9d4605b68dec33fbc4f48f1389058e00a7219e67ce1efa51bf61922cd050f10bdb7f1cda062ac405b3f0d877fd39d1b57884c5078ddb0ef998e60e3ef91a1fad65d1936fb4e4ebecc96802707f68da8251c678cf7d50dcafff368163e1b3e5ae0d23b1606077d639a5464878a873aa5b30bd32b9b5ecff6e8bcf2908e7c68a57dd9bf9878b8b9466cfe8373e20ddff15353cdb1e593dbcbf5a74f546e67a415404b36affbfc74e3949016e2ba12adbb47c09f83ab0a7131572f2956e29a5a4c24a427d062059aa74fb5852a41dbef34682bf2020525f2eac5118d4201305f83e7c86ef186c7608659d3c93c560b2a16bee6aceb98e1b4a2f868a77384b3a32bc734c498dd72fc05464717b47885e9e15fe20997860b9ee589d055c8ff5172c219f79ce2342a54f6a0f702eb632e1fbf2845144c3526e980708a62771eb8eee376defd2a0941744de8b09b8567e7a53e5af77a426f791f100e419e5d482d1364cd7bcaf72880904d2bb01be311de5dfcd238630daf633efa0931ae6f8bce5153888656b7cc5967e8af45e5aa00bc61d4ae26e1c90c149456f01e18e20a0a5223e1abffac9b54ad0d25da47baab132075df84e09bae5a6a09eae8f810cad515564692a774539d2f30b6a4ad792b794fc978eb2a9c809a1e16fe827493d2eb5fb16e524d78d4faa7d4833d12bc64fbd7e574b8656bca2d447eb245789c12439860aef7ae42ca89cebf9a41c9155c3bb627e538bc4a570a6924fb342e428834755fc5a11c2d5c04d095f5d81340570bffa2a95cf4375586af19b5825110c58b469e838dff17f0498126456ee2ee682ab58151aadb818c5a2a9f866b97d2144d7d0f997f9180c56f1a6b528ffc0da309797647a1f60ca355d29d7412057a174909da9a0d43281112d315f7e41f382c5edb367117f54e38d21d93170234225d57d32de69441fb927aaf23fde2b79e519f197a4a2ff0e8b20bf9ce760c6a678df30dbc9ed7347d44c8e553940888663268726783a8485fde2809c7d466f0d5e0ba72b5491ef8ae9a5ebb1aec6758df9ffc582d4bb2c74d2f61111683114ac172dbcd5a7487ce7cfeec49dbd4e6c9360d93871fddcc21656534ef4f7058026663d71f47bcab0492a8fdc0f15b95fe281aefc83f649ab7ac53e3ed54470a6360fd6cc371ac1674e61356ab34a62998a05552ca6395de3484180036fc9981a70bd506478c3afd6e766bed4fc94bd747b9f1e34c28178b72a5a0a462cfaffed7c4951760c31fb7be72e9fa2061b871ff356e809a786367fb8bb1fcf9f90cc9914c58ced2f39b58c8453637f753e557c943db6eea7a7fa18d484d24351f375e1799c669368efecce0c1af8d8dd02f0b353c46768db5e09b11400c690d9c3fd702df34fec4841d407230744d7326d0bc25bf52654e63306aee07cb7412b04b939f01180619afee5ec1d41a7037faa3fb8c76491c9c2cf5f244b66df68f7e934ea7d505117e8bfcd03c80229abd2e982136bf54d72d3619276c368981ee8561d3347be836b059cdab9f89ac48155f9123b4f9def89e713345442ca802271cd86417c36d3a9dbe765b6fc2f2f233a329df0c0289385419394fa589b8bd01b41216161267d802f92618a30173dff0aea501af8d3e891106b1538282a3c27cf4f5bfe4bdf3691e5c39fc5443825cadf90616d169adcfa93d7cd9b291d9519598741b7e8a311f1fae38251a74c06363c97d8d2aadd13ac59cf407cb5bb6f551e43fffe82fdf82c19e3ffb765558f1a636aac8ff6e66521c6dc19dd7773527a5e1fd453a2693a0dd4b49123760b118489293ad1c76a5805c8ac9698fad26ba315d4e418fa122205e2e80cda748541a6c1959eeee559b4edc41abbb061821a207d2205be825b516fba9925c6e8af9eb21e8be21452f7d00d425acd07eddeb569c6574a51a08cf8198855cc48cb86a0120fb4ac5009cbd0883ca897e8e5adc2dca01a4bd0d83a8d3f828a0118b5fb266ebaa5462189dd9e965a54001bfc63be31a110813cd436c19676d8738bf0a6a19d449471fa94e01ca4632283e136961928443e37ab3c2369ca77b9c9287d017af21b10bc6c1f2b3779aec62434ef47423ffd4b98496a1138032968ccb54cbc384179b35a29ed1ebce6c9cf17c84f6bd8f77d1c3f7e291bf42e91535508e1b922403e827ed403f775ae45d3907c54f22a879dc9f5818318cd610c6643d594a46f4d5b6fc800a2a97d543198b298cedc97286c2453e74b574f6b338f745ac1c678ded403f176a9eff35213d430531b1fb24bc23b5bb4d50d5e88f12767f973cb067931d5726c5c282db75f2579802e5d8c0abbeb8d6d2def38efe3d62af2133f8fdf570275f60e4bba187b29864f89cd20267dcceca8c68a28f84672133ffda9c0c37382c194c64829a66e0edb4882377bee1beb28d56406acdfe001f70b1d9f33c6cb61b496d2685f9a60a053b6fbeeb1f4140d64d0e17a8c74c3b33b05541274f4638fc556b427f09a7eaab85fed5690e17c001155a9b70498226fd2462346d825f34794239b50718268de66427d7f2121a18843af46dbe0def3992c3d6af00052f7bb5e1ee662eff90bff25fe02ad5f14e883e8344bd6ef399718f61d434c53a1a7d9a3c8838347b5d3c1530c4cc29534a66a20cb018ae186622219bef3ad6c48af250f7dc2eab581089623680681328ff2f1146522e4d597946bcfcca8f045ec678d43d0fb66434f64989efd49fb0a79dc207f7f1d39ca6b100b03b5e633210a8059588bc8964840bb3b4a060f4faa198f12b2d18db4433f07d0c5145c6009eef34d0bbff963ca1c5a79fe995188f778f19ddb0230247a73234cba5e4d282bf9291cfe7a5004c42d8d0762ffb42160c286e49f0b19575fafdfff825e7d2c48a57e391e8803d8ce1292dd69c5450820a61170950553e04dacbe037f27d7b877ee4c066e6fae5e0991f4c3d4e010a60a2c1594d8544337c910d3452d441b0d691f86585fc17d78e92d3d07147a1e7a2ef4d4e9b52252fb5e5be1e222be71edca01067b95888047cc96a3e9bb35500735d8faf3509d5ed14c0c041c3edb1578c063c68af88e368822fa24380304a79ecdab978c7c637c54921927ebf5e6a8338dc26ddbf5352ef46620b72a0c2710c62b072d76fa6e518f5ff1d3b12cd53b20a298984197050f8b55e88026de10c0c3603454d2c265809f1144ae101115901126958179fcf16eaa41769831071eb39d3b36c8cbca5015a30ec1486e0ee40898e6b6254aec556dd1099930a7c0d85b5e12bf976fa176151acb1ca0d328ac659b685012702f29c690eee9c9ac0d92150b1176a613910999e2ea2dc7cc1d5d8f127c47a012a033e83f0da5e8a925a63b53ed5e49692dee7bf2d2307581f7b5c2922a5b69cb1726f5cb5204f491f2fc4bbb8990acb6b4430419f967adaad2f4ffaea7030c1123c6a817002dd11321452c1d66c5e4c4b0803901fbb7924cfb00fea1cabbe7454b98527a9d98b0f5bd8a8ebf3f36e52685a7431b8120887b7b671aa243a5920caa146304294a9ace20ed806b6da78b4e6d3fc2225cce2d134d3e3d436f2b52dbd1d305954e9064f0e886ecb7c9a88e6d8797ce2efb83f9516ee3c0ca207bcb1e0c0bd887f39aa4c44ede104c22c132ffa3a3607ebd1f40156f08e83f0054e9c46ec5c807ab812c5a7708de2ae115a1a2c20ce018a9442a045d3205a94ad1d9039d2014024c468fbb63873dea36329966a0f2f4e082b5d8e7e6b80ab9fee0a76c935af1fc7ee3d68d00863d81842ee247aad77105b6cc27f7cfc03c7169dcc6354bb8e33cce5279054d03ab2f83c6d94cd351224a00b1d9f3663234f46a78a7407eb11ec5521124be3d8513ce4214ada4a85b8017c72d4f16d9b94d541e277e1afbde4e93c3e7246bf324d1b7525037c8e70c7bc26f759ddc97449f15b5936c408c62e6c801048e232f7c4ce7277019c199c3e8c69b2c99d3fd0871782307cddb19664912e702e650d7d9414b56e70ef539fa665b654178d84c59bc80dfa4fb4f9828333bf3328707c1656b3c32c09c5b8bc9e88498efb448dc1b38d0b3f903be35dbe00cdc761061bbd13861bf5e70e9dbc2c3d243f80fdc204527cbbb0b257890b3dcc268e1675082a7c0da8b18245fe617887debf0628d309caec5dd753001a0ee5446af74f595692768afd13f59fc74d44ca2a0c48baf9ba3733ff2e4b4bd016abc24fda0835242cc69c884a0a4c91442540e53050e8a8357388a994383fb42a84a052e8e781eb2f88f07c4822dac9f08f2d5aafee072f93803255475f23c35f55149c141ee032cadced7065bc41260e500ac8a743c006d5c2fc83f1296e05866ca9cbff4c1cf06d8822cbf3f7056101f278f0241b5d088f487d8fb598a348376eca00b1d90bc688382ac2916548587c477009d9a59fc322d92c48e96d0d346679d7fc819233323fbc0e56b4a0a7a229c9f931d48e2c0cc131aa8212bb9823913914f8a5b4a0b719128404a99ec156ba6bcb5ae878ea100761b889c2541edafa0412b81f92cae542b1931cab071a4b1614a6a8e70358cc8b06bc03178cbb7d9009204a939cc8ebdb11c1e0cfa3ad463fef1483899f69f2d67de2cf7716b14ab837cade5a129720dc950b9c2af668b44677229d615209d0c5cd4b1c839574406b0c6597db63890a3cc553d547c6cee8fe1a5ad1f8093eb6aa92c78e85edd37bbf1e12a3d96c852e2db06b0c3fdcdc9c01bd372570059f61c61dde7779b3d70fff0585b20d299d3b1f87eab2bd55636dc28dbe45daee80cfa18f6308d966c81301f3991987cd84ff4012131e138b7d3f6fb8fe87008365b54d66980da82a8deb08a9934c4ad3e916358c188da5ecf1fc0544b91c75e20f89839464048ad29fb438830fadd7cdccffbf6ddbf5c6e7549fd4f3e5eacb2f2ee93300fb8e979b6e8801e9dc16165c9389bef693169746e1531865af949a4a1b5db889fc789cd40f28224c54f5c2e8a15cc2e7633df1e2b5cb32cc2bd26efb45c5008b2fc280a6a31fabbd4a427f67ef9931a819e3e4bde048ee049254a7af6356a0158388826d3a3348ba83f12ac49e83b1595ec3ad5e3da4257b43129fd0c9731c64fe9fccd9e82bf52e078dc171eec8d2ef13b3ee5f3f9cae7fb94cef72bbdef29bbbe87728d0c0042ee912f4156b5fa616ef4c4eff82abdcf513ebfaf7cfea7e47c5ff9fb9f92eb7be5c77fcadfe79eb23f107f8852dec169f3110b53d7fee06c7b6a270d27a0bdabdebf6f98ad8108319fb95da3483a9532572f6b231a3bdaa4a86012dfaeb41326ccb74152a657f1197828dbee470df8e69db07cd1206dc5ddd65a4f9611f0ba50704c7caef9510d01810c95a4226a0f86c06970170a0afa3533b1ca475df3257260d995edf12053c0aa3bab12155d09dd98283330517100ddbc4097ff18a05bbd03bf8376ad359b1135d1edd40a55272cf7d94988e050b2e03fe66dec32a5d66b02de2420e9566ed23447b5333891e2f4c668f96de1059a12411377244fa94125011bd7ea2c33d43f555fe6ae7c6d351d87a9d7b871724649314d14dae506681a6061c9aedaf7fe02280a4e2cc5bf210d3d0b06246895e3d1363f097c336a1c4b22807599858637985700e8e7c117224cf235d5d478fe217b9e295b42ef090b0e2b77f07b2c31dec11c84de0d796612c4c07c42f69cb1c90bf2e8b02c545ae8ee128fd0d62b2c6b5f6ceba1459131dfd03c00bc1a8215e4d69e658d360e7f0941f81d6d761fab6224cc3865d2d8ada475bfe19df20c454d57ea796da622b87ec9433b5a29eb1a873130e486033a6286795a30ceba4a079a17ed98da12247a787b85b70dd10724d0a4d1a462a380992b0d76e048b60cab0f81caaae3e8222b8859a9c0700800846110447d77fe3806502ddd51b013b8a67aa8aaf62d031daee07af9c9de7b6fb9b79429c914a7078a072d075ff4764e977e7dd2078b920916824c9104526b4afbf4f66e98ca98941155ba28a1c34bd932cc195bfcb4d80449dfd20a0f443f2dc9ad23b7e0c2e537894bd24f04812533777377ea4bfd9054672984594a7971e8cfd9179ef048d82fccc313fefa7a64589be5d7231ff34ea1bf137a8aa83645d8a5b34dfbb2f7fbc4124cdf180c6ab8503d3a29a8c0ac900b69ef9c10bfb0d6d5ca1d047574168ae7439d1472e881a250ab13866ab850504bf8c04950f1fc45ad292a052931f9e8b2aa5e497cf6c81f56458d4418a0d74c93cac320bb8edf7c5c20d3b7d863e2a6a9514708e105355c262d0ce147080b84c83e46010627c0a59ae050d490dbe3189c0066c61022dc51da7fda0f53d41294d21392d30f12901211922b814b3a45daa374b57221dd4f354f090797f75c7f7dabaa828a55d1bd09cc9a579ab60aa72b5f9e7d29c0acead129e98a98a2a42a4df8c9b3e49865b35f58b10ff78414d073f5a7abe2b486f1310b308a26bc482124851829b8f2bd5300a816f3764322cb4085acc86c98e08a2cc69c28849e37345a4a29a56cc92c1e4a62e450473101d4ea4b071b0c3dd1a260d8a0cc1605c39c73cea1213a0445c2e00409b678511d1e29e3755d2e7bd64dcef951c902338f11e79c733a0771190c50d21ac605df2ec7782205dfc1d39f1758041048001484124d2ba062a59d2c410433f315467255d47cc8230b1adf2e97207262e83bc909167c273d914483208f3123e8620c2b9868ead26aa0259ae8ee6e9e0f3924329e5dd22c569efdf2d9418834d6c8810c2c579ab091a5933e230e15cf4e9192b0c4e0d9650d41784e092f5c77b764d60658989e9ddd4340020dad77642b9e8083678e08ec4a1a4ede06202c2e3479c2e244861732c292830e58a6047d0d47aba3f99ba44568486785f135af6bf696aafd262d81c60f4baca105e8e84a12950f5d87d7a853d4e824f446a99002fbb38338bb338207cfbefbc1731733490736877d84def1400d5d3c892d3ca7b0392c3fba3b91fbcab63c7c4b1d51e7af4fa00a512f8fad662e43923ee49f9fcc85c18e518d4343bdbef8991077e24e64f1883bd917c970fc82be7de30efdda5347d4fe25f24dd89c7612afd4e96c346777f7f48c7ea8181db50319191d31cb84cde91b28e24f7f4be21b6d4e7b1429450c56236a187fe20fb3da2310b3fcdb3906ae54ea9025f208a512a3da673ba5a15db68731eadbe32e0d2f693080f94012d513305e7ca04b4440a9d0ad243cdf60b2a8e13e41a950676c923321d188e968b548d18899993d53a3ae886211e581c6220612d27dcd581445d064546e89d26aa4f3ab138d7aa752da46341353c368840df14e0c621ca01e82f27d91e19d1ec6515a62727afa714549242e0bad2ba4d58840f0d1430dca7316c5b3674ab387b9c312adea864ddfedd38a3a06999f666572c8cfaa2db21283d0ac0f96455ad13fbf94d3fd67fc342524a4242422dce5b3da98448dc32eebf48b7e21ad983356975e7af1a98caf3188085c54b03ef645e0d2c5e6ec9452f4f2faa427b525ca05cf92f8fd404444e1464528cc8acf1e7fd8e38ddf2782288aad097077110ce2a565344c84e3a28633ee4e193521a128b424c6e8d1e78724c6c8f243cd885d33625d2c70d2e505ff9c2caa21129a403c81e60c319c428420e610a41d88253f11b8342db841fa13818bd3fad8ff1c7dd1ac04ea9a6b9f0a9a53f769f9b09712a386b4d5be32e0010c3006895332187d84c5898e41f144899fdf56f931eb08e3c54fabb94720e148ca473f12697e629215281f7d63b742282b563e5ca8135cf1e19259c06c30e9a3c785e2a347ef115d8418a4fdc783f04d94849cf8992844b36cf952c4d7fc6ef922f4dacfffd2badd2ad21856ef1bfbb722a2ca6f089b1266ca942953a6353d5c792bb430fd721530bf3e9b085c4e9d7e2b605fed29362dee8ecc214716db02767397d9e4b2a22fb540b44019cf7e81ddd152f0e1aaf1ec210d071687fdf27a36879958fa85e2378909a0df6217bf139caa12176631f1d33650393aaa72148f3e3ec5a30f823e00fa40e83f40fa00e803210e6221167a4662a11e820c05110a12244810a20f12442808910f11e8490413c13c0f4530114c0f5d5027d4cdd0097542df2175429d500fd9970c8b8c29637a0d2863ea410e49212525998392d2cb2829a4a414a9442349248924d14ba5684424897cd82465268b397b927b63c730ce322cc3bad9f38ed14ac7d9dd53ca9976e9492ee8392bcfcc0ee8eee6ab876d7950211a8814c16ca9868b0422459ecde9ef83f5d1de1fd838ed21839b93755143708703c362d0e8956c22c982c998b22f19161953c634b4719daf6237a49394eb76c1cd3b8df3329a6d5ab669d9365dcb2826b5d8666cf2fac2242a55ca57c66e48cad97b30283bfec0a21a7aaa4bf9163b22b163ee88c4e794a779e76dde0b686eebcc85708ccf69fc65dce52551c9263629a5d44dc52c6e15995bb1c2d84df8c6206154ec9748a415efc89f9f9f9fa29604235b2b7949fc96dd10b940b699bae63b8c4a7f8904c105526473dadb7176870111a75769b505cd76f9067f84cde985ea8592bddd51a6429f966330df9cf531255d6c772986794a9a52cc993233e674550c3a0f06416e4b5d5fef30ab2384903214032665c448a2356170c11617309cdf242b2cf8d46fd2095ef8b0fa0f4569dcd058fa0947058fa8b4baa2c50940e0dceaa7af1a3f5df24efc99ae0a4954cf39c5f145813a39fcf272f80da21a6e540a3333333774a8cb6075956783c3557ec33bcf0687772ea7951a6ed496c26683a3e99d6d4de76abc65b0c67778a1cfeff0d80dd9e15b1bd1e19a13d1e19a0a4f7df342ffcdbb6d7c3bda3dfc363535be79427a87c7c3e0741d2030888109306be5d32950011d3a7ce5853ebfbae1d443425d876fcd514f87afb4229a6f4e44fb3e9fcf6a7c3b22493ea48e25f9f67ae6d77c06883e74bc0e17d22b6f0406757c2430387378a16ac990ce8da8fcc627db861715aeba196f89fc14f54b8cd2ef97dfa43068fcc65c479d04c6baa8f24ba59f0e88d5006634a346fbbc4fe9b79a937a9619d11c73221a0a8eb9cfbbe6b1d38edd10ea0cce8d4a4105a0181485f6336f1fbb965421d85ac3302965c4a254b218b62d8d0c7593c280f9f07564632354252c85ded13cf3f607ccefaea1d8d71836b7c6506d967e7e5a36d8776473cddb8a2f5d927e5ae12ad51148385282a7c3832dd570957a46882ae5eabb9579dbc2a2a861e5992fb6502ec5c9766700e00ac8b70136a77d6969a19ed832df3cbcb1a1beff92e55f36d7f6e04d67a3a23a0f76a6eacf831df3d25bf4172e928e1cc1b63c5261d4f91b73f4ad549fb1893a3fae87d08863cc14b53d7ef9334665b0dd1863ec346ad49921d82db7bb7957763783717773b031a494524ab9524a2925ed28a5e48c6a6a4c8dbe4c63438d21cf4c15354a29a59c317072c6755d9793339c384a94dc0b7a5543b3768ee2581c5eed4e54f2e7e2431ccfcea4e4459db339ca149595c4218c961fc24cd1210c943020e8c1417f1e6014a0c61932b98c163c790b96885c0826075ba0e43cc660fad075e69815ad2bb992ab95ebf0e8441e1941a9a3e3c40c33335319f3bc7a66660e554bdf4f3c7c3b0056dd125b68f62b801822f8768f9a6005358e45185cbe9d834af35c6586e71dcfccccbc41013ee4c20801185bbe5d83ba049377d30d91208011039824bedda61e31e110c382307e07e52553c58894181410a066c5eceb459923199c10030325a316df7034c4cccccc021b707454e9d2e500bf68c1b7e3e886f44a091d54dd1019c400df5f1ce1851adf7ec306062ee8020594266364b02109a1ca1d51bb19bc20a28b8d216a74e71139d58d1a0978765a9d9878f69517373c3b8db3b0454a14348a7a3003a906249868aa1702ac145d28f1ec3360f4208513615c2082c916424a34916353365e7543002dba00e2d9ebca419d15283d7bc783435217647878010620a6a0e1680633ddddcd71e16509f0610f0f9acc5081142bf0e1d9b7e9cab36f3a64b4b00230a06c21850c5a3d43953456dddd1a93cd0d17ab0f7fa8400b325b7cd9c20c2e52f0ec3a4d4268f1c3b3673ccccc5fd4303333dd2d4cdf8e3133b3ea9999992f1e26caec6384166518910652143d74d1e296e199999919480a3ee4be553fb25062ba218ba1efd5561c1324753154df408bed0a143cbb64a7363c6361f4ec9e859367671067893138a0c26c962e48cf5d78f9e1d957343cbb835fd22840944aa9a604f6c35386a5860b0827ba1434a1302b9a7c90ad22a4cb8a313c744b0446b3cb0d4d58b502424a0b0823c6d90f69edb2c3502b88a52e37b47e301a434a556714c160c2e90926a27a4081132b2800c28a151044a86a64196c1a75220eecb2a5c438582ea0821a2ac98aa1242b86428921c13c760d900c7ed1483be6d243d2547e3645a24b27125dd22f6c47c1e7314f7a7bdb0c720e5289c82d77f51bea5c47c02360af560b729291267712a00fb1586c4104831b96420003171d4485c9169b3e54dd80f0210f2a90602ab078ae2289e7a56d8a122f5cf5ca40981dc4703203142f5ad0ba6248e2b26549cac28a5361f43d74aa8ac76f12154153a4f1617549c6360514ab07a5d4318a61d431ea48302aa9378d74a32f8314c324b738dc5ce4dc952ba594f3baae29a1dcb071c718a58cbb4b7b803cfc866319f905b556963eac7c70f0c1cb0c45148da004472d95184b528c2fd7755d57d21446244d01c307e03789cb174d644b4d66bb6951ce6e488c9e7d51521a23a591b2804d4abd18a5f4cb9b31d218e3754d193fc6de29e594bdc5469915a30e8f8398d3dd997526d6ebab110c2beecae5dd9d5f7b4eb7676eec8dbb1b7dc60ffc9c92a76cbbbbbbbb71f7851a77bb9b36c61877e76ff801ef393f2e4b32e8fbd9fcfc267637eeee6edcdd7da269127597ceec16661f29834eeceec4664fd991e01366925de0aba38c335e916758866d65e571f4d520666420cc1cd78ca55f987df4db39a992377271d6657bfb4ed9e204296a1da38ca1b237c7d8fd3127d5f59622f1e3c39ef9589669ee9365cec2afd3eb9a8e7d1b7d649701a28fcbb199c2efcc7c7a88c1a904cbbca77e797345b9da3c5e7ef3862d82bee265e61f0c367fab0383242a25956a83ac87d2a3947e451f41e4e49f46e2239932fad8287b7ad9e62cdd1c2f7a38321cd74e3d62f2e9271dfbb6a737ca97524ac961a40c933c974695517292ce9944f558c47aa4055199bd9d32fbfc912b25c73278c5cbdc2ddb65f7b52c76297b0269414c6666665ece82030e98c5c5189d97be05398ee36c9047947730a10662266649c78ec87c216644312962e81762458c71133333b3cf23325288524822109ab2c510a151d40961489c261499851921ac8a2fecfd4230b69471621c608fa0ba4969fc7cb882fa90ae6ec01e36472edf2f817f5a99d7dec1fc72ea3169c2d3ba1c88b6b2eca37ef5090710e3f71bc246ba0a8bd4eac0e51dc0244b36bb4b9736fb1dc15c7edb38b30a4789f9283034d44d1ab39434e6e7d9f7c728edc4960c969efda604fe69613d04f65d2ea42f6fee22e9e1971f0afdeb377e3b231dfc7e437440b264735fbcbb65cab4ae214268b5cba6cd977779097f97291376778f912d53e6777bf8f7578b11d1f7f40ae09ab2fb8b29d4376e8bdce64dd9b8dc376934c0516fc9e2ccd91e8bd097ad8b9b7239f59614913fdb8571d4b37940e79c9bd07d9c9bb0375254251b95f93ebd4fcba7c5be794c36af4cb49b2b55c9467d4ffff46ded7a707958e3027cbe5b9bb72dcd0b57cf535e9d91036c584bbfb8d949a79b370505be7a76b7ec52d987023f4629365510059b8e794ba6cb12b22f2e8e2c42bd2952ca28a5c43c1380a628e179243dfdd4c1f76965be4d26993799a284e77bda83e94cc4e759b2f0d2c88dbf7cc3b8d839a063d7535060acbbe94cd10f056621fa602c49c02b9f48f9e94df9a28cb98fc831d89d03549073859746a62b3d21fa769fef8e88cfcbae018c51cc378a518f31c6103dc65864cef82db97c4c2ed2df2e250a7cc54e613e5d62599fdbc64cafbee4d579d7cdcb27cbb6ac2553e7cffcea86b06f9b4bce85f0b6b1b07da1cf6bceac6531f3d96210839237cf3ccdf7af2bc964e6c974a98b9c185d98bb58e08a2b78f264e6e6e666962cb999254b6e66c9929b59b264ee3002d1299ec8752f73ce39e79c73ce39e79c73ce39e79c73ce39e79c73ce39a95c27622afd8c9a4abb249bb68d73ce39efee6eda4dd9d37cbda65a6354d3b6204debac334a31acbbb56e8db6532fe33009a77a29bf30022d985de262d7c4fc854956425c768a08c4befcf14e31cb78f172c5153116c156ac7e9993acc4fc7a64301460ffc234c2fed0d9c00534fd650e7bb06fe7fd98ad28e2a76f22f974f1133f79b4a23d4975f40b9370f189b6bdf6755a374413c25a8a26f5e4c9f684e3aa4b4d4b79044aa568b7d14ca7dd179f601d912ee5f1490cb29fc2ba21a92d729d57196c1583ab19aff3946f5737242584535efc2e05cedbdfb2f904e84990952f6a0c0a0aea99786d4197c8054d18b755ef24d1bb30629d03b68f5facb246d568ea863aa17c214cb9ed8a741ff57cc6354fd2788c34a9ea9cdbb0517da3f9c2787546b4af5facd211a98ef9b6b1caec86c42d729587075e7ead51a5b3a0f49a0be92ed409e5cf68e52856893e58b2a8c420474f8f79d18b1a8fbed02776462b59dbc91973c6e8e40c39844ee496bb37abe5a8aa554a19394a65635c3b0f97bb7465f3eec64c6691b4b1c476ca66d6819999a5d08298524a2977836e0e9e3b76939e7377775bd8a44f1923effc760a29e5eeeeb619bcccccdcccccacc52665738c5d3672749ab3a5f00805c7d1059b7415e3b06ffcea1645070c66896b851432456810891162fe61244060c5cfe64811430424bfcc887b85262030426230323ff3c39638e0f6e69fcb8526116c3dbde3ee789391598811708cd31db10b8c1aaec0e9ac80892d9021744e219897bba45c975e92cb282e2bed98572a3be7ec5ced18246d3647f2dd40516d8a68deeef3d10bb99ea79b47334e722b198735662cb244c752c3aa39e6600f9e1b902b3fcd3929a5d4fb72e9f3d2eb997f7d57f4d172bbdec8916333b1c5dfd5e6f21732d3f317b2d3f374a276ebf40ee7f2552becd2bc9b5d90bd71a4cb0df354abe9f3ec71e6f9fce5f5cca79f90c6245d90a5945386cd03d3a8e3ee76fb6e77f7eea7c445e90c7b9acedd5dba12cc6297a45e1881e293e863fae552fa3c989dbb2be76c597dd8284fb7941e992b067db4727dd7855dd775b1cf39e575496f4e223a2738187897b4e212b708f1257eac9d030d548dbe34bb1dbbc3e8ded9ed2f94dedef63aaf0759ecec48b9debed95cee3101a22dea99631e106d61bed433af094f0b9b36d77704f3fd126a8b5ed7fc86a02ea4298d13ce6479e5178a26803efa364333469dbf503411b4a018ca60099c233302bc44a40d8a8cfc4d3a52e32362d39068c688e1a36f6ad38298712ba3976988dbdcd404068c98ee5e6a2762af1131c6488233628c118933aa8811887562d0d9f9c13a35e87c69a2ae7299e1a48b326078322e32c63cf79b440619ec658b930d8aeeee8dded77657237c90e10518621ce28c187138e32746252ac7bdaa2a190c59f8175620449b8a9a8c9a58908d2180674fc92691b1e587802184964d0430fad5d6b6c4c5d26ef8e300cfde269131f4db2da0fefcfcfceec536d7b5ba69baf1d347288177544f68c1e527e5f979196961c6878b24f4c416229891270e3f873e5415e95d2729c5b63c361bcf9c7ad98d534a5d082f7779c6cd289bcea5ab7ce238a27265aabf128e11c860f238bf4962983e859ede47bf111d47dc1c137a876fdfc091e3068e1c33335e5353934acdccf826a46b3c1a9ff1aaebf036ea8833333a6e7821cd513d1078a206de8d57a7541beadb383b3efa1d31a171a653a071a6cd1706344fdf1e7a45443a78fe36252b35ba57a4460d699e689e9825c5d040312b8a5992868c8d1f1b406b238859546888595098456d14d98862a30866d9c8cf337a8f0a83ed15e145f1a478450cb6cf7444b017c00d951a5f4a4813d01dcd130f35cc01a514d23c7d6f1a54e328318bb5a88f4adfdbe615056049011b7c0c41c8e7e89153001f3a0418000ed88107063880007e2080b5c313001e1f000048101012f0292004217f4d9935c7d536ae4be9c8c19e03ea3b07540e34c87c7b8e2866adbe5d47918e2278872594830cb3564b1334be3d1d4142df397ca883e81b49cb0b22f85047946f1d3fdf7e4de915795198d53e7c0b60e9064723acf0322773dddd577477fdc21b249899c36e2634a7e70e76dc24b149f9e81a5293605937160462593bae67d78a34a3dec15ce9c68ea3e72a38c7b3efe889bf2131185af9e8461f1d939f8d0430bf3c0214f3cbe9a73d7df44f73fae81a53efac807d3625f8e5d8a731354ef4b4a5c69973cea21965fbddb9f901e28b36ad4ca4a934bf4cd91a13b3426de99b98d550b425a1deb1290153c12fdf16e63eade8db99807d9bd0b73929b12d8f9b5f6c1a2d101cbf3e8fae9f05c2c4c9eed059c3efdc0188d7f9ecd3903647cbb2382da503203e9c473b5cb3869f4ce6d1918d13955545aaa879340375798d940d4915757d610d09c6f870ab91b239ed335115a926a98a16c82a7bda9c76a3233903c5ac700b67e6110e2066853a3368cca369e5872a4a45867762eba87d43daf133c3d89647d801d4f7f56944dc75d8c530cbf2e10c94eff6f0464acbb74b4f47d4e614f14be487379466a0dc5062b0ddc689ca1ede50baf9ed13da9c769b2c35fc9ebe574c1d102d90586334bf27a6ef0e88767851d9430dbe270d9e36305a20dcb76f40e5138a3546cc0a575fbe2866853546cfc4acf07bd2008a59e18aa986caf7f489593569e0a40198ef4903a76f5f7d997f436983ef00aa8c6d79d834a9458dd318510d69ec0102b322576f1cbca28b40c2d29b22176a38b9e78b2f7addfc50c35954847483430d67d154e272396bbaa4328f988594c42c5e2213160887e3dbeb0456bf432562563883269110b3426a44b91bdf3e8ba2349d41f39790e0ba0502816d4da41aeabce6f435d7d134cd7f30a8f1e8302879b8aa569ba649d7b4ef4797aaf399c772d35c6e13cca49bcf6b5e485f0b356c4ea72dd9bb27b430014a42280929fb8100d4a45b2e60ad291bf80172a165841617a044bf9cfe5eecb1bbfaf2ab277f48f892d9513f92fe1863e434e42f33333737c3103d885f97b515f04208bc30e124071310d1ea1e90c0b2310d400b3e3cabf1ec71a505045f3c6f99917288ab3bfa45549b6e3b43d517110bea616035b03a0b44821e6c109e1eae0c966210fe3c581fde1e5e5d7c73341a7e9870941b28aa212e6881c4cd895d46cb1828818c139592acf050d129e28ff44c8114137ae7f276cc318fa7c12b0b9d621753ec9a58bd3e6f1c3235dca7f05aba7e2ea08be85aba98c00e7d9c26b350b81e495d01ec8e23f9c418427d1b77e3c6dd2bce3955577bf494741929d74fe09dda727f7e886214ba225064c260139b65b37c96678fb2bb63645ea46f10d4d1e1e18948989979126dcdedc68db55d994d6666c97c5dd8dca572b7bb05979b99b9b5dd652fa4bb5c489fdbb4ac4b652ea0204a149542892ee0018718aa8d5f2d3cb840c69830671c51b55f2d3b88e1d405af51e9af961db2602715fbd5b2c3d20e4a444cd28a186a7cf1a2060bb8a861c61435f59bb4460b57eaf69bb48650d31a3d3ca843cb5f2d39cc400cd531a2e12886238aa4d0a1c80b273f2db0e19390a22588cc0daa959153e32ca5018747c2498b0c5166f82396861069d1e1474b0e6574ca88d1a20313967dc08601817dc0b5c27101948c521abaa2b06447605a506901a2cc4a0b1062fc8d5f2d407cf1a24cd36fd21a509f82dfa43582f82a8da05efa6a4102e939b0fa30859f9f9fef597dc8fd76bfedb193987cccb16fbfab3f24edfc5b847f1ef187a4bd1f8994acc568e937505f09071f3d74293eaf9e2d9b07120c615c0fd62981c64e1aada2a89b65e60b4177f979b03bd2a93d10188c4e5de7d439751eae36a773212c8d7a87417766bf564a4cba9c73767727659cccbe507ed77599fb7429df244d492a697f8ace7cb6c5ce48ea3b2f32f39d8a3f5c81bf487fcafbe36ffdd1ebba6f199c538300ac3c13621cf62cfb4c665e16829a39fd56d34267faae738e776e78e7aad90de9a07ecba86cb323225f765fe8f39d77d7f9d50de9d488f2d187dfa419a0f1b3ebbe95cb51a6420f7c9675528e8eaacb94cba3974e33ef6490f28ce83397633297693c7d7de99b065cb7e9e06a8c2a8e1b3974dca0a99587f47aacb81a23ea3ee65b6a46c94cca3b1adf979e793399a7ba22326b0e2911b39050afcece420bc4860391ae64867a36bcf8335d03a4d7ea524aaf2e846d78fb1f8d573d24d4488a7ef2db7670639ef350c5503c1e621c46a34666079339bb1ca36bc00ee95d369f53d715e93cf34d7a2bef26bbbc03210ab34229a5300b89f4ccb9eb8cc41048f95403529e79273bcfa451e3a01a74e6bdb32d5a57bc399911553ae6947ae7586764e6af1980f90df39bc4d518d5303e929ef8483aef1cf3cde36a8ea8988735aef67d33a562b0e3e654b8c2d34f7a1736550c76be711c9793a9ee5b0633a690858e7c48bf49c35e85ce18a7bf09a14ba824ce562539090dcd0800009000e314000018100a09840281382c1ed465c17c14800c778c3e7a583498c7b2188761100541c8186388318618600891191a9a220400ec21f0b2476929381f6babed1c109f0adbffc4101366501c27f194756892c6691ff69d606ca20d2dc2389aca3542b0338b61420e859fdd42a4f08609ff460341b4c6c38e324d3f0d428912ecef54fff7c852aaef7fe841a7031d31296af7feabcd23411ccad62e334128e3dfeee18cba02318469c8875e75e57371cf7bc16eb83fbd1be0f8633306758c087667b1101546fa0c4a87e50de3670f71f06a8f6fc9e13f9185b5c8625732e4afd823af0355135f79868588fd7c5671b3f79b4a28e932b42ab1de71e15582addd077f845f6317a023bbbe22d3bccf95387e48d52fdb10fb133275011ea85e684486267c3d319649e48b8880285e27e788b271255c58b362f0252e55059fd429217963abf9ab01287b338634668d3eec5e613eb3795f99b7b859629cd99dc66057b1dd8512af01c68e41dd2e4692ee23e88a109e7bdc04f208574a5efa0c0b9bf80094145c2108f6c1eedac45d8546f18c31b0c13c0f3effb364c7753eb007e60e63d9a6b87557500949537050cbe3e3140087f7d5e03143ea899278d76529b1f01a4f26564a101f82518e83d0f233826ef96bc61488c4ad93e81d25f7f7432866d337366b6e38f3aac4bd1ab7c3d2702e946ee4765e56169f6596baae0dec988025a1f7ffea9313002df29ac6c6896f53922722bb686c9c0b7e67091e07593d9a06280b7d411999e4892c6a4d00b2058801cf2d6f3117cd80453052c8b264711940ec088e5dc562cbebd00ba68e57139db50f309b6f08fc2507395e093e382c298aa41a0eb6930b1e3b55fff87486b92877088834334d3e26229e85d0018da9dafbe17872c44881ad970d302204f433c29757ecf4d8813229910018ef4223c573e62ebc28b4d01d2cea843df87ae07c1f82f34e73c2fbf0b4bea72d1c30784f041c6e06cf5ed61a8bbf6e81a79dea865be74b717be95fd9833e23245da1ed4c8b02d1087381d4a510d8f79799aeae9e51f2405d8db61ea70faaac4dcf287c7677e95146442590ee9f9acfa8432a0f71042ff0bbbdb3382f8a8e99e726d6023c4e6c398d7381d78f1a25e82672517e49eef4a95979d372de5cf9572554f56c1b2fdebd6bc28e4698022db3258a120035eabc728041a396406070031192c063404fb03c88b32dc8a14a672f63cca8bb0f1ed4c68570b6e587bd3c0c08ca081166336c85163c09826d1faac111b587ec5d184cf81c364775a915a7a5da96559750fd0bd71770c28184faa26b4789b84d0082cc16be2a0e81173b8702b5e0f739eb2f35cd26ae838438dbc3c4f55a1293aab378cb590c5bb7b6402ddd3dd096039d0fd47cbfd6bfd7b12936b5835d9f6606c3752f0c0e9e47d71cc8f15cc60588957b709e8981944f27368471ae56a796ce07173c008eb50788ec43e9c3dcc8da83bdb025c7adb2b714276069a2468be34f01b49988adbaefde025f392c8136338e2490bb8dd2300a5c1899357285ed4243b0bd6381086e4e1241cd833a74fdd36eedbbf056c1ef75c38e0cbd3ac0a3501e1e64f82f316d1ca8bf1f145c6fce7b44c14f7f100ff5338edcc0c6bb765cf35e75e34202b0f15e6aa943efa000ada30b4e45576f22e5dba58fbda7f9e94491fd8b71a36c8e76cd23e8a15c8b3f716c8a3aba6d1af6c8bc08250389e07ba6aac10162e888f1b994b57fb87f97a250fcce0d8f2dc303fbda70c54f509450f767aaed0d44703355aaba20d94cdbf06ba347528578a37b26d5a4b4a4d8e6844c19491ffd9837662198963746fd68407a467aab76b36cfe1fb1a88dbe9d85cd07c90df5004d12ac6d3b354430e62d2a575f8409235b60b857d7ab25ef653057aa5bc3a9b9a642978993da99d37109d44e114a20800f0b9d8ff2f9374f32a1e0f85391cce6d03bc46fb822c30758c33dae4b95f007d99c6baccbc7a4668c9d3b20d838489e6056682ac01c23db3c585b0ad4fe8c1af76bcb8edb74684a25f16a4298a96a9b8a2f4ca37f31a17ba5c322a9c992cc802d85bc719d526c45638c22b98c08260f9dbd31c4623d0688216795a34b4351db03c7b8754ec0683454251d830fa70ac23ed0adf17979b37ca85a13069ea3f6495d11cd20f2a72d47a2c4a6aaa7926aa31db48cc862a1c7403d77f5f16332aafad1a1e6a37f1cf456c3fd26f23b29755681b7a7d4e24f1124a1445284da7f88047ce46852fc06e8b4da8d1201c194c80e23d5d0a7add95a626840059343a8b156f035c44858c1f0ec1b5ca5915b9165e9ec0add8e456579627e2fa4807369807e8dc4b00a46c522e222e40a6d6d01e3e324d598e2d0068a5ca38e0435ba33a7fd767719d41b623a516e17ffcf558b7f051513d6370c498fc70b2bd22017af2b50ee27f42d16b0e3f1a8db201522804365f49643233af101903f573b927ed207cd4295fcb7f9f85a2972c6fa9cb97f870880efcfb9b9e02fd40e3a000b03d914d9ab77f9118a26e8db18aabe98f3e5f2a3d72241892e76f49f110c1627e71a637c401150848fc832f6216b0fa84a3f96291029829a03e95f95a4090f23a8fa3154acd8ef0b074521fe2cdf72a317e9411be09d358f662da682d0e16a6e58919d560b8471ff27061528ff74a47dc3f9000890d5f46e928d2afeb59587a3280cf52be721ab5e0aafd3e27b7abfb9034878c07c166d06f049a1c1e0498c0be8681675e0502b25f4619af9887e32805ac721d950228eff9507beda6a4b8c783bb96471ff251aaf74992a4d8f9bd2bc048206a542cde9b1d502c3f7cb0713b7086e6fa4337a4dbe25dcc79b5e8e77d683f75ecc1e893aa91e091818a9f714ecf5d1a3dc168395fad560cc8822edb39f420363b644b027e94d90f771cb96c520cba45c8f71453038260f6260637b0809ce69674cb45cd9632f7ccb3b9b0128907b41d375c7cf74114859984668caf12e2eb67563de1c8ac28b201966c29d171e96167407bbc2175705aafacac2e05f5cb87c5b0aa0ea99928d9918c99862a45a01575bba30731b7c9fb2d1b1de650623225f722d5ae543794c47310c20f77164864b3dad82192ef3fccbb5f59e955c78cc3016d2c5e242a172a91909995938b70e79021410cab93497833aaabfed25e03f91c91fb3c8c12daf8877c464d1679ca5478bcd5315d481b05ea31fdab32f58d19c711b6cb17e7a1e2922f5010d2a43c4844a463c1839a6deead13a8826877ec4f0507a2003c53ea0194eb2a4a9d8ac46c459e4756ce1d3b23f064ca2871263540310136d641dd8ca4520891c1713104f896a111b85ded1539c2b65a98d93cd8fd119f44a8b71946cd8944d4a1052e2404ebf6fc4009148a371fbdd007453f0503ad6daf6cade826cdf8f3b2dbb75f8068614818dc949f97c85a3ae3ffb0c2ca3197350805cb552ce6ff6e81db1602a5d056c81bd215881fd5a6bcd7c59b978ee8133929d27438d3f5c38f5494ab6106a71276d5f3c5262b015e7e796c65877f2a3a18646e9bf8908f774b03af5711c27f9272e9cfa14cb282c0ce8682b7bea3e027e464c25700270d5b7561efbd594b9eba7a4bc6b3004fd99ed94dc56d0d36f9c56cba8f458f4d63f54268a793d9e82a900e083a4f95effdc6b78a8255c384f54c35d92286b2bd3ecbd8ae2c2e8956a4627a00c0c2060049837a3e8816805e68d2aedc01fac0154e5373c9a83225b21be5452a69efd06889614a427f1c27f5ecb70db79aabdc2efc885214005fdfb413a6db8a90c72ae1c126c4ca8f91ee00e9430acb97b400709c21e0f1636ba032bf09db720fdabf4ca4d3195c54a301e961cb3975d8cd4708cfca570a97d30f2858a59160e23bf8c255fee8b3a4b29b70a16f29a2c01fe0b97b0804b061cd3a655211f9de06f328699d72796e5bf940f813339ae2c7eaea859c47e920c7d7f5e5f4ce644d56f2a5c86a7d3e73772921b422f91c9c64ce8543f6e85d09c9c98ddf8297ed892ccd5c9847ac26398f0e0317b6c39b94f37ea2a347f987226d29fdc3ea0f76f87f095994773fae0261e55af56207d346dd8c3ab3a0dfe90459558cbbff8f86b5ceb076dd6a8ff69b31948aa8122e2d8daeab2fbf8f4ff3ecdb1e70e8b2243dde82d1a5210a3809d799eba23d5637bdb793b4550f53c905221e8d442e68e34281419b5dbfe246d21487cfd971b3fd0b33936d575d5ed3d7b1787dacabe7d9bd91801009e96542687a5a3a31f95f664f795a9126bd42b1630d1487608433f7aeedb26e84418929bad2a75bb6a0d0af4204ffb5f671d0dfb48a1c959a71371f433bb2c79977b65557a70f17450fc82e0e9cc21e5d99c35b1c53ed200208c7770468e52c1a6aeb4ca3063e270a8dd2d161a404a873f201e46d4900c60d37a9cff1aca9ec91e98708ea5fd6d950f611d7e486c9df10cd20176459511efb90fabadcb3cf481920e704589ab4887df46d2811afb0bce78c24200b8ae02009a21ada81dc3400b27655ae3cffab904b45d78f93bbabd14a9f13ff741a0aa26ed4549d6fce55319b5bece9fcac7a91e37fc722262a1c065086f053d207af47b87cb79e068b000e8d9515ed13c32af4ee46485686a48bfdc1f5405b131c42d70628bb46259aa8faec3629fe3df969cc16801d635e82fd15c6f9a764765449755144a16e974013cda765f2592326dffa2387e19765a41f9d3e52233c9886c5b2f3bca933b2ab3af151450a3946a5524d586850ce838b743f8c3a245e78d37e44f71f901dcc79e300ec3d07c34b0f5c9930d7105d5d49282f03333ae5da1efc77ffd118a56cb1ded80b2555a2f3b08b67cd5abd7e83dda1c6c026c7bff21ee2c0f9807e7ede8ff3f910a18e920cca0c0c651f7142f904e260d3b321d41d74c0a781a5dd0400da40350c3c04bdb8b32305ddc23eab5a46b42432c2f025bd1739e1f2aa9cc9e2dcd700ec3a72ec469b6ce4d58e8a46cf60a4dad66576e288d3f9c38a0fc7a31c1143c64664a8efc98e22dcb2c2640e89defbd3c3c3b9e08b88828d9aa5fa6529bfa72a1b30fcca40294c96f0b75a4651a043a67ef429b302658c9f35739a4b817896a22dc50a9247f25525ba0651eaff47dbcc53ed8d4c22654e79d2879739d544a9de42f7a2243922b26b25cefe15038bc630619901925140461209655da6cf7cbe30e5926be3ea9af7ade7398abb00e613b61792547dba9a1999030f25601f1a51517fd3bba0e71ef1d90fc0380a428ec71f7a35305ff014992f8a1506427553bc1c8a4b567465bf22f9f6f7aa3228ef0f634ef9a4df963863390cd840ee38f9c078dbdaf7e3be5b83d0b793e527ed2e5dbc5b01a1f6a81cb1b8709cc17bd7cf9014100437894fdabb1e1d5aa297f50ddf10325548f9e1f5b55d0359bce92ffd2bd43e77089f82ce49c8b6603c713338b73fb7de0e65c58b0e2dc17b7ca40e8ff217cc00b9fa3997e282f954d431566a24246cc5e1b50bddd0661d27f129de6987c37701ba3aecf0c3845b95fbda99c11a499c117114b18a0efc899fb6e3b733cea608d39a9254499988f65964819af13fc9483778f9c78f365924d26d8f28597cd25bb36cb802a581aa54299e82e96c449a9f7984bfa25131db290d13a2b4cad8377c14d2aadb5915d37e49f8d3630d3242676deee09326b8828170c6641206840eb045e142e5fccdfd016498c19307b1452a4b9f6d01a4905b97e780dab9bf62af452da64187caa7dfe498c7088d37f4a40fbbf617e16125136f0c8bc4cd897b1c5470d5addfc6aa04a21883f0f62d66c71220614d08f463fd7e4e5329e628aad2e420e9fc0021a20b1721b14d4ae714f825a17d4d16250e5fe052dd4bd57156b23569b0977cb5d243936a6e6bb6678d7af8a42a8641704c8529c457a700135b2f31794e29753c3e5059ae5586d6c9744c9948a28bd92d51cdc3634131e41ff0392d62b1e55cb91f2ad7b56f0ce2e697060e92a2b9041a6bb983b37ef12c725baabc5a1579d1939f7d9be97a723f12b5713faf77cd05d480fa7d1312ff9ee14ec8d9dbf0a6291dfb33dd6b2ecd44dfc740597c1348e351e9533f2c4116a2a8390c384b83c9eaf22ab3de56e5dc6df606f570458d8698acd12418e4c03d4e5825d58cf0f73380b19873c011e62e9ace8b26155ac1e30d95d546232f1a6f3f2d5c6497003390f7701f220d2c031940f6565df8c65004b99c5fad399acd974e2fe58723a014121ea5106d2c49eea1ceca7df369b1fcd7951cb8fb2c509462d8a6f092e20dc6b767809c2b384972acbd866c020fe598380722a5c261210ccdd6611651726afe65ad7d45519da1251d0dc5fd3cf3b84e02e8b7ea5a81be3782c8244530a14bc4e884bf8a83f64251d036f6729fd3d04508a0d2bafc9002d724dc8e7d01b9992edf295db999e1f54bf08dc443228319032ace733db909db508a001db8670bd3062d4ed133250a373f6df40193f3ce1d95a1177b384dd6e34a610654a82fe549b428eb28ca1d3f9e2a4e039cd1c3e011347f4248cfaa4a19e60b9809c6a462f027c8c86aa00c64796cef8e8d105c7b7d228ad8bb97792c8d9d98902f6b4b3530218585cfbc4b0b618b7fe6857b29bb3706af40ba7b773c411374f00e92d33635c9eada1c54c08b1b8f683add6ea7214dacf51865bbf6f749263fb8afa7630631a0f853a864710accb27401411d0993a16a77c2d8a96577446656fd1b81c4131dbf62092b080471b9d999df5fb16e8add1c79044400adc1f37245d84811acd3c3e01803e388d81c3d5f05b532596d6fdb63c2683161ae320cb768a69d896ce499b460caa694e38b3e7c0950f8c3d748ffc2004aa581195641c987a082b47815b2411d3dc8865d4adc4f20d10716e2087124afdf963b62428294adabcf1e42a6c5617c2bc614785060f6dd14e7c430d105085830c4ab71a129fe022b85c39a222e3a023037b422e2621ff19a018f8288c72f4c29b93f1b464f5e62bb2ce2ad68ecdd7159a9150474324c5da871e99c3502f03456052ff7fe643e56997f45b8aa82e74c1b8b997dbbdf8348ccff40a57b658c2e970709b1c3202786b58f9fdb8d4fd12c4396941740c21c0d55e59d5be047ebb4db8a75ba843a77ef7bcc686ca000fe9eda4fbd30e20aefb4b4640b2e723d43e717059883dc575704d2506fb2aef71b8434783254176881b9ba837ca1ccef25a4623e1eae716fb7fb408d8fab4a172352281298d659818c804fd523cec3a2460149cae9e827883dec6ca0bc85d419f6281a0261b3fa3c9562871c719543ec6ade090a0f00e51526f871508dfcc7cc9b2ff61fcd934c77a811b936914a51b4b51be90b33007c9efbe9b137899428180cfde8543a60f395b80797bbca7bfcc341ed48023a8881aa5ef2474a3c0257b7eb18a544318bbb2563530c466301fe6797251d8ec893b4eb8adb7ed3eb2a27d5ff2344567035e0998204bc03747c5c131ce0428822fb16e85fd311ce8b223522449e57cc2762def0af2a01f24ad24b1f551212e2a3ea79efb1dbf06fda61d3438ed9a854b00dd1e568395452aecc8e50bbde08bc93c720e634a69e1782470196fb6560517873675f17fcb9a0e31e028005bfa5404e9ce20c79dae709ab138d62443e03f2a5f9c4bd4c4858b4ecee1ab953789ca671ba4fc263823ba1a237a6356f8bf5d9f13a71840061f7b0c34fac39bff2ac380666551297e03461b812615ac03c911bc8ca24ccac0f0acb3b50e62b7c242092b7fbf1ec29c9694e40517ea7fce63a1d70009b3fff2c303192e570010c1031baf2b0184118554a45e944b1db01bd9c78671b0a1c4e0135d4025d2a92b036689eecc268ffa2b64946499937f85250da23af8291e39a3ba152a117147f8930657d263667d4f5b8db82a1fc5245f259ed35ecfd5b31225c37e16ffa1decac10e9419cdf16f6636f167609510f78af48cb2416cda2a9f01778139fba4b4745b677650030bb473d9da5e74e8d31f849a07ccad9f92fc31831035ad931bea88198c827d046c67adbe0f9d3b8c39806c57e4b093e015008e54d072e13b9a7acd8da340b2fdb0b56445010b49e68a40fd77b338ab03999e8e354b3545225a19ed358277b8b79f04ea1cfd66fee64a0509897abc2a74875f2013b5c652e44cf9eed9d0e2bc3082392ddb6b77b49b67dd4c03ad226b27eb95742478479245b6b3057ef5371ce1a00e7aa720a8a09b41cf6d372782737092d2c9f9e26aa1c49979d46acbbe1c04f5f238d9874622664ef717fe335ce385c650c69d83d3f281bfa37ee070db5ac0530f9cb6b1703b3535d4b670880ac93ba34513c836eca40a8624cf214117497f6f6df02db7fcb0892e0fe4d83181f87dcc2e28e5b4a9334209683fbce6b0b2d30cce0bbc8ba5e2283f090c4f29de7caf8a7f764bb0ad6c9025d44023066449f31418ee532d28733377b49cf3409700cfe77c196283b1d346dc6c5ba641d3a0b23985d051c987094176b5b9c5b77e1cc064ca4ebae0e45ff1ba8d7487a35f0949de9bbbcca33a19a962f67c208c10b93ce7c980db7a9956826e3763c8e24140d19e90d6775158065bb3ead3d6f7cd4e37eca9b3d86544bc9149a711fef6af791162d338afa1f6ac8dc9de2d7931b976cb777c5c7f388a5acb242fd14e59fb446dec2bcfc1bdc4940d92b572d5fd626c77047a67796a6f3a38c56676efff87691f9eb269c9dc2f4e4a7ebf6eb76d2e5ec5bfd76955cc997b20930f59ea5911b9832a60e97b2760d1fb462c39382b9dd5d6a1394f525bcb5a90722599b279c92b8d4a672da581c033f54094c913210ed2a316606e549bc732a4f20afda991ae7b3b47b62d34d76cceb1db42fe5274752de9be8984a0292971af54fa64ef93db0b3892336d947b5a0e2b1f42208e32f238bd18a5c7996f91265680f4d32546eb6937477ad5090a0e000353c1326935feb0972c3b9fe077218bd77269ab35a90fee35a2be321674c3b3b9e05e28794980349716120f36b3712b05047b427b80edede5a6b52029e43fb8286cb51afe579b58b6d198a5861a3e92d6040455e96eec49ab796b3a6836db28eb08097b7600a503cae61bf7adc948b1f10df2088826ca3531a812265c740d69afc7ed3348bd748b4a0905ce7d324d0d54fe4e28670f220a011b3a879875398a34fe5ed1507c903b96902d963da06937573ba913717ac0ea1a208ed42c0cefe9bf21bc9b6845ed6e9b33498190fbedd482ac4c54cd969c29b83e02234b322916fcaaa9455b6dab5bfe66717385940015afa7946c81e017672ea0c5278fba6acce38eadd6c148f92cf1b88597d15cc6e8fff57ffebb3367e260392982487b0b33c28e94cf43d19bea46d17767f1df6534dfb7cbd538fc60eee00f72dc5559e0b794378a913f232861252cb2e0d8f709a378cb4fe68cf49e8e33cac290594e94980f7dd88f617966080bba0e512d6efe51529fa5d3478622378a7dce5ee191cdc98248bd5c3c8677f01b1184c60d6068f6a55e100e5905390b7f60d7da798703702485d73d35136050831378b139c4dca2907532cab4799f0bd28b0e574619783fd4d79b81491c82b30d11cfd1eb7003a5d74a2679683fd3f7e34b3eaf0461d0029fe78a2f1e7e68a291fd46cc3959cae55cfe6d5e20008deeadef5f62e0e0611fb0dd92a5e56db19e6d23a52c43c78313e7705add1301e6e7ec9120e1280fc55eaea3d06b30c8feb8f309554aa7d879035592d7a0b4e0337bec76fc103e562bb376cab3b0abf677107e4edbbf3e021be21f739ead44800169a9d27bc5303be1fe104d9f6672db502c830ef329b13fa193990efdb339142a2f0038ff487a98c4f21f8bcb83a42b60c616d58751006d2e8a82a2813f3a7c25555d5005fb0310267b2506b34476a37a50748f8663ddfb981f92af6561b132932f1113daf35e117b5855985936af2bf54d5b05a57b27d9c26791fce8d3aa7a5270822dbeedadf028aa5d8842058b32f95e44ad2dc24500684c756f6e48575a7e1890d8914c34ee4e61bd540cedc60257b4d41967cfe0084ad09aba546b33a206f4502a06a40d0e949bcef9a8bc809ad026ba18d73f729ef6adb16685b35822d5f60e29760f067285bae908aac48ddc811c2050106d866f57890d03dd5e68c69860a99579e8b7393f1a14bc6a192892ea2c2e176e8faa12f47fa9c91931eb9ab19555f7b480b75a6579150a3e5425dd2e7974d3b2b6a0020a2ddeba36e31789c5ae88cc2821816e4b4679d33fa21b24f7634efc7fab4b21728af7b595df86a53082b06d5cf2ab89317bf68a289f9f225aec93eb55eea38c3311642bae68957b59b572218205a12ee61aeecdaebcf41b3d5bf3ee4a9307d68e19f6c9c2fc92dce38a1ada4b98e7fd1a77aee9000f3360820660cfeb08cb2d581facd0ccc10285e47d0968ffd8cd6847abfcf11e9cede882b47dc1aa759bf2d4222aa5b5967a5e2a27f5401d963f722a09f887e41ca51d21525bbda2b7cb8e522b6499b462c3af1cdb8a4d96e005a6750b071e8e5436cc74db2d30606d40dff8a6192e029bebcbe0ff90eeb86e72a6396515f986d60556c9bb94692f1dd24a3d3cfb4524ecaa5b84c9ddbb4cd289bc88add8359ec81850736a21014d0820349b1d858d71038adc9ceaeaefa5806f912336323ee023407cc4d89568c7893ce36afe89a2f29feccaa7c73bd97d036450aa27ea3784d225b765ad957ca917af0ad4fa533ddc08e0a4821ff1a7740d5af256b58fe8c051b01d8d24bdae5eab315d3d8670201d161f3c4fe66443b70e9a76be198b8982b23000a84206306721675ac2f60915aaf51658c5faa209a5ae847931b274a420bc9443b00c84e816cd89cc50ba6e67a338bc49812820b4dc05ca762e76653429421b2c127f37c2909d43c0a6eaf6b593ea4d29015ce3e1ebe4fceac4933253df4d1f7c3954bce87c17750f72fbd8613fc8aa6778b3450aa4164a508478cce1caeb27c51a6db74d15ad11db0052379297524d860cffa3d040404e29efb49c6d2f404b9447dd4d39ced8e3d870b7c3db072d00ee29a4bca3b44bd2fa27b524a3ae9324af75bc630c7f5f7210cf751dee1070084850eec80c9410d72c7b2bf8a50c7cb360dfd05bcad3c704a5882c3338f401c3c800e96f7a2feea74df1d50e497ecb9e6c66734e81b4146674e8f87239711f1b2d7076a65a745ccd92420a30cfb5f3bb7d3d9a63cc10041167b7fb9c5fff72924ba576671f77ccb1815b74d9562c38d7142e02534aaad67a0f4b7eb61dea84b3c23bd0028d89dea2a58065fd847a9b306cb6c00b161de2ad5d71daaf625192e8f80ee696a6d115b23b2c092e6bb602b7119a137434035e3b61cb09403227fb180f22f650f3f8211c96cd148053d36787b484f0b21c570725d149efae5756f8572c2d7069631c2d3d49d77c1f3526f0f47f032f73abffbe335548cb058c1db281f630258cdd2adf61eedaee4ca0ca6a119abd832200da0740e27a2c44c368a04f7051afa8ea2f0cb1e868e0a3cecf892b849af378d01b0d202696070ee967aa2527184920a5568214fa47806ac02dc1d943da1c754044cf105b3d9d193836554a957e9765a66f553e49b435e12042ce81a7839966e330b30bdbad2a4836e3356ca827b0c06b727a6fdb9e1395dd8bfb667f737237e45b9d7480a49c147e2396572ea5686219a7388b6e9871c5c955c1c22d1e56659007132e839cc4b3ed7aa196e921e0a176af5de89612dd4a36f62d9e474112d85bce87383a1ea91eaf0ff82bbc85faa982e859855b296ca6038add18456ac6d3a624fc7fcfab9a6bd62395876ef115142ed7a9161b86e739d1eef112d3e5fe4ad3f04bf5f563f6d52063e15517460635442e45ee84bb24afcfab64622c369f1839a907c9fa406af080394a783e915cb620eb21cb7b2492f61dce168ba2d57d2df5a131b92d3d24fb99748dc522a4714ff52725594f867e37b01ba3cef04ac390b0e0a4b39b96efa9d5c59843fb1fff0f3c6234800d017342bfb24d08b121214d8666a15306e5d4842ae6b3bbe25b4459858f275e2cf8ce6deea34414a389f2323a34f19be20f66dbc59f16d4d6b68ea18080b210967d5acdca485c49d0822aef1117de0b82896f763f9b31a86a1a9d0bf4d1eddc3081d58bb16f2dd57451e94e3eb8985b348d384c664fc7930b258553643cb8ce370dae9f758c7cf58161e36226f3b1eadf33f91897a2182eb0dfd61fb7a436ea55ef6e3900fe31e8f58bafd055bcf08358d6e54e57bde926856479dd388ab01d16881529cd3abd9e09499b7db1e2ad361110d67f6e96083c5605927b993295d93cc9ead0462e51a4ee4ec0bb0c7edd1da7aca1e7afb285a7652c9b9c56604c1927d7cbf85f84f2ea499d8bc5d772be54b7a6ac871469efa12a44663ecc0ac0b00e006e29dec4b09964f387443a9f11c768ed70c50689a4ebc08c89f0f9d2077d0f387df7308c0826ba340c1691993118d9aa5a48c328158ccc860ab9464006eb1a0471692744bf88defef05dc0a56dcb68f4ed368a8d099726a41827fa8e2568377aeba5137e4b7705615f0555fee39d4738ad7157d291f1374f23649725f2de60543814b71daf479f9c9c0d83b4fb0ba6f63efcb578d740a936e5f18f95d7b1b9d9a131d2c8278e1d3a90fe1cbc153b810a62c1b7f4b96e94eccafe79fd341d1e95c3b7a7f2f0ad050ba5d6d7eb97f9dfbd3c30cf4b3b8aa8ab1ef22ddcfcb13342db098dd2e1ccf1981f2a20a79c843bcc3cefe586079e7a85e7f0f7033f8fb74f82e847cfbb23421457ed9bdd0526d96051ac6ea3162530d4e9f37055af6cc87837e7ba8a68ca8dd051f4ccdc5fc186c749c02a80f04e8bec6a3d2d82699f1650e4e15544addc7abec12ce72aa5746225f2ce3f82db138ad5001040d05ada8fd3dec07e1aa77ee539a8d483a13d96f7ba41699bf45bec83cface1c8cb9c725025f2b7482ec695e2dedcb2b2db6a45822e3062c4b4d14aaec70d411f6ccb385894f8dab248997bf8fbbdba2561272c463e7a1d41999ba3cc2f6b1977812187d918cc2efd03850016e87d2ccf372bcc9027b3fc29383103c907f028c4f5423e714579f27384e6f1c02feb0051f8314c868a59165452f6f2b502b93dccb095adcbdff38cc847e82adbb220c51b0c23aff2e0e55211d60181efbfcc65a92549df27a4acf6dfe37f907de452cdbdfbaaca4a64beed3885f96b9c89f7d0a443629b8d6f29857ab0c8cc897d26df277b05f92970f09bdc4078900856585d3727fab35d086cb74fc433cf9a4a895a40880cf012f09d93b7cf43ffb1cba11894efec58c2746a972af997bd00c45722c88fc88d411721e2b514667a923dd1c93617c4220117b43485ac8564302f4fb91e5ebbc1c7769b22944e9777afaff645603f192c6e886de19a94816ca03dea7d407bcccd66acb94e8ef36c5210ecd6ca0add0d9993a1a06775f3a775b2f2f1c329b30c5c13e1472759888059a5c84a6e0f8870ad42d1038abfa21de3ade6c678d84c9a32f9c314e5d7e1854fc2bbb2bd49888326060c163933cbf581bbba580bd2efc302d11df317ece32a1325b08c3b34ab9aca95ff1d01b8f0c6a2a4685c666c78238de7ee145660f3d49cb27b5f7283396c149d05f499cc9cb39df6ff7cc57f0659a99ad430e6e9e17a01b10cd64b8369abda67f0420363a0919140ad652b296b9359c597861cf14f5d0155f9d1467ce882de98ad6937e93d9da2526ddb2f53d8ea05ceaeb4b0564e7441d1e87ef783fe4a72e15d5d72e6a04dcee4fab6e53fa3fcc760c21661864d9f23ca5104456b58867ec8dee4cf3937a9db72da6d9eab045f81f2fd60b8f8589cfeb1be2da881236ab5d591f884c17956fab558a7c4ae83fb7b4bc65d68699a0dd25e2e7d3f21e81bef388ba46fe3130a37b1ab9d77f2c06c3f7599b56cef387ce60afac1ca7f0e14dcca2c886d0db01ba4339b0de6ba4a857706a0563a097d85c4780857903c9f7d16e79dbdd09254c467071de5c3af89ccfa69a2ba2a2c51fd5801f3ca673934dc6961f8f4ec0ee37b00e4a044f20483ff8767aee4e999cb9541949c224bb212f126699624007457206652d12e65bf8e56ec3f7a72fcd69a6112b6df9d0532263c03c3c4ef78ec02572031c4b1032799f13563d9d1bd819c3bfc7385d4da84301ac468dad06ebe32aa1d98a6ec63c5bf9dd03393cc4f621e6bad62f2081a27bdcd8a074f8b279c1a5b634e1b24695e7a902c2afc55bce9bf000baea7f37d2830f54d97d736cd6d8356fda8cc0f1981e983220e9c42f189de72033a01e901a1923d4528ae2570055ef1dc0f091d32377c9180445e001dc92f16ed611a3ed83f7de04b8fa3518bcd343deb26060cf5ce08f3bea502283a0b192fa336696a6fe259c993a3d1c88a4c3f02743ccaed1e2ec28b67c9350728ee0196c49d4540e230ffe10365e7bf45c8dbe19d936f1ef286be16cce0d17b9f66c4ab627a1cafa82f32f0d70d57e11926704e6825d88cf7f361dacaf230a71891cbdfdeab77e6d4b7a15d266de110bf034259f573af4385e4066f4255927d8b8045323ec706fc59019b843cb1eb174a968d4449f4819e46e15ef1af8a4142eebab895f94909aeffecb037c0b77f983138e4ecfe27992010603aa10e3b41de5c536b848021ac3565dd163d2baf70c1754dcecb90c8a2359b49db8267ec6b843da597cf499a3f66e5dcfa39e62d75efb0b977659be75495200d830220cd6c44a9218c49730c9b296942f83ed001346c8d314544eeea351d108cf889685a5918d3714c0de2eed519927d2a72067069191ba934e83cc928393a40f0460e958868f5bf4c296e85b8f2e7d6454ac4db9508da2856228fa7d265866711e0a543498546baa78ed122542b07b621d7a35f5634908f48b35d211b2599b08f20de7139d90a8d18aea23ded5c2b25b197ff1b4428bc0321eec11e55b59254ea13a5b91ba276748c93c2b6849e88eba9dbddadf301b3b5bb21f0d36d60ce3eac059a334168753bd7b801588958efc7bc9034dff0f650010e76f75dc9ee6d1f41b253ad7226166fb99dac8fa1ab996d9098007013c0d9c577a1903d98afa593311d2189c279246336882807f2c6d4c1dcd8382de0063c7cad2b5c094aff7a1eb6a8f725aee48a075a060e50069e2a29190da7a4bda6cb2a0d005f860437a044cc284951768faec00df84dc6f1433d758434d80ae4a45a108c2b4eb8be9d528297ff40a2e388ec01493a941d5b08f4a9c0707271585914bc3b500ab05c307cc65756f4ff1492a1c90960ef068814bea8bb1ac8eb9e95091b76cc9757258ca4465c430a31bb65fe6d8ee2ed5f613fee0d08a20930f88430b3c82011f59c2fdb9bb255f6f77ba4e6ece883733301826beb4d438116201f1bac1485f18333f303f3488f819af8ed5630b007729346ef0bde247076872c46983ab22a95256affaa458f40949728ecf7429185ff0cb3f90fcbf019cdfbb78285e9a4cbfd796dfe258c6df20602aa17b16f5e7edb4122a263b96b09265915fb809f5fb505f4d506130435255b73f34e6ba05c84b9aba9b7c0e43bd6bfe5b63fb80d710815b979da999035dcc738adaa964a8b809e715bde6246c18b376c1347f84eaa5079d93d62f023d3776c8a44415c4940aefa5a28f30971691554bf90bc89870862c7fe492b02f6fd6fa32ddc00e95d1d3c968c0f295cb8102656f172dc364a9cd395e8885fac88da1a88109bca204bed92850b51107580e4eaa4018f50bb695d5d09062ca320619d2a5dbd082557520013e69af6751da65299fa41b47061ec07fef14b0b1534e4835367d9c809f28d8455567f543da829f8cd1aaff6eb91e30c1445bf209ee99d865783df3bba7feb7ea634974ab14abc0c85861b7dbf90a283a3d00143292094f2f1bf612f94c4932b735e80e8b1cb2105f829c7a98a0c7dbadd83bf4fdd23f045df27c3cbf9e7e97671f06a172043c0aadc1ac846a56ba8e8c89ba6de71417a30de0c8d7aa1d6fa13fb878b8dd3f5ec1672749d88dc563586bb6275d253b54fab30b810b68698d1cdbbe8fb54789261091f4484ee9e3663972c00c8b24a6b6949071f8c45a2ba7b7e29120545c60529ce31a4156ca1b50ba654f0e1801af43ec1295f0817087a5588945a609e0d0552d29aa521718fc5ae77b163c64016e8ab4d2760a6dfa22316b4844a9b3e6e93fa8c3d8d8c2fcf1cd3c11c5bcecf820429b42c1ded516756ab4fbc8b2ba200cfdd873cdce1f9d8eebe1cd53e1995446ab413b96e1bdbcd828110c15105e0e7c3970aa76f93a8b78c2ebb00c0eb05227508e109f0ff47ae50d6c5f6815c1084786a3c18f4bb359bb54d9b8556a7cb5acc8a7b5af70f77c885e6d97a160229db4b505aac19fc65d5218766720881c54a23067027472fbc4d75f187d9d996533acabd3caada6e52c3701bf1ca76c0ab3989f826214a4dd861592910986a84ddbc3fd85465fa924bbe4cecd2948b4277861b0e111bc9c5ebb1c0efc925a01d7a4d8cc00c4fb3ffb871552e52a158556a63900261d842e960c595073876066026cca1aad024a060f43a10141457e295a834d02e058fc303548545b4c4620836f00ad50fc9fba6c4d170fb15f890108db2249e08d2338a9fb15099584909eddb11626b9b5bb97bf741cf3311c65a97630d2e1a7fc8dcff0be32893dcbc56ca71db575ff69923ba6a6a8140ed513e1b73475e2480b02b110e33cbfa483d9a62c835103c0f60cb12c1872bcb06d04a1501a232360600c2ded961faae7e4ed30a5e24b5c3d6e228150ed4f8d38c4c1b498ff27def4f88be0452c5d1fe1ce51c1288f063a46df73f06b323684534fba3d1901e74666a1dd863bb6335dfc0237c3aaca7544029c9436bf141da3c27d59dc57ade87973bfc9885658b9d96fe2ff169877f74068089e52bb6180f81835ca7b170fab64a8517a5a02090064a294f15bd5c085ff016ae74cff7395705e9d5f67b36f108df3fd1f6bd3d7bc57e78edff12adfac36a19bcfa33e7aa794711fa5038c6f43104ec78135714bed4d834c4990ec4b453a0f6082a2cbca26adcdd5df684cd4859cbba7ef986c0eef4d2898213f2b70fb01bfae00a7d0c0051b4dfe25766702e9d2e82c7f02b1f4f3b4a857572536654988a9b481881512216c8d029337834d1109e0e61e0dc2382dc067865f9761b48251c268de02d0f0a70441fc9af0d2f197275b2b2147d172143d210fb3abfe0e961c0023854c0df0b005bd17a949f9e8ff8b5b64ab9bc9f84c7856cdb39d1aab681e5f729c30bcd19257308ac0e4bbdac212fa8a7677f6465e44538d43c19b63e046fdd9952cb75fd5c6f46462d19791f4074b0d0cf16ce1a59ba9b6ae56916b8130bee1f17b4a2b3d7558c4cd4c369f5359da9ff42a6f64d9d41cf85b4be90bdd76363f9ac0a9c4e9a363236dbf697444fff02e5cc04b46cbdf3b351914601e0ca5e6d2895bc812311cc3b7a973b153a358e1c91ab03dcf5ef3a7a8c0ef1e9515ffd7b9dfe19cbaa671442599aa944df55dc06220d3d2b86b8f45e83968092a4053d1b5af07b7a81db3db7556f27f8fb4989348803a06759294098d820550a47e2ed41028ed1a493e08cd6e09fd6428a5a82857ef2acc9c13d50c1ae05504e48f8420df5f86c1d246c59a07f86d3e3054c85e257e329e73a6524568be55c8d5cec0f4a78c60a90a969939ba79adb916337c309e9bce857c9a7f39c2425ce0ee57ca91e1bc9c69862660190e2c908c4090d30d03a0c685626d269526b52f5c7a6b1db1f738e78f1fd5f0383a70bf0e5ff1fff1ff2b33c00ba93766059394aee6e71af43ec11ee3273d43d080396ef8746d1ba0a1d212a5268078bd9cd5a6fdee0a98e52a6d62593edaec6dddb6e6d721396db07ccd52c55a418790aae673d6c072fb6eb2fe6358a5150f70c69ec0e9c51741e9dc275e47453f8c8e045d7a0ac072285bb59fcb17ba39a6d83e0772958e41ad52c6a990c21057e56ed2e9e2a0fd8286b01d5e87e6271f92002136b592f4e9b163e74c805096bd35add227729a9b8237c27c35f8099af1325936433235c23546b2943cf1acc2ae9ea155cead3abfddcb18dd3db1e19418ba7ed437a4a2315707bf3f1f1e6a040cd5990cf14a8091dcf4c9fe43474b3c5f4c9f316ac76d8aa013881648106fd67f4a2999fe9d92cfad5f394d4043da6a4fd58ff88ac52f82a750eefd6345989db7f368eaecc53caea4c416e19f873c8417dd4d4919dfd118d6895e8cc95b291336d902bfd59a54f72126e3478fe5374e43991a8d2f5fa27ed3082732d4bd9a751252cffe04df2776092052dbff51a49c7d6176495deb1787e1c4157022f6d1ca03c3f3d83b643039c3373b88fa207bae798e1a07e0422c9d8877207c6606178f13606eeb9485c28741d54b1f5b9a6ff41f7502345fb04856d9004865c2f6b7408c96375a08519b729bbb9083696e924fb382ce5015f8812c473cfc3319af42979ba55403479e32ceefa02269fa199979a6bfd083ba07fcf3e2c6cc5ee022e6cb91d81fe1a9e88097bf23c79e180330f0b4c0ad1d0ae329de5df12824e87a5f04475b337a649b8c95c949faf38e6f0bad45f4511ccbd27e6762b6a8b73cfef3d11b6f4b2b9a82e9d24fdc91afd000afc93f4fc95b8f8453186c289736283274cebfbc26de7923952d9ffe805b4214404b5df2cb64006f78f5d6a7bd0af8bd70094c08e7a133d00fe1e9365e08093d50ab91079edcdb23b55000240cbd3101791a75d69c00e7387807c7d4e5f05e0ccff15a59fd4dca69959a28071f6d45c4d688f47eb8f9a10d983475ced98134f055701f2276852e6731ae7a2fa7e5168b0169ea6c9e70c98401ffd62146c845fed7ac49582fcfb2a0ea0dea159c543baa0f7cf39b73e734baa3ad4d79b9f50f73952dd16555e2f6d87d60f5b1ac28be7bcbe8694e80c4995e1d0042c70faa97d51a64566cddc77e03c655042dd91dbb33039cedae4f85087c11516732e0253439d14cdcd63e88ac681533a3f62ca8cb82afd623735e9eadc6a2c168fee647f40691c860619b2be566e8f1b2a61d80dfcb86904ff5161912e7823019826c7d1b81cb2359ccb679cea5ecb426310058ea61653e6e9bb251951c0faf7d11364c37252849e52f6ca13ad6d3941ff90facb7303923feaf662dd6c57e2dd15b676966e15ea83af431fc6ddf0c54e735ed66ac50ae01d14ab611515c842bbf8c8b853050356d81cea4aaaac94a8eac16025b14cfb29e7ccdfab645429e84459d24ddd68a668d96fc9ac5dfb83738c4a0700e0691386c9e943bd2964f38738cd0e86823d4de8436ce3ba055dca1d8b42e6e52120a2686fe327c6aa93ae0e01cdef43db18c329691da937ebfcefb0fa263c56354b29f36c5f54641434908cf58a94d63c08c662a079fbaf0a12ac844475246de067b9d10c663dbb86950a004f7052647a7ad67a8bb5c5773f139e6bd3e14067ac167ab6f932788d9a59af64c41499db18bd5db69738da839e02805597ddd8770e26f9fb7688125d41a16800887092bbb9e0786b1e181943df4bfd77692d34c368f383c2da9f91529eebfc94c5473ab72a51b2ac95b0bb7f7167492a2f0b255e7b3258f0517c96036998ea64c581340c55470e27d3ae4f0c866aafbc39af5443f695532effb53a2ed7d0ac78009635f5e87f9047ebfd042e6fc219352a23676b4833f0a686f2f2cce8ce7758cfaf9161e48aac5ada33d1216332b1bd82ab44ccc5790155393c547de04eb803becc29a326c75940f3b0aa4bff71a11357fa7fc0fb0be8e3b73564eb9584ae6b7ef798c01028180312b47a75d31825262937530d9c4b4acce3280aafd52a7e98dd9c0752f9461b63220ecbaa37bca367fad478ffb88d6d15457e6dce2abca04bf35bac2aa771d797f82c412701f5b129fd799ad9d865e834ef42de4de31632b76e74379106f016b94115324edee0fa165a9fd0cc097d5fd424b5c87d5b7ad364d514e8a7acfcaa49cc9587374fbc121d61c04da311aa772db5bc19e94612e23446a4dc2203604ebbf23082cc7d0543772324231ba83c5e50f0bf2030c13a5172b15cfebcf6eef8ae4024b8089abc9a04e91aba2e1b4e34799d03f17cd0e7cf90ed866e7963ad743171ac0c2471c300d7561fc8b3270b5adf36e2377c04258ad8881bfa6d5d5791dccdf076e687879ea9f04c2fa963207e3fd14a607439b5e7af463269f8fabad5e0f28b61054a3ac63e7c9f181193fb3d38ef8f0d8482ed3378312e72115b9e7c145fa5b2d11fbf7d141d8c2c4fbd78cdbdfb8e82f8c5f8d76845c5a83a534bcc91e10a4e545d4486e0bab549a9a27d5058ba8e8553e1dfcdd7c81a65a50c111162a61203318340eede410d2f5aa933722b4672c10b04350ff29ac4be74b87a58b385a87cd04f7b70b50d1635f159832f17d0724788d59f1a77c32ddacef4d699c9e36194fb2112a0cb2cd61afeb48649647cbe32d8fb69741e123f36df36fba2dcc509d13ece972e8eb16b9b8f821db753c70a45e65b21336a41237a78d1da819cb8ad6028765c6f796604f96ee1067582ffc0cea82ddb9531d317550f19fe717a573056d69ca17166122af24b8a6e1e74923b0437a1c75cc62970356bd8c707a8a71c6711d4e6f9e6ef90106da7ce49542d7aac0f29f52ad26a3eba0f24514eacea5b23f566acbe8a2fd6b3e22b23e9bf474a93c25be3bca995a74980210210397338c78a186d473de7813f246ab4848747c2bf3dcb393a9e87bb68fae9e13b8caf974464aaf0b2f337691cca9742824508ef82dba73af45ab4ba1c1c5e8e9c489f661978d2e99fcf7386dcc7afcf638bda3c44fcfb5e29193e73fe841548e326261e7289e593edd014764edbc254e76e01c056f1453b93fdf5d980d7d1cad2033ee08c4639d923eca4adee51855b433317e2e5d3a883b9811079d58005b67727a4dfdbdbe7315dc6f65a64b5f2150d868240edb1ba9062f40e950fa9437664593816c093b69bf3039ef944aca30433730ccd50e2f10f90c2078a3ec29e2c5b1d205709ad158f9b33a407f711790878d9a1102e1eadc52812522254e1593c20e893463132b7205d19f70596b0b9482fa8b67278ad57f08b89999bf5f5c96ee5090999bd40919242b06cb4cb82128387926f971e96b7ddc85ed031ec6eafc9ba2b27ce21e4f5d052500f3a6ea743e4eb6ecefa92335938a46bcb970fd1a96a400c99083b8c86c1c6939cf03284e37ecdcaeb753a11d021f80c1a10a5a80660b3b20522a5807fcccf3a9ca8cdb0493ebc2b5360f5254a23b92daa32142506c8aaa575e901528f8e06f05ae60facdce33f30aaa676debdbe667530bebcdaa2f2a594bc90042071bc9317549d782fa5685de18ddc013462dae8054704724fef2233c417c61f2b1f174cf93678f4992d4cc8f508688d25280a5b320770006dae896237b4d700a1e691bb2a90527f8918c812205f32e93363a2d5c7b9a17b4fd8df6f37b5c2d6e2ba10812300b9809b52d5d3b26adbbf69ea366b0fc116c421dc49a79bacfd0c462f0d460e1fd96e3103d6c6fb263fd57611d85cef32bbe213775b87fe2d8006e111a767cb46fb19f483bbf4cf5c6cccfaa862f342e86ba206d5ce9deea7ed0c3a9bcadca03018b7a1e776cfa676e65cb19f9ba70320ee3615aff2f1f6bfd07456536af90cec5303b9be891ed4659edf57ee4b29a5de47edc00591038e16e074f50d789e1b29970a821d8544cf0c12b0772267ca9e36ae897845ffc9623eaff516ed3df96b98ed6d6968dfc14e6856a9089c5c04168962ad171ea74ca7028f6ce851286e1cd582e35c9720a5ecf76ba3f75c8a4b07a500bee84930959e5016fce40e45ad1e97672bf7966685fe233138c749c0b2559f1de9a6842252e65dbb1bf44780f80c6fd02bc3546f2215cbde21364a8b6f7a2d65102269a8c11727f06f1200b8fe099bb2e7c4936fa3199251f71a4f087fdf75da25de1d5627428182066f4a626e511d689fdcce891fb7609edaa83191085d56a7242bf9418dce6b8005f15f96e80a767b92d2801d16df6a5bbc9c2d89d3527817176d82877efa4888236b7e16c8a3c24d98b9aeed84c036b79c0717511cc4b1ca476d2d28a67bdfb2dce9a651bcd6240b7deedc752bf5fa60c8c08ae8af9802fb1d8445f5b98ae0e09223dc8bfa9802c0544fc269647707a42d48158aa6a6e749ad94b47a83da2b63f2ed7e986452d2fbefa97b7b3a73f7fe600816fa1044f170362e901b06a16bf784c38d967b4402c48358a7d2a3c666fee936c7183e12b15eb100fd669e7c003e1fb77d3280060019caa7753b5dbd2d5d60ef3e5e1e93c24bebf4d4fba41424483a714bc0c6d9a22aff504a5cdc93050e8d830e482d167aaa6a6345e8d22aae921444f602cfbf4831eea2d2d81a762337ad64b95ad8c82c1cc37802f51f68c4bb4e112ebad9fa7b0b103b432ad10bbf5f0a7128f22457888287892d557fd984817fa2aa95d7280066aaa22a9ae18e68b7bfe1a6b3bb7e3e5b42bc46fa1b58a9eb54cbfce52357bf89544346d01636afdc6c4ec22386b106ba4d0c85daa710bcdfe57f25fd36f616d6dab806a2175b1b4485d6b073d54171d5189aa6c09678fa251a520399af07996cf2236f2ede5e166ab24b7a0893c4bef807f6c8f14cfceb860b51e8bd8a5b7c9890854b2c54e0319c7cce3971c9465d649bc674b14ce39c8ebbbaf73da6b2e78ffb9fc86ae5a8a1305bb345668d4fc94a699a66c0665035b74b3f00eceaa22ee90c7cd44a820a04ecad16bc8eeb9c486015a00035a0ce2055333d00665c4d85f97efcaa7cf5fa5e62295d3483c36abba2f9a9cb0c819877ba5b297af86c16657c8f7b97986a3808b1a2e28ec9fc63620aeedafb03c234a8c72963e77c380e69db5364e489e682895bfd0b9d8fcf45affc2a114bd54f67b96ea192c2577ffa4adfb1055229cce8072ea1cbb970c5740438a28b753ce2d288fad14edd4cee31d30a1b15588cbc202af50081f7d1ebeb0e575647d4326f6975f97eb3205dd2ca3c5da14ff0ac759a5e625697edfa0c908384bdf029c85df1d901f6dd38516d6947b71cf569b75c7a3805462db900928b2e1dc98c524261758017e4ed9ee006f2edd755de270165a937e01a9ecbc3b9c1d89a8cbff945bdbee95503748b409949d11ada2f0816901ae547abf1f17916f7f8603400993d3e30fc9fc8cb25687e6a4b83a46b4bf0a79b1a38996a3c0e40578ea0a96da134cc7c592c11786fa4da4f00e48f6f4a3d4ca57e34f464e49265c45d445d1fb5aec1b6e6f78e1619c3ebfaf0601c34d884b044ea7e9435056ccf4142a5f37cdd057234d3e2cf73e38865785dfc364945b64045630724e158208e51bbc72caf8978a6c7f04da387a1f1bd551ef83e54ed61bbb93424ed8e444ea8846ccefc41721fda74cabb9415460b181a101659fdefee4a773345a4fd13365d4dd63c0930adc38b103fa604981b350ef40e002027b719581abee3304a9502e7aab62287c5911d89acd0386ffa9735f86748eb23ebcb86df39b58055ba004d8be543de11c8ce3f34bd33552db7de0aa59aa6d3b694c4e66701ef9991bfade5038d5045209ac6f1c8f32f8cec4a78a064b931ec5162e4b6c258981bf4e46d4f8c6b980f7bc38102f02e51a514daf8224124cdf57cb487298510c7c5af7ba65669a6f6ed73362d74943289d028638a0f59f06c9cd99066ea2f8373c8211d72eb18910cc9882ce5b164a73fa4a23d802245a1302b04219a4905c1942f247ed384e38f805969220c579a80c095d7bdf4a829f8226ca7769d3b764c2d326c90cd0cf4d1b3192be0169727acbd1428f0639a60492f3ff9ae22a1a4f973971a3f5624495efebffb4a96e037b7774fddd76d5f177235ebdd561d3df5dc6c21b23053cb02a61ec17a09f7d6b13bcd1a2c0b168fbba6b8dfa71178f2538ff93f7a6ee503213f6777c795e36e3dbdaa238a7a8795a6415966e408d3bae7edbff9055fccf5b8eb9eb8e3fd8273d5b67d5ee60788b9767e387798d3bc6c3bd08af64a9e9ddad38a3a978f19d5cd927882624c9555fbd166c012ee76fd92832256a6ea6db69b0da2d1c4716022a07773703b2148ba67abb6497ef3cc46aec542e45d86bb296cbb3039072fdc31baaa9fe25c91f66d15b3932f5c6e8b8c1cc8e84f07e89e06771f7b42df4efb1c096e267604fb0718cdccca62069eb6d5adef69a6962d3661a77d578eb7cc3c6dd1193f711b40abbf58a9b397cadb335720ec1dae7697a575a8ef7f469ade21b8ac433e77164e1686f9eb91a217aedbcfa4f21cb8859af6f7f5068df5469062b0043fabc635c387ba4ac609086ef75b7dac1e9e5c4be4c71f6e1b850ebcfb9a11b446ceb9287d101131dd8efd05e5dd8950831aff93dad92d7b07f2ac2f76bbb72375c1316877200623c3c1964b468ddc63e66a4fa39c1510e5330b73bb5037e0553bf10f1d9858fa8861533e2b34101e08f9adca771f489ed371ef0af122d21d258276b1b71e7fc97a891035aa8bd778c3e0cd0e22d18830487a82a89c42f576c50d21443319a9f6c2c17f81aa6964f0976e3b0be1cc0afe11767a24ba89fcee71ce698196140e405431536734dd7df0cc209ba294bb0735b200563db69d82541aadaaea28a8350f117007d5f268430d323d6400155583175f95223311f5e8db6817e2e6cbacfb7783a92a02013688a75e97a1895226140fee3ef8ed4cd795261f4be37a0127d41cbf1881e5cda05c27e3d1b89bb158df7a3a149275d7f78566dd7daddd11073f43f05ba8d1345e6b642e3ff16752efa2c747d057dcdc4041f2c1c7711c05a45be4beb95ac56d55c57a1105531308a29f9a1c2b2f47571d70f41d4ab8192e70070961874576ec020a6b25e66cc1ea05bb8ae95f1a81abd1433815c7894b67a56ede139083ce94fe71924c7bf321a3397140eabbe72e5d369f94dd0c697df96d9a7ef60175f321cc9d4bedf692d5eb83a7f54f04a8d53248842f54e1c7a18926eb1deab56ed106e55ad7c529f0bdf845669a9508f7d755aa0ffc821449626036de353b2d6e2b046dc05678e82bca6c1e33b5d4e1a45edb9a339fc2ce3042d3ebe13a05ebc334c11355447058b13bb04a454064e096b448ac24aa7a1d532fc4e303fceac31e556a037d686d977d2769ce6409542b0508575c4da93e14caabcc6f4aa94b552e9bd0c2a05218ea6021d098bc10c68a85888652b48fad5bdb88acb3f4b225a4524ac0d9c529f51c62e53cbecaeeecfed85693d53a5e6dc207e6cc1b4af74635db04c8f99e456dfb4d55b9466ef6bc54731f9e6c9ed2970ea33dbac294c9d599842f0d0384f8475466b249dcce37abeda6e7bdde9dd3074802de22541f4ae9c349418f63c8ce6c86d2585b698f8ffb7020711b2e354028a5144d58e20f155e7ce0c2e78ba13d109e208087fcee19c9259c81df01c6ed802751b9c484bb7344b629d714a489f5b68dbb6a4f35008fc53448c4a98657b21bb5cb579e1918d1f2a34dcc0b4d158be1e946cbc16079e6cfcb949e710fd604e7fc2264563940723a7e95aa356a2c107cb6612784e25e039f4dcd07155cdc55be07b519c3087c368e275d591eafcdbea7e6404a636f4200e612ee651e0bb20d78d6fe30f2fa6f08b048eb67708dfb778e4f41ecbb3a89a45f94c4ec6d812c50bec87652e5420a74c35142ebba381d440b2d3efbf72b4ca1c8d3699a0018efb19aca8c7859537c755cfb8c9fb8c2c9cd6392e8fee601c0a90ef5a9b56bb4f07af83413e1ebb917662b9313fbf86822ac301b47184bae68db7b51df352853231e73960071bddab7330a4926b9eb1ae95dbe46924d60a2b6cd95f8ce413123b2ad7488890f1a07e1b31f368ca8f1428eb87640f45caa70042fba128a88ade7abec1613603b99c8f1055a18554e4b942aed94cbf141079595fed78a40fd577e55d87203b9cd12900601d17abea6de10370d73bf62e8e48a23f28a3dc5641013964b1fdeda941131f93eff0044642401f02413a79104a6598cc1289a786d1ebdb908aa19f1af25830b769da773cfe952177119dedbaacb211c27ab3df9e803d1215602c5506bdd5b34f97c05bd8b72ea8c6f3ba1788a401d37f1aef544a743592f28a497342225ce5af1ab117e2e3acddf7aad690c1d63fdb5b62dd1ce30607e15e3a1a1d44f14524363f21918de3d151afa8027c5daca7e002dfc6c843e60c2816249033824dd9e086138477544cfc6225cb63ff56c2a18656e73db57bf6703567c25780166ffda172e25d39a49f01dbc275db192b75685eb3f6ba540f9c2ba1dc198cdc3e670583aea161120ef11c9321df689b721047bd61e6fd32adf4424b231343c76715f392849ddfbcc913b1e19b2c828afe00cb15de46e3d4265b29ae4e5550976b2b4be07111acbc684ba7a065ce847941f29f69aeb42ebaa1aa0552ec06102b57695b3e3258c415cc9276c5fc1ae8b7061cf4e211b29f19ace5d9073e1c517258e8f64542117a24af92ae66b2359b525851f19c9cb272493dd9076664d250b52815dae419e7e0ff4cf3a9e36523f809115cdaa7ef4f5d92c4bbb0dca5c99d5bc84d2240e6cf94cca12c51cbf10a554b10732708a4e569e1ae3141af89292d2ce455e70c5c6012de25ea58dba6937ba5e2516140664bce3c20991863bec90165c14d3280d345ccc5b433dcc374898b1df85688d5283f69487a78e3bcc6fa1f203d3adadd882838974fc524b25e3b308b63d4c67822795064d867798580d44e2d9c37b0ac3f1c215fabe6a303946716131a5513556b4c23d4cedc99ed5ea153b09c269a5c4d0964d598ad614b752b8aafd3459a90d35f214d725d27a15335800e28381a78f1a1e6bd79cc1af6253ab9acca0358fc8c61b675ea6a38b1fbbddb4f7a8bfd5c684d8bcdbd61f155c41bffd355b7ed90085f61e9d8fd0d9197cc1ed667cd5315cc967b91d5b08f6e53ed2e69dc391f782d20c870ec957e9c87ee3ace30211fa762aa3d67529b78fd0d29d0760f861ceb4971e011f6dfd41320aa005cfb6f6b6d74c89a01ac0b8ef76e95142cb55049ee0be3cc9476a45824fecc478d1e6ba584d167483dd4d35e858953362e2b7cb85b99fcd57ddc2aa005ed28bc729eba6911517ed913551aa96eae75174f1d97480ece9a217e1b9ef342bb5d533385cc7833157628fbf35b12f72ca0c76fd0523bbc3233b9b613fa8b88577583d1d2e9686611fb9fcc4bf2064be4ea8dedee40c8143585e825093d7f7cac56b1e303e254d639b1e7bb0f2a12462a4a74d0bfcd4eb44e22e70700f4a63cc948e059f529cb868b473f37c63f2c45015407d2c370e6a57ad6a97f2c5657e8d72202f2a4fb3c2f8f57f42104414331300a6139c8884bcacc66b349c02ba35d4642e9d77235815e732d04e479491ad0b9a0b929a3f225785dc9ee81102b3929de4beea2f20deee81848e49de69c76e25d8a558d794448ac5f2724fb59f5d9013ddf7964115a8571760c8d0a9349a6d4a20f901c64639e82ba0469938d7a637db42e8b5daeaebe149be176c44bab9044699c729c44727c86571c1668cba05b7b5d6550be05af5f162ff20c982384a3b20dd34ee7de3761413fa26642cf41d778f395e8fedc70c5464243619c87589cb8a3a216877b5b24245796aff18707522b0273451238b9af08a891e02ad610fcd9c071f534097171c3775c2aab62fc6b28fcbde31e3e94462a71aa106951e12cd447783dea45fee7ef38f99179121258ecb04d1b2aaceec1692ba5ba87f214832fbbbe2a8267a583129d7c488950ef4d1f0a911e6d73cb5025082e32e22a9965bf5865b7c7b7b40a20029c70c79ff18b07422b79ca3b4fcf5285800c890eee9900c2284c87c05c8e5ee8ddba754d419dde2ee769016c51e73ec90f68f7f4f4fe439b16f3af05159f62c420631212c5f097239f7c6f5290ff5463770573bb814a571ef1bef964e24d64d730d709a182bd18de725e829f24adebb6b466622d631dda25beee1a26cc57dfadeb81cea89db742052beec56a42c6251fff947b331fe6b880ca17fee41a6e9449eb621f78e57af8ea86102df9d682258c85aa297c437ac530e512342f1212fdd5bd2036a98780d4064d380f45bc7db8e7a4f37ebd883eb5411fed55a4d53eb6df5ed9020463bb86f6c8b2eb1dbe35b5eabc316728ca7acc65519860dc627dc4a0a6ff966b8fd188e0215c20673b71450c2fb9506f3a2f2b4e6e1ef92fbb52e66938863f5f5b6a429569f590ff3400bb3682ec021edf3571818e5929200ef607c5fd91126504eab632cf04e7463afb87412183dbac82a642b83e5181043b739bedcc390781319d201b0bc9597e8e3b2f0a1964ffdd0e50cfdf73899ac4dda1568a959ab320661a219b550b36a2b85311696fc10eaf526e8519e95c2b1141e51274c0770598904506520ab70a73649188097b153a063745cf5066db5023ac39924b84af06473cf04181f2ef16f29daa66a24c2591665e5ea1943690a007cbae20a6c35c5690835845ef29c91834f1179de2bdc882513794e5011d9888267bffdfe50d283c8f42eea1ab393a1c99ff712a0307d9caef453162d91e4b74f53121f689f7e8257948283bcede76e3607f700b0c8451e73d410eb04040f8ea9a677a9c984d753e038464fc59b2e837aef60addff7a0bc9cd93fa84ad19e6b20a87ed73a26776c37a6e5845f5fde00c0f817cd3edce63cd4117cd6c0be7ec4a64547affe743ec13e7510026c366b944da0d260302e49f03b2157c8381bb218931bfaf55de3a21844f750ce39282c5e41e854c3bc8b0332e33cc69f01bb0a8cb32086b8a54250e78f386a98f108ce9331d1ccc57a062cb83041f966efb179f4b41b37ff73c7b35438622aadc311c695109520e1441225ff8b8995798deaad30ffa4a59a8f4ddc1c99458dd9a7bc92646d37f43a9535b0c0a6d1e7b1de3c0273554e0ccea95b84450d4228dce3854e2835ab7c06857b8a329d61a230d4f7699b23474340202a35e3b99d6f478856dc3dbff0819b77479c53d1bb0707ba0208c80d4f59334e8f041e2e3d84b8a14e3e680d350a3d3955a50f4611ecba079290635957ec7b52656071c6cfbaf209975cac1965ee47c89c0e0c0043f2e4a2ddec3a2c0ed831868d488977b15623a55716d61bd6d93c937d1d975b43595c3d08dd0d80372ef2cffa1f93e9a171c1264a4f973c6b90f3c76f11ecfd67747cacb20552258f0ac40768f83059b968bfe0c4e5e8bb122099beef125060c7c844880747d8fbb4c3c946f9052781984ce1ceb62a9a47c6c64e4d33a1e028e08b693195203f95a68001de2cfa5c8a0a79e0d0b90489d1fae7b95faf3f63b31af1e0de80d10c218af5e6ad0ab8198786a8125296a88fa00a358a96de25e84256268d20c7c51dfd66b7d713699ee15e429862354e014042256d369f527aeed79122cabf8b0529d7ce8a389bdf4b7d9a9f66cd62957b256c88a23fe46f5bee3350fdd094232a3910481e00a55c43b22cc1c0747ed73920d78f3659fd8a071fb82ed39a7f8f2ed0d86b174b0a775a89aed990627114e629b2ab95fb170cd5a97aa6c8f53e22867fe78dca147319adb960773bcaa5b7236f5180c1de1882ebaf43fc963aafc3dc71fb00576f27ea934db83382fe3bf9a62e9bac917020fd7786cdf5bb32b93c6289d947544ea6924da1638c8e9960f5ecb48c282e44a9f14c4333893b888860e099f49c9d838edc359ed252f73edaa2c84ee518bbe6cc4289c801c40704dae916935a309ad9a8036d017d95b81b7534825bf2d2eb59e7172b99af1215d352945a7b55d4f985a604e02d693b8187001706eb32008b85bfee0d409597f7ea7b5b741153d7f101e64cc00f7bc9101e6b3e07a234b27709915b4a995292015309ff0801094ee9f6a4c1e9ee350dce50c7aca9b973de50b145972cbed0e5ba68f1c3165d4202c4e8eece3e41f1f3f93e2d3fd5188f9452de78dd4012f27844375a104ff4862be4051bb6ecb4ecb45469943ffd192b4f7d9efe0cd68c239dda9efe8c9946cd9869903efdcd9b71a44ff43fde8c959fe86bde0cd70c240dd28e8df219ab06a9ec6f06ab41dab2d352a541fae534f84d6990ba7f6a3ed997d32095f275682fe7a5d2066925cb4ff23fd2468be2e052fa9bb672050f0e403597b63cf522463facf4378da4852b5c5a82f222563f2469e1d28883b6ec406931f9c04a01d4441cf44d3db01febc6723c5ff87205ebd2f07b89451c547e78c31577b05cda4fdf468e8d2902a8893b4a97bec7ab2db0393237608d0abf9c8fd5a8f086cbc6ab51e1c7faa6dc70d1bf81a4bf23365af46dcc748a55375c97da685dfa9f8ca77a46f06071477ddab27369a57109f002c140869beb4fc5865ca5ab820d1987715ab264ae9dec24576e4e93a0eb41ea0b69946b9e902035b0f25bce8f46360d31ebac5ba89ba1d9cde9df6d32efd5d5ae76ef5f3d914e87be50540589be5ecd132fe641d13c9836bd9a395f44454465a6b5c07a349a269279ca7b45ca491af72e06f4f9783697f78a32bc62b8893b463de2275a7953cc3959bc9877e345f1a4740a064db451c2149e6a559050b49a8005e268265a4d11cc26c1e2783827d41ee340b1fc954a20884bc73821d0a7a9482681205e0a7d58ea55cb34abf3787ad532b5e31a4923e12a8cf38cf395ab5c5f49c9612a8d9af14e5dc9f49146d5994642776815aa43673211cc26c1e2c040709b6d3adbce36db749c3bc21dd9622f5bec65a3b2cdb69b0d6793b2e56cb28dca36db74b62adc8a93e18e805eee4e677447ca1ef2324ec4e14f659bcbe394914b3627a9459a21b91a4591344a6a24181352934699a4dca2d496974846a9b74571ba49b9fe2d48d8502413edacb0568eb45666a69cf2b6b741f17fd228f9d2ab2db030d968c61380b5d8c0868c335f9203b1c0f27475ecc913282e1d309432b7e7bd3c184a861bbf6e1ab7f176b3dd3488339a79ca7b39e94b5ffa8a6c241f0d3a10979d32e7dca66cc661a971e167c7596efcb094ed29f194489efcfd91244362792f98a7a4478a71e2c877921053323a01f49434e8a30b850dbd9707bbfedb36e292b03d4cb388c37faee20f3d68dcfe2a67a31be530fa078c3efaa987fc968cbe8b69a9a2ca92d1cb6fe5b3679c734e6e01851cb8f2670db28d1f7ad8b83d6c5cd985381b6bd04211cd2026ba81b5d7dfe248712344e14a9a52db927d988ec540841808091bf35ef28be14605d6f472350a49a7b8d1e81be9f4cd75ea042b5a89563dead117db98e5e7bd746c68493252e53fda71ef158b512072117281651c2a9d6a4d08eb83bce168d694e50a89567ac40e94c48e6671070ba926eee04ab50536243d11c558b6416914b39417295689a40c11b751e9dfc216066e8870ab0215e20edea81077c446fd0814031b8e66235289542ac55045f7198e69670556ca10bb1ef3530d13ff50062664a0b93238d9e8145ad61f4b242379238f93d1c5d09f1ecfa0c1d1b85c77413774a5a723920d61c3d18c04f314d71a1b2bdde06481e53d8ffabc16ba42ac4184c303883bfa5db087955e7cec62603c4c8b565584c3358894f000fc59beb02f0c6f058ab762713cb55263bfb0c64432d08773fd3fde0a92065da4239a354865e529914809d7208a421c80ffea3fd16a757d0a11878b946cb73f916c93c286229ceb0f13e14811f916639cb8a3df29e3344af3d4e612c136570906b3b11d1b338964225983fd8d5c53b436b5176bb6980b2c6d2ef0869b89337531fc5bdc5ab619dcb602cbdfdded99e40c1658fed1935e7e4cc24a7ed2681571cc9f3438ad468d2ec6f2c77d5dcce76b373d49c416c413442a3b1e9ac70b6b5cfa9b17962efd90c7c970831199390de2b80a2cbfe51adecb53d2366e7fdecb654b5f473adda73e354be914e91bedf4943b9ad92876f4e107e77a30cffb6378de2b06db3c2eb634b191716a136c389ad53bf2fa412f1b461c5783fe5f17b32169d01f0000e8623c1ff6cb1b430c5d8ce765d86cd8e862badf22e304c023802ee6c68d2ec6d3830b79ec1a605282edaa46a77755a3b36a7486b8de6edc9841f4337431768256b2c8826a539b9352a1543c9e4ffd7c3c559bb47360e716e23a39b710d7897e0b71dd0ac80b8988dea5c8c670aadc183302cecd71630cc78aeba9116915cac1e5e862badf401c773db6efafa506769b82260404cb32c06a5c598a5b8c877643df64c53c72fdb55fe3bcb04658d2bcd06eb1500626b4ff8c669f7ed32cee78c51da3ebffe97ad0c9da620d76251df1dea275595a2497a764fc4609259d92e9565bf840b4da62e5a956f9d7f698348a75e49d4f658d0a45b22da6042b9f6adcd3e7ba184e24932024ac01646c25b0fc24130f1aa39fd1fd0bade9157198a48e52c5698fbbd153a38f343ff23c98d744c45149bd9757e3bd3c1aefe529f1601e8dd7c473e229f19878308fc66be2c47b6dbf7da88b916fbb6679ea8bd476b3e1d8d8622024768bc51d9cf79ab23315b1b918c7236343c6097d7f1659cc494325c9590aa9b5cd48ab485ac934aa7b7f12abf3463aa32adf68a741d2aa411912ab1bcda4b0fd9366737ddb4bc230c9bfedf22dc5818eb01bcb53261d621431ed50fad18fbe204c4b485ffad1973eaa628cbcd18e9f666d00df7236d9e59fefb56792cf2b93fc7060216e7f1eac4107b16ce8bdbc977bb0464591482492bd48068a64e188c48160609b9aa46c3997590ac913cdda00fe23ef01ad12c9b4b0a2558b4ca3e4164b89607ef2274d92ccf588b3c3e84b5bb061c4e16e2892b5ac5a44304f89642d3f14c9463b394c15e91bcd4c60e70799800cce029df25e1da9e8dc70341be9f4c9fb1bcdb8b02195b1a965d7db74d0d828130f1ad7f48a38fce5b571e36f3b5c5f26f9f1b8db01a81544b26d07570317638af4f1871053a44f3413ed18e1fa6f36d8d0c57c98b7e70e0a16f4dd136c48657183d222996927ee58798af441444d44d045d375c3d18c45f2465e741809406243efe5bde28e4e046b11ec3a7dc28a609fdfde2362d2232e5cffdcd16c948598da5cf70bd73fd4c53468c6868ce33f9a798aca46333a922f3bc8ab09583fec76e495e53e5c69b91a15eb87e69ca1919556832b332fb87ea666b2c2a4c18f87df95232259a3bc3f5b031b8a641d725eb87264a5759d88f9bae168d6b2e5b7a4249790e86cdab3fb5b32fa78ab5ca73a944a835efabc17e91b792fd9f5efac273d2f1b402fb0a148d681c186015e1d46d2f0223f8c3957018d8a3ec042e6bacaa35cbfed7d0c19db1ffe9319119689f5889db92123f952c9da7f89373f23d881d650f0e94f7b5210599312ca34c9d7666cc84eb6296a368e714a57d8edb9e3747638ee0b59093f61d7e7dbf3946236e4e49c56844176ad08434ec31a359bc99df2e327ad96483a15553031a7268c393535b55a5b2afd83edea9bd9852bc428d46a6da9f40fca2f04db75270c9cdc1a6eb4412273650d4e2a77be676571e4738ea7402738383737311d3a5e5e9c9dbc90b8c313127138f55c9ed877719981aed8a24bd8ad3be387910995d9ddb54dd378db1ec699dba8b36ab5f55539bbfdfc96df3ede8f8cca6c87ab747bec6a1009bf2effa6c1748b892d6ed3679db3d6dfeaacb36a9a36a5bb00c7c605311b17d4508d0adbaf39c17a3e7611f0bc9663611aa6e76fdea685594d086c5871b0ddf5983caa56bf70ce7e2258ade713ddddedddcdb5bbbbdbbbdb6fc441e59cddddeddd4d5b879fa2edfe423adddd3f7777777bbbbb7d748cdd35babd1b65bb7be56ecf7cfee89457760dee6eef5293183f1b9ad2bddd654b1963d4d17dc1b3b0fc119c54892aabac52a3b26a746251659573ca8a4595556a747241568d4e2ce8d4aaa453e3c2a4746a58c84a27165556c9052ec88a4595556a747241568d4e2cf845086eb74beaf98f17e3297a78b81e1cecf5ac5c4a39239d9519042c25cd9c936af2b5e2417601ee67d7a32f7dea40224eb5212211c0eac6cb29d6c048c0b883fecac503f082744b80c8f942f27a315db9388afc5c5a77b186e6619d0772eff12c790f46878e5abb987afd3d1fdee15afd8f1abf3cd6c02b1e407f5cd26da186a768909f34afb806c6411c403fc36ee7e00bfbfae784fa7bd75ed1a9925b770bc58f345694b835c6a05c7e215136bb619c451ded4396615695cb2b70e26a54549aba21771170e7d72adca9021a445401ae7c37c04c154fb99429ccd8d63d42cdb52d2eaa94f2a5ac5283538529c44640052c54d25f5742f1842be50a40b041fe6f901fc64c143566a2dc7eca85cdcc912bad605df88a1b722950c195d22605545c69bd4ab63366125576e08426b2e842042a47a28a24aa689d1136aaf823c3b8d2e5060f71431a6c884faebc618c22e5ca304e8954ae8c3a376ec832574a29955cc94caee4263712e74a1f5cf9d5961e7cd9e2cad701e3038809a470e50711e2822bff023c28b9f23b50021aac5a336039e14a1b1340e1ca67d7ab0505f6baf2045fb8d206053657d6748bc60db8d0d5c88d92c1881a7421d840dd875cd5fef3fcd9366d033dc7759ced4adc57c488aafa4982752e11126402f26384dbab7810fddbbb1784133af46f5f10fe3faf94dbb64d5ae78ca7094ecfc3f4ac73ce5a1fa6ab67f6e64e581999fadacb7c806c2d0dca2042fc24dfe3f1e027b973df2a61a3aaa3f71c424a66081daafaacaa8a37b0aaaa228005aaaa0e55fd307443cb76e7be251e4adb536bd55eb4af881115e8271817987571f194f7cb9904e536ad7aa8901e224a3ea594522a29a5dc531ae44337fa7998e6bc8d7ea807a37a3e34fa2126f4c5a54f57ae104a29fd172b7187e4964c5a8d9a29ff25203f01bf9425263a09aea97ea2dc77ff7ea2dc51aeb3496ce8ed87961041e41622a2e6db974f631b71e987fd624aabf538e4d16fa6a1f47900e229966db3faf9c26a59e6b57ac83296691f10d012f60a71876b5267197d9fedb1414b422c3fd1d057c4888a577ea22e3fd19f5e9106e9771ebffc449ff34232b021cb2efda6cf541a45c31aacd3a8305661599128dfbd46dc319fbea845db53821bfa4318d626b1feb5b6770e1e4f143e7eeaaa3965a25b1671e4258382f541b245970d1485edce94037dff1de69bbe3d530efe9bff66daa17ffbf638130f36f570fbc66b52b96762ce33a936af1a8c9fb4f7782f7ed29e3d20d54f5a3581ad9f900635ab84e5d7be2171d6dd336895db2496bef61a882918f1c42fa48788627d0562caf3120c573eb7623cc51ca2aa56e085785ec6b841e28e18a518058fb7c58e0d5ae2d981559eaf881155c94ff245be7b21256c28614c00888c7fbfbfb42b8f6db5d35e7bd01784133a749f89bffba8d23e08137f2620a07f405481be7bd01744112776e8be20b4073de833f17bde882a08530ea0ef5e07d077cfdf7d459ce04f87ee3d5f91edbb2fc23de88da8a22aaaeac7f8d0c303a2caf33ec4d007e279fed04755f54caae955072f882907d06bbf03e8b5ee833031e877e83ef405a17da61c3e5fdfc4df0e9fafa61d3cafbde7b5ef7e5ac0dbbc7ff160dc559d175551e5d1fc02426a8ec7886c990396b69dbda659e2e048ab26ca68d537312436353128363956b0ac5c75ecba7b943ef9bfd010b98f399e23b7ef1b4f715fec93e7fdfb7cd64f427bc3bee16a9d2db484041b7a8ecb5b316f4cd0739d3ecdb2b09286f9f272bd6fa4dbf03a9a603d54d8b06f62cdf462dff40d6bc53da188d09c52b26466240dfa0ce8091b7d90a19422054aa77cc58a72bdfde67aada5d23ff8e26c274f66fa52d2c91486a3602e59462352f264f9e486f2b603348f1155b040e6c62a9898a982099924b31b6d92b86e8d1b6d62b073aba79886c64649ce0d998606a7869f58c1862d73cf91b4c8df040526a3cd94ac12c039323509eca65668da460924ed0e6b54e82d67d2283f62497d249daadfef2d3f49fa7dec849bb4ac651f107c63a8f4ac5155588d32f9d7526ee7340e15d96ba36495801bb27497d069a42c715c8311a641fad50b417d8f7bfd78651123aa0aa47e3588d59d757a37c01995063b641a19f7cda16954934639695477aca534d83d651ac186356c9c96795381718b90a9b2abe422101d91a01cf52227a9276bec5a9d9d466463b384b564d5b2d16689d8ad29818d364b1c991fef1232b75f5a227488982273a34d13b01bde68d3c40a6e113a5ba847bcf2e534523bfa85305a2e578a462a1702d1577acb4b82991b6d9ab8b9d1a60927973b1c5ec2a06f0488beb2148d4491c624dab0a4f477ec28fb5f7e4858bdbc8c52ca180861dde50d5b74f95861e5546dd185ffa594374958fee8ff430f1a3d685cff9c75002501250125012501250125012501250125012501250125e9ee6e8e99bb298d6c69b3604159b00fc2428d2e402f2b92b939b618638c6f84f987e9eeeeee36783c221d771e209ea68e886356ff524ba73eef729aeff1ac7b37e80cccc55ab9247cdbb60e14e1d5c7c371bd6a14cf667d17d3df8a79c52deeb7f7e799bb5166cfe779ed79560f73778c92888c32ca2861b0b42dc3eaae570dba911dd8b05b9f1f1d0082b906dd79c52c9eed3428032ac2869107203ad028fed7a6841b38b1fdfc71868d99993bd0df2897521af1f81cc5f6b4a7ff90f69c8e8273f96794db3e39768ee5a41ad5aae7233f1f6ffe54cf6763fe6c5c0792209037839a378e39ee744234cc176eeb40ec85dde4f2876838f62c33efe8b44caf74767466b127516e9ec49ed4b09217cfb08b5bad13a7cc29426868a641c7e56e6ee6666eee66eee6e6666ee6e66ee66e6e6ee6666eee66eee6e6666ee6e66ee66e6e6ee6666eee66eee6e6666ee6e66ee66e66666666666e66eee6e6666ee6e66e668e5f9ce109e865638cee39e79c2e7da367b468c5b460dd188bb2736f8c45a172632c8a943b6f8c45d1e27637c6a2c42ed712076c6620538a013b07c1a68999e812e6985dfa5b9cb173abdc193a0dcaa8c86633a4349833434a8374c6944b3f8b2ca23408e5de5c4ae34983f4f4a27de9cf8835a89d5a336a4e33979e5c973e89a29d28a29d258668c24c76dcbf192cf9335a3746fb4d7a9b11a670c525bba7d528f6820b4b3fde6833c4eace1bd2a809575c37a755a35a8990249372e9d378d229a9a23094dcf074e4b49a51737d8872e98cd8a53bbc0862c68b8f2a9bc569e5a996999f58395476c315178805365c719d565ceb86530624480c377e8c1a292b2ea90a576039643b74e5499a6802c594690f4487209b9d79caa44391f9d9d91b51494fb4f2938846d3887c6a444c1aa42c62b52772229aa2a641faf5db13356954143111d1889a50cbadb838d7c74e8976fc44bf6b6f65b6c2b2331e36ae6867734d86e9054a3c06dcca4a4bb4e3a91c37a21d5ea29be52ee59c946a5aad4e786650123303cc33c3ebf3d9368eeb64ba2ee6c492397272c556c4013a12736a753de43db1421ce865b70f753c44f7d40a4000661aa4ad2ee6b384d51ec64fae285a59717f4dd334ed3443049c71219969909e5a0dbe780103064b177362354845be22ca91e32b2bce6ab10c342cd68ac873e4342acc7193634aa398090b894482f182445a119148244b0a8148a48e2403e8b45a61cd6fc5884afefc8a9c587ea2dd813eac31368b40a107ee691571d0d78ab0462efd8d94e3c6533972e4b8b9f44b1d0f1b37474d0b6ccda5e16925f2fc69f5e1c10df96a4fc3090d270dd21fc0751a348dd2be7e4aa349a3f883d160d22827362eada29526b58e60c63959a981e29233829b4b7f05e629a9a22fe24aa3e6e38a38e2f524588db2475a8dfacc9ce89f90506f0f880e0dd29968b5f2414ab9344883f4b46245894e5cc4a4535545dfce7c6bd91a5193cf17da9d4bdfe6c8726e3865b04b7a790a74118cbee849a362855d0ad2dc90e4443473a9e87569dc9cd417d81761cc0112c4dd6d64aac820cd6c5e20e3f6bad6b29cd9cd4e139772130ac50d671577cf577b5d6ef7435ea60f4487203e3c359f3ee8d1a3fb8b2fecd734afb60cd7f34dd9a4d220fdeacd5983f42375d9fe15178d9a06e98aebd215d70a12ba5dfa2bb195289df2e4ac2899b9f4b46a72e98a934b576a2e5d8172e90aecd2af5a9f568d0a69d49c64a846a3a651e194695346a72cc7aceb02e0606b6fe9395b74a9344ccc64366e692f1b18eb32cbcc9cd795a12d98106e4883b708ec8d4c099c9d1bb85826b381cb062c3660dd1feaed21140358143951e8d0908d8a727eff2ef85845009c24f1240a58144f6ed0763935273649c854a1647563154a88b8f1230fcfd5aed7835780e375f38d7a69c2e4b22c1471f40c52ca77fe86f0fb8d77fa674bfefc0de19777083f8c52830d850d43a12e821452803efe06f2a2aa763fe70cfdfca9ea2d7641fc39ad87faa1ec1eb07dbfec80f4578455fd204faaa206f25c15bb086c527e219197cbef7998ee8ec7479326edb77e0ee4b52af499b4e73eaa603a7611f0ef4cda0724fa69ce49697b131ae88006511544f7ced476dc05f17c7d1db82f227f2bc2aafa9d9dc2522a83f097ff89dd0e9ecfc4f533f107a4bee75915bb1de4734c876095ffe659f7d0efad1456fbc27ff110d0800734a0e209273752f104ec2ae1462a9cb0e26a1f6d2cbe49d5b1ed15b633e097e3176195f65e3db14bc9ad9fa20724b22cf8560aeb5f8c600f4e36af199750e45c6fc2ba2e00f9dcb94b77e92ebf21d2dda5bb7c7f59efd6dadd5ccebb4d7937ea77d39aafe7030a8d444fb16c020d11074bc9d2dddd67d54c931ffc960659c62867c85dcbc5386b832597e52fa1c07694097c032b29ff1248e0400183e2090c9a19176984af6e6e7fdc116f8ca1f4d65dfad43a6e7de957ac5cf41457e9936c0dc9ed76a9f26a257df27ccda45f35f2d592a6514d66c9a7afae9c4f6cf2a39473ca66e11003100dba03b9f3397604a2087f7e2b5d6196b260b7f744d7a234aabb188d6a5b0c3f69d0b9662bcdb4adca1a37da5cc90a36e41a4d3648a7e0528c9f0693d830ce580431f5e2a0cd6304a25342fc2413773c0f2f9e6211c49387be90995c23745fc84a5cbfd9be9061703d765905d7bf7e6c84b34cc4e13cd42d58f95ab06c4dfdf109176e4cfd552ec2901b18b6e812c618638c316ea01b1b6d8e98a243a3a2a6bd0e4d7b4da3da0d4dc8478f9af69aa669df1f71680faac26a9a276109263eee8861c629ed9476beb06a7387b5e329a6413ccdc7c18a7b7045c431614bec84cde79d5aad2d9558b3d209634dfad1d73cde8938e67fbc606bbdc1165d6e3438e701eebce2345fbe900b10b100af80e8700356d053158802780550fb847057b7b0fcd463cddddcdddd9371c2433e72e99ceeeeeeaef12a89e5cebfda85f587791f14c6470cac2ba041ea6386edf9807162e7c71bc61a145f1f0ad0ba18ad6380012ec77079064b4cc13c8cb8a39f1986be0cf785918bcbab9813f22ae25c1af2aa7ecc3311077d5e8188d0d2f629a5db94cb1e7d496d8eb8b9b173aed199ddddddccfdb93438a90cfadd1030a2fafcc61c7fdfa49f0b909a09e8e0baf37998308d62fa35efa739690ef134ff0897093f5ffb3e2037bccac2165dbe566b4bb19b48839286693c858178922fa594af84122b630e8da79899b9c52fab16f878c132bd428ae4e9ca7892b546c1faf7bfd8d0a9a8923647481c294056d81c5173e5ffdd420db2c23233337f34784471761f106c01850b1f3517f061736060c86ed9924a2aa99c31eb5384a7b360eae4fa47f762f7e3f978fc0836e416cc5bad96a7e5f13ead176e79cab65aadcf57fde4ade28f5aad5e28659c756075a178b4b021b72a939a16d31b13d830586e005f1662db3e443017ba65cffe823b1329e968d093d0b28b39051bc6588eec6ed4d3041bd3364f10b8d618f5413dcd07e8c353b1d8c7a208ac29095b7377f3e76eceb5e7f2e88f38e24f7a378da3f36e95d33c5cdd3e9c67e33ebe753c3650bd120794524a76699152527727d59b8a1657b46c77a38d1653f8115ad4c052aac5924b2975b9d4857ea04c8d97c0866eb4d1c2b585b537da68c1baa0162b18364e9420a9f0c251b103ae268a2642d051f18318382042088e0a27bcc0010b449003264da298c12c0433a0a2092a6cc0fa5a503f27075d8e14a597249774a34dce111c22148104144588000c76866c89103a6661dc58051152341756c68d5510f1e241844b070c5924e16fb4d1810deebcd1260a8e0c162fb07881f42209e945921749727832ed546b8a99c81f694f7ffeba1be58b0d9d42dd7039d0a969799086c9a811743f5a3a9dfe52da9eae6e8fbbcf1a7fd63ed6d2572f81da175dfcc38299d3b96e0dd788835ffe6c35caf919e4ca91fb413973a412482f92a0c00639d8810c162fb078812d8cd9255d7f1806b4e4dc788d200727f09e0cf3777fe27a77614ca4a4a16910f409a301931bbe8d711d069b3739d74fc04537a8146de9a594923bfe92a79cabb5a5ab2367eb8f2f62ae88df90a00a1355910fc42db6e8812ab6013eb0d2050994b43077943d44124dac54edf1f5400ead9adf3e039c1b6d688e7c37dacc40765ffc5d4620f3b9ebe1a014d8eaa1cd0166f9c30f7431128991ebbafd41c8aefcba856be058d356d8a24b1867cc3c97443f3161a31424ad925f48a45dd7bfea3962e3b5118711ec6c76cf37c20337f9fb0073e71b69ff78874c771a4fe3f6d3488004dd8e35ee16b738675ff7b80b217ac591a3834c1ebf5f7a26ef587363177793dc04552cb6f650ad7a3e5c4888b04a90ec1c06f5eba1c6ad3ceced8167b7f6c039b7720d3ce5268446a5fb4b5929d7f1f01bfa7fded3fd98efb16b40a55fe8bf753cb6afdd8ff9b57570376f88e7e3fd78433c9f8976026efd7a48c0ad2cdb25e887be1ba0b7be46bf1ff3f31441cb4203620a8c3e7088a839803b5f0031a523cc71e77747ac0f4f959a60c3d2ed7ee2403fe736c979c77d9670b6c1eee676cd96950eac3bc129cc34364228f1d982cd0d6395c854878ef9371ad535801af1ea1cd03c280dff7e9e3faf3d77311ed0f32d61bd8afb6ef4697efd6c83933e5f2c6c186377469c3baf56021b6d9e904d7afb9932bd629ba297347cd00725fa100d77e5431d77c5fbe4bfd858abdcec7ef8dd3ed935a06f8ddd038cbc8046882caca00a27b458822ac6d3c6d5ffbcf6c2e30be3d58b44b81bfa4a0d7e3eab9100b46d9d2723b8fd16c1edf371abcfeff14a0dc2b02b3ca5d0313eb3a6c5d88de2da4ba9ea5fe839c2768f4ed3e6ac1faff649b355fba2155bff94314a7f193dcf5187d5de1b55bfb9f7bce6d5777f29995f6808e472d7d39efa7288fcfec2974be4e56a2febfdc1c8d5be5f56d93f67ec1a07ff7e30d23580bee40b04df21b141ff213e41febde7bfc62b3904022bbfa50a5b5b5b7409690ee378aa5955b4326928f42ca167193d0b0caea1499cfff49ff5e7fc5017e6877ea3c1919e8544fa18a3d1932a3f817323116eb47162cadd6c7cc703f440805c045d469fe7bdf8182c2c1faad96a743f3814a2d1e1c0f2a18f9d0bcbfc9100388e3bba07be21d297483f92517a920c8f6f8dd1e8597e9b818b30edfdd3983143869df33f73cee9c528753c406f7f0b00d72c4f954a94d22fc9968e654a9ff4a16c1d61fe4da39a1de03f6f68148a43ea7ef8cf7ff11be0fb22c6bf78fa840e09fd7cdafde0da80fa1746874368f42ca497a718a7be3c1555d55251c7a3ef286cb92111ee967e54fa70be8c1f329f1477989ebe3c41dcc1f2f43716193f59dec4f224d2b3fcd6d3636179d2c3b4697ea5ef21744b1fd6cb7259be547a8e3b98633c6143f7f3316a6043d98a01b3a15c5d96df5e622c593de1e4fa97bef4dcf1b01fbb1e432669caf8c218a46f488c273dfd10e9411d0e31481f0bcb778703cb8f5e86172fe959bc0df02581be30c6832ecbcfaec710d203c177f431e4ea52c96a14e95b35aaf485a4df9e8ebe21a41f3d7dd0e8bb0e07d2a8001cc71d3c86801e08befe34c658e2e9787017319cb021ef5cfad2f5b2b53d1dc2ef79fa1dd7e1c0b1c1ad9de33641b1c1d1e7cdee6384f17d18c38c5b596eed76843ee46ead47d8fa9a0cdaf2f361c0f80b2f5e9017ef8ac701c173d5a81dae425fb3a325fac142dedc8938e4d397a76617e249fe09ae7cd0179688b8920a415f57b2bdf25b3c39d327b97d12896c35285bbe6449f92c05677e52caade5b2fd7ca10572eb4f4a298d5e3dee32cd29b55725e43f3f2044e861b8f3bbce1ff487876175c30f0871671777fbc0e560baaf773f8c78175d5c186f23eec78b43b48fb77a43b4cff483fe7cfac9e7eb61dff9d50643fce13681e869a743848706e77bccb6ddae76beec62427f8088f27ff1a08f3105b217f431de603f2c5dd0f3eb27f7aa331e1ac5f7028d72224034c8c3ca5331c655fc42fa9fd760bc39900e1fd803625ef9f140ef02f746770b909f19cb37cc750b2b7f93948dc059c49f367e373bcf4ba3a6d53eb0c11713d81b2fd5635874d2a0565b6bb0f9c6ad7f37f3c268ba98d95285255dffd8e140bacca4c1c94adc5f2cb9eeda8525dd68e304922b7ffbd0862b0d70258e2b63b872c6e5970c0970ba5114bd4e3e219ee64f8ed2a7f973fb42b6c200f346a33d25e298ef2022d850878a51d1c609d69d1f04f419fb86950f5deeb4eff37578ea455df992e8bbb19f8e1b2a767eb7b666d982e5e4f88b7ce376ff0e62cfaff34e193f6a709f929b53d6e0430fdcc56d8f13db570e911bbf04827de037c5db6d84fd177efa376868540f1f7817f08423eef07cf7c0b7bec7f3f96064752b0f98ec5cfaa546511b26472e7d977e41bdb8347629c8826e3f6873eee3993e1fef10cfd7df3cd3e7fb71c3adef03ccf57c261c6eb8fd375c8f9fd61d6cdd1c638cf1e327d22eeed831b2c6cc517e6b1f4fa5f2e3f10d4e83fc7944845cc0029c93334586840f059474c45b024b0fdeb85ea76cf10183fa11a4e8a8b44d1566450c198d00000008008314002028100c888462c160301ea99ae23b14000e859e4678589b49c32087710a1963002100100300006000044606db00717a79d06bc7bd4a04f2404a6cf04f531b43d1829a4cac527b03fe8c9c34712b7ece8f9860f8a20ea117f96dab79d61aff747f86413a8c25862cc5283099a6ef92b67ae92b03c81bf22b1d795ac2c3439188306b89df4c6258eb0edf37373e8c667a4598735f0c606c5c390dabc33bfa0fe9d63ed948b52b313e54be141298b682660d57f0f0644bd58de6ecf5175ec57badc26c8a18519b086069215125de3959be84388f37b5397050dca6094b692d9ba4b4d076fcd7004f5799132de5e17eb29d9e8ac60c7910a1148be09e4579b84cca06e421390ea83ad44c82c91441e6acfa0b17ab47ee74d65f498ba1840702ecc065e20281224e7bda082791bf3e2a724acacd089ff6854667330ff69655fb5c6472f402839c321e2b21ce18687a7131e8af0b891c09795c1857b862c351d436af2c1d1750ea565745c778311099ccd1007cec36837462f9ef6f69712719fa5926546157c7bcf1c3010fc4c0e0c37891b6e706a9700b7f6c00c8fba607daba9181708e39a093396eb5b397f16a2f9ad1bac475cf509aea3f1a54bc4828bff147becb7be31626d636e398a359a569c3737fd8169dfc4479dbb9c732ec4aa37bb78f17ed6454af29005ac19fb6bf819186c3ec0f76d6e47c0abc36b33472f13a792fd1ee0a258d5f7a48ed4a6fa8d7cfcd2d149157bffdd218a0af1ad94fa4195974542ac58177f8b2ad76d7a9861acd6a802e1814819a30dbac8dac55ae5429ad71c5970ce930e2a817e10fc99155dd9cd8c5a6890315ae7743eacadd217ad7460cf785d536749ab8af2e2284ae6e88699d6e756c456d26a7f916012972a9c01a7701a5a0e9d552475bc8c345c2af969061a995d71a3df8aeadb5e45c3e4bcaf4790d30dc49488d144093fbdea23bff2d3e0aaf01b34b5ac109c4086d543983e93d4780ab57afdd84b5d39daf006fbeb07f69aff4aa2eb887276fdd10f66f25b68a72941da9ce263879d0fd2ef899462e310b4e3807d211672125520a2efb60117448778ec4be842253991abeb21b50804a1dfbca446ce2f094da3a7b2ff70b7e6f98cf35b8da3c5a07b803d97d092686205bee286f8326d9e96f3cd701f929eaa897a4fc084a0bac77ac9647cffe567162884b3d9a1c87879b0af455f1a66446eda17bbeaaa4e95b80aa688ef5acd04e53c3d9266c310543fce054b8a3387a67fab079467614b9c63ffcdc293b8a607740b2b889cf3a066c098b9bbfd093c29d2bf6ba1fc700d00308099e24ae45a6d509c8ca3591a11d3fd88ceb3af0deec0687bd1ab8131dbfebeaccca7a480544afa01c4c29fdc8ee4e171c3899b95084d377c19e3c626eda5bedb6ed327d0230b58904e15f4d0d7485dce74cc094d595659eedec314f4019f20eb891e09617ac897502cbb4de6be35457e9b348d73e61fcba93e5c931eeac4b2d6b498a989c9a2b4daef092e2e552b77a8a0508882fc67f2f0a2ca15b90ee488cb199e8dff828eb787797590fc6b50cc041eb8b78466210000c366b458ab9c45554b4ffa73e0515bd4f1b99a423e7ea8ed144feea3a8256fdef43b34c2f7a7b743181897822a2dee10643aa55076bd52724e481b9a16c900786492d9603b80bfc86be41e068c458d18d209ef4411e38907ed03f041fec746b28ced82119863037624fde21fbdd440f8ff7a5a5c779d7f18d9ec5f12021d473a3babc307c67d20008c67d868f39229f14acf52617d0d06dde2d0b84404192269db5c509c3d7c1e869dc2d97148b073c2bd6a928276225d174f1b0ef6840062f708ae964e8599f80e19e15ee86540348e06158291920eb6f6a2d3e019f72141c0f0db84b6a2978fa2f99d9bdb922481a551a7e1a4aefd3f4fb3cf14274f9dc71cd2e140a3f30c52661915d6fed62e5c2d8999d308687054817feb9cf8521ee55c01cfb5c5d51178ab4fb76c5bf40d2cfd699e09a49d90a059d0c7172cd98ad18f4f727c0752b144c43b016617f5e7b34e42797c432da50750f79171b1ab6d9561b6863fd884c942746ec8758954d70323b5315d01aa1b65920964884f63f38ee7122b26ec295fc5a51d3fbcad708c714556899c8de9e69a14533a73bf4e21cc952f48a72054273ca0c330b949f7ef444a882f2187e4d391af6fd74dd7073219ee71381807d227ccb46287c9feda297abe5c50e04a6ed546ac7a7f37feabd384920a749866f56d6fc2147f94dd0b20f63be10a6ea697497b07563683f07f75adbf01d8a5c42ead8589ea9d7fb9869249d05e5a3b20e70d3281068065a1b00cc0c909c97df169ccbc8dc1d75828c4f680ec4502a3232e45236754aaccb7fae09300b73e598dd699570c3b555b206cc25f35514dbdd8c45e0f5ca71a687f78508a1fe81c15f4ec0744e231d0fba12f834803fa97a5eb86bdeb6eaae85ec41169c1499f2b43b15956d3ff40e30969aad407e280c46d1f4863fca7ffbce5e50fd187c8c7435f66dd428ecbaf9bb4254c63fba47596e0821929ec9d29f47776c9274572d50508fa94192a50c1be6f452d986f29b30d664f597394809a55b6ae2bd39f79f97b5a192fecd0b185a43f778fb9be3a98028db575061905170600891161782784ea975fd48fbfc69d2140f2573371f7d348f7086b567e6409ab2356f45d5b9c396ed848362a32846d286b19344ce5fec4743cbe08219d3448e89ef1ac651058225b0ad4e00b8e8ca81592da51a415fd825e47dd16636fdbd35e651f8220c9fc17d965ac744d932e70b733ca8c12022cb56d4b1940f69d728a24b50ef14f335a91803e63c7a4d3a73f02d17c11f63dcef84d37526b9c12fbab01019255bca36d791e94c64ba1209e01e34a8e3153cce519e20487f0359388ce572626ca9971aace5b38559c718a400e8a5e88115a8f8583ae32fa8266377de5f8f8f70fbbf81d8100bc14fb1b33c4f64eaa46d5b97ec05eec50011d74b740d9bfedce02271e9f594d2a82de0631cd89b4cb6c2d44f1a39fbcaffa91311ecf11b6d5268bce86660410668ba7cc1ab419106ce8ac2874e390716f981754888b1d3890a14ef9957bda34f80ecc563272d98adb900337e5e7fadfbc11093b1817252c87d9866dc0cc86f390422f185debe15447c6427626216ea940347058b15ab17ab9290580362d754148da5fa8c47f5abb5aa5451f4394cec4e502781b4d0436144d9876b426b4de3fb0df1802bc9e357526911d1415af911d29a7b83fb09ca4b7c157d4c0b17229ebf3ef02944c45708102d3004032b8750676b12539f164289314782d4fa8d44185b3f892065af8d68bb87e01549b7d6784d8bdb444d2280e02bf193678f2aa598779c8793fb996a64f16cb57f45b6c50dd08eb97f0a70e90a38fab2fc6957ef43b7d41bebc0a51962fe2be6450fc7586e08b267c5e9e843fc7d4f36125b5d3dda6b9f178780817a02b6f798e6f3c5b6c91608fc275589eeafb1e5e75e8e051c43f966406ee6542452993dd28ef93edadb95a013fb4f46e33949a2efa563db2267aa89859b2b764da60148e04de5c5b302f3a2fa4e743075f597f762ca12b8e67fab321c9eca9db282359a700bbde14593feb5507c5c2e6a245cbefd2168e3571a19af75ad3c33c1d7eecbb68f477e93d4b83875acff96d37226d505108f5429510ad9835faaf7e9e824cf49c218a317e3900e0a110b34b4e7b5759165fb1be5dcb7fcfe33ed7386283f81fbfc53a92cb2a5c5e13bdbadd2392abae3f2fd142c0a5c8fa199b201dfa1cac58bdb8383ddd1958fd9708ad486719d19ab3d662c62e98af1d1410e40c88fd655819fd43c5f0dfdf2d12835b6d0475d9948684d28f17446508a91c03640ed1d5a19c68f373cf4142efd7df082fb95f6511c307c56ebd2a1c9301c8fb8401bb5bc62acf5c27513a035ae37224a6d497ae27bab044c6590985e474d4b31d72cb5b8ac0c725d3e0af5a3dc834c582f7ca27e5c9c73b68702effb3d9ea96c6ba794815c7391a62d722c990f1d7407c0bc5ab7684252713ab24c15ee81e3fe26aed8942826286a50347e351f3fb09f18addd0a914d7c5872ca1270c9bc1b67f52b18dd571f785de22ae1a81e90b8c8b60f4866754109df4c5ab8ae44da64bc50624446a3532e4fe6708386d000c2e55295e8ec93c6e67a38c35e4d042028d89e3911fa70576adc797a1cc50a2eaabae78a1a643f26c44585cbec67b2109fcb8eb4b0037fcf40a891924e3bb1d7a08687ce635dba31e1354b503a3704ef98026b464370b54b7ac5e2181fb13e5fc626c4b5493caa960e3eb2c58a6d1aee0e4f1476a9bcbfbd048264d80c370e5788985606632d29e235f84de69315a2f5666832b9492730c542ba548c5c2c6a11a82f73f724d93e49b2fe14eaf984889f36e8a550dc8d623e1483aef260023f5df3214e2a2572ac59e8c4976ec6e1c5ed10579d9931fa1e18e1e63af31bc4f4a2cfbe67d4cb8b5d268f6d55e81ff8cd4e36da9908810aab7647cc001237fbe47de0ecedc7818f74d44c45d55f1c54b71f27febfc2196c04d3b168d58ccdfe9721af1197ce46c6aaa74b401741c2904c2e8a4057b297656b9137caecefcc590b8356f8225f24f1fc726e96e2ee985d5af83eb4f1ae73c13261c0323888a1856cf3192e9bb47979eb49b9a11cee7881a31854d4bbb4611f071ba65978427e12b1d0a94920002b960c0992a18d2a43f25b240510bf459b4b00f9487615860bfcb0eb79cab63631213c1559d943f8a8d4b0ecdddbfef462543283054205b1a3ab4b8c623e4688a71b65d9b3747cac40fa7381f2fa6c090a016e329eb7ed961fa21ebc2d2259f2b277bbe62613faad7d270b10491da7723082f62799dee038aa08c929b0681578d43b1f897b8786bdf58f3b01cd63c264fff68f6c9dd4882c10259f938ecea0537df56a36e0297d2af854564c3ac7350d9c77d6d1998959142b5f42a919ae21ea65f84430a02acd71936bc260332af1acca57f5740a147976f0ba9f674e0d92b8c480cd912835976aa667d394c575c8f6013a6eff3bfe9fca59fcb102661df7f23a547744dbf0e018b896e827f5d765e6554c1a4273c149fa7922dbd60f0cd24adb18d2cff70dd58e2365acb85cbfa4a33fdab14029b167edbbb1576bccbee83d10183549811ec3d30ce497d910745914166a34c37fcf604450d46c9b3815101c2477016afe80c2e838b0c6463f25a281ea3f0e5ef66bbe0206ecce6caa8a7e83a6bcf2d034f0720630359c99bec9f516da37cb886741f3642f9df2751637887a534bedfcd8d8ff59c2683f0eda017b6f202deafb1b9fff3c3e2bbfb3be3858cc9958fa75da5533f4de294be216a677d1e76459f869a2e639712655f36528c96ca291c01999f3615af0f9b069b151a530a02323d99da449ca21fced214387995060c011d5f8db6b2860476f1c278195f803f7fe4f67d9d3a89ba333197550341c1bad8e03671a5019524ca8223616af2e9c009846edb61ce9b74aa811db0d614c8815e2befae489db987a98b300e3d20446487e1996368ec5c32adb5e8b452ac827fa2434c3a49c440b5f42ca349a745a8ed3cc2c17bd06d5085e016b997d91ef411d24b8f95d606601d9809a60c08471f13b7a9d8ad70ba8094888e44eabf5556235421acf7840e6cbf5d437ca7a717d1d5ebf8b9c15b9cfd6f6512649df4be711545b2d1c738dc3e2481478b2fa8a69d1dcd9976dacf87ac29b65ff1edd26b7ab0362d5f24c05d1be6db7a7bb32ad84082b8dfabf86d952cfe7824ae25d4607b4f688b5c5fb1e3e8cfd7d2ea8226c17a041bc2ac866e1604be24c9e94fffdef592e87dc5a4963ba0ef3dac178ff03e1db447f5b0b85affcf9896fa9b87644bf2f600e4dd8d63e9cd4131d0f79f07bcc2e6de6c9cd63c22cb8c35d11e623c48da295bce9eeedaa32b45b3f8300bfc66c737e4b428d0df5340af21e68a82c27319d6c09cd737da886aa991b68f6c2f9cf26cb4b51eb12b27abb498ac35fd0e4ada4cadf87587bbc4ecdd44f8a86f85f576834fa9de67219940bb581bb03fc9a221597bf44aedbe01927a5012debf71c0eda99bf09593d6ce6ad131a7dc3d36b6a2f4ba286ef33416fd7eb2c9d51ed63d353c3db281055d66a6f6aac030bac403d90e1b7de1715f41d74da738849dfaad29b0d487ce74a452bd8130f493367219bdceff00d4862013b8646d2474a3510204fa5ffb3f90d2017a1610070ce968f5e320d189beef8f4d1426e9f6e93a5d863ede0daf5cc27c6470b81cb310f2dce6172c6d7d75daeeb8b34d6743db72e1f73b65cf2bdd8608f2d7eb7d8b8b3151dffe484562527b283c8ed49f9acc8c0fa681628be06fae93c7921bfdb9fee462438fca5ead64142e636104ee679e3733d5adad2d268b04e4afa302328ef518c4ec68194824651070b814f76bc9e8b322f2fcf6a59333725f35804f4cbcbd89564566a6bacd8ab680cb9339a79350c5984ac088a1a67d41931cec7fa84bec8bb5a95ec66e083862271c62299a9da4fb7cc9abe6faa38f66577799f9615eeb608c1d218f569e4b205d355cb338afd3e8ef2f731c6f9287b8efa3e3320ff1c449c040c5570f67e0444cdc3a0c11430c744f105c3d1ae38d2f045117c19ee3928eeadb3c7ab8445a0bd084cc5e4c03200284fd2f2c9ea6c598b4e6cf7368424a4a1e7ef3f856ca267da9a0e7d1ed02d0d4c3fafc43db1449f7d67bd11dccc5f2c76ab12bc4e95bdf1e4fde6e95c4a8f40ebf777904100ded8140c9e6e1271df2cb85c77dedb8e3bdfc98aec7501b0bcf52d807500f49c2a6bd0b1b503eac085d88ce6c47801070f4d442ec821c80dd42f141bd6b4d358c47b9b31c659297591af1832fe8596cda850e533fdc09d2906001f79cd1a362269be1b1c36ce32d0c0282e8984cdfa6a7982f11c6e4d20f7f0c9e3dfffa864324ef544f5b59cf8c2a6b30ed216bf6807a03bfb79e38a2c2081cfdbcb9225c7bf29109a992b7e5f4d8ea4c6c45cb1c7abe186c42e6b158d80c91c9b542ba9d1f78a93c8f3588379be208dcc16d9c82341b476bec0c2e8ca4819622f9480b70cccd5e26c60e8d3b53eca8ccee42c7e903bbe1c05bacf3df0d6d939e08cca405f66a4ea399fb5dcac265b06298fcdfc20273976ef74714de4ce8e9a1ee7feef484866367c7b09f22a49da19d55c602190cf5491b6acc570dd30c3bc037a7eb9e938101b2413f25e5162a7bce5b3dbb3d22d10a19a2274f0f1e763f3587089a58b044002bb818b5912c8c00be049d960ff0d6235c955592284a333fb5b85550b9d3b126fdd64b5ad6a35f63b4e65d15702cbaef129d1e6e7845235c589d6bd79165b51060c5c2425ee881f1335832154c3282b113a148283fb6c90193d3480a2343d8455a64d343d58a54ef069c8b751487a10582ecd1abeb91426223dfca4bce26fb2c03de9e10750432d0038a2d1bdb74e8881931ef625afdb58f5f49339f688325e3198f4070e484b8786009a59a9386d8df28210520856c52a4213aa4c9c1ce09554a8e373961af925ce750d30951c9f253ef3fb6d838581849a87531ac3ecad81677993539c4019d17e810618765191ef73566e08fb6560916f77fb1176db7eea0ad19815d1a293ae6469a8c2445f1821e97ad0c61bed248c802fc06e854069746d4b61597e5ebe520af9f4acd6aaa0ae0cb8085344d082762fb9b696165cba19162de218e9a6c2a008a21b9a456081a5c1e1baffd36f08e4891db57d4fff53a645e05a6a9be91d993a489a478a8f321df4252f09d59475fe09863bd15be5138f01083e2f6c1851c044f69a447a624bc20b37c7998b13e30fc94fe1e45ee7d6f5fe8320279954a0ab24a20319ebe4a5efa7ca97c6c07539a0beafa7cb426ba08ef806a4ceb438b4e5d0245b6e655cc982d073db9c4b3ff1c948107744bb4996073211f530e59d93b167241e3d8e9b3311745c2b1b63432dee642699b56eb77be8af5c24fba21063b21db520b94a0635d2e49d4ec701d5f2ac2c5f71e8520d99ff5619220f6ac3f75abda189a8854a99f6d80caee362beb6fba3da2c33d1ba9af915ca4d1ca62a152d76e3b1026f6d6566baa98cf4e1b8e1d724c9ed23768a3cac25e7d408ad8af2ac6e7382908f405fecf3aff245689d09605570e221428cffaa8553b00d709a2b91db9e7aa382b7483d886a880da7609cc40f89b65d623649e322ec76b358872a57a122676c503d414ded9bc345ab9dcbb7429d685a3bf47575acc5f2e84a285024eba222ce0d41515022a3b1ac9e18f79838c9c846be214165bd991289c923668604d5d73746d7a0bee0005886973a60b059c75945b2c9f2e541d4827c48a58a7980429dae03eb45f23b906af977e9e37d5daf25cc9a06a80115529c4222421c46d3450ec549bb4fa725deb166efef6b60c580faa16a0a1fd712dbff27853429a4d340da477ee71122705eb8e2a98ed717aef8f8d53bdf04e77160767bfe4d87403f59ab6d8e24a486df9a4a0eecf6cd7762abf95a300a70ffba88c5ea22e23f6c7fd0fffae73f2eb30c424a13202a386b01268fb8a7d148a2f32719e3174510a7361b53af4f29d5902e186e643cd31898f5c52f9a9e7532b2a452b3c7b390f7ffcc335bfdaa6e4d1e2362289c55dd69444aaf31e733fa42b108761b04ae29700a29202ef487bf2a9bce12b6a55a27fdc6fedb5b5cb869c19fcadd4be8dcdb5188e97c18eb2eadf45aa943d898fd06573db382bf6a3e74a054fc7d517a497a6e1d785b30769d2b64475410f488f32a220618b40f09b60488e092e1bc6bb544e8d46b7f977f69f71afad68db8f042723bed1e39dddcaac54e4c4625fb4d7cee7ef13512d7687db0509fb3c0bbf5d1ce7857211eda327123af1a0d5f742b41f6fb2984069af717c361366b902ae3549f6a42804a7cf552d5551b9514f9e1f800dbf5c1fe8564eeac924c4bdc485b5da7a10d210b1c09d8cad079434b43a4342960e0a3478de1f6a700bbe44a82544b12ed1b5a5491cacc59f49b1a43f6e634a4ab66135dfa3e4aefd050c842e3d9014c1a5018bf12633a5af55c56d17ff2583776f9da4f542e4946e665b5f07946a854e49a96a21a0c889118432dc4b28ba4612c79714d6b0bcc0783ec6f4981a686ddd9fe657f5671e5ea1f6a7016777dcc64350f2d6714094470a50bc906caa4bda2be4a482c34fdb2aacc55214a7493c489ff5a41497a171f83c76e9598809b0a369412ff00453def12e50b1d6ec113fca37aa6c3c02aad4950de4a2e68dace5f3e9e9a17debae1d5884ba32e83e0be41be828e4cc04d1b373034eb7dd115fd9935a03817c862fa2db93c3927136f015665d5ad448861e1eaa86564f222ee00ccb5cef8e530bb64ecd74033b91b20d20c2dedefa466f2c9acbcc3b2adc703db39ca90a9da25b868486c3f086dcccbdad120ea583551c2c51b287e40b083915bf9003a401e38cad3e41ae037f2d00ab0b4dcb2ea3a57ddb508ca463eaa7326ccc668775c19a9de3c2a080138d06282b16ee380388703d01dfb1a9ac97cbf73a8445ba45beca401cc9a0aab4e2674f7cdc5faca1399c48b444156821bc1dbeeff41caca868a9307667b3efebfbe5e3627eb6c5b0c2ab0f56b8f41c30abc1049e0b02536e15b38b64245132bc38a23470b6c742e4b1a12a9e6846960915c56461c61ee006610297d98808efb85b281ac141e6d8ebffc655b7eed2cb172e02418958c59015657b42a08ff14740402196ae2183fbc2ced132d9aa832319c81fc55694ff408400b7ce01d7f1666ef8820c67da8d53bab0de1370930d66dc4579700fc1754a5c771f8162f28eb9430dd0d2c6f99c906019fde483955f3c012599ed034a3edf2c28becc77a78742c924986d73fc6fab1f6e1d406b1d37d51116091a688406a684b06fa178e3ef9209c4c6a0918dbdff5c33f0f0bc1cd0173b73f274be41c30ae3dd8bf3d36d004b9f820e6e84f2f97d746143201b5446f86468f6b4dc2aa383bf0b1fe6c5c1f05c0d8ff90ae923e8dbc73603efc18701bfaec888ca13bb15f591e92e5875432c2221833b69d0ebff2e6c2413873b0fd343c774d3e2bc06ab6c784c530274caa792543771135318e622af41141d5c74dd989a40331ef7545d1b3f9e2355dcdad7c40d6ffefa414bdd05ef32f4fa9c7d9ec6264f45469495c77340ba77b628b58edc3b82e84a9191f1545abf99d5e58a4c17ffca2799c6f9130bd5d09a1a5030581fea6a42fd1f72db33ed5a1ab01620d8e53e776fc03e767bf430282ccc103ddb2f362e2dd9f394cc3a958f6d0a6f2754ec421b009b6c12458e41a53599942896ef2b8901741ce62347be4369b8485d3173c13812b354d7ca1bcf23faa629fe6bea7be97d8c71404685d3cf671526a2fe88da00f3c50248bd6269ac4f4a59088506d4506383173281efbe0a1aa862e815a4e1a8f2f21f6cd40d7f060f11fa0eda8e60691e17aa108f1991e43e97aa3f7b1a431ffca8f91bd6eb7bb46770357a9e10b5a97fe3ea5462df32fc643a8c11887aa117118ea32938b1949316383225ed464c0c23f40737c804f5c2a380361a10fd8673a68314c8a06240e29b3135dd6c3e198a99af8188410e9c67ddadb2351beeb991ae405ce3bb2e698daabac24c20557b68b8244eccd4c39ddaa13d8ed2d1c3293ba338b06fb8e68ebfd12f42c2b11f0d9494488195c539bb4d58a4381f4d9e752ad56f2508a034e63c381beae8a7816970902f8ecb4a15745064169f18c8b40353f2da2e970d6aeb2d57da45ef7fee6ca048ae96185604603a437f5977c4efd726d7ce73d62da63a58d650f3045d7f879c95f40221115837e4ac14740fba1de54b3f89a22b5fa32fb39fad7d5f20a15d78fa0afcc2acc7417e6b8fbfa9586324c4e402d135bb05ac3e263405b48fb766dc88461fa3e7201548b6040856336eb53a1e757798dabcf3f544ac6bb2c7b1cf42e7dd30ab70c12e90646a332885eb6f313b23d14db54166a3c6167a0a091fd9569ed2370de6f51ba336def382e4679377fe95517d0f0327a189696dbc1614f30405fcce56d3491b30e5d740e7a07816abdb2794204943e54069b49b987cba8c429475409fabfa64da29c2ca1f3badc67f7ce6e90a701b06f2338b4372c1dcc5582123580ee56941963c34f7663d3f3b64ce940fd4a9d5dd04583bf144617d3db87fd227c0703e160f89514a985d3ecc14983b16320ed64f068d6c9ec19b77f9bac901748ee955d2c73e86feefb976af81d11079325b7913ee7da5ce8dc7324d45d52aa400f00e13590206ed80fbd0348d01ed745f8c261ff578537003a6c54a130c9068cb134c6326e772da34c66f7c2de869ef516824d46f2081a1750d0c6dedbffe6b8bce53c74031fac560dcaea95cf467dac9559d17710cbaabfc5a45be60d374577605821141eae0080e97f880d100647039f9d63727fbca2385a15f478792e255ef083079fe3d0129b30647129fcecf92cc9eb429e297c146f94cd60a439a6308b20756305dc93f0f24fabbb0fa2c1c407b2165422e8b0ab3ff6b16e1d2a023fdcbb7ece9547d0e0dfea2b98004e7492983957ae289f27ec6907068a1b9172d421f5a40de6c8f9593d431e7bb50f4a03780dbedb0bf1a51cdce0dafe107e9fc2d8f75499164db19bcf3b2be1e9949b0e2b5c60709a3075449bad36f2d728b8a5d7a3654b9c6203cd868c7f5fa69ab0adbcb239377ed71d5449f8fa5cfdedf98c034ad45204b878df44c5912ce360758bac6921dda1c06e217095f9049eb9580e2cc3960c461f37484d782863002dab7fb4584006a962b81b17e9da712d56cb958e50f7a71164a544e46479750f6b5be73687d22ec2d8866d97ae91e242b7aba75abb2ff0ea15e404325296d8714e2c018227a2153e26ae85ea83e46d0528151de8b550f5f4dcbf0b4ffb0e489a1d81c3883985753fa3f465e56c9ad0309987ce6b905e689ce85963a33ec0712f9ec0821c8f747065f8f84979373e51bf363343d07d658a6e4f38c45d3647fd3d21f4d77f6fd41e4236b46c77c27d44d8861f02bfe9d9e92de1853a98761b901d2fa2e13f86938c61734ca9a5c1189b6588e9dcb501fd38746e55de2d3423e48301deaf845fb835a79abb69b9c2f6e1272a44671e9e929d8a364c409d41e7691c894619e0da27372f0092843df9610641876c67f394f1017c4534280012a65a14d8113b1ed9fe5eee98427b8afe7cd97bbda5fff77034729b26d9e320019101c8f6c9a0e0ce9335aa503d3d31b4c8f66b83022269cbaf18d75e9121bdd4bc9b485bec9c71b62b135cdebeab595608b49f24866bab91c61b99c0014dd63ed13d0320061e0416fd59da0ac954d6165c10918ad53c969abb68570d85183fe5b1704d81a36a8fc55e1d638144383c5034892e1f1fd7fbfa952c72745b1b8c52636433defd9d87767de94a5684109ebf25cfebbd56bb1a3a03e030dcd43a5960600ffee23ce9437ecc9b755d34c5f492402509898dedf98d6ea5b4ff03d94f38f4401fa0ce38b6c715b9f7ac118212c57ac5a0eb6f5b64ab2645045def01e3897eb3c8d066f3eec3e0b81e38c8a0f5c1152915fd6ae329c2001035d627e886085216a26a406f71ca6d640f7a45bfed7f44f21e8d585eafe57177c8bd0cc440b867c80c25c83b87d53bc25a75bdb8be8e44d8035b5e3130f180e08a737545a108eda75c1ba49b9131f4951b9bbd2529bba4c866d432a5425540470c263004f18272dd65fcd0fedc6e54c3f4963e9e89eeda09762c532f571d71080e969e1db75c79b685aa65f52defa56a12fe9b680ec5457dab5e9b0f8a4e384215554ec0534426ad733313fa91a7b3d9c1398eca62fb712559105f81e8a13354f4e402fa4c595fa55d07c45cf555487603bd41b38e95a8da7e54f2a9d91d1c6f941e377f0b6da04ec1d85221600b065b22073224e6825cc56f3ebac4c9f403c2dc24556879692d2e3602446110ca9c1d421a3adc17db2c38a43aa67cb920d84c5bba83b317628fd70a7df25435c48740015bd2ffd89678b9e318b11aa0e3089f4820212019905cd91f4bd3e88d48111545b52d95cc4e22baf2e2aad111ca2a6cfc9961f63d85f514cf6e7e1ddb7d31e86232f12af51757c1b80cf0495222c3fee069413b052b61ad4a90c92ef102dd7603a22f7de0602751b20893bd112fa87ec2ccda9f1e7167460d4ebbadba4689d29d2da2b45be8eb118bc7a44d7f9bf47eb4e6ab7d9a41cf68ec3843516ecb821b7260a9ce122e031ded2dcf3b95eba7e150223d8ac562b1c2dc98e7e099a1940abbd2e0188b84d9c719e38b5b9bdda173fa8f735c8036045b1b0a29ce138cc81fe35b195c831f24354f651b9128b0703042f89a04bd718ad1ff865622ec443814ebfd4c62a1c30732696bcbf7dff77ff563380ab09178fa1eef205a92f3e890eb44d7b2124fb40c0442dfd0a5751589dddbef2e2925e4fe44a6f9d84beb9262166cc744a43b410c8acf9041c0c925cb61aebf980edda2c8a908b3ead2850fb9fec8df187af33bf08ea22578fa8e6ff2ff5f7d23cbc6e643a19c6b540555066b54466471f1241a507241db97084a96850277210a0028b0137d09536765352dfb2db79222ccfa6e855b8a62975749bd008e0d6400d164519519fa281584ccda302d08165d498564df46a4daa7d87ea33b78386a90d666b20f67134a8de9959a700c023908a0bf62175aee16c308941fb542170eca38e82b97c7865f51dd1e1861b36dde92601a4723bbe1c5ff5a64221710f709304968eb4e060ab1650f6926fe26104445f3d4199227bd35bd4f007984cdc2f00c8959c8ba8c9b5e39e08a12c1c9c7c39f92cc388f22e49b6606ac2a44ca0c8f7a0dde3b92abaf666660cf85396e2636286a16ec7d37991bd360f6c2d1edb77d64754f34ac00747076ecce7d4ce3a1ef2d9f9e316602f21c9a4a6b949628dcb58787f463c8b209c33041a807cc0f28d3b9e9a3b6ffe1d7b41463773f28690a01079a0bb878a9b115ffd280820d016842792acf475400cbe098711ecec890b5a7529b39221d29418095fc466d19f9391d38ab9f9f0921cff4f0cc34397b948c8d18534fc845028b6c22a82ec24d12193280b4b87fb31826a60fe2cfee57bdcf6888e33e11aab03a0514ca83da230820b077201a0e6a6874578980c5a68a282816388606cc384f149042fd198941cc4e0d3ae7faff2cc3eceb237a925c6aac709a1a7294ee8dac80e2464aa6c99df2e2ecb066383e1a96ef80d596bdb00646505a6b24fe9a4b9db0743e48a0de3e5766a26c631cb2b0f2b576e61dc6490e6e33f65153174bdf00439fec8676997a04ecefd392e1de382930f6b7043ad12a9966c5463e16ac9754c320506cd98c963b7192d0ea5f969066d88b92691a7593a24c8e769ecc6cade9d105b538f88801ad9b5852a788b39ad11f812135db34256bae283071a1e0cb12f555344016f93c682cbab9879b020e92449561cdfae3a76b220022905f15f46c5611d47dadb75a7ebdb69e4e81c27e437d499ad45103952f93aea4ad9e2394ee79ec10753cafe11a4242b303029e0846402074e1cbca2d4432b3eb23b05464ba0be3b0951b2848d556184082c9f81200eb318252934a57a231ca8278710fcb8f5ab7ecbbd28a60074a86f7cb4420673bfb15c812a9545513e5aa317d3c8550da6e30d8a7ab6258fc44da820a83e8fff7cba1d412d250ab700842b29c2f6d85c047ec05b560099ed2122c877179389d528109ec5d558b40564c0c36f6b668dce120b0ce762ec1816df091e749cb89c078a1945bac9af5601725d931fff75bd8b23223bb540151b41362b8a876571c0aace4b03291bcedbfb4699ffeece424b6c319844351151a29b43caa9274ec4f291f9cc2155fa489b7da82ce245fe47b6978dcea995902ca5ad85dc2cf733503a61b1401f6de6ceaf63104e1f7b44306903757e6daa0a1339a5d533fe83154505e8587f0fc38470cd2d2a2d0971e3b61c296e260d67b58eac32c7751a8e535fa6b4e8de8329256859da5daf6063a5805b8299cd48101b1b143c4cec36ab2911dae5468f5e07f6a4969568fc2ea57b8c9920c3bb828a01372f75b2d7443128831f89d46cb49367a019b1e260f548f39d14f5190b7095391a35e2f98e532cfe4b1ed787860008f5f1b92e9e980292cfbf228473469ee5510be367badadb335c24eba59b1f5c804e93a9f7dabd62a03f7c0c40bedd64c5eadf4e5a6f777b132aae8969ef9f4bc3c6ad13843beaf74384938ad5ebba29f60ca14596a56e53d32652660ffab63b5137b77c469ae1048267a4c49bafba6e65c86f4d807ea54a7fff61bb1c25dfee462faeedd39b7ace01ed7a6ca361d974ed9002d7ecaee5f6b65e4b3c89714eec845ac33fa58e4c8175ec9b54a014ab7a960fd2e372e32f4bf2014ee78db32d2e5e7541f9612ead550986ca7aa398594f3f1b136b13d614c73ab8a9b010cc39b9120028b8c197ac92fac7834668770c4ac2b6fe22d01d4ffe6756628a8cb7e6d50cef41cd32e2075e3b2e320d9fdcdb3024643248c01eff8907bff0357eafd00664faa6ccb3043c382c61e896ed1c4a034a3cf9936ec98adce8288749f6f06d82768b5d3b365774f74645b991a91ec4b7f16546ab837f326dd786fa8b9dd67378833d8b6fd8e44cc45ab274e3f805e6da2a1c4ba88708944ebf390acf7588fd1847451b2f6afe7542ba0ac4f650bfa34812029c5998b3ebca56337f2c30e19d4c07481db2248c8d4f88009d2ceeeca02537d43a3c6dccd8476b72dec091480eec7fb4c52d9aaa8fd71974c8581e4a10ea19210955f7e21b1d55827c83a6a6f51cc01780d0bf9f30b5ad2a7307f17b408f1791606b389c6f04187258ae331c47834f1bd5cb862f7d4ffa57472961fbc1dc8f53312201dad4220d0a0696b7397e5d525b616ba838a49cee8950250d42016d182318fe3b135e14461e23fcf4c687ec46049c6dc9401c8c9335aafeae0abc609bdf7666ce1a41e781e464d627bcfd8c83f7440135c33b11d799bd2c10711a30758b7ccd8931eb4f8c59bc04c2dba16960eff9571849d76cbdde44e3fffbe2217f7f48e21391e25fc3bd10874e731b65d3c0e95d430f2c4257175579547fa338e75499e76aac42194c801a05948044d43f713b807d8837451453c900b28d4329a076a9d635b73f43cf71f661b99626c2acc5465f7ebc5eee506c05d19904371fd9f8874937935b3e7e86f75bdf62a692ba644fc50dbfd0bef6309b2d83adf85030850bd55b45d773b9b0103fc3bec055c424b16bb9a5339fa0be8c3de1bcee6d18915420bae3ebc1686d9a7376a35f90609bf9238ed244a91885e337086c0efb7c4de0c4badfbc94c1cac74c3aedce64103e2e6da744a18dbd51627d9a4d676fda110cd94d7e1e3d3b67b36eb44cde41ce0bc42f793737b042ec78264bdf06a0227eb58391dad42903a4506471072b7a719ba802b3dede46a8a2f90c7a10bcd2921bafb34796d4c4c115f7402ab00abe324e13c0beec57e8ce7cdf9093404b78a0b6c0681ab328933c5c1ba8cf20ef3e122f1f9f83722de9fa5de6b1d9d64bbb11cd7188490e5166811ab86ab7ec353cc8b4db255dce84cef5482334305d3626c767e60a3a3c73035486249cb723474e313fa73572a0425cd909a40813832ad589b2a1b49c4623717368c532944ad042929327e013e68ad7c3d4ae82b0c032a34968fcdab1ddc6d6dd50999254ee15427b2f7ef4df2dcd90e48b7bfc314c251992169f7cc43646c4c6878d9f2d93cbfe45a12a75be4444f778b1ef6c90c98047871426982779a1521c98add64f050cbe3890958ed2d53f4136c8accbc3d7e9fddb206df917b423bee71c2c239fd5eca4c09e9f3157527792b97f321034c9c30c52781850b88887d72d7667c061eeb7db07979b7aaee72019a4487e504eed229fe677792fc4783bc977a405d98912dba119682947af4e6fcdfd85a546438bfbc27fec0dee7b4a5a385ebf07547cd73552161b6a0dfea60d9d8983a88ef1a22d5ed08f64f96024c410bf32295fbf1a59f073a71655e033801acb2251f6db25cd85c9d2fd796a249907bc7270eda4454c31316fb3975339ee5d91f34375177e071725272f2425cb11560785be6b54f400ffd2af96804a06a468f70f596e39d89c7abeb7c0f2e8b2c99085c91238e163681acc1b9dc71b0cf1c93f9312ccb333099a917ef2fdbb6be6d877da3884cebee924317b042e30b785f2d218dc8934dc26fc88aea25eb650162f67b0ff56168d9c9350b4293719618ca229b07c03144e5e29756ce168f65d00348697773a44c2f503c72070ea5d96546b1cb8079c47b59a00c12297dcc3758e4981cc2d9135f4d83308caedc48a1713deb08beadc0e86c829cf32ba773630eec70c89690726099ca380d6c572fbb372a14c3c82f7e169e4dcb1523badf97492996b834acb9fd234166185c1a16e1a12fbb4eb4a1c75b28174c35f384970a5b593c4919564a533fea0d2539298e1ec74c771c19aecadd7301d9aecaec5ceef0ae6b3ecdfacd2886bd7fd2c1ff66f06b65b137c31c0c16a4f1f4b0c85cdfc20696a5bf1cd9149e5294bb72e0cc2539ff15f8f55b6c8a4d8e8e30a63bf791d6a41f68632d6b63a0db8385519e0b2e26f46a52513bdabcc78f5d6feb538ae4e592c1878c5e09dbff69ccbfe202a6bbd3dc918c266395c3f4d6b2dc09f89d4c67a6abcc0bfefec666c7d6dd502d0bb7b81702db0ec0f4159c286859e0a24889cf089c13f5d63c2661ae51ad23e31ea4a19c69162c3186de6e0c7dcc9e2cba4d57cece27176662bc320340a2dbe9d0761b7f19b652990cf44adf64ff2ac3d32ef7802d2159f1cfc021bf3e0aa46dd9823403f0f65249563627fd9f6ef5760a10a916506802d5c2c6ecf1731ec8fd16c18a7585b0d6f41c061ccc54977968b7ea923de7f5641eb5a330d33356911156a586c736d08b0329b4928dbd1877f530b33e1e7097c05f66c6e7592e12c6450e9d03cf56e272826c5d3d5f9bc1153e5bfa392f708a2bccf45e469c48c49c57671f1338f02f0c9804046d076df9c256656112f9bd101603d5fc3fa3a1c0022f2be34e4f22df6d79c13fac30fa167e4de7b7eda2b59b12adfccc99dad401b807a8dbd1b550f1c4911027d0780d7b40e71cbf48296fe4008e9038b4ea1887e0bdd7a27c463dbedda5690ace959dd839299f9c56f7cf41781372e24425aab3ddbd9d06ed4a4af96fdc948eb7d0be1e6d7afd124d82bfdaf61445195253c67fe22cd3e8edd5ef33434ca30ffc61d3dd13ae8ffb2a8e97c6a41ccbc0f253191e420bf3982f267d1f70f347bf4c2cf50462a7d303f071f34f07aeb3ab01968466af8c1697539569a413fca4652dbb5da2fcec288f6cfec7a6c8e8d95632c25d73c074d861749ef3e727d3062680108e22efcc2890631f92c40032f9375c9a66b749be65131d45eb5c1cfd1a21aebdf3d8b10605288ec4e04c1e7ecbd52671f0d1006be3195d3fbf02e8c0854dca80b716a6c1f665c26ec848fa535ee174dc386cdef40f18ab1910e4ae03184836fef8f65111e4d15ce0aa04a3ba05f24013ad4067b4ced54732f49ce7008b325aaf5b13280a9818dfc8366cc9620edecd1bfe957e41fb8c2a2e76b5d086a41818f0ded9e265274806da0fd51a46b1ad0e40c0ff88524c9e6c1f935c82f01e02a466065680b5d14b148ca2fc18edf4c5b5a40517ea2dab2bd77e2d8360e1ea8003f864aed89127c046906f6edd6a24a46a3611f101edfc2992ab3309eb2e851c715e7d1acaa3f7cc79dd1ff558bb67e2eb37bdc75af16d3e0156734aee0f1d85866f5508d42bab6d0f124544691f1047cce69501014d4fe388e944cc72e8f6897b88bd8f46af81747ead7506c3c02576a18973d53a66c03997750fd69e1cdbe0083b33ca7c11b87ea40f5b60ee895dc41b4bc68022048d9939a93564f1f196b4f63b324f7b3e5835b1fdf875208d4786e23f4cfc051edc4ee4c9e951d697105726083f1bb89b78916723bf046f50a5786198003215c79abb8f895425b4bca8b4bb63121614d108bf89788d9df7fdc2202cb4a82228edad0cfeb837611dd91936c663b4e5218689803a925fdd5d1df0929e088bd90edd0bc0ac492cc58fef28639a5e17083db853a593ddb4befc03d6184b19822fd0afeeb7c9251862760dfd84fbd8c7dfb072718898278ca5c64a3ecd72643640971eec6ba5a11b11b481bc9de74e1065621dc6d34f102f3c64af0221aa7371ad5a9e574e8557a2439f8710d2ccfa048ceb2b19f5c234f992e0029ab13d75764f30ed0ed7b509333857a0c08ffe301db350ccb28c1a3b5b39fa1203684b412cea27dec5ea81901f3493c100be0a0ea39e97dc737f27de91aefa42daa3062126b766c0a75416a9b8f18c4bd9ea726f2c8c4d70f97eeceffeda01a3a7599c25b322e810a12d585a20a402518c0542567d568e27ed6f4c95106e7bbcd7566947fb82051a1d00830230bd7866c3b6a3b39b0b6640a420b56cf3dfbfa7637c337ef8953ff783a0eb1bbab3179dfd4fdc12231e76c98145f8ab7638e6ccd68be8eb598dea1a6d15f4f1963bc3415140a7e1845da564068daa1435cdfade09b72ae532ef7d425019aba0816a5ed7c503bcda72f806663d8d0a84de4fb3e9afa61a45beb99e67546e41f90fb4551a0a663f61622d4fa986f2972026928589b73f3f806986db5084efa750c0c30932735675f4a083cb37f2fe4bb43155c9cfb49591a93b5978e4c9b75dc3722ebf0de462a54785b9b65843b5b3ea77f75563a5f08291b2a5e447461cef7c0bd8097785becbaea515c1aed86427c92dd8686a2595bbe77155b4b194773c01e9a6475d17f262b35b5c0520e140b2d60a25eb0c46146e8279da817431a0ecc6aa4963b62c8fae9d494ce4a20584278b98757b1230ac0d45a52cd90d4a1045e7817042dd7fc6cd1dcc922c91011e7cdb638bb0cf3432ee1a5dca7a6e5338017af3ee386e26f8b5c3f5df35af5a9ce400df75065c736880035cccd8740e3b34dfda2f153e70ea80c9a12827f6f92802726439ee23ca32212960a30cac17d31b50a765cc30c1e9a3ae95c41115a6dad9d164b7ef7b7fc61e2838c0bff50bbef4ca88263b814cbae1757f425fec41f16cfe92e6f8f2e12bae6841e7f52095b2b4b60d22d91734b686d95dc2f216339bf5381957be3c06abc2f1bdc25fabc4553c4a0b5df00860477cd7066d82adce46b64d268c9804174d812286a1ad94534f08f449a1cb7a039097e3764093e022af0d61bc7819379364fb94227201a2f2576efaab731650b8f3ea593f72fd4cec7195d3584f5c9922585dae0ff0fd6fc480cf45889251848c95901f71d1bf2e701f3eaeda90aee77d475c247fdaa49f24528b2a222e4a80bbdbe960436a1f4420b0774917755d0e8c6d199da27b760d2e19cee6c47b157ffb934c76b52762347cd0173aa748d7242cc2eff80778f990d343dd3a2bd28f9e8cedfff80e51751bb68b194b66c5e4d5700cdc54781af06bf14f25a6a7e47cf1c42337f397aecfecc586201c8f9f01ee529e06ee3909ae9b040fe41557b24840f51d19adca84e95931c8b20aa537ada1980116562ea7f4b40d72398c93e540ee5d187018214444764e820c9c5c0ce5673e5071b263c963b0480a2d2652559e429dc810f272d8982e745f1c54aaa311b28fa3f676381e6c11db00871a3f498dcffc8b829b0bd06f15a03f047ed90103ef30edb90e851eca672fe338ac2b513a88586ba4b708a12da1132836e916a85370299d62397717e4f84882e5324ab33c52abb999ff31281733d0063feb38fbd31b6991d50897c4349b723b9494243e01c69aca7f323a4efacad0fd5407fa682b17ddeaee73f2f010c63c582571a2ca33111cb249548ad6cab3b2a21dab69f9f860f69dcd954b9224d94c980199f706b6c09a2da476843b79830c2dbd3ffedd05ac323695daa8e35af652e698903cd5f15468beec7867150f63c44e83a6595ebb45029bee364b00eb38a926e402b602a89d200cb93bd11b5992b90fdd2cb30a8f73fe0c839a8f0f4eb0a7edb075440bf79c6ea579d96cd491ddb5a31fb707cec1fee7b4e59adf8650ee615eb7602188cb189ca2605faeee594af7c069e99419b96c7909eacdbe6917b8b64b6e38d71b9cb6c43d153b5cd900a4744496ca6a156029a345794db44fd2ee7d7ab084d7de17f002a71eabb2d297ba9c179f4178831fd0b6a4fb4c1d448608974c389bac7d5dff69a8089fc68b3bb9daee14738ceee55425795e758d6bbd461f15315ee05278fe22509773f8739d6cfab32b0bcf3a6422973ec2b5fcd61e2213469c4fdcabbaf4c60ae92718368df39b4db2c65b692fbd891207a69ea33a99ef2a69a04e1dffac2f04fbec66aa5f2c100358b1a1604d90589ca973b3233bdb19cf492dd150236f6162eac056b056ae628b965d8db91aa343396ffae39fdc8cc72f2ab5a588e99798b8e16260b1e0095790174123ac79759bdd035abb1e28cbb235de17fd3101393bab876aeb436d79e41c29a4fda47289ffa24c60fdb6da8b1727bc7b1ed941eca38f6d1b23a548b62a35baa693a528f8074a7c903c890c458ef3a1c1a58c25a0cf8a76c7b5e78b613fbd9cac24bb7f6e32513f611c7fa60e5e042cd4537fe45ea18f741127883ce9c0f805d86655e8d354f0a9f79561e0f80e261403558d371dfaafbd7ac5e6774b9274abac68f0ce4fde1ddab9406511679141b5eea020093aa7023d65aa8588b2ffc035ca5dc38039f75026de894a0106d5744f48d265720b9065e0a758f2fe7b528f6b350af1043b41c9b6a824d84a7b5e6809d093fc8093e920ce6381e901973d24d658cdf0b2d30616e5ba0a6892a0a11919ed6be8244c76c65501ab5d4ce3a8a79b0de3a991bb7fbf5f2bb444bae84a493b902931879b182e12dbf7c0da04287877aa4f878ee43c8125f15954292b8eccf3dc7abd66d0ea6dbc684442b9551c0642c090b89192f7045b329f91fb1b347d0f8d93337ad4339d67093e652441ab92589f81062bf7e0a5aeea70895b7f299628427518f80c78b9879f3fc866e79e37ed5a297d5f115a293d7aadd1fd4f057f52ac102456aad3a86b3ec294ce6b1f54e7ce7c059fe7f0d16a005ec66157936a1db00323616eb2a1767fa2bd9d378639d0ad45ec5a0cebc9835bb6aae412912da2e4a997f2b689785af7bea73aa217b62dfe664042302a1fbcc9023527cead1b78614f419b1392f7371b9cdea014a67dc6541bca4f379ba850c38d9c1d89a67b865f998c29362a052bf4928f917e4c01ac7695c02f2b38fd39e741f6656477760bb4bc61b3e803a231011c426206680fdf28ef6fa1f6ccfd260de1ebdf515c64d82081e8242f3d1b7135fa8d3230e8153d9cb7a0799f0005a6fbec28f503b4d087b8794f5c32d39b378736e6720e8a4cef4ba16940fe80e75326d43b345e8f13265ab1e6530e0530a726cbe30f1b721e7537e72296d594caf5271bcc98f084111f3b49680a0bc80074726ff8cea36ca15278d8157e072174422a06cb78710fa72f9139d75b2c39e71b3fb0dec835f91d30357f98c201eaf34f2f9e623a0bbacd195d5b7138d909de3250e11d945bab45e37c396e4c4877cce81a1470cb21ab200d7ae655144582c2051cd180032bd8d69bedddd60eb7b6703874a3e4ce8675645820153041c57811d4b49024eca3f84fc7e63d11ddd9e10a5023b0d99139981daeabfcdb3a6da028c14357cc60e69699e3cc4546b7d274cfcf412366b6cc4721c6ed91805fdbe53442ef887ee4808d7601349df42a618221995df9ce55880cffaf50d47634c631902221cf9773f61080d480415c9e5421154f439b262688b9a8694d856fe26530c446bc5896e4e3c7a21e6690a141b0171f898d1ca1a82db84f028cd343c2d3a2dac6aef92ef9de93e4949aa4264b6196765370af0e2c288c07df84bc3729c0137a5d7f544c2271522585c88a094ab8fbe32947d4f7c3244bc2c5731049934f3223fa51d6d6c5b27a422292c833efd9cc68bb5d3fdaca317e9eb158665d9c8369d8732652e530a28f19e748567788292d19ead0c2e220e50203400413463b230a61bdb264224dfdde28f62beecc96e3a8d4a47e63a16862ad80d3f6f02ab62260027bb61f083e69293cb048c5cc9a2eaba72fab560e290842a73ecdc333fa28c1d997bcab6266a47df08997ed8de17f1bd80ca59573db63bfc05dd84202c7949340e13cd23c2ea19a2f7cd291d5c06f633ab9925e45a46b758a36e1d2fa68527967499a2b7c91f9832c6ed0085610e8baa7413f744d7c400c086286f479e745daff03f738226424fd60d61d260ef57ce9af90d9d379d5ad499bfd7572a9aca104df3a19193fe0549e476847dcbc7da7d9ea4075648a69649d4086bfe2779b38f44d9d5ba7ad5b1ae95e69a62d79144ea8d9be07a5e2b374096b9159988707222e63902d9b58d42c1805073c3aa7ce551e2bd7dc50d559b85886494decad104c56b2bc5550b02a92bc7c057f6f8ff0192d2d2dbb386758bab22f2863260d78a641ddfbbc62cb04898bbc62e244d1f3028cff34b7d52f5824e69d857702cc09e7e97b1b9f15a2a395b86374b5c8c92a2c6250b8f9bc394d98a310459fe898a8175b8bb9575b7983aa63f37b2b7d55822e191fbc495a743bab411a46a735b91ce163d3abf3eacf65132baf5905b8d7825e38e6dd112dcb3458608e3faefd4479566d3916617a62cb7ce7e5f8ef522f4264d44e91a333acb574f06e6c7d4e3e2be4241f28f28a9659374dd041c661d77217c4349bf9dfb6c5068a17c51db16f5b1407ac6e406d36d09b7e33d1a7134ac75105c9c404346006da32cf21441789462dd69dd390b5c4931489cfa0f77b439c72a00a685f83b5f08ace2bcd82b426fc659735f67f4709e74eda7ade558bcbc1adc3f252f7dc0e6437b93f813995350c8f2c93b169756825c8cdf416e6cd341bd4c711bc8186ddc4e55630f04c3f365e4708607e6b615600e96b2fe0e61c1c51df2121dfd5ccdbec359e344de90d9d51fe5e13b8273ac5c2c4ab6a1a34d587141154ebe4e54cfb20e64d7f29229a8b2f21d7c48f3bf55aeae7f96a3984df20d5e25cbd7a045e18c7608920b10c1237ed147a7be297ce6ed869c050709d1065050713a68bca86883f71275f66079a78c6863aedef9deb0bfd17b43235e14437a255aba60288ab63b3805a44e812181890734b3c606844075238b5afdecd20101281a8a4b42317cb782901e7b26bcad1d7bb0bf93fd2de52c18555ace66053175bd19aacf81c422092302b837b084698f4387c213b0f2cdadbeaa9b91733b917561cdc750c8cd18c548fba55078eac53e12ba426bf0b7c173ac379472082c3df4f9a695ac683c943487c951936da05c30bc92dd0f4e5062d40cabd3778da5d26394236347d5ea4e3b6d74138eca0ca160a1b8378eb9fbda3a698c391107d4a8024c756bc15c821bd8ab0a895f0d18019db60f96c4310d62daef2c40ad47b62727e25d4149717a3d077605d5c1aa09395418e7ca512efbd8d37518b81c5e17f77a607b487aa0233370d16402cfe47f1f617d0ca71608b836c15544c992d5eee3841877242d1005069046d7055980fd4d949f58b200e0be8ab0ee8750c61f7a1a6707b12f49ecb233678ad562fdfeea7376b870e88094be2daf3ed2bd1db9bd6d08bfd8c55e587d303e62e68d8bf5702000e88bedee846b0dada7a66c5a037766a9ab5b5224da740a951c5c522ea82a8c9c64786b9c07c0f342e228942fdbfd4176c6598f2da1e20ad73f11f8af7016593243098a14f5bdc0276ec65f306286b7fd8078f960bfeb33db6f3ed95eec922bcd8f65caefd7ed509ab84e27b70714dc0a837672a35e79f110ccd2e22aaa6f0d18c506875134c92502ea0e3e4298d0b37c2307613b56f7579f1e19168fd25a637d288510f351c3ceaa05171f404b1279619b2b626e051172f303b6f123395d67f9dc6fe66314ab1892fe5c80437a8b04dac440ed247a8415d2aab4381bc458a482877f73b41bfdd1270d6f5605641a49ac40936269bf8d8a580d4de6230456ff1e67df66404bbb440470c787385c4ee3ab6bda230299160104017944c5adcd73fad0701ca01fb97a0c2d4b82ace9920ce1fc63c159856255e02f98f1b28589359053056381f8591ca04f011107ba25b863f65b92d34da6e5a1b4d8eb207b513efd461558b19d7c88a0a77066cdbb3e050d9bd6ee4d2b9d52ba55549fb3d2fdee92108596033413a7e648e986c3d9b5a289d3740cbe875090a1ba765ca3b024f3be5ac70cf9375c2210a908cac4b0b24dc3fa1fb76b0438bd070608b459403eaf02de375213c57e89b375ebca39e35b59b828e65a2680ad704f968836c54d2ea60f00f9fec1f097700a45f1b4532de0a8645a093edbd57f50251811f42176a7f1fb5457b52b01bc21ae3f4ea39a1ede9c7155d577b7a3f9bf2f7aa3f3e96c378fed78224f82ef98db36752c4939e524aa3eddb7cffe2bee1adcfd5db144adc06ab3f2b054ce4f87862274074a2d8149ea8602e90007f1ee56ec359629a3f7faa49adf5181df050256c8e0ccb93cd9a6cde18edde0d83de96012e9fdd651e0f699d7a159f450cb4a6ef00cd1500110847ab9c3a0bad55f30bb212e28866186e88a71f9e79ec8612de750a3033d99e3993a177a678680c385c79109a9ca98d5cc4c7206d234b4214a4f193c7385b887da3f66e105c62f429c9194f9be75e48b2e8e17d3acf847b0aa4bee22def200a3c558c18659cb60bcdb29b190a89a34254183e52e77b865117c0860deb9103841d43bc5c00e9a53ae8186cb337744752f5e019a45b446f8e8e6c7ab00ef05948f676f1d2846766534892e7473c059d3f18b243dcdb3a5d280135f3831fad9cd69b090443fe7a39e7018f03d1da1f0bf57a02c8631d72ea1d5e8439f1de1a562903d40976a51bb4a63bf4a68dec2f3ac53a155c99aa4638ec692787003129e7bc7c10d867897b3b37c928f3d722330c5e51e7485ba73e1849ee2e48a853d248121be6848a4784208a48a27466a531226165aca4505f94f70f1ad3fbbf5714890451ca2998a2e40c8e2dc7a8798af2bf787a109175da9a02271a119128ec70ecc9ad0ea790e4de4be84ff64e419183bb1b66e31ea20307115acad93473920a22ee95038d83f55148a42adb2f498d4c66bf54be8ea8cc4a5330737806a57ddef2914a24176f01321e988470d8d34cdeafb3c7376045f7aeb66e3484ddc342113cc866a8966d4f339af2a8b25358f05625790fbe130ec19526a262de232aec68155b446dfb4238e723a6f0cc1fff3810bf68aa4b033b068d902387479b49f1a27276e8e866ebbb08d9a5fca2ae7fd94fdd66d1d443f5f98986153d2dbd3b78e1c8d797daa84e3d70192c585ec3069f59ec638322f94ae302621e4243be50fb965289b1bd052b140c0319304a1826fe2d4c533c0697507af35bcfa8772d939d10f4e35899c8b61846265ba23174185c6ae81828e95c3bfbb219497225a1ab25fdd62c530cd10b0cebdabd5f5a8c00063243d401b17a00224c347f404bde756a83e6e0ce1810777ce40bd0469663ffe0ff31e3e5d807bd4fd77c279fedd9b89d1e593ded161891254b8c5e4c3149708b711f51723c0c7c0e79bae8cc6b6341b55797c404c0797cfa8f1f05ea504783ea0972abe39f45f45f8df0acc73001154f2b65917d4adce9bf021b3f4930c787c57355ce0de628043fa0bf171ff02c7a0a823db91cb38dc8bdae6fe2fc12057f3196a90c13437fedf2c1a08b5fe1477c176b6e9520f72d0fe3e4d4fbe4cf2b0e316ed9c6cbd345d8f007af27617839f843e5d4b5360d3e8b4258b15cb4ad12acbf3f644d60438081d914e171bfa334014f71e2a50d82f7f71035e6719741c13b5287033adf63ce262c826a345e0472a686cc841c43e40ad09eb522faa686c9c24cdb6da1a73f7a7e8e355cf0c31c33477131c0cc8c60222a0397ef401ca4fbe5b285aacfb7ff516657b655377d9bf4b2d8b694c11a0284e8ffc6b66a379fbe4269619a50c36a62133676a66fdef4cdb30886d06fcd3322c5ac8a15f8d85a4461f1b3bee4e2eb2b864d9674f6a1f93264bcf7452dff26f2232cede3d012ab43396bbeec4d51c6747497d647ec99349ae14fd282dc9b67b2fe57baea2c50156bff1ab11712911e99a1a8790374d4f2791e0152356c52fa8f68aaad2179b806ae771b1a495469a95a74b8b7a723cdef03971cbd0e947de8aea1554e17e6700d03e81ad0780d3cb945cc63d4cbfdab145ce062b762dbe15f08191a804b4f85f4d55183d48b007dd6dd8b536cbceb3b73cec55136d0598ecefa19c1e1af65841b4a171107e8e11aa10df5a23a59622874d8d0008796a03c9722df3ca5b8acb421468031453239854bc2066bd4400232d47d3f3970bcdf81734f9234416e030459f3cd31f4bdd9766687c0ba727307d8e22ff3af6a504c440dd0018e83180a4c7a39aa03d55b104ebf4ed94cfb310cf8c3cbe90f89b7b171a20eb4837fe52e74c0d53f703939d0bbde39d58d12a48da49b77b7f86096cc6f122f1ca284c45f537c4877491ca6a0f9565033a8ef067264e59136bea4327205dcbb82e6f1d27344849ecbd92fdd9635424086d7a5c7ff868b2e0682e393f7d6ccc136cc008fc6b93fd7132c0f39ff30e78be3c4e1166075cbc1a91d00b69183728d0f00ea35ec4e7f8ae3daf352eef300b0fcb8ec432fd3c250401ce0b14ec36aec4347599bc657cae1f4f75b3075c84513f2bfbfe7b55f6739d8d625980d339631ea3fc015e12466f675913184d3c1ccf5b4d43ff88aaf73ab6240d3ff6f67423e7e5274eda312df50cd59c3f34b2ce1dfbcdd13554afc84b8a92970d2560875c8a2c7d815e055745f958db82be0ad95bc64787ff987bd0c4157907e5ea1fe039b834f4281730b8626ab2bbca3f00140ca7fce2d82d0dfe26a88b9451c6a4cd1a687c1cf6c775cc78aec1316e8489b0e6db4071b92a45e13a99b6cfa2c9f772a6823765f44a989f287eb1b9cad1f4183c00b1385f7ddd906e6b59267e819c0217e1d962354a70c519f7c2f49a35329fda1e6300024f31695a87918c69dcec413b8eae0ebdcccfaecf2deddd4f933a0b5c6f13dc6883fef215438f5b151b0e82bb250dd5854a20cdb6f73c13bc8064b46fe4e9c32de992dfdf7e9daa49ba5da4cda95135832b75d0e02a4b73de07bcc86e011aa407e11f2dbb82e7b0a017e98a83ddd45c8d00bd4f44d0ae22a5ec6f8db2d33749b487b6710fe14e095b005a9ff39957b69d8ce99c2b2a49cdd76b437e280160a2ded7e6080c143eb84ef6a95787363ae6e37400d57d2dc5aa8faec9aac1713b242db62c1037a0d525902ed873a6038a0f6381fb305bbcb5be6af9364507e115a2428d119720a13e4fa5df98526e7aff00de3dc1a92c6dd90b6c4a211410f7197b0a29b2b815433912ba33a37b206102831fd1a6d7893ee97e084e61193bedace83a4ed34d608ba8c0141d3a2526f0686a3871b13d3402138e2add203aacc89f3d520d9393e224d142cd45c1d983937409190ed5dafdb22934455a1f22c16ba57c4690a9885faedf39de1d140225f43baecf5752a05fabedafb937f11b2eb544949925ca33142ec7792f5b09c1570196392eea0c449c1099059e7fd3979feeced783b4bc1e8ad745767b0f9b312733e0fe239f7de2b78d060ce6ca5db3d1027b5d64dda2e070ab2a47b9a9fd73e0e7a049846d80d3fb1e772f57e88034771d38524906937a88dae47b68ef9f01f388a4e2c09a2c6bc4c99d6e737e74b382d5ef588e167f94b58c45596ba0438f98a807d8d39be17b86380f1d062380faedd5a624731f91e87a3f51f2aa17435a3bf49c8d904ce79e8f2017ebcf21d1de479fd83631cbeec57c9c3586097fada92cbb0beddcacc13cc33d42d6a451053cdcc6bb70280b56130bc522d7f9f3137de266c91708641da84287780aec62ec15221635428e4690bb36de8c40629c0d9888bcfc3b0d022365692f958322775fb7bd444c6d656e2ff4479910f246b3ae0302416e8c2b72479172a0013bf734858014a12cf4834a340b80450f2f441763310a53edcb0664633c4dffe78716332cc3aad05e0023a088c32ea6191908b9b0d228471cbca33d8057b3048d5737fd1f56cfde77ac713e70c38200dda81cebd02d29b0524360f401bc0099ddea7e31c0630f449b9f476cb8e9312042a6295a1605da6539865e551d60f73a1936029c42b4b2fb4fcd2e0aae636fbf84554482814e072d5fb06c9ad041aada3e51e52249b38761c22e8e39e642b8e5430e19d9ac4868a3f65e34eb9d2b21c55c045a7244c40c5dd75bb1db459654ab3db910e5f764efebb577e4f2f37bd66b567b3fe601fbf3b25ca6a7e759be5346e0a218b4c6b5c31e31eb4efacbb5a0810bdd41860fa00803439a830e8956820af80ba71e346c78ec03db03a9c69146013444c64d76142b72de49f328bc30357c829796b15d33f1251e14a10a00f416dcce6fb171286ecb093ad6421d548b71c4a2ca5abce2f51a1ed74ed0d4a888750736c28c6aa21adcff9b2398711383a225bf88c960522afa45f2e0581dfee7642670f12d2250fd80291c3899aa052c0294af82a742a82b2bc60a9e2eed6cf2c998b41487e36f6be74476510d33a2e875055b20c86cf10c48729054da3c1236a70c65b1556269fe1431ea9a7d390f596c71044f4decf1ad7647a74bbc5e78784461684a02be1bcc10b55c9d47f396b3fbd4cf644c24d62ca61337591b7811db209810349819b68dffe605a90c81b999c0c7337022d70a88138e0ac549b1c3178499452c29d2bd8662eed0f4afc497cf5dda3a7c4f30e076fad0dfb42381fc7fd4a59aeafb3414bbe23e05ef35689b3f73da7e63d0274e7c362ddee81373cd2c73cb147cbb25aa54b1572a3603ac0691c4dbe7735124906782690add6321f7837fe321461b6b9cd7475d60ec3a8b1d710dd39db0f94533112659813a3b41a84b9829b9c21255878eaa29602652e0a4e11f650d03abc08da98379f093bb3830c4ab06df69bd00ae8ee76c72bf28146848d96508008576b10a29694166f26580aaeb39867cff1f5e6eec2efc714f05793aebfff40ebf8b0672c7b7aed28792dabe4581799ee1b9a271990962f052e37baa16df0a2abc37759282957ba4e3d012922541db8c3048fae818e7c41096da27b32d6ab5e939d5548ac791671af1ca5a1cc1a1016243f91d39544e2c59ea97c62ec3fb3c0f064e6204d6e394c9e563fa05411e2d6e739b94a470051dd22acc3ad054398eb9da2675b2358fb80fe5815a6f755b284628d935306d0b81a9e17b49b06189c0a96ed6e609c67590d83a55bd4d4da88fe6a835b811284e2494225aa36a329869189e060b2521753fd744cdbd792e18a9ab9858e17980876ba75baf94af8c53266821c00a7522bbcf0bf20ddb9d852d131a14d3d52a906f839b23f92f875ad298261ea18de2067db76bf3841c1021242141039811d4e33403133a2df5eb52c5dcbd608703f52107fae5140fdf658c05932347dd2ce1efe4b74af4d018e5f66ee4cf5fe4fc0874e5be2f5aa273e4457ecb547458df356d8d77b9b864b054f3323c2937789a758b4775940c87b4c146ecccd79393f0a616b7f63c8492f6f9ecbe4c66d43842a31666b10f74734e71e8b28d5ade03c02dc2f575bd562002199c2e3485d780ecf0d458abeb24d88d004b4c46b0dd77ed5a34747041d9c045b2260a7f710fe17e7124b8b30cb67e0921c36289474cce15a633883aecc6b770f9a7133caf08025211fa655d52a5b384e846e27f4434d52481d0a89b0656d456e681a018ae40ee5fd0ef50c520a68c9db2d37ba5b9aec66c55c080bf58c633863cf3f8714c82d5c25078f1eb4c12adcba7fbc50e9d3f4a2aff2cb53a820ab050e9d63b67783447b24a69330bd7dac487a330cbf617efe64c178ff84f388292078c8331fb3ded30d43042686bce03fbb5c99d73ef9feec66fda3a81239b04b64243021e7d091a6db1741f0d77202282573b7ccc726fd6c5b4683a31b889d15a6e428b119134fba8afb0701bfed124a90b340ee7ba7a3e640ef90c81e0994131afc627cf591de8ebf483ae060b0690264df494793054c0b17ff8ba59a46c1ea21cc62440ddd1d505c00146743ca0e527abaf3060b9c441bf0d0bd16980a5bbae4b17db0910ed52f2999fa59d5598022f3b9bfc9944fff932fcb2cec4cd9460ee6e6748ec422d08635b1662b83a27725faed47c7b04b74ea67b455075a14dad2d5719c7dddbec8941b88aa846f32d382df4f578dd4e9fb41cdefe220e787a1ec19f238bfc7702470c56d63e30247016930035cc4984a501c94481e60cfb273a83ab9534033843203d063fca7904530d72c29148755912a39a683478bcd0151060963f5c2428ab201a03df98db8a55483faa4dc97ec89107aeb53037e53f2763195c1ea31f9e3f211730ebb1709f322f6c7142c5c0924608340dbfd45a977bb8239844a889f246ee269107d79fe40bc734ad0f2a39cf103ac5d979b511f6a1b850afbbe2318e6b667fa3d39a1b5332a1556f2cad3aaefa3039736cd883ca36988d1de17894410411acb44219a1cf63b1c357a1187162ef53102740d6d6843eea24069691b6a3ae091e105c1fd82602f4b2b19dd3bf4f7aefbe5b6fcd4c50756f76aa94868690c8751cd8d313107f79e3aeb45db2c49b5f279c2332c43a743c64c45065d2231921ab47c5d8d07c74bace70667fb9653a8b180f9d9da2d0b76fe49e60225a74aa07b368ff9e254c1c9d461c51564dc44818e192e4e06b015e21cbaca9267f6603d1622b8c1fdd941677873461bb1667250a9b666165e87596e00877b4eb82f64966a0a2dc09227b8d712ed993dd817fa67381c7048ffef4e6dec964650470a09af0c693104936f66b9c6c6c57edf46c0b71a82b516209c23af5ce5ff191b0da94383783de2c50b315ebf058adf6e42c169826ad72788ed73be709ac99fff5632e6906389ca219272f10c9ef4c8c034a35b82d0bffbecd5867c0a283ce992ced5f0c217ac4b7070bec2c09c45caa6782a485109cac09a3556f3968838941fb23ec318d78be8ed4e643f7f068b67dcb558e2da3a57a36e96bd0406545c576288c167c5eb9a359ebe094a380a5de7b4850874d051ab371ac85e7cd0c3b46698595b783402c87d176e1fc7b8c6d8b908150f95ffe976250e92bd05857c06a0b3b4b6a48d872450e0a1c8b5d8d71378b463d7294bce0a836b298ac410e496888486733a29ed06c57d483137ad96c992164ec0b835e9f348dba61e4ebaaa8d44cf0e6b1ae093e0ec5695b94003468144546133bc356c591c7fe6eef48d5c0b61f83892a4127d851f60b5a9f6618be4c4be4c4c882fa4e9e225e2411dea6d38007bfaaa6aad23e2d20beef1434d528e8b32eb3f61d3983a5a18a2492448a7bce4e01ed88f4615bb2c7c23089fa8ec806b252b16a7ba80ff0038a320be16444e1697fa0c3f187ccffb4ee7e3c10d7c6868b35642ec5b12108923a03432bb8d1a39bd65ad99402341219c1782f595b018990e5ac46d87e29c7bca286f002b19b0f0845e7f2d41a7eab83f233ed6ed98a74ddba55e700cc53ab46f18f17bb93425041a2ed8b6bfaa467fe2ccfacd93d9ff634ab9c1620b81cfc9bc04dae204170b16d128b0fe778f15249b37f94814846be2176435e37d03d2b97423ef20e56b7783dd02dc31f84dec2752e0339e7a392b34e2a2636d5a92f8b10fe3f53e0ae8f8e8235ce73a9b52fe0703c6bea30a064830511fc2658dfe45e295d2f6087d1aa01a1c26031b84157042de8248b66b8988506e6eaa3b26471848b561d60a43bf87a0048263f31470813677831c09751e8b5a89d0ca679baea44171a3e0a21123904d998fa46cfd145848f44683e4885fc7672fb042c42c9b649eaa8bd2df34dc5ed231d16abe11936fd3b29d0c61cc51c8c2cc56a1fccba2ccdcb44c9f405215b9b1121e02fef2108b0833b5a463f3ab2b0616ff934312e51206d8e9b205265b11514ea36b2d57c9137eb539c7134bd27319cecf9727662ada18af056bcf1f6653e465e53bce7ce328468323404e36e71bea81c9c42dacb795c63d41c3f69233672909f2ca1c95f1b59288951b91874a12b63e2aabf95de87893c8a56561f2775c78034aa05a875a20d2324e07a91b1a16d63ed926ac4fc13b87f3d8b1ac2b8e88f58ca60bde7b8019ddcd84ed8a65dbc54c1ec0e4ba333b3c00882acc3d80f381e6ce3cb9d47f7f9557b3959acba248ec433fe8626fd8480891dd6dcbbda54c29c935086d0852082fef16d616bf1a64067356802bee2590b7ea16d286bf67645c7e2d40de92afd9655a35aa4696d262ac5187fc6d5cac6fa490134a2ebfd7d337a7cb51bac54d8ebc237d23856cf043f3bd604c137202883b7a40b22034eeb8e335dacbb5113a331f4f7ef40d730d2e3f3331721466e3f2f338ad5cfe9122f1e99b91cb72f9b90a71f9f9ab1012a79f6920af48a22c72cb8e3b4aa2d8112547cceec84d4a45a56a54892ad0e5e7b28c5c8dab5d7e8a643bda90288dfe4c2b7d33d990387507bbaa5d7e22afa75b24b90dd3ee1b76641a1312a79b5b6d58ee06aa5a6c51550389434920c1b824f0255474b8e25e86c0815ff34b489c8d04d38df8eb159e00f71bec7a4882edfd55e02e778624d8c2950a7c2596bb3df75b480289e572cfbd875824964b4189e54e3087d8a2b862ed088add9ef5cdca6582bd52a8892ceee8babcca39a1c3d156a309f8f58a62b878524ad9fdccccccddcfb265bb4fba9d4e6ad8b17e9d34d58825130f29a52783902d594ac9cfdd31049743c949296fb03c4a2f375837a92089acdced9b66c8a2aea462031877d5fd659aea9aa06a0516fceaac1eb9123ce79c9973da1759aeab024b3f037a76453b704553e08ac680a79c5681f26553f375e6ecc161ce9c73ce39a7cf39e79c734ba19e522ae37da7266aa27ea652a9ef4f79def3c87c69bac012b8eaf09b5b71dc36b7c4eee4ee9c386823e81bf9041a5cee7cd9375c66357c3f5b82e0b6964fa4c1e5cedf497de977525f0a799cf4a7c215ed70a7be17f2a0be56273af665c0214e86c478ef3d0c380415ae5648bdf7ab0e7752e18aa2bef4ab1dd4977e45c318d88210905f40f6f1e69b4e75d28ebaeb5a05026a260553f24000635e057e3f03ca3c0ac4f12998926751dcf4058a7537ec9cd47d73c959ea643ee66338fe2bcd3c9117ea63defb9d182fa6d6eb5faa5592414fe0215b18f00285972736e6bf975207f7a587f98ae353cfd3bd0c8e97a6958ee7cdfc6cb1799c6ce1cef733210f8e2f1c02f3a9e7910963c252ed4a30a91015c662a7f90308c358b6f54b0d4c6821490808443ab410927024a99266928078161402587a10c0ef55a0e97180303ff3727a19fb31a81040f9c231a3e32da61119e91809c971b9529094a48214084992f14c5ffa18cc7f329e1793fa30e4711213f2c0fc57fa4a257e8162a743b19433ad74bcaf807c79a1573f2e954c5ffa60aa29880a7ce957938ec779a0101f5ca853d47d4ea760ba3a5287cba2d27bcfc2f7312f4d3a32323120ffb450df86ae7456fd9ecc7bbf23f35e873b38de7b2fe4b16ec33d8e27f2aade92796e48cc7f5ff2bed4ad569899a9cfe3fdcc8ecce30879625e26946ec385de977ec72b79315f58823181a93016437decf431d37b1fb36ffa7e409eb9cd090c1292c4f5498b276b46147399b19e36eaa2a67550cf6cd85b185aeb79263046be4a2dfbc96926f2561bf53891b7e4aba87b66d8304e72c38edd1333fdcbe774f778eb855fba6e494b9aab48f7744f1be92e32c3eee99e067ca37d37b9b0eacf0b42df051aaedafbf926bcbf4ba7851174ab04f3fe42b6dc1c6f79ff305781a78f953ea6c7bd439d2f5cf57f4fe4e57dbfd3fd17f238e9e739853121fb84dcd33de16b03ad14dba63f05768fb762a44db7cb974ef739ed41dee3edce4546d74b44201fb94d0c6424b7298289eed64f2d283b1f6f79a6f7e7206f9ddef98767fc6299cf0f5053fba77f79d3f75ee93b6f59fff4cc6dfc4f60d3dcc6df8244d7768ff8ddf3a8d99523c0f011d93a45449fd036cddf32386050a9179fed65c0d3c75414587a3bb91477733e836c1fab7f9adc73f6f4727a1bf2d43f3d4ff7db665f38ce9eb002f7dbef70bf853c4e68b8b3fd29e47909b7d78999fef3bed47de57edb6ae9e3927a7c7c187931f038caf5e728fcc2317301a964e4918c7d7eb8c701762f03963e06b40f9342995e6875798b81663417edc791f81e383afa80077e1c49af56e5a8dacf7dcc7e877a79bdc25414f7f5fb6bf730218ffdee794acf3d9117f7d6f35ad5c3fd8e2e1797d03dd761de82a97f8162fb8eaed50adc97de47a7631bfc5191581f3a3ab1faa8efc021a8ef9e038738d97e488581a996fb9d8a0a4370354be0d782901e9f9aece787ad474cfbc4d8a9520322c45b323f8218c63d2c631f1ce0cbcb80dcc7ace609dcbeafa0e9a56927a771c4c06c3fbcc54134a41f48407ce8d43848ade642102035766145bf02f205f35d107e79fb31ee51dfe18a765db8ea4f39d139bd3f6b7bff9cfa5c77bff3f2dd77873bdca7421e5428431e1b4ab7912fee14c662b5fb975088dbb88d3310c3d834db80d4b8c8bb06e06706566ab1e3c98876995cfe0030e99be63fcdc08eaa95bc4994716578a2f5cdaabf2fff89e8f4c43d9d80aefcbab063bd7c320ac08f8db518843d72fded111f3fbc8563e41f2bb27e74f10c581f07c87d0c587a19b07b18f0fb14e8a140d34bd30c285f3840f98a01e54b06942f18f9922f2fc7fd81ae7f87b6c7c260aa13dae6058a9dd78509b3fc1ee7861d5f472571fa312075f8980aa17447697433c036fe63c9e5c280d4e1c31a61f943b05558aed55a954a14592c972bc86a0b32bfe7f7f74d900ac8572c56fa3ac3d516eac0d8f7a759cd473d91979fc2d50c572bd4b7bf536dc8e364863bdca3429eeeb91f45d795790e8c7918d0fe0944fd2908eb01d992f287c4e1ae3ff7780bf5fe5e5d92617f64d96a85fa32bf535f26e471b23d8ffdfa5bb8c37d4cc8837aee731ac6047c69a803f35e0ae49769a5635fde1d144c188bbd7cecfb58e9ebc74c1ff33ed6fd6b4ef0071036812c731b97492fece3a34a4711996d4ae2ad58ca977fa54c82008d4e0d960e026a11775a5822ab0052eaa8c18eb20889d66812ac481609f1e136feb6da9a0e163bb2ae8b5d98d79fe686a9ac4559ade2014f7dfa869f3269f714e45df9cff79f482d3291953bafcc232c3ca69116769c46957b266df8895c66182079cf3c1c1b080805f636cb126af71df6cf4c5293b0815a6417ea078101764bbf712ebc60bd69e2d1d42401a9838491af538fb9fd4367578e008347a4d40f4883df7b9a163becaebbf3baeebabbaf95ab5ce56a0df9082b89f2848b242e6aa3aabb9fee02080aa1a4db85236b54cd80bac07247eee9e2cae9093b72cf898a75e1057bbd882124095c8395425b44b9a30335b946461e85db82cb1d1d480a6db1c5833c8a941e73b74da1f39e66c87aef4d530a7e3defa5d451df0b5544d8fe12f5265f09a4e91b2e9cd9917b6487ea71e534c5726fbdb07f2b7f274d29f0ed7cc80db52341fb5c1174bfbdd775e13804e976df2fd781a40efe93146b42ea59c15fa6b005efc03a728f3f07bec0a17ae440d6a2bc32bb2a1f16ba0274995fecfc5ef7d2e4d5fa1e33b9df7fe1d8cf976f7d14bafb1ccf690e247247eaa16c51ba112afe24bf0406ccf29903b5c8a3039d68f6eb983646186104228608823109599edddd2d6bb195c05aac5272b5c2bc043b0439ae613aec58d8b006f9568eab5eadd5e33cfa32f41892c401254e5fb79bfdb69eb7a0fb00683f680224857ea0e54aa11f3cb9fe6e44dfacb877c065c0f5ef9a2301598fd1bca503e404ec05f344de8a797f0f06641a0cb86201e6633e26e421f253c405f6820999466d0a55645329eb0590fd00e62fff14ca1619f51ccdac3496bee99f09ccdbdcb5bf70df5921ece8488e747a6e4b52dfd8f7afa0c75ab4a15ff9d167462d228d3ee3b877cfe2b5d93365de8a4d5813494c26dd8ad1284caeff6cd22df1c7b7f48d147a23a9b9592ca86f467726aebf47e91bafc1041c48e2b8155287bf876ec58ba40ecf5283f59a23b568013b2ff7f44a9a912f2ebd928a2cc6e1e8c21e0e6b18c311ceb691d2ddf42770a4f990c669c338829a2bdf144adaf77c2471e8cbf742b66427511dbb3b4dd21c49ea7032a48dbf1220a07c8e7847ece8b3a3eb336fc999d7ea3f1b5dd78158d71d49bcfed7c53b3ad25b47f22c27d422b64798333ad2f5b749d0fcc85d1ec1c86cafb478b2a6d63ba5eb27f71bf882c4a852499c0580205b3856b0a4b09932b9cd153491d865424e0b2f746b8676820e9054568c8e6416a2d1c6d0d4d8184a9b4fccf764ac7bd1f02f27fcb2632e33cc656e6e70e6b2ea7237a72eafe88a7eccdd4a974bdd274d2f3b52c7fc0b481df31b345da6463433cbf29b1c6a448fe603e8158ac4eaf10079a382146958757a741115e40d77953b0c2be78665b9e6cbad6f25ce0a52c7fcf928d9aa00dbcc9fe217777271e78f20481df33f2e6c075219b3c4814dbef377646047ba05c2cf0664324b8c36dbbe99df9d3477f606ebded8a84b97527af3c3c24d3cb0e0b86eebb69d16e9735f2af3fea7c9a5b496c6af7ed7c6a838a4956532598b4cf0a531d786ebf107830b230370c52fa19822e84a28a690e16ee08a4312ee0d686f30a3831bc42ff754a8713dfa6db4bb0de17074b7f085be41b86b8279b9f73629e087c5f5eab781a9dba084e2063bb85ef74171032d2bff986f034b7705d7f33690666629906d89bc61d8467ffbd9826e780629d21f3726976e404028f734cf206fb80629d2a745b886ede9f6260ec31a4a4484644d2f6d7a0972ba497c24145520b9cc32a9e37e6f625d9d158a7e44dd5116c9a21c2c89c349a40efaf40f205b7c03dbd0a7a14cc5e1c83fa0d8660b47dee152d665d9c785156554b66d7493511a7db95596b1122a73e933adb5fae11dd0ca6d634d8e4b9f6ea12f478985379a958157b6ee94db24fdb60d8aeaca9f261ef2a5d339a97be5da29c8f33ab37bf7e550448bb34b2fa28589161fb414d152440b17d1c21c4ee9ce0497aba79d3c3d76b4d8df26568bfdac49b926368728d26dfbb135bf719446c5d51963bfafba90da9de33934e94d152e4f9e29677348524a19b6bbbb7bbbfb74d9dd3d96dfa59ca3ca8b62f95bd61f74ffdc29b1d49a93b0a0e28e0dbbddb1eee923b795dc367a1214012ab234310415405c644bdc9189c4b82317b1952206ea6e33babbbb5b47321c11030c189151d01dd9c78a26b849d7884445484104962656a421f46a25eee82ab2a159ee400746909421850624aabc3a892ab4b8dd3fd65861c6bc42873baf08e3ceff23ae942514e0969e33f1285d95b7fa48c80aa1dbf1e86826d44c1cd851f5002455da465ab922626165716d691bf900245cda465e5558e4c6ad5fbf6fc6f9aaa7f9517555453ef3615bd39a06c62d00cc0eecd845475384c160324cb07aab8faa00823d1bb0c466795ffd73bbf6c2436559e49a5c7b0a19952e3410a1e50c27af867284142f1284b08a18aebfaaa690a83e3b93eac10d4c3cdca6941842064786b1a042e8fac7a8a40a056c48f1c61049a630a2c393275174779b8a54f18731f170ebeeced6741d0232c8628d1e18d9e10d365e8ec47524f7cfba0b80c68a3bd62ce08e397698d02f475985c889db3ffb6b142cb8fd56158511b7ffa580dd7e510a2b6e3fcb65e4b6d01439b82d546576fbad2c4aa28c12df9c2691ecfabf5422a0d8e10030c41af80cd5705b68e88adbff220d42505871fb4d261eedbad636142aaa1aec285e9eaf16c284ebef0d215403cded2fd927426cfcd96eca135194e103cec4a36d1a54ac0ce0f66f314ba9c4aeffac3198f24568bf20335eec677bca935bb36365fe1c527c8073bb8640a4aa342b476045b4232431d4683dc021061f2932f010a39221a96d24ac6de49da1a76de41dadca871d603d17261392b204929c2eadb252342bfd60a3db467b0d8bba524889a03bbe50140e64bf2b8594b0c2444889232b4af0804a900c0e847db95228c808cd6e570a05c510d483152015165adcf15946684f5c29447bc29fe34a215a8f57742b90e45e25da97ae6a6465ab8158b2558d74648bd22460e44e7bb4c857a7452ebab51e85d5a8083b76a0cbe55e86cba1faa507ebd1854f0eb603d2395fe2373256e221317df064cda358c63ac89618aa66d0fd528a3edcce913aa45896fee3c3b24de6326f19fb33c7f99c1eb2333b3bb3f4979be424273929597a385b9cbff90ee2ac5d36cd2ab2ac4525dc93f26c581786cbc59ab4cf8c2f2ed76a2666d6e284659865ecb3858c98cb958db8dc31075deebaaee676396eb7e3765aa6dcaeebdea2b182dbbd0a8d2e6ef79fc611b77b718b91db3d6bcb95db7d35f1e86a65d5deb8dc6f9b0d3f94d25c71c79a1d97527ac610979ec1c6a53d20baf445faacda0e669821e54d857d5e33e6b8358b10d75fe559c6b8fea2194cf0408cebdf2c1aed765cbb483206908a5519a672ddddff3ccb5883073f77fea712e1499037e79cde0c921464f44046ad0c2224246a104248b2cec4a32dd2183a40c0edafdf932e4c3c7c0c99a37ad3d00f3fbcc4fc69973b7fced96fdbd463afdc39c29d4dec5869c428a23f2849be39e79c7e65042eee98a30077dcd1002c4061c4c210420c30cab8fdd2563b4455b1aac771dc15f6bb52e80bdab5555a505ffc1a5108a8f405173a705218756fbfd072439da105340a240e8c243bcc17bf46248a111d2152b2a9ec1751c4adcac6a88892e8bf05e3099217bf461c9a4dfb6050c181a598b9101859288922488dda290c16d752d8d4954251f80cdb5d2914a50bd6220a961f2b73a550943344b852a889254f60c1e2f93daf97ce3927ade6c513dd37e3c73f2ce626fe31f3fff8f8fc70e5c6b063c324037a9e05d2841ed60bd50054d8535f56084b9dc8e73f5d61ffc5fbaf63dd73a467903839dc1860dd44f5b862f4776cd985dc754aaec73e7e1a6b38aac251e5c3a5bb5e6d58adb08ef1cce5ef969cf273e40ba04e62d24fba6552df3497bef19ed8f523b52d7d534d9c17c27cfa0883cd5cb8848e41923083d4c177642e9763b5d6dad5ae76b5d61a76acf9b7d00076b7d09bc29fa510a89ffc28d0c10a4a95d421654e9280a20a14dc4c14322d88263916376fff8e1907c05a62e19cd63723ffdc27faa67ffcc7bdde205c97bb75f7db94d2d5e214cbbab37f6319ecdfeead5555ece56e8f9bb4d80be828585247f75b29ac874fbab8a36515753224c14cd422bf0db263edc2af6df839946dd361fdd911b2f8a7c879984e29a5343539e44fcaccfc514a671593b3a51a9a98b975a96bd3ee40497ba33133f04c38b0fc638cfdea3c92a9d1103c2444eeab39553432d75ba9a31a2ed7dd7dcaf98d31d7251629c1a751c5d8af52777729e7fca68d51c3fe690db08bf99e289f8fe6cfe72f240e0d5b0622eb3f7960295dc37a38ca233be7d1fc5aad85a966296c8a74e79cb4a483ed772b1b6fa017b538bf9b6f8bb0db53fa35a421a5f4ce4be5579c17f58d17b4e77393869369379d3d7b4af9f45b5ae91ab25ed76d552d8eb69ba34d7ba34dbba9bc65b1b62eb64be2c93f4db1abca3cf97d86ef56d26897dff20a866a6022014f1a61815d39a4c488dbe10b7d4fa0ba2fa88830bb6044edf273615867d236fcb98facb94f4f59e3a73858e93edfa1ac510a036b535d60bf9a95ff69b1f23d2ff22a5ec4ef42bce5d55a15ffd4faab1daf7bcf7dee38593b9dcb6d587ea8150a72a806262b16bc6ec50237a4ca17fafabb498599dbdffeaa1504666effcced17c1f6fdf4b77005810e51d04335cc5c07e59011da6d10c84ce9391686745f7dac76ba1e352cfd58ed54e64286b9cd1676943549f35add81276b4698a3dbefcdd9ec99796b1691366d83db33c9ed375d86756ccefac6c39ea1dbd3605fa6b749b76bb727114ccc28b6b2a617b75f8e539630812409fd63c70de87e9c5fdc7ec90c9862947e82b2825f0bd1535fa537813df6557a6f3699b3c96d3f67f349ff0cea9bd39d59bac54ecc28b1db0feb89e5f67c6ea30deb7136e99b55cc65b0fef5fb4b478279191287c39641eae8ef9e267d333faefb0de4a7a057f04b415e17a4d4b19aefc99d52e989bc3a30d6fdb7c128d0ed39bb5e80fe052ba0bf0369f857b7e3852bfeef4da57ff9d2bf84d26d60b7571caed0afea35b7e92237e2048c2e468d4bf0ef9f44781ab2d535481b2fb1580630c00823ac5630307774249c177e22248e87466e7f73d83d4c427f05606e43867558d11d59740d3be97c3283c2396bb1ad1376a5ba342d7acb9174bc555556bc638bd791faab0dfd55c3fe6926768431f299893ac058269edc619d98fd34f176f7769f5d2abd572a954ade61ff7c45a8ec888c1ee99b9e7907796b1a55b7aa69f4fd17f27ce16ae77bd313799d9ec82bf5445e2fbfda81f1c2d56ae7e589bc504fe40513127975388a3deb20b7a9df4a85fef9335c4dc91ef6ec5542b67f54cd2bd388bbc2362d06f52fe16ae7e54f7f0a794eff62026152f66dc863df14ae764c2ff332210f036105d0cbf4445e68d85174b1a8d8fe9195d3e30cabe34389fd01b68086ed3f828cdb4f5ae4f7598bdca2db52e8eedf1b3f2d5279efd934f270ae66974aa54e8a12d3142947889822a5895237857604e40532ec285b088f18769449a8d40d49dc0084373b12820b33163b32ec64dfab45497c2043e4e5812bff9727f22acd52a92b152df911aa41a99bbcf3bde9c56743fcc0c18618ea39e2854d8128906545ac09e431fdf72f20cfcb7bb3c87f70c27f30fa698264f94f2003ade005641ac84158ecc841b4565b0287395b82064455d0b0231371b335685064a50776e4221d34277e92a0c1e84a187664a31a8e74331eb2d0278eb4e08922b5232c67d8918f8250b499ec065ed1941c8c31250731a42c5aecc84840231c51f9e1e76787ef880a50142a30d852db32861db9b6061355d11a4abad625cb8c8d2235c8e024e67264474eeadad112536cb425bce0c27482fd38ec89202bb427a0f8acfce5134aa4a104d622bfcced1818f603db03fbdbc28eddb3be794274f93ba86f7064e9968a1644bb6c54a5a8c7366a2cdd6ed437fcf38acbfa86b7cb3e2d3255628fec539924b2f2e2f6ef7c2f47df4c8e565a7b7047c8b02ce39e16e723b1f673bcc5729b69439657697d814e06b9a760108902e78980fbad7ad273b95a34f66938be306feae97b29d0c54a62533fb26e0aa9d0d5e214826605f2f06bb5a2114d9e2d09cbb016a76d815d3d4f9a9f00f819809fef2d117e3ef7489cf9f2bdfcf7acbe898199f3653ef5144cd114a55446e653a9d45370b43725937a1c60102913ba5221abc519d3435e1abe302fcc57930a0ee8d2a5cb044e31e0bc30e04a854e04a7ef589879115e9a5850fd8740dc66823c217c0f1e105ec76d668eb5c1a60210ae587010de5fbe0010ae58e817e145088784c082f1b123a3a484d1c1a668c2da368fe2e47da92327b12bf05bf4583b5c93025c972e5db4dcbab1fa468a2dcecf0182005614cc5b431170e176ed11578116e4b6b044767c3bbb7204184a44601ad583e782855df95c172bbf666e7212a156b384c98ed83c42a26868c916cc9f7771fdfb7e5b05ea90fadc0fd99ebe735fc3badaa135ac00bf36509ada4a1df25d081cccb834e3211fd9656905367447793469433e62dc5166b9e112f8f2d3eca8c9e1a292a44a57b90cf31e0670e5d0122472e8c18778f099245c39c4c3929bba7228872377b4b7979cbebe41c1bc2278206ec39ff4034947d2e60fc9f2c60f49fc404312b1d70c596ec34637a71b1c595cdc51b5c51d45a33bbab4b8a324cae28ed2080756ee28b1f841c914337a3022c41a2fc6e2721b6d0039d183315c60c49317bbe00a173c448143111fbc5a90f2c30f48a038e30ba1979c92c50e8ab8e244075704f1e2d7f11691266cf83083284ddc40450c5efc3fbc45c44796450e50a8449182cb8b1f88b788e0008410d2164b72b0850d5e0cbb39ad86959f225d9c809245183d5c214692082e48425ffc9affd1064810f9708413468cf1daac08e204e5053478d1145f6367777cd65644c5dbb66d49361a92148e8644546449b5ddd00b8ebc2b875e40bba82b875e40c65125626405555c5144066d34c1c40e3a9c9a4605df1123ebcfa1dc416697b0760887d9954338fc70b972e88626b787b76892dbf07c164b14ff6baa19d29adbb04b9a7bce09fc3d80f0742b351bba81b925f52997b17cb769cd5bb4459f456b5f592c4e14c37053d94993bc259dd6c64d6655cc4f93faa693688d26f5d32de3bc340956eb9bfeefc52fc1a9841d290c269f7360475aa3491b6c239238dfe5dfaa9480248eeaf29792240ecd2237a39bc37f425fa140fc5c7e9ac567df54180ce69582a08136a573968e92d4e86c5312596527fc155d2fc0ad675c0f80cff5179598a08e8b6407d7c37b641b92c9e0fa7ba58f36a593bb16d0bae9ec511eb9ee524a261edccbfc920905ee4eca8239dad93075c8062ed72dad094167bf4bb9cede2d31a3200c1c0cac1c9214bd9cacf8895c0ed72003090612b80025bd642b60fca47f1494666471c2e7c57d74fbca85306cbf852a9f208f93ed7960a03f9fdb421886c010c2e6cf906ea100fe120362b32734643e01ad440b625629fb2547b9bf7ef79ef85d48636338fc8ab0fdd47be98131866ddfb3932f252d9e8056a20551a2519f163d0545e346849d9fe3b5fe04a912caa4452a6bd19ffe18b12395cd77daa4451f6f51a09f168db0e3936e79fff44ea62d8ff64496684184935dca29bf512f2911b62765a44b417a3dfed2b85eb3cb9e2f361394435bb85c0a76974d3c4c3e7664b176b898094bc35aa52cc293356337bbfedeecfc259b7667fb489c25b2d5b486c1ec2380e60c3b0abb74d2dc559cfbb8c568cf75ff4bc35983b9c08eb376dd9f7bd6e696a4bee16e7685e78d9e4481c84581aae4cbd0b7f7cd3b5a294729dd28a5b46edbe6a19c379c88fc884ef739db6e74cae935de74fa07a055edeededededeed277477cb539239a7cf39a7cf397dceed4729a5f449ff45564d70da83e51f41cdf50e3f362cbf3b0d5628f00dc25ddcf97da3dca4936b92c6e46260adadd9009d6d78ecb2c5103e65adb5d6b7e756b52aa9f2af7247dbf0ecdfe1f21bb4b400460d5c67296ea18ca2bb67f069afbb9b065b4a40e352840f96a001547bd9206ec75ceee548a3008e33e1722f81b81cc70dcd7074b9981297fb6fc1e5b81550b9dc100d4bb8ed270ea89052d61892618aa118a05c2a69deb25f4c8d50171aaebf55d9707d08e6c56529969f2f3f570508aa317f4063a03180b4bbbd372b839fabc5ed27cfd90da809b981349bede7f2dee536db6f2edadef6837d1e48a92b751d2704bb62d471251f3f3626b60964ab3374c0edf7b834259523212e466cf54b933f6a03d86fd3c79b33fda95f7afb6e20a090167dde86633dbd842fb4e8817c55de6218fc10773be26ecf425adcbe43202dce366c15bb97a6526982d20401de4283186e0d4720462c90be61d187071f893382c4f1bbf1d718b0138672b7f75a47b2497b60cc1f3393ed9917f0c3432ce0226becc355b697371099e56ebf6dd2e885ed9989cd0677e418cda60402b42930e5087ca8685cbed16d73b931c2ddfeeb1ba6cd20a90104834986e5608165892df664c38ef247f1d61c6dd3bfd336fd34aa16ab2c6a1daf462cf730cc12e10351c5ac246454d20d1c6a68f0400b1facf8bcda89294931a46212c77d50c49d3fe9106d68b162f08bb04ac01d6b92d4b8fdb3dba0e1761b40dc6ec3095114592c9628b29ab348f2a1bb356acdc18f0f4d830b9a30b791958d299068f961830b1cd060c30c26b5a4a42239a071b4e50daf884f62c65e740b6cd7850c16c795424dccf049cc78a085165a489c1472c08c2185d8885dd79148eeca72fd4df86e13b28813c38eef2fb248b8fe3a64093344c1f87116965e1f2d7a16b65ef9dc3454d1c44861c79f3b3a1cee44617908082ed7df65440b2d6416b6fb250beb2e136e66ee0bdd2aa1e884357eac64b9d46713e0d2fef128049038f3ef0c5fc0bdcb07fd8d7e9538399d6c5d69439f521d2e7479447614615048769351f395532c3fcbdcc443be9361f9e7f4a05839e70c6873ce195eca0196304c18dc279592caa794524aa9a42e039ed4a8e48c16863d0d15a2190100802000d314003028100c0704428148281c9466c97614000c7c984c80549609e324887118062162100100180300010032223434a3006a49d24a09e5875e96b9a9420a4d1bf61d2b220c8854e6651b430124cb0e3a7c5dcf6cae9cbb239d0e1af20c1f0efe7080cb55eba54558738288d4f77de865b66aa3b4d77ab1ca41a0d1ec71bdb329c8c4355016a1e71d6309418398721f5d80b6b7adec8ed27a8aeac522a901636c59ca4374ecb5d9065b595221cd40925be9b8492d53c70e390ce3546b85d8bf77baf66900eab53b46105a26db4c0ee06d4c1c0293cb83a3a5a355d50e5933b6c66ac8ac7cc663e869d11a863b7caff5f2e4820237642dbb5e3edc8cba2a9faf37325cfe00d95753b48aabf3049c19b6a0714b21c4f6922bec122213c1186a785789856df21294cebc86faddd84d1fcbc4a2ccb22929bc0b413145a4f03c111b0fc4694b41e91cb96262628ffb59469fe56d0109003eb5809ba141816fc66992a6082b425ed2dadd5f2e5a39e5425945d49e49dcef3addcca2ee8fcd7786f66841ab889b90c855909637aab729796b84f20ff35ac88689b4f4951320c5fb25d506c94a0c91b4be59b62f5148db256944edbbb02c025d405956082389f51a0f412d3b817dc51df1b868795e91b80703afb80ea2ce1c6927d77cc5d91087fbc2e485a6ba8b19a1e351458749b71c8201ba8e1b0edce7d99f7a2a791be5c11464374df03a7b1aa53d5588315b3047b033a72175737a0c308be2fab2111ddd48d8a91879166c8dfb13291c0032161657004d5e9b8883f4db015a2319a117cb68d6595abe54ac2799515c19b2f5aad084e5824b5c1190e1c0e2774186ee93b418436875abc8f6aaeba2315087a0a7fff1f058abdac2234eb1862d70c8255d431c27665936c734f10b50791a86c330032e2530a1f0c2b4cbf09aef303b514928ab314ee0fa6c331f78f7b5d3ced8169808646d4f592c9b2ddd441f6eba692c9dc7eea9e0f148a95c2b498afb8aee713d5e4c7dcbf4d79793913e850695f2e30f6ace7a4e296d5bb26c1e5eb974c9a95ff66adb885bf5ada242d37c6168df53628647dc1e5948dc7010df002bcecffc16110b9fb52fdbcc35f0538f315080a61b75e87160bfee3183d00e22c18e49adc0b6e83b57f4c680059572dce6c6cfd1ab42635233421bdffe9237c3eec9b5e112f433fff60d44b2407c3febf2e53aedf6dda709409abba0db222a337d10784de393b629bc1ef0043f852f20845b771e0219c67f9041053008e55ba7b98f3bf27be6513f61c50753c32f020fc89f877a3c0929e4931e89f23890430e6fafc71a7d76d39e4b4b6fc9bbea84bc55be361be112a5158c84b5c3b18f0865d9911152049e557d13966e76a4153ba33dc2bc58ea8a68f48824a16107120ceba6072b6939a104996529f7f42d424499fad1f90b1fa7cbb0b5f1458987c3a655fdbd97b00d8931c54e4c9d2aef409d82aee804be4d789b65b37005e52140a1d8aafca1075c31101ad7eb5bab909eb8750a7a2d497935dccea7c74f4ac70defa75ebebc1cc70c1f928e98c009687831ab41ee611a394b8cbc45c04290ad17b5863e1502ffa6e35c8ff6e9078d643986d1d1075e5fc650a5f14b1f7c32bea6b240e50a7b661a9a1ac8322c24d08310016c26d8fdeed0984bf7b2d2bc8849344b10d9bd54f9d5a6109d93ce7cafcd0e8b160900cc65da47d2f1458ca7e83b344057d5c89276aec1d150dce459c3d389e1fb8721ea327e6ac02b32506bfadc4b7cf72e600c8325d6ff338456b8f3408b301c1408e8fb8b26a463c709f00c73ecc1f2f3b17d1b2f631868d8d78c2fb4a4e6b056df8f601a0919e9bd0f462069405693c7a34127d8ee3425440cf2a9190aba13a1bb82048201ab41775e16e88234541caa70d5409dd6baf619b416706d10108fd94cefdfda0d598f25d50f1c2800acc96e1295ec54a374407d36e9771524c25de09fdaa7b9ba7848789f317d15b706fd2abc5d847983a90aab741cf23e7dbca5e41019411848a737105a9ec31748ca7dd01dcd26e0ec2d200fd6892f12fbbbfb6231a1bd9d380feed28014ff05856003b8adc18d6c341e8ced8ca2ba98bb08395a4991f4625e7244282649f19ebf9c7cca4d8f645e9f065f683ce83d4feaf19f01e8e439167e1afadc497179fcba9782051059aad07c1ea0eb8e1436e931dc92b5988c49f9c39362f276d2fda8decc1a7ee97e1b5fb8526120ebbaca18bd8f22a00c7a950a4025788af509ec792478204be7d4a10417e99928923f8ddc17c78dcdc9a5de85d367af293273693a08364037d907bbcfb93dd7502b061082a5c1a67a4294c0c18995052da8613e056c9fc10afe5c359cdd8358b1f8aaaf59cce79d50e4306a88ee6ce235f59c0bc44ce8be234dc318c6e98f7f37bb21f5718c2f0c0ae805b4afcb77f0b97efe6799bb3258435ee48a0cc2bd955bcefd70803d79e6dc51c3d4074f45d8d8bbb83a9973b7f6e2342ce50e83427aafac33fe90ba60eb949abd1a0f39301d6ab9d3070792ff9c1da3c01f6e11879aa95315d241f4998264b11c41a03b5f16150eb93c90f3755aaae826786d4132c605d3820c88474aa79b8cb245a4bce1cea202e177c5024556af3b780b79452ab904345ad36f1f3534b04772d53d8368df7b782045ff72ede5178848e78b3764028a087936c22e6b813602b59128b75cb7b16277e76e7ef6da3ee0ca628d52ea7c3ca6e561103151b599c504dbbd05110319645c08f441c1a8b3b2354bdc99231983c908cd3c9d74896f05d77bd58b85126eb6fb470c6d42776f3b8f35ef9867b15664d880689bca3e329b479a6bb3e55637100b72dfc6b0dfeb6d941ec9c411e0476a1a8df0ce29f419023603d2751cf6c1bb3e37d3fcd4c0b80ca319a7d34b00a8255f67672c04dcd1ad06140faa76ef130c8b16f6a1349a746b94864510ee2dec79c6b47a2eee2d3ac7c780a24dc107c006f07fb35c63f9b099f9fb18ea5751a041b2c079df904538060ee0138e2bc487f8a44916df6fb140da3985f458429c9dde5f9978c3b39f6095d5a98ee918479882bd130285ea6f893b59f79e08b420226a12fef81970bda7eb464b104bc45b40a9768f6288b592073f25b4473362a9c22e219f43bfa6a191fc91eb0a4951ac75d5cb1328928ae74314c7d2ea4b895e6f195ccc41ec6838f11c4b2e517da1857f4587901e190ddd8d333703f2578a32a62923b9064503b6e062d38da5b948464a01d1e471ea34206e3881d633883414241f8758c5a0c10b6b5b8a67bcd0d8a164d1883e72369d01a6906446f69b378a02b46ed701fbcf20ba6511b29a49e845d01c49a51269ecf0ca202ace3a32b70802194834b06e3b7205d508165e33bec099a19dc927c11c1f00b1f4877f5547a0074380aa659f584e3e97246b3d5a489de0764d4b5d5b384954c978835366d7991acea2b7bcc32396dcb2ae8e270b9a80408909fee81e72732fab0e42897e68e5402013adb132add98e58a49a8cf5d6a294c28a9610575d7dc7b3a180d878c4f5567cdb8d6e24dc4fe19445d5fd94072c48917a1b888850135fac234585babc3366a159854b8061759a7a26e3b8eff3597e5e256af521c12c56f9ad879ffaa4772aa46f8e22ec30b1a17978791aeacb409b3873776e8f13fa281482f3a4c483f597b6edaf2ff2375fc871ca7f42445b15f739e3b31bd07846a59f1f8d35526ffac8c6721d5519248d284f1fbceff992cd8e7a9c4b8d0bb121b38835af8c6b5f182acc3be40754e5334f1fb0a2d5912eeb9bf1b4e28d90ab008aee414b4d84e7cab695b8f3d03d1ec878ed0945145611d5496682dfe9365410ca865c0262addaffd808d0db3e011532b7a747a7afda5ed03751d621c2ec5666f10a6cf06d2fb8cbf0b24d58b01777db7db1c898a9f88537d9f1c7ec572fa7bf9b0fe34489912a64cf17dda0c728609c1b2cecd85308741e90806fd0cab439bd0ca53963d269fc080579072d7a123faaa53e7c34f79bc66219b06bef21aac319452a1ef113306a379f89d020d0a0ec1900f6d02bbf15057cc05f047b5ec10428ad84e97f8dc1f777ce6495a71fad09d1bb029eae16e16e861cdbbf0c37c5729e48244c529bdd8ed66111fc71076ab73e843587b204e747befff8f6be0f9ce720c07abc9f221a1760a5676a49459ac15c11ad63a30448318920bc8ff58cb1769635613329389e0815327eebc7ff1e6f40aec012d12b49ec67734328f6c1dfa36d7dc86e6247d01d88a4c2ce021243a7b868e4ef6dfabab5b722018beeadd463cb26303ff0a9cacf72954a7983894f0b13ba58cecb8ffc4671ef83b3ebec87d6cd80959a9564cd9b91d5d775a3384717e6b2d3f8271702435b7a1b189bb3244d412d345516fb121f730b38a71f0ef0be470b6db57e1b1a13d748dc55de9cff683c001b98012c7cd2314be96cf48774ee2ca133d7bb56026301e978f3b68fd704148cd6cfda16b5d490311d6a913bf9e394f64428a8e282a18c0f59bcea263408613f68c28561664e0913499cc0254a1a33bba5c485bdd5a40e55b599f2c7b0c81a1b5fcb6bb81db82cc71882bb74df427cb5934332b9667f6c12adc1287c47fbfbdcb373ae020789a5224d8d820bee93b1497c2fc1cd2b1ef875515a392795ebaacdd42413e966e7616d32a2d9f9db9609c8273d188388dce1f7765051b05f32be0b77451669422580d672421c8cf17ee42f27823edeff874918f02cd0dc6773bc15198a88480cf2c2615c4f884b4375f0053917f3d266fea95be76881d35e4c67b3027e8c98d6548bf6871ad9c25c68b915ed508f6955ee97d39aef24c403a4db1a75ccb57effa21564da594c850922f5a7488a37342b58c162de4e2bf305c42e93d1ce449f6e96c354a2c936fcae0de9f253333331a0682f98a9a3f4fcdd7d9ad441f16c8a83785bde342c431d6e982c131f87ca7802ce1b0039036e5157fa26aec699f7655819c57d59a931910c5818900d9b5d34f55a61184dd55034d33366e8e9da35815919985bf86b9d97c9fc25d5cb125fd1d017eb5d4e65f956c618d4913134da136220d279564e0686f5d21705b128b86f898dc6b84f9f1b1fa167b8f51eb87d74c0e7498391f007f1521eca3c6f4018a70596842ece684c0d3ea98c0dd6ad920217020bed64cb4ea5b85cd8a8322588785dfcb1c45d1276fab4afb5ce7441a521b5731631121e43d9e3925e3d12ad355e14a073ec2b572012cada0a4e9c76e1190ab593c09e5b8a437cdc030e234b1148dc260db7ef44d16f278d3233cff7377738b6b93ffd601a98d61d798ee39489a7667e0d1de58084e040bd61e06e46d2c66469caf330a07f3f487196fc6e13577e60d8dbfbcce95b25ec25f6a828bdd9e88a2b253fb909440fa37b78c50aff8d69ad45eb20d2f42c4d5d961d43d482a82fb58248caad9f90e5054d95b9041c59b6b52ab819cbfe628bf34fe7308f1c60ed282fd7012f2c256906d23ac0c6ecc1ca7d057b267715c0a9c7145bdf954ffbd54159af9ca081c81c4b100f17d98512b6e5b857918793bc32ec58b4a52e1eb97c6808343808dfc5140fa7701c8ba64d1e6e62568932212aa1a9b4ecbb9bee148e4882d4a5a8d1c610a0d595d42a2dd3dde5dd4af1401dd506ece7a3157ac2c10a59461e1b506afeecb4877415d7a3a15df7bf39c51dd92ef0b4b248837c2d8a9bf7d611b5752e7bb740473d5a628d64afac6fdb2518b51c374a6700c4349bc22b3b53f2a84d187ca646c47908f6ef81d04da82acabb82557b1e35d830b8a1c37e8a61c90357d3979cb02882ee1c3bd4850851a22c42895ae912fd09bee1bd632174c256d9355f37b9fd94ac44757eff351ea521e48343222af386578c4c521cfd4ce716b6ff11b932a66d84602a594b619f7e3755097faa2a808b3797490a135c7740856ce3f2fbe8b9fda3bf8e4453e263e3a21cecbd674afaed246d22e723290493633a78a6f0d5890c5d3a7de7103da6c51f358c17d00f53da9b619030153b68dd4e1a75454497597c553c56b8c774f783d6686c6e78ef32a4b52b9cecf76275831dfb3e31e6ccf498e114e04a7f73c6e3ba369a3ce948d9f28e92deaee53081a7c9cc77623286dc7402a218f5bd4cd39a5f7eb31165505de3a5e7f8bdc9b65980e6c80f514007487353812fca0bcf7643c725b1aeaff121f7f6586504e0f89133625d99027817e811db7af59600b85003520d8134994fa80f415355923e47a420ec75a14eb98fc09c7904b41d3294d0583765659f36000bd1e4aa4c9b43461ad8d74a4d8392f92a42712a2519382f91a06c2a71f18f8cb3fb497c1f30f90a95d2155250c55f22428a6da50ead3e8328d7a7c106c7b997d68335cf559e0e316615890416742f20574f68972915772681eff28a185c574b5a1499e11ebfc911c9e5e44608cb0ee89d80f84881164cf3ac55b721de9e59e6a6f793cfdd6ff91515a1453f607d9b58d56053fb115a894047b8052b29daab9a2de5ddebe9ef31ed2a7fb62cf68c7ea9f93bdd154be0e832d81c7c845e74068a51412f0cf81357365cb18952c4558a713fe16a718c9ae7f2a9fb08e41fa26132bafdafd77e43410340fae188fe8bafa95b02104f8f3a859b31d4aaed17f926bef966da9f7be067efa70174d3576848bf463f9b847c3279bc2bc79828464333843b417642a3bdd8e50a8894418fc2bb84d505d2c771a4038104b0b35f3f219e8934a00f13b27e07941c45c4f4b72b5b3a8b37c7fdcee0da29c93dbe976e7509075cf90d28d3d9be8191520ae7c49b56419b34014a37f8ccbaffe010a823a1c44085f181b27fc5c36ce6d8fbcf84291cbd3f5753abfdfac1605ad11418836a5db74cb1f64dc774645aa2e80110bd591336cd5d3fa9088af76a8beea86c7beecf1bbef853128d92f29f67b28d690ee0f18c1c4bc931b6744b8c53e244914f5acc95d35165148fc4489488c710ce7db58511b2262ccd38846dac657fde39b3a5ca310c7fd57044f4551b42d52d4d090930360bc61a337053e7afae74974013f7a3fd3500a027f52246a45052b25e479f44cdc42e89eb06e23875012dcca4d517f0d7dd2023c8b9666e47c643cf569aa475ebd115a19e990171e93ef85782f906486c402251a2f7aaead80faf3f59e510026ca64403ee6017701260dcd173cc6fe05114cbb39ee85218a23486ae24a3b71ccc22fffb14a7d8b73ceaf8a7742d122472143405685379c52ba949aff9e4a3276183a600b4fbe36ee3b32b8d9bfdd636b894fc3a40d98f09ae14be288a302532910bde5a6556c70ffacd8e9d08cce3174f20655676cccd4c595207a6228c751635507087ec0020fd312e1298a257974636dca914e1697aaee7201aca8cdb605e793b1431ea302fb038c24dfdf980c33b794416fc57789517cb20b642dd1b1fdd4de3cc581fe093102d4dfddaf3cd08eb710e08814aaa078bf0e490a8c315d6768419a3ffb73ca0dd0c8d795c70940e77adc725180cdb351293bde6f91d7f7e328e9fec608e54c2c4694f151624040abe13317b89429f27553a2293d1a10eff20093302e965ac749ff0d6a08c1ef4c94eaab378d77087cbe0bbd67197add8142f937b1937b7d0e61a784129276854590734ba1d8ee139f767d7fd038a5a9b7bb98b733fdb4adff519f6a843fa1c8439cc1d66e2a558b9a0dbf1104bb5dcf195db1806d66a109c085fa6b0e79627401d25458db0f7ae9179ebf6e05abe00a51242069411c33c19f129b6172a0d0184726e7297cfb8ece9e7912f0188a763830ebe3e9860e4a567a39ea118b10c89ebe282efbad49c58538b01e8a339c18e3e3db4829ee5a0c3f955846052e82927490140f0312894168c7213bac4cee6dc8d0b6478ccdca4086aba53bc0d579ed6b998d62f6df0c730c30fd8fc13aba0d606b48ad1d55e5046e6ba09252379b14a96bab3a28699e2892fdd12d8b890b7289ea8200d31f18d2f898181ef1b86a0cb2876bfea134f616663bf5325a213b69b3c7f7e71d9f8650b9e1ab2e351004821567fd878bd3bd184aaa4c15293f72683649e8866496488b8531be7c6f0810563a7285a744404352a94bbaf1044796c8faf53dcba1bc007751cde97f81838fef67905aa302b5e79a8961175595616d0292f3a451775314977f6c986cfdb160f4da559066c2746fe5b3b88feddceeba21fe0b0b3cae484746410006bbd485120c08b2b636f2595c909a50745d860cd2edfba7dddfcd6048baf459feafda4365780df1b4682378aee995385f1dcac79c5277b691dbb3d76058e1a5b237b1f8f5ca0876a061b6f7c86a8ebf32bead4a74eacb9de75a9ae11ea5dbfcbf3db725550916e2c845ea43d490dcc9baeb23ec9d0db0656dc6701f4b71739c1fd88d70000221b09023a8f00f6a5e975e4f3547ad132056c7d8a1d5672a8afbde412ef3a510755d04c7df16ef8be4989eca9271837af6fae164751efe70c50de7d43e270304500e3152af6db807abf7b2c8150e9d5e0baca3a55688dfe658ac8533e09d0d58d85ef5c5b850c3e6d60d28ab27243bc1e15520d25a9da6760f13a3ef1544a866fce43aaf32c4cadc1923c11a765d091804265ea12463d30da55956b11f0db2318e8fe05e2952a540637c04352888f55aec8f893ebc9dd306efff2c7dec4464503983886beb8a572f51e8fc66ab9b586b25db53baf71408fe99332e0deec31e3644b3758ec90808f0861d8034a4584ba8c5e67f76ff686b8c203c55c242c2369e9671741d76d663fa61af2be2ed046f40db2e4aa9dd958d8e8cb2f7afd2c9cebd241330815a7268e4254d676d3c5db5575fd0fbfdd2744736fc3436df7e1d1e9eaa399a4891632824fc957bfd9c10669c3bfd676182f519d919fe507bfc428d8fe60dde7466a0f587560c8dc4140cc61ab9c9937555c8cb301863018c3978d17055268b29da1eb20b4dc1450ae972cec300bdbaebddc3278d650855e1d8dd0b6d8610310434c0419ec46358b06d4d5cc08328d63d6ac84db707a5bd86ea269e6f94d132da82b58f859e8225783834561209a8709b6aa5a27018a0a3d34dbcb1bdce30d6977e2789d971ff2542a73c6c760362db0c81d1ea95e8ee7315598d542f6971c3cf416cf5474a1e27c8425ed0f62de301d4e9ede8028f5258fae8d0117e8e4668a9e8721f70a28d648a898adb442cbb72e054745a1b3e556f8f7e81ddab1d5a5183b4da94678eff234a2880164ee9d431ac2bde463483bc8e4f23cb49daef111300b2fc5ec3034d9e14c86adcd107e1bc0de7fcc5d4b93c820ebd15b475df795c89bd226db911a7a8af47c39c3fc74a19cd90afecfb3599b7c467081c35be855faabf57ab325176859cc549d994ca015826df03b65fcfe329fb345fcc546564430dd26690104db4e18f5af0f27c2c12ec9e00e84743a4c6035c5eb92294ebc1f6c23acedcf0d5ebc63d60c2b1fcb06652ed0174d1d04b690f06e54307451a2bcebfd380c7d7c067971ce82f6fb6dcf83bd8b75b56b28cf75d9d366a7e3180498fb3d8e0a19549915fdacb49b2d9668080c66a39938ec03b216287e6257264b5cbcd3b06d239922715ef4f27854d50267c6917941595475356390aea8a599d12335eb0748786cc185fe1517e8b3b2ed7d3c21f1c29b04fe6ea517529f24ad21640778f7a9eab3f0fea616cf42840a4afed4cf206e282bd20669b34bbb3ffb734b8cd15b99e7f2156514c10cdd68393bda353656aeccd4cc25ecbfa7846d93544c706100d99e608c893446a539f0e6f67af09ab86516877add7c2c6c30c71bac4b350e15aa223706179a00512a03894d0971d5e9aeeca87cb2ff352c69b419f4b6089fe2d52e03b9bcca29155acac9ecd608b874bb5c9e1cdf0929859b093aa4973a13a854f42d13591de83466cb14b8ce78610481e36a127350f229aa1dcc6c51c69cae2608403839db39dca81beaf2ef539b04fe4fdaa6d51b032cfed26d54d1ad41a0a90a96d8953a8a6d2db73eca24d58a6ad0d3a2c8031a5417b948fce5e9a62e7744ebfd21406fb1312c9372a2ca91d37f708862fd165d74bd07c7c9cae4cb0426ef0beb4192ed1d6219a463dc892a0071dfe6c288ad01954148857972529d3c90579e656c796a34d11e8c3c0e14849060a1006cb591d63f5f892dea79795462dc7b9dd2b6f1932e075cf067ea587e2a258e2a783bd35962578c676dca10c898ca9845e25ca59379dc51947e52d33782edfa0a965b88259afa3371b84e6850b3ba2a0dd91c09dd8c008c147d16786a3ccc5560831aaa5a0a323ea6edd9c7cc7948365665802daa0c4b5c3c32d49c78f7ff21a873d23fd540cb76c6c39f03c0fb053321a59b06f068d5e36031612f562e1864ac1147f81ec9d17182c63f59808b9880bbedd0726289f5242c6cae2831364e7d06dc0670a83cfb3b169104b5a895647c24b5ae35f568c05c70e862080135834daf55a70b0cce98f9e86b1ece88aa107cd6cbbe19454c328013bae1907a917138253abab253a960f325375aa1b7ba0148f25b2f028ebeacdac7346d41794dde2bb9681fcd158519714818a40516a0c25bff78133f49409e647df1a0299d635c222608a51e01e63307d57ac446adbd19ea799c8433f87f643db3656d9b8c341500242c2151c5824d86002bfb1e27c81c3d59b4d5007516ff7efe404a4b0e372055c1c4963c5025e2920a790cd75cfc11032d8a8390c03f995e6c0616086c7d609dd5b3cf82082958984f113567d2ebe441a3683d5b6dc051b8659c355ed81dcaca1ca33bb0519e079ebeb3f148e7798f7452d299916aabb973dcd10ff05bcaf0406a06c941460110dbf1ff20bc2a5f11014cf3a7a542b11297cbb5a24d7065ffae2776e7c4f46f30e3cce31a4288a1bdb9542eec807dcd457e7384d24684d88a321270a8dd86c2e824eba0f3ba65c0901322821f0a86b3c7ac7acfaebbbcd22d415cf114bab55b0e63649710b9d79e72ce40e3d2d9a523111fd1bbab04ce0287c6fb0990ffdbef20909bf613b55cf50055bd88c506219b85ee33ddcac408376fa9a06ab1419e35481131adcab03fd74b9781ba6b2deba5fd8e256de8db69bc3cdd6ba759e6777a421790a7cf20e3b6756acb47dc18a15606801428840f21d9add54fdf5f11dde5bde80836ffedf4bb3e07d4690394d8ef25cb2c845270b0362e8eef27c11c045ef48a343452163f202ba66c8b17047ad2d5b0a5e0d639fc2a591251057719180aa7b2301f79e4078ca93f4bef6d643e5c2335ce6ea3e073f7b81f6e9edbe5f60c97b1bd9030d6e618387bc9edf2768ac8dfa92878ccfd3e91b9d3342dbb80862d194187daae658d75967baf56f1692373eb058d73116c0204524c46832a3a51d300c01fec3b6fa87be570a0e7ed31126e24d65b2dd247170ffedd7f482389a4600b9ef59ba68f26a277df03ef4d91e0954d22e5a8f7d6a97d34c029ab2eb64504f52d577d9c5032e0a08199f513fe14ee108a5abdae2e70f2ef43d658b98df723bf46a8e98fbbd7f31eefda40c634f914a3134204c0016128da1faf0581edbd66b3580ca6e9d8b517b5b778614edc8db84636cc4a975601aafd69dc5ba6621750c9d6a9cdbb00a3198c77a527737d32c63a36160ce53aaf19e05f40b41274f3934f0d1a6740419fc2f118570e8138e31238052d9fc21a7ebc3e5a37ee8598c813af7a8bb7f349360886c1fe53640235381b5ad1342044242fdc103f1ca505ce1f9c5c2062a183c8c40b1debf95736cd05165a5ca58a44fd8a5bfcca4b920c5d71cb8de5eb0070527f0ba69bb5c7724e9cb84aa7a8dde15e671f459e8e4472442120a1644889407d1005b4a302cce5821895423c6aa782ee7b96dfcb6cf568ba0b5956ce70850b7276d7340a83be54a29d7b6c60e016009fdbe3d9ad14c3eb9cb0230e2f0c18b74bfd91f3791aa65f88fddf492ec835e001a4a68f7882351c4058f76e9bd14e67ed0bb64da3ab2f91053840413b322e42b604af8435dc59d4e413547ec7ad091184e7e0c45672bc6d7ee08bc665575101d5cd92cbbaee30cab7a6e34598089d2cee829adccb1ff1fbe4a8929d23c237e61ed34c90398bc9ff8f42a5588962b9e61198d1a288df7901e2e6f37aa6979bf5de7efd239a21c58a1c7d8c6653f245738643107799d10095c8e6ff2f47d6e2db1bc87b498c52de49aac05868a6f87d7136cfd499e2df012997f3955464197219c39748f21068df950ff58756cdf3eb3aaf8ec612adbc2ac1c9f418ab27555c5a7663212cf9dea09caf728f71ab430cc2c556ac81d3dc4e649af93429e5c092aaed48315e6320c2874c250b5317835e51ff94d169dddc86728902f3deaa4822753556b4e1c9515052765f29317b0d659c82017fac9c8c3d6da0efb78187641b8b0e111e5112781822d1c2254d02d347c9d5754f7a53c00498806a998cc819381226a18b383932c778186fa22b8335d25e24ab8a862b93ac340ea42cfe8fa2a23628037e83ac484594c36841fe7aca72bdea8430efcecb8a983a3a2d99ba348f764a57c6ef2c24c876ec25e39c552f217a93aaadfd869512a9c47c06b8c371ab88f8d2ba72841cc6afb77d68f97138ac87cb4b19bf6b6a5edc10bbd1a99e9b866e11256d6fd4311ded2646946452d24fb14e48ce65a56286053a6e66f0d34ce6429a30b4066926bbcd8c75a7a02b9740f517eb7c0d11a1bb4a2595c0fcc598e96deebd1ca340f52902f5d9f02d2965b9a8691932e238a158b885d8244631421cd6e5d942924c646f15bf1c91d7b4d132336d717657b37a4f4969bbcf4101a7ce734f99ce25e3ceecbdae66964fead001abdfd241045e45c6c5b7859ae4701029f329014300b69f5a35a814a9fae448df2cda6aa4345e22167f012f017ef2b4840118a19b24dded57ab0db3a3561f9b62995ddef211a4da568ef532de2a79e666369c446d67081ef22df077ce14a123ccb37149a1569cbe9a756ac9dec917122681d79aa467a42f5cf273c74208c9c45a8119fd425ced7669ff1e3b1c633013d7775984fe7392a2dbb85c32e018f601cc97ebfcb29c98e07f3a257ad4dee3f80e993a2dcc8ebef62ebc6ccd8d9aaea892ced84ff5c97ecaa6595710a7b34fdf8a072322ccc4cba74187f302a0313ac4eeb6e1022269d67fdef04c441161e33bfb32cc20fd03eadf1f4f336e71438546446ec05b48d53990e7e535c4600206234ba9bb96d5610d05ab0018c68fef7bf22fcc62470a38b8d223f251ff3d9445cbc79507ccae53e84ba125d22474a9782c298f5ed73e42da99c5d33998e3d42146a443f185a0a83c64d459005d2d1a702fbe5df4550ad701b409c53d7f1382435c78a658f5d9a3ccb2170aa13da506d9af4b4c1c72ce5f7c6d02a5c043aa6746b2ac8658b81a12dfe6b1a49eae6772ebcf2a22064ee83882151f6a948d76c5836a27c64ab76a27d42b863213b6eb51bc6ab38bf6ca370bd4ecc82bc9edfe1b0ef70cc7cb778f8d3676f60fcc0ff60346fe4e541dbc5756f814b691215ac49204cbc3869b728887ba54754dc38371bddb90cba9c5e632cc2620103753ed7ac1788cac51a1c5cc55483001fb026ad868b6f3104ccb61bc95095453584b85c061abc97402451c16464df3d663af556b335b5f77d453431a236278eccbea0a49c0b9f64608001d4b501157ab43033bb90945484fa18d78abaeba36200b398da070fc1406a78709c30275e84d1deb33a3a104adc73200cbd694385a6497108a35641bc4b2d8055e497ca2dfd1e3778c9b70e8ae246507e8c62298042b7b03ce86940f618231763a589d149c1dff05fd96ce21da2116256b8f33e2de0dd6a49fc265d6a9d207499016ca6bd07683331685405d3077fc2e6c3420015f94fe3260f8b1b67ece083fd4de11b07b882f4d2946b7d994a53b8ecd2ba91333e106808c2f4fe5c88c59db8c223e6876c6d1878221e49ca6e2ba04d8b67549811b7620f4fa2fff5e5c852c1992feb88a3abb891af35988042bfdd86c6c17b3d18082252ccd8b0a2624aa1888081c5a09fea17f896e8caf2e13dd11300ac23b0312e845fd20b292811d2458e9e56b528b1c2f4917f41d86253d7158c6aca3a2bc4b5de3d5c9c80386a507195761ff0c636a01d7ffb38ab3ca69b5a05710fa20007843f11b054cc6c2ff9ff956812ab02f76945326cdfd470f978360998b221ac5e2736a7cf45e6dba774f20543e131dd1317df1d49de047b952122f12ba567dece9b705d1de1c6746ea92d18dc303c42c92358b2abe44429424a997a2e80f1e719de2f033da8ffc6aac96d2fa81a8ec5f78b2bf34d746cb3d727a656dd124c28bac8b1ba7124889efd562cf00d9a608a8d0ab04ec46fe83196d0ace26f554d86ef45726da394f099c0c8563bfc0c37519e6c79d25dfe578c4e35369ac71031e6e881b0c8eac4a8a2ff2b279e75700642f6e7c0963731495a002f540f469688d36e96ba58b70ef0ef4be43a28bb2de62c6669e59bd2acf02177d8cd9bf48b2e7ff33a92d6dc57a935a356d652281a26efc372e517b742d2deb07d55ff5d65a6c14124d092dd063b4bbc934bba4727d205232a1793d5aa1686ed48dcb45da0d31b28ed5d6266167f71f18de2bad2f443f4cdc9431ca4c30802e8b29afd80c6061d99ef2855b26b23ade968ff2c1538476273cd34adf5a98922f78b8bcb6f74cc9139903b13206fe566a3bc25420df18b0653ddee6a124b2e3bda30c2e210d522c14fd6d07f996345358edaef4e3b50845d57109151e118d99e275fa0fafb1f04d7ca243606ba1373a9912756d3edb205a236d426565a2ac2c1bea64c9e412685a1090b503f58b1dd946d54daaac76ec74c9c9b871e58e2044a04f75e97663d64aeb714d5365b563a74b4ec68d2b77043694ea21f25d8ec8320daa2d75aa403319ea12752356dc10968db531df33372847fe6d2a5cb12995abab2f699aac3d246e20a8fab1908547827e9c2e992204a607c6c307c375599ba18323bffa26ecf067b5267a414eda7c7fd7ce51e1e9e3a6959ecf112a47612776712f866309115a90600cd3cfeb581bcefd08372870c69939d1e49dfcf5f35647023247b226fa292d7c95be19903c72c23e90ee3cbd5917fdf0c6e03249cbfba3b09330252965ec034698ccf88e906665bf64480d8b0953069d16cb8581431579cf898db6ff1568c5da5e3cdb31636db757d1322f4627dd309135a88ed1d2eacc7a5047c51ca8578b9b50c3836e25448b8166af80506270c4217690c2cfbcecc025bd8f5d93d3862af0b43d0f3602e41b4905e6a187a6ebcfc3984943ac788a65d4a9605ba8e765a8693f2865499ad009616774607ffe31a86152cb77b4393c221754111fda029d097252bccf982c99533964a7c939c1e3c4d30afcf96efbb1c477fe4017c5c77fe7018f391bc5bebb484f29bc1873d869bfa1f143ea0a5a93783f1a63582781de66ae557b1a242687505cd582c7cf4696f0850782a44954f142e863747d0b5139c26cec8a342e6e63ac49cb52550515eba54ae65a228d89afec983ef4805d280b75a9be0a2a9aa47fef8558defb907e6700b799a2c7a5d9c3e044ea35df5464f0759160b2a79f47be0486647cf4fa6739c50894a998b2de6cc0c740a92c0d6fb63190cfb37b1642bfa38a82bb5eecc8c12a8e71809734b09bd173f6ae05e5dd66eee0e7914317769c095e1913c78f4d94b2e0372da27220088d92d6f12b2fcf561dda22d366b608a9168ba32d16f0370a494c3156a29b0ea0fa2dd6966ede432164b78b65a51412859caf0165c2f2f474fac3baec774033d67f5b8b103d05906934507930f8080b7ace89258e47118ad684a5406ab0315154f38b8f634f609fafcce7a4b6d304415b02539d51c25001b59bdacd439a116cf8a4ff3e28e036d6ed25f1f5f4ccf3b6f3976d670df5b9ab3b99a7a837e1d950a0a79701d871fcdf4960849bed9363ff9d009726703d18d22ad705086ee5503b85cf5ed469ae6ca5be275ea1a381bf7a193e853631732abfa0f049bae3b1d06303427cc7ee0599b2070ad5ed66c8a5b0c46c7d560f7e6b2291a4f39f832be131ea599ed8c34195f129658be1e5ab6fe75d201f522d91a73fd590dfc4718bb503fe50e2e322c219bfdc72b4a21223e9a233241d582a126968ee1125bb938868866f1af422f80214e882c48d3cb019cbe515264a4848c8bd2a5d5e46e207d72e1ff083d4ece33f6ac887e28d8be79fbdb06035c95b826c200034c191ae248e94f36b1f5253e9ae9433040d570818df4602d461a3186813fc6058ba4c8f3401b394dd1eb84e3bcee1b090f790e5c913c92e8f9b45d18a3224e6f6989330466c9a6bc58aa739cdc19e57b09c4a4a09a360e8e34f1c77671b71a382a1ff726e5303cc2865952d8851724a0da20bdef5c874e26d32740533775951fe42bb36efa0872e52d659548871d67f834af8451c6aa5fb1c6b791c702d036490dffb12a85b91fb5e14f5c237cbdeb0ef62a8c0ef9f469bc45450e2ab5a70f328b1b3afd694a824ea87ee9bfc3066747ca5a228536b8c3245c90c167d12815dd2d1a8b2bfb9d8d5e1f3ed606693d2affeebc37f770900227b8cb32ebcffbc9145db05200592c194ba40171b220f335b8765708e5e2c93389de47f836ad23642273bd405bf715fa37ba27d3a5708423ebce103c53d3fa2a1ccb89c8695b701a1ad096e6abcfeadf989fd964a2bbf4657aeafcf32084474057af9cb18a2f769e07cde2aca037985d9a202024128921917d9f7278b861699cad0a8e3d24df620c35c0885ac11b9b4900f745ab2057f592b24389869288bdd4ed72219579c51ac8f792fd97a9ee7f4ac3d3e06cc67eafa14744704b975236583673b43406fc39fd77b47e2cb5bda79b0fa3056de9c5c448b7fdc79f3d1f3cca7eca9e7e9c62f045a02f68550d3d861b703878bad0cacb528d9d82fb111dbd1960dfbc45b0dcf6401460aa55301356963d95270b2c8fc25b4c5be8162ed08c003f44a6434801b421cc4060e8d7276656dc670cef7347e6f3c5ac498cda2735a850275c8cec97a5a854cc94a1b765ef3bcf55f0cfa9d5bd4a921f86cf56210bde967de01362df7d7f4472e0635e243069e4ed5f447186ebc9ef98df37c081875f262d00ecb1389904988fee9810521195d25434f5cbf9e43df8eb04abe3a2869517eadae40141ff391e0af0826b04499cc6549c5f30ec45862baddc4559a9408b45b349567b503fddb48a1e8fe40f88e721b6718538ec0b4393084bcb895eff59fc8387e1c4baebc0e70204db0b2fc3b6280b5612f0626d554ec39cf036a10059583bd82768db71a8c4a93861e6c89e4a897af24790ee0434bcccd9c566b45dcb53eed95e6bea707d19b10655a7419440c368a8f96dbb0e702cd39a0ad63b70f01de51caf1a8ff11b324eaee9a9f4412b85c2bf24ba430f501ac93a0b923e4c2c2e73a141d23aa0e346ae4e4d040c35a0faadaa9b0ae79cf914e1a6e6abc5ab7fcb54a8fe55276a555ede50232d0b12d942d926e8acaa82990f5f961718772430878723cb6e5028e607d5fbace6684b5da4aa386520f56f4a54d05e035c988742e4dda8928490bbf78bfa50078b277e02d78ce01dff0cb6ba860b09471d776a7f18fa6f80469a76e30f336a3eff424354bfe04a3d5406ae952298d69885a0a224f4a9c0d80c289c748aab83259c23dd1e8df7de10c168011691d60fb16e2f16da096a2c2899f064b70fa732bce340589c53ce83e23fc7c109988b28e34f44346b6879d44ae196504ba27e41ff6440ce1939f4860e8aa9fd79c65f2665e22cbf4100c2bd3932fc1d8a2093a4019312027535fafbdeabb4c3749ac60a596389dcd9d960175fe130289ad89389c845bff40b85031177087da19bc01081b1a370543167a2b86776ea11772d16b6dcd51ec240588507eaee5db18ff1b72b88b232a764498dbbeffa33466002b49fe1448d564d0fa16023e5c43da3fe8f6adb57cd3d74c682922a886ce749ff59d1c2725a3dcaea658f140ab7aaf124ba52b2b5dac93af12a3312a5e4055b2d9ea6a5c2b3617ea5111bd7521624d66c3ddc56896a20092666dce2baeb0a3511e1e19013b275dea7bdf38f7f380a5808776e16a258146977fc880145dbc3f25e830bf292d80d4636b80a97a98d5efee894061e83053dbfc68c23556f56c7a28988af566a7abee9ce0ce5f59c3e05666fa9fa0856f5b0d90f9a005d295b4b307e840b05aff764481f2562a8056408944aae1ced81a2a608245188f100690ae231d9594481c4c1fd69c5d3631af392017af1b717dadc6997910b8516dac8813f9c1adbc68d8ebdf33de8f235091732f8e5bc857d87c37977d55c0e06888e4d2db52fa06960405b1790251a43f31339cddb084b1d3a8fd6f9db384132694a1d595386a756f0032c4666b9ad32715709215aaa390f927e6d2feee77d3c3a5c599dde4aa6ed4984fa0b6fce7ac74c01766717ba15ff2da874f4e9211e8520ab3cb758d0214b3039eb7f27d9f00439086e14878882c2049cc67106e04006e136208020165329c1378a5be4b53a0494ea254c5920407af25811d6ca08c852eba0979dd81a89c49fbdf508bef806df9bb01697a3a54147b530e6495b18f4c420c06d11658c740c931c71be018fe81731b446c30af6a86b2ebab25cc5b1b720ce910eefc6bb97585c60b80215c4ecf1b42237161563ff5f3e085e53c9b158a87e69d47a867193aea926a8cc41f35443412c74aaaadff00aceb09231e591676c8d8f2538d340b3b43d547832ed0e94a46bfbeaa125bea63ba3fc4a52c13def9abc50cc3f930f612c2737292633a9a11421dc04f23ce5cb721a4f1917c0ae80214ebc054c4d82c2868ae3d85b7b2884c238431ab7735d00f2aa0718cfcf83ae94b2555ef0d61eab89eabb8bac91b11ad349430101bf088f854810291ae27897b7067823e39fc439efa9a29d64ae9122e1d8d5db5a8a373903de01ec59f5a41cb2fc7e42037dac1ce3a14edd29cbad51e14c3f3fac2d98af37968dbec0dd89e267843920234a192a6f34350a271db09f54b6eceed6a46a6f6af24c02e29dff0740666a948ca8a81255308efb487e346a890506cb7cc342a2747e28178efc6147079b84cb763ca9636591e82197ac34b84f41b9f22446b4cad725a61f8141979696da6726cc7d4b5586ff68b75bd00500a3a076779c13e04f204d3991e4a38a3170a07cf3aad80cb276f7c1c4ab1c075c353c60fd803559324e8d7cf6d4dc05a574a71191b2437950813303c11315559a36eccd431518f3b8990540109047e4234fa08aa78c2339b2b430f05b16af818b1b7d290b92b6a6bbd984c8fa3fccdef4272e2476c12bcf2b75d7c6712ff6ea12b143f5f7342272107a827da768c5e0e56d13b1dd0d0b28b049421018683203fe07991d1f3a7f6f1d727fc17a34aeeee4bc02a276c6be9c243381a59d3472552e8e4a37794825f63ef2895341e1b21c8ca382750fc4e66f1ca2b40649a99d1b85a4c3c8a82dace15f0a2d3882b5258f99cf6fcc4260f19775c935d02d6e5cc5cea4021029b11b63cbc2bb605d3e17f9d13d5497efcdc449d368bdcf007dcfb164537f5a9c99f66a9f2d09266eac6c54e0f82b64c424896add2d32ef98fdf26b7a2bb934b69fd3001b43d0e435aa736dcba86d765719d56cbac09c5759a3d1337c11c9429e461426b218aeeda954bd21097b41e7edb17dc136b901ff5a54a0134dda4f6715b7b52b8cbc2a7fac4f4c433c6681b8535838ccdc92606b609acd3154e45c949d7e3d8ba592a59a12038939409a6a3b2dd868281f50fa28c6c353864d1592398f6375412dcada4c21cde38c0addb6fe7b7019d2189400640c8ff9677e319e99e643bbe7e77aeaa169982ddf41456d62bcca7003697f4cec0dde8a785e44805d4610ca42ae3814314a5965cd553fb21424d5ed06cdfb3a36ff278349d1740ab2d49c7a762d4899cb09af04a5da9afa15d2a1bed42cd22162aae34b1e08a2f124f9ca9fbd4134b7e4c9260e9597f781df921e3904d870c0c1488d77e9f313a7bcba54b19938d1dd275661a1f55a03e65706217aaab838868340cbde41a268da5cfe20172b9c1bcb06453d357b220632f6d44bd3a54e16f3b2036a824feddf80ceaffcf62f710b50f368d9fceb643b2004fc0b7422d7e8d6564ab303ee2d2af0902df0523f39818f5a8b345f1e0d7cc7f42be003c4afc57299cec29d591d38e5c0ccd57fc9b79916da9cf13814df1e8264d3b0a35edd6011a7a90f147894768dbaaba2e1da94a0e0bf7e3f59ae36d0686d3d7f599f1de590b6ec96c3b8ff9d34550e388fa7088757ff684df7a8758f7db2bb1c4654f9c4a7686a2ae7aeb4ab89aaadbcf7ed5763ab784b41028dadbe573e9d57e4917d713a43218ad96cbeecd41e8d424811eeb147bd42001fb7a55203ea1577011535701ff87d2d5dcda9e78c1c9354337b3307c3d74f68f4238e4cb809200166c3db6ba803de3fe64b0a5333c94b79d311d53bf4e5dcf877d12d3668ce81c15cae7fdd01d8708c91329d3497758d35b0f5ce578651f3d19824c8d67bd11a54e6ca31502f7194f0186dd18b38e78856cfbf86efbcfeef847bcd31301241c37124f97cf90435a0c05af30f3145239bfcfe4c7cecf6eb39faa7d303e716cfc5e80452dba5f4824ff2b63272029c5c2d9ec134a67c1220051296273b378d998a8371a5d72c9b858a31725011adae86459d1c11c5e827f1efbd352dfd77fbb6d34cf7993cc59d4ee40090c278ff5b3e987226d838283729496acfba464fe5d28aba4bdec4febe86c4672d86ac9efacd87fbc1654e6fdbaeff61e0a5d74f33ec8d173b06c75dec5ee5ce09c97f8192b659446089a8946d1b57d5a5fac464630576629915e8ed3dcb1152532da4f31d2f6cb2fd644ed10debb6fd809ea210a506f06f7d659f7ec03c248d5d9473bc140b9c4f767e8031268b6246d175469ca96b8c67a1be249abd846c1e3d7d1609231ba1012055de18669e88db1311b359e3f74f7a3cf3bc13bf11edbf6d1400c33283bb42c1c128434b3a2270c4d26ab20fcf0befe7b9686184b4a2b9e9c704af32f1742afd9ec8ca9b6624f8988a64af78c3bb46cf6fa6cd41ba2caff6fb05383d9d264400275ce99c79cd93e641c9846b4b2e670e48dc6baf225c3412784f0339bd940dac66a4db841abb38dcf635dba30f72de6b61a807962a03dab5c4425a897cdc651908248e75bfa5d11508b04590911cb27132ae9cb538e06a9c3979fcaf6fa96ec108ad98a0001412926080581bdc69ebf2d0b530e2484e3bf9616a8a98abfc3348b8147cbfd8f889200fb4d5132993e2f9df7acbb43700130df30b4a0f1e2dd9b6a1811bb22b085f0f7e173ba0cd9b30d513cd23e17d65df6dce706199ebf8411ac7be201b4f2944fceda7177c736dd7e21608236ffa7b1d74b229d1010d2e2f655ee120437e0c2ca4c0142a8209869e8fd8aa21ddcad63023766575d702c94e5609d843688dd07bffc884e263bfe74fa9a14c84c35bf45b24374e85a0168aa37ccf85dae61a3e64496c903d140cfedd1a0672de0c1d85a11f85bdc7c6bc7321030774c47ed708baadb0dcbf90b8d5925a1c480f2b7e4bbd8d2907b6167911b66985d6d7b38cefe77656072ab2556775853937527038453ec07470daf80e338b6857cd326c77f69e447f85b8aa3d5cc637f64965b615ae605f4c2843e162e9cbf73ef337f6c639a5ce1b542e2aae465f14e92e635b8d8d2b344fbc076cfdbb142f489d8da562c1ef7d6ebb03ceeaed07dff826e4a861cf29642fef5f105e48e0bf0f8a19b624c1052b8983a13f315f9e581a679f10fa1dfe952eb3a874d367f2ddd716f000e85913673d23175e24da7710b1ac4e94d1b1f72b426d84061d74b4cd70adf252c54b5c27c2856bed277b931322b439bb0296abc5e80af31017e3dfa8c1175e73fec872bee8de3fa513dbe2e4565b1635ecb232bd5c38804fe0d74f72e9e85c42a50bd54e3ee2f460d12a438b4e12c303abfda8f9ff29f2c207342fa2bb49721d652d71f9fd7e52c0fc25bdf2b9fb50d99a46e68c99845dc404d27cf0e6830d93a7acd9f11eb0689622b8adbb27fcb084f4156c7f4adc55570fb04214550c1b480118f5ec9aaed049f95f8e722a70e1fee0c6a5c8dd21024499539a11a32a870ce35f8e81f7681b035d6647bbb6565818de5943fabfa42e1652738a84e33001d4ed90efe0112d9ec2cd6f02db21c938eac60f81ecc840a974e21305c2b070646c220a439558af0f9217c59c3fe738c45459e694155ed8917b3ed2d7ad578798c39736c7481ccbdf3f8f910782394605e14f4bdd65e0398a462ec5c4587469f088f1683106f32c331635f91946c730d181d752d1f96621a2e83ae1803abb7d3393b2c7a0cc81d65bafaea84ff00540355cfe30c7bbe7cab3d3fa00b65f221b9a6adfaaff49406c679947d323e68649ae2847426953a7501357ffeeeb41b3277c56dda3fff92f6e7875e14d206fe52bab583435d5205b3fcd50c0afc16e0d5b7153ca5ecf6ad4857c54bc8f7b9cfc596ddcefe3df6dd3cc52e5bd4b9a6173fc245e9d23ac685752a18c87928204cfb6d4123aef3ff68c8487526948faf1b3978b67f10714e56c1888fa646991f4256932d9c9ad7daf41e94984266531f5092505c0474d1dc887c4582c39b27fef06674bd3d3abc59f46978f3dc256f86379702b856ebe6f8767bba25bd8762f92b4fe1a9c9950833b2042ac60003ada96917b40b5aed7c985d1bb4eaf77801bc7265ff5fd49b68cd4f07ffce3d231dc57ec1faa340d87d722a00db55bc6428e9fa5b5cbb5a9db391ac4aa5de941ccc994f1b0952f75eb16214d5e888c43a1dd7d6aec764605a1b0163f99ffa11f49be5bfd9fe38887f19ef75079eade0b65a7d447de5d63769d26cb4e24c440f4e9db4b1481c8bc71e75d55e46bfc8fcd1e48819cc01c4de18fc01fbb0bfc19636faf480c9063bfaad5d8cfcdc79bf423ecb41f4c37e6ba0e2941209166ddb1db304eb810a70714d66524fe49bb4ffcd048c3813cb2fc07034ebbe600e464902a1fc1351e3de7a2864b01f8b9c76f8f485267b7bc9ad842a05eb74e6757842c2f71b043936994d6827cdb5830e848995bf11180d28f4000e93bcbd2995d66748003fe8b1a084e85f20e482bc36cebcc24f8877e50fc4c68a2d039aa79c61c6912db31b506bb22142e1e0207add6d202dc91268d6c4532da305959c3d14e2eb6c423f36b468ca85c1e2d92d1079c10564b563da922a511bf0ec0a454d554134ef33091d115436e42856999fb9c28e43c68db38f5f0410c2d912cbd89309a5108915c4ed54304ddacaab05add9835f0535368d1927f452fb6772286945b2458ff1896fddcbeaa5d5c9ae220078cf4436cd69cfb545d236675fe25188d82e44552766c046997ef5f05a22d7d15fd3279fb10c635743a06789205ddef8b2b8a37a257959a4bb17bf2caf9baa3ccfc47926376909b809de9814b5eae3d13565f3cedc05358d648a5198a35ac2c081e53c44d1a7142d4dc154969dea4de9fecbcc67bac245b5753b6ca22dcea18d2568ad8075eec211c4511d8374f21ec121f5d9292a747f127ee456cbffdea020c168fd3bcfca563bfb8f0d409f098982503f3f3074db2c9f41249f8c4159d8f935af1a14f25d6d3f800c13d00f74b3be48e080610fe982067ca5ce914465ab670bdc779517db48320b9b09017f6960536091a8000b94026e4e9155f3e4d588025e2af9dfaa6a2254c560806a356988272fe449e67b7cc2616bd5d10324fe496560589b4d38856c9887d531b8ac14c48e0a53f54e586f1906a10aa764bf694ff585ca5687a2341c9690c2c516f64f84a7db1cbca590ddd7a0694bc634d2ecaa6756a716da873150c591cd33aa8d45780aff78b50e5000dceaa2ace8e9113c7a9b5d72fec63c3c7c2b4605010eb076428a209d612645de3529309210cde6d142c4949252930b216dd83bbc16b7e4640fd079f9858314ab96fbbd3588cdcb8b76799a2466010a85c3d0a4bf6c45e72d8bace70ee9d1a610cb88a1a1b88ae474faa1f70117f1039ad2fbc3420a7b50d0eaa4cd840976e92761f6bfba7f2e60008474492ae124fc359aadee1c31ce27759d55201f83822f87b8c323747e245b498ac47477dfb769a607bc0e46be0ef22c267da154e65465dbc07a37838c0c63a0f024995c633a399edc221b930835c67db20058a24d1679806ceffed8c83898e11dcfef8571c2bd60d3c27a6925056f56b06a805fd933e10ccc71341ba911878d03fe782475483c3cfb926c8fa8cf236a48711cac4a4c873103d7a0c9967a36c469ca44c12f6d0990b7241430256cb1b1b377845653ed8579ee64a00672df88c63220a04181b357795085c391dcef0a4ba75f1c3cf9bb22800459022e12744f4b61e42367e52ce0207ec5d6ca87b35279ef963effa3ed8e436dc2a0f90a690aeeaaa30ee44ff90d230709553ac4b31606cf1198ae21516ba84c460a68e370b892d6abe3d21f27d61454dd232bdc8bf14166e4b558bd1fe76dba97b3afe8cab119e43dfc601b8c03abe9f93d4269483a51e9e206ed0b76fa2a1e118c0b5225475cf05f5c246e5c8e44a1c7a0444f71c536ec1f5deb09fa0bdcbb6f7069a8ffae25a0a98c29972a1935fde518baebe353a670694b03ff6520c8b9103bdf1a781c0d3d1bcbcc79b9820bec64d02adf2ebb559a74229d95e3b995ce1f922086f0025dcb7e92ea9033176246537de8947e8a185340c355f98c10b0a363f076476e471675fd4d3d38e037fe4088101d7a387b0dea526f2979d45d8e0e67d467f52f2ab19cf0792daaa5bb7726de5d63821e6d041841566354b7142301f0869a084f406c8e622b51d6f3499185e87ed61f18eae44506a09d1ec35dd514393c6e163b857acb7119bd0519bd02eb4023ab319c47011e2ebedcb8176538ee48816b13928d65e6de3db676e6ea4359690df6b335b0ee7ac08232b98d3e5aa3cf576fc1b8feda8ad8c831d716b210683459e8049e1a1567cf31baa5aa0cd8e5be4e3c8e4df3dfc0425cf16496dba0b1046237cf7dba49102c4bbaa7cebc295bdd60f5f2784f1897e34dd4e333cf674b1cd55b9d3e313f19ed551a4bcc090d12e28dde0ed9030d7f250863c4fdd49fe3585fd417a7074622f8f24e0b84eb9ec89ea33bff012ee1b640c2fda3dda317b22f09b5e166c4ee2a6109642178bbace073867dd7946eaa6c2fb2730bd8f1e5b0919a17e4f6cb3a7c88d2a2715e0e3854a248378f6d0d01ac3f192936bdacbbfaf86ab91c03874bfdfcda69becc714cc4e8e02d790495b5f6f53a1ebee9e9f81126f4747c616d701f8df17d2a61c95e309f17b100204713d52484e262c2a1512ff8d7fb3a51381ea68695f4a5d1ac34047d2f5348425e6cd18a023fab3d9a9bf429c702674c508bdf1f797933abae37898cdb5f02107054f32d17679fd360c57ae4789432e05ade29db1a63bd6af8086df0f25254d4d2b3260f32db1ea26247a97e5f1a73042c00c2af929791bc980454a6631ad5241e0aec882896aad9f924cbfd1ef63621f8e07cd03726b0c1af08253266d844002d0d71263d6dce5b88cc098fd3c61eb16647d0663334f0ccd925482d63492f22794c2a23d23da0dfdf14676bd98274d538ee87b05ffbf1191bd8b37f1b502ca349d8a2aeab8dbf6c588b7df9ba6ee5147acddc68330c222b50626cf03b73a1936e93f0182ef57cf91ebc7270f84f9b8e4d2781e8c4d880c112ca6b02e51a11ca138a7bd702d268d8e42bd2549cc80965c6aa8e5987c649f91b546ece294cbdb5466b03a9aaa572bb189474a306190c431df962285e0e98d8da1263871c830786d30d66014cb17e5f9c410b9d50e19abc922126df64b44fbfafa9a55fda455f1558b6db06d5cc0a4f3c63f97f6198f30902b9ee72564f79c006c5de941ace858000b00f788212eecf59589e7a9a622717af157b291f9cc7a3efc23840214659403a9d428c69a7ce455fdc277f245ea391e368b51a98cf0d0e268c67d382da81837d3f4799b9cf5be02e1afcae6518983452a28ca34858c0bca67933a5f2fc127d756b0dd2e865c40395ec85c35cba80aee102edb1830a735b6f41ab1662e4dd5b03d6b8e1ecf1b655227a6ffa2b44b1c63ed8383ef149f1f4ef58b58a205761ab6a25763345e45274fee93f33951b28011b1f234af12892c921af9bb93f40fb38b535537df30daea8b3b7954e091fa41fc6cceb2c52501f303eed5c29f5bfa0609e69d24cb859086b662275c825085d7810c5f6624c9ba06c16a7c0a17ff1c0d5059628c43218b098bac392807c4fcb76ab20daa08a9335af518570ca2fdf00876034a2a81648d9b79f9371227345780f3d0ba465bb48f8a50bba1c7a63db8e156f90b37edb0e2c3085fe4825c6925f9a962b085960ee242bd7acfde1751334dc8a877e9f115d2a1ba2483c20c48087e4d20902375ba623b983a1c19b774cf6fd5ffd280624dc37c83f360bcc37d7a4ca1892d348f54a3322088ff67cec29f5f950c00ba7c794fd43d12f73d00ac38f306ba750d32577f74b960a41383d45f89675a684fbefe9a7ecad387410d76ae9e6f17bc4427c0bf862e94b5f91ec76412e74d280ff3c022b0da797868c00d08e8f069339b8351314570100670445b979a0dfbd92657e76c618b1f3baa8dcfed9608c74d903333c3b0bc8bbee996db50e9f2a22c389eb0dcfa0814b3f3babf40ec3e7a088a3d043c5ee3f5ff721386730445ebe4747c9e88286f5ff9a5e941ef2bb000a455501bba19fe735d3373e5cb970688021b84ca746ebcce4267e727542e6492ea764a4284e7c21c7a1154bb547c86dd58ea873f891a34e51b56f0a992a3ccbfa5acb3b5ad7f2df9ff6b9f2e5fee62e01b39f4d47e4ad73d2bdba1223def75bb9155e5cf0354a0e7d0cb3d242a37ba1c640b73ffdb620ec95696ee7cfc99928d8994fa2f8017175fa09e8c57334a7b631e1ea3f026eb457d25d922780279d07da0be597f891c9c7a9175c0a0c4965f314f0f5586f0199b1afb8606006afbd5e5af973bd7eb4b6492691599fb3a0ebe090c2c84f1195cab5964af7df13339fc558fe03f42ac0b7c2ac65c6b3775df838b68fadf41d757b71c82a0d0afb85792ed6e5e8d4e352a1b849f8a0579686af14fda476fb246501bd15c21bbed9b561c9d847ead42f18136b21f02b11fa4e01c9bbd4238ba9c88747804dde265a458929511fad9bdaa870a2006ee737240d5ecf08dcb4f53952922bd888417130643c03a3fc8aba17ff6eb7397cf9e4763a1de7139032a602f4f6deefd5a8a1c9ef1cab93d05a634589094389c930d341508a2df02a1db0aff298abf2348e53a2f7376598d45d3b52e6ce5085eef21429813533eb3a68f6c2800ad4b638d7f2fb4ed259149c038486a2616e1b905bb83347e266bc0a2bcdaeb465c793c7eb0174c8fa4db3cba4b203be3b88005504852ebb484e6de72339344af3d0e1341968711619ede4a55965a6b63ed59727b89ee34f36c8127a354f989465a4ef33e87ca5b7f849f6f95b746714cae28c5909610f1fe50df887899b06f45ae15278bce8ebb53d75ae4d567037313e69a18c5ef0c6ac5d5ebecb1a6ca9d644e00cd3d205f4071975c5a75ae7a7d49c82e4330432755165003f6940f1dc4769e652e3cc763619b8a9f9d0af08971ac4c584d1732ac535147f6de3b01b967380e92a4affb5f407d83ea32c3d9b9e66d1280f8142e24d857bc43fe8e4b9cc1bc237a6007fbc1f0ff48ee95fbddf748922bc4b4a0f392aa5d4dfe2f8893bd217c53412e6bd24cc8c1ace79a917de9024e0031abf428a5a3092b24a0774aa617e1b91213d28cad17924336a9311c1c8f70c4cd22b47045e65a70271c2fc5018567e3cf33e0a1359721d8e18c40e21ee26ff9bec0143b92dc90e8421a6848edc3e61396109a31149c8de9b6cb9a59429c9148c08a008af084c3f67337589ae281491c4a113605c3bf6a82ef59092be016a4d6fa2eea2b9ade91d95dc9882b25e4bcd39e7a494d22b9ed8620aad92447be89342af100ca5d3439756a644b1215ee194d4e82cba28827ae8bcdd2e855e374a81b01ef3a8972551e14b2f7a607c7bdfea04479ebdcf5f286a0a0d87e3d0d6b52dc6b8716fdf1e3711cb444cc4444c54d459ac4d34b22ca1be3979ad84d18ca68b384ab75ab5d54ae823968acd168227605e2b350ebb637aed851785aa0fb59dd25a572bf72eb10b121212121212121c1a1a1a824370e8058ac95dd241b34a57984fa983d695fbea88d2125397e88a42bba3c8a86f805ad1fba86f622b7a2345ef24f85b0f9822d215ad9d72da6058270fb2e009ede9ec8e01b62b5567ab7876cc6bb95dd41bd47d8c190d2d88fbd05f1fd237dcd2b1424dcf4cf711c1a8d117872a65b42a7d7a78027b3901d81dd073c0e6985eb39b764aa5ba55dfa0bc9c9e99ce51bad5aaad56d46379437a26077b8210fa98736beb0f756ed947c761cccb9a583507816d515f6e85cce722d5302a3caadf22e5b00d4324509604dacaac572bd579b08db3faa676ad7089e8312d9df64d0532e753e6ececfdb0671ea5dec2afa3e199e845f8d40a2ba0e4f21d63b72ec0c036fc51032fc00053bf158e24eac74222959676c7fce8d1736227a516801f3eb2784a164df49a15fc57c24724d80db01f54e85a503bfb76a99042af672043131831e39c724a0ca379950119c25a94a3c500bbfb06b6a0d1aaeb315dc33af82922ea0b30ec8e7e66eec17390455a496196115245c25e68d0a194d57ab54a67b9cc8470357d6b75e9ccf2a0333b4b764da8c354f697ebf9951a3d5a6550d78bd056662194e242d1d22fa7600f50760a7432e5173af4ef080b255ec085949bee197a43b18d3dc3321fad4cf1b5e92397cbc5a4b52ef3317e4e021bba7ba4d71e6afb07a4e6d7b1f5d9c3b311e83bdeb00e97611434ce3aa2aed333aad05626c69652ae1797392889da0d8181895dddec4526aa13d589fac9a1117e374775a27e957d865416c3c84008218410d2fcc0f13d274f0c5599c5109ea8d413d14909b3b8a8ae0f3df453d1d2facad9a7878359bca2592d51bf55d7309329455a8ef881094ac83798a87901480ec56116e7308b1d859cf839363407a762511286ebb5e40c2e3980f1c40e929ef8c28b126aad15935336520b38be554a3f072e925c192209d31e59b9a24675b435be37bbbd11ee921cea56a9189d4589d1103a94a194444329a535ac231d2924e950410eb0c856b522e4f40cd4302c070a27451f24012df5686372a2f43828e434a5b57609355cd41e9428c038e2c86b890d922061586c39651c8228d265aa5108b6608d186de286450b0b96388210c26ed8b1bb1bb61af5b56489ffbc9508829bb449bfbfb467588859ccd083aee9b1cf393d28c42c57d763467ec18520b6f9ea11261a4589553958909cc0c113b85f4f1151774708bb237e4bf15d05cce2834e40230db00de63ab816443ae6da9c3306b9942089b0c796fae6c237f00625fb0a22e9af1c01614592464a22a5b919326a52eff3d7c00b4098d59967ddaa67da574a56b0e0218986524a4b1822e8bf158d34903146116d8509841076c38eddddb08647b6654b0e1a7466b11015732618382d7b6173a4c888dbbccd14faf00d8cb3c3a0f217d92d0ccc82f17c501eb7791f0c3a791f3cc233eb7d9087c7a1625ea54c9106bd2dd5f614729885024e5ff1a012a6ef9ced733aac153684b23d1dcce21520dc97106b3c166dbbbbe1228959770f45c87ea134fa287b23db8baa905238f17032d3dde66c67d6bb2211881e2e8c59b01f54762d08faaeb3b74460548c1b514da4f743a4d5481b9822c491286ce8a0c3962d4cf0f36791b92589dc32c40c33892d425b74e0867e055a284184b444121bb43a8b1a900749d404c114cde092c39c73ce2b341d084929a534a32933830b217964860d6c88dee74068d1b890dbef0b082b804092e96e1684524a092504646bd2cd81a4e3285a901f1be407ffc2ddeef677b71b6e4435891fbd1f22adcdb102285c826c32df48600dea1791963d22f54d44ea5eadbe2378d41ecb30ea4721c7c2a92e12520b3cb3a27e1387a8f7d160de2723bd2b98237b38bced994f8693b61ab4202c4f090bf14acfb01bf50c47a266610594e3f0c6c54820e51c508bf37e384f79949340ca37ef67f394b73d138d863e02b0a5f97210802d9487d3659a9fbadd4ec9278fe252cf78675d1ca2b4d638d44b431d87881e63a3c2a3e83046473a74e94524a4e7d8dd64c298dba2ad0c16f33717e558587d7029fae79f45fd5652469de93cbd18fa40bfaa328c2ae3510ba1adccd6a1aae599238b46cb94a62c96e7d81007d7f0002619f06ebe2d84f04a951e236c69da7215a08e793f9d9400e659cf347b43c8ab427d22c070a890c28e5d05199ba119bb5b639d42075ac0947124d65585a9358629ca88c10896ca20628cd6c4a20335280d44a4aae042a36c43b767ba7d841be64f87dccef4393dc3a1f244afd3a52784031fd0cb989d08c33718d6c1f08c0f5374ffe86ffeee583fc6ecc0077ab9fa9f908be8f9f4b7f3c48e75071fb962d6c666231a659b15c518323364ee41a33793621b158a49323be73226312c4297589c5146e8b1dcbf1a1a14d8e6a391bfaf2563fc5753dd945276ea7d98d3cca8faefebb8f0d16747a17fbcc144fd366905d6f151e8fc5a1205550243e9a76bad9d2dd97559da1c7369ddd604d6c87c0db3a6438fd533d0bf392464844509299d4ca69377c9e943faa6f74ac7999302df680e63c4a4c4628c724e27a61fed8efd9945ad624af1733ea91273780406314b7a10df4c293c337d1a316b722ca8df1c9a43f387e99c6b9318e529d7686bcc8bc39aca07755f4c907ce69b6b736b52b96df3894fe752cdf17074f0c83cd37ca3b4c7467dcb9cba77facd53dd4fca69cf44c845605b5587caa28e79f4181618e50c5de52a4f869443df56749cf6a8c7a731d4dbd60e15f3a47f7ba4c2f4b6134b1c147d14c197bac4a4a48e51aa9215499c642e05201f2e3a4e6f2aa8df1ed9601293580ce6c529b72d6a2fd34f34a0abbc88355ee5a8203d30df5cd350beb17ff89722c62802cb6fce1971e46f3867db723ff6e8531eaa5339aac985fee80e75fd867f40601ee5eb2ef48bb7035de51aa773c355de87f21b5eca672a3a876aa36ade8ea3a13af828f8d3ceff867f34854239e57aa0b0d5633818d703dbc738dbbc1f9bd747a57cf86ff804f2aa1b9c639c0a2f1ccad7551d0fca0302f398d7d40e74cc6f7428c7e9da018189c13c1f9b7373ce39274cc7c3ce79733b9b73ae719b083bd4e93cec37fc637ff1f6f5e9f4a55b1df61baace3f3553a99437d72385eafc51aef2e9afa15428ca36289408421585e23a76981d76ce511d8dd1e15ce59cab5c53752fde07f42fa8eeeb71c355dd77c3f3b19b23e5bb3950aed2fca5abaeeaf653dd324b55bd189d946f8ef20de5619b77ea3e208e751fcc32a5e0a6820d4a7737d35537abd9611af3a8d6a575ebf6511e6cb350eab76a1c4ed374ca2da600f4537a5b022e5cb468b9e28a2953a0405161f1c597be2f1630794a1bae8cec403bd4a40acbe9acb3c7b33ebfe9995c20cb799ffbf60ef7dbbcc8b3be0f6117a3037d73e8db0ff8a76c6ad1e3598f1fabb0db06e4633f65de0764fb9353c7e9930e3fa45eedb8cdfb36f6930aeb1bcab554ac5eddb66e761f109f5307647b76ca33d4e35957817d3d9eeeeb3e94c3ad8b9d4d744edb3c7a86ea806c1d8fe6a76da3bf798ae3bc75aea3b183fe387dea787a7e3eba07c4e7f903ea6920fd9cf631d1d3615f5f6757a24d51a5d1d123cfb42f26e56504959fd37b19e104f2320a07e519f6866c682bf34188d337d25b01097895b6eaac1bc6183d86defeceb4677b845391a8dd903992400b809300b460d78db48c623395e2ee6ed6cc047e678486ec3025a594187876d811024101e109664c1b8b53ca19b1ee1333330b76fb535aeb6ab5cc98195ab61bb91ba646b9cd28753e8c52e5c328353e8c527be3d2a87046a9fcf0895f9f5166a4ddd8ee94bb72eb9e3015e6868e2865948d6d5aefeeeeeec6651d6cb3bb3bc2aa68a8d0a1ffd03ea6b1313bdab7c34fc90032cd53d215252424e8332fbb09fca9e3ec6308b87bd961e09bd3f1294fb1e3635b7a0bb6a5b7605b7acb9678dab2269c1a42084f9a7642631d6e292456274206030486ca19b19326671ca2719133f622edc629e1102aa7f78da0a4952841ead5aad69dde1e4fd33739bd330e8561534eef9336b5cfbab7300540076d653e86dc5102203275795e9a734ecc0df0944f425ec77bda5de03bc6e8410a02cc4dd093e945684b6b180db8a0f44277fd116be14aec50c0492187870e1d0dbb22abd6131f1898b48ed704e9530498469999e132c6365162372808d94bb6b134f793b6c1ddb1be9ab6a5321d7bc339c6751b06e429cc538c528a514a378ed6ac0e9508d9d6ad9ad49ef1018661189665aea15c877abc39a46319e51a36d531a4593dc419ebc26ddd42cc211c629f1e1f0d351c3aea1bb844eb6a846f043e499f06f8ee5878adeb9d91be71da83e075e863de777781e7381d87cba84c85ec3f229f79fcf54bc828b6a1190a84d4465320681995d13316c10ced1413816d01b54ede4f4d1023106afd10010105d3dbbb1d73acbd186f72f288b4ab7572a00722025f2045124aad939fbc2751fa6c5dd3df9e7e355dcf0fc3e6a4d8571c5615864184a46da82f6df01635c5032cb5febe9a70c1d4a4063c54eef7d5e4a8891111486d68abd85287c45831c6183fd6a7432b82e346d96677777777b7fb84a451b6a1bd4420106488146ba8500d6e11c5a9391be615223cbb74df5ae8411714fc8046941ca48cb1640c09a3d4cf1fa29054978950b003ac48d9d2860a5c261012030a5356bfa8d091787ea6b436f9b0c51736e80088334ed0ea3090c0a1bb9bbe20a91b01941c81e46a4167f54d74b95a4c943cd33d0dd1496de4c43a7619814b23dd81127d31317243dd811011af1520fd573d7221b79da82fbf2f14f0f09ffb58c1d16be8f5f1f7c5840c160c71611e6481195d7469615e4c04b685438abaad05249921a9b52dcc3b729432c68ea74f8e9159465e8c7da02d9118205a4f6276244f8c0ef4e97ae8c5fc80de936a6ac23ce9fbd1fb814994e0a0a84564a3c20852eb09bbfbfcece86f5f4d47e33ad2a503fd5ce35590f91ef81fd85e7a9fcf03d97e42a008590dc802d886d041156933f32085183df801102e2e39a061e386088a31c6486536332c8b3176370c1c6ed472066aa5a466a9e663091638609283273518b18325519c8ad4954dc418230a445dc808b2d274448c565cfa88c56da93290186162ec668a54aa00be00e30545d460a305556cd0410fe4532206e8f0248729a21c9d40098c2a3d6c8143105d6cb0450d72b0044bed618c16dc50832c3cc021888a1863b442250908299608b1c40f5a518b0da2c7d50ab6be15b3b8ae927e68a992c49325429441462b7ef1d15d004b50c9c33653c48551bf5a5732090b005b2ac30831daa872021c96064b5440250f9c095054a1a2c90a2a6db0e1c5921c52d3638c3156017cec5254d44690133182a0a18b25357ca41f638cb18ccaec2b3e025612769e9c208b264aa8a00a1fb4a2d2c72dd9d2487730f30eed7a28a5acd2576c03f372065cda28030823485f44d1925cbc94524a295df22e0e37925869a66559b6b03765f43055f4d0a51add9b72f270775344cc82f1068d62bdbb1bb320f30cb4edfe7c626c19658cdc2ea8f1e1f3f6e030ebb1bb796477c75fff81a7b1eaeef689dfc415c3026c49872de9096d2de931edf0637452df9efa2622e86623c2ea28df343f39855107ce94d6ba5a19618d04000fc6bbe1bd781f5579d5fb683c94d7488dc494791f126ca49ee2b1ea436d26368227b0d74c1c83dd01bd3683099ef0e27a5e622116da1c049fde4eac4d4cc1b2a64f10bc665af9900894ae6a0d61b532dabc78b444ebca63580801040fc1b7154227030d21f8ca7f565e0c0b20f8ca579e0c2b07c1b7f51382afbccaad10021008dd9339271324824444908808120de9d2a54b972e5d90b627b6483da4f5d0a987568ecd0fa1ebd1d03efafad67cc67372b83e75a853084f1ae47c6429f8dac933d7bec6e1ac8b272199c34e08fc2c8bdd28e8f5f4c7f8e8870e3ddf1cebf06997793cb4876e56dbbd0c6ab4fba8c3d34f6c864b03fd73005013bf4b037fbed93315baa7bf7550dbb93dc743eca737c5e5fa03fc34426b551795473a09ffc9fd231bd81d416abec93ad082e1d077bde90515c9d19a2e74a305817c4d0e56089a2a2eea078fe4e0a3ffacd79ef1c10f30080206454ad0355734620443d0b064895650911c2de842375aeb4145b885b910038418d0fa792201e83f2bf8cf139c9e8912980ebd1ff76a66f68c1ba46842861645747183d6b616a8072a4b2449e2c495176c69d5b8cbd58247d886048844086d77c7579372787a192311f9c5474f7e138b8e791f98de371d48bb80e9a5f781a50289f7fd7cf4ee960a12fcf49a508dc87fd05b3a10a01f7e7e600141471f582a44f0717a1f582d2fbd2422261060e589e84961f2387e5f528a7c939f7522a9a6a6a61f5a591317085bb8a0861db85c2dd8d34d4ddfc425001d56c08392203e30ac966fe2428095275c4216107434c4b5542069ad7f2278f9ed2b064cbc0ffc2634ac9656b6decf3a910f2411228b19d670b95ad473057dbc1ebb76e658408015279a585075b105116dd4d0d4d45ac7e16626b48e11428c4a59a59c3ca50ebf629684dc945976747b8db7b3a6b767d3e96cef5508bb838d704c4ccc019bb19374732c93a1adaf4c94bb037e09cc92def435fd519516f5e452328bba741dbbc3370775e95a577986ced010be6f563c435dfa6a9542d81dd4fbf715650dd8f4fb8a42e571308bc6603198b7af18d4f0fb8ac10cbfea899fc9cdb1bedc941ecf1e2121bbe89b23fa2f71c5b777e47a8604d13083eb7bbe10fc808da69f1e03d04f2f06e934888619be07367d43be77a8f70df9f6e83ea6e3740f6c7ac8e3f5fc3c141ae61ae47a68beb974da71bebbc3c7e9bfcd355fce07751efad1df57942e2f391f1d3bd7513f75f0352dcb9ceba0f765ecdab6fd38bd6bdd8683bab9e6271d9a2db7c39eb9dc60a7207a8d8c46a224bd741c7cc32dc983e50cda2e2fc30d683b551bb3605c283ac6184f6c457330cc7960519c3ce616e09bd8c2f60a8603760467f018ff4a8ac73019ac8bf62565e93ffabda8a0f20d24c2000bd778201a3132b2695fd51c6d01e044dd881e3660512030e44f6b677a761a1e46de3728f45c962071e58c2476288118ad0541d5aa57fd58cf3a584643cf4ec3a3b20ddf00bdedf76524f4d15bb9b4555951792b80b62a1a54f60de583f6fb8201122130183222c60d58ca38d2da4d83de4c99dba9f2e93f611a9665de97f9c933ac2eb320f72316f993633efd996346bcb53cc3c2f1d0fba637840df0911bb753153977f83edd322f8368574551a1ea553ffa301052c2e8d971ac3a551415fa7e0cdab2a6ed06d401d897124457bc92284ac5448750464f077a7b4fbc92500205e9f7c5098c1e0860ec61d42864c0c90f4db7e559af87884371f2fb8272e40ff0fb4242e95b9b33667a3ee04f9f1e5c9998024d6554dbbff628f6c7f65aaa699493bddd210f0e3ba1adeaa84291f2ebebd0a35a4aa5446d67ef21f25103da7277ecd81d7b3b24da2b1244df0ed38dc67f25380f7be465d6c61bf47dbdc08917997d80db74e1bba220ba94f1d7ab3dac9cb5757167a0f730e6a0053d816e027bf5a2fa9682faf9c4df15a633909af71acc8f68427d769f3f4b6f639650ddb7404ed1d1b3a740bdeb55fadd8db0a6678ba8305bb7db65ed42f71468968ae9fa582c9a12a447169b50c45b352cf69a086553ad82d6d414113d7b094199535f19a8847645bc0589d80476ae8d4ad40d95b00d1326cc824ca093d75e503fa8843d9554bf1a244d34cc7938cc8a465a370e01a27e955b52bf652a42b2316d4a2449211798569080962526289552a9700ca6a54ea24195439b23074b24423da3833b499ee192429b245dcc4a5aa4a567cf4941090f91899138894d60a74a4c51a1d720d5442566b1d7c4259c236a4a98d2ad2e3ab16822d30c2e5c69e188a367b700dd7068d923081de63ca9df5e6961db6582a3d905613e2b8cfbc4bab588b7566c023b8c41fdea3b0a490daa954de0f582c8f789dfb07a9f4ffcac04ab0a441cae71d61144cf4e53c2dc66dced086bc2751c718445c32308eb6ad80436817d865159417a481cd0e9f41ca408a30b1c39599a60ae35d763195b0c426f023359abf9adb085fcc1158e0a23d178b5600c1db22847070f2a911d070e29b3bb553231ab4b0a881f2a0bc2d40cea278b92aa2cd291c434059cf43113ec2ba934ea57c3f4ec3525c8246ac4aaa80546cf364026506098a8ae60188989d80812b11154c2529e8f20938e8b986806f563a2e7222276c22c864f8a8a889c1071511b1509f30f4c98c8c90fea47e936a920ad742879761e39292c96e9b1db6716e621b942920a3d8af342c7cf35cc4a6185169a2687fab158d3a361b842ad51bffa304c720bd7ab0541cffe13e42dea4866c3ee863de59c73cee96d11baa8a81f4d45513f9a861654c45bf19b496fc4261cb109ec5a52fd5849ca89da37d1a1111321b1cdc74a58c918cfce4ca2919448859d3c1b3d339192cdc1117ad1281a454f2a39f550bf6af4ec156626c519d4ad3032dbaafa50bf45cab951bf4a030db67808b338fac41f66b1c3e033e427002ba55a9f4c797618187c60108e67df18638c314a28e184134e082374c9d2fba0eb8759437ca0d40f2a2969e11b67a71b354283ad95493551e1100f7d50e808dd70401e506c547676fd0787bc5f49a4fc3a5b81a343c7131e9e9dc74e89be322c19d826b0a3aa54766e2d3a287d12f4ecdb0a070dcc93f622de92f11444f462dab7b5714c3b76838f257950d99738597a76af4e7e7836628de7163719ffc9b467516a71d097d8a608e97df86388df039bdec52c2d278b96d6e0c65c987e3f478b2e6cb33388e28adfcfc9a2856dda7b0662ba6cad250ed6c719a7cc50d841470db31a43f2c3365ff588f930d6b1bc65e6ec7a523f7db5dabc97d4d8421a6314d8867a7bce0a19cd4e5e4c4665c7ca82a8fbb125c336b1318c7760535353d357f7619f3fbbc6ed1f909a9f1e338fe0a5898d9362f41eae547b77d460139bd8cb17f5f3c7b04a23837985594d39d5f081032a2cb9c22c960ab38a31c62cde4594527a1370ce1e2f58d46ff57385c365586c237dcec520c4e04ca2588c27c7783067ff302f46078b1e0cb328a594524a29cdb21a25b428a5d71bd4cf5d86153b1c48754ef9c5164cde005dac2c6591f270b5f0a28e10970b16b4baba808ccc92f14afdea4b293bcaa0bd5851bfd41603d07eecc31ffcf0258b0a7d13d01bbda2b24399a680b632cbbd64a9317a94892a6471f232bf2f2c4753623a9f8cccda972a2a92166b542318a3d949731b5f969481eec2d6ad706840eb60366c57839d84b6329f4402a930513948f476168d37bb4ae09b9337b71337f827efc3d1dc4a8b351aa34d527e4a79ec7a88b81eeb242abd91fc4f1e9d4f5eccc9a5b73177f228758eeb388f5d2761596a2eae23434c8aa4205da13406dbb00c621bee829e6c5ec45b4f62e476a647eff35e730a9f71f0c3f796713b3dc5fb60bc4662d6675c9552bf3e624e8783fcd83f39aae3e7b41dde91e1b5cd7bf322b733bd9ee27d42e07f90761007479e1de31e001ba98acdd18d24172e941215fb0a66c910bd17283ac906896df691d0245e827809e285e9b5059417265e98883ea4008508ca10528e1841e4c3f6cbd0b2e6943d30b833a317d9891d7c384a1fce52b3038fc8232e8f57094d59f290a86655695c464ba2464182e8618d3da12c56c41c3616a374ea75e84f5696bc942ec3f23e2035ee2ea3050eff794d0ab495d96ee6c7e3776f686bfd8505172c8ebc1c8931768c5e833e3acccb8a093ebee0305f5998f1a7df57164b49af2b5dae1c791941dd5716575e5908bdb2487a5de1a1ca1afc92c38b119fee7e318205dbd699b933aa30fdd8871042086143f6cdc155945e55b4782aa83c57c1e2d779febeaa24fd0aa631c60beb3c82d7e1bfed049a2a8a7d5181e5c515b39e03807e5d0627ebdb7a3282162309ff4920f9c52fdb2f5e6adb51975dec64bb80a9453dd7528124fa9791f01a5cae02243c5bd1a9a2a0d17ae0331443bfb974ee628039fb99c7033d0be243bf679b5e73e67c6c7ecad9d73c10acbc3d7ddda2117d8342e837cf5cd36496ed449dcc37cd61a779d66ddedc0e75083ff632d7ba8f1ffa38d0845c06dce807f8e8f3f544d24fc75668e1688d3662a4e6a77419960b2f3c71441ec941cb182fa7b376977ae76c758593c2cb09a397122688cfc6a900c363ceeb3e53866d7a605310769fb98381c79c62ce8b79bb34ebf9c8e6697e1073eea887751f3bd04f9e0ff3f56fea605ecc0e3be6eccbdcb56b90dbd957385e4757fcece4cef01ff57d2961543598e9d43f199a9e6778eadab28b7a5887d3738687dc948bc6e326e59cafc3acd69dfa02fd0948d3b418ea99734d70f9e85c1a411f9d07a9694030ebf9e9d75c8bd9c15c460c85ed60288cba969d661773f27c4cf9e82787f291477ae66d59733e4ea7d3c9e391fe458f0148059ee9d1bfe99b97394e673fbff9ecbed85c643701b01594f222b085d35bd7b1a777328f87f3e81ce73d39d7bc18cd31ce8bd1bc1deab33b39c64329b64331bade375ddb3c269b5d4ce6f9907bc4e4a36b3c27d7a66f9bc69c0f4d47d3bc3df35621fa740d3e7c88aad171e2ad8726a4f4186a18756162e80c8e1a77d297797631d2e163be53159d846fc77c3be9bc73e74e0cfa4eb8581e76ec586746853e436f326ed2650f550f2a744d723e16cbc4582858f31cbdca2c1220d0847d036d556cd4a86aa3f211bfaf170a9e83f87d41d1e559bf2f2888747c6a60da0f042578fd3a54b551fb89dfd7145e3c9cf2fb9222c9d3fcbe905e90fb71aa81d2f7b8f04c49be8708ea3f1f2fb65805f19f0f51109a17fff9e4e085fc2f862c9238400c92fe8bc128e997ba5cdc36fd3af7a0f5c097d2cac307bc6cb0e41076313bd2ca479756fe8b01e8a3cb9e6d6a6a924b0fab1c7df7dd6bbc7137c2cb4e5a79c8fd100200e8f9f0b377c7c31e5d934d1fa303bdfd07f4f67cb4a742cbfbf04bef631702bf1de8bbefae7bf6d1be0d260fbd6f874dc4a9da52f7b5040f4f7f609e39edbecc794e4e5dfae6ed1aaa8b41a162744e3fbd9ed40b61ae8b4139fc1475d6892ed76b1ebf5f73ee9899fb8172ae4bf9d6a17c76bd715bb73e78b96dddfee0b98e5fc3e244f5c0a64f391bcfad841c51313b0b45ff072465b1fcecb89b9eea38b93cecb409eacbfd804e29655f1f103ab30e3b750dd569dd9661c66bbda1bae5a239aa8b017b21fc1c08025f7339c36bae4d4e6546d57c39550faae63e168b6c082343380780d27c8a8b86477535af6527678df3dd1c19e7dbf4fe936fa72d3bb9d605d9386efa76f2ec4473f2325fa7ddc2a992deb7b7621673109301c76d1bc7711cc7ad0cc786a69d4e9aa6699a764696519a650e3b55962ccb32a7bb23ab9007da3233c6cc9159d8e6c01c76aa2caa2c357ab63b7ab0633b96b45555a91fc552b37b7677cf6d73604ea90a4bdd97942e2f1d9b8e3111da8f7d196dbb2df22b66ddf0bb3952dbe64839772a2c15e59c6faeb90a4b3d79a6c292f26d77b0a79cdb1dfb29e754175a4a07730cebcc9da977e6d467379d9910f8edededf9538f873a96c1ac697753c7e99e1fa79c3165ca0ed9b78b69878ef3bb341fe610c37a603488cf06830b4f9dee4e71b9a8d3eef382f42c93cbf53e2f7ff26e9fe55480e1b3203d0d78de1ced3e93667307039ff9083d0d78b839a8b74bd38ec3d2fb0372c80e7da38751d1a46bb2a7ca31d7e00fb6bbeb58cfcfc39e7ee8a9a514767b8cd19e7eea09a90f24ba66f88fbd075b6386a61742bf8c277ad7ed63f67a8208c13e06a0875e8f9dceb307aef1311e752784277fd3bdafe7e723c47e9e3a77cccc3d3f0e6300fa756ef8ededf5fcbc0998a58136349516955910ee4b0a1408a167a06ab1dbee80bfed8e20f0886b6efbd4ee08025ddfd300577505a338a8240e1655392a15cd88000000005314002020140a878402a158341e52855dfb14800b8b9e44745698c9b32489611442c618430801040000008088cc8cc64100e84a32aaa98103b553366244dfb51e7ef1e67970766edebd30313ed43fa9fe8b7e96998759ea9a1e1db82bfcce61e5e2f3ace0e05753781e71a774045d99db22068412b4d29664c394c0b2b5e7517e6518d747f3754dc2a584585e9504056abeb17a5df4da963e364691bdfaeab9198d3b3e0c1c0d3b93b5adb5d1b3205b73a3cdd9cec23e62f940ae9676ab424b2007e4b172428cca0a1937516ba765fab19fe9592d29f4cadf8d92d3b537ab6a7d5af520e9e00b6f2a2c55eb9f997ed72573008295eb7ef22a41472e39421883223f6ba4ac792438706c494433e46882b6ef28e128d483246639f88921febab60ce586f97d7b2e8bf880937511bca7de728ef913928f4a958319395600dddb9e0cc92b0b43785671a0d550ffbdc6cfcdce48dbf4c0550c227f2914ea6d74fb29fe3f0890e43b67bafc0a416e8d71ae27b6a6faa2ca77e4f9b1efd60842daa0e51c3bb10a159ddd24e8221d3041473d7d6f3aa604b771d5e9c6bc35b6e56113da961989c2ccfcd98d3ac3091896b10f1d7b05575cef3728520511ea01f4662a266ee47457251b3b434aa426396315f22046cc17645081e3afc761d088a9be63109f9008cb9168923d536797e71882310d942149f0b9a6f421645effd689dc968086652f4160a984207d4de502dc81abdd3042edaa2c1b2902f1e45717c950e445e3d006834fb8933e597b0a6d75697308f104a9e1a2658900943cf16f5897476dbb6fcd5c630e413e6181fb0996559f6a7bc6d402c81d2c623862d8e85d45a470f155b6acacee4dd0925aa0dd2d3db9a0be34cd12a73826bf2e1bbd8ad166a4fe5bdbc00cd33089db341a6cda6c939946525186064e94497473be053353b8dc4294a19e417a765191bcc689ba2be9d7f95f8b7292e5fd7fe5023348e988a49998389c33a98a184408b6a3c98409a45496f62f8c0bdb5072e177ef84d896c97dc44784e852b38ec4578cca56b3a2c7527b269dbcdc1883120858ae2254d6a3abad43ee3bf707502da508ac18db6e9b00f1b4e63c10a6912c41123d4f67b586ef4d5cda1807a0117c5c7feca5c3de4bcc93569d1386908bb34d96b855003ae82000943f708ea7c177ba371df99787c28af8760b0cc9f06e15a49ed9471fb965e4e3298c0bb2b43a75e1d1d280f96314bbd31b1c2c6b90a47372aba592d01f65795768a02f6b45bd2feced2d24bc10c38b8e02a1cb2c6010d5aa2149c31a3bd2f4f9843cc0a15580d2d7669638fd059a2d051c6347a146200865f69a0ba0c0035128216380cd8447868b31e0874cd2189343fd53eb8d32b2d008f01ba632693c7f04d912343f884771d12ba76b0556ddb77ac2a2868d9fa4d5fb03788625a1ba18c7a078684635dee4163f2bd737d5dda06990c6017b48edb139481b738f7cec05322a39681c8a6316c9407517e42cd8650227a96fa286c9ff7421708ff9c2974fefda63121ad12052bb1db4e0be07b76907c6846ccb2c729b984c68e6c4e1a53d0a77a66252c923710c571d80941ae39f57c7367846385b7b878122b280ddf7939899b38ee848fd4d9154206ca6b684a593736a512c5aee8ccbf7a44e7212a82b1071dcf2c6ddfff100b572ea6bc21b241b31c49b2742b582badde494a8b09495c5fff73ef2308ea8a15224167f060d84a07dcf4e8f67c73a61c4d83395665d4df8c4ee7f6f71a6a267c9537a2625850de4ff5e827c6a525f7609d04a053dd60c3211165b13e9f7193beae530a0f0ea6f3a5c8550072ac7a822406740bc95db8b9548c2cfdbff6718d6b67913ecb364217363653e3e89d33dd450152b601f3c6d74e8a29f2bc88f6f786e7da9c6aa9b5ffc0f84d6957fd814bb1008cb2504c2c7b5696a43e1877d4b0097c9811b7c1b0bfe968808ac401105ba40ddd6cd183de2dcae957a62887cf71b391a4506c82a47b8ecc0a76b92d413697f6dfaf1cfb1ee4044442a202d58ec0b2f8b257d9cec387dec74c4791c405832f009346b6edd7a74a2b66eb3cda418f90b1fd55925213691922316fe489253262cf0093122dcf5fb916a68c8cfbdaec98a5dfe74ab9a2277b4e38cb55ad255c2894554aa8e2ecdafb67cb93b6f206862fa03fa4f6d9c9ccdf3ed999377d750a863b022532807c7b65afc82065efbee77e037a13fecb1170dfe989a1151a9d38292ec03d67656ccee3686785a920826930c98d4e642f4cadea3371fdd1b538213f89fbdbcc94d56dddf615d6cef72b651697a50bac3c52b9e2af585c776c849f54a77106bd244b8039bc42f2a49eb0faf7b4199637c4209005e86040010134217616e2e29a6dd5cbda4545cb0284df183656028552e9450f7502a80fc421eb870a1e96a2ec1fe9d449b369a2358a56069042e7736d5c9da03151d18125a33602bfe148fef1f5d593deb9e81655f2229431844b07b358889ad7b14e63d63e1c846767a3e529cdf264830a0bb623a993196fe3098053a2bee5d59c13646080bebac285ac8b0e329ea3613beeba6d7152be31a8ec8404fd4cd64cbb4437c496e7afc6a95f5e963295dadf1bf510e65a8e59e8a7a00b748613a93a71210a7d4c3066925f86945e20b74e5191f5f281b531c7a1504526a7446fe501fa1efd17aab89c02d6fc62152b7a84eba91f61b8ee9447e20fa06382c6f41d08b8df9776c2283a13348171f01283db2cbf1856779dc118ac34b3d190c735010aded84e96475ac4c420e0d6571ab0abec9ce733c37f48d9e1e95e25521043dc00aa18d838c23234645b18f2c28740bc92016ca47476be466a62a4328d70baa00866469ffbb60761108724944e4859091479d938088c8f8ab8dcbe9d4aa7797553be93a367c51c1f6ddf8ca2a6c9a8cfb98f1e813329b7b405c95b1aa45d2ba1e9b4e97c041582c48c73b16eadce47d422758a830095082ae124b410866d4fa9f66ccc747dbfc3e78682afb561c59845faf18c9cdf2f7d94961557a26e8afbb5b5eaef7681943d2483afa19cc60ddb74992c9076612dd3ad686a8f68ef1619df56f8adcde239ff9df512c993df6ae86ac3cdabdeb07cfeb5c23d773d198255ff24a219883cdb7b422d6fc94e866d61bbf3f5fe2ea1fb38e71ac8b058266b42f998c6dca58a8a74ccdcfc84030cca99e961a6b9d72d0ec30746c9ed96acde0cd121a318892e33cb914475b533b045578808a53b3c4d0f5590d8f324d08a6184a8b5601cce30515e66a6690fd7d6d3c3c5de4a10d35fd1e3ca4d0809426c120e65468315b31a29dc148e56535049e5b29862d8b0cff1fe6f16f459ac145e684bef3c1210b2c786f7592e0a5a753b8294a8d2748683973131f527a63514f205cf3e254c169d76f7351e1b6b8c03238317c817c6935296bee7dce0881d8a86a0cd478c5554dab5d83c1bc78c56ede16b2b737f4b99195193e8e5717f85cb54327423c811db07da6d84304d65442f323136f1a8e1d2d448c369c6d9a3fd0c40d655e9c15699e1728bb96d48378dc112749627744a18a0992f9bcabf14ad6891adbe148f201e97fb7ba1fc3e25218ae64632d71e2307de24c66638216afe765b8a3118e9391884241500a565544b2b8d8454c60244c825ecf052bca8b85b4541ec6e27b2382a3c1b15989f7e10d0dab9ccd881085099ee6f1f548f8f3c87de6a8e7496d88666195fb920692679add3760d9cad5c64817bb1e5d61e9a953e750d8336b5d3980357bf8e4f02f257568111528402515fc0dcb46cacf9f26509d3de374b97a28e1e6db1c6c69664ecf7572c68e95ea292d0824be983c48c7971ce4be7414f5a3a589df8f2f19eccb2a8e2bbd4e52a1f1d43905ef0280a060ee56e8037c3417d375a08c1a6491203ce78e0e1172a003167cc97de4ce5f13069a7f70b33be724dc6c4c10c70e08c26d2050e8e6a696f6d28714b47e921bc0df085cd807f233274e9913b7f221d7f3150b934d37597024832a1bb160b2fd6e1f3e26220e3986aa0596f1cb9d849e27d954d999e8631d61fed118620256191378c9d58e538ab6331ffac70c27d2df1f0d2cb5e9f3bc36a5b04479052ccd7f71bf159959efc3bc805edf14ac8a3545d86cc5ce5443e8cf3d68203fce9885ccc1cef0043d771d9be3233b14332bc88198d0c43d3678eee2aa5b1ffb0ca02087d2d3b8fdd831e6265b19dac1ebe28f540ffb22309ed91187b8abb95613aee45e30aa91bea11ea6174ac0e4c307dc020f5fa14e110eec0b1e915f2e10714e172451cf3d24887db21fcb3af9669099f23bb9cb2ea589fc391006ef0c0cb6f5648230f3728144da018d02c7de52da8c0b7872c7629a1427cd4f25cbd33377bc6f3bc6caafe91072e2d584293390cbc15685aa5e8476414589dbead67dfd34807ff43d7ca2d71a9cf3eba3188672bf1e31abf5490ec231dfae22c3749d0f023620f490c90d33a0818f2f96253797ee62c2e33c0efc9c8862030c608b4ad9f3934a792b150b4f79caa09257f88613fb9b85eb78149a33e016316975fd6dba1f3254df7853568bbad1c016ac599db8ed7648ed7558bdbcccec1d7171deaba578c201a5c28e10dcbe09c8bea096c63bffee0d7c491c9ee0e3aa32144632ceae91e7cc099bacb5813f11df6ab97032afea1b9ec030d04c60e4c69155c781e1c957c68d510740ec9131f1fc4346ef8ac4bef0799bfec202e5b3abed3b0bf96557334268e658a0e06a69864d6143a378477217a80a3d54b0d0dc952c773563670ad87d41bed7cc74cbec9d6a015693818433844eb0f900d4abe7b0ce38573c3a3eee5270ada3365600aa2515de6b8123efb3c49fdd0f91af6bcd87a40f4b03002d59e937d93fe0bb84cc1faadb137ade0adb1832046a29b57f4ecf50c3308e2520786d3e45c987dd3f961077051dc23e0cf179bd1ad604a811c30fc01ebc6f1a77f0e05423971c1fccf82fe74eb7c6359a3c14e046cb87ab394b354bca8db2de64d382ea0acd64078633d98e3a0d78358100626711a554750423f42c63f0f2c8192ee1d4522f8428eb95ec4b2456f3ec7c501747cfa4fd8ecc61266763ba3854721475f47e9817cc19a9fdafa4fe7298a03d484b25ac17f0b558bada259f23d8e2ca8bf34057bf5d7d332a8dd33059475950ea0b7e0760e8ff4febd9dee487ea8cbb0171c26fe1c3b37668984bba9a890508feb2828cabe370c2bc6d0c5bbe4578c62fade498118cac7a91c8367bf7cdc65fd714267e19c6c62d3e10062c8c254f8cf42bb729519e943fc894e3d499ff7a373f134dd4f88f314d10a1cb3d371aca34ec894ee984b1d7705f7a61b57b480b7ca0bb4bda2bc8035b62fccd755fcb9eafbcaafef262ce4b504ae7c43c119ee1e24f4c1b84c2a0853624134d503f4b4cefd4f0a0e716401aab50f4c9e05381c848718b1fcccc516ee684216cbfc293653d9eb17ce053e633f55e81f94d79ec4f6608607cdfd6f46d2a3b28a7325f2e4255a5cdf11c77eddc6e94abc9988aa45e89fae0de9325f013398e6eb74fd60207ae14ac96d2264b40f2269100e52a13f87cf91ebd19ca0fb5dbf7123f09b004292dbf182a563e2bf6d9eb11087008f787db8fd35cb9b60d63cc98d5c31982a7e4b4c1cded99eba0fde05a05094ba99320f3e9ffb7a03950882dd82820f7438bbbb079bdb1d149e0bc284f8374df8f56fed35c02cfeccde9925447cf1c5a88744f708531e4a90a72bce7f1c0f7c06e4eeece09008b7049a4daab46e25f50aa84fe007aafa75019ef2e4a289ac5a42ff6120ce96dc365c2987ae85447a2118da7ce7dcaa2e00f6e244c7b0ab7b12f17649d674dc4244b45ef930e0381dde337d2932e02a21a90dfe03a890be684bdd50dd8f435f48320c0b990a5861364b3ff218e16443bfb523fb6120d3278dc4ad9175815103efcf4839b0b323972b0b7f82a2a0304d3a7ffad6c37f04aaa7241c112fd0fa9da03e95b050aa266ba9512854dc015c0cc53f876ba7dbd005ad5ab04350c20ce57a673516ec8107ee3d86ec1fe58d812f933007e6d37a52cc0ad225ecb0d88d3cc1bdef4e16b5a70dffc9da5626b9aee728ec35f6dc5c3d684ae5a685207757e23f11f9a643847fd6cc9851293bfb5b0ea4d2472caf7e1ba65fdd301f67faa9487f99cfc18ec00e5a6ca6bcd7a6749166c5d086f848c32572644338ee0b2f8aec371b7c2ee2f538f04364c02e5b8e16b35e4b9bc77a8dbd68a0c02b35822c41734cf6c8c93c53dac2aad54b760c7bf63c6a83f5a7fd4b6ba04cb1567b88571d19ba4de08615286cd4402685f06234248bc8ad0d48001b3a7e19af70a6c5e3cd33b1b97d1062c1e1299a0033253959f2dea34aabec1eeb10c706afc05913e79ccbdf62d5215f5bd564ec010d97cf989c24993e2a97ddc66a3a50158c88d58f76c1359050244e91b7d08ba77487a40ea2fcc5e0f0cfcb29543bb95924352a059ad5b02a407d4be8b204d1273798d057b09f753df10951e2199678d2d659e36dc733c6084dcf8158bf1e71fc2b18fe86138021ada15a0a65ad37e2c57570b9f08a80b1bcbe58e373553bee94fff44a8af9f7ad860e2a825c847f7530fb7b2e7ec2bdf642b0596263ff0e52da967070759583ace26e6e5a7c36a182ee356fe7e25f0ba2ca90731224f3f5762118cad2fad8a56f8a459fd3a71dffb419082a188ef0821594294025493fc44c9088d77f8be9e0e931f69ddea1e87a5e3c2435e4304e754338f37ba87fb6af4bc4c346f5c5ce26e798fcf3c1b023e8e52be11c8aaa18e74d1b79aa215feb33559251fadcb910559fd10875f7151f8875dea4ed6a3fd723b07f0e9abea6411e61482f8317000b0ae5fbb78e2fc8cff9c0dd56c00ec3b087d70537cd6d502ec003531bdb17c7f28e5b71584edc2be6183bc3aa08d936999412aa2f755114d2e7fc6a698c6e59e9f150bedf24bdc4126f80078491cda1727e399331cf5a2412fdb5fd970e8ccc3a2c309c959f1115613cfd0d0a5aff00ffff33d5d18e720e98a68b597602d24b2feb4a71c78869795a524a3f65def70b6c2708fa571a0220c2177ac564564543267f37af202e7a10a98283686e44e05264a12648f937afb3e60794c8dcec37e299d6008af65465aac504ee68ada743b91da955c6c00a42b03e88ceeafd548a754d95b5977f0d47c1bc14cca4940936594767b75325eb285b48e8b2062d028ea2f1ceb70595a6896264f9c29a6bd758534ee631e6fbd658e4f3f2aaa0b7a99dc546cfbca58e952d1a46f1385d8807ea04a4ef8690aaec5a2e2dc3b42395f193106c0b03c7760e352a9ba85cd0ff902f963859d41814b44718e70d52c15233c492cda5f19ca8eab51f309625d31c07c27479692f05b46f897bfa5bc2275e3ca464ee2b704f979e97066c08f495ca7205f7d7245f0594098c67fec512d20a2e12f08ea4e5766871e8bb9015399fdc7292ea4910daf9aa5b7fed99441bcdee9365c52cd395a0801a8cefae81c67603cb7dbf25b08723d2309c6f1ff50803f169208324d4c2dc9fab1df4481df9882d979368e71a02e7d591322bc905b7288b2b1fca2e70b42cc1351fcbe131b7c6e566759af42309c74051cbd2097d3cb9de5a25ad580114791a01ae480e7c75365fde43b7ffb5a7d230a0fe6f30b966bc327bee9a0d7a55c94ee7e54d6c5928385ee9787f910ddfbc1cf08e8ad54b6dac3a58408ed923b542a338015b46f4e54808a82ea127ccc1a883a8d209d8ddc544d19d366277ae249d20eac6499fffa589168e6edf7e2310f482676ef9d5e41e4702a25b7c0086f60b5e627bd5a9149f57ddc9f475254ad408f67db0cbe8634fd080a57fc23ef055bba50db972792546e638b971a04246a3315c3774e2d8880b0e5a3e32a66786f64b567ee2e7fd3b1d008c5b89844460c468153b8fc56ab53048191a6aab8e30c162b28123274d2ed89e9a853a9164403e6265330db701a3a6e92f0060babee7aee8a4ec0683ccdeb0bdba9c4749a042d435de84ef4d71d3194c647c64c72cc9fef2949165c087721fc5367e89f07fefe38deec6b7194c3abacf793619b344ee92238c4a1bafebd188fb3acdc9df1d9642f24791fc10cbbb7246ff4fd28c46b586a1203e07ae9ad747967ec455baa0fd4e2dcd561ea76a1128f3ff575380a9aa1e6f757feb58b8e0fcd387d00f0b3c3b56beb1473f6737414c27c2cf6b8fb326119a5db381d0b730ec570bf2a514fd2fa3588236391166452236da5fa54a244773ef56a040ecb32431831b29e8327fb274126c72a97e9616f216305a507e26557f42fe1b8457961495b8100f45d40775779015ead4bc36512eef7b42e81528405378f30906bdd85a6c311569e6f32a96f6384c9ef297f1881cec15fe27f66d348bec919a0302944fd78af6295fe58f12129bae11891230a4afe01e7d5b75be621a2a00915394877b65b8f8934b2f49b8d7d05c1f4a693f8a53a1e8f6cfea45c81c796dd1ad6acfc4a8179a8505a31dafbdd8d4d9690f4494610af3e69bb3ef765cb9542cd290ae0229d0bdce7f703dc546c2661a3c9218d7040c6a159b6523eefb0a80b8fc16075b4325f84bfd8ce41219c5602849c700b722a865d3ee4b87f2d784ba43819d856dd1b3e41309ab0adb574453ec8fbfc7afe471b75b1324f600c1f54929fe4248d3ff25eb7894aa8542f09fdbf6d56a46bfa905bac5c3102f6a7575a31dd98d9f027a1c12f02e26ce76a6e0ae9341d4572231e3444e5b84e74b95b43af4657ffbdb6da43134980b309044220257c18f61b13497f5c107a07b56f275c46d618226259bb5d50d03154b44cea8d14f8979b0d33e512d4390d4414169bfdcdc75751006e6bec5ff3dd7818b3e47c8593117df83cbb73d9deb970280f4bd19940a417550cb631d17e3b8298a401165e64dfdc418300b8861e4c504712ef6ef24fa7ebb3b3513771f226eb4bf82bd371addf1be8e8094f94e1e93532242c9badca9b9b497cdba93a0581a3268a22318fc912731e765a64171de6b2be6acba8f9b0dc431089f6e18f9f0421098ba64febd2400a40add11728dcf003f3a7cc0013b942cb58ba10521294324762d2659b8247a33d680c261db398ba2fb8caa3828c2c59eedff94a7059679ff8ae28906cce642c5754c028bea041166f202d36090d59c42a16be6ba72439434a78c8384c4a5fa94da144843baefe4f9993830d737084a373463c4513285c92cde0039ce26389443a77fdb59755893bd6266bf2e0b928c0f697703bd1f47a2969469725612169313fdc081025418720201eb72a5ed4291bd9586ced4f950844b8087646019f6774c5d97862069e65243cf37f5476550fbd6cd263fdebc4e268d9b0673a42c5d97f96ac910e19871c44d725049e465bdb78b0092aa4a010c4bd99a1731f00f0108c4c5168c4a7913809828bda95c5c62956a368782e4166fe75a0cfbf18ae63268cb1f9012ef3f62b345af47a064df6545f917202b597b1b53f5c9a7ef8b330dc6fe6aadc8c4937566ea6b7fc7aaa86b106bc175f7ece8764fc0032193943f8ad9c840b0c9419526a80261688c1dd7a1b93f39b7b250f9c3810c27a964c9cd081e3bed16089359bec1a5583c5f2f61c9b026cd599ed8021cd2730320da8e97db4a75f61641bd3fce7bfad13fdc0a1e7175a6761397660440a6dfda4dee21fdd67593585f6e86918a9dd555ddc98069b6758549eae24311493375931892d07e8ab05f429cbd7b2c5ea4051af20b32fcd6642805b5cc9b318431b9d447c7af32b2c131f442a3b69affe3d3a651a7449af8ede6b0e60616d7eb2c31913d9cee7f22192149360ac77c0bbc8c6abb0ee96ef8cbaaac06befd9c57daf5061ee04111a78ab1d36d525046a3805100e51ab1429528a6b95ade04929b4219cd1d5d51e5d5c6a6e025444730c55c6664ca24ea08139eb667942b506c4e6cdb6003c2bd9779443a69bf1d6a5dde0b3e16cf6d8594859ec1930ee1aea5df535f38d4c6fca0029008e5212bb2d5f0e893c3a6511cd91172b2ea1faa07858e96a0f3bcb167e7fc3bc7b2f4b89944b2818c674a3e8eac176039cea1d0239ea4af7b9578844149df8c66898539e46bb12d98aefce63c39301e31a3ccc985e346d4baaa5d06553801266156bf45f2c5bbc970286a0c7df453611a359eaeac1926abd1f8c416ec08c63a4402b06dd5150ef5006d3634c912b7841ea92e0d6eeea03a0f4da5cc7c2a504ab8bb43ad942b7f6d147dba47165418a2353859073d6a2de9ca997f34edfae92358b644abe1d3775a0cdf47495c64aa113be9f6872ebc9211da3170282123441228a17519e6cd878a6f082dc0216fe42183287d6291e1f95ca6ec34a694f5693d5ab2d8b5549c6106c153f5736407ff5c64c39eb6f74ec5558f67b692b75893ccd6c5d25376d55e34095a7e166a75428f85338668acaf6fd73d85850d3a696e832f6da94436470efee2c3cb7ac14b97dc0abca12eb6e2182a889120ebcb03e1a24645125af601a4f7f50621a2a5fae08b02c4a008f1f82a4d8ec95fc23afc0e0b16c2f789094f0bd34ff960c0c722007dcfcc3ec7204e47ab2a689d620e3a06801ec993a8e5a8ab80d66e9abaf50dbc119d760915310ff7261c6112b3723e02d4b8e19fef229f03543ef057841f13cc46e50dc4ccf0bfdeb6bc9066593e59da9b0e532d7381d32946df200945c96686ad113cb92a4c5a3ba0e2a67792599e2e9e54e83e4d57210a8508dbdd007f874b2a4064310b5d38b3e625c93fdf20b56f7d6b514338439f6a59396f499af4c0384b41a19858812b9cdae03b744afc485b33d690998d14e6767a299c8619be31ee8139413d716a17887c5b5a217888b201aa4fdb9148eaed5f1f9949c2fb45185eca89e54efaff1035f038134390ed5e7a1f50ad2aa4beb21f8882b25f8be735c29fe73c3196e223ea684138c86b724f685827c3f3dd1b6e836a7481bee9b8038ce3d80099042f8a2254743678d2cb1d604728c372dd8beb5e44a4282b98cf44475d83dd04a9652036ac0da6656c8ab069eb74f9c9ba24ffd4773df6efd00bee7a29307700f0a4e8e257c75b095a5d823e3a33cad64c64fdf2fc6b4988cd6746f5c7d31b24ffdca4aa1d303e33a24236c345d50cda72aee7764fbf0ef8582f662e3e301c64269051b7388f8c5332637062a2bce4e06a1bb3bc911458bbb30961c279b4710264d7cdbc8a5deecc58f0311d6d67a3c7857253ebf245eacb75223ed3bd90bfef05bcb15f468afec1181476093f084b9f1cf057bdbd704bf5904807a89df121de9b14f6d568dfb822caff3df1b041f92f58c78b9c10242181a1c72eba9a2f3900c36ff8c078af84b81fcc331cc1238b4ed6253c6fc0b591ddb3d5f70b0ce02450521d1ddb0a5695a8bd33074a19e5b7585a16622fd600016514e12c200705d720149d30eb73f06a0fa9cfbf9433bfcc784bda04574a3e1af65006ccf82236141f70841e7df2d56a7ffc3bf410e986c5ca01bd31dce9cd823d6bbb5e12001b7938c7b9d6debd2bc4d11a1949e68c13e1ae37a71680529f13f642eeec084b0b4149b58a661aba9b9843a97715fd17575ad744f522b6bacb2d726437f0e2785fd8b2db11e7f2ed3383e61a0f2de68c6c56eea5d66151f14f8481972ce2c0b4f65550e49160251726075516d8916ac51016d1995009db07b00243f35daccbfc9a37dc38b4cc0bb970f5fadb10a09d4ef1200fabb60eb5bed97ef1c8a3b994941e36bdbc8e51799385935fa804efb7c35474ec7292f76268fc314f7e997e63c612ad9575d16fd02050228fd2598ee48c4cc46bcf85d867da29f84b9fde48b4e71329addf9292e76ea87a6cef0ef4cf9fa31cbfb80f06b838caaa84e959fa215b8261d71b5ee516b34fd38cc183d1af50501d66397afd1c353895ae89f92b64886d539658616e5f2516f00a3352dadc23244c3b36f5eb4197a98c2a5d4ff9b1adfc89b3f1cd7c64ae2747f81b3c5efc19d275af7b04da3d9176254d16fdfde1643349b91062134b56fff24b88fd9d21a1837edb9380d767f02481414e4245ae317f96cd5ffd08fbdd5bebc3442eada0057c6e6c5389fd20cf1fd11a029737e7778f18fe82671831eaed2fb65791ce69215dcbd788c72bb5cb78d9b8cc2530eba8bd7e2f49da57c000de463e61a0368a8cbe87f5718e23b0bb010d89884e400cef8232b751c2b90d4532d77a1c744fba931393ff73d04eca99cb6b217a8bdc3c7bd5a9733ae2762ef9237ddf8caba16a22ed23c9f207b8158511865a9b38b6b7400f1b87cd05fc7066c120a0c9ef247a10e107fe70ff830ff6539a8c007f7a4d4fc5c31e12395f3583db79cac676f2674791efb38559ae5233b1cf3cfe99ddd78306de9b312a02087636fd079543120c342429099eb1f734318f04bbbd449dcfbb2501021e95375f9214c33d59ba9f62b4a545b9d2d2392872a4c17d3322e27d8054c743cbb1e835742e1fd6f5536ce1304a184e449032e1419717d60764fb921572e62391c94530d722e5d9c4e763677b91cac152455b7b9ff24356befaee274fe5fc077d848f8ce1a98ee9e001805cf15dfbe92ea7118d4d33fa1a830d4595970d4214505e3eaab723ec2ce7a9c9947219465aa8065df2322ca0df2c8055a2dd536accd589b56a80fd61e7ff17dd46017656e61ee49fe3b5a5b9f9492205084a8f3f9b9897768dbdbf91a5a7a0c53f5fd5b29407bf0d3b9da7af0deeaee6d35a8eee6227871513f59610529d2c798965d90e334c7dac8edfab2381ec37ca0f490901c0a3e4eb8cc42803e4ddc3e7c6e5d211a73011fbb0068a9c6eb4ccafd3a8888bfa2c797c2a51cd776d2e7f464a8da628b054b92021782f451dfd20b4e0b3b6db05e5c46dde74cb30abd80c94bb430a4a2b3fd9343314c95291af9da77fe8b58db83b5acb169929ea414623902009286e15a7375e6db9824de69c3015e181e6a8759c0c448475727431cd8300f7f07ec730d7691284e94fda3524e6049fe91c0b8ba72bffd389ce0f48d4945b189cbd37fe656c75b9aaba25bce3bbdd7c3d3a84cf7ad036bc2b303f1b3bc1b38bd63ce892124a38bb06c72d971548c077262ccf36f500e39edfe21c348ed669f9dda4095d26a22f8dd36ba2f9f17dca958c52dcfb3bf6cb50caec643bcaab7b6ba5768c811179a2fea9f8bba1a982ed97b816e711ead67d728741b35b8bcce3b97713321167a93befb8b78f2c7be5c466337ed17b5d2a5c43d3fe5e451e3caad5f608a4f5c39d428cbd6ec4774dbc33f04391ec40002453eeb360ccbbd5127a89f215576606e363545e0051b1424f0672542837745edc4fcfdeaaac37f9ee275f255ba838a5aeedf377f153563fe7a4c79f14bead33b3b3239819cf0484896d47a5e1706d9eb6e707922ad38f59fc655ab506e5652b347e5db224a1f98b43ff7b150ded829616135862a9fdcf705665cc5f9105a090963d9a107700c807c42fb4980927b8386838e4465f10ad6cef943c4ad1b90066212d002aa952d23f6a4a49b8a34ac2562f09bc01cd59a666828cc801e68864dc42b92b093127568b6d64fba285bbc8dc413267b78aa83654daa8cf3507380886a0ed290b365d11bb9490505dc8127b0dabc26e2e283056c5f65bc733b3e02328387f9e8e8ee8b90d33c5394ffa9dd90c6687a29dc55d950ee8cebd95165d5132f3e5a9d948d3e58e046b99df408c0e999efc4f261f2848bedd4be391b96a0b791a9843a3b99ca228abba3c1d163b2c4384b519c8251a12e4ba85c7cb62918e44e64d6fa3b17489f67a2eee2bb241e60426adea5fb8b3bcd44d9558cf169d53d190b3f7b5e22b00582551260a304cfaab732999bac52b5452df427d01785087aac6ebcbb9c7fb87e4c9464bb0829893e6fc8f8b32c05b72a72031ac72d5ade803620a0773629dfad9f6bd1d5272161dbbe8231e27f20327fdeb2c038459e803fe0babc141bb8738737a4ca717a8ab5be8f28ce5022d8e7f8cc6f119d61a75dbf0cb0d701107fa35e16bd3aaa5328241a250c977f610cc3a33c719998c8423ba59803e653c1a80700b0859248090107b1de1008e698d37737df1b0bd6e2d5fa228c245c6128d38e7613d388c7b40478e93aa497fe57dad02a144d6568cf7600193700badb8d3985edb0aea22ca40adb109e89c03caa2be8c3b56642f4471bb3d6b444fb407ffb82817bd9cc37e859613234acdcd71cee8a12f1d8f85e72780c27e64b2bf017bb908b12711518a869e3a58cbfd77bcc30c38700ece387d647fc2ad6c44ab88712ef4ba34afcd9a79e2e4e8e1e7b461d9865ca2a40ddda4b9ad340d6075ca82c1d5b58ef7610a1c644349d0f78941e3b5549ea400e5ca43bc587dcfd108d4d97f2ae4da386d0247134f8573c8ae631ac3fb1c1e0e444ace36a202838fa7afdef3d8a90bee85017364fc1d4c64e3a5a83e73333cd7b348e02059b4f57770fd4609c08db70ed7f27ef6e377470e7b5825c05a94befd8a6cf8bc14b340b42937cb7b431c13ab590a16dce31e16f4b1282509bd2eebb5514a0c4f8af00ac2e65f8709323b9ecc889bfb6a4014108c8a5244054f9a40a032b409f0e1edb4a54d18e5c16a74f4073e576421a05029a9523ba41cc90c6f92b486efc29735fde8a50056763a66e8a9e994b03992e4d3e824aa368a538d1cf58934a184253122a60cdd1afc101a8a432a642770b0efd5c60133c19a88f0a8570fb0cd8516a90516b091e4837d5c45896ece703a0cdb3f7b51e420ed7fa15abcb887e192c957dcc87ebd0d1281689df7d9438930e851aa2a2472c6e03f388082df1e0c15364235ac699470822c1d99c4dbc11ed74a25c950eca2a916c32e860f5d602ac668867831ecf35bd97050f980c9f422e908a2dd84cc1779fc834be665094a1f182ce1468837b02913334d7b09aa8efa44a3943c72260680bd4646533f436628262b800bc1a0b6060bd63a7424921bedfdaba3b0b3da12e094e15d75744df07827de2d84ee50065f925162c03218fae078bae378b5d2f8309f5fff6b52205621aca6ea69766045b19b6496d5b2aa91c7cd2ff5405e7da8b84cd0e59ed97a572409c7b06b4fc6b3c242607988c79354a178fd620f5a68b5846c2e4d50eaca5ab963b6d9c637f761909e492a5bfdd3ae1922e0578adb55ec1c6292930aeb6300c28dd70e84b29d7c5fc977fcc26cc287c1aa7b1f3791ef026fa10fc656fbd39d36e4af27e036f1db9312cb779086bc3fd7d19a348e8e76f97f4143aad87f40554e94b0187012ab6ed78499e3cf09544927f00454e823067649166d586c69983cb7fde266e8f8f558920c8455c4bcb699283bde5009271069137df2fa69f8c11b681ac9458bc56d4771548297d510a75990e69bde250ec8ee25f158d6e1a23122e9458ab4e5e3a973fffd678829dfd01c5cf0dac94bcae27859b4790bf492993d164ea484676a2be121f011c44ca465233625ecf06e25cdcad5de049c9974079524e148fe52b350b4a7c47da50814c2ed32199aeb5314c230e94b5715dda226880b28d7dc5675fc106289720067dda862354c72b87c624de468f5cc873607b04c3ef62aa922d20ecc545b892b38adb1d2c8dd507d87dd8680c28fbc978314d8c2d0b07abd6a28c4a64c63055d240180686cbf4bda93bd5b326c038bf76c4807fce3c00e4fe49560ca7bcb4f1553f12f08f4d6c0bf90fee389cd47fe01b3183462811e2e330a3af1032b349f34161a9d4c6daceeca0ba391cf4d703385f44d6c3cea235fc5d64fecc17b0c1ca8647206f3631cfc3ef2940b60f81ff8efd8a464832186d91928d9821c966a3b1e553ae794bc6c3a0c5cbedf763ebc18f2037f2c221918d7176b17088a30c3f02f70945f09df9b1a6379b89d5219c21292216904c5b43bd28ac8e0c4a0814f885fc4151bc08bf1f1533c61e7f6f56b6e8a2ee4472c712e8af374e71820b1c21d0e3f915d75baef9f1d8c02227471160ac828b5561a5386ee69c3159f8ad93da1810d44b9c9b88f75ec1f83cca7ecd40a2714fa1d9a9e71f76d89c77eef6ae9224c2f256b9a2d1213088a0806423eda9eda3f7deb07ca5d4cbe13bb9065ff7c85703a7e2168cfb062d2c117f7fd9af8edbf8f92ec5af25ddc76f83b45b29a67bdd0118910b5289231b31bdd7010b42f982d17c4182b66d07f4de3685a83277ac851fb92fba8aadcc9294a49914f1219b3ceca24c38943c8b39c210d31b5d3822048f70ce3dc1b37bdab692053b94cea2ca27d0df783f5b10e17e01791a2f580a8c60b621d07e2bc8338c8db6a07c86a1ed569cc78cdcbae00d751eee846e13d948fcde9fe314ea84e92231f5417e2c959cc704eb23c42a0ca13e0c2e942aa266744762dcf3cbe1478ac54382ee85598e9a17e71366e2799611043d7d61442b2118d5b8b3b6f76db13e70730c51e2f86c0d08f59d77439f3fa9d85298632a4de8b6361c9bf0aaf9181e38c3639d86d113a3fb1b9a08e35d39776befcacaed55b18e549e3e03fba66e706ea6bd53b203d767380efd758a486e3f04050112ac8de3afe4cff2efcb0b936790a680dafedfc9724b9e1ccd22e50b2d76670085822807e238fd0a4ed5697fa48b5cef2274eb32e800684068ca49a2f114550868ebf387d25613675da6e192092aff13ea281af0f76a7272f8a19904249739a09c190891555fc51345a8104a161ee67090ccd214eee8e22af2db1ab00e24e2590a41b743e359cd6106b75cc1a86f3625048285c2093d2d1383e29309d7c4f734544d7318f840c5a0001ac5248e5290c19363b59c66b31790280bfe28df302113db93ac40ff08f92f93e0b3232911133bccfde853f653875374a4455161786642a242f322a9e3cac72fd0ca637ba947a9ec380af5d7cd5abfd8440133b88fd505a33ec15d11d60a0a81dae1622a7d18b766e89acc951940bbe808d2989bb28d0414c30c38a7515e764b833655a52b62251ee6eb695ab2b263f7bfc7edea80f07160a09464b8a43095a1312080467353c14f06847de07e8ac071699fafdb1737cf7c886c0afa365a69e124dfef95a76d7342421bf5dbd36f7673d25626dcd77c04e0756ac749d3c2b4aaad9070c4d16305b898cf6312872183f21f57b08013a99ba637d3e7ee9d8503957726d6c019b8170852503d8f716dbbdf341fa6478b44c44c58895de006de04820c0674b6d44beeda9fc4f971cf196cd0a45a3ced5cfa49908a2e968714025a3210bf91ac9505358e9234465842420115203ef140f6dfa97ab5eb72d7ee7f4dd8043108d27ff18f33a332cb5f0f81deb657deaaf7245ccff48fc31fffe439969503706f6b4b2fca8597dcd012995bc0ab7191cd46cddfd8ef6324576b6c1a7ec523d4029ff0329c53dadee0b9f080bf369b764e47d2fa5416ccc0ffc66ecdc5681c412008b341aa9903c891122464c3d928fb7e0c3dd9a8d8ce59f039fd6d47cb09bddff778c5e578f0c1f8520f4e4654e6fd8270cd9ffa7b8f5e6631501effb5e4a9520a5e1d4b2df26395bf155353f37bb7a13ff50632cc0712d1ac81620170540faeafa8506300998e895f912518ca08636a2083ebf4d6aa9a5b5aa30b7ee9e092c4801331aca1a08611c68040e351de08bd918b042a9de3d3a989780a2464b0b25506dd497524173efc1aca65a65fdf87712d99f4e5b9fdb1ff375a2d3ba2bfa8133517a14c866f00444b2a173c9db11821ed08194d24a80c0b8981f1d54d54e1b100ca8d6cdf1884850a13063f4f2465b2082d953cc01f979d4c0d1316484c263c646c68430aae0f784282db8043e7591b685f6d5ae11b28fa3c06bef2602513b4c85acf09bbd622be510b85d147b572e33e0ae1da2c760dd7c9630fdd0a0093b27675e98f9c9ab70db23dfa4f322f41cba9d26550ad0b4a3487a961feca722fade93344a34aec1552d7e13e997ba8eb041c93807522559a4385a676e08c338322572952d2c1168878f53044ee770a75878e6f648f3e458c34174f708da94baf3bf6c6b4fd6700e113b5119aeb64429afd1be91172fd80ef2486815107fc9dbeeb6bedaa3f02d155d544882602efff75b11590409153b493eaae71fe31765972bd9f890313a938656876ffc0334e17016c2808cc5444344fffd8cc973ee41ccce001af018f4d80393be931167b32c62452057ce0f9e3aa4b8f0b56019e272315656d81398e905bc5b7fea5dc5791a013a5b55e4aef098a6575de89634dc38e4c8833303cc3240f103baca85609b64073c34459c00256fc74cd3bfeb11a2e6a0915907381fbdadc9e1dd26867636997abae62e4292d25615d0f7553af77f6f0e148aef5eae9b95ce7ebddbae3501d9b8b1846fe427026e82c47de45cf81c93582720115c6ea46305253fd4ba0eed15d57d59d67c93745083bc116d9befaa4e4f7674b4256b21fa65741f14d506d04a4eaf12379a411d8c3cf6428ca712d4c19962cf27496400d94848febc4ae6661fb403a6d9071c95c35b0ff9b8ab3256e0d606df935905ecf00de1f401451eecea27e60c68cbf8d456cc7fe4691c642c09a483444ab843334a78882d86d01851284ec3d9c2b19355a19ade34c6c9a2cf064b21f752730cb31aa19a3db96c626be18ed05d68c437bcd3c12cf013295af1d86df778af51b66659d6da9e51855f442dc5549926da0dceb0f1794e3c668d4b14ffc622cc53c9db3be53a7937a750c56eb61826354e553753988f6f10cf0d12f5304c9e0a55b2d0a704b01c8fcb558aab3cfdf742fb07c739ae1ea91efd9e9a345388f94d70e61d75f902c851bd9e56e725cd3161d98203e0f02d27b2b45fd489870ea3ac4a4d06bf462fa6eed403f30231854c0177790c148653ae8c051bd6fb5d31c6270cb220344cecb897e6aeca8d16374223225bc3224ddc938abdd9222164c7d53eb1f52ff04ba9d74f5f5afb3b4735d497674624602651b9f7a218d015cf919d814ac8e2a55dffedf82cb76cc5563f4b51eac39442de03e28f9c4b50034d60ce19a87a4aebd43fe65b6822ec8caaf8bebd404c7a3df90572ab4f8c6e5028473c6545eeaf2856a1e5478188254a4553d9eafa4cffed7042368d83f9a974748f3443462102d920c041b1f589c0140da4449c371bdb3e83c42e9cf0cae98e6b6601d021090bd174cad9d230cf71b1b626068a293be94ddd21786fa53d6c41325976537188e50d83beea70056a49645a213a89b117e91a8b947805236d41fe8398c2dd775e70fa0c185826443b942bad5a6be3c9e203f4c79843556820c3a759b0bda4923dd7aa60bce3d94e59f35cbcd5b3bdff8a1adc898b5c7920a5cf4bee91f891dc321fdf5a001708d9468684dd8251ed1e3b29ad11fa5ab15aa73b144e8c4cbb69966b35ab27bba5b9dd2f7ac7d7629f7282c3f8e7f453068945d47d25516a21432d6a522482eb65db818cf57dee158b788fd7fa3de6b1fefa39be87fb3a3f71013963a093c5cf474ef0f936412b2438efbd08aecc9bfb2e4e9632b5ac79f750e6a843927489b82d4949c02fc8e7ed749a5cad6158540f98abb4107589bfd6798d626e2fb69f1bea0fe3d4cb61d89444ea0a5a93bafc184ba616bafd9917a5fc0bdb427988dd73c66f62d7b784af2b62fba4076ddec14f472ed8d563540a4753fed5adf53905d79eb00bb53b30fcccf29027825288244334f1ecfaccfbe9bc8137f53725d794d0a51fbab8f37edda8b9298a689491847a09a08658d4defa3fc6d1c95a79f19dbfa81e980b8b7610873bb0c8a98f6765248cafe36b419703a78a1496380c5100d79149125b5c5651454194ece08586fa010526c57b7149d361d20c0877af6ba4ae0864016ba85c037d453408fd9e8052ded94271645af574365570d3d0cf2a6e08a494b74919232c94a40eabdfb87e662b3e52d1419f7a02c6f7010e23c2795eceffffa8e211b883ddccf9e2d2ac59db7a6f55925349f724bd989dfdf5c385dfbe85afb7b93228a2277b90431177e94484f575764516ae385767adb0ec02e931de478e2fab29156486281f5ff54e23721376294d8ba5a17409d9c5fbe341ab6e47aa511505d6e9621a1c3ba50e47b05e311b21cdcec16348d08b8583cc5fcd0e5122f240328918803515888daa9275b0c90dbb3d51311cbd272cfc3381c3c9324cd6f85b6c436fc120814dab78269d9c6534137a129393d72e4b971d26d57dd39a4645134810668c1365704b5f6d0e46b5d639d61f27b01ad551cdea81f1663ff6603ae2be0372dd33b71cc2c43a314341d44595ee639d1b7ab8b7b7eec8d5d87639a4c086bf70106e9ad1d34c7928d49715fdc02dd7575c5f070491becb88898cab08e19c1d87b12e2f4b4223223a67b67d2374741b87f20f078ec16a4be1d5c0587816b6477a81163a51980bcd516f169369c370ef8ca100f60b5e75599af2f73a154447a23ceb14f5fecfaff4e5f5e1e5e1deb6b45c3332f9f3ce6a34e9f57a44712cc4612e9d3022453bf5fd4b1f46d4eb03792afbf766184ff78a4e35cd5e5cd862dbb29e5150f8f93563df92c499bb013cd5c4a4da2f2a263694bd471cc2b94a2deb31fbd491c1f60bda5e8c9471361c401a2d2ce15bd6615d381d6e732b0b7341015bf93ca70f46ea09c3c2e20c08ea7e7cef8a730684d23a26aa44db034a51b4582f81c246d37a4655b3f32047e76925053f8b2520847dadd7cef1937a0b89f8e24945b1612034688c4d1496a1d9249567fe98ba71c87a8cce1886ce557b462f327e45865815b5981c630a8563a83e069bf176aea990f66091934b142b2811030bab6b174cf7e4ab90bb8763742c9e49e16491f518f93681dbb5240e860ad69c6431303ff3725ca857a345e871c4753bbc547090a41a42e30a0e2241c76491bb32fca113ef92bfd8704ea7b6b509f9f2e9a5e29bbd95af57f560289e9b92839ac07b8983ca6551d0847667789c5ceaa48483817cc49afef9a2897135583f08ee8347068f1977e6c5bf1d8cfa6e7743bb865686af90040280aa6bb29488a222be216438d1a78bcd1b7d17012d3da2549968695a9b34e1a4cde39abb43965ef6f589a29676ca4a3434d051280e5e47b38c34fdf3a521ea46265210d5d301c11f8df3579e6d056690b211fd813d34c6e377758f281f681bf59fa60a0358936de579b711ac6085b826608abe087d46bfbfb6d6f8a75fe41447304dfa460ace157cdc4df706e6f4f39cd1c38a464c0ad4a9db69448ef1ac9c064ebd4773aa40c343f042923627e5a19ce39051282cebb844337013b742e30f6a133372449f4b6ead9435210cb0d8796c194d8832329eac26fe3d32b72f422ccc5c268ffc1ef4519a30e7cff88ef3d8fa6c173ef8dd93b9a7066441fe014e211a0d8f13dd39b01e3012d99961722132438c487a5f888246f1551687d68be18160f8f5cfd95b8f86b298048511060c6e66cd5f2b29641dc0b76afb0a88abc2d02691d955e24706293323b53034ccaa768422d27c26167fc21080125644e21c7857f0a4be2f76e314c5c58da5d416b0089d05952625ddbba528a704d4b827d2f7665446afede1fb6080cf8cec7df70e01a4617c0c5039405b8978a74fe087756b9da0430fbdfb1a99916312972203a8b5311a04f3ed7d9bd8c0310d4f38176625f2d3fdc250690c689c291f67f44ff6b794aa319a75a4d1f7d8cf589d70da014bfd789120edd1180ce8ef1eab87c7a0930c4de5feb08fdd81d2d57a91557a2cd979ef94ac8b0b88cb08e7345785605de9aab8d37c9e84bda0fe5b3bf82b2858f4d10feed4341b53405c045ca53247c6c64f67201001e29ede270246e2ead7f9aa593e2eceb5eef68cd77cadb0b773ab430def44395edd4350b9b80ac2e0f998045ca25fb6bdd53f8d752a9511bb1b76184d70786b0270868a9558426c8b35b0a883b789082bc9cd5f4a2c4557a75643bf37a0261df110d85a9ac6db82d1e48f9aacfd09cbc38847e35d67ad18d657ba9122ffd134bd1a4f437c1fb2dae8a07992b85e03a6bdb39f07b6971922663f560f4d96cdfbfd66730e409815e0c09765a3a1a6bf4445ba46a0556ea99c9c82e7b87308dec95e8983c837647e3e17bfb6c7452224cd4b7d5d4e7addfd9b6dcd1f62c2836394d4b204141c986867671281cfa0eab24c28ef5043f91b1995a363267dc7b5eb5c35cd32599378beca4fe125b08a1c6f8d09dfcc8a14d41f60a9513089d3af11a4384c8eb4460994f86dccd9c9ab1ea7b7f6e6bf032d3bf5933e2a8ecc3117f5c5af91b9212d76088846b18ecbb153dc909336d3cc915065f73b7170667637b243ec8bc028d0767c4d85617ca0127ad802c560f7057770ac39185a13dc33186bdd000befa42c52be2e39dd9aacbcadc002de88ff6c8293374098906a2e3e8205263eda433753f3cae97411bd4b4ebe88e7847662d2f6008f7225d6b82dcb69659b8f7747e4cf2efcabf403ec9d03329c1a3f2e4c06c1c1179057e24da1f95713515d2c6d4e521af280ee9e424bac00e8c83c735a6d73337c081f2d54f1a898d76a5a37851b738e08894179b3a5fbb62b5d834893a1b71b0803762fd88f042e5dd3416bc0efe71d5dba614ca90526828770eeb8f8d8801b2ac2891ee61c94cc6cdede7cc3389bd32d9ac078ef71f892909a50d1a24b290fb9a122acb981f0a7e654c50e52bbc0d9c214e67667b91688f97a595fa2b4025b1bbf485e08e222bef4c5a2b5a72e2f722226a7c0a4064261ee2b6be08ca9d377ad760869225b789bb10bbc60ced413cd442ec15146c58cb0fd80fc97a5e2c06e69c6a7f5b4b0d4c93a04c377545552886dee783c534bd71f97eee1d561f10565fabdc75960bca4cf8a390c25908d5ae9e3b2a90457b0449f3bc6c23fab2ca5b6b6aabec0ebde1e0db076095893f6fa6cb889a1b3d35fc449e749fb700d138418cff0de2632aa636cb21df0f49ccef5039a01f79cf3433154edf38a136258274c704b7cc6d747092dd779185898b6815c1b1434697ba220427efb2cd0b0c4308beeff11107d14c916c0427bb8d903cc5a7bba21bf28ec44865a8b915c88e284624788626452f7a382f93fd359b677cd7ae2292970889ec7882cf5c50dccfd0a78438e0ef498b657319de5bfb8807813121144d6519aba464621a9df54803b74c56de342d4df2318d0771962b349952d237a17dab7f01c6b6c0f528fec15a80d232c10514f84bb76ab94e321313ab54bebccfa56f0b6a2de5267b7962d45787cb43301c540644f263cf8cc52dfe0268682b88f02a0bbae9613331c68c68b02ebca5db5a0deadb9488c13dbf7b0ce82173ebaf83c16e3160c2506a5bb2b25b0da453043e065c1c9807daea04d1853970c1b02b335c6c8cf875e71068529d3b09c8bcd62a147d6ab7f8e19b09662f67f2a0cccdf56b2e22ab643753279d2d42575baf09c7a7216bde428303e0339fc2995a7689901f13f1c0dd6c20b920c8815c718ffeac314906e464b33ff91dd706304eef29dcb385afabe328e22177ffa8bb5732b0593c0c0ed1050349fa1ade4824b47800a9eb63863d631704cec78b0907e52b88e67a8e04f2f1896bef14a72133a7cbf21e4c91874b6cf6108c5dec291daf826ba9ea4e8cc99abbdb36b1e423bda3e9a2b827396248cd00016797f168850b48ccba87a8345e72c85ac3951fe9216236039c259c51d81c9aa0a534c4fad90d238799a396216ee8fd54c35c441956f3d93e108a339855eb8e7662b2a604beb236ba2eb4a44b9173fb4f613c440e2847afd1a5a1ee3ed8f817f31ab18f5e6554593d12bf93eda059cb31aab6852eebbd6c22ff67f615a04d30f7db6d64e087d52216fbc51559c7acf0e58b06e7faf09a822a9e7809f25871622908b7b9e3279792c3845f83701a4109bf5f04be27fa1fd98803346332127d7cba2137e4fa932cc29acaf9658e0825f27486c34496f45e0619958e0d1a67388090993dd2925b57ac0c8080e90cf9fe1205df624011938e9a1a6ba50e390c3a3a6c3793c7019678ab5bbf4ef8d24322264fa5e34a1888d32a3b56e68025beb7be8025920e9a0bdf7224d5132169e6fb63a02fa66ee10c703d0a259c8da12c4bc60020f9420fd347e0422d0b48501106fcc03d1968c8478c35e45a0b2c30441776032830a1a0dd00316539222cc303dd1f9171ce498a302c6f02c4f58950584650eb7e4831a099a24af1e4a9ac78487c538d8602f22bd11583d6bde90217efda965b13af417096844a6e16b6aea9465a092fb29f8a6355e869f48c27fb570388330b0579b1ab8cb9d397b5d258db3105ec3747072a4f7af7655812bd268d89964d423c599bc3e03228339bc0952cfa0772bdeda70ad8c42096bd6dbb226de27252a46d0692557893bfa032be8d17e8809bcc4a39f2265ac5d5acaf810ff047533e13f7b80c1abb3217dabebafd3b2241a6cf745cdc6f9dd73e1aeebd2f726cf74163d9e34c2dfbf37d2392b25b8e0740d21788efaabc0bde9ac980b76f980217a7b10539a79de1255b88848e64a20c05f500a9df3a31a9e9864210705bc036a41b40199068b51a206831326810567a3d88bc0c5325e31929e27063a602c48545fe790a6b837236b67ccb34976a58425eff5aa0e4d723f42f3ac56b8d23c45e68aebe8d489df7df6ee140934dcfe66bde34426694474acb9219786b110f74bf6d33cd19dab38794872b5a24dcf332deb8dac523a72f7b21d66063b508fbfaaefda9ff1a816fe21b2dac93549252dd0635eb1afdd885659880e0dcee17ed39bbae8be15d4f016a0315c8f45fdce6af8ad0b378eed3507da0cceb1e184c360440e16338cfa83dc3429ef57821ad5fdccce7d4237694b149fc4cf62a79845f8f286e4370ff2300f97746a93f0fb8f44d8ddc0022796394b19999856754dcf2c1f3470c6fa94ec45f592ff03b7fe48e93b54e1f39b15de38d4310c1dc81eb495e47aea0270f4f71c484a7e54279b256a42afac3f71dfd11ab73e456e6b18385bdadd0613bc0caae448b523f0db90d094a854dff3cf52874bab2ebde982e542abfb1896a7a95f4708f905257c21fcfd41ed1684dd8dc9a98692ac7a9c83622bb93c7773d743ab87db694c7158242ca37d2c1ca3701c2fdc51148e067b38fa2233818028335cd673adc23a0c2140e1a087d3a294d9bd1d7b87a251a10b4d845c5ed3489549a26af58a320dabb55bf8acd4c063c28a19a11a0d825985b2dfcaa49367434a66c07e0b678ce25ec4a9a44b103d074a38943dc5916ac14665fd69e7ff784809385d1ca0ca280ef78f2d71f00381007950dbe4914168af9b150ffdc795a64188764144249e0aba7ac4b1e459fc083cfb50c7cbd397054cc52e08513c4504aee3b82ab482bb6caf69463b3b0733396d873e8b92311e160e80e4ae8c9ae30c1fb3bd161831027e9ebe374844c34b58935cda24ba85b7a30f58432722d01e8852527f3c5552b7c620f47cff4e4fca9c58359f28d40670bdf059afe5f8c52158452211c792ae245208b0fc04328ea2eef4e24588ba020dbb9c627ea32606241758727f7fdd38cbb4ce085b4ebe9b8d98fc7a8f2ee2d622e07f423535e2e61dc85f2d810f67c698838949a6064ee4cdd64435b85488092d9a49c22ae33b122b789812f318d259aa41fdb47372e847211069cfaa962716497f6f7ff6cd290410e1b73f9594255919adf4e8dcc21d985752065f4b1543a55bc111c594667e1022ca447d62fae40851e1a24b10949c3b88f7dcc5706797c0befe29519945b39dfaad2e81c47ba4d662f8840dafeb3c22566968982ef8c53b86f18d31a19f988e8e34385fd89c96462aa11ab3d1440349aa47845953a02c708059a9cd905eba874ca48456df7643fc176387234a40ed1990405320bb7d35e3458cae9db30b41aa2953489b0e160a580b124824bcd8faa940e236ef4c224290eb1b9e309fb5c27162380e2545d372e58bba7e0e650d9534a71507ba59a986f54dc2d57b1bdc7e42c679a4cdd120eac6eccb81a8a741c991bb70a2322fa8731f3ac2f2a6586141c07d4e60bce44b075127826606b681edca2b4908639c29ff56535523be09a1aa2096134ad5e402d0a9443c572a3d31b9993dee02db7e015269f6ffc49426f7a28dcb9a0cbac83f957a0d9e1fe624626954185c1b66460a5cd73c62d61fb4bb7aa4607c912524c19d6797d2965a165ce652b6f56f5724111b2ec4a6126ddb993143dcd0eb14aae94f19907994899588e59637588ea5d24931ca5e18e092f65ee45e7a80c5eaf9fb8710fcf3bdbb9408755d45dc713c4cb7432d5b1eb9636c223492118ae5fece426f59bc08e50ed53a649043497d56c0fea470f6ff5f4872e27f9a8256488462620f49f6ce2515fd32820171461212e15e438f216f6a8f9e8788b8b058c53748e4fa861562be935768515c3601d9faaefc145066152ae5de70624782a9aed3a763dbb81ebba776230fbfb3e547f96fe020ea913c0e1080848335cf9d5972c89fa4d6c7109185e49757aa32b51028a28b71e21e50f0b2114d0afa4605af1f6f04f61664143b6e06be64717eadef223f326413ac86c94d2690255dfaf0a760beb3d4ebfbd82f5d3945f2cdf6f265f32fc3e7c3402964bb2d0030384d2d34a46e7e48a398c5ed783aa91fa254b291fdc8a879240710d03c5d7eb5466cc629b1e4e55a147cecd3e4c5b4af15d73aa34b412f43b59980db70538f5ba6246107b893957c41054411532d117c05bdaf0138f0f37d37aa0e6cdf02b4277da9f2e976b67acb82ed19b9021edc0c57944717c030179c63732e6e08bb5eb01a45c8e6fcac47cee7fa7c3fadfe971ff84d182d2066ac183d02cc0c7442f97892e4a1b1c987ddf779a2b39140e51a608c30b70fff4f9cc48d594ff037a1337cb249560081b103fadd503352b88cdf19e4388726b9f7668b5fed8f8d6fc4000dec8e5409e86119c0d616f6fda196ca16b66267e6c39ac17b6aaebc0355a8c28c54928ec919437bf5450860cdd96751aa6d9e8ccddda9ee9a3d793ea2ace9b14d0049d735442b892c8fd150c6d992c45d2fce892a9aca397decfc89da90dfddb99a824571246100fdee499ef0a9a2d60e8c675aa833c9c5bb9f586d1b1a6e0ade6d0efeaa9a33fad55cc270eba5bdc8db75dc3f1cce50457f63e573a70bc1ade44f2a369717404d8091f7626b4808a91f18469c51ade5fa2120ee89226ddd5728a289f1b6c3a47117a288bbc22268416128e305997a799d99b320ff242a0250203c9a15726d64160156734eaf37e0b1b8535404b6659bf17741b96cc002b2c4466aaa427ba6715db562b51564a0a007cc878eea1b02b9cc8fe26ca26d1a30c2304aeacac89658a3064882e2cea9f8487a861b1ada8a6d0bed196ae35401ddfacc5ac9b141bb9fc99a0aa0e6849413ec3c60c87f7561c43010d2823515c935cd0a15ea1b55834b1b8e1043b8f4b9d533d6c0d1775f6ae39b0b89c77404d20680608c64a4c8f96d5af1997ecb9f85bd00c533c4bea611b71c7ba946aa4724746bec269d6be6b2825a6d49b3bbca4ff16ab12e543d593dbb021a46a3fcb086483eede6a1d4384122f5fd94ce4de2c883d4513ce8212ecdd0db88e166b59946a1d4198959e872d6d1f029d8e4685477146a957a7c4e3c84413774dec4b58246163b227a1844c8f9ef0372c995eef42806ce21c89640cce7d5a48c3c76e608dcedc327646af69ffa61409b77cef7bb5552deef4325a20e89da192ae0d9a5005c90ca4ef79852086de7f1f2ea0f481b3887783d32985b60c59e105d11889da50619490a6689322308a52286ec9b2009452f63b281616537c2dfc6da774884212126639304088a50623b6c8cf337b41b8518aa844b2831fbfe2e7ec313e0d509f93475b3f10f51bf8e871d6deb63794c86e95803c9f2d3694f65b76d6abf8fb45003138dccba5554168763e635176ad8f999725bd3463a416d432d210accd6bd625b8d81fbbec52fb9a7ecd6563d71db5ee2db769c07fb9ff769463f528f464abaa25f3f8c9b6ab5bc4923dabd9d916017796978014027a7f57db9da70dc39fa633b0647a3a4ceaf86348f519fd5cb1609ea55b63e6fb6cecce131b371ea822a4debbfbd13024305ae0602aaaeb2a091914652c1d1b97fac7174fe2b16b3fc4ce4ccb7216ff499db59e04c90a0f4103935c2dacce139f0592074aaf632a829810640178e15073fad3c6827959665f68f8e4d323b5a23e316921419425aafe13fbd790e362c1635d6aa0dc75b103702a59d2916fc00f4a7b2796f6c2e63de1b2bec17d7d23b2f27a60c70efcb0070c24b9ef0f9ae2ac07c5c1e71d83fc62ad0af8dde74897b71ddec5125f3f72168b9c441d2a4a3de432f321a34f4d3c63e917f078df41d5bbc3f56719538555be69b3fc2c25404aee4856a0ab3c936a108ae71dd89861109f9dcba57926aa05e446a320905469fed05122a78ff9a15e44497f4b4e84827eeb7a2e4aee2468fd6711b1cfa66d6cd1670e37a4ddd94ccb2857681c3bc4ac28e6d94418eccea172777e552455df607f4359d03bc341577b7775dd92d0c4e5a88b005c23b9061e0987ae995d2afbf22b8a2cc5c8a13df0c142d6cc8ba8e01773d2f5d020f022d7fa85e33b8542f039fdec2898276b2db25d4493d17d390dc1115791ce2fb21fde3f9c1140b9a5b59185d6a63a6fb72a7f1c711bb28236c0af1da14167ae6085c243686b8277e207d677c6fa836c254c6cd27ce0b28f83ee090b76e76ead0ef984f6756286f8884cfdbeb4c2e5279f64eec8cb6fcc15119c29bbf37b40e7d368cde535bf59f1f0b377d501a9d72870a75c4d944deb01e2ffb71f8448a686f2c86fd707e754ebaaeba6235581f67a42165ef5dc5e87e26b1939df33bd9e3aa4a5fc8226141500bd2f35f3866e8413430158c0a89931924279458652aa389f7b18cb01ce4d1428afea4f261162b8a724180f7c72935e51294c5d53485a33e378656ec08b596eba1d568e74418ebd6b7bc6aae9b993a0849ff156cd172a6816b010c2dfe2167467c52446d81c58ab28ac74610a5b16cb82576370b8a30fdcc90be837cba17217163c44e55a8aaac26a625fac3064625f2a881e964b720f278d31c58851b2fe21123a09022905f470b6d328d3ffbd8178fd73d120bde3213c2b6a62644e602a5181569b8e9cbb1e468a72e924ec811d23410819ac497e12d0cb7a958831cfc373a82c0acd5375cd0cc92962c4114b188c59c04357ea31838a1cfbf9a3725237423c9b30f64e45a29a670a90f33294c64a35780f137f0fb52fd4a8b56d65b4a2cc1da8373470aedc20e160610beed19cc5e9b1bf604929966016050d5440704b7b24373eba894cce81223c360799e8e87aae12474d7b5623a7a19b45565acb1d671c15fd148ef4038fdf699d2b3b9ea9c08e3981e416e851f9468ced9c96564046c94a74921f0dfc2aa66890fcbe7919f32542b303b6215d865b45ce01c76ad4e0c4e1719c40318e0ebbd39d0aa8100f59230b7141805590878cd793616b8dfb35023a51d1d8f40c8edd3469c9caebc78c70521c75c21c8279cc83eba2f7ae443146dbcfb1c1cba900b532258430c1e84683717d00f6c3d1828e4da4184f657a5ce9a9c95230e86917e445794890e49e659d213144502b9aeb64749c4c1df1161e8eac281a9afe739e7203d25b4a032fee3a7fbee0104a602cc41191e89c55230747a6d099b2e9a96c62765c600c520f8a84b52c09d11e4dcf670ad31f560aa5d85a66ce70a4e2b99ee960ba941a50edb5247412e2a2c9bad5d61442a5e147ac53042be9a27a7630c188948368c455b91dafa0ccf0f315cc4ecad8e0b0d0701c7c5cb7c85e224c6505fac991c24087817da3eb748e4d1792b1c1a764517600c8e9c9f7876aecfa1cb2f06925e4f9f9dec433b8c006944ee3bbe0de67a640587dd6724fa1de1bd91b468468048b692b91ce081978ab0277f922fe3cc61fad7679d5533183efe330547c6690d5488e085c08f9451d987b4b3f6623271e350e0d4bf7dd2d1e60c3f1cf976233aa34f6665f44a89cd09c7b73d4e881a2a300644d663eee16aec47b5ff1e5b062cc692a8baeb661b15c1a3cd1440df9f4277cb6c5f9413ed911d9e31bfdc017e2388c27a223dd8323f51cc14d8fd1732684241eb654f6192d1789fdcf5f130b311e3c6ff1081184b1a472f158bfd7713386224ed3d964624ea7697c1d783b8abd2c048e741268711ff1198f31f93aaf2784bc79d348038b0fec2e442b7a54b2316b49adcbf41db8cdd0f76c0b2ba4268747137cea38ddf4c237ccf88ef11a3ec5a6e39e6fea59b13cb30b2681a18cab596541365ac8469237cab06c97f811d4290cb0705c1730fa66e21c5c57884a687026454d65c6205fcfb0d1dc934b59023fdf7453e0ffb5b094e8344e1a89781fa3c82850cbc312610b8d83d3a18d57941608ce9d01749332545f3761dab11418fa2b784dd99c6ede23bc306e6147cebca4fc15317c45e29cb8abc3f04d9981694f916406a2b9330986831a625cbeba1e092f619a11a872336395d23fa35b819953e21ddfc54f092de004851b78118a8e3c9628c7e00a94acbb34830455b87aaab222dba61cbea9e4d44a08762c6aba767aab1b86d54ac03cd52d52205e9a700769b7ad01b3f2c84eb79278e047a83a9d5d00ce2a1c253fedea7c7e901c504aa0ca97c4bd094efa09e6a16299652841c9484bfc120b037273e9a54a468af374c89150cd8c5784d2306f38c0dbf91160a597582d8f761e423ca7222033113f57c192c4d38d5fa690e8244f5132ed89dfa1519f39b4a6631690005417a054074cb0801881ebf4bd18a2f6e31ed3bb40e39612f5ae02d3d0900a875ec0b6f4b5ff3b1cc17a2947425c8b40a16517f6f8d59c9a382eaf64569f4cf1ef06ca2889a4484e59a1b33eb43f7a87d6f66cd04b2cb11031635af7f86656ac839c752a33f497563c5595ad8b6c2e95f93c38d58ca46d60386854ff8f8753bc8bb206a08d694ecd7acc6cb4dfd525d406bd1c88050a8aa25afc0f501cd71c66b5730fa527f1ce1243035ce18127f8e63cf0cf1ee095cae27605f20c75853c9ec9a800ee67917544ecf025115f3fbb4082c0a8dd0bfa10fea3ec7b52ec132f87d93e3a60af7ed747694f8c3314d2d26e1325567a2fee29196c21f3ac72ffb44b7ecf8bc445af11469c762a027a76c362b650e02e473833e2ec2bc3568112142b0fcf92bbef2cf73fe3b405f4d28a13a21acb0667e07b094a3133f18cba5b68ce6cc297e98d5d79595f365b354662d5eea1f109bade71fcac712106f8f8d7c7409b57b72d57346c4629f971664eb13fa3aff941afdb1cf67fe93b029b44d863c8d89e55bba5802673d1ad811b53323864780474396e5e8a2012eea5496390e8f8228e210f2e9bc4099c926cabfdc64a561147aa0a80ae4ae450c66fef64d086a7525dc975d95010ca0fa9d99559a7c663a54cdf1c057b43ecfbd399fc40e28337dfbb02c963a1641aaa9d4dc0b05570848b41dfe0ab77a333584bc8890105a3812c14b5647f7a0596064a10ad89e6026c534858d4938aa7f55cfca97da885ecc8469cefe55d0c4ed8df093e6a91610b0545346009c637dae105af59f68363b617fb013eca5968d7377632284fb560e813f8c38ec2eaca970f98d6db67c05d9d6c0c5b89a2012969fe76f91e57e07e0d608f7c6c087e5ef7ac38d5e9e67eb0b574955c8365223b232b7de64ab424708f5a42ea0f9d7dd07057cdec11c442396a10bb89312a16429718424619f3bcd2a7886bf580e22a0b7480a9d291fd618b33a99e1e427e050b36787cee51c111f5bee5e5a2b039ef4b0fbbcce7cf303b53cb87811d1537c7d679fed7b8619cf60f482b377b8de12b56f30c7fcc762b97538003cff7e1898242a65a453e56a3fc0d89fd4d449ed92b4848893aee45ed0b71eb90481fd33c2ad8932356cdd10a4e30d1ebf8c5d8927a6522f1edf48345485b8724fbad25aa9bc16a566a59a138d4c61cb2868c027fba9043c23fc844ef31913c945318ceb38154e6a0441559443ce2af8d107f7ac430870024f4f322f512e4e641999471fa1ca0986516a43008fae7e7b828b343a1f1a85c8cf97354ae6eb253cfc45cf501a9d033fd2bfd988100fe5c05019a5aa8f175a6ec902b988544289c327198382137962016e555b7e7a3804d7b56edc8047a33e7b0f2c65441fad1c80c4b412d4140b1bbea1245004523a966882eb1912dc0a9ef41741405dec67a09d0ad0dda336e1b4df2c8bb3956036602d2ab025f8eb442f1cee7b7d6031429f6a85e3ddf75aac855d701f484025aac51595d400cd24a07d7d6aa264511b9b597ae4057e1176d0482e3006f62c4ec1fa591612001b54aa8c992a4da0e58e6eef6e61e596527f60878c99f6ca2d9b9dd37558aeb4f7ab9a8d380992351fb7af9398ffede841291bd37217b6f996492293205e7056b0551c0ef6e8c4f061583769d9325cea5e50c5264159665943a15afdbef764dd6b80192edc1124bb5ade0717b27d88ab8f1d2a5658c018f1b86a5ebbe2430ee3db5d4eeb9adaeba6dabb5b719dcddb9544e05ad1ea6a94e4fa26917dab5c86d75e38e6dc7b6c27aab8165b46cf59eb7da9ccbc1c5bb6eeb4497d7ba6d75eb28ac7dd6e91b95e6d2b5b65c2dab9fbb3b753a3798ed60b0d94cb9eb105494525ae9b67175b5ac3a57aed4290ab8d0ca71f40575297dd1f79342bb749974636206266544e706e6a5cdd3c52d6b597753da4d9b9c4d7677336566e6d97101367657052a07bef8ea9cb35a3ae7b6ad3ae7aa37b91e5c60f0ad1e1d574d5d21bcb8711b6bb4dbc64c79c5d4683e0aacb5f673ba74b4312ff71cd364c072bdc97523d85d71efbc63db7157e794503ae52adadd29a5eeb452a74ac89c7271efe25ddc4bdfb69655af75dbb676f759c51b5de75d57dbab7be1c2c5a8762ee709d67a6bfda424b94cebf2744b5897a52e4697695dccdc1246eb22352fad5d7caac7024ff2240ad44a6e8502c9508a8af2a4284fe269a530458a14efbd9ed8b295528c53c2560ac3306ca5d05ba93bcadd7d157efd868181ec5d7d38ba9496b992d3adcf25d9247f3d71d50840937dc2a5b095c22ffba995b60fc5370cb0af3dd53ffd5d0df6caad0ffbb27bb875555b0a451886b0307c17ed713cb970866da595ba4a092ceb8b42a35b513c0ab21da98ed074c69aad9fce9a3f7ffed763c1cb980005f88795e2cbeebfb4f7f5cc401cfbcc7fcdf95d8a3bff75eefc14a3f79d331c7b9cbaf1236279b56df030ba9f2b292ca5a4a4566aa786eaa85eeaa7c6726bab36dd1ac3f36648d9923c894b8a41046cdd70295a3dc3ef25a6a6567a8f7eacefc6ae65acdfc61928dec6bcb3831410ddd64f9296717524a496d16cac65f5757ee74f3cfda424a0673a0155a569e74b7fb2b274e3499ee4525c49c759b02f5ba97b2739303788d1911db4aca2203937f2222e547cf7221464fd1f5d88fad4f7c0988e9323c5e8c0932051a06e427deab7ba4a5b89e9d01435ecc66e6aa6962db5ac95a84f45c122cbaebe0bca3396f3f2fcb646053bbfe7cb56ca799d6f256e81a5a3faa7e7ebc4b1170786f30cbed079176dc1786353a2405d542b45f59023b4acfece97ad74e24baeaa0102add64fa52a6da5f5fd6ab533c2c628295ad6329cd1c6bc652b85a3920deeca888a8a4a413a596badb5d6a74eb400748042530191d7d3cb074b4b01141c685366685345b422ccbc9ea880e81ab1e6729322882ebff3379dba5da9925326aad46de00bee5f6a2c14156c57cea83ba32859c26814b3bbbe96d59f533305b77f4acda82935a79c4e6d2b782fa715ad082e3f20390015114338894193664a903389c0eb8abf9ab093c5b744d41dc0659a981faee79f6fe197e0652010a88ffbd7d6d7a05e73bef46fbdac7f66d47fbd5eafd78b74d1af31f67acf35e62539406f140f509f560717944eee7cab7eeb7d2c3bf71d72088750d0eb2908a18ece2d790afcbfa065feb1eb0f989792fe4ebfacdbef8c433b3b0f1b3db0e3727d155fdcd9d9791d388276481d382f56b2c6bfc828b0777d949dbf69913863095388a9c951119109398286b84d500e59b413042379c05e8728c2602fc2c4d701fb9cd781b3f33a72704817d4a71fa7eb44b29dc2a8ff39f802ac4fbf1fe9f774f49f171ce7f52ff9fbd99de0ea2748bf7b91b1dd9796affb53ba7dadb40677fd9b45f92b988d7a5b46eb37c963e775c0766062ceebc0c9c1c1f91d1e3aaf2347270796f33b275e87ce899dd781edc0eaef3cec775e07ea96b1cbb42d4e3b309d1c1c9169491855776ff26a9c219e7ebe013426b4e480030bb433416e98817bd8e189922321128c5922021a1349b4333acc508858068520539cd6c08025c969088d7c0620677ed45892e58b101a4e00b304ed8c8e5bdaa7f28303274547cce8c0b464a98a0b2c413891ca42d5040b324420f411fa986939b183abae11e02ced7076959162cb2ced4862ddbd0c90d6501b35ae323e866e4eb03a818a1566a8d65a7b9031c2231366a8cd8b8c0940325b88dcddfd8909a9214e38386142490c649a7e90b96195b303d72524f2922c9961284b932e41602093838c9010d45a6b65336a5e402a8a8aa1c913a5315475cc19a336750c976dcc971d6d702ed3cc5059025ea69951e1bb3a790c162a30e19053773348d7cd18b91ebb2bf09dcbb4326cee77995666cc2dedf3f0026781d2c3d29212506569018530472c160ab9c7700801591285c70f2f3df8a02281199b181b2936b1865571814625f54a037ba1b1250c067ba5a96ad9ab8cedaaaa9719209eb7f462e2f5e475e675e6f5e4f524c9652246445c9c3211b9650745a32e986bc3299776daf3a5260acbecad82542fbc98789da1513dbdccd0c034d194488a227e8b1d8e07b2c096d7f2a87d10b4ad96ed3a16ab6f97f363b1baf7be9fd53df82ea8d7faae63b1480eec74ad9665756f7f46d712bf25b6ba6f91acc11245511445513c61bf652dd9df02c756abd56ab544b1e52d0f0767249a3263fbafffdb9ee5b11efcefc5f1fb09fa37f8bd7f64e9912c328af80c96300b9ad2df2d763f8e5ba3694a54da972ab77a01d373ebd6e1b8e9b0481255b42f4597695ed6d0bc807901d1de6cdf3ad6d7b230984e5718fb219104892077beede69c4d7396eae7ee5e379d20388eeb563949b0582cef635da621d1a6f5812008b66e361eaecf5a6b5daf5710f10bc33014715a2ae87c393939393a307fe1c4b7b3b3b37382c72583055f0ff862f67c4f0f08635a007a2818641a1252685e9a3ac537c7ef07efebb089304b60b0f090c38d932335c50e42f0603384871d4749628c6c1227ee354a982476c498d0e30818e6e90b1325e448069769470c71732ed38e68da76a830f86ee41c400c4984a0cc10060d19fef2e58a9227477620420491e7dc82fe772bcf063d6f1d37e84beb9cd503cb911506ade4023af0601643c60acb40348b0f5a84109aa2821f82fa610c848308d416a72134c860431041fdcf40385258926605587698d0c292a07e190349a00710528cb61061839aa04981e876b1809675cd9e6f640370cf0f8058cfb52a32b81a74ec0160065f78e173c081abc705070be0c0d5b36a56ade99f9e15e5fb85a3087d815e452cdf9bcff5ab1720cbc117b641b1b50a377091a55fd7bf7ee74b169712e0529f4bc74ba94f5703f47cb95dfa5d142d9c5b884fc992860697feac652c8c580a83768055f4f78c1e9818f4f38c374614e38ce1bf43962960ad43e69038e4346215fde2380212e2c4228a566c0244e004ea0535305ca1e100426584903205052b5a82c277fd178e3f6eff6b5cf1c2f50caedee53f57feb3ae2e576115ed4137eb1fbf3eab4c2badd56a07334627604209282d637eca0710a3a71f3d66e8a9e4c57a8c96cde6d16c89c71155b2741142e10baf816129082117804059e13146a4d01024898b1825ec24c504426b5c0042488707e0ea999d65d4c6067e6bdcb8ab6780df150ad2a9a96593a9653262fc30cda9a9e46207193448a8525873e50c0e100cf121071242504d943c087206cc1227453c518380205aca242121a2cc95596532fd943a4c55553c99b4dc9e555ac6dc651451507cc78d9331a0bf8d9455b00a5a7bb4e05c4a5f0845f3b432954411d892a13cb0808cb20b61e64c0c84e3484c102a2d34c920460541fdd38981be3037a05902491a286582fa271403e15062c4972b46517058c10f41fd338a812eb0e44996da910b26d021a89ff6744f198f63eeb376735f2b4752fa2e68cfe9d3371fe2087d10188829c9f3a5c3086cc953970ab90d9bb78f3b51f74ebfe9d88d1f13a03e37eee882c202809c41fd170ab6847110f7baa16723bb9fdd67c415301501b0e0320e03f1ff70ab1fd64f364073a37763e7dfa3b38fff8a142a6001b8ee5f52760a24fbaa576b8a5b3f356ad8884004be860d193c1fb0fee66f4812bc67f5db90c1f3c1cd73cf912480af6fd2f5dc7bae675006f8dc6b9cb9469e7e1c41dd7b7ef381f7ac6f5006f837ef323e1996bc2179e8b7a0beb1f3c8182b32aa71cc2fc1e2a8dd09e1717f351e4eb5a22d2111a1096a3eb408f14310351f4ff3eb6d1312fee05ef3513444864f966f903b89853ecc62a10f33fe19c0162e253124357505892a20e86104643a354421205146aa882f42476282ea95a9dae58715c7521b1a22b4f25dae150561c3666162510f3cdac244765a94838f4a5ad9b115b94044910f138a7e0cd58a885cbf5c2b3a9299a9ed0043543b018ae740db9bb536d4543361081c6228aa1151210a450080cb35221fb5a11514416b3992d4724829e371536f62c0eff52cc009588b96da0ea7fe698e5b701bb7d82ab7a8945bd0e6163db90503388eeb2639637bba6d3326b76db5ba53da3debd6fdd65d83bb3336fa1bc0719bf41484926a398cda30b5b9a10eb6fc8f59b1bbc1b29e21c0e29ed5bd4f06703fc97206c78d33b655d95de69c52c56236b8c76a30412ca4614643cc3d66e6f9c5e358b8c2569b9a6ee882edbee4291068b3940ccb3f714db76a00b4a27dea735d672d0cf63f6b1911528cb27a17dd4c7426b3b7254ad907bb16f83d73ef711cd9b58c2bbb6ec1e318c7711cc7711c0d2e031cc551fc17cc56700bef634039fcfefc9fc5797e7efe679163149c0f459bf3dd6f61af70cc19bf7db8678db29671235fd73f05c279d7f733c8728d769cb58cfb9b71c5025ac63d38723076d032eebd3106abe09eb9e77e72ddecb02b0bde56c97276c1ff98c912fc4963763330bb33be677d6439bbdf73e2f33f6b20924d72cbeceda73e3731f02d057ad9ef7f95dfdb0bda1216e3cee0bedb6c7b0a737653ea5ebf58f8c4321942b14cf2164ab1fcb505f6473d67d5d5f6d1afb556b61d7339c2a57c66e6715b7fbe536f500f99f89c33644229ede12a1053c17f9ba19318a55d0bc62af835105271f1eb388ef3cd2332bfee614b0e2ebf8f258beb1f26d9f9cc9d0e21930b5ab03d4ea9cb9642166870d3f5f0053b3f64c262049eb7c205b7905def65d4a77fbe6d9cb7bb9f210cb6b49676ac011ec812822c4797c60c4b072d6b59ea3357f382a32865e9bbb8e9680cb694b13c269ad2fd8e0fba79fa0be89fd2453feb7b5894068b06dce8610814a54b83a3ee8ab5226fb86932d42682ae3e47c9600dc482fa8863acbf48c68a5f6c39912652734d38320a4f93b66eb552cb54a294adcf818c50060b82054210d240dfe89feec892b1802f3652049f555845fd1ee9c89d01922865cb8954c32a550322f44f12fdfa134aff94ae010b17240d6eba1f5821082f5397064fddb98d7ebd0baaf65e70eb331317c4cb2f64f25de58eaca5083ea198525a39aeb5ce64b19e90491db965b57254bcdcad6015bccd7a8699991f267e4ce81017ac823560516b18c5ce771ab15b57c8624c389c32bff236c6f3d901d4a4738a403ba0316ba251773a4dd289054ff1944f5a43d7aeabbc824511eab68d3512206bf7c9b5d52cc86e562d1754e0ce1873cfa511bbcc2b58f4ccdadccd4df48777d3f929d370061b320967b0f38bf0bc56aecf33b8bed71e175b3cab60104227b57ad8c46912112c343547684d34d19a18425bc1118d0d5177d358a804ea072966686b96686b8c684d70a1ad495267e07f3822d0ee590ebe586dddddddb3c7c4a092cdecd36f3b8c8a81ce10444d0ed877ec3714b911112b5a8ba649141f9d7b5f1a26d26cb9afcbb4344f4dee55553cac98e6061e422106f0210c0a599658e9b2c586aaa5b00a88162a00a0943182882c637e18c26ba83aaa52210577a741d07c71a169a2885b41d37483468823b43446d2e4e0c19966d775676164d975ddb33fb1c771e0a7342ce2d5eb1133982d420931684eb0e2c353b01540fc2b20d6f373958bb27a8f670a6eab95e5ab516c1f510571ca8b99629425432728017344494d09162451892bd9c17d0cf5151e6a4b6a4b7a882c085163427482b804c5e59a92274a52b8a57d1a9decc432597e77bafbb3cf16a1383333f3a424152336119950f1301ef7d6dddd546cd3e9b59bce2db39d928e141393cc6662928aa6960298bad1dfb6c771bd0a3a60b6b993fc2606fd0e8a466cdff2bb1e573fd188f5ca2dbaefedb90634e00377bb3e637bdab70319e0b79bd4276dd54abae8a7cf130629035792fdc799db00cd6c635ee656536b2948dd1bafde6489e3ba830da80cf819eddc829dce362212e91fee9a8e3c9fc19e3fc17e15f8a42df6e8c9cccc350833736566a6cc3dba364ff7e279ddb86ed5d1233ae54a62915824165135133d9af2ba719bd7ad566f42e5d48bec7c168b264e793b07cfebc675ab8ea7e61113553781f2ba711dc744d54dbc6edcc644e575ab4c5e7d1e399d953cd51db61162151dd40c89d22d61174acc61b98604aa8684a986b474044a34c1720d49931a12540d0952ed48140fb1870dd9846c4236219b908dd82364239e10b209d98846a20f9187c843ecc15fce49760af01cc4207d2302b1fc747425561183876b2ccf2906b1fd5db774a4a866a44a14221e31333333d31ffc6da2917b157d783fb338c4ce6777177b74ab15108f63e2907d21352505c68b94d49aaa27a827a9a827a827a7d94da97b9ddd94bad710a87baddbc6d56de35694725db75ab158de8ac5f240ea7d1f08b65a37dfd76a2d59ab74bbd7ac69a32e5ab3a60aea492aea09eac9694a99493349923b917a4d1583011342a941971adc762616b39b52f73a9b52f75a37afdbc6715db7e2bad58ac5f2bc8fe57d1f08b65a3760ebe6c65a97eb157a18d29ee12b14451c9c1c10468e0e1dbd57d863c369d35b6ee05214d7ad88449c739beb1106341ca0893468d018f152a48d9a226c4ca82a628647d589cbb5224314090204454398c0a6fd32662ceb72ad88d32dff65308a30511fbe34e7728d480e446ab8e5cf946e607cb76d1b63b7ed68058308d28a4893bb11996244d55cc82c115b22b6c46cc8512c06c63df625f6251646c8901d428c7674976b4288b621424290c208c931a4c4111b22258ed40c29513b52e272976b475c8eb05cce72b916644d77224fa08edb87bfecb1edc3d5b64ffbb40f85a23dc578dc97dde3fe8e9d9220042b19905092c6cc8a9cf5da838fcfc71110589b73b906c4885bdaa755de24823aa58e05888551fb395d4a5fec0fa16ceb720d880d3520546ebd5c03d202125e19c52959c214fe293368d0e06f3203bf81d7f54f7773cece7f1e31837128f56595a3beece963ff45789d7775dbde46dd8dc7966dabfe934f90c312164260049b294882a81390a40b0322e4dacb3520396ef93331b35909ba6e068494171e6c90ca618448131e7a6c59f5404285a72c2b243b7c60fd38936406b3523b74406c5801798a726669051886f0c18685a61e4cc01e52803cf5f0d1ea81e4e96347d07e5809a5c8232f39865648226929c87728436b3c9828123ed57e0471bdcbb51fb5304bd21405dabc4caa6d42511f1236a1a8ad6aaa681e4da411928a661488fb201f9ffa6cef23cf074e42f71eb47dfd1b256cefcf91256ceffe374a20854ad89eabdf0fcb800a55755212cf07503c1f8ca443714a3ce616f366de7ceb6dc860fdeafbf59f8c1bdddb088024bc1e7c2fc8f5947ce1afcee5653e474571de73de5ddcebc1ee3df03dd02379fa57e484a23ef35f2338f274551fd5d1467dd07bfd8b24c105be3efc902481fbd77390f74dbe68f143d78b1ffefc46e2d91e4790f85e1d6df4cbb8f9d5d3b72183f5ada75fe3831bde7f7ff31fdc00df82325c32b87f7d0ca2ee5fdf77ff2279fc73924610723206770638a438ce0f479e8d9c56aab5b181790398a8a8346e3755a9636668000020080a4316000018100a8843e280388e24a22af90e14800b6b80426c503298064391480ec3200a8218884218844114640c4206198520a2ab00384cd46e0c59d7bafd9125bbc32a7a6caed40e18c6ad04599bda52527f09ca900bc53fe979a97f7671da1580f0a693c1d93b59201e84846789568bd6f38226ea3012680842569287cda5e2e0aacd80135f3fc88d9aa0d8bbb57d2da69cab0cdc362c14477331654405195e657767bfd46681f11d8da18c5811e6804ccfc5791710270de845c8e80e47777d841963158d4a05aebb8dd2e797b93b6419e49a94965e55941e62a3bd63da2c81183766590e2ceb1063b690323e666849a6f0b639d259b9a78dcfbe355e4612694d1612aa6090cb56589656ed1c5d16f196e7d71386296e126f5e7bf93d406aec85eda9ae53cc689a6cb1a9b2803fb50c0927331f2d9770c2bb04855879da0d3df7cecde6e7e24866f3951d7f377b17c7a6792d4d8fd247f2a328382c12d0698ea4cbea978357e2a799c9cbe56dbf481c47622979d1e27f319fce37ccd1a5d15ce6ff70e50074ed95b0b67a166829d1effd4c1156775bdf55e399ad99a5e1fc0aa9218f5893269fe6494c655425724ee15e6bbd2721d8e616af1da33227aa892dce973859e22c9f2cdfd55ed1f11019e9bdc8be1da05f74e8758386b6673bfbb34ee1c0f6602b59f789014b5b66f2fc6673d166920c0eb3c4ff83c877794e2f4f17c89e162fdde31ae53d74a8fe9e40e03bbab4367c0d6d89091aaea3d59fa06ea5408292f7a2066cea393a38846953026035d10fd087c8b2e1acfa512f58a65081b2a020179a394407bbfe366a88fd84f683f28942e51cae0a04544e96a2f142c5c91165d5e5c157a938bf4fc8d6c8193abe1c36cc2c678a49bf87e5a9d870fa2a88e88d8728c7e728f30182df2b3ac7175f3eaad8936e95c00a2e283b37fd1802b91c7d6ffa1d7623df858d3f48dd5e93fa00e143ac5c92f8db7f9f07773719e86778005eff4085aebdfc2d06e20de52e27348f72cf46f434348b250db126c94feaf35451755d61d9882bdc38801058f67aaba3400308622628a6ab0d08941e405029b2576719534f349166353c2e2aa575fd879c9e8f2328b211620ce8811243e4be959d899e4aef82cc0f9bc4ccb7874403e10f340729a8f1c0c2fc32f80865d1b39736cfbc4b5e84ac7aa039a3944564c4f3dae647697e59d7ed8674d67863fdee6e13547ab62f6d79a81e83fb0c80f454020023f02d5ab6233ded7d17805073f014775ae2232954c03ab625018c81b24e9c2e75438bb992a60afab28947d817a97c58bc98d45e4d7f1f4d0ba76b4f9a4eaebcf8204f2ff7a04cd0b79e721bf8bdd83693cf95b75c557e4a1c5e1783b45450206cbabf09327dc08c1c268995474c855cf3a91e51b213a7974a42e5eda45770b0647298d30f997390c4181f957d49b3ceb55984720a2277186f34f8f9b8713bdb1644338c919784c10d83d716ebc41987dd03a963d18297e20048c4cd1e4ec49532e83e2019d71dcf77a41e04ebeef7372c7068cee399433a721204a71f9d40589325840b856e445e2921cdab084a8e68031d154d4a7a36f8a76dae7c3f59776aa39f9caa431bd481d7b0303a998978c6cf3d0352da421be8c3be62234588eb31a5466803ba56eb7b044b09a3296803f5d76c07db9a2f425c01135f406b15fee5fd178027f0222901262775ff6ca0cd72473ec302184dfe154023f5b0aec169317f619e2cdb6d44cc5f76af5476649bcda171459ab5ecc74857b2d9260d1b18185767228ec6e76b6460ad5db18a48fa824906d8b321e9e867675c1ba6b492bedaacb94b4326f7f52cf60c707587344cfac2fcecd593601b57a84b3be9cb473101a373d8da6c8b924789eae87423c4370df9d3452bea7352faea303ad632813142f9651a5ae90bbd7a1ca4fa83cfb06a2153137460365c0df62a6a625c0cad79b4ffc35d51308b9806e0cc571c44b89a347b5c6b233e25b89f830bf802a9f6535093c6c6788fb407ba49e01b7ae2cbaecee6dc436ab543d41c7ec2ae9acae3f0c33b3dd59b3b6872dacb74a0fc8f43e2bf7a4b632e1df69ae91b0b3e7d7fd1650aecaa174553393dd54b6c46da1402076c9997196a244ec03128f6611f5111fea90ce9821155aed6a830cd8dcd8527db212ee19b8fddd63c51302452e0ebb1fab3ddbc5881fca00baa9db225885014d441d0736f98aa9002da76cf17d9815a56d482600ad6f298ed780d70d593f276033b4487683f1574e74b4d7e3d0624240baf86553391c1989d35c53ea5597a4624bd8e76f1850d9480423c377dba64a9ba63d62df2d86b8e0b0175721c630ad853c498cd261a694fa02e697074681b238e2900c4cc5ff66adec02ddac12af4b448fe04151f22239e5afec43f7c7647fa35914726b838ebf501edd254e93d17363e3bd45e5d7bca10981aa2ee5eaaecd3fb1401cd5d739d6236b3a1c2d25f8d08d3f4e31ad262f957851f4752bea7b3819cc0f392a95064d29e5bcb2ac8bad001aaaf3338aefe0d220f3cb322a553dec8207e327fe5520b071a2818b940eba6fbac12645d7f3373743a6305e31530de558cc906733e10b290373c9873c9759a5ccb93a8ae72d7692cb72bef58dae9fece3660d8e4bf62c2ba322ec9c43c1ca1e1ab616de9fec8823aadfad67a2ea2691e1d9b54c6fa1a12c0e632f903ec7b0252a7b3a0cb98bf6602d0ca2ec0dc28b396b641ffb2492a66eda1fbf80b60e7dd64c49e8a94497d81ecc81502300d55a9550e6c707bc92fef1d709e899b5eef00d09934b3ca299cf8eed004b8f99325dc9692c05eade0b207454ab69fa1605937eb66b919c617067da7bd25b03c6c206de15292a2e4492652d602817da3589079dd6d3fea01b51882b91c8cbc583b91fd8618519fd17d8e28742743356c9c82979db1409ae2d403466feecab779855de7769a07aeae260b7a007bcd38eb286346c552666b019666e09294889403bf65929a42736c83237b9c48a56415e23f47d5bf47a33a0f632fd3a76062fc68930159bc8d3de4ac2c4d6327bec50d19a56b7a4719771d3f677c984220e45518e2e38fb9275d0dd27a666d8fbd1d90df982096cd8bd7b122c751339d3cc09fbb28af31964e0f6029ed5af9be8007220ab40ede6854a2177f4c9c0ad5249e3758aa240a206f44b5b37ab413ca9094dd7285d2f1dd2c79c31c3b8245f6b30029f6e50e1981600001dc6e0214b5f9b7a43702620480c87e161045be572447704c18845ecec0a240127cac0888850da1cc310588ed4de3184c4c925efa9b861209fa25d7c7c474ab192e348afe4756cda4e72f79e9fcf47ab19a2907d1563b83c1d4d4fd8b428eb674019d2ef8284bcc72447606c7ce8ffa81fcd67975a4d944104c9f0e5a175093be2f9b1935298ab01927389fdaf42df140ecfba2915937a62ecc55bf5faea518c756c9544c2142500f7b2d3ec86cbe8eb3e7e5ee167c315266b49d4c87ebd35c8e7f3f81c9f1ba5e15b251cdb0a653b6d73ede674ea123bcc92b351615591cc0e131ae5fb518177f19b2f658925b084c2ba7ed31752c4d1ee7e79465da1e077ad691c917f919d9c683ab1d59797250fc19cfdb9d820f20beb597ee75b682fb9f37907d8c14e382697189998e903a605cc084103fa35f6dfee18336541bc49b7f283daabcb41f0ce56d6ac6bfabb9dc8564ccb66f5398d50a95e79c26d73779147969c66a61f38f54ee07724f8af5ced7b9c6fc9afe444f9b60e5e12b204340c149448115a7ed28fd2f7e622484074db35d8de113506560b95176f782df248d0e1f9f321adabb20b68d5180185635794352b8cc81a77220591b5a0ec82f7aa9748729dae54100ea9583517b5d28039d1f295e1153fce85c54ce198459791015977a0c5af36e34c207e2605b1393c4808de961f723e3237e980713e2960b089295ce8f4ee9b289fb37ac8dbb4eafc4cb06c87103246f354f3cc1468fa899ae6e16799fdb178467840a50bce51a618b76e312db602b6e096611160681641f76393bbf0644075c6507c55b8f8d890f07911d789d9b3c1c23148fb1c55e219b37f83ce3d1ee250d18ceb561741162c8890168515e77b5e768b70c03d0f383367852b292d4718c72f930ac3756564325e5f31d5582998815bd4a531f31aab136bace80b8a54e9272a953ae394fec1e36bf8e57f78a50a7477c139dee685cb012f11b5b6af744e2236f9905337e0c53f16e8224d66332359b1b330bde6ec67081ca7565125314e2a090dc90c44070b3ad736ea23931b4a3578c14e7f06ec9f615e26dd64eea1addd1c8bc3541cbafe1e8bedf0e1482f87e45579fe9a8041e356dfc35ce88f0572aa271c6dec498afdf52ba0febd25cf44505abbe1b2573cf08be97262914624a76b7bca7c4b43ecad43381b53911fce40285a8a95c707453a509e67b70ae1ca41e559ea6402fce09ee1705c6b4f932c9b43aaf3c04138c54e22418cf8c42f1101454b0396b556ac254c5554c56abe82b089b30631ce375bb0082c1bf96d65af40a02050f5c18176d19dbc858a59f89def2ea4abb3233901b0e7c2543e4005112d8b6f4ac8e9bf7faae29b0b46a2e138f1350b281d94bb52af7bb93fbd3f3935e51563c8c7f32141e7b27314b5e96a4b3294b30e9b7d56c81df2ecbc4fe9726194b73a0d583086c7f446f58a07e04aa256c27a044a3e9e79d2b2209664995250524dcf664a600e2b6daa74accf1f812cc773e65454cf8ae9537d07f1bed501da43519c1445f99d88b42dc9a9859fd91b86c92282b5e9e98ac44a221814a2c657742fd26329b005dbbbc2774bcb4b8445b9a71a8a586653d151a3995d1427f5cf3541463a87d267154a87d8d52032478692eded41d09ae9a0594a19afdcbc8432920034512630e67cf0817f6664e48e4743052abd3d31488f36fbaa31130cb78d488f3b26ffda4e950e79f5bd685c48421b3b00bccd11b49896401eef7bae27c7aec4e35aae5c144681f2e1a9efaa10ca291eb7ee617b481e10277bafe2621be681f5ce1e63e544750f9dd7ba5bdfbbc4422a6935019ee29b158d1af9a62134a9d01e8712c635127e4c5e18e4834d4ca6534eb614b7ef85d6bc097c27bf1582b73d44b2a9e4418b85dd9522eae4b04f744654be477835b10b873b25dcfe81ce23f469668992eda444a64d3b969c5e6b5ac601c1d2fb72e1e733673fd0d5e9242de9fe2c1bb84fb7abb04a07fd2e16e79d204166c453b84f2447c6a5942e3972b120f74f1a2c8460954541b1e29d8ee993925f31a0c6b995ea37ced7844147181ebe8833cad846ae8203a2254a8cafdfa375ae2f215d4b6ab0c6c5d533e8d1a7ede3025422849c3a329a48bbb2ae95be3c16de4466b45a95d6c674e6fd7cd40c3d274e517d73695feca5062b461e11cc0e75c45f52cf9514f36dfe2c15d6deb6182a2ea28c9e8d1ace29dfe6570c4e997a756532a01b93a8ede801219aa64562a4c7290bde2dfb0f0ba1fa811a14c1feb850a1c6ab0fe0a290f388a5b8599ad59012269658c1f3fedec4d8142d87106aafe849ccdbaba701c0746de0aac3e3278bdabc96c3449dbf66eef739d1636e0278cad77503a8db302a0fafa30456952889135b93079ea40f80281e1de2f9d493e7a0550af511a1504e5e6e6946c219d04f5f68fedbd3b399bc06119d38fc444dad1cd6f3030fcecedc30ee50f9d4d2d2756ec6b50727b535a009e4820834de501d20379c0bd3f3c11fc1e69320fe15ffb9e26c85e8bfa856d54fb6a1b7f5b19515f2e19ea6a7a069dbd61717b688ea2fd1b672460af140153ded6b670187edc021d99045c635358829012cdb9c999584d4f5798e74d625a26bc7f13a9c8adcc4621e94305a3f210a47f26cf4a64208e9208a133046ca412cce925c2f549fe26e716650d90f42c09dd056de0027e9c98e96fca30338c5209cb917a1801eb41df1c4911524702d027e7a545ebe45eae212ea5a74f64faae64b98167cae94113c5117bfaf2802bb32665bfda563ddf96fe98b2387fb14c592ba7024f3bde72b39d15e6efcd907b471765e2c237c035b8f162c7f465023fda43448423e06b9e4d7831b8a6ef4fda44e75418a13ee7ceff2c296493f0a888204b31da1020e8b32128ec0b1fa496c77702daa13801715a84cec2dde30941fd822ca37d87217ea4bd037ff8d5dfebc2e5ecfaca07e0d583158947f2f89419a4f2a953c51baae37cee8c99331d543fd7914da9e98fe244d86c2620a6656de56ada7443556221049e6f4771496fade1b2c7795176d088c8c4926212bf302a4f49baa8ca0562db91ea16fea6bcc6ff4e49080123fb034cbbc486b7d70dfa3201b6da3f8836c5af0856a5ff64615da1193d2897776ae7b8932778e26b7e5e8dea00ac31e659aaf2d94eb08ced06bfcb40f4f782dd06e0023742cd8ebcdf8b447ebdfd18122427b336feaaadd5f96af41f9e0fa876fb67aa15b397c196a2031a1ef36fc44e3da119c21a4fa5799361de3a4616b3ce1d0184e4209ab323c2680a87507fd54407897bbf3ee30171b5304da2d85afb61190561a8091535fab645d378a822a564146b96b04743ba1bc0b33a7ba04cafe044bf8f8cfe741468456bec8bb22542f26ddebe4d64abde541c8219c696385c6c64bc953864897f1017f4a994a71550c5e78ff98edf6a36da50751b1e97d8de941680372666f879fb303c26785aa5fedeed8cd202faad7231aa01b9a220096836551a27df3ac72dfab935132bec72f16912fa8c887c4a20890931085da64a17e2ff1d3ff55d53fa90d10018c648a29d963ff0ca107cfd69b93fc303783c1fe8c634468a27bde048bbfdfc50167211adf356d2909c0924adbc27ea4b8c36a54247ac9a1ee9b2ec1d7bc3794aa2d751f3f1155c633f37fc14c42cfbc5b2f3a9737f8deade13775bb6ac7ee2f5fc6fc2a40b8726b0f3e2977ff9180dddda0f25c1355ba33ef80fadd86663caf6c4f407074cfae27a048f85fb8e8d52c8e6e88e49cda5255c62f8450e2250b43244f27c4ab8e7441b7e4caecc801481e03e8de27519a1d24209f2c29188c273138ca6b3e49a040da8ba49c2f8bfc1540e3a6072c6224bb5ee5cb5ecfe30d52ff2a538103166f6e4252aef6a534da051489911ce182ace808366a53795503f94aa7d764433a92da219548f823f82f4473b8138fdd162293d6c7409bbbaef94bcdf25bdc398fb3f4b20313106ee0885f6f0f072a7f4aaeb2ae5cfafc72866f345e4643d7b27d007f624ac74d816f528d3235c7b5fc8a033fddc85872812474312e9d2bff1dc1a5aaf27ecbeb3e4caf6da736ab75ff46911f574bcdb5909c1560e135e922e52d16b35085fbe4813c11a8c3d07511c0e0fc86786d3fcb0262e8864c1e5ca0f91dab1d03faa8cb59fd7f1508c12804ea27f78d4f483687639aa4c6306ace295b9a4d3cf843af515dbcd8b77d18dddc58538ea811f7c12804944ca2914fccf8ff8de265104c341c8669b02d6137f63cc0441659a3ba08f0f970aa8341247aad80d88a99342be185f6c7924c62180bc01909b82b8050df128217832ba6d1b43774ef749c626594269c85950f35cfa7afb0586e5a12ea57f71018d2d2bfc9867938b6a39ca61f889f40838877d99b2e7f26ca3c564e250d083393446ed37517d1c6e204633900bb93a03cd69ec5a60b9984f425b35f95a24212eb963ffda87c81ffb51ae17578fd398639cfe85809d13bb7293edb6ee4b3804b0e015f443d0c0a490a9c99e2c9d5c33103ed070a616c3b88208b6e652289345a0c0b50fe48d95b0d3e0918feb7c7ebd1d8172fda8914631593a82702419a7a592ceeb26bdce9a1dbc21e63954845a617b3502cebd833d26989c230378a0c4e6ad29600f8228e05d53fbc414ec66636a36b3649fc131920ee5ad930cd0866e93a829c8d3ac7476a3dcab44f40e9cf1df38f252fb32f9bfbe78b789f2104d99190f28de5bd9129f322996f823becf90cf9e0f023cfc352198e44862b05ca9d8f7238c4234dcd8dc414c4d21a404c83d60204fb5601235e9d584c87fa4c68cbfd2041b172365a42989337402be68215064f85b0bd0071eda2ab6dac3753d8ea06a72ea7ad8a54fe982e413bd0201e87fefabe461fbedbeb6432ab755338f3cfd37d28750055ea640725fda3bae980a834604ed145d6b4e5b63d99c1c6b34de0880034c227ce464b05a1c44151ea97617e3bb6f1153b210c96a598fff8fba1fe2aaab0c97163c266b86e2afd14ced11e9d1afc6716e490a80cdec085bda03394acec554626e189c08761903561b021455a813083bd75ec18c368f0942548a8eaed97948841ee123e20d130c44196be749880219597f843d31682b6f89414405acb4abd385dffa251c413035e713051ee62174844273cac31aac4981a3b76fed0c99c661c7f33fb89d1daea387288bb75f4125c243ca03f5a7a0a9e70e912072f0448e97e5f4daac63e984a690f79f4245a16bf0a7a5fbe16236e3568ee6afb3637679451083d3da4cd5dc690d272ac9a0501e1df2aa787eb9643d052d81c658c9a3196bebd7177f549d5b9dc2213f0d2f6748119f945b49ca7f97c5c9a4fbbb5e818c5738e34430ef68cc1bb47436448d9519e1aed3523ce99e1a2120fde61a0063ecb1681c6ef2f6a816d4dc28f54a85a408cb87ce3b93b0989b078a00d5aff7c4845375545377399f22f95b3ea5511300e3a44deaa17aad751267c7f5b96ec661869d4cb0011d71133cb47074bde6803bfbf85f379d6634b4a4cea318ae11d19af9e918df5d7d3cbc0d899c4f1f905d1f00fe697d26ee71d9934d0c482f488634c12bb5834432d4bdf17d3526360244eca1e75c4681dbf405c8669a209424712b2af4f059344f717ada4f9582fa8c3b935c0ad4aeb70956d3d9480f35f6ffc2425f5ef0fe8c2dc324a658761883d74664dff514410450de6a06b34dfb8abb4844e86ddda99cc4854c35b0780774f9ef17451d1103754355c1ebb3966187b509b71767e8ebb33ea006cb4d3514487ba89b9c22633503a4db57df9895209d3db25f4208f50ca724911658a21d4568d4fdfff0223e0b5504272b9e6bfe2f23343ee8af2a74a43c0b6b93d862b8023106a3e2e523cc5979d5c0aba3b40d0605439a7c5a4d3ed39c3e54568fb3d31430e51a430ef41c20c1bf4555bb4090dfd1aa62561155fde2fe927eb72450ece3c61375912c3af25d11f751347df2125d5565ef036cb1ef9a744169b74cde2e4359ad3ab47434954a7c30b0cf25e6dfc866baaa4cb455f8e231dcf484d44ed821e3b372d33e9f864396a52c3601301d7d8f149235cb8e824702186b27a1e532a950637d6e01e583fc0b17637e4e4b38987c5dba2be4e75dc9aea5ac39781c244ee63de800414559450df592f209eddf32cf1dde85c503704bbc071b084bd214c7baf5826441fa254bd3619d6b4e6bb56c384a5fb70d4921038614fd33191640731728a74b6c40de020eac0e7d0fbf4501216b27b5fcdef6ec6a0a663d79799011bf59e82ff982a10e1675e48fc26228037c6ee469dde8dc2b32523047678c7bb0e1bb61470ca488c64048814b951460bff50463678bf929fe9770614c50ecd4f8a92d9fec968026ec24d8edd2e5bf74b416c67f249e6aa00f843b5ed008068cf14e5d1c8bf54cded3fbb805767e900eadbeda1da23611c81b5b7d90fa7bffd329e8165221778bc33297f6033ea41d3adbbd9fef942b1aae4702b0a1a0f76061a6ced92d8f25df1c15c89f67ac1601e72a38857fb20c37646956ac6677013c4136e5b128a354fb8a8d1fe4868d3f5d6062a9dc5df9d40fa39c00bc8c0930e1010e29e116d61b9d5a723ba0563660e00584de65095f18eaa394ad829c63fa240e1fea4798c336502772b8b99895bf0b097b11f88751ec5985224c576a27506ec6a96e49ba61ee21c625dc7ce33deecf177c0d67a726ca4188492a7e98ad1978e0e0f5ad9f7d6c08863955c40c3e93618f0654334de8109ae7ba23498c87e8829df2bbd59a2481895c0f5831b4d298ad4d2829831386c38d89579d408ce71aa8b838676c4875815d3b78a42e4c65ef915e72e8e291cbd5ef65d1b814ad8f574e3546dc9ab90a4030b585aeb8c436876b966e82c9ba0d73e1ccb45c8d875235cbeeedf4a6c7153404419ed83130fd9dc50946fc90f07418be0aee1b182ed57cd0eca3b4f473f0338df5c35e0be80bc49a9856ab206a38c6e221e329d98329a6cb0f4e64a23b0f904a72b24c9410bbdac7ae58c7956ef0951ed66600842c93d3648b880168f6a392ad33f2e830934bfa00e94172ad1f58621ece710937b4a83a131d4625cdd1976ca8f784664d0ec0b27f79fdb7000086b3227b14744fa449445a5c4f39942331311b74785e621c859f8109e31b518ef5260ea265d38422f0645126365a66f47f167c497c4b515b51e212bd44111ba2c332b0a01ce9e7ad18a72a3c73a1c3edd30c481a9dc330e9876e30b0ed419f6dd8214c600ef4522b6019cfb2f7a7d0a2ad673d3034f2114691140cf8cdb36c1037d454d2eaf562efef18fb25904ab91f7ba24ac3c9dcf115a410f62a4f17e834a0d3475bb098c642f49faba930f846bdcae3281528ea3f9c7d6a38edc7833835a0c3584276099bc811706ae6f26eb00b65f296294395723feee7e3d7ef5555cd4e2192e537da8f438d9e1b8d73904c904331da1871f04b995ed57746c0227e564a9233707b3650bbc232b831ff14acb515f4a8344cd48abd87ed2a740633dd474cf1aa26777c587ec813e490f4142fee26b2368186334e331de3b82c35085b24d31e5c51f970623b9ae91ca8541a928d62fcde38903bf5bfea6e7bf8fa4a8ec2f184f699a4b413081a4f8ec214fe69133fe265e643d6d07f1b684805d1d49210d742788848d96b4e3da0a8a5b0d55f96017180f96617af54df2a9c0e30bea63fe0bef953483175993d578ef6f0c54687742b249e25631ad67a307e1cd425a22cb5408ea72d286a705b2b9fa08b36aa409a51555e031791acbc6134f3c08e06ea74e70a5cb5d47f64a07073407771e182792a17eeac25d7de4ba0916b536d41fc9d888a596d617320b2368318a3d69037864d4d1c3867b8da5c9b435da1dda1b0aac539618b8d81105784a11cc69bb4cc34e0e553cb381313ee0189e97411cf182999ed041e0fdcf35a9606c77a4345c72fd500706bc45467c9c10911ab0beb063fb020ea632d2039c638bf180f7a3788c4011299d7f6bb12f02fb850045b20ad8e60a9f0d5ca5a48d95b358598b506a49125bd07d969414a5a0aa52f675994d48688588ac498e7f04534c96032aaa541503aa5d47a980844e269433987dfd031804b9ef5c223cefab1ff4017b82b64b42164e9928a6af8e1aa2535c51b876cbb6840dba2d6f8c22e01e821ad3b8c4e3d5b2982482e485d131b26df2ee67e2004c06134e6687acdfd4041bd181301333f5dfec4f55ccfcdc3cbf4af05d60ab564805130e5efba0c5c8451971b31d39f66030c1c7962a8f9b07494e8476b61e9054b73484057e1a6ea535830bf54429a5055b46ed8b5745285aa4fc400bc17d626a46e7d33f1035104b8865b9c5cdedd5f3c2642b476a0eb491a946e2035650492a9ff02fc871bceb620886daa1222aa948e7a8de66e28563feae31863166e0c33e6d19d5e8747f2080d6482b61a3cb0377c8ef1ba1b0692dbb6d9c9102ccb3017647f126fead8755d6afd00183fa6e55b392a819a8f6a9c831651ec12988a043be46707a84b78f92072a82240f9dfc8eda434910c035109e05cc27a093e80c48108af236617e48e0c9ee2125874d43d36651cdbcb97c1a4666f04cd8469cbc9fc7a0d31c8bcaf03e2c8f38c603ae58beb18d8a7003834c5fed31e76c70de8141158ece895968e0b5aa3f8055c412fca42087b0eac8d17182bd8e714beb71632385e8050f4d18b31df9f1339d962f3c51653fb82e81cffb29561da3ea1a848724b75ccbb52ea03eeca5e3c166bd04aeef155d8f663722fb079586c75836b9df7dc79757f82dcd557caeb9ef8d1cbd904ca58c6248cedcef3bbbbf29e1d4dfa5146d4c9bdf95b6e546d3043b66507839cb50b07ae66df97027740ab070fcda3015ee91beab8464a85564f2addeba0d5a6cdfb44a1eec46eff3c1b562c44b6ee30183925fce5aa5f0bf69fc372f63e5584d895d7fef3e9af14b2882d940d90fe35f193a201f3c601d8ff73d533bb155948cbbde86554e389d857f345c9d56ba1827a8af9a560a977640fff78fc8d4681d222302d0a40a3406911983605a255a0b4084c9b02d12a505a04a6cd1418a06dd58c48d5103b60fb07eae5ad069fdabfd6839eeabfd5c14ef7b7d5e053fbd77ad053fd9756ef60a7f603debdfe541a92cbe7a4bc9c76196e524710b292b63a9c830efe4cf60453ddf3c6146019a7d1ffab8537481316d7c13c4dd854644e6747d7d4ed39afe0ba353f1148c234b86e051bb17bf2f09bb770f5eb97e05485483a3b61f3374be87c507759461f0fd3dd9ea24eac4f18295862bad948ccb6aa7b5203bac9ec5775c35a73e3d65d9a5249f46aafa3756c115904a296f924c084a56ee8f78728c6ff5af71d55ab56ebcf277c0505d41cf670519a40426ebc5c0219c158a2ed8aba533c976371e534efc417b55d646532acb50e1e45a6beea232b5c8d467cb229c082839f41cbcca5ae872c052d9cd170f793f5bcacf97d49222dd8e5d0eeb3a5714091bf94d866af2f1766dc40de803c66aa198d6161893d691345ba9b8e06b86eab80ffe6198005a720b35416416a46c3c125cea8f87641ff0c4df8f085b3a9e3bec34d3834cc82bb66347cb765a8184f8439c010cec3f80b19425e66eac25cc1fa5e239c9fa1d992a7a1fa8fe65ee1611a00de10dab228839c746db558d6984066349a0f013e5e0a076843f7f0ee5f4899d12888869a3cd8a352337fc51e8956339aa5d24e412fbe1c0961cdbbc6b69cae8a2c2ec134e7d255c33abd01df53c83ee18d0c3a26d38c4302522dde4afd3db825464633c25449b864e2aea0d117e528cfc1ebfe7c8aeaab57f6d0a0762a773e8ba5aa255c4dd44343b95f4424144573354fcc4904b7ace4310b385ad7f229cfd9943282916dfae8c848784680cec5f085409dac8285c12027061cb289833890831c9ec31cec409303ee5594ac1850d500308a9554586e6485a7b6196afd4365e223abc83236156f892f1855cc2ead6282379543791f6793d291036028830cc1abdf851b1a7285791b4714a8b4f18eb6a51661dcf4130115ed8a3b7ca884ac02dfcf0d18d6721d42a0ad8d3fb689b2ae02e0b6bd3bdbce727e51b0dee61eda75998e2060d5ee6fd3be8497db6f55bf3a939122c619fe16cc94f2a14767d61e6bc2cdfb79dd5aba1581ea62c352b4bae8de90c36be90a16e33198caa0d0024bf482ac5252d39d3aa33bcdf7ea2e7a58042c6dd8acac3c1671914440a33db5309f053114f06bcc8692aea802397be7cf79043e346ff36917fcc5d1676dc1440b320ab2823a7fdb8af7ad993c7316d2149c7900b4377f09e8734e2ca04d30a5518eb9f6f219a56b421e3202a683d41be48510ad104797341683b420745cd4dfb1d59c8f3a995a872f6418a6a742cf1f0ee7897de661a19394c4c5836c8e07e4cc8af387bb14818389129f9aa63c2078456f219c732e34f23ec458d939f39f5fcf641d28dc5d25a9a78b42ccf1277ddda99f04d574c352d5ddd0391e6320293ba963bfc8f39aa0098e2011bca75542e34e733b98e282116c88a031aa58d480ea2a4a797e1869b6ec59c37d82ba33a0928bce2ce5d616b8c85fc11ea3cb18c45a6906b42f66c39b3a4de65564e312d990f7ddea484ea29ffdc1a727ac94f4b613f60773725a0a301372cf44678ef79c990a4b892a773d0f91ae33f9848f756fbc786ca48ea409b9b39370fccacf02392baf108171810ea6f29af1593c577470bd06eac13959efbcd58a746385a2b9a4a028894aff89107d36c84e473b15f0499068456debc4e84c3addb7a20e5cfdc31947a37d9981c3d2542560eb26f31144f6a28909cec6908e1fde488900fd5444585ab7461c235a015c864c4a033f274992202cca4292bb0e90929b73b824175dd007ea611104b74b7aaebeb93848c6c3f82a1ebf8cbe1abd040789b43570f88b34a5cc66697601a088931fcbcc38718c043b6dc99f9dc5c436776f5a59c480710fcc18e38f1ec39a539adcdd4526f4a4bd7baf52b02fa7cc1f49411e92621226271d19e6c1403126f2d80778f211b0fdb25cd6b8106153b2cf6d385ee51b66f7ceb13656c77acadcd343ba1b251c0724a4d052d2d200cb0f02365511eaf4c26fab21f0366fa939d7a70484b2782d77a3e6182630be3c82dd3f3404a2bce723f7456332b7dd77d89313d5b7939f0756a279a26394d1e0c8882cc482f8ba84cd48641af32deeb1325c3c93693558807075b9155819df173136c8c6db57a867a0130969da7bafde5bf0bab3493588ebda927f7b3d0b3fe490bdecc5f7b30a45408d0cb678c86e0a7daf04bd1c0cec0629b98ae1f5205f5a1f4c84e9078eec32bf8d95e083d03a4c1c93c2acb61425cb887fe6a14d952fb0de2d0fa1f14cb250d188da26a5fc668d8a72f4de9d236152f253dfeb6882b4b1618d628522b22b83efc35edbe7460a917117e4d4579384e24b0d4e93721429cecc27f3379b56f43485d8966f8abd9e04bacfe060543b6da6f4bab1cbc922e02f203bf7ec4ae43f0965ed1813a840832210bae96f686e6421fa83c16b218f2189da145266b36d84b58dea1769a7146bf7b33e84cf0278feb53743e0d6e9a688f27f0b8ab5ffe96269d8635affe2e4a2cbae9336b0fc515e7fbf1919a09fbc0ed0ddc9a0347c78d0a315da8ec8ebd089cdd6192f2b2ed8f54eb9dd0823fb0aab64891e0762a0cf48f59001e3fa75a3d250485bab5cbcb645aef14de32bb287123e87287834c5dd787bb1ab4111c2b1257df53bbdbc41798a0591f93a3caddf544d4f82dd92e7e4eccf468a0d7a245c26f180286d9663af7d30e4300aaaff050114628e5466a045da3cc2af3b836775507e35588371e788d296fcf2a1086996408e2c88bc4dfe2d37fbfb69ee2231ebaae55638eb6ddcedc3d24d869f2e6929dc6c85f56a225143c60d1a85185324cec5073c9690bc8f7bb7b46424a202a798207278a3d7b05ba8d18961d213689d7836e837a1dba42208abb70530bbb795f22765a3f43c2a480f5e7457b9002592b0595270abd73e059a288852f0a5203483306d13ddc2c1827381e9559f1d28a806228ff8f0773403fa6d521460d7aa9d85e5b0dbd1e3246377bb763a874fb455fa2ff1ba9894974f0276b162a5dee1c926f6e0866919fd7ee89bc318dd61b24584f0d1119cd3d6ae6d37e468a50d791b5717e5a229b7643b9c2b9dc98cef6321aaa5043158f52909376ca689f040d8ab81d8f3a0d6fe3849bd3c28bb86c3dcee38316fe6fbe9cead90afc74c1ae9a130fc636d5970e30a4d4b660e1d735132b18b7e8a7c95bde3aec210ab0c6312c4515499b2fa089c9f0ca2d85c2d77e95ef7d9e7c16a25846f02809977641b0fcf12f6d86d2ec28a7c018b4bbe03afcee1e2a80e3de1791a01c3080ee90760482654e23d9ffed0033d6a220d7880b5005c90faeb62c973469f1f7ef461f747acf3cae992b754921bf6e546effeadca0aafb22d1cdefeb01eeead0740e474bee26939326a408935719f9063690c03c63f3a12853487b86c419c9031f358217e13e0af01859eac93f882b71fbc893fdbe147269a9699b2f655c1a541475aea68cd6828771ca141f8bcea92893638f5c650d30c2b06e5379dde510839a715a558783509b2c029f90bd077198adc33080ef03a577ea6ee426736d0b08c09819b5c043e40d2305ce5c9c6368a90b08bbb660d5f25081695c31ea4883cff5bfd892a68225b0dd583b95d92fb8a54c2e0d6c35a57cb0f9338b8667e9014e9d069ee1c58a5b5b2b8471c583348ff985d69ab19b8d374eec02c5c08e64e651a3e819adbab98f004fedadaabdc3a06b8726e6f339d281fdc1dc68e4248b9aaf278320c3e6d6b9893cc1273a1af87485723c627624338e4052269d29432702f98e1c11ef19681158bf296ae6b5d163d2919ce6590961063b36d4b04ca238181154b767c91943f2a3893511a2244d5f3b7083928c9ac995ce22c21d6fd89059525d5a98771541d9d5825d389c38ec759f611f46940417d77af4c97cb5a7555d9ca9069e4a29e8899d31911838cfef603c6a42e6655188c404025343ed17e097dcf63b0ec9eb068cb2bebebafe9abc14efcdc385e0e540a13539ac4637041b044a035f26c12c093060f143eea032c52b3921e0937893810feb7522d757475007fe05baae2fcceb29cd436af3159ac1e8d579848680b2c2b6384a207083bfef6d1f4eab510f98b6b204d93c66e224ffb7328b48ba19dd94d93f7403ac7e16145e2181851b0532ae901f3d7c5cec19c27d804c35140713968dd4a31f7086e0a4f626bf7346bd01b608e7a942b7303f4d22006e456e37eb24720525cee70fbc3251e9eeb62dcdc15f16c65b683f03cdfa1f78c274cca73069808578724e34e063dc1e6a29bbd0b38a7658e46e9cb74e01ec836aef121a624c36fdef4eafda4c6444ac2d327b57481f38d5feff5066edf69e3891e85a6e57a8879e50db8ba88e80a6f43b62c08da564696ba30e4045683bd78642b29129b7e01bf24143f13d9728848f6947af7220c1d59c7665d8f5c754bdf2c687702da3a4893e8aa43fca3a490a6a85bb124599df75e992b636a57a67eda11e9fb4def6c1a293be3a9b048d6367c0c80fc264404ccb7fd8484870fb823305a5d4ecca0ac5b7d4c227aeb5d74765dff6a93c9775e43064d623958ca9b9443e77787e06284e2b3f699ff71aac7ef9875ac06e7a2552c0cbda2a54d415948824b04afc23cd267951a69925a6e372cad3f714e25d9b3e7025991ef24cdc8c1af9941ebf9ea2e626414607c796141498c3a41ba764fc99d23435557044c03c014606cfba55abd2c3080ef95ff5d05d12fa505b015a287f7b0b5ef04677ccad90532137a533b880a313fe058ea93036b0211224a0feb23d73d35244b72da9b212a219139b9a6bff776b9107be96b1f914906273e7b07f07abb7185028dd1329358d781acef92994ba220878bad9a58e00acb404a5d7edd12b3c6088a305dba42c9b00f6f2b27bfced10184c1268a9b8a00231cc2058ab3b74cef76ee63112f9b48a8f00e9a09a9744fe3866a2de774bb80afd90b23a9d2b216fe7e73880fd915a1b36fa7af6417794995c3068fc8deec741c617a5038e240634461e40d3ba410af7eda16750373b7c38e16d7255ed383467d0fd5fc6dcd0c3a15c869a5a5d96c5e9df405c19b6bcdc963f94c5d7132f6f70aa923e1463cf9c65610cedce8040c57f329a46eb5e91b1b1818ec241afdd6f9945619ec764e583e3998420ca05b74425d4ee639c1ce696e9b1f6f3a85fbdf92a2bcd6911bfc6d3951e13c31a67946331e42811ae4d0d3c4100f819782864d1e07444be5cb1652aa4c7a50ec56d19723cf96eeecb13ecaeae3e30fafe98c9f221b71cb56d48bd62fc67421090cafcb8176980bc8812f8bad887a66ddfe0fb33316090cc2353bb0f9382e5c225107606462ff54d213d7633b222f8e1a8eda2d9bceac899a2cde00c09f515cc83e7f0d9d9b3373a693e845a0eef5eb9cace0037b59a05d92623b30c7d0ad05bcd04e96f15c667b3b2601d6a7524810bf6f5ddb7ae5a786c38954ee6321cc745ee5d0129e62419beaa82b3ea429770688ba071950d765c1cbca0a433682c0b9f0eb68ea6bf4dc66e6711fe84c4d55aae4c1afcdf3a87f9440e915e93f5f11248b0b65a96859fa6dcc338e40e56ddbce3f2afa8c9a405500f62c225d0896b33b5f65a11693e72110821d55542fd85db6601ec26ec188e1ef34e66edf0b62c948ea6cd62177fcf3d324e414d4f04a13ce2caf1b1ab98d82ca565ff5bf81403ea01565c214cb7ac78a902544dde490066e8d358b4f19d8929c1c29b6aae46e4167bac2f0df3d94c9569eac52ec1a037c6fa21744e8eb524652a8606ed2fc6f3f412c518a51437e3c99cd23e2cdab2cbb712ff6b4b7edb32b56d7186fdc512ce991f1e62052063eafc7c357b61efc4b1eeb66e399f1c6050568b7818a2976b4ed3b72cb028134c64b72d727ba658bc2a06234ef0ec467c75ce860aaa403a4a6c3dec5459b9fdb98bc7285e0adbc9ca1a78ee4353504f489ca8e8e10d64cbda324313976c0a722c68875621c6c6bc564ff641148afa5124697bd79e4903181717605f64c7494c0b3b0b09425c5ad7429a2d619f13e126e6871075f38e4d9e1a304c1f2e85615f1d95e45595dc92ae21151f277acd633ab6b7e26d45b62d565f1db29851b9ba125453ade0355112b404dae7592c8e06a3481fb120c898031942eea6806273941425048a6998391eacc7f855273ebcd7c7004e1bc1ba5f62600e424ca477223fc5615f1145753f87efe3e79f1c44e492692e90b865c1fdf895ae26bf9da36bcb58a6d55114f7295fa70ba18455839f6c74ea96ac8d1eb76c2ffa2d952fffdc1e9268d4189f39b1470becc349853302fdda49728a22a622bb94a37be3b362cc3dd0d3f6e9c454b30a5a112849768567460609734a62fc4f00756ea905d76d1c00a80e5b1bb7a85f492381b4d6d3fc7158e250a09432d2c74e60cbd0c597ada5eb783c35bec1ea47d4606133d51833281ff542dd555d6bedbdbc43c4a307c1f0b1fc43dfd3926a2ae2d0bfba00c2edf857f1289836679268c7d17fc80ee8cc8a1173502c823788255e956a1c440bd0b8fa38abd3bc3841fa940ddc67b3936a348f16153207b1617a1cf2b768a1a276002d1bb960690c4aed914a7de260c9101201aa2cf32a8c702d8863f7f9797e469b3e70bcb925d72a10e6aa66102a064bf873011a57e35ecef96641131bbbd72f7de0ca80481338b2351780426aa1e80935147d85c4e347649e7b6aecc1d4d21aa62adb4eaeaac8879b432445109656b4d846212235d6bb8a39881bada511ae2b223228570207bb6cd7105c16dcb3bc5dbf3952ddc73501f67756abcb320fdbad11112c543d81adc36903ae4f6c9220ed53c78e4d149c265069cf68854a2314a60c06bd9f6846fa0277d77587120525e885f2979393f315e9a4cae402904fc93816016f14c276509a9494b43c6862d9083c2b45c5d8b1629464280527e0e2777ae09af368cc7320bb44847e07c6063eb612fc32226ec87d511c950a0adbd3e53bac7088e561f1a8f0ccabea2a261115b70afea027c3bbaeb9e23ad143b1a02b343c69d4d7217dee1bc35bca6edd3ca19cc828ff895ba68aee5c60e8d4b46cd8873f0a941c8671ecef72e70244df6cab5d534a8f2b0e3f2a85ad0326c552b2f69d27833c07ac05eeb7d18fc1ff20098522b45b21327e45f1081aa28eba6b062a9984252dd38e024e3528ba65088c6bf63c8fcd4b9ce9d7331a5cf881cb36722b189792196b16bc18f0905de4fcadfa356d8965b587385b9a95ef18e9584139261ed8246505e249e275e83964d6bb47b559e83566157cc261b71baf1626d4592c44c023eceb504287604428a3b4f2ed0b43abf27bf95ba909d28210772a263dabeaf135ba1d888af4cd70160853ec0fed236e9bd3d1aa0f5e9439a957a6c2326a2cc045afd40dde0bb275f3e72c11f04436ebf0a3d3c52e02018772243239b4dcec346ee3c21b685f7e7627130190bbee0883f111786b18f9457aeb5a3726ffe5c5108fcacea81e6f7862f7371bc07da83960222075c470a13d8a2a7674824ac8e80c8f541e266cdc691a32fc90519123ff3c49d5c66f97313331d47a5328fddfa09602509863444ccc897cfb9ce3b4e90aea7545475eff3d2b11e9665fbbac6a1dd68b94b9dc5fc59cfbe8210d4cc9f7d8040bc29b69c21963927e01c0756217e5a1a4eb313a612e34d7d5a69f281eb1047babea5d13a6e092c082791cdd39565e875c37cf95751e91b879b6be7b036a9291595b4d34726075f8f60edb7ba00eeb9c12da8620bbd3134dcec23b75c4407b28674258e3e0c9d4122eb62f38f692566d1698cfe26045c4c1f203ff008d47eb7618a155a04684e428e091b346be101120de4a7415b3478d32e6509da11a7b4865e741ae2bb17a68923d48658558be86c3069659f19dd89ca9f07e8b10ff38611d84a21d896056d4f7b803ebefe8e1acf8ab1c265bf95ffda69b2cdb2bc0f79570e3568cb8e689f10331dfc65f80b7698dd4b53534b65bf90beb56a61c2db6b949b7a8d15f5596cd859029507372af86a2e1e47891eca92040f0b09b66e392ba40d0225120139de519c9c3a67d20cdf37578848da37ff11217fd09235d3924898febb9b19088d6a92ec9dd91193624a229e84d0deea31f785d11328216674950eb20d3abb85a93d03a3fe31378984a93542ea0e6193f2ce61c9f47ffbc7e920642b644aa8e895120133082b6001420894e31fbb7e7b4df4e2ef716d5a1c6cc7d76cabd7bdc41528105553d5cd0030cce23b8ec48413d137fe4194f0456d173477dc52c04b67cbbe883077a941f45481dc3e8bfd1b46096a374810e662c1f653a82ce7307ff6dd403bad546e9f8dcbb395a83cc730293c47e9344b57cd0d883903859b084189bd9730c30c2ea099f92088f1d970e6e76029cff2b90a29c72e7a62fdb637a53d56928797a026d4d457b47a56d1ca5f10b2781d312f4ae423c3c049d5045202f94c869b8bcb856aa552a27300e7620270ed7b4a33e986f5c197e2972c3a99805ae8dd2b8c4ee8f0be833504f5bfb4e1713c93c68b33c198090da93d26868b1d8ab0ec2bd06b6551ded6625a441f735492ddd30142a58aaee55f8f750ffd150795afc7626fdc7a2006a28321be4dde644ac3c650ca6867debed146d6618c65872ddbbd49edfa1aa3439920451f28e26da147013dbcf4018a5ef1465c404cca4d1bbaba2fc55db04ecd56cdd64ea2a31d81b4352d2b4bd52dd5dbf6a572d5079168fdec08eb9742bfe2d331e4a31c03f61d99279a41e3c2fb3ae2b558780fe0b5b45e6e364b1d24b906cc8d280234b196483689eceedeb40335041e045f04f962fdc272bef09bb677e95dd65ed05f2f68fad52ffca69f745ea7f3ab574f04e91efda0f8923fc29e9d47fffa5f6752ad7ecdb9f1f0a217397e2ffb5ab5e74515eba0b8b10e8a3bd3b556afd73d9f9ac7f4f7a90effe93ffd88df985ff5f4527f7af99d299d6f9ddad681e7f8fa306d39328ce936d56ece29edd39cdfe8a3baeac3dd74ed5fbf26fdb2dcce3ab81d493d512f28badc58bfe675aa03bb172479ec6f4fdf91f88dfbd48349ec4df780c94172fb7b4172e3a79f7d4e90dcf74d14fb289a73ce39e7083e748ab986a4ad6d7f7f23178eec0c368cb5a0b4632d18c5a6bc89b57065e3adb5d9ce82365291d3c0743275f1e3fbe079b1ca12632dcbb2ec658237cb34cdc5fbcbfdd2de74dd0b8a3caed65e94ee4134584a29a35743fb679e7ca965a6e57b41516e4f6a4beb90d0081bd8d43d11e884b3bdfa41f1a5f3236cd97972d38f4b7e83be9d6eb65781481ed9534fea6c9d36bb9241938df02419663b040382cbab8e7def87f5d7f99fe8d92410248dcf18b5e9ce73a1336d7afbd507cf0bede5d58ed6c158077dc9632ee94c4aefc557fb3a8fda7858171aa537bda3bd2c57ba48aefcbd171a45ff94e9ed37a7f96afa2e34d5f2573b52502e7fd3b61cd668638d564dca9752c79b4b1d50997aaeb34f4fef5dfde8ed482f823b7bcec982366eea6b481a20b4a9b1611b6fc7a568b448eae99e6b67996759bfe86b9fbd37b5c9c66f78be6d8ef65c352818179a192f7a74a83ae817d5ec67da677ab2c93a2f286ed73cf7edd707418aa4317772ce59ceeceb8f195b7befc70ccfb56d50dcda96ea565086ffdd3ad0fe95edc643dbda7b92a4e102c2f09f4e06469e3f66ecec51dabbd9d79f6cfc86a6bd206db689d67d3bb829a5d7eac9c60f50e9ca9f0a866e3401970bccbdae1d5c3bb8a4308942254c11528e8ec4a0d41045cac4e0823da318b9a2f67cc2d99188f8d837286ea06859754e444a296fd500063be2a8f46bfc2a9fca0c437e10fff82a12729452ce0f3265340245caf6320b59b6e398ff5cf69c176a2a41863625920f1822ca2a12f29d1007d28b7e10aa3580c17614d0ab4da6fbd794dd87f0830c41fd9c8c34fe94523e9c4186e25f7f68d2f79afe9afe9afe5ed35fd35f9389be35cdf970cb26fd204399571cfe52879c0c724a29a564e1db525ec9d930c6829088c3d011269ad0c046891a2bcae0a0638ea6dc232c4a2c3d2a81e9c884eca8487ef2e6f464496bb161ec488aac0817c39855124a29a54faaf03c0993e48912ad8615c40e1715141d26392b18d956201a82e5ca7a64c376b161ec688ca6c6370e4b1186d127e2d8f1902b2cbbd7ccf3e9269fba7ec55f49ad43fed096b1e3d1f1435b763c22962ff972c697dcf4988513374e927032705b9c187132847257e6e6a4034670b819b2f7315a714a6de6eb38e411a3a56df481941dc7705eb207d15c40fefce460c8d501b15543f6fee584c0105783af58ac2c73b25cae17ee7559b162c58a9519d7deb7f7decf4cf8b3b75d662d0b6734cb3217ac2e6a4df3efd32e03acfdb25f7138ddf535ae4c86b1457545b25a11b1cab12a6eb6dd30868548ec8a1b2c4a62d11527b6b7da7792d9327665cc9672d28f41727251646f4584e44c62578e368c59d1b213b061cc4a914de94522fbdbab5f7b41920691e74f7db9359942a50f7af8f01bbea915a23dffca101846d45fa880ed3d0b9ed58ae19ae1e54ded3d4d144a4ad9c258eb6cdd19dd9b8b2debbfb0197eb85d1cb39bcd6da5d6c5ad75fe84a6652e4d96cf4191e79bea8d2fa58d319b3d06f69ddbaf76fd7bba1d47b3dbfd58ed1aadd541b34e96c431f280e57346640f82f140d2c8d2d68772bea3a01d451f29d1077fccb19d30fc542a954aa572ce5ace39eb1b5ba9d752a9eef4a8d49f724efdc94fa7134cfd4ff4a71bd0dddeaa1deb53dd67edf564ed71fea8cee16b2fa772e7cdd8f971d4b40ec696f65be70dededd4f9a734d4ff440837aa0b8afba4fd8fc45a8da21d7f29c3f71c85ec81b63a726672148a30e27b8e7f63cb2f94a14d91a1c4493085b12a6fbf6218370f7e36d4417c4b20f3ed7710820e530840fccca6ff23e396efb966bdec177ba8049b3e103cb3a9b69e7f90a17dffdad71eef74f4753afad837d9b560b2edf3ec906fdfc5db1caba71d7c6e668f7adf78dc2cdbc6908e72ac5e76aaa7dde931dc80646c3c2c7ea9bd21bc7560bfc5dfaec5a7ba17fed4a33a0f3fea733a9ed5c3adf333ffec7856bff3709b5cbca9e3d981dfc5e377a179561a6868e73ccf8e9dcfbff3f9f3c3ed83218ee3c8d77fb175707fa7cb799d2ee7559db7fd7dae7ba51e6edf78d816dac37faf64bdee678deabcfbf5d4bd8636d6f6afe6019a943ece3a99b5c9ee07d59d7d08190f47b3a506c2c186f701435eb6d410c6b86fb708ccb77303e21b686e10caf04e335c7072dc3056a5cdf687ff801814beeca4969e100f10fcfd1323842d38a529e6d8c7bdd2faf051743b56cee66c713e647fcfe54a02fa7024b2e3e0156af66f189342654a99edda303625caaea2b40bb0610c2987ed464b2ea594527bdcd49e6bb713ee36b783c0dd31a08cf84116933110a31a1f92c68c30e04f78d1b4cabc528c35a6ec321218b2a287b5c7daf863441fd45356f449ddcff47334ae88ec41a50fa28f174437f73adcd3ef91b3f354fb9ccf79fa1ee8742bae7b21b2f6706f3cb457e9e81b5bd447a5b5d8e280c8706bdce77430b6763aed539d86d2fed469b7f3f0cbbf9af6f0d76cb58626274e22d9a3c961f6192074ff9126d36337c90e08c4dad465c0b5bd0ff6cbf42ffbd7a43dd746bdd3c83452ad54f71d97444675598338fe35bdf5561b636df22dce2a2b0210d142112bdf805c4a29a5942ecffca8917314a6841f357e08a23388ce68e68718638c510649332f448890208a419c38716af61e3e753d1b4aaeb318bbaed3ffd6f7e17f4d3fc2f6ce93bbe2bfd77dbeac2f03954e19bd8cf9f9f129b40141d0e533934215375c4260c21327d67cd9115dc4d8d0040b538448d3040d669e4822a5892e492c79b2fab22b9723d50a2ed50aae2627a48941cc902f0a11402c41e6c4c2ce6088d920021b94d850a4490fdf1a2744c22c817284e60c172cf286312e52708162ce3955297c8410f6009b13b8012651a0c9192523592cc645cb1625551325b88051156dd1b20569c992d816a2580d2aa83e30b73019a24a92b3616c8b0a5b4ed041091224b0d290a48508d511a844864d155ebaa4817d6668505aa22a12a3c1cdae1bc66860b3841115111511974b25c4758118222d463bdb30a6454c0c5cb28c5111c5a034244e143374c0fe5de1f75a5b9a30447dec84907a6818c7c79fe91ea7975ac83fea9c250cfbf67b62f34d5a3e8e3bafa3fa9d57bd10520fcefde7f7f09ff3fd33dd63b5d21f6821a41e3aef5ac8b5a375ecbb8612864a0b69a19730ec73dab4759a1642c2580b216599ee81fa9316f22209c33eeafd5d6b5a080409c37e7c8c75d4422e44c2b08f75517cd742510b091142eaa1bd6ba10b4818f6b517fafc108743342d1ee2b0390f71f8ffc8169d10520ffc395ac871e0477daa13417bd3e74e04a41e9a16c1a8274caedaaf4818f67b62598b2084240188839ab408f4b5f82e43920c4a9bbe04208efa2cab5f8e1a92d1b690451659b2d40d6359a8dc0d63598ef286b12c39449c6304ea3869a65b89688021bd81bbc9b2dceae3ad13d6885d907f941d100e6274089bf8f022eca316514a67d19c45d48be89c718e669b4d8db814e4d986c26254b2c45c4061c65c9a6c625998c4603832dbcc361c175beedbd33a4e2967ef695b732fb823b2163788cfada8e65bdb5e02f1373dd479beb3a7f6efd03675b0ccce3a48e514d6faf8fceea7fe95577f08c9044d787dc60db2ff2cebae84c13931df1d4acb159183eecc5e7e0cfff952fe4a88ec0fe97b575fe9fa66a0648528b5519337e0096df9208cc9be835cdbb5e3628bbe101a39ce29bb9e959aecbd522c83e18cac79e0eeeefe3344d6fe47ca1a5b834af7c61f0760edad56ff2daa3dd7a6d33ffea8b123e728cc375d3b132532e76db2b75ac5d56a75a592b15fe5cf8e7ead376eefecc3ad5aafbe5f535fad45232fbcd9f36b7dfb3aa00ddf5edfca7007fc9ca00d8736dcb32feb337fb61c403cceeca15f1fb7e8eeb3fe7670f7449fbab4e9f3ecb85f9f7b9af3b5f3ee7bf54ff676f755efddaf2f28d9f42d16381bffcdf9dbe5ef893e55bbbcf42fde34d6b6010dedd4f7449ff973fbd9f1e47c8b87dbc4bdaaf3ea4beef1739a27e77976b4d87ebb2f5f27f5391dd0d04e699dee762f94d69aaef5356823c8bdf75e7b41d8d7de0bec7bedfcfa38ab2f37a0baa3fefc0965d01c3aa00c7fb847f0212ede21775ecd94fd9c530b9a306972803dea70a297a3739aa63e9d5efbc9423ddc4e287aa2f444a10c7f8d4b833b3837dd80ee5bf939ea9cfa076b478f961334214f2ea178414d7e947d6f6e586acff680c9a8cf7ecb911f6ea8ec01dc281d2477c0add33a53bed6522967bc4328974adc7bef0dac1d2a3858c5129f0b26c40b4cac143b20718411951fe20c218109489c70302a5f0a1f54d2600e878701c06012ae0526f91229e8e15bc167016463c2978295f95a58a1c18a6fcbb0244ec4c467441361a019d81043d67c18c0aaa8014c083e2f015601ab32a508163d54f9818e2049122762be0ef6c577f8a00f6e94d8f00eac19c19700d8149f9aaf85cf00302fea81b9142b3e00c0e07c10cc093ea46f07a6037331f08a34377c26f8bc042c180c2207147c2a1cf151d887e48bf3050096826fcb9723c3b6602898c7de48017130c11c862c20141f38810f4a3817a883d11468c27c4b1f0d1730193e173e1f05080076f4f1a8606dca7c453cf15df1043a41840314cc7c59602cb80d513e349f9ff0a5f96e6c006301b033df99158c518106d5ca77e44c60f14125ba7c1a6602ffa10858950f0a11e753e34478f94e3011c0215c8a183e203e2f4200d80c2a3e27c10ff794430a2028c104244c8688c06889d10b58ecab71009891af003027de04de50e1130ee6c81b32396c71a288112adcf0396183c3b7822d7d12f686872f068c8a249660f9943e103ccc8f7cf1270a282e9ce952c607129ae43044911112300706c07c06272ecc498046f894232011eea6890f896fcc77418561984301e553c1e7818f0c105348141306b1f87a78ebd4566badb5d65a8b421a2373ce5c6d0adbda31dbda67d93ce743fd5f629d28e090c6c8131d3e04c050f091f9b2c800e62b04f1c12f9e9415418608082078d832460c3da1c8e7c4a3806daeec5891ed54f1a9d6a8d6a4a88871c7083d6a67a2c9d2f65c316012f4a909b12647a8090ebd5cf5eaa6b0e415fda73ae6d314b2bf4bf9f429109a1fa6abaea84c90a34f36c2dd1d42c881ed7bd61dbab8cae16974b56ae5f83c555fbd92aa20f21a3c800c2716405e45f7a803907d019895e1acb36a426400640a01ad4da826776f3090efc720c3798105b8271321b9063825464a29654b0545cfb35a3cd9e557e0172e62ec8b980867cfd5a2c8b552f7250c59348860cc406ad0697d8f524a554b59ca28bffaab962aadd47f683bca1a56ac9d18e16aa5d303abf20c5a9f4a9a6717828c71cbabb94c8494d9f936b344481e6dd53c32b6c8444893bec6306e40c3a7cec9d21b80216bfe7d95103995ed132151d6cd90b5da94b2628b7e4f6ca9a0c82bf72e04dff3e7947246942b12c140be4f3c966a2a2bd9bb9bcad885e09b3ed573ca2d1321650f95a04ffd4ccb4c1ddd1f81c09aea4b8e1f7fc85dd31bc6924ad8d0020ce14d7980c49bd882494ab66f55d2abbc64ef6bcc37db7dc6f62452469fecdd3da7841b160ca38fd4f1612c8968c33dbb21e915a8c2daa73ff4d5dda9b096d2b74ebf83b887aac71430ce709a60968990a8563df1454ae907f0fe0b970d635fa81860c3d80d7062c496fc566ca9b064af1583c2a8a3c4be28edd8c1d80d70a2dc242818c8b7cdd06220df354b55ec86a50d63494b7bc686b1a41836109ae10f9e990849c6bc38b1636019337c58b16722a4287f85612642827f82a92ae6c7a18d51a86ec37777d73e776c40f1e30e0becf9b2f3b48e2e6e3ab61c33c25039fa54181f46a923524a5f53678f61c8f21e7e972cefbfba60b157b1e55d6a297b31f2b264fb67a8638c72c6bc5c8979498a52a8a6c8974ba60d9168a8144db7753d0a15d10800400100c314000020100806c5228150281ccf15c63d14000a868c3c744c954723711cc6400cc3300c822000800000022008820008006114638a1f539420858f7061137e724068bd07c5b86e9365c4a555ba9ab9ae67da5e431047e159ec6631e7a173f8106088720f89e7256462a23adef307266d16531e5c7317b69eb49d4f6e8ff8cde30f2a983d6e6b6688d478306bc743a786e701ec87721e1440a815e2629a4746a7a2a967b6e1f6605910c77af778d32cb87b16eea420f650b60588f810a980288b8647d56efbc1e2cfd3e91c3f243dc4142f1e648712420f51c307712024e3593c3a870d42441066d1787cda2cd03def9d09c2164473212eb278f234b14330a8c422583ef5c7fe5adc70808c932287676eb3f0e01170c51f708eb888eb81a04181e042643f78f9c10781911052883d1ed02c883c4bed2e163d62ef6663f62cb65321e7a9bea5d0e5e1360e10ae3da860e151b6792584593a8f67370b78cfeaba8b1d8fcb964e88b2d27a44ea7ad63d6adb2c9e3c98dd7ce63c7668ba1e84426006b102b147a4ce67bdc7dd26e70f2a683debb41a88f043ac88787c9d4441f844f44ca259a879dcb834435cdcf048e554d4f62cdb2cbe7a047317461ea772762a7ade36f383cd87e63e5864f3946b2ab479786d20208810ad1fe536a53ca8d8f7c4dae6426cf1f006c1830805218648db834b0fd221f410b6219a037191dd53def5d4f6f06c0079b050ecc9c45da4f190cd41d1dc23d3701f92f560f1edc1968be2db33a641f460e1c4e3eed20462c5c653dddd16c2f1bf9e8f737c00bbe21e4c21e21f2c43a816420adc1e9dbb1e02130af180bbe33ffc0962459ea7136ef1681e6b9b6fe34e57e1530750569ec916f581fb60ad0f16df9e6039e4b87b543df050e25010b820b621be3f84efc3a81e2c19f1037df04b57c6fb7a9c7090b1990fb122c833de8611844fe61eb1370b674fb693e3211b4437047e20a680787cd72e02a1e702cfa9a3f90171105254f608a659c8f7ec9d5e0f413ea41cc48a82a7272e8a741ef79ac9436a47205a808c46611074b5784a6a49f980d1e09c3cf8c69cadc59ce1a0be6b0f4365360b8e475d8ef14f0fe006e2070b674fee9d4f268f60350b79cf8ed3f310fc87f2071442ddc0965a8a07e83d5a23cc94c1203f4c0ac34c971c04b286792ade2cc43d4a753e153d75df2cc034ba071f305d8043fcd3ea5972b358e961744c20b410c9057181e5599ef3d9f6f47617795051ec31b5497d105f4b00d956c251a3cfb119a3751f0521d941f9a86ca9f9a0c2dfd35d734224cd83142d1ea5358a0ff57fb081d0ed03bb077b85b8f8f784db6d438747830e0a318f6abb82070b394f129a0a688f159b37102bf8793cce9f432aa26295dd53d33cd6ee26c4ff2120502a8b39a3cf0a37c2830a791e481ba086272d84a6f3b00f0012cb5906987ec4c45e6a26d78b131436e0a6afbf1882b17a0dd48ad7eee1ca33e41942f11823dd507112a7aad26320ea708013946265e0edeef8ba9f1e30b5cf5344d6e17bbc66a2b5a598f985c7104015e4c9d58240beae743e8e227dc856fc09d6e4548e4ef7855f00aa1248b8ab1212d50a182bf8cb48b688b1963edc7c935dea6c8d39fec6be83a024e7538a8754cd82866759a727178f62ce27718faa73e1830a388f841b0a22b943d1dbc06323ff83d30f4e592f9fce4131c9b46c1c9ddcdf767c0b004ab7e93f84fbb9d0bde379721830a59da7bbce184efd17749e4f297fed4e0a2198ecdd1084faab4292ee33f123a1873ec23be2625b130d41c638d866019091ef7bcce487dcf7f901ced961cee82fef0351de98e27a9599258c42b3a8777c854acd267899fc039709107046bd8aab7262be74f4d1d42be38cd3d59ba8f61540914744c91c58030f407ae15875646df56291ff3c57ce6cbd9dfb01977403cb099291438736c61d3543a66a88a3507b9634be8fffe0ef701aae3b942ba6ccffbb0bdcdd71228d753e71188446c673e64299b8c624a9cad405e7c731ae49bdbca6ca058750eaad3317949432d51a41c98506d1f91a3baff9fcce47bcf010b1eb5e1cbce219b3f3da170e8639ed4e5b48c2bb0ec0e35b0035c6b0cb58d75e9281d246ce746d9504dcf028cbbe0383b24c0acdd6469b4ded7b3dac8d30f56f6f73503d56041958e210ca43defaf16366fb54bee507fae841ce1bdc3092a1b56bacebd2eff9740f4dc732a055f094ef1197f2c23a66d12ae89c2c00651d2a388b4bd183500704f0d11a19e284ae7905f558b1fe0fe5eae5441a8c4c557139c6e03014b74e2b252c92ad5d73a03b18f38f73a92517b6aa87742efd76699646703153fbdf844cac2022101bbacad63bbcfa0bc5a46c6b0850a53ac6b6aa4b9180c46f7d0951317c8fa21667c16a808c9b0e746b9c779bef8db548579c3892a44c60a0ac1de7f321dc6bf24c27d77cc775d5873812a0ccb101312a41bdd3c3d02e1f5acb29a10f3469edeb7ac332d6935c2a676e96ef2091360c8252245755ed54204b5b6775d149bb56f9f45cff5bdc1395cf8271682a79f60129014b994aa3a1e536c206f90075b4ea239414912069476fbe8e7a2eaea89940f44b600e5c14468225991ff7021c0d3762977ff37c11e506b09dc4b3e8cf31d324934b9c2f9ca8beb00d6f4b8b44f25f99e29bb7e5f580a3ee70e8a31663d4a110ff389e079d64f9f0b5862a14c2e6e497c42bebdfe8ff2c7330c3af61fd240910808ea07bf3023bd604b84ecd9d87f8902555c64c5f8af971a42b72d3503cb1e399a24cdb34cb2a1f5872986c2c936cd62d92efce2e082e111df371ed5199b076dbf9d02032f1fe4e86720b0ae31bc6866bfa8a571c21455c13731b8b4740c432cf55a2b17391986f09cebc5a5cb08298e6b9a4ed3eada4e562331b69dd05922c1aad0ccea1d50be17431409277b4de47f56468acd7b0ef0e17da6ec827ce9c2c14b6dfa5430a89391d9a050683cb64abea2d2ed898563c1cb72478c258f575768577fc4f868c6db18237b7c13baca926f5a5694c4aa272367eb5ee11ec99ca7a6ed0de312ba4fc21ddc941b07e90de2cfaf8566bd55a1e3715a2f921cebc2efc229a730850f858e79b424bc12ad968fe4d08d2601104cfd6f9828ca65fadd374af163aefe6aa8762751f60672e32296c2e149410fb258969c5a0798b71bc36915418c496fec044c188e7486924d9a0312a2bd66a331d48d8306ae8ee72c215364be625185089393a434dcdd23fd3ca613cbdc645dd32d7409aabce0d011c7191aa8b3c4803717dbd8d681085e4e481cb61025487957eb4361ffee98bce05a381f1adb022906764318c49099a78870a72c536be07677ddc6bd0a09d5f2fc0c38db46b16a0e221de584f3d71264d037310a7ea09425ac918299fc3a7b22cc0091d70539b62afa8b5e9bcd5e8fc50b6bc1821b443d1946b69c85d542aeb6dcb0fc22593ea5019c4ec0d098f9215967ddc6992e17f680bae536ec2d4d633a3f4c9a00d13a971b965533b74209172f4c8e8d5f53f167d5ec222500818f9a82792a9feb51de26a7a05200a842653151f4179349d9b59aa618b6aa50805114dfbf3efcc362e3ed95f785dd6bc2125ed51bd9f8275e5a7219de9df88733326fee47c1fac8e6b314d13e968da6182e27daf0d8ea0295c837a1b035ba3c92fd9d0b1c8a95f330ff5e760b8280daf5e2b83a6cf9eb6cc7e99b53e1c9ebffc0361e1035a0310f6f5d67e14f401e50ae971cfd48233260b5bddf56b00e86ac34151e0cf3be457bdb07a3d04fd5200437fb360f70c225ff7d3de43945d925cdb60dd8ed70cb266c3416c175c9c4267cde735ec11dc648e279b664cb8672cd7abdf6f5070a39a7994f37655fda583c660b8a1fde2928c7b054ff9335852721d63df21794318e86d02561bd091185438c0f832abc4534d07376dabb4f301031c9f2dd52ec92c79bf47e30158a8f17c516ef4235290e611d3abce3447eb73780d8a8c11c427e8c2c5bda466d6b65361cd2ea22c348f4037b84614becf76f42c1bf278c623bd765de486f0604adef822a13d5471ee900ea62c7b27d515a68af281612d8534941da195b65fe86641a8c7924cd3f657b87684183deb8ef364f7659d02d746d78605b5df358fe8ef68279e17fe50acb549fa273682d7af54d062bcf563ccc8484f4a49e912280c3c7fb47f43931541cc1acaa8fb92269a11f9958c22a119da71e3fb1d78d2564765fd124848e70bb06a5174c1939a23a349545bb9a0a0581e70d9227b173d14eec742fd9324442d431815d266a45b51c0ec39d1d48c38139abda9825ed6cf807c2ef789cbb04175163a91c951aefc86232048b3f9e4de45dd2b39b3ebd92b15be3598ff2ee7fa3ccf6cb75174ed171cc62139940318cfc49ba6fef319deee2e433b6b925c5f496bf638a418f3476490a9017161156156ca563ec83213ebf94e922908791241d6e84d42934a0cae598429304394c0307e731763f67e9321766b74dcffd573d599de541860772e65b42c16e84e2bd87682badf8ed18700a91876911dd0c88918815b689a16572e1d16c4034bc029f91985bd7f4dd0a3a3f6a14c3d9742b9116e14259e084064d83644af22d084e51a27d816f6a3dc32877ca655946a9edd4b14bdb876fafb93191f0abbba2f9e71a3ed86fb6000d9f17e95860929bdd3ded459307aedddb02619debcf1fbe8f3168b059b8d98b6020f1921fa31f5386380e5f6b0686b29c57c187e32adcded4cf5ee33f7e71290c1a6e7fe656f877977c3655560435a6d9f144853628dcef3edb1865d44fdb7f6fe430371bb225062d63f3adce005a6f407383f20661ca29f56b89838d56aa33b976c204539c6b9df205935e3a0b71aaec490da19bf855e796bdd4000857e0eec9b31e142e7f44eb0e91510e0dbb4925ad22dd9cafcf29c1253fc13ce36d38635b6a74b06684dbb6b70c3c971410c05fbb0467afa68ac797eef47e4d646fe447583cc7e1ae9a44505caa59300517146ad5461c874f6540e8882077e7d83c7f45d8f4da7b7c06f778b969233e62c96d7bb71e315e7963c0db710c30ebd515840f91419778c69528731cf65dc78f81d44ec4b2e38fcee968833123a1265f6684037b1524ea3247c68ed581d2a9ac165f8ea1bf31356c1c05356be8428acc948ddeef82a2f64f2fc17db7e16c34db0456aea01ac322313a22cf9807dd2d9655ce310691718e533e6a303b8eb9b60e3eac0c41bfaa8f24832a51f7da20a14b79f0b98b7a1a8a77765228031b8e71549e7ca6c17a9243707cfb43368ca9ec344d554b47010ed0b8aed3be1c66e09f0cd218207500265f9315c2bae8a62e2a0d08b5fc373df484e8b1ad6c5b8ac261040525dd750aa22be3e02934328beb4bb1cd8cb1e18efc24c139279b5ea942248e145d999e121afff6186e4aea4e1a67ce60ea18b879430a19401b2ccf21bf312a28b77f1fc17c5b040338f165205b786862411ecbb855f466ae5269d95ae2d998d4ea95371f30179f820b9f8d9e84a67979ae4837b140b2f5dda920b6727d2f31004eb662d5026fd265ee34d28b34062f11e4fcc87775815358ca26b28b5c8df561f0a9cfff89019bbf0d4d255e7612710e4c690b634f22d08328f6aaeaa56ae42f47dc674a8696cf01b747ecbc0ec672e0ae2d414607abb77967ee4904d7f6b9c8b3a7ed16cd8ff37f916c99a9a9d08994157d5548f9e9b261f93f499d316f661ec908989c05348499ebcf06184288430a0c3761dc6950cf3a2e1cb8f0db2fdab246699a597e837d5587acf6dc84a85ef6612157bcc12c3573a6d2253511314400f88856ddce7aa217104a0fbc5ef83333d1b46060bc53f5fe28120ff3b51f9efa400ad0253c67604671c52929212a088efb7f0359eb9de0e01ce3402244335c9d87886d964032529de57e50268c4f406aa39ec6ebf7f1ea165408ad4b6c33fc608d345221ad36655b723ec3581d4110fbb45312bcefbfcdeceb97515d2e3218f32630e8fd975d0f7d8650310405bef0da332cb93cc1b3cd9de99b491479662e54746c1bfede5a7d88ae809bca21397d6c825222d1e44c21c8ec0aec650c634bc2f0d8c1429c861e1230b9d704835f203584702c122611776ad0390f40b2d1458276c621242e785528930a508a3c2b91d2545d582d474f9be5ae060b7f855855935587f94390e1222968d05174a411178a25b5b2ab20ec01a922781a823f2bd0638bb8175edde472dbad2c076d94f2344c728d1afdf721c503cd45be07c7d4ee66992ce77b68cf19d11c0dfe8066a30571a51f054b31916f5bea6b0c5116105efc6e860371f55c3248d6c8293221e9394663d2b18afe0538767d9658f040bcdaec862e876a4da830d0e604f062952c53f8a05a751853c87f1a62f4415c10d2aab08a48cebd883abd6c543fa5b94298c38e19d059a261d7a1e47fa69b9d1760a5742e8799fe9f13e106eace4debb59424310066864eaaf495b97ef51003ca05c43f725b87522efe9626650e1816e773f0fbc43cab1c4916f6986322929203388069699af807dfe16a1dfda9babd7481a48477e9bb8699811d3a884455900bf18617625f39745b08870c0a5173a6d88099174f58c44f4a3d18b0a6b0722ed2f45ec1267d16ee7711da9e4af04ca66221c729ffd4ed3d7abcaba25c7a6bf4cd277789bb02a0c74ea83e1cef614ecce4a7820904512f10816ac2c27a178457b4b3f10b3e36406747039cb07765fd020ae495bf5504b0371588c5c7470805e107df25cecfac05f9cea71ebb8b454ccfa5b960ac8de3868b3a4b28b23f3476771c4c74b08f86c8d404dae01325470a13e4589d3875cc0d4b863076de4c88081b7a9c1ef812eb108cddf55056df1cdf43f99c93d52bd2497752a1af49ba939895dfff8f8f4267b69e26c51191bc4d66c651e53517610529ae257c4136feb531ee19d3db0daad5fb8b80d91905f177ce2025cc9c497cf36de36e7603e6ac8a882eda0bee283f802f971acdb0580f3bcb955b41c63945c7d7d099445acf1f02b3994c72a9dfac106d845c9ff1516878e7b761e92e73bac2975cecf9b93500d0e08db8c88f2089dea11cf131529e5aaab05ba5199551ecdbd407134f7a5315066ea26c9731d2e5b98493e400eb5218ecbaa453d29cfef8c730810fdb85600949779da0d7b812866bfc8110e1941c8163dbe54da7b70e5c144e4b61b1c3df0f5af35a42a776a7e5c56489b23d2656cd0b1b87bb61c6f057994d2fcc047aaa61afc9702d7213bc57a013cb3cc9476d026456425a17cbc7ba031b0d16463ae162842496949503b9c1aaee0df95af4f57566f22a8f4568b910b11b516fa042c88a26ed94437b3ea09750f6334c821380ae0368cd743113a0f7311e6278d7828f201b8a5fb772e565ba2aad6681bc1fc026d47562a6f1002cd3099e957c4356ccbc49a014859974b6de0c61a75522a6df747d9a96ec96d27b0f2c1028ed56e9a332ab513a447de48773c849906e145686558fa22958a6669c7b8831b574d486a1da2bbe7d40c484d3fd4052fdf33bd100f07b130530c3485c74f2a9c0c45e6bff2bab44affc795866d34090db58475464bd446afc5daa873c1220e56be438d30e8466ef5ea8e62a01505d847a22f7a386d1306348b6cc8c6ac5594107795ec80a2ff22f961a0db016b6cd6cc7a7ec2ec56172a16383eb78e52ba96a2956e4438d888fdccae1dede98280b0fbc8504dc11c05db38751195904f53ec4ab5748d5cc7a53be06ec1f6352011270a568d28d8708f03b2f913ebb1804407160ce6efb0ff1ed250c55f4dc64aa76dab4433c7de9c4f62df2ee6d8c7edea01129a4246666a2433250b60ec78ace3cfe4d7ce6b7e2fc7e4c6ab82f4bfe0da1369d4458b39ee3bfe47e655b86e9501c64e5219ee4bf037a0c6058c2c016dda66a41a4f16bdcc3fac0d02b77a43b373acd9b6c4989209ffe5ca38e49a8b521c7b6e67de6126c2a85e3d2fd2cc347e322e339460e8e26f7ffe6d0b960a4bee7748fac33a2f07f2afc72d2ead2bdb7a52866a09639fdd4d6faeb52fa7edb0d705f5a5a6135d367a3dab441f25062912629c4c6fd6ff076da99ca1962e086f264da024a5fa8f6db05712b314bc3546367fbcee3372b9671f3fba6791a71fbc9bb5ed33db147ab99157129753381814dde845eddc70e3349830c43dba8af5969a3520e644d3a002ae6a3ad4d2c46bcc18794d6710014216c498f0904856053197840970c3eae32dd9abbf4570c5589084599caf737b353938746d39534bcc3ae0ecd5abd868237e7e6bcc598c99f4606ce5141b5ce9b0a1326609142613ed424dfb6577b53ac84f0cdd04391b809574d118a1af532c02dc676797783f627297fb945d1e1dba56172aac196f8eab1533de350856060de9024b0dc14847c33c388b6034b8c11673c3e83058e3ff24f88b33ecdf759648d804576bdbcbe798c51d3f328efe21f982e08df1a1841e528b30de2c6c1f03d76615d7803ca3773420ef24f8af4432a4682567c14f33ec95076163be662b2d1cdac93e6f921ad250031e1164257d9bc3581b3661ff1a3cdd80c59ab9de9995876ed0e56d749cc8c2f4aa1b35db1579c6e66cd8b865fcfd1103f06e7a768fa0bc5f3688974a4cc0583f0ac7d655ccb6c5dbc0a7f5500c0e24ea2257378ae0164d243c9e95957b49652e25d0a91a576c89789ef2849788e0eb8671d34b3ee5e0daf0ea368dfee1f1ebe259553b19c2b5aca8de1a79d97212cf9c9c727071e0bdc551a2a28695a7b020f99e60d63407ea20e6a4d4d2c6b3e6e0603ffe630403bf680ac7709342a6e7bd887314214afd0d49476e6ee77ea5ff432d73392379fc0b9899b3e4f87ae54d41870e4638c5f0405da89964074f54a18d53417e9fcd77e29488f02ef21af4c97a3a498eb49b374ce70debbc16025353b4ab847f133fc3caec293daf0b8ccf270648303927de43e8fb522f6137d06db75b1a65061e14ebaebe9228b58ccac3efeee03e67f87f20560479e12b9de2938b2f0c2f11915559439057815ebabcfc7461b78c7dd01738512b0a3fb91ed6963cc32aa0e72c57642f9233b12ea760c843bd8c1450c47c2db0a6673d02ac0dd60ee08aadf510145287db4564f8c31bab73822a4e9613adfe17a0c6f9c585a1444367066771f2bfb06508bbc6fb039b2bbc5aae55782f466d70e2b574feddd91f43b0f811330b2062b9db98fa17398018ec721cd5ac009ad9eee5421fabdcfa9ac2d42f0a9f3cac14e41f8360fc84b51e97c126b239e22ef3b0e9bca7292e78a7f56513f5b79827c4978e338d53113b02c2486b953e2041fd781bc2f1a6956237076f45056248c868ce2df2bfc6fdfe4513e940bcd62ff73047f334570325088c4521d642adc5ede8a7239a51d2a30ba379b11fc858afb81a7889eb46b2bf4a918e264c639a9841269025d3efb80424ba764b41e1405b8588c64d1824fbff30a3b2ffad50800f8c2f69c8cb5823bbc6a5de65ff8133fd7c428b91e46bbe5f24bc1dd2b9ae1791aaf487f346fd41390ec323fd59222b65107d0674f0165f015df9c03fa2e57c85d7b8d55f90e5379b8c7010529ffae747fbfa082eb761baf97a2e2268aaf90c3424017c224837106b10e49110f2a5d2f99048994b64336bb2846af60e5c877f52a8d6e5ec95dc107ee2ba1a9296fefe95e8acf0b152d5a0b75c65d5564d0416b50741f0ec8051e508189983042c192bb4f6b50a7290787c09946cd91d538c3c0aa72b8dc37bf39e61cd2d002d4a831ac10b1d450016a7e61ecba16ad0d49afa71661cb9c726a85443eebb5026d951aa056193fe8ca0c27ad14c7dee50b55c14fe08984872157680875b318f2d33030290e3429d5950658242170651337d16e94b11fd348332ac2bc1d5a156dcdf9c091f86c1f483a34a47596f676f92b7a330df8f262380185e661e15bf6c255d46327ece19b54888b127ab71a837804a5b3deb3c6c7683790b565e5f7e33c5703b06904bcfef336dd49a6d8f663e9dc3bd6ebb689816d7187d6ad0df2a76df86bdc4e08dea2fa22607c16d1ad42ff9c1a8e54cb43e925c287c4b75c0c857fbf619f33318158ae0ec8c72680afc043eac86769304d1ce2227e9a7e093f3bc0b1fd12e7c3ab6c90d6473b37d2f87d62a10480754915bef7ebc1466f1031a2024a54f2dacabb34fb17e25ea5be741ec515afb1f1680b257aac75665765c58e04d9ebaedec83fc73c0c37f94899d3863b99d2e0d17a931d5e30d9117be7a943d711795208625b871013a9b3bfd098825dc08270626b03863e62f8afbd07715aa5e3661336d8afcb5a5d76df7746abe7564667885bcd9d406cd16b5a9d4959ada4d66a58bb03cf72acbdaa6be6ba6ebd3a67ec6758e5071d9e82c243a52a01eadc27ef616e0fb14b5a2ca9967e888c12a9649b4fbad378e1c34a6b2f224621beaa4e22dd28596ab2951dc69fac1fc7b1bf78117cce218265e202471e5eac66bcc9a4e179e9468d14eafc9476889298a6567e7ca9c5d5fc25113316a39f1e8bfcb8d7a7759a34e1d76294b12df3a2b53e567e0c6074756a595475ea5a0aa0370498096f8cdc1fa62c178990bfceea4f34b0f77a5c9d0b66ecbc551251d26980c23107d743ca11a1d0335dd1837e9aef858ff4fbf7b6cb11f3ed7ae502d264361639320cf24377002cb90de0d24235777572bae9682f5e554b8501234dd49308bad5dc8b844cb964dc016c9abee43bb41e9391d5a6982d44bbc18514f91d0fbbeb5a7fea52673e67611e3def88c4bda27752b27aee9c033dadc6404b2d3914bae4cf442ddb8c0ae35bb3206f49d48228b12f237505cb8119d3ee9f35f4d6005c636d953f7c203cdf2d033c36643db01e87c573a7c8e81a10e7a8766e738453907bf5f826c3f355a49884ce68102b3e6bab6a36c6bd47310e45300cf1689385cb4a46025ca25bff20c93c54cf36103935ac20335fbb5937cbc3073344078d38e5ac743a7e23cc44b36a9d5e3858935e088ddd52a3c7d7459f6d4562fb541bb3b7fe1d9c4be2a07e26cf505b011921c5cca74519ff033e9decf3df85d2ec518a654549776251c528db56f204125c76cb12c30f1e6046652b58b37f826495e7bed21d57504cccb43ef90fb2fe6c2c1823ae2ab06641107122fc3c60e11f8c90e43210c748bb2c1d5aae455e48607a2d72f10bf8be0bc1efad2c9ce63362ff1c948fa37d1d0212cb25bb1d14616317ecc4bc93a04bac6fb4c72b71a67949fda8804ca17363cc9f6bfbd033c4e86ff5a2526867e22d17a55a26090ee1e1352fd8cadb3fc8fcbada8b016e02813fc665c30dcc3f1a8d588ff49e0c50a1a07706a5bd92110531ddc00ec0438433498188a77e40352cb8a70961178bc404de808eb6b33cc6739190cf61eec6c9e92e168b6ef65e769e57193056d607f88d7f93375a718cf73943af6aaccfd88159c128dd3cac2ba6039c8e407156bf9f0a9a19e16500e20f5c6e47b16c0b0979d4b850c4d3920d8a7d3cb39083bef23f2d76ce871b28106174eed3b693906a66c016eec3d3e6f4019d334ca160a2842f65a03d0d9bdcab94d1bfe003bc4686d3c7e054756c1f56d56db8bd95e90a2798736e1ef3d7ff9b275e45b686a1d81e0b8067b55dc3315513a7a9bf18e514f99136b4ccd45c6b9cc16f4032b1f5a2621e937dbc8757e6b2c4e6d057acd9363808ee792d210939434c333332c1307df9f3e7f3cb187957565a214021618b7f5926ee1a21227cc3772bd6b1f5c612d7b62c9c0d3c0fe8963a3a18a3423c629fe338d1d5632e0576769955e5e02997782a11f703b1860f9f9a3f1ea5d81cde3ea1e0b8807ed7469acc4e59498884bdd52056751949810342c656e9cf113b76a966ccd79f9193f0c0d5db3e1d186fff07dc2b64dd8ed464b41157ed25dc6bb0250cec30a6be76c9cfc769e2eaa524a53efa7f5702cadc9067a67d26f00cc70cff120554d50b34327d58493f6a276cbf498ae496ee862e4adf4e874058a65dc94a3ad8116833fa0f85086752d80344f564f8d0f0ee64f129a692a4e0b3f145a4841d374ff386e79245a5b06e629035900e979ef46c5f5a926d7832bf3fae2d2982603639633d248f57e74a1fa8987c1ba5b3c50754f204ede12bcad6674898638a94221b98682a86372c56d623d4b58137f9afc3892b0445117d36be10ac8a25762fd4dc0b4834f2a06535336ba6a9378bca7a70401fe83b769bc77a77ef57c7abf3e2f8ffe23aea0ec044d304a41061e285f9314e915518699cfad3918dc1d92b844bcbda992a7837c6e7da881c99658926fe490da7c421f3b013dfe9654d682d3f3e12a8d5270d1939d40b17c62a650ceb02ae5245111683a30269a8eb8c11de965134242af48a728a72c41963f4e4d661255b8b64a2aea124a57e9ed8d4a31ec5170ab138d1e0cec2c035a7ad48b98c475f5cc6091d49fdc8687a80a8f36312b8e6e241738bbc36ab902cf2b9f0c1067523b83bbc81f60aecceb0a7612f16cea55a79deb0fba8079660981de1e32ac2af310b21de663f6165137a7670403a40354886a20a0c3fd32678019274017ca2143adf3aefaa53071ca1ece46d7ad649207f4a3c98b6c60e0180bd31200edcd427a462aee8f51bed337058c70329bfdf55c4b037fbe1088dd9091c8dc27644fd6720148d57a5bd4a4dd60de045717b7a36dd14ca46326a4667392e5626b2dad4f05a352a3cb6b1ec987def50987484b03259bad1acdc8f997bd8dcdce7bbafde0863bdcceb9c02b5758d4ebf71eb19a764de3e30eff37112e8e22d512a23735fa0f647715a43a7d2fec08bf0a954c84ef776648ae2aa8083a2fb885cfd1c8ccba8edf181cc006e8deee4a25f0862f35cde9bdbbc7f988390bdb07b3d71f94af5194f892d8e65396205493cbb55fe729258f633453fbb1b231d368c9e94285636a06f3ed1fbbd4c96af2d4b69880f50487c5d44850396c7e5185bb6bcf7a36ea838b64788055c59f83a9545122e1af4ad8bb1bdcde92ec16e71d16089ea580b3f26701e2d61875b746f4346e554410dd13f410a075b1600a0fb3e16a952074c549002997f0f814fb6b85e32816b4a3cb0779ded4f57af2795bebe0ed081e7d87936fe2e91d68451652f326c43d9a7654f14ccd100af2aa7280e46a0cffd56ad0f3c1a9b5999dcc3bef8835c068c6dca9ecaf73228dc216a6d05a68a9fa6afca01cc0bc9b31d05204a49f3daac94391a0cf11914c8c6a958d859337e1a9abb03f2fec580ae65d3fbe15a957addb42d336b8075950cd0818416b51f3ff0418683699623964ece94c81a083d77ab91664d77525afcd65e28f594d0797f1bbb4c3555b172cd11cdb6d7c72ad2de5d07a353ab2cc1a12a0c8d9170bed44862bfbe59d287d83f1719076b4a3f7f631479f21eed053f85dc27ba559464973319ce831713ef34eb5fcca60430c7d2bc2909eee50f5526edc5917355c030eb6de5dbe23b399eadc629a5a859e1e29db9ee1b6cefbebc859aa15191900ad89b696b5ffcfee17f9bce79f80255461966b50b36c98607f1af1ecc45bbeb51a863fc1410398c3edfd7d174865126afed7ea04a8038b64c59bb8112a6e9a82dc20163e4e89998ed7b807af4b8cf5a3120a5b65c22815d77846ce9ffd4005b3510108979399b58d5354168647646d341360174d0d93cabea8ea2775d46add3ee46a380269b4ce6951b8369f3f7733486c35ea19a115f024621d63e2cfe59c5e95ae8937f43a3e8e9dd1af50a3bdd5578412c8d98525e96813b7f95a3199f1a892939c3699c644c8938147eaa0ec96069f7fb84a18d01ef49a3961ec85a4cbf9e24075eb99027b7ab13c4abc728d99020120070651e6238a357231472367aedba6206323c5f55d81fc7bc57e48e3420541644d1feda6e6c1116748415e9d5ad7c04a1e60e1f765afce0d6523501dbbb86ffdcf2d39be6efb2c7539783f1922ef30f84b6491e0863fdc5f1c04117f18c4ce9b80236fa9b53db32c0554d9f17491b0594ef7f34c2a92f414209122450c94667e2a28b2efc03e59ed92b9083bf570eba65368b239c123ee01530662006d4f0c5ce44b907a7da50df630223f498b10b122204e510e5117e0b4013c83fd7f5b79ddb343c3df216f58f7488d205e249e033cd6016a518a831cd8b00ae4c866125d23d30cf2e0f29feb918381c65c582a5e87046690f4762a5aec1d996d0b189e9f420233cc6ccbad94c3ba8178979295b7769092ed905223e1fb6b7fdab611165a78050e7a7c702abbb3f1d115e09dfa1095b9bea1713d7595f1c1a113e0f6c8f8603946aa4e16418d2d9a98b1d8a81a45a562a2853f80ed586eee46f5aedf7f83036e13636ccb5bd97165ad9376361d629aaecaa05f8569355de509bfb52c4bc9c10a88020e8efe2de7da90c6c1e6aa632c4849150e69942e6d5bce299700f2ab9715a6a06972807aca14f93768b5920bea2067f008d1b2e19407aef6162e6a60d16867a649b6267e5041e48b5b6cfd12eac03883f4bb3be8a5c40a15a2d57640d351177b18a96c91e573f048fa5af325a21c830688fdd00e0c7423852c56c48e81c46557fd6707e409cf9ebbdbfbc54fe8fbfc10e9b2a517a7e2aa738006ef5c0cfa516901fc56a76bf31f00328047f6c2815da0dbb329267c069727191812f4bf3d00947c01d16833e860589a3046b98bb900486a02da0aa2f6d02db7dac08e2b177ea337a176a3e7abcb90792a4474f4cab9534bc9637b505b9d64d1de08856fc21f7d11d6ba263cbe48f0d50cf5e4eed6f66ed758551594ec95ec7edd9bcfeb5eb00bf29d0dde73591a419861ee039c4f649c245aefe8b39c269fdaa1dedb04dedd1bbb94fac7d0a972ebc5647eb8786b3c605a46aadb082ecbb2fe7a6f4ce63345853250b71171304ca8be2129829773cd76fa31997ae782f8fb9e70fe8d804e42fe169dfff0be823574d96d1c907b92e1da04349609848e014c6aa3c5fd8fcd4c37949dba21963d4446d19806720c3ce44ad7280390cc6fe20441310af1181bf2744793cb185545a52d80c768375559274b2769e731a8ecd78e97dfa699a3c9a5c03e7e639ef205c1581555a917a4a9f7dd07f0abf9a1e7e0be0e159f6d26c193c10512e19662360543834e911d3a1c98c6620089817a5d72959cf5d5989c5f178ef694179517f98b09fe0bde6caeb1b2c0345c1e52617f1158fc179476efd61817393e573642da67836cbd3e4b426322d0a4e9cb2925cb65317964dc8983e62862334491d523b236293a8278919022b3cb32e6b3ae6a3021d2754f842e33c4cf223eb336937ba8a331986d258476a79ddc89349f489a9ad54409ab9292222009de2093998cec36fc83400364484a3f462e2e839d45bf6537850ef604853e50386075679128423c39ade3111a10bef4256cd4cfd5a8fef6249e98c2fdfab7a21a2474a0736690ea3908c18661f3c3da72e1204f398d61bcbabbd35cc703d2e0c3e80d70902d076d259d15c405cfe0571b7ddae4072fb64c2af7ee75bdfad32a6d0f85319434c409cf6d14fc3474031b039009c9ea367d975b79fa621c1642cbfa2ab18b4a590b425204e47a2e0c1b4941e48e0e7194a85c0b2f29a0a75871369ca2cd98eea318e2834807744173eb96521d0b2bca7635238d9da0a0fe8c414839889f874a1a336a38611ce60242ab2c0ca4df69336adbca262b9edfe16a5767b6e03b65efadc7831c1cedcb9073ef36a86ec2e6686e3c67b442f2b46bf604bad5961bc67978ff3319a88a62e748509a89815201869a7163062c1d0a328b51c7d6e5244107e9410cbf71141ddfe7a02481ebed1c4851484346a13e1d133a0780e1ce4d3748cbceba4915a28620a3c70730d4e0c8985cc860fc3c8b61ae2fe37e34855d52d082b8f25a43c6902971637820727540710c9a9cbf0518e47030451d56ece01f1c9a9e0090eb8d2c73c019030e77916b25166beb3b929484a80c156fd99b06d000e62f87f3823168630e7561b03ea7a53c95dd3e48955f69ae9603d9cae1359735967cf810946d8d594fdd8643632903e536a2cbcfe77ca36b1b1974faa77891a2d89277e20b5af00b4f6d960fb24a1208c39aa3bb6ca242150cdb7e0ef1c0025e58d6dc45d5b0ff4f05835ef6c54751c74df577ee6fef71c3b4812aef1ea3025cb1032a6ce67d5c400ec29236ffcf6a2bb2b3ca515edfdde2defca42bb84bf1bb1ffb33979f2636cffc05bd19de778f62de66c67b770707079fd03c069a0ee4315072ecb09dbd8e54bea81158e09b1d1e2d2045f41091b445f677b7944422bbbb37b70c640848075f07302bfd5ccfb08c046f36ca473e508c236d08a1eb3c5f9783de82abe1e2cc30786f3e82f96857564d368207a831c66ff1375ca76fde66b66c560ee36b568beb1ca6a367a35a6b7eda5a9665d9e5b211f4ec2809ee4c5f8232e93a2cc272d6dbddb66ce4b00ea8babb3bd499de2e978d6a6ca949ebebb05db7added88afecb371b410d6036aab5e6a39ddd7b6f367a6a0171a0561036d1719ffc75bff75ed28f3ffc8e28e67ee483b0fbd3c222a58fcadbe9d8769defada49490c893eceb07434aed6316a41a849ac575bef16bae73aeb3d63efe4e6249b91cfcf733082124a2ee37e71aab971c5e5d167dd6ea33fa91b5ba99c6d52cc6afb7adb51816590d16e9588cf1ad346f69b8cf56ab5c078be0ed814537ebc1977aa981b80986f51881cd0691dc27677f75f6cb9fbfbe28ec5143a369d7ddfdbe7eecd3dcebdf5bfd625c0435f0aff7ed732bae730cdb75ceeafedd9cdc9ccefdf8e4b2f915cfaf386977d45f843d3dd63a67f698086a003fc347b346ee67bec3ece96b5f2bd761d8a66f6bfd5ae151a5b57a8db13e9caf77ef116144229bafe1e29c3930914e4c635887613bd3b9460dab268b31b6789b7963b3aa9189eb802a248a5f399b165cacc19946a6ddd7db735eb9ef6b4e9f9868d72c23ae747e5d4904a5122b262559683998b3cddfe15d047b00d53debbedf01d5ec21bc3a6ece5cae1c2ecaafeca9941f932ea875e75f653a70ae4fccee2213eb259d27731d50ddf557dcc3b8086a007151966518b66fadb5e222b9e41a62b2f166bd3f91d90bd82f4a0aa82d0552145ca438b2b59dbd1f6d8d85ff3d8f8510c21f0be184b673fc927b35e94f09b3f816eeb9b3df3efcfec2ff58af69d9bb94d2df61d23bcf5e3ff545d8d257eed93b0c6ae09f3952f670c5fd645f69de5f864359f3345ce7bb7e0dd7b91f698e7b59957ea3c20d3988307ab4ebeef01d06aded72aec17ac96116fd1e2d1dbed6720dff1fc87530cb39f0f6e720ea4d3acb9fb696d52ccb6c68dbfae39f2fb97ad197111d763a3668df76fa86eb892ee27770679fff4518361cf697dbfe919f7d956ff3e7d49ed8b633fcda7772d3ec7fb0fd9b3d0778dbd2086a905dce8d280dbaa194524ae946f000b3c64a8c514b145a96087a01490ba6d041aacc992733e492275cb67cd2450b55144d42082184104208250771a439daead75aeef1c0872fd7a836fb456981da5a76738d1839ea6563d2125a4ca541ee599f513e8ccf3ec8cd282d49568831c6317a9ee0868c96455a23685921055a84eca82c5fb248c1c2db52e8c9202aa0019bee514016293bee179505ca76a90044182752984891a4354511570a56700591e5e804597e18314305a645854b4bc815149270b910c54542ac8a1ecedc906486960f47c8b4e01ac28422b09470d3022c3da2ae20a2828a2c2d26aaa430258a10537c7061716a99510112277009b9f00452abc815202d16ae8ce06a010b97942e5d9eb8b962891618d102828816ec898b053ab8bcd070e3850906185a4229b458e04410d790134327187363451451b4ca54451de1322ae2e9879b2c9abcb0a4850214b87c20e2329274534513427e68c2474baa4cebca9711b620391143c28584944b4b940a3c606143eb49494b8b1157092cb8aca81ad25aaa22ba99e24c12615a424ce192010b154a8891c22408139e5c2ac0c18914fc88018bd2095c9828baf9c284095a348871f56801112d3a58719a7a3284d20a9a72704dd1d4820249abea85255000620958154b08b97ec862852a8680c24503132d22545a2fb0c00754931d5c46d4e04a9281842525159658316af1608584961464a6bcb88890c2a5431348542d134069c58020727263868992243114a15ac115a6ca51902a3f7630d30498960f2e2e2757ae2421260a0a623754f470c30d2e2666801d6931b5e0cac184224a94d0eaa1440f970d57fca062862cae23fa56696dd1774acb6808d794272e303b34b1a49544925614232e282db89e38214812405a4392103a618cab8a97a12d445c3952e28829ad150cd10ae2890b861d5c5c96b47a48d2cac1080f2db89e9ce00412405a312021e4921a93c44beb862dae1eae984089d60a535c4086b8b1e2890a767065591245122b46925a681971c21254405c3b50092931c6062fad315baeb872034689569329ad3043e0f0e4068b1dbe2c693149e25a62c465420b474ea8e108202d274708b5aa8c711d7981624b0bcc951494702d4d996108284f56b0830b4b8448d20262c455d582abe9042f5480e04045a895c598960c5e5c4c5b905c6959a1440ba62831c4094facece0a2b2444892169411d7112d6c39a10420ae226df78b3242a808322880194245abe809170b5537679068219992b2bb9e2c43ecee5d354c41edaec77b7caa498c3dce15a04c18a32943536025b0f68b9a828214262a65898f2925079894f2032c4a8902b34294e547b65f94142ed60995514aa9208060a9a9224d6a8912b29b2ebae5a45d5db4d311e5a2c4e6942192dba0f49b51c894208504989421246421b3d2eeb446f4a8cdb420340c5f5253524bc418a39c525fa2a49ea094d26a8fa2a20c65f8de7b332d0a5394251dc5294a2cca14168bc5d23a0a972858a290d91b097b731e1b9bb7b1b9919c94242943b42a52477e4825816521a570d85498503e26d4152353cffbe4cc841a1a8173616171c062c2a82750512ba8b2a1205b2d68282d25b1d694aefb4541f1e177a0e0004586dd7d8f0cab305386a2a6c03426ba24871f70142956a464814e689b529414128ccceeec97fda2a0608982c20494a9bbadd49128a9241b10207a7acaf46c1b196bb79d1d971929776209969040c3932c59b2289af192c468a3c2139a2efa7138401049196113da8b71f5f957ff76b68edf1a0fbe8d2eb2aab3b3f3baaff99d0eec1aa3b91f1aa3b8f6dcf5053c3bdb37b8b3c1233d68d084006c407bdbfd63c411f543d2fe8823a4e1c7f86cd09eff0f4c9aa27e807247b2d3e3ba4207de9133fae96b8ef5d21eafb6addd4ab54d4f4c42736c29844118844969d8eab88f5f2a5168ee5c13a5d25f4a4923a5d48daa04c569a17741ee4735bae46895dfa94b850386dda0c1f7de7befbdf79e8f94d1e39933f03df8de63bd0de41b6ef85e4613d3648c1230fa458951daf53331b0fa569b33d3d06f6b97bdcd352af793fdd8ec6bb759fa9a7bc3793ef76bdc7cbbf136fbb8d6d7a0ff646f69c6e81785c513164d5aec2ccc7edb1723eef8f7c96ddd0704dfd7be18ef3dfa7f41668302f4bc410daeef986fbc996b646fb91c1bf0fad6b20deb5bb31bc09dbdf6824ffbbe9659986dc01e3db67d6bc3bee56452e6447b8ee5e39eb4711b42773d3d11c6ff0645fbcfc73d1731c61dcb68cfc110b15fd4972aadfda2be406df71fd7e1798ba2fd9fd0b66ddbc604133bda7bcd8f6fbe362bbab31be2d7db167b8c0efd09ed39fd2ae5fde9a56abf282f52db00fb457939b22dfec2e40bd2d6229eaf559ca7dc31cf1bd4561cb48cfd472dae34e80b49c6898067cce04ff306fef486cfbf786e2d62faf2af367cfbf3f04017b465f8bb36b71390868d789311b3fbba34eee5eae3fb38fb9773d09d3dd8d90eeffbaa56e6fbe65f86bc74d99ae3ad7bb1ddc7408185d4d622ce68de60aba2b1ac785b0b82dcdd96a3bee867cfd20d29670384861c7ef82c162b9eac10da1aebdda94be589a66d32aeb36fdfbbf9a02d0cec9fbddeeef45b986d689cf549ebb6021d9fbeb42f7d5fdab10f3ada9755205501eb40021dbc3ddf4e6dc2e7ab19dd57b54b6c7736aacbd3267580d0d6be561dfa2ac25dbd873aa1c5183f564a6b377f2617a7d09e575c040fb0dff77b9739f77f2ed0ad419bbea4c55b72b8e8452370d1a5b2f7befc2b83f6dc3568d7970ebc2977a9cc200a21161e2433de2b6df5aaf1f6eaf1bf3c01297fc5c95fadbec6db2b2e27fb9fd5d3cf381d20748e8dd5d35f711fb4b5bf32a20c0fdaf7a563dbd6fdc95790061d8b26d811122c4e0c9b3842a227717c4dc344b888c98f384f7c98a483c64518a6431375414b82f261cd47ce536b7c92f169cf3aa30be9537b6598cb88d239981477500c81331261d3bc83f668e513ad4ab2cf61bbe4092a4684d414d586837ed24ffa49dbf0041523426a8aca7db44815a9621527ad6d369ddfe1d996dce9a4b5cda6f33b3c3668b8b5cda6f33b3cd928233d69296df34f50fa291b65987e4ada9d3f697fc29af6b4659bce465539c020e4ee6b9a63982167a3750df835f8b8420e3bcf0d717b1b13fd737f6aefd4043e41b90f25024ab98f563f7e8cc55824a26a725f195142254c4d90c97d94c0a6ad863bb71cb4fdb7bb8d4973bc6d7bc77de484dfc1dde1227800c81d4545c1b43be80463eed341a6bdb4e3ebf090d9b1c58e51510c6ddbe97233c7dc9c60bb73a7089d76748a30205374daf1072dcb38e8742f17b9ce56a628855138aac1443535f868d6d4d43c26aac1493535353535180689740d24d29846d71a1a8c8b70112e9a62a8a8da70c87e515310f1996b915fe7d735bf56fa11616748e5ce35dfcff5817c5b4d6f86b36ed142b768a15f46d4f981fc67a5695ab478ad59fa59b9458b97e134abd72d58ac4c59b6c5731da4a161cd1afdcf3557535bacf4d7d4e8973472cbcfe2b2afbf7a652378807c43db19d7d1bf39fe5667695d58d2f37536d25a7f8669ad334c6b9d8f1ca67f63659c595ac7b2f2968db25136ca4615891628ed02ec174545974b7fbafaf245d87066ae9bacc7ac6cff029d58fd9bc5cc6265162bbf8c98b37ffd6879c5627dce383fce58dc8eb8599d08fb07bef62fe7d03ebf0c67e1cf8f3916877fb5e230b75a752208eaf48e3032c759fce2a4e5ba7d549934655886655846da369a2687b9a6c1ab8c31c6eeee34347fe3345b8d679c8d367d9dc418e3b591c6f7f8b3230419e15f7c71d2cffcb761e62efd658438e4a55cbe6c04ec17c5e5698a26770a25530cdd0b54ef773a30ebfa63fd2caff1e35c0733ec5dceb8c5e71a9a5fe9c03b7bd62a638d03aa3be3ae6f9ac66d97a67c9461f9281f35e95553d2d3553725b5f8fb3636fbda6affe6f5dcc5dd746c5b5fd86adb1cb66dd9281b65a3a61a9776a5f4bd3af9e253281d5b691fffec0861db7f7bfea630faaf931bc83bc8bdf85b9e7602f68bda02e3d2c485686b31db2d623601f68bda22b535c714dbb03effed1161c0a3c71cde175ce0abf75af3d57247f6f50b6ffdcf691ebf38177ac57136e334ad0bfad1e46719771f46cbfdc0af95bba1edc975f0658c4bda691619d1a58a23709f9cfd4645814dc77bef8df8de186f5c5d2a50838ba3cdca66d639b93a679df15e2cd7efd4ad52dd9fca7c1cf3af5faa4d9592605574e1f088deac669702999476a95cf71baeb32db81aee4edd7ba92ed5bd54e001327f0ec3697cd66f55afef55ad1bcee1a592655946b4b51863bc542a7caf0e6ff72a05a57a34f9d6e4fa052197ed97cac3a2e361b9f32f15df36891d0605bc4aea78581e169b9dbff32eda79f253c03e2c5e75a95c2a59965daa7d6badf5528939593c3fe5a3a7a5caa4599fcdfcf9e111d10e5376efbd56bb376b166723cf3ccb471bce39393b27179b97fbc9ae43e8015f6700f6ac0f816dffed9ffaf3ef2bc772371eb0677d67f6cb34098ed103620a272c50f922454b940e4ab218d10d43623f5ed878bfd80f1362444ab11f4c361ca399c39e73e29410dfc67e0cd95bec4709677080c47c50c57c74511203a28453e44efec3260ace8f2a38100a0e09d1fda573b15e9e8bb73b16072460792edea65c379fe2f8d0aefd3eb0e13f695d1bbe1d4f8233c2c643005b0441fb08f99b165de9847e75ad6fadbd3077007bdcb5fa0e7b9dc4365bcee946724e871dbdfada716e47fad38ee4994177ef69fb1e37f2d78e4ed55401efa84067c0036d849e8476a71eac4d949bf946b6e1fb3b05e5a572a9dc07fe8baf10e0739e1e34e80e3231b9cbf8da96cc3cbb99d1cffd3d7f21007a31e0c7877a71818701fcf7daf1ed07dd41a919efb5b73d0efb732a300c5681445218e3224c848b300c37515a428afd911438312969721f1b0c8920112ed29b5729c54898da06d82f3614b5638f5d7f3ef94fdb747853ae83506cf86e64fe6436ca47cf977667ab765393eea0d30cf83006fdc1874eb0097c729f771eeb366fc79723a7b3137b486d1c1368191b7ada4f8b9eff76f7305e171b9ff950ba3f3d9ee7c3c1d093c7f37617f7ab6769e868bfd8508fedc37e31a126fe5f6013d824e6c58062f6d39bbcc08b012ff030c0ce13a38c32461921d28b0191f6fb23cd5f899052062fd3c06f9ba2a196f1ed2d2340692c7ff81acc1b14fafd56a521ccc5c02e5f9c07569844e53c559cc7feb6843ea2d25088712eee33b7283484493c3dce03313542cf39a1d21685863106f8246c029d6093769e5a391bf9f4ed6bee899d87621de7d9a664f0a2a70132e8dbcf79e0d7973af07e39159fbea947c47e2f85c57c6d5268fad5c638e79c336ad9b5f6da2bb4b86dd1f2e76bda66739ded3ad97b764ae9b362cc791573ad974a8cf7dae870b3a2c78bf676b6277a74ec91f5b595b1d9ac9c58ffb675bad7711f8c3fe74c639fc6eab7411b685f9ad75bb1f05ee1af38bfb48935fcaaf65a58f3f5714dac9952dcdd8336dc9452c9e5c87f3bfe94d2a394324af727adf78b0dc13048570c6d4f68dcfd66857edfd9383f83d7e33c0fce785cc7b3dfffcf2864ce39a73b76f7e73c21e8c73a07ffdb8e1d3b7eec1863c7733ee9cf7ebedece4f5f86af56abd5b3de026d8c85b5ca38bf8236d678368549af52f83af605cb97a83058f68b02c3654b305536b521d4e7baf7c1fe299f74dc53b4e7ba8a588f2aca92af49295b163db55ab55a6bb5afbdeed72a69bd19b8cfbbf75eac15107fd21c2a89b459e68bb67975e03cf0be071cf800e65be3edecb50b2187eb6b32d3b8f1fabdd65ecb592c45cfcb6d52740733831739fef0bf2059e95100941276e03cce011327254c5a6909e98806128530021b020e7870010c74c0818c0824a1a3ac91abf9ab10f60acbd5ea31c678e54faf8ac69964507952c08a35575246c9751f6c89a406cf1a29a364c98852466471da5de06d1557f2e7ab931b471925123abe8c08028df834e66b71da7b67a6a141c8e5d4781b7239acff81acaf407b3ec6acd78bf95f9ef3a503efcce1f8c22f191149cdcb57cc346ab808b80bb732a83cd5ac409bff76623da4363462bf2833488e99d09d8d31c6bbe9701effc079a69c8788186ef2c4a4493bc2a49454617008f6f8eca98e0f746c1c5042776f4e7b33d63169cc9ff74ece613bc629a584704228b357126ca2e11dd0c031fb0d8bb63f907b21c95bc706e4dbfe1052cc316a1e44176ead8dd1460fb4bb80d9431b71c0b81da1a10734e0d3b0167640757fe0c1c60307f0572bcedd05d769ffeac1948e312772d366cb3828a0c9d7f6ba4015cd992d8bfeee35fb1201dc1b12babec97a5b5fb2d6f9f9b591d1f3b527ed7d12efe93e2f5bfdfc2dcb5ef31cfd3facafcfe2eaebbf1fb4f3ab03da59bfde5b9de9c4acb53d18ca30176e57f47bf0bd07ef85f741682d7cf642c8c9f7af7b1fc26ae73f776b39eb6eb56addedbbf0df6b3b4203edcebeed6ca63d6d371bcb6d5386f34c05609f6fedfdee69700082dc1b0fbaebe95e6ccf9ee862becdbea001b66619f4b88f85776b558eb0de7645d3c775ef753c8aa7c59b101aeee7f43577cd39e8a613530ae907bb83af63db6e660866c8d1d72a86afb775a4931e6d4db8bb95ad898d422c5b4c771069c7df96d094a3f452d5163a1d9b1ff95135017cfe684028b56deb80efaffaef059fbf18e78c73c639e39cd1edcd345eadb656bbe1fbf9b262587df4c597f78aa6f94b8b31461aa3c797479aa10cf75adfdd29b5b87a8df3b9d328ed7b5af584985894163251503845414134866aabf294045f48b4a72fa29fa17dd2cc322e1f4d0138b9127711ff1e711e2598d4447702d8f1bbc72e00cb83e69e222c8a2afbb55f54144e5b63e5fcd220f69c390ccb34ecc320be5582412227ba63ed783756bdf7e69cbfc6db39ffcc3f3fcf5f3d05dada2a630dbf82b6f6aa9693be244fd8cee80b36335a62b1d9741e3ba7fdeab5f94856cf7a516b6d8db7319793ff077f7dcc056d7f75783b507ebd9776fd2523aea4bbf0cf3c12d917e0f4d09d8531c37c615f86437cef6759a65f7f7e751bdcac872ffdaaf1b67ecf1c44cd45adf5d7785b7339abffd14f7f15408a441a5518f5408118292d4daa44452188a211248c6070257a985042589ea02105467a28828930491811813f8e6431c488fc9e09068671424288981052424d481439633483c081c8842b42e420868a8c0f505431542525c80b08f1638a1cb2c8a082148e289244042795215b902233f828421b80c214145b86a678d1c18813285181cc952d45aca061ca07158f420b112b544a2c711465069df203121148b444996c05669082e587092838a1420a113372042216746062e28910172824518888f1016f684249280c112846a69841af9820849011d884c88f291ca6ca10c18354112641600850411c7962041915846c31001952e1d2924f20481246b420440e4e9ea01089a11e49618811b445132aa4a04cac8a99a122a8966250126204d67a04b66b25d2b51ee9b16b159a1a5ad29bb6d19ac8074a9d362d82029e3046300153855094120ba6822cc91b22504230f454c943f43cbff34f247fe7f53b2087129e96f8c147165033ea83dab512fd7fcfe5a1035a8aa8614c192233ec30833e01e2b48890135164e9c7120c2c0c51451555676af831c491268c88f8d31412c2889348a48890113b340822381005e9c0c4981f66ca14c10405028a2009e23232830f18a2701283911dac0c298acd6004c916285ea8724491a8084491213fa6868c48292314c0080bce70b1a14715d10f44a68696a6449d81fd500609144e50456251080f106668f0c134441527cca84f762562c3aebbfaf02352c9000a494cd9618528534a984157602406a2a50ba8d0013395d4c40a23aa78e28a15f902e4022e3068529340307c8101cb140c50fb7d45daf0dfd2d086dfc1fdbacb6347dd3a0f83f7e0c3d14177bfdf7dba4de7f5769df2a55f5dfd17c551ffe9c7d94420b9e5cfbbe54f8d661c9c949c8df338dc4fcbf970ee9c73c08feef37aa0322cb2bf57647fab138452706abfbfa0c3f6da3a6fb47139997139e73edec0b7761ee8dbf30d1bb65a2c2c86156ad7ba2ba713d3f5e194f5ad8dcad99c4aa3fa8e4c362989098bd8ae6fe3cff9b405bdf4dab4b82d6a3aa96f3024c27de27edfca1bc0a75dad843534f6c7fe7c1a9d6d58cedab7336e03df75be7c630d6f3b3d0de0e0021ee818c1d0911213d7cc98095fbb61ffb15f7fe27c03be4065b30dabb76d83729273095f9c2bf8c44ab24c5c38b25f4c29a6a4f4feb2d4b67f3d73e6cc97ed1cbc6d0397eddf056da80423b3fdb597fdc67bf83beaf607da51370ef8ddddf5e3fc0d9fbe3bf7fc3d845974d96fbf167a6896661e5ff6a7ded9f195fd7cddf852defd7ee24b1022d7d19f74bee2b49193f645163caa17303290af1b21a41f33b00fef6b67539c8781bfad701b4184e141fcd7c36032d1f133d8de00397b9cc7795ebcddf578c0810f62087adce7c5787bca298f788056da980a55fbc55220134b414a62d9bf5f2c096a6b12d7495f527e9dc08bc91783e2095d5ab8b33588e90f7df903d8d96f274a236863940f83fb7467cfece5abe20ddc56f9f4abb4f4ebdbd1417b70b330b402962ddf5798daf2e70a5f26862fb835c7e1f20502d08e1a9fc78ebaf1c360661cfe72661cf35f11ee69730ef96e63becb87ffa43e65c1973829d0f17fe6d7a0a57df950a3afecef6b076b7fc67ddd128a69daf1e3d2d1864f7becb78398ec00ec174b42da4070c78790420861b5f5c20b2437a654cb3428a5ff30c85f5f19ddd9557bfab236dbbc01fcf8eae6e521b909a1fd8e7e7d9971d0a7b4fec06e661b90521abfbe80dc8bfd76b0367c18ecb72c486dedbba07d9ff6db5a55d3d27efb1da1d065f5ddd534b6d1b217feac6918bf9dbd7a75da67d9ed286bf3901b3f90dc73c33adb9c2cccddd5e797b51947f61a0737cb520ac28e10ecfc3ce453fb2b08e9afa8cc38b2f65973ed5734344d7b0f03bc7ad99d9dbddeaf72ce5e661cf7b5d7b297d5b847279ddc7be13c2f83496df8484ffb6deaaf6e6a2cedc50bda90cb360640df8fff7b6963003fb01341d0f6d74dceb9175bb4f831a4a6cded1743826df8dac1f281174e68ffc2a72ffbd7eeec17ee43a7267f42187d9c4066c7d82f7642920a5d628000bfab5fe97b318068c5b9213e95f83df9e26110a5467d095ff0cca6be03da780fbfc6e37278c8ec211783b6cc39e63f8e478ee56cd41ffa137d044aafbbcc39ab2c5b79cc37e8e31a2fb818660320b8ebe320d127342d6df9f2dec8a0dc20273f0f69fffd0ed67edc0e8823e37840cf39e43f8e078c9cb5f3c68ea5dc0be7e19ef08f0ee18efbb97d31e612f951be9cf467471d4774f9e231215481169f36fd3ab5b4e34b3047dbff0ee9b163eca88bc6b2cfd9afb9c6cd3e6707dcef79c80d7f072be7a05fb92ee6980f391ef1fdbbf920d8d05b04febbe6d8887f3f723fdccf1561bffb1dece0b679076b8be03d0cba1b738ec8c92b7940b8736cd48f0f0437e5e48fef80db665026d1e45729f9367654b5ed531c634fcfecd903d89053c005383de89e1d2a0044d141cc1de364c08e2f76dcd1be385f0e081d4fe9369c939dde4f27a6e100ecfbaba32fdfbefd982bd7390f488bd09efbecb0fe9207dc91b31b98616a1bada70c52480d9bb55f4c489330306831214331a316708cc0900959808aa525a822320cb1d28310241b13a22484f3c2cd7e31231f9bee1733eaf11c6993c0051754e001850e8cb0450a0b445780798287205171bf5810283331d891189026382a047121a3608439e70c52d402043d3c9962ea6949114b33a693333288d00f2ff8093c85068b907c294410263706731204078558101df66abf5810251a0bb4e7d6f16cb28206a008e99224aa8916f4b0434f0f4e0a3d4838483edd7f1c532074887e2f718468f96fe21763ee1061fbdbfb62dc979f5f8c1aa4b1a804ce564631643424010009a3150000280c0a868342c158308c9274b61d14800c7fa044744e974da420874118639031c618420831c400030440886caa0058956e60dc687ccef5e20280ab452f25787b747e97d7e6a747c374e0b440ffed039c2161819fad1bdc34bf5008b524040c6b078a1b826bb73164c6b104e25a5de1a1182a8559e0dddfaf4635b0da881b6c1aa897558ed8be3d22a7067dd110bdd451755cae55d36d890827c00c6ea2242af0b7b735c123c28e02c7d4b5ddf56edd702fd1ece0093063a91724ee3759ce6ba3e0a0fe1bfcc67812f4550eafad8d9bd4061dee4f034047be4a4c161e736547869f287bbefd7f5337a706a10fa0b529665954a8554ad07bd63e5a75afe02a32bfb65ea32983894160d59e4ba7af69906bcdb40462adae7041c9815502d7cd7c191f5499dda49a9537ef246d5f94925b52a0748a59429e1c0d6ad5ce4db1aa403c8299c12f217e631ae28412748b44bc896817d15c087369e9980aed45fd57479cb76ab99d79bb1dd0029bf4be2040c315221117c01b97c5fa765bb4b870f7ba2cd2b75bcd227e37a11612a08b80d0699fbef3bb66035afba84113f58dd2105a62c5c1ee03faf456cbada54b28b4d72c790204ca154c7ffa4d800d3e1b71ebbe443a42b2de73236b557cec969f099bf4cf711c82d9a630d899e46d9d518457539145ab1d1c839246a7e2dc15e4957eadd0c38132a1b0c0fb2b7e85d39a3bfd410ab937f819b6d9803e3829dfc53289ab67789f9adb51a5db31361d14f87a6fbfcf1fcb5c70d8058441027fb67163ec88ee6916c3af972b27178205f7edc3339a6e98adb6f8329b92c30bd6593c6cb3ed79a21a60e48af20d89bbf8a0bf7486ad6e600179953c55e67d0d3e603aadcdf20112e93400de01234c903aadc1d99e8640c07a5e338b6823137ce4aad0e156acca640e50582ac699740041226b0031f4de8144047e31258fd58b840c81b8ac964c58d2eb3e7be3424b9c585187424d5704ee2a0589355af1637630af50a872db3fda6e0efcce5e2df7167dcc62da5cd15aeb46c30d9a9e5c3e82e78b24d286eabdcb7494cef81ce62a14a2d549750cd5aa61b7e06713ae430fa77418d89f26336f01c1529f007a26ac139e8cce3e50237bb00c6fc14c9cece42d644bf62924d25072eeb11699a5eb237f3d5503b1ad955ceab1354a1bd5304ab46ab57463775509fadd77ef49bf9db1edb3230eb39f7353a73803ad855838951ecf4da26fd83941779ef3126c7d3a0005fc945979da91d92874c346110887285b428eb1a124372085b14657560318281452908c9b7a7735ba6393df2365916504ec6b5c49e86fb9aa06828e36cf1647aba68b9cba398cac3c63cec3ecb950292f2b163402b709550f882c3caf490b7674fd093e135cf596ca8c9d5cbd8c2607d49e73dc94e8eeaa8133a20319cb0b71f36c78cfa407a9ec64e4e4444a2eacc009b1f6a07f93e5396a07485c38c36447752b852aee692c9ebbe35e9c751e48fd2be5718b5d07999a04060d02cf0dc624b42f40b18527e653844de88cc60bef14d43d532a08fe05503afbb664a716ce0863e46cf888c01a8684b4915c3956e408bbd1d36baa98ba1ba0c04344f259b944b27aab6493d2b767044911b9e857669ade255632bb7d23d0db64bc2db77e3dc7a6f2e657008fbbc16da395a6cf2150e332a1608a6ce1d8848ada39b3d1289611c90a690e57de879b1e7e379899991a857afbdba1b80790bbeaa3198114d0ecaed30d2890bc705f096a23deeede2e709fd0925f858a9dc395295b54b15378c14e05c1b4145d53d47725d07c61ae8b028d00eb97edf118dd020a24d9135799dd4a99d418c9c28f0d19b3c52aa7672d671a7d23133a9c27a187e66550ad73850656b60fdbb3d1d82c766ee14ac1f2bbfbe371695202f49dfe72bf79be1143e5f6e3fe30b4c997029872021e79dd61e3c002667125ed1f4c23e74a18322d4eca52499a4e144d81f8a0a59f79345d58a5833ef06bc5e4755e6ec68c7e38b328a2c27c934d46c0257b473225995c6318df4883aeb062e82ab2e1950238f25ee93fa0958d65c17ddb51d2949034919fd191bf0a2f9a973225fc231b4b4292fe396098672fbb9a28ade439c007ad6387ba682663e63bc802e6384b8774d8b6243a885be794abd5908993efd73b6fa6f0804459df118d6023b4e40015b185a1a7a840ee26aebfa0825bffcb807db2eb76d83bf66f6868dc229602b9b9d3f3f33eddd365653b5fb3db27c4b34934d31f8a048c2d4d36c4da5cbb464e9928ad4f902718f298f2a8e1b15d6fbea358fdbc46e51c7bca6629ef44eb35fd58c634d34a89df296d723988bbdda5a1ab24827f02aa5788dd994b249b4e12dfe4852814ebfb3e859406f594cd0d89c98c4a06b911a76a82ac155133bdd97d29ab73f27433263811e660d9b4b457af8f54214b12692896ac49158c520d560f13d41a696224398430ba0a92b34234a80a5190cb88037d5d60a7b44ead44325c3e1125c8dce2902cc1780ef560f05c179095d1896db3cd0c88ce25dca3a0f70b2dae5ba6df399c38ecd43955dd6213a75cab25f152cc5ae8f8a71249196b068e7e4af7290690020faa38774130905b3449a066ebbdd03d5495b0679d8b8c94b1a364e9cc789988101c4469cbf84a114c38f7208acc2b4a41c9ea76e14dd47a862c7331f4d12350a70b188e4a1733e48266b1462323bc3cf1e601135a46d29dab548d9036bfeb8a51d2b89654a31c14d150237c7556b9c18906e4be54a13675c47239bd6c3ca7e6e52d808c5ec53648ee6d3a210174fa4518d09ca4f9872b2b7d222d0544809fb41cbd1f4fee37a27990d41df48fb91b0f735a09a465091571a04c0a2ec530ec93338e851898dad3510c4d12d0500c0f4a40a5999d3f2ccd03080e08667d220421d5600363b0aeb85482313ceaad93a35df8e0c2ce4245728307c08d370433207858a1c502394631545fcb8ba83e55c58b20459b6e61a543461a9b16022d32c3351103b282b11c889896ab23d7b5f3ded6f7b897d3c1d0fe53f100bace5ae87c6fc18c6b79fb0d0fa0d6d490178fafeafe0fa08428ce4478205ec50aa7cebb8f53476f2f01af821d4a12b5a3e794cd772792cefa3aa6ba88ebbf030a8d452a53db41e3bc5b9a6ccbd2454243f7889fd82625be31a904a101080c449f14d13a6fa3b1bb05263cd3d5378035a598598ed0d525e25424c8090a53154f2869d01967aada06d19c3441c31f4e42c16c2cccfbd1232ab50b04cca1767712ff67de96be262a9e85c7c8fcc26e536082c8c7e6c6331aa261b7595767105e4d0554edbfb196f3b75133b73c0058a3da77ab52ff994cb0013f9cb4dc343c06d6fc84ee0cf7aa88c836c6f1d65164080a8b5259ed7f9900901212e588b0b50efb852c852ad98678149a901d0ed498f46743ffb3813d9c7eb62084b91bb18c218bb0d7566f8e5fe82a7672f0c277be1549adedd74d4c0fd90683643872c55bc7caa8b063d2db8d0384581897c153de1de9908093e8b9a52a589fc2da8569c22e2b0fbd422705e181e4f182e03a2c347fbc5544a4566e4fc5bde6af7b6dffa661d5f196e9127394e9bce629837f26d32b18e244ceacdbced37fca6dd043eff31fa9c6c43150b9dc3397a927588c0acbc410d15a4ab6673dc583f9eefa14ccb7e06085c47cd2388920b44c84e118d936f22c998aa02a2eda228a01beaee6c045834465d3abdaf60a586aacb2ec8ea2522068cc8f254abedda11ae283d2a3265cf07292eb3433e508d040199686bc7bfaa85f6ce7829a0f5cac53f8e42a3ea0d49436a170d6683dc325318b78590be8a6e8f334b715b05ced5c838a9379287ad38d485a27babe17052a5c5d632b913c44471ee7a0f842cfc4c3c1267c1bc65978a02857056237cf739fc956900bfcd81d257c48cf5b4577157a0d284854680c0b4980b89d117088fccc477b765d422d386c3ba87960d9e4b73849e131b358dc537ba6a9ebefb91be58e67a6f43110a7808e07996c19067ced2d5c552667f687c33438fe37c5b7a4741d535ace6609f0ea466711929d32fb2c9351733ece0210b0402bac10d554cac085e57094f44b7d70a9561f52f21658d394570ecd56d5ba6bcde8aa6741ef2f28a88a40178ff17f9f79ce3ca963804a3714b4a16ce751e8d8fffd975ed01dfef1eeff0121384c7a3df04935774828b7517123c75fd573337aeaf1b7fb70a6e282dd06a33cf058a0d9afac0998dba55084fc5488920cb0730fba12dc8512952e9117ab0c0948c97e550c46e48c3ebd69450c4944c296eff9cb3a6b310d2c2538ac7c02a087b59799b2ced59719ed903777a8600342cda457c4a8f5a937b14299d0d8ca0308db154a6290a4bbb30db8d1ce56fcfb24701602a93756c8f11ec744475d92d4b4632991033e80841907cb0d2dff3e25d34ed62a40abdbc1048183f78e7dfbf9ae64e463984570e6036a488b071bb0e64c762c010877eb820afe0f3d9b46405fd15c99acb400a4c55be9734e46dd4963459bae8ed79a1289741555b8d9533a13a919072b150421dae7259aa2ca5fa4e83375b121299ec39e25059cfc938310b7cb282debf72b65f723761850f9f6157e494ff1a35b6bdf1c8760587172cd5f44b597eccf610688741eb1c61b5b06f50c29c55040fda745275418d5db0141bf461f3c544633a401f568ccc38d671efe99cda168a629a076069a8a72522da245554862c01750304f15d8b909fc1900570a8d11405cea55e149b30bd8b00fcf1fbab76d4bcf63ee02af1b00cf2aa0710b07c8ee0503f121a9ff9abb3aa662b1a3f0c00cd13da776338960077998dc33237cfde6b999930d418b6960ec5eab18f638546014e5c8ed3e69e3f47467002e6c20f3c81b322619f0e6d7091a125a588c384822e127a6f992cd0d77ad8dc04ab49889b3c4df6004ea0f7bc5295b0d0875968822c791c6f107d25a8e2ef98a0ffc4b91793d07921f7c4be8f8d7e5577f6dae34ec76f39a48dd458c77d6de70185093eed081f63bd3c59f065e2c2526d154b23e2f2456d8304a70e6bf6f716b1a3e32602c9026f9df2f956f073d3f1e5ea070c126af09e25af1387b84a861747475b4a6c3f8624c8033f92570d8b0b2c81393ec3d3df809a19daa8c12be6271c220c60a1d568a73a7ec074082f1109e3738d268607e3e12f070efb72f408032e446944cdbc1cbae6dc6d974bc25c25f717eb261c410bf4e160fc0219d3ec911c4741b719710a90bd70bf894cb3ba8c9c27d1710ba7be864bed4a6904d80fc3792e6170a8a1f8a44064a7ab223834f05c70e6827f82f150c11247613e4b2bc651d4c608053e2451d18008e3114dd45ca0b81110b36cf7d0fff1d4fd60cef8110bde7d8b11cd4e9426b62c431e18d09f3012abc615e7583c18186bba0c16c0065a642d2d85130bea98ee28b0f23b8785805f670195ec0d8a374303fc15a10666d90acc4b035bb8dc3b26dd79dab4224afa8f81366d020fe379eea71a9977946538072c1241ba36d7e310157d93735fab7be7c4307b9591e1ef400ed496e75d82c231a411ffcf1bd4a5159b76d2551eb768199324d18c0d52c428ac7534e0a6877140f86566b5f0c377d7cabc2ed4f3777e9b1387be23230535dd1d1024042bd71b73a08feaaa1295b17300f9ee0ca9eb1658c04235cd2f27189802ab64044571eadd00bdd03b00f0fb3b51449b3c0790a73932c6f450926ceddff3293fb5df662fec7bc90623438b9871ba550c709b53cda4c55560e12d27e85347c04fc497d3ac2b0e914590fbe41fd85b09d4bf30a7b2800c13cb0d1ff503b6b884514221baed6283889351d3b802cd7c929fa4b2d9fd619f24286d4039335a65b384a93ed31a92d4cf53c5a8bf5387c795d33ebc151b93b957e7609006e40a299430dadb01442b59f6c0e41afa9150b63980da41761f5dc426128d7f7f83a4daa8d1947fe194e292e906de95cecea4a369b3f580f5ab3721b157a65aa679193319612ee44f4f977263acab35ce30c16016ed491b62e185e55190ba992a8290e51ee1b99cd5361d8465762d9b62abb2ca7e45e5d24b29eb1ca0137a5f31bee7befe642828f7727d6391c8410b1b9e97c6b5c2294ff7f878bcb7120232ff54a0b6d60bfd857c5e9f7cffecec29b209647d87b5650a6a5454dda3254f724aa044bdd7d790031bbe95272130c21373b84334569a273916a0f30ec3bdcae228519214fba8120b421dbaf4d19f3e1d5cf29308dab0899454c85919fef530e400ec95bd5836137d3b3cd7071b66fafac11ddc850b1a5e3ec737f74fad0529125f00c7cf2f3c9ecc7e7b0418b08d1906670be587d59a84b7e589b4856ba4a32f29d2a6ece3832be0fc4a80adc0f02f3e397f05454d020a098051fa8992959865839f3662e3c00c5aee55dc3c10f777c2b1dbc63a5c57d151bbfb3fef89eb80df24f9f56f10d0009ca73eafe4a359f1954802040e321df43c0a9e50fc77d7b0b2be025b814bf3086292748ca53f3b190dee6c74332af3e832583c72b685bf6a3a54f1c7c73da534ac18c7abe2c35d66c708c71ebca3049754bb06c9a4124f60d42a8718606a21d45d5e18b115aaac492d6ac4724676feab6d69bfa07ac1af847536e52361c7586e7a7b843f5dd1de15ce588c137aee4dc34bc023653d98f1d581df8aae5d40907260a8daa6ef79c27cd1fc8cb291d6b436d4d15a60e0d1e6890fcb3449297724d154cbb035235ed451111340afdd8d1e5afbc942d4bd922009fe29cd85c789b5859eeadf3afade76d90ebf4328749890a393802adc0d4dd2790fecf01dffa7588a60edbb78e8336572aa0f5933c85cdbe154b220105a34c1679485c04ad4a53935eb0151f2154f4c30b0fd6939d48ba816c146295ae851509e6d5b02328687fbbf339791ababfa5c8e22f0d70d9942e12ef481400493f7493eb39a41b819c2734414b06eef342a6cfa5134ba14747e41a0e6c3d4989d7bf7a4787f916fcad84b45e8fc1c4b62ec8ad9f014cf3b867308f0e32233871e1eb06d7aa1be5c5aacc6e2bfda515c7de6216d1e66e53ac84ad05dcc18a4ddd1e841462718b6dd4700853f54d1bfc4c12b84ccfcbdb0ae36eeb85c26c5f867b53afdbaec5575b4c4b76b71d1c480f6969f4a642ee03b41542f4c65e93bfd53851d2f062577d50eea821cbdced9598903e2e4db4618e74e2376d11780f131781939199e09bfee11c4dea1a50fccd383fc1ae95df5a89bc84508686348c1ccf26e046118c925a6a98d813c4ec7c58695908fb1c1e61b943af5806543698ff2b0cdd5dd8d8e0f41db4e9d8de50d55361079d7c2a0789b8d6b559b78dab5a975baf406cd404435a76b15169428e44aaec9c10b27c3183a0c2f99fb3d416b183b066fad0c7fbbff4c6258fb6e9656ddff6d96c8e89c2e74ff29b8b55e8166e2146ccea18be68ab9404508074a56e9f0bbef7c3ff6692a9c5a686be3f4ec7c3aed88c8ce9f5c7b956699ea2d47e3197e4a09e0bfae95839960ef7fbcdff10de65d9181113db54139e02417e5f476669bac8b776197db6a13862db9ceeed26114d37133ae6f5ccb74cb71fbf9ea777355f5f1c28a059084895018519f6b85e66902d02ddd2fdbb0c4c8f99ec186735c17a0f83da4d9a6e03081a2202c5a7a4b47ec8b0690e5d3d1eeb8d5d56ec8c7f533fa576ef04b0ae9bd5ca308fcba943f915599bc753679dd907559abdc570c59431df14941e7e3233771df532850b85a33b4838434c1546881d54f0b8fc65ecc513c00157e7f225d207d0c0582cc8a8c460fc0daa3e911bcebbfcdfa3143c3a76f53b5c57449d123ac55a4e42bc87964143de8f5204c7803c1f6b848ac8f10e104b8f1765b4f857281dc2e9c887360695663f179403657dc5e7514a8192a4bfdbacef125e9850911424410c89595d492ecda3ba3486fcd1359ec47b4c486dff23f3593b68e916ff00c9b6a26fddb1eb4697966eb4e0328a1242dd9803adbc69cbae1be3f701bb6e8533ce7e85b2fe4bcb2202d6c87af1e40a2905cf86f53e2c7f8a19c5983a6517598341c9631bb0833175a47124a3fdcfa9a3e13a0514ea0d661238e817a2b1efc297ddf40e1acf7c8d9640f2188401f8ad13273fd27b158ab55cfa083203d4099036d41b9b8b55a88ac6b2147f972d689590e72a600e9e1390b06348af2a2578509f4cdb6ac081af4c30b32cbca56d2c277256fa784d339fbca5007385db6445af62b53e38733aa62185ef48c56802ac3a5eb7512406a47a26269c05756c23347c656671d19d685f13161a20470003bb3dc64d05e0b96a79da1a3cfbba0b6a4b7da518ad992a20d7c7d3808f9a4a973d89249d4c7f8f4f0b56008c7031d9d66dd03a20e147d39c7d81f8cc33daf7b0d8a99f07cadd35e58fa735f64b7655500978216b8aafbeb4043591ed24c2b835dd69b36027e1f170bdfd53670284d1c1ac8e563969017292e58aab825ee0d5d1d8c6ddae5e7b4e632c70e11e0a234a67f555fac13ba99e021bb7a204c9c64a7b72f01389273967b46e6a00b8be9879947ebd918b434ace168e8abf4413c7c05bf0974265f148499dfb0df3b5d344c443f10f66770cbe7cd649bd458cfad983be20a8e06db665548c355bcd572cf136ed371c5d64e5584aaf75e138a233658f62f3f9973b722e267021d9b114ef53ae264ff53df21131499d07a5353fc6b4e08a3a8432083008341db94c40349a844035d26883e8c05c45b5355ec84590d88e2c80160857d8539dc2aca3579e306b71c74a74b0dd50b9659f0a0669cd9d7b91ea5139070a3f71d185090b2951329799d61fbe93d1f717ef4b97134df6b8340315e9e53efef62379eeaa68e5e7b5efa463c65caf5c5e65a0c381406c8a2096c90d95eabc61ad8fbdaa3ef52c331c2c3c5a6367f51ffd7f82d3dfbae828d5ec9997f2baebf8a384ace8cc08b209402edc913e546b7dea618196d6aafc11b94cde2ad877a9723f90215e01bf6b3523e103741baeb6443e1f5887c5ef96b4928f81410a83064b85fad32338ef29504a6ce5081614f19d03c6aac212f6a4dca2a3a24bafe7c9110b2d1d302b30fb2a52a6971acaefaa07536fb3e314afb5dcba0c160f51866dba919515a3b782684c9bb266c5665604f578d40314701d1a4233cc98853e2a18f5f15eceb167e61210b9fe83a176ac2f0022a39b31f370ace4543a3718ad10c0b2deb6ee09cfe0c22268adf21f11a995639cc2031f4014467a597686f6d2526d0200fab7868a7d1cfbc4425cb4bd0511380d13136fe24248603205a707ee9ce6de3b54a6c01c671fe9485d0168cdb111d9a00ddc0bcbc0daab4c2300afe7430b4b305024a2132c7b6da32d1fbba2e07818e1d87897ab9e0a829da0c03d1a1d8f4dfb5a4cac983ed038d4652d151c950db4bd1fe7f8b1212685f96fce4ba27ca077938e95a69b669f830c04767ecc810b9a2ea15f74eb2f2afa8de932225833349915a4df85b8383f009d56dc5200fff05b64d19c15593a4c2dc91c0cd898254dbe2b3b178862d7564daf513f0fde0568f12e05a2dc5ad65a607523abfbd131bb583f246d9eb1a5613e919bcf092f3e588396c9cc9ffee38a44e882f1d97d61191a9b68c768ade5645bd03f1044a789dca43909643a2a0ac6f775021de277feafa94440b1301fd29986b91d4a91c0e8e79154d2071e8213218054a950f7522fa2b0542b755b94b7746b7a2ae1574b306a79603f2b0bec8dbde3d611b4ed27e415ca335c11d4c2885c7f3b9d721c83e8bf7c53f9b75615b958a3df93682c5b1c1ad8e918f42539b92172389742f008faf2b9a8244a099a17642ba7b3e5f6b42d8bed1ce88d878a4c7937d98be22ee1e2cc6d7990bd40ae33cce6f3270450ab68bbf020546562433100ecc17692e674e900ec57e8978ea2b1d8dd89efeb8f1b1319780ea35ed7223ec6b5d0b53e1396428517cd115599997800da3d46e83e197a54bd8eea404ee41fd617d89fc868f025747b6cba84f5a7a41ac50f2ead04fd3449e05c92d1daf0fdaa97274b54c5eb01ed4a5747c2e8f989bc3f81d4c7a3682829b4a633d8a0357d4452cdcdea2028986f391df27587aacaea9760e536fbcbac239da7d92e9b133bb68344c658d92bef58e56072cdd4ec2c1631df5a3113753e101756e9202ec98fbf06f0faf5249a038093ac8310c80230050a32528dd2fea21dd1f43426c6643f1f5cb1b7d8de3a032e078462ecec15bf84392dd290b07c45b52c44f9c0b86174e8cefb2819c9f8233fe8324a3045e0c3b8535684107db229bfd3a0130fe227eb48aa6c4b2eb17d52188f5e1140c7b222578ab779d4ad0695086f5db9341beccf93ce4a25cc9882adc19550c958a60920e24b5ab2c3921bbda5ee01afc9562f35eab8a8e87f59e9f9052b4ab098bc770ea1dd4ce91bfa376169d50cb4862fdbb00a7285ad2fc6ba025669a9a3e26862c17b3b775a4bd7899e3a673fc0722f34d43f7d83ae3bf460b7f469f61a4b764974dde522382d90b1d41d5823cf04ed62fe640c91e354b132525c9e8e8977d1a8a875bada7a02ca03b8872260e64b2ed45cccef0d43df3f314647c21d2ae0f0b5b732b95ac79b263fd6962f65074280db9a60ed92fc12b1bb1ba6f4a3f8e24ef6304f43ef4170225bb7d4712d7202162aeecbd6566a7a214ec734375ce36023de13644a25c96abe53ad804beaade4522acadaf67ee40437e4595c5de10760f865c07652774679946fa78a8292753f5801fa3e0e05caf140f2346d72b4cd078af9f828bcf58c7ec76abb90ecb566136b6aafa20e98d08355879e0b27d88726e82998ae223ae28828c5e887e83ebf666a792bccfafa246b97c93deca6229d0f3ffe55ebdc2c9166c5a5208e4fb415229d40f59be519ee28dd3f30afd1f32b4e2257249b804f05f0c07c79e2db8ee17a07dc5224e4d866728e696770804b958f577ec75ff9158649e4ae3d89044a23fae4050f06a912fe37146cc7f2aa3124ef4ac13d9fd17037bcf96312e625a71bfb054c4669b0c37a68b748565c73ad02395d8de420c67a4340dea1a399057844ea36437dbd8c6e1cb3303054a3c23b3043e35bd11b02f06c9b7187605e7b4f82fd408359053461557ff71dbe34c7ab05fe836e32c28b5055eb47180399dc06cb0c12154d27d6a4c9b47c6a8205743e613a36b8d4e3807a1ee4aef9d4b9e8229efab9e914ec37715165299ea48bd29accfaa585ccb8b2564c1167ed833f9667b1a72fd1d48f0443b62f53f039c27bd6f54be3e25e2140280b3d7dfb025cb7ba608f934184365f84688a78e90c59515059de9c65f8ef7be8c87515c3961e8a7b734d1d69502c78c3347fbab1a34131050cbf5f047fb5b202357d9a8d13d8871ca60b6f9358c164030c804bf3bdb8e11746706723c9cf705960e7f2fdefae1f00eacf95c0963f70d6f427c5c77817d234f9610e0be0eca2316293ba85ad9b16daaa7cccebdfe63a9aee710abd835dfc23795b1b5d31b5499a0686414c5ba122b9f2590a88ae0cacb9bf46d4eb6cc125c00235191b579781b51086d62f1bda4e1305ae53d0cf0245dc8899dac4c880c24626b211648675dee3cfe1bd3c844630594113df491bd510c4bc63c230abd718980265d5a2abdab1f624954d6709d3054c95d35d8278bace5a070a9b0d09108ae2a01dcc3a15d635be66dab78f29738eb42f4f8a26d9fe739d8802b41204ec1f9a6be72cf85683f4fa0e6ebad003a764ac5dd70583e458eba2146d0554a01e0527d01422186590539389198f327a08f6143b36323fbabb4d7d8d2792e4161679cc2779bdfeb6b06417fd5173687bfa02ff802b72ae049605579ad7987fce8b2b1bfbb5e14636b8367e2b51d65976e99218405058c74ce7f299a07cf12c9e213ef333c94a5de311f72b73af4c285d3ab383f76ae3d1b2cc7f43b5c5679e77abfe8f9a1f3eddbcd858af4766995b3db9809782fac44b5da880c0c2e2d00056b81f61172d2338ae4a33bcc1c305f2b59f6312ec237c48733eb72135360c8725216be5e6468fd65dd18cecebb12690f000cb6b6dd6d33f273b01ae434d808d2cb22fa922ebe16b026ea119b8d758100773a8897b95a701c6bd28599003f59676d775734ed5c04f67a40d418a30124c8484fdd7614dd2612f2c946ce3e22233987554bb746d4249e4e926ee64380db68ac38fef31972530ae0970e7e2604d83d036be6b597e3767839aecf42ed007fbd2c91201f3cb3233aa4133f253488856c54c21542fee5611e353d26c261cb9456da002a0c5b0e7b593050e6427cca3c3a0359f70307bbbf481151aeb981fa983ca364dc5a7e58401c55251b2debb464931e5af9b05256f6741fba55fb2472230ac177aa1157d7a9b40b60da8d06b80af78ce324142344122acc4a8ad8d5948cf32b1a062c97d14bda015fa7caeea08021663180d025c702eacb37cd5a42943460e2d3449daaf9e512a1019e86dda4e3da95e9dcf5ad0c5f0d0d331e2d39dcac57a10f284ea0da4d1e4b987bc9b6a609c287f4f738422df9f188f5a9022395496f2a220a79f3406a8b2382871475dbcb63df5e39e162430a5f0a452b37632732b21a8ccbe2a972ebdc3d0e84c7aea5600d98307dcb76536750dbd2431a1398251da4966f1a73469e9cb1907d2d52152b669344f3b65e0eb3091f0b219f4fc861545cbbee44dc32d839e1e330f7017880adf4bf2958756b048c05ea0fa5a841a94531cb37df5eb2388ce5f084cfbb76c0d49d62b6fd0b49384f468e7c7be1849ce1f4d67ec02e6ea3f9e7e22c8255e839c276ea84b2b1a55716e261c41570d2215b61524c078486d5bde254d88fab74a87d533ee2c711952c317543c8438e4d5feb3b8dd331728c84f7dc43a9449aeb55737b40946a591d143be4a0267f6a0cbdab5db8288cec4d20f88842cfca51bb10c6b6b154914710ae3d864e860414e297e18193f73eec2508a89fa149ec48b6af1b3c8e27a58c794efb82911f460971b58ec97119ca0aef28c4e392f6c96ec9cd7384d1943d3166228ceab996ca18512a1c859fd7d4cdc81cf8f5dc0604971ea42d250a374a068dbe06d106a871b78e426c4140e3424c8b6505d4a54cff694ef9f9a90cf8b94787cb661b760fa1262967e06e8b1a01e2b0c8994fcdaa45097d505f16551a8b52560c2a642108a575c854140121a747d5ffcaad61c11aa3d85a54ddcfaacd0f45cdc32aee64f3c1410e8761458f75bfb7e6346ce5e6c73bf360481ffa7f243300373e75dec154698921d6d4e69d172eca2457c027a1c7754b68b27dbf6590df20af3961a419cbb5c56d6911c1969246cd726a5dd8a6d4e2d782c94ac5e0e522ac27db67af68bc6ba0acc3f3370067282d4ca635e2af39965035600f98f2f85bc932d628e4d31fd5fd9252bd008b3590d56706010992218dbb84e430001ca35481ae92b6a653899fe8ce03356a8eb4581c8e3fef1c5223df7e8b294517d40bb92dc09c69449c033b1fff064a2bdd2c1ee943b7ece5ca748b7750ea0b3d507e24944cc6785c83c50290aca49e399261e3bf4eddbbe9feed74ec9a88690b4e093eef5d276061ea022fcf07a0508a9ba53d6a19cbc9512690a082d2d765430f70c8f3984c782db718f199636d0a0fb4492c4632eb6792f20fa529694cd0b2ce9c3f91467922962151f19e165142dc076fe41ed42c84201735734b6c4a05e5a8c1a735a25328cb5d50a47bfdd82e5c3be89921e4568a6decf75e615af3c47977acc8ce543bbecea56dcc50cefc296a1628d01221b624d766f0b8f9b50fabf42a05748757a7d75bafa0a2c4bafaeabf9d2c4e2531c9b6c6d7a7bea89d5a1b7140d91051e4eb5db9f22cbee01bca8f77252c4419cc7a1bdd1f784d7ac77bf14a9d44b73cd14c3f71acc93ea0339baa94a897dae5d174ed07b415d36bda80f5a4027e20022db71d3c119ac666f484dda0d77ccadf4c1f620fe5361df7eafae2cc45408fb690da5ba6a86b016fb1249f7d1b3c6c585bd7382d0b9487270054fbed0f046e3e5cc438e91fc1aca4217164207b8fe207e0bdf0b59ae38928db967bcb6e4c2a445ab65e598a64cf47c42d8c3c69511028518916ba753210194cf6a9ca89bdb64247e77a3c6d0ba8283c002b000c0d8bbd6df76542a20cf62d78612c0141d977eaa7c5cb9bc723c3fa52b42fd99537dae2ded6fe4b40e455064ae8d12d1168f90e62d4434e9606213a2e193bf488fe8e65a53ebd23bb0cb4f2c7ead2075979361b26061801dc6b97bb2a011604af9856705f072b05b664a4f75043c9c931cb0284c4b7d1792aac7389615d129aed11e479655f1207667cb6f88032bd5ce14604819e51e4d2815c837aa85c8bb45f177a6835ee2d9660eb35e82e6d29da22673e0d943b10f1c4a5fb6e24762077c03d4fe09ea60d56b538aa9f0a4fb601200278ac8ebd9bdcd46a9c88fcd7189a2fd38df6d8b1ef4947ecff7e1dc6a1f1f8ffb3c6d6683ac40005c473b8b265e4a84e98615b1cb9c9c612adcee42a0f36c7bc7af54d8aef7ad59a65b41fea9a30576b9ded1e037be58be0a64126251c3ed87af261214be9a4261cdda81fb5805734f49fec7b78c4abd0eb2682e02b7de03ddfc9bebadd1bd75a79f39398c52e4c1d6357ccb02fc0974310dd952773ddfbb474eff14d9197946f7c7a631aed6e6812052f74b5dda2c674ec9f82fb60ca0be357904b7e4cede59c3fc375b200782dd782c9b93cd373fa563b3446194914007998f0bac245022ce422613265c63dc07ba289f867c1d0225c86d804493a26abbf2e9658094eae697b24a3073ca28487f9bd7e0713cd142319dfaa581ee237a0cea5b1ba548791e06b1f6728d31de1a0cb6a3511970f5480ee3e90f12bf20b12554e2168d1d7421cc11a5d01f88f46f6c278d28bfe99b28968254247c7212de088bc15d2e5c0d1c1f3fef7b40a4c225a042a19f9f7be014efc227b38c1f90e8da99ca393b1a421212edec3990adfc863602cf7bdae2414c3a7f244cfae60888232d08ea08d4a8b9696aac14da4edfbb468f852d4ec46ec27ff9a9b27ccb085908eeb961dae9cba3ea75b23d3dbfdb5c1cdd9c38c78e6bae5e2dde31a12762f809ffdd0dd4b2932575d66cda7d68b41f8c6764c4863b617eaa324834ca8599a34eb0e8abe7f55f5c787fb74ef42f57c0681dbb39aca260bdd23bb156d0dbd2d16a28bd3facecba2eea2465445113762b4ebdb786775c6c872eb013a45f609b23468210eac6d8a92d58db6bb9d8127acc7245f09c4c288de4476dca56109175aa6a9c06d2a8a56c2f877c7651f033fe6f38ecccc3e51f2c979752ca7a905c9d3bde1f3bcc92642f4561bc958c3ecf38152aa3231f9ce7d3efbcb5524a3edcf75da4e0b7efa0a5c5842956357967460f77976a5d4c651868369b44827d6b98b8f048afdab7ff18c07753a49defd422d9d14a80dc86a14d10779b1d91ad33e8b31d6fc20ceb85e6ac70f38a007729850e47e6658fa31893e296a8b85148e3241b4b92b1d4a169e3536d9ea1741d6b8e7769c281be26d0f1c49e011c9db7f7dadfd8cca85f3492034ce458bcd3ac18d4019b3b7101ca9765c79f14b901868c2020b26ceb95bebd30b922711072b6b7bcb3c508fa0dfb7e91c85ed6343fc1371cad72263cf2ea9c43b0e07b3cc32b7947f696d5a973de5d964db3702c2549ded7e84b715031bc4293c5ba4013891c75f5413256ebbd1621840ebae00ce1e197c269e7df3c941dd27b33cfcaaf10316ca27e7c9f33f73cb1a94c02846583cdbd06c17b1a3553c6e47ba2b2936761c3d9587059358907f738ba5d14d71345784189b2b178340eaeee4e5b20c25c23eb75e42547c599835ba8fddd54358ba73c12658da85936fc052f4c9f86c6e0139f896ac5e46538443d32904dec99093512f7650f1149205af00bc05abb6b17d23e076d10cb013799c0ad3a6eec6746b50137c3a01f44985be1158fb573daaa5eac9d58436ff3d48341ea3c585137923d81bc4d9ae770c46570776703997a772de630a7009112b4c8d7159b3b0e0b9fed47a7e937a219c5119b879509a7a7980c71c1a21b3dcaf4a8289a2546bfb64417ae25e4b8d23c9d45d6117a4602d54403570d4a1f5ce524f05da730182490c4a4f8eaa55414b25b8f8ddfd9074b6ba23598793d8f67c70040412e3bd295a3a98b500b8a682a3565e188bcc6fe20a1003ed3635c083c528877904f3dcc72b448b61deaac5de552d57368b56862cb39cd0a0192995b2b599a0d36a7065c16af8f072a5f8fb429e0081ad12dfd1a19b4002dbe70688c2e2fd7b6b3d145464ae94f729a491783bc1c158c9562d2d09548b1b34a6dd7b71ffa035841ee85dc5ae2ab1a94ef22d14136b0a4e1567481cf1cce39ef128838906d25a2801d6b74016353fb25194b9dc002be8f2c05f8574cd59161f468fd92cfd17e3a6675bfebb03ea8ec4f8d727ddf0658c2a096d0899789bccac1c2d13d93078947bf2aef40b1af1480242bfb98cd7219de66b30e3124140c467ea989cdad6a1517db37f208801842d98f9ff6c6faf2110da7b9c8db5e3c62ee3f205a5b4ed1b3db07bbc79fd669d85e65e85bd45231735dcde3aca8bfa99815bec953e06fa6448f27c2ad29e9ad43039a0ea6fb21ecddb0ac6a9384345fef28998b7b2c34e2c2dcf07580086fa91751893b825e1bb7df32c4fa83a962d69fd374486561ceb9192c5fa61c1a9b6e759248720cf0d3af2e295a1d58ba465531b5831d8a1b914a8c1ed0e25cda2d4bc2066b8f350ecacd463329f3e8c0714b7a2f330e82a0dbc9a7b4f2bc87dba1ccc0a73f92599135af1d76750479508ceb74be7fec7fe9ba4dd8e1772b4893afbef0b71829bc39cb4c5bb8a76c4ec6346f026b80ab4791d9c56a95d607982ee5837c481218ab86b0b6238c34eff0522f4487b6d1bc403bd1c37c0acc42cbf98fa361464c7207c45858a0f74064a580ed9aa8613ae488a8bc1f401130a531c85be80acdfa0e190d068cd7202eaa148ef1c9dbfec336cb8ae3575c1a063e1b56c3a1ef1fc0d29cfe267bc7a54eb642188676766c2568087cf429d3be54c459a49c9e20a2fc5e1c0dee52635715b696e65a7a3b62a93c1cda05b73cbdbd7a0918fc3cdd21334656818ca1fb5f3341b80bcedc3a16f77f66f0ee4aa30c4aff149fa3197a43d4b5c30e3d1d11bde10956feae925bb5283a5ecaf7b0506830bcdcd1b5eeee50f61d28542cd4614abc20a7962dfb42cb6bfaf11b26e4ee5103eb7080e9eccaa4244239436c6824498f4b99702b160e5fff77ada3d68f4fb41a7470e312131f72ce901070563ba1fe9f282e5303601e5623155f2481da5a1a17d91c70f02571eb659d1713abd45923b041ceed76ad0fc6285888f99c6e34a90666230684863bb18634a72d2d6d2d2bcf4109774dcc4977fa0c2bc66ca51acb16abce184b2c666a0c4a492d6eb5d350b574541c0644970d75772d077ccda11bbfe6dcb9878b4f8757a6ca13d69d1901c8d9329490134e9d40fb23d749ab3538411196292730fcc110655cbeeaa3ded9851a3d10d3d9b1ffe0d2e092110b4c4d548a9c19fdb245b41ead35b46b41a9edefb40191482830c92e94e348a07f218d14a5f1d5824bf42875114bd9265756003375a3ef953b7631e80652c7a9b266950996bb8e4c8ac66991ad40752e3ee247b54f7edcf565e12d221fdaafe5e152a041060b93c9d4c384936c0ed39d078afc4b5e268cbcc34d18c0de0c59afa8bffb9fe1c4fb962bcd9fa2ce80f68604f5f4da2cd7d1bba56c55a53fbbd7420084362e957a5d70f697ecc88d114f9d04c86d26c65ba121fb33b1abe5807eca71965093c04d9e576080029cfec09e47f95a3df29d30725fce274a9c5493250e2832a2b77d9b620671e5f7e5e90dea87544f888d689d620202accee9324a1c8f68088570d0dcf743a5f8dffaabe6d18bfb07da6ad4a6a60a9e1db60319257fa198c8a1875d41b169ee8af9c0140d1c98712991ba356a92046ead585b87dab195fa681490227ce9c028ee3b4b840514c038b0bfb4e078c98288afd862095ceac265cc2f4a8ffe7437d50dd6b80d24adc286ce7a84a2960106e56ec64dfb4bda0ebacf46f2f4bcf7bd925607599d2003f86fc1001ef862ff1d1df947682c35a0af309395d8c61799ed4708ae99ed6866745ee3e0d598646b311819385145cef1361585ae41fe08faa5cca47ca9f3ed03cbd04dabae0ffdb5ce00e938c5cccaadc4dec13a52756c9a0ffa657c60dc5c5bc14626bea2ade2f981b0fdda1aaf1442586911b7248340575fdb25b0ab32047b003f6841c704fb7229d7c24082ede377dc021e8e5d81c497c6401fa423bea20eb6be231819e4b374466f80caa3af5bfe55613dc1d83aec5fcb6bbeb50e6ba41f03c0c60485d5b749611e0d2e4ff64ff10e0863a19da84be9a70e5e72bdadb5ac931f62b1b5f02c40cd0eefa39794d656b851aaac32c233a1ed84ac31eec33b97a8f3b17374df3ca94b52af0604008fd05708b9292027965cdb7dfd711a14ed4df15834d940a44f27a69302013ea10a47babbb0162c2493e6a21092bc41bc4725823dc16c69da7f6d93077ff6b1337a36969e6386dddba2d0f5b335ab6ef2e9107db995260411e0bf329c544f0ceaf304eede12892337c8607ed5773c9df97dae307698c8fe9976edb6899e799d1491866e4a9132a9c1b8cae9325b64a65355a2aa9c4a01f8494402c7af4b6f3327f58e23ca78d712b3c06b92c5f8e29ad57da4498137360ac6e2ee6b20bc81f400c45b3ba2e816b4b51f420d3a043876b88299cbc54b5ecd2003dcbf4e95d7932c89a0dbdf445a59c9de5ddf571901c566997fcb6f98f3f8ed05b7024bf944d566d9d2e54d78ee53dcd9e0c6ede698d9073d4411c7c262b5a57ba8e06f7d638dd7ad07a532a1958640a715c480c24b481a10f7b0347b7c33b6d0128b05303a41dde49b7da8aa25e6faaec261d9fa77826790d1a532570c085ac04142a14410c9a57e500622a1cde80686100ddcd48c581f769a1b1f4fc29f123cc2d37ab48cd00c0a7fa16521a6bce57610852438a5a41b5c74c0bb46c31134ca53143dbc8560ccf814b402b2c03298b8b4eb0cf97769878e13aea57f64f33f5ae4be83c9343df6309c642fdd18b0ec5f64fb3ba9f8cd71113fa415b917b9d3521b85372a60eb5c443a9498400d83780e8a21e0160aab1b603e3e8167f6d52648e48398843804df3ffbff7399d81cfb2fdfc0edb81d6b536670affb83b38bec67e088076e6dcaf5551e7a4489ba4ef976b95f39bd45dae8eb2007fd6bf1d67573add61ed93626755342d4c1a44055056d021d6e239e9f3dc54f83be10403a70b6b8753b45bfabc71cdb5389e525f68cefe0e014f250b6b93c271fa4918b45721a491f3e1c6f3c5c0c3f4a951a6a22734475fd3486326d73d9571339a117349d433c49af19157d1247751e471e5595a24d91bc437bb8cd34c00ad737fc1dbf3fa5fbc7e0740dce264654be5013a265e4b49081b832a9de77643fcfe3af5358a3a99d7924b6233111d490632b71125828360bcc49c140ccc5dfa4ca4a0e6b90018092b58631af0c1c08a4e302e36661ad5c933555572c6efbf23237177d69af8cbcf445682f05bf6ee248523a99d785ea4044c8c85eb04c3c7da68a0751005fd40b46a16d4fe82f5d97aa57b7052b6ce60519242984d7aee7bf5f86c04fd7e409d65fb9d001d7d99aa036d31c239d15053a83d9b4614800e1d7456811c54530e0fa7c31a549b89694fe5c93818bb4e3e0014b73f0bbb23ec711a941f2187fc2c38e6fa032699d1a03e30ed35a3f90d89be464947c2229c39480334b229f6470007ef50f88e3e954337406c4cc9710642dd079f699fa89e03fce75349c27d87a77592d8c8046b8701349d9d102c3d79cd9bc7201051b042228089e7c58ce5e8fe1e90deedf6ea5c94299b4c4c71bf938c785c6bfe2514fc366b7fe082c230d6fcc66cb18617d552c8ce9257a415cb42211fad918c7e526312efdce8c3b7e9dda8fa9c6d6883d139a9064c857b6f72005f6a5c8f78ce1ac0a83d749628175aab1c50260fa232db9538e695a47c2b15268de29bcd233587214c1c5e96ef1b1d809812ca4bcaf02cb05d0d0cc5838db9f3ec3b558be6264b68e92787f1d762ac150db1d0f7791cac942664084673a77a427aa94333302eac0ed11b248fd6874e988f9f5f5691c1337de4358fad0dbead55607a6c33e332d51e9e23595180047ed7d8ae52bab5aa12992ecfeea93d90ad07d17dc3839117f79c498e1f44c99e3e225f4b341170aac0c633a45ebd07259ed25382a070ca47c744da457988fc1153643aedd081af0a34df731e1df4e92f075f661d0a49a12eb77bc8ffb2100f4a9f531ee130e23567f85ca52983901840b1957c405b2ef82dce2e208d76e35743418be58416ed084ce21e68b2aeffae948c5a71713c0a688e1c0d4a9d34e25488248a24868cdeaaa8333d0c048434eb6565cfab7369faa0d92de2a83684ac3023b39b18053742e479410284088ccf83ad016cc2e49b7207dc90331850cf754915f4343061884a585c9b0182f5cd390b16ac098714d3afd90378b7f59d1451c252130f6f80eb51ae5b8c7eea1174a34c5323d1a6a7d056a75c73a3954f9c4ca3db5505385f6bbfbcf040b6260a3f135ee62fabb62f1bc0b05041aa536e9d7123d5f24d8530e538b0e8f925a02c54000d66b34ef7f94b7b8983682ad135fc33076adcb3e7cd612272b14618b7ea3b5550418de4ec9e3cada5ebbf9b23ab0c32363754814ba99b8e5eb8b57d1ff423a382a4d3c76f27fed77f7b2a84ccad3de1bb5922044e1a207433040f5102ae247e1a7d6b52b0b261da426df64a2b4b75e6f1ccc35c74983a54280c87b3b1914920103c404e32a06a2ca79315b1e22ccde9ea64bfb64f01f7243894e5d5194186d3ccb091a8ce0128a584332c28d5ad1bff2952d10a974e134c8c758f491f00d979768e6fcaf01cfc04a8feace6325f1e2ca716e165d10426676a2af3cc6ed67e83fe6a88628378b7ffdbab527db6ff5b88fa0cdf6a82e62e44677cd6068f14f9e7551103e02da444b84f172efa15f18b4871cf06ec0c4c9a1c9db18307ab0d2b0a56c71cb9475c1eb308235699308ee7e0abeb017db96663a55b602bc1635d80afcdb9de3d8f568a3a037e44ed9466c6d331ae3469a842bdf36819f80f4ffbad664592898b5dd1fb0cbfea8017080410c9575e0c25fb3c4c06029e526fbb6bbeb989d9d2ee761d6011e6936241f82246ada35ba4e8c1cfee829632a947be7d1a06188f16db35f45266480f2f6efe6eaaf90e2c50e40ef607d447031f1c4823c96fb01e7a52585afdbd6db16e756609bf3a8d33e08f497b5a780087aa6338d09f43ef37a6284d3bf28c5360a5c9b72280a039fc95d75aa2cc6ad31ae8ec4156e7a8e45a1b9c2c8bd006cf9b144a6dc873af687c5fd9d9c3d2665b2cb2a01e1da87379d538231c62048a95c0253ea30884b939b0802a815998b973fec783120b4fc6d359e08511ff5bfc8ea456b0068e0e4b7388924c8b59a247a6f5ab60baba0568f3c4cfd029b434af48ab3d9822836da8600e3c3856d1a4df27a7e84f4d49ffaae15567c30287adef92d518f3a8d72d231c51a607c812d79526fbea80236443e81295bf3eb2ae737c99898ffac4e9e90228b4b64351695c73682816f368b16d36c74761688f19f05e4f374c71d517eede16555b2a33dcaed7096d81c5f6f05c1985908ac4271588b6bb091478e843240ab77890b8ef887a3462c4b4f63941141536408f1ae0aa749d732173c9edee1b17665bf51b606a5171dce2d1115784998c1e430ddb86bd4f33a29d550ff3daf7e04cc4261e13fe676951fef2f2725eba5feb9e4e7d2e17f32bca7d436f47f4589faf0ead5cf16071e384c3f4757733308794e749d668d01283967416866edf1bcc501fd2a89ed6a8866ee43461393f0a5a579232185594749b415c6718557c7ada3d4f39d7e803e212e3efa1306581d12411bf633b092c1d8a902304b6b1c6c555f8e48219e68cd39b69c6d7232714938e7297288afd38c588024dc37feba27ae070b566fa58e2436b4cecc01bd3e05d6b91012d8c64901b4e433e852b68e77e06633dae4d981c59150001cdbeb91b94c403887a30939b769ec7cbc266ce1bac41a86f353ef4d964625eed3d15682c72050fea57d0bfb1e7226510a18d4ebfacdf90395e5b78dc05809766df5793b157405fc066397969ee40fcf8b4d08fdb8b8f89087b7b5f81c07d6778430df7e63c8c1d9b1a27cd9d1315c141b017c2dfc800c0c4ed194f9f46e10bea5188d25415ab2c2ce02a29662579a30e53dd908ae8dae45938f1cf1a257809dfb8426f5fd6b4e83c6da0d70a60943492424120ce6baa86fb511de84e978a43044267703ffdba5f99889b9ab254358342a305c656e574740d93833a49b79ded793b720d623104e9fcb3f2696d6dbe2d981e2f3dbb1df784f1cb98e4e3100b490d2b413c7c69cf6851ffca3f28134a9babbc509fef9738f8bfed3e535dbedc607ea0294aa8c207c12b0f3d1e72bea6d385f10be94cc8a8b96bc0c8cebc69cd16ee24314daf4079678944bbb12867e1b03f921d4b88ffc28c5e3e7b631fd23289e050ae6b50279b90790a5b830142388a72659e8953882020ff3dc6d46e65c16f0b397c55c5a035da1d08115f63a55466a1493e317e88be2f4c2189db688c37895aed10bc75045596299e65c92c4a8bbd22b5d74cdbd17e1ebb345a2f91ec29428f115ef9d244c45610a067477117b60e4410d7907bbfd8fdf145c3ae83429c00df2e62c9aca7d282c21cb424cbc86431804a29457b63b0d223e8c3884ee05861ac5ce23d84cb102a48914696a115999ac9b3de3dbf8b56a72c881ddb8f790a3cfbc0d7afce47d7d38b6dbfece5dd70ed12cd81845bfb3a7329001ac4584ab5788c22c833a0f5d7ae3a31eeb0cc246fa76911cd2fc8990584f6635cfad8c4ef831c1b04d89c181939fd5128fd627be407ef1cc8fc7d4f97b845b8042e0a7f8b4cef2c87373c8689bf8f74570b94509e60c1dfea64d69aaff3aa90fbb72aa6ab203283f72626d6c9114718d4e297c96767591611128d12c9281047522e50f498c209303b19640da673aea6082c6df8d9a8fdd5246a00d2a15c8dafdeb1b067ef31bb3f084beb02286b75b70e1131f7d570142241ae28c7cfa1ba7977c4208d02b8cf97b8eed1ae452635d92253f81e37f8097f698141a0645ebf7a526bf452ee84c07b575c4c51c7e748579ed3d478a9fefdabef598b6222dfc7debd8589e30681cd5ac9930687e7199ecedbe72d8ffb2b41d1405fb5b61262567ee0edb5a1b2bb69246d2d22e89f4051d1cf29d6a237c456749e40307e78a8ca9f65732d77168f59133436ecccf1d8350b55beb1c97d09b4c9b80c0ead0d384b0506526f8c40ff3456364dd1ee10bd59bddc51a89676002b3e90730dc6c54e6b1648869bd569676a15c03754439e7cfec75c1a7674c1ee014c766cf8ecc3368024e17a56a03b56923edbb8f8afe18be705d8321b0830915466cd0df8d7cdc288a98437f51294896746d4f1e08a27b831155e5345143d4df5b5e03f37ed514d7ea5080461790429a587811494217c26729c1d53d4a22b7c3693c439b6efbcb42171a77a80b639438da0800545c4b518b5cd1c7ee1ab77e08be63b61cac396cc6c3088d2f164148a8a78f91bf2de3dae84d4985fc7b4ddd706115d77fa188adda61827e9e0974cc0a24529b2a032b1c72b60580b6ae7cfa709a91a644cdc057d437061532402c601028e9faa5c44da5f2878983691fc02937130826d4569cdf5b405dce0470846f52323c9a3bea78dad4b0a1b5ae1e05371f81250f963afbfb08eb41e16e4981f609406cd8bd578e0fe128351c51956db83f4466a085a88a99f596d5eb0d8d4c57cde1aa3f489c22572d9261b024ea7be5df6e42fd595c0ff146c865dbf888c72ac9bfe8503e6c4f47c08a87db980ed1554c703602464ac8a303e198b6f95e3c23d17a718237e78db4347b918eefcd4b2a55c4da365064695120951020d7a6b0b5c5449202c2ee3d91ac035774e57096e2b56e2831fb77b390a16dafd51daec6c40c0dd210f14309edd1a10cd83f15cb5fa4e213baadf4848967ff6a9ce97d503883d4a303b591f2b149a2d539ee5cc69c783549757be7be9a962f4864caa6d8295c311800931c53e85c222e3279055e0a22ef732cce0f44dfd0e40eeabd1d859e3b351f0075e92f269f3425d1beabc9adfa57910b687eea16ad20e3b32d4eaa368d3c01a5fb408108322d21e053e80f25acf3c4cf301f20868503eaad73805ee9b607a6d709e6759f984a83226bb0c15720832e3eb36ad088c1f8840a0d1083181bc0130a906f32714bed0167d53e452f870f716bb8213cabec020803a2cbc32a690616c5428202a78c8985843dcdd97214745fcf8f0226c42b8d5129bb68ae0095d2e2f70e7c025d67948f9f84438e0f8bfb76b057b83fac774468c804d811e1016e65dc2ee9a3faedaa362f20b42344ae1f581f4724517d19a53c0b1f109534473ab9e79f3f24925b5895034bda6f8e7477326a3afdc974e80ebce78f87f70e475f037e3b7b8cc3864a48254923553b65d64c051582058171ca5a7d35f412b6be872c0b8a4acae8386b740711412ea86cc2e7d399606d20fdf0544cbeae6d8dfeceb7a0b87f0335f7925aaeec9f5813393c9b138c666b0cdf54b9b7f4349c0dd23bd46b02132b6ee7acbac61fd44f0fd669e3890f7b811178db9286b769dc4de16bba63c21fbd6e0a4e8790133fe42c22fbf449a2c30f5ee348a62665f5595a3c8fe5d9408f73503dbcd22da63a801b5b8b6f8b798bad81bd290fe1665fa1fa386f0f41d83ed7e453c1df6e4b0724fa163ee5d1a2c2b0041930bed8a89a997300445520f38e9bca0d2dc9a1e4f77d871fc3f57d67374a62cda7c4d0b75477452118a9b240085dcc3e0f0ca2a241989b3b8dd9957423d160badf9d162c7a6def77bcbd0242d15e2392f8532335534d9fbebf7fd4dd7c6a24d8d9f2c35a96f9258e1be8f8e8b2741206ee1562144610e39534f58893bea1af153b4987494efc9afe39ebf73ff5a6902636f43ed9b5c6e03f46aa1875fc43f1d17e43ecc0d4389a9722750623b6c90dd834509f7fd1a58e42c47fc37c848863d1581ba815593fbeac3244c73242c06e810b55e9a733d47a1a7e6a07f467a14341e2e9008950cc14e22d1a73cff5e597e8bc5925384a841cbee470a0fc773836167ac99ac876371122eb23909ba4caed2519fcc762e2f227e39e90c2169477ce435f3bc453947981b61e87f69cf80f3cfd074b46033571b8e7484512d4f6fe33599c8206c6ea49396572689d81833d0cd5f8d85ea1fd334485c3301c7fadc09f44776cafe8d66083ed6d54afb0c77f261831e04858769632ada1678c271228ed89f036859c2af0c6efef13a01201f2b5d2dc12763e47a9e8d2c7b990caf5f9356a05a1002958042b7a5cdb7b7107fecb483337b1d84681a4a12838c4f68702cbf7a5249cb13885271c926c3c41945260af0e4908dfa2e387e0d8e23e03a767a2bbe6160ec1fa6c9cda0529457e921c4289b21d451acfa7af0a8599510f652e377291c94a7464d5340d982b0e85ddcd5ebcecf33ecdc12f24731af75a795a6862843215f58d37c6c80e02c99a24ce0d023524aaa0f07b8ab211e9a198408392f6740aad7e07253de860e2082a3628252d1e7f4cf9d5de6149095d0bcf9d829b53895749fc3eaf5f7f0f5a162841f43e520a5af114b458533dc52e04d647f9861a45482eff6a577f9f7376d1a98d64e572be1091ce145928ee548cf2ab4d752c726460ff0daade17b97c2e91ef9d718986d02677b45b50ee6bf95ac6123d97383395c1fbf20bfad7887f25a80fbefa7d289da6c9c056cdeca6e24b763cce894cee0bda816f6df989f9b173bfff706694346b830ebe15c0d1a910c553041929e9819d82e01f271b4ca8c481eb3305c70e10d6a02188be4bc0b99e6d18b2d0f70c0f8930dd4d43fff0365d0400b4b280e5e938abff4661efba48725c3e571a514edb92a74b3a1b0b4b44a808fe802870d0d5fcf84e27f6fc8c7e6bc1d742100ac893a17fd17dbde95e2c82152842709fade47c47da89f8be8e2c6a4b5a47c6b10c07b86d14362fa7128c33be0bc9b339f45c22baec05d1e925ef37c428c54369bb797c718ba0de36a0657ae56edf92dca98d04e16b24f9aac0caca438308ae23bd569ced72c424a0316d0f8b1509eff2334cf899b31aea64d0200e2066333a9b8a80edf8eeab22fd0bdbf30cbe98f84024047e96a8fe3105ac4a640b8ceb648ad7398050f348975c5fe951d0bc2db834df01a00b458f3de0ba4940a993d8f52b54bb76cd9169babeb959d9f26e8e197a0297d98b35f5bb04d40632ff213e271f6069212b97f8618ce5f215c112127688a5abb34d4aa09d452f805b0d4bffe4d598a187b526588698969670f7d43fc60a79bdf2cbd8276edbcb528018bfb4c89bd19426015ae0ab45d1b52b585a2a06cdb6368b652b60e938c2ce79344ff2cef1c6f646cea1b223ab4be40c3b3e99171d55ff3f1e11a4ecbf04a40099302b853f3505ce79f63c9e4d659ba2c9712147e995c908db5899f44a6ac6b3968ec1d1e66ac8113737f1fab807b79a95db646cddb4ff908bcd90e155f53fbbe32c98ffa6003c6bb6342dcd4604c3dff9e195d40f041e51643b7eef36f443e6e4c867da88e450762bdfbd98b3fb4c86db8c73b7a386c7ba7c6171d1bdc3b12e3aa39f82922b434802ba951037b195695b4d49e1b31c479f1679b44da3d8476122a8729dfdbc771b7720ff95ff2bc2d3255c7ed0b6124b935212868b1c80d43f4898102de3ec12227ad36b039a0bc73886dbff0b4e1dd5fbd2a4850b2b5401b20d0845ad7d4602cd4b0fb8b033834d2695a2fcda1d848fe40285a2085218ca92c2d9d09a0ba3b7c0a47b0bc3019e25d37bd865e35fbdb4ae7f1dcb3bf5958e6ea3490ca1d5ea21e0a0e579bb2462a606187bc6bd6b0f9d8c833308c945f8fe37000fd898c567e9ce82520d8b670e312b04d52f0126fa782684964128bf8e25d3539ab92e824e762c8ea014d0c5f50284278bbabf685d831b4a25836bdaa19aa28f42bf4312fad31b3fe9a5432454bfad8464d79a220eb7201c71704e1dbbf37ddc90cc4467f6de2743c02f68dbcdedc80780f090fce404bf70cc4f3075c4da5779f112b650348df88054a881c6ac61d4c58879a81b63b930a92888943f8f09ebc7ac41e82275916dbff987e4cfcf9e0a207ea4adf433b9747924c7acd8818b4db2ce8adeedf91f745333be9c9022ffbafd81c22a271d0884c5c8406a44b1ca801c721d0c37b2a040d2698aa0b11f51435291f3ee8355ca0a3ce17951f14f97ee1420fc0987f10f80baf1279c1913f08049cddb3dfc0946778a5ee55f4532c35b4e59810e5c15bc92919f302689923af3a530aefc0495e0d8414cf0c3027051323c8b693ef861b9171a21c60ad7219b0de2b777031d2d1909e779aee709b62bfd4f930f0ebde7e269cb10f15293861a517c7634eaf095e8e4a010d0a559866bb9a115aeb5d1b23df5b80dadc07ff78bbe16a6de4a8bb9c96dd45222f9691dad32089a3baa5fd2e3cd02f708f5dfbf16ad704485893e0401cfe41d9f5b8fde1708b764b9dde8937c10218bfe7a7ce26d55a197fec0b8cc122056011fce12f494a0e580f12bba8bc4b383621566f277472378cb703f261682568b769a30d127a5647f4424abe1fd6e95bd1b0c8152b66e050a87c23d36d3a757087a93fc4dd14b58b8c3f5a3bb8ee8f6eed93ec8355068f2d8edd51c429a055041de5d1e9ccaee8e9e69e51911d3cacb21f1c8978045e6e91147f8b1680e00123efa6d43833ebf308307a3ea80a157228c885ff219535ef270b9585c21018603c31e061582cf235a159213e8a74c27fbba6d1b0d15be0097b01c727acc3590e96a125aef25614cffa125211b2c953fe4ed2e8948544084fa2d82083f05d15cbd9875538eb5c21abb7123ff3e6b1fd7bc6ff468cc3fbafb29a8d9000ca8021571bed6ae66ac35813dc1b4ebf78fbed67b573ed5d66d141ae4d50a5177467a224345a5687e31a41e2b179a7bbf372d6eb5e83ebee9263bf75fa2bbd35aa22156713578bebcab2632057a949bcee59d23d9e469d13180b3f28fc2700d99a5300aeb7226fa041963e1cf61eb2d6a20921fe53b3e8065b45f5d2c16d52036b5413c54b9615716aebf07ee577565a3064a65019d69d7cce451cfbebe8317d403f1c3349f09234ab4cdaff2b9c3205c82f0a4240c715e313c89f93bc1670838915b2f97b73e744029521bfd9a318adb9576d25d290a3ffb804c8b4eec73a1a32dc140adc3b8352f5323200fa604496f6815cf19ddc60315e95cf131416445e09e71838d105e5e6467bf374ca3151acc10bdf28c1f4aaa996794bdce4094ef6666a65f62649e80b3408bceb856092f1e7724915e8137af0ab4116b0d55b044f28939a3be786b660470cdc43037455eff53e1810a1e20cb47476660b1b87b0649545573ada15512ddb6d4e9922b1533a05dc5e87c9384fd9434806a5b1912c1d3314b5279d9e55e506253f3795e9bf31b0694418acc33e9ea731071b9045c1c404568afdb9e9634b22ed85a2160e9321eee1a8d6cabef6cd2dd4f154030c97c9637f79e0792271f68e972d02b239ad549e1a0d8695b058088788793e61ae15ae4b67861c8f1bb9ec398a22008d241143042375e896993773f3c0b60dc2398f9ed09ada86a401f495a5df9ceb5ffd26fea8dab0b79d57be335e951c64c749f5d59b9c8d7be17bf418dc90dde6f6d8637aaae3167f696d026f538219c18a0afb0e7f666e9afd46d7db0bfd8dee2114f98a49fb14dff5afabd7fed441b63ce64eb8e8befc1d7e68df20288bac52db24efb5b38d62f9255d7bd94ecbfa9f1cd0ce67c3d412b46b0d3d2148df98185207529ad7b6f6a2c332faf786f7a90af049f768e320ee7dac4b61b3595d1ace1fb1ade0653879251e12997646bb215f5d6731419d48b264ec5afa4fbb6f46d5aa7d7516eca527419983ee55acd27ab79da35e97acbb86596587dded598bd4a57ac1a278dae8cd7f5f7d246b3f657cef3fe6a3463a6d9db79e66e257e1454aeb3c51e35a929d050742819159e3255ed2ffb93ed6495523f8dbdd24dadf71c458edb262fd5bb795dbc6d8f9717cb6b30e6096a84dc4ce57a91b14f6810d8ae5ae7b549dae4b297f9041acd38d572cbdf6c1e3293b5efc9d354465db3acd3cd64b195fb465a1e839fb020a85ca6f61b953c424381e6ea70daed25bea16637ce351e9fc1633479283114de261e23a1c0586824585d1ce7db5ed05cc6b24657eef3f669fa1cb1a5f9e2e5a8e2bbd647f01af78a4720f3d313d82b79f5c1d1ea26684769d8521992d041e4e8f245e371dcc0fa6fc08a50807677f977c3cd8e1905ededdbe28e7454b9b4387529ecc312b9ed96b2293bdd9c6bf48bde18ed74532c34b74f6ca0c225dc09ec6895d342d8eaaa0c056a7403945cd5f61fac42f84036cf2b6617da5fb01255be1961a2612a55c7799b428a77e2766366e1bbd6da563dff9a0d2fd08d7b722fe07e97080828f60c75a337879aabc444a0bec4271e93304c16b0eb894f201a4bb22af72d36c480fbd21c152456ed966069c8b91a236c54ea7a8f5db18038484286390fe962b3e27138c86e28809fc72ff320fbec63849bcdc3a3d87307968e1d31f60c590649618849588bd6a24330ac3fabc6e83f435ccfe4c92fb79764d5e17f7114f46fae82a17e5e782a8e26c8f96b390b9f75f68b63f5d2daeeb497362dc3f16a79485bdd99e3fadf7e20ee1d6f9ca2d51f80a4214d9e7aad6aeffe0649144bad7fe9dab6c09be4ec97ed29c6eabd724925fdb75a63400142b238f67c558210cbb77e6958a07053270c3d90ab57179d3229a64d9e48e7fa3c6121de0e45c6b8ed1dbc84f67b6dc5725d5254daaf5d4df0058a9b40adef361d4ca70be3d068a6f7afeb2bdfb0b1d632e563b5865a90d3efc739dc31bb5ccbd91c0f26a7ca3b0e4b316ebcba9acc4c001cb44b6a050e7f972f9a46288b6571f3c76fa41b4171b864e22c2ff39d10f8d8aeb03d21ce4ba8c487d43f32340000fb26a42075abfddf009d002a510a30619948ee431122394f68fea79475f7f0f103dc9f7dc760b902e61298c6734e9885150a17faf0eed7fc1cb81caba74e43fe29c9e9aed9af569ff3fe3fd40ef48eb5626938b34b36b56bf79cb53058e101fdc65789c0a3d374127e9677dd6bca82a249f8e00020b9684e6ce94e417a74183b9c6a3f07238a6a24e74835ba28f07c3ede57e05c19735b9e7ffc6997745544f197d1bc8a9c03777d5d629fa91c23c15b42a49084c47380243e227d1985a35a7fc510968ca31dae5904915c720f292f7019b34d5f060e7f22a3ae1794f12de2eed64e4fe4b47fbddc9e11528c539298fc97819560b2c4f68f39cfc3d66dcd9457afa15d9343205eca96559e99c8b3671abb2e4c6c1bdec6ccec4bbf014b4eab71cac3d90c569a0a66ff8870d054d741a3126620ffc4e187db762fc92a8469cbe81853e5a69acf44c2ccc56c84caaaea426b544b1417e2986395cec5accfcb0e3410211a34e482c84faad8d29a341e9e28d4eedf57cd569d50116b499eb0e8f59696f0b9a706c9b2208b9fb14be8d33d844ee5f2204c8532e0c1cb754b50dc18ec8a5365507e30d4da5361164fc3ba61f8be71ac122b0713354dae71070bf56116346dc6303a1dfdabf38cbabcc7f8381bbe7a5b34fedeb2a6cd0e1be4d350605fc4d620815f570a0b35e06359bea2385fcd67221e2321a2f80147c44b6d8c435bd94b469c1d6995a14c51a31622854b97d0648cb745baa1b7914d7c29fa07a7097d9f06115a1521a545ac934416cc6c7a5df178de0dcae655ff83e63b4c4ce4118de308cb2efd5f554e60399d22dbc7201a837eb48956b0f8e85d5bf299e4bc01848cc213046755421b31255b82278821c03a2329611f6726421f42ef5b8d6f8c1c440103dc08360cc6869a1664f93e19532291fa6d0eb0c3b83645694c3b878477ecc97833a2fb17fd45a5352f6e387be7ae27b25bf767d132d0ab86ebbaf2803553fd88960578f9f31cee0131ba8a877911079884819423f52e5350abf12332ae67f1fd266293064c2a93eb29ba26d226a8fa6a5009f4b3026aceb95e6050901d710e47d6ba854efec14995ae1ee7fa3cbca4c31e5b99f60143190bfeb2838bec5efa5f08f384db0760118f3f55af72551c6da0a39edb430ce0181a2508a5b100b43f7c44678d5ca2f8e93021a0c07fabdffe777f6f91bd456e29a5943206060c06b105f7de8b6ac1877b5235be1e353549dc1e363e3a7e66703aaaccd864d4d022b09179c4a001c144605c8b7b88616a1bdb01b3a9d6a29a7c34be1bea33c6e3865e43e923ff9471a15a6e5f52bf608efd1acc61735ab19f83b3e69064800c6a9f7fdf4b73fb77efbd74850e750de6d89f418dbfbe8157675aaf9777e74012c8f1ad63d169daecaea7698fb5f05ba1c1dd2de8750ed47cc5b4b7daeb690f7e1c073390419a16de51d755838966a0219d3aad9d39f41eae1e45f4b4d649acf3ae87927eeab1828c839de281521e3bf020f28310fd74efc598474fce596f3ca2703d6a708e9d7a69ba47941e34d87cd810f381d37dd87451f798a2b9988f1ed6da7b392154d3b49c99701fb66ddb38cefa88e2c389f723c7ba623ea6807efce0ede88fce0953dfc67cbcf831feb0f9b1a49fea0a04027d1f0f273f860809d9a91ff54747950b92814522d0341e19c80e223dc476c0b69db4346148136647d9434ebe19db31db3104a9b48389ae32e26aee9812e341a3c84ef588e9f0d1e1a4cf980e26bdcf980eb2cf988e57176faf3a7474dcd0a94e9f3fbf62400044e410841830c6181391430e740e3db8b8073244d13a339e157ca89eefe7bbc1477e3a3d8f42f719d369d2c58fb1319d034a134a69ad35b11c52eebd17631cb37c9373d65aa7474c87049ae3b8bd7560311d241da7604ca7a79fea6ab3c1d6aa237460e04086941b3e9aacb84ffacd41418d8fe48362c1e7832f470e8c24aec783c60caf2ff535ada3a1aeab596564e80cc65496a8d231609f76fa52ea16009d76ac54b77fc18e05cf13a63ea5dc82df4c89128061cfba593347ac2b12843d653bf8b42eea188e1702c80963c22b8b090820b1b0a0e8338663d6c5125e9870696151a550a71553893412853e90d7f1cd6d59c3b90473565a5f3012857cb0e9cf580e1a28a5b5c67094b11c26c8e9a7baa2f596a0a200e4757c739bfeae0c3120720440bac0477286f0d84c7272c0c018e328b11c323ad7672c472bc7d6543c52316a80aa810d88f004558423504bdca08bffcdc84117bfd4a8039492aff619d361073ca8f97f145b9f311d7c5083030d574e9f5d7d13aaaac725801020288211362f1834588189c8e146e7102307193ac6a9285a8725bdeb33a683912e7e7f6dec9e3a339df844f999d2e7575995c9d13dbe9d13920a8b61d2e757d74f9d61814ee9b3d6cc2a8d7a339f96145667e838522623ed19a993d18746e9f329496fea0c256d20690e497b903b24923e7594efe734a5a6d360a1ca68efe47b4e4c26acd7013c8a13cb47aa757f95d16ac78b79766024cf4ece7a07d6eb6f24b737e71c142d141fce3bcf96200bc527744318d31c9778716ebe2f7473737a7dd1758d70ee0dc65b0c96a2e113ef68442a9962bdfe0a8ceaaa2fbe18db4af1de255eda268a6b7afd15dc3a61d4fd49dd54ca569a32993a167bc5eb55569bd459ad52e8a9eef4faa8575dd98e86aa7258300b8bad9405e3afb22a53ea1604754a29a5b4ba3e0d6f3a6bd75e958a5757759525f8f5b2d47659fe9465599658d32d2d5a6bad35a93fcb699b2ecb9fb22ccb922461244992a4de7667c204b7a0a86fdbb66d9b4faf63eeb82d7fcab2e4ca72234918499224e61d283402416ec1512f498ee3388e6341735bfe94bcb465599230929396244970cf663eb3d96c360b8948a653eac4098e5d314e7ac5b2fed32b9eb2e9b22ccb92849124496ae3388eb399cf6c369bcd5eaf9dd7ebf57aadf0140ddf29c5e2f2e1083c262626465cb150ced9388ee3488e244992242d29a525a56549694943176e17edacd4fafb4ea7175775b5fa3f9dbeef643fdbfbc23921ecfab00b845d1e7675dcacac32db4f599625deec485e18e892de25bb4bda4b72ba9277bc37dd1ded1db93b6e77d477ec5dcf2196d5d5acca683eddccceb8d936d31a7f51d7abcaec1dd1beb6d7ebb5432cb32d5b536730e7241616142806505b345dc721960da1b94c5aedc55a76b95d044a4ab7189a4d95c183244200c30b6e68cc6e78b08b19abb1048610ecc7dc26d506326010e16245071e354a4ad8c4a00811b32a2f70113b3268229c2cb12ab0b12e88f001c35ce08fc9336ce0f810810d1e6e7650a9818d9f19aeaabd3041ea090c4be8f8f84182270139047991738ec1072d2da0268c25a2f333a464ed70ea985283222061a768f47b32e407633ca4ec18e36b87f4401ba28322500c6a223c448480413ac5880c41c4e706112110598207064f0d3c3af004e1f18127089e2178a0f014a1488d2237140952842cd282224d4650e40734187161c406233b469418618191205041f0122d60d8800532b8e0494f1044d8991a1ff9c5af055aa573fb6b07b9c7a13e670579df20bd3eaa167ea4a9001bbd82fb9cadb5964e9bbd1c5c8877e615dcfa0635308339b850770cd2cd72da9c34c4b96b608abacaa0ed7bdbdbd49c277ea06b9ddabef7e4d5133f50c1cada3686db2c0e3b6f63a0df70776d4e8ce7540159d547b2eb5780fefc1a86b3b7a07445165374550f1c6cf81d7471d5ad1024ba8d09fb8c09890931d2c5ef99a07020244a17a2040d8450196233e406a5c490130061c88f2843900c5932a4c91019082a90aa990109c7d7fc53f5824e167cb58a17e339356dcea97a7d73e2a01855c40e0c5dfcca1d183b35ddd4674cc858c39e2844d1cc4fad8d4c75316ba60cb5166b13dfc0239ecd6aa96b8f6f8c15cff00ccff00cdbd4d594c178c433175326cf982bfb36dbd495cda37db1aff5f79d4e9f33b96daa0c7e61e21978d66d4da7f685b9a25a7f5f1ef398b5a764ec1d4eb70ff2d98ceea6db54998e86b9faf928279f8fcb2377438f9c30f6533af842211008040281c6f0beb4c5333b13f2a92ff69d689f2fcae9a7acba8867226824bbfd1a9ad9f844d0788178e45491df5a1b9b8e069f3a336b374e15f8ed736bfb7db1bbe976caff9ca27c3e95cba1064e98da23da177a4ac9475fab945a4f6cd1eb8bc64f466a496a496a496a496a496a496a496a496a496a496a496a490a084a9fc51d474c92a014102c52407c62ad6549922920b4d640b065a4ea728d442419a2e3f8cd3903797d7eea878f565995e9eaeda19546b3a60dbbfeae20cdc7397eeb004b22b1b07cf7de171ac4b2214fc13e91443b9ad9efd49b77bfa3092f147a2d6799b4da8bc33a532bf8445d37a73b1696d2a9e26a166abf99395faefef4d5b4b576bb978584c3cf9e7e45c3bb3b1698b334f4eafcc39e73ce396b4f6dd223ab785eac652deb6dea8ddbdce61d9d74f2d97920eecdf9a009cef9a1595d7535ff47ca4f098bc17a64b0188ca43774ac3fe5f481c24209d5ce12fac43193d66aedbd98566befc558bb58d372d67acb7adb386e6fce6dcebbcef3409482e8474322d12495a669858e48255389d2b0ca08a0c3c6523e2104d101940f08b9cf5890257a6c4a35cd66cfda537dba7d3bb3d73ebe55a7e654d7c59a5642b7dabd9aee589adf6dfad0cbe1b036bfbcf3401704b29582f80581be8f5f3004da5fa86389ec8f46fcda2789f80537a96391f8b55fcae9f6f7ca89a32a0a752d4549f9a935b5f5f353c6603d32188ca43834c7456fe8f8534e1f1f2dc5a5d1ae58028b6a1d8b4e0de68de336c739d7f1dd791ee8e34021916824e2246e543299564cfc84e250285b290ac55750a9942ac5595ab896165b694b0b57b5b8b89870e10f7220682b05358a11462081047e4100acee6a652b5d85fc3b9a0b9ee858b4ff0551ec137c848e453b2561934082ad94044d47d0b4a729752dacb27e4391fe7c6a01fb8a3962cf5a73aab55a3b22b9f7826e8727a7bada41081fd70a693244de38da3790483a4aa32426d9cacbc9e9351a8d4624d20eafd48b8aca93c1f2e1b4783a2e7c08901f5a380b0b0b4b4b0b102620a7600c089410089513a21b201d50f013274e9c40810248931d12ba2000c029c8430c569cf2708397529f311e920800772100548d019c74360138fd4f899d4af213fbe113fb71c4ac23d8a1e606093b8400c60842ec700379e3c40b66089fc80077d8018ddf4108314cb464b8c888a1659c81e586192a1d37a9243750321aa7979319565e485c53bff7de3bc477fb8cbd88f0a2429231c3086714e9cc101a42e303f2830c101026313c205040d001a15203bf6163eb5083dbd978084206cd430c6ae41bc488f190440d415cd0b8416a88c006d1c1a9414819689015d8c482d0a0e33e63416040c29204d86b696a86cf884ffcf7d1234656c1c70941c9c007d827fe0b1f958f886ff6e11f44dc1d2ff852ad8fc907836f052ffe1f851ea28b1f637776b0d188ccc03fc488f110e2de2afb216b77d6ee95ba60cf4aedb457279b803cc2d34476a43c0285e3b8bd91b43a24385b0f766a8969ed4c12ebe23d55d94774349524639218eeab883d91187b929f4eff43cd1e707ad099f667579ab487561258929d24407ae041f74004cbd8a920f03a519432d94a28a5b522b141e244ab76465bc45083ec87924711391c5981b556c7f60442ccb0dfe9237d1fc655098c287ad4670cc98f24396eacb5d65a5a7f1663adb596beb514451129246628aac5af225248d4a0d56bcba6e6666cd9d4e016e5d9c1304ce217e6c13bf855311e716e725c23cecd584af929a74b2767e7e5d2c97159aeed94c1933e391c34f4a97f522594f4992bcc077d6af6060e7d3e9d295c3cd3322e3586827deaaa629a11e746cbd15cdaa8e16837dab877e69c73ce39e75723619b26d3621aacaeaa46e65c935b5aa949d17eb452772c53b79fc59e9c73ce39e75c6b1797b3d3ebef17d7c9a9abeada38e79c73ceb9acaeeabc2e9d1c1797653cfbf4fa5d9e79b949eea9ab9a7572f48e7e6997d6d139bc63997a4dabaee69ca1e65b975d4dafefb5405a8afed125cbd4b7dfb36e3dbdbedd645b6c83d555dd48cef9c91a7169e6a2954cd2399cabaeaa0bb78bdc287238dc4d5d558ec48df6a53ee566dc8c9b71336ec6cdb81937722337a2f0466edf773afd7323377e276ea49c05531576d2cab22c698837d2ce68d7466ee446726dcbe88853ba4a9df255f29432994c2693510baa409465ea59bb5ed3a52eed4cfebed3e95f5c75aa618df7e5f4cfcc29a5ae6b419692e2106d6aea3ae79c73b6376b9776d9192ee3daa5b5eb6a9776715aa69c86b552fb74287e99beb86d4a29d537cb643299acb4604bcf2b34674b29dd3acbb22ccbb2ace4b2be9ca629dfc88c2da735bd71a835ccf5b96998cbb86cb64b7cbfa60c6638b8912ab1911556dee855bb8ce815c53107ce8ee213a41d8313d31eba98baf53a6ea9c6711997512ea35cf6d5b5ea9cd6d6b39de53ab4206ebc39ebfc5efe8ee7d06e9aa6f19fd686b9d365c61ab7a1385be032f51df2bdb51d62cbb3acd72de4ad09b3c35bbd4e6bf74bd71ceed77eadbab85fbd7e76d97ab3eb66d7cdae9b5d3714ab2cbb4a3b535d3ae5abe429c932465afa548a6e7d36a7f171463b43635af63539516dd4c65e1fcfe8e3b8665467565ff4a1e8f29b524eadb769d999184d7eb1538b9a4cf786a6deda404c83e173c10f8ef8415509420f3e185f0cbe1c7c475ab123321ca99932172d2b398a76a642c1745b2aad9ea801170d5feb891b30b1b94d5f4fe4c0873e90d7614fec00785a31954823cd133d10be09971616552a7be2074e84e17da1a04fe9ed41b1ac02f7550b2875fcba8b14898e1f77a91c7c5a0a07734eba699c6ca21049a8a74f5d5345eae9d372aa503dfd4a4e152c4fdf8e5345cbd3b7b3a9c2e5f5d38fdbbcf3409bc6d1b97701ed6cc2b880f75b40d2b380a5954f89961e8275fb2de07d16b01482b1f0174537ba7df0be0a34dd50752f8a8ce8f6efaf888c48798f024f9f1a0141811eb55da7ac6ba5b2957e02539830f45740ea02693961e897c04a92403b4e18fad314fa0b72b20963452f52ae356152477ce2768df2768d445826722e00813874ea03b12ab13474fb64bb40b2a942efedc2a4160bdd2868a12cb3aab2a98b2ac5cd061365b7f83b4abfebc2a7f51bc05fe8f8f38bf805ef35612cd792815a13c6884f2491645345f798c6d4477d7b3155a65c13a6e44acd3e3155727513aea902a74a4c6aa54c1bc4a54a318543b7af4932ae255e202613e595d1425966b6aaac4dd98bb2a7f03555acf419ce176edd17933deb9ebfeb9c112863529870420a0f60d2e3e4be481fd797525aeb0f6e4c0a134e488149cf8cf4b9eea5c0c5380213884d32450c12295dfc0850808509fc681e93c28413500ca084871999d991f471bdf5e0440a4225594e242e89939492e5b479c042844eff5960094599d2f7cbe61cc5045746665d9cb397a962f6d5541133428585232d2baab15345541c1d94523a448af561f3da41bb195bc7791fc9f10de25f17f24473ce39e79c7386287d111d4d12ad3f527e4a580cd62383c560241dabc5738a2554b1844e31add5e28b359df54643f7e636e71d0df5bcaea3f16868481412896ca52253c71aad9c50a6504733ea58b47f9bc44b1d8bee692a994cb652d3e9afc6985a4facc0bdd7ee5073f7de6eb1b51dcd4acf1d0bcd6d39e7cce57c73ce396f3a6b0c8a1608b51d65689475aeeacfc73645fafccba322d39ff3f73604fc0217ab2e9d8bd9194f0ac806bfe8955e6947386c43f04afb32729565af6f82551319e57d4e4eaaa927144d4c4653ca2a837faacc955265ac86f3e542ae7258e6952059e9fd7852bc29563655ccbed2356d2a9398489b3ba48b9306bd8f7bdc95822e9d1bed8c47ce97fa5a7ffa458fd4b4173db205b57aa4a81c911ed9eb9b6e4c6395b12372448ec81139224764af3f1ac146b12aa3591b131963dfb71ba03f69acf5ac66e72c46adad345e101dbf40ae1afbdec9e22952d817eaa5a8a05eedd767d9fae1d7f98586436d5f42bd7eadb86389543468aa1a9f783ba957d50c5fa9d7af5577618acab7d1ba4dcd79f65527dcb7cfbab5b6e8b6da2a7b83f0abdb8e46eb3cddaa5ef8f6d35dabb576df7beffe8d390731c6186f4dd3344ddbaa181ff7791b679ab463995da58cba44ada58ab175dd6fee0455172ea0d4b7d6dfa2a3c07f7b167ff509b3a9f877afdabe45efc20bf090c6850bc58dc625876228da3a35a79b10950d950d950d950d950d950d950d950d950d950d950d950dfa3434da933d5188a51795a934c43a2b4f718c29f322a6d1e963574829a5e206649dfb977da9645d550cc45cd58de2d79c2b4dc3a205f0ab6b1e7cf7ef0a680237e598ac32ff18881873b5aaaf4229c67ca90f9ac17cd11e843b1629d4bc30fd8b95df98567b27f64ca8a59f2ba0cafee95bac30812a1baa1c60fa955fc1f42b6f7f25ec800f1baeb0f2a7b003a85ff90e9c4a2790762c54303eccd24a610bb44a2fd94c1576445f14023501cdecccca57fe73ca9c98abfadfcf2903f27e25ebb5db10db58122896707afd11289a0885622986eda05004cda0d7f740110445c4a0b2e4123720eb16c02ffcb2aef992718c7922639d9e7f0b45ddf3af80789c2f3967158ecfbe3d61f64b7b50d243e945aa1b08d1886dba5eb9b9aa4fbfd70fbd9f8f694c3cce19fe13c79807a01fba80814fc433fa7cd06867bc10cf46175aa055acfdbd2fcfc1b9d1ce4cebdafbe78a4d3fe7fd86d6868f389d1640b4316674fa9886d08c526a77e51974e9784e6ebcf7af4782c6904f9da93db9d24a2badb02e72d008ba01e15499fbf5735865a271abfac8abab7ed8c809735fa5e413391c3c8a9b179b2a665e1017a7df8f4d98fba9275f15399c0b72d960e3986a29ed4eb8ab957d61ddaff5decaea50e0b5db36dba9b082ed58f4d6f957357e226f7151a8f76b7827cc84992c52bf8fef65897a7eed5f6cdf0a5ba835ee58a1908674c2ccef58f759b3df3085de6e77029735900add0a394c11a6b02f5b77e77dda3160f6bba9905f83400ea77dd162fa7c3a69f5ee37e05e80b57bfdeb8158f4ef6f5bc7e2fd3ece1aa727e7d9afd267c7001cd6dbe2d6ed62cfabbff9acd967c89282187d634f6cd15bc8fd3e0a2dd02a2015e68a9a17ddf37fb19f7bdaadd0d1e7e1fc15f6d8e7b3b47e9fa5f59fb54a0d5952f051d64acf5f4f3d5b8bbaf7a67a7e8c31566150d3344dcb1d8ba5e79c73ceb9a567150c9f785fb35959aa60d8ba05947afecd6927e8f0b5b06b14b610b742dcba008d4bcf4fe3923590dac7345aadd26f886d26cc7c6ca3f50fdc16ba6e19e016905ee7055d7a0a7606b7e64b7dadbfefe4c357012bda17b10d35ddb8166e71ad1a969e6cb5fab90350d0854cf1ce1ffd6dfbad850fb4baf61ce8825659c1fdd6342d7bd9becc02d89aad3a61766bf3aba0c3957db12ff35b74dab12a0af7ebb3eed7bd7b7d6b43157eed039cc8a894ce76f62464330300001d3317400020140c0a44922cc9812c4d73ed14000c5e74426454381aca84a15896e3300a83308818620c410020428c31c81461011a0dbae69e79f17659752ec7cbbef4c72eca575b70c437faa021233300faaa6b7f52e26b4ebca99421522625938182ba4aa00cd2bf81d20fb0102d98bc9a735b57c1db113660cab6072ed6ed89ed836dd7f22e8a511a0065d91527dacad612db98b3522fd06a21e35df8b550f09f2fb2e2ea4d7dd04941b3e8b49b50d72a2f2e0a3bc4305cb85e26b18f84128bfc0606052389661e32304e7cf44acf930e56665cea1833fe98e6335934b241b9ded17e4fa7b021f43b6fc7c1ca4a452f562d930dd9d6bb657def1e6c304c8ad0ddbb65c7700989ac4ea28ee2ba56d345f9b7c76e50d77479c227277cd48e1b3b47a8b26145d5851b6aa5f92971caf2228265595ba9ff512a476ef35125a31844815cd2cffb48fe38fb951f9eaae5c48518a8452d149b8a68709149eba610db86c7c096060c0bd4d8c2988a77c4eee4ec8a6f9f67547d4976c67975eeef214dff2bed7a47218eb150e5f1abd474641613eb7216006f63f15d0090420c61b7228f59b4ef09ef3fafda0470cd9ab3e490e3d4eec2ad2795cf1eb2c8185aac1755992af582469cd22d959efccdeb83e61eb819b182ea6b0d9f8d392d019e451c37ae1aa32588644bae0ee0329425e2113292c82c680356600a9c5cc5b3aec12eaa2c9abc80b380a862fece822af268cdc27bdfdb7554c3a01d19705eadc875b3a9830c49a7f02ba701a2bb6562116a00c6c05905520c7ee02002a820f21d1424b525bc9714198590b9538df67143c99c0398e208828616dd0757568755e0d6a10144956b6710edd23bc27df7e4360bd04fc892f44f146a67b6f10a85d99f7eb45c0753b78c7a70369a382385b1c358c3a8b27a20fcd404513f4b1fa50855a0bcd26ffa5960ba4b2952ce9971eb318a3a4dae57b91ac2b32e5f926fb7ce2a48c01b1025ae0f0f32922a8f553a3f8a57071e900803f8a78ee0d5f383544d4cad282d8e2fcaaea7f108dd238d74e2bf5621707c8b991331b3963d6328786861835f3f3a4ca491494e12528f115ec10534e690a41b5d9f1e93daecd423896ebd46f9c44d360b6df87f9a375e19d8995e72f87cdc2fd1da178399599d01f41b79aa831039337c2b4c18947f3d089e4e35120f2a10afb10e3bd8021166e11b00efaa86317b2232529c518376b54bd423b3d0e29e6b498ef217c10824080a9dfbaf361f59dc2a64c4f191a2e1b9039a48f105b95a9fd735becda31175abe956f87bf06f4f8e60c70ae6c2900d275b2f4ffd52cd802991bfa75fd8125c876950e4c4016bfb4fbc52b97ac98e06d417457612fab0bbc9e57b2cae25dd16a3ee3a2e8c1ba1d701db8960ac537e84132a03d613427ffcac5f76ab55c71c748145ec1851856557b9232fec0edb5c4d53e1e9152022b4830b3101d1b262612f2f33ae9ed11610ed447666530a79f9cc8ddca2e10e6e242fa80c17bbf53d807a59a36c1389e031d0d7de34af9d582280d30a14827e5b440c79cbf2a83f9a25031bf8c0a220c4c73233eec5a043c6931d9aa45a2536ca36021192dc042257845e3f112b84fd2d194616be80890a1854d4197771d0ab1c008fc0bf7b4e262f870dc3de3ce8bfb652277a77c2981d8cd7041abac6fee1d5e3b631b151fbcab687ad60a25dca8d7b0e32a5fb6e1e1245f58463198723a3c0bdc271e80933e3b353fdce93aecd20e9b873b5d4ec444c4b0f41465b05a53f2418f38a9fdceec2f6630230ae743b6ae486c40147907a275c4f7367db99010d9971943c282e17754e0174926cbb6bc2add4401136485df48ace8f3e0db15cdab00216192d122e70e9f6e1606a721d73d3c4a537417f81fdc54c4cd911976e2815cca2f9894b5b464206824b990a329640a964413d72a9b22fc1d4cc379ab8944c69bc3ae15215e474604a4a14d37a73cd819921580d5a23b810ea3f444152557d328f8b47f7bc90df67cfeed284fce02c3c14ab3adfc9ea7f41346ebbaf4a3db14580823ac15ded7e2d270f6c908a9e47835179bc8aef33190ba27a7100e43d808402e97c49c0703430ab60dec1aaaf56e8ee0772898ee82f2a2c0e16115a43140742da93838b1aad3f046f179aea094b2b356b1f420dd46f5558ec0678a67c37729c4e4425331bf19d51c4986b9e7643be7cd3cab1a33980d09a5c566f9b54d10cbcce3def8aec993a00c1740401833fb5424cdc45e4916c5ddbb1ef1147b48c0f7b94d68218a0c4e8c2ee51a0bf573f7d28dc8fbfb952448232999c3f0cc8adccd6da1283f920436e8ea28ee9b80d35c86e05eb7e592cbafda465dd242e7528295946d559e78cec0f10d39065360bf1098ccd543c779e9346ce7633ba5232e9cb3fefefe7d94b9c6e33e7dd6c7bffd2d7ee7edc3c1965463b84fdf2b673eeb8b4d434470e966cbcfe9edc0c38d64f38ce44c7529a5700f812d7aec9e667aa00416638885809beee962d408b8f6dbb7bd8b7e8668b425deb60c1994ca6e54d0ddecab02138395f5b66dba3345722342a3f8739fc01d781ae1ce2ad38cd7e3c9d8c3848f11c5607af42e4a8868d43f130343a3a8eeb9dd2934482082965960f65b3d4cbeb8467f4a3eb2557c7173d1c430ec83d9f7ffeaf223c059d15cdcb2c487a634fca4e8e1f706b6a7371883d51f731d3c58b2db7c5e00a46bf980d3f324862e8b2c505108ecf90c02a5ab994d03c6e67ab0fab40c6ee682511c81311540874387764700b5329ce713460d7addf12bf2a2f93ec73b4d5a19ec17465774117f4aa163d1fe27767b7a4b7c0c63584e7278d8a9fe6c32948588d2f35aec7d4385f5981befcbaaaf74022d211b60f5f933d52d45b9ede86cf881b4bc4501f5dd73f48595694421f22d564cf5a61dba0fedeca329550dc2e40ceebebb5cecc575a6bcb364e765c9fe5719b5b846e8212e1a8f0c9064442888fcaf596062503d714784cbcf225efedac0f5a2b2292658f159e5e3fc76aa6947b74a36283398505eaca6a63bfcf201c007449fd6480a4149205728cc29a7ced86aef03bac486be23ceb425122b9ff138e2a376e71b8fab1a2adcab8035c35c9fe836dc9c5d85849f2139278875a8b71a10ff8324bccfac05c509b1a303d5337fccc8d93f307fc16fa4c2c05fa6f5c91157d251f4d4a190f4d701a76e518d010497cef93fc287ed88c7a452b9ec4706a11b7417ce83a2251713f07d71800af7e8b16400ea1bc39d974a80e65415900d7a07a2fbfb43c5dff49a971e37e10174c3736aa5675bf639682c6645c9a6d8c10b5150e8d2cd0657e492e148d71ee4914fb444be878f9a6da89963002ab1768a281d4c9203462a2bda41d8b74ce40b544937c1836a6058c04abd5ed74200050bb337877b8ab65af950460312818401f57cc23fe13ee1e5ab920e5a87e6c4ed3db5248b4cb490a85a2870fdb7360200e1dd38df6225b0a8cadaf82251b4fd1f40441406e298877edab0e40f51fc2d3efd25f75fc957e968eb954b33769c304d695c8629ccee482df5964215d27a4f8c2c122015eab7f9778e222a7363b08cd13d8abf61c81308ff3d9e18afa9f3c3e08f7219998c2488cf85ec0e2446a7ecbd13b2d9d292619a1d0dc1bc7c91288dfd5788be62b75fc62ad1a616668af3a1099c87b6fde597a4f2605fd24af64f8ab190e13d918e49f819964ec6a792d32c460c55a6aa0886c3fb04d5b20cd122b50f100e4aba3d19ee8f1128c52cdbdd51326f2be693e81b05f870724f3db9258a5fb478a394cd2232620c05ea204f7d7811e69ece95410d637bb72732a7fa1f8850c0f110a51206f07921c8a4d9575e6f280c37d001b67e8e48160aca07b1267e1a88058dccbb58cebae1e5e03ee31d2df3f012a777127d64eb73d7c14c983ac287819d84f493cbf9a459cfc7bc21c18613bde8bc1b64584225d4c7711c5d85d89e9a4b9192c5c27af24f01f32f5beac27afe0b8fa178439aa84092cd4dac53aacf795a6a45ca8a42e6155354b697acff3f6e3bbab524107c5b6c3eccd66024a3f0556b59c19f372d8a9a8737a99e63a4948c60189f9c1cc31850147f088fd1c789d512ecf0dac36ceeb5c9606f76904d042feba387cf65011e00044381e60281a27e6c433914c61149f6ff0a71ad5675ccb7ffa7f2a56ce67d0eb057217f95fab2fb1868335e55542a4f0a11792de9fb3eeeb990a42b74488b0f7048185c3d5bc2625c7acf2ee61807a5f22b4c95ea70577d5895036b68b46a3c97168015cc38a994cd9f1f322168dbde31f2673e39e30f1a648edefd8198f0a242085e91a0e907c9977d5ffc1604c02e997dfa47c8ee45f12788739c45d9323e1e10c5aa99e483a9f256b45aa0b32cdf4b0bea58cdc64e6926371c9861ba8f5e03f8c42b2b6f121bac12b76332e6034ccb3034ad119180c7e87233a04acac4c316f363eab40e2fda9a04b12e98992cd5dd101efef6c6c29064c242e8e2dc61b18cb62cd100ca98bf418e47da956856da8f09cb55269c508776f2c2c7d7d62b794ab7c2399e279033129b9040e4dc437d27352871e645b0753fa8961f9322a6be3c2c5c6687e4588e4eb803046f28114b683fd241ea0ae15e7e5c2c5fe17feb36f8cd27bf1a2d8facfb559bc3158737806698970c8e9ca794ead76593db244571d3a01624737af9811c5de9bc9c07229ed08fa6053d0e8bf2ceed265eaf4d0067ae37202b861bc0214da145737b234e18c5e8dd04f26144a30549f7117d0346fb39dcc7611d0ae9c70fa54bbb81b8483cac0fdc8f412d6627c4c5073c68e03edaf66dbe7c63595480b3ff9cbdf9aa2f53aae0c31dc6a15e3bca8105d7e8738a74610fb2c91974c3234967dde2cfbf7ef93042d6452e701f4280ee5d8412fd7ef1c3b59211f0ba0ccff95e0d9032e07cacad072f13c357b08f371c674d8224ae59fb20930169724c00b6887ec52a86c2a08ceb9a9d7e0aade88382adb61227dd5d64658060bb08c50cf97256263d979b1f23531fc235460c40bf2a2de78324cfdaea05d35d926233d5f39e19de063221af990b62e4a648a4c5769810fb12493e61b38097d1895b52f1b16809dfd3be67c18bbc61bc8fcb6d1408e8406f2d088859bd9e184f735e3869cb1840091233aa7a4d398fd944a5d1208a17f96033a7274c1a137eaa865666739a47b63a78c39581654829d7068ac241e1e1d3c534a8565c709dab1de78822d46c52cd0b63a030f710be8488280350db081cd44f1bc5cf7191a04a0f7e87402d351cfa14f95195ca19bf870be6f8d0cb0f102f67ed07fd4d4d617eae65bd1274b93ac40c2e61d0ec4a3b6483c3cabfc49dc314f46030a59362cec515243c903aaa0db89d34ce12b6284afc923be15cbd50b2018ce57c02a840953cff4f0025f5611e9337c6d19364569b1f0e0f5d332048a807a34ca37f921afd7031902335c670b0449c2739e511d5da37ba3dad2d00739eadd90454d02f1c73eedbbd7e537d7fcde8db1b693a6d0a9c95858eafef0770a972040233e65431b6440a40dc45a87c5315cefe3da1df4914ba20af44c6009b09b4e5807e22af4a203d1de716d252ae34688ddbc4658298c1e0bada913f060037415d87777439b9c8f0ed097ab1c30ffbc8045c0de9b27f11d76c510d4e848f4c33a29c3ad51859d4846abee7eb134931efd02c5381edc815a35aec21bf60446b90ff890c182262767edc32722ec3704a936ebd173f063a0111f8258c5dda7d4869a3966e59ab90e3f3c8d89921c7bde724219cea2844bbc6e84c3c940b000dfb5799a92c42387acddd361ee0e4f62fd2eed26e1832548a9f94bf729c7610581b63e89e7a3907f30cc66698876dd3321e5c0abfe363db16dbc20663e2fe186e1206fd5d32141fc9f2856b34d61982bff58febcb14357dc0420e218a0072788cbe7ee3598ba18d3307ef42fbec79cd16ee838988ccc9f636032497a7e6bc1353a6f158ac285142cc3b49d9bb224580c28f83f81e0ff0a8e91c23e65dc17d128f7b951de106d1cf391f18a88c33dfca9a4b752016f5632aa1f826b0c210ade6fc654fb74ee2f33a73ec7810e81cf5b98b41fd099fb419740145b1ec187b00f45d1f012d0775ec76a01e353ecc20fc381deb5ab1fe4770dcf448e809b99f9c73402b884931e63bbc86bf447371eaaac3f5ae3e512dbc01b227e9e796702146b4b3f433be046fbc31cdc31b368b3ac7d7e7eee527ca7fbbfb2075cc5e143f83e16e659c2328470717bae1c2f4e7bfade4b4e429ecd8438e21f04d32c85d2dffccca2bcf1742ca29eec2961182e9d1b0e3a2906cbb8174afc9eca1f737bc600abbc221ea425c10651478628ec5bc3210cd2a44242dbb43abb746a2bc4376bca964e0530b9063bd0e0293591b764181e5a5fce998cbb07a06403ea070ee32405d16f729ef3b2e0c34fccda1323f50fe2fe2d9bc19186d51023da40eb72b054bbc59efada3ea724682e10aee5e6d23c0fdfb4f77cdc0274c6d59935951b6cdbc68d0e3cd8d75e35611ea02adebe5c9c71688726b7eda7cc3b8e6ae83604035d8e6cf2b55dd9c449c4be5254df3f81d12832466ddf9e1e338be7002aa6bfb67d26b020bd9929c3227344ceec1b44f099a4f75a6fb79b852c28c34cb9cd671276a03beb6023bab825139859af07e1bf11f90724e4ef610939e9331fdbd161273e4a0a14915c05d4e143a1b89c163e17352ff1fb04572bad38b0556078adfbfaf54a7675b7e42783afcae549c8b588181462a078270cd7119a60aa828884133ce832070d281fe09cfb4056c4fd83ed9621b758d4c32e1a50f22d768239f9403badcd6a225b71c1840bdf002000c0778b0e4d35c28fca82f090eb82b8417aa67bd099dda7ba07e08a92640492e79d599c27de90a76f1cef47b2895beeb9a1b247615415b9783e3971c9bf17b7ea676975f90dd0ce24f6ff0e79ba8161d2eae9cbb03736c1b395be5d1d013f2e54b9d69ab1fc2865f27d30e72202131ff84025a1212c815e8f9411dbaf0eddaf50424ead474e39ed7f4c8c55a862f48f173b3c68e8ab9903ee59c49f3e78cc7a828c16a3ec83887209a31f06865282351a12617362d3de7cc3278a2a0442013a68fc01ee18dbf8c385a2118bccc03cb08d924f075c8c75ed1e353733ef149fce4e0d258f717d195410746931311afd48a6c30819f79d933ff176ea646356a59b17de0f6212706f33ca73c27e34328bd9b667e8b96de9a745753270bf459755a7f8ce814c728d055b827d0de80578a0278bbe6db8a46472239b791cc930ad3d341985092bfffdd5d1bcb497525335daf81a0af42605c4db6da0e7c58a6507b355fd74c54e1bd1152fce2368518e0023761e5c5f16b3f8c5c8c3d0edbdd1f6977618d2c529f6f400bac0d8ff46ac7ad380a271a24dcbb3655ef640e3d65581b942d85b39d822479a0dbe3072a2a61369e4facb2e382a3fba49370ce991c39405a15fb9f620a355580aafb95ce91584b1ec2da49eeb68e6ad7d451d97069f0a67eff0791fada3d1ca0273ad47214f9e900aebce0512fa52e6ab516db086b2fcf5fc54f963eb89b46c3c11623caef5841a7a366c4848cd3e854bd977716a067c71ba68a8d2c625dc7b35a7b167599ca9b7e89d900c06371b4580b4fe5fd2e235935754ff5ee2401576629b0686ba032e8ff22ad88425d65b9ce9cac2295cd1473dd63b4e1c6cc4fe659e0f9c12654b1b33e66532b66e572aaac9059c39e958f623fb942f2576cef77c62290d23aaee67cde8ca7cca5034f9a9e3a24647623862abe4c8743969fdf7280cb54968166f57e310b49c917bf7795a88250d84c6b605dc730e4c60411843596198e2182a8c7fa86446c70bd9baf37d2aa7502fb8c3e656a0e88e148cfcba88a047f0d985d6c3349dbfe54b27814ba1979f615760e9e50fc174a3f41a0df5b318f27119d7910e0c71b7912731b311c2b56b390c9310dc9583eb9985fef88ecde85c812f5fbb29164373ef9c9b4731b6bfd2012cff7d987c63cc2489c4c643404b37962df6e67eeca5dbfbbf2202a6c95e2482228917fa7526b8f6a64f238c9a30c0ee9f74cad9f5108bff3887f5e36907d503112942fc25341e9cb9531c8461b2df82308e8372cad97d86642580efc95f793a9b155f720123f801f90d30665196a46dce95212d999fccc82c6791743786afe19af472988a21b419a891f4a2e71e9e7de86136d091eab27743b2067f2d3df9bc1cb25a21f3569b7dd90c9fa1a4f1902e396b7230bf37d2358526c93d0313be479e52dd054e6afa7272c8034c6d9516bdb29fed6b94e2db9e0a6756dd6346f2c7401adac4bcaefe11af9c3383614e9065e5534b8f90a5ecad1381fa212d8101ec12dee331e97536cc6526bc004a091cb49230f31f09184c3802d05ef82b816fbe7e53e03c13646eb329204eba985052b7d10bf4e676b29a5017fa6327cc3313b2b44905f484c302b6d607df016e58d0c5f8f766d4a2e7a028d050b50d6dcc7912a274a4cb54325dd1229eeb41956542e97fd15d6914634ae3b29e04468e7ea2302a28419eef458e5125c2266319e0ceccc4ec34719f7b861a845c205fc668eee06d84029c67d19b2e9a1d8c362d2e09cef7993b67d8530eb2c21d77916b7f528caf91f0035ea340191bbf3f178a46319a11d65265b61e9d5fa7657a55dcd29c2291920dfff1ab1a8509590da597ab61e5a180bb329336182ae74935dc198e4abe20519f7f3855dd6c6a63c5e362d97d724e1df406e92a26ef82a5ceb688e3462342d4b09b2198b67fd0e0ba0e46599c37224dde9fa2a0ec2f36d68525f5d3d8471183a55e013dd85cb44f9e5595223d851d682e9a8e16387a52a8930351a34208280ae78ce290f4d441e071467326265a6275fd89ca6ab7b9a1e596f5695774138391c550e3bbd54d8d43a6bf381189cdb6cd051f83a41127c295553f6ffb972a9bbefec3b502b3d905ee14552a8ef4d383d4f26ce50067bb6b49dfa07077826e4a3335908d961b8837026aab2c0643dc607f26c6797d33d8a0dc8d6b67105ce2250f62cf33ed30a6f6b23df08132d1d0c42f5d5c3958b103a6ded41e4e58421ff98aef1f93e4ceb546753e0113582f1abaa84b577b305074b849c2a8d4105e0834653514e7e868eaa8d3bbb270bf0a079674a2e50f2c3fc2cdc8bf9e96159794aee662ab8d5e894a39ed6d15a86d9e1710477e7e11c894773946beb4aefc9c08f9c75d9c9d08c62d6a1982577420a24d75f6255d08c5ce56d0f9d6d8d99c7fc07428c925d009f5b14ffce237355e1cf35ad726385ff71980582196fcef11c1056f2928d0b9381eaf1b9647456246f804e7fc95c91670a4f7477cb08e1fb74b7658ef1bccaec04b4e1cac3ce1c238b4cb2416143afa50e87a8942f77f143affa4d0558a355690266a58c1abde240ce418ec54f455b60d2b79da417da1dc87f7e3394ab82c5041d7f3c951f48ba0d34777bec011f45c28e517d9e165cc2c0b9c8dfdaf96eff8171ef67ea9065832dd3caa691333461f7fdf71a87874c61bd3b7f03b5222d55b650fd6e76f3ac597b3a108c702f427081ef7f3b70c3dc5929eac1c97f0a74b4eae80bf95ca9748e690f80b14f5b362a90352ec4e0f782a49d2968779657cf228d5e13af10f79a142b1e7118be0667067c35a801c50602230e1a2af4c2f631fa98acee89933747321c5e18978a450eb3b396e6e94c4172816f5658a5d5affc62612b7d9a211a199a0a67134dc7789e74d7dbba781781152d5e04a22dca1873d83e9ba14ff3f6059ff3a77e0cc760b1bfdfe0f4c3fbd1f6410eb071460cd29601f063164abb2808ea13aa7ac81cc13da58769093d9f21777b432b7d9452b79b03e3adaec28ca5753a558dd98c320097e156abf19b5c1ea9e8d0dfae2c48a771fc925045ee101ea203b23fdf86c5f3c3606b543cc6ac35a4d6884fce3cd5aed6610382365a3136e10aac12cd9209cc6f9183b132b964123595d32bdca46ec27f8bc253b307b988898be5c09dc465f82bffa3d9f67cfeb63544db0ead7d8b3d333aa9f99f47ffef356188ebf9fcaf78279d9f3f01f19175698f49f4ed1fde02b4d02725ebe7419df37a37c008b235985c10c9b20dec48bfe45e9daf888a3e2bb11f92de6a1e4984d6d46be78dfc5a17f9bc9dc4291ed1477ae0ad7842f0c6ff65bb81e39002afa6d7d92b01e2eaad713d38e9001eff708505901c398803e9394f127b4a3741917e2e0aa3d89620ae53b2f4790138f1edba7ddc725cfd36f20128866dc0cc4bf516854f3b25eeb72db808e321353da753b772bfb1b6d28361c2100d14e2019a442faef1240c5dbeeb5cab7f2b9e9d859413355ee36f819814737d2c62b85e6e6011ef667a144c93c581c79694417b32192a00061b3c782aeda620b32f6ab9f7dc472886d5fc3f9eb934d82b9d65bb7364f166fedbc785c87dbd8976b6a44aca66623e7573cb44c22e1d7a5f24b259b576aaaa6dafcaaea1348b43e7e431f34259f3a2d026c8e0d15313eee9bd5f55caef64196d4b3584837a574c6cb45aa07776b0ed1875468c9ac72cbf2116c237ec02515216f734a0bda1b8a56c70e939b33c9949e07ae1dcdd6cb011e09cb6d61b16da1470013fb4564a894b0c9913d493dd84acf0d0f19e04f9a202c8abd2f4f4e267b4cdedd8588dc646bbf89052353577a01a98f4e085abc18d2e15fd5f236758595416fa558d93c44f794df224b8b1c29e2b006ba1bcfa4b1c884586ff01fd9a554baab2122afc1ff0231ccb71b9e6e34b2d5017636b47eefe7cd6ef3fc429bf16c31d3679de4fc2c7806ab08c50bfe10a439bd591822fdd6a12614fa6f076f249f36b242b73074da4f17db0b7b9b28657ac028551fd57183f6a93e3b0cd16217811bd7f936b2c79dad926e8155295786061781235d8c267ca7c660e9eb8f7478d2f2078050cb99fed4f0f85655103ec48fbc2ecf379325e28264fca779b2ed834e84887e203580e92b5ac973330926ae27da7718bfeb0d83022e1ffca5aca645218b5610c17be9e4c9092299eb42aa37130fe0b635b825d330cfeaaeb19408d4161bd75fe10c0ed9e9a545c05615f460aa017347c84b2f7c3eca142846f2aabfb2302dccbfdf08a11e78d98d09fb6242176f017a8ca3080b610529978ae906e8d52d2c015fb09877dad2288798074ab2ed2d7d024cc0f223a2198d2fcd25d8bd3d3b1b52f853de801cc0740273b95e11ed0d3d2e627a75f40d4b3082dab9d6b0cc5e50f6fdc004baa3f5474dc02db8be5a343d149bb0f83c826317fab687fee6f09cd45d42596bba713d8918cd012ed633ce508e3188d8c6dc31fde0dedfa1170b08162db6df9c6bab88d6f82cf26e5cae9f3071e3e2fd881afda67048f5982e0fe78f9f7643e778c4b68fdb2113242fcffe951bc73261cc603fcf68338e16b9b728591fa2117b658291371a718650c331a127a1ac7cfb5da8ca7d6baad8fddf2a17bfd2ea755f0cd5579fcbbac46f652deac154a89aa0fb7faadc18e8564bf8da256ff1b629db58e3ef1d25067397c85e034618b4bdc5a92f421d64796ee01300d019f401fbdd8b927f39a936b77af225f3ff194fc84e3ffc1c9394d3849080319073b6fda40da2bc1392c1c5bb5e69678bcd5007322b16f4a8cd9bf1224412b3e45cb5fa6991c3239a999d68987c6245baeccab33129b63072fd630f243a6752dd2a1eff245bee4f26c6ffbd52bd8ee2d815e4a65aafb7e3731523c6a97ecb437d8af15e24533e34c2d4ea43055df3fc00a6a4beb46829c1ca4c784fa9427a8d592ff8006cb19ef95ad94167ddb0257739346285de4929ce21d7e66a17be567fbfd346b15d4fce5f0338adb5fe52c6a97df852e84371cb19c505949cc9a6e137771cd4b5d086eefc114ffafa31162abc4178fe6332ea39b1bab2602736bc68fc813305926c806c20d8d5779a42b17bfa9d0a10860df9f2c541d0711957b462f67e2fc34646343a103c548290b9b9ba145f0ac3bfb2f3ac2535682b2e836e720aa2ff62e3fc9194cec2127f92e1c894e3e037d9ff4c2e25549d81045316dc6ef8db6cfff6829aaf3be6b9b653b18c64fbaac676a6211c85483a4f347e856c39fbbfefdca1dd652c3468ab653f93c58f29fc1ac4ca6c8ca4ca0f20ebe56739542ca47910ef27e642d3b70b8a63cf83e44bd89a89632f4b46c60b127b90027d2242ff314574a1a6d21c778ee72846e09ae369df6b2535ad741fdd0bfd85a631b1957e44ef0f20749e0315a1a35f90ed3374327a319763e8b0ee60cede64cac13196e84af4b972fde5b143b662ded540b70ebd2fcd5f4075df76f66a22f61272ec8f5764374809bdc7ac4781d2243e37cd536097728a7d20e6b91243e0de242b43e14770dc9ca6b82279da21b24cc135c551a0983f39fdbb079b275f22bf242c09ef1f52cfa7e06cc305dad8968a8a0943241fc4b97eb6209048e9cd866ccf8d8a7f148a130956e96fb8279e1ead2cd5754d57e90068366cd5156af6ce2077b41b9d50de9c627cbaf59d50214975a279d6d2d50c659f2444ad1dfafe8b85f22e2326f1f936795d94317433a67da5de7b19e41e37672a6e29bbf65d6e4e2995a71889cc2654e602a02d4019d08d3c9acaddc33da6714162648a0af68f67342e267c89ea37e41355194907073e6f5b674f11e292886a8fae67f14a9187a290b1b72569f8923129f8c3d288dba2a91785e14c5d15784d5e4ae14b2f8fe8dc1c36615c68a951d9c2e4b94ec182fc4a5efed5eccbd1747a7408272b77fcc172387d26cac98b00cdfbb08e2acf80cc91153d3f72f308174009af66ff676d6ecb6761e6fd9984915228ef455fe01340627a15417a6ae704600222db61d1fc0790408c964c43674794b66ecdaecdc257c818ad6c25267fd9052b64a21f37c135c111f4adfa2fa766c5a3878217422bf3ea6e9e15f3671f891a1f08ef6cd04400e1a4090c1ce1bda68c0ba71d9cb358f38939996d226810245239650523807c19c40d804e539f8f1f2e20d682285d8a052b77ec788d9026ce3b995b931fff8f58866d8dce7ec432b35ede1ad1ce254e68b2c85e0ab7d5c0a0f379821dd878b7b09e826ccc02b2c2100aaed94374667ce6f7588be9b2937b91cbdb2cc9f636e3327d36c959926dde4c1d82648498104fa228de436463a47e9ea415a53bdbe7c885a654456611440362df69dcd32bb465111c449f6fd482d680f8706b3a112078a6b2d16f0bf9a15c6b241d47ca24061921d44df47cf8f96278fcb9378d73740052322863f6c2d41ccdfc8d6e50fba6244a9fd011d2d2f25ad44c982d5deafd312958c94ce4a98f6a2c0385aa0fbc249ca014655b67d761c6d7ac3961e17bfce9065ea5b3a16c60cb8eea681fc9da89b9c6d1d4307b40c6c4194ff4cd21b9feee56b823b9268b17d316e3a3c66955eff83b1c545d26a0d4efd6599d41711f7ca9a95865630544e63033338f0ecb83a7259e54e2d30c5184e2669cbbf466b16e1d37dfcefd1962d6fd59151761c1bfd71f79fe0ba9f0df7289d6cbfb3e7c13ab184b0adc018e426f674ca454173d2bbe2e70262a7891204fa0f4b14f129d1e9319ec5eaed50dadaa4648fac596f12ba07903ac18bf11e3db8e720b20e4701c4c7bb17913eff9ee036d0dccddaa00d7e18e22c587b993e258050fd585a3708276bd07bde4a00ea3ecf1da1277ebe072807a84f42e0199ffc1e51e2b40ee866cb207f35420afa11b89fad9ef806212a86fa22c224606998716d3c8e442c25e05d67759630733196d83d665b9d3e1faa43a130399196260d047d36474861e8c04415536c34ecf1092ba02ab4f8d969b79727859c100d2e7bffca0d9648d290ee01ff2bf8a440fcd0a8cf80ce6977a1bab36bb5a982886a59d10ea8eb2dfe6e0b959cc83ef35b744a9ba1406f22d3fd96b2a38f3420d0b80c19a38da06fba5db64f4d61654b10f809b37ff90b7155a649d679c7e1764284a70b4a0d85ac13cd9527c0b01a8ff3eaf294828c4e70398f480ca6a6e3d07b57106a6dd2e5446f5b807acc4bd3ce6ce6e128efe90528ad1516ac457b69f2b2e0e52a10392048fc2fe02c79f2bba9bd16f2dbb5b33ae09efc273b541db74a70b34ca4db844a5e959a2258c7d52ff7ca807e3048a4a3cbe92a073d4ba55895d643a64265d4925fc9bf4d808ceaf51dd4b0725fe0b8ff1e6e1bec914ae14ecbb99ab538d775520772a4fab287716c6cde0edab76e32c46ae410aba69d1e4098bbb83883a60338209cc8cd61b0b293bc0eb5884facadfdf70c1432e41bc0d0507c26e090f1d36b7b4d5c19d7f1427102ebe89c612627ef0e965ce8f4e16609edbae4ec5d4fe2bebab944ee265b5201f346dc5f4bf3ec1e747365a0f6f15492a66cccfeb0acb3850453e01b10bc038206b31b313f529658aea1b77f3365a107ab0ab9f4de8b1f90ea203fa919c3137ac8b25fa2870468db1af80d946803fe17fdac6d490c366f83a952956f6d5b538ed3b364d02e5d81496d8811a69e6b2644ff9b73e8c74159ba581708405237fd3123c88d3bc84d85805e32470072dd5c0e5ef5cf603ae7bd3ca91b80467b5c8862f67eb351d1afbe3949a948fcf808ea53b1aba846bd422a40d4e0f28b2e93f387f723257f62cd0607da296b48f8d8e0281b37eb9dfeae466a08dde99f2c0e8660ca0642403fdc1161732ff2b7200dd8b39a763f8839c96212f119b9222632573325af1ef3250c590458416d79a3be361f1b8f60df0c6366f8fcd505f714d2cc69f6b8fbfecc3d4a6edc8c09407eaa9a9cb1d2dfc03d0090e18c49249393f75a7c3817e5ad52586ed79d7ee468655515bc964a6d27dd08e7ea379aa5bede7e372bba94fb7e9a5d4568dbb5d7e70e489e6722911b2ef4ff21afccf23514bc2e9eca970ab04c0ed8b33db1f42875d27c664df46dfcb0b077fd8dcbf890d48df1424e9dca6bce83426242656ff94ae11ee394b59df48ba747911cd3dbf88e5357c01536a00486491692b07482fd179da6933302c8950e61eb67e83286fc7f68a9acad7d222309d0111aaca31388c8d6487b05c00f05a1cedc7f8af399d65f3c99e1bb4d8fa8e9c710210a081c5d401a95377aa6130fe0d683509ee04245c38e38e0687e8577150060e40c44bcfe21fa0012d826cdef82ed8e6a5cd7497b0e4569a3b7f2b0716fe81be286777232634aa0c48a3b6895cff1ad089b8b8d00a8d69aa0811d7b2f73911752435762d86dd698671b2a000b87001b5c60fceab5123451c4085f0b69a6be16fc918808d40bc10ea5989f5940ac5d776b4bcbba9c46b31054958610924fcb0891a921fad900785edfb2fd67b2b87a3e15dc3615907628af5ee707ab130039511c8032eff1522d3c5a6e43046eb5ca16874068a55e25dc848232e670374e0e31ee83816e41238a6d920bb3737683b3a09b975259e00e0a4003ddd3608882f7c11258e285a717d00ea9f921907773727c259d80ea26161a615acf9f7b807aa3f416e19709488b67c34c97ea6c2da3f2897cc48e1d27105fe7768009c83f4ff31242b654ab4658491711548281993ae3b16d4b8c4d7ab3f9ae9472e106a25b59030d6d0f672cafdb90a0e25a062d937d531d31ad43307a04513b1ebcd41264c1704a173749a357379719fe414cf16c9a1e827d219113f5ba24f8521b9771aec2629ecbdc8b86da87656df56cc45b53a606a82dd89c654dc19b9620e8e6145712bb1fb7f7c5652b0161b3a2dda86ef34d8174839560ec030a93f0ba2e76dc9b9f779b2b570a69cd12b4889de27837d28b72d38aafeedc7cbddc5969016a7677cec7508651183a6d85d0db25bf088f282a07cf3cd932fea8ec8831e8fb98ab828e3f5f753ebcb072a87b7e8f9e24a7c88a10405b9c39cf676b6d46e171a407e182c7e55dc2d882247052f1f2adae0d409d44cb327403bcf425f265e34650129795a81fe4a007a7a638307673454dcf7c62ba65bdb050ae3cf909bcb07a5cbd5642a7cb3370b7d467ab38ed1e462c93748a2188c557b31f84a80f06995e90f1cf48ae7adc40704e975faafd89eb61467b940ea010110da6f00e7b5a64180a58bbea514ea924068a52191ac73dc8778e85ceeff5e158aaa3d41889eab6f9d73b123a89190b92b1d0f96b024e9021bd561a244e172d43a452dc26c59eee86eb7438409d3dfe2fa3e2e97b918dcf5ef2e089acea27b0dcfca9649a5c413572ce9871d1fe549d8185fef092336204dad872215ef0d81b676d3d0f2dca370a990bb055943fca14490289d221ebe400baf331be1aa061efa48d044278dc1aab38cb5f513028e2a853a33065f6546de0af9f44666caea35fbfe19608c30ca14fc0efd9eb1473c61481beb2f33b302fd49d423038c19ea85c4f933265e602f89f164ce5ca05e1ae3c99cf8a05e2ade97519ba1b949651d4064aa0cf54b6c9d339b832703515e999cca9d81aea1b2e5f4ab9f44cd8154552cab44875bedc1dd5b6f06cf5c644ddecd4d96bbcc1a6edbdb56ce2d83d2db76d3ceb9642bbcbb1b5cf6266bf8f63759a6aa55ff356e76ceea9df3ce8ce58ddc489f9d53ed3424ccb28cff35843a26e133eca4352bef2aabd2dbd89b00fe44b7745c4258edfd43124a597533543ef9741f5e10f64a365bfa245c4926c23bbd8d2d6dd22b7f5ad02cffd59535da1a4a3039c3f6e1ffa1eac0834e7ae1152c94aea0b798c15c165ce621e4f7f7db8cf7be5453fe5bba0af87aaa085aec7fb585bc446dee919d5275c76def0af691267ade97c527ed73d148c8fe3f3c57c063fe7fab173ae26edb069f7acc46fbaac53a84f34128319ca489b10b96e16c383d6b458197c20fde18d3fef87d7c79b29ceaf7f3905d09d0900b75c4fbdc6727860ab2acb21e6df4d5aa1fa148cb36393d546a276da1ac8590899280fc94a0b3f4b784ec0d6d57589faa2b86d4d43b88fc10e2c8fc20f334ffdb233d5056d742b9a20b1f51062ba3c9c3bd5bfe9280986fc005736efd9d799b35ddbb1b3c7729637a763758e62d866f336e0396ec8ca5b4655fbd0090693e3b1f8a0c84e430330a50c845c88659292afadcf156122b2d0e4b98a6e921bf50a5e24188fad95418ddfd76b0977f2617910e7a9486189094744234691356e7c4579cdf0006ead460350a4e83f403a60e467958d20500543c5478dd2e93aa9542bf4d0bf0c64ad61f588404fb1ef818edd9f5d020a5c4ac10830131894df952a339d236068e28dba4a2936fe418782065c8519f5ecfcfe9743f07d2101246266f8591a71217cbea7b0ba807220931a6496fc8d94f914297f15509cbbc79b45a423d7f120430d44cb0d43112a73909b53d1f993b16ee114e9a5dadd0b7cbd7d77a0621441e32c8a51cdd780c0492af02d36a392197b34da3f98c790000e83b4ce820dc07902df5fe401b47bda0a40a50dacd87d6d1379e273ceef5ea61c55e4ae7d50f9f9de8d647984d6a308b4a7f740dec529abba0a5bc94c687ff33ad6433da905588a3c48a8d0d04de3b9658dc42e6f0573d3b8f559582518389beac34b6df1c18164828d64f8db3e5025c01cbb411fc3e6a922717890220be8286974b36fc3d2e185be6d4e0c7ba28d18350ebaca62393587a155780abd6be87c32f6e1e17ba6b61641cb61e6760512865f1a2c32d5dd993c1415fa38839a6d7fc28edbc98b9522cfc528931a1f79df63625bad143dacc77b6d8043989c590ada2b14adc8aacd693a1d061deec02483c50a7249c50ab47005031a3d7077991c014ef20bd717cb94e67a3cb5c3f4629c446059523445661bd67b2406d06ba1a28c8d3994dd760d5cfb03c3fad6cae453ce4da1bc9f9895d06dd95039f6bbb4c7634fe8f9cdfe238071b892dac6b5c23381c8f20c59854d20536b2aaeefa249f564fabf9a602a9d41dc2fcef31ac5ab934cfd8c6ee674a1bf32c6e32130fd70ec54779799e8a16eb44ba2299f0710e5475682dc97001f0ea9984e5561902dff8a49bfc3a41b16a7b6d7f2c51b3f96d2c8b56ed5f219478982ab12824d4677ed5d807ebb140d22dfc7e0ba7ae1cdc17c6c7caf0a19fcd94cab439bfdd49791ae4c1a33c9c1c576cf43aa93f84ea1d0aae4a8a523c6128af737f076a8ae1b9767b5471310f7d457ef50b79c2533894dec063988af309cb96118cecfc333c0e51db0e46913b9859bbd0928fabdb5515ebea1935f6f68000d21d6620bf32f557d9038af9ec038bb6549fb1b3edb2f60642778d3d038add1f1babbffff69c1c5a69efcdf4690948765561c2d346c77111f0054266324422d1a5be518cbb59613230f09f4f75b79cfdf7cb727e443cc40d8903dc9153703cc3d5d58b169b40d1257d0cb915f908624761c539bac4ad944018665ca66e0e7f28093d0d540a0e31480531fd8c533349f621e6e1d410d1578a5a336a134d8d538bc866f2680c744581d7a05acedd93284a85ab85547ca1fa53fec0a4d85e94b93f455a37e83e2b18376549c9ee1a81ba169c98bd52ab9717c4e610d62f7ace0610d20450e57a42e1de7c770e6f54b6994cb8f8cd258de56cb3155cc2795e8818f162d2c56a12150c39fe0dd7dae44ac174ecace1f6146251b90bd861051d38dd2935704a8218688848b7835942950c39993dde21216ac6e25bf31bf2910bcc3e79aaee751ef4e7cfa3d46707b2f96814e84721dbb3849c735e11a9b1347a9b011a403fcabc67954603433ca96ab412dd63474d1f2ed97f6d576388c8e7129a8c1b40c1d92a031dee22c6cd5685a15d6efdffc31663ff11bd641a6ee499a5c609f15e0740c106c82b580b3adb616564de2f9642f39b7caa92da00dc183c63cc2be5ece095b26dc31454ca6016c26028641d3ddff373dc253c833285f49f089c780bb15e992c9079340757b5035a6ee3609a475dd0b02c1c1f9b61a09b2d2445799fa21b28436f5268c89ed4bc8096ed25cc1988af914758d7dd52e25fc63486d3e868706589418c4572f15e50dec6f6535e58bf79b049cee705beea23e5dbdd96322baf91262c595578bf0cc2b05b530731ecd721bb867327b112ef91f3c33785cec30ee8ef7b1eac3bfa811c7a73dfacc87b195a5d12af43a989dcab10ea917a179aba645e6d9ea464457e5e43d1157948bdc55055fd4dc98b00b136f97e6ee851398c7c5a0f4f58ca18e2d04a1d7cc7b2f4cd54895c1ca7be85dfde32a1aa4a1bdb7ad3b913b8987addf434466762433eb8c564040197f8199ddd70dc61f5dced430d827e73d2c3dfad6370baac4f28a48d4dde6bf882bcab069d40cca0d1546cab50670a76332743b09b26e26c2b96e0aebff22a5eb87fa368befd2af57185d9af12f4918da4b750d422ed2d60408cd6da374c47dc6ee615aeed45631cd9e81d9bcae435ecf2f18264e2f0f72063666a66ad9733916c4bb4b9ecb1af9ac83e668e52e38f1b544f165340545595b64a571002e82703431f7fdcf5e6ef366bc6b9070e4d5fc1173fb72cf457baec27f440f4f17cd9e473420504636f8367fa1c02ecf40ccc7f922f2272b4dd4a966ab7f4df1eed2c60025edc0c55fd293be1edb31065c80912ee955731d2709b481396904f2bc291fa7c5a69d799b200d45b2810ff0eca974fba153779cbce85b67885dd171472b65348095908707b2d730ec2ea390839df0705a4713a3112edc73fe105888326d56ab37123b119bda02350bdc7a4640953734ba79789d7ae0508ff17f51f9a95cd398fe909f4b8393eed1d25de995ebaf30607084ab1955ded2114d466c77c5005bde0bfb629c863f160c15ed7fd462efef558fee13ec6910f704dea32a666365bca16abde86d2f10885ad227bf6403a207e495c948d6b3ed9975e0a8b04dfa5f00247fceb91f2cd261190303bca2ada4de10804158d4f16fa1de261c85a8bce0e8c85c8d4ed6c5f848670d9d9c053000bd56efd10a9f719315b7d312caba6ab1aa4d2d18584399ff5cd110f38f3416e053bc9f652025626e40ced80bf4255637ec793260b5cd778025cda4cf00d29bf2cbb501fab5a3c1093008c46a73f5d03d9de46f3e011bf762061d5d2ad00354f018c852401ea69f6945f6f92045dd7baa1ddc31cf1437f113dab424cc6acbb4340792dd232cf674cc202820417117091415d61634e9e48b2d8c537b3b65204c85ba29bfb95d19933b4b285917899c1de5b6afbd032c8d1d73c8ef0e61e6e29be4efbc2048814eee165fb532c9696e65bcc6fc78c7ceb51a555cca41eea888727050b56612cf6e3bdf37f76c2b36b746e0cfacdd6eb0844a9e402426aa17a646da5d4da49feb7fce2c37292141682c9ff5863d41bfe68da3b00cf5011e89a9378c542c7c32472c501229ffb7d718a3a6ff70670003086685e63ac8a70bb66e0464434901cbbad964d18b8cf0345a0ea88b49734ab14308642d4f934817917216f4a87769f97a3a043450e66c551d342b22d10696b9160cff41dc8f2f4604350249033a66a44bcc7883964a0766cd99c1b8e47a1366b150caf197760a91206c3d7677c447a71accaebbf385543e0e28bdfb96414a8e1efdf0de37c44ac2d6079d0da014cd99baa34a6420aaedfc48732f9f76f2f33c9f009d8db30afff7a01a2951970a316c2e83c29820da6dc02fb0f34973e13c74780ca3f378e702ebdc5e81c11c943a66673d0ec19b5a2fba2e7014b99b706a7a78cc7c4ea08214acafb03586fe0e53a2d66838b9a2f3ad51e6110d1850ab051a6a8fe8cb5ad6c74b508675659e26be192b3ae5ef65c0cd1fe8bc962ca3b1c8f34a6e490dcfcb1088f9890f4b16662258d61264060f51a393ab1a9176c51ab17dadbf481b76bb59e014e6d21c3a980910c6d6ad3a180ce1eaf2d5c42ff7406756e4fce2cca8778332ed8f36a1880d3e2ac6ae6c856a8c3a09ab36fcede2ae07c134aad8eef599b99594dc6e028067e4f16bac3cb77b1fac5d6118988c0b728046e655edb8ab98f97060fdb665e624459dc9858c52252ae7faff50914a94c03b1754ba11a6ab1c171f8b164fe2c7df11e488bb65e46b36369318d8e10d7ec32c52d5a7845d3e7fceff99450f9d5a937adec82878c2a26da36825458a0e9ffde70e8feb2b2c68081685b4ee6a0bfa6c25dfe8d776511824b8a5b5f000e2aa1b4ac7221de7a7e37f70330d2c600f1e5fef51e99fe7c962fe7d52ce7e2f31ff9d1cc08a186890a3f7b794d6bb3c135daf939e5bdf5efcdb67631ce570028692a05ab576169b4705dfa773d472d87ae8c01e1fa8e31d43998898dcce76ed3bb77987142eeae1963fda672c27a71fd5ac6926d84844d15a2d0e6a0050cfd31ccbd610310e55a066c826270b736032e7051dbb678a531c6c910d6612307c31e461decbe91809d163c61c7fb02099cc04ab33798d1d62e6adb649397d7b46a26dcdc1b927271cec2ccf890ffc5ecf3ab9a66d2bb7fa7926de006db4c796f9eeb23017b031078248707d33f7f86414594cc9bf023016a0ecc0fe8cc76b82d66962b6a93b7d02a6b19d12ea568e006df93e19489dcd198d3994cea630014ed3799ad66ac348caa94daf762c83f9437e4b642d0fef222562226278d05cc0d46d3295049b1abf2bdda5ede566c2fa1e678e046091da62ee011accb6e1830b859e0980cec4c9bc076e9a908fd89619103022a90900814061b2c555b591e95541a2662413055db6dd094213c51c1ed6698891498da4acdb60d13fc9a466f5cf9e1afb9913d9a4f300db102863298f90bf8add3c400b29994241c6a338b04a812bad24ff3a8d7aa84d6880eb8e841570bef98965b8573f137a8fcd9660579766ab1d7ee6067ddb956d72d3731827efaf7b7fd989c1be7cc12344cf99609e7f34a0a2e85740bec526d54c730a0de28037dc0821ffa3b146a6dee23878bea4d525e1500f512f0e1d6cedbf5a4ce5cad9410c6f1fb778a23737a3314f19db8b8e90e5ea2311e34bbc8d9f683657586e8b678414574084e654341cecc2c03e3ddfae21549341bf8b181cfc10b80c64c95984b4bbecc3bcb40bf922d25ebc22824a490d60b92cc7a094865956853694adc2f1e34fea8eeda2c31f4aaad98178fd46f03ba1f5001948ce1825dcc8d22563f7ef0194d11cb3965547a7da8dfaa1a9f96ddd69b1d5c22ed2f9afcb0db0df47160f45c56b57127ba5130bd97586dd08b5614ccede5b6362b34abdeb8f8d18dd7aef1278a8f3762d4c1448a6019a8900c857ae91992b6c6aa59426395e2c5f835bd50d5386c9dc359dd85b201742f77f92e1e69fca1b10d7825ae5c3e76689bec6ba2ed47fc4197c0ddd34c8aa3169930111a003889fd14c086c5fa197925c16fa322b385feb6a8cfc39713560b6a0b9f7788d95b1a2516c24df7c5b60e2b01720554171be4876d807781e2f85cfc4194e0919f3c906dff895a91ae911cb17dd66b6fb5fe8f5da2787e92dea6a83cd22bcec383f8d759e5b3adb75e08c40637970dc3e9348deea42cffc583c61f22b915ab89156697cb8d862b35ab1eba8c64239344103f63826b71b5319db8806e99c0016aefa01e06a8e7a58a46374d5b8f5764b6641d527114a207ca3430b7975b6d568a5614ccede5b6362b34abdeb8c89368bcc084fdba8f3df79e8c9a2c2b806903b1ea05bd1cbe4f327b1394124a0ea23029bd05beada2f91763a124a500a50bbdfa106ed4c0e9c202ac44032a5006755d5094554d1ba0dd506fc6a7ba58b18ce2fba28a8f733a8254adc0b0af56ec932491f628ee9a9234e261fd0ebb980908e84886c2258410022036fa5879e0bfa2b15374953601eb11f87fa3642519b0681661f5f1768925929248647777ef580711073007262653cacb6bfd095930fcd9e3c0fe3ecdb08dcd95978cd7cf1c965e27049d97f9f5b32dc6acb3eeed45e3f56bd57aa4a33be5d8f652798d188cf50773ce39e79c73ced9cd69619c73ce7befbd570a0109e1f89d4234a54242b5562124d65a2ba4e4de7baf10131d1db992ab9e9e564b881643934193614e914824ba775e0d880ce777b024df84c242065c912b9884d165175ce090e360cc1608210c1852066c1100c73b74551b79e4aeb530f03583e0898284243265208bf9f447d7a288f76ae7bf0bfd597b22576209268e50a10a4df83861852b3c01c54f8d42a6860ed8700387076e40e5f8000866420059a44bdc9606b2c87789560490457eadb5fe902bf287604450909004e03db27444967c64a9f6f470d973043805203d1f604109a28022091d95c61e008922881024c870c5f4b42c3940081c4418710322827044006a76e4a094520a84c5871835416319c822bf844f59d4daad01a5b9422bdd1ae0c25ca19fedbdb6928268132c4ad06b0fb2a0fd9279d5eef612b1cd85d2c7ae966d1867135982af6d2f2588a9b6b5eda5041f52edf1f6422964c95bb6699f350d37803ed61ec3db83407bd3b4911334cc0562143c3efda185eb828c8e11ae1f473874f58088ab0746eebd2326885690c57d131dc822d394756be07420cbac35d48a75b822f87c0cc4ea7531a2e051762f48c8b98c7a1c7ee9ae310631bdc23abbd71ab884528677fbdd0bf7b3fb9063bd7b217bbcabccdedd0b227ea594525e990c259c1302590151f1501117101d406e54199e55c4404db0d7323e8cf0638c31c618638c31c618638c31c618638c31c618638c31c6187f4609ea305c7e8c8c0548a207352493a7b6453ffe9cd95ec9977c9f2008d1983570d277f9848aeff248f75d22218da87c44f914df2987bf3b86a9ec8e691676d79d0a4d4af1547cca6fef460fa4ef5898ad396766c123fdc8b33cc5a5495ff22a3e679b8b0a8ca482b4bdbae291c5cdc8a39c0578803e55dde4242f45c586b2d479904baec28390a5247216bc17a0dec5ee48fb057bdf0b4ae9bc941f8dbccbb79792c233f9134fc447249736f992474131313129f15ac0562b7b146fb6bcf9c39b2ecf763d74c9e3f74ce4eac42be15d0fed4f3c92d689178a29cdbb138fe35d0fadffc403f1ae8789c99f781b1745930d65a964dbcd85e4e3d6629278f3698749b4005fa0d9679f7df6d96a4d1a9365e39c737b41fad06304986da9c8900a325cd153c870597fe4435360b2384cfcce06ba57518bdbc752d274c2e5635ca6339ce52ccb40bf25a12307e58d358777b6239f39f6aa0373016e2e9ba885615a05fb1612eb992d10eb89df5540ab4cd5b797cbb15d72d3ddd0bd4467c5e597e860a9c96a7159c223854cc6c4362df75a730ffa0c1a2da123e7382f724904d34c88f02c7e06eaf3075d15e1d84bba3ae97ca03bb6379045acbfd5d7eae7fab87e86575105057b9de3c1e58fbb43dcc1157f54561032a5b27a14b2c4d8834689cf5a2710464aac5831eaa174450d76609304156c006208337021013cd0418e9c235778810d2e20037aa0a0e3430e3cb44260673ee7d947bbd30c83394ae9cfbd3bcc8fbd38610b0e9f4f72935f88bc5b80d7981c06f4a2873090fcdc273fb537f9b97bc9630f839f3ef6eaf340c5f19ff0001543ffdc9dc6fcfa12a3d8f29e1bfe0568cffdc47bfbcd9b2f849ebef6fa0c3a306c3f77c7fd7cfcd4ebb8cf4f3b6ebfc09f3f3f7efd19cffc1646e4dad30f6d924bd38d378cf6dc8639794fdbf4e485f8271b8a768dc9499e3ede7dd3925d6372908ce8751af8c45ef6e21663722c01cebddd626c30f4edf3e34d65a9d7981cb43b07f89c3cef6d73bbd3af31f1e4f3e59c51d073faf8c0b9c9c0d953afcf30cadaebfae3f682e39edb1cdc5e74e8398ffee6cd90fe6ddb5e6f3b8301c7c8367e01866cc6c741f070fcf329eddaa31a00bde5f931fe197478e843dbe3369a9f72df41fb62fc32e4e9f51974ba1af4fc0d3f0d79771a387ef85d8b3cf320c75e56819e0c40175442d1811f1ea7062287a20350f0f8a296287244c0a18340e5084207efdf824fcba7a54410a81758d81ae2039ad371425b251871e388d6075a3b5a3db46ad0ca6905e183083e92e0e3081f4bf8b8424ba675c3c70d3c307dec00258bf80022c4061f3ee05dff10ac8f1b364c1f37481fa9948f9507a6407db07cb8a00691e78dec061f291f2b1c104208a1a651e210baa8cc967baf54b11e95db8b00b847aa7bd62923ac14443b8e448672095d05ad397401710408205c314629813002441108598e80d240d04268206e6083052207ab9c79d7313fdce009403a20644812a2606b58011128b582118dfd1ca177b0ca09020431a708a5263949f0ae7f02a94ad04074e6d095634465a5a05510057d7182680fe81ee81c558eff15393ac8cfdc8b284db4520b2dd0186b8ca5b9f22f99bbe042b59b8ca1ce7fc9bcbe643e37065962cc248c12b2b8cccf9ccbcaf61279d6e08c1983e8d74a27764339f932318cee2e867cfa2f7253cae7c314a43473317252bec41871631bdb91cfbd7790927631289d3464fa99a37476b6c61ceca575e259a3bd32ce4bb19a599c69ce53c65c338b6fc6b49b312ddb30e80328ede2d425c6af5be3cc159ae9960ae75e241a9d672456c369dec169de81d3cc03a739bc63ae6dbfafd6c92bacab296765551c4ef308aa8eb1c171ff719f8ef643e9fff457c3c54aa2f587d2ffcde80f06c485386e7b12ee49b690267992ad7e27093d365b4c3eb4af7e923721f91292d056b3bf14a5512314eaa3f9683e9a8fe61ee1d2e171018188a6dbb66d97da6da334fb8daba1e728e5fe94a2fdb403f7210f06f49023e1b8ed43dc872e3049381ae242dba85f0ae29e7bcdd13fd99c6ceeae2c28e49c737275abbf7d34dcb67da8edbf998d036d1bf7df0c57b36ddf428236eeb9dd3950e8411cf7daab415b76502889428d50a88fe6a3f9683e9ad46d41113bf00170e8d2f1d9ec9c73826eadf5a576529d54279b6800fc58d370d470d470d4fe94922da6861fa4691a7e90f620a94990b641784aed374f937fb239d99c6c4e36956571a0d9f9a1be996fe6a25020540d2de79c77aff1231290bd6999f376c7bb06bddf87a5f2b4f3ce396f8c8d7b4a9d6aee69077aaa39edb8f75ad70f5c40f87181d00308339b66826519efdd63ee3dd9a0e897b5428ae72f854dec4be12fa55fee7e3fcb1b959f7b0cdbaf7d686b2090e6b21a945f2dcb9b4645f18be967d9a43fe347f3d17c34a94dda1afca2f2a48a5ac3e2c329ab9427557652f1f92e8e000e5d3ea8f901ce0f523ccfcdf503192e000e5d3ed8e1596a71d34c7e5616c6fdde6cf778b239d940b13dfdcaaa3d2a4ecdb1f6e4ca2715cf18b3b692a4b598cd1fe5574c72d2db7a83a203b7d33e84d65a1269c3409ff42d6474b96fbdca3a31d1727b515976774d2b2bc6c62975aaa1a71db207832149ca532affa9269f766412e96433ea48ddee92e7938a5b4d49a8093da1b0a2208beee9c6f1056c779735d0a37cca29254dd8db3fd558294faa932a931e63294425291fb232db5eeccbd0b87d9d4a7938511e4e9046efc945492de4a986dbaf9db7fdc8cb1fff7ad8a778289e973aa564a9e2d75c999f6277ec64774cb44d764741096d153e7772d62aaab66ab5b5722fb717cbf16bb6e814d651546646712eb3380cfdcae277531991dbddfdfc3535bfa4514a5c85e3a0ea67bf7915b5bde487d1e82643e3f94bb3a5e61082cfa971f8fc934d3ce5934d3440fdec4fa9795271cc5a6b4f36a21687008819c5144ce4708d937ccb0737780238740d19c25d4376b88838e21ac2c397e299aba0b75fedd71dd2b4aa3b48537efbf5a6a2668bc997bcc55ef6f8e3ad39e6166383860ff549ecdbb17930dad3c023c645fb1bf69be43e1a98edb5a83dc9ee926fffa5b8fd18b726bf471b23ccf6da5b6b6f8c0d0cc3be1dbc5fbb517e1ff7af865fec4b7d1b86edfea5f8fd1a92cf96f9d863bbd7a0d8f78b4d0cc342dafeb68f5e7fbbbbaf3960b0f63f9434e1c7fe9b8927cbe597fa521c7f96496cb127c1b9c4839304db3d6ed8770e070cb3f6b1d6e279ab76b3946f9eb51a96853cd0739ef6f17ee661ea651f4a966e11eaf7d8e14ebd9126fd2a1c0e5847fd18b3ef2d2d3c5cc62ddbbe7ff3b56d7971bb6faf41f14720db86b1f82f0a6e30608fafb41fe3ee320b7770e0b5fe776b50be3df7bd65876bbba22aea4d3c3857b41ab465875f8dfec037d086ea5c0fafffd1c493b6bffc1f2ae72fc5b1cdacb53672f861262b90143d913b13cc19e75cad28a5b3d65aa7b5d6da79efbd377a1d122612e64ccd946cf5f4c4555c75082312f47c88d12d035964f46037276908975c3e8c19b2650a91c3e54329e5ca089b4b882bcc486d0d9724e0dd04be80743d4899821035e80881438810581e048942909e204c04e111a4479022d00722c8610948b021a2d584110429626483ce3c80f4c8394a0419c110417208b2023b0a0190201c8810204500d90192042046801cf1bf628922f3468c110559d0898d6024f011fc44be9167720d9752e2972c75426029f9d48b314ea87dc8b5d206481313ade9df174ab3c77e46f65886dd8d61746e5290bd29110c9b955639271de9444abb97baad0d19aea8f6b17b73bef7de69312274c7f8c910bae31e06f4f9b94d0aa2413b4359dae226e5f673ceed29a574fb5c3590576bad75fb6c359067adb5d66e9faf06f23a1dad7da6235775d5d3d36ac5d4fd0e5ff7efb63b1d8dfdfd6ce33ee3fc1db75fd0f8b67dde187f0c8f01fafc2fa0cdbaf9e106da1de8b9eff2c7706ecb00da2f447cb332fc7ede2f441c6fa9d327abdbd0e13466d6114745e80de18a9e5b94d627b9b4555916c0127df873255b6c6aaee48026bb92a5946cc93ceb035c49ada4c9de48cb0268b2318025fa94453fd7550ca08942a8007b43ff469a26ca077bc32908eeee73a2f6140177799544644b2d2257e8172962c40812481ca1b4cf1ba8b9eb4e2784eef666d4ed4dee8290d2627c9d2e88eed8567fd25a6bab5553296bad6db5eebdf76ad3c3300c9b9e8ecee6d20dd15dae7a7a5aad988aa90e5fbd85212ea420fa65caa0208b0c2f4472479ae8eb14e91886611816e9963a58892c2b9e81cd15696212b5b6012bd68bebe123b8077ef42cafabd98225eee996d0bdb2700fc6812cf22bc6015c913b2a45a00c8183e0b2e6ac642a0ba216dfc6289c52271b27642868fabd4480166494af801630302c7b98b964d1621209990823dce3d045c4877af8d97ad3f54ceebb1d135a7bf9d8eb158531bb2515aa0e490a5407b2789c9c96eac715b40eaacecc912b4ad278715a3fd487fa96c8342a12d6f27052b5843033c445841cd710565f8a240211e65245a1125c4225648f3b32d78f71e615749163f5348e8b0ce394389c0e64916fc283a35add1f3835bad7d5ea5e9d2b9052a4142985d13bb1b982daa979f7c5b0ac678b6808b8225f0786c3dc5d77dd42e9ba85164f551df0be0e1c70d041071ebda6200bec7eb636a7a00b0a2395a04dd573caf21c2795149b555ffbd656eb616d7b31ebf642d3b65a5bd105bc9b14024db50ab2885cf305591112e85e738acb2c7e26eab9321bed60c61965b8134c20438c9174439c9174439c9174439c9174439c9174439cf7de4bba21926ee8e921dda0e3434c4639ef8f67c00d1c1a0eed6b6aaee4d2dc628070a5e6802b546b144639b76d6bbb7b611fce39e7cffa5aae1ee8b5cf9aa6695a7e08d2be7a96571c69ea95c5359ebb0ab7b6b2e6ca8ed5bcfc953531765933bfcc8ff367f9b1fcf77bddb17ccb5f71a449db3dfedc5ed0cfbbe2c852fcbcaf576bcecb42a1aaaf0e097d7924c2e517a99d154838905f5f524841f1f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3935fd35ef4ba7bd560f2d75a9feeddb3e6410e52654c9f33714f7ff793c76028a1db8b9eee6ef2982c4d2e7ffdecd99c3f05fd92d0539d5f6214200acabfbdfe9937e82d25f9fa21afcfc00303e8e9ee74f53afddad74eef17f9b57f2dd3aefdcddb936c924bdbcf1b667bbd6152fcde291ebe8c5b8c141b9eec1a939774d1a626bbc6e4dcd72ff13a0d9c6a9f3f674fcb346b0a7aed3347bfc2a06d2a4b35bec6cc959256a0e90bcd8ff157fc79d4ab8a8742311cf4f4435abfde1a85c5495e6f245ee8413fea28a00fed5e551cb4b1b661f067bc7ba634e7a7d5eb28d5037d556920f2d04baee53c030f277992dd69c0c8430faada570ff4214f6b4f7290d7b9ad7939bfc6d33da129dd5ed407e1dc6978f184d6b28a6f1bf7b0057038cc80ce0c3c7ace0f18e1041e55f303461851594698448810214284cb32c4bcbb26b8ae09d20a8420048e7bbdb7bd51dd35a129e5842084ae099726ba2664483251903a40aae1458c8277affd8cfcf871442ab1a963acb5da7fa1bba27645f118e5a4d55eac7342cb9f4e640ca33efd97ba6bdd178851c470897dfd174c45dd1df6d977f56378b665c0f60b1575431639e326e7a494d65aabb5d6da7befbdb7f3d1f2e7ebd5d3e3a3337d51c1ad0a4ebdae7da042109e28e2c53307e786d100f654c34fa91be4c975559894d65aadb5f6de7b6f15b2b9bb2a6c2f30ea9631ecae329b4a39af4ece1a2b6b5ee8418f437731841ef43342f8413f43e3e73e474a65c5a6d52f906b7f3718906bbbbe1663db4c68bc7bf6127f8b49437e8b692b9c33079f4f038dc17232a187d5e3a462b158ac53cdc9c608cf4b966a12ac2458ac1e383860962a5249f1d3efa39f792d2f4c77457ddf8777b8e178855358c56b37858afabeeffbbeeffbbe6f0a5a7e9da9298c5595c82c560f1c1613b21374afae8cca285746b932ca65249b52b9469a6cde9157d294f34dbec93c9c52279b277cb28ccf135986c7e5e31aa24364a7c82b774cc82a8fcbc7354487c84e919711976b880e919d22af93cd694746f9b8b2cac73564e7898cca33ae8c3ad99c522e168be53ad964560622c315b4eee031827e7b14ad9b8031c658f7043d1fc356b8ae3a23344cf635631c9c835bd2f4439a2c1e8275a429633f7f67a70886fa68eac63cc8d28d2cb1f00d6649d38d190eab6b42fcecfbba232abe3142476c879bcac3c4f968be1a6edf45045e619734757c5357377c3ec62e2266f87c5c57f8a3a91f6af5d1ec8f0ff2b61ee35d7d34f8a5e5d82c08a46d191fea9b49d9ed356f8b315a5b6be5360988a62cfed1fcf0795c6baeccac7daf3ff069513f5f93b0696d5dc9d2dc814fcc8b2f37120edd31151e9fe274fa43c470200b15100f6481fd7ced093c7ef698fde8d95edf6e1e280e6499cfb9301c3ea38d7d6bfd7a81c9edb79056931a0d6cee98a061ee576e2bbf7f5251cda4d2cc92255b7b701b6316e32975efcf2de3de7bb39f5e16efbd11a390d33fa9a4a9fee71ce9a8d5c36da49462d845baa1addd24979e36367dfab5ae302898f03ad9f4f0f89c54469038f23ad9e4ac74cf2acb35cf2b9e54324ea953cd2975b239a94ea9930d76aa39a54e362715764a9d6c4229d6c926bf42a9532a7ec633a37b7dbd727e0c215284c8eb75529d6c705a2e9d9d9316f6a1c369113a45f4e0396a5d13340cf695d39734c52f5587485324e222a940f73cc3e99354a03be6994274f873e81ae206bf31a6c754978bc9c764d0cda5d6da624acf6258861f0af24af3c5eb558f72f8d1e4abc1d9922bae3b7e34355096e2cbcd8564d38e755d8291e4974b1589aaa88aaac8d35ce2d5482bddb58c5e7992570f7208ec19ba864f159f0f90a5a9656456d28402ed8a5a0af4bb88b626c32ad124d9111dfaf939bf66e6a9358a76ada26824f3c8d2fcfcd264a469669d01b2341f0239a4a0ec151acaf36080e2cd45f4f8d5234dd5088f341d91261f6992bb7a9a0e1d0169923fdf021298408d0d4419ca611045484119e5534e224acaa3fc8c944741f994944fd93652b40c47411672b6a4701bf71b4acaa3886aa4498412cd8852d294c2ea30614116f22d8f9691a6d0cfd737a449f4f3354acfe41c3af8cc2f1e3eeba7d4ddc580f2293f03e553b68d1eeadb48799419231b283faaa28fdb8b4a117298b020cbccdff34bcb78fa8646cdc892d452cce875f251bc2e06b967a4fce867748ff2dda3fc4df1505c37e4f5181eaa0fb790482412795dfe7d121bc3492e8cb917204bf32b0f64999fe2d51ef8d2282db3842ec9ad6529bec6b8ee9a6b14aea7fece1b7928a1d093889ee44bbcbbb9947c9e1bd531a1eb87ee7d9148e499884c48fe927c8b594212fa1632b4b15e5139563387a6654c7ee0f33339efa6504a5be902e68e48aa1802a1fc88437bee716c0f7a0ef4803d07b4e7de6e0fea62d09efb19da737bc6f6a00de78a95d8d3083dd20edd356cf78e098d5f3e86bda59f79311cbb2dae2a5da87cda44862b7a475ad5a0610159a2ad131d7ac29226c933942da396eee1ebced3e4ca08c36016ed88aa7612c2b91dfae49d104e08278413c209e1e49d13d18e68475444f412f1889010f5887c267475bd6bbdb0d1f14a2c42164c5a6f022cbb2370f2e3c425b704ac8bcebbe7d1772871d4fde867743f1a7d47ea9e54a589498aef938b1e9ebc88aabc8aee873475f8ea70baae254d294f497ef40a9db04e7a9ce09ce448137e2ada5902ef211cfbddc7b8907677318cbefb19a3ef547eb4678cbefb6edb50f1a30d27c977d1cee8055968c977e873c23ae9e19de0c8127dec9de4c8129d910137545ec567d2091e307a16fe01dd9338d0c30c154f7a0ea8ecce3e0b3f7a169e032a7637770c2cfce83bbb67b0b047dd8cef9eb4b34cf332d5503cfb70d3e69cdf4f5815653ffbed710cdeb2084c09c4ccf23269870e7d664116590a9ea4f86973a6f3c26dbd5e511d13da7eb7377c061f6d3deda717c32528d33228417d062e471ef729bc3ef9c9ee35c715b17a5d95785d93787db4044e3f847116c57956464696e80d59a2dfe100aed0d7a103071c749072431ae86865a84569a4558d27ac0e07b2b09906da992bf4a5bdc93bf341bb63d8b6bbe632ef6495bc83034effdadde1904077e8b3011b3228f9d44a6f44ca418fbefb3e8112fd6ed48d6a74cf3b273bd2248bbca489070969eac93ed877b931593291a50925a4588c1464edd6b804e0ece95e27f2919052ade0cc24f5435e867273d93e574d6e2ff821c4b964c3398364d71488dacd65d35abff43a26b4fcfad1eb33f850eee1169f7a313c72da83bcdee2e25a7ecdcbea0c3abdb22a4e652da125a6513836b137f160c5bcfd02c9cc2747e7d7346a46a7a4a9866365199434cdc455d63eff0ced737e2dc6c59d9e8d8f77e77ee0589025db3da7b28c976f64549e91a52ca393f14e943d51febc14c3b0aff17a31bcceac59433269f58009c0e8d0a5a12b46928a64021d35797dd0f43efe1817d8dd5d0cd9e39f913dde367ab86f23db312eaa0c4823d01dba4823d0fd1f3001180b80c10069eab0086d419a263595624fad82cc7aea0ae4048c9ccc6cc95ede3cf6312e3ab8b78d1ee0dbc03edb70aec4dd5b5e3330ded0058cbb57528ec63efb9ef5cc16e8423e7703b4822ce2673dd27424f3c99e70a2eac0bb3b4807ea0468055964d6931dc97cb2276449667a65255d41ae1f630a397b7a68b5af170b9c9262408ac10b22183386c7592f10120f120f120f120f120f120f120f120f120f120f120f120f1e788001e9a6071e52509a9072d076742d7c4196c9f1e03c9005fd69654a8ee073ce121ec832391c3ee38c5407b2cceb552b5ba3158f9f47241c342eb916c6c1381807fb149fd9525173257e2aa552ad5612d405a1b923525690452b45071ebf7347c494156489d8a487c71fc5ec0559e2ef395ad11ba880cc80091549875a493a9c5227153fb94ea9934a4eeb7da869739d1f4ade5a6fadb5d62f95035686711c8655ec6b94917bcbce47b365d983344c638ee33eff08d318b739aeae76f0ea4d0a6acb0e958fb1bf9bcb47130d203ff3b4867d7273c1308d9402fda5ac900c576499166f81439790142f024ebd9b8ccc638c5edc64d05a238fbdb22476b2c959c35e3fa5409d06c89e6cb697cd25d656fd214bd5655ffedd5c6e8c2414e8932a6eaf53a3c0bd58be4f2af931d3ab4ae8aea2a94016707a96774b68cb7b8c98a0ad8717159c33e6bc9a9a2bf77e943163e8a2b6b09c38ea0479d0c588948365f00d8c92a699ec06b290af3d5165ea0d1770b64889f1ded9632fbfdc76cf78643790a5aeb8b67b45d51bb254ff7a30db4e7df8bfc42878cef93bf5f05b2ca58d9dfae0542627145966071ae300beb011cc8391c07287cb22e0103088528e34d11a9f4ea8544eb47639ebd4cc000000004315002030140e07442281402c8d2541d41d14000b8d8e4864509b08035a9203290821070c32c410000000022033421a8100f24870c83a800ce3eae86d3d0f7e390d2ba27ca400b8423a61000c8de64282ffbb893e3c2e61384c521daa958ad762ef023a760035ee6e39162cbdbe5e07e3830192e56b9228429631375c2928c2f5aec630675f3eb28457664c49972935545dc4f73ca61f6e2d2333caaa7c9cee6880d1ef7d0b88423b35555004fb30243118aaef5406eae46b7a7c6a805c54cf8b2336074397cbfb201bb70946777f8b5d418f128d27837e0ad2232fc41d7137a5d7e2f0e1574c6de0f9548f3e9a67616c3cfd6ebd5c6b82f4dda89c9295ada9bb354e186451fce7e8018882bc3d8e129b52f8acc03d36de6aeeb783e8b51da540418174223181182135704c3d8a416cdd7ae95d74bb486ae45bd027f49c46694294a01d3dc3570458ee48a112d9e166876cd677ab10523ea293884a22e35b51ab9a2c38b33d25ea6819d3611e512ae38d7e31cf37f0b5a2b8c46212455f501071332e66a61bcfd85c03f94fe2817adcdc607d1388873e67eb71f3712c12dbf20c8a8570219e3b1d21bbcab5b31b16591c6b41bff53aee49c2df445b0c1cb7b33b0d111331534f108e935c07cb8c4087e0fe6113123043b8d575f58015a120a29a8dad9842e3d7d50c248db05cb884f10ac07441f7849877d2e850804359b43478c2f9721381b81fdba86620673f88e2e924d8d363c8a551f34666b08eeeb219d929a175d43cacfc4b7436dff678cbe3e5826f77a24826e1536fd751bd99b7d309744be385b6403a8f1bfcfa4264c14597bd5832ae3872c19ee0cd6423e441765ee69ce84fe5c81ecdee23e2355363e61ed13e42bf890127b4b4504d792a93b623f443e46d9f9233b5e2c8fb20228dc10634fe8689f170add37b479b1042b35e8cc596cbda64a5a215a86ac4aac89d883d86ee0ee72d095ae5d44d071384b43dfb095d4660b9aa2eba053565c727126ac5b33888651881aca877ed593b6bef7c4e93648308953e22556d0d33d51867fcf94bf6c8109b0a063fe9d984d929b07cfe32fdfcf10169c057c1164da5b3ef0c86368904ab48fb82f6d47f83390ee4db0a2c9507aa01a321bde00658d61150df1862e3a0a017ced37bbc442529290640034c341a2f0fe93a6fa431413150faac9e5a625c2ebd53d38e9adbc83757e7b35c10a45327e308d0b69094b7e86f97398c7111ccd5325d93b88f3dce36583e94e8601a0b7eef98906c60ef1c6a683649de971a3fff4f80c42574e4477009b206b43dc50cd71f0aa1d9c8e41b8645479213f74d4631ee1561216b1da8628f5cdaccd041a6b7753c1b7446f423201273ca12fa21e9191060403f47585c157d1ea07d3612856411d26cfa4e3806d4509a03d39e488c0763ea84f6c3d000203e0c8a9f2c239801515f84976d78424cba8251294bc322345c0339706c643c37c9d6ec4f708d98316a21e2680c1430491f4cd17097f2e535989f392f41573f2b5cc6140b6e4be674bd452cb99087ec87ec4341ae264f8002353cb2ba4b202ef904baca3d4bb7c10a3b57df78c368ba28b890380805b2a93668a931ec6f3312deca0aece055e5242a763085c4809fa1ef6f63d4823ea2ecce0a8874aebb52702598074bd2f446840ccdec9bf23b6cb455d1866113caa03129d28641a880062903acf1e3f0cc81a9df509ae138374b896fde35ff4d788db138ca5364874ceb285c75ac54ef4dfe24104a3be822729c3e2b13b407471ea50a8373dbb039c47241f17720dbd16c4f6f2b095a5fb8852e17b035ad69ebea0e9062e841ad311884b1f81a01b6b59d1661eeefe1abc83037a7d22e1009c6c5d52941f0f19eea1f02f36f1437e45f0905f6d42adb5e18d08b1397a7217e252d6e399dc02cab38f047d4b7e21915c9b9ae530e196cf567cbf62b8fe6511e01399e76ea8050f268285c30b674abec4a5d59fa3bde556cd9f950435c920a9bb5801afb382b3016fab87cf94db842c3b86d963cdca97ea50818fd9f461e3693274594ac2b8c8ee317dd1fe4124619f19b15e9409839f7f0257bdcb87abc9c3ffd380578ea43d9366936ea4f2828c91f87211dea219bbc57e2a9a9815c74e7d1104fc22f8869a2959d86c773fd7442e2c206c433ea7c55add4bcf309e202a0f8330809f4261ae00aaeca4e5fa101d2fa6844093cd682dfb368f64d7d00f0be59615965b49091c553ce8f91bc1143825470f973e600f1a95abd2793898d1949c2530fc42d91804e63113813b4c43a02b148809cb34a1b87b3008d90e8edb9c1aa317104fed0311a13fb4dcc8d5ac1ac062a573782227839503295a711511325533462ee68f423a9f45bfdfd333438d7ea2b68ddb6db82859a6afffba9e6b36e29a9be63203faca6c4d312d999bc9bf0204addbf7c45cb6c7cf5e35364ceda9231016be3fee620ae10e4bae2889469a37d0648d0ed44e3dcd69d5bc1ac42300766972cd11b46fe04f665d5f99186da0f10d6c1a8ea21b2b5b07e71a6bf3df408e253de73659207340bb5893e63caa4ab020c11ba27ab59715583d3e977520766de65c70e8d83f39c167cd44fc50bed8fce38feb7c84ec0a840af48ae22959392bb1a3f872274e55fc84a9750c271049d8bfb3cf80365da4f2445c93b702f5ea42cd44a455ebbe098d64ca9ad0ed6ed2d7cd87f0bf70c4ec049495e5a29f5449a299b0658bc3b49fe6ee41e75f44f63670b61c71f20aaaab8a710a1ff46baa4b4d8b601c860d8075350f244c7e02d09e4fed013a5d093e3495d1b9da0e13840fb6653ebf99eec24f4570bebc7136e17f33105c1574c08d4b3755f7fd997a3db24f4d3115b034095e8b3dc88ac781b382aeae234efa9913da93a5babf1b44c7d58c93f7634314cc2995401fb996d8d60e8833fb692b295a77b3269224e052e7b02da755afcaa738be2f301331c677ed22f7f5ba21cf60d7d7d107cea5410082f43c60cf358efe78f4cd09bdc2b82812e44af024db2d0b4f17fb4e46edfab60363203583cc640616ccce6edf9d75df8a6dad824ee0d9b66130b691a10168ef34154e8558c4bf98b9a0b6cda58cf3c4d8e1411b441d13d40589bb236b40ba714116e50d33d2719d3637ddad84c8b2c061f3ebb40c8c549a06ed9f0702a72e2b6c41913180b29b1631109fd080851b54872e157ec01224ebf765a5bcd37aca0cff4dd50b7f9afe164b3679a7130dc61d0e4c35a33a46b7604c939679d58b664da91cce0f8d7185b8d30d0812a8343b7751db2b78bca6108a6ebe9b6929bb580d969d492848eb92327a1b953ca9e7f25899830bf9d5f0921205ebbcf0a4d9ef83d0b1d9c647d7977ce94fe976357f9b21c811971d2ad1b916815321841ff58e110f42fd51262c27b98a8aaf6906172543e61fe423e118869054190ed5efda12b7996c69bd735850150dbbe9fd88ded80e55eac5cc3bb6a1afeaf86e3aee8153dc551525c7451b2751e5ab9c3d85d8ee01f93caa6e33cb63146719b3a77cf216e1d1782012eecb8595c5cf8930eec710d163e760c4d4909cfd1b13c9707f813c73435ebb0b4565514351f48eda35e603af62ecd45df959a2454bbd5152b6de39592426db67e04ee0a4d238ddb077040257dab12748fe5177ca428117cd0a3f2d921a817c12c352b42a79a3e18ac7c200aaab9751fb72f83dbdf49db92a910fb829d91fa085f2f2b812a04674eda97632b36404d1c227f62f0abc10f8e4c885d39f000051488eea7188610c0b44a51e86002f3a7679abc0fd1538cde2c7b3bc3c92fac772bf1f9d99e1f45c77bd1c450b4eeed7b83f2ef8b307180a8fc45905b2fb83b59f1a28f2ced05ad6365a5097f7fa2bc82b109639f21567e17d8e39899b9fcf26775f07d52481d46b12d2f519729dd0ee0076b191f0b044e5d3ea6b29324cba9cc6127cece6eaa50675bb767785bf88952a1a75c8c6123872ffea352203efc93a3960f733b589e8854333c0acd993ae2e3c93b72bba0e8d3976c7d716d5cd93f90ca54ecfa095020061ff600af0d3841301502f0033f876d9482f80120721a4d38c3ff4359c7306738851e83a9ce37c1f3fd1bfcfc49b420d38169fb617c6bd5be7c4d593deb014f734ee20929c7ab4a8221cffed113a41a6ef870ea84f621d031f4caa287f35b572105e9884106ecc8c8af4a873442c8339ebf39732a5f536e796d183afcc65a3d10d6700a088b23396b8b92b53129ec0841f9ea69cb13789d21cfef98a8069df3f65cc59a671a3c17d3177380c775f8da6a5576532af04b7d5c89fdf5a4405f1ba525069a86bbd6d6d0d50fe077726dfc4045a5098693950e899e5805a6815a2f5b025c1560a127e6b8033540c5e027b18ea225d113b161e8e09bbdc15b118f48acab48c9f4446c743af0e66feec899ea37e28801ab6b8c9e408ec488db4f767bc1d3acd5a44580b8c56e6082053d03dcc7139699c899aec9dcfa069749695ed20e03f2bf447539556039363029559f4fc50d33a85554b8721a14648f04395389bf2b9a0322f5a2a2b13ffdd0e0c74064c207cb78832e8814c0f5f4ce188f3af689734591a12ab82a156ebd8006d21b504128f3c728b332890c2082073cbe9153bb82460f08bcb9818dd462bf5cda3f6dee01c4be6fc7ed3ec84e8fc228b5fdda3de1a6587af42a5da2f325b2e00d7417d5cbbcbb815209bb65672718f1ded6533e76ad53205005901011627b612b025d21cd526dc7b8332f2ae1a06b1a992fb9eb1ac977cb968ccc3d91d90acf995f34e83372a751cf46a6aa3426ddb637627116cdfe8bfb2f19918dfe6a423536ce4b27bdeef9ce6a9b5e5c7c4f4a6ad9617f701dd911f92841c74d25fb20d6013f56cf28665bdfdc859defef172667ebccf0bac099081ed5b408f1b0da070591a8433693935dab7e0206e42c6ce02ecd6e65ac5b8897aeb3e76f73f1358594a09012b0dd19ef22bd41f8675933cf4835ea5bae942facdbf2c061e61c9b9f6420e6f9af2514a30ed9b5cbb43f6dcd813bc9ec7c0336ab3a76a330c05e560c4d2984c216d779940480a0412be82955504802fb02d9ec6d3428add1fe533c219f2471ba88213c0e122bd645fd4e9b9ae0995d68cf42d4e43079fa74f364eba71e88f053469c4cda7eb017c478226aa5f91ea89406fb5ab109f92817e3905b020243556df1f8c5494231391f4d160edaaa1e2dd3780a8cd75a65bea95edc71536cb6692c61c1506fe99ee99678230d82aabcb9350fac6f20b5b05bf994ea7706aeddb48cb869367a13aa390ee4bb8f8185e43f2dd4252202c77d15ad38e05683cf597ad0914519d895c8363fb28b6449bac0c85191df6a0ed7da7116388b0eb138ebea52a6c59f95e37fb76298214478f65bc4f99cc9c1478a607931e799595e08c6e7fb424393b06409aa90a225407a1d6d7e3ab4f332da04a26be6112172ef3e33ed18d662f24a9003306bd7b4fcb0a8c3a866964bd2d2b0f8572f9e0c81b2f83849c547d8577cb7539b2b74d560fe294f87ce818d989f5db96e4a6206e3e12a2ccc3ceb24d3adfb095c7d4e418de8e6ea0c0e234ef8d98646774c6b314410231113353bb473c03539465e4e689f818e485acee9758f13e960a204269908a7197d4d88081af678a42ebeb424354c6426318a0e850f042a49788b1aed07550fbbec05f6721e3ff7169c12e1fa6652a8e31c9079b09dff8b9cfd74cab9ab8f56957fb6db283954c42e51aa730ff493baee4ddc40ac34ca160c488c92f8cc3bc2434947544b5f74dc27d43d705bafc0ab5dbabb2711befbdd7051cf6012c1d9a6feb6fa8750fbf484893292de7e3ed34eac260d79b247db4d09ee28ecc502f02d0fcfbfa32f1d83850d736596db528a2861516ac44efaf31304c147863096875c7d6e9139e23e1beed013e8c942e4f2c8938ec65e964a85918eec632765833cfbe4e18947dc03c7a529c821c7970b70a87b9d3fc5b0be66b8d1b0ff6f2381a8714c91cf68d4f3b120203aa46d2736613a48f8a2a71bf1b8ff4e60f87e12b6e98e4be4ed62db28927d8449216ebc5f8a47441188a9f3be74fcf4a37b3b121e19efd2e3736c36bbc3d4bcb079bc213d8e14f4629101878819b4d013434a1e7f2a3cb863109dc3f9cb649e504b399819075afef27975a68c926ef972bfce2f448c2fdad88fc0a8700dd995b4e62d86ef6bb80c6b1cb0dd1c2a5186b6632eeb920afb3a797cf8cac9e759147926ca6513b2a04c9418fa89f2fc2ce205c3722c336119166fa279d10ac5f59529d4725a91f4ed0b102745c55de0ca88663a8a42316a2131795a9b00006fed79125fbdb1105458f5ff6020b753f886e705bc356e5f2cf3acd33c4e8523dbcfd0a7114f450b1c19defabd038ce47187ff9724f7dea9f7905815bdfa489abe786b334d7b533eead5ee2982867d39a088dadc913a226aebf2420cbcf94a460cbb9673dc7ee4254c1b8ffd7136ed113450d0f03ce4e96ba5e5c14c525d82e73508ed07c8ad2e122130dca15cf2302d4d021bfb66ad43f99cf72cff69d69d5f32cb62ab4df271534f53270b49d57d9614808e60b0e6338512779a4282aa9cae392888d421f748960d71df8ea546b9872f7530e8403fe722d69f258a9322ff13801372ddda838e845f88970d76871c5f1e9b38c4263582b657c978732d58fff0856ccfebdaa331c195075783fe1e3ec4042b43158c599342f23e87eb60f483748f3b20b95d18732fedeb8b29ca0cb7bf1ad61e637119021439516aeaf0383d8537e621b758417299b424233f2131939bff467b37e109954e36e8e1f9a6203e9c48dc41a46311fce3d4307434cab8f5220d4e413fe3275cb10dd5fdb9bca2279ffadc36d72b6727811f78b1d5a4e2b6a4ca0f83d713c49b621f8770447b4c40eaa4019f564bedcb164bd250556f3f2db40bd60657966490a0e346231e4b970ec12f0927f436453096006eb32f5a588b3f34c89dc110900113d3216e27ae658916290aa23a74c86bd9edacc79e8ae93e2ecb403e9cd7e63932488b944c701f7b7f90236693fdfd7d62c8cb49f2a8080a8886f93455b7c9837cba29aef606f8e3f2baaeab0909372272bd52ebec7ea5bd5102d3a6f6c69e37b90249f04ace36ae69115da976a3032b41b35123a989f42e159689c96540b752376153e0d7d2f02528da091e5280122d837121a32b054e5d59bb3667a95a01b8d37f73d47aa997d31b08f51047f90ea9bfba9e8a28ce5c4c3b308e062a0b92120aebe8b443d6382f83421a2613bfd8db48fecf9060c313c3619bf7ddb4dff44db0cfeda870031e5b50707e01e21cf8bf3bfefbfcdb0218b4f96ec9e8def78ba8e5ca5a94eb8a1ea64a6c45bf42944f7bfe7c8b8e3a99f8627f23fb3f3b825835082536aeb1390d135c355ce3c4b299fe4f601697cb6efe680a38b8b7b81ba8c096a9c7ca44ecddea909b68a56d7a018e1aed574eef09ed44f4ddfec9404c19e362cbb6ec6f479148cd880eb81946f1b50a0b7e7bbd154f20895d85bfbdde8e49289c7a803fc74b7cfc650845323e2a40871d228aac00f0043eedb2496b331981153a218349572d14bac6cba0328604e0d04301734559ee9036712ba7e11a96e85b41c4b4b8c346ba4d04a1b0d02502136b5902a04803b950fa313de0d026b6a5539d0206aa3014dbbd12473bd5af89dbb74251bac3adef63baa08a39d4bd5b897aa70ac58ed11580bea2208a0d985ce82a5e0df99dfdfa3ee06054136ad2bbbbc10f1b065695ad263959c9f51d29fe019e6ff6ee021b936a42585d25ee71c0e3c9babe6126ad0bd714b46bb74691eff21abeeefd0caf6a59f995f10a9e04ee2166c0ea84c6b360eb50197abb33c843a6af6d90aa83f932fd06810b1cb35c3b2cf791e16e8bb301c348cfbf052c218c891139b92e20d4704a085085f0074f9adc99220f79d36f1e2066dcc7dfafa00f25b117584bd38028a35a3b589c28f38ceb6c430351c35d138e63609510d15d7f20945b556697a0a333f660eff6c41e9252483f6793a6b45d8dbf8d5f47ceb225552702dd99e18179381d5695854d485b11337f1e09f492f946c60d846ebbf54a5e2c80f976424acfe5bb978682f4563b260c7e242e0c9c1950b9e95b3eccfc689f9e985cf53b95b7c959838d779baecda6ea7f51b7f404d6b8332ca425dfad1ba7f4bec83198d24572127fd59953363d05617d26bd2fc37f08607db82f9800ce3db769b7437c9ed51efcdc880801b97bb98a005595d0c29e70672c761c5fa31cec122dd8408d3862994072229dd0e5cc6869052e81ae0997c462af0d5ea93017f56fa5c4689626bd075fa54fa4cbc0d4dc2d863289708624173c114a68dc6835928fa2ebbc024a9c775d0cabbcbc24e9d6be1ba3460eb1183ca5375a71129d54f8c0d89f82f508d3b5c9cd9b1623f04907d76e0deae22327b38e1f390059f542a6cd4650fc3518ebe053e7b3f6a71cb20c80dde4df45d51868ff281f91a69e463dad4aa79ef48fe475ead6481f97f5d8c3785058a781c00edd1af9807f73c72f3509962691e49426dd15c5ac91d64a9b57445c412819f882fde81968f0846defef61f4473669bc41f07154a3416d4d04eed4e3cd93353e3448e5025c8acbd6d45c80b2e01ce1e1b52f8086aa41e2cd31cb148edb8205457cb89fef6d4a50a6b5cec237fbbd8ccd9483c174feca8af6f80b22e3b3c6828e049114aeeee01b7c2c109a811259b9c95976cb43c9f1ec13d36a04e1385d2253fb8ce0983fa6dd54933dfc19070ba48b33e44afc47e904ed14bb0d666d510551faad28462455377df3ba0b68d4c132dbbe20c2d62ae1e14ba1e75c3ea2742b32a902e2d1145b25cb4a0005f66021834680da4450c50700e9019f93c023bf2d609f4a723234811bd3125c3114422c497d3247a4f2b605f6cc85dcea86de4dce8d1f4a3b4138341af070a4727857054572c99cef8700188d37f2088072e1fa4b8029c7175b50319f729208d8df47785b67aa00db3a00a7dbd331ac87ea51e1b8e50540c2a9407826fc3c51d9bfcfaf1c4001a356eed516604ba15caaf47a5770cb587ab7a446b3d475b06876ea8a12386e75cd8202df8733946b0f302a52148cac9744c9da62b8a2af351ae6927c72e69bcdf442eb65ff269f8b869525a6e79f129ac5bfd38cedadb6af9d1c94beca6d4c17c3d3304727eb344ca06131cd262ce848d364166998a091751a26b0a4d1641a16d230c9721a26c482934ba38facd4dcbf2b18de6dd20d4a764e8343cd53baa8311f9a66798670113279d567fe14f46ba1ca55b034fa3303f5d30e8e0a8282be5d99dc8ec4c50b1486d15e2601c84579a25a721a0bccb70656f7e9182378bd203f6b2fb382c82522b417abbd4c7fc53507335a26c6b1e36b24d866dacb14d2644f006b6ad8cb8c0c94a17c50020df243692fb3732a49398f9a1c848dc3feadecfd1d6985ee7a3da999650971d109087b997a12492f1e3e104077c0874d622f63b8c1b1a574c141e0ca4c1eb7a680031bf29543947792a17fff94e05d6671a640d469abdcd5873463493b882bfe3642881707ba4524ae5e32dd549cc970196704c49ef056ce12fe9a9f52cef4103d870c53f81425c697435ae01e2e7c35a9ef356c05b497c157c05fcb8fb37910440e4b5153d12309f0f00eda6d2f73c79c337f43e52b39d4e939620ebf10ec8261a69501d071dabacc368df877985fb36974eb75971d2d6086adf3c2f332c6bec24e89b40495ae83eeb0838cf2aa5ce89c7fc95cc1bb36238cb9d498975954443a5458137c12ad109c64f96f96ce088d38eedb4ec300e59470213e2fa32fa1194b76301bbb6296da7f8f791953f10a5f0a69593b5dcef1e665b030e924a8841f278caa2acf705c22129df180abfc5a6194365f6434c530fb26c3a69335cccb5cc330c76b9e3a5907119b6fa76b788ef72ec0af51afa9afbc11116da9220e1410df33a7676150ca7a720936b6fef6219e612e508536397f617b0e84cc3aaf042d92782058bccb072ca615feb692b1141618c69f9749b1c2ff04785a28ea89b79c25c648019f92b12e93f573f5d5df77daee4de6f2c4e749391024abf9c2c92a1b2b03e2416e736465bc0f675556ee89820fc6dd945f22b89691967db79895d76456309afa4d56e7e559e106acdbcfa66070e39c6805989a6f9956fc0e4435e95b2c540e183255a45afebd595c34bb9a5a99a337842541970b6cbfe5a05b0e40106bad646c476c4594d8cdb495e6468c5b516f79b25b5ce00aa349f38d65c5ff8adcafa29cd3732840e04031237a685911715bc4d4e93d1193cfa23076a3a7b0132c4492c63f56e11c8eb6626784cd9dc81114d1beb902d6863bcc6f3628a25b1545e88eb8c5a8f137d81f19e15ca38c55c8bddaa5c3c2c5fdaa3f7d7be5176711ee61db2e1941fdf84258459bd2f1f9b7c3e1abf0a295bd78b0fd5e7010d51c15e1feb06d921b295edf1455e9c94c664ac8eb8b9f348df4de51bf46c157e16a05fc5acb35e3ce1120f286087db21777ecac39419ad264fa50dfb666c8d0993e4a7e7d24104aea75c66b43783b0855a8f1443ab08da9b36130e299307736e429400a085f805734e448e4a80b8b73202102ca6568177f887b653b317992319ee090e914557c2383b530aee5a1bd6c75f0d6c6b4465a247c880cb626a037da905c75bf7a0a266258c8a75a26075210fce8407635193459c561ce5ac401fc608b65f15c306065218866295e5135818406adc84e78ea59f163ed25a1e566a2482348414ccf88b962f0c521b21dedfcf65d210a6da3ef6fddbee3e67957df08539dd19b13dc3f7837f97ff507c6943ffb051fa6883e437218bbb9d81f4e4e37cefdd4673b7be29e458ece7038dcbb22ec0f98f758bd07e5582f30fb9d8e01b6485e2465c5c85f92f71c586c42f71d25ef1113a2def41aec95bc843728bad23291dfb9e4b9193c214133ecd269b762f21cef28281bc86cefb189a6012994e8793bab503479a349d0044da7f0953b72d20ca397463988a6bac9eb0a9331e65ca872317db9e308fa558322d46a1463808a657133939b54e8f3ed3c290e5fbbca0ad7f58b4294b7352b4419b27ba2269d707a24da8707ace94608aa961fed202d3f2e9ef4b965995a057ca8f59e4be345c995e2d11de7583b7b76fd805eb20a540ec087cb42d0a5a18b4d90e8e2eb7490bcb230d3084173ce5a53985810ac7e023ba5392edba452fa89d602b14569e206f32f0bc017c8de6524d7b306cfb87cffaa10bdc2ad63a45df6f1c2930c604fc06c59d410ed5256d17c97996f201b6ef6651f4f999bc60632fad1e9db0db11f0d6c3b204f31d61cc843f48520c5f28b1cf0273089cb55e18490004c81a741687567bf450ba780a8535ce26f3ee56da039e58f9a5bee20f40c439e842ddbdc5fd67bf54f0bbbb75a70dfd3ea58a5c95ff7bb87fcae14fd2f31b2cf5b424870a20317666cf57632c657318a71423c91f727a635de52151cf7772f12fe9d62dc2d2f88ef6ef98e6bc9a41080421f832967f189e07b0781924208f2ba3e194c7525b1f861f9b0fb0672a6cc1e7a6d252efff31574bcef1ae08bc6374da29090ab7607e3cf8c17e1aec9b68b4a259e2fbe128560805003a6db79291dba057dd22a80c3313cfb196e83c05da7937906847211bba8e54e741acb88332e7654829c2672a158a72f2e13f1475b87f8658020ce40a31410f3bb665ea4e8d4847d27a6da53ef8ef98dcdd30fa4269b72c438e5fc6587d7603e356792a856366fc0b20e6375c46cdf3d38a6c4980f0b30245e9075448f2864a61f40ea9ae240bee4818a60ec37e21a23f4b11930607ec9b085393f312e2be860ff1fab67f201e8714a00ece9e89d4de27784dd7169c720a21e178bb6ff5738344c35c032c89b2c1c728f033837675bb994d91df1e1b4574847ca57c165e1b4e459182c2f72f6c9983865bb4228993b8652d48555abc1b368e6e80239e7f55d0d86a376c815127337f347ecb7bfecea3f0d68bf4092df5b78577c8207d7542d667c9a09931b1d86a83e3ecc1ff62c30dee643b46c8500687bd2cad45a21839f63c38df0d946f2f9c1971c06cb172f7106907805125566894b44557823ae9b011bdbab19fee5e3c6e11ce737a27da640b6028e3f8232bafb3654714b945813d4916f4d755d3e25e03bc8d9f36d01f8de8e01cab9045e632e686687281512865faad6351c10a557cc6eabcfd02cc9ba4467e3391a05c0920d94be484aa04685b4dd5d59544844b2e2ca0ea1ad5b4d2a0c8fc11f1df93661f615f444622141cd9b2170467a5cbe912a3e75bb8fab0a20064b45f7756a6f034234187563b6bb2d577b29db808ad40a9502ec1bac79c593b649792ed2b940e89810b791233839e0ff1a5b7ab21c56f8e4048e5304cde8470e1eeb0308aefc213f8c4d0ec43612b8ca3d0949d51f57cd58df209b85049961637c3b18bc6d0471e3431908bdae7e298e4047fcc8b963bb0df8ba5b78697236655fcc4d1258c205bb676570e94f0ed0c4c6973df7ff7d40c452026ae9009d6aed236da9cf10c0da9e37ba9f67cf705b7d3c7134d9e65ffee261a69212d702e6ebfc32579134bb10ad63b5705bba50873f4a4bb519d253f1e849d3f8e74bf96f25ace1bac47aa0afccc85fddc879502c1396e30f5306168e85bd5d16b094fff41e338685b2b46df447c2d2baba22bcd3bd93f1a758283cd5c5b6948ac2e499baa56814a406c3daa5430a864133e14d7781fda4bf2bc5ab03ed965a54ed2481943fc52a0346e5043cc170a53bdfeea92ac95940badc4dcc5c5bd906cd9f4e46a4919eed01d2151c50072a02d51bb787fe933ca5cdbeff6c647db5a0f6f470b35a2e6fe258778c450c92c5bb303af1ecfefb80bffe894dba2287804adece5706f3bd8da6a67de8992dd404683ffe61715b70e696bc2e0be19c2c06af88569932fe70ec687e382009503882b5c68f8dc3ab3fda7619e69aa37daae006066adc7e763cc5f0498461df1eaf1da5fa14a4c2a5e42cff5fdfc3beef50e3828f7d62710e2811b2b583c122451ab92c07ab2f341eb5494b68451afc823b213e4c9462e97aa7cf2f10a75af5de44b3ba2bccc18803d675e2570be0ebce1292aa800a89839cf67582267998ea57f73e09b70228bf8f3cc9a2db9ad1f520c4734f6b6e901e259e4fce4cc64c0f321b5dd86e4c28a1709f0306583812cf0c07fc137d7515e95a95f32051771641a8f8b8e946335b992668dc5fd02a7a06d9cfc44f9696e702a7d014646fb7a7b3f1eb35e49339bea7443902a36f066690bc8c352c949c3c54f21f1730cbe2e904228cc7d66d067ae7be4cf1d928f00eb9cc1e6d4d334bdae4345c25735491b20a6b8694491038b1d9815ebb90e00851115b987fc4823d2761281ceaac42b15232a513cecd56c450b05c7ec0c746bcca005eeb4d05b164a3e3c639188657058ba535445fa4e84d9c759f73764d2e47ead9860f6c91c2e2bc741ad61b0b366d22c547b1a6ce230dc6e4754af9009de310c611084baacd0e66989fca25e9a9b2714895d19209c0488300dd6d2e21a79437523e1cfcd5dd421df05e95fae3953fe3222fa11ee3a38320909fe44b072dd525161a8488e6ce59819c65361899b058fd11e0038eba548aa1206e0d10ed77aca49c3facad615bc108871ae13dae2e45d8a31eb47ba8bafc3748f2dab22c9965d656d9cb36e56cf40821d9e23923e89d968a48ea17354e21085e3de4a8460c8828c2a5632a20049f4272da22e636ac4abf5975f5eafb4b344b4f59ea9070b3cdd01e4f42857306d8929f046b3654064554c5dbe928d2e4a63e6dcc0d7d3ccb267df36c8553163fa3981852f6f5804bc22c581a97ddde395f864f386f91ec8608bd82ba979d5bc2106c7d8be35bf06ccce21bf1bb31b5df0fc5c462f896f0d453b184a2ed86794ee4ae338b9f0a3a572310066a40210204a96643255b03b1a29fd0904224ed2140bdabed7456de88b95fdbd4a06da65875a11bff4a068474d2ed6f304efb23645b98e71ee16260fc0e7b570c3b0d6e39148d812da645055751fbbecc57d82f5fe6e5b7284692c9318cadab108495a01f0f07faba2dc9f64b713b51a1b2512c92951bd9af78f4808b152bb9e89a826c5e056c43e4e03bc684028d763c6c60d2598ffb35371b08384cc9615245616febe8d1f0e7011d260352dafe16b5b837a02376afdf411f207023f7d19a23c02b8717d0c1ad2ee1242d165244bb181631e4e0551f0837e7f4c860a89d2fc0fd5f41a0b715a0ad34830ba2e51014420718215ae3d9df6156b441ddcfec5030d14b192dbb6facb02558a63846b692ee5e91c171c25e927fd644aff5a3725355fd0d2381251c864612f19b56b73e5d08f306537f0d3b652c717dc001848b9da20f38c223ecde262d8274f077c2f61ce03fb9a9df1c00c0ece437e028b9b77836e3a782334ebb23ee9413d23c1c938f42c542db34e018ad5eeb510ca7729013f81d0ba305f88ce26e6f007bd07a5f21efc484b32a8ba76b98e40264e0bb6c3c314ca457d4e137c61bedeeb5aea062051c191eef31027c85aaf5fbf7a268b67412ae70bed65e1c918e6a9a03b8ebdcc2c157600465831605cd608d8be9f6249407480de003294a070dcb7a9f43a7e9230d46552025b0997d3f88eab21ad070dcc65998e540cce0719eff415f6264d8440bc66797579dd9bb7ef96dfa446160ee4f69f4046aa859bfbbe9c27a37fbcce37caa3f2b8e825885c5cff8effae9dbf4d12a2b1c19652da3181b821d1efa3c75f05491d7b82ce038e04505f0f8f33fa58c86d269420753b843ff6ee59c259cbea10dd0124b7594473e0edd4e86c79f8723c4558dc9d9382a02c781cfd4dff31d8e52569545d6440128d47cda336ec29c4a3d76796ba20d5df761c9443ad2a0d1b10dee0f1d7a1f9bdc67e1e20816461bd8558d7b80d88d7d367fab10cfbca25ce2855e27b380dd0f297a29e2142c1d0e97bd0d8c34cce68dad20c79dd5b1a9cc5b28c8ecc708bbd9def7e0f4a1c636229a106e8d66a6e1d2e210972f7c63c16d0348090b02f191f771ee755c916f7c77c8f0777e48dc78f552671e45eea24ae762fa3cff8cf9f06700f954a1912e03bb557039502f00b4bf432ae8ec78b78a2514acb5935805f0a29b12c1422d03cb20f9a26047ed12cb95391c14645c7bbc6aec54842eb6e6071403e316c1d308587e6c86121625c7544285df1406c2f29e631543e4073c41c68aaf74cc0d395c0fd21db9d2a3b1861c77be3227d333f10cf6a7bf62814549c7015eb5818bf04b8bd8903e7e5772428e50a4294c92eca612728c1ee64a0ddeec6b54b2d2724a0c7f6ee2003277aa7c841c6686347aa42a53c16cf1d416e2aa55506361469e3bb0903043727f8109fefa31beedd767e0192ee412606ebd1996fac58d10460ef70bc48a26ea89c72b0460865e04dcdef4ab763bdd7070b182464e98f19cc58b92958675e0d2fb6324ee26aebcc6847866465870976c249116f53c98d1f8ef19c12e3499ca08f7f873789932256e1096ad9ae94f75f266a9bafec1a3a41b4a0a949978289befd67f25683a3ed65935fed7b823ad88272abdd793f5a592d0e5caad0e8603223edf4e1c7284145d742a920107d8893835b10021df40025b8afccc79ce3f210b274145ea3c279ee3b2c418fbd16a3b3c47b330bf1b65cbbea95d2a3c47fd0aadb3bcb2a7a06556c70e0a6c1cc0e515e56ba0a9fdef1cd87fb492bc0c8010d8d144d497cc1c1bed103a110d3bb273be956a22266e98f88d90739637e0c4a1866a0024f5da04c6162ed1fc137e72de860405e81ef99d708a6b3edaf1281dba010647953e8a0b9d6b78e94514c2f2f94b07ce782e4a85c9604aef884684f77d9f5e1e8fef8fb017962e90d25096f41a222dd1a3b14c496debe099b437b05f1e0eee060eb78f1211d04aa51c611b81af462959dc36aef1d5de0481e9ed424d0409491da0e052f8cb7ce9591c47c22ad69523e602ced89bfa48d96539fd45581438dd115d07aa42bdc05e2d0fe03e5dda21ab37cde094755db6f5852f452c3e054d7c038823c3a68f10c09aff05d4ef6b145407a03247ca1562c64b42928a25f91c7406249105307962b5c49ec181f4da456bd61af165aa5d10e5944933a9768088302ed22ab9d1ec174d96c9c3d7a743560dc6acb167b160ab3cf82b17ef72613a8823c10f966145c71d72845006e0ca50542f4315203d97e93812cbecb4fd89e38889101abe8cd21619b256bda7ec84b81c2ae784e9bf151357c30e85074e2ccb003a2d5b99368e3ce4195fe470bfd10164fffd85830dae84244aa3b50e5bb2b307261a74b0d159708a250947fcb5ebeed1cf3d7f2deec37bc1e64480b8b79743885e9e38faea15aa061cf1a1ebcb907d2bf51f913051b67bab6d2007181ca2e09ffde0adfab4e56e6fa5b916326459ca963f5f793fc3d220dd3ce1cfda6a984ee8719792c9486d21421ddd260b7cdf3c5b031cc2090c595c18c84441b75263710a84fd42a05b0b56449ffa1bf7381427f69a75a1f2cea5ac1129464778421f98cc9240d1425edeea2d7d0d013dfaef6e9a6fd98868dcbadbdf450b9aedab8f916b66d57d3b41ad8de75b23ff938ed84d0eaa665787b800450603a8b5a6e59c4d8ccf3c0657dee1612a584f702c80861f67a57a67ffb7cab12a1520ec2d41b4b74eada2ac881a235fc2a5ea2b1f9422d945cf459a5a3cb8d119df56c5d02910010a0bfa13ab9e4bae9a75253bb7af30c22b96497908957746104982edd0137ae413910e39788cd839910e31427a4116e123ddc7c8166d10e3975a32dc8fb6cc29b2332aaaa889c3b7f14271b0fbcb1c04dd53023b717e97e494f2a9e5f08e096edf993347f2ff0f9152ce6c43a4d459554ec72761b38f7a7372dd2d80d2d8a9d73bb3c69f217bd6c61d949b6b35bffb9dc58b75068b0707dc6f37ee7207ac491bf057cea94ead82f7c167aec719463e2e18f978f5a892c60e91e951da2d9eaa7900a7b13501d1cf27b2d1c466ea967ab04a2f257d16ac43ea130f2c560fd54cd084370d7e88fcf204d66a20c7424ad5f2ba007ceb6826c21235adb51f85c4faefc0173151689aff883f9fc31ebd2fc5efeaf5db4267ec83ab80804cce1933a13013228f6e217e8add79db589b570001998ce85673a1998780be7ae8642db5330d4db58999ab142a6bc91200b09f6533bbc9b41bd3a230a5e1821809eb77613847347d7fe52446c639a37c5ca44d5cd8f18bcc24db7249dda7d488655f41cb688fb541e4599ed1878f93dbe858c9e017594eabedab8989e85c30b106e40cff524b1f43c6373c261e5587f7cc7370a8aa8245e19b173bb8be31c12d0a65adf5517c968484e347ab10e0eb51a0ac5e9a2e082d37f874daaf40fedd4833ca334cea0eb1868763eadd0eae8aa2c4a34b0ae6e871252291585d7e2ef11d3e0aef58447b2bf3117981db2292e1df34a159e631575f4942585d8c0df4bae8f90de0725e07dfbee3ce33b7ea5a81d93d64afc5e9276a846bda1ee44e176871aea1fc3ccdfdf9a0902f469330c3fda8bc3e1835464127c5f6c3ea597b8a9d4a86cee83a3f6741117893a3a9742e9b54b72f7f017979134640735672ad5cf68e05ab3935fcd7f4a68478531d53684857e80c2738a5e9a91cdc725252a30f04b03bedc69eae2da6887dfd38eab537ce2f01b1ed68a28ec61365df5de976970983e55c4a9bbaf0a06f371f691a8f0e76c7ba4f8ac82889cd262a7baf6a753598b15999466957799af6b86d6c6976662c3648623aa3ea4061baf5ae3e6b3022367405f8a773c5789f3b38e459bec2b8d4c78dfa11d9dbb642c824905e3f88817adc48c4468b18a67671550bf9adb1c4cfc55c282c12ed9b09e4af2083e81e91a4959eb746c5019b087690eb7bd6b05ee9cb3c928da52121245de083cc8fe66823ed3d89d9fa11d7923d7fa6ea4a182a79367bf2fe279370d7453c2a0d24663d5f8eba74afa994656e50909cba6a11bde747773d4541043fa0c7c159702d5d65c16b1fd0eb5b73fa717676958971b102610d779d4ecea07eadd23cb8f11e00ae78bfb323c565cdf14640931e30ee67c4368c1ab7eaef3a3865d8c303066d9882b3241554fffd70a3553023b39638bdd881fc55cca09ea55736c518d4dfd519bc9f018d290348835f06eea85984fba211a6a4b11134f4117d0e848f44afbc675e6af1cdda3dac026adb973a49d379380744767f1d4369001e5d68e39c962b4d4d67025c5eaca270d7879f0b4245f5029f28523e0dc628c7f03da68ba20fab0468a1d2c386206cf8b9724bfffffa11274e9d5daf0589c42ae187f46861241e43b83f524a051bd94c585cff5716ab637291e910c5a28735643a52b56b6cb9431bd104a360f164aa9f4434f54b444df935a7fe3af1e13b22e42d801b48167a3ca76c205901eb86927d51ca1212e0a648b074c9682fbf74a9d169d565071d66423aead2a339176875f98c5bd85abb4b9773ced3419ab11ec3e2cfb40b97f6e35b76a29bcdda27099a8df880b1150b0e361e82a2669379033fbff953bfd6963243308b788dd010a0b132b54c93e424d1dad11a3645a86fe3ff1beb5c197a23416bf173ad14ed848712f78782ea43e63ed89ee05e6b110497c6415bba43afda65d8ebcdb309af509ede742ec0d3330a068eaf2023192f02dc3f44b7d3e290156d3910481f112a3f9b6ca4017881633d34d6a13ca35d812f1700bfcccf2f2e21a6fc31d2e88c26a6d9b9bf44207197fca3c34fae97f1a662d0d7f0c4efd298c330ce2c77c248904178df04694c3a930b91a8367fab83f27f9028a87772417b4c6259c4a09534793b12cb1f71fd9c35b9c606e1e6971c997fe6515a7d37ac75c3847fc8f88d8f50ac6ce228283264c9273455f420f201ddb6e408f2d475f710e7cddfa84088430e0c83ba3ad4bbaf3db17a0b02c4f6f91dcdc9f67a4f40cb7e64ba646dc6de0297bcd01b8512301046fe2258d882618ff39c8ef3a73f5cb8ffb002322d528b7c48a563391c5a91fbfa9aeb881f953673cab3eb69bd66496fc194ac6963d5e40715c07813aeba28080aae1989076424e028984d07eb70ba2bcd1b3ac71a07176ec03f2097c4f11833af49e87ac39419ab4db4aa1bd74095901b3bf22f20f914bef8230769eb0c2be2870b0a0a4625dfc7623e246aa5a821586eb8d390a2c2a99f7147456b3729a468ef435e6a405a948e41889022f9ad7dd1a0f498183d825d3348d9150f9a0ee3eee5831909522cef64c26eba4db4cef52e2290b03b65a6f0d685732d482aea397e5a58121fbebfea84ae7551f932b9d95f0cd18f28b8714047a20870de5dafef6aefe265d31bc7119456aa1cf76bdfd6b2da47da0847554ceecc3030a6c33b5997528e4fe7c9036780cc754cdb3b52e5a86fa9135d61f0940ee36db9c9d665e0893ddcfe71d03d3f0b7d1109ef9ddeee35c7cd58f83fc1f882acc38a99aa90a8e163c779f1cb938eb8eff2a7075df85825d28d768f66eb87cf6937a86a1babb5ea021d269775edc4fd3a5f8fd928d2decf81de734bff9ce5e5ed2a7dace1b698e2038a9b182ab947a7f147032bcf6fbb4f378be307a235167d9d46054a944c4f550a13b689b2ea3c60aaad88559f682c336fd11c70764609624da5abea892c1954a07d3a4c5121f2c8e95131eda5726af65e83410f544dc550ad2b2d70226c56b1e31af684533ce63267a067b03a5fa9a883d524ede9f098c2c5fea28ced50b81349c8b1b775cb7baea4f2880f6a2597ec912b3b9a8956ee96713866a23183f33ec0373049d4afbb78ead371e14f13640f285c837dd68b3c59b370031ac462dacf62c8c697ce1b426ddd2e7189018bfd68f8fc2ca91817906249abc98e745a41638e10ca4b0336767465c56932b4f6e401c80f3bb90f3c87b6b6cf0be098c19591eb09ae2dd5a27080b4b8471d4f7d7414767d5f87de5285061c8603990f85cdce0f51b08c1ed412eddc06a5280d039bae723e730a5d2a305be9661224d3073d7fd081cee19a4299979cb8b0e0e628788fd865d1141438349929dff5f1012a2384907b32180e20e5c4d4d08dbf4b53c7db27f7823b92f66fae137c38093ef536db7caf7a693bfa85e70cd0eca1ed54583e92c7dfeabeb24cb86a8a9b15e755897bbf4f4ab2a5ea728c759d7bc981f32ea74c343583f9f033190758343afd1d42cb03a29728ebf210faa338463232298361d9fa35c7d13357de3de897f241376a9fd90efca06201298930f577b1c2197db9444e83b9ade5f202183de8a9bbdfa75e5b8777a682e2b3d4cb1f141aadc6562fdc1da148567a5615be6950b19cf8cefa69b5f53a3cf5b38e5c83b537a81211919366f4cac28d94412d1fcd81592d72196ff8e460adc6a2681a46f9e208b256c7fad159ac9943e74fd2b27a6a46c8ac6d316cb82079b8f9a4ca5ab4151da9539b332d423bb25d8e08eff1b37250f0f498adbd572619b78299a253b088f3e5a88b77050062c10ad1decae70ed0285e09f4308a01ad4aced9f61a53d17be70f792b6ad475d535487a5be8284ff5c39951687d0411ab96bfb8e073c875ea45d904673894b7602de62109dc33b7960471cf6da48e100613b515011222f8e598d01c21d2d7191dee29a7011627d378cdab2a94e7fdb4e71324f498cb0bbf3f0370949f0ed09a465a62f4f64f767d88543cca617adc8a4e874c586afbe9348b5951e0a8cf84bda5d5e920dedd6658b2f2fb3a2d776cd070e740f58dc94e8b196853ec21edd113d8b19d966ae0b49bb0a1a95a6f2067773a40dd3656f183f31681567326804ac809ad797c57c8d9bcef327aa297c9d72a308a7ff608a1dc35e6c2938df38d8d3db4b5fb16ea773a601b86c8eb425a87a0342ae111defa84a0a0fde144355eecfa7323b7e71cc353004eae3f48ff01bd90419c04c99e2217a1803d1933a6a71114d451aea169a08b8fd2c5f462a2a8512e5513b28f348381fa1f0fa731e6227221d24853d7d0431af2550f228902cac41237f5195bf50ec6a063d6bc5c8476b302a042d74b812d4b316d95f582dba8697b009e4f12ec231f35948856d609cd21c2923e7c6c41b8eccda152f5936119cc956669746a714fbe1d0261b73cb6ec27470d19e62993f3cb90479430d41b6c48da15a4976981ea4c6ac9a4ee990e74457f837f182140d127be12bab170f4e48204df8c36798acadb4b0dbc841a3f1c0923ab831479da02b9ba70bc6e83b45a785e6e4352858fb54c462d5c2f5a27a11a466fed93822a2af09bb7e0246b6fff73179354f231b039a224853d22cc520189364c1e2b67b149149fb94d28b66b5e6e04513271532bc9be8ce92a6446b89dfcc71c9ed328d188af2a531e18efd9aa28e8c92001639d53792163c96a0a125ca822a7d563100a302cdbf6551b1070aff09b91dcadf57c02f1f50ce88517412d4e06c4b4470d1cda4ad539ca7c0cefb7fb178df9056464285b2cf75bbf3894068fca9d17e2cbbb47941efad78832a31fa6476afddbcfd5dce1587d60b1297e49068304baebbc60fe8a8dc6f33af8c6ee5e764b3a13e3ce71715c1a23452e82346ce0219263fd1f43103f8b34cfe29debc2bdcd31330cdafdebb852a31e4c0d0019e6e05e36ef255ca65eeef9f0878090019727db1e9570304c109870d9b18e92c79ac16d1defb87edd95789167ae10522d2bdce3accb115ded55c96a0be802ead0492a7bf8346a4fa116e56725a1b9d216f8ed3db0e6fac942894b6cf13bbc493e157def8770f197e3eda5de62afbaac6b9c605fa188aed34240f6c3b9fee357c384aa0d00ac4ed4ba3865f608eaf283d5ddfe500e28f04085f748d669a64abd8f0ba0b2295f17d7f7e0d2ad8a894e45322b636f283d8c1f0c7f21be6905e5124ca5b5b91abaff7a485e533eef37c47eaddbbfad1c1de7a2fdfcb3bd47d538f6cd9707999c9a129be54a9ca062453f403ce1893671408ac4747ed5767f7c2ad3c476482732d927e65a8f959d2a3a8f7dd26111ac212af1f7e0d7b3165504ef6320310e6f822d10d72252d5676e9f75a439c8017c4a78b394d85f1ddbf15fcf8cc7433162806c2ee48812123f090a8d1d831db465c69e1a4cfd15417c65df6668004f0e892d685406d9187be9ccad45ab218c5c5320f0153a36a25e463d55ce82055e2ca70208d40643a0475621c1215388b4f2d29d88a93d7a6a1128e0c98223a8161e1f4a936ce9fdf13131694d33fb0b86f7ecd224e0ab9c7fb8b450102bb0d8dec9f9317c4afd1607cb4fa4162e72ccd868303a6a6778548d2217a4a43dca935a45d310d05b863cdd475a148821b568a76bf2861b7d9cf92b6a6f1d23ac125993dccbbe119c7fc821ad84cbf9bd57386cf7ae49ff97e6a741bcb5564409f8a89540d5451068e3e2c93997dab8c150b78e80b3e5f48ba4810a02c6d661342a70b1e53dd56caba6523b515e2c3193a9f72d2843d2cc26226385a6cfdd65f0768bbf0dd1bfc7db16b56a83761979bb852dd6a1b14cde6d51426b682e23ef5b30ab84991741702b80d579dadb648d53d72b328f4d228ed19649da107be1ad17d3fdf181af095f7d41f07005ed01423efe0379c088d493824680e09f1364f66dd08e89706f6cf1460dae0bd863025de417a48872d16786e8014f0516c6c9ef0093e101e60d1aee21bb16de53ba5ebfcfca9e450df4cf1657ed50d9262a2e875e0e8138ae0b6a969febfe487810d6a758ede6b4ddf262828f57908e230795e7055157b34d408ef1af9b1881450005b7f12799bc487cc0408201e408e57516425d3fe057a2d650190e9e964e7b03d31c8c706904ed297a79c08c46659165305b9e1f3effc9fa171fb0a7313652630c06cb9a775166c067ccc79370b627452bd4f669b90786eee4ebc0c1072b87acb20a14bab70d2a6dfa3ddcfa2e9dc9f016c746133b06e8f7a04c64d807961490e51f4de8a1aa228a31196f27f4ad3392495fc18538d00021c412749fd724d61a6f1c63332ea8651ce8e085bed6850dedff8643bea03864d376565856b6b815ceffee3e1e90ffaa81aad988922e8b5b5fcef4ec39b4148592126cae194b66c27edd421946a614ac9482a08eddf2399ac610430c902192002741cc33f5491770fef460aa971815d5e4ea706213aec1591ca89e9f694729ed600d300fccba63dee126b69543ae57f9962e3645502cb6808ebf36d495001a4c7824b8db67115aa3647c4ffa2a57753f443a0d604437bca7def36849c85b5a1b2deac569dd83a3b0e2c66e32fcfd0453f19ddfef417b1a298fe82293dbf8f1923e6007e9a02f113cd11cc7a2218493dbe509c8c7a715a16f012822bb82429f3061b98d57a07e3268f413ea21e5d99b4fe812fed56d89b041d92e420efec1bbef51cdc1321a1de1b4ce29aa1ed64822d52320e0a4318079ddf74230f929db67e39b29287131ef256dc4d600ddf9e6bb5907ed4bac1478374bef38907970b9a8a57f85daa3a7c9e6d330613405a5bf87608ef744c8a7c0ca83035607c6fd1c50df41d35c1d3c40ab01bb14c3012065e9feb86b8c74873fecaffbd7092f85f5e5c57e1cde2b72a60dede0227da67f62ab9d0045b3a3d01281e6f09856066a59a3353d371ce52a45a1a4890244cde40096d26f933d38004c5df5b7e25fa11b8dddc4a30187b18ea2a6ca84d34cf61ff5ec60668111581534c633f08b8f37fa16faf56428eb6259556fb582b456c5be7506ce5a3d19ec43f6cc5697e419304da8bdfa372daf8fcc0089f421d5ffc0681c1fc37718de9081ff5f3204b34bc3e84aad09f7fa759bd9c0d29037c99560b9a307710a8a29b251a53e62a417157c9c71c0c1d468cca51d03b9f51c5763bcb05e2e52f19372814a33f43da085d85cd7ea4abde2357e5d5a614e32484a1d497353bd2b8dfcb3653a9633d87648660bd10409d33eb27b6e51adb2b8d459805dc74daff73431062f4b056efcde442bfc159ba713e5e6ec848112bd8453d1f57b6422e7844dfb9eb9ee4032317795eb0e9251a13dabc55c80d370c48bea8088d31f68136bc7e1ddbc21f5e2dccea4898267ecae494da99762c3deb384824294b4f8d9ba7bdf17f5d619bd3add91879e60aececdd2da236491cd84e55ddea3b01d3ecce9e06ca793c3383cf163bd533fb890f8abed9a51d3bad1cc9699fd23487bb84caf4c888510e5f1232d790568839a976cce9695d29e86480e60e9f98ca2e7eac26855a179978fbb5cf9d35c09d89f5a6597c1535b801f1dd83667784a1a5b271743419a3ea8fe7a89bb6e1d8693ca1750d3474c77f1756d3baa819560204783b10c951b0a0d2ad6db23bb5809897879bd2e1798d806971d3d1c61a94effb7736ad7bf1fe1168d32a08216d1be6d9e184b51079b1a5ac70c68dc42104869bdc0e416137da1e88eb8055893955aab9fa0d364b855ab6352d6a2b56a54369c303100ebd30c5e0a028abeed97dc501498b34e5b24ce0249995a32c9dacda26a2d225d06bf62a10cf67763ce3767213e59fe39ddc168b22bb046ccf2de7aa668868aa81fd484c546097e12a4c4c70907dbec9599b08e1d9062a53898883aeedd7a98b8f248f055f9ffe45a43de8d0d236c89c19a054211121c260e47b32bbf085a5fe7afc5fef6146f8e71d22f376993604824baf867ddbe2c1dd491fa5f41b49736227031f9bad0caf6f15c3613a0e938eb4a15b9469ecc02c549c2b7908a95959af747a6d725bfbc605788184358c4243cd16245205586359970e9175e5179c3d077d52a7b8581729e0f7d17b7cf193591ae27ef3bbb1517fde9df1ce5543431123a7a45fdf2273b6ab44ce076ffd4ced7f8661a886d45f7afa32be6401b1e9c0e68d8d5174191126cc89d415fb7ccb6a8764e7ad21b52e09511afdbfd064750a86de6f16899dbe80a630d2c2b21749204726c68dd1d1a264a8ff9887f42a0cbf6e798b3f831b2520bafab3e514452fc99a12883addf3f06a30f574a8204016da12fd00fb524ec79ab247f9ddd857fd41bb62a52e15a85a552303f237c065af18d9299de9d34f8ccef424e667a86cb5d26e54068ce60057935041d37ef90279d8e77db9df238923d465ec9a451d4ad49c94d34b5d1af52132cd2879c86b6e0e09d88fdd4c79b983a7e3f1dc0c1bde04551a3a0431aa401ef23dc6923902f87880dad4aa23dc399b735c5a2863f92d8e69d9b4a883b27931221111cb0f8e28f1c9cc08baa1042d406010aa894399a829327f94bb9d98d1a420ea7f812453cb1086a0dde1e60349ecfea9b8ce464125a4c5a2f2b3f5fae54ea2def977477ae7243a5a3c1afb73608bb4b64607d14ff50a059048660d53ec71d55f072aba0b830999048b057f8954a76e46eb4a1fb01629185eca4bd7ea4c4da27cda8a786c6a7745d28c732164981e8fc8b49d9fc41d244ae594ddc6e828bddbd6ec452ebfa72ea25def11e02dd39ab30614a72954b298e1143bf409f9b3952253b51d242a786fbe655fafee859a783c1491f22eb4798579129390d7aa5e51b861c467c19a62bb1086b44ed4798618aceace778bac4921b7d81041bb386aebd5c8bdf2bf163aaf4566278f0f17a6b276c0ccf355ff130e0ffd58da2adada8b65c36bf529d2cdaca694b798dbddb2b42e8fd6f8eb4179747b55db8e841e78f3fa309beb4cf5bc21d23d35ff2c3ce498b46854c40fe60ed3e2526e7642659e33f8db99c91b027f46d76bd14a327efabd85ca9b8c230f0f381000ae12c7bd02b6a0437d85cc7285ef0b49f17a4cf0eb735ac8f32b846944e3c91f947528d6e8651be710c6743587c25ef141092798b32cb5425730cb944c2cbcfb98bca1b1133486e205cc5d53a20d27fc59ddf9de35ceb405a8485508c624650e059e3bf40bae7e85e570ef5da31bdac6cd3a9fa64f117fbce10abd343ef777f58b2c7cadfc673f5ca79d597e2afe3d222b2ccf31e0b34fe67e6ddf8f517ef22870fed5a915874a342d498d548cbee214c7a037d90c3de8300e49c62b3500b049354f0f3c3bfe561dbb68fb0d8d548495a28b41fae62cc153aa194046dbec7806e1156dd7893aae5aef4a5c9be3baa29da92fffea35869d5c7ed8665a1748bc2721ed9860e65e32d63df71482eab890b4ea8e5950011395829faf2449e66d66c2bf166501544aa8659c317d6b9ab137e202ba0f5088554a4f36bd576282eab261ece086ca3221459138e492e42412fdd72417af58a65ba479bc5acc5bd1a929254ed9164b43b80259037c4e2f9182e3aadc959428f6e2ca0081da2f4054b5d44ba784c06190e81170367788166092957c4e647ff4f981638e5ae11410498b11d2ac0e0c2206112b81dfebe057f79e0b80d9544e6285492b3640d80d1b533a71aa0e11c6c2922cf59005bfcb0c0e667b255d9aa31a3d8c8f9ef612d13213c9616e0c67f5f7d25aa13efe8f97a652c86129978d1e3ce6d7b0b9ea2087098d1dc8dc0e02f3a7c5cfff8578eaea64f754e61df41bf774e44721580858a74b45cdc6c262d31078396401efd00d2d66608e59d245db467c8fcfc8761ebe238cf998f312319e2ff9f3d575c7e22100ecff7304a1ab5979232d06d015d5906b1e82dad6fcc7dcbe66e4d0fce011d81a766b700605e014fc38140f5d4060f9458fe618503509006ff174073a43c0e853cfd24f99c2b8a5e5a7a8dd26e481088790e135235d029b952c62c040b23b7ff709d4112bc52c94f9652a38b51515852bb24b386a8277b05915a90bf1e0370e873c4a2a02d218e8e0b94b9879ef06905a3a34d0c6dc4ccd549446ba11acf9c1008087ddcee77926d8c048a146fd12ed2b770b494d4f02d384928caca3569725ba305965adde2b4b011f74ffd4248c1ad411ff53ade707ea707b70861a94717d13e7863e6533ff67deb462c91340f6390f29c1db16edd386183064cd9ee5100252252a7d300dca921181b2b7cb059efee3a128e21706847c496155cc2aa7e16ef3d621825dd1e6e37bba2518d846dc18d01f8996565f622519bf2074c0c388e08628e81e8b33f81a4a17d5a3cea374dda3b4707f15c4115734f38adac72d9af245d8af7081faeda8bb0b3086b8487243941b9741c00366bf85203b027b1063ac55946642c9209f1149602ef2b815d044c90050e6a85eb11561058aa4c3a74016796193ab7fe616ff8f5f757ff0ccbf5c765a54221db0636feade2fa9212b4a564dd526e514b4248da9bc84725d8b208e308b55fc72d0922c24314828611fb1c61d45627c05ae4de6e018408d93b051f11bb10a611b5b446b17fc2b93e05692c56e27991bc5e51362cf1d0eb15e585a20dda8269d5fb57eb31eae15311d350b4446a39fc1998290d437972df862576f2c47f161564a5bb9d3cf16ecadaec19975188829a74fbcff2fdb03e14edeb509450ac892ca7ac55b2335a952d12c509023d0048aabb39850ca1250408429ce8aef653362cf1749c8ff6f5bbf19f65212904d941772be956102dddfd4a633a5d2d46c1276d5842d1ac28665c698e284eda14f83310539a6e05290ae202d206101a0079019030802400c80dddbd23e430743c3c3c393c3d4ec3278a13fcfca9689e54f47ee831ebaed3e1dcf8677a695e74e45f9b5ca7c3e1fc1bbeeb74383d374dae3324e430741e9437621af5656b9f3e17c5094ec7b59f721b9638d79fe59268284cba2f24f4a5943f029f09beff758a4338592c6f84c06782bd2121f09978d956efda9837858f327e190d314871a537573d994b72862752b6f67a9f67c650787c42283e3f4e24a87452f561f539c050d3ebf57a8d6055502924880f52983482523d560475aba74cea564f4e164b9cafd7c26e3cd38ca3ba4a5443b77878449ce9f8b45a5a295197a9877f6db5331a9eb39c6ef1a4b0e3811db1a13cf13c777c7428e8a874974bebe1b59e239265e89366ea39f7a9beca2a9db3aba1f3a10386ad987ede4fcdfcf92cd3a9996be124ea964e4c4749ce1072b43494279e9313d26ac9f26136e778ce9df86d9803e628e5e4e4fa38ba9533030d3f5a30979316b34868480287f5a15a340069284f727eb8f1430a284f3c630b7a8e93271dc625266dc61e7ec65f4af9fb4d7ec4ece816ce173851e0283594277e9444fbeab7b4323a5aeb78d2a6877396a45b373118d2ad1b296ea27cb8d1509e78d51c3d479c6506a7e764b1c4e1e79785a5dfcff3afcc31edbae5e38aeece47141f26743511cfd90a6660c00c524e9e74a34fbfd6419ae76873392b0bdd9a210619c668284f6495d626e94e9e7438df084a85f5a13e97558acbfa39666feabc4f73fc9fa4bb57e8960c52c890bba13cc999e13973274fc879b3d739d2298250323cf9744b069f31a020062a31dc74f7e13c7ef29c30c9736ebebf659ed3659552cd99d732568633191d897ca2388d3e9fb5f41af4e8160c1d8041cc79519ca4e784d49d3ce94471823ea930adf3061860d87a3ca087548f189b0a74f7926ed97cdd0de58987d4739c3ce9c20fdf5a99d744d884d94b3aa6f5a9eee73aeb7d2a2b737196752e79173e49da1bd64418c5d892feb38c4b7b5f566788934bfb23cd36fbb5f492e1fc3bbd52bfa965548769a573fcb026e2d252aabfa4d7608ec12992f7e9e712e68fbd76bf94e2e167f0c9976ed9d4e06a7477cc7a6dd21cd31c3c69e15fdad32dbfa2bbbbfa0ff3a79a33c7515ee5dedddd8fc54a183849ec31eb53e0147d6272ceee3b7957a9e3d8db7d476ffc4b8ca9c593e67f77baf5c2190fd6bf1ffbbbd4ad17b2747787a7087ee836cfc03a75fe1a75b78f6ebdf075773f8b348683ba175ab93e16ea960b5d74be4f55e9f7f174cb852c1d9e1adddaec78ce94bae542d0b5311aa3b97e48bdbad02d175add9dacd26b634ddd8dd4ad16b6e8c2cf7fc92faa05265a40ea70c29f65fc97ac7a7f58a69dacfb9bfa5454d642eb2b1defe70ffd61d847148f2dbabb3b5c4ec773e6b570fe15a767ea0f568a9d3aa65ea45b3caa6a6963feb09967582d89ff5e0bfa1375b753b7787857ad0d31a5790d4714e78d579af56cebfd4a63a18bee6e0ab422be8f4b17459af1c5e5041fe6e1675beb04a7402bd6d27f16c5025597ff47cfb65e9b27cdb4867492e18d12aab2454343a2388d9e5eaf21198d556b846b508ea74709098da0141da9d2d010ce8dd0084a0d8da09450931194f2f929878886946816cba5f0990855d9a28c3f5441062a64e970a66eb38775d29f659ba730d10e2bbabba95b3b7468756b07530a6e74f797e20f31acc45e13f39dfe349b1dbfacce19b86424e5b53aef94fd755c7aedbeac5ad16dfd4b4b29ff944200ba5b48b752b84147b75228eaeea76ea1a04677385f4b9225f6cf75baf061f482f8439755fa30c73466ef2ce709d25a8ea305fd61778a25e853146dfee9160a3014604ef8c1094e9c4034d3ad13804c300348b74cd8d2dd5da61eb3fee3478b7f10a996da5c13f1a4d57e645b7f387e1b3aa633b18c61fc20586227f21792a826a6eec339566b6bf83f35f3387d2c475a35c79f658c5fa4b35ced6772fc9ca7e34cbdd2877d5573b44c5d4d1427cd779235b15abfd66be2e717c9d1821e82fec9f0f8f9fd59fe19cd6535acd4c3ffb03ed57b25ea3cc773c459a73f92eebea15b259c20a65b2584a004b2841d78cec05b924b465239b53a3d463d47e7b31dffea7453985ecff3615e9ba328529a576b7f963da7ce59a6d78e1e4bf2cefbfcdad84c56dafa6eb393a5e7e4e816094274f7ec16093732748b84956b4b7f588a60a598d218cdb6faf4f04edab5b19895d1bf4f65b1ff2cdf8fe13a6f5477c374cbd5d3dd50dd6ac9a0bbbd5bad0d74f754b75a50ad5777c3baa5630c1d3f7437d82d1d2f74b70dddcaa1821c5474b754b77238757754b770b871d42d1c58745e9be40ca4339a759b2b7de974feb0da5cf2ee3f56b3d9c3c7a5d7c0faf796a0ff2c8f9fa9677c69ac3ecc6d769dcee7274c02893eafd363d689fcf5d5eea4208d7d7dd8e73add4d8f94cdee61fd0f07a7eb6e9c3c27d7bfd3e9c647eaf387794d142748c95a7651a44dbc9be5aacfd51ef9ebcbf8c3cf75ba2523a912fcaaf7b04edabc61cc9213fcaf8ea7d79f9a5914674ef8b52c67d9c39052d94b62eaf83fe6d7c63cd3918ade7de367ea79ffb12f66fdda0c3e957ff8a3fee11c1d53ff90e26a3dd71f6b93f4ff245da57366b3fb686fcc717e8ca7ff68b1e76bc9173faabb99ba85e34877abd02d1c3e5a387c74b74eb76adae86e26ddaa91a2bb7b1136310ddde66b3d4756a958ced887feb3287f7dde0856790fabcdf1739d2e5bfbe40ff38ccb257f7d95cae8b59feb7438a563ea997c109c5e275869c61ffaccf5c1bf9eb11dfd67d963d46799d23cfcd082f5f39ce59fe51baf96e2fb54be6424e51f9bd8a7e787e26638a5eef6ba550343e729a354f34b7152f9f7d5327d3ebd5aff706ec87969b57cbdaaa5996cfa3c7c519ce0068ba17d755952a53ec9c9293254c471aa427a9363aff47ae5998c3edda61c7b2577f7fa6407af0aa9f7b2f0bd2970deec40e0c2032e14b8526428f6a20b1045843cd7d2824e83d717bd3c93d1eb85a25d55a144b398bc889171e8458a38ce4d7db1032f5ee2f1a117ba592828a15b3155f487b67a2d93e377f78f6ec55475fbeb430bfa8bb03903e7df5a7af8f7737d7f89b3decf951211801ed789898fbd48119f752f0b7f4a773be9164c168d6ff7a1ad3bff37c37670b534832f8a16dcf989853e4133d603e4f33383c4603d41c02140427a80fc00f9c07ac009c680262c28c6c33377c08ccbbb8343549211911391910b795548fd15be4867ec3ec6b3fbf512e7782d53f8599c483fd25ccbeb91b06bef45f26219daa59785f416855f694da2b5304f56e92d432fa493f4fe635ed824cf64f4695eae4fe25abedcee8e597b9d70381fe9f5fa6b412fdb704e8996f448583824043e93a1a052087c263e3fa50b026cac26cd3b9d70a57306ce3b498f84e1703e92cf4f592dcd145bd2fb7c2d39522f577d9de18914cecff88bbc8c1f36c31369dee934c373e685f3674c53a0158beec3c6b27a53a015615e9e2c21669e475fa7d2e799a18e7ebc34562d889f96d4ea5cb2362aa4e2a4aa49e18bf3a9f64ac08ff24f24ac26e1af530987b5b4b05c36e579bfbe8c8a3ef8374fb19697aca517fe7d2a2a7ee8653c454f9cd9e65ac61f8ad6abd4c3237e7a737def7e6d8a7fbdf0c33b697f6b19d6af54f6b02c96f89f66609e24a6474c2f9b18ffd283d5d630e9eb540aa9f7758a1fda2559a579de279b408a6751779bd0dd1de8ee26dde2bcf1ed429196309eeea7f3d1b1b14a755e9c20def9f109027f082c1c32c44708087e8fce68c10e56ce3295d56ec654f677c7caee0477c4095a71e7e27227fc2aa3184fcfb6fa2c4fa7fbe17ca44ab375aa2e9981798e982e656b9f5e36afcd5043e19324bd19bf463c6942dd8da33b2668334aa15b9bd7dd27746bd3d2dd998a24f62fcfafbbb76e6d45dd8d42b7b69cee26a15b1b55d58795ba38a9ecf8b708be5d68c71df0a9701cd31baf2fba903fcc43b1b433ec38dd2dd3cd85ddbda35b1b0bdf2e8f31aeafbdf7bd4e0fbfde32fff56a699eb9fef8223952cf74deecb24a739ee3b5319fdc4b77f3e8565775b7ab5b9d4477d3ac18bb4f65bd962938c909e2db814fe5d1e6dd012fde01ad28ce9dda9d1746c5ee7659b1ba9b3372450515a8400d32296c5105ce05bca0e214830d44c5be0092831a11c98dcd0d82c02881d6b5c1041a4ca21052c804f5c079a2078c125418a25b91f828062351b000400131298871c11834bd85152b44bd8a9101169ade1183c386739a6d2bc2518507ce637a5829aaa97119981996cfe0b0e1aeb0bcc6379b2a32be713238bc072b364118295af98ccd152c5bd1ca833052c462390e8f5af9b615d1d8e0c0e133348e652b9ab952734317c95ca9711cbed95499f14da647152ba2667cf32a34ce15cdd0d8e06021098cbf4cc1f498a2c6f94b8f296a2b371283c3a68b686caed0788ccd151c1e8417cdf886a38795d5952a3cac7ca68795d5152c5e84c3573872c06153858795d3f4b0b272ee061c512be7b8191b2c5bd18ccbd860d98a649cc6e64a10468a661ccb5644e3343537b01c87e7b0ba22d3a487221c3daa7895991a9b2a34453336415821c5088f0db4a4f41b442f3da6a6a8713e458de52c5fb17003cb398f5a39711e63e3c4f2c6e1e42bee0503459b0d162cac2b58583d8288590151a1c203e72be761739653b182ca152c5ee4e2417891155132eee23cc4f88c770f2a4178914b0f2a3cbcf84c0f2c5eb472991e3cc4f47071981e3cbc6ceed263e5ed3cbc6c9586c71631cd830b1e1d68ad705c9c07095a2c77a12917671d5929a16916c739c7ead75601168bc5f26ddbb6a92d4a8c6f3da4d05089b2b90d3dbc388b0a15e72e79e52e7e64e5b272a162430f4431de63ca65caa587142a44323da6629c731767f55845e17c83b1998a61b156ab156bc562b158ab55fb8643890ece9b1a40d32bce8686c6a55d7adcccf2971e549cbfb80bcb46ca0ea229ae4713cdf490420569238a91b22292e91185e55b0f295488605e7ce52b9ba9f656a283888bd9382d6878788086660b620be20a162ba418d93c08560f2ba418215a796fd14407912c449268972692858812449870f9808dcb16ab2cadb37917dccde62ccca0b5f90a2e60ac805cb66c588cd136ed2bcec6250bae3dcb4a082ee8a2055910b9f8102c323e44c6dbb96e98971887e9215886bcc86c1ccc8ac5c2a2755828a3c539162e58ac805cb07038eded1d0334c60a68f3e6baddc5379724b81b9722b830a18c13ca78a2070c8c14ad8d68f55246bf24b125892438212e2b226c65b0ca681c10614b194f74d14347190c68456975d105e73a6630d345129c239d80861adca6650b0556442e3da250178fe9118524e266449caf10654a19445c8f282b24ee66731e0a6845d99ce06eaab89b242bee66732a4a339bb7cc8b96558c6fbe51605b697191a122251585068c1930886468bc20da1cc666052425a74d4a017dc3b98b102d64d3c2dd60a971ce8589178e63712c560f169410713d58e0bc0722e768ba00434b8bf318b81b1a3056389bd3d86c2eb502a229638543e3456f0aa02901d1e6db1944db16345db4cec6c51003d1c6e389d68c1aadb3398f245a9b4779d982686bee460d196e86091dcd7571e4c505c625c645c6a5cac54547100f05040955c9b4609a8d9766a35faa78d9b2e5654b172f55341a31dd458c90cb4bcccb964dcbf6c2da02f3b2650b11c769e15a672ca015c59585e31c05265e62666ab6319aeb112566d58ae9f1d223ca0b515e7065198388f323abcd9585a68c1e2830d13a1c0f0270393131313131354374d1822aba6841132d28e2841bd70e2bdc4d538c8c095b092ee7fc08091ea5b7206ab55e8eac5aadf626c9f4290352b4d2a44ca03240764375f7d82d0c08a1bb65646466b0131f6dc6a5918c8ccc0c0610d07d345bbfafc76970fce9d07c0a9c30c79836c19e1f0abf08d6309351246ba3c20f8942b129fc9fb824dde2f09fa4420319e84b234b0feb7bf6515eb2372a909266782279e3e7a4dacd158905bc689f9f10dfeebe3782559e0536264c846fb773adce0856ed8c60d54e50104c08181bb203030261130603d203ce9f9e190ec1afd7b5155052019ef65758e994e7f8ebb5c4c847f59b88020bbafbe5794c4d49504f489878486a207a6a8a82f2bc1709ba1b4ab720800309cc98b6b4a0f1f7f99da40e3982553b78076f29c016da16215b76babf8fcabfafc93b9c1f3f7c06523f028b34b41c80069e3782559e53b5f46577ce8c26040c2076c7baa71541687cbbfb99ee7c29e5430f7caafcb8889087746419b18a9a25b4a39b15d4ac9d66d95851035211578146ffb55596e4795a34a78506262045d31a3045f7d88aa057e59f953227ad159bc8cc747f67f822e89e671299992e533c61d3ffcec0fa3117e7a4ea4d8719b6ee6d691b92a3846e1d74b3c001355df5dd1d051195d2197b9a691592d595212727e8d1be6e417a4bc28034ba1b8a7b917c5e9f48407723408b13ddddc4d7dd07e86e03743713ddbd44772bd1dd05e8161555b4a8d042809a01743712dd9da5bb8fe86e01748b8a272d2a9c8ce8ee2258dd44743705ba5b4ab7a6e0628a1ba6c802df6ee75a708a25ba3b866e4d81bbbbc37ffd490b62af147c2acf9b9a4f7bbdc22731f84cc29f9adf84a7a7ebd1019f4a14e758974091239da2511549941f0ae9a1702992b7840a6b19fba75a5924ab34142dd2e72a244f617d10ffd17cd1485629b6a21111395ea4a7fa649311ed99c8f1226570861f3e51131faa1194da4a90428d0ed7272d9df7480239ba5b031668057477956e49d1d38d6f977f077be17fa533708e3a3bf8a968a623bda42833536db54d5e89a09890b6d6577b84896c584285b55712d2b084eaee59b7a2b04177cbc8c8cc7432323233e1679c1d7cedbdaf83773068439d4ae7b86369d761d0863b3b2f4ce63cbb81e2c47f1625232333134516dd8d67eda7bcfcb896362c711445babb3967b94e7a27957fb54651a2388dbaf0a53811afdda74262c312aafb072d5a40e1c4f83969b4b9ce9993e74141797c7c7e68f3294caafab04e9ac5727cbdf0ed6054b4618977763e8cfdc48082767ea610203c3c3306fbfff1010224a6f379de8c6125dec1b9b43b99ca74b494dd6eab03c0091a0cfd38ab900d4ba8a7a7270b5ad036391a41297204a5a0a08c483ac39e0325746d12519c46e2347abd665e2f0b7ad8ab1f8674e651727a247ebd62e2e3211b96d8c76766a8276906878f3dcfc3a5588e2538f47a0d815484954e244964a3c25adaa2d72b06c7d3e72d8984e62de7108ee7f8fc9437deb5516698c434752d2d7fb5e1fe084a651a25d749bbb6d2d1fb3c2fe8553afe38eb8fb37aa38dd5a4b0d2a9fc5320cdf83f86abe688e44558698fc24a953c59cb9e5735c7aa395e4befe79a94b11dbd3cbdaa3986d41bc1aa244f456543d5d29a24043e131f2aa111947abd7ea8c297328695ce999147001749720ea08c6e004c74e10f20684ec773002d2224c6804d1d24aa1a679c298cceec259108eaee30b54c59cec88245775ba62c06c882d4b5bc6e33a561ea4e302bc2c823d4e8aee5c57646fd082cba0b7fca87163c628a94552ad219cd8996ccf09c791f0a8005023842004e0430c4881c18e14538af8c2c7342a24cd26c44018c30d27d7646fb269933966138499abf9cef3fe67dfe1f230aab8555cdb108308ab0d29d08c381dd7858c3a79a339c791355440a446c414415226ebabbef739b3f6c54e9d2e7e18f23f530acb6a8061a7d5db5fe54f345effe6808371ae63550d6ad217618828710637477fe345baf2c7ccf93a8fb5bca66323a93555a6b79318d257538f8c663569c1f5221b47487afcd53142de838374e452fac16e685d413e2060b1bdd58c6c0824537a658b47c89ef3b61a96a2c4e5dc592aa26799d780916cf5f3b4a722cdd798634ac1374dac7dcdaa8009cd1dd0100220048dd1d762b08336ea6efb539e6393305cbf0ef9741f410c477f7772b88eefe3cd2def0fe2cdf3246da1be6d80440140070ea50b433fc7796417b49276dc6ee318baf3ce9d6951fae0475775ed51c3d7204ab3205a94856bd87a395f95359d8f4eec712cfb1562710460021f4830d7e88e287f107a5ee9a7caed3794d1c4129728edff7fac259f4b9ce92911436ea960f32a0ddf2a1f42186eeeebeda043ff4bc9a383547eff1987522efaa95d94bfafd8c67ec3ecd8a4ed4fd0c9c229d5493c456b0e8c63e72a0ac2cb11264a54777c7bad54316fd79ddccf3e8cb2f6ff84a711ef9cb753afa242eb1cd1e8a36c7acf730eb41051e76d0dd4658fc9a4bbb5369e7b46424857f2cf13f0dcdf0449ae1390b1f97d7defb797ab93ee661d6dd9d38cb90523d0f47a6ac5739a3ca960e46b14fa7a52724af4455aababb4b965e89aa7c5d95235dc5715925a6bba99441658beeeec25944a5aafbb3139527b3e8f5029feaf50a33f6486c73387fb6c3cd148a1d4f71c3698a1853ac7447c7d9146f4a4c7777352a3ebf6831cc7a6dd69dac16d4e1d26df6199eb35a4c775745c5b5310be555566aa8f218cd78284cc238333c913ce7867644e5032a045039a1cae98ec8e98889e8a9861aecfb726c5e30fc9a15c90f3de7c13a33aef4c35847e7f3efc3168bf33e957559a5e1e3d2f3c224ea95587079249fd1dd7df9a5143c7396eeeefc61116c8177c04e380973b78bcbc4cddd5d6df2d717e6fa54483a5cdea78a597f7d9febbc70579b9feb745fd37f8ef1245f94d50729d57da8a1212121f0994851a3fbfa53512c858aee4c27cd31953293c233a348229058745fed73ec21f935e9fd758a3603764696985eeb98ce281b6463ba73f74a6b1754b6cebac311679eb11bff6651754727abf4f19c8d63d4603e3dfca5b14a07e73f56a9cd37db1f2d1e837ac4d14d453338bd3cbd90fe7bb1a69ab8a3362546f7ced493a99728178862447777e21c69a5a33f4c3645c7b24ac3520469ace2dcbff1fb39d31156e23c67b9c8bbaf5374eb5856e985554b71cc56d922cfa37d9d791e25bd5ef7c912cf3c432fd34a8786923c274a48c8861ae8166d44add09b190a663fcc8268a51e4beaa8dc5eeb9586759616366760fe98db7c2d084e4c7d7a2d46ef3391794036ca5288b511a322e6f46c74932c6d2dcf99e4b33cdecf5e694d04e7256d2e6ff6896947b3758a7a7e5c5a1cbc76e7cd1476a7d7ea0c61612dc15a9bf3669cad2dfa29fe87eeeef31aac36973afc6399c589f4415d7e4c4335ba3b31dca216155ae97019252ae4117ac60f0b45eb987a14980538c309c33bc33a33d6a9b6e2e92118d4853f858ab0b0521a6ccbf7398c00feb23b45b7366a083fad9a4bafd7d0509250f8d76628fcd4c3f53dfc60b5f7ef8730da7cf2a650ec5159d2ab9ae3ccf3289c8f14ce9fb95b32929a40ba5b32929aaeaf8cb695668f3e2ce8c704c55f5ff6914bbea087f9a89f77d853a3bbddeb4204875eaf2821f09978b4af9e10f845b0961eb3d3698627d250ae3fdaa5d72b8a66ab0e0fd0a1091d8ca00cc103502600050645e7c9184fb8e8befcd8bb2f8675cefcbd6a3fffa9995faf25213dfa3c666fbc67c479d8971f2aaa367dd332ddf897ebdf9fd5a4ef0b2dbdef349623ad74e9abc1fc61e438337e58f651f19c4d7190d64926f97dd21facf63dfc294b55e98f340f0d85499e03e5394c482fbb330f31790e9290d35f324c82c2659967c69ef77f63a3bd319b9fb4dac9164ea884363bf9c9a18ceeee9eac6571569aa4ab167cec42ba958396ee0e07ffe730ebee8e742b07ef70aea55473f6304c47bf3686c3183836df780885c3143848757787b1156f2e2b7559a53947487209a3613ba3f9a411e1c063edab5b5267b414152d4e4c6fce546ad62f96e1cf404ce54452479a866e49c5e0aab9e4e1e3f0c759999a8cd1ddb49d265474d3199326b4d6f94b16fddf9847fb2a5a0bf3685f5faf28266a20800901981ce9ae36c5fb54762c319e9e4b18784b8bb3b577d6746b8903963c59f2438c281fa268885a41a5008a0ba80bc5c213174fe3534e370cee5dc697621ff77139ff4e51d1332e6d709c2f6fbe71f27fca3fd779679993f17cd1f3acfa5cbb4ff58ef3589cb19b1c3c675e1f7bf3456b75ba18252908d575607ecfc139f87eadce1a8cfc4b7e2d6d2d27fce936cabbfbb01c9cef4bfadd38fe3c67197b2fc2a6e735b1da09e6fa3770e0062b3710714ac3890aa72bdd7dd9dafb39a6389ff7e3bcb1efc6738e5f4bfb4a2f692f494ec7f992c4996dd4e73fcb168b531469177e1ed6793f8bd32986a6359a70d0944417da59fe110671ccb3cea9cb607933f65797eba479696bfe23cd320c1902c5c984620a82e9bb1ba85b4c1d0b96ac2c2d2dad94b6282540298bd291d20b4a4480a45b4a7e50c2440909368411a45b3638d1dd8d45476e6beddaf147afd475ba29cfb6babb38c3707acdf9f0fc714e5d4bf3efa9a8383df27fcafbc25a8a33e3f2e8be4c466fc6612d63fee4d1be7ad5be5eb467aa4dee2e8a34f630a721761df7ae9669954f5c9b34f1f3ea04bdbf25aeffb98efbeba92bbdaa8a38fc99935ca77b2897792ec94aef9336479c20cdf4829e6dc5b9fe58caee3c729deeda0cceef1327cd9566451fddfdfe6f92cffffb1bf3909652a868c3cc06a1eea29a9a9ebc0683d569d43d089bb0254fb85a3cff4e8156e9ffc6c2fb1fab3fc5de9807623a3e58a7388beed358d253ddc7ff14d22124a12a5b34cb982a654c456f8649f9a1c20fede8d9b096f60e85499e0355cbf4da592e8966723cc7e8f59a6458e79579b54cc559692cd371b4e09010f84cf083757af8b3a542daa2bbc3b59ff2913af691a95bec394f45c5c75e7b25485a3a27e90c239196092908530a5a8c2766426ad57eea2966ed256b7052832719429227924c493203922f904c4182d439f8c95af6da4f55ea38333c33f61baf8919fc4b71ec9d75fe5813c559e2f2c6c7cfb886f1df309c8e7d1c2539b5e0c31e0a3f1d5d71944477df74eb483ceae9ee19ba754463240223021881463c8a6ad0ddf72bada9f64a3e7f85f43ee99fe96d023b729d0fd75e49ae7aa6cf73be585398c424e77be170fe13eca88ac66e7048bdda4fbd5eb8f64a5eafa8cf89bcfb707a8c5ca7cb555f69aea567f063b1cf3eaecd73b4e0c370769b2751f7e1da4f7df8a6f653f83194386b54edda3cc33a476c6335c9c62abdf6def7667822bd5e51333c674548fd243967b5a9e82624cbd757d4ddf32f48445406396931748b680b5196f64ad4dd45dd22fa21d2ad236c7477c7e4da6b8d4471821409386fae4a72448bec96a4bd3f45a7da6aa45b47a4ba1b2da8536d753c67340f430ad212eac88e86d53782c5e3f917fc906645f167512d23491871ea9e961fe63fceea3f8b0222a414450abbef547b25435f8a93c987161caabd92da4f0d396068090cdd1af2298286774636f45cda221928a2a5fbc3e9c1e95611b1bb331d7feacef1afeb54fb5373492806424308591122bbdba65b424d4245447c406401448ae84e14cb9863fa377f13ef6af7a972c2fa64f7e1801f5ab0c7753a9ca7a2b83aa6af1788e9f87a59ea91f8732c8a13bcf10f07eaa6e9f397cd3daee3e5c75e9de0e7b1aa0feafb3eb4e02ccbea8bfe197dfef76734ff70a09c0637ba69fa5ca7fbd0824e7b222bbc4af0c2d2dddde72996a038c1794b10bc1fd6d271eda760252edde66cedd3f56fe23f7efcf8d155d1d85f12e73b33f63c67798ae27c0575b7ff2c36d4ad216a7477e4fc99631fb9fe58eda554f5bd26c2c6c79808f64d5e36498f596fa15b43ba6851a44a8648d1dd8d9fe94321198a022969f3e339737a3c6724be3f96b85a70e67934f30cf1cd014e5c5eeb7083f617cecdcb734171c305658cee86f2048adbfce4a65b1469cc231f632f97f6b1278a74caab31e18602edbae140bb6ec068d70d0cda75438476e194d02e1c15da85a3834209353dd3dd2281d62e128c68170952b48b840bb48b0412b48b0416b48b041cb48b041fb48b0436da55424c0c0c09355a6cd12d2dbee8961633e8961638e8961640e8ee9a1250d8414bbb76c0a25d3b90a05d3bb8a05d3bf0a05d54647ca8a0c20a2e9840860add2243866e91a1d42d32bc6e9151d52d3294e8161911e8161924e8161964748b0c36ba550609dd2a63a75b652c75ab0c1dba55c6edee9a1472c8b8a41c699714a57649c9a15d52a4b44b0a10ed928244bba43ca05d52ae6897142eda25650526d4b4b040d22d2c96ba85c5ec161663b7b0a8750b8b20ba858512ddad2305124e7025e169571223ed4a6243bb92cc762589d2ae2454da95e488762591e2258517a6b4eb0520daf58210ed7a418976bd604577d794b082eb8b6ad7f7b5ebb3edfa7e68d7f744bb3e0c74b7cb4c4d8e191c2e24d0dcd02e1926ed9281b54b466c974c0fed9221a25d320568970c14ed929940bb64b668970c09da25a38276c9d0a05d3269b44bc608ed9ae1da35b342bb6672da3503a45d334672e0a8a19991e92e8104574b478c1752313238d882c5121a6023033dc80c5d99693ac3bfda28ce065713026c56746f556c0e18c006ccd011e9bc4ec43a6c2c2fe8b8243d9b2bf542aaa56ea47e79a14867e0ebc3d11db68b8909f876ae25653cbdb982e3fc9bb13fbdb97b636153810a9c1b7cbb1f2a57141bdd6ddb15d54337be9d0fd5eb45b60b8a89137cb40b4acae582e281e38cee9e81798e4e5b0d9eaa7822c013d99fdf5fc2f564e4f57abd02700315546cab1b82bafb862dc993e29444b7d3c7d138c5506b3aa3fb05aea6aaeedea8d81ab0c3c5d4457d8cdff39ede0c446b179391ef612758aaa2f3974db5f48b969cf0d7125e9a4d28ae251abadb8a502254f879d29b27f94ff9a1b055cab83c0a9f66c38a44098a921874b7121faae65237d28cdfabf527adcd3c1d4fe74305c5e4ddd4df982c674261ef6a62b5f8fb70f9dd9965d58af3731c8bd90fe7fdd182b92cf2eeabe23e34e439d77384a084463a8322e79217f1276b3bb8bbaddff7217118dc664c8eb3ea3b9c692da3704d7a2a2a1291ff4a644c6495ce3f7201e70349053a0d1711306e60b0712131a11b5f7182b65bec1680104745f4743d3ae2741d21b98e84a8a8521ab9d1fe7a6a7a7a42e23636363bda657401230b1839e1324aa2ef7b77079722794bece5782e23d28b1429d2bde560d463c4a3fd65e966435114454ab88aa874671a7e919322a4a222aea21d38de8dfb2bc75f33afd47574370eed22aaa2bb63888ababbabcd5bce5b4ed7911d5c477068b096377b8e577b7f0aa654f7c323b88820691e2356ba69a6782c8ad3db969022035b888cd4f1a4d1180ded4884de6c69d56f0268ec35b1dee94f6561491f5aa6a2a3a737cac33a5f2fd8585ea3de7c701569e17b3fd32180d041084bdbfb53446ff2c2e212a281e747079f5a9d8e7fbcd39d5c449ce8f6d7bce59cb79caf23803d9d4ff7a3037e68419d215756606273810843c839bae00c5750113f38c06680250a90812424179006687600c48373f3c3c48749f7758a5ac2f95ea488df125c4d9c9b29eb61dae160a782e3790ebe99818ae7783598e775b8d224e19d20a647444980c400c8d7fdb5e1ebf539f93e8f59d089bcfb6a8ffced1468452a7b5a97e78cb6c4ffd23aef30e8986a620feb9c560c61763e9eb479ed0c93b362c7b029ce8c3d27e7c230cdf9319d653afe8cfc8e8e1e3e94ef73325aa8cf31c5e1b535c93b1153eb95c82fcd503e4cf17da7cf498ccec0f91eb30fab16747fb905f18350901c3519bd5e9e283ecd0914273953c7caf05351199177a1f834369396c82a25c95a51f84e325a646d1451b64c50d93259a6b02e71dd2875b78d910f7be2d7fcb2af63629d9e8ad6ff7acd99de3ba9cfc00952dd87f224a89ad0d729e6fa778b19583697c26a1daed61f366f49e4d726711146717dec93864b5cbf3eb633d02f2ef3bc9324a787f692fee3c7a47d91779834428fbce3810987820d160ac030460bd98e504198233f344d815609cf3c8f68c01105e989ab47015c3d8268f02f4c7ab216557b2576e2cfabf6bbf345d0717ed90cbcf3b1d7eeac1426c226b6187b267f0a7c2aff984df20efb788c6b3fe5e197f85a3a7b98cb2a7d0ad2d1560b7e14f60e5f1babbd92f008d75e89656272bd70a4bbb3bd4e2e1e136829601b02cc09dc0a2cbc24a047cf85155a70a3050db47085e7853f8582b37e122e1e2ae8ae1403fdfcf4505cc3b9eabd79cb99af5d1a2dd457cb14c544093997fe6f54918b474ce7c73628c20a4c6c4b74370f39331e69c6b59ff2320cbe0101b142a7e052e10b0cd5dd13f861f3c18a6b871b3d741b1b0fdd1dd39b946b074f774fb952e8c0ce0fd50e7831145353529411d3121b3c29a2256fa3d2bdedd0bd4de9de6a1bad7ba3da72f786bbb7dbbd49e9dec8eecd766fb57b1bb7a9ee2d4af746bbb759f726ebde625bd8bd81dbdcbc0dcae6a47bcb81b54935d5e7e5c75f37920b05a9ee62d6a8dbc885424ecff0140ae707b94ed8d23b9fe75623d51d86444cb4a73d93cb0436da3f6fb1356122ce4cc5129373f4ba2970c26a3fcbfe7d8e6b12ef3208e573f2b09ad8e1a9e45d4e2dd35a8ea3056f7230ed93581b952d130e93882c53f8d9325ddb85af97cb45e536fb14af8ddd2761dd4bae1615dd983a0dd556ef7eb654427a3a1e1d1d9776b508d0dd38ed6a4581c1f374da5759a51349035c5431829e80eb25470cbac0788a46efa48108d8e0010b70c2e57245bb72acc0cb8fc36a615e0dcc8fa797ebe74abd199e4848f722996a4242ce4b73e1b081bfc879696eb3119e4ef83fc9ad4b9ea06e10e94c4292f346e19f9a518fef87b5f0692d9c4b727d2822178d105c34b9cb9af739ac8f5d334d4bfe96f5a1c21761b32e49ca96a996c911c93fe170164159a6906695f0146895b265cabf942d13fe4f922d135e529346b0ca0beb6397cc0cddfdd22e19cff8431446b0caf33edbea7a21d2fed271b94cb95c965c2e352e17177c3b10d37107c474ac6ad70a0dbc33033fd732bd93dcf13caf483a91f06f19fbd0a76b05c60a8c6e7fbdd1ddcd556eaeb4e8f0e7173fac492f0cf0a1f3d2a493557b69fea385f20ec34aec33b0ce5bf492859225085a8f858fcb17272eac486897d0a5896631110420e0dc789876b59ff26c566fb096340b09d04ff7e61303a354ab33b4dd5b4ff7c6b3ed746f1d4e15ae2ea37bbed39ea99f70b514958a4c32e06aa5ee49fb746f449b4e376bc3d9d2db0a74c045b7dee011da5aa7ad56acc1fcaf0e1c105339cf0f6dfd5eaf0f656093ee8d86eeed8716478c1004b13b006a24d1dd6d842b4670eac6095f0737c6687c3b7204abe658999055ba15204bb5b44a119868225b83e758a6ad6aba956db53b0c9cc6a10d345a6d4060ce3c8f7476701b3bba2b9db2e9b896d78954cda5d72bca87bc6a8e4430a3fd25042244800855fe753a8eacd23a6f3630886084084de5feda5cc00613ddfe029f8a0db1db5fdd1b114340c110ae1882136ce8b0e1021b280cc108dd3bfe0a1f975d919016790d21d61a82549197876184358ab0061afd370eb50605d670a2bb49a3ee8e8110ce1002098470c51a6b0875db845e13a1d620a1fdfecd35e808816c7fe1fbf9c77927298a36e614861062ba7129e5f190500df645109cd8094268c316a215841df8763ae14b212245b4f47a011152facfb228fa781a116ca96296487ea8fbd932f198adc13cd471aca4443496f8f52261f3e62a247fcb2c8ad3e8f502e9fdfab225e4fcd90b7c1257e5a8d70b2be1030bf8c0097ff91045f4017c80e4839ceeb6b1a1317caba2c027a1a6c09f1975cbd20842396eab6e238d2b3c0d265a280da1c6b71bc1aa3476b4bf42eacd3b49a61e64d18446e3c118dd84073b74b7e7e5e96159f5f0edd0e0020d2aedafaf53f41c1d0f538732cbb409a6b3a8d70b562239e9ee9b6e1cba8546ab7f3a9f9e8e47a7d2ec85cdf09ced000da48db5d1a8a07b8a9446000cced8b283afeb33c1d47490bb33497ef876d8bb4f8234ff68c5b683629a27aed35e708e4ee4ab9a630c2ee43855738c6147ca8d481ff41d97f2f5962eb6a52d07afc17c5b825bf5c682eecea15b3928a1bb3bf0435be9cd69f4a01b06dd313863072c7c7a33109f8e470716ea3cbd796ba17be3b1b1d0b91cc3dfcec04113bedd2cd33b773069433a9dfe93544b3b6c3349c1498e5424c7f9b42f7c3c614e8e254c9663a1bcc3f7e9cf68dff739111c1a9c88ff701afc3fe6d11b2fe224cd5e7e91080e49f30d284ed02be29e5fbc3689efecd09c8ccbfb7d9f773598e7e09034dff8148a756ae29d221dabbdb14f9ce3ad96cafe7aad96a4d9279e1966dd66ffa8becf2fb5b60749b38bb3cc9752f5f0cfa5cfa62f53b11c89bc065dd2140ae5d485d38ab5498a73a4495d48bd06ef4251240a69cc3a3997bcb3d5478b432babb4739c0f572bced977ad6c8a31ff3cc3b10c5faf1a382f68c70ff18e5fea3f66d1a777f72b6d7a4db44c5fa752d2cbee54b2360a27dda75fa752ad28fc4f328baa4949ff50f971c8960927d5398bc2d72e8dd74645754b0667b4bf64800515edaf289c1732c861b70c5a32382283d74fa1b8b3d93d064674770c70447ded3e9505313dfad8845a5a03064374b7ce1114395ea4fa4a484491e888f64cd8e7670992f8547b2534cb14264ddd39decfb309395e248c3fa4971c41a90fad91d37d27b096b65622225c7b25617834f33c924d27ebf422496f948d0a97e0a7f7a9487e925cf52f9bb24a27d58bff849d329e4a1f5a232647a194f128e3a9941faa064a599a4ae4084a4d81562c32cad822652b421925c9f8c5eec136c34603c185052fc0b70beb4f811326b43d114348a906aee869b615ba7b0ac544b038a926c6b79be139abe5ebb50213daf334ea15d10a685361dbd18d69f65e0bbb254824fc70fe9d659debe3693e6150a182234ee253bd1269a3c438428ff626d1d119bd189da56004ed79fa97b5eaebebe5b55200450aacb4bf3c4f9c2f6b3f75d3ab03a420c7fd4af3bcfc18df0edfa7f74ed8eb358255289809c38d30d0082305ed2fd0cbf1fc8573fc45c3bf463f35a376701854fe0a43060c31c2078302feb2d91d6330a0c018d2b9cd0ee5f245eb8b2d7a636dab8ddb447182dd5be73a694f45656fe0ff18eeee2374b711dce8ee22fc386b77b741043686308255ddbd8610ba3b081908dd3d811ff8a0bbd5e8ee347ac003234a6065099e5424022969414adaa8fc501fda25fc9f243f147e11363f5ba62553a055ba569cf83e15ad4bee7cd1a8deb2e8a77df0a28aaad799b79c5e5cc1e27cecf3a924f217c924dd1d96504f4ff765b3cea51ca333a8119432ca5548e62d89e62d896cac2639ada0bb55204677a7a0bb51d078dee9b4852008314475f716821a7477579180136ca0f1440846c0b4c4173b62a50217495ea4104194143574e8eead250507d0d866e06e46a0421b02f088a8ddbdd5800924a6c400830d18ba7b83820634b07812811636e8ee2d059956c680521b61743707a345ac0f6a40001d74f74645140d702a41035d0075f76664004e9ab20881802cba7b2362072fe05c17a1077437a784c4880d88f041c410ba7b75c21160f4600926b2404177bf1ca0006c6c62bc8e70a4bb3733848004153796a8015077b39af8600c3023411432ba7b1303e603822746488901aebd92150142f0a4bbc300a35b0c670b40e00010000102b25b6707ef8c60d5eb855fafebc1ca99072b673a2030a16dfefeccab686c48c7433ac95a3a1ed2f17c2d59f5fe32f257d35153d4193135f0f11fa168c9097631f19574b70edd92c095ad6c7da086c6b7abdd5c7f879c23185d83715f7051d5ade3e1fb140a47d562345ba68a24d787caf5a19284b3289c45229d493c20d5655b978ca42abd16876eb148e4dd175efc7d3e2995875f136be41c696d668c7f16e5e183749cb5168295c83becf39657b41656cb5467da4aa913f96a3d4f77ef70909f204184d8ea347b658ebfcfbfa9a66fde3aab384b29ffa0a5650f6bccea54bb1326dd271f86f394fd95c9a64379581fca468da054388bb4c0d2cd45a0d5bd227b15a557517c74afa0ac8854e85e1dc9828deef6d79291948ed392919453735278129960af6490c5ab93552a52ec5da53e85e2570d4c494c4f444dc811949ae19934c313e9f5aa761c2de88563a5d3eb25832090e47cbdbc8943f0c3ff0b054810214066603359bba0cf4f88b31571b622141021650c723c929caf9747e2213d4182f85c0c83587707f9f42c8941c00b8ae87ec11ab34c69af9747e22617a400df2e5358904f8f87ef93e2cf7c7eca209f9ed205df64d4cd3d71d22dde8b04b4a248d4020ab85ac0638c261aea8a6b0c202cc8fd2ce8a2bb27b953cb3948e1fb19974f2e7becb34cf47a757338c070593c5690868d0b132e45ace048055c88a194022d8404f1414a8113cd002b34e8186ee52908eaee2a9e20509045a596f4506000271faad76b68862792900cd7a6f821cd86a2b5a07767de5856309c0063c717547c21009f9ff20b16a0609f2758ad5198f47ab17002a7e4f46a9ebadf280a1350697c3bf043194cb0a3db045b09cae86e2f22e4fefa3ecf789254e11c0288124891600c1230d1cdddc0393591a0e5c3a412f530a13dd308a2bcb881cb0b26babbcbf865b2123f59cb8fe78cc90b9eeea2013c1e8fe7c384c7e3f17a98e07f12452210d3a5ff242f412e1154d1dd1ff30fd7d2a39aaa22bd3ab59f02eae980743c3a322b724b2e115c1141958dd524359124891ecf9953d4eb750508d0a0443eef5aef9b615a69cc8bf0e56ae092b4acd29ba13238499f9f7208e3297af7c9aa390a35793c61433e3fa550d51cc519f501db5d9b4ef871b04c2e2ea4b823467a479c2f0f3475d7759ef38b587caa97f8542f7aafeebf4970fd299b716d3afdc73c5c9b24c653043b30d58d6f37c37336c373b64516ddf9da19766bf3ebf17daabfce151102da2208df2e24c2812ebaf1edaa773f536f7c12ef804fe5e24015dd3d010e0ce9b07e59e9e8431ed69fcd9b6125766d20025a00753fd2d09050b543deebf5f4668fc49e0c8f33cf10c9e7a77465000c512c9358a6fbb3dc34f33c3a9a6af2f92985f494a2b5b08c69d57b581cc2f11c2caa9c718133f0bd40d50fe110e1f8fc94a094d193328616f80aa4c0550121570576a4e1ba424b4e0e5138ff299cff94ad08f55328544d22c78b6484841c2f52374703f703a7dbf3a078b0801260a0022351e4c0065e50d102212727bcf4105381195705be40c2353085ab40b0000f00d4000b19740040c900365970f8140001575ca1f3831d4aec38810a1a70c4030aa286282283176e8609e448e00ad7162c3819bab918381b0e866eae878d2b0245b8222015be0bdd5c0b1c8f22acd14d81d35ecb340304c22e3f46528300cb8a2abadb06bbacd8d133cfa32ab2546142773f408cb0fecba693eb012d74732ba8d0dc8eae322ace9f42712e6d6d2a199922866e35ac7c0d33c234ab5945b686d1b2852eae0da6756c2e286c39b6181f1650af9260b900c3c5c4b8b056dbd629705d73bdddc032b2350c1197d6f1b2f5b66d2e2e40381aaeb76d5b7148db06b371dbb67a99c26ddc06c3a1f0b46d2f1b4c731b8e6ec5bdbcdcccf8c6e23856968db56ddccc26046e9369978dc5ad7edba270dbc6c52ce158e036ee85c56d3be0b6ed85db6ed852d8b61517c385cb0a26c786c2b66d1b37843b2166b52160e3b6176edbb895b771ac1919205ccd3614b3b9c4b02ab2c1701cab6735c3711b8e0e21db0eac1e9a242b27ae59cd2d6063ad6e582edcb66a97026c34d89aeb56abd55b6fac8ddb7ab0c5703fb68d73e1b8d5b6fd6c3b5bcc56c3711cf762b4f9e03816b7f1ac3a1a156266d87090b002b7c4e6c2a9b0b1b6d5d69c13e7331bab39fad2fe6d46acda868bad8f84805b6d2f1b0b66c3b1ad98e0b898d76a0b5a6d1cc772ae46e66673d936181e56cf0b3c1b37f312c36ddbb6c16cdb6be3b817ac5e36198eb5a2da7a70e5b6ad7436191a6e8b81d9b8239c0b9c0b5c0bab5ef5a6637be1b80d07abb562712a701cb7bd6cacd82613c346e3c26d2e5b0e2016b824181a196e83c5d4c070db0d5bccb6adb88db5216d2d7043381e1c0d07c3b96cab8ddbb8edb5b5c0d17030ac6de5b2da36a06db5dd6c325bcdb66d443617b89915cc16c3dab86d7b6ddb0b0a9c091b8bdb5c58db108e665b712e6cab15c7b138d63684e3b615b8994d668b6171dbb6bdb616b81998d5c68a616ddb06e4db8e1f31422b1a5cb6d526b3b1766c32322f1a272cb8b0c1c0da38ee8583f9ad75020f0dd2b6711bb76d9b4ec334d939f4b0a981430fda7180e20c3070b031038d26930610583283323acac6050d0545180f35bfe82728b240c0938d6dba018a286a384d204794a0700108489307cf102876ec0c99391e0034031c3100d9cc6c4266b01941c8f7460284f811bafb8ae082ce4705975306036c8ef3fca0414b4e1a2f7c5ef382a740426b29014a7f2e48b4b380682e057cbb972b333290c00a8c0c8c0c2b478c101899550b35513e30f588b97999890962d5e8d858352f1e8bb6c2b172b1c600e200565e72703032ac1c1c2d0441c0ca0c939a909593c3070e080b2e4b58db8b9007ac14513a7201c1b166584a3843b072831f7e80e1c3e6f272bd70c4078eb5c24a87dc6c865c685081959797179cae9ca0a30800ab1a7c08801515649ad4985063c2942427903178660d2b4259a6866f002d1899150e9729961045f810c3caf1d2c2a6e3c7071c4421f363055762d858220747e372c4053384a5e49d6cf9a104979b99243333bc3c61fd8019f20306a0a8e107071c81428be70a0e5069880f58a129634d0081c347c7c684c78a088c0a427c988119f232139363059454c3aa8599246b58998149c2011583c40292243332483550a11006aca4c0ca1132f3f22a01c7cb0c0d921e3a30c0ca4c0c920b3022acb8ac5a2edbcb0d2f332e445e7e0c15d194323e62905e666054c0712413038b4926051378ccb0020a3f626850f234830f33311dcc0f8e1fa02427981a16cc0a2eaf9510991a960f3f02230333c3da7151c20a3ab2b9b0b0b27141814726065c0d41426447cc0c0a32403e1831d10089b961c5c08ae1c501567ee4601db9d0e0e2824c0c2c1e3a7a562dfc60a5dbd956b1950e6be5b2430f1d08b0015b23000130b6d81201074831440f55aaf054ecc14f87273948315902e5a46403d22be8e76606196070560840f0461a68e0c0c8900c3d565001468b094a3005036e4062e4d5c20935342f1fc8220356503185140a80420b0296dcf8d8e99a384014d41b6e040196431a3de080675d033882c68688ccc0508144162099168cd1002a5e4384a46002cd4c0c0c0f162e6081271f3631d99094044902042044004828238816118080037a68c20448c80e13586180010426434584c8c0bc4820880c4c200a250840c40e4f2420014734310d11f151b31561d506c7064b0def0177060b07dc0c5ccad85cc0b1805b818c0a6ac4e0c2e0bee0b2e024b08a0037c58ab2c5b0bd10d3820b0f1c2a702970276c26b84a702181736d2d560e8ec66586938989e160605c38d6b6dab697c6a163c5c26a03565846586129bdfcf00d46083eb41860c5c5a506660617f8c082a28795428d140f7819c20a4b490685191e3b80b02283032b87950b34f9a52626494cd0cbcc8ac88e1917125889c1c28a094f7c90015a21e0073564587179c38a4bcd0a2bb052937b6cc08acc8e1c1c0c11971d9724b09c40a3c2123fb08ab0a2c34687cd4ac7b513b3c3f5c60f2e34c404c108ad74ac8462a4b0b25aead9799979a9c2ca8ae66505560e4dcf6a63d5b0726692b8d00083c3cb0a2f332f516a4ca8314166870c0a3038ac8cf0c3cb0aab2d3fb87461458628acd13d195a60bd6a74bcb858db4a6765c472aa49e185836902c3020c93cb0939b6179a950bab66b55a712b9855cc4a6686076b05b30486041d4e332e9817986d05c302c2da616d312aac58f041e605970758c951850fae17160b31473fb0c8f8a187e7fdc61170d08231c200c38b2e4a191412599014192902646b1a3dd8f1d1d21103e3248726a6374e20060a5e38828789258e1c800c167c21020e5c200253ec74332851e623f015820fd480810ac40041057ae041872739483531d590e405175240210b0df4100108f050c56b8187ad2fe420430652462b6f1c61086b3c7192020a2e168820041de0c0052c208123042084a7030e4d9a9892be3841084ca00513482f70c1172200010736c00029880000103e5083052ae0c006b298c0152b1f262171e150c3066690e105083490812b1a8084111f7a3a3c71d224090991178e0bc71727f002014d2071e5431d6c484a8284c82b070e56dbc08c2f4e3082106820035748a0010310c0152836241521f20202d28343828bd56fd8c00c198cc08b1080400319904003068084008cb842030fca13233b9d1b455052a223470c7864298208a9263c51184144adca0a99375647e08ce05204ae8d9807b01cc035809b62f3e17a563c5b47a3b3a261e528bcc0f1d858805961a5c2b68395c286824c092490b06ab174d4e468e158a146051a1e332e1998981718eee5c5c565b5add033507c5de925773eb420ad250c1440a078d172042d637c7ee3446a77ce19e8530b03b444a12589ee8f8a18c78b08f94d1121d7126ac1a13bd76050baae739284f4b2024fb70b911e2e3e9e10e37ece93dcb9f909249ea082f3840e4f20b5e799a7e3273e016b24c00cbf36766709b802dfeecec0f1657c9099d2fde5cddddc0ca7a34b801287026702a763b3410fe9f121bfb7047f0c191234046888902141860019f233c46748cf109e21438282828082840405090212f413e413d413c413340428080808480850102020403f403e403d403c4043840409011222444810214084fc08f111d2238447c8902041418082080912240890203f417c82f404e10932044810102020428004010204c80f101f203d4078800cf909fa01fa11f213e407c8cfcf8fcf4fcf0fcfcf109f201f201f213e417c80f8fcf8f8f8f4f8f0f80ce909ea01ea11d213a40748cf4f8f4f4f4f0f4fcf109e201e201e213c417880f0fcf0f8f0f4f0f0f054f13c025a65ddc9630c8a93999c9b999b2a5ec57770cf99c999d9c1ddddbbb912ba3912ba3917a7c38912f88b8aef0727a89c089d50f27dfe9ac9f93e7fe5d88bee331f8ee7d88b375f9122456cf0732d1d4e9ce084099ee3c387e749eb24d1e87e35e145130d68c7f4cba56bef4552d4c495eeae6b224a13603711d4382e24e43d37ee00d7016070802d77aad23bc3b4afeb004bdd7d2d6c76732edd1cab9b5bad8480038ee3b6373623746f6e746f4570c26500210304194000ddfec2a5759b3d7cbb1e1d2f22e4dd5b1bdd1b11ba37361a533f5a756f2c267898d8d11d2ec146b7b7040cba1b8b74e60fbdffd8125d74db585d624b7727b144ee6e5cfba9277d7fea5a9a178bd9258e2c71d378899ac64a04a15b0918b4a74409669ee1cc3f2a718513ddad4411ad4455e3a7472811831235dd1d7ad98ab8004328000c6cf6d0859c26b61a4c6892a4b7190ac0ea6ea09e8e47275b7bdb38c2b6861082d06da8d0032034047ee00335b6347ad03d67554808b0340013f400a454bdcc054a989e9248712111019b106372dea8a2f0a792885f369dea549a791ee1584dc2384b159d254b16b1bd56c25e2fbc4424fb88315c4714a0bb7f160aa008dd7dcb9066c5184865560949782df84fb93e1592fbf7a778d5d611ac32a20d3bf33c2a22462b62887016155143440f88b082080674378d089ceebe7f3f871f51da354496114f1a3863d53221326306edb241b7cb0644dd9d423791fed12fd46047bb6ab0d3ae1af8d0ae1a64d1ae1abca05d66b8b4cb0c1ada65860ded3263b6cb0c2ced32a303edba0152bb6e30db7503266e808176d90007edba418e76dde006e8049925ed0a8249f7f64577c76c5cc0ac58249613495629cdf567ce0f95ce0856e9504dd98e8eebbc301662060b1bfeaad6fd052be70c8b969ec2a294f3226c2241f2542d93386156ac4156ed6df27a05202700ac6e57100ff097ac520f66c5227c3b990b005274b791aeeb8cf84bc7af1d4129ef876ac948caa1c0ac583482524eb613746f2628c1f60112746f22184108bcd844d0bd85a07bfb0008bab70f74673c959a667822d5a4703ed252f875c9959c06820d207a00c40a80c042c8dd6d06820820720082c88b0cf9fd9c1fe679d2a0aae65287c599b18fbfd4da19cec199e6e0bf3e4a5bf3183805ceabc4bb70fe2cff38999cba4fe7bb641ebd5227ef3e9c1b9bcfc519523c65ad92ab871d3c88dd4c8ea9f3d0060f3d70f1b045b78b072d3fc466513c0875f3d0eaaeb245952caa24a0bbfbdad8fc0fffebf8cbc62af50e8f3fcbd9daa70d0b1d0c008574ed10b583936b079ef6d7dfe9b467729b1d5898c2c6141e927e0a85f2a7399e589c454adce7e729101f9e1e3a6316a422ac243d84e1bf13d441a2446404f55328eeaae69293ff4a6c14eeea9c91d7bacdb6b4349713c433b01c1d4face3f8ef675a69ec437b65977e376773d634893caaa3fe1fde26eff03b9177b09f9ae30433cc6916c59c0f7f8e339df9f7e1506c0a1faa26b9a43429469309e8eea176915f7552c85fa2756242f2a17abdec19dd6d45603960b5e4f8cb5675b7fdac52fbcbce689e637f747bf876f73339c3e9d540572deb97e78ce6d50986600245b8c62074f774dfae8c5be019c588a55bc7a8d485148663ac81114b70f486f851d11d9718437717e21a8cb1e4c848109efe42071e3340d9f143912c45ae2733a801288ae886e971e8f09f5e14a992251f5aa312ca15054a9420501406dded1414040aeaf1b49966eb947762ee91e7786d92a02018f700a748fe0d0a0205e5e1fba8a109eca88bd9199e48e1df38f93f35bad21468c50ecfe0decdc8d9f7d7c66ac6b9f1c7e24ce2dde7f3537edf8fb37e195bd156bd05414ad618fd70725eafa89b6f863f6631ad7306e297d597893a7926a3a26390664c414a1209fdc4f97be33e33a6a27f435142e3fc1bb33ab9b4b656d922ef742af5c9c43a614abbb944e25d0e9277df07e51df681c939deb877f865367b2e4971e2e0cf3598577ae336cabba3a41f2713f82454928dc2e13f1368a38c8eaa65b2f773a8964914a70b7d72bbb71a2e6678ce7268999c707941d4685ee8f29a784e2eaf06cfc7e3f176ba49eb3832dcf8cb771c4706f71c19fc455acf91c16fdc5f390e25a900a8c4f028d7c1a205194334032000000000e310003038281a0d872362d9a0c24db50114000266c46a9a501bcab3308a31858c31c41000000000000000303310004a1258a65ae1fed656b92aa8fa7122590a3c8212b538e3c31596bb447c8664475bc7ed782249f5a68e05e76d66774d9cccb486ee38ff5359e6e36035c9f30919d01b2c4e0fa3de2438222b1b7e828ddafd2ba4ba0a81ca1112e797fc76118335a2575d9cdf0af84799e148751d368dc35f349e26e2ac7015aee966c83fba18828f9edf0104aefaa901023ba4cbaaa8718689eade6759c374e4eb597e04589bcf17667eecd586eafb8e4adadc92fc008990125a09072b2626d56d69efad456207deea495f5e3d5a8ede3d31fa74d80d23f6d4cf8516c40fedebde3decac7d149e23c293bfd56a36421f60e743d93272e53a4567fa7601c8dd45cb51b68b439923871a362e55d0bcd496f021b44eff2390a67ac10c34b9d397b5436e3b6a2557f0def6c14a078cd9decdfc330d84bf0583760fe8fbb44bd8ce495206085cdcc9b860dcbd2f99c045a8377e146b452179c9e08deacd5e3c9107625165aa07d1f24b025e77bd908345623fecde3f4139dce2c66423c225742b25ccbbd3dcf8a1f45e2cd16575a0ce2779da716d8ace5976b2e4cba11c4c8daacb7aaed68ed799852be7dac30c25e716e681d831ae6f9c2e98e07e6701134d2b9034f988e62a49edc5e342833bc53da718121f24244c57a417fea14333491133cda33534676baa9b9b63ca3e408e97eb71d93a807b378fd5352bfdf79bfa0e3f63fcb949f96dc94cb19da940b34d7a11e5d2fa516d0460f9b0bfb4d979b9939e2550b000e26021c77b059d667fdd1977e73704757046fe0dc5f795b78b5107691e6de409f60f95f77c565606f0a677d136e1c064c05a68fd3f83cc80f00fcd327ccfe0f6b790a7d876bb9c7fd08f479ca113aeb87f0cb07ddc85fb3dfe316d3b5570c858fea0bfb4ee496ff6f877c9b4ef0ce29db76ac6634334926f0ddbef4539b15010aea6158312df37adf287f73455ced4ace65580982dcc4c083447d90c33a0a73ee8f87002854c46a8be12a319f0756e98cf8a6e1b66522d243dc6bb780645ae3861a1e4a2f0fd2d577207c4ca2841e31b3d44b6ebaf55a7dbc2b77d52ccf3eb75595cf58e938d967f3c816c2aff4c503f76a48dd0f1e412be1d3bbc51a14f2b95c6213bf8fc30fe2bc7bab310017f8ec6558e054892fa3f4e8e76c11423d155d351511b1e09fba87c0f1af03218dad1e938e48b46dca6240702986aaf87f1e6ed677dfbfd6f02596d13d7b08cca6414f0fadb9996715529aa3ee41f0b8022df34bc90ced572de973a3a93169e2af9c00a7bdea762b3527ac60e1bbf6e151d9fb222282f3efab2e26217f1992bd565085ce91a2dec2179045bf06159c213314d4d3918d7bbffef312207804c18adc2bf75c951b5043d043a48c15ab972363bd82a52b5af6ba57e56227374f5dfde9b243990f8451b6e9ae17b1db5cdc4abaac8c5c5b26de5a0a37b60486c97d6106b546f14264f63ccfa8fe59ad2bb4a7d8dc5d507a10f45b5f5611210ab671725bdd12c384a92e0f7ead3937324a860586cf61410b93ccdb0598d5ad92313807e41685ed7b8fef46aa6c4a3353a1e05fc2f2ed547e2a5918c6e620463fa334cc16b0ef4d83f1bc4e2c54727404f10b62fc9cb10b4d6947bf6d6bf8adc9bb2971f0b2f8bab7437127656b0d3910346eeb2e8d1e89f07fd532e5747e91aff87aab643054219cdb7c44a6fe21eaee98f86eb2c1a59d25979a4868dd9867d396fd39675650a782cbcd5763cbd2ff26db5beef635cc6afb6b7141fea7cc94a91adbf42da21ff3a1b8ac1818233c9ab973a62d913f1b2c8c7e632e136518b9e52ee60762b68d91b926b5c02884d44a566ed714f6e31d3a0defa549fbe09c55ec3c242715b8016031e325ba164b38e2ab3c39086f29e2fdcb17e61f779f88439faf111d5aaa5598c27cdca7beb35a57159a53ba32db3b8ec3154bc50ec7ddc3b921f87e7695ca2fd4957e41033aa76d24e41ca126f72620152073bc4bd5b8b7d52bba935fd2dca770aeb7f90028754cbbadce48d9e902f184e984233a1b02ec1d548a5ca2c2c2a6a5724551f80411426b37e346ea35e0f20e1d1d825be9a4e055f0624dad4a042f51d075a978b279ca72a519ee7c9cb06f69e0e37008dd5fdf497dfb82af085f3f9df588fbf81cb5b0e02d55ea7546a1431a6ee7ecae2c70c2504f49b84380f8b887811fae7a4e262888768642d056b599a8dc4c9984d4505da0d1f3af4312e5d5eb36230413ffc2f49f51ef39101a2ca2b622f320b7a9e8ef7ced64ef2b41470e7c91c4ba1a55ab216035f417d6d18004d66b89e0a0198583cc9cbb1acbda48972c675043266900a0660db6e3f1fde9e747250328aed91360ce921984b78e81f9e8754da1d1abfd6a48d40b8400b10ccf6690facac4f3af023e0343b6eaf18bd84000fa779abde680550b00b5698292415aeb4b1ef2b1a39e4848ef9ce9c55be7af319d379e91f7e881600bec124f408bb827cf6347ca09384bbb51843edc530f8ce9b3151ff93ebf353d6b320e3a6f9fecc71907ad28d01e7eeefb4b8224a2a57ff9d7f9afafc717c0726cb03284121fb2f04513806742f6e82706fb6d643bcc5e8319d61898d5b151260b6b52ade976a3f62456f96ac9c6480809a8d681d7bc0fea10870cabd18d54b24889eede32dca9f1e143fb5c4dcd1d85a73920e743e08cb80d0ac6bc4ce834b168d1369bf5b717e81853aa5d77b05f6d5b108048c09104b17e0bab907f856788c4f1fe5a8f9d866652aeb1b9ce72ce5eb2ffa19f8955e6088a43a5125d6f00f969a5e44e09938c906f18c898b211edafbf8d8773edc637219d5cdfb7d0e0e737f58e1f56b05cb4f58fb5b37b4f45d86cff5e762a016fd2ca918c13bf2adcd191be108e61a4e791c44d437b990b3fc429002f237c1e20fda79021d05a01687a5a34986c75aba78371e6f37435cc713c7b43e9ea3a8764958bf5fb3493c1864a46f5708037131502ccd5767cdb0f5983749c8c4e74e6299c758508306b65c2bdb622d276903f1fd44736cecbcb82f2e3c6922daa41765612e3c8083123c63fec79813586ed9071736623805c65d467d066ce55e4a469ebe7ad3480e16e0f5fb37fc9dbc7dafbe5ed6db9a38e600edfe6f73301d27de01f0b6d320b2a4a5051d45a02e5204eec6b70aec865e1b97860ee9889d578da46fafb431f7115c9db85e3b1ea6558cc5cb00204f028b93929a38d6bccb6ef3a3a37dfef28318d79d63bffeda9950c4f721babb6f38f44adf96e3019a33db8146aeb3a589499e5432e0d2e2670add37ecbe32f71e740ebc4e360fafd0797cfbfbf36860f5237ff5faa0f21ff46d2e22fb30ee000f798bcd04fd6717fe46e1c06d5ad0bd8d12e6e2901e93998cac6c386de8bf650dfb93b5e3a32d8f3a1e36f4d5b11be7a57c06301a0f3dba88513efb4b4dfcfb77a8fe31acfaa0fa08d14364be9b0a1ecf5aa9df76010e2f6d8aa1cfe3a400fa05a530f657880f7bcefa4380b4b770d9945858c0cbbcb0555daf54df8e79c170f4b31a86e10b92fa48f6cb72c389aab71dbf92c20e33677e40b8ebb590c170f355956d9e16721553db3d0263ec6d29ca297df2e4a69abbaf1cba25a51992ab06315468ba32a8687f81adb1794d18f600e6ae567d619e56cff603a22de8548cd732c4eaa49485562f69ba7754703624ad0445aee788b83f07afd5769d1e4a06eb296061cdba35073616df1e9e0bcbd3fe8f66797d034b8e79f7b36eda3f382e34b0709182d63f8a321f3a7aa62e2db69e48f0073bd7607effeba7c4f13422881c60fd6993b478721f9ad26488dde4e308e5e7a1acd826b4f14473566dd203b1dd93b5cc71d1c7f6d9a5e7b32fa5a317e27519fa569485fe8d4f7f432759f7d98302a3eb696ff278e653db13cb1675909b5794a703ecfed0f839bcff3a1175869b777f5d90bcd33ca7482a1f7481d5302229249ae6fc8cd12fdeaffad9ef18090f33bfb0771faa7fd657ce7de87ebc481fcb52c5cfe7315c03212634e78de87952a8bb4d84da82071e9072440dba4b3c701886c98e4bf24a6bfb7c91fa18721eeecab10dfcb6316088afcda1c50b769b17b16399d0e9a71641e780511f381a71954fd4841190b05156780e415a78c5eb381fcd34128fb6183f6e6332e25ecb21a77af403a8c0381bbe9249df3439932d449b5c9bc418351f0f0d9e150d07e31d28d01e8981e2afe390e4f1f1a446a268b062f0ae4093837e9f1f4cf45d4fa3fa0158f0e8d4a41b1bbec2f1153f79ea7df68d41548fc083373e3ce38c302b4effbc457ebb0b2da8d580f7ce3ff872d59b51970583342dd5cb2aeca9e46aac96b4132f2d4b41f3d73dad96ae484a129f7d738f3c3b7c13234147cec35566ddfb51e28fbbee35a99079adf0193444c65a3f3829901c1d9089119f6b2b4f9b0bd837b326eda45826fe576f50f41960aab72cdae75231030cb82e7113685734f64cd7430df605c93aa07b76139c35509e6c2526eddb9be6f74e56d3884902c71fe28b86a73f92825964a786683f90eeee947b7cdb52df56ef88b2ef26a1b81fda42046a845e8da533b18dc5d1066976feaa51e93bf1dfdf2d0c903722ed2887c26f4fc5333ce25c5dde53549223976941478cf84e18fd41397f5b08f163d07ddb09db07e32ed9af13b4fff35b8fc2a0a40c41d8bf0006b7b9d5e83958dc4ee762467ad71ae47131cafe6715f44252aa95b63304c2d27524a9339d085769547ddddef7d1acd1bed0b37e64a448d70c2ae4dd8cccb928202eabf96306b2aea929c82eccc55978af5f95d4684d21e292a92ca5945e791ccc6a782c56f711384d64e57a7c72acf24291e7570de231b755aded5184a89b99186a0f1dfba225ee0a5a3114557aad479a056dee4113aa28b12db33248e233c7a40e1da4f0146aa9a76882c2307bd41dee7a85fe9c52c1892b817e8e7a059a4c00d21eb45e3b9a854aadd54e4672e09c4453ff7c6a5293d7c2cb98702d8e5b77c4aa499f83569215482c838f9f6773bb6dfbfb1fe6795d7453632a67d1cc5e0da4ef73793ebc7e0024be421782c713381cf9cacc82bb65214833784a8bbcc9e013509ca426e96ec1957d3bb86920a570d92d3451d0ae1372bf6cba1a5d7d37d927f0935f9a609842af5e4df726eaf7eaf5b400f96a30a0a9285b7005a7006303c43b34cb36ed78f3df647693f712328b30b603e4f1b7c853e60e666b73637179aed678d2aad01c0dffcbf978321709d711f4d76c35f1cab61ca2a7435193857219e4ddebadb8a47882ec11752da14f3a1cfc0687ea18566725e68a25b45a50e75cc270799aa9c14965fa3da6bea0e540f47ccfe27c062b72b88b3df8a7e45cc9244f382163e62b354930d389dad06980bfdb9d6fb2c2bc9c486abfd3c84047d8e09ec0f5f2a921cf9d0f7c76ccba18db66fc5d745557a309ceb50543882af0ca783a10a93171bbd64fef5f420cb1f4ef9248fc30ca0abb9ac184831920e7102fd843c2f01e5fdefed39a1704a1ea08b5bd723dc1bd5fc0bc1b835d17bbf4876012e32e6f5b28c5429de2fb7cdc72f3e2532d917ccdee16223f4dfcbe85e51eec10a8075e04dd4b48d90607afb54c7c41807c194d3e541022ef104e16ada1db3eaeab3f65f01d7f5520e106fc344c9a95a7f44bb79aa3cddf6ce36ecaa3878c5a02d42b6ae0cd8613284c2760caa97221d55e07a5307f0e27c483f30462c20389a4dc2827df90fb9ef9f09afd2f8f18d1f8bf0575968ce2001e3d919237974009d8c9128f91408de8f9b401c547e0708aa1a6ac9446106f7934424c3fdcc84f156eccd8433752145a96b72fb63cbc2479678891ecb6fb47e61c739e4fe62311b2c69f053ffdf86b4c82ddcb11203580276c1eb4504016139d59905d27b5acc5ea17299cf7c87ecc0f91606abf8aa2211b2e3a0a0a3e857fd479751ea95b31f0ee6f34fd8eca0956a7aef6668cd660227d603c6d09172eb5c3c20e3202e880fe3aa04d582d275306c8add9bb95ac3cf50bc5d61df88f8f581f648d23e00950b25a26e1e644f539005d2287b3e1634fc67e54cbc6c180239db7e79bab9baf16afbfe438650e5ae29696cf3fab9275a04bcc3d1002f3a3cc9426c378d1d017db34336391215b8439735d4500ccdc21a23fe6dff469bd6c311c0768ce9dea2869b20e6cffe4f7017e3d29176c60e0654a4a3b001b70888e8e1be3aa579030c2119f813f7dc1bf1e3d707fc269ce9eff2f2c9ad1cdba6ad35ab72974a2a7b1340285458005c68daf33e0cba1c6a1aad06c376e20621b5d2ded619b4b278ae49a82b88ccd2a760f0c404ca0f307ca5a8052da0ec88b757b80c8c74fcae636cdd86df2889a78426d138a937f19a2fba1740fcb7cf143f1a8580807ac3fead17ef433b15c6bdf5ee54691fcd2164ddb4da264b50c05181bece3ed7fd472e64f8a081703a6eaa4f633c24e64c9931f4a0384f97fc48ad40d7aec7d2e55f57cf0806b4651a5623adf15ebfa447c382ec181ed654390a9dcd893de46f410f397da6bb1be7f45056be80066e96a520da4f0c8dd79c74661288df174ef82c0b1fcc9843fa63c1139572617e8bf756c8d24497e3910fc1e9c40f41e3c7ea0549d86e28bbc0d5e0a76dbe0fc9f05e382623aad788beada56ce601b8c1abd689f662ae95560862a9e424b7f2447c0c39ceca047fa31701f8c0ce50cf73d750c4a50b0d42a3abc300c6eb47681074a14207927d04cf8d7f499c4c577ee9d88c158ca87d45d45d2ef4017ef84c68f2dda0c74d211f496f33df24e1f2d89994673ba252dec361f55c15f5b99a74890c0b5ed3d2f1d586f7acf21bb1ca746e73d16b94f3a227298cb309db343ef5733d44bd8adcc7aeabbb089312fd76c28f124ef80e6590f9f8824f87575c265f614e3590934c349d2838c58bf5b3dd1f5c214a31283c798a1088ae8330e506a98535f19220049879799ab2f8e507c48f9920b356d8aa700e75639ae2ba0b326ce06fbc1ecc4afc9bc94a13fb832d45426c8b909c7bc08afbc1bbb26fdf78f031e06f52bf335ebb35ceb6db57214db6ca8bfa028077e935c048bb17a41f2bf5fefec156a7a32ebdea15bfab21fa2d6038269d37c19a6abe6245b946ca51203f2bb7cde40cc174ab44b279f142363e0ecbc88205814fb9361ea09fb1961c4e3c207e8ca85e4a1f74595b26f1b6521d6389fcff05f4318983167dfb8b8fd47197c3bb81a93de2074123332951346dffb9632630610bc109f390a83e3c3343954a73829239b5a1a2148bc7975044dc27945a4781cf0022a2438fa7beffef1ba0578c11fc6311e3e3f15b8754351a3475a6db93a2fe40791d2ae9fb8d5f0cd9c6c895365caa72f6b8e3f3f84588dc225408a0116a01c32760693d54d81832a1f821d1ff9470920ba879ea126ebf29e8e950c6c24fbcf373b6fc9d6c9305f32aaf14743734dc32e9c927c49fc5c2467288f6c0b0e03540a084f5bdcf0d7a9b702d327f9b6f23f6f91645c6b4d2d10622a0756c6e1191ec6981b653db063dcac0f0d85558b40a6ab732a01082430974516ef704a8ba86f88c9114f080c3944424f676b00a1ea8c933ecdeb7eaeca67c751275993bfeb6ed55d08f8b5124be225075ceab64781417c7d1c4974a54e019914a1bc0e2cb7b411928cc469e2c76b0505d53ad43149d58e58dbac06ab342c803e484d4c7d5876e91f1f1ac6a04387c44c48f1480238b9c67e7b8c4824e6350dc9cde09e59d12e583a5d460b4af37968537049490a55ffdb21de4b7cd16b1875a3c380731abcf08dc6f77b41906c918a91e9cdc7b6b5cee5df835c9aa5e01c3a8cc687b74bced277916a53de521d051bd9eee7979bedc92dcd02f5f37c3f1d9f1750f9a0609719d105bba4fef047e3e608c9f248cc63f0c015790772c5d826842963557593d6f190627a7a0ae3e7115659e67d3645b47677804ce52f5f35e6a5181ef5dd2fc293d07da4a05b34d19e9282c96e2edd203d3badac7ef0f9e47b77ba243f92c121257af9296452c67b54b69830ce31bc0e0185242818563c21445064140a7570c64c7e4f0378515ce828e6584a6909ff3320c6596f47a4249672bdb443f341a8098c5fe7a8bc59cf964ae59db3c97fca25a9226f4cb81a3e863319ef0dbf09f12fdefabf0ba3d72e15812ff8c03d0e83007c40171f083b96a00917fb132690b533ae2f463e5634a68b726457c7983fc8c45e946cb7c93e71c0846c410d0ae389dc345e683777f7011a984502fe309ddd8b20f92e38b74f6aa1bf1125bb5fc493eeb9163e5275060224ab267003bc89ce8717e6e46760df35d97e6450a6da7e8d4baee93f4c40f5d9484b50fdc57a75ffc6f3c79dfaa8f45bc02f1789b562547805a49e8bf29196d44f93589a26d8e4e7fa3f003160aed360b9d5229f5bc81b3f388d4327f45454a889072d9527026b38d4a8e4e0b81e873b80367c476c9f0459a319b9443ef5121ea77552cc98172044fe97d8e3853b81823d1b570171bc5f26f81ef28e7c36760a12fc3756dde0ddc98554cba682889784dbb495b5e8fe4dbb292c3840275e052b8df1a134d464e8aba11c95463a3408988c0687dae6c3872ee67eff3fdcbedba7920c2564a6f988017145cb542d5e8dff767f51798d6f9a2ca41e51ccb45806e069172624564cdde45fc2fa5b0217918cccd863982ed6dc6a999a8fa0140dcb0d409205a1afbe034b568a520e49a838cecaa87bcae147c3c8f728ed440b730af11dab346c8c9759c3e9d21f416bbb2216f66a8f66a988b293cf747f0effb54b50de0219435a3e5da1dfe4a6279810fae1f06f12f97bd2e413a20fdb039fe88e784aca7268ed7d60cb00fb5cc745139e29cd6c9df3a7deec9470239a9caefb43666c40e087755ab2f3ae02c273a0f32329da955cb81318b73ed598abd7ed51e797cdef33c033b37fffc14ba7c1873c75c8317412c251911bf0389e105c473e66f4de34711c340bb9e30ca7bb735225425f2a5d1cf183e80bd222bb1aa346eb07da10d82327e4850c691df2a7559e0bc8740f23ea0dcbd286f756d052af27e8d88b272e1828a264a332e6d798a768d222e82a212a581a6e98ccf930c41914d2b49db1baf2cfc68b6926b0d3662bba857696128ecf6b4c8dbe3b6ba19eb177f9b016b9954dcc536d20b36ada01481658956574eb5ac4f45c00a671dbfd00b7b30a12d83ddbea27468f964ab9eaba902193eda9203164c3e6e12d6b6216467c1e92e63d361ee31e75b68ae9b75295ecc0581e81889cadb782058d4ec2ee83f1466fb3313fc240a010039045093e8b09a8ab20547397f47cf04c39b9c11f269be59c7b911dddcbe9d5714f54d8e6c48ebce88696587330c540bcde665f538930b2a1c1090991a072170cda2ce0995d2f643435643181036fa8f3d86690f4452e56f096543d48ac1db1bf09316e48d6d4ef614ce6da39ce9415b0eb95731b88cd9e6c50d75a151971fd787a7a0ab667dbae18745cdf2505c4ce7dd71de432f614c7f5af75c1ed48b67c062ed4bdf7808bbf6c5bb4f6fbbc4667efcd2b7795c5b98b5d50e4fbf2598e9fb03dd99ee9f0443a1bd00243be667a9316d3fadf79eaa3f0e885288b89aacc71d0cfc4c7c83e4fa6cdd715fa1f36c0f55062eaedde3583fc8b452acc2c31283139112ae19ed9c524cbbf582360ff405b9fe2f36a38ce47873a039611b29871fe87b2f95c9c828dc937bb5d355925a2509c24e537e10799d8bfe1f0486bd7d04173f6902bf53086dbe0cbb6672c22aeff3bd986feb064ff887cdf297efc868eba833d67bbf9d15ee97a53d99201a18005f6b788abdb631ce43971ef6d9a83e77a1ae07d5a4ab2cdb3a2154ad0044c2dcde562c1f53caa66cd5c8bfc9b5a1aec2bc7b52517f463c508bafc5f14f784b9d7d4deececd76f63244cbb499f84b3dc31b585298839d1ad84b9fae73ce8f7b32be1f2bdd97c5b0d42eb9924ae807d198ccfb94986d682da4cf159e367ff38f6e5c896cb5f6bedb6227f041a48700a654d1c7a71091f15c239a030461f355fe9b9fa2a43ed704322898646d1dbc58b1676a5738ed952d072dbfc19473c5955b9edbae66e621c9af2715516d908504b448b358c9060cd0b527c13074278275f4ef06817e413a726e333705f94c060dd7c5b60e833ca5dfd53c92c5d9626ee93d0ca556a0c51b7b42864ecbf2621e43c20d419d07d91fb08179e1d92d487a7ec0acfe1177fab2d89d62c719927bdf69c8297c83d46b902932b9b3b08092f211cc8e48555d505dcb8fb40279da7af25d36ec0362a9a2bf5758b840d7b75ad31da4c9ce700c79f652ef3fc11691f1cb85cd5cc862f276fddd6cddda9c2acb1fac4db6d9f91cef800c3cb66674b6c574298c86517e9dda37a708b5626d7cb3f0b902d5b79659598d08e991f474a2f408bc71ec2efa9c8d353a61963438ba57e01819f861e102cc04def7824ce28fcc9bb362118d23e7da605b1a7142f68e9ef9e6f07e39ec6f7cd408ff77a5379fe85edecc851a7fe4e5437c8762928fbace3feb7dedf961c5c1858dc91635025cec7521803e3529b8474fa910b2fa2163d635442d00d20f08da5a4eb716d89b67e348ce0ad62c299b3a3e7cd05fbc153b887decfd2c278dd3a3474dc25537d79ea2dd2d89a5a0fe79dd6bd4760a5cff3882f8ccb4a265cf850e4e04e0a9ba86998e1fbe74e6c76139968baa21a4405e85e30e8082574b156b8e129dfc40a11c42f57978f4a6cecc52b7d0d36203d5fbf3e0d341f2be0b0a16aad93f882f7491044518e251c2f3e11936b6cd04f98de91b8af3e4b85c2105c028f45bc7289ba193d9063a91f01c6a51b4094235c2730983cf749476e6cde6ec2c589f960aa3f8ee24f18eb3919e6390ffac207ea3e5c5a6b17ae2b840c265481af94d72221f95f1400bc0b1b962607fec34c43e8cf582dc2a4a82de61f8f2de2cf872bf1d27d09647d67dfcf7666dd758449b22a20cf00671c1c7e5a5a678688cf9d10bbf071c573253f91b8594a6cf840424b85445f193c82c94e6f47f78d91552b3e824c5de53cce4f807f36411f3b3b1d195216f9148825151690f386cf75bad108dddcbf86f47cab83ab5652bc35d0cbf3214d8c45616ec8c6474360addfe01b33fb4eb97f085d95fd006be1a0987686e0b1fcd6c961b5f233e033ebe5d6d3ade8a5ed4df5aa18dfff697b3c0d441413449894f7751f54bf7a0623923f6aa9129523c73b99699697b1347fc09c50d72e1ad006cb796ceade633fdf0b3d7fad50240f1ef15396c369f259cd74c9675ef83cee0fbfab9bf56fb5ceec56fa43789ed9f24f45a37caaa398de349f7500fcbcd353913795234299374f16c1fed17feafcd0ba717af25fb93af79036856f170ee18e13771cfcb019c7b1b91263fc6ede3e0b7203dffb62192135d1c0074ac9ba4ab1126b8271a880444771a4d1c9243030e029a7c636e327df4249a1b37b98c43c06269fc720bfde292617f315ef78b6ec67f32f034b9fdb3a12fc5f6d3977e4662e8af6941c71eb0d474321a7663ac6c3f37fe2ceb183383ab0c5701771b99678fd7d6ba7da593b783990f95af84d1ca6cb95d0f9d764a9be731d518fcdeaca1dd1a5f49f352f13e7172f0d273b40f77d2ca8b47eacfdd9d1c27125f661239ff1e254804a95ae757ef9c85dc4dd3928ebec8e9824f565a41d14d783d61184f199230d64ef95d27b15dd5a14c0b4db15b8a6df7352c6b782c5dc57147538ee7fb2af9096e553fca00ae5c33734d9a36e3e2c0ac7bbbd5600b5762aac6ae395ef9139fc2d3890a02c66b85658087388f3b2399299a7cda76bec22742ab01055ed1e8388f632f39fdfbaf80eebf8ac1b136fec7aa02eebb05ebb331f0e01e34259d293a7104724c8c7828b12635d1cb3ac0585ac0ecb4a6aa69152cb2feae0a43f79b731e73a79bf6daf6772989873db3083794faff695a140a1daf7fd09ad41c5f0a3dcd689b802ffcdb4db0227745180c5f6c6d9418092ab70d9f875a51757e3a6edae7c6cd56f75909750fae1ef8c771e02370e5173f6c1f019175908769b76853c703a9635c26c8885da3aa49eb02b99327d8627eb8e2e4729095375cd52fd4360f5c5ca9fc5ae1da3864439a76c9ef433a51e854221a7aa746bcef38912b41872815d610e5ba10e918777a84b6703c4860e512167a55d5355e2ab84a0d41b312927c31df6cf048069a96552a72611db810911b2321bd20d11c0d1f8f25c8e899c582c1dc8a9291c3f055010676264e46c27b24833ab8a6b8afb297c2d9111379c8ac34757aa912e0c13319c1f5d53a9fd5704db5a05ed18a90cced579fbc87c3e04c125e5ea79638486aadfc6baf71cd5fb8ac0a21245843b95a753a5c6c8b2cd1c0bd489cf0e42340d19bbc997ec9d38deee799e3e7bda8dba8751587b67deec40f6f361bfe290c841107368bff267f57a0e6ec87119b4de23f622e855f923b7298be61f9632cde9143252d7f46441aae9e41869f784bcb78a50c909a0a2dd498264462f7846d37cd1c6fb0ff3810175a8331aa0003730f2309bbd18b6b51e4f458a641c68dd39abba7a1abec334b48e3109bf424401aa850dff99c37db87ca6e995384ec0a1707d1db82d91dd7e7f3481fabc50b45032bad4e84a27e26f5331febb74a71454615823215dfed37e99bc301540468d8057b82e0eec7d919775770f4eee54cb9c140fa0e6e52e0d020e1b9a8565a041f32d519ef34f41b7ce9dac62153235b3f040bd2a5f2269b2295ea8e9c435bdeb3420c56a81a1f5eb4b2caadad246ba70dae5c9ebc0f6b5e3d52a49cfa012831fb8bc013b912bc372d9b2dda211dd7e7cddba0b93dce3252097378b623267eb22eea9c492b5195fa6c4b2b24a03b7618a9a44b1ee017bbd64c747ecc672289141f46fbcbccb7132b89eca23add6375857936b7e8d1b8bb27ac63b24cea7be5be61bb802a28a982a7f7e8975829d39313bbbcd60915ddc5364c72eac7274c9471f869b40be292d903d0b1c0f573a087b11f281baeffb89eeed3107b86f05eba940bf256a3b6d97937a3e1d0135a5ecced4b1eca5a5c650ac5867efea45a565f51150a3ba256dab0c35713435ba74634d0642f4463cf00ae09978a37d9ca77384fe56c8fa9fbda312b8e47393c11800f79e9e67ec01fbe2fdfda3b3f79edc13bf7b7215481bc17883f26ff0c2c3832a36c348a4b0d82e82d5fcd1692e6c0a47a6acbb5cb95e6d796a8efda32b5bc43c250d28f3777335fb78f4c0bf4b4ef82b0cf76fc951f4b039906bf8a4ffffae0ca805c55b31b1f6927e7ce88903c323ba5771e261d519c09a0cc3285927ed814ba62d0d85455603445995100fa27ffc8cd17b73a82329c322351e0459c460ad8a890c5b7343c89bb3a286b715b205eb7d131cd2852e13db7531d258b87ccc72e823bd1f42faeeff35840230ee7173eb30c61fe812dcf6429aa1df1e63d7825ce60e47586043d0db7ff2bf198d92b017123744c9ee5243d3593addcd07e81fcd2a4fcf9a50cc1483f00c87fc4c401ff3dd77ff61179d383374e28abb6afe392b8989dd27870fd6fcdf3d4ff6ef48c18919dd539ab00782a6141980740e381636d9f7fb65cf7b5c0941efbbf0cbe3018b415bca5f9aec59d187f92137b3e88482d8560fc1229637402b1285b9f543945f9933f16041e926a6a7d5bdbb16b2c3368f9ffd82d10d728631c0c39e9c8c723f732af660c5eb1234889230a1bfe00a272966b10c744d81af07230df97d6cfb7eb7f520326385e6f3106a1a58e4cf72d7c4199c8ebbd2dd844420722ba73526bc23acdde2cde7fde4e3d74580b00bc320aefb7cb248599aa8f0028d19b1ef8ca811132b0625167e4568dd4e2725c2bcb8a80633d4b06b9cacd68576bec2f8ff83093f49a153e9862832bdbeb5e19bc6049309b7f65e37f14eac75ef52e627d93b02950c7b66e61ecd47fcdeca6780f6764ac54f12560c77f5d8f44f325f7cf84baff2fe71b8df3e8727e430368790eebc8350f7838aac0ce88a66f719ad31a4d38710d4d73d39df2e08bbafa8dd17f1c6da7fc54eb6151b2b288a08621d7b3bfdcb9b34fa29c5fca6446f3f11219a5868793e7cb95f333c355ffceec65826f970241b1a243674872c814558d36ab1e88a2497543bc3097a4787f815d53c644db2bdd666e398d8fb60837808c83cd4b21bd7047a777dc4572f713d571b532fbc157582304a16cfb182c218960d9bb0ab61915a6948858b269de959123d86245310c9d026888fa471f8f1a7920c593ee5154171cfcec67cd311d3a969f31c44c76ce78f3c492487e0014c4b9205c65bb44e3df5e74af32fa3dee324eb2a50849c831baf085246dde094df04e16b248c3b42e53f9b56553e1d939579fe889d7c81b9fb19920352ecc4f72c43bfb917ab98598f5ffcb2186827269e9a6250d3c1db83d4329367a80901244440e565b5d367c6aff9813251e8f21be50c8c78033c044f4a58fc9411c2d468d4641ba41916944a0a0967c63a542b7a9141b131e78f49afd28e2b3f5c4c9cb681a199f9e45c69da8530cb66fa9c611a2cf6374009b1238c51faa8978bebb3412c0120325d7a6d94cbe44e1932e79390b5a8c27cc766d5ec86a78fc034a2a9d6e43c31f5f0d7bfc3a777967f5071923d6f3c6e6ca0f774f753b8acfecda3d54e15108b74fac5b28e13b10b49b52ab29b6e45db67073160c4c04b87bbe9a92d2b1d612d870a53e8c1fef9bbc38868f2dc7f3b30e663003a90b9c9d5871169904934f394af82ef3b3ad22813f97975d8162a6e9beef15d52436f694164eeed05836ba3b86684ac203598a67b0a9c081be8a530b89da8b077ad6e760713f5228a12b62cc6c8efa95965d236e30a9412f97525795635a7ad9b2d5ca157f907cbbb0d3f0339dd75b79f3bd9272ca0c1ae4180ea83eddd490d8a248c9dcbcbd84328284abfe162ec49a903af9e61ddef46f416c0769f319b762c2976ff426448dc4a08360ea7f63f393c8c54996c63af9a2557b61bed7e243475f49d6b020daf72cd596f158ce4fae517b821e2810c61a6561e0c67c58a48c1e8a2d65f3a42be4b4884c8ad35a5298215d9c78c37399908f914228283f4de9ff03a47ef7c7d66a162d85ae853920d20abbfbb0e7649166aeb51ffc17798e7b8f5cf75a94756a30b5532275d5768d8dc3700847844e707acf2163b18d161d77d90929e2ac1643425a3da39fea0d211ee4f52ff74e0b602783a8559cbe16e73b65c900cbf688a4d452fc87a1e1b36811636e5586a37e3ace6f32714970e4cc67d86a50fd5a20c39e22b19346d520461a466d6b4fa52b2fba29bb24fe6cc4a2de31c8dfbd6f79988d34736553dc2ff14339ec63858cbfa7f693ab5001e40846172fcfb6059c8fdd3dea8f080804b27ca2849dfd63c9781572cd82c09b40f067adcbb41755a8f6599d419a844059cea8e6f8fda0d1e597a2c52942db7c6c59efc33ef3287cccf2fb8746b4905f7f40c964f656119dee42d168e7505edaf543eec29f1a07927085bd231cb20b6f45f3bb6f4d702e8db76e7478edfef609a7e7bc6fb36556effb5dd0ea68232782ed5d006707fa11e6bbfdd7729bfa8406e7335b693e3a4e50ffcaef90b3858384cd85b0e03697a254ecc4bda7ade8afb2b2605d7bd99f4f1e9165745d6af945d7891d3f9fbf26ce664e13082c7bf7975c01b17ce3d29fd889e2b2cad1ffc6319e825c8a020fb11bb63f5a6d38f5eba0f30853ac0769339f56b04d38ce676a926e0dfefead2cf7c68e6831d5f93cb9ad12f6c6af902dc8b469c16ce59e8d0be835efbe4da22f0fadeeb0f63e2a763f70f09554fe4275d69faa030ae4674dff352d8455007438e58e6749bb712d6728091107d87423ddd6d2a6002ae14dd4247b9e53a6c763b02609c026687de250d051c63e28227e2702da225a32a9e7e0db499f51a367792e6d3603c2db04ebcbe4bec7ed197869ce4deca40bf5d232b54f6763d88053ffd30d85723dec5a3bbd7e6fe3accb283b267141dce8fc5c01f6c66ac5b49001c3c7a5623ae8774402e2ae8c25860b9c3e8fc957f0b876ce8fbb415d6fbde3c296e37bcd124fc73ab907a02e62ee00c10c59c4dfd50dde373372c1050dc93fc502be87ee0a4816ca209fbe7f72ff94b875ce7bc6f1cf23742fe9b58b0abccec3d1f033a3704b820d1657eae2e88db7badd8b7d3e3aa0a8ea294ddd5fd84b49c19628bd74156365d47964bc6e6688ff78b4798049bb425ea6d1867adc613eb3d95778a57fb6b31bbc842fa238596cef0393fd28818e20660698407dfb1bfe2ef5b9c63bdf51b0caab9aac0ba0f0ef4ac6bef12ec63dab7d70f15ed9f22fe27ee31facd064900a2f3344826dd7a17463dc0792addde71725ecae8b373745ab61f48ce160017a64710434695c4178795fdd6a836dd1bd7eaa3bd479a941d08a2a4b02ec1ccf75b49804087fd8cd17e3286098e4b3ab4281e7e4a4c2e0747e95b3d7b19d9e4acf5aaa40c9a2e96122e3683fae7f6716b7cdfc8fdc87662d023b8a847fff44d02d437eaa9da79c2f34c25fe4b8c3fcc454601190fbe8aff704e71492bc22f1c8a46d38bf079d4d7d9115da029598006533adaaa4d5012b44559cdd23ad52b5280ea763ace5bec179897f26e4bbe487a15384d6e597d5e2af5bbc5340895b2a9da29f5bb34bb148d38ce5adce1a64ecc1e175c7e711aa1bb2616b2a052edfb267e95ac5b78751e9dac6b54497d739d44ae546b58306326e5c1cbb4ad03443fdbe01b9c2a47a63ed38a5ba7faa248b75596034a1fe14b541b3c10491e3c7c2fac09b3f87f4bf73524fdeb815e2afe5fbaebda600d808f669b3c3d48da24bbf20bc4b4cabba180df386185462d937904fabb724df40424f1bf5dc0f73ea1a83ba2825127908b40d41b699a40011423011ad3fdff80e82b178bf78d68892762482af564bab8694c0f11dfcb3b56d8527fca1bee0fc5fbe4f84ecbeec2f0728f76fc32acc1ee8ce4e28d0be2cd118ff92565b48c7e1efe1e2c7c2e2777b3c8c26379e47cf0533cfe8cd1a9762070b996b31515b5cef7c7883e306a82d24f15d8d158e425465647633d2d56df429d0fe4bbe021becf627ede6173c37b3c599ffc5ff91a1c898805d5e85c3615ab3d1fde84703c37b9622b53c9d661626de44a7f307387be77a61c8744dd466217c45e1e44f24f438e53ae73b5707de9feef0d546780c38c389369993d70021c58c01e2b8cc09d97cd3d7b47cb26687c0cbee2c98bfee08d49283021af06752023f4f523dfe901a1d3509916f3fe31ae2ae21767e0d11a4631702f67c25361afcbeb01368da6b02ea98bcb2433706cf9bcc5f0b4d50300a5d343da6f4276e37e4ca57d71ef41cbefa9940b34e5435192a9b4ddce937ff9125511b7ed760b81cc256cbeb2443709307ff55e03127921cc1692a85eb4d0e32a727cd7e4c762c2701bdca85ac3840b3a19139f997dfc5c5570e5fbcb6299d64c537addf23121772e8f46e18de100569f268978f5704c7032465adff57af5d35db80644a465670416c586a65e696de798406aa3ac1999ab0bd814d41917c3cb9363ad738787ce6425a8245882ce430f5e3a3d97c6a065423eec70f644364dc53fac7747b027af5bf860860ad3f49ec37f4b5e166abb2a6648db6a52bbf0d7812b85f56ede61373e6123631692235c96af8e3f6a1568c7e4bb5b2a27f7c7d63e1f91078b85bbc235b156fa637aba45c4e8b7bd1f78fba9a00f98f37732428e0d88626558eb23e628da1f5cf766625daa75602ce83e6d5d53951f6191863e704c6bc26a5d31ac759cb4875d6818df37c48611f0e03c8d90907092bcbc8e3d6a49282dee176808614e099e589ef788c82f53a14ac55ee8cd60b38df2296395467b4b11cc4d4365504b71e9f4018b5bfe44018b3f403953356d01d1eaaa720a89a2f0a8e866e6a402369586b00fa8aed914153d87700e1c9269d6e40e701888c4e3611d92f6f407c5651e60438e1e20eea0f09442cdda2721025ac47135e35ac25aea5508e1e6d408d8704416992d3fff1c6cf2c75d1c34120910b5214bfaec8e310d3606855225ac6de33ae44ab80d301b7650a4e57950dab7fab2c04cf29323ceaa55aa1d1b71b15c2161ebfa5bce88bbb62a8e8f0a2008d95cbafd2f58b13f6b53ee0f5f8774226406fde8b9f2035a9260398059ec2a4afc67ad42a43a03939bf0d533afe4537fb3982063bbd117b1869a537541b6146faa08c5b4acd8f42d364ede60fec7ecf381c54cdf20d941a8f7d3dce9af9b6fc655b4788b191f18456af233ef58683a49fd26880af91b5a71d5ac4b5a58a57941101e8bbb6782d2ce1756463f1161f38accde6ced09505c191d3192ea96959ba1d0cad1b791b07436f7c4a7edd7d41eb94a4d1bc88cdd5a005b6e34f2977944a31b2d166352996544fd5500a10bfb71ad848f29d7becb2e264e7d5c8d69b407604fda8f62c976745788a9590241ac7e7b6a78990c3061978adc607554338bcc9fa96ca7ad94b8b38556e1ac0c7b0b374612940d6be83e71bb44099bb9f473c17489ec19dffa2bd79c97be167037ca18b96e7cb82496cba90652b57e9f89a45091269746590cb054f406fc8fdf6a1681baa54e6e113e4e10ceb49d8b4addad784ca4f3d303a89e7ea8e4bbe0d544d6d9c4896980818e0350d3433287b2408a67356825f8e31f864be8df246643e351b1849b2423b9ab5f1a29ce692a4c2d5041770e81f2c69ece00eec55d599f8ca1f05e284d7cc2b905049836ad71a02ee020680025e4091f689623bbbc06a26d87afba09dc54ab46cbfccd44dc26258741da5c524a1c4c35f32254a82923ef500b14471c844da25b15968b5a327d8d0524b0860dd55163c0b168020216cf1658025e8e36a2821e67431f95ea9531ce7ac7d219b5a9905a628a209e693c28b511ab98d790b835867206e565d2475fad2886006654611f6fa2950a4e22c98f311290b9b357993774ec1e999473c3ca9674f34d7194733fbca5c9a91b3ec5dcb1c1f310ec48c74ee8975074cf7f34ab49feda228e10e8cfb927a2c20b70843618b71c3f39b265da821546908daf1357c680051364f6fb59b1ab8aa6f51bfc909114fb119e3c3e9f7bfa056051b651c2e4eec061e9f48ead5f7edf62bb4da28d489e477ae9a964454929d2b9b9a98c356e2411af3bee2ccc0cce9e3def257bd98dc942a61d5cf20fee5ebf6a9518ca230bbaa48818b098e211d2732383f6386feee8fd193b8fa1eeab98ceb218fd02f9ade5ccfdab6d294356f8762a08d2e8935e2aa124fa4d66f3f47165f35f69b030918313cc0fb5327f28d3551b9e4ef341adbc064a241c33642b071499cff9380b5a53d098cd453360985a84d51f8720a04901bce62a73cc97eea468c95681cd9f09c75f107b97787d0a21f9b076021ae7b0f4a8dfc29b8db2ed7cbf45410d0a2438e789fef09a4b707f80f523ea689d13c4ee01a72208ea974c811bebaf3951e928ce22cdd1dc42933630067771df34dcd52b58037a8ce7b6d151252951df35a66e89feb1ba988e9f7b71f0fa2b3899242509a5e01dc4f01942589614b7dee21b1a42d5d78faff21fc785cf785648ed9ce752a1f7096db6c7da2facf57f4b25ecb257174d34f74dbd82b84e334b8c05878e5427fc6835f550e61869423ea253cf233b4385f228dea20fb93c0cd370dfaac5df3036797d21af38e2514d90ef0d7eeceee4df4c2432506c7e1c730978a079b1f57c8563f13460a802337aa37ec0f38a2ffd72a36e1251e99febfb2d6ba6392d8398dfd3bd974776f731e4e064f5c79cb2246a38b97b17fa7fddeb8b042f8bc9e7d24a1ab807cc32f51062afafb560caa39f7cfb22d10d58d0a6a6b48eabfce3fc4792a5ac6c53993a07931a1517d4fcaee1a8c326671853493728f336175432f74a5e7d7bf285abdbd1075863f8a969bd6ae388a59185a17a0b726c87a828cde6859cf19a610b1f37ca1fbdc08b00350a4ef49a8e1507d4e749c29c1b580abce411a5a80d663901598777223f2d0069a3104ab6edbe6db6f127a72cecb7102a759ce2ffa3d042ebfbc287015eafff100095d586a4fe9a57164c9d0404c240ec5b7fd2170a0553fcf8cbda0ea4bbeabf0fffbb9d608f8f6035ba32da635991ea3233709b764937425a34893bb89cbea9940fdddea0c006e0692613b8934cc7951c7d08f4d83c9b9a72f05767c788076432f653df342832690cb98ec03d532754710d792bb03fc41f48b48e0f38efa0c2cc33343ea13dbb40e05d44cb7e777fdea8ec6977c3939ee07cf32a389f7f01b4b8d391b0d54712e032bb2abea96152b3e0900ecf977c0532266096360ab566956bbca81492914319582fe7d893d72470c9a31e6093728143ae30392366815f9c43747da68a5f781dabcddceb1594dd636726dd9bf7dfc7353fbcf6039106bcacc930503c598c3e198eeaca833b99cfa000b0e5f70e924f31a4b9b06fd21a4579a5579e599b17eabac0ed9490e6a21511724c39a7e668d062d2742f041e32572d491b719a2b0a0f3f50e7b79bfa4b566941fd1f5efead09283bce3d7a696e2748a4a6a148b8616dd26227a238496a1eb8d5da8116e7a3a9deebcbb3f91ce7e69cb6fe39497ddbd1e73e51e7a3de9cdf5aca1f64b1925ca7b5af52de85bbc8c83cba41fd9bd358488467313b5af30abd9a7a102ebc69ff007c1343fe0b92f0ed219520f942b18101e22280f4a2cb4be20ab801e683b07566b28947461ea255685468d6be8da8bd749ef31e8f72f5d482ab35b0e3e2ed85df5da3c06e21ed149295054cdd983efbdcae5aeb5dd8cd29082eb76e5ace2160cb2ec66af26cf8f57be14f2db909fffa2857f33eb4f49c485d5bcfe8794efcae8d5287542077043597a7c2ac88baf77916b64058364d1883a10b35081ebd75bf589f56df211bbaed5238a52a31a81bf97a70a02241162b3f188c9cc46fd8812bfb38fa212ddc2e7e0f0f3236c3340ebd7f3d22e3c59381c7f283bfc22fa185db96dd0b2a8548560a4463556d8d4347e42e264e03d1dad082416af612f782742340a053443879453449bbc22500c100e07c53e1010c96b7bc310c5407b827c6fd9306c401e8a340c12a816937ad8ded72ce0257aa2123e4cacd16998df3798d3804ab7bff5a8f4c4dacc76fb794cf8a71105833ef35805c7913a50dd78372a61f79d163b7e98e511dbcb5aa347c80b04773be5d1ac2b0fb9b46c496bd01feab63f44c9731b34655a26e81dbb857a323db801872330dd39b064c7a6187f0d6f1a804de549e6336bbc137efa75d29946a678a447ea8f69bd4c0371ab392b77c45114616b5c8d5132ecba0d2b3bd6debb9c60c60dd72cfcdcabc3bd52df44380c692def3003b041224be775e2f25954a8531b8e02f9c6d7a23df1370dfaae590555f7452258ccecbd471ebfd322b0c27e07605573424fc0b3d857e2d4463cc2c398e3549bc89a99f6358ca97f2e9258fd6e8cd8e66dd3c9132220b1dd214dd74b74c6e3299c6547486c8bf75eae3013c60975f547ade016bf628eaa6625736f1816284bca08dbf8deba2e5aa7973bcdaa599510b2afa7a4a09069686a75cfc97490dd159ba6bec4ba574fbcd45a533103c99a632f6baf0d84613bd31fa3d542bd6aafe273d07808721ec4c5a938b5f3b30697e3f979fbcb76cfae5181d66824a83d560cd5c98d4584725bcb2ac53cf01e407f7f646a3f267b0bbb94aeaf791f357854965e3a574b16807ffbc9ee17936e7576dd025cfbc125759864657e6c4d9ac330efd23f308b7112167c3c899ba9d169ba2cf84e395006a872d7b5a628c49529ac1028255b3a7ed3cd2f697a3afa92676182ef59b2222618554f16432116068b7e7dde2a6e1cfa291dbdf8989647868b9b14419049700d8dcdaf867c794387e4dcb1e272f9f49b8251d3e7552417ef0fd5807f7ef38720c5161ba1dd3a187c903f6ea0ae50c7a700d4ab9b3834c9c7aaa7ea78d758df7d8e562d5ac16b0695ae5563a3b6329d41dbb81af17587f00333f6cb27faf3554c300c5a5914930d934b3b5b79341d246f4cf1a65f67405b3df413a7f5bc475473214fc5eeceae7a918a3dc6a53e7b55315d5598d9b0a9479d56aa143516223ee3a052a7c711618af67547680fea3c278432973a37a8fc56e12b695aa1828ef8bdb6be00f89d787558b4e9597546212ecb2a6d72239a9463c93ec5eee27759e1a19be9f2cfaec8e54c625a9777a7f9589a9175293c05cbbcc6ad416412d6586e89d4e9c89e74e28103620a2b087ea3af1cf515077727cfd44ff9fbba9b50672a3e4ad96dc749a6ec8ac7be298f94b90e9942feff41fee050af15ca978a4e7ca7c21b5af31f7da83014af797d4d303b99641783ad43166a2b2446ac234ee4aa30d54fff9f9471862bbdbcf87323492da19a71bb3e82093341be508b92f2924329a33a34910a034c9cc05ab53e55b33dc7425793d8e6631917fecc28d34e1dee20f233b03c69f518f6d27f4f614a661ac04e2e0ef83ca521b17e12a5aca8dca0e0dc3409a5b6c993f037a460443717762d9ab06cbaa0fe07283c0c0b4863226a8f520fce244be3c6a3297d10b9c3fcf696ed231d79f232e10a7183d5cd804c153b68b4189f9de665f3b9c9467647631fe7e3a97ef31e6769a6324056393420bb3c6c13217bf4bd34187238019b6897e3c81a6b47ef7c031a7b7d884717f18d0dda1b99510f5b7586d8562b40cf1a671acdf209469fc303957b8f16a5ab8d95ec968feb227226060fb504bd75254d60a027d23c5f50b11691351b6476c1bb2636a8982ae17ffd594bf1f2bcb7065ae6d5c0ae89268971d84fa5f7dcadd865b813a2528a88a70bdaf347cab3ccd25123c221b5b195c5fba1c53b2cdf57f8c7aae4ce3c313f5563fdf7e42e6ab7c3dcf894614c9aa7fa3cb3addd1636c8aa49a082a7d2033d0b1781ebea0f386899f592a36dff1a1ca453d041f94f64d58ec403d0921a3ffff117a727dfb9f124a67f06aa82575c3acdbf619a409461367fdda167fdf9b753dcf2d567242bc743f7fcdf5ace48065972302fd9b8eb36d9b00f804ce5b8b994b65d72ce2ca8e77de8a174fbbc15be42856d2b3c3c1b0858c71e1f616f0d565e86ba28c3adfb108dd52546ac5a6ab90942daf4000247d8eec984d99bae444c488c2341a1a84e05a9f82cfd6d3d706eaa314d4cc675255c1f873d10b2f1999f3071f3dd384e7003dc12e429f027be1b3eb574edf60f18d8d93f0536071b86d3fc29cfb1c0ed6064a63ca4f76cc5c93a1b7a6817d25ca82dac442bdd0ab4a497e6289968d76c18d3a9da7c909eb8244eaa7bcd3a9c8ad4876ff292007c76f1975721600343ba831f1175ad02929a390eed0fe3ddf3c40b28a3f485d3a2c7d452629542518d9b270f000252d9428f1c593fe22cc984936ab67f36fbb0a775d51f59d01e8b604fb7d582ba41cd7125ac875f14fc8f2384c33f196f8f0a6a4c5b35f1e12aba801021a1052539fa40ca0e8a378c8d5aa9f5ba22628179ecc701d44430e751b0f47890c5295d2913e0dd8f9134aef04b44da6603b4758d1e4da7b87db5d1680756669fd0c4238d73f3ee62a9e2649a5bf68b0b10c468bf968e7700fbb558f5607956ff34161b22898b03e57b6488c75b1f047f5c87752e3a3c8d36cfd2d46940292c1d47953f257c321e912ef897c730cfffcf7dbbdc5711335c8386f280330c7ce23013ef3bb48220cf97d6253e87626ff8e07b74943571d6a973436a47a8a0cd47826779419d8f1fdd4507871fb00d596026c508b3dfd0f8618c63308fca03b76d0378972b127df97e2c892837186822239c6f719036d9e761dc6d99b01cf86446a7d2d474ef085e74254e589065a59805c2e8042c17568a75f7ec3d949ca3369ad779248d45a9fab63c59330b7c6524b734f6354756583c378741ea65c6f661e4f76d5ff8acb47546e6d80af278f2b8ac20dac53ce8fc22588aab7aa7cd6610ca6edec0c5e4f41ff75cb1c58864f5f593c591806a2e98a1376f1ce68fa028e163e4fa13dc4c53977f01a7c2bd10b2601ac6498fe699bc31dfefc3af7aa4f0b145362b95b718bf0b06e57db346899a53097c4f90f8539b20d0483ed48ec2297183787f30c5e08a78ad1eb31ecabc4dd54f289a97ef806249a3e5c0a4ccbaefffde76cd69135309e42bc310ceb1a703a0df8b8776f5bef09d32cc58c4be8e5462234fe51aa2921685b4a8a35cde03a37ea865770a075c79a1175091748e5247b8005aa179f4f4c282c46208f754e32a989603f6cf0c2112143ff5d9c98cd7b8438df162206040dc7fa236a4d7fcee86dd9f2d91adbab205f9f56c028dbff65ddc45464b9aecb1315917fd07aa402cb46ad9260f8a65e2284c6c5cc28d5f0b791a70cc053ed3d288af8377a15d681a5b3d4fdab8f81932b1b2aed73ec4aac5b16b57fd4cb603038f3b1bb1529fa9f303fe71714d74d559a60941f8bdeb5a135449e0ce8ebf17128ce72367f9cbb048245c49b82c696d96eb984072976608961efe098cbd092c465d8ec12da1cc87dc002e7d7109b47a7ae25953ce78ea0fe630371da2154d643487cd183856fcc6f4efcc13e2e3a91ab84491cd7890f465653c61f09f88f91da25dfa443bb7d0d47e326122fa4b2ec5d013fb80e3b4872f9ccf137b8fc6c58c1a023591065f24e4617dcf7b5f3ddfa40b24297c71149473766cc7d30fc04a4bfabf4d4a33ed232cc2e7bc65e16116e9f3cd760c15ee0d571954c3449c5af7dd0dae273e4bd50a7f8e1d3b381f2baeddd3ac8f7e89e9d5fe75d538205cdc08fd3c194c6276c2f488ff739e4878f72445ccb5091a3142f60a59e38665a7a1ceb3f1c7bff0cf047b6f7bdcc66aace8f3bc47e25b0d4f3fc3b8f995fd1aff93079019b7dbd6d03f69620d420c9f541f9e93de3db5d6123e1591dd6d0e4b7c2fe729f1cdf3940e31b8465d2212b419d4efd3c476a3793d1d7eab55bc6a43f7b513f83046f9ab9efad3a403de765b29c99d3e66bcab5924451f023e3e4e0de3850ec04c3f7da647186a72950b9a7c4b84122a9dfd358c64cd06a813988b5be5d71454b8abb8d415d73796bfe873f6f8c65c7bbe9af21b60a72452716d14b67fcbff2fb26dedf31eea88fd09c9b48ce5788e2372249b298a22f39052f19c1f4390de2b49b4dca4df450d84da966fdb39aaafd2fe630555a4e216b4d9000becb8499da87db867665a01564dddbca5d7913668672ca526cd3fcbdf7d76c72e93d7c5ed03d08e3abea7a06f15e83de08244dc4f52ffd39592f631e599612ef83e9f48fe3601e3f5a63d897ebeb92f711ab311bee64a9cb9eb1154f078f580c96331679eba5253eb7180b8dd2d59958952a7163c3ba42c12400593dee7f192b9c27aa559866a070a6af2b3375af3eb08caddcf23351c4c6c9b987bac5e88656fa8c868b1b3b80e8f43174e3809109dd880abed5b63cffdab5927d417f93a3c9095268d72412f662c1c00c9a357e21ede9e2ad2889cbcb6b2a19f8ced00c9cd34a841d5db0d1b9398b5a055f34e22fc9138390782ad01ab2bfd208511bf8a4c6bcded921d00f28db6687a131730720b3865d9215ebdbce37a4bd3c4f0d44b3ab7341dbda301072dac7293d54aa59827a30b719ed3aaba611b31ec4601c3f7bc55ad042bd2ff863eb1374930fdb0120c6f4c7dd722af182493d3ce87968ce58e5cc68abfb691d343b6b4bf926043060b5c16ab458213521b2a88586be3670c7a82ed617782cca56682793be9cc02b48c1a24e6183c0e21f58b3c1a44312231d12ae9ec3ad3c6020728f92818c79e206e7583012053a7650ce57c836bd52cf1c19c2d0882ef102e676c091df51f8f8d8b5aaf680f06b3135236b8bd2ddc77645af084037c5caa4c4c1cc9bd1a9af192964810b4b3cb78df6621b29798737df05d27876452b69e82785b5999ed098ae6dc58ce30c66606ae7e5caea7f65022e7767865f1d27cf816d7fd7b623ff40fdfab4913e614362cda63135fb9124243854ef65281264f23e390e9ff1de2e22448a53362ec4e983c1729c0538ab83373804879e8a3a479ec40e4ee939729bff5ad83730a5773a84a83407d79c403082a69ec826e502585835a442c51737eae9084e17473aab8c52d346b4c6bc5a155fe978d8b2641e6a694782e44aa68fc5b3e94965e41c6673b3f7a97018549b7b018011a136e8ddbd99af677c5798c4f0f8fdaf92c896991b2ef76e36b75a20dfa8236e79b7073fe6343000394822ecd0f39e9e86e4f6902d3508761a283f7c486100f6b065537099e38ff8e8c3bc67cac1fb0279a6b4ed0b31df53c081f79e01d1d874a51c23fe626cf5fa6ca09c6f314aa223393cd842e4a17e64aa0b58a675433c02da79aa18c573e3ae9fb226d81d4958f1ae931537df44227939f017718c0864b8dbfceb76e03b847754cab646ee8dc76985adf5bcc259657c1b27f8b93fdd6dd834a1d877c81d308743a9e080ddc2759201bdc4a4b04626f45ba465a255c10510c954c3768c4bca894a5ed8815e602afac554a4fc859c9d1706b010113188eda3cd9e11d133280746e9dd87d7f845c7fdc96bfbef7852324ea16f472da5affab71943ea61ffe9c05561852b23fafaa1e4febaf206f7fc4f04e27a32e5d6156a73ff423c72949c65963035bfc702b6e2b7ddbce9116cfa79d08f216c5b6901e92a9b8bc138d81f78d7698f2398b426cacc89a3e11a636a0d2e09ba67f4f15d15bdebee2dbda501ec70d37ca5db2dcb11e073ff480e9d50171488995e46e65a014e7ef223925518eca9b4af239deefa98ffcfcf55b6551700c8007c371b23f81bce8f700bf26ef12bde59ef5768fbd66e3acd6bf8646465e5d1fc94fbf81a7fa6a4a8aa128d901f8620231ff89c174289abaa51466f30e4065a6be8c6e15e7d1583e01e78e48683de11416199ee6021e0961d91709a37c9ac4b8d3940b3ff676f83030f2182b135820e3cd3a40c06b89c10a8d622347961cce941b91893a1327308f5b6ca2760012a3957901336e6a4a853eabc3498c17b09f4e09e8bd98e297cd9279162da51608552f2d2162d1fcc022daaa4f4adda9ed8bc8f47e2edfd6e2fca90bc9ab5d23377f3d0ac40e4846cf9d889d5289644cd94e6ae23216cf59f29a8e2a2e07fd24de6b6514cd97750539a960b712e93cfb33cccf7f45860a9308e7659a90578960d690f6a5256c01567f6e7e31961f6ce79837e35fa23101544b9d40a7ea2e88bca180c33add2fdda685f89ad7fc18f558768ebd97192064b9d54b5e7da1b256064b9df8304356c10d62251ac95cf5ee30118d7dc694009409472b1e7598a59cea89395513e97032c2062bfa38979464a51f18ed961ce5b52f307455f75c0b5ae5c8a0d813c5ca7adbbca9bc5bc13ae573b9bcf5eedccb32703c56b0e889742cd06114987a37fad8de4f6782c14a7976e23e36410af7d84c4c42e156da96c5f53eab8782e4f5a87f45de36d579c27ef434028ceb9ab23036023744abbe1fc2108c1eb7f0e08d32e1b0b5e9b99e2b94874d7c213e883ef5e67daa17a821af25e564fefd75f9003d390492193b734548edb4841ed27221129da0dfd343e9d3b525ee37768da966d74549908f6fb78851a26fe594839f1224ba87ab3ca7d500b7278fffa2aa18dba8972d4878385d57f480d6dcd4fc5ecee98fab82c48eb0fdabbd1ca8175f5e118500403b5ac7b3c3ff1ae04cb81eb196286194321243a4063b25ac96d3990060a8582309a389c8f8272ca13ecb6d7358f4e1ac96501cad925261342e86b5464ed9e9d7b817f3b8005e382603e59648a83aea694e22a6122a0ede7441a0ceaeebf0d86785119debacba6bf634cb2bee174c8f85f45bfc5b35fe7d961d8ad4eb007037d4b4c83bb8cd1aa9fc19e718c075f74d0622d9c4c9e0bb5d0c2a459c4ebdf787d40b81e0a1e216e71db8de8707cbebee401d8f9d722f45bdc8c081b1ae91929a54ad99a88bbc4d2cdb31d38bfff7aee2b9f396ce16b8721cf95ddd73aedd64e0db0fb29c5427659fbe29aa1fb05bc41044f33dd369a9bbb7a4b134f1e8876096371f8031c089e02f788d74bab043517e921a8b123f8cad3d20d0ee575f38eccb05ddee368c46b87cc6a0eec07be4244078338d308efa1ea34bd9de0341efe9ea4e4ff5bcc7edfb37dfef540389ad557d3e03318d716d8f7ed9fb7074e1c601c21a78b48df32b941f6dd000eeeacf88488da287980380e9b61df9d3b6f483874156ac90d2601ef3b8d0cb6a877970918c185ade6eda0b984dc74600290f10af74a1619f891bf40c0a7abdd04c279b0223a34caee25fe0de0229e5ef37150c2e877a468b11deb816f001b849ad021a635947c48f761d320c090ca8aa72e938f028043b4d32de615f1472040e87cd29e428376f2316831d1be6f862b2598e0cde0f1298906248325b661177208240df3bc911387cd495ed4c1f0b881f929f65775f6e3c83b0000815be116ea553c60ce9029a9278a1047090c5ad12c9cfd5722278ea8bf969de478bcb5a0095bf39a477fe55e3f7b34cec6d72653f5ec7080cd1d023dc883a701149df4ab1f1307ea889eef64a654f696258795a55420a7e43e16c0171aea9a117706de0c960cb061760843bc8ca372aefa490513d15a0c7c7deef70fadb405ecba1b2c3c1d1d3c5ca0e7e0a92fbab6fdd537d87d368f4087146022fce61fd263e61f8779b281fd25c5dc55da20990c6b3137f57156fa372c83e3c0aafcddd17d4a8e3cfc233bcd3b2db43cc3970102e6805ee0652355105275ac3c0eba26f259a683b3283b805b9b8cb3b1c2d8b3a42bb9d5465b8dbee180badd26c515e77053a36d21440c011ba0931fa89bd3052698ebbe4bee39feb707d4ccd1c88367e7b6a122bc3da71b8902d29989d6deecdd6ab1cddc32f661f3e831c5fdeb30f49241c4af2c0dc46205469eccbef67557500cdb4efd0e097e914d7ecab567b5b3ef409d87b88b6dc9881225c0cfa3069bf7e620e24e099e1eb2fcff3acaae477af9affef4f499cf5eb7cf7f7ab24c663863303e8e8504b4e11292602c7a4dd8340d93c3cf9b7ce9dc753e9d8a1ed92fc38a56a2bd45340baf701e38c2c5fdfdec30f5071103a77efaf0faac08cbd471c0db334bdd134fd90932dc59f3afd4bfa4ebf4d6bfed32fd767ea97e132f4e4baa87dc2df5692442e07b52cbef9a73d11c7ce8226fa8992784591c431be580c83ed240c214662265fc28c4baaa11ada9944da37b68e13a760bd6f33d6a3d0cea7e1061c66490f37c4a26d8b6c391716dd847952a42a4e6ec2b68eeeeda913080f29c18f703aa378f1f824afeb95059fbec00d785bd9737e5f08ea06cd593cfe8b434dcd1b7dce08cf8aca495002f0404450ea1a8779d177766802058f5543bd0865bb43f47d936eac89d41f605b364b17607852cc9a83f2cc12aa2ab7a92725dc776637eac5992c6eb9c42579da095d7b2dbb750747ef417851ca5133bb72c80bfb878e2a3e39af9e8d62c6be202d00592b18c44806c13b944cd7cb14fe1d450d1be0bb309e271e4446ad37a6e03d35616cb7bbfcea08347c10233b47abdd23066fa0dff50d8cc34ba55c71a7ac4b45d62443a82b68f7b10161e9b52bfb1f7b1298cae63158ba8d817f1e105418f04f4a4d833a39b911ac5292fd4a09f502e7fa596397a6b395750ed46658f2272e3be9dc4467cc74fe6937c8dbaa6c5e1d714ed0ee4e209b99c0d93177ff80c016f0ea2be70d4011139764187267de6b973fad135865c07f41741dbae9a097870bc8bb2b3e718de3831dc803d68c5f7797cc3213c406a6a2bb1d673ebb1ac02f25146920e266edf6365d6362fb1d11b7ad280cab3135d945f125341754caa05544350c89674058c229ceef75eab7978fa786710cb44fc127736179008e03705254e80acc01986490732437593607dadaf25a93a661da2b2158ce68e07279c677eeedb68bdfb7789c416c113eb9581c0220f1706d4532bea0e0151a327ef352cd573930a85af2635f4ffac82f8af9cba07bf27d60e5e9634db266237c82333231f1657f2153a8a7c6e3b417ea8c758f8eaf05b96588d1d3f4551aee0a6ec9e95151109e7029d3be7ce0ca31345564cb3d369f5ffc140af23f9e7b9a70b13d619f9e9ca98e8521d2a2128a06bfe8e11195291eb0a01ee42adf94f98674cdf46299ca86885d89c652be99092c72fc6de1d746f1e459d2e1e1176c6f7d9e8e7099edafe562a215b9e17aa7dcd34963ce20b1091b0ec5b5a41e7c2dae3c03a1ae3447f52c2439222ae764b26b42007c9f22d80b8772ffa437523147a2dd14d249a396e28ecd35023a53c8fe08db0b1a29b126b3b52b9f584385992666bb12129054a2a2a4cc62f55a13bc6f248337659f7d8c2dc8ae603453e851ecfc29c57bad68ab0d22b16dc22f28291d67c5c70cd1f1c398b3fd5e591b4dba953b631d8d8c3fcb43d201f6953919d55eb7904b4fa506410824921c43e23ae9b564c7934220cd6220523e5ba1f624518cf0289d8c6892587b1eea9d314c8f861f4d549fe35fb7988cb6d8a63c3e16b9bfe7fb807fa9456075a2bf37d351f73f4f4de6d9e67baa79bbe24982517e3fe936b3e607de35ffe98f827209421e31aad2eb5b2f9f5a5efaa97300906f1610b2ab2a18544443d4a965d9e69eeafdc5db36b1c59b1b2c2b7d5aafa7126d6a7cd8367b9bdc23d6299cff4c26ad0ff504f0175844687c0a71620d5bae28ca63382cd631e3be0087f1bc7bdf3f610b9e9dd375f1ec204c4b845710d854b58e66b455ecca7544afb86cdd1cd492e5784bd4102f24596196397e683f7d21ab018066f5e2c4d0ce654bba84eb7729b6c29076e9b5a900892220fba6b722de7ae6ffa3f4c1e0751f70fac51dd58f60b21f4e188bb25cd9e027951ef2d796e015579f444b89384644b2383ebcc5a418f7700fa751d5806a833d4d641c0f0075e9c4bc828d31923e26cb36103a2afbbe967743b880e8f88eb4536e9fd9882b63f6835469ed0656ab86fc335f86298ffce793f429fe92df6c3d10b8cf595c15aa78d050bbf685268fc1b33a939ce3289bd0bac499530c3382b742482be7544881e8069e8b9301621e4ea33bb1c25fffda97bb516b9985d4129a6ba7d3a377a326ada8dd09ca6a55f88ef188cfc5ce652d3d831f741c4397cce1d9f76c00b43db59de68e53031bc088b2ff34fef2facb1575736cbfb74eb832dcd03118768e4dc0a5e0ffe2fc6d55421598b2840d79fd304a638c531371dd2d8ad8e674528c0eaeae678561881924e6a4b9cbc74374a5b6e5fa4dee4c3c4af5b8bad8acdb263696a91ce5d534bea8c523697cc8923c885d1bec45c69c87eac855097848792f327e95164ff4fde3f13097d0bf018aa382dcc2c344ae5a537155d2ee1f9e15507af3700e281159f16c785571f5be05b99c47451803de32a942799547a071435de616b7779e2ed7bdbd8209dd91c0779c002ab24061610795290cf18a390f839239e3f6dd8291d621e3c356b554e3fff36048ce7780a1eb5ac2dbefa6af625b80accc3e45b4ae370f50aaea458dee878c01b13316091f4d51daaeb99f50f3206d09fa98d82b91ed44e2f6c3edd13dbd29f600584887ae71a0479eb0d5b3f8af98b4a907ad742dff3386a9ef162e72bf3cee2f00e71fb6634ce7df1c3001d3f72eee0368621d23c7b858dbbf9847efcd71c7d18b970e3b4f2d47a7985914e523480974baf4301c47bdf19d8de8151dc4a85e26fe024c07fce863478a02c61eed7ffff865b89c3ce478194ee90a77cd01ec201334e9f81c77bb2cac8394ff8870dd5296d8999f94155176bff8a5b965eb34ae40cc4a5c1819c48fa612f3640f7fcff5fa1e6ea30a49ed5dbb8502e50d5c504788b2b60b2398bd23b3759d922e4e98bc9edb304116bcc10ab7a3fef6828ade783c3601efce59bd3311945bdfb502a31ffb8fd932d1bafa0f5de377048d86e29eeec4391e1aa6ce2e9db037bc41ffd8d1e897078c8f9f182a9021429a36fe62a9c9ba18d4226b536ae49f301489ba75fa3f58e1a205c39160a4464a315067b0a52e721e0aedaffc4b6c4a8228aaf1a539d98173d164fe142d3813b875db9e70e03c4f2181ce66481972d1414cdfa09c82bb43efc536ab6fdd21b80501a08c4bbed74584ac41de5f2d6067d35ef959f995eaecd3e5ac69ee77eeae20ce5dd3cfe0853592947c440605c9ef0b9acd7973878baba569de3582d83a33136e8e8e1fd9cb24f2e5bfd802e4cfd23ad807fcf7c63a51d7526f95e91463ffb18e005a387adc60a13ef5778ba34ec8925e8ed3ffbe310d1b4461b351b4c141d14d1222db2c253b03b190cb23a04217f156c4e647432009a28e3775679fd74ced55bec35723ff04c50f6d692c170e54f76e716dc1fe4aad5789894277a1df2636b1bbebad92dc23b61362155167a381de70de8707d1b50fbdb4aced8406f44d4033c520199157346ed71326d76b30188f143819947488d738cceccc487a6d1e038a7d86565bccc8edf7b6b3938ed15f80d735b5e8007e6c44d966ee237240dc21a5197eaa44d92ff72b03eba0b6389d6539ff9499a0ccfafc2551e29b0fdf4705c4078f1e268d5966d75f95fe2e0456f1fba49aac440a681ccc883a55e5c19ef34871784db70a5560a54298e76e7b06745cda8863b3f676c15bb6613a7c915c2034af3fb09296624f774c04ef722e02f3ec8a2364d0d9fb20567a4f61d9335282821110be8936b829f53d2e6e3508300fe28a3644caa3224f47339b9e7f31f36fbfbd4c3a7be00e3d32f9ee6fc32836da0041918add57bdcf05e4e8080610d70e2a1e8a20a802c03ad910f099678e745e525ee7bf1540dba755454b6597c9d6a75a90a02c2bb327ee7ed4ff3b7aaefbe50e9157259e7bd77d7df8b4be0b171f7e60fcc9d81cbd6072d22c592c988749a2daea53f98a3dd8f7dde95e4c755d7a99f5c84a05fc923511e5393c06ff2dac0bbf237bc4e4328c029e0d93efe6565df99ed23e2f082c4586539c2ec5c5ec4d4f0492744dbd3714d30bd45b6b3334917ece6a30c80b545ef35931d1e7fc3b4fc26a27eed764bbb65b2c62dce814e3f2614d2a875c9affb8ff76397ed06720e2accdb3baf244928eb1a589db95d5ea94b24e6f1999e19ae6377dc9199af089e69c0e9489a59efaf617c20505c6e8fc9ee03a6ed90c9e4e8628cf7be9811411afaef14a58f02a262b550e492a45b1823ee4e8cb1df808caf822fc97ab434070dd1d0b12f344dd01789394745eb40302107efa54886c39998cdc599429dff7491a2a993f2707cf24bc4cb81a1220fa83eb75cfff8cc0673db93141c58cfc986877a913b7f988f17f9563ae9913b4ce33f4c74eca3135f417bf9f436e84962e36cc0c509fd5ef7fde12e62b35e66dc4dac93ce56a1488e21a0b89886f87b8a778f6065a94067eed84e65719f9567614bc13da7b5f5b9eeb386f90d214092500167ee0ec7e8b7d719cf29ec547a130ce3ff94202eb77e11866699b0d7d9399b1d571938528b182b160cf85d798b9df06270bc24f691ed4738c94b68fc3b1fafa5790e5358397bf086ae51aee074905607ee78ccb53835edb97037cdb4a9f7241de0eb293d1610e1d1b55b00782b5b7befef625e7f10ef7cf2ae87681e80472df3295ab0675253a3c8f36b1bb22bfd8d7b04acaf76bb20e38ec6dc779cf5491ee0c2c1263ecc247ee16c69fc1908a646e369c40b4ebf12b258006b6156e564a2279d11f08f704ea837d944b7d42350bb35109f0fd681698697682733dbc8e205e5e49fb12f2bf7de4c6dc238a7c45d76a9c9c517b07c4c8e20b4ab6b1567a55e10525b69c36dcde8fca80c734cfcb8e8eef67d13ab4885d5b53c89584effcd40d3f4a20fb5c2c42dca73d60a56f2f278b87d8cf0eca31df28a0414728f38df8039cab41d2cd2471de220d8815a4cae114d759621ca99b7877c354ec44e1fa87f81defeb2f5002a684a8957369dcb7ed84d0e6e8d81a79f5969f7974998212cec03dd6b6b2bfe508d6a860e8740ed7f5dfb51fa4d0ea63bab8c2d12ca465a1885e8212225f6125c65cf826c6033c052ef9240db05cc9fd7266902412e1a456d948117611e43761b880c1be5e54186ced75a863a01ca7c0983d082e1bf4010f00ed952923b8d3a07b0112b3fb27afbd520bf53c78bc664b86bdba554390bf3a29683cd3f316869d8e0812b78c0dead748e92f43e2ed1127d4dcfefc9c3eb3539dfa4d18340bcf6268423c2bc2757cbf30ba37a8db62bca57866a6354e810a6fb850d65d14ef2c9c9ef3e45d36c79d31872338dc56b0febaf48e083c3fe7ebac7786db2500640acf0546c4a4b022e2e7f53705324d2d7ddd2ab03ea4133507c192938deac4b9278199a404e3e34ac35609f9338833e934dc31896799447d88032f43871a2ff4f040292bb874004a8ac36278b1eecdfde8f0f7fb0dea7fe8637e9470bbad00384baa9f87ef02ce1355e89546107598d78bf60b23fa9bd5a1335c2ac7abf39a8309fffebc52dbc77fe3ac0be30aca9cc4c063c4d1ce27d4d32be430c4c922db60d6a8c7641458229f58613fb109d803e0c2560b6828d00aea5fc3de827725607a14f1d055dd251131914b23fca8826cfccc64929ac81658f8e0570aff2335d6c43f77264e0820e8053516d17343a26173f8a14e3fe87a26a1a18d395aeee80b9971828022d97773a500e3c0c53b4734a47dddec72b6f180d4967bc473a3dd37e0396b8ffb1a53f8606b195bae7661401a5a881a743cf8ef59a075637327c97891a1a17d3774c6b75f616ff3c6f803e63c3fd5ccf4573361a3bf9df9d1bbecc730b90f82048fd96079131f5ddea17c21db6e7f3b7b436f815aa2dcfcc8012e99f656681ce33fead2dfd914ec390429940ddc71c0326385d0f955f9826eaf72de18f2a3f9a8a6493df9d868f437c5fd39141b6e9398bbe0890fcd799319c8de0c5b2a377cb1dc860526ffb60469624c0dad187e3affd2b43fc06ab59fd58b511d1d55b97dc212c7ae59b5a1562310b372992a2fbd9581afaf7c915fe5da55ec136698f5e395466512deca25ac1fafeb37792e5e2defc5b975ef7bf370fba64661f4b43dd260b5573cd62e1854d587106157858cf90423c529e997309da2285540d2cb01d7b5b883b9d7c6a73c14c39d78647f957e33fbacdd5a8edfcc7d3f36e3269c8fce1f0aa2c702506c21c059e80207f40f99abb320ccdb75ffc509f34763fbc1d6a04e524e68dbd0471fcc488ee8d2216ef1feaf4321f0cdd08c705df2bc6beb16cdc1d2571ff45f17b143c859c832076a4bb5705042a54eebbc5d3eac00c5b063c8c97c4f7871edd7512756806a59761fadf09d0dc2325abfb147719ab31e942c8e6115ca0ec4ccbfced57c82f08103e4130b5198722743c11b5a658a21a768a21e6508e4b9d1627a3de02315ad3364dfe8f0a0ea3b5d2ae1a5d87d6dd0d9101b1c553ab2888826c0bccde7535efa7d7756411da3a12ff0ed1e5aebc8b2261b3b470d6c5e900a2c084865b4c8bd7aed517f80e76bf08b975c41b124fcfc6e86c21ebcfd270da0103616874a8a9d406f5e036c939eedcf817ef31eb361863c5f8de0e42be05e4585b563787055bd299bf31a193701c18d1a0eb9301f14dcda9ed33e2613a98b31be60da8b9ad9042b59ddc1c1c41418724eb5ac9ddde86d9ef2534aaef0f5a1ddb77a3bea87d949cacc971c542f5698ae1b57f3127e784227ad879273a99aae8399ba50950f579227b2ec2f5c23197645955eaa3cbb104fa80cc09c38314c6f783c8c22417551407bafb60ae35d4abdbdbe6b32ee48695ed8ac46e9aee54d17a507ad83df2c53ac0f7d4163445b4819e40fb923540000516bd211e5ca55ff262fa51d635dcc23c27fdb4c7014e4e06dfd5d3d9218dd2c3d7e91e00dd1e5204a203055c279835905eaa87aceb3966a0f8e65227e70a77368afffa441e7e5fe50e9d2da2b5f010e6cee3f52c40c9d16b0dcc09cf2941fb71b084f588c84d58a4b19ac42bce31223e3ac8f395ecdc6ec821f727fec50118820f3e860c1dc5c783139589e8cc3e00d7f74043c3327308bf2e1afdafdcfdd0ca34a76eff71583c6994e6ff82d504b914a6a69f09ef6b94112f142c6afb6375ab6dbb8e36589e5f0a779cd72d5df7bb12d15165e6e88fd9f1f6efa0abacd8df27ae021561accac2ab45f624a248386c4cccf6d951d802ff96d0835421a36b05407ea9fea1de994d4550078ba3d0c044190c87056160a98b14f87b298b71e30bf9d378f0e9b6581afe3dba208b9da52f1525b83b8cbe8d532e6e5af41d6bfc471e8b7ea413ae676b7da23af550b9d07504342fbc5e6b639e7cc97587ed920647b222a5b82a16c90134b4e51bea6c8691df182afbc613f3e269fc053e30c350469f9586e72a976deb817bd605df989bd28195ecfe243a2433cb85aff7bceed38284a47a8d153e5d2eccef5f101524aae564f8609dd9c9333416cb377632473814398d54c48802de72f5b74c9aab19e8bc09fe89bbf17a2566361c7b4c6112767697bacb084fd7b2437fd0309e5ead51fba5e833de354e72e0e1226841c000ffea771160335ca52412012ecdb608a4b124503b8a037843bd05f71398fb965e1e9ed0dcf030b8dd67abfbff3c622162c97d36d93e2898f49f8dbbd37673317367bd1e32f7b20c55e51596b2929f6c1338511f5e149c93db7bfba9a6c8bc0c0fabe08582a7474a74e52eb7f2d2eb93b22358e4797b40c27400b354efe4fba63db6d531f298aba6f9b6e55a8405933d2d18f08edffa25e83e28b08bef93002c94fc28172002ca6fbcea451a864a243bfe5d940591e24b5754a8759620ca3aad6d4afd88c87721e2cc8a97d1289fe38a89a44d0062c03b3b15eaffb5d09d869cb8d19a60ffab6024195ee84caca0686373c32345dc1a04fc23318133f55284d5d95299231b450daa6a950d4c9543b4298279624ae51aecc3428ff8fbcd4e3983af02d1806d7a9ab2c4dca3e4b0db55926da73b4533f4145ffbeb04cfb1f9f8ee5cc24370eeb04fdda5a29393b2300c3e1dbb7a6917729f4eddf10f7674e4ef040114b19ead03ee1f124d19c3da030979a2d7aca08c526df30719feddf121b573dd23d2880db8b9b0d409e8c32ec89e564e19aba9bbd5553f6d9152679e4b4e3956c6b588561527d45a35391a7868d4edc2c6fd546e214347a896da2ae1dd16487e40789abe3ad2861f4b37c98e7275820756da386b0bb23abea32283faf3da7ffe06a71b365c0abf781f814e0dfdedcadbc5951a000f3b151c01af04ea12d4377351761df7759a03c46bf19023598e62696900a29cd6b26dc51a575f3fac6430a24f96de4cfe48416612df77dde00020bc485dd38892339c87628eddc5bad472ff3fd25da31fa5414d12710f72db9afacc448ca3cba05f89cf9015b14d3351719cb6db51063a22b49c9aa5244e8f209af9bff2884ac508605d0c7e3b112d7192ffba5b073b4568b97d731522683306701eaa0be0eab7fe313769d4a5cc6c40a0a62206af1e8b2c7de4d2e997f8fbf3487431f0bdeab302a9df0721d0784a2b9c905010c80409c3517d785ba1eed50217fb21b65b6b911b40c7846c2dd23072aea7c2bf13c2cc955d5f91c0ccc0d3cbce56c688d4223ee7ec9800de00474f110b4e38c617bdbae4431e6700331c25089cdd4b34c3cae8e424ddb87588f31a27ca36ff913d809b317e642ee5ca2fc3122777003d3bb11a851171bc1a4e123df2f813b1c84912ae89bfc5cd3796827b805e5644c9ed323dee8aa123b6c415f71b40775d175744ff57ab4ba300c45c54b45e960f6c900ab5572e4751803e395242acbabb8de239f58bb82d13fa04272b0e80fc84d8a3ea6e4fdd6177d1b1f27828472673dd2fda6183bd59c2f4c60674348e5d840a0b36cc6097ee3b4fbc237948b6edb4d0cae735741e50fad031266ba30e68d4c2661feb8744c2827ab297302ac709cf0c28fbe6b07550fbdaf9f70ebb2c7eab754f9b16ef86588bee8e53cdd6fe3154f720dc34240cc68f017da6af0af99bd51c1b9b6977c681a71ef47fdfe0208c52340b9e400387708e570cba31ad368001edfbd3c8f817cd67b1d158115bbb9800f0d450352ba0c21df5819d9f2bdf67ec8bbd9f300e3729d7e11005fa9e4bc0c6df2123f4695da1bb2f84cc28d861439b9d74d24551b1142e029560a9ffec296305e9c8b4d0ae9527c4bb02bfe8d63c1d29539a28dbecd283ee27f4c1830e7712b09e29e92402a29891057e10e7d824e733f849006d191086b071bda629a6835133c363e51087ad6262a22057de8c2403ae22c5270e3410b8a44907fcffffffffffffff5f3e10bb37f8660fad4dcb94a47c02f587fe533dbaf5c330a524a54c49c21257b8637d33b388042b10ed0cef0de7a4f58518328649e5d0ac6ba79f6b1ff48d2c0e8d627732a9b3aeab0987a6515a5da78e9b7e6f68789731cdcbfff5b3dcd0b46d8f16f9ae4f7fdc8666b3b713f639efd15d6c688e17229ee77f4e676b68b21be7b7677f54436e3534da525d75ab22a6f46968d0fd4129b53dcd9a8ed1d0ac1f237ae4bfc4c56768f8284776d4aaf6a8ba6668bc4fb5f6ed67b1b696a171961821568ab5d915191a3f9f4793fe1d2746636814a9b4a8f152ca9b1f1543e349f5cf3acbba216a6168ba35ef86e90de92a303448a1f31d6debf4507ea1c1c68cd1f35254ac5079a1c1fed4989ff61666eb42f39ceeb07de16a55ce85266926cbb63a2d5fcb690bcd2b9456bac6527975d342b3faf4a87b6fbe1a4a280b0d2a4e6d5743a5c8dbb0d0a0456655ccd921a37585469536e6ed126ffbb542d38abe7142c5985061151a96f4507a65aad8d9935468122b578dad86ccf4fc141a3ddc0ccfbb426fee526852fac6e37b8e324b2b0a8d42ab699d55efab5a43a1e9948dce41e85061524f683a35f59d74d4add5894e68d02995fb8d13526c75131ae4f94b35b448f33c33a16988e997f3d3d3265d42b39cbdcfb75247f4a984a67375aa37dac693f092d074f3a5ef8749e52324346d7d5bbaeb8becd08fd0b8f7ead23d2c31646c84e6b7192bbc7e45be2d42a3fafcda72543e97784ef4543ec584fd1c267cc9c464af071c266784418486e162a74e1fb45a3afe101a66e66871a3f4bcd2a7ca0bfaf1230d855b893084d01c5eb7166addd62fe43bd1fb677131b97b20417ca0bb44109acdd44d6d3bcdb871eb13bd39e458d5092034a8499b335c452855fa13bd39e43069fd2926e8dc7041c29047fca0b955dbc952ee62ebda7cd0b86a85baab19feb6ef1e342afdb4839db859779707cdc9db3e4cb7fda83a1aa4e16baa2b7f118f37ab945679c1a930705147c3ce3a4d3fe838f72c3ed1fb38fcd30a1dcda3e25dcf0e52ed7cfa895e1c2a2872b03858fec7a11c73348edcefb85d1953e7d6899e6a2f0d4a299d47bfac192b5b277a29fd2e2d2672a4bc2c1f6a94c4a182c2efd2a095ada1a764a99af9b73469bd4a7e86a99dc5ed2c0d1e3edafcf4ba1af31d34e76053eecfac3ab55c401a3fe4868829efb62979a5c13f46d610d1d9e4532acd9f6d4b7da9d6e76df9a3e1cb94eaa447b848179ee8c5a162a2eee3f0a65f0d374a7894b8a0844789cbcb1a990ccbcbc1d23f5a8f4c269361c1397c5f5141e92ec40b29cd31c4aacd6eb1e34a884d6eb5204773147a95941bcb4fb516ab1e70982c1d34ec10be42c6d8527ed03922a034efbbbe3b151e75cc53550f384c7270d2ac4c7aab09193b7acb4c0609713497a7b1e653aabefa596b14638b353badde522df5cda1857850ab6d6afd2d6fd4ba56a921aec34c9759b37bf2ac4bbf648d1d546335b8fcccf3b0272be76b3ede83499357a944872a39f64a67cccf05396810375c2de5b2df41c489c960d2a474c40e626bf162e8bf9f05ec051c348dddba162a3cd538d739c5851b34c9113b5fb3be45ebaca791c9647ac061d26cd09c57cde8d4f54a86d41b6ad0dcba54eb7b97fe4a9ee068fe1fe531edc496394a3468be61638eab31365d95de6896e61d5fcc12a35d3c8383c8981062b42a116eca6cdd78215ed5f6122a72a3694d214da98eca4b8f0e083268944fb55cec2b353e0a65322db8269748f0d1ac5aec572b6b87d4728d41f3c708d77b3bcda3f2da68d23974be1df59cf4e8c3a059be536ebea6b2d7616c34edb0234fea0f29d3532f4008f1494cbe5b74a9993a427ebf183adcdca349baffaa5b4b4b9dbdd63f0e49b046c3922646b752f2b49cd721821acd294a4ab31d66c99a638b2ec1050d2a52281b35ec764cd76d115ad0a84b8b147762bde69f5685904683cce7305e7f0b175b0bf22ef0010b1a64c6ee7ba58b5d3a27ad4912d068d2372166878cd399e2cc690f384cfc8c86d1a976aa354c8fcab369b657d72663c4e5f93ed05871514923c3c272067b2031c8644a7ca091c9b09f2393d1bc1e709884f1861534298f2fa4eaab7dd6f1440f4810172ef181464989a797921e252b5f4a7c94acf452d2569a4a269302e4074a6a4b0a901f2840e0d1bc36d6eb38516adb963ad10bf272dcb5047939f02e0566347ecc91f3428c7d6bfb6534673184be51395377524ff4e6d0121f68949c51e2438d921637f492c9b91e7098ac4005cd72e6e9bd787e7bef6990824635e26b42edba31c49c062868146aabe4464839edd113340899b96a3ecda9d55d0d1334ce487136f3d13eacad12b02274d025aea653874a6de6c71c292999668f72bedd5f88e954b98004cd61aa7b7dba23feb65ac6f1b27e05994c467bc061120737cec8fae0f274682de4d656588d6d702554c7944a98e998e42975ece748439b6eed5222ea46da8b77a26782a75c7eb454aab9568a8c1953aec74b4ff45c585605a951a7197fd9d23c69bd3ad133d1450d6adddba971267688b113bd3dcde6aca4946e3baa9d6c9e277a74acb0c471880d424b1d645785da7aa5277a2c2e2dd72c6cda586b5bd91a259fe8b9fc682c83d3286632bf624d193ada133d576993153f541a8ca0f17e74b835f1ec33d64ef44e5a5e80a8324a7894f828f1a146097b37e8e836f834f580c3448d4cb3dc7c3ad3fbef3686277a2c7dc24ccebba0b0cc209371f9d13299675961337079f96172520f384c66208246ad27e594582dd5523b27ba2f2567b45152e2438d1213151445161720f92c2b0cf580c3e4042168d2f95ea9d7d97c95923ad1f357014173b2b1a69c9fbcdb3674a2d7e2ff2d262e1f47ba121f68b474c9c19792672c2f2b31682393f9b1e22acd92e9121f6a949820ee018749184af88049cb0b8b8949043ce06fa2127ea0032cedc2010e40600311d0801cac574acaca074a2290810a6000021720a30316c04005244081091c0624d05c5e2270267f6262c25a507e4c00024851e00175acd0e1520107a884643460f14cf0e5e89465d227643020cf0238a00074595919c3fb593a90801020200e0e1ce05a3e608053000f10800303c00c08c0440301c01f2bcce4594c4c4e0e951008fb1605c00973e965d201314c4c32b0a38efe61019233594931894305258e3856522030f2a3022267f22ccd8465c5a52710424618676262e252c763003301474000118108e37fb4ca080410710b020c00a3884f88a20ccc15ac50052a4c410a5180c21398b0042524010947304211883004210401083ff0410f7810848e395e5c5a5876006445e547484a881c3a58980424420a980424c217221051c200308ae08426cac0842015128000068309a3092050050613878a4bb798a4740b4ba6452402180c060022101803043618c3181f6884c803441c60d2000f78a1640c0f0c600c0d58610c0de4608c1233f060c9988c9101398c91812f605a3c0d44ab6eada4982182803130c08003881820e4162205b8000bc620c30bfe03f06f09a25206b27ccb0bcb037989c10ff646082b4404908232839494323cc414188c4800fc5f9092520606230280160c46440c959786c188ec7094345af12ecfc2cac06044483e65a5872d648146302b22a22e48b3bc42240483c10cd22c2c65b4603021c100060683c159fca215184cc82b7c11031f2d28c060302fc08c118143377eb03af25ddee553529a8a0c52525ecef894d52b3d3c658505cf039418d4d16bb4342bc06042780164854545063f503a85c7d7d16bb4fc9fb1d2a35959f101a4597cb0075247f33fba471d4d4aff0a567ac8c156d458e9e1af22834f59bdd2a314bfd2236417981053e80283e969a020958b202f070613828b8341de0521b7b045b3bc0b52d0f01494131e4152545e80c184d4c285a581603021b4701496971436309890593c435971a385e5d78f5f4365e50c97967e355cca5801b2c2c2864a9ff8789695353c0d1459b0bc1c282a2b6b603021a6c03429cd50544ed8fbf83950e27835521a85a1f84b0b7410c40c0c26e415184c48296281c1c8c1b272461033fa47b7bcb8c055dea5e567d0c60c18ca8f97e659564e5a9a870aca0a734131038309810506830979852b78fc606fa084b42225e5a5070613c20a0c266415184c882a5211820a7f1f798a10537c4003648ca100068ca180048ca1000260424a410a0ca6e5599e05f706be916fa037d21bea0d7d63dfc0394840821fac8e16cc394840029cc35778ac20bd067b3750b0d254d230017b37585654507efc8f5641c18f1595f71f2b2a2d2d2d9e823203149880bd1b2871a8bc9ca1f2d25cddbab0341b417cc50c179606e26904f115335080024c1360cac0a5456585071921d817132cdecbbe047939907a7114cc97311090301b300303042514001384039cbc220c5884c18930fc10c61f0c6088114e2c0183c1a48119c3006f84840cc2022948c2180590420000b082310ab080304276817e410097428c31002708c20061008317bce084246092b004231ce1082c287029000683b2460a5e05858c1220191a2043e5530e1927b800192a9f9202c0461961941146026c71025cc4c103180ce60d180083195b90c20730184c14306c60dc484104c610a311984f590bc5fb59907a19438c10cc61420213202d63ec48c5183b3631c60e4578800c1284800c12fc0f141793f62c1df041c70a8f967d61ef460b0a90325a5080741c2a29407ea0903c620c92c5a081823146ece0873146d068795f511943e4102d3e5c3e06638808608c10160c17b060863854509460843076f02c738c817104a60c7900f22e2d72b4904186fb78202f58e911c6ef8065a58520000108400004a0d26604716141f9973212100c04860400c305f30b1f8cc117bdc06034d0035fb00083c194a429f4c20c2642f0410f78e10b97766909c14322789172070ca62501bb600383c18cc10e25ba30030653872edec0605672e1065d040043860a0a1975b84a0b4b026ce18404d8e28d04d4c20b188c4ab842878f97a353cc48e98fc1b3b0d12cdf2c4d4a29a594104208218410cacccccccc4444444444bcbbbbbbbb3b78f0e0c183070f1e3c789099999999797777777757555555555529a594524aa994524a29a594104208218410cacccccccc4444444444bcbbbbbb3be79c73ce39e798999999997777777777555555555595524a29a5944a29a594524a092184104208a1cccccccc4c44444444c4bbbbbbbb3bc7ab2aa1c45be3471a181a24601566c06030cdf223602832586179388298b1d2e35156e0292f2b33a06325e5cd78977e161ecf8315963342cac0f2272e6d060613428736567ab484cc01830991030613120795151419b411a4d58043c81b30188c1bbac559d3a0b8a4bcacc1a38565a5474bcbb7a15206cb0b632b2a65a0cc81b282567921e359fa517cb974cbcb22e30c7f153a1a480b1929fd2d2a2f64a8b407e85869f96f413a880b4a0aca6a95349ee5c5e5a5b1bcb82c324e5c585016194d864a46a5fb57a790d1d2749491697997159512c481d24246081bd680c1a4f4cb0e48210b0c26440d184c481a309810346012400a459ce155567a60302165f81f2b31c06042c880c1848c0183091143187cdca081238d1898aca059c10b78f4684cda78411b6d34268d1a3258c10bd63079a38c35ca507939567cec0b0a7ebc4b1a71aca4a0605fd278159438541a1ea3f881f140073061102085250c3588c2074196494b4b03097142135c545a05e5070a0b6b4171327ebca8fc8f96a6838c1516c4a182e2c2b2b2ca2023f364b088622404e5048369c16042e208692bf0675961616952daa5cf6028292b7aea7857e15152e2438d924ca605cfe4f0f620228986d9c2f3ebc591681af2f5eae7aaba95a7a751c2a32406253c4a7a6432ea9cc98940a26167eae0797514e7f5b3d14619256ca85182d48bc8231a67b4d27752c908b1b5239af5ff67fd4e713b7f97d2253ed428c964708e4ce607ab834d0ee7c01fac8e448248239a554ad92b5b2d75f6c21317143a18d12464f68baaf32cd2c6269765105944a396b75764b5b90d979de8398a499065729065c525064da645e54d80b49851c2a384054d26e352c76732af8272c2fa87099016114534ca3d2d74f2945aeb78221ac765ac1da4ac8920a241942badfa4bd6147113394493db5225e59799ccdb440cd11c55bf7ca93ee920b44da4100d2a6faf4b7335b76a132144e3abb5bc57da5072d2263288a61f513bbe87b59592361141342a9b615ba8c9d6a536914034e77ad0c2bb6387596a22806818e725fbe176a8106a227f681aa37407bf314d07fd8bf8a139ad3f1d74902ea410bf481f9adca6a919b11b43a82fc28786b12394c919d9ba4f2fb287e6702b6d0cdd674b782fa28746a1bef3a9d787d59d17c94393f637217dd785cdda45f0d02463b6da79e3c518b78bdca1e1b54e3abd44b8adea227668d84aa7354f2b694aa55ca40ecdfbd9c37ad61d1f4b2e4287c68fe26bc7095d2d45179943e37dad4bb1b6cb57e82272685ab6f38628f1f612ba481c9a4566d4ca7a74659f8bc0a1b95b8c3efff8ece271913734e99a4aa550a6f3f5c644dcd0f85e6b4cfbb493a7c644dad0a4bf86365762979d1a136143c3dab9b36eb9c294f099c81a9ac53eadb2b61e2152c544d4d07862e322968dd8e1642269689252e88e5d373d499389a0a1416ca9a37cb4a94e25133943c3780b9b8fa162f86d8998a171d5ed199ea7f35e6d8994a1592c7b1513bb644cb5254286e6a8a52e554327adfb5f22636898a59f7699edba8d2d1131349e89dbaecc4b89a95e22616814cb961843fdff077b8980a161a81c153abfca0879897ca169e7e8a4fee784bc778978a139ac879ee9606e7a5d225d68be57b6e453dca5489708179a4f9454753f4a8eaf96c8169a45cccee985e8fc795a225a68d461eb6dc9edd662698964a141958ea357f6dc6be512c142a3b6e51ecf45ab7e72895ca161db504bb8ab1ef9c9256285861152dd14674ada8d4ba40a0dde23c6da11b1fbe012a142738e42881a429e0ba15b225368d03384bfd43d2f42b744a4d03496da59cbcf3763ec2c91283427bb1d3fc751322e6f1128344cb795f2d6b62c256f912734ca8ea6f647353e8a598b38a1417d16d2637d52da632dd284a6173b84ceb95696ba5b8409cdfa9d362647dad46b8b2ca1698ca5c4cbd6e142d7165142a37f9e9bea697dcd528b24a1f13beda064ddb221862d8284e64fb5830edb94e9075be4088bb926f6099d1631427390393badbe6dddcf2245685c651f5fa7afb5e767112234d7f02ca7bd5cbbfc2c3284260f37b796acca1b3a8b08a151a86bfd619712525d1c8c467d3353ab9d4b99ba19188d3adceeecab43c9dcfc8b8625869421b6a4946db32f1ad6f658229534e5dae65e34fbecdcaa55fc9fb29917cd41cb91f269284f9f79174d331e3667e7f89833eba2698a176aa5c75a7d53ce4593aca15fdb0d1db3a58c8be6f4b5745a35bd4593cbe8a4e6b79ced52b668923b524a8f6a66e6a916cdadd2dd3cb95ab79568d1ec62e5d4ed3429d6d02c1aa47f2ef977624e45b268f29e25857b786f21e25834cbd5a536a62b5a448645b316ed66e21e5e878e5fd1e06a9ce8bef3a4e7b12b9adc6d681bf3a3cadbe15634ea96b9a385f215f2c3ac68fc98972dc412faf9e15534b792fe3af47e9cf9aa68d63742ee4c8f8e9aa7a241db961fb1d584788e8ac6fb39cfafd546b6d8299aa6fa30cd7594afb596291ac4a3703d537e53bc4ad19cb60e437b2ca953b848d1a8f27bd3e386eb648fa251e82c5fedc5705fb1289a83ce62aded97526c1b8a86315a0c135a0ce169bba068105a76c5ab0ba544693fd1206e96526173c4aea1f544d316c354844c29bd46db89e6ee56273baae8a86839d1b047ea96dfd7699e6837d1a43e8b560fa7f6a9b99a6874fb513bfb660dd967a261db9efa107263c58f89a6976ee3eaba5b88fa124d6a5fab6d9de5cfb22dd1fc61fbbd955ef51a57a2d186ed29ddb9b4ed4b89a6d5ee42d5d4612c914ea2f196daf6afa318e3a324d13063febb2e33df1945a269aa4c9ba975f6d22148347eae5c111e274799fa88e615a3c46ca9734a9d544734ad907f15fda5bb446d44d35072be1e474c6ea78c68de8f727f7ce5aaf5e9221afc86994d9d3faafea8221adf758469d94ae8d8d1443499ed383b6d212adc8688060faaab749ab35dfe8768ce427898f09b4fb3374483ddd241aa4cb5f4fc4234cd384f156b4d55f984681a2aa69a3543a74ffe201a46aba92e7affab664134b9cdbb3b75912dc58168b22f61aaa49ecf9001d1b8fbb6564409a1bffd4393e9ce26a243abda523f347d7e0e63b244e54afbd0a463b5f05a9d2376ca87e6747abb46d4f6cc92da43838db5f7f9e56fde49e9a1d97552766b0bf774239587c6d3a63c6ab5a6471d5278686e25773fc384d4375477681a4f1e2b5ae9ac944ed9a1c17c3e89eb3067e81c5587e6f0674a74c40df9353a3487d569d8687526543e8726253ddefcf1974acce4d060fa498fa89952e73bc5a16185ff978dd939993ac1a169499dc59672296cb8e90d0d9f76965ea1d669992637340b25452bd149a90dcdf1630a51f5a9bf636243b38cbaceb0394bcc4e5a4393d8aad9faae748d9a1a9ab5cd2a9da5bc16ca9686a6e54988d9f718657b3434feb7309dd1f791ff0c0829740d17a73743b394356dc48b78b1fb3234b75c9d5d8d2d233e8c0c0d3b5608353c5fb6878da14955ba9479fb93972b86c6d96fa56f0b9dd92d0a43a3bcb629f2cd4c291381a1593dd7945971ee6a445f68fa5cbeb344ebf030222f34898fe6762bdae689a80b0d5be8affdb0dca37c880bcd29c53e19d1a12d3487d8c9ec74b2f5d6b4d024434717c35467f75b161ad55e2d3c896957aa85852635d60d51d79b25b4aed020b5de071fd90fa26585266563db94e99ee2ad2a349d30752b7ca642d3502ad2c67fe99cf3141ad5ae795d420b93429542b3db8a91a5b4161ed48c42f3bc8bb999b6fe7534a1d09c7674d43db1f3ad2a439ed09c6407a52fe6ddaa94214e68d0b526638aaf511bca902634fd4c7d0fef37a59f19c28466179da1968f907fca0c594283fa8cd62d2f2a42cc102534481342e950ebee3bcc90243477afad59a3b68efd3204090dae6ea52a5d7abc7419728466e5d2c610e2c6baa1cb102334dc2ead679f86dde832a4088d224c2a3d3f716378194284a61375e6bafddce31f328466b5f5afe7dab3a6b3102134ee284f6a6fca0f190b46d3b0912f2164e78a17309ac67cebac4bc713e6fa45c332ada6d3bcd1170d4aa88edcb5d9f669ec4573d82ef50a5d22b633f2a2393f7becee199e45c65d34ccf0f0d2618ba96d51174dba73d376d6c7912be6a251e40891596ba4582ae2a2396c663edcbd12b3c45b348de770bb53fd8b37d1160d72ba1fe6e81c6699588b063156875b2e3f564ca445d310e2d656aba6424c9c45f39a7011fdf94d691165d1a8e7937a9db366481163d19cd6ce41bbe7202c9adc65dd74ef204b79f015cd2795abbd266abbf0a02b1a55c93c175bf78ca5d68a66cfb2e3f6761c1d1b2b9ac6ea5a996265a7aeada259cab3b9a1e769c95f15cd263bdcd94dfda6f4a9685affdf3aa91d6c073d2a1ab69c756263c95a433f4573e835ed375a743d6e8a06939d6e9b9242f98d95a2f1d312ddf1835a4b67a468b421fb4c557c84c746d1bc9e7f840ebbb4ce2d51340defaca74429f57b87a261ac8d1ed7595bed09140d7ab6aab731dd279a3c98da417dac3155749e68d8d16f7a4ecff539e83ad1ac5384506bb62e8fe571a2e16eaa27f5c9db44c3beb6754288b821e369a2e9952b615b8e5e2d759789c63b71628fd8eaad1b261a6cb9ccbd1d53e8ed976854db514cd9627dd6334b340b7997bdc26775d8956892b1c6162d1e443f4c8906a1f48bf6bf5bd3b69368fc2c579fd8e96fcf54120d5ac8541e23f75ea79168127accdca35629be8244937c0bdb9ffbb7c6ea110dba46b88c37b5b43439a2518f8feeda37e384a9118deb4a958b506244c35c35d656a78d5bd1221ad6f7b9f7aba13fab88c61bb2fcc3ddbcb14c44739f0d1f1d64e58b8988e6db59ec0eaff6c4c54334c7f6587a63e6eafc19a2e9d6d431f4c8d5723c278c42342ccfb13d95b2f1b774277a2b2e4a7bc061b21206219a86d86aebf2f1305bd7133d35833006d1f8f9678cd4f714216d41349edab93a9d7cbdaea94034d79fc8898aa9ff960988467331869cc8cf3556cc64329937520f384c4cc2f843b350725b6dbca7574ae487e6ba1d47dcea64cb84f2442f4fa111461f9a6bdeaa0f2a56f6c5e7439367f1d5bb42ef97d4f7d0ec26e5a94ee24b295dd24383122d95886ed7d1525c5131c943a3c75d73b546d44b2d3c34abe7ebb0e5ed36d569776856ef6af6c71a35e6b243b318fa86cdf41cc5e97c1d9ab5b72ce9598b3e37391d9a656c5c8b991ff75e7368569e2ebda1be44989cd039133790cca159a8fa903f6bcc280f311b48e4d0a4c5acd75ab5e7442f0e8dea65ea3095c9bb39d1895ecb1d81040ecd52ec4b9d3a4c35734b07595a32994b4890bca1c1ce3bbd7aabad422b4ff45e8e76414c5404891b1a54e44c39a3e2aa552c0d246de084d90ef6272df18146a281840dcd713e755019db6e26446b6432990c620f384c42a040b28626af512177eeedb41ea9a1612ae9b5a693c78b71b9a8346b371683b0b8a87c02d23fd284240d4d6f2a736c75772803123434cb19afe63d4a216b745c417286c655db399d548b47d5cdd028e3a3abd4aec4575eab202943b3b787f9f8528b2574cd64d0999cda0b0509191a67beee987cf2a8933493c964de28e151b246098f12377eb8b4bce8c0cbf83fc3dfe5e360691f6716246368dccf9f7c4dea4efb3e71aca8c16420114393dc4b255fa931bfc6380c0d5bcd15eac39eab8c080c246068d63ab6b7128fc286f95f6812634e687db2c5dc9761e085462db56bddb164f5ea5c179ae64f4a1df4add805122e34671be2b6ea9cc610717281640b0d2b964eb7d4bfaa399f13bd964f612a2f2626aed208151148b4d0243bed6e442d65ab55277a7b253ed0500949b2e0781afb57b94b2b418285a6b923c285ceb8fcf731ff407285e6dbc9f4fd9cf4442f4f354162854633252f4a6795bff19fe8dd5982a40a8d9e6c8cdd5bca46cc469050a141dca4eb92bf96f850a344b5132453680e62d78650ae1ff35e1e990c2948a4d0207677b6d51ab64ecb277ae85e50e2438d12974c06251e8f92d37c0a90171f256ca85182ca112451685ee539e647b49fba0b85e6f417196243474da573a2f73c40f9169606c27897ee090d3a8f18624ae764428b9dd030b59a6aabd2d9156b3c51a59ca409cd9f3fc7d4d83b6bbd4c0109131a4495dc29b13ea8ed1a2dc9129ae7c41652e7d7254b0dd91b990c0a0b1b253c4a9c4789e28144094d6edbf677850e6ba83909cdda5fdbe84e42dc4cd3895eae9514339e750a1b2c2f2f28e151c2f22e2f2b6dac951433322d6ff292c9e48104092aada66b79da860ad321394293d02364edc573b61f19a1c9cbd4b9966152449f2e244568d09ef549cfba456dcc89c0ac14b55e5babaed2428164084dfa323d98fcded7518b44080da6773fbfcdaab94783d1b0d53e5ce694cffd0a1829713a47f51d1e949c9d241617202c23bf6892d7b66fc4ff69ad947ca1dcec4d2f55156b5f2f1a6cdf8e2b4a0cbd7ba11d4678a1123b2bb55deea2c955aec88e129fcc96a35d902ed8a99fb572b942ec72c18b1a75724bfcc81bc385b2a4bc592b3b3cfdb7d84efb7a5073aaa39a2d9ab5dad9dabed56ad1d873b795f451f918a145837c09757bfc832c91611dbd46098f12951794f0287115141794f028f99794c60bf22c5fe2438d9273a90f23b368a84fb174e89935a2238b57ebaf7c4aa1c3541d144662a158a14c65e55d857a0a61faf9f973529b8e1e3c34d005253c4aee531690fe61728e65c5a5390c498cc0a269d489eadc1a9de8ede0b1c4071a253b68d8c86480fc602d994c2693477bc061b2465ed1a82e26a610b92e4a8b277a26790ddeaa188cb8a251e9fc5cda9550e7e279c3482b9a85d62eb50a3d437dd4133d4e3718614583cb39efd0ae2a5ba9595cea684a78f86732990c43d964c3c82a9a8554d75178bccea94408e130a28a462dbc85aed7ca83bcd089deeaa5a2f9555fd7706dafeb93277a2d2b40120c23a868d86afbf37b4a15ea4d277acf0204bf30728aa67953bff2e3d344279de8a9ac6c19464cd1b45c46e8475142d53e65327726a7b0071c2661d061a414cdb7839e71535f799b5c0b23a46812ea5ae47d7abb95d251348ab919ff75db96898fca6144144d439898cc556a33466c24144d76dee333a40e5b2c9f0b23a0687cdf57dd337614a1fb271ad452ffd5e2a6546fa6ac34e4d27cce138dbea76665a41663a45bc2a38447890f354a32192028272d67a4130d628aa8f0f8e2f5ef8d134dcba5db3ced9ff6add8269a7e947017275cd78d5713cd49f5eb67bd895143958986ad2f4b747225539b12130d43acb93aa52cddf15ea25187b025a4ce22ea9396680ef9f0612ab1b3e91c56a2d96698bb490f4b541796959673cf50525ad2084628d130f7b3b9d83ae7da3b262e1f875e890f345c1e8d4c66124d6b8ad57a4e8e68b1f1899e9244731a3dba8412b7259480a0d081828c44a2f984a7eadc713ad1a3e3559e2521d1243bf68f8f8710634b859147348c547afb4d67644d4961c4110d3366ea9f6db5df62e844af110da3f34cf1266bc694f2133d4634a7791cdf19cfb97c1795c6288c2ca2b9839ec893bfafc5aa262edff22c8a689672c6f2a88349e562894e184944a3c98a7b1dea4c9777be0511cd299ee58b15f59ed6d641268c1ca251958a1551fe2de4aebc841143349efabc31cb6e6fa710cd2e4aed526a5a84bb16211a667e7892a673766d4b07d11c96d85108216ffe6b2c88e6f03ab1e47b5aa73cdd4034fab95063eecde57bfe440f104d77b66c671d62e3c650452161e40fcd2e63a82542f968b552277e683c9dcf3c6afbace6dac808237d680efea75a7576d5b7b52d7c6890e141db7a1d557a5a0b4acabba09cd061e2f271eca1e9a36db166c516d95d47f4d02047e9ec0f9b2beb75f2d0fc65befac56ba8d82b3c34ea3bdb497498327de2133d24c2c81d9ad599d8695596f2ecae13bd2384113b34a7b186eb51adfff5d60b2525a59b0aca09c1481d1a5fecb3bd79f377eb3cd17379f96182c218c2081d9a5db418e69d75d8fcd989de7293963ed752c6ee1c9a6deaf8a0b3ed7bcc288726e9fd694a79392f557da2f7e3958e913834dc58ad9f555dad31ba133d3834d91ab5d4d8f7dc3fe989de1c236f68f6d131dbd59e1b9a3faba126c4d4cdd8fe446fdbd02c4cea3729fdb698149ee8b97c1cc9a3840566ac60390b4a789498b1821e7098b48cb0a1c1fea558b1d6b2a9ed5f4373bd7095d79d75b299a9a169688f4f21ae9f639cd2d0a8d47598ceb2b510a273a846d0d0e4b57e236e2c756aad9da1697cf6a9e52de23c8c4ef4da25e53ddda3b0b89c113334ad1652bb776badfbfb9dc9a953ed018749187c18294383485dd5f175e9b96af509ea0187090e46c8d0e46d33b41c1d22d4f247c6d02837d55d7aedb6d5592a28325871514923f580c364062362d8a5bb8a9a1a312d6d7fc29ac7a3b0f84869b6a24626c3a331c39f3590180479171c1201c5481856d16126e54c4a0f296a5e4aa4c8f59b6b9ee620ef82939666010f3356b0862a538c80a14188971b97f5e9d61add179aa64e73dbc98656db1d37329997353299ccf23b1617205718f142b3def4fc1c725bce4c65c52425a519339517a89c91c964322dbb2d4056c268c248171a7c4da48dd462ad8e1b171a86160fb76a2cf52b565b689a324fdf5bbe2969a733da28e90187091a235a685c5d57353f683f39337d309285268ffdbab245074f17169a4b9d124a47b6cc98d4151af6abe8ec99ba745c35208c58a169d42dadead65f85262d7574107d6b57a731151a6cc51af2e1c59ecd7c0a4d3aa850e169ead83a9b149ae66a25c5149d8314b68d42834ede22a65b2a9df3ab072350689a33b5cf9a714a858a4f68f2eca9f566db6c931e6b1d234e68166a5a775c4e9cd2594d68d6616dcfcee57a769ac784a64fea6527adf339a5c90a234b68b41962bf2a9762d7554a6892b35a997cd562bddc49689ca1e6dee8542bd5474283ee2b134b756bd91f8fd0f425f666c51269a7e61123348b101f75566eeb883c5284663f354674e5e4c8db23446850bab6fe2845fce8ac73a298dc181942d316f559db478e7e302284268fba662344c7f6dbe7fa402418cde16c448b13b35e0afd0ff646528208301a3ea7fef652626de537fc459388ab1bfddbb2be6b3c10f14593945da9e294e8dc14447ad128efa5b808751dc4ca8be612f36dbfdc8e1da713d945c374f9af57eac99df111d145b31cef15d3e695e9acb96856fbc595b0bb53ae4c5c347deab86dbe739e62768bc60f3ad45d0ca1ffab75da28e151726050c2a3e4b051c2861a259b02e4078ac9a70079713151b66852e62ee4e82065cab6b9a0a8a0348ba3b403915a348856a9d6a73526e7b9121f6a9424ca21428be670eaf75f52a821427ea2174465323c4a80f41a778ef5c2b883c82c1ac696dfe1e47a6c99d7109145a3cb1873a6986c33a90b4462d134c7b3f21c7e7a7afd444304160d9fb4ea346d3d48d762277a7945107945a38bcf5fed518ce99b277a77798d882b1a64cd6c9d2a751823446e45838e6a9c8c1de35b6e87e320c28ae659d3bb773ad4c39ec9989c5a45b3889e4f1bf2afd474a682882a9a7667113bceaf2e315c277a9fe228262a2b9a86ca0b3299163956564ed4214452d19c738b4ff5aa6aacec4ff482b0b8dce96d0f384c5420828aa6f5781d64b71059b79de8b9faf12e696432253ed02829a3c4871a5e464a4aff884126938906915334adaf7f29a6fbb984365920628a46a12a74eb0a1f2d9e2d61438d12ad8148299a54483d42d8da496b5b93a2e1c37416c3eda6f6584f74930d4446d1e4e9ad6c5faba14c4c4ff482b0b86cfe58613923088b8f4c06cd20228a86d57a757f0ce5715c8bd90b22a16870f55e9dc627f57d51174440d1e4fae1a30e367e3dbe9f684e3a6a6debae526d519fe86532253ed428a963c58c4cc6bf8e9585760e114f349e4d5ff11c6b224ab8dc89e677f1e243a888f7ac7551f93938d12c46ca970bf13ad153977a886ca26967a48a8dd16e7a57eaa220a289a67fd5f961685b6a4da1828248269ac4923564d98d59fbd530d1bcee614e2df5bd6a8b41098f4c2693c964140b229768d441ba183a4fbe4afd9de805592698b2da5f4506994c2693c9643299cc8abf9c18442cd1e42666d4d452ccd5732342a4124db34a844df5524a34dfabadf5c55653aaf59ce82994851099447318d3d57579d452148868d22ed438b1a5091dfa9982188768eea866c7955aa8473db6cbcb0f93202c2e8668d65929a14edb8ca83dfa979476232521885188c6537bd7fe4fbd0e3b219ae4549c69756288f5323888318846a1a6f2f570dfe2769a209ad47e165d1d6a46ca59023102d1bcd2a3897439f5aa76e94ce6553ac504250d4e3688018806711bf55fc25569fdffd0345e4adf88def21ca5128ae18746d5b7e663086562a9d9895e8b494b9006723048b3a15246903588d18726a1a37ed95f5b8ce12b3e34de07a58514366b2919fab813630f4d63d7a79eada76f88dc0531f4d0e4a52e568a29ea85d8f3d0ac4bdf9673aa4e8ba9f1d0b4a3d466ee7a943011dfa1e9834aefbc72d5562ad9a1c9744c9f35865a2236ac43b3aeeba4d7ed44fda743a3ccdb2aff6ebcfcd61c1a94129d65f477b2a5741e253c3688187268585b940bf55a76f6bae2d0a4c3f4dbee13c3a1618a5aeb5f87d8351f552288f186a6a9745a5a7cfadcbafa8b186e680eaab6b6d8f5b62f4caa458c3634adc8471b3aaabd101b1b1ae6497b254ec7d4d8d1891eab408c3568ffd2c5f5ac997a931a1ac5f92b17abfc6b4a511abab143d5abfbd52ac5b262c2f22a741c6c62a0a151ad9d9e530815dedad36b811867b8634e851417a542c4ea6c428c3164e88c4e5a2b0331ccd0b4671f32d5d2ae765ad528f1a146907781192b284373d459be3a35c7f4ea2e06197ef71d21eed63e86e6da27d276b8095f958ba1d984c897fe69af545a2b0ccda94eb7f4153ae70e6b3034d7dc57b66e6e5b8cfd85e6527b4d0c55f2ffb4eb85a6a9b7a3eb20dd2e34e9dbbf51ea7667d9592e34d90e2de4fd736c7bba85e67ccbf4aa55ea3ce793161ac57fa6981fe655cc9485a6132adf27a50e524b11161ac68787f9e249a452a12b34dbee94f7b4cfd55d6985a635a657c67d56159adf7510623e9c7efe910a4d273d28d151ac9aaf368506fdeb69afb6e8514327850619b3d377b2f936fb281c3fdedd63aece891ef311c48042c35023c4f47cf95abac9623ca1414e8a7aed31d46ba739a159558bf1a8d5d38faa6234a151c438359bd50c5c1e0d952206139a4fb8f87da9ed55e56d098de3a6b58e735a4a68ee34f29ec3dbd865da2434cbbfdd793563e9741509cd3a2e953a91a3a2644768da2326b6f872e51ea58cd0b41ee5ad4e357b554c318ad0ac85ea09a5c4edd62f13a15989f4acd389adb5a11a42a39c21d5d077911842683c19fa53087dbade96c1689a51a985d251e90ab10446a3b8a957ffec2eb1f6fda2598ae778a2be45d58bbe68903e6fde6a62ea45c329a5a66fcfe76ea99378d134cd66b91ab7e4e9dbd94583ccd662b7ab4b112fa4ba68d241290f63ca6e78efb968d2ba6caa89cdb775ea295c347cdcf87e6173bf5ced168dfb1e85ab2df7fd94ca160d4b78eb7c0fabd334af164d62f46a39ef67dbf4db2ecfa248b08316cd5294275dbba2b679d489ea193b66d1ac3b94b85242dcc9519de8dda960872c1aeff354dbf6b0c5ab3d16cdb9a3d8ce6928312e068b26bd667a3baa584a7a68c270ec7845f32a1b5ab54d4f2bd5ee8a261d53747fabfb646bab158d3ae72c2e6e2a74cbd00e5634a94d3ba93e3a799fda2a9a45a84ee15985bead5baa6892624665e7ac747ac8a968124a8b52ba86dedf1a77a0a249c6558a2da63c1b32768ac67fe9ebc2b392abbf3245b32b37a5c456cb9cc18e5234275ba9955a2d746d7391a2f173989a2f5bcac70b768ca269a592a16445cb11fb124573d8b9b5964ddbedf63ad16b3945d9118adc018a4fecf044d3da5ffa94eca43ac9ef3b3ad1ec2aaa73ee60a3b57fce496107279af4bfc8923b52f69d7c134d37e375548a5b2d4bd744f3a793fe5187a931de9189a62d66b7f3a48950ea8589a6e1724ae7d9fe599f2ed1ace6213f8d696ff9594b34ad7a7167a25c8968a94a34db1abb547ccf76e924251ae4a7729b2f3bb6d0c11d9368124a8ae1eb7d5b695b9344c3f86b5beab73cdded23d1b46c68dd23d5103711128d1fa6f8dfd977eb86fd88cbb46087231a54ea91993655d456423de03051d9d188e6da1536c5cf9c88f932a259c5b61b6abcab2a9d17d1a46e8955733f53aae84e117628a2f1c37d92265e9988e6e0a926d3b5981dd648b1137620a261b498adc6792b21c5ec108daa46c728213a4dbd19a259bcf99fed12dafff38568b429b5c856e2a3db9013a249766d17abe265424dadc9e510760ca2495e5f08fdcf265e8709a2698cccb7890f72ddc4dc1188e6f3371fef1a29bf8600d120474e5ceca4ea5bec3f34abd7b52bdd463cd8e187e60eebc4d61bca6bd9541f1a4666e8d4fa9ea3c4898e1d7c6818f15a9dd2b94b906572d2f202845d5e80b8bcbcc08c1d7b68bc8fc267cd5d76c998b9430f0dffe2566a35112a962b0f8de7fa74b58e69071e9a63ecf93c52678d1b3b30ecb843f39f8b993a6e2825c36587e6d33a4c6b7b7d1355d7a1c1a46e71627c9a52ca76071d9a3feb0e7a960a1f39af0b3be6d028942c79fbf6b4f8abc9a149a64e53662a1e6c763be2d0a8374c0d19a5cdd4773e5ce50e383822d373986e1576bca1e1f473ecb257bb476c3734eccbb6f79d3162e8dbf0ec3ceb2f527d454fc88626addea58912d38e355c6671b0430d3bd28018a53dcae954e59dee4043a38aafc973537f2a43699c3371c38e33348a5ba3c487cea767eb30ed3043f38fb78e2bc66bdde1e2512283121e2569b874ca19998ccabbb8744a9bc28e32346c532dd7db560ca16b6468f47493d96a4d2c1bf33134fba9165bf4eeb77689a1f9b6bf88abd6f39b79189afb85d242bd7ffc6e0743c35ab1a6c465ee97e85f68107a4b78d48f5329655e68bc3921eed9e40d25b5bad0e0b56245de4eaf5b65066107179ab57a1bf57262e67ed6169ad3b2b5f4dbb0531f590bcdad7b969c88193184360b0d9ebfa6e778a55a6c2c34bfaa4ff71d66959aafd034648735c492f52a27b542b3d6695ed5847499af2a34ccda4aa1d3e4874885062155cbb1bd36a5a7a7d034940e51b6f5ecf62c8526d5a7f3d6b510325e158566ff10e23bacf123e504854653b2f694fe5be7624f6850cbf587adfedf3c8a9cd0f8e75a97db966aa7994d681e9d2f5d479b9ea68d09cdb7a69491fda144ec71090dabcde3499debfdf4a78466b1f4ca1a5daf336f496834715d1d75c9fd131e243487546bf8ad9b8cd39d23342d25e58ff22826950e1ba169868f58bbaf3dbd6d2a841d45687c21cd4dd44c2bf77407119a4ccad62ef674ada572a9e355501aa5770ca161ceed5c22b7d656e7ce600674ec1042b39a9edf71b427bd21142065047139a30d14209d61f90549301acdf3bf47b5dd7ed92b37cc306159692629ec1d48cb1ee4061260342b31c6d5983ade164afc45d3ce3e1e74955453b1fba2e1eec4c8e8306ee7d2f5a2c94baa1ced6ebb7b563590f0a251d5d4a773ee6eb154e90a24bb685073528c3ee925d6551e24ba681a62b5ce426fcbced97e40928be6f4af85abe9a4d4fc5a144870d1a8b67da9cf396bb5ed160dfea152ecb076c850d9a2596788b7b49ddf5baa45a396d3a6d5faa7b1859e16cd69eb101eae6bbd52d92c9afbb45a1d3d9fac375b160d7b7adf216b47553fc7a241dff7a9f697f594824583d71653a79571a993f6158de21f6d4ed7da75bb2b9aa3ad569f642cb13ad68a6653774f3a77cea16c56349cb8a87ba1d7a316e92a1a4fea2e295a3e6bdb49aa68ceeb4ae8d3e2d43d2d15cdaaa6ddd0596cc7bba86816a2e7334dedc732d1291ad5bef69558d96248a5291a45fabe4ca1b62cbf95a2498c534bdd94cd37252445b354536ca85ca1b5fe378a26b59e5db92a316a4945d1a4c5f6abe99a2e43e947aba0121f68dc310249281ad65c29961697ba735a110f140d62b716faddf3a9eb6b0a249f68ae1a2ab47a8fb594cff2d2b292c24c165352143dd1fc2daebd3efa88d0f1133d4447b7010c924e34ac1aa1c4848e997ae7133d96202c2ee9509ac587154838d17c62ad1163bc4bc3e4d160924d344a2f35f4fd9ea9af3693d92d9068a259d4a79d86e8d14a9bee99ca8a4ac685470b8f202c6eac81f9921940194832d1a4d3d44a6bd73fe6e362a2d945cedcd55833f5d4279ad289e4128d52ad3973942751f942339058022bc12f24946834d367429a10d9751fb1b80069836412ac5929cd5327357432992693f9c10a0b1249206aeb9c6cbd105adc1b89bfb388e8e061a4f44ef44a5c5f4a7ea09cb464e62859fa5272100b09245ee5a1224fb6cb6ed7e5650d9533121b248f60a4e820428af32465454cd58e52c9f3936a2c9de8a5a434432a67601a248e68d2422d5719bb1bd1f03752a738f93774589e33394634dea9d269ab4ce9b376248b6852b1a2745cbb21e6121f6894005961594126936901b2e2031245348eaa1442f45ff795545652786432afb292c2039244349894b1d139bfb8946a3de030c901092296236be94f7fbd9d4c8760988b903362febd18211d9018a259dece4b37a9a01cf20d480ad16caa93986e9b177e734234eb273fa9226eab55ae4134b9e8907aba86bd0e6b4134a779ba32314baa99a240346a2185daa9e7b76e2901d160a29eefc48d57299e3f9c956b53f345dce9288390f8a1e1432a9d46a4d82ec4b2720e7140d287cbd441c20774ce5a3b77c8fe5142997640b28726756a4a1bb65dc9c793440f0d3b7dfc0ebb323a2ecf3d90e4a1e943acfdf9c66ba594961e48f0d09cc6a81f21cfe40b9ddea1e165eee9503fbb03891d1ac68a54f3d5de3a34ce2d1d2db7335ccd6c03123a3401915fa8844e0a15711c0c858120880110844cad1605b31400203020180f4683d134d1b4e90314800340545ca432281e14c822814818108401613020100cc3200803210c03411005ab24748f0018c71e4670c790eed24b1fb48aaa1a8e1f3cd090877c1a100c3dfbf503705f9e6dc341a64406d43a71909887b6f5c682ca2941074a15a5ce31d139bc22973002545540e6dd5163512b1ed9786adb4301d8fcfbc12396f764ded133d31af749400fad43080f4a8b573dd02bb3e3030f42c8c23dcf373609fb14b6fe876a81f2d81e74686841223dc84374112845e848ed67a78481cf359745eff5d54f81ebb57cbb048205fa283723f5d1efe08bc051611da2e5be72ea98e23930c73456c0d074c4e3209112483a02076f7e506c2c19cebd05e483559af7dd6d40d51cbf8c5919ce0f0fb0bd48663d31200fcecd25da915dd549f0a5fc7e7c31de9eb445d4e0208cc55b22e5c56c458f46ccbe4ce01a595fbc15ddc51190a63d123b4a633f6a7e80721f01125a230303f107233cb002a0c6a7ec04bcbdd00bdc63dac32775c92da95ed25ac32a8206f09cb17114030fdceda98170467ed2b72009c32345011773793328843812b5bdfa448ec210116f804d842c5187b5e97e737df537ae6691931ffb4a456b73c8da48c2bd5e901fd53dcfba553cdfcea696e6c460c106805d8d92bf1f2f40d762689c2980d18703a877681f2a0a4a70d4451478d025ae5e4cb2f7287774549430d405286e9426143a5058448bc3cd5c68a1531654fca5dcac0cb1ed76a01561197884818c500ebf9ba642caeac5364e6ccc4d47d99fc1089eae784cf8761b2a7c1c9a5f668bb6318de360bd90d5a0bafab6e251cb509a8fc28e13a1f13e5b0495b60bb85db828c62706b6ea2fa6960872c9740bef32d92451b1d65002d803331a4047b7df2b8df91415347d0784f900bed44c5c4cd5e57e9c80acea22be1f5ff5231ab8c83ef07a34daffd1b85cf0f39484b1e4e5a185e4410582e5ed58118cc889ec2280d7cf8807ce743b509667b469f46c661a439b94c2d5fe8a780c98b012592298e29131d7bbf66bd8debffe0b88c8a6e573bbd8ec0e4b114e003c147b57127c1118f2ea0bf29fc2a0abcec126679de0b420e8075a9700951f8001021b30c7aea3ba6a58667aa6e0e7c037db9d230302b742e7282692154e77d753c9c19b9c45b3f65b2a9a61e4f585019870c7f608627a7f0bb2b728c3c2880d7f88ab091a4a9a4d24ee9aa12bbdc351d30b3065da20b39e9ee63e984808f81776f5d6458c9bd82e6525abf2ec58291a83d12e4e14c5894ebdecb6f9ad0667afd8fa40551b7c25811cd4c9a408a78e2b24ad145c14ef1c8fd2f99a5f6d484c23d2ad60480d2f0eb046b714b11326325775881b91cab5c7a433b808d1118d42cf272f5fb9e75d6aaf0d7f169388552586bce7cb70918a949cf1f2a412693889a78280df1373de2cd1a7f6e43f8ff05fb99201c9d31bc4690d5b0c0614cc4a16d0ca5153dc09161288d8e103c8fd350dfce83961097db2f9d4082f6d5082d41216a83f5d0e527b1b2eedc304816fab3309a32c60271180023fa020925c737eba0f9bdd005e52202d947e9c36d1eeb204ed5082c6f8876222692fc3d83e3184a708cf2fbe32b4b0625688f80bd3dade7d699bb3c87a98a614848d27e83343cee06fc0c752540c14a2dd9a00fa20f034cb0889433337ddfc2b9543c28e07aac0dccf6ab776dfd8688258f1467f16779100c31ccf9032e20206384726edf817acd7845ac1a865e03129b81afa9a02c6bc92640c3bb09863a102eeaca0057cd0dd4dbd63e06caf08c3be10ed1c1ed8cb9314053788d197148989d9eb9ad63d6eb0d510a5563ea370d80e6e4388e3981c5b7a2c234541fe337aa4d4eed36907a373f3240e98619d2ab840db461573e7481090d4b4d856556fa56a7a7654c9265dda0c714658f1b0548eeb861996a0d401c3197af0fe64f7dbbf0767275a7fe51dee1e747a4e9a7e83234af723c8f53860e8c880523f819746d2a547a21d7bbc48674f74b37baa63d08bd387d5b88cee79ca7bd97a1d3dcbe14509add1e3a8597b2d6f57005de6db820bec436dbd367817ffb611be02c9fcb12008a7518b302f1044cf201fab41bc1e4dbf9c40113d213ebc183c249f377893778ec6ff28554b7faa214538929bf6a52502f917322dec470ccd94460fa9a3515c2b31fc7742d63ad44e9847d34f5993aba4f2191721a60eedc236d83e2dee9cf4332798f8d503e7c4198135e24082e6861b7cf314883e2c606649431f3ec95010fd0210a41f1ad37307aca193e9e1936046e81f72bbeedfa5ddf12c4a6a1d7b3772b385800d32d9f7af8065739c5f98d90bb5feb6531fe091864aea7905d1636a506b99a0632727c559f9b16cdabe4c44f7fb8a0a1a21fc7c86d17d94d67e7ddff97ed9885fa270b8c2958fdd268f46fbacd4a1eccfa5360fe0e218c1484a80564ebfa93f647b15526bf515763b10305d61c5ad05965e649599f7a3fadd2ad1bbe88cbf7b3f098b6e002da739eeff3799bb4c4d1b89a74d172f3d874e64d2c0d46d4273120c65f109cf546b692e1404d3504e56a6b8a1349af99ca246059aac3981f77441aa5df07e6b260141fa11efd493ad87e2560882f9294467ce2d1283227f8f5ac12d87ef4008089c5cbe257683ee87770bd7ed7aeacc61ac9dd87d7d8aacf607ff5ac6929d5f215aeae5a4c64112160c3de2d3dee43a5c750437c9c1b3873411faa85441f527eb2f7a4ca871d2089457769d5380c14e3175365813bd6743bbb6e117a9a2c16596a4c2617337b5474caa260cccb7f985b0d03b6970dfa743e942267bbffd8be83ba746b1a964a766c7c1de36241d390f2696541f2551ea070d4c18193f0b85e9864061940883712387f6d50d1a01f1b54e47fa71dc5482bf15a1e15a8414fb7953156d7a2c8155250d03d2aca6a8e9ead0f7bc251647846ae92c8799b96db1a797aade3bbe4c54d641fc464aa92e7574f5b560bdea20a782332aea674833d8b8525990853d5d1b03a21f78ad9634838df2c44a80ac0e1d9d7ae790fb6df0b29a43cd9eb4cb41718f4f39a566240e0e9940737249d65d224de27b4c6d27ce051c6095f47ff694f086cec30642819f15238693c7e29f6f65f6389e7e2216c67d305157922e1e431f392580d203402df241aa04b0f5b88811fc8403d3abffdad202db7ec59c8382fd0198a51960083c93bcdf07c13944a1d785c86bd178001c1a2a4198a377fe73b8a1b1fdbcad2c130424b22edad3c40724cbb4c9a56d4923c89a6523eb2eabb1c70a62a10210c5b3b8871b113369ecf7e9bbac61b5abe4a38b65a87d83492e55898825aa06bc8703df1bd38535cfbafa7c895d2adaa8efbf480eb69231a92a32c363bc14a2a64884be8b9fa4ecfa5407979336b99661a238e14e676d5e2277ebca3ec0bcbb55da751cd489fe539e703675f4a7edc7ffae6cbaff4c74feda80053d3c7ec0edb491cfe4aa57d349c45442c955fe39cc9904e06d89014f2551285c525aa1ac58ac5302707744164ea189084883d748e6f5df8c2188c33df501df67468dcf5005ea5535659ec3ece5c257fd773bc2807e8de748355cbfc5b19d2f12d376c4bf561351b81663226595df2224a786cf3226e448a726938fef6628a6eea4c6a1c6996627bad48e3ba9ec7cf8c7496966d40af2e70645165a0af2cf8ce886622377cf1a2e4bdeec9007a831a7a52e5fc6baf3488c4c01b27740ea63e223abd6663feb02602a1e18ce1a244afaa56962a6959278f94e86d436fb763f2acb73717ad938e52f9de2d3cccbe553e5867093d1972e31add23d455a651e89ecafbd3554af60fc2766015e437de15fa1973dd102187a310f2bce2171e699de3831795d75cd6200b2f7d8b99a0be0870bae49cc21628a1c8f03a4ec6afcbe73d9cda0c8828f27cddaa158e1b7b2e6809e2e741f388bbdf0ddcf64fbd203e440b9b96a0677b1c53373e3eada6e1512c28680d1b61bdf6f05d8d15ad9332e73b5cde53cbb6135b5945828ea5a999c0ccf1713f1f52191b35bee472e6e4b676c172f4e2c467ec27427e87d424fc7496cdf4d8a3537e0bd9f33ffee1e486b58e79d3ee91231e9986f8074566080fd510c3106291bc85931ae3d34412cfbb6ac6f7ae49ee646ba463e9956335621dce5e93f05cb734956eb91ada35e5b63f086c3a27233b6d700e4b1b3a1cf96352c1c7564178239297d92d82c5d1384390cd58e3c54ad042273a941e9b08dc12edf5605814e0f014d7372e2c22dfb546b99eb4a3b90f97680eb83dee27dc585e3fdefa340e14b5839b00ac75dccaa299b5965a4b4b080845a6c6483bdc14b83df437b828649b3c4de650c43975bacc262c17d8724bf44a61ee4f37764bd82cbbcd48964d71bf2153776157b70241a25763a93ab63cdec06c34d9342c4375cd8a50f94c1981816888cab43794246b16cae129d3080f4f9ba6205d51232006199fd038d2514c257811dff569d51f85c96dca32d87932b6130a0899b3ec6dbe0dfff95068d443f33fc0c8230ba22fbe5ba0ab0437a3444c527368ca444171c6a055b8c23e56ffb29f2b4d347bdbd880997f3103d8c0470ee00421588f938140876f0571780a47a51852d8f9ddc989fc50266fb6334082066845573de490231fac8dc5eb44907b73de5528f147e386eaadd63c2321d0eab51d4bfa1aad91a94ae6364a41d223418b1bc9d4ae078b7280915e202c3889e933e5a479ca5e376da80dd0d1b5e83611d78284598518e632eb8b1d5e8f7207a1301e0e650833d65fa64b87daa72ebcfd1dd45a93af6d3266b021f199843d241221fcd17d46ddecc9d458b00248dcea895bf3d7e769135bbfca201626323b166b433d42314bfd6aaa1e1b2ea6ddc7a81420d9a5228d29af619d7f50e03d8184ede7e41710f418b8bc24fe644375da5c4d7d99f5608fdc327f8af3ac518a7d1360c1b93a4080ea5190e6edcde1019b4cbd266539784dce06cc4b91ca42411aac9870bf0f47af134095855a950cb61fe85811b32029cde562b9541ce0962c05fafb074fa06608c8623df6f0a8617ad0c097d4d0d3025b15153b1b11ef0bad389c70d10be48d133da692c233de24ae16698ac57a589e08f4f49273da52f47548556e81a496715dc68aa2778d6c7979880c53445094d42dbe11f4b08724520e7e50bb4da220a461eed50bdf038a9c10e1e7ec290844c4d89819c23c928ce4d56feaa0c70afcea2c4255977aeff8897e3eecb9730e4b40054a0f75eed020932b1c6b03988cef707e65fef0192899cb0d7fa736daf5cdb1e12b7dd675c85f5be0acfdb03c4568cfa4698fd515255d67f0105646b4a7643bcaac8d801d0b92660b3228c925b791eb351e7b5c17956e4fe9bb42b41d64bc8ae8ddaea1f2b457670a9e0c7084459baaab6f473565a9754d0112021359df84ea85cc94f4e08f03aeaa157608a1bffb613617d639395f3d8ea9e1729c0e86b3ef0f98285a303e4474784c496ca8851c1e4500813449dd589d2578c4a49f3329d82af02a54be2b736a68c6646ba31e98e8848205135885fc8581179f1009e1e29136a1d69a2c2d377782e4aa48336fb098341066b3702b9b7d8c4064a68d87de482e1a63f6fceeee1325693c25d15125474c59ea6d19b417a021c698bb486e16235cd780e9a6f40f4eb769d543e49d08aa01e04ba1cce65d0793624172414164c0488bc2aebc22068f54f5c58223d6b7e5f01868e98b6f548d20ebf9a799fdd0364cd86b7f19225abdd3407798af49ba8c55f2bce9b70ad40a87816b588702101a6f7e4406f187d28cb68ad94cc50e172deb040b489026431e09727d89179d5820c3acbed2f139b9dfec96d266a656b00ebf2d95527b79bc30baafcc516fb1dbeb577b1e609de1f0d23c58b0e54834aac5b8251ab4285b24b298e3c322fca3be65579983fc142569347680cce7877312848ada376597a6ffecf4b7892eb537a7eedb8f64b8b672dbfa14a83f2fb380e11e8c5dc29ed555daee9b6d63190b38c345b87251d4d49d59fe8507af408bac9a4b84f085069d20a7da68afb56a5fcdcd7be654d16d1c557598e8c4321b3b5b9824d58d2a6b52852ed76370831c1f91dcf8ac8359dece5278849eeba42df21ce5b20dfcb96d7ec14578948f37be46bac93500ec9b78b82f455928f7f349360a557c0e89d058578e85d37226354d21a0ec2b10936aa210479a48d1a7a011f2fce7857deca58e98eb80adb9a7f0def8d61654a83241a422ec1c00d521e61fe42c64e2463f9dbc46217d2123a491264f4cd2d4ea1901c39114581509c026e74e4018b34297bb75289f80ea4d637fe7191a05f32eac527706f7c630186d6f8f156485433c9c08cf21db67d51aa04e14b8874096118c5d08dd5c8ad1f23db160c5d9f8b722a1701100c905080c2f4451e7e5a891eb83cfe382987fc98875c599611ba1ceeae04ffe05de95de61b57adf2e893f04c7959911d0c24cb803358d3bb189a6a2a5c9947f1cd8a8a1c6b0b7131b869dea1e4b905aa5c92d612e54384ca3d021021e6c6a3fa8cfcd709a2d0dec52fff6d5e3917e92154cb59ce7c5691e53c0e48fc3501fd93615743b9cc99abcb0daf4c070aed760a093c04c8440bcf463cd8e0c2722c10a8f95bc8cb6dd7e0b729bf4e5aa2ada456a5044d89092a66b029c9c30941177c98a3ef1ad7b08de27f5370bcd7c9b9b192579b21596b5bbf63a54a37ece23f4d2d2c477bde18fa3974b9dda3e520bfc121d4cced09dd34a5ef06b56d3d3b96bc6ed60d1582554f6fec15d5757fedee1fc0ef4d5c57d4c82f52128bbd778d661946556a3b982ea568acbfc165b20313b9463d9e0a97bf205a6fd99dd9ab44547ed486a2dd2c1e0e1e16de73125619130b2908e3ec4d298d801b96e23a9017e5839b4454bcb011cc0cf01bb8b8a70a3260b02d9f412305cece6f2e2c9907e37976e1f811e9641fdf5eac2389a51602f87dc4a21f31a21d0d0fd0517584a5b002323eeeb68c47aa9656c6e03c0c4cad37d8c06850af8d2948e6b3a929d172ad8989725222699d1ddb5d1a380dc078b7052a97d2d263d9b3167fe8e96a7bbea99455d57563771e989d56458a321a344cea97a2d4ce09b2c728c2be3f1f968e95a367edde6a0fdb9d4c570356d89fdf001f27d69817804819e4a6c587e69c16370469cbb4af546853d5dd044894543a6329f3164c06371791477260b9914c66248149a892a8e485438c32f0745a06453da338f438493a94b460880fde51b8f4e7829239ec8c727a90ab41dd4ee84bb53c007c41f1d534880b3335ddc04fa0d9dcd4131cf998c44474a88fe69ded30cdce3760cc2e8cc25b02bc576b1103c1145145d41a16657bb826456cf5785b29a2c1f5bc82f645aab96aa098c7091f6fc826488e4e984ad564bb876ae7d28da1f7110836926e80035c8a06bd4468b0047709466131a59aa16144d5c3d9b4b2e8ea25256e514074495a900201ed32dc5f94e59db4c5f40cd07c3faa96dbdb10f237b3734232df47629c86a214ee87299412d6a15d83568d3ce9edbce6a2d8b14684d10696fd7d575a96c33bb3231d825bb5f07c8006e347a7ed4e510c6bc94cc65825112f70aba5c35dd0ecb66d77b440286d49f685e758eba6169a0f28618a0a1b2bf439685f19c4985c5ce890e158f30f6c7d81886cdc9951a46be0b0192058bc8c19d580588272bb3122da4395357a3304c168669a1787a877258a47e60dcd12f3d48aaf0611839e4ddcd5e2ca52c85359173eae81edfa4f73db927414c6f3f925906f86f2669b88e66517a418462438b29bd06fe0d629e6192e45a99de236bb2b4f0c84c02fd2f843298265588d7cdad21e303256388acc09df6f364b2a651a39cacba0421f4686c132bbf894e574e208b917bde9d0381a8b1f0567ec5872e53f36e1ed9faf1bf4f127863f42a5872c1b6e25e0208928b4554e4b08a30f0675bb6bb82004027cfa887937abb6835f7fa1e3b734aa56c94038d1279065bf7a3da7db53c9cb32803c6d4e3b12381a9a246e649fae618e624aa59fdc042a3cfafad44ca0f4a73b2f4ab66b186c36d40b7f20d46d3135ad7b80928cdf6cc406f0a9cc9620b2ba654a0fae8507bba6e73f41642e49a0cb0f5c9cbc073b05acff1347c4f7e61c6d05eb012c71e601d3cdabb61ba8e550e24ab154a57abf51dd3f3a1612221c061177ee8c37be0ddfc543d0ff81ab0744fe00ef119e25f3b73feb6e64691f2c424e550a60fb2b24a446a7dac88268c80d1b2840c2d0573ed7f9edcc671978cf8069511b45e509b27d55ea903124aa28fbacb5b37bc10c7e6957ae4c77d85377060b8e38b1e8f2c0a4c495f01c0004a17ca6c7d01e1f8e2e3cc6f99d123d8583a0337662e6ff204ea6316ed0c369200ceea19b98217d2407c8d80dd54937160678a00687413db82933d047e0b08cba413b7523c0a007d7a030d023373103f8380e95a137b4136c240cf7a01a08437a701364988fc541337a0374e24687a11ea82161a0076c5a86f2886e949da61936064379528d05022638c243a81392ca103c124ab0a51269b73b933ca35e94f3cec13d0e439d62c91c21106062220a14ddab78003b9987dba37af05e4927e34019235122f4484d810802e95e681cd623e5e23961cff02876681eb50764518403a925389240d01448397b4a0fbd47e98038ac97cba1e7e41e8987ec80bc666691e88822012548e2a2f415cc33ed41191031a0aa64baa2e7fef02871e29c60158787d2c538cb0ee2734d0c9d72bb9edda3e8d2984d022438c243a891b216bd85b150e85d4c8f336773d4bd94ce1a5c2bc79cef28dde5656609498e200eecf01ca4613fc5066a3c1cc8e11af4265a4c1102017e2c8eab76e55fa7c5f029cd920bc1a79ca48af0883441110064a66a848644bda54b03a905919e88fd42f6229ec80ef3787b480fbb27e8621c574fcae1e7e81e277e1f99b2fe76e990ba521e2b16bac3e15b4ec613767170919eae47cdc139f21ea40371ad1e25a79e83ee465ff0d789e4ee593698404a7a500fd34cf1e7905cfe72f85ea42f119e8d110837aa0dfa4eaf189d9688580b4a2ff8bad4e0578aa4b0f0aae6ebf016eac08758a88b269035d3db60d9bd44001794626fa7af96bb9800a71953ecee17c8d1daa8d6a4dc857e4dbd266bcd88ca24f605a55091a74cda9959622a273952a4c4af6949199531e12e80cb58d566fa944e0b50f892eba2d768d294224a60968152b4a9b4a20466252c4e97ea42d39253e682b64c97324519353b680b75859b5506ba3e0a7257dbdc4c04290f14abce565a9984b4f25774ded69aa901c21204732c2f6818971977536ffedc66a0ccc2f7d2041d00cd69ac3bb5e747c7843b4eaf2b2f306ccb7daae670936a9e6180338177950e88a2df59cef3b86325696aa9233e1c53a78fb65ff4c1e90238c672d3147d1a3e323d91b9859cfd21f3598d934948e61b70ebd5ebb8b4b04226551e7155cc5e5215beab8a281feec5bb86f39f580abf0cc3dd5526be93943e1d2343d7025d1d7145e7cc6adf58f8268715e4c2519160a809d36e17d7dab71f1929fc2f2bf47f16f82f0afe070afc3705f93fa820f2bfc23d2b65769f5f32ec4e1a2a496a6ed244e104c3fef0847f592707238efc34bac0bd062ff552cdad642a27fc25659d44587c7fc445178472255a48ac0bf85b691b0a5db65fd9aff0a28fcaf0ac878efa4d1779aa279778845bbe69cbb9dd614e20bac9db813adf58ac7a16a37b5ab0c5e3f43fe71bc51267338b3e369b40dd181488ca8edaea2ed4fdc08e8e510a20585ac6a45dce827cf0c1d52aba30f978d1a284fda380e41ba61d03c2f68ee9fe2a3cc7f5bed70e73daff39d49b6a1cc84e69419d90f837719c6459535f53dd19180af34d81267ea6cc0687cd436256ffefd211d06a3605471a4aa748fca03168b90748e1574083c3c2e8360f83e3add23fa205dabef7c688d4cefd88979fcd200631a0dbe4794c160c4ba0878056e35dd114f5ea4a0d891b1819a998bbe9374f3b5de1b14bab51432747e55aed408472ee11347f5857fcfea23fe46c6a850b9e792f16f14b128d2e13fabab45a42fc640d15717974a4168f56ae70a11ffc3f9a6f472561c492d18e5386c2e887c91e157164b68b8031439010c14e1285aeb9938e47138d0c2277ba3d81492bcc445977b39e8013c65940afe7966af6f4b12770fef1cc66e0a15e0ca737d85e47f1e6f03c7aa893aa57d134fb33b719d16be5bfd35e59542ae463333622816f66929ad5ab07de0e9aef6b75ac604046839760be1093a0242edd12d05c675ec38c4f00876499f0ecb232340722e989f8880d078c258718e8ee2582f26b36c0a360075f9d09da7bb03e4f640350385ab3c6164aee53ba608a193c18cc77117c0b5064e538ff8d1436114c305a417857560e1f1d0d499b2dc223d49a731380859b5420b8f2326ec7ffd2776b14520a1f6720c399a5a5f9775c639f0b1648c21664b7968da606d0835ae904eb7f2cb8e2e39c5cc61506c2067a477dc60db8338d35f8a5a2d64d5b81891e97de1916141d7850dbcb0a7a33d0bd161e47eac9762adf72a1a432e2473c8d63f1e6947918663add0f6090ef5147eef1b0c326377a649e01bd14bfe620b6b1e33b76a8a02a6ebe83de695943308b8a788acd338cbe7a521671f82fa2419b8283a4c28bce24d4743bdebc18f144f7fbf0afa12d1ee549697f1b18629ce0c4d3d43738a3cf49781e0050af012ab111d8c141c19da5db646003645fe7e7b57ecb660ad38d81a1100bfc09c0c09b40221c2e32b6ca8c7432f6e09a17aab0349b97ff64b3a13aaa48af34877a5d7956baa7132d5ce201e635695b7dad9cb56a7a42cb510eba4bfe60e8fb6116b3402b7bcf4d65afaae9bc605587f19865b9d567c331533ddaa97ac9a0ced0d8aa07d850b2b1abd1328ad871ca0b3e66c088a3b9889bf72daf84f06d9a1a99c49b03f371d0ccc4a6f6570c7a5b40c85b6b63452171b0febe55be6b256aaebc8a9476a705013accce50e08e117177bbbb480ca6388912d05d3a9f43f45db3018a3de9c2181d0fdc6d6cf11c050a194a091b0905198a440d89051985448d89820c8584a684c20c4582a6c44286029998c66692196e033d7ca4fb5eed594d76a7b4c5538bed7fd0a6a65c743138edc3a2b04fc1f69ac2cd1084c92c06076c709cad6acaf5e3668616ba6393004b056427a5754737f4561de49b8686218de9621d38de4ee2c3b642c14a2435b63ef1746b9072a11639f5607849b84583f0afff0466ff3e7ccb2fcfd65516a3c0c5390024c2b0ac470560f99aee24df361bce2bc0bf2eef1c50ac91ac27e4eb1c80e963be36c3a74b19564344dfa4773b1c148ef7b4b7e04d1fb1e5f4a27909a37fccd8add1b4aee0d239925fe4333138d3f7f0b16a4a7921e29e131af3b86d235f03f3b5e122aec8983407e59068a0ed2ed98310b595ffcb40c99a9bd72e13d05c30aef074e24c3ffb269000c03c0eeaf17f58c03479a024f0c0f6f56d3db5dda6b3d6096c8cf12fa98b65a9f1aaec130455df908377090bec771a6c66e09bcabe37960e627f9531d57b8d2b05a51712604481913d3852cb5430932a5f978863c25958e13f6d206c722eebe148ac973d721c39ffcbf0ad5e9e9230c1a3a05b10c1c0e1bfe5c3ccee13f3fe4fa9545106bb23d36e3557f8f4f742500416adce2386b1a05dde61d14df9e9cfc7ef1e14fe29839161073cb152bea52b88409cebe7c2ddd27cc771c7ccfbe49ab7897c254f0a8bccd1c8b14a9cd4fd336daf1765d73e7eca7bc737d4fefea406f6c48e4ef3f3980dcea7360a24bfe07931fd7c315b926fb2bfe0be755d03edfab00989acca377596b2119338c0b5cd00de52abb9f51ea8ca5d9d7400cad5a790eb1eb4e51e0296d109b5716e10df02a2cc5185a3460baf44f7863f251c0a7e49e26a7f11613739852a7650bfbcd5cf44b09883137a736030e181511e5ab7d92849237ef9ec32853f97d17cdc3212e92f782c9018b7d7b033cbef6a5a752ed77385da624d1d95719c09f307fd12f2c370ad6a531eb5b12fb18881f329c0d532878a966495c0beb33dd426cd6c706673d9597d66b81e69926fa13e3684a40786fb819993fdfa9511a95b288e406e0ba99ddf75814ff89f25b4bf64a3c10b198e7f91e21449d62c747479260a2355dcf7ad89d800e2ee3ac4d4b54ac3e5c2030382f55b9705aa3c6943b5eef963052a5af766ed6c179abc29416e03212eaa1e0e24853a541619d86c42418dfeded0229b1c3af70fb29043f1df693d557caf8546fefadd93b7b22f345cdc3e7008efc9518686db36ee6c5bda34cd101dd5d0ce1684e2c8e04019cc0cb603473a4c70b33683e74c7e3811bf983c340e1c02b4ab4e431fddffaeeaf333981c7aa48df50ec73bd5487f9222945d1749e24e33fca03c25e69350876f06dfe78295e3dce8d2e24d523de41e82f55060a37140bef6c2dc1225c4fccff95969d7d72459d5fc9908dbd68f2bd0a774e6c6e87881a8c8db930f03912e54fcc93ee45a4cfd85f4df93eb6f6fd99cdf8322cdd0cba4692f1485b3fc08f70f1e0e4971fae35bc60e6c76d5d6c5541e038b1748feff354e2d38290ca6b2d3dec8e4b76d2c326c75bb1dad489bd02d5286600e47d08be9a951360b2e776850c79c75e68ec6f3c6aaca696ef67df33b7490d496f983a255756315144f2aff5660741ae7e68ed772a461f6ac6a6f9dae57ad1f05c6d6ccd8594f120ffe31287426340add1fdf31f351496a9551413e034593b8f71e5768a00b494d527c30f734c36243ee03436eeb290e47e0f7584396c9b94abb1e9e8b16045f86017a0e224bdf84e2d1ec80b0e14b27e44ba29199e281d24b5edffd011ecc77be7eb7cabcb2b24ff6ca6a7ecc34ca93b5bb8c13f4e4241f2062afda83eb045bcf59767d1371d6a65ee2dea83d31c0a8adbb1fb9de464adc6bf1706121f139243bff61c0a1e34202a61777f048b734131c880ebac12fbfd06d99c6726305deaf3c30a5501949dc56eaff9a1efdd95d7b8aeca63f04fdab772e80f6e6e72080dc383ae65718b21e299780918e41fe497c31fa8f7b3c52d080cb04a617db6e0b9fa482b3a2f175ee6bce59152baa730be0b6d3066205d14aa4087927cbea80f4bf1fd9f53f0c4b47be469f21b1cf2c60cfff68c794c924bdbd18826f0051c19448b4d3e4678372adb90dfd25295967e419ff89ab18e79c0e56c639f48a4e49e169f3bab387d0e3c083c5e0feaa6dc9d187f3af0f0f1451f2f721d32c1abdd576520a65ec0e6b1d09e048d27d7bec47027a3dc47d761bdc5c67e55d976f661096310a8cbca70e4bd0a60f4e399eee73f1dc929af64d5ee495dd85b057dc42e06ebc409846e15ac56262d7b85d6d6587a2807cdefcd9680c47604bd6091f850987bdb6b00dd8fda31d996e7efd7c700b0901c4557d7eeebc1f3821f9cce264a398e35f5ac211dc62a0f2efa120debe8d2ae604b78705e139e0cc20fefb4b3703ef3750be089ec2fbd585de8f988140ea8dc039742a0be4906454aebbda54f2e4b5a7397ce3d0fd9a7076f50a21fdbc426d60fd064d2faf5fdaeeac72c85c26234e7181299685f1ef65d87b710b4a231d5ba2c778b1948ae9c159441ebdabe6ba63c9e6bd614b335b6d9aa192ccde364d71405b7c8905dd1dae891b2e05a828d3d5146e161f485d23810b8af72e601fcd6aaf4cce9d04185abd04d1f4f1cc7d2fa3dc9ad6789db8b06adc8a6a0b78f66f2d9720659a9c2e65fc05e133057afe4c1732920e3a083ae6b7fc8649f575d15b275503038f542770f3f496a4b1bef55781b4ec4f937a6d242e7f0c23fdc425dbba2bb72dca60311a71629a7a9a7d072b50264588e1c20a517d684ead354421d03b5485d7f75dd36ee7b9cf4aeb3dfc02495cdda71d3ba20f9311c624068003c38fee488bb833575a41fc44abf144793b12f09beb84f0465a38ced1e1ec8fd8c6a61b1f3bea169d67a4386b3ff0a21613f31f8c45cec1a385da41a153ad732a3f6edef4bce747fb5634ea1b0cf4ed2797742b5bd8b303ce95e338c60d1fc879f36ffa89e23748cd9d90a398701300b3c427732f05afb142fb6b7f7b9db80ea239cf5aa48f35fbc460bd9737bd30177d27aff771dc6b7a7cdc31fcb307857f1b42c03cfec849d2994c6aab3a8d65f47ac35776514c07f061fc297ebf5f7affd7d837d93c15fc4e47b151f829f37df9735192d28baea173a80670c394cafa56a6fa474eeb2b989ce885f40d8c9221245abf32ff5eaa4917d8bfaf5bf2932c9ea464001f32da26adfbe14078dea6b5056bfa882da78be6a558fd63c66c8dd3be829bce91c6a2978dc199be861f3f8dafa3129efa3aaf68c71a8fd711af02e0779542dcf0f5443e71ca39c4103d52e42683a7b2108eb98b399e0f3bbb0094ce7a8c41fc9fd034bcb18b70c577e821a29186a4f4b49036a012a774f4a094143013636464888d1a91114364a02a5a16a868a3aea98e8a4e0270f324778cc898311a314446a6c8a8211a31454786c888111b31464686d8a8111931446386e888111933462386c8c81419354423a6e8c81019316223c6c8c8101b3522238668cc101d31221fddf92e6fb764969b20db8c69d336db2dd8185d13d79a31d534a5f6c06675b986aa52834a89464d49a34a49434d6943a54443a5ac41b544a3a2ac5155a2a1a2d4a452aaa152d2a45ad25051d2502bd158516a502bd5a82869509569a82a69a89469ac9434a89434559436a04ea3945c13e90ab9f2d18de36aa34bc6358bae060fe251dc46e09c84f288ec122fc1e7bb7cb491b5886ce70f72d8bead922c324ad14d651447a92db89778140e2f0d513bcfbe8a55d6e172b1b8f9b961f61045ebab554bd637b8cbf7f17bef62d88cf21235936f2a3ed0134eb5cb7057502321cd988f5d148de285cf8d9ee092e13e33e03be026d362a3aefd132b4f9c540c5af3da01376e542a9633635588fbfbf1e0daaef87ccc6fe6c6abe1c49cce7dd9f912d21f081bf9aa7a14a9358d364efa8f76173ef218b21aeead37849768c62fd304c4ce512408171749b07118512ae336c452728787ddc46d1594e511b47b6d41bf181abde65a78b045b40c637cafb07bfadbf5669ad1331a7a77972e571b93104416a682f1238df1730abf98c03c107eab5e392252ad0b94d2d4d5309ff01443ccd5b8219cfc3898c594249c0173a7b2d32dbbcf079ec36f2d4cac210f2a538b0bc0d2bba2ba1831aa24aa01c3c62c138469f682da08abb10d7a18736244943ded0256b080d8e7baca4302380281b687b558efb619368c709f23cc39cd8f82e8503b1f653d202dcd9dbe295a558da37679dd0ab6f3383850e55dc4b33986642324ab31a3a99e193dc83167833e9f16fda0040fd42cdca1cdb4f4b2ddd8835a922a1b4205d0a689f43fca527ee736021865ba87cf8b69e9cbf14b424b1b4dbee25c4d2f70b0f444844c822a4624c87026c7ea7b28167afdd38a411c045ad0f4abe6b5c0fc944c665dd82c78fd201f3d97abc9a9aa99cd5bbec1d7f1d68977ddcf7d157c4901f85b3d26c0c67972aaafb5a0be8610759f64d3d9d30138a64f025ce3ec9ad375eeda65bd2029b40ada5ea9e3947479ab996fd004672bf696c709d913a46f62a79e860c0a7f0ef4d335de7de54739314a558cfab52dc43202e47be598d714bde4107f89f72e90e248e0b296b053253cfce7c330667e606f66b590deecbdd163bca6a7d8fb5e32be996599253167a0dd26ec054c6f6995fea8655992b20db313b53b38b9940ee61ab4e202feb9042a0c3550b4960efcffffffffffffff8fd244686bd9ef2f5392b2a61591126b4929c994528a743820001000000000000000202222c41f0c02090c170c0b0c5becc1643a247921c66c26fe39baa0408efed163d8420fe6b33cd6bea78336bdf2d0bad211812e6c910783ca1d4f3e288df0eb6fc04e8c568105b6c083a3f3a2fbeddc3cb45085e18009e4e0220704acaab0c51d4c9dee3792c8ee748f71095bd8c11c45fe7c10c9a3ad4550d8a20ec6945157977a3cfc29eda8e2872de86090379feeb52eb7c8f4ecb0c51c4ca3c387f233df8f0feea825c416723027e923e455c89f4c7a8caa7c94c1618b3898d4cb4f8aa4f0252f0c07e36d7ef018699fe42819c0001d3a7c6cf106a3ba57049d3836a74e6e3089a9acff13a73618b4b8084922cef8ca890de66032bb635932f517b406939fbcd98c4b72923b6a3047086f274d64f9c86d1a4c5e72757dbd4727ada2c1a0d69f75ea84c8b9e8198c29c24276fe94ce4b3583d9545f9fd2749f13296530c91519c9e693ad9f4706435262ab4f8e1853790c066f0f992357e6455b6230cce9842bffc26056ed6022bff7e3ab028361ee529affd42b6f7ec1a44aa8fb18ca62e44740b085170cb27b274982124bc2d405d39d4e62f76d417cff5c3079dccfd051f62d182cdaddfce9a4823c212d98b458ca956eb162b959305afcc8a7d4f9f2a45830482d1d8b2d75732a573046b44b0d592167e78e150c3f5f61173fc41bbf0aa6ec174c352f8cce592a98ec43f8766d9d54750aa68c8f93bd74be2aafa5601e9d2cc42fcea99da2601a4b193b4925fdf6612818f44ecfe811a523c7fa04c366ce7f89c41e9d2f4e30a5f7795ed3fd4967130ca77c26e999f48a21134cd52ade79bcebae5b82b16fff848a97afd74a2598931c555a240879f161120c3ac1c37eb44a4ce59060ee8fe5e1737f2cad1fc1fcf12323b26d4630859df095345611ccf2f7b91ee5228241ce442a91629cd0f12198226c5ad07fe679ae104c95f54c07d9c92358648b2018ad557362f9bef53f50bbe06f0b2098b2cbac499cf907062176467ddcff4f9dfa1f5d0fd8c2075aa6cac60493ec25615b7b235bf4c014d1b4bbf8967a522169d88207c64bbafb84503276cddf80077a5c40033a708b1d18b584fcee68a6683f3918efc37274f1001d6334043c1046173a749cc08b044618a8942d74609e1d61a7ad7492a8a61c98a3790ede262388f0c1c00b1fdf630716186044c02ab0050ecca73dc48c08b57949f43d90025bdcc05c2905bb53297ce5fee2eb74172d680d94b5a07b1c5b20043b26b0850d4a2d61b19273747e98a38b07e4f0a24717950419b53069be29ed901523da4e0b73e75c91be7448b1f0ccc29cbae5d963254b77280bc39b1a9daaff218eae7868f90ef6818c58987330dd7bae5fb2ed630e64c0c27cff932cdfc2f456765e610e11a3e342f6ae3058eddd8909b9897e91d10a930afa63679f9583387968a5e0dfc70e309ec78f1d160219ac30859988b4bc5b0b1e0cab62ab307b9aaac975478f3076b4a07ba8c294266be50fe1fb61d74f5559a5013252619e207e62c8656c299df7a24717580019a8300425825bd6085fe15d9e818c5318438654f5eed71041640ae38c58c926a4b5677f61fcd8510a938ea4cbd7e3c5e90b18450ac3950c13dd4b93729830172940fd63033d1a5046610a425ba8505de915c51d68a454148614654384f959ec80d15f885195a38b07bc8761011d32426190617a3fc4962de98e307e1806acae24200314e6cee946f9470e717579686d8e2e1ef05ef4e8422d10821d2a90f10983a98c7c590c997561199e30e9a9d4baedb9d89d4e984ee81cf15349f0893127cc1e5288ae3a69e949f681dc8439d6ca3b2975f71e154d98d39d70b32b55b284ac8bf3808c4c98e7c6534a389f15a5420cc840960eaaa23b549bfe12e8021996e0e4e536dd735b39ce436b8c82289051094ee54fc152a88788d80f1f391ca0811c6b1f904189a2e9e753b773c9a72a750619933056fe9a7c3aad5ebc5e126689ee153d89cce89423612e61bda3f387f7ad17122699a9264b4d93f108c367899267f1ad217747184fadfbb9bbd5a7da5246230c263e64dbc6d404198c30858e9e2348ca49e79c8b30292529c7a57f8a30fde8e01d614f2da85322cc363aa493747f7a4a2c031126b12a351d26238730ea594a3db2645dd25f1c6418c29c32747a0b7f0939c70a196414c238a223899276d967db4208b3c58b8ea273490fe19641c81004b636f1476b1e5a2de81eff630c31541006ca2dc80884c9c4e6cdc44a223c90010853cea94787ad10e2fdfc83417b749892b9b6162e6c90e10783960769eaa3ed3a731f4c4aadc95342ef259955a541061f0caaf5463e598ab463d983215df987eca972d6577a30ac964cbe8f97112a98077387dcd7fe99f8f8919664e0c130f641896897be834146465facdfaaa4199340861dccff412d6dd6aedcf475307be82c4aa9ca39f92930c8a083d1737bc8cd074f72467330a88e7ad63f23b276922107f35e884e592f280ec65b137baf24f408b9dac1c1fca7da63e992fb89d21b8c9e3c68f7ce311e44fe61e0850f93e106435ef95037e2c243ab0b0ae4e0c2cc64b4c1a0477a3ef59f6383e1cc247f7213eba2a20c780758ed283ec85883d994c9ff6ea848dfabc1944c049d2dba9d46c564a4c11425e5522288cf1a55cb4083e9ce3d45fc988d561315649cc120e48775ca3e9e935da220c30c8668b9e37e9809292459190ce6397dc6e54206b3c7307d9f5354f590c33b051963306955aaecb19ae35b8bc1a4f32b5d9d8e7c275d45c80883c944cc43c9133d9d476030c5f794204cff05930a7bcd13394a38e1f182296fcc688b746b9f2e18462d5cbea7f7e439174c7ad4c5ddea6c0b26a1ae2b37cb3fc84d1a21430ba6dc5f4224e50a1ed4aa2508195930c976b0962c21a7bb601732b0603c4bf791d7cc52c298011957305c5e24f54f63f254ce18492920c30a86f49f391aaa5288e92932aa60ce0a72ec83fcfc924c55195430fa276dfb930b9ba1a71d0e46ef3803e3021cb8400e2e722c800b4b195330e7aff07229c8b7a519cb18a98231c0b81f2a4832a4609eb82563469d0e72291e5a581bd0a1e3bf18230c1e3e4a46148c22aa948ebc5ca253100fad2ffaea07188f7a3c069454046440c16c35e66b1d6f2ea4d2130c2e974e6ac8fea03ae40483fa129654a99b60501ff25509152c4ccc4ca8ae547abf1751ba0a838c25987765bb834a752b9f0c2598642f7704a9ff016424c1d47775e91dde420e1b09e608218af2cf1d65613a82aa1f4bc45a66873820c308e6ac60a5528ba93eb38b60904fcbcdd311a16c2582d9aaa2882482aaed55866010d253f420528460b6f41062e95c42fc9a413057901f7dbce7fc43060453f2349fdcf574e7ab1f18aeece3a4c976a32d3e309db20a69b154357444460f4c95425d479a24db4d3b7efc09b01e208307c64e79547ed83ae51db40383af2757b1f8b468b30e0c1224b8a80bca64f3cd81f952bf62a987ab4999063270609c1417bc7dc526dde906c6fe136b79ab61417c193630e56d4eb2f83f51d76b6150a72b5f6fd2c27c1d45ed47521647df5998fd36547bb794cc8564618e7f22273d6a7eed736261ecb6ce312d419cb40e2ccc75bf9eb5cfc944f015e632151ea9c7930a49ec0af3842442389d52d80f6d2b1c934f9fd4575cad9215e68c34723ce54a3a865661f2ac1a953f4aca3b125518b75754bddcaa481aa5a274100015069d2bf97b9e3193f9398529b7e4c85fedfee1318f1810c014c64f7ee13572ea9c4a5e0aa457d049b7e7fc713b29cc973c9bbfc6a57eedef6314e6a8efdfa2c5aefb1485f973bef1a4d4edc8974261d2493e88c9e5e4f97f5018fff4ea88542ea7843e61bccb6f59f96641c9c713c68b783aaf2b27136d3b6152b9634e54e4701da29c302813b35f56694caeb9098367d56c7ca74779356136e121cffb526cb364c2a4a183049d4ac44b121b430c4c387ea7e2fb7e09e345089b273c5e626e09739d8a31e955af84c133e227b71c4409e36ebec4512158d88a266138b97212f383a6762c097386bef029f7ec5d682361dec8394d547d8f1f5b4898726f47cb50217dbaf411c6f99422c7a9926b5f728449a55f9d2d936b32d1469863559820bc7ad75f46183f26f78bf96f8bb42cc260dea696ce53184b4a453c22bfc42a7122d07e233a840e1b2622ccd9c4248beaac58d23f847dc1a298e5a5d60dd1ac7eecb1be1004af969093162546680961d6339542458aa79db2419833b5624cd7273fcf04618cf8c12f7ad2ee1d2b10cebd7aaa0bb59f0b200c29e699befd3197b880007f209c72895939f5a6290c04f083692f9d105912d4a71cf6a1eedd915ad5f3d00ae7fb94ee1fcf83414727ddab3ba9e55478307dcfaf65adbf305bb983d192dafb77aead17e10e2bd0b083216fae7b1a1547be8666a051077366c8fbd22b67fa4c63a0410793c58f26ab9fb48d4acec178f94125f5fbf1de4459a0210773d2d193645fa42a21e30bd0888339f5c7740fd9534759c51a40030e26d349af7bd63911840e8d0c34de603629e6f55ba64507dfcb0d0675f6e7f3f5222eb13618a47b4774f31bf523d1a1839740830d4611e94423e6285d527c038d3598ccea3ebff64e1ae9abc1a075fe6c24e8ec8db4b181461a0c36334186ee130de61095a56584f4ab129a1c5ce838832988f853928288194ca2436a4921226f86476a4715a55106e3b56675527244830ce61eedfb97e2a564cadf917ca0310683f89bff5127c162e479685989d13d3ca043c7ee8086184c1b629d69aa84cef1941fd00883797d2c7adcd32977950b048106184c6abc76278ed24281c6174c4a3cfa9892f792a1f28221fce86cdee7b4ab9c2e940e24d0e042e948018d2d18f269778972910934b4609668a92b6ab759305e585491b5e6a9fb724fa08105b36fc44a5b1ff28bfa2de0051a57308748b1b81c618296489221d0b08279e2e5096d42984a9b106854c1fc793e63d4e477bd8d0615cc5a91429e143f29ed09a2310573db7bf67435e1fbe2625230b99e4c492bc931f0078d289854a5f0b127c46728090f2db31e34a0602a11969eca5efd12866f8241e309c68cb979b724eba28d5ca08386134c27e2f4464499a4e5eb1f3e920b349a603635fdd0d2aa94c4cb434b8c307ef8c01eb6051a4c30d59bfa16911e2ba8022ffa0b64a6051a4b30e9fd206ded92107a9ec30ad05082390451919024bb88706240035ce4c0c2458e1d2307062890c38a0968248106128c138258380d1d635455681c818611b408067b1b4d1799a03ea778685d40870e30c2200279db5464eac3c78ff7e1a3abb0406308468d5cfa3dc9503a4751173484608895fe6df3771dcf2e3b018d20143d724a96686152a4981c0e6800c1fc225e6f26ee851c45a4d64640e307c66a0b16497e3ba4c555a0e103b3bf675f2eed78feff08038ce2a20b0a1481460fccc1e3db6d97ed9a52a2c103a307115424ef1153a61268ecc02474105a27f9f98a2d78308e1c5b20043b0c0d1d9883299dd9a9f47869e5726034697593ceb332a08103b3e8c82167b4898d0f8d1b188436a5e742083222a86240c306a624e6b293f2ab8a6cb760462d0c3aa6df6fd797b7a96861f0cead6d1621b3307916895bc28434b9932ccc695b945aa9a9cfdf53e0bde8d1c58f19b130dfd8d97d52cadac73166c0c2a442f81c32f711fffb579864ef5dbdb8573221628110ec0063862bcc27cbd6c37d8e3265f24893c08c5698c7839948f62356982ce229190f397fca41ab309fae5afb1a4b156651513752448dc89c53617ae8c8395d825061ee0839e8c8c9729b883cb45e10468f1f3ede9502760a43b2b822b4bd4c8a1e738117894d61d27e2a16ee156f455f8a8256f36f719314e6586f9dae735bf58236c0c3474300edc7fb188591adcf724893ec3d4414067d51df57f663bbf7a130bba54ffa2397f604ed1761ec500ccc0085552ba3b6ce3de9cd9f30e8d415319268452d390fad1e6180e1c5056678e288bd9b9743524b4add892e881f4969b42d3f831306716a6172def85988f098b189f4551023a2dcea46af89f674c8ed91b5d2bd3261f0f2dbea0ec145b28b896b62cbe5fb582f61fc206f6a415a4a114e96305aa8dc31f7c278520b0fad84c172a7f7f653f75333254c27679d54864a934fc27059d9b5fb926b972909a3e7ac96f3cc29494ab3c248983ce89549a99d839b1812e68e574a4848595e4f7f844188b4a5befcf7cc5288596086230c162a05b9b9a712ede4a1958d309b0eb9b27c4c903f59469893564818613d42646711a6760bf2b47f99297915617cb7ece53a39276d61224cf27a743c214c4418f746c74fa974f2adcb671cc210fb92fe1a8d1375bb47183b1e0c2fbe00c30b84c00c4318e4edbb87e4bad5396046218c9e2ec7c90b12b7e56206210c2fe1fc6737f27778076138657943b28bebc4490a660802d57fe766999577185f94198130c8c58ff9091f2fe469158119803075a568a1e3681f25fe3f9877c4ab625c2cdf8ab2482d60861f4ce9fbe7fa374a379215c8c1458e2e6030a30fe6efb8ca081e29cbad1000c50c3e987445bcdfdaa5bc153ac18c3d18c5940ae55d421e5a5f7cd1cae38718ef7a305c888fbc48931979309aa90ff9435ed05f8cc1750230bc48011b60061e4c2a2307f57b97f52346abe0b2ca1d4cfa2b7d9712d926b3d9c114ac735410fbd3495533ea60ea7f4b5751e7629bc6436b7bfc0f2f5067d02167cc61861ccc5a396b8997944672639811076377d0cb155256795167c0c1ec92f38752ca6f25a56e0b33de60fcd71b51325d726c871c61861b8ced975208422e7495dec3b0c08c3698554689b8a69692aa6583d143a5ce77b9b5c32a6b306aea08ffdb486663a9c134124345cfef21240fd3609226bdfe6e25968aea0e31c6d8918230c2f8422d0339b8e0c2821968304baa7dedd2f39cdb9dc1204fc7529683853495c4436b86198ce1c1a25b4bea524ac943df8b1e3f4a1a04700d33ca60502e622968e54942725a10c68e307e5cbd8761017c0f030cdc1964305dd5d57f05bbf41909c18c313c9773c510ebbd62a118103977234208dfdabf0c6684218f936c5e2154a947c180bc2c324fa8b1f81e643998f185d37cdaabd4d11e39c5c73e0fd488d4d68f3154c033bc60942d934942757e112a35a30b66ff90ef2a82cb05c3683b3d4f49a58821ced882e1ecd63cec5f08c9453b5c8bcd0c2d18d343b25ec9bfda129330230b86116d975f647cd384b06088974b37e2e5b4a0b43e7cf01961c615cc292b2fa9d74fbf6d6240a9da516733ac602c79212fda45b7b95530fc8539935dfaf99ecfa082514db2e412c2623d4c664cc1942d64cb5e6af647da3cb428c087195230f7e4784a29f9f2d0225681195130c86abb30115bc3f4050ac63319212dc79f139223b787194f3048aab3902bf68356cbfb77d4cd70827172a4d42251d4ac844d305af6ae27ed9644268909e62b5f8dd75695f87dce58c2936cde2ff22535f3f0d11058eb194a30e648ca4155ccd57d20c04930e9882d774f194ad96501370309866439e7e09d9d83f4a42398edd22cc9c75fad3b19c1f0be9ea6efa7e39fa908a66c89a7ba5ad192e88960fe0ff1c2c4c5d0b1fb2198f65ffc2f485e0826b1b35f7b661ed22908060f739f74ee87650f0473c82d7651ff4377e8072639213f7d5a1493a90f0c923ea759e4f2adb007868fe149c64ede08f1c01c9e7368115e6196f31d982adf5e9b3cb5934f3a3047d07341acfff3b51c1844efabbf4ac8f3e2c0ac6e71cf2c04cdb881f184a4b90a6ded16cfb08179d443103a478514dd530bf35b4e6a42829f577968618a71ba15cbc74d8a6716c689dd72e9e15c5c3bb23089603e4a9dd0896f762ccc25b224b5f3d3f21d16a6ca9d64e991941fe4af30f78afe882234cf3cae30486a17253f4d7908732b0c566917fe4a2d3dcbac30e7fc6d1131cbb398bc0a738fd23631f4b643c2aa30c75516a13454bce5371526931472a689cffadea2c21823a382b29238c9db5318ad3a462a13f9d34ea630e9a06d94865b0a5345acf83029e49e28294cc2af634bc6e910c15118b2670d993f15563c1585a94549ac4f217e7969284c3df9949a0b6a494da030643d0f2a8e48aba5435ed03d7a74aa608b4f186342cc9724d573fac513464b2de6b9d5b4b22f9d30e9787dd6a75b4ae40b27cca3da72c871f73dd86513a60bb1f5a62dae989e9a3079ef5e76d0127cb49909537acb73212795d4041313e65035fa921ad1f9585ec22043435fcaa152ef474b988212a3c3695de97fb112268bd19d827213d94e4709732e91954724ad68f74dc27442a86a8705a1937c9230a79b4ef210dd9fe38b84f1d2847ac92374763d48982babbf87ee24fbeb1eb18523cc964ec77d92f47256234ca795f95d935b2fe41861485aa9ace65d7bd54598a4e99db419426a8798228c621756a7c487514f220c5924fb44ef9cd3418439a71cb592b4ea10a6b83db6fdbf2130b768d1cab4652ead4dfc628b4298c462686df7e4217f4298b2f36ba6a7ec41e88330d9554ace88be118404610c11eb1eefa9f306c260ed9f5288cb90e30184e1c64daa8f4af1a0fe832929b77822abe513d90f86393b216ce343f0601f4c25d977d24e080fadad2ffa2a035bf0c19cd7626fc9c512b73a021b10630cff06f88b3186bf7b21c96e11aabeea409892a4cf95c27d99970c08a387fdfacfa9ec808d3f18e7e3db999e117152d80fc6512b41d59fd28e90b5d1075398de53e236964a5e1b7c30885cf75c2a555ea98f8d3da80d3d707da2e9357b5b29f721860fcc43edb08107a39c8af569215a529f7407f36b956c086d32b2e67630dbe7957cb724d359540793ae11a363a6c7310bd1c170d967d474ce44987a0ec61017d6f4cfe4dc2339985c453da6a9e4af4fc6c12cda3b3fd22d870a271c4c6ea2d3be43aa4f267c83d1724b529c921b8c274cfd2aa90efae2d706b3be658a8696ef7431361844947a902a59e6ada4031b6b30a74bd515b265ddb4bc810d3598478d7a88182e3968cf38b09106f3564bca3fd34b7a2a34983de49b901e623b8c08d7c0c619ccbfb9f771eece54fccd600ad94dbb29191dd6eadac04619cc95fa5d5216b38e9892c118a632b2bb9ece578c0836c6600ecb96a6c23d6c5b2e068375ce4f579ff27ff684c16c27534de4b7486c0b180c29d94ad00a53fb4bd9f88269440a4a9bae3c29d2c80b0693d9cb49cf249124dbe88221e8d3b92a42481b03c0680ae05920043b04c0031b5c30965f690d35ef0b36b660b6f84bd331a284d88ebb2ed8d08239a88e3bf92b5c5229f3d0ba828d2c184bcb46dcc7a460030b86b73d1521238e7e4f47c1c6158c35ff93ecb5c27bb06105c35b5aadf5effa3979158c112e5f571cd111ff52c1746194704bb2d61d6c4cc154ef9244a52c6a44dca460ca9d63c288643a5de48e8d2898927da55f4890cbbe8b1b6c40c16ca962a9758ffb2109b5c1c613cca2a39998e71017d725c286130cafa124774932f1b77274f1801a848d2618f47a7256e4b4f37c79c106130c2aebf396241f021b4b3067b5701be25ffa4d65430926ab2fb125f2f9ec5406071b4930874bb4f291905227892a6c20c16cc983becaa72adcb54730ca0819da1f4f6eb6564e61c30866cd097a2c28d3e16ec74ed82882e932237da64f4eca430483d0718b223957c94a1a82314fc623850be2eb7e21187d7de4e71fa525ea41308fe7ff5c8b1772e10682f1f497f7f787e07dffc0944e9947b20c6556a90f0cf7ffa5f4b989aad90393940ffb51279ef55578600e3ae8abc85de7ced60e4c6a29b21ba29eb6441260430786fc25324d2d89f853ca812189c5f270e791431c074613263c999459b7cf0dcc95533f48089e8288bd0d1b989388e9d74125131ded5a98821c1711b2292dcc173e3e632605152a9d85b9d3e64908cafc52d80e0d356461cac92d59529d42895cc6c238f23f4b4c5de888f70c356061128daff18c0d91ecbdc2747f21f8e5b8bb5d21355c617ebf14b176737bd96e8539da5c9c52e22ee5cbd56005aec22025fd8e92da6fad630d5518fd629e50322b5ce42017c8c1450e62811c5ce42015c8c1450e42811c5ce420355261ec94e63ea79c15cc54cc891aa8309dc4cea7972fcd5d763151e314862d5117d47a3e1d3f4a440d53940e52a31446519e8485cb5022b2355d440d52983e94480c4b4945cee5086a8cc220ff743edd51ac4c0737504314060f2af9fc64a51437b3136a84c2d8ad233c9a8e6fb7c80f35406152a94285e8f727cc3d238450db1242f6747ac2202905c95e4955feba1306533febfad71c696e066a70c21427c95219999750631326375b5f0f8f1644eea0260c417cb0f3a81afa3acd436b47187e2ba89109a307f1a02fa50e8dab7968ed08c3338c1a983076e970952d992e97a44b98822993913d755dcc6f0983851a2f8dd851d4d74a98e3252d1d77534d878d12a69d302a6dc4348dbc9d1a6a4cc238e9e255565216c77c2cd4908429779afc9744867512f9682fbe58c00ef6448d489827e424928ddfa42c491e5ae97becf8d1e359b09030799824ea4da4ec11e6c83dee2647bda5be3bb40b351c61d0ca15614689daf945cb0a351a71253f4f0b2a5c8f8c38c40b35f1c36a267a8be8dd57d5b47fce3f114594725415bb2f11a990aa4a7b88502cd353d8ff1085899ff99fc2a8f01f43e47d25b372514d5d0874c364f6bba82c5912c20fbbbb11e635dc710230bc7881935a409a8f1a83308857909c277a9fe993200c9f71a376b6235523100659b14d29e521b4e41220cc2329d9087d3b0fadabf2c3c79fc00b87408d3f183dc9e7acd7c9a427bf861f0cf2438e5fbe776e8a3c748c1d7f35fa606a893f496da808419438f0450235f8604e7d2912846759630f268db8a5a4a907d3bd5dea1c467f7b97f2605041c542a98e24d9e478307d0e4aa98f91d3bba23b186c6459b724353a2b6c07936d6c7dc841ef8509a983c1d25fba588ce5ad4f07738585dcf13f6ebdef3918c4645d0b2a6639982ec75badd23befd6d5888329cd27a145298b58a10207539c754991b7fe428d371875b647b4fc93a8bde8d0e1e328a1861b4c6adcf256e6a98a9e1ca3461bcca3848b6aa76442ae285550830d869ca5a56657164d6a54a8b10653ba0dedea295ff6fdd460d02952c652a7a4c1204987cfc983d6aacba141df1271ce822829aa710653fa17dbcee5318371eb23c89972cb60b015d5d1a1cffcfa440653e56c5a3a764ce8f0180c2baaa2f5f54e344737a82106d3a5093a554c8eee12660c5020c78f0e83d9c24248ebbd81c19c845ed39e17e3a179841a5f30e41c63464c701731a04387116a7821e5797499d04a6bc1645564a8d105b56387d2bee173c1102496659bf42476f9168c9e62d9996fe4104cad854376a73bfb4bf9beb42c98d305616274f47fb1140b55e30aa5c30a26d5b1eb7c474948f252053e5356b256cacb7c4549b2278960badc22d4a082d92bf2fce4b97851b329983fa5f797381a5f9efa1380d11a10420d29983abaffd789b41e4e1205539c9febbf514a95447dbce027b041a80105e347f30f2afc4fdacd0ed0a1438c56810e1d359e900214c617e7460d2718429edcfaa726c9a90c6b34c1543a4e99fd67cb113213cc299cfe976012215a5ff658564309e648e24ee229ab245efe512309a6dd52254109a1a3e85e3590604e3f9652c3741aeba41a47308cf0fce822549660790d2398c3943cbd78b18b2554a308261549a9ce69259e85d42082c9c5c35625e9fd4ec21a4330a7fa5fb7e8f1bed52404c3c66a4c90e272322c542308a5e3d40082d1ce7b4cc5532a7f8a357e60aac959412ca74f9e92357c70ecee94535918199f7bfc10430a357a605017df279f9ef9d4411e6a8a821a3cb843e57c715311cbdbc13bd6313ba88935511ad4d04171418d1c88510307a66c8f1c793fb40bf8418d1b18f4798610f149c408196bd8c032959044899f6a61526d219cd887f34e39b4307d8b94749f2f7928ed2ccc5f29a9fb791031f2938539477db43e6eac592a16e693eb109f4c0e0ba397c66caca577af30dd785df0d3ce0fba55dd4580862b921eccf572fe051aad3056b2ccbdf83b2b0c7f15aa553b82aae8e1a145631566bff0b7e6f91e3e3f63c1aac298a1efd2451116fd3ea9307bd5e9ba1211a97562870a9375a8a99e9eec5fb1198d5318eb924ec943b6b7a417331aa630871a954f6eecc24a4a611ecb6184ec6adddae4a4408314e630757fc93d66466314e68ed9d117f7840add9ad1108529e51443e8493e312fc78c46284c99dbe152bc6edbf3663440610e796742dfaad2e5cc687cc2a41dcb72ca17b2165c331a9e309eb7c7f95f4ea576e7a1e56596011a9d20a94a965482a809343861b8966d5b99d196ed4d98fd45ab4f142d9684ac09f36d89139f92b6952866c2f02974592abd38359f0e1d582a402ec0020d4c98e4cb78773e8d147fbf84d1840e8993c2a3e87747f9d0b38461de45e6d4e95c258c992a736b55126f4b37d0a0443257497b27cf6ba0310983eeb4086bb92a95bc2561d0b1f3ed2676eb7838122649eae2fa76c8e1d4dec08e2a489824dabe48fbd3262ff608b3e7e748c8a5bc52cec411a6cf351f7354bc202906038d46988457b23c22e5f8acce0853dab6dc8e307eec782d15305a80c622cc239e72f1d1445ed02ac2f46a3997fc9c589c9817341261bafad04988b06c723444182e49af546d9e7f6ff1d0caea8202398859398d4318d645296dfb383be91bc23c9e6657475dcc948846218ca7e38d105751b4854d8310860b57415b9d059b5cd31884b183c91e51713ae48a82307f325713f39a8796d1088469b44b082b4ac7497e03c29423929022973a4eb834fe404a26c3744e896dfdf811060b780234fc600e793de269554c9b8f1d8d3e18b74e88d46a1f7fdfe5a1d58207a30a8c30bcf0e1e30d41830f9ddf965c0e41630fc64a9782cabda4603aab071a7a309dddce019bc011086375be09c2e2e5f4541c8030a8a94f1ad9911582b0e0f8030e3f18b4e55c7d129f2462185e10cb3e98736c52b2244c9f0e3e0f2d3e1894f47ccff9606501c71e4c4125996bf9afb7a8911760b4189738f460ca214b563b21c3d7f338f2f0fa876fd3bb923bc2432b056184c163c77fe15e98e901071e8cfb5a5ae5fd29089df2d04a1c773004131fcff9f6a2ec45d9020e3b18d7945e7b0af2e27b9c0b1e3fbcf0808f2fbe58400e2e727420071739b8c881a4120b38ea608a1d3b9f78132a8fd938e860ee94d7741c952dd5e870ccc19ce358debc32eb969d87d68502871c0c23cfe47dd0b4d57d75e8f0cfc1450e1ee847e188836962a54913bf4211e080834159c95b093e417b2adf609ec915ab457b4605cf0d06194ad693de65eaa8b80d06d36954591cd3fa79ac020e3698f5ee5324693bd1641fc0b106c37d2eb7eff7c997fa0570a8c1b8ee6f1d3dc56930c44f7fdd71394ea8af1801071aee0cacba8c59f96b031c6630f676a54e2eb2e33dc943abf4f821068e3298e33f47672df19083c943cb8a0ce6f289ee92f2eaeee9c760baec6841f69e5998470c260f9f45857db174931406f3ed78fbc689892c81c17479b9c3f75daea4e62f983bf642afb576b78d5e308c8e5e9eec635de04526679aa8b985e0f23d29f7eebd09511318cf824f22e0e08231e5b774db27b568150f2d1f5a3b1c2d0066c0b10543dcb0ff8d943c3f88f5f0f1c38b2fe0d082d97f634e46ee20546a5930eea458d9977416cbd233e0c0824168a410af48974cda573004892d419f4d2e11210e2b986452874a37429b028e2a182eeed4f87cf01cdc5275051c5430bcc90b1df9c29d88f0053fbcc0038e2998420aa3bd7b443a79f30e6270c0210573b797b650ea1d4c7ba2606cd912dbf6fafa161d01071412c96582889254c602f3c1f504d3bf5e9e7e13fa20030e2798cfcb249bf768fb7013cc6dc244b057bd70cb61825975e24dcc30fb9415c7128c6db2344ee71a49a2027028c1acaa9fa14b9cb0a0454930bc6aa8acb997bc9308120c49e9b14b713f8271c4e81479e2295cba4630cb9792f536d1502a5804a378d4906e7af31b311c4430afc52a4b5bf3ecb09c00c7104cee979428bda767f4a1031c42307da914fa93427648bebac0e8d001801de0088279532d8a8b100f7000c154fe963db4ee9e109f17e0f881e972ee0b69ebf2b1190e1f18278ea8c41c3da1e41c5d50e0878f3030c0e387171ec0d103538d4afa3016ac1ae0e00141b887705d591203c70ecc7f21e9ec974efae752a1000e1d18dcfd3474597ea7c8171c39e0628498267227f49c827f1f555fe0c0816926b589ad6f15bb51318e1b1827c8d5a95a87ac52e1a15506c06103e38fde7aaff4ff29be3cb4c6b8510ba3a8a9f825935289d87868ed00c38b13fc51417f8f1f1b58c10d5a98b4bba588e59cd5646916e6903fddbe62fa4f88b230ef68675519c98d58982e46a77c57e19d5f040be3c958efa09a57223daf30c7fb1ecf904917ae30ff5a5fb0bb54bb176a40870ed4e3462b4c3a459193aad4b845f0062b0cfa2ce877de5925f1f801c621e0c62accfd27c2786c0bb2df21b50eb8a18af77cdcd253773752b1375071e314374c81be197dd97451252e32cf0a374a611e11bf20ba43a43098cc52ea82a6a4d1d95198d383daf7cbe58628ac043742613825a9c4a2f7e7704982c2a0ce4479c8122e2995a9007d80dc27c80d4f984c6427513d4f3137e4a1652cb00adce88421cdbe28f9d7f710373861d4dcaa34b9384b92db8471e77264a96fd3679b268ca3d5745bab4a9f4a4c0e3732613a1129287f7933a14a4c9837d46289ec2de3a69df5e1c625cc5521858ef99de52b5c9630e68e7a8f5479c4ea540953c9b7dbfb5c5a41d3cbc30d4a18e762b5c40791f393d024cc7d997ad2f38787960a7a9413dc9084313c848e5c2657940445c220728cbf5c32ebb02a0f2d30c6e8210642c29cf2e5cc8a1c7c84b163ef5ac4d80aa9adb207a3bf3874c311e6bcd43941eefc241d6a8459b4edeacab47fcce70e371861083ae4ef590b51bd2a371661d2a1bf1ee5fdc24f754311063b9ba0e9e32a62d1449874e74e9f438520ea53fde206228c225b9f63554d7ecfaaed8d4394d2c68e9df9824fc18e33dc308469bca4694fd2f4c23b7cbce053b0238c1f3bf805370a61aa105dbdd408c9a2478430eea7393913c7362c83307eb8b11326e5c2adb225083b85651b6d3a57e97718146e04c2bca2729e902bfd95e7e306200c93c4a4d17ad192befcc1b093b446bc89a62ecf3d6ef8210737fad0c849c94f6afe85173c10a93c62850fa6d8e9bddd395cd27979282afb1b7b30ffc4ce4f4179486bb78f5223b8a107538ced7753177256a5f3603869123ce8cfa36adec30d3c98f2a5a82c3eb31224ae37ee60b438bd3fc9c24292af1d0c7abb15f4e55cb64e7530c9c4d8f2cff9e154420793e7abba9823962f7d73307b788b3fe24675e30edc9083d9bba429a5c765a4e8821b71308652b1ed52ab3cb48270030e867dd36e413b84879606da7e70e30d66d7731777316bcd0d26715742769df2d1561537da609cbfef77cfcb7f9905000737d8604c59cbd31e4c4810b3d81acc579644540b22e7932d0036b8a10653d851f93de64e32578ba5c194da479965734fde17c70b34867f0fad2ebe385e9c008c31920e1d358b1b6830fbe8d8eb9dac5e46050e37ce60c8a5239ea7860a752f1766308f38e9ed1523bfbe2e8369c6ee4d8f4f0653a820da4f88aca4463e06b324cbed693fe6e56a31984f54323d3971295f895a7c613007efd87e4a7a300d37c060aabfcd76d3cf7dc1e839beef4c8a1fd64230dcf082d19384bc2a32d205839eb0e517be6b74ccb860aade4e4975fecefab50563687a78c96551826df26e68c16e64c1d815d664ef3b744be80616cac78d2b9815ec46150c228b5cd21da2a794743cb448f9e006158cf3d5b3b2150fad2a37a6600cf5a3e48ea74e88190f2d07e37da0140cdeb5aa113ca98e75230c5020c70e1f5c704172004fb81105e3aae7453df370490f85c00d281852aaed98321397d3e3a1c51cb8f10483eca518da1d5fdfdf33860f2718d4e80b3adf4216251f637c21468f317c20066e34c134e2a183a9982d413b5ed0e38731c1f469e184e7f2d991a0173d7e8c15ec08e3c7120c492cb36394cd432bed86120c5fdd6982e90d3ba51e430c2666379260323dc923d77a07ed96c3700309e65a13526cc27878b8be70e308c68e9ddda35a2f5392cc831b4630efaf6fccbbfa995cbbc7186d811b45308ffc9490ddad93badfc1f02255750f30bc70800e1ddd638c2682e9eff358be7ae43cb6176e0cc1a09282d9ee86321381e1058f1d0f032f7c70136e08c114247e07f5c9a3e4f575e840c28d2020227d10c93b8909714725226e00c19c4f5c072b1dc45b528270e307e64e963ec792c70701c20d1f709d346488cd7c490f1695744ae282bef5899c1a6ef0400bf974678b33cd93d7706307a70ae592522e8d5a07565232f2eb599b974a0ebe3c72f1a24725a5180e0829a74a172265b871033cb5522ad78895e55d417dca5b6e687ab8610373a4583b2ea9e47afc550b731ec9c9a398704d1be5e02202b4281db330e8cd7ff4b17c604316a6ce12f9f631e3b3cc21d888854124f1756d6d9e4c070b634c981341a508512222041baf305e945339f121c9cb88d870854185bc973284522b8cf676b9e73de928e6af95f5c0062b4cf127f5fb4af4e52aadc224362de24d50a6ed3e5518f743fced47482a95a7c2906eb46a2bc4ed4841a8308df0604a4ddb4e544a878e1fa8c550010f6c9cc29cf19e233c8e1ad52553986eb55228d3a14bdd5f0ae3e5b22dd3b1233d7d4861d0ee921627a79476e9284c71ede53da8246ae41485496ffefd778d7b743114a6bc91f1b95c038579e62d7b50af1b11ff274c6b61912b76ef0953ca9552af9f8ace8877c2f4134b858b1693556c4e18dde267dda7f0a5abdd8451e2297d2a5a134e4f4d98726765d5259689ed2bbc9710db0eab63e213797625749ecff91dc7362e61d2eebc9ec4c52d61c85d722768f70f298575e898818d4a182de4ec5a22ff54f6a7842966f6f9aec8995e7e12a6a4245c6775bee62f09f3e724928a3f972174381286142b3bc456b37e0943c2103d565ece9c6727f711e64f89591e62e37bb28e30fde6d66f87ca766336c21463dd744af91861f42b19a62cc4fb092dc2203e2bc644cdb128170b6c28c29c53decd5f8aa5bce38930643353f1aa15c29b106150e9fd73def574933d8730dc8a4a5a627886786c0873685fe859901f9ec4b3510863e609d5c86b9f62458430f99b49902d6e1a4acd432b8c1f604420d9188439966a9e9cfbe42f7a4198637e1e8f53a8147126d348240c850281400c0231403cae00131308001834220cc68201896034941f1480034e3832483e36222820160a8622e138180e05428190200808848161188ea15892079a14f501c6a2f519398c78796186fd2b2c3e7f007a467d0b940f2b021ad7558c038d5427f5243e62f8995900774ee2ca7050d677bf8c9c906668c6a1714616f173a00d2b8f40a95785f23bb8941e696a4aba5558ba746aa1d5a979722babdc14861332299aab9a2f7c494beadba88fedabcefc6d8836338b8a8ac668c92cb21648aba8692ccca993d8961b6c8e911f0179e031b390ae523d2e379e87b6119fbf7d561b01fa10ded6f3feb170ab79afd10d9ef11a6ed8ce89e2574b971712efa61c3ab6cff3c52bafe198e349cc1b8918d5dde356dc023a695ba06ad9c1d811bae004b58b39d41cdd585bc8bd086ea0739340c7bf7c9c4175464534134a00f18756293610741a96f0ecbb93f282e294a0153b854e2cd1aa973b470fcf9533dcc432901be6026a556e5b571817cdee946812a1ffe7b19089523a3af971b8e4e9009f8299f6e8592f2fc841a655b3e1dce6f1f75d6712b1270012639129a85867adb5e7dbda1e800a7f70bea32b2afc82f09de67555a47d6bcb09058a63dd245e63a9c483dde4e2a1e7166d4891d183351c3dc6b62dc07a107e67bbe57099571ea32375d818a93fa065aa52011d07f194e0d827ebc100998a706140bbf03a1ff48a8f7a9d1fef5520f4321fdacbf171934d808ad120e023194d94bebdede1b4bbf31ab898d069fd55ed0c2077b6f9ee35c3b029c9618406bacda6e3c94837af848a3faf6434008aeac9b96799e5997a64aebca3c880cb808f4361854c2e2f8efdc1e0f9b649b6c253fd0dc7a4a4e53724eede8067958c9d3e25227248f2b4c9a8fac49bb6ab103c36778d402a2e08205c1d5413b88fdaa2d28ca74ead38b212be1050db2c7fe15c0b684ae142216a05a1a7ae86e657116abd3b4d283acb782a5f279da9bbbcda85877eb057a772e97ed9510e2fd2b93ab74cf4a26072e355f5851417df145c3c2a21075034b7606f3710da2c22a3636aaf8e35091bde86e4a2b1417d8062f22a0dc13f7bbf0cd20079d7e137348cb74e03d9618cee4bb7b8df0923d36dc415d1889380a59bd3009c17d5001aece633dd64ddcbdbce1aa646305b483d9c0d1b77a835e30aa6cb81e50318b3c4d873d031a690f970232c79530b05ddbd521eca2e83866508f2faface52a927892ed8d8095e4c11c98747203dcb19d122981e404ff967f5ff064bf9611c4811690758e9c65862806612d9208248a61e7f875875b41c26213adf60d26c876f4659b381d209640215890e1b0e4c497606167da13e093233d6a7c082f284ff4dcd8bac671465167c1ed2180d1b4372241ec9ec763a901863cb72c6e3c8c0b1df05b6c968b45e8b1ec64208fe4867e09997377c90cfa248f105537183a71060783e2d165a8f2df0a07627deb687a3f8ad7acc3be1a05346e55e842cd32f04ecfe5836cc26a9ecf10c320f77dd2bedb86e80d50c1d1302934064f219f4d363f94d46911c3bba80199b99322e7846808a599d3a232a0347025e6c5220f0445f45d291c9ffa092049e01bb1d82060eb88ec741a57103388679dfe6ded55ea7fab8fe64d2eed799526153fa7423cbb3360bd853c1132f620ce410e36301685cbc6510a5e744c05a5111c990a68f51f8e1c94db7b54998d9b2c56cbd39e3135e87145e3df3f8d312c1d472c07dad9752b94280a785c80a0799b1f314166f54f760e15dd34f49036a04c2fe1371c52ebb294013aaaa033e69686b9f33927d1c3b2fab922236e055d3fff42eb2aa9531ddd2e4ca8fe0e142397195e977593f154b7050d7e3201087e0e85abd68934267ae0888928a1908c8815caa2d2b97610f900a64e4445ef524306dffb982ed475cc9844c8160660ccf114a179feb8e8e151a506a73b706cf7406a53d8030a3f82b0a49c272885091adb1e9e72b8a9dc964093d105d19077c4d6a033f297954408ddb2981862dc409bb0dc80fadbbaeed1ff563620081abccad6ec715efe4eb1dfb64a23b3667859d0b98f90489e84d769d5395d49bcbe4e2f2bbeb12ead259c6d99c69cd4576614ba4e9bfa40118b20c05ede01996f101d8ef3979de0612368521b4bc5e5654c8afd73dcbc09de96573d6272c85a97ca2b2215a1486693579f7e8d3c1c83be880a4bf7008e0ced6bb4aeec2eb0c16ccc0d2b5633ff2b90ee9c59329fbfd7ffa6f5f8e7f52a86c300782f9d9cdaf96a043ae6101cf5c2a15632c54ae923aeb76552ad4ae9e9ce784a85766a799d8b11b443aada2a1da736a3d1c464157f60ba7c8eab410c190a9db7a8ce92b68808217647b9827dc3d6eba98f472eed73307c7d062f649768fcbdb1e0dce21ee2dfa4d5e2066b67d9a6d0fb64261eaf59298a2079c0152dd0088e766cf807550eb286b53c6282a95ea650ab5477ff809d8cb06bdef8080e0074a3c8441bdf74ce87f482100c8b57fe367ceeae49abc1f55a300f65c189e201f75c5fe642102d46fc83d81a82132c532f1d82d8d33635f944906d1f1947e969ca4c4d3721440ae373617c2750376611c1152699c4042c2f46d641d1985c7088c8c69408299d2d2d8b50c498d3edf31c27b3ee66cebfe4330222d9f9f339f8eb243a98bdfb868cd0525e272cb64efa92e4b565d9471db5523e82e4e144a75ecc7cccd9833ea54172c024437f1177dfb0ac20098c4362283155b7faf23c3b2000e7217b2da8078e5bdf311a160b611b94614d652e62ee0c0ed54c7a7eeed36c1bb95e08a59f2f74c449fb26310e3167fa861ff6a14421f87dd6b59f23955f603c4074a9db9cc07220af0cdb56013be214839105b8314040268a4e00ba412af21b23dc8b9b485688486e6885dff4762bbc6a120c04c1e4d8350be5807b420290eda2bd83d19624091676f758109261684e5bb3e5d0039c4cdb2d3bc074b3604983f9b43b8714ec081a80ea54cd1384010044eaa8a233f344b0700025981705cb7763a5584606e146a2fd01eb9a6d1e5d1dc27a02fbfe02f24208bc72ee84d599008bace6e9aa1b95352def3da04981a77c8956aac9939a78282ab601f4b40a42a7db3183440ffd3703001964a33df5b59ff1df0ea30f1b1f03a590557a4691d067d0c7665bec0895511c5fad4194ad5c594688b05129c6be2d872877ff26b1ebc7be0bcd69c9a06c867b2f63dafddf2d6b911b0d32d2d917551873332493351910839f7bcedeb9f63a41e299de4e275801e1a070a1d3613498e34d72312a3612f77233a4a7fb589a5adaee36fdb7be0b80d17b09fa595d564302518b68c6d62f57e7e318de3d5441d12c5c0ea02eb67b0cd09c2d13fd3b81c1ea41121cea34990b227ca065e88c8e12e9f223eaa731235797cce0a882b563d4bd194cd34dd87b5cb5a50c04ab6ebe381bf116180e505f93e31dbcce7d9eef2a7b9dd8dd0255a29c3ea1cad640d1387b8c63a923b2020744a7d00d21c8b0dcc1a1550328f285f81f976a3a35b479ce37087442f438b760d8d52bfb1ba5df3767b7c367ea07076cc81ed9c0ac3f5c708463733c800929be84f0d4194f9776ec27014a560a6907e79089a2a996a8e005d02b54ad34d87587f9dce654a7e744fd39439d526bcabd19a609bade44262e1ec029b56ec39277882bd714565b1ce843869bbc13c1b9332d7632688a5ee085d0a909d96f306ff9349210fb9aa52fa5d3bc7d21e3da23af29b2c34fdbe3b695cf942b763d49527f576af05d81ea2eaa28ae66937c442c62c83d6f6d0476eccdcec164a25e6ccc31b48e006ef92075615d2e5718cdd3df4244533920e3e2122827cb6098771ed8f8b8c7d51c6ec4766b3a964203ef1cc90f0a8e8a57d68e463990acad455221ac690ee8589403ff2c2153868a178c89439857c19b62cc99091f4b9f766759ca4dd9eacf4466bf22a8e8b4340e7d044d54971d1cb0c1db4e00219e83f50ccb63f1171870bbd694c3ca837a0fcf12dc7139597b0b082f94046ce53fe1b7f82a265fb5a43e91f1aa64f4368a17d27c3be27503bcd5cec03a67991367a3458ffc8f71b13fb5e290d85686c001ca3e0fa56a99217ec00858f0c35703657404400db5f3b5206749d4072394c0b884e32828ddd358272b7ea161eae03af361e2bf786cf4a459888d4af907544e046e3aa973c80d9ca5a434fba5035008c4c958f21c825b68cd057954538ba4d20d67f2c17d9bd7cb9efc8e5793942418e45efd937cc7a556119854dac110e2ddecbf3c3d1201ef5e6c8ba4cbc10730fb3521726ac57018301031d58aa82e648766b4049be4f5bab40868e4199b560ceb3340d5b85037f49e07dc939f320052e6d6cd283d2089b0b3d7d66eebb15e3bd50413f302f45ca704359c02ffc92e6d441404d61656e406dce4e0072be89558d38d4dd9455ee9ab5666fc2971ca9a0ee72905ed607cb81a791c263cd9bfa451958e446a9776aa3e42c1b5f2cb15ab44f26e8688069c860591adb8089b01a831451703f37c311c76357e868f0ef9ccaf236004d3f9c43ab6a07b9895e51b9ec2c7248b85a9cc63a80b189d1f2a3807b350846a85c958764aa67541bf24a7a8069cadaee7e571b292dcc80c7b4ec12276c91ea37a635808325821f04a903f50ea2d33cc868e74a0cabb201f5a16503c86a0039619f105dbe608941ec405fc15345881026ed054040d41a9ac97e6f796c0eb990117bb03326f39c5d6f95f85cbab4471b0dfcdeb5ccf5fb8233e4d71a2413c76fca2694063c9629c149690642416a8432729bd6087527bb2824211e40aa40ba422e11485df6505bebf8c41c149ee91b96c6da85186a256e2e50ae906b0ffa21613952a552e294f1b5f076274485f4772e0ebe5b2ca381c69ccdce54d46829b407ab7d3485ac26617a08ad59a28ec44c27c1f214843434b98338761d2073d2b50cc9a0864b2860f49d56a92f1064f9f3bbdc6fe62a3397172899d10a74c2873cde3f259230d57a4be8cada11e48e8ab66cf5f94fa33409c5fc1f8f6a252d4083d0ff66b1c692e9f794e6a617d316586df3ef6863a7d41aa040407dd8ce20a43287af36cf7625ed400b8d1db14f0e7bda32418aa03c8c6b714cb4c2665ea00d3ee17ad218f5a64fc1aebd835b3944f43294c760ec59a971d6b5e9956269d470d73692b137797401f4eda22552bc11d83f5641a2de431637da6b7a09f83332c2cfd55d4f8748bc39931fc6bb7a40b746605bbe7439c2e82589135d54e4f387e212f39432a856f24ee8881200673e124dccb10eccc28f3622f257cb70eb8d6c28e45c1e4425ab09ae90c7c5bdb6b99d2050a8eb16b72fe096f52ba86019e7c0e041fee1738907c0549dab4e352f598d1f747223e45f0e7b133a0ff8d5c33b946e1c3b503a4401cf3e39defc84c02add5f908698d729d173a98854d630480ef451581907eb5833a549ceaf4ab5f662ce4ff6d581cf3bb200651993af6fc125d44b415e4b159c3c24797a13aa8264aa96e7eea360486a8f904503c2b413aafb5051971ceca39bd6a9dc25f00662e7d39520e12e33e029f2b936fcdeb76e79f211be94076c687ea3dcd4a91790bd556d8159117de70ada5f96f54b4b133fad902aa136a8574584b9d6b62346018d85a460e6823850af809adf4a3aece520612c4cf0bff3666bb92aab50515b74e03ead83ea6e1733e5e51365346ca80295886a75bc921cbc901165a2b0bc8581cc02b1c12503d902a698fc37fe3542c6f5db9b052d79b5baaa9910bd0dd3b93045c0ee6634d041be23a2e4ba5e62aaceec833d3b0399afeda153c888ea2404fa1f9f5a08152c1582386b503d87c57560bc5929a424daefd06d3d51f984b9dbb8a3305ef50a890c582e6a013d91e171cf283f0cb7e3c3e4c95ca688a00ce755cb8e15a39a63f8518df1748cfb58329a74b48a9f813441517cddfdd8586113656c94363ae4474b9b36c5af7b77c00df0e1d9a8940df9aa007fa063af51d68a472afd31d6fb15d574fc675bdf2ad4cb34814bbf36ef99070369a8513a470cf507fa88d85c330740bf2868560439aabc8d8b13a4efcc9fd0e1a85e8a064c961c8cf1f5880c1c65fa581f14191d87dc43af7eaf79f3723e6030e6ac9af9b4be0881592c0692c3cb17cabf0eae3e2ad15bef69688f4c717be1d70602857c1639ed4740ac7cfb0febcf90b7989556e93e1778c82a0f189a92237d7f5bb00d7aa803339424740c619d185ba3d2d6cfe72d3fcdbb2473d3214dfc18a32f6237a6172a2ba329030a93e6338d8bcd949933f1ae8a81f340af9b06d5a193a8abd0e2d42d2fc1e12d39b7d4414aa90b6dc467c02835e0320eef37feb9ae39e53003c08445b2295742c64a924042a1ef1f326628e9bd6150b92e96997bfe356495f9931c72edd79df8abf8eb55e91e22862de1058541936a0ad4c4f3365fdae44a9b59ddd9e467ac0c3293858712fbb58013d129230234e1b226afdc3d176fd56039ec97ebbac14a44fae56c90dad8f93b1fe318c10e23f38c2d3dc2badd5e1d8c5be4ae6c3dbd6c3023c7559e4b61b58ea5241bfc96fd111d6ed237b8b4c82d4d662a25707f9bfa1b6050ada6d4f7de28ec53063c520072fb28615ac1d1abb600724213eb012e2a07805922ba6f49c6216b0d507ac230aeab3eea6fad66885edfc15f4ea4a67edd55d3923fcbbd4cb0ad8a3c661b5909d462edb7c827b3db005347987606f6edf86d1cd6f7a0657846cec2cad9ec923c920596fdacb9973909e75c9554084f1666b48f6c70a115a4d8ccf8b1079d51683c890510c349012f0cd04ef7ef3500987bc5d2ea1c23d7dfe6a7f9db770629550f5fc9089064c52cc72e70bbeeec9cda1428b4d5a7ee5afa2d8a28add8e2552269ddfc634ddb685363aaad163d669d69eb0f71c4861c435849c5c899d840c21e3369fba5dadfaf7688678f6c7145124290f436ec8977049213b5d46c7b7fe9b4de7565600b1ffa452b3c581d3f8965f52b6d7a51b39d30fa089ea8c1f442950cb50ded094d059451e21725d1c7a4bf37e5876360780b475768197f0226066ac608e60da49d3018e190d729b12069001bd5cc21b3780bf481bb6b4ab2dc3d461c996d02c942692d9b6dcc4bf707dc55c7967aee310a4248af4904b3922f9c72d6af2c070f2bbc304b81cea7ce26902905285e6b242b92cdae67b2a7de1e856d40363248abcd83f3401e713717c68a7cc59b1e18b6e4e87971b58096fccb2a0cf3be02225633f353f177e339595ac25913ccb8f4d36b0714ab4379273521d00079cdad895ebbc9028e5a0af09bf1270f0b107dc67033db1f383848679e7f909138bcd068dcca8ff1186939817a556144e85f6611325f17152f13ee1602bd530017523227ff21d8cdc1b22f561d79524b1683de58fe7995641b713369c98826fbcbb5dcf926b0ff514378f5bc910d5fe092c708b85813c5b0b580d7a494ab269f8550ed780fd1ff4e2b40051da6020906b1dca60c49aa6dc00584f7e38f600860856173e1e102f76a9dad8f9204bf318ecc8f73324a61864de13e16e1916c91240632fc941cf721bb06a74450f0399d82f036bd6c17a1b4d5b5551d0a306286ad6d08928b7e04d076ecf82bc946c7e4e68447f6d83380d11b1a70c7036e47992aef952411b9d918c46a8f6ea2baec4a7126ddbaede4daa5512fe28c945ff874f96e419dda5ff8cf9121f64034629012a17377f072bc8e6703b6ee4cf7839d77cff5cc1324ad1d51f907388d409d491c7c790874ece362c909cf5a09eb643b047a459bc523d03752edb2ee8b6afc64698e660d7f95d8b667c9e8e8ec90d456c6641eac26f4957fc6f4d08dd0a9f9dcfa25d205b1b62a308b682cfbbbaadc0616a63b380194db67221ffaf194da3d19094971a7d5f7512dfb99c3c169d728cc073f00a5548070f9607d4194c8e9c1e3faa87b710ca97c62906004e9fcf70b668d311d7076e312c89291e49f54320f5c855d6ff88ae78d3a6c3100031817236c3b8fa20fb96ae91bfaa96488e087bdda62b2cdadd4a805c41e77be0b0d0cf4545d0208ba1979f0e3570ccbd8562dcadebfd7848687115613b553a28652d85f5d58974d0d569bff5d3da4608107e15b344abcacfe9308149833a955f4e691fac9225e06271af96af3d0972514a0dc50b4d6ed3ecaf65e9556399b62ef29a3ec459ee686efc4d4ddf82abf38c481b4f40389206c0be26e56a0fb8e9607fa70c39f468919334c465a40af198d1921aff582c3e9066add5fd57f7966bc0ef934c310334589d5b5c0b6161684a21ed08408e80a363d614f89c3eda2dcb7ad95809ca0f3cb085eded98718da407797b9f8bd530f7d680639e1e7d7c8b20319fea5fba1707e3d9d52bff6795104343aceb442c7a3c449ec20127a8ba4ccc1847d36221683bbc5e3e2a5ba6d87c93b506c4f6bd87d048576ad89b1ea8b241f82387ee7bd612f970acc85a1e9e309288c223580306aaf8a886095cf5b014b4bb833ac896acfb55f5962f378786cd2f878f86a1aa761f4e18dce6d7b08d2436107a6228b2d899156669f8526afbb4424275e33dc0e5ab79945cc1ac63595185e6ce0f213a42877d21e77430cc600b99fef2ae1af29ede59350104ca85049763698f9d2caadc5c2d29bc9b4a60ba7455238c1915c89d99b73a7c5514a3b2401d4aa426b603abfc2003db129fb15a7e422f436d16e3698e8b934ff45cff62b38f8244395765811144b9ee45eb878b1d92fa3a3de4249b022bac18bda8e1beca7a35fff2b8ca18985446b70c72784a822b2a28c4446921f259b4b77286419066c01f3c1fc5a820c059c1911855c5188f8610268b644e0e23c508545ccc3f0ec027221d2d157f21109280ebe52fa04f8fb7bd0515cccb03331cbd530f4fe1ab46d4db8be9bff99959b806841e6cce217d3abc6dc509685a025e6596a89a68e61910609120bbb94401f82223ebf72de42dc8264d09f793e7e4a564ea7da27091a83126811195b7991da117ff524e46abbf33811be055af00cc1cc873df9ba7c57a818304c5d624b67fc15d788f26049de2a010084d62a9b1e45b09bf3468c558c74181a55919139b33c73a26d073ec37be0d7893da7285a174340d5188167d5d3ea02d0f86f19664cf4e9f6fdb9acb79672605e597e8bf485d1b8a4bcb1a4012ace813133a4cb8a79f73055e36e623aee1050638489ae582268bddbdb1d09d692fc03c80ba4ebe1d7bbf43a1645708723f875d236833b2bb0091e9ce19c3f5c1ddc284699937a8fc52246081cd3280271796d7b8131b8f22b45e03cb059b26b65ad861ffd5b0e8dd73429edd04a3d7cf900471e44bafe85752717900e6fb21e2aa409d215d0cb9e601f429480708783d08e682d8d6a4472ec9e22eddc55321845c79e5f85b182ac869905ae3612032485c5207222bc410a937e831b9d139b128020ca00ef10dbfaa188fb9b2ba4ca4d29a344cf7f501b8766e0ffd9ea9608faa2014d50b3f90f2679d322d28e4dae827c9038d593a6fb8fdbdadff89625f9b08e6dd83b77e50619255ca22892ee13b30fa452dc36703c0672c0f03b42554a3a4b24cb693872c2b3a191c64eb802c270f39a24e0cbbb52760c34c5f760a25ada119485d05e0143025dbf6ae72cda802d6586965fa14811b1ff9dfe7aa6099dbed3d52e30b4d0a45f89cc85e37e14b29a3451b0d322a255f3ee42e8ab42ae1f0f7e4c4b75ec38073e7e9f40fb45b100e7ec1344c52f4094fd58b934764e7184fd6454d2cf11d2cb427c3097726a9b8ff1534ca5b864e0f1094b9fe3a5a4d474a197807c828571b7efdb2471351b52464828c81259e6441cfe0081868fbd2be03a5b68ee970e0d04c3c066660997cb3dbc5f4d9df1bf9d8f92267c05fbe77e428cb21fd4a496a16f5db1dfd4419f27b16768c5ace075bb1b1ccd0ef3129def9413308a47bffcb9e803a06095f3ec1e8180f3a2205f70633501295ae5edb84835f637ed5e27fb1a409fae135ecf230855e9ebd44d41b2dd1c7d9dcca4041a8ab1debfa670389a68f12042e7bd24b2c987ee51f6542bdc2503865e7862c48520d8422f24790e57432b27a48e801d41c63769967141a164b0f2e14ba6b185f0584a9e9bc35c289443a0019d4a20e543211699cba0809bb2e121d83efad3c75cdc6466f6d7d7df8b58d0e400fb392712cdff2f2345683ab8a196f6ec3c6268c954767857c30a5a022a4add22a3e86deae3a3334b4cfed0203362c16112f26532e656b64a06a054ab5f66ad95291dd992146d2ad17a4d54bd2e7911de2d9d5872c2e1ce51e2d27bd5efbbda96a1f35a06c98d4572ef7e0ee2148ec5d34e9fd30cecbefd8bae04feb20759e8b371b997184d705d16b021a837fef13f886446a77500f43d30499040271006facf55a69807fbcf42f5db934aa8c5cda5e3d9f54e4ba1934f014fdc7b97639f39e69dbaf5eded8c9707755492dad4c7e4fb093d9276bc60d448ae4b496d5ca1fbaecfd9b34d5487400fb19a943653be394d9863a99bd0624938490331cdeedc69d02422f50b78775b12812cfd1e5b0190ce0b05489aa5ed55a24238d5fc195b8ab69988b7665b0561e9ee1e221a428c13e41ae9bd2a2a816b3c48f92f54bce8a696ef0c614d635720e2b04f6e194dc304587d3924de5257b248b276fdc75c94f65fb6cde572537c4cc6ae22dc0935699981c82c0313c9f9b38f7fa1d06aeaca90a35c16501313940b0ec26b086ae78ceaa622da0bbcf2acd53bc47742eec94c127181f7d7c2525681f6003bd17e3d94282a48b03631784cf5e286b46a04d5ddf98b771a36311a809f8e61c634932f543ba94c13351b4c255e31ded150fec8570c03c387fce3aa88af3c6d1614205babfcb3e202d24e7bab24b1c61c142245bd291b3cb91fb265c8db76b02e72410e9918d9552252ae44c1325cfd75af25516d1451a7979a0107877be6d86a4c0cbb477570b385f26a11ad30c0ec25d593075229f71c01eb6b1fb05a9d9fa95abf55bc201050f21505d3373c092c856182b3b2683859637edc2a37612d6802b1dbdbe954a9867f1f7ca853f224ec5271af32641641804879f12969b47ca5018314bacbe5c7c766012d6b381267f8a99e540e1dfae35859b2f929bb57fae2a0b33c8fe559a801bce717163979e6893e119c595b393d9a7af9912c7344c16578013e4153757cc5bd7961ae97c393ed461b87a8df6529624dfa178424341baad881cf3a538c359e092bc3238cf1c09aa1e6b2618b49fe188e45bccad7904d6ac71fcbedbb7b5e30a2831fffe5486a51b14bf234af6853e6fb7676fe28410308687dfd688567b31e8ebaf058b41eb50bbb41ade014336179446b4a64c825e092be164c64bb83192286a0154d7be8b031a994db818a4a2289930a47fc138e603f5c1c3d6ccbaf45a50c67a8927466e12ac9db5aa9070a87bb64764c5da533d44b0485a14a7fb34ada2929ae8b248ab5ca8b3b5f4eabac061b825506375619965a0c6649e5658cc5194101e65cc73a303919f7dc8952ba717479310c2b0223ef2d1ac5b904d81505a85bc43849b0b2749882cf99f1f35472dec36902ce2720d74b1496907486e85c399908b70a1ee2063eeea2efab3cc25033e3c602104b20905af43321e2ad6e0f3988728d1021cee45985da080daf4685280eb64f01938209ba9b0ce41fedb06cc7c38afc1f678b18ab03b40fb1e3f6a0bcc6e9aec80857b9ccb7a0e44cc2eed5687304c9f6701f17690e6799fd3e24ede9368427b35673a4e4f2af439eba8efb17347c7b8890ba80ac1c1f0e75ee6217565490512ecdb46b495168c06a375f53a1c19c3f06969e20abb1d0dd1cb05b2b8cf582b064c779630e7ce86a761d0bd8c5581dc646b3161f3a67ad22134ed3f28a8352a58c1499d58661ae6e437c1db441ee6f2fb47fd1ceb92294ec198dc9cbf1dc39cf27faca9ff02596b610069dd2548d7b5ea26a49c8f06e161c02100cf9771d24aa5c2b9678ca16d93ae9d4b1991ad3b7e51d47c5a6204f7f4ec0f82d1631d770f1fc8eeae7875ffb725fbac188d2594ab98c2e07830d01131d9c30f64de216507cd1c7e9391a9bf05b059728d181cbc081363a573ae9cc039ec3200cf49f42ea739d4138bcba7359f85f234690e2882addcb5f17f0c4c9af923ee792a3e1a6f8b9d63fdf93304664f8de0a8511264590ebe0d6002f21567f9702190195e0b89e5b305d3493e5197223fc008e2bb7e77f1a012b8250e042ce8d6bc84fe12cb8bafdc93ddd11a6287a914a3e0a750cf980c3f2951c7ae9061b0c0ba4704f708938b4aca37591fd67113356e817e5fb01f01cdc7d7994dea21f4dafe834e78d63dc4de1adf234fd89cee5d5d051cd0462361408e3b72b281bdf5be084898b62a6267226fdca86ac44800a48bcef7743b327c06a8e089e644bfca0fb44a3c8c01c033201391a6f10f037885d38aa102e2e5ce86368e3c4620888e9690017fc31de6db63af1ad7f987aa3703ee240ba57fa54ab3cd014c3301b357d8f2f502e87234d7b0e0424b4cbafd5bce856d41ed1b654b115916d826f2f337596d5afd2ac59fe1e1fa2217eaad65e181c3ade97cd99799612dcee7d8406e97f505d7861c468075d4c40ba8cae028a5c80f50fa722e586bfcc7fc369c735a7d128041ef69adf3410c075342848f09a8193176330cf79d0aee0c7f8e82653ee31a7fe159fd566cfd4c31f74c60308b16c378e2877720d8702802dc651a1869f48bafa41f4f00377d91fff74b8be07403b73cda08f788f7579feb2c2b7115a7597853ab2ab6adeb3cea58adea3affbf58619ca8b782411d5d3ea6594bd6a2f027b438d951b69c1609540effc53544f9d0c066f3805ec2b7647f13cd8aa87814b94a7b8de0081aa860c342a547a1c9a1bd3ce29af8fe3ee2303b33477261eb19c7c691f4e875a0b177c4d08992d86762dfcbd007766e970d329c1f7a64033ec6b9b5b9d6bd7add45972fe72e438a5dc391c0d9e9ee1c11c7761214a5e09b6f7232ff80996850655dd2a522c75f9c992138183c84394d15da0dc1c997d2dd3712c716ad9932a6e4c43740a62c152f77ba28e3bd3189d8e58bb54a7263ddb81c44ba6921811384d1404afac4e8b82a986bb8a35c9c0980cfaf6629be05354c3049632240c98a4dea67e6da2ed6d3a3cec35f3e13c2866c429c3438b22e26743019e41c3f9e2bf018c5f657774a38b7e10ad415c71c6b68b00b015dbc23e9079fc396b93363df87f1f21255c518f2de74e46a6cbe506edff1ea4e9bf83adf86ea5f81cd926af73dfefdf926c4b1f4fe93d2e2bdc81840fb4753ceeb2c825da988b208524e805632a4ae09eb91867496a7910a4fc0f3e5ab0a807d137a07d518744d59bd08f9c64e941a8d2741a6b801017c5bf9508aeed2e7f7e74712773c192e71759a5058daf0a20e0f4d5105c615d9345f721fc371885e710fff258859bf3c46e103f0c8a2e9a8ca6c83020747985200b1ce1c9ca9822a18b4122a5d49d1005b4bd9111b66e5231ffe8c055b0d8aeae083d5437d00fe4b6d24d656042d86afb8a29b709a9c39236c532237731ab5b7c6b33f0092c6116d262cfadde1d592ef684527efa3d3c9660559c56f1b08975d9b6ccb3686370d3901b357b9ed50b3fa562602a6415ed91b61c1757b677be2c7ec40c36513218879d100a979be29a535883e93bdfb358f6afe3cd3fa6595afb8b8e865f58028e4cf7761ad289d6a96974e0461099c5265ad8b38cf866068459a9d856b3bdae102a3d200e35e50d09c64a000bf55e39e0d3d873549fd75166fe6d278494bf19070dbc26ce7d04ffc1719a3f000a14a732c33ad5fdb74748bb183b15f43f9b562996eaec0dbda70facef0fe0676274ec9121d913742d47807838bbcafdc981602b7c858970e454fefd33e3f889058a36cf6cd9ac60e91adfdd731e8dec1d1642c49ea28c682ee7b6c5dc26f7bfbce3e53f6d9ef185f97de64ee004c4546140f3955a6710f9dfb2324c09eadae806da625dd3e077e8061bbf1852e434527ecf84d9ed6a91200ed468265448cba43776e39ce05b144bfc8b9aafabaa650157c26b00a8bc52add26e360618072a6ea8d70d4c9a42403b84f4d9c02b48e4d4d75e44f76fb4572b5a8924fcae9e449311c16238133925eb1dc3484057209a606753f5ad0909f963d36712f19b4c2ff2ca74ab425dca4149fee51d52638bd179d594690bec195d248df118bd0e5722808af1eaa82b1eb7dcad864c3affb88d82b876438450286ffc60dae416e1530d4c66755e205cb8018acd40b98d03c28130c904fa7c064585b640133c85d812a185336ad03365aff4dc51d04bca15ff7e5ec6f1e7a3a09e9587bd32da6db5aecfa58c4867f12b679a68c3ee9463a596a2c1a26d00b7d3d42123135389c834fc397748e22eb1cae4d4b967d086a1370121afd121c11947bfd73557a31d9d0b0665be04562b1baff7d84cec6ebc38662915d88a446c8481c5ed6161645977d741dbe6ba2ff5d731b2226e6216ac956e18d8abde4285e35744d3179458a021b9e5d63e3aef0f699fd900034869f83b8f2043e89481dcbf6a06c0363525700494a5ecef1d13103b58401a8c151ede63cf5cba4b1d12a394e1e5eea6cc8570d58ec34ce52aae51406957dcc3442d1e848ed3b4623277569a4a3c425b0c3dacd3c2eacb1b71ea01657c965295ed274d247100a7cef25acf460e35e98074190fad49eda7fd0a66008bb09debf6588f6ce330cff38d6b188087c2bd2021588838cec44756699cc45bbaaf49daf36e0a6144b5e46b75a1e77b4a9fb2afa55b9ef2544c485500b6487b927681f4ab81fb79bd1eaacfee7b017ddc9d8449fe2c4df4e0d6fd74b8253ac216c7d5c98848ad4c6da53b8c1d4914d2a6d287c6b7aaa37871701d592be3ef88a45578f4fa87009054337a5f7aba69a9add5cbd0ca9ebfa9bfd0e54dc199354cd591d3c6cd7528882fa9696ff20cc63f26f13fc3b197f0e7aeea78b64885272f1b402beed550e800418294f71b4779bf5554c874eddc5b86c25f20bd163b97bd983bd7c0fa788b3dfddd02fb60715d1b98d67c46599a934bfbb05482ea93bd31f69fc3884260bd35e89c750f59314ffd687015bba4c8f9be740e0bcbdff595e6d39a088dcb483c7132e3b3a2c41b1060889686afb58174cd00dab557508fbba2fd7326e75b3d7ebf055e56833970fe303988f099508efc28302842a13e052101e0b80a00435f392a7101cc280638029dd0d1111a3e9233041813608942b20b09e3784ff4c0cf087b15abc0e2fbf050b8e141d3664b6aaa975e0ec6616f065a47901684d3cc2163507a7031762ba3eda4e5bb2f911c93102b231981df4f20960e752ea1bb42ca18e5a32950d5d3ac0694b2486b0042aae2a4ea4625d6268ec5e5ebea5da4c0114b59ad46a11e69eda202b1468de880c0339ab0e8ed73148bd3cfc24f01bd09980972fd1780ac86b215d1bf7e139304cd827a9241b2ce9831ba6b8ce730c7d8c6dcc49071e3becfa9767c8e678d410b9af97355c4fcd8f27a5a640318d504f8f85cac08e70ec0d0e19b14341f0642268d0c5dde98541847cec8be859ee8de34ee3959c7f8ff66965d5a045f60303264b86d391d6825d608dee0001bf8c987949876a5cdea68cdfa0948bdb027056bdb54738ac457e68ae9e12d00ae15659a9c5dc3055afebccf6f2042255627583237ce64ae15bb87967a310797a8bd1d8b0ea2af40fe129f3c013c1d031e9045480e3540b0a6d0013c0c3c0c3c0c3c0c5c7536dffa5afbf85afb32c94d060328570e91524a29a5943c5dcfffef2bac5922fdb89d60df01b8d70e220e120eaa7dfa5e075b2efb2063077a400408652df9c3437ba61326ae821e3ffe0565f418418e1c39729061461921bf28a91d599ed6e194b43b437c51ec13df84ff7079a344ef4541c6dfcf744d6249b2f4e351f023080accf8471020c88bc2dff6781c212263a48ce7c1e6a347a216460a38701fb28b92fef6091e5ddb3d85548c105d144b26699b774509a2a71f8f7ef4b80b84e4a29c4cc99f43eb68dc871917650b9927b7c43a25590695f19981905b1447f9a888f53be2b6289f52627a1cf1e4c1c2329ec7c8a906426a51b4fdec31f5d42a95fd303a125a14e77478f5d14107ed263ad8d67e0401328bd266350d6f9fb36a92e8e011203bce5c168531912509c26c7d67d4c1a88c27e375f8303fc6503be39058144f0c5fd3237b93a9820081454147f6dc3cbcfa8a8230c1eac3678792724b57943fb47d8ea7f4181dc4561464d8cff41373e97dfb9415e5ce3fefbe1172a49a405651f2f6befe7c2f41358223236398711eeda9a2a07a714a5232a6830d191f98232415e5249ffb8bf87ac90d1e2aca299ae2b5aa1b3beb3ad8808cf44069fa2308103c4541ac44c6262d1de3634c5152f3eb9d3edfb9c51e528a629dacf0cfee9b2d7421a4286c12cd4fb9ae8c1a8ea25ca7848d90a5cf84d1340819094551da1cfa32b4da63cdfa31c623e3e3ecc7070962c8f8d16303de21a1f864588d887bd867668c042123f50642409129993bcc2cccef1dc935232de4139527fbf168824c075b88278ab3a159372819d484589d28c9e8239424c937e531a4834d5160d641c8484881104e1493e416faf24267f670246d13a54c1bbac9947479d2e9c89420470e203f7e8c104d143eec37779c641966a065a2f499c6946a5cd97e8c1963fcc080eb085e30d282104c9c5fda29337facd2d120e412e524c9a8485d0d27bb4d20c4120671dbf9d6677e1e414825ca194f3b76d0d4655b8e0069a34479cb939275e44994be63fd68d4bc31ffa6834d21102289b27ab869f54e4522512332960d89529eeca4fdf45eed731f64ecc0904714b3e9bebdfcdb112541aa8e96eb2487995448238adea722931c93e825cbc98e324c0972e4e891cad83112c288d29796937e639af025c9224a9fc39d86a83a791f3a78544439dec4cd5d33c4c62582188424a22c4289a33b98b618f172e4e0c1e39311bc60c405218828f5b8565acb9c9cafe732309023c71965989043943ea8d53521aef1b91d3f72a00c0831445648214a56aa9320e5cf444fa703e4811f2184c04b63d6c636d4adeacae498d94e4a9fdaf977f8f81d3e7a241b384118f623880f31840ca2f0aba97dfb3e89568d04420451f03539b67f90a3f2203bd2488f20231e080944395a674e3b52c3067d1eb380104094444d62706dddd3b1d23f943cd7b86e585ee6fc506cb913ccf47dd0a793fa50a93ce93bfa766d0721d4c34d10c2873c3b45d326c1e4a01e7b40f95b491f7ed53e08f5d0c335fab47a34eb4e10928782ea38aa041d7d90b16304fd0fa3034873081efe127feaec4fb44a772908b943c174897651c2e73023cd410f1e3c8c0e206c1b6207e6c4b05755b6996e0f327afcf0618782903a1446632cef9c33c99d355621742896e7581946891e7df23818e911046d0e25cf7e9a27747808598f243b82982040ce2c440e85d71393febdaf83ad3300e4839c7174042f18b99038b49539b9f989591bd7c1962347081cd8d6b54c9d7b2f07216f38811b4ed0860a61c30e1f18b09035b0400d277040481a121082869104849ca1472ac3033c78f410438c078498a101216518d9e10303090821c32765f4286364870f0c7c52468f03848c010121620072c6db40012161d8e103033fde063f7ab080040a0801c3c89bf16698a08c1e3bd08f318649c10342be00c40b0908e9420142b8306280902d848468c106490f1e3742b22012828500845ca14688154676f8c080f96106066e8454a160ff3934489bf524b33ad87a943172e7215428a8bfff2033f6ee29ad0e2ef311bc60240221532846b7fb12fcdd7c452152287f89d29d674fde54734814ca25c9aeee5df7ffa5040ac564428fc649f3d965f384c2e6b251ab394e288870f3b10dcae3c5d88462ad8c8fb9ca8472978f88273f3107cf9650cc647667adbf0e1ef911c447f5f8b1214ae0a412f49d1a5faf2c0c31c2383463032708e3f0109284c2c6a4c2834c9adb84db4b084142b9d49ea9d2a349a89584902394d4a6ca5232fe7d5ec8084513e799d9731b5284f248bb93e4d493f3ae2342c96a9318d38cb4d7d72e6408e570b28cc79c124e65fca2102284e2b907f5eb99369a3b288404a1b0664aaa0cb109014239e5a5de096fda2d55441abf28ea8f3049aa9a81f0a0e18b92506277b2eaa917e5984faa8c3a5a2d76bd3bd0e04561b3c77df17eed40631745318f1557a5a7dd73ba28967abafccad1bff97a1a68e4a21c231aea63f43393a33470512e133e64dea8656738d20884c62d0a52739bcef15593c9a911207e4174502c880e7e0488d9a2f4354a9fb2d1754a5f8b72ba170df7cff1b9a145f1e48534bd1326a6a75994459ae99fd3d8eb709245318b66508d2757dde76351cc66235fe53decc48745c1e784d9bed20df9a15794bb7f477d6c13579494fc9fd8a9ac3d666f4549f2f38b4d67b3a298e55d4eab7e8ad9781505dd3b193d72d3ed5d55143ef73f5e73fc9837a9287aa5989cc9dab9754445b104fb34266c788a72589aace979c2b97ea628beea499fc4fa33d973a3518a729e3a41c896983c428806290a3a8490494ebda97a0d8d519474d04992a284f909cd588086284aa579e4899fd7953385a22449d273ab869fce290245713c773825d3bac712fd4449129f3c9d09f1b77ba2a0434dc9661ed74f4350d0e844f947abe95327d6bf361d6c3b6870a2984dc9d5249320e222db44f1d53c77d6a07a9d952a6868a224ea097b1fda34058d4c94b4fb79d0d4d472f3395ea581896290a3aa63a8d635d5250aa3fde336ea8312ef6149d0b04425cad7aa495c9fd2a995fd4801236850a26025893249e718861861fc6fa0470a120e34265116fd246e08a1497a3691444166936f3f3d8d3e31fb3064208d48946a738ee341d490286e9e5ece8ce611a5b036396d7a53b2d9ec88921276d6c24abcd0db1a519a3bdbdd64a145c830a260ba43986cf7ffa44c8bc853c9922335bfc9194514e7e3fa6adba6e69c24a29c314d6b493f393e5f4454e268c9703a8ceab90ef1e892fa3fe97adc1f0d51eada20afe5468b30994294248f25e4974c72de3809515211e557e2e98f3af9411493cc993ff66a2a258782e8a4118892786ab6cf7793ff0b8882f85ccd74d61f8c9b9478dac3d74b26f143fa33b7c91334bd8c923e6c82109eb4492735a4880ff99a6ef8de18d53de41d547dd4207fd703324ff6f02c4f1ecec3a984f3132c9326133c54727286fa77d82ded644e5ae2d88cec7075f579272ba5c23a1c575a6ed69a63b3478792f6cfd983e59c92433a87c7e4a0be04d34caf961c52925a569b85b5e75c1c92e4bee764577127dd703079c9fae1bdf469d3bfc19149892f7292ce70a51bca6ea63a32747c028d361475e3f7743e17b91d67366cf206cf915ab206affd8365c6d5500e6f93ae269f864cbd9594af36254e101a8aa272f4276d910eb63ae34110861861fcb0f661c8403d434989571d773766889598a16c93b379b669cf67cb50d0e973bd234c12255528a04186720abd6d629f5607aba00c129c21193bbc0634c6503ad14eaad03822f23a0ad0104331c8ddf598af3386ea0a136884a19c4f895d52cc35ed361d3cda020d3014ec2441c67fd198748849e30b05d3b9af33951423dde485c2270df723af6e626acbb0a3d18584dd6899f111b53dd21fd0e042a9e4a04f6cfadb123637d20399a5e0d0d842d974af75de2eb19a4b8c2fd0d04279354c909a6fb7dadd2c6832886ff64a071b1b0bc2484108c240dc010d2c9447bdbbaface84f2756051a5728f58ce9d64d9d95020d2b946468ff8df953b3499f2a147feea3c6e6e4a184860a2531a5e47b7e279d4a3685626fb0d3257c091a64ea051a5228ff28d750d1dd156844a19c35c99fa36cccb9dda20185f2dec9f29b5f76477c46683c01bbd2d8ce8c99f3b0de99c49ee7ce55d17042d935cf84f97c55ddd1193f82f80c6834a124eeb6fed4989e0906349850d2e92b367fc814ab5b1829e08006dc0c349650de9ca1f3263d269c749786122ae962ef36d826061a4928a767d2f8ab3dc9f33c62677da0818492d0ec229b4b64b24f9d25681ca11844f796101d0e842146181b08430c0d88d1041a46286afb7712eff7cabb9350a05184c28b0e9df33cd98ecf3ffe0cc3c36910a124e9e997ac26dc7e85348650d03bed31b27f6ed38910caa9fc3f9996ab6c111f176804a12073f0123e8f9538a6a6018462583b39ece90bf3c7f330c374e00d60fca29cfdd2539f6de94f932f4ac2bb4ce79cab4e8d75b0a5200c318e191d684080d18b72525a54c9871b13d3880960f0a220838f9293593bbcd43ad83a0818bb28af96ced334d1a4d2c9d34541a9ca49933ccd9acc80918b52069904f5a03d682a1d1e407e60e02c29e30c2029013070514cca4d5d346eb078138c5b3818b638306a513cd9d489f7142aee01e95182325ad82c0aba57832675a19545316d89acd2eefeeea95814949fbe10ea049d1f755894ac4b694cb9b19bca1cb90318af28e7572bdd6d25d33cab030c571443d34b79cc79a21db7a254828b792ce1d921305851dc68eb1adb2a3c3d6715a53e49f7efe44927ea070c55946284d23741f5a928493a0751ab38796ca42360a0a29459e4cec987093da55394529334355a3be76d638af2e5a893bd93982f6f2b45f184e6b8499c4f2ea35e1bc0204541cc56bb283d3289761a45b9dadaee9360cad333828c9cf123c89a000c512c4aa3499ab1444351ec93e7c49a0f3f9a732000031465f7caae8c615927e7e800c6270a27a63aef7cf67f4aec89b29d0e2274909b533ed589f22849989bc991effb79303851cabcda326b93c963b4897297ec9b2e596487de780cc0d044b15a3608f3f0373a9899287fe60db2b3c7ed9c8e063030517e13e68395ec5ea2583d2e1a931271994b33806189827c9f11677a9d94772a515aadb52e71d5372ad7c1c61d008312e5ed93b4fd6c5a0d623289e2be8d36934f575989e960d40018922869ca8bd36aa34894ac3b577c29d122e5a3830d12a51026b98d90a9a233bf07301e511c351142737c3ea78707301c51f2dfdc18ce7b3580d1886296fca074775f3d857ab829c0604469acda44bbcd227b4d74b0f507ba9ccb67adab5d77d0c618caf2fa2786ccb93bdc440ca5396df2e77d71fd20b51186d229d98498cf26c55564030c655fffae38a165fa5a1b5f28962969eafe3a86d7a00d2f144f676c4631a99d7b75a1eaf6ea5a3bd3d777f36c3519a427cdbd1a1b5c2896b4cd541d5457e36d630be56b5bf5d9eb7427c936b450923cfd8e1e1d93b0496c64a15c57922821e63a84d06d60a1d42708bddefc31a7aaae5034d5abf137a73655cd3052c001337c94e18122d8b04229c694da69f3fcd3b58d2adcb97a57e9dd5a726f993d87930d2af069eeb1f76e9e3b1ab69d8d2914ec6c839254e6932429fac086140afe717233224dd235f7c046140a9e84926b44941ceb046d40a118546bfcdd68eaf3af53b0f18462f013cf3ba6be1d061b4e28092dd2aab38d8c60a309c5cf9c93248e14a13bfe4db0c184d2273313757479106ab3847266f393e35ac8041b4a28a589f641693bb143466d24a118ad3549a735886803092559b2e9ce66e308c5adcea2f44b6484526d5475df9c63a308c50fa7466af97c8dfa2c608308e59e133ee893261a4a6d6308e593523ef58369923f2b84622731998950796b82ca4610f8ced6f39ccfd6add7b0d4517b5f61bab5b70184a2e99bf693f1464cfe77357e512a69841ef3bdd0bb9f20403e8809410d5f143d89ba116969f1e1ce408d5e14d4c92a6d552ee273c90bde72ed3bc6526b57d53c73f896899fa7214fbb28993e49899327ba286719ef20ecae5c145f6d838d4cea2f931617a65d59acdabdd6ff7f0e4b137b84b8b728c99c34a9f7d059735f58a8618b92f8566fe71ca73afe558b725a4ddd7a52498b7267979d24e3cae745b328e7b0784d82f664510eb552923a194394d8c4a264e23cef53932c9f5a58f0a1966e7a3a73258ddc479b5f517ef3bf8daf9de4f48f183c0c116ab8a21893ce9e744b3f58a7ad2846bb784d9e54cd9a9415a5cd51337afed64966b20abcd5533cdc524dd47d8fabfa66d9aa28be271bcf9c4cd852515453a22b4d7083500315c5921337c9dcb6b1733db2c38719791f23232378c1488e1aa72898acbc933d49acd2243c927ea086290a23f33b425c4fbc453ad8c6e8c1033b50a31465cd8e254d3c792ae63052c08130c6e811025270e762b6a66de59ae26dfde9e4add2129b34c6183d42e0a3c70872e418a3070ffc11c4478d519476d3e8f7b9e98ba8841aa2289776cf41e7dc77259f80181d25c8918347ec8c1aa1289d55674fb939a5f230830ce461860872e4d01164c70f9323478e1c3f6a80a29c4ace41b7484f4ddaeb60db1a9f285b9ee0267912ae1a4b9e286f14552354f6e3675ea313c5134a0e9d4f9e70a2984bc97662f2cad31ab38982c9f93953be9588ff3551d0399f204fc59ca4eba4a5502313853b414f5be68eac7c5a0313e5986433e1e33a895fe21a9728a60dafef195ff5edd4e167895267e7c692e3677b95b85e332f6f533ce698667abda9e490b183478e1c35285192c3e836e9f762afd21a932827496e27a983be8ff149a2a4c43331e8e4d176bd8f44594698d6300fb2447f1aa80189e2c6d028eabf47947396fdb928fd95dd1c5154fbdd5a3bd1333c6c444179384f4ad4a4bba3c508e5e53d6bce3af34b4c74eacd9e1c4335165152615d2b1af4d566aaab504311854f5b6de7f107627888e156a8918892789327ef7d7ebb3f39c39420478e26d44044593d86aa3d0fe27d926a1c8213d9acd5135bd37dcdac33393658be27916b18a23c622ce3b7c504c80e32b210651531a1a3efb1f24e6e508310a54ddf1bdc3db4566310e53969fd41c482484b12c46f499e06a2246892348f58b1104dd5004439e99b4e72d021c4270935fe5052d95c62b0d9d4af6e0d3f947489a6f7e54cbe51a73e94d4c9b0cf1c7352bb960f25b3da3c4ad548371dda43b97dcc2ead753d949350b5a99468cc1a637928099ff583a9342d4a8cf050d87e3ba533d5eb6f77872e3b6e67eef42cab4b9f888f27c345678792c8f1b0dfafa635b735ea507011715ad776db734c87d27b7e4a3bc9f7eb338782edbefba5a818519a1c4ae9eab13f68a546ab8a50230ee59834cb427bb693dffe8f123ca1061c4af2ee7af814ed7d9c918228d47843e19458769bba947423fb3823056704393794f33b08fd0aef31afdb505a1b69926f920e430d3614641833759ad3668d0ffcd1a30442a8b186626b07bd1a3da9114295b602316aa8a19c395b54fed788c8a9461a8a71cb24e16e234e526eb2a1061aca4947467591ee19ab8a7186921cd38b6caad2709023471a0e30cd508cf3ebac79728e49496b94a1243d7a124b289dd7f417d72043d14e95e7969d98535e8d31944f507be7e124d94eb4108fdfb1811c3980ec19414e0c35c450da2fbbf494e272529d23478e1c67043923c899a146180a329759a7114a867e150c652b0bf9ec536b7ca1a05378bcff36d5f042a636470fd1cad4e842e1654b0921636f871a5c28072da332aea9eef8921c6a6ca170f399e4244c1c93458c871a5a2849b132134266dae7910f35b2508c322fa784d2e7c14e5828fa8693ab1bd1536258e30ac52023b654c9e24918d90d35ac50def0d4a132a8f6b5be46154af96579a1f1438572ea2c1ae4dc48fd0f03a9318562101bab394bd0ca0daa2185929fa7075d4d1285b257c921f4492d75e2040a25258b87be9dd3573d3fa12c9feda4d3ac3aa1a446e7f42841fcafef4d28f6be08f73cd9cdf56742e1f96215eef55d192a5b25bb79f3a4359650d27a57ef5c2efb5f76a186124aeab6a66f99fb246552861a49289d896b7afaca78e23e861a48287fc9b9eafc3b4c3afd166a1ca1a4faf2cf32a9129ea6166a18a17499b3b64975ea72826a14a124cba9c91b4463c5794428e58c1425f3c86a0ca11cb4c9204bd23d6a6d53430825b1171b9d63be8d3d0a42d9d3a62769aeac9390d70042d93edd4ab7067f51cc9ef2a21e76733fe68b5b4fc4e3eccb73357436bbc4124f92ede3ec45c13a3e7abfd7c9f8c88600031920410838b02b10e145f1459e072dd9a533b722bb286f8a358d266af64516d145c9bb4bdce041915c9454643a41346a27a51a115c143b28712f9a31b710b1454988ca3ef7d3afaab1482d0a278618d5e826c8911f115a94d744d3ffa0bcd6244f641605df1c6fef9a41dcc322b22849e599a4cde56910b6482c4a92b0994c899b4404169beca7aed8655896b0179bebb49a38b1c82b4a3a8a6e3465a2f8cfc90722ae487baebc1c3946ac1322ad288956680751e21a1aff6158041156943a06b5394e9f876d9019e931460b4164158517fdd22ee6e1212023237eaa289edcb79d5aa743241525c965cd74fe112a0a42959df4a6e323a8ee14652be13597f86eea93ea603345a9d466379363b092252e4579e36374cf27b36f57f923880f142145399ef2f313fd348a527ebd27934c1a0d22248a92c996d93e4567b68642518ec1f7b37dfe8f513fa028bc0927e8e85ca638cd278ab29a7da45ba9c7917ba2941a6c94951ee5bde14e14f64f7edb0a9313a55e5355f725353dba9b28868e874d5a35dcc3d5443928a536eaf5a69b8966a298eddc94f43998288586be26396c68e67989a27ea6bb93632c511c1de232b331bc6b5889d2cb69dafd690e271f258a9d04313ea7425f956c1225eddc57c2e5e9204e24514e92541e4d9f3ea9eb4814362693d4d656b9a74a0412e5de70f2ee8312b46d26f28892fc983bed696f8f1e114794d7e74e2651cdb74623d28892709b84df4e4222106144d1d4f8bfae2bb288c2ef69acf44f4af073e408f263e4c89123c7f216441451d2f5f50ea594c7946222ca9efac4854e7244105112e466adf36822872887ba073b9d476f3b6c228628659f3a49eebdd8cc26852875fa7832932094ca0d3110440851f0cf582aff64450651f4b7ccfb6492cce132558808a2f0f72a4ae8f196d1ad4820fc90165af3af49f9990820942d7d775517b17dbfd7249edd5d913f94ac9369294964123ec9e620e287c268f850a2271d7742d68754c738dfec2912e143b9d5c74caa07757a9f1144f6503ef9564ae37c7ecaa88772d8d2f8493e8d7703913c9464bf07d3fbe99e8411f640040fc5509f565976927bb57728c5ef95564f236f366e87c2e84d63fb5937a9491d4a4ac9b3a38489ce76ba0e361e3c76d8c03108227428069325f36492bd4de7db81c81c4a726cd11bc79368750910391493b0d97c4e9949f6771c8a9e641a319f4c506d62702857cfacb69c896d7dec0c913794547886ccaf1b7920e28692186972095be9e7412ed481481b4adaf309f7d023eab5eb21c286a2e8f4aee29e25b7d91acaa794b89576fcef4df58088a8a1f41fffd48c06d350d09a3127559f2756d63ad87e98e791341a0adb419618c694d2a7752743e40cc5cc1a4e89d87fc9138f6186f28a6dbdee5599d4b9ad814819ca799e756172d08f11203d74b0a5a5d940840c25dd21c2323c243286828bfda6f8e630cf256228c63a31a677d4ce1e5f2fa38048184ada2553dca4d1cc260806ec562be5cc62b5432cb62cf4846cfb75b00d41e40b85ab6f0fe349690d9317f1424969dfa0f4d68348178a7a92984f3e4f5207112e14c427a5acf6ba34a46b86c8160a32cd97eeec1d996346440bc52d3169d45d08a5633e0bc593d74ca82d9d3dc9712c9464aa321d6ed36ec47d85629049a9de2ae13a976b8552c8776a129d44e89645aa50f8fef64dc2c7dbb050840aca95ebab756e8cd68f9fd3438395660a65cfea7af71ecf4c9459109142699418338dbfc9be795b41240ac5d238f95866fbe5252250286e36d933eba2a39854e409c5f02432070da2d96452c4098bbde75a8ba568767ef024767cae838d05224d28dfc9d0f4983e3eab25c28482eef876a7ad4d2e2528b204d3285d4a9be791881216997bcd2c570dbdbd73cd5d6ef2d9273e20104942b1646e977529133a65224828fb65feb8b979cfaa9581c811dccaf332798d70a792b7d7bdc79334102942a932eb59a92fcd77a1138810a16cb28e105332bfc39a224328dbdec751cfa293b9688788100a73a2744bd21977bad4664482e0be28251e8fde52527b1213058f65e61fbd4431abc6bc47c465969628fa89901e2ad3086d254a629424aba84889b227eda6e1af9d445946569a9cd74aa214a7d47fea18c3c452240acab48508dfcf49fe9028e8a4c6a43449ee3ba14794e3a94bcf49e8fc337244b177add395a0f7d449234a61ea644db731321f4614335b3fa68f413b7716b19d8c8d228a9dab27e9b058cf9a4494f54fd04d62bc28d511510ca64b752961d5fe4394776be4fec6666d3644b1c4d4497b2cbd2772210a2a4b47477487f84a8892923c87d239c6adcb4114b38a9e31f313538c8228ac491936931888629e8e507bb127eb01a2187e629b66ec0f653969edc62df5434ac58850a9497d28df9d59e8aa66c9890fc5bc59b7f5419352fa3d946b43efe4ca2bd15e0fa5fe34da6f72de1244792867265972c7123c943e937492cb9de67eee50f8183de67327c94b76289c7a7528093a6e87bd9bd21c1d4a82ee9998323987e28ab9b7e9590ee50d9f31fb6d4dd9c6a1a04accf6591a4a87150ea51ea55e3a9db849d0e51b4a1d6cc4970e1b4c66e986b2dc6c99a9d998e5b7a158d26a791e9f0dc59426ea8c97d41aa13514939c95902656434992739ef4a374e80fa7a1e0b56a56ea4543d9ffd53fbd9e3cb9e2194a5e16a349bff7cb8a6628ac5bf99d32259e3eb10ca50a1b37318a9c0d1992a1bcb33b26958663280926a2a4289d97114331060fffe8dff7610a4359c3ac08f799bf180c2513cc3e6e8efa4dfe17ca7b15f23f73bc50148f93fbbde942e184a651928ce142e9cb4527311b84d0e92d94d6930ae5776ba1aca726e9f5344a3ee12c94fceacc7c64fa13652c14635de6a9d05ea12cf233697a92df3c5628c8f4aab249aced74150a32c91a4c56a5664fa9503e592f67a69d42b9b74d34496d470ac59ce25b69265128effc89d9e40c2ae7048592ec7f799b24bf13ff13cac9e4a0d2c3774271f5b4e6d8ef617350138a5aef59e999b73188092549c9d9bdc64b891a2da11833a9dcc6cb3d494709850de2da3509a5e43d4928c7fc959a1009052d934e8a1495df1da124d4a4fbe4266b2e638482061119ea2776eb4528ac6b78bd7053de39114a32e769f21f5af4432808fb354929f9df0409a124c3dd7732f14c49128452f95b670679257e2d002094b736fb4539853cf132d442269df9a2d872c2e53de83a99592f0a5b612137336fe8245e143d56d77be7ffbc41bb28ef29fdf9544f99215d1493aa2731a8925c9474b66eec33ae830e2e8a9bb398c96563759b5b947a84de289371d7765b94f39e70cd2e3167b3b52879385fdfd8de9ea745318fde064db11e6a9d4569749ff027d44fab2a8bb29bcc19254db32435160569c2df9b1cc2a25c3a356692b145ebf78a927fcc1853488d39ed8a925257b2c684119fb31bad6893741dd3fa646fb0a2e827e3c858fe9db8f08a70631545934f860e2a4e7834f91baa2859c898da16912789592a8a6e5f1a5b63f3262ba1a2185b72c24ee7f4fcec8d539463fcc9ace898b3be6d811ba628c7369dc44fbfa519bc1d3f8264298aaf1e73cca7e4bfa8cb468ad2e7c93efb5892ec1502328a92a4a3e8b7159d288aef1e5f3fa39a8927a72e14a50dbff52553846992048ab28a2ea94978dd5027fe445944741ab5275388b73d511ca59359099e84a9d98982d89339c99daae644d1e48cb1303957687313e512f9a267f34a125113057dbde2ef9ec944c1443706594df54cf906264ac2f59ddca55abd44499c9c5fafeb74fdb40e364b149392b6e45bc9df900e5ec74825fa5ed7b87f62debf7e8312799bd7c89ee759b77799dc984431b675fa32e1e4830c0f9c910120404c09529023c719c70b372451fcb5534a0ab976a434e2810d6c0088c9418e1c23772312e593242df3179e0eb65cf6c00d4814842a41644e9f27f59574b0f523ca226a7d3fb77a8e2886ddf71a4d75232ee3461463369d37689eb5f50d234a331bc7568468857e802ca270a12419364bfe290f15518c49db699bc79afa97888212d95c9726556c2e35228abf3147d5c4cc2e9d3f4439c6124c8ce96eeff73544d14b2ae94d46934294cb4ccdc4b9124e6ed98e1064f4e83132c68f911100dc1844b92a53c6f5fb587dbb0e36327af44005dc10042a63470eca3023e4462074f7183d2a101e3ff2a3c7880f4386190c6e0002d3dcacb0ccb355f5f4ec93a6f73aa68198fd4329349c641dde417bcc79f0d801821c39fc502a25ef8b528b11e1b60eb64ec18d3e14e3bb9992abf7b0b9c387a25ae5fc897d6298bb87b2c62c1f235be57d4e74b025a21b7a28dec5c9e9749864b9e7a19ca4b01f6166d2c19656821b78286a3a7311ad2b49d64a079bdfa120caea636b4dae8913b9dcb043a9e6f575344875b091b11fb85187b28b7abb92a26412b74ef9a34710e31db8418782095111b7b9a39e500437e6502ce147094a85f7891305321204480f1d2365fcb8b41e3b467c00b92187921e0deb414bf81c5abe118782124ef808cd682b7a73030ec53eb58fdf4986b9f974b00121a3c78f641ab8f1062df754656b7366cc4db764feaf78c30d2599fbb37fdf080ff7abe0461b0a22332833d119376eab83addb8c0d9c208c13dc60434134febf9b2e25a9cfaca124772aa19310f1fc9d5443d1b47b7eb20eed92848c1b6928c858aeb3b525a8d2e90d34143d4921e623db3ee94b851b6728894934b5c2b6549f243e7c181f3e0cde30434916a19352f239e7ce77190a1aa389dd194f6428c791f1e327a54dc6f68c616ff1d05611cf36edd97553d7365b622868e6a8d56d174a93347ab40ddc08437154d53535980c1094bcfee006180a4ae99cdf386b31aa7fa124ffd5485155923c4bbd504c5751e2dfe69efc71170a3a423b4cba8d339f03e106174ae3b9f5eef7744e4a76630ba538cfd18afb9ca2365a28867e87b2ebdc8d2c14639364daa49a49f74958288f89b6b0188d665772857218d91c83daf9183369855227bd103a8e0c42ba78a30a25ff9c9aa4bbcb7f0972106e50a12c5a4d8e416fc9dc69fb3f7a2443b83185929ce039e6605ddeafdd904241fb55bb4567df52a251287c3a9d3adde45048f646cbbb4ce55a5b3c4df24e7aff130a5abb25faf9271d5379a020478e203d78006923c20d27144fbcc4f5a079138a9a653f36af857cae985052dead5ea7369fa04b4b687674ae5d5b64d75ece549a2c7a3e4a28bcc979d408f3907d6d8e1c3972e4c861e6a67a3f80b0f96186e11b49282637a99ebced6b3d4642696b77eb745849a2ec4728ccb996ec1b5e256776c3087d887d55c69b8a89a66713ef1b45286586939f537f76d56bc30d229433345756863139cd98851b4328c9def018e3c909f59a2adc1042e94e4c93c46f1d11e16007420d789f3d8f63460fe439821b41282941a91813bc648dab6f00a1307ea2fc09b2de3ae62faa52abf4b65acf50c2c5456b7e744fe28be2c6951bd9bca525cb62d8e8454938c947a8dd79519239864fede31d73ebbb28876aecfe8f7126e4ed3e6ce8a264625b9acb895b576f9f99918bf2c546cdfb9e848b74b6dba4114aee1645117a7b4a64ce924a124b1bd8b04541959419748d6b9d7758031bb5289cf465720c25aed6c11e3d0e0f1bb428a8bbba07a1ab55c19f338c08ea82ec28634719668cfc39c3907146c693b1021bb32869b3316955d5f193b42c4a2766708f311b3b09828d5814e39db6d792ac4cefb4018b92658a698d3e7ff41829e3c77b80edc7efe811d87845d9c438f357f3911f625714f6a3fd0893a48ea2cc56946263f9f68c859b88e935f03ece48012b0aa6393d598aa938edaca26cfa64cee9ea94e841aa28ae78e7899b246eb63b15b99eba6557b9d5556c7dd092dca369b612ca12ece0e103053f8200715414bdc4bca643cd9b1cff5394bbf4e829bda17394db1425d30cba772c53ef88364a513049959c99924c38d9b5418aaf2a7c6b74ca4651369d44a6556b703de908624a9023478e3382181ba228cee6d85aed233378fc0f1fdc364251b891f9246d59bffa988e20a604a92388f1916704b9b4018a92d906a194d850075bd9f84469dd4f9b6a135e820d4f9432fcddf64b29257f3fe2c390c11db0d1896250824913d63153e6c7a39da09b13259d04f139e898be57a8830dc88e36203c760431a30236365138c94eaf28f9741aed68a27872c998ac336c23139fb0a161d54a305192c13e7410be9d45852e513a21c3ad7cd49628a83f19a2f9a41205ad59937c569bd7e450a2f8357a4549523c8962a912a73c7e85086949a2e81642e6a4e7c453491289a2ae5b5a8fe820512e0f26c7c599127465361ed1be698ed858cdeebd57965646b92fb5238ad971b36c652731fd37a29c94741d5c7e3c67c9da60447946ac2419ee5579af8d459404ad6fd53fc14d68491125d9bbfcfccd67ab631251729393e7959c464449b3492a6b36eb246964e3109ceab6da8b9e8d6b8589f66872ba7338d98e0d51ac13f4c8cfa5e111532b4459732e3176c7698310e5d6da117a948a2f31d483482cbdd4dc6c4356cf5c3b66d5d0b9eac08fcf418f1008a2f45ea64cde32abdc0e44b9e24ab587532a44c7793b7a9460cdcc06208a39f234dec91e4f96c9c61f0a613aa64b1f53fa166cf8a128aa5f746430e9ddbac3c139176cf4a19cb9f3c65f494a8cc91a830d3e143ecf6e9de4f61973d9d84349aabd31e175194c7b65f4e0a187d297f09a5de6c943b9ccb4868a0ec2437994aa66d0313556a76cdca1982646696f923a953aed505e334912bc4b5687a2d8c9a35465b834bda143616feb57545677553d87526a38dd417e954341c83f5342638aaeaae350dab4bec2019b93d7dc6a11b5306f3599b653f69d2e49d2df903466fd26c7f7dc5038f95373834cb6a1a0b94fe94cf7a649920321c3fc3025b039d86043c164d331a64129f5bbd460630de5283a961e5e549d12b3a186f295321d72d28c2757cb6ca461afd0af716e2b1b68288819ed1533d9632a491b67287d9f8e7a273663fed8061b662897243a5f6bb50799dd46194a1964e8a0839824a3f636c860955a590139cae8b103c88fc7c1164af59d63744e42002d94c4df9612ba3c367790005928f76cecd9ba888572d05bf2caf457289d9a29498c155ba1285a4d1ead331696e92a98ae65efa9d76a27b3accfe654333b158a3f6692e09f1f1d6cc93c0ff341802914839b2a175b3731c9e12910400a65cd789fc49c272310200aa50d37d6be19d642e45a2000144a3a7aba047842490ee9a145f5228013caa961c4c9f1a975aa2240138a22e49592846e72550d080198507a2bf1c326f5d0213e4b28298bcd5695a5839c93004a28e76beaa8e9f312552640124a55823439476acc512912d4aedb5bf7135bf3e2e3356f8e5010b712630444af4dad2bdf44636792732a3d32ccdb042842417d8b1619a1c35cfe91ba1ea90c0f201e3b7488e1250601885012c5e42449d031bc7d9c0043b8db5637d62d67c4abeabefd327b9600422846394964522e7b26bbbc2640100a9f5409df1b94080084828aea53d17a92ba93fe4551e54c63f343387c81be12296a73cc467ef4c85e94e237ec7f7b6ed0e7f1a29c447df2e0f1ee9390cd00c72ecaf562aad499b02e8a57527e925c378effe38394f1b9288ac618a366f60f22322e4a72f36ed0b8db27681be1b845f924dbae6a8bd338992d0a6a6b5f3e99fae0a845e9ce4a6a9e5c213c4d5a94c3f8c98ef92143c87716650fe3ad77f2d1111cb228c6efa75262d29a7fda58945db64c921baf0c70c0a2204fabab9f92ede9321d6caf7066dfc6b35d67f32d6b43a606b58e41cc84a280c315a53953b1ef5d9f59351cad488cbfebd8fc8f51468e1c45041cac28d98fd6a6f1d8e5225a4539ad4dde3f11ff70fd781d3930db030e55f4d69fb7639c8aa491fd12ea327ed81415059d214ccc1d99496cce294a3ab5849e485dd1ce1587298c7b7d95b3b91c4dd5bcd798376da3a414c50b79c2efe6838314259395e326e99a9d18846314c5b34f92a0e4524c795814b57e7a977bdbc9a955699abed1d7d7ffcce10845b17f93be12ddf94f058af2a99226e636fa8992c7a7dd18ca4d535e1c9e289b7fd8eeb53f41c486a3138cb55ade6ba8bc685a6da6af8aabdd4e4d130d3838515ecd3ba51f4a5b897c1325df4c620ed14e937b6aa274c28e97307a7a467b3251b2f8f1aabd4d279828e7b8f02a41b8021c97289b0ad9b761642c51d8e41b4f3c8df4dfb212c55423ef4b5a2f49afb653c04189c247cfb7b0d23bb6e5248a1d548b681ab59d4eeac707f1000e4994b7f64bc66c8dde6d3252c6f3b0eb008e4814bdc4357aa62cf16489063820510e7652dec893e151323ec88e9133ca3039f832467ef41831447b84d5b5b1a697ebb9b1b39b1d229e65745e8b7138a260995b2676f0924a72510c1c8d304354354fedde45c746bff3480ffff1931007234a1d949ff892741c8b289d303d2d25e38a2886acefa9e7ec0c424e44f94d8365ce4ed2e98e21a25442a8d995f02e310879a200c7210a9b39675b6c8b6bcae0304441a7fce849289d73d4288e4294bd4e4c92d45163d049c6c3ace028818310a552f95cc266cf2769aa83edc728c3fc305ee01844c93a6a9242c49356d7870f330451ca2e219360d95dba84efdec7192960038e4014845c91969aff613509204aa2fcf8895e727f68d732643e110e3f14fe53ea9a899d6476501f8a6573d965aed94de0e043b1a3680d23437ecce2d94339a85d97ed111b81430fe52a6155a3949b879220aa4dce2e27e72b0d071e4aca6544c91e993b94bb432771e4afd6c56687826b6efa18ab318973eb50d22245fd7e1cb9f9d5261c7428e6bbf28c414f83c93a87d2aaf688f468624de670c8a124bb8db9aee98e319ae250d816a91e47753814f3a6bce79c6fcb32f78692dfc953b2ef3987c670b8a124dc4916da3d9824fbc2d186d2ed49ca37ea95fecc1951020e3614a355dfabc7ccbfd55801c71a4a21fc641ca17e336ef02336485e47623c8c19372838d450ba8c3b213e447e92e49e8662f67811a3cf33556627703494cb94a039ee99b89ebbc27186921c17a162ee53f5a87f941c39cc8fdf81c30c05d5d94bdce99ce2f383812e4339092d4f9adc838e6fb8031c6428dda634f19c4c5daba80738c650f6204aaabc0f3d51e9085e3002031c62280953edbe6dd2c618533c0c8e30143629419a9453214b4fc9031c6028851e755295f97ef00be7fa6f90ef24cc8912fe08e20387174a2bba3a8f509bcf634e178a69a36abc4c4a8d6f0907178a99420831da4fa5c7ec16ce552526d37ba25a402f3dacdc4e742c353683072d6ed2290d8e2c14d427297d7c93a49f7c2c94aaf39e2c62629a6c325728c6f5cfd74a4fd2366e85922925b697858ca30a25e17772dc1e9f0ac5fd919ee4547392961c1c53288926493286d689965e38a4508c21333ccae7d9d4a6f4008e2894379d38b2bfcf0437792814fdf2e40f1d6412d4c99f50924b8f677c4eab3c2d0e27946b4b1a15132253368fa3092521dc4c4d83fa68d77c193bceba0007134a629e549d543e89db0cc7129c0b5713d7ee345a19942ed9c2a184f299cc67b2758ee2c1f33892500eea639295e8bb259be04042b154c61cfdd7e45cb763e03842956e753532377799263f3ae4c7549dc719388c50927be4546decfc004711caed59367d94d21c5414003103071110abe93fe7e5a46e087cfedb9231239349f7c021844e8b6b36495e93e4cd0d7004017db56aab105715dbaa7abd5c311945642ac88e1f2cc89103b10d7000e1de705a9dd44b06f08b82d076777192205f944563d0a57ceb45b9b3eb0799ee74a650f2a2249654254aec87f81ced5c30805d14943651f2e37e12d446ba28afc6911ef5e5e742938ba2673e112afac6c5b29fda7676765d1fefd9791e93243be7926400b72827cdf0e99d648bc25a9aa93ecdf764aa45d9940a2db1dad3fe6951d67e3d2b49dc244cd02c4a264c120d9f74de389145594cea8927cebd4a271605e141cdbb7b92593db028099e35c624d8f89ecc2bca353a1afc7357144c3c49fc98e35733de8a624e9f847ccd243adcac288bc992f57583f610a755144b9e26556206111d4daa282799a7f7ba4abb9529150553afd9849b2d5db2848a9209ad35a5b1748aa29bfe384aaab98c5a3245f9325e568e28318a954a515e9b0d3ffe69e20991a2e41993b8f7b9268e925114e375de64a26a429a88a27cb27fda8a3331772714054f4deaa70414e538a65affe95e73f389d29d5493f4aee7774f1437c79f4d999386dd899258790de336270acaa4d2609d6ea2649b94f7d57da74613c564a2cac878dfcf9928c9aba6ef151f1345d3db374af83cfee15f42ed1eef2d51703b497ce835f5f0be1285edfd36a143d693d853a22498127372737faef69328eee8bc4d5286d46c9244599390395afc7350279128e529e162c30e89c26752eb41c5b9fbfc8852db2875adb7a249eb8862504aecbb96ceff69234aa324a963caa0c3c91231a2b84926b1775a1fb7448b28dd0731763d2aa224b45d88889a1173224adbfedae271e4680f11e57ce2a7eebad5b20f51fef66cfd98d14365c81045d1fc181db5d4bb10052fb9676fdc7dd42744614e36f9314906514c927c7efdbb208aeea7bf438938b303510c2703a2d47a5f2fd62342c87f288edc52ba42f6434912bfbc24f93dfea40f852d6dcf6099be36c987f26bbc7433f750be0c15b277b6e418d44341acda4d305d928762f61525849636d72dc143d13a57c91d8ab1754b57c865a8c90e250d1b8f569f7468ea50cef85994c89e0e05a576d54d08cda19842658e696f749d20391474f3b7c589414925270ea5f0b2f62475e050526e7e7d2665936bde50ac534ad6ae6f5d3714c4ce79d6d84f1bca39fa6af2c86c286d87df17359ad4b86b28dae76b12b583284d8f1a8aeb3a26532931e84b4f1aca2b27c82446068f221e349443ba8f97a43e299d3b6728b78bf847abaa91a9638682dd9af0546f612b2a43499630daa762b3930c19f658951ee43c866295cceeb94b12e4d76228091a7ec408f3246c1d8692ff5629a964fdd03018ca4918cfd93e1b6412fd4249466d936fa25e28fa69f60cd32b4249174a27c5c670a198352669f4c22d14559448357552cdaf85922937412de53e4db2502af14451935f6d355828dfcceddacfc9abef0ac532b9844c5aee7356c60a85d7a4c49c9c64af4d158aee56bf9f4a5428e924c769ead0144a199bba94870f3e1b29146cbe4bccf3d6c9a4a350924d909bbf1e37970c85823e93bd4fbd4f28e83826991ca64e72d509c5f528daa48ea726a64d28061db7493861a99932a1fc5be641b5c90d652ea1282a83e793cb834ea3128af7b94af949288bde6849a1cbfb3b2494eae4a968d5b3123d4261373727ed79fc47334241e7d6104a30b1f0cb8b500c258e66927a22a084934dce984e86504edd8f498ceb26a78c100a1bcece72a3abba1e84d29ff02567da030042d9f34b38b1a75c2cfd45d177c3d97ed0bbf9f44541dec9d7fc1bb327d98ba27e7cf4b061377b9017ef8d3e310932d8bb28e5d9fac78ad26262ad8bf2076dcd11ef9796752e4a924713e593122eca22efd357589aa6e91625a18465c9e9d8c17d5b94478785e887a9aaa816c55c27a98f661e4ac9a145d94c30f7dc2aeaf43e8b6210428829e9c34dd46551ec187563a963515ccff5e92e4eb2b56151b22cc176e7571454f8c8cf86de24b32b8aa2be593ac656949349728b79ebef8bac28c74d73daa33509177915059973e8a0a1e38be75815e51332c7d6c4fba0394e4549850ceaef3c4645c933a52625da65b68e4f51f2d8d2490e2564ccc5a628c9b959ee73923dfe2f456947e820ee4446358814654f9df7e396dca2a351945a64248ac2cccab7a8d8fa982414c592e76b545c448a09288a57f2df4bd0270ade5a59da931c6d1e4f147e7d845092b727eb74a2289f499294dee0f1349c28e79ecdeacf31e8d437514c3751a77afbf1744d9453275592fb9c89627637058e83a8444ec5e2c1682c148604c2703014081e5f47003313000000101c108682d150382616c67d148003593c22463230221e1c1018160b8502613810060483a130100c0685c2a13020188cdb5295d603cb054c60548c0784f6397345e10bf91864179e38dbef2d58da42856dcd9ea57a73d4e80c2aa86d9d328280fbdd0b7b708d041d5df71eec76da3fab5abe9e45613bd4dd24a75127a7b8710dcf8fe7744892e6289db296f18b342ba9a637b3b1703bd23fc4ede7092cfc05c6e54ee63193261835cc49728bcdd448e3a5653a4b1d3088ec98e22fda517ad7f80b9c7b960d1a3750dadd9d3df8d503be34d5c5d2dcc74f63f434f29c0f46e353849df0017656ec65e9d30df0ccdea32cc178e77fb3a333266f23c9b994907c4889390fe6e48001859f89c0df5757c2a15a652573e9b8985ffd5c61a76f6d0d900f8e743232c176301f992d92489c788707940f79bb8481876965cc59f015c9fc2a947568e3be9f058baf0026df1e108be8c0faa6124042342fa51cd46c2654a0d319b6612fe175ed1eb503837c4d70570b256786337bba13f2a07eaadce9710ea1ccbe3a775c976ada286a9006199c7be6fc04d7767bbaefc726a1c9be22f7d4932f4ba423bb57b3e77b3c0ce3ac808f40d8d3de3da0e541ce80ce8a1b40cd2414f119dc587e9f9ba3cc0061620e9a96d92ede4f925b9a35a66f7b2c89b02df4af868a044f2818a8bb5c77b0a83dee9a3627c8fe68a462738bda98c65d3dc5d07200de6fbe26448f37f167959a25187029e197aa84988b31c3e6a30c04ca743cff0c58d7151e0b62f562a352d63271761dc5dae7725586bd9a7c8ee74ad5000641c547e032b7332bc9054acd42bbadd3c05d130a1a69659cf8d75f60784f3b88445377127bbda39ff9195eca8041a224325026209eb57d1bf251ea08e0654849bf4e8b3d47006b1b1517623c22505b1747c33418254420fbb1aed461891a267281e16fab5d720d0097309b934b31a8e801c4402087069983f52767331ac1958e93e3c267d19eaf0be92c434c5005b409c44260bf049aa01b0ecc126e09a3a054062258260f52d051afbc3cbcaa7dde100647046526b4b88ff836567e9195b1efa6b64333b3c12dbac33514521be0170b38ac5ea4aa86d1b8448dc868c80049c56b9bc07fcbed8aaf725927d0bd163bb8185b16dd4003259c428d1d32dcbebe03c2d2f1df069fc501f522985be8ad2c680213c56aa2d55b326529d7bbfb9f525739a1af8c9f6966d23fa9eabcec7e9e893c56a2bd07229574550ef4987d6179014d2844677de29e254a529f096aeedf460511efab751a0e142f6453cccf8112a9aa247f10122db2e6a8aa60b90d5e86600fe4a9c14d7ebd6d8276940b2c55a8e4654295122b6729cc1e2196aa9016481c9070a3a8ca5d82f17f709811fff44af5d02b6e56cdf3090ff16c5bf1764d9eca328254a37c32993b14f1686de8c8fa3a2672532eb7e31a10b9e680afee4127fe4455375925b1eccf17fcf8fd94af1dc95da2a778a8443647b725ea6874ca7be3429a368320fa59d7900ef85eaa4574fc71ffc8b8ac8852e1527ce95b4615b12b3ca46a6c9f33ddd8ac95b5d44685d2c885212c26aa3131166caf2c8cd09b0ae85505f150eecde26e9ebeaa96656f6be158a5503f1c23d1e8bbada6007a8e5596a696be729ed99cc67f3f2b6b5385df1af9d18966a8f642d0e1c84a8aeb725d2a14a1b00cbadf04313d796a5c9831088fe121552d7315d02585a78797c290f7eacb32204c64e1944de58bda0ed86d2dd1dd8b4026f52c937f081c7fe40e73b429e7a3d718e1ae4ece50f06af56f3208aa8e22bd0207545a74e9811e193494adaecde40824e3be7064762dbff8cbb39577e84bcc6a7961e3798ed3246c57a829883f20e9cde2f3540341078035a33be392278bf9ce6ca5b70bc72546204e9610d89117dc09709c87b66b2ef02d935776edc44076c48597feeed42426d0141ddbf88ab2994df92ca05b16d56456036d1377623267f43840f512052d630a23a1e758c76a83f936d169b23c638517413871530e6dfbed776e859b013deafd00167cefe00992c3530466710e21cfac2b111e8e169786636a0630e3374b2caba268b3580aba762eea38ae21dc1ab40664c5876ba656e1aee31ada063bc47131e5448ef2466a0458441be0bc34faad5970aa8ddf389edbc3ded4aa5d207e6c636512b07a4ca0abcba09fa71286cb5bd6fb305733f78594c6863776c291c3e0e3fb4e6d59c0839e9ebde149e2da2fb7571121eb5967b7ea5cdf3fef29a44ba52adda0e65aa6e8814314cce489691af69c2e59c7577be0094d531aae83d1ec12019ae686ced959da7074c903f0a985b6476984be61c283389ad39dd6cac2b209469c18c4a9829eb10425c1b8bcf78d2dbadb19e8d8dceba490fbda756185e9868edbdd15306f7d2f920d93b491081511307a25b1d2f5ea84e5c8462fcf4e555cdb9736fb2f8b5378001cb6b3eba07d0069f4dcbd436aafc4f5b02ac44e4d68ef17fd14c032f6e44fed56490ec8d579cbf03ee6e3c49339ed33ca70c15311f3392bfc1ed699abf4c6b6e7506063e4854815e2f4706abd832dbe1fd69684a4910a896fb5e276d85a27c63476795a5fe047cfcad28e11fd7092e60f674ef28bad8270e941eedb535a17090440ba08000081aa98643102cf865f7c327d89d07378c3126fa1f463fdb299bd88e57dfb757a0f7e9198db22c69be641581a527362a74e665c0f8d02a4d25ab514a16b863528367fc6efe83544c3daff50dff9f68b85a52a2a766cd32e12042a4d8442ef6693200702407449dac2e6009a1997c5ba128f1cf428bd19b70317c15bedc745cd535001ac2263f71c1dfd4cf4e6bfaeafd9f0e16c88f12433df04a551f123b35ff4b48f28190bd07b6abc79f733d71a3098c5006fd3eb7a545c09b7b3339b7f692403a7e346758eb1772505d4beca73f0b03613e90fd037eabec8144498cfd6241d8ea4b7e41e1a96dc680ac3c98b5a65a2afad8207ebd1f3d61ca087e36438985d4463defbcf9f2abc88577a4b35ba053bb3eabb01539007ccf0922441ad6a3d1ea8c116ba40a2952587c94172d8dfe68a1575e0a0c1397e436bad36f4c6a3cba4b7c4d970ce0e542ca2e16c6dee9e691c5600f02b3dcca96d3153912ebe48a899a20fa88b8274ed68ba56795717aa3be4d07b4c4fe92171d03041f328e54c67d69bd913711b6c6d89eac6be598e967be58b10a80861e9687fca82c7eec58d29b7f9d5cb2832039f11ed0c97c74938c80e5086bdd58460f0dbdfa21c23ec758c634403039811a8c49f23bdd7b25a1d8f824cc377cb51b241da53cb6811bc430699f1bcc345c8b342c3ee947266394d1191e8264b8e848fa07432631267190ca3792e4c5b901b7f4a7d5342132c53e286cc47022dd2088d13651f051ef6387f2b20e1cede43ed239cbfcb79db0e987da29c530047bd686b53e46a20f02dac2b5a664ef796138ab5f2186d03cd99756e9f9743c901f3099acfa76a28c649a506446260ca1028c43bada59408dbe4fa0710022f880b15295e5723b75a13a40858c0da3e07b7fc71bfab6d9b49e6bede56342cde64e79d47b799782a81d5202247025fe020c594cfd8bb6b0ae44d652e7604034dc85b23b8817fd3b7aad82071a056ba745affb326ca04847c864e185a3343b5a9af082ff5093738a2ec21a1d4c211ba64f6c1ce60764d93cfea225839998baa2919d027fe50051ed5e790fb1fd5dd840183dd00ff5923a23cd4802a50d429213fdba14b633fbfc1a4a804d5676a9d7957dc80186d5c6ebf1d753afe59ff985f31fe4ec2e728ad0d3c9383513244ce0eef74ce7f86c5b9e7bbfd811500d809aa6c5631f4af84dc6e89c339ab95727065bee6588eea276f86df69b0686390f690231c53eeb650e6df1158d355bb5bca6b1a26754d82b4a3d50a9190bc87e8196de44ab6dde5bbae0625212ee4d272c203bc8a0801ef7050b30e275cdd84df5dcbd66a2ba818e2d9ec818a31bfdf92c28083dfadab95963d4944262b795e8dd7a8855c2fa71767ae9996b03d854985a23adba0d9399231cdc1fc6fbd8a1a3a45ac13c3f11bc3fc0cca3f239bf5c72096840665c6b7590ec5c3192e46339460daf2ebe6e792c10cdb5bb5cf698c9c8194e6233a456a0d9016be74ee70ddf745bb6caf820bbcb3c14df9008a87fde04e77611e5af3ea1c5dfe7b0ab280af2b0da0ae4afa78861a05c36c02e8cc42aa826bac64aa4ea8464177d09f56faf311bece6ffee3c013140fa7ad8ba888e23af960ce34cf1d9e0fcdcd0a8b5bdf0390b57c9531843e261ca60fe4e9df97a27b069a6354d816ab5a4a8d95659593c3053a8b85c65c8f5fc6ac40fca13638b815175728905eb74a49575ef8c1392e81085e0974c230c6587c48d108b3530422d7056ca17fe9be29325be06450023c2081b950ff02c681bcba5584238267b17543f8d8b6c0be7cea95f7995e5528d67bf545cb0ab83692ad4dbbb84a0aaf4854022e64b07a3dda75b8e541020c5dd9ee09e64d017c578b1cf84253d60cc0292d5e6fe79a50613033a9f870e7db394249065548edb6de5b1fe2fb2605c4c3815e88d786d339c128808e18b5d9d2ed752aac942118122127bb5153a13a2301b080c72c03f5c92953d1bd8ae1da9359a1d162665bdcdf7cfa600172d0219a2fcb43711e71348e125a12167400ea1b631c213201c473b487fdea08dab0db5ebe4ded91ce8a6e8d0cc923c052d29f702219186008973bb2d487116d3b2086cd04b685cfc5e0303927ee26ba3580521cd312a2b73ae7bcb0a06458d2ed5cc4608cdab2a6010d95aa1b20143c1a2cf6494174412fc711ced275785ad3b64f1dd6548c0e31026cbbb0c156a4c9f20930ca7455a47078aa503fa58a59480d8254a9432c79201be7d433a0949953de30520b738f6c91aec6cc03644418a98662ab0e01212def4f64128a598ac16e08428e8532464660bc7a92cd908170543617062c3b476620692f12b627860c58bc984c8248b8e1bb893407a81564773a1ae494e1f11190450aea1e44814aa36e21e016a735733a9ad466ee4bfd2cac5c87c47ed2140006bed8ef47e15a100f344004b5cb23fe9d73dc59a1df17aa88f2c41aa732ba86427600d54304911359c9220e25302695cde96049b68e84ce4f9a1055cb75535a2961ec33d8a7728b1b3516129593f32f31c51d58bbdea7380b09490ca011949373afb67b15acaaeb3b6ffd701d769fb754e270e75c6b4d317ba1dc7774064482670aa671b295e260c1dd98ebd343be1916d7809791fe3778e23ab1f86bd22ca69552014c49ada4c9e4c00de074b260b4eff485dc48b02d7573ba486bda40b03502e441566a017a57a81976b7abcc53de6665831e5f5860701041b6a0e5fe6edd5ce47423e60af0ab05cb1553480c66a7e34aad39c1fbc1dcd82129e5ebb861b76898de449336ba6bfdbbb45a97de2687b9492e5cf3673de9d7c340c47907434c5fb1caa8fed036637abf78eac6e720b59185e4f6b0480908e6852a5ab6c5ae39d3872638f1af4b0c9a65809eec978e5a2e91b5971950ac72aaa5a8bc54fb61e5182d56adcb3163b869ba6210710b0116bcd4db5cb2071b5ce7684a168274b118a4c4d9b90e5d4544803501d8e871bb12b8a25dfdc1e920f7e014588c834d4386a0ff0e8a8f3806ccc8836a7194fd774d13c50475dffaf3a06b6fa0c47e9127f7c2ed15ab5773cc6403a0fd2e51a90163a73c6325db2458508acdc091c017d916fc29e3d500392f4e94cac927867007b3f3bee4070d7e78257e22d5be9282ba2f22b9aa6281240d56b0507b0b6480741b8e538da61249dde4726300809979a945b41146b730b2bdadd00c8d9e37b7e4882d55aad762348bbe76c58bb1211c5b648099c805bab360d8f81dc22ac09d57e8400b0333179185664f3ed22e84fa2c673a2b5b732948cfd2ba67d3f96d28a11da54f9282ee2a264fc55c4e4be603926e40cea5c6b853d5d5316c8986fa7c4ab571b00fbf8fa6c557fbcb9539d3e59a963eb07acf74279934d6514ae29300c61d395c25d6f9abe3cda2fb941f58d30b97882cef5a099c6bdd919970f0962a8441ab6425a44cd9c969d982e8d8626d5cb801cae00fc1c6a62e46c74ec7299352194620747dac03d50c70364570eb86e202a6dfd3ad683aa145bd3a27d143a7a9019cb7530cd1a6a4f5b3c5cbce1f0a985078ba130c23026012b01682cdd8ab350eeff95bb5f11cdae6311c840d4eb285e03ea79b0b099af3cb34edd89c2a246826e040d7c19997fe8e002424811b0b63026bc0ecc5abf22bad8e257d7c7baf7564b4856f899ccb8995283d19fa6162b8918c3410734e8df454b18d944548f410136136842f228abe2fa7a924167460d84963c2663cd66f525d92caa7c159a426d309fa71c454bcc99b05ae00ee33c0062d196caaf390a825aae77837f21d4441e8c39af007bc0ecfb8531ac56f537341028fb582cd2cde7f8942cdb48cf468b263a6beeebcdee6626bdbc423a12c24ebbdb50cc401b3b58e5eb52b63fd0f1629a8aa6aa87081c3e62b758b2249aa24a3bac632ad37ff9e7acf4315ad9a179c0d8b35573e77a07137eb401f7a13156e344834324ef3a6699ce28d9d9710d42fb158809f572aea3920a18e283697378fb04c9d6765a17a34bc1a420330269586a4b079a28100b4a9c9ed98882e987e1b55ff098b101159e2663a778c0af8fede0d33ad13ec78d52eb3801816f3afc7132358e7b7fa09c82e59ba89f997453c1c3b67e3b77f792f22e37e0a8de8b55f7056df7033343dca32fc95f7a62bd450b0393f2851dadc105fb1fc5c67da77ef6cfb3a056543933b6fd41c4c9d7eef1f821c3c88a03a9024faf3731db97c88ccb8b516cffe1357704139092a124055776435940ff9e1a04f2b24c0bad9edf1149d2964848b9baad0ee1c9026858f137227702b9023547a0180949c1d0f0dc7618617d12c383803a7a104d731be751cf50ea89a19b801886d4d88916b73c85ad16785b9959091f437de398426731992b523a4cd49ea274a3554296fbc909d004b6915c9506a80fb9a3e5080127a2abecd2a8454ffed3ad1543b48738fa08cbd73618d83b5a82e695c43c5b075ef9434d732de4392ad52b5488126fa473254ee765d61f39ae4da2d0165cf7b1c13cb0a15310b404f2aa383ad87ca319fd0c088b344eda9089d0e903e28d42abb6e943efa6446f3e700d24bea07954e8cd135e462dde134effd7c3951666469217831228d891001b738b095eb5ad25795114dde1a8a708a3c81a468ca88a41beb520895181589bd8b8e5411890d3ac5d7d933159fe9babb0c220c3e03c750cdb3819a87818fdf44e250274e456d691215b9294f002c6b84b2bdbee69d4ef9e228cc13192027716872134c32277d257260c90fe8a462f59eb9a09595bc9b23038dc0ea50cd589ec99385139e96b44c93a1c9963a7c4a3a92142300c91aa158c80e00238c34a71f27d08a6f4437b0a8fb119d4ce156fea6d62a62eea7634624ae14ff20f1e02850b16108a794cf17ed032c1f61b99ca1866349623ff3465c64641e845a513ddb27baa106c2683948eaa1446ff2211e45bd336fcbdcaad27ec92a099805512cc2b59f7d9f62be97f80cc92253185b43c65e1212118cc8c46e742d6ee50b29ed987b04b3408c0dd690be384fb99be7be6aba37c43f9290aac28650d0e6a4f508dcbede6cc92a75e79127126aa32c75242b4c97d67692db4d2f56ee202829501cbb1b9d6a82ca668684e2c231db61819998ab6c765fc6708e8231f76e5e9fa75b7279c5c2c4b3b52cf7b482faf2c711157dbbe358d0a5007714d5ba2954f44b40fade8df1d9b575e5c84b6f1c0d0998a8a79c547fdf17de0899cca7cc518cf139188c40d26f25b2905d683428168db889d1e01ee6a20db8e713a56594bb1cbeed42d37d9543786ba324f0e70cbe6cc6a191b8382ba785dd2777904097d87dc571ffd76d91d421c01e4d9cc4541a1ee54072d8ebf384ddf51d026a4d917aac523bca0065f2d9a94a7dbb6ee35180e58f44d82bd7c023eec65789ad7df23f81b84477b0826283398f7f164f7613bd187c877feddd9ba6d4a520a20990a02ba224bdb235e8382672bec92d713edb88c65532dcc52b794edf49d8d8cb0d6586d84be627caa05801fd1a7cf50e0fb16793606102a2e07d49314506e294f31facc52c31ae4aafe7e864e1f67c7836b0720a430edd61faab9b1403a9cfebdfff10aac1e10fcf122f5e867f195d098d6599af3186347d7b83e935e1f0301b7363a2fec3a5ad47260861df5f4da2c87f489d27cb658dd4d6798d85adac7a10a0301238204c966e185ad6c7547264df8c830e6062ea45049bd736eaeaac294d3daf9182457a6e4a8b9709d0a24259dc035a31a0810346bd3cae9f5f79649f70abcd6dca2907799c0ff13fd1085ba38dfbff7f1501f644b34c2e20b7ea27e5384eadfe3fc4f6b9a8425118eb982a329e1eea3138dccc583d9eb8877e4fb43b248d5fcbacab1b7b7558678bff0a099981aa74b521d488e02ab79a4bb80b5f0d92ad85a9e01d458ff21ca064faa182875ea5753196bd3388e1153cf0dc51f80dd903e8521b9a670bb4783b7813551b2699910adce52dde67c4e0d4d5ceda80197d6ae04f936f8fad80fd41e473f0d1ee48220c05d736d8d8880538e0469177a8807a0d937fc253192fbc2e518f289a35d1d276f136b66b126289e65017936a596b01187507abac4ad6b8d77ca992e5753db05fa1742d6448123a17976c6898702b2448f723c7267a66ab0c205e53d021fcd91b13f606b616833f2082d57990d70e11c4026239e2dc23af80750b1217fe2faac2a1657e987fc88d42db3da429735fdb8a11ce8927ef0821894ef4cef893730c49a214c03c4eca8766aa6db06df03d7af98c87386503e1c2461ebaf0caa814168b09e267a77008a239088d4c41e3c0958e168cd8b0bd97e2d7dfa9e14cc1ec60963e1734248dee4db0d0572515266c50420ed52b3107b362a58223229e33bb8e6f47e60a3d20f2e8757afe4adbe8299ac58f3ec3ca7155f93305d06c608f08304ee50e53013f23f1ce717e0f9c698d307801ccea2a40f502eda7e178770891f08b91fbd97c2398500c56856059995068ddb129642c4c8fb90084bc829e8080de5a96d3ba148b33e5f1f383573053b0fd25905d8251a6cc4ae3dddecd7d077164ba38285f05587b84a01090a45cd97db5f11b622f03721ae7e536ee8d77791551e13b2f170b4cf63c7a84f8e1c9e6a4679f9cd8116f52f688041ae4fc98068060a807b9deb995070897dcaf134f3e971dd98c71311636a9fd923472d99e1d7919e325675c9cfd003c4718170ba0e3477163bc9731ea15bf01776a2d72c31d9050772c14e706a49e58d225730306b6610b16bf17018d37107231e5f55c3229c81ad4c402eae4981547596bc7b6ed925c1e4383bfe9a47a754d31a4639f7c8c8ce2c12c645a95421c03cacccbd6b0c918c76e258e03fe30ebb0d8f43331fa16d4c55874a3a7d300a9189f0354c90f54d271b016e87dc360a7068bdbe4469850753908021b747f94c7f2d481a7f4ff5640f14c8701ab62e6697a7a24d199762b9f91931052edf014d4c6a44237cfe790810df13f124a7a905743621d2c8b7bb35b17a9b72b25e38a91f10255a49420a7c5164758bbde64138e5f5036ec9314fe4160744df16060c1a7043b3ca52f8dcbe6b59184a760a2da591d8410a3ec2d75349b255a200bd0abc33d34fa4a4ef5b2621293b5262c94e26d4c1aef7d6ac248c0217a5671017cf7160224f1d761c34b9f0c4cd4ecf64f0dd624d43ae31533f318451072620cb660f0ed7ad5467c8e23bc7bc2e7073177542cfd64d81d28b6aab35020efa3268a63217a239dc091f9455ae47a487cde2d9df56d1d4170b28e2ec89af2cc9b2655a75e0285165cdad94c35e3155309ce6036b3a4c7297b7f5f38b662310c4d033edabfa8b3087a39015dc25df6d508fbf619e17c2444c56fdb7ef20ded043ce60d4af863c49660cf123838ece9e74cf70084280864cf3878122ec6a6607ed6055fa13a54af3219979ec318c44e9496da351243400ffc07ebb89e0d27587328aa49fad8891cd4ecedbca5e17d8c25b63c2ba0104430c1fddec594bed3d1126b68f34836b96ac01426c6e53ba731587ccc53c8f2e01c6524bb44a15c9be20ac6fb8292fe63a0f46c2ccd2c64067d3e72e4395e43bb743e230e797491cff49bcd6b4fd90443305874705238e626448862f2d18da370b468d99094423afa6e443301ef7ab80bc9dce2e65fa1e410a5be8dd9d8fa96eb1f922485ad6987f30308cbd1b73a6940cfe861f6097eb8b71e77b20df91aed65cca2c941cf75b079ce1a08e3f0348229925f6c447abf2baf358dd0cfe8b7bc2c521ff9b593e141ab796834855d7f51daccd392a1f148e5ab0287cca014ef1e4b7872b2bd683ab31d2a93e3f311556235fe623a942234a94cb936364cc35fd2b582b8026450c5b8c2f86a75214b3a38b1010eeb9b05125e484a5038c7c0fea9444d98d0cdac0e8ae27cd85701f552627b75f0a966c63d39106b583c832e4c2058186214c45a17b8f3084270b382a4ac2626f73a7033b7e1927f51776d1cae845c1aadfc628cb6a02a70a1b38dde8ef69c1747f301ccc08a17c37315ec8b845d8d7c62703f1ae6f22da59cc659f30940b05e61d97b8905c2e394d314c6b2ac69b44ba1587e29a67ccca84cd582c348cf4ff7ed95d57a5f26376a8d869973038346e09921fd111532f4162e88d2d7afe389390b29a3293d2dd0da9f92610bb050aa5790967a053ac676c537ff86b2d01c3da489a1d917f4c2716c9276e5e095b40d9ea88ecf12ce4e0e797fa31c851aa5a436a32cffe85c1294ee0da8ab9c0d72b927b06a13c009f4c9e9d0c48b89b8bd7e1c34b1daebd214aaf20cb3c6ccb494d7c75a6e8c12850c7c3fe35073c3a22d1664e66c51183dfa1a506242091d188bc4f7f8a954542102e779f57c924669818179616f212763293ca7ad133c467ef5b443aaacd2a2e2139bfa2d2a683401873ec4356da588a6f976ed07e2cf4ae18df0b23e583f9cc67cab8dc4887c670c0698f6957c22929e1e1126580d804e0e9e6bd41d7460db7c130df0b4f82410a8b06bf07869fe6e89ac2c376d489f488fe5730c603454508e4a22484790b568eeed2e2865ffcb9e20b3195cd1f961c49b2f558c9870292bd978f5a75a77ea1d18a5b7f7170ea7d8aa5fad2ab4bf3c2c01329469508402624649e0c2f5b599a1612b8936b3c44efa48128cb0da2ab508c4d66ed129997c88efa1226b00fcb384ba1e2c91635f49337e04e339521b57a63a2df39704e3e516b0936a69453de0d4fcf2cbf401390caca759b283ce9546b3955a31f9bd14236d7872377fdad8ecce65d1b32275bf0d4c3ef3f47d90717b59f59c39cd8a25872634c96460c9edbd02cc0c6ab7e417dceaad3a0c9e340a1b6da0614cabb418aa5700839576fa625fe919f00395ca4fef86c7c7b055de446e7c860f2e563c76e9917ccd8933053b20f00e0796309853cddc82d341f776fc6cde1e44436d9f00b8cbc614ebbdc48500e9b9842567fe5adf2801f6e64f97e3c0e64dcadf2f325d613e617850d3591f2874245fa39422643c228565badc098c640a2047993c8f76113d883bccf2e34c58fa537c9843dc3fb3d6aec58aad70b3093316c416d8a967c0eb2eb10699b2f87b9b32c4e7458a94e962d1508a568f4ebece01cfbeb7b28acbd9463155961be79de601f1738cb1de129d10e6a21ab35841d555ad259a24226d7461c26b97e3535441ba34d19c499d703986880f28682fb99cb7bfb884bb826030566a11b60671f35a257fbc71aa5717f34a008c2e853db4c7c7944f585da294679852d517397af33ca36b0a2ff9f01b69ca701525e19be7eccf49247e0853c810ac762cbeddc7d3cc51b208b7a04d97b3e561ece9f44469747d7722cb5a17138211c535995f18ad252f818f82fa5e2d416de1f6fde3268e39b4fb89a28ad7b8af9990b1cb49677147592d490b9cf8a00a8d74510cb8ed09ada98b02bfbaf114edf376669a871bff3d8d628a6dcb0ed463d9005dcb36af96cc1b3d475188ef65111b906febdbbc7b65896d808b1448a7290f34deeccbe3cb6af8defa8c667089c65b62320123daf89ab940d3156d5fdd5c8fb8031c7b4acca012aa4a95eced7744848882e0cbfef5a39ca3c878de43e06942693e1d40ab73da48f683ddf5f34354547a85a882d0520cca4c6c4e5afe0921a2066089c24b7443e9199b72fa906849cc54f55ed22f865c622e61c375c0c5dc9470084f6eba62d5401e2ffd4f5e22b66999f61d00632493950906f7d4b0aae5df82567ce622fb2bd383f2e0bb691c3dbdfbac835e79f19eeb0dcb1b6f303587a3816e914f58be64025d81d1278837ff033329c0701833ab9d57bdda5f720792a107fff6a2f80902f263544254dcbdc3eb7351e76d2f762655c7ae8b30c8780be2ac2ff7dce6464f44f7c1f3606907229234e177b39bf6f6c53b9abafee94a4b179fba40cfdd73fef0b758d135b7dced05a37022b7ae3bb1ba3bde3d0bef2ebb7b6e4015b930e9d6f7bb7511f053bc4f784966097ccf79963b87d261880ccdc14b32cc54d0b9eea14ff2ebb82a0dcf4c345942ed9644daecaee4f780d53d8884654b5d4da2ae0623094eedee9189aa04202907f17c57979840f1c1aefc28f3464811a5e2c5c5086b060510ab4ff7124d9e41cb14e31b2bd011db226cfb14034cf2abd1134cc80ef1f1fc54673f054c7a9d9ee6fb0ac3b68108ac8cb59c6d63f9f21306b3c56529dc460c6a620ef72e8838ef8dc45aa1cf16ce323f230585ed8a9c7090a63a4fa0943ec4c4aaf76769f05806b4bd255640061f8068507050261547e2561a20cd680ba18d8e169b51f799c940be7a6dea6d2278d6bfd525f70397f4955c8c19ad34d905036042904042d6406368412a224feb310492e0dc1e2d60686f6afc2aa29b9ba14f0a3ba18de19709eed9f15c60f2193e855f724319298ed82b0832249300d32bc9bc045c670b478058bb23b0c861f763984aafa14d4df9f8273026951ea5a2193810b743badf459c94663a1dbf601b882de136264430014a90a1e3c7ea2eae8f20c36c40efa7b3b75a6a5218bcf5fb7e075a6d173e226cf6766ef39b920dfcd1cef0e53be293b63b1d9d00c37ccc76bf92c1b97bb7d3b5e3360458efc00873f78250071d5df87e351497de8f93aaed1c62358264eeda8f2360e19e6524c5dcc8970b4f23a65a6809e675f308d98aaaf2a82e330a2905cc15e5fde17a27d404b5c104dc32e1ce98e7f86738a9212331d79dec3dbb567e002f300b8503877c69258034849df17397ded4bce1edb7b6f51bc6e6aad1b03a33c954411d34bee200565f0c45a5be2b6c971a94316f28cbf90a6d6014c783133ed5c1516fc0c4638762caa51420ab1b435803e123adb0a7d179927b4fe401298dcfe468000874138290ff4366512262007e48002d848173e0a8adb00d0d0ccff1a0ce864429bdac1e0779eb0b1bfdb704ac5e2dd01f26bf6eb03c89567880152ed36224572f9b85f23e298465c7158e8a1a888f5a6ba831716ee662c9c0ab0a1d30a1455922f90db1ce86542b860bf3ae5078a84445b4441de5e47e55dbf632161cdd52605ebdf9b765b2e62d4b30a5b442e3660ec00e7089bb516a6d3efe91239f779d123112796a36f6844742a6a0709a075832b380049faa2e0904af1feb6ff50d482bcd449e44a4a190de608f44c081ad415421aea3b44c622dc05bc3da4dcb3ab257e63ac2169cf13624d2460fdda13bc7256e3287fb4a4b0c9ac8fdd0d1c7e141cbe6240d0698592c9c2a25a8a1510c0f87c10423f6e6bbb66e5875cf41dea3f88c81de1173bf7e9f8231879f6a9afa424e721ca4f17dcbac68124646a18e0c11a7247acfe9171e07fa5ed89d7db4c907ac22b127ccda14fb8945f787f4ea6a4c69f580a8a93796e04ecd14d7ae9516f9600ba81cd1e858c57c971064bcad9134cb2b94eaa438b8c6e8d1c5e20617f5f72154a5f44e515cd4c61bce684e38d2d58d9938a9d1a2e03c6741250282d19645adf095bc0f4fa8aa9591c51611dc3e53c02529e9f494c8a0b860dc42f24b85d7134b9ea50a72a68a0ae0bac20a8fb88192ee59c6998ce2b959bbec44805f35a419115458cc1ced294a17ba30392c61842716d16e027ba6700617b7ef8c11c23c7141d278b717d53bd922205ac3842ff070536ae0e6483611c9524d6a1239a23b84b9c198e4804dc8003f816feb6f5078ef10952aeabe5dfcba45d41f155a8cea52145458c5aa0ea4c0e05f39ff2cccb56897478ad6e5df441110490e7448cb231d9b5d5be9afb4739441b5010773b8a8578dca4483408a5f0862648c3dc2dc3e6ce97d398aeaadcab60c769ca58ccb13348cf9c3dcc9e401a408d017558ff4495c3d88d7f25aabd51205edbb4c7cd2837a2505024de3ce145426fe0d7fb45e64f402c79dbc0c4a0e0faf20009379cd4a64edcec883cbb90f3459a116f288f24b8fef6a15e14520eb9c388cf7612c0032dadc55b942837bdc080b65512daa7efa1da6d128c6ab3f0cf3475a4d2f985d25a059c591582bec0414df49b29db5e55a1f2a747483cd03fa0b2e0f5f44ee07357f413799789261bb617f2e18493cb0418c18866ebce99360b8a6375905bde93de807210c29860ae2ccb1e44a2123930f5f7ea46367a486af04d291a3e423b63481d7a87ff0393864f32c82215a0eff4054491e1171d2133b22904b0b3cd996013f0055f125491258fc96462c9a7fcf4ba01b5e8b4c5d46535807c3812ce9e9d4767b9e198f53f6efd3531cdaa6d85343b1dbead95dbadc18e517e626870bcac0f2a6d4ceec704417842ce79e4b841bb1742a10ba52aec00a80c741e3757502e182d0f7c31b8164b6b804e96a06dd5cf52a2ee3aae7563ca8eeea6c7e82f82156c29e43d692f90d159b7374f9550a48439f80565af1dbe565718fea436d4a2801112cd6ab1b27092a5b1885c959f5545abea5f07b699c2df23714e038a4acd721a2ffbb9ce7d5bfed0442ab39f6daac7c9040cace6e888e1753885591a4c48986a96682cb3917eaa75c8276cfc9d08815de897842945491cad19fd9e77157d2ec6d254f80da46c24f55e5959ca4f7d84d5c99049882115b479aa42276d13e256d03f1ec3615c87190336646c8cde1dfd7753a864e98ca3d104d8abe3a1c2735b2f35a18fd236aa5a3d5e518017ae6d94e3750b024e9f3ffffffffffffffffb73f03f57fcbaa6f2d4a59fb65da8944d5ef7a807af9a5945292292535adc8eed48146180210c7d90f0fcd04b804bb043930d8886a0511d51e5604c18129a88ee8ce2e29778a6e61cc0e3fa9593aa505912d4c26e8a8242f7ce7b4502d8ca32fcce65f88160613775626e6e9704966611a5992248bec9a51b92c4c52de0b4ab224562733164651f9203b5ea1920a2ccca9f925ff69e715263f694ef45eb35caae30a73b410bf22fc6e85717ff75450974fa974b3c294727e3f69292f5db657610cdd15a1c397aca6b62a0c9f3c4fa9b6b0cc512a0ca3b5bb247d720e9d46854157504ac8c896942a9fc2a0964d343997f2944d3685e14f3b09af914b618a562373e123c3362685419d65cd77d372e11f85a94fcb522ec93a25b4a230883613dc7cb4a130459f5b8bdba2fdbf0585c1aebbdeae4f78f9fa0993b8f112bd4b3c61b4973f5953564f693b61529742af8e494bc29c308d2cd5b7e6269c979b309ca4e414e646fda8144d186b9409da62c7bdd93261743ba5c4f48612e5524c18d743a7a0b7d2bde41206bd7d92fa5b3313479630c9c9d36739a92cfa5a09930795d6b994a026949430492a98a4353fa5982d2761cee1525a923f5b9f2449184c3ca9663929132a14099329492c415d7ee6af903028252c589e571e61bab85b26dd2d8e30ff49b29518a37fd408f305b153d9a45c7135238c5a1743fdf6d8d58b30a97cdecf29ddc3a98a30c55f117b233b7b4926c2e4f9979d4cf65c628588307adcedca265db6241dc25c42a992666135e4378449ae0acafc94a082c70b61de92a414d51f26d784307cf6a4b3a7a4648b77109f248ae59c94f43a04611272c2883ea9b33e5e47208ced96fd2cd72a3e781d803098bcdf2ddaf1f9afebf88349ca9e35397fadd25b871f8c9d46277f2fa5fae2d6d107a38a907695264ab4a475f0612d49f2a86aa3edc194dce7f4da45857cf46014f99fa7427930d9bf7fc9322b9e82d58107737a3225f907ad97628da30b1b30ca021d7730d58e651b79d28daa708d3383049fd810017f62a346c10e3b18d4cb829026d71828b83a987265b760e22bcf440a74d0a1b4faacb41d9f765dd17ecccfb5d4da310793fcd78a77d5d6bbfa8b37c1c7c7177f430ea63c3d6d82aebb78d566ad1d7130a95ffa24c90b71428dc8011280e0011e30c103d214d1018752aaa0f69682e5aab16e8d35134b8e8fddd8e90d06e9c94cb419d31adaec7083e1c28485d94ebdd0d106e3e757faeb3a134c996a78a0c6c7078ef71b5ed8c0c10683eea7afdb9e3a99710ed7603c659e35777fa4c85a6a74a8c15cea3b093725cc4aeb1d6930760e1ff9e5b26e227466c4a0030da6fc567dda2a7781a8e30c0671d2f66a5aac8b52fcbf683398925e7ed51eb598178db1d6050dc7e4c60874d05106831a71d55be9c6b4cc186b33f87b1588a1830ca6d015c5ce4c9e54aac73106e3abfa7dca0e93dae4c458cb917e4689d12106936be8242cc878d1ca6c7484c1143e946b5d8b095d79cae80083a99312daaa748fe58fe18d8e2f983aa83bc94e4ba9138cb41c1d5e306da7f4b5143bc274ca468e7619334e20828f8f62f633109251a3a30bc6ff9827ed725ce6ce1b7470c194265eb08f224abcd4b760f49c534990935544c41d5a3007937faae64d7ab2307764c1582664c74959d34fec3bb0603af973acd3a1730573561115de4411679d596173efaa32b3f4626a1645e856ee58a9c4f3245605b35a9838974fa869eb686083031f1f34b071e3860dc43be8a082f1a49ba59c7f8a9243018d16952585314fbd7fb41247a17d7b86cbd576b5c77b66d50553a1eea41721a2308d3a0b1e772c47694f284c232e94459384e7eb2008283e615263e22f25d9669e4697104f98fa724eff29492645afccf81b9d30eba7ff8a9ee54af42cb231830b183564c4c82183069c3068c895346aa93142c93000d9845a39b32d879798dc9593e4a6098367f595cb7f501bba7172c090811a8064c2303aeeb39a9c3e3d39709c19850993cef1aad6a22b077209b349258bc7ebea5cca4518104b184b12de74f2201f3f59953057b9df875c0e29614e523ac164359dbe5e9a8471cdc38d9849a1c47c499892641ba271712470f3e81e2a42050983f6399da6db7e84a9da040ff2a2234c694eca252559f600d20893b5e5b973b5a4b3fe3b4698835aa9d58f16bd362ec2ecf21fc75384c1bde4ee1d5dd2c929ba904498b4e7fbf5511fda468761314c90dc18419af127486a7861630310442c725e2f676f2b2ab3e5df5bcad28dccf77511904398a454fd418c969c32ad3b4398c2851df74e3ba410265952760a5b32e79e12c2e8e676691ea247251f3208c39a24b78d888230aa77b40ad7c1c273c80524101040e0073282fcc17aad39fd491a75e5dd00e207fca4916daf2fa40f06f90e9fda4609840f66ed183a27a5ef41f6602e13c2920a262607a207ce5c35e336cebd66ecc6e2f6bea46f06903c182f77ec4acc0bf9240f0382875665e4364d3353e5d2e99c961c2c873e3d668c85dce102103b986f3e7512aee259142da40e667f936221ceb6e3c5e960981d3b2988fb925abd39984c89ff9cf23b440ea6950bdb2a5a2a9f4a82c4c1a86b96f2587d14a15c3898df5e3e4ffdc59b4e7907903718566f4edeefa9684a47216e307fa8ad272b35f9d678006983394bf612f7923fa5b18f081036581f749e135eca5d83d9c2eac77559bb92e58d43023560ba4bd7a794dd7a1a0ca26f46e4e9a89ed92068c8cdd4d52e5cb7659a9b7b8f2ae1562f8f2067307cb4a484d99f8a0b2066305af2e416fc833cf9446530bcfeefe574e2daaa044206f3e5205b4cdef2929fc76052a37d3d1e34e3348688c1a86fd144fa6e8e12475b0348188c174dd2274c4c99bcbd0b08180caea17e57b25649dda7085a00e304902f98bf4fd07a32aff369442f205e30b9057513457b8a5a62e50b48178c557fbb26e7564f2504e142f24dac928fc5da93186bb702c81698b50ed5f373fbe1a40573ea3fa58352313c9664c1e415734c1ce963c1a8ad5d7a74ea573078aeb617136b2b98c6241d5feca29b7cb80a7ad6b8ac254d8fedb62a69491ee11e5f5d1840a8608aef31599d84a995ca14ccfb27f7eba9acea5a918251bde453a1ee954b08a3603cf1f54e8b1a14cc39ce86e5d7f9fc0483b61a0d4bca8238e190259bf006a40986535fb2bd6f564ff01c01c2049310a74b7f76ef7cae41966092847e3dcb7de97fa64194600a276246f498902498f357f07827c9a6764a4830dc8efb7c1c1dc1a46f525c6f7c50af18c1681e522d8c2839e73015c1ac323a071b5de28d4430c6a8efba8e52fddd0fc1a4334fceddf5d5878560d6b41f5df766bf571204733c11da843a6942f44030dce5ccadccca6ee90f4cabfe73153bf829a1fbc0a0fae4fc7b1c417a6032e5f317e6043de141101e98f483099526277377116407e6135bffdbc4835b164174b085322d2457588a2f240c7a544a3f25a5f826fadd230c32dba6041d3d098f9f3b47183bd48e59ec85b7cbdf35c29c66a14a08b1aa2bea648471d6d3c7b8694982c7298216c028416411f6918a30e60915c6fadff4f2788930e8d2654ab55cbc31f1106110f5112754ecc509fd0e611ccf155bdf4bcf8c8d6708c3de98dcc9e3df8a2ac12b847d1c218ca95eb93e6f26e860d4b05103060e19798330a7e55714154d9b247f2708535ebe6a575341977681305c05f7dd5ddf8ff7f10061f450529592e28df707ada2c9d85a18eb92d7f2d1f220c3cea43cbd88f8c124f65ef9e4545e0921d207939c75a73b07cf41590731c207f3872bb9b2f3af88e8ecc1ec3958aa2486a6589c64b089e8c1f871645ec9e1e46f74711f88e46105113c984b09233c58524a8d6877309d9af1f8bab61d0c16f43e3c7e3ccb35eb60d47093dc47ae5abaa509113a98c2fdd44627c152d49f83c1b35b44e97c729768cbc13ca7bd7979b471307cbffd9d56090783d27fae960455e40dba99c6c9aaccaadd89697625bfb05a8288ac98081137183eccd6e4bade30e9b7c13ed4236c30fb5cb2de9392246889770d26a992f8ff257f35184f4a4196ca2db5a1b3481a70537331ad1173bbcdd4226830a75cd62d9e44cf60de123cc4a2b7d696680693e5186fa6bd8a94c1f0e5a9e488d08efd6c840c863d1d4a9d9cd25fc25286c818cc77d2ec86b84a5391188c716a6a746cf912ec240ca66f533a844a3a184cda757445498b279e141c44be601262d55277f75c561bf182f1938a1e23c7bede928b23d20573565f532709251be18249f0cd107b674ae7186dc164e1d2d55aceab33fb225a30c9916ac95b4fad11c982e943c4e849b288b9e41f0cbb1944b060d0b94a6c4e52929a14bec815cceba574112b186458f83119179b1ec58b54a1a895c62abaa6855669c5c837f5aba7741ca182f14a6b9e78934fafda8c84c8144c616e372ec7ab52521129984e38c9e4b55c6d498723513025db4cd1fc52d2348582293b33a4f607234f30c75fe7ac56da7dd50f224e30d8dde97dd2a5ebecdc4813cceff1536787fa2b3339c204f3979da57d2e3df1cc8c2ca1a08812cc27c529a1a624c9e45125c1543bcae37dd43d331d120ca2845162a5ab8cc8110c777bf9835b122fa5ce8c88114ce2478449561f2d0731094cc0813343a408c68b9e2ce90f675174878508118c15547928c1b38b0cc1b42588144fdb7b4acc63ac71062242309678267e4a90be732119448260d2bb24a876b2731c9731830f87a5200204b355763dab4a4fe407262578ac94fde492ff5368d8b8e101ec42c407873d3d33f28332d20363a509b1bd4a4a4e1131c20393a835153f5fae0d4476601c15948ae835233a30fa9a5842d456bcf8d9480e4c9b66629fa0dd54504f0407a67613d7e4246af49a30e4162679af976e517c456683d8a24fa9e40ba416661deb4fdbb67f75265a98b4099e539263eac50e4166611eebedd1c93b76d09185c184d3e7c1d2f4e5edb1309f1c42092c4cd2df099e2fe7786a9757982de6e514eee49b38b97585e59e844af1f25e32af1587adb7dc578a6bb1d41631bbf7c9533065b1c27c15c6c6528696d3294280acc25469ea24f171559893ecdb7925e8a4c27c79a93ea26254dc953a94f6fd8b0590539853de2e29a72bbfff92290c4a3b49295ee3545fb414e6edec766d12489b80d904eeea1c90f6204e5527020ea8c18c1c31b890516302f5b1fa6110f01c38bcb8518000d4004d400100c881c30b13102000cfc5f1828b1a6706020480e3460252d081057800878c2e8e0202d00900e0c0813e08208093e3241f0508c0c971121b373e0e008001342001375e067e72e080016301000800030e60ac38f3ae0e33c6f299176734fe068c2f64243092b7818c19a72d470d1925f01804cac87123068e19306024c04310080306023c0251832e008132727c2123f9431734dc0f07f0e803cac891d8a0e1050c1809f0e083c98288f07e17f5b1d53d98564e70b7d01e3d1894f7869d7dec2a59a3f134563df26070d191db26d8a8b9ba701a4f43693c0dc45143468e1b3f434617346c787ae021c7e3a0f1340ce07107949163460c1c07068c0478d8c1246fdea64927874cd73dea6014b1944fba5eedac241e7430b8575f5abf246b29f939986329f1e24b28d93eb57230bf9a5d3ce250b2f1b42b79391e70306ae824cfda492de9471e6f309594462b08932495f2a181ebe10653e78fbba35715450563ac212f7058063cda80ce1f3f28398c5b540627078c19e7ac051e6c3088180ffb1baa3af529cd630d2639fba34b928331d6b6021e6a30a5b4bd26a898ee114a7028073cd2601245674d13c4ffa90e3f3e7008c1030dc597b53c6562a7dce30c36f030c395c13ec870ad6338317018ce030c7785ca655dba979d6dede85c5249269e7eabc4587b191c14d8f0f88251dd4efd72b78c32255e308a5b8fb78f55504914048f2e983e69f66abff8bb29f1e0824909a567743479fc6b6381c7168c27d2b2456f71cb51d5c2a2f55931bd6b564ff9e88d7713c4ee910583fcc99eaaf5ddb88e0716ccf771b2f33dbf9a24ee1c785ce1f0496a1d932b556e8542456bd3d2c557c5d57ca69ee5ad932f7854c1709d836ba79e50c19c644b313b94b833e314cc6fd2aeabaff86875a460ae5c9e4b7c1ad1ec2b0a663df973c8311f0a264bbf21272ea5028f2798e4b6ff441de17db99fe539c160ba72580a7fbf2709c9e0d104f37712af24ef4f15ecf66082f1c45db00ab3aa173d5d82c7128c1e6c54c7657dbe42237828c1e44149426e554efc94f548027a2001cf051e4730ddb8c7f44fb2af24b111ec051e45307eb59ddca3abe7e2c687e0e3e3091e44c8537fceccd7368d57e03104cf3aad644ba8fa3709a1ac8e97042541e8eaea2e6d78a5d02d3deba673cea13ce50184f5f881f94c4527a54e849b8b1e3ee8bcded2b344343f8e89631d4f4f25c6dad7b81998ee82867bf4c0245a177e540e83c18307e6d6d172ddf7bb73238f1dd8470d0f1d182d2e659b8c8d12d6f7c8817d78e0c03ef416678bfba8458416c6f22c263dcb9da4adcec2a07562da63c57afa5316f64123120b8585e1444f1b162fa556669157d8872becc3059156184e4c344c0e79841595588a5517ab2dd69cd2b3e079b7fe22b20a93877b497aeb8ca8c2d4df5ff949d653bb4b85f1c47ff5e92b820ab327134b6d07a15318b44e304b734fc414e6d1b72555e543f4f752182c9a3e53fa7d3f6c8c90c270f22de5a024c1844ba25198c24d4c2a21e74fb89f43c6f920220a83054b424789d4930453241486ad115baac2fddda423a0309e1aa5199653d9c51df984e9b3f7e32c684f26ab114f184f734476344148ff3fd20963bc55b9ea251d3bfa08274c2572a71df224d32a463661ce525efaa944e3eb8a68c2a04cd6df7811213f7432616e9354b4362915c18449f420972f27a14b20b48c1425689f6ec83824f8f8d044c4128995306585eb2476f696f88f12264177f6abe724b953bb9b84e104a5255c5787f5b8ee24613853274c3bf64e30ed2e1266ff589df428854aa853b75196c32808821808422086e56b920e8313503070401a8dc5e271891ee9fa1300418ae2e07030128d03e260481c08410cc5300c82200cc5200cc2300c637005d979bc62f63130b36589ba7ce7d9e5d1ffea47b4f9655e35ec945bab29af3e23582b974ffd70e05fedb2f3ab5a225fb162270aa868e2bd37c31c2663ce99616128361c10c6189d2c9370d89adec83512183e68126746eb68fb7ab4fc3dd619fc36c34b9af84f34e5c6cd4490ef0b0d8d962b530608cc16214b72459ae77cb9690d418b3d870919ac409a28199d9720a235191a58dba83f9f2814a7c9d460f19783e83e2b47e7ee75481c3a29ae359d27f4b108ead259242f3342155c8f4482da07897c9acf2540d73f748c109a440bb18b4f717de41812994799adfdc8571e7a9805ccab9d0c9a604511c0fdade1c95c583460d4fdf1b3d1f5cbd96a79686106babbaa2bd0f9cf706cf1db7a5165a52454a1d4447e8b82c6f816636e9328dba0cdee2f3e8722b7e62201b611b10e85a597eaf3a9d1cd36558b5fb84c70f68b38ba942b49f3549c6d58a16987a247bd7894ad1b29c3e12ac290baaf36dafc3fa79992f4421ab7c4cbee943ca64cc3af2e11c46aab63a5ee6988ace3f7eef0faa488c7c54d1debfa0f67776d11c8612f93b296d9cdf1132d9c1a0d733dfdabbd34ec0389553de28ed226bf53312ec50da6c6ae8823fa6ae1077f8bf3004658bae8a612404f33b1ca3f4c6d09d316252c3647101c3ee0f340a30945dd1a1fc8210ce8f7bbbebd23e01b60dd28f9e0eaccb0f072fb86561074ae4d73012a1cf81309b85d963591bbf0a274f40032fc270debd1d4eacfcd79528074251a0cd7173a53353b2bcd9e2a2128ffc4881001ed63ca6efb01b90ea5553e0031de199944c3492af3719a43026b5e368fa41d96daa1b22b493f722fb5247f09c930862f32d68e16c56136352b88da9196e46642207117e23ae64b48cbe1155c8b8ade35785ffae8b5707524cc19ec4c915f4ca13499fd526c93d2d342f832563717446e8979742c6b9911d6367a8eedd650d746167f409f2ec120945b1dad143da00af9b0437c00cba6140347c088cac92549894713fbd4ec03447e7dade80ab7a366242a71f4c27b826c3bf6412cd4d62f44fe1a9f142cb3df33358ad016ff86f3730885f12c108f6f0ffb769a2ea424e68ea41ecc8093fa76d880e81f0c240f551762285318bfd3b7836ecafdea967cbfe0bfd07a83293610602a056e0325df26732b78e44071415cb607a1109d3f336317c911e77cb475cc5712c75277430b6261843acb318c4150a8bb49c3e4b8018b791175ccc7d3d03595aa33028bd7af6af67e3b1d2713d1b3a56217e0e287ccec3f24031591178dc550848a1163e17781dc7186303b7654f184e4aa8de1aeec7f4b2f84c7b006149ca47ced69b453aee3c35d91d493f792e84277f872811c000e61df99834b9e0f6759468b2417e4ac1d52cccb9f596ea12b7cb9c0b7bb872a023a21434e2896f80a3b33f4fe1e8b40f03b0e7c20fb8cf64e32b32fab86b4cfd18c7426e65f3c139ea0a15191ab53df5ff07912530e052635f6b0d0308a1bd2f824c775096dd5905f506ca115a155807829cd8b7f5f66fc44c44b221308b1c8613534615acafad3d6631dc34cc9fc4a2086cfa2ba232e40732a4ec93fce6d1e0254078f22f2fa74a62cfaa69b13aefdc2fd0a4e9e2d2077d03ed255644f2ec34fbba5f0efc96502d2b6bf464833479a9fdefbb8c4ece33e20352e8710b667ef796e1fff5b8177278eb97c487c26ec80084a0381f3ba2c4a505ca52c56622b4230c2f54381107046f82993d725428c70d5799c44f5167b3b319a5a5f5e45be28fd574540e46878e433a6ca1021481e7e03275bacf10d0ae107a8058c408aa2f4ab58caafb1961880f794a7feb5bcb59931fdd16c4b6b612093b3993f0b41c1d4a2b175d9cb4aa568534065fe604fa673b61bea5622ab76ce0334e77c870a026a7d74eff068fff6b7d3ca28c462d69c665114f1c78abf040df6e4cef31bcee4e4bb9c62961e4b2a70b592acb5d152a26c9bab4e1168cb27fde9c174e9853fbc11a92bfdcfedd69b004b6fab98f70b397438080bcc61db579a24ff529af59c64735ec483cdf9cb304a5562910303b3226a71df9ff171369c1c6b0e110a7c9fe52a7c4909340aa065e1b96e21fa661c51c6e4464044a1b202756f2adc2bb366612925ca81d857a2e7a72ef57f72a7e5144ef4019d17eee8d98c0b5df9e75cef1db150720f755ecf95ea614b4bd2fa6262406ec2283314730081c43bd3a982b4c5fe03b5a205d41e8e0520127cb650df57e75725a9f12af4645f1f3f2def1571153997f8858cc67890a5fa3d0be27f15444984a1a6090e5c81f89083ccefce6356253eb3c675157faa8ccc1696cf0b2d52816d3568f0dfd8f5edc39f4afe85259e10129d3c86edacab29cf52a5c40f9c417a80c7c45782737bcc8b2e4524deb942c328035b52a22840cc104fa33fc2a0e9d4f1104df37d483a30a7dae084d405f424f7ac98b2dfbe00fba44bd0496e05f88ffc4519b28a3b7c7531c928d7a104357098b80f249f454b6426c2b291084a7fe1d2898f7edfc988684b098b8cdf473467a8adcf4ca7f279946c302238ddb39585d64aee16ccf01d34d52cfa30b3ea19c82295dd17b790d64ecd54b88300e7380b756d1c1dddec34175f91cce3c861a6a804941f2b4c8e1e123211dec5c105ad05cae072402ce47debb0969b7338a9593995990bdf2c56438241cb4d5719074096461cf2c02b6e9eb71502ea9378becd5f11b8fa4bf0b31306784665f13f69391be27e12062f1a0ab4e621791ebc9bb5f95763d1dfb940053910a9c51e792dc1bc71190ca7286ab62045d90449a4601286ff5391c076e8290bd93631e52618b641e0599ba6e9507cc5777b72fbe0c4889a1495758a33d0ce825294a36aca2c4493b2d1e510132480f4b2c21221e1a545f87e3f2aad3608d59f554c9713bf74e9cd2857bf4f0ef5121ffe7d8e34f5c0765139745447d23822236329d9dcbc3e313d522e612946a4ab0d65b5c81a3ea1188d6b61dde1da56d69203eb7d95dca060d02dd6ab89e336a984bef70176c5302f4b007f50982a7a37ff800d753b25674c14fe514f116847504991189561bc39425d2de0a353a43381211bcd401182508d64d5e99aa8cdb1960788ce4e73ea04e0d34cae4f904278436926015f1efda4fb6a4a6b7f24c6ccddecec9cb53eddf8adc55fb51f38a84fedfbfd402d241ed383e097338465cc006f9b8c2c54b83b5fb11e2ebc0fdbb6bca99dc3514a489bfea47b95a96dccae4532096855db5cfa9ba136cae012a03c74205446b79eb7bad806452a9a317fdd9e1c49ae80f7412241c6d2dfe5b05983808e511455ec09de62dbd40a957cf8062935b30013698af69884e1d6aea6b6b40325bdaaba115bd538301c7c67f19f2c79b05e1f265399aaf61e3dc2dcc106df34032dd935c2d3a44603779f8973bf1b08a0e1172420c532ec1e8e2a4997ec2ef58559c8391d837eedc5e1833e96be19c061cfa7975f27976df4dc4adc315e2c8455910e8752a6b0df326b0e51d4be038d0f808f53ba94fd3b5d7fb31d7fe5c1fa4cd08e68451d436497b12a34e2f08bb7191c7607a64871e03b70c11f4f95d6b2ae163d36d2edf20bfa14f19a3dc97e7d7f421a41b7083da1ed9ed973019202d6a3df08062e41100202578d21dbd233641079513bb2c1114ddb88eecccf278965965f11bf4292ca1bd89b68cb1096b11ea23d828c5226c54b2ac7b1dcad418c6e8e5dc0f8a66caaaa3020617f6cb970be5ad79723acb3e94c29a9e083745843c0c433374a610837bfb287c7e81f71092f85de62da004e7df15187f3dfe9170ba10f1d6368936e025758672b32f6e6f63aceb840bea38c14224b12b98ade25d6ce5a2a7dc4d7a9cdf15059682ea533112449b1ddf639b7c7ad38798e3029d1f85e8246b82f117fe920160938909ab37221eb4f221cc5d61dc85065dacb58970948602ee62d8d67b1568e90a882d8f302bb492d9e8d6f356255e938b9ee3b3b2690cb4a2fc684a85908520b6380459343d4bb13bd515d13713dc6d8620e230369694e03b945a01258811292adc93093fd5c9ce59e7bc2340d1850f46998ed94fa687d75dfb717e4b615ec8768e1ac8567dd566d87a3a60f401cc5dbd906ea176ce7663c050d0504d87ec4351f051414d51485b7a784dd251fc54ec61065b76a129ddbd725a465a4670b40b4bdae02b6207a49950094812e368cec46b1007e34ab8d25a957cda15e9a01c9740174c2252ab0c9fee3cc8ef5a63ab75e66f4c92582d6e045d3cd05b40084cab04435ba291a1798b49d53ca7bcff080ef3b72e0d0482795af2d5fcd75190d9f2a95f39e05592188011008bd8627b6fa2437d943024da2629f674a1453dbe14020fbf2c7776136dd083178a070459c198d58b3294c3312138d375657fc22e9e1f0ce3840f64d402c0e694381eb95901d33cb95a2670323761d7d94472360752625fb59167ef1236b07115464bc86cdc625e165b1c42665386302019c3d333d8ee701b3fbb382902ef6194c3629919599efa64016aeb24ff80be1cba9450e7cbd5ed909a2d50e83db096a94ce681a3954073e5a0649b7ad3e04eed4be2995516f199008ebe36ba6e45444061fb10084521211aeee0de2647a839c64739802987538a9e7c8c454a85d3b67c0a648f36700a81b5de56254b35e44bf95c19ca62017b2355c029e1d4eaa6524139e32ac8f6d02987d3331ce73d4401bc672686e31ad570f36c5c8bf611042c503690e31d14c4bc1193115f6d003daa0bc29ba08099bf21548124adc09d5ab4b7d8c7a0010155eb6ca7f75ef2950154da89680404e50084d806c45d63891aa6ce3412ff96eeb9336ca17d0d6f350f925f91bb262730b3255a91f3ab6235b124a42ab81e3ecd1e2efefd3163cf2798874b3dbe0fc7f898bd9c9a3ab9494bf8214c0cc6f90a339a43fa8c8cbf8310b0ccabc66323a5e8d9b3838663282b27df5ac041f764323d4b5fa8a5cf9e6027fe1f85d11b20c4a8bfda294453132a311026c49861a20c9d31abeb2b2a8538580f522820e86195d7aa7acc39213c42a1d64acbab3146e22b9f4f476898b18647dd9529243c95edbb07e1a695f73a9df83ef16772109a26a38fadd619d4a6a0c0f0932500e9d868a481c061f0765dc3919ab40b6d274b8ed680d93261225397dbfccfa33ab1aa60bc1fec8acb6b0d54af50632da13523e378cdc0838d282e6a2fbb091ccf009a8ff16136de1945afcba4b97455f8c3b28b1eac3ec6558d0ba13fa978598b01241ab0df3a92741c56e16b495080fba5ab33b2eea3d18fcab3cf8eaf7284a6fa7e005afceb79bd5f6edca8c1600c3687d6e2f9b801007b6be708055e1a941c3edc5c97755b8bf97e46489da9033c9217e20395d3c788ff57308cea45a11ed8ce4222a7641c1f5da012d892fb6009020606bc3a4b89552b6bd6263a724e398b49e31389445f4aeb814", - "0x3a65787472696e7369635f696e646578": "0x00000000", - "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", - "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", - "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", - "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649", - "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0200", - "0x88fbb13c02428a6ba0e3c362f503d78c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x9ba1b78972885c5d3fc221d6771e8ba20f4cf0917788d791142ff6c1f216e7b3": "0x01", - "0x9ba1b78972885c5d3fc221d6771e8ba24e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1": "0x01", - "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000100000000000000000", - "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3401bcd1e9f3885b9b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0xb0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb381d03c816fe51e89926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3ae9e7a6969af6726a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20": "0xa8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d6668b8260aeead3b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575": "0xb8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575", - "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500952b0337fcbf1d46175726180926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195017c489719c28aa986175726180a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20": "0xa8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195026ed82a0e5bfb6c76175726180b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0xb0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19502ac2136394fc85866175726180b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575": "0xb8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575", - "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", - "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", - "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x03000000", - "0xe81713b6b40972bbcd298d67597a495f0f4cf0917788d791142ff6c1f216e7b3": "0x01", - "0xe81713b6b40972bbcd298d67597a495f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xf7327be699d4ca1e710c5cb7cfa19d3c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000" - }, - "childrenDefault": {} - } - } -} diff --git a/cumulus/parachains/chain-specs/collectives-polkadot.json b/cumulus/parachains/chain-specs/collectives-polkadot.json index e9f690234e4381f54377c7fe7174f25834a973a5..259669cf37a0643534eaa5d1776daf79ec402d75 100644 --- a/cumulus/parachains/chain-specs/collectives-polkadot.json +++ b/cumulus/parachains/chain-specs/collectives-polkadot.json @@ -25,7 +25,9 @@ "/dns/collectives-polkadot-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWDumvnNwPbBg5inBEapgjKU7ECdMHHgwfYeGWUkzYUE1c", "/dns/pch13.rotko.net/tcp/33573/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH", "/dns/pch13.rotko.net/tcp/34573/ws/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH", - "/dns/pch13.rotko.net/tcp/35573/wss/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH" + "/dns/pch13.rotko.net/tcp/35573/wss/p2p/12D3KooWRXudHoazPZ9osMfdY38e8CBxQLD4RhrVeHpRSNNpcDtH", + "/dns/collectives-polkadot.bootnodes.polkadotters.com/tcp/30526/p2p/12D3KooWNohUjvJtGKUa8Vhy8C1ZBB5N8JATB6e7rdLVCioeb3ff", + "/dns/collectives-polkadot.bootnodes.polkadotters.com/tcp/30528/wss/p2p/12D3KooWNohUjvJtGKUa8Vhy8C1ZBB5N8JATB6e7rdLVCioeb3ff" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/collectives-westend.json b/cumulus/parachains/chain-specs/collectives-westend.json index 7385889f0ec7cf82b702a2607e1165f47564d0e8..e459c631f8be9df7c5c52993c116f11ef619fefe 100644 --- a/cumulus/parachains/chain-specs/collectives-westend.json +++ b/cumulus/parachains/chain-specs/collectives-westend.json @@ -19,11 +19,15 @@ "/dns/boot-node.helikon.io/tcp/10262/wss/p2p/12D3KooWMzfnt29VAmrJHQcJU6Vfn4RsMbqPqgyWHqt9VTTAbSrL", "/dns/collectives-westend.bootnode.amforc.com/tcp/30340/p2p/12D3KooWERPzUhHau6o2XZRUi3tn7544rYiaHL418Nw5t8fYWP1F", "/dns/collectives-westend.bootnode.amforc.com/tcp/30333/wss/p2p/12D3KooWERPzUhHau6o2XZRUi3tn7544rYiaHL418Nw5t8fYWP1F", + "/dns/collectives-westend-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWMAgVm1PnsLVfxoDLCbYv1DgnN6tjcRQbrq8xhbwo4whE", + "/dns/collectives-westend-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWMAgVm1PnsLVfxoDLCbYv1DgnN6tjcRQbrq8xhbwo4whE", "/dns/westend-collectives-boot-ng.dwellir.com/tcp/30340/p2p/12D3KooWPFM93jgm4pgxx8PM8WJKAJF49qia8jRB95uciUQwYh7m", "/dns/westend-collectives-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWPFM93jgm4pgxx8PM8WJKAJF49qia8jRB95uciUQwYh7m", "/dns/wch13.rotko.net/tcp/33593/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr", "/dns/wch13.rotko.net/tcp/34593/ws/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr", - "/dns/wch13.rotko.net/tcp/35593/wss/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr" + "/dns/wch13.rotko.net/tcp/35593/wss/p2p/12D3KooWPG85zhuSRoyptjLkFD4iJFistjiBmc15JgQ96B4fdXYr", + "/dns/collectives-westend.bootnodes.polkadotters.com/tcp/30529/p2p/12D3KooWAFkXNSBfyPduZVgfS7pj5NuVpbU8Ee5gHeF8wvos7Yqn", + "/dns/collectives-westend.bootnodes.polkadotters.com/tcp/30531/wss/p2p/12D3KooWAFkXNSBfyPduZVgfS7pj5NuVpbU8Ee5gHeF8wvos7Yqn" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/coretime-westend.json b/cumulus/parachains/chain-specs/coretime-westend.json new file mode 100644 index 0000000000000000000000000000000000000000..c79fd582348b0223cd4c1de71b94075751cfdeb2 --- /dev/null +++ b/cumulus/parachains/chain-specs/coretime-westend.json @@ -0,0 +1,71 @@ +{ + "name": "Westend Coretime", + "id": "coretime-westend", + "chainType": "Live", + "bootNodes": [ + "/dns/westend-coretime-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP93Dzk8T7GWxyWw9jhLcz8Pksokk3R9vL2eEH337bNkT", + "/dns/westend-coretime-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "ss58Format": 42, + "tokenDecimals": 12, + "tokenSymbol": "WND" + }, + "relay_chain": "westend", + "para_id": 1005, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xed030000", + "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", + "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x084acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28bc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c", + "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x00a0acb9030000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000008277f47279c4", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da998af9d0b9c163cc7caff71526e1e20bf4acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d834c58cefa69bb125bbc8bef14e90eabc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x66763d0040636f726574696d652d77657374656e64", + "0x3a63": "0x", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058544104aeba85d4125110284d291ddb4a3c50cb1bd0b174da063483a55a47260ab964ae54d7660813796247c0bec7d0f1b338b9c2280c6160568412ae63c3a7815a544782b1d1ea96f47ff926f8d0da2664efbdf7de5b4a99640afb1340115a114f7dbbfcdd2d91be3a3843eef26f979f4522f5a9bd54f4a9d7cb0d9bcee28fe9e3738e1937a9166f6707e9b5f46edf77febca5077e3ac8b3bb558280dee92c0ab13febf7e59ff5341265ce93916b5180787e58439561514395b06f6fd71165cdb70f35547f3b778f192b9116a00609dec11fd56143b1b7f3986840761ad9a7b3d7ed94523adba39a76fbec6eeaddce22ed7e7af7937c0d995e7d7a25abe653b2368cc9727e93533ee5800cc2c835da9e1f1baa6b28aed1f03c7bd850b307c7caa167672626a7fc39c76c4e0d40be609ccece1ec80d35793e3be8e286e267e7866a2fbb67af0d359d1d9c470b50238777f083339c773e8bed6087d48f26a03256a73e9d8a3f26c933680169fc348cbf9d5e6e18759086f3ce6fb19dc54a72c3caeea947358b3f6ac3a84775544775544f9101b36f67f1c7f4a88eead99c330c90994a7fea20076004d982911b568e4f7d3a8b3fa64f110809341a2dca4f073be4869ace5e1baa9d9dc5ae61d4a37adeb061d4798ce5d0536faf5e764f1d1c6a286e183bf5a2869a4f1da9a1a653076758d450fcd4599c1ed5515def93a57732fb265f43ba6fefbec9d790f6eaed95ac9a3fc9da307616854c6fb26c9f1ed551cd4c949cf2e71cb39e330910640e5a401a3e0de3e786b1b3f8a3368c3daa19090499831690864fc3f8b961ec2cfea80d638f6a4602b9416eb01ca7738d86f5ec2c823cd38c5c3be3e727eda9833cd38c5c3be3e727eda9832e76662a27eddb41be53cb38c5ce5f4eda97ecedfd5c3b13e8e74f6f71c8a43d25cb497b76904522e357a7f749fd4a52cdaca7ceb79c3d3bb38fb2fa4bc8f795a4723df51ecfe34bcefa92bd3d8af2e5a79779073b3843be53cb38c5ce5f4eda97ecedfd5c3b13e8e74f6f71c8a43d25cb497b76904522e357a7f749fd4a52cdaca7ceb79c3d3bb38fb2fa4bc8f795a4723d75bef33cbee4ac2fd9dba3285f7e7a9977b0831df29d6fc6e9f3992d609f22953f7516a9c6a72452c3a647755447755447355f6e183b3335393569cf396674f61d70ce19a773c3d839041983713a3b7b4f564c2cd4c8f33d9df1f4e7c9cf139ca73bb0219ea27872f3d4c4539d273a4f443ce1f0b4c3530e4f433c31f1f4e66989271d9ea078eaf234e609cb0909272d6055386501b3024685531930269cc880d581110183038be33405ac0898113029606d6039c06e80e1009b02e6c6490998164e79602cc0528089719a03530186020c4d530db025605f602bc0c2c080683ac1a9052726602748e00706c6c90c9816a7349c02358dd1a48593144e6a38ade1c486531b4e4fc0cc38dd716ac26909a73a4e4738e971f2e324c7c909273a4e28387d71c2d3c4a7890b98104e6838d9718ac2e90427289ca23851717ac1698c130e4e679cc838617162c12907273330139cca38c9e034c5e906a738b019607a9ac86832a3098ca62c9ad468c2d31482a63e4d5d3479d1f4a7e98ba6329ab068b2a1e94b93094d5d9cb434a1d02443530a4d58b01a9cae9ac0346535a191a00b09c468b22241194d5b48f0c7298b0461345d493006cc8b045f34016a4aa3098d26319afc4840869394a62a4d3134e5699aa2e98e930d4d503449d1a4a7694b1315303c4e4338c17112a2c90aa73730289aae34618145e1c4a7898a939ba6354d55c0bc70a23915e164048c0b5809b02d4d7b605d38d560779c8870fac1a98fd316b03c303f4d6a9ace38e9c00488290d263098ca601a83890ca62f98b660f282a90b262e98ee30e5619282c908a6384c3a30e5c0e4860907261b98da303dc174039317262c26354c6498ba3095616a81a904261898d630b9c0f4021316262e4c26305d3189b95f7c0f530a4c3130c970694c3530cdc0748509858bc245e2cab94c5c3a77893be7d6ee1057881bc405e2fe707db83d5c1eee0e57879bc33de21a718bb871ae9d0bc5dd73adb84ddc3a97880be7beb94adc2a2e1557cf9de24a71f35c3cf7ce8de2267167b8692e0bf7858be6ba70cfdc3297cc6de18eb95a2e1707744fb85b6e949be576b9265c2cf7ca03f9184e866739155e854fe17a5c0aaf791e27c2e3f81b5fc2e1b811fe83fbe03d4c1d669c69c66370357ec665f02b7ee5519cb614c6929ea526969c587a620988a51f96725862b394c29218df614985a51296b82c6d59d2a294a5b486d2194a642899a154c65214253f4a5f2881a1d485d21e253d4a6094c228b1a02446898c920b4a65945a505a41898bd2094a584a5e944c58f242c98a5215a5ab243594aebcac78a9f14ae3c5c64b8cd711af35906078055a1ae365c66b8cd717af3290c8bceebca27849f182e285e745c64bce0b89579c5713af33c0aca23cad34dc9ea324849e38a223d4c411124b593c09f184c7688a9215444d10915164c5119a2335de134a583c3d71b405c626e47274550446384611202317261146568caa14f131ca62a4c5088bd116a32ba3128cb81c19717404d195a23ddc15474514e1196243488c100c457d8ab2f02a447f96b03a1a96bc049109a2419c23d221aa82e807222188862082822808223b444010d588b8200a811702a52e2d322d168c6430c2c1ebc304c6d3e2ca706330aac1dab168381a426588b08868445e8e6a445b88ae86b284c21071212ac18333d4c6119c232288a61061116241e805a230b388a01c826a1042230485d00f426b5a68847c10c20aba21888720218286084a130443109a201b82ce04ed10e4264886221d8a70081293b446919b2124b818382a5c6f966018925314c6e6d9a6e8d67c736e6cb991c5c5e8d204e9b004a66313c4c2b7449118b72248ca4867cc1a9918db18e78c4a8c498c7ec6268eb25eb451cf9865d47264e568ca1195a32a631ea32b4654462ce3d558c518c518659432d2c629aeacb1ca48659ce29a19a93052234425c887a21b9ebc28c2228c4224056c83cb82284b51152d354266845c408078159211aa110212af846d885dc412442e2216518bb845cc12061269e119e294300d314a58862845b412b2215209d708a1089f08ed844e84688459a1192296582544214c21fce242c29584501dd71ca41590c020a9809402120a485f904e403201c90b12165217a41290b8206d41d2829405090bd215d215242b485590a8204d11b212d406d1164c663a1b9a0271575a7f5a675a6b5a34b46a50a2c20886a440621df72332114407c795d315477598d2f819e2124251969208ca0aa708d9790aa26b012683530f620c4e622c4dc1c404531da624c23c4c48846088b48454c22a445688aa10f92024035197d04a2885a88790361448088c500b476f846268cd20b442911e2132477184c6bc9e08af6041845796c068d9b0e4e70617ee4c510f37b418d960a31877187910aac2c9f049b1f40248c35219904d50202129a30edd0ca21ba1294d6138ed6064c6bf30ca6189cca7c607e83b23a807a23e45360495099a41c8093050eb05212e2e2236295a654638de0c482d787196f87844e0c0b224c6cb09a336dc9dcd088fcc9398a22994c6006b00db80362cb910c4068705d319a53698c460f2b334e5d3a3848612a0a0183822884c98703a3845528851885088764427c427b819bc2fba1b96c61051e1b4604283e373a914515174055194a52f96bae0d0883d0889c16486d0198e0db7a669ceb5b259e132420805a21d8878f0deb46410fa227442d4b6c5b667fbb389b1f1692db15de1416c58089920a482cbd9b4104a61cb626271218cd62cb9712c82d6b4ae08d1827e0852639406a94c100e4c5a2899095ac18f083242298ca0165c6d2c140e84eb082616967600cf00e5b4c00455b164825318dd1c5896560b5bcd2886a6283a3a5e98ed8d111aa333e0114b347862c612bc3162134a3034d9615a2108081007223a4c6896cec0b06c15d78b1804131bbf41fc42498cae8d77c5e362890c3b05ac07ae886d08d00ca72eac1437be386161a443b744501bd70f412ec0e6785bb87c087aa1b5c56be382e35d8d3d34b5e134466b05cf8a088493174d337834387d0173628985221496ca80fd3004674909d18f52154d59b63b4e3e38f1e03934d1b958ad314c2a0c1161e4c66d68fa21c88c111babc76806a52bdc4aa7c67130ba012ca3c8078fcf520bcec3a7048c872636416cbe2444219ab8040561f120a971fa033be304042c86262d5e1738b27435048df1fc783534a5697202b6a6e88b110d4c67c0d4785e582aec9d5bc57570374a250451318f984a4c246612730b14d9942381a9094c1b924c34217cc003554a38108139c3dce1010ee02001441ab0010316a08020407e680000d8a4338dd031810f02ec78a260039d18e07c9e4170bca6524e9068c0880848a4423062049cf4832552468c7c3b76bcc6bc5e621884241b58c244aa0a8954130f9c247122b5442a08d32b8c0746986c608910254a9820a0ca0329254a98208082d70a3c5e60942841e28112254810a0420855202c915292024b496dc008932a235521809044aa034f2f14d889922448aaa43e302225b501a7d71766b28125498c78a089d408541e40c281bf4e60290f963059620489912422bc94074b9834a06ac9084c3cf0e0935e267cc0c403a90f965e5ed88907a4a68c3871e201a9214b982471e2440a02f785c51d4822c237919a6ac2640244eeab0b1b911a416a040714f9400a04109014a1e055023b99f2402a040fb8b093aa25524da4425892c45f5b9624d9c0eba5853d48e26449922655244819f1801211444062a489d4074a928c109bf280899218d22b0b3b397a61612446905e57cc012348aa907c80a44a6a83a0d7950f245182244955922549885e5646f0408a0429a157156ec20401e18b0a2f6122c208463e509264040d185982c403234944f8aa25521c00410a021f487d3082120f9624591201244d4678a7d714762295a42a49132621548d80e4c64b0a2f6192a40a4993113e3052c544891323494478244d4678a2560f3ce50113251a30225585e483244a903c69f19452a20124554b46d880912a0fa49a24f960491223468c6e9787db93841a3bcf33906b34dacf1935daba31ba31a60893ab2f8ee338ce5faf66aeb6d9b6d74d6cd9b6e759ef76b3b5b5d26a69b7672ba594d6ba44b9569e9e67adc7dc2d669e3cd9329dddade6f6badbf6b4cc936d579eec596bbb5a4aab6d6bd9f6646bd9b265ca5c2957a9b55dab999976dcb6d932b36506bbeea363d7792dcab69d687bb57ad54eae75a3bd7db5daadebdaccd672edd8b2b595bbbba910b6ddad00b66c39cbb6369d6d2dc8cd69ad05396b9939e656c7d6522b766deb51d7b5abd8cc4df986ad5cd74aada5dc9452fa715ba6b4d66eca93a765b66c6dad96d9d619a42e55ae4d992d65e6ae636bed9cb36933536edacd6c9932dba6b59b76b4e575f41bbb6ddb6aedba6da51fed5a2bb3e568a5cc5c7976f735c9dd3d5ae6aee35a29e5aab5b67274d25927b5b5da5a69edbeb0295769d7516e6bbbaf56dbd5d64a9bda6eb6d572b4a394d63a9b562bd6a6b569d30a93a0bdee6e8ea3d4923870e0c0318e5ddbd66a2dedacadd6327365ae73f6e4b6d572d759bb76edb6d6325b6666ae67dbb6b6d666daddd556e6e6e65abbcad56abbc56e55cbcd952bb7654be9f731775dd775b5a3b4d5a2ad16a5fd7ddd5dadb5d2ae56da75dd59f2e356ab7e638bdd5cedf6686d6eea6adaf1b4d64eaf6366facd8e765cd71cc7d1da51ae9572edbaeb2aad1d534adb7237c4d3560f88e7791c672db7c8956b57b641ccd66b668ea3b59963a6cc1cd390b6ba9b2b8b2df2edda97d9dace72edee4a9927a5fcb99832739dddd336cf8e67d755dab5ba47db2d57fd51bd24e6cacdec2580f6a4cc744eee9abbcea3ad27da35ad5cd7ee2a1ddb7250100e4aabb547a2c8f3c68d1b372c775dc7dce2d608a81d830d35edae5dbbae2cd4ccfcddedd47577d35a9b6a9db36bcfd99f2b8afbbaaeeb68eddad559c5da1f336dd767958e9879daca2c8aac8104b567655aab57bd56aab5bdbb52ca89dd4dc55a99593c129794447a80393434bb6eb679ab957bd6aeb3fb6b6b2973b7ede6b176f7e4dab5b97eb3526b5d963f97ebfbe6ecb6dd5fb7adb5dad9b9babfb69c672767bb9bc5b6cc3c6480dadd3dbbbb999916a0766f6cbbd2e68d85b8999bc10c6a57aefd357794bbb9bb6b37a78f6a6464645439cbd9e69edd5d6ba55dbb9b52da5df775b4eba825802deaba5a6bb596eb38dbae0130f52872d12228a6223ae7b41cc7b96cb5185c600590c3e6985c9ddc7459aeb3b655abb575ceca6c6d73d6b6a5ad00749576d552ae56cb6c375b29fd68a594ed275adbb5abe57a6b5a9999adb5fd64c5d315f406b7079a30e180140736902d907a22a5c408094838b034c41680c0448911109624010104264a8c04a0444992291e1638f18055008b4047044e009860078fa00f942409c10323554a4640b201a90f9e9c04a90f962431b2240992103cd8c006989001c8a1a30a4913a9255220349120e40048397122d544040e30f1c04813a9274c3c906a620489930f3c903262c11414060110c10329273d923690a489d4132304b000030c02f081941126552278a089073928f840aa89d4074d981881c2200052499a544d3039c06c8154930d1869c2a40a89074c9428418274015412094c3a50a5048541009c280016180980d41412251a3002c2922421384112824b712049d592119a904203d840d508494224488560a46a4992262218f1c049120e344152e5c429c889d10d0e0013264d8ea63c40a201254a983499600224524d8e3c203565a48a04a9254a48555220181139a97292840429a48d2d58c224499507489c2c41d2a4ca4855120e7080092c020e0009493620d5440452a8091229251ebc9e2c91a2416c8191254c92386922f5c4c8074ba44410018911291090346152e5c448150952484f96485158a8453367653d6a359b714766f3b3b29acdba236c6565357956a97864365d4766d3caaa522bab7a6436adacda8a1e994dab59f711ab39b3b2a247d8b39af511b69acda86c568f3038b3d5caca6a668ff06c6665359b6d4778666535adacfa08cf66df91d9b49af48895d53cc256563deb8ecce68c3bc25656b3593d6235f908cfb623b339b34766938fcce66cd6f408cf66b3593dc2b3591f99cdd93cc2b3796401b5cf64e6c901274838e5ec67e79b48e4f372fed4f4affbda67f9797b15876ce20faa2912a13ec94dfc41f5633ae820174e11c8dc1c9ca27b2e7ad7b1d3b36dadb54e3daf80b885e09d0fb218e4732317bdb879ea9fbf442f669e7a17861e76de1110516c857326b95c2f251732be9492c4d946149344f126b5ac98e45e52cb75e3f54a6a3baf97507d390e7fb9c670cea1250a68e9f5ba62107d891e818b0e8af7b5a6a15e6e1d1c6f4c1df9e73cfe8608e4c83fb7e210ea9f834261779884248a4d84844a1e8e2ec4e59d874aa187235952b9c49b445259fdcbe72dc3f0ce477abd3caa93e6b7f8e3e55424d2bf39d2e50fdf822fb72fd18b5c74b0fe9c2290e99d47d16974cbf947a21f5d23a3cb5f14c1b5601a661dc7a56534ccba77c57b4bd907dd52f642b794b56e29a38028a0b76ec13454d7693b6f9d96d1509e5b07b7f0bbfca0f8912f21d4a7bc3ae72c2a605bd350a25b074551f416ddefd5a5c0ebb93604192f71bcd8c1e28365072bce78e5310fdd3f27651ebe15458f6a500cc521f33bcf5bee79cb37201aaae5d6c1d6b7becfe563183611f27da077fe7d9f77640986d755da31dfe51f5badeff2bb5a2d4f1c02137dba48ce350db3ee7e3f12a058b9934e9234f77befe5d8ecbeeb97cde6ddadea9f2e12b1e20fae69d9f255fddfd34f7203a261d65bdcc0fb8f2c3bd00798932c65778a3f42b722e1a7fb9d6ec56332772b190c5ffcd062d769548f175f683127a958f1987b479615f48d6abe775eda3193ceebf977a983b724e13d677103d87b42bea71ed5dfa5a51df3d63720ba4ec3acdb327c3bdbbcc580f5ae33dbd8e9e6edd05b1fddf9adcf356e3dec03b8db2a16b9ee4ec522ee32d29dbc6ec9a9adf69cc38ace5eedd4455954849465cd53af6190d97355d52959560929654d9e74be5555dfbc4522b02fadde9624502d7de72c12b15d8121a43cad800d9dde7a5457f107259f742477ad53d0c5b67a54db5bb657f5bf86cc6f17e2f5522dfd74d0fa764bd96fd6b7926e96fd5271088cfd89fde92c12f9683fb949fb64e0e8b926449f07990266be325ffdc8d9bb9901f7e5a91fddb2ffe5cc3e78bccb8f9eb2c697f3f872fe78795433fbb0e0fb296bdc9cc797fd9d4f71c8cbcb7e717e3bc82210023cf38e6ed6bedd12c8ece776673fefcb5db73cf2e60a206de02e47424242a2484a480ed21009c9a37a1c9594941c9ce1789594bcc51f4a2e57abd5727086aedbea4285022d079902498e2324ea3cc84676e4ece7ed6e397dbae82eea87dbec96f9b59ba0f8a3e5aecb3f5e7e3082708ad38bc02936c0fae62c4e5201d6c12e904df2a4a4a4249bb4e44b499e94b4949484e42d476ab5c069e6e896f35f4e95bce54a258f47f2f2e5ed9b0884fa6b3e92b78c8e40b045fdb45a60ab7593ba45be8644e0491e8127959d74f4ba01b6c0248bd54a4a0293922e18267912f91a52e4a01739e820d26d521fc95f48439b1b97b3b8c1fced6391c89391bbdcc85d0eba2ee848b7fb5926246a799293d9b7383cf3935a975ffc010e41bafcd3bdd73df2287a74f98d2e67a761d58beed6a661d523b876cb2d65ffdd52f6e5e646c638dce538c80e50c3aabbc859a66195e86260a45f7278383c5fbd0334743130f6979b9bcdcd57e7ec3494dd72e362609c5f5aacafbeb56928975707699f860a0afa3e178738fdd7a7a13eaf6077cbf9bcc51f9f779c869a5e1d1cc7717416a37af4280dcb69e647a7e28fd1e7e50fbafc23d9711a561df4bce979f5ae83b3dfe68d6a70c24a28e3773cd74a88d3c5cff778ae757183e5e6a19e6b5d0295e0a60430255c7dd2738d4b9fe782e7919e6b5c7e785028e41c64100c59ecc852f69dcf5b8e3ffdde2b8fdd705e5276ddea8b7163d0750e82e1bd1c9bb7fad64dcfc130f43c055870bbafe9fcb37f0de97cf3201d59523f5f7d96932c653f7df653fc313d8adeaafee99c48e409749783ee22ed9686815e1de4c4214e0f8a0e76384521d33b9fa290cda7e87323b96193acea0fbdec02893f5a0eb2e8224bab77f976cbee379fb7ec7eba789b0811bd7321422e926513219d07b990d13b17c9727321921b26bac80d15449654f345efc8921bb6f9e8ba214965b55df1f2b72ed57c1074f1f28720e8e210d883e46bc8e79e7f24edd3b0ea5ed905faeadf9886f2c859e6a88371f30e3ae8609c3fc5397f75ee0803e354e7fc25f5d3728e30304eaf1d39b93bc2f855e36fe4992b8fc9da4d99255ca0c5ac6a9f3761e0c4a07840368b31f9151e312b3c626ca6db2d0d45fd34d4fc31cf5fd23fdf72be7acfc941fbe468397d801ce534f3d5271ab2e390d34c1f80f4ea474f59e304f3fc65f7d56b43bd363f73e5b1ebfcdac822e3f5fef9d5b90f70bdfabc45b382e9f61eb18ee956dc402c42fa752a16f1eb2d3a806339c8913e4d9fa2033846921d7d9acea203387649a64fd3bf3a4bea0aaecc320de5392527a032ce325f2b4029603f07c1197ed786adcfb6875f6f9b779d7f5f4704f65f77a77f37f4ed72dbe6dfb5e4f639187a674520a27f4e6fe7e1053d8a76203b78f945ebecad5bceaafeae4037ddf3497acee28fcfe7ed3e2e8a527e90410ee715ddbbaf21d43da79e83f472b33a1589c09e92a575f0cce8514daf7590c5902c651f7ee3bdf2d8e8dc0718499995cbb931f85cc68d01f5d1a9579108eca7fbbca087b77dbc1c735d36a183f38a64597f7a78bbef7a2290cda7d3fb39783b8fa25fe7d5a3a8c7dfb7aa7fbedd6e59d55324f2f4d62d39cb3c658db3ccf3539f659e7358f52ceced0ad650317413aa86cfd37e4a5decafe7da9632ada5cf338d7bae6999f3ccb50a841f5960ece79a1637cf3e82cd79dfb272f7ca635cc871a48c73ab0aeec0a1c5388e9faaab00ac4c59d54f87383df5339d47755f2b049e873d957df5beddbdf2581776571edb7c23cb7a808e94756ec53937065d037d75b0c3beecdde5188b44e8b757928a666565653d77b93128b94666ce57f5976928a6449e9e92b30c8b3f9aa4e2ac7a667b0303e406b94190c3193d02390323d7b28c79fb5cc302c47310489d994aaef3ed20bd577c2a59cebe3abd200de99719368f71000d558ecf8e34b1b8a1e28f79c2384b1e3c06d03d9c46a3d162ed51ddc363ed0a68a8eedb19c03ea80b09cb7c1d0df5622bca23cf2210d6f3ec200d6b991a5a2c5736dcc41f81b8702b40f7e0d89fcd5aee1c2d7617fb60927907f5e63a4f184b1e513d003bdfce03cfb75fd050737e01be9deb583e3ccc4a764c32ed0038646c980b9c4546161730fb5ac93913d003530418a16898db3bf4161930fb2926a9ac9ebd6c9f4c0a71c5c373ed0a4cbde2e76905c1c9220366cf0d2bebcf7e8254fc613bdcacdf1a64760df502327ff38dec7ebabdf399a99253639b0187cca9792798322703610bc6f0eb83aef626a957bbb537d40b483b75bab9f529ba79d750fd5bf8511dd5b35964045d579501b349760dab5e1d7409a1b2facaf32ba901909b524abdbd564a2b7529901b8a6b57e43c75f6f082dce034616c3dd7ace8f9e9dc02e2ab6f3d380e8d1663a73de8eca08b7dcab383cc3e5a0e82b3d05b9779474896e00faafe202059f6833e3dbca5d587f35ba513ab07bdfae7cc3e3c6ff18e1659824e5d74ef32ef10c93274ca3f2f15979595d5c6570f6f390332bf7a548397ffbb53957c327f7321fd9b537148f526cbd9b7f3833e6f397f3a15816cde72f0be84fc148384cebc637a148deaf052cdacefbc755f6210ce3db29ceff977cbf99f83db2de76f2e7a1589540f3daa41a6c0c8352b73befae620831db2184474902ceb83b49f74dff216394521f5370741165b6439fb9687f749f7a183b79c0f3ab38f902c4357e2e39977800e7a14052f757086e0a5ce628ba4a23fc51f9b8bb7dc8654dfe2d0a43cf839fb1483ccac0f49e61d254852cdac07dd3a8b0ae00749661d9ed3eff27b773a8d7758e76ed93ea37ae6cd4d0ae8b4471597bb33e75570f8e63bdaab1879cb0140bd0a96e9455e8133ed51c5889409393df22a4b0e3a7b477b54e15125023772f622daa30a0e5286c35d2e370a72d7751c2ec341ca8298dcc865383c749757a1a4aceb7c2465a373d4659494752496e9b2ce953cc845671f698f2a1f29fbbcdb412352463a97c39dbc39377259bb1129b3d64752f6f9e8322307bd4af523523634e442a48cba47ca3cbfa0cbae83a48c529781fef21be3e82129b3d63f52464959927f3e521f5d16fa48caec0d97597f72095c36ba04a46c68c889481991739c2b91b2212527e2885c0297712e012953f2a892cb3c26120bec0629bbe14444feba4144447a8eef73a5a4ef033fef1219e108a223d7de8783762091755d1b766714e4eaee72231d3f8e08b4210e231cdd7505d9cff32838724441e188c30b8d82b8eefa735d4b442d9111ed9c2618c1765dd0290892e07372ba610452efe9c965ed4f4f1e0e1ca4c3c6ce61e10413b8ecf30926b81ce772d29fc2cf9f2c0ce6b2eb302758e7221a9df4098c827c02a7c065389c82a7279781fe24c19353e032cf3a3981d74987592287b59393d3042ee37c0252e6e414449d020924487259e8499e93fea4e449397050e04f2e33f22718acc925206530cf21813fb9ecf31c2e1b4926bf71a38d9c74a7a424cfa1a4e42f4fcae1492e0bf2a4ce9d5c87cb5cae63f240720a5ce6720a2693e7a0c075b80c87e77059e74f302424d2919e1cc965d791a6cb404772095e2fcfa144ca92485992e7f09792bf5c16fa6bbaccba040e73d9e8b0d9e4e432ce9d4859939393a4acc995941ce632eaa4cb3cd741ca2698c09948d98b94bdfcc60d4722654747fe2265487ee386bf5c46e42f5276f4f223978564143f4d67720a7284429e44ca8e48d9912779183ae9b2cf49522694c3499781a408fc345dc82970d94792c04fd3292065139058e613e81290b2eff3215236e4a0040ebacc233b0049d9073a1229fb3cf43c74d92539e0a7e9482e232215c04fd361a4cc89c89b481949ca4897e03a1329cbe14e9232ee824a1a73b25820c384abd86de2ca1726d408234c95266255ae4719911d3f4dbfa4cc49c741323f4d5f22993eb12b25193912297b15f911299bfd80831775f0c89086ce0f31a399182952788193270d3c6462462e0b228bf869ba11292b22993e554096b06797b9c8919fa6474076f4891d87cb2819023f4dc741ca88482c335625c8874899cb6f905878c4aa50172265412d7791b2590c67d8381303a032be102346798a3f38c0e0450c203869c4aa5077b96c2407c04fd35da4ac45327d0200590ee0d9651cc9839fa68b64117d62973579849fa687a40c24b1c0625536ff4859017600028c1aca78e28f9a984762f15895a53767e430841456d4f02256a59d735947c2f8693a47ca3692e9d30eb2e4f1ec324b3a3f4dafe4489fd8d967d4eaa977648153871f6ad82c6ad8f47eae9dd9e187a8ea23f18e4972c3a847358bf3ce20fc5433ebd959147947df9077b4d3dbe6cbfa3dc35b8d5cab52e7d9ed73ad8a992f3beaed22159f6b556a6080d52b9fa9f3430d63b236ac9d45aa99f5f676bca37df6d4f996f3d9a794b17e7b1845a3ba631fd6db3df6c1df6e2f734161c3b86165d14f9fcf353354fc50c3a6d73b9f2f378cbdce59abf22510086353a7d429a5739e524a29136d42058d37547b1d1baa74262a58df1e3614fdf632fcd23b5686cf234c29f35c9352c697aea74ec1734d8a9c075dd46b43cdbe6c9f3e874c4df235a4fef4fad349a0d168b4ea4d96edd559145292f0d52b4935359dbfe47e3a9d54407e660267197ecf09cedb35d147399fba00c62094ecc00ee7ad1fb28f26f3cf5371ff44c805d2705efae2932ddcf34fc216fb6832694fa5c14f6f679148a5d1a62864a4fdf450c8a4fdf49e8d07e4ef1aaa3c9a80cad83ebda4ce0dab606ca7cece2291f6294a760d638fead99c0998aeaeef0454c6e9ed94cee9e00ce924bb9ef5a40299a91cb96165d74f6cc1b8f97c900330820c82916b527878ae49c1fad2f5ed0278ae45d1f32dfe28c7e95ca3614175de5e1b6af3f60e6cf147b74d9101b3df1c9ce17c708a9b83dbad9a543f6ac3aa0b197ff3292a807f73167f70c3caf0a725cbd9dbaef328daddb27e59bd8a3ee72cfeb01ed520c822254bd953ef6ee9dff9764b6e1809df79c90d2b45f8cecbcdb9616508df7914a59723a96401734eef6a7b37e7f4aea968374be0eaeafbf2beafeaf52a208af3097638390e08d5cc6a52158a20e4c8771d731c1aad73b03af5c82654fc8746fb2aa1ef9cd2aaf03b22227cb7099506dfbca37ad5f69f974d3eafda861879cf97a834f8c93b6af548661dd54520d637b2de8ebd49253bae0c6a1db477c68981d44170f6dd2cdb3b7a69734e9ba492b1d3ae54f4e7e49c0a8973b06b58e739774bae738fe3486e58479655fd54d3737a4bea4fc657526951beec9c8a0b1425eb9f74df4d556f079d23bb1b725e761dc979d9395547454a946ff264fbe94fb877327bcea3287737ef2e3fdf928afbf3d6378feaaeaabfbaf712326bdfbeddc9f9e67d9f74e7fdd63b671108554927779d639f776a92dca5dd91e57476ce85d02f9dcc1e087f25a72839c5deecd3e7f605c8dd903fbb3314588ed3856cbef9e6b1b3e7ecf9e7ccdf1dbd27f3b76d92ce3b189c21177ef3c1cf79bce7736e0e7e1e72dc90cd41d2e5f9146797ce99f4c8a186f1e69e7f6439fb6fbae7517482e0e775dd4ff72eb7b18e765b594703a93f492ad953f72e6dd6d15e2e6036c44fd6d1be80d910cfaca3bd8fbd3b4b18a90b19c3a6aaa5f5e9dd7d4d926a3e4752f0e00cbbbbe3d9b7e99b10585ac629eb4e5f5a7e7e29f6d184c79f619ff1163e9d99dc482a5676c436bfba11dbd250435f5dc95be7f1e5d053d97c5e8de5dca2c55797b18ff4c98c313b90d9a1cd1e1a2db6f904375a5553020c525e082246c5ca8ed8f4ab1b31762a563ac6f9d50162d3b77b7580988c4d91e9320ee853f55947c6a608e71b298ba24fd53952c6a6c8e693946d240fa64fb5daf492ebf019579038f3851469ae7022c6319ea1302788165a7883e70632621c632f7914711a2d36b73494d357aceeea0238b3d0271c33f099c188f7510af9aad76af5d4279fafa42db7ea1cb96423cbcd95501bfebc25977849c952b639bd74b07eb35e5a7d399d5a57321bf3dc12a683f5d6cb0ffcc6914b98cfdb6d8901a8d4de9275d00851c11d386449c2539f5e2b7046c66ee5cacaca1a13f3ced092b8c4f0a54c1309cad42087562502f3f933b40aeec0a13d3100951a0934a68315632f3ff0560af38979248df9c4d84bd95bb29c4eb982926b67daf8aaf9676838aab0610a17345a8c4919bbd592165cc81325108d16eb485a597d96223c2d1274451532b44185468b6d70e39b8649e33a1c1469a8768ef57c757032c174f0f8ea30ee96ee4cdce5ea1dcc0aaa176958750e38a03d9ceb5c80ebf89131a8f4a74d5be5ace33e702404114898344b0668edd8509db33bfbe08454d03a8b41ac57b7b37f4cf77cf3167f0c5d5bef93f9e57c32bf56079b27776dd9cfa7a653a75ef515dcee4b08f379d0e937f8025914c2f57990e45941d96fb7d94c5b3c2bb04eeb55127ebdaf21746a73eafcfc6343f17876eff278ff36b7777a19a717358c7d68f3aff32952758fc43bd8bb3b368c9dbbce3b9864b256b20c6b20d03a3395dd1f2b63d9d56a0dc579f52e4e43b557efe43454e7d5cb6ece57075f30f3123cd75c08f4205dc3dafbc158a977dec5a935ac7ad931c07a4776722600ba1aedd161f553f5dad58c8cd6bb5a87d5b0ea1a182df912d234da53b7a2d17e8a2c89fe49ad834584d9a8bd2595587f2980df766ebfeac5183333e0af9df8837a3f48434035e46ed9617dedb06606fdd5bb2f667cf50e4c43d1af0e4eda93b6fabedacf5c6910f37cbebdf3b9fb6a7f59e75a96352f4b4280639e73cd85329e6b2e90f142e85ba76f49ab2ed887f5ea5d1dda03f4ea1d1eda63c6aa777b688f9657effacc0c3aaf0e6efeb2de8139df0125be755fd667ac63e07d59d2019d5b9931d07dc6ece520f4ee961d58e237af0fd29e4c6bab9cfd8b83cf5f9cd7a7ff7280e82f8ee44024a7701620cfebf7cf8f6afae19d326320f9da482a57d69fb9ba20e6f91457561b312b33e63995ee23a770595959fd2c22993e8398dd9fe71c56dbcc366705381d9cf367736200b24f29b4d66b7d4ecedacda973d65a70bb483388da3558af3fa866d657df48a45aeffcdafd0438bd631f938441cd0f358c7ac73ea83310b3f28e26b9611d9dd306ca0e5a3a69e77473ba39f501741b75ba39a5d42d52c3b6f6a1dacb76f3b9f9abfafcfe57ed6173cb91480ddbc8daf10e9e5f6f3b01b657f6d1aee9957d4c926a66b5e51d5c79c79c02b2f5ca3e3667b7ec635ed030ebe67934cc3a8bb40c656188d43d3eb75e00f6e1bca373eb3a1a6a6c58e7d607f0d43953f7e8faa92b5f61f718eaa7cead33d69717bcf517fb986f41ef2235ac736a43547be7d46508f4dd143b7f712f8e73ae9d59f35c3b53c30f35acf3edcef748a48675dcb5bc8377f07c9e33db940dc836683a90efa73f8b3f9a32ed9e44c0cdc16d8a3f3eee8a6e1d042f90d997a17f9c33e7ece12db9e7da19345f798708e4b68b9773efce07c1ce411af2ed5ce49f1d5976dedd66b274b0c3f07eef1f4841d1992c3b2a12a8488942fb266148523999bde76507d66023eb9d701d345fb2bc478664c9208336ec6e835ec12dec6e3b8b4186886449c283ce21c96e45205fe81fffd4e7cc2495ec4bfa9fb328a47b909ceabccb6e487be779148dea1938eb0f208330562f8bde7a4359bfa0a1fcad179da1bd75247b79bc2d7aebe390b5ce352a603932df00d90eb90238d053493bd5ad73d69fb30ec2b8f90038a8b21b8f3414e70568287fce3b6828aec980e639bf4043d1e718cf737700cf3937d4fd92e770c3ac473512d897fb33c178aebfc84f4fa6adb1a1a8733ed450d539ce3967268e9ce23f144d9a38df43d8a194723b7524f6c14f3d897d00a9d5867140a50a942aaad4c1aa7244157c624c9faa7c13379c30a74f1a68bc89317d6ae70e0a1b5649186c7af74c7b703f95e37b3fd762a0f2b52b47ebb59dc71ccdecf9f621580168ac0cff86cc056d64d8304b86cf353380be92430d9b5c7bce31b3b30181136be4860595e3d3ae84e918ed3cf521ea5d680170b619bb86e7da0f5ebec93cd77818e361cf351e5e105235e5cf973320b62f47f062c4b3bb669b91c279aeb9d9f333ce730d8731afe3b976439c2906f996d8f354479e3dc8f7a785a7b26a7d598545154564beac424306c4e2cb2a2d58d82f9de4c981021fea7ce984873acfded16895467bce7a76f973a300e8ed1daaf9d5c17a6d25cb39c521d49b2cdf8cf95a7dbebdf3be86d83b5fdddef9d2c9ecabdb4bc5595959a01df341f4f9fbf7c10eedb577beefd24f31887f7bbd4f882659d5feed43c0bee50c32b3a8a5e44bc874ea41a653b20afc1a98afeab764d9fe643ef5d9d3fbf4deef2af0d96776361aa08f497bb0c38f7df8f89e9d39039e155477b10f66a3c7369fa657b2ecbedca28430c33baa8bf5398328215ee00af82b5936595afd937e109acc9fee0d7d2318865c83c1cf53b9fe49b7cd0cf869d95f6e4f7d8a4dc6e9e014a9c6094e3a1ce70755a5d1784fe5c1ead9f8f0cc60f6d1f3ec3d7b708cfd9b1954da3310d6c3755dd76ddd7574765d6761ade99b9da539208cec4c0d555630d45025d34f57fae9a119343f7d6ca8f99399e606ade79a992be0730d86403f840432ed71a4478a33ce1083258d9a184761478d12f62891060630b1e9946bc366756f58ddc8d27ff38e6d506d185956d19feef95043811cc87d1f5972dce7d441eece07bfcb0d9b6290a4af0df39caba2ff39389b276df183b086d25122fd9c64b9b975ea209d73bbfcdeed34a8b24fc26fceb7ec9e3bb64155646a1875cebd611cd95e6f399f6a6eb78a4ef10793961cc3a186e2a7ee8fd450fed475b00ffad499fe9bd60c730f4a9956e7fba43e7bbd42c667a7949def3c8f2f39ebcb49a3e349c6e9652dc7b18381cfb7874349a29a3613902f0e6318c3d5b70f1521cd0ce67c334e9fcf8d013bb740662a79cf5767f6c13e6677b48747adb51664b0651fa5937df4b3835de710989d7b0e5a07ed15f27177bc1d7daa6eefd7fd68ef728cbb42c0a73e7e5367431dfcd16439ab218bd6f6d6dd6d6de85d286cac5b75ee6d937dd0670741ea9e832f21e58ccc5bdf66784b36f1df7cf61be95da74fd5bf5b449feae604029f7b2414cf3f8f00c73c928df52a3ee08a06b18f84d1a7ea3ce853f54e2442dd92250713d0468e757e458398e79e97b01f1deb5bce860809fa910d9ddc93b6ca087cc3f0e54bae7df50e1aaa2c80021aaa643c17682828aebdf0e7ab2780e77c651ffcec53ccf1a555011a8a7a75de53bb6bb3e7c9e79a0b40bc0b6b5cb8fa3280acdffadc50d54bd85b6742f27e16eb543bf572f472f3f6269b92536315e32ddfa9f6ea54dca0e9bbf0e7cbf1ed5089f4969ca2de5efa5beb61d19cd46706fd74031ecfbc63523620adce6ebe3768a765cc7c3b2d13e7dba9ab86f496ece373cfabc800ebd5ab48a49d7abd95c77fd4bfe9168036b42211fa390d3da4fe7d4ec13bfd2ba94fdf4409740ea523a174be39e7e0165271483b75b00ba72881ae025666ac732856666cdb1ccae69c4775079d43e93ae89cab93ae007e79eab24d2c43e5a953eb9f6f413ec920dfee94ce37b7d7ea8b5967f6c1f9465a7db1cd4b91888b39b8f2dcda3b63de6543a581cb83f1732b9cb3019df6c5ac5777393f9932ee92ef410faf90afe55504325d28f4f24e29013d74d1697897802e7a149d975fe896b30f9aee72f97843679101937491238c5f561a6d7acbc1fb02453240fc7daeb5b086cc9a677aae91b96a01900dc1fb6a89a2287221785f60ebbe4051f4a8b622270e119d8c9cafea21df0338b691533a52368b7da415ce67cc9eb9e24212f4a8b7a46cb3ce791b6965ad6f2eb3ea7c23af3cb6f995c73cd2aa23af3c66492b571ac4aacc62d6ad731b05c9a908ccacbfc06a3576b291044bbe40b7d6e7245fa0256765bec51fd4a7b524d3a7ce6aebce0a904918b99625d0772d063dcfdf851980cc54f268a8da50ed72ab0e62a30444b71aea22d18b8a6eb8b8398b1d885e7443073bacd373f60e64506264918e8d35e486e22f02e5c8438732fa91b358e4c84352d6f2d13a8b1d8c2c047aea2cf8b162911b1ee4e115bd75a1dcf0965bb102462efa768bdcf02052d6722267f6e1a00d7dc9652d231749d90d0f5b976342174ae846be74a118f90d27ba5064a1df70ee96b3e7e18d9e6b2cd47990c51f9cdbbbe47bebdc15f2bd752207c12956a0e544eeba505aeef2a02b6b79908f57d6f2d159acc00d37f2a826ba32972f7978670ffae720175a162b200b72d1652e1772168bdc70235236bab874a1c864a28ffef28e095d283259c831d795c9dc953ce842b90ec51d8a92cbaca020b99013b9acc865385c36e4a3158bb43cc8add8c1e83748d92ce6f2a32b9b3117696f9196f52012cae8410e7221152510ba110945f49673e24616b9e150427779d19589de22ada044e04b1d84e414230fdd455a7db12017afd5171b997d58b2e4f167ae5a1e92565f2cf4cd83aecbc72bce5890e99db328819194852e92565ace6623a79458e77cf3208f6a902c793ce71fd92290e9d4b9bbc4fae651744e31f2205236632409e545ca8e5c16e44beeddcec33bc5c8973c822b2bf2081cdcb89479a683b57129f39bdbaf3f06cd73133f918c6551cde227cb1a16d08c0134a64f0b5b5a183306cf1839637808437b5f1d39c4fbcde77fcff579e6c3f5f9cde9f7b32884f9fc66002ab58a051bda6fcefdf6602bb4f765bdbb2f4b0af9cdbdef9e452115dc81430b452106a052fb8d0e56cb9748d98cc92eb934bacc979446d20ac9454f22ad90d8703e3a9147f5bc63fcf4a07be5b1207fdd2b8f85eeba562e27f223d22a74a220d2cae52e17dde8ca66b11ba49516c9864a839f2a79565072cdcb9f9f6e9d7fc68aee95c7649dcc65571eb30a5df41b1ec195e120adc2d0895c6625e444e495c75a3e445e79ec066925445e792c24ad04915334880579e82239c595959595152bb9e6a5cf4fb24a24c24dfc243930d2f9d29e69f34ab8cefbaccc7b61e82dafceb48757ef4409b4dce523ed31c5e52df22a8864b3f92c569d232d39c5911198c5c68eb5bc5a8d9d6cd26823095c235fd65b6ec5632ddfbc14e137f265c922e3f4f2c84f9fceb4878b84227ae8d5bb99c1247d132bd0128b84de22652d0f5b62c8e339875504b3aec71ebc7c79eadf567bfb5179a6c0a4e1e6a1539108f50fab8ca73e1db4e1141b30c2f873deaaf13907bf16814c1e6fc1b3f8f91643afc3951065ba3f74cab498585aba13c8082a2bf0a006ab01539899d3268a33a09001ce1a7fdad89400238276682c50384fd82e896d03c36b17d3ca5519ef4650123d5c49d3a78a324acc62a002062d59e09961882c98c00a9918c3c60b0a2b9248a1052d65e4b0700496181ea880c184156a9851bf4079a603faf0d0054b0e3ac490c59675c21156c8c040e58b10b10d882e7e2b630d5f120616795280620d27aecc7a90f2849a2fc4f041e20a0d2d35ba2ee401ab33418b28070c47ad1726466fa8088e1e387636f1c7365a137af8b8ab5d1266ba24462f7d42ef82226ab50a3c21065680a1f44d126b420c92b0dd1a31e88225e49810e59bd3e7823bd616b50281522e6c81e50a22d270a266f4c21938a410c68f963d21204a63c80a3956d4792ac17e7f62376011b1c5eca1cf0f5788e0fc308688e3380ecc05377431451b50b06046192b00ea689f0a30e8f0051e38410c01688fbde39605037001a286374e4ca14495a42f64b4f451a28e0e637868586b6d2ccb9fb0b3d67aa8a586b7564800dbb66ddb9f0068dbb2e0388ee338306a360a0c3923c7e9e1c0e82e09b734c41989729033bbda308ee3ba3b35ac5d68e70f65d31b18672678c41cf9600124860f7368501344cf1a785c90c306960f6748e0b5b9e10934c260830f123eb4e499028a3f61d4c892a13363dbb66dc3439fb8a378a0881c9006167a5e2053c2137c92b0b2020e538a4083821577bc32267863bfc811a4c4d2eccb9d3743f8b132069e35e42c11484c135eeab061c61a3266d8b66dbb02a89ff8bd2b5a376e9b1795853214e6058e0a6db031e20c375dc4caa22b8e38c14f175df42c5184123cc6d869f8bba0e130ac3831049eab2e725ee8e285082804a1062a6a609c19e3470531b62f018b0a15eba1b5d65a2e4e6d7462806033840b28a8f083151ca49006971fb6b879a18dcdfafe30953e436357c60327b0060c52c874e902893eb1b2080b0c5957a408d1064f6c83b3049509c2289282a986258ac8418940808888e2824415668099d3264dcc6e6aecd9c0a8c551c2b3839487ead9b6087edbb66dfb03a8ab5c948f0fcc5afb0015b6c840840f6bf4194301265889e1cd1653c6c8e3820564bff8c268c18f116c60e9b2e5ea846ddb74e80963420b6ab2e02286d8e6a7e560f5114e5c90854d1a36c8d0020b59a021254f9b2ec290f2a6081e62b6931326c80b1b4bd3c6f3c20faa68b2a1d5ca3f048dd21faec6a271a8284f9c3c73f200513462d90931782bea787a7a90636d51f7c34473840a615e80020713cc50b2620b186fb2f85153a68b2e46c35514319b1a4360a094c20e2778c1c41a55b0a8d1c3842f6eb408e28a3163e07001830b220c7c79214d1a5c64e9c10427a81cf1021524beb8e973860e73cee820461c3b1d7803c55518294af4e027049c9c91092fd80a1e2b7ba8952cec983f514bd466342966aa6041c5851b84e821884e1242b33045d056b861072ceaf4d922041d861c8ee3b82fdd6aac50c5586b6dd8d903704438c503da50e6891a117f8e2801b6edfb6ddbb6edca5557b72965fc36858d2a26111454b9a0a58d142e74a1020687406ae810854d1c1fda90e2849c20aa1637d02b3552a0d65a8b4219cf13734268adb5dd1d1004601031054d1648bcf9130131d8102184195a00c1c6c847866edb3c3836749bfd9e06301d00f260a140a50440324ca982068a1e4a1883c60c2ec2b0e9c2e4d065c1449b34ec0579007180c0d8580f57b3ab26aa54d1a5872a687001ce181e42383e0072f50a60a86922501436c260c1dd098918b7f96b805913468e0c6c8c61e7852e88b0e28b2c68fa88b1478b2e2a9f2296aea042b864e1e2093a67b00122043e50c9428f1d30ba24f1c795c5b487e39c03d9f09c57ce09381e1f38540f17c5588318175a46f040c59fad0a3096e6c9f19ad8289c2c33140e177dc29f0ae78b1c110d77010a344ab53cf1d56cd7c5e9c46c2bc809a2badd98469ff8c7d6cd24b238a2091fbab85025c8183f9499a10b397142d08585b302ed86155ca3d16854c6a271a8e8aac31ac114fd1686d63eeae0ae7658a3edc1075a973c6ab858230b156848d3c74f192cbcc4de688144e3a95590450dabb12f65ac2ddad8d8b66ddbe205236a3384e1c4963739ccdcb04697345d8081868a16845228f3c38a094de0f963070519c2c0f0bdf1628527064dce1531d858d3c31d3d4a415ea800c6891cccb439e1823953e0f1628b174250268a15a229b62d841378c8e20207159e80b3e445072e5edaa0b973441c29c49082c6aa8041e359c1eae250305d08c57a643801d00a73e09c2943c90b6118c1a70e9f10600552c27b13c80ed2e7698eb332c3735c0e6d050c115c14127236af5b0f2d84cec0dab66debea96c1986eb3b54d1f6b6763b88013a60c2a9a089347853469fc00e884187688e244adf5c6093a43789eb8b21d07049ae7c4e7eed4a6b8fe0b9a98192d5af890040c6780618217464c37f3810c0d4ba4b96244183d77bacd06b529632dc78518bc1ddbd1f190b8caa26a6a4576131f07dab6891963df6079e15db0f1a0d16853ac77f68ed6cb2eb4e468a65b33f6f828c6f413ff56f461ae6a982ddb057f041e243b667cc33181021f2c4e18e326893159572c21658925c228a289285e18a57867faf413f7889df9d34ffc6537f2b09161adb563b409bbcdbef9a10eed82072a091af6a440a676831e3a333437c470041559646419014506ba2853840b7cdcacb9f345873066108186952b63443b1c57bf9855f45183884f8d25383a7ab834e4805d121b0fb40c976d23c2f5d65a6bc3ce663dc1d5ac6460f305203d377861c64c0c0eb52c940033840c7b6841d4f1893c2d1ddbb66ddbb6c5d0ece9ea0d2a26c4200399335854819306ce6f1b1728fc4684f5849b22de0871f5841338d88926dee0c0a7ca992a72c8c3c294ac1e86f0e28a36028d4d2c71006b8a1d2d7cb4c0e28a930a56a451a38a0f3aa04007853a550d96d0123443d001038829e860d92a52ac49830e17658cd1c69db74c042961abfc8980892a69be706941cf10575e7edbde5ca1f01b4d0514b8a401031617c888d1d962dbb66ddb7e05345d1553c36fdb057cea126a709689335d8d891ac425b41c599bc5e6ed8db75b06d65a02b8912b269c91a58420a840e3bba863e68b3f56e44401478e10bf1dfd36c7dafbd65a6bc34e0c2d4260c4943c6c5c1db16789366b30c1c213213013a58db75e8394b09623428a6db3bf6ddbb6bd0a603c8fb9f8cdebb66dad4510d7861dba0616ce004db4a0c5092df4f1220f57faf1c282942a45b051458cab4f7449fca007071fb8f8c0420e6fb0e0236689326ac033854c8ce3b83753b83cc72131563fa0b5d65a6badbd801846a000841028a4a181c403c61425f4c1010d326216dc22be1c232c2d75a8c883460e6b2435a186cb19698461a68798058f18a27738047bca1eb288d3460a25d0b04620cb45940f96a508148e58c10e1a2362d688b7d6766317b212333891860a38cc59e30a6b54c2132918a1821337ec10b3d6eb9b3e7ddeda22b6502db2c0c842678d305dee0cc101ce165a80e903464c203dbf6d71c8a1b16152c6b8c62840806a40244c78c106cc161dbcf0504495315f90e1a54c9b1048d1a1b2118523a245e0428d24a40c91458c1bac4cc0e28d1a448831538585c3b9306f1823ce0972be3c91658bd915304ce893250b062b6cc041224e056f66f0df7ef3a2eec131a93c5966a8c1fa220b3d48d42143041453d8d046132b3cf85b4dcddc346021079ff1650b3654c819838b2b0ac872c20830a4642903cc1b295a820e502f50210826989022059c2b2b6248e30a303a4cb1e70436d61832c019a32dabf61b01388ee3b8ca0dab1b9cc0c2115b8cb0d2c61004d8e3041e16b2ec6051e54d1434383570180b40ff4a063354ece0c294134050060c35bc69618d26b8e8f2adb06540419d31b2dce142cc1458cca8b0010405202e69b858418e095d1ab5ce470f80fcd04962cc165dd4c04cf819034c9f2b96a87395c6145d1a597004f08dc6f5731cc7715149b4b1d6bbb5bbbc849c5614f4025a79ce1a2eb26eb8420525b050c08b1a5e80a344145f44a14366a313c3d000c6a2f187985a90bbcab4c7f882162b27b071c613719a48c0092b40c1031d3464044147cbd7e7bce3f31c8ee77a3cc771e3386e6eacb5d65a4f7c14e4785ed8352c015e7ef8c1cd1a686c5144ac2cfa25f28062683718808605336154f8c2892f31a88de435d6a803031442e0b9c28505c1183a26a4a1d3650b2c6216092a6fed9b30657cf81683b7d67990433cf4d5b3ccdaa38b591fad75b7d6da1678a04f3c81600bc6eeeb3bc7715ce7cef9c8f95011c7711c7742adbd1b40d09045adce15953e53a8c0c49b2e4a2071e70d2d0d9be58c77413776e108a6cd1cda9ecb1413ba206382164c10c11d00862e42bc30850a3429c4b8da11543cf79c96209ef3b0a1849e736f28013ce7b086b2ceb98eee31d268b428316ef3a186216d5e0e7d51c3aa6f2452c36a8f58181ee813bfb5dd0f213153dd66cd02a178f3caf9e6614375becda14672f68183e93923c6cd677799f698e1a0c61914ac4cb9d26242ac2cfacd47daa3481b25fef010c5175da410db9ce1bc71e1373fbfb93754007e7318ed51639bf3204b2503784b860dabcec519f9cbdaa38bd5b1d11cf1e5082a4694f5d98834b6e79a1164d4aed81822ceb383cfb521c6f09e6b437051c1684a29a574de491bb976a626c40f716d081fd80c33a594569252a674cacc8ab1614a2ff38e24234869656e582bd1ade3db8bf8ea68a810b8e9db79cdf74b50c4038521f8b422308bb543a15e8592573488b53769d5d3c12900766eb105b53a7d39bd4b1e33db593d67713bb7570215dc79931563afd7521bfec43a92c674b0aa97de532776162be0f4f3891e3d4a7055aff5be36672a575dacbbea629cf32dad8a342dd6b977ad0c20c6bd84cc326fed4672c0397f9131d66d1b49e57a4b9ee9664f3d8a7ec75da64f65f7b61bab470bf55ed558757a8b8cb1eacc949254ae3f5399bb271931946e308605be332b68610c487b4a9693365900e79591ab8cb361932905ec7da9e6cbe66cb6063c98f6e0bc1c378ee953e57c73eafdaa80e79b17e99cebdc92453cdf48191beb1ee94d8a6cde91b2913ed1ea0fe058117da24eede6a53fe538b65d8ed55bc2fef2a04d5f00ad8c4d75ab2f66c15a81cd2d2963539d72aca34f318e59a75648d0371bb0964abea75e9d9265754b9ed982f09fb9f2181bea541ce27df5bee76ce6a6dd5f40a9f1730a402a27f33d726abae73798f9b29b4cde61bdaa7b2ace0a3fd0faec7273ebdebde106371f825edf72d62c76e90da91efa148784ee89413807bd1b7a1587743e7d92cc3b66d9a4e7e02ddb7bd23d156d835656cdd936bb8b3a3330d61696781bfcfce6b40b3b3802a6be9dce410736f0405d0f85f178eb1c58b7b02321345459c47218d006d0dbb74c2c27c797ac878f7fc97e7ad85869e79c739b3c37e7e955dbf37ca686b24dd8abb62146be7d6be7d924b38ef9638a3a039ca9cf0982df5c06058a5567e5b1477664499dbddc806c4e876c24146fa8ee8d1c32bddd3ad23854abe1cf6fd7fed850bcf964a38d369ec52cd6c1bedd99f52cd258077b546fb769cfa2908ef2d39bfef1d2802e179d7da8a8a140676a28ced9610db5f9ecbf2b3a776720f839f5f0be38f75cdcf3d5bf3a5fdd73902930d58c5caba1cd3305ca7c75b10607bf70bbd5417036a97bdeb5e00c2d157d17e853af219d5bef3a8f1437f83aafd5710e7a9ff37b1f098473cfbdd0b93b7f9b403eebdef711a9fe9153d6cbea4e66bf39271299dabc7a47969b87a4e7dbfccd75695b61b660c6a8b8dc0ab3059bcf58140def4752c9a8bb2ec74030745d8e7d97bf9c3d778778e494e8f5c7045246d1a92804f48d2c4507c917e89d5b6f518848be407208e7dbe71dd74d711b50aef3c812bc65e82fcff9e59140ac830e921f82ee6d539f836439bfdc888024f30e926a66794ec16dfee7ddf9179405788eb177e1cca0a33c7b8bc7001a8a5acdd9d7211610a44128d944660f2a9f53614341d0bb5ceb063ba4a177798fc8802a12a11c6beea70b9875f98e080ffed380a8ef9c8a43dad52aadb01410f59db71864c9535909097fe9b916173023f31d905999a73af29d83ad566955e637661d65e77301b321369fce75e1d7915ef85d8e8942da37f22bad7ebc4e494a96542452a9e84f518e1ca1e8ab8a7ea33ee9d4f4e9f5be86941e107ff4d616d09a3e6bc0c4b141ec5823fb74906f7df189904bfc907d3073ab452275b2f38f261d1aa7afcdbc63e37c63ffd807d71de7d33972faf7d33ab7cf5b1bd65b3ffb4704243fded11c7727eddb9977744779b7e4e8d46fc089354e07419e358c754cf3e7a73313582ff97682b3450684de777a78a7c420edd3a7f39461e49a1ada7f42a0f9ef4307411a4e3148e79e4f35d8f725b2e87dfb452eaccc3b5c6ebd6fcb5db76c2f81b4b7c872b608a47df4283aded790f696b7b7c8fb1af27dfbf74d1ef10eca3688f3a63d3846bdafcb5be548e4fbd08fd807752b1279f10eea2179d4f2a89eb46731c8a49d09f2a81e698f20b2f61375ea22cbee379f6affbc6c6706cc86f8bea8066bb728a4fd739e6af10eea2ede41695f79071561acb4282f3ae88122e972e61d9ee8f25658d207bd057ad5f62df13669b9428f6a57e82d3174ef96f3411d20e9f9e77df9996d10478e0ddb48f22d39e4ade6c480132b0d178f35dae75a1a3ecc3b24e8a694f29c96c99284e7be9346bdac7c27d94514453174d0455114456726b1a980200882200882ce4c201b9ee7799d73ee799ee739337967701cc7711cc78d338cdbede014fbce6f679177d8ae61d6a775be9336d6b7d645f6f1a3d9471f8f7dfca0ece302903b30729c832eea94524a29e52d08cfade36b35722d8d9db70eabdb76836c5e3daa39ef185c2e57cb450f1d7497cbe572e90141f073cf3be71c0441107402646666e6c9cd85bc83f379adc6cd6dd93dc779c83eda3917d9073fe77d9938ca3cc027f6a9bc3ed5f8a49faa3ed20d3602ad831b5986bf3948b93f2b2065f7d7822ec2f720b9e42badbe7a97ed4ae6196fc9255f7b25cbcfbbefd3b879fb9f8335eced3f921b56d6df2cfdeeb496b4ed79df9e156c0ed6591bb6753bc8b56fb28cfa76d00bedb5ceb5e7da0c653cbff9266bc3b6cdebb8a7cedd5249f8d637f247f5a6cd020ae19cabce79ee798b3fb6eec97cae5ecec16ee71d3cd56dd0f9e61e59bb5a2fd8cd39ab77339c0ea8c4e955ea4381182471d0213353339a049000c31540303024140c87e3812ceb3a14800d8ab24c4e3e1507234994a32086611886611086010000000c200431c52cd20240f98d80fa372520bea03b32db02dfc3c5c0646f1b737b5671ab412a4bd3d0b46d8338fa132f40f8f97f0e6842a284a96cfdd1076c01395441522d3f82a6e5d29d49a69e6cfab01634d157934db4c0396e954670f0205056eb49d63bfc6974c9b3a92ab1d84b2bb7ec8f3336c46497d4e722abcfb4874887f6508536fc821b4fa23df3138578013124db10b427a358fcf9a2cac1172f72e458467633bd79682c41ed91b536a90b4b31d5ddb59f787cabb1f864e8fa938014d16c4299fcfb881cd59999483f8900b9ca283b8bbbb9b90451068fa0516172a669c0b9ff57636a0fe534bed582442000c630a942173385851b3a1f23115bd1187bd05031642f476c7ba424795252bab50139b366b1a405306c6f4ccc9719f1b67f155d434e3b09921dcfa2e435087f9f8b7118c2b44f239196a50a51e8c719c31a10b57a80a4a83dc42045824254da68bf187a0a7ddc0006a67c81f8d7b79a5171320507f60b9825d30ea3ad38a751c169c4e5be36f60cb1d66c30c0eb092870fa0951e872814f3abc3225b81f51ea6c868c0cfe5c27dc12181a59f5f1b78dca9fc2bf341c594c3c0e3b01aba9e9ab10319139370dc491baedb28c27ca48d09791302eed9c8014a33b9d86b6ee4ac165a5058ae2fded93bec92352d0696b16a8a5bf502c842c3d6f7241e152d5142029d8dc388879a6c5dca08eab615a483e87147525290c16f38434eba1662fd488b6243da065b71fa4f00c76499a503193fc16a7d8005de30c26316d0a833392e38913ebfd7084331252c47ce24864338b0960094bd64caa963c24b9c1beeabd95ecbb6121697eaf734b3391c6ac9af1f4d1731ec476f3131b16f25c8198a9af8489b787cc6cfe572ab340d2382d2e508241d27518ed748f06eef1af2d909abafae0c5b92601d650e0ea8204838611a26f1c6169273853980c39177cc37c8bc09e1dcc129303f7c9817ba7fb910736e3ac80937278ff91180543eab0068180e8dd7a3d5ca04fced799bc12b078702614291a96f1da2323c3660de1f3bbd6263617a1980d2fa29d96a4250f52632363131e6a0ea090a415967ad27738a334ccde5a94338abfbd2382a7c33d9c3d6c10f0c38b43037fbd3c18d1374ffa8645cef65febb6c2fb11069ee80c7b970a7e8ff569295292749093c1263a56f1b16e0a72eaf490e80584701f5eca3ac1ac0b356c3ae44249a42314bb8c0d91cb4cb20c86bd8e0eebd90b1da7ddf774a632f289451149c6b2dae220b2c8a4ece63fca9bfc56ae532a9ab4f1e2d5eed41abbbb3a8ddf0c0f33830ac31982157e4fb8d9d7361b2584b6c1e8a8b1c1d677cd7869aca31f2e2a72f8c39ffac0d638db85105cf4e368f14b511a7508900b6ce666c46f84846a5229e83b3205c87b759de873182ba204ebebef8e0b4a298737ba3d1af644a9b36cfd1edef8500891e781fdc9bafca08592d73de6d2e2c5573fe5764b929d817b5394b756da5276aae8a0f62e647d259123f98c6418c900d0175afccd67c6d64bd222a487440fc5a6c6ed63f3fffa346859bdbb17079ca724ee535f0a1d1af0200df269c898621df1815527a4274ec8dc3230e863158f6b9eb96525348b2e3444a74644eaa9cabcad96b0e12b7b138115bdc82ece7d4c62224a569448681384e8738495c60946fe9caed286a639a21aaed28e5ab463c9453f47b1b304e359abb66e324edfaba71ec50900ff2d4f9df249147b65bc8b12942720b846abbcfdede10186b14c2f46951a6fd37ee65d0bbd0820ddba418813d07d62cf156bf30610e0a7d6d842f3f76a35064c44b32a06227df918c00c7d08d7097baeeab8c624d80670ae83d966f7bb11a88491b95f3e87e3ef13a51e8fc352da9ef9ed75ba4446cffff645d5bbee462381938c6c8ce9dc25257b7c33860a3f79a160867317139858ea3472b5a8960fa635d10e6c0284907c2204a9d3b974e105a12dbc6d741ed924e107eb2908a1655f1e998451eb12a8b0eaf12716d96ccfb70fcdfab0f3253cf5f9e5ba446facc66777b63514acb870425899806ea596101d5340c813baf06eb94a7f42985cf290a2036c12bb4b744292fcc8d896c93420a8dbb990b04101904d8c52f909439a3f6b1bf1a397c75ce4ff4a10bd321e32f50c896e4cb5f9fe148fcd9700c7e3f38a0871935d2d3c9e048ecfffafaf2587ae45bff8a2d289dd8d47f7b06fdd5268d4a3ff289ffd04fecd8f6e8c5c9faf11d6f6fd4330fc59920fdefc30d27b85f5d73a876193a657247b1116a4d88dd0f0bcd59392629480356ab2cf17a63a8ca37ed22a32751000335a3d2dcf69cea7e0384e53e0e8c6b4c79366e36504d2ff29d7a91744bbb37aa5c9d12f9c640496a7b6cb1cfd0451cda9917d0e0b97fabcb8683f607c9d1b604eb4fecb4aa48d52b0360a22c97aada4c7096c19099171f4c07a186d381f51b4cbcba01bb996b9554e059b9e0478645793aa30a3336925a3eb1c7705e637f72ed2c613f5f8072b87e48eea9cb45e2453a168bbf559b39059b0b18098494b0515cb35c830a7cccf3c6c0e832c6b61788e0722392361a643dcaf9bc185e774cd28dacd887daa4c327592c72822724bf16ca34680c22a181f9b470231055ba0661036fea389b93a9664a76953000e59ac28b1333357f4735b530d2d7412f224b67834c5832090394841e306f96038493e147d6a24c6611e268bb8b838088c51c0c7c42f282de337ffcbca009c4be2a3af655b4db8d0898c0396a8846aa9aea682b107034fd50b32ba44fb083a12bf0cf76bae99f4d95c07ef9a60a147d43421ee19081971ae9fcbafa604b49bbe7e14e997149cb875bd5f637052c804cd1b43a4e83efa7a40ed5eb2d26436fb374a685b5c2a6e87b44a7678d66e410ca8224db1df70ce8890106a802807a6c01a86744db1ce270f35865543cc13e0f6a2ec7f2b829377b660ad860382f4711f0042802541809ef5ef222bc0ce444b424b658279930de78ca84e7b9106fdf4e6210f6f294485d570c7a1d70ad9ef721eab3410e4c801ebdb406ff20e262a4647624f95c4c651e5dc463c434d93181cbd8f0d212ff7a179344b0e62015964adb30a67cfe6cd92186507ba3057a7a71c4bb711b15c3ba44426462bb43d3b9e893af6b2a8bcfc4489d0b0054aeddb146486575b6c1b4b2a970fe4363934c0f15ecb6d9fd80211f87c575fa023b685c0a32e2cae30f679ab7ac5a2a4d938b117282a896feac5c7898bf11c3e9c2b0f6dd224d4c9df4b84395c4f16a6ab5d8900f834499202c9f8c7ef4665882f801be5c7c24f138f0233a75f8702b105a15d9a4e94bdf56973c654393939f0bcc99a588841a9f98175bf69992932d57828c18daf7ecab2ce2131f858a69fa85e9a08b7f29b2376d6b159df51798bf7c5f139214d59a03f304cc360dfd6305b810b12a5e98da0432cf78fb7170a3765400dbff9c7400560453eff4c900e827baeb3f5af08ac51024bae4a812fa35dc8e4a79541a323dff7c4be8030a69a668bafa66e961fa1f15e6c825cb13105224e745582f280bc154a7be6d0b98205848678e9a11906fe4ae9e5086021a75314836b1930ff10eec9d89aa9be8cc143d083b2292279758786ac9dfd5d3988283d82aae45fb6dd02c613ea0697c35ec5f5846db8eecb84600a20a86d947e3b57d992f00d6de082449e12fca1e6848d8c74668c266bd9ffd673baab27c7749ddd23034904642bef4590edca2d370920a194335b7767a6522b0b16f27aa1a5d8889d0a18cdfc359fd39d7153bce269b9b37c90af5f08525114706fae6e692740a344090492472cec7eae86768cd8e2406efcecdf4280e3a03349056c2ca247cb74fd07730f5f02988f4bf4a9a3562a409b7e15690c46a07e717bbc90c7631f9f0a7da046d7c4442d2e9ffcf4c6becb6977f81700bc277717ad9944768494f2e30183f4cbfad1d317f9088051facbcc162fb2bbf9b6f90f6e7b055b7801018070376b1fd09701098d03052f37dbb647badafa142e5eab0e19ea516e033274c089e05b7b1503a10e53275bd4727b8f947d7b9f6c85a18babf01375f81bf474afe58fe2fd7e15ac01e641b32b85b246bf593dbfc18436f6dc97c11ea030ed77f13d17536840a9d077a29216079042caef39c25dc35dbc5f28daaa8d31586111d9c2d642463aa2f17eb562d44600eb18632e76e35e55d40900c18bad152879a2222d28141289879a94d216e60330d39d51c6fa955699e949335d4249566ab436265e40b7863b439e1a13f6d99e4e0ad8dc77d2f29d0ad3c884ca8332ccc2873ad907064054b704227c8f424b8d237c6e049861ec6fd514d973b879a83ac4e21475d3bccb1cc567e32a0c92a50b14012a487a4504f79d1995b9a1e90ba534b34fe4586711a81fef40a4277d201ac6655b20f9c4c588bc89133f11dc2f0aa0ce71e2739b302f2ad54778134453ad8e001841306f36455dfe1a0d165fcb08502e6719f0352a9e6f9c34f10a2a84404b7b56704507cce052864c9b7dab7023c4f3d25ea03c4dc434563653857e147de5417dda63ebfc3f349eebc2e76410285b475c4e389ce740a4c69748e66a3b098158e91734ee7b2b355ad363857250ea1ce18733feeb2f35cc4a7eb091d9163d22e84b622df62bc56ae3b228d1de11c20871a16d4cd7b92d4f749cf46b9bd1ee0e15c50e370a75d40e86b86c7dce10bcb5ca261a917e68c4361b9be4c7c0b57bf9a8b88d7c9b15816ad1e99339c6ffce45489152020deda8343eac8da6d311679d90630a0e19e5a552e708050afb20cb55c18fb3c3c52fc1e5cf776dec2aa5d3677c828b07213c9aabc43774aed044abc02c3e00dd97d01177484bbc0a1c97b80b02d6cb5aaffa69a15cd5128c3f6bc958b20e2626b42a3004790085beff8a2da2439082460f41982f6dc9b3303e3e017c8e00bcaa3002d80d6f41c5b99ac32d3013e25295ac25d02e3b37b40cc95f3b9c28717020d1b8635dfe40823927e64455aff9d51e501cb995ee1132dbfd7da1a96dc17d4e19cc66a57e42ccd4de2ae266a10b9d9987b5dc281128c1e053ed6623314c81aeaf6ae72059007e68229102fbe986526d62a058faef918a58a98b37ea616dd2869151fcac13f9dce68974d25a9d1086298397b839c5e08b30ceb36a4866a278ff817b60644c4a11c77f92572e3886e553a387447fa019c066c061494f33a104882db20c5f2b141c1a73229675b2dfcfec9adc9b98e5dfb4884b72f5b0a4c1059c47e6951518b9b85c51b06c62f30899c9cf1d8f80e7009188651d6440866bbc4c1bc7ee8c2143200eb712e8344b06740459b9fa26a8a033dbcc618a1d906a6a31749efc012ebcf63ab44e2e6789b25ce39a98701c1f3b935127ffac6f3b284a92f3c045e86191415ba0eeec784f5d4791cdf00592b775c387b0bf40136d88bd503c525e524f204a62f80d947ec2e1680363952d676c4916064cfcc2511b83270242c465447176770007b5cd7aa11a5c3e60a86e9eb1b0bd852832e84d45b5dacdb4db25b26f24b0ff059f486947c3173495aac70212d857fe5b6185d432be8902b51ad684749a43a51eee3ef8b51e94c27b8270928e3e0b19441e07644e0745139450f5337c8456b285d6493a0c12449554b83bc2407741f3e4719ad1b90104fbc59a24ed7259602e2cb1ca2848007ff295f48b803cbc68cfa36f86993c0a612360f2d7400c9ba93a62c8510e7bd0d28e8271c28a397c8f9f19206430e01b4ae4d088809a4991f560517e29c1a6c511f9e6e66f5e0a2bd566bfccb218f3a10a6d506afa99e5b1d077f427f8e6eb6c13e1af4e69836b66c4db9707227386f3a20076562385eda75969a717893b62b83a17f4067bdd7b9b14cca728f0d4683a3ce85cc03c48613c97e2ecb7486f35d179567fc65778b7030b4083d906c3aad875f7e91b5ecb475373e4362a5399248dcfd4002e41ccdc881210b7adc8e149475f0266ba799902298f32739301b76368ad94398783a0d444019799e7d214300e0833b0bdacc0b4acae510f36ee89c06a5c000b3a1b495daa3ecfd8ad1bc3b4dfe8355f54b942b5b935076ca49e405511142812e54f0162522ff2c8e86a8ad6a28326442fe2651a457075d77bfae2c1d9eb382cab346499bc929bb27da6767d19a4f93ec279b339298f87cbc31a4ee256d9f99a97a35ad9be6e2da34e5ae26493f68a12cd96351c995cb20ab06ea6e8acf14f76234f86bcc5eb5d244775f31686533986d7a34280735c72646cf2139831505c392a1155d7ad63861226978241714cf875d98c1b71af188cc5a5679f164522d2d4b0dfdc7cbb573709b0deccd0cea01ea4e6d63a2e0cbb5143037f089b28306334ca18e9995e9ee94574a477ff245a918f6c1846a676dfb80f3b800f334b9edbe5cd63be1482c15894bd109c7195602b7ccd45de9c58efc97ff7be17726f049afec455b888ee2189fd641a801a90925f14484ccc4d28202515de24edf177c6c1de88b0e8aea25c749a95700ae098c0313427214a0816d2b661a68248555a41dbe69602b5a9dd43135bd018f74ff162590bf075c5ece05841fa7a5a84d709663833a7152403c88e9723e70692fe9a1f0175ab1f232148bbc88e766b82e22c6747e793ddd19d5f72dd1414480d3a7202f1106c7358fcc17efec17380c62964847c951987d64703e14f9f48271ed47f1457a30f0d53ed01be5ca322b33253672ac47dad415d028748cc3013e2699c928f4f664e17ddd40c2293929ffa786268b81e223cb07921c7ae9549bb1e53e8aedcab387b32c9a7f11889d4383ff4d9ce40989d960a1d20e749f41e178b133e483bd80ed4478aa0be033d55d61826a09bbeb1f10e045e8380c4c6400c04bcd870b8a8f39145376721c824b333dc3527e3610b7a8c481344b21d6b8f0ac8b735b69df22e3a7e9455689c304221570c6d133705f9368e7d01353033464420ba862323521942cc23f1a68cd322c6c9f544ea4cd189af0dc719285367e7ab8359d55959336b914df7ca26c7e676cef524586b5a01ae9baf34589e7c2b581ad42cc622588e996d0e809b299171c9452c91a69fc65d611c56baecfad5b239e18b5935b7fd9f575a2371c72bf1916533f01203148d22d8a08a5fe1a7fc186143e4a5424799858353231a9826a395079b28e75e7ccab9e9b8bdaf4263e7b2dfc89cdd827c7cf3efec50f86b5eb76094ff4268d9faf49e1db5075b10727148f024ac9af5326525b2d8f26de68c22c448ccee7db2faafba9653ecf7c65862650df08a6ed1ad2e231caec1562006463b72a60543b10942e6a8176f24ba6815748ef0812c8482b50a54410011c88e52f9c60eb05a1ea9444ee5747038fb13f2eff53d27ebbbe8077b1b18736b00d43e90c26efccc0e7e538350ca3851f210f3cb8086cc68cd9038f9695e1e4b3f1c4d3d1e884e47d41b77034125352f118470e1e7d53a9748a934d03e8b4477198195c53328ac33f0c210621991222115b9696a2c81b10f55f313856a0386b86a003acdd17b41ec8523fa3614536124b5acf819bd75eb5213704dda829bc85d74a65b172bca009da1b5ec54f514a9d30d0c0ece781a483fe752f8fd2ade5a42c84e4c0a1300e69441a3c0850b5223dad0211e14132f9d2da5e16b46e55c8ecf46054060fd40725d16b145d37c0f2cd5a1765217c5ff1023241cb30ca03302c9ca45082f098815dc044bf1f516cecd0a07379554562ad950cf26f799840377d603e6f3482eb83d13d21061a4bb419a0b22a2ad52124aeb7b1f7e42ab326163eb697a93509a820e83eb144c9be8c735138020195047101221bc7bac483b5c99a6f196f34aeb91c809d0f1360cac89f850fd73bcb23d3e9c2634bb4b8f7aa836b29215f08f601f4a834822726a47498e6459a431b2b31ba8e204da5137be156c666c94fab8720329256292c7d93ef4365a7f258aeecd2116ad226b962a9e8edccbeafebfd07f9782e80e85d0141c5e8b2d3fbd20d2516519fd53126ef042003ecbf7b8394bc9f43117c911154abd5faacd33b3ed31d47c5d8bb8dfd3d323e8b8be55901c9dabcb29a2d2b3e7787136ca22b8c3253b9d885543adacc0a9181c9d421d90756fb5e8afde4bc27741a71425a51a79bf5207664690feafac9dc066f990a8055760e0aaa0559120b0a053e050a016f69dc4c28ed0a3d55f5078d96b136d4f823c1c0f74093c742800cbfac1c614f82cb9f9e04eadeb3feaccca46000cd5291e24918411a0c9d8231d4f61c5e7beeca0674b722c934d53236f480fae3f3415ada4677e5b6e97df42f412d25fd2c1748c8c487c9509a6243ca1ce2a5b2622891a930f06ff221462e7209e6c7069504d4107695fdccb1801627219ed3ee23c3dbb42770543e6df2bccb2ee25c56ea4e65c632642f6ed0a729f4eb618e6160a7968c760b77f41050505cf29739ae30941e25667dd1bc7f28ac5201414cbd55948f1a38b6193cb491e6c2cef62c226026803d417e7bc5b20311d653ab18d0e98260c88f7df7aa10ea3f9a1e4a0761acfb6c96f24ac1c04f935c0f28b2457232c958dc766eb16e0b2b09d9e230fa769d49997071e4de0362f9bf9caeb58497cfbce2d8ff694e6f6f665aee96a30c13f882016f0e4e07dcb78d3d00423230972b3d21f1c2c55c682bba906ffea62f6657eda4dc3a28e542b091333b24dad6c0ff63a9b994d663bb7d4d28b29b3053e47a182e12e9a443d73839966d924ef3e8966d7d3ea4621f361e517b26aa8f110d6465724fa1b89955525950c2e668f1a4b30ea053578381ca8a19c5ce62c2da349f0aa900a335380a96c215e43a636c2600f58b89538887fa6f57f4647dc4dcbff5caa8fd6471ff492cfd41376b1821a9d468a5a2d40143b428e0f2821448c8e18d601e3abf3c3f3a722a8eac8cbc3ab9d88000b8efc250de3c7d3e785637230269cd67aaa95d9a0cf3ba7e8743cc89cd630b59e6d8b5bc88714e6c6f656d016dc1beaf8ce5434028002398148bb403faba14017507c71361cdd39517a16693e9c8cde19216c4ec87e46477328d4f52365e8abd882715dc7f960dbb9d241c61298b0782521c9de6c8d66d6c8fe6adb79dc04729e8b14cc6178e05ad90bc9f879952664fd1ca2a1adabbe51e856c7d1be40cb6a1cde605237a188ac6699ffb09686dc82a8991148e1688e99f8dbd38d85b40dc86b333039b80792c0a6001edb188c156e7f7b0d2c13d8447fe5bd97786100ebd1b24f87992014dbfdcc78875d745560b043cb096da79f33d3bd40ce29a6eda0c39464c2b823118aaca132f9d9100ef9c991119c2e4d1636948f005f25d878478f0f317387f37bdfa3b1c7b74e51951cfcc1896c112786ee4f965d2b2491bd611c49a36044cafa5666bab4d7c4ac4b0cdc3f835877437ca65f552fea781c567d4c9d2c0a1afb827f50d3a9bd6620e1dc285a9d5a2417ce7adb52669cec27a421d6f4361c4709427208a4cb0a634f7a08cd61ae4de26f5457966fb48ed84d3d938eb025c5e3bb8d1d445f0c75a9aee019a4189576ca408873edf34a301f053162764e17151d10b0e54659094a3bea2a93502da86f0a17d8181e35b4b9fec02942e47eb17b74a5428b3fd8eca8645524a8a48c497048d3dfd679d9a99e2267f10a2868ef000e7f85bd3869a72280040c237bbdacd3c488c6c038a93ad9f0583c60b68e6b4446670b2284231c11842d0aadb929d1431a0febb051ec33a4cafe22840c826295c0f542eac265a8689daca47329a983d0de1f669ff9787fdc099937937233a8412be838f77807b26bfa2b949ef29343c0c7b5afa9be4149221764fcc1a52b201293934c8e69b787385a3a98c364e7afd06c72c594889763a0f84d3e83a9ea1cb9fb06220ef1b2ab4c16ae5c7909663f39ff0c470a404db927152c05889543cc7005bb6f8ff70d4bed81b5147f1fbb4f2700bc6fbadc80148f4aee289ee8df240203b25c7cccbe317c238ce6e8f77a4e44b4d7f2b625ac823ceeb2d553d9c2323a28531fa7b70cd526c0b1504c0145ea0efee2a70bf8ce46aa1584f7003824a7adbeb746f544a1298b2302e1bba4e07a483de1714a32ad81fbf05500a70c6aefcd662b6791b18d0734ddbdb64f283a4ff409fdfa47374aaafafb3465289332e20f74c948e707290159d40588722ce4fe61a988062734e47a4f1e086920c2c4bdf157cc206a670f1cbae26a578801da111322a64468708973e3780d668dd90588add099c4840ac5d54afb40e047f3bb9bd52e795327a7a0a7e98583df8d80f50240d15a76aba88e7ee830c30014aaabd2b0981a8c50de13c4583d0e39451d516be6310ba87741789434c189e32b724924e669a69b2cac9a228a385470bdf25abcf379cf5a94fa84fa0bba2efa3a384c0bc304800e8636cb8b6d24abd890af55b8fb4d4499b89544b22336bcb04c4820fdc5dbc8a428770bee3936b1478e36cf55607d44f59688fba01b995e54a24d8bd7f875f0fd8a98df01795e115c991005dc8e2261113687d610b445a596da35745c68b7dc150548ec81a9921950590538602dd85a5dddb4001d1c2e37ea4baf625868f8e6d8ac9119e5484a313cdb8eeb6530557775e00c09272135b993930f412ed0635ee4e65924f293d8e7f99017ec37cb41e5438d2122d2d4e43c7b4e289aba261e82d73e6a807dc5ea6e01b14ed9f7a3cf83c4abbb9e53dd12b1a9ee11642da7747cb71caccea03c4820235cf99498540e25e5f12610799d31e9b0f17d01d65eb10b24aea2d8e71d9809a54140d98041b4b8f1bcbcd082141f53541902e7ea5a1d32e967a8e711caaf61c1cd2cb5d4604532e2c69c7d40047afd6c5c69df593595a3236eaec2b481f86e0dcbd99e857a9dc24c99262597e76363947493d941403b2cefeb117c6d79fe6c663fa93bcf154335c28f6e65cb7c802c502fb69640f9a580aa01d31c446d9581d09d2cec45f596596c333bb24999e266833338bbbd621bb28bd5378ac4742e2e8634d2f99269c5012e3420deeab6c98fd6a725c3d44320bd46b3d84b11e234a41f1f6f4bd5d38b2cafc89c1e4710080a89a6e14d93b1809d9d216dfc4a83b29512fcf20a8c11b1197c49b42c4e3040baa8d5faca87dec1ecea146542fa79b08d976c6359d2e530253ce3442367f18e12e2d7b00ce1581528b29f87ca85b006c675192ed1d5d7785f723a939ef911a1c4c2fb2c2aa09abaaa8fb52fd3880616566e85842153da86ed48f0a36259a7d2cd4315a05dbc2217438ddd8c069f42c3e6356a2b8b0718bbfce63067b534baf27250e4f7cef252430d02adaf35c1962edb8116aab0c918e36cefe05374982753ffb80d0f867fe2ddae359062d234836b5332156049fe0e2ce210d65c7d0679d3455febf877908058d75a33bc34d1155e5614b002b16035be28fd492e284ff9b2543a95252fbfbf3405263dcca5495f3f310d92bab89023f95705c100587c35b222992ae7002d8d68e1e33e9fd1ad7ad0fa7fad9b0004c76e7d0f08311138b1b319b385c9693c9697042e82764946185b668cb5122acc0d826e9f6f8b4685becc55c531a38222114199f4f77946855b98181976685a4cd82627ceb4110366edc9343d1094972f09582a6766a05010abcb193d2deb2d37af155d28eb8032e8b086193136b0804621581e681371ca80db7165710bc4a032a1841b52ac852454a9d9b1609066daa851f34c766cf1c5a0c15fd844d9b8e2944fa64e4b1b0ce07785c4eddc14880e5bbecaa66a0fe1da81cf8894dc0166fb6ddc14125a34d9306d808829391871ac4919142ee48c40e8f3bccd5a06d746c0140ae26291d9ee330557f7c5c5421cdbcb497bf27a5006fd147f63eff544707d1e599a6d3cdccb52b13e7b3d654153b0a69c04831ba602224785f67f61153dc60b9220712f59f97a386e378010ac69d032baa2894957c9916c63c9aaeaa24175fb76a67a830c442b864e1991730f114241641eb297f133cf9e290421556db3e97fd04757f88904e27f4c3215523fbdc04a2c49be1545d3ee52eee72c6d2f4a3110938c5b4d0d277cb8bb564159100ea1f1c9a7ee5463cfcf3f3ce963ace93f6fa662b9017f00e4a868f8352a99681e38b04115bd01def38c7f6e07e1fdffbd6a8d22a065371b395f00ba98f954f74443441d049dbd9dea1083100ec143e63ab637a5339c23ad43f820653f567d981afb92f4fb0c3c3ebc3181fcf16eedaa7ff8bcbbc1480acda5520dac104d03dd4b1af0c97210e31168286a591aa889f09966329718f0d36963c71f9f95173281d69f94df3462cc6b75b42b9fcf85aee96164016e5d14dd4c2f300700b7ac4bdcde6a65f989faaf80087d6d355bf466b2db139001aecfd1e333006cbb47066fb1b65d2ed54026f71d0aadeeff602d89a0ec7200dc0658be932de10f78b719366b3d4ea514dfad1b145fc28fae03df2e919b4c311c04b1aa5fa8df46979bdc13f8ef5944b5f0841410a603e00c04a570e0787b9534037232078765c2aaff246d8e00d879e90f44bd790675ca399f0be2d076c27e607dfde8b5c1036f9b5eb4d475566ec6a06772988f9c7cdb294ccafab8b68d1029ef4b4f943e632b9ca6cacb1d78de13ec3f016fba9a400d507ad084ebe518a9ccf70768417e9f08a295f419bc3326c75f86fd34b357caa3a23423e01bf5cf55093051e58aa3f17d7f48606e0c77d0da972b0245467065ed236edd11d70c20d84e357db1cc50fe37484826580929a869738a6e612e4318230741450c96c2182ea25c14fdd01ba03f14335dbe9688001ad334bc9d97fb2d56971d7a9e14f8ff2fe453933d0f67f34ee0da2e39e39df3cb35bbe24ed372c9d9faf2b7f6f8b7cee4be22e3a2c8d91a144b0bd60cee21712bc92792a02f315bae3202def9804f6c406701d7605c004a3bc1eca316367e4983f8d5149f93357985dbea27ead810ea4e95e95a63b65e22001fbdd713ef207dbcafceb5603c6ac59026f414a5db7150036b52067c4a865a372d325e2883e4187886d793e68345bb250360eb3944b38e17caa60871f72b4e8e88803f7f4dfa8c0267bf15ad50c47e1810b57488ed5a5fb12cc28870f4ef9267efbd4157be6b14be162632c6987933554ed791c86426b0cd8eb5b9b6d368ed715f13ad858d40ea35cd5262d3901a4d83f2584bd5601f1e77b2f8996d17eb36061f7bcdb810dc7245d5673d1b56078ca7c2238f6bc5f7ce097b7c481a87be064e5ac57144dbf9c21c580340ddff2894de13aaefe32d9cf396cf0634bf382d20fa6695d154c7197c9b8ec7506e247d98d3df1e99fe02ab492b34d32a26bac2ce32c90c234ad18221e587a05ae23e5359ac0284039a1bc0cb1d8f645a75b464cb545c2d748940a6885b9c672b35a4641e340d4488201dfd4e127993de473e4dcc83c159fe57fee01b80f18c36bb4db41c4797f5abc90273d967800de7cb769e535edb4e7946fe5a41a483a51baea65d3c3b70e24d5c3940a809f239c88097dac2997a6e87dfab5f27d7f24901876aa2472ba3d69e480489f91f9f34bad28985cd01fe617d6c7827a13dc488adc3e89bac0df29abb54389fd666868b784e5cc25079c0e20f6548aaede97e0125f512a6ea302a581571664aa91fd86ff9de97f27bd78730deee2af36d4666c07268490cc49c62b6e3ce5eaa256f08b39aa10060f86e8204d9287480e4b621732055d762bfd7f6b227a658a76b1374564be16a35a70ee2b2a7a087b241e4cc1b2de8fe301a902befe769b6b85ad03fa1437a7127f2eb15668648315f18d6edf703d65d94b5de4709ee70c54c30f5d64fb692324427fa37e2884b40457c3d9a9ea070dd32d642ef98a95bd2d7103c361ddaac2366768c2a4588885ca84ef49ed30a620bd5b0fec9e50a8d9e98223425604a6173a597a0e0c3520c6695981c0b3830224d92fea529e93106ff9f5fc3852cfc2ed6a41a1a8fb5da023e05ade2af9b9bc498ec31638be2b226e3aa893785508dd3304f9974b92c31da26a78604ab35472c9056f437ed387748c23196e9ff1193b0979efe9130d33344160ed5621f20509c359ad0517ad96883f8faebbfc795d2d062783819a3496411bc5ef86b059accc2eb5e034da126a17aebd366d28938cedf084795b3372e3c9bae5ae0a1164f8494a409b81741026647f8c37a54ff96c342183a89a31dcb4fb9951154735d3d0a3b3611192160bf5ce69ad7b09f4eac59573baded954a3d5582dcc52346d30036c5d930a8f0a09b68cca937e181e44df5bda0e10d9908948b365b970ade749336c0c0f4f1c269c5f93b61b27e5cf22de3310095f1c190a2084e1471b60a22ae41e80c5c2ba892a076deab3bd75a45d04593865604cb6a64ac15635e35de8bd048edb6f0d38554996ca2146f663d42afe548c2393070b04c5e8e0009fda196379f0ddce8e392026f5d116dd889cb9058313a532d82d117a85a86fed278de34ad703c5d633010c2a8e18f2474c7d4eee97994096c1e0108520110d0e06a208bb224f83e88d46185096b3e0bbfd44b74526acddb4fd824b4a4d49cfe5b73817b1ef9e33200eb09cc3dcc61028528b63451c01e078167ab04f50dbf70e8f68e1629460e4edab7b7e037f0691cbadc5b9c36655bf0328f992cc0cddced284c1faa3e1dfef43a9d91dbae9dc3a64486aff7434c39ca5e6b8051d454a9806c6c23790748a0f1eea5e7f37ac3044ae3c21ece803ae3ebdcb964fbd27437f463469659b2e8c2aeb8b1c70e3a7faff68c7956abaace1f3bb6e81184afd2fe354327d20d818ddf78f4c7c81747ce2343afe07535e65ba9747b6b223bdc7d362c06aa75f962595041d408f5e5a65474db4cb7a755d349a1917989dd1949fb80630b14236bbd60a313ac405f09ad227936d8be98be57be68d73fdef061faa7848795151594f0d040571ff44776bc9d79dda81a939185e67195f67f984c50258b8a70add15d6e8ae310b1ce0407db687fd739c9e5fc90115044195f23f8cab77ef195fe3a49b6862b4c590de31d034526481795c73f02c3568002ea85d445fb6b1e4dcb7321087113348c84f384b34df16a7f237e17cf8c543f463e9239f3bab68d04590bf17f0f58ee9434c1c29ae782ee3e5dc6f0798773ce79b82ff15124356fedc7f96edb9801eb4e4041a2021e312073b5318744c1a1c6b14e86e2cf99ae5de89a291e5b5293b484068ef72cae436abb4c0b6d03615690d43b08581bffc74019fa8e3444fde35f91045daea9fc478468583b6d571ad30ddfc2304cb15bc04c13a46803bd805e660316d5300e3498e7c1f4196761aa731011d24d9895dc2927e34adc7237ee42463585274bbd5dd711e3a968798d2ca294f7b48c81ef16878c0854ad9bc449d81fcbe961110a57e99eef1858a5e8d9f62d5fe5d2284d9da4aaa637daf150215b06736ab2c5f0723d0f68e79ec41521a588d670c2ad0dd0349c9b64c5496c466517e077f52a8c8afa0a59e551efb253fcd3e554fa1284166098b06529aa97ec918ec4e479388e5e18dc864850e5bc05842252bad4ad93edca857ae6ac6ec5e1c658a488c1b06d692062ec9a63ce8f87e2ed2d1d493ecb3fe8993c42284f5311674219235972ecdaeb1efc74feed50d3086f38719aab3991c3c6cf5211741519ab863f6b4d5f4299e7e832a52b30962c677baeccd79cc823cd155146d08eb699d7e6bf7a089cd5a1eae84708144c64578d20102b1010d7f4a42ac0a21404b02243a01965efe8371569cf7f59c1bc894a2b59f7a30240dfa8157f02e3af0effee7b2b03097f7e0a3689abc8b5a3414c921155d29ebeb873693092e0de1f15ea5f123ea24dedd0d159041f65db5867bee85d56f9248ff4b0f57a5caab5461fbe5ecc1b9b524413685db363589780e7daf644d28ee37f906f38fc4336b4ec0a1b7ac4030f6ab9ae35ba7cda6c672dc764e3edbb60e9d1be58bc870f02bc4c159dd16cb5eb713bd132de1f23095553e66f028e23b0ab0d6fd0193fd3490b0e402287e32419e7cb668d689d25d1bc3c6d0dd9bdcd841c087d8338a7f4b12aaf06c61e799808b5a80f79777b65e7b93b0ef3b66d2a6aef856777ab3c020cc8dae28b6aa6020ed4de4cefad58646e4ac0039b6f0343469ddcd1681462cdc593000ed0867d67a46cc718c773a4f652bc4cfe09d6759737ea995235285b5e7723ba9dc52ca15faa63cd342999dc060f6323001778535acab5afa40562dfa4894a71bd5ffd982136e9844c7955ce9792926896b4344941556a26edc827ec14519845e723ab0a3717efd5c7df22915a050f4959bd4923116b6f13c9ce68f87028926d5fe03a4376b690e63d00634b9b380ee79b2c7ac66a063a4f7fc086be5e874c61e0ba692cf0b1e16302ba1ae2bee104630a9c43c4207b7e99484cb4dec0915783306f9a08049d41cb2eb2ba61ece541598f02df88062c75bfc9064c4da8f3c87cbe707324349625404a352275daecfc314927fa5cede5d018b18e75dd5cabe6041c6874a8c48cffd838d4302ae0fd29be986bffc7b9f450bfbcc1e64134a13dd75001d42062870067017fb05469696e7071e45d1628fbd024e867f53c1a5910fd0bd8c95ea23ae11c2f81626bfce1fcb3070024762417b450aab1f82764f8ac01ca74e552882862a3f897b0b1d3c7a44a00a7b48e73c4e9aa35a9f5b39ed7600b76b5a7f8c57c742ef7712d3bfae7c0377ae182ecd6e8d4e5de5672fde51d42413d1bfbd573f1344a213bdc896f70c8c84709b70e291a7c38b4bb23c49751d7b429d33b0e164982313aa64d236344af68476629c4d127b071a44844e1ec6dd6a0a1bf91ae437db1ed61deeba9922ea642827d0e32321d3163ca075d782bc311028620cdb5de10b8e4d45e1b62abdbe853ad875c157f34c8b67bf48e2e651ef3284a7e6bb356b72849a744d7869033106c2e735d3d3508403bd52c010c16a41b579d0b822e808e0c583f4f4ae0452a78686eccafd5664b524ca77548f28ea33cde7006ff27ef0db2840ecb521fd2198668e6885f4c5719846a7acd9803ae99246e15e4e79cd8db39a1b11b5322c92ea6cc49dfa2f2b1674a0f35ac9f5e63b8a0452cbf80041406cc969dbdd3985daa9cf92ecb216ce5a81d9ef0cba09e673134d1e33e8c5267b35e1358ce2e49696f40af33f4b5346096bdd3c50c9123431dd1339df1771465ce71ab145e6cd0d4486549e53330d74a4651c0869382df9951359a6be34672d310d29c88a6508f11e073980935199953a71160af7e2a5c751848ebf8124e3f623f59dade4f1b947b06965c7f4d6b79b61813fcde5e8a27569f8397c3fbdf70ed4b576486b44e9e4a1a385f4e0593888c4b1afe3a03d778e2a7b4bdf70857dc55c6f03504aa56569ac66f9a9f8049eef5db0354c167423ec69359ebcc80f2df28420a17a6690be3cb9fc58a99501b4d043746a29a8eecfd1c9276d2a53eea1a8468715697b081f2c8eb51c66193aa7f81739e89be6be28c45f092000c912bc22918440a6ef3e798b74075d18ccf2b9dbc055a4498012801edfbc09ffbe69baf4d0416f247685d0a3f94766dd48cf45653559e7c7e5a261af00146e4bfc7143735e534fcb52d0ad1bec84206643fa5f3fe6028cade8f5c8cfabcc46faf22fa574ee86737144a5cbe668cf585375438e6c32d0b1ea19643c12da7423e895fe7bae83c5d10ef0b21c06493eb8931850a4343a9e504b709e2472b39ae4f9e371d20b44b38b073fe8dad5c9d310da7424d7e8be2eab5db1d6ab85cc8dfdf1741200e54c67179fea4075d9a9c7a4f8455069f059bb39d020c22432da124a9b823852165e79cacb30f6473e8afcf8be954e5c1a2894a2c68cb9ba8f5458cae4d17bc89502296aade703cb8493029f15c27ca849215155e7e5921ecd46ac42052507e99af3e0e2f8e4747b98f3339cbbff1f0c457b151c3103456357ecf43c97a0ad8883387ad6d550fe7be8588096dbc7482d49df3c71a68d4feb604ba1038e7be6d37efa4cb9736f2c7b2e84acf9c90bae2f6b490ccd7f682ad6cbd19586205235d03f7545d2b36a04e84c9d61c835ac5a58aea009809b46481f1c79c79a4583be8c0df45da1900dc60d5686bc0f1e55d053ab1ed27b5f036933eb20e24bbb6e9d580eb3f1920e62d26bfba1b8141788b6606e5232ed1f1d356b10965e5714a1840894ecd0748eea628c33608b7a611769dd776143aaa8641f6a5196a7c031eb1fa88fd71f81fe2c79393cef967c5d391d6bb4e0bbaa4574b1b0803ab045256bd37a1d55a82401560b221eca45fc1b32d073cc941c13af82170bb64210d45c1d824a9b10b42304a1bf411e113d9631537283fa843acdec8d9fe1b4947e9681ef5e8af04551824a544eb097c5727b11e0a88e3466aac3df86ce73d65b65dba8689b9f9082b343dc7290b96806a059477fc88c7ac81ba5fa155b91a14a81b7c177812810d1bc075e79b38cd0de2b3ed5508811effd043ac73479e1c7bb43d47270de84d0fefe0b6ee706a219b588404901cde4aa35280180834a662292ea4e635147568b88260c98ee610c5d54797b9375d4c8b5677e0d0b5e4bc4517a1d43b242b8c87352b78cef161aaba0c8622b08323398db9eb18c5357d969321ea04cb6e90636b94b1bab3e76f5b77370708b8748cb291f30408c91ef13424894e1f2e76c3fe4c3a152ef732f082e17d1c1f7ff118299d491038e8ea66f87e00f87eb443050eb8dd41b05adba3ce23757a8a9216fe03598b3d14320b7ee49a978334ccaaa0b46501b5d53c7a30a305243ab6d4c5da81e41ff554dc8620ca0e1de522c4b8895e272effde43973649b097b1cdb206eb57a7bb976344d13a9c5413cb83fc99299770265c988d5787b93603652d303723477363853bee3ebf3cb69a5b9b2ccbd466b5c8207d0b072d75ec6291a8b4fe59e3c138bd1f18614d46f1d73a0055c5b914c643ba73bcbde0a89e324b989050e3c20102cfae9b0172f9cfc3d159847ee34ae7e19f69200ce3ce0fb376c172e49a5cd4000a06ee4ce868c84a76d538946a0812671f518e6b32c49a05b1a38518c63b1ec69a82cad14298e39d0c11ad41ee6c428871cf86b1c66072b011e471c786496b90b99a08631cc6a15c3b98397046961a0bc344ba893c2ab63adad5e0a180d401a1139d6b3dcd81ea8b7a0e977360ab17879d735560476806e28f05d776fd9a53c8050ba91a64d865f5fa41c90a41f903d9b4027cef16d21e414674b0f08510c8697a2f69d20cc18c5c438c5587e1dcacbdbc952242d0021cdfb35b29a0e9ce97a18d1cd927d3f8ffbc406141e802c5196f06d1b10c07be45448a90e79d2ec7d0b2960687133bc97be6938ea56217946b898ab16964a99bdf69f06faf636551ba557f654bfa1192222124cc7b66922b609bd44b3380c72c983a55508ae853a82c5da6a6aa389f771e1752abef4a7e83e9292d19af471e99bd6fb1dadceeeb1e374acf57737821423c60e9f9bf881f7063178d353848c7a8ba2f6ae096ddc17e50cbccf9c8308f8e0f967627b2c013a4d687d4ae7dc192f4f2b0245121067c67b43f41d2f21dbaeeb4c74fb11f52534c5295493ee17e58f64595b66b4e1ad3ed464b540333ec33559f721682d1080dca7729ccecbf3c6c089c161ad5b793057f1863b28f407308f433b210c57740f3e430c63442a1a4fad210b29f9bfc83d9174c84649041227cd644f2652e1c1236e88573567fba269700edac9a452c4babd3b494099f23f18413de25ab1f468d06e75ca2eed4bf0236be6c6e4d6f04b1e714b34adda427dad5d642d1ae5c48edfbf4888fa8bc0f87210892d8adca010b5e0aa1e1750dd34806303ac394f1430a9f4d143ddcf346a7b13bfa5f52361c82fe1a7ed52dc77849b5ad860bc547c1fa07424dba848608fe9f147f1ecaefd759cc1abebd424cee6b98330ed10ccb87166384cd03ff060e654ef4ea7fcf6df6a6566e13642e5939b8aa1a8c4e8c53fec2231feffa0c2e7c6a6ba9084acfdffbe862b982ef25365d1517b03e9a468409128e4aedddc6a51790137b793df1b6fbcbd27208f82dd4690be57e6062b1772a61f502ccd572e0eaa7bca992c31f758de65acd63e78b79619dcd8f5a840723a019958b7828fdad9c84b33a12ba7fd7ad2ba861fab98cdf07f20d8104acb9bc75191d895b34c9b4c1f5cf9c56bfb0d7816fde5e33135aa89e445e5ff9c2b30eaf015fa0e1419886580db51e490a0f9b50423f826b1e032d3d83d2fd0f8ffeedd50650f8bb71ebee0521b6a8259c60681e2f6c32e1c92be19dfe8e8779d538e7dc11560fd8fccc2056400ea343c412b61c592cd9641c521d1016d208b7bf421af809066905c057f5de0762720e601acb3f8373376f17a329c78f029c682cf14dd75d580824d06bd765082509a02456b2cf47a289bb4add18aa608171e69fa1da29eee2c42d046bceb592fb9ea002f4bd4ca6d5c618ab1c502939d42eb9f3ef618be2f69858adf17b080e9907e129f461904a3c9eb2494d47d9e90938194420710b2ddb1b48be0181ed5b2d099a5bba744609ceed9939ff39eff6a14c486c4ccc951a7154071d80792d24b9ae4a9da9cfb9759a4daaa064b6919308ce224937e375f9eef544e9796a621604ec487042c7197088cdd3e7f5b7f971140709c3b7e3f60114a16cbf3688d617992b688b9fd5d87e795d843f875d2f567b454f27b212473d8ef218808b57dca7c6a01d9286cd7ad54f52888b411bb5809505e4c3443dc573d35ad2c23c9f783ad6fa2ceea787a9d5c187bff7b6b590ac56ede50dcdc93ed982fdc516cf7c03d84d7d199fc366810a9bce0ae67ca8c9c81a4f8d4d3a65a5a946adda1a1aab0e10fe5f5b03612d3018a2a25c406cbf8c2465de332d816dcff18003c93a668fcb714c02591986f7b33d8838a7181469702d456cf5891403cefe0df4876c0dfe86bbd866f8028e9a2a094cdbaee704fc7547f0823299ccc5258c1771c02fe9a0e6d7ff4ba58f0d9b8d851b2441d0e3c089115e3c0b65c7a367a3e9afa9611d17714481ab5a56260318447672e5ce06ba58afc44c628c99d747cc21a590794de69b69268448ab9d38d5125d95db75af62286c922e014c22280daf6ae7f34b9976539a74e0fca294501703f21dd5d3cd18382d8a30cff5953d99b80ad050a5d2f1daf61b9c178e374d1940fd2e9f3ac0714156b970dae85d295a5eaff81aa80f5fa35f14f875d79f43bd9ef3f1ab389688800070997f830ebe04240a055c17207bad9564f587a814909fbd7d80c5f31ba5ca154f55b2913ae05d74a5e3d6a2444004d183d5af56f6b7b25d41a713ffb4971d21284481f492410a60c9f39969b8f93e6090c189700c4a1a50dc84fd1a08e81027214ade42750e7ed32771667ba3ac9ff1ae8458dd1a9296eb21f78cbe6ce3cac8c10e32173f0c0251b9484c8e89668e24976d6364c182b54aa6c16809ad6b4c9e33b6873802453a39c617e4da1a71e698a1906b26c3d1016b20f3655c6bab5f1c80414a327ffd5712facb0f3c75fc5085f66225536038307f8c2c4252fb01870fc78d2200ed2e3a3dfe9e9b041625c0b71adc9b20126951099399e1a18969138f064fe2564a483c5ef2b4a1208b71ee0e46f01d4789438a4fcce813fc04ec596341ef64cb9d2e4071c0e66ff2a2bde1132ed62021b51d94fb6f241bd35b065f133c5f83b7f2cbaaa612cbffcb60511e685ef3783292f3ce5a1c11518929532631132b0d1a29e7e49a8fe1eb30c493f25ef05c096959d54d4a73a63827f8ad7ff1ba5e0764d28ca4ab003645fa5b07a53e76227bb517a8002cbf8e50dc353ee25ed126617336115cb723baba81e9242006e598ddc957504a35f6f0a948ad9c3b64e9c0b8380dae0f82d51f73c8af822f4e4031590addf40a6cd8017a82d95359508a2cd51cd4c6c9278e218716747258403bdf9f9f64a31ed52b00b33eaa07f6c2fa89d453050f63e21c8d2eea320395064100f0180d0000000008861c74686bd4bd3f6fa5042a51ac5bc98d33de31763711112152e4962277b405af05aa04371ec6536739cbb29605aec753b50cc920a5594b085317b141d4f50105db1423375e105d89112d71a2612e8b16109d0bb1caafe4945a6badb7b24f2473f6debb8630fb44921caf6e00d13ce7546698932a4bb24a6badf91a06380c908a52024b95ae29503008c285cb891b5bbeaa22c0c853843a8b37042b914b31a32893b160752c5d6a071354e4988c15099a25e15c62b40885c05a52050b0b39250df7451eb7109a31012cba34e90032212d9100929525258ab48cb6709923af0ba5cd44ef0a23aac6a6c690c6a608d140595c8c1447aa2c21eb02e1e89a0535c8429c736e42987d22e73bfa4899a37b927912923c75ab3d95d7eb11c49ccbe82871cb5ebfbb9286acb278a51d718b92b1820bd92f68068249e80cbd1898170c778e5a8b536baca277d86e71561de044baa57463489d19e1e2a16b868727aca7235b9eb850445d2318e29113dfc18d95e5dea605175a6bfd43c937331f4c6f603d9e8469fe63e6cb8d450961d817b2db426badb54631502d25b1c83103ce838c9029ab1e53183c359122b3773cac69adb54649924b6b7d1271d39199db3ad1ac9810b931cc608acbd775e505090d9e4220cddc206613f11445ac4491ab0e2794846d46968c70e11882702fb8d1522a7594645ac203905c4b9c738e06d468418d26d41ac8d332a7925c9c73ced73827b9c436309f70403be7a12fae93814dfa8ee93bfb9cbe5adcf89203041855082556535e4765545959516c97c5b622be05ade6dc508ecdd066320c97b4952d39482eadb5e6224449adb5d65a6b0d93e46ef1aba7aff4ec4155a8ccc842897b8b9af224a40d4dc611dc111d62716857458e02b0829c22976684ccca392380c906505c5a59b478405a97c4bcb8906dadad5d15640feaac43ad611525bb96386a814ceac84f4887436afcfada393de444decde8456bada5b24fd45aeb1db02b5619f418220a82d35a29909c4abc695145d1a0bdccba3640d72966da96585870912d4038b5947a58ca18f689c3328314b41e4a6aadb5d6bb6986294d5bca90e6a46a6d9246ba51e462e30eb1db46d0180a3242085c1107a240c93115250a598bd14a4d1157eafc4704f098c4b851e5cdcb4990589b87163da24e6451fa9d4029cc4ba4b270f32addd2536badd5230f780c7391311d6aa14b88afce500b429c46275b128cbce042c4891230af200fd0c83281e2060bac1a1fa6fabe40ce391f6acd39d781c98c9a4d872a8c84931811312f1500ba2176e40356dbd552df4337cb57d9b38ca943677c6ca18a318951b64eb91e47b8c895a1f87116ecfbcc8584b3ac3934a196a8267157489f5558c4168abbe5426badbfb24f24679818f448bd9aceb44860998bf2c2818f2745866d504d5c377c17c5de7b9323d788364c3361f28674406af25643cc040a2d1d13a844f16224c90c0f1bdc1303e0ea994a44081664022e0a921e105a303410b14528d4e21ebcca6b02cd0049483576dce842925663e4c2c3706e0712b0847066c812066fc02a2c613f62b4bea0d831b643480cae221f891a293721d2d19b198f195d8658741971c5c59817b22ad38a8c0b009dff9e04bd900c6744722493485d3c967d895f19484ad4984b8c4d4034ac58bce032923225891557901188a6205d57ee88db25c5af0c284a4490d12811266ac9b25584c30aea448e2e1e57c4d2de7bb305b34f24c9710b90beb7606d09f68c025a54116d80d564c690046001124246902126b8aa9a3608b186e58dc9cdea8b88ab1bb01b545340265f55479820dc20b3c7a1696de69d632cef1c5e0fc9182e737a902dab2f23dac6f0475388ae214a529819fd18a95203aec95c9a8c0b42a34965adb5d6239c4648a9284964430a09b02fc0ababab9f94142a5d6d349a049d48d2f0416b4d6291d05aeb095d6badf590a74950935b171d5f8ab07030da8005d88e2b4d6cd74384240dc6809482441a640d21914989f10ba115b5e6bb404c3bfea16c94e2356e2be3cc5600c7115c22554149d6932d59597a4009d281b62c28403956600dd142aaf205c6abd425590f3ca80ce1b042b2c6c5c477c513338968404042b8e38497a823271b8a20e150e1042b0c88cb8795124fd25a6b2a4e65a9d3b6bad65a670d09eaa25a02c407908e28b01f28a8b01c7999d29054a6e1a8e23c9b3e9a84f838d20445da4f0d6a48b464ea45972c0d9c73f25291731c01202ef03bb3a2b286e5618371abc599d0cad987221f5f825c262773009c0a9fc34d4c455a8aa8485ba1826cc7579c980c10e50552e5ea80b164092e05d7972c1b398678fd483204cafd01838c0a0201929952957d22c953492edf9614711f9c6819425192011652721eb24e68192952e663096f146e0cfbc4211c9ada1bc5bc252ae724b9c2b65b95db96f85b7252c92af88bc4f2030b16962e9eaa4506ccfe11d3e49679ea1c952371426048433d2860e42b4890998c2767409c462b20505341821b4cee4bedfdf6e29c733527ec13d5a448150237e0588890e1e2aa484db735006a05d04510105da634f01e7c8d2cbbd0c8ac90384b2104c611d16d0a2ccee8298ad25097d18dd4de7b93239212f3e4a4d020b7b17443854c708c6c37b9994122317e4d5ede664c41c2e636222908902d54416e1453d90b0724ef1af26e78bd9baa9b2f77849cd49aa8010717ec14a4f22ef4cae984c939e75cd54dbe0d586d890a538a9a3285f4854990880fa7b42525245c1e5a6badb5862aa5ec9992f32267ce0524a22918406ca400ebc0e34993d80a28b032332d38740b48ddc18742b1382d7241293090226c069b93164b34b460c8683d3d80e24d0b5077d07602caf9b9d8bf19397f69ad032467b9f2871073cec925546e46a5dbf0c72b4d4e84a04823e2a1a109181298d1d987282155daae0e0a7a5fe1de572b7ea39421101090882df24b1deb0d1f0d47c305559bc21994d3a4da23d368449036058332234ca6ca62acd8b06409657975195932f364138506649cda1aab9ccc1e175d4f408890646d6100945cd205422530b610c17273286528c21c4090d2d0a86c65192271f242475a1290b2ebc45b227b812aed256613226a5290825f9cd03acc28b205b8620957d50f29122e0e9c6dba691b4099a60d480cd35a82db62535203469a74c5c55397361249ee0d02909a6d6c5ccad1b58f1e5d975d05a5f60e1ee231c1cc99d9a7b5661be3dcb04f8c9323559473cef9da6b1c5843d298a973fb50a821c7988828566647538a0c31811a9ff48d01545993b9a0141b2a57c138e75cce29fb44724fe9fcd6e09c935ce35826d45d50c55e59c1d5508abae00ac293242baa0cba8a5435ddadb4997ee618622b8adc50178da1383518bf24fd6b4a834c2b84aa30a1c5dd0c4a33333333b3b33371c417e79caf01d7826bc29e1964d4c03476041535d818d31b8c9d9c73ceb994d61c65915de41927b7c6d70d1839a7b7c5e0af2c9b7c2849b192624b6898648fc1741091b436d6a405c70e104ca3909c8e0865b8a4b590058439356585a5a022b230e48406dbdc5395131af438b4c197b4d61a051a4a6aadf51b08664075d9a1f5c16ce8c6891145ba700cd980413a60517a954a157221e88262ca1a0f284d563acb0059544a634428b27cdc24ad35b964efbdbdccc019a071ce493327c04d3e41d35bfbd81ac7225ff98814f9f553ad96747cb8896991003e90c5cbd3970624c2989aa00d15e1028503c88bc6d2cdb682ddb24f24bd4c952f9158994ce562dc6dc416a2b5d6426328a9254c6bbd4de87e06fcb1256840b60032b2d2398a9b988f2daa2b222a5d566c3d9921a7e3458caaa6b3b3920d691c8aa0f264d12c2e460f293b9e0851f1d4c012c4a5048750b835f6358373ceb7b24fe49c73cec7b42bde100d63caad840c9dcf469ef834369639da0a286bc839aa83b65b1bcbb036e4a4d9022474c8800b4494539915165059aa3c510ad1b61435e343cd95c1246a25e9a0b2838d089136116c8bc9b02d48d9063562947b97d194596288ecbdf7c6b0f7dedb0d080868c4de7bbf8d619f3824d154b7de9bdc03e1747de5877da2c6d033e035ce39e79b0bed774d4e18259108490d875af00114d8bcf9529065be7d986f717a2b0ed3b5e67b34332ff330796dd50e1564f3335ace727df716f42bdedc8bf14d8a6f32ff5d8e90b22deebb8fc0b6b8f3b58eef5f76d75e7c4dd687ffee2fc6acd93b54803fdf9cb39ccf7d0b3ace7c7f9a3fcd6ed8bcc96a7727fdcddf34d936af61f34d33ebacb3e6ac0ffe9b654c996ff1d92649f345f182699a6b7a7bdf9234dfaa33d9a777f833e78f591b08bfdf7c0af9020512fec1bf83f57536cfc389b98fd9f601f7f130a3dd1dfc1967c0588220b5cd6d5eebe6d7acbbb467f798f5c15ff3ba9df91444f896ec362fe20b36bfb2ff6cabbe04b625717e09664ef3ee510a19a312d8169fe91c64fcfb1f9ce906743caa593184d50218473f5f3180bf158797cd6a38a3f5fb3ceda9c17fb3a27fd77d7ecd2a769bb5fdde8ac31acce221c6d91728649ffe076c4bf6d89674c0b62a0ddbaa3ab6edcdb06d8fc7b62fc3b6df63d81607866d71544ea6eabdf77235aba59fbfc5672d5eeb986d39a0e7eef1a38f75dfb13ee9f337d16b9aa689a62c49aa6a4fbc208a59ed4777f229e40beceba7401c13c0b7cf7edb03c0b76a005e00ec05669ffef603409200505556ccbeb697d55a7cd6392b4190faa3fbfdf647f7f4f7a3bb0be49fd5b2da9db01f7dce9ef57d81bd5eab762dfa7ef410fdac86a2798d020b88437198d5ee8d15a7fe5dbcfa3a4b22ec14ba7885c4ad7f85f50274f1ea48968a8e76dd6314d8c730b26ea18b576148fd0a0caee374f10aacab9fe20454f12a2ca89ffd9ec3ecd4b5fa62f9e78b5de4fc1caab91d7b665b0e6c7b75e904f7d3b407bd62375fbcf7cf21d631ff3e9a6eb8791dd30d2c19cc2bfa679a73fe49571f1dfe1dc629fed4cc3b5e7b6726e0b12deeba9f615bdcd373d8cd3ff3b7d7bc37b33ee9ebfeeadeee0e1558d0ef9b2c36db11f48edb73adb7fb571ffc16f4bbb6b99b3fa60fc83de57c15cdf3acb73f7803b9b777821677acb3cb3fb8a7cf39db9ec3be9f7396739eb23ef7f1f3f4cf8bc67c7b7ffbf94db5ce7cccde359bac8dcc65db07e0bf7faff927d9ddb96f660982d41fdc6ffb83db73d8f9a3eb2eedd1fd657deea3afe33059c8bb9806ff4c9af6e0f73159dda398260b4d7ccdfb332c2f0b4f1488ae77c54199151f6121c6d1afa3e255bc13da93ace3e7690ffaf9f77a7fb7f2c7ebfe0c398b3d66457b8b8328a9aa3af2af7aaf8ef521df7cdd9f183dcbee8e389a2fe66fef6fff5923010854f7b84602102a07b4a3ac8ff9fc53dda37fde5402fc75e92a0153fdf492af63dbb1abac4df9fbf9af4571305f20b34fbfc638e66f914bbf9ffeeedbfbeb9c0307b4a78feeb4e7be4fcab6b79b13a0fd8a5c7acb41e4d2cddfb1edfdf5fded0576fd64fafab26d073a74f375cf53b6e58076f351b61dcbee8ed8f98fe9067e0efb6aae38788471f493ab8edae2e003ceced9b3ec4ed86c768ab9345b6395fae666925c403a189b060fc3c939a7b98043a34a15545b0db9a11e2b19312e78cba2c523aeef3b046f69bd7574ceb9ca4b5487ad40eec30826434044d828a9861a0c9991015d79210900d6b375cd01811e5e7051d2b1446e2dca1a932a38d82b84298dcb2ddc4aad758ede9c735e632b0b915373b4d61a6b8dec58675b6d02044384a436411bcb1cb25faf4984b2b71d4131610c2842e690043da6fdd1e8eb2e6b7e8a2fbffb634af1798cee0e3651076cdbbb976d7fb48e6d498ea61b509e2e1e23fe33fcf3aa7e611cfe3aeae6b8fccd13ec89f9f3cb157f86cc5b772fb3a61f13c3bfd5529dbff9952fa8bfece3df92ad6a9a5f30ff37ed21fbeef96abaa9bf7c415dca3efd3d936d7baa8ad91fccaae33c46473def937cfcedbd240cc963c4d3d674ebfad5a57c414b659ffe5e4f5597ba7e1e237dd4eff3d7a59ce43ad8dbb1f31d077dd44d2dc56394b23c465a4a4b712dd5358fd14e37a03c47ea96da8571b4baa56ef118dd097b66487321b8f130aec2c05167a67b6ffde9ea83f7b9b7dee98955cd5f6360bf67fa699a9efbcfc798436f974e705f97f6ec3efd11eb887dbf1006629dfdf7d374c34d37deebee49990e15a427527cd47d78584dd3c7f8874fdc1dfc1cabe83b459fc7b1908a310de73d0d8a817dcfb02d49c2b0ad4ab2b7f3d0e76d1c01de666d605e7ffa986d2f867ce1636430fe619df4f7e3139b73fd2d06f694f7974579e9d38c3329e6cf59de9b180bf1d29832dd70e2aadd8a8530c0ac3ce89a0163217ca2e327d39edd7d753d1fafdafc11c5429aa729dbeaf4ee58fccb17d2e724dbeb3ee46ff275e8eb5e4c23a0636dcad77f1ffd7c0143f68dcfd9564d592c7477d65eaf3f893ec730e216431f5fecf1c742e89f8f7f5868a71b4ea089818763c08085ee04cd8aa3cc95593b6fc6ca2acc47d3b4cd2a4852679d71ce69dae3e0f3599455a0e986cc4fbc3a78714606e6cf736dd5dea1023ccc407eb659d8db1f8c571fdd5bd0af48de9b6b3e7312e7a2bbc33f3f38cf1eee65153dcfc8c0b0fcdb9cefb33ee4eb32cbf39ff94f613ecf35677c3e0e667ebe88751e6c6cf3ad2eabe8fbc9ac79d63c6b9e9e36ec0fee29eb93ff3eff56c7fe8822e73cb3692fff59c3336b5393c1e6e62164b6cdfccfcfc0bb93ffcc39b7115474fe0ef0f999f5c9af3b39848fb9791e4dfefdfc8cc0729e9eeca3e7ebfcfc7c08ec0d7bb2366c9a662cdd9a8bf29aa1666500b93a207f2b561565a083fc0fd6f47bebeeefbad177b03ac89f7dfa695852ed31ac4a96afaa97647dca3f3f1791e425337beabefdd1e9eb589f53774f129fea7396b5313f5fc8907dfadbcf4577e7fcfd774d2f99fe6e15310ec9595d06febd1eff56edfcfcfde4e7a2f3756c2eba13f6a78f87e9e7a2f4c774439a558c6b068ca3df5c19505555d4a1e63a30c35b3ccbd13b9d599665a9ff07fbec922c4b8c35c9fa5c71ebd7e69fdfa29fa66c9bd3f59f24fb83bbcf6d5196fc936575f9e5df11e38cdd7c0a77a734734ab6bd6a3ac10ee3dfe5532877851cac536eb8eb986e284554ed1d2ac01b3f05bc5bb1aa974fae3ee756f72e9f7c0a77673f89a1b7325f6295f521ff547fe32f778ccac6b02dcee9e5b732ecf9e49fe43ecbdf64c9dac4bc7e73effde553b83bf74b36a7b732afbfcde9e6a3bab4079f9f9a5f41b7322cc668dab3d309d0bebfc2ddd9d84c3798e2598c8e9aff52e841bf7cbd9a7bd5b7c2ddd1ed569bbeffbe4ec7da90afdfac7077f6ebaf49d78d9e15de34bf8299338e9bad702768f5ee54ecee7894d7ae10351f7d11eba0f7d73550f669b59ba63d356ffe8875b2d8876b68ba21920fa8b40c590b62ce18c51100000001b31700002800088683b234d10335f614000a45ba90a498501a8b47426138180682c100502000410000401080611086623196ee706b24bb86af8cb951f749e57dbd23f830dc0547252728c144e4aff9753cbeb4de4f851d4f0661944eba0797013593b8d0641e5cfef5f89cbda4b1d905624a450bb6e927a63fad4b85ad3dcc4544420e2864eea051610dc2d551afb24cb17e03b471e0f2662ad6c2c26d41e922297d75f1456e6963d95e63572a676ad6f8f60c8d9dbb205ba78fd6cf2e8a6c2737141d1c80b00c9d77eb1a94988cb8926dc1555ce6304ebe95069bb3431c29e66b6f7591b9948ecc42599464e86bc15de05b9109530399d9d9e1dad18c24b3261ed66133f9e78accec9d025c3788cb18b357c8f5b9f8792a1e2196bbfa8afa4eb2667ee4b050b01530194590eb029296fd6f553d09aa49e1e54f525e2d05c7f79ad572edc19a7c89701756138ce2e021da0c2f2ee11432e0356edb228dd6ae3fe2b322977529723b786be1fd4542129eb4cef15cc3d5b175a643a5c80a2611fb2cb3dfd6cde077c4b8317836e520b330c84e606fc1d354e21e90abbe792bd43ea6a3eebbfe50221cd287ebb8461ea524a5755a0920e9a2a0c5997a4b3f301af1410aba3a6987972599fe894ad74872bd5cff0c7997027a184b3ecd0f5ced8ae403823d001975000b7c84e4224c38bf5839095941d5ab20c146fe1067fcac790035bd52c8738d6bfe3ac7cacac2a624fde8ce203756c66b0badeca1f04a04f40ed619cb8b4920fd16447d203e7982bc0ce05c881063e6eb78bd88e05793e5f74835726607bf24264617fc0e46f3770c2f8215370e9eea7a0038fe09a21c7d2f362f24142b0ebea63a8ecf686e9ccadb95e7a7e4ec6f54a3b40e2851dd1af908e0664d5e69bc1e3dd0c3e83f29cba773a13c4c24a3804d2af3364af28bfaf17b30c8daade85fd6644b20587500abf369d7cd4c6b87d4592225c411f5f9ca14a72304b53a5758e9c07449319db1af7c0b5a9065df98b42acb2a0f7a3a3db607fddd1bf4d6f343c6d46be22112a9feb21588858a6f492c46a98c315b6a4b75bef4329955a22c9ce4da732e6cc4e8df314b9f8c919a96228c988ce5f4eb8d7832c4dbefffc0e9e7e7a4a0528b825da28a7cfc6427bbf5b9136e0c49520da85996387fdc6c0e1969ac24ff12e0af8863206a47b665cf7f88ac15e8827ad3104ed7d58fd660d864770e72c281c7aac92ce4881cc0276989a66bcbe9a499c081ffa81e7aa809a4940622e132dc346a86ffbbff6b1c08908527dec3ecd8a03256c277e2c4e14e4c586d484419d45788d48470acbd97a346d1877912bf1ca06e2d3c7f11548212a5232b45f46eee012219e04c2256b98f54db11aae2783d40246f86a8866a7105c5787d7e789f3a32c79463a21c7e96138a2b456d4736bd84d09c1675ce6b5e46b8633efe26a73c2d7a2d8a6ecd7fcb11869af7e948da6a3c7eae4247d3430dd5c961a2abfda960f43792da51f71c720ceade781ef5cd3281eb4cacbb14530989f3b5efa227e636a6175b59da01c7a8f399caee74b87fa8e485fbd3c6dc2ed3417745a588b7c3c8c86e0d1d9a21a83816d8608b0a2bb82c0a583d462185f32a40ca9659ac42a860a2b70cb44fba35c09a38b3ad8e6cf4ecfc041876a1429bbcc5e510c3060476150aafbafd01e4c32956896bf2b30a21228c7bc8bc74124739dc59860e06e96aeb3b2b89993cbf213f123a33eb486b073087683758b01b17f209610703fd70b8330939ee00c9157929c63d60944d14596ee0d7d9acf28a5699c7d8605fded0898c236213c19cc656fbc9d46ca2074f55dfeaf83f74e5162a69d5f54e639574a89100b3782850dbc47ad8e98d6f28d2814900a74680358b763b8a7f6bf8400eca16769ee5da0d0cbc6d2edbcd6cf13edc07df0c6e90606f21c609f034cbdf022ad220fa1518a87b48c16659edb8c68b19470c00cfae6c075275059944211fbbbd7bed3ea5ad8795d178263748ecf521723780dccfe47a3f3532b6eeb8c2fdd4a8413ff868a41418fc8f998a4a886172be22896040875832ccf48b2a0ce46a95cdbb921f9d20c1caab5eca025510e60d5afc3619c445db4eca630c8a1d737bdafaed6c0544a13ee2b1ef6f9082b7ab30be0d0e6edc0e5c66c7ce27ed7cdc894ca1a4a0f73e79005ee09545ab976673d369bc4a7b817da3901929c01a2f3676d33d3558a59da5467121ffc89dfa53e20c7458be408ff96923baffa33641b84f0555e484f0420db5a20b7be5e6a928a0d3e68998e9230f96a9abdd800bc19f6ad7765bfe9558196ce5e103c3081a0f856539561e7b51a4254e3030686c5d3c5492c04cd0e0c007fd5191a8b12dd8bee11bf838aaccd54637821741eaf090cc39b14acc52ca6c46d1451d17c1139d1ea665c254572e4e33ce513bd9e389d072a7f9bb0ee4966a7292c4813fe67b57fa8b99fc49681a36b0001703003c3f3405de0e2c776da18b033484ae5d10ac8d9125082e40a1073bbf0c28bf7e4a146979a913f54e53e6e2de007e57a516bf264ccdc5106dd488bb20be7e54a6ba47c898858f26d347fa98a1d581955c5d8cb518adeb093d24167915624649d41c6b25e3bcced7e62ec93ca7340366731beba6e9a3bcf59dddf09cbb2cfeaa29850253b8a7a57953ba7e536664a67c63726f78fff27c7a69c10b0584799b96eb1f91fa5c811090be48c1ef4ab231e60805b882affaa2532824075ccecec9d1602ff4c8a5cf819a47440f6b575b2aa7244bc1d4fcc74d7e14ebc26332ef9ca035feda29d5903f1ebd2dbc233d29a97c86beceea16cb26c5253bd9486c79926d570291e73ab7e64f22ad594239751f4f18f32818612681399ed0a147b4c393db0de22d912cbd9ba4600fadd5c4f3761b46115358f5289549b76611cd40b9227bf236cd7b2b5f909dd5df7fbed39830ea4087ecfb6570f9d76aba35f6479cc3cc060924d857d67090c5a1d1d60ecae6b2643766dd6cc2bcb96a8e5985c8149edff7114f0c4dd02027ccb6f2eec561124abede3ce73b9c44c7d74a00dd59e1606ade37b7d1ebd976da49eb022a9cd8028712a2d20ae14fef6744f47f457dd2de16b4d249960a8a6ca914646de9d41f1e9e651e0a766727c6dbad1c7dfd8f8865a3b539f14c857de4f12309606f73e05c333eebc75debf17b23fa3fdbb4e8f733bf3ac2ac17925c95ac7d7a4c18024e8f8dbbe23cb78a9e3d30dee3eac87ef915ff1ce40040e2bf218cd009c204a2ebc9963066b40034c43fb457a81cbd467472e502a71777890b619f6ccf189952ae552f859083e6214e6a69c891fa9ffce49d6fd9b8771d186e70a2536433ee9758fc6fd5194b8eda1f7d650e36cfb823bcfa7a0653c5799e55da2224ae1fd97050bfeceba57748e9e04c496b9f09854cd1e8c8fdf2ea306c2446648a2a8f9922ce97a3b570eac6b0078f1ea9163c01bedd094b041a2de827a54d8271ba6827597983bd9be5f2d7bbe8604ccc5f716fb12910ca2ff4bea038c6b2be5637543c3e91e7c854620d4d970bbc4e473fcd91bc547535fe54526a4783c00243add16b1e990a2732d1e63aaa7a08b9609868f9c6aa5fa2a32c46b3e67fa27601e226a2fb9e915321423ea267773c74b2e9ae9b35bae0d75db2f9d5f2e9644b88aef0b4808dcb2a7e5edf1d6d3bbdbbca613126dbf6ea46fd44c63b0fc093ce1897049d3c379161a7a14fa63d91c0be9b5ed818844c8ed9dc81c4b49a6aef670601ec19e2411278e98270d19cb9de188132cd4cd7e9ee6c4e44ed693417ad89c56785e6eeef745497c638bbf91b470b4aefa750263d1141d87aa9f135e1aecc542a712a1f3ce9bff76f0ccb684b78889dc3fa7c46e77369ab97363f03c713b12b95cc3955c50640a47d4e67c07f429f66573fc03ec58cee88eeab2d0451121e37a656905761f40c9d4886fc1348d438667a6532ec2584c9953f3028e406444174c45c1003e1ad82345e2b560b09826cdd19fe7ba56bb8b6c2e2901eb7646cd051ad7c7ea3fcddec2db90128194e204ea2423613c9699c949eccddaf77bb2d4d0660c2c37d1d79e8927c46fb2f2b0a7399a83dd224d9b369a09d866c27d17ddad392cee25aab8389a4f1667477fdc382e95195dcd77268f5461e7839a0cd052018aed1d5d394b524a88d252767129ec6b553232d96fd88cd123b5bfabceb7b930abd63a6d14bda2ebafbda6f6814e7a6cc182c7e37396b5eb374f97c208905ec936bb89195814f9346e1b9bb56b1dd69093b68639db412ad17520b259dcd8c1a5323e97842cd68d271bfb18e46c9ebd88459d7c83a951dbcd962b46c7060da7d894e87ca215a37a57551c082c005b7e0e01b670b3eb9c2f5785a625def435a8c25cff04ae4e86a17dd4a77979abfa80f19314065a545c405db15cfe0c90fd5a840f11c2266a0b4c7cc6ebb67475fa55eb6110fb5222539a05b5f79c60cc5a94d7f2e686ab53d73fbc8314bda99afef07cb2ac97c53290d4562bb45cb6e4736f1198d5a8e8f94a0d325a3c589cd54a4c715c2a6276c730a135005faa99f130e6aff54c022d01b2db940e0ef6e21df2c48e0793c5e74a6b63f2e83b1732766aae9db2f0a7b8aedd9d9a8916113673d71ae92a5713dfad49a97c34510191af44361c8d528185f67893ac8d55f9c436efdf2453c47485941d7b5666ded89fa1cc0f9225cab8659d4df833c087e807f17fa1219c1ccaeb8716e9612446a0fa356df19c88843d5d5acc1e7f6abe4568e4cdbaa73c8055e1dbad21f9168b5f35b3577adb3307fbbda4de561c9365252d883baefd6c668271ac024a5a9de2f5bb90a791cd0069582998c8717ceb40a6c391f5a9b7a71aec4dfde39f103ff5d686404a2bf7905f9d4ae820893df0ceb554dab736e3d3d773bcb3bd427d328c38d37d0e770b72afae10209a27367ea7aef37400161e90ccca759b67f573c8b284049391ee4d4d8d1a0640d6a7e966464d4d33a51996c61c3e74d2acda62a9f5dfda4d13982ee48294030fcf2de9563753ffa7dcd80faf464779a99ceb4d0d1d777eb29f0dac749aee6d7b19c2fcbdeabc84e91184505746d18d03cad9b6160e867cc99abcc2c57f93f1b9b1e95692a45a3fc75a7cf580addac1882cf131ab10ee379b7c0e22234ba745a9406bfaae0d1a1e4ee4ce28c072ff25c5a6ed3d87e21157307c82f2ff48361c54a8fe9f9909d50aae91dbacf152baac26314fa0f64e180b880e2b6285b717552cf22b1f56533c1048a6b046fe230d80786e68a70022d0b6427399d9c6c7a3259022aea4a4f6fdc7de3b34afc19d85b98f00407ee57618c6db2a7ba9a00ad52a7d60c171018f7168c47cbfa5e511afc895456f806792aa69fb5d7f857a2ef80283d37e88288184b96b2ae00d3ed4704835c2d0e992b6a24c0c279284f9d480c3f8085e1eddae8f066e414c8df3ddec5ae795972ee11a473a45a1679d5273dd93d20553d06786e2325c25cfd6fd4ab373d448123cdb1521e9c26f3aab260192d9ee1c93c8e021fce4abef1ed762ccee92e71c5e92d77960fb856c479c32c1b0571d9c0560ad0e2324a444bef6eafe3713c0b852ad0ff5bb60dda7de28a6b5f48a5e80bcb3b2a45e9f596084971aaa86ccead4151eb4b7d8cd7005942dd2a4555ad0f17e77b49d0c27403515223918de91a4a268d0707ddea0432eed2e2a9c78d6d6c378dfa9442d904f14d6fb811db28b57da5c09a169a9f7a2316cdded16c2bf674165f021bef0766e5110d6f38315a4254e7ca3026d6865a270f91575107aa606d20805478d2e5dc741f96890d181ed8459210b262eccebf6bb0f2b9b4cc6224637d89ac84157ef0ad74affda6c47ee731655328f3618955a18f1940afac49dcb94d9b746a0c5ad4f86d142dfa9b5906cb21daad96113f3873dd06825443d1624d098e305d687c48bfb6e4e8eda47555a00921dc0218d77b3ac8533afba1123358535697965b6d20eb55665ae29f47b6fbb93893d1e74d3686ee3f0a2de05d11ff27497e7c8c54c8236961d774328ba8ebd09a7c3862ce5495ac1cbb5dbc529481845609eca2cc9784e3da52117d9b6946c925d068fbc92f68ae9de5c3e810d7be50e14d3bfa902b2d771524f44b108c2884602a925983a8791fb28a1077e2cd2626c6d805b53560a26d4d8d28e9f03326fe204c45129ea7bee6bdcaa15717acc744f530f2a0ed3a2248e72d38e4575cb4f8ec33f3467ee6c15104febe44472f940bbf7a3038217c0f35abc0c6fe4d0e64ddc577e4e35887ab8cd97d3a1410c88791da5b8afe0a3bd8570e2b5264401d0e484480ebf15af1d30c5af309bac83f3634a42665f219f543f95225e825d70d7b1fb37a545630a083feeab24b2fcd416516d33888e401c18eb2e058c8e8586ec7450a6fdfcfda03df3046f50ee8874065d32bee7ab04ef527494d46c6da403183f9db292df2e97d95075027eadfd52ea5ce8dfc8e82876097d83434d8de267c5dda69aab532d545a2122bd145eba3e5bb9b331b6c67a1c1975b1bf8682e5dacc282510116a32dd9e226ff23600f34910c7c174c90f591acc9d169c96c4da3f3b7591f2b40c584c7dc429d33455eb34e1d38139335ad408ff8c505767762488b27af431daaf44a08cbfad30820c3c767e402bc4a60be177b0529683a5cc8db34640f6694e8ab01be1d17289544eaf6b7492b2f71454bc38693109b80e8c25fff3fe17a2c76edb557401cc00fcb6c12ddb17504f94d519020b9234c9e02678766c9ef0a25c8151583950a076a957957f66e374349409b49516352a0eaa47fba79c44a2d515922db3dcfcaff86cfa6736f8f0ec2326eb5a0a8bb6dd1796b03ac074a8ed1eb6e1719344e85e8b6c1688532752eb4e5340cb6e1d62002573e4146c3517438ac79155ed6024642f66d47913df842e2765b24b81b5fbf77fd017427d8178408686c34836b93371fbf18a2ac6fad7821b8b1f9dcee8afd090c67274400fd8b482dac61ca6b65e5e76904c9812f3059f9f790bf278e359f2f41338cba4e829617c79d09c862e4c3d720b69b85e50416fd16b377044b8be08bc69766e4b15a60096cca5c88cf046055c84003db853eed7e153c59f5dbcc26e7aaa9b02b6e40475e0c5c05dbe84d025d2c92ed2f0b2b2606565dec9493adea72fbd3eb0d753921e9df8274532cef9307b37b3d51f97128e8440095986a378f3057c7594241fa44183962dbb648bef6d3810470aa897ac3bad3ebed8fb63914550be05b11e9611a8fb3950c8211d3937e96c1432817d802172cf0ca268df4beeaf1b8ecf8d1e906dc41654d8a3e616136079bfa7721d74b81e8fcaba8917716a1956b8239aed39966b668ed378a88014d95e02a268a503b25cb1ea845f159dbddb1e7266ee2158ed48ae019932c83ad258194d8ec953ea4c50d834ad10329cced6789b53fbcc5bd65f29f892c745558e08dcd47a98cb3050aa28c02ff04039904940bf1b032aa76282a571a5d104172d0de2c69b87bd2ee7ce5635dfa892c5c15090f177c8c7138a839d4b5c22c6a3045e06630f6695507904a2f4f58261c0633218b50d409bfa92d85ac726c99311f83e83f7164f238f1e547ac401bd03d39fc214430ba89ae8aff05326e8e4efae50e05dffa8f8229ca26048b83a2d5621c6d80c86722dd4cfea591e2ac1be8f921b4dd0ae8a2d28024cec64a7c38a9a2de045c54226381717eb905b1a42d38ac4c9fd23e2aaa56731f331937c74113a67d367bb37a5dfb95a15f6152b6f81d3e59a19d8de4a1323d4ea21624c1b76c63b5cb3da7b9d1b612135fba3a63cf980d12315639e98508800319b852f805322237a352a00f2ee9cc830bbe01566de5b1bc9fa257bfe4ea993a0066db65c138d3d2d809fe8f68d861be9d5a94751db4799f832be1e58d6c9d64c985fe5776923810adeabb3ec1c005fccc9e64db891a58505cc72924033b92281a41b1b856496e83f7d0fa2d671d36412fb4c8d1d5ac84d27d09b7a477da7f86f6d1085f2604d5a2686adf949697d04f0a3312cbd041438d2b4c58721a3f800bb9261143bdd7807bad5b5387e69da934a4ea8174f98b27305060200b7a669fec51b91639cf2ea55fd216c4666e5c42f6da13dcf01625064b152b2b769eeb48c238fcfa939d2fb9741abd0d88b3db52051ae345e0f814c4b9c8ef3bf9b7429ab6078895d4f2634fdddd4cf7450501cb5689fcd1e266fe5130314b2b652397bbc10d6d9aefd8aeac87ccdb7ebc7eaa1da49356ecf2f4633bc27090bbd76a0838c5bbaf1b4ccec73ae64500000215c2046337250981cc3a3eb91df95a49ef4680cbffc2bb5cab591e6221882a9cca21456bccf97ac3090917c9d7a7902ad12a9ea4e37cf17297c66803e770e5ce82472413226481d0408e8cdf79682cb9a03f2da36f5ae80d17548ea01ca922ae461546c6131689b4f6595ea0493baac51104a9e2b4a52aafd0203e18381d2aa804b99bb45484637f507fb475348c23cca44ad5e5404986aeb4e6dbcdace85bc36112d5c80a2a6180db4ab809b60e47a0a2d47baac2422e156d67293d276eccba21e09fa3f94cac1ae41c5502a18db51e8c5f8ba76b62b799e6d0a2aa406d92b2147acb1cdb366d9425ca3b08384d56147c09d3793677cb0454776ae3e74d85a65eceb4ee4597351e7a694829826daa8fd4cc11cdb3e23d3850f07314637c8c3cec0a66e6ca88e48723d0703e93de7cb3584e97918e23ae2adcaf03dcd26a60ddaa930cba20c96d8dfde951686774ab9145218fcc7376f4d11790468eb0f51b318dd2b5a21219bbb9d6d18cf031886c54fb0bfb63f2274314fb067fa4447a11920c6b9412833071ebe6376e96a7d5fd769df53421702f19e178abe2f9af5b3542c5cfe3d140116010d26844a668b0b9526b124421bc5a4e0ba88d2a5f84aeebcfdd199f7611d253d56e5e5a26c6ceddfac5d0c5d8119999f23307028248c2058389c8c7f36582d5ff4fd07c6e6acc39cd10053dd0c05b250147b648c64f5adb8de375268effd72e756bb744bb2ac6bca5ea496e0dd804289f895776d135cb862ea6b116b8e846e890869bf81793428627d80d36214c324afda99317d43f2b75c9a11cfd85e408d8cabae6a94b74cdf8194aab94a35e79e1f029400a231dfe9c6e267a7ee61ac7c5e14c3a213d2251c42412f1d1261743454a64ce510c8ff45f5d1e5d2e2459631bfbfa60224ba9ce3c2e260be16e7b0f6e802e7a5314f1f1e6b11da08525b05fdd5326cbf2f2d7660d39deef7e5e1638cd72ff548522c56c20d3328259d7599f95de98d0e99ae0ef10693a5884bbfd9d8ddeb1c09755b5864412fbc83344ac4402aeaddc8674bbfa6113a41232805fe92ee1ee5559758d6e0f49dcc04c0679006a5cf709238fd203cea8651756a122e07e005cfe26a2f1431ec0405480080928028f546f48322054a851ce42a4eaf718c80c53dfcc5025745b9ecdd14783b44b5e1697c94b18e79527ec856023e0ab6c858b4ce8127c3e88c6839af407ea4ab51d808a558b8bfb16c02c4e051f3f255e200c177c33514cff357d9146f289eca57de0b977fd115210e05a13ad48f28cb2721704b505167035e672426fa0c003b64e879a2d52685cbd95f38ecf81c53ce560155415d8caf918df78c0e1b8f9488a3ed6344edd1380ca27a278232116d213d10c75bb56df460b6461800acc14a800a3c78d64f4245b7ad9dd03c8666b0ce77d6fe3cce961d5949fb962a2dfbfcf3f8232c8f14700c53e82f6cd2272570f0f6728180cc53fcb21b3baa3f993a8628e26ab20580762f96a38c31b1cb9ae863c9d0fb0df5d876e3a3a719babb37cf731f63bfcb01653e418c7120e9e917cbe6e7f899987af5b568459299e44b312f948c2262f3c66b06be2e8f936ac63bc179d794658c7c24a2e05f71b71c049423947244008ffba41dec5960901f2430bc4554baa6225dcc5de100beda54ddc09848d50bd3b1904bda579e5a301dbee8107c98c655945dd7d126aa09576ea6dc38922988838faa9911197018726195142128201ca0d21af23cc7b11dbe5904b00dba4b5ebf32ce250bb5b10aded79501d497cd6112d57e5b98ba8941c839c74b5309414450c9062b5053f283a78348a7b9701ea1dce0971ed0a56ac9d0c95eb1e85a0344681fa4a54a33c49849d4b5c48720387d27526f11449da415c90d15c6041bfda48fa5c998667077ef05c353a8d108ed6c3505fcc99d36046246d594d60162fafb8230e72e7ebf3cf2545df23b348a42a738af67cca3bfa3059ba960eb1619b688136af979fc3df7c8804d46ea8287618287ae5b3c4564e0ba6140ea8d1090f7848e3cd801ec9d42131a1a884277b66909b0862300b9db1f2f7c593522a401b5908290d66baaf9bbcb305e245dbc33bf1eac0fa4735181f919630742c68787d9986322a5e026d93d21309055b17ad2214a5004991d62cdaa4eb5209d0f41b5cc99af318633ecc92d54cfd577499f6c4c1aa557e3662964def6969d2224014046d4343d33723d6b2fc4e262b99fbaa5beb0e62132f842c9cd8783a871173a842ba23959f9d5906c64a56ea0a05945a38ede7bcb492118ceda2e574a7b5c8aca9e5f04df010c5caeef6e0a37462d716025adb71f7194e107d0948681cdd972af1b6668de39959a39bc80967804e314bb8f6792c62d3d272d0cad9037753329a6356068f8ec64cc19bd14aa9991f6d914fe0ee53eea9521d373f2a2195139af6b283073329d235301e8641349b5c789e1477a0c72690d3c48e9c80d0516fdaa9a3b792146fb78ab48be2326684c9d98ec890b8e50776711a9de88a13d57bf3bad540cd2bf86220d7645ab9118dc1ab44c39838bf86647418746d9ccce36b622204e50eaa76a82edf346951503cd5d5e8b3c66614bc6f62ff3df8862215e9e2c264d1ed7bd160c55190b9b3332053241ea8a3dc0d38cee41ffbd39c6517a9a2391767eee87b34dfa6223a6662451ab28ee7c32d6926328adf37e3697f41ce28e9b315fcd10847f6571405be820133698da0436572c40dbba0c25060f50da124d38209d9e00a8b3fa0a7cab9a58ef546936e0861fe49bc411ff0fcbc9885c15ba7448ac23f2980eb808ed78b1bff52a289c1ab93d9fb799430da8f94020888fed6f334f715ac8f0ce47427126fc20aea4db35e392c319707d19a5164cdbded34ab91a4a522dd0d8ae1baed6deee3d130605b6ba2bb73bf3bc0adc72daf63b7444ca5af52ab84372b5578581d116fd021efbb2bfe54c37bf1be2c764b1d2ae9820655ba84b405d93496b8cec558120b8c6170013ca81fe1021f777cad2ee99743e579ce8773d54738a57dc40e5ae1fd4a4e9077c013df0c4578c551505255798725cd66e32d16aee8c543ce750d6cc8f947927b177b1ff955e910e512a14a81611c83e8e0969fd74726914f632ea655f962c6294ad4259a50519fb1c558adb4bb5b9295864924ce76b9b13a24442d514d0e7622e88b18c94150b354167fa30095a562df5aa9a63baaf3c90972738c11b58d14c7c30dc1b3e9d86bfb7e8dc71ced9d17ddddfdd5dec4c41576c93e7eb43780c50acd5fca9758da7bd648c6eb3a899112939584ee8d0e600dbf0b5e4bc659f6c0ba42d14c9d5341582d112e3e825d929abfcb9050bbe7e2415d4ecd43b24e8a478a76d09062414d29898f2411e7d79812e4498acc76691ea62055d57ececbbb8a1078615b0c111b8d161ca558ce93c9a60bcccb8b8a0608168958f0a204653623e6d81baf923e5e0cf72770d9442e3f9552b3036a103f157003e69faeacd747f260bd24e2174a5b432be054803d3e0c7d0c07034a6c549d9362b6e4ff5dc00c5e810d40af7c01de7c1c47ed9da20405da64312002caa3799a1feabf18933879e0c394f5cf3c3af956b077b6c8b52fdf526478df2d35e802c0ea5068c1b3115657628c6b04cb1a3e46fe036ce0a7ff55c4b962fbd007a9a87578b7ea6ff9eca39149bb19c93a064e38bc7832c93e198282d2ecfcd649e434feee4cf18d15f4407ff1a1694984c1d16964168a5b548a5c9d789b93d61e5074d013420908e20ca0453a219834806aac6f650caae38f59249567f9343c2a5589dd8886decf49d7e0202887b27ba77481aeb06bc5389f8f603f7fb36690309845303d719666e12ef56d99b3343871d46a1e3bfbf735cb3896b09ffbaea233df64067df709d355dc665027a8bc7e70d820543a1b2996c61d24c0be2cc72031351fc80e6f36691c0cd93571feb4afae752ca80d28a90cb30c6430fe0b67123a91545e48f11e52969c774794bda8981f1e70a5674643e9784561891023dbf66e0cdd4234a343c7bd7052ee8c345568a483697b9e24a94f4af85d8007dba5238c704461ef9212906d2da2d3a8fb782a7a5b6cc5f95a8e789493d0a7802db34a8f64d67ebad1b9a8091bcb9440b8ea7e7dc8726e2c59104dcae96cd7431af28c90219b510c4b6a5319aba2252cc3e535c300a782575231cbdc30a1dfde6644e2fc02a4d1721ca8af22f5e5fad04d3247a1dc69ad702a57d8a1dfb04f16eb1045d610177baae8f041eb6e72ee24a3dcd4c35b1a7bd11a36beee76a27f8bafabeb0bd8c0fe45278b596fb06fe7ba6b2d929ea40812522d5a1245820dc247adde667f60bc37c91abfeae88dc5e5e1f8cc6786a2c57fec4570326d8c3be78d57162db8471076bea45b2882fc653d6df72aa658497535ab5f94df0794b2ee5c5a4db87acb2b4457e768ad80e316f4994b313e421c5682a110826e9c9a22ebdf46977dba86c1b85aec2a150fc99423e8f79caf30bd68b02c18d61d3eb5870ca47b0be348f4fd1964e503b7037031f179aa5fb9a9b8fc6803fcaf20afa9fab8522ace75e2b5952a74fe9c7d6ca5e9d54b293291441fcf4879e86951906d6a18ba68ccd4538bb4aa2c11d5fea27e32bc6006330e5c408a6ff6ee53dee40c76c88b500df529012c326763088af42f4f5ac57398b3915082cf34c4d0284993cea49495c7ad07728108d9c70aaf52d62a11a699a2ec38652213e2c2b3fd4d8dc4a2cf536adffa9cfd10e69ec6ffd369d1da17460b45d0a61c43f9bdacc8b161beb4c09b5b032d2bf68394946850edb19444b3d6b8ef132983810e8053cb967adc0b285b8a95adb610c8fde24e47d67c909e80085bac1000611bae24e7fe67b3e8f524a8e53a2117ac5642a066c1250b6af190d2a0e12889dd6f364afc49247b02fb18da54050ee450cb40207dd8c6cb11980fe82583820877a2800cf70b36928267f8b1124055ac3c00c994f3af9e163922780c60a584ee948dcd25c0a46948a38b780c31f03aa56333ed2465bc498ffe3ff534af74f8057ebe59c3b85cfce68b94ca21af9d51616144655f247d181a94720003145ab6cebd891a0592d4e7895884d18959c3c1f833024bbcd6189e29b1857b0b59b1072efa08548ba671923f6f6b235656f6f739abcd7795a77eb258943dd2c2379991285f1818c46517a816a9eb10717eb5484afada0711bfc02c7229f6eec6d8387dcbbc2f6d29b32d56255c71a38c4d54d1b409a3903991228fdd7815d917c9726060eb066f46e25e3af89ea9215cb1778bdfb0ec716ee0c9818fa884cbba2d8c4c7f5dc7171212b3ee8da1161c91080c014e67bf417fbb379fe4fb9ad2458c2e6533054c48d8eac89484187100289f402f93513a18c3167b310963aacedb83efc0dde0700b4a36f6815e23c05a32c42d047a8f33ab54305e19a503f808a2d062e0f54f717eb72298b102d7294de80620ba28c3fe4e40834b918e06329687682eefeea600c3f0a3ba04b4864b6712db3b9a525a9c9e357231f83294b5cf66c9267b4bb9b794522699920c6a0a750ab40a34b08dc91447ec1449537fb3f1e54529a53897fa4751caec7f5d1c9fb33ba3eda354bc6e477f6558f95786ed183118067d2abaaeeba22fca70ac6fd72512d1abfa7096e56d1acb2e111687dffa17fd5d79ac5e19968eaa5bf9171b6157fdd196a55e10e4dc150cab5744dfdfca62300c5a1bd74bd5180c4356128cc130487de25763300caf14636885d9edd039e75c0e900863ec980324c648a974532006a2e9508c910a758cb15482d547d1e59a7a17f34acbe99c3fb169cda773524ae7e8e7b77a31966ed28b26cb8b9e246ad1f7b3ac88eabd6a8f92a51e76adb2b6aca286d020121535f5d3ed0ebea6280a3a98b5acd98ed175ebd6addb2184cecdb0b75d5314d6cbf22f9df547bb84240a95304511e7a673530cd982d016a0d818b631d9420f4541ca51f0ddf970967ad68d1c2b7ffac7ec62360748142a3e45b9f70ce7391bf963f4f83ea9eac35deadb378aa228f9d4d329b29eb6850327562227faeb125dd785bd88e53b5ed48ebef5cab3ac8c30516d995567ad530a898e5a66d163db275076b11c206d46f169c5049a13287eb5e364fafa2ba9ef1d57c33a4ea65ffe55772ea5b48a7a6c3febb89a956f1ddbf2316ab1d1e8a299ac559c3ad47c5aa959e9502a654a9952a6941eaca34ef44b85ec750c7f3253dfa46ceee553207ed99ca5e437dbb24ba958d72c948f0afd68eb54c83a29a50baba6799c4c7c497fbefbe671f1256d1e1bbfdba7fbf6dff6b151f471f454bd18f6d48f9aab597959472f7fa55e49fd28938f65b24219aa56591bfdfe2b13401198557bb42c6cec21b3de1e258753557feaf610ebfebd7b1ac440c8f83042af34886138e7637584620b3b520cd929aacd65484185f5c1362652f848d1630d601b132856d8d9a6b70a7380789b69efcec20cc70710ce6efb81f5f2fbd6fe72ad5786ed87d60dbc81df2c38eb75011481c1faa32dbc3938ae5255fef5ef8fc140c4fa9ed6cbef6a0c86d193bd499326cee33b4e6408e8876358a924a51fd1bee347384f13df7122ed45fc081edbfc5c1b087a12bee344bc88f3f811cef3c3815880fc08df7122cee33ced1aa8891fe1473811df8912c88fe81fb27e38901fe14730901fe147fc7020dff1239cc78fb042e4da8a6ea251f1a10d8a7d41b40c7313b6aab79d0bb34f2fe926aca87e4a33662e6f399703a4629f56527593e85a0be7985bc416a26872d9ed262c3b572f73c5b95ef45595030476131ce7b4aad84749ec9f8ab6e825b4bb09cb4fff555dfa2b4b5fd116c7dfd56555bd8cc47235b9786bc5399fa6a9bebf957413b79bb0d5bb4491cb5fd5dbb89a215ebef58be961967ce5fefe700ad355d48e7fb80543695be192f512be3206f49256e2f2b14546df6570be0bb5ef5fb24b5dd8c7ea255c2ff958bc7a49576e457f7f5c0f2b4ef55144e95531ad90f41246420d1144d5a0d8c8993b6524963e6cad7b492ff9e1487a897713360be825544a5c8ac215d1fd0a913fc13f16b6582377f77ae3cacb7a7fd418e57bd3441ecee489a6e19b98b975c373f69bb2d1c7ac9f662ff25bb8d00126fe0edfd60d51bd98855c6fc9eb7d1bebf5c142f71c20a2e82ff227913e11d6493f73acc8bdff3817912a8e7b6a8ad7ffba8907cfae3f61e30e15a6de9f830ef596da30e3df9abbd7dbe307917b7417551ce79e5dcc7ae700218204d6dbfaf527f6f6ff681c7f226c641efca2eb4da43f7fd7eb4fd8fe53d66264e5b37cf7d2559c6f5370114fe6d0a40dedc7c2bfed0c56ceb062a8997132f06fabc2ca6f8d86e9f6f841ca17790b170ea03f9c09e6fbe5f3cedce1a90bcbbe94c33f7e8ccfa369e4f3fb0f03115ff4364e747ff4f527acfc17918523befc254b9fd5eb03f65f7980d4efa043751da4f74ae63ffc71441edfeb05418e33895f8a8fc5bfe253b1ea507fce2b5e3286c99808f3f4093e0d16bda3ffd848bf337f1b67ca46ef2263f916d9ca9332d2bf60d9955199571e7d9a812f457f4bfdd7077b676cfbf632662dcf160e37fae66a8668b93efc9744f596a04b669a4fbfd9dbe387cb2dcc14ff6d61c2404491429815a52bee67612f1761b9c61fac553dbfa8562f5fc42211bb882a7fcb8b6a4b35cd8f4ffa211cf4c187ff5209ca8f3f58d1df1e3f58ea3192e859b2951a7f84435da2e71d57c34da6d1b476ba2725643e2d8c8c7a2ca47eb0f23167c24088e45f6f3d0f6702fd09dec998489fe053f5328f85505667c23022ac380cc33bcca1998a421d793221f3f5cc511eb1b12b1471375e823ad2fde47a287a9ac5f780ab69f146cb0102ab469d933d9411ca07d28a5a02d2caa57a17c8cc95f51072f5fcbaa9a694d84b35744bbaa78c503ed0fa764fc1e76f4d1db1d4b756338226f31e02adc6034e884683f4b1b2b87e8a3f46e866530e52c5fee9289accfbc7dc431969e7ee8fbed4111b5f9459385af4575c713e2e917a69eaa2e8874093e9aef0299f8e75d617ea5daaf39954146a897ed2509a8a3ebd7e423d650487e8aba732be9e66160b071165adc703aea67f46d06a9a14c4beee1f57e37e3a8a56e3a28882510c63961f9f5aa20159f7b74703a2a88a232996235925632f9251eced273fae8683b8285a0dbba0c9bccb40b49fb81a0f4420cb2f7af893fe954908d15e58a27ab10b5aa38283b81a083c6940363e907def016a89f857a596983189f7b2a7699698933a52c5ac9f34947ed24fa82518066cd18408bb2612855e268d4f238b23b916277ad75d1b0d97bf6e65f49873e19a0707b150d3b475d3eeca5c7c2bfd9cd27d741f79340d5094cee41ccb218e26f90c00e74c3ba449c8b22bb97ad9b173ce39e75c741c7380346ca682e3dc026220fa250fb9fecb42b259c8c2f8d1aa381d638ccfb22a8c3146f7c3c98c86e96f84f90bad0e7fdf0e3c7c371affeeb9d1c868d477fcc39962fe03e05c3f03b5942c2459c8c68b6300be85eb213a179f3a177b084013e120860163de5bcf550e6220eb2e0064f46bb6f25936facfb0e72173f91d32d1eb90b53c4c467a53c6f2eecae6bb70c1e3b5a82fccf54552cf2fdf5a8d0fae5f38a86513b272be94ced28ad31f5f5a2cad96596d353e74e52086f1e22cf52fdf0fd9392b5d5a9ee52741cbcf92b5f662256bed05ff28e3f6821fcbbabde01765de5ef4c7c791d5baea6d513888818856e5201e1c44290bcdce3e463e0ef2216b31af768972133c6446347380bc79cdf9e6c54358c83567bd3cc4e7cf36b38b7578c8756a290bc13e46c84278486cecdb1523eb44e6b9321c3aeabe9e752edeb9e2a8e25c4f57580803111f76935648432cacb2c5ebe9d5b4b290cbb4f2ae0663794b8787f0103b1fa3d853cac2cdf2d72dfdeabd99c5e226b08c857ccc959d9e85b00e36c258c89c95e78565cdf295224959f497a89a28ccd5bc8b2c24bebbde2fc84258080be1211ff3cf4246bcb2b3d2359cd428d20264df4ffa357830513f2c9524d71a38b0764cdb168e15d87050a348d35837b0b26eb8a92765a28719f5335b7996ac7a081f8f93810f27848de190f11fcfac3c5d6fc9de39dd9c3578660d232ec3b11acb9ccf5c83a767b2d4c081eb6ea951c4c2efaed0ea86ddddeead6f5851394028f85cc388bdd4c3ac6776dd728d9e0a36cb7cca718d2296f2aaaaaaca551cf87d7bc892aaaaaa1eb294dd5a4df5c4c944075453cb535f61462e1717ff02e1c3e7ef1a3c0c047cae356e8d22b6fa95ccc2b162bd68a58a317e74516fa9458cf0a9fef0dffb67e9b07484c056d67f46ea5949076571bc62925482793d483a0c80849329cdf26acaf15e39406a2298e86ffdafbfddf8b773cc3cf0ddfb74f35eedeaffde7bef357ccf7c1061df63fb685ac67da394dae7fe322a07ecfd751cce49ec0bd84d3baf3f5c8702aa54a9f2effb5f15815b1cb742e802cf9549d8db83f678cc2ca9237df2964e1d89ef5d6d0c03be6794112b7b9c5a8261f84ffffcfc2c51c23e4b7aa7775aa765e0549c8a53712b7cc8d1e0c46c0f23358d1b1279888cdd2cb7738e2dafcd3316e23c7233e99d9681483a9955effaa7cc6e641575d9bc321ccd5e3f1d950f70ffab5eef7fd53b6dba59573531a0d9eb71347b5553f538d7c3afaa00548099725c0fffaab24f39d6a6d9caaab771fc3996ca7a94acac2d677247f9d119b2a333812610f5f353bd833e4e2673eb52ca8cd2a6941e234958477f7ca4cf18a69429650c3fb1a1109888b7371250a13e452ae40efdf97bddd3dd975c0d7d5841a9c4c9b877199f9dc17f19c3e87f19ac97f93b63dee1dec7bad213eb9e95b89a52cbb82f29b1ee7d0ad6180da24153b0fee6d23da11a7261b69340469f38da27a34fec23bef7b1ee68311806d7981f9c0f61fda5ae01e28120418204b1d182b711830dc0ef9e04a43afe1b48c4cde1acf5e90d61186c72e609ad51be05a65ec0ee6701c3a1fed9fb967ed3c469d1c79915bf5974523506a904432ac1b42df89b410506ff92603c0b18db8f39fd3023a369bcfa180c63d6f9353ee687180c8363dc57a3743b591ed1a3c3cc3a5c9b4bc133f28b825de6612891a2f3bdf8919f8d7b3d4918e949c248f391c2df9b428f0b187cee9e248c1cf131028587a74812468ef84c2913e83d9e9e223c459298526694d898e7d228a58b18f348ca3d534a0f18668bb9eec7ba672297796c2c626365f97336652bd6e653c4f93e56f62a306735ab050c47fe9b8fa2936346dfbf6fd68c14bff86e65a5418e63ed9fac9fe826fdd34f34691ad83c4ed1ac473a64f97db82a55bc0b187cee9fe96837699acbae058cf9df47244d23957013fcc3f6d2208691c3440b3ad735c1469aa631d14210eb9e8f3012da2c4981d8773cebc355a942c45e36e2e227bad37141c5fab77f4ab7c446a28c551aa1410cc3dd0fe586268634011fc2772d0a0b81cf63088ff71e8fa611c2431c7c3cdc674a8cfe1afda4f1a708bef7de73cf83752a0fa99eff5dfaaf5e1ee257befbf651f4d67cbed7bf77ce5ab112d5dba2b8776b551c59ad874e424b561cf7cf5a95855c55baf7fcc69458367a08a91b652b2f33d8ac1bd6db34cdd8ae64de32147b2b9bd44a19f993e9d30ccb64cb60a4120c112ec76a0b0ed3bd26fe84b3b0d6b06e867555469f1c6d973eac578675f323fc86fdf0636beffe6e67c56933fea535d6ebcfba8f417fd28ae3ffecac31bafe687b5d460c8601e1ab6c1acec8808eba97c16f32a0bff7fc41e71c7ce7e0730e66b25e6f3d2b7d9b86f1a5cca88c2fe7d36cc29b59513202a9ee3277732cf4e7ee722a9e5a509264a424c968341a2161252b2bf5f6a0499228e1a1992bc3c224489228191a25513242c24958090f7192c6496243e2c4c66f489ad8f80d09921f1bbf2169c2c66f4894f4295e5662efcaf3a5c449af7a517bebcdb0f03da9de190be1fbd1a85eb7573e7c160114815575c6ba2befbd57d1e856cfc6e7240c045671aa17e57856543909c3e0ea554ea2c4424ee25e5d24aafcd945e016879f7aeaa3122c31c2c73a6631c6883d24c2c2c882e3bde8a757ef132b41fa973552952d83510b7b9b9ea20ccbbc4fd5b364b265aaeafbb164a4e7ac12bd27b1485209e6297ca8248992249d83f5480c3dfe8c7812aea78b381eeb884cebdf43ea8894fee5afc44ad4e85a81a5fa52d52be535c4b764adc98cbe610f474f1a3dcbe8faf72bf1c7b3f2bd77b544efa938923fa2f547df91d05571462f471527feb3f2d53a1a413a1a8d462347e4492c2b4f1079c206468ece08b7e38c70449e18392346ce0847e40967c468d4ac11352deffe5d4d4b1dc2c9b86ff951bd259787f5a592ea952eb53919f82cb5a5aefc7c916359796af4d5bf7a63ec1011e2b8d8b6eaaf21dcb3d42b4b31f679cbbca792641f8c7d6ffe133d965dcf41dc951773a21765d7e98797cdc87035b265f831e9883823569ab464ba8f2f0bc7c25dd154acb715d6bdf690a4e450ab59f92b5dfe962cac97c22749d2cb6fc15a5ebe4bd69a8cac0d7e4b46caa85fc95a938155004560b4fe78d6fffe78d123f64f561cecfd29cca9e94f451f9ae2de433de478da9555c9ce7032ee63bd3f5e4f1162205a4fe9a19ed2432dd4403da5871c4f4f69a0290cc39f044ec6bdaceac5ee0ceb7e62a51286f1308f93918d06dfb7d1d5fb96c25e7b8d8a4f0277094005d85beae3a8de5249545fbdd855af949622817c39b1b00e3a086f54cf56a4242525f5b162ed05cc46d65de62995fe4fd6cd58fe3b635d0ff514fe7939a25e19120a120a120a120a120ae20124d4a2dc06c4127900f10812e21124c40388479050108fa039ca58de9aa20c56d925c187855fe1a8b4156ce1908dcf83812a0bf529be876fd57a59e806db0ad62bbf7d6b35a3bfd747f85413b290053e7cf7c3599959ef0f8765ad65669df3af14401198ac3da895f3392bf9ddca8ad3f19b3f0751cfc6277aecf5c18a926cfc8a7b580f34185b91ecedc124eb15a7aa28f557a6c4cacacf7a49363e96ad3cccac87580446cff2ceba8931be8d9b908a32f8518995b7e919a995c1af20307a96c7b2951765d6cf2a8bcf16aca3487db370505f55d925d9ea59322b937daa7e25a32d533d5b37a8af7e3265c946bf9215c1041332be12d8c7ec669479cbf05b192dc17c8c123a6f03ab58cbb0509050905090509050d08f964ca6333276788c99183262ccc49011c3fdf42c60cf624d23c3d2f8c7f7695d4f65f2e78cd55938e87543c66fd60d5ad95a19955d195b37e4533fb971bbcb7ada24c5487d621931285347729c0b466c0bdb987061889d8ebe1e2fe2789a8875b7b1ee58af3ce4cdfbea8f7ed60db6d6c7f6323e15a997a9eaf548d197adc977db2aceac4dca7afd59a72acefb96e3551cf8cfb6ae9e851de337ac4fced56c62feccb0235b583940ad571cbec1300d6b625ac6b1901d35d45f6a634ccbb81bae598c9187340dfc266c9c5b37cc42e090e94dc33a4278c88e69ad1bb769696f4ccc080b9873ce954ad601a936ee0d11c2303ce745587d38fb5efba9faa20195ef512078e50091d56575f5d75320eb9292ba4bbe6549f8de494b5279c96faead2ed1201a4483681014cde57bef399c1bdf9f08cbefdbe5f7324247555f5129a59452caea7f34cefc7649497f46a964f5e1ae39bf256d5a45f8bead7692fef55646ab83e0ad6fb569f5b4befb9951b25edb62dae2b804f5a6bb7b3b7cf74440f7e93f6dfa7a2d299a0324ce9f693f3855b462a43f4361443cfd2b7aaade1fd76aac5e10e4fc95b53e59f5d2faa3a9efafb2180c8392b15e2be23707510e9047a93c3a4485a8100da241a31c20ad43812895918f9e028de8d0685e2ad443443d64f4d7e988326c527f1f66e768cec74a348806d1201a4483240f4625f573ce1c20f1452b706ef5b055ee2fca8f0f7a453d29f1e714bdcfd9667c5186737db3ec237791bf288e2ed1579463ac73bb082b1a89aa63a3d19b8f61d57bd0e8f3df5f994567bd1525618cd99402eb94927371cc220b2b00db9850f1c452f16339d64fa65635e5a02feda5f4af7a85b056651c79e1533915c338f83be79c7bce39e79e0ffeb1fcce55f2537eef4c8fdb8148f76d92fca6c86f82dc6d5fe69cbf7b771eb4bffbbbfc6692cd4efd8d7fe1dfcc44f59ba209f69bbc5972cbd8b28fac71bff75e95efda0e586545b517eedd3f21acbffd95ef2f330e6febeccd5d7702f6bd6f4dc37fa56cf95d258d4603beac57f64f297380b5953ad8e2c80fd85811c00fe40471e884c8c56f2f0c18bf6c62b01f78ab79bb75e33893ff046b7fe563e76eed78ef2fdf2b25849572c7634b007b5580c5674980283431d858618d695732ab9fb469e6578f150023596f0294c5748ace39216d4df36cf5cebaa948d6fa69b22c0562adbff7fa363593d71b95e5f6c2bde9aa37dc5fb7f05db336ceca7c0081f227cbf9cfd2117fd62b7a6fed85f5adbdb87e66a27af9ce7f0f65d3bc7f7b2bbbf3ef7cea29ebc6fc7e5ae1c86ad211df3f567f7f985dceb1f809c13560577e13db4eb4008aed8ec28bd846c18d4fdbdd12001f086b8fb91af8fd5202187c2a600d8a2243f6f27080e7dc00f86773cdcdaf06c239ce3594d9d2861ea0b44c01024b102a2f9c94e8210bb7fddcd64486bd27cbff6e5d7b2166b81ab7d11e6b1a0358fec62ff94bfcd2368b9f980d0458fbc9c2891dd9f623072a6cfbb19207cbdc9e5b4da9c9f0b737b51a9b96e1a7fed6684d6c7362db0be16aa46bed17d034ee5b9f2e006ee08fb280dd92e512ccc7344ded31c33e2380891e211d3c618a2bdc50829d335039b204305049821620172043a14414059512cb504541a594be0463e3621f8147804cfa6dfac4801f1c0be8133f3f5feefef605dbd37614b6bbfb674a7de226a365f86136f33025378282ddf663f96d16d0346df9191044d3dcf6e3f6c6d89e61fbdb605b86ed2ae460fb65ff589bb6e952d7481bfcdf35ed87bff50b016bb631a9e28835d9c6a48a2e58681b932aa8606fc9b627b22b28cc2705d6bec53927c3dcb46f4eba75e3ef4118a3941445393b725319a9e6a4d46a587bd138f34b7a17b1ed5b8f6ddf78da8b8b7246e486c7b6175938dcbe9df6a2fd1451ce30eba68d1af3af28c15cb5e9365ab16e562ce798857246b26e943461dbb7583748ac8b0bbbbccb94035afeb65c2f059e8f6d2c24524b8b8b75d3faf4d2f2f2f2426259797919612f2f2fa297cb7a79a1b3a2647c7981cf5d73f3e1e2bf3163b95671983525cb9524038cf6a925148c9deee036a254c2c03f764b509cc01dc37048247bb12323905204edaeb4adde0e58f72f076671aeb46c833fc7998d6036b56fd674836d5bb658e36776156b9be35804aee635991158f7aed97819db68d60ee99a5f703a817b05dc1b03c140308cebbe5b070a6c5bcb3bdae2b87681f3e6da7b6bad55ef16b8b77bbfe3f8f3e71ee5bd0b76ce9d3be79c73ce3d18deb3bb51039640d2a7662fff7b5833ec3ded9393fae4589ffc52eba5126d8c04afc7a1fb73ec1435763737bc6236c7390464c02cbc1042c8af0bb6f5a0897f1f95b5f75e7edb1b33afccf1a38ddf0db3fce6abecfb7eef7d6a99f520d9c75da01ebe6c35f263cc1a26b2389cc96c4e86cf0f6b4b12b0ebd6bde62a7e1ffffb7e9c57659f9afa426d34ef2f7feb130f52301c7e6f3fdc3219a4c4b0190590c95a946a587f890d9114239148561c48926235463a4ff8a08613b06143c9844718e1eb9682be9b61082c60a0f40814232d29839bc3132023744b119e40d96850ff9e6a34aa7fdf604c38614422da5630424b45f8e27b104688cda0074e2282ef50ecbd962ad2d217d603605d07eb584bab1182d468c02741114290c0d5f0c083946d61fda1bd700f4b2f2846725f83bf57399c7105d5c3b651e4c3c2776ace3bed7b0f818537d47f55dbbb082c7ccf51397ccd9b9004a6ea81922aaaf0c2a4e4ccc5acbf272c1365cdf598895760f2af5b27ad8cb658611eb0f119b3f1298a87999625a45842ce58af557b0f60aa2490f620b214a10c10c658a593f207dd469630f3e20425a4222871862a1ae1794565474a2b96fe347367c029a10c824949fa1356ef694684cdb8ea9d315a9955bd2f84ab2155965269a5d621a4a4a1e2108fca9ebc955004ad57dfb9fac1c18350ec3dc6a209596f43d66d0382407313ca54acf310033115d69f9fb826aea67aafad84b110d23b8ec25cec1dd83bdcc4de522b8180b35155067232ee454828f110c5488c85ef30d1612c3cc863c113c2638333bb940723739352ba8184549c8f7577db5ddd076bb1ee884bc2157144dc10ebae08ebdf59748dfb692caac3c2c536b89fe97efe74b2d7fdccd8eb7e6c63612f86858b85d5e9b02ce186389f2e0212e703a94023107209831042f8de7a940efb6115c1b3dea7b7862ebc1dea8a35b83869542ce145a8c4b3035f59027b4163e11e62d7dffd61bdef3d1caf181446701b3cd9683c1d6d636ded05e7c808dbb16086b27583f5bf9365e607212451acd1785f5a41c9d5c43424f529c60698f729c6f6c9d957bdbd70eb27b0afb5537c0d32bf210c416708987fb324b9c14f692486d20a5e637f6666e6667e8f04f3de37f93ecaf7cdbabfe7ef555abda578afab934a30dced48bcf79e6c01f6228c5404e260842fbe577f380b2174d69d0c5cc6b52d815435219cd55343587ff6870fbe079f6322adc8c7ba9f9492484fa7d1c0f7eb46b0feee737b50920b28468a7f5b58e73e8d8a75efa875b363f9e78a0fb642048cebd580fb587ece2889c508c90bd87d3ddc970fae624d399cd580a59eb4176e87ebd8fb7a32fbea45054b0fc67f2b23d619c1deca08c548bd13c51856b0635de4a306521440a712122d8b49229560bff760c348654cb8fc17dfbbe8ee50a475d3ef482598eef604140e85f8902da9c42fdd87fb60120c9348303014098654c29a06def7fe54c684bfb4ac1c2f5a37f2a115448e7599c32bceab9894d414a4120c77c37e3c78026484dbd400f4822fa8c1b630015449b1841440fc24073f2b4bb88d0c8d0ac982364523c30b4fa3626261c90d1a153f8f87b0b095302af358058bb159d87bf5beaf7e62d1d2e12f2bb47460c07e4549eac7e3a14a952a56be6d96aca6fe5845f02cac38af6255ceb82a151577f7fb1e0cde8b2c3e3a9e0f3b010a4e783e15959e35c4c09a36c9f695cddcfc6aed857f0f927db04acca5c430ac51dc24c55a1389ec3d3d0b2ba9bd70dfe203d207d84a8a7177185a76aa1febbfa204f3ef5b61a1fa896ea89e506161868484eaa781c18916543f2d3b2e0bd1c54a55d3fa781dc4441fbf1b8deb639f68fc6f278865a2eccababd786f65de5ebca7f37df59e8a39dc3a29a3f5870fc6f8a8eb740feb58ff91d065245827ce815dc048ccb8048175a21360a0c23ad6bbe706940af4b1fe2b4930bf100ad6a18f4c012c82857f211460ecab947c80edc524853edcf3c408f4a1549c0e804e2f06c67a35e3f48619947cfb6690a220e9a9859329483839c91e855c2102e66fe3faad97accbf95786f5ffa16b18c063c6130ce0e16adc6c22ab7a2556ba8d0aeb2cac3348516acc5849420a46c8199683b814fefefca13f7ff8fcfde5f05aa50a23f1e6246a1d9e1c0bccfde46475a7d110c93a07c8e75e1280144b4851b2de2725d15ffc9751ef37107bcf41da64decf2a6b3bb3d94eef453eace77856b29d8f5bbbabaa1555ef8cf586c0facbca8ac0c166e5a8ac3b8cf13de8ef30f8d7b8df73e68308eb2df3c1b2625262587c979e615960ee486fc0fcf92fc9b627d6abb7454981b1a4186d522c21c512de6e03d2c37f4d7cd0f348a42bbf6bdedf93605680f95f9275afc2bc93d4bae12cac520c18cf384bebf506d64debd395360d238d19ccb38ffbf1a1fead01b3a546e372b36e1e83c9da4e1b3d7cb33e70b3f220607ad1d6340e731455992a49cdf9524a2ac2222a2bfe2512322d242c8ab981629919afb86192660bfd7f9ba678e5dbba51a287af6e5858be5937d95fd90de96306d93047b4878cb17032f0f989cb439707009599123dfcece149cbc087f0e1bf29d930e513cb549402cbdb3465ddacfc9c2bb639118320d84b0900b060d8dbb485848bb769919016900d18b4cd891814611bdbf93466d6bf5838dac25859c064730a98b7e9f912c494b1fdcb9a2d68c5ac1b12c502b32800dc3627863821bd4d4f1734cbe2f3b062ddb0c038acc444294c171a33d157d64ae52bfa045f94fda00560649b133150123375a000c8d84600321e7232f063b2e6645ebe590cd4271960598b7583ad1200f8a903fd2cfe0ed98b75835506ea79993ed098413760d23626572861af8c2dd5038dd96807eb868132b6a38c857880c50780c9bac1d6c5cf1d7cd02263eb830e960fb62d7ef24063167b80c58f1f4b19db9315eb5303c631960fb6a59fadba9a05ed05fc6f79468e18b1f0e70e34c222da0bf82d6bc078a84ff0e1ef60dd68cb402cd427f88f05ace5f9e508f624d1c3d68d6ae7cf2aa3b266a5751379d8e1a9df81871daa29c77cea67a52aea7aeaf99d0ecfd6e930afbfae1a001a65b46ee2941917e913fc0cdaeb679c3f7ba055467d55796bcecd0a93b53565cdbac89a6dd1ec0b7c0064cd864bf60187df925d4849cdae64cd625fc92a635b6a342e11b5e65bd6ad5e9aaa69ca8155d5e84d2391754d1f40ac893ef5d4acb0aa9a72544fd9748561b4cae877114d439f9fc5ba29351aa5f6e2a5df7472521425fa6bfdbdfe563f45d65545a65a502b7b69c09a1342aeb0b76760e18d26075b7941324b279f7a4a66d4cf2679ce7a4760abf854f5da3b3af48d3578fbd8fed9f2f2e55fbebdbc54fe71d969c95cfee5a7b36e5e9c89d4604d73f9c752d134cd56b0fdfca4691a133010b1fd0ca56932b61cc5f633943edd583f62717979a497ccc5a5c5c55d5cfea5e55f5c5c5cded4e2e22ffe2dff92b1e5d1270bf4a93f0821624e367d5a409fbaa50c1809a3d2a66948dfbf80a661f97e0634cdcaf707d134a3ef17a269b0ef9740d388be9f47d35cdf6f81a6b1be7f085753fafe1fba6603f4fb4bae86833899fe2bc3f2f4a98b6cc0d5b011c73f5e2f3664bbe5bbe5fbe5d9b6a5e3c5bed43b0297b7712e190fe9534be59dca444859ebf4a99f25eb217dea5fc97e9435917e51ffd56f659d449ffa4b591b7132fd34632a195b9131948ca9e028465c0d5fd1fce36a18a88be02c6c1be987fc63fb557e5205ebefe7214db3c344ba06ebe7241a0d0ed2efede3d2d4889de6240806a29fffe46a82684e4eeed49c9c5c75b2621b145420d98161f0372741748d0f1218a2070980864e1cc47bbe8083e6043ef94213f636212a57d8dbb2e0224e4d737fa669ee0e969fa94062572c57e1e49c733b3010cd4a4088209a13176648831165584111234fb08115a1640a54e818018a1776e27b4d4ae73c4ef99e94523ef94fbef7de7befbdf72084104208638cf067b618987cf6c650046ef94d39b8c7f21476267abbbb4ddffe1cb85b6e2d7a89badffbbd35f58efad7b73ff0a8e783ab0421a5bcfd3fd846c6e0bf9f6da00244158ef063e365cd467f0d06822d7fc9d5d46832fc7c6bf45facd4625b142c3862bb5ecc470d1002147b6b8cb03e99746cffad619bb4a0c7baafd1355810fe050861810de8582e51692a5ec8420d5bc4eb61a231e98211f64213f6fe6d5658b671db150d188293ac014a60913540892c9a068a29ac60a5e587691aae82e52ff0587662b98a4987b3fd389cedda80213ce953b339d6a66fbc9f05a0020cb30be813dbd41a7d621f356009227d6ab686e52ad0008425082188064357d8e0390c91010a2c3ca148902136da08c307530065666666e641695edb10f6ed60be5601779052dffe87a6815f1ac280ac10ad460827d35f6a32a361edc6d8f633ad6686b61acc5119095aa67f2801e37e219eb88f0d2d1d4f0706acfb917246cec838e39ca4a6c0f8b1fee6baddb763e7ff70dcfde087858a5acd8c6c332fd4f2538c549a99218308314c814194294534a18402e89cc1064338021a7e88e854465c549899312363c6d5d0e61e1804c852a0f41443c2689b9131a3c68db13c05288210032054c1840c98b071319b5304291409c28113514882848d9c2254a444718624a8c041156cdc192ab014b0d042052a5c01064c104214a64049d48511ec3b42d9f096adf7770feb4872c02eb55d6a4ffc96a2a47d6af938b26254845edd7f0183e98a44df367ba96dd928577fb0fcb6db0a22c7bafb1e24fbaab42e33e5700ff263bdfef0510ab3b91d33574d39e9dd1fed6defcdbfabcf07e18b2e1dd515bbf7e6b8a2649c97eb6ed983f9d73976ef4eed4563777f9f1e639d8b8fd25de9fd2ec3fa24ffbabfd2653c48c1a475cf38ae4a8f42bb3c7eafb5d7321325236ccdbdb9736fddcdb52a6c7c475d0dec0ff86c9da5e3c5c27a4760a3bf7bcec555cec908dd5154455155d8f753626eddb4736ef2e7dc6584fdde73fe2e73f7eedc73d73025b4b48b9b83757730e6ba23b05e231c2053ddd745f85c4b49496965d38898d97d73decef96377e7dc39e7deb9b716e3ba23d8e19ae29785e7f720f40761f5a68a92d11d3abbd6ae88bf0cacb157117e840d61eccc545192bdd9f67bf8cd568eeaa753cf4eaf66fe3bb54c0c16b0bf31a42760fd336e67d08035dba0a721b615325af7d2f2fac08039db9878810c7652937af6a860ed9f0a338d4665ddcff822a4d7bd404b27a57c9c5d370708f04524b07b8bc4ad3d39193e0206d32777bf3fa64f0eb36ca74be7f88665d61ccef8574ac991b317845d927539dc6bd65e38ebdb0bf74f08bb24189817d61fb3e1fc5521487b37b57bdd6ca26484ee5c3be7bcfa8ec9fed839d1e87bad392923747ecdb17bded1fa9df26ece91ec86e0b798c9923e2bb0f6d3c51c5557cf62bd99b36ef8bbc64100dbfdaddd3784ef26d71a02b1ce1f6592ed4db1bde9b566eb321f406cb70f67217c6776af8372d0dd59392a6bedc03163bbfeabced089f9c43045ca3138e8e037f82efc97f39891ccf9ed67a42c68c292192bd06cd08a82f9cd864d4fb62527e39e720728b90138ec95019b8f63bee408344b02f3e118e27c8a728bc20d0258ea6d5a561f5c859ad915c2ca9f15f5183dc2e8103ab5c34a8ad1083376931a03c5c48035fb166d4d43b167ddc49c679347c8593f57f14d547c938c6feae8ceafc3a23333f5e7046936a67bd7d60d02d8f86ee5a822fce8168e99589fcbaed630df1830f71b906334c164c09d261030c01f7e031a0842c083851567bebfb769f9f1a7e89d017b2f048eeae5cfec52dfa3b2f3275b3aa2a5a62001092741b4294840820a765695a5ac1df21dacaa87d59d4f559969c28a81f8957fa3c94dcf24e19b22ac66e603888d55e4f830be4dcb8fcec6afded558d5cb94bc6c23c724668b3980a91f07f4e9b78799293ab7699a2a13e56f92ce11b633846fd36e6920870a12b4fac0c22eeb4fdf15309afdc06a404e7303b8adc91374ec0f67a18e4682e407396070c2061698a149cf02e8b7d5801ceb2fad0cb405d04e820501ac21e06189fb16f380744e6cb78244297dac69aea7367de1bf2b33417abdec5a4f992cfaa649df54d13751f46af6f90062e195f970163ecd207c43180e7dc7917ffd63b32e05b202ae628389f744543674bc082061c37db378b47032050948389982041e1bee8d01a3efde148c3e0e7d6f7f3dbddead9b8bca5e6662ea5dd7f514f534bbf36fc54fff65a649bda9a2de04a93731f5aecc07106bb17d56a859a8e4cb174c9bd829443310000000027315002030100c8784c30189a80a26f51400118caa5060461588e32887614a19638831060c011001019011daa4019cfec2b25db599508915818aebe07b478a4d07421aabe3004a299cadfecab867b531b92ec46e6e358cd3b07b68ca4f4f0fca2426b53a863107765629a9fac1e0e324b22e991696c8ae1977498a9b3e615ce2baf94cfffc9c4db5e12c98cf70f6a7a51d78fd492191e7dca543d02860a989e455c503209a40ce3623b415000c2c74a7b8ca2728392a301218a97bde0151a973a41509a0c262e624e5a32644fcb4f2b89e794357558dcc0fd346a2048325bd6102a916bf653c30bd1fbad41535e879e0bea4af6ead2d5950a52bac6894fcc87d7388535d39840fbe7584eebabcaabaebf78b051038e07ae204998cb3f73fb8c44d484eb494bca76233627113390bf27f9e82086d404422c957dbadcca081e2d66e2b2a2df4c55234584cc93e72ea922f4bd481f6ff165e5d7d10b90b5632eb71895e8ff7210b602958ee97d0523d5d542f2633f9c0ac240f7cf84582ca7f092209f83ac3a86d0914c3be4c485263edc8347348a3d81115eef44e6c7a354e3a2807214ccf1f9bf6d53c01f2f982952bb869c6d019a73739c98e5e36b9d6db87319ea4b5ede49804f2aaa391fa8dd9f53fe98fa4bb5bb5b5fd3bd90cfc10ced3a7d8a9e719876bc1f9dc1f6aa6dacdc36c596ed4f138500a6ec9967f8f54c35d490989c799471aa158acf157492119024d2287bb3474eb8c4cd47e7d6e421d0c58206b6281386c1a52b121bb6089b3781e483922de78f71d51e9a7c880e978a550fdbf71e3ea60eadd0f2d3b7408d9d27ef6680fb6748626552d054cfc784f50bb720ba69821d7e631e1aac50099fa404708dbd35047d695dd8b716e07c280f3392a0ccc6a0bfee0e7bb3cc216857898a31ac056ec370d2cfd3490f4870e42fa11bff079070f0b639d0852aa032395dbcab695d36c83345540560be43aa2a6a9965cfd01602ccbd4bd3838b152fa238207b078dbed55e7704865628592a5c40369f2c8fa359036ac0e03ebfdd311c627e9a9bb8ea265269aa1be32091b5b9428932a81ca4fcad6b0e43770b85042fdbb14a385849fe947ba12db5c689c21254efe86c1b0d0e1032ef8886d917512bf5328e08f3f6d0003ce784ab43f095753663fd77854aaa4f0a78d1c6a00922181e065cf004392799434bf0d97b3c9aa1b91c3945c20cb3d2ca116741470ac19a25cb5bdf21ac54cea988f92417efbd36bf41c559cf17e2c15f96eca61e9e504befadc4c7c71a333e0dd5da2ee40ff4529a569685e669c9051ac3d50811645f105c1749e7fc9e3abdaf82c93fdb8d80dd83975316f00464d521a7b4da487baf6b274c16b156dbc01d7276b2e2de521b3f05cc899845d5046a75e2d18cf60aa438f1e16013e19770bcf5a943c667216c8c13378afdda36cd58f23d775e4e3c0e5c307e63b625713785e3bdf53655cb8ada1f92d5411f78fd3d85964cae332a5ae88a765c625a2fd649d5d602f93fa7e71c3d3a81183a03e3a6add5049191bb0cc6a991aeef01e2ef8022249aeb9d4268477f7a001223473ea2651d2bbfb79371304f8c061974c7168b3634494741ede3fc9c9849234b52c11d8fe2e5f590bbbac0c343279fb3e0e15482ffccc6980dcd1906c00ea8bdf051ff84798cedb4ac8950499f51ff908901774204603dff911877ba1177f57fc73748ddf23df045a70a0c2e7252e92eea42b8963e52cba41e7db85a33c9ff33ae3c0fef0ab5d9e4102d3d9cd8bb8346bb51beda3c7eaeca540fe0dbc533bd0ffa7cd65d4aaa4a74a99a94a95eb1b24692afb945fa9b8ca25ade7d984e480d3291cad2405bbc28dca050d47a34a921c2956cc36bc0647e954f22352bb1d4799b25ee1e547242e560b4781a3f4d8e169043b8ab2ce5d0b38ef122f34e64997b7ac484fc922507a82ff42c156d44ab7bffc71183821192fd7a2ff79a5180ee65bf5d18ea33bd5141c5a7f8064ce2f6e8f5a14b560dbd711873d2010170945bf2bfe9fe1561ae4d9e6e53df1c472539e65317f72a84358328d846826644241dc4b46aa68b42f00910d905f7ae0ef195f34ec13636258b264e618b8d0e0b58f00f686af4ea8af0e1248159035592625dd1a3805f429788b32f412aa35b90c2caae9a2687a4652da9f3a4e4217b93db098a919c28f9f658b8595f210e32b7383fe7ed0b13e5e4e4e8c3ec2c14165c2be524695244bfef1fab58304115972093d2ef13191c60fe00a200172c5c50b3ec6585efdaa8552dff6094582ad1983e1c3f1dde885cf1dc8a87c5c86c4dfc60219e2372c7528832d48d728223645a97336a0429215754cea391603f04205d3e3074914864c1758ead8eedf2c76f4fb7e8b913ac7d1451e9523f5abda93246e3f6edea7460249313869536061ac32c6362e0fc0205ab5900f1fa58f91b7cc9a190d3f770e33d0d3dc212c33cbd6709d539d10578f39e9ae609a8043a9c041bbf82c7635502c03432d393c318beefc423639f53d977f275c519a0e55b714923202d2f047d048b0ab803fc22704eeffbe21ae396d6d5ca1de9cbcb135df728338ab2b35303e8aafb5e26a1a8ea99c00e3d3bd22c10c8600907c39bd9c4d443809c13a875dff54f859999aeceb0c753769e285062d876d124390a34af2b86e782e0d7d8175916c91a87d3948d68d6dbaf7544242aa29447b0d0aeb138cb78c8ff28ac4bc9b420d01e8104ac5775474b7da3b4a6fb3d064ec4569f6b09f8bd0cfd65b41f682420044551e5d13a8b1a35798f71a3c41455528852065ae06f882ee2b5024c759475ad31943e96902bc5374d16c0434e32b4a574b38ab0af9cf694065f5fb3508f127006c9edb84ab63e42a8884a0a5c21f853cd07d9fd32b0d1d9ab4851a00f2bde3a60bc2217c0cecde13c4a53c5cb5145b4a5cebd68a7dcd66e8c174dcfb35f60538b11adec6e3c81202480c211239ecbd1083a1c7e514a6efab10d3421afcf0aeb6650be907ef6a2486fc483d491e3e8f40dd07341b0f1e7f1724f1a366b22f4d9592ad45d7fab18376caa45d8b86278da15e1b2adc1a1fa64f6b9b3dcb3c3df966c7effb211f89a648c49874576e284795de1d98c93f5ee8d2510b5ee8f4a66b1cdf4d2dfd8d17b97c0525bc242fd2b004628ba2da234a74ee8712ce5be4aaf23e88b2bc502cdbee38e4212ac67e35ce930deb89f982b695f8fb7cbcef0a757a2cdfeef87cb41a1100d5872e28ae08626825ad8f642b8a6277f13db1551f3595e732f86fa23784a49de2be1eed30d995855b9a864f53cbf5c0753b81a1b09f0d2202a5d0212d1b83028dd42c91dc9c62232ee732ca674406087250d3015d97e96a9d1698b168cdb20a7189f55d0233dc7156baabca96917ec63a85bf42f7fe6fb3bd640e2944e9dfa04a99c66e24e0420ac680af9cf53576cf54346d75bbe8878bff972be5798486c9e4a5f7833b8e39eab41d690f33f8feb78bd632ee3332acd7e89bf0e2ebdbd06702529caff9b356623ab22671e49025e99a48836bd08d66e4399f7d87f4893ca938fe9251d547d2b3479898d19fb27bd0372dd89f0b5c028f5423af90b79cf4b62405795c3b9448b57ecacac0199b32573f90ec9ba9484c0716fa5901cbe99de1802db34971a8305ab54cd51632ef5c416c19a6616021c53692a43c9f5f6f52dfda726ab7ffdd244a8e6ded14fd786bf99b2ee06e80580afd46ae176a46dbd0428a2da46442c266a4643d24a66abb077f36432a3796df963605e05e7592ab6945b0f852f9fe19f3dad306e3a51d42f0baee11eaa33286687e069845e770c7b14fddefc9e782593eff6e6775e1c89e31df15e512fab3c741cd1c421df33f7c657d889425981bcbf98e93715c0495b6eae1f62c10374ddb1bd9467b42feb3f3bdcca2b7b42c6dbca6a7702ed7f27c237ee986f25c5e3b92dbd01f75895e6cb0facbde69f32dcb2695847c00a0115a268cb55b19b0b8d09c8f8d01e13ea3b377cdf6add5637d112e235dc84399bf84ba514b9fde676398eaf471b8c7dd4ea421104daf1024fce76b8ad3f243a6de82d3deef58c7be466d3a15f7a54c83ad257ae9ed2c7a6b5935170a278b5115da6d27dca7fb3e2811a5c908607784f2ac8c8b0180a4096b89d1bf497b02f60d60fe06271135e9418612b848891cb8753e31152993f816e83f8134573bc0cc2bbcea35643ca6b9bc4e05f4c433062c47043317401d8e1bcaf90d7919e60c4059dc5ec45f876e734ebf6ce7f6a4e28e7463952e31c44cf91d941c328dfe03cb47c2663e3c2095a5af784e66db8bb3fd1030ecffcc64613e37d8370d5266ff996d169d0b771f14a2f1fc51426be19b40baa14c7960d5af6b3a6542b5d1ff27678196f04f40cfb883621953b684b3ecce5941d25983f34c40b66723fe200186cc8cc6c2311a3bcb331947272fc477a0e52f7e0476c7081b99d17e4884149e29ab01c234c7607936e9d44d462af8d17241bf6b7c6eeed8b8f183b54273324d7bd8127df400ea5fd1cbf57f43887daa85859a43eb5517d58dc3564a5abefd2a7512ace33067d983a2ea816179320049703c02c807f265a5f26e6d93e879d992d2e82aa93990f8ffcb2fdd7501e98ccbe100b93c25ff5fd4d15d4174ec931550161c43db0e19e4b5ec3984c18a465e0078980cee75e96cb40027fa9df2f3da6f44febbc3b70f1ab889f332d0ccb37546678a3bc977abfcbc430bc1f4ab76d565feb1be416b19462e14f428bc946b514bc417fd8266cf1857654a593b3bbc9ed37f4ea8d6a430392ad63819a1b903d8f9fe75922b8d7d90bec25149f9907283a11db2b9b46523820ed28584308f3758f823f6c1b1b11e74d90463e176d762d4747bf2ea68291e1f5a59c389ef472277a00f29402bb30f1aa6450a4c12d09401e112bddcb715081c6fc1cd105ce61f40cda0359a1fec7ce3518d5a575dae0888e9433bb0c1380f6a6fffc47d8f268e899bf12847bc803c19e754173476048dc2c146ef9c6dd3f7b4346f118dcc42400183c55df5fdd0bd2d80a3bdd3334e0bdcec952080ce6bb6ba89332bf25f0bba7d97b23916e76b1b4fffa03f97f2540e8aeb7946345bb21469988257d196ca1b67cbdb5a90d235e83ed32407975be891ea3b464205d63d9f6d60828ae85a4add0619fdac9b56f54b2116027b4b44324094cd35cf9b12b4aa22bccae6e27c0b83aad84354644062df69b36125330fbe32585f9020d2ca6ad3ab05c7a9742b3428d86dd9db671e0ae87110585601736363d4294c94420535795be1b2c9aa8c2d4aa24ba6761fe8da75a4ec56a17c70da70d5898a318edad957cb4e1351de914220d627af329db40afd430a403cf68892de9835729930fff6cb7ca32f20f38948d9fa61f8546d175baca56b8286cf89f260d58d26a20dac04b786782d98df4bfa7034a6556979fed283062b23d5f25acd46aff33e9ef03f5c5a1febbad08b1ceffb70048605bd578bfb2b78ca47d7505035198550013fba3df5fe725447f18e598565ea76dcbbca802b9825c484edbd0f31ad23337c2aa62cb852e082d337f959e172ee6ea58d96d34bb49631b815af38de81910815861a0dbc9a184945290c7565863698b8a5d9bd5a0b3c85cacdcc20c22f287ea9aef6834679e548d7cf33a244b22fce7a8cd24b98149325fa9cc935f196984b098f0e9a9f891d404bf90bb8d5adaeb7e75cfa706d33a19b08ed93f7c48722919eff3329aadc9c05d08703f366b241d84545fdd631f3bdf74a70429944829bda222a9b5b621ae80ab650fdc3c6b6b386fed6d99cbe5d341fc910ab35d075b93b5fd17d0572ac454e9424a36ca9f6cfc6232ae9b7948cade26081212435139bec4b9fe918a4a2b7bd918d15865ff7c5db8e8a57c9b509533cc6f9dbc369c33594205964ef047ed3032480f57a51daf0f50898532cce9f632b026f03ada662cbf2cb674d429a4b01057da37ac63ad80cca8656d30c98b0b30862ce6fd5e6d9b6c8ea013a1d3a002e2361bf80f831f9afe2d19416130425c4653c9c0bd15601955d98e9a683e396f35861d06943780cf4acf14d0f60bfd74d7b38e6a2f9910d264ab490e397c4225a7778ecc558adbf1643892aeed80ad5ff58321ea645da1b7983cea071faa7aa0229c9301c97806cdb3a0f1816914797e2a40a0cdd60cf39724154e8d7b019be4f8ed3aabfb2cf2e57bb45fd91ba504954643d926b6cb445e72f6fca556ebbf829fdb261276ca380ba918784a3ecb57a538f6221992f8dbf1deb492d38d684394e1967e88c9edddf75fa0260c05ec90fb85d18edf66124efa0c7b9be74316ee259c59c29e4504ce9210493254f3bdf19935239e383373e78daba2ebae1b34be290ab10cad114f2d5feb5cc91a21a5554483a399f0a7e3c65aba272033735920c0505ac2368071a91ad0362813f1f9d85acb68e322525f9e2e283d6306caf04331f4a8c4cc269f48176cf84f20b009f1f04b44ea3ef16ff9bc5f6bb32c718c465f152d09058d23faee9623309fd4780ed1085b307933f544ed547c536cd75fb0be641817479708c43a2eb89762eb1b9badb2b1c1951f9d04238908f342fae6a64b15097d621b7676d41c390b8d7095bd9bed61ece6c61a94f1c8d397be96f1e4b3cb45066ba476aeb49c242b5acad77635f29b624107725a87f625a56c2b82b885591e4317f2e5c6609eda1dc12c53aba8484a20ff98e164aad449bf3c2c120a184a4da60a1ce36a33e42a1e205b3e9717a014982065ef7651f87e40a41006103cee135290fe95affe5ab92143362180a32895485cbea8fe01de534516c6f0089418d52522c8702ca9cccb131c652db6139159169eb4c648d2809e2238822de9bc7b24dca62515c14a48659ebc5391ac13e56b93452f6118be74710847bd141f37e015c17d2c02cc0251cc3ac37175939add8107d648d107fc5144455026b6b7029187841ae63613486b7d9ae5469d1496e6a8684b3adcd404c094ab9e4010124d3dca930c8d05d12daf9b4ef219500a66a48512aae667e27ac4845bfff9a67b22cea4b13d9a412139221c42d82e4c60a8b211a2b847f7bc436b544ad30a6af65ae6b90c9545d59911c0ef867021934f5da7443a33c33b0156eb1941640a1b378d37869c616426570922458a90e610e468fd35d66c067fcde095cdc243e26b417fb15c6ca3260b08c184efd1c3c8cc9653712ed35eef44d34179e892eab5b45a8d3bc53855ed42e4bffef31caa3f3a8d17c70df9cd20292712aaa4e73921954a36cfb468fce2c9c4e8beda9042bca173fce9be524f9f2fc094bad23d3de37ed096776d9138a23a20b6c0789916d970f42ec862512a959dac4690e93cefef7b952f0fe2058c44f150c570d28f8a5d0c07bb7981ca264a988a2cccca98c449ce7aa59d3e5b28f337f46502d7d78fea8102b68065f16871130e8731f171090cb40f2eec39408a89c75418543752e87bd5ab0a2081e803a0ca141b47064c620c15654c7d4be212fb582d1563a995c587ed54f3a5ef12922c65df2b853859451c9381220626a7e912a18549a4242ee95a26915ed88253e5bc1af85e617e15aa965de3287c9a968d833ce6502129b7224a643d9bb1a01794f86a5b802b5cdea433c8fbc68f600b613df2bd41f4615907e308871254b23ef85e4ef9abc4d9dfe99798dc0aeb97d107fd542123a11b9aa592c84e2037e5ec29186971a8c6dce4e78d95922da1431f7c0ff28683fb5442bc64c6a267c07b216647693c3cbcba1279fad01fdf32d695534e49eaa4e86cbb52aa993854fa6e44ca54d3ed305e2a084b0718f6a0178d00e6ad8ea17050af510daca3afd2d714b6d2c553962d01d2ce5f168697dae36f44b47ca46f4c64de0fa6b8dfae0b2091b979aef9b116bb0cfaadaca5773fca38e8dc290a82de2002fa7cc6c02b23181c58431a6a99dc541727ee077926de7707fdf1e0b62a2978b3cdc463aac40d311f840e42aeaac2cb040171c24bd2a30f886792cde82c55240a2d0b5155ea02f92a47b324920f7b472f62cfb46f332c12c7c2e672f4a41511d9d11eeb020d95bdd494eb1b10afb4e65cb8cd3a18b1ab40066c03321172c42f4140353866a788852caf0006c957af148522e768279d766bb4a119e0e07134b3b17b04483f7feb45239c19982e0573d9ccd422cb00f52f20ca38ce3f7b41448c6ba2ce114626f31588f9b65124cb1b5800a0af29b8cf335d091059ead14ac5838ca1af54a79fc66c069e4d580d02d09443f4271f097c2aa332716f93ec3f53445ece18c16dfd03292c5575fd19f010ef398d8883b0c48809e9710a2b381f11c05cce06463a866460624a0e2d1709197620df4f6c2f602f643f023ed728df5b3510ca1e5d8edf365572cad7d2027a699fe8fc2fe74e008339a39af39087303c463846f981121ad7f05b26d9d90f06213a43eec74d473fc05f1aff4e217626ac68c8164a426934e04af43332f456ae0dc4a68d91d46fe5d8566c6fef467bf6608ee472808f070960e69fde29c409f142f233eb405258db8a0ab222b3e4cf6972645f59fe54c38f58c3b623eae44382978d1d5f1fafb6713f1e2eb520642022215ac9db28113a070c6bab209826f78da6c7bc806a4584507aacb8c7810afbb6729d80c161b370f0d89db7d04393cafbbbd8bfeec07bf8ed649cca3798a6d3ff53c2c169965b237629b2c5f15250fa6b0b34dba153cf8d2092201ca65fc21c4d10cdaaa46fc260f229b803dcb84db241403e1246ae5c8683f2df23158841e0864c574066cdde8e87aa4faf1906c522be92aaab1d44ac78f339a4c70056c8b6b622d22c615661d118a4d8507e90cfc5fd9fba9db592fdb7f28cc277d46cde02ca40a26be1b4938d77769256d9354fdfb6792397ec2a8c5b1818e5576ee83cabf445ad4148590890a3f1e5c43a7e51abbca0fca7a35239a67e7fb0f5190848ca9f610251d238027aa9168bbb0c88592bd10d99c1cafcc5d1ac3c2c9761bca28624fe63d17e2df1618a4d70bf73828b09ffedd886ec562e8861333f5ed01564996705df23f8f1adbd53a50d6f2cc6b5b6cf4af521395c060a3988e9ff9c313b027656dd6204e8e13c21a4df0ada3a9510477637095104a833c351f2b5f557c0c5905030dd6628222ab1d365900375ede0f8a72dc4191a7abb3c391aa951cd7ba34b40c90ae5d172ba63de98dd269bd9eb469564e4bb3632aa460893c553e7ae0acda13e96958f617aeb8e12318c8ae6b23501a4490ec5f68de024113bb729b9ba0a2352036f22ed279c4d88c0e9b04b3484c5583943977d161f6171c5e13d7f849a9d534d72d013a42d5818d6abceac463d06691cdf8da77a09232b2c56657d985cc49981b173301f3ba4c077c486a2ac5c3c9f1f3b1c7855b5c5ec2d9e5764a374b3c37ce0ef5d8108f08ce4cd9683037fcee0cefabba7f240021c2567013ce973851b294d156555dadef26711f46ac1e128a5299786ad49fe6a2288fa6985423ec1914b1a918beefacac8eb0d0531e753856d51b20899d1508753670a2335b8698498666bd07aa27149af5eb610779286d62ee85d207fa239dcd1669e61833a443333b28a0d6db6f1df8071895b16852022dd4e3d78a33137749f6cc0436bf71e2d806488e206faf1a8af7315222aa40f6113895ca4511361f1d1f91b8336b2a1ab9a1adc40dae0889b154a6653ec221020cceca2cbdb752fe8f194e78e668aca00bf0ea3409045bc51ec442f8dab980eb7191b0169e492e2c56e65c929a7ccaa5ea94f7739c66c1c0fe4550a3f0746a3987b43461741ff7b42e484f40fe7e6b424c413ba644a715259262f1b71fa80277129bb760a62c5f98991b2c4d6cec3ace91d5be7831b1b47c22ac29d21600485be28d4327cbfae15dd433b2de582717fbd1b0f0538ee668f88080704ba635b3b8f76639e9981a72fe70f8d2fdb59201f9cd69a36d18b8c7a29cd1810470fd9ab19f74621101a0ad822c69c5cf79324b80eb65f120c1133cb23511c7dda91259881e6cc56bee5005d4670e60c11d8470006d2d1f530fb2d7dde4cdbb910414cbe9c842c0e7ca4deb0c78864daf616c4500583932643d85644d76d4adc7870d0db3a51f31e127a23feb3ba42a6c2e9269f5cc219fbd3b3bf63cd9f192a783259a4cad6a91929e925e04946f6185ba87b1a3282158529f7c8bc113a219d1b5c963c2e0a2171aec49f9fe5294e56e00b77844a3f64c22ad1267e90761a89e11b9358e6c8cc286337e4c962df46bb5b5a39996c9347460e82569ad42ac872e3aa620f4900b1faf4dcf256c1f8d05b81aa68a728370a02c365a705604661bcee782f75100625dc6c711c1fc99b3a567299d9c19a5d346ef637284b07ddbab10da19456b84c44c60118937078c0f484aa4d348e971a90652d5a69b576f7a55818899ca1112c1dc30ec3d84e2a537445be77781479f85d43b58a87f464bc761a2fe0a575001cc13324125eafa786e14c1257a45da4c11fcc1e449f9fd8ecd1297d7d7254135e174e06f2c371f6bb5ee1118180d9a5b8b0f74517d10e1c073ec66aaa143091f196be9b0af03f115d2b812f508765fdb62c4b6b5adc71d30ba4bbcced166790505e502559145ed681806d3586084d94fb2351b28f11b078f45279afb3c53d8e292733e283e23124549ae84ffe85cba7703b15d161585bda2affb634c699c1935ebfaf2f5244e008fd03e0c637545890440708d0a8143f46a707a0271236fda05562e6143e26524d251c45b44bdb907d001e4b3e652dd0eadab133e5a3b3efa6fc45a4d09a9a08f81092e0739ac48b4c1e5b7c2dbe9ed7493e845a176ebf5a216f051e61e9357ecdaf4351cca4d1ca773b1ee3d3e5dc49a35ff7584f2c6422bb6305088ff8c02ea4c57803429c00de49dbec6b333789149eef1fee484a34528e1181ecaec326311f070b93171fd7dd1b9cb9e56ced4024c7973818358ba22d3a26f71036784a12cb6e313d10736a932f386420e1a0af80f34ad8827083805acb6108c6f3b95f631edacb1e65680e3e7cf84a8083440a04824a85b82870a2791ce3aca38911100a769449c77c9ede91172c403cb4bdf6420bc199833b2378cdca0ff0341e8e868448e1b2a033732b1da6ca60d9c7e64dfa541e40c239d9444684e52e81426534efa9ed624d929e8b842904b37f399c1cb8a4a0d9ce766221637d4db48105855d15b20644012d32c85cd4be2a1ae14f41c4d14d6796b27c6e8c1c0ee72e0797cb6f76625fb5d1ee03a29c4805d981a7d13c226774541562d8ffe522734eeed1763abd24099fc532fe0a507fa98322a56f39a87aa6dc7ce9d7c01e005b1cfd27956a41e7aec392ed358e3b1c2bdfe98e6a990434bd17f8f12590f08cde1ed16d75ef3f8a16fdf230d1424b36c67e4c6b5aed49ce7f959be4b4ecbaf4a1a0492a7e343e4f1d988ec0b5f987ddf18a0bd491b6b4a53c02d83bcb43b4e2c29b0d2f11bc72c7619d7493206934f3367f6f3aceb3f93a9deaa8181adf7eb98983120563dfb8e190fabfb13e0600ebacc9df51b4c7db53124774de234276988bc3a7aecf2ee022d40c8ecac6994ff325176c58a169b44daf1bf1c817de23fade073608f2020ad500c48fb575f9bcfdf48e9cd2d349d3e0dceb0245b114934b2d8c04de5661d27769ca3f2ed1a118750108cff90079bb90976fab8cbe58e0f9f00fc234fd22844a42e215db8648dd55da30f4bb34a59413c384d7a8f6f31499d72a3acd7f1fbd99dcf44986e547c9b2022204c3a51ecf4fd937e4884b42dc3b83fb62f12e19f4be4319c881c150427b7e29beb552f48af83059262856623a714de22bb4ddf94bfdcb08fbd647bce37e7404cd00f92613ac329549bd15b6d66edde3d315aa933a61ab9614a7d8e450887dfc985217dc4940397f33cf0e018237807496bb64ad5018a42ea0055da5049f730175bd1b941c0a5bfc8fdb12d6ec6bcdfab46f50581e5c5d1d18abb400d2ecab95a0a3a8bd32a88cb496fe383a5042b47a68334930639b6e37a39cc2426172750828f3f742e0f16483a70772e987099877d10572fd344703a4cc9254b9b3d3c9a6748e388417333638ed93f5600044354d4ca964891bb6b0f49dde6a865745db0eb1f347bbefe6e4197d63b15794cb1f2f472714382f9d87180ec0876127fff3b637481c00120f1007998a3f6958eea2bf69ff41550a10c40ce1761c3a2f27f4862f1758280e6e10b1034b1d1db90ccc90ea558e00229282d3e4e9d90415e8198d8534fc62fd5b737a8e0d19d890640c7bea423924f06695cbf2f788e5dbe55e8eb53420107dc34fb7cd8dde4f3bfec3fd2e87614ebaddb1d41646369138acdd9685d64be14b829daa360533ee65157113d048a642cb4a9a03c4e6f18f08f17e1ff52233fcc965205362a8412f609d685120f3144d6ff88d4b72187837cc18038c55214e258099fb80a0b160c4ed2abcf9b6eea524df3f79e8fa4391f7440f3712834002d5e13918f900bfc39c16f97a6d559461e430f7cf92cbaeee7111cac18029f7799ceacd579b0d42a7c6635aaca1306713921c6916474c4f524bbbbfb686957646ec918a06e3ec08591b01c17f1d8e625c36fb929924ec4a88f95d2fea6960da11b844bbaad7c03ae5860791dee533c245d503a46b16866dc31b825b893748e14b4720224a60ff4c788a6a087e6c6cce317af9cdd1a9ce6fcd4021acc60b20c5f464573d17071ce9ad3ea542bc9534a58ebd3ea68d3dde93942adff0c97e9fb6ce7702ef360586b33d2649bf0c4ed774af614dbcd3aa09b3993ef52d81d4ccab155416159ca6110280c97eda38f7ebe57fc70b81990f4091673b70c6e22befc5da54da0f7e2225a0fba18ec7a9827d594c641018cb12342f5bea5cdd2bb2d95418f974203dccc9e8af94a7c447af6d57b935dbaa976ddfab64c962b526cc7a59f06cf84a90159ef6ce0eb3b92833d2c9491187ce4530ec99cfb4cd038ccbe0c1803b5c06c251e8a7a8656a91dded5828ec55d802a0033cfb9747edae1f3a0d72608b85921e40d9de34f7fc1907e2c1610104a7840ecdf2eab0ddc3dd64f5dc6e17eade384721e0a62ca5eb0003d54b018eeba49e23479cd76af894e3d29d6f00e3367676f0505a616a75d05f03f1a1322addbeec039dff1c1502dc133ce545177e018670890a35d64e4bcf103dca5a596397cc792f578c66cdf06062c32085ac3738b6bc9d6691dbee16eb164a85b8e7f2962aecbb9ae17a820626d07ed2cc93ce4dad485d31655a23de02740627725e3a2d5041ed484ad10cadd37a25c36d86db58cebb8a5d571651d17ec314d02a9230933531ab94289a6fbf2822c4cf062f624fbb6f1ff8a23e831e430a4b78f44a0a19c4dcfed2eddaf08fe8e00f24f4082d883a46b4a81da8934fcfa9b3d5551c75fafdcf68b5905350d033e55906591a09917c1396219bc4413e30675ec4fa6a49f8189572d40381f1d53fb9c443328687e403ba931bb9b7dc72ac89802383a68eaba70290ff2c1be6fc88ab32d62d8232db3d85290d4949699b3778ea04c41134403ba5ad8936f33413826a9b38954cf8af73305635e1f7ffcf282d2371c2117b0be0a8ce4b1facaf8d76a8a967be7770c3de309c111a233af287dd8155f10985c033c703e41c04700f07deecd3f5ba6f5af2e20624ff2c73b04da8caabcddb10022c8842c21e9d06065cb20e7f3df9c0a7e78be50ed35b56afb2c582837538970467853189e05377f77cd139b0819b8605d93bb5689f510b42e25e36b17a0721bdd13d15b129da3b82e03c9ec9935d625c2e36e1095872f129b25b2f2097d9bef24c5b2b7b26808f8315e82d0157a9c951b7542addaddfee165adf59a87cf66d84c962a5302d8aa3e0c4b79b8eaf8fe2af92ef9bac3c7c89825b6d416895e2203f88a9e025c906eaa9e33f9874c11d0c0ed3f908bb3a4af9221c87709c3318a9d250e4210bc7b01d4c2e61de4b7ddd302d95794442345b7b01ec1d6e5a6a71422d30707948273b9787d2bc16c7cf32ad2494bfc2b1ced293157830ba77c965f5f54141171553e5fa0fa10482766a523d061e1c5f46e1d20d70428461b7b3e5463c5d0f6d46d64f7ceb88186580c6c56884b05b5f0744bb2165f6ac97b93ca650ebe70a341e6fd07032f1aea03581b25b2ab3c0a02f91edfe1e7d2f82087f079393a3efa0010709934df362eaa15e20be81354d776134da862f7d3a607dc93c2bc0a1a88d45737523b8d99c4b24ed27a8e0756cdfced42d24d11d9e50f153c9914e7376d61116630add58d10787dab5295170dd1781154f80580b2296c7016f0dff2fa0a7dbba351dc2b848ade144cce2504be810d4540b80380281c60dffd1cca6f886a47a9f1ef3801e90d151d1154180892d25a063d2a56b96ac783c12dfc62413179d9fdf33d1818124e927c4ae1d4f9bf8f25aa276a462671ef4a11cf0b4222e1721ec5f64d17d41de39fd885a68939453b159509d8cc334d8ab00ab360048824d28b79da9dba7da8559b3eb2cd090ab32f9689a2e0895909b40ea807f3d59bd98169e723ffbb6c63a3c93722138e23ae30f86900d918ac5179a7db8504920002a4605612fd8585700d4d62c836eee76831f0b888db59c58ffb77a1180fc505388b343ab353926dec99f7fea957b04a1c32f10c2674ef0d206440ee1e6fc6d2dcbf7a112043b43414d267c6631037324cc16e465b520dc24379f3aa35506ae67c8a92674f8aadc5f132b52c8b358b2aa2a6c4ca2ed6fbed9df484d339a067f278c972ddceb603affdebadcbe7ebc0f719a46e4dba8ef5f5495c89a8aecf94cbc7317f71f0742d3cf276089c11b8304c8c70981b13f11108f5c60046a268072edfb7d1ea94216570d854f5484961ddb377bc17f8ae0059f6173421ddfa9c5431869b0d1cb63ec5576db45c986d59cadd423a211b924568b97f99ed748bd4009f758ce23599bc7a81fa6694ec28941aa647675d28fdec6a33daa33fe158d6e1b7d5e1330d2df796470e1478b15be05eded45b03dce11d681e5d2865f3d3d070c9d711a22bbf702eca0359f6c399e36503a45437e2b3b2aba8b0145145e0a04587b01b88a742e7d64433054679bdaa28b1d31a65557b09052df66c4165cd424f03fbc9f89ef59bb010b97d974c2416367ca3cee55266ba0e9b11dd4c8ff7b4c338b8f6439bd99862a07dee9fa1319421247838cb70cb2f97474031e082aff9be88831fce1a8656fdfbe944b4935dcec3eed5ed0309769f67aacb29c7adfda679040d8589c4ebe56cb33ef7141e7db7ff3d4bb6941415662cd6616a7e7d0b9f066475a764e58a17f1310bb9dd653336cfc9d2f9cbf2b6aa03485516574c1e988d8b509695278aac86e37061b132b546b9826839b33305ccd196cc9a7a4e55a3e4dde815a3037217750eab75d69963b5a85f06f29e2e3aa135494137e47474d32a0174bcb8b909775e84490dced7b60b44f11c5dd2c8f2e9fddc03674782744a128584e9f921f0a5d35209f164bee8cbe4a1ea9934116d605e53d536aa730a96911a6ded5b9931c5d9446c0e17ab04ef6814a6f2781f159e5b24535993060b06964f19e4d89efdcd7f0dd3177cf0525dffc45435417a00edf01ac8051d211258e28f61a76c82e3eb751d524ae292be8746ddbaed3a0f62c2c2760ae4e2bc8ef97d6c089ac09709010c72cecf29a720bf718bf59fe2709729165115c0f9fa1d4daebaf439326d1e1e142e88088f48146e8d707c8ee4aae1da9cd0e47229feff049d0601861bac4b6ea3ce095a9ce823e7ee05da1b5c9d6f7005bb4b305dbcd10014f241f3e4e5cce03946f5ee11482844f992820fa64344464c8d20148fc7dcc0a4c75fc465e910d96d8a822440840e1a8365ec8e63319c534236eafd7387fa04edce1d053d51893b9eb95ba4a40454b4ae7f468bb16a2cde7df4ce72f1721ed57859770afef63f504784e95f7da17b67bed0752750f50a7641032d40badca62f008278ebd4ee2e7b86944fd62385a1c77f6617fca5014fcc95155a6b284da8f640a3c278bf7444f5430b8731fe42f628193cbf543c32fedaea7d6bffed24e65e947f67bb08ffe586fc25d78b45236bf5be19953b926764922f2113df087d642fd3e201d78ab3abb6e922a83794dd0a939744d00b359bc2d5515a80013c00f1d36bfc00c5e2e13f78ab060b6edfc8150b253bb18e8fdeeb33aaf06898d29ef6a7ae37ededc8e31b613752dcc8a2f9fb9a4227786e2aa631714610514e142f50c6ca161c42d201f005426f9fa7e83e7d8854943e337bd156aad34db8a6b6d2062580b14238e7a029c01b31aea228a0ed832cf65aa0ab9e8abfa81a3627a0abc5d9850c73b5c0ea8602b307a20aec08467b0f8207cf2ea52f96ae1b179f4d0ab387f57405ddad42b84307a1b47cc801adf4b9918300c3bf6c1584001503a624aeb07942f1fc42e47d5e003d1c1b0f658da4f501e9d9919c7d6941fc903beb062c18c7ba0899b8316e10907ca69936288fda1aebf399edc3ee9e315a75297b5c4e10cba3c2e468a60c079f66928d038b300612ea624818da5f301eefd79369c0c4373f196428ee8205d1a5766b7c84f418464c52157a62453c40f2403e2cc290da24effd180f31f8f571a63a0c5e70c3b9c5bc0a04f1cd67d1e5c620dd82b8bfc15e8b90fb0e4585457ebfaff61762092170a1cdc6462b3aade4de42744f99f8201b5f3d5b3ab571f55db7a3ff392c84793afc6a388406ba84aebce604a75972d2613bdffd77edb7ae89fbc717cdc7c4c70c84d4347f472fb34c96abe2bc9d0601270592d8377408d2c76d3440c04e1f5f416131538f896369c3a80dac3afaf127f6fff543ef27e81cd075554adf27a67d7ccea699a6d4cc627c1c7a101d08c455367d9eb8f6294bbd579b253834211dc040cc8e34ac72b7b2ff45516948962c93d74f89ca24e37f0c548fa8911aaddc2c40991129d7bef38a065d814b014cbbf9f41e232085355241faefd65e140fda53cd3befe86058760ec2b8dbee9729620e333239228965c7e2994a5cda298824119fff613a8757714c5347dc3d29f76e4558ee505ef4bc034c298f44ff8453398c0b29a797c3b1d8bfb34b8dcfb0a3af8041f77f35f0eb709f0d850aa9fa53cd16d9bbe45dd75ed7572bd505f36d62deef78171c2690a5a6aa2d882e86ff5135b9424bba1f511f73d931d9e8d224943d2e4f9c7dfc78ba1939dd522601bd80f254595d0a8ad27b6735cd8537ed3246abdce5ea074ff196e66058b2a1eb2b14ed06d75f3474df8ac7f4fa8786ed7dfb75e3c08854cb73f7933e30852db0a2060628579664687323bec6a4d795a6dd072dd7a281aceaaeef6b996e25563287e016e21f2eac476a0e89a37da5b24b1758619ca9d4fd9654a215cc8efd61dc49314650118028c90468dcb7dfc70f609bd6cb17e80ef929c8931ed5bae485d3f250b4c8043c8118a50a7366d0c150cf35b10f3206217cb9397f80406ae932e79031221436ba58290b89de280f7c0aa141da824bd7e1330bf7d5198575d0dd9857c09432ad97dc30db5ba7d8f719c8ba39c00614f10e9e445161f849748a51e908bdbba90a81f0434b2bdb8a20f5f8a22262ce20e4e888fabb229cd62b9770930ee4aa44dc07f650da0a538a85134be03ff4f9c5723a31c2984f53d6a2555f2b9880bc28a5707ac2a88022613fc1e454cc60c08726036dcd16a694e806ad4e7773bb0538fd816a997ad295dd4be28dc39d07768e1ebdc827844c51634af9b7fc8f05b82026849b2267b604cda4bfa840a843f66769aecf08f482951f3a21b882d4db971aaa5036eb5de65ca0e4ed3620b9cb0c6914913e2ae6cb59628106e9a4dbd8de4bccedbb12d9f540123a4bf9c349450e0e10272f2381f262819d12c406da1563c0db61448db497552f74d61c18461761a0976d6f7ab6fe20753dfb5c47cef784e0ad5bfa62020cfefdc6468458560e114484ef22a6464075ca0b2591545292ed3e30238543f3b08e30a848082cd4100858898b14dc246d236d97d1050c520fa7627c768d8d682fdc63db9ef4608b2b0d1008074a800298b5318724820c84d9c1e90eb9f0f8bc574e1691f0ac33457bbc9bcb7149195fb3dce492dfd6a61a31ce3e3a069d77d818a45312c94306b23dae1acfcee74a258b701d2476d09c564390c8b926c46d99999c0423fe0f669154e1a139756be4bc353e73b0d0a7703a47035c4a80f8bd8839739e22e2b830c7c1cf9cfc7442fc01383f479e20f4e8b4809f3f11fc7aa3932436ae92b004676c74a8a03fe395de86bc2a30179db5bb4c2c8e526edbfa83a7e63e428768dc2a1195380a8ea4b90e3e77576930fa008433da3b1d1d044fa7babdfd3020c2773bd2d93619a7b58878a5c1d8d54e9081a654c0e920bcce90a233d474121d3b5ed22901d5834128a392e3e30918cfbd8f756a223f5303f61685a0fe058cdd27c4efde1dd77f85ba1074c796df37b3ab3462c24c9012e08fd3ea1b37ec981426c87f965f11879f0cd9076f2c26afbad9b0bc96d8badf2561ba6b014a632453388ff3148d3170b60e0a7f620e030fce2df18598981510c4b05a122dfd01cccfa0d1eb3132a7ce5812869e7c1cd613fcb78d779c551cbecb4288ce059d4e4e8720da1da87c69d366b373014e05b69a028b249a183fdac9dc71afa336fc1aa0d855174e09b06318edf085ea832e9ba52380aef1b5021c2bf910fdbc7653401b45b1b1ffd256ba8522f2a80711dde4daa7c66963926865fa6f699026e83c37d55c17d734b3f06dbd3e53ddc4013d318bc76dc19b4077b10f1370a548bc3e3d6be3b458b9ce39a1277ce8c825c64b1d8797c117c2b777e9dd1a3e1652606bbb9f07dbbfdc0645d6dba36b204a40938e7a83e33ea3b5f98304796c29ecf0ac273d8a3fc61894ea38ea21da248d4172ea10ada39d9bb751fe1cee289b0804a5cb8506888b3f92202faf492b17697cf299528bc72e2a08088df1b3a800f83e5684ef2a3d90851a935a2c9eb0f0c970ad6f085db0485e6129396624d89f63704e17f56529219366ce7ffda18c4f8d17b14bfee5707911a72b307204fd9763c022f6898669d6b14a98e12f08c7ba28c3bc18c5a1d53d14904679f1e46d0ed2d8db733cad26d1d351e5244efa7068c687143ebe9561b8fbb12b7aa68af39d124b537b677ea76bc60d3cfb474811b453501ba26a9f4be5935cd9b3795f5fbf7575caad2feaec345a7d0e1831ef7207e93714aedcf17d125dd3f695376a9a7301e08984ed2ee38e76c2802b90381ac03591fc175d4f8572353bb60f217787c7dccc5576efffcf5f63248596ad3ee818d6faa7e53af7bce91f31d630f9217cd46a9597d4ea87a5ddabf17a0114d6cfde0df2b059987d36efaa4c5db8db0fdb8a3bdf6bb7ff40ba688e4dc08d7c45892a523281829ee882130ea5d4e39e602926fe8742beeaed2a0a1d2c90c5ed0f5716d526943e0e1d98ddc1b8f5b60b3d9a0c5542025646a6d123dc19f3440741dd292a092b6646469477ba2826307c0e01f2aefa2220c6eabb7452f65377401a89a1d7f4a55cb3ff7e20eb92d790250e5f468ad37ae0c6a33b79942455f7a39a8ce01b22cd31b0a15c4e49c4d70f78af92b3adbce15b7122c36fc50054c9121cb95fb703588da26a4a1621e1d5643f7d90f63b1266725f29e92452f7d627e02b6a9788265e83c2a9faa3e6bdbe69c38716c07ede97ccc2417682403373228af5f9723d5e0f6f45e8d2acbbb178a07927d0ab90f5ac5df6b5a67b410822e74256f8a44c27452a327d86cc86ac928ff8e44854892c35d515222787bbd2fa87c56738ea7f5f8ec95f63c109709fe309e9d21b344701f18825abe943b2b72a77fe2f33730baff222452109b50807822d642b8fcf64dca9df9aae7ae2914501b65fedb8ed13b9a166cd7788076d9b3229933ee7e064da6cb38e012d6908472da0611c6dacb1b734ff7feeea0cdf4d9bef31b7e4e03bc770d5eef722254a174aa5f9d53831eac5917900996e63eda06eae2a7cca6448cf7edecd0856658c608c0f3da4a1358d334d4f9d07d81266e6d0cb4865a44bfe7dc4392218ea4e07e1b62ee2de7595cba898c5de5ce1dba76c3a3f0a20db2698baf0687a08ceb3b9485dc60035c6160c2411d9ce7f0940d86c8a391b1d932546705b68bb418c30d422b8a68ffd8323a839a6683979f31d95f19929fe4a133ae262217dda6f11504c7f22011de72fafb2e5df5a0cc1785c3378c9ebcb563a61c56149607c7be1558cfdd968e1b340f6f5a3c0be4b3477ad9a09d09f025feda475dde6d22f382acb2afabd97b097bb860a46041bb892bbc3e1add8d662b011824f366dd5148f42e447bfa213478c41dd6f2b0d247fc104f4adaecab44ddc0ec035145d9ee3dc4404d75063923cc746a8d04c9f1a48d989df3e3c475e4ee2e68b72c22f0828e56c99c14f53939b96f178b17f8866f619c4d9053ddc10c6833ff0e6fcd1495c31bdedb438b1e81222e88481799d41b5c9b0eff3824a7502a94e692d9ee6139482aa7fa508e48e6aa20eed4c952fc0ece58efe032c12921bf59d1f6cf40d84d8c5ddcf52ec8644217df88431b212b015c450ee622c64cdac37c9ab1b548087848754cbb7c936796571307c48b1535e89fd2e58f8249f93c1c6d931e1b0e8c2e822498a49e0adc3722fa23a249fc0933fbbb881609390deacfec077753c8fab01e021763d945c6f62730dc043a58ea85ab8608bf9652096ca7d3a5c7d00421f0e71a61d61241c2657451436193d273797667cf17dcf255733f88d2e27a6ec8dca5c88a860a5eaf6d5801ecaaf9933a559459fbd8fe685e3b356b4b7599909d13ffcc442410cc4381610cd49150b02346d79b97d0a4f34f7dccd1d377b27f7b7c64a159be28129cd5a279b9226ee4854b079188ca737ba461395f6f243aa80b992c7c56024a1a017d99cb8e8772bf3bdcd07861274b1411705bb0dbc3ab369086f11bdaa1118929603200010a5f76d2bbeccc526321777d251b9f9b7a513f2362596b93b25a4ea2723419e8256f706bc047241087f030a3cd8299a95bbfb7ec5adb4037687aca5c14e88e3a047a8ec3742b367399830bb1c8951bce310139b831d52de51bf215672f53e47cbe208243d49ac13455993b9a420872e8a0a8a1680a44f825b61f759608852b9024abd0345c9d03289ca4b9907b09573422067fc2917b4413443f40b5e90aae5488ccc0febe58836a015e59511bc0e23bb6d44375f83471ba8f146872ef3adc27096f8be34c37939099326dcca450c6eed6cef048f33e064310a946e02cc1a8685fbc9a01292bd9626c2e59846a48859e4e05683daabc65627e2e3c2bd5de471c12e04e945ce3b9e7623a49af816a8d3e622d98b91a3aff59b9a836b490d40c0f95d1a1f30cd16b0a203c9db5806b94437bbb02fc74de9828ee6aff134a6a647ffab5a965795c0846dd98325025bde53ddaf2038e65899da0439e293cfed5cd88b56e779ace5b403e85c5a07371f837606daa6adcf8e73e5698cbb694cd86a941d1a3df83aebfd0746498e7724011305deb256c081610d1ed5d071de90c1937931c673875036d4248b8aec2c1abb09b4a5a321c7256fd7d8570036debb9f1359887204abda9d853927ca300bce3262038d976a4a33f486e95bc1f82992e31fe5dd700605d626262d4ab013028b12a3954db650891184deea9eb0a28bd4f5be3ad88a0d211c37ab5d29aefca456e2758159fff9dc193d36a9fadd7519620c3bf3c7e0ab9d589daabd80741153ab415d323386a0aa4b481af08b302034731b545fef51607630367155a2e7cfb4fb7d096808c98b526c3e0cd7f27125cd38bc24ef48d0be532f4058acf2e9c2d2e4cb4439aa08300203aa2ef48b9c4ffc7e5475d03fe21a68684fdc650aa916de7d851babd9999310409e0f15cedbaec864bf8cc4f2afd25785dbf7aca062f02e8433311786d75be35346f19cb1c1c41627f6286252dd16cffcd44b3f05b0b6f7aca3fd7ef3b8371861383b7efd05821484c0cce6f4e7be6a8b497fbd65c9eb485b1a3bcad7092505292a9f9c38f4a6f0e7a88f6edfd38916c00d52a2fedaa931c01a4fd3f7f86d958cd8301c1fb093d65322778799eb28108c4903a6b4273942348630205ba474640a949749cecf3b24835c41fd0a93da229bddf7d0bab8873c35558dbe7e642743f63512489615c9f72ba825bbcb9d7ea586cdc5dbf1a6e072c90080a7a820509af1f72e0dedb949c37de849caa18238e9e2967f57d971b5fe2b4be2452c6fc89b2339fd10f6c8d21ce49002598e9cdfaabe475f9347292187c8460e41841f4cae873abfbc85c02d6b3085a34d9f7286fe7ee243773eb6cbbb6ded2d0cc84c12b473f8afdb50c2df47b53f0795ec282fbc0eb9c785a4863ed7f23cc1ae18742447cb2b71ef3cac9fff1ef6b48918fabcff4e434a6220cac3f2031846a05895a97ce3c4c63672e3f00453ad5a8b467dc8f7eff1b1757155e414b960e87979e300fe4780ab04b86ba42d07840c32e0a520409870507b4f4fa93da0f5236ee49c2952838520b6933fbe5501a59e07761d0717e6e00f0457f7bb0efac00e3c7980f61371ca5ceb0136d207ffc289bffde1994b3f673676a2a991fafffdfbbb2e864a7c4870ca70780bf7a6b3f755516aa0f0adc291f955bd38a6d88a49b6c6a42ed3dc390c31d5d9804708b02dc6e35f0c1cb57da48088dd8e91596e49344efd4969604ac59a4312245668d9ac503d4d521423b56972c710c7b88232e5525418f37c2719d943cd7bfc69cc81bf7c2dcba165dcf83388c33ef15f5d08a048780391627878c714211b71b53176404f2cff43b04ea6c3723240b5697dbfe0cc9a3fac6f67bc8dc97af0d35eefb68f6b93c0fd5e25e1843c609c1a9c7ca78b5c161ce4df6d49edba7797094b489e9bd80d37eb20a461bdc09da8594109cd5d5460aaf5bc8f61a08565f535f0bca958c002a57ded9534d71d10f2f49980fc2728a51312b0008927c581a687f6cb7d0e3cb3ccb033032ff33199d8bf0a20de616e851034ccfd51f8405bc603fb5c43bcd7d68834d3843577e55d07773d4583440b019f37cae21d2561224bd92f7a35cf481daf3527a0ce4379c45530a8208212fee940ab9c95d6032f5a8c0d2867f4320d48a2833fbbf3476730c96c989c0128100c01776d9dc7a4c47a37ab04c1c9c36042abdf9238de87089135fc7c3e609896cc036a556cad9f74454548b87461ae584772b7b57138f07a22d15c19758d9d2909dced1bd4bd58cfba01b2d7374d4c1104dca17d1ae16fa6f871d5dcfefe7707e05be8bfbb278567becf1b544f73b56c44d2f542342c408f32fd7c609455e3c998e8cef10efe13acfbab899d3b8b1b99ac88913eb50e3b60372bb26eb2c040e175abbc904971ede858f5aeba798365028ce74c3a7a22e552428055db2dbeb883ee37f4a04314c37c6d4c0741c82aa28694cc0849c0adf0eb8cdd9a322ec53a92a265f73db1dfab00f66f87bd5d82858a02e7b232ef6c33000eee45780985c958de9e4979ac253b60d7963927a8def81c92e5d47cdc459ea596c6141c821ba8a5e22996f12b3299ab799dc72083b4f78ef8e0cca79d8826c1a781028585f0b155a2a7ea8cb8329ea259c5737f7593b1fe4d9a96dfe07f59654f5d130c343ea53a38a9a9301f3cc596f853796915a0af109e4287b7080e8b37f085ac39a512d4f494507adf258bb9f1f7a3a5d4befecc96f8fdca736b0bcdfe406b4ae19c43de01c097be8f4cbfc5f04992452c06272ad1e500bc63d593ba02e32fa4cdfff4a830dd6e88fe326487c3704c51f42417961f6b411585177ae0f9e7be13c7121e3cffdb973c3f1d3c6dc559dfbe16b2dabbc9c34b5c3361d098c6daad4cd1154ef0225e2743df24f09aa8ac4f2f5a55c95955f64d40951d72163d96b200374a4c75fc8a41c4a71bc20a1751259d95313d1693858192cbc6f9c26de0e544376a45e2c64b5bdf88b16ba374b8025aff1bb2c7572ba030bc2f771ab3bfb226d8537b88365074663ffdfc5157b83cdc3f27978d02a2a16200d26e8c83151fff465845425fe8dd86b8c7773d600ef2ce50f6c75f0badb28f4912a21769b619df0d460f8bd3cb9e536a6569705d838e6da6921b0506c0aa65d5982613ccc04b104cd388d06af82932414a7081be45b070f18c2055d80ed69f5151494eb72a7e66db3f1660791bf5da4fd4938466a26d83aea3535a43d6ab30410d0cc8f0992441b10306f0bafcf4599ee9b7804bdb1e4f6ba2404810484226600775f4bc9d8370e46876ffd20078f1b242231367a04d3404d92ecdecc2eb643190237bab89fe533f293bc62c7c685f4dad40101cb784a8df534068b3da274458cb0bb1e38d678b4061e369a50d4e47629c404240064105d96882c000578d60a003c76fb2b6f63a86e9ee405650c0f4b833db812154914550015689d0988300edf7f75add8ff6b59bfcd087ff10f9a72d0046b6abcf300ed2795e7e9f727987aa65f343b9be49ff7c13d752fbacbb84a1386acd1ba032e4c2e620f6524a95235b0e84a40cf68f2e1a4acb04cb7c1124b67bcc470e344089099e377074af8bce7a6b7b3c68a5761df7c1a91003996013d1be219ae26bdcbf79836a44136e376e5313d7195f8d78f50eaec287b5e84dbc3065920d55c2387f9f7beeccef202b3eb8980c22feb61ab3ecd09360afaa1e26441f50617c5750b1226ffb140f4bd090bc1bde405d54886540993af696fe80583de1be557a64cd2838575a99a6ceeddb559bd7b932354717830c821d9484d510f3e2f9ae9ab8abda4a468c94f37072235ec12b118274928ba03358d97ca80bc238fc3530afc7e1af805362722dab86b23ed5ca022c27e44aed81d31ef1bcbb400c7a99b8300d395f442b3f77f16377e58c23a250eb22d2fe26af86f5e092857cff21438f3df3617c870e81742a20e031fb21eb438d8270b8e7852e87511d52b40a7d54c8e31ffebcff6a90ea261297f13f240e6fe885cae1ffa29810638f99b668ce9a685e13c814800dc7357e97399c546813c04cae2ffcc315e531e597013634426c7344d2dd748868d617e6153c3999408c46792e3f12781642c09c3cf2a59a0cde911fed8540c4e97d58de79c3d243925591e738ace66803e0bdb1fcaf6b00501af1449601587e37d12e5df09a33f7b8aaa79e948ef0f148bb8661fd7b1a6b5ebbe03600c5c63933ffb85fb11c11a99daff88dee9f151cbf1284c9197ef55068011c5cbfd2af3585da0410aac4f21d41ce2d0e3ffdc86f4881fee241757f067919faffa27d1c6e146a973c35368798c9fdd64ecc506cdfb96066fe80efc0307f30156845354474b89f41aaefa3e03a4631ffad5358ea5d7f1f8e52b76f4947b2f803a7742acd202ac7c140a9bda15e2a8d565cfa74cd57857ec090b9ffd415abe6943abf93e117a63f126b99bbf0b421dfde6bf316bce2beee03ff9e03d0492b19ce8de7f4f9608673eabc7e580b10de4df6c7198d4fa9d7d1730388cfc149527233347890e039d961f888959e7794eba56e106022a6f4095ff2f9cc8233cef8a51dd81637bebdc83945e70992f1ac1b2a46705f0fa22652a8e69b4a65276ec41416584602aa0ac41cc8423a5f19038ad67463c8c9bb1471aac683f062e92817b24c569b42187a8d06e34fddb527f1ca085f204ac7df615756f3af52b1019ffe330f44aaa80532198e4f91bc43bb3c3f530844ec5c42388c899c410c7e08823c74944da36320d9b56b924da3cc139c02d944e91189b7d71c0365becb9ede664cc0ce49d5062aa7576f0360d962444747317ef980db89f77b66d6742bc80083a0acc3601979d21f8b67406b099350147536908c7c797db8972a6c75f1a17399200a4a8d2d089301292f46fa983a2523b17fc71a23d90b38f9059ed90b8c1fa54a71cd59001e77ddbe27639434167b5a66166ce11308a9291583758ce1beb877a652848ba291563521ac72348ca29064644faf4212c7e6d0aa718efe2012717e62c8b54671c74dfb8add90320123bdb50901f0eaba445cc7c95df300ac6aa64cdcfb5bd21b2bd1e6dfef48161d9b039e0971aa61e2883a92e83323c18e2923a7128ffd4763caa2fc0beb1bf64272d1fef248eef8d6aca165e1150b1b3da23e4b83de2492b0141420b4a26f1cd1958dfcfc0f8d7315f569e9ee8651f6e6277592bee0adbb7cd1c3dad7f468ff745952b368e17832c22405f0b0d4473a505d0cfb62f6b5f376fe994de8d8eb64299787d0ca80be8c6ddded1f0d76df598065e1b8cbe371928ca3e551a60d5478d5caece54f834440e11b121ae07f6106bc32bf948bf1a2516b0ccb837b7f6405eb8e597ee31a0248b9230da68b6a57f92b303eb857ae41f6c06d6c176d931630b305f068b975017647ba6306706d6c1f4aa98d32679f09a661acdc1ec60609c8caadeeab00dd8c9197ce879e48ba6c05ca3e9c7c8717ce99af107312316e8e8fea6ce61e6c10e0de0dd38e5dabfdf10ffde7a9494dd03effac68d9e40fa9eeb0cd410c3186d126fcda4f77c95a26c55182a365494051a0c7a46461c93331905cf1c851c51ac541997092554fd93e4883ca29111c775a63323293488e1703b15e3a7721b758555769b108ee204f64a94d95672c930b4f22a8299113cb23501b8a27b72c0f9ef2053d88c3c6e418c1eb4378f200efae682dba25827eb2521a01e9b412de35524a1fb1b5393421a19cc26b72515521d2e7ac72e9a42fa477163385703a64a380284a5eae7ac0333c978df5cbf23c07b0aa21ac035db763224a5d5a1b6d8f392287f82b71667f49b6b948b4d80139088f1b96a8595f1dda5baf13612a7f4f58fdd31506781468e9a7256491d08e08f67e2480d19b99e92520c5081df67eb53d7cb4afd4e1011af3420eabc1efdf1e6ef6ffb1161fe2b523887729840149d27dec84711c5542cf530c9c792331353bf046145ee41591adb4cebfc197b55cad207ee123bf01f928e31d2f063ecac733dc84341b0a5d104edbee9b20de985067d098a74cd02202c7ef0b37f66a61f029ac182f94f3c69f0687028c43a9704c216d82822b4de17af5a19e9937417c98497635c840002b0471a0a32b56910183c1168ce5ca30f58ae335c9384ad07238e41ee70824cbc8dccf1dbb7389b86fa720bedb35c874de6b55b7486e798653d3dc394d3ab2a6546b1e0a03256cc9b80aaa79b63f13e7add14310f777232c442c57c442ce546d449f627855bfdf2b9998e15a5b28257c04c616fa8238caa610027c5d889d2ce3d55644d06f12d9f799c7d4246e4f5e10ff41d5c7235106e993ca0140da254b036ca422a3b2494364643b0b3141fa9caa30f77068b3e4899d3b90876fa5bbafdaab07968b36ddcadc961017e1a37031031c0e35ce1e26630c54a9dbeab97dfe0cea9b334cd834d5be1eb0e2606244cace2ffea67bbc3b86f8c8894553b52a86b234155deb24ed2d82ff296878f26e0e35103e1b9ad0b072f39a97ba425118aa5ba5a9fa0775cd96de29323a5baeca4dcd320fba952f84a5fd0417385ca28f8a01edfdaf250a07cfdb9508cea29bbedf26512cdf41a12392a8e461ee36fe05a3485c635cd0d40c4955242a23c7ee02b40b4d7fa14fcbcfe2f566f5981c29c458b94adcb7d513c66e2e4be5e124f04e263d6dd6aa7ddc7b61cecbb9eafadba8aff0e8be3efa82f5063ecf2b861de814941237db554f5545979900cdb2633cdeb08bc4358e04e8494d11bd4a7ef3fa6317529923e991c4bc80cace4fbb1d376e170da9f46930062986ed050adafc2ead92c96b281bd7a834e3f827e7f521c4ba88f12c5d26bf368c748bd00a31527416ff02929ba3b1468215813e5c56e67565ce8f5c792b5aa09d1446fa66f58238b740a30e26c701d91e056c08283cce61db4381a0b1e60469ed7d00b65ea22dcc027afebffe8746f9076ec959e38b1f2818a3b1e3c5ea9fb4bc370d15b5976f002c88232c20f3adbd272b604da91722e668d5636d57e5964f4792b12378b0d670b06fec4b72c83b99d3159f3062d38e6d834cd3fa09b8b688dddbfc24d926cd08aaace802859631c43118972e16b708b02f4397b8501333b8374d07b6175622f3798e32e3b70583f51930e3498e7529292026a419acc60a8d8e17bd6eb1e90a6089580e6f4d4371f796749193112e40a10b8df3027e233dd01a4b66b0c572e7be9b797f8d65e7f877d8a688d157de3f36d85a29e19cc64986ebdbcb55008eb379d176d757bf528270c0f6b41e9edaeca341b7e68a0f74ae0e05ce502af5c275a56d5cc5fb8b77cdc87aa4b61c586a985fddb29e0455453143e934363d9fab03a74a80bd31f09f04b37960fa05ee8bc00df1cdf2c40fffcd5812d438dabb2956b4b53851298f333d9ce99aed2ea2f262405b6473b0510d5e4fce875d01a074501daa94c57b91828ea22b4589734654ec46097f053f2bceddee82e278ef26b28a86391ffb9bd5750842a00d51b6d00408a8c850ace1357e42727ef345a66a65198db9fc25382192387caa0912c097ae923e4642c4e6af56459f017ba28708587585c7786cd2e504d7e1a8f925e28032992426c8d47514d0bc07334b64ac929d19fda5aa3617f1fa55422e772b47b234525767a0d26187033e38f96187b87b7446700f3ab69fb70197c2c36b65d86e87e491c951ad777968685c155194bac29cd67d01725d63bfda30f9c69ee2b05432d54aab1f0ed0c61b2676ccd117035ac19c99ad028aabc5920fb6f3d223a1dc1ee6edf1bfd99745c4291ceaf028ab56e183c04064c56a3edbd2dc607c6fc70bed93c4963c1374762847892dc3999434e4b7049dabcacf09beb670c36d09076e6b66496237b9caf248e72556007cbaadc408bac478814e799ed7cb285f849f9d2e7c7e75415df2e5f42465d2b294f7d32671872a2c21c1f4e502057e73d56319c41fa6eb7f6905b38853e3458282897e15e989461722c9ef35f7182be46d04f8f8a9b72513e71d684412bc591e673c08ebd5346228a140641453348d3182278e7cc65392f65405b96364c2b83cf998e25d1512e239e1a7e23abea869217ec0560f1869f1ae2a27b1786fdd511f1bbe633882542f2959b17a1e031bc00bbfc4500035324f62987b05ddccab55dce6ca37e2342a6c97a3d2bffb3f58d70011bc731480110f9ac379eefa500609f59ccbbc195d917a20a48c83ee2878fb8c2cf7a3c769d6495e030841538777972f57b90831b3c33bcfc2b6ab22038618d90e318d7c01b673188aa5320a8a7719635f5609f7b0b626426d88ee2d49f326850094a875a199ef345d78239ce12aadcc5079dd8a2a0d7a1f1bbdfbb0d84828e2904be96a396375de4a58170a03a5fa6bf4c11b4fc6bc99e8208ae96273b5b58bdf5c977e6d833f1aeb6866e3b1c923831339f76e673eb9a1dd8a36b2089201ad8c4cbcae92232474673165efd71f534250f3474c980d93ee909393b8513bb17eca849fe41a0f153cd62856f5de9a2d98c49423481809b464f6681d39925e9372ab55afb11acc981055f272f8c0eb96e3d89b1fb302c0616970e0abd58bfc974b98692f5ca65bf64be4c082b3ac19f8bf9f87e3f08983d38af6c7597cba8865b21d68ff30d97c34c9c8e957a7e9df6b8bbb24eb764b9b452033aa330ae20dce07882c5ab53f0e5e8a0807fa97162e9965b25460c734f2c9f89fea5df8a84e53fd80d1f8328add271227328f8c5a63f6882b2e1ed17113f3a6513dcceededc2e9a11df1c4170ff9fd4be0cf0e99e18f08cb86421d2927c7646d8fe5d2fd396a8c070556288d669b1e1b51e428c56f5820579f0e8826210422035239a080a1a7fe893e9165ac07fcadb8fe6f96bf6064243061f0a8a8822d33cbee804f313915f4903874032815a2d49d8d2a6e35a01d72687cda576c9b8ed5b190d8be83c66e2dc187bc523f64041e93313a5c76e16fb4d8f4291e33a15c626a2cc719c6cc8f407ec9dcb9de7f7603f84f9414a3af2d481cb130ca41d3810b530eec52dc5220e2631c231330903b987a58333cab9fd48186efe058fbac2e327a3b092308e9878d6778cfa8d315834729a3d4fc4f93579c7a80b80234c5b57d16ff568f2ce3eaa1f6bf8aa53a3912984f9339d77002a3af52ff2c8e16ca0b7609fb5f8d566b8aca477c51866630f0a66e4226e6730d1a5b530638abdf3398513a219edbc19ccb5d08cc3f9addb9a37927e9d6120c1ff34d97b8da4985923405b5706dabe87080dd421cace11508e9f85be451ac5a07663c18197ff25d46400cfaea22a3cfaa61dca7bd48e50071cebc8df346f94eeb2941e7c4a99eaf1413160ab49de76c8f41685ec34b2b85694e03d6893f4ab0bf8952306041d7414ba52288220855b1a17eea810fbcc9cf9e32fa51231661b798926c2746d4c6a6941d8164df386601b3a615fe30ab6384859412afef5c3b319f8f3731e6551c7f5a371396f6038b4b554881cfb3eb8cc3b4c32e72aaeb6f1f1cffda861df90417612933c1b6a1fb8072d83b8cf64d6ab61395805392ba4111cdbee60b591dab0a0fe637af4017fe005331c861343c9b03ea749e15bcab7a79d2fd800a73726fca4d0042020bd35e01b097199b0810f8ea5ef28fc7c15a6b6daf521e612bb8f52963dc240ac60f94cfcfa269d887e2dfae7b1697b2e594b18a6cc31c8ef75f3f3361fb2eaee8e2df6af8fb0e67fcf4c867e51a47d72d6043c38dbd28f9ab520986d93fa8a16af7cb548309c55e237195f1221cfbb70229c87534112d98f060651b09b5c70bab5dbc1621a1cf49904590081715412cac94363db6a0e19c0d67269ee5c3580fb7f0016548d2c5ef7309cf28e7b3fdcd7a54cf448d0070a4010f590815c846c50b02a07e006fa40fed32d680ef295b40898d899f4534a9afc1889a87cd94a59ec6c48defef385eda0cd850a6398c13d8a8a3cff1d4f414a5afaa31efdaafd27072f9f456f9f1102830932522f5f992c8925cf4763ad00a042905a377a21fe01503f30a87aad66c55a542eeac275eb23a355acadab3ee02b022ec0937a3d062854a26b57fd65b6b9a7935d4910865b7e09c9a81b729614555a1f97d8fe82981646563a39e6f9f479a898938411bd861bc5ccdd19a60ec8c6859235da5528e5361c20818446a3c4f2176c219669721641c22db50a0f6883b5d82d33f0ef4c73d96d7562e6e022af81a15aea512fb442c5140903b7e24864c8f6554bcca8df495b04accce70cdbe3dc6b025e2152071f03795876d1032040aa3a9da202f92f18af9ecd7986f0bac82027ada1f36483a9dc6fbb52dc2e4c4334af01d247e28b136bbf2b07e19ad315ca9d7eec4c07443335b8b10a60ff58b70f8de156e8464eef41244fabc00bd296a8f8f0745df93a7f3370bb407c1a9bae4d2e32b3c75e1afb5be2ab6d3e7c6c3a0dba0ec0dbe1f7706139d308563c641eac5b7db902692292def26fe1380c63e566322fbf65ae22906223a0c390b03518043e943c46e209b836dcb7edd50292a398251976b96414f731c584624043d116a7f8075c9e4791df164266e9b6492e58564803b215b207e7f3a374c103fd867ec654941ab4136f886672906a0c61fdf7f576858d20303dd7e014ad60d576565c41c7ac9b7335bb42c2e3d4fbcbcaf349c053ddae0367570625e4839fba2174095b0c968b6ffa1e84f32f0fdac4d788115fc2e1b228ffb4fc676521f09d2a810d8366e97aeb04c02779e94c0bb920849a3d9bb06d47615885084eade4677bf7e033e4f0be5f5c14e3d3c28d236071cc3ce8d343826c38305432455eb2129d98b3e6044678d3b5c3f40a5e0a03c52ff2b206b3365dc9b8196b18660c71936c606cb99efa3da9363027867ba5738f7afedea2ec4b0c9f060102bc965a0fbd31efe643954629ed127d9c2f56e014718b9599e718fead709a3f458723064797d16ae9442f4706d83b7ca4443690b2ab2b9fcf6b047812e32803db2b157b4fb6f6106483b8229689d48624d179df3890aa1d5c8d6483d3ffae285e4358e03c8d70160f2f0b4aa255ae68f2b82c1c61746c6fb83af6a1b5e440e2e8588c89e3fd54b5e245339256b5d4f35623319491cb386e9d16993c062cc221898c890fc8b6d454805fff18ecb26eb17c0d0bf0ead937da0867b125781a299ec8ccb258027b38f72bf8ceeb678c3e99040642c28e9658e77bf668a06de491c2a4319281d881bbd944faf1ecca7fd9591a18ee05c4a1b86d2e0dba8da107387bee066275a64bc15ebd20b4db1aa9710cd4cd108c318a71ff313527594f26638f9c6b631ab5eedfa9da00d267f34040c1e1d8b26599f26235d7d126c73316d0107beea2313041bf4b3f588ccbf91bf8fadb67cfd6e6f3017c8bed19aa0df40f24613d825d4a481e4e6dbd6f48ba9257bb6f3f215978506115c7213602a89be197a3608c680b8cd9114e0f1eb25433dcd4a1f4daec3cd39a83f62edaee450cbe1284b003172e9e2e1d7ad854be60b6865f4a8335d70381196739ad15105f2106467c1212237a77b49a9805d1b6ddb0f6d2fe9ce621a985085c71df3b4357dba226daa480402d23d4028c0b48581c7c1da45807d205067d3eb100bff2dd26e10fee1b04966411174f0f6e0d8d3be376d27b913228c2b6aec48e7cc210e4b0ae35f9a429de563de2a371fb93a4f36824a437045cafc52532728971ec5bb461c281e87cb8f56dc3e2079129624d69ae525ab9c3953ee7c56422d6645df32a4b562bf1c84da68d61ef3f772c7ac71860af2d15f13b160c39069c0164f403ca58bfaa59200ebb4a070d854c341707bf3a0476b5796faf4c957e46bd2c81b55a7348281486808004fb94e489a36ac8ea073317737060407001e4c75a64ce15b2643d8560ad8cc6d7c1d3ba7d7f8a0917ae6d92ecbdf7de72cb2d654a29f40a7f0ac00a2015f087cb9fb242e5f25beb71387a96cb6f73e566dedc48ef1b8f4aa3262a8d37712228252f5f94bc302d2135947ed251a0f493a6d246ee74e6f2cb27a7345045f87dec71f9facc91bdd049a9f4a6aee7efdaa59c33cba836354d7a6b33a354d36addb8c971d29bd3eab6715cd779dffc3ee9fd719de77d1f08a6ecb4567adb0f4ca5acb5b1b9c1993838d21bc77a6de3f5cd8dd738385ee7e8dc98376e48ef1b3a1e73385656a93cc681c3e30c5673b592deab71de9837bc0e31f880f4ad18781daa3e202aaf431c1f90be3683994106d23b830aae846c2daa2ab812526fbf07ae2f398bdf86e715eee1f73e40131db7cb99a5681256fb80b4284ed66a057d098ab3b8dbbddb5dca39b38ca5797487caba1cd7759e67c1f03eefb35eed3ceff3e814ebff7d3f3611f4c0efe7c60bebe29893ca49a97c80983b3936366de3efd7c591021e9d62476b7fde6cb1ac29964eb1f23b1b9b023fafb399d2c6c6c6c69b6f80e3d055a5ae8ba305eefc6ecf91f2fbc9e8a4d2296dce001c872e3707f2b9675e7fff9204c08cabe41bd25d173fedba3802b95344ba19a7859d53497675d2b0d2c515dfbe610cc1658730a0d8e88188024e62928a5480820a48806b74d658777216eb185df247fe4cc7ece12fe6cfc419faf838ab7538ab878fb35a8a0be01e8eb253c7ad97e7f5d15f87b37a38ab75dcf67156ff95610f7f4d1d3efef267e90be01ef6f1bb2e1ef1ae55715541c73cfccc8c81aeeb666662ffb6b06861e207326882f8828408081f52e045882dd488f55bd5e46f76efb68bec2c74df6ffb55fddc5f3980e38965e9d275d670771108265d762872820c199270c01145325c41c512359c11ebe7da51fe61769977f0ab9f99d9ddfd9bba39e79cd9f7cda6db40d92b52f07084093f7881e202505030e60a28b0b800c40a9888410a1a4660e840868816581441b4469a24317d458e6b8575588e58e7b83edc63a208ca042e4e986082bb9cf054a4a4e54c132c5c49b2e79cb22b7bfd9eb31c88dfeccfb0e3bc4164bdd79cdebce3bc9e4468beea9c72877b5aaef866e3bce211ef0edda1ebc35d82f1d96d7ecd9ffee26ac8cdae0f7f410c931ccad860440ace380ad3c40a5a90c1840f42c4867a70e10ba32d36ac31861633361821260b2753d2f0428cdbf7f6edaeafdfe61dfcce6de3efd91523a0cc602106349e865290429636722843ca1a137e8cb37347040f4bbab8029acf80dbba50782882892f653091c5286a0a1dc6b0c265053e1c81011563c498018a2e3448f1812515144da9c2044669486f2e64b3381ef158e4b9f1c4f6f77c57cd1c99f8b77fea44d9912747f338cba794f31ef1ee5013261d6848c114470c710293c1e54b1334184d41c529d6cf551e75ccd3fc95c753c2fe2500171b7a78e2046c6c299259a28e30a3ccd30aca88f51366cafc40f4a4030b530c4921a20541b46102349cd6b81acaf59c55a3d8e7e7b7ab7e8eb9aa6c15f112a60127317090018c333ea010630e54e1850d297a688309333166772539ce923b3b3b3b3b3b3a5c4546002222e4afd155640420222ebbe30f00867db0b4b37377549d976559662793e1eebc23abf117da31bae38ed1d5806a48c48b52c12c1da151810b2840c164e8072e556ce9a10a26c258112b0265863154ce6001130f212843430c4b616c31521303025e1781b8a705bbc9dbf137ba3f7d977559d52c32021091cd4866f5b6d0318f55a9542a22413749e49092994408f2978a7be45315ef002f1167c917410c52c18e2ea65aad6dd7e59efbcd05cb3df72e56eefa11d7a53dfe3bceca4256b97c0471f964485d96b97c7cf3dbb9c746b297afc9cf5ebecc6ec636d8be3f0427bb1d7f43b22eab95523a660f821ebaf23329ab6ca227662d3d16ace4ff0d57aeebf5f029b0fc81d274fdbb2ed500cb496b7cb9f3adacd2325b9674b9ac7cc25048fce57da595fa4a6ba962f3465625556f55b55ad6ed9c6f177cb99ce48222035c4e5a43cced2b1276e33de77daca3f56f2309bb21ead48a63add18f6fc8f7f5fb7bf99d5c795f7f3524f5fd3d849deeacd55a07571fd8fccd8760f33722cb10703e4764d9dafcedcf1f5d362f5bfd40d265b77d29012b66f6ed77e10afcbedd77a970e589ab0f6edee62b7023da88df5780656b7b96adf1db7bfadf77d2b32a30565e12a6f6d21bebddc223ae3b5f86556cc4bee22d7f9ff5b284d5987fed2bcef2f77f7f96bf557553cabf2d9dff0d971ccfdafefd257db80a8cd5eee8d5a7a111edeb775a3836905e732fc73d10be2f8eda8360e866af65997596ffe0f2e11fc4dfe5ef8345d7b6efc8606ebf07bca36f7f777b27e9eb1961fb880e4e3441a191018d18b13640165b9068a38931a06c89757b0d1df3743e495811fc256d22d04395ed3751255bf23b1ede01def75726e517912df9dcfb11d735a28dae1e96bef6fdfd707533d8be54ca1d67491e062f0ff7c8ef54602ce79f04d8e9665f447eadf2816e75ad2ab0bd2756807b962d1f81ae87e35f1edee12f7f03865d805bf2b71fe075b1480bb684a3a0eeb92fc1fbed4be89efbcd055bc251fd12b6f7be04eebbefb7b923cf95bf12a2bdbf8be3caf2cdf1a2d80c9861060ca71bc2e8c2892464f8d0822acc302d114505c6da957f08db7b1f02f7ddcac50fb6f73e687befab18c47df7fea240d788ffe8f242962d1f59ab943e2a3076de91c79fba1f716de10cab6caf92b04df4a957256135267d9c25a5b07f479f2b1f89fc25f28bc81f413e11756f86b6f62bbff8f592dfed815735856dde015ee76f889db307fb77fceb7f5d129643cc1047c860c50e658089f9fb48581024d4b060ca17596a7012f37f091bc2b266892a68b688716223e663a0c0e4841067ac60068a983ff3380d5b144a98c9628d14b4e00aae05556650a20b169e28c302cf0a1e96ce4c69e2a40b00a0008616393889a1864919287e60618736d04065e113830926a0619283172a8491c60b60b4f8e084082c47b1dec2883164c658628c1250b019629c008523c0c0e26486a1262e1091260a2598a26078ea98877ac837c9103944def92e92903d091cf42b515d1edcd343e49a350c6056cf24728d1a09f41a2992c000eee99720f02b9b279844de4cf4e19efeeccacfaee46c22e0431467f1e59bddd1e7f6b74f8f97301e1386090f6984185383090358c1531037a031c40a44c4faad84752b50b106982d699c9888e150912445114614356d8921258101e572d2175f2e079793be88728bf8cbbf896ef703054120c88eb3da593e3d46289201671571560674ceb047fc357253910c38ac89624448b016c15f3e1256631fd4111647acc0888828d6cc4caa7641c9263ae619dd480b3328db2aeefe406ed36aed544dd9a46a4adcf1d7cdf33f8fcf73009ef983f82b7cfe0efca5c1f303f92be7f983fce53d3f117f89cf2f824753cfcf92b00d9ec3adb590e3d0749d7b7cb9d0af0e1b3d314ad9017c5f85005f85f55d7d2ed4fec32a03f81a5611c07b6115d6876115027c0004f022eb3520806c5107c7125c6dbb1f1bf57ec13df4e31efa3e9cae61854f53bbfce57c614e8e1a04089bbc8fe2ac61cf2cfda080a652a91450d052e63e3d7cb22c48074a3e7d40006983b0493ff90236789eb049d1c73610fb8cb7e8a3f031f6058c3937a5a552a954910c34655e6ade8ecaef27c5cac1aaf4fb49111121ccb66da9d4a73efb7e522e1dae0184ede42d2b4ad169244aa0a8ec543baaec1be1c8d3b681cff3365f37759e4e1335a1e057bf9be695765db64488a3346ddbc4310417ac5ae77dd3e6584debbaf013b51dd0b09c94a6e87217276a6edfae6ab3edf7d9f7f37511edc05abbad829be7d5ba6d75dbb6ad82bf816037659639a87da236e57a11efe0e8a7756f5b1269d39dfa40ae6dba33ef0b37ea7d9e5f1fdef9f06d7b0eb53970b7ccac82fe6e7cb0fdfc364eb6eb9a43d2d24eaf1471b38596b12aba8375d1dbaa46bd4e7ab6724f96514dab35b56dfc1cc762eea647330d1c876ed3ef07070d7664a6cbdf2129996f7370dc603baebc794feed16a6a9333939f4d97618a73eabc0fb4a954aa39701cf27efc56aedab6d12d9b72dbbc3f1014c710dcd651d3fdf4395dfacde6dfd031cfb85db9f5b78cbf0d880fefb8fbf17dabcf3bbee7e781f9196f5577f25625c33df54701f0f02f9803eacf63ebf2955b3f64d89cc2ad9afa8e16a19424a6c915ad89a3d4fd82790d3ee3d669e66e516961a978c2eb32d622b3af3fad4818fd3a97a4d29dd4085b7f36f9ab6ffd398679f0ad3fbdb8b5d61647952afb7eb2ad05ab8942475414da8088231788629a58b52525a218c74e906bd6c48efcc7ba794b3ff569c774c255107d9dd711851680438c8a9ffd28c3dcfa43312d5c05694f9faee48f52e9d6cfc4512e09b509842062232654026f866d0b19b123ee87621eeb187317169eb022265482ae85d891cd97808aa12576c4fd68ef0270888daa5badd8ac89bfbcafa03771abbeaff9b9e5d67162e9e1d69f4ef6d6a9464e2f7532b9c490e19c1f7d8c9b1f1dcdadce0619de64a7f44c1c55ef503829374516e7475f923937a23789dedf0f10cbd60ae7478954add8ecebd730bc83bddcfa338cbfc0af3f99e61486c9a86da630b3f854e3302039c5b88fd9935bc7ccc9ad63d6e4d699e6d67966642f3e9170f6f9e5d6efb250ca2809935f30d4ad3fce14a4995b65148ef881c7a82891b210bc3a64ec28a374c4d85122dd5a5f22f10e2e0c7389a344b2e268a9b855d4e25697225bf5a1b4401c2b2de2bbfd11d17e5a91adfafc85407f4a912d1d30ac3779930a09eb1d2dc28e538a07ddcff79bc7097338abbe874980f06539abbe904ea444d8fe29d6f725d605c51ac559f5eb07c4131d4ad8dd6ef3ba90bb53e91639cb9ddc63c2d6e7cad8d1752bd04d4842d747c23bb6af0f24eb575bbfdbae43c97e936263e5d62b24f494c23dd59bbc55bf566b55f5bf88fee4acfa9de84dceaabfe90cf1e42feebebe3b75d9f7d36d73eb365147089b89cc3dd994c2adfa5ba731d92430acdcfaeee4afefebfb99e6e15f1ffc2f742767d5ff446fe29efa43d8714a19bde9d69f526a6f534ae67455bc438584edad8a46b62b9f6b7042d7bd03bc4c7f7ef77cfa73663658a942c2f6f38f16c7bfad4da06f159dfbfa4f5b497627fd2addb38475227babc76a650d1df3b49c1ef36bcea7f5ce9ffcd2c4d5f6dabbee14579b9824c98fec6affc3ffceecfb99dc4b5693e5629128f197fcea2c96b3b2f9fd645f07435d85cbe12c774e646f217196bf13abe23aa904090d05090d31b7ce1aebaa1ebb010058e7fae7f08eee905bfcad8a80cb0cb8cccc2fdf23c2720008219c86d0410a13acd0019834a288a2018da215c498e77c17bf0ffff3b37829882e8316e4bb7c9ee5ecae3d6114d5c4dd356666ae4aae11fe239ec71f106e0e467aeb53edc7a714c64e40b174f8e8a032bd8f17a525ea5af5ac1a4e6ebf4a8da5baa3699a3656165fb23767f5799666a6892861aef633c7daed7dfbeeb5ef38864292f9e682f51e7c9bf74c1c6b4a053ed28f2c5b6bcada4fa51adf63b1474e3ce232325feba40cb30fbc07bf02de836205beafe2c89f1d715d4dd34495670e44c447902ff4eae59c7631a7b8ea1f8a69efdf50242c13572d0235994f7f28963d0d8b844864cb7f8651d7ffcb78a1a3a0f9f4858e82b29f2f7414543f7b4ddc4421966cf9ab64cb9f8a2c5b33f3af4dcf31edc7fa1cd344a5f923eb7a26aea4b8aa40f6da07cdafa2d051502694fd508c63545cb518c459ee2161dd1f89ff12ff22236421cb9694e23eaf482121e1ac7e6666e690382d482a95bfbe2ada6b91304dacb2c85f7d47f79a02ddc1aef8872e8f59d1a53bd83193f223ece7ac90b95295195dfe01506147151fc98caef4b08af69c16c969a9b7725a9cd59994db3f725a6e7f7625d39249c9aca490948238cb87b3fa419519ac46c85f4df4c174be3f4807404a96f8abfbfe2087a964abe96b7235a4bb5cb81ac23df8a068a43a8b4150ac5e379dc5f6e63cfb6b2524f5e0a7721e14c7e92cfba038e6dc9c21e0a7fe26ec409cf71b9cbf1157436edee681dc88ecac1c1c71041f047d6fbebecd0f797f137a0ffa9096bf1f6b82ede12f10fc54d8371506e90b7e90bfc06ff0035aa2a48320a10f6f0579ab5febc167ae4b8aa38bde15fb77ecd1df50fa9ba88ba4e4b8a606d267affd50acd237c1506c0ec5561fd0d73e88ca17a241f33351688a2b176bcc7a39d85792f28235da4041164659c486887848811921b224c184536cc8042f94d9e2c415252992621c00f103104d5411c415505c4bc79e0a96bb9cf0e58e6da5caed1f9b876b45ca652e97bb9c204667cd3bab7f70565f11db8ab3fa690d76fc3b4295544a747e1f92430f3be0738d70473f20dc774fbf1f557807f7fd3bbcc36f7ff3c03b3a7147ec197672b0da2be11de0f71791b020b2d5db8f1a70bd1dd7171473b88739c8d673f9eb0697ff131f0097991395b670d4e08e7f55771c198d3ad6233eccc57e7b69628b0d39643105991362fd4b244cc88421cad042c309515ec4fa8b8c801446e70da5a388fd445bc212751192251df3f72161d55a95ea5721cc12e44fb1869095303f13637fddfbfd3b96301fb2e5efe33efcc4aa56f5d7592070cc47b6fc93ec7b0c4434f1c50b2cb0c812731aa6f80206335c30e386a898bbbfb4dcd352daa71a1d4be9cf4e04e846894662e425207f7155498c264a65ce5cf948fcc54a66a25cf94a263bc06e5d985cb91bd492ef7b207f7d747bf92ba0d4af40e058ea5721dca4fe266ca2b0e807a809544bfe976139063e41c3d10bb05021228addbccd1f05d94f7d4a143a5a7d90fa9b0f4afd8d2874e42fc4b2c5b2957a1b51a8ca96fc94c8922df95654712cf5634d892b7f8ead5cf4215bd243026a0053c4d0850f4cccc01245d10d5ec4a861c6099517beafa1f7fd5eff11ef722010bd711c5b84ebfefe2c85eef61e8e39eed75bfd8dfb8e535d77e77e86494ad071ffd1efe7cb66c646fa73ef0e866e67f5b9a7b5bb5962842ca8a0200a12555061fa20ea09852f6b9ce1c428261fc9922020ffee95ccf75013a269e2c83f24648aabf9435da7e4b6904fc8f6b79558099b281472966424ce923fafffb6f2f7ae406dc08a35a420838b0b3eb080d872020e2d7469810531f940446818e42c2253ecf338edd005140a498459212687ae88b182c60b0c51b82813938f44fe12f959c88ee4b9cd109d13e4f63395828ff8642209f31ef1b93dbfe8d2e4ccdd71b94b932bb74b31ad3a6b2ca53cd8197a5f6fd4d8f188cfbfcb5f46b497af7d36eafc30f78c39b78a23fdf997fad41c777f2b84fdebf2f7f17f7f5697410b96fa7c2cd68eb32c9bef72183771858888c8849808683c39492335c47021a1a1e58eaca446989bdbfc74fbb34939298d283051502e274531f198e3f69aa14bc51590bef43535567adfe228443dfe79c4aba27d95304d54cd5b256cc6faad4c42c240a043992bac68c38631bc88f50791300f629042862d9ec0d2248d58bfcba789a853376baddcc3c373831d5d5787bfa6e7d3aea3f6c840af4e88a185cc9a32606ce8e1c109263544d5e082342ee8ba145387b9fccee4622ebf0ce34c4e4d33a35a9db54aef4ab5bad58debbce979d2dbab1e33d779df123841507a83489cc7ecf107a6bc30cccce28459fe71de76b08b1f1cb83219f344ca1427f0e1288a98db4d517e58ea98a7bb87b3fc5b87b35c477f8ebff8b67875601c34d8295556ecbc2efb25ef88a12bc2aa7edadd5d4a972e59d765ed6e76253a6686751df30e7b97266a722e27a14165877bb8dfd61d156ce39e137776444a67e634ebec6624c8329a4d1e874180bdb097072c89d0052888c803267353189df4727887f6fc0be01df279a9c76e70017059876139f2bbd58fbefd7e9b7bb297dc0e5ef6913022b2e55fabb5619e8854a9ace5f04e0b5cf91d5e39998a3b0ef3e1c9f1196ac366c05f23af21224291a13a99355b8a53868a050e5d544183491042483124c39a2554f0840937b4b0020d2095adc5d47ab90b13a43baab4321db775b1f6f22bc3e66736f5b2c54fc35a67c65ea6933f76bff7339fa71453612c3f773929cd1177acb77938111b779c38f41593067b73b98b0962f0889793bcb861cdbc3eb38adb2dc597fa6b7fc7d4803bf28c3996a4944ccf239f41c01d394d13a63bb6932ceed84fae7c1e3bca954dc50562d810828d24aefc5703c395cf5233c595ef7ac1942bdfc7c70bceb8f283105df9406d185df9448a0c71e58fd086d2958fc4c995bf24094e5ac3c56806ee4274e6caef6282972b5b7646c5de5c864d2f52b444714516221ad2888db60bd4931bd82033861660625c6401f6acd7f3972482260a13d0f090851461c4180d355d9e9e0ca1431719625b8cd14410f91dcc98f434303ced161df38cb2e9caf9d9d3e729ae3ef0ef0f2113d9c5ecfd331a7648bf9fbe4b5e915724928ee6d12f9f25e5952bdfc3ac5640da83c0314dd4be8a26ba644bca7731cda3af7c69c59596240d5d8b235df9b2c95d22d131cfa819ddfadf3fc7aa84ad28f507477b6f9effdae7d10586f67bf0bf1fb3a8261c1c0a459b6e7d5a84f320700c470ca10a8e289ba893e611c65f4d8940ffe8cb7074a25b6fc297ad6ac5e657f48762b2295c511148b638308e88621c3bea1fffda2f225bf5e5832f7414f4bd7ca1a3a0d47fefa2d05190bfad424741e0bb28e40314a528249412e5d2734cfee88a4971459f63489fb8f278c945f9df87003e159138abfe12d9aa552e3194841971610a1cd074a146cc12b1faf249c24430630c2b6552f0031932b1fa72a93e1876912c025fcaa5a286225bf5bfb0b5855d057defffbdd7fab2298bcaa26891746a1e57fcd5b00634418ad59761780aa318c7388bbaf55b8bbf562070ac49954bb2557fb5024a3d081c0b21f55552a26c92adfa52cc184f886efd46a279f4adef45feb2d257248c63f5003f24858114e3d8d00a64949a94c048f9c2ec49b6ea268e19d4adaf03c68e72897e4c33d28632ef54d0d4df8de0ac2e525f7b0f41f0582bc9563fad61fd3043b1ae863464a495fff6433121a5958bab0fe8d70fa25fdf5d0caabf89422f5bf285e4506c28d6b5f703a4922df956d829dac438266bada1e51e4941d001c37293bbd4b274fd253373376b9ae4dadddd4d3d244a96083591c3367b25473979ab53b0939dba2905dde474c73e13d54efdd44d59946d5f9a5f7e9cd399db61f09991e066ef61f60999d74826afe6cd9a04adbc43b4db0c77293f27f3cc85861db3ed3331c7b91f354df4e7b699b9f3dcd6b215cf9869afcd6ce8be125df59757667172add3fb6f10d99ece92fea31c9aed5a7c136378803082034a6d44e1841429baa8428b3394a0550431c41254c0a892451a7e33c6eeec6c5ed871c73fc7ceabccb8a2b47065cc142640a920893066727628d1e48b35463de420048e2776545de7f1a9b386e561bd2375432e6031c30950a470c59a206a165060bc2ac61817b8b8e00c1064d8b8c08e39b029868e2f551ca1050b261aca600105e566c8072f30a421c411485bb86881f252460a231b8ed81883af28638d4d182bbca026892eca90c1640b194b50d1040e53ba582385d8a83a018c262cd8b22685a32315130268262c278049586078160021c2e2a4cb18589a60c8524316224cd8020d259ec034beb6c2f53baacad3a3ea7a17266c28e07217264a17bcdc85899a2b664e3592c052830bc2a862c4155dc5192b727c8a0c4c3c5101c2c9c9f3b225ec9d97256cc6dcdd0b7771a609c76082a0128399145660030b331568a44030634a0e3d8839a18ca3293bb8f1c48e7f5dcaf937470f155f1d3f19a0c0f2f86c89a53a8b4160e274051565b068028d589120971f44c26a2f5d993486962baf648a820a1a92b8d822871762521c8ff0d395520363a594b04dd454db268e55cca1e2583371b4538c4999e3b08ef94b1f7a8db5b99c244697c9458c21be8881025781ed2e278901450ca3a79aaa1da735ceedb756d8b1be8be7fa38ab7538cbc98516be368c526b5ef09a6801cfc8d059f9ba70813b61c9b38285cd84a62e8aa21a86eb52a4dd17346d5764a0562a191c66c898446964548861de3544344ac6889ab2ea2a038357db366682d1fc7eaa4bd6302f344f133e6d28c916d70a8612acfd3d87714ce5ffbc83e7b2b336916fec8428d9e25b6bbdd561b349d3b890c75bd6abd5aacc68a918aca05e8d25c35cdeeaa7edb76dacb0af3ec27373883c2c2b367539890ba32bb9e082c855d822ea4a186ccee5a42d9caa992bbd853bdeeaf750d54f2cf0e15c31b89cc44515775e4e7a02e38eaa4b44d74d79e305dbcf9476925e33278bb9b3c79c734ee9934d4a754c4ae99c3367ba171e0fd9fde7289f4e7166c059d23bc5cc2ca59472642e32ec5a552a7f76e7e9578a6392ebb46dbe7463872b21248097ff0097c5ee39a7bbbf9340c701dc164958b992ecb2b37c74f755773334020279ab8820fbedf217348988827a0631522c1359b684f4cb1d7a495092adfe6e2b21fd3a42588f84799564b7de1ece926c74b265cb9e73ce29e98f926546bfe953d621a564b94a25db480665d87ecdc7a505f1ed8bed8b0cca701e9da339e06f297586b0fd45feca39e94f0dae115a65d8e3d09552ece19f0bdabf9bd997f22c4ead37268ea1db0160a581a45c9dbcc4630e2a2c97018e1d1c9f4a85c10d15007e6addf799dc68e53c1fdcf277a48c0adbd11b18508a73a3f3fda0322727e7b3d6a66c3490329433a39b56b917e44f77647f64df9afa545a2cb783dabf2af7fd5411fc7e480878decc9e0e913f5ffb869470b3ebd788177e3f529c99a4731ed99f19422048d6c44e91fe643fa668a14caa55ee9983506de3bcef2703e79c93665ea7cdae4baec63c853c6d529effcd4b7ddba45ae53acfc8b79915f9bb6d5cd576542a9af252b4083b81708cb2ce386fdb7e6edfddf6b3cbbe736edbc2203a644c5e823bdf6f673d39676773f3fdd0f74f0b62fad0c17a481b934d0fa57ae8b8dba54baa75b5f33e6ef2ac55e51879729867f5ac8eec328bc8cd26b3ea5637715cc255e8d28fe7fbfebd94f7ac202e7fcdccfb36cdc572ee3b1189e3fcd5fdedbedb6edd3ceffbb84ef43a6e87fb54aacda6080b3eff4e8ee6215f5e7e9ba3e261a5c22021cbd60f900fd9c27181fe54edfba161c6acba8d21b8cdacfadb27645e2e9b24d8bee3b81dac9506f97ea838214073e8676d3307b284fb09c1f170be1ffa3dfc452f7f97437bf8e01e2c76721304421fcef2cfa8582ff31efea2b31d5461de56dd795d6de5d6dba598cced05dc1cae7e1f27b75f0bb8445c04873911111191512c7b244c6017f593feb1a3d8da3c7254133dd97079fc68f5c54ed39bd9d7ec6d992e377b2ee32753cd69a5fc92e3ca9ff22b0df255465c19861a57b2a0cb954e4857ba18e65f19c6316f2b77486bcac4f4d78ae5ffb49793c0d052dd8b62bf9fd56ffb553da5eb523e3dea9ce04ac8bc4da75899bac1923dd9d1cb022ea401c3a94932dd5103eb2ca5b4f9c27a51a8e0e58eecc5f539b51d7fb158ff2a95b539ceda496335202a71c759ae627d2078cc7bfe3ee89827be6cb16047af5aca2a6d7e3f5db78629d5348d366d5fd8e9b02647f351f8187d7ffeb15d16ab11cc0818e7cb21b6c99cc1523ef1c4134f3cf1c419fce59061fbd9fe30439d0184a73a07caf673b7f79037081a4afa4301050e282c7f430d99fe72470af91b8a77709fc1527a0ad08f4c058e282c83b95324a17fdc681c5158fef6261d27c0ce577f544affbecdb429eda694d226774c5dff7ee0607946479f551a779f7d27c1d46ca7dadd9a1c55fba07d143e067ed75e461f0c3966438e75d3ab927a302cfa18f8366c82c2c740b1e86329b1997eda9cfd75afb64ab3399b9bdcc7715fbfede3ef27497d207c6596651909db6ba1f69fec4f9361d6029bae1e45a3bd9452ce4ca3b56d4ef5b23087b392d278b953279be1f37ce735f7292f0b691adff7c335fa1d8ecdb229bb7e43705cca79ed79ddd3fb3cc8424a43ed046b80cb4a667eb8a9d0003130e418e82c172bc70b59f6158089fd6698dd4c74e5e0eef6fdf0cba2c2d66fcdcee9c3c16f088e3ba5f77990515a6f789a9d2f2595947e3f5f0b571fb755a9d1ec33cda314030cbaaefbaac1f7d3519ae58eaca6f4334d6a9a465fa35a6b54f47c356674f13361f9fb01bfd8ea3efe1a59294817547a090b304bd55c5645332d6b4dd3fa332b612ed9f2afd5950939cbbf899ce5fd852e5756e50bc1639fb884be8088848da05bfe4caeafd031503444b084978082323198eed83ad4ad3bd71943c43bda033fcfab5de2a5ef477999f200790794e5660afcb8eee66aad75e3c24efd16fed4e742205da83d90842d898ae2a85a6b98b560732f057e32ab9cc769d9e7814d6bc1cd05f7d0bfb1af7afbf61bcc62fbab16de2c91b086c20c258aa144759c4d779ec79f90ae6d3cbb75f5e2789ca6d5dad5aedbbcfaab54fd15587ff5556eebbaaeebb6ee6fd8685da55dcd40fbd1bada69936f7c38f81b82430332c6497f3fbc86da65316a2eeb721217686e77c3637fd9fcd675dbd66de1ca03bf6ddb58ec529b4d286f7f40988dab697ebb54f7a9c709fb723a9ff339fed211c7a1aba393921f10e622f3329cefa6970abbeff8fbc16c5c4db2ac5d6bede6496daf13e6b09094e7ac1eaadf0ff79cbaf1fafbd11ae7d3c46c0cdbdfe9785c57bfa31e4ec8979de56915075b5f7bef93f44dbd114dcc7156d32bac36cdc0e1d5ef47c3c0ebba59b31982e3500a343a6937a5dd1b80f55321dfda81d65f5e576d1a9acdf707e40753976e0a23c60b63e3cd9cecc67b2f057e36bfbdcd8d4d2a95e36d9d8e8d77b7e79efb9efbbcd4d71008ff68df75e1f640927ea87d7fdd6bdd8971561266d1effa2269285209efd89eaab4f416cd01fd2f1473e96fa197db5a700f942f2e7dda6ba8ceb8b4cd5cfa9a18867ba8133b72d4a5f49708f18ebea90ffc6cbc8ee98ebd46121dc065753800dcf88f195ce696ffe84ee4718f142b7ff4dc55a5f3974f7f72dd48b7da9413574232ca3b9ac7f623275df6323297cb0cb88c80cbadf0320e1bd676f93b1b41b694ea92e5302c1f17278e2e16eb5f25c58c3579b497e6c07ff49e734e8d6f966559bf16fe643f8284b5926cf92bf5936c79691e4ea539f0f771b958ac7f95ca5a2aed85fe0c333456f693844923b7aa7f16cbe5734769247b60982fc996bf8a8bddc24b58aebf349230bf225b2a2a8e2aeb578c240ccc817f0da50cccf2772acdc3bfb4520df7cb1d9dca751984aa75b8ae54e53a9411f7aca0fd288d74a4cda959289f38cbbfdbb4ac7a35d39ee739eec77fe01eff4e7e3f788c67d9abdf4fa67296bbf61dcdda3da09a26ba1567f9c7d02dff1bc2ec70bd5ba3f4e1a967b8fe7ec506ffd23c640ccd813f16a51eaefb142d4faeffe8535034240cdce3d2887bfc3515ece856acf80f15fc7e54bc4313c721693d9a8406893b4a30d75fda78a669e21882db371ef38e6e1e7cbd73b18d4ee7049667ac73ceee7d782b49988c49b9f39d15c53bba5b83612ec591646bfe7418ee7415ee7413c23471d39beeec445a245b921ac9967ca8f19f9a3acccd6022b9144991aea4e22c69c5593293c23d92165df97fb39925ea8e540bd207325689c699e5ca1fa9166ff1e7cc32b52051af644d1df3705bddb6add65a2b277f634deca6336738aa9dfaa99bce5cf9479028f1a56c4b6b5bfd5e5e137f75d93b94b3e4f793b3e4df8862475ff225287f752faf338511e34edfcbf733fef25e6a1cc7711cd1173a53f53677f70f5cebb88ee3388ee37e96f86b1ca263d8b19ffac95f7dc65f2ce42cb9840ba7b3b89f889c497ed7dfb5d65aab8f477234d1975660a574314f4c1ec697b235ecc869ae7c254b849aba7e28acb52858b9dcc5045fae3fec7217133ca962c79d162c7769a2a6dd7d1ed045852c97bb14295d035cee5214e5ee3c8fbf3458fdece507c29fa0011411111151ac8afffdec781f594d5489e34e8e673761d8692f4f86d4b54a635e55cbac852bafdbaaa6f1a77d2b21b46a5f438ddf56ed059df46a7deb2feeeb6bdbb6852bfb791de536f0eb835fbf72a9edeb6f627d6da3692c5ff6c24474557eadb3b20782c387cf6f080ef0f9f3a03ef8d5c14d1c876e15333496ab6a76bf859c27aabc5cdeb6a7f539cadadc9e7b0e579fd76d9459ab5cfdae32b76962a59968e4c11e7af28e9ea2463465e73527c65dced93de777f79c995579e14b551361b56f7154b5ecf9c939e79c73ced933f3e4f7237b6a1e75df3409d4cf83ec7dcc714d9c9ec33a0666b3cbb6aa6593e33a8edb3c3ba7cca494cd6d5593946619cd6696655fe5dc001c439089ae1ebed236ac44c1ebf7f7d3a13901d7efce19d142ff24507f7bf69e6af5db5e0bb7ef5a506bd7811ee86d42b410fcae867efbfb799e1350ff7e34277280ba5dcbb8dcea87dfefbfe7db71a779ddd3fa7d69ddf8f3a0f3eacf075e4e42f3e5d6d0efd7d19417324f6e95e0652f644c70b5af6d63b55abfe3c2cdda54d7759e57bbaec3f16ae8b776b57e97533bbf36e1f83d354dd3b82ce3c4aaad90c5bd5e55e14db1aaa9524a9ed212e1305180c344810c4f7efc90c188597c9959b3890b2873eabb29bd54ea7db8166668bc90e5ac90e7b3e227a521ba5c41eff3b80d044170d37cf8c679a9507bff52afa542ed6dc83765a5067aa067434e85148df7d9efbaf6b68ff5fd58f179a258ed4b794969a05cefb3afd355d0fb3efb5a48d3d8bedc040b3fdceefb290013db5d6e82052957bb9dc8e2be1f7ec194077ef6bbea7d077a1c0ee881decd6f7f8373e379def7799e27de78369ffdedfbd1bafadd5c3b7a37e53de7dd78a94fa552a0077aa9df3ec5e1e0d430f5a954bfeafb49712a2fc502953b72d0ecac811ee8f1e6ce6dae0c5ab82f95b36894cdd171dec1b73fcb5cd3681279352dcbb8ad769644e8ae863c7d3bdf9361953f5639c324f2ae84684f65c6f5f7e5be2118dc16c710745dd7691fe7f9f733b9a7df7329a5a6494dd33419d2efa4d675f742911377d5957674e950a5f074e5ef48cf1675d2c747c9ed9d63e69c33851f5ece3c738ccde16fd35cff1c58c7aa06f8c387c28e5306a31ea8744cb7b4454d2ac5684400000000c314002028140e080543a1603cd8d5b50714000e8d9e4466489b8903254a611832c6104280218018202000303244c206941b9faa46376312ff1a63a5c71b4fbacf63e9a9071df7b380c69b34fdd16da3b7909ede06979d6c0d6281ae85a7314e100c70c7edd1c427ae349df306b1cf8aca32ce9dba0849656973dcb9cc03e8ce604e1ba7dc5df41049232033b25b32d714dd718af9d4c2ac61a45fd4b2d23f2244047c96285ad220c6ccff90adb41309107061c5f317243a37115906b460931e54e710c50e60880b357865f246da1a901344a80ad7d0b0dab501432a9978ddc6d9162e52e09365f1810dbfd4e7704d333401fc7b7a94c2099353226ec48024d3881ceb4eeb8ad073be49281aafa0ce44f9929dc5f316fc73eb775edba78c19dbb2db8f2fc49611e316426fb0001700f684422949cf306f190c84bbc8566142827af98ea999c8c1f479a23d4f45b0147929290d4b4df0aa65fe94a531b941d0dce236274c6628e3e6d4151b9b1c1a2b1b86b020eb338df9a0be7289ce5de752fb840416b836549e33de0a036c8e58725ff7bed54d7ee02a37d7bc807f5fe36637cafab576efb40d46e6d7f9e9a7844b58edf948b0d173838bfef5d7b4b2b556fa034e70afe251bf16c5d444913aaf59f81b9ad064fccc99f70ac213294fdbd8847bde78c19a0c9d0142d53d041022a0374ad9160534cc753b33e029c14ab0499041317b0dc0c8d8990649a588ffebe3cf3011b4099ba42f04a5264958f2f082fb1ffaab260586d0d88c9e359b3d43bd2ba4cd1d142f85c757354e23e128282ccbed6cb1f47451f30b02aadef5b35a24798615558b99cdccd61534db2d00f714237ba294eb80f90677006c8741ff8a3e96f80056e61432c11877f15b0819482d145a98003fedc2acf134ad9962a5fbb77d899f845c48f8710a2b6035152370abfc199ff3c43e6bc581f5f14d8499b7de720b84056b7e6a6fca9e7ab01532ebfb033895353f3c692a263ca2e6f24baf10e6a68090654c5a044b9e4149ceeb7b436e79bb04630fc408767efea4225facd7624e0850ea779b279b52efbc0722c5fa347ad3f8dba07fb862af8612a39b7f94b34f96cd42f8beef4d6edde5a9ae163865fc81edf7bf7151530f96470f1960b6d58b02c13df9fa81cc1233050161b8896c51e2aae7c8e2bae7a8e72f1ca6ef89e8cfa8169a9b7414d5f40ea8784ef2606e4c16cbca59ab6929741e5b96b27e70f06701ab66ba40abf38fd8e47045a5fafebc80dbb461b805627274869d20c2c378428c8acacb9fb57ae4dda63c985aeeda0cd8a7da4a5b7c3e0670bbbf11c73e16d6a2631650d407edcc3547a9d6da4185631d26c937735a7532a0d9415286cf904e2c3963d25dccdc12a13a461b451a7931ab8c2ece1e4f5a5bd3258607854b969eefa3d4cd638a9aa380d3773890ca1861c419c103840d3e057cd9cb9ddf0c644ce90e170c7c3516dc73609c21290d30704ec75caf3d238a9b4f200b757274e111914d6ffbdd9e5045050baec1de7f06f5d8877e0c561ba7cd5ea379e1a04db2368b6e123977cefcc9bbcaafce1783b3c230f8131899c535ea63644cc37ffe734874d6849ff8fccacb30e5dab7c186078ef88277e0190c7e1bb0fd7c71420a3027c2f51257d14a4ee2f112b2cf61ce057fe35319557e40844d9741b9deb38bebe9c0949419581620f65eb05e2d026b6b51f8a12d01099febd25afb38b15d2c4a5792f729db721ed5a755e106671a03cadc0a2f4cc23e5686948fc58bfb44ef8720bdf95c8e3ceba67b1853f750d4db7dac1e3393daa3d51fe9659a2406ec8c3bdae3861b9dfd3c28acd484230093e2cf7eacd3ed6920e69609d11341e9eb9fb445344f114da2e70d77a40797743e96e6db611e2093bf8f7adf8b677c41d441d255ac01377146e6c52e3935b468a9ef3596154c9fc2e781164a87170010450a68a0851f3b953c49f8fa62ddcf9fb2ddeac306c7b6f3978382ef3f83b61fef9494b6befea32660e113d5d838899c51959ec64d79355aa0e1f6464d2d960536c081a58460c858e8394e258033d898cb2b9ede158e651e46ed5bf001289e80dba61149ed4408491223039689623a40dcfc16a1eccaf6b6deb207446fd3db741b7f6bb0b0786f09e4941b284544ff13f86687d48e72c51b54d544f18f89a2000a7be04d8ec2126b25b66749f709d5732809412b31231c4370d6a80dab984e5837f548ce08af858e8ba3961625ccbb990c6785498a9f7a90779ac7c43d79bafd705e9d97b944d32628c44dc0b346c1010e94daf7019e6781d60a0c87102eb56af399bb7d1c0c277b6d7a7aa6c027c99afbabf8c11b12ad567c8a47989bc54880591c1d46a18e3ccc0399a1cce64c809bf5d4de8e5b6bb3e246a052c9af69c8cd473e27f012a118dd7744ae4d915b4b23a78d149877e428977f029ce7026fad3e169243ed0bbb655f5faa911b67eb618805942e1109a5207d50718250a3db8b3f4de5e6a9e5a1da7877e552af891ac957af801cbd6d5f678baa757cda210cc8bdbb368e35266b27967c71ceae0f1c19752a3ee156b530206fdecb3e51302b03a930420e59edf84265e926db99c09880c7ee24e5f98509700d63e921e48cf5b9096d93d3c6e68d6d4db7c77cf5ed5cd8352f590b63ab37d7f820bbc4261d6856c02ced5e1c76cdedd4203e80e32ce2cea446e2d05ee8b9a72fa5d0d4e4a3cca4723fcac7c13256f4285a9600a005921a479bffb06acc5b8bd9239bcb24933a474c0ef2ec07ddb074508cc35006d1a6f924363ddafb888df5d77cb1d8e0042f40476a38281da9de802485ae729eb0c7eb989d3ecccb306e425bc3882b85590737da37ca20614bec5f669a33cc1276f9b8ee5cee5f14c0207893240e2137e441378200d6e3a0dc36a82b40e4e350ec9c56671c3d6bfa58eda974603512a0b75051b0c2f57175527613a9e8c0359d9daaa1b7c0b19490c4aa01b5a6f8c6337128f459629dfcd75c1eead2f99a33cb61e85a005e43aa6c00f507230b6441d6c2f767db5875bb5c793f9aa1971e6577ac6d06fe606a70601ee379c0c0ea26fac9c202b5f5d56a90c2aabf1bd4b1b429bf9e6a13a67116493b45ed850437c31c08687240e4065f105b8bb193d3093498c1d62d90ced9fa520775e15e37f5f3d955ba6c5fe3daf6415acb1f7080112b391b73216b69001e0bd3cae32b6f4aa6be7116a1177d79f10c1ffcbd4afe2655e29dcbe5de13044f85f28ac2d7384040ff808b8bbde05b15e6eca734fb3127093bf50612e3564f709287788f2557758d09691e21fe5bd1043a7f8c7072aaffbc74f107d2a4952fac14ac5abd2a4b20219eeae6d26fc58f63d83bbdb67340f336060339f711d59fddb810bcdd9f025686ddcccb037dcaa814ab5d86fa12487511af9ead81b5f84ce1300180503774750da2d8e4b3e8562f28e4483b17cae025207a7f737f833a6f58e4c8e0ceb27b66c5db252c04f23b59417e5b7e18db8997075cf64ce7f5c51432cde664419cd61b5840e829783e2ba5a6a34a918a7a0f77080bfe22e0622dae0bcf7ba7bcd83bcc3106a48038cd6788c68461a97f0a56ba764bcc316af112e29c364bee2a70be2ac25e6d3ac326ce143700e9b2499a1cd4a51505d219630e567c1337b21673eb062ff626d018f259c21f2b5a69948278b159175632d1f32f79d845dc4abb7886e1a08ee3b88040072105a9926181e818ca2daf203ff70a6562888fa3f5a9e6ef4c38eeb65dd3a6ca8fbcb9fb549ed56df2335029a0614b9100052b509b230ad9a1cb8fb5932e4ae32ca8fc9c23c8067ecb0be654ecddbc4e382d998db6794000276abbf098d6a790a11dc838ecf4d000715c357ffe50aa38e4666f2e0a8ab14aca5ad9482220a88cd2cbc4d3db53f4388617aaa4309e569dfb303a9f67223ef35a3a06c58fddd0b41a23fa72bc75c92e87081fb8d20967be2418c02ac28de8fb340058582835b2b06349d2175dcba9e8e98d72112dfc8bd8190c2635ddcbd5e5f45d2d0867ba4d578f560008720399e36f0c0016db4b11c1f8ded6d469eefe43f7cb5ebb0423e77c1f7c7d4c9c8f0f6faca36136481fa58de5f16c1d85ec49f3afbd631064294977b6ea46bd406a8a8513776c4bf8e8dc5fa51a956fb4fae503cd196334fad5e7577dd2d8d80cf42b28a8a9b6c231e0853bffa0c8d7632a42e6f5e0d572a28cd112a61809ec7184bfbac717ea5b701a5c82398882526f697f14e9cd92e74680c96538cf10c830af5c9c025fc37957ee3bee8dd302378725451ead038bc19d15b6dcc13541acd269e56aa7e7fbbcc0dbb374a64591a049196cdac06c5072528cd936faee2543aed76e6976c988b123d3d0af907b0935ba63643aac5c609405494026470576c7ac97814c6319427f0980aed844e1ab575dda6cc9ce85b09ff34a0c7a0462e44f1d12975fa53a8f808b09176eec814e47decb90bd98eb7195897c92024e032f12b2619421f09d0cc3b049c959c260df7d0beff951ee4a7d0c09191775b9f93a56e489153646128d0488f242a1e2d2d13eddd57c9f1014c6fccb2d672ecfab197e314b5d9c538605cdf0450106c84026eb8355c51ee643ed6e139c7dec81c50363132b5a43b0e03292f604213c9a74dd5cf7812c6b1c3c7345d1c09fe022637c523651f45738aca92e21a5469a6136083db2093eb3d65b71d3c09c9edd66b3ac95507cb7b01b3bfb476cb7991f5519969f6e9675024464c4765d0d284d2b4a2bde2b92ae7c2b568f3b87f0d7687bb1eabcda4d6baff5d1bd1159911fbc7c1c8d7ccc82a244e24bebfcf622afab7a5cd71885ef59519d1026bbeab7bff29bbbe050936551d85a072d65e48bf63b4b59e80eac3e6cf47f1beb9ecc9d2729ffb9275e9fe493f33579b7362750a3b2f9e1a593ed3461dacb9043a7b54e7a6d695f56d9a72b233a8e88b78478bfeae62384cd2912a7fde98bb540b6b8667f54afc06d05b2f5c7377c2386d2fcaff74596173fb145269c229bc0b89e257ab08c7d2fa67ce08d66f582f74650732ed310888c0cdd439e4071daa714827ce15dbcb604c0f90e71e499549ee8aee15c1a6883c97a7a44150832f8630458db9654581be7cc316be3e439ec88a2e6ea934a3be3a858d19143565159314905920d88e35adedcd38574b87c115ef9c1887eaa9399a13a59011c2886013089823510ed006a4a0095560835ad29abc02f4155509b995acd9171d75667d1f82cc339e693d73aa6f36d9462f86ce2241a62c1c8209562aecfb69e29e660881fdc7f182c064da0e39426dd50c23e2f49d2f3392f0a5c0b81b966a6b150c79819bb3f8453d4d681e8e299c05a29173f0c4e836fc8c2477ff32178ae62c20f0dc3c4cb209e32636fef02463282071fcfdd1fa70d7e6748465fab71550dbebba3e1046475d687551c061db1c2c42cccda9beb7266272bf8a2857c6be3491f8239ebd3fdf49cb24abbf2e5132f52d63f533482a3786d8406ddbb1126288c31ba849d1fcdb6bac5e76a5e08556c034b6fb7701cd311c70be1106643d16811f45cd0427e12f73d6495ee02a0492cab80a387d9f1331f65a3d3252b9fa081bb16f79c56589e1b1dbbf0b97f8cfc84459dd9c3e5310a15e3f213cfee6bd404a1d201599fe45b05592378f1e5e2312788f13a4f282eec1cc0c514c8494028886ef223cc53ef158e0028f0b1428b9cb3b8b7b7034b78eedf8db48b33b8d9bcbda2acedf96a156c8a210390aec5d981f04541dc9b64b3679ebb39d278dcfb81dd29c531b9b756091a41c4d6c8de9d938bde653ae2b58fed99c16137fe798a9789fe25056e692721016ac4d4edc1b2409bbcea2799eb42e3687542252f9d861954526ea86e276df5ffaece88b8c02d6db0ac42b8dd338199b42ec5ae68f8f9024f1529534262e8da8a98de45539437618f83b384110b5ff1e08e2d73eb5f892f36405239b6415072f319a111cc6bf7ebac70e21848cf600555afb8d722dca12fffb9b28b7e805407d7bc8b682d088b273d2b713be2037a74eb8ce6cdc39ad3278dec6d06629b2f728eb09b15d1499b76a4f825db3ff98a85673eba75da82baddfcc1171dc707c87da6b0372044caa61cff99b1d6692ae52bd1f164a35d5157df1d232a64855710ea0c909d15aaade278c27954df94ebd23a55f6f00e6ccad916aafe71e6e1a323fd6cce072248c1b791252e87ef3a2c3b8266dccf0a7bdcd80f84bc21157f16b607326a740f2e90e75f469d8f960e08070c58ef2462e27777b12c9697e205d35449f488084900b982bbaac32ce2dc9a5790a243af71fd7d1cdf286aad156f32c0e34e9bfca28a6420c1c5985cc296459651c12cf6677c73c28e1f045c71d81208c27b0f8ee61a517678845ef5ff5fbe77f8211db21f0ff57e086bc49dde893c0743ad69b1dba439ca958d8632a3d6f12fa6638a41aec4051fe05f319ce2303a37aa9c87f599d6755116d77709f29c8889d00e4c22d169e08d8e4c2cb41f85c006bf7386f4668b27a105f2d7b7b2d75b62dd0696deb899a007cdd27077665bd7a89c00b46494138ca7353c802c74297071304ef009f920559af44f5afb4ea65e8e287587a471f735a7cf83df28fa5885847367a94900f25ea740d1e67cddcd70efa11e3e1fc3994c0647ea1e776db12ace6cce67d8ebd4d3115e582a662307379997cb02d3a237cf65849d1cfd2fe544126c1021f511b19f1033f7bdf8ef48408fb5d0bff22a5b26d9564b0610a65ef6e802667ac0be668acc3a40b79afb95a8127a511015e8a5ed52415b488517486afda890f7f49c0ad3fb32809cf34dc3fc79941381abb0052f03b1bad25ef69632078497ab16ce8a4cb90ae7cf148b9722a30c163f8d1353ca9bff602fad3405b27e6ed016b3c09356c36a769391dd8d64df0df777857f4d201faa95677f1ed5a8fbdef26ae6c9510f0f7302414842f5fc508d26df0c0168e1aaf3e473f93276da48ddab60ad93dc4c1dd3fb5e302cae528556934446700f379f16d6b21a31b5de3c1bc2958259590fcb5aaeb6ba26b3618512a50ff63617d8722b918371dfe78fed85c7c29f86c1590e0378dba80553692b1e8ec6b14bee89883ee4da719ccfb02e0c9c9363860dc5a38d0858fee1f118bead6be7a910cc1fbbee0ffb28011d919d42b03d6d89dd4a6b719ea2bee8adc2761774a15470879fdfb3c15c64c36c86d63233de76ab76f5b09c58e1e5ebef3b856036d8009142e0018ac39b7d85db7b22f3b80b59dd85df3663b3c73741490de5b4acbeb38ae599d4ef6db3e77e27fcf690d25be9114f76b81e2e44a268617ccd6286a4d2462c4765a3940091a882444f0414244ae4b814a910c8da668da567b891fdb9ac55d83b330b24bf507f8f2bba97914d1dd8a4051e9b4222d34bff2947b2b692fe7345b896aa53e03068e3b0c2d7fa127a4eebbd603d2b184c144ba19afc62095e3d1d33e02f91c16bd4c04b1f51b5e269a3522cdc35f69edd3a6d72e8d25a35cd35401420200f4874bda31bccc8f700da681b472191498b526556a83638ee08eda421b7e878fd8e223fd5eea9e33bac5a62cfc67afd316ece7c1144972233609e39e1b49aa3efaf79d5e2e6e7db78e3e516f02397ef2131abb6c389a7dc2ae692426868da65239696d2605d51ae554603c56704d7459f52aaf55d6be46ff01c217b896cb7c46677e199a912a350fb8ad0675f022984fa57448ee4e2b0d0e4b612e65d54736b8a58ba4c94a52c312d63137496b23b47ac4a654ab314e4e54fd27859cac94ba5e5cafdea1b26008822985b7011c3766a037f10fe6a001e053d69665c1116e09b04208a7885cdc2162885e7ab5fd4a2f49c1dceb6b3f33298a61e74386deb6a6a8f2539625168019037718ab3aa2216abc181886301fff10e4559666fb47fd9698a94fb04fac21fd718b5623fd870869b2b57b1493ab21f48f5b2612f5c180854e273a119ef36d307561d55f0efe5e2d7c8a0d44ad558845a5229c6c47e19f67e9e4665466b0434e87dc7f8a657f98c2f1740f94605b78eb416da9038f14b0051e722710ce7bd910c7052dbc412199af79196f0947e70fc970f40cbd1389c7c4ddcb5194c001a186687bfcefd88d72a9c08d01f23dc24d46ab19b30a16b3891d6347804efe8bde52b63018fcc29279ed6db69486399460074988502539f5f191688141da9dabdb0292fc0a619d0964ec0e8472cbfe4360a39bb91cc1ae75535683a1d7383288d36a9b32e4c26a18844b47c260fb9516ede85faf39bccd938a0f7fa00501c36245d07a87d89cd7cf3719c861024eb4284a522504a24da7b20d073508e0ba22094a407f1b7f7b4f58e47cb96af09a5dbd3beb46928a27adb9936fdb868d3c882938e38cbbe3f6d42a63661b5896044912c2ca482ac0b654e6bd5341db5908f9a4e0cacd6a659e392fc02136b6ad312ca0f69be9d66cb939f409b876dba9bde4d4f87938bf9b5deacb1a62f8438293bdb2281abf83885d778ea8046687bf649d024331eabd270baccb0774e50030476a5f8f8f7a7972eb4981b8ac998b22e9cfae0a8bfe9ef948a03fe9d5660d6e737b08330db4baeb6e9d06391171f73552560f665b6e90d095040938e3565dc6e72013e2c9bdd8433b7efbad575468d5e3c436d70d24635094c4b7c52c0e90668db8a7d5f3189a9ecd427c26ef0b8e84cebe5a44c094c048ee002f0724df1cd792a81fad07831ed916700a6e86604ca5c5101b8904e902cdc618411e2b1ed3a3ebf281843a504494092169685c13d717f2e29a8ca28e3ca83b4e63b90c90c46864a2841bb66a3b34a1fa7fbc54e2adc4843cbed95184271544ad1c9fe634730e6331f6627d6f93003998dde2c91dd3412af0695a7c59780eab8377b4aacdd06cb1a22634f88fdf604a828b06ed27248304a8d34a592636b60a309d6a0ebbbd1a10a894e2fb3628fb3da68cd5add5e6fc4a86fb9fd54db1edccf2f3e8e8f88e5e4b84e68cf3a1b738f2a846a0bcaba93a2cf2ebfb8e90f0c7e171be84cb5b949bad6b19e036bac8febb387fc8f773d7533cc0701fac2fa77e0d645ff2cfb493edc87e8fbf2503a961c1fda97b9d403c5a61ebd13ef218b0af0a0f36482cc4f506fbcc47daf07a6c525bf1f3dc08aa77d3c90ad5fc3d1f1620280648c19a33b191c8259fa1ae18a4bc8f4d44d0f203571820a1662680b5ff14151d33633bf1743c2c33463e8a0408762a149f55f06d0c4d2f69e2c25b6f689639a94ba484dab52fd6e1931835e517a0f475efdc235f50609a6cd99e2817b2d9e1afd4bfde743b53e54a75ba9c9c6c17960b317e1f48f31b07fb1dd878e8407f57c187c02d36e4a68644f601a1837c07ec6453f9a6b99db00b714ee93680e86ad204fc506b8903aba06f8681d4970999a250dc5c85f1dbdd2c4316ed115019a9e6cde7da92bf0c24da9159873379fba2a926fb4332dfdd6ff418cdf024ec9eeeab2668ad1a3c1d3e2b1de09b6ed19d610f6723abf25d1c8f0a852f2ba977d66ad80bcc90bfea283202a8baf8e7e80761196dec82277ad5c86a4f0b8e0cc41c947983fbadeaaa711192e7e161b61b594baa5f39701575237d681b79f6578cdbc74d526b2a0534071d78aa9cf49f6d818403e4d82a3cd1e4077dcd281ec2b0980dd46af6f461a18cba7aa4392618c56b415ddb936ab4cd961e999b3b0a5843914d6538f0a15fb0aec51ac2932c6d67b824f2172f1ece4166c82b91799f8eb5c40bfc3d4481d263e75bbc93dd6f22d8922633e96aa3dcc16e730332bf96f28512ec07367cbc8634b846ce63c825879ee29ea440fd6a31daa6b751e71a7b9b11aa264871387fb8892f06914b0a0ad5d72cd8d81f2372d873548e587a4df0747459ea1e2e38b2e8d6de5bde3572aa30da6601b1b39d9ea887e2ccf88382430831aa4fa1e04667fdb580308235259d827520807d3bcc5a3ef169d46a2b8eed530aeb63ba26590bcab0325bb8cce3518f90914418c2146587270d13a2ad53852a90ff6072f7a3089ad9254dba052bb00f0761e063ed9b21afa1258710ff433986a577b0e4b63875d8f3414dacd41438a8b196b35a790d0713fc90ed194852ec02ae10dd6a1019d61ff630a9bccd061c49f68057ab80b0bb1d7e9baab67778d4fa88b3d451937f73061b4951840d3e799182e6a1cfc80332f1400457234390e330bc35b72db5f9d8a33cf2a6c258d07b0d35923cfbd8a098a5abf8c5647553d071bc42b48329ba099b943272b135053d63951399062c1a7688f632417f312e84499c8c3c1aaa45c1154c69ae145e6fd21329fc8146184ef290367b1d90fd49d176d1b714a4d1974175801e2059ca0060488493d96726b6636525d3e20d08aeeb2a187a81bbb9b59429ffa928c4a0c2979b3a4aa2acd2d1cef448c884c8a465eec91a5293bf6a6a8772cf1aee24750a28651f881615882a36801cae01b57275551a37ddc9d86413b67b9040bc4cc871ec4faee1930b42c3c62c03cddb3eb04ff444a22110c00258251740890a001332d22e22dadffe1fb40c19bf99eee7fe203bd6bd4c8dd002d2a753b29e6832467dd7c7a6715f1922a1dbb0c3b02914b7ee16d208449fc55b04ee6931bd9fa6ca82c2889c28d0549641693eaef3c90081ab74efa6900b90a58950fd2ed454a911b8ac5c8b4e0f562b882f312a7be03eb963840baff3fca946c51b34c72f9bca90ff910c839852391bc45610870ff926e07e6ef5f9031b0ac9c28af888dfd466b486b1d404320380bd8dbed291c06d36bfe8a312e13596ac43030358b554c5a01231661cffb8cfe4b2111228010a9288ca4117966192e0719126eabb97ce01bfe46b852df3f65c26b73bb3b28251a4bb3964dc36aae8ef1e63b444a701dd6c4e31b3c55244de87e098648142683385ca3870a9908f9faa83ba8140a4212ba004b4f9a73060a25e98715231c0846c4b6fb4a9bc26d05daf58bfcec5ee4f970c2befd6dd9ea146f73b9578fe952ad1482892bc69f0c4bb88d2a6bb0fa14d4cbf2170ffa656186f4856a619fe9cb0ef3ee200e0508460df30a2328c85b30192feafbab1007b58ce8ed512c7608576182203f418a3d82245e2f229730a9fc436fd3aa19b6104ea96fb964751e5089d71381cd183a4370faded01970bf11d3cf9af4bfb3a34c3cecb315bb03ae806ac0992e65ed45e1f745c129d7428520c0b8832389a1195824f170a199c5fcb7ae6ea0f3f20c1e666d7d0dcbee659f790400fd3060aebb652e9debc51c8341b46280c7fe8016b8b3612aca83323ae61c60e6a490c40c9e60edb8823d48acc8a706b75fe8fb9c2946245bfb7f9b60351bcb8151ea0bedaca95de18e36bd740851d5121b4ea49b70dca3d4315906f6f2d5149b1b2ea45f0659852bd57fb3cec108646d8bccbe01ef077da0576a8a16914b680119c8c5348d25dae6a867eb3d94f89c0578908cade537351c0b5200268a888d00d62f74572d92105038b1a169e128b20e45ca24d7ad1d54344e0efd25120e1021b97b4df20c2bf90fe90f5e12688045fcb33425ef76dd865e3bb087161aaa82434bbc8c8655dbbb0d81acbb41a1efd6f5e11221046b4c9729e9fed43dac66d640e8a915290d5946d9f60a1dfa42f61233e5d75321c471958a032ed840a5d24763fb248c7cf7b692bb1f46e75c840bc88a6d0f50b0382468104009121ce7930f321686b124f24e043a7f1a10e13eee1b69dcb12da8be02786fe01adb0657890f5d44b07c43a2951926b45ba41181cba1b18b1e24001b7d03f40fd94cac9d6ebc2e23553692775a1002f55835ad6b9fc1d12c0884382f6b17cea47c732b8ce48d2461b16196dc7d12af00ea50af2293fa154e59933ab531ed7cd8ed52dcbf2658ea9041ffd1e0c3661cefb954bd405994a440b3a62c1d0e1438cdf0604dd45a8e5f4e28162bdf174a0b7d0b876dcd280c5c923222a9dc084bf281e7fc8972fbfc171c2ee5f03b5b9154bd130fb4f3c902ac21a0cf2f17e02aa6be69a11766615fa7b23ada6fc2e013ff8bc7134ba8f8ff078d2f83b5521bb24b78b484f921957fc1f1685b6233246e1eac421c893ac1b8c5d8ee88d8db691e9ba88d340dffe6338211eaeb7e9fdb10021f578b1e4e1faf1943e2da422a6aa2cbfcd86e7f738a6cf5cffdfc9d31940e982c02f8314bb691c9aa1049b286b813fa6c2ea7be9174b850ba7822855ac157ad755da38a63b64d61f80ddeea7d79ff18f1f039be81d8e0a1a1c5d3cbb1a2253926079a6cc6c6dfbca8c7bea601243802079bd29c75b9a50cecc85a31cea1200cf933abb95e26a59dbdb2645a4fbce1d11b73412e477c2390769cc6baac9ea232b4542a85f91d79bfd27943a7d9b747c526aeb846ae84d31acda5da4bf414ad612727e7c1c959475cd095136d8eaf0dd07766404a9d8a1211e1f5782de066bf09de42ac59c4f866297506face4df046694cf94814d3971abba3af18ef5582b8243f783fb477e99960789f5729e207be748bb1f6ec964b8feee88e2278bff93c7d162c9fe794d8f34c81d568f1000bf3424cb033336b9ea7917aeccc41b40577d385e9cfc17144053c9e648c22172964d3c471381c3cb2a877c3fcd562f29ef1de1accd196dac5772f92d400b94c27d8e4f1f6e5a92ca6c00128aaea92319deed46b58aba56579af5d3c7781f172c805103577af831042276b78c803cdf799b309ea184fe5d44371663cfcf5b5b4fb495203a3c658da743c48f3dc78e962a9699591027a0062515d46fe5d3741064ce0538af91a747fd1d0c49fbd7ccf81c552f46fcd75b99941675377cfe0865b3b63cefbf8313514c0b2f9a037fc542cb3a46d6cd29d4ed114796751ba168f0f34ccf8a8a4ecbe0ed52d69b649a7e875f00e32967f6dcdb44d822abf6c142e42580d27bbd25baa61a341131dd0a6a68a224bf055a4637d3bd6bf7f7fbe5b509081812522545d3d7f049d7ea93b9ca22527248f5094e4538e9c042980d89ec4342834e846f3c7c7d01b47913b1330cfff2bf9d07be0f43cbbd1453f4e42316954cea6e4f19683cdaefac2b75e1fa07a6c3172fe4009206bdb586759951d7fcb2a17a9b245af9330666b52ae46b63f82d48dadb62533744e7db1b67e0149ba30b62cd1446bcadaae12f94d99210f2700d173d230354a6c68213818b08de796044d8ca901ec043f570e546786870a366c923bee48e284b78b8a82246e1b2ae61d666baf90f96d8b6f6b6995b43329ac904bc701c207e82bef26f4bed9865966bfefc4731cce3f4fa40f687f0709b1e9fd99e7e06b1d0763b4d2ae99284e90b96cac6b44dbbdb9d6a059afc29010590fb29d071c20f4e1e41b7f178091b71f3f9098e653a6e294686b5c616fd6090a839b86d9ea88f911c12953f525719d4a41f177e893f9a7ac8acc5fe2bd09e57acf7122584b2b55cc30490b51d89ad6dc7de20158809e89aac40d6b673965784c0ead222d564e8f5b33cb51843bd737cbcbf53a90546dc84868e11ab170800f2a4da4f82904b75c862909a6a23b421146ef8d1714f59b4db966da56e4088872d88bd290c96de677dba023c80d553a1539aa10d80d3e5a7a50f5f6be924adb5746112803c4460a1be945472bc1b477e654de7760e6a565e0f27fdea477efb88efb9a77e00158db10ff1d1fa8b7c2d336d7f641cd1153a3af01ad90d6d51c7dd58eaeb75b369de120f79976221c495b0fd2175e7067c1dabff0c1082aba9569e5814812cd96694d076f017c345ba8a583955c7457d2ea812a5a9495d5d20edcac64b42daad5c18b543457d0c283b4f940a08c6aa1d60e2249d169252d3cf84445555c772f22dee0368a2d7ca841796af1907ab5941c6dbb2f8d0eb73013e9d5917ea07431f34af9d626e645b067519dd00f935eb09beea30973542024834b8b18e0eaf6444b171b753444f08fc5a272ec437969c0a489952fcdb2a09f37bb5ab4552eec9639d15bf6b9709df45a42bdb0beca85bfc01b6238f7cb450f601fa859a0dc689fc92617d0c314648463276b1e1df6040760e9f85302de4abc0e93279da28548ad036bbd806a4e1a4b5aab48db7e6a08412dfc76c97098cfbd630e029fd7f1be60b858e9f37fbe506bad4e29806a58b4b3d21fe5091b33a7bcb86b632ff3a09f7a2733452781434e4ac6211eb3d86ad151d3260815efba86ac87937cfed81d30087b7720fea14574284ee6b7d59c2c9578051c5747c1df0a928b5eabcd802b610eb936882bdf113a6cf6023d9e1e311e39bbd12ef00a0a8dcd0cb54d1ce43b0d7eafb7f2cb872db981145b02bb1a41bcc7817dfe4f8a945f5e55605f421fa879ae340e769dc1559d500be2fb690761c6f05a7fec8888556fd5ed02beb4438c2fbfb56786b8ed50569b414f2dd4746344dc8511809494669429151b1616d77cb78b8fc5d99a768a3f1356b40d9198ce7f6ea8c8058ad254948016bde7b0ab9fcf54c26d96e170a150b7fed4168d26550f623ea26802ed1785623832a68a2473fb27a45a91c15907e4605027178a58e032340ddd63be8a63b967735d545cb53b8d2307a536602fc45c39aa5903a9a0a7c1ea67bfe9d82cc52c88c6cca68fa53df8e2bcb85954c70f1dd86ce30b02df353c6931131d7f80a6b3c707cb5b8b061ed08f559c93f5a811f467420868c75270c366ea1d76b0b8e9488253aa5d0f9b9009fdf624a917a9cb2ffe033fa8f287473ec6f2f47ded260dcbe6622a00b0cb0e218ae15f0d1b8eef3aba6c80d97f783337379fef70a8aad1b1df0eff3188589c15e4d7666f41b96a58df1664d9962557ae6b3f9084a1de3aadb9407ed5013778713c372221482ac2eee465a9b47c4afadba01629a603cfa31f39b9ff4896eacf00c39260675e6908f8915a5737d28e55cbe8634b0aca89c94710d17a9af877de4b32f1fff5520b1aaaeb09a2067658654101882405fab32754d2856ed7f455094351eccd5da678a33209e63f09b50ec549f1f08c0e74621a5942394e78e8809b069f8c2fc47cbe5a6b602369c164c2c249337b0a9818daf96776763b2c00a980ae6479946a523deb515266f559dbb6c13f17013bfc80b21736987c1569ef23a09282ba7442bf0d510fe201fc486b771c9f03a66a57a3f0bf8b9b28803fc531b3813b067e80cef25f575cfd22449f58a8eebc50b5e39d599977cafd8ab58e53aec19770ee54c0437ee9ed03646ef46dd158886687d7224636d72db32d85b41a5e5db4ededa21170c4ecbac6b7fe8e61eb8f4cbffeb673bc11a0d5809d0e2cebeba0ec6fa1b74448d9fbf3652bcb9a7b3091747662be3aa9c9b60551f80e18189df2dac0d04abe11b198c8209055226fb705ac9cbdf0a6dc474aabca96d86f4415745222ee79b884b0ef0eaeb4ae613d5521ed690ba62249cb85f6acec030a49c68785cf941de45ba8e9f8242cb24c09007c9e311a6b8ccb90dda25b363a59fb8693aaf7adf2d7e934d4f5f537e658306b7a3bc9f0a74b4e0949a7a91119f36518a2400241da868602f06a18000202c13524cd3f39398442a850cd99002ab8beeffb1699cdd0719fda001a4a303e116f38a7741b91239480c9861a5019e990d300323aef2a029e8fa8e5c0ce754b34342486aee483b89f9117a7b05b755c332ef0f2292d99970d367df1ac5c4aa82ef7b0fb4b08483aa7c1b11a9b19d75213f7b7162d589aff9ddcf5e3ca01b3c1589e7cc7f086a3b8676b01511e998605f672073c2f37db956c5daf701ecfb6d8a1a0e2629b509c426bbe8509a52d510a32fb8d5870b31d92b8909ce9e33feaedcf48328435e16f1a88ffda92165310badd6931208ddbd000c60ffb0af303a96d6d9d4d7d000359e4777100b1f19f0062f5626d0a57723698b01caacafcf42401b4123ba383fb1cca6c4c602394259ffbb05d7e6d2efd93012aa37dc56374713786fa56b0b5ba582559255f0936ec2277d612492f358a43bab6eced8ee3ebbd85e09e5b47ff87217ae843bfc2639654238abdb5981235baca9e81432ed4a538a135e9b1c412a0766d4052fa2dfb5d5f46720cc4ce066240ae5b230ba78875a95d09c028da1e6829cc99941a6ee74591570fceab1e734856d2488ef8c28b5fd088356d65e281cb6b55957a1a3e65993e2f62a79ec7cdc66fc9614c7b5202d36be0d464e592b2056e9ff41b380b1b0633d57c8ed464d4a003d8cdc8c18c46d3a3e7fe4369c712a7839ebbc0ef759e71dbe3be456cb0d7be33add08a56e2a5d4fc7f56d35193193b3b8999bc468c77bd449d14120c40ef6064d7ee7de73305b99216ec9628c80d8d62c80b89348f50adc7462b036b40404163d7bd04592b24c780e471851b87a09a33ae381c828ce4eca60b06f62f75293fc011772bc6bedee7b6469a3a1614dcf48e578cb5b79981f1a1c8b13664c29858b5a53844a3629cb691f517d28bd7c624c36ff78e8b4327019113f1cfafc8a6f7ea9734f269873b8428ae843c09265553e22ea495a6bc7d2e57bd50db41018753cb5332b8831ca5cae08b3d1b84b834483a5959b1b11149ffe32d7557455dc1232206bdd3c091a8e3aac9dfed8c8114ed15541368c95da3888ede0981cdbbf8ac423e9dc3af6738db74bdb79370b8965392ed8161c719cc44db4ccb073e5ee4a3f3008b671f608298d0e1fb6cebbc934e2fac7bef21934a931a00d3244d8710b6e116ab56f91bad0b82f8bf64ad19b548a38f2b0150ac026ba223f0078a504d9550f301702b5dfbbe893e4752d702910d479dd9da820e718bd2edf4340049680f8f5c4609d6a9110fb878b918400158b390d2400b216ab03b095af4d8f767795332be4da14273a49420e3ab3e65b7f4cc6e0a66ecdffa2af7ea7e459fdc0501d74b73ca6bad19eb60a1c629ddd50f9471e2f6286419e94ba7ac0c8195de30e67f4996683a67b15607e3544e1ffcfd4a28e101433510b47bac0025669d3cb149fae2025f16fa487be1405ebc68b268e44109dcb5306733bc8fbdb36d6decb049aa1c370bedfa2a3143cefb5ca4e1f58720cc00d58b42cda2c0a623fe2936b18ffae79e5bf52a13382f81d3de2fb53a5f45cff5057d6414b509b142643984ac32323cdb5c6e0e167e06955830e17b809c7a82dae9157e38a1a5b1935b30d22c38a52a74b8c95717c17339af4fb8fa4cb884472375ee10ba9d7b4285eabfb063100cfdebb88ea49d05443e1dfaa2f2be6e9aaf404a3719ffc8fbaab90fb6bc67f2e5b3866c36d3591012904029bd3b7e20139e4e51d56a0cc480a93e5a0feef701d43941a60d5d1d1c1fa0d9a167de2bbf7a4006eb69447d51bdfb8554f997bbefca0dc5b610c572db8cf6d15abbb7bafe577de45cc762db760fdd33a47d0fbaee4bb24f4ce94bdf56df9d09680d8f6a12f9c91788070ed8f933916ddf7c78f8222abc4f18d7d8cb27c1222b5bab5e6ceb8c70f66e44c2e76aabe4241cb2612bded706d3ac4fac7b50649227d3c3fe96a4a63e9baab349707ccd0aa4389f599bae6784ff25927d9d41927be22ec2dcc092afdd36d4228f2d6da2227d2bf0a5e918d1e9cc9d8f0e4f7d6c9fc795ba63964d15bef708902651c82fb30a9c7ac98c069872544f380050c2c079d59b753bb66a586d1d03caa9804be3c5edb3bb99062bf208007539368016f34c2692b687ab9b6e732e7ee74275bfaf3f01b12d980e9ffdfcbd9e774b92dcd11a55084f8816646324996aa9271f9c88b780ceb55f64258293a6aabe339189bcbe4273433b99518e4dfbdabb6f96c5cfbd69e3bd03b742028e894c56a49fdb36d6f4c7e155ffa21f4bda1d32dcc1dac85a78351ecb0f4167137ab10ce18fe3d1814fa33fe07f3f175f86357d1bae2b57882b5e372d76c1f162e4016e1c92e1d37801a27e94f85b82ee9cf10d1f99cdf2ff94f58f765d1d537ed0a85273942362785c3281ef7a6bf0cdf5f26e5e28da123fcdeb55013704bd34996596e6ff99aa226552d53972d73ca00396dab7fc8cbab52d1f0ae6bf2d3d8419f96d1d3641a5728ed18aa29b2022478188a19c54d9dcc5eb0db4104434ff7427eb72088b82d4d59c16981e5623545951fe0b6f701a2e3e4f6a7a1f73054ea651efe529985fbf19406e2f8ce23c6dc7ec0adc7b75850392b66dce061866942ad575c276f23971c63f9a798f2b671cf0dd2f3beada409866563c827dfee7a8933041a0d1df2fb9e080553fe7902b78d6db5c7842b5787f05bdd8d84532a98a58a3ead10b92b54a6d129d7516be17def96c5b636150845fe0bfe31a06b3580f9e0a5a9d0db4f012e5aa71940bb6b7f25b3786ac05e109cb11efc913d033688f5acdc957721d701c3430651c97f873f56aed2efc9fa467d88b800ec7efd96d6ea03026f5c26be6f4a6d354b0d94e0b20dd62d747e50188994c1499d5c793a687900ae18152e574c57ab14b0b7dcc5ff5905311866838dfc51b06a27fa44d0785c74f76937e55a83824dc773370fc1a6a35cb0e5a54398dd79894eaffd24e1e238b43a9f463ed45a29cc7f5a604053e3eff3fabed153a69bb02247238bd4b29b3047d17daeda575c412be79ad227e31bd6f6df4ffa956ab214cb95a46dc96348284742a2490bb8b8940c23e05e795a2709d514345fd898b3a0b5383157008696ef5fe0ce34c13b995492f8c79554a23fe036c8ea82b93cccbf175a5e3782ed92f43cc5c843d2c143b0c56d74fd95304179afd7c0dfa8dcb2a185a7a6e5b2b8e442e45f3f66fc17946983a9e9a99fdea1e8fa946c82ac7556c04415c1712c70b03ded7d7b6123f6e0510bf5835c4407cc9675b5b4717675a552820ee58bad4045926cf35ebbf96f89ff48a38697d15ec7b24c1a1297a16fd1a911940c82745db01889c39548c184d92e33cfcc3617731e2531401ee7590a4a4a92a931d99cd93491fda89380f02ec8e1ba46d0c04365f372dda7b04a70937074862688f628e05cb387ad52a3980b00b612ff76667f0c20467631c0a2acd9d7c02e8a3a1b28edf9d0344e1f12af08602eb713a0476a6a7a50fde937000188eeaa04253f44ea8e956f3286ca4743b2d9d83dd558ae86e6ca8684949117b37fd6d3b8a8909fdefd0397760c94f34a099110680f631ffbd46e48124984464e4aab83067ce478b4479ee9130bacc787d0f1eb97340cefa95db97b925989fccbfb86e6430749af7e1b56d0e6b058324a4128c1243f5e6ad038fa91e903e76dc0161bb565939895063e1d258fe36f9230300dec879898d81fc98a7d99655a4bfdb6ff9d1d320b463a4a9c01117c96114a1d53964543e7afee0cac406e4aa069e3c6c7125196f3bf9f17a209b23cb480cecd5434fe08627c4357aad90f3194eab7aff140e18c3f0329781449bcf42448f3ee96c8390c638a0c2334ad749431cb121ae0b2618e39a92c6db842eb0b5b1f60dc7cb34b67e7164a83b199b02580e1fafb3e26fb44b15f4e36d44b6eae9c3256faa84fec364a8a4f83ea0493bf54ce926c423f71b138d0a4dd5cd1d76c079c0d73c97996e8545df6bfc85f177b713798481c681e8b90f4f5e0a9bde574cb62f35441c56e87dee76e4b09e90e24f2bff7a89b8e340ce8af0cbb7ef94bad9df15dab7d104f9a4f62324a420ea89277d6da438dedf3cb2e8a657c29df9318e13d042859f79643b3a265e720349719ec743e69a152061cc6b3b3da45c0f50e1ad0b888875dd23f5413ceb1660891eda8c4294a8a43580a7a0406248df2183249b19d8a1d179bdeeabb1ddf3ce6bda84894be20160209374f69fec29938fe217aa02f6b979d9e93e26f49ed5ae8c69609506f7bdbb21cb9cc6521d2e9c738224d20dc7a3bc4b8ee7bf696f5dbb9c3352aee739639aabd800a56591841dab86b49de22301d3de698b03c2eb1b310f84ca42e9bb6ab3b22c4b6c832e91e9b2ba7844efa8ef4e2be829401610824e33b440d704bb861e23b39345db6e394931311c6b6f5c2680911f0c83a0cb0b6df7674f691edc841f4da30dc6237e43e882e993586c44ded6422b2a9dc52548d52ec919dcf806472f48804a11ac7c511423f20f7ae6d9c65c8694d1e94d1e970bd3933396204622c33bdabc8050b35eca2c1d8e4436e429e4e83eac9aafb47a5b0f3654393023a643e4a4d61a2de5e945772601240bcc1a7f30076c120ced526559aff5d977274be355836c4c15819f25502116cbef787a69a8e48d06e99cf41ccaee086e5bcc1d3e55677236bf35c7540df936d6af2f831280ceaa735128f10b25bbdf6435fd6dba974042165a801a4a5f4db7254e0327db6afae2db40c02ab0ea6d068005ec66b24bba300e87adad2969831316af6eeb4a7cd8bfb602a72c334f1fe1b6c914479d22bb0e41f4694bc74b8c4668eb356a348b6bea2ab2449b17318777cffc491d084e44486e92c6331827422dde812a639441d2b68605096184ffb664e49a3bb05bdced10a816898807f5eacdae9513f41233b607966ada606f86df50b3031504e7f4cea8f2a6e6862ebdb92c9d643d89ac1f15317bd604c3e29fc462a1ba7ddf2ed1f4992694ac97f93c0bb6b2d612d6d7a217a1ac206cc64d855889e2e1309f472ec13435ae5f315beb6bb46259bf07b5ff90b09d8b437f800b7a46a7ffeddc11da0647a5b03157404a989dd7a44e80f96a5719c9787065265fbd58744ab273769fb22553700f0c00a5562b7cf3437631326a0e2906651926365742d8d00009845e1a294374ac42f43ffe7e399e5a63b7de715b22ce75f9158402c5d1c997f9df945b7c553e847b0467b46707a8302f19f87701031563159f50f28573cb8bc162aced081219757b3a955230c26f065a04e8bfb10aeae420c7e02f402c4205a4dd65c14deb4c340007f45abe6d81d8eeaca8eb2e33179f58f7ad880d31d1e10d568c2139f84c95436c72fbe0f54ba83a76d3b47d70f489058ff13f77df26ddf1e8ccc0d41591b5c20ea52a4e83fcd77014b94879aba9ece901873ba630dfa3ec60d9a3ba7927c20bd0b22de23456b93b4e2fbb1792ca303e6d37b02175838d89008c57fd3b1fda5862778491b0e73718f9084606ea8ecbf17a70eda435f7ec877c04d232c08d65d917386f6d6e2750ecc2ed597c6a8bf3685f10c43f0ab2a02b59febac7e7b0c7a80913e6abed3917f4764a432c5095569551268575788b22d98f87e26c0ce76a66c46d420483ff0e0e299a55eeff0c33ca41c70d2b27bd47c8de75dac1712ddb5c58559eff05874f90d8846c6ca7ffcbe95185c233282d3ab10a2fe83bda4906f4e4bde3d33471ffe0e1284ff586956c7690fa85c1435d73b0901772d413a502cac518437e5fffaeb5d2980a32d5a9654a8775a3772a3b7120f7bbbcf8727ce6bf1008db85571e4f9d4afe0b6d608de548c6d9801fa2d86e540d2236c052f9885f4218fe9988e672a04443fd8b9caeffcb50aaf9e86eefa66a30c8da2ec69e4ff58ddc6e64f8ad8b743a164fbf3d934b1856b0848a5dea6de4882ae3b5e47c5b7f9f8953d89d94359b7f5a34a50e74bb75e91b688ddecdd746ef6fe445426145fb1300c8451a4ed5da56df74970264c0967786cda6c81f9f387f4e02a97bb8199f16850786154ae57ea38b4df9d530bf9a85749341f831f2dc5853315a65f5683c31c6a40a67109c00d39878ae1bb9784009956d231217b2b9368a0499b9d153281739c7a80dee4172cdc9d2921b275ebcef1c8d2ffadac0a67234ae44d0f972a165413973b507435655eaf434716bd18ff321d0adb18d5bc03050fd6031ba318af09517e1d38d3e1030315f2085609880cb7dbc1da5e78353a1819ffbc9c79b3e73792bd84ed4e7fd344faeb1491377cd67895a940cf80b3ed33865f4c87bcae9d6d3fe8fed7d391006fc08141478ac988360b81716e7e49a4641c90264463d9a5d42163539bd69bd3065f138eab08d7ebcaa43521fca540423e19777f4d09e162f85948ec4d1422d0e1c3db79d650da8b3b18d8825a407282f67b0886a26948fee500d6512ac1cf16ac0cd0d96999b050f83873117f296a30bf83768a56a0e911645532627729a44a9ad3b8bad531b7cf321276234897c265081fd3f967815c200043434483bfa4fe124b986361ba2fb004b1cd4ed70c383443762923fcfbc0d00dc1c9aef1aa5ffd4358d58054caa5a916e36616425ee677745b24ebbc82fba500513eeeada1516c793941c6c8f7654b20fcceedb089c1b4f0abaf12caa2e045102c3e9487c52b1aac59f8a19ee576705d4b092a9ebfd7188914d71ca0e34d837467fee159e03e685bb305646da44087f147066830c7bd07ba3c170cc9453617f1f9fb07da5b65948322a733d897698624d5d2420dfd0e064dab1fe31414021cf968b5b06e41c7d80d7977b38364abffc68ef858a93f43b716a276ea40c914ccb22ae5f859b5ade943aa16a833acd47f4d95d8bace19d8854a06f8b108a40130ba04566b3b3125f7d95bfa0f3229a9afdf4cab3e8e051c945239bce60008ae7eb4cd6cd4798c1211aad38cccaa0bc7a7e098ad952b6f754e3efa56217729f915ccf7084ac22805d1b272629aa3751fe7624a801929c676a71190a9b0647182a88a40ebfad781dabc035a89d86117e9543ed478cce67426b7013bd9ed5f8949ce1bd27e0f35446a4c4e1a041aa9b3eb454a091964ca52d9b42180247e7b6a072cef508881f37a57ad482e68f3e1ffc0660adad216eff2d97ce26f47835661bcdf94f729b4a74e995d6bea74869ba7d13776a90568294f5446c0ae0785e174355bd0e926359e15c1a35a3f96845315170332e31c97c26a092d49e975ca02f0be2baabfb67979f7304722003ebd9761f804ffc4c64ed900051160174d91c280f6764aa573e19cb1872786ed871b1db3b72e319b6d789635b2abb017451aeed391f5f0bd87af0610fd5d957611f8c375e2890bfd60b9b37a911d9166fab96a5d3d017315fee08a809360d1d026d40057be88a165d8bc5f40ddd912400443f1f37653176bbcafc24aa47873cc0ad55312160f677c051cc5da4e549df151569db359e813289cc8cde7365a81a69639d8eb4f40b3c3cd4cbc31d0ccc9f8297e92513e5cdf79bad09fd1ece6c1e5d72b167e216c0073f8d060e19faefb8d3cdbaa1a3ef7c30f9647646620b7a592d8e771eefbca2e9baea48702d83d1be0b31afb82d91d89208640880acbb6545b16bd9940e011611c65de1263e4dfd2508359935aeda1b384803253b5430e85eb49d1e63017e5ad9f8689c206427d72c75bdf46026ec507b17a1089ec45e9814218c6c0ab6a130a3cdb7da94e43cd27b3245c73cae5e8adb5f38526dda695770711e44a9f270cb12392bf30519867ffbd95b84b324388f4e73921e15c782080e2dfc2f66e58571ce146eb1b27cd8020f30b47c0173b5cec10eb8f60e76f5e3120b77855c3cc9c00edaaf2b3fc7c10e79a437b8d16a09df434118a26715fd66e3ae5000263f0788dfdf2e30ef190c5392dd4b65f757cd55b848d2ab091d0586afeecaae5266d94873353f70bbaec7b94dedb99794c202282f5e179702a31b2032de44090878b9f27687c8de65458e28f2c506ad73d4a80ae56f38a157b6e4e641a60e9b4614ba5b9fec4f4d722a8b6f405221322e28f1fc81f746067963cdcee6316d614819dc92d103d8704954f4dd41843657caa2e650ef524359cba7ac4153cb8b408056f67c5188caa20165bc9d763bcd100cda0c695bada00376b2bd63a62b6cbebfc35c5bd01fe6b73b6618137c53eaa3ae9c7cdf610408da17628c744b92e0e89563b0e1a0d3a903f55ec03b93e0e3a8f6b44e3169e3cecc77ecfa60bf0ceba610c27e4994b02db437b7b30f20484304109e4a208b4825e003acb9f5232039df566a353fbc4c9b20507a3fb956eca5640912a1443a61197bb4935490f7b42e10754b4846d81fb4723da2438577671c44a53128c59dd015daca4dc48601c700f7bd5407a0fbb8a80240b73003a8a478e2e426af5d90d5bb15a2708631a3588e37c2bba452920d76181dc054d969c46854b9ac4526ade2a6d841ac012a75e68f1d369d6581d9c26a10c9933a2b8972c1dc2c5ef1812f5d772abbe053d179a753f57f04bbb3c959d90a8c451b760afbeb7af669233291fd4fda0d45ee99985ee0c7aac9e4ba3d5ad1d8707385aad019d1b826942464287a930fd5b45fbd0ca644356e87d4223d0636d2ada83b7228d2b424a0a0bba31a246ba452118039d58ab8117591a0e40105da2dda462646b234fb526c3de6c882cf24469b8b505ddda3dbc0e8f589c05703b0b420421702d239cc7052660dae3461ef9d61284faa3e021a69d77e427f609fb8a1dfb1313b0226ddc1664dce104dad78d6c21965ba5ef6260d99bfdba750179e9c6e6fe48dbe9134a2d318b51ce05316d652151254e88b19e97fe2040e98558602f055562b246dda17a8fc6bfd241c0daa007a4e232c24654fbbea54d8a9c693ba86eb989f12eed1566a7aa8b795019728566b5159659c8dd2049f2f38dd819152319554c508b15d85d69ac0ae9e5d551e84919aed41e195d4a78d4c61aacde19f8acb6956ae09e32c3ee206178795082549784158040ec2da298aa8ee88d37e8fd71f825e8be2b0830802867d5bb42fe8613a5c0169249078e967b17065b51803ff08683d59256135522152a422709ab722d65f6cbe553814f86bb80756bc3c3946c9f7abce3ea7d70c804ef531b17ade2f4f21f3d2adae2f1716ff43e940c080e7a4434e93062fbb6eedc752065f417757f79b0d0943da1f4cbc53dd07ac61338a6340e50f9ec2c4ba6db4d285e37e87765ee3ac1dda13d512a46ac473d6907f244ef2400745a200ec1c863e5416c549d4be7dcfdae03315bd6231a8c7e138534607fb45d245f4cbcf2582e74d3c6de6150c7f72203b9be5783e574df67826f3eee9439b3992d1ea1e19e40865a7178a54ee4612db2b765066d3435f2d498bed4172480bd20e1df6d32e1a689909f464a8213d916c53eb6cb73fe7675fc131813d213491206e286f06be8d8bb225028bcbd1224c3422c54f1cf62a0d6df43be502f16cd8174bd61b5dd802c65ec3bbb4c0af8f13d5bc3dbddf89613075094eb337f6a02a347cd369277aef60672510447024675939c78c68b5ec78415beaf2a9246ed29eb642c92d8129a0401363e8523e6195762b3e88dcb2a1b97954658bfb8c186fd98c63b452fe81f5d2119eb40fbd9a4018a3dacb6e6790e3930d9369a5c08023394b48278900221371955ccb3ca06802bb0232fe7241e27e42ef6c5add4ff062f043efb41fd0c51def906c0e4b230d19620677522564157626538a6be4148267142ef97663ce0dac12e10bbed816504f06b8ed0684d967177377ea0cb4909331150d42b92efce5abfefbc7c9420403c58aca25da72706ff24cdd940bb4c78ed21f56d7e512ad461feec04831fda26af999e367c9a692306dfa2c3e87e088c1990ca7baa75b615fbc1251202fe9f108674da82b51328db082e6ba298367a4af744b74b2de8df079e7f50474ea8be5638a639d601a3add439179448135f79e6bb6ca9c4e6879c90cb67bfda58b30f97ab2f6d22d5d731ad03bc28534a9d56820637dd50e60150e1ff10126399930686bedc55d77838fe6ff601f052256fe4f463e832fe0ab07af6ec8dfeda5cb9a982858650977ede035186cd37a8fcd6c3017002c0a2761b37c88b324bc2789409655a0e9563cc77ee91c3a515c8379e8a1bb4da68e4b28edd4a844d6d8886b2aee2356bb1744e8dfcab256d55d10f10551049ebf13ec0ba09c69e6a4a06c174b130963e6b360b9892a4816423a6b5a050ea494dd8dda773af7f0ed510e5e67c82d78d657118a9c23c1433ea0c333e5b743771444c63edc84c02a84a7e449be2eee2534add49cf3f100f50ae2d02c7ae237a326fefb043e4dab5464c81f6f2d933a0280b1edd4b3e38a4ec2a712d18d45f91e43fcc15255eebd714a4e117e8dff15a5136a0437d8135c78f7cb87f06ce18e87784715ee8489d41eec49033c99e214fbd8f1d7f0000efffd508b4479be63649592ca64eea62b382738c45fbae1945f24feacd1213a3fe8ac8b03a1b383cbf93811dd13bbe195644c30c000bd2215d98195a50441cc94f8f44d897d6611da083a7482f69dc1f158ef791a04282f8c24a8aa859bb3fdeab332e565e282a8b11a5c8abb93bcde8f58cf68117d7800ecbf84c16c8e458f06fdf6730a551d49f03d1032a53d12778895b8285b10139e35b68d96c11fb6e6b3f06d13cdb85c98543c9e2885d64862a71e19de692bb7b960c1a2fcc98072bd4ea3dedb22f5fc3a1bbde2f7fb753aaa287b3a0fe909a3d18b1da2180c84b662de646230fd8188356c6fab1ba2fb429c1d454c814228abb35cb0fa9662a5146443f1ff01508b1970876a083be050f0823baa6aadf38ec707ad40b98f2e711a29bf88ca2c32a331a4ac5975762e45e953b863e826312853eaa631a29982206f46b0e9a44d0c5b1952928d464c33a385d527ffde0dff2d3ac3d8a94c5b8ffe37acaac134b5d19c5d5223a91ffb30729ca6364d14bbe68b428de845951b317a33dcad4c586088f84660155502829fd9aed878df1ff9e2d57eb9a79221b9afa5afece26bfbedd5901e57c90a618d8e2544d0ca28ee5cbd22af637185acbe63d71c399e312815bdffad0c79a8e920bcb5895043c7d508d96ea3225ed199ddd57004d92bfd01bf4c3e368ea60c61868b9f22fcb5a37476fd724c8a70d6cd3907f3c2b60c9585f7f2cdbac0d1b49fe30578b1fa7f090945aec926be4cffa170db61a5780795ba41164b1934b71ea9e11339ed29652d42a6e74681a8641b6d4c9e0863543c6005e93c94f8d9bd291284aa0e6e183671ca15191893b22494db0a32193d2135fa6977dcc935fc6cc72dabff358c5476dbaae49439ef3873c116249292bd5140481b139d684a00844be00158a5e1f16c0ce3cd75a78d319c4496f2ba62e4d6388974c744da645ff2f5ab452b3ccf478609016876fee5208adcfb40afbbf015d42ec88f56644c5b5caf687789493edff44f68e85acc048597aaf6f48d6963afbb425f7d4f4a7852b5a8ab76caddf6a36b21e0fa4bc69e6538e7088cc4b87c77a758e60a63f1e9220aa1374bb9a86c1099104312f0301a65b2614994a44e7c7125736c4e6e180dc4f38b580561866e74dc2919604a2405de2141a3bf68817df40d3c8b43daabd131f53807dc8c49fe289069b72186cdc3c9d2842902d06162d7a64b21c3ee7daf0c608c2592c58c60229d1c0868cb2791cfe019df4d509d26f199794528aa712b1d135f229f994cdbb373f807412e2911637682d228236c5e6aa8f92114315b2ca2af9a4d3d5ed47a293dbf2fcea8b00d812e13dd7ab04de8ac78d0da7010d962ed0b5c64803ef5c7b2c262b4935264fea53d2fb2e19b549c8c69d45635c909bbdc9293f6eadc0de64c13d0b5939a6d30ece9484308347219106c5c646d0acfca7207668b038bd50180b61b8056c84f0a1e9f02cb3c95aa6e208bad3202ff39cdb933c64304f252f3070862921ea6171369faf4e1a57dd6b431722cc2ca517460269450d329dcaf0e0632005302351ac362018aa42e622f9166378a1621686f8db050e9fda066668a90c109941966eeeb5a982d73a8310a9c0d88aa6a09799a2b0bfc4489b90549677847f252e22af290979ced1755ced6fd97b6498957c1120d190368b9f01e6559c1e8ec3663b529c2c3e09ad053612e663a5628a2ea6fc046b418bf231d5d7c57365a9e28968b04330f9b2647f67834c96800dc9e19b92e694dd7968a4d12359d1e96cbd7fc934610eb172029c0f36955962754715a89550884fb77a2911e21c87838d1bf987a6f84754e4914f67bf269e984281e56a32b2e251a9e1af07312233ab9c3843c2f58c4e05db31c8dff88fb5e088704327473a200bda42e1fa43fa672823c488b31dd90e21896515c6654dfd7ac361474398be6e9cacd45c2c7b3b8dded4c60fe083ccce9d9e22418b89fd868913affb144a840ffb5e9ca97281cede2c1661ebbb144a17c7f0bace85fdee8dbfb45657b5dd8f21eea3c3bda57a32b7f1f0c5dc1d7a03909447e1a54796883c3582f9ed17a00a1fea0aad9a7ee391d88f85578a908de1885391048af06f425ea0c3eca24df0b501152a220f69e43c099ecaaa55c249bdefd30c84f0fbb23ea3b14d1db6206caa1483591aabf5919cd0779014204ba25d538e33f526d6e065cb00d3a947e1076142f15390f047b264e23183bbfe9bb917a4d317460aa2a5974d124058a2274eb2132374dbc079b61484081a91de0476d2f0801817119817edbb294af05a7645705365702b0334a46a607e7bc4c69ebb976980131a1cb0889757ac12fad530929d250feffdf23b927d00e022389d44a62df3f593cc1d9f4f17eba850e67a0770e8ff08fb6ed650e6f5e3789d2c31e50781e2b23135529b8a448bd1dc397cd0da31148fd17f04d88b01619fa3a437ad9bf0969bb27821a89704ce3e40c7c38b66ac38cfe13685f9fade91845f3d6b4ece6733cc16fd86e8e1bcabe5b6bf459e60fd4c5a757c07a53e2ec6efc5e6237f9d6c549f91414ebe6cb12d88cdc6a178115505a34b2a94524588c928f37db40f6bf7b37f4076ec3d1d09b2883f111ff71a1439d793c77cfb9f0c33ed45c1ceaddb20ae9a0432bcabd85ccd45ff0d6c04dc813a24e8681c893dfaf3e010d000f9df9025aadec73c008e3845f4fb0a1cb5dfb1ca9d1ff04721b6a46dc6aeef312b3055cbf9e1c6d289daf2ca40fdbf0ca1ad39c5a0dc480e27316f5edb6a8341aaa93e531357e8157e5b64b3c297d68632d83a064a4eb2049a3356dbdd662a115b2e1dba80f1221b3bf91e30c4c9617084d725b39c8da73be9d060bd488182cdec350f4c62de6fee4321624ab54de56ea9564923f51792ce7d955858792007717ab9e5a884ccf0deb87ff9de7f4b9a31480b44442b88304eb8853824fec1c85f3dc23ec7e30a4730b746d48a46788935211f60d7b0153db1c25f137b0cf33cd3d6c19bfb3ab0895ee3d8779797c46b3f1f595fb0f7c1dd3242a4afdbb1d8b06ce3443adaa36a8a8d15ccd8b338cf5d48c6c4d82a16d3efc30013efc7a3d7523a21251043205ad226d383b0a8b5b68a7e44cd337faab371a0dfc8cf6d538d4f5c082b36bc4e79e20ab01042f395808b76f19de364850f6a58a8937cf0f60fb01236ef16004d9c4f7724295e9912f591e21f0258176c830dd8a5054226dd17a950613a5850ae2dbf3c1a847ca85259268b184865732dbb0ff2ee9060b81a52fb81e4976c92f14cc42045ca049cbb9314db1bed0796396fe2c93ddb03a5689bee9d78a2526bf70fe98d50759962d13d93307521855e2052ee2428637aea59652be6a08bf701ae89bd03aebc697f936021e9900ff69c5fbdd1741c96c77e03aee3ee3304715d8942b1d697f97fcbac49ee8ffb620fe99aad2e6b7c1c85a5441a8d237dc069cc32917f8e84d91b092953a00309a36b6eabfa5e8f3d1fa9f32170700582463b551be6498094c1aa3c1350a1c24c889c8cd4ab5f043e0962f9038da8ea145f4d880c35e078588338b6bd29103ac3150e52c1dc8cede213c6609d04e51b37a468318860c7a43c9770b52b56d559581e1eacbb5c0152b131b9b181da220204076b1ae32ca3e28372fd8c9f4372698c84289b2be12408a17e8aa20c8a72288cc8961a3564e1cbff801a204f02555e1538d568b5a6a9810fe02acb427a7c5859a2bfcc0e08bba5d2a033ea234a92f119a228eb6f8bfc0b993b07eb2f68a475040e01bee8d7f6c792637ab252ec08be6b4b68662754d66d37cf6d05fa37e8858634550b51da33e330dc9bc67b7278e5185997ee8ea315934a081f8728bc10000fc36d6e3ca970e2f16a3d90fccd686ee421eb93677318e2600f2bd185e99946283dea0a002662f49e50b1682ede3096d6879e3114236a1b6d8482c082ee018ec3a7a28486b0e6ba5b14b12d36f522d38ff9f25611b8862ef9489c8b3038981b927b604bdd03b208d73bd723548bc2519591338eef661c1cba8ca2f4d3160bdeaa21dfaf440a4a742aa4eabbc8cc4c6938b991c5402afc7d9fa7a56c661b63170e42177325896c3fa2fba0865cb7306faca8bdc56b00c5feade2f30ffbe08e72bc11b260fc12b1ffae87a0a2a26fad64f7316ff578dd4dbdd9a99bdc60001828366a5916418f72ecc6b7a7adc8bbb5dad9bc1035f98552d7fa657c29e612d898cf07806815dec55c781460002edb6ac3a3060b310a663dd780600b0019363510114a203ead45a945920cec24a075f1fd00b3ac985d1d841d9c327d207938fe123091a38ef60b70ef480fe2ca167b287411a0e76d48b0ba3437772ead78059dfeb697eb128eb5b317df555b08bf1f4e7be234c0b43e210642364efbde5de5bca2453f008ef089c09417cfbd02aa8e30d9d8c63eca1715380dbd7b3bbb3bd0c4bbb596b619ee7d96de338ee529f39414aa90541109c370a285e7caec9662f537a003cd79644b5f3122848fa043c2f8182d983ec81db33a698acaa2643979f3550d949981aa8e4aa9f0d54d2191da281a6d34fef21d4d0d9f4230debe252a5f649979ebd1b10a69069187f59b419f119eb211a369ba961d37bec59c3e6f44d8cdb491a8847292c5461e75ab83e1fd88681091850f1ccb072d6151191273291b0cff418a29c48ddf07396f3c84c152a227e3a12b1239ca75bf6ac670d54cea379c4434668d03c3aaa324283e63c0ae2fa4f6f5a43f58c06f99021c6982d4c68aa70226d1ca520055d5c0942cb1139a99e9dd23a24c039320290de3ed3db3793f4f61baaeaad14f7e45cb9b334b261dd90cc3ce761776b654a7aceafc8f154a566a09ea35ec29cd5aae7bcecd853e7c41aab5031da51b58aa59e73b11fec90b825557acaf992a57aea5409ecba50fbad882a4b799655bd6e2273a5dd29409d2e5d74baf817b0323dbb35f3ec1d4d7a768e7dabecb4759ab8dddd6dd9ebac683a67cfc902167013974cd66db21e97b91432d8989f2a5a0c8762a8f71c4527e54a696d1aa452ba825ae90aea9561565a5d94c995759bacc7656e858c4e590e4685d7fd487d61155f14ac94d2d5ce14b7faacb369ddb8cef3587564c6315dc63ad3bec5ea6cf302d01b52d17bf0c66eb11b1e425b4c280a9b2d86d35d1d3f75f64386556733b8299b1bb029d80b5653ad8dca583d4e65bfda99bad4a98c466d1a8fab69aea41cfa20d54fdaba546fbfbcdcd170a7d7e85568a7d2dbe974a33a4c5cba8db48e3d40255748c55775efe7ef74707439ea69f5704534eeb81cf5763af123e7e7d88371f417a5e3b1a8f7b215930e8b3be7f8a58ed73e76e4f6e97e385803f5d390b5e496e1d307d53c15b70767d8c5833d297765dd308e762e5860f154f41847bb47e572547301724f39a99e7242406c1ed4498083bad1b60f8d22f4e175b77763f51e1893a54155c641bdc372cb290b52a4a8562345a2a6d32a3aa0619406b92a0f1e3a74dcb8d1336525cf97533699aa081567055c3171673371a7f3bc94524a5f1f3c1dcb2150dd954be99c8ed374a7f7a0ae86d12bc42defd34befbcc2715cadb5729b57eee15008634f7d3a7d51ca4dc562b11578603a68377efe08fda4dc500d54f6ec27ad81d8a7f7d39cc197e1ec190dda7c7a33d1a0eab3fa6a846db4d487a90fd8623999a6a55bc09286f33527a8445dc883200d549f3a0931a8a7524fa766ed1bedf4699a2fc35a43606e0c6febe9eb83fa327aea2114ea40a8ff0805c162b1d80a54b118498bc5be4a01720f08e3a1061947f5b23dce519d7790c03e8c96ae6723921dc643f3f6d8cd6e7b8faed4bba74e2be03ce24ea76bd3813be70c774e27f706f8e472cd8633ef59f15cb341eab96603d4b373cf351b687f9f6b4cbafcf4f00260ffe6a3d21228072a5dae067af1d7327ef310caddfc039b071932fdc57141226d08078a1c71fa6ddb98cc6fdb5635635c4f17193468882bf69b874e839884fd08fdb605a13e9b47fde63f9b0b6dee9bc3b607e18728905bba7325c217019bc2e79e6f7e69500ad63bdf1cfc2cf5713bf2277aa315bba9d4dd9d015128b23f9d7b16198a5252c368947f499374a48c39c194269c44b144b653291ac45d3ced5003963449c07042b6531a85ead6a8893dfbd8cd2e7b8ffe009d5edaeae5f5d2d9cc6f54dd58dee8f9e6c69267a36ad9397a23539f52c7f778e34b1d9386520ad074ad35a0ac86b0158149d0e7ea842153df0eab1ab249519f76d94bc8e7a187e311ee1b372aead3ab1146601ae4734fb4d467e4d0ad11b9f9c6297d4fe7943a68062d168bad304403d1b82e0dc435264f75aae4a87c77b49a9231bee498943c7dc93d7d7350df5cd4971c142df5eddb6490b2ffa8cf9b87a3a54242f7c6235bc9c94a2e566e54418ec0f187fa4cff4617f599541bd3469b7a33df6ea7f8f957569911e9892f219e7ffe8d472a12f559fa722651d1207ab72ad7f58d8a527d6f543488c9ad2a86546e545c12a5959cd28c63e294b8a599449f1ad64a502d3347d33a2b94c651718de385761bd7b5a777cd6d550632618ba72643ec3978ae29f1f2e0734d890d4cb3008df1de5cb4208303196986a891a5909dee1ac2c3922e62e0e1cb17723a1494174f5b66954f174d889145e1456a0b72ce90a50c2d9e62483489424e77715902fae93cdd569fd6d9ef0d0fb188001b487a84a1bc68410b52854e062148db08986eb540a911825481abad213b2e55635441aad065e142762337028cc89710eb9e7bd688ecc6d7089d7bf63582eddc1badabd02d51a5fb5a0b722ba25aeb484388bb8d08285d3f2f15addbf1881d8fb01266645842aa40ab203b3722c1863502e804b915519d3484b8948ea1963150dcb04992be3decad372d40ee2979012e2fc3cf59d5e0d960b058be123df0a007e825d3687036b4e17c90eb07bb8e72dd360bae56e1d7adba70ae8878300e0e6ceeb6400f74906e22088e421d4fca92a2e33a630feac33910d101edc3b90b8ae79c9bc1a579d093357637f0e0217ecead180694e7b7dd06822048043a282507b444d78acc0a5961af2010da88c181017237cf5ad060f12db7a28de7884755aad0b80183575272bc47afec8d387f85c3eac6412fcc1127d912a5e4b8cbe5355c23539f198ee464ce0884fa741c8b8608a461f63aaf6cc61e0de33cb8c1bb5dfed2b03f3a6e42fabcc50a99af37cf5bacace03b0767684123e6ebeac77c29393e57487ce7abe72d5666df39b8b14a113c38026918b7f2d18fe39f07bb01a301b65a2c160dfbd9d0412f07bc216b884cdae4884ce274a115f9bbaeeb4050a881a673365fd7e44b9e82a76e8f65cad478887ae18995f2c44aa95501254203aac8c23efccc3ef685aeb360d7cd1342cff3bc957fe2e7a068d97eee81111275ee75a3732c16ab7a5d188621cb57be0a576eb72f6eb882b1fad1d330aeb2c29015b25621087a6118dad0ce300c4317cfb7fabe958fd0ca6fab706421dd9e950fabf3a08337e14a9c1f861e86ddad42d079f0b3d65af6a6e7cdf9857415b244feb08c32aba732a4ec0803c170fee877bbb259fdd05985a0f316fc6e3e9c30fcf83dcfbdfbe5e4e8f084b6894e0ea0652deefc39c5fdabc5652a05141cdf0150909d450c9a685295c4130f3e90ed6cb7d9260674f63b77f0b398a81245b6084a41728d8913c8c8231c194b55a40aac841654948e742f205568fd40d2911b01ac050c104c243bf185136648ee8202309648900ba938936861a18a6cf1670812ec2cab34faa81ca450225be4718154a1e73c02e41ebb7d2e1eefd968d84c134bafb8397bde39524b79c44beb18b264b71d066b1f1a0456f168c6aa7cde5c58ed832293dd4763e266583c9ca6cb75dc8c27b7b4944a01375a41af41e1b86e04c7db3ed4614f7fc5825bf2930740b6e755152b47e056bf41ab6f0f8ab57da6c7511606178936b0a101cf6b47c7552387466b06cecdb561ad42f0f36cc76d95b614ea3dbae9c8ddddfd82eacd4ee7a4026edc302e6f58b6762ca7334ffff6f9d35d6a387a17e75c12d473dee2409bcedbbb9bd61d2814d53a92c0fe2c4261fa8bbfe7c401e3965787875e1fb47b8e82732b9fce390f0fad4cf8bc9d73ce5d1af010cbcb9ee75827cceffba0b801ebdb59acf7074506d8f8761b361e6cda9a197b509c59fe135f1f2ce0db17b08007d98ef35fddd85f7fdb0232df8030a50ced58b8257709ba43ba4ff0ed30333f1fc8dc61bd1d041b64ff793ffff4528399037ac90a0abde49e7df210e7b6f3b27e38c228acc697b01a4fdf470f19d70173c7e6edf3631c9c83f34ea49c63631f8ee7392f19c0ad9e1bcb1fe61c9f33e728ad77ee8086f57fa31091856b4714fc39ff41803f81d7a8f9ed4739be88c20973cde745fd2730e0bf71328ee99cd80fd6d08af547e0fa22d76fceb4fe6e24c16d90200d6be187909c4ffb5dc98d42e6e880cdb91dd04193dc605e7766b63c9179def2d4e54ba12d4f4a5fc3d353efefc9d178e3aa73c3b69f15db152ed76a80e24b1d3c5a4807f5929d739045b1d95b3cab07cca7a3c8c265e75caed1355ee7780805a63d09502081fdcd8bb8efe7a8cfe74406fc369ec06bd67c1d4158823273f0f7686f8721893aaacce496d6bb851a0d49b086f10b7a0ca5944ea73da659b0f926b56d4e29a5fd85524abbc7504a698bf969a91e4a296d312d4a29ed31422e293ae7a494523a9b8ae17183f4b4030f30b32494c8b2cd6655956971a14a6a6a4a0a4dad810618269a4dc6258ba39f5e448b284b814f1891c452b5c110691da0862e59106931c1cda652940fdc4c8ae36e2c16f352b4cdcd63b19897a2cec662b1a20ecc44ea6a54783191c22f53befc4024e469a0a2cab43a3589f0d407df972960befcb83af832058c8b084ca70b1643c03e1a535143c1476b019db5952198d0d90f8d295f7ef829eaa20e43574494d6392b0c733a097c6edbf43e014f3314004bf09b79f43193d01a466b994d35f2d33ead540d33fa49e9c8d30b48559b61ea07f05c73f205a8695b70aa073da1b3b445109c39aa53af28b4d36a1b16d229bcd5eaadeb7123dd38f690fd368c568e993ff03afe172005c11e4d60eff2f38ec5b29e8d377e2211d1047e960df368423b7be753e438aef5f36dfc441338e7f7c2d0ae44b65ea7b3fc7b502245df26f07fe32b84af23110a72e0e6d337e7baaf8e26b0f3b66dee7d2347bd0ffaa7ddc4522c3d08b05f6e55fdaec63b32378cab62f9b54fb1fd134b2245cf44c26f13d8b9f2ce741eab09ecfce0bfeaf841bbbe646f6757fad0801f8d899b61c169ba74b6999a0d69c4163898d93c432d65a1b5721c0d55744add045fbdd6f905b86d63abb6d3af7d2d57b7572d4f68dfed8d3f0d136a981d75346c7ae7610ccabb228b6e16849eecc7821092932a5bdc90c64c17275e3a043f1c71e4c41549c83085ebb9e6e4889ff15c73b21405b496991b6d68ad738fdda132cf4bc76dab3c5ef661afb56edbb6b9a0769e1fe1d95fdbdedab39d1228a880dcb37d9f77b57eeec15c97b7104cc09fcbd57305812f040cf43c6742577b1582099ef3aeb9160d775615800c63d8d679c90314c29e6f3cfb0d214f7b604358a2f790f094c74b78ea3c1c78da727de0a9b7883c0da90d8b9e7246ab5963f0bc157c37edd130201e58303a485d4ccf43a29ad1828e1d4fa0194384145d8cb04296ae77e1087461c90a4659ae54f9612a298a1cc2f86c8f07401cd0f32d04fbfe6902c6e561684db27c8de75a9325af0770a7aa75435b693f51426ff4f28ecdcb5abbba18ce1cb384d6b66d61156bd1f6b45ede38e78bb6a74230acc7e7c653b75a6b756d636bab1c477d736e9bd41b5647cb2da9d54614b74b5adee9a59de36d585b1710b889d7f257cb9cdbdb47159c23ec7f7e0ab5e89c1da5455c6f95735a044f1ec39b65165525d88c77468a106c460a046d5a7776df1f6125aa1c73dc6c3a7ba9a9cc96e8104cb3234fc7568ae242764b7bf62f9ceca1d2b3a3a9aa4a83264d159aaa46a282eaf2d4250aea090a0aa928da9c497369f2192a3455542580938710d14f77a59b525a6bddb68dabdbc6715cd775d65acfebacf53ceffb3e1004c30f04c3305cad562c16cbc666c562d9d8d8dc7b6f6e6e70eecd0d0e0ece8c19335aad160d5b5aa290c856a26e11e9541d1dda5387cba9f12db21b2009fad1beb72daa0822b020f1829a30a45195355e40b1c044124a8ec8f2e7b7cd81d0a0cdb9c67e73a10662f29b7bd040347e8ba10cd3115496c898e10ba32866a0a4d1420a1830c103b9d55abf59c3fa1b625bfaf6ae05b7f46f4f8a71e070cbeb49f190f88de4a9370ff5d3ff5c2062c6f1cc38ba2b9783222edb9f5a7ccf17b0d495f22121093f44ea963ff6e7c7fed81fefebc05a6bd9d9b2540586b017ac055311db6000d93798cccc4c99a03acfd61b4b0c88de4df6b1cd4e9a827a9e75ac6ae967678c3b9da3ba609269d006a35efe5467d1753b11e69c28f4e38d1187509f20d46792de983b7df6aceaa91ff0307951a6c79b5eac9d4bea1836ac15ded695262e7cd8b526488eea770facbb256a7bdc56e914e201c4248de73dd7988011031b52ddb2d54044845cb488fe0738519860f2d3ab4844e8c17cd6760f9ecb13bd615e377a56406b6d0e1c0f7387dfb12422049be3b7e3d678ae3121d369b999138b98ef99ce8dd2cd689c4e0d6b1b64a7b74f280d54d430a50e32d23eed3b4ee01cededd3061e1201e368b71f7645732c227581d0206f9f766bef8502451e9a3ecea09ee4041a276cbaefece0fcf412e74b9fdf4f2fbf2f9d839f5e72f0a5bff760af7ed077c6d13369d6c03cb4cd56692acdd9649a4a5b969f1e3f34d0bf929b3dc7b4d166505f6e491bd226db903624a4b925fd44da6661a484ce2fa463643806a13ed33b4072992c81faccc96548263f2f796a84d0bff115e473d099e45590707c3555c3a6d44f276ed952964984d2404a34c832f592ebbacbcedc4e4d251ac4e4d28c89f63da33ed3b5cc6c285244f5848740d0b4f165e40857d5c043d4414084cb7ca58ac5be8eaf193be232df2297f95813a424482d933529a7ec35931a06a561d3a79dfd744bb35076c604d2bc762cecdcd8d33eeddbc8ed039633be79689637cf227bb70bb53b6c8e2ee5aeae5cdbb05603720ff768e7573b1bad30050b14588144062b2041e22cad71618a0b25514690ecd705c436793210fb9c45da8d94d0403cbe6a08e5b6dba5bb25f15002be690638e806fbb4d3a4f2c65ce1fb698cdb801c1ce4ecc3eee59905df6510034868587b0d9a5fc43e50b4a1f99e341f98f0d4168392f42557f970f4e53c9a48e594550a543299573bf9ee2637bea78ab41b692fe1034d4203b11f6047adc9cd79e8073f0a2ec651fd868b02427dc8b265b46938c1d4529f29be84ccea9cf812c2d5b2883e75119a742adade1310b4997af6263345a6cf9869a986b177dd78709df5babbbb44758122aacb2423c5e18a65d34d65466588191527a6341d4bd347693a095255541728a2ba9091829a4a506694096552994b7d060d159f4953d53c9e6a17289b7b736f7066f8f0de1bc6de59cffb3e100c3f300c572b16cb66c5b2b1b9f7e606e7dee0e0cc98d16ad198d1343aa76bb872dad53abdb313f68ba7f2f0d09e3caf904516cbcb3678830d4276b18a22ed29ee842d96618b3c618b1aac7ecc6fd1c6ea878db0c50d5cb4a74f6a831b461d64d774d769edd8bc2ccfe559f9f05e83296cdc0d36709f6eb9dcd2ba36e099d3e55c6ec9e5b283ee95babb4f9fb6c31bb2f768cb73ac4c8c837d4719e739997d12597b03820f68e6fb97d7fefc940c3e3fe980829f6379b9e725a4c0f8f2e7d981f46007c2fe23a485d9a5c36d671d2e7d5e116d1d38c4fa4001473c79cebb0316ee7d5ee24bec5f60af7edcf75cbc1f276ee22a0c7329141043a158bc0775203abac51f51a861b434f2ea2203461d088f3d80d019cfb52525de05757f7c78ea3a7e9ca903a1fe23c489e1218a07a81e98bef4d9c3ece7df207a05e117428254aca2e83a49419e3a8be58fd22dfd87d020f6c9791ccc1be645d5791504f4e9451a367dabe1d2757d33b8ec3b600e9743174ffd6c0f55703767be3ea9b3ff70a57fc94ee4e737e7b1a371f6c8912a424207c723a087e3915005aad24829480fdaf126726512149924f2f3f36bbb04d8a2d96ef68e9d63df2a6dd1babbbb45bbd367d91da1810d271ea797d38e938e93cba986538e938d0d9c729c6838b59c6638e138dd386de04e374ed7c9c689e5b47272d169e5143a814e9f93383a7d4e9e93751a01e0649d3a2700f438754e3d01700a0007d4298d235a37aeb3de07862b96cdbdc199d1a29153c3a5b3f3e2d1c046134fd3ab69a749a7c9d554a329a7c9c6064d394d349a5a4d339a709a6e9a36f0a69ba6db64d3c46a5a35b9d8b46a0a9bc0a6af491c9bbe26afc9368d0068b24d5d13007a9abaa69e00340580038f860bdf6c5a37aeb3de07862b96cdbdc199d1a29153c3a5b3f3b2b1c19452acce6c68c0f3dad1897dbbcb450da84c698b9a72d5c8a1d1daa2be7d8638f228cdc0b9b93623005e328eaa6936ac5508809e1d5947a30ac1ae27003ad3d977a68f53e993058003d74fbd375bf9c8796fc638da5b46dce9a5c7418defe0e832e330e20a20e7d9415858673c64c3f358cc38c255085f8cc69528503c2ff15c8b92c486f53c64e365f85ddacb9fa7f988d74bf0f97b16e2d140349cbd4703bd5c359c7d88071d04a33e3c34c3d93d1ec271e66ce586959c4fefaab35375764cb05e8bfcac5567e43167865b725490128a8839428690c0810f3490cbdb8934500d77bd5ade725ef9b0ee79cbf37cf3dbf25abe596fbef597688275eabdfac08a9b839eb76639ff045e53549fbeba4328d7759c7942461392138ca1831112621c552dcd20862cba90ed4186581a6487739dced9d220170d428360f435ee8cdce88d1b882e714269989238a3d010674b9c47a210c6d18e234e8071b4df80f9f61be6db6d44661c2c31dfbe1af3ed6114647eca6034c802ecd39c864b185cf015dfde5f6e69e4375aa69ba0e7f90f7ecf8d36b10c7feb80855b8b72f46087dda4de15d1e7c659c5ccc15275ee4c969ad78bc79708d5371fa153a822847a6f9e8233599d49beb4677710b5fa7daee910e599812aac89275442d4ae9421250164b4c0420a7a8832c60559ce30194208272c2081b951907941131e920843071734f1c41349b848a9610c2334d470c593253f342929e0881a827c8099b50ffb00c3d4307e9fd3612d7bb7e8f0c41214539cbcb085acb76148eee4a2fcf42b91d389da5eb965c8ddf3470dc83d411e039e7a79f35c3807fb50970eb763314fbd5c3dfb94089b0f9bc46d1dae2d6d6c78e8e7725001d887ba8e0e0ecef7658ea7aca7391807e592c33a39798a62cd122eb0f20027846821ca89345864d093322d2de578ae4919e3719e6b2a2863d21a0804ae37613ed36c4929e9e7943552c3a611d717a114898e93991cc0189106881590db886473b24295cc8b16b8f00559c712c492a7be4737932f6129f89269327cc952b22f996ac9971349c9c7f0e56432c241aa872b5cd4c678c9822c9d0a1596a82a5ebac8aac8526887091da65e903d216506241da7142e9aa2f030554613d963c96612b003172b9021031aa460218d8eb43439a24c0c5564b090e54c02e24c11656267b620b1429673f63389110f5b7688010cc0c0018d12520c630b1800110412515c207d3c9111ab2931431213c9cdd0454d0c35637c7042b2c64802c60c3d6ce14316d2c8698ac674250c2cbec4d041196334e9b0c5122ac0819c3390806189082b98c4408a9c3e976890d10b6366556081a489531a72d2b8305aeeb2cfce2068185e3c410f4948c46c917d21a70d65ca8c4942892921b2e002fbe93cdd4e774d25b6d35d5c74e8a891f19ef2a3a25d56ba4bff9df9e9df9906ea59c3e610b4cbceab0945aa8a31bcb0400662cc5ee028283f5489d142104014e183fe9ce4c6d4c1d1dd64349be302292f489ee0e287353b57b03c3902ca0b4730b950685bd426b5d136a88faa2a81dc536eb36f7779b9d4594c3c34f4eddb96ebdfb37d93f1d0dcc00f5496402246460e515640e54c15573ca1427202755d56c40b6aca08824a88a617a6b024f1650b244a3421d1848620d2df3ebc22db9496da37d9963421505b0269498dcb0e3203f4d0430f538c5822c6096ab8de0310024a2c0a115cc8e00b18c040891b76a0a1d6248aed06842947950331c87285942e8eb60422531fefab0e564b7d2a110e94612a2c60c7f39ade737c89103af51156744ca18a90f6d5e7de370671ead3b9a42e1d53a8b2790ad443ef3185f695afc617e79e33b98d42da57e38b1b837cfee27ce5fd122174eae1dcd13d530f5d48574b7d3a1be672d648c33ad1457d3af73a0f97ee91b1a8619d7f33b83346053801237273ee29b728bba4e3d36b140fedf829fafcf4497e6e83389f1e049c0c75b439f3346cbaced06eb0873669380d457b9a1b6d5ba241d596f72be35872bfd76b84cfa98b522b5cc883a89f4b5d7488e1cb9e1ccc7899fa92c7cf9269b4b7a5069a3e7da3d240ad43775371517ad439e7b99500939bf7e88d8a088a9c5827694526ebdc66db926b5bfae9bc7946b4c31840ee2939d98f00fa37a630a65045c8f44ea1992df8a3e346247521d03f67ffc623557814f2b9ce78a4867f9f1f713908ba1159c488e8f2f086192b62844cd4b314cfb528c048a1460a5584d4f0e929b85e22b8bc7d488d309749707c75fe12327778bc4783acb3a374d98bd0a0975337425b4e7d2ed1a01ca75ece5851c3a891220d7be2aa489d0367c82356890093473baee32f91c91dff441d9f2ba297084624e8113022c1f1881179a48a101d077dc7851cd9a1c9991548703c726487266762a48ebfc4498247335edd8e8ef7689eb9b20e76d85175453aa887368f1a88675539a3d0d9144ef6a464cb97134949d24c9a4a5d650e898b35103f29913d75ee88aa808bcd498265324d020522c3ab5bbaec83f5451807753086cb5e3295f51f5c5d45dcfa2854145edd18644727c8d1110d9fe1472dc7717047e4711df1e52ed1edd2652ec243dd53df967868c753374283a65bd94da214741e17023acf78a44ae747da41ef46219fbfc6237347045d47fc1ce444fe1c91491a22932d91c919229338af203dbdde889ee788d7698836de1259a15731024c1e1dd9f8ca8f58af20a0478049d047a064b660b286fc9c57db18e4e808c9f5a3231b3f5a39e8472c3f0abd1aa13e345cb16cee125d894cf668a19f8651dfb15f2ed732e27a460b40eea9529d29dde8e64c72be89f68a94524a23c0e451fbf41ac0f971bcf158d65a2bdd2847ab4781986bad955e4eb41c57d65a79db782c3ff07cb3c59d4e89b888d899d68edaa6d43650093275f6c99672fd3c0bde2b46d3aee321f43db86d1b277a3f450c88dc175aa933a515c817243623be7c795b9dabf75bd87515cd9755183a55ef033d6ff2101202bf073fef6b0834040c88be6279de9c348461b15644b47605ef5c3d80f3cec3550826a8e3a534c01a16c6e67ade9c2808736f3c6fce4d863037389e37270d61706678de9c348499d175cc5aad4228c17fed5a734ecf9b9386303442d68a0504c83d254ff0d9c12ff989879010fd9e524a69ad9ef7936b2dc137ceeaf8f9099d9be774ea80864d0f6e3cc13acb6d2edc1e77eb6db5eaf2b85cde575bf70b6dbf655ac7fa6018320f79c03f3888e34621ecc4b7b32512d2cee0e7852153855e744c4b90886bdb5d8526460ced092beeeab9467ba24feef75ca319f153a35981816db54a66545c9be71a4da981b8f7b94693d190baeee3d8dea92f5d31eb2aedd7441218a29c647824a9599217a054b0040625254cde0525fbe3c50d6daf0d6f12a9245349cc5c57982731d06280bab51f96c4da87ff5f406a1ffed25e297b5ddb732d0b93b5c142975cd773ed8630d3bbc9f52268977bd3031d7383932ac30d2f6071433594ea8260cc2e6e0d3253cdb5cf352850541a2eebb90665891840a135204bb3bdade79ad2125f9f6b4964f1a5cb07b09c3ffdc10e27f5e982e1cc17b5c7745607b9ea53ac953e58697757313620f310bbc838d83dcb4dcee5ba979bfa74b9eead75a4b4e956e79cb39b99997396dcd969b9dc7d3dd0596fc6d2a86706356983ea96364f7d48439dbb01618a53c3489f3bbc67777d60638b3be3994b1a2e4cb0be6147b1cf341de49add51b1580c095936d4b7b753cbbe3bf42e1bcab7779214df6527f911bae19b87d2971c55836692324566f0ddddadb4d4c47739a9e4f0136a76692f2714dfce53627c370e5fce2adb59dc10440e4da8ac210309982b3a8a2fa8b058c1821223c874abf9420caa2a29a7318220998a2bb82079411661602003b29c4c4f684e54506112660af2883440e040451926a688822c2715104d645e284181171c66a40e2ecc344171c28a305bbe167cd9fa8fe779beecf96f06fff9f7f995f29fbb9008e3bf249480ede8e043f7564461f3f47c79d06b03890fba5d1181b0ef69d8e471b18ec0d060503f514252ede303cc54fb38c1f09e7bdead88bca01c61b4c448300c2a1a696f77c4d277be79ec6b074680f11bf753197e698db8a2e3a9cf2eb7743dfd741a4e447d47ad7600419e4debc6750be68c12b29379e42f57205951f3c21251c630da610aa4265c68a2a6852886c2c511373b5078a88d40f2d479ac06ba903561841451bc28c15b8c61c31449b4600a599ab6812a0a16bfc6262b9efa8eed42e69a3a3895363d41c65377dd2bbe084363d6f0c224e5a9e7d020d50316301011234389d10d6b909072f002c30a54e4132fd4e0f0108df9a508173cf5169d92544412468ca9a10819499f7690e254a5c915332491b483a6522c167b81a494d6f094524a29a5142700fd4402b8e4f1cd4f7fe5866f26ae6031040c434c41c4ecdb7f88d0a28625cbd78a4801518361e59b40b776f1d46d5644d4b2e09661d9e2f9e6f2c633400becdbed0c06dfee0a2288efa820aef81662f62dc475999d8047734f5660c0f3eda11461701308ac5aac2c3dbdb06ab9f6a443142b202e6f8ae9ad889aa84e314fbd52efbe007185836fe75644dd45ed6e1c65c553af4c45bcc005287ce0628d1259de0428295972a44353194e644f20d4a793bce001ecb25cd24e257e7ceab4d56ab731b0e1bb0a19528213770888004fab30794ac52d43174f8d8a98a1efa9f187da0f4ccfb51f96be04b3fcf41bcfb52969405a27f44951bbb9abe7b6acd465de68d467ba9db1cf74fea4e82775bffca47e7663df803085caeca99642724b0cacf9ea2c7c3d2a5b0aa7eabe3a073bda549369a9a8eb521b8d06d920d8677a1394123f9d09a99f341ac42454d44bd919e320a27e551d34a1006349c0a4068ad2405c9bd2a4d401ca7ef67cf955814a3f4b7029244b70966449366bc25403ed09940d51503accd4d19685aa81ca0ecdac6aa0d25a792b3c14a581da49f84338f574d1f1e5542253e60c0d5f4e343f67f872a6f97bfdba57f5c5ec4ceaa777681aa8abb2b1aeaaabeaaaba2a1bb34856d65505a109a8442d1755410b15aa190900000a5315003020100e0904028138301e8a9bf914000b80963c6e469f8aa34912e3280a8220638c310a10420c41c698192a1a07002f751e92c83a4bdaeb979eedb5c708fb48d241c945e54ff49a1368c555fe9f742d849a58b1a11da42ee0ab66e675d3e580d841d6e342d7e06f0ac052d05c128f187ee149acf001e939d7c0db5a7cf9c22e8cae595a53820d13c1d4ce347149e9b9ab68093d4f168743d8dd9fcd3f15d603def3096f4ea0336509f3234030b4c376fff5f16710549d6390c509dd7e5d589399562fdcf90f9acfab43dc511132a36a6168667b17fe565a9039888236966a7969c13c8208d21f24475183ef9496a1c112fb516b2439ae8afa7f4d8a6c68ab35c4f1d9f68e29e055b6be3027017cf18a1f36228f4fa9025b71919e82ea4c029ace74046dbd4921836633a2db83685c538024216c48eaa957f6a79768d966090df8d5ba1e60d2feb63f5b6052904069108e5d6bdda57810a1fbeabd8e532805ecabd8fb32e20468845f083f7fd9ec92b0b47b64e3772b6da6767fb8fbcb18f7460bc2bf80335b1c99722a04bf300ab1086a1752dbfb8aaeac7151deec0a33f02f331ee5877d3f3e830427fd88f01b7bb779bf5b0e193d1c32d173c58dca1c034ab7742c9d2723105c3409d88606224c12878706d1abcdfafac729d918dd39568fca1c61a247170d6d7b9157749d0d53d220a7d28895304d0622ba35fe5a6fc37576dc8f9c8294a3b94e95383d7fa980c3c9161af4765dda74d17fb8d6b346f1d4c5325d7c75987b656bd57a341b36c92d9966e23a5850c3aa83e5f7a985e8aca18fefc812fe15428f93ae569933902a44ce9ed909c6263a65beee84243a7298791958088501470b7e590ade2a9f25380a6a1b4281d45620557137b181695f582d42db0ef0ceba76bd72ffcf81018a64df9b1481421d79d561b4d7f4bf79c0cf3df50261cc1d2a0bee00705382351809414e8d44bd1f2567673a601cd7b2898206b642b0e113a996fb10596ec0ce7c4f9054e8059fb4ecba2151915ddceea8df6c25923e1d518642c09aea47f4ed5ec6311cce2e94037c16ed15078025b2ca351481b1965cc6b20b79a8870d65d310c6e6721bdd780a73a5d5ae6beba6f4f8d3123a52c39139d46d9d22d2a38444c1e08c428bde7b011c37c3459afd1d6774c9ff5107200f3a55ef5b6da55020decebab204c702ab964c0c4fa674bbf278e25d65f07b9f31fe8b65546461db0469a9743352447165b2d847deecfb74926530a6e434bb3575e4daf00d7e5d886fefbc4ca678483c4565e38096abd6d565dc1af2afac32646538b06b4bfe02c0cfa8059dae901bf1952ac666a9c05428b7c550ca2ccefd484516ab69771965a1459445033b37cfe1320d49376722e6b340c3c3ed81cc4c38fdfb7da2e873a351ecc144de01703523d4cf420a0d218d4e0141b5bd2b99e1ddf4dc9b4539165fc927204b1ff38af237a8d8b7ea2d743c498fb01c2711bf3adef2c7c48d0f4a82ab86538a273828884dddb63afed1e02bf8f774a752258f99616293844d9de934009d50747990182676fa1e9d85ee4243531ca5ae6dbd5d4298372b4a7483930715f81da5491e1624fb4901098045f290e07594216bbb807cef25f26fdf1ea5e91ed32938af08222401bd3fcec42122052503a372e11e91eadb9f5502d3f50138c6000a847b02cdd358ebb6bb0c0b0f5ab736226635c25f060c8b42c5ba9492fe26b551cb677550410921a29801b482e1c21642068b6b6a76807657224b247db3edf84499ca04f4e045a70fcf22e1f38ce3f34e67776c77f969ba20ca286b59539cc61257705483f413ae9e80ac709a9ac5d844bc979900231088e68ce3f60fa90af847318b40f717c7a44f29c93697cdfc98cf96c9856def347ca83b1b2838888258004218f76066c24106358460ec284d320785ca03d5d80f05c90c38f8541d0f1121d648f27aea44210d55e7d9a8724c54d19e4c43d2edad0a4663ed076eef05ae0aae06acfa354cc8a03ce5cf74f1d8e3223b43ed05e52e1005f11231b9473f5b966747545962d2b1b10929134bb1cea8a6952236101348602d7af94512ab33db9d297e1428cd3c39b866a7d6e693e3daa068e41218cf7af0d4a09006be841e60cee060ef943e4a35a5da3b06c0bb2286a861a8649b281405dde4fed8e09521bf3dc52953ec3cee2cae148ecbcfdf6391f935af0cc53ab78f1dd50692e4dcc1edc51db10ee1364ac822a772f39a1a237625b3a1e426117274e595832351357ac507a3acb1931b6b8c7eaa4b0e427236aecbd555d548ae1168bbb7a3e28e8a6b6442096e0dd543d2a8cfcf557acdbcecf6b26816059543feeb2e6c11832cbd20a1fe77d409f681c1a9317e25a52083e52a1111887da780bdbf0279b532288c109b1e91418e4effa995e2f4414580a2627eefbb883bfb8fd6789d027f5d1d48c4e6bf46bc758e1d1c7c0082dc6ae534e1717708910ad9f7583cf0393174ff4186548955ae0590d7f7541566008a814450120ca1cc6743416b45e6fa5aba009e4babd733c3ed612293d4155d1fa41cd86bf7d4da53caa46067a259f20aaf17d60efcd1e5b246d783696f5741ba4e5c30c398bc90657abe5445e91b2ec2b540b9b557f302c1b9d6b5dd483d6d98f80b39d83346f6a73f7b764e4b555bcd7a629faea5d40e85a8a0040529842a1b578fac2fbfe9b01de7803c1e0d4eaff3c0efff295f88682655abdc0e6408ac274a68d9fede0fd447bcbc68bdb8fca78ca1ced5e4f058e0c2ed71cff05b092189a3b464830a0f30d6692cf54962cfd0cd6310fa4bd5665fb1660e906fd142f12e3282c977b37938388e17c68e7b4fe97629e9f9750efd65cdcb149308065fae520153ecc548a1e3c505134ef47252d3dceec0d890a01ef3bb0b68590c5018c8b81032ea8220d00001d2fe29ee451cd1b1548be5007f0ceb3a5b589fe850d40884d13d01631ce5cc5ef21cc80f8996c283c6d5e676ea936fc4160f42a84202dcb382be863ec1aebe9a8ce68d356f0cfd45027ccd370f4334cd07cab5e26b614109ec257748b2de36620adbc0ab40c57dc516a8532e1527a8fc63c177770b0382ba8464665b8acfcbff22874b873cd241f82fde52c9d19aada7c3b9ec488b1512efe0b572edc0b9a7dc4c692f3459fd5450b8299bcc30054d5d20557d294054121d715f8e655e09716740ec6720f7117c46b8c5e64aebfa06eab62df1a55865c20a09ffda1c23a31bc37d7dd733d1506d6da4325e3c8c2bacd6d26f33ef3604dcf47dc3d219dc305e1fcc8d447529b66cc15fd06b7e195ce69834ec9f942e6ef6fefa66ca245f8006a192d23b6483a73957b7f1427d9b7ce20c79a7dd4d90ec692999d8326ad0b7bea4061341ebacc0e3b82662dddb8077fc34d3b4f3a9d13279e064d26d5ad922b0fc22ae10ba73ba167280769722fe99ed6d6ec34536621fbbf4dd2a3c667b8311f3c66810d128206c952efab12ee5112540dcf36d932c16b8813fbf2c76deb661d26a387ca21ea251981c532ace274402b50b57d181ed1204f3aaa45f778b122081722c88d746235b4603544f0e2fd273ad38291f49e8343c37b66abf5f49ac3f649dc18a5417f7f07256729c7b8c3d1e6869c6f2aa034216c061c7a4d55fae0f93eeea96d0bb379f9855d95f082ee75b8e050b26f673612451e6781a36ee238e81c1396ef6594f18077d16fa18f77165b7a5f73b8cbb9d4f2942ac992b8afa5bcfbca9c55efb0f7e90d5a64fb6af9f1a8c10c61e3182f2c9a56dda598bc4b3961ece06a93ddfd0f54904d0dd7e2c8ff6b5fa2dc806937cda5bb413b825cadac9ae47991acead6798bd872869c98c54a96ad29d788f9c6b7a2d642911bd3ea38e44c286bcb5c40b25ce4facea0749c2b9c3a2e5bae98a20f3263731e2234e96081ff51193a8571715f5dbe3cfc5fcca0a6e35bf9af36582a4ddcdc36691f88a9272a41bb222905435be7aea2e085d9ee1a2d9d3147ac5b5419c8abcee8f760a8f1a8534fe9dd0e24479f08bbf5d270b72a5a3ea8fe6842182cdd46fe3b0100430f100deea5dcaf197923114b6b60ca47fb9b78d148d540587317ce22624dffef3d1d3185c004bab92c54cc574a7be1c3ca2d7461bc2eabc1735e0c394af641776a1d5c5900ff16aa73bc4751818bbcbae21fab19ebe20a1226278ff4abd83d98a91066d34c989177cb47ec613fa93a4ae258a08398ae32d7570b7e5b0b2684ab050f3727ee7f737661e3f96a521d7ecfabb6c639fb2ce85da3aee32136878227f140e767e71cac7e5de07bde32d03f18df727c0fcfdd655bd466c73c7a9ff7c85411885191786bdecfd10dd783af82fdf8db0a6ba5efe842b0d556509e5e90e7bb9632358f16c5924a92351d9d9011d4a7b90a10b2e3e7382f05e237c6c14f7650426b3e79a4c224f4719bcb09727c9da23076886287f93563f1bc799ff05b78c82563bea62d66f6933ec8310246c7a53d191a39a549f33c5763851dd944108616acae6c23f5f5c6132331b86d74042e06afbb49e72a0bdca601ae6a519f469a1045266c2993173c1807d638cb92b11f4fae0600e731825cec06120e0b805270a24ae970526d8d5be1da73d49bd6f68cab6ff9474ab15eb0518893624d3b04c9a4c6fb0200b24dfc1f66e7e08fa95c1967808805c51a627004d9518cd57b0888508d9058a8ae7d2e0ed93566320d26aaf5264d3ac114c2ce802c3211be2ab9f7f1b6003216854a6061987b37bab82a48b68467059de3c0e2d5c4b04dceda998193176d1c6f338e3d481fbb009b97976b50eec2b26fecd7ec6d760427fc5379c5ab14003d8152340c5caa45b92ac71d1d9ed62a24d490ed8dd4e14ddbddb450a1a0546096a1e46321bed216e7cbae70a5d2d61662c486812100909d7f8e41db1aa3684c64b7efd66f34b95fec04cc2688343721bc29963ddf260d93b06c3dc9511acbadc18c734d39b8568f2db327db2b0d04d3bf828217d156634957362b28b61754cfb2675084af8a0a7283d35724b373f378a8439d522e231d322292324def12b777f51969f1a0484ead82025b2c128fead96bac337ed7c4bfd12a2bac9b0bd99fad605977b2d8661d4a88848db87e2305a2b7a1d93c66204ce4b626256866cc938a255f109fa1dcde9a82eac1567f3ac83a1732060e8f19669c1ff1013761050ff9d49c4d4672c5aaf857680785c48a078390dafb07b0895650a0dbe0b40aa56f827861e5b2669e5db0c2158f144f383dd10a1254c3f3a70ef206dcf06f876b845c1cd6b6c3c12a12eed5a4067325a70d76ee02ec8821b76bf54a37dca496f2bc21e0117afd6cf2de6bbbe57c3f29464409c8a9fab622e0cb9bd0d287150f29e028dae40fb0ce96be529cd331d0770ffed5630042d631cd81941e02c8016bab89575032e2a81415a4e58db75b57ef52ab981c38d10412ade7687bc14720e843e462c6db92042342900290d60d2c363986ee644bab407f38b0181a9cd308d8d33476494ec8c04bc6fb38bc8ec300ec05be418eee20964cd4090cf1f406fc7cde7551168a419b97d9a7d5aef3c1297e96b3a8189141168af7a9a2ba01eb11530537f7b10b90271b95c45bfe1caecc58480ec1d20b868dba5815a992134700e0f0d1ed51ea44fc1c51b2efdab16cbae5b64be156765e033988eec5697706b8dde1e87aa7500829df9b0f264573fd8f5500d609986307329a7a8e565aafc8748f5168cb729ebd80c7ba0a12d5fcd35cda6b6d0e35bbfe7f95bc86ee8853f84a6f395562ddb5edcfe57a8438b8ca2b69fc7fc73693f2256d678cf0147f9ebc891555f6b80a86f9c2acf8cfbf586ec395e5b820952fc9404a1e12cf3e166b0160677059bb10d278926fe0fc47e4e9a15b8b3996d17c966c5a785d9a70a51a562fa611c7150ef10102099e2528d89eaeb2ca8710c92059109bc4c409e6f65301045d7d5e80faceb0432ef4d200084459b1a88d3514804aba52875db457128b23cd8716707f3247788c55e0c710e0f1142ececf59650d6418ef9610a4f0c168b4bf39b3b51dfa611bc0cf51a507eb824b0499fc36d1f856bef2372cee5866e0f6819dc776c7d49e6da2889f4e83942db9d346b687e5d0cb4aa9b113f7aac1651ad6f98ea1a6965064c36360a4fcb88a3ab6ab82d2aa0843c626dded97ae744c75f9f33113072206c3517b982a7cb6d04ea12c051fe6d2026c654a5de7f162b361f18a2816d5b61b572e232fad55943ae3909a81bac56f8f7e09ab9322e66a8c29e8340706bb6ce4c188d86a7b03c6271ddf9bb6cd912ff429ffdfd28ddd2b032bb90d3d6382bafe633126763a621384a1468b3bf90174e7e1f02c9184a96a883a0b4f8fb56722f6c743515574937bb29c146ce50ee94e704ca34f4eb411148f17d2c81ca352c6248064786bb5f420d0b1e845ce8b9102fd16fc9c7210a39a0a42dcb10557f901604c5107c75149f43fde852657ad7179646001235d79685150ec052265f14171a4612e15c24e9ef9c5d140ca414a1786cd24c2a8350e64f0565646819b5e48a84818ad425d8890c443df7e4a5afd2740679f9871ec08394509091e9cb952f5f78c16810d5c2d670f8c71539348e2abb38d3e135f22b483084d3060a84c1c4b1aa6c3c2ffed6c660060f172c3d70c76d5574614d6134a174ff450a8930c8142a1447fea7923a08b13b02045c07c30c893c0204e010f88d3b501600aa9ba554c69d8a5a215859e503220d8887000de8e6032403bc1dc55b4cb5dc34073c7a40dca4d089e2e452684278d43bc2c45149b033a6dc79a6fb2210fe68079984d85b1ce5a5eb1bbcc679ee305ce4ca60390dd3e3aa05b2cfc68ccecd9610382b43fa790216e91b6660de5ce442b4ef760831a0f95ecf403df2603de5607069dc2fa81cbaa4bd4b5096b0a83d3f16faa4e1edea5d9ae6fe468ba7755021b53ee61896c844bbb789a758ec5a07bbab24a82067c5359dd90ac2357df31dec24b11367f0c05cf76663f5137c4f279ff71d3122ef7e640f958d3575b51a843dc8650b93969d95bbe1a627a9de8ff421984588f1348a5252a1b057a5e360abeec445535fc855a381d4b84732e208f3aa3e141e20c48193f9b814fe2941d10792a2d00b2c4a539b721df1bce830d098c03301a45b76734b658d4082bd3216fe16375d459a0e7f8e9b50645833658f956513b3b5a38025e3bd84b302a4d1db916b991c075e8bf32befdbf53f34294833159f301abc58498d4ca1a20caee5872a71193c3c37878429bef1063b8772f9ea84a10558474b1cd5227d448889694dfdc19c42e0769e89551e85e0ec277ea2ad4066127a7fb68d7e467298dba828e24c0548a1e26691f8edc5b46082109dafd49c6c6ad1a18d531549eed449c8a42bc31f7df13ad91ce8ef5fe3ca821e0e744a78fc14ef9a9b9042608fa3a4b2fdc4b2bc84f0bc755841b97d1874be20965b23418ef4acf8ac60599717dd25bb1cea870f621ee8f76c701a6b876b4a02548b21e718b779a969b281846563bce0b00cc63c0960db770c50ef2fbd255877f4baa514b4876c52105d27ad42822aa972b007790f048d72b11e0d67b224aac6a14f4521777c348121ac6ba1b5de2b3319f47ddcd65e0b450d63fe79200bacaa597439943fc0f02aa15473504a5d05b537b4199d00d3bac01b58c8948dfe56a499f6c9a99f96b1b160d973d6b1b1b130ce97633539096e0be47c0c5ab2889c5dca63d3a06bb3b9a14927e730b06605a3b765f49b7ac475e35e7c2ec409e52a6b9f5183bd4a0149d05098cbd9b4843324aa0ab770c623968fbcd01e313ea74ee49f29c06db2eafd48f4b9063e3e066a98ef4c9c37e555b465571171103ea3ab8307c911dd45746fbbf805750eaa7719e84916254ddbc20ed3c2ef591dd83f02f09c547f70217211282bbfc707f140d548905c429458e3a34dfd5c24b3f3c772e0fb64221b787fa4dcc812454d263f0708f4a493edbe1ed84083ea079f56413146b0729e190774ca56e9bddba3e762253c365d2ea2055868f1a9727c6ded3d7961a149ab2b20540fa6e658dd639610f46c691593059ae4d8735ecf45ac6d062d6dcd77fe2a8dea7d1ee53f967aa1a83812a9b4707551c49111766695845f9730d5fa314db87e98101a8578716eb0039c48abbbeb3c17cb595db16c114c56decf790f09642291258aeff494015a18fbd778d902c1f088633ad060d5fb870b1610164f0287060d764f23912b9042a4a2800785b604b1c238c05c8896d05592bd005c040441750ec8e0b35bc014e965751d33de00048a6197a4eb1a51d5bc4048ebd05e362a3db4396dd74b9f1f2b68cd32c91af253babf14f8053884451fed0e548cca5bf8f5242b1966df0ade697090239f8aef8c03173c27fdac08213e6bec0b0f7e9fe02c8886cab825768ca49fe1429973e56b666c07b238e5ab43e02a02dd431ed21e0f99e2ff68b7097748c0be289ee4a95a2c878ef4897a1bf3e75065c814ce71997db8e67ce7c3830b41bace56cca1600f41a26c2b04984ba5928bd08c62e63923481246218eb22fa568319a40c28790f623488affdadbd3314a2528fcd4107b2126ac3a714b02b0bdd952a4c1173cd92f6ebe25429040fd6a297d5357d42cdc0722483e41276146fbb8a00f994fac622cec974420de3744921f261de9ff38181a75461863127a5f4e4706de1ac6abc60d119a829e354ebf9ab980aca1dd88e58851651a0155b473d0d9b071a06c20b2e3e552ded146f0834e3aa951c776331b5a9811d0e8f8e7e16d83eef0479144aba9cfa058e38c6e6babb928174eaac3e28bb8551734d9e5ab04666c160cbca1cea14f232450138e94c69c8b2937a444497d326c1f6efc7b7a168a480f481387f293cad277540825056fd874eab1859ef33e4a147d6b106b5e061fe226b4ea9f705181aaa064e1830d0917fa953f4fad25158abdc165552e852686e624fd16886520137a7dfe53518405ae60b1815df12cc59cb82d6ee8f817ba392236a68a4cd1183bc6cb89964be4738e7ba539c389f1541df907b3c42290be8a149f5e01a18ec892b2efcdab83699d9f017d2aa1f92ac10e6177daa2aadca2ee61f136a654f00be921617beb2d476bd2473d57c834dfa9088de77deb99925e378db6569ff84827ba890eadb4641a5c4265e6aad86c652a22d516c66c4ab8722a5eed5333340c63d6eeec2baa92b1b9a296ff44ef8af80b8e93250b44f923912d266981343123bd50712e611cf490a150581e0ad8e225cc6aad068f9fa81cb0ec2efc00d787ee49cbdeae0c3e34ea2a147246a293ee10399c50e8f204133b9afbce1fd9c30127535ea31e8a5f91fea91fa87152e22d60384c24d6146da870fa90938ab46c88a8e53bce5ff99b3cb166c734ab956c2b95c5a11660deda83846bac956cf0543f7c275db01ad7baf799148f95d4f525949b868a5153e360de22364067e1a19e9ea12725f54d508b652210c8ba3980999af77e08ec0262c3833474c2406e36b44b5a12c0e9d3d8d504ca294638b2e77f4cb0b860ee5b87ee5b49874d1b5223711d0634180c5fe41ae1835dbc072f600ab25e684c5b140786429770d2413f7fef12a25e97d07d451afa2412f7fccd668b0e3027765608757d079b172cf896c02f1ed646d8b7497a8d2b822c0d09a23bc1d60119c1446a1690859d931577a866d600b5e8c2714108d4f090c376ea23fb69b5d1cfde2535d6a8b4c8dad9410e29580afc256c3540fdcc94367b62c2c75a818ebe0c208ed99e07fbc246d5b819147acc6201091b1058506f5156c5b74a535f4db47484da00de45ed81422b7cb3c342934973350ebc9eee78bb1e9ed5df5f8eb20b6a3a89ca0f9b41b547eb247aab8ebc7125195ed5a2b75be65edfe0ec719d1c4f557137ae25edac6ef4d0ae37410f4bc9b9558adf7035696751229be5965bd57837d726eb2ccab20f6e71551779db72f2f60d74a35f958893ad6eade4174a659b4c2a545e8a20cd0acd67a174580f72a1a3d22d9bbcf556511d99832060afbba9eead92a94b8d9240723c0523a1aaeae932a97e15116e2d4aee5635e2adabd9f31b0ebd5924cfaf52f4d6b5c95dab47b898ea25ea7d29496f55a3375d27e15009c9b66fe65631f6d6d5a41d3582eca3db4e55636fbb4ebddf00dc7155157f32d64fabbaa5b26c10328148d5af07d5426e541f3b5e84ab196417be436a8a1b179a0662a0bfb3b1b6ada509169aba380d4684be8fc94ee551d459c5e591fdfc010d9bb93bdc3a719d7a90d39832d75d00c2ad8e8eb5cdc488bdaeeba4cea06e6cebdaac1fcb746293e562e0bdba0cf0a629ed89b96eeafaeae6a46ec3ea2b9e5e69ef0645bf8eb94b91e4262ab2d81ffde5896f9dafee60d441a3d70ffaa5ecb417b5e7bea2142d1b74679a335de2e63a49d6a973879edfc8bf267b4c6f85a5d1b37fe411283ae1e61739824d1d3cdbcacfb0ea4c7da98e28195568c2fd8e552cfd75275f5cd5814527db4dd38976384b122bbce9975b9ee96d2b0b1af49e1ca4e1e58a9f912b0ec6e4779c4a11ed7711e441a5e23b6c9b732e0b3efab219d2c33765caa7a4e734b05059ca36a4a2f9904e5c77bf8204aa22f75e014810d109a515694bb07115c0a53c78ba0d459ccf359d0a46a7f5082133ca13bc41f9191094af5553378b05a55d2519d02cf41398171b501ee21466028b5243e7c5f103f0406eae48bab233ccdbcbca5d8d345612091b0243d962f6995fe6e389dd45cdca3984b45baed03ca8f7aefeb51c7ab553847d6cb14189f012a7eade9375b3c4743f9d67b89f764409f1b58140b36aec80dd3255fad705170766c42344d311aae17da3dd23601700990a04af0cbd394f5a440ddd5712bd2914d26797f38b5d5d498d8beb64f92d2d5aa00b01463611fde39a013b27d8e72b84631031c01f9390693cab5145895c6081072929e321243d9bd668f99c1fdc23ee294c3b03b13f548b9b56853f8bce5eee2927febe0d7690e93d348a2f0fb1fc107437fe45dcc23c82969487783c914427deb5275d3043b54ffac0572babe7c26c154fb1f2c9157af25db93df941f977e88ebeb5f69468df9a9680138d3f3bea0b931b9a709226247060e1137b19cccab433027c6b0a4b06b78934c7b5da1dbac9216a4392fe6e88a16ecd2adb4bf85ba553a52efdddf7d17f25c5e39a20ccf5e15b41d205da9952c4c069f85f4f235d2675ad5b20439b29e23aaaa93f60f2f2474581f6c712da4f5951e7aa25721ab6022074ad302a2e2569166684ea2812c9e035d14ccdd48be527daffa34d6b8c655f6ce2aea03882bc5868a9101b43aee9d2ff3516d0dcb5e8da246eb4bf83667f167bc505e105ff3da91e2666c434f661d97542a37506b3bd78eb3d0eb99e5aca0de2a1f7515616329d3f0b5aafb2f235ceca88eb8c69605de24279d814d0e1bf1f65631181d99b5115894d5574d6e1aba71aedb1ac42f63e9573dbcaca48f3f6958166c4b84bce5e0de99e6079f1e6444dc8aa0f60ebe8cd4432009aac9a548f16c86647605e8a584e41ed8cf5f035779b9b2e7d418260672a9931ea8c8680d99c51bad6f53c86eebf204b8692c5b7c2b703a5d690baa41b247f76a25e4f3dbea1cf1c39758def4cb43a3bdccfd9271b2f7202381ca7744328ab80ad34abe03aabaeffb2ded3646402130a9eb564cc60d28084b779ae10173c8d4432714828846c121e1571bb92a372209294804213db70f4517f06838a869c9cf424c410aadbada5fd46c880340ed3e260486540d5522b26ab5e590958ee47c9c748c949c173a917adfa75d6c45291175c08d711d270498bdbe334877a96e61fe18d5519e5ecad4cfe28dc7719c46c14fd44ed9621aefa5b880e4dc3da8c2c824f6e826ff6443c4730e270d86133067d70499e7e0ddb56b01a40a8b8125b023046c22402db527c007abc9f99b49e28b3f8cd530676bb7cd6dd2e8f96a4670ad7f6ace161cb161663a29e94f747fa1f75b3c9c3618c5dffdc8a56d6382c21493708e4e5707efbc37a3bd468353b051af851f45f26aff16368abb5ae0c5227e1657e8ba18df8a04bd7cc84863d58191f307ae39e74d59e36db04e6d70b6481fc1c98fdcc25a32aa9fb698246a4b1db809b2016b0c37c29674fdce70ac241d8127d9917ea45ddebc5b46d1a1f1414ff46151b4a29e2fe7ffa2c96c36f9c4419372353b71b2e85bbfd9bc26e2aaa72b7c255d10d261f3046c78dc0c0a85a8048b36adeb874edd864cd843b92bd6dd0d9a808f785da9c174693843dc435d0041b693f7ccc9aacf24b6f88d6d20cc997647fefb305dfd8acbccf86f89df9b2879b4c0c2aec8bae18ecfb516443d01ccc1e8e5137464b2c7c4d0511afb580d8ce026dd0fb479a372cfb5042a21e29193c30afe4767c37b7c587fb382c3f318fff581067c17f7dd75528541fc3b4241b6eb380c90cc1c605eaeea3bfc2ab4e316a9e14b6ba07b9ee997decea12dbb80f4dfb1b1736b003c568c068801fe48b51476af0d1982e3d4b5f2c7fbcafa79774096104acb0e36ea2e4a530f8bddad31547a40780e30183ecf57bcf7a7e5e43a35423298061d328a585a94dd0d93b320a9e3443a99aed49aa660447d5ec5c849e82e113e3c5db1569216008594285e21e55c9dbf9fb6f5c8c879768806deb1744b7788a96ba1d1f4862845b9c2f75356f2897585784b87074c0dc95714e96383a5095c69be0f641f3026d7a2ffc82fd5c9f847b020e3b6708c1fe643cc9949a46a0b258370d481a0febd691de253e90e42171cae5a29f0e0748ca96fd4c8e1bb3035f2974664305ceb87053aef186d48a6fb0719b5afd5565dd44d58b1a6aac03831e375845e612f2435ac773f5591631036c47af440e8e4c65cfdcdfe338a140fc03173ce2f46e25e8cd8cec1ee071cac9d40ca99593306d2938b970c0e08af05b3f85e76c85c240723162bbad7de80640ce17257409164d9346cd9ac2e470136b1a0af6b7bea4e36d11409635e95f6409d9a45715933139d9f6a886c1f3eebfd91406f47bcfbf96845eb5307c441bda3965ef88ad94ac9c526934ffd8dcdfe56cc2603ee8dbd6b5f8c989bbf62ba8e6c2af3d5986624d2e1ca1fe3f048a38b7704c26d9780edabae20e649d2f90169eb04b68539c318e86eb9e0621c5e279f99f412151edbdb564eccf1b1c22af8d30d80d6d447c0238cc404c23c624d112da46acdc8b8216caec5a4f501d92c61f542e8ad291fe9a01f960aad20c91b263051407ca607c4e244dd40645e459a709169e0d97fbb29ace1dabe5901f5a5c777728014f44372b0dc7992ce3bcfc597555e9d71eeeb5880b207390b00a4b93d6af5e55edc905d8b4ef730b53153de33d4390a60d019065d5db4cd0356b47997a60b4d01b955ce82c84e5b86d45672a131819711b94085c50b1c58fd2c84fc5cc0f350e674a7a8d18f126f874f84036631c6906a4ded7d5ef24fb8336b5725c9bfd68557048539b5b2c40bd79344a97af82184d9705006d96c7e98d1b0314e5eac9db783c96c9d700f5bcf09f29ff2601cf84720271398f49005c8d3436098dec9c0e28ab2a9b5f85dc97c3b540339bcb502b833203114241ca65201845859f943870314c5dba169c148edda79b2e8f84d461083ef63a28ca04af78db9903da2c25b69449ec7d1e9eaa687714380f498cc4077fc4b6987bfd19248b8df16ee9004013f3a49c5d8caf426cea5c8ef7849040bf4cc856d04d38b99766a3182dc05fa90b4eecb24378ce6bad33e6426a403ec34d99b1b2461e11c10bd8b49413d9a309ff756d6ac44a53ec2fa2821305e2f056c0875d6e7bae95018e438e95182a46352724316499a661cf96cf67398934013e5c61af89eb3ffd1837ce8ed594ddf12e4e99c100339aa676f76e18280b79361d3bf062741d2542cb08b563e2ae70d9ab61c66604717f23b8c8fa47b9845d77980e5dcb30d34626f8ef0055f1a24e4e6415c75e5e90b4b80be492df81ece3c385865537e9afa33c2ff7103af3e87471b18272b438c2f3bbebdcb91170ea572993ed1a3c2caa65a2a07c4a9b8dfa37e79a347e08940d3d7b1f253cb6ab7b398ecb4647025617861ac53c05f20ab553ffd9a66229114aa584a78a94ff3bdc73559ea4f02d0ad2584f5d60798c9e3bfff1009492c3824ca4b20ef753cd9e787cba9c087059508428fba152ea813a92c5fe2c7901f011ce7139349c97c88866fb4b2246c6a0ad9208811b5f38a35959264204096357487485df481d7a91d9e3e889260bed7a241a1e2a2c08df44177e7edf67aeff1627afb95dab6f5740012d3584ec7a8f5ec6090920bdb06b6acd1cf82cc577892ce845f3488a2791bb7233510ac681d13a277beef896d966282f82d8a40d3988186390405ad2c55568bf36db9eb5458537d3f4705ff7f7ddc2f24770c50e29e760fd2ecb9ff474e1a056185db0737411cf1230d7e2deab4cd5ad826b38e76039a750e1061d4f00cc281405786556a73903025315c2071a73403592e2475d3461710c9ff725c7e4b206f5a3ceb329bacf4f198432737c223e298d41e96b46ba54cee99398eab4d400e31c783728400111f0e97c24994aea0fbaa147bf443052e0974a804f18f78e00e7c8343fc05cd2751878f4b8e54f40f9115008763d389cc46fada37425f39324e6224edf161218d514eacf14d3824ec0d684eb21c1ac1c0560b1e422b9e7cb90beadfdfd43bcf2cc77f3c9461395301df9a0007d569878711c6f1de0f1766b7bc12e7a44c5d5bb1b3069ec64ed3154253ad3de9be288d6e130ae13bfe62c8346098e7f90ae053dc0d5cc08a2edfe61855b82e32368f740d92cba04762f9be936f0e2469463534cb9ada90d45472bc30985473d385ac8930918b4884c6e48acf01b41d0d2754047bbee9664dbc07a18498616a0c22796ef62a8b4351a2eab7b5b3ef9fdce028e7d1db5a1b085cc76d5a6a43729a8ccf2d6d901b085f6b41a2874225b19f535fb45a41b96dd72e31d916af339a611995c4e0bfaa9015c7ae0ea8498a1e4437d7937c1d61b498cf598217a72ed0bc5698149f5e0c5a53526bdd8332901decf8f038536729bb16c895f8bac87a58b732e059027f3320a200f23330ff1d56d7031dce7851e98e345b99afd043394c59ea18a03868ccb45f25cf0e9eca4d14000a7c58ea9c3ac5eb84b1963a22dbe916a3f13b1b3fc48123db596746395528f1ac9a6dbb82293c42d4b56be57edefb95652501efdb725c3ec0549eba3483f5a28828f634c91e8cd56b6d506569de1c7d0f47ca8b5135656717a73d621299c993fdedc60f02a15d68a2a8aa6583c3946a54e55a9852328078f357d43d0cfe1ec9009b959b6604f4cf8303cec9e423e45e21c820ffa65eb0fa0f7b576cb77be938f52a16e3cb63993affe71123bd0ad5621d28265fb02898bdc5d61b975ba9cb64d5df2c9dd1198f3a25efd706359f265b7d209a6830366225eb3e347821eb26a608750023d756bb47752eb855d89c25e85b21a425f5fea14957d773d0c4a274bdf9efc20d6285d2699bd9cadac758ebe8bc4564717db22ddf7343b31eefeb57843ae8f624313de8ffcb76db365aa63035aa0ad341d340376cc6c5df3c7f14c36eba91728d5b45e84bf618bcb2cf9cf2b29bd995c96b270e2afb2df524f2feec6a4854b1cd6f9b929f5ce54e6ab4676fa598285001d5d34054fc511eb5c28dc69fa660682d5b5d6416d2f4b92c2d77772939a9bad90044a26fe1d765c03f2149a1869ba5260072ec13617172a170d9175dc6aed5d71929639540ec3024571fa5ed4d1a348b1b571e58594e5f4dba8ba4610abe164578fc120c98225b80be736dc57f0ae2e893db3a0bfdb111a4c03f141e2930cf6f5ce8840be17bdd45e0224c0b5c7af8dcdfefa5db1cad12748a26c1fe6c69fe6989a3af82681a0de31305b02990b01bdaabce676f21ac8630c11fb466a47802a28156cd3d38b3e490e5a6c20be86387afc0b961b0d117828788482c47e971260ef60dea09d35300a0336295767b8d8619d48908f20a73532497d13297c1bf0b9ab6950868428a9aa9e5a40cc672f384302777e3f226bae0b39be0db81ff4c3b99b63bebd4b990db853210301a83ed956827b9b5db3a9986dc79fec3860585b619b7f2107d713ca82aa1dd071a0f08e805c05168ee75c88646219d0c75a541bf0cfd4808e2c7d7c62b50bbd2ee6e171e53402c464badd70041a7640caed574b86dbb0e752306cb07fb9ef55b473fa72b903469194cd46047888009533282e8afc7a36ff48247139a9639f8269670b96ef758cab3410b8f2dd423e2bd91f28d47a80bac66d9c571e9be5b0d27519d080dfcb690eae41fde17a48a94cb121aff95b79343a7000a460b9bc0ad96a5c9380a5b6e8daa181c77a92328820611ec0937124c550b6aed9d558d9148f497abc5633c4529e1352809d2ac9510a680eb8da8d07c4c11b654aa25569b51dd0e2366eeb9009e92d20bd8e66be8d702c1e075c46f4935727e32be955bfb7f0482fb33ff083ea99e026ea6611945fb55ffb9ffa18c3cfc6290bc13524109957048c2d0d0219ef748fb1417b6a6cb0afda3f3834745a53ca2946e11664db36b3ac381197fcac4b6494300e4887d065de3ae4e5e3f0ab272f30e98abf854141ebed4d4048919364cac315c284e45053e1434f82b5617ff3f5cc445a20539c02b8cbb2d06126b1ab75e071d41f03430578305aa54261704915dce14a30bcffa4eb5a717ae478cb005e452d5045b0e968e5eaddb56ef77f01e61a00527ed70165cfa04e8eff042127de058b54559d3a0502d178ea17eadfe157df8f0994a052314f9eb991196137ef0a2a91821688887ece2201bc1a0cb7c94d097123781f3d7a5bd175dbeffee2f4daa4b0e62f44145cfe9d9fe64f4b540f1fa19713404dfdcd266f1490fcdf0327e64736365930144751737801edf11ccb476c6241345e3e1f160e7beb6aadd19874e99c72a71fd3b4f1aa3125096d3c85c8d9860a2e944602c0582c3475b505cd63d2083c80c5c4b41254b165fc02195e858191c0055a60eb34cf04e95e7ee70a255c82e28093d6b3aaf92d4b62f597c0087da485b1d873b77abab3b2d55d056da414145e57706bacf72cbc465079ae9f3fa05a508eaac305f49fcbe0fb594cb2fd9b275a3c472d5e98d520a45e68ff160689116ffd4ac4a99953b94fdf1c15bc550afa7ae1783d8c4de6b6af2fda85ad4b9c0fa8ee4f313256eee5e9003a803b2787176b6fa6d155cec3ede2dbaa9f13023035b6dbcf46393a8ff5e7536ed7714d10f7a52596eaabe9445aec12e051be07237a937ea9360cd8b1340162f863b52665b29ddc17853f8ad3a8dd6f7083a4f4621abbcb1a04d8fd7e1d8e11f07c8ef09362ab45463a80d379c12f240f8d9acfef06e9fe9fa32239b215a58e9606fe496e26a086407fc8494e57099a4b582872f17b57e42574f1959b3e4e48a785f7063313f4e2d43e27081f0205ccce3a56194305c2cfa5a4184b5c96ffcfb886166ee92e25fd169d1f057066bde1649080eb8d0f86bd911a9781f1d6c51a83e217d5dd1bba566185001d6d8073277f2865f7f6b49ea76ab3b65ea887ea476843fdcf95e4722f1da4d25283a039c5ff475ab88b6b44d3836dd3c6d27af160cc12b2971950ab404d1d285edde6f749ecb3408ec12cc1aa5db89d9f20fff98e33572b73c29c435216271195ba075f64dfda888bac5593e9b51430e9ee1ce84c2c4c412d87fbecbb7c0939c9bdd330a4650e2b8adc5391d5a77a6aaba4f7753ebfa282b05c37001c38c33a1432a15ea9739421f7aa39636aba9d5301fce1b8d3fd290ccb829b504b6fc8bcda48f999a56a82a1fa064c7c24e2cfc2fc820aa5e14bf45ae46d267eb6a92c734ae126245cdd7f96a4509c9261a535a35c3038757f0ad10554d20c82bad27f432602035df6e4772709b0f1d0234998ebd6d3021e10ff9e6fba0ca2b852f5d7700393e2710c2854dc482e54c44b627cbd0f83c83253d29c01d768413c8978bbc049dd802946869ad3773d5801729f869f2eeefb551eebee26c6ff31b65473e4a21c6e26a7873711d3170e86f287f92b38d90158d11b0f7461af7f0567ea28ec87bb093eca40b8e26118762f0039e01ee36fe26e147f6376bd7add132e6a694145c2750457b774257a79955dfb00ca672141f140ed9a1ef0e9ee48a74a219d5b1f3c3b46c67287326789ba0f533c22670958f03572da89d39ca0dc44d9096953486421c82edcf3cf365972bc966ef40cf1cfaedea94855803fd4682b4d6c758aaa039240ab39e49cec4a9fd5ff4b64fd0d94f0b38a8f53abac9d6f54e444f33dd191e88a63a01fc1c4e316a8816f81fa94e6751e4dd106c4bb6f72129b9f775fa38a744a7cc61311c2ba59ffba8897c22e1c838d0300c0df80310268883ea6c36e875191507afe71cc14f7d9c1a4d1244e3f0176b3d4af2b6d3c4e1b7332f95615bc7dce29b7aee5acf9fffcf1fa0d364ed5c8401963cc7e2c3d03f8d533e82527cc7dd0c5f92cd46cd9c2dc514010e0129c1be5b8a608800ef8ecd31237292b86179bf6f7931fb21a5a55ad8fef039651dfe21d7e780d363bfcbcb9a365f9d375ffd577713d4380281a7caf1b8939006cda5fa607324bc08f567a199396bf427ca366cae4a281e4dba79b5974a47b9a7122c1484a15a521ac4895bef246f3bf6abe6ba471614780d9071a7886c5396c77b4227510c9e99c68a2a08748e38151990c3c2ccfe7fbf0f3da6777d5aa87aff0446693ce68235f7978cdf9fed07080d894ccf0c968d92f481cb6740e4ae8bb2613aa2d00454f26f5624c11637c6b216e31c54da173135abfc05deae09a4e4732025b687343e289096879f88a030aecba882788c551d25514ea3c1cad46472b9df5faec8560ccf90cd2c7401bcef5756e46ea1fa231b2854f1dccb5aaac8ee027b4640ec10b53f3599494b2a9eb95cc483e0267d59bb5faa5202cf78ff9e3876a3b3d42b78ca0ed7a4c4aecfc0fe6451db9a193bd7be6fa209b0fa27e7afd5497e639c8780bbaa55a4598846602280e4bde6c351dd3871babedc17438d0e8589b204b24987c8bbb3c83c35a09fb9919d45cf0d513c8316677294ccc47f076d0f29186f6527c527e70b249548a563592f2d650a10c06fe78b51637fb2bf549cf486acee4f3b7ed745024c50fd5e6cfa9b5199862d24443daa09b752690cbbd1a1c38d72e48e953c771d3617af0b8066e60ef40961b84af3498cd8f5065b7b2d3bf1f3c5de849f3f56d10266dba8fe34c8f811b3fcd71e51eb5b2e0e9ec376e4c6b7a3995233cfda67f072d69fbf46a24628f6c340950d2a60cb53e3d89665223c9f40cc525ac00f58472a5672f4132ea91445650cb197b4691e5d02c67e5c06c491fcbba4fbdaa2c811052236f57a71d0d4aead06107a382e63d14236345ec4c2b344a9603a6a53c645657d300d14a491ae18ac62cc94a3a30883b621f002b76f4160f894f03882c27f7ea86ef52093d755925a1bb1930e2a44197b527019379c263523a01565f42b38e11445ac9841e9c0b953486dcf0cc89ce023e318db9c6f72cb9cf8034d1cb2590e58a6343a44e1911dd4f190596c581972d31c09abafc46b52e3400593c5cfffe14dc3f3915aeab75ea9ba8a31efb80566cc0af274845aec5d493f17ddd7abeb887aa0b0f8743bdb1c4202ed2ae23e72291cc379e1c4374c17cdb87f2c39360be04a1ce7ee08948207fecde49c076fcf37751489d883666dc604e3e3f772250c13fb4ed2467dd7ff2d949be49869488bf9d36489b726c609b72e0a9dc3dd03a9191dac27a539db0f3edadfa14489a97fae2907482febe6f302490b36c7a1c21de1b974c2b164e40718e4eec9a8de34cc905bd8ab5c3848c2b27b5ac4c4e56ac1eaea47261312a2dcb0a8830ed12c3a5a181a9a97c2016bb4e7a93d5d726071b4b8754a9b980109396d54d44d856c9e9dad2c194541e1c455d77afc9ea34938185a1c2ead44c58885d375193a07e4736d278cbc25c9e911cf38cadfb337da83993e11fbfe49b4de2d7bc9726866cb2aa6f88c226d8109b84f34cd2db5585be26e60b0d89aa64693790185554c811743eebd384edca2cc726a182dd2e2236c626fea0f63fb9eb2a2e6a9a33554f0df26a78191db3461d100f02aaa172e4b8eef358aca13314ff69d5d0c7d3f565d4b3e5c5fef2a0bc5100aa904e7e61097419b1213f197a7f2912b3b5a5d2701df26b49b99aa1a654e4b0982712b9ce7c0c3f3d43dbee407a68fe65b87af9edee5006447415386a2278f15aa16abd3a437ee700e62845b661b0bd4d669233e85a8239b001bbed9a161d687da141b6528757d6fe38ed038b1433358ef82fe13d8e8bb2b22ad4b37cce09f243e9efeda6f0eb0bf3e53ba057bc5c5ea65f8002b0c94a14018f9837cadfa98ea10df2a75609d8a022cad00ae335cfa055421a1119ca5a445abd29ada1dd433a42b941ce6246ef0fb5839961082307048034c6100849cb109bf1fa4e13ef7703a019d2ac8cd66a7f81e9709e9b95140aedbba9528564a6ba52ac7934b242e532f9f5a9a673695ed4e85f3863cb8767173622a7ca03874fc65372ece96212420292a6be4361c753976381078273d75dc41224f380525592820c4a932ba4659fe19d9ea3ebe186d473c33f802f31dbdda35d2f0d22fc2bfa1990cbe97d07a5453b20a55bc0f90e04324645201d574aaf71973fc363c89a4a38b7004bc2faf117d2a24069aa90c9b1dc1b3111a9262abe89c25bfba7adbd161493aa3fb6263c6586b2280bd2816fe4b84e30f8f58e8f784b3f6ab59b36b6df135b12e98be2e030bd160080446231d4c38bba6ed09dc2c0f6e9a4d9c798bccae1498df59689455a64c66d073e6c43f2be80042977c5a2e4395600a5e82fc1047a8da7e7a9bf7c89882ef6e7db70ecf7d56eb2f0020ecbbfab1ed5e217adfdb3847099436b14fb791f07de3b2d1b31984e874d65c2145d1dd842ebf53d1d4bb73af49bafdb32262ecea02bd4424d6453c34a86dbf2fc4a703ea2cd454aa699940857be2f7d14953f3b936982cea5d22073a27a82df7df2c92ef057bd1d5eb0c07f889f5032b49f013ad1da171a83be0539423298b3717cbb8eafd659f842ce94c44efd73f0ecd8718dd4c6388af099891cbe70953949862a81b539c9525699f5f386824e701b920888b651d59031b1c60a13c7bcdd2cc09eb7d304e665474079e48d35b7d9ff9d407de85ed5568624d6bbbd1389358125968606fd235111bd78a87a988bb6e5a6089402ade5e9f1cfc580e021397d981cb0f011e7462c317e222a6fb144f4fe2f5139ee67a64a57de9c2651991c91a98c5e1395fefbe946a367cfcf894a3cd10e106a95bfeca26a0c73772393d501b0e80d6cb0ac9f79f3b4254ccd2543ccf4958fb6554d23c529abe29a6a13ec37085dcf477df231f405a57c591b7e36fdb3274b87e364cefec3cc6e99225777ae302ec994085cb9efd778b3efc692a812c46521dabce3617198e5a5f8476d1672839e5fac8fbc22794956f9d3807d884fcff9980b71a708ace3d61cfaea8e85e17a81291026caad070bad2eec2f589542ed2b4cf9a373dafe6037bccffc1c7c85bcd762c45a5983e079c0825fdeadb541163d28d58f2a81284dfe9471cfefb93cca978b95351c2f072ebece9ff337d4bb9aff2313e39595fa900fdc947540bfcb43746a0978ad23f3c0efc2f49ae6a81c847ed5f089d26421814c84d2b21b03b8fe8014fecc0f624fd51f525b75cb465ef93694a037bc06765882824656adfa58a8e0b836a0ffad35fe65115c6196c894cb8e38f0201ebd82bdb8192064225ed3432d5c4263faeccd68374322f6e525b39672e6174556f6a0a952bf6030594282bbcd09dfc1b45cf8c8f9c1883a43cc88c048ebeaf46b737833605ab5137c3682696705207e04eb0fa77c2f52e87463733ad6c33dc319ad6f69eddc91b09e0d040d2db39ade823a29d18e460d4851307faa27e6c03ab74dcbb317686bb65397902684d347919760c8cc1a982652e6bcd92b373d03dfe06a78f9e55ffd14f20fa52994b09b13a6f62bc872e064d4626f848b69f9a9e8952a9f26efaa0a4a6edad762e4bcbdc6a28b608d70e05b16864e00030b4538cab9632d1fd0b4f44257728c330ad8e84c43eca4f6d4371add9a13ab1fb5589594077b13460a920e2e493d15952b78bca96f66daf9d53dd0681aacd428d51377a25937eef17e7b8d2406ce88c77ef082d1227e1166f2a43c1cec040a2c79bc69649feb390e155cc7e3b16cd9d54d8a635eed63ab4b5b47d1424afa6708faa9bc40cd12558840f12613226cb3438c76162b44557051360a51cfbd01d4f5cf76a79f2d1107d05ada6f57cd11a4a04632870435abffa5664b2fc96667038db777e2e3ed15b4c684d3f662541ad776b92b13f021b287703f0f0ca6729239089723a88b19052771df657db5b5dd44f3931fe0010efb3877526380db9a606ecc8df19f7c224123b81dbc0a2d9cc068198dea4c500e4aad40732edc50c3f338a3fe183d1516081ad0f8894868edd8ea79861cbc1c96501a82926a891227e36a7a54fdd4318337345c7275552f9735a8e22732e2ec8abd688306e6c167060c1bc99d59238a4a57fa459040c37cea4a425705611e58ca246ee1b4124f2f40f240f4bea216708278c5f92bf1e57d9e9b49b01806ca8ce3e59891fea0225e77fb7d349eadab53cedc3be5a9ae6223a28309d76e1d27fce5743b679f8a00ab62d2c4dfb91a8bd184f2756900adfdb2afda195582c2772f32368ffeab16945c3076465900ec82ba7d9e294e1d55fd5ea0abf5175b478ab2b981138d12565f911435807ed223bb35c27d24a8b07e5ffafd92a69c5e8c10083f2d5cd91faf37f48d244cf3b2845136547ae3665bb8ecd3dfb21b033645d4b08395c4fe35be20772ca6d2fb671cdd3cb2b9c7bcb59e825dec2b604b13520fb0c27be4b687de4428e3620672aa27ad96737777d58df347ab90a80207c656a9b139dbfe1d5b7d887b40739baa07fde4a79b0faac11f43ca6331d10295cd135198c13e0f321d7711ad8cc314e5df2a3ad71380974ba298a19eee4bba32705d4bb7b6497615279beb73118458b47f8377332adbd6dc56bef9d6641a19df4aa5126a303d1b5762552e9628978b9be7e0850f688cfe5bcb56e32dd7ff8cb31537f78c4312c5bccbc2964495d8ffc680ca4ffcf4449f0232b26833a2fcfcbff1bd812e67eec3a6af41e9ae928ee7631b35f7a3dabb89826ffab88dd8266c589b96e90aab73340a1ef8cde0ed91687e89ed94a09410c270e37844580d4cb422665e484bae907c9b597082de1da808f61097f9fd6a4d0a8fc1a7fde3cf888098913175d709ac8e747fcb6e70dc1ac1424fcb0b1699a5ac019b017e8a3a8f04407d47b395a1bc9786a50ea4381b7f12261996f61071d59c09557b3405fb0812008db6b99c67fa0167702aa350d2d01335b1c39bd875f1f3595d41fd5a30368730cb035c71ffeff893602ae54d6e1265c7a259bfaf617480d216caf7555ae89a26121da226473d862b44853d0cbdcee12d788679145aa1484889e747fe5d38f6f3af2d0836605389be3d7df41667937cca1e09a720a66459a8b93de29c93971e428741c3cedccf774978a4389eecbac7931eb9ab488899c049f83370a8190569da2405af27b55f83549ec04290d639d224e4a0832d9d393435480b0a05ff27f009ff30c26c27169c5c757edfca4f0cf31dfdc11cd1b853464aac4a0c3cb308a9f33d8369989b2f432b273346840e70ed2181dc219b3ddf6dfd2fc9847b5894809f30497c06ac8caf998d35118ac74b00337a96e8ba0255e2e7e870eeceaf8b5fbf1134bbd85dbaf7d7444b47645747ea2f2323da34bf56263be0fb81b903c6fae48b571ca1084213c57a604040c906d844def77dcc6d283027796a5167f29134ca74dc21eb2dcca56aadf308a6d4b05c955845d8cf2daa92cec1b666ec4ecb1a474fc9a6568c8d43429a7cda22aac68fbe7c64a38784cd2aa88527a5ff209e4e0d445edbfca51cc8901821447150469218e1f7c3f066ed5528909985d5da49cc4bcac4d737f24b328226929bd1f9ebec52142633628b110862c750b639f70cb90167b54614abe0e03585b1b802820793de4d6593083ea57793790bbbd87987223261a73c251e7cc66b25bc9326aa2a1b0bf4fa3e81f567c2dc4a0c6731b4828ae5b5371f9c51310cde20065842b01d2aa577ab775644220796e52436c31bca17b7058c0b4751261adadf7a42705edd731f2607db23e131a48eed93f0d651ea7bc376d61b71b4625d79584e8fdb5af2d43b91ab4871b75df55105d24ef16744bfd78247a81a5890668bb4a8ebed0cde3599489f6944dde0885b521a93669f4deb13a62b0a0720d45cb3b0b96f077383752ce464f8f56a38e0ca25c5029f09d3bdb6bd06d18c6c1b6e88a7e8b843ae794da458fd086e8ab02db6a99427cab0580b6640ee87a2fff6ace44cc59f5d4a9712daf327409f6fff82d13b35b71d917a150edfe60d9b2b3a4270644f6e012394a90454182d92e4a4d7d1a6fa7e8b1da39494cca7970270770d95033c51df4fa87a3bcc96ef0dd5067f447a0a5a74b4c98a90fd8a4b87543509926d4ab704ff87ca8571971b1d7735c0e8c0cbda208f7189a14900a8cd66da1a2326da24218154de3343c874fb9e540c8cf576775966d573c3603e94570c74bb6f9979e855448cfa3eed96ff280cc8af22d8d27590374176cd1a07c234addaa3814913bea05530fe35f733fdfec3760e3c6f965dbe71d7baaa0b018ed5b5b73b91afdba15f7561c40fd5519eddc13559ed0d0dbef35921f68dac1d2bfc5a2e1ac718435b8d9e759980f40552b1a97462b1b2862fc972bded94cf679da3aad7c133142f310c5b69b3a04c0e18851300c181adb3b7de384aa59aa8383a92d51b469117162c85e0ac9b5aaacf248e4c3446378813e34a829cc49923e5f51a60d8df94af45a7f2cc140d8d00a00bf80d89adc9ba5436328df758ed945a73796245def250c38a87575a5402efcf28050b35e23c5752af2965c5095fc38ec074e8962983337cb630e6891307355c5ad6c88d54127dc135748d6f239c085844753da76f0c040f9bdad62a75dbd4c7776d139775b8550518af8a4d8f2b55872f9e66ba16aeebdcdeac9c448805420118d279fdbfe21878f691d561059b210b8158ac02caed2ef55083a5769ea59a24f15bf19bf7aad14403a082eedb7dd07a411401946315d991978547a313e28d177e5a9b5647e6bc1624c35e7bcb71109e8d7921e4ed576881194605581d0296733143d4207ad709a54ee1b0f3cd15830cbe16896998e7da003a87067b7dc6a4e65d270943fcff3487ce5a5d0b126056538a209e42c80645053a185e288e65b07188b364e7434856211268d1a0e9312f7b9030347283b338cc8ae01249e96ff9afcc6386042bcbda30b60cbb2f33ce4624706b88cf4e5fcdbc2f37d3f18834d8a378066382293003239fa7b8d8ed23c5152c3896df1ded8d047e5db851691430ac586ced7dd0f8f537a2f73338b95d6c8ab52be762a12b6e4c43c1908fc110c4ec3092b33edc0b92f343838f5ed63dd16e61e499c42640cf5c64c2699602f5d6871deff01126da61475fc71fdd89eb71fa486db0d25cd043f410edca3a5cd3a7902846c74b12b2c73158408bc411a739a54fa227fb74007e850006e7028a7971c8e27612dfac00b500ca9650475a2f9386953059b33989bf74945875e5a38ecc43daf59e2285b6bdf606b271cd55a6e436078f8c58b8d563b4e82e14d962bc5f3a795cfa169bf84c11f6b189608f360da988cde942d0cb21ed81b82b25d851a8a9620aa62bd74907656263e1293df62d3fef0c85f4c567f4ee5e41f7b00eb567c63886b796259a7ddc820a9bd1a122c41d28295fea3d018705d662f3d036493dc27e19fa04fdf09594ed0bb85b4b997bbf9440e80f8ccbec901722d46603b5f3082841247656cbc0f67e3d519988e60169ed7a9b4d88709ac18803cc706b2327c64f357aac45d01ee0a725a5bbb4e9c30837694dfa4d2dcac6a29afa78377152f5f567178a7ed7b91b1a4779f669d724ffbed9529b26d46be4bc65ff9ff1dccc838bf35cd9c93bddfb49e822e0146804e68a3f5ba8d47cccae6173e95434a331438374be27cac932f3178b31e063c5c91ee7b87b6cb2bac7ac15a172d510ca7ca87cb18fc51793297c3144908b39ce2c8a26eae0bcd3372fcfac88093eadbb26568bee2d36e46a806d52642c626ba458e6907744464756756a656b445d9f4cf9baa6b2ea3b4de677bab2e923a07990958dbe04ee121d0d737449a02386fa8c84bbd490cf55b4715a8446d71b6daf7e88cb7c84755b18e95bef012eb5f6626885805211ebb585ebca63e91a067889138c8e1eea2a80175db5a05e41558270a4acbf8a68f41f90f4ee9b28c4b96fea3451ee3fc699caf9c7fc10401d879ecf0f030ab522c290a4cf34ac4e82106bc6c566e24266584dae50ee613bd65002bf4e8c79163365d534acda828ad64b6a7d8139072b73b7ab71e04f51ec136d128dd0c75f0917f822fcb64249085f2febca9fde8fdd3d3afd3a222f63d97335153807a84d73851f1c598a48968124228a0851a8b24c869565e0622b69d0ab5664e1d2f09f7353cea017729441435a0de93f3c6d07c1763e84a630f4aa1283cd4771fd384485be1a9abd09431088db9dca183800aced7cdde946dd20650f55623cc3d34f3802ffee8711b9039083c768c3ac7715641727953c7be0fa98e91d9a12705a8eca0aceef250c3a8545b32a1b9a731bc8ec7fcba9dea06d922b2d46461be4f4a66d4c8ecfb6378d86977ef298819c35ff8fa6eb602105597444b00a4a492f4a22a669a67f02ccac07b2cd990a9352f0bb15905faef12d541f990c1ea5853da7e4bb223aca5221748093f9d7fe5ae82618a30232c4342e72fda1a93969ebd10836d40990368f08599ab592e3a1db793ae7119ce178785c19413c1a9bc2ac2d1e64d0d70f2fcef39e7cea00d9c2c440ff0051d5aae1617ceac0838f6ea4ba4c31d00e00798752b31b21dca04c5aba0c6e9a3b2e35a0c9bc47616370fb8c81075898282419f40a241a51a8a00d63b4d513af9d9b80d9b4e0e8f9c9ce26d9aed061b6f5e717b8fc0a250c536c1f099bf558d6ae5d30672d2d5180207131987e0d10e8cf48600fd43d3a5cf4afd1eba399feab2d734e3e8a655347996b593424bbfff162d5e9783fc7528f21d591b1c3027b7b40fbc2c804455f4414041ec922b1bd7832726f17021ca6e6655d02fc6202a67533c20f218f20ab061237f20664f58bc3f385dfda2266a013890a96d029e46503c75ba56f662debb4429d89e1e78feee4c3ac31de50003f2d64e72850eeb91b7a61346efc057140392aefb61f82fbd5bcab1a2526195dfdfe95a345d24fa9624093bd72faa58b9c908de8efec9599d53e890d0b95bb3c48961b817a0c51af0589b743166af5737c564cb1b7af15ea2bcc61cb6107c55e82e89035e9d07bf01445014d91921ff8d1c182a5ff0c0461390c854c700ee9d5f8e9a748cf0ef62acd82fd498d91517c8e6047c89774e9f98736272098cf02fb2d9f41e3f153299cb0d7ffe0ed69c5a2bb019a4f514baf947f5597fbaf82c02f0e7aade262fa0f5fff2eeac980b99f712e2175e3bec1e4fc85ff42d9831d9a13db4dbea55c65d2e15ad7a0bd88cc21f9b81594e3c9551ca6e29d22c01cf02152d100f927373f155150d036ceb0fce1609a49fe56eef4cc0f66987a466dc095fea4ac6a6f8af0646a1b25dfa778b7410a9c6eb6d2306470a1dfd0febd68b79611441729e7b72719df4754a587bb032396536c3d69bdb2684ef4c034bb0c8006c503c9c6f990311dbeabd7d87daf46ad9739778d2ba3ab812fc15da691d0b4f58f22f42d604edf3ce14c8590c491bdbcbc534075649253355b480e9432040712d42372d9a257c265770e624877d945b053829f295ac4c13901bca69ca23eda86cf0056e44e6c8fa96631a9f08f830467ba423125e5008651a9327a1524cf6f8b318d37ab81c21a7ee13b2c0ece2a8ac2f05511e83031ec144090f1aa18373c69054421fa01d96105bc2024997057219190104edbf8ef507cb3b2edcc8bf1475e7996b51e6ff42258c4ceccacf0ec945549e8cca67e775c02512c16a729e75808e2bea04336be7e92ba9c6139028b10dddb1887934b64d2585f365781059e90c0c0a42096c3864b682806a00b2ffad3615321fa2d92807578cc1ab28df3de370c62864f5de4406469fbea1685a37acd4d47571d29c6be2494a6ec30c6deeb64abb41d3ee2b3eb87548bd04f98fbe2d7ee11afb920a8caf4c86b64b449e329eff137a4f162116cc31cb0f588f80b9997871533ce20e7352a27c5dc00ea045bddd84ad50e7b963a4897d06622da5db95013c5107c9711504fa873bf2cb9746a3289c172f91b91f31b22450f5ac33441f0f65e062c03842504ea7a53b25920d22a4e3ae5603b0cc54767e4188fe85fb84761d92f5a38f09be35170aff3a8a0e731c7ab202fcca6a272fce944872a5d2110164607f6a691c3411fa2504557d1ee35a7ff7a892a8d15ec11fdcdd65a4208219becbde5de3b860a210bff0a930564bb489cee851c25ce7c61c6143a9a20e44b3a912d2aa9cc578f532c6990c38d47d0eaa1cc42f3505691e9250ef2543ae90116a101e30c656c0522992d0ac6d92a3ab2519d3e031267e6f41b88424a107399cf7b0f6319f5a537185e7bd3c3684676fc70bb3f3d8c6768473decb6bf3c8c3ee09e3a2a41779887118d09c8578c11903833202d326d54602c65d51416bd0a9441a517080003de7268b37c6f2f14e1409473a6941285b410c4a51051480ba629d9f110f7271968479db6f022c43d55447798d3cba028a48512f46419d0c61af91830c69a3e3dcca7c59a7eeabbb1a6fff265b1a68ffa6cace99fbe1a6bfaf8a3b1a66ffa3ad674dffbba2fd66459d217b214924432089feebdf7de4c798032a84a20f924666e6470f2dde00af1a68b740a3a774ba0ee01f76344d2ef66b9ba76cb6f66d9b2654bfbeeeeeeeeee6e0be6d0ee8efd8ad49312818b3e381984a594d8a249602d27814d5949772b23f5f252e2c4a27a148544284cff8dde63bfbe249f927e9540199b2dda20d7030e65908fc469225f3df248a82471be595ec9386ba03402c197e40976f54b9a4e754a297c750218dad78f40e284325b134a39036550aca1bf67e079d3e596637f82abec3d307a24f3267095f5c024d1e9aa6ed10fed988e8f4126433b988232e8d16502794c45ba456fca1e614319581989195f10c98af563b2288bbecba104426fc1114820b4532581b95c41162410fa9b28440a2920be64112e1914ba6450e89241a14b06852e1914ba4aa00cea167d97043ebd04a29750623f356671d14b2259450649214bb4cd6d0e13a5aa24302ac7abb0c4d9f27caaa84ee99e65599665dbbb7a72634e3a5fde331fef4d0ef3c0a19a655f98cad9b52f4c65ad7b9438dc439cbbece1cd1d186adc17259010e7ec977efbeab5d40130cdd2cb1ee3ad41b3357f6b507705fefce759f3785e35eb95cb830973c0b45919015798b79738a94c4fbb50394dcfe99b9f18ebe62e15ebf44a0f989e0216bc992c0984430944be7e3aa048ce64d1ae61aa2b72ce9928a448aca1a7d7e2eb84d8a2bfb929a18456ebfb6666c209cbc09c6ed17ad40181ab18758bc9a2dfeed822d3d78e0a7e8e77584172e30e2ea03cc901468fa45f3d40fc2029094b100eec7003469f435fc40e818b3ee4ab3b5a32c2f3fd2b84e955b40aa52cd7f562cc40483d309d9bfab9ba951dd7db0a9ea7ce47d810f8636564aec09aac2cdf40cadd319f2b8443699457db7b602889d3e5bed442e2745b6448d3668bfd89422214b92fbbf40be6bd813a3ea7a7de037b79f8c37c49baf529f9509f44fa5e3ed555dd0bf2bc3cbb41174231588449a16e0d798fb0a1d509deb57b46b4156495c9caf6092e92381d1812f1f223b28d25a888c1785c00632f7f015d18e2fef2bea442e270de397065a43b770e5c817b17b307aebaf7e42b244e95699381ab13b49edc302559aa9038dc2df7ad4002e9dbafcbbe28814820f22f5f6a26401ef5c40a5ef76577056f05fb80ecb6e35051606994b4051d47624c9b0692405ecc32d62f2a81ba2c518e21d4f0451d4ec8f185180168638839b0c8010e5d50c10551aa43fb56f431d77316f46ebf5095adb596ebee792dc8ec8123e8b20557e87e99dc77e7beeedab7e26eef3d5981c4c9c0d509ef3beec81a187a5f0952a09dfb4ae09ddbe2042414d1b4735f9198bd18cbbebd53dfa90579472e79d93eecb86c1f6e5ab60f5588b12634657beff1e5af1790ce16f7fee438d335fbee331d7ff2c964f5af6d097485047ac951c60e9858628a1a54a2161e4c5182063e315822d667b202895302c36dfbcbfb49fa757a9f3b7d2fdf492275abffd20343fda65bfda1d3e9a8fee9053c3d64bd3cc2500f5fbe081be298dc0cdc59fb0a56022df41db15b1c772e86572776eb26880d0c4204cece6580036dd6c92d26ab8bd87b20ca0a9647a0349aacdeeac0e176eed94369c47df7768804122f440289bf200324900c20d191b92787f8d503b38f9546c19748928b34924716a93461c8e0708c91011c8e99c3f115b33841858aa22c8660a5c9ab668e263a9811450e8e2802161f63fc8df1b101b4d67a31d56e0a3a51c896e9eb0f124ab38ad4bd0746b773dfd9ce3119b2e00eb7b2da996ce0cadef7c0aefdbe5dfb9064f40b1cb3f69d7b0d643264cfa47f77ee99cc5f7055c19b1c4a294da2d3ca5093b2bd3ba52d059d2884cbf6f5270ac129ddbe2279954bbfb8f78f98ee813c26d379b67aebc7ea967d89ead4c72c73295ba150faf69d12b8ca3670451f613b1cb8cac055f69db97d45c11b584eb76c12fb14ec75ec918028f44bc740bf04ae320c64a51fe15e027986764adfce633af723dbbd7b204fa9942d21a25dc7fb4ac7642a954a2ffd6a9ffd2dbd7b5cd59b3a90c7bb29ab5b26cb22b1ef6cc9c674ba65b9204d963d52129dda162efae0b40f75cef4ed3ba66fe04a7e7b0f6ca7f4fef69d9667e2bd2fc19dd97dbe07263d222d1891b9be0211b61d45446a9f85d94d56bbe2b60ca6693bf3a76b3f72ba86cf93fae947f05ffe02f268c7a8afc2380df52544ee75505fe9a4525f1dd1ae3d435d9a9d8e6f53a9bffc887614c883baf6179067680775ed3c2f4f3dc22c3ea15eb454ea8b302dc2b08ddc77fadcb7cb9fc9a25ce7954c974ea4cf64352c6adfe96be05715bcb09cb97d114629fd0dfde95934ebcfaea7ce0b44bd92ada497f4b348dafee6f4280ac1c97dfbd8968ba50f71fe02cf2e70ccf50480a16adc99781cb8d39d097e07aeee4d8fb0ed937986cc75b77d336b5f96bde373d4fb4e770f6432b49d097df70ddcf18e41267def3933bbb9563195ecbd6f75cfea965d5d50e7ddb202562a9365373bf07c58a9e8f8d0b7907d7886bccdf043a7cd9dc166b77427cb3ecb370c5cafd8186a9665b7fb6a965dcbc0150afdfb23f31ac8a3f10cf1dc37f7ed3ab85bf54e56fdd5f191e1be2f83f6954e832bfa7e0f0c5c51905270476392cd9f8cceb0d2e9fbfece7df78ef6093299d79e3357d9fd56d9ef6dc59fcefd62d71ae9948a94698c2f9f18e41db021fb2617e6f9fcdcc0bc37b9b0a9824d166c4aeed1a7c90d2ce63f17867a5819f07361310fbd05fcb06031437e6e60310f5fe8a7927904894049999c6651a4280427b71166654a737a57bf4aa7cf699d7ef1387d927ecd9cde64fa301ea96e7f4c1675b1268bda3a6cad32fd799a2fc2faa56f7a9e374f633ace993212cf2dd347afae6ab567d1bb8614a1dfa1d5caa36913d62ad5680b4acf4d29a5acd2afd46cd1d3148d39e0295bb1865e7a1f01648b7e7e3e4d0e8b12016129479980f000999e0359b1869ebb61496da19e034d88f9ca4b50a77bbe043da82f46c7c76775d37d781c9732bdb7a998c01d73d491c41c05aeea518f302432de513fe21d553a0fea289067be04ebbe08bba9bf745f7d29d577facc07d31df59c79573a9e573a8e30fc45d8ea8b301e5f84cd68a7c3dcc727759feede7d5eee837ae993f956f6f81116f3adec4dbf2bfbd2e7ea16bdf7a1e83bfa972f762bf5c56ec17cb15ba7aff34e474ac7204ff7d2eb77c4bbe9f643c1bb09e419dae95e3a8fe9de95e7ba6fb5a4850a4458f72544ea75ba8cd52dfa1a077d167dbc97aa95c9a22b7b17ccfb5616d461750bd67d11d609e17aa55661d52a15e9d473e74b0db4bf3dc21a5ca1909dfb11fb0de401798676b6dbf370cf6e4466f90a44d895738b4c2f270a52e79e933a3efdfbad562870d77e64fb0579ee8776eeb7f368308e3efb62dfa8edc4e1cee475c933c51466b95af5db9d7073a8d386d2a44dc7d8194c7736a6528e075415638cdddd7d43e96d1e44e263476d2ece9412480eadb5d264ba22121ff1a53694ca95b54229a52c9a158f19954c0c0c4de9e60917cad9b4da4e7e25642eebe4079379e45476658eebe487332a735e27bf9bb952273f9a39130ba9159540e27b64123277c2d854f23018dc8b8ca83971a69c12c8a9312d35a554956976bb5b39196b68d36ddbb44ee3eeedba9f042b99674b5dc599534e09a49b52109b32dcb44ff3a56321fc0196ef7704b76d05fab190ca8fcddd20a65e50276c2a795d6aa649eaa5793aeb2cd74bda3c9dfb54372c392abf138db1e6c65ea9e3b0e92b799f8cad3aedc6759e473579754a38c004ac8e76b7adb260939a953031e1cc166605ed6e9cf650d35621b871c668a7c8aa302be4bebc54eb3aef84d9ddcde33aea751f5742ae1438e5fad508a4662a909155272acb53e2c808a43ed6179b4ff81481bc8820f7252763ebabf3942b0972d7c8aa5576ed4b4b87806be5424ac9715ce7791d57a55e9b2b29b8cb755ec944c25327e17b216104cf084f4f900671b946e8e119e1e909801e349e111654ea2300011040288192e4481f86086000178ab3544368563dbc13f00058de0f166bc56a754849646b761e90f3f30712145670213424d6cc5f21648c478b005d9004923ac85792961b87032c7df0f07e0214a00b5abde4782e874dd34e5e14e04707d4adf94a850492507ed44820f294407187f84ad22d1ff2b4828b05c4d61c22444e2c1a92032ccfd5f098f18cf0f4344096e11995678404270cc4d54d89231f2f4a02998f1e9e02cb1054c978464870c2ae12ba3b6d64a5e2d2584296c5c078465850e90128cd19428580eb904abd40401843860c19121b105ff7f38249a217396211a5d82ed0044a62031c24251363278f33e46c5a53f645cabe12274649e73d65ca846a1b6eedda4f89fd680442eda3cd7405f9a3b22b3d6e8f25efeb382ab1e76ddb26a4fb91535187d2eb6cdb86ca42faa92d66995b9077c89e4ce33b8cd21e37146cf8d4c5e4f8d2ed64d7757793afdbad79ee9186fb3629a5bcf714ec08c43e469bed0a2a654251596ee9c27cb139f68d4a706aba90e51be7a87d314eac04c7749772319d29654265e612985e35737a699fd7e99706ba666b8600d81eaef27ce1ee74714ccc7430a917542a9b236ca1522251f1c39d425a9882c0208588700e2512b20a298fb284a3053ed062471969f460062cdec1c30f8248da82049014b0105f91445956e10150d64ea78d4744cbccc8d9ad4724dbb2fd81850a1918654a1330451ab97bce2b91b226ad1d715c2f8a6c14230833945025e4f938451509b4d1035212192f10238e184517330061e50926a61411840821e01eb047a4e518798641642215f1502a438a2ce8bd31e8c8f4d48b2892e04ea0c40e36d0e24a1a314c8185ae4c291165a091c3113588f1821391de5319641162898c070f320a27609ed73832a07023f7c8f4b70920caa8830654243147cc1272bcb1830e2f6cb1630ed1060d57460145115d670137d64822085464107a01169b10a28635ac3c81811b38c0e69435d0b0b50179665123cfd97509d041080829705851441111962e414fdce0081d888019499d6ec94f2192ecb072609c658a3ca7c032852529d32ff2b4a1541691691d9926894258a058606ac2ed249628f2a45dca08da4d333bdf19a5b4b36fc26277dfc07d4a6da535247d84241c76f5315261c4946cbf0853757b54145147b6df8451b08b35dd069e5de7199145e81fae3efab0b94ed59f647a4ef66b95d365500a34120808262bc90b701891327d920ec4d7e917882dfaf89910735b20be5c366164aad3af075c1ce4304691a3ec529b10852c892414894296d8ab4e884296641f04a29025f7a1ca4814b244034004a290259b0f0944214bb8004c200a59d209800251c812ef31d68403c8f4f40f884296943e42ac7921d6d0c74fc2106be8ed27638835f4d9277d3e2943aca1d73ed924d6d06f9f9ca1a3a1864c2f65904102352863b1861e049345534832599c0db82f3f04ac37c95cbf9511b9826ab228ca4a926913ba6e6e268bd21718ba26ebc6956990c9a2234814143894b124d3759344feb0543f240e8d2a0aa9a79f89af6b52154d16fd8906999e334df115a37cc1044aa06ec53e2593954402a1a61c91b8d909c195728cf6466cf5a7c4a98f9cc48143e2c4316dba78e70ee50950ac7982c3f8048712883b02cf679741fde2c0153d2773882f99145bcdbdf40e1c0a89e03830c6812e98cec3bd3b93a11d7c0cc6ee1e901cc20d427c491f4984dc2f7dde77b5609b031ea22f112056f2c0ee1c141c4a9fec2e08c5a30c24717490384f244e05fb2b23326b79843ac2a1f40192484c2ad24a16ed0cdc9dbbf4913f5188f6bea421da482e930584c8923edc3763dd6a19c764b50442b489b12fe788ac7e5f5026c59aee82b084024aa0c99acf40259305ba208174652281f45b281285c82e27c4979c23b6fa37372594d06a7ddfcce44099d4ad06ea4b21202853a8d04f0a0149209dd49f3f495deeecbca059a6c7b3452fbb5f69723fc6546ef9e514992c0ac310380c92a5e020d3868411563c66327da8424caca15721c69a5006658525afbc72260b29962b06dc0f5926fcb8912041e2b73222b303e62b46b6e847c8f42c7a170dd21d15b808983359946b8241ec43e2a42410da31b6e82da8d32dba051b65338091a6d9e03ccc01f29c947382bf3aa1d20a8394aeda4e41bfea91f44baef32a20fc5391729555d67bd319b83fdff5575afb22305f1156a3add656a36ec34786a25ce64fbf528fb56bc6662cb4b79fa57fb5888eb624d591347da4119738baccd8fcc97d3b6997a4389228a5540a8181638679eaa12b26c5e83572d3f9d3e36bfac8f593b1768263d2c83b71a96f5561be550557f5d935eea43a7eb9ea3933fb76609e7afd508079eaf508063894b19fd5230cd68f31e7f123325f813caacb9c07c833b4a3bacc795e1ef308cbeacb638e44751926431ab8937aea4c5e0e73096585fb7ad5875fbea1fa9576d57b602b0d5cb2f37215c824820fb1ccc30fdd47580c4c55653ef9d3ad7e89f5eb05bc571a751224313caf6384c6dcd6de2f54a13e54217ba8c2bdf65085eda10adc4315ba872a780f55305d85d2b9090373020a0c4fa92fc2a6cdc4f918fc4a9eb67159756140be5e4029740a86574921f97a01859acb6409f54b6e315b7d29b4a54a9f8b0257d0ae61b74c16e70487d66ec97d9bd4cf6278a2b470d107b7ca7d50e2f8a895473d1759b701cbcfeeeefe0d9149d554269b3e8d70e9dbfc0dcb1a65df3e16067d7e6e60a537b9301f9f2687716fe28275f76e02e99b5c58e9f3bd7d3f620d6263cfc8810c365ed1074b84d1860c72d0011c4930af0212b63d67e24f96be568c09084bc8f3dd176309e12b4f048409c8f3dcd661c1312c8d1061f5472381cc2af33777da2ca85a5f8a362388465bb45919b1ef3788d3af9ff11561159525d03503f7c39b70fa3f269836ab13ec5d9365634f9fc2b459dd2e27052c4c9b98e56317084aec27fb5638856b5647108db2edc9aa2e0af6f9646cb2a805b241f45688de02d15b28f4f66014627500407cd91d628b5ea57a79c9e133976f812490ae8a058afddc20a892c0524e1fb44f7f8993cad307f8c99a4960190ae0ed8ebbe66fe6cf9a70d4a06e4d2ecbdf695b90393bb941c312f4d4a09eb64f3b5375b89fd55a5fc12a44bf3a65b2e64c8e950dae9f4a02d3c74b1c2ff77db088a8c0212956dd26385541d54b1e4134ca3573f55bddfe4dff2c1f3f3a0ba27aa55f8bfa95a85621aa449528f75d3767193541cfc9223d6bac0a0cf2358298c4a19916e9168d6de810db504223a74424246c6c63c6e29f6f286351e28429b04b3fc856b6e4c34806fd7d21c75789635fc8992cfa0a466ec6623f40b11f1a62b1582c1693b108e81409114b1037bc807332259b60e6186bb8185fb6ce39e7b475c2a45e26aa4f139b66c9ebb8b969f366b81b0cbb158dc0692bed9b9e73ce97ad73ce396dbdaaee9e73fe180355e5d7986613d4c659be2757abd3adf9396790b96212afc054d05701fd89c662fdb98aa00e0563de8957600db2ba156132272bfe32176b49ba307be6bc42d0b4484d5e0af617e36bb55d29adad6989c1616a5a6270a031b3f722c5d8936f2c99b0b66d1a195946eb037ac2a627da75d36e66378d8cda37b31db5e184d2341f2f507d147da9d4b696a524e0192141aebf5f8871dba048636c78f94b4adaceee750fa82006ad97a230b4376dd3c8688eda904aa562686fdaa691c1d9d0b18dda10d3c57844ea63a80cedba6937b39b4646ed9bd98dda2063a45fbfc5105d2ac6a59766b68a416f66ab1320b6e8de79f5d28cdecc562757d2a657ca1b5bc6a8934b2fcd6c1583decc5627b66697da9a8951adad99137a6d7572e9a599ad62d09bd9ea044609a4d9d4f66be37e9570c9298c8167775f6edbc6654131243a3762b95662cb89f8dabe813226a5c8b108c48a745de7554dd3823ccfa3444230e4691fadb55aa954327550ecc6e58a75c923625fba78ea04992f191434244b58801ca1e4ac1571641409e43e06e59fca6641518282e6b51b83228fa4510c4a3d3ee7bad94b06758d38322681ccdbada98d81e5b35c99ac298b741081ccdf692485f27422d6cc5b19abb9416925d6ccc7dc804f37d334b9a5d80803c7df1cb16871a29b8b66c0f6a87ddd6c6da0cfd0fd7c130cdbdedb9b6098f66d035773dbbabbbb5b9b4912633addadf54bab7e2abdaf44c6b836b54ca7babba3544c8b74ab29c77dde4f5ff719eddbe9dbaefae4767c6dfb12fbd5d4e6f7b9c0eed75a8be8a46082eeeeee5a6b77777737fd6aceadaf75779da08ef69877641ec3bd7c72a63eee30a7c77cf8f593f9e59330fbc9ace3d12fe654f45ebe38bbbbbbbbbbbb63769ab0d02b380b3bb23c9d4066cc70cdd082d3b2c466c618593bba148b192d38007de5b0cb0bc8f5d70869079b03a46872fbf811c466480e87e43b06516e329ae426838cdc6550c9dd65782106d2c477087c053f8a7cb92dc2c82253faeb031bac327d06461837b432bdbd5fc829c07041a6148c2c99be7a44283e03a7e6ab63b1580c4a97a2f1f14336c9619021996689346ec9d4cb618ce3470e631d3df29450224105a55f647afa8b5567b9c8c8f437394574ba0042a64792c4874c9f829221327d0ab8a041cc0ba29f2dd32662e9028d202fac10929269c4725447a611cb1751647aea11a140f001c65bbc20cf4f0ac71af2e2dbe11b5f118605d083376e07649af33dcf43f3ae34abd32a593d3acf8ef0a292c1a899982ccb4c9b29dbaa649ba9cb36799cbc771f01638cefe1ee237c12f6f2f1accee3a88f676867751ee7a1b969a5fd48dff423b37add766e665956b3da73bb8f7927dbb60aeaf8cc5cf595e948e6799cebbe55fd08ef81791f7ef71dfceef51dc864a8823ba58f003259bdf41eef81715ce5b86d05b4a58a0b7218440a20949002081f29b9ad569936518a1feab06282cc758502c63d402634e0aabe37706705ae50e89bbe33cfe3f33c5efae9d475198fcc9eba9770878239e12a3ea577afd854f2e29d2f5162f7b1890be6fd07c3ba298f02559620113e4d2e4cbe0986f5c3ceb1458ffbe5f3e382f59b6098774a4fe7dbf3c0952655498ad458e99426a97a743c609e3a5dc5d01745b9a6fa5d936ae6a5f79879f7953ccffb8c577a0f6d2667caef73819e0108901565597d119d0ca460025a84b4465297ac76394a8a23e9a85f41821c05390a12647baca12bed0424108845518b4c2f8bc41a5ab4dd8416eaa5b764a5813a33251d1f9f98afee03731ef58b58435fbfcae5ab6078f5a86ac9f482f528d65c1466ae3a92d269aed3e3a58f67befbccc733f3d24d8f8f46f5c56e0d0183cc7c43da4b1f4fdfbb59f5d577545f814c86ee99f455bfe08ecc79804ce6659e3355e7f1c994beeede77c09df9ae33a7fa22cc743acc87fff2714f7da8cb7ca5c792cc5977999cfa224cc7c747869e580c751f9fd3f17d4aefc4982caa5332e113eaa72756a45b14a91e1551fad2934d141a7dfa29658d809c524a2adff2f600d3e75823604dffeacb17e9170563e36ec5c965c290a38fd0c6f276ee6e81b5df57a47e652a55f66560dbb6e42d1e2501850f599061048b971e9188bbb55dfb88b8ddda7e7b80577147fbe5398f0557f196037778327015c155dca9d75eefcd4a3c590f2cbc793b977d31592669ab660ef3ed29f40b7f7b0afa85fa76b965db697bd94c9b8cb283f4384fe6e3b67bf4fd992fc2e861beceb2ec34ebcfc874a00ea5adfa222cc24aefe9efd0ee974026316f701599947a60dbfb63d19f332222bd9507738e76db167f2b97ecf64734ed56035791477b06ae62846db75fce16dee46ddbb6edd4d3c9b6da712a26709697d06c82cc4ba807ea64194cf7e9c88029f496e56d3b927ec57c7becb845cee7e527e9ad50c84ebf939d824c86ba3329812beeb4037760de20931870c5812b14b4d31fd14eef79e8b51fb92fbd04f2f4ef230ce69ce9a9d331ccb9ed43b21d6f3f6d476d7fd99e326db09e582cb66de7b62d6ed45ba140af1d49ffbef44558f60d5cc1c6e83cd5c0451fdcf67ead60f72c7cd38f54fa6a02578f30f95d9ca4ef53e87b9dbe47d21883ab4ee61e31d39c36127eef53e8178f5fd5afcc6fd24ccc3502cb29d2d31e3d1d1487d2ee39ae47844daf02f83bddf4d15cd6e3cb406f3a09abf378f4565d0fe9795a5ceeab4e27d6dc7337e025de4d882d7855c50447bbe91159c27dd5813a271a1d1f9f99afeea33a8fa7708fa45ff4f73a51c8cd3756ca4362cdbd8a093c733824dfd37b1c09cd4778f47448a85c48f846e8d109e1ba0658b74cd6bd5bf23d773ad1609a8f87f20c75e7a9c7f5f83bf5f81db8436f5a7d1186fa224c5b7da8cf7ca9abbe97c77ca5cb7c30e7f19d503f7d2b1dfc0a4418369dc71761599565b612eaf874d33fed9aeef1fde91e75bbe4fbd43dcc054af5c4ae067eb2eee571b9c0b29efea27aec340121c2ec8e760dc4f61a78bbd53044c0d2852eb96a9fddd99e6d9fdebbd5158830150d789e3e54b140b32fc222855d29e79cf1540a579063ac913d67151b304c8e58ae60230ddc9951c7155dd45004e50a1c58b942685ac12f3962b98287a9c5153f4d059be6a043609c86ec82f117f3079f8aba087756fa07734a4839b03da2c88a28d305389523162b7ee60dd834470381711a34098cf2820b6cb39864e03bc51c036f4e483a3047850261ae0832aa0822081818555185b5da8401e38a15188f70831b5641403d8ee05144e3ba31c061c675e30a7263c49515161b74500101c4c375b1c040838cebc615e4e667e676a92146882eaa2d4f6082380a02490667b1432a29c6759fc082c38bebc615e4268a95285960f01336b801454451ea092176381901f4f204141b700c504f00fd98848c4e4fc47043498a10dec11c35783f1c9976904443170324624065b6620e2f4e63861f6236146d51439e1264c50c425469a205322052220f6124444326e2c787a0241c6ec85292b0ec000627b988203a7e5837ae1ce44628d6ad78d9b8e107a26e451e20c18051003168142f72e89a30601414b307b8bbc1bc037743730011a5e30022072d8080a1b3604f08f3094cdf6825b005422369692fbe00ca42065cab9048f866996f60ee0654075823923e20623a81b31cb1fc10b4056b3962f9e10530605828b85066c9c9adf391ea702a1aad4ac87107a81b24731e2e5d53274f9dfc5499436d91b954bca2820969e69125c8424a7e314be6646260522f2fa853662b6d1eb81871624f2981c47331d2b6944a39e79c734e2dbdcad3049a4ff39833dd2a2a5363c750982c75fb86d2eeeebeb9420331d7478f03979c80412226ef26225dc8424a226165b66acd553a1ce0572ece80c10537c061c3131dc0ea0f1974308428551081c50fb05aeb3ba4ee9e2c23d7d77bdd1781c9aa47c951eb7592283141ce000bf6294c1beb651988b7c919d9aedd848d03c3ec28d0a696d3f53132e0bbea8f69336d3290fa6005b9a9a679b281bbf1b5de7befbd5bbd96afab09517e25baa1b9cfd201acc317ddfbab81a1eade6d0c9cbd7e03433c421038bcaf0f2bd10d2d68826e21b93f26f20c2c433f3017e40d4c4441033644cf55a27e7ddd27ea578455b992bb978861cc008a2662d0431c4be4a08223d24032a2062e50eca05588ebcbc3dc81b3e774f68577857ef42c1816c936bcc9597c16f6e4ac43989b5c2bf863b2581306576293a026504e5819c99952601122160310175760b608aca45f3a462bf457d40e814309c5d5adfebd5d0d4645596691014a1619805cfd52a924a7244949bf22eceae0d8ea639584416cf5a514a1044c4289354b40e9cb1f2529f8775fc0e1cd4d8425e2e7300bc6d9a24b8850504699acbe09314b28ab2ba04869149ee6fb78aa7d751ff3f6f88ee67abbf2316d3cd7b459758f995b71ae7ef9f02107963ea664a12284850cae98c28b9ea12c340c495143d2141b4a1f0b8b22dd77bb283705763071a58932ac48a30b0df040063ae812a50b243736ed4a992f927d3f7c18e1234608226e50c41a5ec4ae88678cf1d386166bfc6c7106966508e16305094b1d61c01c1005961940a1450617ec009b77dd58283c82206bc71823555d33039bd1dd3d678c34968438a594d27aedbdd7d6396777539517ddd4d6309b8ba65faca1e006725fd68af1e468cd322e2a17b66e31bbd66e1cb6682a31c6b8d6da8dc316b5bbbbc034ca0cca6aad9946bbabcd7c5148983d00ccb4565b3d231c08bf13be7153e6e2970b2a59d1a4c8852173514c2a52a24a9a4160cc3be79d36e0538e597cac28820f152db02ac72c3e51b2f8bce043bcf45631ba170b9ec9314b0c5c620c465022ac0a9478b36cb3d90bf8de23285cee80d9000a172d31802dc1a5a807580db85cd7134de6c5b7c34c602e493c815129e2e2c30e30268e4c34dc99658816040168ca10863065cb0072c4428416584555bf74b16e54f2b254aea42da63cd5b823cf39e79c73a6b2c0f004153138c9fd4fc5c619332facd1c571c409020f3c6060801c763134bc78a189dc27c123d2780d187c73ce396d46a0436867891da145eef7c0734e36aa909973cef9a2c10ab80306b9bf82e30998225891bbcfc323d2375ee10697120acb8b76118228c08c0a8a8863c71c79ce39aff2884c35e0f064587664d1aa376280c139801d2ed8d2aa158aac0011f42387195e7c4007357431658c28b82051e28d36e69c13c60e4c456e551d3ae4eeeefe135d8a664b9ef31713414a9e5711818e3c8f3a123b8ea0b8800c3b7ed87204091cfc9071c7143c202272438e533c7944a8a4828e1e884cd7e753850ee8c0820e2184e8d9810b164adcf1859520fc00d6dd3d83dc6bb0913b2677774bd93bcc540f0a30c70cf2a4e327cf63d559ae1b2ca902e4f0c71086c8f343e822cfabba2891e7cf9ac327cf975c7db9051c544adddd2dc4935d8ac6c72bcf5facba1c6d6496942fb4e040099f2237dee876c10fe6b0b283306ae8c20958cf2472271991bb5f2402dddddd1e117ab40a4395c32063c48184107c840084108880458e2e8280459e376be06046f38850da81394c49118421387e90fb570547504a5279931b8e2472df7a44faaa6820c20c7e6240a207467054dc588207666c39030ad8d19c436a6c99734e1a20622af71b41bcd1456e38a4e456c537b27477d3f860334248e3c38d29bc480a4a6a22cfff8d17e479caba54d540102277777723a1472c7049a1a916d2641f39fcd1051b3d60438c368e70638635ecc8f3d3d5c61324259ca08183345a9003c50f5d880083157404013623529611c94422cf27f24c61d161983b70c8ca52c5e3898a0729205001630d1c721faff145ee4b9514f2658c1cfaf891860d5aa431861a4ba851048421ba14953736681f92ea44d59c32e4f9aa135bf30d86b7863c1f39703e4a95ec215751b39b755bf6ed6bc185ac0de150c64a9f6f03975e2afdf49d929d3ba563904909348137567a09bcf28c19a821831e6250128bc15c47c196fbdc3bee71b2c2ec2c74b9e3c090e6ee1ec759fbd8ded7c28fee76d62d371bc54299ac693b299c7d92e75fa5c07dfbd03e21808f3db77944ec8dd53332df359050644cfe74500765369115e26419a193d5fd15a549a6cd7c4918320b687452153628082a398c4768d491c3b885841c462e31298c71d0acbbfb26ccdcd49340b747845e02b5808b3eba548337d2efa6f4f426c8537048be60d84781ccf436fbdaceebcc2399cf29f20667efb52fbbfc26119d2bc17869a469910f230efd9c728909682622b63a2261f51256c19f0e56bfeacbbc3ac194fba64cc40aba63b722ae83b3e7a6903c02430a214d23a21009b6161f4f704881a2bc3d05a23710a24014880251200a84baf784b10929caa44fa116ea5817d1b18ebdfb06fe7e664ad0586b3f83a290ecd674fb2925da501a22cbde9ac046ba0a772981e10fd905e581e10d2f8764b105c3677b497f628dbd07865c1de1c137bda85ff108894b5214223f26a7f3c060fa095c1dc13c2726a69f40ece998c06f6815272b0a515fe9a226ea2ad65a6badb5d65a6bed4d4e111d24497c3cc1d6fef64b757b962b15cbf1934b9bbe1e18fd49b2dd25dba657b4e9c53450d3344dd3348ee3b82c3914a96ec8381f381c2cb64135ef9abff3f50c3b4a4094871cf2bcebcebfa59474981ccba59debefebceb2650679e23c77c8f3ae3b7fe9e2ce711bd881b40558e6985599052bed391fe58b1ddc7dd0c8a654d5612a1b3705caf41c125273e9a4ee4263f4a7632dd4b17ed5d37711d1a6bf88aca37e55b089fa0fef8f8edd7b338cad4a55a7e0d9f348e24cb08926185148834d833e4aa289ba7499b1f933812694195454647484c4254942e921aa7225724941490afa4561577e24d0cd4d4e119dfb7882c329448455fda2f10acb45d4d3680acd2ae114a24059109c8c1e88421a0c650c59e24850fabc16900318c0c2986548906c5e9adc5d303c2b4f0b821d48c15085f3a4d743c232a75f49c02b57a41097a45fd227b6e69d70791225c9f4640cb1664a24e41419439e9753f27c366b0fdc121370f65949f608db8edc679bf608d3aaabfea6fef5acfaecd3c96e0237a5e6068c5a0b6647e49c19bd0a4c5806baba15a336d87353b8c0499ec9710a1e9232d79dab5f1326c11c09c4de1e15854c10166baccbeb38cdf4a5a05b25d0bbb7c39853a9b6df5e5089d6466604ce40ef0b5c7fbb1575ec91d8e7d817b1ae2978fef6cc497dc04927ae6093a4a1a69e634d9bc89aef6f7eb1adb4968e00546bad3ff0c9b5d64c4a4b25d0939a53e4e27eade8fd91ed573bcf045774b347349e06ef7610c378b8f368bf5703573bdafbda6bad2d303007ae76eee7ef6badb5d65a6badb5d60cacb5d65a6badd92acb6c0fa25a6bf50151aeb5d65aeb118565960ec13a0ea8dd11755e9957b28d77daa4723c0f2cdcf09065b75455030e25950c6502c5f840afdd5e665f4582fe8acc326d680426cbbe3f7b67db401c597163e292b57e0e982c5b3b2f67b26c0b067802d53981a2109beda55183abecdcb7dba33cfbf34738f076cb7e7b837ddb0203cfcb2a13680675cb5a6bed75a450b7ecb66d27620365956e59fb9dcba09c22560a6596ab5bf67775af149a2f9a9824b2228b22962d45d93e8a91add11d39ecaa51163d18c561b31c7b3bc401b6883c64dad4cfdbbbfad5b757b82f9a9c410710d8c621441bb20e7c7f500712cd82352fda086cb39836c0770ada04be3ba03f60ad4afb802d0d6619d822415b30443771460c9c9ce1823ec3a7d5a043c2a1050e2e5ab840411a023ee2414b1632982ec795e8a0e1078d1dba7c814392154ed8006aa2056c74a9810d44515e90d728c2a2450b6e504387dcc615357608e2820720206521050e998d2bb8bb4fb171af2c815739629182944397137cc2228a1427c29042d486942957a4e4d03d9012c318d80d27882e8d416e86dc683192c901a58ea8242c980a9b182a4533100000a0008315003020100e874462d1681485ba6c1f14800c76aa467450970ba45110c328ca18648c210000600020c41820a9a26d00847b6b0128020ddb83f41b19e8719d962746b226f59aaa2f1baec8fbaec571efad7439ff382dcec3a0cb93b35207a3d3e90a71ac67fa6383a2037d13055532547de8ba807ae399f943884c38f1e1920e21f60abc26ac6e68422429729f84304cffed49c3260cd244c79d10d55ebf86a49426b6ac0d0b03f6e1d54aa67ec4a33e9dcc67e5116723adf2d2cdae1c6a0779268927d241efb5c250862325efe6bcf7d70aaf454b7d3cf410dc859ad54ad4520e2104bd07e89220157958681459bc4e0ba02b770bd81b911c607d831c707d98e345d92cf6e3387d045123f0fd174a47af91db47fa7d54b74f853d7e83c917e059594494840a6b807cbd5f98f1c0741719359a3d4a9be956e247d82cb060bd97665f14b99cdb851f48404567636ae15b7c790f9e0bfe33dab8868a07e298eb7c1ecfba5f580b32e8aa6e50cc3ef31a70181a916d2144f29c4f4dc4a11f25d1584cd8d25b93c22c677ab42515c482860a1781dc2ff276daa2f6f54d6c2e1c2ebd2fc70b567987cc6ff3c66a9206fe96b9b05bfce592ba28f2cde0729d862a0aaf8275c15b895aef932d38adfb4f1b68d3867c1504cd2b1897640a2844481e4e35ff225c70f6147d599767489bcce61254d7a50751fb9c533369a07790e1a63065c862a2e12251d30600ceaa2a3818b2a27b37c29b986c6cd0d7b50057c81283412283968f8e410b37326819c9390bdf82dcfe7989374a715f3db6f3c086e681a2fef8b8c4357f2999e8a0532d7e1fe40177d91e7db6a418aeb9d4843065fbd3e89706aeedd517a469123b4b7c19de14e4314571ac2ad718c152afe532412c87854a5bde1dcb6eb593bc9685184664541d4e8d2f2b5443ee9265301d852278af1cf129d09313e0f9f8724ca1043eb4dcc86f8e69dea2b014616d9fe47fa782be0c3b2c3cf298472de81e99c92de0b58b86512851b075bb1cd37a590380c56015642bce7f403e35d690e5ebdafc9c4d01eeaa31978b3dd162d56acce273e24d72609951f2acf15aba4117442f67fd71fdecd6c56815bc06367b9be34bc2d3ddb5950354eeeb51fb4e5cd55984035f5dec9f0320080d96652d15e1944277536895b158577b60a98cc11e344de02b3dda58b23e66e4d5e5580fa09f30b8dcefd19e6934a8108b5dcf4506c3101785dcb4e42d662c8ff2ebce2df821d4ea6a8d8b41fa55cefdbea06cad513ae55ebd6a68de5e5cb61c2f7c28e9978c57a5ce838c02cd661df29c8d4f5847e726f06c56db3e3e39a0820208c8176c803ce2ba89f2bf74605989309cdc6ed3e1eca5ce7a9e4dfb3f153426488c65a3cc81cde822dafa58272d97c6979544bb650227855bd71dde2917e11201135567c89481b5a958e16ede08028494c3cc18dea3b4b918ed1ee54eff415409fb197521e2133b6b192be82778f45879ff37878c2f175372a0dd5a94879ad440a4d7c9ada82819f5773d4eb96f7bc10eb757f44f49a8d650261a38107b6cd74a67ee718377f1669f41fa1f1d4605ecdf266a8adca197be84a95d5d83636d81eb4f59496d2a943433bc456d2019877b2efc731c9a7494b74d231d8ee2d3ecdea6d4cd5d18747486d0a7b0408c050723d4a6d6bb2abc63c05faf605cf03eb82068ece8a579d17f6c3d767b159be05eb414b7eb6f3623a84cb13814cde2d79b60b88c9eee893f53bf237521313ae28357f95fb8957c37a2d1a2d5b0efda89ae60ff9e1d68b43a74028784e7172a2ee8b47ef47b39529f94e484ace3dd580456e1f1a567fe207bdb36228fc6e22c9de4c75b00c87bbe0979815942aa8a524ab313e5a015e02f58514a651aa54de4628bb60b4892f6dbfe9059ac5218ff1448109b2a7fc1142370ce66d2389335c3717b0b074b41f24a3ac02e9f0d4e2033430991d81bf1cb3d2c24c18f92d033c2a0b26498b3e1a102b11dab56522a4e04fa6d5122e3e663f20e8323abff23e922cc493d480db50ff1bb7e3f12100c19325e542b8e9f65aab2f859aba2c3d272d7c409f12ac2b4e869e5735d25d635c10f5ca412948fb948b4ee9470228a7f88baae5a3c6b36c5b7a0beb69022ac8650fca09a2a88b92e9b52968d59a8be86eee706afb1834498fb0bfb1032f3ddc7030336f6b3fd1615623b42082730f66e4817c31adc8fe2cd1ce02446a370c5285233184556722ea27535c7a39d102b39d5224616cd3cf2b4c9d6bd732c2535c601f144cdaa7f6625b861f96936270e8e05544b5de440a7d34293d3c1649ee2b94e1da28de9d59a5e1e752f561a91b63e33d734655d93e3e8474de43e9fd21b082c682296c4e33ca9bf4171f5ca5cfd6eebadbc13ab7ed259837710e1aa8167d9d449da57dccdd0b9d184815bd1e66f833113e4d6428d8f552f23864ddb4fd2020461aa8854308ab4d4f17f17c2b2566673112ab813045f75f5a7b53c470dce66b282e06c7057bdead702ca23ad5574727ce718eec94fe8c69dd579a5f6089e5b176880f98a3f50c7171fa66f16de9ab7c3e6ae775d170dded8e62fde3ee89f59d6c7862e76a0f31dd41c385f542975b7d0aba48adcab569330ce0d8e1f94318335489f409bf11a14b69829f46dc54a0371225027dcba72d8c4eeb49f411d533a50007a8fee7ca1ab83eef12d15b0e82eda743b01553b4672144a121164ca48a78d8891b5bd290081aa9d84dcf30364b92592f147800465693408e7b352381d1a9d788166b44641fed08c1420b3e7cdeb36f0cc4aab99d2d94acfd9e4f1151d6730dda7c23ceadc56668736ff21a29e1f9de5222a6fbcb1a24b300360b35428747fcf3970346d9655ce086475947641561120e224e41a4964277fb568465d8bf0ae1c4f82f95cf940c72f7d865fbeea06ca344465ff5265b21ebdf9af22ba7c63dea76e6aa121009e6d7ef06480b818544a7fd578033fc827fb96357da2e49a67b64b20ee6c9c8b9d1bf875e150718de9443b92b52cdc9cfc0bb6868236897e43b3d7e31824ab3fc6d181262201bc85ab6509281376aa9c98f0f6c92440c5170baf135219f39298a341293d7d4989e1a1548f1b7ca67453cc41bdab1c70c9969c679c70b1864ab4e40174a2530d9ed3135417e63a8fe965de1b3e6f3b072c256415a385d22ea731047122694537262855c7b1237f32e19f2b537aae8e4a45402ab270c050d57973bb393258213f9686dc43fc82a08c9891c61fc229bbda8fc2d7bb258911496bde646ae217f2dbaba0707eba7e4f7272440a0506085e45963d212b570e0a9684b157a58c150581b5f458b2dcf445e0c8df41ef22b6bb4141cb95ba2dc8938905323305f2651d0cdeba796b9ea688260a45d0e684ffa525837103e67ddd0a1113422e1f5f7833779ccf91b0660ddd6085d94e138feb329069a0e69070bb2f27f0124c98968c023c5c5f9427d546c6fc19f6545596c2fbde3b2ac485cf9cfb27ea9c99aebe4236a1173bcfbac56ac85dd805a96caf94b81b0c9e0cd5a8de517e88260e1fbbb59f07946dff6d08db31999d182ecf6724141e94ce2ce6559dcf317de38287828bb9c1451af9ebc6019c25e814419a2f0763d5be84468db8584791c61f424e9c52fcca8d967d02b16ec10e29d4ed8d384a8ffa0bfe301205e6455ee798de14a562fe65d615b446bce63d88f06e2ee45ee49e87fdff83c3ccf159630431c31c4e4eff0eb83c6000f2dce07094afacb284a17ef2eaa52df9c0510a728e3907270f507bfe023600f187b8d84a8ab62c95cc143ce8545cab4aff640bc215052eed7b11c78ac144888e27049a740b91d0dd0991da6bba884540c2d44a313d7058d5ed9c8f6b1ff41fd3485b3124b06d00cdf256f620e004292c49bc8581a248f541a73185c8b0432841e97dd5ce6c786328812f40ff7f5f9e07c2a625430e98f4af66e15e3005de84fe1da5928713023c86c042357c83f22f25271f82ab1cc6614f229b23bb803bd4f1d3f24e23fd019b30c570eb73421e0923b7910e15076dd54e08dcf91f56ab7721d1d6c01c84ccccd5bbd58975aeedd5b52d9205b00ce67674e0fdda955e421fbc7065d71132f6ca35ee79262e4a5b5b4185f52bebbad82d555319f6bc7b919d5fe3012c5c7789080ae5d00ddc9f22460456d33a350c837f90a050c471fc1390038daa7e2629e496a511838f7cc4606dde0a1f58fd0251c310e28e003a060902ba1bb4f963319db6621b2164ead83ec4e47c990ffe3020ae255c1cb6b16773421b6f5eb6f7d488e36bd21dcbacb6435b6fefd8e09fbebd10c1a6b54b6ba977875244a9ffa4eea491834651aa2bcadc858da53be622d7da9993ad9e4b984ab887bcf06f2d991f30b9e9a8f11bfd9534e41b507a2d7452e0bcfe94eb3e8e5c50750f44d1df5dd9e754eeedd115392f70368c5c950bd081aea9c6d198b7a0c79503869a1fc149ef740aa97c94de69a28b28f449566c4e23e016721a6b3bd84422f28d36f5881edb61115a6c31ef5f2e4ee2d3135711ce1a164a09683949ca975072e1b1f492f8abaed120779bf375650f4285c5cb78f3821a49b6965c38c27e9d6446f2a7f0447de7342d07d61ace8c79a967a135edd5ead80878c451fafb21b9a84c823bd89d3331b56cc237f4fab401e194e5154b82b686929b08d4236491549f568daa1ccf2b0ea3e8ee73ba5760e34b7093a5867444be94440a9399b6a04200754d6fd44a3db7b8a3d245ae41f5f4fff4d390eb49274bd055b1ad899ec0518bfe492c2048318e08460441cfb9cae45bdb09527bea58e32428287153679a2c9b6df508560d4fbd38750406fe67ee3a1ab97eda2a3f3f059f12dd1d8da2f39a4e6d7e49571190e9aa8a9f093a40f10f35c64b5da72fc1e3808aa6000ee29ecc231d18a006fe8eb203cedde02f66c46977995797ae9615187922d1d6723f16e35ba639017334e9d992e1b3dc68278a6b36cefe0bab837c07af63f18405459c2d210aa2fbb0cd2090167907c27a0c230c9786048d176392acef4ff22a3fff48fb35c2cb324d375bdb2aebab18bf44b7ba52671eb33a0c25e6a1123fd329a40eeb4b7685eea885e9fce1f5a2582e630fe9dd35a5547b2bbd6546a594ac48a594141946b2577e31b1a78860ee89a56fe18c24da6b149c9a436225ebae21395e371c7e728dd9963310158b8927c7da97d75cd5afcf182c6d0034e07e8da5457c3e4f9b8b86acfb4a6323629414f09ec3b2e233a5498f01a543e10017a9c825a2a201098ad361e678c99cc6445c820dac609a05d13dfd9c9fbbd94012b9a239b6cfab35d8a9988bb594516f74ba30b43bd4a20a99f29f5ad561232c3bd4647f25faf0562479d7595ae2921a5a5462a8380dcbaaf7509e51d22a564c44f7621ce70538875f97bdc180949b93ebdf4d42c8cb8ec63c401145e121c690c0d0beffdc3e0fc16022f14e29cfda1ddbd543e02113a273cbe30853ec7de72510b2f9f4520c1233fae1f26625899e9679682b37016b9df3044d28f2d9b3262039b5fdf141d0c262e96dd9c9d513dcf9646935afca88f45e15cfad0ca44b315a804c806212209b50be7a6fc943df6dafddf3d7e2e34d57b290ee0d261fc4f60b7061259f0d8456007ffeaae4402b0dfbea0e0e722c1bc0b5776f9497253dc23ade308c5e93764ea6a57f4f444a3da0989d7c3779dec3cfb3929414a12a4cfad813b760cf8d7e3db2b81e23c3c6c1563a819c0f28cdc74a5d767f6109d52e97612db0ef2bb874678c4569829404e3fe12331cc5863307d37d1e9c064d20537b34a1596d6069d42c8556efe010f2e1d18a6092faeb56739d10f497894e1d02c83a4d74d217879e8a88318f8fdbfa3a764722b96d33fb28246e38b5515d08ba46866e070949de1b977e68f422e7849ba660f124f02dc4c369fa0ac8c3b939b07d9e19e8addd010e68a6add1024e17f88e610b5fa38c7a2876c04a83f1099314ede4fcf7d57127cb120bb433c362d4a6091a67d21fa783df68d00391815ba80e2798b331c6b47939a35cc6d4a2f405d3cbba5c6be75a0cd62b87519b6bc567b4be4b4ebfa588108b6339195a3880f97062b61d3a6994fdec27475f372b16958fae683b91f95b558f240e23a4eccb087d4d52fd3568e63ae22888bd2f40aba91d8b80d86d160c20f5c874310e8b43e9fd07c59aadc2a6084c2f51c657653bc39ec776d4df00877b11d00504b811fa827f5d26b27b03cb8b434500e5c8cc18dbb984613bd20e9d0259a1d4b1f2e4f7c468db50239c30456e5c8870faa60b1ece396cf76cc3cb39f172e66d516d68c33c90ba78c653c3c85383dc8a5b3307ec24e0d4f5a7cb616f4ee30389b82fa8b8ea726b32bdc08bc0016ada7b1b1a300d299d2325ce9d77561829b3211123815dc2ee2713031010591d6417bfd48b745ed8194b24fe1e1b67d26d6469a200ffddfa6ecfde2980256dadeb12fcb830d5d47e0478ce74351eb067e40ac3fb25565f00b373173e6547428b36369a697a944c514af595a4ecaea922f5a20d2857a48af8e28d73193d1eeb554d3a403c01c47e7d0bed9b8e1339a1cfe4b347a653722b3484ed2ab1ba5cf9635cd48c6f65a97a68d40f4b87d200caf4017c69c7b4c1c6128304acd32e4afbcb4eccd213c6940032dd3114e6824ff343dc08b34eb72bfcfb4fb58235227b3f79cd77685562ef7587f29132439d1c30a2c85e590b83f1a83bfd01b32f4e66d735c27027a1c208abe60c1a55a1a65371f00c508d4466ec4e98d479bf272819da0d60989524d3bd211e725d4400f03a146a2f468809cf404b640d2f69dfb021fce442d4839ddf6a75359927384288e4de7aec48675e8a9e8cafac7a8a36cc0ac1a2c9598dae8f77266453f60c1e3520317d105fc1150d1fb70305dbe7c7ad4e79c1d7325fbc1e8484f2a8640088e548063a3959e59692d07ff243642f0ed1641ef08ef68c4b80b5eb37c63ed4b6c4157d1cc74796329fa6928fc1108ff4851c049e79b9a34141375124ea6cd6eac3f6b8d736b3c370c8a78b91641991af87b0ea671b81a8fafe60b1d8dce49c8b79966c42f915d5cdfa375dad10c4029a74b6cf0137e6839b272030455fd571ccb0de1905f739559f7231fc2cf4a0f503ad91815f3bba8aae1ea6846730c8d8c3d737c2096ce9a428e1a0e93a7fdf83877ef6b602250f75aed32311c3ee35734e2fa90248937b47103e8ffeb49a9c19d65cdf54646b02118070f46f03639529e2138bfa207ad7e0a1ffb9b4e4089df58b5f14289635996c07c8771d4d1b071822d424961e4f0cfacc5de9c3a536dee1789bf819e18d2f1932404ba47993f74ccf06fdb327c6aa871e5b208e4dcb127155b6a9f51408fd9b0e617bf2cd706b0b80f9099472d7a039656d2a1e9e495c9a7212475251e23ee98f25c87cebadfce6e8298b71395a54e63836087b10c97a89c8c2b572927f513ec750f2c971b816e36003fb2b8930b34f0808af1323a28eaf67801004f550a495731713246d071f91da9dc2daf9a7b0fe88548aa62a1445aa6b7ffc93a5743879b35b23444da2e6149be041794ad7cdacf33485c7bf80702071f643f5c167dac740512a9d16e2e8c83f8c0151319e7f90a632addbd41e42e0127224f18de94abbcc1527f9084059480cd239d2dc33f0044ca50d2ae65da49bf24f776824f0e6be97e540ef065655c42fe069b1d0f56ccfe606147789be950b60cbf7d79197f327d9e6cbcf1e25e81dadd3372686f9c06ea54cbdec32062b7d29872c2b908b5497e48b545b0dc4c7244ae958dde4c794b072c89c3ab1d2ce73697e15341efba8654bef526cef5b8cdb1f107e346ba990c700c696af75654818d4cbc7506f374d8e88d929770af2a36ad96f42514929eef4beb8686305cf22a22c95759c71d4712af0dc2b8a605985bfceccf3ac638095ed5a9578a70aa796d3d1ad58131eab8eb66fb7b875af97f6167958e89d97447808528862a656572284c33480c6f3738010d06dd250088efbd1dce732ae81fe57f7eec91e5dc7e6dbf643313143e674bcb3c1a5db3e1a7c1f063ad89cb5dba7a43dad9dcb65521138e74373f5622a101ae142ef723d36c4d79f1a49155c394a70e7a726643fb8abbcc2762070b126268b39356b38efad7e2a6bd831a6a1a1a8efec26ea6f9e154049a476eeda6cb3023c5aa10f8d526e2e10c172a1acba591bb2356eb1c026df288afcf5342eab321491a2291974b9b32c7514007caa3cd5313de93840b0ecf032fd170ea93991340c4403ddf00b69deb2e2cc161497e4f2e00da33e53aec6238e355e359548d6e82e548487ee664840d71c1ef5fc70c0fff9f856850aeadaa9ac06a54ba656671de111217deb4cc50f68ba5604a50650f4b3378d00829838fc957ebcb2a2bc42c6e2d60d4c3d18fc511b4345a9d6eb28dd75d462e06f4accc2587f41072aab82a9cb14ad6dfb7d30599d33454c5dcfbc9f21502ce2ea05a459dc5bed2813131b8f5c77988dd72fc79218e1854a6084cb61bddfae63fa2ffa8b52fd145310d253648088459f15fd704c4cb7c4c7e97c31791eff10c27218c5ba25c0ce804c043d464bdd607eb16f2e71fd723ad2542bd790a797d0780ae5d610cbebe8e3d4c00ede13167b5dda3ced6d01a098f3973515c9fdcd1a2a717a5953a1dcdf02a05248afb52a20b931dc979bbc13ddd9ffcb5d1e72efb684a6cf5fd6543c7497b99cb4707f3ff0ce6b9b043119d3ab0a5524d797e40c3e756fde25b70efbd5897d58462938c54d8183611ef97594d6b7d8100c388943ebdd98a147e22ccd2a99e08449f3ab4cc440f52a043fd0a909ecdb939ae114d1a328bd23f230f7ea5e23deeeb9c4bdb92416b09f95d84faa68af474d62dfd1443cedc775964f2620f2ab8c069f8f111b3852a17e9e455e95b10efdeb9effaad8bbe2d0062d4ee9485bcc633f9cb8e2afd1a9dcae45d67c4cd5dc39a6f86a1d8a8f3420c3ddc2f7bba4e825f5a7f7ffeac5096280767ca3ed01ba771984235398aac64a4017751ca0aedf5bd582a636fff5fcafe76ff4c4091240bb1bffb2171eca474b47ef3f7ad87334f3c734810d81d66c04ee54cfef97265e6cee457b7987a631c20036dacd95f6451da9d81418bf4ea4dedef2afd7efcdfee6bfeecdf64379b1e39b6f83eb8d0c8e0bea0400ea560216609513530a59c0d29fdc595b3981653722d8d37940db0bb9774a999b66f6a3682d88e0c5e7287385a3f0ff6d3b6091e1c97be2011de621354974a2d9d7636587d0c5d3990e6f2e4caab1267e6eaa8a4195a53b565dec0d2a7542a64266e85cb1c6700b9cf02aa89284a3c97aecf7f8c8f963db0cd8ede12b4ee1e40cd71b2444315bc654a5d218cce8133a5622632ede0ff3b9bbf3a9b7a67d206fe6c7a5d8f219540455a32fcee5dd42a9915277ce9be5e3ed89df03d00a7f56f6161a9f3d0a1c841cd629ca9b640d99924be2e33dc8b30646fede2b35df13da7fdccd1f1e1d8e3e8218f87bf08719965f682ccfe0d91dba572f5ad720c4865b84d3eab53a1db0b7118af154a4c8c6569ca829b079fa5a627d3bb4dfdf81c0c3a350f8305020b47ecc0e5b2f8647a3901549b24f47fd5cc40ffcf5cb9a91e5584413a848b9612e1aaa836c353ce484e2a4387e7ce34224860b0025a4fcfeb4e3d9c584b30bb6c33174417f9f4ca20f7068f2cfe7df0c985818bb54556e0b36090f7013dc45f827ec6e73b50904821c994a27610c51b64020c75cacfa8da02f69e00adbaca3621a97dd796fd5c931cdff0517e5fb59add9fb8909a987c0ef52f4d470996dd85c3ca2e7d9765149b0e789426acc1a55f82e3abd32cec2f8e484d82f8f984b40ad2f16894693efc5b7c4d6f527562b17e692feb18062159f63fe94e79ea9b694906cc8e11adb3a65b6bf72bd6bb40a1375c0ca0916b745a2bfdf1c72b53cd4e3445f601f31b9f87115845c129941be550b65ebeabdeadbe164104a3a69b6aeaa419f54f1578f8fed84c79f2a45a7d6e32a8ab6ecb0d5dbceaa2af554f3f6149be659818c78d63f16dbaf3393acbf5deca42ad83d293727cb43c4b668ec396024ec4548f924bd16a306b8a695d20aab9bc5033cc32dbfb6a906bee5c59405efc1953c7ecad1cb147e0720a5614b2c26ca147bd704caa378b582ac57476c24f8ababe3a1cb169d39a50d9b1562327a22b9fdecc9841954b5c5c49a10fe1931009f2b5b55350510e33dce6a178814ca86732656ebc7cac4b2ab86459b3268470cde912c898f8a895dec3734a78a469ea8abd78f1d4b676dd7921fc294a1d054f598a241edd13e95ea9839776f85898b2e471a76a978a1765b0544a57e2a8d4473f995ca656a7a8367cf8131631973f46adf94f0066740637d5ca6b080ca3b4f6bc1ea3b2311dac6452bd555b7c9ac3e0292c9f70cac73826aeca9f1d513cc28824abe5d5e2fd63f46958dba25ee1d4c251d3d51db6653edc85a95191958e70454e33ab5bf94afe524768a937a70578bdea9bdac7cc7afb0d5bca13e47e643596d3409e3e03b7972a5379e4296f6438a10fd38b12439148d97f86f9359903c5d658e49ae9705187319194e46d8d32ac3db187939086ae8076773a6d0d24a3ff2577f457f9d5cd7baa8a5555af62fd49345d420dafd48062ac1024dded51173cc5e5d61dc24ae196d25f2034cd285fec6e6aa68a348a0a243dbeca9e3dd0f7c7ac82342a7c8784892023522136a56de70a724430e4ea77214948f0226cd75b110c16cc4fb1727237642a98ff6d024e69928b4d68d263109a020d76339d27333a08c274a1dae534c549757a401201d3443ee03536bd9960586a901cb445a4ceeb9e78806dc5ed2ad24045793ee652712562cade01743edb707df96ff78732551ccb30668b7eaee103fc8cc9bc2ae8e054a4f2ed53481e03727e3f641a609fc016c524604eec0ff810692e291942af70c2111786f69291ac85815f669a2ad5a793bdc280c0e396d9ad195c8db6e9fd186b47ff56662538d0a59c2cd4ab65fb7af24b0812e021a48283f8106c65fc1b9398bdba20e47fa821c07b94221cf9c63faf47a261aaf55af50e8e3978ad261750ed030c4b2332539a825bd7381a903a5d4dc6dbc0ee2729dc164568dce1ae1f3b89a10240b5131a0d2bd69e13f64462fe3a6fdfc17639ea840415a8064d78ee6c34e8490e85aba5e101f7e949b579483026f2a5c586ee5647c0aa0379f54398f2f4e602c3c51cb611e103713aa92c0aacfd955b2280dc1be96585485666f79918236c86854109e2ceac80eb86da3faf9dc1d085a66dffa75e7cf7b1e52af89b0e6a8730b6c1b948b56f25cc6ea6c74732440ed2085e5e31df38146810634bde6da1dfaa094b5a50bab7981f9ee7d0f4823dab2592a09f00de149ec79dcbe7a22fc56c1726e094cd4047e42db1231fa203273f2301512385bb376b65bb5c1857a2a81a96463e7d3c75f12441a0a21b569842ef630a3a54507ff632e16bb3054c34b4e761acdf742fab3b1399928e55de96e021b9510124c200a34fbff1450159c20c1fe3369a8609e2265c99bff444f0a4eb4241846bbacf1e8ded26261106f83ca64c9b50a3a573a660a562d93718b407e2cdda2586f316a2644469910144c8864ca4bf855b0330a8442939575f20db96c09cf3ba782ed2bee546d5098d72e0c3b8020a76a269d30e30416a046320bd5274e19084a6d3a2fbf085ce14d0668c9d44e98eb083481aa4b4dd442a3978d01f35b1e1ca68e9ee30a79ea6959ac953abb921c9608e71fc23c24c3489454ec47867246f8e2e37aa226211507f92d0d40b2ae74714ede18556b48495343aed328aebadf3d2e44a03c8077e5ccfc73913afe41cdb5518a467f178912d4be2f554cadfafee771fe23f669e522d1efecd1f551d220bb48eb1549f5dbd8e6228d5d9ef5cc85c4b5b5091ab111e4b64802f15a0f2f694aaf50b48a658f48404f80f9dedd48246e7cac8d8537dd979e81cad97bd4780c9cd6c3ec9017df4159029ab3f576ed83a57e902b77f7de3276dc1195df7ed1f976848d1e46eef19416b57654ac604080f152a20903a6788ea5ee49cf49cb255aad6809fca16edc561966f0023af5298a068dd5b4758d3118ac82e1b5d015d22a7185094db5c3b9eee6d538ac39976403eb600cb420055a7b7060749193fe222a0f20546daa0273ef00c0164b977102690256414a1ea7ab747150a518e1e05a1c7af569e2b63187acbf812b31113ac78c310fe890268fc1e5a1517aa849e0b5e05cc9a51b0b81088d89001d04e2d4af57dd718a81f575719ae0afe929286a8868fc2ee0ad3a240eaaf52bb9c841c6f509756923562c78f76816ea6b5c655e5b0efb9b7cacd5f0892ed8dcb76f2bff7b50003c3b39f3e3a579fc428679435673e7815efdf59735c0242243234236bf37c4a93aea8472fd7c5bd53252ad0f530ad57f265b3b84f39a591da6fe592db6742b1c942845c85dc7e6a858296a309acecb9dc0ef2164b6b8ca805a2e3e06e217341e9f2c327a627662548401ff895f85b6bded91d630e1f31fa3aa154f96d80af575bece692e33edeb6d912303792a450b4229408d33311de9369d0a53f730ed042ff0d315c1c5b23c0be266060947b3866a4d49e5d233f17dd0f158fbd0769f3603d6eb4c27fb7595ba40de27c4f72c435a67d8de56bb732df7cb2e845f9e396bec51c5f39b197f176fa9426e5bb60f624fdddfbc3cbabd8a283ab082c0eb4f168d8bc2920ea26ee279c3f6c32a18a9eeab19603c379590e3f3124c2350308919b8275fc8852e1a6a387b89f487c04496a69e4afdd0a83a5210c3e9d05c4c5dc6f200a5428acee36455c685f8e347ab4000fa093cf6509c542f33328a4b5d47320aff00a33ed210d55b0139d83c6252b4bb803f8aab83d8c3f157bd8c9c05cdbbcf18fe51f42ab116ac65c52c41011f827de0279563639713813ebed5b269cd09531797439489d0e7719df439327086b88f2d8482f175210df36472bcb23d2fd3bfb6667b9b17ab40847a852e0eef0836a2339a88341757ef3a816b57c891fb2db2e1bc6b258ce1ecf0db8cd71fcaf6c80b73543b0ea45cfed282992e2ee891966d6e8ddc0b69ab784fc80f81a049ff5100cfa617894959a208a380bf570b6fe88c6ee110455309bfda5708165719c10e16df67b2f428235ac88cd4c99529b0974e5f4610870d394edfd713658cec7905e7a0b254cb0e05d6af120028318ceaf6c1fe4cd31fbe60ce1b83e9efa2dc25055a906da73daa003045a1a01abce0d8bd1fd7e1d10094c2df9f316ec70bb460aa9492a58e61f821559276f0347f167e80ebaf73456caa0bc4eb3babe68f97b528c85ebfa135828b7d2803c7f45990c5f0228794aa0376cc6ca2e6e0aa5415fe77773dbe1d736567a1eeb6f1ea2313e8832535ec21b973635f9c85fd9f30256027761947f0d1d98d0ce9a4298bf6763dff263ce6b1ac69a853210847112832b7e25564414a9ddd70e0801d65f4baf855a6b3d4b10ed1f5812caf697c9586828d572e767fa2ceaa7ec64d519f8788a8856053db456f06dbc9265aea8b7fcc7ba564191cd3b0232d156213a687178655b66ea34d69e00e85e80797a7b00fed36a478e643c4f920bbfb259b626028b0c0a05436561f8cb29523b135659809864b48fc9852c1a8fae7af02b1445e65f51d2e18aa87d2015f27420bc8f03c271fc70fcc89fc8d69ae480b4501fdb0e2c34eddfc310f07387f2f35c80f10a296735bc6b70726ade9355cb0d7824ba0e225c8158102462ec466f6ee15aaf4f0826116e56fc8e58ac0d93f62a8209ebaf34427725cc85b7e5c5683a2c7d0541433a7b48f9e90c9e93aeb4717f4bd8079758a5e0f8f67f184c40c27af720640ba79769513fe7f189400d75d69243459e4082a32da3f9138bf22051c80d3535f8565f9c1afb64398dc0bcc72e3499576f6995e4aa8c4c27e6f5de94290f759315efab1f9c17dc60773de757f6899be699f931329ee04a36a776088af0b7ac3f2ed6900ab155bf8f428e786b672bf6bd202383936e8436f8aa0706a86ef32235dc23a7815348fef6079fd06534c9494372e2c818b4546fa4738897749edd3f8cd0178d337191b0346fac14401d47fe095048fbb1c48cb47620965eb57867a60f17d201501e32d6ea563bf080e33424af284d0fee82d546d104971c43865fd88cd3f3a0854181af7a2b390013d139453601a2df6f59926cd2ec03605ae4985c740ad1d16ae1fe741344f112e263d036bb6e2b6cf31c46dcb02ab3cc30b64f2da88e3e3a26c2c050dc3183f5d452cb81147453683ee119214d650389d13077eefa80b8d47612ba9d4183b072a60faf6cb6e0ed105d95de5f707857082e281d607e71002861196317527356c034b4f718d71130040864454f8c3af86fcec3a8bc0116f0341f3781a6158db02c62fc8df97631aba07ead233d06232321a4c8354803af67b60ce699615c2d5d5412d26024711e15129842f7f1113fbdd7df55ba1fa3a90e0d0d577b40aa1352d2c73397ad4fc6073b905254caa144cb89ec4f35c36d7f68526a206b8fe80871baa350b991e0480298a3bce9dcd4405402092770b644946cf941d48609f59d2ae9b141c462607682450df3dad0cc93f10760178c23003e254c4d204d9de88cd20d4862ad2327a6d6631c2507e286806aae6bd109976900b01edc97719c17aac207fa698776064a8940fec2b4d87701f2633831e714b2014c0a077abb4b2d225ffc46415779afbd7bcb68f8d6d88958190d4a7a0808a68a64e140f330c9740ed275cb1291a0d35444b7615a72f1168df1f93a6d9985fc50e1ab66f02d263f88ec3fb5d07e114db656c5a73412d9955629fb1072e34a24cc79741abfc043a70bddcf93b186b99cab3947efc7122115540bae86601ee4b7e8c3b4c16d6568ba349aacccb173b312084ed593cbc46d84e9eabc6ef265dccc94bacdee42518e79556e5ced649727ac21f2e8941fc5494fe5089fe431f6448856094581fe2b8707802123a254aebb5a98467705c1f0f934dc9d5ca8dd1045bb78470925f340da7a83bc39e43dc95d54c7ab14e244f8766902818f1ea346549fc548700d035da23ddc5569fae8957c8eb3d272c907c493c8a1768cf6cf87f525050986eadb6842671c6702bdee12b610387c706b01eda3978928a94d333c4d92073fabeddad9b8dec480b8ed06e26bb157a4d0e887076145b64d866378b8a2e205004885ed2840204297700d516fcd8c4530af887834233cc438fac533434dbc6ca954dc9670345ee2ae8243bdb9ffdc99f2efe1889da5cdf140a6a302a6590a90568f85749de91755158867819af5d1b9d8d656f1c5447f526eef9b684c69a17b2dd86c14bc992011d6a505fb4963e2e4b2097377fc6cb490d5ac0a6befd07087c956ce1437fbdf034b48ae2cb453ac5b0390745a8e60d19810fb95f9edd4ef679736ab7cb7d52df93fb69fb63ddbf9475687b3a3aa0502b279d61205514b10e8e3c22067ca4f53a9a52912281d5b42ed50ee745cac33beafd6cad30b1c55bd755b0f6dc4e2869019f0c5ffa5bfd8309d807a0d7c50dd043717cce9bd53ab23780d90ce96b042cb7f667d0d2149a79e7dab478989246a87a5859abd5d909c36f43e3a641936ce6765abdc4822280330eaf382cd6cbce6af39865bd36e8a1760d51e1fc727d98acb861e161ea6934407f561dfd06a4d8d2b151d3ed9231efa004518715cb92976d2a92a0b7629275ddb2adf99864d15631fad7e034ca8985fa1a6c1abac4be1f0d4c979c58aa99d1616e22d1ff401a9229724e3fc115230356373ebd2c0bc3b3fc352ceb3e8d187b37d650f4e9659d8a876ed8efe52e4f45d78778cbc32044b0be8b77ca107abdb712cc83d03dddbb4e906230e497d52a34ba675e4bead00dfff213eecafb95fe4be48d78f89900ca84b4ceebfb0480d4c19b1d98334d68cc56d8f0c08c0db5c3dea33de9c73107cdd8e0522a3781856eef151186a4ac4ac76bcc16c51e6c20d0298106d97bb3bc957f20524e6ba317e7bc10e9f1877c33d2ff75caf4c3538b008358f7ebfa1f5ca7ba176958ddd92dd7223957a561063448885f2cf25401b8ab8df33d12df0ba55d988ae8be7b5956a839b938586c0335ee66cee64281ee04dda92c260ca6770a31efff1aa842009d6469068b73053225f3da13deb3ced653635fbb26cb077c32611cf320040879f50e02b4e40140f68563a8dddea80bd17f51e3dfc11d56f5b152f457548f1b833b78758b231a09115316cee0efe4edba11482be86b8886d9bda6f079827c6941817a761c801974d5d0dfae50cc8a6a538c9363c624f1426edaf8428fdea0b056b510d358a83d9fbde6ac0bccf84d8bbbe74a4d10def9d3050237a42ad62775a0decc28ed3f57c90b446bc4a8e4bb88d096ec271080484b762516a59178c8908ceb3c5664f166be0e073c3cadc2dfa38521828523ad8915d8416c4a4bbce794844755531faf5cadf449b636085f0f5917bd616bd36b52d38a869691e280035356b909957aa1589b1bc86fa937244bd46d14ad9e92b788c46fa38f954e813d79500ecdb4fee947194367a482f338402e4b3faaae0d6f803c240e08473def478a2cd16029ae0cebc2e5e4e2819aef178f9dd601c8351bcdfcc0d2d077480c8df13d20fbc6ae202805a7e106204c45044bbe481c7f28faded83630e432b5821e83c644583f195ed8f1edab4a1f1a0e183ccd7e832c5e6793dba84e6d0b98410bfc1169079eecf04c7a58a9b9f26929e990b7fbbb46bcd39d90e61853cdd01c9799d2caec85595f5959afcedf140e46da5042a2fb5f2b7946bb51cd5292f95ed13027efaccd753092c5896ee3deaa85b3546b6346dae5d702f286c3888c8369266460638824db4dbb7f62ccf8fad5b1d1d37afd114fa815458f77f68d1b2d8a84df5642655c6860e883992c18f77b22aa1f56e7fdc4e7d4b2195273f825a3cfb8a0c06219015a6eec26983b8bc17ec0a9294f4cd7a79aa16d59464960f4a57bcf2a2cc8cebc9b11be172bf78d8de2e6f7fdc5c059984c8813c103b11b8a12e60a6df07d5d0955f0fd0c0aca270ee3472926f566f07ea4023f1a76a15828f2f7154b7030102ef35d10770ef5f35a523361129e0ee7a225f70b7fef601dbbc72829b48bf0a40f564c02f07c1efc1baeda74b95c0a6f1176a8e5a7e21d61a7ff9bc23b360ab72ec7c70c9864570050f30193de52c63078a351b603181f8efb75648be3cec4f85dadf200b33743b4c5429fb1ab51169bdd521cb762e63c2a30f7aaa77ab7bc15eb3579d0efbb8493e68c954efa966807a4914e6b3f698b0031f07371f47dc21c857633152957846232a5f2bfd9d6c44b441b74d92c310c19469253800c362444093d06b3e5e00b193d806b6cee20903da379eebe446e19de7f846d79b089a893039f084b49f6504d393a091978e12e37a12637c97ecbd992d2d7899345b91f4ac413cde22e51d539a1323546bea32f9755ff9b7581a4ccab34bb649e0446cdfd178abc0803887eefe3ffa71697604c2a9426bf61a94f663bf4bef5883b8bc14dc6eda673de51ac04acdb29d5a5535f9d74a6d2bf86d79f3d1f78feffb9045494126a9717245e2a527ea598ddc217e42f439f9527fa2342384584a4f818c53f4365a2ccfa6c6b32e78d994cae8598fd4e06e917c94c87fe54813b17028eec8341d9e734a2c6b8f378ae1418a8b6e82f841ea6d89b8c5f5b31ba9f7f9c32c74ea845e6279db3cd4b56a7b8eac54de2ae20c2ffcb63c1e75413946490357973d6eb985d1cdd8683c8b230d7d98d99c30eff4d45537b2cb896e9709a94059c1ffc1dfd5d4bb704aa0cfd1c830ad1f196823943f627b5e365f65d6a5de073e50dda934cdfb184ad1c656f35142d320cc7d089550149acc944f0453e4a9bb0e840036d6516288c1461fcbcecde789af83540bf9e7ebf8a20cc51879e9c41b8c59b275b7cde0aaf83974888d6be12063fff5de9ead265adc1d61979a01d076465ad1861b4768144d007ff4d1230b62dac34cc95b538d7c4e2635ad88dce6f4f9dcd02abaaaaf5db453922b2b034d14ac4ed3150f964989d78355826ceef4c39da3b62374f7030ad889c040649df169303adb748dd7c4c418f2a4fbd0e9ddc63fa61799ab86bae0d3dc9fbb2c8983ec31e48bfdd1aa3bb0ccb72899dca2be1c203d25f187590bfa55d58d1168690e7794257096105e7da7e1a8c716199b534807ce001f08f1a5066644965e4959d5f8653c34ee2355f471b828397538b066f0077512bd8faff085d58ab43a14ecaa5c3aefc05d5daac6504e248febbb6cb27832e33dd575fa237478fb44e73fd64f11b3973f15659ad7477a714a4350df49654b3d05f4f9cdbee8af3d55684449798bb97a27b98ff1a1045b89331691de45ae4cbf57bbc07a1752df6d1d4b8daa56dc0384d0a4fa697c2c60fafe44f2c518fd83fac81ddaf2cf8412fecb5bebdb28b6b34085fd42c3aee265f2b4d04bb53a665342914d9081308f00870224142a9c42ecefc873de184243d59dddbdd31f3c5c98dd1a69840c938b6b080ca175f76c63a82b0ae3ca0ca95d4f9456d8d03af776ed5f719bb140a6e761839265711a99d5db64247e8bded60dd2bf3c1f86c1e11a7ed7b0617b1a284afe471973aa0a93102f322c80c82294b174457d2dd28a2fb30b2006ba7355a59b57f3812c2a0c0a6faac7685336181dca29e3b4f71baacb0efc385d101770114511d9f1e731096d65df32756521e9bf7444cc45b160380232846b2ebd7f520fcf7503bc328a4ee165ddcaadb6acb06676621045f0c63eca8d36f3fa0969d324c5c1e1de0b943abf6c9b8aa2213c96e8d4806f17013efff67411d43e1a71179634362ce381320dc17f79fff5750e47d51670efe44339f8004be7a969c2fea2e3c5433d44c9e0a967e4bbad32b852ef20e5b4e6411afed6f5d06369beb935c3bf896c5bd2b0fe479306ee8f693541b6037f230e202078e192fd4a483b03cda8705ec96f56fbf3dfffce36c6a3f8f49d700c4703ab64a069ed610bffd28441c4239620b33e284cf15e6810c09db65c82b5ae26efcff9f332c7b76004e79566fc7b791f20d115d2928dd6c9fbd063bf9fb7b37ab43eb5aba45f6d0685b240a580e8d376f06c381e94c4f84d2a34792dbd1c399c6f8188975ece66c435b090e36837534399c1514b47aa2985b35d554df681267fc18bf3613dfbb99bea0e5586a46f526ba90116ba94000864718859257600e3dee827c9d30fc4446051cef8eaad1371502803c71209741768884193bda7eb9fba46af2ef08c1fc3c997aa8afd7a0ff98089d3ff00b68082d42938f0356bf0877ac91e64f9398b6a56a3e00a6ff07990adb9b784db4ebc90e1541adeb3d563be612e362248fb87e1e1d84949b0b7faa1588dc22d1ea911bc04e1dcc9f99e6ab83086e6badad288d9b97506bfdff4fe48619937f76873694145ca65af2964d38c14c8f3a777db482e4ff703a10a2edac4f74cee042debb1d1c00af464cf18b59e95d4ad26058e8a4cfbfbc5575fde13156b8e56c4d702251c096a95c50bb3dd01bcdea4d18a65a7b003cde36a213c649f9b37037dc08b07d65da1cd5486974867cc5b3a3a668d9a34eeab7f8bb74df75e53c8216414d1bcea2f3ff0c2b16bc0c219e9e146caf6aa06baf33169e1610b7d4e351cfa7bc5af6df1944efcbaaf624926e53b66601dfea26d00b0d15a0a8ef3f24ec63fb4c8bd369070d718d82d0cda09194afa3947b434388c48ba5543f50f3fb270ab83fe7b4d8048db736cb5d0732a0cca13f2360f3933f28441a4796cd8dd6b0eea03e77adb501242fa25e07cdef854b0e7d5651b830b74b1a75e8155794c31bc5d290c19dd44b18d671eb290b387e257c7315f4d8b46a3c0a87339502ab1819dda9aa9132d45ef00e78f0e957e59a7573f898fb94a36f8903556e88cd460a7c5674f2ea434ddc1d3fc519dbb67241e7439c592dda99a11694e1a7f19b8871ef6608c148bb54a878a23928f36fcf910ea0a4cee08349a046737db72591198547fbec06057c175b59c6b76b99c472a2ed52afa6431908fe6c81e51b97830e6e247123605338ec937025c08ff6c313d81037b21e82dbb3156703edc4f2491757db34b47a97c0f5fa78035efc6680bed550715751ca35815b17bc12c50a2fb5c7eb300de453a38eddd50d30ae1da77d1191923525b8ca28a3a2714139a4765f285c831f447460b2b6d3c38aee3567adc0003334ea1fb1ecc80f5f71266319d4bf612e71f4daba8c47edce5c88a33b6a77bc30677a50ba48ade925445dcd09a32b14e5789fe967fc614d8c9d5c7aa84b0090a64c45b553476f45ad37183bf94a395115b010567572975d56ace270007a218351fdd7b85762e6be87523596bebeeb65c7be1293c973c27824e751323c6ab2b382d8a7c54a72ab4ab090482ea133287cb76069014903efcbd55ac220288b6f8b1a08c6a99b329b3bb4edd791a98d3b0e63681fad14132f089fe34ec668044e0803cf06535c044e42f48c8b9b7ba3070f89de8310df3131a98bf9fed457279cd81e4f3852d3838c203e438ae60bba15d5a7e57da7dc42c3afd0e45296cd4b9ad3e110f47957f4e7e1703b8ed00c4350f1ea1094a83fcef3e43e3f4e3b49067d66b0bebc4721d7a61d6e1a0888629110abab2d15810c346dbe4f9a32ddb55791bac674d6572c473554dc8689957d4cd7cbe4f54016560d73415f30bd86cf19e23fe99d1d990c568f99ce25d5f00a33d10e6166afa5520afeea8098b34c86ca9d7b0bad45fb09e606ecf1041940abcba3a50e5c519ab6c9c93ad8759c9113c951c4671d09085a0f04f1dea5770cfefb8bb54c4822a86dd95d10ae02924ac5d2d92dc5a7d2c6b10b5943c71d61a0bb5ec05de2466afa15c02e7740da8c5295713a923608eae3d63202fe4303d3f1a5354efe720673b1a9846f3fbfd4cbd1d2664c4703b57e483f1252547d7a1638501e19a6f233a51e6c4c01dad3825f7d46357d9c3a7b2af04a8ba25c29c57f24fe0426aab8c7062ef2afc1da91111561f3c28840c87494d1441088d8f2b82014837f1bc57b8681f0d69dff76ca2f3a03a63c8472d6fc312c4a96f2d6680913bad1fd834a30bd7205c9e88f87dffe95ac3fdefb2a34d8ef72895f78004f123b595bcc6a482337b2da51d015cd0d9265d27291866cc360b8a213eac4a71f95868f131ead3be6cc45235509485a9f64f8d20323170c7553b6b0f129927267bd719fe265ca93dee3367bb644d9d1dea2c1c4acbe0d62bbb94b4cc2d2d435dba61a2a6465b0855424bc7d4a86da0abcdeadc8094e45d3e9a3767decb53701d085c9393fd20365a242a32ebfea0c01c28595a9abbaa65e3493233ffd599bc15937bffa9c0d2511e9e94b1b912162d02855fd8af9e5356bb80a0ad39a896aa232e99e9126c2f024913d5bb1cd3d363afce34a18396d51586ab9cb2f780578f445ba6f23b40bc1293704c58baed4918660e582599f0dfa1fa61bdd51fc68f4c91fe6f500104781f60cb8eca54265e6428a17aae212690b4e16d44340358a2db15e60dab3950b6b9b76ecaea2226a651d683035269656326c2287b4de5757e6608a2ecc381b6efbee5d65de8ec50526b70f7d82333d13fce525133f03d1d43f830aae047b7952257fed9004f54b8187abebbb544a65671b40c374dafab463c6481a26c11b4269072f3283a8aa6b59062e5127d84a230f3092ca632859eb952917a72951c97e52fb22aad8ba1521ae4f0c1272b1d24c7b44a29ee574d242ff97d869d1e19fe112022614f114be6087c020edf545b2f4dcbfe42fc6de34686b26337ff6971a23d57bc9680a76821f1969229e170122778eb401a2195cf976a15fe4c07319b0afeedaeffd7ed93e70914cc5d321f22fcc0e2c48497b78400dcd398671f34f20cb75b4290e642a0e6820b00a97cda989dd657ce5aab55ada13926e63dc59c0539d8c92e6c43561196113421aa1a5250b303e0f62789be75ef4562cf0c1be326f7f9f27fc7ce6065683ea3d4c7dc88424d83925f6bb4f73bb1ea84f532889e739b4ba67384983feb3405056535f9c59ae4d72db53db8a0ee01ec2cac203560d8712d533e09be917dccafecb2ab568e7b0c919fea1a91ad73b79dafc8c7885c704aaa1e38b7d7617b86bdbc027af569f846e1ec69f567c3a24b921110078265e18da3375b6501b6616186064baf3da052c65eaabd54b3f7766abf3a96463589d3539c0b7abb85cbe7df47069e3329ae202604ddab8081864bc3d30c3b80620f4a79bfaf80aa5d5631368e70121a5063164d7287aa7c3b41e2fdf60fce88eb457fa21fdd8774d641b6451b253e9dad8c4cd374579f52f0222a2b108fc8a92fe3f65b7fe0668fe38b61091947684f136dacaa560ee17b9d84e1519ec4f6cafc19a0770e574438b2758bdeaad5b15ca2d45da420b1c9faaf92b81124e911ccf4793f1a1aeceeaa120bebf70833f50b39cdaf8250c1ceea15fcf4ac31ff38431bfb16fe4ea0b032251cc93c9ffadc25e74c234be8accebabc8913d69a650334723e8ff84a27ad643d678256ff743bdafc6e1968632654b2709514266d1f8ae5738c5b80fcc9ff2760c5e564b212020afc86c21681bf2bc76e773a56b4690ec579c48968e2b6c1237604c02e108022a0b969bf8b322bfa7490b22f683c3e2b12dedfa97758b5592c19ed5118ae954b75531c9ed9490811a967bb3c73098bd9e7245e0c1a8c229827ac5ccec06a73db538312ebb040d0ad6c7cfd17eb3d684b4f6bf1ef46ea160ad029d9e3791b000b003910b8607e8d0cda91072a4e0a430117e2c9bf590d2915f9844d76cd58010decb5ac692ff75dc31e860ce179c1eb667113d7bd835caa63fb631819cdffaf125767cf0d350a5c211d82cc49f54ea268ec1de773f1d533760da191c28aa8a16cb9861d02f8c90baef8e016f1ae30071b5972da8d1b452079b9dc1ed2da6410e30c21dbe0f4e8fc0449e4fd56486dff6d3f9f6a89bff2a4e1a22a9a4e0174df3df3f6e52f8d774a042b98f9f5816fdac0ef6b2e3944cb9e77681582a95bb42086387b001c9c862c2dbab6d08c8b9be63483efaa498f8f9b0be161949c2f1e1a7b13d328c673368a9cf68c49b7698fd26ab5a8292c2582aaf123190d66b1502aac8f9ef3c0809d8730c69fa656b88f001492e53cf7b81744c1b8e63a49cea50123b44371d435e508e8bd80bafc42d1813525a460fcd866e98db6a0ebccc5b285d245edf06a72307c16d320e7bfdbdc2ed44416b9dc49f89cd86fa8a7f72d65e006c1d28b0b77db7642de1dfa1b530ad3cbef62b385cbc1c88e1a3ba71425120dec7d8f2ec2c4930dbb92253e1b9ab0d760f70a313695eb3dabb9d4718d398a37e568d5ed6a3b95a3f0b7f4a66b55c0dc286b3f64776785c859c5c55b1537a8751d371999c895eff104e212dea745d4d7094153c5f09aaa3c235fe9ee49f36f37f2e276fa9516b16b12b23e7155ba057661137144c128c67dd86c8963ba090201fc81661be477cbf3d0892179b3012150ab27c72291ad87dd0601ebb59b934385d29634e07f582ca0fcdad424d0ca091837f22caa2454a84d0e0dcb31192a1405106221cc4704ae197357db9172add3e010cf4dec0df047555a7d6d0901b4f26c9d7dc2d1f7041730bf2ed5a2783279c0b99b6a14ac774fd77a6d2a987c3869bb8ad1fedb24b632ef85d611438a24cea1327106ed851fcbce29481e8210796bb48016afffc43ba00c004ba6b60a3935e6ae5bebfe32180702d1dd633de637fcdad74680e9b6ff3e247c8176155df7ed1e69fd5d8c21925783dec7e3f984281321d8511bff344cca0a3b039a367fb5b0d98946d82d50ea248fa5894807034229ca31aa5ded468c3b273c8d21f4f615c7280096c9c09350f1cec072de3d711ad72c1f8d3e3ffd75ad038b69e2febf86991f6011cf31c827393eb04ef38e3a3e119da0e0c75f7478d07029f339fdb478e280a1859c1ca2cd690a6db35226d63e4718d37fe4219f96c31e32c60b966f51b597737bc555eddf6bf0dac567b533fa1bcbd5aa14d2d276fa7afd0054c047e163766437418b1db874332be201508e89404499fd6b25f3a4a7e9a538641f4c94e998f2a9d96fce744913aa951956eaaafd5920012ac1f800db1246cc5b2f9c83e3dec87844d805f0e2a70f005266357a17435381e88d1e12a620b04a4009a96763e9655aa9e155ceda9b66bb231fbee17a4a64034934caab239cef3a7993aa8d24529ccb9f56c60c3289330e6409109f7fb15ad0c310c851ab8bd156b4ad88bbceaf320df96d94274821ff39e51127a276e856f7fe5a6de92413942f50d99b5ee8cb45c88d52dbb3d428f123b186a74986306e255578609ec739f033efcc962e9894f7484316fa286df560c623d1969a57108e067c8ea1faf48a01981cb07965a46f33a48fc85538cb7150196222737f80bdecadb6c156701ba06456c1ff3347236fdc2cc8f23f1f640e807f010e442c30af01cd45497f206e0837510e5f30b12432843bbb350727e405ebd30f565f7202a4722870a5ac34d02d952176179682783231c093cdf277575a4444632807d5256d36091651c528fae7b07b670c7503cfaffdd4bef14d02410d57112c3cf608e73adccd8f62468110b335ed3ef8fc825fadcaca8e914d4e41b574452d1474ea36c8919012d52d65f3bf074000f830ac3e5b1b62c4c63faf2a2068682d4ff242e100ce1b26379ae1622d806c314091b8f4d5550d2e4665905e841bcec4131a0041857c5a319944b49f5683e08bd4cf6958068de99b6d7d64e2fcb43a11a2d2dafa4daf4db1b84a18fbec67c745b672a046d071f20520a27f80400dd7cbef73d78c871444cb2b28545bb7bfba9c302d1b3532deb0eebeaf670828ced338961255dc126289ed2d5b971c5d5bba1a1965e4a0ab8c702848b72447a4cc4fbcaa3a72f81d0b4bf26e80d558abbc04c5dc9fd1c7afe5674daf8725ec7ca798c4b8eb1ba1daeba4eab327f0aaf1427a9842d93b1b97aef12a9e7c1d4b02c5b43254a296c095b3eccb72c13ce3a63e25908296dc0f218cc2496003c7d3c8464459964c86ac5a07d7a3211a3cf78677dd0fe9f2d1640bbd15b921715bef20495f70ec9c923113b44d371c71c5fb10daea8c43b3569de896df111a79311372df3766924338203a1b296c85a20f12487cba596298f42c5aa966251161ea9752aa726663023751b01688e19fe9890ec615498e5ad8c8ec52936ec5127ceea00ad14be37992dd259bd2ad497e1015a9d8cbaddb97280e5198b7446a1a4ec41ce218d069ad46ad3a4ed82659ebb0cfc903afd3ef0a44e07ad1c5e37d1d1d9e020f2617510ad91fdf9e233c1601c68b25ceb1c205835d63a7ae2b855350f6304b95c5b5797182d9a9bb5b9f7a3d5bbcce0a183f2e15e783051b91f2bf5a2dbd0ab53f6df86d2988586c33deef07d4249d3c06a38c7f8af02e1317c933f1c1ec319293c22f14e497fb4e53361709213f9cbd6bba65254c934822a713c7ca34f34d2bc03a500546e42b28f6209d7800bd4d6f943e1da184a3a1c78c203f3be02bc4f48b49fa04b52846d66f009d6e60793340a4163c13a151f7525843ac4288bc4e8863296f5c667325cbceedeeac4ffb80d030f49ba7c5d4549edb78ac614e45e2129171b0e8f38f4d1cf4d3b57b53edc587bd40a42738f64f559bd158116604fc01428a7f054bcba1cafa12a0e79d648785f168d051787e32fbf22513db92ae15be65bd10cde664bb375307e6edeba12795acbd752da68f758065a7cb938511c47e9d7e028cc3a3f55c820abc95e12950e97eba8b41a3416e6437d0c7c35cd60f52712e788842f4866159596f81b10f89cba085bb7d006bcb71e23306753c0e47fa01f1e561e00a5bd14f82e2b2baf3403df749b8531f61470f4f432825d96c3598670db12828a2d5db695c63a47f420a04a07690ff8a9257687320b6b13ded6b07196a375f7d835b856ac26a7ad3dc71504767db882528838677c07615da721ee9ab45e953f698bf2c52c1d2c479948f9b1591048738d563f6d1593d314a62c36db348eba39007ae91b39ded8c84bd168a2a5d15f7857e9c734fb76dd52e07a8cfd59317bcb2af0c52bf2818a6561d189a04afe1e145ffdc06c6299d89b56cfbec614372d494ef8ab095205c09a7460f17c48495242d18afaa5b0d06e14033fb78594c051378a7d12ec02ca260cbc60cc2b05e4e514c9e8f52f8aef6bf452015ba48eaede0b466b58a0b939105ff58fc325a33a07ee7d0706fdd022b6156c0d58447755632610f33b3319209b65427118fb2b29f96967fce2abe4d0a086a5caf1d113cf4139f74abeec457764413280c66f4e9c60753f6e75ff6c9b15c5023753e73360ff4c3a59898b2109be4e88005089e814a603b0df99cfa47488425592ca5657170834d98cadb8c262e48129aac8fbb4c062564da8f1f34cb86477d756c2ece19c2309e96540b9252f7ec95e6821138d2858816dbf4f922764ff8827dbb9e5d6080b83ab0a09e282557e6db43b0ee9d4b8258dff8e205e720dcf42e848ba4f007b474429ff118aff83d66705287522675ae42e3f1419c47904e168ea4394e906015d582c86ce854f326e4bc0eae4003c5f7f5e54a7fbcbb3a06e080f5a310cd75ccca16eb1512e7335722e217a4b106a986ef60323a757e75c816b4f31ab1a4e2af356b719c0be72c930c9cea52a409b018eedca933f3d85888e71860e0416e910580e72d18e39a26531c4ee03c864b19df1da41f88a6bcb45ca6d82ca3c393a0edc7de9d0000d7ba82f6b6bcea2d930d89eb574bb5a49211597ed75dd297ae5b9dd60df90365b3ac95a21d50631367b603f3d43e661ce70e6b179afec0d9db337e10b5b9dfd2892b1230750f86a651a7113aed7d5ac02e1fe459345c2cc74d4bfafef3322564fe51133862b573f8ac91162ed8806bd79f9eb9557d8db5f41195f522f5ad119b08a809db818506c152eedcb8e0b0867016a9d407f8e44ed80fcb34303ec674743e1672706d567c78694cff382f7105f0b426f8d0954f5a19eaa8fb58c6188eb082ba57f7ca14e089c02a1c8564e40e7d17a0e0de61cbccee634cb87209879e60c69149a981e8aff1d6a133d240592cb34e520b78118a419a21da26c45db52729848457b094dff8c0fc469761fdf6cc1e9033afb6b45600622a098d727bc9f8b049d2bfac2baa5de7e9e1feb25f4dd434c6938b7b10b13fa4e9ddb42ba973ca142e0b0ae9f1799740f376b042764e96856106bbd02c88cf89af086e175dacc3a49cec6c95b1e53d94f2f6314b26dba2ab6baabf6d682086da7ab69001cc7f82f2dcf169e3e8e2b90c28b40f44cdd7b9e9af68d7b4cfdaa08950b51795cd969434eb24a6ba33fa3696da01631214aacf2edc8587174a9d424fac8d012965a16a954f31ada5161449c153a8f127c4ae59fb2b94ead26fef6a66a9122b22f1c863e6296667e34d33c4ef1022ce34e054ca9cd00a423d95911afe57b01848a1287eecdb0766b32e695ef765940ad7ed6358bf7a279beae4e0dd155ad9a31009219dcc56001f677686ca934f518d04a2b7735f69ea13d22eeab33789b17ce940476bb047afacd8e9ca31ad30c4ded917912860cca9eaa8442632fa29e3c7248135d43efa62b3602e0b8be6478e01c9a64536dc19acaaa1b4a19bfd0c3875e2cecd4dcc5b7c7c60e71e11b34c2582ac7f51387d02ebe27751819a2156f48925dd4bd7fc5d7446812cf849a3d6c72df7fd6c3457f4d95ae456d4915b9ed9c787a280df9b1a5aa7d8d72a7665e2ec6d3262dc40976ff9adca0c01c33ee03be17377e8027491f54e4f1a76d1be54771056443e40b2829818267d1189f854375ea4ace01645bbf656a454589bab0c009f7f3e4c6a6f0d30d4e3c32b1eda056564822419fe4613a946840e70c4b323fea4f1a023fccf925207810acf6dddb538e15c7cdf66490b0440c198494fcd2c31aa23f8a889c0190c0b3f233369d720d24a13564803e6778cf010549bbe64c55af8af681e6ad7ebebea7506dd418bc0f5181abeebf08cce0b707d129919993f0d11d26b0a1eebec7edafc4169f9260941c995fb5e4f5c38bc5d48590d5b49507708c34f02f554189647655bc49f53adb3617dc355526a35bf1da04f1a457e8da2e5310d98c20cde6fa03b880a263630f2d3ce5dc57dccc2ab0108815f0b6cae2f4fcad3b062ed18bff3cf0172764e6c784fb707f7e4107f77b40af63cd1eb875ed255bc9e46f2cee1b6c64f33809b1efaa4d7e63b22f01d078a3c2f7f120de7e1564c22962cb0b697392c7bf0e06fc5bbfbaa4bc4d7e693b9a2d34955e9306c1167dcf9aa665338e88103d8385462fb0a6fb4605813a4bac8c09f1a8e0ae02df114a7eeed067c91b98a39dde01e455bee3e555b3a806df3688a79c41c18ee5af6baa0b618413e39049bb187df22e325af4e63a90152c01cb230dfec0a1708ca06a50cdeadbb5975a8a6ad910823d19e8a0f7bfb575a82ebb902b72a6532c4a50c5a2e0029e8cb8874c3601f7dd56d1bf8f3ef10cca1934ea511c962da3d34dfc6694a1aea41d8fad33ec0ebf79c6d63ca422b99d19c0354218fb8395ef27e9e0e140804ffb2ea063440cc4b4b8a85b676118f8f43d938c596b900d850175b16ef88128baa42f73819a42cb9d8e83fe949874a877c42dba057b3a25e7ab8cde89304e406c5048de87422f035cf826398c823cb18ddcaf9dfb85dca5d6396dec66c6408782b779f0a64dc02cc550d5d5d9d026978a9b716c95337486c7f4dea2cc7182841a2dee157026192a00386082eef03abd90c0825c7a7932a9286e8e1678d1b063390a0adfc2dbf4e8f3b4d70d5aadedb63a9a613ee770e66bc4f67f13a934354d2708fc3b8798eee3c4373911c03b285b20512c31f3d419ef7172fba6bd7c768dd8383edd7d3b3dc43e8ccc1bae058306d745eefa2bf69cc2176137af14b5b4d5f061f2c2f62852e8e93769995895165636f2f722ba1f71fe502dab212c8edde39420250c9bdbb5443c3158965e7ebe3cf7580160c00439d9a830a2e1d67f39a6a42b7854d8e84d5dcdc4d279c5929f6ee7544a13ab33ffe6c2ac0d0cd352f2fa86e4037b992d3d58f07692c3c10b4c29035221f07bd7ca6572c05dee1236c781289d2938a912b43f6f6d5be3bec92f6d6a1bcab87ad3565d5fc7e680edc6a82b5cfae1b99b3fe6575bac9f25e8e7866e77cf01d69c4bd77f5c2fd7216165f7653f98c724e414ae426751a98bb48625631835a8301f979661921b69748e8cae68a428150abd40b056003863d094423e15024b899e1928653c7a5546372b5206114affeda26fa77ae2c1e15adcb0c71339ae4687aef44df6451f67a1a0de3c88880ef24888a9c800848c4f8b25130fbab756472cbf479bb65190c5cc25243f0e301a2c860d3edbdd94f0c08ae10e00a8120baaefda936092622e06252112b8c69e953cfd55671ab4b64c2bb2a9de49e391aabf0f39607efa908ccf46ee812794948f1e4412d83a5b73218ab82d21c75a9e1f2c4347ace7e6a1fa0ca8ecea6c8858d5a8f73e3bc2ed93127189f2b791c41d0ddcd6574102d4077bd5030804bf7cd9ae5e4fc8943f980c3f3567d14ed7ca1b64c1282bc378f21610a4644ecefae6014e0680b2fc030c37489e3282ea92e38d678c229d00dc1fb604b0b1e7d66e37b7d120efec03298bd636611b6a537a902a7ede8a476689c886d619eda3d2975b934cb73442791251fa608d688eb7f72d4b38fc2d9129411a20f519c8ff9a31b39b20e42892f2ac7e9c560213745a1d1e430da85490df21661e6a4ecbeda3c8aee19068c4d40b56d5180465cdb6a09372d24ac927d5d5d8e54301c650035e4a1b967e114b63e87d51b107049eb8e39dd53ece45df43b2de07e66a676786fbe61c2038d2fbfd29e3ff7532898200ac2eaec1279e2e5b570aa9fafed8cf1f8884fa1752e06bd90f016c2cfaa0c59f4471e9786758a0192492004e279630c71e747913e1eb1f8878e014fc2bb372b31bb4d57eb2548bd49077a98eeb386eb731d130bd93fc4bc5987369b2ea977c821bcf4a3a842d62c634fac5b972903140f738304504db08c3ae63a3b5f896c125c6e2184f4c6beaf32dd6378370d86950d40fc7dd7ce70c9dac8f020b207299ea000ce96a140be61c30c222b330a08307aee9a8b52be06964b0e8bba7db255b02073809530b3ce794ca81bb5a12f06bcf0a86d703a0f31576982164c6195e3ef7d3877c849d0200c0144167471a961fcd026a0d2f452d658652b278ed075c9328d323580ba8450786f5de46e61553bbf2744639805c0a12956242d6cc6e8193aebbae909c606ce56ae95113d0bad62f91a4d8a3b153a97df6ad45c3651c8c0014cf7680e9e3e32776fe7691ae5cc5891391f087d96f461405b1a00c29ed35d4b4f4f69ab860005dcc30da6a232aebd49650bdfc30a0819daf4f29eaa628183a75531c9ef2d5d04ca40cf523fad41adae707b0cf2d31e1c9dfd5cec2032d13dbabd825847c0ed523f2159a30761bf0a6e6aff37f9e92679e81ef2ed92bc2e98e0acf8bcf854989f2eefdeacde24c3cb7aad51898dd128c5390fbfd18d3e9dc8ba045cfed9a4760334aa7189be034a5f68c8a44676299ab651afb16b82c5722f7dba422779f5ab136451985013426cac99b97c706417b9aa12541a6081ff82da21835fe5b3e36f2c3bcf950140705e4b45b4e27dd7b400d9407d8ea85229cdca8f5a3d62817ff7845c3e2567f1433f775af25fdb61b13ee59cf7a4f91cdab714789f2269357f4a6e05698c40633168fed1e67e04566dce7a8b542652b23844894072c694a8ec8714bdd1c7fcbf87c557cc82bbab75ba539677688c8931e8c3d9b6b28c623fe80f4b2736168fd1c7837e8b188eb190cad146e1a9f31ece216865251a7ee3c5103596ae60062f3263ef2960f83544df2541e1b240a7ce37ef79feabcbbac5b2ef9928a96f5c8728edf091b2916842025956580118f9433234ed8e47c9d7f06f88f577c23a4b57d05bf4d6619d12ecd1ee50842fa3001722c4fd7014620963d33ed2afce9c0037ea6ae2285326a41d36d576d0da686dfedb440f0baa59bee77e68253a89394bb5ceb0fc2c0ce9e6d748f86c68aa2573a2fa0b9a1aed482f91d704c543d2469522671354a5d47454971c9f04317971c521b29e5503af09bcf3e68c4e6894b2d3ea4c695e7536cb18d9ff74ef6139366ecb0fc2e07056ffdf316c112f921419bcb5aeb3907b6ef887614244cb53653911454e83d17a982b5e57a511ebe86810dad389d8cb42c56dc6e098105455d7fcd528d047688f6cc88b0b9d151285b0fd75e274553101c05cc828ce2698944b5d7daa851d0c5c326c4920f90c7ba623ae58fa8bcecd44ad39082c81e8b957596c4df198e277086fc7cd37df8d8530618947b085595e9186fd407024fee4d06b73365e40fd18d46fe76b630241c4c7325b0fceff7fb09fdb90e624c803c0b95b0825e12f172f48e03b81c20becf9b28c07d22cc050f7fcaf377f8a7899dc7c126e4a96311027cec45b5ec9363414a53c71e34e9f09a0c18bd3c93a221afb1a51a993bf804180c04d340dc17c0b97b1ced318d1c88c74cbe4f8a2f610c9b55477da3c945f312a35230acf42f7c245c861302c2e94945f31ee21706c27fb457ba243cf5389dbc338307931b370849ba9a85aadc7d8c5c19038a6b667e5d3012bad21c8a66ec0b81e119bc54f957e3e5351d340db2b7d4d8f32b4cdabba71112624ad4a3db47c2db73a41b227bb7904b10711d1707c78dd7f53e6960661bcc3f8aedfc9a0ca8bb29b0c91acbb569f3a7595a2f2d2dd9524a99920cfb078e07b4073f56cc9ffaeebd7f5f317fbef76f1bcc1ff0fd1b8bf9b37afff6327f6adebf6f307f6cdebf71307f58efdf39983f37ef1e88a550f6e03b2c6980e806543920efbf10e87b0f087ca0d5d78440350f64f3ac108885816e643c0867be43d54d3867ae93d55f59fd21f53499d6a47d52300e58a669665454d49ab4580706cbfcf6a5167b61cef2d6e584410488c203114b04a48c179b37711ecd3b824b2da659719e7bbfe9778b251559fdfe5a960ae4d09e7e0e2a9655ac537c745ff291e94b82abd8b3261fbd9b82b82c56c0a468727142caf8ed09dc7dd9495a3e2236daa13771d80b94827a7682274d64cecb088376964819efad60f812ad99f1fedeb4f325f67cc0a4eb3c095fe2fa3a7fc33d9d9045df849f240921095fc22740e74d106998f13a5f8248c3921e335ee769d879127e925e6a864df8127e4baa91b5186df997309b3f5b7613c45942e84cb405dbf919afc5684f09efdbca134b5c42d8c41de6535c36231c610398457fe785481615817accf81d31688638690b0193dc29ff27a9f3e5ce0c7109fd4992104e724638495a9fbe50d6e2e32467cc101db6610c6beaa7ddce44430438a9c97c74ad498bf9586ab13c448238694b472c3dc6da318139cbcee4a3f370584c14b87458d84e10a122075246d89282dc9270e9b0aec1657f2fa26b40cd1fb267dcf83a60c364091f7ac04111224fa60d88d012648a0fb88089974eea21baa79b282d227ba7a0bb895837e919fe5cd84c4b301f957ccc09fba5e5888b94117ed003e93588c3738b5aac677827f9e84ebad5569a8041764dcac7f42d8d10e6a02dff56c8438b316c010021034280fb4508430861481de184338010b66b08ee17c33054e980fbdf3f46d82ffc04f76b31e7b1a2c3fcbf12e0f7d7669a152da635c1b0f741b84921222489f498c33e08b71f984849223de6ef4dfe0e7326cd85d08dcfa002850a578652252937142e5f727f0fa592e0896fc033ef498159f427b9a2ad26b8518081e0b2edbb0883bbbfe1948a1a4729a594a67e4bcdba0804b86c97ebc79d1d955bf12c2ad19d1ce64e303ad3e84c869a3929b4e419cbf38914af3cc5519e4fa63022e6f9448aa8dcae1e527d7f0b711efa4d8b52b8591367cee3e4b515da43f3ad825289656b3172830497b38b8eed55623bf9d8331ffb35968efbaab7797a8e8fa30fcd1f11cc1fefe9186db517e7f1be595a97a9d3efd9bf67db86f16ad53deb2ef3c7734f524b6e281c2b25b88c0186cc892ea452a50facda365518c30c1326cc1659f55b58d264aa1265a8198b250cd905edb71286bc893e6a8e91d144fcd37954aa52fb944c586231466ca294d8aeb0878860167d8db5fddcc41d1a4876b05872c51256a27a88820208061d00034411251ed081013e36907039b3943eb9e95f25b8a3dc3b0ae61d05eb281ffb5d445ccfccf7b39ec0e50b36721bf1b167c266f2563fa789b0a82f378418992139cadd4ab969d497fbdd2ea216d2433e76ff0bdcd5ef44515fa8f831c19ebba8bf8d8854ce32c29b38a547650084004218c303560d0c17333231a0c7f508d2debe1029448e22e088ffc18dcdea058d0aa7beeeafc8ea717fbb2f44cef746008c00b051332644084308b71de07e9c108310aeb06031fc301c9d70f931429f25f007e10d0fc21c56f04dc88315b6b005db84356104668057210cb4d50f239c010a2e6754ee7f11b68bb65a0aa6e99fe94f2981cb2eea176df5cb849d4405c661c362c266b25ae0b261293005f616a526ca86348b8150e0b26379465bfd5cd8315c70d9336dd3b27bcc161831a4986f89a1c2ddbddf95ba86d31e579aadfe6d23dac2955cc9979c67e629ce63c579e693245d723f916b517299cfdc29776fc9fd953a91f394ee2a729eb2b770f990f3dca63285a8b7f0251f1be64a3ef610465c09ca7184aadf9c60b5aaf9e6b072b9b25f2b3ac8fe32b62889edca0dd91f5b570c341eb27f4c7db95c2e233164f025c9fea9caa54341f60771d0b0f090c8fe9f1697cbe503b744f6f72810ed4a97ecdf45b95cae20ac21d586eccf6971b95c436e8a6843b2ffe603e7830c92ecaf79cce5721119a23341f6bf3097cbf5c3922a4af6b73a44d9b464ff3a2409d08aec4f8b5858382640e0726d89c921fbf7f7c26a88f87de44d142c4ad93fd615cf46cd5e4cb83c6045d3c5df6c4c62e35c5aa4dca0c805419e62308058acca8d8dd2e10aa74f06348ac8175ae4489d1581157171e95cbcfc95022958785fac0a25b12aa63c6dc1dda2a8572ec729580044eb5c0e7392cb514b0d5586549dd951941ab2b045515c4e516a502aa27db024a25a042529881b830204122849453ec8d459510c62332b86e89c60591a692ca96a626394cda390502d2dd06452442193729404a872bda80bca6c051606854a1328b3242c1c8c588d4282c196987a532bfd1bef8931c3d9b1912329f7e7b157141a2fd88b9ed295d2642c72e77edc3e56f19e9a9e92788ac19c519e64f0c4a5a33c61519fb6d080d421532bd2323e74434cd5526f51c0242687c53d26cf2856b6f8778d8957592c5779fecf58cc141c50cad3294f199195ecc55817343f73f838b707bb4dd3340d86acfdc8e0d7f9636aa61d175ae0550b949464444db995a78ce803d9f3fc6b1087ccb758dac8dd6fbbfb2d39a70e938d89a182bbefbd57a5badd54262c5f70114edab24f136e45e89cb95c244b1379748cb6eca7a2c0404a918b4c3df8f63f20989cc0e988082d92d881100980f4e0054989206400454a136d3e0002c750c1f7634296ccc77ce71ab264be3e7e6fe7be37354ff5f6739c677bcf7b954ad5e9f02d42592bb5f7669e0a65cd87e7199116e919dedfa00d53fad1790245b31911d7ce0c70b95add3c0e8bc7f633f770a28dbcc187dc9e682339cefb2decfe06a519066efb0b701f004eac5abc6bb8773b45dade9b9c165c6e194a0898ec4f43c11eee22376fa28ddc7ece1fa4ed4bfadafc09a6309f9be50cb9de7b5df0e1b98adbcff40ddfe6f53a445614955bc60496b3ace89509e0cdc9532bf55cc3bf9c734ea0f6e1f3eba473ce0aead0d1b13c29909d0a7e60f53c20ef0bbd756cdaf785a048815c6bc83a40eac16f21057e9b87e910487a5e0af6b5b2ee08409d735e70051aa67ee1922da767f86f604e4ed7c0195bdf8e368229dec45962dfa3df174ed2ced166c0e6fad910a8c77b5707d7b375df7ee2bd329f0d590700fffb1640f1fb16e68f50038e617ee17e4265215288f4f7cadebe70e8fb495b5d6b073886f925fba07996a0fd56bb30dff247a65f430bee08404f30c58932dcdabedd5b3f2f5ca25d5b29b52ff0bc7089a6bdb7a3766215f10ebac30225a7639b59286f5ed9dd7be190a7fd9c2594f66f47c11484b20571689c66c115689830d90b97749bbb73de57c192f39e5c4d61dbc40e1eb7e47e0d81eedbec3d9a137bb6ac3f97bd05a22531f86417c62c4472efc5d061f210f7372c89c127fba0255cdad94eb2ee98c9da9c6e539bb842ad1a0a4b64f0ac6995fa8f16d2bc1a511a43dfb355878ba5509ef52bb8a3ea787f176cd879feccea3cd87b2f8eb59abd2d7e3dc3b7b7360492c173376705d7cc59c1fe76fb92b3dbbd7f2d0e23a87f4582c91724524094ad6fbe89738e2c04d9aca36edb3b9036450bb240c38eeaaebd8f2d851536eddaa7e00a56ac9b8f5ad3204dbce93c544775b97a8ed68259a1c300ddf7b1e59e3af6db817454a0eb6395ad7feba8323875fb9656cf53c6446976ce0d155a7077aab4dca66c4851f67fbff1a62ad869723191ac25fa08081545903f3a0726a01c216a1ca8d0402c6a072c2f2aa467b17c808ad9bd028a115a484ffb6818e44a4f45e840fef89e000184a4ef3f66ac9054ac2e807e6ffb68e84c440029223d24a10be9713fb860c98bf4100709d2eb3e1a3610319845793650f264f47d349c4e36d04411e921ab09d2033f1afa912aae48223dc46182f4521f0dbb0812a49817f37938697044d230a8ab12841222c81f1d86f40e40bf873f1a4e2b504610457af839417a324970f9a2fa6838a1d860035a480f271727a43743b3e33444d30f6f1af326eda9d4b3833162690828928620900038451123690d83b422124841fe00395067af17f9c346112f4890f56918349bc81f330a59c525ac7ea1dc6e0511f963860ea458726b1834a348d242fed8d980510abc8a2ba820022369f85d417ada57c3a0ae018827f2077591d305507edabdcd4daf8bd31144240d3d2aa4879d7fb850bb179167dd4262da53c95947ead4a97b9dd4e7d439b2b6d2b7d6d2b754dc5cacd7524ac559eb9734557451e545bb76e254bac5f6943e67ffde3b7f7cf3717bca3d0deddbdf7eb3bfd97761b3e13ca58f0f0b61b6e28ae26d85ffb3b6b5a669da9d5387be75c7deb66de5c6820b9bddac77adbd7765e3dacdf1bd9b063077ef739c887dbcdbb64deb3570dbb6d19e95b7aeca0beed5f6ad6e9c67ebc4cb6d9b666f374513152848acc09b0aa8ef53b27f97257fbe3f7f3a71ebbe7bef5d2c71807c34176af43e7deb02a591edec922dc842687fd28dc67c1923d3982f61641af3654ca64f27cf7d8a1f3f7d6d52b1e438ee2d4743eca3a5e23775ece316e0fa9d4b1a997ef9d91bf387befd084860fed4b7aa4ffd7d964adc3166d5abc41c1fc19779fb36debe4cb8f9083e783b46be5fc6c8cd6d3404ea8c43a0cee55733287e9e7b9be7d6b2505d70df3a80adb55fce184ff97dce13d6497bd294bb7bd775e2e6a3a6559aa76dda73c35bf6adca8b2a0a7ff86f9c30c5b266197abaa74aa17cc55483548234a9f56bad150730f9731e190e5e32fdad0031d645ec3c4eddada8a36617bac8ca82c88220c95e4f2a96cb9dbc5d9c430fc7e7ee6bde31fa48c5af829b0c31c4bc87e5cecc3b3672f460c00b330c1521495a8a79f65438b4c4f31dddbb588af93e48e3650ae26c8170ccac3d8ecee027ce39a3dce13b68c8f7739cc75bc17774e2e8635fdf76b4ffb57785512c6f725631c0258ee7886329c3559a0bc05316444876afb5863976565007deb6af200a9c1843e7943075686deaf56f4863ee4c1dfa2a2b70b913c589604c420a21aaa89cf3396250ce08a30838e2cb10ff6588413f6a502922462eb1ffc1cb781962908c160042d0f9181f430c02e17544568f18e1c7f8ee4fcd3bbbc9b375dfef93e77e7b1d02288278b196888102bf306719646102dccf4ae9d7eca55e46cc09593d72fc75831cc4c89c17226584ac1e325ee775c4a0108a2065bc10e9d9c8c41ce1982db87fbecc3bcc79c0aff99af7d935bc4f2cfbc1f7c2ad67d055cd5310057005ae9e867eb4aaf95953c3fa1bfe35db7c8be5a7a4e463ddbaaeb17aea47d993ba46f9fdd6336abe06fcea634df670d49442ae7414d6ad67f4afc2ae67d0ecb1de2664dd848e247423f724e751729e49c37e4f7ce1db31ab7c50e052b322fbfbabf3d635e8b71bf9e8af46e2423fe972e2ca2dfa115512d8c8794a3fa24df1a297398f0bd19a9ce748d798d9bbfd5d87bbfa8bca065e7615d9dfcb1768b28bffe23caaf76f30ce43f3feee729e9987c1f3e2fd7bd655cc566c461916eae35edec5a17a4a5393a5b689433994433994435926876aa71ab067312f1932747440143ea7cfca8c8498d7fc01bfd9e6b465d9b6c862166ab531753a168a0b3ba95f1bd306357f9a784fcc91b7fcb364d9b26565c598d70a0688d4b01b1280747659919d7b7d33cf2361c60ed7428888ba881a0db5ab4bf84cb02f4400ca070c413ece604200be9b9585eac163c6801b10a905564f1d6b59267c1d0d0b65eb37ebc21d4dee75c28d9857cc2ce6c87b3c2fdceb9b71af6fc6bdbe19f7fa66dc8be545bd2e0b44e18adec5a31cea9bcd3e27efe18eb8970adc2b47130d10856a0560ebd664936d532c9485b250166a63e28eb824142cb521804f00200a570096a75b71fe35b45eec170b268518d98a360a2aefd48048e3c8e33d3e34b8c76700dfe6f37147dccb4271afec6fa13ea76ff6cdbc01605701448005cbaaa394d27a350d0b495d881ba8d5aea6615162edaa4a21436badb552eaf7de4a2be782d95aa9922e499e94d05a6bad945aab5d4db3f689d6ef45439f94c028add6d65a6ba5f4d65b5fd495e0c05e4dc3422ba548b7b15015203d3d3d3d3d3dd97a9f2ec5922b45aa97e36a05539fae7691923cd92f97d2aa21fdbd286c9a76933c49c1b2022c9ee4a9933ccd244ffe510f582c1d12dcbf715db8ae4bd7811f7c30eaa13bd2f1d019c1fddd0e345d91171d91aea8d3a11bd2e5d009e982e0fe0e871894c6f8c01e6db4677ea52d6f97c0aa73f2d1a995ecf9e616e96cea7cf1d12998fc25fb6dca9489f6c45090cb9af97446da7692c272a9b9e81724cdd5c1aa70ed395541814b0acb76eb582c16a3949b5514c870b3dd2bc64b3058d47d592fdec30d5d187d5d187d5d187d5d187d5d187d5d187d71ae0b7b71306b446cfbb6afd65a2bc55b752b57c95b494649d99f1a5124de7399eed25d32daa08c6252bc89537eb0175eb29489c2e875516a2497379373a2dc8c7245ce13e38c78cf77c4cd6e8c9bdd1837bb316e7663dc2cfb5bf17b7133da18c6157db31a30f539bfbbbbbb67759a2a5dc8aec5c7220b758d6c17efb1168c97210b46c8509ed788903c2f926b841ad9a8d7ac52d2d72adba89925af0a05d6bd7039669f77bcfec57992e60f18efb943143652d83852d83856f1ba60428a6eb29729905c72b7c8954b5a5494dd6a71114451d105b8ef1590a69706b9acc133cc9e1e6a2175a83615fb8b37714a6e7a1d15827f12e592265d99b7da07fc6df68bf368ef7f93e60f18efe18c78cb3f4b962d5bba74f172633796fd35912bba4cb7c95da249d9bfbc4bd99f265125efb9b21bbbb12a988b21e14d9c92525a8b7c8bf7b883e94287bc74c9e58329ddcb043373915bf4a820fe6edfb31282be3ae0dd7477cfd9b3678ff9d69b4c1fd31eeee7ca3e87b48db445c3c7460a3132153b103081635403de375984172e71bce7c65bbe126295032b4f225a9337dab342c2b40504f7cff7aef642dbbef9238e8f50b869f2ec6ec0fddd0670ffec86707f6703eeef3490faba1a707fe7c2fd5c18dccf6da1e2b4c0fdf3277355300706f77359e07e6e07b89fd341cdc77db1f9b81cb03e0e07b89fbb01ee9f3f3779bee7c1cd7bc582f613e0de107bcbdf6b676a55d3b4726127a0f9bca9a3042e57de4d17cbf5fb67562326539724285916a6d89499152d5ba2606192949692609306d38733379ad58f393b3e4c4d9c8929fb3331d5adce14c0e64f98bde932da973689dbde6631d2383105cf9cb56297a6d824efe9204516d6c4c66c93f3ccac93f34c991121d9df6af11e27fdb94d7b394f69ad3042628f9cc74bd043e0293382246f6f13e6ca3475fce726b3f2f14b8de666f3d1ed3401cb50d6b5e13c3439473dcd81d7cd0e29e81779ca764092fd6a54c9797c05d9dfa394527adb2679cbdf7649d9ffebe9be71f879cb351d1a7625644c90e49a3c6542c0eafd1a3651cfb82c1ee08de374b58a3dbeefc42056fdf1fb205615811030c9a7adbbf2a1adfb42a4f7db23e0fe26066de5cac79293c4f55ba0ad5b4520044c92c77d2172041729743b91d5e363f19079568f263b71c90490b6474afd245955e431d43dc80314839aecde7ba16b5dd6f6c42032e23cb3051ef072bfdc47f95edb9526c1ae12f9520167ad32e2d00c4d729e29638292ef5325e789c1b2de17bac097a99f7ad57b5ce89af96de637911ad1d6fd99ed9bdc84c82f14226bd8233565467c51894bec8321ab87cc832f23d257029ae4be49ee3385813fbf97f904a41e1469003ff532220d4b7a809f12599586d47fdf4f5226ac61047cbcf7372770d9ae54e802ff868362bbbcefbe1722633ed6628feffbf7fe5412ab6120f8df778b3dbe366a22dabaa038690b6c176d5d4dc4f53db1e3aa8bc6dcbd2842fff6b736b2a537684ff7f679506b6753b6960b82bf075ffbedb9f74416ed4416a51f647b782dd0967def8548fff6c22e9cb1a6f72971a42ddb89b6b3ffd40b2765f5f8defb4ff4c48dd543ec4a9f39edea2dcc02e169ef4d55d6bef4dc33e6e47c34dad23891c38175b7ac3d177483b6342edc615d1fd6fdfb9b18b4a4db444c5bda078d9a86a78e7d99a9f3330a03cad9ab558d66e55ebe1965c4a1c1df97cbcf264f199392bcca5346a42966858603ba40e001f367042816ad248a4a08b0a036682f8d944b9f19832b666c06a36f70ad6d2db304a659ddd8a0e1e3014cf3941181d2ef3420f224c772bcb59a37b827d4ce118d43f67f27d97fc3ab1f02df3c65445490cb7116f5d43f372201b9ba108e8f951e510e805aeb737a980c444517ad13de9cb557f7eaaec2d8d34516dcdffa71fed479c4003ccff9d353a77bbc5bf3fbc5e611dbc6a654e52ebbdcf34b1172bb3b9db32cbde3a3fb78cbbffa84377cf4faa54ffde6a9a2d32a96e10d1f1b5b077c401d0cc8f47d6cdc68eae33d4dd2f0d13fc0824b9fec3e369c877ed94f9bbe8b3eb33557322098f8d8a4f9e16d1b77c6189a0a5acd474e7fb8c54ddbb452285b91a5bd50d6f00af0f735f41e0cbb2ad284a07804d3ead35dab623665aecd1372c8844092e994d939411628a6e0198c7918bc674645055104c6542a4b82caf44e77ede70c4f50c147fb79ddd3a736bf19b91ee36306187cc426c0e5967f3a885980b7916e38e54d0c0c3ebc454b9f0f09ab10239e00854171de0ba210011fe97b3954104f78fb7809cb1af042962d3233305663eae9fb9821c8795e3c4de11c2a991918ab91f6b4eb8311ce3c43c7940a583c214b0e1971fcd4e3255ccea85478820a3e4e197e2af3aa9fd9688bbaf8172fc21a4e1f277d06380fcdd31cf3a91c1a2ca39a99212a26a4386627258ee574026badb3897e13d18781fe0cf4a74dc8ea61f34bfc6d5e8864852c1e355f23063559f342a48f19c29ab05db445df266ca222a32cc2e0e3128c3727f07ce12e9c48ed80b5bfef69e30d59f377682b644d31859a574b3a27b0774de0fa134c219ccf52a1a775789c275380e9b96f43886aca6e90657f1eb64e2053b22b413494fd697c4e1d340c985c75d8aabdf46a3b3ed96768086c32a2ce28bc705a68d756da2b26be3bc4003570028bacca0e2da001066040e440041a988204125415fe040a3b45f6ffdc89152b03db8155135988891055a44c4992c3152eb21c9db893237ee4c08503469c804a530894586dafcae41df1c4a3495ac0d9a0b4d1f071177112362c91fdbf95e747ec7849e2a8091d38500325bed8b00445033c9cc045963b3fb0207bf61f64d9bf3c653f94e087a4ee89c9d5ca756f9a58e22bdad087cdf36fb8510e9c524aa9572aa5e99ce2f412cf57d988d9bfa7ebe662e830b6767b7b7b23ddf7dea9513c201c1308c7e3a0d9d66a6bcde95208e97615cb0de38d9b3afd339cd99dba53778e9bfd33776898f9d569adb4d25aedd7adb97c3218402c56e573c6af070f159cd028d90f45b97f4e81752664094204566421d301291404074d52dc100229432090197dc9536614cb25e42933d221c7d4f76e0ded1b674d04f281b346c5ee426ecee8d744af3b18e02a941dc78bcc8026b8c4b9968de78ffd179a58dedfb654f7ee6127964240bde55bbf6e76cb2c1d4eb7f933b7a9d3ee7ac28452a1ae95a987e6cf14b8dce2868286390ea80d95492f817323878ffd5a1461390f18d7a00aae5c643d389163377aa882038c1b394ec841450f52fc0338264f590f2ae881881c685c0084113d1ce92b30ce53d6c30d38c030ace82a300805ade188129e04fef2941d794287602f4fd91126208e28c9e58cad64475e413a9c28e203c1aa3c654786f8103c93050eb8060a17de98702f98260a069904c1a0527de11779ca783892cb15dd01bbc853c6430db9af60304f99112f3976420e15563f7a5c5633af8a50911a58398ab2258a23b2f0a102c5061b84a882042a7685b3974ead7e610b9660594c896aa24357e1c2c20b182194a4c4b6408107ce02cd48135ee72933d224c7e42933a244a679ca8c4ca13e9bd239e79c5303b5a9134ecc39e746bbdaeaa2b53ed5a76aeb537db2b73ed5a7fba46db32a55aa54a952a5bbab501078a5b1b363c3c7c729c5f9cf196d6da7d5ca66cec9d223ae37d7e8ad76ce72ce39e79cbed56b8472d9ab5abed9d36af6380e9c5deeb2e77db4e7be48e33435a09ff7ec6a97674e9c7c819ab456992ed8411c3699da6aada5b5d6f9345fadb5d65a6b9d552c1d90a958997873ce39b9279e9813d76c755561cc7951983f5f68d385463327f744d3984c1678e6669af1c036c33d61ef55799dcce77df562f0c33129106f3729cfa415a5945e23a33c6788c00d724f187dd645d2e6cdc9d18d724e7cf5561817b85decc27e524a29b5afb72e92703f2dc2f5050aabdd1196969b373d9b1a67d9f4cd37c1299641b33c6199fe8c07282ccf67ddd81667e0d4526b2333c5689369a594d2d77ce26a53bbe08e0cd8baadb470a42d1a36b98a3b5c469b7c2dd7e171cc49c2f29a6470db7a7b58615c840b17239aa820054cec10bc6d5ad4a00628b87265d5ddedf692f70a3984337145e94a131925273025272f27a55aab5d72aadf8bc649e986dbed94524a6bad6ee9cb4949ca95a32b448e9c9c9c9c9c9c76a8cdc1c1198213aef015bc0355b7cded923b69573b7a3925d17e8fb49fe2ad3e5e71be9c5e575870e5e5f4727a399980527a658952292895a20445900036022296744a9d926e084aa953274497d42175417422e88eba241d10b585be789e4fa66177da9dd25a6d3ba5b55a5bedbd9ab66d9cb6715cd779ded779df0782a9540c988a89c1584646a59aa1a171e1e2c50b18abba5a51ef958c6a668686c6858b17302a0c18d41b868b235ecfb1feac1fe41903b3f2cac72868bdc93923f6a14163db9c87b5394f8c1acb0176d96a9b3b72ee884be25ed58b72a7b55a5aadbdf5757de3aec781293025a39aa171f1c2c6674d4d5adc8a17c363cee44cffe1ca971c168a20bc504a4ac271d1c5fa91bffc8513027714029794fd4308817b714ddc14aee9331261e32c14f76a920bdd08c98b8a36d88b1c1386200a9f93080208200a9f130e0054dceb4b146d3111f541d4f79a3f5eb264d9b2c50327f2e006447297bb401865ba4041dddcb04268e12d5a2c14cba6d51a41868c9c113a8b959c64a874ac5746412b95a11100000000b315000020100a87c462a13894a6b1347d14000d7896426a54988a644990c3308a8118c6186308308018828c21c628433536006cc2b3e3b141b12a1d4f8797fb6aab617846eecc6d28193c60c203843906d2d1a5b10187d5baf848afbfe5cde06b474f24139841fc203477cdde173d0b3979d1529c6517db8d4e5d0d1eaf49b7d361eec6c14c112a1f9a82b68b4db6be9c4f9fc3a01753e8852965fac68873aa80a9db0cb456f1e63765bd6d02b54f295b2a4e60ddf5ae0bd966db91b7cd853844f1778e73672a1471429fefa8cb74709e20718bd6e9b17fe724bbef9a1f00ea7c9d51ea8306b5564799c261ca6aadc5449631d829e0559db800b333ce3c2e801bfea12f07364f8cc0465d358f719129421d732ae15fc2140486fcec91d89541edf44c9af35d269baddad04d4c705b3870b627de7150f5930c5faa03f4e497d20b5f439909a664c7980c3a70669fa6a746ba0b8b7236975b3beac94316061be741e925c1c6f071d1ad2b19160132a82206b4e463840914d49250b6c18a6e22ea1fb81f5d9804666c422977f48407494b240d9a06273e8052af125aa01919ba6c76a2043ae3501a42539973688127358254e2de9db0439d624448a7c12bf7f029a06787afa6bb1758f893d4652cd2c2464ba5daa6d826b0dca206246f64ba34f85ee25e7e5445e66e705f640c8ec069d9aa692a6df3165ad35282e2986b6ebc78c63400d9f31e4b62e334553e6ad114bbf9f68aa1d700c9c67f8783af506491acb4e1e16d0c7c7ca3dc20c34b67f8071edaf683a1fbef16588a1c664a684abc7e04afe6ab72f2776b20ebca0f34fc3fd19dc02f7e6432a73885e1ddb74233f1cd54465bbc9f73b8dbc4e575df14641ef3e6ba66e558b0873630c519bbee2706d741e0e602a884b3348b380b51c4348079c14bc1b953412c5b1280b6824c44ac5257faf9dc3c3a1dd141acdf25012fa5a8e52013802c08556cb55063ae34c030fc376b1bd52f441c69d837e4c691938110a193f18ab57bba625af702275bb47147ee0f7bbe3f04936dbfdd16073264584b624c3b9efb93434b01f2660d7535918906dc1b4624396684314d943755dcbec549dbef4354323a3afa8dd105d40335e3c6f0d07293f4156aacb2ec8ecfc0560cbd76e46535caa847b593634a968261efdfc514707be2b136cfe0e08a513f3ce0caea52c726ceb989914e3853d12ff591b8812ad1bf9bf1239595a3deae4fbdef870fa2ffe1c4b387a1cf024f5e1084895cccd90e2aa8bd09a36814d568b27a62abc7ccf6df9b23bd5d5d380803a315b335c08aa88894c4260fcffc5355f1d3a8e5f0c5b380d05856457d44d76063b7719f88f399a2c37a15f9ecf54972f5518c5fbb3d96e8238be8d86bf529eab07fa0adf80703e1f30f4a1b02f0c5b0ddff0a6593523c8239879a2a154938ac62f4b7369eb4584f1813dfd86ac3821343919d2c6efd58a1ce0aaada33b4be6e81f0fac74f435b75c6a48625b2fb46917481c339aaad531a21cce00354fedbdf2fbdc5e4f5226dedbc5a577542f325e122fb845f2e3262c635f69b37de81d62b5ccdb472c988a2ca2031ccc139f4aae08a4546e75c345d4e1d14d1389a74f3ab4055784e4779abbfa51126a232e4bfab17870030dc0e8b14f7eef8032b5dd401e1353fb2707c4ce6a9a05f989f739e6c897eec5f1918e1eb81c41827e67d15db105cfb3b2b1ddcce88398b0b96fec4d1fcbcab97c62f321e0acf1b6c1a6140dc670399f166ccb70a61012036187fbb2aba189bebbd2b7412a7342dfec15a92d14e9c33e2b1b6de4f0f153dc288da840f7a9546b04b809f4660a11838c809011dd301ffa1075015b41b17095a053cf13d3c119c30a84cde4aebd37ad3bda694b20d4dd26e71c5b8b104d0d932311a753b9ab37fcce7d4ceb84546d8d493025d0f3c96316852f9070dec75b89ccbc60d0d25f35122818b641ed6dc41f3c0ec12f674344304d94981ae7c67c6af047dc5c316bac577668cec383b9c665148688de5a400d5c6eacdf4b6c4be59feffdb624dd726b4b4b2b8c0fc0844df44c474d244889af7f0da885faa123e54fd46adcbf40099c032d8a7fa7eca4277fa6c313baf761901a49de6157b722be7d256b5c8dfd9b060b960fd6c0025b9521eb9a8f4a3a5142162460d105f841454b638f0c93f0210e9910eb69160d40cb51821b3de3a537e90ba2b659e7cfb72aec57dbb4aded899a34c3a94f0249f20a339e82393883272e2d1d7d81c219604d3d13c4469508bdb0ef6cf0029440acf43b1163be709d7ffe1bce353df6a68350b8b8a8330b8c948bb0d337ac7a37b234c0db58287f21b7568116004a603436c5049365592d50c4ac3d8b5c8779a426809d64a69a188234e4921a6592573566121df3477d5bd48c225f4430925d9a87de15f43894756fb222b50fc51e555e4a1434219bb079b096fd290797b8dea8165a701f76ac821371999c15aa021ec886eb7ea3571b89e0f755ad3aa36df9f5aafb4d217e591965698854967dc0744ce200d44721c752c8f2acfa05a921f4efd3a5538200edb82b7c21d486684ebca71f400a5fd791fe85cd25e6232fd830b9f7224091298befba8c69827aca409a875db606f8b69d0a2c9b142fdcd40f64a5572f2226208c740188abb410fea9edffe845024d20c523ca9665c464dde366b63656bc327bf09c4a66f7c1dc624a5da6bd54938af4f0783ff8e0b432e869599ced5e7c61d4355095631c014e5f13e59e99a319bebc8d3a728513587c73b14a91296734ea3b76f86e64c8506e6971425f5ecb7c4623c67308d769784e42599e8b0608ea131f557afeca6749fc0ab429398805efb437b2dedc183188ba13261f0f78b5c8b06fcc29abcb473d2bda7c5ff6606cd88db5e878352bc5b670580d899aad01e2dde00a547fb7dc4ed0c9de7fb05f55e3e50bc0c65f81a87d615f1090782af231ed8eddfba1a16952edc75e7cd70ee633f906ebec76957459d45ea6dee0933bc83cdc6784f70b580bc56b193bce9d1f6d0009ad3e88ebf42234af9c10102904c15f4f512f61255702f1d4112ed1868540142dc9f5977c130781d55665fff0936a5b92dcfc752fb8dd9c75730b70a0f3f6a40acf0de987da8939aeff56ae8a1feebd70ce2d6f9b726977ec5187f09dde1b104f8c40aa85f7284eaf7f457fb451270ecb0bb9cac5d952f19fe58cb7e452b22ff8d36d6a284328d5330200aa8ffbb994c3a5801f55de1a908af68b407f182bb03cb40350f48c0d2fc8b797c825d0012c6b54194ee6ac6f32036e4024871d7713d1709ac550fb0bcf63a7949ff21865836c71e5dc1847bb3bc614313dfa391e06e47f5752f032c32506ed532f681f5b9d7ba52f39224a6a783b0d1934a6080e2fa069538b09031ccb6b3d4ee1598f5dbf394f649c0c9bd5b0ee67beebdc6bf68205655f5e052dc8cae815d9c5a1b6a469aaca7084a5b4c686c2ecc1939bb0a552cb48b553018ed8750fedf3056197cabde8438d55be98140544e3164b244643aaabdee4207819ace4205db2cab9a4d6ee6fc4a4b8a6ff09332af51832e0ddf11235677e7ed52db00e0f5511f26c5b16307e1fe99c64723e584444c5a12e83fc685e1fb536a677abbbe73d3b3349a3a3934a85864404fad58e33e5c24ee5501b20e66bf1958ffd0ab84511cdf9ff1eb3ba684ad83063d09de22eabfbc5799ca793cec55fa7c910a429a4382e119540dc5a0813a48cbf8059c4801a4e82ed3b012be1cd39a232f11e63fc3af8a3e563e37ac5a5790d3c66a04ede72bd14580eb569f24675bd3c99270605bdb305df92f83283ff78b079afb8a1da4e9a180acb1cfd267501947bf0bd8f6ffcf7c104d2f9f65cb6c2fbe2e07152ef3c15f2535d7a8ac51a539ff3632f2925bc81298813270a87a73abfea574919e030096b8c5fcb57278a31e54d5ccac7f27058bf8c722ef035626315d186d2bc853ff3fd56ccbe805d7a2218049a1b10000fc6161365014c4072272f8682e554f9b88fec03981481c6543f582e363c1f8665496573e118e0ba1c5c6ff4d7de902542d8fce6df6dca45ac9e2110e887dce6fa662741b650467051490fc9e38154363d0dec90e4b949e9b93c446f02ba00cc923f22b900c6523f8ab8c27e14a5c5dd1cd2c489ca24e1ec32b9a520d879783b1d144857059ba45cbbfc6ce361fce721fcccd6d156e0091bae97dd6ac27a23e789be9d43f69070fbb125328fefb562ef8adcc0a11e97c1e87d8b8f255d6f79a30bbdf86a0a2954d46d0014ca5d68b1a5ef2a6cf6715830ff17f4d7cd753ccfb959eda38cb807f90baf886b4af1e970459897bae9af624b4545c7f2204fedfaeaae9ad9e8eeae9bfea3fbfa92236b16023d78ef19167e06097d68d5ebf47b5fcdd29a266001f8d8ac3545829e8ac3a52ecf9a24eabea03897a363cbab38b4fd43d5790a5d85826c3bdaf742563c05d34aade2e2849a50b3673136e9ecb3a2020da62e2d1a8ecb8f80e48acec489cc4408eb3686ee016b90a365166794ca2820592c8e3039b64d23b09b2a2b044ef81acf026b4f455ef29baaad22499d865cdfcbf723608a0a2aa9490c07602501d2d53942e980d96303419f0c8ee962cbc07cc5b74b9ff86fd18849e91a0ce9c6d81f2c5343a3fd241546bcff21f4f85ef343a93f9d8d446bac07a187c66c6c72762d89a5c66132426ecab90ccbfcd9efad456c7967350f26f8922b62865ddfe5def572f5fb1eddd9606975f51ebf7ba606b41550be411f54ef1d9740506b9d8b2fcf2a6d502a56f7fed3a01b942642609a15237b00326ccc5e491505a057e066bd00822b5836af55ae66b4fe5402671ead042bdbd1492ce9a23996ac062d8ec34cecbc0d5e0ef547a75f45f120cf005ff05926c271222d31e83d54688770d40dd0cc63d90c826f6299f0ef957452fb30a2bf74e47719c5adf7af00203dbb407c517c87f3395cb55ae80ae02735f7ebbc44c9bfbfcfc3c4d405e0952f55dc7f42a617b6be0f0d06aa222a03e3628e15dcf9bb54c71a7f12d2174882f8b07d4cda475ba3095e318f0e04da2b1c8bacc3d75be256f0aa6458809c31640655d7ecb1c57928b916d8a1272ef975b15a5d1b1f316a6ab74059131e29c7cfe8bd2431412301dc2486b0b4c27bb8baa579acac162046442fb800515a6ed2817da78ba79e3ced4fc2ceac8759040214bc0ff80b6b860aacfc7e16b6ef267594de1a993c61165cf38159679947fb9931a71957c3821c911a328e282b4b511a1a4e36ce38b4aa16b12acec7eebc69f2ae10907855a0042607ff48791736dc737ec9029d4db864f8533c5258f626601da1953f6fd47549d11cbe23af6440f806f3ae0a94ee7f8d403accfa7874a9d056ff5b331b91501b5966bd3052994412d53de04d9a43b6463cd6cccb38130a4abd3f3f1789d18537fec547540b14605fa35af6e39bb27e8c54141259369d3d589dd06f0d8149baa5fca6550d3162bb2c3d1807c89f39c44bceccab585ee5c660cbad2e9995a7df9f87c8ee2fb38dd3fbb8bbbedb1dab4226d3f21f94790a39e525bc8df89405686c7213d1ef04464e173c99fd8e4f01cf134547df72b21f2af6cb87512d36b291f6601a1c298686c99371407e89fc2d9a1c9bdc8e902292dc26681adbe9961db4a79adcc78c8f761e9e68fa455857a735d57a6d35224f77f0e8bdf48415e48e507c1350bf8fad01ce8b940c9ecc5575a35a002754e57afa82a90cddb07264b43460b0076684c561ae5cfa8c493f56a0c6129aa0e4cfd15dbd7eb56934eae84d6824331058c4be9f876013f888be68cfa678a9aca454920bf37ac176cbcbd6e76efb0a2b0f5fa92abd67dc764e971652ee35f48b3cc1000487dde0e8456a95e31bbf9b04dcd69fe135cea9ecc4e7f2d9de7430f031e91201f30bf02740b5b799c903695ce427c78c01de6546055dad07893cea37626e9ad52e54118dcef605756c27781db723067bf66aaf1c11b5d40999b98be8aaad0b7c480ce845897e724f750fbccfc341c84d26b0c618ce73c1571e4cd8d82d41e7f13acba7e43a1f9d2c3d26f7eac01b7ba27bd961ca3b7d6b1bd651db38659b14b2af889f49ba65b9564c4730d13e991b17d6df50a97f4a378a30b72ce12177441f360eae8df938633833c350a09732c2aa4aedb8fd11f393b2f7073e967ad43f257a15297f4bdafb7343fb0e2694e81b27609d03c9e3b2adeae813bfceea76a438a5594f8e84cf400f479b4512e2c3cd1eb87c883f423e13e31520ca7fe8a323ba5fd76e887129874e2713cda8a0d201b29213bba84e199ce8c0798ad7b4648ccf47f0789f4b23b6a6ab16bc276c19dd0f674328d801ad80e96ac1a5ba1c1d4a86bfbdb623087e320736b359765f423d8b60f3a8e801c63aecc770ee7e196dc2a70d402590d7a99209974d51dd22b4a4c0ac94bac9a3cd67d5f502cf4d6be97a94a23c0839e5f1f74fc2a0c4b150e4f846686f4007e2ca10a7818ce2111c1290ec8672821404a5cd8881eb06cec8629fa8083865eb15ff02382b061c1527f3ff35f85dadcd4004316cc5d5deb92fa567649a8a8b43d7c30523eb86f3c09cd7fcfeaed0e0ff12a0418ee502467ba5c64d9210c3821ef45a1cd73b33689175faf5e14304b88e7b94475c901d5c4c67abc59f524ee8422629411836bb026abcfc7090101a5275caac71aa0bcf64e280a4e9a709c8e61580d3f9e50a81071cea07bcc94c68858910489422b25514b02bc11e68ad1101322e8475bc7dc997340e4f54842be253950d382a8ba04b52ae3f745d11bb84ec06347e5c8ade7e4eab2ced5aac26e34a17b473bae4c72b9f4860d5976479bb2f73b84dfc3115c28ca0799716bdc38718d32f7161ada45db10ad6f529da7f5695b8acfe847c8022eeae972282255f55920d4f120774b558c25111a0d9fbd21f4933365afb793f6c062a4bbe6337288dc51c51916b031212f35c78ee40287563388bb91126ef66a2dc1a99e0bd7bd2df3d5ed87e905489bd6fa58868ca6a8d935222f9b63a09e34ba6976a18d9789b17757def25061eb9447f47d27d7bf029e2ad51488ff085e4b81d4a5a4a3ae5da36ed5d6b988980eaeee1c5a9abd6609e6a9422c84850eda96d948b8430634b0c2b774221c8ba1dc9be00b874f6dcf5a89e44a9f84cd438005800437e4418531bc8a92c60178bf3ddf1df1bf83ae1497db29f7c246474b6196ecc1b7bb640f1b5e5cfc3d489dfcdd5506770559d96e7c362caee2dec38bf52a4848ad3f7b5535d1ee466a4d0075f57ff79cd27d8d1602843a6b4e2cacdeb84b842607687e8bb5537d5a6a935b59c2b54f4adb14d49ca82a2d814d3860384fbeca6f70542725c0995365dc000060a8097c2262dca47f547a590171da85e72394267b2c4acf93f9647e323273bb59553159c88c9cf8c1e2f0e0278464da19a264365b9f4b7e62c96a73767ff52b9cacfe060ca61f338253292db2799f6b80e2554023d198cb0a7a48d686f4db5db7e764c1bd5de0218a3f700b83dbc82ca0a9fc23f68ec12fd118f0c1e9f897cda8c9bbe05d1b3bed2f4d24f850be8b3f2b0539535f3ee100ae85dbc395dd38093066499fb6c189c30078d48efa7014231968ab57e086a02b39edc470aa7a656ff6609152dda3ad023ab5737939353b4afd4503312601a4a89b33ad53d18d154a4e5f1d73f37e5db89f5860f2f9aec82f5d0dbe08e4c3f572fe26ce4ba98580b0a74cc36d3c114e111ccb05f473af307722522c7d2fa70e362c18e8b10b71b12374b949f17f4bfa05672081a8f1e4b8b7a2c2b6274dcf82bc7504f2fff1bfeddf2658cf3fe27e1c95d82affbcf75191ff538d4c822304a005ca3a27f247d5b0bf21f96bbf1a6511123289a0a712c143dd28a6f17871eac30bc1f27324251d48bce56e4d4a99651eb06b8d08f717eb0ed193853590538121036d2f942adf71217bf0f4929547c6b15e18a513ca171cab3399773ec3ca7dcba9d019032f9f6c76922b073337395619e0b34bf590cc08f887829e0fd5ab90e365e9ca1a43607f3d0090d5a134d7e6c81f828994e37997da468449202821c5592583cc8f939a18763d772898ff0767b3fa880019569d9fc82d044716d6db8221be3e9f63fed67d7e5c5b90e7582b0f6cb6e98f20ed8a6a115f0b1504b69afc2ca900cc7315c892d4b59197b2836585de290caf052d62e7f56d4c56ef97b6c083613e2a68d3efb8077169b4c554822e8b998cc77c01fe1eb8d30bf45809118305a6002d414cf36944200721330243e52eda4a0206ad54da16010c00b66222ff17facda7c819b5030a7d79059b14b91968062e6e66e184c0998337448d861747f10074988838f94b4e7d298f8688c7c177be7a0c7398bc25250c9a1f7f7157a96e08197ed372b406dc47bd3694fc3664dae4828bfc5589c6f44fffc23fd3198314bbf04221f75a0b026a9881219d1bb4a612c2d6d090a6619ec20a339207e35b2217291404af136cc1f64e15411fcec319d831b5a3003743506abddfecd8458134461a4ed6b55e06ed05e978627447cc8e674560fe785e0f55d9be77ea110df2b68817b31447ea5978135bbfd676ae6677d956e05864406b425ca66d1a2e4a9ff12518d1d6673fa9bc16445021bb0cea2e94c84c482f325419ab2157ee78378ce659128bb5c49c5ad6e4617c7ea4e0c81eb1f8092af6a5114e63e894b45aab9f0afbc27d1297c48dea0536de5eef03171b93b9e7e165d46416ac35642ec6ff8154468f154fc0ed7a0a408fc3132bea6e4ea78c1ae36c57fe990be2c6b6046ea30ee758ed536c2a69d3eb166419b8e9ca06d2eebd7aa31c9de2b2f17fbf4ef17f2b73c54ec97a5bc011218f8b3c4891a0a86946aba4a0d8ccfc1fa177955697137e07b9e88a9bb8b9f963e63d82e3d8c5334ac76f66047b2451d848faae97170a3f38e27b674194b84e28f7af59f6656219675632ade868a9e9fa83c47f220018461bcf929dfb237e309a4d2a6b8f2814336dffe5ba13e9eba2944d91d356442c686a064f8a5f34ab39aa2268cfa1329e7c0c0bfd97beff217c543d02a3d84ec3df1cc42d027fb00275fb6e4f4e79a1c2776c738b3ee74321f0448d2554551391e3e5b3938e8ab1f6817339ae786f221e73ec3bdc5bae87a22474281d880b181905a64a639367e250febbe4b0c5a4245be1f2a75b93316103cdfd2639ea4aaa8047c4bc417aefa80c1207d718aa1165a4bdf11e9579efe1c5dae31ad801281a60c53991fd0640b3a8b7f657cb0383b3686413e7c7a7d898ce1cda5f1c93a0ab5fe581f83d196895133ec340e79b1b6b1af60fc4ca55ce27bc07c5f2e45a4dc18d171ed3d6516ae5cc200bbf6540a465130e199168c8148f0578a1598ff07828220b336138c168d5e0fd14d6fd49e0be71c3860ad6770dd7fa5310f845114f0f465f482939dcecc745c92aa776e1b2f8d3de5a98670c1514da3901cb37dee01ac9d653855dcf1b9cbe5c2087b7e468573c792cb1a8a8f0bcbd05823f5547ed94dc7eeca6402358d6414399af5ce3841950048d6e35c82559e570d5ca14030f25a2d8e1d6d0ad63345fb7673433d350d9ffd079370c1941f5cb39364ce396b603cdb65248d7eccbf22b153fad7a709345ead35f730b0da8f7f86724ea4218c703c106e306cff5e284a44c66a999f2360aca70a42c8dc168ab89118a2d06b6ca438a8f7e0cf7d802048eb6c2b2e496c8cfde4ac1bd91395788648036474a2613788ad5b3a380646e6443a460d0b312079303e8092a4cc435e1264fdf13b1f9fcdc9c9f94bc3e98b1eae3e1779f83b9ca1b3b08054fa0ad13ffa683cdf22bdb92d959230ea630d5e2632905609daac486d7b8a311bab00d34bbe55d7c49e0faf8e72fd3a867738ce0b57f69580c2a20fd43e06682843660ea2019b29385935dd1e5e6f9866b7751e147c37b5b36f0509cf3e9e88ad987e34d9f487efb7469428a474f23d3275a82df4f74b1ab19e27c0a1696cdf04f8c7c87eab9b7d1fbe94a8015a3a6d447ad10e907d90c7943360272a90a3f643f1b11a927b860d03fa7c67e010b1d8467e232d0dea847235ae04a9fc654b0e4a0fbfc10c22a390e70bfd00bfc7081c818a67d28f639932260850fbf321c11157306c867c0027bfd35056aae44093088c0eb55f7f0b1f7a6f0f003984d0e4753f2280607fc3ea45225e03625aa9358420a8743c0cbb1539ec907b98af00f255581de5d5931d9a5771dc2969abb08223a03a6688404bc5f7bb7228adde046e017812d53d70ebe8058aba3bfc6cec3c108c278be98108055dea2f7adeed5d83548fbfeb8c1b8050baa36f2727448a85c3185fde0df72ec19393be390059039d2e2e4630b1363bdf392700e738c07223a1b9a18fcca631798056b9227653a6fa1b267ce5678fe15b649ecaaeafa866995ea7760a3a8d5e89bfafa149ae541cd399fd765d1471bfbd0453d9b195a40f919703fdd976ee6e1d39f17c1754ceaca44fccfab44e9468fb7ad282ec0016fcb6b91290c3d31cf7a680381d16c65242451c44a429b8d889f3381f12152d14aa2ce191ccb5e2a20605367d450f6ff0e700dc789a4b9ea5a185b79e309db748536c1559339ce8de800a10a040cfd16c726b3c45b9823252254e35118d9dd0f083275f13476df3ffa9d7143aa80483fe50dae0c371b931ae12dfc3e397d7cf89c3a461bfb9a97911da8121e8d0377b79e0a88d4889c54df89063fca91a6ec30b83a9e8f4b5b9d78fb4450b86f81d129687ce0c083dcc05b083641bb2177fd4bec2f04d1ff0640b28fe0d0d62917649c992a56d55a97755616697407cdf11877c494416f70484cd196b32befbb06ac4bfaaf4a1d05245663d5619b0592de753bd88e9d2158d89c3f1bcbd61796a42186603fcf88c9e3b038a9000162a47486c1f35ba0052bc455542dfcc12f86692fd29a4b7371b432899812be838e63dacd2ab8b051de00cf4c408af924cf7b7414fddf07da81509e2b6cb4d6f79611f066b0947a79f50bf483274c3c2d6fd591584dba213c1e73be786459d2f35a22380fa1c0902e9d5c4534445248aa93f142b98805dacf41bf4ed097d008b89c991b6b7929a5d64532d0b9ff715638807ceca328d66c5ca7d13cc9b0cff79d69f10c57407ef326434ee18e5bbbbd013789d6c4900a3bf7e8b0cc03d7cdc6b3c28c2c65bca5cb472f768f8280852a0c5bd5d1f17184f3a6687b9053868fef547e845152f0d6b34ddef37dbc5b62ef0de2e5fd6473dc6eeb8916d61b6afad46581b6fed7d24b06f802bb03921a69d66169238ef966f1d27f1dce5bcea0ae6747183a20d48724a4ee16a9f3c8b04abeee883123570cd81b80ba79f955cea0461dfc149b8b255ee1a88632d41e3661e80aaea5f04a3b000839b72dfc6dd804511edb10b7c956e75b7ad811201e6903073c92b81695032dc76f348be54062a05e8a0f3b2a18ec35484fe41931e654350a7dba8752dd7903434cc3dc10447a6976b010c146a4fd3e1c1b11edf78aaa41c5d6c19a865c5d8e71ece8832f1664010be4905b8c5e316fd094ac9ab455490e8de5b43ed2147e00e9114c66b8c60a6b39ac58881883a77f15d694244cf24fa2b652efb546ad42afc38878bff7df5ba3b21dcaf4f8ba7821674f82697017a9dd6c6949ef7a12297955027f0fbce127ff00b54d9c5d4b29d426c98f120504d46cf482f6e96cc5a397237f688e6780f8d1a6cbf102ba5d2d96769ca3d9b229f94d363b2670a1b5069c469f8141114ce986288217fb2751259b2c45898d9e5bfb8c429be4545abf7510ccf2f5eea2d46da9693f0c54b8990aca4890f3f5ee41c570cba29199dac505d0265142e0f09845249547a1c8c648a09092945d0d7be3d80393c0b3c1d1b887253462bdfe688d0fee253012ef90d5f90e032774f8d1a2c8c9615331dd26af58b319630bb2da780405bae992b54816e4841a96e0e20cb863bd5931ee396e44e248027188b5c7e4f0ec6bfc7fe23c267762eb776eb022e2d54b5bd52ee146d4aa4d6e85edd15dcc60aaedd2ba79578d8e180eb6f23e8d4d91238081e5e021bdfe347ca7861ad91c7ac3011ab6c385854a75817f5d9eed393df17a9c60b698a96a753fd646748771bdef013e2683e280cac400aec9701291f5d4887ea647b66f0f4d4f48350dc661663e74fd26832356dc0cf25a1503de505724d2d854a9249b19153c883900a9924ff8bc33e263f3aa80cee8041ad10c8cb484aba1703ee3862dbd8487d1286fb130924c48dbfd45b21fd2d35f8468762a8459e9d95255f2411871d7d1bf883983f59cdbf74f6c60d1c2e95f9884a95d400ee5b3c4906a18b82fe34bf3a061136d136a8fa1a849feef6e8fc6bcd769dd50c0a298f5e10c188a493318ea0b6bd902f6984f0b1f9f531e55db2ef4283be2c4db97773dca09b22cda063c831f629a3466a46d2fc5306e1cd5c8587b953bd3d06eb69b5771c10bf07f611617da11760602654205ae02df1a31a601d1cdcc567f0e6cb0ad55543bcb9f0a040ada43df559b2b91664ffc6f8a2703f33ed33b4fd4ee7d3e72ef90189e5ae34ad9be44d12ac1cc0c26352e45f7f243d64c8262a237813a7e4f59e09299d9f717634a91e1a94a1e5c94f589234229c1e7d50c325851f1025fe3272d46fec667d5e5c241c88ac7e6d97403e4d30664953511202029d99487281a947b1bf9d38a3110a87a1f78d0b126691788dabd0d84c446cdbdf94558269c50dbed53842ba6612999b845587eb03a2280f784c386fb8a4d6d0624b11cda9eb04caa2ff0059fecf266986c89fccb760804833783d0539ed62e0df7aa40dec0d9207a78d2c091c07df06e845283736c05aaf9facb58622ade3a5323a2275354825be7d11bfcbab7baebcb3602189f15b07392874e231f3331cd2bc10e1ce4593c06598eafa8f0f15069f649f357a1ad588559f5945a5096f57bc035727e8e03f6c6c554a46ed5768072378cdd2db2b295f952a68c769f1281e2e32e638c5dff966f6aad7e7703259cb96f95185ed2f902f2408abfdf88f075c460bd216096861c8562f04f1c15b20e69dd66192d2261d5192ea5570277e2ff34f6ab8f1a676ef59d3d94b30b0095b4b6f350ec09078edd5e61e009cd15aa574b6aab77f004fe19a28baf191475b4f0b307a56b44555fc975e9e5eec1d2550da68cdef5236774b9e84035d4bcf5b6530bf7181654649aaa96fcab654c15826ccedb35611689dbe48f87f43383cf030c35a93356aebd0acec106d5d50fdb0073176f5dae9d30a667f6749a3458a7758e12a34186aac1109abca0a0ad685157819373d17626aa5c62edfa9cc330d4357af625b7e41c0313be2070d25293f618b034d459d99771c417c2e04824dd83bfba0c0cd1865055a1e89bedefae7901da2dcbbe5c1ec2676214be1005e9c55bd42d65f82fde5acd1eb959ef7905bc576660f8548a4e7c2f6453a524b9966190a3754b31f9fed7164210b2c92b69ff9daa0202e706f9115ab49b1442422161da6001b1f92f33fb85affeaf41598d25e5f6053dd47f8d7e5ffa15be2c319423f1030bc1bec0064909d95735eb116424a6c77044e56e9a6c1f783e7468b8a9612258b6ff6a5a4c02ebac808844aa1c7b086249cfcc0815f4892fcdb806df84f23559fb7d6d7341af0bfeedd25d161231365fd5963593d8e49f00a8a4d1e3c3641f5ee5d19b8c1f3bcd69301b0f9f2168ca0654a248e9f5ea8e7618e7dc69942129e16a0e1bd2eb5c8db31de92c7fd3c83662fa5107274bfe823b2893306402e6a71f77d28cbd3a2622d73526657c755e79e69862fb4c753a337a130fca5759e7f28e0b7db1ce9493efa7af401f5dc70ed29125c4aee35225aa396b304035687578ed98142b01c2bbce3e9485bb521f17c630a97cf302ee8ade1b525041a87259b4505b6a2e8c78ef79db5a5c7ec53533fc2e3fbeb054e98480e27f5ac87f72e4e24a2c638fb912318db7fd6140e8f400542f4c4340747a177e73d899fe43ccf44ef738857f9d1e2746b39f0245d526162abae1b7fc9c9e70d6e61cca3de89f174602e9847410044c8f80d8fe5217bf5492000509831f3e69a43044163dec479518999e19fdafe0871f88c21f7c052d8adafe6c19ee67d63457d3c4c4e4437f9d396e51931d85558a942d69f348cce2e70149b4a124ea380414c7363199fa4b475fdc2e3c55075004cb538d34e291f629fce7a5d5d10634131cc57816b490da900048f489b9198624246556e42a858d415a1c79c1dfec37697772c1ca2f19af2e1ac2f228ef9f78138669fb9cf05c7baa1780cbccc64cc9fa14e04312a25a764eca76ef3d7d8ee8cd9b2f80a6b73ac890fe71a4afa9d95d2b6a1707d3a405b65a4ba38cd81211847192e9baa21385eda1acd13d293ab0498c3e8e8e856d74f64d1d4a50577532490147ad1f742d744d87834acdbd6c78f9faf2e402faef625e37a4448cfdcdfa1504c818e68d231832c1237dde651a78865a571d4a50cfd070fd024ccdd93b5fe20ae1e4e41a89974488866c4928037c52bd3bd83a2d8ad676f7ad507fb18852f23bcea5dcf092c3f4a7ab8e59b4859c67826776668f208c112d6832f70474155ef5ee80b6c69af29c54bf9071596df1eaf53463367a34130799430c9ad3f3665f3eda858be0b9a5e6935d01275708141434b6f2c16e725e2cb9a100d47d09a2841982c8d851369a8279dc9c41d00a5c5fa70dadc64773daa8a3831347d50466401d688f9a4ab8c557666503b492386e9c08a27dbecc44fe2018a0aeb9d7f345c6870bec8429c09cad2d3b0d382f87ebf0efe9fe35c20777aad3fe5988e3be161e53df688e3e4156c1057e5af888996e9d3926884da021e851c1459510f89734a15244ffd9b3a265bf6c8dcf9f0f7e702504c296910b13d1774de0b3e51a693fd582e6f8d110ab7f302140015c208587bddc2007345b6436ff5d23db0ec2639f0c69aaab3b515d71a9f73b0db2f791d1b421e4940a23fbe7473c3a66a3ea87a0d95213ee40061116d0603f0e6dd734e05868dfaaf678ec0ce29cb690b33d885b3324b29b58ddfb93b97f30e379beb8c5da9c2d7367cae4ffeb9724d9f74c6c45a26cd189aa171389840a8ce73a1987a9255f0c6808b16f33d48c5557e07825ec04e3b053308b743c889c1058ff36e62a0cb9f6330e9bc2cf81fcc59368b373e6119b872e74ac1d92944d139177fc8e2aebba40db30cdd41c55b666bf40158b67b48a08822e3f6e2a0c4ae77cc11c1be29dea65003ae09185db3647d5a3a70d71f0f40dd5f237538ece20a110a62cf6a2b907a0d8b90f9a7cf4f669ab175b3dd04f31ad83e8fd60f4023d78f503b9d24614fdfe5c223a72f24cbe5f223dc7737d3aa05210894d78b1dd92f3d6cc1a8030b3aef13a63d4cc9e2d1ffba6d06397b562cf9a34a453b88859f10c2847831c83b10cc126c3a4bc151d68807e05eeb071e21cf408921a80c1f5ee10b26a75cdf90adeaa67410e0a54948efbc35628ebd9cfa24eb0a4c5b3456ff597fe1dfeef069b37f5ba96858b1a8c5a29fc7bbb72f302ed98b00a6cd5a7ae860625e2f0806a8ae75578e80086f17104da75c84a7181577edd6e29989dcb35a5f87202d24516b29a31903b16cb20e5f15a8d4414150ef52079471359a28fffb033b3d4edb036b08593f3204ae3c7a5d712ebcd573d6d948952e7d4ccd43a5aa885ff7f1025b519555d08120d7f2184d5372d6170d906d48000a72946cbc854d67d67a1d6885b1be507ac92a23343d4cb998e23790029f65ccb584254cc1d0605097d231855a3ea72cdfed19f7b01573263f26990eb9b7f2c94576ca0797f2fc95dd4c8ba954525856272192ff70c9a90b4936f8db86165a81e0cd1678e9f2bcc1597e62e8b2f34cd39b3dcd248626d7a98944fb96d5c2ddc779a8529e230fd96f09c2deea76b1af3e7c77f081b126a7b55868414220e935e6a63d7a291a8aafb75213de360d946571a95f062699730d9a5ef51669e23af014edc613426a26ed182980b7d94a61fd2104c1cc34b0a4b92b55455661e07afb5f5b51f99ecdec56c444bad73eb4592f8534ad3bd44f98352c2ad411aa228056000a2c687d4c8d3053d06f4017195efb4c987f8b88e0690e077f588f549d97468a506728b7e85c3a382600e6064627aead5c503d7f4a03667b9971874099a1a369a556643959e2ec799ff7dc7e5385830ad4756fe8a24ae075d8762bf5dc13bf076c96a73c4266390f07eb2039c21df9171eb0649b0449e88cfe426b12301fb0a7021d83f9c3fdbe42f67bb7c664613e9450d0a5caff4f5eadcf8ac641f9760b37ee609b32ecd92a8dbab4180ed25991b9eb849dfbaf638a8eee0e30e5de337482294c2e095dbb0cb1b6bfc43a991c7bbc226cd695508f89ba73296526cb34cfee1e98fc7f60d4bfb87e2d8f00a6c049e22c458c12e08c705158bd6e078cf1f8e6282ddadac0bc79f2a8340f4310004411204320702e68667d7a0bf32f86601fb14916c261c1d4b439a91108afe5c2206b0e2028228305426e8f4120aeaa0b8e2e02ea707386d6ac03bb2cf292037e4aa8844c355183db89ca0b2fde1f4a34ec7e0f1d78405fb82482b6b5c264ebf0c704381019a7cb336325b0ecdbbd4b75eeded28a2bd1393b634a010997807056f1b30ebc368b411b7cfd3c0eb039f8523e0cdcd14620ddc15248dc2b7f3d4750b0944adf3f3800ec442c8ed6690f4ae274e616de5ecca3a908d99f6613d1cd3e89c54e1d9aaadf388ef31c26987fad6ae855d4f3f513fe89bb858aa08581737ba96307fabf75e95705a30986c8df4ba6b4400085a8838451866af91580f960e45f516a3c2e9f4a077a0ba5d8e82282f99c9068917b17e6dbe7d78cf24d53394fc56320a8a29e67549c6f31f71dc1210c3f69017fcb06bb835d95a7fefded8f2268fc18bf13047bb01990b5ec560ab4a1a28423288dc43495542ba9d5676622e4ef7f836b0d5d15b97916838ca94e4c000387c870edbe8ba9459a960e49589178fdb806d1b15ff9087a5aed37621d4fab69bc5d8a32d020ab131c7464974c04d606a7950d1329bcd600fec317a13c6a9f04ccd3de5f3178f5ef24e1c68cd1d659a8b0b53d57fb71c7c793d7ec5eb1d799b6d2533723efb3d7a8aa91f310960c257d070a8465b65e6c267491a94dc55967ae6074f5a361f609ec91c077ab0571b6c14c434ce6623dfb00fa8879bf2143ad52aad5afd3be189ccd9dd507736a8b5e7565bf620477dc96979f9cdffbd6c265a5ab3f33db5676ee2a1eef60de5464f8ae9d61fc258d62e22f98e9d07c485f6834d2a8e1ede6a52710252aac887fb0530450e28057470e50a274b8dea05fe8cb0ebfbfe915231c9360111149d056ed1e52d1fcd27872c04be447bb3d846863657dea9cbd72aa285ec73459b254bda9590a58903a6496c10bda4fff88ab4828fcdc368872bfe7779b9debe48faad77428a5b2df9d042b4322e162664c9269d232cf738fd2145e0b605fe7bd07f5e9f0d6ba69f001059e550b79096ce116e07fe9966f80f3278ee439228666cb657ee5a72795b3ce972e216e1c15f5010c773b7ebfe220303a53084dee460c94fb796701268057e4bb5326a9084f30e3b026dc8e8445f20e9421a16737d6a524e4090fb91cfd14e8bc82a18eb84572302c153a6fe0027d6662dc296c3814c54cf98ad7c80687248821035bfdaef3df6ee6865cd72bd8ed28a8ace9b98d9874c66c9dd2d57195abf9121697ff91bf41edca4066ad58d02ae8acbe475a37dc850e7dc09173fe465f2fd6c0f5a04181191d7c483477447686ced22d0d05ea0ab69fecabf863cf4243b607b8b0bb38b96dbac9fc2d0b8e998d1851455e5f295fc5922f8e9ea80733391c8f75689aafee0bfb18df9c14671063e7ad2cc57093ba1b87ef04ff8554e355701e78bbe7352e553d240de10093f990d2b386f1aabe205dd64916c75d1155934a768d5a419df275e274ee208264bc51e64a1b2ebdecb4eeb444db5de490bd7bb52bb6ee3f64cb143fd64849664059bd1006b8bee221941de8f333990c1df5aed0f738c66424feb76a8d98153375c4e28c9d7620d67d7e650d39bd1ceeb2b98cc68edff8c6becfd4dd116db00ef8d473c5a45955505036805632f2f6c78e97c0534516db299b1a7de81e5fb6a12435a897ae20e0417004416d9811a173aaf145c766e1a2985fc06e26894b586a26060927db1f58f43c613cb4c4df91658a2d75c6cc36c5a329bfca2eb3b125841ceabd83ccde598911658dae9956a51e3c70ed8217f73e501e04e25282a5c59f78df0881d72bf153f9cfbb7ba70a12cc01105097e488573923b18733fe6cdf719c4af9cdfc3b86df2caf56ea17c632f2def79aa82210141db3ed2923308f2d8242e3f482a63637bdc6ce1e5c0153f951e27bb4bc077b0e3a8455a043a88a1c2d55c26f98dda7241dab9237923198cff1c6d079951b2033c4eae8bfdf3ef4ee7d20605790008540f3c4366bf098a88d33af0fe2c4872d39e4cc2b9beaef8efaa05f131a79059ec05e8919999a96cf5596e54297e9b1309971e0ccf79c32585a25cf1d5bdb54052bdebd2603247cdb65ee2fbb5cb354b87bbff3fd8ae2e7e8b559aa887d31ecb28d7f7729f4f9005e5d30aa2ffa3178d283c1426048b1905ff39018529d645106de0fc17b4a4b625170c50daea23deee0de8b7f6050f086139158c747cd7d648a5da9ab05b0c7ef26ba95942b33fdd74e7ca808e1921df58218410d295c497901d830d0e2799e04a38dd21be5ca212db177c5d34229cf6e2da59ab07213c2df89ca9095abd0f52dfb950d52cd19f03e2c6f8cc0b53c68e069528169382325df36016c3e2535bc0869d360ab37c40f80e195c1013b4f912e657af5b8dbec3025f0c80a2079073b786cad4d5124ce8fb63c9d8be59d9d2e1f1d3cfbd0fc7b704803b486701114be09bf19ba48a16d47af3c25498c8d7ca702653bfb31aacfc196fed116c598c9b195c6f37b6d644f07d0beeb0e36f7fde0926a9627f5ccb881ccab8d84e0d183c2833d6200764b3dd624cc55325e018ba61b56658afbd7c814072ac08ba88b4bd13d394b0dff569b4b68be80bb166a38d6944905d579f24734938a704fb27bc3955942402f082adbcfeb59e1401cf2b5ab5d31933eb1b20f97aa360dcf8e43841608d22dbf8165e5710523740c973f55d43cb759abbb279fdab78de709a5c71b2d87c6f13e7182e56ed6c8b748461f4e275c956f1f73ac4d7995b1754baddf538f0b23214815a0b1514af2c1f0c6bf015b65dca0a220f8f0356dcc8249487be7541ab3bb1667b44e01bec553ceed133fa29a8202a20d2f0dac8679ba00a00dfcf836216b75b4c6dd1f6abc718ef382947f939022aa70c436a451098f3268b31147e24e294b207d813845282eebd31968ce3c8be43a15461c9c7773e3421913abba79e96d14b992e0452f0d1f1595dc15b2785a2856028a7d2f43f84216a48e5167656a3635ab4bc46b1321a1218358a79c436b4f6cf97b5b6aa4c8d015dc76a650fa774f20a56f5b0abaf590182cf03642b0c2e512c7077f2a046b28537090b688688a00a5cccba355d18b969d6becf20c787d09cb908f5a492f3e46d61249899745b740a0a92131b654950cac71f5820b698400fe85b59c8c3150d4f3f9e875e758ea08b55c287a177a0e08f3f4a4fbdfb0a5a8b3480d0eefb16eb582b0af2a14a83a73887abc54b2273529d388bc8e1de1160fc42ad74edab7b0ab40a1fa8e6f67538d15eac7a385393102fba0aebeb689d311e43809aa7c1dad8739811b675d156173819ffe24d873efd4352fe7d48bdc9486a8df48c09c792643c6d530cfd76a0585d1db5921a6873c507d689e4d3a649f29fa99f1e8c7b7abbf17d05d1a0bdaccd4e49c544fef73715f008bcbb8f3fd20180348999466353d745342167828c7c042898aba0436d08b2c61e4a1d3f5894005b6af420ff13368b05a478a5c29821f1699a6f6dfe12972ed50ba9b15460c4db42546246eeb00d29930fd7dbc7fc5a40d9c27ce6dbfa477f1b62c4656be9b9b75321a65e3a27bfd81dc0a59be4f4fa3a21cb9cd246c9b7a41523781d15f206035ef87e71b4d8e15f501552f7cfe775c6e89494606d25c5967a3c1150805a6c29d272fb97a89db2ea47a3265b80566176b919381c16739cddceab3d177eeb90d8bbcb0461aee024c000a40ae444f173d9bf046921924e44dfe80216e75c00522f2109a686942afb3c86335d21fbc11e97a801772963255239ac132fdec5c8e7bc76287e007ff056bb9402ce1cd3cacb0acd866c3a26c6f3357e14be0aa38342dedf09ff25bfb0406daa077651960a00077b93479f176e49952aa2e579d2dbf807167a793e2263fbe9c3f14b03008baf20f48aa32e0072f4953b17c357cc5e34f4c0cd2bc19e00bfe318d8fe2255ff3336f48710da006114b102f48489e60c74ea502604043eb4f8c0239bd01fba9da2352479308d88df3afa48b3588dc61322368b83c0ba9f574f64c5cb18144cc4cc75c90f41ac1cb7ef532ef44044763f84c04f20a07e246ff2033655b52d33184a043558fd8f0a6d55b08f1b294f0ab868c81a3ea71758f129c25739a9b03270b412bd312b0262d1209ec3bc3ac8c9feccd4a247216ee2e6157a41f5057dac2115806b869a19a4094201a74baeb8b51904e034fffec37db30eeefcefc762e350af764e50afff0ec8ccf531c9219c26174020f751890679b25f86a53b47c2b5094da7c932914af50bb08e2f65253c5f4e35b47b574b16d5aa586413a7a7cf721fa5f08c16fca4210559ebb694a19d20842fbbe08ee3e49ee1b8792850cf824099fe531d382780fecd6dc54e441a6fadb23c3eb4b132fa3d045ab306d0e25255dc5803a1a3616ad835a155983dd55c062babdc9f35d3a6975b398df7e252b4291665dc3a77e112c8c64de32ddf5eaefe8ec77f4a688da6124de7738f32e62c73cad1e0275a7747d4b8fad380bb6112ccc1c98284055dabe58d8ba246b047215559f40020057e0f3562b3b537b095288470437faddf034e7c7090bbf2ef0e00778a19a56fc869a485c93ac03e60da46117270faf7d9092d10486fc8b6c316365121d887118d7e2c229a14076eda3a2ff1784ef5af1fde5f289a229a87fa4b18126db9e6c303bde7ad1b5c6a5c558755c310861993f02c631ba3858b82074a1e6209afda7908b4dd8a3f82f5d8dfc0022986aaae46d21a7d20b60e4f4976063c5dc0d0387aa79dd74879c4d8913de26ca2b31c9bee3e257be18cd1962ede3a2e4021a7d7596ab852d6865ad9c75a05ca28ab7e591fa6ec180cd1ceefc03e3619a49af0efdf3bb841953bba28aa774ff4b45de330dc5023f25219aec8462be6c15f46278795a7a612e3bf555addd197fe4dd81a014a7f2ffbc307de24f0748bb9368f09c1146c903f7cd3bbcefd321d0d3aff996ac0e74b904d31f0e57646c6cc2d084f320bf0b8cb0223797c82d984921188aa19822a38ebf41bcb0acad54d6c8a42a75895418b607c821bfff7e0ad978d7db16ebb6c6728a80d25bc893f14743d25dbdeacd50fe7e89032cd095c9af2d3d516c7ff7037d05959f289a5fafa803f7ac8f2ed7e7236f351749150a8e1761c63cbd4e822c1723fbd17a421810bd90b8b852d19e242ecc726a0d8100d649b376ef881bae0d008e0ba0def21296ed6fe5bc340514fb2116935c0350dbc450413dc3983d595c9c366b132f55951c7ad934aa7d8f457785e29abf8f9ae245247cdf5ddca10dd59098eecdee9a0deb6267577cf687c136c492fc9a880a333e62ce93f4dff249bc69a634e6bf8eb9fa7bdf4c69fbccf4c106a866b4fabdbd26fc5b3b030c3eda01bf432dc228f853ee57b86233e651b7ed50c3a46af16e5bfc0118533cbb1d73762071f5e332d31e23a51f6ee7ea27340e1168ab2018584f74168fca27c9b6b75cb96424fed60c53b7d0b6b6e5c9d3ede313004a05aad1fb06dff0c016e759cd7e8e97653290fa18cd9dfcc88d2fedbf80bdcbd1529521f1f622ccf1fbc51712071d42daab8255d2cef9cc1a4cd97ba6c0c93b1ca108bd2f2ca6deda97c9d6fe5f7228e12fedff33ea0bf2a30f4170bca7f5d8d93ff3c63fd338f519499c7ac5016ba9fb7610f07eb8d4339c201ca91f3de4d97787df367c7d8848f03effb989cb86dddd2e3f7f0e4f8e8e8e874681ce02c0d57b8df8e8103c21667f1f4f0f070f47ce27ce8747676344e1cf856b06feb076d37128b0776f3c251013bb8fb09d13565630803d5492ddbca8a010467a74cff46b88cd7d30f4fd992f3292088681506217de5d3f2ce98b3b62312667e2d7e51423effb73461ea329c2a9093a96eb7a42aa5893a60d00c155bcb78830d88006da07d97d18a7372708c179747d0214b40ab14dd74b352aff8deaabfe0893a21f8a33d649fd01e18508241621a94c4f434de37b7d4209dbc951b766443409cb3eaa1715c4c3ab827ef522bf4a42a104a70150ae8da96016229de5985997e9f6372bc93f8b83193643ce80a2d6e6b7260c8ef542a33146fff3117dcd46a16b09dea14132c9b3fcedb8fa45c7f18b65aa73df27d1f081dffc59836909fe55da6ebff9ac830bae9d3702dee48ba551a34c2165008704219b8b423c9bc8b18ae5388f185e49e87f25590ee0a94d489fa4a586561b838d54e1d8cbd80b2c1f8c0555a9aa4003278ea5b9a4c9f48e142f7a2e4f5b5ba172e377a0234206f5714df481eff8b0312f3fa3b2f911aec1752c640f02fcb3dbbce9442ab47df9cdf9a63eb23a3a5ab934e768c6216f54a4bb1e22ee7ef5f64d2bce5a6337077d8b8e9e31c1d0b9f267bfcb4420af61f77158c7c5ccfb6cb897067270ed54ce5eef40877149ad49a92a8118de6843f752f0782f767970de630e11b0d731a6bd793cb4cbc98a535c1fd520128b59c67081efbdaeb8a41313027c92ce1c6d55829ee896bb7b500676832c4d06b8390c87911ddd08e13b089682c4f791a1a66f1f45cb78ad997720dbc27d22cf9e3ba79ebded890abb199bdd286ef23ec2af0d4a76ad8d5480a5fe72dcb8d8ca56b15cf42ca0ef373155dd488276cec980d74587d372ede4e53883a544b8649d1849bd348e5a265583cc18f36095791af22891889460fc8ecf7a74309adead520f680589f3e4efd27a778e1931a7ccdea6d05a37497695a84206d115499dd629d16e44590f4d608321b6f313040ba07e7d363fdd04a608fdb0b8d26904fcf5b1222d5e0dd5e199173b1f6caf692b88c6ad29cb5cca81888e22296951f6f41e25e983f89d0fc3a98c4229573b47bd0674de31fae8cd9a912c969bf2d50f40f736f7c4e8b42203402c03d5ac11311fa4c4460f70f02785446b8e275018ad082b74828f3f71e8bc4a2d4204c5a0f717763fb00cf50888b9c191e0365985500893bc28d0dd93fbc805236cb517db2838876f4565cac7e2b761fa6314d10aa048b15dcb389982126fd511b7c11609961ff287239a844f1ab4e695056112636f68f2289771f308b340648ec205c940bfb3dd3afb5b6250a22c56d05a0e360611adcb8fbed316463822c95141360e85597cd9d717f20f5aaaf0ec100179e10752fa2471e1b0d18f2a4abdb7bfb4af72b2b8c6e40d718de1f0d3307a1bba5f6996d12c7baee2288a55fbe69cb21867e1a187cfd0fb58a471f554a48e997c5815402745191bfd1894855def027cd18c44decad90cbad09e43f2d173067592a10ce12255b205e580541ac580a2050feb05c40ce625778e2c7a6edfe92a56adc10675750d1e389a3d86443f2f37d96158a6505e42591a78669d2bc4cd78ebb9e177fb4a0d65c6c4b903f5af88c760887893b94baa949bbb19d0646c0fc349e03fa20c9673f5c7fd56b566c72e9e848cdc9167c1cfc8b83644f31da12042f488374afbb157281e39504ba8de2b8d1a583bdaf6355a806bf79861ef7cd853601fee296bb258b23b45c0170f75d390f8e9a31b7f566b4f1e68be6bd7ae55229f6d808196ed22320fc1e6c9d91c78a4d43887823e4144da617110ab8863e69618da374512a3d71eaae931a66eba757da03b6d5eb4e8a3b775838f0d30a252d9cc4828d02ffa096667e21d0984f4b44bb8daf4bb3497c2281e25e6a107bc081f7f890385af90ff059a30910693f77c09f94bd1972553ad3280e13984ffbb483e513fbaa8d203f806e42f05ba268ace9b2f7f46657bfb1abe6d6fa823d9ee116520c5c3813c68c464d9e923f1a0ef3e094dc38188e537e5c869a2c2033ec8bda9eb2d908fea3263350b6a0b448a29d02ecb15a8262b584082e66c71ce51681b9262b003539eb6e111eb377039ac8c16b82f83ff11ebe0b2103d4bc033acbe2925a2090329b0ea5089256debb18600017deff55c1888e5196e41dcac7d878d58bd68b2e348cc3a9341360a1cb2dfecbc243285049079773b2aa2487165250a1afec1dff8059221c33f5dc8703f5db979915b1ed6e7b4b29654a5206e008380926094b4fb4408d21a4196b9cc1c40b3658d04316361c914349d485a289d5cabbb1810aecd97082171b6ee058d8bbe1e18887252f57504a29754a29a592868ebd825d482b6432a3245c7c5029916195040b4ee2858c5225b97d5d949577a3a4058f2533e27ec0430c2f6ac4e890440ada04050d5d2c8993df0f461116cbfdcf3939438715c49882861b5451e44314143664b045962b3a50b981cb978406e344450627a921471228b8f3e9010ad29165cd8916170b5b2c065606dd0d77b9a7ee46124128a1f1029311153a300551012b9ece60c2a40627e8524555c3fd44772dd2e203ee723bacdc3527463cc93259d69cd4c082ee1304f16468808a8d29431a4390f9de94734e0e63168b2ec993e65da0c6994d14092152820a3b24e529bbe439ffda2a6bad95aabaf973ce39e79c33d69cb3f0b6ed02343a0a8e73c3167d23b36bc3167d39d1954911e2befb2129f785baf7c2a18e35c4855323d7b16cd8aaaffa51bc12b8dfbd9015ba5cd8a22190109914e8ed34608009fe5c78bb3a81f37caf95254b46a50696f38912fa8bcc9e5d4408f7add690fa5c8882bf841c29725f8a507dee85ea73d26bfd1b0e71a16847aee2fe98b08744bc2378fa18f21920656882bf0d67189cfdd60780b8bff786a34f50e67b825b6686d1249798a8b4d84f03be3f4e2ab97f04975ec47160be8b8c5eded697739c446188bff04531f708d306e550a3983f5bee32692ac7913e8284921fccef4099ed6a84279f0341662b3b28f499330e8bd3681215cdcc256107108df1451ea516364c417e05a114619186410ec4dddddd33151bd556b574ad52de1aba4be9524ae95d0f35ef08cbf760545660f99ef4f979864b59ba3765f9632d5a891f764a6b9dd5bdbbbb5d2b105c7486a32bff7c8bd2dd35d3a2abc57be7b546542778da6aebca08bb5ac2f34f209f8894f547978cc9ac0547569ef6fe075900f7fbc93fba4608d2d2b73294524a29ad952e84322c1cf02893655c322c9a2be89252887d1eb9cb3dcdc91da43c810b03bfa4f204994fc6be4b9998b103b9c6c4642c29476b2bb7808c35d33147a6e10932cec59333de0d9e0c404d428b52551c7198f592d603e31c73d8c6d98a60eee3b80f737822994547fc294729a5b488e3709c35c2fd46964bd995e6cb5f4ef0257b49780e2171a4ac37999380ec3f95640ecd3ea1984bcc23b283347bfddd7b9bca94edb64f705269d1fd69c045a61de56d75dfaf4ad5fd546a9c39c4d3524dd69a1ccda6713265afb41d94c8e374da41298f334b7618b7cd5bf65775d39babf932bfa76b3722454868d17d52366c1337ef4d26a60c2c7fca55bb5c3f58866bcc20a63ba5328fc3fbeed6181d9fe3f18e5007285bcc11ca80b2451c614c88c371d26042d9e24b18865c287e17f7396992a73cbb5944601a347d5aec69713ecdbca5f0973c679409a5451af0c8cd1987318bf55d9e83c12cecf45d4656cd40fe6a1aca7cd405380753193a32366b4f54904516590c993a4f787832430c93f797c97c502293d950a760b27b5f76c19efba3ec7ebdf6de7bc3b147b62393edf4dcdab5ca48a8f5fb0044f6a6444c6badb5d65a6bada5feededeedddedd3259d743cd0727b2b7cd8a57450ea699a58ca1d168b4a6997c0abe9d754e7777ffffda1330fee9912736b04e18e3f125d3b712c7ca179d3e2626a28c27110693c1645206bb81c16e6430d98c155c1957c993bf607e37ac5fed3077ffe9dedee1685fd6b01b99c36433304a61b0ae3d8982c1d2689bbdfa4fd9fc51467b66d397f5c87ac29b169d46e62f8382b2994a9d7efaeed46b3e0c21b3893167682ccd0d8dbd696be50d0d8cc6ded0dc882f69ba768546be9ae6e546439f34c160b5c8713ac7b91da3df212c5729c56fb17e37d3a264ad90b6781a7eadb625d812ec56451adbb2b62e355d8192aa3d45d96e5b727d3a9d3aa5b4fe4c7705c2051d6b913271420bd551aefd795722c7e99ea34d32a7bfd221e81079c44a79ac4660eeb073c251b9af1ae55a8d1a074fd5a3a55a8b922b945c95d6a092c7ca946bfdb13ae5b15ec9b5eb73d7ca27d488ca76ce1e51c85875b4832711e0b5695d32c7573f7552598bd6c3bed9b9a9d2b5edfbcbc29e1681de07e8e5778f048164937c79a59417a8c7a7c5f6f7e9019299f6008d361469770927d4dc5df4016a7f8e73eff6eef67eba3580f39c60b528d322d79ab37f504aa9ad284b1ebf16944129a5f467bbebe9cfb4e04aa62e7716fd19b25aa473be8c0c225b9cdf9986d239a369913e16dc851fa5c9942aedec744cbce9d9f1d991d2a7070604dbd9d9d1626767ba4ba6c5c744e0fe5186c3371de4fdfe39b99f99e35eda0abba1ac47fe2893bf1393b21e59d7b9cbf797f54829eb9137e2d3cc1f699ca66787c68746ca645226b381cb7626ab09fcd157894131d0f2551f0b70bf10912c231011229285088bc56211111a21cb084244848808f1d9011a01ed08922115b2c870c93b79d2b814e1c245852c2ccacac2a259bec8da408b1d590f8e2c6b4b466d5419f509a24142578408d92021fb3e8530c6582848c81521414233480868e7a667073c7e57829a84b608691a61cb9613aee0c6b236c58cdc4a70b0bbe353af4f50ad55a020ce27a81664837c806a403e4134bb4f500d7b38190932197d4a29a595da5ab3ac4d1943d62c17594f0f900f500f101050934f0f50a53d403bb6224d2474ac8413bc49daa8a43d4d4db52939c834944d9788a93ac256e07e4ff6146f76b89fdce47e9a30bfc7e7de9dbb93fdfa748c86eeeed3b333bfc787ce6eeef84c2e94dd9066a6caa481fb4758dee958145054943fb7b3b393eb9586edf874bf4f8341fd4041d3278806758cba7b904f8340b2d61f44bb7d82bae7c6bb84a99a0a2318ea199e40428b1e4081472a6b520a48884b91e0236bb2d68213d01f28cdcdb47f637bbeb5efb6ad6dbb7323bb997f236bebd5bb61373d1d9545a86f6e565cdc502b6eeb4dbffc527a9ffe7873df04fab29d9b09eecc9d8e71efceed8037f46615c4834c5f76b3e393bbe91d7a43223051a8c59b9b3064635418d93f0353a7df1f005cd0d16be3cddf38f51b96ac4d7122d370c43772aaa6619fde4fc187b7648df390a2d7fb1ead4f2b0f59e3efab054cca92356eebd75cfd5953060cdc55346d4a9bd2a63af22b05c3e9c369f7171efd68a8b20277efd1af7eab6945341aad05d70bd961376b74d96b50a4b2e3f4ccdf53555ac7c6e97d257b4b91fde78dbb8979058397474155e425145ba4451c87a65158855ea80adf3e0c78c42afda082dc81a3e8df170cb9f0a38eb1972985ca3c1207463ef10298d07f42a9619449253b4de388266620f560850653b3afe86bcee6c91160fa2e6b556859564b01955c93658d072cf928298fb0dad1d1dcc9352c6b29b065580aa072ff8d6ca73d9810fc019486ad07c819ed00533389040c49d4a085162b667d80a9999cd1afe1fbabc5dcfe82c2925c8a908a010a22a32448b3970c4d5e2841b2822b3998f5c3fa65fd474e59d68e96b20759d67850ca3004c81c8be4c29c49f1f7a8d0945198ab07fc593ec14485d2c0e2a4ac22055bfd3f9b60ab652d7a908faa0bee9b54a866e061a774d2a9c5f3a49c53ce091a991305afdd9d52a74e4731cbb9c59333a3bc626a093c6777af7ef07bee3f2fefd1283f20d04c6b5dbd67ed0ff7debd2c2c6ba6ea3d8e9b764a2359d37545aaeffb91df8fd784bd979717181898d5cfea076318fcb2c2313118638c5f66f63e8f7bf960561ef094b0b59155903ff3cb2a082b4ba72c5d29cbf724c7e99bef68bd7c37729c1e2fdf8f1c87871abe24cbf72432d6f2088917e1d07a4be338cd5fad7af90d15157b79f97d3b22a5d291261f91d23d0c3fd381539ffcd58e5fb2febb9ff7719eb4b693397478ab0f06945e7da12c5696543489d0e4928aa6dae1f8f09579e529d1fdcb0a040af33978c32b42926fb52a2eea251c5b4a6a1586aaf047930f4dd61cc963a5529364cdfc4e29cfaf51561044072e7c63e5604c3e00940dd4cbe900502fa7dcefe1f87270154472d9788cfc75e4afe6a1f110497dc114953b7081b22db29b0a0b3cea4ce94c71d1e9d231bbba3f6a512e25bb87f7a369a6e982af26591315f585634b4979e1d85bb6a8420f94aa9d69ca3d7aa0f43a563f3b92c00ec2e41e60ece6afb625b5188b12538a51892d71b12f624c2aef4737f21fbd686563b3b1f9eb36d562db70b15943672a05dd11a298528c8a8dcdc616360f6de4a1f9111eda6863cb4f3d8e2a3efa58ab9fef59ffe33db7e37bda2a15c1a98eb083ab0e44eac0d501541e13609400a222d94d36d585d545d6d47e38fdb8f2e3698d34b270c9ad33e5ae33b563945ba78beb4cf96b87b643b48364670917fe68ba5cd562f3540e7e2a3006153b8a418925c5a2c4a272c7b4c892dbc66663b3b9b5d8365b6cb85c1e5a0fd0872611d01ce8e5289b54114fd28e22a3265038b1083c4a289a685034cdccac7e54efa0ec3a0f069c597584471e5a770f3077a1f92b4f2d3135d59c5ad4e17906574637ca8279683c477890d8d83a76a53a76ebd8d38c2673b8efcf593a2a4aea25b4b1d9d8624a1e1126926d91dd2af776be47c4d5017c0358fd549da90e446a395dc902d562d41749f1d078888a5aec254d9076589a479335fd3a5c1a6747c7ec0e4bcb0e7a0722b59e05f2d076b4902cf1207f210f11016080c76a6b817887cd716a92bfe6377559caf3c73a95ab52b57505f81c6cd9671d9c52823377d3e76ae61770660cce0ce3a0cd15446a7d0722ed780c4e59335b2c50f607e36756cde7a0b491ea57bf8d4d4b55cadddbda5fbd037c0eb6b2e7e373b047ae2012eb3b56a803c4b0cf411eb982483b9e8608f81cdc91bd7f0f3cf060f56323d562775d17dadc6c00b0fad199c282471e1a4f51c7ec775667cad6eee67350267b1f7c0ee2c81544eae03b10c9f53dc059d1ccc85fddc1eb00897ee69a1d31913423ca4f394b86ca51d976673164a91f7f0d169725b6d40e401e1a8f114f131e281eac1cd0b31dcf439339ddf7e72e1db38ee3fafebc46e3c86eceba49268e60c90412d6926fd23728093062228ace148f91e388e0e4af9808502db68c89c085149b8705da9c215ffd2ed0460cf9ea17810b19b301438a0dd5311b5b8b67c89c1cdf6f6386ccc1f1fd3665c81c99efb7d922736e7cbf8d0c644eccf7db90217360bedf660c9983bfdfe666238693e38860b30943e6bc7cbf4d0c648e8d940d18365a6cbe9039aaefb7b1c91cce692a80e8b9ec10c166ac1f6d6e90fb6da6fa07ebfbbd0eacdc07a3acf590fb45b0394e526e25ca24f7631d723fcc0eb93fa649eebfe124f7cbf090fb71a420f7e738cafd7e83ace95f813e38f8e400d68c3ab03307fa2451813e4a401f1b644dff07fa2c91351d38730e50ca1a2422d8acc7e300918864b31e7f23a6c7d310e010b0720d917a8452da8cb2f643ee870136931201a3ac21e57e9f1ee407fd5eb52ff7b30bf43102f30d7d8a6437c78951e9573fd3ad76bb42cb42148b8a49e5fe310146363600c49498e09a8dad2e497f29d58a1c7cc91544723d758522d8fc558023b86f797c590b4c000dfc3105fee872731cd9edf6e306fe50fa41e5c712f883490df9634beeff3186749ae3fca8fd90fa285616fce34aee6e16a833a5d3e5c97174d638a263b50056bee18f261089e8673b7e003ed8d854dff7f320e17141e65c11456e41ea88e0347968224835ce8eef17c1b603d4e1d23f5ca8338505d699cafd3c349e233c441debbe5f6749e7a6c3d438390b0f4d76e3a1c96e3c34d98d8726bbf1d07868b7dc9f63b5801c7995c3a887116b05a455f53e8773182555b01a5d2879fea76ae2c46407a71cab1fee73ac40a0d995237b61168bc023e6a1e5289235fd9e8c056feeeaf7e08a72579a8dad16c99af99533ca75499e2f826dea24c9f3fb6dc8535e6173b04bc0e720e7e558f0dbf9e4eaf30c30138010ac74878e1def09934306c78d9897f93a403993574cc974095a7852cab7aaae7bd2e943ce316804bcd68e4d1c550e02827c81dc202cd84c7b7612327b3052f6a4b9e4f7e012a3d4698cf32aa814bffc752abab2ab504af1fb86545d48c9c05294a1d47b7ed3cbc0369cd5bb2fa53f0da51455217d0f544d1ffd3694dc163c43f028192c6941a41de5fc76a5942ecdaebbae8ecdb71dbd54aac0cf05e08dd5023adb104f1ffd3dc6c0b35dc0eade5eae01feb4abd5ebbaf7e7ee77e0f8dd776f95e5cf550837c80ad50209b242a6340c4f4a1d973fa60ff93d70c0f3e54e4eebf4944d1f370dabdff4bb5290c65d6c513e0dc39bedb4dacb75df8fe9ad6037b2a9a37af9393b53c79f0503e2ee5cb91e62e039237d0c4002d943e9a3fe5d01e9973eeadb15900e29d0d5159096a28b044ebeea7ba0b34a008267774d6b60c30f324a9e3e2229255cd00213962d567ed8804ad3549515b4918612307028b9414c14320c24162005e20a79662a6b7a3af72a9082dc03f9c89cfefb3f12a788bfeedfa6c8f7e58846bedfe03c226bee17b97f9ba07143bef7deff90f0bd61ff91a372bb49c01c87318bf53f8af7efcb2eadc57befabeebd5ed414b8e7644d81670addabc0e5fa3d9376711cec2f1725b53d2d3ea68229c8813b2db6867cbffa55c88214eebf10af4296bf5cccb415849ad00a926dc80199e70a48976dd80a624d6805b9f46f38634351059c6d8f8facf1e7c02021fd81ff057fe413f536059e593e0d9fcafc390e636fc29f7b5af49d9b2619e67fcd53c6eadf6bdd8e2ce40b845b7449c1415ed4f2e78445ccea21aa1f01fb6b7e6765c6859cbf561290516829729a714e933cd577efa940efdcf577fedd4f2445987bac1a721fe6afee67917984c8a72d20a1e026b9e3baee811aa7ebba19e42e0d0c85d3752f657d415ad1cc25753f89565fd71a72df7b2f64c1fbee2d2073ec773f8dbebbc0c4e1e4ab7b4c86a441ee5e4e1ce4ee2d089335dd5f905b8172f5803a5b85a2bf3a30f0d7a79fbd2267bad0843a55050499ef4b0ee62f0bd4e2bcadf9aef74c02c7f57b9112941a05535152ce2c37da3ed76e34dd6bb04759d65400254ffb4548a8214129f5377eed6e75c03dde7bcf5720dcf8af45ee73c24dce712fef5ccc120f1815fe9a78803d1e078eb9c48463464da82cf3890ad704e3b9c80dc78b3f1dbd78f8e1b7dce3781bf35173c9718a4cd09fc78c9a503138c0f9047a2ee2a424559e4bb03cbe70261d4928a1162d0fd89b2b106e3c91162deb8610a1c699f9c62f77a0b800cf1e7e8b560261abdf48ee709cb9c7355dae1a8a5c530552dddd1c08faac7e5a415ccfbd2b046ad1ba5ce1545af55fb0052434922d949723442fe1a47153c995d46211d6cf09de0960cb0333bbde487685e39ceefee272852dcfaed003b38ddc31dde2e1fa29c5834767cbe468a966edcf2cf62754c76e64fb33cafebc354ecfecf370d99f50665253b63fa3d86ed7632bd8f59e0b9c59c682941e0f86b221e0871f864346666008f397f52800be41cf5e95b32b7de1f19e79000094b9489116ed8feb79b86e3cfe171eb10ede3e8fb71fd4b177bd557900a503703e6569d13e0f7042d1eccbe3971df3fec6cbdc72ad56ae702685f30800a077c3b5c7f378155ab4cffd8e67bdab036bbf83702abdeb31112efb2d7012b5d80394e11162263c4a5b088221e72fac2446135d666028fdc502592ddadf01ba5a3ca1c5221deb97d7118e5cfe728423cedf8d970947569eb4699568ddb372f737c291158343989770e4f287846752388f6e98fb790486435813fcb9120ac087e110dda1ca2c0021e7ad0578165bb4efd9fa9d48bca99402cde38fd4c3a11f27889a9a85a15c35800b7b7c5ab42f37e0d1952dabdb4e250eb3fef377ccb3ed98ccd696d0a27d6b59d98601780fc0cbd510ec2fff3000e1908c428b191872e087e1d0e4d28515a219d8b1d934011471749b746c5e28d9ff897bd5fdee87bc1121efbba7341c1a323202a7114ae802057473ba4928a1ee5531df3d0d8586605ed525fb77608b767fd8cf428bca565dcc170eadc28eeb68d8724007f3aa6721e6bb1fc202ccab5ece46990456ffb5688b862a707e07e2bff146662f730907914c3834938c9692888c6e4cfca2227af9711ae5480a5b35147244c78e9733fce35c9a313f24a57ba7ec42307f231c9a377ff9c3c08443134a150edda6d39c509d5307a59a30fe18bc4960dfef1ff22d1b1751b8dce088469bbdfc8d972284a50879f58788b2bf9724e4fd8d70a8bdb0938cbc1f9bc8fbb18d929ce8d6c5896edd8505ee00af0ed076379513c97c43f9cbff87a408798f7f488a108ef77e6829fb13f1cc6e08bdbc3c0e5fc22199b09da0867a098639bbd14e312f67313ff6524cd8aa6f647695b23b4cd8aa466637a9cdf07ecebc7f79988781f9981782f998252118237f49a249fef2efc02437eabaa4ec2ca8c296d0eafb2e6c0d59fdd712d2854ed97f88ed82830c9c3871a2d166ee44d433d597a0fb2f6cd1d197b2ff2b0d2c6750504e4c321041051552aa90851274210d0825a244374ca32179a1dffcf5a34365ff3f821570a4294b29f24893a851f6a7469448a87befbb9b94fde56ca446ad2173a6fa3953bd77f1dbd7a2a1906b74e4ff31718db2ffa55d223b65bba87eb4b6dca221b5d9db5476ea94fd39902e81ef029633d56ce6912ee527da3b9a5845d1d44052d4c6155aa416505ea853a68e80a2a5861d98cc28a594d6262e274d2a426829e2861b6a30c56c64c9868a95c1694007275d6268620d25b6c899b125a22e8759b0f624acd90518aa18b54dcc982d576831831867dc3084bfe0491405c4683af24593194a58d080478c7ba0b07b1217c4061304e11484910aa4989d4515decb0f5996b47c6134c415ca7937133306366e70e249176870a183ec1734164f987278c08723564491c2052631c8995d8349239e7820820a23aafcd0b4e2d2031a388882140519947a962b9ea8407606961948614697267228810e554e70f3828c1f6c68dc2309ee6ac68c7ac203a594a2e1aa82f6cd93196262545ea085259858c152832c8aacc8018c0c3dd07040861033407aa21a65fade0d256c85c076e61b8e17c8e30a4fb429539713afa2c527ba8872a246132c5255dc80638244050c3960c122860d36c0422b7086143348b1c4195856209fb2a80590724617215e8032430c334a29a535a4b9f16c13cc2d9c88a12f7af71530640142882d32e032a5b1483086e8c209195ba2a88822c48cbea8048f323255065349ea1086961a4cc0061a33d8d25400712f184b7cb8a2c68464ca091e59b93db3648d91cc71d517f44676d1434729a55445abcaaaaeaae3e40d47ce8623ae3399c699341491393353265a5841152ac058524416d47b0a1d6e3612030c9ad8486a811456b4604629a5b40b27bdfb69d222429226d57c50c2d11769f712d6d2fa92da90c326c810cfc020e381163cca78a592d6ce72e62e3adad5ea5eb91a7ad005a597d65ec3cba9b53ecdf62f956fa5b554d65c5b2badadc609793ec9cc607a28ebac734a39d7e456c173665993c713e45396491465153a6349a50c1e69f6e73eea5efd29d741f660fa18c2d391e54f00645b6bf794c958e51eaabad166f9501ed731ea738a9efdd246ec9c31d802e32c6b3da031440f3148ea01ea4972b887a43cb2680f383e98bb0326e3d3c3512bc121d32c6b47502e8577a9e0f9922543d17bd2ee6e0864ba64c50a049bbb4cc3510459aaacc023cd6ea594e142d2bf321c0783c964ac2a9870dc0b18b358ffa24c96853890bf3c2ab5885a6badb5d67e9f8c9c9919a513104602d335eebdf7de7b67ecf784826f109915f2056ba625c817ecec150c95eda94555967ff1a62748266bf24942a99a9e9c3a36aabcc8355593ea4995434e61ae8ac0a36a29fb2f75ac86324cf7baca0644944fcea1ec8121161633957d52a42f5f142603922f6444e1cbd6dad003f74a2ad65a6badb5de77b9ce0b816296c0e34c06b114ebcb9c90e9a3803b9053c1b9ef3be3de7befbd37e85624f84ae5fbdebd773a1da275057aef9d5d027b912c9152a41c67836d6ba79d6dadb5ed817b635082ed4822456eb6041496b8fbeca8c982ec576494b5d65eee41ce5feecf42c2a3acf9dc00ddbf0f93ff4ad4092b1c3e40f6a7993efce50706f58ba2816d10cf8221b0610af7460e13748c99be7db74fed0d5983630399be8db9d1852307327d97914760c03d727d96b4a94f73df7fc67b72219e89e94006552056a9ae19b6ad9d76b6b5d6722f3b152800f9a23fd6c4643b236be813198732e57298aeec7fefbdf7de0bd42f7f595580ef8f33b3d219c20da714eb0a35049aef7dbf610a77e68e539ce9580d91b877592f28679c6d6b6dbbb5d6ce3a9003a5acd49d8894fa68f7e86ccdd9eeeeeeeeeeeeb3a9bfbbf7cf15a594ca10e9babf5f0abef033650da53f44de44c2f3633c7f1f94d5e28e9412c79b92284d8928ac4d093294a882e5f49d9b98d372b7a0ae81b3ac29b194c71d777724cbf21c78b4d6a75f80d9b1ce93334d2447a68f650ecdf44790395fa62f57344907fc79c4466a68f2440d0a0545507164365f741c1e183891318314254b40cde60bd69842a4650a1b5a6618005314288c28220a32c498c1e67b2190bf62ca32e4648dd7ab103a8f2b64ee8736a594ce96ddb4db5f8cf44c6166f72a08c3cfa4483a5b3e97a76bf694e9906210cb1c9f3c78e532e76ce69e5f573f5e5741463a1ab6808cd443d6095f962d1ac9465640da41cfdd861439e2ec5fe6642f2f704e39b9448eb1c111187e56dfbe9c197105bf6a032c75d480c79ff41b29082f1986d26e90f6a57235658da44c5985ce9e427697e168fe57ab5294e77320885966c053617ef5d084509a309f8626ac1a6082875f8bb706782a999ab2d98e689a4a70932c3f4a96a1e8044b28624014035a9eff29c15d965048a191e78772024a0f68ea9f0fc4ca8d006330c1128aa9a9ac9425145347f96609c51493cc6509c55418f921e0c9bfc1c6c465b45bb1a9bca02a2c2a6d924314d60abd3908c15969b24265c90a94a31c7ce858dc8783ca637284779cf15d255668c02b1a25898503961737b272843271e170680206479635261b66e2626693a31b1dd3e7048e2ecb1a1313c5a186cca7f4e12c6b4c471c531113110f2d39bad000ebc8b2864454d3e4b6d0a1246b4b8ca0515ec6053b90c8b2864411b659d6904041ce013482479a5559d690a0e12a53b71260593b22cb5a155be672c0ab283c74e0d0a3f54970b6807b4c1a96b52a4f72fe484399203c39f3d643213cb4c05124d1f22897f4286b525443eeef9f35c89c99fbef0fad50c55312a799fd0b0e9120074ab3eb843393981242a40415646be46e69db32d585ca76fa7b8872df21b5052cb32b040bcc589ad9ef69fb309ce309d3278fe37c565186e5f36bffacb09b16e5abc8c023b56e6df5e06816c164abd7cf831ab07c99bb852c6b4520c9348f34e37723caa791ca52d6c1511ba1d4d3cb520a004d33d0a47497f8c9c7f2658ad892251879fcba02c1dd7d4e2b93fded4b4f9461b9fdd91e98b962a96fbd002ee0d148a674f543a9c704b66da891b96f09036f9ce6c382b950464546387efe9eccf190ca1aebd6561b952c597e361a2a621ec5a8b6d567be3c5bcb37e7ec615b2a9ade94d2d456adada39b9ea0113a266b4648551a952d79946549c4924cc4960ea454965fbdfb65f00ff8f38ec4b928b8ddd0a57644152a33f93e12e79ff002ca852a5b2445cde4946b8626490697272172f004c40c6b787f91299567bc60065150aab480893472709fa25b4120e10213313859624412417c914653104250a1c1d65e4cc941a7ba49652175ab8571b484098824b2143106922442425a2c6a33e416336a6dd100420553b088e281872e6ea6688173a2b4c0640b173a38f961ead0177a3e4d410286292ba033e0a323538a5c58b01e7b1c96e1f0d7a2103670b3244a2a18f744868ba45f7249938e05840c99e338ee599ccd1cc7715c0d4cfd9235ee4abf640d59fa25fbd35032c3540d6098d190044651d1d1163114416581618684b602286450b2a45f32734a9af44be691c33e5388f0c771b521bc88212222c90af2086bc25d96b52447472e2c6cf0c69abbeccd4fd2b428ff956b720b48677f9b3b7b734e98ac45297ee720954daeaa1374d97c80cf60ad2033cf5419a84b7186524a296d77984c75a3474f77da7dd1e4eeee3d612dca27eaee1e1add17f80b8ebadc599352b74fd23d07ce53d1f608d0cb09c151039eef7f1b50bf9bee7d35046f8ba56bce564b5f528bc553554adbb097738ef321da7476aef59dbe676add86b27299edf4be4bd51c95a2b5be02e24dbb045fc7b26c4b82e9f9ad25a0b467dfa64ba2ec39ce23215ee06e68fa6568ecc8272212052b72c32073dd42427071032d56da30326256060da0dae09246134ab371d25ce3d7e35e5701f23893efcb36f2bdcf61c9f73e4b698c7cff8748caf7ebeae732c1aecc8932c7131eb93cad9c21dba7f68590ca96054bd9b2b89f4ab370e5712673f4f1925c8b4246a65296c8f49dc854a4345a76ef798de827187c944e144c5083c28b5b1b5c6081640a065048a0c48821ca1022849862835a932e8e90b62646c020858818b02c79141753c6a83d7c90f985ec5fbb16c413fa7d363300c3e33e9f3e80e092c320aa42c905509214311b613f9ed490840f2ec020098728679860871fa2f8e18b2c9aa64d6f71c49729a6f23019104bd8e4e6b82a3c39737ffab997b7611159df8625f8ebbe91d988bb49739c27721c51d6e2bddef5c2499b9836a9802a2ff00b388dbcf842d6b1ee3b598bf7650da22cf9409b5590ee5065a63a037b4fbfee5feede2bceb7a1f88ed31a626466c3f7d77d39bb8f9b70771fbcd96b39bbe94bbfe75685de0d1b9919998dd236d6dc9fcd28384efdfb45eefd2941bafa7eae54298430c283e0ef3dcd357b21acc57185ac0aa5c42d64e9ab9f36a105a4fb169059f37c23b97b2399ae80cc50caa290a8c46c18753d0a15d508000001054315003020100807c482c158288c3361f70114800c76a04c6c58990ac44990e3304c21630c2180000000010364668666db004c017a926d11c4130752b15a602131317c6e6a4bdd6fedbdd5d5e16cb797238b705e4ec4bf2adcdc0e0eae7e5e7490ca1e4c3dc2fda0b0a28590e934039af11f15557dab4adc8b9b003345e28d8e3af03ee2d02dde013db3a84b0ec17709c022b6e773912f663de7ccd62ecf8a48e21c907ee84d69d7f3263bf103d40285a56643ce28ea2b488998947048e7732ea6a833c311084b4ee013ebbac7a78e23838c13952dea58611784430c8aa0834c51a9f7fddd673d9a64cff4319bbca0022503f0f72e7c75ac58e032a7897f8c3f98e51519c54cc83f9de7ff6c55fb8badb0329ea7820032f9407be11de6d0c92af4868ff8273a2321aa14c5aed3630fae9421f1d729d7675c21500fead410d52de529413584a3ec0bef92257cfa60d8dd07cc3d0f48bddd7cb5e47599d650d364f6177669d8d7639fb59de51bdb6cfcf47a5f3fa5daba38e9627ae8c75138f24f3d4cdabde6e8eb7b1f26802aa06224f90113b5faef365f48b2a1fddf47ad771bf959cf960c909f8a4c1a6afb0e267ea2893ae4df3ad5b43a0a2d3171b5e71576dbc23e791ee7ae884c7ab35bf2ee467cd9ed31a2212ad5aa6b57636bab883d53eac9d91dcf306907d3d6cd678c22549617c50af3be4b66a5c29cf15328250ad7037d3613a78a83f4de9796dd4b36b353302dbc1500c040e77ad9565601c31d0a071a449edc868e57293a8e8dd58ecf66706ac10c6394d424cada709f7582bd228b2f43e2264fa399b43c7d4f4aadd21e3f22ac07f53a923dd0e303ba9cea8b6817e7e73a14ca4a26c6a875850386e9c840969d532704dd7b0a91b57f7db8e458cb8917a983a4ba1a9090df24b5dee710432ffd46b8f5676148313e05710998a075840e4cdc84ab881ee440a11f1bf8f8dba67046c517f0c2d9a6d005e24c58ab6ba5e9ee505e00bf930079c159bd33a293e57e9ef2203be3a7a970ce0008565cb9ed2597a5b215fb090b898aad0020af86bb4252117072469bb2ea4c19867126264c4787b7192f5ef6f100b71f451d3d08a2f43dca4acbd29b2bd614d1319ebb84879c855d46050e2c1219af6f0a60ee564769d75a910dc0e19c343600091a02913df1994fc869c58af395ed4231381366d87717f9b5d94b6ea8667e3bbd64d2ee654e35be1746c384f638c1bf66b152007b68c2df56078912faac660bb42b38c45e0f2d93053b3453cf38c54b41577fad46014c2f2f3057256068158a832359c26d65d04231a166360ee69685a434657240cd7f0b8d7f0ad286c3aebc1ec117092b44e93104f087293944a306d0c0f7421a4674d503754b02dcfd4f087ebf71bb2088a23358655741758743d972f39223fff673f510d516a1835877b2b61992d6af8b6fb7ca2bd55655ab098d19cca6b154826e89791a7ed8e274fe48b72ccc05a397e22a4f812fee25252d323fc3ed277932ec1cca62c2c1a4459498495217297183712ef00474981891374f0815fa8121afa132c745352e9952bd50495d334fed7a63745e2fb0337c4742146594cd34a77cb18603aee20502a81521fbb294d2f959881695ade4b3f414f99620e2c9473ec8da7bde60177c80aacf30699188ad9538d01aeb277e58eaa68ad0444257803313284b861bc6445c5cca2fe8553c85edd080677c6cc114af3011736bec038dfdfddac6bf5d6fb9132473818a6e29aa5749a8cd9d5932614ee8df262b091c226c7413d1cac9dd047499af9a478e6595522c8b060051bbc47f83d7adee0297f9a0de60e0b1de2b9d514eef9439bba12ebb5e704674ffabedbf20b14e6362755e693e586a2ca23a7f575c44ecacf5cd223e172536de5929f1206d38824743f5ffc1c3d5c776600c436d9ac9554de13d3be8e5d76b250616fc39bc71ab038503cfe5cf2890a72f1657717b04f41a390f6b9d204174762b546bf76a86c22b180deccc53e9a06bab72b474738b97685559b0ba0f2ef220f7a62ad65899a15fb0088cf0b8700674d40fe9b90298a0f07280a5f4aa2197b2c8ea1d33cfad4b1d3149f04740a9700aecb4298f5cb9fd80909fa2d78651d76bc083f97034d553d160c483f4b764b91500be3ce82b55265aeb202964cc3d6c6db02665c8d7164a2e78359a86dabcfce052448001b226979e0f19cd0c0eba44a73dce23ad60dff14ca36595dc08be939d67e90447beda2bd67098c4cd67e79a0e3c7080016800f14d38282d43e0c52587ebdc49cf86c3418efa90828df29b5be027de6949b63e3e7861465281c913539106a84b4d1ad2ab1168620a61693b38728dd6d8e0ca658a503b07054b1505f80ef73233528db049b149b8144336f2d8ff7a4c1b4739cdaef3eb0e4fa0005373620e21c209a49666f8bd516aa59f99f1369d8afe861212420ee38976b7c02af828851c3b07f15bb958a2ea513b0363785950fec0f4d2fa11de5726875cd9667b462dca1259f9938b18a081001852a1bab7266eeb1dcafe876220cdb089597606e299e28b2207151a2a46103f26727379f8c3e04ee65e4c03fda202d1432f0433ac62bba56b43c8c204bcb59cf38d94bd5d06bea452472cce743b002415c7e307453c671d5abca31f16b5f49c7b2276718bb969d5c8ea3d9652097fc53a48e56515ddca49ca0859e6de18fa96d5809896d7801f276336ccceca9406e949a83881a0522a6553b670982b629fe092ed4a78020bba267f3d1976d8332a6a34a711c618e9150d8e2503ff4793104245c6d83a694b7fa8cdd3677eb3f875bd93af00071c25e5d199c1d063a0c959a802b07eeca10ea1b9ade305f6662729faca631c883054a90cd28d88ede198bee83f7b094213015848894f395a8d77245faab9a6bb64b2944f92e5e711647adebbcc3188324480918b0a894f5fea43ee3cdbbd2edff521edb4a76c1c5ed2134c2e9d0891eb8e130d4a26729c2de5cf78c6bf9e3ebd8dd7b9bde8d333e772c264fd67a1ea8c044a80e021b2935af19f187b1a742b2ede3a46cd291e637fb75afc134c29e601b46feee37d71805d1c1f114a185ecc516ce52c15a6092294938354562558024350e93974693353788d82b114c9a40ffc49c54bd8d9f3be76f88fd22d86d3620f2468bdc4c1acfdecf0c5d7cd55358850d763c71b5f69e6f1041b1f93719e51e63b377fb80d6903491c0cb1c15e533578c39c8308a4e563c4ce89083fcac71ebeb16f6425f7dcc13d46a6aa425aaf0166eb0cb7ceabb482003ab5f5c7fdddf2cc2245b8882250ef40b554969da5eef77b35d06a72d7dc3d7bbe30fe0d21413979bfb24d6ab82eb578823c5cd35dcd6089fbfd20c71572d08b7e18f25b7fce1799ed72719e100e6c7efeff3a84edfbaa8782cfb2e7323d723aff626c518a1b57e08d2a06a72ac6c023be3473bcbd352a39e5329403ab710e917f84f2a6902530e002fac490be22e9c306873bc1a27d836d3685c8e6aab6acb39278789aae14c8cfc55232b00637c97c1b7be935af4eea83c318e7c24ffd5d0c5aad86943d393728fbe8c950320609110298e5e824bb697d49a8ecd67f2514f0644a9c83e619a8b0be15e01f0d2225386aafcaa6107bc8bd18a4c50248d1d93fd9803ee4ff6d83d8301be4b075e06c147dfc0397494e036e578be8f87e2560063978edc0bd0d10d4c45706e5d23388d3e34509b34bca40b3fa5b46402be816b8f51e0b08c94b3a903c6c8e6003e84cc41f43013f4cf1ee8cdf1d0fbc02d4035e3c23b207a4c05e1e384a719cf86328acf0a20f62cd001d94ca07ac1347884f6d969b0a033b3a54dc6f34f75871764f0066385ce2f876fab885800cf7cb5322ea5600341c142ba9080319bc4e5f6b29f789ebc4ce8b5b430a90e13c94c8c6a1b3ed14057bf9089475e539cb2b3aa925b7b8c341140a4c5b0ed350335a51db5d1262d59f9e928a8be51edafb581bc3e19a9ccb3f9ef0ed1cc78f2f5a6b4f2b385e0de044b106d1168e37190e2f6fddf75ed2cf8df1ad069195e9fdd41b7bbb40075185e892bc5a9f8e184a0275a14700de0acadf243116d8bdd5939a6855ed12f4ac4bfffdc71846e14159ff336a882f1e0ce1eb09135c2286191ab442e72f1350cca2d46b25df49d1c0f0d89759378cef2d2c99fba722b93c91a1f668a875de987fdfc2b6c0191091d30627281f3ab6c5d4def14eb50d49bd9e7ef7c5a45166810f3f166557195326008ce61d20bdafd271149cdd6d57ccb60cd6d7d533904c31e77d093f5ef31775fccb9945cebcce8b30dda364bdbe53c8befa3890365ced336991e817f8cc0d126a8aa869b936325f674313b7d4d1be411e13587dbcd3a54c23f09db4a6aac38fc5c9f022e67e7a6b49854a5f9c8bee1d178f6a9b2343d4edec70f245b98fbd30492c6d73054b119ae38ce26cf797806331f9c66ae6535117457e795ffba70ba4d78453bd7ea1bf9e7c02edd7fe6d170f94f77fcaee72d58c6ca1cdbba3e1938f745a402aee7c3be888ac871d91d90a324e9ff02087f53a34306065f8268792141b40ea7ff6c704080c8d444ba7ea4e00a1d908477902220e53980b2b5f1cb91849e63de2040e98f9c0f575146f0c5831c0fc5810c58d1c72034e2f975fb3c2f38ad01354c451cc46349e2157f80b1556533f441b2bb4777a00f5a8996924786f983d9d4b86a55c31a759080523907512a08071c645543093a21c1620d7fb0f0b2a1dd32db76e030cda55df62f92d40435890c7d95b4580baea0a73fd271d29bc9fe1e2c202fa0bbfdb29a3d643075108e5a7ca8fbd83968d7381b71e81aefc06852d3eb0dc777649974b6cce1fcdf1c87596f07f53f1cfa059ad22244b50594373331c4e16edb347d216f91212ea80a85213b5453adfde0b3c7c558e397416900e1e34954a69c1457db9fd841bf9b423f90f51c535d09eefb488938220897b6ef09b53b24ac6371a90d6666a9bf554ca7b104a354a6d92d40cf78175ef369df4f59b55711ad8da75cf388bf3a18332070d06f13009d016748f0f10de5aa10cb5bf3a28b897712995d9d24c4bcf8f8e1e8b79843aba9e5b35856194ccbf5bb1c2e1df20642b26b10f86daa5042493564ba741ed55d2910f58014cd33f7e39536c6f30f53c991503eb4e6e6656c05d25bd63dbcc112fc01e37db561b2d48136bf69ce350888513588dd10bc66e1f350f05082ad1b9bfccb57ea44fe0abb73932533d10b12e868e1353895114e25a5589c9d21ffa4547d62aa41053cce8b520af91188c1f86c11ddd952c6b4950d3d4366301177d2874a7be89032363151670d5e32be5d7f0c216dcd4b945793d579db606315aa73e8a2b40e888407677930f5cbaf20620f6db8bd23dbae14b5395922be99cd78d3c165772fa97dd79132b12fe97fd3c122b13be94edbc186a25543871cc3dc04d326168ae1eabe77dd6bfd5aeb429c189fb7267e0963a8876ad3967c42a99d850f4012a13a9b3767fcd8cff02bafa02ac5fbebcd17fde948abd72714d14d417271103577f5e17a92919ed54d847697fa429cbf75bf362746054cc11bdb12c99e0e30c9d642c165e1c4c7cd50fabe53349a76cd801d4b47a7b4e880b4135fde32db5a2af80f9275b688828d31218924a1130899d3df1d1a019cc617be01f138384e3f53077f9b2dd349236550228e196bb32f09fa2bb62971c86108a8f03d88e6218a32045de85c295d2eca570ba5889c24e58e0069f61fe7c71df9cb84f20cfe4490f55c23f7b9b5be86b14bb4e9fead0d7fd6aa5913f299bd0aff7d1162232222100156d4bef93eb971fce8da9c86e85403018ca543470ce1070c0f1903a4b4f81e3a4832eec2ca35e24d0af5ebe2c44445e67c9fb3376e57be0b019bbf932c7d1f807a98cccc7ea40799157291ad3adf5614fb3241fcbf774df85266827242395e5c6c7db16e43fe3ed1f0c8f45c4c9638a05dac9fc592f75b0b6616967b08e7ae212f5ed86e30eb165fe46106da8e89ca9b6735e87c0b56d0ddf3472360e6979f4efdad4c825a83e42b51cc102af56ae669461bbbe8b8ce62b7025120a5cb89ec00968da1ee3b33795bfd3b62331675de40d647969f70c20be0cb8ef85a6e395b46da489e2925a0048a90931c944425fba9f5ba5b0553cdae47c347cb6ff9f70c84a139a250b20dd13896fa3ab0acfa4a63620dce2038ef53b7588789e437af9334ce62318f71bdb3e5320cf1fdaeb1b4409209a94f5e845e6e2466e787a43653a45e699da00b24270d41c9443053a7ece2c445e806b734a508a9ef9830773401f87466925e23d4c8f8a426206b12b79e9d168a9feb96ca9eb2008cd67151eb7829a300a35eca2e457ebe5310f0099df39f5af407a1c728e5de5fb5d8aa1e39e1017225f1fb354f3283539a7637bb33f1b86242f26ba14f466dc3e958bc5eba056b83862d7320ea8b8f8275d3ffda1c2f393e63f51cab4403248b429984f3ce9242fb063ba9abe9a537481762ad615d85a916a7424bcf2144050bbec91a74010a490a0efc936508ecd3879409fa895e1c9a74ac04f92c5880622282594d5dbe3785adcb9d5aae6ccd484b05968b09be03f194270b6ec1ad91383150c3b32012d671f45eeee6cf5159cdd75b31566988ea1dcf71ff5899a463e18a491101d0583d9e0c8cf25ccba4a870c3986e40f3c5a6a9db7a4e0bdb606afe3b54278040bf64eff4df3bcffddb87c8e96913529a90928db9500f2078ef239c0784d8b098a4c107e512755aad461fa556f9479f6858ec1e5c3b543fb2ed84e7d3166f267897585f49a82d1ceee6a0098e1b641bd41696ec4769d54b20ec66946e3422b4cc84164ed17d80c2b1eff88c2e6c9119c5e21c79bf44d10a10c41f087b334b1cd184b98803b46118046dd78e73249215983105474b3dc4808ae4664ea041ccefea314ba107e256605c3251cf006943dbf3cd83d74ab670d73f682f3d06d29258d1191f8e27c904689c3e03598c205d2cf4e629c6833cb235d38b69c6638e43a5b23369ddbc179d99257127c096e2d0e40befd408fcc9e3ef4f59b1d0d8d304c91bd646c83645cb80638cfb133dbcd30457ca4a20794e0e760f0a90742324bd972d91d3d010920d4ed481f89fa033cee09f2cca7b121794e9683662c25910e517ce90488731969ce3039a41d5b3da47e89b8c3fbb675e263befda9874caf3e889128cedfa97f869f6520c36c533cb88928084452ecb5c9d6d96ea5a19539757ab7b6e4d6a2625a7fb33eeccee8ef73352984b58df8de3097082b3b3d6c17ad3a880928b12d579e9dd84b6ca7a3698784d2b97bcac69df9af3f10d9c0313658530f734f944befc9c36270f3118dac9d5c58345120bef2f4555e63ccee7cc2649b5009a4545197a357ae0d676b5866a17cb9deb94ddafa9da32ffc806d85a23478a39744bdf654d5d1f22bbc89e39cb1782ad4b7228d34dc3ea823f2f0c8348f7e69d1498071936121ef6b5c8fa5c65d36aa31800cb83cbdb90f361bff8f2c2f77723d5da47eadb10c94e495277f6b1ba6e7543a89b1eb92cce5cf205e16cab4d2ca73a2ca06b9f57d3f01ba3148373f2ac061d7436e536fbdd67760f8543c511a5a20c87a920e491205c80f51b9828bcc11b404b12f3a1c2ab6dfb7bdcb12e2afd9f7fc7bc34c1b4c6a4e6a2a0f0c214921e296273ab2bc0fab0c1f2a927b543599d9fc09f2d5eb966df51d3de3daf8db64c652e7323d8256e801f42b835347751c98bf84c0b5081d06d49756c5243a5a0f70c9b78d7e987a5e4953646c66198d82a77b5c056ab3fc5c8bfe0f36560fc09415c87ec2aaa6367d44bb15e0d1729a0eef1849e498a34d633a48ca79d0aad828ced8dc1272a4a26bb595166bee70787aa1683a8596f947d6ce811719c964f5a1d7d3d501879e606c64de0027af56ed5b280422ce1b7c3caba3606ff7da694858a767c166d41a090d808c6c4403ca4e684269decf29fd835f4ef1a2b3fb68f7392c9e34463d458b26ae4a499a5305ae680245dd638d9463b440f84cf8027d0d21642ceeb8a5ea662c406e2a1abd725f9fd7d666270b266b2f52babd7f71502a331f4541d7f146f5ecb63132261e912fc5d8a6c81054d8d1871ffe0fe9e42c0c749adbc8ecd0c852d749731bfdd2d454508bf17f54eeb41491e70320ba5c5ecc46270f469e39c1e6aef61eaa31ad082e570d9cc926c154bca27e101c8ed568ef07901fec56adb450252acbd1e42b81131e17698e88ae4f917b490f70326df19f170ea0381f4784b939961a67f7f7e88eb995b6651912d08857d964ea12f3c2ef5be033ccb2c4bf219225f7ee86ba128891ed51608996a512e45f6e8ed51d05294d5cb6e4b425620e3a76a90e550a086d762f8bd2f625682e777843c857b7f0328f800d320a24b7d3cd3f5cb24e0be57398484e445ab736c8e77433f5a6708226e4571903d1577139fb30ae5be3c717a1803a97f0455f1c0560eda7586e64fbf1522e15b4914ccb7fa9a8292894a9dba89fc72cbd88e19c0e39e425773d523f9062f9299ac7ab0f8ad6fe80805ceeebf15fd98a724f856ff0e7c0df547dd6b1c254f08adea0770665237d623af3a4bcacb08ff41756cca398cf7099cd512a7911db71788c3bcd1065bab756215bf19b16cf6d12d1de55a041a82d85b41afbd71f984f6cde1251b3683df9abc3a0f8bb925e6779b1190779c0568578c7f42cf1ecd889137369df11693abb9f8479c98645a7156db65b8594ed4d5aacbaf5a7ce113b97818e89e3b07dc2e3b89a25406eac687c7ac6766b9fca2bb191bd8e94887d72f0f2401b0834d01439d535828ea8562f98158e8ab14d8ec7e8c46f149d311c8077ccb7ecfbb994f797bf6158c6aa6dcb89e79b15bc0f102b18ab8424f56517615047594fcdb0740d50e18c398091a71f0cfab3a82d2e927554d75cc6e8e1c659543cca5d97908c08f517da2ea209497db7ec357e4ce51fd4c5896bcb1a1e3b6c4e1c053033aad76f8839541d9dc63cf2c4488c59f519f74745e5c7ac9b4452bbda09d5d8feb2d9fdb47461e5418086dc782e249d6f2a0678ebc05760d85dda602299e861afaf85063503a1447ffaa7d95be2ab3ccdc4510be89694ec662826f9cbc0740824d01c172188cface96c3d3539ff1f68e23145e00d53419f28fe041d15f76439bd0189f905b74b13179eceaa38cd88ae9af053d3619408677580e76bbc1bdf3bc7f50d64b7986bbbb1693b06ed7ca912b26d7767ef29e7e6640eb4aea4dc52576b6ac35da3601e7d91a0802428c1dff964cfd7ec4dea86bb76246c68e4ebe88cb621bc276c222034f47ea94c9f034abda214e223f2b14761d360b9bfc2d601ba2b79afc2b8a4a5bc4a38ca3b43a7f2bd65c5acec466491d7a6e48cd4bc968c118933aef80cf91a494df6a63c18f9391b182795c1e63486699943d26208898cbcfa3319b3839dad941d74e65b486f6ca87e46340b8068a994a6eafc66ff4038eff76c455523790438fc9cd904dc2a98d4423c375ee3ca35f0cdad3061af257fad6e753822e574ff35d70e315d9c9c1907dedcb1a582afb03d7c047b610f286a53972e5491904c096534645375343a0389d1c269e581dd1b9e62e2f4b36ef6c11d2be23b2262687252e0a02cb82ced1b483e3f729de7a6945b82a4db702bd8c865bf32c79f62cb40f67b2ec79e56904c02a7701320dda59794f164560b9a88dd8cebb55ec930e83b918d0886450c2d713ca464aba4495feeca9fa12b06967512dfabaa03549e0d42acfa4b36e5e12a888552a0bdad5de48feafe3790dc898745b67e88239983892881b711d5a0ae2a35c3b7748087d670e187c0c3b4de8691d82560a11d3ea96a4512352fafccf99e4df9885efd888719b8dfcd3111cc41dad3328eac01e1d9510457e6e1c8354e57c70f68024b30e4201be2aae72c3a7cc5f589bf80f269e32555db36e2d5170e10559f33a4779b7e67ea4f3dcb5a5b743921878d6118324ea090295b22d151d9ea4215dc55eeb0855186b7667c8b9326c842b6986a88d868f89052d84666386810829456e3a4b2e54e79c17ac5e1e3a46eef7748c97bcd44a224f6b2ff5cc9734ebb59f18f03ed87eb7942dfdc78570f9c6d030033c194e35ca2360614d624348999e252882db7744d8e89a98243838249d8fe00f31359cb2abf863c9eae258629744c1ee4ecf991ac82f8525d702ecb8d848558dc87f9f1bb85e2250d72f7fd72cfe5d6da74ba9aac906eb921b2c539158db0a0fa25805421cf4d99bdca1f4908f694dfe0253a76e94b0ebbea1a6bc5e44e2afb5e13c6566e19781d91ac399bc8ad63d2dd7798e6103a13c8742660698206e5547eebabc9b4dce8abd711be7b70f1b84611f984de9532aa0a9a6c4c1946f47c03da52ab633ceefcf07647f5afd6302b58bc5868aa3dfde13029b5dbcec2afdf06c29331da27032b7fdeaa106ae2887c70bd1b4290118e03f3efcc413e1453d5df5c1b84107b4d72038089e5794b5221f621b578b0b8e7f01601ac6ca28f9b5a0fee3c6e1304a0433bccb8ef1efe16609cbf93d8bd40a488e47defdc8eaba21a35ee0bc5a899d9d912a8345d27277b418b6dcf8a1741bac48643295c6391598ad564c59119a4b1f80c3d467b2613cb7e86d784999fc0da153ffbb4831f17ef4162baa108c144d5a01b2aabc67aec461e5b5cfdc56fec1b99f43ba332aa8fbf8fafb4eb0e3898d675409863dceac1d5f33bc1a83be68e6c192d634095c99e8606c643c620e391d7c196e1761b6be9ccdb4e47215ba8239254f65d298bce19d43306fa3d272f185bb6e364cabb81ed3be7b64fef67ef2a0ea9758e234c4e81ef7aea7c6fee2a30f6f1816b097c715f0db730c56bdf15f97caad7982c78190c2552137613b9432f38507993cf7d3a5bc0a9c41219222beaf63dd5f7da41cf6f0bc73b4757273e8b211972df4aa4c5203632b7e69b695d2faf7982d7f6f8a5845741c1c6ca70785f894c6e8a949897b1b1a762513881b50c2678fcf15e0beb343c3495627adffd1eba117babe4c7d63825510684f9d79c635c336ac42f76e936ba6263f32283fc2fbf0f504544d92d7b1b6a427d19f519478bf2c401a874bb7961b07c75f928d5aaf75476404d5da4165c3cd0c9be231399b44cd98f88a7d2ab508a4666df9a0a53ec189603a7f03da9b142194317ffeebca9e906aa393c2d60c1cdab03e964d5eace908905a7401d42ecc7c45ff3b2679e852226a5d7b6ee28b194f1a14c2cb893a4afdf44b500aaea1ddfffe18be797da81152306a74d94653428393cb9ce5934e492c7c75ea67435c358005d3ca93bface5466aff3c644504912da5c0772d03699ec4b557f1e0ad508caa74a6b83c940fb7af867d6a5ec86b7a4a86c0f4a22a389b931d704374dd6097cb4019ef28c40b10f2bca6067ab4def0450392794d6a8009dd75dc430ddf88e73720cf200d45f94a16faa6fbf9cdaec792111d09e85a65ed648eccf6592c33b421117e975764cd777704c37ee3caab3059ed1832bbd74d809d100c7e34eba8435831203b5c67373c19c60e33d05cb31942aa3aa69fff807a0d84c628a8c563f5a8d063ea688b896e71e6d2167244f37f2f90242b73833a227499a2d6d48bbc24d422474e4ce59711b708f66ac7248a793ed7a6a8d0e066f2be894d3155bb842108789ce7c6f4de5804dda8636c8ee73e9dddadea52a638403fa188f602b8b49a4020c442822548ec40d1f24dce0084013843132483245c8ea1b8e1abea3acfdb4f596a230e0ba9bd61b106282c007c4121c1433d5c599540d7043a437dc23807ce743a6a10e53e2416a1d49dc3c12b46e78c0356a84925f2f0a1f31a67bbb8cbfe6383c24472c0a95ca959ff3452f400792d954fd963ef994a8e5edf3a19e6c725cc5671ed411462befc9df16d61360a847791b60b0f9c5fa631fa14e83145d048aa3651ea9fdd463480204837ba53afc8bb628eb5bfa084021a12e6272087560d860f68c2ba3cc560644a407f25ee5cec10f7a4b96110e5a6d9b5d209995113a6a207f74b71ae442e413b8553372aeb9bc2d2212d2bd430b6cd61fcdb6f6af1086e3c5d9740524d216f0e772dd8eec979c1908b60d8d89319436b3871bf4741e231972ebe415ec1b0c06a916f8a1ac5c12169013bbc211afb833c18d72da16e8024880f96814252323439fcf10c749c0bd8f0f6cddfc8c95c554ff5889e33d480a54348f342c9f3bd646af3b94fb4c6a940be80a6b6ed8e53c11c3fe47cfa266b4d94e4a593614224936cea2b44dd5c08fd856db953e76a160392063b3dd7f53ddc9a92dcf4cd524bbb76ebc33a1b49c915532e630f949779d6b9c64c2f24e3c4f391d144f0f862c6e019e1bd72c843913631d022c3017f24c3dd9590ce7b3f7bc80ce1a9542efa4177493aeca3dd4baaa715c93f9db44ca5485c0b4945f461610d05d71ed998cde88ec42c4acb29529a90147a94d29450cd13ac8118db6038cf787bd1f40f50930d0268b0b6e7b48eb64719c9f6e426e2c5181891a6dc9fee4f2695a5a1813d17b713f32844b881355a24cc6793b018c5af6761540c175cbf0d514645283c6084de7055297fde1d3c0714b945f310b5d807e8b4ad3c901b4c19aa611438653a13b213152311129cbbae62b5e475f6856411b1a25cd9d20490bcc4c3da902e7fb163c0888a03d20eef58729c1d83091d2fc474ffcb8cdc32a85e32c22b20f7cd4496f109957b86d90aa6e3f5c104542223d17c24ece85ba36737dfdcf174342c1c857f5d2b69049abc20e1ca2f1a0529b119e7c5dab39fb55af2fdc41d1cc675707bb11870554051fd91b6119519988da4974a9b937109f0282ce2ecb8b8d12e8f90035bd4a428f511a85a3328baccc04a4dd78d13f27ac271f333786dbb83c4a2e480a5ae31d2141ca34905f7693eafbd601055642fbdbf3f8cf69895760db1ddf6e200571e38b0b5e8dce929eef322787033fa7b599a7fcf40c174d33c06042b141198406b88252188916cda6998573daad66dea074f9fc3443c659cad55d1f06323220462a1cc493b214a59cefdabb40e4bb7bbafa53636b82e6323d5956463f5d0c780dd19c86b1753435bf5deac40857f457ba0d713701596919aa32b5273bff40aaa89a72a9f192f588c1ae36e7f801ac3328bfaacd29f7c398a6eb9bef6a2b90dc6df9d51ed25b64d2cce305966a5027f1408c50538c6eab5329f0ee810a2ca86364ae694f01d1e950b4a340c33deda96b9d655c8c2ea8104a2a80c49d464a0d4c77231d0bdc4f875da6a64b3d6b1e92ce5de23d7247e55cbc18cfd73d0ce830d06b0cf8a851bb7dc4cc1a2124b79dcf9fa261c03fc41990de3964b96fad16452c29e3c86b29e8459f3acb1b4c0e4441f588e5470fada387439860661c977fa964ba9373ec8173f98f0eaa34e86f40d90574a009320caf809218de809f810232bd73de46e9b17b8670527629a49deaf5a70859ab525c7a99fa53880f45d5e0be5c8011cb001d6012e0de61c110d3462cb1955c0c906517f40c94cefa5518b4ee9b0c39dfb0ba9217f0b3882611644dbb1fb668fb5cbe7b1a97243da5c471f05ef44d1f17bc00c821391caa382f8ba839d3adb1042c69226425300eaa06236d29a2ffa5b314a995a747839279a2508f22a085fa6de4c9fe55e10f4a2e0af2b84ad6a39aa3d9e18bd4891c1ad123135b07a568ec84171f366875cfca3e2a76a48dc5c4953837bfb583eba42984cbe7108ca4c615701d6b8686732a2ed93188b4c78ae5e52af89c517a0ca58f2dad9551169917c4ecc6ba1237ec62065f1a11be2bf16963046f7bc7721f0ac1c6a3bf95c7e33cc7a3ed8c9a91fc54c957cf29333336e9bc5541f3fa4ecab2806ae5a9f2411d7a1f29acce32b80382606bc7b83aa9f2e114d162fa62b997cc150c938453b027872da5824d66c5d399c86b0576b2d2634740a8c83a48255e6307e2ee049d6621f98f809a5bbd3c5897106fa89236ad9b0909a07fc27592fdac3d437ba141d7694c60524ff51469d5b30ed45331dcc791a49cc8fb48590e3e64c88ac1d008586df68bba7eb5bfb23d4428ace187ffc9096b2ad89358e2d051cfbac4a04f9c1d31088dd4004d816601a0de651f85bd6c04d5947229d147707eefc0a6a5d7464497425696929a7aa509b0098884d45f6032c25cec2c8f69469f9cfc1d1206e39caa91d6409fa82a66cdec0de0745c83560a8db3b14c81ed05be2532b965b5b39a9ed917da9682b5563a79261f199425fdf392954c00bae73dea455576ceb5f0ea0ff9ee38e6617a11333e20617111bbc4b428d5373681362f5c7195a241a5f100147544ca3c5ea5e3bb0477e52a82c708f9cfc6d7ff8cff1c634254091b935c8617a80e28e686dfdaa7c0563aec8e8b1ba9c2c43820ef303372c745a19203fe44dbde40de7caf7fb598a332da1604dd35e9d75e7dbe9994f2d5296f6e3ed1505b5f21a79c6ded74461dfcd8a14c7076932d2aacac939436395a6f70627eef555c2fc87ed38135fea8beb35670a203e6a187354a7fe69fdc7f9a96bf4c49628fa0e15a9a1b0b0310f90a49b9faf3022b9c5d9c1dbdf88954a2161fbbb1238c51f765ddb0eba0d8cc56ae8ba26689f17a28f301acde996de70161bfd4ccc64a1f8b63adf66add88d6108822dcdab373eed0eccbe4d0082eb8d2e9cd9ba9d7a9fbfd56b69795257dc018965d1ab6ab03071dec5e1bbfb55500529ae3f87fe237e9dd2831c98aa4c595460437db5165066d6523e3375d8fca4ab9159e1eb003b665e6808ef863223cb168f706209c85ef362bef9000388bba851d48eaf6fb2bf5d7b2591214df8f7a0a10158c7925fb783df4a598d5ee7d146ffe9bd284aa433177776e0b759f005bc92a7b63771955ef5ab91363af29efbb7cbf3c642c12937b464e0898c1a64971d60aa17e714bd048094047c8d6ff4d939502ccfa4c51e875cc59ec35096d944d4233d3721b8318f96f14a72ff0612faa2d0c16e310b38e9abaffdb8fcdb17e75f28280f58ff92ea14e521e2f9d4e960a8617e38bd075a2c2899f3f088b255942b7a6190f602fa39333eedc702bb2403f930b21bcadd9b4f113248dddb63989634091df8852b2af73674c018819d333602617a34c33dcae5a28127e8881856f2606cc1fc3931e5cd83ca29b023226876736a5e065dda5ed12f7e7fc0cb2384414000c8699ff65f20781c65c44425318999c9a5a51662182e9bd74685e0fb28465059e0a1a8edf90c94f7bc42a1296a765bb0a4ec7e003ca59fc65a7f564615f920090e8aea3ed76cfca75994ab71505ad9b8e2891fbba6728c5c7618165941936f85ef0ca2989c5c632ff2d6154b4d142c3dc5241e7ebcf7fbe9567e008a8c0cd2bc99d1460d3985e853d31524c178df6faf452f7df013d87fc63b62f268af988c5db1e887141a081c5c66227c683d324acbc1809f221dc33aff29c8ccf94b6a20012378cc5e640260c4ad49ee6e8a0c5e152715a8148f790d8c5a10006eb8927257aa6c08d0b550524e0fd530a59cff0c37ee8054611cf6330a6381ac3d67a30943376e6c1eebf69b783d5636713bb02ce3975a63c253ee26669e139e9b76ef6765ebdebf1c84b32bbe6fd990f0318804993057d2a4ff62ba1f80ea0e1a4754b7182116f636d137b6de62378cbcece344f4442a2de3bb008229a9759c4ae789495a168df810d45ce598eae31d4012070779e9ad4707d3e283eb7678aa2b0e6d65500dfe197bb38bce87dd6e31c02d20e8952dfc92be73eb296b9dc6e5187ee617c337702660719f1fb7ecd693fba16271e854f8c216963baed9f395bea82ba2d47bf7b2c1cef3a8723a17569f79069f2dd9dcad80c436acdbcb8151238bdd5e1fa5274fb8c544ac14235c3d92447bc3937b81ca2943bef413eb152c08271646f3aeb82063e8edc3b3adf486af3edb670b0dfe8586d78a31c5d096a622adb483d6fb08f4d9233e8eab296d1048873ab60ba29368c82a9c461263132e260c1d99c6dcd95093dfe41f04bdd6966351dd7cb90c1d603bbc31a222d1de79bdd9012d2c64cb2d4625a565b4e42bc2f998890fda1cb60de84a774bf1b0644900e995fb96502c72f45d1a03cd732bbb3bb5722db6473168c30e4890d6c33e2a6895f5c07f9a1ead521b321f120518015c9da8962afff4b15cd4181e9af542997ac2609cdf1792b76ee2454b1abeb4941b409726a64535f828a450b221794aff72c5c18f593d5edad918c23c8717fcdd71704eee6f7e4cb47c7b5960af504258b0251ccec28a28895c4f974578cf713e288fcc3b9a8b004f38d5eabb16e11525218b784fe7ba96697896e1ef05a37b932b7105bda4016f18b43ff9bfa53893d37121ad7e4636a573cd1b8298e18dc8c50ed464f1358b9131fc0ab44edd2bcbece56435bbdb05f364375f03dc4109219ce78070babfc15e78a01f2d362235946868969f64dc5d0dd98a3c4694761ab86dbe0bf268fc23e520c8aa4a089f283387eabca356549b78d0ec89b0265bd8571adc27ca910e13d0bd2fcb465dc863e3ee892de4429e35bd6cebfcc25a8c0bf965b0af9bfa88d1d14b208990cc563ecdc1ad977dde2101ccaacb5767718171fe671a5692b848e0d0b5f459347301e4807d467606387f63b301715d18d959bc0132b0c580603daf1099e06f93a4bdeade87e5261e3345487b8d08765dcc7740ebd3880c377fc512552c930b5bb26015c7b3e59aa357385cde37120171ee7f7d427600139951114933222f29254d59ab03252e6e441a0133afe8c2416e705dcc59280f5470e22d72c695f803f52d789327867df1172e90509ad4a49095b3764a6f9f22051c32691cb0c4f9949df8f934bb67b63dac128f1cc1a098c376f1546aef74f30f66a7f1cc315acab3c2dcc74567f9362079f1ba2a4f3873a32b79e38abdc598a8cb2c3b83b01c73040ae125201205ccd972ce49421c6d97d73f13b0b2663c9c2b49660a5babbc2c786f36feaf8353b6d4a772ca1d2edc16a390be220f979b6503e3eb97fdbf58f231cb9003937ee18d67def3c8516b6b236a9b2f3f73756cb3c9eca592c064faea9369f27a5f6f37ca7393dc426caf85978cce3f4e240e160c77919573297a43230c206993f1632d4902490b16232ed840deef7a7b244fa6f8c1d20be77430cad060876a40d0b512c7d6c4f872d0398699504af026c5630d9da5e4b465c51aa42d27127e9a372259822948a1f8ba000ef0f414d83ecf287d6b6080bb4023cc53c83af0873239f25c16d66c61cc0d57a40f03897a00cf664434a3af9f9b686afb162907704438b591046bc8b58fdd0baea396cb7a67641cb41381f9687a3d2b26352e2df11fd7f156271fe34ecd6e28375064d8dd22ff8a49c9baf976df35e1be92eb7e8da67809b0f08713dfbfe0fc005189c00361a595659b768e40dc0729d29a5600be2dac5d1c5696c612d684e900c2228dc8f118ffec61fb389e0e7384cad7e6fb18ae059185bd66f9b550f931a03e136541c0e6fd00c26c7d355bf88b3e002ff5f37bbad73eb6fa078850f1c0bf3838651eca9e29818ce84d071df2f2e373fdc5249cc6a61b0b2203cb2503018bf584e8e95c3dd907f585de072dfe00bc1e62d053c68d60fb2ad97a42fc723105ce3a2c59a9e52b183edc2e18fe83a6ec1fb282f97b9ab3179ea730d3d618015b7930fc18638c2f0a3c4ec329486414d161bf055090eb28360f1ffcac4a20d20f11c74b35fd782e1a08bb07223a3d7a474360f5e7fb629873c2e08f95014a4f666937cf23d7a6dd24599b3b05c05f6b771ca16a4ac1d0e4726a0f4703e2a6dd7eaff350d078bdba3f616adec3dc111df654fcc9716873b3fab8990ff646617572eb93dfc67c742814138335bbc8c71e8e26b34dec2dc649a7a83ade8fe3fdca30d387b67964cd81d99f24842d660f6eebf6ac26716b3bb757f0d18e3245fa79d2bac8a5d17d6ba17d032ef9a3e605f5c0acb488187e1ca3726de24f657d90244a809fea7482b8e8c4398cb522c198140f6f0616fe335bfd19fac099f39fed9177b3360bfdef33891e374c078c246e0f899af60de0a9d9ab07225385f056b09a982953542fe9bd05d979c7e4ebc5d8da859b2eaf71262ac0e4f9239afd81ced68be0e52b35284fadb13f90d353b9383969dd34a211a8b1f22cc793084373206821c4073287fcebc3facac8cfec999641f8f810469105a310a73ce90b2535a4a0f7a5793f14d54004ed3de266dbb58c3c29cd7eb0447d7d01a742275332217227f40ef14636661851272e92c240247f67b61749a299722340e103442bdc42be055c08ce98b796afbc5a82e1150bfeba379d75c44ac7d9e176951c82fae8a482b5f3836c2047d9008ecab0695301e4ce20ccd15921f4e452b15eb53e5e25455c0081369ef0c15562b225424caa7d18c2c0bfcb28ca245fd5c86ff061965579ac1a01a888df2a425d7359bbab1e52b52905b17b1c699bef900a3306bd06b368ca15e7c5b9d0abf5c1e4ccf6a0b178366a5de706a723b9ac43f94a6bdb9ad40a30f5baf23f32b9ca64d827a88c3097d132cf3c7e7046f3567958e9ae462c5d0c2d3767f4528f8cb610acb28b2195315d887356297b0d13a982f5d6613426f2de094ce698f7b6c9ba0321cd813e8be095e800fe9afded06e162bd4177dad0466baaa9c18332a70b81ac2e69f61557a5c631e4e9c38fd2c7a82455116b21664170a9cea8806c6e8d5638a62e6c287fe1dcd97c113b58524f812bb65b34414b1ceeb1a33694cd781a6fa67855e60d53e230f74907e6db9978382123fc69aee6cb6630b527e53c1573a4b128dc3b718962acb148378bae6ba1185c952024203ee57cbcd3f962b61fe4573d05a33fc870ffd31e63a1e7e8e04ec2bda3634bc21de85460fb32474e6cd8e90c4e25c5beaa3eb4fd0d09f71cd13fd1b402cd92d1a6018f02fbf04542302e17607a36904849109773753aed2409e144ee991c09a15518446b0ee78dc4b0703a9077bf8f77e959baf07be9c58e4cf4b3518b27bdcc54facdfe787c3fb33ac5ce3b600a168c861708377180f8ffdecd4e025ca730c3eaa903869ff8a0e061aaadba996a481a6170ee6d86129d118959ebcc8971a3548d9c07a4c5163383993e173be7aa1d4b44f284e2ba73958df8fd22fad0be5e81f1080ba9f55785e789472484ddb05943dd95792acb6b6c55981fd5f87e3a4dfbfb1a87cd009d6d66d416b36988a9f9467f899366caf3c08dda1bf20ee79dd6456a22eb454a3970708e058203725ff901e5213e0b10dd2c75bc2fed86ffb33bb2f10a33de85bcefa819f12ec14c1ee8668c36d88052d150ae40b9c0e362dee63f26cde7dcb340e5e5f98637de3e0f9422300e1fca2b77cc38c073c0abb003e124b5ac4af0160d582658de0ecdb4375461923a279d59893c1b53fb08e725aa0cce559e441e9f6dcef0804d44ba81ca288691d11b5a1b9f39e66054e2b0da9032e7022376f4e8c5eb9aa3e849d4627cbb714af782181d0e281300fd2c80da4d57a9e7591e1046620971187b527f61b2a1ea54d4ee9c934b41803ff9f84b4b23fb8167e6afdc1b279bfd0266c0cef78acb5fee1ce13f62c5b600ee7c509e761eb635f25dfc0de14a3008596675a14fb7a2bd26bf8fa3c50a8252ef691077ccd4d10adc48f957a2ce70ceaab18828cf6e11b746b136f62bf7f1cc06f5d0f682035c4dec12fe20f5ee0820709dda6a4c6f1f0af3e333638b172be569b6fc2115891176c0c851bb10008bf0aeff12c20e3c2221c8a1a9efb9cd9a7fbad5bbdb718000222f47e5d913292fa83917d7401d1aa8689281711605b1e3b7cb38fb91c676a171a4ad42c02dc084a9a20ff81cc566e58e6f71b5a431db4a5c78860fba439b1d25ecca735db17e85a239e22be1405ad6120bb916bf9d7236e1a56235854fe1eb089f1675fc02f0963a4e5ec40997f5006d58178796d8fd2b4b833ea15485be3627be6c30bd0fa61e22c5abe2e2004da61c0b613ed135031d39924afbfafededf92b3516f28f86532c8e136e98a294279d344b27d0a9283a444e3e66d8e04d1714a49f593a439522cdbd4b430988dc05580110aec7b5fac42760e8dd2ff6292899c7a41de08fd8365666b2e33abfcc2a0004ca2bc10f861690a0563c60dc0c4159af5e1932b2672a84f7d3ee3feeca9f3ca8296ed04b515fb4e2c82b49989c4df38aff092422b96ef4237ccb2bf0957c3b5e93ff4b5f2847296594b23fbb87fd8f681472d3c346f313d03814963fcc237d7aea37eddfcbbe9efa0bf41465949e304c4a27fc26a693ea7ef2565e4a3d7a62209a514917dfa5d9c9ca08ce84f4af7264bd2b076e73e70afc13353c6245a5cdfb912e9d2884d8496c7f6787b2125f631b4b90090419ebdf746988c9612c598749b9d9ed52fe01acfe92051fe60048080b82d6748d0c6608c424563cd14a98d1c809559a476c5145a32aecd9937234716b42be075a706002db8dd75fea29a552e893689ebde4c01a2a12c2e9bedcfc8b6694d9c10dbcef35b9eda1d9d97d75f4a71386f08154f0e6dac171bab1f5e30e63cffb2f0424fd604d9cf676c980a54d881b8cf2a9068870e83c880ee5a00675266a7d547bdb1e8a11417306d08907c25e02a724dcca5850832a773202e5ac041e732d6ab01eafb4b7329782f5b5f68cf5607a6dfe32e64cf4f7bf119041174ee0c6fdc44a0cfd1430dbe97da11f439a6f584d15e34722faa7fa6e87dddbd08a279c32babc6c292e96c744df05d6300571727e9d664d2a9bb4502bbe6481074dd79259a018374a8225b3ade4b1c35d3f15d59167564fdb2506202088f6b1962cd12998bb82e6461396b8369520862000720b62b75835251a6241818efae61bed0c2c987df772022288816efe862628e5956213d816043dff88f4712da67437548599d8ec3ceb3dc1ba26d03cd31d71b9562f59c9a7e7d8d209103384d658206d983f1661ed213814ef5a9502d1a7b4f696fb764962b859d89e82a216c96b94088a73963c97a0102add0c5ce5f9ab3ab5c03ccb19c01ba28c31158bfbf4f7f90fc8f76c8da3795f03cd4a2ddb49e6738e6e65f4eeaf19bd3731e34c7a68c24e5a48c47daa135bc250b496cc8795ad494e614b15c06d1bb48905e70fdaacdf066e61c95b972188481aa943e45af90a89d34e0c8c4d2ab039159440a40924f087ec159061b663d2eeaa800d69c93b72f2a5cd4cb23a5ce403c24f8963e7cc2378240ac7cbc8183d56f52d63de183b80e5daaf7055edb14fb6e9ebebf29cc3c39ab3fa33188a254f4beb876893644cb9e11b28c49cd6159c91618f9dfd1b432f6d2d057bca22f731c04499b8c86ecf8936e09c4162144b82f2df57270ecdd577cb0a735317f6dd0413b68173f4bb7db773d1d9ecbb42c1e0d04a20a2fa8ee98e554709c1c5ad78f4b5795ad7d360b36b70897ee5ab4681d275393ef9c8cbc8b5d1f157280885ff00183fb286bb78df7b9ef551e25aaa50e423f94f507718792995e55100c0241580d62131ed0cc019a083cb40ff8508def866b560335a41891ba1a647cc500d1937afd17d51f357c421ccd6f23b21f94cb51572421dd6e959a77842d81d62ec3e401f66d36eb01ee2c2c16b95e2c96b1edfe93584a4ea03deb49b6a6e08a940f3b0b603e93a74106f25e8b7dc2f0690f1c7903f204e8098bd629d19893820769800280b8e58b8a21c9da7bd941bae8a173cd3226f511fc58b4f6b6a1014b262e10a90135df80ff981c4ce678b50ee6039be1b81b4b1c35b6dcf64b7a7f8649a0208a4e9c0d41673746469d5b5db3939e84d1601f554a02d487687dd364312c8f98a153e0b25c849d02631a4ef245adbc26f7ee225a824d9ba93d0330a32c9eec9c73417cc5a07e27e8bda06d6e4c830505fe6e67acf4aeb7bd5e3efb8f31d51a5c39c5671cb581204df0ac3570cdbfb5a119754bdfc62a8225f2c7798db5dd31d5d6a8e3032a34da3d1e5716ae9d059c35faa381852db7a683c068409c8edf9b984668fac2f079ff117a5b853bcf1c9b36ce52599ed84c8a3e8b888ef23b11584a83d707bb173703008a72f260ebb1bae69f8540873fb92eaeef55faf0b1cbb9209ed0a8d4f0e7d075bf7d4f57a886210e401182028ccf8bc3599800fdb56a94c47f40d75ef2216bc1848e83684a4735d70f980cdd7d496babec59fe381f53c24a942f36169a20212bdf883ec9b8a7b5beb95c6b20117f900c67d2d8437f62ca9482a68a9a1ea8ec880bfb3aed37afb573ad3c9b259354574f1a4fd654745e21e28c0c9fc26f9d9f0a38b8dbfd079f517a4815351efd96537be1a68cf0add5d638596ebdae729ba323a4b34aa59d11b0794a35980f5cc158838304e4a45603fb415bb0f2e9b0b7abb526d1e694c7ab10398d332e3e2c490f87ff9b4cf33365872f43f10d9e903f327b7065745562b1105e8931ecf2131923f1b857dc1b92a0c733d696430269cbe5c487a4219426e9516edc05abc96573144b51b6073a9e3345bf9e5b4acdd54b3e3d73804a2795234671fb8de12bb25cd40079ecc2a9d0b2505dc5536a0736e8d17392816eb090705d8c24cd47e0c154029533325e5e271b4959ea4b48b93a9e6133e0e85a36ab73046f8e2c7cdd8ab3018a9b47ababb269237e2633927f6403c422db9e9a67990294d24cb1ed8c9ec711e3f15cfa8caf14c8be59302c6b1497f4656ad76c04b431279927b9ca2c960b80cb038252cd70c115cc5564320312715c50b03da6dfb8a0cc49d3a5f88f4e95c652f3b399feaa56470b637598d9370c4bca8abed37712dc2e8ad4a215dffb3cceb47586d94bf6c14cf3358005d5fb0e0ad875e06809ae7aa02a8c385762200a640e823064e55ad39bdcb13c88a3a249c274013b2b62fa963da656bb2455316d8d8ed1e89068689a230bc6b9a507bc4e40b54e7a13dad93042cc31fcea3a2e584e1c9ccdd26cb4c18ba8fc7c4e1a3d07aaedce9956f0c740b21805ccaca5cf6dcaf57de3a0bb3a7f049d34b623b9c5210f69f0b2df56a559d4546898d01f1f70db35bfbb5eb6d3b20f32a3d48672f259fb92a9c00b699989190307021992f2c816edf2fd0053f30f01ae439caa9c90bba64b05772635ef2c172dd75c4ed823b3b53b27d63cf35895cd41b1b6d6dce54c566a78c52ab3400839548144b0496bcb6bf943f89d4a87fd47a0b60b08c90a10af9e1f880d43fda5d4c31515ba4792291171ff6a9f88706e0cc4be2402c50e0c2ac67ffff1445cf49990e392705175bd7e0a6fb62712b0ee49abb8b1b644c17e58950c360aa982b04cd3ac20c8acf68bd8d25d13d76785a28568d5258310a4f2115510bdae15ee32c55f13af6b17c76db8e47a579ba6f1ed274b430d94b663db941fd0aac1130be8452a0b3556ab4c0feb30ecfc2cc565fbb67e4d512670b73929ec4f576aa68b8a513e4ed71fd0d0a1c26fb46dc9199769d4955878ff217198f0af234878b249717a46260662e0998380a5b4696e9844ba879973d9f821b63330e1f97ef03074c127bd2d01a65fc3e3b2538c57ac2f80eba6e8661307076189d6ba6f885b734f71b37c8186ba96e02e71e9a3a122670eea685e77396b1df32390299d4b755a8470e3c32f83db440061e4650c12c3487f059cf7c47fcbbaeff5e34f5095d850057f6c6071c69b4f2307b9419d8dea05fa7005d8a14ee2bbf5072994fae433a0040eb23d9c182e4a9ae6af16d8b0aa297a6eea6d771e99954dea94560df7b8c7b68ff2a244b5b1f209830cd954957defae69c46bb6b3b5a0033c347d2a2223601630afabc1fbf95b41f3e6a8c84adf38ba0e0b33795de35229e241ec102909e7fb4562b31b247c166ad80deb2ee2cbcc3c766f9bd9685a5ee6c9e87a29d1715bb1359d2371656f658069b7b2d58502260033cb1cddef31ba979a75f794d1d03cfd0a44f8fb5f10e8267dfa297a12968d1d881656e2c8b6f1a9df2fefd06ada7a4b571f57c55c472266cc4c02d1d3a9e3ef95efd661563efb98338dfe4aba7b3bc6b586ecf9095797866d43a94dfb8f9638ad230b29e751968ed7319003ae232308fe1a4c94a74b24127cab70ee015b8195f45ca77aa0756649184125baf94d1a31cadf7049c2cb661403f42cd1df7344846571ce87435143e69a41f659a54bc8bbb44d0b12fac3c54695ad357ba3c0cc0ca878b09b8c27864add75e145120be84aa49f477da3dcc125fa9180cfa40816ecb69fba7c9a1c9769a3c885234c0dd48032f1dffdb25493fd91f979170a7df93d0d7cad3ceac1bf363e75a1d935fe1040ce51dcea587ed17c2c3dd9ab0a0e2125b7ae948f4da4b70093f0250b1320c54b63eabc9ad5bac0da9d99cd07fc6661a0780c8b7b71e8b24387741b1983bc1134c8c69ad60623089a9f29300a12158799aa2bec571fef9b14d4d472e16feed8d2f08286af04718afc0309bf00c4d560a3024e352944fd5caa484f9dc3c7c75521cce5426c0567d0c378f0510b30421529b3bd55541aa371a3b14af59825c0099edac915ec8a360d048bca05a16cf1d41e28c0ccc31340325243a0dd0633fe1744a28859323150e29a200127b0e346c01a230cff88d1f0827601b7afdf5391e19c8b60637c839601d0f30cd6b0655b38f8249fb493de113b1ad1f0802474c6440e2ac3d1a2e0aa8356e9a5b6970d7c7d3f56519d57023d33b3082a5f786dfb01dddcd0c3a97d0e3eb1298c3c4a03ba4e5a0bdb5c5689f9002692f34ba23a6dbf46c263400ccf38616af6b73ba5ca63562a68e92fed5783aa142fab02a96941bdaa1ac91e1527a79c6b2b9f87d47cd58c0d2023a8dc6fada70c39b6e23ca4fc41056ed84335996e2c46cdab5c7fa7f61ee1f5ea9d67275ca2e9041a7ada11ecc6870f54132e562e10c1d1547f96e2fd75a6f940db3cb1627c2b13381c6f603897b3b3fc6dc098cdaf9bc561a63b8eb0f71df4d12fee0bc8167359e780f4301e6134fbf8013ca9c825510bc65b57be27b440de09fd00dec2ee0d4f6048dd4e96df5de997aec7b7730cdb41f7a3a48c2421c0b2c41293dd2f89d77b27cd73f4c451d13bc67ea8e6d405cdce68f683c54fe6bd71e014515892510134c94465c1c1783d7ec8e243976d9e711e852c37c486cabdebd9c24826aa1103ee5b48dd4823f416158b04dea13b687bc4fe41ee9a783529c692c420bb7a8f6fb438b653007fe0f33c9995ae59dcf3eb9a274eece7e457bea49b628f3c43517c05c2027047260593d24926884ef2aa2de480a50ce5ab383ea3a8dfa8e1f70bc60d92e54bde8f37e8b089462d7a55d33aa8838d76d7a925ccefec1e09ed74fa9c6768da299a1c9382e368b3e99a22a72d606c084a1779eff2fa451027ea61727c391c3a78c7f23b697ba410325d02eadad416c4ab3eb4bce0c5819aad3df174bbe4cd313b64f266b20c2385440d1a0f853aa25bac320a6972d5d22864e39d0e108fec0593d60375bfe10747e99800c5af641a3c726d00e7c46b099b789885c82dd4beefff0fc9f4ad1f81026cfed37e864d721b7bc81f054f448263f06ec24f3d800dfa4d7c982356ab7504dea1fe1d539bf8426a5b553ee98fd59277640e87a607fe1483b005bb0ae68c9165f316fb8aae0f17f9a0036f39086f78894cb151f10171799f1cf5813b96327722e9827d3e8d2b928761ef93207a181507b9c17333fd35d37e737c5e99efcccc3c10d4497e081dde48f1d46530883b5f791ec12e3690b75eb3f622bf1cef93e0f7bed99561601a82e142b0e41afc58532c094eaaa8b494cb39cf61ee212439e829a64b06bf626ce274b45dcdc03dbb086c117f1a6ae4fb0110577182d685bc214891c106cc00ac5e8388e1bf3a5afd7515424556da1f863200f8e6be8bb8e992346d756fc14e15c19375dc759bfe8a8eb6228710e7453e1df33d2968c02574fd175fa5f2939b73a6a96e157f3fe8bbf853afa7ffc5899f33ea9f1b93479a5bc25b0dd4cf3aee321702273b3dd61d7a39202039b4ebc1fe4d0560866cc9b70a4a3c2f252456326f0a5baf350b5c0250c5ec8d5bc80411f4796c61a278dbfc747a29b29911e6dc6268d3517e9d0a6b92f2b21cc4c7d1b477f253a936c637437219ad023164662e26b4ef84ab0c2bc8d2f8283fa746556808d158b0736719098f7daa763be080ad411f29369da5a860afa9dea58ba1444e4a619f0702db57f091cdaafec18f3b6b5f7299b051027c467cf8524d8bf5c884d2957b45d1dd1368afc8f37b814c8835c8e620e72a9720f94c529a6dabf940dab0bb13f58a11ea739970f82309cc5bd7194e4ad71aa4678b8d59099ff0098e0f07bdc7e849c31553a433f08b40a74f3428a4780897d098101927865a0dd06245e49b0fc5ab966355694a4456ccd10ee094ebafecf4d3926c3bb91339bba1c560328f4ab72f0f044c964f0cef4de3f20294f6d4231700098feca547c70134a08e619933053e7da790e57b1372e97e30f93fd0692759693d6eaf26dd50949a2f46dd6e2781454643897614482c626cf7488a978648d792760eebfbae1d695525420278f2a0788e3260ee812ff32e49510308c06f984a9cf2f119646eddc07e601d561b4718135c457e11a84ef3b0745af88db8f7f678f0f8538ec21c940c9bbd0089578d4e1a7a2c4e9482187b492873dbb2207e6aa60cdb679205e41c4704eeea4180122742587e8c42da9169476d1b95ac1ec618cfe0b1f97d52594b3524eceb0511edb4fe8bea1c93945459998eecdffc6dfba4967b0118e37a778d84c20cf9d80c24e0579ce3ab94c51b58effa322cb81f612314c6f2dfbd4e903040cc55fb034a83c2533dec39b6ce264adcf06542120230d530732d7ca19bfb4e4f6b0a8ce0783534abe743b83d585851e33d1df82c6f87bfe8a0b26c10e24c13ba7822c3d7116c337cc28713e44b6c6e73bc09d2b71629a6e10b53aedeea644e14b9c5abcf5ea9ad0ba058e7176a306f9bb152df4e752f36aa4187c1271508b9818138ec0c593481434f402a10831bf731b6f2a4cae5e117cd2b85e97fcb018c23c9fa5b2366f457e5b07bef03b334a54dd7d22ff8c4b0fe5d2bdd5b99549424ebe81f33b40691e12aede8012060d99794f5e3bec7c479092dc1fd1d59cc085b4cbb9cdb3c36a4427846718855ed0c18f845c7fea6eb57d342f4e6cc6d2d23cf799c2fc9e849177ce8d009070e80c02ff9da22c995d1f17780bfaf4d12cd6d559b6ba5437dbc93f524805548ea5753b16c05287c464e8dc49d50046266d12afb04b27c4cc070b9f425b7c30a27759ea633a46664f2843ca657c61e436b1fd31a81ba815b75feac002d125dd925774c919b313c372efc5e5b4d9ed5a9856c1837dac1c419d29ba0454980197ea02258e0e58da293a321ff5d04d494e979de2782f373f6c5aaf2376ae7f989740894ca301f2e39fe750e3005c27dee1f8aaa8a7d6c6921807f9141b4849ca0f207663217768568524cc53c8fed87b00a0174bb5193d73f2560944f0e67bef8227c40c71c0097aa7ea4ab9dc46ac62a7fc09e117c884a360d9dac2b9723844c9081a8a616dfb15f9cfdf3996b18a727a5bedfda4ed02303e53a84cbdbd0d8a5c4b2ad7eea39ed1cc18da2d3f769b8928e858e5007231b6ff2c922b70afa50fb37eb5e692643105f470ff0686f2aa7d343602527a81358862aa8489cdf6f8b4fc5c2bd1d143410a273699e21f6f62705c5758403f5a29d945dcd8f1ea8a3cc4ec22be0e105bd6fa67d26d823b0df7a3a20ee3c13c9a4762ab644c1640c9551d6d6844119b2d868ae90535235fe85675a5947f0b156b90208f2cdd907867ce65102b722e1c400ef0e77614c00314eaeabe6c350b16158e2a593d4d9584f98e243076051d2e43c1ff569b8e8045ec7bf5791cb5bb348c9d36ebef82b85341a2179d0ba3f29c635912d053a1443dec2a0a5a203e0f04dc2a4f6195112686314930aec2fa397a11f1f2d6d2d5870dd25edcedaffe7549bbb6d902138606c24899d79e231f72edda06fc0069af96f8c374c7be33f13314d47ca89a8e1199d74b88b93bd6d26711c3dcfc658f6293b9cec3f6f18a53753e176b35b349d56ee12cd8cc7503772962a4ecb422def050e803a9c04fec2e7089989d168a4ce165ee08bc85977f09794713756c99c78f148cbd1990699faefca5f0afa2ae4d2625c34494e55cec97b830e7d5b619a935b35f7ad68dc07013c68c8779bc8c4e74bb05f011391ab38b348290a16b58de7675b41f50e7ae3629aea480d323028348c35d184a5349020335105f3449e453e3895f92316d5d231506125ae66412f49e5eb070117d052ae284d770d947a8cd24462093a2792040d95822c8293d7715a281f746026b2a5727faf1f2f7848129098e41016e62adeba0b233bf391f3e20c91e545df7c996f7ad2406788582ccb5381f119ca18bcc74dd5390e7e00af763216af81913ccfcebced47dc6bbfc2c16a8e912f0d079cf783ca335532aa9d32b740fa083a1efd70ef776dd3e519c48c033b4a83796141d806dcf6c7863d7abfe7be01535c8ae8ee6a894faa162536a99922ba7803ec5ae1edb7f93759cb71c4204fa85e98dfde1b0f6991be53ccbf4ea5e4e6ab38cc981599dad3b6e685b9aee28f4ddc8c8130f0f808416fa22d0354570c3c012ba96a3a1b242e3938c667f9c8f03a61aa6ffd4c45c500293997c14d162a5c41e05276462aef2e7ce4fbf962750ff5c1b02c0ae2a4d9a9c2a1c6102d1c79d8ef88096dc29d93af66634eb471aae05052947f373d46faa5bbdc72d5a86a2dbd4f367997b265ab1a5730c24c0e6a7e65754f624301c15f573968107795d1d20b90059abd4f675c10000f7bc5052f104c227215c04ea9d01dfa046155be8e0880435843b38c51574db3550e005d6cd30e0ab86ccf78a9cacd202665eac3946dcb6b59483ca4d033557a5b55fa3165968ad14f208978feb4d69fd53b3075bb0d9f082eda1b0b476d1fde2b35a14a495c1a69e2a6cc7e67506bc5b0ef1f4b3a2368b2a8643a6b9e3c782a1a1fdb9cc8a2689deccfa3dd3a493187b54f6bc5b029e55d99ff2118ce80b72a328085895b136e9b8e3f62cd84435ac3d79be475cf3383ab9649280f90e7b5c5c5431c795f94f5b1236c9c145aadc0652fb788e3a7f331b75e969988bd12ff12a0027859698865cabf0d9d6ece4cdfaf5ac1193da8a8e77da4679542100f31e3ca2284c5aaf873c4e83d7211211267cd3de4d6c46cd9f5e2b52bcb28bf87aa08e5a9dc82845b2074231a6b6c7c044e32212be1f68b05f240a673e154f5412e27bbc73ff12508532dee3336a0d62ad7fd996d0e2a8f8933126cc7702a643fea59090a79fbe994aeea5bf40aa1635fc593a2154662e2ffa60f59602e35acd4628324d123ea8106afb1f127e495d710931f7e537c910111759da9e11d3575242a06abb629d047105b3f1ee41f83bf2414281ea8c097b2e9ccfb8de57f1d09560cc499b50de20129aa1476597bdb3dbc9988acba8bfabd53238f75853a24b0ae275fc54603619350068f04e304740df164ba34e46db94288cbfe5e05d7196fb1e8d22d968a090662dec38d32aa711b7b2c33f5b86cf9b1c3985b72b810dda67c3b2a17e24a9542bd32be179096849ccd353aafc276cbd4a81fce2dea4b795e73457bb88217cf7ba621cc1045c6d98639277cead96eb2b9a67a11c9e499d7ad83fc36b5bb6905d9ea7ed611f46e80614bc02668eec66da14e4f9a154e03bf43639b51dd5790ffb4957e34b837bd8b7446140d6d70c2cdc050e23d41aae83434ec361e240f8aa78783fff790e5c41e6a7fd3045eec4a3087cd3b8dd266d0f6da9c991b836c2bd6506275ca336492bc09393dc77c5e14259a59fb73bf51a6f6782e7a93d9609e3a62877e6a1988d4f0a03304c05041a7193ab58309855693a7f79f6efd1ddeb6534a8f1dabbea165f5291e3ba7a8942646ebeab69cd1c6eb3b2686365f0fbb57a32636e8e832fd050b8e181a6dd6cb2f6cedbc5607e6f62741b03b258cb5f5b6bb6971a2d355ad2f6de72ef2d0a620ad90af851ca086d16bd7bccf8d7b38ee67ebbdf445f68933b9b8c455df7d5907e7d3f48bfdf8f949adb7d353793eee904c5c47472626262a299689a764ea471269ad689b4eb2a695fa954e2388ee3382b7497b9eb6c321e6d5e6cf062edf8d5455dd775dd8cccdd3de78537739fda29c8e8f834a48bbe286570c72779a267d8a3211d7fe4cd1cd3cce0df7b8dc8dc6566e775e66866dede9d8274e74e63a50ceda24f3b771ad18ce8a31a0d37c3e1ef36a85da66383da6baecd85c13fb88eaf2bacc956d63318f661d50b83c8d8175a8fd3ba6ebd161bbc66b2dbb026d75b3c95a042923b866b2fd3b19bf214ed2837d9b89332ce131d7b1d8de8ddd7bd351ea4731508491963181f3fc3bcee23cce3b8474d47e9d88761177921f7781eb48f380fc38e4f37d2312facc9d82bed07fc0e63cf38af7b26b51ca3731ee9ab4026bdfb31fa88a0890dea287df498bbd2171bacc9dd47a490c3b493bc9084518d8776ecfba17df4fdd88e9df36a48197be785a4af8694b50f6f8f3f99a47c473131e9ba7b7f7299beddc548c71cd69d747cb1ee240ebfc32613e699f03dc6183fa35a0e9c1d3b15469779f4eefb717f7deb2edf7d2196ef8f3baa1acdfdc5792129739924c217e18c317e777282bfd0e4a48b44dab9789257dd6474ed25af0899471fbda74633faf5b5c6e31d65da9277f46ce471a32f947944bac82bbdf3b2a9d1885e7ad466f04997e9934623af7336b799ee98d4e0f6d668f0b73d7e3feee5372fd4be1a52bedff588730d8933014ba6cf2c5278b3653dbebe0a91ad57cf2ccbaa18b6f5d7a3dd1285882c42d4b01c610a28bf34d3537e4fdbf14be7bceeba4cde95debdd278e06f3c4617852c99bb381c3e73dfa9f4d18cb677e7ae9b78e1f678d3459b77992ed3f8e2b010dc3b6bb76d3b8d8ebff0c443f4ed310b711a7d34a2ee9b10193f662172b7998e4b9fe9d7f7c374d1f783f4ab86255faf61c9a62fac6139dd4bfb9e72ef49f693eca47bd665272727ddf67af2abcb4e4e7e6d27ddb90ee5e4434141e9baaeebba29e0cb8ccf7138c3a32f1492471779214b167df34221f2f612f785cf5c77c235d885a47c72137715d317e2cc3568f2953c959bbcfb42d255bcd82d9ce83253e4c56f5e759217d6b064fcfb11c61863a9f1c0f8db394ff4ce0b5972f7a9d170177db1c1ae7387b98fa5c11a96f823440d4b2e42a2f4c6861ad290f2b0a837adaf7ed1a643235a26818bebe49808e528f7a68f484fc1c78eb157fae671187612f785af341ed831fee6c5463dbbf665df96bde485dae3ef3bec65217e864bc78ebdf0888cb9eed8179e7868cfb0735f78ea3e1a8d8bf988cc5d7b95dd635e88bdf4e5300463dfb3efc77df7fd20dda643fcd974de488f4f19d1903ed1eb68c47122918813d98be16cce8c4516e38e33ec1c767232359ace27c7bc136c3bc7611c87bd9ebbc5380ce362fbeee5b55d7edbaeabaf26a49bbbae19eda46f1ef64223327e6c54e926dd4b369dc30a4cd147dfdee17b2db43949944523d15784ccf5ba3c4d6a3c34ed9bd71d7b53a3d9de7db1417c99893dab358e66fb688357cbbeeb11db23d4906da4496a56f8dce401e4e8430550166315431fb956d565f85c738cd863fcb26abfa9f120248732398cb7a93ce44321b93f65d0c51638cb2454f44c31862c801c7da6d8a9599685369f7e3af93dca473fae73bfbe6d1e9e620a9d7c3d650a1c237c91714e11684ac75f48c47311295974fc4ae3c1fdbabe79e1f3b66d1be9e3414816e15f57d83d9e8793937e2d40e693cbf4f5edf407d1b95f5ee8235fafb41fb6e3eea1edbe130f4272774c3a3e57012199f4596025779f8f8c1ffac8dcb987ddab1d329f9cf342eea22f872198fb789cfcfa7e9c9cf4fd48f96553f375ec85dc6753f3c9171b3cd59cf2f89117d28cf2a9f52069523edae0e9fbfd628328f807fc85329b6ef2a8fd60ba89c90b37904df0b7679b37b389c9a5f643c87d7b6c30f491bb39b3d47e08f1b96fdfcca62fd44036797dd57ee86cf2eb077c0e48eebeb0de66669387b4c1ed7833f9b2579abebcecdbb72fccaab7dd666ec73c1b99af4b2dc76643f3f519ec6e814f3dc8cc5d66eef8d3de1a4d768b2f8fbf10cbd98fec16e3214f054f76c9d1878a213993da0f99f532ce93b7465092ed33d147f2c267d23b2ff491bbd24ade8e5fc25fb81223060c182a2aa7534a0a0a8ac974726262522a9148a39148d4751873dcb669dabd598661d77572f285366f2f95bed0666ce5ed27fc18634e6e3a7d6195b7a33cc58bf105e50bab8c6f824d5f4833bec94fbc185f4cbe90e6ed256f7b8ca8d2b7d13b107fe13de93293e4c5775ef59117dad4ccfd5ee415213377aad174e62eb51e2477fb85d9b97bdbb3a8cd488d072151a6b92cded67470dbb1d77de2775ff88c2f3339af33f6c27beb7d6e47cc3974003f6b0f9f351a19decfa6e6ecb38f9799328d79385f5ecce17dd41b22ac36b4a10d43d986d294b244912429b2d842e28178fc8eb8c1fc8090afdf114708a922038239e1e8073153a7291f3f0dc4cb78f99da69c1107bd2cc0b753c0218cb422c39e6d7588a625374e920683be807b26b98490fcdc799d90736f496f14cdf72ee18c7c6fbea67c59f205f37d6cd4bd9f5717b9bf93035edf7921799a6087caceaefbd375a5621220ecb248a3a0a019bbf4e9f1c958fc0249c68072288d64aceae9b902d6832463d88132f6abafc7c3136a40859d838c4d0edc7b3f43f3bd37423eb1dd7a277b21d9621d7cef8bb4914e6dbfd769f026d12056023f81731cdbfb9d44becfecb59ef5ccebcb9b6eb9af5a8efe8eb8e794e0fb3ebd9752449969a796434a91effb0431e6fa7da340dab0bfa7debddd259032ee97c3101cc990b15f5f9320be60e7c6801bc37e44a3eab1cb9b46d9639749481b316343701a959dc8f5b26bda9d57bb0d4a9ce6d17630b10ea6f6e25a65a53919d6c11d5a938aec1c86b4c4029b2072571c8122478d867322cb32ecf15a9665599665693d08817df3d78b11c4eec146dd2f3c22dbec636970f668706a1ad6d61637982ce09c2718032d43d501b6584016900564015940225555811508e30256555565d9b7a37a78895955252358e7bb2766fdd555619875495aa725e42cb1eaaa9295ecaaaa2a53ae22487f79d56aa55ce31440f40cb3abeff1e59f4225d3872a21e966ca92a2b1855251a351b1aaaaaaaaaaaac68d4e694fedd03c986e71e9a23a808d1b3b5c0850c055550788d181d53800f68533997eee98f33576647a93695e43987096d56854b46daf3001d698759b491c9977619dc561ad1c955e97090bb187758448d1d01517492c6fe6a6dd565b94524abdce72c6b9d1276cb533c7805a6badb5d666963773d6171b03b62f625619d0784c0b0399d2541e34dbca7e41ec6d6c9ef1e57a4f0dd0387b93f1a576e70dcc9634d20dc3688e912378d6b86159965503c605ac615996455f63a6ec075a94247008e669029313388c3f8f9692b39ce52c67398b17966559966559d3a5b4061cbae41b2535e030feb8dc60b13e7f23c6442d7ace39658c2f74ca1c481b8df12077481933c99c69b017ca73d0298c146e20576aa22c885690197c40828a0445b2653d1a792deb05a1a1f659e54d4a675f57b441185f98dd64fbe9edb836794ccb91dd2fc88cccd937447cb19f71c94f74df314d268ca93d0d281ab497e96b034c83e7a3b1d3a07d1891c0fd90c64eb6df21c660b7a8abefa4b5de4af321fb2e69edf723faf4c8c0c96265a7124a5c9315f490f2881aeb4e8c09414ced88406616764c2fc8344a267ee409327d0f7189c90a780b2d6eb5d5bd341faea5f1202457af327b6dfdd9f8d2971e95af69d4a93f9971008c2df4568299123018926c54e869006c805b7e70189700b131e288fe66621ed2281c2232a7514934aaba8e13456662a63bfdfe6e14b8c1202dab1bac0b593c92e5ab283294e56733738e8f04c850a4c0e1338a10b08c48c8c93503655a9fd1940654b3bc0f3c492650153f2ea8420e6b64f9e38223f207a8aa20c8f2f1d59327dbc5cccd36be5429636f34968d2ff130350e12b9e2c8b469c0e10d93ec39e7677f2a0d52531555745185162dd8b942476ad87e49849cc8550811c9b18a2dd45023c72aa0f045111c737d18b07593111714139bde05458342f2851d23c2ca184685225900395a31a44496da7c26611a25af4faa7c26b051ad6117e098abcbcc50f264923e41de8aa02b7072f4f9f9a24b90a38f154a7216b51c1806d88a9f1c7dac38924369822c1fabc8d1a78a25796a39f012b8afd81dc95dfce48d890e0ee9b4175bd3335dcc6df8bb0d529936657c0e7b3153dc7d73d20625e761cd33a99ca54150d6b8d1a0bc0cb0ccf4d4d603744a4a29c53068016602476c4a28ea6b8c3418a1f000eeafca2ca7c822de01d601a6591659326b13ba2ca17487f96d55e025a3d4100f7c07c1717c390cc1a3779f8fa3d1e8f3a2d798e6d743cdf3f333a241d16983a2cbb48fa125434be2c518c894dba6e5b0dfb41cd794390c99cf80e4bd982d2d87f79d9ecd7bce9339866763c4110da40c587ee57149ae31ce71df5e63cabfd572fc3b715f0f3573e73e231adc5ed276c41ce33b71e7e4ad955fd274cd3b39e6995c4f2e195a52dd7b29e568c66519a5f277c35986bf28656ca79ecc283da687a1e508429ff2ccae78b1418e5b59998f9af66d5e4e895d57c29887f1acde4fd38172fcaacda43ce59f96e3744c03d260cd24902889482f8c4b548e9d6a33303e73c0c8a4e8d21b7dc5ebb8cb8b7199696378d7a567cf61120c4fa5c17a15efe4812e1e8c37a34683f5289e4c83f5266f4783f5279e0f5e0f201aac277942345847de13df45df7d344a76ff1ec62cc3f53033a4fc9c77ce4b7935a9cd89b3cf6f5996655aa6e50832df7dde10db3430775c4fda5e334dd3b26cc39cfca6699a967997f64cbbb41cf3510bd2ddaacd6833b3dea5d372809acc0e441269943cc633eed0a0f6794d45c6dee6bda1dad7f3c2e8e3a3512ac0f4f1b296892b43f551186dda22c34630bd8856d143c980e7e9fc10ebb22ccbb2ece5e117e0beb53670352abcaecbfe621660ecea00cf12384ae7ecee9e945a1fa594dee6da1ff5013b81e729125514a9b5ced994523a6badb576adb5d65a6badf5b5d65a6bade7c880e92bfd583a874c9fc6208b98c76eb73cd18772f0153f4892a31a72bcc20744b24ccb217aa6e5d8a4ace2264e61bbb1519b683ecb32a9e5b0465a8ed177b2b279b74f2d87e8bda3f27a683b8801cf5f3e9a7d1f7b8d295bc7b41cd677eaaf879afbfd19d120f6aaed88f9470e1da83eb7ecb2c7bc7b79ebfde899eb071156be1e3311d695deb4d8f532fb4529c35e7a995096f3f2b0cb4c2c4b8de60641b992fbf4f0314fb3d66566837747a786900e82e3207d37c097a712e3cb6cbb7487cdc7273cc9d798b2fc7aa8595e7e4634a89ddad3537aecd906b1632a8dbaa7526eb237905bbee65e4c1efba6278f754b59a53623b5998c52179621e030fa600f5d7ce8d1a8ce340ac9139228bd1830a543b8b7351c013f81e569830ec8e223cb121979c66fe3bc80fb72cb3759d75430269549c406f23c11324f8984c643e14aae92cadb603d07064c7f53a89683cac60dd658e9596a1399262b6765c94b9768abe99204eed316f201cad1c7a727d3c3e4e8e393d3b2e7fc42fb85d6a541fa1529984c594a974e4999c91cbae497d8424f632220d3e9125fe8392fe0f9becbfc58e823900b324bb26f2f9c9fa941fbeb046cbdfac528c05f6cb03379e7e31547a098b7596411e700d3064f4850a2278a1c91a084911cafe588042596c8588e48508248961f036ed55424ecb6afaf8a551035a42f3918aeeca68fb4a75cd7b65d2b62653bc75ddc7148eab28bbaadbb4eb7cc914e0a6bf2f68db45df4d5907297bdfb7e64177d3f34925743cadb53469b86b27da16934baf7de7b5d2727272760209570de8ef1a9f4530dc9922c69b3597b4822e5ecb338926a48d7a6b89565d9e4ebb2d7b240ec16bb3d36440ee331d946f8c275611856c40a662d867535d75a1b9b9a5b73350cc35fb8aeebbaaeebbabaebbaae11d7087b536f41b8296b593cac7bb45b6c6c6a6e6db9a40bd78569d8e8984985851b615e78b1119661231bccc6c6c66654736b6ecdc5beb0e66a9aa6699a76ac8471241289d485ebbaaeebbaae2b73a3ebbad7647361ae18684bfb557a442525122e8c7e45379dc4a5705f4804cd45c0c8a58e0721b9f469d79e7dbbe665262251c783909c3556012159fb2cb092b30f137522518a8894322291eee8777447f7de3bbaf75e2e685e64d765814ccb62f55505d234d0c28874c25ef6cbe3ee35a9b07019e6aeb0db4e176d27fc0ddf63cc6da2df2bfa68baed3103396ddbf7e374d1f703e597e67bba97442a916e8944225dd22d9148a4eb1a91bed16874efbdf76ac15a6badb5d6c64ec1b888eb0ee35e6b71beddbdb8c1a3f43543c5691491f8acd6ca23ac39578afdc20c2cb1bc4aa4c15aab6a81a11cc6d7d41c4bb60a6a4e162e11e9a3939fa4dce425d351b073bfd7c3a6791be99db7915eedf80b8f390edfed5aca33cedb522ed3d876b563dc2bed877b7cce3b65dd372cfb68bacd880c2cc9f861b7a5685bcab7ef47cab3ef87e9584dcdc958cafd4c8f4729994e4826a51289442261f7dc8691b0bb61984824d39827c218638cb3705dd7755dd77571d7ebbc1c8660ed9c17d6d49cedbaba7befedeec5c9984ec6ed646049cdc945c89a739980abea1986d16a1ef364aee8e9fa0f7beb1710ea83f6cf1548bf4a1f996e3ab909ca53ba8bdee1ae3b8a498565c3b98853ce44ef2eced7f59deec783909c75f8ddafee9b17dec773d5edbcae13fd12bdf32aed87ebdd3de68e2faa8090cc7d1658c9f73b65ffc1fd3ee37ecf71bf1f908c0f248b2e7a783b91178abe75dabbef8776eefb81f2ee327d3d1b2afa6c68d6be0be5f1292634281f6dd0f4fdc4c4a4f451e9a3d2a8341a8d44efee48f4ae341a89eee8ba48a38f44225d41749945582402b2e1ad3b7932b3b3a1572412894422d117ca3ceabcc9fd07fe3d877f3faeebdc894ea2fff872e8003e755aa42c4096649b69ea8d0896b481bd1e93366ed43adbf7764a5febb36fde4cfdaa99ea0b7b9bd73c79753569ec1099de0252bdea715a989441ed2b2b65d0571ecd35735534d5ad5b6fa6ba65d964fabde65e499b80b142e997c935d24d3f3975bfce3dbbbcec9c8747adf1e87e554066d1af7bbf93e83b05d92eba6ea50c7cd1afe373cf441ecdbde89b37733fd1471bc434a219d1afe81a79a1a849bf7d9d3b35a9b0dca4c29267f6ce068b4ef7ddbde8f8c7fd4e4146ef4e13a58cf0881caf01324483f646d13d3570d05290b41c179206edbb2e2c20cbeb9f5a3b494f83d606d50b77e4eb196ed034056e1ffbce8bb3a5933d3bb267a8417b5a515aafea45b64aa2c87612c9761ac9a4a4b51a62a8a651fd816c73b5af1e0f3600ea699f46590f9b8a6cdf468836ea17c4063f8696a3ea9d78d3a0eda06e24f1c5bef2faa7b18832ec6b2f21dbb79049fbaa779206add00770d5415f0b35d86389d06080c34e92adbdf5216cc5d864fa41d007241dd4491a55fd415cb6142dc589e4e99f4662c91e19825c5d521153f8f61227c6a8e4caca31258b44ec354fb7643994320631257baaecc93939db91ab0645158f34787db5791ab4ef23378dba620c914e451c493891ed3412eab8f20c000e70d83b48ac2c12c261f34891ed0360036cbbdb4f9c9d4675b66f28e8f5aaaaaac739bd27b0b533c77eead8cf1bfbce6b2fbac5beb69221a6b51857b6b21fd85e341d438e41d91e8818736fef43a3b4dbf7c8606c5b8c6d5bd9b66f9bb76ddfb6ddb06d2cdba6ddcba33056e63dbd5e756fd07c60b99e61f5fabc7ecae64da7e4cf12fb899344a38a348a66fb09054fb633ac1294e490c5da1c4e9e6c718413bb7aa48fbdfc914032480e35ca5abf32090972288588e49065047686bdaecbe6d8a631824b1677007f5da677f41063fa0d88a948136da84819210b8cd2f5ddc503a9276d782c2730acd20df125c69c744e5ad360eb880d76ec79191b8cf1459b6169b09f837490cee16764a6f1e24bcfcbf437bf97a2b0a35bfad69a7e1fe24b8c894dc8fdfe8e27fcf064eb57b6cfae0912b89a01aec2fa45b5f30aa851d7b15baf82628cbdf5caaa8e64eb3d1a75e5281a75a4513a33b543eb64eb15ce4cd5e82f66aa2a992d96f5aa87061195f174054544653cf8c9d63bcf663c88a81e3c8db2aa85043e1121db90b997b2498e3355de2eeb5a1318b43035ea073668edc8da2f2fac91af679915817ab0b94783339f66e6bef0023d808831dcad99b787336337d7afdaf12a28e28bf59422e0b0c6fde68535635e58b35685bc3aa464a65c5c94684be0ac0e356855a1066d8e597eb50b7a352670666badb5564a4538c015cc97cb75baae6aa753d2a788f58ae7a74ac2182290902c82e40839bc35078985c4480e59720594ad2a875550b62a1c613594ad5fb247a34297207c0cd1280b04c2ba118daa0e24821cc62e4290c3e8054e0e59b28584658204c66436410263b3d6cfd05cab50a3eaadd7a11803447cb16ebd5221a21a1041ebd6337b4d90c058fd7a046008b8da99d6e3dc81e6071f628cac9a58424c42b6be6362975a4b4915e3f91a0888368ab0b9de7a8e362a1d291363e46be42274c8d66fc498ea01e105115faccb1e0d5aaf74a40d084419d6adc718633dac9ac8964a9bc2a018b31363e4ad8c275bf5e386e28b75508b8c6702c517ab5a9f9715812a507cb1ea4d7cb12ed32e50d8afda69d0ba919d6c619e524f261f59ec049237b204b3942859ce9087b962aac76ca9d5e5f7012c6ff8c4968a311086895f686d097ce5f9d5288263661b155a8f41d93af6b02606651660ebf29fad9138b9faf5b0267a360757efd37b8bb68cb4c100c800cf67a01338a7ca9c06eb4117e0500ec93527d71410dd525f7b0080073f3b7cb02ea5f4513f447d8ffa20eaf14c593bab95571359bc8696c0b26b15e1205a24b0285f38310675248726964ec536bd29500a1238344d528ca99fd5e79593c518341423672d6f39362aa3935a9b7699e632482a03aeaadc03882084a83e440d904665af5e71628c0172759b4ed5982d1557857248ad6c86df2dd56bcc9410b18b5c9dfbaa6f5f187f7205c403c2eb010eefc55d597d8bde3a08d328976c5931d7ab57d817adf7aabaf5f568b08a40433d516257c0f2406431e3e433d9a22ae02b6f8394ca1c2b2b65e7b495652f2cbbd6d5341fdefd1ab9a3c1069bf626bf533f664941da5d6bad550ec9b43b368893e9110dd26b4b602ce2e96d8c8831d6e9b3231ad52a907289231a158102c004b636c4b4ad5e4ef3a19332561eb533b54302400b6cbf4a9be972b4a2ecad8ab227ad95555d91c5925cad48a81bca6eb0e8509ed798c01226cf7a14142f94a02c892f284a660a4508c5a2088138c0610c254279dea0135f26f71399c0ade9f0f2e80bfa2532ea694ae0be75daa08d2fb1e27eace69090c4230591801ae47ea40d1214a28c49da02092a2613adb81fee67f40509e80428beccb354eb3b090508e586041423c692186088365084640c2531bc689485220531e4e983fdc497f989f508491bd81551c6fcc49220854b6d0c2537e8c498eaf337288931f41345499eb4fa62e8c4978929d17ae2cbac00980287580ff5b89ef832b725f165723f791eb132f6562fab99eb07c69799690c2f1a9c31967c319434388f04960f6328b9a10c19783125e5e82967dd4e34d503724b6d4625ab008736ce2d361e6d898d0777632670fcb42570a4026b53684c90729045c9066d092be8bc56ea00f3fca403c8d9a4937ab2c2b5be3cc125c71c72bc48a825c72a63b59d68aa07e4f9fd88d639fd8c4b11824c11c2ec3929a5b44e9da993e5ab2fbe884139450895137470294fc0a1d531a3ec496b65d9ad1f3f822cfaf464cb04b64ce0302ed1e1e5185fc25a24db6b4ce0b07ed1d79dc0615ca2cd74beae6f881b21d6d198c061a90765497cf1327dc471793277da12385c4992ed331e1d80a7ebddab27ea45efcbc698edd7af18a3fdf2702882c3ce6c11004e00b20070228ece8b38324d0538bbb6022cea66aaf6cc54f53353bd6511633400647b1c8a340dfbcb0b71f80287dc3bd9bec28127dbe3f0457f38e85801873800657b9d6c4b4338a4b103948a93c64ea364b65a13f8ba032a07d48b3e1a3b9d478367b6d897aa80c395a10c0adc1fcaa10e3d3974e8315981431625216651c2b224c66cb76701438cd16ecff2058b121c705894e49695242b425c96e12cebb24c9465a32c23655929cb4cb2cce680b3accb3251968db28c9465a52c33c9b2932c630287f8c84fa332248da24031e616c05a55a5b3ca96ccd96757ac10533af4e8f0c305d18854323931a11060442989d212a526949e506aa21485d2144a1f631a8a465d33d538b0ce2c922d8d1d3c7580aa1360bb836174608ecc008a312cd95ed3c121d681d1a1dce98ad00a172b4956821ac5ad4cc15be9a24125f1c59eae0cad6861850bb75a3b75e8d1a1a7ce4f014b568674e8f98648d5fcf0ca10661962c939a22d81431d7a3425666c7cd1e18a6803071da9434fa3b8dbebe0d3287c7b1d7e624c16ea90042f05899702145fec3be5a7417b1c74a48d942ca20c7b9ba245b60f7168225b9569e22697f203e327be608f45d37165ee7b83f6293f5ff9f9c208e4185fc417fb87291fca92f8627fbdbc6d21db7321dbfac5116b31c63a37544a2b4a2d4a2da517a518a519a597d2ecda9d39007c84c6ce457f7d65e8f268ec783478b880690e69ec3881431a3b3476421a51644be348b68f31e6393cc010876118c33a0c1361d808c3481856c230130cfbf09106ed714f7cb1bf3c2c4583161f3992edb3ec765ed45480b525707823dbd3d8a10184985a198a2df676250b3dd97ec50a31857156b880b3accb3251968db28c9465a52c33c9b293eb6a4be0b0d4a3e3caa59e06eded0e0d2968eccc54df9e060f8d28681c39c0508c69c9f6190aa529949e2855a11406a531285da1d4bad77c109229ed69d495ed330cbb36798c3353256a9f699aa604be2e5ff269d0bef4135fba977a66aafb6c56ea71010f101463e8ed637696b032946d8c4c3f969cf8620f6a81c395a1ed26bed89e46957c1ab5ddbef41363b2db6fde8c062dfd600030050eb39bccc337f1c58611c81c4f7cb18f5ac8360b4898e06407450b2d30e1312da18952cec9111590864c78f081914cd04fc9c230e10922228bedc55a2c215926b0a0130a1a31410429c05b88d8408413bae9c620822d8b1e9c851496d0b4d8e1b2c0e204d7093b5b16452841c6059096451498147a64a4f073710d44703d4149c6028b659143022b0a45ae2c3a50822ac98f5dc3072a148e5857d0a15ce8545aa060022999d5022501a72db617372168b644db33022620916182c5403dc89179820e9ec2117cadcf144a108020a0410a6da02fc0f80b3406f8248852815772f4910210e41af073f4910213bd041c230dcd0446a94217b8cb6222812d24482db8e04163017339fa706164ee70c1858e501634471f212c7aea10b2134d3f3edad08c9dc2fe4e34a1ccddcdcf2924cb79fde8f4e0bc02fa552eff889f33623b39be4883734696de41901610e105536822470760a0800bc24004212f6e962074cd13549d0e25a856d0a1bfea0a11501a370ae540149d206484dca69b5272f41152c274c12003001638b4b9a38c803dd1c4cb1cbf93cc33cbef442385e4acbdb033d91d813c3bce209d6b22508e5960f007584e2c64d92806b6bb7b04032b519df2052c3fbbbbbbbbdb830196dddd4db120359d9a1318acb1aa39b3a5abaa33535891d9d2395f8ed7d5440289b1c9a443c6e8058d159929ce62455632c7633790657ca9bd015a4bd07989dc938e82acab3181ad5f2d6dd89d1cc13983d8d8dcf1856a20d7f842bb9250c8fd5150c8ede47e761921f7c32b771cd2e1e5f955517a5952ac48ee9ef8d21f15c13d493cd81ab218634c2943d8dddd317224c04670cdd1472827d3243097a38f1007280ab095a3cf1668c026bcc517688084206cd14506d2c25a6c61c4905dc10b5c94e4f6b2c50f725fd37274b6451b323dfdb5b08802153e3dd0c10868b006277041031c5280051a58a0031b542106316707d3a71891517022003d40c3134f709103356011d3d0c4089ab8820d2b50411270d069822f68914bdd1970789cb238a250dfd0413eb9a1901b0a2cc80d8533e436a18185522194e96d900b32bdc55f3ca103991a21d35bd60c4eb0e246a6af6684e10c9bbd7509b451d60c4d7001ae827c904aa657484972932428532700654aa98d5e78810657da4f0b1ce47ec4556829f1a34ccf48ea94cc79acd1ddb485ba25c77e4074cbbcf5c974512401d6557a58196092fdecb68ef5d5d86ca95c725fc614c97e00f78c49252581802290a90a1cc69e9094863cbf03d34ff2f838a124db384dd3250bd9c9217d4d0ccadd56cab2685f627837554969d73eb06a8f920b31a57d4d440cb9de0ca3d7b4f36658889223ce6ca98d135fea2d1fe050fefcc89f0f3153d6f617626caa200803e61a43c4978aa3a489f4555ae5884a0df0d7e7c91d094583f5ed499e068fe8967a15151616179719213844b7543b2d1ee0502ac94a1aac97b55bbe82b3e2799ab3595024099d5c2bd04ed56a9d5249aeaff435c42e55a5239da73625070fe09af968598065b6aca5f1a0d2a82b574d89beac3c42477c306f9b4440c0f2b35bd20d4b15647d51cea64a9629a9c58d1ab27c54c912cc72c6ec4832c918639cd2caf6e4153218f9f9782376b19423e9246f1fcc3c8d98001c8b437b3dcc8c78433b0270401c9af6d16ea98f8d9aaf27754abbbcdbb31bcc6f1e088e437bd61701eaa747679687441c1147cb38b1cf8c7de105228ee95d59f3220e79b3c4b84149b51cd2cbb06753cb711df3ae2981594e9aa9774b85a9f531e654ca265576997480f1a5de145feaab96bd6af7569b3965d84ff79b3a6ebeea5962ccf59a2d1d37631fcc8cf8529f5204981e43a4fc0c6bae624c95e3cdb1071341167fb16555abfec69ad15bd953ac76b69ca2c9de9eb45696bd6a13596449e10096578932466ad263c50a92486215d151430e4d39bc61858e14a6392335cd9826956752e0f0c60477883137a6f7eac68d4e95727618623ece396769ceaad2094e7995374abe5a219b50a4c0b56b95a5215ab72005b580b6a0248771880b3f55f60555e8239da9b843ee108900cff7a58622044c1f3358293d1ddd504a6ba66b3892291aae5882062fa0218a52554318195a020fd4c8b47a7d9819a4fb306431e3aaa9a231c6b2d7d65b16a9c1aa62ab0885fb3052016bc10486c733801c904b88a9d1eb838831a327e42aa7c03d9459f842f7509ac1fa421a51d6ab9107ce967a1b45301b4a0211ac0f63cc7579939d29ed967519513e2c1c1f8d6a9d964044f50a22585f3fcd83e915e8f4174466d75d02a4c1fa2dc7cd93a066599b657196852dabb32c91658d2c8b04447cb1e64f6ca929ed75368e8ca9c6892df5d348b7f408e24f2612c2d348ef541c82b982f999a938edf5c3640d0a1b6020d289181373fd119d8a386a12b91e8c31d5eb7b00d1291fb325d6df1a82b91e20d768841004125feaab7a1f73debab262fa82c8ae1a27b79136d2465a27b7915cdff5ed44a79a48e334184a9d5c0f001ee0ae8d335b6c22aa9f884444541b011263eceba58e11b573727d7d8c31d66b3fd1386da473acb599b5d75acddacd5ace5a6c6df7f85512c63b4b9596f422bd9138408c20d9895d60628c7d556daf921029901951f2a7c16a651280cc54bdfc99afb37a55c6ac0b665627287043e08c1e6c08eb021bc294ccb0aa926c49296beda253d850b75807bbc5ba17d9c2943468cd39b1671886615876cdb8aaad41d6d6a04e4922f4d4212f1a1587600105911a5497609124872cd97a58e1541c619593ad5b70460f36c49255b5d68a0d615d6057c086ac1a54836472f8b0232a210947c8d677983e20a1345e6b671c40e646ec9961235baf11a720e11a8471b0206ca80661433528071b52319940974e55a11ad4a0f51a94adb370435889ba24ab31f2d5b61c69466f0fe1a37a1887aa0f6dc41133c6d0c87d1f7248a766c4a11b4472ef70818943497c4ce094e55b88181344ad2c7b6199a5f180bd58e24416fb5a8c9c753633da54463aa9b5c973399bd7091c56255589b42173f765e5911ebe6910071369d47c4dd23a4ee022f8061be95495c2fa40919b7424c6cc4092bb02f1d4281a3cd260e31ba1dcc73743a4bc9d5876116384861a454daf9567a64e3d240e2b0737393385a20332bc200b4252bca00338fa5867a684c8600ba0280089808a35cc00471fdff4314e3f6a561e8271f08d1630cd21bec90d13638262cca5f5c5524e966373b2d596c0fdc7ea5953aacd5c197b164540b8d565babeeb420aca936dcec93657575b025baf9a0e59e4a456af5a8eeb99091298ceabaabe79c3836f555595a3de04097cc22ef3494795b16f0ec9a2aa2ad5dfcc166b24c660328836b2cf06f18988bad77e3d6c2237d37ef220a6e417427b93ad10b2956da022db9f9e37f1c50738ec219985d8627f3d698528a91065d8639e1c8a2fb6a75becad953dd8a7e5006b2fc0610f2599569216d29ac0149b4332c58931b127c66040d39b3dd3a7c12993746a26d1d2083135397063c60c17171616959e79135f6cfd668eb5031cca9e21111470d83f758a46a492c9890965d60a49b7354102a39800dbc7ac11c9d167094e64691b89cf129264297d3ad543b3c5f6c8245ddf43d3ce2fc4d57ba27be585710cd906a179ca38e756ed5853744bba1dddde3a1263bc1cb248de883c23668bfd08c86cb1b73c308bbc385baaef4433ab2f0c22d70f650a2c7a288bdc74fdbced2c4b645923cb225956c9b24c2cebc4b24c9625ba27dacc95ed3322d735895cf3660e69548d31d94c02c81035291c77e238158e83c17131386e85e3ce711ec76d5a0fcd942c3265912ce5a40223c6cabd9b618cf16da5e3cad5b499e9467952811163e5de7752811163e5de67724d664e1299488373c8c49937277767472269d04a2498cf4f8cb9b1d5e78d44229364fb21a6b54aae8aa6c1d0b4189ab6a269d7344fd33e4dbb41d36ea60223c6cabdcfba37683e6096e9565e1891d8346803a427a8040c586a8b5a324524100000007315002028140c078422b17038cf024dd4011480108da44e6046950aa324865114e50c228c1062800c8008008c90066910008fed8ca9d50a679e83796a94c3b9986ab4bdbc3a5c50238467cd5ca33de5d5f1821a23392fa4f102e5f564811b217c136d8d0bd4af070bdc88c0ac987fc444b3994b0ad187d42b484aca4421a40bfe88b9127c45a8b5c81a6814696e31f20608ac22bf88a406095c317a29b28d504c45d5f2c81aaf4045e9cb486a4ca215cf47a051c11cc8fd88e4a288ce821b505fe37f27414777934dc27aa78d895776eeb210e9860856316b39d20d2b60c5be26b206695411b78874631456a1d667bce0cb0d7635b455da506a96262bf175ca9984671c6f793d37e5e513b31a1a2ef9c1c1871490434fcb78dc6ce41bc94623a35902b72db52b829a356c94e2b9d9ead719a14c8050c3867627b29dbf717664e63ef8bdff76fe9614668361b9efca8fbef1dc165b0e22fd5ef90661747e8410ec9ba50a03bad9497c163d3a9cb7379c75ae0387b450f438049e3dc80ca6c90684fa16d82fd183b535d195eb25524b86ef48dd2d92b095f57f5af4153c6348ddaf49685e1b3335eeba05f262a805f968b2693f2479d7e88a5b2e2778c1956a6317b402a9b2ab19171a54c54333d5b553369ffd92a5d2a8649c0acc4a7617b76683158240ce8ecbc9ea5ad12975cecd077dad2271153b2b5f677a33188b25dc7e7c89752f83b3e1973968b00ae5828f23d2e8a103d15bc6601ba9e42fd6439da32ea17d52ccc9cf751716e1571c0866c709dc98e716b02f83f2fffdbb6c759f5ad40cd70fcc9386a2154d78e5ebb1f080187c3c544f629c9c6e0bed321446eb4626454130b39cb1228353a01048f55c9f7db97c96cff9d222a23e4f3098389083644cde1f889a333f0b0a4cde2a3d39155b9ae6bf59b1559ac4a3fb6272e513902edaf2ef68471372f958bd91bc98306e1d24fef1ab9a9d03c6dc3b4df4e46bc3b0eb542254a67f19e3f2098a43aed14cbbff88ce2af0fdd82283d88d0b0e7e97bb0858f052a8ae70821cc89cda4e243d504da8fc49c5dc5af9bebd7f54606f933d4653ca31989521b320714b0599f36161ebbaceb930c4931cb8d901768d1f4b078eb262640bb28dd25159f307107c03906750dcfa31ab3ff801a7cae0a47ec9d9ae03cbd95e27b3a2b2923042606a0fdda58c3fcdff68e5b6fe38614ac77c9a0e2a56e98825c04dcde69c30b9115dec0163d3dcf2d7551fa9d14be88f09d7a8e00fc1b128bea50bb8d277c261066a0ee31346ccb422a438ba997d8ba8c40cd92e4828717c504af366cb6c5467fb7028a9764b55961bc1248672c901174722c4d63b910a96cd691209e22fa105daee335c1e5f2e8ce886020fe5f12cbb552ea3b75fe1fef21921f36482fd1a0a52a1dbc0e18853baf936394cca357694f979b9f9b5465895e5d614e9236f87c0052106783e5b61a466154a1605049b3bbc6e16476a702202136b02f6e017fae84c5241a4edc21bed1ce7d1cdb1bb55c7f4297c321280323edf8f5ccb999594cd56a633614fa052ddfaa4f79095cdb0a9689080e35bc44889cab1f1cb94d702f76087a1afbf0bc390fdc032538aa8a9ec3bb2b4952d696bd930632eed5d5817a16fc82da426ce6421a35b956bda70aa3ece222d6844b4860971b0df965d58d20708f3d848a7dc19c7fde931617db535732cd6eaa4136983e7b87129225cdf59dcf40b732862afc06f60e28016f4218e9b11904a8d103b04e41990566384592cbe57ebc51ac46d327abf41d88078159313e423f2008464128d71d9103b51782032c05f5eff44808241c18eee3bae4bc66ff003e6beab3852a856ba26c4b7f96e2ff1643fd22b2bd26dca08cbf95ff4e76fd2f9bd6c8672df64c3d4d9d9d44d3a1f12a6404abba8086d3180e29248b6bffb7c95f1a283fd2e27717690d2867a3b35a036bcde3db900f632b9634145d24bb53239d050af59f30a5bd630cf854c1aacc58e35e46e13af5aad2177629b4ed6c7c0a5c4c034599fbd149e6c4e50a7b96c7f1785695904c3658e5b6dd029f952e124ec067693287b8693c4032aff61ed927bcf313a29f17bb4e201e177d404f2b6c45d7e15380dba894eb4144a1267c826b28eaaff8061badb1f0942fe5b04716d5873d997192be15d6efe5ede9d5f41aa2a0d96803a3955cf657c8a6bc3d611b83960267dcf0295446618a45c795c3557b195c171a7749d8ed3650e0f25d0eec7e6e849d64849f33659671041ccddc68444e25d432032fb883c13d14f4b7e06fbbe0ac762d14069ee318be154359a57009105729b31abaaa48bffc74862bf5c20734e609d204a4f754cbf26b1a7818ca692c7945dbd492e72b9c0142e5daa57476c2695db1f3ebb81f44b046a1cdb93fbcdb06f1f48e991aee32c48618152c2fa815c874e157d9f664274f20e18f212cca0b48b325adb5e5773690c2bf80e620d366ba6a95a5602f490feb9883a214d9639ecc9222b0b2477ec4ff72555040de756e32c4dd891f79da1ba92fa55609119890b4c7b8ddcf29ae230fa75157d3ddb298eca15318268e6d077d6b4d434518d5ccc41c377838959023c1a6be0d4334862fc38f669174db6468f4fc38c9f2571316bc0b9569add6f6cb2e7a6b816a5bb06ec922047b34f3a1fe46c72b65cebec721c1d8f2034fdef89a88dac8f9bb561ddab2ba915390f6ed3df6c505a7239c9975e91d15aef868a56dd6480ec029f0e12af3b5c37862674fb4de0fa00446c2f701dd4ef006c42ef02132b11a80ad2c39b527463a097cb5f14b745efc7821e000a1dd1a8a0c2cab67b54705119776ded9c797267a193dc6f6507163b09db5589b0e5365ae996e8dce3dea933fbf1e4aec53cf3c7e3628ae9c1220606047a91ee8c3e8bff3383a72003ec2c309a08c2c32526d6e4bfada3206fa13e9b80e7dc4a206463ab0236abb60e8787c0e64355b53b8fb6514024c9c0d9ef4197e8762c40a82a567c5fb462836653933e369a7144a03f38b6fdda30b1be872d3021c6c6971e4e6973fcae4069ce2cd8ba7424472c5137ed588bec4be2f0ad49704191e2193079e8011350a1d260966f23d8fb7e57f6982fa7eb948cf2ddf40473eeb72b8b4d674f9311ec84a33673210034c285d37cffbfa3798020b339eb64b29147c04c282a7527b170d456eb0dae89beeaab4939f6a511bd608490e13d2f8d3c61315bc8761b93cce4a50605066f08ccc8c2c6f21943dce5fbcf547a522edd648cac02fdfa59182099040297a415a2de9961eac45d7454643cd36ff01353f62e215255113253be8f518624fe9a4c81750f55d7a95f6fd7646ec838d28f39d7136ba6417a878f1f7e940d8dc21d375d9901f400155bb9763f9555cd10ae929b81fdaff0a28c519032b7e4c6042657b8ab46e2c004deff0d3cdcc4af06dce1eccad45317f63e2ef991735a323aceda6858eddf0ad69c37cf976990dfc10f015bdeaf5504cba96aadae325912bc4a84f1609c97b412495ddcc8b65c6217c810cacc147cf41cb8900992f972fe709e25571dde0e4cf26f177c75429d334ffc79e2dae21154e1761537e5311014316687b133649ac94da484a1f8f3a023846d3ed185fef8736f4fc4d418d23c0d52e4327813618506e19ac254d52839cca9e02f915a54e4299552c70135e4971350a0b1f5d3fe140d3cc5d209922f94d418b880d7cf2a6172576fd47aa569304df353ebbf848b920a8f0cf8a2023917027817c491131e6b283acb9e7f5cd02da18935ab5e8a8a37bef98e3987f053f5a9f3c134c00c6d660d04072e97be4a3872e1d038abbfc11ac1c3e74e0230aefa39deb827de710d0d1fbbb01c007c72efdfc59a5b300716ae4d7e50d1198d6c7fbce737ea4c85e1c553a9285d351cdb8d2875c3076d25d0acf814f27b0ddd2ab4b39e997615db5861ffba9faf4b6b8af83c4c5b1d9c4e8a31a2c826239a53c9a3a165707dbeb60096e931c1cbf8998ef36f1c8f6288d7dfec300462e283cfe7f3cba51bd0bac7abd07d19a06c9b452f7a8f2c3039d73581721717385f35398465c3506e65d832cfb9cbca9723b534c8cf4efa5796908c97adbcfa33951a92d7a9acffa090eaedc6b8e677cf91830f7afa67f966d77b18c084521d70f57eda50754d5ddda3cb30133e8b8c4c04af284b84d92e81532198760abdc925e55ad2ca535d121481c30c22f0aed61acc14e825e7ea378c0ce45a078d6210299a7d23e71e0db0cc35e298d06e9e75c28d6771b84e7f76254593af4935c3ace9742ea5a7ac7dcc2e5d21840c07703e8dd4a8817ff857bba7d6a4857c202b458f814dbb6a43cf629c11b7503f3e43b12c6f1193d81424e6d4d44a66b84a34e59f94b92d75eedbcf811053b4bab3b0c0f6d7880cb19e1476d4bf17009b7a0b9c7c83b7cd529b8df31d9bebc36617542da4610a5eab5d2a2548d9b79ec7099a6eb07b549300ef3aaf25d57300d896d4f2975bba6f8b5babca5bedbdd42becd0bb66bd64f41d8b292f2bbbed96715cff011e15c1e0d5fdcc2982d23272e3c50de3a1d83efef20867b820edcf5a7d4a1522d0d806bbe5a32421d8692cbfd6f8552abe41d8f9a0039c45cbc7540121c4529808c9497f6ad983386b3d79bf9486f08584744f092dc2919f386463a1f61b328a09de843adf8638818922d4e5aa0ea3a1eb34903d3524ce10fb8286df383a8d8c38e86eb793f0cc7e759a6b0acf1cd9a92ba95a518db6f64aa91a12fb0da213d178cae6b278f8d51b58083c35f5c488b9edb80e8592065c4477ab9002524b33412e60546a9d9bf1014e41ac3804dd2546edc691bd5aa2cb7d1d39d5fd85fd903194cd9bfb0d9b9171f51c899b0cc62056bee48f8dab276d642befe158da23c9ab00b1890b6e719addd82476a1b2afae70e3b940a72b15a27e8985d3cad648e4ea7b9705ec6cd84bf60a2d28a1c751e1f454d17e702fecb3f6740ca019f7449cf91dc6ccbc6b98e2bcd662b60df8083e66991dea34a31161fcb408dafb8357dce44b423d15679f210dce1cd4fde3e1aeed1676a93bb53a16a8b6a4dd4a9b632a000e10a6083b2973c3120ec9c2d55d9f8d4095b2f3727356d9bffb40e46a93285f23ea21c81428774d656b53281bbebfcbb4df987ddee9428a902905c18543f597aa14c492e6828fcab0f8ae7e8d1e122e7a8ce81d8d92c38df04f066339a68080e3f2f95e590e496e61275db59f56ff1e4b01418b375b102274fd795aef11ed5ba0584c1284b60890701b97453adf565c0b8865cc39f81406e222e12c0dae7a855957fb247be9f3faad6c78a69d3d75d5174547eab16f8f68850e48fc52bee0106b65d61d85bba93085d71f63dbd3d6eeec2e9ca4fedbb8628366924afe965e83813853b03945f0398394b8a503331524471c1811c12045cbd28122ddfcce14ac3aaca07f43b3cf806721361edd6d244d179a8dd7483a4b08e6343bd2673b409e0116815e143d5bf85af8d243b4303d9fc13794bb6645ac5184ffeaede1741c885f525ab70eac22c404dc64bb5dd1c3a7b4a62822bf44a2d192c62826f44d85bd593e8fdc097ec83f6740543d4c157ccd0d160e1208769deed6c5494ae3fbd067317c5148564164435226538e6c0357277cef3e8883ea8a196c30b1646831df7d787a5eb9c67512e41991acbbe6984a46784f6b6b54b4195b541f430473412345beb46f9dae3c789a42dee904115cff6af229a9e488a973e7d54d210c0297232cb0be9e46cb4afd38009e1a79b513a7b406a97679a5acdd18a18b6e2e2ef55607fdaff55c7c7499fe4a381c7008343e486e4f0cf32573277ac0c40e8a1ffac36c6df073c2380c5b128857edf573842face340ddfbb04e25311f017e4c73aabb5e9c8ba46ed19f6bc763f3a9bb7d65402235161ecada045cb44bee807510838d922c3ad6431f6e13953bb92e7ad4215cb16dfa846ec798989e293f7054cb10c7af77f941b3761e145074e4b088bc674ba6236a3727b2dd7182b8abfa4d6c50a53fb28ac9e27aaf3af103c2fc9b7bbf776748177accbf0e5032f90b5124e8b7fab58fd28c2a9403ca2e19390c85300bdaf46d24921109b6a8421bba3aa77c1ffdb2c491478025c07e70ece75465a70049431928a3418e4f361abb15085b1d4f8d5a5f4cd0684fd1bed7e040035fd81db83e7343af293fd474b8b2e0f5a0a4a5928ac0d5af0207d79844b771e6366e404e6f63dc624afa7ac2d400d5382a63bb12f84d0d7909ea2c059d9c129cc239c70ef28aa9e20085d40835a9de816fa0f28ea7444eaf8a36c1601818c2cce707be211aa4577a1128435d764f55bcec9b74cf15cbb3c59546932ef9b9125b08966d652c996ee2a8f30ff715e9d262936f347d574c979ebcbf61e9e2d201bfa09472ab45cc3d82ccb6e1f8c9b182c3678bb43dacba773c002fb47cc2411a3af2b3bf0e8cb53a25e036ba8c655c9ebebd172499b4a381c0f21815440c3ee25fb4105b3e998cf88857ba682e4e10c97f69d85ca025c264e94c9d8b40e32c8c0bef80b872c26c473a2061ac3bcc3d5c34420b5312a6f6730bf95cfad68f56d3d8f6f251376def2ae14104152d7d5d0b1cc823e29e2135f552b7775bb03a6d79f5d77bf59f51cfd503e1d4303a129eac5b4865d6ce8c05c276612d199a098c29db91eb84db9e03b78252e3973bbb587575dd07904e8834b5b6c34eb6cb2ced8a78e73640d132595e799632c83c77e620add0bbb1f22763f1d31fb9f213b1e1426138ce29acb07a7f64619280bcfcd03559273e1e978ab6a33b75f0ab3e74f735962aed6e81721d8299f5f568b035f41140411fba17be167dc9fd6f41b510fbfe81866e00d094b08caaf74061a9e28095ba7e6a71b6abea43a4f5bac8b5eaeb1cc6e6f3c46f2d935b7692c35abd13d028234a7512a4eefc4df5d5da72f2e68c2747a93ebed2277008ea468966f069bd90ae4f87efdc5a94c6b4a2f44ef52c71b5c8eac348c57bab350a69b96297ec0e1ee8f2a16b2e0278716fae288edb51111a1f7c1c2e54bd62de726b422adda025f251ebc3702b5d6a9cfa6000cd4a82ed0b9ce11b5c775d2b317efd129241edcaad6308ea4a2672d4e4723962afe8c6221ab01afd4bc4625674f1a5a137ca7217dcff5bc90e218ca04d0a58c77819e01c74634151d433d959acf7964cac83481bbc8f96c1c2eaa0bc0f5a98b1d1e5e35a139bae6920f6dc577f3287a9cca7b688818f5ec304543f99331cc876a0cb389072640b8f52809e9245ff4b30cd6750171ed8234cb5989c4e5925755825e02f0153e0e34a9423cd79fdcf339856896cc5319c1da0d9245e317df878959a8375151a019d7d9753b5b83771dade7c7a42e1f6ac1a4eca00dce6e4f73c6fba1e4671591af67b68c454a1741a114e2ee568d5b70b6b3f48acc18f40120c0043f5b00134a8c29019dce6cd614d83d20b7bdb6a46b628ff8bfdb6f93a794404cc21e53df3107e1d131b6e20ae72191f7cf9d22d660c77ed8a386fc2ec4e90e5bf9308e40433e3e8058b14d50d9bba6b9ae796db96180fcd05612583eb34432a50858171e403cad1ee96fc9595c6e27474145102b06cadf1dc09cbf066372e2697bf127b922b8f06d6634dd7cdd5ba31af1c2ec3e2faf8600faec10eedbbd65810e28c1d13774160ef4679a4ecedc346c13d118a45edca5ea0ccb73efd0a6e4443337b95c12988cc1e5a3fd6d3752865105db1ee28f72361d7eacaf9b5ae861a180c741320b2d13d9ad8a86b52e34bc07ff73741cde633b3aea0906ea02473e0f47a06d7d84dd2646cf3f5e376a59b9379de1d4d4e7e1475beed24f2a663414db6148cd468da5a622b0b2f9866b69d9fd868e8940611b4c0a2558a4598250e8a8ad52453df728ffa23cb7bc3a492951fd6f4cf3e1df479b5354a94e44ce12030607b50487289df787ee229a00c222cc00dff3add2405b3de149c313b7101df2db846b48f90a6405fc90fad9220c2ec0411457b83e2d017cbf4f75a14a1fbd3952164d7f100c613a29b6252f4517f18514e1bdc6ac21458e75d49b3aadb924a48c6f1708864d0e308029561f92c04c2a78d4f74c9f3583e727bedb5c9302071e795b9d9d2d9b212e2c3ac9eba1c488428e1a3980fb46cf326cc39f200ca5d84da48503bf438b30159536d34ebf6459c79071de5f9db066b925e42c0b373a1273b6217805907e442898c98630656c35dda2a01fa0088d158caa092a7aa287cf5eb051b8acd7d0ac9e829469221a4b35a0ee7b6c482c9b57005dd54d2b5a5999d94a317189adea37fec8b5bd8a5c9d164d3a8aa22e8adab1a862ea245709c8bcab4ed1c2cf41b61834a288165d7a1cc72661b2d53f09ae0eb2a6dedfdff40632a7bd7d37246457f098023d5c5497616b5bd91fd597047bc3b7bf94ba3cad2ee12b0297a2aa88e6c31e4fc0be89548eb25a12d88bf4d087a7e9f61a57459f1515ee30e621d678b19563e1114e2503e6a707bcb284f2304ef688fdc828f4f2d91ee827701b28454e8afc24197677795f387c8f73c3a56f28e41ea9781ba6f02a332e1a276c33c0b58451412679bffe45a83a76634dfbc5796081985ed9c8eb63514f79b6019954f1e1742ead43b0a790284a8625cb3c6db21a9a348bdcaff61292e22bd6a84ac2f9b0be9eda276fdbda632c7abb489517ca12f31b4261fcf62234493a39807a88eea678f494f98bcba455635eab6ad19c272bc76ee5754701c6107ae2b1279a3cae55d5985e48655f6f43ca1a2b95434932217767024b5099a8abe34bc321056a22171fc79d1bfeebd01931b29490e557ded2c5d361960743b3c8531688468f3a01bbf70382d70b2e693d0ea9ecb62bdde77326351a7273cbb9731699e7e3f4fc3474fe7a6a76fb0ea2eaa914a7dca8e9b8fb7b31d817bfc12dce865e0a416941ee72812f5dd3eb7702d28a5ff40553b604515fd46f84fab546f966abd233b8bf5de9d31cac052ed77b26295757cf63b983a2e02a3369a1bc948e2ed80d7722fc8dd5829e9ce74af6101a3f1feac167e5d37920e02305c1ab435b0322a700784aedc27131a353621392891dc4912e820d1b4eb8ac690dbcd239adeeddc96677ef161892809da479294302aca12256e361ff9eef3f858ab3af147fc7f09a67d0d01815d241b392987563f3a6c2a2d156fb71c59d5e878ef824c2cd47e53369610f4fdb04c1edfe2ccdc0c1f1827ff5793a3f8f679820e569d4f2fa42e87c625b5f4aa6d1a4426ea8115e42036fa121531d2f1c43461ffac93e715d4876592a59ae3ed60571f52af5568f53f2ac4da90f31e42aeaab4839f9a8260597b37ec3c88b915b772419200b786e689f5ae9e9914aa77f226622ae2b289995f3d173a15ddc599ada13b6b32a72fcc416a826e32616c24201e70ee82bb0766382328ebaaed82545d07f7a8771edc9049a082d55024196c65bb8d4e6e5c6811bee260cbe931d95c5682203f20ecdb5b9223b86c6aa28bcaf06551475ea685967ff508c46997b885a0cf70361c486e92bc786e591476d1969b37966f03e53127c16fce13749e224ba8dc4dca90f16d0143efd4a6077d1cec8737abcc9cf3cca6bea45c9f52ea65164198cf77bc872763b58a646a09e8989a672db90e5c168f8c6cd204d999b36208b84ea4d695655190c0a3e295e33a805e774012e512dffcce3838d1f5471c24f0ce92571c8cc6e57f02d628466aec0ee3a31b74a7026153b7d95cd23f00433a0f178e214af978b80eea06da5a6a47449a32e2e126be2bc51deb406756cae9dd9becf078a0e9118ef611d0efed777fed694f6b74656fb169e76915ee032263363784b0692266c8be4d6b486dd95d6d2040ef8050d98626624d4be797aff0804eee9a67ffc22794f4de542d8d6616a06e6a2f1d6599876a6d951c8c574527d4ddcfe30af32edaa16816c1ff1942166729da2c02e358b8bde5cf0739332c89a12b9f8a455b34866ff5ad2d0674b43903a342ba3967362e2a18134d348b2618d8e9778fb04e39b1d214a6a2a0070855439f14330410c88880b501a9db7b995ca4606528b99b63c5f174e5da3ca28663361cee38d19df0b0e128981abd21f59e138c2c8bf922abd9de58f2f11630ddc819e81561eab4389e88480adf36259f3f812d3fae7a2934f25f9a933fdea5d96a9238aae2b0f8fd4c39e5ae6ca32d97f199a0b32eac2d038aae54cde3f14dc8ea8f8ab24ad65fe4bdb8b50ee928a04146b7c3531883468836a3360ed2bdd4f0ccfe46f9747b511a4093c770d0f83afcf42b2e0b7d67c01394358b9730602c152417b382633c5695e7e5708ee223f678f444625b53fbcc52271863d4461e4324bf5bff6f23c407a1cacd5d162e729c314b53a1f7f3e4040a6c5d87ccd479440983b089f2b9c87c3f1a62764f8800b4038bfb774183019ffb11fed17881a2988b117efc074c134f850f634450cc6b757d592c5dbb0d6631273e842ab8c40641589d418399cad9918eba94e12db13a1d140830186ac298b02e82d9ed76222b4a3c2c5f460b1cde70049d53df015eb6173cf6e2c8f370916fe7ed9be1ae5c9b236cefa3fd0a54e8419b83081508de00de0591bd5b1f5af4d9b156cb53d40ad7e6da22d939e9e7521e552b04b1b4d53bc158d49ea1725e70a08d5686ab41485efbc3a11bb953499dec42c27106fede58dfab855ab950a6d1b036459b85768d2cf8522d19e37d14581e7eae212b6b7bdb234cb81ead1e517410094f5cba5bc1749ed10e1e69c6bcc79b8f3c68c64765e3b024a39debd4b7af003a92598103b8448d39367699b3f1df6ecdcd0c32966622beb8ba60b92d3212967736a255e50d0eede9fdd9425d9986a36f830be9f9393678882b7f93fd871e546b2a2f509e7bf856781baf0b8346bd9a8cb7d5791b3fdd1d394e48a1312b00d783498cd6f15b4ab5181479102aa9ec3262198603356d0162afba85817186d66b9fa4783d2e726df5213c2aa8f20884dd5a8cf17086c99f9a0f04c87eeafa7c53cf33606d5e45ede3a549f3a454d75799e3fff071fda76c09c4b7662e1a6433a533c1ca800122f6fd1bc494721cd1adc042dbfb9c43f17e3b69fee1da17cb22d684463bb8971db144c37560a1bc8cf6f8de35122bac384d08c21b343f806fd9c0cdb32936f491cbc17ea1bbfa489430f1798c5c521aa76b2002af136e2428206ed8a34539cc916f537c57760de15580d1d985c730a78968200559612bf00a3b42acfce3749196aaf7d1da48fd506eb3ba7d15bb442ce45738ee0aecc61ca5b0e52dfb684df95f20d0c3c1440a81df393c5c9a87b03a4bb99194ff8d060b30b1cf82aa5bf793ac2a1f0c5cd81463e64d2983e45560fa4b448fc25a9a3420aca48545e71b493ec0711bafca2faaf23546ed148c25468e928cc9d372798e97a2989545f3b6fcecbd58f7f9196d23c8de420ef715f0fa65075bd7c2f2a65d3911331d55918da2bc68d308e456952248d0d43f2f97a3ef716f3c5b9ab0e3ee463b0cff157a3a1bb3967ac887fa5a94723be3570c3e63f675966c0282d5185a3e6376c2e80c07ffe017823f47a347420112c07da3fc0983aa4b5bf46b324399959f00cda400433de55265c7d08b2728f2e8e5ce5030f35dc18f8bf596b260e05f1292805e9429495feae50cc30d43f08251191b8aa0adb0cb04b516b52451aab02889260a42479af1e582bd105e833867490740e3f3ff814a1176837da9fff19f80dea8f48624b119559086b8ff1251b110de3c05c5bf2658dcc857997a6fa7512893ca9f9e38e765ab1139ad22410fa90ec82d178d6da5af32a744c93998c9ba132d6e2c3b5c45e33513e9ad9f9f1c173893a1a7f50b3e654b7c39092078e78f869ef7bfaf3941f683da74ce97a83af5ea20704a9cad0f786b35cc109239727372ddcd19ce87b1fe8b83352ae066bbe99b6d708cdd43ca8f8884bd97b10391e3dc66a07dffbd5a0e4c9c5570a78af7a0b85c91cebe4c880e9f735c5e140242cafd7176ebf69fc5ea10b8abf60ca6a317dc82374c9105ba69227fea497739572f6649deb7bc5b5a1c57d8dec4d360cba77cec528c1033030eb351c4a29b69e5e73045af06ecd8cef80669d405e524d4065cd35f59b143773fcaa23a4af2f8f92b1e5fa299781376121d2e9433225fe3379659577d707b246b5c1be4a055cf39a6a8131539368390f0613ba167c5fef1186005c133d14dcaa074cdd472008e1c6116aa8b3f35c16e2da19ed17ea71ec5a290ccbfdbc864bf3cc8a86d6cb733b707c27f16574c5a1750f6a0872dc7192199b49f60ab030feef6c8bf00302bb32d773d7fd09a9d27647ebb77e17e23b41fc427726ca0bcb3b4ee7600582b9faf65d649e9e64e1e8d52e95230b9e8bcd269ab321e9bcabf26f297d1856abb5ba90637d64fe6bed32a0bf32c43d091e8868134af56ca64e4573923ab321361ee11ebd3687206a85e2a93585744b1634ae7aefaab55ca37a1f3f866ae03136aa8bf39eb4c1b27f9f0d68149468a5b6d6145eece24bdcf5861f0b73b739447c1ea4148179a4158f70ed837682e660f49697c657d527d59c1a52a5c364226a283f86d118a6923b002b9f4db9ec9f7ea817044478a1bae33873c5da93e8eeb85a0c86bcbfce11026772788cac7077502836bbc2a77a3d3517334436de0ece702f382650ee228bb68ae8c50fcf385b3b968eccfd447dd16aa98161716b2db7a099d8c9ce37a9b7b22e78c80d6d773b734a81915a7e286a8f9a31b3882381756a630c0c72c3b09caa7c8f9d1bc020192b09527560318de4a059c2d9dda9e9e51ec1eec1320eab1c1c95f738f3dd2f03d50f74807bda0e88256d44084e9bc7d7561d4759729467fc95739c4688b098fe1fc42158cc6df2e2a3c23cbe2ae8dcf10aedb6aa131d4f8fe495eaa4a1e40ffe03fec6da27b347a464bdf42ff30dce7ea15004a8dffa5b636d0d21f4e34802a9bb435df3080096bf0d55b8a39e97699f16257d2a6a77496fab24c891c61d2de8be773b7551c0a1e8964b3a012d2bb30ae4e38ed9406248c9d19e521813fc91753d94b2b511ddd44b841b204496c4fe0002eb06895cadffffedaaa9ccb02e92b834c4be4b2b97f37548bcfe99b46269fb913bbf33630c57a35daeaa6a785b92b93740731d3cdbd7c33e8fe8b68c39ef5deb3bb1789a2f1bd2fc0be4595e19f5b9e203a98b3a676e99847325d861d1608538c201a621a6c03b54889da9111e0d41280281f90b76460b9d67b328a3421199ddea7f92b2a3722e82d66810c45449f6c9257e4b98e6a4a12521e011b3282298110dc5b82d2cd65db6219e24ea3f78d5f57b5a41b8e17680e43f703dc441a0d8765fb0fcd8623e745d03ffd495f724fb6b356b8b6e05060446fc80eaf810c0ee2e0657c18886dbd9f43a62fc35a9f212ac33983be187dddb34fd3464b4163e7be5ee277fc63914956767c249ec6fa3ae0100853937c34af14a39531de329302a9de6eb3ae2860ddc237586c6d8c9cc129de98b1c3bc9c1edd5b4429600319e0c1c7e5604f6c847cdc3d9f939e63b33165d5241813f10493850c644e976125fa81d5fa7524039e8d64e6c8be392957b8da8b8b4410013653fb287a96c1d59b42a0a4e55eef14a8fe808c6fe9e10337f013b35df41a46afc76cd5bf4fca7ba91ec643e1cffeea4bb23e1347d55d30e5e78eb959cf6c639a789ca8769a9aa905ecad6e9e2f204b1dc002b9ff5882ac3e01838640b9baafa140025b9e44ecda825bbf65136769e191477d15a163288bf49f1146e613bf36036b716ff49bffcd13d53b84ea1032c8009918193ef17707668e4fd62d660cb71336ffbe195f61ebae640345ff2771117b44dd0b93d733a14de613426781db2bc42c17ccc4f169cd72d9f4ae2e59a64020c1630c780186518a2fff721d1f4370bf1ba459c307ddc31eb34415fe88c431b1360c3221f4f729680ff7de6d393ec53bfe77290f0f0215e2a5e8eb9f7d5a1ca85e2bffdded0e87670e7d5131874488351f1858589bd8ff8b877815d396095e28f2ec5b6b93ad3e6f916f0d3c3946f9496d9290ef300038522cf5bccdbfab03a49d68053a75a0b5be70fbfc8e36c1f06c7786765abc0a341385eb3e35b1404170d36b6791cd73d2cda9f7b229a34c6ca092bf95faf2574a0044820d75ba0a65f21580e1bc803ceecddf99d013c85791f1e3c34fe0bd65a2b3210c9456385543f61635f7a9b22a90bda4d91720eec302ba738051e252dafe21569fe296880ac36730f728345ded9c44ab39b5eacdddc5a7f19d123055c4ad8351bfb54bafb34b56cf85128cfa2b1872a06bdb4e911fbb5fbaa7f6feb4a4570cdcb748187dd28d8dc579a38ac2af4d2678cbfcc9140a9c2a20f5b8cc95a1ee7017c5999a0b7ef9a4bde833f57134750eb7b160de70a11121a91bc2ec4beaf78312763a39edabe01685f6144f02b30f2ef4d5a8be67e4a2bb024e9910db4173875912af09569dbb563a79644831fb20b8a72f1afd57cabe1df49825f7aa730abf157e2e7fc8bcaefdd6e04800a22d0d776e4c89237605edee335233e9c51cdb42182551f9e8a81d5312d84710d031c77d61780e76429509cf1a55eaff100c47b9a4805c7faf48b7cc938f94600aa874d89f2c0ed0c98a6f69cada3f2629604459a6d32e5bc121522dbefdafedc36a3e155e01eb1890bbd19a9896af8475a97cb0a2f3482619d74c983f8c42500eac8c2ca43f9bec860cb7c11db41cb8b2f133c55b76ac54cbaaf07905ab94d61a417f6d1d9377fe2528068a3cdce41925fda5962c719a3f010f65b2171ecc925924e94c07cb0e28e0a557085155612ab12f2e5c530d8eba0a8442e40f2cc316428be4b3cf6496ca649436daf38d51438a2d7bf53b0dd87b2e57e3e4e994dcde854576ddf86da31e15ce71c35813ef16781fd9b3c2b1c4a72a043aa359fb565fd0b874d2cc61e3a892a29b3f636d0e981f54c3ca736650d42782e1a53c533ac0b9d3f11d3a8b0de30156e9dc8b25c44e4fa0cf67b042f364ab09579325a67fda7b6c88a54264b00ea7b97237ad2476a066e55212686ca3d4491cc4d991bbb6ca4df1b176ce0bc59cc2922d27d6372414db9ce8e1b1bf6360d1da8223a1b07c0d0e95895fb39d61c340d6f247f3b62c9169ce4c09c40df642c94258b8cc55ba144328887e233614ffffd5345ff249d0378ffcdac85801a2e84ff089c42d8ec540f5f0be7d5fdc6afc7b1085321f288e390cfc644347188702b05de34f54f5a5668f607951c05b599b53393fee986184da900f950ae8c7b3101d825c0a28cc667dafca45b7c68aca1a9094b30d54831ee043d879faa77b43dff664b0d7b390faa1f6dff028e9e490b5eb61a84f38c3d3f7d3ad6158e5d5ff5a0c7236296a6c38d80a33601e35338fc6ed5e62ab13d31246b54db228735e945ace98529e1942e240e827baea9603b2e61ec7e3ee9a87d3e2069cc6ceb8004d1906d7a96e6fecb60b0143a2637c4387babceb1f82aa0ae86cedb60f8a5aa009fde67e1c63c7d1829e9f9cfe6d2c03d4a2d5f064b0baf66d32b2461d9fece934ac737dd5e657e3b0da5bff699889ac44dcd87264154cb019ca94bc45eb8cf7ef419cc23c64702ece11205f236f7b64848814a23b2b2c599b8f0c1f35bf97d34fd10e49540a110828d682327c4e7dcff05048368b9ea798d091da14f364074a3e1a7d0b21e09ecab66eca2dcbf95fcc66438557925904980b257287123669bdeab3a2871d1c928300aeac5a3fe86809bfca082942e65748c285b30ba7cc7911f82a2c54e2602398ef9b9e61a31728fe6b2a06c056b72294001f620dbf7ecdd6a8e0a59640a173c8ad0b0dadd77f75fdb055605198acae855f96dfefbcc0c790e46f70ea52de05e4298224a0403cd7ac6f48cba0ecfbe4da2ef75b81d1302c22a0b7b93c90bfc0205b385551c09f197a7c1d3cf742e4230ec9c12f4e7b94c152ac0c0b1b5a2eb67a0a35c458fcd55840fb4efdd47b973c6d2ec254ef4576704ed9ba2841c6c56caf3908ae9051184ef35c28ee756da06b2b1c129d5d832ea4b1012ebd9aed83ec344d24ea8bb93001c457c9ca21b352e92470eda963135349434bc7cf509bf037ac28fb337de3688de8be84b3189fb27b61ff6c62d921e751d539335688a5e93882a8908abc3668724b06fbf8be8dbfb71fa2419010d47bda395a263d68eb75392a63e88d8c807c4aa094faf3c3ade347c10d7ff0cec4ddfb7069c6f107c04f3011e94558eddfc11167e9027b1bd38cbcdadc14db6830ad1d5fd986468949cfcfcd95cf9a896141234b2740d809a207746a95eac13598c2093bd8b7ac3a61ab7915d2851cd5203d3da924f676bac75576622dd73826645ee81a9ad25170c06f1bf5370f8b29b3575bcdb2b9369e906df4df57168f3f3c26f4a9870b7cf9fc762b66f9042e18f57e6f27217ad81c97a7dbb67517b8a32ddc59ebaece2deb97c3fb9bfcb40961ef4371336cd7ae804ba1a789137dabff42bf346363aff9f3653d3d67d3a3e76911811d26a0122c388825fd557710bca64681b82e35d65bbe20634d72f6e27d7291b289355e95806f4a2c2b10c105ad7fbf1001fc480fdc98aa9597f56eb4eaaaf964c7d3054b54dc47aeb04e1562e3b5a9ab8dfa666a2d09214ac5de56317f958eeb8737e1f2881baf478d456593f454f5b06d61bcf16a27d1eeeacba9d9d25714dc1db55febc1111bfe69a9a9b8c40c62ed533f37964ed041412af810a03113be8e56d269527832be87a0c2cd1c4d4d8d336b82317cf39821d5831f86a9dd13f70b261fb55574d3b391fd2078993a0c82eb872284ed243486b04284e324fb6ff1aa0a0cd4c0017e6ab08b3b6701b462582c30c01d73c406c2b2817bdf9c1e658026f4557dd175f1bb84dda06258ff75bb86e3bb6a4a74c7e03e201de1cc15312155273885589925a3de8c1304a03294daa6d74656695db74ea3217d15dee8447aee6fcc3ab58779788ae75ddda28a44f5b7b235cf955be69ad53d86a6f5c46d259de38ecb6f4b5a840c5d0760114bc2701d0d50fc7d3bc532c58480adb92889be1b841d51e84533b499d4314a2265566e4e21ab3afaa447c1422ad73ce3c6524acb9bc510c5446431aa14c90a6538698994c2b8611ffa79f5f1826e18fbbb7cd95c249bf614e448993d9dc090e0a54ff27cb0b3b015a1033f72794d0ca48b997df4fd820ced9ab40b0589b982a39665352a7434d85f7906b134cb7bcf950c651197c7b23cb86de5cfa043428fbe79ea943385a69726f0b8a05928c0573fc7837106d5e6ae4fe377848e999594996d6e180d75d07934dd1c1d54de185cd14e3f6a29d25f599e91ca1c99d8830510c63200f7dc544f74e127ab998c4a911a23a7f8e56bbe6bf3b1edc5f76da256d9934f04687613ded20d13d9d89f1d81309509f0faa4498c3220b03e610bac639fe89869101c44fbb9249263beee46c11c3072d29406c4cee85e7d37b3dde1e507898cbb8e7f02653b2c450b79c8569d59bcd08ec00e87eac97b19f588c7f22ab04afe199e5f98f78b91652f451391015c684aab76eac641b11dd43ac44b179643901f04e9ec0d00e92e2993dcecf3926e9e5939a44aa8ef65c2a05601ef319e2f24820340469783f37d6be5ccfbdf7857b90673e71ade173287eb06048e2634c4928ae44c0865198011c1bd4e34dccb06817fc9c8d129c6079465870f96d08759bdf19842a8db6df4cff03f7e60adae5f9a58b6a0735c3e8d5b498cffe0538f0ac0cdef52ba90152012b096611c1003c2281bbd1c7c4656a042601f30ca4d2998607148b419754a1d3ed87f32dfa93c9c77b4e81a02e6f75579bf12ef608e7a2a38e97013aa14b5778e2cdb31af0ed00fd627e94560ec96f31bcc1832d9428fd4aca0e8fd7b796707bf543a2360532a27d0cc9e5b74b49d14bd8922ddc26f13f217e7e30b907c8352c856cdde59d8ba89fc3ce080698d1f487ac8dbc6356f8ede8a9b4dcd4bd8c49299444442cd840110aa080328693d3eacb42d9c159556759d26e63251287bfe4425285859ed629cfe6100731eba757a4404a1c0b1f3b66bc7460e094aab19c7f212a1d95e65940323a36e2d259507e0bce9e65fca8888131cc60ae8420f0e5ada4fcfaf713f22c2237b1678abda70690ed229cffab97244b0b153b39932fc24c32227d2acbec2226f0d4d681c89386cb0c63a4a6f49cfbc68923faa36accf67026e6f29bb052ab9387522e1792b8d0ac0d0e86a95c74a800316606179354b2794fb0d5998a8269097775e42071a82268285c2792642c9f57f91609d0eed3b6e5015d1f47c72f24ff83d1c37785f9253d00763e58ec0bab029c75e0da54b03cbfca1cb073b48de788391e5c5988adb22d7244d5d8f11f988be3ab26d8ee25524d5aa854478367d62a62314300a518f51e2cea30a5c3a1c483cb203b6dd62ad0fd5a166469ce680cc2494086c9c95eb1f95a1edd4be2add42b0b0a18e0d520afbbc7a046b45811a4d94f02b1eef095788e33d7f071062160c894cbbe93f386a2507964abe5f51cdd1990a529c8b83a1a6d0e203d34e6aa8822abae4de4c7afaa0f01bec97ad046e3bc97c5da2c717831cd04e551f61dd9955dc124cff00a4808fff00366ce92aeb65849c37b1ca368c5126934a31c647244f7e060314c52b7090e1cb5b01772ccf43c37ab13c167f52a0d1da700a55be453d189fb3e83ad405c81641a0263ec6bf5d54c9535c60b2b15513d9af90fa031602e623c26b32db6384a5c1078a00b3cf011389a361da7f54b6d13234dd3b7ac28a7d381cb125d22c7d8ea60a235c75f18f05211897e71f8ff328fb0d69b601e56dd5b8c8280189f541f771e0707e796327aa07bfc9a5a0f5a965df21f1a9413ca1f199b7db3194ca7f7b59e61c1d369c7e984da0010920ac521b6b10fda717b4942350119561c4219052607837fc22ad95a2d96c1ff369aa2b84e9900995a17ba829b4d3ba670a50944abe163e6ddbc809869dd3b9018373e8ddd53145c49184a38924d969560ffb30c431eb40a842e1c5b82e938f257aa7af5d601da02ed7d5e97dc13426c79c0a683a47f34258e0c61094d1a7078bde53c72c61c3acdd244a2873a726edb1cfbac1537a1401b1358ea10d6eff36cf929c40ec645e2eabb224826ea2f840b40e1393787fa5f8c39c80130377ac13bc71012e59eea884172082cfc9011859fbfe7794f8262ca9764f11e13910944b78c15cc490bac568c92ba1afa15d27a051e8910b0809850df23522e829099e84d6b4520486d09a615f0525b57a5d027fae4e5fd331b839457cef61cb6c7fd278775e5b88478b346c6a338ae604f2feb39997a882878453dd24caf4ebb512063f1a98e1bb9d1135d1997c5b736bb8c45285af52eaeb84724160304d02e9c4b02ec48f20db866afad4b144e855847f90fb5190ae54056156aae76dd773167646d0e39a225b88d66819528dc44f042c0dfba6b61d891055f0002b4a7d077500a96de93bf726a8294ea0a4b8de8602148ae9152ed6f8783e3397972e67bab869ac6fa9a841b9b226f6be8f9f445fa94f0650100986c9bac59209303430c999971c068433facc8a1bf68c0a4904719dd2fdfc4bcb71b1b8388ca4897bb60cb064d7572c415684282bad3d601b4f45a32847a2282ff4af2e5fc4f11fff58c880ff976d5f7d7a3375f81c9af8494a7be5b6cd830b2383e6a4415a6883d77821a816f52fdf41f7a1bc3e909dfeb7aa15c048208661dd0c927523d1141e9eabab73cedf1d0ded359d1953b6c2fd69cf565549b51ef040d48687194431321abfad3f702fc7b10b074044d22f3a0002e3bca7a63623869680b0ced9eaee392f2281644d82f29b36f3aa88f5da09886e9006e2466b74c0c7e638bb016fee1298f8193096cec2d7cf4e24bfad15a91146bb4aa8266f6181f543a48f2e8a62d1d7794f2bda23fafa87cfadf18ba48c002a876997068f40686fef806b6c59220abdf315c4ab2264c0c13f824d4874006e5d82da59780c338b6b18392ef7fa1a8119073e5f7be5f09ec10153ca827642c441ddf0dcdcda50443bb1380a0a6532c71406e07fb82bb1f8a8e247e428fad8e46f2d1b404847f6b07820e820fec74e83c89fa3b068c5884ce859da3ac63922ea15d68a2c3ff9c239ab446e520f35fb102c6c09ea3857aaabf02dc5714d984e8768e22f9d2644b07fc5a78f68db27fa492fe57f572c43a38320c6ebfffe3576ff3a263d3c2b7cc6ba29929060ec9a21d9b3730c5cb34798380cfb639c96786e0c5597e008ed9ac973903e1956d8f3088713e0170e6da4afb4b8a27ce62d5e74d140263c817204dbe0c0f1259aeead629a679d43bdaddd45363aa91508584f8ae93a4339e84620eb71b1b3d65dc5637e0dc84590c6d8e51e18d6c4ccf86926cd6a8f780092d53fe130767174a82e369cc2b4ed60998d8fb637fb0187e38303df88fe046ff60fc2abdf5230aa1aadc02f2872b9cf546ff64f5c4b20ea275d867bfa0cc80077d2c896b0f460345ae82d3a34fbddeff422c8faf31285663f3e1d75861ce7d7dd1463aa490a776727086872694bb469b90395694a291a459efedeeef9d188e100ebc669319f0a9b944c425bcde767ea7d0a9688e348bdf63530fbf1df3ce810cdb3f2f90348f0b4e0018c1e6edd1a10e1c69b957223d732c8dece0a05349802484be2f3c59b9602a4bf84581e4cfc18069422cf67838940a11fc6983b778c4b4ba4f1cdd9b5eaf7ae11913c922293c19265ff3198c07d022b8c6f3f34a294a1bbec7dce08d260e8be47b5cbad7b0fb047374cd4a45a6fd4ec3bd093fb1b57fbe5cc78ba3fecf38b17820046788298dc76eedca49365cb01ecd13601236660b7c70d2b703095ad0eb5c5715755e88b1f48ec03bf85379ac20efb1d5f9301288b8f927e670a3ab13451d9349e68300c22077f3d9fe196a16c040bbe0821e1b500ef3b674f929ef9175b263219519e91a7238d4e56fd46704f004b1ff75b9b23d90f8cfc85e415cd3a589fa1349cb76de92b733d020bbed4881de7323a2737270b5450cb19d1b32b8680c6d1129cd00077c0e4ad83dcf0d72d27386ec85e8a2e72bda4e199610fc1fd8d5cb744cf279d4823cb8d3c836dc460c441a0004b1e5bfa629ad7a4b86e39891b019587fedb932590f4cea45221cdd7a5ba49ca12151aec22625567a2be43e5345b0ce2b46ae81431bbd165295cccc0d6ca26400e8f660a3ef24559a43b1dd497cae0558bb1d4293a1dcd4e9b0a41238999c5d2fc7a1c3bb641b5fc72f726dae53cc423c70a0dc8e663f40c347259d09fd0a513ecc56d24e29ad275d56354ce306194b33a775ac19d11eed8e9531cc1f0f598e30cec9cb5c6a145e44d7585a6bc509d71a5d5a0a40bad937a4f513f1b9012bf6993354d76cc93b5fcd4c16a1b1fd25f7cc96b763358cf7ed09c55885b101232b0fc0ec13fa4eabc2deac08a05d8d0cadd0dd255125d1323d30bb96b9d0c086ad2ec7df5e7e699f13e7b46e391e9353b1056239a4acc180a0b88c7317f53951992815da416601d115d56bb2a7c884b27059333708599d528f3214281a570c46a7c3980786f556c486f344ae292b6dedb06047c29f4b8e785263eef636122aa0f8ad39f8ed637f5fa036afb8058990ebc9172a66a5f1d02fe8674033b50b6ea567cd18e2b36f9cce5cb2e80b0c60365fc00ef539e9247f96549e3f6672098a2d5698c1f5627244c81b3ead39243085c97237c182b2c35e07cc47294b5a91286cdd499ee2d66c11d2bc65ce1c7fd3ff4e6d0e925e1f85f68d80cb45767c10da812f230a1acc2edfe5805b86d9ef9d6ad09e73355009a021f13ae0e58a1a36e710f7703caf8ce08ca6ab60cf553bc0af96e880ec41b27bc59a09c4ec5ac453606d9cb9b343132138526b6b9a2dbdec9f45640ff313b2f1e08e9159970f3e27efff38b1729e6bb12a4138fdbac4c1e773b6602f9447cf7f9eb45835ff501b3d252aea4b4573eedf78d5f62d1339be22b4133b18cdb62164422a946fa97003f1b725521eee5b6c60f1edea33da4e4f2cf4bfce4b83511457e5ec81412bcb8802f1bc2fe35f82750c4e4736f5911d4c574440c73fd2fc00bdf353798c98002af52de6f620c259e5a116e79cfffa00541771583056aa3c976eaa5a4d7545614505d7bcc35cda8250b2a165c30fdd2ef08faa7f381ce807db095ede804e0f0ed94ab1ad39bd5003f6de11d26f5d7e4bae01e6bab8b1d58ed420325c2414beb5d63ff64f2a594cb19d56b9ed9f664208885442c6eb5e7ef5874e3b9d0deb1e7ac110392204f9b1dc22d6b8ae83992893ab7dbe6906d7977f1906b1aad3f3d606db70e344b69bf241f9d3858d7423cb9bf3e2fda2d8d5e4ef70c8b6260dc3ac6ecebbe19cb4f2120983cfff56cbf1ec21dd1e1a7a0b7a9dc47e24e33e1c994fa0bc947689baeaaa0afd7337e12fd5cb703f1efeb0293a826ad2b3db157c6841535695b95f00293bad7f81de10eb0e6ebdbb33062fa8c3132adfab40601859131a7ca1de54f905d6b45e395fafa1ed2c4d3bea6b88fb3f506397389792d898c1bb47dc97c4036339608e1ebbfd76b745d989d2acd10244bd7d52fe0169a70b332f4c9131a34bb54210de226891224f01ffc77843ca6cd571f1ae73b8dc6c137ae7ef058164a00fd22e035e3dab27a711a094809cea435847601af055e3e4bdf2a3b14784f8137464e634802a890042cfa904222d937130f07f12695cf0d991aaec7e5b30704e2464f07defdceb36338e0b113351400f8b3b487cb7ef337d3d3de87dcf7bcd6bd952987ba22c373f400da3adf1b59b39c376a3b0f50b66e8f742aaf7d323d7d8464a439b94bfb724908289fc473bfe297784d4a043f23768d159e1ca72c31fe106f145966ce07c7c82be46ea55a83463c010db70a3397d238ceb2ccedef9b1810a0404441d7725002f0a2807a7521d7e7642f49faaede38b7d53ad27930d858196c25dec0112d999fbf799a4b8241d0fc049cf4a6fd2a1af826fb81c30fd6666500a28e442dcb93e6e765440f29c05fa7121eb635c8115d41b5e41f3f4a71fbc24b9f10c83f5391653067b20f58fae793e554851698d45ef3eb0a7fc45a7e03b660fe5beb355570df45060cae1665635d74d9bdb0b43fcc9b6ca0d933c27bc30add670f01c5086946bb5d7db1001d6605a22a0583fe99fe4939c3013bf2e76f0cc5d424fb63ce16b9db783f7a5e1625a6048275a77266be7a1ec2dc6eb41f4114255d77e2e0747cdfbda28942a2ec9e4cc2ba426c4ab60dd56793be5ce434b1937a37faca5ece715ae3fab7f78853b2b37519672ea825be4eec02553dda6bddcb23814ef91bd1e35b694588b310a70a32ccc896b0f875a4655aa8c17b667314fa1bdeeb3a984554151a0f990394eca6d03c444d4aaceff7f59b9552c3c28fc5bce83e25f9fd18a0557decc98ae4c1658d8f996e57391850eb21559053175753f71e8278068cab0873cd958d257dba2d4ef04620f050c258344529adba879a96bd5e09300737f5f104b3123c091c4242ebcfb5b463b4852e98aab1e6e717f02424043b13600c1b48816f7e33387e5de0aae4cdb99b9c136d45166d9500782fb35dc58efd5528be31489ae920af72bdf715848714a72acbaee2348d69621ae121deea958b9522e4f8f62f5c053af38819b718eb5461dc9811dba1333eaacc5469864db7eb8f12aba04fe52f96b71681be6ccb775e846b7d056fc47b90f21f809d8da9a3ea251bb56e511e8a31659c57b04c4fd3d6acd1cf4c382185a5c87a528393c1906001ca8e4dfa791a330bcbb62556412bf6188088d5bad3d62475605fc1cc99742b050522ce3603920a9609a537d750d4068071efa29aac42f55e1d526593f28cd09c48468428d02a6175a372cffbdff7ef4b63578c173d8d72bc44592e6739931c04c32089ae16bbacc0f5a7f7ee5ca45845a95cc848acff2314bf2ba3198b2c5b0e89fa8a87514c33e88d2c9705b2795e61fb7c31b949bc04f71a8a47cb95ddf98ca503be706dbe5c0f794692370edad0dd7c71dbc2a3872429ad0cff7f26a671e6e38910dc341c0f93cbfe05b6181a4469616737474b5946d90f673ad0d00ba0c820d554879ca55089477b54826dea5ceb0a932153eda384b5104b9f9af2ab1b3e7ea2ae8106d24f1e5a96720ddc47928aed969537985c21427d074a430fb403b16243d56511f3fd2fbfc1c522f8076de72d02e892b9f615e4864e5f421fec63fc70b2ac8a9d8600e4bf05405626d97b812fedbab0dd2fa955384d24656662541bd005805f78c2a4290cafa73efd2e062f1ebc9dd1cf4e737fc829901b20212221447cb2530125c979c64af15107b9a8813b89c4bff5b560a97817055899003337952f03d7cf6387f03cf34b065339938c5f29dc45d2456d5aae464f5d8db9a2204d1803ec5929f73659842b8476ab9df8226f9eba7635f9ba5f61a3ad391456434f179d6195567adbfe5de49f8df3c041c745b10025491f4ffee7b92841b491679018a04474aa0511c375e03760d1790b7ce0e8a80c5bf1b5394ff928f0d5d41d27dc8974690bab435860b6936d8d0d336eb5dd47510218c9bdc0a78ed5cd02b65a258741d5b57c7118366db4feb34294768dec493c54b0553d52632ad247f78fd521a04689360a86fbfbe53211be6830a4b6bca248301e9cb2d4ef0e0007857668dcfc2473f6adadf16ac0a1754b332f2ce66d35227ea3b5a1d0a312da7299dc3f4e23c452965d3af390c7bca517c0c3c64c9cb6cbd7f220f8cfe3d350563112f01b5ab0e0512ead5052e04dfd8d05c954bc03e972723b3af31bcd096dcf372612b7c53f7c9cc60055a0b4b96788a5471ec6ba6799efec167476cfe5f77efe09b74b1e8035a5c5109762b26fc400cf088683e8709c3e640c4a89557d181eeabf095d73c8663cf63ee855cb017f26c57d22f29ad30504a15b544549636c69b397d2efca6d2d224a273c06a543860e6a3bd3262a93f7a05fe6108bf844964b156a8af04939344c31a9eec40b2903358eb443ad9871565b253c7f463336cbb63986e743514a93e52344da2bbac2131a6ca0de3e04ca5c2b7c3457c0fe7e619d82942f8d6997b33287a51af251b52d5a7675679961bb5bd83fd97d6bccfd1fc74064a8fe28ca68562819aad17c3ca9b621d3cc1743b017ac8bae3e2793b0a7b8f1cfabfe32267c2a1bd7f3d330923e2ee67a09aa3087803bca29f285bf724970d0828705823e1ed96ae165462de5d387606e34dc79c5da04be66083c82378d2bd620e3a0214b335ef7215ddcaa690d0bedc35848a6d68953d8481cf919f4feee1916d2c074c6a153d848660639207973a1018560630e2a4fc38c1b63b9b043c86c1a20c13273fb450a061c3d33cba4b8109a929a394f9346b6faf25738fc384684d9e8519115f0f6b223e24164244841b894faad611c674b446b1aba283265f01fa0056158bb7fe6495ad671a780df9f0676c3a06f029adffeb8a86fe81169e623966fb88095803c97e1528d18b89819c7716499d051b8d89d9757e47b2e161b7071b9247ce39139382be39afaceddad118721677d83055ca55a6a8721d79490930b6ff2724f5533e5ede331417097f109537666e4ad0bd3a5c9ef6b49b48a4f34fdfe87d4f0a10214ce2d07702879dfc9ba6a4b622831978e7cf164a0a44a4b39a6b61ada882656268e6229844b22d6f88e7e0bc3c7b8e250c657a3d5eb2286cdfa4c95299c590e07d6f5564e5cf24df3be56c44bce82ae26e759f84b54f5ca7348cb97ac68d366d9f860161eb1baa15008002e3301422f947f09c1c4e4a6dfb7693586651a66172469103e4031936d6572725eb481c205122125ff110887feb0f9e005b956a122442d93bea341cd3dc8c080602e1b615dc8045e1294c06765fb1d3ab67cb2e3ee5f2e65bf40853432a4d64a41ad810a880caff40b5d45813f45deb8b03932ecdc09ea8286db4ee88c4dba89c70feac485ec6b0a16f8d538dff9bb32cb1c194f0c3a3be6862f2d5f6ba4943f9048675710a88d8d188f270e80dd68a2f8f8056ee0d82a3aeef6aa57725c0168face139a13e02ac3d6275c3a8949edd52e80d93b10927872461fbb7b1717484635030148ba80b419985a80ddf36bff2a2780ab6821a6dd83a2c5253f772f21418fa67e222971443f04c2b22cbe43aa82efe34f5a14a0d8772493542a913d9cc0693a0c30d8029b71a0ed287205251238d4ab3e9807f1f92f300ab110fd7abd72ba03da690ff2e0eec5fcaf8257ea77de842352894a7c7c4340d1414a26fd706f64c0111e2fc3a7ada6d2d5d6a9f3ce455839d09b292069fa0b7c73c37ac3f1e65a8986c38642e0f00da783ed0df59eb7fee2dfb011767f92683007edf33347c1837af7e94bbeefa3cf81df015b982b21265becf7046ef637c1128209685346d11470a3ea2e3268479cdba49b552d9e3a5978fc369b814f74ff697506c1f9f3cfd92e450ea9e129bae94789607e4c642bc4846aebea64a0198a2f252b00dea9c5382f0827fbe8e9c82024c883ff8a9306cba01f671288369241f3cc3b7b73dd267300863e779230d85d7e4ea613edaf424a0d580c9db58f4a7ace4ba0d82e02ece6dd1915bfe9e8ba39cc1400ce65620f336b40f3280c13b395492cd91096098706145d8b06b7cf5836eaff09234220e8f61485a85d5b5902e6443e211146c62a570fa34c29bfaf14b981b254974e0c43533200eb3d342086d1348e48046a1a2cc86e07dd3fb7a17421a1f8b58956c33cb041e175a6ec813ec3dfc0453d8f8082fb686263cc4013047c190472925fa7a1f0b30445543087594020741f296d7d50038990501271e3954a40a9cdf841fc7aaa84fd1c014f9e7e9fc5e0000ff61b9fb337497d7ce183517dfc70924f3fe9e7ad03103fb967f91ea54c2c0b4beea23d0b71fa20fd3b09e1022ec0e4fb345920ce5928017eddf16a7b5763cffc81445f24eb5f357c39a111384971f85081153414886695e2285913edb8adea217fb6093c92cbf0a97596b114da1a8f7fd29c94c71a32ba36f2f9d7834c5b834fa49b3ecd68ec1ba4b408ac8086c2aaf43142e70ac195e2f307dc7dbfce1efa47b61de9493c4fde43ab33ea086b04370a86b907fc09b3bbb45da028e1a83edfcb190a4cc6b81e18eb8f6d6bbbb2e9b187807a00cd6305bf626c59a8e0d86cce34a7a46be8539ef3af60a5713baa3d396807451584f6dd24c6088a8032c4ac5fb8e6d2db85bae79bb64508859e3624e52792d5011590b5517ee1c3b864b9815a8e1d566783e5b6406c687ae71198330d5fa1c350d2a242ebf998628a8c2acaa771edf83c5e58998caf4948bd06b752ffe0d54f868bdbc2356d10de56aeab98c756fbb511086c725d5b577ec659af960d79d9c325c4eb499d7d91d60d3413b5d60a9876333d863c1a9a6af844242df9dd1c21418b2a9be6f86f56bba8fd6914ff0bcfa5061f381226407b401e510a803cd42d381c141a01afb54e06234ed7ea0e107b4c6c82710ac5ff897409eef31cdea591fcc50199af3bae3e47464704c8bfe810916cda54e2e3029041e9781e4e959680430d4345dc5541e637cdff7a1d35fdc134cd39822d89f78b2216f58bea1efb49faa9506909cff0d633a12f2aa4bf98c3c5711bb08d0c81affbeb0251a189f84b5cc60d67537d0d2e130af96c8037e7597036be3914c6a138a7c53858cc4931ce0b715484e3628e15e5bc0887823914c5a13887853858c881228e8b715488f3624e153d52e80c272aff87634531e7c94467ae9c0d11aa9848db07889aa184fba64e4e59f2c481a67808260d078b06ef52bf5b43d24d3377842cf709058438a369b981e38aabac7467baa047fc721f7d918114191c4872c3f8a63943473c8a1f4e4fc2ca01afda14128a3df505bba6130400d3fa5dffe436459aae65c69451e96fc4af44e3e0abe6ba4853349748455f561ea2ceb49a69d5011704e6ba070706aac52de55007e64d9c1fafb6f19c49920e5f4ca851c0a292d2a25c9c968049371cf1aaa2419cbe4f742d6e729d58e5763a3df11cc313efa07c04c5a4e4b322b3176ca090c542ce797f9466e756b19eea2b4ae2c19aae0a4fe714528b450f1e46157b46b163bb58e4ca2854e48c00825c440d13cba403ae219b00723b4454e2224adb3f50afa4eb452017901548102eae101bdc10dc81359d588c7c6aa3374c7c53cd1be49b9c0206704470a667c89e0a6986c2874e7cfe3d2c153439a895db2a6f1d0d6099e21e21360d0bd96c4b0a5b83550a6d1c9967970bea42a2fa47f9f10b474600668308e7ac906c5835dd5eefe41797e304858a1a33caa0ebeda7800368cec02507a9ba346027aa9db2af9b413fa248b99c6a9bf429a83c0bd1e6b8ce1d5f58594da80139118bf24f9454f53b39126dfa3a74a8d063fa22016ece2de1d1315ca4d82116caab0043b8f00d74d09622029154c10c556d7a9bc3849715b8fa53a57139259e7490504d53af5032e64c74230bceeae163309e2f0bcf508f629231601f7c2983e4412d7c4b59705c0af43f6321fb261bf0b5b4475a0235dac2697d2052b157c6c297bd8b1fc7cbe090485cade0361d13452ecd18f3aedd64f4ac1a3a2c803d354d20abc8fb225d41f7347aaca1dc85415256b540b382cdddd08e298986a259ee56dce18c2792105e7a6f3a9689bc43c88c7e3853c39625652fd17f1705f07beceacb6807e51ceb2ab834d6189decbfda8018a9e9f66e9cc306d08d71df7db9f7c5a8f96847bd8b20b9cb705da0fd1ef2edfbb7d3012f486964d1d84e399c6f3e1ce2c369f71135ca7a75db6c62e3521e5f92228eb4b6e6c682e1bcd7f291bcdbbb614e2d1fb1db1db0981975c6a05623e035542afe0704fd014b5b0194d122fb2877c61422850dfdd40fdfc9a06e84a0764019c0cb26db00013ba169c0c9863b675db0eb98c6793163a640db70877a15cac8f6c3abb39228aa151daa9fd9823c6ec4a51bbb33fb0c6fb367da100d2f4ae0ac2674102105609551034a69034f298009f898c89296f7b9e0420415187def311a965920a479e1f7c3d80db9cfb3736ea0972e106e263352551401b2106e6cf1349bb9d632de87106f9076efb958d91badae7718d5ae5e92a0b1f171c7d1c06d2aa324d742b7f7567721aba62ea8e52bc6ce2948a598e9c195a93ae4a7898493d56cf4fed8a388245a1faa371441590b7f0e74304e72711160ec3d2ab486646be20e6c43bff08a8d1e3101ef8cd9387a33ad733306b50471fdd270976c45eba61842843e6ef9ba4044e1a28de07497975524d53f95035274d4958b8ebdb05785a25ee8606f6fb8888904c4a14f78380877bbc5ec815d41c3b4e145324ac399c8161d44835d1a5e4c938548225311290e54b3c874880bdb4cadb9c9612803b9ba7a692070e97023504ea4bf45101919e6729bff759c4af58bfd17093b6cc4a43d7ec251c6616e496141e93a3bc47633ad547609f7925697de50eab756d847cf47ab9e39f826bd1a374a69ab4ba4c9d4e8ba2ef3369aac7375a3a6f7279eb4822ab252a0317bc9a372c0dfe1f7579475da22f1e18926dbd209a6d22ca6497ab6a1b9acdeed17db5d98ec410f0d46a325833ae09528d1059b351af9c4e99f3c6fe0a28bd13949c0f65620152d28160d4854542dabc3c20ce9a3e1da27292c2796208e219af525e91d8d04b1a010e619c9411c8a48d309fd090f3c71c9909af74a9f0771ff9640cf1869a149654783285428261fc4fb9d8726279000120930744d39ef8291ab427c4d9f7c4efbbc99312e3ed1879646369933f111f83801e8a7ef1f078f23166660d93810ba43dc5cebd5ea3c90fcf4d2498880fcae35d9d5e27007968fe67c8c8943103373980f547d675ac5eca7da0be53002e5ce948f635056a139d30ba0cd01c75d4ecbde045a2dde8154d2eb1324371b91849c1c5a1ccc43a89b3b37261a8e96422f21809a7c63604025eb24d1a2dbafdcb0ca2eb2e8583f37bfb996f5aa85f0d29545403139f9569e98ca93a6d088d1760b6a8b013729cd6f9e8bf7e41283626c4d4ccff5ac0ec79ffeebc9e21b4fd444d33f79dec8041fbb73fca000c0df8bb7ef020756eb0d859ca399bbbf0d8eecaaecf88791e10cb98e2e8d1d17830f68a154fbca502fda34f7b8dc56c9de6ce1c70f386d457c40b741f736dfab9677f2321c27cba5d16888a9ce156a481662d871415dc9d109bbe6cdec09efd82ffa2be3bca4ce8cf0467e86012a2fb7004cd21b8e4d484b625a054176d2d03b57e7f426a57a8d148cf039fb41a014e837a542c149c1efa662a6678d7746255e1b43e7815ef942e88cb6ecdbc8fe9dd9e124744d6e87dbc613e61ae993ee0ffddf0d7dacba34ee821b3845bf0c54f15a5cd14f20c7956037b3b29ed59a0ef70803fe3e7a5251acf291632517e47903975d149d9d014cf61bf4ac1b24f8c2ca1d67065989ebc7ca5bc4f83b15c6eb53132858868707e2cfeaa4e9a9c870bbb103d047c3540fa73dca97352c5dd76cd1af9ae14f1aec8d855644e421fe159ab6fdfb48dd8a56c8d577a5a68d9451bc7f7202175b0cad2ecfa252284b9fc7b5d20ba50a62b3e3ffcbd62f5d0b4f08be6129da5ddd02f940ab7da0f51995b478bf4a68d326c6f516033f7fb3f9effc3915a9bfa744233be262c8c06b312a96cd016913c88dc13f9d69e09a56ded614b4d8c3fada88cd3c06a7a3fb1767c7d00f49b3a0cdb1a648df22f80848b56366e86569cf0ad05c663a845da23e500afc43e3434c890178075a2f9b1125ad1d82cc4fd40a071b4da561ff4b4b10049605009068aea55d4e5ed5239d5ee722ea5e99d943ac39d7f10f6e884c6aea37e15bb80f9c4d8296965a7390397fd90a7be6c8c09c030c469a4c6905ccf485635369959c040420bbb5c9cac40d027401bc0f042a0fd79e5a2092b29a0e1669870fa1f9131b2cdbc7e2fbc749ca3795058990cf755e3bf4a75fe6b5e17d35a61fbaa90a6d6006bb39183b01102b807a2c09d6915194b5898beac644eeeefe6b682fedc101e36c9090d6f6cab6e5de52ca2453020a8709040aa7ff71f37dfbf704fe9019c8a9fb1f3f75178efe3f3c28fb584323124ca6333c328b660c047d23df053c439638cc39270b9d23a75138895c947f61c06310d10e17fb6bb8e7119ee2d6b88136adeeeeee3ebbbbbb9bae5c7477751e2592cc03192f45abdce6eb4506937f652f6f61e4adc4d70a7b5e8d2f672c999114c6c401beb9af38a1c56ce2c415453811c51727b0d4f85205a594524a29ab05984a2453ca0ce690d5024c9b29b35c505d52960b585eb0fc49a7cfc97201964f5dce1964c4724133cd9e7353c19c93b35dddb62bea159488ee54ab132a4e73ce4b27a5f4631d55af5627547892884db60ea55fad4ea840d139bd7b22297dd1af1b37a7dd38ce8acd8a4967b76d4768e9a6b4d287c5023c5fde15963edc3704f5b6ed082d1fa5b408e92ffa2e7dea5fcf8382e97f16149f678756faa12a4dab45a54fea2b45a17e2c2a983e7da566ee5f612c7d3e19a2c0d459e9d35c580bc818f75ce8c361f2c7e6410cd8b32aafa5cfd67d24d0ba2f17777fff5cdcb44bc3a30d8521042d8c343a31b4c8d9144e866da8d68551173490c16b938b26b8232eb6f89210e34a1eb11403892cf34dc584d1a21e1152a680ce4850dec8bef1b7f8c3e00b700f1e2efec823fb69b66a5d18f00ca5b888b4b0fd085ea8bf85ae077023d89e0babb8ac6cabed15bd885b33a6872f702d8d29377564bd09b78a5459abbb8cbe002367600cc92149248d5a13f01bfa5d972144c0135ca47fe3460a1ebb690bf7f48d1cf1a725b4487f5c057191d217822191a5e460207d64967248ac659467c68acc200e1b5242c31cd0f41d5064c65a43e4d32e5729ffe2ec2e93dd41974bd0e5a106a47c971a9052035e7655f655760f4fd95d7a31f265a79e05816cd9c3ebb97fee28f7943b768f7157b9cbb873a1dcb6e0ce9d33d03c902e00818c232e1661d2d474a4086e2ae2b119a5232dc826692ca1f428c686dbaf51da2edb7e6207a56cc119ead1af3cde52fa6248e98b2979ec2d4c4d5d7349ad377770ea55f1bf78e8887c7d2805f94e8f28cb0cc1e0440d5b723bc1a486eca04b8606e81cfa3a1996ef06e687253e45297b98c4b3013ac7415519f9769e835e17f99e3c075554e47b3d075749e4d32743074f46a01a85ead139f4bf2cc176e28620f2dd3c07c5dc1f8fd501b65f80ce99e9c9f46db0f344a63fde64aa44a63f823c9d437f260c5ef158503a8f8bf45535f0f0fcf0f078ccc6d18f90135208c216ca6df55bf2f1eae485aac683253c28fa19504c441c0eba991734a62f50d48003ee024e8ab88841450d4b5021fb91c40b36b85294822586fa35762d6f7fa9c7ddef5fb5eefd6fd8189429d0056976f0f497369bcda2c04edf79d7dd8b73756fbff3890367ee0f08c9a97b6e296f1d38c482ede2146c2405dbafad898b447e129bb77795d0cfbde7e94cba1aca262eee3491422e0eb978a47e92fa2decdcb716c9e9bb97e0099c373cf3a63eae3b3b4c5e15af2c924d76726d228daa955b06cb088b1bc1404a89570ebb593e8ddc62694e1f3f3de4db99947b5bfaac0e93db07a4ea6c7751de8ecc8f3c1c942f03768bee1e52171b1004e5c885329c3051850a2c64ed37a777f00b476f4e07c71632fe091af1728ccbac02c51df503c261b057a96df1fbfe3e09a51f0ba8cffb4e3be88a8a3b5e5e308d8b14cbcd07af7dc9788811a0e88202df2a1521f71531cec85eee2b5d76984a1065cfae23f7153170d0a5062a9aa35ac270118394175a902445d7a04a0e5b58c0830f67445162346713ee26f795da0f5a04c1a35571c41526a60e596221c5114d6481831b907a90c92e5984d1c1164814c1250999fc96bfd0df17904a587e33c18124a05479c286232e31c846dc3904e96004175ef86003d2144c64fd8d815ab7fb444d8b252d984268b14c805d24d05c9fe6fa4133b062031296142031a1856c3ecf8cf9c0e10530b0818a920a6c90cdfff9e2fc8d0a1e3f53a5eef9ba71b63b7d422ea555fe7bcc5730ab59d0e08962c9d431e7ac72968066fb1e60f9f26945e29be7efd995ed6cb2c0a73f6d7ddbf324e8795055425d30b5983af812b28c0c444f91afed4ea8958bfd33fba594e279d3ee53062541efe61b233b97e7624b918e35db64812535c2936538919c643a7491204723ad641304168bb23c3ce5176e8f08d2c67574f7bcf238e79c93d6d47003b9b7393a1ca7102e8de7fa6ea066522d6c23a646b78ab2e69459ad6458aa1a3136702b3583baf1b93c9a1bc2094797c33e076e61ada1afa9c36da4085e77778b60a3e35513829f03470834ae1b332d11b5c2a26dc8d9cdd3acb9a232b5a52283569adc31ddddddd8e258672d27f343ea7362ce39674b4435fd2e0de803cfca6be1ad72d5d66a77e47e8e47eedf68be13caebeeeeee061973ce392db6cdc56caad68d5865bace913d96b686b0fcb696b396cb2a34b6b9c69cf3959bb66ea0e0e865d9366ab4502bc830d945422ba7f20c470a641d1de6fb3be8e2a9ce1ed29a3794d2097a764afbaf77879422d9e6d75a2b923a678882a210cb770f479991d46c24e987a19ca4a53cdd95d2b5b74f3ee79c74ce395759b09c734e6a2d47b71b638c1c078349e79421c2f2e9ec1c366911968f3debc9f480e5dbae9e77c5ff3ae54ed7b90e85677078351ee5c16373431c32d30a61466c89734a996384938d1aa18d116ad4e01419a2d4a4d434e409256ec56d32362b1c191c9bc36500f4cc9973db1a0094ea5049e9d5410ac05c7d4074c70a2b76bce039770460aa00201300954e1500ad127f4035e472c0320094bebc02c002487d4078f080e5a35662e03907e0cd7034b99452ca517a9de254ab85237c893738393b940031ef48536b11227e4144b079e9a8f980688ce35e350028c0c947f8fd808f2347081f101540cf175a889c2074a48949e80852ad47081f0f8d6be6468ff5f1e9b12203d3560fafb7cc83863bf9460fb07c03381929024c98bf3d4de59ee0035004745d0ef0afcac0d4e7677ab665835563f501d14f093c3f7b74723b60f932df8a0c5c5f7e8c2a532aa361e0fa1f50fd5664604a65db964249231b5cae5409f1a982c01e8ed4674c172cb538e54ef77b82ebdfeb1477f7aba15ad5a0a1f9d68035228823783b740460c3c3f3f2c843c7ab060c9fc7cdb27ba4c4819dc0cf41812a8e4af301cd989765077ef1f01b8fd9f70f8bf868c322f3060a71db685c3768eb03aa3331cbd34f8fbae716408695c84601a8c3bcc5fa5b8fb7f97874528f2da9523f8697d90777884704a03e7d1b1fd0b6440fda5d613d236ea38813a0c96380598325b352f9789f1298fe013ceab0a9fa8068c839c1f2e9d75a3f86568c3a0002eaea0bae151f55314330d7b012cd2a84801c432bcc343ca584fb5fb37addb3cd6daba0672bdd9edaef47e7d9e70fd07e3f36d98ee2b63052c070017f4c33076c9798e042abc225089f01172726e022349d28430a3c73058d82574cbc90c510416056ee2b59f4f0c5861464311303dbf04205185b0106563541578065ac30c114650201dbdc57988aa612f8cb7d85e986255804a18685195a6051c3a289c6cd60b1b5d8358513a256ed861839555eef43a5f0274445371a97442eda2bb0e7ef513f36962024d4981e1bcb4c18fc7a922b11b9be0c2246fe90eb8f5de7d44759dc0222e619ea00d39f4520918344ab3c4a2249b4034c5fe29881c1d414c60c87520faf5add451255a3ee91dd3992a84a49e4b18d56911c76c5617d050845b556cb6d34ff7225b5b7e4ba81f269fee5d5c7b54aa61a615cffeb677cbf83593e2c68871974910694211db4ccc0801554696a9a9da1644683cb030f9e1ed20c27b2bfbd620610663429f2ea465cebb0ee97dbf614cf9b793363fe35c438353d8892c0dd9d500a8447e7ec70de06d26af3165acb81a311cf35d2ecd861eeeefe2e4aa734f4a7d6fe58eb16aa50dfdafb8ac1f3ec6feff6b91fc3d62b06bc6deee5197e107746aa677577ffce91364ba794ca1b2c1955c6b76ff8008f74a4bf6d937683b26bfd6ddb727b0deedcba72e17452b9a90eafb7bb9d78d670fa9c435c191cea9792927ae1ececbe75bc72c7d921ce862cb06abe3a6c3c3bf315c1b360cb6e4491026d4a5bcdd327ad2dbcd912d88db3dde97a1f2a85635432abda37f5eb409e24d8ccfd65d5b0d19ab9e1a20901478e07c39a970e1b116e44982e5b145134a49d53bf8a60b690e7cbff8d085f625f89d7df9ab1d3775076982842be9c4dbe56c72bff6c40cb5a37d4e47bc37c3d30dfeff345e5c83785235f1c42be3134f9aa5cf9cadcc87735932fab956f0d1bf9daa8916fcba3202b5b50c991d94ca64231b3caf7864cbe2e55be3431f98680f3c591ca37c77ff9825ebee1cdb7e694efabcb5787cdd786cb57842d5fb1e67b43f31d61e68be3f9eef03ab0651494f98a39228ace1f415bdd9c8be1b6bac7398b57adeef9beed4d0072ff4ccf566bc4050002b63aa78e3318c873d562d7dd2a7c6dda298ab41d9e966d51630bd922c19d332d278a1e6b19edf4084264496b8b48cb52834ab72347d3b21c2dbab305a63a67ba28dde13195929013ccf882a67774b7962d54e2d7d3609aa472abde0f8aea8c06590efda08b1f509181cdd70b0dfdbf8e3c76c2f41396ccc041b05c81b9da1734e491df4c6ba9482a92ca0706cb666581e7778c85d9c74211161318252944a590c39848211666ece5f3e5b5c7ef37f3fb3b673e2b077894429f135ca56fe6bfc0ca20cf9994e78fad97c78acc1d920a0b3356a469260c0631d039f325158f5569258bc36a9e7de50c2cd3073f482a7972331f60c9a3bce20326524b9eb34b9e5fa9ecee22cf9f34e4e9248fde94603b0652dde33f57576e0f65eacf211561e7b496b353dc799bc591769f931fa7f9716b6d4b395255ee3bab4d440e2b721812d427b1d9c861f453b469a6a06315cbad92e97d41a6e34522d33f81d58bc7766864f2867e4523537b9b56b150e15388e523028f7776ffe4b0cdfb0fbcb3dfbe75061eefec87fcce767693b643236d85b699c396386ceb4ed7fb50a94f080af461c12040ad43dfab81821901dac703b600edd343f2740f37fa646a7fe478748f0fef217d48681dfa148a4c7f3c40a6de4c1d75cf4c0a199e8e3a87be38c5369bb4cea1aba6555ae7505aeb1c0abb61bb487dfcb8486d8ec042ddb3cd96e4fc9ec24fe774deb8cc6dd982357720cd270bce7c3f219ebd4f4827efa943c59654e17e490c2f136555ead3a7919cb5f6467171d63d4a863cb6cdee128709398cc975a17d76ae51a697e8da20c31f219d439f0383803e5ca43af4b7d98cf50ef7dd4ea634dbdfc2f0464122f10c62431ff632c9f4af50a61fc4457a8732fd219d435f7e40ae0f17696d1a1009e4ba903b87da3b0b7f5857d04f40f70cf118f6dc3eb275e4d3f7e130fbf48308f9e99eeeff3d954d689ffaf4a9fcaf752476d5e873930b256d865209289853cd4ea7d3b66ddeb5e5615bbbda6a247d6afda17b6c4dfab80bdeab091e25ed49c36aada145fade7b8dcef9a7d96412758f7ddad43d34e945c3e69316e97fe1b7523fe20fc71d3bb24d61ff39a342e31cb292267d6ce835a944f7ccd0c9e81c0ac473eda4c035e943c6a59ed766acbbc82813dcd2096b834cadc85426012425cd05b84952ba75387e20a3d039f46793293487261317e977e05c32672e52afcd9b1a8a327d3a9fd4866c38893a873e102ac013efcc585c301752a39d19fb9e625a8d664cd2e60d7d8a6d38628e69ded0a7292bd8bfe665ceb9855e733032c0a3d732fd49df6b1ea334afcdd914f29a83899959afbbd65a6bed1e8dac2648e70970c863d53ff2c85695036c82dbff91815d16b796c0fd9220b81d0d70ffa5d15a72a42e229b9d3792035db2bf18d78fbe42c312d7035ae67ac0e9bbefc2116c635f99c13f7fa9c20c5d24a8b2bf2afb2751c1037d858625599527d8576640221f51e5fe13e87a80777aef0123e0fe3be13e17ba8870dd735b8874c2c90bfd68e3ee8fbb927c5f92ef34c7529d9a71fb355aa3ec7f2f9eddd0d5a3cea94454da0df588ec2fb34d025330f3c61f8ca560e6ac0ad5a119f3889eb40f8743eb383704a5639c0e7de3bfb383835353e372c9fce8508047ee6b2d73b419eb03f4bc1939a4ecdf7142274ceb1e54f69013626981478ec6c2028f558aec53c908f5db51f6af65748c13aa758f1ca37d94ba4ad13df5bdca9ca243ab06bc29c91e0a9d91bdd5d45d183a143c4e97f5078405061ea752769f634dfacc502a491d32ba4786f2069dc305e1421bd2e484664c4ed137fedea2e5c06a817b909d13ca5ec614ae0a035ce6853e03a552e7d8a075fc3ba319d2d2b6843463a2f8df6a2d711b12fe0d691a1de5f1338bcbf860b2cb5a93f0149a3317fdbb60af088f76559b3f33e6bd8b6f3175064c9917be008fd687ec60b2fc94152cdf1a61234e2f739db32c5f4a774a29a5748e465635dc99f33fcf3ce71022f0e85f7c41e4be17ba8878a80f92852e22f253a063993792d6968677b895011e77c61da0cbdffb20d407c96a44ee7b610bdfdf9f2011ef51610b37f8c26b334618f5813f037c29d0082f609c5db399a062961d0d170d108327d96b5e1c0cf8859705ff90137e82581ae09d490b1264669a4f7fb2bf7ab970faeef4364fcee75770ce8e51d9ece427a15a93f52c66c145d9751cf733b953c8828bb295cab0520695e798788c0256d0c489294d32178e3b3b79b447799c445ea644f16ac07d850c2f799c44998c89659c4259be1ce750962fa514b42c7f16cd2859fe14f258ff4b078c2409cefe83ce91ef5f5a47be972e59cebc6c912b20ed62d7016d9be4381fe105dc7dbb68ed07a45becfed2ee3f20fd2ac2361cfddd5970f1f4a7772f0ef350cee690c34e217631e923037b6f97c097e3565df86fcf811b9060f29d23098452d9bd8c81e5f75269def8f5efb08061c0947da1c52d0e7c0fe4012671b16b5f8ef2d860b2945fa6642927d1e5c051660e4cf60ec8dc8548023bf288038e3646d9479b59eec8c0234b4989cbf15ccdd7e4005d44727ccdd7e4a8a9c901d68035df1f90f9e5cbb6e5000493284261e01c3f62224c7487728037c0cc9b3b64a3e395e3549b311b1da7daeb54cb71aa7da1cf529ab1568b8586c3e8ff2bb1d070d1cdc0544eefd2e4a28d8ed7cf1c363a5e3964eee2a287bfe4a26f9140f8357f3f09808f237411c1d15e944086e3836435a08b48cde7f81c610bf287a62c8864351f24bba71a9e3fd68c33a443b39102539ca11bac1a0eb0653560be61d48d817f69c65e5ff3a3cdf14bbff40a5935bff1afb139c2f61b560d47c86a26dd23b3ffe907b8f3c8aad22b48bf26046727596b76920d657f96147864d5f2833c7e43f30c775cf496e1005b1602d8328f06f8f3c8aa65af31f3af21e4cfaab1c0ec9c825c0ed4446b0825b7c606272fa0758145103d80528b01931631180306318c517df09dc0b71a3cd97c5e3c3b4459647f2b933b89a806ad13b47620c311a00a8e238014d60a8e005eee242225b664995c0e51e8c00a306870610a2d50083305144b54968ccc60c10f9f122c18c204494431e092887ac8a7dc49444343780fd846ee2b5c1c710105072e9448d9420c69832daed859e256da728e41748a7b57cb7594db4e73ab2450eedbe4b422044ee54e2272e135c3d85a4609a060d162080a181ea8280114a2c79eb516b7c0dc57c210c20b914709191e525661833cd2c0f2f8da61872e793a89419e3c3011615c53a69ea43b3b1da54b4db20880f71660fc80ba52a9090e79fec9f35a0093021d68f872c511404823490b19484bd400c34c5604c6d195b4d846df36cc7a493a64c9d35ff8a109c56d528728629e5fade71991c6174a4ce1658c2ace40400d8cba6c41ab52431599bfe8125b28d99fc757d9dddd678705de4165ee737a21d3fc45675a2c8f832b37105521c6d012383cc1418cec4939b820ef3c81473ad2b4153179ec51432dfb12494c5c70830d5e647fb1042cb841862a53575c9f6520337b379dd24a96ef56dae5448d4150e0510a49a1d93fda4dbe5bfc5cd88af5e704f3b4c4ee9959d272b4ab63e26cf14a99f651ac423211a5153f026346539119934c849afa0a2c3db766bb952379442342094b9fe046ac2cb0146adbadce91f3a8d229eeec9022930e3df48866e81bf9724af6500ae54012d137f28fb04964279878acf5c9a3ce99b1564bcad03972ca238fb990b7c82e594a59434af9b35d862c25cddbee91010246609ac4228f24d2ac066d7b69bd2878e4b27f8f5266f946acf7168e80f6b6d16ac4cb93e376785eec1ef9605c9afc6267fa48e35ce6a4b9d1aae1f65a9b72ab7337c1edb93cafb5cc7c0c499a6149f6eed2a3b1b496993cf69247a7c7850457fdcfdbe30d0a1e77f0bcd1fa82470fd504cb12ece3aeebbc7eef81f47b1f942938d2162e78faa0532800a9c33db599be942f7ce1560cb61d3f41767866d74f51dc7ed5efbf37d481d29cb0b1995c257cf7f563a0f590ebdf931455dfe021344b710d51853f6fea8f0003bed9993775270f99ffe31854fd758fe9eca9b3b6d3c1cc9e3a1966a0eafb034a81312a10bf12d8be14a197c9fcfd4b4f9e3d79dee9099a674f351cd5d3693b79f6e479a727a098a1a18b674f637fc125d7bfdce9747a20329f3e134eeffa4133eabd7094d97b99bf7a4412b94ebf93bdf7aae02f6c261452f0c021415630ab7fa43e0bf5bbfb23a27aacfafe96c8410f4bb2989722f346640d683534d1130d693b2a0268888bf54fa7571d814fff334fa0e79d1eec8cffc79708bd0c7b61cec85ec8c205a5e0f0c8bca95df3ee7b374452f3bd611864fb31f3293cc2031e9b0985143c362991d4c7842d3818216ab2d4e3ef1d28c970e431775a3113c242e51542441ed3bdcbca281d885d8c750e6a2cd4974baa54c55c56df9eba20f585d41f220353ae4fa4feddbaaeeb4e2af06e4344c163d7b2a3ea03e803269198c7610b1e12b298971f11d5a7c2160c50c483914ca502531f03621a747a1f0e3bfd4cae269f96038826a0a0916b7237018586ec34b3725d6012ba0f55e8504972f772a3639723c1d1fe26c1d1eb4690a5089074e7c8976397edcba7d9c3d1cb5d7778e5c730cb38b99360d092ef8cf9f9a5baa7d2c7fbee37e983bffb2e10987e09aabfbf721173c1f2a9f717ec2c1fff0a338ceaa21e0587390f8e668e2451a1eef57c1f88e73b9bcd76e8fdce1ee8427d7b8ca7c29e4ae57971a4c21e0c42d99f87e7799e1cc5ec2e1e719867e7624694fd59b85429fbf39852be5e2ebaf7de5b87c5bcf79ec354ef85238d07becec0de8f34f9c39709a91f28140a15feb8e8df02f07b31322ed6ea93c880d845172bfce19915eb2d8684a090827f11234758f0d894f9b35632e28e122fd99f07cf92a1ecdfc3656442171099653c55e8427dab76c01d28505459cf7e40312f3f12f0ab5e0674a15eba50dfd98bf1a1025da820e18f8bf633618519e6fbc6e08e1d17c3ac703dfcd16201a8f072ff92132504a61f683ba77b0fec3aa77b0952a9d3fd0537a9d3d9af019e36af098f4da33f34fe2f17a5496809080813264ce638570dc53cc11d32c0434ef809b2f21a49dc7ecdd89451bae3453db6e34ab1aa34918af8785ca4b4698f067928f1f28be6e537f377288e25f8c7771c267db83863413cfc8162d5034cc7d7df00f14229f5ebd7004c63b96ee1df31d24ec142dd8380ec452c0d478c672b4fa42226a5530a2121d1041762e46e820b30b9cb5b1e3defaa004fa5fb2ea9a4d47441c9f47d15229ebc44ee7fd753ba378906a42c80dc49342ca9d5db3ea0130de92b0268684aeebd7e25d81fb73722ebd71566983097aac0fd49be44a2512997a4d2d33f6981eb266916ee24b3b878a5032516eea53cdaa6b8e858a494ced9a4cf283b15a8d491445cf4211b28f3690c3cb69292142bf933a1869bd4417251ca50d2945c4472d125924f6b1993e4517efddb1f90514a69c4f380bc3794579c0b3d4ba429699c077ce8828a29d278d2032c64feb2491eb9e82d955cc4f3955ca6072c54694869c88552ee4bb9e109577a6d2fb52e6858115a927d0e252941729f3efdb77a25e9855af6242547524a8963c90ff048b3cba58d8935c3adddeebe49f711c4c52aa50f1779a60f1e58005aac5f3bcb4ee7d41d4678e4e1f1af5e7f05a67a48a97e8a83000345971698608623a4908d387b18337c1003252960e85286ccfdc91854589b3caef284e539935c9821cfef29f26cad56414a909098224c1542c84045900c688083232f6088428829b229676bce4994a7184879ce39e78c89028fafd78e2d55a481945d47764f4307d9c31c917d3694dd5b45e1ad6870e4bec2451350ec00f7cbbfdf0ef85df47e758fa42121a594a98bdd9d43bfbbbbbb694fd0768ef47e1a8fa594be4710262b40830947a3c83f7777ffd9d27398ffcabda919b76db750f577a84176e8f6939fbe76ee5abb3616e930f9b6c8288ac3bc3ff2a4cced34457e537ff4505b97af7fe08c0c054a267e530930ca26b3be4ba2eda7d4fa2258358c8515cca38d7ef2b7592d52e5ec45d030a9a4c5aaa461ddb30ab6ec9326e89c1a255759d40120f5768081545cb00c533826a59a73cee92a99182c23232323239f26cb7015c6ed086c290f942dbfa1ab32b0a4db733fb9962e86ef331851c97d650c2bd92677920f48e49bda7e024c49e8e3d504ba482db5dde94f70051264ff215dc44599c30bfca92b58cad0a5908ba2d852c252a86513228769c9144422733f53bed2a3b418bec8136c9a0c58727fb2ee80e67a29dddeceadd65aebb60209469a6c6717f69f402246e0cc852d247191fe90131cf6029648c8590d8174ee9ebabaef8c5da45d6749248bac78ac65425cacdb06244828c4ae90caf6691c46f323add40374ebd0bab8d1b4d21014889c3081148a849ecd429278e67e7b1f65e0ce3e3c46654180369ac473ad78d5a2e192c85cffb32ed688013f7d893b78d82db42bc0a395b4d30c22d05208dc4927d892f1bf7f40b7e96c5a8e09479af1db1f7e662c8595ecc2fad1c82aabbe3f8555385aeef153a098e7fec72d0af0d8349997d907c7fdbde0e713f899f5a3c53f62d57faf7aff805496158e16e370c4f98b09c756febe3bf6d337dc7ff7bfc8f2537f5b0268e4d57b961320b17ddb49f503f5aa14eb391c8e46325681e32aab5e7e4056906132cb7ffb16b00abdb30a832aa84223ab9cfa2f06ec9c7a16a8ea9c027fa0deff03398ee3b8d4cb802af8a7de03477faf3f13fc532f637fc0282370cb385538934e4025b3feb584b957c19ff5a3ffeae7ab9efb152bc4e0676b6362629e7e40312af033f72d607b950a447956f5ad025f4c58f553e595e09ffaf9511709f8f6b70ac7a0bc7a1538caac7a0c8eacf047778eeabb73621effcf8c01bd5f81322a9004d5a35e158e4656f97bfb9d7f660a6cf00476e006567934928dacb2ece752f2fbe1c6d856e7d0ced6ea5bba18230833c0d0ea393843c8729c0482e90c1870d1a176a08b883580114b18c96c68595c3a1068fb7bbb48dea411700ea1f485ac85ee4920a794014656657e90cc821d4877401bba6c0776a18b48d73fd3dace5c338dadc9e282eb8ff6b7d0482b4f09663b37ff986ebf466c84ad749f73ce20597f403a0e0c9251a3548ac5c245b80817cd29f3e8c934cab2b692130d711cec3bcd25ba447de33fca196a7222b18dd34a960ee8d836345b094ac7b6a15a93464e6b2a1b36308b353313d36a6d2e1791211b0fd97f9443db50dff86f3a748c1b2515d92889a45194eea9590a512d952e6fa96e71d163ba6039243cc155862c482667c0021d886436dc88e68d7f906c1b9a3741320e0c927553b024f29a8b3e5d736e431eeb6a1b51148775f6df8e3624b6a2ecbe59c9be21d1362cd9a9bc440e1b6badc8e9b792c3c656a24388780da3a3239165a337c9f268369bcd64a317652a47b7227d5c882cdf87ba2726cb6fda317082321f909c447969de30e1f0b08b88150191cc8676dedcf04edff6f754b75a7dfcd45ae775215ec32058e6df60065a08dee9937f4f92c7ce4e0f1e9e954c0eafeb5eff3b44514655e359d0752022099a8c0343a822f328948a94643b3668324500545869927160cbae0ecffbb6bfde26e746d36ad56089e0dd4ffebd7569c66c58fd6fede230b90d9d68dd33e4525ca24bb4a5b0b41251ede2a25777eab52ec990da2849500419e0711bda7c6e431b158107119c3cc123cd336c0cdec8bef150635096406041a526e340d71432a31e4c2e75d8a43c627827c8175af6f7b12361588afe8eea50b951d9537de328540b70101751d9c70d580533098acf6ff9d4ba30e59c7352ff5182a067297fe5208d09327b08be80e56f3fd2d06f39e79cfe94524a151044d35cfaf89b00896cdff39dff63b339f2fc3184dcad1ab951b9c78d762f20e097f4d97ebe756765cf913bc79f0365ebf85370b68e8723d7ad63bf5b67f4afaf823fb5e10e17a787481e0079850e9346dede412ef707640c33b7d9cd8612f4ed9b96b7b03ba7faf65e7db928c39d5434cbf447eec64d18cc8563d046430c05db3c6b4cca362d8c8668910f8adf9642a4bcf9f2fb65519659e6241fa8e46a420d2d0dbb2501dc493e30c99d3b5fe9ed289758809039e0546e2c615cf9a33cd2e4242846342d59f41cc0526b9247afb1d4867896e0913678432ac19d94c46482492a109de4da8871058c51ee57cf394315e89fb20add33ee04f58fb49b862309e8f78fd3af60093a552411666c214b23dd259320d74e7a822527919953c148d76028d370032f7fb4a7fcfd2da06b30943bdcd9e4ec26960062e6d986d19ff3b7bf144c7ab284080d22db73df1f03b6e7282ee2c73f88ff8e3f8fff9c4e67e03c2f7a33b6b276c6e4b7226287e7456772fb353917c76d398c28a534d3a4274899524ab58ccdd4e50b6eab222d4292c920c82078cbee19b83f22b4084936df415a8424eb8f888b31439599ccf18ccd198451761c4f543652f303b281c3514e507584270b625ee69870b40056bd7f24f416b9bbe00f7ad862c6aa49c1a3cb484acdbb8c66ac267497116d56ed86d18d5af7c4cc7ceb97a60e219c21000c758e1b5d571487c9bcbf6b4af7c4787667a8735c87d6397e9467c09112d162d54250cba3ea8dd4a19494101ef489a205348f8b542845f5ac9ac3464c14028863718654500122207440c51344948005773301cb0b18161a355ff352fe53ffb782f43128f36d026ba6e01acd37a6740f4ff6bf189c790bd30f09d0253c89063d28a51c86552c1d3236b3d6f78cd9ccfeaa409d8c6f2a959a01719ae010a57086c054d6519bb1cfe237fe595cf49f015fe906f85a6c667349cac6666623346336ef6f43e4308a33b4b47a0ee748479767ea9ea52d0e4be1942afc2cafa5737474d1d16483831c780001072c3356f336886c5e3cc9f203195c9052384d885221ced04fd9028f9f45caeb3fcb2b394cf5feafa57b5231dfed8333933afe317f55e06f79269c59377a06578e71cafb1a8e532a9cfa2933a623acc1d8e6a74cc9d6626c06de7e64d5b691e2ceab11038e5d1e3fcb885334e21c657fd668f37c0c7adec262527291a5c5c5d4745697eccfdae2a2bb8e12760a96614461628a0c0e11ce945963e3c80a0e920e96ee69a97ebc19e749fba87a3c80718e689f1493aca3f6f706e8fa1990e65ba04eb651f32ad00336bbfecac8c8d080364b401b2110956d66a9acc211c299b908ae1e83ac9f608d1910bf0c383f05229979995781a96f8139b906a4b216ce921970e5038c2314e2cc585ce01167c8652403dad8d03afe36409b590dd06689cd0ab461d239a8d7515b621dcd988ef76721b1bc66d451cb6e63c4bd008fac283844dd3384d3c461f8fd53aa97f9db9da6e751effb84608ffb14f0ac28ac299e4ad57c4ac7a75e9f4a81384b7084409c195ac773bc94149bc799cd984df84bf3c6ff975aa98712d064393e48860344e389d7e30ce9d0ba67e8fec966d6231c758e3885cdac66a97370660ec3598223d43df8fd637cba8ea5ce711d429d63a3844797d12fb984e68dbf8e17e89a81ae21175d4636378c3831186f1e716636b39112513406a45ca8246d7655512d11d50800000000e314000028100c078422a140249ce9b2ac0714800d839c40724c170a84490ec3280a4206196308008400008c3184c086b602722f905f137be5a7470e736967ea9711edda4f1ff7498eb1ae9a57449fc9cfbe3886807955925537a29cb78eb8fe54914ab60b88752af6955eb0dd38e6709abcbb9ecd67e24015b2e91bf1c6a1476d60444d8e971a1b9e6ac03911fbe549b3fe9ddbe3394b23a44a32e9336e4ca5af622193885f578e5879b4cc87882dfa9faa00e889c1a627fcb878deb74bdcf884acf769681f9c300b4c99345871bd8ed270d38fb3846eee4b12dcf2b8c3ee6ae0502c3e58dc79e79a5c7e24d0c4630e8266b2509f6f2113cd082f46dd7324c1fa0ea3f9302cacfcc6f0af2e6f9f45080e1035b5480d74986014d0bb8453be0a2ddb9949b1d37d4af98525af429313472b326a03d0a0266a0589c50892b435d2121f1f1ec8866c9d0fd5be8c14e928569dcf06be18db13ace3a1c0960e4bfbb8589956d2363d920dc99e796e0b87826985c2d4fe3b94d30db07ed52f9169215b7924b670ac576c357a511e11f97044640edd64af70089a748243f3753122a6722b4fbe33ae72f41e2cb7fc2791ecef5dd09abd80dae3a458a697558c4c9ce36d2cfc301d2522041e93ebdb76f629cb9dfc6e13891e84b3353250cb97fde514104c1ad7b86ad56c59ad85efd4724527487cf16621cd0ce9145cd0826f4554e0732a96b69f2ad0fb4ffa1048e946b4aa4096702b5d28e6e57334cca72c2f22bb4330b9250c10373597b16ba42026ccba9f248e3c66ef01c47c01b384728249d6d26761ccbf2b6c52c4d37bce62a5384ef8b1e3da751288805b3e2fbf4de3e892450853a0806d1e386eef69d39f001c795b029cef5bc29bc6e3141b683a9fb658502ad6f5827e8f4e9cb447a30b0418e3ee4763fa37f8d9813296a6d1a61fa1ba19cacf00b86825dc550ddad46b7907525a96dc7fba1c6dc4e2fa1d49945575498e447e71db73ade13b3c30ee562f3f7843df1adb82c741c80bce996ac33d3e2d21e5cbbacde446ebbfa23faa7820840adf6c097f842bbb3251d7475de715542c1cd96d980be1cf25985b8b2e9802468be58de71b624239a3a4a3ccdc1533e589b6175e0031c054fe61166cc92baffd15d57cd9432bbc51449090ba5b04001ce4f20b833de56042224e0e145be7daf84b46c6d1506f522a0e90dacbfd0927a76fb8a4e9b1dccb82379486bb0605f744c9aee69947bc9554d288e2cb38fee432ea106e399538c9aa0584ca5a3bc953c972b019b9525c72e79a6d4588d5cfbf33fda19e03006ebd1ca0e8a37de01966a0f2156c78fe09934215c1459d88f7667cd37f6d9d30164b78d89f2269a87810cc979fb19aa859dfed2eaa092d885446c78aa8b31b9293918c90888a3fd84738c8b535259ae375e79405e70aa1287aa2ec19b55abf696ed512299b3b1ee8973c069684df9141952a129264989dc54df7cef54f4cda4894e6a14ff817bb5702729e248a89fcd66fac019a66fc8a159536c17d5051eecb0fcd9b96d03fe438c182d008e6af5d78219f628e0e3f9ee9c72f56a5658d0d8b48ca8b084b31143059b9486b9033704b291204d9072679c783482242e44aab0b118d8c2a3d6f03197886fd429de824161a30e7c0d29ba8e986a89634625cb7752002ae45dc52ef80adc5276f3f241391ec9506d4ae6a30b7cc2a6a2ac633d69b8a09074a178b301fb06bcbaa993f9e9c7e1d057b192d4e157e76da3cdb744b56490dc6eb4f4adb5efb2c12cb1eee705e70e131855bf461b67ca8f4b80dd03857abc75a20a8437c805810e0809ac575717501324b23edd4c50950622069605a22813816d9e9a81bc65109b395e44024883e5ddd3c844159204396dde181ba210110d24b6c6ec9d6f974d6537e6fd4081995dcaaa044c334f5ea78107de9f8145f89e4ab7f63c413fed1a2df7c239af964448d72500c501000aa1e78e3c4884c96d1abf14ec8257f9f9c06078c487d9996b9feaef631426801565bd423ab942ad2ac5efb35aa4f117c59e675aca388dfc83775246c0645ccd164a909897d9fb80338fce430ee19d9313bb21360b9d49e3c454d50780801480f014e3f09191211c5a2a06d811d0911d4a842f9ebbb2061c387825005461dc844d3f482b6d4d03409dcc5d5f4a35d8a4c6399d2a74ca6c5b58ea00a7b867651f16bdf66e6f08f10991a910ec3c6298c3ee0e466fb8bb36a6b2a92561ae819268977e1b7276eb1a96b87eb27f15ae87f3396676482ec9b1b2dea91b2b307f562c46f5c15e8a8260d9474c6263e0f297d8a7d2089ddde27d640bc69958de74efab8e14a6c2fcb694f30c62d7348019e5b9cb4c935ac0b4c02b6dd53a91f7cab87b562306b8fbef173b6434d3f8830baf78727c54eb4b10918992a2b1157835431902a9d4e9f75b60a6f3cae252eee8c66d7c256a839dbf045b91b491ccc3ac91b97b78b8da9941ba5b89ea729d9f7e4ca31df6dd25798cec1259dc4633aced19b3757b195010210db8b985978a68512e83c20fe9580cfeb6ae8a04c243ec404a2234af1ea46215d22dbce5a55fcb1ec636dbbbc8118d24d9127f1cf4b5967160d11c2946a19455ab38452f5edf182794040b21854105ace0e4223b3950ced401f11f90a9f0e3226dfb71947a02d7672eb4bd0c6e2f6bf68bae1b1913309056e81b01fbc55dec02692977b0e7c9fa9d522142cb88289270b7ad74aad7b890e28e17db6a434f24120f83f299cad2115e62e7777ef652c3caaee4506acdbe8d412b09c617b06ead72840de019b83d44d89ea5ede38583cda42831c393ce99b8e739e03a2ee05050be466f49fcc3a08f9f058913a03d016dd0b83d8332eb70300d8902aba176419af32e7fdf40337c6320f8c7be328ca2dad08b2b888e6ebc0e6da9e58eff4036f6e29a1ae93b269d03f5152a40f7b3631b8937d49faa31a05266d55772726382a692e7d4e100a7ca238736f688fb752d6d6ce7dae6dee118dd5f35ff824c1be089e799c5f4344ffcd96902df5680a2b2662915378c39660355c44d5df24e8e44f7176fff91b71d4deb3912ec939491cf05b768ef979bdcbb58db7e1636f7be8c931f67ee9861aa53e34c2e1fd99542ff791fa73b31fdcfed08d1ba8c4b21b760ab86f4e1e96fee29cc95b8a61a684a3c02b1b0c3b044c3a9af8f0382562fc73c4ab1008f889fc98df2b4efc23ceefdf32010e7186d2278ae257c3bcab35b86bbbb0d885ebd64c4cc80d379ac178553a1e35cbfd17ba5baa9aba83ef5a7681c1edf2e269cd775795c0d3145bfb70476c42812ac6d34fbd7610f0eabc921d2b3bc65e5bc3043abda685a18751010edf394ff0a02953db571599101a63cd09391f0d40af4c385e2741e0ea9d19062beaa96bac6d18f49e2bd1edda16766e6cc173f884699f682b72d0995098d0ed9e628dbe147eae9b99e6d20e41cf122557522d5bbf61be49b0cbb2ca50cd6b9c8bd46892c14e082b4eb3ac18550704a7fe94511281dd62e9cf73b9e523a0c9fec6083927bf5f35d570207934079894113cfde37703f702ed24dffc845015eed19e01d3547ce3e970e7539536d81f304d70bfc49eb52a7ef068aef7287f018c9c37afdf4ff9fbaf69544d574ba9cd80f8a0f152ce75d114fbe111b41df44c44db6c335eee0f37b956b67ec0cb5953a45720624237bd5e3392273b4350d0230b94fe96682c3016093895dd8ebfff25381d1722c02cbda80f10f93b00008ddbbfa90dd6d9989bd07ef52d8977d424062a41998bbe4172ad440ed3c8dde77d746f442d78d97d4891684b6e8672511d843e8cda0c6dbc659d123ed53cbc2f4ac375c107cd0311b4cb39adca322db1a89f42c296b7970638a3cd88e0db425b270baf645b2c5fe02198643bb371eba8e5c761331e6c1bb92eb76f312e406b41a47f187c694e0af7fc2acbc23d737bafecaa4a4c365c755b78dc62157a23bdd99b3d333dff7c6fa402efa8d2d03bc09ee9be0a1a9ab0c0826ab449b7bb80e8834fc25f8a7e95287efa2fc9c92e8a7e557caede99451e7ba07e8006396e4a950795f29e395903a7736dfe10e9029de557e933e5740818b860437f13d3ee4f8ddae8c712a65a934cc9bbf004b8a695e1a31d2269f0a9cfcbb63f6f840e470bcc02738864eff62ba58ebca13bfe1042370734f4c9ceb9bc0533bbd12c863cc9aeae7fdc1a7be5bb6635657eea14b29f4c6768baaf9a19dcea6f0f703ebff97e6b3935426e404fff442e4a5592d76dd09848953da1f6d6335fa7587ce91e75b67f8a2ed39092b1e5a763e701ec82ecdb3d00f0c30322f069cb6b07401943aec782b47ac9eaee009f76ace58020e245c0d26e413268a823354b6f40fe3e433c09138446747980cca80271baa726076aa80b67b0efef9fd6fdd472372eec55b8516b569162196378dc9d3655ae1855224b0061e648f7369a31a6179cc7344aa17e913517d1ad49cebe93295b7e9520549c7896ec098dd535ef4c5ee36305ebaf1b964f8b66d8321211964eb46ab6d271077b19562625acb1a6efc17a44f76f5356d68abe56fa8d6c646bb6d2f38dda42b610e975f0920aab86793100ae6e5475e4c879ab7bb69e0a40034d9c5318012059320012de20bc71c6549f229e308ce170c8c4268443763468528870c424022d64035fd6e6e85277eaa195063fc371548e57358c4d5a4ce539470771b5a4b7b173a7a5df11e771c8b1966619feee80a02b57fd46bf0b14f8a16fba8af8362df6857a96585883888be27df52ce4d3c30b0c8633969742038c4c336733cfb028eca631898caf1fbb0c216eb740db0078e1063bef6582f3db739f15921da9846c4c4c238a80c5598b23f0d1d261c9f9156c8fcf5c4a20eb4a06cc25b5ee803604969b4a00bb39d510a4307c28111556e222c023c7f3642789cd0edcf17a1de0912b028567bb426151352de3a04edec2447abbc9a4f2118d2bcb809a0ee92b004e593833a6ee379365114878790d805a3f4dfcce0d38c796b78f022df1d8e3bdc6ec899431913bb66c4398cc66e27fe48bb87ef07dd89e993fac7598a3d599da10c57b8ba6b5bf178f4f13842573ace87a1646eb26f03c022bd49958ef14b73834df9c62ee813f72040387b0fd94f591e32e5641f6ce2193ad26921d962174c8e8ed1837504a90af669c33c2a5aadfe300e8801458b3b7a28b4c850ee0bcf9878958c911098973c366a8f052e12e94caaf8b91d4443391052dbd6617f91271dbf452d05a33b7d2ae0b4866a433737efdda46d16fb931f166d337552b659ae2155f067f2ed87496be51805ee2b3085810df78be40ca7460223e6f4deefa69c7bea5c8eca9f51512f9a83620a7e5b209bd5627ec9d64800be0144b1eb8b10c0896b8b119132afb1f51102b5782a5a7c960459f44a172839c923981dd941ed83ec63f1c3f2f7e755ad19bd1b74f7a8861e92f3bc9e179c540014c106de3ccee0a517c83c6d4c45246a4b40388ab7b1986700aa71c2af572862c0701eb909922742c6555a48a882323c44c9ef3c9137e027ebc0e301946d1b54176e6a58a01dce485ce8fd8ed5f830f49d5c6f61550fdb0f46ab5d10a94f0816dde8fc51dd831dc323b467f79210da67d44de33ca0f2ba597f0cd09768e453aec883810c09c2a3884372b025e3f48f1ddbfa4edeef9f188487296cf78097c25890982c743ef96a93a33a638223309bb2c8e6ca33d0c3b1d48878b88ec36b4ff6662c409974b56e676f2b7d47ca447812f1df15d5f92405ff93e6bcd08c8923ddb8e65712726bff0df0e4a546b9bf087308e86f92e6540d83d4cd0666d25bf321960ccd9c2684c9560002d6ea27942f2aaef19c24bd49dca9fd48927d2961f0eb6c20b07c41d9503e55cae30bc7b9a46e2ed584793371173d35b23102e91f2bb88d25361ffbc733ccb66ffeeddd375348803ef3decac492eacef165d803c8b66cf6067c86757c5b4f9758b68f41e7d5f8339a44ebd15d8664409a083f9d96900d93971f8b6787833a4110d03f69c4b88799492bac2f1fe7bf8efe7fab3cfd194708cb146099fc37e55fd4a9777a372593ddd24e7bea812a6584ad2ea3c33c7a9053319ed436f2d6cbb6a7573c6a491b975a04c08446ebda5ec520fba2830b2a16f3a7f6e5d218328495e383adb814b059c553ac1b45d17ca1aae290967b3583343726620d2cc68478be8eb39a3c6bf1e186b53a5538a4b84a9e9cd041da5a79409830840a17c842bc45481c032a2f716078b30bd8e127263fe55083903c749dc52a48e86c60937a3e3bc015d10400b3006c528a55e0c529f0aff3d0d38c1a1c9d2c914ece6990b2eb731abafaf744a19c995221b4d51b5f870540225b55c796851a2f295d4b2762814bd83142c2cc09ac29903d22bdeac7ae74cb34414d6c15e870052600e281493b430d9b6775b0cb3260844de84c1e8e0b08ff4b132484ad16da9d58f060a86eed50e2908d8c24c7d1f0fd5728b3859e076c26212fe43c26db85c9a7c9bc090c4af9865d28a6a5eeb367b08d7d81c178324c998b81351e4e8f0991ea447eb9d63fe096e989770338858e2339e50a1594fd12346d19b686047326a489efdb7a553966b413785afb67fe93316e445e1969401c0453f71862776312cefef2c645d1db88dcf0f503cd1544c1553f33935c17c7f5bc4e47a9c39628e654238dda6cced57bccad93157761b85b2dc76e82f720ff7cd9af7caf491442d1e949d4a38062c4b9e0d6016b218122abf3e7d11dd1a2167f017bad834e9d46e51c3aea6a3f529f58e494daf30f98b2d9dc5a9fff2114b935e952c3229ac47867a24f331b3e61e6e691166c8b6c769f5c083818d0acb2745a4b9692752bfffbfad4aae9953e8980cdbc6daad9134877b704c61a8d653a9d482be474e8cc0f5dd672ca1f0d8615c54092065ee2316d16ce29209b241d5266fe6e763f4251d8d9496e32ca47ce0412e499a3aacd83d9a30da96f6ec2c4083725dbb807cc282c7de2118f93a92c4975eca0e1773f5f8c9a76fd46efd5b6a452a1bf9d7ae4e9776d5f079841eea711af2e404abaa4df33e4f2451ca4deed5e506e3cf5cb4dbd55c16631cd7b6692621f51375a64a3ccb1b2a477e0aa03a0d1bf9f7fe3f6c12fcba5bc391dadea018e3b15c6e95a304751cbf5b1d0b09f1fbab51197b0226f3452b4c86ccef8e7ad604adbe534b995585925210cf276c6a223ed85400ad12004a1a89a8e5cdacea0d44d804f481f48ab8152582505e9802ae7bfdb08bc9ecfac3abe9f622e07cc0b3d7620af19cc4442ec5fd33344858771004f5a58629ab9b2c6238a53b2101ed0a93e0824b674fa7171aa5e3415aa48360cd07dfd17b0380cfc18f627eace1d2b9b92ad7846171666bcceac1c2cf1b81e4264090d65e2ddbe2c048b39a37a2cecce3bd36fd1f40d084f27d6dc6045485be9992ccb4715ede65666b6d7413eaf383402b3ccd675f0d75613be9595992e26b01c349ca3b018df11a6aa06a422009d1ef505bf247a3e1e76926e8de5c93d2232e335978d8931e45fc72be3478741b920c19051bc8c076bef6d35a282a3b46df40ab55560041781142b8ee8c0c61c12122512a127e92d77a2955b16d113bf5320c8e1e2834d724bd5ed38557412f14d687f138285379fcfe419b5ae068150f860115b297bf8a50a4ffdee9c0d91016aae92923fe0189573fdc08e1ee1dbe7742afac158e7de1d41eb979e7b11f5ed1ca560d3ca4a71ca539d048c94f156f1d0dd9a88603ecd60d7c751e63c7b22e4e4bb5ef878ee110c8d18ef01d36d61659bd07ac353c35f4eb9d0efb0b7abcc0c1dabf8b460e8ed143b57d2c2fffdb58a0c337a5dcc2401496621f00cb548f760ac920a5e8e2fd435d72840d0875cbff933a4a356b4896db47dd3c5904ee03059d3e9e445320f7eb3d96328e00e0653b822e2eac25afa62096c5a3d28c52fb7e4e8a5a7e2c017bc3bf90ec4cea8b5295450c6c5f66d4c02714ff1116d1cbf8c1da3bc05c3d9a0fc42614b7672a006fb02047d4fa73b8ad8dd502dd162ab2edf10a6d953231fe7917f719ca0f4fa6eb5d854b5a14873eb900ab1b6f62fb5d2817d9c5073f1be900eb620ea46ca1248ca497f56e2321403ec15facaa76e5cce455826d1131cc33f467c999e71ab81d31327975c7b2a6e5713a51060e0f9a92e52f50be06eb2e6f768b51e8bddd00d739a43d3b4a918f781dcc7599d79887dc8e47c7bb62101ce9c5ed4f50b4b2ce501c97054a2e31e2dcdb811c1e4147dc3a38d5caaedd755501b45c2d126d3a10e5d2b373f3c2927a5a2ba438a506c0055bfac4ed40571fa68e08ce4da3233808ddf525029c92266f30de1ae3f63cb445591b9e5281b261b5b2a22bda69ad2c1a502d4d54fbe60ac8eb5b060468309b2bd602f0464894f87e384b04e2de46c4de03e21d965713d1e0b211a5559ee921aa582b8ffaaccde846e9896f1f9ca30efdfce600bfd53f7b0f7a6f757be0cc93fd14cf26c73a92c2bc427449c438ac1e500650a2bd6c2c0dda13925a1cf3fe3f9f3ba5466c8f421a54e5b195215a695c7099e07f746d49510eecffe5d87d2316d099a52b63d732839de11d4ffa90ab055b5da085aa0a966a3565659b5da57098ff006fa0465c1fb00bf382e714564ece06c512d6bc6f216e74d5a47410db394a85c13b014147ca80905bc2f06be6555b15b508c03e72503b75c96a00d4b7949f1183931520954eaf0ed1e1cf0b2cc70d99c4b67a0fde5e1967244192dc155c98aafb44d90ae6f3e0393fc14403a08f70a730aebac7784f35f91206e8cb73ff238d3587288911eda5a289d86543e4cd13f7baf87448f88b63cdc3b3496f0f676e4d997ae2aaf8038593b3509ca9ed06553605811fe99a04201db4236deb22a698a29c68f36ca82770d8d14a7917458dd067472759a6daaccbb9f1ad3aeddb588a165ba0a665b8a97eebe0b69fdde3fc2ad1853344016712df630aa074b9bd4e81d2d9e4b21bc8b9d62aef165fc6005e14f900e388fa7d7c2c8c7624f9c5ad769addeecb1cbd38e109b526c346ef046a6dd574769948e71a1ffcc7fb94ed3ac398ed1a7eb347160467dd095f2ba66da4306400b5e49cfa9f96ceaf2901da946f32fccd191442045b8111fe95d6dfc098bdbe38ab3ff756cd0393df1f57bdf87b665beb2b9f81b719fa4299726d911ac3eb14b22b3304d1650a1e340ea46d7b3296b6d8870d355f77f0654b04ceb23c9e0ef75cac20e430cc3f025134ee9c7244a43d44e38aa604f48e3011f98c33a32a99c61000db28c84d82ab3f9d4dbcf4dde02ec4a8ab6b837451e309db76da39702db804873cfa019b59f30cf8306920744ea87bd99f5ea08499d7ef15959bb592a13ff61f873a23757a46f7c635f9ac8304d7a8f0a9c8a8f8b5929fa4756e3dba981492f6ac19b61774fd4bc4a2405871492f6122faad0ba8e1b103591288387ab481a0ea3832d39de102760058c406acbd9b65df88cb2a3b0f5599c4524978479e880de612271b369cc66ed1ae99d80b132b8054ccbcf44eb09451c6313dc524700071bba7e048ac4314444946c2d74993102559eb3573713c6d63c42f65a8bb57dd873ea7e0bfc1e1287c45310ce042c162999c84fa00989beb2691054464d2fc5c96158b6ad1b59a998b71b8b5adef26ad588d6d5bf206962864c217bec2c2a95a65fbad5781ddec39af36c088ba19c7a01c6cd8efd59d34c43cd6aba668e44f7c6ae42dcf2c3b01b6b8cf25bb4e87bfed6462e66ca256b136aa84276bca9de0120a3145312543568282582e447872081219549813ce1095b08c7462aca0fac286437c981e8c8c5964941a22876046c5c3a7802cd8779da1ad84bdd8a31d81c006ce11ed5bfa289fa1ed56492e01247e0ea9334010f676586c5a6a9d533ad6b21a42856c3b74cbbae980b3444184f21ddf42dec291a28738dd8bfc925b9b6baa3972140d4b2df43f205fc52301f3c43187e1d01472b31ff422d9d580e996a3f2a9bc26345d7f47a9b62b1bd7d5e756d50baac3dd9f73c7293da33682c31b03ba972204ca04d0a11065084770601c300ab7f6c403ebf0dae73d9c69e7b64affdd2a1f0c287a423942da89cb98d3565970723f9d6f695a04b5eede32d2d585c4442a25f078428d4e7540af9304165adc2f547998ecd0b510ae49f34720808829782894c671fd8f24e8bdaccc8ef93b6be9b67b37c5955bfd050e1e65e42903d3560c3423fbe9ee23498611633cd7d281d29f67a0be8a036c84e4113bc150bb9872373f83499e0808140c5ce127a14cfb40226ec24ea0168c6abcd7409df4432fd9ea44cce797c2fb89e83e985f4b3a62e0914ae362e39b884dee808931a080413a4c3d8a055b7c9f4e461b5875c0ba507fc2e0eca8ad78e7ca550e510a2deb5c4b3e6d2d963ba08a49d8fe1b3ba07281ddc234f46e5050a6c09dadfeb1283a19f02c81a042f606ede252e0be0a49f33883d269782f5faad65a9cd1509dd6aea5321c2c1a2cf300a4da6ddafe0f50bb62411eab1e49800fa3494838b30578d6926911e64802ce3756dc5b0607008e858383fc02dc81444fee824e92571550a544f92bfa860dc7fbcf96f0ed0d7ba011e788ab6e0d1f7f839d3f2926af360d8fd9e276e7d834666601243fbb098d22042e8fbf3e0a02355a106705d5343db13be5f040ec3c892cc7e3b6667f904b32a485b7a6699bc97c2e6be8341243f0697c32df14fd060a1ae7d3f28cd129d985fdaaaf0ecb972385c9251d05697d4a95434f2e84c933c35d672001fd2066023b5040ed1fdedcca6801922b301f7b2f577eaad6b00c3b778a96ab85112b29ce2b6d37a919d263060b49cbe21523f8e546fa0bf517b84bbb6a976e2123d5544f94e99b0400f86c6670970896445d00b966494a7a58b62027d0172b8ce0760f3dffc30e1bb133fb017094b16ed978422929f97b604b5df52a721909d4b26ea737a870efa1f2ecee0fe994e8b5275d8d634085c5892c22912ac64be5237b40f00a09b13f5698dd6eece0d9f89cb5d70c08bc6775569a0c74a999640caacd892944dbcb3336e8941b01a1175f57bf27d0e872ddb94bc6699c7b8b9e603ccd401c11ce7e0f350c3d38bca00e3bf5e0842057615c1e879cac89f5a73f2d2faef41ca599d2387c56c913a73435b643c90624d2b744eef9314a3add7b85a61d8022b0a6ce8164cb3d481b847252ee820fe17fbdfbcbb9994852f844d8ee5615723c9393e4a596d858347afe0dfd9832e5f0a80ec8f6ca61bf6841bb60e7ca7cadb057bf41c8b8eb4d7fe16008ae05186f5e14a6075f4e6df9a1875f20de209cc33bdb85a48b00684922ad2107076981807f010abc063de72f97ad500348c0176807820bcc242702f0c22bc000e8c558629bd037005b5af8de32d34c864bafeb845a5bbb621bfc286f587500c55c10fe3ae4a4a878eba0b06528e97fd75ace677ac0c0815b6e7a2baf21d1457e3edd35ee17842e5766e24ed9d268c51b88614f71b5540442f717fd35a002d3abe784f2012c912196e16514548aba933c22eb130aba5ee2955c387856f2ca0dea65fd01e6d0cf1203bb6ec8637be758bb44d7b75a31b6d66ad7c020ee30f4415ca37e0e05197b2dd0f658576be0ec21646b3e13ac1d515e000877eb876a8f34f51c35e94c20b4da188e5e2570c890eaeca4835615b0386057b4fed12d501e211257da9545b68d1bc782612a14cc1b62af5e469c028b6dfed1299ccfa9c7f0467db05a74772d6addbfa430ad56793eaf7ac4e6a6cbba1c7e4d757ae8ae0a24c933b57d5e05cea15c682eddd60187ddc207ce588497c1d948000f84d206e1d1cf3504c4b9cd7a538f573b17a84db640e95765e385a7bb082cb93c92957cc68fe944cc6826d4da81aaeaebb68831af4347b4c859b3a4f6192937de8ab240d33494b735382ccfdc4c6fc11152e07970f98edf0c8394be5ea318cef0eec4f8142573643ad5fdeba63dd0fbf0340119567bad5f180998b633623630385abd1010c5e4c5767cd07c0debfa8a168a9fab9b1719427990737d0acaa5a56fbb11c28551d43e7b18069911ea55dbe525f75660ba16cd62ab645fcf4bd873fd3c4ad3c3981729a22ef371a7ed9dc5d78ea2cad42c00ee198d79195505a5fd58013dfdcbec3d395f9d400589cdc53ac38c1ce0fae2d5bf93a906d494b82846239afa937693e9cc6be2e35323e0e45d35c1bbb3dfaf12730baf0764cfb15209244ba567e92f161f8a3096eaaf1211460870b80a8fe2d5c1387689e696a0e040a0bb4245cac088676a7a7b963b34a4d146e768bd7e40a6e840ed651769dfe32f61e0a518c34fd3ae75e1223f2d207ac169c82d1c376e18ef8cd688a38e242167ee26f2aae89eec9180e321204eba027ec4fef032f65a527460119ef46b4b0743a6b3515870a46166222835558b0dded55c5c8a0f9319d5b469577162883e69bf0cc5e36df91adfebd6fcad2e36ee8bd7fe3c605e2fa18a14cec503749b8b045521c4e9b1649df291d2cd25c40a17a93a13ca5ae299e6923be454b36a9bfc3819b1952a9bec20c24509c6640b0924d35e91ec8b824feb84a57920f57469a2e4f42d959bf16d22919109a6827cb46a78ff64d27902103e78543639e3a7b0f0899f26dc8cd3e1480ad58a2953ee561733fe1d06d102ce8c90479bdce2d3224c1b689479b76d83fd33bfdfc36e39dc7214b50759b4886030348bfbca8b130b05355dbb3fd375d7f7729192a72db4bdc207e8a664ff753f8e2b7768b0ff67957bc14cff790a566361cadc3367fbdbd08dd1c5e785a3692c3bf24a4778290a8522302978970e5480c690cd3db2e75bac7a712eaaf9ccc6c61d05d9877198d4a4a33f52e44294b349c1351f3fda863243492a9f3e9dc187a1d42c9196ca9ffa15af12234c4d254d0678e4de910afdd4e547fe4103afe7c6750d6cf0a37093ffb3fc25dc01ee955d57cf3af69f02c0215df68b2d2c1d318125a735d803038385373eb742e50591fc800b9a799731e468c8654a7dbf43ce86684f7c64c8939d7f4b563f9cede26fbbdcc66aacdecf603ceddafd6381331fa0ecfaa1440d500aea27805ebcb4133a74965752edf2d63d031a45c07771566c249f30391cc0663bb433920a18aa0e7ea63890f39512b2147b8c98aff7990e8bb9d8a24848507121c52875705036404aabff60b65e4d65c22725bdcfd97091293eef8324698e3f261e6894712daa53c7d49d8fec450d7c41d43bbff7ef6e2567411ccb9701d8fcd004dcb8f039d5ff923a03e3dfdb003e1bda22a866c99cf4e280b16eb6c0eaa95ce738356130de58e414da7710a6ca03a90023ce9c1d359720265f2c647248d749448ad8ab758caf49f11f2bbf5b4edcf6ab5dc00d4c2fde080e45981ff874435ba35a9139e986179848698b481a38f97aea635e5da1a1b082f17fd68917187d39df9e920322ba535cd1d01603606fab67594fc4d3565636f0fe020cfe8266c3a05cb0b2e011f2568d933a027739c86232423a39559489aee0d2f660da9ed0ef2223b1a5d5ba0186d7a6b630b147e0c6d21a8d65cf0731f8e9a8bc328121f7a96a60660ff27eb7fba40de49318418921725f1c37b0b1208689ae4f7f1c97e9b844d2c2f5684efc8722352469621aff339946e0235c7b8c619619b4bf477e1f7e5af4751027160be6706e30a573e52cfca2db45e007f46e3603f862513dfc253cfed582ebf86c04783a8abce33dc66906c6ec789c13ed3f3e61a6403edafe6b439a990a94712a8da862ea18909309f1e15da8b8812d95fde3b4595f624589fc045d309f287b5a5f22d8c50df6b7c9d69355a9dc9528f1dca2be79b24ace35632d002c45a672f295a85926aa9abaf421f6c9dc02418e68da3dd6303f6dddaee5068f014720a9333690e967c52541577ed438df38f8840c54e6044c7dab9409e271a4e0971f41d769a6855690171940aa8e630919007ca7d1b531ba2a28e73c8711ba2a16ec960591b2735a1df2ec8a5c4e559efeb618119f1949763f8d3ad5d3c1a13c2cc76c65b1247550fc56a98004c10dc4aa26c71820d1a268b7f5f1a8c9c28648130b77a340b8408bae9ebe062d14594a7af3a2b66135c144c1958109833bb5c082b349a0f88153a60f496253607689dff9fb5d0751a3990cc9ee5369a09cf11e2e6ef4f2f4fbc485c10d2b599959946722296741ec315d893d2ba0a469086ed0b704802be95535b4be6784b3033665f6d8ac4708d8bebb1499f56343eb4c018fed659f153996de7c5608778acc0e6dcae102b5109e4af790ca528c11ad5f22563cdff474eb3d205a968c5ae89a1ab5185c1e9a83d9bd6518f64f5f5c231188835a8076a9848d6bc6a9c3239432a4b3dfcb83d109dd851e4966d5c11ce0ed56579080ad984349f5c21fbacf54a533029020a13abfcde1e3cd4c08cc4aad4b7f0e8b70f2d26d9cf0f45097ed96d749aa52ba5f269de075f58ba5323d48374bfc4a69aeae59e20f9da4e0fea007366e811d44f89c0ff941915304ec2da0fd40e087b0b010c5e7fb3a1b15869328831e09b146f928bf84ca0e723913e65180268fa183a01a967211f516f44cd23e78fbf11886f6bd31518cacad4ea292b9baae82157a7f07a1f9c921b0124d6a1b84e0f4e94340e22ebb9540445c2242c26f46e121c44ca739770c21f0d79bfc1f0a21b08ac1a62997329140b9ac0ee2e4f0abd1841097c0e478cbbcd7d87e3692e3bddda51c6fe46172bccb2b49c988f4cacc467761e9bda6d8841020798852d4cb487afa90a66044843633f42aeda7c4634748263a826f7e6c716bebbc0282bb9b0a2bb64586510255f5610812b553560e9bff9216065f69ff98b329bfb08260a446c0f0392df4a000097c626b9704fd374798b816790d1aa980a916b2a0f5c81b4e84b1439685481d19b3aa951affd2a10164082260d4abc85690ffd88a372a6864b50e43046d78ec4543cca8236690f8ffc224531822318319fc74f1b5283e4a06e1f597b0e7d2f88ceb3edc721516626e346f0467898830e92e4e65f5930538fcdf08fd902f379e5586b39c844e126d34f96fa766ee72be3022bfc7dc4fe19a18267be10bd41cdd968144dfe318b7df421e5b9e790cc743530fc33ccf10553a9135ee8940f3de66ebe6be97cd9580f3292cd007ca8adc456ac9227114e20210efaf0b0d75381f7a27ac2d1229235b91def09acb551059a44f015b1ace6bed269e75b861084ba3828fcd30e61a1aad05388251d48a61e64e14a5ff68eba8632dbdd149123b8987169fa3c50e95b1b8a1e6d4cd79e591f51f4b595b6ee7f38a0601caedf274b853c2783a1e2def7525a03752d44f82e6aceb53d7f158256ab5ad1d4fce2ad6283d4d4a5171df4a09730ac5c6d5ed026b86dcb5bd25f8829f652155d813146d7b4abfc744d11c225d14d2a31220d3978fd5ef2175f1e6aacd827117c312eb12319bf905c0741cce21d6e438f6af9fe1d9e7236d1b317677427500135084c6ab4905e7097c006553ec031101e762eceab3cf011359045ec35ea43693a8dddcbc72052f1356fe819b051be42ff1f1de48bd39cff5dff63e04898dfb6aeb5750d93a9c5573e1e292c707115754b38e758787066dafe481630eca4e256bbe328f802c2d050270a8c69a6c96e9a14386c8d7936053cb37649adbec1706ba86014770e8a45ebfcead2b85174729a8e9f7eb76b56debe6d39075c6ef37aaa4363e8bfd7bb764f7581de99500301b92f67c5920d396762ad99c8b3c623149741e176c7165cf137f84131f57c9c08360b39e386eaf939c1610ec72b1d514c26fcfe0d4963f37da06af3dc152f6d2890b4abbfb34dc3d651d6188856fc9a8113890b01f3deda67045168e1f1dcc01b5fba8fb95df7f7d6c75698647deb20252d6a6ae7dd6065234d826682abc64f8da11c2c73cda8d4ed89a8b86f916417dc94ba2f7bd2a7a8e9100c8411bd2197dfccf0a5603e6506ce762e817fad0430511e5a13967482a3ffbf92c60d826aac19ced544d22d1288d07d9ea7d460ff35aa2ea82a98e79a9e2bab0c28604167f1c1878fe4b4ecf3bc36956798e676767b78fb3b515a75a34a714377f983100764b61358ceb4152961443ca4d2d334abdbf553550eb92137924679c852f16815af77b2015ba0817b9a98ddf3ba1d11879dc87b09ad52c8eaf6719ee3e654c77e924eb1a8b8369e58545b46c5d8fb2f30be78f4aa6424230e7970b50e303819bc7762a96d9a79f354ff7172d117fd0acd6bfd41377f5dcaa4a4e0dd397d8765b159f65492070c3657527d8e7ac95cdde897d54f34eac746bccaaf12b41f0c3254ed1f11a3b8fc2fa55d3e0a82e84cff29b5f3ff828772d1e2d0009c65225fc98517051c4b655aad449951a6d09e41f305190bf952a5ca77a11053bd161b0ead1330ae6d7941a8e07667d5a9311dd65879dc7a2857d6afad4b359a66943fab6de1b2e5651512a0a20cffc7ebcce74d7f210582d414570c8dd5ed4671e0088280fbe57f599cf81d4fe7edaaa9bc68d83f8946afe4d72c5bf4bbe105e0e576ba343c0c46ed69c37b6249e5730d8601fabc689b939f6e9111e782143dd390bbc369ca31f33de4e6bbddeaf88fb45de23c181f3639a35d7687792605f888025561aa7022d7f75ac4f46151654d1d90f902cfeab15ba5e58195b0c5a73b24931962ef7d281dacccb2ddb9b51b93461c5e273f6cbb926b5ff32720c67b3146e9e38a173660b798edc4c982f3c5ef0cff9f9d26bfc40c436ec346f1f728cc50e3ca14da161ef0d187f4386a308ae7e0dddc5d844dd73df681dd80949beac709d5780a6048bac8a36a0f008b590677a2a6c6a1626d30b4d074872d316ebab519bb9ac070bae0a8b03d9acfd5a1a83192e8562fc099638fa5476ec4e0877dd1b82d59065e97389b324b9d25fcef07eb24a630c8e295412caea10b9be9dfe867d9ae8857486970f7ff2e57faf7fca378fbef8a9dffb11f7695a9abb392e810104e39a22c239411d1fd4a05f7b49fac2117aa46247348a90c21e11289090921db95ea5234ba3ea04a43efcae44efe77016d39fe165ccf12a05b32c9e266fae82c0488cfb8eb68b59da389fda6593c92104d7726ac7c922c42dd562d0173e4eeac669432c165077823f0a8f017f9a07a712b4722873538d5985170bd6bed00281060ccec2aa9b306bcacb79e19851954b7ac143aa05d18f46d696492c46ed831fd14263c114ed5c8c78f3170b1c9e0ec46083afa31a3976d4e1720892927f21e28bac3a562a87d8460d6dad353984ec8148a28e0e018c107e4ff305026f312cc49dd04b4cf07891da4abf302175c6f4da4d407573babfbd286bbd215ae9f249fc533016d5823f1f7ca170113180534d4a0a75df0026a4208055c7dff68365e640386f4a7259dc9b52b64012268ef86018d20a16cedcf7fc2147ea8a61c818b65d8be736f9a33cffb6f4298c3ba464825364a771307f0e2245e7801941803c53406022d16d3e18ba5ec9c3c5ede66789500616c13e36f9e5e7e6535b6bc3e0fb6128b9c06c14b1871b6d270071a4869ce23b4783766dc029a976dc5e05879f7302b8ce1ca4d856836b92b479ff7ceed694cac98cf6da1424eec724cc0ca96a758c1fd69a7367da0bb5773150f11948b13152b162ab30ced998809be15db5327d7609c217d8506c7a2a9d5c7ec6ad8006da39e3c306146c814a2da24e81369f58a603fc2c81e9f940b1ab89ed40d431dd59221b69e860347044a18660d9ee24a4dba4582bf79368d883b0f208026e6f5ba5317eb5db45e5472a7793f568655597509ce5bd2f0b371f39a77c737774ee53933d32f6375001b010ee97628612073c7d503fba3249c7c892130785493b5b9ea1b7a13817b281e978c289df8507d82bb2e44dc798f15a91e5563d5f28115d590f8ea5d5f368d212c8f9661cc5ee57b68cb01149db4798b0065b2a0aa02222c7d3fb0b880e82f5d3819b614721cf56b335219c5e0f5994c9d3d9c5912f721aee3b184e4732715f71a2cb23dad766c571241e53d55e0af52fb1916fd5fa699b418faff598d3619583413a1e32556525c6bfc403136650f6ef87ed2c1c4de256e14132fb3b1e498a0cef87e2357e5c7145ec605d7dad4fa9723d174f9cc76412b1fa269de98042469845b6b2105cb64370da82a18e668bc4b182ae79053dfc8e8533d68a2026221e9ddc285c826fb55763d06e107f4b65f61f8bd7a6ed68e22970280d866ecfb8d3e3c6ab6c21f7f2a46e71b4bd1ad10e49d26ea8d221065cd3d523c00f8c09a6053ddd3b9bfe3ce5365c7a36ee2320d2b892dc33f72abd27436f23bd165c822a080cbc4811de8af64cedb9b4e2cad96a6ce9e9f4fce5432142cde43944c477856635a842977984529b6d43e575499a98dd7959cd2ac0cce6fce23a671b2b154833937b6381549b21f655f3890aba2739ef4bb2859ac5534b46d46ceeca3f0c5e1c949d834edf2b2e4a4e9ccb1d252ae3a02cc434f46adffe0f5ebd407aac94d0828a3974e0e127881a3a30a1d928a31978c47c8d7531edf7993982d0940950681bfd77bec4099d082a8ee80b107f7309bbc81f9d6954f147db17724d909f38cd8a34c9864e978e58eb6067c2680e61ee5a59d09f23c4990053173d8e7aa40d45175db6fb9010054a34e5329040a05ad68acd335610c611f812f6e0ed2c7c897ddfcf12734eee3cc31fab48071c08c45be4cb2fe1b4ace30e65afd0fbe76369fd951e0c242ff7416e2cc6b46bbe59a4ea9551fb01af8d4bc0303ec031348b49f77fd36d1444055d4eee3a778c983629543f1fad81c1958be999b160ca7a62220bce9c233963120cd05e478c9b1deb3a250aca8535e4f8ae3874b2415f4f9526916bae0296a46fab037ddfccb730ac923ce85231d051c9f8f64a37e43937806b68097f30cd22bda4e8935336023b670c4a508a61c38676c747053109ae53579894a9e28b456a817ed45e2e2a0a2ef6b1455a4c720315daa5378116de065c3ea456353ea69443921ad20e54fba92200b540977bca44d26cb0444a6d0fab973c2ef8b98694a09b1c44e7cd047f3c4da7e7a03846177a3773680fc63bba43f21d34b1a44ce8551f15cefab13af9e95dffde6e36f6e8e84e49e2b403706de5fa86e68a8a0d38c9a8063833c4b94464026d6fa84a857e439b93cd0e6a1b74014ce914a854549e4ded46c29b1b9ef4d92da3e6470a4a0496efc925734f34407066b777528258797174e294ec2857b1647988ec61840976dbf4da9fa63196eaff0b9bdf2ac9446e3518518346e63a96fba3360d769be52b6b8d572982b36e7a25a431cea32ba86d99138450c65f4922b7014b23f13db201ae224c8f06aba50f9712031445ea7240b6181df22b6e6587c713bbca22f8dc394329548ca6b17030a623abdde8955551ebb23e0712430fdfcbd3ae92af2f81fa104374a60cb7c4d3c50c026dfd3fcce877200a53dc0180a229a62b2d25d7c0e8ffc9eaf60db6fa16dd10525b6b5e33b681976dd9413cef4d564ee4c04417b5ad53fefc850eecd3a8dacc9bce1d22f146740e8c1d7472d14bbba3658e25802a2e8376fa4419e4f4d62d0f97562d5fbddaa1d3ad7ee1939039ee1fe7b22a2d750476284ae4d3da2d2337e40c77a9ff4783749f14a3f450475906b164037a6bf4409c4a3997d6323cba787fc2328f1909ae3266864c7be592d23a214234e23fc02362a25bc195ebaca45983df7022da912b1ca747a0c9d29701b0714b0a8545b39c1891a9cf1f4b988e86daed65a69886f146e6a9cee33674ae41634e3e20d119639dc965eb81806d3d0d68c3d5bc4720da27290ea35d41a513c4a6e0ae074dabc0f397d497f219571cf9f15c10bac82f14a9c45fdb8ae35c533b5f853460e9658bf40e99a81b38fd8318375ee9b6e546f550a2006e98b775725dcf1a299f10ab32ba213e2fd6b69f6c510b63b5b9622dd0eaa07b12494effd62e6b2a4a593bbdee706b2b53bd16ba2a1196879fa78798767eabb4ceae0b9d52a6690d74448abcb50a1f43f950815473015975729b60a487b11c02203f4889eb6bc3a48643bccccb9840aea2ab0e1efd70aec4528fbd6f2e852164a944980565693845bc04a0de724c2ff4a7ee03bb081f991070c9fdb0d506e1084a231013e32d06e1198d32c8c8ec448d8b81b4b151304179033aa66aa6c83d447622e9208ee6bbcac8cceb1b20ae522457a7fa9179511059ea396342437d2feacc51c7c4ec199ccff69bc0c0434b6555d44f75eef2170dc9972d68d7939c0e637bfb41f11c5d2384cac426827d10b3b9e97450b7261075e011f881347e5114cb13997ecfc60084ee2168de1dcfea59c9897cfe2fc02c9e347ded97485fee27c2dba843e6f4ec57e337b49f677cf3d1e9d4c42848e4a4d179ad0c1bdf422ceb811dcff90284b81cd7784d424ffc7f6af10f8923e9990016315b3fb1f50b126c28a2142ac98742a4739bc032fa0858a37fba741f20be0cfd3b19465e178992e11c119d8d3f406566147fd2741d91bf68b97478a674e19943510f5d4c0b081bab4927e927819196b1043792a3c03499228f8140a1d6439c026a8638328783ca9fa18e512a0bbf1830e887c7afe55b1cd8be895e1224a018320a384bf5eb0253564f427e9b38f25a94000f6ce6b9c1cc389e6347ff1c2a94a697a6c343776303ba4c4ff43d923dd454d8f2a05b715e818241ce46060d1152ffa35c2f09eb38635417f97ff9d106a092418b959905995ad31d303f6699460c79fa854bd6e37a186dfe2f8c39cc727833275c23eae2f2aabb903d1488597f143c13e6603b2f7448cfc28746fe02d746988a80fce8e7cb9b51f23180183eccd1933c3e3586e832821b410f4a9521dafbb40cfa6cf078dc3e65c71075281744fb2e6b6e01c5bca1c8cd11ae6b41d76ab82233c6ae6caf190ffce5bf557eaad523c1758f10a6137322e3bf8921140ff1c44bc2f46d5fccf4cee39f4de3a854ad50d7f3897aade66daff0d8b15de0d592b64a889d4c1323c79d8d138d0b805463450ee135cef3a1aa188afd7e90af9a9066d8e3088b1f768097c10f1fb8013594dc4717484f8685d25bcca62e1fad73d67956d566657b2f2ad3309ba57ce65a06a7aa98d8eba7dab0fdf96c947a48161066268cbadb9e1f1912044a62e872e5b294bf35a9143cca01ea3f14c436ee15ac43bed1bc0d072b9c45da9f57c16f89b24851427102040059019a1a146054968f867c276276d668b3c752d54c221e01263f21044f5f7027ed28456364200785b0258c0e1dd9ce1c635a80078ae2f62fd0b0f1dc5de404df74bf12ce3ff1d10a8f9332b62a1017b6e8ef9e2cb49a1b8aeed420a1d3422c370b03de8feeac3f0f4ad9f5ea45875f95178fc6086c2be660178a3e23ea3e6ff10927f0776b88e50e3a72e1027a9471b6904f8867d345941c7bb4b1816e9573258158e4ba392ead97e3eec428f6fdddc6ed0d9ee62b15398e8def8960e6d0d5f0e2c175894f7905cc8b200ff4ca0147c1cf85201ffcc97df085db2e9565d89cbc69ab354190cd22b7713a5603dabb9b1447335eaffbcf645960c66c46086e266554294108c257171b91537e751da80ba560219314c3f55fc6d05198bc021c0b3d2b84f3042cf9ba3585484acf53b9b6853876fd4b6de9813b8bcc8156f68db06b665e011ca3590f47e18ef1ca5980b1cbe8b6ab7fbf1f57fc9041e46930dcfc28dc8bd342fc9af947876774d7d2ddcf304a829f4632108745209f32829dfc221b13e8b4719bba50b5058ca1964f2b56a2036fc298ed426fb048285c98c2d8cf6f8f36adc3dfc4c31c6512553cb6a9c58247fc7834a48f2e402ecac58def374108b92879b77521de26076be56dd2f30559eb40694271d68716e921b07b680eb0d3d280e32cf978e41f24ed78c0f7e98f58fed6a8e60bf5e15d227ae1592ae346ca659052021080ac39199a67eec84233ff310d0ae0e9a7848552df9e3fd46a7d3f72c999a8a6019c1f3da9f2d1679a97d16706df92d547062a9974f50d897f2446148acb54b4f3ba2a07da7bcdc68329462823330d7e06b392494394f91dd53735cd1de3643f6306c8de2af3cec869d97facd3578bef4aed2da6b9633c632bdf50c1a24b325147fe29140cc719eb2018941ea3ae7f7bf681745261b2c0a3a9cf63769e1922df6a50b7cd7b97de35a3db74d469313ce4fbb1d48419c2231fdc7ca108d454bc82b58388fe75cd2e41b43b9e1067a3e71b6a2684dfc333aad0d74242d3b41eeaf3def0545b43d3514bb9e373545b405298883cd333b50b5a5a22835d01c5416424cb49663e8bd176bb5c4705e1127348ae67a66fd8ced114cf86e73198b9a8926cbd59d88577f3a0f926e9c6accafe1920660d683686185b70954b85108435fb13d5724b7ffa677129293a4ff6cca71bcf23115424ed0ac267bc9dd3b940e82ab41d68279db2df74a4a5e22e99bccf2c2f464c1a611f0041192321490b9b3c0fd7ba79bc58de2b35cb417247aaace4f342ffae5dc237500a5ed3eb69e1e6ac4748dcb2e1139ebe5612c011b7dc4fa639a12d66c309ce7c194cf489a3d9e4deab569d9fd347ee47c785ccd71cbd1f7f27671834e3acf96d46c00a786632af2258ebf1339359ca428a1c8b5d9e3d65e26f9ef70e787a9a5109adba7f3aeb27c79bf906341c34f50255e8bd0154c3a9b8ad1190f17ccd8ba68bfcb432189b080534e5768254891d97c40e0b4cb3d125283c63c368833c08da8925c7b081e562b6ee4c805fbb3f58f718d4fe75a5d300c6335a88404527783c459e42e1e2bf3da19f5e1e4bb0d06c6ed826db31f7e99d5dcd052513da566f15037284204ac6a3557b5817a4c143c3d35ee92106973ce6ca00efc0c38b835660920869e04e85b4b326f86acefb8254dec262fc93d0beefe919783b83c2e9a0b12da2ca3caaeb860957b0405a5bb6b4d3356a6064275f863548995f1110bf23ecd25984e3568aea7572ba476599f27d60713131a9a0e70dc3a5a5ba5f56f42db739039947a7e398ddcf91a5c39985a4a02cdfacbaae106c126c790babb2b09922732ec4d47d5ccd5d566ff8642d32a8379f275c2ec25d8eb5e0f2ec0d80c4bf53bc3fdd41066a7f0438f375f84c299c0a051a85d1c050a1ed3bc6dde315f5eb20061a8558810d3213b8e004ca7f8332e322ebde4f7fbd7f2dd5107f3f0c597e231fa50c8236b23bc6e902d0e711978e13cf20371e9bbf72b14d6e6145fa08c076c741f80387b2179c0d6d8f8785d14eefdfa7ef41ce864fd898055411b7fb8bf7a67a5ca2a7b3c02c29e61d2556eebdc69d42622123b3ce78ca952a2deeab6776c230f7cff2cef89e91c791479073eda87c0dadc68fc8947a24c535aba9c6053ab1fbfdd503330176354eb3be1b7a7a3f2cbd007a7a8292d15c2e7760325449d64507b4f41ffcfa2de31d40d7d0eba1419407069001d548a6e73bb8bece7c88a508b2a1ae02cb1d4032a37e902fb56168e841550cbc0b9fef9ea3c7186c84bde4da69710e482aa3c157ae6a5b4f2708f38e51b91a55827f5c5b01262f84a42ecbc5d5a3060c1f9c457f4d46390151832642d0ddaf3bb06485f1b5c95dab7b54501d0ceb6bfac441d939f8b34a86698995ae137b70bdad4428fea98dad15339b26bff97f6e8827d4bd583ca67e0f3c1b3e1d69dbec2d2ed46860ee2c252981956753523d45d4ee9509270a501d874cb24eef76e1beccf63b6a0ba0472ccc9215782a33289c092fc1fa41ed34df0c5e580aa74e6c2b50bcec6d787ff2d074c15f9e82411fa70f5fe5308fbfea3b0f485051fc99a29d0caba6fb3744f680b6fb82f1b645db7b5641c0aa8b4f5e3c1bd926229c18c69439e40cbc67e77369279ebac52174ea6d903b11d20698a32aace5b20a8d0bddae4750b3cce959eb2e67290423c5a1a74858c60177e5e33565c18c616ccfc0219315da6d1882c4a56cb6172c60f8bb0f0c086661e34701504aed3d7d6a5a81e6156e482c3f74cbde1156da3283cc3270ef9202fdd6e438e1fe9f35c611163efa1c703247802c5de878bec456fb27f81f71e6a23c54412cc6ee053b94f75918cc5dea341abc15da895082d8ee38f5e0029d0ad074a8e7c0d3845b8f594724266679c3cecfdc5a613872f16ec4b691ca51143ca10004fe85404815f469e566f89a83fa59212526d7b9feb62b45c898419e78232067b2776262e3225895877b1f9e189160fcdfb4837abc0df56238bb7f55c41ae34df1816368b784aa6d38bd470121389198ad0dc884da9278c10740635ab62bb638a8a539859f3ec874b7a262e6e8cfbe142aecd25937a3dd802497662e197ae9747ebafc72624c9c530a16bfc933d4f0b5f2e3ba163f9f36356728181c8ab2138b955eb01c88bdd1322daedf4141f307e186d0f05983143c178b8687306354069caf1d937b6c8086c440523e5a12a03b01a944110b89161402b73feddc5e7004ff7ce492ab87c2d5d24f4bc163e201df4773e2ae695c392d5dcdce6dc57902d217bc542645ba85e284d26c746c6cbacaa4415b71626740346df3d3712a8f111069877727f720deee0361da5ceeb1c0ea6e8284a579298ba75d1b985207b2d1879b3ed083ecb4e8b0b08e5e74c14310fb7e2b4ff75069833b5619361eed5ab45031dc27bd7987e412dbb7b39917f7d87c0af904dd076db1f88091211dd0b62210ae287b101971f71019e5898a925142e303dba0cd7a669d42a266c0f29c88e82eab9591c2294001e86c92378007b643d416ee108dfeafa8b57d76e68a2746c853712efb65ac315f8086fc97a420e372d5a001638b64bd2b44b383f5f21999b74e5e8c831526c6f8a40a6628891973553534cfb4d18b6bf2f41fa009e8a2fbd24e2d1ee0eb0b9cc1bbded316ea369609afe61aae588b40f4e79db8325de89743f97199f2a0687e639be24c5787a4848104c1649975e6eb2feb00b03061d811d87f5dd0be4da9b1be0b656efb7e9b15fdd43407cd4aff560062a2631b6a61a6bd787f453ae31827a1597238a2e804fba7dcf0fda9939f8f9f631ef53a189ca3063188794261ab61cc8d6ba57a61672c1f45cdd755e4ac2a8a1c3e84ef04756eb3b3e84f44722d14db131a6e54fa87f3be1b5a6fc1d43c5792272e4fbc30550d27873eabaf6d3d6db052e14431908541df7d99fae59b320b8ac71781f31162f15baa6e539a0a1af52704b030143e070bb68085055038d3d9dd62ae5941a52eedb34cc846666605ac6aa0fb74ab860176bae5ac08f2262af9fd0fe7afb276797f9559df36ecbd6463a3cbe52ab7e245d68ba0716e6f843a03b6041a0ca2d9fba15867864ff4c187f89d16c4296a8d736833c44be46f2eaf3d82544ccd9f8e7ef4daad51074dcb189e41f495e4021d9cfae3a67ea1a147f185a6d23bce5df827b1a01527bb7e1073cb5be2df0bc579fb1b53407a80fbba9ee25e18f76c3435b7ff0c800f00ddab191d38a61cbeafc9d0cb540b0098bde69771d177d787bae3abda7f50ecbe55f7ffba9dbfcec6530e224f7bfb2fa7b0306ead5f47ce4818548740d2b4e6a2ad84ec0d70617d09cd20d6d6cd6ac061bcc379dd64182604fa15db5da82ac77c81c52698eeedf52fcec0ad171f4e9ce442326a2987cdf712182e6e6d31b02d5edb25650cd3e43c374b6b939403315d3cd25b74abed79a33fa26f0bd76571c0aacb2da87628a53062f8ae319654a3acd1cf9fca1a887200deaa987021d39f95d4755cdca992bef28244b7f68b2cefda30281ec6cd4ed6c4ad84874dfec2e74e23684dc70076b2f9400c628ad95c1530dc32f23165df90ae66b25403efad45a8aef83eb58b656e57082790a31f35b54539b6bc13e196d52da139ac7e3dc6f375812e550a78a8c3cc03b58deb4e0e1ead5e53e613d6c3e25d331f38786973115f03533d3248a724b79e04b5a7249416abdf5b02de58054fc4cf8251536acf7f685c08458815c15e80e87420f727da0237157dda937791ee37dbf7abf9f7cb5fdf3410f05756f722db6fd2f3a091cefea7051e32fd76f677952f28f76fbb2b237dcc84671f02539e115fc60c295c9959dc3e0b14f3f6513686060de18f5ae2b37917cf3ce27e273a78849e53e59450573dc6fe82606fe16d4416373e7fc8738353fa3af662f6ee42dcf578d225e61349fe66daed2494b58ef36d8f4805b8732022e512e88b55635e21e5c2ff4feb6459b98a8cffc98605179c5194b89480881c9c002aa0fe94c8cbc2861c6b904e1df9e2a34c8ac664736d0a50ad5b3f47e94fa9a0738f559c65db5d11b20e14c8abd86f39d936e125125e62c1f5a5774e46a31bad445428274e11cb807537c0048a46c6186469eee4d460fe35f4d14c14c75f5987a26742906070713f8aeaef5ccb781c72ae170816ac147b2697abc164fce88150f397ea42542e60fa89c5003334b20c3577ba4a0813d39b7e96b9043acac0bcaa690bb2fef9a53e0d0236f98c1af45a81e2933dbc99a75ab90e45d098d7d25c6655ac53e3745e2beba9cffd16a4fbec5d70a5cbe95eb25f9022d9ab7b57915f8116d9cd41f2d729c0d8c177bff53a4169c08cb4b8db9cbbb78b4235d982176a89c1c205867d2099f7153e82718c0151b8f3443602a5f0bebc4ec08a69879f2c45668f5e5fecc91bb71a5fd33432fb5d100a553e9988e0b7fd9693f187014e595b423015de2873094a0930602dc84c42faadd4a09c9a4650a3d26f6468ec4b41d8223eefc9e8c1eaac0367353aa4c4b8a467e86f4b38ce369f02c326e5158e0e11898de34f63252b9ee353bf66b822beaa3dad78ca170430a1565afe413369282e4a649ed3ad9497a52182823209470202faf29fe3e676c5b66d49d07e2460ec50f27625aadaf3a8d1f8579bd5f1fc4d9e27c43cac2e006ea0665457ca216a03c171c6e0a292c4975302d20049473b6d569417e699043d3c71f5730e2e2951e6dc07b7f46c2d3bcea66294aabf5f709e69d3e1ab1f8f482b0d1231a2380185e3016e7da768be4c4f1b69cf0f8d6df71e7b923499d16bf46151d42f77d7a387bdd45f436ddf39cc02674b48dcea76747833f4cf4b78dfaa06b19e80c37cc5852f641c396827e22a7768ba2631640c8ef629cae0ce84d97e80c3025bd0182aac95565ba7e2ec95c8176aaa68ad1082e45bccb0918afff364bfe3b2d59a45ebfd451b52dc8e5713d9f50ea4ef1ec2430e819cbd25029a54c6600ed69f56cf49c4ca2c6f773057b1b00a0f37f9b8b1fa0848aeb9888340d54b944ae3ceecdbea14064d6ae268ad8c084c6185e4fe1289d33b8b09b279240acdc09ba1e73f9d961e5c44c2dcc629a64991431ce0e2bd7485e4ec35799b4b040a0c1bcc1c1c3babd3feba05f3931e2dcddd26219c2f7f3001cee6965a402dc0c8f07c461ae10a82834e60fd453a130d3cf756ce9f5ba18107c80618b7c9b6bc370b86c4cabbaa58dfae9e829d212a7ad8a7cf66bb914ae004365c15c56cdc17e5a64878533674be6633ef7508cad1cbebf367da261bdffce20c04c032c0f769299bdd63c2a9dc653d6bf48d37de5d1602e0ab1cd43db37bfb6023ff53a996cb4a4fe3ea2825d006a16ffe079970e8c3f64bdbc8973deeef3bdffddba6ec09c3c2306c7d3246c1a6df7a80316628c3a361d8164791267efc8793c2f27dbe6adc2fbfc07703cdcc725574b405ed11c9cffb623d945225845fe464d36a918727588486a30217abbfbd83b2b94dcfda69135410096458bf788adff2048d2b277f947a9efc41c93af95b09e9c95f49d8c99f7c7b38f957e7b96d88dbcd87e7172bac6c40ac9f4dad99e661551ce052d32fe0ad644a472eef459e7d7f9d31b7f51467928a8201553434a9aefd919b62fa42e962eee610da75b4dd8db79a4d3a7cf3f5a659984a65b8eef70033b0bcea3f5ac842d8254f00b297015cd080f6644c32d85bfd3dc20eb1798640c4fe73b690f59a5685e5918c936c7d885ae7b05aff41e9fd8da150aa4ba2c57458fc8558cabbb9a5eb015d5287fe1e6d86255f1d6fab7f8207183e631bdad03edf8907b4821193a5bf22349185096ddf86df999af5e621477a8bb61d72515b06354a6a4e549f0d6f4ed6db6cac0c75697dab6eeb5b4277e61c71b6941eb2529a2d4217ed81d085e641c80d0ebab440b3195feaa00d8b7511d7f415976ae11f691dd0782d4a257c862bd04a96a2d7cd158678ffbf11c44474e5aa14909eb8e21fd4fa8020961fb328556e3d1dfe02e32e5741f3c6091bee36276b88382f1561892c5a84ced8a0da45a24c09a5e1e425a7faba8759d8cc12af1931ec4befc38bbb68457e4e925bf1c1128bd09707e2b3ea1bd6f820002dc7091a3a74d68a4744c071ed4982d830f28723180b335dc21ef6a8873dbeb0d1d7c33a9fc025e2af4e721b0d83d13443afe14a59f76aa7eb5c6eb7a7c84d51dbfe38e10f1bd273603f068ce7427f4ec8c786f518e8cf01e7b9b09f13feb1213d06f69ed73a1b00789eeacfb102ab97301e39acf1aa4d62070634ae320c55d65e6d9ad5b7adead8ab0f4b9535579d66f56daa36e6eac3526ded55a75d651b558f75d5b0a85ef3ea69acda565ef57e76d6fe8fa03522ae9768d6f300ddfb3591fdff8379cce2f24b1f2cf116ffd790db9db9c0b6d223796081cfefece4e725cc7b4303c11e7d3fa139193372afbed43e6270ec7d80867c3a0742a76fac56f640e9dc18ac592a154c91cc6a36438e1c28d64cd15680c53b21adecbc59611672dd850fbed1eb95ff63a3e48785b711f0c5438418ee210a79f30f0b790965488212ffb98375fe153a5abe65e5c5a1c65df55ddcaaa87c79db67a8e5e136614c10d1e284dd149e08690c98e9e262b9a28893de092511b4a1e00319d837145c927828ac0a86b7953164ab5c55a905e984197a959ad70dec4857199689dffbdfbeae914ac5277af374be22cd896479d0e786caa1b206fdd813f0ee98f50a5b70d4252d568f8f4ef7581507958ad0ae861348d5bad258f836f3fcaa870b8b2cbef5685abc428f541cb0329d983227994feb1cd2787833411f5288c7a4f159227eae82ddee1bcb3041aeccf36e1c3e2bd0c9c05952eed317f51b27e686fe53e1573412a74c3e79f4bc40a3eb961f43071da8a8956faec00333f939d8400d75c1388d96d24e4b792d112b51e2e8ef0a1dfc10ffbe80b3dcd48e18fe69bc7c2b9bb2d84090ad03f060cd47aef2d75999f20fbc9c5e0038873231f691e8300f0d937283863c8c33dcc29743d0c9da34d40ae982781b3bb8d57bbb1e0875e51c325fbb428dc7b524ae4f28dc4ae25e6f87eb1dbe2d2e6ce6e639ba307ca531367ce6f7a290322b9cd33f7bbd5b186f916cd031b93fc49d217c23153148d081c83034c00322c93dba8aa2edfed4ce1a26c904c4df3b6a2af27e450bb9963c9755265648f1504355a8a9bfcaab2e2e1ee130e83669068f8193bc030177a3cdc505347e2f85331cb99f76a52a34c9bb67891b0fb2668a18de4f8ad00404f214c0befe54004e5d05b794e7e8906cc7bbb53dab71a6fa7d293ac5c51340b8a1bcbbd052d31491cd2be2579570ad251a1e4b927d39dca3a75b8c40555459e91c1146902ff1e840c723163436a179e34efe24ca188cd9529c8c95072c9037c13f00a0354c898e260411db115e6e0cbe1a1c6011e5a453deb2fe30ae5c5140937f0bed70bf0621ae14179055f22554eeae31e2d30139b7be480c897c36c43de0d6cdbf4db30428cb77656f06652094927ec24442460d41ff59a50a74192215a19cc0cdba2588bda407a43e49ab11bf49ac799d2c5db24dfc8547a1f46f4aa1dca84ec92e171df0d2a432e1ebfb935e8b5c2089cb717f1f27ae96750d4318e0756a8dcda69b5416e00e0b3a58700c17abf892f89e42798b83aee19344269f1c505e01c97049cb0d1420872d3afdb9fed72de9a0eca0df195453b31f2bb6ae8fa6926a4948af57d7ab0c838598836be1546c8ea14aab616d14d5ae6405797db1a98d683eaeff729becbb02cbe43bc0cb44ee7495c77cb93a0ce7aa63d251309fdca6f17ac1433d0bad2d1df5f5a5f63725ece921bf83bf6d1d88fafdb27debbf4cf351a38cd484546dc13494c4de5bc64cd51f8270679e89de8e5bdc359e03a4c107c2746c72cb511abed9ff3c7c89dd7c32423085991cc55b07adcc01d7908052748a0ac817313b35dd3e9594a4ec6970c490160e58d2a20638a5007793b1428cbb7fb0b3902d0edaf6c0ddcef58bb66701742ed555e771a73b458ca6cd211cc0d24ea6fd18e4013c4b155fc095aa8bc04ae282af5a4995a660ade8a0b125b663d85b80917ee16ce6a61b7d9c3f73779dbd9e151703946a03e79cbb5fc4b9328094fd755bf9a0e5968ecb6c38bfa4b97d7c2d6e8ebca409fc64b1dbd2197cb14a67baabd1363a12153b39bc0ba7adfe4078055c17fbca495080245b3a8d2e7e4b72806e750d46cfa0bc3116fdd5a32d326be1b4f82c212e1e7ad9390d221d68a61dfc39df3e3ca4eef4b835c47d2b9e3cb346bf2771778b869eb38bfa1b0cfe30a6541ae4361d8371230ab0c438e42fe903165a47ea98dc9049c737621d724fdf4ad0067a43cf8aedb906991d21dea3ccbb5c424f23ee5dddf49ce5cd8e56a666bfd19ef5cd7dce470640265fcc937d78d65f72c23bf8cd0d869c24418359455a488d36ba9c4f18757c72e2e0abc2f97bf5327b71ed42bfa2da8c8243ee825b332cefac021bd35dade4c3e2ae95281abd16758d899126ddb227516f9e44cc9082d57ede8463b1b356aabdf94fd2237517ee7fed14f20215e02ea38ba2d7b4dc1d90972ba234112bf39ea5c906ae7ef178df4a199bb94395a2fe1fbd18d5233675a279c95164bb606dd32896925587d8c2cb5aafadb11a281a302e25465b8fdb585b17cb85979a7a8af564587b7943547893c36f966133686fdfe9bd548d9bd7a7c1d0f7ab3befa4930d06ee0cdaa5cbf6f4917c7efd06ab8419aa5fb6a32a5c914934cf6fdb89c32f53a82dcb813015502f85611781cc2621d0225e486d01cce4a267ab7438141d1beaf5105587a0226be8c6bf4c447b3d9094cc9db1ed49d91a0a51a9a08284fc3068e8395c70aab4a777a1751cfffb998867cb745c7dc69ecef6ad3ef0896bca87302d522229491e45043ee08cfcaade8a8750382533d1a16a7712ff07b73ab3166fc787bc351879d0780156daf42a805a622d61fa9859fa8d10afc9d54cb014a17a587909586f3528ee1ec2c92e509612ef98f0e2309f58bbaccedaf702b443cbd2895d7e6657d3b7bd6601da15aa98e5f0055a2ccccadfbd12f8c211a61e20764986aad4606dca5811b00e83286f068a2aea0536df800e1a7cf0a5af95da8f73d8d89a918a86b5df5b39ab798768eb97d9cf3ca40f48584ca848130b0af1332bc7df163f75494672a2ebda5f364b7f44594c64b331d7d7604c861302d0d94b59dac11f372ba7e456aa83d9d2bec8c2a22e0dc2dc689d55d30c2ea7bbd4249f53cfe135eee78462de2c83e389e8e87da13b13d58537307d2d434af4f0bb75b120c8879b548a3bf62819c1214881c697232c2d121bc5e9029efad98ff1bb6423fdc8593faad54768fa49d75052c8cd06e4944038efa198392c6c2d6486422cdbefa0a662a35f0a1aadb681c207166af67ddba68649d59e8daa0c588a02a619f5fe03ad85527db56065232f96be8d16f2e371a15b4c253a74cffdeef4e8558ec44567d6856269e17da1ea895947e9a17bb61cb586ace373e4dedbcf8e8d4e1417dd1d463acd755c38708b0a77f0cce86cbac362ea0acbcb8f8db188a55cfb0ed64ff60fb95797efab79957f421803b648b00ca734e57703c6a5ae62c18e54707ba0bbdd34c72cc3d56ad3e186a508973f616cd2927879f6999aa5d7d4ffb97ee0c233b2c558cec9cee6d6fbecdd0588a12034d46502fe28a76305d4a63ebe31659f6aa725787033e08a8cd900d1f9132e7fa8b0fef381ef26668ad80a71448a1d74c9d4637d35bbeb3d56f582a7231d928b78189091cfddba90ec378d5044c581f82a20783817b88f0693e71260efa270f5577bf48fb1a4cd34f5998d98b19a5838c430766ef969a127b46334148de499cde245f19d73eabf3b424e9f09e7964d0e526ec7275510c51931c10ecf0a7c7cfb5110d27060d3c3d3ffdc01a62b7756e8737481152cd2337debab5c01df519da22e0e305dfb9f28a531d6dd22d2b6e894f662fc60a8ddaa6ec59b9ead501ad39346b9c13ff2870129ae59bbe1c5d0304a813ab60a3f855f291a042e7a00f86ab56c66422755aa34077aefd338b55bdc2eb8056ce03da00566a78ce23f15703d41b988b00edb34638734fe3ba915ee97672be46da3a389134fac5c351dd88c7eab563a86e5794be90ea1005171717429d159508e4717cc98853f504f1976d0d49050bbbfcdb3413e0e459dd6f42b74b04555fa3971335a8660032c7f6ac5ba29a6c04497025063a5b3f2430c7bf6d10c15c308cc037dd177f1ca4dbc3334e067920f078942885dad8136815eee93ee01451b3638a79db42a2169e90c0144a503ba6faa7c5a30141a37079d9c1aac1bf75499a599a3bd1333d9a4dc6d957636a2dddc6b0d63ee66ca4679f5e5bc41c866ef25645c4fec0bd6dc6800c7df6573b0784e66bd9dffa15b7e2ed6b45e44b2c446bdc728fe96365ada24b4899429a5aa07a907b20731de3396db7d99b590d8ed9bc69c26a8f6cc53c7bcc77bbca746d4f1036804cbfd0b4eb6c27096bf163cd7b1b8f843a5559565436ff5c0329f2cebaeb065d6ca5a592b75b356d6ea629f1b60a7deb8fe9e368475ffcb538e88e2329fdadd9d9da18004cb4ef6d6c46b21dc757313d6c05e9ef12f6996856ebeb065afc458cdd0034198e3462331c2574822d1605a17b55acb3e24fbd80efb9b9cb3dfd4029f5a0a2aa2bcda27870524a89c220a40695f6400a8eccb30a77f8e60c40a50394536190c01a58d4e8a645dfca0b2d149cc7c0ee9a8e560cb7a9dd4746cf35829b414db2ca0a9e82c5a082a803247ed88edb164327cc230ac62b95bd6b8be1bd89602c553d90e82a5709d6d7278c63fc9131538b93a5c7f250da5a1384dffa9775a49145bb68f92f6f1f1b9e2d33ede8fcb85d06dae892d4d26cf9dd26e10860e0b5b6badb5c27eaa5c7f6f4915f9c49f3ff30c17bdc7f008b664d70b318461183e69c31486190f9ecf38317d8d6db66fb07bb69c87fc4d4aebcdb6ab6977cb6ea5979b757afd3e2ea47dcd3e4dac9a266edbf669a2563531e3b84f1369a789d3f33e4df4af418ed3402ea4bdf6739b4e67a8b932e15524b3561bf41ee441d383466fdf8e39f647f48da0caf926d35ca5e6181281dda423d8f9620718f6de248207d56a6d18fe6b4f722d49d8c9def6cdb164588cc8e58da03cc8825b8b6d188aebef416c33ba2e12ddcf7fb8e4ff719c0ffb2d634e8b8fd17b4a74d52cc5f8207dcb93464fc5f4b73ce95dc4181f2feff22fa3abc6987ed2e83cad598ae9910647b9bccbe82da3ef60b1a5fffc7094dee92873a4bd151985435f0acef7441cfa96bde33d5ec575865d6ae9cf13b76c495eb7e2884b3b2cbef4a0ee0a5b7ad0f50f5961370faa1ee441ae1909b9680a0ce32e4e266fce9ecc9d83e31132cd1375aaa1c07a610dffeef628e3b2a5e97a6832997e1080982d1b093bd9b55a1b86ff640f8c5872eb3c91e43f4e9d2747f99b4c24aa6f8f259d637fc84a48c5bf37f3c4367c47c03ec7213ce35fc5b041d7df3b21d8b25eaf5ec37b33579b0b0bdbcf5858224e7c70cf4fa4fb7e22dcbf2c1127f389f4774f849febefde477ff773f4c1fdddebc0cffd7dfa227fb8f7e7ba691947e79c1f0d8cd2e9e7cf5a7307dd7af282c168ca11528a20a3882c538c5072798a11474e97a714a17379b290596ef38471fb656cdc2e4763988c1491830b809efcf0451358644f04ed2066042fa45081d7f5d7f43ff901c1967f7f66a3056520ddf9b57263997df69a902ccb5e4876671965947183b0a590ec4e127569414c21220a9e5b1ab9730a11aa4b5e9e4283d4f5c0a7ce5f6659f6b4e73ac8636ce27aeebade7ce27a544aa9ddac0451bed0e27bfe8c6dbce751bb990cd9fd4240b588dd0f88eef79ef641b91e371196a710c1da7c76b4ebbaacebb4aeebbaebbaaeab3442a59125d77b16790a1139b89de8d5923ffbd0975ce8b3a7cfbf81107fc97d22b0dc1efcd087c0186e22102af9b9f7d92235e33d8b65df9a6559c6bf818c72bf01eee773ef4f4108703fb99ff5bbeb5f4e1018a94c8d78ad6d01032ba00401e5226b910294d7af32a85cf141ac092ab030620951a05c64580ea84e81932b8ea05c6401098a882e584340b9c84160c23f00630a2b5e2817bd9613503909b0a204305411b0065074c1990055c4250accb1f0c5155817414ba2479079ea2c30488207657a82eaef187fbe62901f2f6b90213de6c93610ac5f55a3b2d7d9c10554f6aa46695fda981ed9c8d787a3b43186c798ec6bd07e1b8938f15143f6db673db6bf71ee20e29059ead7c48fc7fe187b38d9a679cabe633dc64c39ce1c3b32d9dad698f06c9f9366da568fe0b88e2733333328e20f71b6e219511019a1ed99a396cf65babbbc7c4e22bdbcf44b167221895e605e606060482e2d3030230b030323820981303030a6ef081818b549d90be8d840d43a3ac9b4df1060e7e74db5cfb2186d9421bbda6be39091ce39c5f058c6e9f63b3c6a1b9d506d72f6f46f0865c86ef6d938c4493a7e9b17b69b099e4743566cc6aeaff1950361e8bccf8644d5855d5a5a5cdc65489614926a584aaa2f4cc8902c2924d5b0a1157186621c304570bfeff648e90c1afc62a2460d7007c7732c71b03b8ecb658d0e00046005772bffa228ae100000d48811451a33421544981792288a2e62cb48b4a2288adb8d503f4c10301542fda60e0aee4dbd7e2f31f0d869fae4d12dabb53bbac3a33e5a86d73f2ad6eee77b9ab83d48df031dc0c2adcfd973200cd97b1e2883e952eefb6655fb8e538159a0ef8930cc02fd4e24f50dfa2dda02e4606bfdb8bfb1d2281e33151f9e253a299816507a5849b2c47ea834e151e96081b9a028610565b9f2734d36c2c892f4370c3f549af05015d5c102734109ca72e5a7d5c48fb80a088b14572b212388af849ab08d23d484a53c43fe6c49b0d47dc451da92904d3ae90c997886df6b8da76ffdf48672e0895d77e09329ed9fb77bce1962e2d567999bbdf7b494b9b5fc5b431bf692e600d23766a8095bfbb217da81adef75fb57e7a4d99d978e65ce659ea9d5e7e7dae9b3c6d03964fac60c8de09429edd7f92ff48df933942972ed7803cf88e6bfd0fd7c8e9bdd97fe897eed1796bee823e3d055e66affc5c860aff6f66a74d478e079b67b2ce9ac3c8363a66e69a2226dcba856e9b773f9371209766bfd2a4f8d1a402187b8b0fef9cb1385bcb01ee86ec30fd7696041e38aa661c5695041411a55501a5664f5d3401a57341a3ab065ad5078847868da130641d118131a998cfe1b63c0716bd2334fa3b185e0528bb5249801a3f7e726d7ff13ab0ecff868ac3a70c95f0549ae33b9fedb58c1d6f5076b60cb4ed59d79aaa9593ac2340d0493e00998a6590eaac016b89aa7518ae7ba3796e093eb60cf0559d77f648148f1d7752f1f0c0f56e3c6badde0a4bfb644db71d2e7a8f138e94f6baaa6fcd44baa2a49dda94d564faec36ef9654328e196953584eb9632b7f25cf7da73fde964398fd3388d97ef37b80f315f9873cef18629654a3fdcba1bc7496ff5ddd1aded38cd12b6692534609a0e82497fd776b6160176903ad2430b92b0cddc6cf0021f569d5167ff66ad36744a07ce11827a4fc039b656df50b1cdccc672b3010fd7fb5bc94eb35a9da36530c4660312b535f5b5c014db4c5005aa463da08ab278c66b84cdd258990f8e934286f8973575fd6b6a9e6a543e2bd6ada954cb2ae4364cf0b232461b943603b14db6c3013edd3013ae9341ce53894bfe3555ea2c79e85231745fb0d8925d2f5bd8b232d0c5c2960c04040808080808482aebc2ab3cd6016c03639b1648cd890ab365e6c4f5274b7cf2deb7b164e1fa6b238f954bfdee97244992244918100d69f5b79189e405bbb0da370ff266e36b61b5f24328b0f3fb6f9cebf80fc4d9872fe3343463923dcd9e34956c8462f9d7ce609aea248dc7a4ff2686fe3222934e83a3b4d1ce12aa4ab1760ad114a2299c64dd32337f562c8f22d10ed6da2eec17c26249eeaec2120478ccc528016042d52f57b84c6730033cf1fb4e6cd4a42213efaba8aa28ef452f2c8f369ca4945d3079c9399151212feccc28cd28fd8981679c131b55b9dfc184aadf456105f8f08c27a0ec299759289b07d73f014cc37ce253ff48c1a40ba88f7b23288f137b25366b76cf7d37f62aa69b15d3df8df5ebd83c63b76298935d935d935d935df3cbbec2960d639bee877148f27335710efafdd65f065ce217c628824b7c11d03798d422b8e4ef210fcff8c3fc63f8af551bc311068b7d3f54697087a6c39cf3e7e9fbe8ffffffd31fcaf8fff7fa3eb0dcad3e7687e8c630a309b67c8e39410441c0f7f9186af569f6f18921e59d01e13d1c4257c8fc6c903cd39f652de220a4823b42f7035d864f08ea9de30c3c799640c0226012709017135ce798d3799b7bced07c2a6a16d75d0831f08c3fc782c4623113cfe090e91c25bed1cf527418e0762cc8ceb6e3eeee5ebd6358ec0b37947ed862c4ec08ddf9eca7ef79e61b6f706f0c230c70085b86b77beeab2b8c76d9806301f1472a35436d4c919776a77e6ec794f8d1aafcffff9b4c24c902125419e670155be400085544e3620928facfafee6920b659c0f55ed9fa965c3d7105f87bee5eb97f9e7eccf99aaff99a2f7e0d619bf9fec4c91e1f01e7e824f8864b098e82b6c38f2043aad8ef961c04430c0dd9d3af211b6bb0ad76f9f8f7ca7fd542f8b265b7c856abd56ab5baf5e3dee1a0b62a1293c562b1582c56b358ff930536cf9cac56ef78e8fbc6927b4dcb3e0a665f4118b2394947b0f46fe62c8decdc0dcce79e8a25c3eef7a5919dcb11117d4e8c8f51072328f0fdc3197cf222b8f3150f564e912941200125b2d77ffb611b866234e6d8d1df3828c6f800dfe55d8ca03c2b76508c8f960e9a2577e119fd68f4951d9de5bd6061bd10c1961e731ff7f1d84b15b6741f9febcfeaa016ca1660f8c6728244640045092191512c506454f81d16b69c574c16abb63c0793b81038b3e46f3291e43fec4b86897f71867409f8d445e0923f126e9614ce84c160305863a971cbc6a2691af91e15fb950bab721da7099aa71eb34aa172458b56f29dacf708c3fbd34a5a89efd430ba73f574bb8603eb7f33bd3d879818ec5c6596a22e425c582b9674b2c5509db38adc45b7acb754036c82259dec1d78ac13e3e9739417022e4f89e2c37d02c5de16fb4e07f80b86c5499f3dd7759c74d5f539291deb9d3a3d8f9011e41e86bbae4bb93ea35ccf71c96979d17b45b0255dc5f48fde084a24c6ccb76f0445c988f908fde8a7e843f4966671d26bcee843df22da9c1c582afe480c99b0a5b358568a97fc6bb5360c3328604e2847f4a3508aed7eba57e7ea7c32203fb5fc774b1a03822d69ac95f9f8e9eb341f6c99b5b29e0da873358cc62a3886df58fe77598b1b4bd3cc6a7c604b2a856da6d028fad4b966c99ffec0fb925ed17d49b9a046985fd2245c20055c9ee2e2e2662db6d1ae7f9604db68efef699d6b9e68b5218d75ae4bde8eb3273c7383d83c6538e892bf675cb0ae58fd401545d6444ccc96540a95e2343ece00d5a35a46f6d293ace5a47f925b3a14ad0596fecf7f0d8461e41ecd4933d1ef038c7045091a2f4f714189b56e3ce6c796dee3b99ab8963069e9cbbd0d07c4362db00d6df9f9d965288a10e27a2d226da13197c7df092c4f71c2078aebcf77b6cf902a6b39e166ad79a231fa04ebf29428539a785def99a7ac35e5092c539ed082092f1e5069dd2996300fa8f05cffec4906e5fad71b121bd5a2df39c305b67a7737d7dd3e335cd06f0158e5e85b12d7a75c9ddee3ae9e96eb6766509c663ed348f1e13902b131e9dfb8f75590c7d318dbcc9fff52862db3d43d128b42a1601b3ad23178c6798097c6aad8d27be81036e634d90f519694c0ca2d6996eb1f4e61cbac15a3e1e61c59915cc2662eefc97ab21614d655b7cc5ad90da8d8d4afbba338a885ec3a0b7cb25cf21771a2eb2c2a4d220eea2141414141414356b40a9b6c3033576631366dd398bc8ec251abb547666d9ab1bcf96da2f695823bc8ab8d96c4699f06ee083d38963517fc4d0c1aa3405fdc50d302d18fecc8be68649aa79691bdc4b930a0e5e432dad08285b1d830862df9f563ecd6dce564bfa6698e849e7eaf9d749a17bb654318b6ac644d3b79bf1fed9c3dd670b29ec0765fd65bb20bfcc41a937d3796360361e0464a5e6feee06ab5fe55e1b14c29aa2c4262d3b29d79dac6d0b5b17687303c58ef96610c77c1906db21d26b6e3f1ce49129c83d4375a6c436194e89ab0cd7c52e710f9860be0fa97309e653bdc4fb7c3137a851d0febab2185d932dbb9e4cececececece8ea85585688f27f364ce7e76d7d1038ac852adb53689867517dd32311b4d0216caebcacfb26c766aa7837a49bbbab395d33815204ed31f7aff21f3f4bdf79fd8bdd775cf898c0299789f79ef39f78fdbff8d9d9a25d52c1920b4028be3a403b13aa6565a06cc5dca921603359525ae5b3614b2d2a64b6c7052b483dd5153a36535e4e660113ab68d0b2f2ca595ab349fb2675b435ed8d9bdc4a585cb75931d992693c96432994cb3566bc3903479405ae59ec57558739aa609c80464b22691c994e32c98e2041254110ec9112811903da144a39398ec996deaf5df78d8660157545f243615d504b96dba66cb96355657eba16375f48ab0a5c75af5b5d0bbd6ad81ccaed1938fc1257fc275a723a7023b369598077dc85b3fb6c67ab223b98da5a93479ec04b6742c589ca6ec18908d309c663ec902e914dbf0e53c86a3c6e4a4af566cd331efc17f15f355cc574042a368ace167c983b0292a4ed6d7365ad671c7fc8c6674344d95c73c88052ef9d728312962269e7999a82cf198c7ea0af496215a4878df9c81674c24a55eefac64e008304dfd2a537daa8f0f57b94a67680301d3642408e225e9a20571b2fb8797806033986cc8740e08902f5421bdc5b15a1f36125c84ad1b906abb1e9b244bf2e6851848b25b9b6b2e77ab63fb19db6f5f026073bd54b1e54f72dac832b16462624bd2a4a1ff9f4dfaf384f779dfd951546bc85a300c59ad3db195393be3c66bf70dee81ccd3e5923f57ed04dab6ea373ce3cf72b1582c1657371d374e368bc57de16962741c8967ba91e425ff26bdbcd4a7d6e285737416ac02db741078c66d98b66df3486ce39486506cbd2549fb9d256c13ea2cf8d42d2eadd8c6a1f889fb2aaf700ceb852bf117dbb4dc530c5cf20f9fb837f12555c71ef124ad049faa0ea0e8b2586e05966729c8f71228d647eedee0241027bdfbfdf224ae013e75165cf2976232954aa2f8027a01f9caab413a822dbb45ca38e3be629f1802cff56f2db0748eed5b6c2d78bc94b02599dd9244ba2114dbdc37ea6b608c270079bd5eafd76b76f7eba5c542477855aed76a6d1872f8ffaaaf202658e699155dad66ac6ae6c9e45c735d3535975f3ffaa7f2ec08c12ed79daef0ce39524ae7970cab4eb0432ac7a71170c99fe346a321b5776265d46aedff1776392d8413ad1fa08a7043f800d57d0e0b4850456850dde8a4ec57c332180cc6fd42f0a261482c8cb9635f10b661ae613ce32f433eacd5629b5e750f5cf20fc3d188d3b8da2037eee8ec5b487663b2f16b62ffc6fc3b0806e4ed8098b0060cd630186c4b85506032916418fef0d6010a123da8229b9420504e9eef9c3d6cb331c1a71ac54ac1548c804fcd44942e81aa97e8257ef8bd310ee8184bd2746c696b845a0eb60cefcac1ef9e9e9e9e9e9e9eeef9e196c0ccfcd99cd99c324c273e5071996ccf6005ec36a69ccc2fe224a65a61a71151485645266a824d71cb031c806dfa3f0a4ea8828721ba584d81722b331882142cc8a2478707943fd7865579534ba9e392d3885c8a1ef43dfc7ff8dfc4e0ca329c9ad512b0eb4488199d94d2e7e197f21cd9b3e1a79a39e2e81b97cbe572dd48d183d591cb3d40422501977b8084756bb5a19b4ca2327edaf5c978cf29d3d3dee69f97ff663215c76f8ae46dd73cb1af9c5c72fd7b2ced6d15cf20615ff3bbc1878441352f30b0ec426e08c2d2f71bf875691028b8a7b8dea2819d545ae5e2c1dd6eb16cdf925f9dba4cea8f97ab5d292f881b6fb2d137841999cd916783cdce4708c3728fc72b244cde1edb354bfe2e977705c8742b3664c5d2efcaf5675850898a0d0244cb461f16a8f5a63a95621b325e681c3f71d9f748f11390590ac34fddc44b2fd2c481e30299960613db414c930126a3300d8e936c03e434f4b9a0bf61d8208e33c0341f93fed958b9cefbc090c9025ce7691f08ce100a6f8f373e7565a968694162475b8b67ecb8b5b656b7cf4929a57466d9ccb21108a574b4a19fb3cc9015ab51fffa9e4f181eec16b262391ea2ff40226c8a95da627fd27c7c2a6b9eb4d72cf1380d578ff84903aa3a308d16e4a46b5160a7d161a5369d4de534b179ea23db6b9e3ae59bcf3c09c9e2c3dd2d36372027fd373098f4df94781f1812d9518bf78121911db5b4930f0c89eca8c5e50343223b6a7189cdd3c6036f6030cd660226ddb7d43c8dc62d082e7916bb1b0db6d4d6e46e48369611b6dc5a2f1fd6dc7ae6c9fee86bb5360cff6bcab4c15e5a74aa553ca3f3b3f9380d07da518b0ba96b6e14e2a47f8e38c449ffbee69620e8ef6e168b63b17e6789930ec383ad3a5b2b64c5965b2b02dc0d812af048147ce6895d7b6940f30426712dc8694015683ee00ea80428fe3cb98b58d00578c6bf0b1b3864e02161c56a9acc01ae7f5883a4851d18b67618665f8421199a9ad6df361ddbb6755ef2d739e2790c322bec7e0ed9c2f66a3b8dbebe08044cfa83de7310274722f5d2fc2adad04bf32d08482f8d989cdafb74ceaf8eb067e8a46b7724f6dcd0db52c8931b1a9bb59af71b716851076963db9e2ff7a4141337f2602a65583f668eadc4c925acc5dd49d243d60843d0412772780a2653b050394510e0c31228a0b42f828012447105aadb7440e568dfe5e05baec0333aae2b802ea0139d6835d07efb0a68bf8d31b302db73a81e4e938ddadb30c8101c2054cac9470f86350ef5ad0a2fa3d16ce4e093058065284c73523add338d9a4ca64dcba8b561ad0d53154dab2a928ac433a42c23b10dfbab48b70718c840a5fa81400c532357d3b1fe53fc2ac7829c3a35735464a8542a958a54a9361d1d3a74b40e2fb98e5ec23f1d59a6836d422f3787f67fbf3e1843437dee6ba8cf8d356c6fbdcb200cd5880f64ecd040e504103f28414d60071a9441043196e860069bc85b0dda73da6fda1341f56f3d56edb79c6d133514394b74fc204370804c066198389c6a85a48a98ab6894852dc330c481c16030180efd81c7e2d839265f54dff0af4ec536c172df58c1b582cbe572b9b21478bd5aad56abd5aa86bcb0b3b30ff96d7412173947f7c0377ce019ff52002fe2f0a7a52b02c54675df77600f909fc0adc2d52a0bbd1fb8a8822a3287806290c9078e3f6649c401324b44b01e03b6e95592663949bf5f6c60afd818acb2ce4193a47bc5334ad8b2e687051cc7ff6b7eac5eabd56a95d1b95a550ff5b49060ee44ce90472925e709038db3040bbb71e1fabf5ae5820b150683c16038ad96cb090e674dcd8fad66c82ca528a5b6fae0f5019ee9e76e8f41b2d8b297e89eb259ada46c9abdb605116b4cd586644d8debca0a7684b8eef95779e64d97bcee7831fe4368f07e7b1fde6fdf3de610e99efb6dfc3e87881322f5b7fec4d4ca7160cb4e1121b27ded3ea6df47fded7dc4f44843fded6ba8a31d6be8c656e164599665db4ac7c94ef5604b86a5623aa6c7981e5b1d47947f039807df740e1d7dc34f3ff35c850e17cd5ec7bf0e6d27732f891320274f605df48debdcf39c522c47a162ddf2af6a75fd65388a1c92dc1ef344c7981f5961cbbfd9983a400cd5ef69a08f1c157a50f4b31ab4dc6bc073ddd94109308a8ee12cf987848d80115e5c44e149e284104674308517ecf860091e44d16117810d884842c9ce1043e8005584162b86c4164e408107148f01860b9820038817f01043bd70821f24769ae04511c040f9fb379d9e28e4be6d21b727a272e592a61a9e8ec5aaa44db47def78bf7df97387c5da444a8e701d27037cb2cc2506e3fab7499bf44e6a04ab7ddb566393216ddb0644c8909c1a1aa3405fb4200486c02fc4c49e460cb03fbf796abce45eaaa9e1a9a9d9b69f53f453247a065b241a6f9822101b703ae5343fc5edc56ed5a47a87c5309cb90571d286f9cd77dee0e43604a7b497e464c9586a4227b7bf53d4ee0eaee2fc849c04c6d73d3dff9082307430eb646a9ff7313b482ffb76d4dced356b415148140245a391e7b5fc376a1965df6735cd6ad68a342d04825c58eebbdf3eb1a4975627bb668e3523efc7c463973bd16a771dd97bd9d7a66eb51880ef82a1eb6533f3ce38db42f445d7a324a53274febc73d26d6e74fbeae5e199b55a1b86ff2469ea3e0e84c1033ff0f340204256644521db8216bcc09060482f307fe340b86ab6b4b86c624ea7fc88d73c4e6a5bd8d2de1a6fb14df6348460d29ffbd20279669a56f2aeaa4aae9397679eeaf414074defba92d08a42e0e73dad65d35ee264bfd33a67af9498bc1ab53ccc935ede69883344204230e9ced19defa31617d20b8c0aa1672f50b02af0388d5faf9bcb14b5dba246ca5ee85421e4be300cbbd32e0d51ac2385d1b1ce89e5bcdd8843dfd253d365345d5c462efe4d0586f473e4c117e6fd1b0643a2898119fdc8cbe8499c147de5227aca55ae936556f494eb38e947445f79921c3ffda0af1e1c6873d67e2ac5c919f3d43e29a01d18cb573ca91df4f6d83dce40409cf41c27fd5f4cb873104f3f9dc15dc21ada735d7d8f137dcca0bdfb6c9eeff9d9fff6dacfeebafbacd3c23b438fda3c5cf2af48d4ef23f8344489eb4f80236ce9a9920f6dd27a32f2e00b857bff1f9ed788258c5a4a622b146fc6ca6629ea295769337ab01b0f37baa774e8ca93ccd0711a1c86e4d44fdd6ddcb66edbbc6dfbb60ddcb6d0b689b6cdb677bbbb15c79cdf9ce09ca1394573da394773b6ccefd1e43faf3672f7d43cdfc7e3739409678c21597978dca58227e08921a610d7df0b819fd771d5fa3f57b7f736f16fe598347935e454d9972a99a6fd601b67544d8f1f14e0d307fc0942000ae2334813967e2cbc592cc8dcd1bc5631fba4429db32ba74205b240e6e9e70ad5b442df6440f0c06cc346e70899fd6f9cce2a93e7e4396dc87cdacf4bb50f4c4a5d87161275db802ce0e48ffeed290f4c93023e851307bbb8895858be3d0ae9cb3ca33d05b8e4ff23f54aa552a95488230c71a452ad1eac90c06038323019980cce3cc9c8c06460323263f5c2fad7b45003ab81d5c06a603670421c30180c06c359b17e7eef958bf269246dc2c793024ae9143bc0b0eff77a5c5dbd2fc3d07454e83d51048a409128257a0b5a11113cadeb7a9cb6698c8f4f8cf1f181a1181fa2075f347a2aa67bf045dd8bbad087c0de8972a7a7fd44719fc7b988ca0a09fda1f3a8642e16253d0bd18c0c0000004314000028100a860322d170402e9935351f14800a7d904278589b4aa35992a3309032c6284318218680c00080c8cc681404b5b220f9cc5e6bd3492d34d766ddd02c8c3e5fa64eb3cd378e510d666d1b230dfe3827b357333cb6b240192658e0e40adc68a8da53dcc0ef3aeb9cd64ac2b925bd57efe8b3df3d2dbc1df83114630bda482d40e9e1b9747963bd8ab829dae53fa6c4e850e6f0e8038763e253f4534041e8b89b2ec538e2c7710d05ac49417048c945506497e0682b7cb2cb558305b36d020dfe0442cd6c589b98d83cb5928168f71069bc42595e49322875ab49e1755a1729a952358eb4391f9102d398bca7f0927416c399688e726de656ae55d349e8083006eaf9fca3fc09962a3cb3c1338ae7a80ac8e6f22c2c4485ce3b26257b273eab4bab644876215b5d49c7543c8589c62e99b76e538bc91cdd84935f78c136983a2e0f0f92645886b678f5c68417adc31aee1de48dc9dfbd002e7bcef52029a641c280b920a09a1550cd8572430c4e32e424f2a4f625cfc67b35627bff09844e73fb5ee50d969db3de64d32a84badd967a3e2295b150359926226f699b81e428533202ccea88d7c44fca73debe9b286d9990cac1909064ae6fa37733ec4edfb9360f483889a74f3f7062785aa73e81c596e60e88c8b01e256836902013d5095ec93e349fe6ce77496ff5902a8a09d68930d87c6750bee58e8d4e62937f7fd114528bc8c2ec13afe011a6b087d7d1a67d4b95dc57498d2ad83a113e2a4b9fc2cf5dab0ea343a9343309990c806b9428539b84df61d5fcc8d6a7576c96c22bfe4520d9d17b8ae171d25c5eca69f7633f2c83c4d4d2dbdee80d06c2b50958f9a948f8a1d488f06105eb7203496cf25394c87591e510a3aec8978d4b0c0ee01e25ee8c6a70ce548642803d21d5d00782cef316e80e3d55ff7f64c4dc40c0e1426a8d4feb5beb29c3fc9a25f817452759269314a45d215ce057716325e084db2414ba0c7019e4a0557cb24649159269c222f36ddd224944e262ce5f1c9d4432653bf413aaf2d49136e2c7458742dc2c92d4a4b04f8fecdea2ec63279ace4149722b51e5a2acee0a2f77030efaf01778e4631ca607337c04fcf1f9bbb595a589c8ce43fc6888a9ed9a6560dec997ff553ef856316160dee4088b4909b5186b7a15a80c7181490fb41f67c480888b974d4a1273388ef516d1c4536b5c9a5c139956d8b579f653ecc71445914fa1ac3a00e7c3e8137f4d50fa6841014a3d84b4764c054306be24302568e644cd1c3b41b30c23fbf3dd4f9c4bd50d5be0c5a0e8646846649f22eea8ca7903779fe88b25ee59c295f5a1fe74e0fb66f85507ecc6e7a8f24fd6beccc5f1ad72388bbd51620d3d0584b8068c2746656dfc0d468df97494768f935143a44efdb5868bda432d40c53bd399b3ca751a7daf4182fa07fa22b74dc2bbc3954d956653a3221c022d66e2427cf1116b4a3542f4d0ad54abe3b0a428d08d783580e265f872ff5415a37cd2c290cf0ab3cbc11dba9f0614cace9a8a6cfc2732b412f8f549490dcc598a78204bd7c5a26db73cc26e2fba11812c67582944b047ced9fc0e53a94022583108b3e6493e77195fb92c049b92c3346e3fa3b10a2ea307ea2acbb89336e2c82f7f90a38218c766726a2b3e56e65f3494365c02100b28bb8c0c40ad5e5c69bebd12d7ce85a5671d7039e71375566f919af789c3c05516778e3629a0100de9d4c0b8eb8bc15aede334edcc4e45dce45a80eed14f8bb0e94411b3c1a9a426b5bcfbc8de7c8205237777fd631ce7f2d493ced6ffc40ed7395f823b74bb58fb866c67a2ccba87e6d20ad94e54ea840493c026442fa60bf244ff9506194cf84d3cb9c66736044027fbc0c2a58cea051a34eb3977ebadbbeb85fbf55c063a4d99a49ad8ef100d32bb6d4c178585e02ec62bc71c472adda82f830c4ea6574786a30b55fc1fa6845e8edef03448ea162239171a5ff2436e1399c22b1b0ce5f3d4a689e1a034023c7bb6c71e67d1919edbc847b0a428f1402e4d85837c5d00d2c6e8330945913a058132eda3231634b829d55d6cc83bfaec8abf06240e6eb71319f4c4edad4c043e5a93925d18bde8e37bd5671ecdd472dfa7f7d9fae404fd2cab4130ae9db3d9a83a6c3b20df635e68d96fc0616306bbfe876bc7f62433b9ba44a1759a6216bbfb677201c3c237605936a03f0b80029e38ec1957166add2984bd5f5f6119db2a9cf1647d10b132b746ad72a313cde214316211d309b0c07168cc0b8a642863b05dff53d18288c33920979e68e12fd252b77c49426e8579ae15e23d660c9a5fc2b8275e72d026a8a667790e8b915c3fea82c56fd4da04782132a871225e13727acf4a14bb26c4dc6559944af29febcb46b0929d1e02df855836fe4e440d7e9675239269554f504b0d4c6edd59cbe989b31dba58dd7d05b3d9cc04874261bc8c6832b66a0d845095245f19f1041b59e8ed39f559d99f35c8b70901297295fda7fb6adefd5db117fe092ef20b0f1e0d74c7633977b691c153178c3f45b46ab2d04d51a390080bf51571d6e611a28a08b230317e47822fdbc2f8e580752eca87dc20416b0918791a6f0343a1caf112c740c43f9614366557f6dcca5a9943157e3a99bb70ca190ac526ace5a055caf112d385674364832b58648682337e17165d2215b2c1af89f4f9144774e1c135399f6eb018c5a6c49fa527af9fdc04b9b37fc9e5f6d6ec6813c334b8ade6fdb9e561efe564685d8851b32676ea0b145fc8694725491e8866e7de41d84e51ab884ecc48b0cb8094665b8689439e51aa1121ced1a13888c2442e823115cfa156b3f854c216171a64e766d3869c96a165fe74872e309c22f94dab696bca927b6a1a2847640a356dac515bfd8dd4e1b4573c3d670e2e98328db213d28594db5bb514b004992e3ab5f54eb324b0fdd74ba9b290eadb5b48140b85feb675ee4e965b9bfe929efe8e587f298dd67ffdfa291f248cbbb0f6049701cf6a64b57248e96540fda2f624513470dc62d05bd7455bec7106eea22ecac93ea14430a3321eb55faddea25b0a2103fa403cb228ad464b25edee9fea2de470da67139ae2c6cc9d9300880f9f5dc815f850348da410f14bc08906395d521fd4e52c486eb64c96cef7e3c38d575c384f09fb2b3361a1b75204e7f5fb464681b24b1c964c7fea2f46048da41751c2ba880dde09a48e886db58fbea804f3da3216a124da816811cf50534b82f3028a881dd0eee825101e39d157c2a2388e298425cfa4acc75bc2358e4f319b4f2ff2922e5b5b874d135efff9a564ec64a63f7b0f5a2b7bd3af6c4d1f51978bc27fff6f0744b587ca7c1cffe999bf6298c9cc8d5254ac1bf008fa2853c743f705fa88c8dc6f81c0028e4cd38cd6131ff14ffb58866cd14e88dde50e09b516e78eb5a7b31a37a90c49d6023d7edc20261524578857a907d139d6797b87bb7dec7e72e4e5154c3919a79c4f9c226ab140bcaf3b83c542b2b307191d187a7af02e1d5c444a680c55c42f61f3dfe5411bde8001c7bb065fc59020ba4e1de2fa0339ec9f7f67499efa0ff46000c3808019f7a04a01b448f198c36e9b8e251e7f3254acb7a7e2ad911d11ec74f225f6404c507766abab8481aa52fbb266ecc9f133b846bcc9427f7f2c9ce0d1c6aaec1fef4068e43525f3dba49189f67ae87229848a03dd352d21b7485a33c70f4243f575937285170b987ad1b826e853fb603a586a9c337b905c5a4446848fb1d43d454dd8270cefe6cbb00f42dc4fd4473f14cc7a5bb301caa190d9f18a95b27d6890d42b89fa4e7cfbb7e7a74d47400c8d9d61a9dd6383ca342ad5991ee506bb646ef2bf107a4d6404d5a0fa79511b4a3142c88a5b0884c626a5181ab542ece0541d39ca979e1a3c61d5fc98c6d2425575f673b1cea9670fa452834891cdd984de0933b2fcbcf20c351cec0340a9bebd639c501bfadfd31a56239f15157562df829a5bbb495708d0e5f9aed5c68a05d19a24540759b76b3c5b40d6e5f3e3b6c10456ed01093abda0189a28f5c2fbfe20fe55753529cc3355c44d67e032dcc8a2755756a32b1b1d6f230b312cc6dd319fc669fe7db9e468be156a14af8f8c4f87a96bc435068bfb4cdaa72fbde0829f3492d353d0e7f9300e69501b7b8a15502b74a1f64fbda140dcb703a8edd30986488fee286a3af049570e173a5be020c674478614c5be9a1ebabbfe2aa49051bff1fd0b3da17d76f501cd46dc95de8f323f6c3ba5795fd32c5d5b9b670b192eab5494c49e1638f93555c22515fcf792ad84f1ed0f51758ca0245ac30521e5821846ba1daba943fcff3f95f732aa65aa812d26d8cf2ed5475106ab5e732ef1bbc8d9cb9fe059a2070e0e34698970bb3291dc03a822f231c92432ec946a76081825f2ed14061e50e8528d5be0e5b0619517e9d9ef95e905d98cc97c24dc84e00bb811368179f898319d2aaf992baa73393ce00fe7c02f7867cb7161214dd761f597f033f1d59aae5309241c8d2250c532b37ea2bbca68eb2b71cb74aaffcd24232bf58919e5b4a247c1c939f534129583c2021a5599218e73aa013d73ea1068169102cb4a9c3247fefd293aeb4815859c1870de770af2ce275e1fecf0160855599892fb51f33fa3118ee08b27839cff185b0051a247a9d35eefa850db992a3dbd38e1024f9daf097926cc088ab191c578a23545c4bb7932b71d1ac33418cd43247640198a2bc905775d290c266dfab2751ba7c0a23703503bd16c2ac4deefd8fdc7d5e4ee4b2bfc147ee8d359b7907cb6a8c471528d2cb4a804a885c54a5975230fa1f25d0f04b150d0c46289e864a41b27ddcb2e1086b3534799f6b0fb2183e0bae2b5dc490e31855d569bb055d2da64f3eef297f7d9736c0b1853acb01e9e31ef7f6b54fe188d43e7fa8e604cbb6bb534175dadbbbb1837a17cd82cb058664756a71e68d9b70db0c4adf7f6bf7d2b22e752fcc0081be352a58402e472b463285d63dd8ab8cecca7f1f5022ef9c5619889dd7bcaf3f869506a855f5632ef2ed7eb7b11987c4a461923723983daec943d648c45ef421a6ac334805c6dac5bc496c83f043705385281f4d0d0047a557087fb74a7c1c18da86dd08131a7a015ccd3405cc01a440728f5713169c5d10778b0abdad986b3ec829aed2eef706daf42f5fda349eac87fe58628655044796c3ebcc215e49d934a7bfd57dabc49f09b1d12f011be0798da76722cf3889b6634d2f8c94f2ef82521d41aadc757f00632a48bc9587c151e2f414ea83c0460aacd23a496795f9a82a62385a87d9132be6c69dbad6eb8398df80f5480a71de1cf099d39e005c46f114910da7ad29b24689854c8f012cbf97c2db7b586a6e38e34e437afcf81a9e05714502b704c9c32dd84ab64fe0d360284f1c080c0a94a3810e0b60a7f049d5f993e9594bc7cf53ee35cd964d318a7f5f3c05e79c604046e82944c368f72b585973e184c8c9401ae15d57bf087ff1d464c9ebdb21f0c02881ee38280d524b22693bae9e902a7dbd414a16a25cabaec532c4fff0d8b6d5e90ab6fb688c902e7381d60e3a8fc49fab03e4080b599407b31de814cc1b9579f7ea7dc4610afa6a628be038608ed464047d0d531d8ec0e8264c17bdd463055467df7b2686ec6e04a8c59027aa86c46a9e5df0482f4323d20e2127d7e6979a550fec363bb19e570aaa6c50668f7cffc3c6a36a344431a5a8eb9abd56bc2dfa77de3801022d20f1e4e732707e796f83cf8f17bb68a825212575a0cbbe13653bd4aad488cc43673534c84de359260bc38ac4ef4cc11a7cd047eb51887044d75a016b5cdf83038f6521aedb681aa0de57bddcbaed920175c1bbaa86b67334124c984286bcd28ace9dd8d85b29f06440dd18dc35233633f439df75def2805dfd0799cf9f87c8d493448157927c073c8a20724d72b426c8824f1b44270e289d45171f126a8bb0e763f97f201fc640953805ca5ca681284a98dd32f10ef3bd62555a1696c45c393aaa0dee02f0ee949969280ce93c4182f2e4db8b8bd51d5a3e9f6f85c34e5086cb640dc7d2c8c439660b3eb3c58ed956d07b000842e5a572c6739db672943b6cafdb7b88e5a4905c24df2de942efae40a9f0c8a368cf1fb2399fcd0b4565e8f9922febf11cac81c3cd302b903eea1c195afac19f40d7f664ee8c1c2b1c467fe34eca5d2f0da2a0d83c02ce69fc166be5b68ae258680e95c8b73cc9d129720ae3e8bac0028bb2072472c3c68884c6053681e515c1722d7f3819a6f88054b91c65d0162a79255c63c95e39472a384a8605648c2d5efe43141073ee5b6ee9594e91fe7ef6a8201d39489bbdabe6ec269d9a4764465b0ccb274984c9e0e3d3682d018d61225ffb4ae14764d8c0c2a518f42a4384b514bc049e54dde2094514c0bcb8522d8aab855aadfb06e4b9f81107acc9456517d22031cd148d449ef204eb4030150641797512a3938b0bc9c9a03727cd0d7b280d46933b2c8e246b508dee97b60fbd0fd94e520ba782bb5144c36795d2cc86cfbe23c0dda84b6853a4313a15fb3ac058acfa771938141253681036325d84b020cc2a186b076df61b30e2564daf21ed95c5de7514252463b3934b4393a1a69e9c95c15db079148e6cafc7dceb86cc49066589653550af01a94ddc3700031578d0f88846eb75c750d791f70c3ab508c6a43084e82f5849132f2480a10bf82b177e5795bb4e23f96a3898caa3cb90a10bf42cfd9a771c18cb7914859db3406096ba12297f729daef7ef68721c860b9e2ac52aea3790ce907910683428cf9edbb902c3d0e5d71ad4936d855299261cd712b7b92c75250e11af43241e55b83dad873736e6aabe521ab418e0bd34c3ef67dc761d0dd544d1d45de63082b24d08cb2d6268f79d8433ca7bcdc97133f54625e6d0dc9f8392d8ffea88833cba64fd450829bbeb0a2e698786f183c4ca4fd4b7e741f41f56a989bfa9129a8eb79ff7fedae0effbe57601dd2dadda6c886a821ca7f498e4c41ab227cca9f3859f37c1fa7a2159301261c14f8e974b70a8a0efd63430cabbb3348875e3181c7359cf3471b9459e3bb1b37d8a8f7e4c6fd397501a53acea5744c712c5e2f89906953341c612447eef4c0e7b59ed9f2038ff8d093aa6e5ec1b96f1cd03de966b3fe3d8a908c69653e251ccbb09f2d3cb77e8b9d05ab38031f8180149c62deab136254137a07451f5690b2197392943fd0f60cb332c780ac63bb741ca6761c9580ecc10d0df4d7ab6f5ee2c29c3249cde8220ba07a440b54e723479f5e8dbc3f00c3dceac46564191c45a5a75c9e54bf8010d96071769392efd718b94653dad26cd21a68bd39d00ec9adeac6d39353753ce773c7bcdf63407096b7b78f11fbf2ba27d1a153f1df68419ad7fb2b9f32fe438aa2cafb18eed9fe9e2fc8a6c46375c128b53483aa8d7219e7b74ce49ca83a9da719358e2e75f43dfa348cf786b7c901d0b068f167974a71ef85f15abab42d9b613d65796fdb3eb45cadd2ab41c15b377612e7c70378db10289730e717231c6aaf4b1e8410161565cfd801fe77ae039f44b6eb4d5609a45a289dd644e4467b40b07a55f4d199c1d3abf5127b0a64db4418c32f52f93018b3338752146524982810eab0b51be033007614d9ca3c92af5dd449c9e978e69556c30c21f092a653508b37768f25b06742bad8b766921621b098b8d5daf815444c31425a38905e54b562568b96edd2d5e054480e787cfc6a011aa83db25833eb2c0d5577f1a4a66d8997311be975b30e56a8d3c86a82ace881e3e68c1ec6f04a27ead00af81e2374eeef410696de52c2e82ce4785b9e9e5cb3a72754acd9045f5e8255f1c5e8d0a128a9b19b477df0d7c6d40ca91b18a51e36f372c871e55faea6c408b45f46413cc397deafb28e6ef299fdca6f55ce7e6eecd5f004e334f97df96fd921ff6b7a5151d10b8d4efc0e512ce957bc56e6c0e7f721ec1d08fe625e85ec3c4d00bd1b8c3dc76757d6f116c33f6d508ef9e89f665c92d3982efab761496dab028b4c90550d72b112b74622fccc8b50401864c1830c2bc9398611c2dbf5b1e8af72bc254aa268e9bff8ce9039af739298ed003322c808ebaf34480d1c13a6c4f03a9f2d3cfb86253aea5c35825174781b7058531441590759e61a0fc7a13db7cd56f53830d9b9a16cb1077f4c8345ae64b316abacf9d1a4ebca4704b720fc7544e38df566db9c74a0579ccd52dadd0a2ba858bcf521f5ade825024fec2dd486942509838fcdd8f49dd9ba15a321f7a49d62d4861e683e31138a674ef1a2901882d1c289a5de8d830a0ecf55023966dcb0fe0378669460f5d5ebb6c9cfa6d810deca5618d5735b2e8e86744af37d0d4585e4f15aa9767be498985b1ace1e24501592ef0d65beb6f2e5e61a7d3983d9e4f82e463352b76520096610502f45f99307ccdbb2f9694d232497ba50088e3e5e76a5e26fc664ab59b1d0f81e49e8d54bf51f0fa1545f833740b4c4518fa86e437a6b9f47c7bed97840d6982a925bf928657b0ad46eeca8c03bb67f633e45a2e91ca9b9d994b47eec72bb13363eb30db9238db70a977494fe8bc4a878ed4e102fed583394986b7ecd648e86572aa081eb0df5d37e658ea4c90b347490d5bd00ddaa3a901f1bbff322b54afb6980063691476a4753f7e8c2cdde97525c398f52354889b712024e12408b64ef2c424826f7578d346e9d29f0c770798d41c1d28d4e8f1dae0c1bfb0e9d026d587021a4855c5dd8301830cfe21b8700e4ddcea802de65f0288a851471e95854ba8b6f43dca659a3ee2d1083c6087d31d3589b1e29af27c79598c4b7619f7ca63cfd034ae346ddd10ba8d43644b181683e8458ef9a7a05113ad8c48dc330e27864857003371aa91b5acf1e79ea87ff4c50b3042b9965ad4837bd8686985ecb24d89ea5e68f8d0f1d5ccc2c46b730fa11e94026d4f5a603b5c0da8c37e565821aca6f19c8ce5981ccdb6a272dcad28045a93475c62d2e270b38aaaa571f647c16bca172bd621f0cd4144b6eb56ce0d655889847767ed4633c2adea0e61d785fa16d01e1516383a71c12bbe08d73c7290cdb727b6af162020f25f105e6ecea13bec5f585bb4223aec4b8929a91382d6a68318b308ad1643a7789c8e3bcc748df1ef9c3d8317771109311395936e469cbfaf00fb0b7b5f495ac5f25eaba9af7052138e2cc04b5da6c667b222cfd931b6b8ac79b337d028a56e76e93c440f6416e2c93c8d536e6a4e9ee27c7410559964a9d480f5c693bbcad3201e4773b6a13714f253a6c4b3cd9afb493bbe68bfb615bba417313b151890a33372dfe06483875c0066cc7ce778097c66f372476f51483f1a827470654122db922082f6e92d339c60ad357b33ff03a35d4e5e4b006262217719798d30b2906ff1c7d335f5d5e19e7551d0f4c5a79bc3c24edbc116996221f5c4847b43cc03fc798c125c7229e656e974bd48376009458b08392a0de25a82615299be37f4304071f9076e375a597c8f05a1a4545c675b1f9aa8547d47c6d4a5807565c828c409d23fff6e94610835fc19b2fd3a4e6f9b6d3675b9bec26627819421ec1420d437834fdfa3660df53e674aba00d2b7e9567be14829d9013ae48b0aa56d60b15034aca8fa33987dd0eb878c941235993aa13741f3aafe5e1814c2bc63a510e3730fdbf641811359760d6fc1f67693cb0856df356bfc40f030927808576943851220516a2e89525b11adc67ab465c455a490e6defb0f3d8753e108ecc664213da2edb457251c42689f19c50c84b7d2e379309e66a793aa89d67e480de06daaec01f4f8a1444d8ce8a57f7691cd03af6d82c9473245230cb797540745ec5f9dd8ce1f4a0cfa04b8f093c8291314b098a88f4ef8ef3e98700fefd639ac1d97e49b17cc68c9472f6a4bba9de5d510966110fb0d464e5e06c0b27ca6d90910d20048bd3e123b9b69327733f14fc5ace278204696197bb5e0d4d4346cffea93356749be7b69e81f6f5000316efd449942c7da8f5ef2eccd4c42dcc49e99cbc9ac8512d32f70313ba53dfe32c760104baa3c735ec398dd6cdab46938e0afd36c09200f8109f6397b631892339d64f5a3d202dfbad638ecd2ab80e586f485792d4b3119533ededc65c5b24f0711afbd7154ab6f87d86437eef0342e58998a6ed48d5de90ec4aa054e92a18f0161aab97e89d9c135b2c3362cb55f194f0e6708d90b36d12b40c7358b7734e1929125f0567029fb2243bc1b0ce75657206815e31ca62c4635abc1a3917e51c934117e1be88bae0b41dad6e0553b77de79754db39496f12f71ceb7dca23e3e2f74704ac21cdd265f36d83453e705cb5298f4db5694960a48e6df389bb49fcbb9da40d36f43bf53e43eb6e77a0841b9e7a952dd8fecac89ce94b519da14393ccd9c10c107ab626f86d1403c56ef2633ed02e5f527be7c3c7af9d63417b065800aeed4cd6f63bd307b0525743d410666cf8f7ce6b2cc3271f04457644a26cc173c983f69944d03538d662e6d84325c5ca78a881a00f338a6d9287e4422682319c3d2dc38eeb2e37a42177789c9979ac762c1000ca2f1da35e466a73ca5e4928d3f15d83cab21eb80e89e1f330f5ef9b97916064a4d2e32bf8004d40aba154e408499ea528b6f897d1366f566f919077f00aeb712de4f8dd6ac3e2ab7b80d657776db871a979ed4860b357cc02718f1e52002b2088c28fa23193f509e2cd8ad06c41f1a0aa2030667868ca91aa94d9a19997b75d282e38c217e860ad8e0fdb5cb2bdaa56e9bbfe77943f9f30efc8b54ec34ab46bfb2fbf5c84d0d2f62688d84bc99f1639afaee5debef25ab74f7e4bb6448362ea59f3bf249c21b06e7f49f4a937b8a236b4458fe87d9492ae0e2935b45274ac00384758f49a9dc29446641804637145094ee13659cbb623fdb7b2316290e19649f12ac947999f19de34f4caac7f8a86c6d93ef95dd52188280d24e7f61e238399abc6ea75e3aa58dcdc9268838a07419b848d06425f3f5c18b3a6618602a6f49bdf1f3c30d999fdc7ded6d3fd65a74c5dad82196249e85b187d6ea1a58244c5fd3ad76cf7052243f595067d3847b5f6b38106fdfe3ce95f3e2fc5101b46c6e7b98407afa87f94f2c1838253c920adf96f55e1a257671811c6e18752756eeededd804638f45d551fccaaa2c9c049d98878bd3a842d1f68248a49c82dc760e90d5b8c3c888f9bbc6c4a68f1a0d406cbb3ab3314abfec1c6e2e0a4dc90e1cde9688018b3a2100bb6182555f4fdc1856b45d378aaec4be917989f65e9894be3d0878a2a8be32f71efbf2a072205e95793edef304a75a24c6ab6ec1900e509dd49ca7a828865b7e2d6ca091102dfd8886358a42f731e368698f8132059ded9668683de359946a3784f99ffa88cd4a7b4467eefba92f9b829ad934b974b41661a4539f19874509aa21b9e0e53c49f13f775b4d65015e91401e7f5023ca8cff4ae7c2e48b0f3603b26b692e8c357c6cd2f4120b388c40a9e2edf496d872aa14c75aac6c0401926c584e73d760bcbe13296fe49f47c0b9ff38d8b2d8baa69191e68ee0b1fa49f4fcf6e8492581af2090d610d9a2ce9d74634eece9fdea7db0edb9ba855581af035711f1ad5aa675ec070e1930594fd473093445e2e39ef090f30161a1b953f4bf70c17d690a9be5a9a3b2e6c94542d6f285b3e8f4b04604130a6a2cfb9b01827b1ccfe20a6df8779861afd355d532ea99e77bdece500893956ac257358023473e2ee2f2ba23955daeaa8ab3909723371a3f4525aba6fd5821da562edb713898419cb1ee4bb2ddd56d0efd05a04adfaa9780a0aa4d54e6a06b6f1d37b21ec094270185b585af17e832768d741f80592fcdf82276ed40b900003b7d8d69893290f32a2eafd495409af8aa3c28f932a4ad3992ac68e862c3ed8757baaf9d0895bab16a05c55faa752597b6b464340cb3f60c8bb09c74bb57534a266359cf9ca9ffecf2cff9cc7ad976c0a915c02b8e27e2cc7964df3cfd606124e29094dc91b7cc3754768db1e9686ccec4a1278797c2232fc58d990cfc64ed7f67cea084c80482d26ce2d68ee8a89cbdafda9d29092dcf65d5a0d1c5cd1352581eb5085cb53c27b427bfa8ac4f9b98f46d18d8b872b18744844e573850d12240da01477e4642e448d92413c670ae8a4a3f4d8c26e349752b46a7019f3a47d56ea0ac6a6d7b02ae33a0bd8df4ecf19d363a0f83b28ca6ab4195d215fbb35fb44a1cfd736efa4d164aeaa3d3930c5786967753c662eb4f264aeda54019e345d27485726413e812b93a0a3228853979dd9565c9457b08a8cb62e4512495ab044c89ba627b22e6b5b02746b96973346f7d03a21ccb2cfde7570d8111ed27f22fe5fade88cd256aab2bcd2c1329a0778b324b931d9b03fbd9a41ec161b860e82859943e64b28e800b28db506aba65c6387c9162702653e47b3b81653a403f05502dfb4bbdce7be4984bb1d3450326b94770dadec5dbfba5fe18097d7d1e850441b65bf7302ce9cd6b44e7210dfaf88a87eec6193b6431d3670f9dcaeebdcf4a713019430d338244abe3509432dd97d41862940c0c624ccb785f0b2424ddf59d203216ece1de9139c111d5bd3f90532ac23c8e134fd6cfaa2461eb8b21a178a1ea9c87ab905e981f90128c9bab4a4c1c40251fda8143b5f35d91facd50c4481ff3ac37d205d943c5cb8275a15d08076cd972ca228246d8c59257f8ea5b933a05e09ef06e62f108f296848cca9d82b66d733541c1768a5f21dba2dfdfe1b400b5b4efb94e15b1a255923db020c17cee8a8db3b800920cd933f03613de04d33ef5cc1e4ca88e63a6142dbff2c5da9bf3c340302a8f36b4c7521f13c910b2c6a0330b6638b4ce235bbc417ee7be721ba19876387b78ad9a1c9b44aff526d9c965860b404e46add946e6ad1df54845662db207a7fcf121799f2a967160c092036cf1dc73f45eb207a4c12403cff8e66ff4851e449872ff2fdddcaf84f9a7488ca34bdabc770055a34f1f0dce637f39441bdc7f728f0159e72c4a6287bf47ce2f572ec371d04ecfe133f4e1c7b02969b6962843468d012d539b6b51ccebd58db2f342f1b8f44d644a9ab3668c383f8ef4a45b4f67ce006254bd79537fd634978d30a6bb4c33a6839f56301521b05ca2a6d4e7063eb1c2d4ff822c0b4826f4c64f058010402eb571b7f80995ce52348be374f0721f206e39301cd30ef089733f0ab146ca959e9a555d17af546a3326a4c20123554af6f9cabfc0efb8f00408276d5f9f0d4b56a8aa019cd716891f47270d61f5c87e0bc63a036f36d7df1a18a962808678e0ce3a5fb0eb74845ff52950eaae3d5606663d24477e4096375ae12a05d21c6f79244275ada5174af269c7f57f97eed1c66d9be2fc3a6d8aff60e2c5ce3bbb445809847d2d9a99fe85e9cca4f42b487629fe7cfa6e82d10891c538ec3b8027769eac83fa75bb5a479b19d7fa401e7113fa938cfe4f675c8afaadbc9a69ddbcc9e874fc9cafb196c5e3311eacca2eb2bf328e228797ddef29273be545903003ade81e59fbcf2a11d81921024743f301eae1e90650bd8c3331f093f8a375cea55ee8bcfd0e788b85a25fb5e500ab64def3cd92b7f07be1f445ac683190a3002d9212953c8802078455d43ecdd5b0e6e0aa7882c1269184078f8328be6d485351d5502cce6fbd412886548010c32b43f47de36f9a5c2f0d281b2b6bf25b2e7014c60e7a5995eaf96855106f697a04d5ea492d8e87b911e33b9730f2d6dd9c7c44b18d2daf2cce4cbd6d2eeb11f03cf99b153a1d2552eade32298672658f5e8c12ccf50dd36957f0c09668c8c7ec03c56dd5885af5093ad69462fe7dd32f2417d1cd4a121b1b9c010fd7ca4db3d156739afc5956b0b5771e56c59fd67a635c773e4af3c4191f2f0d70eb98c454c86dadaafa997719ac723f6f65865835205752593adc8aea7326c71e4ca16461c8641992c3cb3274109cf515d99cd9cda3d065ff07ffd10ecf589a13ae6aa6003eef4e74070e3f0a885b2ea132cc8383a6f6784a4ed0e912c6b41b095000e2c8388f9670b5254fba4231f0e3f616ed2ca4d4559ebeda285f9f2c66063c27c2930c43105847b22c29000f43303eea39a5f12da726441509837cc9e26ff7e3716cb9117539e7195a7302bcf1b93482357de45b4f77a793c5349ad454e15ec6bee91b6c1700c5860ee778d16cd7a3af5b8a36e9768790440b1615f05a8991271d5d57ef1301464480fafeeab943d7d31582383945f422c2eb0d5e8adb477aab441077b34761da4b9cd730c08a2cd457dcd7cbe2052e439c17e8b41de898b335a9b0aa9e31a174cd9e15c5f091b3d02d4978ff90ab765d26780ab00fe4cc587f5edbf85b067be6759b8ff3212d56d3980616218db8154d06a1d4a163f461919026f3d742b1dcfa703d456f2fa34cc645b6810da965a76f16af533ba70ab78efd07f300147684df6cec0c79c56234794592d3e806fa1ac675f1387d5277820ebf07608eb29e360857391cd83c77682ad8e828017c425d916843aa0da209371b8299ba1f294e6d4825fe46080b730fb86cc060d3cc2c42418c8551241dc39b602354a1202b2df473a126a3046974c25cf8d05836823b3e9c4d843360fdc5fd5ef25cd87062b2b92b0e37674a5d635ff3e58db1024bac7a9ef7c5708ed0ce3d9846e5d1a089f8c31c859f5e656c892f212f416b4ce15eb956dad2a0660b485c43a854f9be623436971defdbf7331d571ff7cc815930b2c776892680cb1d90910c1c444410b0d8409f5725aef7c4d1268c89b6728f01f4648b73a3ad2c4a2c1930251ab8d5a12fd65d5673d05501b7112937e366ffcd9406435d19b6d4b6c7d592df665b0f2836be222b079e4582363586fe9db8dbed08a074c8fc9ff02478274a1a812489c359cde6df5e97481ac0a90500c3032b89027f2861b13ca1278ef10f9e602d40c8bfc57bdacda053db10dd2c6b62e2954857b1cbcb9829952a2e6b00628426f53b22b041f615726f82ac670598cad4d04ca803f4575e9c9fe6dffa8c220f9e20364d90de67eaf407eea6ca4f13cdb48240c9f1702e0a30a455338cbc068666f80f03156a179a98cf65529647cdf26a6e068ff2dae76278c8c85f56d619556ae934ac2cd92385fb5e4f4620d57ba662d1ece5cc2c30209b1e94a07c2239cab633f42aadb40d1b848bb26fef282cfa7ef57f76c61c880cef7a0168f351c8ccd8b425ce616b50791fa2da1760cd452bb59c2c60c1fc5845cb1f1b5e8faa7b0f8c67070fd582d2d2bdff8df6d6421c9d2fb564ca822a01dbe538545392f87ab8cf2ad3be1815efa8f41b58337f51ad1c67d1306b3d01fcbbad76aeaf0b3d9848d9f545a6c6827b4e22a005a20f097eba06089100ef949d17a36f7eb239ee40cb1a35780ee39b5c53e426173e8f2dbc3babfd008e7914f6cd1e54498e148187bf25b012d5c7f0eb2090ddb951f8f36a01a1f09537068a4d072ddf371ee6684d31b22c3f13469ba509518fa3a99954ac55772eca5cbeb22ad7e321fe96b09441a617cb61c157b2e1cc80f67ee1efeb616b2daecaab9707e5fad23dad6dd18856c012bceff01480b7934153f122b6ad4579ee71a362bba2cdf82509a8db4fd67ae36f8023d5b03a7a91efab38b081c375f59220a60c3a39461984f0502a7214cea89852ab33aace085e11a185665ee8e618ab3e7984d722faa67489f79a651a0fbff8035ab8a79645d737f3224104f7b1519133832721741c08b35622e63aeadc50361a8709df3006598259007d577a636566ff766be70844dd47eedbdf6cc0d7269c9e0d9502ff8f486142465ab72a4d7b5f6816eae59d8e33c4517f098551a52b63dbd556cf67ed7c1109a0a54cea89f697a9fefddd08b0f4e0333e6ac79e896ac1c195d45a7ac4f4e4d2681e2317f480ab471367a5995bb6fc6d19610c72d7ef0755e32139d8d8736be9df7997affa1e5afc5728d20fa9c9ca6409ba9587a60d099871cae703def82cd99409866ebc37d9c1b8f510db729f19a3f358478a3eec285ee8cce3faf2af48a285af253458ce95b8f0b9ebdf4d5b8b5d387b07fc6a6ed1f02d4c622206bdea0ecdc5a8b11ca0e7d0165873e303493eb9a6c7da2f3fec31409b6dc5f08641a42b53705bc82cdb06562e73c67573ea413656881620d1414647e1a2216aaf8dd1753b9742c6402751b5e26edfaa90525571f53d738b6951ca5d64530a0f22c3d1b6be0de0d6d4397b8766ba615114c4eeede03bad671feb6b86821d545575e18289d4123cd89faa803667809ccc74449f1e18b089a826379b784e21046f081e182caee05c653225b61f3e0f9abb6afa3581f696d7c0c7d046b3c0182dcc8688acc81c36826c98d534c754aeaccb0e3142fc2a890a36892b17d782044b9471917c90dd3a8fccc0b1cdea722debd1af90c6cdc1735f813fbd429dd75af8bdd77bb6e1ab7ac6ae786bad7027c8f743206e83bbf5060e2aaf8f4989a8a9b648bf0ca68e74695fc0606b276e47d5ffb2da06a6c31e2f7855f92e853c83673cc4c067679f25ad838f2ed690bd63a9f4bafc42006031e8c842475cfe711667f3bed9b1d85b3ddfc9dbd3ed2bb8afa1ecbc7e6c9cfe342ac5086d716572e35280649624866c4faff2692a67ebfeb9764425f8656e4f9bd17c60277486649c08289bc5f8dbe5bcaf443c9e7f8db8e31d9d294ada1290e5345ec3f18af0b598bc155d3f3b2ec75e8833afdda490e9f947e11939a5e122c301bbe0663cb5105a52a570ce08ab1ce5855424a08c0882aba1706473c44c48575d5605f848780813a23045f57a103c069d4d12e39aba7e6b0a9c02a652b6797a060acc2f8850e30b960dbf0b4b438304ab6fa7e0dcc59133390d8cc91f478fcd63ec196aa25a157e41dc1a710298b2a708966b0584e28d1ae1c280349a22c66b2430fbbd6557e233f989614bc8fb79ce5077aed9d804a1a8e7a387fc23d4aa81fb1ce117649371c91704786aa6a65e0878abf9bdaca5d43e20a6e7f01dbd8c8bce09437ee9fafd899461568145e2c37c7a43ac32e2486ba0f138a9670aed744c135d3687ef09357bdd1044457bd7ac52baf13c77c9d6baf7cfd55aebce6a297c692f6e65d77a1cb6e77dd9dae4237bbed6ad75deea68bed527537b5120596f9076d332cb515d877337d1c147ed27f9cec77aeeaed1d1bfce072a585ce39287ecece856464dfe73442df7c1b24a8df7cb7f48268cd9f030fc6005c36eb98810d712b000a3e0b26aab62a81c5f8f4eea2549554ceb9bd7e40b84825d6c552bade7293b768d0dae1f2639a9e9595418470f9cd4e84cb07e7b8af4def71238d46d58690beb5cf1c76134c892004b2ff6c1ef28f60570e5d0e30c4b5831f2104f6eb74ec292e0abe6ad26f2ccd0f76fd2e63e8f1f599a8ad5e9998d6b722fdc26ffc7ae81281a8210ebdfb33ddf2221ea6c2e689c73735c1aee759360386deb945068d65fe2ab3e8a806e3063618a791732c55c8b25e85e8b2281d85c494a34db4ea169249a93eee6ec2900f0a837401b935c4ce0cdafe0c99557ac103404fd07ac0b6f6ac073365234156b0cc3fbb051cfa4b7166caf34a2f0754b433ebb1b364a1cf77862ecd1a2ba063b8cb5b19ba21490daca4391c83d44099751d69ce7c30308acee3341f48f87680cc4c8a4fb84a263fd68e79d65d1f8d0a24021a5c643eb861bbca481475dc1634d4b003f121d4ab539e3a68e61ad53671c21d70a6ed7b75b298ea1617c40ba905cc95826fc6a95c43018d8562974a77c46b1ca33564950bf5dda91de16a40482db2568473973dc568ea8e2748dc4aca8536e80cab8241509e224550f7ecee1b67dd005087aac127194905e822f414da0c7581327fa65906674a9853c2cf2f19b517f5809efce85cc3dd150e5a17660f262dd4e43490ea69f19bea1a5606bf83e7a443a27e1eba1dd129b030f059b380830550c3798e6fa48c46bc99ae39623819129df09a61839ac945b452a81cbc79cb6a4860ae396c39bace4a4398a6108f2333ee04142ed46e59badc6b327d2ddba06220472b7028f308f4e10752d6c1e73fe3a500e11d77e56f2bb50198b35fe1c2f02cc599757a4788a3d2e99d1766b383fc9537c8771c963c12fa85fcaa6099358190aedfb6be394f33d0d352864aeb2e3a17b0fbae9131b48980d5608c22a4b7934cf5d33515caf807081884817013538803a45e7fda4d18587b69996c088ca11541d0d449604f521b767569b0f349bc2c84e2197f93512f671a5dee23f3b86de1ff4cd99624badee75c9d3451e444e14a51fe1e051beef3744edd0e0828beedc371a537cf9406c518b38ac5556071ec6907adb9848885479b4bb354098c07da6d0dfaeff973b528638c7f1a2740f14d06562f8b7ca085bbf373f54e957b956f56ab6ea7029b09dbf0d6c23e21fc05c4ca06401379851700017158edb9afa0ef507879d5109b7e65f0ddbe2948ec12fc6da51018637f00c1c526567d71a0874a5e5239371aca33ff6d1d001b1100ee19f2d8b03b53975e5fdc3dc5360ab3aa7a376ecd0cad9acd735da2562f140fa53bde46763c0bef5c7def1fbff28a9321fe6ef8384e37d0270347f0447c8a9a68c07b606911ed1f284921667fb220b3a6f7b50cfa18b4684b162d373726dd8320e3842709447a0542bec52e9c9255a77e57b9b30204de231f7a77f0183449a899a4290c82afad7199adea6ebbf6813612b74dab82d0b5027a9d8ded3fad20584e69f584271cc1c2d789b6e636adbf7cb780e3615dab7f9b6b245c7bd5ceff621c27bb715b37484fd71545132317a6374871a58d4c2afa5e71f99ae400e1084e995d383ab043037568c5896bdea1a29a4a15f325bb77a3c3ac8b1335715624b6bbe4ce09c2d1052f7f1669ab87809350c22f770d351f4c2531aa333d4620e7fa696872c2af81a426cab900544e129a5bd93df614ffdaea2a460c5654373bf57ee01b5a103cd86ca25433d0555cbc0e710031288d88d5f8dabfb31b07469cb691cd08dbf96be15b7cc7a73e4ea20b3f9dfbcd7701266c492dae64ac3f7121b23116a3156d0d81054be6f42289511992717198cd343479d44416485a0420cf522a38a58ba4a5f625ecfa5364eeeb4f3cb4ad7291a2ce96eda08c74005b71972bfc766e58b8521ff81e0c2f454f14e378ba4c061e2b58f8f4ce5f9b7ee127c567d9187468b6dc874f5acba749a807234b774473c238270f933819c447e915dd5694ab2ae0febd9ecc3f2bd98392700d9f7b26bd401e2a9d215880f28a6b29ee27737d2647399c1a011e10ba66aeee5b5f0f10eb9e87e4bece337119c961b1d73400671f47133060110b77631d43f8844333c30190062d4aae812e8c4f2703ca9fead86210e56dbf9637c63ee9a27ff18aa1946ffc48d5e4b545d739a1dfa66898c436e8711488c738aef4d1ef8c0da652fc7d9ab8010ce88bab2ccbe656382372251db130be9594c5cdaeaea66b4e8afcb84fe10e3de93203c1f4e9d8da73d06635702acf94eead99c745a9e8408b65b259e33579c1791c2686b76cdea8d7ec0f58da99bdbd743ee1b17f7e384c0f8f5538475cb0d8f4a076840abfeec9b306e917b86f85f7f844021476d496a9e4e754c010449a65ec335a3a26f48aa4b59cb2ef4336f270f56a07d62cf6ae7dae8b39b173955a0da2aa69c143a6313fa2d78ac77c0a141f28ea20acc1b16ff469f9b70954bcdcb25788c26c17807987a56b786d5347a54f855dafdd609182364bd7fa21d65af0df75bee0b31e547d007b821cd6a0266ff02da877322e370f379b14fe0fc8f638812e4bb02268fb17af4cbb1d9139442ddeab4d771eb90530f63ae1d68caafe927dad7c1b072a415087d99c7c0e83d2869ae58cdbcaa396c2f961a75db28d2874117b87a9a3ec035d6a28fc19ffe62c97e37a6056adc8e0437101490c678b5980e0df6ce1053c0006c6656e7995d7871d4f3bfad48dfc6e8e1d343961b3b3ac24543bc9ac84df0e482b233e6289a77bb3d711199351e814094dbbabfca34fc3823c9d62f853318b8f3dc4bda2c099843e0b7135500d2023b8b2e978cd7c25e3c32a7ba03e98a987effd21af6c8ecb6ba4cdd7885a69dff224115fca985f099626f4cca8e79e361b23dcbc6605427669645a942be1b0b1dbadd05be990c1b765740f462ceddc13509002c4027d31145709cf027a34ef722396f759352eb156f9ad5117c8e405e6eae8d18264dd40a5f2371f3b227a10e8053156a77d1f00fcc722709c868e6e0dc0f8e8e71a7d836e07a5d7962653a6a448152c470ee9987a99c846fea95a91270e16c5c845ae8cbe52c36036797578e7d187559455c24a9cd3293e88d187f5803870190dc07d0e50520d19a925ba20ffce9b944a6a1ae7fe2926b47c07f5394360c00d15df0df76c460b4bf7fe43e5e305ea26551196503a8d0784b8088a85cf9b7122a1be2c6b4d6b8aac6278121bcb6df67433bb8082486767328026f8d63f14432c8b9d115b229b046467776cc0e96d8593a4f0996da2dd44a3d243cdbc9ba74d0893d6124bb07f59dfaf9b20773325a214ca8efa59231f877de9de1c59b23685ab9c2004487be2caa590f65134ed9a4146e5c0ace8b1b27fd3d878fd076224f76e8c9103278c5cea5e9be340562d778d83d292c932ad6a7610afbd0fda0fdefeb6fa40774d4bce56a84c023961c3b162e5a6f7d782dd296b6ac8136551dc82c37a6948291e2910822f490c09ca8f8cda08c9089898cc00f62d2f0e8945bea351db5da1fe41c4b4e4949e486a4eb18c1e121c38f18630f447301d61e031b5799f1b6b2225b9c125fca7d5c4fe5ec015f0aa06b9fff76a6c0994986927eb9618370abbfd8644bc299df8a56249f290fc7de633187ed07004d7ab92fe8f09b9a28076aa0a73757448586731066c2df202cefb610141fac16bd0cdf1d946284113464d5d6d5bd06b94a12b7999a7254f593e88b4e7bc44d88936def40acd80beedeede25aa815656f340cfb0c092393362cdc23ddebd5478a5d14be8db0b85bdc414e917fd2520fe6a0c0ac02f2142c8446a06539ede84fb50302b951422c7786dd36206b32648004da3ae15d8b7b16682b7eb13fe4a4941554444f78e837afbf6c443436f290761930cefa64dad78798bb1ca3bef54dffb178954819fdf1741e4f53cdfd89c698e3a31c610f89dae29092c8fce941bd21b1ffd17fa833b263bf262d985777bef2b8f7c98c22881f8451b8ac9df03cff692570d7644dc250b4aa0dcf1496755dde8359ee17153309ea60d9d102b1c7d74b16b2651d264f0a3b775a90193266f62c4f14b1a31034ef41d963c736df4b00123ccf8fc008b262cee2317935ef4141c60287777341da011d5bf2ba2d23e2b3dc69942a2790085d5fa65116e885496502214365f126678f1e96d903b9bea8710cab4c0ac64885df61de06e700d507a7d2f22f865e062a590e5e078581a01ae0f58a3937a78611815a6da8cc1de712ec8f51864981d52612ab48413c62cdc885aec5b71a3931b48eefd9d708cb747a4fe35b3a0aedb3e9d7b26a9f1484a47a213867f15157e2403d41d90a3a8ff39e3ab738a40617a164e4bd71b1c3a75c32dbaec4717db7febe1653bb5d90c335f0bb82fc81add0c1fe613974446ce24bd48407f4cef23a60063e19778b25cd3be0af5393a298326c5b12c2a4b7935d50589736b494582fb786fd62316f6b7bba09294bae4a4b8224bf4102eb067e5595f095566c18b94932aa60638ae5a299d8ba8948c71702a356dfca6319d6787ab9ff9db2d196480bcf7483e6357d8776ce700b045f69a3b83c7690c54bc45931ad9142a9f500838c50828dbfba4829f5cb96816d9d38cb851625c397352ac666f2cdf688bb1eede289ace80d276eb225af4f4052a29467f7a24b77106fb2154fb71da3ac694ac3bc46906e54a7f695fd341297e586e5053143ae22600e7a82cfd11a7d7facaf1c23e2dc69544eec7f0e511f7139abed2f6d7d1f7fcbea18497f9484f5e0a38cc2346fafbf033ed286707bc5cc97a621ea77e3406f53a331fd4fd5eb643ec60df558eae1fe4de08adff937978c3bfa4d2f20a4156d952be9ab4f51e9719e9b33d34f2061d3e2f956255b4b0643851162494906402dd17b2c04d68e7ce5a65425914797640ed91e6e3d8d7aba0f4915fa4a355572013fa18abad8f70fd8645c27f86ac80b8ba80331e79f9e1901678656590c93096d635c4d64e09e906ad87a1d7c5a52d031d15599f01b8230f4737edb7f2623ae22047fd8c9074e9b9b8a8d9aae7538e6ecf1bb772545c8cd582f76994737d691ec8372e2d8e755faabf95d01566d6521b1c4001813a1ab0c46237373a21bd0f1236f64fdfd28e5bc23dc404d993227c1f98c202d34ff83526de75088dba4de2a58d1a430a089d0a745a0fbac706f41c9e9793f78b8fb72ef8903927fd72404a82a485483f00a60a78a58f8ff68f798c6a3bcf834204d7f2736bb1a8ed93740136929c01c7b789b867bd82fc98f98a6ac2a3d55ed0f79cf8ca360def2e3d80af00dbd51779ed732fcbd7a8111b03b4ee54e62e55a02e0e5724b8aae7e77d33b45a3d5f9d8aff55c988f66850c27898c244a96dade006d7023e2b4d03154cc7474b206bcd7436619a9fa207788bca8e7135b9918960fa77862db313d6b5df4ecc4a266a6d94e1bc08b8bd534e71cce7a9b96f63de77fcc5d03b177173f37d253bff66292b1f56a696ec3ebb9033ce26c7a5d2c6bd21f9460064bb072e353faad7f744275c230602f3dcaae68f8db2c7cc4c2bf89d486a01e5c51c260019d4da82cfc490f5e785eade26108d2d1b556156882370157483ef52011a157cdac2362ee5bf03ab90ecfecb1604a61bd72f97806eb811c1b6e18613b23548d57098079c3cef0bddac758303b7cdc899a4846b68bc4966037ad4282c74a4fe9bcb558f6c19a662202275dfed490b128b8eb1e261800a65fd32b34a42daee25859a1128a16cf86c9d1cf7fe985f2e130e7083f8b980fc1085d6e25971d84adbfbd179f5380e8a008326f9518866828f081e870ea8c2959051c59d9d2a18c09a5512783089f4f217cecb172b12bc4a6f0d52e44998c2162933044d5c9990226567513e2ae98e73f7de67a951ca41ea3fa2b9f56756c4c7f26e65f6f3eb7c9a20a261a8f3109bd858d012f94bc97c83c5adbae4a60a8623e91ed53b3fa2656c787abbb85240adecc25864256857742949a14ee16573de3287bc82a0b7afa77927aa5dfbb0b24d7f69e13fefd474bfc24a36ab909852a79d22e814b3b1b08c7c2877e6e827640a2f04bb516a68334ea184a91ed2ee04ff2220ea692a3b0ff63327800d2d2e2a8e4b04e60fed3e8be0982a0f8e5d9691b8530c1bec839063908bbd72bdfee210963e6f6d23dbb79cc95804922ede2c4083457fb4a490a9a494281257639a7b03926d4a25d8a92750ad6e0801427073545729249c0883f1d5820ff4609196d804533261b060b972c326e480011b4f2293e1eb847b41253e301b9aad19e34e448bee8edc23a9a054e9fb3f5f72e1d8ab2eafcf2c094cefced2a901d55f8b45cb225627c56787a487d435eeedb3b804d2da19371533fb831aac0f7a7bedd93d875c38bf8f6538da0d0eba65c784184d2adf2bee7aa7ae4455875e6f9c252956f195b848bd5982c58fb30da67e4ad2bbb7e7ca1e6f3a465c4e676a2b93c1085f8a80132bc7b6f7723dcbe239e72dc4fa5862425c7b12a86f53f5b0772614b08c4ebe9ab517cb5df3e0662c1193c91e9666cc3a6b9148b01db6bab5c36690f081fb48a552bd61ab73fe65d4c6192674f08ea8c764c4d993d661f1a52835ff84c6d68bf0599f829d77d4938449da80cf9d8853f9331117d4ce9702a03a196a1aad3085225d0e1546adda52ae909df79fb2658d386144c5d92345d52bb65c7b9de2c973c8dc5ea810fd1259ef7b45e54fb43aa50314931d8ad7abdeb9ce3da950c38ccb7205dc95811c4f0ead999453fa945b5372566f0ca3e3f28ab221e8f848911bc0aef239ea692e142f136b7f53929ac0b5bacd69ce6fe8e4dda0b9acc9f3bda2f5624aad4dffe8cf701d514be8a54745c014fbe8cb22f5177a146530d2de29dba52b2e882862a038868e0deaa235feb14a99950b7611f72d57d57944a7685f34130029d2c2c3d0d5ca9ad3bdc4f4610311e87258c19276bce586058f440be6a48d470f51c19c6639bf595ed02ddc0011b17d1dfd2aa729451092e3cfa3ef0219a66120162b3e9795f55232fc6946b0851c82a80efaf14d8369b9b7ea58d7823515f01bccaad2e71730df20cd8ea1bef4a5f5fb2abbba4f41664434219023fc591030e8fd533f0072cfd3668f2b2bf38a385a088c004c384f3ae8fcc364382f489c06ce60e8c337ae0b60f34eca2b8d124c6d312d8241d9e7df9598b1e147c3fbbfc8f2dc6a45d8fd0c048b4bd6e24b77c04b71e04a954828a5a330c97dddaed9750667e5fe7542920ac295ed4fb5866a0375b42a7575cc3b5cb684fbc0aa74bd0e7b62db9faeb9715b3ae306a35f3aa0b1193ca07aa710733ffd64e15224f9e2d42ca6c21a022578c55c675987dd375eae447a7a97084a51c7cae7c74459df4f873ebede1e72c68a31297ea5252289d28e6155b040509b739e6421143970584ffaef2d56ebcc1b1ffc310cd20a0fd7358fd488e83b89dfe410796aa3bd3eb2bc6d55fc3f2a1e008b6a6f933567bafbecf717caabbb01604a3b6de4e75b1fdac45d55b94d68e2e36aeebac012eb6efc752c35d70d633dbeb3d9183bf154f538aa7b92ba5742e2abe7af501064d5f7c38a364fc6eb17ea66b7621561d64ef0c33407fc65235f0e434d8630734191ee9933846be042e0292ff9d19c7aa26e70073879e3d973565d800d794459521686870de57888251d4f420577ce7ed02acc8f87752455811e46066c82c33ca536dbb89f4e70ff78e5c443f9bee14b7019f7e2ca701b841ccc9316ec08cbc825b1811c624babcfda0206aa85ddbacaa4e9c95274c5f6dd32a4e32f17678a0eb4989863ed418e4c77c5c4245d3a8dd7dab49385dd3e663f046a890c6eed0bea8ca312a6f49e939f6ecb408dd2b2900168c2d0c5ea9e88cba7495d39ffe9a2263ec651c2bb5a315f6aa708e2db2b89dce8160216a0c4c8bd14f3d535315c466b018210c3d31f465f869431c3ca9ec1910028ca80df90dd27cac9eec6c0da18700d025772775c9348f64bef1b49f51e879c4dffb8be41922f2e077f220f95b3aa0aacf89a07f82c9260dba7ac62b93aea6f1e1bb659126337b324d387f35118c0c1162af24fe000b441d32d9203d4106e5e4136d05d51d7490beb06768e8d9b5b2035bb8618610f79cf6a10694af2eacb57b740ecec3d53568a56c5da2fe2431995134f044a4564637d5c4aa0664fb577917bab48a6d1a57104f831b68377b2a9b5a627bc54a4f6b0fc263e4e1529b9c888c02d9aeac42549e8c5a69033ebe45554fa5d10af5336873cd23fe900c95969467384b30e5bea78a01c0e35beb13dea287bf67781eed641d5d8d2526a017021bc2f242818e29b872639ac057aa7e2e46ea196877d18205185fa157834eba6ad321bb5af0bdd536114cf928ece201ae1a2ef5a3af9d0085d7ee57627b12b2198f64d39bb0efd746a1cb4b0775efea93ee4542c9a679faba447c74bfe2b3b42e96183ce537a284deefeccc2bfb085c3033f56739068fe18411f7621b6a236df094aaf6b58f050c3b51f34a8c1476b00bc2555d60206e516de08af27a9ca62d762c8d1a3fe0837404d6708d77802cc1412de75d72e8f083353427bc753addaa56148b484e5b0e9a11aa9cf657cf452059ad3e53037c843048cd7c3cc09474f92d98a32ab9b2706e97b59af0e75d25d516481bafcc0f707cdb4d8590b9b93d77569ab2167620c0ca03bc97f74c249724ca79d174d1c8286f45c5e987fb4f9d9b1063aeb974a60d8d138c45111388298c162ea910f3e216970fa7d83a79edba58dbdea4e72a1ddb83c175639a9f7e14172e18cbb1b6b0bb86978d7fe120f34ca64999a5d28d9da402ef527e39c0a8f19f7505422a780ea8d40af437387032fa206aa3558844b3ebb7aca1f0e2a720ea28749ddb6713294c94e95d7fb2b93e877f30f2bf0b1a1397c5a1ec15058674330b27a23d02f1fc6904faf7eb81df8768e4f024da7cb486c0420131f72ab8dfe66b24b192800bc9af657d048f680d101284018a63faad50d7bf93053cd74842180ba4b844b8012b67553c0f3d6ff81247d43500af78ed6060cc6cc93b54ec6f5bf068ac8d7c22d260ad21e59895f6113072ad942ef1cb74f2cb7c4f54681d2777c9a2e195381b99ca58614a05f757f073b7d0d1c8919fef3fe2c976e69ce8321b7ec3cb7401b7ec0bf85e3ee5bbc857d1a4ea49127357b76db4ed6d55d43abfe978a0fd3232bde81fa9c95759f85d1ff6bed3de8568d9849454e9a695f83aa4a30ee8d1d9cbf9aab470121f5744a69bbd937d0ae1bc5c66196afa6d73720229d8288391dea2387da23bb31fd4ecc820c13ffd6711d07d6a1809f95a5978128c26fe920bf9cac61404d428cd6f81aa48eefd100324d3b9882eedf4e2e612a0412cbbfc4726bde32812011be3a6b7a6a7d9688f020e0865aa2889867daa37dd522de90a8e012a6c8a427ed90ae0127b46270ae16bd9aac28ccb58078e0d921a80b65ac7bfe782706a269fb508ffad1b14581af3f9e5d25cdc87a106a8ec9370e59d164494588a08b36af4664dcadc8f844c622365973505939799f90916c3855240d0eb050ff76e9d86b9945e9a0f4d635985b8642d4ed3904056358310e2a5ede0e17bf2115d3e009167bdaf3e49e15db9aebae09c60f21552454a101bc7a327898689a98521a494d5f4f40a8d42d7aac5efc9a455f324a780cf2f2e5ce50cc707847e7426d8853e26ec1e36bb514f140dc4b167a08656cdd91d5574b995bf402e552f112a1822c3f26d48a05f6d469118bea3afc5099e8c417150f5297f4c822d3e4d2856f87cc3e61935fa52003ee2d9ca2a6daac4eff6cce7d48e7d58b6f002a78bc7894cef7c50c8f8b1eea8c03bd66c8af5944d7f9bcfe4ab951413ae5e446067a2357705c407c93cced1d2eeeab4d8ce0b58cfc2ea586d9b75c72300b99254e30fb9f2489fc2ba69248173d9163c314e78c8e8bad6f99efd61fdd870199786fb18161d93ef076bb2c9b0ff7e1940575158d1242a9c67d9224f727f3141b7da3d828161dfb8d7ba048c2396e8104dc9f185abc41ffde9fb7ac4f14e78622cc614aeb47eeb208263048d0018bb17fefc78195a571c96211fb06868157dfc0263bf50d0880bff8966bae74298941f2055451c9e5e82ed69a1e6a56fb72d70aa33ccb12ae4a06fce06a41c4f4f978d7b9c9eb2ce73c7ea22dc2cf9c2a30b2cdab0aebf9d1ed1a291c898dbf6b9949aa17195f49f4271cd061083c11e153e578c7445769842d27e59bd106319ddb61d9a38bf2a795e63142fd9f38780277055685ac643179b98a3168eccf73d91b34fecf5cf3ff0548b119389c48ccfe81d3e1136cbd14362dbd60e74caa92debee5d50d522cc5e87f43c367b1c4e52c9e04f688736904dec5535a832f700c7899aebbd991e76afaf8400763ac6481d62cd8afa001f666ae5e376e31fceba6a99550e4531ebfb62f933554d9698f34ce45cb5a91290c015a36763bbd0e36640791de29c9110ebc6b1070b70badef78049508f994a296aa943b14a1ed4e335bcf4196dc1437e9a6f348861e10f4806b926dadadddafa197fad8dfe11de132d49d7a3a5922de0973d3739dea620130f111f4766b1dc63e0cd71730d77d5c5d04082de72b36676ae0e18d2796f41e766b6caa70d82c26fc2a6a9921e7e0822f6ea6de0a2c9973ba550a4a33b7b4fb21ef7dff05c318863995491136cd6eef5134eaed321db667e18d57d378c5a3adbdaf68d5a7286e6e4fb8f41641991482bdd056083efeb13be655f5f6afdf65285059c2ce9c994c2391d9fc869fd5700d7fed3890ea7bfedac3dee3edb0d7efb6f6c699c91a7fd66dd5ada0c1648e6991d472a23e936532b5ed12542a0f9279ffbfa569e7676e4bd2b853882d960561124c0b7602b103e31e98acfdea12f85dc222543a5cf2a0d1d416add68958224063163da7d891e682eeeefcbbbd16e6f3fcaf747de370c83450c5ffcc77bc9e746cd2e6b6c0e4af6c83869e66698a8ae686d7cca7032e793b0725cc41b7ba7a83f8b50f15ceb4ac5868d45b5293976aecdfb843e01eb21363419335903d84877581e899ee055c8c9611394f6a7bb5f488d55a630bb034a91f7537d379cf947c38016552a4330fd6eaa627e6fc07854db4127d0e1771e5f005147d3825cb6b9dd4767913ca4960c78ae1880f47ded302b56f77250fb723a846aaae8ebe8be67bb701fb2c46605f318219269fd9d1f936d07f9d0b3d9243a0cd2784e7e32b5e712cf8ccd403e604217f96920fed50287d8fdc28ba12a34f9433479529b469ed41964676ca95c5bb4e3cc5d6072a0265b81804d5508d23c05a7ce011b3870a7949d4ba2dbeb42ea6e1950f154c1d96ea27c84841988e1c992f573e43b33bf162b80aecc39e0e9b44e00894e67b6ac1a47fd73761f904cb9b0a780c695df2935a4711506bdec6c4e7f9623afe6125e104bed3ca06c4bffc59048e680bfc8de6507b20cd5eeb33ad1760a17fd0e7f4969095867c12b22772d7cac632731f3943c661913f6d5f67d3e630ba5e25ac19a95779f7adbc24c6ac86a37685eea61f1d8225fc2169398bcef1f30c5f79dcbef16336daa7b14f208780a6dad0c678395a60d5ae4e87a45ddd02da3f59efe3d5b48735405faa4be847d0f935b2ff2633abec608117d12fcaf4c63df8bacd0670906b87f6e35f49d7f629ef5b6157a4881acd0ec872cb602db9069405f5a1a9f77748d4965a650e90bfdaf92a97d520198bc9d2de7a0c1961691d04e613762b79eae46b402c55354f71f4744c3644c9c1c8f9c79614ff948a33a689fa3a0919676912259ff71853be4e315dd4b76435f210e789d1f58e2738d67f54b5c0a77039a521caa45a5cfdfb8196568dd4a845259d8439955545990c43e1f9b0b0cca4c293863ea38f295c2fd8eab38d9bfd7f464a68afb1e48839fe0d2ad901ae1218cec9407f3bfde259270553e8b08dd9e3ffa32544aff1671eebe318d83d82665db5844308d847498536d018c96c661613beb2cc92ed9630766d2ea9726080aea40066eb27739a317152ca94452f6fd1e41c0a4c3b87aa02c2e7cebf39f0cce2d642eba94542da773a67446b2aab4851c348214c8b0c51ca564909c6851281a5b35082a019b6f43379fbae3cb794d47e66a7d239ada2b6301f69d7b66fa97ca923f8277bb09a7d7bc66a9eef3cd4ac64a1eb22051180e761e324d90a0ce9f32991d10790b8177dc8c65e4eb2bb7474e2fb7a0f87bcd974771b15ca8102e124a1a1c9a3336e649cf8876c49a179f8338ec377252d56904ff200f03f0fe3a54377909f91b62cc9a61f490f4b8c09927dc3405a57bee6bdc9dd7ad5b8292b6cb92b4702f2c742434005ca0177933a230533314b8d7206ba81df857a9ec7ce82f76d8a3ba004e29db5e44f4e5b08670a1552339c5a81e7ae540c6964964e58d870253ab5b05dac986c18b010cc0377c78692728f4095e4acc57d95b08caf318c6bea42f643dcc6348568ce1966b6fb6d122495782850120b7788cb85bc45af115a31d78c3881744831c3ef9a166298a5b3fc0fada51837b0714cca2ebc3605f1bd54161404e7caa0ae0cd4d501bb2640570fec0a015d1bd07583bb7210570decca415c31b02b3580a1b7b5bd88c06e6e4c5849a9177439e16bd525c21a35f122c7aa0da694728b351dc523154a44435c3ce8ad1f0674ede0aeec9a2abb8440f19e5e94e0f810186f82f1402ffb3918d706edf2131c9e78201ef8b94a7ca6443cfb363a2d0d358d13640f0549cb9f7859a7dbe18e84b295dd67438e89827f8c48eb5d6cac32340c619be19060634e7265528459d30021a2233aaf940f45c277ddea98adae59717fe175541465196fe6fde619cda4dbb69ce977004b38af1c9e2f396bcc578d6f791c26eb8a854b2abc2bed57be23662f360dd9f24de2d1b282ce856abf93f622c5f4af7a21d09203467cb60c7dee314ffc52a47f193435e3a103739a05271d306521aa7f00a5b8fc3a9a57970a757d84a586a8d244310535d55120b192e7e954373172bfec598244248814c68b920eae265128935278f6e452a17b0d33a37015ea768ebb153beef89d247d5d48c31d95f905f278bf553ee791009b7e7a6a624aa3370ec58197da1f3e114e4610bef31411c264259bbc7879307b031e6554caa472853a0758560b778d86d051d1040db8ff13868c782fa8a8cfca520738193dbf8559f6865100a06321921d7fc0670c632fda66ff5d619970240eba08286f099891047884c5ea13da3e6f29ab5de91ffa269d09db9947e7ddb49f1ada721b915c382356d7bc10bf5c21701028a471a71a91f2bf0bd66f36a6d27612629860b24f88cf1926517bde490cc2c58b7fb778d9239014180e927f29f2fc29933f6b1773c698ef1cc5d86e756257daeddcd9e7845fe8a1116b273c4889b83fd102522b760c0362f8c3b8dd127f81dd4d50ce37560c3840c6e41608688edfc78c3348abf0a45de823d4a80388bc491d6a99714af6ab074f309de9f172707aa0904cc8abd161e8eafb633a141aabf4cc67c4c9632ce6e3a70c3ca648aa0d199f68aa2c244fefa4540ce9d593afced69d670a93ca463f410c89f6bf20e38d3e6301f4c3040aa52fb85a309a59a503022bd5a09bbb51269988adcc54adc38436d062eaa2f12e466ce94c03bf9ec512407b98e56f2fa458d4da5187c3f0bd59e78f54666733ce60f41d49bb40c85f1e65b94007344d22b0212209d97bef2db79452262903dc0b6d0b480bae769d9f1da31058fa0d6f9c94052e3d7aa63b3e1a94089803b4a6e6364dbc2d85057206293f8f35ef9df4a3b2abb7dba8f20b695844fbfabb381f3f212c9032b494b0c753b79976ba422c03186e43e20889a497f19c73d43deadd04e9bfcee5b0bdb003469e73e9ed34879c4fef5cfb3c9f6e33b92c70fe4affed87cc4324f570f4df9c0f39af34c8782f871bfdc2eb1789a5a7bef9f5b9b94db7f436eb5ae6e204702c22ca21da79ea9d739ceca6e7f98643c66f5cd6808c4bbd1ad7694b07f52b3be772d81e177297fb7e0091f15bd6808cdf7ccb1cd85cc8fd18614c9fd3a31761687eb54ffa0d65e35c59b3c0c59aea1aa40d4615275970032a3634872f62229468d3a0f4f97833bd5b33777f34b77000f23da9d7190427456f01031cb6ce476fe10396acf84fc0a114fae82d78a84208128a29c17c2cea81d20b217d2c1232fa50a3a2c8073a5f249453e4839f36c2e8617c2c3282e79170010f123be420e10224ed92a14da2cd596bad5202517119a6c1a64a2811dda149a494b5ee4c1e58d3f7647af2548908831505cc82020e3bf6dd4f1aeca346f289252d19b5f0f82c8f9fc327cabb78fc16cf19625242caf11952797c168fbfe2f1531e1f7b7c94c73f2df9ce959862b09da09ed7a47c8e2654fe7afc2c7e05fa1c4e947c539cf2ed8e2ac22daac0e155eaf9abd4aeb06153dab5f44d9590b42b160929b52bb4b029ed0adbe797e40cb15df1274d7cc5a021a376fd340d7db5dfa4aca75dd115bf1c663e9ec39b04861c5ea4177278a3b890c30c082687d9d0b77278755e727861df9ea3c99276cd233e7e8b2f4793254d7c9af834f969d71cfaf8ac2f47939f26b126b128ed9a40f8f8ab2f874f94a4a429ed9a467c7c972f87cf149f259f25a276cd1f7cfc962f870f9191d193764da18faffa72f83c393a6a6ad7f4c1c767f972f83435c9699223d4aed9838fbff2e5f0111a1af2a1d2ae59c4c74f7d397ca8f830f930f94869d70cfaf8f8cbe123c547c947c9074abb260f3e3eeacbe103c507c907a94953bbe60e3efee9cbd1a42927c7c992764d1d7c7cefcbe16489131f273e4e84da35813e7ef7e57022e464c8c9509276cd1cbc93244e789cf04869d7c4c1c7b75f8e265294949a5069d724e2e3d32f47132a4d989a3039f969d78c7dfcfbe570f2e324e624d6644abbe60d3e7ef6e56832a5c95293259d764d1b7cfcedcbe144070643d2ae39c4c7af5f0e2748767680da357fde095050500e274ada3585f8f8f2cbe1a4e7a397c3494f4fa269d4aef85409251f5a98dce1b13ded8a2f3f164159f261a61369e85113df4f48691ab744441aaa347fda455944c0477c7b8c4a45dc29984a689bbe2f4b4af9dd2ca34793e62a4d19a549d33094d2118c2a7d5bd8b41606ab0413df2e3bca1051a65d073ff8ee22d8ff44183e3d441a171ee3a1ad410c1e5a213e7b6873f0ed5769d2b8c83666871878f8ce79063e2c29f05b1896026ff9fa759f8e354d7cb5d0b77f14c213431fb26072c06e619306c66d4c466092e9abbe7d356491aff2e5d4914d0dc6bcec9f8eb54f83303e95a9c2bc327550bbe255ba4adf5da794e9349821f9769bc1be2d5368614cdf16066b7b041cb60fec832ed696bc5f3ca1c789181f308704da03660102cd01e31b4825dc7d2c62520379056ef958c4440548e0958f4541344d2860978f454140810a4130d18324fab1e86787125c5aedb458c59aa9897184168090650838c20747d3652b9d316a66f4e0049aab072df03e16158105155293b3a6b26ecb6cb2a89d73cecccad820ab523bb3be75d3e69c53bba91d58735e39b39eb5d65ac339e79c73ce4993d92b2f9ed315a976e79c736a21ce177184c2a94e9361c86846061950a9ecbb7fa78e0da4f04ae6752c59f659292ff0df8fea99aac56566aff3746c208557dd712c29a54b060364950ca972958e6a092d6ae7e138d2806aad35a334649a4ddd54a599b5b5d6fa5de09b9fda0095526699944b98d0a2658a315619da7fdfd1fb94461c7036650fafd37b78a511072ce9d62d51a669aa77f61fb813cd92ac805f3e161511c587335309a7904077807110aa28c2065d037cfa585444cf9c52c4cee401ab3e160535cd2c60968f4541522002a73e1605215101e38f454146334345413cb587cb82832376f00f3b2b38206207d5034be1c08724272062180729d8f1683084a2c243d793748a420ab8253d1e1149f8b02d41ea88b8410f9a12c7c30b6ecf5644440e30c85e3458d2ee8c1556602fbe1dfee126b56006414f92009134846406b0d84e4f8c0718d400a92806db21889d2913ef4dd00e0faa50e443a5c82788221e28b59c98be3443ce9842eddadcda4a2badb45ea799c665de4d96dd6fdb8c80ed9625ec08ecd3a5d174199bd60832075602fb31f1be6623e0ad525a378fd239718431674419fdb3c75bce061ef4b3c1460d0d4ea7323669b8ec5bd63aede7bcda74b9048627caa7632fc718656ab699f7e3807cea9249e7f463e265984f1ed386dab18c80eb8733be85363873c072faf6f2081c9b73ca9ae9a72c300bbc3ca41e01552081d773ce395fd42d3e22c00e90b08325df5ea7b36610964af073ce39e7a4aa17befde221e8a0c937eba3e0185007b0ff7e7adf1955fc2084279c481103a42982f044135bb8e2042b70c1abbbdbc9b715dfdddd4de5aa3eec7e4e9830c6054539a0e2a7e3e92c2012fc74074a0265600421982005195c01083a884f5ce1b3821e1f9e2baa6832850e3c2be0e008df4eef6f4ba0d6b2667b481b05d44024d3370d31a6b70322997a075c2c009ede5581c34ed609a4cb4db970665583dd7dbfdad5daf59ac3e9aad6be3b65f0cc3156cf72d7a0aabacd754eeaa29336d8aa76356e2983745537bb8e9406c1557d7e36ec6fe198aacd00f444f3a5726996f9e8a311ae99b917d6c3613029a594dd63ceb993346f4e0b1096334ca5eb730ae1f661effc9c54e8e1670d92730a5146edc155e2c3ac870a3ffd7eb687c60a1165369fec0748b0ac0f7e7a4fcfcf3a54f4737a7881f85983e40c968928430fa20cbb840863fa84d21a0d7068a1fc74db732fc62c96b43d3f3dac5b883440a2cc929c010351062b4479c552c8f25158f1530a58ba475bb3207c28756cd023c2906ebf1bfa7597b42892e96587fe821b6c29b30a77543a95523e951ceb08dcaef3334e19a65733bd4d17d18e7a379953cf469fecad5a76b2e18ffb83fabc7788d387dab8cebb57de3e31c118e575dc9669f50e69d54f357bb684ae6f105c8c09e9d0b7ec07f4cb738e9ea4807bdce0c37399c3363a8a512a7d8c335105dfd131106790485a3a286d30b6519491797340fef4b9f75e198444ced04924925377123943134919dab34e226d9f98a03c7a11902f542e62e475ca7467caf04c1a1ae4ace6a13d13c868c6e6cff4219233cc26538808a39f05042c3d9c3ede8e57acf2387428a322b10d119625bc5cdc884a88d710d28b442bb4e0c3d2cb483ca20654a078b9e4217e6c2e73cb7abc0125a53cb96cf1d317f27894cf0fc8cc4fe75abe1b80f88a87518af4995b9eb7b0c757b9e7f7bd6c93f21bdad5e22b2b53bae72d8e5a41adb4c81629575a3e7923f5d168b00588976d86f01739edfec9b94dc66f1f8c8337599ee310a5dc1065c8db51989d6fe9e85cbacc3f58bcf3f85607d3951695afc83cbde5f3d152b97c554b956f0022736013c80d0d4a595ac938cb9373a7cf5b6add689adf7fb7b94143891c180e44ec8785a49e50be254c26e9f17185336e4869cdc468b01d072273ce68d70a1532e76f87231254c6f87c58a37d1889987c9412c41351210a4e90a8420c8270829710a4d05112850972b8e2c5e241102ff891c10ea438c10b07e808323881931c1835a10aaf302efd9ce0b3f71dbeddc6051f8b7e64f0edd707831f8b8480e2573e16097124b96eae1481a3f7294726f1232ba0348190d2c7a499d155224de30de520699774b96488f634f1a1d4d188b0850fe50e118ca4ec7929a58cbd6cda351186945e83f4338791c9595bc051de1065bc0c8f6d9033d88f4c78b2849c5718937e49f4a68f8eda01962e67882f9d075c8c096b8c3fc819eeb76f728621d40db0ccf7dedb3344cf0f228832ed522862058ed72bd2648732263cf4183c555dada9691a1749363b6cb82105393ea5a657ba299e51c1ce03e131a46c1bb41b81c31a6ae79c53a6a646073b2ba0028e53686003534c310515173502992377e69c9d67e3d429241cb202a9e7dc05c7255183201ba23cc54212349062838de1d89d96a1be72dae29ccae9cde1fd9c62df32d7282b53c5b6e54d09c69f0dce5b3538540d9ce7dc66a2dc66ee40c91da2abc5b387d6054f2d0f9f87d607f7d0cee0551e5a20de3679ea2e3e9d2136d7c15f2edcb1a366a4b7c32a168cc754db61150bc639b73766284775bff8dcddf310f95f0e3747659ee7fcc7e62b49a74859bbec73d506da0cab58301e9375361d399a44fe8c303820311b0aa5720e6fbee2be244e9deea03245d22e3c9f8043b0e55b39872987b9d5571341ce73f6b96a73f1c58800260070171f974d81392e7399f23448398e9b91f2b40be5ab9619cf791e7a8ef3ef8b9173ff62e45cf5c5c839003e28cff9ddb6d557d320e7311ae468f464a0c0217dce8bac88f2a1dde9796984a7564079f954c9731be7d5bbd9f24c835ca63ab2d2d9d1a29c3e2a87467ea2dced0e50bbb8ec3166477d9e671a4465aa835537d5792cb262ca879487a75d21d551d2d3ae70c692768534c9fbb4abe5e10d312b5335a879b63b4c5cf8ea13221f678a24890df3b67278b14b0e714b0e592ef28cde32d569574863df41d4592c8c83b61cb354d7a0e6590a37d839128dd22ecda9d3242a850651ef084505a6e7a206702c9a02fb9f246220f49b7349c4e0c96fce85e06f7369f3d878f388f2cdb76df3e8dd6c362d009ddb5581e3c7a601805fe7993400c8bdd28af00c1b1a2f78f46e60709b9e4972060c4594114574b9d3fb2cfc2c0f59cff21869b228b339cbfd9b11dc1c7fa86f73007c9bbbf836e73e946b31a01cbbfb5b41ca6ff207c7222a849e737f2eb512131313636388c163dcadbbe75052e758dc51284c03ca711e22df73c8f9e63f361456c2f16444edca4e3d336a1a174efdc5ed4e121eca83dd858c4f83d463befe69907a0cb4b2d01cd84079408d408740811093688ce280c630a6314c6398c6fc9dba639f396f1fbd0d6ecea35d2a1aa3310a4483503936b8b582c8e77c08fdcd83a07e430d915cdef28e06a9cfb770ab645b82bfee6990ba7fbd24eb78b42bc4ce43556bfc880dba0b914f81649cc0b1cf3d87f755391f0140e322002d8820f5b04e9a00640b837c0323003079ea3f04cd6077ecce66c39fa3dde613c549af0676192770188d7e08918ff30fec42e457a0414a6153863aceb0962e56ea9ebf56d220f5f91675eec301c8e703f6e5f052dfbc1af53dcb588139df9cc6da85a2e17d95bfb8788b539e95d395e34c5f7017be50be0b8fca76472bc29cbfe0a1dd497576c7ee4419495e50c2e2e15df110c32451a08f063548bdf3b89d49332fcee1c52f641a9ba9ae41cf4f1e52cb439411442e718e92346cce39f5746c6ea9db2476730e0f918f73a8b90beddb379dfbe2c37c216e517ff9c2aec5b7c3816cf585d4c5ee3cf5962f54a968eca9df983433284b0eef4a0e716a263df5f6ee74d6c4197327efe3b2efa30dda4fa5c2c996bea64105c752e14e4a29a52c929d529a0017605054e0e921cc5bdb0dd9af934aba5a6bed22102a0c9640a930613d3a5767add465a78f8aa458a14fb12229a2d045522475d6dded954ea1ef763ce79c5ff696815101a63011059b137ab4a384a1232a9c6c0c38e2870a5708a2882b98783961084faec0a28a0b928a909ab0528484254746141df93441937e778ee062cc12eeeeee225fa907220c99a39028a35f5a2964468ed1e04c834998f2a88f454990c17ffd1129d2209d3432470039e0ead646cd4ce68743166a6c441ad47724e911918aa288c5a228723e8c52663ca9e425071cc6219b183b5a67fb0be9b4088a294f1d7c1169a451488421dda6332b5a11d83c462a46d0b9387faf76efe4e15a112822a7107b6daeb910f91468495f05bd8aac825edc7741cc92890dd625889650f24b48f958b40414181f8b94a0e2712ad2cc04f1d860083de80009545eedf7de7b1de763bc454a44211ec599a9848b31327a3b7e07a97449b34f19f9ea8a80b1749b4883c364a7a987209648dac10a2fe937220d8e1142b8c24993104028b1e2556bad3d3bca97a15a6b5502e86badb5d65a65ad35b662942f39b5d6aa0492afb5d65a6bf5286bad5eabfba895d2aeb4d6eeee9984207ad02e2075d7eeaf35c6d439e7ac996d89cd39e793a49f73ce39e7b474524ae99c34b3aaa539e77ce2f373ce39e7cc2ca594d239e7a4954e3ae79c3473faf438930579944a2ac252aaa70841330a1592c46048be0e38223939392378f98349f5ce3929addfff71f25afde4a78972ec9daa3ccab93bdfceeeaed33c87f2bae73387d72b4eeca35783d3380f42b53ca4c56b6eadf470dc0f42bd033fae9ffcc7f5c9518eba75eefab5dfcd4134ea2b2caa54aaa565ba8ae50b8b8422f8e9a92fb4f979f2f89ce38f3be57c8c3050393ce5e95cadce799dce19f9ebd3eac42caabc5c9c04b0ec7504e7e5926d0e6f952cdeda881f24fbead3e757e3a5dbae03c1897910cd43223ff39016ef7938efcc41340fad8b5fcdad5f9f5f78e774b13988979998c0c58fac56ab95912338be5aad565c8c050845f0d65ddca65d562e2e2e2e2e2e2e2e2e2e2e2eac1c63015c327e024b0f79bc7559ad56abd56ab562ad56abd56ab55ab1f291951b71c9476294992e2e2e2e2e2e2eae43cdcb251f59659cd8dbbc79fc4efa9d34d2e3e6ddd6fd686fe1d07e3a90ec87ccc791a9823597553ebcbeb9f46e364b4356a54a952ce0d0727899b8787b4c60fe72c946e2d16b88160efad6efd33fc2f4d26f9223485e0ec94088f31ac8de3aceb787e3f4360b994f43e7e4e4e4dce936376088fc2cb770dca70e247b9b87ccaf9966d9d27989a31e63b6cc294eeca5c7ef3cc8f4eec7c9739058e599600fa577cec92fbfa09e80a347ef9483c8a779ca743be28b5066d9f170fa85f7e9ab720e627d7a57432f87b7bd1b1e3ac857ca53373c5499042a2f82f3523967bd1a416495a71e5b28f706a98d160ae5284a5df378f43f824c0f4f3e643e1310a83cfe75c94395498093f3224111235580f00415576c018b2a2f951fc179a9f211163fe5205f8813fb2eb7ae4fdfb23778b30ef033cfb291ca593e233267a86707a693c3085e2488555e38c35abc54d9042a3f62e4088ea752a9148b04b2ca6b88ea2a255ebaea3b62040610a46812042a8e4003a457ca71982c21072d08d183931120e1a552a95443d01c238c5095f113d804abd791222658bd8e1851a552a9542a954ab1a452a9542a95721d6a5ea97c2404443002063e80a20a3ec45e2cbee2478c1cf980d2113f55f8208420b0f04ab911553e825d47470756f352390c7ca95c07c67aa91c765f2ad74131e5e4b05e24c89a7474005f249859e4bc54ae43cd4b958fa45890405194d4c31150207ab5b37cb865240c8c48820aa028a20913bcda531fd6594287064f9aa2387ab1dc809bcee5bcf493147a4418b2ba4d57d7917424a4a42425298d3ea4304a779a4a33f552eb3438e7abe67a3465260e60ed4232854e403c9132429db5ce5a6bf459eb9c73ce7066e6db5148c0d1efe760adb5d65a6ba5b6564a69a0b1a047b7b5d65a6b6da552b214d02ed306e92907983a5d41ad55e626f4ac2d4ad45a8764adb556f9d55a6bad95de202c12a594524a29ed224e2808abc7a61e80ff51f36db0e3fb60b2f9cc87510aef38468a5cbf9b96757480f1d21cc6f2ba33f453852c9f79f46eaa8759d54094f2416648ddc6b8859f5ebf08bca4535a9988c0485c62e715738e4b0f515f7348552f652e129d3565541fafa084073ff02096745dfbe2ebeae8d0afeb3005bca46b1f4c01af5bc48629335dfa1035687e73911b4586a8e1bacc456e9e353f6f148ac5ed01ffb0d7adb5d686d676cf69ad9cb2670eadedaeb76f6b53497477370a093846afd189287bd8173d5e488df9fe80ab771354d1ab6f21c61863cc818b9ff9cbf4ea1e0d35ffa8be65b5d25ae9f4e961ad744e1b16f9da56083fb2a7cde1b63d37bb6ba320620a537c183dde1eb27c1bcca69d76ce9a79cc6c666dadd46809e50d5d97f1618dd5638df1e5f404b787d6d61861c40260edc72590a8fae48f5cb2040e553c774e97b019a3df9f8f7e97dc1ecd49d57ebe3f15ab4807420df634c771425a2371b791a60c754e7309a47d5288096b1e24854efec28bbfc0e2210bdfe7b81cbee4d8e00b2e788b1c5e8d2547cddbbbd1a8834893a67bdca64780639480575209060f675e3c045b78e8979b81218733e04b0e416f91435f71560e592f646f19ea4286c9b0560e59cff2cdee9934f4e690e54340c1d26bdf6cba99541aa4b149d33d53863a8b75354dd334adc8d3eea1ddd33ddd036355893f9726951bcee3a4d2c414a314c6aa92058e455124f9b07b62394f9d73ad7b96b4fcc41ac8c5a9b7500fcda32943fd65b26ad09cead09cb934a94c269cf79c7ab1416f1ebde479d422cf23569e47b87b220cea282bf02a87118ac7bc88d1aeb8720ac2df4f174ef5b17cb741edae7c21eb531f4651d8fcb05d76702887421ac3113de1b28df85090da2593e88bba14e286a064ca87ace7244f266574498f60da004e82b82ba43f2608b87aa51ec606a35f1f1fb66a77cd0d84f5d6bb57f59b4320accf9c3ac1b1bf968dded6eb57cf22d3d38fb9d5441a9c3ce827903e367d2c72e2e839d4f51873bb1d2db85f68fd3a57bfea8393420d4697443662d3ee1ab24cca9aa3f51cab5f577da11c8a992e2c90413336d13d66fb58acd8a3c1e9493e7a8060cd8d18345eccccb0c97a90f4f4abe31660da8e728e76403e8b0abeeef9c981749f4979fa6ed651fffafdaedfcbfae4132221e090555d131a923e358730315618ad9c13e40cf2afc77b93b67661150bc6634e5ff44df36cdb9cfb429ce7502894db888fca2dec3f324779e6d8bbbfee7da190f73cc779afdbb66edb72b47e6f563b698b6083241b2c3d2a6a3182b1c17be517e13c666b6f23e07af56283f8ce9bdb7160df378d83e42b97d4ddb9b5f98f76fc1b0e5cd484298ff3b67e9bb59947efde0f48f759e69c2065a02f75a48c214518526bda9343841bece01d7007e21d1cbd48cb7a7c5d9f996930bee2940111d01e3da7b55aaf5996f38c182e3c78a65df66d749817edaad35560b56930fa107ac0f363d1112ca689267a607d2c3a32a20918cca08a9a38c14b3945ba0a091fc2c02cf133869f4aa8e2a7b39640f4d31de9053f1d449a62866615f4ba4f70dc044d0a25377648268e402f66028b2a7c302f08c5e89b76950541507c62caa78d4c3bef26dec4971c396d403dca9843e3120ec738d48414906ce10947988003093c11a5084c20f184931ac4a224347df4c8c2d98873768843ea2ee05eb0c0d31b7cc9229eb89726bcc2d196a15485733cfaa2051cd21570d1025362079a503246d93e240338df3ac06fef0cbac4b55d9e85d1c7a22c827e001f8ba6b0f3b7cacffbc7a22a489e9b2e001d6c2737bb168e8eb4669953af0e88dfddb1dd6670a2808b31594cf9d4c7a22c90fe5e0fade6f58b1f2a0b1c5bd5c1cfdcd60c3618e38c5cfdb22cacedb2653d5b079f7ef496cd3a40adbb0616b87aacfdd5876400e7e34b0f87f51f36b7ac570b7ecde01402d8e0ac6990c6784a63e02168b4cb86dfd8d1ae968ce15143dfd0ae76a0fe621aa4dc4b0e382646d2ee8e9436c1c518638c20e0a20f3a549448f96ceb3c1c324fbb4ba95fc7d7bbb93336f35a83e050bd0650cf5838a867e057af19a6c129ffde4ce37a35fa33ffd1366dd3786427a384c35e12f6922823f3a9f98fead7c3fe0982fa3a3d1aaed3eb54fb9c7addf1f1f886c8afb986061bc9a97b45e7ba29bba6a710e76f34385dc60a4c9d46bb5e34388d7aa29a70d8dafc7a6bcb3a40eb46e047b1146ec7aed486a8596687ee0a7694cd492cd1de4e4619b2c53b5f396bd5453943124a765ec6c7247a80f22ebe72ae45ddc6095c3fedfcc9bb93774adec5bb961677b92e9300a2e70aa28f47f8588485a5e7629c0b404c0c31842efeb9bb10b35ab5388c77d3d2c24396b7b4b05a2d2d0efe8bb35a5e5aac5a5abcb05afef2e2ac2f6cbdb4f8e4b73c1a5a5a5a582dacd56ab562b5f06858390bc6abd1b26ac9a10b003a292305a31efb8b8eb6ab8bad9717d66a65af8c48545e8d7e568b5bafa9e1408b7739c4f9b0d5b9388b77e3925b5dd6017ee71d27bb2883e5e1ca5d9ceb3ecd5dbe95cb5c01e3afe5e2d8a357a3e5e2e0b7f0556eb9641de0b7607d42e8b7d444182db915447e8b0fa1efe24150efe25de72e9fb74e832ebeca43bc1a2dd925374ca6b3439386c5c2d8cb3c9539ae020e2994cf320fa90f479d70f999a73c1aa85b28ed9a48344a94113f73ece9a06e5de543e4ab72a8f216a74fda1576f4e8e49953a376594f870422e7bd065c3fb9c5d1f9c9a3674fee33334e864e7e7c1927e0b0777cb2ec4f2ea384b1f79276fde8dc25ca6079e6ed4394d162e59973ddf7e22dbe20a8f716befac297dc22b33ef9288f0617ef9288019277f1ceb9ee5a7cf15961e7bda3c365e5f275df10f9abdc79236957d8e28dc400dc0164e8649411fd6a2e18f5c93841777e344f8359276930f396ded1e171f9e1e22d9e39aac54f1e0e97165727a38c1a3f1a4983990b91dfb0063397fec9383165a2c832ef2599b792768529e73cf3ee69d78f944bcffce4e9b83864e6663dd929ddf5ebe2ad0b2d6fdd8b343269ca646ebb28e3e4d666015ba7f625bb7ebb76757e9fe55bf9427ae433c7d7a6a76748680a5cf099a332eac367999f3eaa136164d9cb4ab8dc4a1aac49df4abe36985469d267beb9d6cab20ef063bcf4188f5fe22ccbeecd31dbccec5abb9231394b4f3fe7e9d73b0d1abd18f2e9228df59932b0498dba259f4923955aa6fdf6c5392c9fa40f2f351ac12379991b49157034d29122a9101d6a1775a16fa73c1d63e740e9a40a34581da5056c33db1dbbd6aefb748690f2e8095116b02462c2d14349c4235dee849dcc014b4fd260953cedf4933cf7933b381f3f943b3bed4ae27937d7a90d67bc8df1f6451863edcea457f36eecf7a4e92b112889583c92087505b6117d48283808a9324742a981c68e7ba3babb2c924a7c65e2ab5fe9c4574944d439a722c48e76dca84e5c29be56e1eb15be4220cad41a228c5a6b751bf161ed42128976925031aab7c1ec8b5fad95951d8932d553f0d5b5eac3d72875228ceafdb125b81853eb8976313eb6100f44199d479f51d3ae98301a355218956c3e1d94ebe02f541ee274726f9efa74ca5d96655996d9747b5ebe0dba1061f4e57278318be53974102996d3a06c6aa00e62612cc475db303303828dd4488dd4d80672a8652a876d80abdbae85daa50105ad4481fd45003af8877420335abe9ee51fd72d50d3dccc0a1cbfe60e6a705aab65ab63f3ccb90e85c8ffd1e08f5a4fb7c19a43e99a5f29447ecd2dd4400dce2a969870757bbbe8d56a33efa67a0bb5ab851a9cd9965b9affc802c801b7b68ca3baf4ea12e79b4a834c587ae640edba3e9b4a83424dd34c3535333320e80e04f4b997ae9859c78a98f08121d949d2aee9cdd3edd3ae56ed9e9fb37f2453839369d2f4d24b0e58324da9e4920a4b4e9935c69de9f7eebcae072a1065a44b9993a68676a17f8c34f405a485bca899345dc51cc1cf69829fb25156e0e9311386894a39a7a44d2885b7e3baedc4e97cd8d9af818830a4e34f2e913e0d4a5ff9e451835064142539452e354845f650ad6b50a5d1a0e550f27c96659206a9dc519a342aaafa62cbd8cc8532054f3d942a784b79d08c0a6c352d1f4d24da3a4819a8bff49108833a8a0a0e1b16ca1d9e250d0b1b565d228932b81444192a90336c3498a6c5a9d3b8d13c6a6897d7f98ad3148b83dec4a282a750ed4600394c67015bbe292f616c41bc74ed0b658ccaf3681a2a75e453c733a03fa5a15cf20dc4cb9e06a5cf9ed30da088f2d4fd6733b15ee38c33fad33c5f3438b780c338a5083fa713998e838e2883c60c25e0f67046ce3441ad421ac4a1c13a535fb4c801b770887fab3cc281c794a95e6b787968b9facd2c6b057e418b504bd9d2532ef55c94d172c1e9d7e75cc699f66e60e02ee7b143f06f58ab1c82d82f6b53da563e4ea6a71ebe44195cd2eca09df006c0431cb2029077a6cc0e8bfbe4db9d9d18616cb45d5ce402d07217ce5b3c009f8c3201b28594274d841f25529044d1243dc67c97366e3ce7ccb280e7fcaed75dadd3d932f35e8c592cf79e892daf16617c97091b71775e88b577e3cec56e8c15cb2bdb452ee6331213f30db1721b6b90ae7c738e5badb8956f1f277d6870d1e8431b933f3120a1a7edaa31fac99f2c8624611226916412d62e942522d24c983d4245394efec0268d8ce590857de6ce53e91305ee3cd39cc60d1a1965564e35a114d1d316bff2d03ee988f8a0d8a476b5df22f0114f3f8bf434b451a8671e07707cbf729567de4de6ab0fe4be0de7f87da1fcd5cab9d975b2bb9573ab2fc8c62c1086026b7e6dcfd315a7fae2ab3ef9b6674e59a262d4347888286968680a8fcf9c3346c913238d84b9f018b73d93c6454c6c7a37ee12264f2051105d5f8c8bcf454c781dfb09853f17dffc3e9739786888c8e8fb88c4fa933945b05dc2d99e4c4aa614030aeac1fe7dede2eba696a1cea18686888cb0fc49c5a48ff489c169629c3a456a575325fa8446a152fe884279ea315099333444644424d632a7c884090db56b23923932c7b9542c0614f4110d310de55447d4e04a478de850cb508f439808bbe09e9fb6238bd42e179cb63f793a145aa2580c288849e614913f34168b01057d1ec3d753965a667e72480ac9a09896635f963e43b6c7f6d81e18fc85176230786863b1185050bb60108a31461a09f38f1219c5268d84c162b0180c990ec5246ca888060a9a33d6ae060a9ad7850fe68b8592281693b0586c084b18914913a363f8008203b6a1b1d118353330c6d85b00406c5a6cd7ed6ed6ba7df4a54733cd33cd6e0d901e0e9b969a474bb7acdbeac7846baf5e8d76cdb7f656bdf2e2256e078f28834764a91c068c9979511363e8a3116e998e9416a159483b8f1e38a81a0cf1b766a7edc27b3dc4f36b5dd75c4583cdadebf5a7e7d9d3a7964315fe1ab2eae3cf5e81c58a2c161478649c4bf209d29c89c19244bafdb2a2741d70599679ce5104643e3dce3a10d6739cb56ed354721ce736e273b9d5f90fcea7679fa6b9b5dd17b3edbb1e3d0de7b56ba9dbcc4a7530b21e63381df5ba4df358f1c801995b3aea671c11da02bc39e7b657bc3ab7f2e9acb80efe5ac94374cf0ebbacd56e3d0a2127f6b1a8ca949ff9585445e83397ad2cfb0b06e6c5ac69574d7df2655bbf524a4f3990eeafdfd891caf7d6c43458dd7e30355f2594af2ea14499f1b5c624d0db2cb331328f27b76939f4622646cd0c1bcbc9cf935ee7ed7e925476a7bc7329e5951fce5f6c65ec35b7f6760d56d7b2ad6af14583d56334585fdcf0516bb881478f6a933c20242979555152926fc7ac952778c664dba677e4b28f4d49c9c71438da56c518715ef320d96b1d3b76f4961e3fe931768c339d64e5761ce9fc28f5ec55ac982161efdda8d5c16164623982439611608ee36c8c12985a29734f4c667289cf4f9090b54d99dba8f51194def95aa791108165457424129a7110b8e965ce0b8b8a943b836b58434715883452f63831f408f858e4040a9e93319ef4ccfe6919ea442ca1a714e82952cb179c043355286f731f1575f6928b31fa58e484cec725708da94b393939257855aa13c178e3440922183f9c3fa77f31b5552023d2ca1370d861556699703e3369b8ec0ae03c731b1f2d235d2addf898028711c9864c3a47135f92c85fcfbe2dcbeed558419c94525ecfbeeb3eaa80c3eeb32ccb5e482dcb315bce42fa32f280b7b63ee0ac4b89f2a974523bae01ae01268a30a4b3ac80b3240da5913a4abb3697520a4d9491aecdcc482469e27cc051125df2d4dbe7a96fdffd79da4a61f63787528a979e25f1205f3456d12979f00f587e28633d27f5592b95924ad849545e7a272dcd9ca6764d29a9cc9e43404c8f31d185053864b500677e1dd57ef2a9a150a8e83c6ab0215aab9d3479e3a975ca3aea9ffc9489ccd99d58edd2bcadf7e66d61ba8de3baaeabb5ba8df8b5baad6e51aecd89fa22f53c7a737a9c53ae03db15b950f25069a9c5d9a7ea69c364d57ced1733944eed0a3e1dbeb1c3da1b888ca01cfdc4848228ed1b7c08c1214a0f2ce0248b3bd9a23e3db32d9a75d427d2e0cc7352596b9535c8c8677635d8d060b7272fb7f9ad35f3ea35dfed9bde8da6759601617dcdde63d2742d533fda32b5557d7a763d6bd5498455050e635205d2bdf41eb60269b0fd063a054fbfc1519fa13d3bb620e9b2c00eab603c2629d584a3ee0385ce4b263e1625fdfc9d1ca553ceaed6c448c2d3e012d9438b2091b079830f2138f40e1e15b0a1460d0d9bfa33cb1fd615d8a6bdaec1d99a4e7dd2963541463e5ceda4273dca04156384cca10f20ac53e652c887da8d36435b46b6a44f3f6539c441f29967d3bb6151c1d6e5cca489b154d560744dbbe19c524aa957ead5afe3fcfcc24ec894b29b191314cac1a641797b4cbbeecd79fa4c4b6f15cdb4d658bde5d987bb191bb0a3c1e8f4931186d72925208902e56351139a3e94c1bbc13d387ed83ab72955c5d41f70c4e2763845ed17661edd5a6a53dea35dd86b8a13800ef6ecb381abfc1ada1581b01ee756967976afcdf1d2cc1b4ce59afaeea76a10c7d01d60cd0dee7eed9a20f862264637f86266864d8da42c1856b594fab014acb91183c68b991936747b42a5ddc9fb6e5701bc1263c570cc465abc03968e7770f53cbeb0cf4cd9398d80eade47c41b949293dd9eb7f5aa646ab0f2e8e17d3bdad57da3061bbaa50d914963633d8fe621b0c6a12d873787b8dde6069176c5df41c3263669624853a6fabd2cafeb52de8daac1c8e231259198a4944c9249ca5c244524261403ea5877de8dec6930c6d8b1e4edeeee9eef31b1983aa761b1450e0f8f121eceeb4ede8d97a592259fec69b0121165aad75a2b11be5697d5b9163918b35a8c403221b5773747e77af546523daf95c894a97e6f6579b4bbd9d33bcd934a228ce944264dc52ca62f9459f82a99228cea282b7018a17ce7d369547ff9360e79776d336958bcde15962f4699f6952fe66009b4ea3cd66075c9d43a4dd56d3e1d8cf3109ee7f6e4799e976fc41cdee25f4ffbe1bc9bcdef4d3add8bd234ef78384ee3384dfbed5ef7fc5aeb1c672dd5bc3fcd6d66c7f08d5102ee1eae657cc0c518aea5c4e22393583df368b0df5ebdbedd8b0074ac676d6d56a54a95a6cf3293ebf16623dd6b08af06759be556addf93524a339e55e09ffb3e703787403acef6607be79ba793344fc3d8c631c0d5bfd6f4ea767a7deadd9a59477d9abb577d6318d91d23fec431c074ce49b3147a49d41f8d021763f00e4b600edfebb776d7737aa29852cfc321ce9f3ae53f6eca39af1ffe525fbcdde18a3f9caf28cfad77f276452f94d6b3ecde7c1b9499c7d78a6fdf5df96e236007c74a85dc9bc31996629fc92c7515cbd638746a5bc5c2d4c419b1b3acb7deb9746b61da45ddbabff56edbaef671b3cbb42ccbb2cca79f3adf3eef0b25e7d1eb70bee3aecbebdb759ba97d99cd730551e2111f8b9ac0f34d807d2c6242ecc30570281bf125cfc7e80d25ca9050a4cb0e78070f1e11c674d901872c3a135b3aea530f65d3c74f4618a15422efc7a728cf3d8a9ad2f33a07c27aaf3b6559e67954f3d97d616cfaceed173d0ee7b97af23e7977729b9969367d3dcff372ebe43f3cb77ef2dcbae71647484c3a7db1fb709efb8074bfe5fec2bbe4ab6b5f88d3f3d56de6955b57262ec684f4e8a97b94513d46a49d4913693879eec598c5720fc12a83becad54f02352885688a5c18b00babc687428f4d47ad11e0b07ba83766f9ccab582fde4c8de492e3110c39043ff4687473e82b398c432f396421b1587238a30239240d2953a197432c39a4658a04438221c16aadb5d65e828434699a24b5d252f760016b9244143655990251a1068390264d0c1f520cb9671065a8e726f723c27311df3d58c09a906047485192a428f594a5a6d2ae8da969e6b40b069b50e651bbbaf07ed376eeb3113f73d55763d42a818402aacfa8742d3685492b999a010000005314002028140c078422b15038265484553e14800c92a64e725219a95910a31432c61842882100000000044064660201903e38eb3de841ee805adc29813239aff108b46aea00ee5368f5c454e1dbb9f50d57bcacfb0cea2a528930e4ed788a6b787fc9a547d295c13fa05fde8209b1d1b4b31b18c0d2f84e0074d5620afc02960a45834b953129461ec535b9b1ff227ea5b1b1bb0fc451fb139e3d3d7cf9fcc46621af7bcae73b2319fd2bfa5d579c55c560b119920c73ec05e6835f382d47a515a0394627efca95a3a41737868418ae7928a3075bb2fe2c3085ab012453ac01db100ca4d7c8479a082167611a8be607032d8e5d117cbd49a36d1775029e846a01ffa7b7658d360ae4fe2abd2224c09ef5e4da55b4cec642452c8524cf173a9534da093118d68ddc9333db22947833063262a5ed319885111adb9162f426de0ce1f5fef2f23e641991450a0c124102f647988cc78b6105367df783e8c18a5b2bd388dc6dcb96ae186344dd9846bf27d2ef388449c5bbdd88d4e22df253bc5dd8ee7668ccdd1eb90185799ef1f7740e709f224d1857d3d439b181bd49ad035a53a2b3b0e47e95bbd6a191ef2f02865bf808df98a664379f71e3c86eb00025952c91c0b6ac075ff33aad85955ea153f5bc27393c92b17bd315af0b98953d6578109979f36d8c2d1722902b6fbcd6a4a8c6206c1840278c3428f582a622db744a8a1cc590c062f9dbdf440be6240dfccfa2521539a5eb247e5cfe52b864755b016c4ee18bfc8b270dd5cefe90e627f775d64391800cee56576087f178bbf7202ce7d4dbc043c19d2b48b40e1cf07f078620170383aa926ce3f20025e035e8eab2937d6c7904db7b318654666cd318db12d5ff2ad4a7ab686d200eccd16804ad693a20e261966c463c0976c5ae388b48f38678aea05981ae55fdef9bb285ca531f962d8615e67f17ac558a2941a0f65d44575c2abcd72cfdd87151e1590d20d550492a6936104385b72c5f89120e414899551d3258a3c207a6c25a4ee824e15390efb7891f93137dc1121b3e888813f6f4f0b845c4ab4e5e11ee44d2396382101c4483bd9427d76579ae292753cb6220fc66ba96f25c4af9c33927d71056c214a98e4cab3641bea0245629bf827f8dd66c00d51df8ce5b8efbbeb9c46be5816462d20206f32d6724d5d2cd7fd5a142abe5e8d92bcf34df8b3616f3d6f21d40c4787a0837841e8412b6f300332cc9ccbff8f6833e9af4030e7ace7cfc0f2f78892aa31432c0fda978e80ebd95cf2c1dd492526f8b892b39bb70e8d76ed745c660dce61f0fe6513b1496111ca7d9a026293e724083133e1fcbca73bdfb96cfccb32028a01cd60c039638e1ddca8d9097ccd9f3a39c3c81513958f47648acfcbdb14e1c29ddeb727101e17c26cca1980e79595fa783e2cb7acd20a3acffcdc755fc5e16cac8fa2d780f581301f0933c26d4651ac6269920ebd9b4af47437fe14ef793b1fe33a216a4299cd92d70ef5b52707be2b0617d1bc2fa634dfbf489ec23307dfd29b0b0818a810e7255f6f05a58c3ed06780b2f55525f9fa32facb71e8fc95792bbe9f5d78dbfb04e864ef432a7050d8c896ab095a73a27957e85f559ef02516a234d68e6571b36bfdc3d6fa87c70259c4c1ca33af5b637342cebabd875af18040555fd6dc4f7fad8f5e6d6c5a938b94f69d17c87f76b2c9ca4140f314aecd305a28f5b599ada70a21e4b49028075b2040fe9045a15267dbacb7bf50fdffa88351f41172ec77546a6f74d9f4e3e3f8a45d959d04dd9c69004d298aa3de3e5c9544883b16560b28649b2639a1fd96ed15f5e4a5c05f57c5e8a912ace8410673565071e78d5ec2cc4ad536bf3022c4b7927d134c412d18385adebe470c510a352a18d94580ed352d0c2c4f854aa124dcded2f3db54e520e1fd49bd2bc063b05e5e0a48b7235f75ebb6dfc72da5bc115127e3c28e64b7bbdcd95c9dfa72dc6c7c7d253b43f6ef4b1ef3345486c545c5eb6d66f7f81a9d302a5f5f2730b40442f9d943b1138b11757fbb0bf3836cefa40446c5d2437f0040d59cb562949544de36150264e720a9f41b8b795ed73855d61b095c08b3d4194f62879def1961c56b551457c921d693edbe7c373fb39dd03ed54874dcb76a9460056aa0a9410eaabb8e8c79114c106a71b5bee9a6b817cb4f2384609e740a3a1640443a228533f0d28740ae804a4742d97eafd3787e28d1febeac80ac7e84e94b99645a1017a2793d1cc1315991eea22a85bb90e9655549a3e071505acde8f134da212f6032bbdf83a948ddb65869943824b3d7b8ad3d97dd09e1fe67db27632a0b55709335dc225ef195e42689cd145afc89c438ff4f891a421846edaf3264544e2ce14d98be57f08318c7cdc85182118b7262ec99de23f427ae18439596f182cc71602acf8613790191ff8ae078548cc08e38930d238a5b8631047b93bb21f5ccc405a5f32ec76ca8a68b808b1baae45c2ef8757af4111178219b7da8640ead7e71a910af8ce4d154d0092c56180600d202c9bdea24b63fe169c8687b6e4c453ca7ad28cd268d4bba08f946c69b197ae7b34c082bce29ab20091aabdbdfd12b480186cfbf3ae13f1a6914dba65890e221471a8baeccad6aaa1b52800925cc2ba9f43e0792c6c0805838a679eb1281ead0cc88bca21e426f912196dd3640210e1ba90a4e139c26d1df367aba3f7cdc9125ae2c98835df7a5b1a9338fcd281b79f23b846acce9b14bfd864b23bd08a8ebac00b0e98875812c564ed89b9274597bc248cb9622c7f1d060f3e7ea482866bf09c00538e2c2f99fbf15a272e9994e48b39bc0641943ea055b823c30ede930782df48b514d36f7cf13373dedf114aa6e26169d255831e9c1886a73f3a9318fbd60669138ae6991ea7e9e5ee92352de042ec0929b3670a3cf9d8b0e87014f1065c3d5ae2c7a85b86c0e4d1018ad1d30e2a69c7b6fe22ec02d63103e6080efe139d827da87d504c1031f9e6794744e7124a99dd69ce2c524f9c5210baf1de46d238130d9d6fa0c5dd2a050566489dfabcf8e7a31f49c7122ede3eff0d22ef77e5ebab1fa73440300f5a5d2776bbeee6b6556c72c62da6ad3db5bbe97ea1591a13aad729da07506e8c7b3ac47d6bfc22aa650c019641887bd26be4352bcd163b9489ec5c2afa56f4fd5d47a8957375de08a776aa55aaea0149de260e3d3f758679169f7a1660345fa4011fd8a8a224219fba8702a1a36b9cfbd49af5b0cdb1797f4a51c97f25854c75cada1a46bd7dea0f904c9af2735dd6c89067ac688c39a8309a304949b44530ec7080004c4a1b79f5fac1c5e1a86291d578e38cc7e0d21afda0d385693baa44d32cecd50ce34b5b45ece47220b55c577071c037c1d477922208cd3bb51e99eb02f3f1c9106135a670db491b17431ac0425b243a9491e24d2cf1f280e9319d37fa3ac59e56980bf8824f6866b15fee8c79d9eff338d83f0ca930b75e2612728b937dd1fdc54c769c27db36fe13b2b75ce034546b3a4d15c4effb0f057920f801836af97181d27ff46cea3cf6d4a79e9b886ba37240bed7a2b6cedddbc46c55f40e2813848f9d6307f5a5dc06d91a901211492fd76836e620caad77d188aa030a1a48a6509444c9a009d3bee5a2c8f487149890deb894a3250cba83e712afd090b22b0e4276a00c08a98a65563339c8ecd4fe49267bfbbe23a6f3acd030a7a7676f429f07525c0dc4ba6cf709175215b3754831d8a010c78821239023c8532429b047472a35c468f70026a932e77bf51f44034cc4f02da70966be349ea8ac74d142aa9c3e871b7f4a5b20a528c071461e747afeb39b51ad8945f7249e3955065dd1cec8f7082d53d17988abf76522b2d32d121757cc4a72ef8b7eaf417b363d911163705ace41e79dca7f3be205ff86346a95431ec4e708188443e8edb3a2549f6ce625856cfa995da85c7f45e8c8d9c12ded98e76f9c3a0681b8fae6256e2b2013779fcd9b97e2a785cdc1269f47a19f5621b16c69f5091b77c3e236edd46ad3178840c2202dea1accceb9d0af46cc8bbaa05079c15213f8417cc779c130ec11f98d2f2b8d32c1638462dcbe498b75da2cd627a04048c5e14756b2680dca2d76e7865d0b774e6623f4a654a6b369260ad8b72ddf1398dbb6e6c3f2cf841342d933bf2e98e4982e05293ba5e78c0e5e49940433cab9394e3a44f9230113e8d5cdf23262b6c5a10675ac26ab014301e20e990b618df6d0a04da623036e1da8c0ee677a5b2b904d04bfaa51f8950b7240d2d017712d651a69318880200dbb3822036d4f84c574965a91ab2a64a23aa22de87e8fec5b29b7fe22dad4207fd1c1a80feb55e078204e1209035aa2fa8fdd28d7f33423019654cf338594f746ada6d3533f82535c204d6e95954e870da628dd7146d029cb8473211e40fdb62d4f3fd824431be5440227c2f7cb0e015e2e10d16cd9b8eb6f23025fae1d71f25b70cb36a4d2c64192646e3152c85ec2a08cf62c6b7aee33cbde6dff633605c960347574e73776396d88bc84855253776d802812d21c669f977cd93430dd4640aee7e7d6f85536a7fb478ad3b23c42d7c5496cfcdf02f8f7515aa07204cb92e86ad02468fb3878826336b3064725aed1cf71209098b05e22c418661c1479ca243d2fc672c70b943af5012d709d60ab9310b9a4630ae8dce9c019071cc38957d82b40f8f89eaf87ae8ebfdde3c89a43d408a06706da48020680a02e792117e7df646b261a987b98c9a6f05403b33d6390728d84398abf925e8ca780a88a88cbd02dd3ec3be5d522b488a6b7cb7331667202e6b8fff75fc81024dce4a8a116e29a2570ce80897e28a3adeacb85821406a0f7928b2dc10704a6e021c0676aed92d89cdc065b4bb60146cdba5a44a769c44aa1ec3d87f6b961580f40e14cce69664b9612227b983210df05daa481d22138b7401bdca5adaa610de5597ccbf1d35443bd225b30913568abf490d455b34ed66d11ea0f1ac73787a45b206f61064297841b4b9c562703773051fdf1805c20acc9278b95a0cbd8680462a1bf762fc99995f17856d90bc83448c5270c687575abf8eecd83d25c3e1d1e9fb7802b660973561cf65c93c39314345db02cd9075239dbe6076c3056e640dc3a41f1c6d96d65deb9895a237687f54820d52b679c792c148cf03c2900f75c05a996b5b8487b9e95e02134bd37ad339b3f71fd5fadd0371d24932287e964e366c46219edd54a4f1df60436f51d5cd824325a2c4e95cf46681c4f496ba8c1ee8b8dfcf658cf34bea02e9c83d9edcaac44bda1ae62ad07d72a36a7b9864e8d0cf92c72803a2aa8207dbcbcb8f7a6e54a2f2d3e7d41e6ecbd90ed565c764e5d9d1695372906751e4ad1923f762cfe0ea5aa4b2c638517a3779efa0285e35700290c98b11c253b6ae85de98f19a074e62ccb1d58b95f7981a2d7dad2ac739c12c5bd2ee9f2dd8047d1996c0e95e7291713bc7d11f9dc20df43896b8ba7ba8a6adbf37735db4273ab40ecebe0d46cb6a457bb82a5c26477d4c86887593cb7578f2d27c1550faaae10bdf206446b1f0566e3b1ca9ffc06e7b7218efbbb15d784afa25ea8e634bc6835209d71735b778da5592e1770be931e5c5ac4ee7e72f90f65573019c48212a10a97eb7fc398d616a4f898c0e6e6c860d95d8cc86cd24d860b847e11602d1d04738c7961a85d34b5c93ec0fb2cff5dd9e8a188a40a6748659cd4c17dcd5a54c92b23677afc295be85d604cfeb4dbd392689e2ec8424e50f0177a1f62f00e42147a78f0a1571eeac31817fd6e5471e04c0fa9973a335d82aa45926c4d4f8c5481fed060312735b7b3aa00e32340453fa57c69c77c175203e836814f9234470ce570d0efff966ba21e5372d694c5d89e7dcc0b4ae6633dc3eb1f57f414b862ba4900dcc6170aae1d38c447f51882c0263db00d5579fb0c0814380a15f403b90b98c5ee30e2bfa896be45fb5d01c5efcc42bd89ab7622492338936a281970bb1d4740a7f069e24c11d6ff089becb221a2ff6c904bc289a3ae5dc438085259ddfd35a4f20c260db05f561c50e2fa3384e6261c0e45ed7c5219c585facdc3cd3028503ee85d13761a8ec9faa64180e530cc989833a016d0b786633ef29647212894d4979a9c5b736bf57c0e8105806debf20a31856463d310af5f3f349bbd3efab0647d386b5592793f62985d231b33ed5008b480bf7588629ffbb57edc3ffd1cd8480d7672993bc9d0316235a6a8d20a67f433ea9bb771b6a2d1980f77f465bf30887f00954a564e580c0e6b9cfd70737d680c186e57487aedc35f72b5bc00c97fb491efcdf5c17fc16cc33e9f4d38dee7d26342f51d33702b00903cfc4932b19f8d2884af85b3e58fd298ea59094a90a31d9cdff14b298e6434638aff029def133714e0bd4e2766669dd4274abf3826c50c9fd4ffb6936d4248a19af89ef2056ea7d01514d16c7ea0537c996e269e79548ae0dab245f11025a8d1069096915f966614bbb796b23d715c04da9160ba217d19e45c729443a6dc330a05e32878cdc000ff6a82ebcca021084c56069f9abf2ad7861455091ed0ce27d47c6599b3bdc992124e57299a4fcba04f3cbe6474b6c132bffc8d33238147a820b638ec08ceb4daae08b81ac302b3ea0ab8a202debbe46e0a9857243041d95df293e209666acf00d20cacae1cca2cd8e96045d98a3703d0cee8cc3433c87f5970f20376fb8620db21b76fbeae189570bd5c34e1d8da8848d177451033e324a8909e5de1950cef2b9d18030b57229891caf3517a45fdccaf43ccf025f724054e34045e90b7066fe24de80cde5fb77fa179a020394575b325274d6d55dc79043ec1656111eba75dc6029be9bcb6d3148115b72999077bf02e13fb4bd7ecf20cc97107cae742582337f35fdb74d61efd0bd1e5d82b69f2b274a027fc2ddda40914314b0ae803ba5270e6d034089a1ee2f80ba5dcd7d0c8049a5789eaef22d563be404499daaf98a721683ec92aa377c01951632c7f491a0aa941e4df20f92c01e6917e3279cc276ec03438fa0be22fe3383258202c751fd2643d55c106b3b06c8a3725d0ac955ea4b223e05fab0bbb51d9d5910bd5248c476736860d11114ea2acf3258a4ac39d3e548ad58d41cc0c237ef6d5524d5b728513d29fae1b79f46c2ac8f7ee0816e18386ca748fc032e8f6f006f0138004bfc07897a6325903b6497e48cfcb360ea02fd3ad32f00655812d5bf20051e8954cba1ce1086bb450880a4193aa2e53b296fe4699269c19df1d255d7fc99b0cdac5820bc5fda4214c44c062493047debf217ea25811ad9b5de12274c85e7c4900594bd82f49f0ddddc4e79abe68047e5cb13f286b3e8086208de8335c785e3d9c20194275f88258a7c202676d6c89f1e3241eedec0df147a9bcea1d2c39d1a376b3e79462bc59c7fb7e501d83d7f22436c9247ec506a61c7c912c51a8f6a282f0d995516e7c0195bec8429968655a7d86ec5b4825c01c70dbacc40e75f89a77d34330e5432a0bb70193106925a0ee617c838aa9ea5cc5826da6052d0261cba0c724d8dee16dc0a25f68ff3c50ac7e55c2d8c16eca86ba75c0d6acb6d733ce3591a04fc40d5a87038f770e9ef70867e1c8aea175f8198a93238bca4929f6ab887fad563ac50a2cbd1b53e46ddb0283ee0409a57e62347ed16472f21beff3726dfba25d1003839704c343ed46af7c362a3777e9802dbec426e0edd7a181351cde384ef375905d15e0c0292261aeeeb7d3e67321ecf79169c9fee1aecc4d5d72d53e106c0734cada0591b42b103b012d72d82df4b24351b2f05538dc87a2543b50d344e93501ad43ef5097bab87ff2f6688f953e9ce6166435f88bd3d98de84f382751952ca2a18610c4725909eab59f19a4349d2f1077a944445ad7e22bae368f069e4e03c7bdfcd521f8b2d30f613e03214811ac9001358632f44b655a3a1d17c1bd2883b89f0fc978c02f0216cba9e70ce2bda4bffb117c43eba19f9872f3773c17d715ec7b7048cd5754277887d1b10d942af51a7e39d589fa690533663665efaf4d039ad86a687fc211e16c49ef0653e9c01ef0aeaeda5e0b96af440c9dbf4a764ab5768deb69599e811ac58d38662b0e305626c1dbd3e82562f7fdd1ef6bf5aa7b62eb9c9cfb2447265951770cef3c638ef63c68e5163715fbf11cc730792ae34f638553744e1734a86ec1ef1952591c383805086a51207f444a91aa1a83ff4221e93bc9599b90e7d771f994e0d566e411a44e707fcb6b9e8e0a56df3ab4242289442369817e6b7d8ee82e963ab41bcdde16761bdaba60817701f38112c7bfc8fc354b30a3f9ae2bd1d1a16fa59a560b915930aba19f736996ac079f08e294b95022280521e6a0e4999067f2a9083e5973e7546c1414b65b1836ee71e5bac7e6fb51fa5d2ca12114ee93cc8c1c5134226a4fb89f993d4cdb7c3b865e19c88e7d8d710492334b1cccdd0804784aecb7cb621b7c77e3818e670132923eaf6d735be3e1fa3a12d1a42708bb84c0e8251db1749eb5a98871795b78608fca6b908c384362b2c457cfe41f40990075938ac53c0a90bb2688246ea7347be3dc17a147d3928d4e05c6f64dbf0144b4885d6bc9000df61949dc389b315d527b7065d33e4c5035ebca4b20fca91c1a5811968d3dd93c758deb1da039ad3fe4742d65fab3b3f9e3ed61bad9148ecb9abb7f14386830e6052e240113aa01e69b7c1e5a77f857252eaf9de860969945e86b66e5c1f2b25abe5ecd3d81fec3e71032a2afb34cccf5f6a88aa4c67df0d8475cd3af49f2008edada2c1b07e4dec3464854aa8fb5b4540ded46b634bf1bdaa69cd8e6c9a1ffd0e2c21d50977e75ab83baa379910e30a76d833aad38a3571a4374ff6e2b895379a01879e99195696caa14087e2060e04eef40b8bd72022d705f171f513ffb46d31aad4b45cb78ad4368ba83031147a9c5717d09a9a4456c07c11543a3d989376d3cb9e5ff4576aa110aeb28524fe5a27334328dc3e04f3e322a20ec8b3856db81c4a2a7ceed2773f4468b54a98be4e350e578367935a628927f03b272dc565191325cd2e24f45b40a2d43e539412b937880f0f4fdd5cbfc39fb51a960e3ce0dc58428c455a6799f1d7b2e771eb5e1a58a9d8906212a685ceb17ba216fc671f39f82fa4accc98a08f5d269506016c9ce02c73ea86108b140c4d86c063bc458bf99becd8a00f1fe4810458d26e3bb301c9bee3069e5779e8e2cde37fdee0f456a87d497c4174ddf0daf7c2599cc21d88fcdc99c8af725649bd8da315d22cbe6077583c4f6e4ef5600901ab106b4996b3fd3abb023fc5d960db6705d347124608bf2ca5e792f3c820ee7e6d5d47d10a171ecdc1ae200c2024ad581684ba999094edf385dc1a0fc9730184e0c8bc0e571c0554f88403128fd876a25c7af82a81acf837c01cbc7c0d09c0926372d5cc68fae1e36ecf90288b000fc48cf096ed170a86d2cd5914cbfabab6085f217218aa5e182f13d4d81e8a20590de96521df74081abdd675ac0023b0755a7334d942513f549b37fb8d3e4d0f2cd613575e5dc6c95de63f8423eecc744f6751638d577359b40ef1790638c7c19517f03c1a57c3a86f5a7b4d9a315daba8aac758f707c0e472d7a0ebc0787e5fb098ee63001d91de1cac35de41b9718fbd2ba7c5ba6df40cd81bd6418f9ec28d275ef32efdb55949444d9eed9c3310327c520ffb04c58bea517fb33b058b2078ac69b7c828ca122566c15f0e51f4dc1dba2d014e0793543f95f18ba5a256e4c0eec81ec5a4f5c5f0419c658dfbd898d42ba1c0854e059f778b4e32b20489dba8ec268eb0ffe9f5195cc308ba917eb1269917ffc8a5dca8ebfe2bae2c6770dd0904a2a12079a39d2aff55f58428496b0112afa6c6370d02e3c56b82adf57f7efb656b2fe3b9b7359c0a52061bff34c8e3c657b0d2278cc5e929858ba05f8f50cf5b1be15584124319491f81a4709a95057c6f260659ac175d47f8b2908cc44d2f6dd841e16ebe4e8969f187d956fc0321098662da82e8a3449c3b52debfd5718704e2becc8ce0067104a86520a0d4e15234a25d95a32fce03e0203630e49569e43cb6e75c1c56ed19e7215363072dab6e27ba3fefa409090c07990d332325dc16ab9610d9881326432dcd9d2da44978a5e9b75db46806ee2581978e3037e2ddf574452543ff541d8f8351bb784a53d4004cd2b702c254c93c014e6fb813d4cd1c8e52e88fea8a0f02f8505f37c5aeebfd4341315b627ea55d2e7359f97172495c9efc4528bfd056cc353698f82ab26b9804964b1312de60632bfe26eeed6bb08c9445e0123adc6e381e50c8b72be779746cba3710a98101d7260ac981c53fd1e7897749ac7521ac5586679e97270fb20dc75b5f5858a0f8c62ed82ffcff3c9d55f4dc2cac59684e60d09cb6f12971647765fdc8c0b79b13f446088298b8444915497486d7604c30771af05de1128ad89a008763fbaed40c53cbc93fd64f0a293a39c02bb9e26f740a20ee7a4f1538029f9d28dbbf2e9a4cee8a0576333a9be2efc284b189fe0ec3185792aed3d5bbae3004c4a8d8acea8a5da12a544ac0149d310e72df4cf9cfdb83b70cf27485a05750a430b8722d06089d8f88ecadce181ae9e20bd695f4f50ed74d45ba8f7e3cb4189912e9b1b40fabb064969b06d495d148572536ad678e8ae1138939a375b6b40ced16300ed497b5e5d3f766e547cae9c7ee97c1fb4aedd63e2ff3ac1c5f7b8f6042b13f9567972d38ec4ecbbad75f1117b0f915d56a085404d0e369b660bc9fd278673323e50d4650c109d2ca2da8b28a00ce03a78cdc121807bf2e38c5cdde0b418d76689802d23d789c4cd0ff819554c969b2f257261da6cdfbef7c1dc4c0883b707fb63f024a20592bf67f866d3f949d79de4ed9dda990acd60aebd473e3f46938c3014b6dbae91bf6ee02019b09e29c0c8b876d3fe67aaa700f5824760aab8b82dd3deca1cd9318a65463630fb1fbf542e72468a63181eb5523a7cabf2ecf75dd8856c3d41023d7e1f6146fba7c5993d0c92024b8b7cf1b1f53f0316a955c68bea6928636d32073a7d8a4e3e9d29a32263f1b3bcedff0724f457cb36f861f67228102663cb34686470207fb09a77a13e593dbf8622ca3c6f00e95fff8071a6d7ac400671ce24dffe66ed9dbaa33a914281833e4904fa99123f068522eb4050fc9bb83cd397b9629ccc45d5fc83245f10bc9b431c1fcda4f52d8c6dbe7f8432c39cc7ba4c7555ad11b43c613653790ccd3529ca4964900891759fd8392098428753ce1a80ccbcf826fd88f58591c4c7f46e848823b2d3ddb504243700ac6b3daa0d13f02b0864f548af43c1453671a1f8f9cbd2be96d50bcd36db9bd0d62746f0fd2f8e96bf223bae2d2918f4c05586b916387814dbe0246f86df18fa87a1713dcb60ef2518952c7082d80809109364a82481c7b0fc12e054886e1d2426c63c12079a01083cceafd083de16f9da68ea6c1a2e4050494fc2fe4380499332047758cd6356513b4b3d88bc6a5f83ad7aec5b9239df0ac04f92ac368988dae5d295164dc1869a11d209278d2fd3e7e585e15fd5372c59c36d2133c4c29f1879b384747effcd32df8db46ce852e45892455b55ffac2ee519666bf316b1bda30bf5a836ed4c87388fccf4ea1dd310a8c0627143b7f10f3f0a93177d03b79950d6b42138a68e22a72e6e451d9e0694c45d99780b9b2d74ffa3d18a362a803b4c920532bb2afc2f1fd8c8ca8f3d29827040ab453c8202b09940869c01182bc3c3d5032c050c15955b2e1c06a424ff9ceca9f6eb30d27009e51930a8c958771f541f2fa196836b69c871d3bb4a8f95d21337ba39786ca75a39da62a512a226edd2e895053cb6ca18d7dfc2eca7278ac1088b3086a3b5a66ac120c786eced96877a512479c2c3ede253853fb8433c5e7bc91b495322889c6852b484cb2dee10f2304d3be15e96cf99821b8c9ee9f2ed43708af47238f7812d66f672ce85ac4b1eb256b4da5abafd90a59dc87eb5da4c3a5118649a24343f83472f7af1afc0b62a64bb650e22034a696340b234bdc24c5840bf85f04a21d8b89ad151e4bbbfd4ba8cb15ec7c91e203d89cda6a57e4a00fc1a441523bdb4a9ef087f6d8a8c30e8ba0ff33194ddf446b7118c5618eabe57a2801b8040bccc9d3589de4b4ec320a8932e04b69a8a6d316bf7df251c356d7ecec115b6f97888358b5985516f800a9d2ab2cfe078f1d5514ef5f2243b5beb71455d5d9cbec03c3edd5f3d319d05e43e46b9ae75687b51179e326e4cbf9c6560328a3e24d573c1ed9c9e006a117c258889e98458f44205678a3d29ba242248817f794a9141268930b6798107bf759ce47aac9f3af05a358a31b03464e556dfc21949a0dd2939040e853f0ab9039e5fdf17fdbe4546b58df09712571901cf552d04df4d8093be27003fa00daaef74cd7fbbce66a5b626d050f999c1880a4fb42ef703474337bace2b5f1e404c04ea18fc2f71a7ca935b04f17d2d45241052a49505a33a802b44dffa5f4b12d46c8c52c358203b2d71d7868dc97c5c0123224f7f2d63e24a3c4c22a1335e5d2dbfa445835eb8c3e4422f5a2116c5d90cce21365057c1d8e58ea10ac57679308d9342b613ec1e14e00ecc8d5dc0855f377cb22687d858cef947df73d516ba702d1dc813ccd2175004238cd9615760e757213f94dff00751c98fe14ba26f3982dfa78dc2941d05e2c46546f12cef5c793031b02d4cf042752cbba659381c14e3de475ad90dcfcebb0d28b68a284455c4501851d0e486bb59addfb2a3cee89e6d611270bcd49fdd66293f16d168995d1c4bf83ab0a838bef3517122df71613e96b12210d1d2eb1a4a915b29a5203634e8ee63c16c07d9e7d1b60fc6949309c241682b03d79fe626413727ada262fefab412ee3f4162c8905e0d6018bdb7442afe4c9a63c4b2e4761138a63dcd8e164b709948a525a8b7b4b2a4a9b6692c1b4e63d7584bd0f6b5754d107df8404f3f357228e86cd77c7a96c5a117969026f5f4b91b0919c3a276759956f54fd8ee8919914cdb6ae2919b02cb6b7525d57d1c9df44afdbd8cdd1cbaeaf9349b222c4c7d2b35d869bd54c7cf6c431488eb852ee24eee7436bcedc0d58ee927dc361b2d694c86f93cd124d1af002e4e767b203c30c52af7ec682560fd9c7b1b61eaca2ec2ecd33384ff759e285474538babe673767a36fc85ce44687962439ce2af1c751254360ee422950e95a7099db855edab2a73ec4f3696f98c5bb09edfba12cb36dd5673d5f79bef34c24853f8a49f31896dc9e665308454996ebbb0a5881a4a7ee01c4d71e418664cf8c2ea5eac24195bd04933138d9201b4d4e378859cea80f7886c6cbdcc69c154e90f494cfb7f63423a5027acc7101eaa0f66cdc8ebfb70222efce8816b8d30311ec6ee9252bb175c11da1379354b37a106178839f9cd9e77c4008b811641694ebfa38b39c77927d010c4c184c5a5c3533761195ceb8f2eca99183ec4908e70670558ab4dfa2116f3883ae3c845b9c8dc4fe1d46dc18460cef89e91de09840900a98434aa31696fa3c488a76d0d3eb4bbf1c2a8bb96087d84f7beaae86af999dad6dd9a631320a87ff9048532e45018bc6caa561f0fe6070008137d2e9912ceed4da6da661dc86a09f9bce8cdd2688b892d311cfbce3cb455733c105e5f42b86f8aeb546bba3fb3883acec997c03906668d05d99eb1d00724045a690136c0f995140837bdddcdd303393055c2dd36b82b3ebd374e40c3d02005c8c077cf489534b5e5cee1b6b86d059518ef09b9bf29caa5df4150a7701951d55349cec0f6662951326c2d583074b1e96e2f86fa77cf6a2835dcc02fa648919d398e8fbb6df2c6cca1eb3e5eda0b650ac6abdd8e8bb6a03cf459e53ba38b807bfc5d8c435ba1a5d8a5b5e9e82cfe77b275e7df3e35ba545ad44839d2180136a5b3f3273c494b96aeb28b245e3154d9ec80fd0873169d6b675e354c85483b42bc13d947839e505d82d134fc0c4d3098d1c98f84acc4b4724b8a0186cb0537548fc6312ca16a21037d6139c8d051cc149641ebabd8272ccfe83b31095eb5c33ad23f4cc70daf9e2ae1b1fc6f060fd316c323f9bdcf7f1992e2615081ef832694c8961b8d31bcd4eea5312d849974e99dfc5c9a7f2a875fe8ad706fe18a938ac51b78be09896419e9f27f4264c877fc90e619633673b75cca253c268f3df8be16a70d2f6d099a4e19f3feede03892d18350e3716e2bb1eb144b9f432b27ba0c305cbfecfedaaeb2aa3df2ebfd7610239a07ec2a3c7b0578a10713842c9674ba3111cac0428206e62020ac3cd1d5df8fc873cb0ef7adcbd5551f8d93a788628f49f573e8eb78bfd4fdea2a2bca5e2976d20af7b01fde58b577db83f6fb38be3ea6408118ccdc420d76f6ceeeddde4058ad0b5f73efee007af8963129ef37ea84bb97b64d3e9bf504ceea4412dc3de6c73fd05f5bae1946ed7522e874c45ee7d306ce51e08a04e8501eff2d70132154176bed0ed9da1f1d523274918671582b5860b8c33cad6adc2d24cf4aea12ae7a2d7010d695bf7de7323dbddfa8a3bf4b5c759f07c7afe5321f31a8858b598e38c382b9ec48b4bb19a8a0bebd32b648a4bb5818505c2066140c4cc9e8818540ccab538945b8404c0360d6dab78cee409482295b801aefc4d8a431952551ddc124a658301bb61079e6e6de45ba567628705232dc84b68d2bacf44ba01f4ee2a412a05097408706636cda827e32c6ed29b6cfd97132e5ab76ef32577649b1f8a1ac2365f2d217c758de4117c4d5ef249f1b8dbf2f72f4c637ed1661cbc6862e5a8981b9c8e6ed27e86070248eee6a25d4663074ad0bda0e76a1c468910e11256200814a2321569ca2e7924325249c43250cd2782887ca20fb49bf50c62eb74a8ea48041d981788db8fcb6ac88ab11a7bb1f0aa07ebca07ac603e3bb17502d532682b92dc306cef387f2c8470bf93c14eb1d73e011a25136845cf8592d2a4623af13e004eb2a017c2e447858b519fabdad879914441a0f8bd37f339d20177e9d07354c27777e46741d8c53daa6ba0cd57f39bce8f891b598c4133f6b2b06b8999b86334152e94d5d42f97e9e09521c24e6fb74d5f89d4b85b08ad91ba91408c3443e9cb05389f1ea6ba46438b76f17233c0d0a6e5772456d5ff44f9aedd086a4bca27c6717cad86f019e5a51c587792ab9b2815546f81ee9a60e39357de46b9519d9cd4e6e7094a0255aa13d407722202e9fbab66cc3844faefef4622913528e1b204921d14b5a5fe85c14fa130763b73351fd2ca8ebd8674628992be6b03c217ab0a5c1904f66608aace9628728f24ac25ab863d869bfef3f0ffd928cefef82948a01058b3888a51108308ca6812d053b4a6345194288b8bc3b473003e75b0561bdbef461491c696d4cf92e89b5692c066204a34062e92932c8ae8ce0122f6874033a4d620ede3ed8c133ee7902b4fb00c9914ef42ceafd0dacc98ab9aaf50695d053d6b9d989d81a2aeeea49de11abbdec401ad8551a8e9d7b9c8c474eee5c261781c94aee458372a439300ccef50808a598d960ef1d3a7f449d3c46fb55f6c22aa49d9577abf0ce5def07f4f08588cb749ec24450a3453b7a73e9ec8cfd53ba8453daef4970d698dfffb49c8984e3a20a93386edeb05168030bf01c97a46cbcc4e217d164d27eee6445e01df8e2a64537e1e3396ac25a1da9634b81059c70882d50f0cd3282221022fb85b87012aed6acb5053d914c7d5bf079b67fd146c93e33f5dfb760aaefb95db728aaf220bd0f50db99cb8d6f140bfdd983cb22cbe5c7a2024c2d91088347a35aaf7345f2eba3b767d00fef22eaa103181b39ca45c101fbd2c6c9bb2a219a9ddd104fe4a46df44c96e7c2fba80294718dcfabf682c1336aac7bbc7142bc7159a6ef068abd85c032789642307c8327ecccaca471a40112029f7394c09701a68395118cc2f803cfadc4cd487d49c475f90a4dd32ac0cbecb3e820de2492df9bf789026355771cc202cac66029c9aea716b98ea492600e1893bffc5e3034e327482171f7ac36cc234c6bb4795552f18214f95c9c1d8612532573ed2c072786517cee8b3f7fe6980b30b917d32382411898f2e80caddadc72fbde6cdd3695dee7637c5f376740190b168bab6ba0121d59ceec09b47d3123be4ac773ef4926d4ca89782b00a7da6345df7553ecbcf031a72ec3f6de42111b7406036de0013f0968adb3504d2ce960f52884ee29dca7630d2fc13e3ffb95ee65b2d61c4dc9cb86eb89913aee8a3783e1f270333ad1ea07dc1b02a0c7a997c9456871320d4a985f81c22071e30049c8c52ab11c305c5bf52f97e2c7256afa0a0f7a1db4ee7d684bf82380d5b7f0dfbcb794d545e23e661c18bec6bf4b5718e89c1e67324430e9ec73fad09279d47b668ed1f21aa1428307843125390277c3b36a43b4d1b4d4d201aa7b5e0b059455e7548052243c662ab1c8fa630356462395f473717432d2c79d1891c3ff9260a2d0069976da555ec620e2900479ce55f29fac45ea06e658389dab3e685ac41b513c0c88b091f24d1956816eefc4d14daf23d5de22d5def41553ee224d29f292de124851729796f4aa37ea6fc5748ae2efa622f51ebbdefa4f49006f10248d05525beb0b3fd05e04c608782e16dbc1c2a2c2383e33c29dc214ccfc9e1bdfc153e5e1369c13c94e3d33ae7aebe02f687cf56be5c0bc81f95df89418d21ba9838b842a5a24736ae892252f0784d4e6f2b83dfe65d4575784fe15586434f8b31852308157369e812e3981776fa745140b12ae1a60155c662fa144d75392b139fa6d46ae16874bfcf0827a279e9499778bfb05c368c7b9384e9b7c7f6972860a34f98272d1804e19ca8b75a4005a0d3f2fec8be19697e7b5bffc0128006ff39a87593befae335a987034f4d55765ce8790672f8ce0fbed078de18c57712027e902e7173337c4564bdd27adbf5105aec57715faaf702cc655d079647a2731fbefa8cbcf039743344747177247afbea54a4f367c58a6e4d6fda233c663582d8e5f9a939091d6307ef889cac7175a1dfdb949a6eb87b8defad0162dbc900b0d04fdcc29f78b124c4ad5433c89a578c973a032307ee59a5a1773e24a545802401adcc64a18beb187b7267710bb2b91127bc1de5f465e370a7e66c2e411657565b7049a72bae06357cf4b880412b5f8c3c5b56ab735a1a7f3d30af730e502f6afcf2e990acff136ece9fcf40de3bbfe2e01a89e95f14f1efc40a4f04a8ef4bf127249202b5ef741183975ed2137d6a7b72863609d80f2eb6e1b6ebb9beacdbf0c1fbc7de317d729d767f1a6e7c42f01255ea39b48e94d11cd527547a9005d7c00f64b9c4daa9bdcf96a46512f6f28845359b4a6f2f47c29ec9df132a584d8d6f4540531b8ac1cf6c40db39eb715bc46328b90c1a2f63b53a7dbc8335158c525486c05778d277097863aee0e00327bf5c2b7539182beabe4afaa8ece8dbc265a5f1f15cf5d121868af3a648e6f260cf7aecd73097d21442d25cd348003ae6bf3b0507301c3dd3b7969ae053938be1db8fc80eefeaa33f6516851f9a6c6542d82375c3e11078ebd6cc85104279680c27c70a05c467649ba41f4a569d46dad683cfffcc28aaff5d0fa1dc6d29d7f9aac33b0b986d5db8b05909935a98a52db43c2b942e093d983302edf0b71742b8481719c260771ba365f563263e74231e3e28d2753a8f207818742ed5c39c5e73da2f082fcf4529cab5e4fc2a33e2390f4d2d4fac22fe80f0c2f44902b3924fefe6be2f260a96756daa5e99d6167c919052c9586fdd74d654070bf2df71114cef74fe41b092d7dd74af0a2570b1ad6459433e700c2c436b5ba8f044bf55704889aed445ae162ab7257d4b48c9ad16024ef26e4e229aa273c89a6519ce204d68eaaa826cfcdee10fcd6dc194089e99967120a7e5cbe842a990764a9a6bf0180fe479470e244fec32b096cf78fefc5cbedf4c293263d630f5995e12568bb603a4a70df9279b8ebb1c54cc414e23006c5325cd461632d25a88d62b259f7488f30670a4f64edf75f05b33bf672dee11041f08d6313c4052e9f930bb83bb211401e9a6adb019a436cdcac837ea95503246ef3fb8250218ed30e140ac9503976e0a89470a34a775401c1a3742804eaa3f882dbc6d42bfea95d0170ec799531793adba165872859b131b5fadbb03023c3d31e36603403557328453ccae9e7e27793422be638c536eeaa7f3b2fff30e82d5a7e1f8e2ce0e2735ce4dfb4335bf8deb91f526e72c4a9a4106ca8a4ff67dcc557c40fd0bd90df45da5bb767a9c366bf68b28efd570fec9e513b2098e0ce67b7f8cdb67ea6079a528c2a128f723c323d378400aa0a58000f111e5022cdcbb0f4df41b2ac6932c213288aa7cc83b0ad91e2e22780b20d472c544d638a22464c8bed5bd15cf255ad164643291e7809e71d09ea905893768806b5b847a6991b6bbfd20f37a4ea1aa9caa80379b49f8e8307fca10efcec6b940f2722b881b05770c335ff3d5cc06861ae9f93d67f7e4ee54cde185d2ecc9bcdc0b38ffcdfd1fae94d76987abd7f3929c7d416d5a32f60bc15f46f2dd5346f3844e66fc06594c55bf5a22f38c9b14e6c6a73fb9362713397f31b2eefe9d29d730cc5310004a9daa7385c69ada6796b4afe986228ab36be87cdfc0bb6f28cadde4420d6e00f54216bc6098572fec0ec8e8f2ffb05e8d39be7f9f90af0be562bd755f275123149b5d4d64c7dd5ebe0d6f16faf0bbcae9e5aa158797eb21168330cedba0d978a8152e9840eb4fa58855973265f94f35e37fb4203ea73557d5f8f5c17b2497aaa26d3794ec9fd03cb0a183b1ddb6519b673ff795ab283e09da99c7c33ca792a40ca13f3f82c28415adc9a1df5aba35067ea1147250e9d5642a7868f7b3b66e629016f4e3b6a95e91ce67e46d900dfe032f1d6c18dc55ed8258b7397d1b05a3b40b13550c328e4223c5a8da0669bb42b85319c08f45eda1c8cd449afa4a24b72381f13d1b9801e7521ecfeaafc66513b0d1d7b407447deaab0714a9cc7eeb81e801722b1de891d0cb00d2718d89a27989105fe19938e88f55b8f8d5d12fa1524dfb380aad489dbad325a7e7c0af5c79e424edc90f8263e1e821dbd208544e4a6c5ce65ac66ab457e42e8d74598bf208780b0043b540d0c6aacd0a39e532d95b7aaf35967cb372dad63b499b8163a518b6ac97e5051f7a4f2307c4d4dc0588358c90cfbe819754dcee96866a6455180bb1f81c0c1b4bf46977f182ec7cc66a6dc58f6d65d4ca4f043c47335ebe9d91359656d3a697d4350cbdc0e65533b901be87b6d18c67b3c99f192e574cb3e44d6a9fcfca7fdca1151bafb68bd86bb5b32ce9f201c2474eeba221fea82ed544dc628dc4db2df93057aaa863cd32b9f15b75aeb246a560d199eca647e69cbe73b4159832c33c210a5c2c776b96a4290b0755ac4079bbb4e14aceb5df64c0cd6078cdff2c0b9e50966ac032bf31144a99e574be358658c73eb520656d9efac08081f5b535114819597659432c87c761762e44c29e53f58cdc0d5cd75b99a2dcefe1ebeea660c43ce24cde9128b60970ad2e1fe9897c72c9765b3755d18e3923dd36a3efb2e21e95b67463cc68f99169b0428b4b944c8d400c209cba66b998ecac651488f1f2ecb780800244463ac719f60f9ee22617c11bb847797d48b1845dd285f8bbb965a1f8d48221bc6abbca41f1358d4f10f10df5414428820b48baf75df01d3d2afc6f7e0297e9aa4d403e06c04bc9d013f89cc233a02b9b5d22973032ee0a39ed3f9cdfae3573d3ac87da8c49e30773493e00692aec8ad0dcbd87c121a9c14a8d2792950d1df750e2b867126d67f98da3a616eec81234496ea2f8f367f783d766ccce7e783af9c1ebe7ffd3a3111f1932bab0b177d1649ad4457ccde092afad9cca0711454b5ecef7068ce226c9a520ca41369abc0eb5849eba0f4ac41ed48f6726adb8a1a2f22a458c2589c02f49620e5316efb43fe934ff6cdcbedae0ea0d1ec3b34eb88c9705f24f2d391649b506b74d68d210d61922b2a7fc0a2f219ac10e708913fa8e69ce37df99992052eb61d1ece17f32b74eeb2843ee910b7e6020494c4507e5ff12875494f9aaf4e05bae115000cedd8d2d3f93068afffa2d50810ae455033ca3f138b2bc312c63baeaafb4ca4bccca5e4165c8c83af971db673a41686a1b467fe0c392351ffb0467bd338d0e46a16d461aad178c49cd10cdd0435b4e27945a39e0e8bbd81fe6df23166af606c3fbb35c6c18998a8b815d2ecc7a54b20dd0b82080973609f70a4e8c617c020acd6cf65299be45e4026e2d228a11ff48f03658b641b202b8e52400a514e3e65ee162db4f9fa7a18e35a1f5b129f8706ae4df36696635b3b9c8979dce32be6f7fa5ac65547e82a22ba37c1d0e4fd8e5e637594a476711530805cb815ffd9fc1938bb0debb789be8fe0ae11a7a2c4ddde2905a12207af901243b6b303c9e6d0ca907e0b79e0389843899c66a90276f217097d9f07d2071039e2a3a1ff41b4b4df66fd0a1938ec3845b7dc2f7f50aa8da45c955e716baf9c6f58057aa5c6600122069a59f3311ad24e4d239a4ae5043213728cc51e32a97350100d74709d31eac0a1ecdfa88a6d82b61b1b95e940732add74adfe9649e15cc74230a8087fa11456dc33acfdb13d82d4b6caf63024fc8c7f5c62150eeba1ea69c8a98cefc82f3f7358e7efa3029e25408c336c581e75c97aa5f4c12d3b3062e6db81da74c15839614eb8039204383b46514d2010a02a3c8eba510b5b35864125e80709711e46082190549e70eba5bfa5fe02f19db513e37870a05ef14d7c65e8a32044299a531e81875bec42d75953405a21978ddb6a193e284121f5f2648d0f525c7a8ca3625e7d3c0ba8cacfceb66009c846f7676ca71f056391bb84715015ba100ba235ad10284431a66f9494f841e020e3f7cb9115c5380067e5c163e3a969a891d47bac102ab3c00d490e85f2bac049b5586f092be9966441159a21ebc0f16c79b22bf352c7c26d6adc0e996655f4608f85eaf3c866b760689a5013d280a196d6b023c708a8e76cd6bbe9479be521d2d055ae3515fab406b3f233c8c5d01174c5d50223cbaf5f3e794ae879404b0f3391ee1c249739f88a9125641b566a017219619c425331a23f516a8974728015c8ddc52b7bec501aa33182c07867e925cbbb017fe8fc3f1efe00d8829f50a320c561c5453518302428687bb6a2cf9e05e54e36284ed3ce09d07ee2092f79810e4ea11208e9ddf0832a8f628c3974bf2a80ae1d212e3bece1847fdf008f857a28265649600170e03c143fb17c68428f49e6484d7e219fe2b681b1adf08c474ee995995f395e28c13875fa694a98322b7cab0480b590e50a108a14d8af5a012e3c63e23592d56006fba75f1ed5fc81452bdaf19792976a2f7308f4bc17335d2a4df5fc9664b6606506e103151b99c75dc55b07fb23f79bf13a58eeae143184125e447b8ac6561a0f86732cc0daf703529ca269a5a4bf513e38e4b1715de08bd3237d54fef4411e3a6daeaace7055aab3298ae6a5351d5747865eb22e7cc2b22f6c7ec887d4ead995b35a676f4cbcb0937a19253c99b199d11066d5a29efd8aca7556028c0e6ad407adee589d6bc20161efa03988169799393cd51114d673bf1ab593b92df6f6dc59dc0eed83daca3d06de7051bd43742a16dd52b370a8b195301ceeb564ac511b3fdef93888e178280e5cd940815b8e1a7df52ec1bbe0058909e47122961b463ed6e73ee8359d06c420fe03fcea03ada3c04cc6ea205d939979d9a640e64695cd27ea10a8a60397d7119b6b1b30cc9d91cc9be78475ec1a537dd1d998977c05b65e8538a3b271c309d7bdd4c32229c5262dd700691eb4507ccb6be94b29d33310bc1bc2be2c81924f8e09a2cea055aaf11319ab99e9e4ff9d880bd7e611ecf56a4aec9ab29d69e50c88c576948c4290d52dcc0775e09927ec9fa414ab348542501726c47064c3ca3b34f8f0ef8bb6f6801e6293850b158a8907a2199b3c9fb341773c424cdded32b756aece6661b192dbf665e8597c33ccf8b98da5e731418f1a29e5b20cea388d4e1a61a3b3f66ee64663192d2f192590624307ad8dfd325133861310f6657308b02fde5afadb67bb98e5228a9732889e3c70c700033f725fe765fda503b8c3edb9b810212080bcf76e3a8b7d6fc137b1d9f2edae758eeb5375224fbff93efdf2b908704cfc52573c5494dd818c899ba8de144fabf87b2efe52643035fc9fad9c5743c457efad1cb2caceb029a6e89bf2117b25120203b21def16d510886f46cbbd536f05bef05cb7ef5df83b861cdf37526ec88aa4c06d3dbc36875b39481776dfc1af464ddf355c29e53d1ed76035038aed5f78374be7aa36a931376ec179655244f398c9efb35c4cab56190bcd97c0caf5a3cffd8526921c325da143c83b28e0843b03c81b0add5be411a07e123cf3a303b5cd5b06f745d31873dafc1002c0806f91b210e687a0b58ba13fc98da9a02831edb881af0adddac84eb2739f62c2341cd5376d865ee980a6b8390ad248008215f873635f5aad65ac4909ad482171f9f26c55b82874848f9ac6ac8d1dec49f6c4bae677b0a54f43ba8dbc31a168ad7415df2ac7079be1ddc23139eb852eb203c1fcdc883a8a3c6320ef5dd80d79824fbd0566e0b347122a36daddd05a0440d46cac12d2d6a7a664a40739f0de5df375882ad5b3b2c6d1372eae46209268163402bd77df4725ede28d1bc89d37db4c4485a086e0fa252f93e8f2203150c080ee0ee2c7ceba8a12c15aeebccc33572482d844d2bc6c408975cf3b4ddff7fd07f1a647845cc26d5a2edc465846ccfd665e62a59477f126b3a7d9b9e5d10584f44c4ea6fd0e7150fead5208a90be8554beb8c6c28c14efc08dd87debf043a822055bc962dc9fc753a4553c62f142d811fee9c0c105b43ee60c5ef495c880fb4125f456c896c91633720e18ed7159f0e67c702bbcc8b01984d1a0b7c245953af82093be2a3b48002f8c49b26b37504d9aa780c0cf392d2b1a4ccb7fc96c42cd390d23e29d6187ad03da5874ec70b57191627d07f500788daf429208f4d60c817b8d9a9490b4a93b4ac5f1a90b2dee478d092c9a61226c4fa84bd976ac7455fbb5fbbfdb308475b29494ce7aec458eb22fd84212a7a8ba43580d64278bffd05bc1ce1d0c435710c72c65eff968ba446b8695a7bba16603897b4e5f418f42150e6b2c29a2c15abef3e3cc86b1d5a9df043a97575227bfe67a83348475c11984e09b80f64180241320096cfc6c07749e2b1df8269ef57a2eab36b31d0692b46723ed4a6889198185754332ddb0cef8d23b1612e3b90a62b902b0e9739042fa4205cad924e67dfb3468b9feed7c8ffc14c3e0edc88eaddc353a3f76a33655e04c079b5c2e801c092818aedbe910055b62aa5be14c8d85a07e392a929e767601f87d13fb29dde58bae2acbf862bce29043add479fada22e43c78305065e2cc563c947478225a003f27827a9ffd2decef6e94e719d5a9ce303d4e643203334a50ca8b4868606da2e7a0d7e4a30e5922300eb9e08460b633b36237c817b1419973cb3e6c616d71d0be8b95eccbf7a580a6940fda817268ef9db5da70f739791967ed172f35be4bd1516189f40acdfa22d3b4b203161ef6d8486c5bdda637bab67889e2c24a8f414d21f70df1e3b3218b44c8699943a8754b2dee5c5a2f3d76046b58e70da8bf932810948fb650dd9cdf5d8f9947e0fb2ea2ea8cf6b9b81b1c60199fd0cfc0f3ab73e2dfd9d860b4a8bae844bd629b824fd876b38143a26bcdbd3ba669ebcd515faad665653493da5046568d7744c42316e3328a5230b61f37b002bc1dc335f82806446f4ad2d344e3130cd395024b2b6a3c656fcecd89f03ffb01b3ed5772a6e53c15f7e06c0c37305a43c8e9a4f7043d9c581156c63905ffc283c40ef068289d1271f58d8a10f234680d439e3feba3c62dea692f325a0cba55145931f7b54eae9c1f4dec0f31fbcd611eac0955f3a4ea24125b121165d5d4c07b0c31d3df228967aeab98af2888391a88a6650e655a26086bde9e131842465d842c5a60e18ab727ffb05c77ae66375277e57c6f11e904d95005fa2c65a2339872a3b71c7796f91d6421bc7c6fce3aede79bf992ca6b62f2fe48a28b4251671fa43e4025958d0af46ee16fdbe1124915c20155dade7c5ae4b1a1653427199959827540c8ebf4db7e168ea36bc3549da119233a0985ce1a83b277dc18f00b2858b000d641b1be45e2cdaa6cc117f377a9e34e89bd39b10eead1244e8b7dede0224ffe3fd606b97ccab952c2ec609dd56b5d2c87e0e1f4463598f6792d6180b97d7ddee6f435d9252223d965684028e56c1b59f85c11b2d9fb4ac4aa94cc89a837dd12ac4a8042a52823ed2292941a97e57d94380f3eef3265e9ba4c6daf799d352a57dbfbaf07ace7322880530b0a79a813e456b0ad45500aba79556bec40d686b3317911a6ee1e0a2dbb888dd7fc80ef27e3a8124a9fc8e10052e0f2610c78c6eadccaf0878e9c04a7d1250b2a8d6f188996a8afc367f83b7c85eea79f3d118193c7ca8c5f934f2393293c20478ea0add79bb0c532f058178b75019f430206aad6b7b20484e9c024318770896ef95d3c4065507e9c04df8b705a5626553cf3962d42ddfa79465bc9fece3c9b6d71f7008c05c2057ae8e7c9a3f0fafe98b2f2ac43d91db2c8fbc27d4f3cb96f6c5de5bba7809fb4c743ec1f96dec72f92aa2a848950db3b6dfbfdf0451443caab9f1dc7e06363dfccee3f326e11b9aa6cc6ab54ea9b66d465b281812402cc144403134cf29d633cf9546eb6409a5bacdd265fd0ff5660a2f338f9d5bc744ad6d658c2f299bb3c7c193c328336186c6ae8e288959fea5fba057b734cfb90c7e2706772966f33585fe94939a3152584c4a4b1a42b958919844d16fb426c517aac399b0add412b284b2a593f788bc0d47d91de2b70aa33e1961b1fe072180d4dab7aeac40998b94195054ed4f89f5b8593625ee2013f2929bcc93a40d920bef6deaad20aef3385a8bdcbc7371ef2b18acb15e082543f908277de31267300065a5edca12f2b58b934ef173817b2241a280b49969fb6f2172d617dc0c7fc2778be061963618298cebb2ad88b02328d08fd6f75bb9a1e14b6bd90d69e4ed0384ace3860f39756a279b4b2b1fc4b2377bbfc48f4a4b1e779d6688260ae4fbc9f3eddb63d7866eef8d5a2855f75cd308e1ac27d6cf19cd452dda5b292ffba75796c381a4355bccee86ac98fd430492273e625c4f16933db912ab4bba47dff57091b973d91f4dc343fc8f1b78384680c030716c3fceea5b4a1f90a956d0cefe1835f32ff250e732590d98cf7e74dc61488d613f6569141f0f7dc89d32ccd76d235a04ce3a4dbca7ee02d5b5dcf6d120fc5cd428a278bf7c27479caf94cbb0e32e710fcc3d5959157c9e5e283773dd76aaf0a6d1b612942a6b4aec04187fd6a2bf8352d9e4f56e5c43e9be84e68daf39c2285b451def3405bc5b00f4ce13da6fb67f3ea68f10c4b155c1ce2ee4a88ace075aed8c984ef717a86070a23a7af26eebb8eae8d1a870da02318635ddeda1bd93c58d6e992cd8a197ca2c87e89b32e732271640ffdc1334572b80321d771eb11540cbcec678918b118207836696f4ef7dae0e67f8100eb8bb79716c8a5260b408ce88f950df1aea000b757b1570f3cc3685ac8a03f0055630d47bd3a8e9a27fbe89cd19f4bbed0975691cc5b2c523c016bb88e2cc72481b9ad07d005da50a5cc17a3933423fed48c8643d8de74f8120f611efd3c9d22f9c865a7cb692ed2943f29fbee544c9907f5e662de3dd08aa707ca3475cbedd7c3adffad25749ec1aa6e6e1ab27eea1a07b31f5e2d823015fa5c4d4bfc9b3ce60faa70a15e17208aefa6fb800dc6c79f058cc31772f4513893e82b126f4ce129f911ae19e121fdc48991f12fa483e70173879ae448baa02e73367b9c1eafe4cc38cb212c13fe91f756f56438aff2a5d3813214af9c5a5acd6c6f51d12453b26cd6c2ff01167464f6d6c5de9e125020f87a768f7d81b6850e2201ff1f78d891e3fb45c39c77fc7315454cd06861016b8d1405b612e393b7f587182a15a221ae9c17e2fbde401a3fd15c26469e5ef211615c677cf6c7a05fb6601af8fc68a47dd1dfe608a3bd00caa4997997d83fdf1a502e32ced71ffdd6681dd4b15c645aa4dc833cd0f79770aa4ac144bfa23661c5054dbdbb6ecf2157679da0c047e8560c9e96012e55550616a240619f97fd0a86aaf8d11c7de03ca3e5d2efbfd9144e2cdea1cabe4112f3ab5bafc0031693a571f3468aacc0cecbc5b926f3a77d3cd0941118f50f167e7d8f60cdd3e4c482274419691c8c1b1987081dfe02422fa4452a075c1bab7b2dc80141c5d1800b1dd4a9b5afb2dd7318d37ca162a25fa72c4f3d61d2efbeb431072c61b5efa7cb6850dd2e538dbc80a3c9e30ee7535b45dcd786a199f1d587c59f99e7dffcf21d2f4758f90a8167fecab2b59216cf2dcea6ce082d0f245ae26dc76f730b76e6c6bb6de38c35ce683efacb13dd55e2a3ac1fa84abb993dd8f3e86cea7fc559c1c282c968b5c7ff883f5816412ef36e331d03a9b1de23093025f8db81f922843300c717e7e6aabeb5ce30545dba59709b2c7d96662eaf359f3ba13f96381e72bc144fae38c06fbbb0154f36e1d804639f7dfd8ff0cff61f4f60b694a56b29cc907e88d8c00ae6ffca7b08f4916ebf008aa7e2fb5e8ac8aa6e0b79d477a851cd394cb6f549aac2c7b39a0b5b6be46fca819b9bebbada382ccd2b33488a9d6ff23c497b70538b49b02c396cfa44e17afc6487f1efee02057b1f6ad10631e89884e93763f3c84001891e522f8c7d9b0dc2c2bcf7dd6bc3fd05e2d8eed1c2ada6c5796da48a38def25ec96a49da50b903cbd1ece43f76b65bd70d42caf4d2a6e8273507fbee33ebcadb874d1cd64eaf9eb7e605c4d5b91eb6bdd7eea956be143f304248dfad597c12c259159a4b0cfd6863a206eda5c107dfad8608fc45cf3f61606e5182ae753822942eb9a98330dbfd8a0f9ba7f9db3d8c9c229e4add97cfc257128181732dffa5c6c78db6fffad2d058f6209a96b8bd69e61dfcfc7f53118375eeb9f9bd206a071c8d44290c19a0759a4d10b6e08a5bf597398c34b4e5cd6e73e730d44de8003a47940d8d05eab85db69948f14a889f1f49e35d51b319991961e328af1e18e942c0ac3f900bd736a94aea17504a85d2d03afde1ede3908994644e1277cce83ccdc595c1a1e2808c7f0b01009ea1b0244711a7c72a07bddf4f1c3dc22a28e879099a749dceab7acd1db6b676185c2059e0dc08704378874849336cc4c971a180001fcd1578367e932b102693c2bffece4df0be8e5832efcda832c4d9a33b31718a5ff5213300e27dae1095b2044208d8ce859d196102841e433a536354d1ae0310d42eda58e9142df1eee411ce5366f6f8a2c8e06265af8df8065e012e05b4e5ac649eeaa246ec579c6bb36c24d600d3182238b47632ba49e264e43948a849aecbdd6d499cd856201a9cbb264136ab03f9ac892851d583cda6e3194c8eba893e77f8c5459731ccb9f76342bf376a9bc590129688de157b2454d498ba62822128388dba7af0d2d2760e6bef1c07aacab830d7ea594c135e56ab939091c6b2220840f02b0f110eeb11e84a8c181f63d3b95258ca064e2b2d72b15eb5b09cb0cc1d003d25832c3c42fe4ab8584ce24848d0155aa16538dbb2cd294d5e426c0625655497b06a7d9abd106c6a1cf01f87d20be67045af148f9243b7916c11325325512970e7631a01577b4fa1ea4f81fcaf54b10cd20c24154cbfdc59d808defa5de967a3eb8860735b273268be8a234085803db2c00a99d0a5c8eee25e61e384a1eee286365bc610ef460e84371984c5f3f38442c567c208668c1191b51e0b153a5e023a92a2b5b2965114c9b369904284be66bebc0d48ec3fecf3cb63e3b59b7d8787bd34bd432cb17c5240c1af29f9893b29aa6eabc52e1751ed041776916ed5d005d0d769b2dafb2125f3dacb9e59e25e5acc5272b436a46bc482a40c81f80344029b746e591e2549723716e96f87ee7a6ac9958884e0aa0abd8b9a40a53484b2518ae6066728c16d87356d6b3f83d5127a688be801169dbea85ab9f86f612c0e8fbc744390357ebd4d25c28f9d65b3090b8b1d125b4a5f325ea1c03f1aaeb2ec1e0ca36e8b76f1980d1eb10857fd557b6ad7417bc7e7584624682ea1b82b5175387b6fe333ea81fa0dd58f573f6a352b66e537d8972317fc971b658e609b195094c6bfb0d75b45a9b59ac89f122a7c8722f8a14882159e2d2dd6239f01aebdfb1a8ca3c90258aecdc40705bb88614622fdc15cc918798a1beb3e250234cc93c9b5d6696698df9475bab19e8cde596023927f748084dc8bd6e99760f1ba21e330484a9d0ca7e16f01600a956bfa14e2031695552335c940bdc0f082b18c1361904e554fc02b7db7099258b3d9068517c11b7a35bf054d450a38207e415556e73b1de589aecafd6a42f21588cf0fb7a1d869442a85e6d03a3baa9acb8dadb14006dc014f6dc1cf56a6839abcea3d8009566b27ab5fc3c80fcf42e51adb08c5fbea870b5b29c3e57af07cce911ae1c90ef6580e8df94ce0ae83fff5a6a981966da3cac3f973ac6e3a54348b2abd4d1c80e07125a9b22eb29e1e1e092477afbe780d9bc4aad8fe2d68dd853df15242af9b4a76f471b7d6da7a3a4a28dcd1de08909954eecc772772093522e31a6ca745fa398320c3489e6209ce53186cfe5fe4d67faf7f9d2be7fa333b910fb3ad15663537cca20051698bcb32654a9370a2c766b4e91ea7446d18234905c085ef7008e678ec9d68a62f2897a558b31eef28a3f39a2a4273d6b9209f6f37099389e585e6232b9fc463e02c2344afb243d79b1896bb6894314904fa9e69b7002b365f67545f629fe0e680382ce6ac795a4919ccb00d7c2cddb9aa47d431eeec84bb3cb5fb44def90a7dcef58a973d415b28ac1f8d968afee68d9d296f97710a772abb201e11a4d3da8b15dd5961e3d28d17cedcae0739e841eb90547d1e642d3cc0b9f61221ba114d9aaab60ff237e8e74843836e5614870a57ed96300a67eaee6717fce92468fb15a4b74337a68173211041ddb80d8a3186d92ef9dcbc6411110ff396454a7efa8c70fe20c1302c4a1fd29a66d02240fc6aaec7baaa3ad86ef4998079924aea7747aca4ea6098001cc8469c494f4ed2762e11b5a1d86610591bef158b29188ca4ba9ed8d81cd03a42faa40644464cd2ca7e8d2cda6cee7c33f24e10df9819deadcca14d4826946cf0efca58d6ebab789afe512a03e23a49769c6595878dc482ec5c7c5370be00bdeb695fd51a598982d8b1bb7000c26155b51df0ab80cc77de3660be454117b2a52ef80b9318065e8655aab140a3ad65f4fb034620a9f6ae7c3e4e66bdd9017c788328ca37d4fddbfea2c2f8a6ec45feb2bd0a6e7d59d284576359f4d3047d32eb082b575d9f007318d8dc7bc46a33d91376a912e0523966a2a7d6d715138438c635a8e0e46287fb78cbe154ffcc9d22f45d9215ca2046414b98b9c3023e837c87f08f8a64802a11b9edad24e571c27b73e1cac9cb727bba68e44461263549a3a820deb64e12fd35f5f0c48a7fce69b9cb32742cdb7167efb4d5752aad10442c444532c7cc894526450e114b751ad4fd684e7a9993680c2c0e794e33ffb656c7432b9796428248ab6c637338e1e4d4e5a02c9daa7f303670775496ea0c07192a25f9b484b9e60a369d91931ad91466fc0c9fe974c2562b4b34d0f8e466d276f768fd44752074c4646337582f34bea81138b30e3d302e583c9b1c08b40108ba4e4902800e53287d1112b1d20b6bde3f138cf6102cc6b5b653666f31918a0f6072d3c2b60dc3ee06a70a44f9d8b283ccb96fe03bde6578f9674b1bc9c37222a8d3c4a3575a6c395a9a40262f0ca26742a436d2f72c02f534065f965f5e61d12cf651772bb861e7f89e995661c5f17db517534daf4cafa81421fa0f1277521299216a3283c89d4137f6351ad9f96fdbda547146b39615adc945045dc24d7abdc44fd8f2f4343006cb4708334eb51254702780198814ed0889226556607893922733767c7b763b1a43c9a52f2478d04facfb02569f115ee2ae4b978316c1272d9fa461fd72958d233168600f73621a5ce8b11ccd6977112845d95631b995030cc0421c790d4e8b546095cd55b3567bef81b870dddcb90d87d4a9e3696c878689db63635b18d74068a7452a5db462c8c721151f272c1c90ff1a4c3f04b73b9421815c74375d2c0baca51785bfa2b1fec977944f1dbfc1d67d734206ac42b02f16697a05a9c070ae441a10d354a110906933107a33179970a2f81633be1f4224e7f8c030ce4903f39d9cb6992e7577832f9f853e94cc8cc9216aeff130dfdb36b462e6e08608cdfc83b2e5d0fc072410709dba730d51bf5f51ebdb6436e1ed59c15c1cb75924056ff34ee2cc8a5fa7c6143b17aa4381bd09bb831c3c91f9465b48383f2fadce7c0838dd1b9c1d99772e6cf2ff4906e33fd12167033ca4b4b6c1a437cba71f663892d4888a6d252450ea3447b41dc10270a02896f2d94eea68a2620b4665f7211c131aaec49a777cb7ee5d7cc102433077c97cc2c633de847d171fa5d51cbc967b8065e14cdd242869490ce708d3c287a35f5199acd4b8a8ec91745c3dd178a0a7f2ec333fa42d1b9fbe60a5efbe0a2f01a1b29b3a7322ca30f149debcf1505db978a7e6028c356af51a6f60e8009ade4ab5565f6d9ab7c52bb84a23e5dcb9becac8090e0436ffecc4a916654e9d64682bb694c4ebd181535fdac96b271c7846ef6465f32f82cdf8d821d5def2fb5a7cf5ef83c1a940f4df0c0c65ac49b90191f39de13bd8e3e406fa337749437756b5e62fa98674ac9198effd54153fa6bafb42550fef605ce3040b78ad18beba4eb9399687f0af5755ce81dfb355fd05b67e06a5f880cfb88900297c367a93b524d8d8b0ee8c7988e0084d35722c242c7042fd5886a1d81b3519bb49b1d5d0a0b73f5001e7d5a912b1b922ccc63dad8987019f68ab58fc90db48abfa62644fc84025e8ac966509a84271af2bb4161d6dda6f9039c041bbe902ee437ce54d4036094fa946418a3414bcca4e8bb7a3a47ea785e3d96f361c2147529a7bbe0c3a7719706b3f4f0519e959aa4113028fd2f686bbf07e2f442db85b0001072973d6191da197010476edd973aaf4ed5c042b5f790090787a2aae7a5745c424190404dd59abb717ab77a96e4b711c194f12aab6f068c05a2c0c4c6ea759a4147f743bd18316af7224970efb540521568d1f392ba8934ff1f15343017a64ced05d847e0228bb3f38f74b237c6043bccbf6bdfaf6b94aee9ff220bad548e22ad1944bf3673c0770da2c76be9d1b17d8501617fce2e17944da10fcbb9a1310af3f6d944477c0313ba19b66c1f8a0c9ecb44d0bdbaeb9d04532f1c1daba7f1dbbd9d61bacce7dc269f106276472d8f2099f552eea40406ae30107c892a16f09272618ad7373ff414bf8134e05e6f2753ef94a3a66af3ae6aec7296d0a2455fcad6aadde2885ed05bf16ea317c006d27f9130b140d3cd3840e220d83cb18100e13f4865966b2d1fc176b3dff8ea1ae21d19dbd257425c8e236e7c0b1c153d2b5e6b2d3e92915ecfe619dcf873c840a4db7f10e9df1b263a96f1f4957c991c8be9ac4363fba6c039bf97f9913f6c15f3597ec6166ea97d3fd0c38df737df2d91035e8b6e76e66f31bb2c9099d484ffb4c0f3450b6fb1e319b19b15ee6e64a64cec724358e00665c2c465eecdc17aa733ef0b3a2c48f001ce9f22c63cea55a48030c968a86ddad167674215f4c8bc43ef083781dcaa1a2d6e807ac9702b76a04cf4d715215f4c45df906f17717334726f8f3f6cec3843ba15eb08be75d2d2e29298292caf236640be8e6fd01f49fed2058648d2cacc6c083d2992e796a5497d5d1334a1cd9c19d285826cda734091708b7aabc43e14126d697047534a43a3bfef9ed1b0b8a698fddcb9f5fb3ece292cb4fe675ea8bf64b8cc40e5ef732133bb1e2cbc4181ccadf5f3816b8830de276d90fd532ed671f9d6e739a1b4c57d15cd82ed0d9b3aef7738ecfd468ce85e3787da7e926d1fca5bdf2e0dc58bf7d7f2c23330c476c131e5587f8cd4b0d28fba4c7452714bbddd0a04c21925fa3e94c6951d732d86dd7697453c0705af50f21a95481f9bc9af99e74d135d1b243bd8b4c18960076d69af3e582faee091d755e50e9f111f44cf95d7cfc5748f8606ee03154f065f0b7e67c7ea7cf918a08b6479732ad5ea72de2e982f3ed073f46078deae8ef31b5655a70171dd55f600d277b2e1e474eaea34245b82414d050088abc09c6913a7edffd797ed8a2946d82788088175e9fb1b4ce3807fd7916ce06552895b156c8d9df64475a524681f073a1c735a38ba111639af99d325435202ad8d16464233a224a81de51db791f9d64b3e42fc69d37cb0f5efcd2b46813da628d5d16c26eed4441132b71392d43163bda691ba821552b885d5a86ac847557949ea5fd3281e513ad4c9950d36f96737fa35c98d110db6866cf85272f7e1ada75b2bf97bcc4c7c4d4b5f432fb217e469a3f0eb03f47fa58e6295de2bce73635cb11adc4ef3be9b40e081c87d40f6d996c99adabfe58bc00b76adc15c35e96a571f0fde85103b1b58504fcfa5a0804ca5e371d43e83c5628929513b2f58f9ab0a131fc79395414857bd59027b94274a865a094e4ca32852fecbfdfaccb1eeac976e850383d023d879c88b398ee18e2697b20f9d503a3263028fbea6fef18e0f32d0d511435411abeae94a506c7ad2da7a3f13c3fba024de214ffa738592a4abb82ccc0dc36806a91fb9ebae89097f5771c643dad4ffea5c744e43b4e2735eb79ef96d965f5e1a1d8b6b8bcc79178e078ebe424c8e61017cc9f0f480791ecdd50fc29e7ed694123ebdae0bc92c62c704d30187b7547af6c6779b147bf3d756b75cbce4a67bfb233037df5d123078df8365cd88f560d415eab632de844b65b85c507bfc263268b645f8c4ef5205cdbe822ffca535d06b6bf896dfa40caa59e06b7e291528b7165ff94d6b4059abf18d1f950834752d3e8fffcd2a83d6bfbe4e373fff24de2d99eac8905a2ebfca0250ea8ff8df3f6818a794bb32eb51312896bf31f6cb027c2dd0635d5e014f09893eedfe0a7cca48f468b757c0534ca24fbba7029fb2123d5bc0ca08b070828ddcab78853b264afca52f04d58c3250dcac9b93a00afadf98c02175419c08366a53100fc7f05635f9a1f57acf1a861f52946ac853ffbecf6772b18319c1af8d16dc631e7d1470fd973ec9ba9f563137d5f8013d83f976a64193feaeb778fd563072f300e8fe6ac3af46c9bbd083fe34ac88a900fed93327b10e082e4b8db5dcd1803e0452f9555762de5402e85e3ed38736301858600f44e23aedb2834c6007b23d8fd62e94e73fd989280b40d3430867e8ed25e011e2d66aef8c29e6651b0a0e2871c0f50f1867f139e325d7f8ab2677dad43ddd1bb65821499e01627c28d53505a4ea5515f9a611d1490d6933f2001982c417bb58b5ada4e2d916aa16f8830d95b858528f39c12d76883fc9298da61a388c01580e6785886affd20b0d7fa88fe5c3531039e7deecac6db090c208b63ca9e3adb937aa45b2118446956ae141a8d1e28465411670e1fa7d442250862992d1d244c8358491c6f80516f7f8c61417e4e971f50b069c975c3c31556724899befa3c0ddecf9ceee3970683a5b2288d7b8cb113d2c7853153fc631ab0216ac14900778398dd45549461909924cb3c60eee9f151c360cfb0edf14f0ad3781c4f651aa173273dd869bfd4f41cfe4da02b66e2c25707a353a23901f5989822c01cd5de92bdb44698a84ce5203f295abbf032f1171d894188eb94e145a41176edf7aa66a331ea8f62983af71214fbc2286186fccdd1b270a8a2155601c11e31e45a9c6bbf52fde3a8c00d7d6aac367ab30268bdeb8b23608c5fc50646d5c4c21727b595638c2229fb07ba834a6c6ed79d1c8c3d618b34c252bb5dbc06346c44a7c1c72895aeb726bd024aa1a4de405ae5855c9310ab18bd2aa696f6180fd07da5bba0dc1286480917cf6dfb42da5868873a536c785d37800677dc9136bab022b295dfe485b5e226842273194ed3bebd5e5c78279aa761eeb0f38b55378cfe75b0f57fdb49deb7e23554be17a64fba87970c13b2610c72d65e14ae9d95ec23b99c57be100cec4c3b8a2286096c6ecbfa5f5fcc66fc4acadaebd6c9335428e0f957c8cec508a97f84241b700e0b8384fae995de0664045dfb4f47bb3f5e15b55d6d395982ebeb6ecd4f9ba5f79f74ee983f625756c0a0275f0ad897bb2130b0e2e82c2cf5e4f9c500ca0153049dc7700235684e6c66ff7f9951ae30cda9547f3a20685c4f9f9819cf6c974390a29bca20dc2d1aa8ab8f7271af57ba63e120bc5e4371de520d534dcbdcd9e3c4572fbc8a493586607b430b5816e52de480cd687eb6c231e7324fec496b17c8e6ba4d105279ef70ae3234fe095c4ccd1b3866b11e9ea895621fec67a2c932cb3598e6b1d1d3c5aa34d5e9d8488c84649e6ec1bd8f3f8562bb442958c87ea24005c70e9b5fc4bb2ef2a2bbc154147678aabd14406c8ad8074fc098c93a16f2df3340142de2c3aa973b1a3c062523eca51e95195e3c00ff7c354678929594869e17b8fb3fdd7553d02a1f25b832fff7a915c5aa121289205b1b2b15c422edeb24b2826f65df4d81a3232bf96c5c197299c6fc305dd9a4c05be4fd53ace9774439e0602830364c56489e2b4a5f8273157759969cd938d0ee0c55c4a8892eee2c623fd0e6b37969882e704d402950a1e3b0ac6795454caf80fb74aacdb017082e9b4d05052a44c5e971eabaa39cfc0d074deb662e5066bd70b8cf27332d2998c46f95b105cf26cc1396bf59b78a3701f122d33a30a40e62af7bc51437ff00d75e0481b01b85528bdf49210522f3b01a24a47535e0786f686fe0cef0a0c10dba804d897f4939f4a1c2888cfd5f99338ab1f0926d2bc474aebc12da6f84c49edef6138d713002f6161657de04429fc8e921292f65f1d5f3400173d73d611e9c1ac6fcfd270b7627f90000f9fb61596c985e64aa6a0183eeb7c0f7344a04c001110b22e022fabc5aba3edfcaae7aef2119d2f0131b0fd70807de8711989481ab482c16e289cd4c2354777000f808da821061021506b4e92b1515af0154af698b106b7f7d02c5adc2ec557791e8988c15fc4be0a51ad9de5b2c38730c09a2dcac3862c446bea45b503210a54d4e9817a2604faafe92cff0a33114020e7bcc8c6aa748c7b8e9950f21cd9859461a7581cf07078833d9e18ada3661df4654aa9eefd59ef99cd8564136f6f1ff48419a4c2b9ad313104653b3d4832dd0f94fe1211314865ba9694e12c0cc94b1d2205351c3e79da61f15b498e44c2041c470fc55d35f66006eeb923fb90a7b8ad0f1321563f37a1d2a3696d53a402671ca50dc4914d0cc6a32715f5a8b63f68db51c114b56d543c0f5458b311edc0fdf9e0d7d71b4d522d0e661e3885196aae02b180c64e63331b35158e5e360936a175012742ad2b88201dab4b4f645e4504c46956d32336af9b3a9e11f3ca15d070ffde2d55f087087337e8bb42c6fc597196be1a9fd21d6615287010b1a211bfd7d2afdc0af2ea0b6942cff2132095d6e9db15e11aee91f05ccd7511ecde462b34d3a26d67f6dcf9a2f1a99cde008d13d632bdbd0d6af1e175438082075e1f21217dbde1191349ef651655f4a2c6375b7e249fbe032b7081cffb2acc7f6c5aec3d8b00b97682b8c20c547c0dbc0464f3cd437c960460d6fe2d03e689fc3363b97b600ce185a277953eb7c809d35015ce854779e85614a6ba15238181c0e970294ae700bbc8e2385cfee8aa7bcbf1346eb3facfd0fdf73ebbad77f8c565e8afcbed5a33468993fdd1a1f4985747a51141028598459d87ec4b953b1e166ada21515cb3e07e1e62dbf4e2df06cb29b61f03104af823f00cb01ea2de7033c4c11f2a98001ffc22d986b0940814d55d93404c945bd116337270396f3723c767ce33813d6fdd793534528ccb4ae104e014db8cd7ad0edd8dbe1c20387772973b6c2ac654a4f4cfa1b1412d0b278a55751a7fb9d628b357d5524a957454f9d58706a1c6aa33fb970174456013a143ba3dabd72c9ae13fd64afa6dfac7eb862eb66e4bea6f31109463351107d7120eef99fb054cb0506afb4c2e28f366688a0e8b82ad329583622531b2862e10f3891dedfb50608036076af302288ba034789a0992b0aeff4af4515f52390d33af369ad0d91fc6110b825b3c53839213ac454d97aa4261e7c5b3454edebca5bc082dc111c0888ea816fb9585c79ba2d675a6ab4246064507b9feadb52a43e3e41381a0114746572898a58f7cef60f3e10e971ae7f9db798502340d6e4e0f62cc5bace96a3a0000e9a35405c8d128e4c132c8e5e7df082fa1148ae267608c2b87f4e4e46ffe5567dbd0d05014c638031e79c35c666a82d645d913cb866eeeb82c0d7522d4b44cbc8926793fe30dd2c751bbb3fc1a6a05726102b07b6002680a3f626b1fd2e16d8282b5f6dee81c7e0412f98cd856df53facc49ec286707ad8f4a83b288df6e313da163a0b81b31cc52fd896ed221e95e84b2ed5e936500543d10ac823161f5ea0d3e1e565de9e1694f6f3a11ff94f552d547eb67a1aa98256fc6fd4adb2872f0317480f50d7501fed312a3c26cb0387b0a47881d643718b80e482f1b5a946c13a5cab8e5f01add6584d02b59c61fe9fc41ec06da668c309d210d5e4d65d21960b78fe304e3c3efe14cca44077541916a8ba1e9d577a3347aa645d56252359ad52dbe233b52e37cc485be9f478900671599ec28b607a14a14d82d0b1cebd8b7c329fc30a056356ed90cbadb7ebf19f9ef831680fea1b41e14d15d6de416b9c7fa85b30bf1217cc13a8cb4d7736c3c1132e7712429a7060ca934890d151732f2e5c2de6b7a78cd4bd33bfee91d05772be0e29fbfde0843cabb5156fc21f51180eb3c482a9ec42e85b6d3b5a2bb4abfb63dc0fcaf902ab13bcf9d7c0fb9a0f2e7e61561a62c59e56e0e28f309875857f418dad4fc67a9c6bc7da3b894ceec402ac69f15c52657448c314cb2d3aa48677a744c037872a74deaed3244d72617a9c05368e8a13d1a78a9c427577bb8563166761a88d6af90f2a41cc6c4c40ff218e5489b4c678262c3df8e214fe9a89d03e7f40cabfbfcf116ff235e2a3c3d3795a197f52f8537d12491e0c420345d25e04add32ed440d3426264395782179a929a482f50d15177c9719e3f86b9212787076cbb240e4a04506693c73aa088d5ae0e7a4d8124c0e8c466001f61dde738b80a28f462340d0607eba5c46e6b20c2b8278708e99706bf60f5d323b0e2618cbf163483ea983aaba076071e0ebebd37e16173b16138d21e90eba08a43ccdc6a689685fef627f1ff2f152bbca78a211f802c2ec46b24cbc8ffee871a848226f8d8587c9b5af44aca7f6ce99cec8d96bce6c6e2f225b6d61fa1b031fbaf1c4677f9f514dc9cfaf234bd90e20fb1285895b1d4df267542de5044ce4287c2af8dd9c3d1f188127e292c757a578b19b20281f50f4f87e5453b3d0d7476a180c24f7b0d9d986a7ec1537c4db806de5f25b628e829222ea52e7763b0fe12a9cafa03c821a178a9e0a2e5c0d73a8ef5a87ee9f04eabbf64a8a86ffe9a5865e53d37f37cfcacaafe2c2cd461c0e983e8166b26cc8cc36a6304fb4e8f9685bd75b433de5ce23ce2f6a4ec20722be2a0e1d12ba801b957b5f67debb6d7768a2acca295b80c0dfb38612271c658b4fbedb58a1c6a50c2b9862533a0363ee94f77179f1cee6657704a4691bdd2259ca2dfc4688f2a1402eb807cbb8b12075f3077eb54b5a810378f8e1e1478d0157f4cefcb8c0fad8857355029e2ec62a210d7015b25a1acf8fb84f288361ecb1484254f9036bc0b3102c5465d45a16ca1bea6c6d9977969a047d926ca903b4b6c4fd8f8f52809ca3a3f82beaaadd02699ed2106d48c945f10bb4614a3c8a4bda4c38331d1e47c418f74c133959cc18c6318c360bc5f60f23ac280d21de2e58a7e7b3f42ccc72e01f786fed75f887b161525202ebb5481291a45bd74592cc99a0d1fd33300b9c699204ee2521aacc7f8877e8f42311a8d103ca47262573803eedca89c392351f24c4bab1fb2dce0defa4193e6c992648ac1e728d085d6e66f19b7b71f9f5fa40a503a9ad0dec29035a99868457205e515107a9e5caa1a5bc7a8f2e5e8a57614fc56e1f6a840dc0e699b4de01a32fb0f88b3b3d57001cc422f57806c0e245b49e297e031a3d0c260397b66deff559c28798dcacc050f66361addc7bef265bca2da54c52063408cd070f08a29fd6ce288d8c8c8ce8fb7382d5da0b661f5f94b21a50bb5c9d2be5f2648abae64b769e3767360541ddf33d107e7efafdd420f880b80f525103db67c057be0d14a78ea7e6cb55dbff6973519719f2f7e3944d2d7f06d11c88afccd9fd74959d6eb2d78c35b896ffd89b4cd1973e415a94fe8e8d27ef9db299af8cd3e5b2d9db4f9d29f3d98236bddb4f202a5e7d84a7b639f293862f652865f8539cfa886f1b4d976cc43f52688f12b6b3fd364e9abdfda6c446229544274c53d663ee4f67c053dbcf1925e80a4b15fded25d577fb23601e2474184c4871c59d734e8fa635065c6d271292a529459f9117cf39e794e27d29d1206b122719cc797f5e2c25e6380ee73a591b11ba4c577a54628b5e8adc6ff87aadb5d65a2ba7b156e27aadb5d65a2ba594524a691d43809b1065baaa4f29a5dbb63d0a6ce4d8be0912d83f51d56cc2478e4d37b13dadef5a529500043092404a1f640fa24d4bd983fc59459963db61d259710919c37943133e405c8003117290807e757777777777afb53e0a6ce4a8ba09aa8920d2269d761fbe04766f0add694bacb2a5775e279dd20e749df431e74a0f0fce4d0c4bf6a3e36ad1d084603d4070646280642f9d9a568de60353910feb018203247be9d09a1acd07160488cbcc80765e43b11a5a45d2904ee98bea5e51c31b7452569b5a33548bf9e404a133f447d08c678735f41373e54c9a2d678492e80902a5cbb13c6d133ae99d74772983ae9b2e250d393d9c3ef78a2cbfa33265e7bd225b946d3a383f20e89e2faba441c110456dd3ffb9670dab7407c2dd81d09656d801cd1e5f07339f93524a3becf8d613aaf4f6df291974a47006182e797225a79455573a67139d44b942a7a315e6c14a30140c6506a6e53a6463defed832345b7e0d72185d7135ae0683c160dc908846f4c3471413e386442cd429e67392d527f238612bae4655fe52c4e2388eabd15595fdffe9b4b99a57ae5681ea50a5056da74053cc58e4aa5f7bfc4cabcc53ce93c7cad5b6bfad19ab6cfb9c3bbb25fb1f248b6f972cee2359bce6e5403240609426595cb2b864713bf3a2dad6e5299fa9964ab6717aa1780a65bc292b6546431eb34fcfbf75cd7894939d29d91957398adfa837345b5ae126668f5ccd0a3e88edefe63637faf30b42eeac3a43aed4e5b4a0148d3831676de0d9520a5b0a6943cceec236e419e4a9191479c6f658f7e88d796a6fbc312b2ca32aaff6c6ccd98da154b6fdad9e42596428f20ff795fbcadd9f79fe9ea9a9f29a41aef2c73608f7e9b07bced690e7d71fe9ce6c656de88ee7c206b136945a1c0f73f3ce3ae9d76d6d90205fd99cd59ce9b09bd33688903c35edf9d106a13c9ef21984d2207467b4fd48775c95091b86fb7a77705f3971949ef23c7535be3367e8f215327702560c13ddd3ee69f7f47e77823c9f5eafe3b69f3f62e5f1f735f99efe8ed4bb776f77efa7aae3bff7512e7d913651edfb764e51fd5ee877a207c1dc223d8308aa25555991b71be05c29a5acb7459b31d259f5471644298fd4a1f267eb66d3b79152fb0f4b9dc667ea4b1a69fd478da6b4297518783fbd9fdecfdfbe8bc9d253f31b491515d9be64228fbf3713d004d43b527b76ffdb6f5bd6534c558947284f715fb747494996ee3739dfb5896a6f6fbf21e67b262ff33d6dd2e91da9dd7da7532098bb138944a8acc8f2e584f91c90cdfdef2ec463c91e477bca9506a80a5feeef9dd3bfa9018a3137c5f9b95c6e6eaf81f9c3575a1b902b1b54851f633c4a366cccc4e6221262063c858ff0147e49475e69e3b7f86d60b7a1e337f7dcfd51ce342059e8c6d509ef4b8e1f7f4a45a36c3c63e369f3864ea28c13b482fd6dfb5cecd78a31c694e2fa9bdc94528d040d42c5460d241c63ac9f0e81b5132620427dd1dbd862904795ed239652b5abd440dd15967f859f3883cb0c71a8220a1be4a00184274c50835a0dda40cbe1af624345d2f094a0e25c0ca6538fcefb22ceabf23d5145e27091a86235112917648c9f7e4e9cb04c595bdd5224580164fb4fff4da7cb3ceccf5977f80e11ecf91ba554a678ccf7ff5ea69dbe0293176b7596eff9d856a324be184f3cefbdf75a91bd17dfe771f1b5fb3ee57175a52fe98bc98bc98eca62edba6565b0389750965e4a29a594524a29a5d7524aadb5b64afadb532cdf6eff71049bee7b37d156ff4a675d77f2159317ff8dbe2df95f2b6af6fdfa76671edbbbce263b7ccbafdbde9fdf0e11ecfa57ee39c12a695899b27aac92477daa4fced375965a5b7760f98b8a3158a12a74602b9da215ac49e956b7cd0e0036ab800511f286a01921704881102b64489badb358e90d405e2b152b8bb5e7f752594c6cfae308767daa79506df2529ffe10284fcda76ae8c2f7f2e9515223114bd5c4d97f7e0d01de5d084e99f2de8e6077563aa520a871b88fdd7df79d48f4fd89a04cd5277560bf6c9be20fecf4f8814d52073d49a3d2fd612c53d5ebb1bdca1a82aa25e87509b552d4a954e9a44e88821382ac200a55c8e7c3662bada162e38657f2445b0aa982662bc20a9c10e48bc38b064d610b59a8e2892a0cdde08a23a220d403254431030627d49057c084d88615f4c07230e0a15c1364ac1456e004e7c31652450afc25078727a278c2cc0d40f8c20c0f0fdc1094022ccc64c1832cb0b0a9373ab08196a1d6c052d1d5f001ab0aaec0d5544cc51aaa102ab2286d29848a315001863d9e609d140010430e5a7110c2908fcc909f2139d8810f0e6ee0832b365ab3b193e52ea55c354bb2868d1b282216fc10010b6c80830d4f8e31d32253b47c8022085ee0420f1b803003191bdc5208d0149e0ddea33fbd128fe9a1bc92974f25af943de5d5cb35973caa7d6ca76fc0abed2fa61b2c4f8fd2fa3665ad88b26247931ddb6e931d73d77fb1f5ae543cd9f50933c784207ab9b2861b6cb206a5a7df95440f84a41f8923e2dcdf07c33de61e8b1b634ee4c13d7e9238729fbffb76848f5ff4bd808fb3806d89862d851001da9dca93be62e2491af3e5962f74e32454e89ebb7ee77522cef32ce8987370a4c7126c4e634f811ddd6cc59e02bf7e060235fec47a63d3d9ae1c1ebd1ad949539cb9e2c746d952481a66b12db7dc767274c23ac93d91473c775c355f723c9987ffa8c890c72a622177d6c39eaf3aff2a8e49a4b6a5d87f1379d4efe12ba308e52b3c44bfbd64c14f5f46c7d9bf5664edb593f3cd4e69c198ca1ada9e56cf5e79ebdcf55a5badadaa19962cf7fdad64c1eff5b71fbd4ab7a7db53913e16e5a69b288260ee1933e84bb9e33715913ca568427f4c6d4b75aac70c4963ced99dd2fd5a708a2b6ab6dcd5576aadb57ec557a4efa27e2e96ce19ddf5b353d1d796099b29fef5a59e3f9eca42421336ad9eb02c9684f26ffad933cd9bf5acc64e1f5379652754836af55adfd24929a54f7350f9d12b80ecedab8e2cce28cb483715a2aaba69d04cf1076184447acc41b365ce668a3fa783b6efee13af4c89de133b99128552259a2d73e66d9f4bf809e163e3af769fc5b93f710a899306bb53e877f75368e6e0b4c8abddd70f88df9d16c93b1d1c7d6ceb49fc24e1ed0877d5af4da938d67189ede39b1f17b830fc54a4949be7e7b6fccfc5f49fee48e2c84f99111c4e12e72ed5adfbbecfc544ee292359e6b6530572857b92c4f8ad8cae50f48451957daa6a4155a797bef2917c37855eccf93d8b72bbbf8ba207268dc445b9391808e3fcc67c94f10c9ab449bb3ffafa70afeb33e2deddb5733e6973e79f41599cb4fce30cda5668d23a92163fe2d3891b11e9abe49c47c4dcc247fcc568846b713a9dfee4dc8f90bc2df192934739f722d1f6e5bd38cd83f4fe237a243df71de805b9ccf9cd6f8a3368d2388ee3384e85e4df7123239fc5d1db233f47de4518cf891de967d0a49160e8a01f8496f488abdef478a22e9e937bd4e2678f5636379ed63b41959c449338f6b09bf42ebea32e9e7e2e262f3f92894884101f02c29138fa1171f4b147de3f9791cf39bf0eb9f3fbd82271fc50fcc42c8ab8971f69f49fe8639374a5c14c19fd8cb4e782c3bfefdb3e9238aa6cd277178b4cca5963d197b990bf21341a6d87ff21e9e79611a8013d0be4a9cf45a1688a8ca0dee7223282ba25fdc8923ed43246faa963a4cfda66d2cbf74a7f1a7553fc5df238bc085fb12fa51022b1881b872cfa51f610a18bb0324badcc7223a3efa6fd74d8ef48e29c69d1e27329a1243bbf1412d4b3e5f742921f87ef50eca6777a1c6e2291b028faae54c2a5984d267cfb9b25d516242dbc162d1e977cecfa2dbe5289387d62245178496fe5a3bc25d1238ef19e1eb349d74c8f82710c6d32fdc8a7e33718866f2a8523a1e843b1fbbc6dd157b9527ad1634fae905ef459b290bce8479f8bc8db212693fe7efe2efc5c3249bf7826b960d626d724ff7865df0be9af36c91affd017c755a6b0c35ec99561d19742d3bda50f4da148fa6b0a4b3788166f8eabc2eaa96679ac32f5c77d1ea5cf6fbfca4816efed5715481692b7df91881e08f7df2f79f9492591f4243bf2931e877b250be4a99915f2947d936869b6a46d90a7ac6f91be37d4259d151c7d6c2b233de947bc495a7a0aff20bdcc55f6311ed163ce233d9e4e3fa636f7a2509b709af4e18f5636a2493f1249243d7f78ca7ee8914824128944d21f989fcb94b12257192b93342cc99642827af6e840eafebe1bfbabb1bf19fb7b1454697fa4ffbeef4ede6900b98751c5c9d196e22a255a20195dd999abecdbd6b62a237a4ca5467a7c59d632911eef6c5b20eee32b630a488eafe8788fafcc20977d218af86a6a868f35f60d999df0b91d12498b7ed4cd3689445ae2c7262d3123f2c0123b3f491c296c8ff2e3709107381eb7d1f194bdaf6d85a48167933ee727d1469f479f45b93fd183388fcdcb7ee2051addfcd6494e72d24f699ff452b22491daf9af0e0c49df9dac2f8f8a8acca3c7dc2a2a32e9555364d106d999a75453e429dda905adcc53f6e90784ddd253244dd2a38f4d7afbf47371e1863c639eb23fd213e6298bbf21be97fca49f1f0ce973fe4cf29b59267d7ed1e79503912afb222c72e1863cf2a41f53db021922bf8c3efffc60f28f463fd26ee3a9941dfd85796aee91f69be9dd1ecaf1f80a2d8949dba4e75e9ee2786432e947eea583dbd11c8fa7aac8a32591c42b85a4615f7e5796a9c8a230f4d3fe52564655f6bfef4425fb1bed8fdbdff753cecfa5e7c27ce549a2f7232e8ebcfc488fc32f8faf640e2fbf7d0be42ba3b7ef228a4c05d66bf7f0ddfd954916dfd647c68378ca7e32fb36d423ce7a4c89f4f8243d9eb27dd51af2e8f35f99af98de6ea50dbf83266d65b6a4392179a479907e947fb4b2d11381c819742040b67d4f1cafceb6f6af28936debdc8be3e160272e840a3ab9bb704f1494999234a60c5d763041f61fb10e2969ccefea7409eba697f3697ce9bbdbdd87e0a5fb2e0ba92feceebbee71d709f56c19b4a5102c64bb9b1fd6638d3d6b583add4abb81262f1bd6e312fb7a7854128245cf1e6feceeeddfe856ba0fe2445755aae6775ea73b3da23af7ee15793cedeefde4a90eff14fd287982902c74772f84afd015d4a9eb5ea2741b1c94a02b9237bdf792ae5a889e449c5245bf8528a5ca24be5adbcf1a9d9dedede71fd9e654a287a7b6efaf38ff1375dc9fb72ad1c357c0df7e4a215799aab63f59c186bdfd9c18dfefee8828ebe9b46d23dfcf5ab789bd128a678b5c6475441a29995a909c4a5c98a04e5ec0e8444cbd6e2a3352be92dfbdef78f8538feb5483baa8ca529f9962696ca650576cb6842f9c631bd3e9e1fc90899134ec8b669a9029e1cbe6bd146cfbe1ebc66eecfef86a860cef7910bd1bcfc6532c57d9dfd9f5f470c258cd0973f83094f161178bc56221ca875dad16c6f8b073b95cae507fd8f9f884e2875d2b0cc3f03fec727242181f8a6ab55a2d7cf16147139e7c288ac562b110f5a188460b4d3e14b95c2e57e8e243918f4f58f2a1a815866178fa50949313927cc8d56ab55ad8e243114d189a3ee462b1582c2c7dc8d168e1c8879ccbe57285a40f399f99627dc2d1875cab15b6c2300cb99c10c762b1182dc4b4307ff84af80af7e12721593a97cb153e11bec22eaab23e21f6095bad10b7c2ef43fa614e580b6b61f8229c1386b7168698260c657c2e610e1e2b26430d394489d562b1586ddb176380ba73f974aeced5b97cb6fd17bfef5a5d4ed7ea5a5dabcbd9f661c0f05ed43a9a5aadd6d16cfb2f4e3a548c168bc568dbfe8989c885c8e5e372b97cb67d54097712b54439a1a82512b540512b67db3721c12db89a8826ac7d4d44b3edbb30dd1217e3685c4cc4c562b46dbf646423712ece8773712ecec5f96cfba7911ab2dde3ede15a5c0ed7e25a5c8bcbd9e1cf5318867a04676662b4582c46dbf673f71c49d7752eec835dd8855d58a4ffb5a0b88573700bb7700be798e8cdf4c6668afd9c69178a1e8e77e3c978319fe8b1240def47ac665b9a0b866d3f6b218fb8866b78c8573bbc17e3a97ae3ab2be3c70a64e7f063cdd1e1c7aab3c1af36be927af06b0e64cba5a1fc5877b0c1af359225c6831707a7cae0d4199cda1af29567836cb93c3c6ea84916fde3ab2b0314448bf98ac805d9726f6ebce0235960b87cb5cd66af19cf0cb641d4834f8f205b6e0c4dc2069f06912c2f1e7c4ae3ab8d8787d6f0d01b1e201b3c79f0690b64cb06043efd21594c1edc70706a3894854365827ce564856cd97676d030932c2e7a7cb5c9f8c8fc006db044886cd96e6e9ed8912c2438beb2b35990990b7cbff195d283ef3c902d5b8cfb6083ef3692a5c583ef31beb23cfe830634bd41b65820a0380c49961199af2c0ece0c470807247d41b6d89d9d30c424cb48c757564666a7070c1ffca904d9626f668abd59c2067fe64896fc609dd980a297c1067fc240b6d8981ebeaa3c433c3f36f81c902d1568ce4896f9e02bf1494816fa3319f844f8aae2509505ff45b6d49d9a6fc8803fc3575546882024cb7d70d461830f82ff8974cb902df50645b2d4071ffb8acec09fb2a5c6cc14fbe0834ff2b980af9dbab14bd341df580efac664e81b43d1b46d3f86f6589286500eff5e9efbf7da1fd1a078effd7b2f09c687daee6cdbeee88577b26d1b6adbb66cd29db60de7ed872e44259bcc76b3c99c36994d66bbd9f64112ae34db6266a5d937db62b6fdaf0536f100813c251e1ea06ddf1bb9249c1d50e3e0e0ec6cfbdd680bad8cbd397d5a5b999b6d5f94ada8ce6c4c9dcdeaccc66cfb9c50ae7bbc3d95078887870768db8e9b60ccb6600cce0e0e0ececef630b5b52a7373b3ed5fb13e768e75ab1ebf201da6b86d17bf999783fc9362e8a37d6690343efd61913dfab3edb33c96cc27fb803cf5cd5c651f646dfb557ba63f5446817cc5ff0baa619a5bbb354c5382431a0f5bbedae10b029a79e29c214e208a9fcee80ad7688d992dddd04cb1a7d9ac1baaa23f16bfa0981a5d7932ddd06cf1989029f6eddb6c5bc3355c63f14ed0c161db17698f8545ae3f86af6da594327c85afdbe32be327bb3e305f113fd907e42bd5dbff823ea18f663775d1d527a32a9fd992678a7d8f4555f63f99cb457d30567df75199dadec52b535b2755dbdbcfdb290ddb6e3f7eb36db7ace3b7bbd83c6f3e6db11fe64104634490c5a2ab4fe62afb18e7ac1a924727547fff3e28e32bd22795d882f47178dd9cb8833863aeb2e30c9578da7c7e9f8cae4aaeb2bf7d324cbd185fd116cb636dfbb44529b5d976f5017db24d7f9f6cdb10c53f6004dbd994b4a8cf6cfb5352fab26efa9b68777d5acceea8376772e549957d4f0865e50cf9b2fe4cd9049223fc2315a24292657a2c5f494969b8ced8a16d1f6ff473c12a9bca5cc4d0509a8cae68cd55d635e76d9ecb749c97ca986e425bfd8dd26476a7a990a7ec27575488aa244b7d5b656c5b59dbfe78b2ad1e4d355321ba7299a9b27f3a653a5542dbbee873a12a1e15df65660b0ba4bf40aec0b73fbaf763b8fa7e24b93fc2d8a3cb5c4965ea3e15bffb9b785da4dbb66defe27e2e147fdb06fb600005c8dafe35b54e204f39cdcad7d0ec098477d553e62958ae349bac523b6542d8a9aebae5ab4fc8a3f115ead1c83c9aedff09d1d504927d42734e4a29958fe5ea4a157d2953b445ca14fa72d6cdc3be9a405b86ab3f0659170b3270823f3f0bd996506c298448cc6611f1d979e3f0c1f041307cccb1c207b9c7e1639b1393d147f783a73ceff3bcd42aff49d25545f9c2ede334ce3d458fb979bc4099b870311a9d484840fca3cee7488f73dbc0cf71a33f7d2e23ee39d28fa3e7bec3a27d6e64c2719fda233fea5137b6a19a22dfa72fbd1de1c6df71e2b6bb18e00445df330cc31b8217bca0078ae38c5d5176f51cd75aeb0c7cd3e7020261f70d4fd11fdd67530f63fe2359f2d3eda11e02fc30d426398723df0bf859db0f47ce61bec2037cfadde8db31c32142b9f3f7c23d06c1c7bfc1ef72ad2fe9f79dcea3fc21e638f0479f71f860f86016c110673087a3cf8be18122f654178abe41719cdf0e70e39770e32ab7efb10499eefcddf7b998ec486dd086a7c0ef429148be26f96ffed40643ab6d4c9a3f4fd2a83736e9e9ffe02b3c463724cbc8d327806409458a142952a4489122458a142952a4489122458a14e9428af2f7af7e51fcff3f397914ea4b4afe747a93e94ba51f8d3ee7ffbef7bce7b8c7f86b7d6bff8482ea019d99b2e5dacc14c7d766b6dcd74ca12f9bd985235235c56002d36162199e94524ae1cafbf2c7b0d76cc133259423323549a48a5e2230aa0e426a3afb0af98aaf4821b59c4d1ffbdc19fd1be42bf3d236a5bb7ef55545f258833280810b5c0ba02051010a8813d0474820020f8118463cc001270d78510403168052000c2260125032840b217620e0f46212840e204c2e2d7e38408ed226c16180028c0840bad1c29207306263b5f20920003ed4e801f442951400d0d88187199c08ab523ae420a34b65000317b0001215a0c0048e9040042060c4031cd0802218b00005100193802184d8818097207400e1f2c301726c1c062800016eb4b00cc0c66a4500357c08400f2a2900a03183871d54291d72908172efbdf7defb1206e55e198ff23250f4bd3d28e2dcb7075685992c7605193278304c76effd7b77f860aefefbfae25420f50775a7ead49ceac4dd28aafbe2df981819266c5a33271045968e8d8d8d4e4a260312031da20591e1ffe785e83b860e3a3a2f26c4603c4d383971e5d06ad5cc2007c84d0d50281a17a26f183fafd7eb47068d5653c38c0c6b484989908e8d8d8d0e0a0ce60385073990ff44719abf4d6200011224083c2fd7104c261c2d23f3a3899b1a9a14f826f979bd5e3f2210d04c0a564dc80ca3914cc7c6c646e75faf1d27cc1f9f1e28f81e81515363630357101c1c7c5feb454d0e4922343f626411dfe1898f4f2c0b4233d916380e86fa603056d9546ad0e8d9b99182b19eb1f9be912b1b18f8b102b9c08f35c7022ebe178184ebf7af2cb932a2023f56190afc586726e0dee308f7a73f72150209fc488122f0230d82c08f94b6fda94f1a23fc854bae66eb013fd297037ea43c0d709f3945b83fa591abe962c08fb466013fd21b0578c9fbf421c25dfc6ac6607ef45a027ea4ac21dc274d08ef91ab59dbf1a3fb20e047ff79f9d1819c26086f812357ded2f1a30701e24777b978e9dd737e70b972d7017ef41f397e749aeda377f7c1f1e38cc3906421c9e4ca6306f871ce0af0e31422c08fb3e6b41bae23575e6bf971eeb0fc387b06f093d2cc146ac37fe6cc965cd1d6eac769b3f2e3c411807b5fc3694e0f49bf871fe550007e9c31db0795e7c08c1249481651ca8f7206801fa58cc61321573446557406a5cd14faaa1fe5ce0e3fdee0e1c71adb7f865cd11a152208c9d2a57ed461eb7092e17f5b2892657b9f5155f597a7cfc56d500c5cc00245cc960b84841e7d1b518118056213d03d66cb951da147df21a0ab8b25a0471c010d014d335bee8f1134d7e6012d07b41ad0ca992d57a7083dfa6531c0b50097025c3eb3e5d610a147df9b0c269680d810b14d7fd266cb3624841e7dcf1a7dbc438f1801fa45d3ed27089a165d6d363a5a40b45c5a39b365d3f921c745571beb007ac4395cdbe5335bb61a1c7af4ed316a8058016204a0efb4d9626f68daa696d2cc166b69ce6cb12d5e63f1da00bc668306aff48857f49805a06be8d1a965d99a99427f556554459b2d7568a6d0f7c11500570f2e159da2c75306801e338dd80c5da3abfa6acd96fa33536674556d6a664bd59929f479a0b51d744da56ba9d6d6418fa79c831eb30c8da26b36fd0ec380660badc160305a031245511445d145930f468b22caeb47d17a9c5bd4a228c2b438b708f3a1352882807ea2a06d4041b9328cf195c5f8ffff7f51145f14ff258cf81fe3c58f31f7ebff0712439cfbc51aa406a1f2d457750d4114f5fcad5f3faf9f1727272727272727ffffff27251fcc9f9c88ff2ffec9c99f9c9cbc10e73e11eb8fda44bda93595a6a6e0ffda5c9d6b737560a05028140a853a3939f99313d4e9833941a1fe4ffe4f50a847a1504030c4b9514033292aab2664869313190cd1f70bd1777759b7e6b22eebb26e8d8b92929292929212140af5285409c907832a293979d49fa0f43877494949c9cb853877894877a8137e7c7a28145028d9904c261b32399d4ea7d3e9545252f22525a7161f4cc9e984fa924795e871eed3e9743211e73e89d486dac0150487e2a0a464fa76f1fa79bd5e3f2d4c2693c964329d4ea73f9d4ca60fe6643295fce94b4e26d39b4ca6dadca6da10111afa83c614399da68d8e8d8d8d0e49a9542a954a2593c9f42653a9f4c1984aa5d39bfe64d2a5920f893877c9279605a1996c0b26d3f4dd42f4dd6dacad66636dacad86341a8d46a3d1a8542a7da9341af9604aa391e94b6f9a7ba447a3519091e844e87123944ab221994c36349273ce39e7d16834caa40f669473e9475f1ae971e6bcb3e833ae021cb7f196b360349abe49af9fd7ebf5037edff77ddf9773fe9cbfd10793bf6ff4f947f9fbbed92c28668836abc8d346c7c6c64627f43ccff33ceffb3e2f7f309fe7e5ff3e7f7a9cdbd39eb7138a737b3b3c5190c56053f8be97efceb26c8d65599665d91a11c7711cc7719ee7795cf8c1781cf7bdf79fa7c7c971a29983a933732690b903cf930dc964b2a10e638c31c61cc761f083e130f69e7b6f623c8487268b0dad393365dcc071a26fd1ebe7f57afd6cb5d65a6bc5183fc6f5fb6070ad1cd6e3ac75933fb420202f601bbaba33aad2992d1876b271faddb5d65a6b6dadb5da971f4cd5d87a7daca7b5356c0d1b3decf92c1e211f54248de93c78281975671ed60256c1f80583a5f09cd30cfe1f8cd5d84fb28b864d3f6791fde79dc15058b54df1d542913ca6ba19de09068351159517dbc8af13cfc3eeac05db3d5777e62ada220d2053f3318cae0e802d4c0c9b5e21b6698a575dd6b0b1e7ab8adc59a53eedd76d351764cb183e2e3067177c86ac8108112244881021428408112243860c1942a3d168341a8d46a3d16843860c193264884f11a09d3d45b1276950e92915aa427555f124bc196eff5757bff3177b437d37fd0d377b625fc170c83aea18767d2b5be6cf4c999fb7904d80c05b8e3dee9e1aa7a8c98b1e4bfb349fead4be22ca4ffd18d5ea9734aec8b4b24e29893d2a5357a2eab6fd86dad56db5d6befb948901b627ad76bb981375de0786019dd6a844ebd90b8bdeb1a1910040002316000028140e86430291589487b29ae30314000c769e44665a34184aa3148632086210530a18430c01003020002322541a0005f4fea0970ea8de067a9ca8e7037a4fd4e3837a4ed0f3a11e27d2f381de13f57c408f13f57c50ef097a7ea8e751eab999d61a04663651cab5761a911d98f684481d2c18483e2da1e08f722e11e30381dc908b215a380d04fc3c623937dac7d46c317315e8a7bcffe1c0c443f2c16500f1508fa3feb8eb805d950086b853bec7898f427702aca8bbfc5aea5dbf9d7f178b19885000d49669b9522cd37f8e2a33644123265492560f6b552e52df7c2d626c69703556a5e2205098a4548490f8cd22fe46370aad1854a916de56563731707c51b6200db0921d3e7935617f2a1b042b37fbae0ee108cbb8ef5c2bade46292bcfc96655880b5f40781d4be09cde4fe319b1913edfebd337ca8f526ec1437eb94d2fcfde41158f3e1f59df9827faa1257dea31b8d54b9cbc61ffc4a6a6c8bfe9244a5568687732a095dc807e34f3174a51fe45f279148c7f873d954d31476ce79f49a073a08e1941ae4517633e2c0a974b2937740d95447b77198537e5b038cc39df25f19e08b19feb7d4b34c6dfd4a79c794d7e63f89670cd8b6fd93bf8a816f935fd99798596dfd9779c620d7f65f92770c986dfc937e8a81df867f898f28d2f6a98fec2a2299a849d17186ad36db8a509ff47e2ec3e054969b1204ef0413346884fbe10f02d0deaf7bbc7d2f252899465a95a492b4694b09cad390462554927e5a2945d934d2aa2495a44f4b4a519a4eda2a6d7261913556acb2c09a15930b96acb1c49205d65931b9b1c8120b96ac618dac813288aa14de42db4a8f859c2944be8a84f83298ac0c42656ee0d6bed4192418841ad6b1658accaade00124fcaa071f09033ff44ce2c2b72dc667fb11d824ed8994d584c1ee56ee442432579dc7b12b3af9b013607dbd99ef4a0587824f10cbcc6b6d14c8aed1c38ebafe33e3e0d9c01a475de50265706c9250171c7a8514ed0bb0b376729850ab9108e00b345a15bf0dea4f9adade404fd2d681d07c617e01f44aef60963a771f409b1df58127250dbb5987f6972c551c9c45706f58c0a7c39c728932b77e2f62cd5d03a2ec4219813887ce50940f19541d92411772c3ae524fd1bbaa506cc053431fac528e49648293de142ca98815d3289ee0aaa8e037108e70722577d00b93bc56530ba49b1ae925d66d93521379499908bc523352821bf580677181168490751932b37e5e6591aa13a4e94279017984cf521c0eb97c1394910e1340a8549ba0bfa951acc85856c0b1321478b47360d7d1e0ebbd404ba3b281d07c021c81f88acf20124771a97c1689362d3869b90323474d431e72229547fd866659090a3cb23ab8d29689d5d7202dd1d341d07e210e40f4656fb00534f3050048ebb319b9d2609b916c62c5fa5167c49a65b06b5b700ab24283333837d7b964aed305be21f9b62d4385aa1e012f9063411729d798becca9059a86f132e4ddc81ab88f412210f8cac30483546c51ed405fdc07d467a73b98c04ec141b3686b543171e5a08ae1bc9a7a24370479d877e746af16404863692b766979c96f28045ad6d1be5cbdea5f94e825b46f25125b86130e034fa8e07226f4a871db7819a80de47e7f6bc546004a2b938faea638682781bb67ca360b46ac6096de2851e3775bce74bd845636a7c7543501aefe1acc0a5fd4181af4014c689572cf088128871d991d47a85eca07e6583b640b7aeb41c30396d39ae80c464b468e8da5e908224bfab60bab16903bd7ede16a93e7706582b31e0d253b7c946045ebd47fc3bcf02ae6c23dfd4bc4461b24856e830526e3b2d792386f9554b1f289c1811be115fe7d24ce3b5e1feda5a0e4eb93fe3e85d58d13f0823cc27c11276b941cd8817e2d7c4a0920ea61e7b55900dbdd4c45c028c789a45bdb12626999674a526adc79c9e0fca63babcfc85bf01abaa6d6ed1ab927b813d6df3ac7d880600051284e5ea2be68b9a8c4891befdfa7d14a62260d69875f4f9ac304f27b07ba5890500eae10c68e77eb259992a72d81ac7eade64002547db57aaa7c50fa9a80535002802beb5476daa82a69dbd7a5007445e8b0d2bcc2027d193f27227622697c64a38dc7136fcd5552bb0cf8f0f6ffe20077e9a984865ab7a2efc294258402b52c061cc40e0d1747e9021426003b0314714a90f88749e3b5de9a1fba3e2d69dd7c07e2f076b6c6dc18d675d40b6479dc581b3d2c32044824e9859751b8a75413090a3977db56df2232c773157675400a71d0d5a253b35f6b661ae4a2d4505e41c2ebbbbd58b9f2ce43526645ee0764dcb364de36d032530fb5e73062899d6cf0723b303ba2ca71864dbac434264b421a44d89e270d53aacf48f8036fdcdd0e99dc7ee9f55dee8ea36b99045ed2ccb6a27e649b991a09e60b44023c5f6cf5977a58af7c21a0f2f1d6215bc57a375937e77bf56de784fca5fd5b3b9b5aa7cbdc2b9d18df5eb35dfdc97607b9d9ec09ab846a0322d6168cdf8d08fe1a00964d53ae2333163455fb9f976a15d10f1a2e167b512b0187445f0f1aa7b7c132d03ad0cba2f6ed2fe4019190cf75e9045ec7909fa4f822e8dc0cd4911318f0d586f0795d8f92481d7426cc3d6a053d2ed44f479477e4b1a3eccbcdd471679bf95a96c9236d17fc53b3629738404dec089e3d38923b045452b8c98bc113c00c36211fca74889cf82fc8a2482df053ea190ea81c6d889a498bc5f987201eec5d12443252e1b30a3097016f16f7d08c4bd00f516df6fc8cb0066e5ad0c855ce616ad824b6cb579a819c33c2a5d627a2a88807e362f472e736b14a5651aa7c0e728a41bec0099a993f067c04b25c0e78b15a17773d4fd25c299be282003c2a2cae8c97554162f60a16640cbafc3ac631f2bcd5902f9e2215701a2e0eb00e17305a4e84550530e8445605894827e6125c1f141816fa24ba51569a14716b06762be67a14986255f6d62d610158a8445ffa14a8938179d9483aa05e2c8937827e91f94aa0c0e0bd45160f367636d7484b4971decc28b22857333531d9644b299c245991242415988b52f0ac15507be9cb3e8b3ae45011114565d505faca1b258034b35011b7e1df256132c855ed8ebdf441d359478f499331397164502a5641e329d0358d229fe576f027008047b27df1ff46a09ee0b7011e630a548dd9f74fd2c2c4c9b71b90acc951e0c1723ff0642fd637083c54fe93305e2997aec5c20bdac0b0984f06eeb52f8d32e99c3064326868fd597c89c58fa5d5e4dd0b02818e0f5c570eaf0302e45417007e3f411ed2c0489969e49b3863e0e641a8594b1012d751cf7aa3f22208281bd557216c04c932099e1a300ef02f4f88937f584f894fa9afbbb1a6f7521025b06589d6cae40831ccc99c085166962285fe9ab380ad12a27de216ab41c7aa10926a704bd548fdc439218168089a1ef6954a23ea54e3f0f07a6e0b554ff472109522a73b1fc665aa587744ac70109094ca5904cddde1834f0e59ca34ff52602a760b07682c2f45be6f2ebdc0153f15d74bb9862d9e42538369603a50a99d5df2054ee022e53a6846d787d238017470c6d7a62600084dda6f6cb26c648816470890098508df2646e98905a77b85170cc04e29c3638e84c2da9ae42d3d2d827446f66bd9a012fa3201a6ca0e54e477e754f06c900b077557b13f415de9cdfb843ba18f56e0000be051793f7f084112c923ffd42aa60260e1b8ce1605d0cff41d4cc2350f551a38d8cca0e33d0b28ee2bffa36008620d85642fe41b8ca60dba10eba6042f9ad4ce74879e5c2c84d6e8969c5d368ba0016df825f5b0ca0e50fdf8637a222b4cffe6308f08fa1213eb0b5e169595cc89b9cfc81b080c16e32f1819ed265c80257dcf7954311e2e90774a03663397bb9b30b67b10012a84ad4ca21612b651a114f3a63a98be931ae47be0c8e7877c96db326c49ae8c941b1079e9400e124a68c4e45b15a1a61c687837495e92801177a018ea0d05c2de9bd36d82526df041f4e88e62736b8d1d0a075dc6a9bb05a9057b4e30064b03f7296595b2cc94c734f32754c5393de437ac989272aeb32a39befe415094a3921e58a8d52bc8ede725f6ba28434579ae164309c20aaa2d9883cabeca66b30c34fcb4952387576458145b19ed3085f8d901a921bd40c21e660066d556a8c4c57c602443c95cb52bb8d81c80dfbffa03cf08f1c378a3f524c5e939e47d7aeb210826e4ce399d891c906158bc62218456f1c5bc8154b495ccaed9617a25b0f226d2094328f4a3608028067cab832cb281fe9181c28e47e922a703510528d9a0433a07b75e75e0e31277c95df92cc12815273001dcf2c805346f298f996f2761c19440a01f9d4ab28a2e821b78021776a58501b5618b9c3b630861636c9651eee838462f8afc67cb76ce5929339110f991e5806bd37ac1a8528f92460f5787559b006379ae256e73cd96983059c8fe76ba4c81ceec05ab3bafa7b996a13202a7bfd01487f989d0b63efe38d6e7a9984ea46c614c6468a2d8819195b20363276c1d88898c9d8207483a31b1ddf28921b8ed3ae606c44ccc2d84831053122630ac446c62e181b113b191b4237707423e21bd2b831c1de0a5caf69bafc4fccfd4d91d8613f61e4b14b95a756b109536af81523063eb75cd80766c20521a1bc5f8974b316c399dba2f9e35f6e78177b3c8af2c0731b7a2b153847832f1bf446fe539ed201a5019cc0f4ce01a80772e9c06986490257b0210361d06d2fcecc88bd2faaf6cd78dd363a5cacd3a23a9bb139e2c728853f4eb359e869ed64022e3dc2597ef761f128cfe9bb77dacb030144f0e738dc627dcfb084e032b12f9807c3896f2292da38dc93536d6118b2b4da74fe44d46d9df722870bb1be45bf860e54ef04656b890074c06166b0d9852ea382c2a4a5ef3db67409b3c238f7959e17612f64326208be81ddb9be2c93a6087b29b85b2d202cf8c5fb721a52ea0b9fe14a68d66796860215187db685f8fa2e54e5586c300fb1ef24b796ef546e6e89928dca3d355a2eabc12320887fef64c6037b6ad61c8e47edbf1eaa8bfd6611c6f976d860f0a356371e6ae2aa2fbb1212f5a011033352b9ac7ba0791ca394b952a0b20dca8d36db287d36300cbba20e449ce9d1d2520a01a0d151c17dc0ea225019b82e35fd99febdf253f64e4c6c074962b4da8bd87e123ff14d4dc87fe9857d7e5f05ad18ccf0d31e94ae8a3e55426d0a4ba7b0c613c07d108c0184a31999eb33e11dc120c934b053942fc622252e1aafbe81c15de0ba99ecd265175dbbe9b2cb2e5d76d58dbbd4d08ef25a1bf986d5b871c61e337e2ce3c71c63ec783a9ed9783ace31e5c2844623a11ccdad53a579d0f5bc5a0c13c03167680118e935881bb86ff7f9491015e85e27a8f550b9bcea5d3df7c424a2698db71e1bcd78f6b25e7660e1fb5cf6696756065e9870628fd0f391acbb3d1681708f2ae90076304448fd8134f33959bc6083fc75935bdb07353de717c85a5636cf19deed64258c2ae5d91a60e504b034ade8d668dc0cb28c82d0cb66520a6abf9adb5255907394c16c5f229a3cfd4bd8be009aedc3ad8b4fa69750a668004a683d50a75c2b6bd54438e64c30682eced33589f56b8cb660d75c68a20863d74c4d974dca4eb4a723808b4939ae9000030c187d426773a2a3c09fccda109a0535905cfb0a32954befd088807251958a62ab4e76f77f39975eb33725fae32f1234b3490ca632a25f3ce927fde89f698a12a66cdabfbab3085302fd579efc137ee4cf7b4482299be65f9ce469cb702b6c26914f70b8efe1ffac2e274c15f3f7eddf11a66cf37fb1f77f52e5f7f70f61ca9e7951b9b9a86d3d4c0992dff4e14fffb1bf0c1217a6eccc7f91b002a604c96f7ce0a7fec85fc4c70f5376e6bfa46965a1b1a2a83bc2e7301edf2402f8230a98b269ffe2c7224c09b4bf78d24ffc913f6b354959cbb861bb3715a632c96ff8e0a7ffd15fc489075376f67fa958075382e4b77cf8d37ff42f62c414a6ecec5c42b3075342d9f29b7e0cc3d4621785c5da51277ac2542e4857eac321839d7a09502e215121dd38e36b1dd6970dc25f42341e30b5acda55225afd8ed8106798fa40002702830f960a7bc054d6637f660b3e4c7d78f142bcc72562f64b18e0c8b8a6f330555e3ba7c1d4cf213eadd5886e717849b7bc78ef350266090f2de6ad39a11ea65666d741a117855243101be632aef96996b2c558ba352596d21c8513bf295ce977c8b5b7bf00fe53dd9585d9f728cee298329819807c0f1559373b0b6627b739d9eafc81d1614f27f8d00976474c1aaa9fcf69872c0642e0a91876033f5a73ec2f1e93d36df9566d292cfe66b56f831430d8af2a07b5cc4cccf8188c86725fe9096cae33461cae87b2adeb8d4812cdad45d2840b79293ee146910a52d49004b8ec89c594e2387d22e0b73891dcb4ac35d254553b8a5528ae0f10e2ce5434570a36150f6b688ace0e7181db85e6c3acebeaf5d902b3e2372b3df217e8479dbadc22cda6e514c75597e79690347c3e910f0aac628aca9472d662e496d8e7eb4120b1426bde1d637f28a2bc47f76fac9a6455cfb02aada7104027827a5262670059c88785973e84409f81226904218082384f2e829ac39ee3592f29148a6960c3427763bf19eb844a23070666e63a165b46585ffe607f46a106d6ce4b3fdca7d33cd25292088316349f80bc1301a7f0244e84bc8a568115ee367f372b40aa5305a4f8571132b5f84390e11fe96278630e0158c09e976db58a867ba8c9be41971415ff6e8114b4c1c80c69ab45a1d4d1eed3330b4d066e1555996cfe7290802334fc177ca06410887e75530a716db0fe92f61709208edfef28280f5bf32442312e580d8cdfe27d4b23b26b0f2d1636f63973a82418fc0e5a1208dedb7821fffbefe67a4953c9f9a749277f80d98ec180f8d18afeee4f08c5f560eb1a3feb1bc8d93f9fee043b85728e974cd19cbda732cec9f16f0ab6af056489c76ac90fc33d4918ea4e62fb6da3e149cb74819b1bf7fe1c9a76e479886b5ab12630c8a01d04bc4b10b9cf2063355488227a9b14212b00419ab0930f48099a26990968c97e80c0e41bab7b2444a0680bc2a5f03f10adf7e88e1991800e64321a6af489f2a7ca749615a67011c8fa73462f5044d4afa8889e1970ce1a080f3fe48f1b587744f4547acf2746da045f86a59e8f30fd1ec5f05bf659f3d0fb63c28d0e679089d33f3a92d34fa6266a17aefd8743626d9315370a1528e29e9598ed4a44207718a0cf6f26b6140ca47164cf862505d94b0518177c854e9745045b940531f81f4d317a706521c023a9fe61b36ce87bc015d7c18a2b586f2200a9190ee277b25be85b24c705f3141e3cc99bdd6546049952e4f9583cdf21e60f410342debf5426975cffe3c39aad255d8cecd6f2ba61d8127ea09f1147ae418c51f86c9eb469c5488970bb43335594e0938820f2eb4d5a73c0fc4badd92fcb40eaa657d5780a17b2d6274930dc342a98c9542349ebda10a36ef0b6157869e468e40ac5c5a8d942852d548910d48f657385234181dd8b88b344e391d3e34f737afa91563ccf91c776a3634262642ce737861b32386982e61aaea1056f88e6cc76433d07cc5c8f9a1b1434c4e0052eb37938ba5d4c867045200931f6014cbdd561b2c4a0ad0f23ce6570d9feed870bbf25a3d181f09d69043125dcb7c9a69c94b6b74d43adce1d48d276cc8891d024919f081aa33279a0efb033323d58ede85968985135024b8b895c57570c49691e06e64d70fc4a423eeba96ffefba7381cce9db1b072f1385e638d57933feae782bc210b75b76ba8ac291e15e33a3994b5de3b478a0141989e2d35754bb0c24b3ff2d2d9486e6692391e6000df476e6ed2f5baa20b214115b2b883add411a8aeef7cca07e82290febd170a977a9e01f1d642fdf22596924162c6ef9cc3a043d43462648b1ee13c272738aea5fa0d453a5c09cef8b8c677ac9d2d9097fe592e408742f1772102d2956bc1548faa6ec0316900a5fd929882095e07f0a498eaa30a83c3f77f8941628cdc26140e500896cb41dbfe85a023daf5ad8e0316df9ccb70b3a1915cf731860536ca5b1e49c1d93dc281b17097afe87ffc8028d7a63a8be813f9665bb45e6c246d362dfdc67c06836b1cc5b791ab7b930c27b67ca079632e94fb69dc86e46c04731dda40e8f5ea859c3e334c58fd20b304632da58abd6682f9e7a0e3d99f0f2659f76a465d98ed30966134cc70065c62b16beb0e9c01829cefc8901f34b4b40f98176d063be08ed0f55a69fd39fa84f271680c3d722da3b57f9339370806442f9bd8adc2494f8df73c91eaebca9a14fce8a13b727da5b8f42f9109c95923699ff931d9486a999f7725422755b04e715a28d8673c26d42c3528c535f9609cb46a82d082fbcc93bb9f2a8bf9c4e3b3317d8e46e04acce846e4be815f5769d88ccdc4c18658dcd519f4ed0d3be28791a93281d308f156b50aeca73134c8b2d9c4e5f759730e5d3716e25c7b343ce028d5c1e2b37e3dd11e82aa17c7a10ceb03113efc854e6e0c1216a4d01ba21093eae7123eeda42cd790d0b446a4c123018d09f11e60ddeaa85223a59d58c50ee78b2862f2c65fbe516873d32649332ecc40eaed9c2f0f69d66de8a5be847d53f1a2cd33c34fd97018a8385cae10fd9006e166c98b1bc820f30629e236b8f014475ee9c1a2b6f8a19028cdb83a4b067e35c201dfc9fcd07b8308ab2afbd77cd7ebfb65306e084af515dd3c76484c10cd5a085d3cd0b56424e45c41714fd483579aebb441d08c733f45009f4867e8efb63ac37a5976cc4f4f5d7559233b2abb3b9e884d7047c9404cbb85c974c936e2084d0d44fe809e05180af52bab1dc428af43e44252d373871907258e4f38e548ace53112c9d98abca439ca8370f5ed37d9e0cb97e7318436db60f0faa3d23d0a74b3c1b45fe969eb64c07d18abb786438c87209ad87947fe0142855c9c80a7168ba79bfae1d4dbe3f71e1ba492f1ec4f33e6083f3e6c74b0ad9bc0bc6979e94fa74040093f329709f35838b0dfe88262fc7ae6350ca8f9a73d5fe4c75883aa0aad33d50ed1be6897bd1840f0cdccda00ddec32fc73e44bed61282dd14accf3a984b17257611e497ca921f0d215cd39928cb05de34a4fe1a5f6aa92cde88966d9802d54930b158a96fee900f75de306f4985658b3317b115cad6297c74cd838b58a93715280fa9fbeeaaf05a84d134ff307d1b13f9dd090c3a08615ae29289ecabed8c6fbb57f5bc36e14e3f46ea1156b9da9d6951dae38d65a3f3aea2808ef8677e483a45dfb4b6a165bdca6f92cfc545674a6101879df338c3cf4e779f93679fac4501246314cab89cd1e4032190a24349152b9b2c25487a95236f9a5a73d4790ee5f1123850b37eefb032f7b990a8c352ef1592de857a7ef743f16f6abade82eab248b3fd578bc75647e41446ac41c40ccf46074d10d6a8cf882e3a40b92a0c2e97d2de4229cecb628715f7b9c0a3b24b251c9d357c0216e07e76a1a1d6d1a3e68c891d64f3430f7591c6e9941f09ce589a3745f4a2b20ee7af4039143378124c61d2bf558350e24049e6c65a9e703e54360166298e9b0142506aecf475ac9902f6815e0baf9f7241b514746a1d06ec8145a9f37f64e1e177a5212955fbd7160c6e6ae70f6eb6d113198aebc1cfe1fc65abe593a469aa02c41ba23a8f4ca3fa220411821207cc02734f0ae72ddc2976427780084f6a283286c52ba3eebfe23266bc1bb6930e7df42fd58a1b6f5d24366375e099f178ec3169921e93b1e9e4b051706d0262473955648ade2c00b939106d816bd17a281289dd9f5e952816e80d824de4ba9ab3dce3f7cd88587c492b810dff9b8c5930911c974a88a9237c63a836920def5e045ad19d783cf6f128fd0ea0f6b3da89816406ca1f34930c6e086553727dd6ce917ce7f1d2ef8749f1d0a03b31016a4181981453b0a15785ece90dc8e25b62c5588f9e068a90b1c9aa03f3b5c0dcd7bf67d109b464afd9e5b62928e3b95ec18dfaa412f40b83c25668a7e101b384fe2bad2b0e742aaf5d63450772fa12ad9bff51eeca350c8ecf823135587790215372ba333efa907edeed72c6e5d9061ad14c9bb914d2a8260c1b5d7e1785b7afea5143c4fc6ca3b25258812b102413401fa9d0929c7a6f9ce4c06c8d68856dc76893a5ac743aa645000eedac4daecd658f8c50229c5f8396f7918afe40a49a3ed22598d5ba242494671166f8ad9249c79a783e58e13ad11b0add3568fe6586c13e9b96e2e66aca71e01d16e8bfc423c9aab6b37199af2fca87c1106316cf35d1702b872b0287a6ea80479bcc7560e0a11ba8ed57dc0802273eef743ae92ee5255c98890a551c755b78de932db4640bd08125a5dd0b77880a04b80acbccca4882bf4276b83f2aedbc6c774b014c1b5191d6adf122a2922122431c8eefef545ea6b86420b700aff81b5aa2878429f6e8ea4d94a862c846fcce523ba7e005a0c2888d05184360ed9547e7d4bf7d8e69f0d05667ae84304654b03e765377fec91dc9a41a299f52456bf8a3fed341ac40ecbc8e58f781b8d41bcf126866140ba6a127b3fa0348e858e3b719e06008802ceec47d8df2a3d40b7b80af81a2a60f05da3435e3498bb39852375bb61d1d8f3a1f54b0b67674e8c83f9419a1c75741d87fa602d0e9a8b03a143a4f95c33f07bfbccde80db3d7b5eb2473016c88cc343b44b0cdcf05f63b2648a692b9e7ec518a2eac0cefe9690a78a38f7878e03fd0b6de1702d0eb73fe17bfab01f5c4f2d251cfd62f21729eb76fbf7c3a1fee91f637dc83a03500456bd03c501d73ce56414942fd5ba348de0ac38b9a123eee50c8dc9f8a5596a1d122b62430b0cced7e57548a2e3d64e2a1d68e261a9e118d5ea6a2dcea2e96b6b586763e383e02a64df6ca6526ff76a2101316aa479cbaf4081023e8f1ead510213dba206daa723d0ecc29f6f4a184be0168552ca36cfd46b14637a836c25025122d31a71d25753a6f0180e259dd5ec520d89942962a01d7aa5ee7ffcb784fdafc759740ca1351420f2153d6b3f012381febe6d47f836f97986e017123d31b1e007305a0f3d9b538c609be274bcb5df008cda2cee4c21a8a6f5349ccd8667c9d5964042329bb42622dbe5319704bf1be48db5f8a690b8a6d2bf7cac1a835b4bd578c6addfbe60e89e9ff798bbfc3ee7bd918546937aaa1aa12124d3fe43239cf7941f86add97371f48ddb45eef27b8fb59f21b734301377fea5dec71a71d00b92edc690c3c7da6760fdd0880111a0ce145e75aea84092a3034fb3555d21c1866155779c4da3512d58238e4431193ee762125a0b4cee50eaca5fc298b4bda65d06cd4271dfe35f2cf5ef0b66a1c661c616f868cd06ff6bd0ca9e676b7bc3e5e68d04dd619fe7cff2c6387beee0e0ab5f3f562a29e6a947ebbcc208c95f52dd4d78d0d3f7ed4521dada0030d3aa9b431482d894124087d652e979cef383b64adb377d5a19521bf130dc905e17e281b17d6d7c2ee30bad36913927fea135092d8edcca67a2059ce58898615854224a0d41dd98e576a55b471f258a0d6b05c9ad6b0c240a54f1496d7129b739450b59badec872a1a0b75af868ea792e8d16f66c6c07e33f13fabd34efa8316ae57be3781265115ba0db81deb129a5df871e23ff3ff8983817a3a80abaf26a0dab347cb6c721ae84d53677415eb8f3db5c08254735a17091e9dca1c20bcfbe0a31ad8867229d64f853321f32cab2efdd7b0a2e55e42b1cb8485d569850af92ffd5451dd140082748f0b9a59d130fdd839332384a307668d5a59e4cd1e4fb83f68266d906b770a81fb4a5cfaa1639d3367c7446dd30c803b87fd15f88333586e1541a56e8ddc9ca7051c01c1f053c4a6cb0470f6f0b199b4c1ab248d24691fdaba419cc91dd8987b5e49b03855053468ecfbcf557051b9fc7309dc0bb3c0059098839a4408da505a7f7d1b3baa3e7b29966bfeeb3cf7f5ceb644608d5d3b3229069e43edb59d79a6731f7cb0ba7a78c7fe32fcfc49e42284c40333ae68c8d2f7f877ab47fa7121adac00a013696c10361f774204f785359ebc180094a29dec63c7525e4344f615058af123d0286c76d0360246a9cbb2f138e83eeaa543b45febfed02e3f19cde37c032681720354560ea15c3bde261ddf138a4062c5d9964ec6629e8c6f3c2f8a3a04e66012a85b4fbc924d36fd3bf4479e85e300ba63463cdfc229bd647eb24589481eea25dc96c7fe45eafd622fe8b00398317e7c45e936949f6c0d59629fc557d405e3da778617b3ffc837cdb83c12a52df3f935930401d2f2d191153a06db5415702138fd2169582d763ae356ab7d7ea9b93c71eef30fe2e5d6da100695d96fbab46977b5feaba01dfd225592b6ee2ad2cd07fd7978b34cc977b812380678344df4402c54305dd0d4c7926897c30f3cd0abee79f1a87465c0e4de9f561254e93204df8e6a2d64744be91222e3558beae09f5906365596d65189930700087dba06f8d6b933b9135a7bf26e80d2260ff6d82904cfa7a1559d9835d8b4cd6cc079dfe7dc7ba464dc9ab18df3fcf6143e2103a9bc0770928cba23026be7deae0b6102ad8f2db62390107b027b4ea740e6052fd314c31bfd909756be533f9c9408d3eef99dfaf060a2214d19c25311230ecf78eafe60b061db45162d86624167dfde126639780073a63133573ac9c3977695e63a03c1bdceed27f0a6d56714aa25924a4da3fe9d2a362ed91c669ffcc1202892cde4e50d38a5a6721b9005da4f49b0e6fae7180bad9e80d3f7ffccc16dd9a076caf260109bad41a041f9b6230899ea4f0ce045ff0eb5941ba99cb66860f0ef736d351c197ae27eb11afbbb4f364e89e05af36520f497a3a1ba70070937b22a27d2e77b0000d296ab002a04d5c057580c51559fd094ed0197e5027e9a059fb16f298101da0872f0088a68b31e73f04a1562b51ed4245968e8142a2e1b71f23e4a9bea2656ba23a4c5bf0b7120b36e2aef626e360b5a7841cf3a46853d510a4c36f1f372c7257a8729b9235a8b66c29c091eef315dd87237b8b412d1cfc98243ca568d45b731e7c2ed0b0175ecbc0694ff931eeae2aab364103b7bceed0f0d5944983fef3b4ea79a69161c7d55e59c038eabcb22347f083907e0548cef2b1fc44883df6c39f45957895946b99586094033713987bf66114e685de5581a320bfbf766c4461ce36412ed105cbede013c40545d30ff04d23df66525028218c5f6dd59b0122864a4af0c27286163995676a7db3e05c84776eed05dc0cdd9e86181910fe9e885dc2f9eacab74aef6a17515c01d5aa7ab59d53b4af12b933a26207bd8aeab570c19ef602b13a630c1c26706abcbd68977337a93f7ac5526d4f74f971a2512472dbca6365d2ce3eebfb056aba0d1947688fa12595641656b4c4ccb8f3e837652ba62987d857a0d174a0c406174e11f9017e2e910d3e84b82fa775047f924d0c7609421e0a6949b10e3538c880fbdf8841d94c40680256aa9ead5a53988f0a4faca276c25a50a901818d25a0c50ec4a780b8330a8e61849a93315890a1896ebd2f7d2a0dc7fce0a7cda6fa0ae84f83c7bee7cef28b316ff881d0a980970174b99b521780253462ac2cd87c863c26362b22b07f4e0280db8d5c468a33e2758af2c2d810b3f04ed0b9c7d9da1f0dd6b20c60dd5421026243ac66a483c7c057790c9ad1f25d358de0d56d2e5334cb384928371327f90cc43b374065c38d5c8f9f57dd97d1d91ba31ce9fe356be07c18367cef4de49b68f317fe180ceeb7ea84624aef5c2b50d62c8d78334e652a40055e283ea47405199ae2577eb9ff69641ae51bd7c3826ccd7c27ecef2c1ce98954dc5e88dc5fa5c0bb425e629ca3a6381b26d850067d66cc80f531d9034ef4181e5ecaad8c86da9e37422fa9a6b544644ae4c71b3c2028deb771bcfbcb36cebea1d501e2ec48f62d31136ce2bc1b7718690eb0c647dc2cb857d68bbc33cc06042ff7cf97f5a3a8bd7165079813eefc2a4bbb466173e373139bae1e7db5fea7cbab68db333f533bf2c6071f05f9b0e5e9fb6bc1bc1690f1df9ba627f35c35baaddb16f1f81b3b4f8fb5a177b4acfa1308f28d4fc7e2d7170d3ad77a129994f939430832520ee644c2139803002def3c9c488f15aa27aee329dd54633ddeab14848943518eff26c0db99724a4afd4dc3530f043e766c989cefb0e14604e5708bd1a0972a6347315c88e6aa649d5a1dead00fe3b52aadcee3e4444477286b37220e4227bb691adcc8da208d525351a3b8d9971550a5898533016fcce9557ecb4ffa1e3daa8963a8eb42f4a3ab9c8b40128289fd87df1f16470c053d436fd6b2565b96751b014b0eebc571ea5da873aeb828c03b36f38c2d0f7ab90c6025a00672ddc1316f305e829037378a1cc350c83d19aaecfa14154059b8fe8b42e284c23f84a317a7d224e561c4c37a5e74107faab6595aa858ec75662dcf2878e96cdc320cbc4ca9c44175d6bf2ff7c415258c7a215622012bcd5a11eefe0c403c534cc0da14c4393a9366410220d51644246e615873380dc0d49d61d999762f34f3769fe0a4ddcb1427b8550fd7e2812ec96172b4b9b64d949fc89acdda04cd748de6c09093e40d0c25db9724a8747feeb0af75125dfe46a118b2d5a48cc6be3c805c884eec8ab5f8c9a500440b9642f6fa84bb51cf8684d08298c5c734481fae2e0ac186d208ba7c5f098172792f7e4f42bcba7552702efafe7be305624b03a26eb42c60e8459681b221a48c613cdaa14a837d3916ec88307be8d420d3b191a240a64148dee506e5da482c6b3d3019367ec7d09ccc2a18e539120fa6db5c0af734acc267725ee45e6961af858cc1f099f5ac212a37525d7c28fd33a9a627eb8d321451246dd3c926ce5d7151fdbefdde5a0e5340036999374f2aced26d2101f31322731c1b7358c443627fc473fa8842c40e426a2674a994e74e59797ec7a1dc5a8d868459caa81bf832f5fd91cce4b6976531e7d589823c69a785ab6aab7706ae758bebc8194ca159df7ddf4e146a4a9f0c9f2b0527818b0d07983dd6c896a2f63241c642d472347f2b3dd04b2dad0a284eaf27cd37adb9f9b2f686cbcfb46a51558538702a6737d1ba44f0ee44403838ddb1d1f12fd03e85f04acf1373f90e0d9e2821fd575c1cf1a245912e2b965a07a2743d47b0adbe5a3fe1c05b2aed3398b51813e621110e00dd545640bb222cceb5c83267b1c041b4e94c274c848474a44480ec993d48c08a4fe80f50efc461fb51f1c0f499e34628fa9e2c287e10e6fb05bc54c34122d7cea071a8fe63493c1b34182783e2e2e28b3c2fb65fa3b47071d29e5e093f8f16c09d4543aad09629b052e0ec3534e95992f44b4c08458d197391eb364159a3638ec3352c160c5280305b6fc7bdc4785cf2c8b1749b15068ad430099749b067d52d416917c50b8e090344f2e132901dffc8cec2b0a443955caf93535a25bda2daf4b1b951454f4a7bcbd507a025e3be535f490bb0379e1d1ac26c6b1ed3e273736b56d16eb6aae597747fb56a9dabb3afbc21a23c696bd05c101875dd0fbb876295eb3bbc2b7600e03ad293c76c32d285637eea80804e6b224d663f2f7d651c902d60c8f79320fd0426583986f3517203e4375705d26019e771818cc72bf3453c771bf021b93ebe8e891919b47fb08c3467672dc6663b243fe8469d9d931b7bdd37f71e014884b973b7a72ca1b31ed059f7018fd3112b0a9cdda7d5f3beffdf048891c62a1ef2de81c7ae016f197462033928035919e8b9f3bd957b08548eb22d8b3361954c2f9fa4a6a21ebfe1c6ae9d42b8bd2cab71d061170720525a555d9ae3ea9ecae2b20aa56c5b6b88b5c3d3f50d7b376af36f4e2e7deaabbaacdd55c0c358475f1257d0a605e05f970571ddbb9e6b8b042e5345c26101222711ff4d02b389e9d67ca406f01690ade5e6a8643934ec97c4b9aef00bc1bf1dce831d46a191f665893a2f603258340e884b644eaaa4befebf53bad4b1f7c260d0a51d1a2f9befbf22f2d2fdb990059ec9245709575f26b9fbef3866ca44945b828dd4d1dae91680a5e580619a8a9770dd32805c52c56e3387847f60b8864b64b2ac2030c7eb24d5f840ab64e7773c17274f7d2ecaca91ec3a0536f0a1c2a3dfd97de028b87b301fa440c25951c0a9dd740f74df014b917b08a2c6875b9d6311ec440c1d24882bebc2780f2fd75a8a7cb51811564fc1b02749986ba614e2b3a38686bd925ae0eb47f27a0a4467dc719947a339fcf015f367dc1de52242340f2c295c308a6e1d9ca480d25826f494103474ac112462adb60b94bb81789737668384df8cbf56ff93fb49b0b64c922553dbbf643fee7a3edb8b49e0bd7c9300b9c232135c96c175f8cd985d7d0520b56fb2aa1dac8730ce4075cb94ac36b5dc8b5c63050d18b93dd158dfa0e3832f5854bccd8b9fb00cae082d8f12527848906a067314dfdca45b728e30a7c86852f6e6cc55feec782aea58f432e8ce0949c37e9d90e6fbdddfcce8a7d8099d8488bbcb58e9f804201015bb45f69677d32aa38626bb5d3363eadbe769c24430c2da32b2b6743523b1be20404ac670dd6433b8709d7c29a89cd2369de6528e6c54b82efa90f8ebe8b49a144b854e25f8087363711189b39c67ea1e094ad1ae531e5d76f7c901afdb71d64026ff9b0e70ff270bde66c9569dc1bd228326dcd79dcd5814c2c621f476d59f01e2b7cd359c15a007b1bdb675b8c790c0d31f9d9b93945bbd1d24465a0a9b05d49a68bc3536125d181b212c2e28a861a1161169eef41833e5945b075721087347b756b6c3e4f711d3680100335bf45144d01700fcfe1f6b77f7b98396dea6a355268acbc9ceedd6b3906f88bddc296446d14e3f94f9c47fd6d7828fe387b7a3ced6445ae36e858866e5a345177d2567b50c8089d56ea476b9b78768f4fc68a2c9df42f7c3581ce10a80b63ec6d82ba2b0cbc34b1f594e852a6ae83983328a06245e295b0b8d34250fe54216441201add4a77567ea97ca32c834c2dcd91d11c443f9968604940fc61469806bdd88fb5ab1528d9feac47074334c90fe54b739bb9771053744633d12daa97b27769bf2dbc3617110196d80b1124cffcb4b164264a6783b22446ad31ef8098d92981c54dd9e4254a32210d31770ff49354065243e8431e1a77f595bb29ca8bcf829dff00b39c1250698f2b01de9d9af31ed7f44404b1f21982e06cb30618676e41dda64a90ebb8fca6e43d4b293563cac312cfeffa0b49122b8fba0aaee3247c2b0c7d3db96368a3b2daff04f6888108f3b23b8220f1c824885c94df2ce49a8e93ac0504368164cdbe47a0962326bfa0bea69892f1dbebf74364e7b2a66671d7f31942ce83ca557998f79baa1be5289b65956d6ec47e77f1b48800d24baf89ca4a8a27d0d9d63596f06c20be5b0fe99b1696ed7ff1642dc71717f4acff6dacf13f38031f2c0ac794d00c5708838322ff3667333948513f595dce4f3a6ecb6916d36010c9089cf477c29468d6cfd4ab91a1ac9bf25db50e4962a1991053ae2fc55ca95b04829be0ea125d966449b72a5d5b2efded44e7aca74067c0986dc2f30eb0dfdfc6a8e440411bb7ab8a9c1ebf2ce7ca68da42a1f81e2554e24b9566620bb14c4c9b94b05cdb24945d70a54b959642064dbd17761831b086bf23fd815be16b196383570e71a7f29f5c1f45bc65af43c0030cfb3ef86ce2c693de236aa91eb2c331ceb1fcc5d164e807994e0b7612a4912e3f3c5b93d58610c51674698c7da2c4b677c3d8ea6cd53dae8c3fb6ea731a9f76064e817aadfb724b9dc7940ed6293dde5cb5093c740ac0a8cda1f605944b88f58b3b4a0a0c22c0695221d33b3e3fdaa532129c0ed29fd32b010d23dac4648d524375dfc3e5759bc99415c2a07fa31658dc1e6b3361da808bdc9083d1710582122f820cba2c43d88244b16576f46029e05b808fa53a26f6e49696e5baf9bac71512d6a69e041568890140cfc4e227cfd303c984ffcbb15bd5f447733b82da7a0fe1b41546536bb7d70c6c5ac7b8503f7d9ed05376e66bf2f1cb89cd53eb8e262d67de184fbacf6801337b3c79eabe95a838fa36436455f5198f53ef8c1a8e235106e4d91d2b89f6d9f7025b3576a0a9bdd074eb8cd760f38f1563f52c996dc03475ccd2c2c34857d813d53a064e2d47275a6389c7c69d654c69b29da8921b2c9396bc48de6e05e739d69c3f8e7083ffb2653e4d2c064a1ee26720573392007eb82f4895415e394992214ab5950ed2a41863f9e79f1ece3f3229d7cc733e57def9935c5d70920a74ee01211997d225292ee9e7d9667fa9e982742d0753f3969c34f140cb75695cd17d52acc1e6436fe5e1d1b14fe252260fe8c5981c167c950a0c7f352650b3efd1022da6c877bfe411f2f86d637c4cb0c986230c2da100185a140d646c4e11c4081b0771ce9916b1224f6c7669354125d3ec59122892dff99e964ad83d65d2520701d0b9c64ebf5d409fab4c37e4727f275b1e8e83e2885917fb7138df07448bf8177cd74fa6e131449ae8dc61c714db500cf8e555d2b7674860e99e3ddfe454709e77a9d0074d3c0b28d5c290c05a81d30f39f74c04b944d15e31854e995c8cf56e35e53425c8644edb23caa3d176ab6cffbbf5f6e2c8088e6c31ba05a30c58c0f41eedf3ce310c5a10870f4a92193805bc7e10a1acbbfbe9087bc7f96713624d4096ab898bba72ac31bc80c0195d9a2f4e2671b328e53decd3ace9b9b0a381d0e556a1d67dce61149f8c96ae65d9ee6e8f8ea27e15a7f0dbfa97299e6b497e1575639a8254a627533ed5ae85a72b9084fdbb6092db598c38dfa94c7db71f473553ea1df17fff8af2d40e70724446fa8d48e45b6d9adfa18f76c5f98f4d2b5577c6ff0847b3d59dd5b16f80c5d019cdcd296fcbfdcbce8d9aef2c22bd65e9083abadaf1dd4ec9347d987f3b4aaa3712279dd58e3924c4dd9a9b08d52cdfce88f0b425537e972d148411171adfff85ad71a105abef0386bc319e688907fc5f6b389669c89613090e2e80bd7e610390ee617af6a6074966d812642fe9eebfb147cd8fc58b3151e3a27b49e87cb93f71938ff984e4838f74a23d0041bdd8d61caac199eb3d28b61ae7e6d19be2081bb07b4799c1c00bf26c7a7f2f27196b865d747fa0becdbf035da94013a43c261aeea0e8d233ff77c302241019ef9bdf199c960d368a2dafb0bee4f5c7516f6e1c7fd1161c509122761c97f965e10940345694b574680d94368a1c4837e907a2e658969b3bf59e27f28aac4ac9a47692ef6097951a70d35863a55dfa9aed1fbe981f0711338d9e1dff59c672070e6380a1d01106b369b17a870e502f143af0ee7222fd021818fb61bd4dc83d0faebe59b981a23e57d59464882b62c2a41593f48c2fb33d2b356852175d8dc01f17097bac34bf41662a1152c553a80fa6fc4c303a27e8d25c612387f29ade21f76f499638ad7f5bb31f1576a9c9ed0380fb281968032e87aaa99571b6bad614dbf1d7152bf362b2b4cf7287d7ddb89fc83d5c2c71a7deb36fa3908a768664c8498e8348e0a566425f5fbe1d04027795ad5bdc9ea32f7d9d651844cb94e09c31897f34ca88825544a0a8a822cf4a12d4c812991eb99faeda06112633c53df9968cd74931823b0761c2bcb0689d35e870ee3e092065dda3a7f773b9a6751aab42adc14137dde2e5c9cb0c8b5c7024782869bc348b0f9548cdceee1ca8e8e7f3912703fa0f387f5c4aae985f9fba23471b094d4592010722b2701af8f67a07634e6481d59a818df7805b12262dfe1e3275005bf9308593974c3eb88b579759bfc7ae5e9c3d964f30d6f92174258e66b1f861095544131c92b3d2f62976001bd721f71091002c0ba853e9f2cf4eb11c3316389d37c3dbc5589516e5f6f16743d205539674241c9169d725df1a326a111aeb350b68c6bafa46f1766d621ab9f2069ff40419456682a5f31d9250418c6a1c434e291ac3279c03ea8c45768e7663bbf6053741f07df2d029e09d91469d98ab9e0fe0073607b6ff8145d1457f2ce99b4ddffa3b26e9962bf070deb16728ceb8e7a6e698fbb6200fbe9576e2116afbcdcfa3ae023ceccad1f764f37049503a188ff175d611629c212f4c311203c0e2438336e660e65d05c6d1a106c2cf3cc17d56441b74fbae0bc8c699578544805813649b103c100a95f8ffe07b568e100827d1c182c2754ea10342c5a0ad6b0502b0f4d0d70fdf484bdf3979d458a03853b440a7fa0f81c444f9287c69c3bc3a30f46672643c5252f48678302b02eef5a0b15ad183e1e6330f37a2c27a7677da456389be488c1d6d4cc94ebd18e1eb36a988d121db0505009a3eda427db21f98de1925ca977101c7c8f9d493accc7968901958ae0faaeec982f686c694caeeaee2ec8adbb373a6a1df043fadc600e25c62c1722b3421f9436e60d6f250b6e1d3ff450e861c4452609ee440c4bf02125be23e39d5e7402fa3bf0400f932e8dd96b3f77f54e4c78f8240f01fff25da04acdab81d49fe0a14a004e138583a15e9f03774395343d3d9715fd99ff60a89791aa238a4d58bd719cf59e8ad325cea1a48dba869653585cd4dcb55d44ac11954e983d0c30d1e7650df4c3c5928f0629cecd18917b5951ee9a7a432ba10a320afb4eecb577451d2deae4cbd83b3dc74da8b10a4da34e3215550c68eb6edea965493b48404a71b512c90d7c29e55ac9103f8d662ac4a8a25acbd416f6c449d7c5961f0aeded282977029a06b781fb35c1ce3e7399041d81b312053a2988ace98357a2ed4429e0eb59aa8efdc037401e07ad0f30cb4e6df7e6b59c43c70cb365286f8fe7c04724a331b78eeda118aa213c23987a01521462024c3ea0065241d69d144ac1386e6e094d141755a9a37e6f2e6bde1cb84a844a80662b7c6225b34bcdead32cd7b4dae63c6633e6fc1dac881449f080044eca275403b155bb2b5a939dbe814180001245bef27834d5cb39eb1ce9ea9f1ec5defb4b4c157846d62a4712ac40ab7e1421f8ededdc3c96e389744ca9e419cce6f652c8d38f2a8c0e39726ee0b0f8d0e2d7793de8a80f79769ec46a9b57acbd83b6289923a67efb34b3b6eafc542a6189a003fe83093e805f40915009388e1879b44a3cd4e0e11e918223cba31f102879dd427b59a5fe1ee1db306171d077ceb7e07be9c9a993a0aec9de07ea6cdb22ad24bcca1f8ecdc1ebc5599fc9d6b5920bdf2301453c81edebc541faa7a1bc254ed771196274e6cde5565fad333ef3467098cc02eb11087fa0ffb54b1d3088ca4984a945c09c266b73349dba39e7030d3f7e08d5966aac10f9dd900ce81a20e7793a65f721711ec84ce331f5194d6521cdf52ca9bde54a6d05dfdeba87cd8f2ca9a6cc2a344c9c19c926785a233a123d539362831d9c0737007c8bb7ac328693717125cb024b764c97a6457fb0fbb5e4544cf1eac0c3e57c32800f43d8e460eab407c8bacf1f40fc09d9023c32dc1cdc889ea0a86900bd118eb859b7de912a2ea813bd1ef851b914c1b3a977b1ea3a7936577a6ac076fad336937322e5e977b97b612ec4d6bd2fce0ecb1dd81a0abe0ea2c101f13f4d49801102342f591333e166bd4df8a05441aed5b4d5e7122c53116d5c88d6ad4426afb93a35bbe7df4b188832a480a81e21fec59b263a3d81ca1eeb922d65a1d7ca89ee7d3755a4e0d29bb2e3d970ee8ee80ef3902cb0a5678417e4ca82e2821847e9174021589697f70ca38a0151cd015a526e4b2807b0e31b70c6423255fc4bb3a923e8ecd158b2d53c7ef36c631a06b6b40875c02e9c1c4929e1ab16e268aa360dafc6fa29f8e2f03b7afd94a23d810bc3ec23a540a94243aa115eb1f3fb67431da9025184bbd9bb1aa1afbff9552679171f5a0ffcd012f8454888c3c812b06898c358519ea0a14616baf9764852659999b0e7e83bb9ac94ecd7306f332c0690f40b1d73f5f6a00acda766903fd115e311134d3dd28b0cf303c16fd0467aa1b1a0db8e3b70277f8954507362874685b8564d80e01ed28880ee3636e61f709169b0812915517ff052dff463c18c26d0ebcca5f314b722b8c0490cefdd46ac93e62637781bef8e5cf8839d69e222d3b0661000046ac12bc8d0302d24cfb8b55cb45e8e9807a89ced9c5b95b1d39bd454af076dd2e57c6acf24842ece4ef90aa0564792b31a7f45f1d411bbed3757479aa4362025f0f4137f1b108b84ce17890127bb60f22e1b0ba4f6edf422cbf6a46877aea3fd4c82c929bc015feb78a2f171e815e8c0d9dc2f72006d0956ce3ace9270d29da23212e87c576baf8905c88b8c86789408b3117d25a7219bd96ac588a5ebb9d30835a272b514a1a888d2858259f38cef745c6495d3248b21502f0c42f4b52dafde246d3e01ae19074366b4d6e6ceaf1376933bdadca019c1baa9f14a63524581dc78b1f6d92578b5ff58c533b2b62a8acb52446b5cb3f48a229d2e7a234891a55ca05f045c9b12ca2a1923152b60a895941474e9a9778da3a308c7825d9cd486cf1e83af48067e2c7d18a03aa1be717008c93fcdfac962e7725905a360a50053213990d8caa11202767bb8962b162204242a22e254cb2d8b9aaa0bad36b3825edf0ba79e6a464e76a1d33dc8893e97cb5d7f9c03a8f4931055ab051ba11fc179a2af418871b52a822b6f82a67fc50fbad1aaca73f6bfdc0db79fb759e892f7257cf16255c660a087e57f0c71c2d8f02dd26f5c6d0d5f82b4ad82123a02cd179c5d6b98c0e459403c05b1e68bf88e3415d09fdb2f9dd5d5af8bbc542b0ad90cc625b4705ac91315ae07f7bd6e110fd4259cdb615a1a90656107468169698530a384669754c78ce5bf57f9fd7a870668c23d6190e6acc09cd5d23093f1ccf8d397ea67fcaeb607dab2f384bf13299266aebc4f8d6f15b445b393d804032174260a57bb5b06d9847fc299fe56b716e4765028c2314345763c476400be367031edcb3aa1b9445fb4cadb2ff2acb3e13c680fd11323af102b4bc60516c476b6915c9d5b092b8e3d4d6a98a83aca0561f83f6e0c60d88f4ce473b66968e485924bd6a1ac8546bf75306b20a1817ef10cd0423437db411f1c5ad5050c68a4b66ec358ef02a240d9962ab3423a2352cb7bb3c52255ef1a4e155d37ec4e597884c285ae4667f73b1914f1e8f3f9eddc67b9c98ae9f31afb0ae5007ece6533c00ad536b05e80c3142d64b376814aebb79d85242902328cf0d58e48b85e7f930937834cc64e1454d14861fec4d30876cfbb089805a72f4fc1c8db29ff6b47e2b3c415cd8e86423d3cab991c6b8d00298d3afa27a5f73bc2245412a479b8cc5c1ff10d8e6861da7db4c3c70cb62f16781c089a16febc2e71dc530e1b27358dd23c6a959ed8dbb7562825b96d07a49c21c9e0718ded5584dc5b369ad7e5119b31384a065cb610540c88f1d5d55bd0e465c35c121b66cb29f8fbf0d9276abaa1e0b6489cb0fd49668c990f7ebb15717baba6367cd944573987e78fd2eabc2f45003008e0447489b046c1f68dfa04c53713f8560153bb89bdfe48f900faa6a8648e8f80b390f2b29a5fe3568cf4df7d748e42ff52856c1651d115377f2dfe47011eac51da9c86b99cdd29c00cd432c14095a3fdd1362776b4d276602bb3829dcb4f645b72a4a9cd194c68da205342db3c09f01d8d3f73f0fded3e84bf9862ec1091a0a672f811a22affb0fde647a74b841057fcdca65eb79349c883f1972c902f236e21771492d2d8a0c26b0cb4af1e3688d207f328c20108176432243c0a5ae3ca4ac3e4f59f79cf242e4262c9e0c17a727777eaa230c2139f4e31f819c84adc7954d449d858d9dd1dab119b4eb499e5c483165267d70b6a946154e1d311a04aa2ec75f2d410a29fdb65d99d46e1808b9f5a04d0cd33a773a105fa29b3c0d2290c945d5e37f5e49aa1781e57bdb3d99f2196172c3c8d6a7af17b057dec786ce8635d82d418a9b1b7f4775207f9024299d7f7fec6ae98684635b7551abf24b18724b8aac065f16cb2ec58556deeda5984d09152628f76c6a26422e168582ea7b402d5e31408af4e59de2666839a32c3d40e514823e244c084ee13bdbeb4c5ed8fd96365bfe805da29e0633cb1ab464d556d8b50bf7be02af06af60df72f408240dcde94f82329e502ef27d263613767344fad7a75d8916b28ad46d1cb49bb49ff1c4619e631111ace6851a00b9e867be13ca61ac0e068785a89f74e547d8fcfa45773294ce712bba64d8094303411010cc94290e4ae993f5998c919aa21130efaf373e8231db827021e40ce33df80cc21d999e82526efe3fe257dfaafb4564ad07abd3dd338d055728ee24d6a7cece41241983bba9c51518965f2704376fc466316aaa5d291b3cefb91dbbbb996d670802995604607438ff4820ca2a246a0a483939f9364ebea9bc80ec6a5e662dc909086a8512787e5048b296e7cdb63b037b5690b7d74db44dd383edc370704aa02ba1eb2685e505815271d4985942aabd2fd650cd421c9df55d618cbffc1b5e1d35c1266c4f6e8cf611b7c4658be5876f5fe1e8487007ec401d73f7891f387bb56956530596ab0b3ecd4e940b490cddc519345d45beb2af8ab258410228dec4df6de72ef56064806ac069367fa34aa6134fb5064bb15694a44a0cdd21968969802354bb3340b0894851765e3b17dda1704746fcb911de4c3b337b2cf87f6c916c92259fa44ea03a174a627db5bda8ad71b1d8cd19aa815d097c9d0e4ec9142b2a5421ab4317e54c6e4994ecc27cc9ee9435da8103aa4411c13357f5ab265c9face39afcd89633f63ce3c7fa32a9ee2a9669f261b8f79e7f5fb6bce6ccb3eb7ecbb27cd8684f02e9a9892d93e5e1fe4f9855a7d8a9766fbe8b72dc79b3cd1645b666827d0af1664fb30468cf135a22ed6d501fb821f31428147440e2f11c838e5c41a604044c9d8434cddafb058de6c98fa0b4273ff47f7fe3c4db4f25d974b0b4b4897ec8fcc3092edc3159048b6a794524aa9a4b4fe4a4a4b37defdcdaf6c5f8d342a76a5e9d30f99b5cf645b65b2ad49b2ad47b265d59a6cbffae2e58cea4eb671c6d69f2dbb60b6675f1099b52fbccf9ec1d040f84a7221b98e5c17be5ca2c91ec31708a2c97ec35708a48a107c0d0972f3fc621ac7445d44ba64dfba349e0d8e4eca5544b6c79b8dce1988299beda94f9f66626a8e7a5aac2555bef08794ed3fba8cbbe106190dca1b1a94b141ca93eda90ecda8b562df342665d4c23bc94d4f90edc32df76b44d113c49205bd5317a681dcb17331633a48106e1ccac8b7971925b1207bda6a75b4a5615dbf6c3ab4635f109939d607fac2eaa3fb1a46c3f183cb111bf95344a6755a606aa60f257271bff0d87ebf203267bf361bd9b52b67b846ffe28b75b5d87f34f25e3f2f986cdf2f9b8efab51109e36a89267b8a2f96064097bff7915dabd1306de405f442baf62a38b97dc3740052dc9ec3588a0f6cfb38298bbad0b4a0b1b0d9e8cfc7f6f54df6b50da826a4b949f7da0784ce1ed29d3cfd32b52bf250466ded4a973ac66b8d78ecb3939681f068a22e96b5d65a0bb2b760c6ae0ef9f90591f97e5e83f63d6f2f1eae8194a00a3b78e209254f709fdc27f779797924cff77fe0653c985c917db871f36d970669c442b97a3f5ed8f31e742a756689de66df3ecaf16ee6ea62df6af5c30fb45b3a269cab986ec5549db595c8f4dbb6639eab55abb3d21b511dd3f2bc917cfb907f8e39942d2fb2fc5a4816c36964ce603af0c2d9833d9c39d32726d7f6992e3a584e9fb41dce8998eabc77dd351c7add376d304cb369905e9e4677e3dccc154b976c56397889a79f2c8d0a9d3e8b7886f034d2a58ee9dcdb8d6973d327ecc370fad4539b2f3124595863efd03ba786be988e99ab9fe93373664facb1b5fe696627e7a64693c951ca541a693025d3c903cfc7a43ee49be58198d2ce9dfbb4cfdb78f4651ffbec6950f6fce68f8f17b64fa6f4f4b3a74f24f99092a4ec997a4a442cd1a3e01e91e95f5e90e99b06995ea67732a5f46addd22797889d666c3e49a6f41a9630546ee02f568c421ecbb64ec73448bfe196d199a8a6b3e2ded1f1a691eca0d34f23624a3b0d270cfed16529f395f21a8685f73704438924d357d64e9f405f8c8e99a8f9d2257a3a5fbcd17cf9ed984c7b76385fee7cb10dd27bf35dede84f1d74f041ca315f3f5faf83d6226bcbd54d908964d3a8010954e02931f2f5ec1acd7e0e38a27a441b72284329d35a317dc437ea69bdaca7312b8d09815fef71454c7108fa9a524a29a535405146a6322e2ae715af0ca28f4aa86073dd8e68057ed58941bf8e02023d5f18e81908fbaeab3c7a49ad40b3040f2dc96711df88190bb37ca373b4e18019d9078e1939da833e7fc5527c668222f418bdd87de278318da6781af38db59b601438f446cfd93b1cba90b30f888c98fb50fec38c3c3fb0c70e7235238da6fb8959b4bc50b258240e2c48fc985658e58eafa18c899b0d0cfac2941c57f3f3f2bc67815506e2fe9012d3438ecf6e0c1ae5a4341e05013b4c94bc97156d48a07910f788516d72ad9737f532a6ce8052fa14cac8135b9cc8d8226a0790168fd2600a8b17bf94ace48487a4ca473c2ff18df8193f3f3fda60f76ba4124992f8cd17583cd0491da324f5d7a3c12ab3dc21bc291e8e547cb14b275fecd20d3234190dde4943ef9a325e7d7da105f2fcb42bf2c2104c41d97680b9c6bdf7de5b818bb51b8a8f4aa6709377c0a206edd79321d0b36b144228f128a594524a8fd0aab4d6d75a6bad94d2955a6bad95d6fa5a6b3dadb5d6fa5a6badb52261b68fcf0e3e3c999e877eee6cca29cce410cc4aa62093e9293d18a3374951b040490962947a4a78f3f431fa145e7a90be07ce3cc292254b962c59b264c992254b962c599203b3a7c7c3b9c3a300d76aa20ef0d12206f87a7c231e5f01be2a7aa81d7b86615a841661054f050538982910ed3f200084e6901699a8027cf2a54bf5d743f185dea8fb68117ad06f34d12fb41c068266ed936692b0eddaf6499afc436659134df517ff903c1963515282571f73488bd0227d02f5e9a54f1174d1c6c344ca8cdd5b7b149b8e7b7b77dc5b12103a894420883c87d887b61af61a0e83c876cae47ab10f33a35a79103302e58a16af6c7b9a6dbfe6681982233c410a4dbbb6b6f190dd12a75b87a729b517882a31b56ac2ea5ecf1b8d6ecb58e45ebbc4bb802c82c6284990e92f24d94a4ff6132fb9258b5ccd90a8c109d62fd6d0a841f95e11c16271606e9c9675ddb6cdacd3581ca76d1b97711cc7655cc771dd31eee3382ec3b287ddb36d7bc6bdc3346ed3b09a2fa63c1487cc1c7765e5b699751a4be3b2adebb68ccbb88cd3ae245dddebe564e56aada00ae2b2db6cc3f419eedc982a11537127a4349b8ee7cadd7becbea39cc6691df77befe5de94cbbe65df34f9c9cd6b97bb5dd669bff7765e77b1aebbc7befb8ea3db9cb2cbb44a2b474120109761da71f7d3eec6719f5a97659d9669d9b5675a0a8e1f5c8ed8c89f2232add30253337d28914e9bdcc67dd933ab6db85ee2fa0cd39f5c94755dd7a1300979d889c8044517f2b0aefb82709327a6b4e9c4ec993e54081d42e906388de3b8cf47a6619bb36e6edc5de1b6544cad701ba7715adc384dd3348d8ba9ecdb404c7130946e80e3b2adb2b24de3362ee3b699751a6beb384debb62ccbb22ddbb62dcb3a2cdbb0ec5b876199a671dc330d5f99db64cd64dd00f78ccbb4142a6edf6d2a52685cc7e16f985ec33567180899753cd7770d440e322a6d1368c7eff38b1d8e17efe97798a8fb4996da026537bb17e479d8c7723f4aaf5716fbb1449a258db2b9b0eb07439d30270c9dd365ce39e79c73ce39e79c73ce39e7acf3ded08046c9213d56a25c922e595cca8ffa345291e2272293a3085d7697521e870e3d10b043a33e4d6e3ce4c4e1e22deee2cba141392790169ff2c5b3daa7f649dfb556d904cffe46fae21ac64eb18b577c10e81cdede61ee1eee4237c1282ec2263fc1a2a748f11156f11578d402ffb0c00f79857bbd110a7e711246398b6758e60fb320e1824587e3e9b4c5de2b6b1be60f3b896c67ba649ffd7a331998b32f6c1630b7c0316b2b7e40203bb6f1d03ea7f6155a8b162f70d7d8dcb8c0b388972ed9cb06c4524c51dbd6b6c098d6e26614dc35b86d70df344ee7a8c0ad9302f7ce096e11eefe992b3c591ab4eff064cd96065d84cc21441afcf02ca24bf61a9e466ef40ce83547c6742b46b696ded21cc668a2946e3dd3204d07bc50e6ec3021d7778ed4913bb5566f925a44842ca684d78c468d50a3d68c2835100ac72c592ca6b0c783a46aeb470e3ba5d7d5574bece953df3ede203463ffd1652cbb33b22cfbcd7046f7de3b59b79bb8ddc4ed266e37715f23aa9bb8daefcd361e5c9675e7de65daed1cb0e59365f4d33dbd93ed35cdc3b4af6bb2b9f1c8beb601bd6f1ab4ade1c8ae2bcb70cc3646323093f429669b3b39f3a626db996c1feda7cbbd3df88f469e375db27d4cc1f183cb91eed6e9ee6e71d8475bda77d09781b49a06b383be20326fb75b8ed62eedf2c294c8f469b067f234681b8784f0c28b75b95c20c8f6574b6f3a409f8d2f6d459accf3b26cf698106990ce79ee1808d4df3eb377f7fbd8fccd7ebffd6ebfd9bb5ed2ed9976ec8921f9851c95506127afc08b3a2001916c068d62ee17a6cf7a1a94f26edbbca1c1e6b11f59ac5655ccd9c41b8ffe6e9813eb30ee8dbd3f179b8e79ee0b2273f785ddb7b7d878cc63d9e9b4e52865ea0dc1c33dd3340d5a1cf62f7d4434d9376e23a28a7dc5cde1de70b340aad863b891682394bed874c84fb24020d01017d68a67f2803008049a3c9307e402949dc3f5a0962ed91fb9583356ec4b372cf79bdb67109ae77f74395eeb5f38dd331a255d70ee0ee56eeedf7834d87d5d1db06f898086081b1ca993e272c446fe1491699d16989ae9438970b8c55623932cf393ac1074d912235dc07f34f2bcd92f3159bab8481797f930fbbc2669d3d1cfbeb0fb4299adc6e1fa79b1e45b749383308683dc4cbfe1205ea6cffa067bd84c64d2c603f4cea4d6357dc23aa662b667b1e900bd3f7d84e62fd644f58bc76d17e1098568b237c1d389a8628f024b8c47de17dedb7d5b1f2459546c3afad8e4e9977e89a69ed605a3e982962ee5508b275f2e24b344236746325e122447b02b5f2aabc14ad20e84b6d423b9dedb6d1a76d55bafd5a66157adb7d65a2b928aa422a9489080e09c586dc1adac58aab5031175c1802fc8b51e31428147048c08648c79a92d7d225dd7f50b5bdd3b64c807ca9714359451c8f5b2d5b54a9e0b471ec9552639c288a8ba7ea4ca4b4c955e6fbb37dbda87d993abb88a5f3813d5adf9235595c58b2cd7cf05b382b1a391e7ddeb7919a599da0eac03d6810980c3bbcaf539300e7c03dbc035700e780018079c310d2c001c000c007c0356e1177068634e70686710da1884d605372e1009030e690a727d0c1c522372bd098714895c1f43bd0a0e290c72fd0a0ee90c72bd0c38a435c8f533e090de20d7d38043aa24d7cbc0215522d7d78043ba44ae3fe190fa20d7a370488190eb67e0900a21d7db8043ca44ae4fe1903691eb411c5224e47a1770489590eb5bc02ce0903a91eb61e09046814e21d72ae40ae334c3c00fa97c69cd9b2b8b545db40a51659324b9c24f4c6553882a1b13f344aea7f5340a516565a44abdcc925c4f7b620af47aea44a550c815634254d9991927ecc4d4a584a8b234344bd089290d0951656b6a929013535d1351656d6c8e90abc74454d99b9b22e41a124254591c1c22e46a0284a8b2393941c8f5b426a644afa73e882aab2355ea757e90eb6889a8b23b4a4495e5511255b6e70651657d58bc925e515e4f6362cac5777ac0b3835c73907d7090eb690da2cafecc20aaee0a065175599288aacb922a4962eac5eb5f4f8fc4548bd75398985af11f1bac68c02283cc7a414ce1d75314201155b745aa5423318542255952a56eafa729882abb922a959e20aa2a4baad4b720d323725da920d7672df00b5f815b1c055ef16d3bc6f7192efdc3190c30c4886132c510838acaca8a0c32cc30030d34c89051430da7130a3563860d36a440175a608185e317fe02ff2ef08ba360172c2ec2a39b60d143d8e4de350d84b1634cff61d0e58b7db5b7b7bf5630c070ad92c4889124c6648a9189210699968a4a6b66656586460619686a6698a1c686061a6c6e64c8b8c1a9a1069c9cd329470785d2d999316327d75b1e1b6c78d8f5b627e503bedefeb8b06ae1f5978585a70e5a1f177e720b2b165872fd65a5c0f8f0eb2b2b8505e3df5978857158b3fd87432cdbc3c0e196ed53300b383cc9f62d601770d822db8338854316b2bd0d78060e4dd91e854319b2fd09873564fb1a706843b697814300647b1aec67c0615492ed65c0615422dbafe0302e91ed5570187d90ed63c0261c462164fb18388c4c647b1870189bc8d6de68b247b1027bd164df028ba2c9fe053c8a26fb639468b27f811f4df62e704a34d9a360309aec49384634d9b3c02bd1643fc23266dc104df6214c239aec3d9c030e1da2c9fec23d4078071ea2c9be847b88261bb11388058e5660a3d6cf4c56c951c9133639bbb4cc522ba913c0e3d18a908d95b2501b80c7a31573482cd5ec71121151bd8358aa870183443231e92cafb424cd03a1178b8ecb48ae2f31c10be79022a7d0ae724c699d910ea1f848a6759274955f782beb7ade68f4574148067354f2b393b3d831b2a675f13dce9416c0150f5db8e8e1c824a43bd843ca845c4f63264803e3e18cfae2e1e807bff0baf085b7852ff4bc940fc6178ebe6f0e9943ee2d0b64d5ff454b9bb6ab2f045ba0cc7021877635b2ab192461249aea33cc6a2191eb2b0bcccf7b4639b9bec6094b5cc9a8065e7deb25e4018f3ec9cd4f929c92a3929f2199523a736f9f68e5149a8a2dc7cc59f77bad2e16fa7263ca88986a41ec23e22993aadb52a40e691bb40c726de9178d521aca971dd9e99c94ce8d448f49d8ef69013ed2c3793373e6cee80be78fe8f7d91591eebdc93d98b18b2e8672492c568c2686596a2fe9de25cbbefeb648957469c99b76c3bb8e61d8439748c35d961ae634bc6958d370a6619086310d5f0d5b0d5f1aae1aa61a9e59c3ad6199bf180daedcf8d0dd2a422d5ecc31d7afd49a3813656ec8b546fb8cda0fb469d8cd72785dab9713ec25d64b239b0bf1279b8d6ca465b7bdd9a039997e61ca0f29d9cb17eeb224124da077f9c29ccf85b79a2d5f5883d1f285b3d992e50b837440f9c2980c962f7cbbc8cd17b63f974d3d52f385698e2610cd179ef98768029d46e39829d1043a912d4729d7642cb61ab4488345843a70f27a091353295e2f938829eef554080c44ba92aa392355eaabcf0872bdf4037366952bcaa623bba5b7945a7a91680afc4392e8edb16714936a485a9d35157ba1b95613458b74a9fe5e4ffb4ebf10a42fa2651c16da76d06bde96835e2391e85753138462efb61ad847a29f0e50d218b9f2a5412338a4f5b3a6510778953f06781831a2622c8571499657c8f2a1178e1e104bf2f6dba1c159b9bad58040839a74094db29cf30a3994794eb9edc06e2746e3ce7943e4455303fa44ca71730d65ae3cf4c0a274054f5e21b74b4a839a7c4602dd850492421a6c22a6640d264e867b34985dc30fa3aa8b902af2d7a5bc4c1253a0dfeb79a3112679e6bd9e1767172155d245fbc296dc7b640cfb82c0617bc4c0f60a0481c3768a4317a95a2263973dc89d41589099553081ed31cf1edbb71dc318f689430aacb2fd9c19080287384bb206596a7d8a1567f2b0caf18d796890e2cc20b4860185214030c410430c018221401082107020ca96f641b22295cdcd1a1d9bc6e62625e5a014e545afd278dd46c8b2e8f12b3466a986bc1ea44ad54e54dda30ce18689a6233536366753bb3866a11ada3bfaeb793d904b4495199a50e8d9c530afc60b65cb4617a9e06834e79c55b6224e4bcda7c8147c62ca0b4f2eba167aecd349c827e413f209ddc73b8d0edd0b79a150e8f1a3a2df066b0f96d8f14e60a6dc018fd045cfa49006ef0c2430b5eb42ef42ef42f69de854742af286e0a14091354cc3c0843e5b47f4b09590eb3ba771fa24ca1aa7036d3a445de84e1f6fd594d5d4a5e96663e6133c7366a2d13e853a6deef40905cff5460f0981e3855da48bc406bb562b66b644d8ebbaf6f2ccdb76c3c76db06edb8dd027fa4a4c78a2779f48345bad295b53b65088be4e0ad1709cd192ad99eb670ccc6cd5702a916342ef62626ad311fa687761ba1ba6fbc2a82342b1d238383831316573fde4e993e8f553e6cef4ec70c6c464214dc3354287b9dd33149a8665bfa093682431736a58e6cce67b63661fa22974cde6420be4d04751295e43f79e02cb58ea34ef2bbaee219665a87b085ff1a08d877df78669d164179b5c2f0289b61c5e76a2754edf64275a8783dc1cba3751b386a6cb6c31c193a5d3e04e97a4aa9b88e9e9234423e47af275a8c58b3d6362ea8aa9a9d3a86e1e9fbc62358ccb8c4971926f566ac0995ca74c34d5cf980fc4842e7aec93e80baf8f096e982348640bcb992bcb1d4453bd5422aad4d398a84ed25ee89ee763b61aac325f8909afca3c63fa44ea0e84e6b04f28ea6cf5a9611aac2928394260606a6612371ea53c43427892a66a76e3213a6dc9993ed16bd3213afdc9c917fa383910321fa17b7d371155ddabc9eb27ab751a27a642afde611a6684937f5627b866efe4f5343298987b4f7ef2939f3c0433112e31e1d5ebd8e7e7430404084fc332cb9f3edd8f74afbd92ee773265be196382634e18737c88a67a8b97a084869931132585b072c3f814e11919c59ccc96f1a472dc69e6b6af01d1542fb3948929d06703fb80903994e600832ca6cc99c4beeddb1e778b76bb747777777777777777777777777777bfc5b424162b462a529c884c5084bc8edbb40c845df86a1b0fd98d72172813039947b91e2b9441ed9c9f735e73b68c9d233abad7c52fee5fb885a6d9a3dceb55127631c22b4e599068710d1c073c5fa69196f1921cc9f5c5c319855c653c6f346a997fe16ca13c6c997f2198b31196f905cab78dc768743a3a1d8d5c9058ac9844a8052273385f22a657360abe9976ab6770d3744ddba8c07d9302374ee7b44ef3744ffbf4cf5c4d96c9c2b3054f970685608c27915944cbb878fdd927f96ac48b6ddcc34435a07b946bd75b6cdb21b79669b0355f24ab6560aa7d3893c82f7db25fac1b0ffb9e2f46a20e6bed376445f7755dd405a54731437060080e7472477f346a6de4695205fd4c7a3ff0425a841689a64a63962f51a53ec543b177c59b81ba565db2aba8425d6c29d621b9d63320aaac0ba44ab5ef610151656310835ced79504054d919ec908018848cddde00f63d62eabafd0ecdb65a35620eb27d0e31e5dd3e876e0f0001646b6f83e8f635a0b2bd0c1968c8f62b31c5224725db9ba2cae6e8c090ed4b516575ec59882abb638fa3caf2c48829d2edc19842b97d4a4cb978d1f322dbb7882adb63bf22aaac8f8a6c7f1255f627145577851253bffd28a65adc5e14532b6eefc5148adb2daa2e8b3d165597655fa3eab6cc6c5951a5de5e8ba9edf637a6620a3fe5bdf1b0d4d24f2a5f6ce4b8c7789222c5637ce185c7a8523dc61b6e788c0000c0630c40001ea30004f01869d0788c393f461c70788c0318c063cc21871a8fb1868dc768e3c663bcf11871e0c841001d3aa878d879c763ccf11809f018753c461d1e557cc74b3803e1ef180669f87a8735131c1a61d159e0d14998c55130e92e30ca5f60173f7ef116f829187f070e25ab85c572573f3e3d3c3b3a393837363534332d9998243388419855a8e40c7785450dd5cc0c00000028e31400003014080584c2d178204a439d8e0f14800c7fae54684c1b49b42cc7711042861063000000000000000000c80c6d00c7522080af9225bc55081bad444de53926bbaf896c9734692cbde52adf29c99825e52ff9a8f0c8d11ba235a77af1f2e495527249975119b6e76996c8c314c710069c6a7d41c9913a7521a2612298e9c81c184ec25b3b98e86999150846a43e1b7efd009d93c5734befcc2c61bffe6159ee9e70d49aba17ba64cb79c66441b241cdb77290418a3f840d731aab8fe6e879caa2008fe7944a1ab90dbb80fd89b5df53eb4f1d099ea5938f64e39f372016afef4d293b932297b9be825785bb83d48b66711448dcf4a1af1eafd7442679647db550f4266015e72515888d4fb8d9cdbeba9e9763af683f671ac2be641de938c62d930d59dfd98a45e910f9a3930bf0053d866b893434bca9ccb1bde903360bf3e82e82b824e53d61b2c5b58dfa7231379d67e84716b590e56e7354ca5f8a2fd037cca2c4f3b6a9f35f8adc18a8675322c738868cc04692c787bd453c9f16a87b9b026aadea6a6b23fc200669af31436c1d6c285006b561725271311a859be0ba9c073846144115a74d3b06a78d5a78ac25b072dff49e0eb999f2ff51763141a8b9bd1c8e117bc5ae9ee1aeb195a3ddad48af5970a767bdad827b49e8a19cdeb87b0bf206716f2f65fa63f9a6259f133b30795620c6c1ef729aa80e58528149064271aeb7945d4825ba7af3032095237c9c3951d8fd427cfd62e944b41a1f780ec8cc0fd1ee4752afeef5f2abfc95886e3fff082c3f4b06e485490dd9ef2127a16681e0a015e8c70e767dbb09e99694c305250cccf11ad1659fff79267806837802f8ba86dd03bf553ebcc9c28778f67c0bbd90466082688365fc8e936fa3f48b9c5de486fbf0f2011371cb8e2ee57a54765dae27adc031d9dac670e436baf4895c5d7e7bbc5245a482ddbb09c7685bbb4cd19ea2f78317d1a34f53df033e7cfbc7b68c1a9ecb74a9f189be279d30007a04254ea07234df599c8a0d569d18ca8a6d82a1f1f5aad8bbb38d082cb3fff115640f0b590ab67cf33f7204f62e470eed562470509d9e936cdbab663fe986e95553407bc433217bcb1fab837a1ee61a9288497712abf743c04a02d33fcf00925d3c00a138be9e081be50f30f68b8f3d04566f2b28ef2a46d910bfbbfa86e061b045cc962ed23273c070f7d46285e16a177f4ae3feb0890b1e0cdffff05dca16daba9f5aa936f0cb6f3537c143121b9c7520f98dcba215b34cf3a17772c0c20196bac29644385d294a934a83c94c766c56242faede4c30a270aa6e304fdf4882a94807b2c1cec983d7c5741ba63758b8e82db24063bf1b0b20e09cdde0547b87445109e28ed80cbceb1a2917686ac28ddb8615614170e4140ee90216cba457c026d6b129121d7cfbe789714931edbeea4266ac6235730c3932d3bf468492b71dda0b48563811a90b8920cc08517c760cc0fba91094729e22035a15cb308c40a42765f0faa9f03caf8f91ebc2e7794ab666e85b33b73c97b14e3a59b27e089e3b63634ac251a1c0e414bc57cb63308a8fd48210317b92d286a4a8a624ff89b6ffe5dbe11cfd5dc8d14793ff7b5abc46b76f246f4a952e079f50b7c1b6e913e9e31dd2be57da1d9c1046791d178865269ced199878f6738bad76cd99591ea76ddca7880fd669394a0ec4e9bb637c3028a3743b683dcb033cd288711004e120a6557bf77950d050f6a12613cb69d2c0d3bd064fbc754746f006a09c36aa1de203785470aa0307a2f5bdd06b2d408866b045cae72c96ee24884d6c4cc454919584ba6f38b7997e4d8e488fd9d2e8edb4f6900d39991c629a664a1061fc38c49ca3e308aa599d2328f383fc4ff316394a2f29ebc7379e4984c34fac91ef541fd57f5420b2ff9bd731535b861769e589ae277c28eb2c1bf9a7e9b27c847a29ee38992af10cdc59735a885c7a1b3d9539f22101ffde1a84bf15c20f78e00699e7057454cf0876e9edaf37a59b26456ea2daa718696d3987560bdc00073fdd4a1f32eeb9584123d1fc8bacb6b77a3c220f2ff48bf7be60eefddd8840733467322726302226863971ff351ff9f43937ec4df68c7a73455d8e3c25992d7011651899f32089716c0695796ab35adaa2c1f1e9e1719c8008775788e94772369fb8574e8d6b48e1397b746f3596e5a58b26d75a11056dbd309518ec225f6d50d9d99a403defa49accdb2bc405661c20558dec73b907221172c609807cbf97d0bdf0cc4a909dbc7012a7086b4604646851990678dc36f1d9b08c18d4ceadad9df46d698a4faf384517fe271ba76127b3737e67eba368582028a24024af4cf48c0a2925605329718cd76caa687a820e4ea7bfa614354b21c130826a091722c8a7fa7acc2622ec825be180a893eee4087a2d8d13763fb005c5970f9054794109c12390cfa6dbc3820dd41b21f96b83f927d52436709a1f4b268b0c9fa4e64d140dea0aae94a61ecee2a6fd0af11635dd99300a47a1b21bb5a11b98b2934a68a188f5232f44cc32682c36568b091fab814bb9a43cfee8fb37381ea6a57b21214ecaf3642169262714a4ddd2f0ce6ec6eb523de91cbfd29de64ca4744254ac6d35da28e4dcda0e5c00082c949bc4c6133df55043b29f9007445d373e38788730750f1a801d0b7812c70198ab5459b0e9180f424a15061b9f580ce2e278f8b9c4f04375ca781e2ed3ad2ed0671ac799b593c84934a877e41607aa5ab3be9bb8900ab6642fc19f8e25d29bec635461c5741c138894a59eeb51582c817ec5fa77b0d09ca0108e52b6b2023520ff49a0ccb38d51eeb40df99263622b226c5dfd17f514278e894610c5b4c6093e1382f66cff0faafc3eddec766e9906f9edfb1fccc174ed5536eb3ca6d9043f6746804a7bba80da9e09e2ffa567114967959ba48fa4aa2a58530d3e3ed0531ba114ec3a21f6166bf73acacf579bcbb58a5fb4cad9af87d52fbc857db4367d7a0d4b639277d74bb7e6a5794f02b944fd9dfebe74a0884295166fcf60c892f2cbeac10b3921aa3cce154a6c1a1bd3f108d055c8a6a5c399dedc0d8a3020f74e4009f45941e4a2192e5544aaf0cd25be0b5ecf424693c2057101ff3eafbae31520e4e64772c08ced432adeb47b31ec9b6e3dbe79c21f7478721a907a1a61173034b0065228b1ee7c076524c3daae122399236a947b1d17aa3237b972edf9441404ab0db41276118ff7637f0a0a476eb081563d64aac969c18a14332ff0ab6a55a50bb76cab9a01a81e204d5d1779581a3decd375f92b7b70b9f1bea38e4818e4bd948845603dfb00f553164bb533d2cb587828210bf1b01db26d6c3d6cbb8d0e7e34c7ac1eb68da5d8b50e9ac62e4de0f71a7dee0865d650aa90516792f7be4fbff091b426b765554f3b0bbd6f989ee6b8ae8f734ee2a5dbc0a0f5ab13a988b909f088472306989d4f7c1bc5949c5023ddd8301f46723197cb56c951faaf84d47e79ba2bd025091cdaf91f0dd348bb53b260c4ba743735fa2531b993f76fd808dac89ac4e04ac47745b8df45ace16700db84d3c9900aad093b03d87908fb53b7bf821f58783dd78f7336a128856fe96791e88d252b9a07b9416b61eef70e1b88d0250636ae96a699490a24c58f894e8944a42ceca47d5c39160082c0b37b4bc067070262b283a713d8f9c5ab69192ac6b50932ee589c6ab9d85b6402943067873a54c78f8f9cc2441bfecfa8e4800cd45c3271ade0d84856f5d20dc1a3f703bde9c69abc985f98fbadd3638426e1e9a08f1b8a33573a548174808dbafffe7cfdec2d3dfe2901819fe677f460f7a088fa093e9c9ac6cd001c909512c31e33d083f7a89bd0758cfe089fdf73cd51a20299da29978334706e6708f02f89908685c1565233e339e637810def394acafc86b82803b4fa1400c901479652aed8d1a8bbf9883a323a12a98eda8785298e99b2668a0d80494153ae1c8a720bae2b21332d523780196492159479328d32a2b2d5f6f8b960a1dc843a4f2bf18274c615b02efb1a0cc755e483e32a5329aa49ce30c71f0442f2d8bedb84147b22bc0998e3a7b261adf3d2b14d188e45120263d9533179fa1720438a3314082dc47de4616a1fa22d23658250341e9aa415dbde6a366dd68ae2a957d845eec237ea6e7f6817a08745ea59ff37f891cd70db8d7f6c9303fa1c76eadb0156a7b081b4da1fcfa9fb2f37ed4b863e06573cf26bd50da6bb4dc65995de90a09c70f0d724cd99720268a2f9e821bf210ee33e72d60a0ac272fba79cee3606868404090a18d3cad297d61ad17ab39c6eec3683827c1c7482dc0c1a23a90ff10d4db7f7cfb07f327bf46d9b9f5a153c8bc72321ccba28fe8c03e22d82b54aa51286db43dedd644a04f17ff7e2b50fac586feadcb38277cf2b124ab71476768897fefb4c0fe8ef11d92c8b1a27a748b99676270ebe97eb9f347def992bdbf89156c525e659afa735707595848a06d2a7e85fbe4568e0262dea669c0f83cf1d1f1b01665adcad363f118efd37171e77d721da829e33eeba0872be53284f01b65fe81d4f1bdc10f6f5cffb7712aa755175dc106035db6cdec09a7765515ff0b7a5fb5897214c399292713b41115395f0929972038f98d8db1f74c8e60456fd42a397281f91485140188f8680509e21e498056e0eed92060a3d4c87742f02f7a12850d0fe5fdde2b1054a83621d06894560be84263ae0d71152848430271e8b38da1a37e01a53f115263cf47b2cd0c31dca3d236a8fa9fca98d4c3de930465489ad5eb4078dc808c1aca52026466a3e575c926f2a7733aab98beabb7f80e005179f7b16657e66c9206512bc9152019f877c4cf58a3892831ffe2a31160987396ae2ab491a873d9c57bb860be6a114873dcb2c9de76750d9e3ed1dd97a54ff9847d2715b05ff4b7bec4c24837d8ff430f508a029fd8e1fc9e87d5f43da26fd5b049274e903550c39216510520d1697adcd4591c80f756aaf23913d3b3405a71d85efe0ffc03f05cff73dceed89dcc052b3976f9416af684fb20296ae10d929dd595d39a9b3cf958d292f2b9d1d227b79a9814e31ca8a8f294a166ee708244c590aa55e93c2a0774452ee8ac56b82832bcafaebb75a952cd9b0fca6924bca94312d993ec6a7644c781662242faece1d77d31d613b4bef9655bb72e27775e8f152302f9774ebf63c8f98831a6708e0c887f606820f3d9c494e1bf927ad30d6f23509a72a4c0b0b8b9e555d83eb888f48498f8cb19fbf8f8ba1a88f68c6903e1f0d437ac40b8afcf97f24e2238c4884cf6f20111f413d227c6e8ec037db604a8da7ad99e966ccd284be68195c62fabd95c88d28a79d253da3220b2f57dae8b494702ee2e505d2e6dcb1f36c084c480f0ef6b265728ccc25eb8e3b1600a43f3d012438dabb040a1aa396c890686dab3dbdf41ecdf817b6fa9d2f60d4ec24bd1e3d43ed8ab5035b4a2bec29faf009d348ac7c886c35788cd4ee6fe66155e28d292d72755778ee4c82f7b2bdde0d2586bad93349923eb3eb41fc3ba0837d320d8ad02ffa04aad642e784a83a0b1123b07258b5e1d0bab4564a621b3fd6023911d96587d48a11406e30b46b880b870957c5a5090220e234cb3cbf49f810212e126440d33f32bef7231bc7569571bc5be42236931130bf4abfe2fd339ced4577d989e3326e613cc6d632e4304742243650ab7ef5fc291db2df066ba5d5ed645298761c22adce8f5b9e583c81cf3e3b1f4c2c40b410e881025ee29db826693f52aee9b172b8e42fd15c348cd595477d5398d4d138b1d5b850fe24f6fcc33b323815e7ebedb7832a4359907a40559b25de92c4bb1bf2fd99ed4c8c7f5bd46e2397b099b9f0e915d470093f9e59a898f84d604265c9a353e7948c557de07d0291ad0579d23af332ee4d2b761975805b239d6dd02d0441ca17187e2dcd9fc789d6fb87fb60c38a99abcf9b02e8fe3164b74edbf7e56a1f1cbe2f67ef79c52e89231ac428738a59fce9884ec1206d70708ec5c6a7a2161b347c455e66fe741a529d757ff604c322eab342043614e572f67b17440f9dfd5a9ae8dff1a2439e8cba3a5e960467eedc4adc6aff9971492f15376b72019d5dabb65a3c06d69793668e4259869bec024cb650c1961c2393ff8a25b2467d46e9d584a8d9477a3e380f5544cd113145dab4b1df366eb29beda3df06e2f466e1faf90dd64656b2aced311409fb5bb74fd3a803028a02a9e0056d179b9a01c33c12d848631a2e0dba97d731c3673ab836dd0e31aeda607fd27fbcdc63d2d61e8af2228d15738e79a56df646ed13e7c422c2c856746b0462d39d6426f2e3c79c7b71a4c6a0b034dbb2273f26e641e5e4b4c4a06d4c03bb4f47135ea70f55024fd746ba9db4b040c8e13dc14684f2ecfce98d085134034ed021b0bbc85baf5f1d93c6c0d07a038a686142b666ec7626de38733137c96d11c7ac67207d6cef5989c476f28c380829b67d2d5540da8d77f5b67ef9ec048a17ccd11f59b09f1529b0df87656dbb92a0926a22b337f38fcbf9a7c1f8dc47833e49dc36620d76a540aa97d3f73b4f7fbb5fc6c61b278c6b52dcd7684c60af4eaa9b8812956e338a443a8a8823112e0e547cdf578c44a163f1109a380e75f98553606906f50a78353210e354de44e80f30eaaf5066fb008fd192fcb207dd9825f6d911e00464387ecab628928321c331d2ffb869780f121d13f521da9f580f92fe693420ee9e560b79bf09dd907498d40d61ef74176941f4de6a2dbd2e19d52d9bb0bca8de333bbe2989fe97a7c0d4d7172620333b5898aa28e193ee71108a4b1d41063a91ac9c4ee2e63670f52affaeb653d03499264b9bdf6750dfa16dfeb8f2e71700ba0215e35f8822ef6cfd5d438f30c0fbe575433f380678a3af1d1d74d40890b34684c70d093f28a8f86d2326e0a2edae3be2cc3ca30ac6f63c21a9ed176424c1d4e586988c2879401987560c6281ab3b99b4c7d590987d380768017bc521eacedb10dacc859630ddb4fa19c7d366993a8269b3c8daaef163104a5b37ffe24a9ab81a3eb129b1c6963156b1dfaed80cd9efd4a75d145deba74f1ed3c4c0a72e67fd131187c40f859b289439e99532ebf62b9a5305005e15332d940b2bb5fa5e4e842eca12359653dc100d371b12386577d5d80c4ccdab32029db05c2e595938580cd07858d795d702da8a4b0224e158e7ac2a2c19b2715e56ca240b298c047fa56763529a20d92be0227dc631aabc96167a477acbb3a66a0e7e5119d26731e07d33130dfbed79df9de271157d2c2bbe7278b59c6a6c7087c7ee568ba638e06b0657b43af8d230515c207a6eb580d170faea7c1e28db04323bd5eef73683811ccdae0e41c818eec7a008b9c38abc83166e66b773c236e0aaf440323d21b0cf0360e841878c3d4a011a96b6a1a7ec85502969d29537e014e158cc580deb63c845a58a71ec250a548baf8bac90a6d567b79e1264dc0283b301603025c47fa389702de0f8ac476e4f4429e8e6561e431b191d1a140b419cb976b83c283dc1ca8de47f91b068d57d5a882d25d7ae229585d27f33df8a693db1645428fc3bc6b33288bc65e1a9b6438e85b4ca18e91f43472dceca670205da2d282ce990369a9664798bfc07667fa7256fcade6381dd287e1e367ce1eb9ae26b3fd24b78d6c8661a772180fff4741a768ca563ec4b59a4afcdbd26de44b7dde844522b6c3a04b940616506749aab8521e48a944f58ae6ed13a2d3150383ee8fbd1c6ff6a246c743a16917d6a19bc2a91a0081152f73a5d1ba8b00fd69f802f005a46a9c94fb5eb8b98d24e0f5d40bee91f84780c1785e5a41bfe0ed4fb9cdfdd783a66413b00426a72302904a78fc8bac5a03c323f3a5bc00b3d238ada068fcce35330b5ab082b8ba695a3d90fde72c62b3d7d7ad032485eb9078a86a8c0be521dd8081bf4f0b75eb501d9d7e37e6970cc5e11b8c1e5d74f4d86f74e66d9dd0c31024cd9a7ea7c6dfcdc5139b660c8564aefa8208d8076a3414a36172b0b164f9b1c994464100fe0ceda92f9b78087278127209f7e550ba649b16bb0d2e2172646786fa5dfb5fe73222f8c2a2eec4e39baaeeff2853c2b526ae32c6a7abe5f99c171e4989a67634ae18b34f8318d36a95325d42504d3bb126c5861c5c3e088d8a3c0e8987122c3f8c0faa9aeff5035740685bf752f105db520719119afc1857ab1ac4a0e6c89634457eeebd3ecc2fcf66558b039de769ace9b74e1484881e9510517d1454410374f9762109845cf2721eee2e9820f7776ed740b95685523da2b084d6d4453321bb2630ca4d4860c3df03d884c81f85e5c7aabcd4b575f9f2f6b50f9702427ac13f17cf4b4293f523cb2f08584331097ec0bee76f39cdda27b226bc9c34ae77484d7e8ca4bde91f38950e49e6070766b797e2ee49f89028c0c5be4699a35018d588d55b5cb35cdc343386123c9063f9c112018a8e8463469860159fd2b6d0fe1122f0ed41fe5612706dd5b39b84ac7c983deb90582ed8afc7a5a18f6dcc4ecf7747e5680c72c00c6bb9164acc7082132a6bd1918307de830662f8496398b96ceb845015279420b8823b96e034253bbbacb643eb9f805e6b9767ac334e65b6d62fe235573054ea789cdd7ac17c0929c3e3211f7b8960cd2586b0244bf4284227132b2d22a3614c19b1c88f8dbeb86c8fb1142cc9d5d41f4a63b20e0aeb71f6667990f75d3ab07b8eb8e87ddd96d87aa5634a743305570e972a48d0687faa7d4baa1bbc53009cb2604e3651c79d6b3f1239dc405bf59efd17ad12b8c6ad15223f76f07a5006f75f8f8290cf116dba10e75696b9211ae35a729c4d04dd225cf0351dcb0017d9ceb05c57506c09e02b10491dd08e8d41e9f667c1f9cd1caeecbbd8a16a6ac6dba5e0aec98a6e12241826c329afde2e6f66a80f4881229778650ba79ad395fd753ccd51e12bffec18da714c1b56b99c607c4964f10048db39a956e43615205a3e3306bdb9915fcad154caba25b663763bfa7236d21e6e48656da3f83e20ee20eefd006e881fb4e2bb19388507c1d78e3c55228605ac53d95cf1d67e060cdc30ed2da5f073f1d2fda2b58fac4202d3b7a4bd12e0ed2a65ffe8c3f422a7aba913996af08dcc0299ca8a924e08640e3b4ddb3c1be81ae68938cc84bc2614767f48a96fc1b2ea4487cab0a8f0f8ca7d885abb15c61bb9be8b7d043b33774ddfc69e934ba5fc44eeb884e1704c2566f1289f217f6e123df7d82089c09400e70033fa43ac4042300b97dd3eb9080223671ed5da39b87509e38498108be03a3721cf952b2b96e2f0bf0d7be01e00e36ec4a2524146cabeeef53f7a6f40c90af14c5c870c92fffaa394705bc9654a59b86afcc8a7165459cf22ee0b90a0432ada220f21f3af89eed2f86e8503b626a17f15ac0e72bdd3d6cab40fe72520f8da3414ecb3d88a80f3491b22841f496d8b85d9e54c1d10609cf36d658a3c54611f3ccca7fd4e2475c68fc5891bea7b1aae814ceedd42124088b9bee0da7d3123618cdd8abcf407a7d876061fc722d8f57ec8b6542d69fba8612d0d88ac567dd40d390ed507e66fac0834ca9c64cbe4c1c678c2788362b61f120ab70b2965946a0e25b37b50b9b130d138392d327f7e14c953e19993ec1069890aaaa2d366cdc42bf89a8a0f2282ae3e86722409da81f64da2d122e3896a4d0a241c81040362746bf445f45c3803516e99740f6ff0d2bff66045286552ce2fe751c51d3b1242130ac1cd208d333b0b71ce7d7c6ff79708e4d723a7dfde5010531315ec3b44dd82a63ac9f77de9fb133fdff112c91f8f8dd024e6b4c2593a331352e0cc93b40d9f489824c2c9b495efcc12e974d029cdb0f14e8ce5816d9211bfb5f2b7cb3259579c7b3df512bd0535f1d4b85f03db8f4186361bc124c2e3bc18828c98266eb4e021953f004c7d8c8f3e72ad8fa7510c849a61900d9bfb4578bb171497ba08d7cce85848ef5fdb295287463bada0526a659c0434343fad5548e30167c81c98a708ef0a71af85787469eb3178b78c4ba827a1341d18c95c2b1673be5b2bb79d8ec5262d033ddea16ac05a75c8d71c89957a82754b6193128170265d8547726481a7919b35ef9768a30f918a8d884238d3a07313cce46b33cf326ff0ceac23cb41a6ee961f7b241a8f00a7e0aac337c22bc4d13ca5061ef86aafa088ddac579bd38eb480663a7477dc83216595ffb643ff063a2bc984bf59e8d91923502ae39fb7df1f4c9857a2e0586d6915080f96a18d347d1fa15ef79cdec727ad7e3ac5790c259c9936aad76262be021e950a6da9f9444e88c859343d7b758b1a064f2763166faa12182e31a691fdfe1ad824b1ca05bba03e477295c1ff5f70aec6fc22a5491654dd48ee5f5f85174de4b3effad8db62bcc50a349ed5d3946c37f6fd96b1ce680242c5adbe757cf9a5bd729cdccc413704baa95372cefb5732a73d5b4c16bf20743846bc7a67b2bd30e1c7a234848d887b525e08ee687954e9a9ef514c5b11e91dfc428a157c0216b56937224f39500b00c4af489a64be2ee5f30e25018ddd1745ac50a42f2ce2ef6b548935f580bc8763ef7296043cb032bf69a791447545bc5292421675f15aebf7766308f01d1258e3dcbf0bac2889cecea2092b22f9a4a6405135c2df287450fa9b6d686f964e47cd9919c486e7cd2f63e0b1e5e59957ef140151069d4970ef6d553c9130998dc14e40264be79a49339211e675cdd4af3b4aeea98a4bb9bd8f3b82fd780948e0e629d46df3b275eb051eb1b90eff9568c71835a2b5282ae82f455bf769c6cdee6eb96fd578d860f34bb9b8529d932d157af2a3c358c68eadb2ea4ac2a9a376cd1d4eee4a6afd14dc4b6cf196ef4dc4de3d0edd24068c54ecf299c91279855b3316ce17eafddb44e71c20cfa597b6ccca19d555e5bf923348ed82e8ad2d685198ff90cdde8e5905a1f00aff5c938b0d9b3e2c1d2e2a327b241b4e43254ddd13886e30c0c123207145ed86942a8cbeb9db8f03a83643ae471f47d7bc33a1107942368e6606682798cb012515f312bfab1baf1fad84cfd9e2e336e6e1556b12eb803656413281c515301fd78818e2059949488637349a037bc86c0f4af2f1f4d0a4777fc0b7e0abbb6d724ace292992bd5366d8ceb458cfaf1431e0dc20238ff0e89d6796ec2a68d581205340516e117524c517b835ee83766e44ab81bb17bce48691faf52d3c6fd033d6997ed988b6e8b776b3bc002ceb33162444eb5041b149f0ba3f4f88078e2d6051bd780cbacfc2c33ed57d4c904bee128aacf5ca049009433142a5d62a7b2169b3510ace6d3d30b6fe658b9bbe4f666557aaf189a04170cd0e6ac510a6df54e7c2f1f6c2af5d8451fbaf7750dfcf035e3b3442364a82580234589e011940bfe190809710e8d144171f5d981f384064516d6756a07e1718a6dea8acbd3338ccfc887ba56d6fa419773c3783251eee9106af40cb72c107928a28159aaff4371cea784798c7a92c3872728957fc4a1373d4357f8993b3c859a213d0f2376b9e8e27eafd3c4276f28f6292eb02d41ba2175d93734a013885bb4337a1d744f19c0cea856b0e634db93bc807209ddb513314a375caac36ffc7fca43c7efcbd59e4fb5327a6283b72b04f12a3df57e7f2041462f7ac1b3d33a46ed945c3678b2f533d0d7d17c603112c44af37433682725ef593411039065e00a86ae21f32748944c7b182da5f7ef562fd88439183d0067c9f8bbdf8dfb88f5769b0f26ff821eb99466422f930199f0b9715ca9e20d77b168250ee9d5a2abbbf5ee55325ceb57e34ef10a1dc3bad0df80f15e85fa663e8cfbc505a6f0aba03643f9b05ed7f8dae576e9dee1bde58224df7365f17870f72c7002dd502890aef07d557b7d2e3c8bd1a38da116e26a4f6dfd47f68b4bed6ee8b67b5dafbdf0b20df63bf4a76a26041e3ab2631e71ea88b59af870eb15d11f6a616bad54191de4144682d861bb61ec09f3a0ffd86814e53705caf7342ccb5d10db02e5515f8ab9b746ff9b401c21cf065e7bf596ed46ac6abafc2ed20c07f1e8dd5ab2d0b578ccb6b7d29882f5945948b0afc8b22728ad2d915ef3edd546d97f3d9dcc5070f6558e74e3048888a1534650fbc80f98b45794ebea5696a75db2d9a9d741dd5e6b367186cdb6f46de59fbda6c212c5bdf66baa3beb1cfa772166d3dd9fc9bf63cbca3c97fc146bed3e6b725e547192a4479a6047e73259198f73c0bfb9eeb77d84a16953ea50d3d720e9d076cb8d249359fd00c4196fdf9f5304836c4f3058b3b7c01371154a36a1e52804d695e8061447aab5ade5e30912f49f87b998d19062dbcf41d11bd3f7f355498185c72f57492a614dd63838f48a5f2a15f80bcf5099f0e08bd0c0a2a4cf7bd27a114e54e02b0d8a782c9805519c8aea1549d5b40dbfb1674d0b030f6cd397d963d5b099ef8d88d4ee8f4de6eed1ee02a3d5443ee0b1ef32d350fb45a2a0216fccfefdafaf9b4489ef72ff796a8cbff027d3acd7c5a125d4d01378a41df3f6c57aa47f9ffdb1fca78b0d953fcd15eee81310889e9b5ebe13c2c46bd99fd8cc017ada266f28bbda05c068f6b832c5b0b4c6340e955bdfda9ea27e02211b59872331a93b518503e2d8414c1f510a7aed8e58f1e4bd64ece33e8003882e09fc7c9f5bfccda79bc718fe802bf7f57958202bd4fd27f8f992bcd7b9bfb42b22ffcde9e0a20d8e6dd5584874b0bb8a00741e566e2108d9198612f7bc2edac5ec4af6efb7cd045f6b5e4cff79b5bfc6134a6e6addfd0c20c7a14520b524e63cb868b5cdf7956cecd2790121329e3b6882ab448616b4fca36a3ca1f79ae8bb9f7a6260366793f8194633f5f436abd0975ae7a0c771533b3aabb32e4f77492848a23d3303eef596762b65a29e284ff4fab0200f06a34f133944c5510753df386c5c833ead6b8103394d58c3036a685808bad22c82680bac8e3d6c674738fc4e22915d1ea0441a7f4a0603e7a7275963132c8a08fa6f5a34147509ee94afdec10fe2ad508b483c110e345079621f46ea85f7b61703eda5c75094e3147d5afba1b4b64f05464ec5895fe38e473bb024702f6b941f464e8eb589a9c0e0a16f8158ce6a4dc85a016f4b00ee52cb5f15a31d1a49c3daf04be7181116edbea18bfbda2a406f2fc72a384a900e0ac5751795621bac2eab817ffa2e6599d58f8009f2955de63674005bf94776bdb528ef3814eb84ce094dc2820c9ae83b10e5e9099fc66be08557fb1796f3500e94cbf3c4e515067cda9d3dd713250f54a0160a401a56b0c5caa6831e8f897e045b4e106d45eeef6a6f4fea9f561de1d0b5fe4923173d6dcb6a676f07b439b5c97fb379b230d01619123b9044e6936205d39b803ec2b55f4017fbc3fb68b052e31a2b051416b256a20808443b5b3971cfe7ad8e15998e093a12d3b1a09483964ac9d2ea39f1ed0734993bf34e739bc8dd4e3959ff7576b6c3d8601327ff3d0da013eaf52135608577b81b6126d81416b19fe161d984f6c249e5c303193edba3540b52029279708bc05b4c490ca362a5350d625348a17e62125305445afcef194480cbaa831653eda7293d8ee827f585444dc209c40be6d64e049ac28d7a7f4815e8945ae84c25ec14daba002241251b173da3fd39426564eaa7a350eb713ab841da781d5ef9b6fbaab9888de3a847f2f79ab56d83c2585bfa33b2bcdf12766b46b84789d48a413fe218786e5c4515c0e460764404e3e141c8f961d4a6a1c2359195b30d8b99d3983671453cf5d14d44550a8841e04478ae753fb2a3f21d03357618f7a053a80259ed53ae9930726d741606db63715fa4633c6b0507c773e16af8d243a336c377aab1d9adb3abb65122760b13a7c8b63bb212f03ccdcf0d4b528851641ad4afd9da0be54617970842b9cf02b0c242876bc508a9a210682f1d6130499b75a5f8f8573874bc28d311f9baf85a2c14c6e6733121c8ee1d531a4902326fba1ad1d4e19e191897507d867523627235fe5a7bccaf82aa5c5eb8126ab30d731da2f1cb362a16acfb2bbb03aa9167b74a1454dd779cc83c7a1f71264b75a3579daf011d25c50732ac7c358c5d291fc3a52e6cf36825e01d8dfdb4ae02899a32c78c04111b8544b851713f43161bb7040f356d56aa22451d530a5c444818bd6c5f18cb902456b6ba183e74670337807cd753258d6cd74f71c04a9a031187932dfc0ce9ba2ad80299e396ce90e64a0669b6ffd1fd41e200228f16458dc0c328c6061d49d29641fe4a29226dcdfb2dc19547bfaccd0472b8695ab526104e34bafa846ed182f227bc6bde74d0f46a2cadd77f435cdd5e60b706b342e64153fefe7c70a35dc77f982e1dd34115ea24353041f3d389bcc1f34d795c4aebca3028a3e88c401732a1fb454f126807cd15ccca941e9c85ce33adb6610f802b6d6075343e325c2547b670d9b57a3a4fa873d6b53d8b587c86cc69efb5ea30132b03b7c08349e0d1f5913a7e4c37944690e9739a66adb74dcdac779301b6c9185e9a502167a4c66d33482f8f10901ca7580dffbee4421218c84d9674581a82b28e9578652a37602869dda87d931a0d907a49b88100c3975969270dccee915103f7421367acaf1852fd0e30c9b4472ca4797951819a0aed0cbc3b9d37ad0b26f73f0ef057498176d55c0c1dbe8074215db4886d84d8738d28edc52a7200ad7b91d3fa241f526541a610acd189f87453a4996602f41b921bfe0490bea02f9680412401053c52e0cf3ff96f96915aaee460651d710567a02933db718972a79323ecfa92cc6d8d361a8d806f81931ad8b16092675bf0e62f4113c6c7c23f4c03751c24ecf26114139383e8268168ab1e58cc4fe1e91040e9ee88b9851cd757e1a5895ef8c465e94ebfb934e7681448ea5fac5cf503e410b709deb98c77c6f2aa0c134cb41cdb261c21cdae430d90315c01cc64120caa4cc04146c9c4cc599858c59848fbd3400e28e46cfaaf3c92be7e24ee852c4437fe63332e00c39d60078e72e42b68c4e38aee20a431d4a4548661bdf5a856abb058efaf56dcc43ad094f3552cfde6e6eac98f981314f60beab63dd3413d213ed323eba884485e9ec00d8e00f29996e91a2ca2f93cca0cb31a3c20ce34a2741a455616b75c3d3da10d0865a77f663f0885fafb752abc6a1411ec39ad9c775cf2c5edfcc3235f8a01710ae28d68fd4beb4edf992395e5882180fb62559416f43c8f807ec8715ba74faa7f041607f828c41658fa4fb7501ac580f81f1c5c70e967162ac39959f2e9bd0697f56d626f4359dd425b969429467a0fc075a7d2c24754111625e922dd18f8686b3c290ddfbf7113b025deae432da7efd8f7420ef79ff80115dfb903bd594188afb4b38a5a60858bc7e9ae9873cc8751038f211b9640095147a37b1cacbf4e69eb4155147003e1ef79e37e752e7f80b737fcae0e4dd1ec178fd64aad8b4ea2026708fff27f00df1cc6c8afcc320c2cd503d0157e7359fa3732a857d2927309c6b2c7d1e4b1c1f4afc10d9f09b3765a57f354ef4fb21334f0f3462596312862a0189029ba6cca706010ab5e08f18c5c7016e00535a603bac458eb1df20b301aa496f7ad1c7d61839c818c58201633ac51d7c9df2496b4021802cf992443bd654025413e55538979ca0a9827cd3a6510f3205d202c69ce4bb1ec73738f094b2d9d47f0607ad13e6173ddc2ad8eeac58cd5d132758fb2c77159453ec64280b90ef0090895b6a9548313e00e73619230b31b3ce0c12c55a3a1cfb9441c8b215473d39aefa28c662fe82651d139d27d31974a9c4fb98036113dff67cbd4913cf773bf34a56ece4c1a0e1bf28bf89580a0fd6a7a768b067a661423be7b5e3531754c27cbb107543d8f27b17b7d100bd209c2861280f37276c348926fbbea054cf7c7c366661c4ebe3f5d241fcdb3994f2dee62d0a159c70a1a1dc008ebd41ba1083ec2de8e737734b2bfde6887f6705885bd3a4001acbfe12ab098ac432044aab13e1432cd4bf32bb64aa58c3c0309b01be7764d944538504e290b6d444a31785380ad48cbb1ff9bd0a48dcc51110ec07f67617c6c32230079e973fbdefa74867357fa5396f9b66ca6bdf801e56e29354dae12c8cc6677a490d38a3bf8a97fead9a204847b7cfff6d6d757ab8dfe3b1cfacf780b5efffc1bef060522f15f5e5994b6cdf40b2a6a83d7786aeb0656d0a654cc9804e8a1c5d435a003966ced5000194e01b40b0663f64c125c7d1ec8fc95e87c8c793b2e19b078b7d51aee3ee1fda579e88ad61302ce950404968ce9ec78f6c86dbe180c01b1a999d621077755c51df1c78a71e09323af7139aa6307c7698847d73be0d8215a80d2f25d46dbd5586b7176c45c4af613ee249229f625e3e02a65aaf024f4450925b1abf9c38c4888209cef6360b5a8494ab60d84593af397d11ce9d2b7d9e6cc81e9f202d9a46f0555713b5fa5a0b7f495d0994ef91d270f86069ab0a05ba11aa9d9f10b66a58f8b2730a11bb0f0378133128127a688c0c5e821b0856c29846fbce969890246557ff20310680e2d83ed02c60ae36c887004520a7c4342e4ab8206e6bc6cb05fe9e97bea5f457831129aa2a38bf0bc12878e18e3711e414d086ce314979850d8f8f17dff9da74dca0cc4be8d1e48f0803372328b16bf03d4cf9b3b9fd58ad6ad657ba7a86ed59cade8c2e5a26a8a6903da82139dea0c95b7d83a77cd121281021ad37f32ecc74069a2bcf670f213d679a73bfbc46251ed7bae70fd18c65525e45447fc20539eb83087f6141243843add558eb080b509643b21d1e986acd5c740dc7f346cabee96b9cda5fef9c51368b1302c1382544a879689da9c5ef4b2dfcd6aad1f1efe9e1f0b202534112baf81e66314914041eec9031959cdf1cb5dec3bd69ab71de48177c3889b1d6c0b5598c14902c90daac004f8b6dd96f8a4b5b8bbfa36c124abbf48755a857a09d1284eb1702b90b95b77dfe651aba593077b2a37c50b7409624d11540179f744a1294ec8764d5df51181346b7cfbe15b161490b387f852bf2e6e5d84225bf1fe9815f5f32d512776d392cf988fdb662c38ee7a0da119ef44e3ed5b21c7247fa0abae9f296fe5132f51cb56768653f849a52ea9b33c5c85603be5fc42c0cf536dfe2bcfc5d8ae2ec6d306c81f5a3d9732ed6b693102cffe57e6726562626fb23cd23ec16fe34ffa97af7b28e0b51ba7717c855e40ec5d0e9ca9add810e6154ab4b56663e5a940e809cd2da4ad49a77b99854b9e7514c36cc62223a20b7574d5629e820454f1ac4b68e0251bab893d8af038451e3226c0b9ec82179158ff7e60115bacca74575622b4cbb75fb2b5f1b0330ad5201019182ecf91128f405224bd00c3b45faf73b706f173811a74af9c58a49b7a2be24bd63da41f4eef288a43b0323d8f2f4dec815951e9724af53585c793bc6c2d3c79c7c6fad2309f5d06073632dec2053b1a6d683b63cdc833c664ea4f87f3d80f4f8a07124ae5d3806350a9e54561e6a5676ba2e74ddb34446b931eff8c44279cd2ae47f03e4005d49a3e40f6cb893b9d1a5c972a074d84af322862400871ca57cc054444179d9d05d006654fd029a6fa557ef4311386053c79abc5509749b6bf56c1d36b7c56dd99b74188be239908a647cf5e40bce1732bff0d44d92bd34600a756a050f9cdc11de36ce5c179c2b4b7c91850b019912bf7a1c7af46aa03c89b44365003c680d5baf0e98b33603578eb910ac83a6b499216787ce221a20b1f389fb050c36c7716fa5531172b6276e1c5b7b238b9b5b1b79121a8a3f7dd2b132a6d10c1674ae2f33a9206f564bda0638fbdf7dc0c12278b122e2f4a2abb0003ce028099600c4f935fc8e0520642ae8c9d5bc5461eaff5b0812edec5f8e16b68d708c01926eb53b0391fee3f197d2baa8c95408185060550aa72ccaa2f07da3f66a5982f02e2ba259edfd82b0f1ac37c7689b5e20e1ab6f219fee80d9f8c608487e6de3b68b40844411d34d48e6c90071b0d5c80a20165a26eaa0df1b40456103c798055c42ed705598a73ce72b16d50b8a74d421aeb4cf4eb513e270c4ce49dc426f974f50baaf74fe2776d646a7646b9a9a092d53c6684b601fe9e23d76755b163755291050ef6d9357bbd64df9c011ef2efa0bc1eb675742a6ad278490d0952f6e51e9d59478640d2e5420d283f8718663f5eb3765d26ad729ac186c52b10885883df0c05fa1e1f2d9885285a0f31c879d4830343e8638932c61d9a9b1e755836bf55b0270935daca5384f9f0664be32c333178170ae09393801648d731a14ae01262368efdc9db5a38c52efc358aa263488aba74f46b3058c2b8c63825dc04fa6e9ddfc513b98d3b44b7a5125cbcdd89e9a493e0d5d7cfe6cf208147f4c1a567de44dafcc374582b33b63ad591f93e1da184491dd34323131b0287c0591052b96f2724f786b247d0f2e84ef06b9eb3ec84ddbf007ff2a4050778bae8042822c2cca4f6960185d5f17706dc0dd0cf4392b829edf5f58066b06754c080acbf940dc245c180c3a24fe050458122cb86b0443a5c3cfd23d09d3fa8fb20921ca39224cee76c30761695472e486197c57c0d5f015dea8a2b6ef28a4ab2634dfad931cf542f3e714562734c61530be4755b5ee94a3bef2fea740127a12735652dad01429a27f43ab181f5ba3b2de00aaaf2175cdd253e1506cd94796a1ed9ddd91f77f6208d6d289d201b550fafc784e4db20af02b1ad7b9ab1d5211ef1de21c8dabbe8a07354e5df88ffb397dc1a33a35e67ce364b73788da56c9257f072c24faf20f7eec7274441ab255746108234cf4c2046cbb1238be01fd8f7091142ebc747dfde7e006dc5791d9b96d260daa023749c7203d97ec3618eb267415b80eae631ab8bb34bc285977bce8d2ca8754c9797ab24961d2b514337d8d53f2c0ad9c7fc57301bfaa06a77e13a28472bb10d719d3f4be902e86537fa60c277c4816d8a09d9c940863a5df4df47bd6aea08882a81d6ff8c7972bc43a191f3b37634453b9cbddb9b10b32b557104f042d0804f9402e364bff0e8fdc54b7c2e41d6bb2cf5110cb2088b71f8ff7f913da2a94cf452ab87e0af09741fdec670e552de82c514018ed6730440bdb51837c95c386a87783c34b8e8e45c7153e6dcc09d47d6b31f24455e8d06f8859e9648c60d682d153787467cfeebbbb0ef65c3131cdc4d948ffa2540a523fd5809a934f202849f67eae4258b862491543dc186d92523f57fcc7172fac45d13ea4fef75021c2a5474ed29dda5e4343d0b244d2d1a76d6987a591409b4e2368b27ecf408c2c75bd451571dbbfb2e06822b0da97fede35a07c4c9c3e0907f32d9bb370ca68b0c9a3bcc79ded755d83b0c1275ff014ce3229f88acc320abdf2685f479fad2e75ad36793f5f4191d66d69238fd2256de8c6f93be8ae220a2b88c5dccbf095e7ea1ca47a4b879c2d34150ea20dd17d87e5c78a03905343249ef57246c888c477c04a4bbcd7d3d779cd41c1777f223fda0720cb3398cfd10b83bb6e1c30822b2d68500c18e8ac48becdeaddb3b2dbaa21b787d4601dd3f754883340000e444fc21382a44b3f124230d5b812ac28347eef707f711804ec676f360416a36058cb5c96ebb1991acb4e1da8aa2999a19bc63296bf2744794ddbce3d8bffd1983229c1d9c24ae5bc290114ff3be4cdbd03aa3d29fd869cc713eb03cd1b4453374940f637536a49999447b55e9e11d8fdaffd283a0d4d078667ae816588470e9338b4cebfe7824e191e7f0df80ca7f13df5b63dd5ef96f411b3904e3a2e743cc51e9b1b9b0fd9b5d9ec121e7a93ac2bfc63b140c32bbb89bec79e3965b1362006e24c56fef19322967662eb5b9f87dcba0cdbe14c6bf64f1f6b83fbc2fa739e35218e0993e9aeb80bf511c9dcdabb62a27924ce5cc706c3eac16be38744a46a953338b2f0359c8ac9b4d6ca5a0d3037b5e1d281477fa0ab533840650ee9cff12cfef0bd0d2c8485539d4a55ac31a942038182e9d77f7f711f9b792ec03cf7d1ed63cd8e78712495279b5750773e7b3f8aac20d0097f5dda53f18e5e616b0d4930051fb2729a1b046bc6c0a732f8d6dd85723c0d8440bca9253a3c2940f75e4e1e32ca9d062631c83d3deff680ad766bef9ffb2e7193d7544691b67e6a071d635a2267673a62a673a11d66ac192836e84ea0aa2b7632750b2328504bd92451555ec73f39b6b766238336df17e97db764f32d3d2f215cd3ff079e677488012eb643b4ffc9c832300444393103bb974d946a9ba930df48beb29dad8578be8880617f3f00250be0d631490aa5684d150373159485c5c5f963fd5ccb57bca3794755ccc65c6173085b4ebc4fec276453212968c3fc8bb400ec6af6048bb882048af06937acc8ee07ff0aeaccb76471d70d11619bfe393f4b71daea63d38986245b985d772c64fb8f906ac913ba50f9c8d1d0387651cdcc2538fd9ec16f5b0949cefa4b4f0a4dd4021b0426453596345ee61711efc93ebceb4b3b67d47ae1bedab861563b448b4c6300e5ce234ccbbfd570e18156779e71d605bd3fb0b6840142c7e51064b70385d6dd7204bf15e6775db993807615e56aa99f471a10fe5e92ad5a4549fa1461819933d8baadc48e56ade8bf7e4a4ea6d71fec0ba5e1012043e94407feca5ced1961e3ef763e7e403a4587c982b1033698f2b6bafb940211c72b8d65754d38f13121e428fe29370f72236086477ad78e1384b9eca7142c1a8daff3a6e2596d4206720da8e9b501f71dfd938b1ffd40efd2a9751cd1ea10bd0a0fb0945ce57648d68e738f72dce8a2b2d01fd1aeefb9652c11b521b574da961975418b6b69cb9494db043ddab31555ce0897821e600e717765c33522bfccca9903c19efefc3a080cca3a0f4ff2f0c5cb0f4140f655c1774c23ddbfe3d7f25e3fbea50adbc77de831e463db7b96cc65726be8344aabcab0e8a91ff8f10cce342f181648abe15ae2fee8e449d9ebcde585c1e54178f3073f881b5c847371d2b55308b039e40711c4190c1336380ce6d8710c8b06b2ed11dece1ca0fb76030b6499d8e45fc8fa7bbc13a4f71216b76ef050422e9fa901767c7bc97800312fd1d82dad5886056c136354439550de0d36024086b3dc8528a332e56618f28d0e6889a84dc31d4ceb5b5c1baee71040c792d86f58a608e22a5fc79233351252cf327def88d16a11de529881a9b5775b6c061f49247af68cc8fdb5eb1b5340a852efca79e39bc30bba78add0df49e488f0fea3d51af0feb3d91de1fe83db11e1fd07302bd3ed473623d3fa817fe8d70015cab039a7ada805820cec24a074dbd1637e8db2b83ba36d027488e20d5e308beae2660f82c9090be1dadefd38f240f9e9db1f153fcdf5349c70528244f940012e4c29bd2a3679ba79217cfb4d9b2ef08848899434812b2f7de446e29934c4906a108080890073cd880244aeca22a8c47e4fcf3c492294fdc7eb7b7eb5e00a5f3ce6be71b2891ddeeaeab81a4c90f28a872650b196c2c4de1850c134a284dd143b295420c26576e574a29a54b799530e18005e72584f51bb158fe21458e1ad6bfa5611bc0a3bfff037f0a70c321e9012155c4346e370c8e22503bb84290a8a63f6c4984d0eae377108556e091500563ff84066030362c24ba9c12cb1d884072859c32368c4f22481985b03c0f685853092588d030b9a161b9f24b2959729d25d11d893a04db7523c99c84b3d1c6105a983ad4dac2f4f171e2ee6339830e8e48971d8719743658ede3ed4868812f832e64e84eb06b580dcfefaf7dfdb476bfba5bb5af5756f48ad2f768217e75fcfe694fbf2bab9fdf15ef1f7f75df60bf8ce2e4371f71bd839ddf244c50489caa46cd3170213b33b315363e162a2845c145183264c02813a7560003374c90e012c6cd1157e4eb151d943dbdc5f06b893b51b384f31a803368c0e276bfa28cd1c4ad8086d96d37633e3f05c32097bafefa9c60e7d2df364cd24927c882df22b35d10b526405148a8ce48926aa2399de0494dc2c2b5ae6cb5c2afc5cdcd913d764a9fd648bfd86f6904f12f8a40f225047f10295d826101ae8c6d6085200ddbd2b06194aaa10c185a5cf927e8703054a266922e5f1f93a6e9fadc38c6055f5c0358246090783952759fd25c9e5d8ec27acf8a61de1c71c60b1cc448a15af9708226283386c0e286d8593356feeaa3788a67484c5fd60891860c6dd0c4e99133c6d2b75fd4561a4bb4cfd9af9972b07ce95be1d1dead8ff16ba47f7ff8b7152431566b432eb24cb09e59cc6216b33e2c4227e8d51a67313e6c8867e0b7c0b75c2ba51188a35774d47db143946cd84c2d7be7eb4e2e7eacd73efaf19514482b4e9c38f7b9ced3b4197ff5130ce373cfc9cefbe4d758645ec97173ceba581ef3d4307e1992dce88404cefde04627245097b1303779f73756fb4751fbd57332e9cea4293751554f697a4645354bf354f551667ffc56af11798188bc128c112cd2390ec8f2248c1b9d6498c2b32e367e61a4e2aad85904b2816711883fa78c65a5a9a4c4c4cc542b7f9c7a85f66a317623acd619cf629c857fc37a14a9ae8c3db38cc34d3b372435f972ea6818bbcf95eef973ac366ee340a3ed7d0373f8d42b4adfa303e9171be61f247dce863c094edfc1fa4876a7d5dd3522314e9c38695cff16fc56d088fef4417f3ac83f2618f3a94370fc832fd9929c8e4c695918a77232626689f3d719d8dce834039a04dce8340396db4e34c8eecb89062cb7bbb9568e0ada034295305d7eaebdaeda87d957cc49903f47902fe5468a9b4e073d89cdf86bd2a785618bfcf8fed303e23fbd11e41b4dd0829593ae6c249d0d511c71060cd23445618401bc305285d39b30a6fc90c21b2c4038b1060a0a59114d42a8a08c2c090b2f8650218a61a10a8e1526807892040f5e040a667c4102ca862356f0e40535375071048d18ce88c253d0451070acb8e269862fa8549031482166c105142e40a451c3b40513152480a429cb17305fe264f10508072a6e0843090f52ae48410707aca4008397308aa82269ca114978216208282772d84245c914153e38f2c311355232486d21828c1411569694686a83830a114a0c1459105e88c8b2c587401ae2043224692203032da050408ca5a732b6b00163cabb2001214615306ea8a0d2848c0a0b18f3a68b163c51d1851b59d0135f9e805aa2cb1a30b0aca0c1d2992a284c8001a5831000b0028c0c6da2e8012a88146258220a131534747144155c5a600514497ec8a10a064d56088a1f39f092a20548c4e042050c2ebe05055254d218424c0a6644c4a0d2e6ca130d462869d9820549ba2083ca155108a1420d4faa8cd9e2044f904122080ac880258b2d9638d13206016160b93244162347040184d3165862e00515a62e5e5400c09b2454ac30419c24315936587304124b901155c6970a6411668d0bd2f05085112c3b4e67c060c3a50c324f149da93244941aa09466b8125ec1c30a7c0083260635a480a444061c22ae5062a58a211c0538e03c6103103e84810ad3fd8ea73adb7666f0d065072d9698a2a647f8baab2c5e1007cc9724b210d1c3dddd7fb8ee5ee7e7e3fe802c6450c060420c0b989c9a2cb915272867dc70e2431247b840c19229ef14c393fb373a41c1c2094a1431c926723181a82e9600238a8b2f667c30e3c4142a8c880367872154d8f134460c383cf12107a71ed2c8aedc50c6892c905ca1428cc2cd114a34ed10c68c35ce43bce2c90565d84851a60552f494616202198098b2051967f22933c514a82e5610058d1ef1630a4eacc06e7ec65881154b584c31f5822698297e54418c982885b41c5d277b1773b44f6b1db4baab2bc13874997dae0fd786757314f452e50d2996bfb9a59b1c267568686888abaf9fcbdfe5ea1fe65c663133357247c3fc1d8538e55004f28ebc4204f20dd860c3a130a2b92e877ca0030f441060fc81bfff5124a2ef492ca8e6caeb9f635698fbcb3bb8eef4c80c367669ac6134d2f70eba1da5f4230a11e80413d8b0bb2b46c24a9c44031bc04004c104fe803ea56fe9903481c65ef42315d766e0ab55a4aabfaaa0376c3e472239a9e09479f3a689372e576ea561dc50dad40febc3efca1dc9fabccfa261ebd414dd9e822b304639b5ea33604b354c8a80044dff888024ac3226d1e24a1108ca862e356625bd542c97f9fd690c147d7e8f221271cfcf7d445adb177660c8dee491e0be35b9a63da77d3e73305226bdf44f939435a1245fc87b57f224fd23a756dee4b319b6e7e05f3baeb4b4f26fbee2bd8493d441550a79a09c925346530db34f567ebcaea583384a55deb43368b68773d991348c05e724ada561ec4a11888d3499f4d24146f257cf2f651dd40426127912bf51306497922b77a986b1d4a6fc8c1936ecda8a94925407f1c829d8546c989f712d2ee322fef0bfa0967199cfb8fc47e4992924532f36520d4b4db1e4b0ad5c2d3c6b1898867153c39849fce15f555ea20a39b8503f605897336e9ad5b7b5d6aaa3617247c3a4d720514a59e5b681213fa016c531745cf90171834f83afe7e3f9b8ff74befb6c4ef82d507a45736a5a97a3f3f68b77dac8b1dfb09d0f632cc1b008bdf67da258bef6873200c3a1d80e18c65e6008bb21eb5a384069efebc6732ed7bbe6f3f6357e6ceffa6e998deff8026d3c0e30b4b2a7fa4c4b2db5d4520bfc0e5c7290ab6c50d90ed211b25dabb531c2ae4eecea0ce93c7b453a9dd4b55adb810e0edd30269babf1fdf84bdff535dff9f1bdf1f1adf1fd88b7f536be78739e9b5dbceefdd85aeffae8d7f8b8b5883ffd5fdc7e1d30ac64bca18a40fdcec60d5751c964526883d7e0c3da23fbf0d5df4b52680350835a415ee201c3d70d3bb0c8bcb6c87213a744eb73be9eef1b163677655a50828a3241942185972b5e082926d0224394186a60cd8cd5be7a1f1196521699cc3df5cb7df752264db989aa7a4a33a39a499e9afd132c8aa594fcd5e788bc70354e5eadca55fff68545e8d5406ee2ab31952290ae54b9a20a1d7e4083872718c841083654634cc1c407ce9baa85e2134b494949469a8d16d913a64a32e686ade676bb714ae2e58612895c72c9b20e72bacd3385aa04505748c98c68a1664d8f4ea1082869aac8987803a7477f53f5779a213d4b3d8eb4d3311670d1470a94943152ce4caffbc1b7c626c4f890d4834eb19d04bae740ba6491aeeb6a5fc37a698cce0a8f5cb7d6187f5660eca13daddac70854b5a37be4ba73c710d7a66ad663d3f1b0da334cdc8903b8b5befac31d9767a6c34c4ec1abcbc58f659618b903d8b113c7f6eca8377a310323965fcddd3b3a2435bbbc41ce5c3940f9c40bbbf13330629b9b59b692a258b9eeeefeba614413d5c8f61e1dee596e38745f3d63703112c55967bfff9cfdde11cd9a3802ed0b34d63451064d932d63a25461460a1410d1c41764bab489646801861733a67428630a49cb15a92c4ddc5459ea32e64b14d306083763b0592343982f566c806345490a6250831354886982818f335b9ad4c045952553585ac060b3458c23d2940963441962d874d1c3130a5220065406b2a491240822aa64a862228837606ea0810728353b64315305144730d9a10566f830830112125ebe585a326bf2e58d143c2c59c2e5cd0bc26862892c6da214416b5a40d2e66929ca940bb288542c10e38d132931f040450999041306155792a431050592113d3cb1f1a24a0f4d7849020b075928c07b320176cd4dcca576fe1a3d4cf952f6741e9a6dab885f59c41f88a3ebd58b70b3574eb0b9b9db997bba4f8f605c6b2de49e2e9925b717f41d0c27bf4bfef4aaf9d1ebe8d56eba1f31f7286b7dbdc009d28f8f62324b8b893e7f68a08c2e1be33b681b268bc8eb3fa34b1ae5ec33e6fc413f82e15cba51fa17bf460f363c9a4773e97a8c3d264e173899b061779fcec92ca8524a194197525ceda09d178e1bfeec365c3574726c8be575dcb6aa1a9d2e9bdf775e19b082fa8d64c3c048b2bd281ccfae32173edde973d5a646416ff797d47dba9be93238e919ecbc2e8e5bbf17f5aed81bd7c6ad5fadde95d8eeeeeeeeeedfe6ee7ea373775f551ff3a50442826bf429a0ef5ec1a8b1b3c60cbacbd8943d123ea7524570b58332989aac1d94c1ce7d5d1cf56a33fe8420f046a89dfc5c97ab9dfc6a5c6ed5c94fe7721bc775f2b397eb3816eb72ac4e7edee55a9dfcbacbd94e7edce5723af96d97abd1695fec21bfd5e56a74f2ab977375f2d32e6783c3c1e1e8e4e7977b753b5dcf0e4f062c097a04f29f24982db3a0f4d60fe7dd7eb2e673b32a09f3a74baed5fb62049adf7ddc1168fef679049abffa2ebdf3e937c1d8b07a1d648f650f5cb4c04ad3a6eda0d5f3ecaca6d55f7940e8afbe7a2368bfd264055d7b9e40bc11fc35309ceff37dd2395d42c1712dd7e1b2711dabd5aca353c36d546f18fbb8b85f5c9ab9c19d4ea78beddfe9579c32827b7a74b658d6e1c23a5c6c98808b3e21cfed79f49cdf95ed392e5ad52885648ff8b1fbedb747ea315f5b45a158fcf1bed983dbbe212ec81feda9b8418b77d150384cb9da8eab758fe8f918228578496a1ad357c209f2477b0e220b6e747a82e6ce20512b540d6dc0e90635577b1d1dd45afb1029c44a55d69e80a504261f2e48210e1c22851e267fb4a794ce15939333554aae6e3ac358acfb0db4f267689b45b4cab16c967c1b0c4fb8ac24c6c61eb107d562435288fb2dfcda7343be880bddd465178650e0354d607d0b7c817b1658e58f07beb07d07be14da5e7b9814e25e0dd328a5b3cee8fcae706fbf21f923b9efd508d03e7a25c4e48f86c586dc5f9086695f6badaa433a288c5445b4201d14dafb392088a8de341cdc840c26c66e30d958ff68cf7d0c22853850d39ed3945a882669cf4b53aef6ecc50a83994949da132cd7899a1bb296abb1ec6a3f3f075c81131432142668b11a38d8f7a148b26f1c996106a56e336c372b7152146ed641f1a5fcef8940f25faf57adf6f5c3f522c25783082459e81bf6f468496ccfe51ef25ffcf29fa8b04e4e96aaec1aa6446906d7228a2a89c104928a6ad6581b255d6ded2c16f953b380423936a4031cda428b392591e2cef99d6b4eed6a9144e9fc9c28f9fcbcd1a909d55dfd50a9e4976eec76a39311196ee823b7b0f546a724342ebf908f2653f227defaf5f40f3fdfb8d1c9c8194d92dcf0d5682a1763fc18dfdd3dc6d9710c235657ddc69c477fc1e2c72a7fdabaf69b07448ef0b36aee6037bbe58bce9e1ef3282accd97a130d8d135786b9f239af484e217d18c095bf7945d20316b4698ab3658d1459f408b2a899e20353122d3ef4e86e0713c6527777b743018557f956cd755a42e24a26345cf9114ac2c2684154d5143a38b18306945230450d0f3f50597124d62c4929a54be94337ec969081c286ae080f35c8ae086088d4550ad377fad12341a3715eda183676bd11e098a31437c371774bd9fda33d1fdd7e5b4a8f537677fb953c3eea8dbec4c5b882cb2f5e1eb2c1d011c2fa73dd52babb7ae6a41466b5ba715bd8e5dcbd87d5e970b1f4599e8fd5ede1d2e96cb13a5c727274745ceae8e8e8bc5a9782e105dc29a560f86376e3ea0a2ec6866e85e5b01fbe6a58efd95f3606791061cdbd3ac23cf816eba3d703e3acb4e3bc07749d06dbbe59e9af3ef975a80ee95829d3a4763dd574cf5d6b8bab8e8b41ade72fe2ae76dc71afb655739168e3589edd581eb76d5f8b8b44abcde3beab57d471de77997f933f8f5c3d67173b76dc5980cbe1e76a3133c72ef0c1285f14465f0ac557f8bad4eb3eeedb3eaac99fe1cea5af7d473baef4392fef0092a334a6872bdfbd4a9d73eafd5f67d8d6e473a55356d662fab8942ed79dab0fedf6d9cbd335e7f394f279b4e4332ad76afa31ecf1597df872b9cff6758064db6aaddd57df7d56bedb8b1ec16e4bf9b93c82dd1d282f88b8ed75d792096eeeaf99999b89047932454418724a723454a41b73d415f1c0100bc6d55418937b949d199d753ec56b0e6bd1ebce2ccc3192436774a60115a2e7328843c62561430da870c834a0a2333ac3218331b973dad7ef5cb81cf7914160bda7c4867c85b86efdc9712c66666666e6efbee9baf50b4fb8b5d3de7bef2b83b0bdd7018966803093e572200f1021ae6bc460e82222e354dd0d3c9a5137ec408e6d45a0c9f23a6eeb408d40f33bb08a40f323687fc8abcd1f5e5df7c40ad966cec44cacb4c45a4c13725a3c29634366e2136891060536f4aad27855ccc44c695eb2384155b39e1e1e26a058215436e361420049686c189bdd8ecd7a04901454c5d49383977c5aabbd71f8c26530ac3f9f58be2eb442dbaeb9f06541cf71e5c74b3b6052287677cf4fd77df9dc3999ee0aec0f86afbbc45ac2ba96b8fd5d9b353172c8dc1d8ad71cd6a2911c275608952161c32d6a8b8a443d54966384ca78665ed8500049b32b80a49ebb4551d916c533cb218b8383a9eb9a6a888be3c0edbbbb3d8f1ec2b7b33b4d15725d76a25821db8cc5840de3368b55705db659a7f4f2c1861d95d2eda8866cb34e0907d33cc3836d76eba5dff1e5beebc0f0689b41b17cb9ee30c7598b7ceb6ab56510daf862cbb191831512a7aed19cc2865bd4eddfa222514c8c0d6dd8588a44b1db944efd28c0e56d895b3fb4f1c5fc6297c1c7e24a63c30ed2dc0e62d7c652046a1b4bb7350a761ecc38a662a494fd469201add5479f703a83c033c2cb38716e64dd8eb162b1310681af7c03ec883c31deede57b1a11a45017f56d5fcf8b7edc0d5c0d767ecd91ff52c87b290f1085bcbb0f61205c1921c05fa70e183b148363e76b565207dd0a21f5f077f7399b58fc0b5d39d5231191fabcfa1adbbbb8b7d1fd0d2222dee360fdabf53bf633584520fafd197c75e7f5511cdfbcf1b90dd7d73558070c2debebbeed5b7db55ed7fce3f8b44476437afb69bdaeb59e88afb8fe2d1211e9e7b8efef58fd5e2422729d1589885c8f444468ebe308449ff57504a2ef7d3202d1ef3eff6604a2bf7d3402d15f7d5a04a25f3fed5b45200ac61d9e316819b9b8a69368d374725a2caf03c11adaf0e8e8cea76c2983165c7d46fe527e12a55b82081d149d6a90bafd0fe820e6da75e50b91999d9500d68705f09deb39fe1c85aafce9f7aeb23fae62315808ebc0f0397013c1a54f6bac542c4117fdd09ddfc1f9d29d32b3df091e75d7c94c298f8c01e5d163f418638cee1e638cdeb034de69f2e9cfa9b9a651f0a8bbf3ab26a53182ae4d6845a54bff5a5f2f5b411a935378f41863f418dddddddd1db4a25d6070dc13257feefcf1c79f56b76de33ccf9352e674d67349d9ddddddb2372a74645ccbb2778db13b76ecd831ce1839c6d76cecc831be4e11d17a54968297c51c3687bbd8dd52b66cd93272dcc969c37523497e00d7c5beb863e022171b8b74bc8495c1ee8b972fd68b0dbb59f90397b511cc625d5687086b85b0117ce90c613516ed7670b033976f5dbe43ab5e11cb6e57b7596b86fb584ae92e5dbafceee62b5ce4963e5b54ebb42434ad565647d5252e36d792ec6a297d524dea7491dc59e1384e82a1656eafc8ddfad046a037823502f9471df10b637352a7d53badd3748ab0b5d6ae6259753a32ab53840debd59159aed39159aeeb74bad8f85d04bda239edeb599d8ecc86f67a6df9b460fea3c7da9c9cc9e9e8c418298d34d2487d7ad439c2b10e1096257773338356a4c03949515344531818dcb0d214563a338605ee15c60c3aa04230db6664b46cb32494b0cc6474206189534e53e29415cbdf1d2014411495c139a2329f46723245def8a4841bd78d4e5092a8373e1d11e5644bce8d4e50b4cc392318c7885fb99e0f0a4eb3f13c9af376a26660f2e49348bbe9cd6e3437c8e59a5eaf5a59d6c4b2d6cb8f9b8accb0652d8b9a1333618cdc661951d4e206d90ff99b6421d2652f4d1dc4ccb20ef2c2c28c0de96032d4410cf28c4095305d6eeb38af88fbeef3fea3d6b55aad0ea25a694aa6d446298dd72d254cb79999ff475fd63c0850254c1e09fef2fd3d9ef287ffecf2e9e0bea1b6dc626630f474cc96eddefb7e1dd57bddce7bafc8d3220a3d19b7e3e5b8468d1af22516db545a9448b4009645a21a1f85b53f39e6868662b12b613c3d1fda0d5e8221f773e8c51f573e1300a83f2000c74610356554e27f10fb800040393ce8e00716236ac37130b8793161ef10cce4d7c881a50cd3524a29cd50a7be863a7535ee5ea4e6f458afe92815c492d0fc6e823b2cb06c3cf0a07b07db55195dde0e838b5555d786181bdadb45a22f56c990a5a2144ff507a59191e1341a6c34348c3855d1c24e54d85c9e1b9daa30b9de8d4e559074cec5539c6a82a2b2fe825b718c31c6186bb8ba46f2dd844eea31297bb7e4f19faf813ef8824a431d9330ff1ef3d33e2abd7acc4ffbf94925db63be045b90f78a08e6d34fa9f698df9e099c94d463be44923d91b0c20a2cb0f0e951a5907f9457e2cbda0d80f095ffc51f7b2a4347d571f9e398101070f9636d80931ccc343d81a58c24c8c4d853b5e96ae25afd0a0c7908795da3d58bf0fc5f031af645ad011d2442037ed2e7ea156d200f7501f4867dcf65d7aba76a93b694425ed7483ed2fd86b94ffd62e7d341d29b55ab5158665d97a3614dc150763afaa7630d8b5e510e9f1072b8eb700ae1e698c923bce4de6ddf71140c37bed2a95318af9cae4009bb9dd7eabab55bba5bb7da5ef341e16a91835b67124ec5552b0f5f62258f79020a1ee475575ff8721748e5e564e39407394187b9c6a5bd9df35d07511b3d94f34b2a1d726d3a3f7da8df06289f3e39a66192a9841955575dbddc90a953e57de4f758ec02d661d679fe960ec86e6ccef74a4a923bc831217351c5a1b5d65a0d8c02a34ad0b45707668030b383e512aedca821830db98a4d8f8b4afe74947c6ad8042553c3a49516ba0182107ffa7b5c32bd646aa87ea6927287084204e207532291cbb0a1a2aaa092428840fc892544200986d5ca9d558b1bae6a587f0e5646754ec4a8e853e4b20b97b59e2622843daf69472f0d04325d90de73f98b21ecf6eaaf9473d6e90ef200e1f147baeea0bbec113b9f5c60297946d07e6a600326330a5d910e7aa1832a23d9d1813b1ad67d7b3d69076e600368d55c08a3d4102ab1ea1f068bc58656b26a3564b5f2d5bfd1fc8625a943585b76c474d0e9c31a0472684a49aefc1795252e89f46ea78a0d7dae7d1733f3774e867cd55990737ebec7dce21c90e1b09e87749fdbfabc48e5dd37c397562da8edd8a1699a4f4f13195cdc6fd84e15cbbf238b0d2592db0f874dec1d3bee04976e18a93a898bfaecd77590e17415cb495509ba724d5b813d4839055d7bf5d45a573d48d5fa9ae370429078828e2027d41fb55afb02a76e3b0f3f92fc7846e0d004e6f1d1970eddf67cac2e27e930f534e59c73ce598f8678e4235df948377e8c1f5aaf88412bc1d087a5d41fab3b94a3431d627b8f759dab729d943dfdd3ec71e0ab0cfbdbf60edad7ad462e3bdbfd9c3d127c1d61bb86c58eb8773858beee5af44bbdd69a0647398dfb31bd22ed6dc4f9a3a12b64e81ab1d6fd77d3f3e11569cf0919baa108d7c803c3a1ebbdff0674bb50c7ed3e74ddcec6ed34b85d006e67856c78cfbd8d2fe7e3aff1f1b7f859cfaecb60e8bdb7bef0c301dcfe29c9b8f3e9739fdfed8b577b214337da9e337cadf5b78fedc6b92799b7f87760a4dcf4224ee3a66bdab7bdf5d33a084cd0bd76520a190303d87e4e722fbf1efed83dbb94b85e5b78f5dad315a594993257cdf5f26d7bf68ab698b6bdf6ab0d8c9c57b4e2557da7226af5fe311816b9b2f2e5f7c26afc77d5d9add6289ffee9dfbe9764f0bdb04b347430e46f7b8e3b28ec065a704363437faf9f517f91c6dda5405cfaf7db88d8e5569ae62ff0d5b4e7d1ee40369752735daebed71a61ab2277e511b602226f5d316ce5976b956082083de70e1e0dd3c193276f0085331a0afdd3af81216562431e1d3b44b82197d0b07e13a4eb46af2848e7d6a53c725d0a86dcc595200f101e1f9ea116fc1af10e399dbe06b6e03782a09454ba9c1281e14a1a7b59574e13552d275eaffee9ed4317aef602f76af2eae1aed2d7dd3e2397af9ed56ae555d0e78a75594dd334d09370da470d8c9aa6f99340a475e77791a86acc6fba0f394e8ae5f11e293e8d7db9c53382f7ac678121eb3d16eb0bbd1ff1769f7c4e364884e3f8e8d5c7dbd42ba254bcdc9d1b9da4545daeebba4ffee643f3bef047bc4647fddecff74021fdb3887a5fdcdef37e38581bc6fdf6c9db7d2c14719918294c37de7837fa469bf4b6efb46dfb7ae571aec360e4d6e7fad52bb7fdf6c50a64235023794cdbb8aef41a89b6a7bfda2251f7f4b948e4bd17895a4f9f1589ecd36f45a29ca76f2391ced3cf8944359ebe0e83505db0c8e487d3c811683ed732024d9f54ab35c09c0824bf5f27c77eac0824bff57911483eebf33e2e02c9efbe6d552390fc950e7f2035902da8c3920c8e8d58a91b76ddc749363a511973f949e01a29e9ce475ae2fab3c0e3ce478a40d168823c232ce0fa2fe0867de506bfc1eddf200467dd7e162cebf6b338981fc6186344e238b7bf1006233d3972942cf91ec78d78802ce0f6b310673782314eecf638b53078e84f5a9975c458c98d83f67e70702b4884835b9fdbba1889a60f7b8970958fbf4498ea560681198436c26dcf7974b2a6b7ba0881f9dbcb6ff5dbe71b7bfef50be1460fc8fcb0a5f723845b412221dcfa9ce5ada5b340d0b705aacdb6823e0f10faf3a95c3d0701faabe75bbf90e3dc1ae7aec0f9f56b5ddae28ba4311b21938ae21ecdf0134ce7083c3b59e570aad8c10c942a7a7ab15dc37838ed203543da3d597f233a5fd3420dac1da199a8048ef6543909d1d808000040019314000020100a8885229148301c2782547d14000d7f9242724e9b0a84498ec32886829031c4186088210018438c199b991100f9d2e0966d7b80bf87620cf1ca882827bae51464d18a9364808a6c8feeea19badcbf01025721092335a1a0c3a936588296b992a3afefdab4f9ed77634a0d9dfec9c5369ff8d122c1b9953b11e4297091d14df5578527a5f10332d7999c6cd65837838a85bc0c470e32b7abcbe1e530b5e86a9c7cc6d6afa301402dd1fdc328d085057f7a089410e92fc54cb3bfa371ff511258975f54f6e81de66928b1b9d3698c9dbb1f18cf17cdb88ff58ca3c4ac52727b21ceb3ce70fda463e45a68ac7abf223a78d7547c7aac9da2d2330c3c44c821413dfd1f9925923900a60ef2d05cd2996ed33d3e9dc4259d47b450672e11d26b90aae9c7e966b29086d68ba1ae0e30e64170ec39e8d078603985ccd191b0aac929c2fd63f85776ef87c0024ea528007afb730bc10061ea3b324523758a8435757ea655a414ecc77b9d44cdc2a515ecab091f4c9e4a7f69a0fc29749a5ac18c9e10c09dc3a90d6a322ac22d4943cf3f087acf34d386264ea1f04600f11665351edf5ec9783ea1a197035de640ca1b63e492b2e31391645e8f470f643920ab2ca94750979ddd40bdc12bc91d773365f656750d137d02d260cd01da065e9e482348115933e3bea5f6e5d965e2ebea4faf9c6d2b292bd4e072d3f4d956dbc84431680f876bfc4554f944915f469903fa59a38b1e4cc2ce5d1417250fe936d4dd9a0bb6458b1378f2c670601bf2a72774de07441b5e2044d7991c260049ac8afdaa9bd68bb56daec75dff0a9aa4882aa47ceac05de874085dee94c1a08bf125ec73202eb7481027d626dcce337271255d2bd069ae4795ffb4cd6e4bc5725c9a79044e7485e5798972187789685e55770caf92d69bbab57504ffc7fd8e66b9d47c7c25dd5afacf366fc7a6680f942ee42a3d885b4e71e9544b34f4dc5a7a75b58ed8971f37c2ff7223c33d4fd9b899a9af5f410112cdca39cb9f5225297a7a598d86aa8a5b59a54d95c82de52f98ccf708b905f4a929ca5fd40c554c51a404375c572ff71d2c7f959f5b7d6d2db3136673489276b16ee04efbdf69128ddc8ed22c9fdf7fee8b261cd43184a106292b84a1df8fd44218de3b0b9a8296ddcedb8e2d7982810d407818d908c8364283d2ba35ac69c33df41a103a8cdb28a083b0d10eb2243d69423547501a32ca2dc86802bf99766db955d07c7cf41bb4c15d476d9c22d3920810a619c411bab78a3e703b0d288321609f5ad88adc2fee4c8af6347db640b17cfa84f5fc98a4d37eb708c37778144563287184a1e0add8ef536f037e8ec0544d872b326d9525e9a856292e25844826f41e64eff95b4663c3a13fd964c7a2e3fbc97b27720e701b1b8ce0c50653c8dc20d6db12426163a69cd9602c038c5a991b2539f6c0994a3378fd0c9859f4e6617c8ec1b76593baf31c4ca65baf3690d35f538bc108cba63a156c77ad1b6b7d8492729523886dbd0bfd4b17a58db646aff58d129f4881774b7eebbb9cf033161a649332bfa11b1412c13b1518346da0f033e45e4760a4d5d4749f8031d676b059e1b198a164f7e3aa6f88029197c372d2f00d481aeacf3949185294f43e967158f76c12d1c2f2855fc92bf1113018c20680b8ee8194368c54fc87c480d15809dcefc4c79c36c80c65c88869318697e634fb2a7245ec1da81d537c5500b181b461b6f9d6a3eba62cefae4a12708e227a12d4086f362c9dddacadb28c1545f5e90b2a579a44f1c6cd19da8b99e53cc1380113836bd4d809dd517231864d37448e35f0ced48b223629eb3623e1858b86485ace7e7e445ed98778b17670fab3edceefdb468dc4ee73051e5ed5773a0a78b9f879f9c3279b6ed7504a613948f51199b60af50b18c4b359d8e365bc39e6c091f73f225a489d2290b120399a418dae3ab1801ec19530c58ee40a8c111e51d09e7c241b598045f52780e6f8f255d019f78b38fa582cc69595b56269591e0ce04656601a1869f792009631f917f18e9911c74622e421a31a854d9edd2180fd118a29b5a535827f6a998a7aad84823d62bb1227b8f03c4dbbb61e70054cabd2c2ce5a3c994a0e2ea746a9afb72da53776e8cb2008b57ad94c2b606215c750a399f886db6bb94ace603efeea2d649bf885dec950ae5dbe2af09af0e47a52e4d774f17070685215c5a171cd8b54428cec9ad4c6e2920bcaf4bb6030939978ebe478577a9a06049cb03addf8a2fd7dd032b4c539a67bc23cb53bab896363f500c7969a9ff0662eaebf68c11537059f28fc0ab46aa3906a25de1a20d75e3cbe3cffa292105b0d8d62c71c75e57509c87c654feb186d6d5d161134cd3a9771b7dcac7b7b3b02083bcca3e94d9d584c056684b07de9c7463a177ae687d89d7c163ac664692de60a2d857c0ba365ef1910b86a3c0902de74dbefd52c0805230602b72392d1b1e81bb2ea60be6f6d929f3b168b41daaa53538de4a4989d930296965dfde1d467a3af1fa78ee3d5e38f6d2327b9fd6b0e9c562de6fed670969ab95c2314d8131e620c59b650b28ef1ce0f3c1ebcaa98952905a36457fb14aa2333e034521f9b38e051e86ca3508ca511c15d5cd4d1353e779a68e7c71ffb7137c4b0bc8a52c2740c4d6f98133b03e3224ab802470dceb5a66d1a6017c91d449ba1af23d99e70342cc0e5696fab5ae7091a0a74b6e7881143aa85c57bbc1a02ba6f9cb009b4a299b5fc38c1f14f355ac5960747d429cc80374a06ac659e6a6c74ab09120b0221ef37cf48b0e0ea097e7fad34bfc2a9bbad4421d3c4f1ea4352f12baa18e67e82f74bce60d764608099f8a11c8d63e334482671078f66583410effb00ceeefb59170ebf41eff3194ce84717ae83c6baca9d4d853eff0625f3fe8ad5df517094403b96e6e4920f5257060f0a212ca9a774efb0ab5564a1852c239ac233263fcf10bd8b2c83ab69222347ff70e78f4f2576dcac78ddcc06bfacb120802c612815d99e987d97bc3fd8c293992910b87bd8935ee4e136971d14396a26aa3a07c2547a86395b511f1003845234b4b90568d2201333db964088686e6df7fcb4530e29c3c3e9bb24d7f009188121579839ce239665a85930c1ad9e3bed35d41c0b7f40d0226ade65cffe87bd645d5f46e11e9a4bd9c6aef66f6d2d0cb7ec7c19810fe56f3543db8c6f29f655b8a57dfd0556652152dd554ad1d24f79f3bdd93348cbd8669d604dbd3657c3a229d768d1deb25ef7edde15ae2a4dbb93b8d5fad316273459c9befc742b4a4cb8a221b51bb67516a575064215db504e686441a1f1ba88719dece7931b4545b80c93ed25d651ae8bd0e3eb4140957998135f27699822aa15699086428df1d2594ff8c13a64c4312c1133350f2076bd724b14ffcbb4bfa17e0ba0184590c3b07cb6a0b33ea01591624f064ee9478627ba4a8787d826d85f06213a691f0b821959f3c5cd52322ca4e202909203a5756a0cea3d8dc917859e4783c6a8495bd1e87109fe866500400611ccc642f0991e86d00d5e78a11513313cabe5c178d90563428deb404bcb3b65e167f226ced9288d53dd753d82532f82d03a3c32d2e6c81dd1619b6a3e1b7ccfa6a66af97211e1be24a9c95bc56c4f348adeaf3ea4b784af25218f4712df57121fd7e312157a1b9aaecbfbdd92ace2b839231e77d914b53283817971a00a6da0e6d0b2c0420e5324cc15173ee5f8355081b4fd59db3bdf489fe6162e49c511bda21675738145c8b983e4482423df301bd19ec2a2f6d4197b8ad515a8804e8bb94c116db1119544a73d5b551833d5a412c8c8eab0b749bb6329a1ee8b6acb9ee6a759a4299f54d4d94f1049e090473c7c09d967b42ec455e7faa2f7107420fec90cc596109ee7c040ce2633e6d4a878ba75d75704ee6abe5483eda009df6cbb247b67a204163e8391ca606b5097231a57eb1dd7cb9f1a1086ba13fa7a3fec9cc42ed37d1cf25fddfd7a98e8b50570eb1c83ada4b75cedbd21aec1fd9a8aed53a2fe133350d6fcec562b0ee414a3e5b5a2d9a997db169a3b39fe759bb439f38c8fbf4bea07a8492eead2f39d8a02e105f885563abe73f6184fa28e5f8a83c101d00a5e75cd3f9b55686f0c4174890d2f050ad8e7eacbc653fbfaa52381c7e29be85d2291998199d4a13e74f7f57efa769522d01492ae606a997eca384d0d0bb0d62db5e3ed285e9e11a1c94a1d589456e0ec73d105364d2d67cc2a6d85aa6294ab659c2ccf6155191ec5424a9b6e8ce3ae796151fe805e672438fce84b018204801a5518ae67ada80992379ffc3b46964e7f14e1c2c558463f42fc6e34bb4726945eb29157d1f09e95b3de00808806ca27bee687f389b17c0714d2ee5ac65563797b60c0e8149a72634e75aeafcb1c62daac716e844a83a1d990b9511b0601c85b7f9d8469e62466156ef4bf295b508a6d1c7711254c60f6a51bb38f72292901913ba0b3544cf4884ef42f368fd338f8c1e70ea5455fa203856a221205b69ff20af0ca87778a2f16ca4bc1809d5e42ec1474966204e66bee960fa1dcb7c903489609f250e6d63780905614a381d5efe4ac68b1b8feae78b856884bb04865324706d76da797ff2a2b452b779768c24d55066d64be553f5e948ba11af955af1d89b4a1281c46d71704f7f28ec3d475220aec5708f6c354a0c6a27c0c8fa3d36d329fd096496645070d233cc4a73d395788e569aa8cc857b82e7a5903ba9b43fe0caa153d20ae2e80c96b11f082a1d268c07f3397fb4f126b30e68bcb26c010f540926ed85a7984a5843d79d8c4c31732aa01ba874df778fdd21cacec4187aa816395d4d9ab2c38f162880626db55cfb0fb6fd62934e4759743175594deaae680e8c7000cc3204b0316712d6534b113db47aeb08c4c33ae5c29db42b916cd2bdd627d33f8dca67cb390cfa80a0e6aae8503322c44fd949b5369299e3c2b7baaea149dbf08ce3f8268c3f2c63f02fbea714a15e5efe2484b61384124c2d30c1c99fe328467a6e8b2196737f644af829b6ef1e170e9b44d49809ba8d17ef7f4185027d3dfd627ee873b0bb41dec9149d9635ed6834648c5563466488a935016b13a23f21df1c0129e58fc1984ad951261f057c9d1296d0a23dccc7fe260d87c55b74978e53ebde0f3259a550825245cf9ba89b295bcc6f0319dcd6b244ff020fefa49d5287854655c8e63abe002817694d7f5c61a308b495e46d2ae73ae2d812b0633ff74728f7841f0b1011a1dfc7cd7ef5adafe9b9a496343bb7d423042cb1f50257f2a5b5964680ad5197134b10c8fc0804fcabff15d8ebf2870eec7da007a5b6be1bec3d12f38a5b09ff477140d0b5d132c517a35e7d1899d34d788610ac3c1e733d42a76fc3574cb6c9600e2357c4f3610a1fdfede6c5413bca649d378e71fbcfbffc487ca37db036c929f6e03400d438b1f1280867ab3b2250bc21460bfa65893145e9c74e6cead59238bb254af859951d8bd442437773038d8cf160991dfe97afd6d4fb797a9737200c9d34a9b2609c6b710f25722f19ff28af12640bac93fe4d05e1111445d72eb1b93e5c6a688c95f066979a3289fafa1c59d89c25c4bdcb3bd6c538d0c5295d11c7e8c0cd9adc498ec1fb9ad30522449d5e9af42252a55c43952dd561a68542fb367d234fef6f01f57999fe8fff7c8dd4b552a882a688d8e3e53acbf2d9444df2ec8a6b70a7f52e85f458479afbe4c3746bf2c95cd125fe1406e16956d00c0591f4185cf08db9d23cccca2899dab6bfb40b1f81545bf27c49cfa782e8ba6323761878df1f98f2b80bef95fe6c393371f65cab8f9ff7e2fff4fad466764be759e35d0f7143030b5b118e4816464509ca23c5bf55648464db3f2410203cc16f96c368ad24c3f21572d4c10f69532fc3461d404ee93e8634a703648c04849de2570cea61d0e37d30dec7093a41116761014036b63d3f83c212d05f768a57124960f9df9a7dece8be551ef6178d2a02f41a286b6712ad7e0f9e0b7ba9256346ec0bf7d24398f68e78f8d9f7d4fd2935d146425323bb35b05a1197b2b425fcf2937189bc5f67c09249b380cee0a2b1e1c76bfe8fddda5c48051b3603b5419ef5ef4ee50660a39a0101f18175ae86cbbfa146a74c1de8de64d0c63100efa83975a1338492fb1f77c5a0e268aaf5e9f44b645efdddd6903fe3bfde48a261d2f9723b016f70e5b42467e3ab7266df895ae31c7b330771267ae1037b523fe408e2192a6907fb5fea6efbb098f049b7c7183ff10ff790cc9d2522065b65e279aeb219aa7d38b21eb4efcfc5df805817ab5e9d7b471d6507a2e4f032f19ec0bc316d4685bdc364c1687d587c492cb7086747c9f8f6f85f6272f73548689fbbbdb20cbb3ef9bafe1568b7750566a529f698e4d8661749fd125880c3ed1c0a6f931b43f3dc43ddf822aab6e80b106740d1aa236ad2d1a41d709913f07d6664aa0171d17849ddc0a8b0ea71cc66cae39f3dfcc6b88a36f046b923ec6d1fdfd265c4b448677caba57d3bc5fb2ef9b64d30431ed362343926e0fc3f84793a8eb38f469cc5c934dd81ac296cd740cc84056958a4d85645dd93056b77eb0ba062bd412d67d7b87bd12ec9908b83ad6f0474b12dc2f20ced09360d304c08deeddc94f13639f414b65047c4afa691b95f6b730efd60c0f7726d1750f406a99165209dca46a99531ae2905c218f7df7701b64526e1b5e839f638e07b0cd220039c7808af900566975d2dae3381d1c6c9f3af2b3e605ebe2506bd839b42b25109ac962c1a5637c2dbf26faf722d0253e9cbe248b6ec43cc00858b394a2c9f2bad9c0e7dad6b21a54131ad9ba842ae0f5bf4db2a837fc84510b3eddd02d1567f149494af95ab605290f4c5947990cf0a5dc318c8aa06efd562fd5681d0ae6f08e73255560f06a3354b2a2235c68adebd53f3d91f39a9501a73adaae06225ecd56946681c1ece46224b65bd95ad0b8485dcef550cc634c1809446364dca7b515552c7890a348bb38a32f403e3741474adcdee6269dcd849230a4e32932cef491dbd8572adf9dd507a830c10f5b4de8f05e315eb32511d562306f557a13484b56eb780ce04d0876b9ad6e2afd832179fcfcf06b506c8d8412503371267071d85909de8bb147153061453ec8d7f575c2e9d1512d0bd93136900eddbe59be86107a6c328c6e96b263725108d96d857814cc1a05d58e2c263a3cf159c3f6a7c14d818a2706287c6c855388960a99ba2847572ca6209185fa5126151a588786fc099d5c6358fb900b274f90a1b0232eaf39431dbe07b029a571d7ac175edc9c16b8b9c3c57996450c3072c1d98781bc68feb2165f2251b33f68eab9f4e3bbf5c379aa001ca31fe3961fe03f856948fce8aa2be1f885c508b80fdf509d73b38f39f0b6ddd6079c747e1ed347ef81d15c952d997905aca7545489221195dd376364458d9e89eb198567b31d20ca5136f549428d15149135a36a635c55ec08cf109f3ccf13d5a012f4e780b46bacf1550d041a0ef1f2206cf49c004211404824d62a54a6441931766ceccdf42845bfb0bca5ce7fa97caee1221da9df7e8c7915895e58d60fbba2881bb2a2b66d2742eef504c4eaefd84cc0c34f9da5873e0e1a86fabbd734f7c2d0174c1a255dcc50264616b0771ed4837640b9600220204bc4551e14a2732d627cba0536c54d8c140dab85d0b8d58955186d0599f3dad3697e555ce03bfbb370d73e1713254217c8d0c00a0b81f777079c553104eb9d116cf4047905cc22bcf7f51da3f3a99ea8cb8b9553a62a1b59da438c774f8657bfc11417d2b34098077d70fb65e428a529a8f77a8242608c3df4fa5d83a34f4ef36abc5ed9e2f42fb26af3746793778cd562a65a38fd8980fcdacefae26161feeb656c8640fe8b3a50faaf71a0fe3f4b1fdcf85b44943b623e41fa09310941f8cc21d9d109c75039740c901e834d88cfb9191ea2db4ff701f9205a8242b3ced88b81ade9fa72892dea24e3598c566c7b288dfed7268acf94db899a4c909f833468ed0445ff87baabb1159cea2b1f21030f3a216df134235d9c1c9401ead39653e0c3fb08512730d30741c8758dcb9c0484866c7cf4dfafaebd09e08ff131294bd6ee0fde71d3f40c82966b9ed134ece91c75aa6047e0016eed8268a451d9ef6cf002337b46f1b7fe1bba7f4309c444a03c82ad4948f984af393ab0c64794143bd89095748f8edb8e0ba6f5fd15430617583e87b054db8202719475641cebac41116971656cc2cd282407f63e078ffc87f6f505a8963891e77991bc672d166384f37033d0b9d9268037ee20a1948dcaadf9ed59b1e920a3e13c5d117a21dcc571d70caf4a922fe3e766e072c2b9e21f0b74b04e5e39b22289aadf6c65ac32551423327755b3776b86eaf51a47036a0f4c6da9673252eda3dbc45c5262580e0baf9b1c5385f14945dc24120acfa98c14567634bd33d4d8aaf8bf37f6014624d5bd504ba70a0b2d8ca282af2c367cfd5b48ae431c3ac5c817ac3674022db9a66b0353588048f257736d37d4310eeddf4b3109915c32d4d049999a4ab290e9e574aa4bd86d55e86eb6cb230ac6da9548219de19a4c59876b1b3cb57e7c43a7db25164d6f989cdf1bc6f6790da69b3579a07e95163e186a0c0406191f4b723f970aa7449073a814879d3c2107a393f4b8d3a5590bf7c53eef20d2f887ebc475d48422d2d1d7c4a0b1602edfc2cbfb67e269c805844f5f674d4b61209f810f24aa74b08a8b95e7baef2369348a25e62e3595bde3ba2e2c42fecb765144b75c9737b411dc7c97853711efc48479074c3ac5ec8614d99580b4245494c74f67c38a90b6a0c28a9cd288a57ba18f42113a33bd75661add9a63bef68d9017a27fa7bf33b500413af0032ad1df94346f014303749d1c2bc18d830399cfcadc39ba6cb7be439f034ebd4e70f96d1e1abbd91554c4b5d0e7a27f897b66f914edd89b9712f4a73fa0f8095e4c5f9bbeb8cf1d38bd6b40c143bcd850cb8b59a07498b162fb5192bf2f7cdd7541c43cbf1fdb85f78986df00790380fa8797184c09c5e47e510fe72d4a691ac220e517def0d132947c4e3fcef90cc6ff6827a5d7f9503712409da406589756cb5403eb042b5514fcaa1c26378dc5578b55d5ea39bcf881e6d072f70bf37313142c26b75576ae333b1396be2b4ab08889cfd8bb73a6dab57700821d132ebb032c59a6794ce14da889d5d34c991e1138acd9ceaca2866b2b33c6526ab6b86323367e314702538b939cff0a753643c999cd5276601aaea3d40198d0172f6c7105e4355ca92b8059289457a0ec639009cd4a3d2b6e72d23e73e76b3c30908cc95720dc109dea83777956d1325d3f3ee63bd1db5e23883c5908025d1e2428b2641608a30c88415753be784777f235344084e83ad08bbb9a899e7b3d66b73b6ae7a210e86f1886a88ce6c4d6067e9d65ec4e756bfa94e8793efab25761560a5974ca6698600b1f8759dcef24478608a620122b899f26fcbb8a4269404b30855fec8b0598ddb314342428895003aa2cbd07816bfc001ff64e6b69f77d596346dee52773d58a1eef5062e8023657fa09a6064ee291364d1d3b81fb35065788509128422ff1fa9304605a059d30810415740abdee4e720fd4caea53328e97ca1928414e6afc954b079bf2498d6d0b9e65952aba76b2f18097f70e0b3cb83aaa0c8e8e7c963c49c498378a7c7a361cc71012355924bf7208514611cb8e9f836be8794f0dcd1b2dab37798d596a79ce9fe531ff6cfae4a552c7a5cbd51730231d63c6810a5f5b531ebea804aa128c770d6c5461247564b9988ce88f34d55b99b709000e3470be8733d4ffcb56091f220c451cc5fe8e275614e98618c2be276ece15895256f3a30a52f4080e35c2b0ed33b392a9825eb46608119b0ca0c250720d4950860b44b6fc70382a5824017e9ff1a7691316d405929cfb7c1967fd9061370a185ed8b7d9a04c958ceb752d863c3b7551c718df2a341ee13d0209e77de0d5f07acc830ca80b488244f19b58e9e6e489febdcfcfc32df44ccc1d9db57763f9bd4c4fce2a58a7f82de8a172224a8ba99b2c786749bdb06cfd19a49917a7bda14178ad2e70353378a289b9607471dd334b0a482b162d0cc3e64c64ec750ee952c6850d34968cc6e372ca0e2c12019dddbb05a42ea3865581122ee032482dd1b0bc562a2dd8981af201ec970e95dc4f08d7d4d6f49dbc1660c6e1705b621a8821fccb38391e90243946115b8d932b5f041483c4a8bf71250480a78488e4564cde6cce93c6c3bb9b3573885333c1c0253048d3dd73aefc3a5a0d7b5675b5f10bc74760a7d2a97b42202cedd29cd06003fc1e11957d5c7e46c165e8ac3338f610fefc82533c807c272fa32ae98ef26a2ddbc2889eb8532b38106c9e0243948e2ea28cbcd8b8f914db1ab34466b38db065130e64e5050dc45117cb4b7e1c3040a8e0e1f220a71597bd812de11c8116e06653fe98877806accc10f71585bf36c315cc9c2b7f33cfdc9c3ef8619329419082d206d34c79c4b7dc0e39910fcc31066c6e7ae376e71cc35aeba410cb22aaf12d2dca69b78af4c83da63e671d9507e89d35c747fb912cb285345a16a0b342d50d347c2c914079432323be310f373b33f9d1361563683f9b0500119543fd5c3307c0314733a46611fb6a256cb981a69fd3856cce57ed5328d07d5a1008e0ade13ebc9a64f08bd8f6aeb08b711e9c461a3a178ffbbbbee039e069c99ba20c5836fb1c7012422b6f2d6b74405fc0bc6709a42bc13e86ba8a20dbe3713f52eb014ca701bbeef4a0b891d0ddd2d7c1af20b7a9aa7e1f3acf4e2fe481a1691aae25b3b6e583e94b60d3a27fd79dd76a645e25302f87c40c916239db8b4716d7c2ffde5dc711ea13612feed637d31eabefc7fc163f1f9bcc285498eff66835e36af518e8d180215ecedb76d8fa0f11a67013735c3bdf6aceae9aec3105e99df212354d82a557d12cdcec248fb3db5ee88fcf2eb2eb1dc185ae8d264610f09a50e341db7f7ab7317ab0bae69870e23afefcb6872ab78006241bf10e3646bdda9a08d9592e891ba77629c8557cc64a9c185723da2d0f6d115b9f35440f589543c5a1c9a0a3689d42aa3dd551a48ec3bd06fe0dcbce18e15f8f82864ae9079e833ebb4547d57ef61e4e84aed93840be9d38b4ae8be687c1bce5c8671b3b0e889c640621f7e432236f4c689ca64c2d39a7254f45b030991bd623f054a5886dd470177540bed13af51aac6785bf18cf687f310d13bbecdb59d2e14f528c7891746e1c3036c7bdfcac68c16e1b8dc8a91f20dc85f4111b451849db18f0f10b38c184e69fb64afea8dffcec79f26b91701984336b4b031328298ac14096b59f53aa5357033eb9263066c91b5d98df69897c9d6ccf9fea5e2ed702efe902753389839c06a9f65791a3c41107ed5be1b30a48f48d3961fc1948641c0668a35e7a003e59860752c4cd9058518339e198c3ac40402871d3c440c40e92c8d0bb935f407b118ed400b80c2008d12884ab40c5f6a3a2d5c866a3cf35bd61988e25491865308c61d21ba28fe4fd32cbb1c9aa062fefd97b166a4685b749e61c13b8c1e82195299b56d1b442201cf5d7ef3f915cc20a7f1de1d140469ab273a1c795f4489b89865693f0f2691494eeeb91de93069b6979c71e40e430063056c373f9ef1f55ec10d1a696ba168e9ee28dade51ad9f5ab5f8fd18ff36fcc0684c4256fddf9bf1e1d7551c1fdad26fda83a750bd2e370cc566a3b775635d7f049989954b171b03f48ab5b1e33f2f1552165bb790d146458c99d54ce38c65f18723abef30558c0d063ca7aad694460d628059063b16315492a9231095b94be6ecf348bbf948e47809f6fc2583a382374139650653da3598369522028511f890411e229ad5f3ee6a1292f22a08dfbed0568ca04267c0228904f3410a47cb1b1368a55fb296a7fd8ab2c49f14ae4a45450eefce5326c5a1630e6e14f179c3f4928acfbea5fc7f8a803ea264ac5d67fcdc176dee0f0d63f2d330d0664b11a74311488d83d552d1777f23b604acb3dc8adf54e4a9b49be72f572df4011e9e63edbb7da60022f291104a009e4cded9b362a16dc0b9d8bb72c447349b6c82d56285dc961c524757dcd4821d136b2ccef38f09b994d88d4e2ba9ec23b3f4a24c53e016c5deb0dabb305597c1342a701f8db137acd393eaab07cec87fa80b251d78b18dbaf904aeac4afec8ca8a2c15302ff9a1e83bcfcd9e5ede83586e3dd05cadd78ac4c3f5e2fce8b20e9ce9546c19d0c51893989895000e56dfb03ef37556966829be310b0ecae3ba4e25c55ce8c04ae0eb843b9c1484fa0c0b9226a2710fbf7a5fb9ae4bc20755b2890973603bcfc629037d35c7776f6a28940cacb2f96bc1a9df949b094a90d7c9fedc4e28c6fbb4498c552975b6f58b63e2b3bbdfeb9841170823a8899ce7f23560f81b8aa64f70f429f0e4f3822f324d8623de9b27d4969a74d55ec643139143a0ead3829e5da5525ceb24fb6f498aefaa0f75901b5f1d20e6aa877847600d04057321d1aa74071d231b731f6b4f8325ffb37e14be380172e0bcae1bc35e4a2dc22aab569017ecb3098381f7f16f636b6e6a5b59e85fefbd648ea8a2f5e501a95295320319acdabb13fd6648da3490d745bcf93e00561c950ce6866ff8eb015e71551021feabef0429dc4b2003758de2a88257622a6aa73381aa16d6ff35d63edc9e1e57a62a059ab69e767ed0e62bb6475338d5876c4c6d52e6d307088f1cfc9ebca9b942f987ab8fc06fa7ca68e34bb2d714402998f7fbc39273bc25a17550debddb338970e59e36543a4adc3ea2be41fceb5b46681a72ebe486186fb60f501ece2f655662647064845a6dcc9bfd3d372f0c40f8823e42464c7d5e2d60cb945461013bb5ea04bb23f797e82c617a5d4d2223fe7e6f8f09d82eb45368c2ce18a7021f8a0f7cc91899e8d8105c9cee3f83426c3b2002cd293931651d9c9157428089c847cd52ca6b69352e29ac6b010ba3e78708be0ae5bf7ad0d974f0f572d72a0983498767a65fc4b24a299bb0ec4b4d5a15095f58d0119d20ece2fc17a63cef07d237152ded77a788d0b1f271035b88822a18ae72a7f2d389ca2eeb12c1b3d459856c6b23226a79a0a47c338448e37889b3cfd701124d33bcae87006299db4b1b9a5a510f8d309819aea31ec24a5460fc6d8a64f62ccff5767d9cec1cbc8932ea55f511cf668dc1c5d43734c701b416ef1d774ff35cbc3ab88f8c3d9601393cdaa071b61d131651175c342c3d82e6a8aeb598ed80cccf61540cf9f38ca342aa0cd401d8186af6fd71142ae91d2fbcf1081f961628781b96217f4f5135c09f6f5b38c99bf94d711e9fead64ed2d1f09fea19d75bc170f323a9d3022b1a34a82b71714a6418462a6f08c22a38d8535c3bc6230fa15781eba2c926ec6faeaaa2b2fd6052742ecb5c14c09941c8f9bdb362917c85bb1c43e3135996361063184639224027851dc500c461ee6d490580350f9c47be189cf4fc8207c5a88a1b9387bed77b4b442cbb23585989196bb75d91e13b66e2490223fc5598b356ceb2ee703ba5e61cc1153fc637b6c7340acce40220bf6afa51d05daff981ee3667b03cbe633256bbdfb1821247d356c276f32fcfdf7a7ad0aeccd444b63c2cad2edb35bb10e1f1bb727fc9bb0ee0aff55b1034b68d2ac88a08c8207804043b1018ec785df47a51c10f8fcd9b59a68295e0216a32b6a6ba9e07b17a0ff081c433ea8a9b8d113ac3ed8f30ffe38a96ea068ef3fa850c405c98a14d4126fcba1715e051da43df2f58b93771e4933f12b71421736773a1cea5b9e5428f2cbe94a89b697588d34633310406617f217293a52b45eea1dfd20063d102b69ec69dbe3ae510d1087556f043d18873fe5703e944012ef206afe6972febe612cb780cdeb03333985d29101b08a40891ef4705af5024de5943618cc1c38a492f5fa5bc42098c877a650be4acb6c68a53236fe729201209398f338d6ff0c4743e590dc80ec734101e64296d65bec59f216ce2072d681de7a4b008b1ede67be3edfb450c36bdca9aa13bd80b03579c463327e7bc87dc7a227f942be91f042e21ef6108d00fb1e1a325803fb07ad9a7c8d76a7922068b5736a7f64af448ddc93aa779963d7b138d6082b8bc641311b0fa02a46becbda8832924f08019988d423a30e0297ca9da1c5da4048e1a09e8dfb74fad3824b9413a3a2ed794042311074c81ea6e80230a57fb2afeff0eceac7f17b8a9581304b412634d1c448866e3acd0c8044f4767cbbbcc51bd65edf29832ea0d858c11cdd0a625de38b430a6e084eedfe7b187c15fd0a0005c455a887771aaeb7688eb731f32ae65f8e13f12540c8beac9232c8de322f2bb22ee19e356fa241d228bfed7d54d41887e1ebbaa697232e2695c2dd9e6ea631a7102d2a562148322c4af66c03e10ecc0c5df709c014ec07677ace687612b867775678bc0ac2dfbc3ddcd722637838b49af2a2184bfba7221af3db33fd015815638d6bc19fd6a8beb146a3a5654696aea76448e89cc06c4dd677866ab9dd4b568e4c41e81bf695ab4d40b1422906442e10ff2c6e1e10850f6c41a1ad087202065388ebcecb2add9c48332dcfd7c248f335190630e6b967ac6ab4b36c8a6a810032c42fed9f1c8fac16984377f3f294eeeb9b2895743aca2e7defe6dddee9fbf1fb883bbb6809d9af0c61617790f36a2172f7a2244416df10147ea817ad838f0591e37ed4d03891991d856f656491be096c1933e37ee8432ae1be3600c40d68b3bb290ad576db4587aa10e4fbbbc420874e2c12625d0689d0681998442d2d9be4232965f77bf0b58cda5ae8286e87b8f2d885437eefae21899257c688d3e0de7c4711efd832657460984a0c11c65f69046188fcfceeaf1e59b0f832779667f687df9eefe457a89cbe915db14510945f50f7e365d3ccf0c23aef2bc676dffe22c1868d30f37ca963402fd6b483b4edfd3424267c1d3976f5a0db4316112b1218223ab75d8c013c84b52a8782471e0f583e8cafa7687f661ad1b4369985d5f5f837672a45e0d8111e959fc0c31377e06507faff0a01cea18a968561f4a51e996bd3ab3a68c77c630c31a1014776463db8df50317ffb2dcce5ff2150c46920281022b7366e44471ff8fecdc1189ae0ab9aed2ba9d822cf9ac2b927b50f66f48aade429e0621afc8044c1afc061e23a60df3f80b5b1afcac28a4a686dbceee0a89b4d550178e4ece51537a5c812670efbcdf50d1468520a765fc9c7f3d45010617b6b5999e48d2ace2675c1f819084bf17fc8af6e2e42ae2a645f225f068cb00644e07a7263297bb4a28be317194a0fdce1204ecf7085137ebca053cb10a2c1c2b3716519ac2130a6a84f891edaa2ef17a83201df48be717240e6756d8e1950c1b86ce0b4d911dd36305184fe64345258267e70495704210d4d441449279a18b313420b5d1430d2893028970f2592fceaf48e28d2c96653275a79e644a1c5bef802590a4d691d02c75754e29e5cdb6ffcf2b20ef0bf372769fb56e38ddb974280fa99703d07265300630c4e01eb07299b97c1e60d7d17ff2e7033b45b7ce2bdc00fed950444e15a213507b4b835b562cc43ee3e742fcdc2266015e330873605d06e01c7464d909dfba424c60621c734620b283371249b5775ef45c1d731f95f8288c44939968618488168e9016c7329ae6ef4f1208fa480a6c6030872ffbf40e2ec196088adf6608473576b533788fba0a2e7531d5aaa0723d3ecc4f8261879f5aa31a3b60a8a824c6cc95a0106c7fe1948b20243126db5004140f611e8f4ac762b6784cb1789fef7a714fc1ac4dd86a645a63b5d83b69960c99be4c3299bc064d6e41eaa0641bb05b2df9f5c2bfc3a30d9cb2a81b74085ea37d395e6346ca14b4b6d09797aeab96cb4997c8e63681fdc31a978f20aaeb084f93c524403ebf32b860a3d9634c747110ba0be2a93e86e38f73bafe6f97eaf61c4e72d20879d829071e2c68cf478319645b20d1cd48990ebbe7ed6cc4fb6c86cac19c99643273d8226dbe756b78b4e47e1d050fba986991a89c0baded0d2d4722ccd51f0b2b384056ac0ebebdcd4e26d4e6c99b9f1a3d3f03a0873d5ff3c7a43fdbdb57ac3ff85761e8312cb8e81263672d633a495840ef76d7914c68f5481c93a202d75afee48fd98cd176036bf423b83824e5177c164690c706d55b3e95ce69e8fc942f477fef7fa83de7f88956b1a84aa706ac272af7999dceddc3c2f5443b6c7780fda4b8ee00f576590b8add6b4e841caea576b80a588fd6d4ad99e252c09e07c8c5d5e1363b2439d19edf337011cc38bb620cf49ebc1a6a8c22f0811513e655c048792dc64f21e3692574eff4006037b5cf5bd0bd8317d15aa884d0596fbe295c0336d138ad63903c3c3e6cf7876e701cd911d0641d203dde38950032c62a64d12fb52b999ec62ddf3f2618ebb1b701d48b7c741834a876b7c3a6a986246bb5a7f01a7952dca7b0e2c35b50b0e551f44f309b4225e98eda0aea7649b79c1d4dec675212d8f62be35d6eba0184709e6330bda2634efee5715611bd0ee2ccf86a6b8b226323df6a668ffb22f5068cac531376463f5020a158d983c4822c859d9457cba005b94b3eb12852acb801ed91e658880cd20f4ddb4347c146c280cb54ea5688f0e0b71019b83661e1bcaf53a7700b9048582f20ef8ad93a0d579ce691f78530964c51d7cf1d61038597b62b55ab0fe5c051223db485f250a970f3600c384418f8f4c9cb5d196d801191b8ada728182e42685e253d5f26ce0458fb757a9cb4bf3b4f71ee03cd3c81c4f2a88e1afa053f13d4394aeeca31dcfbc2dca14acbadcd2f2dc551413a221bffd715881df9dd0b1234ae919af16ff1ae18e0c3fad36f9670c85b5130fa86b70142054499d177d4dee0eb506453b319921f2955ee4c99e8006f338b0d6f4049a13c04e7f2442da35c11fb54ccabe779c1d6863cda51152b70a8950b3015f421a6ea7d270dd2c7a1e565efc3f4c122e18ead44e4dee531e094b049bf75a8a97b3ac76af4635cdca89270fcdf6b0a1e2c9c3b73444347e9f0c2f64518ab5b957deaf0f1663df2de333b8162a9a215a75c12dc8c511c93e8af972ef9821004e8639984f3eb685aa762028ebee7258212d198c0812e31ae113becfb43de7996d992b973d1c7488518b6d30e7f804cbb3d1984aa18f1d31ae4c09f1241bfd36f0446dba22d8f2ff7619a0946f01a88edb2af3e0ec5f0841f4f905775b97255c3975aba928af4347a0fdb426130cbd3a8be545b72bcfd4e830f78a45d8578da8347ee2c6fb3d89a0bf4526c74138f32f0b02a72d8326684a4ec003f7d80080cc7888d3c1bbbd46d6031f9b343d8b3f4ae5624db7373e38bf2c545fb7578c4b284d7f6793aa76f538b907745f85eea150c787957e2dac823d0d315bdf59aef71c07cc2a6ac0e64c5934e4a00f833cddef4512991a762b833ea3dc090b714a889c298e993694a388ad55fb833b315b112d985e9b77618fbcd0bba17cf8dcd0f705f762485b100a7067b174580a3eff1dd56791889f632c733d90846cf8781b2c00540634257e0b9498945a704ae0d7ee0c1474282c59f9c1d8cddb97fd6091de26e48e89dcca34e75019a2cd82766998a46cd5056217d2f1ec14c1f1a66d35d7cc3653f6aa4bb957f43b2d28cc4cd32187604c9f295aa04fd038a302b5aacabf079424dc698c0fea55919bd93cfd860eb0207baa0eda1531fadfe0bcd38845342bc69035829ecdbc61733e26e941c844680baea6cedf0953b795d55e57d16c006de2b6a55e13febe70304c118c48afd83a77c36f578e83bd90b376e9c109b8fe2634d69333f5b7dcf74a5b68bedd9284131d3c1fef82fe8cf505fb86ee1d008f0a0b9b7352e1f1c28448e64d671f54930fe805cb52367326344d846ba474d332a74f5d36596e1470dfc4078c1bc042d816f48d234b2d29ef02d4efcd4b68a07359bfecb58443d1c5d3312f8b7cb093057f813eaf251cec4145dc2aa65d6ee17229d4d839cf897eef40eb6b85b8970cb9f80242839635a57d10bffefd8b72211a3e14120c7913feba9e0a8d157bdff538ab9c6b17b4c0e15a8e9b61b84f0c5a7cd87b4047219aa5e5f2420d838f2de7275b934293d36eed7b30c6ad5e8c2f2b8cfda3eeb59b3685787b761e7e6b538b5f6b34fa179c1b2ee6bb4166736951fb6a269945c782f37b83353ee859c2ba2c7d01d1b6e61ad001beb1016b04f4cffcaedb06f9986dde62266fc5fd3cb12584b17c9dcda7046facda4a21ead7a7b37d4477fb49169aa24aa0931992974bdfbf1cc1537b1ef439d2e0a763bd1c91a2141f8b5e8e6f0d1c89d4f346ff93ee1564342b430ea68e3254ed312bed718fe263a2d01569c977102f7f408923b0427e5d8052191b022346974d9fda0169aa1c7a75d0cf8d8fc97aa1c4953ab52adfc286f7015b82633abb05c9368367493ec1aa5fe0e00e8e5f067c1691602ebcb1e32a4b003af59aeb05e9fa5ec905c50eea23185c493dee81240940b480bbf7d8b836b0f52de9c9c241111168b05ebd061968d22e5d335323e3406ad14e990182b772a9e97ae72a8d6db19a4aa9d098fda5d277f0cd935e3e025b9c07c90bde7176e987a0463ffa405ceb1f083a9b2bb773f33d6cfa6fe3d2696cfe88cf7668f9c5afa71df1bb75bf35f8b59b116bd2e60f1a1fc3116ade6ec7b8b3ebf636b2d1fa1acd26ecfea01b4cccaee6ed0093dff1f2b7dbb241a5d8f45c4c63393eae72eabeb3ca33fbabb05679cb5c7fadc06a0ac577e79b26edd3cd1f8e808e36f087ded9dbbfbb2517621f5c179278d989c1709c15baf5051e890f0d88f24523f9ee98c676cb289316d05a211c51f37628c4eff4f9bb4b8d1dbb914cd8466b5334028dd9cf14bd039072a2dd91deda4c7e7a4c34fd7c59a364a27a880f74c92c8af51576f27002c621c07de88aeeb015f28af2e0335caacff5d90156791be9834befc82543485c731529e75b7f15a39cd28e9703aceb142aa2064d701bb0b2f71d9c97182f8d5c0b4a9f96eda611807005f18260654470c6034d3d6d0a263406d132557732bbb02526c9d1ef6a3a8efe249a6ada2ae69e2988ef46d553a573b17994d2b9fa3c4eb466ac4e19d2efb6b68f7f648dde834d2557740b889b1bfc2cbde94249e6c3ba552d06155ed24be09c20a310e89e7a8999d7f4313e2b001fc351dad9cc81000959289762bc76750bb88c6357c7e1815a3eed994d564fff6b2b3aa00b91ac69d1b0ab4592de720c9db3d5f0a6fb818c350fdaaf3c509fc428f4961920aa474aaeafe90ec2d1ab850019e83ddc14d0961b5742c6a0e93c93d197ccbdba42474b8b17a5a7ba5b465d311f013ccfade23d07f4d6db33bdfa007a7186ab8f9075a127c5533618d1286dbc4391a9b2e2eaeac9a1bc41f26c4ac6c25bd6ffacb24816431836d51462675b5323e4d900dbcf144248336571b5a519702aa54c55fdf827b314516019cae05446084b2c1a89e1b9fa02687ed7f3710ad5af3aa89826c09430eb6199ade3a7fc9a64a8daf07bf1f259c7e3ff52c6373d6806bad5446b9229e94da4f66f5547aac7b20295a1e0fde63b71453c065251461423fb6920c1c4b22b2c4526ec1dc743886791e7c35826d3d871821332757e70e265f685fdd8c259de0556b01d4834d1d365126d765e6e0d3842551f81731d139a81725eed6f5367680e73adf134d29401c0d97a729cd85cd7c8c4763a12b63a3714ccb5b4c3740d3469725c97f25147d0ef6e75e3d7b4568b55519a2a94092edfedb4e292d9ee74491eeb24057ac8f9b10fb18adba71410ec7af4d0e71016d118f6c9468f737d8a447d70d89151c460645561b40394d605b74573179d27a4270828a2f0b4335345f5e0de95718739999127b2f59140d115250a90196aa2e656160225262a6e69a5a0c8a4e0aaac128a9a14aec2aaa0c809c55584d59904ec23e870e341f70193c53704da9520ae9ed547dbf724ffa29cd4478abb76a4bee9b0344c98069940a47ab8f61c6c3f14b6d9211910dcc3321919301497f778aa92962f9c62e3a2617abaf904a0a4b92ec9271859971e335a4831134a5a897ba0b49027ebed285314292bd72ebcfc838b7dee86449bfe18af38c3b645d0e8b3a09237974f1ee83551c9b21b4ad684c98a0aedc6150f3c96c0c45e429f1e6810d10c8cb92d0adcd2818f229fdb54457fdb3bba0ce4c28381bebe2f4e95ba236d43a65d496e3e5036262ef8af27556a9ba87b10c1ec14059c1ddd655a9306883a3ef765b86cc7194f6b9f8ac4eceb91c742858cd06dc0927300a018797d90bd2488458341bfae8974470c330731abb7f19ea41221d18b67a0e308113380c2103ad32b4c423f3e45da9a70cc331b47a6528ed18fdf70a235fb8fc611c2036fd5c1478cf136e459541b583f3dbad5fe24e57464179ce34dcf59e5b7b8e19038e805b70323de9441f126c3fb735f93cf0926a8e030af2c39c1850df11e727e3c99e17404d6e2fcbb7586b029d514b93163a6eb9333029545d96bd0bf0e776ad80ca3211ea0e5dff86f1469126667043d215a751fdfbbc007ccc4951137ef0815527184b099205a634400a567c02b641850d1a74c1ef26b5ec5b5cce2d6761351a9e2dac4301f45faf654e0c69c00161d674564eab2b8f1b852780f1d13b2b077e4d8634276c936898e9309a103fbc0de02ce68079453281a138027a925ae0db64ec2045ec28b92d43a241e96eca770f78845fcadc035a5cfb5daac514aa48c81b45ceb2c71a44812a93f1a2e6fade9ad417c414b045d2d772190b738ed033465f78d969d979f9e64aaad59dec6215ea1e50c1540dfedd3aff73f2f508bbdb7ae1d811a4ff7c27d7f8fe947e46bc564042548c3775c0c75077e833731921a9fcc5e34508488a7c9e2c6e6bce91ace3c3f2794f5930bab94b2fe3444a2cc46f15f4d4213e78ac06f8103063595d291033543c296ea85cd02762854f53cd3ee090336861a9caba32e1276c6bad34b47ba20983a23651918e4f879dcb234b0dd48b75c2c83e1d8c704b76af81caaae88397cabacb0a9f2e9945d16ea7ed3275ca69bcb2fbc63354388816e1f39bf518b91aa8c0bfd536c5aab6fae4237a8360e50d7a7d521ac02c4ccdb742594aebd3126bf088a21490559cd8042e4d064b005e0275388273075349f9fd4ebc7e07323761094c363c660bff6c196cc81b61ba0dbce42db7cc3be78385bcec67d587bea8c82f8b80c46e7c3eba0a6ef17a4794d599aea1f27637de12ea384d50c4d81db9be759b5da551a014d84398a686f0a8d342544596ffc0193c23e11c1635a249c0d51e8686606f7a690199221ae2d5bc4d09882832ec13925f85e087a944a59511bc55d47c9d9e9ce193a62b5e323d2259eb0bc216eb2ecc55f4ff8be7306cf1d010addf6c9663d0c9ba50362494141cce4ad093146af1388ffef4e202a5a9d526e005ddac2346e2186f74ed0a735956293968f26fb96637d39a1a6111dc0be4f5c269950ea9a48b517054282f0fb9fa511c9f0a7f68a5ec000eb891d9c6aa34036f9f956d938b770ee9da52c0c354467993e8bc8f2a36c4fc0cdb713eacc7e2ebe5dc89675e0f28c03a7804e02cc6b14b332d438fb84b3d905ccc339ad0be0227b49da8ffcdc82db6529140bac6db6cc72c3f88c557fe1e1e38a4c2160428b16c7d9fe3b02f1937d0467d9b3a9e4a274195e1a42cf30297abcea7c31bfc0a9dd00810ea892229ec0a1a536aef95b8bdaad28081fe36ef58468b5a947991d7379c9b02702613e05a614f5b2963a0ba25f62af17fa973488da43974f9458c524dde8b847a14779653d1f8a74cd6dce0d938ecf30055ca210b47f1d5bddcadee206a82ce5852343767ac014aaa17f992494debe25a1e7cefaad09aa6c0d71c1663bf78102631cb01720501909d099d8c61f69888ee82f521efd015ba2f4338f05d79d694790533657fe62df82b76c5e3f34555f9b2f0388959eed41dbee63cde7829eed34938b7cea7e5ea05721dff8736fe868d0eaa79eb20ec7cb0c40d6a0d8dc4d3564675852570e119ac7d5c3e6263f4b6634850c63e241af1c1a095c0d4fb66ebc9074091c20aa1e71a78b962d0045581559738cd80f035c03a7b7f04a4f7e2566faebae7d8d05ab4fab526033d6e7b680eb06952c52fbdb5ea0efee01b94e9ef0aaec20b5cc383e79851af3eef1384219122d5f64992c7e0688242bd339ee2634302f8372d24e07cac434d5048972f91b5edd8cf1ebc8bf592b849b28f2cb28cc53e7c58c28034934eb3209a557c65f877c519c61c0e1f846a91f5bc59354facba84c538b4644891fdfd7f201690bc408524f42a18cdeef077454b5666c9f73db1947d315b6704cbc03749b869f38cbfd172f23508722bb45022e4d2d19e3473b78ce8d8e7c76f6bf2469b91bfedf7d434366dec9cf446cd7290a477a6051049b9b3a2d46ca6ce6106f380c42aaac2546dee4220de8025969ee4127e03e33db4c750ad03c4eb47d0d09d00b3c291b78f5ba8c43f22aeaede4c608809258d8404834e39fb0b0a65325a7bc7952642a0832599078e493ec2cc06660aa3b209ebf3e6be44735f01486b19ecd421c99c9db0c8bc9326fe912751b93659e966df19f2f612ae60c45b30ae292977d367f113de79923af9c830616cc85182911151cee510f6cfc829299aa30d54d4e1dd864e5365506f34674402bd2ced601227389e688ba5956fafa4c23a2afb174429f836650d6257d8da882b62547ce502adbec9dd3ec2b57b4d1deff2705d250ba7151c148794a88d3d622ac66735dbe9854f69834ab15353ec0a964ed807ca344d36f3a653022dcbbae47f6bfffbb138295dcf295d0bfd48c4ddfab0cfb26503411ee88df07a211104ea46c096ab10c75fb7f92b716206bd604e2da40007b826934753d087f6cd1437305f6a4f3242c7c1037e18c9dbb18f8a986807b1201008519b2e928f23eef5cdbc136241ffb90e3dc1c3861d0b85d0c39d954c91be06dd811ba8a729ca33dd2442d380fc20493822d271e08b1d6078b58f284e13737f4d309234d72e6d7fa95e6e1710102744c75ec0f461c2a95d3a0a5901c1dc2b1c0b77456ec957fb1eb6cceec0de6af21f41200a60f96b564df7d8d866800e91a6710762ffc503c0a8563e419f370470806691e3425877195eeffd460f8616a6db49f344119e9d42cf51066baf2ca66276026e7ffe5e12675e4997a4cd8c7e8cd651c42c93171459053dae1c018fe5ead70ad33bab5b8de79ef610539b72f40c3aa1a36ecc4e6c5eff02706d6b8591b397efff9356660bd4807e7e550e5a3f01fcd1ceb045baf9f801753857c184b8e148f455d5249e83624674dd200a9ba1a2fb75687180b664884a179b4789ccbfe706823eb946f52923df072291c3b3b2401589f1debed268add0dc325285ede61d13c563bac7f436152f1cbd22890f82c8a46e990b3e2d3f83d211cf51e5ef9840058d93e6a3e7c326c1600d1611e37c01c6c371cc1c29144e2417cc74470d8e30e17054339ba0bc471016fca136828e8bb4b08566c25f9fee85345986f082ebdf5a09c34fc9d75f56974e207b1b515d4d6b18d1f1e80c006181508762035a080cc99761c841efae19dd4d64da264d051270ac7fd81dd99859b649b2372dd2f690402c73a9a963d028c63c9284d99c12c9da83042ee40dd5a34d742ee56be3c194041f1470040c51d395fd8c175dcd99b2ce28edde0c7e0d70da0bb2e5ee4a050b578eaa81dbf6dc41ae0b5b2e746ac183cfef5321ae295c5b6a779341a0959b2f83f289269c5b53f1ad7f9894e3be87400e0caac44495b05d3dbda2f9d34f5db7e5dd1d8f9b8aa1bb6d2b266f96fa9ecf798c2ed357ae93a542fe034f72aae4aa91ca5e3f62935ed202ce95416706f5edc7a0f105263c9d2a3391307bf922dcc8b976a404d5076bd9d2c204bf3e91e4be997f0bab2612900243432e61b61215dc69aaaa13b8e33809448830ffb59564086c2d064a53be483cd4b28cdf8505d1a6417342641cb243c6873c251cdd9c1c5a2ea9115ea133815435248a15459c6d145671d27988e5ec0fddcea6cb52fe9fe3c8a8dfcf85538e37fcea54564b72bda0a8a42ff3c42e1b7ed2091c8d1aef7085d92981b0fa4222b46f79a30e0c3cd688746dd05c2c4c72066be4f5450fc0dfc4465a57c8b315d43534d22cca73c6761ff9bba1ee8774bb6d76c2e682c101a538c93e774f73a84a8a7a85dea60e57ff3443121f3317198a4d5a4365f674d2632a4f0540294ef755e6b6818593337858ab791bd8b6e54ab6db0c4792f4aec1a247db3c0899e631690f53fb097a910187f7ed716b162e46c5d173de79a653e0b06ba9bdadfa43a224b7062b78fa6b4f3842002c0c9df9d8300c9b6b54f21a220f963312bd44cb0a3bfef9e79d21e3de39e9fb34653b01e7da5a8561303f826192fc9d6b0b46645cc376241c965282484a9f3ce99ab394ef3cc0980a67711099da8d9474bd8e21adba8a976cd2f06c738c5c6176f77b34b3cf6a73d0ef2fff82997fc88fdbd96f72a952a1f3e4dbf1e464763952d407a198ebf0501184cc938b8465a5069562069869830b198f5786fdab6868085b370b817e2a217572ac859e83f497b84ea0a0128e313a49f2f868fb8d9f3079f6412b81eef22db75a6afffdc6f6f70059f6c07ce988318633c84919a600b725a67f324816c5788ff5ec524c4428fea728b45f5a7802f99b2dda7a250c5c44b81c6053e8d7d8193cb8495b419e70835676f716ac7bb407649242b925661e43a289aba3e441ec9c5bad564c899e6245291603a1808b4c20ed52682fa9c71ab164b46c14c31c687d9a4ce9e3dfb0f054b906f11b135953815ba488134207e5f03781f778874ca5d2dc24add28b70aaabde8203a22cbee7c14d14fc21e5dc529d129803d0affca1931d01c83dca398b2e8a010cc84a98c260c3ad750d18f7f5e8baabf579fa39184b040458921610be7498403afb0a575c59805a8fc53c3006d2ab15e78718b6d5fc876c55b6752cfc6418b08893daa92e0ecc55d76feb897a4a20fa80c9a0be8327f82295d87704028c26e04f58fceb999f8149b886c0dbc060e7600410a5dec8f1bfad7be3a628de3e8fe782dfb9bd10fe898e4adeb3155dfae1359abff85f06d70001d9403076c80f1210b481c68b1c21593668bb91b494a5928c277c47ce152bdc88cb25930033c203cc7e971ccc35c52767de00d87455cc10c507aa8b2bc2f4a10ba0c741b516923ee6ef19df9f27d7beb81a03cc285562a0aaa17b35e2838a3eae64f531903823600c3edd8abac92a2cbf9e2cfc8612e5f91b35a5a61029cc1761c5c78497c7428531c4d02f271e0d040f6a4d13659135a75009f05b0073d6daab224f7c1cb17c4a004ddcced62a9d9920892158a07784c9e48ac9fca26c22143d827a38d0b918361ef3b13cf9e4d113e836e61e8c8a1ea2234b7998a06f1b8bd47ceb02577240d84037dcd8dacbfb5f1d5048ae484b243b780d0b74a614ed5e8a42602783339ad3563593d6ddee1015ea05b405f4e99c8472431c1584e80077d2187844140e4c4c27b8cdcef7965332ebb80962ccd133e70bd0b206ceb24a059137e1f70d997bd344cfb4dab0d96cb64e5c3af00f5e867ab01860503f1c110ab3e5b92e71363e0efe38b94b806508a5a3e017d36c09dc4aa2999665c604e25bfbc07b2d7134ad7d700a886fcd3a99f822f3d7a615d03a217761ba117a67194069dd614fd3a6c3e711fae51172e56032566f9eda875088caaf9d5a082f2285f5ab33117b5b6f5af2c1254e20cc710f59fede2bb1cfa9f86ca83a8f04194fb8300e22cba368e9886388c5d4b868648a7a21a5dde2ffa720388b4d40b44a63be63a3ae0082a569ba1b8829d07a8d9b383c1b112a666cad768a053b534926768837cb5dcc2286543493af91e75be416b9a1c22f50645c902cc9194f42800598f688a992bfc048f4626cd3416c34b307a007bd4959075e095fd6e2d585a968cd687ab075a8a45146ba7cdf5df4e54c83ab383d4a68d065805ff7261d4626adbc3d843470fed256819d17e569577ce68a7895a4e37ce9715a7eaa1f25c0a0239510a7a566d4a80dbb9b69eb608877bc217fc5d06fae05fef7e077e0e842821c899f310a9cab9bb057ee37f1c38b9d090fdc281f720401c3750adf941e107c0a20a9eaa0dd9931be6f4415026b7f1ab738a3733d2ccccfcdfc0b879c096fe22448b5b856738903b553837083ab06c8b3f00fe9f17a293f41ca6ed3fd5bafcb03311928d80330217e54331c5761ed1a4e735c457a544ffd829fa461a4cb6223c61d3d2bb06a55833271dda29433fb3e5da3502fe3cc0a0e18036d1917c2f2e56cd0ba121bbfb8b2f8b5d468c0e172b8367575514ec556d4843713a7ced5a9ad98415ee2eb95352494e5c5677cc7a974992bb868c3bd12d8973ec9185452a3025d2628ed1f6d633586826f423d3f08ed938b6776ec4ee6d4a2e152c032e68a457012a4db9ab99245e4246fd0b045d9d60aa3ef217bc89fc2ea4f007ba5948f51429fb10853766dcde2663b6b36ec02a4d270434103235771a241bf23aa5d8d448cd66127b092ef4120d87b759868eb326f1e11a678239e75452cd01b08992e30ec501174be5abc4edf83fd8a493fcd83805a12892e857c0608d7b29f5b97afc7434c73ef72c65911d5f6767ab75a70d61be36daa18f15c33f1b46b587f185a72e2426aabd9b2eaec28e247fb8e7e777bcc29d02d8c068ce5c73167cc99176a62f9e52307537ff2c3d3c3c85425ff7fe563b3868761ab238d4060dd30497353fc16bc7cb999f7f8efe7b90d96d0c037837e8e88e103f1e64dd4bbcc7112fb85308be8dacaf7c1a7d1757484d0832f20440d78f5ad3df97364f74439087353238ecbcb5a163fe7eb7d438d2503684862a9339c97e7a4d4eb4443103b9b6e42cadb8ca915d1ed10473a85d2c80c572d8e5fd4cedb5a6f2fec1583fff552db41e3c838cac5f7753374587726b8908ef82b8d5c375b38b59d40c6e948bcfc5326f2dfdb28369a4f15e34fd26b8cfe9f74d8407c8160974cec92f5ac806423e08b64bc487073618c48970fb752bd84f9aa0e20cb4486a70d36a8ec5e1b6e8aff462a5666b24834e70cfc85b441d9ff702ddd0ad0a21723b1c083d94e6ff4a32c7fac713bbc8f644942d422958b3ae4b52052a443ae68fdd0682fb77893e9a12197a84b58ff0887be1621116b6ca2f9417433ee99efaa001be62b5106258ac8dfa083f07e6d29d23d559d7f134e7cdb8e559a913a107051267eb0b580016f4d12c4c6748a2796eec2bbd6f158d8c1f13827a03a802a71f740bb978be7388e43ccd33278580a3b7836ff74b873b89954fc91221f5bde535ee86346182d3db46ae60eb4566fd9d4648baffd401e9101c55be6d8896f70c4b5a864f5cc1518e76ef6333dd9d114b029bed9382e996576d376ca364621f7399b4f5c6118876fe863fffc207fe4a4004ac3a59576615f05dec6db2ffb407c268d1aeac0b53fe47607d0279b028fd3b6df03beeeac03a5bdf45d6ad6a5bfbd159bfad3b85e13728761a8177be5d039fefddcaff03a3fead8245faf821c3e7cb510fd31c5a3434ae714920ca7c87344c5b6f61de217f136770ec1bd4854c4793f4815579e08a7d44a3800f0716229912dbe501988c7913f243b450a855811a2282bee930c4fa1d9fad4b95e60a10a8bb5bbf2bf167a30ab57b0ae72b6227bbe7aa07fa12a375f555e9d40191cc0e6160d46ee52604808a6cfdff3f68dc3bb17a8c9a257722e9f19cc027b181b4e3dee729151b7802f8152228ebd7a0d50e000723947ca01ec98f9e7c9d4a86cc3f9de6b5890ad5106339a9d2c3fadb598339fe0604419b02ae0548a03ba0b92eb540236144c7d11cdd014d078ec86e766ce2fb0515557d014c24a3a676de6a537f303126a68b538c74803397684940daaaf9708fe4dac14f74443f688f5d8e93b98ccfb0a1b41045f46a6fd35fc347e903c4f470e0be2dcb1b389458490fe8d396f7d78180a3fb31523f7559491c249dc9f247af4f8feed3182937100032342a8d53a6b0da58dbe4c9f06f711798cd648d836f859ceced34fad9debb37878f6059a63610c4ca27988438819ddd749eeb01a5fb9831ee1f468075375a35b78bfef0bdd401a459c4b706f5df376d6d6ff914ef7a812c3caf004cfac2f6213ecbec548f2258c06973efd0142884178931ee9b03dd624c1899466b76074b172030e8c4775268e9df7bd951a1e23fc76a0399c9210d434fedf33e762c5373a30d7c9dabc6f4e3e4657893bf435cd4dac85a036a9059d624e31fd69465cb91f4f134e6a0ef3e97d7cc0c8598e7d228f00fa76d309762a66efa8e58ee4ef266b8b21174be415d952b312e10d6e4674898e1cf906d80cf77c452b27a14eca6d2d94c6f5fb35cd0bac568b9fd68da404ce3675e512fced6cf7eab2998d114d8bc0a84a9f087be1a88bae6b3594f66c598c8760678132130ebbe746bdaa59919c445f48f39757e71acd320feaa25c68e6e75e5e92b4aee84a84c67d9961fc8eb1ec3a347b94a5bbe75d0bdb58edb201735a6b953af03a6e7cbae7c6a1025979e1921d6326e42bb0fba02e37f5c2b88fbd202a731070536237f4e1db96688f8ccfb1b41958efb2cab993a63cfb22e5c572f768003ff3e08c16f352a9ca56dcad829091edc68d8328acbc11471aed938e3b4c3282024d2eae42ab1c1664394d35bced12b4adb07b71d9a985264c392215bd1f5498c7a8a02863f12c99d903bcff43c749e3d23c18e937ff48f749d0fdd458541832bcd06f4d0548d15c1fd711bd96b8e8b26e665c7dd47eade80396157386ee8f6592ebf7431ec13db9c07dae8171809d7111c27f8495ec4a39fd240408ebda62a75c3d91f32b4228d94370068c8a8f45a87b2d126b69924db8ee7894c7bc4e3e2c7660f6f0e66907046a1958ca8fc6e9abfca1c87bdd4ce6ba7671ae2ed05e009f36aaf90a42f81f12f5d2e7091488844aa9c15434b0212f1752655315560c17e88e764a06c75df4c6298078785e7f80485bb7b7e6fc76272f9e0a136bce5c19be1c2e7843b68a089901aa583fc1323b664f3810a6b2fc4999e220645c120a6a6687b6fd3f8ddb24b3743f979f783a1edf94955f847f7f5fcc690b539492cc9a5b105a3434f1cb3abf84663b4672205aa1e0114d48d731cf45d4a1336c4ba50d5fd8960228d9d0ded26d307fef38135dbb1077d200a70a86ef4d1ca51317cce4cc566a88a5015c0021bd35422be272a0a4338736114e4b7b6a1059036b48e525ef018ae0d4dadc35fbc3931f99b36f4b21b7adcf1c2ed52b445cbd569d9a99878a937009d7f90f8a157bec7df466d3fb8e0073d03978c99de8133c5c23c097e8fe2be40a946bb0800d822435904e811eca1f3f6ad1470dd7f35363b95ebaab44b68f651f60ac5a96453465846ce7d3c7083cf1633aeeed01228cb1fbee83a6dc788c0233bdc71013bf605c37c3cb55259258ff038e88d4b7fed663661374a9f1873930af777f1955887c7761213ebf0e25822ac6778bd179e4b1a252b0deb3f0098c04323500203eded723c662775ad22bcefa66ca693e2a529024f5d0745a8e63a5fe9149dc9693670680eca2c0f66b99a756a041b306cbb63ee216be8a9872654bf445280660a08252193ad8fa1d99624d9bcd30706d22d957dd401d62c97aa29d11011b03fa2a130c6fb29f20f541f2b7d1e0ee9c8a7a86e7596932d545e798ea7f9a000da078ca5208b03c82855bd916d8d0e1fb1d3c1e52ceaa7da8650eb4f975f550299ac5910e998f9a35191099baba9d57d2867ec0449472be8b3f1ab872ed958579a43257d35ac58a97d892a86106e0f754a3208700f9050fa310d719a07ff8877f38e739aff99d151cd5edebfde8ce51c404a692acdd0942a6ad9caba2e5489f6786f6289906f307c919efc2b944e3fd3a7701bec18279f21d06cf00891a35f24bb2e2dc6a1859311ed3ddb1a8f8a5427999f55437436d607b5cf52499175a9467c5352cb1177442449668a371fd46379ad3f8c1acb94fa10a1b3de6e6f6c2033bf18ca5cf6e2bef8a6c9058c3203a29b2d9b99f7f751e37c504b50df1fb983abbaf0bdc96a54e52fb3b1d14fd850a61fe34ad1cde2f07adc507c5d615379f1b72833f5a41224cc6b5d43a32fe324d075dd40d26058f0256b8687f81419623d66578a2ec112a90a12d4cbbc8800f8c06a5bf3d81c5da549b58e731b7f5037af7b54030941cb6223ba9f3f78a6331a792bed5f528244c58644e0e1909c13e4a05a62fc8080c94f53a05e9083fac51f6cef47d1a7d8f7d792b7188008b1b1c11955477729d55038b951d6ee2e59f2e964bf4eb1ee47a4cec04be04e71a104d53a726b48ffbfbe71c76af40ed9ad9a840539f64155887c3ce9999ce11f4e040d3ec220155089c492c4e7acc3498e5660c94b67a6f7a5b54cf57371fba7b3536c456d32eae568e208d300c017773bfd85bc982fb577a1db88c1b371727d09a944f04db70903cd83779e41704b9873b2e69a5f5ed584791de0f1aa3a173181e2ae6a2d2181af62d34e31478e9a7e98c5fa5c5fdd3197b676468b95ee683019283cd74ef452896ed0b408e668447c92bf3083ee2034a802dacdf15d5d4014564d07f9891bf2f04800b275490c305f1af1b0c8b53941277fa8d4b94f076d1b6043c7f5e5f9999df89d1b302ac251767d07d8ffd66734f31c60f9bc3a04f1b70783fd813a37b8a2df93acbed427bffe88b5b6f7967bcb949294297005cb05f0051d4ece0dee291ad751aeeb76b8f295c40ea757a587a2baba28ba9754c92a159b4d31c96df33a4dfe8ba371d37cba49377fe27bd2aeb3d4ca8eeb28d7c51d5e9d775d17ddbdbddbf8bbaeeb883a8c53f24ca56a7dc59e614acf4fe7ee55c98b87fd7f7ce8f2c4e1629c0f072455d27235f0e91627e38aa4aa065cc359c12a1f8f5f98c1b35c6769d86e3ccbc9bac519f5aa48d67049526505d7705cb04a1ac51a78256d07c61dd886b49c51b7585c92ac892f8e0bae893b38a32b6de7451ef104341205bc4ac1a1dd33ed5ab077c3f9ffae777bfdc7d7ffe36c379e8d3b70acc19bda346961b869b6e3b4ff89af89f372e32b95aab5bf5388787998d2d3bda4089c3a71e3c1c6e118b6dde6eeefeeeeceff1615ffffa5bbdb96dba1944915f790ae75bb6b21734c47772ddc05b24380ba71db628c42984cb9054599acc1e9eeeeeeeeeeeeeef68f31c6ffcec2bb249616bc64d48dde9ad6ddb0ea6d5c740ce3f2dc354d93c9598ac832d0adb5d61a43d71f5d59758074a4aa7db420ef0528c748fb48fe48b626ac5746b266b33c3d3eb1093465d307cb9d42dda23cb49483f2c0bad5752157d2cec093430f0f1f1f5e9009e4093165dc167649426ab42ecaa369b3b442afa0719ca8dbf252ae2593352d934888f25c69298fac1142b6da47eb7c105c4ac677afe709d736aa6d59d0d662b413b81f22f5064377e0feea9f524a893c8cdaa59b46e4afc34cbeeca298ade3eab89472cefd46b98d46e19276b8f295c887a257b3f451682fa92abdac468ee3382afee70a3ae4fda95d8e6e5468dab4ee41bea343ca583b167036529582549d78aacbe6b224c91a14e52c0ddb4d675d15474ed97432817a256b269154c9de82fb0bb621ed046a275cd33f6095ec2d6ad076706b42b148576a38dc09458b1aad6b42b91c05e0f219fc2569a486025ec92f3825a9a56da49640c85ac308dcb7acbd5ab0574be2562cf0a516ead590ba25b90ddc47923592556ee5747de07e0b7ebbfe825b9d95b6b7e02ee54abd6b9a6986515e11dcb711c4d96eb8907b45c16d8a1aadcc41b63ae6c337de02f190f9302262c3721ba95bda4bae5ef205843a2355d2b258b16ed1995c494b0404457674250542d1a55d1869afee90ca34f9d224678356e238ed45ca5501989b4238638303a0db4e925c4722091048a2caacba38a642015838c04f5753b6bb9ce40728d71ba2ca8648e6965e37e043a2ba6ca3d7b08d8154dfbfd8dd56dede7ee852397e0a72b923524a29ade8342436df8afc8b63a4942c3c1dd946298574110097532d63aa5b2cc962c2d461e418d9318cc3b1dbe1575bc6ff51f0f7523d59dd4ac1aa6e87365dd4fb265c6eae51843a23579235a4b68d766eb24626ce207278637bd97e0c37ed67933dffe2d0631c23ed86447b52266b9e6ff4cb8d56ce644b12114181727404841b65179265517d11dcf61ebe01b6f4e826978e98e33dffffffffffffffffffffffffffffffffffffffffffffffffffffffdedfddfdef1a1235dafeffd79488b29ddf6ed34397bbb534dcbd795c8816e1f98cd6d2f818a3f7b669ac624bab78ebedeeb40abe1f769bac61e1699ecb9c8c85cb40d870c71bf8e083542131efc0423ec4626cd1c174628ecc7575776f45180032869999b9bbbbb951603ee14d983fe4acb41f7254db0f255c577777733ed022df1112be588dcb3760ae8b9999bd11a6d7ddec24f494dd8ad438dffb638c51de58438d2a3606528db6bbbb636cb1bd9c56667c96657c2ecc2c31509e6bf354aa8539060c944b0fcb2cdd2e1e3333cbf0802e5bf931152eba19ef73fae134c33bc16085c68b8aa551afabbbbbdb6b4a087a9e53752e873827185c577777cfb9b577bbb72235482841e575c7d7e5c8d239a64ff0c184520eb69caa45babb39c6ff046f2b52a3edcfa9eeee8e8217638b53901aadeb792e5bfaa403aa1217b7dddb04b0971dae2bea6c383a57d2580ca31c184f104f5093264d524f9ec02887241f28f6b18ff5ca27c621c54e44c42a8c063bd745578c8843a854ae87d2eee626a7511bada6596abd9483862e8e27c8882c7c6ad3b1a040321ae14b3ae975b76bb196a51c9ba6195104c9dbe164b0ebfa1e5912c748fb1dd183e4131414141414e4ef53ebd9d7430bc49d4f7fcdb1701f9e202f1e3675dc1dcdb5afee76f204c9a24ee33abad915685c37e218a3265c9354af5838c00b2a28a13579d93a909127887982be1a6ab41f0d35c6681e5f96f69554cd3d86ff448d9bf78de09be1dbf1c9f0c550a3fd5e5f14c18b2eb820c2690835daa4962d603411c22908355a20fcc0073d40e2c10e74703aaad16aa1cac284a01c1845c1e20a28272b02e09daa1040b700022035235058a0c0d8d68c97aa5565ed088a9c5091333513648a3ea75bdbc39ef4141d7972655c8999ba654ff0a53dc6bee9538e11252400dcd28f0dc96325d88654428a4026ac542759d31252605752a4cd53601b525b8f51241fe2b21d8aae8c32a5c21fb30cd9953eb5a75baa5e756bab7181553c6a58784196e3051850aacbb2ccc539e19cb04cf6aab6d205ae198155d2ca367e61c500a6d739406f8fc0352f74abf2ea85c0ee6e19b933cdb068aea0ab71630f174de03352a3edc008b2bd3025c3c244903fa9aa749321e5cc8cb528a8a44a258f2498b2dddaf8c8500ec794592a550f6248b1a8e3811ec9c752f8d8b589750fdb1fa3174b5ba7e44a7ece5bd0b85b4dd95ea130535556d557740ce35a41ba51dbb81fbaf2a384b402dbfedb5fee045f2e920a66c870ef68430ac531961b65db0e65d4c84801070ebec1514617d423ae6bf5efd3ad69f9663edf7cd1170da9b1c6a49438b0fd1eee4897f3e448979312834addd9ec8d28e37e0efc383e8728236fd4b4e721575c102e08c7235bcfe373a69d3e5ee7797c0e11c7f3f03dbc0e9fd33fccdb666aa5f0f4e48468964baa417cc0fe0457b2fd1e8ce379441999c2d3add7912b695fe775528cbc1159e34a299252843ac109b9f275b8240af4411ef63e9e47c4f13dde873fc1eba4c43cc8e9157d70fc88eb8a2699fbf8b8ec3a54891e2edbe781715023514676a9073f25a439e79cf4892b2910456224e2a04758465ad904c748eba24a5c693ba4b15311b1d3ab6ff0eed6ada9bdf59efcec608fdc49613df7391c8d71cc86c4a7b0983061c262c2844984b1ec0a875b91875c492b7df070de402b457737ac87ec4adb52c6379ec88d9f477284648bcaa80c4a7551242edac46dc21483a4b20f4d2f3a932b8ae44a4b91c89a169d5199b3a9a3d861797b2c876fb8752547641646aeba05632d8c889e98e3ddbb1c9e63826ffd3ffe9b9cc52996a75b26cbf67b9e88918f3d8c6f74d6c6956c5dc63738cb1d7d62e37b2217d2a28e52cd46fa7259d6b8b458dae15405dfc8c71ec903f54ae676f83e3f6ab49ee57a38f926903172eadbb6efeb81a846cba72635da1814ee54a1d332c8acdbfd09acdb4def24458d964f4335da93508df684831abd1313ef34abd19e9678a71bd4e89d94d4684fb21aedc906355a8ee2f34e505496d3132d30eed62d9622e59d6a00a3e5440317efe4448d33bc13d08b779a010def94c47a271980de0949e89d9a18c13b3101e39d8ed4f04e31124e4bd468b7bbb12e65b2f24e4a98e09d9238c13b1941c13b1589f14e4800c03bfdc878271f96773ac286c77c8375d976365826ad873a71777677777777ef52a9f0ada5e1eefe3eeefd2ffda393a0a7e6ddadcde69cb2fdb559cf3967cf9edd9c737afb12eedc2173ccff7777ef9054d6a36d7c24ead04641658363a49da50bc4cb624d6d1b00c7942e10af0d129b0d84e14a36c852254778a28e4ae518f9489ce0945cf972797825edb6559e21277852848ed4088363529e8cdcb9c9c3f7a69d27040f8c07d6ea84e0e1a9cee3415e0f3592e33ee0cf651b23293ed8325252876c7f0a7e9d83912362ebee342f96da3ae8f7857044cc6eba4f9f3e478040ca8f109c092356bbef876046d6dce4b6d33cf2ec5e7306d9ea72e870023108563a3b977244671991a6d6eecccccccc5d0ab8ed695590f26fbbbc9d2e1fae106788632359ad727b3337c749dd4cbbd9096a6e5590dbf469b539e7b41f458c45e9ff52b814b2631251fe9c433f243b32e13ede030b642df5c42d1568ecaeab4ecd264a4dd96288a32055dc10735437294a503b1bc82a6a8909195413911d4184081104e63d417ae82982cc10440f7a8031a27e53c426d553129f2471a27e45482e766208c28711049103abf47292206a08835002976805902b2293ea97930079720590a0bb417143cb55b15000168a006142c2a0a65c4e02c4880a840524f88a00240551809040faa8d58b2b6a0b163da49686a492243f925c57f522c90f24ea4e921f43aeabca1924f901a4d69ae4070a6aaa7ea9243f469004e683ebaa433d7553d23ef54b2252514b3d4ea4962e278105b144455d4e0223c1953f9525c986aa45eaab2a14808503d494ed8aaaac1acd66b3d96cc6840913264c9870b36f1cdb940c59b35190bf9dce823673bac0ba34286523572eb0ae87305215a4eae1c4ff5f1aa1838728709038c1c112321431801c4c6083155ff4c0210a1a783126a7231e4590a10b26848e7e80ccb04309901738d0e06505caa150c17cd54bb1666c522072ef93917239091050dca68117c3818b9999999979d37cfe4fd79a9999d66e8eb9ce24e57602fa0dd568bf99bbbbb7bbbb83d3dfddbfef64d3ee4e9f74eeee1eba6fdf911a6d77d409333377a3f785b6512ec6d8a4f34aa6949aaa757b3b6b4ac9050ed04dc5050980565be94c2556923be5dc63ae12a31bd73917d53d0cea643c14e8e69534d72c4b6e9a974c1e644ad182a2a4bd756ba34373f3a4cade95f7c61ebdae631762c42a62a1b8c4ab89321654784166c2026e5b5e3d777f03dc6dba645a299bff11083d8cdab41cf48202b845dd6918614d60b208932a2611b681205290acfa795b0ba374772fb510de8e91aaa8b5710a1bbd9bbfd882075138e5abc0b769d35fb5b2600a0665bdc00ce090bfb892b7b892a35c298536fdfb25eb5669dd2debcd5a5794ade0dd18030b1893947f9d8e9a5069a21b87d93f126bc372eeeede319b4b34ce574e5fa434729d572a791d4797f85862c4019f4a2b701786252ca89667f94a2e4820b45a0b9a6dad2546aaa2c2d67b5a41dd0e460c8db5f20161d85097afe40205beefb412c3a59f3addb4d9d555ba6b2aa5b8c8d0628c81291895d5cd4040ddb65aa3b6844c3159191eead2a06ec62c79321411ba28db9f5d2cd5ca823694001dba384a6725cfc01472afaa17c25430704c1253f32adfd80860c338b8c559695918c7b42d976d84758b0b61e0265c110683540555186c06b8a6c319a99aa95e46969c69630b36e56e71960353bd22e22ab3dd361bd60a8441bf0a7c14b5ba6cd32d8e0e6dda7c2f9998f53c59631a61bd929e4a85b012ee4718ba35005e49cb4a22ce50514441773bb4e11869bf246cbaee5293775dd630ea7226190b10d9da7c8f3b88407ce3e5b2cee6c465bba5eaec26b4089439383aeafea943aa5695b537c4000644b0c0e27d6aa95a55d612f98af888f87ab48d725d4f6ca360bce922175337fdd3757747284c58a964b27b781cfd24f133c26ca6c34c87d94c9b4343433fff0f245572f683b7cfbc7bd645d7dddfdf5e58cf42d336bafd88e05fae06bedc0864c76cc76c6868289594f453b37ca3cd94dff4e410191a1aa241a51211a2ed981189724444488c51ce1891de616868e847889fd96c369bcd7eea309b433ff483fbb74f79f43fbe63b664674a9ffd337dc72c06a5aa9262f2baee87b3ac6994eb381d2a437528c5f415290d75f253b063b663b663f6bde01b02859b82114fb8916fc418a3f709f1b9e06bc127e4e3f9763e167c2bf8827c417c40be1f1fec03e2fba1bb873e157c3e3e1fbe147c3d3e9d0f05df093e137c3cbe1ebe127c3c74f7eccbf976f874f848f0e5f0e1f0ddf0d9c0524ba23ef5757a25ade3f85c498bbe47a747e73f9f1e2983b78c4926ac54edee2323114a54ea718b03fa8072361d0ea85b2c0001b180457571441c51932640409e94dab65a5329bb42f72dc140451e031223040505057dcfb75b16e298233ea9ea3112ad8c31cecf833cdc993f3f0c57dad20840303a2a1e4ec573fabea65d9f4e27afe32c4521a17e804a6a39a2a5a725d6d204aba295615d322ed6a572d9546d0172212a99b416d40f5048a8a496235a7a5a9a6889c94025b9a09262a09260a0925049a8a416a02b2d8c0b51c985a8c5aa203596528e16ad8b25032c61296199ae05847da51c6e57aaa89185c69752ee0c7008b71cb8633b10036f35b05d0c68002584224239e14c3297282e452828a828b4b3b5be18a18eb6995c4996ac4617350acd641c83128a386894b08c131411cd922b2d277465cecb247389828a8282424db2ce246b932cc52433c94cb2251ca3843593aaa3ede83e092f672919c06f0baa9443b32ad1dd743b7007c672e04e0d4bc19d177086215a2c0ddbcd0eed8b2fbeb81902031aa8b7b30da362a4a2850ad2ca9095b8e2b3528455d146199654ac31aec46e8ca8590c856a80a6995cbd4903d2e209235943e304ab244d4e920a52b7687858194283b3c2d32d9a18568a74b1e2236b66861093b92e152415247b5d2a4825152415a41e2a48a995d8951646a8080a6a1671b0ae8882fa429a29745d367084584b584a5eac24a423a32222d6ac2564c938860419360d4ce2e1a92d2fb6850377606c07eed480a9215150624c7ad58071b7da4945c6312e4557baecba6ce4b09eb08cb4238036706cbc7a950404a9425a01595058451cc31262cdacb8d2a2a0441c2c254b220aca0c59321264a82e934ca548d6b86d1d97a2ad6b6179210d50afa41f79d2c77a7660493cd840c382b54b799db515a4d101697038c6d2f4e09c4b6303d3c4c0335ca09d23b09f2422847a25ed0b0da3238e61e98007529c64a7528e1d1ba39463c7da181cb8632d057754688038f66686f5a3888312451969699154fd806ba815ac62f1a36e51a33e825bdc0ef7703d52f54d700d67848cbd499c0f158a384c4f4419152bf47224bbb8bd80342d200d129a180aa4f1218a38688eb00cb582c6c82d9235343faca269e24acb1212cd9cc01923a36ed1c4c0ab1d9e49bab06ecdf48057461cdd1e593393839e2b2344f5e3ca1e57f270258d0dd428e7ca5832d6ec898883c58415647aa28634af1723d08863a4e9e5e88b3c3535a3822fb7439025eb95b429208b0864599065d42b69bfd20a32770b594245f505a401d29001d2084de02c819163a2a501d65e45af8319a1a4852f4635bc17f07624d000fdf5ba6930c6e5c09d17dbbd845bd712605c0edca161298d707b7999317fd572bb979719f367b8bf20bd18c9587962a2104301b05d0d7419ef45bbdae6a1502c3f5fb3bfd9d7260b4b0e972297227f9f2e453eff745a595951a92f46d2a69472984a284175b15e8caeb4ac2259239b09b188648dab026df238c554a26991916598b85bf3ea74b8c808f4e87eeb1cb8ff27206ab41e9ddde74ec7ec77b656a493de47856d217687afd2bce9a6c9684e9502ad75e933ddeda6e00e6737b0e9d65c683313c5ccd3f7a9d9ccccd8cc1431332711ff36f859fcb74177f7ff307b1a4aa526b57ddba17603bf65c338d83bd46a60b7ed4e859cac4637d1cbb2864b18a2ba52574a1822d42e7916bcc2d40a191646965ce4eb86b3559e2e05745cb6da06ce4083cd0d0d35bb4d70b314c4b1b9d92c07ce40c376b3591c9b1b6a3bf7f72da42b1c2dcd4e9b5d49b655417660b31ac83754b32a74831748dd8ea5046837032917d06e871478cd828063371e98d6039aad975da20b872f68e1f52235eea06307f8bbf3901881622cc6622cc622a59452d125179a6e9a6f2517de76b8b577a81d5182df38ce6e6f39ca6d94d334d7268fc3dc27cc7dc2dc27c7713bdc141f468e71ab5d8e6e53f88f86c5c60d78000bdd9a40af607800d76480526a350a4e40da882333c035110756495b14777835846d48db816ec51c59531383a044624023837825a3939c213cb0188b3118cfd387c99af9f329b57108b7e80ea59bd62914806ff48c879a4f9190c776223bb2bf3a97ed6a2bdd5daafa49943deb99ec9ef5acbd673df3ee9efdec20346e52ebe9ffce3c9bd989f4ee666666fe28f3d2a0195d6a9135ced4a17357c7bbbba5101d5bd7eb35ef5f2eb5105fafcb29f6888e61da4a295f06758ef8fda173cbc8edf37fccdc67019b33a8940c9bcb69e766060002100083160000200c0a060482c1304d03414bb5031400105d863c6e6038190823498ee328888220888218648c21c4208308320a11510d0006fc95527c1cfca1932aa5647a00254f0843b0651607b8ef5cdad1f8dd8de36ce92b0f9804fe73c0b0e6915a0827cb4b830d4e288c243cde880282a07ad13ec6969e18e131c09fefd9f9f98e4fe831fd1df17e79942f0a2b067bac886d287e82888882318123570b8cfe05bae3cf0a0939cf725e442c73abb42047da94d542edd38e6d0195c98b494e2c9c3f9c5831202c42fd0693fe170f14600381c5304007825108580c0045e0e70c7efd420ccc7e7dfb3c6b6f1c0a50fd909dec21240d98a68fd17dc6361182598c3ac4325e9046d75af161e8d4807500e80a1889ec85bc40d791cd9e3abf17d2d4089f6ab7b4a9787ad2d77004177bbef54d93bb5f023c55938e1d2a87593225ca155f07013cfda95337bccc0a1d751d5fc271bd0302af151adbf19770b8c5042c20bb66c6cedb689e7b1bd896b3ec51be88c9fb4ef764f2162ddf01c6cc44a3b4b04feb192a97daf83da7f9c6c13231b69c9e0d21ade7c2c741100040488b062cb1cf4e46ae8ef6d6b2b84b606547cf5ba91b7711179213735138848730a009ca2a114672287cbc4b67e08431109ef77f6107040793a8208b443cff68d608b6451a1dd0065227cfb87d23568d38a2461926ebd7a0466235fb413bb4342165b1e1010159a7463631b480a6b99b3c35df8b665477114310a8ac8a699bfbf4e7431fc2938bfe01480ab9f39f943282e654c2502f4c4e6ea87446b03a0fee662ebfa28843bd534b085a720a8f5b28a06a2bcd88b011526332be6e235488e4aaf7372a08454dda58b86b5debfca2cecbd3cf0e7e19b2fb330b18163e865be8903e33d920bce73450eb8c4b350315a7752f98f13a289d3d9c16528b8b0dea95a1391fcd8bca5e6f73ff0cbf756c09046dd3900e22c3cd4b18520dcd29b20b04384713360395c7fea3f373050abb99a1f74a951a30b58fc806617fb0a9146f33b2f9ab5bac545f33451a048c3244356763e1d0266a60bc694370385c987f96f0d02c0b5dbe3107fb7fa5562f589df162b3b22c17f38f3c2bee8110ea04ed16016ea2641ea8bfe066f6dc8f61715be3b85ae07019084c6590461a58492c76531b5ebbc514457fe5da9e18e3440844d7c6c36b9705463ecde9d571369e4d45dc701a4caa48176801c023be0d235ba252ec553314ac04cb886f5005c6b6f7ed52512f58000390729bdfb6a03c447e6218ac50991ab380de8a615e943a2348bdf39879d22a23df4028bc8a00d009462bdc21fe152532ba100c16339601f30f0418d63627512126bcaf2a52d5f75bd88f21458e7b29b488372feb1e13a67a99cb07e3863a5e90f401a5ae00bdf5d0050014aa8c03289bc732845e292a0cfad2085288f5080c9b31738f0fc9d9b8e712091198a653be3614c137636a5ddc6d75dded523655f3b15542b50a01db4c84fd1e682bb88076659b7004158a6ea3d7a159bf8cbb3a5569dd190cf8916d2e22ea71d8de0d5b00d2726be84cfb050e186aa838a496b35bb1bafdc57a07b22c990fc7dc495046a9a56dc96c5587019210a38d7656c5d7921f3a6ec01a492385d94307c57575731d4cf6e812a7624099f72b40137fb7d82cb5f09c9ae070bf095218a5e8543cbf0d2b02698e1fa520471005158b03ae2a476935c735db9feafe0b5642a5509c207c2ca8a4f7ea22702c3c0652ff48f0434f9f0f20aba52247a0642f82ee1312841705649d86bd13759fc083e381800f00267735e221a412ecdce7a2191d4861d34ac3ad9c8df35878141a5c96f4f78eee9fefe2250bd2e60d9cf85a61910c7e56b9137f5ef6bd2b6d27d01d2dc4f6e23a2d1e4fe680c0adf4e0fa0e4f59a8d4c2f7e3faad898cfda6c339e3c10f5da2ba7ed55978509f0e02102e863779e3a728bc37a38b551130239ce547487615a3f0409a07363d80022476e8bdd0b53f53f511794b62ba1d4b89f0c06b8a0280416e758c4f151bc8e3a36317755ef3452707f38ad6b80a6768043b6af093ae6f8fc583b6ad00e212a31143168e6ef6d59400dcef18bec04334ff932432b8b2f2198a135dcc6d0061b3eed72d8a3ffbca7210415a9b850bf6f2d2b1ea8a4655b4ab01885fca3e21bd34bbc9e94ae9eb7b32dbb66de7d411e34d05a010f4af2c2890299f408e5e067e62025350c81c06cf631c48ad5d70e53567ce722a23edf14d11facb840cf34a84f6476862b2ff6d75fc3be37df714a105d87ab8a12e920830e613b5c1016f705faaf2c59fce221684aeb6090ad9c83105a819bd3053d202a31b01800d3c23690fb2ecb126059f95a18d4d6b4d9c96e8a24af38b32b4ba03fbdc0ce32bc7224d2b2d35edcd40cadd73ac68b8e3a368c41ba14b2d6ea080b5e755b88bba4016be31a944be3a69f46501611bc8ab132bf44965387a26a6449b3e6d750ae18483c0a7e2531cb42eb4e5a552631427aff65a1a220be858b32836dd86dfa9ef561042f64436b2a1938955f2951229a0c27180c67526280134446cbabcfc8301fe05aa18d6b23c5035124ef17854e982cad178ef12c37d09ee9eb23654c9428b9b01fab58840a1fc0aaa91fe6be65027e6ee764147cce4e32848e201e2721530d3458453a3a90c34389fb769d9ce863f19b8800a304ec02c8708846076696dd014e58999411e10e07e8550f696dd87ec6d94f6cd8c6a272c9ddea0a83687f0e477322577e34f66cf7ee533d703c47acfbe80e747bb9d9d995ccfa360de67f049a7f1e949e8be59448b76918bda4c164a49cd9b8d944f77d2b60d75d09d6fe4c9f8f2fabe3ae1b7f53a8d23e381543b236abcf754f7de5c7011c931a16ee8034b26653d7c4377bd050010fb020f3f83315658da4b1f98dfcb865b7f24c5bbc8021d0f700df55d89da774e4a5370c624468502cc23fc0aa0aad2b2ae9151bfeff844fd4e5c85bc0948ddc47d59eb5b5aa2a469ddc84decadd1ae982c3e51f8f167438ca23af85fed5ce370b5414e5fc1f8c4ab1b29258e5868c5c3da8e555a42dbe10aa374813dff39b69c77543f205cbe0c14ccbaeb9b722989a5894b2c618cab60b1dc9f5eeec9f18904f3be1b85c42423fcb79c0e7fc90492ca0c50fd70f102c69b209b3c08824cbfa294f05941ae29e903918353dfa012bd4ea28ac7f19679a5fbd49c66374773e2f66151969c94fe2af25012d3c53ac0880c2a0768229b11af349e8e169b84c2df30f47fb12428f58c27846f518f2f8e05fb810b483d58b4d1b0996e9f9805c7be1353e36be3302e218403a4b5a4fc614348839bcb91b9f0d5a4319ee7b4cd2d6f14108657b7959d0b185575bed5160ab42dcb9d3fe1e472099f38a9e807c314ba297c53dc568b5e38f8f19f23aaf4eb7c633796af6f67ffc8d7f70eb1ec6776e9d541bfbbd821a70311fb3d0be2e56b70cb0beb2a85f0380162e3413902a6215e1466c657efc6b539040fe2db8f30a89b1a51a1bdb8d0a409c5ccc4ea6ac510a1177cdd2b33e101c7c9e85cbca830c80d83cee910c6c147c578f6075ce59a2f419229e4a468d0fe3b351127f73f2ee32968e9efe434e0c788ea20702bf494242cd5597a7092b81be1ab38651186780786515e5be56e98f2a8fe5bb14ad179e876b5c7288ab31aaafbe2ec87ccd1e374bd0a42ba3bdb60714dd6e0c89773ec442af79c7e5a363c8d0a6046e4f4119200aeaa79c1e798285eab6bef8524877a92db94c51c31b55a891c7a9623a6d987a191e6e20efaec7099d7f397a4ce922c876d3bb726860566a9052694ed02936594c3b824ba7aa7815106a89dfc2b40d2ffd6715cba412c620f6cc24e98cc9a4ab478550bf47358fe338a6b753c8a8aa63a9eaea8eac88289e1e7987db4c90e7b29a9b477642cfeb2aa56980a15bae0b4a5f202a3e7b570e63ace12e551a7cc761ec3948294a31dc53d06f55a19025a14d8a4b4259811fc6194bbb78a2e3083841250f042da2ab0bd375052c448de45c1f2dce770e73dc3dfeace028077d366f7a05022f66a8f69048a944c48d054ec16276c41e925f1958236f29a0de6111317e47470ae6cd981477158921b0a2c90d2dea1f8f8923738a7b0fc7542a289885f71ee7f6e92c260ddf000f69adcd1b4cb0ed486cbe5dbc4551de3e088a823aeb19a2b7341a7809edbc318c39816c0229d15af810c046762b521132492de3c83636a20baf30da4a7f64986d11a6320dc7102e99f0b65ec9e91731f3177e4bc46cc51457cab44da2aaa0b21e2a14a50bc6a2a65aac6c7a4f8d9786f78dd52c55664416cf651a1ab9bcccaf1e30133f2a7d6c4eb0bb9fbaa2cbeee13179142b9b9af36170a303c4677f47843cf002f7f31d23723ac09335c2286d7d4cf54990b2b2c2c71ac0577bf00bf6fbfb66d21bac5d75c3a83283c754b2ad8c2a825737e93ea19a601dbd91de472e08cb90c1e34959acf5f4331d1f82e7691527dd7ec46504789caf74c7ff03a97fbd9420a85ae16bcd0a9b1d3ded47f8024c0020375b05970e4d2cfd2cdd8ced9aaa7514f8350721977a16fd83dd00d024cb213952a6ff1476da030ecd696899b44bcb763e8b5a958d0b5bf16798be43fb19a79322b09f2104fa52840b4a3006665669f6a3f4276e9568c042a1e7646cfcc489b183918e519d5f1b8e58cac95fc7488bb8a4be81e429481cba776ddb69e195300ed90c7d5e82d02fa24209d19615f4f75d69714b5676e81db5bf06c0c8c06f1a11508bfc3208e4e5506d755a15576827b84ee7035f8dfb15c14b1b030c417d7db7f60fd494a55b64ce7b5b0de3aa635e43a11cee4747a976688bf8012aad3848398e8bc592a72dc05a272d8daadd490729706ffaa9794e8ef0193457b1e90dcdc9cb17f7b4265ad8e146a41777fa9410fbd21a19ca474c6a79fc012408b711324d9c1424f74b79d23ad260d6edb09f72c04221bba5b9a5ae546b7ddcf530db26f60555fcdb91b048d6321bf43e15bdb862dd8d63e52be1158e86b065e1b402a472221c3d6997b14bbda2b0d984eb97d8b94611e5080f998bf3f21aebcb9d559a27db5704064df9e4babca91ceebec256e38b630c04ffac9f219dcffc836283d6580381c05a56d8264ea35f1656d9d6ac65fb6019ba960aea4cf16dd269082a26b2b575a389ff3324a367bde12b5c0140e6aaa263c19dacf5669c1c6cfa286e530111585a932628291be8e0ec6ea06b97bf5697327310d8e208f289ea9b4d12d643b8145a3239f337e5b527d7c5ec29b6b5b26e74128ae544b31721fd7e334d665d4b28e1fb730ba6e670a7c17d0407b8066e6d6b410951f5ceebfc9b2cddfc308bbb44f3859d3840591ee535b6741d98b0c09644512951397ce95a07a398ac50e8b6cdc56a5f58ffa89d07120d398d12d1c35c94baba9366121b65145f7d81c7432a8c2e3a5325966084674ede8643a3bc4b830f8e9ece81c9ebe6a2083fcd8cb17c1ae5e8b69f8c8ed6b9af1652774fc61fa25d64c979ef64f66813b5405c4d7f608c582b7e283f0b178ae92a621d231c2c081dbf2bed7aa2c188cbdc64e34fe1f0b03711d4d9b86a47c5a8ee4e62bbf3923204553048cafb37e374b3d910fc5ef31d94c8fcc04c5f504f360d57b0190449a9098082a58094e7a704a321322e226d2e01aa4b6638c5ff53e9cfd75f7f51d925c9429b706322ca2b4a4166842a8d03873cb04b8966d8027ac9a2326302a1c4202017d93d60a19fa81b6cb997d0763c79bd200bf3c402425fe0ba0790570829897227d8f399e27f12a27cb9df21ffbcc7271d13fe6a3058311641798c21ae5dc10d87678f89c8d43859022fb36a0ae3d8de1cbaf49f502d98be9e06928e7cac0e1142c5cf2a5932ccf2effd762001760618b8a5ce0a3f6d09965c52ff339b73f762e7ee1ac45259c5d4d33ca93812fa96534c7071527209e2597154c3dea3c9fd321fb9ce267be13100a5cc03c0f848690b7df7a69586c7c23fd0e6032cb52cabf0ddb251c42b12a8448bc6cab69cce121af090e906acb5255d3473a4715f96d194b80c55ff72ad900762a6143a39a6e6ddfc583090c90cab13cefa352a98fa793b2f2b58da598abea4678384b5a6c8f12670d88a6042df03e793775a30e76d8a14c153e5dab2f9a84c21ab33b72b375b76bf05a29e91feca0a2fa7cd208e8205a581145492c7fdcb128f79edb4585d01602f940218acfb63681919a1bcf4c7bf917c80ce723d19b18a66ad042cd11c6db559c850fdfac7b71db9f47643031d7d23980c1881ea4e260b84c2b80ba0367565e377de4c2f9c2c10255f8be1fa72819f55443ac77ff1066ad0b6d3014ee2a5afa36393a2c9dc86e70d061767e454b05c8d5145d9cb0206ccb155b37e2d34471113602cba2aa073daad6ad6139f5490ea6908b585f5e2c898006a9fa8f8ff4645e7907a1d18d4d12d80652d926f5ef4b592efb2454c795c7f577a4c7c33693c8d1ac7a15ef1f8baa2aa3b0b8246502eafc742fafd37cbb6ea23fe2bb15983efe850f8950d8eb80f5b99cfed027ba4dc3daebf107ccdbaee84c9aa10209ec25bb3713c53605da4a7ea704e22bd4fc94656d18f3965d371b81c922bbfbe2a1e40819275a41d063d09310414a04477a287ddadad4c38f90f5372989b8ee52ce64c6c111215c7c8e33e064f420f245d2191cfe287539e4cb42d43968ad703ac5c980eb412f66cce0eced84edbe20de10e2975a112c3fa80ebecf6adccceffc4330bc8acf9a58e250da2413d21b2795a9d8d6a5fc52ca44a41878931b53ac32ecd6d55fe6e3b50493f8584633cb88d61ef2be59dc555aba45279f94652b281569ab44ce9b272d2ed92c6937f7fe96111d4cd062edcc3947fbee4aa0f60b0cab0264429bc27b1ec36599264df7c4408a40a76294374c2e2c2f6af202fda9db222852949333273ec1f9868aae2be056823a3c1c8069c445e6745b6aea4ee0bea523e1587a4869be5d49e2927e7b7a73a8b02f2d0e4eaedf38352a14a3e6dd822aaf10c64eeb93ca77e4b6e29596a90b5d3a96c19dab5f65b365e84fe70c0b2f1d53b7f80875e507f34351dd9a323a34053caf8d1be2b21d120a62337a4b4d1b019e0a853e3f3d22a97a10d794e4e3e9814dac874541e2d3dfe9b9c6ec62a5ebce782fe6a69793bc6ddfe9195a082d17080d8842aec95f0969a870380a1278b8f4b340de30a56a2a594fce59e5c8a097ce71433a8bda89f0f8bee1431e8489a5ce016a0da78c82680a45238cc17754aa2a13d6ecb58616b45a5323c020f3caadbe710931d17a48664d6dda35a254810c0557999f47fbaec34990a84a3ce1919ca26aa825f6f9ba3bed912e6de27a2e4557327e25a5a3201b15e37fa9a07bb879ea6c9a60977260e359bf3c4672aa215c9cab124dc4715a8aee7458a46fa8cd514b711929fe95c3d2bbedd9ed4c5fae5b8ca927a8640873e5c63addb17e7464a832738cad9f88ba6450958938e37c14027b4b0480ad7a92000abaa6e820ace299d8a226b9dacd366dba9b117f511e3d4c6432ad963313af25737402a04df29af183f92eaff7cbbe4d93548df47ebdeb6fbe300bb86b7443ac98a8063c3bd9d042e8cd5cb0a5d39d068b8e700a6ab46822778fcc2914fbfa8681f45eae3aa35184ec4b8915895447709fbe6c184ae766ceaea9d90a9688ef9629d0ec49ef6bb5b286e166f92904d2d94b47d508871ec5e8977560f1f5bfbac04c49ed3ed2648c12101de136a8ee1bdbc6bb6a6aeb040ae2a1e728e5f2e408ba1bc4ffb61c47b65cfcafdca7e8740bae094714ebf196f793a5a6b1e6d03be87492863642b751bd41bc6e444107afc1aadc08098967f1ff66ff78b0887ac768b51829c6a148a480f1c17cdb688e26d4cc59a90a5803da3c0afe7f2e20b6b8c5fc95ddb869436da02b7e91e683dd7b3242291d3c588e1971916abd19daa44c42e76eec43cfd0434a81a6093e89024f039af88b146807d24a6a15f5b2fe0204ca4861ccb9b113e3f4ffbd6da765cdb19d1e3aef5a8c3c685d917634016b0a9b27ad0d6f44e0e1dcb70c9ca2796bb50c6adc99b2a892e0ef7f355e10f56740c276590638b3d65e2a63b55016aac2d0e045706ff940a923c642084fc8642b547c558eaab85703f2f013ec86f8a6447b8a11fe4a65aaf8274e0c56744cacf10972bd52461398a95275c67eb21e51cc0ff5b40f2d07434ef513bcfc7dd3e3f5a39586ba5bb9103a79f28b89549ac619dc6d4c326812ae6f5b5bb265b455b1aa4e3d86238012e8fb5631850b26e3e2a40b12af7015bb5ac2accb5078c360385be42d85f0eb616ab0ed29a2b0e62bf35c799ffa4d3a8b0781187a8d4d06196feab4e4f54dd3e24f885647430be6a8078ca9b77ca90aa4f87357033fc5a08fc362ef144820418444f8794968346d615c8b3155b07bf7010f718ca050ecf3cb5154142836f69b081d8b2ea3f2b4f48b9822126efe0a24a8a2d5c16b6fae0b6b902991b101d455c69d16babf43b3160e640af25103542ab11c11dba87548d4da4817eb28830a8a2fcdd88c21c8665d334869f98e7465e865832ee718665354f6fd8874d08f88f6aafff15da03b95d33fc585d296de7b37957a4600d36fb6a3cf34454b06bc11b9fd952b7e5606a7d008d28c6ca1eecca68d90dac526d7a59988117c140957ea76d60162b6ef00551324b52e7b06216edfa920b30b32ef2a67bc5a0e19ae1929ef8b1613db73e545623053cba721722c4eae1e39c5285bd729e5e7b9a95ccc051d37ec5b24563d69e46e036f5e255b0528eea08447fa6a1b240fc6ffb7ca936b888c7cfb1afa64c9cf5d994fb4ef113b13c8b421d3037318c687515d00b4a6a7fd06807224a9eb6235cb720a1a019ffe9f1c143c401d3803d9ca15ac1c953e07fa77802d04c01853a7da2f2e796ea04338766d90b335c2d1e6208c9939b0ea4400be823570fe9d3c86dc94e4e595e5d2e5394d6ff54cc27f31c7cfeddfc0fb301ccfcf9943c5a836349680c05b7ec531e54074ee30251cb1a2883860ad8d9c4106cd8cc84d1b874033a3fa1ca7ae02b8d46e0c4503738c0747a076ac351a6e0440be565732587eb552fed23248e33c72163e575a46bdacec8a55d27f6bf107536d63edbd7e939511e0f17a27ff037e00cd14aa48093658a337bc080e227c8858d7673e8037c1536ae0db14bb7378d0c9ba40b5b182270ac3014094308eeda526c6a187aea8ff5284e94814071ba13486d6ef02d2dc9514d22d4875ea4394a8daaa0ab37c2046ff378a844349ba619fa67cadd8868a0921602564031d09bb5ebea8cdfd03ddcf5275378f6a5794635853ac7d7569cadc7d4c7f301240bc365ca40f3fc5a8b9e9392fce9ec10e1dd8284bea9355083fbbb873db1171ed59a3a52ef69c240ffc950bba918a4a6710801c0592df1c8fac3fad66e6b76a91a25199e142e0cf0592c3289cf07637b63879f7eae6d44a67b2144632c0c4d2b2208c14da16bb7a67f68671eb47041e672934b4ac8db367f5b08e35526d49eeed700ed516b9c1192cb191dc784150b528f05c0dc3db6ee010f2bfb1c45e12a6179ac1ba5745c740e19879090d95f02f51ba9a9429e84fe54a6f077bd24c8ad896967e43c632b21029a8f7c4ed0a5cb67856a0cbe37d6989a5a594c8a4121256460d0b20d4711bd3f88493de605172612cc362e0909ee206fb49443e21cee37fac62060ad65ca5850499731eb5f63ff55ff65cd2166149ba536357250373ea52e5d5495dc837114495dcfaed54094ebe25653359e02bcd64d56a52655e17e40b1c3325b4d7c080ab26cf19d6b69ad8e2aa14172f49a0a95c08cab47d9b98519dd274fc1350aa56f56e2208b1806cd253a14d89dd2de01ea9e43e8ceb8ba08490371af6491847bac5e1da763dac6fecd97d196f57090aca8ca112650ecba0b54f39815d1f925dec5d28c6d505f5589f08e4a2298dcec335b8ca179fcecfd5cf0a60d985d2f5e1f143bf03f074b08eb6f83e22ae879f2a26b95e322893c9726ed3fcd23a232efa9e9981ceabdce59fc193c8d36d5f33c252413a62dab828e3e81127f19ea5752e242b51ce04a8f09310045a61f1e8fb145c8df154fc04d4e986b9b70514ce8931ee5d86e23dc90b4358ed27bc553656b641a1a1f2241aaec1e4ba8595c727ed71d80192bf8f5392be68cdcb126969dac9c726b7a5d32b844faded3eafb24ba1390c535c76beb094eadfd3eb7c0336087011c13eb4f084bda017c8a9b2521d884e5a229a4885843a690debb8e46f41355462e864663fab89f5051c9f557e7472fdce2a3dfdfb962250844b4e51d7eb870e9d95a23a4a73b03b05314e4615bc0b8d17f44874d9ab156f8725b73336a387ef4e5b1f34c95fe0158e4c01e099e1d4a4bf0785e4fcb40157ac40d35b075cfcf3bfe79423b78f2b192d9229b045e122ed40ee1d5652897f75c1636dfc5a3d7cfdcdeec5f6edd4bfa6edf30fdc2fc8714fa2138a774a3f822b32b950297c02ad87917c283c115d258093cc79bc172cdb0f3602f65b52aa317f8b102af09aca2d9445a83083785acb460743ecc467a01c911086f70bdfcd139102fa2e46ac8d4f9dea40cd1cf050bac96dd9f05db7811c935810af9ccefdda08edbd0505b7102c2717daa89b21514e9a1674634375ad1e756c0e23427154e81ff2ecc5637bbe436f2a27fe80a68654eaf925625bbd59ea555a2d2709289de1998fbb6c21029f0d8caf8e9c663665cf37917e7ffe5296a139a4d412d96880f81b784225d754cfea44e29bf9af69c4b4fec927c0b62925db3c4d00ef63cfda3cc535987caed4d366726f2485058e6f69bc7aaaab9143fcfc33013fad601cab8d5cc09123a10a7072213119790e7311ab480f486ff9be7e9cbc51714c7befd8d2c05a5a50cab2a8bf9c39f5e50d903940c64f575ffaea780aea81801af5a71403ade992e37facd503e84de819c3576e2b0974b25f886fba1ac291114ab6ce9911dfc5f12b805a3769670ba157e35605ecb6a46a82bffed7afad5ffcde57dc23191b34afec357e7aba5ace8fa8673d4ab9ba60214d3e965b66addd6158a2323de11bed2f02f04fe481e16bdf50f9186e0344fc510b4e3b075c8a205a722292a1a798b4cd432202c1cd9d23f78a519e95357476cb7ab4cc0a8818d25ba1a58ef1c01882c5cafcaee589aa890ede905360a13704827d86d028773d228b14cb5def068cd2bf66a71fb2e3c892b486c21eb0eab6e6a763d4a620771d99b41553b33b209369b01e3d76a6f558a5c35d334927a3a94904489757cd7dd4b1b0b1556e6e47778b5b41e6ad382b0fc6c3d5d0ac6a4a294ab4b5975d30f92f8bc2b86893b57da5d0c3d42d266a875264804707e2a5c5ce27eeea7c97b68d15510fcb99294d48eeb1132b8e06ed1bcff987ae5d5ae0658520f83cfcc91a039199ad48a7ae4af9f3fe11ddbd8c078ce5b4b23f07af35ef47297be3678b51439d0f4cb50c3f18b977cb98f04b7453c60cb2f7c488cf813a3d7e08644f0db8adfd3e87a160edd62ce5198565d38cbf1de656119a69b485fbd413bfe453e5dbcc08e4c889d3a9464a5460323fe25b2f550d2badc0f0982c6d85973aa11fd6ec44ca65a7f608dd61de09f2dc5bcb92c65b6bab149b476da38b438c5ede4b73deaec433a15de2c54fc421114216448d8f00170ad1fe365c56f57198abca44d184d4022c227d1ecae7172f984e8cf1017dfd4e4c27be1ce96c0d12b23bf1c8cab6f87d4cff74965b451162a9a2751e3662b962529fbe8e91209c1bb31a5f42dd429f70e31f5f5c77be6d2163d05a8d7b7cfb57d94daa47312de81ca1dc5fd1f7cbbb1e84899e735c524d9add72e5b4e8c451c94a67bed192ac731fd9fdfea458a7e30884e26914f96682b01735b113747d2c6b17480374f7dfaeb700804a0e8f176140ef6c20a1cc3abf74f0e41f9513c37d3c9c89541fdc022bab45537708184deee6950afd9a0bcb90b92eb387fa4850df1c9c87bac92a708936386aeedf436e5d7552404d3a48602d50cd0d1d303b845cfde3175cd85f4090871e4ab4b8ecbfb557455221ea5dc1c7cbd3c1a94026b9945137a15a7569f3b3d0c0d01cf507c3f5e769506b9453a12ed2f4fb64d572dcddc8d10f6966dae136b5e1600d2ca0071e031ea7f69c1d021d404ab08829485c834b97443ec4925a9ce69a38c24912058dd6799b82f8fe1cc28ca816c937fe93846ebaaf693da10b9d89ab520b5c1892898b65821fe2d3e44618c83f8cdd55a4711fe7eafc1307ff6b01845160c120d2574185c232fc1c1c3c0ec228aabab7417a561b2646fb29dd506f83d6e349f169cbf13f6f7e05d805ac6688b35454ac0ac0fee7b5fc9bf40499f51bcde1b8e6786fdd80fc6c4008c87152bf76486e2f3a2b2314b22fde68ddb7cb9994b72985042225811a1f48b1192f21852b7176c8cd6f7d57e70a4ad7215632df1f8e0dd9af0f915f5c7fa3e428d5745e1292d31969203703379989233e8b44f542c73ffc18ba9343ef0cd6283e6aeadb215d20688e77aa01c3f4024482214c24a81ecb503cc57aa10e94851cb5f605be62cf8c510f170e81a88a40264c9d283f528566b264a655196122de27f8b80ceb8f1c399b09143674bc21a047c83666ef475dbb99e7af24bf1cc3f38e242b00b6622c81e2a368cd35ecc14d81937464ad6804750e30fbdddc107488f63fa53802bde833b140ae65d7775dfd7ff9d13e88c0c67b5695cd0c89820eeb94ca69f825c41761394017ae7b37203250cea8892afe953904908c841b84fb8146ded12692ea7de1f7b482a6c2cadc0ee1fe9693d1cdd011471d64fa3a01dec690498bb1d6b882b5e5a5ef6dc089bd504dc3e2474ce9c43bb8bb4a8b8cfe1b1707162469d2657d388f33100e63fbc27772a38acdbbba23a79a5bd51d779e890164e8f42456b2759f80a2ce8963feae6e25082ac11b9a70ca384c20132475ad01139460cb482b0dcf00ecdb6dc38387feccc7179fb48984d1681df67a05a2b3214148a811f31841f3be6b1e03a2ce232df3f0fe402858514663f0b08980f6c80d278ab9253099c6f60dd585b7c01d73a718b6e06bdb2cf83b18e7312d0fe1ad84094042f807e354dbab388ee5bb603700ff196e45bc3d72333574df5b1688292e2162b625ebe6204c53cdbf593a304883ca2c1b911a08ceb3da638ec42c54555811b4b515609ba5a5fdf11c5a55f511606da9f1b4d58cc2df1b0c246844478da87dab912953840ab435b2b89cc8924d2252762a083b16548269e2b45dfb959a2ddce16d3fe02d2dcc61d8ba328c466bc06473ea631fbeca4e64fbccee17ac3c80422b5a7a93f829b2caf1b50315b5aca495a4f059ba2e7185682275bb1b35f7e0884236a4d8e7aa94747fa542dc7323550ee74bbc7cde425c218cb37d20335014820823909f476295e41f30b781fc7cf29d597a6d4ef53efcc8ebe2e7ec4c5363a1a1b9d330705bc0ade260a47ac6d2b2fbb9ad83cff922681b3b2edfa5b3601142c4b35536fe33367161a1e318e86f9fc51236e575ed718addb05695f9cf1b00d1c078a38155225673b0d7aab689b3c46d3ccdadfa91ec2b1af3344e43b3817f392c3411319e6c660ffd65899d6d615f863ccd697452a5be32a47821230dca706aecb6cd859a068e22ae2f83fb957275997aec63ee0d1f12a7d0bbc41057854b8689fc85efd24a91e32fcf8a0ee5fb20df3be03ba9f9ec1fe24047d02caceef800c008e65a46d35a91712b7f4c25734f11a56dd632f90cd2c7b6ebd74c4ae0473a8841520dcc88c27f6c2324808257a76ad04bc79255e7ec770d122786598f8f2da41f68a9c86daa85b033bdf306fff426e287b55fad3b0310f96fb864994918bc900c6c4e5699146fb8a0484694085c0259fbab23f191a60e9a45101558970fa41c3669f12ad1ea8e8eb1cf50391862029dc39eab31d9e8cc035f4fed0fd1d88b9f32ecf8b4ec4618930d8f3242b2d87c6d458dc51ac8f321a006935f3dfe901194f8ad4b0a81c05e66a92aa4ad7cb76cea36e5a4857221fb9c157045cb872355963edc4888396fe718dba0ac1226bccf59bb1c9f7e6fe2c68b7bcfb27970cd1749238448e421c97e9e27ba2161fabb2042ad491f9a735df909d815858cb50ff9efd433fbf6019e9488b7730162a7365dbddd6c6d20b1785a192caee2e658974f8ec1e1d9ba7cd056f6a29c06aef24f7e913158394082858eddd6515602764af8feab436bd8a8341d6018f71d6d30802047323c3a551b079c129dfae36a3e54c736565e9045e95ee34e33aa7119acabfb902cce0f1f3f1314e39c2a74543f83f66750ecfae7087a6f196247181e3d41d06ac1763c0b8511a4f6933fa1a75c96bda06607b4a59c65ad4440b902fe4828b69513398efcdaccd6f8c7d26f78fa4c7c62772b629d7fe067e1a33b9cc2fa50eb4bebb1a0ee3d977e861ce8159b12681be23e6c9b3a58dd57b76fe3127ef7fb56b7779f0ec9c766833917c35872d2c581a38b3291237e0f00d75c5ce012f64392dc8d784eb12200f602ed5b0e54a1be8b5a0da958453a467fcdde958e3a2519bc19895b0e46eb58f4f55698bee96e243ff6268a453615138b1c2b2a5ed7f603a64f6e3da0263e5547c4749c2ca7b63d37c7717542c1c9339956f7ae81f24a94483c8dbb3158ce86c2b9b400934941d3073bc3a843a1f09cd85d57d7146694d4fb87781bd72d6523b7015fbcc52038e0d902e9192d77dbe4600510a01baea65631614d905b56aa3cc609228bfe20d8a2ee3d60db276cbf838a7db9d28da9eb939dfbff79f98b46818f379480d2427d74eed7bd78c3ae89e4e9d083fcaaa3596d69870407215d0c6ce15cdc265e07b6190a35410de11129815004454a9e89fc645e07876911e7d8b24a9ab8ce55daa4bbf4f0b08c1821d4862ad7205e7a5a6f569b410bc8c4b965b85a8ec5d0ab53a98456e240f9238e898981023e039cf73820d53db879ef7beec1e7c7dd23bb30ca1eacff1582219f951a89f46ba8df472a1d02b3ff5fa33c9c348ff1cfc3e258305c99dcba96ad28623f860535c53ef77328d8903c7a0eb41429d2148d0a295d8a521ea408c65b1898b69e947f69c87f14131a2d6541434b14e46fabe8683d054a33108f8d4fc828e53851da66679990013fd7541470e4bb5269a6dcfc26afa8f479cdade8893638f22a938749e0c73ddd9c1518d5d5c14ab105550f9ccee0f6821c5cda278f21a222c33a2a972b24bc7434cc143e97ca7d0edb430a2151cf27650e70e988263c945cfc7d13c0d33bb90b5e7f1c4e90d26a5efc184448f65cdbbb248063eaeec994a1ed588ae9c8ea580cd22791787aaf937ae3c6d49d2aeff406c3a6a77ae1e6fe26d961fbec863c50f53a839ae23948a214890800c93ffe2a1b05bd7b0c0e2e7b18b535315ed0c34f83d05231f3c126748c9634acafa66518e6a780c369ed4da38eedb2b33b8ffaba619e76230ed29e62c6d21e9c2f736a9f0ccb70215ca90e14bbfb1dde2f7518c6f1825039a0daeac027442790f8fb702722493ecf66f146634ef9d9e50ff1bc8b45eef50c7ba6661f0a9cfe039f25ce1705546a9817e331073b4bd1d8d7a83c49e25390205a647e2a01ec9bb747d597ce3990ae3de38c44191347f4a31aa96f32bca739a4760d6d86cf4b7b39fa6a2c90a4779f2aa3160bfc445d2ee8724c1d1201a42728f8bdf1621de056b86e9161cc6913a7cda61c8769ca34f131e51f280d68c9f5e323cfd926d11ea52b244fb5b1379ef7404e2846d3426558dd364f4b638d18864b6cfc13b3321a6a2c5353a0cdd9da2da255c16c537c9c29200a5d1e30388138297412dc1202bda4210a4f0cb15df2407fbacbd1253792da7e4ac4f7a6dd8bded502cdb1098a5f3d0250066da49d6b4f42fc469728c94e089ccd9858d3355cbb0640c0102825db9f98bd8a47500dbd506e45dc8c06a4c2b766965f1c22f8876fd8da38056d703534446bc0c68ac7539f8c0b5392eb3f460d4a6eaeb275352bc5a25acd3b6900df52b8a4544d3c5fa8c236f6626aea81db89d8a9365fecb39e1afea8ae4d29a9d866433c64776da764f66f3e92b8c2019330ffa83f44977625aa704ae2a92752becb498af1d6df625ee6108873b20fa378db86674e1ad170fe68841a66350d45d30cc54a56267d322c15cf302da8124424c2ab9566916ed1cd1cda043606df1f7b961250a46f944b163ca43d076595f022ad2dd0b4b7be7bb192a031337157a7ab295c15591714035798ba27601009e49c290bc2939e5370078eaff1438242bcc85212ac4a77e700f6710993fa5b65d7c02c1eb586aab3962f783a0720fb3a74c46f317c81581646d5a577cda21f758c988e742c1c4eb31858f8714c1831e4b72a38abda0311c8a74e4a7fc9722bb1825b3d42b31c1b29cd310326c7551f000c86ce7ba6db4f8a4a254d5913a7a3cee7ab115095fe86572fc8c779c7645c14565306ff36e650976a24a0dc28b7ecacaaacfd23dab25049cb1f08c715b81b80e6bb2c5060272842924208a13cf2ab3d61519da2901cda0b8ffdc0b8d42d2c707f14d9fb3a5dd79583a80693589bd8ac2cfbedcc291e174802020986c6400612155982c1285a2db95d39f5529c9ebe41eac4fc2afde7d430f9c27301e9f30d5c80975a7ab55cb5dd26e66b32056fedaf18e6ed7bcabcb4acdaa753413b18a50d7ada08ab84dd57cd24cb0e4252ad35107c94591820e6501c49db23ff3057ec424db07f1f5728bb5ea5f03a1c9e71737147db91342db1bd7ff337be6eb942b6f26456ae1f56a1c7b6d47272c55326fa4cde1a8107d564f9a0b3b6bd572798ddd84aa2f748c93d701e4359cb03a887934899e3eda44e4744722ca4da978414e478fa27489d5cf1a4ec50793992e75c0ad2b8ca0a3c88a52417dee44588d8634ad37060533a3005cf1db2752ef697dc76d81a953baf1cd3d7056abafb2a84879764fa559fd3e52407dc907759ff6255cf4ba3ea71d2c437f325277df70faef756ac9cb86b03af39ea96834f113114b568ad840982faf4761f7d1a5e402eee33010157460e71dbdebfdfd8825614d35e7862e8004709e7a85d65f25f9ad5e34a8472727c2fc1e095a13c2e9a7cd6650b81ccfb8516a840f508b43b3936e1656efd343298061329c2622e56917deeb030d114013a74f85678bfb9f520a3438597610eec49dc7ab04fbacd0ba33495b6c8529bb2f0e25fb5fb79cb1c8cec218aa9c52a59a1f056fed507a8b33fd257d10a0cde36ef9487f59a5d8051bca1e8ca63a47ac7a4710c140cbd5703eba6c216ed8f93a8dd16a4aa6fe0575dca106dd69b79663ed01c7d78dbdd71df06a5e0bee8613eb2568e46563995e6392842a84dd06324596e80b6d8acab744c5861eea5463d6c62d05da24ff07124e6454cd8b67c9a89f944bba7b1d1ffb2972c473b25180048240f7707ba0b97dd70f9f31d84645628f0ae813fc77bd330c5fa452212b2b774a333e812f4b18fe6801f7151c786a6975ebf5974bc745902531d07641e8128e3f23bf91d073bbed6c2ffbb45afe022ae55583936738294865da0bddaada6494a1fee7266755421afa9f27518a27066232e7e50a8ff057dceb212ae12b2077b6e554cc22502445f7b99aee2302a0e3924f643958f78e3fd8f47d6bffaea7fc40e61c3e0bdacde1fcb744aa1e1c38b2f6ff200c9d4872400f6356132038219ed4734c958aec4e0d80c0c1fc05676ee305de7941de5d0756fa2ec188260af293366d19cdaef3752a258e39641224e68f600b16c2b4f09063becfba79e1b78a6ea229336deefd10c7896ae4657db6c409b4b914aded0e3d30b9ad0bc72b47512275799bab0e1247ea6aa3a0b7b321ed1ec169740585768c88dc5d62e344b05eb57b0b8f5ccaa157f8dd3a0bef30204a4ea7d22f38044a48e546be37ef888ec56e48ee856eee845e66e46a1f51975eef171b88327ce13314146d020d9ccb9e7a2a64886d772300a265d3909de34c6fb93654379cb951a44428cad208d351977927b90fbf0199ce16fb4ba4080e01ab194fe04526e6ae8f47adfa7f4be6a79eba7a8d5995fd16783f2d5f468190036e0ca12cd4767cb8daa0711928bc3a8854d21a9e988d993670754512eaf4a634222dc8e2115798a12d50792b8d7943ea9becae311fd85a8b391e454251968090f0726e871344ee682749a2011f119434145e1261e0dc2aa53556e6c892218c6c0b74a679aa19b879b149d56b3babc34c97ef53786152ebfd007878accee37e9bc06f43dfede84e37d8089329eb74e2176feadb7018b495baab94e3501401b983029599293c25974c5be4a528e584e010929818230b42fc4cd309aa4a16f6d23a0c20fea0494f144e133d4f3cba6cb127c8388f26b413a211cc3979e239d870ec84e3dc0ffe3e015844c84e9f77d726c7b13354afeeccccb33734e1f5c38f891537b7a8318887781f773de5ddf59b25b0d501ff186d7fbbedd87075424313a32252c3a1a2d51657638306a9a38741d46a0f407b789406efb8b67aca7a93011ca05d2084260c0878023c190036d4870af90b166ff4196a00556021ff86618d640ba5a72c3ac9520d4ad67f053ccd876890b01e2b58a9108e06057297067e768c36b0b21864b3f5531a98fefeff6f50db1ffe01dfd860db9e946e6276ee422373375052fe058f03cd7a9f87bd0bfec6bd8c4accdcad71e89857282f42c3567166aceae70215d59835c5bfbef7f93509f4ea2ab1160232e8d1c4308059366c34b53c72e2ee5b5b49f3f50dfbe28284efd1ab213587a63a7272c4113b379cf729a6a65b67638debc8ea1bbe0925711afbeec32a3b999513960151084c6040ae2329ec31a7545363742e955cb21580f5fcc2b49e416d587ec9b6cbc486e484155328eed39ce1790ebe7b032882b3f04e308be7c3b5a34a3be501ead7ea3c066c3cc0817873d9a7a5d862250d185671ea03e68be7109c965f4e5d82d31f75a0a961e18675a3b39130e7f12178b28b54a0cdb6a8960577e101d8a3e433fcbe5db1c39cfaafe23f419ab049829250a7b002579e3d3642442052bf2b6357e8c099da60a476a4c43b5d65849d980c895320dc871bc32a5a8a5fe4e254b85984803cbfcc8ecb7a100c4125223aa713e654b80a756abc29041675356c0cc0c70f6dcba11381aefa2c71a9a6652e120ae9432b89af335c7f82463f5d5781844478f05f47e279e446cf985b93672881c99bab05811cde4d0c7d52d0d6242633cdb32927564e2b5af5a5a7874518a275b12d34704c9f0107397bede671e944ef7bb5aedf232868b34161daae22ed05fe566808324940e93d10faf2e033ae0bb83aa0aae7099cdb0fbb40bbec2ac6c86292a2a135a969aa0177d7e719d7e866b0178ef2b5d9a8537f3bd9dab1660b7a1929044538c88bf6418464beb553e2e04bc937e859dffbc6af6d8d25ac50cd2dd76695077f3b15bd02cc1988b420b4c58a9545c6454749141e88819f40644c3aab54bb988c6b5909dec3068dc9e2610c66d144a43cbc61d5ea8d1036c0ec0dbaca117eac6f22f05118c389e6bdc562b36b62e3501e2b5dd1c88b0d993c6eebe23ce983dd4abf47f25e1dcf0d950ce19df0b85cadda41c427208045d2bff1a9ba06f4a607d822d582fa6bba1d549bdfdb5da0dfcf653a1c5a09cc930146b57bf01ba0d1c637c5e2eb92734b030de00c1803cc1deaf6c4822966207d0d1190bf07ea8d48508e23f34ce8d431a9d65346416289ce62c5690e8fd7183120289a821e61e4c563ffbd54434e2f54079f19447c3c1be943ffa00a4441a6e088a966b03f89c597d1b0f119d23c402583e0a0fc6cfcf983418e9b8e5a3e8cc5d8b7d556bf2cf9aad95caae5ba81ca308c10da7ee2d739093050ae4706061ff495297c01f13295f51fa70e1e3dc912a8f40e15060b87b9875de3d813831330c4a84278bbfe2ddbd985ef5586c59fb4aac171dddfd90f929ec73c86e000cb4c85dc99651dc23700ecdcaf4617938c8881b9f5ad6d17678b42e5367a9d95eb76907632598b36a23443052cde884f1e7a4d528c211e0870ad1f72bbc4ad72faafc8d102842f340402ef946f4ce172c34c986a9b6dc908858086ab5862046d27f526d0acaf184520173bb016c9c0235d524e4a5613bc3d922258571a2c3b6896b89e2439c3d46f78715ccdea040ada11ece0af0af4a4e4a90cdb33ed3ebbb43bfa2f1b9d9a44078804637633ee68c74e62b2e658c1a3f53e766ce56eda15cd9207a050584457cf4b47624fd0ff80bc50caf3102bd00023bc1179f8b0b34bf186e4e572afd42f810f6722b1223f87be7d030ce405302d5c6d605442b45397c07667128723198e5378f04c93051de504e871122a6e320b26a1c7226251f19185176b1de604a1a63d0d72eb5323f3d4efdf7275ee588fe1d086cffc837cae5e94d59d18c54c8bd11694147d2a7d99d83cf0ce96aff79edcb853ed8f50b665a4a28ab1912a60459408da0bba0f0ded5fbe14159fd88209894806bbc3509a808b2d6bdc08b58a4cfef490d0dba1aec4e663be0e29c621b0c0f57985a43f8738626e426ee8f144bfd47b7583b7583e0f659f1aef4ae3a19f2bea1ff2c7accb72d5a9718cce4ffc924b659ad3c663a8aedfbf98445b17cf2e422f426972ae5640a018d54c8b4e73c49b8a91174790b2c5caa6e920675c91381720f72caafcd72951aadac213defaaf6cb9990527eb61614b8fff4cb978571a89c7acb78c5839e14cc03612c5857b89071a29e660f9ad27e498e64a6647c7b90dfec059c6b9c0125c5634ddafac6683f061a8a2bed29a428762f39d32a3d291eba2a33031cc82673fb2d4024606a6314e9541eaa4542ed5fc1056d3c862fcab7ef6d0882e0898602050b73c4e0d800d70c3fad771c2b6a408fdf2585a0d13c36de25327d98d38238c21e33ab71e5fef9c4d71107864631484a89094f20f5864e9b12e2d03e0431fa553d5f9197ad70549f8201c484684949dba7907cb9ab48fad87a1131d446928f2dfa1e17cb30e0e9772b9c3dcec1cc6de9ea63dcd8f9c1f4de5fa95bbe5a9802adf26c245ad44747ac9f2df322498078c80e670770cb68b796a85f0fbdf6290b0a85147ebb703b83a192328a65b30d7bfc4ee8ba4f1a13746ba40ada1591430696783d9d9d060b0d3bc7e54249228910692011647848333b86dcbac755e3ecc5fd026cfdd5e5b9eef66dce99096cf7879d064f446cbb87e17c84bea6863abe02b774a8430d81980898899507b1931f4773b537b128d8dca91dfbda1ab7616382e7079f7fb9e50cb2f38f548fe2d5cdc42b64b75917d0242641ea829741b22b6891cc6e56d0de01914951ec1612757d2c4178ff5f321efa983deda6fb851080d12128fa53d54aec1abfca2168106d17e38ca1d5508b48f4eebd6b00ca897af36b0be102871659d1c803579cb3d5222e635eeb3ecbd2c1068c934650830574c6f8c3900671a6344d4d605da8e9f693ee85f86ab3a9c257088942dd4fe504b8179ce741ab928770eb327f3c2d29af6c1a189bb41d74409475650c7975047441777e0d96868458ebc92ac65a9a884668f8de038ab4e34a473fe30a68e10de68512e4cda90f4fb86c83a5ee2a61bb1b4c0852d91fdb62ead420162a916259a34bae2cf28796d5ab2dd3208b2329aaaf68191258789d93f12797510a279aa1d7a33b0c9cdd40445583ba3aebfbc70e53568e90742d621154440d4a8154637237204a900a1dd03a56fae0c9886d6f15c96dcc0a4328d4ddfffdc9607aa551c8956d2dbe89e62d98516a46781740a95b9c724d93a2a7b42bb9133fe832c9a0c3b56f0c5fe0f091f694dcb20f8392ac12bb1c38d04fb7960aaf998c6104d1a518e3153f84c3dd4796d02b7df9787f7f5492e4e9da688d8000241b054fadafa7e522ccb30a627dc49318346fbf8827f7b6d41fbc3d3b1d1310603d7e1a381d667f2340018c0dfb53f49f037dc3a341c73af8720d9c1832d64c34bea85504e07fa04cd12862288126eab349f029cad1a54645f1eb6f8348dbd7fc919d7259203c2010cd799634a96fe6d94ebca5e3d1a1f2abeb2c1ec371cfe9168f12370a4750c46e2395b33d8c4701e050027101dabe768bab8c0029aba52f0f66d013e76e234ac047687640444272afa0415407d19382d4ce83ca4e149aa340363168cba0263494024a02466d5f4092595507559bcffa3810bf85f6c18383ba8aaf50b72bc890878178960dea2ac8fcb014c05a89a7f86c794bb61a3cc0c15d54067786b027cea8e5eed5fee6be27586dafb3bb4f466f030547ed8f2d25b663169d5967fe76fbdfff524a2953f6060707af062bd6b6c1020b20205b3608ccceb23d809758a2b30082b0871bb6e44d984478132a4378132bb0246f82b4ef8609e14d94fb6ed8116f2205f82630923761c49b5015f126566f8275e44d04c1c89b08df84b887fa8dd934d8110a10b3ef86e136ec16ad0cc76531642f90d120ab410603590c4c2eb3e1c2789abb575cd602990b64333ccd95dd9e46062e630197bd781a93cb6ce0395b2cc7b55578ce96428e6b9748ae8df34ee1250f81d9f399739db219321cfaddb29e6b73b7a8ed3e7165b86c867fc96cf8cf81e1a21c1b031a7110901d48bf7bc50e1fbc3b8bf578f4ee588c500649c3bcc1a37d909d8de4356bcd0a1f5cac48ef2cf6c2a32a6888f1a8c0dd59ac8547797c4db05f7c5383c44e12354a76d63bab39e2d1bb6d1d0464176d9ad82d06648a1fbe892d913790939fc035b4986e377fe1ff321a3af25810bf6c781327bbe19b82cc0559b0df2d4660c49c24019e27308a28ead222ad572d3d12b95c3226143b3c1f2febbcc353b1c3376416e9989856ab05c3c7153d138a154150a8d8dd481a4c0deaf5bb614176fdaa4230e09d29b9331a075250b1db3b3a2b2854106083420000208bd59c60819148c364fc72f920d4d5b5aa8f1417d8920846b991e202f296f7de4b7e49846c0159a67425914d963e7e9718731442e43d5b6e99089be5627a2155a832d5133ca19cecd7bdd046698fbbc5ce6921ad542a954af553a95c8654d9260327838b177c403d74dca8a1f0f5696c2e43bf755df7c86d824f337331b3cd6af8d5e4a25709505d718e47f5c2a4df6d8384afb9bb8597f86e11cb69d92f47081949727b9aab83b7bca8ce98ba7361ed7ee30be77969c1ecb387d487b1f772e155bbbdc024aa377ae178ac880f00929719799b9733a6de7a407d40d3b187d70bf77aa81e8d1eda437b2f1c5fa1f008d3830102e36bc5d0b2b5702d1acc15951492d8ea810102d383f1b56268d95a34b470268c6f05c6a702e34b81f191607c228cafd5531a55b504a2c6b1b45bb84a5085c94a6d85c70a6fa5b702649be48aef372a5598a804576a2b3c56782b40567a2c2b3573a5b6b2525359a9a5acd4566abe0e2c4bd4f8537fc01f6e051e61ddc633a6aa2570456ae136f216798e35a276a8025449523afd544b5440955525891a749b2eab906a03c42f21f5696c8ca82429a202bc339b2147fadda59edbe9a75a329e7ef8f4339d7ee4e977fa9d7e479ec688d045551555a24a608e6f91e7ce5de23915e7b1518e1bef16be2611e46197faa09242128328bdf5e4a722bfd82f099b2227eb9dd90c39719b1e1b5b10dfd8c8e001b7c3373632f860e322bd339b17822927c11e27c1e08d208d2089d2eb2b94d3e6f6537fc01f4e1f3172b3d5624d8217bd2b29549e317555cf1a7005962a91e7f81e79ee2cb59ceb4ad64e9ee2273fe0af6745e26b70b29bcc26e391bd90ebd3c48031a124568b13337264557f2a10f81b4f9b1f114e562c156b9fb88dee69704fc3a2d11343864761f45f10500f9b0a54f198ba3a396d6e2a8b9563ed12cfa156dcb6735a48422420290989b4967628e94abcd20eb1446e1a594a5bb8cbd07ddd316543559922099192908024a15e3b21ada51d4a3b4a3c91b492564c5a4da4955c512bae949e4e9bdbaaaebfa1f5b7aa5c3a36ad2c1e0b9654bb650554ad56fc05e65c5ba32a6654f1cf59893ca7daa3aa65a53a592e432aa0baae405508a804f853b158ab75fda9424025bf150a97a11ac115c5e9a703921653d7f5a70a0195fc60a36a665489259a51a51af1705cf1cf6919459e5bedd20ad8692e2b12527de9e11b598affc547ae78885feb10505d57a02a045402fcad4340755d81aa105009f0b70efdd475fda9424025bf75e8a7aeeb4f15022af99dac2e43173dfdd475fda9424025bf7024d22afe392d37f34a808794ae9695cc72128fea177e96d78b5e98bd392a3cf76f3d83e764afa8ecdf5a86fed1391409fd91b81d11fc5a1f12f4373978a379f8d7efdce36fb40fb987fe3bebf819b94b8dda20d15c9dc786b85bec1cd6fe1df338a49fe9658f6fedbd39e7dcf5c98742872922151725f765429173d96112362d344142aa0367da6298bd0824a5c88554baa369149231189e83d99adb009fc6a6894dd066b55172d15be4e753718ec765b70873ce24fd6e9b23bee6c298addd2296c3dae710214144fab5a93dcd6db1cedf28c7252609248da68773cc4bfbaed3f4f45b048982d26a9d9ce7dc2dc21c1e9e163bc7b55bc47264760caa03ba437190501eaa04babe6a2f162246b1272f1b4d0d19d87d2e1e0be20370b70c8f09f1b2bb63782ce86dee0e61786cc52f9d739e31dfcbee76019e501d501ce88e84ea80f25025d0f5c5e25513511ecadb28ef04e58d50decb4653433e696ac03a12bf4e221d0a0a46c1a42dc2a4303ec0f45a2eb46aad5beb85aa85c54c31b570a8ae345a81f1012685e9b55c68d5f08d4d0bad174d5ab73b430109698d0ef4c6a661ca206304638a9f0a1854d854bc50715361830a9c0a9d8a1a8fde1d862469f21d1e7ec1f0b39d246fbbb31da08a172a6e36fccd8e12ffc2373b2b7ea9d0a9a8c1337777dd7ba261c0a5eff05c43b66979ecf6343b4976802b56ec2871ba321a03172e56dc5ec692af10698961b9b609b0ede23c2ed4452628c3c45573e170ed5c3b5c3cd70faed4e57301f1e8dd6148922ad72f2623b4a11c0643f2b2bb5d38ccc8dbdc3daae170afac4bb60a65645c3d57cd55d3e172b95c2e97cbe572b95ca31399d60a5d900e0683cdccd0d09cb7f3c5893b819cbd1388d3771271fe4ee09964e55c49bb54ee94f3c5793b71270e9fb895137702397b2710a7ef24e2fc9d494ee049c265ca8c0945cb9e49893a34ab2555122153645a2c1dab066bc73ac252594958409612acd5a5e6c28254b9d8c290244d736f140dfba585b857777ad0fbeef07c3c2db26ab074ac9d8ab5636d9a49aa9cac1deb084b65256101594ab05617162e35171bcc08254b0291c47e415b75d26240e6e9a4c57a904a2db6eecb6ed139da39eb1362f609261572b7fb04176e234ce12cd894d2c55314b92b2d9b25c54f09407e7a74045049294977056df3b6b3c28e3c0d8bd0455b2c4c150269adcc8422d72aa53cf1c43db5b28c995ed307b3673231832c2c586a2c31b0d858702c343c7a771892244bcfee8601c258883b62e46b84801755b2c282176d72d19a6da73e684590fb4abd206930d5aa3b1f84ccf4ce6246cc1ebe89ddf8994b82cc1e90d9eb61f674983d5dcfd3d89898c13b8bd1606131a3d7ee2c26e3d118709dc5c642030b6eb3f4c08c20b30e05958899bbaccd1dbe69914288c9ec149e6be556ca57b2286b250ca5ad9ca1bc953de5cfa37787a1aa2c753ca0917ec370a6a75f0e3b62063773f2191b7ecdf0ccd46041d8106c8529e130e0d324e91766e461422398fa342d90295f79d25c29272d3664c782fa0dcf944f3c53be9245592b61286de50ce5adec297fd05206884aa5a974b753fa2447ccc71c1bf235363391d1bb51430c1832d880c82738987aaec005e1fa155378eed4bf4ed3b533bb68305508393383c258122163b3d9b837692525410a966a9474251ca55d89c7bf4a3c5ceadd1290922faf768ade3636e46b7e4f5344bda81027d27d4fd3434a42485fef2c16f42829886f6232fe15ebb93a8d145405633afadd61500ce262b560a9460dfdce62311ee511e3e19b189026bd06098fde6d83a94860457c8d2a24de8c27dc656f1787c1945c740de2d77579934e4d1f9d30a08b08326f1aac3c82ccfb84a49254abb63869e61b140470636214b928869bf865636208300ec0249e2b7ab231665281d4f5995d3a0b6fed8b5a9be22c5fa3ea76a7002285cf0023e6345d3c31623ed355dc3e8dcb558a98c3ba8ae78c989bf003e8879e143b52f0643f807ee8e977a7e0a5d8a1c3d3d1a163039d1d3a3de8dc4007073a3c7472e8d040a7063a3ce8ec746ee8cc4007870e0f1d1e7ef8f8f1a3dfad93eaf0a0c303003a36e8e07478e8f074763f723f0e3062feea327a04755ee8dc623270c17eb7ce4de745b7f5177a0a3a0cdd4557415f418fa1f37414f416dd855eeb2df41374163d86eec2f780f13d5cd0efeeb7ee428f417662c909608fa1db7aed532552200270901840efc491132ada6930b5d7e0203100f5c4119c1fce109c1fe010c1298203049c217088c0118213048e0f7084c0f1e104e1f40007081c227084c011c231a2df8da3e2088143440c87070e0f87089c1f8e0fe7071c14630acfc9d8ad673d62bca7c1c1f138bb1bd3f1afbb55578f0d890501f5dba3dfbdd3c5703872f4d88d1d3af4188defe9b1181f93e161f4bb676c329ac394f03577b7384cc52b2c086bf23477bff0991a6c897ef709a8a0de8a1ebe49f1c3af4b9bf5c0dde0fadd38bb3bdbe15d1c1c3fdbd9f1688ae762604bfe756148fe75f7ce8e9fc18e3c7a673075651a514e2c96164b8a272994cee24c37a331cbe1696635663acc6cc860b6c39dcd98dd30c361967b9a3bebd17c2663e633fb59f1d90ee3cb36bd6c94d3996be121083b0b0f01d9ed66f19773969be598d5a0df3ddbe562760bcfbdeccc73b6302766b366b97fddcd32dbe15f778b6232f4bb6141980a03c26a421f6f16036fc45d331b96edf07ba6c32cb39e74364317c04c05154c28f4d6615570e9a41012b5599b0653595c7cc5ffa227fbe48f5fa9adf96f2e8eb88a1d8e9ffd5ff269ec09db1ac5521cc5b90a620246cccfbec288f9ee6289e762ce14510123e62f2e978ef91a1477e9342615483dbb7491866666660666b3d579012303c673a7f09c6b93ce18fed875923b6778c2093422dd80f18413685604e1adf0ade8cda0f41e008111f3544fe1399916614e6bb7b60bb7ed1c9915bc8b66263f5bb1a3866f54ecf00bc7cf56f8b022ddf1331534a8c05dfdc3dfecf0d8e1ed68207eb613862469ea9aaee95affa09efec57a2a0b8f09e131df4583a42c8c766a6bce820c8721812d61f14b2cb1c4123c2d768e2a0739e79208c6ff4bb1216611fecbf545b3499b4626513489a6d16432654e92999b287f6242452251d6b91c738a8acaca8a5d491953a69531c564a698a659a6504cf384344d13659e4ca6699674f9e671881209bf177dfb38b0bcc71985d622d168148624520d8cf11645711c4d18e31316618cf188370a6f144642e24d9a5098c1262727229108052535a29872ce5a8435c61ae3aba26255b4de755c96292356c1ae52aaed384414499cb1c61a6bac31c65a63adc53c662d128db4168d3aa5c37b348eca9234d5300c47dd6a656e20096ff175e4688bf78e5a6bad75f8eb78639cd2e11209a9353691a612a506a4cefb44629c31499ee08c31ce592412894438637cde7df9b374107f73fb82a485fdce5a77a34029afdfcc83b0c744e0eb768f46a49d160219c1b34046dce87693aea0048aa8d11f08c950a791fd8bf8e90f8444a8d3ccfe4886f493fe40487a19d8c16f074276e063073b9ea603367c03693713f22ffaa3c6ae3bc0478d5a905e901e803c15208247440e226610f1826e77d8015f105faf3f908fe7a381af4610a0a05eb77b0ce205c9a0db957e7d1ce871034a8fd01fa847adff0efb03a549baa83f50eaeb9608dc04e4dda28b71680625ee0ecf0842ce41c8e61c821079a0453c30e540a47570ca3112f90873e841f340ca4239e840e650430e3066bc1073e00967f08c3f489842209846d4d07844b8a2518456ed2669f868f8e879861f248d1cb99cc1098d19399430f5732b730a8a1152c61f3456b092aed0b095846029eda025ab74c1bcf2c161a503ea8af0744172cfee21d38ed08554c24b8e1f328cd8ba19ba1a2c4e0783e43925cf50c7736be586c2989c90c9e7b96d811467901949be71886b24f9524540c7d1c748f6ff91cb11e666e4ecceb9c8b53055d61007551462c320137cfc88c1929ceef206b09de843ea7ee050439fc16e1c5c8cfaffc08167e8c70dc09f4ff7e386f4c70dbc1ece409a7e4ea8b15bd7edb96ac1103086b8f5071aa206042574dd1f0808c16ecf0688aea118e40305c1fd8182e8fa0305c1f5db1f28480b7aa93f5010186ec075da68f70a43e8d0fb030d31a3932900f980627f205f91106f06e073fcfc0292bcb33c9af68617fd7b73944cc69ef76a24b3486b91de48a05c889077cf68d9c769a67c3216314779202d1a84df625c8e1790b4fcfdfea891d680d4f931c6196392111263bc35296b7ebbde404a7f537680cdfcd4dfd48f10f12be1853e461036e7c78f818f838ff54e880e4218d1389cff451a8f447cf711cff64b247dc32352f8223cca91bf69ba168eb0e8492e885ae71c84ce3908fdff347edffd380731e7c78f310a58c8638c0216f218e39edf57e31d44bbedee4d8cb6ddbaf626c26db77d6ceb96c5a31bb7f0b35bbbe86da21bea5182f35d5ea789dd6ed2db2d8ef8eeda926ef41bdac21a1ff95884610b3b253db74bda85d7a55f1bbe1915b909e194f48c86fa1d35f1d169da054fd72ecaa36db989c5d12fc09097d0efc67f8e7ee3bc7388826d7b408eab7ef78ffdeed535f1af3137193710f96f44db3e8ad7fbf55992244ee1be1e05bdbfc4fbbe5628fc9e417fdffc3bf39ce20c3ff541ce38bc9bc4afdf3dee36414eb8f33d777844396f349653e8272edfb9007ee111c77b1381fc9c70db70c0688b4820327db450a4ca67aeb44dab0d1fd860c70da7700293c0c62f31b2825f2d4774a2f8d50389451e5f0e820dbd45ad335940d2de77025fce81dfb9b3996a057b6fc7fb9eda04f8e61c44b12482b218e36b6168d13e88f088fe9b047c13e17e55e3740d77c6dadfc2e2da3f5af1780c26d9d602ae66cb46fe46d7f02bab6fe284e3d122437ea67717ddf1a89099e65df4871408df452fca0989daaf7fb1122965b7d8392e7ad35c5db57f5f8b538a3128be79d1577401913ac8ce8626155cfac9fe135714e372fa9d889c54d40bd40d45030a87cafd0bd5835fa8dd455138562a5ca2782b825c1d9648272277160b52f14d4c464f3da9374eea4945bd40dd5034a070a81caa078503b543f1fadd237d92a24232a1c8b9443b2123377e3d74a03143062e2643ec859d2db96289634d4fe14b01441892e44a15aa56a158abdd99e6419d7066d5a280a4697a82922a553097e1a933b7653a1911d94f7644a6ca96c880b2a08c890a0035153b15381ebd3bb5c3cd5ad17b415a2dd3c974329d4c27d3a9643a1911d94f7644a6ca96c8803226b220006afdee90a744969165c43ca6a7f0dc19b35b842e1c066c11cb719dfa876f6674c19e15f79bb9a9e4c4bc40d2c2de32c29caed3053cd9a84b54a94c287227e69cef544c48ec172bb2d11cce0210fdee58d0d7f44e3c08508c54c70d1a326c35c115860c2e5ea8d520e15f3543bd068947ef86a92a4ab803e3bf09d4cdd6f0ebb6ee4b35b7ce1bebfe258c7b288930446bc79c8768ad75369d6670c218f300c502838837c63afc92595afbdc3e4ada2f0acf1357f3db4998c746de2d760e0bbb448e0d6c7b0e475d7fb81fefac45e10e47278955ec8d8c7ddd7dbf46b4433e7af15fa22d6345a97f89b6c8025dafc0bbdea2c8bec5a791d681f82d5249255ca224a608e143d2afa853e0a2a21de2589a568925614892b6c68464346d55554352a9440af745c5d5641a4f23a9248ea35822997c6c943dbd80b48fb239b456abd56a2cac90add1ed0f0d2f601095dc7df7dda8552f7a42e404258598acbfc270861b432f89a3c9348ab5d2ae91762ddc281ca0a82d504fc810df90f895535488b0f648911b2ae8e6268b4aeaf66facfa2f247f433af775a1d3ec124ad82561786edf0aea84328d2811552285a89108a55128140a9531eada009c90a9e89a396bb709bafe2e92776653d86c6d4bbfbbbcf6714c5b73bb0844fece1b9f2b1edf47dcc561fee5d1bb4ff6ee9a7f599cd5d7dc7d5723209db57e7e32020ea43501b617cf20ede6c1b5145c7b1379df941c69714d06a2ca0de168edbf45fa77c657e506d26efbffffaf5a21ec777fa05fdf68f7ffffffffffffffffffffffffffffffffffffffffb24d26936994c9d80767d107e7920fce3e38fbe0ec83b30fce3e3c954aa5522e1f9c4b3e38fbe0ec83b30fce3e38fb9ca6699aae8c7d7026f9e0ec83b30fce3e38fbc0b0582c96cbcbcbde9c9f301906065f0b83fae0ec83b30fce3e38fbb438e7270c4cab151323e3ca2e17bed68566ec83b30fce3e38fbc4a03e38fbe0ec2323e342d1d7cb041360337966065f3b8366ec83b38f0bf5415fa318f4fffffffffffffffffffffffffffffffffffffffff3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3fff3f3f3f3f3fff3f3f3f3fff3f3f3fff3f383b18ff8ff6fdf06f15b16672dc25a340a499944c2d7924421a9248e791cf1b5e35a1a4d27149949125f4b9ec813945499cb125f5baec1a126c155f3e8da9aaad9fadd293c2efadd2ab61518fd6ef3c6925958f0b52c355e8e1e783b1fe90e03b11056828158c54378c5ea0d07a306dc6d46cfeda277f7faddd7c70dd2efc6bd4ccbd927ca2211be5694d38bde40d2f20d4f2ea2288aa5daedb9babbbbbc9bdede9af67c3f15ef6ad757abd56ab5fb53816b90b4dbed76bb5dad56abd56a2f2727272731e51abcb56bbba53ddf4f4dd3344dd3dbed76bbddb0babb3dbbdd6eb7d3ed78e9ed85384dd334bddd6eb7db6eb7dbed763be506322695da9ef5d99f6a81699aa6e9ed76bbdd6e585d55555583b7766df77671a3344dd334bddd6eb7db0dab2a56b94aa55ab1582e2e2f2f1bc5de425c8fee02699aa6699ade6eb7dbcdf67cf6a702d3344dd3f476bbdd6e38df5e36e7e70903d38ac93131f8da98137ca37b80bfe2605ed775bda5a6c5d91eabb33bcb4bd334cde999539813a6d58a89919171a11945f1b528190b532053b3b75e0045f736cd350492abb96bd696d7d3e7d379cd79c5796db546ab7b74fd6e9919323af4bb5dba8b5ace42464d2aa039fadd2f930ab6eb9d09d90413f0b5268427d50a2412747c6a54378187bcbede835ff846f7fe379eb4b074d248d3f4f9748ff6d174cca9ffcf204dd77767291ac8fdca3445b724c911cea296d538ef3c023b144446622fcefa57a629ba25498e44235c228525bc0221e1688b56c818af400849e402a9a4c5d1f42bd31cc5d32d49522ca146fdf7681461aef97d9a7cda9c45329fe8cff8febf2589a517a2f8d95a6badb5566babadb5d65a6badb5b6248a56dbd174b2daa2acb6d65a6bb515599454aa2cadb6226badb6a5b5565b91b57a8bda8ab44561b51559cb62378bb5d66eabc36a6bb5b5da5acb72b1dacac06a6badcd7777c0daba3d47914d098a1735323960b44567095410b039a26d7344a76dcc196d5bcee111ed16a10372cee86cc1029523da3747444d006f8c39edea641a0e7bc41a3dd6d6b8d97d77d8de7d5f2a32448b7bf67eb7d662bc51829034b38b1d6f9cb3ed9cbdba05d01b6ff3ade8beeceabeb099046937cd1c95525e188a763520d9ffbdfbe39328870474bbcb21c8fb765f6cedbeed10ff667b712e8990b414743a1a9544c890442a954471244f27148a8ca1a0a0a0a452652ec3949c12b64ab234cb5549246fd1debf38b4a33eb3b8b23b268231d952eac8ab92c86a65d2211d41b2582e2e2f2f2614019a79a804cbdad169e9d00800004000e3170000200c0886236196a641107bc80714000e537a525852582c10c882d11cc56110c46010638c21c018620c21862154732700868559b5b3201a75f7b59a2d0e44b4b25c1ac971e0cb5fd40f41f1e6d2a81d7dce2389573d4a8dc1013143e345a907d358014a4cb8e48de22c6e26d827e66c4fa65b7de2eb801824ea3792734cf0c2986a495bb2f50caa4062422ac21a8c28ebde7108931ae877cad883705c03a39b78b62f91cb38f29f6e037171efb2bda099b81390f5b10ff10ccd9d994076eddb8d90b55b061306e93d0069a4cddabb96feba2d895fdd45c699f8def61f6b2f9a7931a626b208db588084c62c68278534521d5bcab59c7cc45c8c9733a2d2f69c9bdf2a3b85274cb61fb22355e4ae62b614b5cebbb7492b6ef9764bc12f65ffd022a150b23cac4e708a4e046cb470e4f9ac01ee508e60278c2efe849cec74ba5414e6be0eea99ec27643bbad067803ea6675c0b60fbf4fbd29885c1d8a4ee5ec39b27dcef696b614dd166a4cf42d9a53989035d8d02523938e4fc81f26db64a9631b0034f4b50a028351ecbb387783a040388343c3f83fa83c9a179f567f86afeaf745212735dcd8b3c5bf1c0c0410d451ef3958620054e393d32955cb3963b735ebda8a30077c34daecbd368ba6a1f2b8e11875ed30cee1d41da278b5b0fd9c617b8fbd822fd58ecae453ac35fa27c1dc2ff6eddaa596bdee3580e061bd2e9ce7c0f8dacbd0bc52728b0a274e6eb4e11d2795f732ec0549ce849fb8b897581ddaa9a8c0f4d9a4758f9da62524efdd88acc07bc46a43ac59d362be40501d93de404285e5ef3861c4336771206ba50369b9c2be49d5a3649a11d35ec0411bd8e395943c10e9604d189c0432d3bfbc55cac0b509ca7b38a0b30ef400ccf397be620f90862bae4ec290c291bd6d1bbfe3ebf3889a2eeed342c8e1b454515556dc3c04c57ee004975bd6dc753eb48d9079c4c962b80622102605ff2f4acdbd30c3d3889e8561f891709d456f1a270ec7a481035b5a2d8bb35cc1014394cfba0d60618e84e34e85bf4ca50d034be5209dc4493a7449ada558f2718f2f1cedabac4fc9a1a18e5b5ed633395043b5d8dd42498a61563b1c27c2a5728e639689b15ab0b02a4de641e433c532630015c5bba366f289718b4997394054d45cc71aa031d5d13d21ad4904613228f706f93287dbb036e031411dfff4b28baf9a25df6b75a31eb1fa2deffd289dafc602b6c11b05635fadb48d4178a89d83dc841fc390db698c8a49b398c548bb3b45d3f52d80ed6de3977f31d70d0f03a53d4b89dc00c2e25792340521dd7c4e922079f987cb81786acbaa101037e6ad7bf78091e26f06ed604739be72e53d94afba808269e63447580466f1044058dd71fd2fd98c5983a6bca2654d6c4d2e0269f0cb442b1a3a87b12b715892912b00326997110a8e82045e701d9366bb34e35af6b0d2fdadc51a61f0c360c26a177d995a6aa8919ed418d00f8d51ce50fe8f206c61627f4c5389759c9e2aa830c812520d72b110bd5f2a7a3d556790b665d6d3d0ab877033e29fd88893bfa41d1fb45554894147a212fde40b8c8724ae1845abb087906dfb59381b5065f063e329ab4eeb8c445f0c0248bcc36b624c4d86b10b6dc8e50a3671f13ab0210e9bf700128c48462b76639db90a0c0c193e3da255bb4e3904ce3357816bd68ad6317310880ae6e239e3a8d037a1c138b506882004f9e2f89c22f21b39d545860a0b59d71f0b19e5d23dd6c8941d99974a3340cfca92ad12e9fa263da7183d91b1c053cc6103b745922c4626e1a424bd67b0ffbf7dbee8b102745df909842b76eaddd41fa981ae96a3e73d5d44857d399abe699abe68d7435cd5c3599b96b9eb96a5673d73473a979e6ae79e65af3cc5553cdbda699abe6997bcd33574d33f7bac936cca0da7c4e308a487bb238e435d9a71503fb938648f640bb7d069dcbe6e790116ee8d455937b6f113764aa1593abf7f87a8b65dc0b71a3fd3674bce499e86e5e641fc1574c2ebd47dcd029574deedee3eb2db64139602b33f43f0d91ae81f6fb087ec5e4ee2d820d99b26a72e92d5e8f720fafc3ff6eeee24f73af77c1a5df864eb16272f716c1864e5d35b9f61eef77b10e6bdb513c2f697228ef72ad84d9fb1de20fca6c1fc1564deebdc5eb7d72943143e922dc859cded5369f618abd699b127dbaaa0782bbb66eed5d3ca6d14f43a46bb114a8ed7a5beccaa54a792af44dbabb14d939fa89ed537962e0d6b4c6db489bbe4d6bb86fdafc69dafcd1f4c67bd3e6afa6cf9fa699bf4dafbf4d3b7f9b367f9a7efe346dfe367dfd6ddafc69faf9d3b4f9d3b4f3a7693fb04fd57d1613bdd51cd1187559546fcefde63251fdfa234a53a38bba69adbfaa6b2d2e7aab39a2b1ff929a70a77f5d2eaa5f7f446934facac4bbc7aa577f8ad252f4cad4b8bfce1075e32d7d33ed665d1dd40d9026c6143dce6aacd034ef43cd19b8f812988d69f8d3395e7fa853d99318350109c9bb3ec07f72003ad0426f51e62ec1d2fc239ff8954185c193ec1d6b6daab09fa32c2cf9c5ad1ab6ba955b41043774ff82f88d5311c2cdda9f03f76202a5a2d09eac395ffef15506c067ad83a63af0a81749dbf8c5ad182cbab508eaf38bb4bbfa7192d3cabd543ecb4db4baa0453e53ad4e4d8e69775e698bed83aa60452c5996a74fb74bf3fea6dc7b16d1cd4219300344f11f4173350324c49bf7abd7a7cf489fc5c625ecc40e0c2813c76489702b31376cff1e835984262b1e463113d7527b4181bfa9c410d37c5e331135c95815fb7c10a73828afa628c2ab7b032586c39dcfa02db57eb44a211891d684038ca64dad65d97a49ee85d1f3aad48d5e89bb81a1d5050a13fbdf251c96cbc973cfaf566a96fd5b6bb3c75127dee05fa1e9ef62290cd08a3b006c73efedc5a83d869d01ba0c0ba620b1415c0ce860e60e8617c7a84076bc7b9b2d71e06a74119c7edbe4dd66f3e6ef206c9d63be1ae6ddfc6b7dad676a576cd4bea7f8d71fd5845767b5ac51416b831f7c4559d44203f75da31679e5a17cad43e09033d9daf9809509ab439704e9c4b53c2dc82d731900d1b8c63a0ba0037771019b29f804513fb29837239eac4f0f4cf50e4a1a5d0d5d04c72f9b79b3f12f0b298a7cbee4dbaeaa6c94e9dbdb506e3b3ab59d45c04ddedc28de93a382d31e6ae9236fb941bf9cf2306cb25f7f3993efbaba8ce0974560efc1b222f1e8fbadfa262100ffa2aca0d86f389bfc144e64ca832462a58488ba2510445f50b9f6432901f1e016f71faaf43f4e6143172be90c79b3c8c08031853dd9e43830c75aeaac026de6cd266fe06290e88a3323756f8d6d3e60967b12b2991c3d0959aed4638b72a0225c54b305149fdf07efd5d8c54f2148a45ff042b22c3791b17810f1b8c459cba32b8c7a69cc0dc8f861d6592109cabc327b3950076ae87faa1b73cb1f2f40032ca32e5a4b33e366962c20dec274881ce2b5c7e69828b728e540c5de674d18ba185d00b75f36dcdb0e96c51a558164a3910411b622ec0008cd66cb4ade5d616695e61e46baac82e13775752b6291cbbfa1b674a34892954be63695bbff5eacf69b9e280451f8c077496122217d23fe9d71b8d0d1debf6101dd26f72adfd67886b1c686326b04359b3839127eb38384b96a4f8945c1faf6e5ef7e22454191ce4e5928ef10882b258f447266ca4379b9f772b8cfde590bb061bd463533a3c5690df4346c2de4d922055a8c8fdd3f7ec3b9962960b41ff225286a5836ea9a16aecda4190ee0650d8211d4ef2134a537c1dafae22689c8906b1456e1dcca82940b0b1b442334373dfbf63a7d7933ec3890df109d7028a43325110ec01bd539edb6175ef3a5fd287fdad1b712ea890c71657f79b2f430b7cd6a025f83bff43cd7aad8cc98d4ab179449036668693edf7032f987d8a1f05e3f4e27c005463445c495b9e1c20f63947ac7cf1961dfa4a9dc1eff4f7055784b033e9087d1bf241083c2e6dbc68d4e1b8fb6e423f63a5574281a24f344db71bb188d9b1f39f1c23aca33c31b5215f259884dbf98265115eb8721a3c1af285a460ffbb74df4c6e419b7ee9924e587b0df0803c7428c4b22ed34f4d0bd4781a1e3ca0fa66d91e9b5c523bc49133c8d58bd96c0f98a13ca28077bf92a52249d8f495729d6995d4e89605ed9d96aa360d07d92190a323a2647c7c126dc706b9bfda08c4a5a7878bf8e587e07a70fd3e6e19e5ff41b01c8866e75b32cf5b353fb682b8ec77080d5548e7d8c58b733564aef1c08961d3a32eb85b8904733079878434404e404d72ec1c0ccdbf60d679ebda8fbfb2cd46d7d0d6ce89c9fe3fae21f2c0d182e277984bffadaeffb1e6ec6f0e44aecafde5a57ecae29af8eb04eedf00faa7b1d078929499b425dcc17f67da3e3a431b5e07d4eea56e4163195e5e7647d48515a6922afd1c32d27c731372e7d27be09827c94e53561c3af098ee756c692cdfec98efba55922c29fa97ead647156dacec53c4b0d462fef5322e3475b3f1ffe54123b410b455ef37b712c180c3b83ccf9b3a741d7a32511c8627912d377634e9544525579fef03c3d72539fbc6fcb2ff7314c593c8eb013f69b0a14db4574d44c14d9d456b4b27f20ad6b8b627b958cd35d8a712242fd90913ab4880686d0a53e46d1570c74432b6462f48d53ca8429dd140860c4036abb7c4a35dce56b50bc826a0355f4b705aef252ac5070ba8694845080cd6b83cd8da57128ba6f06cda7301a8dd32f31af59990cb9a98d2e11d95ae2ec5f57471905a5b2334df6412e88ddb836d4df72182bc77e2fbe8514e883645feca0a7f134eed25a0d79e7697fb531b961c78750f374df0375d97f72e067180b11fb707f2f8603e31fe9b8bb917dc95331bc34a69f35b32463da4a33ff67a2ae5e2fef45c0589dceb960be78591b83ab42fa12814de3413a090854c1b2364c2aa1bde3db81d7dce816d4814e316af5920150fb1e3f6511812bb1195a73e29dc5e736dc0ce40e42683d78346f1abba6a4e274faba988bdfa8d90c2b2d968b530c4a2aec94c42e65db0cab15e0ee472eb05fdae3e7fd2680c1ea650938dd0ce22a4354a8bf9b4178921adb0ae77db81ff0eb5c05a76ac5ccc01269e60a340272a1744b5279d968ed33c7b1258525cf0a4579fd0cf274289348c9f81ad9b7e5c05926a22da9d7949939614bbf4cf469cc84a0a44d5c1c0e9f804ecb4d5aeb0ab989cd603af19dccb9ad5dcd0efad7842b97a1c6aefef1966230f9ce749e4fe47cd3a3dd3b1b1ae8791cb8ef6e182634bb703f49d66e3c72370d8497a08473f45a64bb01aab1f3fdc7ce0a2abca42ea52d5366f52a7424707c8c578be6067059951c75d2061057de42764b3b613a9da2a7dea46bf4354b23c42090b1519bac75d7709f047d93b6261808848858646fc2cd3a5c85a4af743fc343fb9d724d18699704b77059b18c69e5a7d6c47ed6c4270fd90241738233ae5873326c7e59e4782f4fc325313f7eff0c3794e12d4c07fb257d95c796806b9b90aebc654bc481f78544257d277612f63c51b31ec6e238bd1a57b421388c4da68b83413d4a5544c500726b7fb9333db8d2d41c2eaf324ba57da854406ce1eaaea9c6a46af1348b5d109d64fb0c99d5e99ed822295bae7ec54643bca3885bc71b877593f443315d0d3f253c72f76ffe0ddcf7a264db0b8a962210c81672b23a980a1e1b47768f5e4589da63ace047f251ba9243c9d1c0004b11536e6924e48b447c5841d75fc8b82ca4b93e3de03866ec444d110dbd6f81e275aac4551c50d9c8662c0e2a9d3e15a933460e81b9e61f43b107c44b17045dd7588baca67fb9f8c38caee4b3b6496d37a086e00f31e23d2e7abaff81baec3d39f03fc628af75c3074cc5e15dfe48e7c902e10257fd4b03138eb9c0bcd4eb2a7c24455a87529bc65e6e88220ede57e5aa9a727946940aa7900bc8aa18c8da498b80f61085d70ade2735bfff2f63a9e4c4a2b7d5d3410ef46080f25f9c373eacadeb4dd5460e806578851e16592de3a519aa3bf69333d5933582b0941e15b94943b08a32a40575b2b690196a95a49c8a68620476f301dbaec19f973ef8d137c7fbba47e15d4964dc9f9045f467e24d1d9a57c113b37af65f2b2eed87221e4089ee47398b974f3210bbab20b7bcccc6698d52f4f1191900d26309cf4f353cd6ca81b77e0117cbb0643556461523422320b5932895ae37ea12d6a318a83faff35e37511f2d71a6d07163e1d0c9ca9c5d1834b649ad9c18911ad178b12f37e3de4661dbfe23e3476419bf586970e6a648ac73517a9a046c4894f7b9f6c038e16f377bc5ca896370710ff2e3236bd16fb682173e1115789aa11feee9083ca686c159ad65960f3ed386b8e6bfb48935249d7d47f7a526472006b9194d3c3a1d825778f4ad2f28fcec60c9f02de27858b85199e9b589dbfb0802ee1439b4640b11018a17b276b1eae603a2de9108513b1f33b31e4245d83c8216221ec06cf0d249cb77427970077fce5f41998c2c76142789668c3fa8dff0ccd9b30a66754a6670a85c44d00252e1dd051dcd66824ca07e00fc816bfbf5bf34484da47ae3e3d26f07bc38019f6f11ecd87c0b76e6bde1c29c76a239ba29ea966aa75bf38170fb71802fb3b630ae9d35bb9da331935f9faef05f542693662e6501df97a2e1c7a1b4c876ab61df945ba1e1bcca4f0579e8e0c0611ab8a49e4e05924461bc3d8c2be8459aa78fcfd2f47777861ab8fd31a03c1879fd5e89f6288b4384766287fca865c7f74358b25b0643f38cb0732d6b7cec7bfd1fb1586f70fc59f0e990cf49bf02fd38ebcde5475492b400e061029f71563ce70bf260033010c1ffa486a61f6c84bb33e95d675b566d4e608942a397e9a9da4b90d9eee0930832c173fca5877bb9c6a0b71d93202cf4b8c8323d091d6d704797f22a4df2d0983a64bd54c121dc7fec92c96140e969c405e5f48307c6b9a768ad1104c240827c2d0f03e82b25da40073a1aeecb4802105afc3dd91c146119573fc5bf685269ec9450eaf947fb1a39b61abebfff010ee9f6fd4df0da743903fc4ffaf1d8e8ac22d678e827bf2a45f98bd1379d5cbce2c939e38145084014ee003b2b04debec5e0fefe292d8f85d8794a619f62e86ac067727fffa6dcccaae74e090e5694bf40eb465a64820c218f8cdca0b9682cefea2d9599ad555d1602376b6dc789e7aac7febac6b463f66d383200ad65003cbd4d732116e023a357f1d9da50c536ac18edea40f3de72ce860e040458433a00677ada40e5c17502434b41ce00b85fdabd0109b530b09b83f3f2056916cbf3266528564a9d9a837c8a3ac6674a2a1582fd6be0e9af76bc29e6619192a6f556141c1353deef31d28a14755057b0d68a4dfd3bae33407a6de075c4eaad41090025ecd8432a81632c0211c02105bd3a15b05cd1054f64f5e70f08fa6db25dcc507ae28962f39d60a2759b1d7e978927da37b5195e7cc08738c9b47160c86ce564a1b31ceff2a43d9a970175cec2a03b2bb3446a44f01f58c1079fe5d4cb93b1b114e746e7db526a1bd513694c9ed9486d7b20339798267113e47a96e593b550a436f46a4ac181cf83e7e4d2ce04b2a1f5856aa2f14e9cdd3e7c88a5a6eea4a80405da3c326eefc84da76a68d35c97ff7efef4bd40ae4a9f8a08cd597b09ee72782cc4c0f02438ebe2c10b2232c1cd84bc69388e821f3916e0b82ec64fa43e95c38d9d849884290eedecbae98afd4d2e1534fa5757166c649c3180ca940898d249076a621a7da50e7aa9c41d1aa2a7aa556241a5f725d119ddb85039789100aa9b2e98f3781a0e3909a00a2ba4aa49a3ae8d84a8801e06cd4e68bd4aca2f68bd4abfce750f71c9b199ccb732c260a3b62ad478d6e37d799d5d472166d2521d8a6361747b54abffe6bfe69979a9bbe69795425121b2d0289e10478ce74c903cca37f0debc36c26957665d15ca86077980d41b866bccc523ee1fac751372a870b08eb8a9757e5462435fa55732ef0d9dd644691ea57e8a41a933e710febb713750ff32265610ccadc74ab98448bcda8755febc61b203fb40347015cc3136e1e664c2d7f10592dc11f4444281e2ed905888451963c5e9287d6f1ab64630ad8cace6587c6f45c4370344099445e8c69d6f7a267d6991e6726cd27c3b7e5e7e640e20b9987f507c8a3a426dc6de055b49c453ccece23b640afa292ec4a20e846be5c9d3b1e1287329f1b3e7b10ee5679d1d82d1c1381e40871932ed278c003b17e050ef3529292753cc9d697b842fa17b2da9250a4ae29c6ac78e3edd44fd0a5c9ccf32601b063e8f654451953871b653f960f5ce44a0fbddd6e31433ddebfb336bfc657f036325f6c8b0f3aec5472fc2eea38f8c15d0b2bf0082338b2541e2661acd24415e60dc7866264783d12667ee6e3003f121815351aa7d01536c686e1ef2b5e72dc728d53544a6b6c2dd04865927487aee48d6eb75b4c3d183d23a2574aa156a762a9988cf9e60cd128c3b9455a327885bf37d08375b02d412f0b1fa1c7acaaa8c6bc5764b51915fd8bd22a189a3ddd08037c4f7db0a808d360168e40dc04cc906a19131dd1ac290be16ff0a0756f579dabca5e45ac5184eb34589777ae8d3d2411818e461c498f18e2cb890c60de3f0449b5e0684789c17f8dccb07cb02e341b92bb3d1dde5163cf54430d29081667acbb3c23995e0398797c35fbce94b822a3a532f5c03b94827a8e392cba44a5e8209fe3db4102d632b5ae638bbdb3e70679725f8dd85be0dbddd2b3a7a74e3d4907dc39bb804717422f5a2c874012df1aee8d50bd2936e540c459ad2443a998679df1447a29b44d710f4933c985d1e3adc7344b0d91e2239a58c77a6097ca772ce4a22000856305850062100e2f060dc1d0b127da8a8dca76f37588e86f260a3af0f6003b5f77189ebb081609a9453c44b5501ddef3f2100920b1691164b1d8fc8d502144b481d67a102dca94f4446e2cc50ba0eaad0ea23078263882d2c8363f4c3c448bcacbe73c3a230a00b0f8548344bfcf5e14bcd7f71b6af63c55f27b94beca35d257a829fb0fca35649343c30a0a849b7aefcb98dbc12648dbde94775e220bb525adb826f329018eb4fc660f7848ae4e27ad942b64007c16f038f7e28dc83f4cf8c50d1fe3ca34bf0dcc2c49b1d25fb03e02b9b3e7ae4a4d8ec726ddb36cfdfd68b29a66bca28acc0d70d96d97d053a56d86dfcb3123bf130d308f1fbaf73f69da43931bb1edd0f40f23f05c101d8efdd001bcb2480950bbed259cf2f974ae17ab0995cfeaee0b3c49dbfbfb87b343960949652e0e9ba2b454de766f51d28fafb8263e99d7dd1ea95e598ad32ea1cbe68ea7b2961d9754029fc5ede02b35455984736a47c19db3911617236c4a267d0f6e9cd6dcbcc6cbd9ca708fdcd27328dfbe45ed359f211e28001611271e88f80ed4e442db39a759b7e2b412940f8b1bdef46ea2f746029e9e9b30c4a88b9e861be4dba467605a14a0964752451d6430f29f01af804dd7a220d6d2609bad1b50e541cd2ad5cfa14309bc981140322209dd9c813829bb4c86c19738dfaf45275e59bafcbbe0fd7e59b40f4633a1769154e5af2b5e7bbdf5381011a1d09912b124ce0782e06e10c8e4c8060d9f679ff7f80b0a52d20f2d98d4e83a3658560bfaba8b4c4522147e846b3a1f01bae7a1add79563e0869f008cc9ae8d7ad1fc3f19450fdde41c8afbebf95bfb45f5a24ecb638d92e08f40c29671445071e8cb1f724fc723cc4768eb76b6bb8464bbad8f71b6fc7f29665a95beaa57b8c7005082557482cd888ef11d7af82adb3839e854e1581d86601f5eb858a866487993f869717da6de289eccbacc09b9caa65f7dd262bf821ec35f3110f4672f6294722c28152d874ad2fb48f65a4d139d434c32b8ba3243b2b43fbcd03bfb13451206611e9220b5fc69819cfcb839e21aedaa8c652285dc8f081915ffbb06c89f05a72b44048f2c0f0d45ea8ae20aae88508b1287926c918b9c4e4a1795c7d4c2c5088711c43f6dcf928b640f58ed0317c700724a8e34df5c0ac91637df98b9117a06156a51b56e7e44fbb82ba49767cf0dd66aeb7e443f34ccda5f6e314f2b08b43d0aa20ab099f88d9c8db96292567406c11f35dc101f0e37134391b05f1bded7b2e44daa1d18dfbc2c885c21eb2dd583b65794d4a45f005e01227014f568b98a74f965c02a83e44fc7606c1be9b6606124b400240220e317922e806b64cc114655c9188d18e70654b834dbe67045db40251204346ba6fe47078d499c32b0557b605bfe4b138797fa1f1d447a0456609039c7e89a2c52056b42dec45cad27a42da16b51c1523a2925f2a546d0b0b04ecfc88bb6ef42afcd3e7321b5e5c2906a19a3f9091d02616089e7b117b0a11c22d014e7e9239049ac97e3d26e2b4530021c66f1a51ac5134d8f015c6f185a37e6c019b9ae83102204b814a0fb423c5807c048024ba42ca5982b31c797924b077bbc53869b558a75fac1c1de2f93d02749f9b268b58658dabe6271127f0c5fb3acf845b2a0a58456647c49a82590737e57b2c9d6175aa2475d955258ef4aa66035140b23fed4f6365ff5e5eec3dd5e953b6578765f8feab0effc166afa5fb693a22e03eba1de8a116b7d455b36f5eed0f8ed340fe3a0e5819ffc6cac1fedcb2cafe60889e486865f601e9c0fcf80c3db3975d7bac7e9aa856a5254b9c55b3dfe0068d255530f7d2a15cf70e20dba03b2f465b129d2f581dabaef726ce0096ddef79918daf9e5e6f9fa51e54ef6a92a31782aa48247b3ad7f8e7509d2315589305e09c85dca2819d25999a67824e54eaa768256a770a2dc2fdb7bc76036a484b47e35897a8996b153dc2dae728fbfff4a4c4ef8363a1726f3c2fad7d99a52f829606f8f1ef56a5cbb63ba45856c83562f3d200aa872c7ce07810fe8a08f53274ffe2224842a892ceb2a76f43e5c3feba6261f3b4c57cc7f1f5d4494e602378948780678e649c36d806ceb258beec3329aceaa216156d646277baccc514137cb3a1306502793f19d89db77daacc7ca31cf8c7720387c3a6c5f16e94cae66cd652cb8b8451212d75136acfc759bcdfdf7275eab2b483e4655ec3c084e21630fcab2696456004b9d6d6d82d82899d5f7ecba262515f6cac0826c8c84e03050c6a8f7ee65d1cde098f196b24fe41ca76b6a6a4623467f5ce86612fd859dde04e11854b637dc661f88d6bd8b0474fae7ad29486c37a33c506206d181e80b69eff17f32c67284296ebe9d84fe86ac07dcdece54c8db359c3b3762e5b26c086c14e81c1bba75dfed79f02bb8931708603eb2ad0a723dba1aa662dcbd424844025a883d8795a621e87aa460d11708d2811c233d5756230159741e0995c0c97e6c4ed8b6ebbd57f00e71193bbed67bdd588c316bda9d086ccd604a66863fca35daefca8adb42317f2f4e1a28a5bcaaf5fe7550e67955868fad0f034aa4d763b16f80d4024b97c324414d53818cf5573fc030f08807048b2aa5835ad2a59a28313e34b29d18094a8756f02e372dd5b691db0221418b5e1d3b19253c5984bea4677b568a8aed3e4dc985e3fd82c145834d8a232b199e5733669d160dfb82c17e80038853a7f95b41515dbb2d14495f0b0d45a5a14a4c5b824355eef01f21a0227992c722e2789a313c7a13b149337213ced718142393477a7783498fbbad9ed247af67d812c0a9f09d2f328ea3ab39953734795c173b12fefa328e8fe8664de17befcc29aa1d2a04e129dcb24d9d7d05a2ff14676d1fc6d62623325416d25151f9e543274faedf3fd43738033876b58eb1880f5c61254b1c0e9f38aa9f88b0ea256e6c63510cf66c985bbc1485df41ec44fab84c9a9bc396f07bd64188fa32a103f422aef304598866f2af487acf777ac5e0a5a7aff965353f13afbca442cd30589902418700e7736b584246f535dd7fa6d1eab42cf772206e9214194d5b6d7ab84f428ff336e07ad129e67959bad70ee552af13316cafeac5a4e7797c84d9853fbeb4e211c5db82efd49edb1d6e9f40a4758e5e77b6ef5ef5c5812fe0c3c94e7796aca38ec9db6237853cb1835e363d632b209da6efec1530e8d389bfaabd31e59ff6f56b676de021eddba7794e5560b6ca2ae3e75ad96e3c090baee433a99f6a76df4f3cc991e96ddb5711e2174d1f836ee2fa45d7ce94bf9ab5a4e52e330ba27597d54e640e33def5931285bae60c35a54601828e5b15d4121f46360ca518cd49389b191f27e18f3eb6219236a48c9874af499227b28f30dae1a5a006d31ca201dfa13a0360dbe7ba2dcd11d05dab689c30438aabed8fe002af48bf053c3776ce0d9545974503f5d96d2e810d9ebdc2d9cce1b9cdacca9de0aebe145ccbd2f453b6b17a87449a5fc2d759a0c6dbc5c36212e0b0f67e8d8d6900246495399c39b98aa1ddea149a35b0878c3745344313e75e8397a96ab7302796692b4fdee74e9c6761e56c1528ead5f10e3648356b2c336d411fa0af370950d42e534fad9707d2f6345e63e69eddada6ba726dd030a85b2dca6ff1b4c50b7fa742ef8504ad251f046f62e8d65c734a0243c94242e9d32f995efaa285a050bfd2fd3a992551e5fb5c943eb8b527910c5b650cf6759db903828ea2a7b5de6a7240c1a8a9c0d2db325bf8d96b8bce22bf4ce97b2c66121523a4fd216842ebeb0b3a46f11a8944ccd158b8c53c9db5631ebaf8cbc28e3d8d7fb5847461ae0659bc3de8561d80e883220641917a39dba935d212ec17742cdc416375799d5db23a29c5f7d2764a4ef0c557c8381019d5e8abd783c8e4a2ab4ff10bdbc92e75cd75d7ec583505ecbd93226ec645962f5532b14d751918918d027726110a02d21add89e906fcff9b16202626b48902fcb3f819117b52976ba385671e4fc3c722d049d3b458efe4986baae15947be3abc9690f84f87876200473fee8ac5066e2d5c1bad1ae518a40f85a614385dca6e6f23cd2ab5a5001128ae2d6b0ba9dc51405f8c44152f749a17f583a771e07d3ab1cb5fd53b0765f715143b539a66459f33bcbb7ee7f72fbbea0fa9f7e3b73c6335d55358982b1b9348105002965e076e3da374d4471675c958ba4a30bb77d73c5c2c0cad561cd31981c830a971d465912ebc6464dff3dff8f970286bf15bd944cf403643ed37349d9ae4c5918e183c4a2315dd59125e6ce50eec533991ac740bea074e4dea58c4ec43337a743ce73a371cec1b36e05b808f5b4ab3e6c9b293a60c541a2d559895d93c8465f688c919fed19abccb1b0261a63faa50deedb656c4b9637c4a7edc7c47273bd7d2f0e875a0eb29af247790e64368076b016a3e1c592f4df5660a560734a8e49a2c7a8c13d95082480e2599625ab420ec773c5b68ab7ad85047bafc3adc3e75d5746521e3c07930d31260bde9f440f5e2812239e843b93a2911f0a128f4f9dd43cda715f77244c2e50a84faa8eb36cbeb9993b55f851432c8a0d338019bf82bb065a07827f062e38118c9dd8f0c28436b4f89de077d4606f5848d1ee45024b65082ac8fc8811b643bc02511746bb95d7381179a3300ca7b23a2e1b5985422a2de05c8fe4e8a39fa76031f1352c7be4a42542f38d99398ee8d233cfc4631ca45411f3e64d433595de354049654dd79458e5f5bc283bc811139bf76444a0fc277a54ccc841f1a27f3e580ad2d1c037ffee45d3b656f4de7533c66144e80f81e28b31de2be02060ca6578e24458a303205c31a0a07a80c2e4db3e619196d9b898709002000c720c11d1a86e649eefd0c69a99eaae72172f022a2f309f59937237a98e136acd3f0e962d7f0aac201c2e848717a6eb14c992a31b3075ab52373bbdd7bf904f9b90b7516625a1fc5af2508657aeb9bdcc178a82159a0afd10be82c0d851f61e543c7c65a4ebb0a7ebe17e85698b1315c9c066d18d91695c6fda692cd5e1f34d85df698ae4609836ae59d4808f11df0b79c6196a0600767292d5ba473beb3c91671c9c8d606cc3d84a8867963affc8efe21a31c18bae76db6d011baf46561a5d7ededd0afb0b5414d07d7641aeca7f8b90b01336166ed4a0074cdf9abcf5b1b6f262d92a2c3816e6c40fdd12a3f7c0f495ab79ff9890fb20623625058d1160f80b39fb507671a19d42a786357a9d0a9b4ec30a37511fa1c10bfd76363ef2882005817c54d15990c35761d69197e5ac185067d4f4561a9ebfb210d72013a31e56f57d2004fc8ab8998554a3ac0815702b53968914c035e3ff9e68484eca11ebc1508219664537625911004b473ce4d70a2ce8f84f388bc8ccf8af7a5e28fb3ba18973db16d6400e36899077c4cf3b6a25b0b3c7e17762a10780f1153758c2957b2126d11aaa5cf8ff319df4dc093f32d26b446e320f8df52ce63b654c3e2eea062f0aed055fc9adeec6c1ed2c9aebcc456d80edb905e6c409ad7141fc4d00806eea41774acbe0aad09d53a9c43c342ff43ce934a5e582f18460df847d331e4e3a701a237447fba28293165355ba0520a50fd78435b9d11f258d2dadb068c5e419b96135927fd8e0dda1b32d07d2f865f42994c879149bde118ad4597f32dc5ff2fa5476e291d301258dc360df6dfa34821422f30eea111f02c8bc2b199f35e145d05fc4908536c8e4165863d29e7ff3f55d08734c29bd5bb53f04bbff391966ee0e0f07a732225b1f6316f259b570098403464021ddd1a4d538648d247030a749a38619384c9dc34e1d3555a4b5480f730ae946539debec0555d25195b05e75d2ea212a2b32e889c8a3d5a5dce1fbfc52c47dab30b1c0ae342fc3c4c1e1e2e0b436e6d2df90837f66054004e1334c61a452fdebe012900011bf31b21ca33283f6dfc3994d9baa2186542a25ed9394d8f92e48913d8af3484707d2eb9fba62dab7ac1411a7e3a697ba561229fd4b8f09c2f13c52e7b651af585e3fe12b4393c76aed160bc0b9ac33d87a2b264eabe563980520c1084b6c431d6b148183690d1a34cc9448376645c0b7585fb78aab6e3bf80c7b39b116b00f0ea671553cc35bace5a4b1a085ab3aa9a0a413427e4021d8746e6ca0d6da2277ba027cb1335ef2b598d8d0165db7e3c3773815652e4ff3c86ff473bf08156296907344a4037dedc299391956136e3dc15028b879c80623530c23cc56b063acf47b818bbd09118115d1680c60eb4b8a2bc1dac542206e2b4ebe47dd14425345b355e857ccd21867998533028e93cd8c6f62e7fd8b7f10bd3e5afe2af9afb3bfcafea29edd111a1b253ff43a023c226619f8bc4739c115e561908b30f67b1d5a3485d4631926742c0059f5dee7556f2d4597aa1bdd7ea39943dd1bc849fa61e7eec093d92aad28b1c6290dc89f604f32aa21c48c51c04bfe834d48df78efce0f269afb5fcf00aa93e2b700858e040cab70c01844144ae66006d32a8a51279519dbc50f78295b86fef4c2934969daa806eea23919a8c54df369d67787166b5ada837f9ca89c4f23993bcf066a91b466dde5fa8eb3e4d76311b6aeb2615e29ecb6a89308b8d9d25b98c7992e4ccfb934725a25553240c4e101e883755bb8296a7dc6c19ad4d84ea060b11a0356c577eb8feb14b03e50fd27ac54b783dec028050d0f80b5110d5fefe0522f9f323c214d3c470668d3760f160dd82ca99fb74cad281da43a02aae6addbd65bc817697f804cc2b490c49f032d252b80e64fa0c9c422065ad9dd5272d7363a5d503b225175052406bbf91a084d01e1e93f3b05339d6c26a3bc52e9f9c9dfc84ec43d6da2e0002b406a29c6555e15af36b61cf0ed0c6f85ed7cc5fa1c72ef484af13d13b04655e9482555e55c71eb55c0dd1d6220f12d981911073f43bfcce9a7e3970a981ec54a79e2ae0f0674a63d3c8a34b781fe9c661f745523ab8f5017fcc3fd98aebde89051e45e3e7048911907842348de2f14b6bfe822180b62ef48f8caf15bb2175684c1b02ad848bd3fb6261c620a2d1af41c58938f5480a9269616a3a00a074570d8a4944b819de466d23ad1a041aeb0fc1537823bec0c510981066ac3eb27c85806e8b9c53171833b30a8b34172e011ea7bb7f2bdd8521e8067743bfd89e898ab8eca15307813da0146b713bd0bbec3455b819c1ab951fb03af80920c324545fe5a370bf06c945d22be44414786c6906960f22cd72b67980bda95d08ecb95281c35a7dd5a8111aa64acc5bb13ef98fa69db881a99c04caaea3ea85f6211823d60dc4c885ad51e791f9875b2b0c2eb1c48c9ce8ba288eb648361d7ff634d15dfdebf0988770e41847b0f19468f3a2ffc66b351431e9055ef5992c4aca18ae2825d2e7cc21398bb2c208b906768878fbdcfdae5e8407e4379e560e557c093c17daa5e08f157222a05fb57cffef8e7c897089291f7e0d0e274c0cae8f9d2496b4b445fed90572cae0e9eaaf8385387b977f56e4144cff5c275f9924819a2d0b6730dcad88352d730381425a986b4e84af7aa2f12dbf15527bb953fc55d6ba494a631885ec9b94d74055bdbbe480beaa868e213d063bae3ce88b024d7cb0f773da43ef945357a75e812f886a92abf435cd401f188644c459549188ea2d5ae4c80b244744e5f9055ff4c06314b628e5dd9e4d0082436481eab52a1a1d6ac4451a175988e4ca8a69bd2a43013419810d50142eb50da9970feb4d6838d40fdf5cd04544a95aa75fbf89c8cbadab0669a7c35eff9e574961e3416b48c635ee849275f21a2186a860f918c23559e0f3d55672acd574b2ef95c5a1180920787d1fcbb596ef0e1dc3d19b0b03807fbd812630fe9983929a0242f834119e69fbdec0c221d1879d8346835631c76bd60c2d0cbd45a329adbaf869e7d08fb5c9c7b07f3a8d45b1271b194c5382256020fb0f9c3aa5484f020b06cf23fe6d14faa2841bfb79b8aea20d9145d56fe11b317736825181cd1996c3fdb9c69c3bf25469fe9af4f4c3ab84660f789bb6ac96c60b51d9a4c272bb02940e608ec10f7896877114518297ee25393ae0ae2cc8f18b643314d5d0546a5141dba1ecaa9ee3c0286288a9b155369a0b36b797a6b05d3347aff59bc6f69ae0bb571c0b4ab0f6a47b3304e9796362ef5f90dd606df41354d27a85d7c90a909fd3855d2c24087de640f9c145ead76759510c61dc4f1eac3055d8762132a61535e3a5010d625c4161a01ccc55cc854d16cd0e5ce9efa7461642cc3a9188eb3a6a99a09e20da8b8998f8ab3c0216380b6ee856e369a4fee5756ad780a8fc64f03b07659a8659e6de0cc607932a8d033e5c1fd04dfee7328021bacfc273fe1b4d96688948789cd366c6029d7f03e681f640b9814c15187fc8846f2cc3fc241dad4ae826dd3115fc810aba6f8a88146ae91bab0f079d3b56efaf1c3344fe7cde4dd80219cce8b6e1074e30bd6fbc4cb6d67bfcfa6b5c7957ade5c03673834ffe4934ce08000e355ff5d3e04ecbc90b5387f9eb5805cc3bc5e12b8bcb4226e0b86822324b145a48d10f76c231c02d7576864dbdd19ea59b35612dd10b8068604ee4a9257e828099c7fb0dbe8f7ea0df9790cf884f172903c91a3781559950d961609be6f9c6b91c175b692bf35c227a551b57836ad7d1f5efe087f265d1e6e35360b109f24a6cfee2a317cf72bddcbc940d65e0b26121c6569da3db0e121333bb0355dbb872e404150e73d40801d9df081022e88ae667b6915744839c11fbc7daa100227d05a59a2643597602fded7fb2d4d020427c9a9a36b9b18adbd04b2514ccb872e8921aa4b1c52c07fcad5672e2935dd886c6b71edc6e0fd00c5c410019b61b7a3b34f098d6b3e0afb3da02f01b93bc08783db14ec98f9e58d77a006be9c81d5dfaf1da516ef2811bee5da9d597005a7a5c7a9f72d2dedfefcee867006c6f35ad06adc0e0312a59d61ee23569d3b225dafd0867342efb202ccc348bd0ba4772cc6fd21e34baa5db31aedc61fa817b53e4000c022c6af2f38d205ba092a81c0bcade6c76ed19898adf83fcebbe56a2987ab0c962abd1bfeaf8bfc09439ae6365a35d3c97340eba350b6ff9d8436c0626dcf96c1456eedfb11d3222f46f8e44dabd314a297840a608ec577ead447a78e0f0f56e4297947ed475ae29d78d12a01a7511c7e36449ac42edea2ac10453fd163fca2e6f69f9779d9f02c9183947d446fd52d9133955e304b6d112df17516f9ee8d00fb82e419bfc9a7df3de5d79ba3f4d3cf914ea2345b1e32a4192fc086e61fb7c8b2d15d8a774fc3be9648e823fc60b27bf74006627e303742a400769013eda8dedbbbf0462f40814abb5ac1992ceaa303fbb29838829c99e6796335677963d3d53bc8973d4bd65972df74992ecd400b992015af3608f5d1e583de7de86ce9d82d2d6ed865b89e6063cd51b8dff9e65a50654ee6346f6382ba8d449f090c093d02f97e9d0a15315d7edc68c2dc329567e7fb81ed27bdf8fbdf2371ce01af3b66a031588db0038de6c8c0b3724f82c3646ecffa796190fe6f804b703157061cd044cdb3fe63cfe10efe27376a099bd63c2cff6e974c2d0cfe978f78e9008c2daae22636f6d4289bfc870f6596bd670cdd7e531fcc338513c310e36fb4ccdb10e5bfbe1b78facf079773775b5474ab0a1ced517dab407adfa47cce51ede92e01774110110fa139056b79065d70117fc81c28702f8a5148ef83d8e8b4679d93cf1a224d37df056ba8d30e4f93572b5aa3396194667518a49e37d3ebce093b92d430f884b0162cb088c94a3742480e61c03c12ed66adbcbc39212363645e9e7af0da4f4295e7dc9a665de23f58d970c2ab104729529e05be6c649f4b38379ff7f9ebe384e1464411ba0e1e417ece077352b0a98eca3516d550550b1a38ce9c844db2e00a12d61b733b4defa84c1b1a1c9ef4627a69d1789507d1fdcb21f6efd04446f6f564d441c59db19a690b44355cf63b37ffe0f36b77dcee95be7e7f655b15e6700f7377145b2cf2995a19a09bb3f8c63b90891e8937f7d0c42731e956187d9c30404c5483182083758b93855a27814e3c239d6bf9c4a0570cc5b0aaf7f6f83925443ade8699838cef65f47487ce8f8096674f5624acb14bddc92d257d64962a5f4184edd1757d9a5d388f76ea2424f493f62b35cabda54101e258ec4468eaf9e02a602e7e5363e0cd8f828603c332af8df2cbd3bb203dd08ee23aca29c5e606d5515e70bb11929143274869ebb136ba0e2d26dc8668d32072e78ba1ffaf441bd00f24fe70a3ecc6f00d073222b3aa893e4d762d283ef179cd442c0e5d218eb788b869c6f32001184c51aa38aa4248677c54404201c607efd542d88627dfd79387b9b57d2dfcb3f766beaf20fea7a9882698af8885a0f5979feb7ddc7b172d36d0df77a7a23d5011c589465d8e624a5918b7601c961c9c8426e2f73163dbf4b9e1f967eb96e9c428dee93ec961c547222cd1022c241c8be2cde2e65a597784a0847f2a1b9804cc2168c687ae0d26b5e55800bf5d95ecc0a00e03e5cd4b415be98885705eec1663f22e4ae3154e2c38d3f94a2f021efe0d9c30dbd9434870f566df1d5afe4159a8c7efadad4eb3068c7182c5197609c8edf7a25105ae0f1524d4dd846ce6674ff051e2ce336c5f15d8473db1de64b99298dc93ede24352acf80cf59438644523879b311c2bd0d34b4245b38b1d069b9d8689d522e4f86244c60ca570214d7f72dd0aa7017fb7a3ae6e857d4a2899fba1f4017d584bcb6eabc74ac49ec2287e5d757bdb3e19a5a8f9d368f82a1e57619741f21aaaf48d425285a074c449d456f5ccdb720d2c9baec9a9ba1a36d5ded75c0661835b3b9521e7b9bb942cf54a7d23806120f40cb1d621eb2183e6a33af99deeb35bfddd51f3da579f5d8ab208a21e743314bab8d9262a4f70b83dc2ddc57d64321f00ca845c1e59b46046cb640da37762e2da4cb0241ad43103ba6f6e845a208e59361db4ba730d9973b86f8662a0ea877acfc750df46cedba07d11a51dca803c7ad843bd034a847a7985d967d40c0c040604030600809eb957a7b60d7e9cdd25fc6a6d15d29975ab95f20804c828c9debbadedbda59452cae1078d082d083e982c79736e8cb46209ca0c4840e4a4e121e6dc58898794374a2e6c45f1293937668252264c8fa5394e61e7c64d29a0ca7ce860d222c39b73e3280f5a80f5b4a48615ab37e7c63e13d0126de6bcbd7050b8652f7edf25a77bf5d6effded1b2626e6ca54942318580072ee8bd4ab612a870ba2c070eeab04015a22ce94396fe1bc754a766eeb653960b7d53ac0570e0e404b54df35cedb4a66d4658f9cb8b8f959e9724111323ece6d91be98882ce941266cccd5b9add257385f7e6501e5a0a425aadbb0281a308c1ca9418773ce8d272e64e0718483c9b92b520868f8d2e2c2ab86185ae7ae4db407375736864c4125113977657a525375e30a07961862ce5d7d2b5e8cd9088b3187878b38e7ae516f7f74d72b176f8e2c11b5da02f6d653a02768d9f5400dc2a7750e5822ca84a846bda5f1f6ea0530a6313016363a8004363546ac6a851dcead9f9249616903c313981f34acd7180cb01b4d64a872eedd6fa07743962a2caad0509d443968c170ca02258b9c538ca33716df68308acd57cc6485aa571f79488a01007f3d63a6a6b1dff99cdd83c45c95a6eaafdf2c363cc6389fb962d5cb22dad4a1377e33873667681d2cadc345d7a27411bdeb96af0e6ad1555f1dcca2674a7a579fd140e11661245bf639a771fa9c6f11f565bf80082360244a44c11e2361a51914ecabeb243d13a2c52bbdf18b561f7a8f75b5b21032d2a06dad5432245a66cd6429891ab71b327a51a7a56eb9da0d89782c6c4b7e537500d3c23628fc91fd867022fd792d21fd66621931aa2a51b2d75e6b99465a0f1687d6ee23096287068146d3283fbc9e7a0df2d7afb82d9365b24474e816b9e3705adb5a297edc2363e401d31e1045ad7138cbd344574badcedaec41549a060d66132ad5c0ba9d59097b9847f416b1eb0b60cf9e7529607d733e17d0a58063903aaf6ec5596bac8043e9bc391c83d4494b0fae91d286def87daa53321d5ef84e2a674c0ad2495dfc8173022d49117de8ea348a8f2757639693baae1c581d384ba06c083b5f18402b8c2f0cab7f359e6569bf6a2a78efbd2d7830ce18678cefbdb76a518abe96f6f4e60ce60ce68c31c6b5d6fa82d5daa5a6427ca03d757befbdd65a6b9f44d2da5a6bad6e7e17042ff8f3e318fcb8836006fd52e991520d6caff51674ea22d01355050813004371461bc91285dd95d99f12b951072830a812803b0643463ad6c01abd3a1217863ee26a2db4d7c8eef0bd1883b584ad6d2330e7306731e759ceb49cc79cc99ccbcc43a537ad0ab1740a1520d013a0e8d83bb0bb323b0a9b004af463c2a8037ae282bafba063408b6c54a8baa8dbb5fa25dd55b73e8212519a735824a90633a69146298d62f229cd292d7a9a338016e1e815e909fc3437a21c844ef3139508749a2ba01265a7f9494f60a7515ad2663aea24ad1c845b7c9aef72976f6f49ab22ad956273e6bb2ce92e7fe3a8ad25eacde96a0922945a4a69ad23c63a22f6a5abeb96d36f92e27a8d8cd6d6686cf5f0580bb24414ebae5f2b2d6f57e05ae38cd586561a7af31592813a66248aa7801dcc3a15aa0f353f7a270b54d0f5ae97191cbd133bd6e52d5728675da16a6d261b69d0cebfaf572be078fa644e19fd3dbdea15f9ab444f5c1fd5a07a456145c4a9eebb5b34002c6ebc38f5ce4a86c20918a234f97a673dab6975258a52efac59bdde596d1c254a3dcb747b481fb27480f5ceea76c966659585d9df15aa7e5ab2eab5c9a94679e01645dd220ea63cf9ceea218c60a36e113dafd215aa286e113d2bd562ad62cd3fcc355a9cc14b4bd051d8300c75e834ca871f5225a1f261013efcf16118566b44da7e82843deea99516d70d63b846f5b1e28a31c65b9ad67b63b859b474f550062bbabace8b2eba7a2d55d1d5cb47165dfdc353abae36b4a4b59a97ae5a4d9e4260a4b40a04bbbb2976e8ed3e84e206ed48d84c7190199da2eaebdd36eb4435cb074f13271a77afaef238d13c54ba7badb5a013bdc9a796c7094f95c6d9eed5384f69ca43a5696829106bf56cac51d51eb9f65e1cece5a9d2389e297d75949c8921485e8c71ce190cc350146733da9007f67794479e805ddb3a2011445e1a8a241d411ca9b183f6e2dcbdba4d8e23a9adaef6b419475aedc519a4c2436575b4baf855875a8dd6ee56d87ac248ad091edc176b89ea1ed86ad3687da4b5fa687324a87f82eaa592a7a504c65aabcdee56d86a7b637d8ac71d913641a0b4e9f1144a1b1c1f3e85d266c6974fa1b4e95960ac36b49ef83cb5ea72f4d5a9c9029398c45f02f96046ba9dc788b61508a57fabf5bf4ab9aaf4d7ef454135b84eb18e036cc0f2bfd68bce741c604315f55147218abefa00dbfbac534173285230d1b31f7a531f4f91d0afa750618928584150e73bf4ecb499a780268aa0a89b006d4745d1435187cbd507799e34d6eda1a740c6d90a1b804a84ca32bb318da82badc411660c58848ed7ad9603cf2c87ae4ebbf7de1914aee2957bef0dc38c33167a8be012bdc5d28cde22b5beafb5b32f7fc40fa8f5bf964ff416f37d5c3fd838e8a96e45017e14733fd2c89ad3c93f92e409b9ccf8deb2b495ce6acdcd74e8d96c56629ad6d7abea7c3ed095403e1ec9ba71d5e1325873e28f2229ce667b452547da8cd26b2bfe1a3ea975d6c381cc58ae750a0473c5f6d61b829e05638c6bfd5a97dc2041904b915aab954ca7c6213a0b31287a7d4c29f817cf280d310e970599524aedcce630152949857288cf362a949d82ba4a299d51edbd72ec95ba380c6734190cef1545ad7138f7ddae7412eac79608fc20e7c71fe65cebfd30e7fa41fe9a6b0dae4ff040729ab2e4830d95767a8b488c496324860e1a5efcd05ef4db26042c0037c478d30b50f24c9cfe1dc73b8edb7171474897dff1a192b7e3543512d4c46317733c76ed3b21381e03b1f2d84b943c6c84cb42e2638918951730f01904dc90e389cb9117bd169c362053dee604287936d7a0e46910437c0fc249943c10e2cd71b79b07dd7ce8e6e24dff8dc8df6e98f3e6066ffa004a9e9941c9c32e54f26e4638338ff35dc9c389b6e7117b1e1e0fe2c1cea3d4ebf5609c3c57aae06c35ad9141caebe4e152e6c21815af321727dd7a1efc8365c46f121d08fda6554a7e28997abc0486c74bcc3c66c2e43166d2e5710e373c7601943c6c4dd374bae99605f010d2e34cd98b2132d0a1379d9a34eb4df04d336fbac7f0a6a368c19b2ede220e7c84317d690961b2c3692a71e14d0f40c9337f0028793c5edff314e77b3ca8e4f538a1f95bfb5be6af7b1089a14b64e9af792b947b7d849d7b89c4f9bb73ab946bf3cbe3bbee681afb7bc9fcae575eb16e4e8394e5d861aa0990a3f30ed981cc963132c67a38b7feebf41600e98c0b409ec67c386eed69a085262ff812c3911a48ce14d45cbd008d989316acb94f2ffc2dcbdf1c839277c361f8c985c718bbaecaf310b9e1793c57f2788e30bd70438585a31db29c5bbfcd16f636176fd107ccec1842e497c505acd346c4c7dbdc4b9e2dc4ef38c9f91d1f51f27644d3fc79d3349d5a78d3c9ebcd216bde741125cfd4af1da7879479ed40fe53f2eecff71fb0bcf90fee53f23e5819f235f16bb4ccd76a35edb5da15395fdba9e131350fa1e622d46ecd690d4b99afe9d4b0929a78d6bc2e5922fab59a53f11689f84a1a9b8a210684e9ac353dd101030d598ad8a8495d7991e1abe1668573531f094dbeaea0b4b09981aae6b4561bfa5d0363917aec21705357e8af8b567e7fafccf05690fc7510a6699a6e3aaee49918bcb692f4da7b4a9ebe95bc9a8e539f0ed7975fbae0cbb3fcd2495822d34b1f32e2e1cb0e4a0fc1125d2f5d845a59fa089cbe2ccbd22b8eb2eef8b2327d09834fc9972037b6bece52fb4aa79b967d597b5f5a65b978addd56f23416f3d8c51b78c020bef6fb9a7f2879b515467a68a9a14bc9961224270ef3613b54e1d10298b11acefba4f4d7754ade75b38a913779656d7a2824d985ec224b9224b7c80cc8af271d932e3a09f2a46b276d9eb4baf2e413214f924fa49ebcb2f1e4159927dd2c79a4f9e3387ad038fa902daa4ce3383ab5f2e39420f337896da283124fc3fca5325584aaf7d7714ea5f4d777607f7d884acd5fab2651deaa8a86bf5e96bc8b6ffc367a8cbd3c76fff2d8459fd663274b1e2e79a311019ee6b4c6d39c26963f739c25c2603f73dfcd7cc8cedcc8888b9f752035f3106ed12cec673058e02ad371c58613937376844b09c80c5c74cc4873cee8d5959f417d3dc65082feba868251240caee4cdc8c09297993ea7a2863a25ddac068d5be8e0163a1bfcd6c1483a3a3a3a3a606f5d074b47ebadebf4aedeead039bf758e7eebecf8ad73550234565f3d222e543939b78e16ab2e6facdce0f0c5e4dc3a586ec8414a892bcb776e1db135695e403635c705d5b9759a44e0a5cd0c37aa98d9f23ab74e946f8bd4961e5bc43439b74e181d66cedbad6f9826d365afbdde82bd75ba7598d85bd758b6e8b2b96cb4dea2a26b8edffac86f9de4b76ea1654a8aca0e2f39589d5b3b65bd70c31c222c60c9e1dcbac71cae2528499cc86c9d5b33bd9d7a8344eb07123152e7d64d1fc81155b68255d29329e7d66f6eb461d1d5c3ef4bcbb9b5155ac2ca9cb7bba655d3faeabbb6a5a5a6e677cdc9ef9a0cbf6b576fbd9655d3aa79d5c0a4be3cb921568686d79473d76a50e99023c46a07a832e7ae294dd130838e346e9e1c39778da906333e283441ca689cbbb622062670e024ddf0262becdcb514516062ac318325e6ebdcb51a68891ae6bcdde66d32cddb649a5e26d85b37b16cd1f559a1ea3e53ebad9b5dc8dfe69159ab7e9b2efc36af6a1c9951c3a969ebc9b94da41b5173c814d18185eadca6d22c87df171a3a78f5a8ce6d32a550c6e205178d252e849cdb6ca261079b2a45b2b4093bb719f5d5260d12314f4dd09cdbbc35ca5ba3f4df259c8bc30a55dfa5188e92cd5b2fb7c894457e974d7e974e6559fecab0930b961e5860006381e4dc25125603a4abc90c32358e9cbb540a2bb2552606468ccbccb94b267a068b0725a5155c00e7c7b9cba636b4449b396fc932b28c2c23cbde3ab9e62d29e737b944d2f09b2449f21781ae1e584e5a76c8e2e3dc24d2882d3856472f4401513a37a924001d5f36f4d042ea880be7269940a0c482960cb030605e9c9b7463a6cb8f16689a90f0383719f5360a0b489cbc8ce5803a37e99b424b4c99f376cc1ac3de8e366b1cbd46b0b73e62d9220b66c72d392399df63eff748f57bbc2ab281032acc922f3b769c7bcc81e486992a6f5ac0e8e1dca3d20a1a3dbcc2cc80d4e20992738f3ebe6ce9fd58aaf1c5eadc230f568e8ca13164ca8ad8b9472f698c2c65619ae1abc9b9479f119a111aee372d8a16458ba2d15ed068341a8d46fbd916cc6c0b663dbf679669669966b31efe3d3b5a9acd66b39f15adb8c5ea48a2452a7beb624f3cdae0b7f8f45b9cfa2d8ae2ef024e52c8a2b49c2993e3dc22d28a223dfc70c1050b48326c9c5b549a3ab3c6aa052b2a58e716995440464c0c5ea670497939b7d8f4564a8f2138baaab47ae716a35080c16424490e921d5de7167d6768893373de86642159b8c317747e872d7e87337e87307e876118fe4eac59d28206264e64443977889480177c3576a8e2c2c3c9b943a59e245da4fcd040018972ee900a10b02730d6f000c605e70e9b3e3c9933c3c816234d66387718f54e9a6248a3e45ba3a5e4dca12f0b9805dce016700b0896d17e8347200882e04f481692c7dfd9293b65a79c73aeda192a1fa1f99d9df2150c3078cc79c18289d396f30d0f1632d0280325ecdcb9e96d0b1ce76d6df1d53a86539170d85b8cb117067beb18cb16d51df8373e42f21b2f0df98d9d88fcc65711b07286ec45342c8164ce55304dbff08909c74fe4aa61cb8c0ad212ad86ac712b29ea242998185bce9c2b800c45ca4c32e7ea4f47afecc4391f2ce758a3d2e64a9339477d4872ae4e9cf3d1e11c75a684957dc06a616927eb852c5b9b21513c55ba946e447a18e3eb01d13bc710b6d210317e0029e33b891cf1c80ae1b3e6f459d33b471f5ba94f183119563bbe7ce708e2a9ca15329f35206c3ebd73fcb195fed429323e29a6730c51cb925c40fad12184f87c40c1beea70cea7fa0f8cf30746efcc399feabd7304b29502f1b8393a73ee002ce88083ef1c7d9a900971d25f73ae3a5dd1423a71ce67cd39fe58f1c10444e61c95e21341e6dccdf104e91c61a5948c71f22a038c1cb221289ca367cea95079c09095733e2bce0c7a9a9e2b583ae9798e189039a70275c242990a3f30ceaca3f6009bc4409773d7e9743a5dc637a8f6224cd889733e2f38359943d3d52ec1e8af5b9a739873982b26b3d5fad5d51b667763b544f5eb955a9b57bccdd4ee154fa9e79c73de275813263618dac08409a8a63037739cdb99046cc51cf62c8ae1d344517415664933cfbbc6a825a88739475dcced9aab316a89ec21a59e735893727bc55312849f0b73fdab5a8219a417ab19672c6c0032112ac776a31add68e5e5d5086bb1678c45608cbdfacd3a7cefade6e301b9c625dacc88ae5ea3e8ea33ab71d6a28bd79f60dfa4659eccd32f3f66acdb24187144d7dcb846531f7100e268ea2b1e63e06b2ef75134f59cb33292a5198219e36b6ba534934efd38236b6ef682a6ee53a906b4d67befc5f482a2144da3a56975db83c7515403eab9f71ab5ba1cd134324f7a5c330369039b71c42ea577c65887c328955a31959e00d97841bdf54a56abd5e4e989eab8a4565e4b5fe9899c010b5dabdf0ad0354f5bce889141840acc49a950a5276c062c343d513360a1ab8fb38b67745667e26c667338036be64ee1ad67b1d91eebf1c31320584fed0e8aa6581a3fd19b6645d19b5649d15be4b9d25cbacefc161d4b63d1e5ef7acd0a3d6d5a1a77492133562cd8aa4b6a0c638c291572ace3001bb4ec8e6e6c8932896be9141b300a8023b03b9ba3140441d05e2ada5dad2482aeefa0a722d5614c79c474f5a07ba36602094e101f3bd689507a63fbb888e9b44eab532f79f649b188aed6fe98dbb50c3b1848da2a07b6f737084fc16db9da5915770853eb22d8229c15ba602a07d5823d1eb2bb2b5e1fe2401058091cd8116489e8d3cfba9a985bf2b03dc16f1590deea4660779583fa55c6d12d1176b13aceeeaad7c6587b6216b896e162685abfb0781b041684443cad8fc8d759123d8e95c45e7d575de3ca2fdaba4e15ddb2a3ae73a54d2aba567711d098691a5343101d74c8b9ddbd7e8b2897246532b4d0f0b1e4b462062a365ba0cefc48c3e2acae714af4367a8a6f4e04bba34edf3eb5ea8a7ad188b6baa1ece222093275675c2a4930121dda28a3755e8851b9d83229183f8d5da0f2e540660b4ed2a10d4ba6755eb0417f803ad2a215180da455111f49d668d150487b6ca11169fbe972c5a6197d5a1445abb575a5438b54ec101d6ae969f2a9d55617dcc21699acad35326cc9a9557468e38bd67901062d5a8dd12515183a8ac64f1a7c6ab5650d8ec40e67d33f412abac800c5c8ddc87d22ce88cce8eae7f5f7a2673dc63da6642f3ed3e3ffd07bd1e9abd71694038c444b54a7315e3c5d81831aa82b393e186a1c393f80a21a5316c54248d6b9ab526d9a4106c6922353ae66a038bc204b142c1e33d2f468972f3e2c73be085972ee9a65e2054d569c10e9214bd8b9eb1636d452d126bae645ea94fa53c751a3a73e445550ea2350fc3404464f02e8a1534f01851158a2ebe3e98fa78e82fa8ebaa6bd5ecf066593d5ebf566d01c3e671749da8ddf419f7def486ca3cf8e02e933cef40aeb33d5b2d24a92c53ef8a66c8a707dae4735a9c777c5b1f4d92b1393cfb7dc1404e7bb4255df10aecff90aeb33889c7358150b51d68bf3d91ef9f8b648493fbe2d8e2526ac2f9edfb64710a8cfd9ad6fc767b755d9627dd6b2bf169f2d18906f1b26f6d9dede8bcff7e86dbe2fd24dfa9cefd2e7245a06f81d8740c9db4192e56d369bcd7739b7d9925cbd8db4a9b17908964884db5c049bcda9cde636174e6ebcfca45cb8387fd810464e922d16645f36dfa1667edb7ee7e777488cf96de277c67ec7f18e8b51507e0707a8df916ac1efecf890d497df71104ade8e08118e1321c22992322fc21f50f244e49c6edbeff7cfe71ce77247607cee489bcf19b1fa9c3ba0e4e53e2879b61151aafc085751f24688b7074af3403b0e04647320a0154cbeecb04549ce0e5f309c401f00bb42038e0c38c2da0045c1e18112f0dbf6b69fb74101f2369b0dca95b745a1f136f7a0e4d97c67676767c71b50f27678250f684a9437dd344d0f22618944b8e943545cbc7933d198a68b608e304dd30472d374d3344dd3eb55c53275a6699aa6699a2615afd7b9ce53943c9d98cbe572396740c9cb39951def7e963c1781808080fc35f81153b87e84a3287923767676767676767676767c01256f4744d98b7005943c11a28dcddb3c0125cfd641c933b16c3c7e4cf6187b50113eb1763c46f3218840c1a8d863ac228f91942cd10e8e1db509eb710d72802b4b04e4d82b9656f57afc26873566b4c6a492cead1f339123c683982419a69cbd206ce2b0b043111f6bb0921b1949985cb93202e7dcd4d7ab518f81ac1e080808c8392879400e72f03b2eee6009f91d3f51f2766c369bcd66b3d96c364740c9b3f9ae91690edd1a058bc69b2e9a58496fba51c9338b4a1ef6ff91d2e57f9ca8e4fdf8f8f8f8f8f8f878d6fb300dbde9072879a618124979e1b19b2879d80d50f27c8c42dca0f5217c83921742c8d56b9ed7dadd12d95c7b90f6217dc3d46b3da6775c6b536b1d385b11cc19772973058915140c6c95dadadadada0a4311ecea7215294008c15c3e207b08823ab62e8fac21f2fad5d9b42dc4199bd2d3d6d3565757176e6bcb09d75a6bad15c536ca14a43aab08051450e84ccb7c65434c5d74f51830fe82bbbaba30be557430e842c418cc983e6db9e8a27fc11863105f01e102d7c88ddce84b66e1669a31a614535cf1bdf68b1c8cbbbaba5280d081d1cd185f298bc12b8e2f488a5b6ceec520f81464d36e9492b4c6b991095a04ba1da183a7238438d385bbf495a1ab577ba33c6d3d6d3de5e8d1d193a3c7851e1c3d377a6cf4d4e851eaa1d1d342cf8c9ea41e193d317a60f420f5bc00d1d5e3a2a785aede73d4c3420f8b9e157a7a73e4dce2e8ea373802c018c9ed0da6b71e6e3cdcca6e6e6e3bdcdadcd8dcd60cdd6b800d4234b504a5e5ea49d6b1d2d35ad5171908ed3297eb63aabd4a8b7402902780a359d9eb0eff186a0cf4c411ae5714b5b634cba906209c381352140c94b4b4b4b4b4b8b8b8c0281004adb22af7deab775e469cd1c45918e2d9571cb4b8b8b870546b4a581818d6b3729d5c5efcb8b8b87ae8fcb87e5ef7de5bb57e6af08f8bd65a2bfd5de1e2e28a32c24a6aaad695d6947026d24632ea5e926be46a81c675c7686517d872dcd2dcc86e686e63b73337b19b995b991b995bd86dcc4dcc2dcc0deca6c30dccedcbed77fbba79b975b979ddba6e5c6e5cb72db7ad9b961b46ddb2607ad3ba61b95db959b965ddaadca8dca638905a4204c37b2f78c799e6a277cec5eff511e071659181d0ae87efd55adf8ab5d65a5f8d20c9581d04154af1438b50f8892a2e0974675d871279c0ee2c0ab7a8ea7635a92659a25d65fc09faf8aab8453b0f9080c369bdbbdae17062097623c04355690708293d0c7217dc91184bde0f8ad33b7731e721ac2285915de35fce74be713a6a85dca9d4206226ce421104719e3d582b14d04fa69a8b0b38dc0f082dbd3ba877a8003ba19d7b9bcbe9e4723b5a34c28f6ef9514a290a151310294e73d118e31144516b1ccebd0ef9bc96b2328b9106d5a74236052d0a419ce87af7a1ae1e1d2d1d2dd5a3113c70916c3dca3509cba0a3b77ca268ad16c98b711eb4b33bbf41eea28b7e64f45a771e848ba1b16882585e24529dd18f1115a3664898000817b09eb191911bb99191d6888ba1a98f346702febdbbf45a4be9dd699dd9ee87ee7e9c9e20ebdddd0e3ab07dd1203b85ba58a4c20a59af3174c90b29171529b01b5d19d4c6d038130505c5d1db66b30ee21aea682545f136b1e18c0b77f8de203d04e292f4bda0484f84e35596118a8a8207f1ec73b82b64e9ee0a8962034abc076668a3a12b64bdf6c0d8fa369b2dc74dca2dca0dcaedeaf6e466757372abba35b951dda66ebe5b0e3726b7253725b724372910371c6e51bafa0dc9ed88ae7e337283ba15b93ddd88dc9cc617892e298638dc8688b71b6e426e416e406e36dc5e706bbafdd0817645abd2550c2905ef059331d500db6c3bb67b2f48d2a0589c8f8072efbd57a4b3149eda9d978b73ce60e8240cc3300cc3522b34cd843af41083a178af19d16b566963ce5924b5ad04db386612c4b6191749e632bbcea0e3acc360ee82e168062c455c9ab3297b71c6172b450aab31e7662ffe68c1cf8fdf3ece99c83223c5aa44531c6da5348b3338d3a5a8324a215605ce665dc4510c694122a96d25d86ea609bceb216d766734b12a3257ec238d56f2421f4912631cba4edff2ea5288555d7bc72c2425618c1883189335b2a6c9fb538482f6e482d000da631d8a269668fc50348e3a1428c6d18baebe71968846cb898f3fe8943fda6c3c64904d24f56da77c000936db079dd2041e7691529b88693bf54bd22c1b70bb9526f0681ef6d45ab338ab8123bd3a454e85dd893a146774755c2936b3ce2c71250d50a044614da15547da5d9ea2edce5e6bad1dca4b4830120cf923c190602e94a2a28e40d1235134ea080cac82a9bf1042a860473e9fcf377a19bd8e6636cc9a442691c92ab15236aa3a55272556090e5068bea10a55a3f0180ec361980c93b948aa312a12ad4947231986b35918cec4d0462d7d605ba825b25fb7b99bf45687c25af77bb3cd65246befbd4e8a157441c565e907f094aa8ad5ef12c812e86fd62432cd82d0df2cc8ac49649ad9300b320bc234527aa224291d69945a6b31b6f75a6bad0dc14c29b6d6adb594524a29a59496e468ceb4dc7b6fbdf7de5b7135412510e42181e78661ad59287eaaf7da4ab0d97b71cef7b6503305c1e0a724f0d868570c45b7ba200f22cb9998e9893c2695f45b7bfd5800a2ef53ab5fef9f747e6af5f5e6f6f8e221850a10c8a12fb0d9d719dc17d6978f8a459237449ce2bc215323ba518365c7952b676a9853b37e1b0d7d2db12cbe9818691271062f18863a31cec4e03576c30c0f4a7e3a808d39b17222468e08667c597c419136527c6af51504ccca8befa9d5d78e97f2d4ca4bd6db70f484e836fc6ddb607ebc83559bb0bfba13ec9b00f71b855eaff727e06a6f8e94670209ea9c1773340d2ef95bfcdf62c801d689ef17cb2ad9246a5bb04a33ec12c80a558f444555611b559dac94cffa9a90e36ff4a2a397f137feacb08bd9a9dd89a198db275c303030bf10c05430b09ce46a53af3fe13e06411154e16de8d7ef781d749bab48d8ba758cc7d15e0a5a9d53ada0718ebcada0b7edd2abf380ee480c99a07fb791d197106c0876576bd2584df2d9481d06b723316482b4913a841a238914d30ba0147f7083c3f97218c7f1378ef497e8684914be3aadf9e2a8b5ba0af5e2b05e7195c0b5514b50bf4a5fedcfeeeab5514b54b7f6e2b0bb9aad759cab98ec0787c1e8fde313b8c0637c71e41ce94563d2bed0dbabb08c309d3847579c39b7e21c6d8da5134f81210c7b51ca4e58029cdfa8048d777541ca0cd5cc100004010316400020140a078442915014456922ed1d148010629040744e34948763b124c55110044110a38c310000449031c4186688684701ef02e9e9f10d5928c8ff26b9734e1f04f79c4fa21bef44782b64cff33b2f6b34431263acd1a9cf0aeb41cd1374b78a6dacd518332b5de98173d1dcccff18a7a54196f43e579d8bb618906c0021fea9ba38a1c086fd556f2bf2d9050d1d075559a5579e76f5447f0bd6e87211202510d85d8595881c7c80a9cf069a92c668ccf222a71e9bbcbf403a344f859ac653afd79c2a37306d68952ae84a9cea103090d4819e2475e42caee6426a3240bdd616122147a06f8f162b742e4540dae9cf0a951ae0206f63548e2cd3940fb1a7cc335351f3b3a9b0dcf0d93763ee6a4eea2a574f9138897f8d438c8f7ff22edc651a75cb9bb6d293cf3c220ea9e56eb05738449466c793c48c33b89eda1105ed81bb1e33a5c36c4a2719471d5114ce19626ef769a5bd894d54d819fe8b05d3f510f43dd0149f5da6038dda77a442643ab0b63e51599e9161ce58441867c52513c2b3f7bb16344810dbb61d34195290ff0a7c05dc48d608632fafa76d7055cdc17dd1753aff69aa9979535a61dea977a427894a5710a2622eaeebf1269e240a5a47f2ea109131451ccf1ffe5367a0bbc0a74e8462888149fd9606fa54e0cd81a7e2bead170dafa0bbf65fdd151b543692095e9b3bada3fea9dd4180af400f92fec9e014b979b1ce02c019a78a4d81a6ebf117450246d3e46d4d91462222db459b2821d3ff2d38a851bd4ed6252d21902575414b51a54a7c7079afd43ac95e726375c11e5a26074cd93b76baa5463bd34c99f0a5be06f13d7f9641b299b4b0540fd3d5f306a64824185b587e75f765780bb9f10b5af5a41d2de15ad4bb45bd39cd2b07015f22f936702abbcd3e99799c8980b432e7715c981b376d5954d9de16b2f7089c7b4ce816db5b0867681f271437ce76348080387345dcb81406080b278d16e17b82935b40f0c32a64866e70b824dd6ebe28722686245f67e4804f6826c8e047236e628985b7827fd4c0076121ae477f46a93d6e2fcd286b270091dd88e0f6b06b9e503f01b7514cea2b52a3356069561365b8df78ded3ccdebb3c3832556c3265dd675174c865c54126cac226118c4462cc1c89bb1494bf36ea0fc7c11969937330578fe4075fb4234985809b38c547cc283e2af604e57f9b92c045976e04647eafcc66400617cacc00ced91596e35e78b57da2bab2811a6ca34993c11cd7195a7e471da8dc4f355c0235dba8fcbee36ac235c09ecad96d75bc3ab31466a426550e0d7cd2e114d44591a58bf87e66b6a5d12c2adce5ef6b50807cb8790678d5edeee501b427dac4e5d01fd93045cd6c2ebcd0bd4d7a5ce1159d08bb5025a6619ebb839a2c1598255a546a12d5ddf8a55412d12fb7dfb5ef5ebe6f2fcc62d143aba186ee84747d02ca6f002e10839796a9972214da513cc96d8a8ef20743ef19104db649d8aa46cfd5bb732a75763c156900c24655d942392247878222feddbe5cd4492a468e7e58600646d238600902c6ae3f6194ca4ee348b025826e317611eabb0374f7163aa038095d1c62e0900c7c1eacb408962e14eb14d3d4d986af0e9f5ae7eda2046dd85782ecb2004d1c01212af8486d0bc4615f192d0d190f4ce0dc0d5a45b2af7ab322aa4bd126c4d58f2e205054395d51681a766e069a12a9004903215614f227f740c11152ac9f9a54950ae162a990b467095abe984f0c45a1d3a904ced3ea25c85e1d076d587630a090096e0785e4ada6a63a6d30307b3986ea8237b4409cc7a869005bc3f18876e8b6099065d2a32bff2c0993a4938c188901c3655b4ab97a3e6cbbd596533a859cb90a283f4c85959b856f6fa1683721e3cd4c0b2727b30fc4611d952815ddb7272b351990a9e1da47219bc3a71043be0b787142581e7837420bac917928fc082c0a968c0588a048ef610d1b8437a9c139e5fd04ecf810a2c990baed356e8ac0b532300cf46c83eafd85a31a1d9018e8b286eb4de7a286f41664997faadae4e246620b6293bc0b29820723861972b88a4d71081d209bd183749a6ca0a0fb035ce99beec0b0c56a56c974a49dc25e58ef28908996b72f783a72da51b76b13a454666d829b5a7ddf1447dfa26f9fd9eec236e9518f8a8e646daa25a8d6a34de7458418a45b2373a36ada75d4cc028eaa2786ddea03776d7d0234d5f48ba5e978ac7f68076967733ccec9ac03bae1917267a9992d530734e3fb0172b242493e3d0a97ede939be4ccd985a74e7e96a2e78121dd4f4132271b9f27c88bb758312218532a6a45a5af865d8d0cbabac64c0f200eb3204e2cabd3e53b0b52b7d331c5860ed66fa8640d7ee748c41dd75107e29dd162ed220f8934be8d15245c42e882fb05d85305938de2077cb0e54670f647cd10bf606c98d0b35d0de93ec28fd5202823706cd3bc51fc9d00b6d13d7d94f6991ab77cd6d085d724059bca53c9c96288a65a6662edb3053e85963b3f8644a0564801814b8920a2470be15bf651c04c967c9cbedc0c03385c1550526bbd37691bb57aeef35096c1a4efbb7b29635d56bd9f86a746eb70f0640ebf17902f9508d8c40be744b7939a80bdf38c0507189344cedd299f4299d6e47946c5943bcd46c6c8e96a27d673a92feb743fc26cccc89ec415cc0857cb3aed15cf3d2868716f13a6268176ac540fa5b61c659bda78b7e4b192b57e8ce1a6e8ea4cc66c1790cc2e26ec21577411ef4b6452d4b3d33e31a3100c3be2f04ec19f034d005f9237c0cc80f3029925ab0b8684d5555f5be48876258bd6825235c18d2fce326c6c1784c0cc05a6b674af317e86f3dba8030860608f33607a08316e67a2d689622230719577369d94cd12d111b5f3c0c69569c1722dc3a0ecbaedbac7d1b4ae0dc958c62f28aad554920536ae125189bfb908538dfc1979abc495c2bb5485b7457c34b740023c22dda52b160f8534a7ad78e6a013df659838866803492a3ddb419876fbd6ca6c22b018bb11099b2309a8ceb187f0ade8497cef6ca94f467012d885ffdeafdc4e545ce2447dc8558743fdceb7693032ac7f4b3d6d8ad96e6de6ac91e7dbd61ca6a9e27bcfa55d26e6f7b7f0d113406605f37a29269b3433cd344c611f75d5e3ff5e14c44416cd106d7cdcfbdb3eaf1dc0c1c38c08e80196b817e41b9907f6bc1ff55a8f66380e86b2f9314da79839182fe0befa772d52b9710e04a32f846538fe9a76e836ceae18d224b41ab02262ee97e1e87c49f12bdd6e7228811b6046cd2e2796a8f748d8f91b9f13143512ad78cf3d5b969ce1e98c440ffb5fc26c2a627d08f00b32b0b34866d164d97e8614d805e7579dbe060a76496ac49d30a1b68179e05868a8a7022c4e63740d22b9564d5f0ac3f845a78fe947a20374e71b683433e960ef70d14a14dea7088a6f86f5430cefc7b307881e1576d40c765272b293f914a872492f56d239b9f18f39a4faf2973fbb98420777db576d778751baf4f58445be926862d826c27523aaf904a647d9112cacb90cf34f43165edae6afff410a825ffd6a7ecce85d5e1960ced819f6e7d0fd7e3aac42c89038a2090bfc95a6b5872ddb2e7f9bf68b71b81c1a7651e225361f0fccc5308f2755b0a38b0c5ef2ed8c96b98abf2eba8e5b36510481f96c1824660b83f5c6eedff13464422fd8e72ecf5c344f42ff07e3661d91eeddec7f641ade2bb2f41d74a522d64ad0a085c6a6d8283d55e414f9cbafbde423728491098938c1c39f48924c3508a5e37bc5bc400b65960b521580f00e920d18a0a1c3cf2bda02f58edf01ec80eba2be54bf1ad9ff3bfc2e1234733c45ecdcc6381b250a879d768296c2ff31d40cded6c9a163e35184174df29cd41615bf3c8f0321fc73f8d7c05c5d93e062443c2ac470aaddcefed66906a31e6fa571c465a86a1982c1238efc183a547ad5db8c4027473cb12dcbe89abec70029a8518ce90f672dc7062e3e6f56b6c2ce681bb5e58e5df0c397e4b1c995ec18cf9f768b6e53261fa94f97715602bc0639822c4795d076bb957327b17de77ec7a2b5c0c2b87c6852d36f519009d72ea3ebbdb0aacbaf98a37103c3882dd44f1c2718a4274bfe7f9c9d1a2a34162adb6b43e2d5fa62d03b8e712f2b3f889112ff7371e686867c561c548b4de07715c6f9814f552cd6a0943c364a37eb0d75a4cb8b5e0c17f2345cbcc7325692eca54c085059665b05c0603cbf80063b1f3d9c8d03c990480f062e5bc8a7aac5371867f445580b10b1b558973d1edb05e8948515956854c9de6266a6bda5fca4a3f9959dd804efbd6215acb918a9cac9d21a4194fbc2dac38c821501597304ef0c0394841a67dc257abd43a6d4ad108c01eab5c7efdfecb03711ed5328b7534b32c621db671cc42d15fcb2cf1492bbcb03190c6b8716dd1033dd2bc8765dcd86fafd15b1077e20a4319462310e0799059b6faeec8bfdf6088b39f6105602f3a9f3eabb6d14b8a2cc31ab7bb4c1a6e799dbee7bf98b49217a6068675b0725cc0c1474542d27411b02ae17b1f8e28ff979473eba20cc82c727c979b2b5d78c1d51fdeff6e0e46d218251599c15899b10f3994c6cbca8e4302a324e1f8e69d48ef16c581a0de0f0463a27bc84e871d5374d6eec8b816629c258026a6f22fc52322e1e41208d9dccfbde60fb9a379e1adbdd36f57b88cee6042e07f9dd53006a1af5310fc5c099f886a50238c61e287df30bfc432ecad37fbb113b2eb9936fc6703f8d8e084dda90023b87685aa3dae550cdad3375040ecc19ec69c46fdf929e63e05367443f3bceeed21120389a1a2a79e853d52b06fa196b0153165a20d363ae4fb6a0a38cb6f655ed4b34420fc7cbff100ba8c9ed865f32c930f37b2c14c12ad42f166443658e79824c6e3dc8c4d6220c00d9bd0c8e22f7b7916654c2b1f528122f83cc1bbc3030b17e9c7a0e432f9e06672a6c48befc603d7122f2d9d19c2c786e95f1acaca592ed0f67e9fa77b08467ed8217481e1597e6852eed75310beb865244b1af0707955f2c5c06e46247c0cf6ffd6d7f80e41642b9999cf7fa218c698c6e968270c55e745e5ff4a20ec0331041fb5bbd68723443b1b9926469d79ad9948fbb0165ee1c22e0e430aa92d7af794b2f4cb32b24213b418c6b5e7c7b8fe84c22469b2fa0b9ea09bdb8bf7eebf4aad2ca1ec21549e84b44082c8ba12a502a21ae94769e296e78f9368cfdac80f0e4e9b13b7c94dba5015b6cfc24d4a91e1080ddea35b32a6cc4a4d395d0e2f35b602227dff9707a9df7a80c3ba3a1d8d58e27600491ea66180a6b08fe621c3816291c147a9a274a4b83da92f6fb83c0ab6d0ef1606744a9f2e6ae5d470f829c01ef189e61598489b0b30c93f16628537e1b36413530446a85451f8df9d0409936d219e8423bc2ff02ab4a1e78b027e90b50a339219cad82bd54158fa0b6870802182ec13a82d9f3d9761a546232b0a677d8aa29ee55dbd312e91a31c4980ac098a930fba0cdb397980b9725597e5205feb08d913c7e8e96ac3f4ae382de170bb6f6b1d9d8e37b5e31b8352d0117246f160fdb8847d91850de543ac84c4f3d80ddc9fad385f72579a7e9462eed90c63c12bb10a072f9750c7b187693c9f063bd81669edc183f3c9b0aa4ff016bf867938ca78fc556a5221329176783052443bb947581fdbbf41d3f6a1ec0c3447a277ae14efdbf69ad4551458d86c5cd9e633be72e94deaf7c504ec64132012dce4a6d1a6a0d917df4eee155a796196fa50c9dc080f6436300b383759ca67de734bc54772e59c592a0444754a999d2561b19cbf6280dfb1682b5061de17ffd4e150031a66fddf0ec9d23105e54222433dc2795a6496e2b2ace303cbdcd4eacdce7cc2a4b143d403f261664be4ee29b31a212cdd33d08cdb765abb0f2e58f5399533cb074b1ba4e7228b7e26f00c63f18023186dc727e32dff97518ded73690d3f17d6127428dc2797ad38de1c1b82c6634bf998a21d47642ef74977eb633398a380fc540282badab46360c2b40905b664ac10b5fd62d5f8905fa61b8b290ca74078d45dc70131d0b9c83461c76e063e28573131273feb7678788c2ebf3b2aa08159e3d36c2489a5f5a148ee8791ddaba2ca434b7dc79d77e36799d059d16513864b304d69cba3bf241e0b4103a1716261399bba4f372080256239626c3c689ae1d73e23bd9788a1ebb31edccc2919464915beb9e215abdebbadd28756454a96a1b5e144b601fc9659bb8841179bd7be25fe227bb2f06206f8f77b7cb8996d1a2c535d718ca504068884d644093975c5e9d02f12f260641aed9ffa27e3d510c09a28c4d463df9cbf9fd6204355ec35bbcedff2847b56f90042b5d2259538246635c55b5f442bf61af979fcdcfc44d46fa2c6fe72c6af2f344a2ad2384caf8802f0432524e9356132dac085630208035175841dbc01068a6c4d000f703507aea4c35a589a62352c53a00f46ccbf8ae59f30325899d7048489c5929682eb48e388a7674ac777aacc007204f0f3c7a8956a5c236ecda2ec2495b5b263aa78c0c687faa3e8da6a4a5d539fee7632923dab1959531672d2a70bcda08ba01efbba10d190508e4d9f883188da0bb6ed01320e2c02cae0cae375dd705349f302ca90a695a4866cd7eed58797f94a06fcdbc26d8832d88d89677a051253b6c1df6c882d2543ed550fd08f7c7434308aaa7da649ec05665294904fdd4578a258ba0a119d10a89b5da274e74f708a781f3e9e9c1e9b41b9f84717dd8992947074ab1dc2bfe8459ec340fa9e1165a77b51e6b3f0a19c34722fedf6237750948040fe126e9ff0d50f14439165463da56fe144345623434c56048baa1ae81e1a463682362ccf14faafd3e28762601f8a98a9fd3bec163c569ed79ebe4e5f4433a21dc6ed4bdd2c3ca6f90e773871a42c58dd94f66aa0fb69905e8eb6e08857e166814471add392270bc69aa57101136ca2e0121ee1ded488f01a8cd2180c11ed4159c06361315df7927e9fb20a5c815633a05f936aa627ef4e780a47a3fd2877bf81d323433f1a7233b6bb8262524c46273e897a3c8be3fb71214a9c208120177dffa0865fa29f2e347269fb02b1fd1da71680c58d06ba08db27ed90a7ae40d060824b8f55aef7118eea0c1497f168243c6c6ea4105d298a5e8ee016bf2af062da3dd102a65a9053a14dbc84a03570561b0418436f76d15d95470a43db5a8e50d15b2bc5420483671f727bd5f5fa9e68b9099dd1249425b3c5c358f7af19663cd9a020caf9662e6cc73b40709efdaa4bf911c470569b362a248e18020872a5c37d090a1a327a3f21961c2c605374c2a0de9be3f28da73d6736147f99213941f72a7b8e2a18ae6b43f13f29527bc38ebae74aab3d010a0d9171e7e6cba2786e7ed58eea2033610cb11652ed9ecad016745fc84c6e119006801abadcb6921746b28d5f3aedc41e94496bf61a971dfc1c95b6e7674f47f80e09a87985354a003fe3b650590aa8434b80772deacca8ee7a649ff22de2f288be7b51992373433f69a22afb7b8172212dba7e15f364a2ce03a3cbf90f03f689d21f727223fae286bdf7a8538ce8f18289cec07b6874b41e388229c967c777684515de8bfb37aedc884476fa87ab8283c1c8d9491d9a3cf1285a56280b5c01853fdd0ba6f9ef9c902f0e26fa9e92d6eb44b1c59bc7ab93caeef89a0330e0157dbbdcfa9254492d8c853a6a113df350ec459c35370b2a574591c81ed7ada50592515d459b29590cda9db15a2375289d9317e2b4ccf324bd522de794423f83ff50365a6409d2329d99bf1bbff745ffbaf6d21e60908acc987be61eeed9d887b8267a708de7d89d588bc5318025baffa7c255b2cc271ea8342a17c75a6a6f73feabe40f7ec7083289f86fac99922da8cbd066041083abfd57608ccdb2462e5b89cf9f3ee5a8162fb3e33cd8eaeb1f45f8494179035e03c77dad1f2389bfc708b64858e7688e802ae1425151e5a8bd1b7630c904323a599de93075d8632c2e7d9f41f476b68c09cf8845b574b5740ef46156f10891ad4cfce778a05ae16ff67f0dd7cb8d84a1d1deb7c0de2fca06b6789798c536e58afb836a4de6acf4b2bb7c8e5117169e7a1e2f142677b2d22e1fa47ee128a921ad95130a110c3af7c49f07082c85997d0a4fcb594426498399e8491f43117167320f3a8f766b844c3727d9c13ce33ac780354dc38a8646222ba0da930761a6882a814cf45bd6d3bf4ec937717cccb125a800b76c1937e60ada63ba4c7ca012c6e5e8fc538db572900b59e7bc97d98ed277f0a9bb34868d4f6d66b24f1e99bc6917a27b9cd7e5b341396592177f751b03ae239dc699af8f2d4a39ac42145877e91c1f0ee7a9c8d1f91c4ddf4f2e7e5e22e4c0c8eac460db60d03d1da136608014af44139ce112d06236131eb846e954db4f450d4aa7b6240a3b692eaed5bcb2e2684d57b2279343a45400be3220dfdb420e6bb9f0d6b88c774795695e70d7dc114964bfb079a37f65f386a915566cdf18515d5f56129ed2232862438d04c4b8593055edda2558c8676359c74b195ee36a2a7d968d2ff634d007cade3b81436ea3d74a99b77686e41b53be0cc466d6e14f04b4397dcc9bbc755564d3c3db754ace740571c0a6ffee3d7927ca72293901b8185d6561a59a014f56df9ed6138d67f442f6501128c37a9cd8883402ddcb8c07fa8b0b3c601ebf253b66705690580f5d50f2b13b4f0784bd3724093e367a4695d8e115e135dc880ed503219c1dc8b16283fd9003b4926fcc32f670414f235cd18906a9f454b69b2a6c58a180dce204121481bfe63a7b0b210b5604bb903fb965d1a04b8503c23d0a39260539f256ca409eb61f14a584f0310209ca3affa0b20967b3a66c4806e4414212e5eb0256c06818001273a441c20585c874baa65c0ac81d5b3041ca8beebf6f89e74f456e9a1113f415e87e0aa79a25fd861142152a8dca84eaf6408aa41491668c00c223f0d88cce2dc02248493e73fc419b8620e326a05f6e90cb76ae5f99b19cb9b9ac974d8b7a92ea570e7741eaad4450f17aa7f4622d9f519fe86a95dd0e8fffbbe7f079ec08997ba56ca2feb671ed8b5d45f687c65645977770ee4f901edb0edd0c6ad2cea1e2ad3b1ca9165698a0f4c88587520bdfea2055589378e95901ebc1d1fc1712572efbe107b3c14dfcc692da23007a1d85cea4b50bcdda5aa51700d6771cf505dd8e64c4f53b25a3cddd8db02dd4f01e044ee99b2e59a44c7d5aff464205144cf60b3a71b41b45edfc8eed786d32ee67cfb2e89f08c39045148cac6c990da90deeb9da4ddf1e4942e601e5d8db1b961777fa0bf78e8f750938ee2c16d60a3c3ed806cb7b0f54208f36cd210423def3094175955c8203f16a7bda8e64326ee227f4572f48729b1ccb9581916a43884b4b45cc7c68ad26550e668036d7546b91a08d22bb48df29e2c7a672f24812de604ac9a43da3aa66055c2b2f73871c3f019cb50e71a44c1d05b6daeb156f70af500e1d4620d384ca0fa792ff13bc234c4bb6a302ec48afdf088b6a3067ee7b4241a9b28db1af824a63e2f60a48c390acbb5e9fd49cf69a36a8b89d54029f9dafff1fb30aa177c4ee700b91adfb2942e9eeffcdcd2a63a5c2a876a5e7e147a43f3c91bcb10d3df9255af5f578cc35656122bffcc62146899406e62ddbfaa717166b45c8be6bed4b6cc08724f1d015bb28aa998f87b4720e6881082e6804d345109329d10166cbce231d83a8c05c28a45a5672f8cbe5a1b44430ebd69dafa9c4e4c270dee8b6b7eff3475ff808201c497c080c4edd0b37ef4d6b2b0017fb59909e3c42c79066b7a35e0597ba58039071f412537132bdf77a8cd0a0ef809bcf3074694ec62ff748cbe0d00109ff7bc022d6fbb47200c4a9c043a239e49e3d595745b5be26657dc5339b0071ef719984bee5fe80979c04c373981c2ea80086d0d5ec77f8c6a00a06cfc177582aa435ad1e019637cd06ff5ecdd19805abec100a577091185d9fe0ca51e354cdca922af45f050cb8df985e20e89146f55a161056c4c16d088dc35f06f992f626fe32fd9b3626879b9f8540cf1fb705fadd0339b12411d24f7ca897f7f481cf0c19f38e32868f61743fea76b61f7ba17c77f6fabef026688677496e81e908faee0788fcc5b44a04fc329e2041b53af9e5b121b1b3a3e164312e44fec6008ad12c2a6da802ee853f02051bc4a6e46b15e0927c41fc53a2203d306962f168837bf0a44bc0c5f0746ca2e5e2b98f866609019d6c91af5716349fd026d2e3298915c36bf59e5a5a23c0d3da19230951177775d7feecf074f07088d9cf7be0d4bed51d6cc4904117771242d2c55f2891f28fa054ab5292ab1d9a24ed2b60b2f96c9d07a4248235b7fee8dbdcaa507ee87b05051fa44b216ab7bccbb8a098d554a67bb1450298caefb3404f65489726253064e0424d6faa5338ed78f6ec97145ed035257fe0cc2f389148b326937a17952da36f41b33d3aa89607d8aae51e9ac31ac1b32b7bd8c78e5c925f886e2974d4895057cd0616a4c28426faa2ac7d01ae8e9f226b94bf253b035ff5d30a65a4986d91af8f60b696f945c5517daadd565c0b3a5b45b084848c5601c27086546c4a65949282c6e8c649fd0ebdedd12ddc3dac2fe15ad952dff272d08bb058c9181a11a1e2c45c2298a42f081926faf9a4acad2c0d38113568ef9a78858d9ed266c9eca6fbbf7dd04e5417ddb1aad1e2d875e509a4e9facc096366f65fcc37a366356458127d83863a24faa80e29343db0857c2014e59eb2fc1120b8cb4e09db332dc004bade517b2a575fe657ba564d6270e7c8dcda27d178c1c18395efac9255c37b122c3fdce9caee38c96c7bb6e57c2d70db3b1b353e144487deefdbc214f2e89032e30a96f410fe1dd22943d5e3c388158dd0deb57d0e29d350e53ab256b701ebbd838e37a97c51483a5d20a2204f037c0988208e1f8676780089da7ed0aa082f88d8871688f6292be3d2288dc178bc95058f94fc0fab84275e406c6be0ac0b62b7d2ae45ce461244f1f15aa16c20a0245d4b7ea442b1743930ccead6876d95eafb5440d5b56156a6888a91a0c59c7c2dcba734c2628c4d598e6479c013565e348ab79e33f816a0d551b1bd015aec35307382cf041e4a8350741f01a0345f88f554e5aa2daed7eaecbfed2fd319d8355e4a7e7155c15cd0aba187d79a8184a4f32ce9c81cad7c91d926859518903904e5fd5a4cbc7de8ea2d5f3c8cf0b7cf41cf92f18b4ed1e24e7d3261f14af7c2381b394e375a14f51ebef1c7b891121a6ffcc56d844add98ee6159691bf1816d1c45374e431a5be533c5df340fa0d95353bffc7094497ef1acb843b82aa2a6af5ca5839e3e8d877ece3289deab088fa3b6d26ed8d22d0674b45471762ebe3eb48e1071f1852ce09a84944e48987e0dabc8fac4febcfffa4a252da1e9c325cd47a53e2d21bf48ae8528a07b0a0921443e5acb83add1841675c2e6194d2885423a4a619b1270d5f00bf9159831592944fb256212982f8d4e8aac3a760e81955b0557be904f8af03ab1f3948e66ae88044244451c889b9271ea944d9c489063d57462c5b901174dff0aa29f4fb5dcf4cd5ed908c320ffea6bd88d31d5a2f1aeea17fce98936f10b7c21108d0be7a769afb7142b32525014ec2c504aa01bc44c185f29a73f29a04d86d99919099c7e757d210bb8b8c895c351b09c74b420a4a629b9f4917bdbc1b429a74094e65b4e61fd614a1e4f9a0bca3068c196d9173c96569c2e73035b9c2176f681d4a92c1c16cbc33104acc05436c3008fa641208c008fd61b1ba5c29d3b7954ee599f631978d30d6c74721e43460390844b0d50710c1de26a12a09072096be9f0e208fcd021db857aeb0b0f2b9c7b2ddd05b139076bda48248ed58822404e40e272c1ce2e5d18bb8e9b92870f911260c1928ef1796333f06e7c40cac95dfffd17ac00192cdc951befc597db677a1fdc36082c566c01133835863baa23f2990a71a4b997e2d144ee93b0a91875a59392d3c54b0e87b17b5de427b2d93464f3c283a65aa4a25952b9fc553b12ccd8e9172eb70e690f3fa222de3d28739dba43c3e79253b66887ac4f9f4a09aae9b62c7f309079c2b60ba11fb4c116461a0596b945cc7282d00caf7322efce75f9e757da271b9a5dd40a083bd185df84e635589690a595e5fd4772cd8fa313958aea8a28fdd0c2cd69a30d1876a01302735f2263ae5b905805fcc138afebe58c1542060aab4f8d3fad345d6c5b24ac37dbbf111101ec0814c3e56da82ac1561fd41ddc52f84443004bdf61c5ec21596ed059be17cfdf30b030cf31076558b91dcc6183e01e4eb299419df8745a03dc3468cd90dbfe2089f3d0759048710ecc987b94f2fa01b068f6004531902d3c4970abdcda0949a90c8deda60bc8de05f173924fb3f092f81e990825bd0c8e572972e1dc1874eb6889a1451afd3a4962b11a682b6a9f2f3abdff3e28e04e93eb90082c18ac882a8f08782b80d41284027bec42c9815f5793b32792ae6aeb2950b6f678820880322122cfcfb80d2004f0b10c364a8139e27dbe6a1c7ccc1e133468e92d70f372e0c2406c2448a8a79b7f03a58ea8911ec2445d4e48f049157f1f73760f1540e5ae1bb528df6e1b9c5722abce3037ab875b7016a6955f05fb08af0a02f493579b5662c9973115c8b8fef596d92a1ebda44957c35c4e5df1743f2f30e9fb81e67e6045cdc8776c3aec3c2f82399d96e853e1939004c684dd8840b6cba8120f40d7d87c8b4bdf597b730940652cd4ae2d9317a971c0fe8f007923c0d33f9e9b1613dea80236c6d3f8a0e2fa20478847c934d7b5d1cf8bf74001b368e6a5c33f22737e5f7f72c7664ed9783698b9d6b7efeab0bf226c74f0a71ac2c4b36452eef74426f1f013bae8e18ea99eb2a7d659aa1500cc8262050ee117d899952ddaf07a2a5481edc903b1e08193337c35fbfb1de16c9432cab419eb4439cd2d3a50b063dc0bccb370843b385d87e6f2da5c2428c996cc69344a89efd5eb61cb82083312d84076aa26078c3ed1fedf4b81689b023d4eac17a0f557192df61b6a53384cefc4c3397cdf2317dd6d9454dd6614c1e37c7567618e3835f55e7e3633b6feb348b8fe33c7dabd4968be894edee345e4f9522d4d18bfdedc77cbb5665d33f4905adf6be7d141662ce9286cb7da0daab97c76eb1e7fb9c84f06945188579d13a1e3194201d718bd890bf9599715309fef4e27e7179701de72ff442fb9a459b561c14be893d2c845f060870646d1587d3d2b08f0707b2908d8604cd925923a12d02993c9ce7e5f85839674ef2c986d6e654d3a762647f94c50f36da6765f4067e09874e8a877d823c308a44a04b0afe64dbe64bac3431e2f93192e2055a378e0bb80a4c001b21f7463cf5c95aad90e2228c8f908db455b40bd2d374cddf2f7a6599163dfbd5963fef1ded16002494e1ba3c9fed023897cde1519b0590413bbdb7660ca452ce9c13bf06a3a3008d9329714be932c5b84ac3c2601ec13ddc29aa0ef7c6f924cb40f84fc02f744ecdff09fdd86c119bbc678161dbe617c3fe4bd80e50ed6c36a463a20264f5a98537fc427a7c4c77c5402786d89473099ade2cee4388377f991b981b9041738c405e7ff58259d2c9a7123b403185aaa83eec41ab703a0d52b24d9cab539528a7267dd597c01d6e1146fd67473c5291f6e3de64306b15a258abe4137d8d44c013cd6888bb74ec5da31383c72dfbcdef3cbe36b655cbda74676f940bb9769f8b85656030813ceaa3489de2a8b8b5c7086549c566b55aec8d7d97055659c65abf2c8e0fc455695872702c8065c1236ed243eadf29a31d994b97bddae79a6f239f4f3145234d33e68768bd4606b09a31e7ea835e7451caeec77492201ceb6589bf13e0243eb8f3f8f4a90962ce5ce189c50f473e4545b91f9570e2ee1508991ad920b9a3b6db2322e5dd787a7325b67ab849ccf4aca3fa26a124f9dcea59c4649608e12e378c2e1981d449143bbc2c06ce1d4ce53deade73b4082df58cbe0a198a0aa39efe57956b2d1b9f66aa6ee4f04d2c590c7b4e4a3f1ac8f0505b1c994add644de618eb18ec5a5376e91a4c4bdf3a8e19187b66483fe63859d4637d304635db9597609a2437f28519ca49c1145c4986a99f55d8f1261d9495d48485c2c30c97e2a4652e6e0954c46660f6804e8f0471baeca2eec71e63e93000a1764c4a3c040b9f39c82294b801d56287a8f843b5cf4bfbc2b70fca2e7a00567ce9608e6767cfa782db60905d581f3ce447f77cd596ce2017d18114319e8358a425134e77afe9186286073d779322082f633f420bd866873d4fe9eb19d4215d1bf119b50e65b67aeabe49ed29dc05a2fed0890c042a42f561dcf4b893da44b3576221b0ef2aa911038068c3565ce303ce94c7111739c42ea431a2a529cd4df23d7ecfa69bf6fda218be80f05b26fd00d13bd12f2d4897e3df06431ceaa56335c68d02bf7de39c24b052f67c7240c126983d6c254f9ced702b7f02418e651e5012189557226a627ed16a899341ab2381529de574a16bf6aa1f5b167899b39310eaae45264dc0d77461f20f093bd455c182a1d70532f64990da3dc0b9e7683597981336e9006c930f5b615a08fc3543ec03205691c06ae086c8100c60e08a526e102105a48f7f69b7b2fd30731d128ed2225a3ae58e5bb638e8949580d3406a8f2a5aea5910c32514cad377c593c69be8314eafdd311cf6a0f525f12cf41ecc94ec3e70db21c5bf23a4f817caf08edbebcc6c6c16b5015040fb39cddfa5847683e3f723d680922e7b1f024c4580edcc4f89a75bfd4d0c11e08d9a0953b519bebf16688dcb29ac1561e6cc1507247e7acaa1c9d91be395b269f03b906839cec1945977a834482b26c3705fafe3498d8a50f1055027f60c89b5b3649c5dd074faa4d6dd197a6564b6055b72f3c842d28b881e1fc76ac3b6c11e9e51ba04592ffca86b3eef2ec0bd23bfa1307bd5e844c45ede73a65fb87db96a0ae05ef363eb5d661ce9d027bc5e89457a66bd457250e9492e4e86fb89d0f137de0556179c203a24ec0e19f1156a578829744350c87a582cea4cd2c0e778ff44bce112cd17c49edb7381804427371f0d2492d69aa39a1ed431fd41addad86fd70ecf6b7c2f20744bbc2f9896620883bec68494b8b6eede4e8cd338b31fa907949e8d5d02205b7860800ac3c5904bbccfba9d19ce7eed9ff64abbfe17285350eb7fbba4dc67555c41ccdb5dc9941eec72e78108996212225186283350e15798f4b06651461520c7123c86853fa229b448d16808820d972d0362240b8e9b23a36bb1422f1d024670b40f2e526b472d958b943452053ce94346cb79a5dc368666f669d5becad046e516ff20ee1599939840fe6beab9db66d5a3bf3318f3d363cbb6a70ab7ed5ac232330f0cf992cd75ddd82991540e4d01634f70853192f90ece149425a863adecc4c683253ee7693310c5f12111f0b7f164f0be013f172ff1f87811f15cce4d4317098f99abf5c5a228c56ecd6fa580a50b857ebf7fc28d2cf6d77457597f1480014457d484e270cee270572d7e69f939f94dc7fcc87e19c777d08800ce611f7d840ccdcc0594877179e368e90425783cb91c950eb31a5deaa245f663c59e0dd77dc0b3cd763920f66d6c5cf332a25cdbd41d87b150f1d12686c5100129f0d0ccf3649d9a81064c2ad714bd56865b8b3490b46aa6e116d08de3d2059d07c5071e7bc0f2eac3dbf9ca595a8030304ccb64ac907733a5e7899e8f090443af9af1db5bb241d2b39010727b06799cbfc17999ca786acc443eb331e983fb1528ad7b1389834374973be1015cc08199e3f68c8fd9bb00f767fede5cabd9883babce5c4e7f571b597cbf0fdd33fc6a2ba542c6dcb3b66a62c2c6fd08af88601cab16217314f348976228a1e014255ea46c9e85dacf9e3376322315524d8bf65c706b34b975dd723dedc7f331f5610e0190d34f3777961f3d89ff2e46f8a4ae545bc059e8659daf1fc5c18108c60153e832a19c3ff96fc212d95fb68ac759154fcb43610a5eafb51da4776ab83fc0710db5b0212b90d61f5881864e9f24a68298c4eae2aa9f69b18300380070210aebbccaf89b73f61e0cb3d397a895c1773d7941680790bd1239d6e4151bc0c5b97100031b817b3f3930ef444cde816208eef6276e5c5eb3dbe779f534e0e04f5e1f422af9a9258876e952a9fac954c20185c3f4bbe396d195b18915080254509e680fd59d94320a99b941a6251c1fe8c22ad9cd293c3e72e8333e24b8217af7da1100cab8c0acf22328c3fa57292c43bfcef2b29f2a62ec4ad3e913d51c4e62f3eb9dc518c8b2af7b8787a2a8106d18a736a1192cb268e984d4c511f79ae2d187f886205f1e72a3f6bdedb7c7f41e5eccdb40495838777aa465034d1435164713b38f36644ef23ab828d6bbe9da6c8e46f70b07a1c17fb2d61de2efdde9e43a72ec7eca7f224f529c7755433f04c9a06f5a8aa699bc45addc4dd71926958d590eacdd950306e0f6f6503901ea31b3de1b0bd7e2c853b52c1afce2250590f32b3da70bcd162d2bdad0f0d8e46ce150feb300ac9f8d2b7d2d60d0fa3e5686a8cab9ba8e8502328df1ecaed17f1d2f50af5b29425d50e17919fd552f53cdb9d2abf068c43575c6c1e12a158160596cb7fa1b2627fc95fe9a323e320dab5306ae4d035e01cae3b4ddb9a217c59d479066e8f5791f084e17798753f498bca161f48bb937cbe2443366424360b6e928ecbe99b8657ad86af7533adbd5713f682088e072de72327ae650c2cd6efac5871d61057e89174b381de9168faff3e59b4bd4a61a7874b33a091f68d12bb24fd17ec840a867f6a8b7e96244446b44fed1030621b4c38ec4fd05ba35720f797f37227b6de1e7b60528409c16e98bf945f64745a301a363e94416c34a2e710e6efff7c7cd67541c7bcc36adf699de0198bd9121e3a366cf410d8890d4859013712f4096d2b4fbf44bef516771befa0bcd1811dd2f3f751ece82be2dc8edb3d70852cc61c77c269daa1970c949bd85872b2d8bbcdf61e70a74ff450c6accb50e17c2cab8bcdba8e03cf207a69f11df6b4bf34d2156d8b521e6229f0cd73737aaa97eecfc1d98a9cdf91b7cdfed8bb3264d2e47853d3834b278b20b8809b10002d3620b835a4453024d4fdd6bd1b3d1b34f694f6bf120e699460e96409d75332057646e51daddba53b8a4a3facc4f705470774815bd2fb95d86e290c59904d36d0cef24c3ebe4acfc57a4a6fce3347ff9b49fa7a69db3beddcbdd6ce3ae3afaf3bb01f271dd3d5ac859717f0e5988468aecdfbb11f3a106452661400d659d4cf33e9fc89110e353c4b7ed30dea4465b02b7c2ac819f182ab3f19cc49802c384358f68580c8f76d2159684957f4f180d82e1c89ff43b046f6cb860937c8fc7be58cbd62feae57522ce6d94351617bfa5da6aa0327678bf81e7bd9896f14f1b9bdcab41776a94b4ba32087b5293fd26f06daabf2e9ea3d6e7a33ffff255b974ca955b1ab10d9be90b6816f18f8c92e747e7b3fa22641b23dd437ae9a3c7c995391eafd3cb1e5794f6386d94e94011653bb6c298638c7eeccc8b95c538d7a8f6319d592c06272cd6d121a44e5b21a7678b10e6955d2f5746d74f8a60b0d494f76ebee87bff88febd74c801cc06ba3b88be316cc0a7f2e88c322ca6ca3bb49520cb4f94fef7a41f346ec7f83ee375300a01ba81ef6f8da14f06d9ff4f8c4a143a16d727d4c0219d3c7692901963caea4f9ddf39a208b5bff660507f366a3963eb97f5e3138cbe017350e59e58f936548204a8afaacb8843459263f34162a260d722ea833283651309a6720c233c88ba333122cf449dc50a45a20181504701070511393c7195be9be487ac0fb3f384b09f2e490f9677277c55264742d509551b39130c7f661bc1dfe211660dc6cb3347c125c4c73d244af5747ffa761ca50241b8816b2c3f589a93bf2139f0b4ecf8f5e5fff1ff00d8c639db58a507beab1ca3351af3a16a20ea3352b1682535decba9a19e3a26f4b76084b51505c365d45722dff850de8083217aafc1cb578cec3624eba28f881841a506969bc7d07113719efd91bccda70716ec9e98ef01401d2a37c5978eb4c38898922b9f9dfe597084f2f53951afd0c5ca1a9233150e5abb17dafc4f83b68f82f3838464661f6603c16e1f1386452f92e0ffddc19a31aae1ffc6502a00af83a722fd40da46f9461e048604b20ea8b5443e8618ca2304d010bd1f02a12806917ffa9c07c3150542525b63a9564afa37636019c021ec6de2966645dd6250e6ff803958ece0ff7d39bd6841b5eca6e69bb1ec22c753d7f43f00a3726441a21d9597cd9220a972992ab2851e732bd99fa375259d1261d76254b471a7d17b9a449044e39943848d8c153ecfa476f1d59d362eaf30cc3bd5e988f26a2a345274e55123e2f596f4648a129f78ce14bf68d411fc981cc983a89d418cc98fe777a91fb50f2d9be48de5eb7095981070f6b891180a363d61abefdef8812cf89009631a8b452bf07806353993a478ee078ab879a40b8abc652bdbcb2ff267121f946d24298e94a09d17925fe10879a2bc7e16ae62748c069722a0aac10854370f24fbaeb551e7c10829c96dffefacafa2a62846a663eb8faa5ff021aba4ed44e1fa4608cb77ad705625f2edcf335dd8c56b8cfaa10349404480ed1079e6c7030f958162a51877bac2bd8bbe7d09d9dcd27870ea8e6972fccc15d24aef238d04dc059fabafba265c5964fc83040188cf369809d1566ee96844911ceba812f6632d3a281253f988ec0505d58d537694efa8ab382d724d84d3799e3904fe81856e84f4288eb7a073f13f4d53afaa68a3005dfa4af55139adaec4729e563d8b6dbdbc105f099ae744c357961af17c0db3110b92419db1446e680ff6e43617e8e00f96aa845a8f9b72befdd67bb2646962f259b167cbe8c07f16022a662c5aba965fbd614b2810aed808e300cb8bc01a8a5797b9871f2fa7a0606db9016e12d538724881bb0ccd0ff3e10aaf741e9bfd18920612ba8ac4e0061a2cfe0787b6f4068059cef5d8cb55833b1a84571509ad2db7856348e8f8313324ae21e5aeff9dd49a3a968f468dc9538767be9aa4b1863548a95ada2579a73846a1e7303c231076e260447b8ddb1033b4c1545ea89dc3b7c858446168db07f5e2c0b88eb7186e3882aac607d562358328b50b3d64b2e12bbe9fbe7a381a307ecea3db8dc6b1d8e458e27b553dea31b1d3c60c1d649db94d38e1c87f1480ae5a9b0b7680d461f97a5a79baaa040de33dbbe5c23405f33715a18e1ce72b08b7d293542cefd5a38e45cb13343a98cdc5e76c1c3070445b828677744d4a08fbb160e0bcf76cfcea02dbb7345d3f92df42bfc61f55807bc006a307ca878a8c7c26c8f4df28379e2b1da71930dd24ab8eae5a762546397250f8bfb222a971fe17fb117cd425e78a3426d89b827bc16ae9b0caf4b6ecc03bb9e17a6df10ce275e59fe315d2c352a7886cb638d2ee6be479f2e6f20afc0199ceb8291e277d29aa9a13860cc84be8bd5aadc62417e7050d5bf9ed84fca73f7c2392481779c497d1227348648940a6754ff0969a679cf884c821a30182510f389a1359625f4fbc1b4ea876f90ffeff23fded7c2e20f88b3e52d906013da72aca0e61bd532224c3a65550c2434ed501bf26de889784b52e232ee716ef565782fb7e9f2008207fc0d4d90501345805215830855782ad185d27698fb02694ecde8c18dadf6c3439c101195de81ff633668cf91d764eb9fc7af00dd284fb94e3b1be92fc96d7ec41110362ddfc88f5560d2f91940919285cbf4372146e1567e5adc493d64a076cc92f2366ec4633a4350fa76ff7bbee20bb2d333cb8813807a5ccb76f1a6acc20aea104e651d403162f448017edf9b8348ec412522ecaf9400ac16d840790a5411cfdb977f871161d27e4243000cee87614e076040bb0422301ecdcf24eb9eaa5ab043af63469c5e1a76603ea9623ffee0df86ad5daf9776435798bb94aad2a89f0a1d431a2262691790fcc717fc8c3930adc820ed65f7d30f77c30f098d3b68fb2bdedfc0bb4d79bf611bfba7a4c9698b303088d5fe58dc54afd5a914964e974ba9514f9cfccf2e54de0c05fb4bf890e55bb34c1932995d923e2845e04381f3fe65ef5ae6b8764ede8a923f3ae4328c37256c2168876330669c626e10135864d237c3cb317e4273901cd4c8b8a765fc7b4c46b7e522d4a7cdec9194a21e3a7473e8f84dfe228b4490b9cb725ded47e985c4b80fd6d299f3d0832efca7308a14e682e46c0ac9acf6073d516968814522e1dbcb3685c50e5aa69153ad1d88763d6128ad44de019714c90fbd246ac8e8ed2336eb13d3ba61cc5de202dac63684bce669b073a7395085bca61cd7c2d881ea933136efbee219ee2ca20d5ced503b3e0961cde624313e23f5644c16fd582ddf7e3b169db5b97274f3b59ae2ecbca190f8c7d90540058079c859237d5c4a2f139c24e4886598fa9cf4cb9f6df3194ed6467718ca346bccd2783679ac79e110351ee04b429a5c07d06251f1f9be41311b51d39d8b532fafae0790c5a5ac6011aed06ed3c68cc598f0ef439b34e9c84d45c923d269f22816289554d350c0a0c9f6e38a0a0eb8492981b0b3f08c311eb2f395ad1f419f8837ba97eb1d664e2f0f8f6c1487a2d998ecfe6cdb25a1e2b7496177ccd8c706f65e5140bd3c9ee32557ba7ca3819f2b1b28dadad6e2a634858667338db045ec66144c6487199fad901c2ac8b1f10cfcd292f6314ec7ce98192b44233e16cc14818ba0cbfb70f6fb4b2293a0c57d69db494609e4b3cc26d3599055250d7db2c366a058a3912786df6f4ebd5f66a9a0be1c6888ee03b219dd41510041269a23289136ba819b19445295f5af7f85a4b9879d5587517b16caf66dc622d52295527801670d91e29cf77384b170e96f44ca70dcc0a49441372495f4099b0036b77d553ac0a4df8bb3125cae57e95c131d4d07c746f0ec0c7a744d881ec94f3d0453ea1a0557235234efb605785656c2e584dc4d4ac2188974b363a33271330140de601a97c9ee923928335777d5d6fe45cce10cdc6eccb3babfbbd17d2ae4d1dae227420a31d1d0743af5f7e8cfea6cdb4596e80df564ecc94eb8d7d7d01d1cd178b97adb97d35ecb2ff6a314a2dcce94740fab1bfcd1cba38573420e30231068251b29738dde480dd8745c08130b3863957788f74a46e9dac12d5bcc1513c8d48ea38888e7c73bdc7abab03d786a0a2d16608fa3d617c665989645a2c54cb8157d8def0c14709fbb06cfe41867746572dfd299b3f2500a825bdc519cac44f8992630deaabf8cf31367c8c8428c1cd935fc2b64f8c021fdd41f730a3b3f2b39976e99e4edffa1124c267bbabf5540b4c116adbf290e10f29d6304368a8fd1cb4c1953982a374b12a23f072cd843a01e6bd4b8609207cdb5c381b9230298d017ddfec228a310bd23b9d0fc37f0aa07b57b9f245d9008741eed2caaf711dc444e8b1999de4b136cc862dedeeb63082ae27a32fc27e55b447000300565f188fd9475789dbb906c1a7abe63b12b593388330016900d8caa5ea84d3724095f0de600ffeee1482a6b0558be5798ae260ec2af075eb07a7ebd7abcb31bc8ec4246bcebba0c6343e5e7fedf02e7f9f5ca2bd2eea855e9fbd3d7a532faaca01970224731d9b57caae0e3b3a8d12e16e8b179061791a3cc69bae0c42740afb4049d814044281667c018b31e67936027ae44778c6c7d7b37099f26564b24d431a0cdb2bee3f0c710ddef073cca22568448180df0ea75a9ad252a55514186b99493fa2e17f50788462afa18a8906dc301b8f04c2cc71e230b005fa7e89c5d43a3247086c4598824b3035845943bdbecade8737e63302286355d8513a515941496a32858f7df232260720486d622354c3d9433a407c9e1ae52d116721d0f6c7980b186496b0dcef0c559563f4c981cf157ccccd965648c850468c8d12d6704ce11dfac36d8592002ed3ad68a9aa1eefa0130f5784edb28ad93553e32417e171c42d50714c89d20aee0759c2c7ce483fc3d002de3cba8250e1107c364f0bd94f8458d22ec04e4f144b3601a10bef98518abd98b5399f5237140dc93792f4e5fdd2942cca545e8d516856a6fdb618e418d35b00e8048207438b5ea8905bbc0ce483e622f9edc1db9d9dae936ffb0364ac6c399ad6862bbe9a519d54967f0b4886b889dbc5a82929ef3862f5b824df31e0328a219bf806d7fb9337d4a6bc0c886539667d4ae38ffaebcfe540a773d702fde8e228f5a50a6a4e925a44e958f4bde14f25b5063be0bb36f6125401da006c67062a8134b70063047bfad1e366d5ce8e32f9ba462df156c58699c80101731d86bd8763ef5642de9faa7fcf7dec41c13fd0824ebc8c770125eb65fca93ebeb04dd7688951430d324627a2dc3edeff189a96c38c1b92805c00969d9888ade9034f183379044197258d4de257393ce9541a0986892ae45185f98b68954c8542e202ca9eb3279762c5ac00e6e7883d7a29191ac33be0de26840fb23e97686cba62fc312b4cbd16ec0547038c09debd33148a1d3c6fb8741535734a160ca715e4cd555ec39101ee7aaff4bd23a29da06fa8918f8ebee42a486c0422391480697d7092d3fb2c66ac9daa2b6e035164bbf29e407f815619e59e39a872e873f170434943c8046ac0e4bf30fbd3b14270ef2a0638599918a92e93d31fb1802e4b334a78c44067f00028a453863959c000ba091704ab7bd07fa7b1e0a0e84020902e8244fe560e2999c7a7920146972a9822c8d4cecc723e7445961b95be49e2b3cf7d0fa6b0875c3b6c8d57b9c156d65657cc9018d9c9ec286aba18ce26b4fd9f5a2cccc25e6bbf53a2b955f59fba3f7905ecd773a5bfbeced8dfc2a4ad93aa970574a412298568dc20fdfbda2ad9f5f2753d4f8988c2c96e1eeaccba564a8778e8ea4383e92b6682559adf0fe794e56900180e71add691c9202b7e6a7682b136e2af47096de09b4bc1f13ba46b502856f0709967e4c9c62ad88fe23aba10e6487a9f1f96749afa0c1f0a062a90cab54b095fd3b1d16583ee147419dc07d5534e5afdd33940a82a766ab14a2c025b73b60a44628c2d50822f82fe7556e7b2da8c248339db895c94378c0d96edafaa947e671ff9dcc2e8de25a572194ed52867c87975feae97e2fc661961487a0ec690dc0dcbe99a913ed6528a42e5717daa4f13b9611a1f1fdfcb822a5bb48e762ac7514934d95a3502c2a0874f9fd591266989b4d69e25ad8b852c8676d558200f49ed1b5cfba1cf9c31cb6db723e2e710d676a1a2c59be3a20a52ffb223199bb6764d60f1636f61fe02e7f2a43f0dff6fc5943172d37a8dc23bb5b7e93f50dbcb7509775086bf0284ac82f3911461d95de7e714e42b11dff3f6e4a28861360bc4d87d66163945de8c24a8e216568e3de8ca52c238a38b73a70442d9d8aad6887088f1598c10193e9c82cd59c3b5d019c661a5f02f17af6bf9400f256b63a75938309c130d19529a0710bb8669ca97ca7bb37d7bcfb58da7d5132f357650dc080e1b3eeed302aa31ad3ad52e5de796314ccba0cc3bf64b0d2fd9d7c618a4649b5da32dc6475f104f7a3e29fca4a2b826000ddecea892dce7c0a622853685da24ceb02bf168d49b428bf04564f48140b663cc4811889f6cd1fadd168402893f0fe6ef19d284246f207a5531f853c52b15b586033cf7d1cf8b887e6515589a474ae1f586531c171c9dabc621b3982a0193f5447bf1aa4c37b6f9f8fc95a0ea398404a5a46eec60f41017a66169200592d8ccc09ee7e0d20f5a75ce512c82fa6b8f8744085b7e1460c106786558479b7feca42c15e9c3a3abfcfd09d3d9b4ae2ac0f7f4cc79fe92264fdb1f0aab30e36543240e3667f26458e040868b1c6d39e6b94165ef54be9e94d8363e1f568ccda2470a71782ef5e0a8339b09ddbb11f3638d6abee0a141f9f3b9037aa9c091d92786e03aaffbd06c942e2b821fbd66c5633a4fe12dca109dd0cb41493623a306524201669499987b4f60fc9048be134303c72c50d9232a34d74ac405465d80c284da6edc0a28e2f1d9f411409d3f6c59c737b95129f4919d0d162da9cebf4815b43d0cb00809b43b7f8840c386f49da831741541a96bc88aac92064651e0657b6f522ea69c05e4d79018789fb41556515eec0652ed08936a51ced6b3d41e009e85f48b4c52d91467e9134845c95e8cad5c21e58dd35c106a573fb8a66e5bb8cf7a87642848bdbc9c6eb5ac03016ce456946dc4d478fe41730ba63608d00237b9a19cb0b938c9d368d80fd76b1dbe26e2dadb9e03e3c9ccfc7b447f16ed7d128ff3eecdc61c6805046176f193e86a5d673ab32f8f0df63ec4869d20400d42f10776eb7d23200578cb9fed14749c0a369c8cad33bbbcca68e88b17085efd69e2e551c5770aa3ca79c64a407028d11c929dcfe92a381c9b3da5125049b345e28c639858e594eba9a9068c73c1d592b3ecb1b5be8ca798966000363afc1832a9f1fc8ae7493ef7ce2093b6c488195f5840d6db72b75836764f721e4f05658b2c37d287b03458e4f819fe453468a690eb089c084039dce0a283d8e7f87cbc9b9764095f567023371f2bb4aaf931bc1e8fb40f3cd7da33756baba8a0b32840c212e7c8622eb2e1632db7f1c10c8f4f5831b91cf21ff5e24555cd764132c41ddb917e663a965a150bd9f6db24ee50bd3dfe19b77551462a49b1dbfcc745c79a96aa164a15e566d09bc640644ca5f5b666c069898c752f963b3d0d2ab3cd7ea15529bdae91a99497bcaf51f0367f9a1ad0328617540626ad1b25abd05c6bf8e7fd9ef907f6d7081d5c03cf4ce01fa54714a5c300acae21cb5fab6d470842210446877f33379a5ea36959aeeb543b0ec22af8907b4b84dada6bbcde19c780e8fc7f978b9fef1fafe3d8ccb455196124bda32c4384876a91b801c27663faebdd18559ccd077146788b426bbb79449cac00485049504ba3fc7c03f61bf96674e1c2c2d5b2c0cc1ed9fd553a03c5e2ffaeac7e50857bc599cee2277fd700ef7829a4f4dfda282fc7594527fcc7f1db918c19b376e3abffdd91ca10d0cbc79f306d835ff09d0ba7642a7891d9d3673b10f5d04279db82197df9edbeac4c1606305c22ed6c65ac638cd892b1af9af23c7268db102b98b9d3fa73d0ee4353a917e44c0f41186e02295397111f4e7996356e789dc4315df8fc11aa205ac2f5851583f2c1c9c60f5b21358297c12ac20f90d168f5f73d5e673b84a7325068bb9ba7295c31bb95a72d57455420e73a5a388d51265acd8580961d58395965762a5835513ddc4aa1784d5112b206dac72790355486022aaaab012555c5e4c1515cca66a498ea18aa77954a110a44a47f504951a2a292a2a49a872f82e5430642daa22180c15092fe50c1251ce98693af3c3992c2150213342504a851e553a42d8c1e6278449131b844842871825d29ca0a4052d1f1227c4d0f04493134cc6683122cc9db172a649cffd6639e31353729cf912396ee312e34c1c1c6ee2701377d25596dc5592247f912fdc4951bcdfe22871c6c27805b30313b43de4e99013f36f7f3831feacc3accff145527113e74c75ca71a7f8acc3ed7a99214e8e8a90f368f453ec0314b4be905ef12c4abde2f44a3e80cbe48b47c25d4ecc31d227e15ab9be128c9b36232b63b4339994a81e2e1b07a07c6068686868e88156c64246f862188662188645341a8d462b92b15881d33ce71c2e4990f38cd2ec8004e6046d9f518e52685c3dc10435946a28856186916164181946869195f2032d6610e47c63dc1837c68d71635c08eeac95edd3466981051760ae2f80758199d912cc983163c60c0988861e48693f4179a306054214940510c0004e1b268d6ba30218d5892d2833b880004357e8c4074861541a63a06fe81bfa86bea16f680cf4d0032d46fe1a94380161187270efd53ceb129779288ae22af630ebef79bd7a4d40a95dec08790eca0e4c1a0a28ef0270119094139b39e71ee61c66b283721cb98befa591d5293d2db79a8fab2bebd2a591930c28f7ea1a7907e3c85d9819e85c8ec81cf9d0c89e79a63df0e0010f2021a0b58640be108041460341ac138275fa07ec4b9816f93034720497b3688c52eab085381bdb52e79cf38bf768e9e972efbd65ce39678d53f29496c3b5847dd1fc08ada3c2605cce1d24b0aa962be1529a8e8a20145c05ac8a5d4b3d2474e2b01c56354669c728fa4a8d518480c6282912a523f8ee24fa220e8387171f60f47582df871442314c081c0214c77297fb6b2e68341870bb42175caf1749963794265054e18823a6be8c19336607060c183060c0e4f43c5bce7da424a4bab447ca231d21654a77a422a43ad21cfa06ed00fa04ea0675026d026d83065126d0255025d0245024d023500ea05ba8116811281b740daa064d836aa159281a140bbd7ae289d235860913e697b30ed3f36c391f01ad42a9d033e8144a046a061d022d830a819241c7a052a8183408340c0a060502fd82fe8002512fa80f6817940bda03ba01740b1a856a41b3a03ca058d02ba815b40afa43a97cf952bab01ec5a43d848ca4e7d972ee44872952a24081cae1090e4e9adc6083af061a982c99410625bda724680ca8130a03fa02ea02ca4391a02da02ca02ba02aa029a047f08bbca8bdec599ee7c99e2dd4092407dfd062d6e2027b851f9f6c53397fb1020a54fde313b497f8662ff28a62868c3486d3d63b817e80f04fcfb6bf8663ac50da3b6c424a633893cabce6ec3e89fd253fcbf564384311ff0fb3ff3b77562c8a5cebd70feae987cbf82b0525fca9ed67fa24ddcfd495e9779f343a69cde2c2232fd7cfcac2a4c16324147510cbc4043622d168619358ab9934425e336b689ff57bba6fff81fc4ce6bb22865a75b5bb4b8489a82bce6bd961ebd30f4ee239de12e24ae50ac4e24599fa3861f8ac4c3abed4c362f6734f419cc748906b5cf23695edc1da930725c5286398349e548c657c9957ddf7faa4aa780a5b9dcf04001c4199fa88a07ca6ad431979cdbf3ce302942a0324b21ff52d0c672f53a71373b3c6ab8798b3b072faf0ae06ca00c0920c094a32af629a673e9fc4d23043f6734948416bb8c324054368788d7a8276567c89f2dac249cde79365c9dab4b95627e380ec081912b224644ac8969031210bcad8c88a901921db9269c9d2c8d4c8d6c8b2645bb227646ffa73591bd9962c28cb9a9204d5eea0887649433f5905655e67dd2989c742b15b58202bc55661adb057582c2c0f6c16560b1bc5eac0eec052617f2c14360a2b859dc242b13fb60beba53f67b7b03f368a855a82d463a77ab22924d98eb5a02482b4fb76a7ead4232a4feda942d413d4212a111505b5886a446d524b504d5083a83b9504d587fa4305a2ead49dca828aa43f578fa83bb549d5adf0a249f535b1be17eb94aaeb2f0b5a5f2acffead7ed6cffaadf593f85fc64c2f706e8b6bbee6ba85d926956db65e9e7dcd06f5eddf9a4d8a6af37575fd203deeecb94dcaadfd8e6b83fa20f796f2e76f4209407ef82041d7c3d67b1ef7668ec0b443041df7b6c361bc67eb7522453e003e3cdc254aa238680e8a023d511d34455114d5eabfd51f0df6e73d8d9e41a9d02ad40abd42b15034fd799ad59f677408d40c3aa2b0151d8392418540cb5ca2e7b8a7e8a98322a71c3939729c1145d9a046f4e7a8d67df9aca88abe5814632047589753734740befe214a000cf6fa04c4b54e549ebd0ed447e9fc8c549f1c4620e8ec1b655ed90e5b9f82d49ea002c0d6a71fdeb39abd6c3699bc3c7b7e644ee0ea4f41ecfff2ccc6c4c4c579f0252622c671f481e9d3f1e1cca6fedc00655ef90a332ac3d5d569668da0ae69b4a222171a6c50e6d5a7c3d627d5a7474d008d56a40092462b2a2a32693c1394d7bd0b9d2d0c68006c7d02e23d188881d6d772df0b8ca77d87ebf54997a6e04139931c69cffcbddc9b8e0ff77676b93714dfc36fe0b7dc5b19756fa6967bcbf23cdc1b0fffe77fe5addcdb0e7ffe55ee0d766f1b8c0fc77d1001f0e1cf49752bd8472d7ca63f2fa9fa73b2aa3fc7b1eacf73aefa731468fa739dac1f8718cd8c445ca271aaff3a8e81ad23999d7514622c73897450a8fc54790e8ecacd9254d971ca6764331ad19f8f5a978f3e63d6287589c4ee5a335871e1af635e84c18f990b5f6b38e55beceb1f8e0e28f3eaf2a0ccebeee1aa1f10ee59bbcfcd93c958e8508b5c6b31f409772b56080909c572cfa4c289fb1c8def093971ff03fe0f4b148ed0f7bd0c6c0c20457952c06ad86a1ff4d87ee84fa0053444980032e1bb593db9345593aa497da46a52355a67aa75a63e4ed79354eb4cd3a4a9d629e3595c0e9daf0eff5e2e8a5c461e4d1a9787857b7319a99e264abacf0583e4ab7472722a9d9c9c9c78bc2099957574a43f3f6acacabb0c15249d78b5325892bcdccbb3f7417d3be517c5253765217908506a476e6dd95ce8cf6dbc2d1e8f942a23e5c4e3f178e4cf4dce65a660704fe1f57c506e82bc20d973e3e305836e829b97773cb2e773c30b067bc8cb44f28e9777afa053cf47b2cf2b5d4ebcd00d1924cb201974ea2de1e51d2f0f114337c1cf227fcd67059d48f679aed14dd08947feee89fcce89c773e2edf26c055e6f496ee2f1782fb28593249faf856ab7b4c056aaadd25a69afb4585a1eda2cad9636aad5a1dda1a5d2fe5a286d94564a3ba5856a7f6d97d64b7fde6e697f6d540b350509aa9d82229a429a3921f178315dec488c17ebc584c44e880d891189a1102b1233126b8a951033211624b68b9110f311fb110312d3c57631166248faf3d891d82ed614d32d79d18bf97aadef856c2aa67b110c3a79e29352b5d42d35a89e51a9d42ad54abd52b154346a963a846a4625429d52c7a8645421d432aa943aa5b2518de8cf552d754acd52a58e8272b1dc91940564ee655f7692a1f2af5c3396bc3bca505f13f194afb97ef716669b8f2d98675f53d27dfb35a51f99a794abe1ce947edcdac37095745fabe1de521aee0dc6e4de747e8697e195dc9b89f314ccb60f26b9b79c189c6078c1857b7bf1ee8d0c069d3cf1a5bd144a30fda551419cd486fedc4c6fe8cfcfb4497f8e2275d29fefa438f4e73ae993fe1c96e69042a54c521a2e510da9924b24433a43ba4407b68302c73c535faa434aa53f4fa194a92f852ad9b46704041aa5bd0b0840ee34ede872d3ce8e46dbe1a5bc1d1d06e40d3298b56594779988114e6e2797550092565454e422186a172e888208d080dc4017b908725054e422f84e3d9e4e9760070c20b3918c847c6524b9857bd3b9ed6415720af9488e917b2b726f2950b8379e3c249f9085dc1b999d7292fddb517d43c9d936f4e738fb86fedcdc4dfaf3733be9cf516c1cfaf39dfda43fd7d939f4e7b00db5996c1a760ddbb7956c19f60c7b096c4707c59983636edfd66153e9cf3794eddb50bbe702082c5d98b9dd6b80033cf059ef92cf2bd1686dcb799a02d3a71397779708986d792be3e7a5bbfe411dea6090e7c40be61def6bb8c7b9e96b3818b331809e14e54951638f62a8b4036109924c7de4d206e33cf8520fbf9c821d946b1faef2a09ff1258aaed0ace1ea38d4fac545b386eee438ee143c2f927d2671eff52928af453d2bf9b33231c15c7e701451138e3a6a32dad29f1b45e9a61563f9f6f14fc783a63d6dfff9acfe5c0d62d1f7fac4396c7d6202e22ab8e6ec33fd0fe67789731eb42515bc87c500886f5e7b65c002c170778df00b5c8842c75c85918e794f28a407f530545e57e8f5b17cfb40a9a9355b413751197f8eb4ff954b94b38ee712912a686c9e57155d27cd3a8c2df7fae4c3f5fa44faac4c4c22f0ed87ebcf04af8e39e6ae8e1d70c4bb5336de357a0da5638eabfdd09fd780f7a6b93ee2f5c88066b3d4d499404cdd0236906a818758172b68464647b8cf1d9579d5f9599fe773d273328392f98e51e773cc6bda75fcbaae5f850827e658cf3f6bed622fdf34c2ae9f7f5b84de1d27f7efe29b3fd72fac47bef7c8f7aaf138ae6188b5e6a3d6235e431de2fcff1f6a1dea8c3f06320c726a303dc7778efba9f1de69c769cee572b9a6a5cff9e6340d798f428e7be72eb5f9e6ad3a2ec7a72feee2999317a7812e6eee12797fb19bbbd690933bef7418f2ddc57073321311f7de221fc771df0d04b4cb13bf50d73aa4b0e1c4e95e3e5e75681bcbadc75d8a672f9fef6fdf25aea5be36bcf47be79c4ee6f5a76d35d714d01d875227c5fc21bea2c8bf8b9aec28bc98457ef70e459c4bcc66360c1286617886e3edb8d2c64ed73c6badd4715ca9fe4c8ca3388a9a8ba2c945760ce2848de338b2228e18b7e43851144dd3344d3eae5f2b4d93c595e5ed6317c53bbb5c14f91ef7288aec8f00705a6471c4e831ba66452aadc73164b366738e7255fa16c7477eef38eeb5e4323ef37bf7b5bdb88bc5897cb3e5298eec796b622ec31556f6dbaf4801dd435c0ccd86d0c31858fc9f0a39ff73ce55a4ebe9c41ab7a2df8fea39be3f2b74892ee731e1c4fcc220ee9cdf07db2c811d0f65104c6c46e85bfc4b248b7b484e2cf21076e8db4a1f71493e067b0337700337700337d0c56d882150e99b8f59029b8b2f76e4aeadb9c8431ad2d0257afe63e8e7fafd03f941f99ae70f7429f441b695fc7210b2cd24b16e5705b7022897b4fe423d67c55931072627590d9824e7b197f0357af172c54b6f92fc974492bdd7e8c57d5a309db74adfbc4adf2150e9a6006e95be45fc9b82aca444a3b52de7692acad8f866516c87be15e201b2db3d72b2288a2c8eabb8cf661697f6300cf58c6f4319fa97a2eac4ed145f4e0ca4917262d784d4ec6471f7de6bc1ce11f634cd99ef6b137966b5c67a49955c9bf0d725888cd4c411e24fcb17120989a614038974f59b25cd0b33e05c0ac48564503063c7c20858cc40fd4892438c169a233f2d4ba23491c5e525d65f497ee2fab9d972d5c6324986048f70b1f294048e178cc86205b442b34bc1872678778cee3877a5ae15ee465d609a1f24928c3445b4dc24c124273cb5366d648e01dcd65deb1c620c84965513bde04b0b85561214e10e52052c8aa28d9c6f318dd55a2b1c410157579638b204103e2ee10821f418a205041719aa7a3c41624cba42f33788fbed3896484a31dafcdefd66d11aa2444e9af2155240c2852754b8ecb8a04b971d5c50b0e261cc98716a64fa6d6112af76d862640b1912b45cd100abc969081b8060c2cb96ef016787966e24c3e5a9c70390236b8670b3d57483d48f148e88c1a1d7ff8ac94b847c451c93a4ec318965af72d0557663c6a40a92b4748c3cfd66c952933595025d3ab2b0e054e3108b3867eff6e5ba59b2c07c386a2ef24b94595c0ef19fb95c4ec7d2632559ba36566d803c5c82535b474a602be0d48903eacff18cafe170c07ae61b60aa2d2fe5b9db70e0eff7fd565f9db89d392f81b25e537e0d57d47fe4d885357fbdee8fa36b10ba768958dc238eb28b6c05f405c2c81086d7c33741a7032754d5100d29877e02951591a6a8440b191462e9684880008317000018080807849124064220c975e50314800e608446645e36944a83811c87611802310cc34088210018630c31c630c65859018621d1758f9e40655cf568492e53268830b4c85596ea483e2a7c0529ed05b523f8072b1b45bc3775096368c12a525c21c92916148396214ee46d21bbd7b26d45ce439ca429e4ee8ca10b31c9e4f34c840de024274c649540856fe74ad5544f8c9a039b36e6dba314c1f519bbac4ca66d3e49fb7f9e12353e6811f76a705c4aceca6371278ca7124a7cb7acd7e0468bc6657b679d0f1bc2ddcb474043f42d199f5acd8f80aa6f17008bb7c01e5acce891709088e6e0371dd577939fff3d050ffa94b78458d90404d2ae17f6f23966b21a80c59f520cf49de1816fa786162d4b271d3c9f8f72c16544bd85dfd8555b0ba27f0605951ce69d87db4a1cfb2d668a71eb3a1e82c0ab6f42cb3116c42b09cb206e381a93db44427ead706c94a65fccead8303a15d687e29921b14d5615a4e065da7853da9fee1432499d1041888b30cb7d96428b9323aff9287b5ec874903094b77af846132d44b7c1d7e413a4b1307d811824960a0dcd51b46a4ad5cf0ade66e66f863af956ff782c99568a4ba3126cfcb9d7fa70f8137277153710dd938fa4aadd19ab019495484afb6556dfdbb0cef275bd43d1f63052921dacf1f364682feff2aef6f5dad1f4d171ee5f63e30eecc856469207a5bbb6e28ea41d8831d3e98824ff5fa23afff93fdb77e030831740c3d9c3b1fdd1ae83b2c0a2f97716fe4c2004b02d0dda2868543a221725ca6bf7047c52693bc2d95413e12f406857933899e28bbf12e74791c9fc4d0599b8fb08dc6d483c1fc400db2cf2949e8afe65f7b52b48023c802c03d3694eff2642edb5dc130f8a40f73b8288f6a9061c5439cf19e573c2858d02d1b7477a2457cdacbbee7979446362ea43cb735ad4dce848afcb80ed8140da842cf3823b68cccb057ecb4757e1814098b705622f71103af66fc89005913a2927cc6e7f5086e6a9807421f9515d340b2616daf4acaa8b736985a1e34b71b041b75682615dc72e8e5e92755ecb1b1488bdf095993313e2bbbad6edf5dc26b8bebe3812eb298e89749b8530fcef01a80b4dc4468e41562fa8e28d0d07e2ba796c04c8afc3e92471e36f40eefe70ad64d1f98385a172f37f657f0e7d2b564db8806a752e3ac521c5dd846866e95cae69f02f40984a621c66c60f4963e2e1a3beccc5d5860364eed26004ad9133acce70b5267aeb627f03a659ca448f0f78d4fda94662fdff7f12fc14059f280a192182fe20ffce1d9e13bcd25f99bfb50da725a124b59f19457438c6dc8b1cc5237b16f2001f358416d007f6bcf10e09558cab7ffcf6e45a520301c0d5745adc136d0e350d7a11581f89c5e3f00a35469582427234355220e6c866c8f98afe1da885ff891e84656f0027a07d625a0ac3eb099486c5de7b1ad5578106b919f55dec7b4127c72ac6d706b7b40ad05df6a3f837eab71020227b741712ac09345bba6e8da24f4b449cc516f0830edea7945bf9f02764ed5263cc76e228c2be10fd7ee72fdcdde09db01d51adcccfdc1fcedd33dd0795ba97189afac6a51ebf0eb92437c7aa54858a26e228fde80321a8f33019f20b1904e72de5ed3456568d41bff7b990fe9e96a088be1e6e40dda589b2953247ca5ff96fa2c0540e23b0d5d9dab55e0bc09af6d04dfb87dbbe6ac1b0d4a2e6928bee1caf68ae78982d4c58ad14bca39bc6182098a1f6f28d2cf4a94e7bb2e77c5bf34e5811c69ea504549b7854cfc4c1d55294c51d24269f2597e1b9cfaaedc2c3d092dfa663fac713f68866b88c58f60e8207cd49aea0a94073b34dba34a0716868592475a8d466c6f10c2c588bbb43c748af556b02f95058adf2f4c4bbe68e84a36ee004ba916119c2d13e2c780b46343d437fc17d92cc7db2b60997bbedbfffee9241ba117379421fa0bda600965091ac5c17d623a455ab8d899c45df92e5ed676001666af20313c2db8298f076a41e4e170b423968ebfccc8b19f11bd9b241dd1ca6f4f4af2d371b700bbfe4fe110ff8ce1cf8e78d3d449f354564f33e1f36a327eac4155a1bc631019be06e0409927d20a24ac7110413fbf76a38c3dbabc3daccde6f36e9fb34cb9c1571e2ab70fcf87c12208ca6a43ad7744027f756005dbc248fabc0bfa3d491c9ca52120644db68b8ae33fc92b08804c1d9a856464548bed2d4471c1f24d8ce98c2fca0e71943ea61902ca80bf3b042717ccb29cb706559d05ba36a2b6230cc568e325e431c30b4d9d86b3c809a149f7320a8a9a5d19281b6b1266f8e0c81398a5fa95066cd3bdc68445b2cf8440ace5e4f6327d12bdfebd00bc403a4d840c239d4fd7cd852ca313871251a097e6cbd8aa1cbc3354cecd01b53a8d72842e754cc97a4260c408615d18e1835a95038c69674231fa20f25479cad5156a9244d9c8f10d1d6e72c9cf3cac3314637fb243035248a1f7525a171a2ae146c4488dd1aaa194bbc02e8e63e42d2fc29a280aa0f628fbcc81de63a40bfebc8ae2cd94e8f77075ab8f3e3023c929d1de5a1896a205cdb06dd8646c8e4b95f3d3f2a47c19fac1d8cb54b4b3b54c3eea3f8e03ef2b4cf7cc9e92afdbf0de9e90620499efeecf632bd3d68da5396cdd87c7c842a4f53eef63c071db55df5b3cab9587e8b3c1ecd9828d71566b86188c458fe1288365799360f49da1e2480cb09e0388af03a006a2ccb4e1a69891041b5b53199858e8d412baa6ab4310b6004ecb88b7bda8c41c3984f647227e5afcae463805af362e41ee38cee0e5d527cf5514502656f0ae112ada505c1834894e516e35ac046bd49060d86e6597f24a87efb1d4e3e0d31e9462cbccb62613cda374c274529f284b6960d81173064b9c63159aaeaa4d26f861742d3191f6da6e8b0a9cd48c59194b34c41379590b4a60b9822310b76e607becc43b8e63b72a510805952acd495cbb32c0dfb0c64f6a39d7432a55c6d1f87979080c3819a19b4eafeac58ac0c05959ee6fdb927c959bdd34dc7ae24973d0f792208fe3691037f4aa96be244de4e227c7646a57eb24e592e838eddc5a351ee779cc5dc39230cf48d84d216cfd5c54a8c27583a45b328c875645b325f3054c018f50f21dac4412053e4271be85943ab62cd10fb5157b718743ca122986efcb6b29a4407af8acf5eb042b5e01aacfbca5bff0b50a02c9fc1906f29fa4b5f880ec0b2ad4e5126ee4a691fa4a81ff393a3759df6e35aa2e9218d2c99733332acdc37d36d08a499b793258b60345c365180681df9cbf4006930734cb9e2e304ebfdb1f1b1d63680584cfbdc0b1fbd2b46d2ad8813be33a813d2c5e699d78ca498aef524f632c43b93b4da854845cbb2de09fab895a8b91a340eb5cfa1c572a18e8efa54a87a877c4acabf2d993c4b621ac83c44388a7c42977ea17b2cc4aa4dc2182213ec8fea9ec801311e6d6391264f553923a2f9e4cf3d766c12da169f5f56377cfc7f80fc8bc2164304dad7d78c479504174f2b5d66ab919a89c99afa1486c33baafe5d7385eb959bc40d9c8fdc4f94c61a26ccbbaded593ec2839becd4bb76717a407f711ce946e75247a6be5e569b79fa05039b050a8b0cee5f7a8b71ac5260a63fc35f38382bb6767af66021020a935568573ad197afe9c5c9994fc42a17bd11efa1e34073c96a4f2e087b79c93c8884a508276d92777af5b39f2d01b6d2c496949a83e92c80b09554d3c4767f1a2901f0b7052608737a950c3fc0058e4262e9c57e8b5c12656cfb5ae7b784b89a25378dd8c300c240c8055f3041a7a1e3de6364a8982f569b1b50cdb582f3f4be47af2ec44a582b5983b8763d2ceb62f035eeb0adc4722d73ea07061911f023b12541dc8f2c4de5ed7732afbbfce0390e51f8b1de85cea6df0e21f8d1e0a5d3d37b52ef8658e6b6446b613508dd54d656b7a3230e83a58439650671cb8faacc0516bb862aa503e3c9aa347d2dd70ecc2a06651a43c56adbd724f0a1a9f8392cefebeb260625a15ed81247a97f100f966be7121565c1ee938c9ac4e19fdf4cabbd217014658daf77686bf98111eb046ad2159783eb323e5956d96fd3071ca522127e98edb6e52ece278bd6c9d3432300ee24f811c4324074ad91a6b244355355d14846b59cb24cc8552f2be1b4453c87488f2d6e72ca527bd09f8ef2ce72b6d504943b2c07473b2a55a69cb2aecdac7760344e5b074f15a35488e72ba72caa8e1da01b989d59613b4255f33868c834352fa2d170e61a5e085db96035a46770e83249a343963ac2c238b2c9f5a45374f3301075d8d5fc448a96ccc83d0df7ec9ea1e560cf3f2c0cf57b9d31a81228a631035ca71e99c8190b01d574ff47c00af9da5e0d9b370405dc0a96a91f0488ca106c882b388bc3353968aedcf999ab5d849cb1b395c5a4842d1c648a951b7dc1047c1e08169d98e0d857a1bbcc0cf7424b60b09ed4690908574d8301cd4c2db902aca1191421fd5d1f51a16fc5ecff5089cc5c2d6c81bc7c004d24a544e916ac4fa5abc9794c22920c619b0afb96d67ea10b588b71c97ad954499b9a2666d959700549843b0d6cb14bd4b7385584e10d0841234a8b82d2524401169a026e02f7c41cb027726c84fa3c28c83d23253aabc674c9eb0981199bba54c600493c065b9218d4bb7e59fe4a9e67c24ac6b080ebecdd74a92fa72774c5e982f4cf94080b38ec312d1d6846d872ddd2735e3edf29a9a2ec1420386c4cd906a10efb4624ffbd55d6fa7da09c56d6b8f1758a9c273bac3042cb8e4e2457592171fef827888241f60940add95d7570ffd7b24dedeccf08155c69fd72d97a4faf11471e9a33ef96dac06a9732895597cd49e10bbecfe446a0deb159ec1cb86c4c28cec0dfffcd68ae0242d514baa625883fd84231a966a3224d87d9affef31997f4cb5298ce0c42885755d5e27614c71c49495f0186c57ee83bc8cd7d9ebfade5c6ede905297dd9395236a3a1cfe18aec6669cc9543800e0c5215ec6ac83b7b55a8e4198314d596954a7c58c7dcbc06c0967a70d99e00bf1677a30ec831f6249850f7d297567055a0f3f1c75edd4f36a371ab1733421637220fd4b5a900446a99d0d4f8bd11e7acd505ba1049b61fc0cc561295dc31f22df935184939aea7e457b7b5b5669120e4d116f00dd3fd28e895cb9d7b32155fb1802a366e8c9197e2ba32a89cad5373757ff257313eb7bbcd623b17c3388129753b23d9fc1a29115a1bd9f05dd2001048059df43107829b38ec28913c55fce84788a61c31205ed31eaed281cccd0898ab6eddb8e86e9aeb71d8f861ef88f3b23e88fadf60ac1029fe90834ae5d8ae7f0c58fca4c614381b60f62df7a9827e98e892449efbb66a78ad72ed1d8653d267553c6d16d4e1796300e6542c50b3392217f36137ab73b86334707b7d0503987df54fea2b54dc433f638baf0c5db263997580fedcec5592b24579865df8ae4f348f9c4880999dea0330807ea110087d20afff9150ba5005987937e2a471f49b5544a7f06e369bd49da9d4e4fba0285e534f93b24b9d79dedc8655ae18bb27a51ed22574861cfe14ce7a601ccc862b0c937e2e28d905c675f5c072c12a0bf51b09c8f9b457d2809a5f1c75ea7ac252d3a676a7fb0344f7127a9a94ddd1a167e636ace60ad97953f6105d426666218ef08417c5c1343ce218f9ff8ace5e60bcab0df00b42010e51e75f04a4c0737f6d9b86b4e9639b3b0be261e20d75d06c0586c40c0635c97ac1d9b79bfb36cceda1a3063a3577891427578d772b34e26ca1219ba998173c594341d6ef6d7ac60c0bbaa2ce526d6a8a32ca0c3597a8d5298c13cec101329ed64770aeb35d82e855072ab5dcbe66813868ae906ca590cce0fc0c5d3b9152685d657066552d9645a290582af79c3bc7d1e6c8425d496ca5bf8f67ed5040f94b41a17f95b68cae12c0b2f4e7002334b9d333acaa32b7a174ae35ccc214c3732c4a618e5b06ac4ee22bc95a6158cff4864806ed004b006cae19167ff31efa39658357031d5263da8770347e1312f7346f97aac2e1774bb82021aae26d77e28c216af6364e840ce61fefa6631af550238d2c75b40b8f1ff752fc8f080ef35f1fa4ca30a039586dffc7b34b4c7016b736ec6509431a0c10c3b339f09bdb3f55e077b5624b3fe8d11a1cb64094845c70c6c7b9c392e1e713a43c3c587d29382f570564156f7de1428be965baecad2698ee1d9cebd98d4010376060f4024129c9dc826fe03982f38b14a3ad55357f21ac4f66bf67263f7f17c082b0bc661d0fc3c51dc5742f35eb788237c1d99e9e3f7a337bb8c02138fbd96c62e4d1dbe604c179fb06c69159a10568d711ca09b8656cdd641f700c08119194ee3536da0567dcf21309cef55108a647494c087d99d9e7a98125c186ffb21be268029356d714de3e5b70aeea1de6d9440abac8076a2bfe94edf8646a6f8632e67933fc8c4425a811ceed16c022de298f9cd1631794326eb15f069966a9a8b27e378848525ec513c32613f0b6407d08ceb65ad570efb9df2433f2e0897dcb6b7cd18f1ccba4e2659f37843c660140e0ec389aad7f46ef43dde1997878eefef75bb260378f57f85f8112d7519ff52b36f0ac3407eca7e3b6b1ee0589617f64a5c7e39e905162c8cb011e48dbc6b79631a7c49d21065de1114ff69c15e47077ecb9270d9660bb8f063205d4ddc70d9c661c8d0f90cdbaac2dc88295559dbcc711dc43ff4ba32705992becd6120ed988db96b62a1d1c972449f360b9cbdd84bced2d95e44d2d041e5f8c5332ea3536591db78285181ca0ab07b1999e2559c1c2661149dca9d0e36fbcb7df8836ff570699a935d92afe9fa00ea527b2bce6c28fc1c4ba967934d16c0f1fd92a1b72aa45c6073124b7d8b357d6b01b9fc22d3ac74486aa59f143a2836123843ac93bf3e7ba7ae56f6477dd2c1b2fb2766749acb6b069f6dbed43c0d254a2d8438d9f72c0f379fb0a94b392d8fe5da415d797ebbb04b39db3edcdfe5b50a1c747b06705d7f95b0089c11dc7428c581bfc252665b6c02e5ea5bec76003527329b008ae7a08b415f04be29d85032191131454d4a8e755ec9a14797bf6962419942033a5a305eaa8beac51dd41a596d1891324a39196305b17e172e7cb81ae98c3f28dfbb45953efe044bb9cb57759907b6703e9adb209abbe3c3cce1fa3e02ca6a9e6d7be88e3f7e077363a34bfd4a4bdedfd6e6ed21d4692dedad371606f7322635fa5d9fd0bd1cbaa07fca13d91d88a2e3c4f72f28ae10642db9ceda2c5aefe8618127eac5883f994bd5752886056485c4d19df41747a171874319e0c2dff04ff046f73ab45b5f4c08988985cd3b6ba3eaaa3343cdb7b7b640b2f47b564d4f9701d20d94d3c0ded50163d3bc4c940d5cd4bf8c63f35c43302433bf7b059b5c8d137611a90ff517771df7eedb12f57dbc4df028d6b73d300e9aecb05645c9b7395f05006a966cab9766c07effe74df4407185cf8f360d8392e31cfceb279f6b563d3459c7c2df617cfba99f1a944eb4e20aabccd5c91b7cab899381ea4d21cc549d3eb93183187b64c22a72d1a81825faf4cebafc2f00fad28782faa20208b3978dd145c098209741d3d29f6937ee638f7c08cbd7ff2db62cd6b7442de25fa88d41028a84fc356cef3724df099887520d58a6ad272715c0aeb914e98bf30219c94b3a5f41d417a2a54b60c0b50aba31a5717651fef67e483939cfa1058f96dcc05a6b505145940b70c48c934fc81806dcffc02fdfd1da77767f52304491003a77fe422e424ebcad3ace8421328ffac7eab454ff81ae6d2dc53f7db4fef06fe96b09815261292fa2dc15eaaa397d084e3bffe30e01202e3854508ec5f36d6e98802979a462e87848a1c8ae97670a10b00cc3eee2a7d4e7bad56cdeeecff5d9f14bcaebff84d70fa084de43731806fdb20675a581d09b0c2692d371f0ee75fa89f3fa8b1a6451b0dbab019e6bed88a8ca4a28173976d61c1e37e11073380449a94eb4a719328dc74b42e1c93c9fc1c0d7319f48d7cd41753fa04f56a68ca6467fea5108e0cb1dea11ade5d93cfe7de95dc5488f05ccfd213ce446a6832938d42c40a0ec94110c081c83532cc6e45524182df9f08f414fd38317c332e17033b9d6340c95ddf0dabf460256d69cc78c9f37102aa6326cb9091fb3d31654056b3268b72a937d5ec28c1c82ffaad440183aab19ac66929e58b9bd1076b82e00e0acff4a99229c1a6122220b8cf1d49cfab2ef257558f141afa8ef808047029834fff894355d97d1e701229b5d4ee35b325a70a38e1961cc9e922a5df17d45c21cb181b34b4aff7d147570c111407b8623ab4622fb05fdd15462471af126fa55d913c5e1b3f5736c07a6697d8f785d87085630c731059bacfcbdd8d678daf6473a881988795445a1c24994b662c755196ab35ea75c721b55f60ba01114c0497d78192ce2155bd614a8c6d4df4624b5f2e41d5c76430fef8b8cad2a2560c2d4aaac100b19964775639b195d98416a02385f3042a7e909f69700273baf37445ac2623cd99dc4df70cbc39b3a3cfc0219dab346fc39b119f3576e460f5b23109f791e407de240c80dde6d37b83515e4faf265faf3af099b987b55c116baf4a284497b2b60ccc1190f2637330cd631293f45e89131711ef6a045d412018f23f0252c3535f3db7a353808af4506c6899b3960a00448a97d6d7e4ee57879f99fb5acd1d64e74d79827409c7c041df86f35e7806a3f1a520e49fea326901f15c1dc0225865a1ff4600723ee957de1e1d571b783a2bf65bea8e3559719057bd4c8f26aba13af6b3b9b5d55c4156df4a28449792ed34fc199cf14173309b671a20f15f90d31711cfd504a082451efa370290e7935e656e511d0269761ec552429ea5749d1192174e83a7beb8d5a6fa92fb92e1e6c770824925ed396ed134821f72bd59970c476bd7dd23144328d92e904a85643ab683767385649a87d9ff6416ef3fa6b0c08690c40b956fdf45368a5fad4dc7e3071e3c139efe282274e0c7838a16da3240ce3c9d04d4f95cec758a9de301d2e1d89a1b2752cb751c5a82d1c2c8ebc9ecffc95921f336f17c0cfb50e9a4f048b903d22fd64193d599d3bbcd643e838d72cd6af0055d7db8946c22235e4298d8dc38ff8d4f811840ea06d10cd9789308546ab5554319ce21c3a6a3857c82dee1e5dd30b48c64d8fee9baf1e9914834e4bf38f871e88e0e699e1f9202fc67828267546a1e55badfcb33da2cda971b0c1fe053487f87daa15f0ea268241aae9f03540d109a5ee772aedf4893c15f8b2caca08c460d2e7994d724d11a631442a4d1da92ed995da6273515fcfec27d388e2a62a92a52f26ab5cd88af9bef1a87811619f671270e82a572a45ece4d8ad21052ec4a112521c4d052bf740ad64d7b335c47c5cde3753400cf42f44f2f1b7ac52452b6a57293b82cc21a0b90349684eb40d70936f9813503bca3e40c35526b74f493537340d4024b472c2029fc22ec7c537c7fa0557e66693b39408dac1eb972e2c9418e73d6821fb5d6ea8fae3a41dc23144bf21c2c0f4979043ff09b64d59288e06dda237c037f446af4f6df07169405ba06e530cf8af4a975a3701814f8da05fdfb9044a749e08cebdd7847fe02a043d8698c8e3bdd6b45e6f4be1c056215c9d6754100b82ebdc49ab3ced282e5290c2599b3b88e0fb1d82513f8a7cfd1302b3040983914d004db4eb9400dd19226843e040e5160d41f0b650c6d66ec6bfb21657d52db97a4a072ca2a39d4fe2f19d7e54591622e80a434337739ec8822e589ff63c432b16b66690628db9523b51ecaf79506a912e3bf10600e1e063a98131fefc4995d450f07732216b55521419ab66d634de5e642e835b0726019886b09bf45edc3bd09f3f511d3fb50e0ee3316c9d052ce99efbb94252b5ddaf4d1f2d2eb6322906f4be891f5e06bcb709df30c267e559ad72c58b98d38c7a9894de601d5dbf2dd57575b887eb92a3b129aa439def8e11fd66eb0e279c700c056c410d8753983bc0983ae9c35e76e5aee3fae04010ef0e744460f241d642a17e0786552bb266d689d94bd01d7c59cafed4d4d0bcfcb57f9f367c77173c258f540f473b4cccf298c7e4ef4678e4c886f2939442af3336254acc2902884cd5c748ae54d1a2fb450d472d06a00ff57af444373242a7162b694bf56a57d6a1e9cf20a3308d903f215fc09a4a10d7635428cae27521ab880e22475704e961bff9700170fcc1bfda3e63024014e2559df23f729cac9b481a7d16a1ecd04d8370df979337a8eaba8ec296522a7aff6837d9bf61ec0ed43a50203832286ca91c5e2eab1a2dcf5b354386502f07d2d580799305a02ac9ec58c91527b3c8f7eedbc98cf770f51add31ea34c4a823ab3cc6457d420ede21547d13f7d2860aa16122299741c9f36de441d138556d0dae83eef445319bc8928bb885356e27adfa2adb9eb38b0fdbc16f360db97fa33b1c51bff40971465deceea723c414ea07896d3ac42a2c68da5859d413d7f0ffd17a53f37593f3464dde618eaa1a489c6513a2a9727ce10d9fe87466d6c345f3f349022ca1aef0764efc6c65da668a47e99682f396aa6c144639c3a3c7ca967fbe792363723fd43f6f1ce8127c3b26cee2b040eb2d6364ab71e7bb1af4143c8bc454b66f2eeb0b3a142def02baac68869c3287e0f271128cced18ebc1f2b7c54aedefa086cd5ae0139e27827310fa3c99a28207f75b5ad3d323da3037d0651fa3ff5aa4b6a16ab50b8835fc64594308bf1a1688351a4e3c34ccd57f93d508ede0891ca307b1067385652e18b1e139b68ea13aaf0d10e458ac3178dfc4ace174c1632833e01daa6e833e3ec917651974ff5d1be81910a9e96a73570fc51c075b1c2eb8e7bf7cd2a2fb42d80c53ffb68d367b3e2af9254b6afb8e1e6a5930488cc8592979ffd63df8f7133612e420a8bab704d8e903583b80826e9317299fe9d3cfafda9a9c44e6251f6a7662c7eac2ba2bb0dfecd0722f7c0aecdb4b8df27f082ef5d28332e55e168943cf016847da6a26c647bace4f5727f71a21a65de3f395d2266c1ba2d109219b93d2ca5ee22df92047bc9b82ed3585dc1aceb8a45d12d9e31d9666dce0d3d3d4639507c92272ef5e7e6e2b242ae59c84c8e1e066c20e08a4ad9ffbdae2377075ea5c9fb8b9a642759e983abb1cb9246821ce6e785c12b82426f9c0c52cbdd815e394b98b5dbd74d1b2d898b4d6b5fae383b1abcea5ed2853d5d7c0d3e8a0d061a507851a96d42ce4129fd97e0835a1ea22655fc07b0d69f0d71323ecf316bb673915914422500f9ca730e9d7fc50d15e7ff5abad842911b6a481682dd8761e05f1fa5a48fe51a8788f0171149d1554d1e5afa4345282e3fe0a1d86aed24bf0628c82ab0abffe1998b08befceb69773d57b54f54b41e7fe7e664d9a6b680a0f164062dadb3bfd92ca3a5af62bd3e8d340439a878f473a342148ec1f1d3198edb0b76a7ea907eb3919f642205e18175ff20aa04990093accd5316903a6313c9490526600cbcd68066924a9b3770d9a1831f251181824966a0d4406af57aa2dcf4481770c197d1bd88654e81f6718b01c155a46846c74577f1e8c3ad197932052ce92732f6204231d78da6a4cfe7aba40092ec5b2ada6175e641b3f5cd76c38ff6f1fed8259f2668e372595453c98ea17b4fc53cb15818675dda5f1f87c0fc9c2433dc931704dda6ac106a5c531ae9d52a6d6b8b02be97bc188d96f871349fd9357758d5b1d7cf8ff95d2de20c7538b2b010d2b5d534a9b32d4fa5bc7495d5be2244e3931b71b0602357c81b747e238fb3ab5350407bde17c2c364760f8fc58516cd56566f90bfcde3b6b1bb93e7ee30593fed726412bcda51d47690df8af7ddca3919492dee5c6a34a25f8517efac9587c3bf7cec61ddf8cc18eaa168c954146f0e3a401020bc75c2a8f4a88b023e1e524149ea531278d297c3427b20f7ebec467d1711e282d117dab500338cc68431934160afc67440330d85a691d969107802020eea80c18b931eec858b20beeb09fdf78b11db45439d058d64d9a67fbf0efda553c631e7a80e0d42588efc01fa9d3367c14265f7419c49293f743272bd70734ff5e2039b49d7a38f5c73fe7a08c431915ed5418324debd5ff2ab2262747ca7ad1d5004b7b6e3223c68ee5d85cba7065e6815acd627599b9d17cf2351ea25dc49cbae87fe18b780a029ddb2fb686d26f1c01d70c0830dc4d493a45fc42cba4beb5a22e1f428870112477a7e163189c32fde5067068084470f612f24e573fe85dd451258f522572fd3fedd7e0fbc776572193cc5ad8cb948b0eb55711ddb3c4475a1f4d3c55e54a02df8a63eb9c76758fa23b29f63d1763cd565b781b5a700c3f029b8ca768f74a84fa8d8fd2e70c0d01e63fa7d5388a0d11ce6cd1aac6229a39aca29e74b354ee51ea5d0bca91fdb7ca12bd07c0605617808ab616148da97071ecbdcabaa8fe0498bfcb4c41244067e8feecd1a0dc2b427fbc97fd9af8da45a187a13a712b75772a730189f49313be5a68524154fcab2cb9b30bdea7f5cacf2e66f007d3f79524ce279153403aa49832fb27e195f543c26d44b2298f571880144aebf40f32921f0ce25921b978d8e8a77be6f477dd3d1738d0b2238298bf6d8bd7bb278d26a5fdb5f35056b803a1c842003ce48e690422cb5a60b42f59d6f42d60a0762cb34c809ee65e54be92494bd41880dc4788a13446cbda260008d1294724ca80b7044244e4a1951122a5e33243314446b35863b4acae469910aff0cce1fd80929695de18c0721c3396db537d83a92561c9f2d10205cce9b4ac4f68afee3b2fd6f5041fc64afa389914ba4cab31e56578df528c279e80f05ba586a9c2ac65820bac3e28d5046eb58398f5def42de3feb2cc54496e2766b3c5fd3a9cc513c5ac6cfc5f25a647d4acd59d05f5127e913f3cebd32032d7b294d2a8288338fca365cd4c53a0177e07c75cadaf664d62ea0c67b8d79ad559f640fc2fa0a217fca6cf1ace8124159e10f00b045813a5af03df624f642abd66a538e71585f585520790c05d62b6a958b02390dc6141a93ea25e0dc974f489996c56bfaef18cd105675a0a704ea15f026b2a63b0aba1cdba8cf86e15e92e534796b63e99a2a520c8daac89cdb1cdba52d312c125c756dd115e7f3f73d768ee08c4ee6b9bb5eee190d59f92106b6973b3e6b3c3ce3eeeab11dfca6ed6724ff77aa779f42b64450e629f6279492e3de70a5ad19ac53e01eb80079ee3dfa643e7788d2f9abbf6dd7317c0389c4febe87b6c62409e31547f0c46cdfde7974e4f60e3d5d3a6d9998d6ecf91b284bcf94ef0cd1f2d3c516c5a59b51cf9877d611064266c135d4e31065f6fc02d0bd5719672640e44ce927a0e17b614414acc952072b9a88203316f0742f02d0da433590b281fd24b23e6f3463811ffa2c579207aff5b8f28f569c70bd24e2ab33e9d8f60e790597d4af7e68d5e8a5e0a5b49e8a9a970dfaecbf12160d50826a8026c021d041d322f2a4487540657c66ec6561fc45e604c8da6dbd410d4662a2af184d2dd56cdb31033a669301d9d5b5bfc4c03143bf3889ccae6df9ede544f110192efbc415fbbfc86cf9766a63e1f3a3b3bd4e36edc64dfad7eeaac44fce8d4f6278c8fa983aef9111865eaf7c7187b9b06f52db3e606ad7f9549276f9e1844aad68a8029932bd7f8b8ca057de29f28df8a99e6b27933d775580f23b6cdfba57366b2103069c56ed6c1daf301ce1eb7d962ff0f696e75acc61cf94b0ae6a77233c9d69b1a2b698284a5d384afc0a99b2bdfc0b84fe349e6824dd0c9c4da058839a589499bc97282ab409df0eef02d2e9531c1e74b6bb6a3a1140aace0eaf188b5ef690e8a6fc1c6aa84f25ee811121b82429ba02c61ceb68e3ffcf861d465ce248c69203d4d82eaff80d03f0346f4cd3f9446faa20c4f33831ff16ac72a93c641a419c64decb30810b0dc3f3080d2f90154b9b5bff663b4bb4c6bfa888f1abb6afe932f3496c5bb8e36f51e6ceace773b0329318cfbab3827fe86392616593d556b7f2bc4574d3c33e2a7adc1993a1aa33b379b16cc273292a21ae55acfa3835484edc159adb1ae247d3dba5b74b7246f88680854e202aaf755655c30165aa5035ad50d5ac0a4af1c465dfea8daffc38591dc620d6cf8e76aecd8cfc058717789abdfdc5a9554800cd7b6d79acf7bdcaf007026fda845ecf313b068c73698a1f2bdcc1628ef05609aa17ea39584ecbdf796524a295392013606250676063b6af5c94016b82d5bcb63ad0bd6eab0b6c7da1dd6fa58fbd3c24e3679b8c9b950f3db6899a3ebc99a84ae0750eeefce4be9e038182ae5aa0b1cf6d70f6353f62f434ed7636f8e1dfeef60d8a270e243de7c7e5ee001438f0320a0eba17ddfc3e1ede7051e9283a107e50e8080ae4702b2cfe40e0e9437468eb0df713539da649c7ce312c5d1c8740270bf2a02c7e5c6aa2254df794584a87e5379a064757fca6ba5b8a442de521e902053e010a7250d1538b49937b2f540d9f9805d0ab97a40627096dbd84adf33fdc206488f9537cfc77fb8e937407aab3140099ff7248c00009b01d4d4a43297400db6b8a96cd7a37fc7393a2f9ea103ae6c2977b52d5f4ce5cbfb55fa653711c614ec9156b17537f1bcba99c3e6d25cbacdc9c1695d463c7735972df0cb6f5746a9fba6edc8cc9b7f3dfbd117d772084559f63522e3f537ca51a15dae7b2918c1edcaa10f659ebf5cc8886b81c3ee12769798236669c459f2a550bbac607f5924841b24652fc7256e00cb9baadfc6410934818043592d0cf3b7f799bf7dd141a0dc9b17f6f710cff75ba171d8df91967eed5658261b6589036d64b7d36b6bb1df4cae7b9f5eb79046e96fd3d299c426937da10c45a83e63e223fbb00d7088e77c0993659efc8c49cefe2af19be9b72833a2733a6759c644f5f331ccca6ff04f4f87b326f73b9235e7fc7bed776f3ff5f581729665d95592a953a6cd690e4b796edc73df8502873753ff15f794fb68f7b791e7b282c25632c77d0cdc73dc03994b70dc5caa4e1fc3709de6db955fb85a751ff785e0fd5ae5772f3fc579e1ad216b7fbdf046bedc77de7490d3761c286bde1099b36f2ed138ea5f6f475afa59a0866ef1fa38e3c5bcad6810c1ce340fcaa1733a77fbabde64efb973773a1c6e2e70ff0c71b2bc1c8cd354dae65ca1733ad3190b5799732a734f7ff63efdd90794332f9c977a40790ef16cbf151a87fc1d596d9c0b4a238d300ec6b97d8cb46e91c619638c51feb66d5bf470b49777d32ad5b22fae629441e78b40b9729101124600808d1a32312b1a3364c0c480f18255a98edbb4ecda4aa7f45e2202b72db2b2b2b29272b22aa2bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb7b85a4bbbba3d8a2cf39639c33cebb74efbd59467d3ad76fc641cf3732fdfec221326a73c96a772edd4be712b4563a95e61274c9c1a65fb3c9d530a59558638d754a2bb34a2d6a9555a416555a91562a9d5f7d4eadb51facac601928910e910e914e121d984e121d221d22239452a79261b57eb413e660b495ce1e289d43f3357ba074c226cc2910b02d52905479014a08ca9523942d4f5baae408654bd39627b624dd0e70b199456c0bd2e450d01b0b6825a233aa148854125b6177cb6b0b087086b4c5f564ab6869c2820a26b4c4b4c040cb0bb434c15a946881e5e90313ac25e8254b148c293184402507597c806fd0a54123cb144c650559a25011ca9294e5284b5116a26a65c6b2e8b0eab68c804998598266f03f453d3df59395f954057b73067faba7a6a7d813d2bc22c8075e3c1df1a4e409f61484250acb1316272c566059ba589a6031eaeb8214604e6019c2585e36b0b8ae68b962a5872956ca5cd290564645e095d7952015666cc1d11f95fb866dc5032e9a3cfbf37896d91ff4580a82b23fcc07b213414d8172ca2107d5942314a8584eca110a1413285027c85a8e50a08872dfc814d84023e40d998e17de30e5943df017657380fba3b4f4d26b69a6d55ab36ddbb65a336da3d756207091f33807a56c5a4691c03d54af2c5d295644150d11f59010d71d52830d317b7b33aea3cf554a29b76d1ba5cf755b66af8f21cef257a5accccdab81a32fcc5d4f08161cf6d0100c6608e3fb3e2efd1722574fd97663c8f47eccbe1a1cf421426c3cad54ad0979ec8ee2a07f2ff56ba9876044d1d2ad7bfbbb7da1e7ede36398ed7ed4386f86ce5a0fbff9186b72ea43b08b35d226d37ebc456b7a08047358f3859fc31a99bf8f0bee36ac5f5e60981638eca55ef256db58752b81a0939393d3cb56f14a75a6848ba02f86bc80eabf760a79adbc564a79efbdf727cebdf77ea17dfb5d25955352a27689a383e3dff77e3a9ce5bf34add31860ce6722484120597e04298d61e619e913d9ac7d70e646761c07fda3df449de8fe336e63044309430466b29c9e00ba812c9f7a4dcdce0e094e78225959f2e2c88a0c29529c46d802c6e5888bd0e7057fef74ff98a5acb2d69fee3b4ea9941fd7f1c3a9418931c8c148698c34d2ee26b99f1ee5fec221d25ff54573a0542828074afdf54a41094aa0c4d631c621293189bbfcf324940bafe2593815cfc2ab78150f224eccc5e8d1a3475963a3e3d97f9331d22c94fd0b65885ce477c3c1f620aa112967a553ca1f78c07d23f21063bcfe4d07dd05b19bfa0a357105b781e21584a61811e0e9e08e2dba923fd597afd68709b96c6ff6aea274d3b8d760bc461f675ff8956e4f3b1ffaa9dfdc57366e84e171f677e48b17f30dbb49e84bb9934257ca5b4d6d489d73e44e3a27e6cdfd29a7aa0870e74ad79dfcf61be5b8bf5f2803e74e6678323ceb4e1a0775ef9fdeeea48fdc89d26b5030cda77c6951bed686f66354d1522a18f345fb2aed7c66957965eba99bf7d3e3a38399bcf75eae4ba9f00b8cae078c97a982cafd37e585f32953dbd1f9d4b42877f585b92f04baf34a4dde5879f3f22663939165166f644496fcede3bc31478693af6c09d8f2f6cd2b8d83ae6eddb82e2539d5e4f00b8cae478c2f5ee936b29c57669579a5ce278e698b334188094971244551aec97189289e721204705c220aa7fc392ec14425bb2655cf7961e790fb97fa1dd439f6eb10bff7e7f729fb859dddcea7d6bf3d75f5e2d9c0f537dbf55459b39cf2c29909658a83e8cba4bcf5788c0e58fb42a01be30b1997b3b761d32e015bcebe46927938b9dc481a47ccf4b3ae87fa5b22f1630c9f98e3dd7c464a07e5e570ad02cb7881e7ac745e6165bec03dd064b8e0f9f2278daf7dda7bf7c28d59d3b2ec7e1537163573c659f3a30ba60838bc795e0efb0d70b42fd841ae736ee42eee2bf6ae6cd713636e19654cc1087ebfd7d64f52f1d4060cbef31d802394a426775e6e7edbcf3efa82fd7b3ff6b59ffcd956ecb6ba5c2e827b99ac01403c9cfdb69583dbc338b8d5a71fc2e4ed756c2d9f78c35bd96fdbd7784bfbed6dbce5f33d369dbc6ddbdfedb96d66fb2c10997c8b1d76e0c1872243433dc0b4df5659f892c3981c252bcee9bff5bba7faac9b1cd4bc49cce1af7d23792bfb9a4a11fc2f73e28f1963fcd3a3f3f1fc5ae9e37edc30eda5f6421cd4b4df714de3fe65071cd6646d9b53fbad230f98fbcd2badb4d24a3d4bfaf167f6af9d228df62f3ee0ecfdc31aa7bbbdf4686895174eeddba3a17353d6de7ae190ce6b260e6acf7961a492b5e7a8e0b09d74985e1fb59183dac7f800d6be9d348aa36ca7ac7d8db7344d7b1b4dc92b6b9aa665ed5b48fb09f8cde4d1e20cb216a1642d4691b52ab2f65183ca1a1728214dd3b4ee0befd566b4cf02feea74303404ebe1fe16e331ec222f0f43caefe5230ccf06209e9797efe5a5d7df8b37e7bf783586200ededfd18103c3096bffb203be5913eee517e22bd8fe86df7a29fc7556bdf5ba8faa6b8da8be6ed23ee05fad42a7d97efdc203e65ef5f6b1cd9e7a3474ca3372bf7a3474eebcb08df27dce0bb3cdb3c1c1fb1a478507f9fe7de9b5cbc1fb9917e308bedf47f7defb5388b7eadf08e594efbdf76dfc66f2dcf701c45bfe37265f2f5f56be1f439d7c97f2dd42e5be387141d8e21637d5b5dd376fdbea716c18066f1ee65248a9f00b8c183032522cfc0223068c8c1934300823068c8c1934563135f972383a516905c210617e1c44e1c2689a0c4d9ba16934346da569319a26a3693526101f0b583de8e365013035336ec349d8521e10793382112e95b2a919dc4effa7406fd588a03f107913e52acbdd8ad16915636c0d1b00188184f7bea689b1e38d00a84878ef2b61c6841354a9f7be12664c38a1eb8142aaf3be12664c380105ae763c58dd8abb1f2e61c6841350005929d44fe7572b1c7ac227ef8c1164a510801b342ae0d871b01f5bef66b04113729c29e1068d0a386a04b0c2833352fe4cf61bd8f564d6da40a1ebb152aeecc5297012df96f5689a5301478d0028b742e50660399bae0750be915b6e28c82aa7c0e105008d0a383899950d015a373805c891d3f5805981c60c95ca86ab99187304e8529d4fcdf5bb05c891a36300165ad8f907738d0dd7a572e4e81880851676b2dc705e06000393a36300165ad8c93c0558e101c890a16300165ad8c93c2ee4e855930ae770e05f0ebc91e7ccca6d260d1a1c0b2dec641e1774f4f45310671b9f57dec24ee6714147cf8eaec7cac15f11c1e6e06acdb5f2d4ea42ad3a6aeda93531d70f520179436b24ab3ef57024abbead61ed84f94dccabfe2c6282e42d9aebcf256fc5274251e18ce24a4439e4e851088e72b8aa4f42f0aaf5672cd7c994ebdb76a55a25925c83c821978372d85272adf5697857b27c3edd5cd82970822dce6c3149b85fdbbe225227e4d865996a2686096cbfbefc50f5f76be44deaefedfede7b19305ff5b16380ab3c234770db77deccb4abcd948f1d0733957cef6fd98720e837d993cbe522c273bf46c75b31df7cffc7c72936f932416df9462db33193791e9f84a017895e27d435c208941e459afb02cac4647dd0ea83561b0476ddbcb35a4e2510a7c6efca25cb5afc12438a686316b8061becbbfcfe7790d5f2c88b28fd317aa1063838384a70d846b8febd18af5646f407e91287e21835134e1ba586805db66c295527982a11c41c92625155441cf6e492ac36e24c41aa10390d09654be91b7925188131c55be0b08d421c8af32f3bc80d9cecba4532314a69adb5f6c82821a304be55b26c0993308f792c2aaa8dda48ca2841bda04260336c49e214248817386c1893924dbc11c7d594ae53d629535f28496962c147e6c442e7d03a651019659451468ff4c96d9db64e23d8a313ed25cf766030793d67628a526030180c06c284c061c372146f5179d365300db6c138d885759feca2e44e741152127620d3973904c23f6855119992e2ee430137c8fea1a6b4b3730587f71e799efbee3537d583b9236fb0adb70a0ee353ee1c7963ab92f4bc71ecd4ec4425f04e400ae1303e09e172cc25a3c81667c2de0109d92edb65bbadd4565a042c7fbaf5ead4a7ac74563a8980a5bbfbe4e24e7dba743a259d320abb539f2ebd167947c96ca5b3d2a93a02e7de28c447fd91ac14979863755b49ca6692dd94d94a6da55dc427d927e6b861a3a5aac011808dceb624e9bd88c3dd0ba45f9d233f07c41c8ce0e7febc70d8af259b2852963ad5109d232b9d2929553cd02955510ce91c99e1d4a4806da5b6522d3f6da5b3e6a5d259e9b402fbfbc79f582c16fbf97921fdc89b864d29fb255953e030c6ba2d6ae14b67f573315eafd77dbd24f8ba5f74c21e6315f020b7b93a3118a716807039f3d7561cf4dfee132c3fc6228d1638ecd7cbbfa3aceda248e30f448826c28a7ed97e7181c3d8d4312052058218933f13c7411d1c1aca1fa70687823764a073860ee496ad1a0158a55cad1cf4d496a758ec865cd954973b654fd93de029bf106bdf6f3dec986fdfebcb5239c061c7c27e65fb524a296bba630c8cd1693f20170a0e6fac632edb6d3677514f01ca742121e58911548c8b299804d19d40cb0f497881e58814120541b2891e38ecc0c411d712633164718287a418f06004125fd3951ce450650543d812d45de4e0209974122190a83cc1854814228824b19404145d24418310b80089105a1cc112693044144060b1031055b6a822b422821f3c7043135eecc061b0ea7205890dba48a2031530ac60892cb080558195e0c824c10e8e4ce9224b6e88d2c4143b5c9164c8135b3081b2820840182208490898ca91a82c4ea220b1c4674a174d42c4bc783909922fa8a248d441cc620827945080450aa11994a024892e7430428b2b46ddd442907321948194589185090e401082272bc0f1031f7cf18414441011c20d540039090289278cc0c189245370b0a1e3a7c791a8d1da766633055fc8c0265322f260ace1ce4999072b08820a23a81d68d0e50b27393a71840a9ab04192144de015fea249a64852583a25849843b23c75855cc5d4b8f7ba70189d98700f3ece17e2d8cfe7cc81aa82644d1264512014b45dae5fde544082f2a683e4e3184475030ebf47f7de654834950f584a2965cdc67991a7bd705593e38702c8311c404da4f10fd239f21340c4e1ef9f0ffce69da7bdeb93caaf031c46a7201cc62e3dcd4bf0c0d62f70c2044d8d1f36d00f07154bb878ea03f1d807fa41fffefd6ca00fc4133f4bc29ff200fe67a900a90926576c4941149e7e20fd3b3f3f842458b878a8177994763a67e6992319a8e4a62e26a010421731c1044433018595d6ba99300153b7bb8fe71d0793c0e16faa9744a2644a2080e8926790d2df6f5e5c3f3fa27277751d119f1c5122c7217a4a5fe1f477ee27723f092acafde44811f7430e6d7247c9a18e677f1fcf5e65972e59cee81cf9713ee0cffffd7ddb7bfad6054f3ae79c73ce39e79c73ce39e79c73ce39e79c73ae82204a42a59c934a2aa59c53ce29e794734a29e54c72c3d63f30fdeaf23dcbdc7dfaf4e97efdfa74bf7e7dba5fbf3eddaf5f9f73fafcee9def35e44c4a6f7372507e96dd2cbb52be0c4cd665445a654b9f34da68a38d36da58239ddd8f76ddce5aeb8e95da660b220abadb8acdc69ae5b86dae5c7f8b91c9844d462907a1794427132de2049ad588c49c266a1c95a80851e7b4527db9dd23aa44e40acbe4011d8830c5a84f15aa72a91ca8c3c15a942bcc4123264647b91239586b51ae443526094fa35c796062a4a4c9c11a66514c411aacc8c1aa19f1967f31fbbf28aca7a98d69ae69198b49cb9cc95bd4041ec5fa8ca22175e5fa3d7da957e307fdd5af3e1b70b703f6afb123cbb8bea234cab2a82cbb9f45f90d0a2ecda50969304d890e411a932c4a7be96094438d2857ad4833d28e72fdecdadbe4ad701a39792bacaf5cdd8d5092432e5f2a2185d130548301333840677ce4a15e0d06d0e080128d8f3c9910a64792551fc9a514946bfd507be5fa3220c0e1846d3a7e36e9a29f7449a01a3f56abcfa324ab4a56a59e84d558b75c72b0324929b2c95b313739f8e2ee6572c9a378cb934b32e66094226ff9d7bc6d30130623a3dfc05491e1de7c3958dfa35c1e255d33e893403cd5ca2a0ed68fb2519443523b11d3256f62befeac296f59aee8c6722fa308db24ab94f2b497f1224f8c1779ac974515c9fab87e9125a2a722525edcf6935beb4b4c3e30e9e6a96fdb9fbc1525d79fd9ad59d40e30674d6006da782c6977d9f15882e42da4d6523f8f25491e8f254b5b70528e9d0f8f254a1fe7d19166278304d3a29a452d45240fdb08971cbb17ac3f8f25473c961c29cddc97c792240763e6b164c9c198ddcb5ee6080e2f9357e347ccd39807e291f16afc90f929f3403cb1fbb1fad5f79255e96703fdd5b792acfa32bac0e165c2008e4f6e20cab3681a49968f0993acfa991156fdbd4e92557f93dd2fa95003396651de02ca3c9628f94d7424bf993067d58f99c7922699c792236f01e598b778011c9fdc209463d7c363c9518c2f3c963099301e4b9a743058ff782c397230e6ec29e6e85ceddbd8658a39da2e756c474dee2f26b5f5c06c95bc751d69c95bd15f6ebd51e9e5c3500f50b9cec9535fcb6e16e5ad1a3bb2dc4c0ed68ff131d7cfb8788b0ee1f2160c23419a90b7b497b786bce541f53b8f8696e1d130338c17ba925c5fe5d130ebcb2c8a86593f861762185ee851b9fe56ffc50ba551ae2f8de44de4914c523a49d8e5a991a7767f918c922bfe421994ab1258abac2ed9a42499a06245ab5056a8e4ea536410ac71d40fe50c642a3553299a4ad554caa65237954aa56c2a55bfd6243efc870724d2546dca2b2ff6afc60c9d85449a9825d7df48d001d78f393291e3130f44c93487add40aed2bcc9e2ed3d1d0844da227448ef29c4c66935cbb1d32790616728c34b66807b7b5cbd9ded6bb6edddaa6b9c1c9b6f70334d7ff58f9c09285232b71b0ce2a1608192637f970f9461afb753e30799b1ea54c75dd2e270d6f9e0eda1b6d96ea2294289e72e6dd486333d74df36416664251908bc887e6ecad97fdc89bd567ef43dec47ccca7b93e1f3b92152377da6f9917cea29c7dcfa248934d0993b04893f9cb5f9126cbb2eeeb2099af83dac7dfda372a69b64fb355628b1cd4aca665d7e903505a6661b688e889d092ec76d0b730cd165925de72a297b76afd0cca5bfe59c0bedfa6216fddcf0a3968996cccbbef629232a4a49824bc69d9c7a6d8592afb881cac7f0212bedf5acf092ec0a9565af2567fd5b2efee071273c85a9f4b823f7759e070c26e4c126e9883f5b5286c935cb72e06ccb6c384dc9ff5fad92007ebdb24dbc4226d73fbcdbb9146d33eeb72d05a2b43488975592342d615e42d6b9984baee075c634796336ee2608d97a9bd2bc5419613922af7dbb3f28603355e8603ab8ff91a9fe692acfa32df95acd5c77c9e5d79599464755d478b5cb0ce3689b231f7b25f79f46d93cb74a564519acbc6ea93b7b4af5fa1a2bc5545de3c552edeca549d4ae5b9124049c95112f701a45c7fc6e2930f28e5fa93c96f224f7d55aaebbaaedb687775b752c6d9d3e79c12a742a02d6eced1186b773e9dfd79bcf319972d23970a379063f77037d268cdcad97b39fb3026671fe34dcc872f39cb3ec6eb1859e14b55ca9a32a76ec78dbe358a83d5abb1bae4a08d231c9a48265713f4c9e5831cce231752376755aa4bb97ecd9a72fd5aa3e42044510e7b4aae358a53ad5f635993bc99f1f533277943e3eb5bef67c7c1fa42628e9be8a8942d78453164686800000000e3150000201008064462b15810a5719ae93e14000e6e864c6e4c3415c8824992a2280cc2181862861000080086808111182a2b0069b489b2b05f35bcd653cf9b13d7d75e07d2645250931e000e577cedcbc50ba76f9335faffaa80e10319fbb9d42d17f8682027cb9d02aec49c0bccade7ccdb0fde0c02e299f6f55fcaea0f66efd1b536a46ef12590a1f00ccf4786ee44bca43d096dfa081c03a8af4afc3074cb0691077779a0e115e0b481f7ddc99d03e2f72279073a8c9528a6c0f0d202f28de6a640b27640b7fa5880addd2da834e8927308863eab4356c136c37c09257ad374871b46e4ae5e17fe211bc02c92328747f4980ae09caf354655a98e9318b3366d43d6615ddf2d9d1f6fc31b9a928e00e80709a1bc6f90096272efe4f11d58e7eb078bb7737fe54aa26b43425cde6c52cffa18b25f49bcbe8202d2f06967447f4f801207cd3d1b10cadc4fa5d59ec69f70d0648893b65af235598e320fcd29a23e121fef84aa1bb59475c9e48cfaeb48d4cab55e63019509f62feb72d4377a7084c4706ab15d60e0a0bd1e698bb7e550cc3b085a67f68fbba9779eec83191c3fc786f284c3180aa96388df431260c3b62793934bc37819be80b870647b3b4bf724aacf04fca71e09e1e2389a13ccf0592999432b61cfdb102dbcd7790eb93b804b7332dcca20192f9ba3bf18a83ad04e2bc9ad6489827fb2fc2091e82428a5d659bae0d5962d00efa6b67f43ab7f992177ade95ac0e6ffa2c490c8098230fabe425d912c20b65292dc06f04b5e718227f6d91f8713357926a532918854551c716df9aca939dd654dc8ed699cc69cfa1cf54ca5b0b0602c0ef6bca43e64845817f9227b6d3104e7b55191aab27df0bb37c9b3fa4dfe4d24e0cf8660e4657f709dec7ee250621694f1b0e4bd08ee091cd7ed9c1ce29c582bb43f53dc2d6c3fead2bb47ece0b5aeeb21810ad1ff0acdf1c3365fbb0ef97fa5a713351c684a1b5044fddd90e5bd13a5a8bbf7fe0edc2dceaf14f1787a60cd8e51c93f0ef3cbe81e01888e8d5cc1b7118ddd5d5af28eb322b88d5286e6da4a69f8eb525f4149463ada06dc1c647a76794c2728b7a597061492c310e7112e63560b5df76d7693d54e3ed79f1df55b8a8554e440bc6d87d3b6443593d10e872dbeb9bd990c99e398abc1b3d7cc194605317ac907cef383900a47fb55f45f7071028bf9cd9945f6373bc289f2d2fad1ffde4c5ebd05b40434d34d54984dbb040b50d183c3f62460b7caa200dc701e2022f5b19c14403cded0de19b2d8cbf70fe8ab26bace05669168b6a69328eb9df4d2ad0cc181e4e3ea2fe857052971f61a1789bfa3aea007353d5a11b6eae8e50a0617fae3fe736c9593b757984ec92a7a281cdb91d7737fa6e9c4cffbec5385d33555e8868018f509f63db48a91eb4b7f88b4b18f38c1b993aa75f4b3be8364d5c456f7beee96d64d642aeadd0f421af4279db501cf611bd9df389dfd7bfe2df3e4aef5a7be1748b7773c803474440a6ee2a3a5286cff500ecd8d508a45419b103af1eaaa9ab8e85de7e2282b73b767c837a9a159d14eff81d289115d702c1265f407e8ecaf72a18d5a28d3602aa4ecff8a60dc8802d4e70db552d30639303144ce8f70fe9c66af96d3a6866ea3870b7451b9c8ef004008c91669381dfda188d5486e2e029ee74bc679bf278ea00c8ed5762d8937345f54f05b3a0463c3a2e8dcd4ce024c5452c5b57e757d68a9a28453ab10217aacb009be226ead43355e30c6226e078220e63c2fdd0a7550bd113bdbc68b82f58cfa5de5b2705e8ad22d53ba2d090c0232c77fac02fc749a966b3da3560797ec101454e9a0cf8d60f7a1ee5b6bc12780365e9fc34b0a616a6291b681e471b1744feb5b9f2769d98723bd48c36c20185b7070c2b54bed7f803d97ed53896bc449d209acf8e56a61c1065a742d6fa53c302a7abea67f98c3fcc76185ad2d6229bc07ad60238e950d0ad75cbbe31057c70c5dc372c1b2b178814bf46f43f3092759594c1c665cdbc535b343276df343f112476840012c43b4d9f18e68a87d957e2c470e67db67186400b900e48ad993b36c09ce7b2856d6af9d7455fc9872f95e00ee3d632fa58fc415c351eb3d71347d82fd6597f18b71b5a65fba32e16227344d59197f9137d1c2d5730dbfbfeb43dfb8301fa29f5271a407c3cf5efac1a03d1d452ad1ff5b42e6d8beac2cfca98fe1aa6c638b4d1e1a63be3434aa36637cc48beb2bc7913c40a629770b36d258d287b8e2765ae7589f1a6109a58c4697e163561eb8269fa54c303fb092f0e118feb2790cbcd0b2c4d6f2aa9ac2456e63af13f3e344eca65cbe7123f266fc9dc81e09aa763d5f510494478f7551a3ba30faec546f6a4c4aabedd253f724a07b6fd50feb9697fc52d5d5a948851b2813f8392e479c99e81bedd66bc3637fc83679b6cd8113dac7a49b4e7faaa154caaffaf1ee5e95c0722895d110a3674655b6d282c6190c4997346f3ff388f17b6e480f8761acbc6fbc5f7328072554e47aa30a265dd5dcdd1c0c3eaaaee53ce24faa21840ba5237bd894d75eae50a48fc5bafbd0fe879ead58dd4073b496bdfa7cf7fe429d39b98ce5e6736b41d5212557e5ebd9ebfd54acd16b750d80c4c420bd4645fecf56470af0cc070727197146528f39ec324536d0bd36a66ad49c0b63e367171e91cb1a042a9ba30c7f49fc35eb11eb29a6d3829d957bf563e992d4d6bcbf7bc1b30994707a9e3c3baf39d4b54ad86a019602a980ed2bd3d63efa536c5f4d172e0a9c8657ad2b8a22d9bb84652b2f5fa9f3c890b6d7fdda7f153f5ec94013493aedcdd4c5805fed7fa4a47edef1852d16f79f542fccd0dabd845801803fe550923a2e7d2d669cbb83b3759c73db66e343b7bac2020d3453949bcc45eb80e36c6462f125a6f6fd2a033bc25f20ac95c4122119e832379cf11ec143bdeb6c93b14ecbc9bd4b85465175440e056ef2e93c0bae10c0fa07ecebcce386a9df9478c2a722b1aae0842d7e695012d56d5dafa257ec7ffd5a3474caf52cce753da36f9dccc26278e2c88b78d9998e7e03dc71d03f175fd1887b531feca30fcf9669169a32282bc2453e286a0dd6deacc155b7c8bd300bbb637978e274b70c25cb63af7aa7306900dfdfa45e7ddebd436baf3ee9d2fa904b2417bd676f7f6855c3c7e5fd80c6fed33baf472db14f936da8406023e111771424a2d3637ed0a84b0c9c4792828c245f905eae2d7da9a231db7a2469eb55a1c76a6875982604b843f5dcc904fde352a4bbfff504638cf3b3d0848e1f2cb89f396dfc4c97ceac4e033f2944300014295b2ed09277859e688c5687a24534f322370e907888873cd095e544f160fe17a0850854047c076256e1b3f92f973c6de0c5197e543f226e2ace78ef451084f3fb55ab803462415f9a7ef806d2b9c1d5a20e46430784aba3885947338ed509828ceeaae14402b6247b1cf418ae233d6c5884e90b34077032f9c857d45954edf99cb525fbf6c8c7a61cc43c565b92d556c245ae1bee4a357416a1a75a15111a09c0700af18310fa06c1081b9a7c5a2d25aa6c987973a06248957b2cf35ead8d83d16baee66eefb6821006ae5d453e89f6e5bbcedc9656c7d9c4513b561c2fc79be34e275c5564b28c26cb90586b30a496fd9edba1b9aa3f2b4bcac851ddd54c5a6db0dff396522668c874ed941c345b10fdfd9b782394f8008a6c2673f770f3fcc59ea9f8cffd52bc1219c30079137cf7b624c3c4db7ad4ffee57fd761b763b007030584b54daa8da7eae3a7664e06b149fe16db01fac910032efbfa5a6c7421798fb8e4533329418d5e66aef639ffbefc113dc49e9ac9bc1c3e941470fcc5818cb45438607ce75f19188af23eabf46076d6549449068eb890e7b241c250190d417a2fa2385bed3fb136c9e84ca7db7b502a9ee198339ad347de0eb2298211fd8b50272fe622f70fcf0537d9a8ebbc818993c6a1830ea649461647acc659bda021ad50853a4199631f883fe683df9210c0d89e2afeac95483e68edefcdb8cae6b37661d012f30529cc69d44cba152dc093e7f1ddfcc4188b10630045697469dabcb9ec3a1ab6c1b9bcb363a63ce0c7435a011b7d14dde64491dd713d7fc55ff02b0f1e70da77ed1748e5af8516a8255fcdc531c0736056fa656d25fec05972a5f204460d2f432b90bf899e0de5285a6f7b73f75e0bb06106995b63323764673783207919c0debc2370bf76a8541580d4377373f75f9a77b44352b8fb301c72dba7ac5d617c69d852d4d4b6dbe8c903f2dfcb145a17d0282d002462fbb4a1d89132af60dd9c6c7af17a7a8ba3e39a48a890abf6953026dcc0eaebc545dcfb4f4f3f8d9444db769d22f4cb10f6847325c0e3a355e5dc008e7555160fa5674da1c6ed760eb738083a4f649c7883f50b7902e9b8e3fa5556a76b5193c441edd2e22b174ef44dca7aaa435227840e5218ed99160bc074456e79ff26469d0b340e60780c3858aa863426511b6ffb3cedadac7b2721aca0c9e3622ed9ece8f54cc752921ed539663e220b16a81cf6a758d19c4f807e2a4817d3dc06d7346c80515d659f0098ffb80481a0977a1d907c5c1ce03892f388bfb48668735a3429e07c7ad015d11cd3cf8b4917d0202ab262743775f7edc58eb6fcc896cd30c987322ab14ca9a01fd20ca7938d4542122f8c2225ac90581c038c2205f465622404dbaf03e3597e944663b0566d8f04510be903450e0410898e1a6d0c60c39a8ddfce730ce689fd023a9135ce801fdc96fdd3394ded29a87b834c724afa55a41e2b965695b3a52f453c7d0db88687864b0fc7abaf688e37fefdeab4dc1d3983462abf6d5e3155865108d590c9eda533077aba252905619d4e9c7324a4d6ff2e7fc17dde39c71ed1f04531d7349fbefdc1ca7d994ba78f92432a250c2963732f94705f2140dc53c81bcf52e2aaa0a571c0845c647bbdf8943080a794c06dfc27d6068c82fd090c6836486e6a3cb2661371ffbc1694a8e310dd753866b9e68c646665f0c9d402ac4376dd4810cfc29dba08142ba769660b477926c1c536d8fddbf2f25dd0c80cabb0f27189b09e5fe230b7d4d08f8787aa9ebc0989e1144b15086b07c71bc49111e72a046cee89f974dc3420063385a684aa0c008446f678cbece3170b4bb6b1d70953aa90297daf3a0a172cf5c5da915fa2608dfe64cbb3cca9db2575b27d37052a45f7de6ed739e928a744a98d450d6b207fb900e4ac0fc3f725bc6d50d7298bed17ba7601c247f9e6492f13210a795ecf21dac822feef80f4bf07c7e0a6099e7248691090a0c3dd034939338f26baecd27ffe3a9205d211d40df41b99c8ccd60d0efd80addc8055e22253b6151f8f942d4d803dc16c905ab2fe45d6184a0d6616dc5d67ef66d4b0a725046c57baf55b81526c9d18ad7744057a9e01fa93d8cc4ecca650cfa853ec383e801ce07cdd0b07cdebec81f7ac838c30838d9223f03c6170e4dc2e6e754ee09ec37ad1d1cb9ead205b1d41f496aed425d2d136e74e4f0463ef90387b68f9e291f6cc31672e4d38c1b5949e47184ae252d3fd36c97ca35422cb16b8d092a25bc11952d6db73066706be9b78835d668f1ff2d8be29d892db5a95e7c3cc356f1109d6814369214c30874609ed148e3236a9fdc1a6a2a2b2525962de9a5d82f32404125158adf58d852e3ea3e74a3da276e28130b3e208ed1aec70b3379877f04528c3891d6f6eb28e86bfc19574172d7552e6fb019c3c0ef91e06f75056c03bedb037adc232e9b24e83a083c13f74c483f8b6d7256d01e88cdafba930cabf7d25b5df996a045b70282ed921821f96d61df94580ecb30483e27853d4797b7e343f4e33832b782b023adac382a3d1eef61959a5fc393cd04c5f7a303f37a1644850df503ba1953f4ef4927067777eeaad8f19d617e12805685f52709b07874525d022ba9bd3137ac9b2d1823870992737fa7275f36af62f3a043618d6cccba4831cc1391c95fac53c43fdeacdf708a2b28a3c4d6c225137dd2d78829d5b1c77f4fcb619eb1361b99a0278fbd00eac322cf1b2293784ffc7a50bd536b4e024399e8561f806c1475b975d7335b9533d0f18b22b7ed2c69a1ca0bac6e59cead19535a4cf0385545e82f7fd034727a7d6da70e0380278495165dffea2e0716dd95912d1b2bba4408c13e2ad2f552b769c99fb91fa075138a7648bebf586e8c10ade795619e546ccd5a48035b9bdba345fdf162fa7e0a1611439f8d60f8b8c55ae5d87b135c7e046d3b80e789611df554cdf21f2c3912450525c7d31d5dfdff0f4451a5a9ea95440c9ba6f653e84d1a7b3a8617a12c0334eda026332fbe4d9c9dd864d55e5f28e98a566e49812bbbcb124703c87d0bbf117b7966f230972d8f947c6fead6d836a5229d375ff1fff59db95b4562f2929c10bd63092889ac53d24d03a6c4ff7e740f04b555f4d32a89fa0dd81421aec1574987fcf53dd4f180cee78bace985865868525c106e47a43208a43f58dcccfd0fa80736b3e325a42edf9bd817587e4a98ac008ccc5306328fdee421a379803fa75e5223db6ab159d77022c628ebf53213767c19fb9bf4b251f36e3c98552d1d5c1f9de6f915e3e51a475b58ebeb7b85de7bf1085ed34b20514e4e6cfd0b52ea55c34611554ce09c684b2017646ebf5987d710aabd7a401506838dc51b02d3e887611af908d31cb3bba7bba4d3f79dd6b9a84d3b88c603d81698eff3c1f45b2594159d09aab4cd50d8557e1a831a55f466c94056426a5fdfe4c88e22b49a957600be5f0909c017a11388c79968c45682bc15f6d985355fc44443067bfe380504271fc24f407e961a420ca4305796f21f5facc68072cb170282bddd0d648461259bd33ec60eff070204f447a488ca34271d46751f8625d3a4e5dd8d0ced800e86582341168b9a571ab6440e9ae1eaa9ccf9e1c75d462e7000587485e9171fc5e0d58ddb582273fc02c11f8952dcbf6fa209e23f1101f6fa6b24f5a9065d34d54a78fec09d54c0f44340188017625348508c59e809a73e92ff516ebd77731254d9af9a9dfd6ee5ad2fd0e9fa01d04b06d8827e6080dbf0d0844967d480ec9d1c920a4cdeb5b28f8a9d6d9ca5d5989317ca2f66aef8bb3100b8df57fcf0d104b768f1efa484f5b92c7ef9f2910e256b1d7a14f667f416533809f7c1dc84a9aae53559b4990d486cf80ceb890376115dada0ef929ade1b64dfeaea919059c01ffb5416f2d3fb46dcfd6fa795f9699f0610e44fb31a994544bba23b9e815ec1ed454d4ee1f1e5069e77a2664d8a8d9de28452289d3af7476c43909b461ff2ec63c17791dbb1e926fda0a5221c8623be1c09ec07ced808cc5b4d23c1eee2f04c777464814d742ba2cd022b00ddeca24a6e454a1e578ffb58a56cf0dad3a783686e7db24f1baffd0307eff538abd0588ab4007517cb411dee4244c67fb9acf48152c51f36b1f8b82ad8c4d6875b4030f2234e0852aaf0369af967fe3dca05526f5a94e6b06269fecbbc2e3dff3f112700ff84403c067f62e162968474f25e72e866e0fd6511694a0242be4b8a9b95a9ef8e10423c3ae438e681ca153890f98e1018010955c69894083faff18ad4f21fd3693e99da150df13ca53d33be3e489190e91ec5a458bc5a41c1a6ffc7b2537bd02ede4b0a769f1ad9490f6e08a80d670de4fd0ed43bb1d0fe88644fd7e986d96188b38b88f4d6d07ccbd3d5ae539e67aa77874ecf6f7f04743ddc36019bb01900501dfa5329d42cb84fe209e228b78fdb3dcaeb9f947f547b0614f7137e3d901164a6e64a5bd6ac84fcb18a40b2b669b276e961b943f156619fb2ec970295fe189f5ff3659ad111a7c12291b39ff5c0b6609faa8473f57bf6ad7c1d239184c7ce258d4abdb807269465f438ff69cab2d2e16409e6d4d9065895e7c7a3b043e2ffeb981777a9afd8e1ca5251f46f7b7ba58ae8f7fe29a28b18b188e484b2bc194d1ac438a1c5f9e72be7b580d92a256631f99886a422aa8d5048db469ea64bd52fa58d68b6f7f8f4c4d9afe3999e27c0e811e0c2a929f2f8135e18e3fa9202b263493a4c25c58292ea66ff560ab2dbf224356abc25a9beba1cf15609069162966afc24ad14836590a89220868f23300857ad7f4c060ac8a5447ed75986a156ae88f1045ec0310df316f3e8396bf6629c819428c1da506dedc584bfb94b3fc2aa1648ae56eadc8dc7a8e0e3d11329c4a69e49adcc458726c023e5141fe8a236771a3df3ac3038fda64abbb90216982a1f4a1ca081b5506ac36b84b75fc5a41fe0280deb3cbb4d8bf3944de04fae3553d92ac910e69ee4e3fa19b11f04cf3344d24fff1794c1ebb3e8d14fc22dbb20f4ae239212e35198a3059d3687c9c73de8fc8a978b456bd8f4019fc12474f6f2117218a782a4b9b2417027616ced63b6e6f0e9d9f4830db8699f01f54205923a6fa9472a54882cbb1cd84e5b68a9f4b004d03d7e05dc0050376f86e0166d74de3d0d919aa20bf1102385b4c8bf7acd6106383f0b006f8b5caa1cf7ca4f74369dd807e1de114239b3fd2f06dd137ed1861e8cf2dde3d247ccf968b45b859ae41ca6845007d7642dde3da7a4ee01fee8c4e939452f744d6a4ca1e0032e37efe9f61ac0523a094287f319cc8ad9181c0c5d90ee51550f0f610a2a051e0717593fda0df112c7867d56efc7e0e104c06c4c05eed7f87a3999e27922d0af9c49931f12db13bf328d4015cdafb8b2c830ed600d4739284da0f3071ec0444d8ffd6a159dbc239539d019c5d01108d9b68a517abdc247d3f370ef2043c7b81bdf9377cf36d0c6dd70550f99fa49b48807d67cb46c0725ec5620fc9cf3c1dd5ba35d2222bdf1e0165d219d7175a5bccd8c72c1a32cf64d2c1ce8e8ac2873cca65fa673671f2a3692393b9fcc7f52bce821382aa7757812192ed2a4da95cf5fc6c2ade860ef6d91c71b0d8f65e1457f90cb73338250b41321f37e76b2cddf5258743b88cb6ae4ba63f3b7e93b29aef4111c11571e5501ce482e559effadc5a2e7200e0b8b72f786d12e1189de81715315e688106fe017ec8ad7bff561e810d244bf4b9d4ea03aba66039da8f25889a3f7e09f71a3dd95bbffa478d347c04855223a83d8e34fe6c357203cbd9e98edd2c9c24c06e85b3cb6078ed5518fc689fe8d038c368ed38f220147f8b53d4aeb2773e1581902e9ee4027a773a48751dd457b11d25089f5bfedd7c68b825e8aa3d1d3880f5c09c507c80829d791f0a66e0664afcda5f3778dcfa565e01fbd2567da5e3ca1402d9a2c2f31e16a78c5139b87e239dfa093465073d6d5e8c88a82a17160c7437b45b3c960f148a2acf11abfa5bf1601a3973b5c2928048d76b1ada1997d5f0359546e81673955e48115f72bd8e2761cc66aa7cc1ec09df8954df16e88a745e195d654a552e821b473ee09c453cb426c0f7e63684ff8f7b4336d92ce0929d351f4722820298eabdaeab7a957080eb9c1af6148efa5698bdc3287d39f4da80100cc54f7171b0a2db9cb6509aa22f65fe705a98eca45d32084479269ad653d696fb54e4165d4b85bf358a7834a835f0795dbe8567acdaaae4bce2c82962ec2f736a892a9cd01c19e9a528cc169b349dff236c2c4141add351125fb8e8af9d0bb1d38d3162129c6194e60e68142dac14ee1c0adf43fac8658f35ce3f83b3208ef8018e59130491c564aa27f89eec90edaea60bd1d40ef94565e4a6257c4e95542ac464de63bdc7c9e028384fd18d23c2870a1952acab767e20edb3a568ad12ff388b8a72a1a0ff10440b99c49076a497b0579853e6fcdcc12bff8ea11275501672097030b88a7003816bcad73a4ddc530d10a1ca2a24dfc22e384bcc82b695ec420d64e5c196b27e512253cc0a1a3b2c39f9da4f0d78f1747f3ad7a490bffa605983a297e5bbf57804405976e186cc0ec458fb73ae990b68bc2d430cae704b14f35df1f08f6d5602bdcdf89486774089589ab83d5028d699582b309d4e35f91c3ab7d9f2617e2af3c62ad765faa58299a2badf97d25ee3df91be278fdd6e18486beb7ddd28fd96d317cdc9bd7a223844de9c7386c8042018d264804187d41d3286fa5393b3547a0f217d2ecc37fd8da02bc4f1fc0c2ea312de33b4489e647b1d3aa70a7e2aec313514e7a0f9582b04c6dc91bfc3d0e227de83c3491250e992303352064a167dbd2536078ecf5f82fa0b25a21bbff464202f2cfcd5b2e62f52241c0896506829c55403506ddd2fc1803490f1e9c678c0f67e2d8dad7e20b209552a55bb72d766439b1a81015167a471fc439681c18ac226cf69daa871aeaa401718bded1fb8f05903f99aa706420a1e57d1017c6251840cdfa8417f71f6a1c8363b3aead9cb7f9ff671bc16f52edb7ca9c5cede0ed61b2bbd89b38ac2c8425bc50d16b54b97c0dbd4686b4b241fb95b9c95ee352281d7228d14afb355b20ebf5b4a7e8350d6098419223ea4bcd26c926da11847516957cee6c01b30f251032c7ad1c612e0700b95a0e4215ed63d375eeca3658b2f291a9dd0a9db7d9d9aef4c95ad32dbcf8f4e3c6fadc4e2bfd275440533dc806f8ff77cdfa250d61386a573f76d961aaf5f9c2d97dd428bed692b026be52eef695e1227add2b4dd742a91421c9737ac83940811ea4610f9d39df86c5da771a494ffcb1d6a125f4aed4814cf25c3d0c4f2429ad03b443e5da43091741607419847cfafcffd3e67e54000c17c8f69de09da900fb42e402c1206b87e2a117c8bb0be8ee30d1aeaad14a3c6152e94d8a6e4554b53ed87971e7e3214138e413018ef64398607d7720b05d3838d6f1d50dbd283416969c95749ca2c62acc67ea3eb178402175351250873afcfcb3b958356027733eaa6d5ace2087250538c46b80706ff1e59374c983485030e1a168345b0201b17ccceb2095781c9a084fbd224ea2d22e6d48504b8403ad66946c489896d47697e730a979ff80c424fbfb7d125881e11acb6b593148cac656d6432299a5abcdbf8bf3a2683a3f435df9097e319e6c7f72d6be20f307a00f1a3233a4ea0842a4e3ce14f3cb06684717c8aeb9506f0f37fd927691143a6998fa9480c466e1f1508ac6ee2492d826e429a291045205424e80658214fef0cea34d33c0e50ac387f55f1724d1409ec8f402d05909b1b56a2694106bc984b7fcbbfc389fcf0eacd3709e71ed00122d082de6c2651774f04667ce5f8b9f8190dcd211c8d2c8ce8806efd01fbf296677e8cc195e8c11bd20a568b2700927815bf6754e0588e3a4ab0b4d3525f47da6c3a900e0f99f1dc4c6e25c5682588abd0f16835451615980f49beb3fd1b84d1e122e6a8b3f6cef372b03532f81ff59d9f79aa08ba38f0d0281fe4079806b7728559ddde5dcd29fe661acb96486f7853c042c55b38a926ba01310bd1706ea46d645ad3840c7c081c912af152af167efb63d425dfd7bb8668a9e1fccf5038d0155e8e7547f3fac131a11fefbb3c4bbcb3a037fe329499cb5217080a38f9059d8542562872a0b86946d7ef31f8d852c77cc2a1c3ea2d5dd8b626e9bf53d4ec627295003b9e6be0cb09c6037e6f0c61f6dac7efed1484730c9e3b0469b7521f4755a4ea4df946ceaf6ded1243b921d3d1d65b913846ede9a56094aa8f5ea40958e74fda531668fe16597bcefcf65beeafe8639af771e2dbe40da67bee08ad7939b8d11d97474b57fdcbbf053e63ef5f8d90d3d3b43c1bdcf64ba4ff1f0ab83bc5d44a4ce344a4b574a21efca9a7211d922333511ac622ff5c4ee7b925c72c215da58bc11073eb0e4a265530036450128322761f1aec49bd3d864116000473c63c7a063c96bdb2960f6d07890454ea202d4befe101c3f2703d07d8c0a580455060084580690ef7340f0905636342c10a902fa48680805a51570524f30d0d4216c1e92196fe6b4df934c445d72ded16a0b8cbbfc40d9b8e6fe5dd5a9cab408ae9c6cd8fda86a089fc2db0ecaaf223f718cd80b5bd9c93120028ab72ca4f7378c291faeb2359bcafad76dc4d895cc16c3605539cdd0e7d7436c7631edef085490b13dfa002f11df3fe713353cfb98e330fc18273db849d6243bca0864b127838fb02361e0ca48bda599b6ff8067c26689310beb2b9cf6a311c1a12aa1b562c291be3d978f46b0f89e25b8a829323c5e218ff5cdf1900773bd893160fa6edb9caee94e0b360cdf484c4f61e6aea6ffa4587c8876961391afaa9e619763849ae52cce88c3fb8daaea91e6a4f95504ec2e0e23b1b66371b1bd692d182f6258ceaa1fbbab5f1d7e8abd4efcf9c89d4fabf74784887079849ee9f0d00af14be20f3870fa8e5bf899563a83f6ac5219c4ae293cc740a45176278a4c9ecdd2dd0fc72c59e6b5c27eb430b0f2bb594fca8c5f9429b2d653ea24d2bfbbea90d9a5a23d1b9d4dc35099193b0863da910fa0d19400c62a46679d2ae9fbf100ce898db248feb41cda5fa3c4e9fe043e6efc184cfee0d81d627f90cef527432f9cf9213dd50f3c976951dcac92bd335f91366336cb2eb24f19b2ad42fe13948815e695b092464fe2bb39a858f7ab364f4f29f5833ce3650f431ecdf366fe2030ac3c5e817969b2fcdfd27393a50530ebfe13dabd4de86fb93d61d3ac1cefe37c09834155f3e985e62264cf9ee123c64afb85df8c3169600d93c608c9b3efdf5a93218a5480287606f29c90b0cb9add26ecb2be81a4eac976e3fac12836ae49ee7c67897a5db7ab25874af7d39dd17bfadf20931429acdbe15df3340477d5c3f801112054d1c676547a74db918317c4f83634a6db855228330504116acfef47b00376d3b1db78b87870049ce7848b90c2048abd6ca790115aac4f947b93dd29a18df9db0c29dcbfa4ae1b2a6b8623c32fb0b45845b40c26868c1facc2c95fb5b8cc623697041cfff66fa1f1f6250e76b2905387c4106d3041cff48b43d5fafd0dc7b8d3f55abf30820772ea75d903e51adefc129f6db6b34e876b2e5593a9cf5b32a993b2cd1fbc29567e020b65fad62d2fd0e7d660f948d79d92b69f7d23065c744c31f5f2b76d2c24cc92a973c7aac841033d3aa0cae2508ed4607276aa6c811a74229e7d83dc61898990c6310da62e8e7dc8c30ae38048bd0d40351e4793dc53a26a972b77ba33ad154cc58034122c72f7e29ea0e11660e1879e8b634a8f953ce17bc6c43e9a386f3afcbc43f0cdbc4ade9febe4e64bb08655de2d75f64d9566a8461f928dfb9e56373326377443c040f20d1cc6ed2c68308accc379460ff66ff10de568ce4ead08b67681584820742c136809c4fb0784bd1f34f2fb689fbd9d1ec643ee72131f1007f61fb1bcd74c976be84c8cba617a500f23797fdeec1f0d20facc9e739344b612ef7ca24aa35c74a9a74e6708337388b88cdaf74d3c5633aaffb59e1cb3147bbe2f046de3bfbffe46e3f0b44f493f0a2506e13c964e7b4e25623d9e30b22e5219c960579e800b1ffde715e68054afca5edd5f68cd48b629c8d6b81672f80f1e4b70b8daa07537b0e57927ed8e9b541f6d6b6b5cb8bc9a1fcd5897878b303631aa2b81ad344de31f1605013b3d7e4c074bc0a708e214d9d6e8ccdf62331ad87f08ca97edf713d8fe6cebfabc7e8a32a7621d1649e3fc30eb1d939b3aa4c5329df22add6ae5e4f48573b518658bf3afb2df3614d4aef1a5a0eb9fbc2e3023c5db7ea304c8cc6b130584d9b75ee66cdc58ffa69435871adf072f71b09a6290ca430358ac05abae54a1cf857e7a10f5583c8ae9989652e5174408aee9ff54ed5c41c8e6df7c0386dfbf9f528c5a1b7e43914003d7b319ff29716ab8c3afeb3b136fedde7a3af587301bbf4c7c0751aec048352713289f3b2434be21ac50641cdc7751b2965f204184d87196412091689f0300809f2b052cdf02497cc5f20aa27d6517e19b58ee70a7594258a7f37299848db19bc11782c7fa870452f4642e614b7d7488b8654ac92ae835d6c9ecb4464cf81e775fb6a11520500b618cffc068987adb75885b7fa5d08c1bcbb24ca6304ff1fa818748d7b5d18e1009b2c16f2710b96a89643a2bd665600c88c8859cacd4081053af5b0bcce05d0889f28250168c222cc7335c32f5f134d4dc19164208f56babb5f240deeb9d29354c0ab1b60a211037e9137ffc5939e0aab3c6fea13aef8830f09b59b0a128c4f1a49833eab6debceea2b1095636b9fb60863faf9be8d884ae048fb8060ba170d962e7e9a1b8ad3fc76f684da54dd213354229d9d17c6e8bede2c53377ab5a194e5ab70f9d0ea3fa788b0882f4f4f1ecccce27543f21e6001d164d572070346804d587143b71c0783dc17fd5145ff4ed29ce07f8d429edb88cc8554d899d017e39bb151747a5d564be7db80ee37902ab2219d55cc9b2262f160c2dfca4d9729df3e180bbd9bddac9803309975201cc04c478a719b2d830c8a406a7a4f12a76ece1b88a227f08d92dc578303c61b91f07ff1c8adbada2f210bbb1607cb7adc516b278043c9cb76096f3dd5d669afc6ba0cd4bc0af312e7fc8b7476b2fa608df4e2d0b3e809bb01b18457f7bb3ce9cdce20f8a4f4e2831a9b00bd1fc59d428dfd53aff99e34927dea257396f28df11789cb5c6fff3636b1ac3e86bf42ff7e97e3b743a6192ea735b3868cc030506cce8012c7b5a2abdd5cc5ec3c34c3f00cec380cc309465d520d6d7d562d5a73454ae2b0caeb1775e2b9d526aaa6a805b95236d8105c691d6704049f9485d23951408863b40dff01da47a2f8d3939ca5fc292bcc98446cddd5ce4bc60499d3d41decb2849c60c8e19fe621012d9eac1e186879e3f4a19a1a7e21f9c5723ad5d62bf9be43dd2be305fe0d64a0a222c4b4db99f4056cf660c767d0d86fdc35f645e95f1a0ee64c07b670762bce2c611bd82f29fb0c344e81594d10dc917b9ae19eb2f42afaaa6050116613ec95c20271b90790594c9f33fde439ca25ea05c17c55f592528c8de077970f72a26c1ad9ab30b272979ea57cb74050e7c8d18738934ae581757440096ea0f71a3b83feb0998a0e7b7e41fc56ca26e23f9be3075b2b1ddc2361236105e084b4ee9907ad0b1697691d072335462c353accb16f13cd6af9ac61c97ef4daa2838a57be8a598b927e7e6e05c76b9b16052e1062d3dada0c3e037b24566e18b3990293fe6b6fdd4219e677c270d5e96af6516c7906963da9fcdeb239da77ad1d74c4962abf9b47eea4ce7a2dbccac47565d866058b547102d0fd3030b1faec649c38418b97615b4a78f839f38eadbade053e374be784ef8c7d39a54e561b063574cec0f0100c8ff477d54025b4368fb4b047261f1e7422162d94ace15a0c7b28fc3ffc317aa6fa1b3725d9a540fb622ebc7c6ec022aca2270d38e599a352e4d661bd14340e28f18c8c2d4c815cad4ebbdc3b419b88745c4d1887bccaeb06dad9959f47b911b1ae5a8c3ea74d185bdc6d7c20ab84b97883191acaca0e1c575316f6b712bff32fc687a4e09e80a70f3909fdb2e8be2c67d6e817cc2f6e2760e173b3514f81fb83c030c74a53e2c9795d1361186b06b8671ad0233783bb54cc5c9d0b7d25b9292e22f8c3feb53e60e4696d2769f88933a4de499efb30f11bc54dd5b4f9b1f0653fd62a48b779adccf7f015e72060c1775da3c87408c58c9413963fa325c9c8dc6031e06321f6212ec364479a14f2697c1c6af22c1a247b07784cacededc2f78708a0c283557907e3c0ef064d419326809bd3cc779bb5468d9cb27569f64a51dfc92479cf5f8bc691675827ac4c949d1f160e5ce77842e2d73cfc020854da830c28315703e5f6d5f93047a2d9300415db5482a562307bca699f1775d36afad1fbd40bd56445fa9599a57fcef074f2b5869223d7cf7abc222ae0d444216e1d1d79a2bd2f3ae97108930404f820885bfc75239a53f0f44bdd611b5fc44492dceb35baa953f4afb4a38a6e01077668128afe8aace974254083bfc6852980c84db8f4b3907f76b671d40fb664218c8ec8776105250a1bd920c8703d9ccf6a516f1c4d50470fc9026dbd572ae73185930b14a22386e24f3f64fa8bd192ae5c5eb5cd4d9af47bad6dc65381cc83e5382933d91740b0529ccbebfc1f4f0082305c32e09e6edf0f1081a19118694190e24d3775f5074e2609f4433d4cb501fcc0f41af890359d35b2dba2067aa800158ad956bd4aef39f6398e3332e9b82ed6becd578faff3005e0655a3985e08adb8d8de88239c1073ba30bfb25dcdcdb4b6bb03fc4fd1766718848c7e95cfa087b711f370c30bd1938248e950eeaf1bd005228673c1a88d1f4f76b459833bc26cc9b99eef94dd12fdd45ea2a209f4d79cb30f311cf92fd26f57d56f0c7955b7cd83b1896b280b313f144e7898f2849828cb9531fad8b4878bb8e2a939bf90d160fb4cc063779703ede1002c45a52b5957803d923f2b722037ec126c5658a2cacf7288aa176a40843d33c533d54cbe951c81f0a95a23fec7ae9ce063565744b02d0e5ab34dfb06b2654dccbc2e4549c2de9adf390901ff66a70879df0cb2885dfbd724e151404b27f75ae7f86bd59d57c70b0396115f54c3e30669bd44f537a1f39083628ff8acf63246b0538851a9bb174c10e89b7b24127b2b7dca3e9302881369111f8e12e3bb56c8a80a7f4729fdeca7492beb363219ccc1c05e9bbc288f84d414ec954ff42dd30f6f7bc87614fa781f4bba2f394c0e2438c395d29ed85dcc4924aa4e24bdc8f3502e0c31d48078fe0dcef7d8828a859d95e88ad069d01f149baddfdd0778e0257c6619bd8e268453a330be071909ad98fe5e539b7d971da4f020248f0971b9e0f85d85d13473e37b29e025f4561dd78654802710efe372ca1f9b5feba23a80ff6327c1ca426b6b12cccc8da0fad5183470530b1f1c7196f2896329d25f0f64273ee220c9c416cc4349c0da98fe85035a05472ebf524fcc5f2e6dc12587c474c43590338892d3a791c200e9d5033a2fef0b20d79bde58d5d2f1e87b4c41c6025eb313a21178c0c2ad7a9aacf3aeb2c3d06600bababcc4a63cd22c50e7f6810884b6aa3b8003b0ff1bced2f0bdb516de4c5bf061ef29a0e83cae93dc21b909ad9155b97ace819e733e12ba523ae6c31e48722af041f07a919d43d06b9fab1f9399b913f9d20925e3df98baa6db00f876092b4e9e610b8fa05ac2b023de54e246bb766cb0d80e01782726081338257388d9b8ecd1c66a4b4efbe87704834c68dc908a033081ac4d190ff8cac7403bc20e12de1aab0b549c8b31314243a173721439c02aede1498bc63984aeb5b502187cd09ee0c52604d29be6f5b02251816ac13073cac4a6d5775a34e8781cf1a19429b27cb2ec5f12b90ecb87c37dfab05da0faad252e89d664cf917b89162d63307a8c8ef320abaf004bae7460090be3c3a86eef761ab08ac6b1c611c9069161ac210c7c074d401ee5d4f02b3e2e19affa7e9dbe688477f74dda6aa0d4b8697999d7dc8e4eed202c6c983246d411c060013581f79106c55ac3b50fbf0c1865a306214f8d31fdbbb76caf5e88a78c733fd9e10deb02dd983ea188bdfcd32462ba0f984761c6bc29bfe6469f579d008677d9334ed064321f16ad70d42f509eae246c4cffe8a5bf5dc2d171fa25091922eda5de00fbf5adc24bad18c26c094c5f4307a3c8560f8e6382214818523c28799e0298e081aebb629e1884071e7bed2aa22ec292e09d7b9e170c460419fcae2d27e20bce60f25860b479a359688464751460b5b4be548b7563aa3f9a6f1e3b67d2814a143cf0c52e35125598993d718319e9e56eec0a9b72354902a781f7e105fb68a47a9b2ea53976f933d297a463c2e2885370d6b004a46d6c5eaac3ee2c74b4215339a6f85fe41e424acfc4fb756b15fa85f76039b46cb45f5a660ba2a071909e86d33fd6bcd70ed61fa4b95cebd5bd29e3a4e75f31d41c2cbb60e9ad1a300b9c889ba2abca568ea60dd1941aa88ca23fe8005356e7f66dbe088d3dea09232bafd7570f17b1acbf0e358893b54cfc36f03509ac887ce5f4ac35e0c98dacb48640ed1a9023c7c2685ba8dea7892d2c133353b1ec7ad4e243a6cb9ca56d6dc06c16207b3aecc5240a18fbd0091d93511680451dde121db243c7128a2e8c1f204007c650dd20b46cb7ee2b8d04a4688664b299e3789e603d0ca70db33636353ee770f00c5c145fae0856b44084618d72fee51927bc4628c4d0e4a4cb50162be8ad7c19e6447b1a3ec30f93258112b20ccb2a76e2827c5077aea8ef0fd0f8311d80f0e5285af6d877b702f541bad9293e987ec23e472db62a80f4673f29899f6e701d36d1823944abe37ee30283dcf23c0720cb5d1795eb64df267f63dd9b3beb1be7074806850cad4315c43ad9900082de85be7c0fe8195c8596e0f2c3c589979492fcf9006f180db286f8912625782bf469d2da4bee4fdf48a02884bc4010ece7c939fbdd21255775a0b8f3c1bf4222c1fda1b46b49e1867ce2d6921f526a73e764daf8aaf388691f017d5710273666141fc7cc86aaf8f38281f629735a99755adec85652f1e3e068405a09030fa01d48f7b61b3ab45174d7f67188507b29d98cf383dd92ff0b468cd8ed9af0ed001c059616c47bbad96ab31057dc24767862fcec3b1363151b5539bd872e007f8730199172bab22aa7cb0b1a4245ea08c2b2252afde8b1878f5d04ed340ba8ad662548a90d7f99ecd6d7cf92d67b098c22389d8a580f6e092070cbf958b713ce7407e8e3ffc0999252a9ee6bb6cfd883c7d252d74519ef2344622e413284f8b6d271f8ffd63d56b38ab5a4f572c8f3afdf66cbf52ee937d7168e72e2478d606b96369779c5cd9e4835a8d94fd01f4cd87b35658eab31163ad7ceb9ce4920d8bdb24f836801c10d6104bd848098efd777975715c3280fc3c762e9e7e465b7e022140f4706a31f9fbf8b3c51f392700171ff3f498d340c23f62eca8a882a1425fc3fe8b5dd909a78ddf9c0bef279c16cbf3c269d34a7dcd716e82df9d62b284e1a4f5f0a6ed4bdb936737a74c9ad4d72f2c66da7541b8b2b41cbe13315502ce9277dc84430c7acd9e95880768489eb098ce80d0f5d6b84b33e482496d9b24cd00c89e19677fb95f4a8ab969a3da9bc1c8fb6903597bb0780d48ea1de0fc4ef06bf19fe653ae17c97a18709b88d3bb0ef92cec05cc9f5379c76b5e7e781160344e9151d96f4aa224e574f105a51c58a1b3f1cb2b06cc09cc5a60d5ce380e4e676875e2d25920efbea035e4a0c85224ca80b1c4fb1f1a0f2ee49dfafd8c0624fb586aa06fcb60e6dfc189801695498ff208b9ca884c16e1493e44e90a4d64c9175c5a6c671a7645e9275bf3fe5471e34fc3d51db1d7c036a154fb7db4416ef83a7e4df4013a6f6f4612a4484e091856bdf87b3e69d357f9c0494b57b7ed42ea847b02923fd87af429b53173c15361f1de05e037f7c709e2f2b69fc195d27203a41515f863037db6751f96eb7f9218b5ae2afa6cbb07d92405f3ca6acf04916da385322129bce3f0416862273124a8d9d1aa56d11b07e1aae0a0887090484bcbd1e5a21d914782885363004539b6f06e5cc9e3417177866e5a9a1e2f54378cbdd7de662029d025b5cbd2d0f66cc166d1e9d9c76e8a77ac9f7754a8535d83f3ccefea26f410360230f20a5c43b91afe5161cf54c841f10069297fb13014efd3cadae75b7345b1c26b65080c3de891a62c59bfc3fe52699cb711d5bda78db832034a7cd227b1df93d9a06c85c836c6f94c29648b72a4bc48b30620f17b2d896054940be2a0c1d2b0078c567f7f145c58f83b5a8e2d3feb1163efd57aa75f950f18fc3bddb6317f97c95ac1813073483cec192adb397b33dc9cbc546b1feabd02a17d824433887c8d44f5f1b2b5807e1b679fcf74ffbc75abb1bb0548253939c2cdc719f15156b41498d4e894091ab1729a61cea0f7485773e33010d8047990a0dc65b874f56b314807572258310ed02f4a7618816e2d847092f29e68c9376ff06fde7afffb90b7c3e6e8819c9e9fa28e51563148d155e331e416303447406e0c6e1b822b3d6bb4022b35422874f654f7a119813ccfe751a41e1066073710fa661681099940ff9c44058c8b88b3778f4652de8e989aff5a03f83ccbb18e906d25d020ce3c9f680d1503050ea7b1bdbc6dc2c92b9da609ec09038d38cfd708ea7209bf6163a7b7629a714f08fe199cddf999fa2986e39501d89712063681e14de1b969b70f7581fa7927671fad6bf11d766d8e4c849e44d9564f95d0cd743bf3190c82c80e437d73c85be4f073e72516ef38792e362a5680acef0d96f13d29e44751bebd43b9d4f1aea49882521cf98fbf61babafedf15c64ed5baf3ffcafcaf19ea4215cd59a0c2f353ec4a22c269b8bcba280cce2d073d9c3be4475865de28081c8d0b29e8dc79bd26e374bb4ef3311d5fd7789cae2b691a7bc7f51a8fefe28a66e374be46f37141efdd318e2ec97ab5f7f873e0b965962d784ce14a7e02d3642a62653f72c6b223161d60e10c0ba7b1728eb573ac3861c911d61db0ec106b075839c20a3ae5c2da910b7cdd70e1176563d1368eb2e1e81ba3d910ad4dd16f8afee6e81b44b559948da3d83ceae61725df54d6c7e194955409c5d807d88952d1a1782914498802eda4fe33cc84b35d6833e76cbce050df26b2a7cf7cfa620bb87dd16ef0f9e054bdbdcb83d0d757c2b1469ab52804e3da20994dd4c5867fc23135c7d77dc581f10ba1aaad1ac94668570e44ed08f56d28a16c8d297953a9b3222efbd604f4974fe409c720ae8b89421101839c551910c665228d10fb4cbe975840738ab2da185a38de47fd54ab2509be67bcba95f355559907dc158837acc8e986a33a0e9e45cfa476f924c8414de721cd8b6e192f5fec13260fdfac359dcee50bccf396fd870f0d2de49dc254c61a892a45b9db9a53e7798da8c65c556d738c41512c4434954fa1485a4a3dcd554f83034caeb78772c56afb750529c560a538ed08214bd59eaf01a4c33a29e5747b481c12954247eec6143cab93dc3e5e16024c9a12b846a9b597e6504989eeffcd943902f5335fd1976826429d95a42b9400d428316cf3f06ddc77592afdeff3fef999e96c95f0fd9f67b9ef42a37e1f56677775f37b9830f617d4879f2ca529b0dcb6ced693a8bc5a7cb721ad67469d56173c2f72b3a8e36e2ddc1cfd2f4a6b833c8b0fec8cb8d0766dfd453a699e2ec6fc697a7c6bc11a41d467cdbbf4a2742c53bebd2df487172b4af1a3c171e202817505ba4378788f8ff9adde7cf0b122f623a4dd87a4d7c4bf59aa727910d7898653ec47bfab8abe1ea8eae714474d0c5d2d8b1f35b1b0bdb193eacca88d109f72a12c7ce694f7e8657aa608e287d4486f5e044c4e1f10e990b182661407f31fa2eab3034a79e79d24c3b79442701afdede36f3d3f49508ca43ccc16b3a3845d244e40dd2f3a2daa1085b43b4468a3242ff197141dd1521335fcf63dc48f21a403f02bad34e7b0fa06824bb302abdca8e4487687e354e34332d6282653a5116efc9f536710bc190eb7d88c5a3c8122c9db53cdf20357fa653bcfccc00df292bc9326f2d106f91604a420326c40bf6035cdd6d20d26c10a85991a524f0c463a6d66330ac05cc92819a0141bd800be2cd9354be9c590357091228c9ec65714db88440380c8280d14e41d85ea788db586b2508ad9a9a1ba08534eb6928d20a34c21d4c0862a6c46699da2deae7d60409c97a1d0e18fb276366d4f670203dbc7713ac39e9d244e5b08a8f728d22465a61a19f654d2c46f8edad36b41354ba72d167fabda18866f790c92f051cfa422562f24b24d98f1c5020232178227e960be05d4774d128040d3133a8edc4573be05d9f9c3bc2db03f4f55fd52716a01add5eedf37ee18a1b750f6dd2b8c530d7a2336c12acea8ff3afc367b8a043d655f347fe9ce29894cc8c7f3895e405444287f14b42f2f4b3ca63b9f17a0a0c4192528cb8650cac1c99a3b800df83beb9d4ea8259496c384ca85b89c6fca3af09b123002c0ed0add575dabe44e11dc4d0926c1b54f41037438c9449ab6c9360d725b9b1812ead8a47fe9a659ad8265042f747712a48b15802769188852c55a27dea3fdbd6fc9134cd1bb4b4135b935fca434921f632718311412a375e5bf22d8d59b0c2e0e77221489ce758d82c47396d7516712b8f637f09684247d4854d439a9a44ae83f922c0d6b761dea731b391af05d6fd3b1d3ac5fd41f54453fdd48111492496080cb2a1b1ce414beae2447a2d9f22dd025fb1de61e66fa550ac378bdd2fea8a5c7e73651d44ff4621ed6cb75a84dd90e839447beda52f8216dc79885e3670c1a77518d69728fcf1c89df5c2d00235ff7b29874d5472e488c79a482e4327d23d72e4133c93ad1dbe30a922cd1d88e2baa593cff86a6a4a9589d846f3f742c66e50b226305ca146416d3982884a0c0137d856727728166fd462bace5840dbef1d42e6be6e3f3ceb68ff2b714603f53a343069d621235b08a20e84a202cfe7f10de9e3e0446d91ea00dc1faf4e0f5f46d9c578e28e2e1ed7091e4ca928db625fedc28cb2988886687982715d959693932e808e4600150952180974d4a394c83ecd3ecc3adc11b64336497d94299a55716437533fc687c23af2a2dd28a5c91e89ae0a244723247bface9bcf7fd712c9353555a09d4555c3923c940aac94d6aeb2b5e035e2666c9073a71bd6a923ed5caf7f2c923040989f9b0bc7aea331c76fafd863e85e5b5c6d33ed568573dbd89d0211a0475d50b291eeac48c6801ca47ecfa1d7743bb552b5c99041296352fb329fe8b03bfcc1c005d0e3a66502db81b5de17f0728264ea24429204334147a613a7cc1a72ebc9673157c6f180f7fd1f12526d4402d1f6d3059a40516a233c5592eb417b9e64ac350103ab9341d7e9a7c0d17b029b88d6ee15b21860380fce52a400308a30118eac95c6f20115893c006581e49653476f800953137cfbc86ca9408f91fbdd3a716f77ce528709b67c6212f8bcd807ed6aa7df3c61ed7bdfaeb728b6f10cd604d45a7b2f078ce3633c6b97b80982898a5b610183a4f9ee4de5a6d007f8f48213885eac89ea113eec9a857c32d8188241a86803ef06a53574bc9cd200c4fa3b06b5ac6ea77bff1a15076584d4940a953c0bbd49fc47b337f63c0ae1532c7b67a0a6f572aefb55d54ec97800885a5a8d8e466e86aa014df943f0c44888c83c04203a11c91ed63308c528428d7dddaabbcf90887fc3349ee5be2e5d4729f6d8feca14f056516de1ac8821fd2887049311515797c92728b6b25ee8d0c49f3e50192a3e30ab28330b1a4bbec2e707964e7657562de74b080bd8db74d431f835604c3a092cec162ec3b4c2816d532b6db0513a00a2bc792baaaa072963dfa4cb1b6e6676db6177b8e28b229847a4714855cf6988dad01ab2af4353d6453c94fea8cd06558a4d4e1859eb485c48e724836b26f8e7ca09357dcbfd88fa1d33fb3be22041a56047c7a0b1793e527cde1ef404e43fe405bec5b0b925cea77a033de48bc4e92a865a1e773751c9c14ac2f17b3bb0203bbc866270e9c474d47ddfb9ba4806ec269a70fddd4bcb100f28603aeb33647bb9acb19849f0b879d612369190accee0367f9f0ad82cf600367f44b3b5470aea83ac076f4ea2bec116618a0d06ff8368ff32402be2f80f8cb56291f367b2711449eda2023dfa49245d36877ed75a64a5599a057b767d38a1b380690c2fab9f02d80492336c7b0597bb06d19ed213eea192df6182b8986a31df6fc494f4611ae8001a8990fa33421b05204638d63b2b2ddeecb279fdf8127eef2c91b2a8f9b102f5b401505f7016316bb99f8b9828a40679d767fca7f7458e858273846e89a73f41c38d83d5c2e94110abe8e1264b4711a02e4f07a2612ebbd1e54ac3451569ca6bdae37039217cf3e4eb7cd813f87fb4ea880e3e65a14098d8c5f0fb4672005a4fac1149b59c9cb162a131c94164781a00318967443d10caae6f15da81630b185828a592c0b1e2e879d4eb8fb8c4de639cee4970a9eae3f6fe01988b1ae7ee4b184a274f448f4dbd6639a94f73bfa26e238517dea4c4661b6b1838478f8fe7fc088ce5168068a73308e04a8cf83406603fa2e4c97a09699cc50877888af06aecb582b884ce8f959c8d138b888be037539363a6de2861b0df3016c1903fafb11645a30d819a7036dab9a05a134ab75779d9d6bc0f2e01c666843da549bb60a1c25bc219eb06530849899cd658b01dc07ac1f1c8007b461d3b229550700df9c26c40108241c732c4635c05e3e46b40c01324c1adabbaded2d654a29c9300626064006222ffc904848eb1c79a2e7edcf8ac88a7d9ec4e2602955361459dcd53872cc56c221aa73341b16a9de9538ce4dc51e3b620d69cca8fc7153b147b852e547734415c158e54773c426e28835f438bf8717236a32610442e802257f7fb920368c5a779668b1852de4d4e844091f2d9c30e29e1a9d2cc1137d50a3931c25eaee6c92e58931c68d9a6218f5228a1b0f83dda4dde1d3caf7b7f2bd701f336170038211289a8c000a272478252981e66dd96ed1021b9fa01d5439c20c57ec40063c5802089240827dc44a66b4c0092960408508aa1fb4e85021b410c58f155bf8e009438800162fa862486708415358ae8655a92af6bdf878f558b71530b161684a08a080418523ab0d08f4bb7f77fa312b68b83bd20a345c9c2abdeeeeeebe727d79766777f887636e8a1f50fef803e9dded3dd4b269b54bef6e6f3090880206182d67777777777777bf9cdedfddedee3e3528507e39a79cee3ea79473ba7bb7ecf6f6c69470051a193c81d6ed2e99999999999965337377b7fcd6a240b33ef5c73ef547e37a95d25b4a203c39527637b333377716822f5abe90deddde434c44e9a20b1b1b274b0cd5e86489292fd4e86409a04a3bc53aace3e2adbbeb8ec1b8d170aa914129a548a1289c0f252af372d7042ae50906d99330c821eb24a9a858519175560085bec80f59a7081a199c330cd26e63d65906bb2ea8ecd84974f8c421eb546769464c21c414d2bbdb7b280a215148ef6eef21160c61c1faf6902aca1552908214335248ef6eefa19eb3863f6d788b0c17dc5d87d6b44629d0893284273c410911aee0828b1a687145139a105f8bc3466a01a5cef3c3021ce077c7fc60228efefebe96daed1b49090dbf76ff37779605e154378f1c3c6af80f82dfbbbb1b5f74099f2b96b004200061c8979bb9fba5866e66ee76ef6666de961abf5b4ad9bcbb91e376408b47285132a46468552ab659a262940a88922125434a866c6e6c8edca89454515245c9909221255e28a9a264488b2b9290842af6a60a2abdbbbdc190bf19a9bd39a3dedb5dce193d36478ed9c718a7fcb09b13fb4e6686a1eadf55cf89512726a9bf9f6055fd379e1a6e40788e54b9b9948e40fde3131e55cd7ac4ffb13d269ffbccdb9efb19e65edbccdb3e8e5e7aa2cf3c8cf3e66fd3ebaa795dbce13ff2449f890bdad8273fecfa09743f880da3ced00826a57c203c47788e2ce703fbe559a1b00f08cf11066305c263132b101e1b46491e1b9e150f109e1583b102e1c9613056203c387cc4796e24109e1b1e1b1e1b1e550784e786c15881f0d830186b90280b7777cf8c40639fa477b7f76481075c2e2b6aac7097deddde43be052db4d0c28b9a6a9120a0eea489a02a438d4e9a50d5f91f10aa5a6f467aca7fa161c3a8f30b6fcc9f46aa1130d6190259a253e70764894e58fb812cc161547f38393b497a4e204b76188c15c8121e0663ed6d4912d6c969204b7270705614c8921c06fb03b2840873082ea5c43029a59459480cc330296973320b29a5942ea5c4b028a77477e95f90e6a41d83bb31b83d737641e7d7b91361d088f7507e77ad059b925b36db67e9a1999f9dc23ffce1fef06f94b1081aeecf142a0a883a3a32fff00ffff04fec41dd9fe9a4dc9fd58124c2e839cc3ad187e0e8c3a6fcf9f9913f3f3f3f3f3f510a0de3cff2ecc79f1f2643abf209fa614afc51ddb494ea242460edb2654d9f3cd05d7786ad2c01630c5b1d81915cd0588c0c75515e5e5e0b3818b4bbbba5b7f4c602edee6ee92dbd494e707777671836a54fe9d817b472f76cd9de185683d5484123f38a57bce24c646a914cb02b5ca480865ff35aaf644169d7692ee57f9284936594f2076a45949c949c04d1b0eb4c5f632716a32736b19f69cccb320fc3b0bf91e9217aadddb3ad04d4e2a2fbcbed9227f69e6dee85b37e42f41005d1f5e6337b0dcc2ccb1ee35ee22764d2b8d2b22bdd911449911449911457e2fcf9220e0757a9324ca64ff1b508449725c6e83c621224a78c9fedf9400497a56290bd9a19168bc51a8d3ea0da9fed89a2062e8c3e214b454b8827e401a39177b700d1b2713e6ad5c8081936bd3bc69f9806bd124b77ab3a25a4a56aa95aaa96aab53388277e295c39e0205c41abfe2dab09d522a3fa1b49194b947850e2c136d65ce98a4cc452f24e31990c106200809258b29d235096158eb422acd46026131353323295574a5790368dfb354c3c647b31d83f84f3f16218aab61b69041d72ab59b4dbd70e5bc987eec0fd10550e87c60f51cbb4fd85608bc17e1cfd34bc8e1593759b762880017a38e1d01312fdb27b10c0f9a3389a1cbca1d88613c0fd002b7fab8282fac72e06fb77703f44955bcdae99da630133f8f841534303901a8230a0010e783109b1e18607e000811c7418b2030c0f312b2a4e4527acbdc1914f1c61d32c26471ca9e12659d694de2cb9a436a62af14ae941eb588c0bc23fc322fe58da6613c3be1aa0920fe5c9bc32ecc42a5182a8c4571c52d9605428430299ade9940ccbd8a83d83000101ad7657946b74c2c48d4c7b6ce1b6c1a7fdce24aa2d7508d5e60bf3104ec9b0a352a93a60b3eaf8cae955ede79d42182595cc39a7a8ce8dae20505c41e6cc0c9cf261b39e73ce193dba660e5af8b453f2a911f4288360a770005540c308640314400d287ff82d7db3e69c406950cf408b354a3e1486253434c508838620682326c747132bf6827d0cc2a8f9ad28eb41bed809096d9ff2310cc32287cd30c83981beba09ce065b43acae6fbc41cb1872ff42d9043a0514bebb3ae822fe01ec08118a29a8a03fbc2a028a3752e032a73625f54ae9f202b2022173ba03cf75b76d3f73f92f7f86f77351b62267296d8afa73fd2af6b76da3ddb2c56b99a1acfb655f8862aaeb934b1dd12d62742550e60fb7e82267ea3daa51d6d85e4f214ce0a2e07ae87e8c379a68a14243dffafd13d5e839cf6021d5282327048a96312a63c99e7fbecedb84cc25894d46f5f79731d5c5d3a264f845887c5415800ddc437e4acc24bcfce06aaa5bfb7be9df2aab7b35529b2084eec5627fdb9af64ae9eeefdbfc4259e507444bf52e341a986ab846aa6f4705bb6f9be47cc8f67e1eb5bf22b2723e649868cd4d7a0a1afa14b4bd37db3c0322b6aa7c8f359a936831ee8819af3717c78e65f9fb2d8ee8dda2786366f1062ba188cb09b6752af001561bd44bb3ebebde3958460c6d937df5f6098788cf24c228223f0045a0171c583ec6c7f886f8b6d5a77d233de68644ba5ab677d00f44b62706f56873df350a442f6a1cf275ef00046d1313a82a7f6c56931328e962a036c90e136c77776f9c1821822a43e5173541b38ff12c8ffdeef470cfc9091a6ecf3ef128197b88a8705beb4a7281f945b23a5adf1d5b77e34ec6e2458e0145b6b20e83bb3b0cae885656381c48868292b8a9e14aa151c30dda292b1c8a752a0c6ab8d59d50f4331c744a8286dbb33d51c7acec2bb453469826b78d0bb2b27d33f106edba461cfca21d54fe9a9608b2280534fbe8931eb3a14ff2e29cdbc3a998171446c5155a272b2fea4aefa9fbfc2dcc0b54f7b34ed1ef2507fa2ccff211d1aafbf4fb5e72203d7dfa11b140dff6f4e0f8c5c66504743f96b6a0f1c99227aacb13f46f18ac6154e5bb7b60756f7b60a8cc3160e446a45cd2d3c362396fd4485ef28929f9d428f9947c5c7a4c2e52947c9ce8ec11d4eb0d39bcf1b67119d75be4dc4b3ecff291c503fbd44fbdef53fb683ca06157f7a6de6053f0295040b2a2b513f485748a7e9d62f9ec232780fe3e919b2fe3de1b2ad0300e099165438cdaba7da110755bd27afc33d3dbc6d558af673b99edd7dbfe860a95d9ed3a5066c5f4cf7dbf793518eccdc4a08d182e09345691fc0c08991839ce08da3b1c6f5f68fe9a7b91dbeb2ccb9e26625ea8533df3b0e9c919e69f9e911abd22b2ca8fc7125e480450e5b542eb263cfa0a7c317880bed0207a7ed1bf911abd300b85086dd416456cfab737d35fb62bc434d8589041e7ae0a943f069bfee6ce324abb2e26823518ecac8a160b4079e868abf5b65faa1dd16cb0a0a321cda6ca9fd9d1500ee9536737524cd10fbbba6d1cc463091e3a5264104e0da5942a5ff3666030ab9285d6e0d44a34712acb21c328466d2fa58d2a57a45701ed318c870ec6711cc7ad17fe0684aaf6dbf69c68c43d75c463898969abc9c42e95eab55597f311356ff4dab7ec91b57127346c09b2c9324aa3d63dfa4299da210bd5d1c711953a8aa36ef4244f3b711e0b716a063ef58f9e831a0abb639b8aec82c86fd468e5fb49dfdc87319917e3893f5a82a05a01d1cbd7b4dda4ec6ddb9e26d6eddb34ede548ab44f0d4aca1b038e447e9882714ae9acd4c7a504c6fdb0a7a145ad1d07582b6aef510a39ad5a40fd1ee68e3e639fe5a8f46a3a78975f4466a4fd01c98fd940907c673203e7192a3f0b6821ef1b31b4ead8aa79cde9327b6c25148d56b49a12d3feafeb55eecd0c89bf16660100a83fddc375745d0d081221684c17e01dc80869249ed17c26064c2a9215107f60ec4a919dafd0c1736ae5081faac16046362783b06ec32193f4d8064cc70a12bb9bc98666cb36adde6b232b2b75c59e1510d89a8fc41c9322fab9476dd7f4c0c14d992515c5e4c30345e78efd53f322e74d3803145983731ccd75fffac931850a945f82404daaca86329e5a44bba523ff4697af19a3287e28dfe928fc92601a687015d71ca027deaa863e30c7ff499be96affbc28ec6cfaf593972d4b0592f64198d8fc27c5d277bbe1ca84f1135b988600721a2a61083fd3cc4280cc693417dead74cde14298b10412e224a4a8960bf1cf2ee93ae78a385f08660d0a6f64f1583dd3795b724a58b942f529aa484919286942f48f952ee7c75ea25a7b8c01ad36dbcc383e883ba1fe3f2a8a10c7579d47d31cd181d0684212941638bafbdb0bdc0f9e028eedcf2381fd39ddd63178353d8b0eb483793152ada69739182c6b0b9f4d018381f5a90e6d2f32e516c0fcc9bde647ad3bff0a68f3d60beedf11ee661be215e7280791a4fe323e2853f27683cccffc37cece17dfcfd0b1e23e006349cab8d8627d97ba161552f35acaaf2278318ac5d0f68d82d8d8a0c582cfa0f82425348a84e61948c014e4eed9752a45011560b48bc381a7775dc0ddc83bb2a68285d73c5a7fe2ceb56bf2f988f5c0ea607ea148c8c25d0705f41800289265520b1e3535751078b53fc3222a64ca6cfe72a9c2b919cfc71cb0634f41eee0a747ee8402d1726d49f5f28228a1a9d1c2154654b3ed1b31d6f5107b7fbda1df3c3f6f5d3a75e2ac49474451dd8378b88579de21707219efa5dae560b08885bfbe2d60bfc5abe504ea9dd7da19c82dc825d928b2953a8cdad78a306346c160090a086a20e30e20e6e2d8efe1642166a83b5fbd8a7b53bba8a88a3bf49d58506346c56017ac4d70cb045d3875788291a5ff77c6198be8e457080b94242b9b5dc6a3593948c8eb9d2c1f9983873357356253d985a42426d5ce3b19590d070ab728a0c8a326caeee8e3fc09654c056f2a1fe33dcfd2524d4e3beb21212ca7d8909caa0cb0c8ea44cdf5f6c569f3cc2a0ead188fbc23d52973fd3f7cf9cce9a5b3231335bc987d6920f057d94a8a06bf337f146bfb600d3e75bdd22a366b8b0a8bfa4012bff20f20374c2f4abfa6ee6016a7c52444e754abbee1f04b5066c251faa49579ffab31212ba7d89099afd68a471949cca429ab632fce316b704f3d9c92e5c38f9fa65959050d29798a039b5e7cabf8484baf0252668b8af9e954fedef86b25357c560f7dc13b4e5c366b54b0aa382a20e197570a760beb38860bf8c1fb55ee32c5a3aec3664071e7af02102129872bbf43d64071e7a989b0f1190c0045c5cbe38a79cbfb3eb580637f2c2fe2975cee2b33fdb64a30c68b8aad9e26acfd7abf64f57d4e1df3f5b5118f5001c2090830e4376f09e9f78a39f3d6769cd97267aad7f7a315ffd43a28e51e50fc4d414e2e6920fe52954fb6384e1b17e34b1b28c15fbee9ec7599d7bfbc426fdd3ac66c220121a39f57d42a27dba097f2524a80b5f3a8286fba25dab8b57f7da72bb9c18e62c030b744612d590971d5e5e6685f1dce4c5fe6c8036345c958b17762d7d17d8ce0bb3ea82176e95da3fc393e185a0d7e26d1246b17cefcef7423fd6966d523b46f647132bedadc2603fc90ba5505d9b9b9c6fbd70c2f433ecdebe3a65fa9ac55f483f9742d225a9d4fea46bdb98f3c159e63d9daa61c3811cc85d2bdc8a17dc31b6f55c6a09092d31419dc573a0226818353a4982a7ca1a2e50ed47f5cb20209c19a559eda71cbacd629bdaee5262add5ece914ed9940325ab89543c355cd559f7a59dd56e1d64b34e216bb7ceb548df66952fb1b4a6d186ac7d19ceaf2f4770ea3244be1d4aafad4a5d5ca57b5377bc4086a53f79de37cc4b9ea54f773d5a21202c3352bdee8778901ed5f15b7f6a764af964b4a615450d421644e1be6bc61ce07cc89c39c10983387397570f5aa539ed3d1efdc86cd6bc987368b4f0dd6511028bfbf108551fcddb0317fc401025b649400361c3218360860e096c33c6d3ac818b6213e806d87ae988d8d072e0857ac87ed0607c448c530021040b4c22f3d00dc8fd9ea54d62757cf6f5fd3618bcd6a563391691683fd24269a95a2ab54d0907f5eafddd99d7d09f8cce7d31aa9919bddecb97d577bb4813b441dfdf4c2ddd9d5de0c55794d71c5568c52e7fc9199609765d3230a1b79410c51b316794174cd58fbe19c17d0ee0cc9c7bc2084a8d8871c0802abdc2bce321a807e9c318bd26dd4a5a7e4b3eebe1ec456de27d195c36d85fa84054295df07a12c04df6384838030ab33de989fe3bc38eb615f58e3ebaa7ec4199fd6f81e0251f708a388cc9df366880b472b00218c8cba1f93e346dc3124e2e0dfe7624a668b3a80f0321bba8fed973188ed7e473c9a45bdfcf0c90531f2c3f7880cf2e2cff9d82c8b01b9621983ccccdfeee63c72d4f93db7e5caf7fec4055d2a756aeeeb91fddb2d478ef871c1babf9e7bdb1734fe4b7c890fd6f92ff1ab01acf38bf18a788dbf358e3e7236c41e339c83513b84332655a1e10bf6f1c16f6745071a58058803283c1ce12ff2f0440f3df2a544e56aecc1ebc8039ee05079822354e3139c2835bea8c6273840353ec1e9a921189fe0cca072353ec149d2e97c3c68228f3a768504a3da1935451d71052b2458255ad448181657d440ddd45748449ae40de993ff8a27a44fcebdfc9020998f6514983e8389213f60de10061dc33e210c3a0e0c7a10067d981e919a2450d16f2f7fa66bdc9d52703fef655ff4fc435435b9c1b084fa9f8068188184e030845118110afcc0a817eda3639fa9d638f234f3ba1606fdd7471fefc41b9b42c04b880f3f147955f79c55ad53622efae84f5bfc41c9022a67205935b451fdfbb5de1d0901c39e33828a3e7e48aba8d6302a460da358aa63b58651b4fa8719a8f36b8474f619a55df721887d613634d2b60de38078b2d6306aa586515c0da3b6ea5fc328ad8651a31a46895e681855ec67c5be3003bb8ac1d2b4920fd89e93e2fe68fc5a4c4ceff3f449b24146c65bb6d45086f3e12244f98756391aadfe582136200041b8345ebcb1efc51b9968fbb1394dfcc8d1ac918a713494926c10713458f51f71378c389aad2491bfb6f1b66ddbb6315bf19f619a4f1bc51d71bb9c58365a914f333ff94d24748cdeffbddb525c7cb01b83a4e7838dee2ef3f2ee322fef2ef3f2ae0b16d4054ae782056db942d35c7a688c16e37fbb351834c2bb2368ffd0c42677f79ceaeeee9fb7732f37096a1235dc6a7b96755dec22700bcbb2162f66669c5665666666de8da168b1866f5bdd976952b7c6113f35fe787f60cf2ff6696b7c13d419dc9fcfd9cb6f7b7112cd861b6c60928f183c40399af6f675778fa6953003b599c6f90b33c0136450f6e8a7676f90318b58ce16f91c828dad906aa4236da3116f981687c46e40fbe5afcdde708a7b89935941b38cd2aefb9716e814113e4907c453cb2deae6d51069161041f934a20e1d9876c7cc731ec827f9222f870c8f98ce32950d83b29be5aa3adb1d2c5c6440f7e38333a6ba4777d7de9f396ff3b23ef96bded45e14e0d3fccc23b243314cbeb6fe8519a8312bf9bc0d4e51804f44189c60a7d6065c500573ce1762084ed9e089bd058a4c51c6222f9b73ce2712312fece6ace2a58607ab4b8d92a5741c2fe924091ad2796b69186c59c841415aac116231f644c15481132e55489728cb5e31b4cb8965a2d16c49d7cb67b8aaee3921099bc553c6c49c96a0a103f9d6fd0b7cf18d4b09e8ba7405f18d8dcd11579094bb0298d22998dfffc83875dd9773ba67a7f4699f469f68748f9066f589270a7952f4a2f2d21cd5eedec4304df462caa5e0c550d01dca64703b94dff4304f63a4b99751e75440f979e34a54ec10ab3624bcda59a9d3bcaa193b5efbfc30d864bf66c2e06cd6b6d57e6ddb5a1c855bec9a51635c271450e638ce879c3edc3fd03f92b6432c5d306f82f1765f432d2dcec75c195459a90de45554ee05d8345e807129c453d757f4a04e99be210ff217501725746fd86c2e3e35c49fca1bbb309ed2a79ea6e629eb05d560bc66c2271f065f58ec0b67c5ce9d6a6dba8c83bea591481add28e7a37b8d6340101b46c500add9cf28fd08a2c308a366eff2a5e7bef42ee3472fe35b1874a146ca20ff864074957fdaacaa0aead494751ee695bc66c91b02a038c6109a294b0c00b4c898e182bc912b7973d331b6920fe5958b12ba1f722bee8b999999bb65aa0680ade443e57497da8ce8c24b72141934656954aeae546dca9d7ba8a46e3625b1746a4600002800a3140000200c0a864462a168389e47b25e7d14800c7a92407c589a4a84498cc3280c520629030800000002048080d08cd04d0250a31098e106c0adf602ac265b1a4c07b3f479c66cf619dcd49303f938dfafca7d9a4e0cf4458684414caaa78842a000bb7b71f4485742a956e6232530884323317100af3d90f347ab5ed6d25d3dd9b87adecea6c5e87594b6d1838f50ec874c23f185f32c8f872fddd7c1654e8915b834d788361149ee09724d8614a5ff669be3042b66303ef4241834401c167f48ae9ca6c74a67c7a8920d7eab4ea1e00a5bd3bbe7c772a8027b441a109f57a60ff1a979461a860726939df0e9333d73a45a5bf28cc7ce17aa266109d2a74347080f5707de57c3cac26ba78d676a7c150fdb995f97406a1e622268d2133554e4eaf08f432751138e98b392661ba26b196e08e49610d0876038ea05b3b66d36c38abdbffec4bea6cd7e52abff6be78db7980bc6246b4c159fda3f4039054fbaa13da87a16497e251ae97be1b9dc79904db5e990561781ac95db096d503bf25521db59541935242e5c3012a7ca2c391f13081d14180ef9781e4ab847a67490b0b7db9f1e3105ea494c43ec97b35a9152d0d157488de65037108267c916b9c076b5615855ba622dd3188935545abb926864cbb1bdf28a6a588b5d217f58bb75bc445c9d277c2b62a270d1d8730401e87994c239774f37ada719e849b0aa7ca45f1114293072043a13516bd70242e019b4c17bde90fa6e0df44c6de4dd2fe7d9ad819e462c41d0bcb5a38945365161d4255e59b5829001cb58a832e8ef0a536e5657f85b812d4c0b017d0014afbd067a7ea0219355cd1339de245efda69020163d7c5baa5afbaf4761c11ae8fb71eeb7eac0dddd584fca5a5771bb35d56824b000728c5e02f7d4215ec91cd998b0ae469b15ae99dc845cef93009b4941a97dcd5b0c2d23b18bdf888cdb51f9476448f970e83ee57ddc0c34e92fef13a524505eb9039cff4f5d51b41ff44cfe887fa58750faf5ae01e3596f26ee1db70e9a42791322397f5dbccda2a713836379cb14ddff665716ea77292d8b4e9e554f7e04f97c225c8894c880ce91752ce26ba63492707200360de5b2e644d09401c96c73046a160b2f12da07568cb361e94ae7094eac14e33ff1f1327f0d5043b6f6dc06ff499daf706b2e7be1a7dfbdb7ef24bf34b6c43ad8fa8c7c6a346f9607759b9890bfcca2c8fa8103a5acbf917a132b5cad115edee21abc417d2b0cf9abd706cc424ef688b10128135efd9e9708117ebd80e2be805cd0952d6b02ba75653f4f08595b6e01612b12ed86181db35e6277e8e4d04543c8b691bb218553c5b94a8517c7560c5aed408ecc8aebb2d852c25a434ff78d789eca491b87eda1fe51a7fea70cfd69e4087e6197133af390ba55e0e30bd20111119d22872f15945bc8dcd49c840921c5ae356ee97a4008853bb6703d306c877cced7a48cbae4fb1006b861cfd1745771555f00c5f208d6bea81d56342c5a559a87202e6442e6cb1f1f4d44d6c4ef5b220fb6c8b98b9df34c632acc8bdb69cd5a2e1c49013d20ca2d1989b5f8221caa83896741f74f1a57d143042bed7338fed1662e548cc8eaa9276725b51ac66ff9fb3ca6a9a74e53f0b1cc7b6b00ebc5dc001775792338d703167ca57ff04637afcc1efc511585bb09a206ced03d7badccb5121e3532ecc5bac651175ba4342f705c3e2602ca293f484bccf7606b366d53d43f72f2962c836fbb56225084fba33bb0df158fe54a7a8d5d0a05ac289116689a7117c25b25dead2065322f80a8b988f6f286ed14c537184d68bdccd41573abc1e2a888004bbbd8374a21a40c6bcc0a63bdb64f0c6ecd9fb91bbbf2f53da936902efd9fbc244a339e715fa21245e68cb98b7179c97e17e31a79631f4645b634a1e6269319c36786732fdda54f3c0d38bb3e634279982086bb29b6505fa91241e57f6f741a178f956962e2b0a737ffa1ceb55610d7869c88074c401fb7d6ddc7ce81669b081ac3ff82e80010dbb671edfa8dc2c0ac9f42568468b0a8e34a124de91f38975dd31d8b902e37d87f9251968e6b03e0c09037feb751b2477053900674a24addac25e0040b8242527e8a8d7b88939264f257cb6073f84ad5e21edf43981ce1e4739ed71609fddd67df25a2798fa2ae973f1fdd7d66a183b257231a6e3af28792ec2b4a7f67b3e71506b263d31fe2f81b8a225916a9004b37f5e529fbeaf8d37fd7f243ed661d22288136fa8aab32b084d8f6602ebd85abf4f17859a1a6d876f418d4cd94726e6cfd306b9b239af46441788d0d7c49f2555abd07e669968b85fa94cfe6f957aed0dc6f166e4669b44c629a649a888653b4fc127fe51c7856c90b4d7daffc78049d12b915145f4a489f99e84632be896bc10964dfb5160c4a2743cdddd0e66257fc0899d3cdd80582aa90546a2953a637c204d658b4995fd34b4fc8c341e447ef9ee97946ef5b2c0ab7675ad68a4982ec44f87696d48f7d702583eb491b3013eb4907f4126c4e9e6ef83109f696c2b902e75d3ae37fc00bb372fe480d5aeffeb319fc9fee1f73751a634ee57d3c0673ba9ad6de7031a0e80da7448694154d539d53a678732fd46d9d4d1b783e9e283f56af79ccf7f7c66aede5e7bb8b72a826bdd32a800c855d2323e89636ba18adf62477b37ca7afe44289e369cfe15d3393b3e0ffc030060a9717248231416e4a5dd54ea7e7a2c68e815091faf56fd679ed1e16e159c16f4a077d8736d6dd81928844e3490819aed4616d2c50d588218be4422bd771aaaf8620722326f066357b98ad011137373d4b14f56aa911ebb194475a6907e0520e2b603e6815bd528056a45bfaa66fd4a58b5c09e2ee19321baea98b1575a0b3dc37ad952c8a98f6d221bff721f86ca799d08c81dcf0216930e2f94ec0738acb6670a5f1b2c656a51ad8e0abf8c30a2b3852e1dec24ec13673856974cb01b66b3b39fb4baa210ded68738352a2b051ae7708f7a958b93c010862820e4af55758525aacc8637c052982d5323c15b8481d5e20feb4addbfb5d4433b8df9024a3eea4cd2815ded4f69be5998e7e4f3c0d4e2f701805c53f55046893345514100f39279415b6dfc737add6c5dccd85ea91104f65c50bf2080fca0f4132a1aa3a4b227f7bed66a18d425866786864881bd33ecd84c87daa3f88410bfb717b7742fb94aeada8f9d2f45440683fa45cbcd1bc3def25ce0115b1a50731bec5fbc772535ede8e32bb6f7af85c85e417d05bf8510882cb90efce510ea52f9c7aba755d31ec73832a6a69b248df425b9238f738a06a7ad6058609ace5763ff3e83bc1d25ed73ab768b1d033152dfdbf581bd36c13b6be7aa90cd42c15580ece14242495366abd68c7fe92ec18d73d918ceb7dff1dc748abb7667d470a03126c272854b3abf1a109cc7b5d6b40662a34c7859389192645f652ea28e9a29907891338cb7be39e64e633d5b67fdc1b4b0b7f1ceb18311aef251d0ecb4b5808d5c21fc6c2630e3e94fc5a02ae3344912b2cbea76d693aa21e9060ee63d8825e59dc6184f12ad4f2ed48ddcd0846ef800b537fa705cc06eb542ab6feb9cdf45888d6d7b1c94e6b3ab84110af23d971f92148d1c281736754d5be1952ad08232a9b11a3f733718023fd5b3f4da237eec54a741e4233e04d0e2109f27b0fc2854016d0e450840855b99132c8f35c9a5acf934a5d984b3dc1c2d99695e585cfca3053bc1d8a71af7b9e78e0a7580c50301ef5988d3486c21aa5242188cc924d0cd4eaffaa3fd3e0f55ff3a52a1854a3eda58e8b12ea22cc85ab694741f0f382b69f516ac3fe59af23c6f948245c5add1e65d317fff90cc86767e82b42d2c2a899052f0583880cf9ca3cde22722ca20ddaca9d3a42706c3a22df5ce90cd7c1cac4ebe237e2b31a5ee1fa20b556f76a526946bbe851368177fa57beb4e77dafa4dcfe2905ee84d0ee8ffafba28244d527dc32efce6a69758a7f5a858f28293f3a8ab849b45cecc1daa0838e2b19f695ec3abe07079b673498d9207209ae34e8aec2688d1fb5554ce9bbe98b38d5f93593e67bec184c7fb71f663bf7794a3825cc78ef758fa0b0bd47c2fb540af30e076a4704b3f69e5fc9335536654594ddbf1bc6b9c72720c0dc9c85ae869b36e8450097dd5aa699a929f5a203da0cffe97cbaf6f266101ed62bb2f1eb11750c18e9ca4300f3c59e84ecccc10dd78b150d076b82ae26b8e015621b7d7f3413d2cc792fa74e75864387593e8c333d6e7097e3f4134ce689cf268cdd92cedc57b30679d41b431a2270ba4ebf5663a23de134712980ff0742a1cde92e5c29d57ff5a46fa2b94a8e38aacf19e19795a0234b58538cc332fe2e7b315a79568498f32926accfa1da0483eff9b576f45266b799d207e3a34b53eff581b207936ced7b0d60f541dbabd520c2a297a1048c3efc35bd0d681a4e3618483fe4165d0a43654045d6204c4514b74759bbaf259605f6a0cbb099b74421f57b374591c140f74442adbc2839de69380b644eb011eb7392e3244ecb94b846eca83cd26119f4f9c2a00c309a8ebcdb1b1b8652c19293be95e43199e237a03c8fa32cf85e508e8c698fcda6a6e3a36e5fa25cd46c9a5cefb35ac5474cc4752ba77024ed842e853e913680aad284d3c0c801c9c986f4e22db1d82f641827af62de415aaec0681a5b37b546ca5859917eb6af22375dc573e1dccfbe2397dc77fc62f16a1d39c3a234c573ebb963c118b96a8b49ebdeb9286e1a132233522a6e99fa5321811fb7a9e1577214f39596a3d5decf0f7addb4272f3a0f2af01669d71f48e020c07ac3d8e2dc652ed0fcc1e4d54d964d138ec40b78fccacb955802ebf43f07353191c0ef7908fb4194dbbc5d57fd6bff8ecb170c3b459fdc7fcf3416bf2b993bffee963d61a82b3911784c458c615cd5e2db83a4ffaebdc231b70f3fe2a874657acba9c1e06bfa986919edc899e9382e4c1e7131ce44e8f178bb01841b74b71b730b7d1f14922835643c8ff6696ea265b928ba35d4fda027c8be16e2d928134cd52715ae12ed3360d3674e54bc0e6bcff719eabf179f91ebf8bc1c8b30f1821ac23e7eb2887afd8b67df4bb34c12c0891249b2048ef41c44bb07b7d158aaf9704f5deec51f11c4cc5e6395b39cd32e90350f217817f902087612f461baa94d33d954d39a50cff613c591137e930f8ad318f1b51c7dc523a3563f8c78562eb54f656f0ac2489fdc900c46da01cf0decd88be42606d4a976fe8bcfd8416a588032a0fcc1cdf3721fd872059777d05f2a4856b1f9a2d9bac03fc663ce382173848aef38995272b366c37abdbe2737a27a73bc308964b16cc22b2477ac73ac62ea598e1ef0e06e3bf67ace489f0c6a81bf951a85980f59fefefc41ae02aff09a917a0797c03ddeb838def4c1dfeab7d2962fdf358eae858a549f5d80564ff2f3a4432d8ec68a8d85eecc3e5a61e1cec34d66d92bd8adc2c5338a6ef86b28510a19174437c390d1925d9185ec14e93598786055f0dc2a627610cfdb27254754449a8c418f443572bb17430a438f4d4a428e6c1a249cb8430c2608f94e07147ac4f7831f47a516799115798d26c2d78cfe10bf677213c4f701d5dabbbee62d2e3ac155cde6658bd830e7455221ccddb249555e6e13ef82849e2a0f01ff9b10efcf97e267dfa47caa730c804e92659caed3b01c6d57b4bf4eab69ae755ec2e33a152f58cbae54d3cd99dd1758d7fbadb6ca91a769511caddbf145e8d6f38ed038675e3e7ed94cde5d786ade0ca5c55d77a7233c4f09dfed7cdb620bf5a07b0ce35155abe717e267b62e6d1885d43cf2390a0a42f9ed96c18281f5b649aab99b056e11dd439d01c21d3d1e9a885653997c006151576b705f54135ee59bcc78a62f11a19cfe37e3749ae79a4914bf41fd6e7a59aee27c79c3ad2d66787b659b79c64686614ccc6067f92377852047e496cc9b7102b0a1238114610f8f937ae256005853c8fa63c90ccb8a8f3f4027cc3b0d50b32bf296d1d74b459f30d186932567871fb56cabd128eb63bfc17b24dbb0db2ca980d96da66a19569fe867a76464be7a7bfd80b01fa25ac3e57ddb0edbc59de2859c001188e83c5793e37398a34f7ebbf64712b1d1e11490f70598d55f10fd66de4e11d6c33bf6ef8270d38e41f98959163ed6cbd1c0058e77c41066e87a21a5509521881fba44514d20b9aa630a17f6e606ac9ce54ed1697242c0386021469435933f5f3a5494955c2a66553acfc2911ff89f927031e77da7093c52e85397aa1f20dc1ecc71bb2f7ac0f8181c5107cb78c2ef90e798e2be4c6a0a66b19d370647bc239ea03f4bbd3a2b00740e2e5a58c1d367f2183a5b199dadd7e2911a7a240e66f9fecbb8095174dbcce011ff2a582b64fc6638919d251662fb00b24fd28f5de0adbe5fb0202fd62d63af4eec42a470581f09608d942a24f3b991581ac90020014aba57ed2cf6be335ca53e8eabdcd43dd03bd772a0fcc3b143d5d35878f103bf97641dca858a218b7774bb0149a06a598db924383310f75c74864c729cd19a52c2ecdc44cb197f0f0492c1e70dd748300fbfd04654fb881b922c4a0e417941c4ca047864ed0068577b527dc069227f44871238a4a402d8d4fc3545362e69d7eb308c3a2092a804667f3f120add195b31c72ebbb7cd0d49a14372ebb1c0f26063745c8b8660a3faa051dbd0454f67bde298db790490317e94450c7bb9def8ca7db188267e60a904696144c3641a1f4be6f95721837b493cccb92fd69c5647ed6fd7a4c3b7baed6932791ca645f0b5ad890d7ad63e876c9b331f7ee94cc51c2da4eccd7de49c11b1fd891b40444c00e25ce81555faadc6f842dfc4c014d22a954d4a5746ce5ff9a90c5e204a7fd278e3802a013e383d74af85d2814208a15b752aa09b8adab524b496b6a46cddd8322e55852ba0f585311dca821375a2da23b6f5473ac660957596b2cc2291384667a861dd284752a96757b2bd3c85210fc2185822e4150b2b136c248a4728f3ac66db9ae60535f58d154e761ea58eb64ccbc5f8c43fae007c94e80ffa1c8d0aaffe20c9943ceda6a539afa3a3d80ffdd3930b3dc192d3ed79f4b8de81ba779be6c12b0350a0a55a4438cbc5fee74f0b568c4806c9ee6221488591ed0a9a852ac301bfb9ec332fbed4183971fca3425084bc1f255080ba9ef37e5e1d40b4a586b7200f6883104e109043e99b61f833a77747cf9f7f675f6b0a198705877d945790a0fdef8589a2a47d060b89a0212de8822bafd2c3f63c148114ea4b070aace8494d16870f7b02d5eca144014d7fd09f0673e67f2db6e1ca2a6ae4346af5275e939990822ee4db0752cec045028c9bb95d48a582ab5d4ba07b8658160611f6efa6fc7811ec81fd2a317d81baba9632bfa36cf7b0f3f4cd6035bd0586d85a450a7a2a658b1d5edac5c513fe042a5d276b4e55808fa5518ce414611cad65fd993195564e64ad1ef27e3895eea8f7667ae03f89f4338a29c2ef30d1053ff0735862fe0b9d8a85a0704643560c3f47eaae96cdda4420982a780f875ee635399213b81126b4308578dcabf55cba1d5768443b170e69050cebecba3fda815673d27acf13a607c24a41e509d88265851b7c5d3816533181adc715955f5193bf0fe90e8c03fa82dfd531954905ba05e169e7126c2442ccd5928c13cdf366d50dcf8200daabb9ee451002f20a17eb1a082f810a9e05d4cacac5bebecf3a8bac649965ac83155330652c6a98c206547970ceed5e84a98a49adb0c1d376742bab6b5aa097179a874610e1e53fe9cde4f150a86dd1015a77e0e1d4abd826707beb26c785899c14810fdef1df30821debeb7907b4dfb48d2a7db0a8a626366d52534d6f122629e5db820b57d52ed8e76ae5fdad67708c8ce7151b4515b51fe023e482bb1f182d7fbd07d404a0edbbbcffc34030966528820d04a2ba225189c225626764e0dfae2e5494856bf6caf8b5160295f62c81f37cb1b89ecde3484bda9545277f9c6ccc3482040894f14e364283fc5b269ec9a2416f682bc76265c29680d985ca098af26efa11e5bf8cc9bdb34d14f1b264b23b9dd2d0847205c29fdc05f48aeeaadc72bb0568f118379b6014bbb3b2bebf71a4e9541a299b25421ee9645df975cc2009a4cd367ed5f729e407231061cb7e98bd6bf66339c30ae4b04eb9b084657796c2d0c94b01b0d97eb9b9c47a022dd9de138313b586d04aa4cf2477d972f849c7122c84834227ca441c85d5a44629771bf2ce8e5bebb8985f8b2b62b203d3b4212a7ca51b3f25537da264003b08970c243c851ebbc6905f8258bfdbf1d537c5ff6fb4facf68524c1a2a749bd84e49ce091ae3606206959024677706955b7b6e6a2a8eb07fc1f78ec99458a422516fdbed2481e7223fb6286fc46201d56b0c953e9b45a09b70ac83b4464c00923bd24ddb43275c49ea5caa43fdbf857bc2e2912853225b0ddebcd94d52444ef6723d773155b006acf05a082bf38b1c27b0bb78b2c2c293cec075e62549c043b6b48df0d33e789f2b2f70cf3772e1dbc5e9e51f57810f9d7d3b4cd4e49bf1f671c7a88dc12ee11f31dfe74494735b766257bbf1f69ed8f53ee2b2376adb11bbddc55c76d5f881e741386b627936c45a22462d9eb330d726774d72e469bdb7d36824fb77afd7221021832772764915a89e8c54f52e50b96e38d354e27b162e944d92d77498346e253c1d83bbfcecf69429349ebe4b7e208c6d6e6bf12f76a82f577dc318e294a180bc1c5b62f13dc5e0d9a4589e4c1d667545f9976e418a26d0ec15ac755a3feacd82a4268ab290596ff84cf773b5daaf659622cad78bba496ebb74a4da67673c745d76c908dd69a3a1e70941c1445159f9dfe6361967de0fbca1ae5333740bc7c08221c07280a76c83fa44e91793f164c2a4334ddfc42b791cd8405f9a12b6792ecef386eda56bcea2596832acf121b2391963a7959f9854538fd5ee23809568b0a938bebc236950a0ebd9ef9c2a5e8491fb0eb39a262ca29ddf37bf533c23d1477a4b4b73c63296481ac171a8cb1e550019b6280e1e1fdcd7184b5d0ca06d9846bc73032405069eb51033254437452dbd0512f4b284de6292db75a0e8267291606a3c7883ac719f9e9b1b4ad224b2f84b9c88ab57b5c40b5e0695783295fd692296f5f11fb2b1ebf9ee25e502bcffbf37621144a71c2e6dfec06097e22bf06f17768134f8856f24bc809043fa3b338cf462accf232754e8f699bbf97cca4223cca9612a2542057d8789573fd7c3895e1970310b05ba9c5efb244dba14536d95edbc4c9f647bca058437a7d85098ca5472c71e7f7b63764d82cf48e914d25b5d4f281964f2b96ef69bc0704f0295abdc2c3526518a650fbee176a1d5817a73d144b2d7be984dca57b9f912f13570431eed019c22f43f316662547b78e72f816571197c75d5bd45af31db8fbf6b04ab26155f518eb63d9b3edc4d13c12f8e300881521f20c21ee72a9e2b706fd4fee78d775eea7c0e2b5321278c913f770b32d8fdbf3f368b7cfc2f3110f55202c8373db9338d48b3bd9398b9429498830aa187b59add40e16ea3de6503a8c4fb38e7b4cf75faf897fd7e14978f4698faacdbb431001181a0476c962a0406945c6067e1454427a2ad81c7fa5ee4db5661063dce5d06ce5da00967b22e654a6fc752820baa068f0f1ed7c4c72afb277e4ff6a490ecffa688a35a70cbe183a0929ec8cd4f348e48d145f45c8e3214e4864a4edf92e0c4ffdc06f1bf99d35b5c889499aecaa5117066be00e62a6cf86abc554616bef177e5b05dac1dc12594a8fa6c7f14f4df55b75f575542b8542735fef01eed7a65d2921f8584222f3cf2a1ecd668ca87115e6a9bc055067343fa61a87f0e525c918996bc067b17c34be94cb10cb6ae4a4770f71c55ee54719a1d5ff89d8bdddfce26b12cdf56aa86ebacaae8c7900a3528251ef8a50aaa7ddae0e427bde2ad738a04291b69ef8869e2d6863e0e9aa08c807f667625e01f0d5adb4e0958fd3b22d03143e6abd51f5341430605b14ed840e84a3a1b798226e816df536f122e3b36c6bd116c70fc6338cafc00681e4e1905a56d2a39039e02c8314504f6588cbce1da9a40ce8eb2223ce8ac2a2e09c7df98509e4dbf5ec27f136b06b62a95049d69d9383bf8c0762fe051bc79ad63677c85ec5e05540191b2f5aaae3e8418dc0cf938cbd00682e87d7c4b3395293e4c87f91826752fd8ef6625ca7615c2f107c48111499068bdd166e382f99ff8f0c9b42e6364774a11f7396306903c3323c3d7c850faffa4db5d1bfa2f7f12444d70d1990641f1c037e559f24bfabda62699cb12b9f8f72eb9d6ab5396704b567ec3e85d89bc5b207605c1fb2928989f966b3c1f331a3426d0a1857dce3b02577c0c8b667b2bb3d7ba04cc8f27cc17e80f08f9bb0882929530a0dba491bab9eb7520ead756064b1a53578b82d52e355ce33cf7c51bbd492f268a5086e8dbf9c81401889a2b611239528c3e90874d61c5af30df46e057929a6832f0bd344e46c62a09f0ded84cfd9898e94baac2e14c9cbd1cd3ce6d785926d347a3a6434ae94126032748000eb82488d95bd4abce2a3667c9b89850cf156d35a4487a3f6520951e6f0a3bd9d0fad17feaad6dd903a05e533883cc586f3db778a742c4d7105cf075e917c46e8d6c1d5039655b9ccdcbc4b5da2ccd3ca55e9bc3f95f0ce833bc1d819bc3a95ffd1f986d7333ab865362c9ceeb9a4dd91d5689a2bf60b85f8a7a49a5ff6198319dbb59140a8d47370026475b3baa45d78c5f0061cc188c4c1f6d8b59dc2871829c6776647ccfbc55c3c80e2e79f7e8b0dd3fd1c05a8e7ab0fcf8aff3a1ed265a5085715ad594f508c5ebda885e4ad07662643832b7ff6a906e09f52374ba5983279e8289c8fb47f191d195c8434b974de943adcddffc9c8c32c52a9ac11fc73593a016daeb5554c6567ff4c64a031b5c69cad2dc2d5d6b298bed28d922ff4ccd4d78d861c6e10c9e7c39426c7e8b3ec980478f865975ebe808a9865a954675e67c0ab3a7bb5697844d015f7fd00740dd7d5330cf664d23480186ef6003a70f507d8bb1b8d57bbb59b08c5780d7a92f7c7cac59f8ac6bcbdaf10fcbb3bcb38c71379a1b268da17e72881573f0c3b0e22cf2405cdd2a28147f3771ef6777d3c9b0337737ae155e441ad508269719521fb639cedd353981fa9105736fc670caa65285cc1314745579f7568fd36d24cc1beaddd81abe3d2e2434753b9fd95b1feec7041efe7342f7fad1130fbad4c4fe4d2b00fe1ba71f23038613d5a966e317c7f438bcb24894387ba703c2047b15374f589f42cb027034ea95f30b763355d11a4739bff1aecbf9391b95f3c38e8b9c5fcb982b1433e089472310282a13ed2880048d3b0a9abcc3ae651e603e4a8fa04218d7ee492527732a75765d1523e8be2ba666ef609c54472ccf2e6d9cc3fd2ae90765b3ed20d2b45034aaae698740c91972d7812370272607af0d87b9d96a112bc67472d5f7a084d03d2c7d36b2718401d4932813db389fa16b0ec7db10032e1aea7a4d8941e8624536b7ddfbe2523c9a8a318e4d283f26adb70c13e387753e43357f11655d42032bb43f46a213c5ccfc804e627806836c0975d700d928c8d62c55833bdcda9a5c01a9431e315e7cff696838b82ec547b85fd3dac08b198b08aad26c66b8a78fc3c149140d0d8ccf72d65867f1ac7d2c8a150ef96f8f1f163e331460c4881179418d164e514e3259de8920c2a4eb58a084abc60f1ab8b61726bfac1a0dab3abf6ba56a4e075ad5d9bcce516ba80b080081eb04a135e9fa1550b11bcb0650cb9d818f3d251ea0df0139bba7ba2991c530c17d27a203d4453d0524620ed2f6c51f7617cdfa1f91fa79cc56942189abd86e0b19653a4a97ae281cbd810a527e061e225b5372e60b7086cb78abfb48d9523d10196b7be226f4f7896451f69987ebe1474f055d4698b2c09aeb2b7ec692cd339fdfe885706e9cc4091224ffb218fdf9adb9a4907ad24a9cf67c98663045384f92726278c9a26c9897934bf74e80401475c58d6a095eaa1fb773cfc3f002b877fea65728476261a005bc3327bf9c90e5ce79ea5d343bc216a8832c2bf7eb4845a099ec1eaa50f67d85ca092cc791f63539426839340f3908d05b7d6ed312c03da087316a381feb09ca781a73bb5b131194246bd19eae28a708f05921fd53f88f847bfa40ffb7d828970025e88e2d8e86825e60b8ab8a8653c85f4450830cd81566302fb035bc2c46f745607ba1ecea939a957538991b832a6309e4f46a62733338a749a48265970669ddaf3af512b0d235f31a4deb71d383c9de50df7ce2fa1e09abf46f80c7c275a63fb56fe34f207f24a4b8e8f78db4511f911f12370b655b0f6615ee350cc17881b63f706238a79da088545a66e93a3795beb607efabc846485980aaafdd472abbecacddb3edeacfddb757c8835d313d50855b3ea2d9697e13853d11159ef265f150828fb1fb661683bdeae99723b4792320f454c813498ee3f02f83892b40cc735848541eaf41c6deab4683986bdf951dee94698b7853af4c76c5c411e4ec4a53a7459a7cca498838cbdf60597469266be562a6d3e698c868e2fadcc3810c3e306af47b67c4365c0dc5b24884f78db7cb4c3b5a47af392ab895157d3744af6edf632136d3d38195d7505dd28a640211313ff4f3d41e787f10dc379d06204da2470c5266d324f79c867d0861045b634630e149811b4b139596a559e49f204cb288081b3304d693b8f31b5eb4c555ae21674bbf83d4ab693efebf95a9d35c4885f3de983a9a56c8bee407040437a0e99776008e45dae1e8b3b03401ce8f7661676267dd0874c921548f4fe3c12d4307191f4ca80a24e7cf6246ffef38f1542cb98a6e1b1423a8f1e21ac22a6d8ffda37a0997d5889341f9bca877efba97eb4a038e6176ea558d325e3b4fa2dcdd5d2cf07f9e766ad0040d62b350c9c61099f01110ae18a9c4a1d8ded5471f757c720788bac022c852478cd7989f00e202c33ed65ff58aa07f3cc45dc432891f9d482ecde48c52b24a18c70c9936fab54700bc6208a1435416885cd1161fa9adca5d778be695e6164ccc7924e124f0959d987787684dab6540228e72660e8de1113983048fa6b2d33b8747bd4ccf9c4bddfdf1f262f83a275d53f7332bc4f6d8e37c67a0cd653b960502da974071224c401286cf89d8cac66cc23beee66c8855ccd1d5aea479c3706a53ad90d4914462df24d51850a0f0db86c594007efe4b1080fbf7dd1a72acb60ad2203974763fc08761c6af2098125f0f0835ceef65a19172a5418d20fb4d4064ca4c1286386ea595d3a656ddb59db73279b22ea84fb57fb1fa8567e57337c17fd5bd42027910b16469b715e7c974dbcbd6ba06aaa1525991e4686f40c019d5bf0fbca3f905906fac8f26c5738a799928b739c2ae44ace20852ea414c9d387e16a79518b0b921d76bab4a52cc9ad8fc9474997ef607c57cd2df50c673a43597be3994938828439c8974362d08344dc17a9ad0fc1e75c4d86f5a27dc75cac2f50681ff10811dd2c01c48d0bb70deed3e9eab681d5b556a607331d49f4f4b635836cd572eb20550b0d42f59b223c5dfd2e6c010f972cf83088de2ec96818c9fd3dc12b01e0a5aae68df9d984c877fa841d115a23509148c9e51692ea3e20eb45921a9a09d320637578b34ff7d40d3dc981f7685b12d5ba2e4a78cbe2d3a97513a434ad3aaacf6d39fd6a3f262964e840a9032c7fc44d1a38911af69b50909b22632cda1291d7c8a5b73cce2d434215d4a25b8f6ecf69502b9101a56f587234393cb7d584c27a8a5c50cfe6c6a10e6029c4bf54257c5916860336974cec13953ddceca1fb8c6641f59f4afebbcee2c2e94e697f19a7144da2fc903a5c6a87661043e758157451add1d5d2b686b747b490ea134a35b651351cfa1eb07ea57cf7bb6483a6bc07931993a29ffafb4ed8ce525fd2e7e00cf4222b740075451ddd122b330d48a8be16d37f395b44f29ed3d4a204fa6c08a9c491d7e90aba2537cdfd09370788730ac5dc808b2f2b158078c6c036424996942091b653782c2f17a3ae9e6d84ce490248f64b9478bdcd95fc1625d8b4705542465017afbe238e202760dc7d32280edb7b2e28672b010720981a1602c93da8aba489de24a58c014bb84412c8f22338c4cbdd01894562e6dd96019737b4aad195f5e27af6441e7e58835275763384c826431f78f62a66378598174461ba8496560877ba01b8e8992a0e85bb5e0cfbe0b8a1c489adcb1a8a5df113c00669d91e9f61db4ba043a1f63a3a424e40d728685f607eab893080581825e82f051c05b47cb8302ebdb6132eb8b8f40679dd67cd37694ef96b47acd33e10aecd126651560049e5e40c755d868674eb4b822676c942c4200ab41d4237a2bf05372c2ebc29a342fe58a08a10066549f768951f8f7b080e25acdbc76ef519b147f901227410c1e498e26e7ba769f3850770a601880b086e2365ed4e5e92ec5b8ef4690d8285eb734afe485b366a8baf20cc6d240d41301d4b64af3a657131a6005c0700e13929d1327338e74c50cc3644b5f047c400a94b46607fb3421de629a06c41ea6a7354a69fda3c5e3f8b89178267b1c24c7b032e72352c5ffd7a904697f718fee513c927b175d1bd12203fc3de98f2bc088e9459d026110297b0030e339dc912136be4a59846d9cebbb66e2be2b119f7165175e3d5c052fbff0c111581ec7c6184e8a4ca4d4e1caf11f7441b7303735e6fa5199a235afa9ae7cdc335d867a97380388e0e3a6970c959f17a2c47fd84b709cb83c12749c265531759319d00db3cffd276c565b6193b7a2c261f3ac211ca5d45c037f94476d43f52a7359a35597f3fb60f807f4c243f083b79261ce1c5c158185c107d3088c04c57019d89eda9a0ee3c137974a10996999198e24b6875902f68882a50e3e1c32af553a32034e4281de0cfb292b8f709974206c70d303181847b8d78e4fbc942c2d356e770af8b3357b388912e6bca6a466556a930ad32adf825ab29eb2da03d248be84478df2fb505562a61b34b6a9fe404f771ed9a398207063fb96dea60175fb97c74a98470c8982f6529203b58474595ddce4f048bec6c495c927e501e9eb8e82c78a160957b8da7a9dd55efd0868db50efba466b39143dad72083c08ad6c21c3a3a29f693f7b8a85d86ce7d6144d308a18c3cf7c723ec26c28949cfeaf562df8b3bd515010a1fbe4c8dcb52bf78b2f0ae2dd026d37c4fc6c81992b706cd772012372002f72b42cb4f0917f7661efbc05104f3a10d39283a6fa02d93c8c51616e2138ac45b35db396aad448a0d9ac6e0be3f5d4a822c84247ff43da2ab4d39891a55d5446818549e264d3fe79cd39b01e1a8045cbb704a51d322d462dc04e9268b4e9c5706d9b14dfe3361c89b6555a57e8ffae1f39bb91529b79152d7694814f691957d0896e068043514e784c09823c812c005cb984169c9098b30ea24ca1e2b0927e5712c50b6b6bf40cf431c8dc752b666e124d95bdcad2a15ea74da391b00bcf319e41da77e5200bbfe362356e61292cf8ee6bd071a792e0397a6e864be0518acbb91fac586c1d7191920d37f1dd08b542e985e5b65204cd7091e8b14c4465d3310076b3a82a064c65baf3090c0021151206ebaf2dfbe9840583bd456a22479eb1d90db5c09669d94ef6d4d6fe624d3b13f31384e1d567237407636eece1c91883609a173d3cfbbb76566699073b4a583a400acfdcbde24e422fefecc33abbf2382905d05270db12a3837c0b34b5d65912d5280605ac11693fc8425c3731c6f63b7641eb465d9a19b1a21a0aa233931d9212c32d37018db4226fbf283dddb0615ef3d28c53e02a42893b58a23c8acec793e2b8a577e4dffb68cb9a9146599261515263b38917d6233d9b4995324c8f18c8c04462a91ef35c4599ee6c4c748a38f6aec9fb165ba60f505ea52d1d10d28f512242aae747858e01e3a22af55542437c84c641db0d90e4cde3489ad66292979cc312c931bbdda7e4cfbcb2d0f27481382d221585019eefbe6facac8bb31381614814e263cb89fc2ddc89241bf54b02575613e339ee16981358eef77777c6327103518274ddb64935c5310e431af2383bfce610bb7874a1c33da5f4e438dd78978623c64355283e46adc2404a9009ba95c68b69be5e40ad173bf6ff2ee6ad4c8b4cde772abb67a794dc4cc8f4ca54a401b44cfd150e4295790aa37db011b2787afc59c9dd6046530e514076b08293838bb44be524793050f641d427a8c4d79805863ad1aa9ad1e460dd172c47289a4ea52d1e94d39204085a7b7b90fbac9fb75e6508fa60de60025647b28b5132afdce63cfeb27e504352adc59d5554a8ab27b7aa3caf3e349e8eb335e07de14e020f99e1f870ff9081cad80860148fe80a103d4ba76c1ca28e9585a3746118046fab6a84aad53d9d9596ea9032cb3120a8bb921e06e1532dd62940c0e1d0b2d03dd7eb06d124379bf24c199b4a63e7aafab8535addc4b233f1ca88fedf068d6cbb614a9de5d261412090931e73038e26710430c6418fd33f402ceb1f0924699aa075439caba8d164fc728d36cea309844871b388cae7c533878ab02c95b8e955411df645c074cbc1771e865ec4fda77de18c3f1972e7d4ec51c98dc29ab4e98a170db29dacfd0f5314baee9c48e1447973a2a24f0fddff45e40bfdcefbe545c11818c06993736a18d1832c80306a88950376114fd277d3d521be2e7d876f1511c6ee1e74ef372a5a90a23c26a73b0ea4114e59d5b6a68543295def9ef632f4cbf4a7bedaa5dfcea4777bee6b4a0b7b9bc70c15f4e5cf5d7163391bcfcbbc70205c1b37300c46014d91b7ccb5545476c29619aa29eda7490ed266ef17d547a1f51e4a8d692714ab22cfe94e1be4000caa0fa5ac24a943bf39c71cdd4955be433dff66ff90a55f934994b696c7948043df6d05b601aead1f268262b26fba135175cb57eef9e02526523e667d26c859e1fca66af7d68faddfb995b4e574d833f8c9496f5192679df8aa221e851ab4f84a7137f188de97168954c549ba3842dce976be3b1cba946e1d4d122cb8fada50ea87b47a8f2fa74489bb3bb351dece1f1d3d436ef5b53cec942fc0117f2da3c209e450b3229d5c560de03b6fa125a8ac76ce5508e308a234603b10aca3dc08dc58b9070775363834336aafab6512b28d636b290733085455290afab3508d83fe606b12e6eb94dc642c0d6bd30ceac6ced2fb3c01783e2671adf8e0a8e8a6e607259f9ad99a95c45c6a51f52faac7cf5e4af40e9d00bfd9513fc54d0ca992bbdab3ffefa5311c69f0d323d9651744a9a65de7908db92264ad0804a4962f9dbc8b99a734141260a60da93933566101feb6c6410d3ecb620b13d1cc474c091f1855d3efe853625a62ea717ffb0ce4e878895f13a68f193a24caa54f7611021356818a47bc2d7430fc59bc1cafa5a198b71f2468d38ed78c19d0e5ab0229b7826a0becfab9809b39b59c1b27be1497a6b85add4bf00377aad1a118d7dc88a4374ce488f0308d48a07e423745ceadfadcdd4c8b3f7040dbb5fabb846e1f4461fc738743ca50f5ed2d4f80ce0cdd8c4cf83dd48429437b2bffca89af53e7e9c84f9c9b39f534c4612a8ce4bc0921ba036534ca9b76b8e85a19cb655b503edd664353a2415fc482dabd89e149609fe30e6dd26dec5e91a56f5904f08d6c265c62011fa03a1cf10ba3918962deb650745c52892eee15597210d909c5668973234c97f4930c379255088138a38bba57236015ea8e74297e9fd0000f4abaab905772cf5594d0cddfcd902d709a1c7459690a9a890e722bc75aa7bd7add491b0711e589c569c223f35dfb23f4792c9ce1caccde5f0265cbf40dab8e16354e579480d2be71590f4e9867bd2650518614c898985bc24167cb986ba0071334dc62eb2e007bed66262564f5a007f276be620012cc80b13b91cba85eeb2d67f3e1e30f857ec78fe3ccb6c113923b154a8b31ca84bcada010e99a9863bd831a57ab5efa0073423af80f3aa40ddf8bc393aca0a8f37eabb03e1b7e343cd05199c5ca6dfa7a667c3d7b6fd41389cbb2d0a03b1d5a192116b14e43f8b26b4c92a3ba51de3b07288668286191049cbebb710a66bc14d311d748a92c2bbceea929ada6ea57392b2e876d40aab33cb8284f0c9def1a812769a0ed33b23a336b237c3a623e2975c54903a415b6ba1d6672ee6594ac509fdbfe7428b147df5457496de05ef60bd65331e00f11b6b80c21d31c7d873d33ba0861d16f08a4f560d1a0665dfd540a783b8b0c3816cfbb1dc6c33539619b7adbe669eae4621027d3f39d3a57cb3639e2a8c03a3a728673832a851b08901874066eb047ba81ede82b38f94f483ad699ee1e9a80c70aca291f8f6d328d3d517fc9702d611221aee9880d8943b0d12327d204f9ce0ecd826cc9d46012f05bc76f6b0032c1ff4bd484e1fd3e6d1d2192d527115dff6f88a7862bf90c81af61864e21abc9198ef3dee241e93a50538f2d4c939e230eff8be4a2c465942511855e4398bdb40b096ad6bd6914175bb2f5d5a0e1fd1a73a994a569d31945b83f3c87a388f3bc0de6286805847f314a69a11be5092b747220b2bbcbe2aa22ab90f2ed72e8b88e2abb3f664582b3ae6bc38407136d25196d4ffddcc12f537f0da7247e7f303ff272c403412d5a04a3a9453093eb349bc7532b09058c608e2461d6ba6112fd4c2f3a09739f810a28ea367f9a173481ca52ac5e087906e26587b9edb1e2c6cefe05160bf62559a120f93700d5aead0296678da3df47b34a4e87dd18810c733acc6f06c96638d54ee1fb5e54e897b01ce406692abf23316c0bc0d8bb56ac750e0e0694e681223248bf461a94f8385853213768db1d2a4c1a1c7c17bdd500ee32ea563185d549da86657fd1794635144020952ae88f8d35412c1c4054a42df44829d26e6ba256a5613ee822c2334e1b2398a7319373f1449c98909d13890169a24788aaf884aa128816638331493af2250aedfc635bf14c69362e85875f0298888983cf19a593ccf39682bbbed82388f035b66b9e11c7ada176fd5c851bb8cccd36fe6a1f11b604b540919816d9d358b5278092fed1b15922b5e192085d46f5e6ae8a153ddb994dc69ebd21ba28b32c9175d26e0bdb5628868b26efc43a3c3de369ed4ced70a918a2208d6c982416624a1fbd3980c851e223b092e07901e84dbdf43841b7861c01ff9ec2620c6bed6f8eb16132feadff9c674c2d839546e9ae4aa7a18a540bf43d4058db08b5787c08bdf2de4871048013c60933db4095c30b1d390d02f0400024e872794a26a47adcb9808d43ae6a949cffb786fdfb8ee9d67346adc9d773e65c301c3377b2d21190199b87b1e1e9f173b68ae98f65344bf75071d39f086075868e486c946ef94555ca795399278e425d321f929ad15b07e50d8dd13f68f52528d4bc18a8003898b21f14b63ca157381c65939d22ed597c5b2937ace086e547e9c72d017dfc33f2bd30ffcf542026950d04a2b58908b9da775891ff3d540e6f66d69c2a779cf79ac2658a0f71d85982d9557a6769ab1d0f51aac784c4c9cf486562b6a064a5c79e0f5e2b0a8963da996a350f24e54358301d3b9b1cf6b29bbf70883c11939afdaac6b0ae8a8afb305b1f5169b956625eb24ed92ae2f28bddc76c6b16192b93fdfff58614527549d8dcb9a8cdf4df08451f26300f8cf3d49c6e25b4da90c90bd8c73532fe6ce44cb0546bb4356e7eac60aae701d61f1dabb138c31a4135519fe177c992557844382a3c93a8133449b4cf6289e4ef883195f74bf1a819a1379e2790cb13b5880cc66a9c8a8dd8ce08a9144710b9304f1ce2f07620c3bac8538708052ce8d162b81511552a891a77e46f1901d39749dca23c24686d0f6a87a207cbc52d6e26c0b91db31e9c082aa48c18a360b101b55c7f12ed5bafd9bd55b6c9ac793660de5604d3cc52620a2ead5a4a30b4545e385aa3a4afc36466eda7310ab748b8a26fe89a8c3b91e3ed1130f53c7d6075998f2a7308e7642e370b37d8fa6f62329b916424e532ef88ceff5f828615b6d5618417b714b8950ddefd9a7174e4813a829bc91038b60e896d69ec5e17f2c95392690711088acc194ac0693be5acefe7ea5c1516be9ae361a35b0617d19119a82e5e370213ccde3cf6430dce430dba8f59a5033cb23f1581673bf9c2eec4731a0dd6586ad74dff6610b5bcdb4e3ea89d3de59dd4bba84d0235b0a563aa5180f7628bd54c299937c78308ea388cf5fec5d95d39064e352ba6f1edaa460ac6a342d95fc0c621c33fa8fe3d21b00e59c73d8d7b729cc2cf6b25ade85205d4130b50f924326a5d5af505fcd1cb0c8f49d7af0163473ba4e20e96bdeeaf4b361ea5cd6395789f7306f82916ee0435e83ef658e4dcdaac306eaad4824a653405d42c9ba0407adfc69bb277f225dbded4c88431411dd88f1bcf60e89fd83737eef2349e1cdba5714e8d2e3da9336d563868c88287f6f2ecd54020957abf1ebd08f6495403847089a24b64cd906623290d058519465e04af52575c962ee95792ed796d437d7c9dd2fb70d3c8e15459395fe23d0c8a8a611c8c7dc25b202e51d73e8c795f99c0fec6ee20da92545f0406c00d480813d01f6c2de04c8a81213b793c6e1fc654ddb761847fc7064e06f942fc2d0083bd3a4c1fa0813041190843b0f80f46a9ae27559df89a70b0516ec35066f5e5adfdb9f8d25ebd5a9bd89f56a1c259a1f8397503dc0351f167c5da3965344476e176e96a4d62fe6c871689c9671ff6184d94827ced1d49c95ed53cc5330d2da26540a19edf06c7aa2fbb2ad2bf05c75c8fdb0da0298f645da46e103e85c98ed8ab5ed27214d3364e07e3c61cb9f31e6c6d509c2aef8d2b326a93ab2f6762a016e1d7ea1c5ca64bd6b2764db4374b87a5bf6f4074ce33609f04c395e30be4b3c18d217a7d77648dec5888a1edec2deb487ddf28d07a3b218d4888913f090d17576e91abb5f237b067d9abb4bf99c658e7b91be362b5606d859de81e7cabaf6f7c04ce8cdb05e6693abf9fbb0fa8eca6c73900f3b27f7c4b6d6f6943e1fd1646352621aa499d66a1389f1c869ae03a33438f495daec9257a4898ab495d1f8420cee8f1ccef6281a2ce15c6831c1c358993bab8077fd89dbc4c117b4b9e44700f80b372a7a4ee38f527561516724dbc62f8d7df7ee505fd1ab537b17ead42958d06780f3b864695d672a1edcd30a99888cd680d37b27121c6d58560c92ffa83770a58e2f776cc7e003f1b4f9010d283b173eb1582709d00a2c995455535a0c667cb60832d9159d706aafc440ac9a4cd1d91ae23a98e8e780ec77827d86de9c02247889c85f0ab5208ad575083765bc8593ffcaa47e8c72ca9f2d4c399cd47e06b3cdbe31f5e3db72d673983642bd5844a292a4f83a07a10152da6032540d86194292889568e174b07a64b3730f74048d1e06d34a98de172b1e88da3c23303e59caf3c3cecd16ac8388d6acbbd2a26875b63ee827128c840034aed69675b62984f5dce1e737170d0b0f027fb9d0d635a013ae0252624e5a64cce11b64c0993d321b93ba1de6c84ce5b1e76b81e69bc65112206f8ee7e8fd0a7a626ecbd31ee6dbd073b4dda0d1c1e851ba417bcd930c2054230f8568ac0cf54f6c9bc88207e0022f27697c9706aa940c6de90f16ec9aa9a269dbd9dcfa48e179a9034922c12b444cc72a8cfc4e670a6b3782615e333a101e5d1f7513a33496f4ff2eeff426313951ced75bf946001ec53780f29107580303d213a335bdbf098ce74ebd4de44fbb50815ba32b12be186f1641daf5e742b6ecc81e1026a776a4619a251c25c3457aae2b5845d1141af43c3b3b5403dd609dd9ab517d17f2d0a5155c877752191aa4a633a26df8e2a1702b724802c3913f4f77a6918f85d5de4c0f996d26b775fe1ee89964257dd2713813215fc7130f769ffd5156dc9db4d9d5c0255ab20c9a691aebcb6d59a68a34ab495267366f25914a5effa372fb2fed89526948d98134f4447248d835f128aaeb954e2bc919d9075361551b844a3624352f5f5a40ec0cecbf437174158896662f94ad6455f33d6c188c8d4d2cf6321ca64026fdb0341f1b274f17a056aa0f339917b46034875a09704357421a5f03c82d2b8b60c36f98723de825bc35eb3167db37d73b3d9e3d3adf426ff3d279b0de8507ba5182e4d2b2138f0448043e1574aeaaa190291c2460e895130a04fe89791b1a9ab1c9638817a8b177224845b67011fa6ae380eb0e09338a17d4085d158aea2d4f3fcac7dcdb323a98ab2daf13b3917d5f06614135720340aadbdcc01c24b04e15176619aa1478584ad35eaf3b1d3316d5d84e7d19923838b9777d56131e1279e185ed722815876bfab57ee85a3d3586a95eb55243c6298e5b1fb6451bc3403f046913f0df0a23de83f6d727283ad79c4f70337e8e95b811afb13eaaf507935feb0b951fa484a331848a098e7bfd33765945e34c03a9c7c34b3bd585020341a44a3f8d45685181645c62beed75663101a257de1f4eb86d65817f9ffbb8ef19e28e92f341abb9d57c02ffa815332411934b457bcfb1a711f1bb8a116f0e7e542d5870d5f9db3bd8346401be71068aca83297450d588a568752d8784dad081f0604e8b9cf9c57b9278d92fd89749ab9694b233f8a2d0a45d3508a2ad327d134648cad24e6ea358ddc998e1ada96d85fb5a480078a0f12109cdbef2b9e38ab2b1f8ba72cb2a9463ceaabc5091bada842b3a1971cc97bb1dfbc4bfde17b0133186c742f56d51a9662a0148ca19eb78fa65601098397526083dd3f3882a6edb1ce7e162c383e55d15857cbba1b501084ac66bbab68ef6b1a9ceedc4354a19cf23c59381cd1d42532abcec4d596e71b3efe575845d6dafe3a90dd95d8e8463f3d5b0fbac1b1d41409afedce46cc4454e4f645dc9983738ec40fd1caa545d3c5d25c281ba00101f14e1fdfe710bef22fea76ad144a3cc3ccde53756da1d3b3eafa80fe47bf15a134274b47ff912adeb514d2604201c818b7d7a84fb795c052ade8068e8e83dba08df6ba746e932f125014878c4d979e4ca41321038d7badf1ee01419c6aa21a81d7171d2aef10dbe1e35d787bfa49908db001c5b5efa9c7b20d58ba9fb31981775406167c5df3e34740ba52b265c85ebc0a13887b4f43909bf145448a2952a6d11b89b09063beedddf0aedd1c0056bcb3a750c8a1399cac42f19586c471c929a7828860f28ec1a63eb5d6e7c70b77c3ba0f6b12b3e2dcf1bab3c572a6deb2bc77c8de46f405782e4a771fab74de495b5e2b84a06b5f5a2e602228943829425676ea9ea384de84a0964a1f49d9d621ecf2a99d7bc57290911fa28cd4dc1ae90a479315bf3eb2775199f6811cfc9a6862d52a7ec1f0e29711d66d08c5caf6605ed7ee9c65d6c9c90ee9afa1c7fae6b33fa1bfdc7488e6361f23181c8edc9729a8e62fb7a6668ae52901f72d3c0f2c8cee55b211eef76b8aadca9e84317a5ed97cf1f51f72bbfcf19b8c073cf09e85f6c142110620371a607dec981a5f5a524a383265533ab12a996d440faf49e815ed041d95b96c5a11cac3d8c2584383bf94e5efe17f0c4c7be0db428b98c28123296ec31dedcbe5faf5c5d89fb59ab6893336e9c8b9b7f883513c1e5d0c7a4233ea3ce9b9c3105469fd641af70ab8d6d22065bec37da13b6446608f2b629bd5ed03ca2cfd052aa9c055e43dd5cc1eaff8942a91d77936b8d86bce82a1608b56b117c1a9829b3ad4bd70574528ad0e10520487ddf5611751ea92e24e1132aba8a85ea5faa258ca5bf69ab5ebd5ded45383d442477f4c852e1766aedd16e545303e5604cf27daa50ca68e9b5b15b44c9bd42c7dac709a88ad7ac3d9315e5f828caccc680c9cccce0efb4bb2f4ed3a9a2ecf596af549bc30b93d4956de52960c8adf452e7b5578f390c06088d83e118d7c55f2c7c13bf4f60b1eb28031622fa67dab9c330e5ef1925e4508ba53e13da440a3fa59fc98da45327f495d0bcd27d118984878a6f87dbd438ac67dbdf11bf0dac1b0685cdf65e54cd3b66d907a338adbf69f8325837b4315bc634b699e2825990db573a1ca561324d1045918d5434caae081601e3d59b806c2aebda1cc92c9102b40ad6be25b627787784fc09172af43b8f6647a8819605f4373baf54cd37c854e4d9251d55f6bec947a61ac3dfe248a0e8ef72903b5cf4ec91526ada2b04a25795b921f9a30bbbe0e2346ffa9476fc7b8cf75c947a2726625d83deff03a96948d2c8fe9d8bc1c2558bdd6cc07e7444a8bb9e3c4389a67574628d5dea3e459043629b71664726a52555fbaffbe53e9735a0900f8a25c6a3d68e638819b15f9a2a7077378370e9182a1ef1dfd1be1206402527bc8508c4389a6f12d3b21199e287cfcd13ed02736773db13445dedf10524dcea0780eec1de97c502cd1a8c96c98731a242447624f53c638b13d0535552c15487ecf888945f4b983181aa21c3552953ab3b0c5c6fdaa7fc889ee510eb9f0ad22e55e36931b11a59ed0849e0449bbb2de9904ffc75129010d1bd867639d7d2c755130ab6c541218eec05e143a09f124198a03dffbad2f8682679f01d32ba66ba6534306c54f651f3970a34d1a0ec20b0f3fc82a5ca0476e4fcc861ae9e8ac9464397ae9e0b8b6a3b3f05ee17f3c7810c1836a4147a93ac61473bfbfa38e1006aa72cd9d8e79f0c0d36aa858e59a06a990e252c3e6bc62f6e1527573c67bd5518c910154bf87dfbaf0329873fe6452c101bfc9d9f7ddb1a5d2a9483cc9c0f5d69db63a96189d1d4553dc21837c001acfb5a1054ebaca6b897c984ac15b548a495d4aadd67224838b1f9502214ef663afdd64a949d865bb80e4724b58f2eabc4f29e36dddf50ac74417433bd01efef8715bba7190ead92a358a363422f5962ca58b1bfb996e3657ac449fd9b01b825a86acf79488758afaf2bf59d9d9136cad61d20100bd81f4d9d724644072ced881fb1fb30fe6b100a86ee9211ad669b4453c3a3a3c4a93fd28eb64019b164ec9dc9c31a2ef3a19b45e5f915ca3880bf79758a3a0dcd6093a2be1c7cbc56348cb2122554cb07acbc072db1567bcabcc8dbe09dd5e9ab66bb2240291def3f38ef906cfc726ef8187b15d5203eee8c919370ad1131da67ac3271eb340db67a26217a5c78cc413b99f3aee381d662ab17e1f24c024f8269c7986cce8546edbcb05d9d42597d992b05360a3be8a705a4d4281eea13ee8f1fa4d249fcc6534e31ce4a5c4f1bdf1972bbff6c9ee8a362b2911a1a5182637ccbc8f878cdd4661ad53765d15703c6116f000f5626ed919ff04e727a2a1deddac0005322804cf57cb087aa3503b11519add5eb8da6cf4d003df3461512f4e88c78522f984107c8f17e8349a5d14b2e0f1ed7121688929799d0d71c360db180635a476d2b7b9a14f1b97a80f6eebc0c805e262c0efbf18f4b51e3ddec0932a732fe93f27e708dedafacdb3b3e6e35e55437ea51a8628c81558be9a7c9059a3622d7d83ba25a293fd08cd502d75a1bbb115f4893caf8763d442152acd11836fe311ab03cbae40a4478e0ad4f6e00766430bc986da5859675ef5504321ab982831e2882c99f1ee5ac3b5b22f53e3e7bca408dc5c190d76982cfcc8360d0d8c6a932bf900b7f124e9a32c0284599c61243637a26de01e05ec173f3795e268fa6d9a73628a2704aecc3ea365f6928a1291f9fbafe01a276760401889c544f39d689b5068f9534e242e98a3d86119ab74114e6447a1e0eed8108811b748a6d373a201f283c6231faa5b5f9eabff9f761de5705307311557f2aac8ac9d56e92f2787d234a758a696ec3c069bd012f44ecde96585aad35aef1b1da2605b3e45e1047af4cfdd4a2718ac8429edef44e3c4b3353c3db39d09e42047f8f13acb8caf5c0b83b63477ecb4e9746862cebe7e9ef3c42fb2c1006917ca6a9e1af9dd01f8587afc5d57f74840126a47134591a91a6cc6c4168c85288acec1a91b8f4e82780d66c8d54c714c19ca6ffa7038758981b5435164861898a5aba8ab2e3414054b93eea4468918d23e2ee7a0b0860d5e17fd6d35b61a1faf3311a6ff3513a16dbf05440c45d1f049543783653c14c5a489a447260d5114649e5fb6cc752ca2b20b2d5edb6bd150701f3a9dbd5e07a285a77e2a17fedff77b37de870f9fe38710e79f8e02f4b06f86c2bd73b5d325f27412a9b6a235d3c822877e1eb8c6740282062464bb450dc509e3021732b7d7c5bbe5ad0c7749f45766ee8d628ebca65dd4afc3bb25abb81e5cc30a36a12cf6b36cde9ca752d53925b7f3b45b07a8c33487de9995ec5e2672209b12c9190f2861172d34df14d535e6651b618a1a705f4ffbed508c748de034ed42bf3d91966182224c7447262a567242b60a51812a36bfa138ebd5cf8a61da7ab5831fd080f4cbacb020922b17901206d96e01bf9f0574ee29200258617e031386d4c4e37bef426967da18ad7ea3a3ae579f5213a920ea2a2dc62d2bd5926b05cbf95b4f7c72749ad7ed94a83e3c04675ea15d1399a1e69cfb15d99ba883a350f4f0d2e389b3cd1adc7b3751dae635700f17fca191cc2e09f092cb84363745c855c7c57386a849d045718e18011f4b59df2f0d429be28981f65a74029e502bdf7c2fc680f58133daabf31d1dfe8ce66322f9f7af8e54e5d94e40937ca403ca754e7b5930d81da5037d9bc9c866b3d92c7a6b3c50e1cb1a77bc4f1c1c48add941c8490381c2a8ceead5edce5cc02fdf38d4220f1ef07a42cec68d385871df0e7a06735c257c6657132f17b9420c8be6ac9584ecbd37915bca2453920196072207cc07140b94f5fd3b5037d062fb552b7f2cd644a1546f08edb56e3064fb552b1653e8287ab51fb20ec7d3938a9e501a455db2075e484cd02744a42659e4200e82817ce0cc3ce2237aa4653ef9b1fdda69fac4e168b20fcfd1d1d17322f721fb89ff56a8f7858d48d7e301834023f37b26d7936324c535c580f9f16d98313ea91a89d5c8ac3d3993ca2c28ac5da5ad5cb5893a0a9ce97f393244e8287008d47ee12c3487a679958ddebff7ecf619b1910c0ee8ed28515a8a562487d861a37282c4f67f371f4322b66f084f0b1d24b2b4cf7c359cd482a2e0bdb1ed7707ce0e9b20209f1a04f9fc0021b1e4081217b22aa04c502eddf59697a3280bea4141bc1cfd2fe7e538504e572102bd0de4400c7017b222fb892f241901bdd34a2c68fb04cda0eb2ab01d740417244a1a0073b808ce0c81f6b78f63d1bd2cb0ba23a4d87e1c3b1e11330e2ba0ddaced844bbea7271d662704bd13d6f880cf0a54dec3997e1f5de339efc5e7ef410066c14717e48f00b2fd2b1cccdb0f55773dc3fe8f5aa1545e500ad8812ab2fdaa15efe06f511b56774e604526f179fb715269ddf5bc2cdb94b5c389e0c50824ee03db4d73bd8993a1264750a4482fdaf1fefcdf77d75a5014e8f5264d604d9be0cdf407310403db2d44932ff6d297838de0cc0ff4cea939134bac375962fbb9c9500ff11227bc845ffb55c7da6133df0bfd324c509926d0db422dd44465fbfaa1f77442f5903bc1da71c3a48bedc7a91233cacc7ce3a35fe8373541617d37cc1dff0d059a06be0d5b9ca679416ca890b5047a59ac97e101bd28942a26795e984499efc3d7755dd705e1c52c351ece48bfd0be8141af431564f2dfea7a904d01dd9e406f0319d5cb947650031d6924b63f2b25815efa7290f2211b1979bd7c74f4eae52d9772974babd8db40b619007364854570c6a404bdfee33f0ee4415c544524f9e173e2fb0b2ff4471ed06e1f1f9f143c0c9a466503274c6b08d1abd98c7db658cda2382e94bf4b810f90cf0ff561a07ea1ffb1404fb5852428c7042a63047ad9a79d64454eef4ecbcf4b918b95c74817caf3f85d0b3d2d09b2fd456274875aacd7cb3e8f2b62842752815ed6e2e5ce9af7995a171b649cf356b7836cffe604aa3150ff3410031dd14840bb835a6812b1c4f66b27f83f7c8eb3709c7cc8f043efb40dc316433df3a2502254952fea8c7ae65918b638ea9967af4d375c821fd19d5109db8f4364fb7d54415948bf8d01fe71485b5214ca6ac0cbe9b4259b11e8fb37c335c5ffdeea50c4c22cab218e3ce1ffe17364f417fa4b3fd014fc6771d4553bcc16ac235e06e86b09a1c4b684aab0f4877de017a04b8ee52f6a9a0c260623a4f497a679569a1cb1cfc6c0248ca6795698788901892a9a261503125d7c54c7911551d2dfd596f9465234290eb9e98ed3f4717afb7e1caca97dc6a56963b2875f8a711b13838699e8e9f65898cfa026833bfddb6020840fe459585332bf035fcab8c77097da983f7132dcf31317e3df167b6c8c41a1e460e9e340182ac542d7782a43a1cf6524a13b22276d6e9ea5af695886b1233066c6321309c31e23c166228be19af4faf724fcc8e14067fb362e8aa88da63184ecdc90190a3333f638dd750de74052c1eb61c47cfb1dffb93de97b2cb3d46c6bd83327391c9861d3fd495d8cd0fd278c30bed7989f5d1cab830efde7c78ed2b2a2c3f983948a2aa25a6891fae3d8418ea1748dd633fef15b4af638ddeda0affad5e5736e3f703a3233cbe7975f8af1bb4b3176c72c931dabc3c162c5eb23ab696275d2cbcf481cabc3d1c97fd61f0ed60524d64922aaf1f8e2ab71057a57b7c3d13492fbd11d3f7bb14492c47a565957d0cbb2114298c3d3e2520913f31715c3b9807bb09532c9e334e46ec0000e449a866d7c0ae8d8e0a36be8c76771ac615a390866d788bd7e4e9e189e859af61bc735044376fe659f0bbf647ad2c31e4b32695fd2fee26e67d9aa16e78cf6396b6a72833fe1f603f3487a12370011507f9e1cb3c64e20b18c65c39e9947c933237d06996b2ac8b3fc409ee59aba7ee7afc7786c2471dbbf2d468e8866b79edccdc1668f03b5884241adc6384540e54792855afcc9b9bf5fc0c6273df7ecf4a9713656f7cb0410420899e85108af961d9bbb6195dc59b22bee3e85eed1dcac837540f820ac7fe3bdf75a0455dcddddbbb322dbac507eef6191cda669d83cda3df2cbd10c6b77f7912cc2c7b97577f8de7baf9344c7b81b06967de84010bdeb5116d9468e9f0e089f1bdab6469a7bb48e1b5f7208a0f59df8367bdc8dd3448837d893f0a12834fa0d7843c6e8f344f6604df49c8c357e33f02307b301db0f63fb5f0eece9e61410c54775343d5afaf38cc4ace4c139d29b91df037549f758b9f949cdbe55fba4608d74e76858949d21632f4ebe19f80fee603b9e6c0c1be3ce519b619a73269b69259b699a739bcd36cd39cd6625cc6630da45b2d366319a739795d1a1cd6668282d55a346c96b7c39e0c30fd8e6f7020481ed8f355210426f41d69f9918c040d8c2cbc2cffcb9bb7bf32c053d2c58b060c182050b962b57ae5cb972e5ca952b57ae5cb972c58a152b56ac58790f0b162c58b04ce13d2c56ac58b162c58a152bef09e1439e0416772c58b060c182050b7dffa290c17fabfb8ae0d1e3f1be7830889ed2e5092c84ff155728c7ba03b35b1a57a80ff9101278048f58043c3a198272a8113f6294d2fd69fede7bef69cfdf7befbd1791903db797edd7c25e7d4d22a02af649a975f5ea1e638c1e6574f7e841f0a24108d3dd10763b1872c2b894eed121840e55e0ec1c07d8fa334cb37b7bb787e1b1fb61628c578c1cd923757f24187e3142edc1d73fefbaae4b9baa192dacd65d37b7a6d149213333bf516bdd9b7c87793d2c25373bc312bc27b8d8cc94694f64b13f1c0687ee4e2794eaa58c7172befd8811a541a1e549f981c2cb27e189efe3e3c31321333f861042c8eda9212c849f1f1a11b98101a24204919b2d78dadb0a691f1f1f1f9ee6227bab223e3e3e3e58ad083a9b86888d851305a11512b3c40a0b6cec571eb6b0f216fdcc833bf41feecc27c8f865bcfd809ad74d869efe21f3cf649123575fdd1c5f728fc4acb342a9eea796d0f957ab3192838f8f8f4fce0f9fc3403002f8fd3523d5c6be3d4be8ebaedd23113e96b3afb54496a12674d3329d7a7d96b5c61bd7e7429c91b8ced410146ea91ad09deba3bd6fb35fc1c7c1da61223d89547586b8bdea497542a99a463e8f421e534250f96cefc34fd58032b4699a9210cd02c13ab0ba4415f6be85374b50b1f057382c7cdedef6b61fd14677cd310ef458f9977fd6ba823d1b93a0602b69902feb85f07f70bd3d52be8e14d7691a7f1d0804272b13982fc17ca65d9f9924174bc3be961351c895c0248d846dacaf3b1d20885ded638df3e15f3146d36f5cfa382b0fb4db5579da6e7fb17ac177bbd59d59b787d75fad5ed1ca9df9dbac3bf0a3dd6add6888165606828f4418b2292907f3120766509b1465678cfcc9c9a75c4cdd62e405c3d1dfb8ecb3c951ce7b667e36b5c7698d7b4eb9494f284e65a43ee8d03a1532f77e776ee7ca87df834bf7cf1a5ea5d893a4fd75912ed245ba48a4d7b81dec5d17e9358d94658fd31b86fd45b7d7fe9a7e23354cb63dcc46aa3c6fbb5bd30cfb2f715a46babe1fbbea6d534b2038416407605fcb09244efc580ec2be56135dacbf97b5d46304c39e8164929d4bfaf83d58c95d12bb952efd79da46d2750b335f63205887343d562a61a512f69c84dbd9fe6114c3e49bb037491d20e56e0f76877f7bd3df98a7f3ef29be7f8d1276e25ecfd02a612a4f5bd284a9314af572a0e7c6549e7ea63a25f6fe1bc74068bcd1a059ac5ed2aca7ee3893a7ee6a0fdc43fb42a289a3eef83cdddd4451134d9a48620f605f8b092dddfd5bff167f7b06da7a8bbdc5cfb2fbea7529b7f79bdc36b9bddfe4f67e939793ec38b4524a281f06411d60f677fbf72f6edb766575e3913b0da52cd58df426aef2409bd5db8391a63c695b5f4d98d0820927363365d9dbfc71fd3c6d3926945801d8d7ca81172d1601f6b5723064b397f9a5733d035d0f5ac8ede8605ffb33b4d77b7fb9f2b4bc34f9aefcbe590d7d3d736daf959ef2407b7d7c53dd2a37801f2a2b0fb4f26f1cc00fcd2a4f5b1981c8ba239b5479dac2dee19742f865d568d0acd7cb0f2b03c13aba9eba2b755cc2c45e2552d20c42a269f11fecdabc6d406a3c069f85dff006d8651b57e3513264626c3d46e03bf68e04831bb763fa1e2c3f1573b76aebcb2d3cd7dbd6f42dd4db5cdb74b9325677ae373d77576a7b8c3084fc38f0cae7e91dfe1e6cc34bfe067fbbaec45e1e0cc3eaf524b663c088c188c188c1187f7b4b8cbf7d04238c30b68cbf54c654dd05dc3565e3e563cf823537dc781aff33b82f9d626610c2df1fe448b8473fdc1e076e2db1afc1993620a6aff12513f637b4f077dad4bfd473f5bfdea877f60d0c6eb05ed6b08fcf828dd5fe1b2dd4d23f5833e31face18934aa58ae6d7018c0a1bbf834b4f9da80d4f8ed25d7afe2b4674e665f837b36c5c5a751ef44d53b67d44b65d44b4bac8bb2b1be8da9b7b330f576945e73a3cccc3bf3b1a7ee826bfb57e5e571ed1bd318c3b619aed7be32d3df385a53a497f5e3df70512efca5ab0740e5a0c75ebae52f5d3546bbbd7c6daba6f8f1dd05d89b5ebbb02fd5db4741db5fd4a576abf23dc85d700306ba8b7f495513bcd8b7af95441325b458967d2d25a0d856122d1c081959cdc29f2fa5950321fb827d2d285b6cf62c405f0b4a14dbd9d78212645b50aad8ccf2cb18ff623ed334989f756e9fd5cba56d03527a7f3ed234d9f3fc57fa2c9bf57a36dfe7ebccf76c07fb1eec7c3d27cf78c66920b8873f67d3f4d33467f57ee6e975a71ffbf9f72fd77bf518e9c1f26733bb2198cf49ecbd9e87db99df8365ee4221d06acce96441f6f234b49771da371a6edbf9d9054b7fb1f7af978ffc35025aaa7c4f32efd59ff972e5696b9a01ab332a4fd7d48ccf84cce7e7671d06e21efc59e58bfaabc4993620f0b9eef0635f72feeb7d7a85a5126f1b90f93b1b0d9a9df57220db7e68527846d350a0d2677f71fc3238ed6538983f6d25aea4d35d4c0c2e857df6cf9a3e26864d1508ee717dcda0d5fef2c0cb03b5baf357fb8cb37fdb0cf3d9ab36430c29a6a6e65f3cd0f25f1ea899b4a7309c7747eb9baadc80f0ef94eaf671a341b35c2f077aecfcedb5d7d1b4f7ed873669e8ec2ebb640c8e7922f3476941047c848184d8d2dac19716951ef81498738a0bbdefcb8a7b823d3386b1900e05b4f8922b4454f1f124458cc49f58048330b4bb6ce609d48f7417dd2709fa110ff2c0aba126c73857e21ec581403b4b77f1db28c90dfc664cff483f30474cf53df06a8a3c1e4a9aa649d3f44f1fd80436fe7b9783b011fbcb4beab5ed2084b358edba333df7197313ab303049c644329948da56c48817347bf9b78ddc54df0533ae1fc3d2111bebd7ca64f51cc7d68d8c8c6e1bb5112b9b9327a98522526e3ffe33267d12e84d6047bfa50ca8ff8c5afc1a1d124ecf9f5a13fa7cfbf1afc33db08f2f81c783f4f127f068eac7d5df590380bf9447cc9b991ff39583af7b3ae63bf75e0e0070f357dc7cd224658f9132794d1700e002ac159bd5492492d73b9f7f879f3e4e63dccf76d243278ef39fee805c0000d755f44cfcec0a22213972e4fa111bdf93344dfd9ad5c7f7218fe244bec4a1b8146fe24e6cac9ec493b8121702d236c583dbf93a98ecac33361a4c3f2b0f6cf9698c2345608dfb40eeefaca169b8ca909176ea633e5538ae72fe133fa505bd5ef5368a340dc9c6c7eb230d24ac24c53c6c4dbfe340ddc59feee2e368e31b61ec6bfc15d7443d1365feb611071d61201f1cd80277d9b680d522ddc5f937b21a9ffa22469ae6dd981bcacaf8f7647cfcc9b95077f17d63962d026b74e04cfc49aa37e2abfebe2f36d807d4cf97db10d417691a1a1fdf08fba07efedb66bdd9c37c46bb8bafe24eddc54f7135381a286e06c764f532b82ff732121bffc4a16c0cb7c305c3518e87ada9cad05d7c9d58aa7792b46a64e307c9e244cd1a6327b9ce8b6e7400beb4a8404901a14505d14f9cd2a2224a6b8a93d614252da2a11691921497860f1241a2088b580b248244cc85c8aa9864713e52c9806addf50f3ea2f66df1b73427c1d2401a427a3f72d8c5892cc50525c521b4888efcfcc2356849b1c576d132a8c019f6b5a478e22e448c08296770c5d8d792e204b3740592530183d91285512b8a1e584db3af15c514cea40667925c40ad50aa57fd0a27888fee7c10041314558a1044e18b143889c1cfd050163168c2959f2a6e2004143e40824251a5403d4a8582353850361394c8c2022438c1c9175d8ea4502288265daa60832fec25007bb57c00642f17ec15054e10a6a2f5a0c85e971050a81dac57354e10d47732ec6b45e1c45e949d42193fb3669cac806261ec6b554194da8164d974c73bba63a2c4ea8eafeb92f4fa560f98d8eb6a596185bdaeebba64ca08191d768a0736fc46d027650ad605fb5a52b4b0121dc87b45580218562001e5b13181d086767e42c5132c4fb278e2c59110d94f67eb39aa6780e66cbb60fb6dbc175053f80e91dd08bf8effa669701bdd6bdf1e7868c87fd334b25ef5f54c6cd5cd45d570e343061b6c6c90f0e2cdbc22bc9967635dc243028c37f3ec8b5b64f0676f3f6030a8c1edc789b583c2681066f6a1b8878dbfd2be8f525e0ef837a268bc1ed9bf4fa96cdebf1b01eca039ec03c05756db7835f486634e33de878f1b1f37383ee01719f48b8c61a13bed8c18311ea76fbae6f988f190c298ea756b7a99b7b9f1d7a6351a345e0dfba0de3f3e1e2dbcbfe41e36defdbfc67bbd446e3a8804dd6123e53d0d6d7ecfbf51ef0d7bd947c8cb01df061703eb80afe264e09eb36183a16d8d4a8197033e8a0bf272c04f5da1a8f77f3c9a896e66c8e8e2fefe685072ae1ed2a8da3f58c37d3ff74c4f575c102754bc99675de08238c1f2669ead5c102759bc9967358ee38238f1e2cd3cdbdc4571f7054188a7ed9c362926f4d9798a115359ca11bc99778237f3ec13163c91014be98ea83b58394a0a0b9d9e03f9c8a83b16cb491196f0669e05c30a382cdbacb5d6de2b2d71a921be637aaf279bc5c4f9d341fe1068a53471cf6a7cc4466c746f6c0d3a37dcdcdc2061052723707202272c706e4e0dc66b726c6d89f3d7f5bf8e7bb6eb6b0b7d84a038acfff53278338f8889040de2a47b34d37158a7f06995281c05d6bc1c7b33ee596814657e141d98857f368233ece587d3d9d4311fe3e0cb319ff466e6c37f818c9d3fa104dce76be51f1f37ddc1bf9105ca599a862fcb280623b25c160cf2848837f3907833cfcabac49b79419e40f1669ebd9ca47d6c745d92fd708f5785cb40d8c726783cfa0bdbd732da62ef73148842f9f4f10efa249d907b4a0bbd9af526c1e3d14960f60de5635c8f85d3a5c7da2923a87ca0d7c6067617d95ba5e093fe67049c859c27fcf3dfe3d1969482ef3d0337c884c4cdc9fd3073606c8c61e36fef85f8292ae8f517955d1c0f3bf04ccb1ef9751104969faf19380b57dde17a9e6999af8d06b7bd491eda5e281ac9f169f3dd4bf6568f47f3db81ca410160c3da938ca4217d3fd748fd7438d7d3cb21f3ba7be3146fd808b7c7e9e67898d6bf833535c099f853ab77c61ae09c57c642ce36d7137d1dfcc9696fbeee75af8344ac53cd76571d8c58ffb7d5221bbaaba1bb2804855ff4ba481b2069e940a8abc123a0af850589bd5a07524f6c6c524ee6b479c0162b8441851c1cf1450ca29430743044145e00c10a51b090f3fe65c0a15f7e0485d2885bbfae182b8fdbb7c9ca43432e9ef4bffeb8d3dfffac777bf7c3ea49e29c97bbaa5445f418af2b5e33cae8fe504b947c5d9c039a80c678c1b8bb9452ba747fd121840efd0b2dcac265f69a86fdb645ea3d1a4b2064cfa5cea579969d9a869df49ae6fe9926a5eca2e80db786301420352623fbc5457708358fd16177c330d81906652fc6a8d3ddfcebd44de3163bec7b2c151534ba9cae095d9d5551412f9d539b4d5454d03b55535453bc6a8a0b321b42d810a2a351c511a0745cc0c6011416c0212339c4424546c821a31546f4bea0a20418510fb688432cc5141dc4a176550e9e5176c513492cbf11d6e5c62971a889888338e5b211858f432cc51429e210d64568bafbf329b4bf4027ee108975f7ef30ba0612f5977a894029902876ac2ff6d22f32acac10e80af0081c6a1c4059c18443b28b6c6243894c10464405bbc2094c07d8152c95178ce87d41c50f18914c90c7e8f19aee369d143631b60a0a270f03baff73e527b42e72680a39050a394565630839c442454de490d6c54745e417b5a326c883365182c01afa59954982f06ddb8882f2bdb1b1f1099a441642d50ea87b6bedd55dc504f5245818abea4acf30bb63af1ea5ac44d83f254403cfc87a77100bc772485d3373baa8020846d60b1f2cd4e88988bfd6f222082b2dfc8714caaa3a1d415513d69c5a5d8461210972504d6465a1ca08faec3d39777a23e8ab615fab0b2258695fab8b2cf69e2ca99d087e5a1366ee6786b0a686380f4fa16ab25245798296550b08559cc8deef9f5102bd7ee45ba86a09bd7fb404aa2ac221f36342318e646fa5c4baf5990566b1fd503b350d95eea6f5aa620289b2d808e1c3f02fb2549265a61417ee66950d5436500d51c5c8c852c166a6b7520d41999999354f4d81c2ef77d58eea570cd96be3831ba15e55831f60c1022b508ab802143d4cf83829a205250062d2b2620a5b0f7e5cf8c11693155e6c9b7d2d2eb88091457b352b8a322ba890b870828b2655b4b848b2dda48e503ec8544df314c24cd360d5e1b25946e25259a60581eef09348cf05d01646566a9af640aed7bec742987197b10cab902349ad36370a72ab8a83d534f077bcbb177f90bd55962a028d97641581c6a979f187711aa5f253424e317ffa1ebb4515bbc3bed60f8a669089f132df63b38c3512c635cf392de460fe6d7c918a5892f61897fd3c7d0c2e262b71f14d5ca95b350d7dbf9e524ba90d467866409f7d5b70d8de297083897b16fe9c33aba921d7cb0772b911cbb454e2604a6d618a4cdea8a9042b6972279a0a02bd2c164e0dfefaf9cbef1972d5a7ba9ebf2745043a7fa5ba56ec836191bd08f46fad1f0cd9edb3f76867a040fb79eee09f690f7174cdcf93cdedc631313dc9f4441008b5b35ebe9a14206fb7f7ad3a94f2e6c77fe76615465abac030809008398942a528cb16f7d2346de1930d4af17aeb0c73ee00e45952dbde7e94ea7b3a6ef6fddbfb36240436db86b4cd74e8c1b721fea47adf661bdb21304a77a7aad11848699a6c88571825bb88f8fd40c8f6f726845afed93a309402a7bc1cd0ff2f48053a815e08e5a3cf92ecf5134c989afb8988de0bf2e371c9eb4a0d5dffd75fdaaa3ba154524a499b6675b50f9c4aa28bee8ff1eb9b42aff6eeac512dec50d0a6b97e7a8fd10a4b297fa4a2a01765a5ea579d464f8f1bc0cf75315f2cb998d76482fd25ff5e3525a4fffabede3fbed39c734e8d3952f7cf291d637c0a85deffa68953f6169990458ba126b410cac28bcdfa35c3d212f45d6922d423cb016dc11f130fb801ad804397825e6a639cf4348d4cc701bdd0f6e3c14e541d50fec7caa81645277b5b4a0af5b9802c6201c5aeec6b5d41c566ac71e4d8d618c04fdc892fa5046d22441989eca5b0d059543e592879a92cd03bedcf23ebff5a586cc9f109b7575f9b1d04ca1eea3b677966b3df3e631837c8f505d1805e0e2aea1f48b9e8c4443dd3cf2c63c65f8ae24771336470efcdcced073351d36854b2672bf34d03236b504fe365fc8c9f4f83e32d908f6a66d4d733d8779d321c0cccc7701db76ddbb6bf7c64e3c7f853a97482f918300f6360602aedae8f60cd0d6fa6bf69bd134365da63db0f0d7ee90720029a7dacf0f98837cea8bd8ce4d4502e4d0981e2f48d0c11c2222ed6eb4541786acd8825b685b0d87245d0153eaf087dad2cb4d87eada22ef64ead4796d24267b2d70e98afbb02644056a92f2b084396fa42fba78c41aec935d84e669c0999253f972a6c519654167a1f15ef87a79ba6995a8643b344db73ddc3483da7eef8150551857d5b9200e3c7094514f1c41204584289087650842f8e04fda46ff6a3fa9bc55a322cf67d0d51b6e822c50ba814f1454ea4c110472862a20589949c140fe89b3f64cc849b8832c50e04648fa67b5da42794eaed7b386c58753a25ee5714d838d5dfa07128150632ee84c2c1c23f11b150a33958f831ba8c1e7d634741aa33ee57bd61c0f2935202932c33bd6fe64e2581762a095d8a0bbd3b3cc585c61e2c5727b2b7ba1ce49fa594f0e37a1c767df37b3d70601dfca924505aaf2b5e2c6dee2b08ac9938708f15eb6053bda84dab5db770a615f7f0c12b865dba3d585325f272e4d01d7fe783b469b5eb9da46fab2a8236dbd73458bddae3c2e51259d9f8da101b6175a62ca7685d016a5931e29242429696952aad2a61b4aa0cb5aa0805b5aaf808b55a495248685989d2b272a4d5ea4222699ad6ddf6b65477b2e7d7ea3f12e5b13f9c886679602132961fd2680d8c17639431c618ecc088075a96944a313a9abf57a7a4e89581e8aea7184ef52719ecf5260e9636eebe2c9ad4d14911c163fce581a418dceb99cc76e57f5974246dc902eee61411fed16364e6d411f88232b6066eec0e7f1070a66c40af0ea73dc339ec8fc447660f56feb5c39c7f31c11e073af48149ced0626a07d7558022b44870050b22aef892002a80b8b0020a3f504151ac125b3d4c8ed4e08b2eb090c40c5c9093012a745142480a41f822899ccba25205152b6cf73fa216152a4f04a1cfa8944e35653dea1491040040008314000028100a06c442b148241e942545fa14000d8ca448724c96c7a3208651108410818c10020001003020033233228e02189a53f8c2ac593db9b1df8584890e7dadf10bc0c48a43dcf91e9f091ece8590e19bf3617be8cccba6527550910b5064248061ed999fe183c18d0e956a838d74bbded34de032ef0374209cb1b7749de674f703d6297c40ecc6cb0a37f1c88d4e815893d48db9a4817ea6582d975bab7001f968009cb115bde45679ea2ea637326d34eb6ca5127f81fb0bd859608e2b10e193208646dc3274a60f1b1279e5ae5b3fb80f0cbdb28abb42e20fbc17c3c7e082468fec89ae1c2293106e2821d6c6fe84a16a0b5da518d25585934bc69e3bb0da2fe6d0b436c4ee870b6263e620ddd042ecca4a452d377655dff990bc2aedc3c9c6124c8ccc19e7732bf0ba7af63682415f43e147701eb587b1d27da7e5aae565efeaed7b647fe700d242b771bdef87525aca94afc32c3d522a2e091aa7bf192df65b55d9e350a3cb75231e86655724abf8b587ccc9988c5931cc344e4c63746955bf30f98e15480521d958cd4a441171f77554a244301282f8aec3f21682e88ff13b73c501268391b4387d66d9b899ea2fe225aa83bec6b0ffadcdf081ca585ec49c40e2f3076928f198e63e0bb023dee306cec7c2a80074a714ed77d61cfc88a19e12c42077ac5af8fba258f713b4033c53eb99c4b4900dacd21ceed1e3abe29b43263a780cb23c9459c61fbf58a818927b7f1bf13bfe623f7b1d0e8f6863f80944d939fa452b6b8df5dd5cc33215bc10194909ab92974b08a8cb33cf535db635ebe0066b7d500bab3067eb7227e80d4fe558477f51a98211605785439e3d8f4f6453884567b15e44d65d00930831754e28ac272ab493c0eb80e045ea7d4e028c44a84323c9415a2e70193ad25b4b0a26f1ca0aa39a4f5375e4b7cdd0c63dfae11e04a84c2a1b99e8580e3dfdf6a65fbbbcafac183abeffb891bc931ce7c966d58cf1ae4904887d64629b87184ff1278bf8853f199bc9fc7c45f9acaec85dc18feea7d729eb69e01dd5ad34eebe09db3f5d029da4a5d8a5e4d44f850de89ec9ef51b2a4e021d8bab6c33e5d2e214224246d4b6f63a27d15292a6d2a4aadaa5c35c946668ff380b924aaf0336bca4a5ca241448f1925ed40623635031e8c82b98b2c817a58c48279042a1c09a7764481e9905520a6d4ab3a947614c06c7c6b577b5d936f5e09ca291a61cc9d66314ea389a46b754ba63814460da5c40294e500392274ed94df2c7e366cb81bc901ccce82ede76e50772de14655910f35224dba41372d047ea373b70fc17a83ec54d9d2c691304b3ae05206b7ecde8d8f2d4679c97ee27bb1ae247c7d78eddc34da3e680a66a03df338d51ea6d2c4d90f31f78193ec1c5eae308024171b47825208cc1255f9971db200a68be4c784d62dace502141d4d636a6a9d2c545cdcc67d82e335809a48c41a32f75e639cb285ad91d83d67af04e951dec940e39438b35c539e896232533fa03a24f04bcb5367f5f5e52fd074172a1eca784c8693726daa8b0d62f239ea51430a7ab6c732bd78229d57b781314b8bd38b1a9be3711533db3dbc8282a577189836824c5c6127bd9ac16b903c9320c0b7072a51b901dc1d679316c4f5d5b113f8938ece1dbd0e43485cb748fee5c5672ea6dc62a1ac193769efd5bdc58eef760651f1db2a6312154a20e325bda069dca8be5674e1945ca7244da46a40ec33b7783b9a1003a325e55c68c485a1cf6e7a0afba112e8eec74d46def717d81973d144edb30633c841703943a001319858fbb01158a9af89702fa0b0a8d5d06868738a438dddd0cbc0edcbd9fea885cc6bb75c790e5a907f4524643772d5a11ea99764e08636727e55260cead63be8c96e6291c90d93661255ee17a5f2dea0c5194b1672571cc08eb7aaf4e5033304ca6e0afd70522b2b6407d070ca8d09ec1fe3f6d6f69c19c52e4b0779ce85795bebaa84e45300172ddcf226d6ca1878ae19f3c660624486c9ea8b3918408c69663b17ddca18860422b54c65ae8ad4e4520c9cf8913d6dccfa10365458b93a4827873f2389026fc4c82fb42c51352d6317182fc373d1d48bcf052cc69608cba8c37d42760380654f3e86758c93924febb72feb47553f9f4b97f544e69cc0f95be3c2bd25b946f0328bdb8229f1f1a6fbfbeeef777c5f87775ddeeef076f7f7bbbfdbe99dae373a7fa3f3fbce6fa3aefc3eca470059ce8018c15c86f79ddfeefe6ea7773aded0fd9deeefbbbfdff17d1dde7579bbc3dbdddfeffe6e7764ef4f2e77505f8cbb865b01fc6719a9945a304569a2acc03844ba5e24ca85e323dce5a2a8281a03e16e16475df4f808e1ed6241949bef1a7bfc68bb283db0c069d89fcd8362de64240270ec23ecc731ff556016f7f310f46f67300cf132390bbd9db3e97c93af22d623500dceee937b8b490005d47e3937c2c3551fdb69fdc9f3028c5240a461ee8f2d2ca03b2db9e63afe1a696564ebf03ce387f60e105ee4a76567cc866c049996779df7f76cc3ccd1fe4191c62c2d7de801a51ef5e79cf9c894c5ea6d9393b07aca944b0f55c1e6a5b46fe706443341664ab3716dd279122367115f3a11746c4547891281286932a547b8378218373b59271a5954431e2976e73352f3cdeabaf6f6b9227d78a3d078261cc5f27872d29f52a27a9332b205e6c84aabcd55adf4b7acdcb38befae6b951e9d4e647fd6ad84c85e021ca99d7614bb20841963a3b21f2b7c6703149f0632fd78497f05ba91d7861e2fc699caa1044acbe090cad3ae0b0650adc469965470959bf2a5f4361b4897575a360de10cf7cce83ebb06e8579a7af2a135f67a929065e8c4d79896fd09d620d1f0c1c15f0245f11a7db1ac6c4d5046d54a3578310064616a9c678e7b6c92e553bd3fc6989e7a022343640e7b86c71170aac10047dce65558e26b6e158bb303a3b5240fcbc26d9a4c7306218956434f81dbd7d89e4151d3a60cfe40f3eb71e698671ada6b383033be1a5b61df7840ade6b0dc8da2ced8cff4bea0718ea93a9cb118e8c5a84635e18c843b53d14db17a4367f661f6600a7f44e03d41ab32cdff34239c7c6d9a811eacebc250ae16e8e25ed86c87095ed5c2081af34cbf1e72b047ef8105583e994d6214131b2abe3c07cac07d106f422f06443ab0e1a49622c537fcea79dcccfca64d4d9a446a3cb3348bb600facd544615b84dcba960215114c13e8108bde142dfcb1c8654b97e192a440bdf06b938ff39c84861c32d8c911ef0466050e5684d6a587330c998aac394e7b2d6ed866f5a3d7a8853bf88e8144df957f9f067a6ac65d6d22e94665b56b0e06cf3ec90c60d1989297667b2d2ff9aade8db3f4f5d3d479a74aec8f767486040f07d1cb0a74f70ebfbcd2ac68be79d7962a36fc6c4c0a54134b1d8d6a00692323dad575cc7b0d7e92ebe78921f9f8f49027544f3490d6bf30590c975700ee55bdd06a1810b0cea7d23d8381b731ef909dc34cfd1598f544f523b15b72438a4e67a9590cba86d97996a27ae2c6c4f90d35d66efbd08a26b001022177b5524967c32643ebb914c9dbc9bc074d9262f103259128210e73a73694827aa6182721fdd4f530c601438afbd9f27e9bb40112e887c24c4b8664f984c75bb7cadc8107c783f60699d8492d77940bc581e10274947e49b970b817cc6e68e8d9fc314fbe29bf54711ed677bf9565adfb221ba986f387c072ca0d6929e2a758c81deb547e9e8c1bb92df3fbafc87c32d873b632ea7a2583776dcf1a1e30e074500d9bb717dc6c064502c24f9f2475c1e74bdc0f32e6ce6bf0ff1612ec063798624c7fac36c93f4ebfcf76ba36e370652b9653e8c7b9ab780a5a761a031f0d550166d7c7b1eba0b14a2c3ac6907d82b2e548973e36ac955e50bd57875037e99262e9b7a1b7fa4c6597e06f020c42faba9b4ed552cdbc6dac19f9c5d43a774714c9f10f8b6af89e9b00c90f43861aa4d44f6e4a871f3ac0ead09f3f2c7e68329c85107250e6f30836423b5fdff67a2e022ba80e76acc35b1619c0bda9df7efd555bd212d6f13795b2d2f35e15a640578c68a9ca4729060609ee7cc232ebe8739255358ec4962029044a3ad92fa15e1d827f422d76168e4eff77af9a33440f891ce824c1f54a7f5ab7d52d7fd77492319ef5c6345310f5b32b712df21915687cda6c01dfddb21d81026ab2fa8ae0af073d771b64486565e98cc952aee4f3d466480f8ad504bf9d31a8d546586390a5f6dd9c1dac065f472c391bb292c165445b96afff34673f17ec5173e1006a59fd388b653d8ec67660bf2344f19a271e547f042c318284f03035ab72a99e6de1e8268e4b2c8a60f95ea5bc7960abc0d29f9c2d71b8d0445d7c96cb7051e9e388c690a438ab00eeebb2946881e7bb903caee7bc1ac68ede97489260fd82d92c99431d3a754c7a2602b45765a7e10cd1838b39d8d46a3d09fd7f26a81398851be9b75cb8b5c596bebc508e9c700b6d0117d098f18ffc3c7addc238f8212e0bc23511f972495def6e6b133c01511bb16b63798d326abfd3b21dae4ef25a087f294f9f2865034dfa8f10862d77eab2b75d6aa10e3db86fcc8f6a37c99d7b1bbee4dae3dd79f36b1bc7100050be4a4538c8618b1bdcc0891015c48631079afe58fd9bd60e7c2325ca48fa92f0fdc81d2419576ba6f5f4f96a3db5141cb18312be1555ab8dbcc0525fc04acbfa465073c897c5cc39c436f0deead219c9b26178aa814277f2ff4be011a5ea156a1418dfed5431587c4f1122e94b9fe7a129fdde2a185c54fb2abd3764e451256e23ff31d1755dddf0f01c1ee1b66bd7cc3b7209a3a61bba07e78ed6271c6d944d4cbdd887ae9ecf784cdf49122b832023ba785ec726b80170deed650e6c085c265b0826a274b006c3f2d06713559e114a571999d1efc05b5a140eb0db8ed2ffec34147154571a5084a815886aa709598bb5200aebaae55e213d61594e66fe1327274d3fdc6e09026c3d82b58b4f99685d10be7b89d6ceb00d9d28239548433c144e46653ad99a3dee5f181090d4066417fe1ce780458d85b62f2966220ec49ce013a7dadc372585d915ded03a6e8e42e24383f10677bdd3f736fb28d00a13e46c1d3f59a5a7092568e14bcbb66fe05ad0168470db0eba24d70d8774ea59e2d86651e8009c8fc32b5cedecf46a62254ba542c9d95498d4eeec5eb77268636e9df941eeba8c6c44d0e24c5bd69fa41510e7fb3461b50488a9fb1dc29b1852945331c7bbc20308328900a4ef926d25b95b34f8d659713dbcee53f82baa2364bd8def700b3e374b90a9268c1cab8485a2b048bbe7539007fb649b9f29f0a0e3b5d5a9e92aa72e77a27c34fceaa130bbc0d933441b44a21f776179737605ce13e27cca57138d4787c683530d1035679b5cd9a98e742d1379fe937b7f00a5f876297af00424950e1faf6751c33518a5c0c97e3efe938d108eeea4fe88f2a6a710c44a3eb6892231b1b410ed3eee38f7df284560137a0558556ff4111405f2100992106cfea644a12e0d243d37627e1d85d1cb30270c8c7ff3fd11726732973f2488af5b4cc61a7f6a4b31fe3107dd93ac0f644d5492fb4d27d381be12ee6b4f71b45ab3701a0bcc8b6d13fc8c295da734bc71ddedeab3210dc2a08c96480e7d54bcfd75c981e25cc01e2560db6a9e4d396bdba094925a6f72f3957d4b30c6e2a11f3641beb58936e95f670e3d223de52aaa3dc38a7743847a01116acc661eaac2607a3be94aa8beba7ed1269747ed5375b820c74285f88f3aa4404f29486e0e592859b85518a6def0c59da3725dc65403df604f4e1c07859de95503e032cb39ef9254715fd2fd6b4c63a287c24491402d8a55dda05f8f3bef64aea8f35e9016a3aadd0f03a967b0508533dbea7acc5dc12179009566de0a2581390734c66031b62f724925197a070578f0eac4cf998c710dc9721acc1e2bee96478d757f2e5afa03cf9d54c507b994a6237d3dcaee3c92298ffe594ee2f00819c335a8a49708e5dea6fe6dd43c164219f03e3e47f70eaf5211651427a8003c80ae14899c8dbdf7487ac2787604813a3c78167f60348d2305463f7b594594ea8eb5c142530058837ea17613a725256031fffafc9e87bad6668e1fcf4623d167a67929c15d1f8df27bfbe5527b622362f08b08f392b36d1684a027112ecd76154f382b569dfe7b99ac5fbc2e2c366d0069bf7a84bb3c39e650919a1ba3080f3d9346dff27948808c97505294fbc4e117792907ce38cae92efba4ae8910234c893b7484be83ad34f111736e82a2517834bc8ddf87694b6eb28d49e208430c9f3669b0868d5469a05e5d3d287d35ef0656ebe03c26be46ce04528a201247786cb0927a8817b514f3335fb089ed01c0106dd19a1c0c227d6c62c660fa777a0a4ac574de82f66fb04185d326529801908870fda1f78e33bcb4827bd54c8145799633eee7b8c4027996465a47d5d383e797551add939b7627b9e84edb4601d5b29d580e97bb18a7b655bfa205914084c22139b1317c7e43f2c6b9443423d92b65b23eef6653dd01974a4f9545bce89cccbe7c9727638af48499c7db34000a90a0bb591ebc69ef03796b1284d83f3362e9ca7c2b579b9b1cd13b2355d8c7ceccbce5b674753ca2e65e6a0e83a6e5978a5d9efd5a12f4d37fb12312e2176dc607e853418cd7be85a98c857c62013e3e1ad2175e2ea985b3cf47befa2efc57bfdf5139478c45de0b44f525f4d0eb2611aeb1d314b5a95c923726a00e0c4e36217b0c458701abd5e49b7840f7f5593873a91e6a4e02dc5c063d0c4371c7ca821fca3970235600694c10fc0e7010f1d361bd06cc8a14f0a0b441d64813bf31ee379aeaa5bde0c16dea9f6bdf74ef361e72004e0a7846d94be2d3bc5faf6644d1275b135ed0d75bb231d7c09aa922b0f01719d5e9f1635b4a76d4c34f02855a027f6fe5f59bd10891bb1d82d0c69e70a08ed75da062ca4da096392c1de8017187de1409605bd7c50e58120078a6c90c806291f6499a0c901420ee872409607bd6c042acb44f90a20ab65554d992cbed21a246398a7c434c5a629364b91090a66506e8262738acd53d21c05261498a6c434539098407d0e0e1929518a6b5d1b79361dfa0eb7d266e102287a329711f163d5299748d9102f95b5985a772517e382678696c1a590e9cd7de57a73319ec0059de1613da252d1b720abf24ccf0b52063f2e74969ab4d6d9f940d1437507082fcfffdbee424610a8c7d5f415cc5061b683c3b1285531a0105669acdb8b29870517a05ba801458162684caeb089fe222b478cc35b2ee6567f566e1fbee09a72ef4d3eb86315fe821bcb74924caf7f372c01d6a7675306799ff9b4761647b0f34a9354c68b14e32e619a0b1b2da496af8a1f80ad22b9bf3b84f8d6843601f0ebd909370040afc4abc6b12826d0ca1d0b6b416e7e2bd4fefd72a1685cf622baa15702a3f89248d5b1758eeb0ec4c5580ff6008b6996bd2a3bd7f62313f6edd930ef2c594fe8068672d4d94d8cdc21277420ec2f9d713a74137e72adb3ac345559752e39a495f4741e228008b7e8ae620b3b8c9d91b229a46b206a0075c7b45be246054fe0491123e9e1722ef0865ec144fc19b224511b101be96953024ad257a29d2f3cef771161180b86acc33f7021553e486ccd9169721a1cc82373abe2ac160241e8d006222b9fe4a64702f7cd2a6c26fe7615b204523f74a9e14921f62942ce6411f192311fcb0a19ec02ae074474757cbee4d9037ec246f8901afb8bea60b8b1fb28b10290225cc98c6fe931b988417f303e3ec0097d5ff6a13d97356ae556ddf12410d88a8ce885b1ee8a1c305735ba00fa7a4660548182262c54809c4e2f3a46f637a6176584494aafc668ad190f5c0c276f9574ca354803d85c7061a8ac1e97f9da3bf3f27636e7eef21e327f3de122450d059b57b63865b48b841b8769cfc6cf2e9e23abb744b40de8830b5c4b6465776892d0d7be43922158429fe7a23b013d00d9471759d5aa94aa25c8c5893bc755717e3be4c5aae5b97e067afc626c5474e82c7df721efe575046b6152908beb578dc3a0d847e8f35acc2a742c7bcae9596da0d12be5c4eb5b5cd314a50c34b2f6be69845255b3c95afd0e676f83f74d853d02688cfbe087b34fdc6557f7810daf0ca9e2b5bab7be04415dda1fceb64cecf0e275ad6b8ee043878a0792e0e9d39b06facda256b994bc953bc7724f65c1ee982b857b5ffef7105a9ae2b7f9fbcd1fba59ce38593eca32ab26a2a91d290b6d9799f72de198a7c8a9261970bfbb6c68e6b82b2da781626d5d09a5a43476211b3c9d99cc127c0721de8c500e865371efebfa30b566819f41fd1d0a5406254ca8b684b399663f9f70e419a4eb5218289887435221a65c204d8d324288d883becbe1f79fc0f4d763dd433909dbb4a4d6058deea0de853cbcd15af63ca4f9e2c80c960621d2c745250a42bcd3d509bb2ea030a84321292b1795ebdfb803549992cd3994cb05c7dcbf1172c3387373c202bf12241bf53bff4669fc1f091ae1fffe3f5ddec490f48fe642fa14d739d2e6b291d508702cfd7b4a322ffbf51cf2231cbbf5fe0e5ac86f7a9b47f2edc1fe84331fd872248891f55211387ffd473e9cf9c0549b13046f79329e9669f6800ccd40201337f4b20e13fb37fcd8df1102b014271e530752499e6c0a61594e3b07f2f3a173bff5f908eba3e30b811c31d27c6c700cc9f9579880266bb08bb844743b0f4893b72f170ef576c1868929b119fc864b69ca33f8862424382c76cde7a3662fce19a48ac6224f37fca6fc0451cc0018f7180b766e51105d5d3a9f22b34c22f71d0641c7684045f6c9e24941d07cce23f0141070b2a28ead42148f37d58a83e8d7e22070a984b557a30003e432d82e037cda8acb92aa51eaa0b32030890426e308c81459648839100df4b22101b5fcc827c44781e66cad539a5c0b0dd4ba58726235823e388bf5a2bc2d5126228abbecb06677c7cce52ebf737fc48a0726449b5d3f37fd8ea24ea591dee2efa2ae8f860766cfeacbc7704c7f6f02835eaaaf3ef93726f538fae7f511f50298554a9bf0ae3a93c9d76914a94d9e500d02b2c86b362720470b20038d733e7e8d21475b7de7add09829b86448f6e30d61a3e0c1e838b18c1756df8daa8b27a8a4f0e9e19e79aa74217545e0ebf7aad221da57b04e95f3f210cb45a6464002997d0c54681a3e8df2c3905956a3d69a067725b6f35cba3476aa7892bcdee8aec6e18fa9a61d1b9ece698bd9789aca3937abfb270ba89426f0ada8dd210e81028ca3066bc5f3da9a624942552a5e956c4406882e5cc6950176cb3eb710d1436afcbcf82acb5c2961e9a75dc9c670f47fecf37c7e892a9bfa8f80e14d5f3744180f5359cb30eaa97fc0d5a35053ef0fa46b37aad67bbe6a73a5435e3bcaaaf1a94269bea4d8ca57d857f96a2221785e052f00081eec02c284413385fb4e52e2830e8e7315535b2693b59020238e6bd670daa60fba165c411f594b7d98dda9827edc000f91eb016c173ca1657808ae5ce88ce5f4feff1399cb390d16c63bb5c2aadf83a8f8374aa0b32249b4048feaab9ab1e9886d1dbb86e66880a83d6a3bddfc19f0da64a9169f93cf3cd0b04a29eafafe18720058dac691d2cda354be39b195bca845e2703aea915c86dff74e3fdbba071a140fc3f1f58aa5843f808f3a7bf04bbc38f088c8f4003cba67433ff007e8378b0d6756fa9b9411023c56fae1fa67df50ac7c948be608ea279cbb502be7ae3266b19727f20df9a80d420c82b727d413fe4d90053aaddba28b31ad661141392b80c9ca5207e9e9a85e551e6c9f11c943a1587da861ab2d15c158e32725032d486bf18262f25fad58120e9dc5b12200850bc7374c843e4be875d82b7bc7a4194146f7b3da74cbd95c6a5c5ad0224366d36189b71c0cc60ff3a4b6a4cff802c8427b51f4edd88054eb8ebed0481aaf7d34e9c19ba84c48f63472370f0e979799fdf600b526c383feee9b95d5568b1b60a922691064244d4b43020cb5077f03f91f777ba7a3ea544f9a1a932b6a46b546d2d09225144d4156de2b76647dda23460fcefda98feefe1a9a777b0e4ef11c5c0e6c42f852d89e21f6279eb4d84339e4b50f02c67c1013a79ca183b229a93ffd7bb61244fb10d1e224bc905ef447caf1eeaef572a831301b43263c63d5a4292dc28e62afdbfb40ae89a4e900d0e82bdcbf3ec55c1f56559093f68193d5556f224148cfc6736be45f860dfb0e68f4948082c63804a22bd5ecb7d4ec60c18b770c70a2e41baa32b01753837e2246c394b80b66c82dd9188b566b4f40c850947eefb6d970bd7ac9b947cb0972cbed362fbd87e4aea6d11e341c8407b9f9e011ffa72d1043f07264f65023ccdbcfb2bab6b89d58055688bef1e841a0300c9c7d4b4b2c6ece7b598a856918dafb1de2f63881b2535065aba79b347117677490729dceb24bd5a403341fb322bd3bc8bd98e3b97efbeaef16218c71557a5a1c0f048e314cb92c6ed6a2e23498dfde92e1f28500d75660210766f9bb080fc7488f7e03bd2ac9032f50c9e7b18045cdf6a8f4e5d7843950f396694e37e25a2dd625930382b1dcbfb8ebcd4fa295466bfcbaaa28c4f1cb3a5b00b6e1df0c837626514e74ef4f811c6f0917e417a609c41a47e0de75237c84f2edfe70e0debbd7872b2e1e84f4cafb7b95d2cb9b157bf983490acd3a16697973dfd494204335cd60c77b4b6d56ad377ff4d1178fd1965a0f5cd92807ef679b56a32f7316a803be746fb954bda619f1b45aa61ee4d9e9524ac6f8cff1d68d9625975921fe84ebc6a57ffb68646e1628b8b28bc77b777d27a4e25bee94e286dd003adec8dfdab7b8076fc4fa141e387359a9b02879cd32584a4e98cadf00e35a7868bf5306e4e66c930b38b2bd2e6c786c8c14777e0cd83209db55628823700d62a9dc1ff53271df42f972ee5b8e6f95ded02330bf566348e64fa7124e00e9765a582e0fa63f82b6dabf017ecf5c2bc37dd8ad74e9039c37c6ab8c4e8e8988075ffdb8868fcac2d26231edf858ae420f383a449002a55d222f09cd78ac57f547d2878361cd6b44e2af0b1fd1622123c0dc042cbd7c67c8a5af23b17a06f3c332f830203fd26ab086979b80ea59b6d2a18ed8c242260b82a6376644dcbcd4932534e5cd51b9b253847d0bbc8762a00d1804e01a82fbb73a5176bbbc8e4210ce1ab6de53da8560926492c3cdfa637da9485513dc94b829dc944fb6ea13156981a7cb3972fc13f6766ceaf1f21ef066d4677e8d5a2f7c81ad0408e3961d6a0ae567987c568dce62edf5796d649e391cd48024685e1ea9679abd9ca042f72ba1d0581a5ec1227b1642f862f60d4dfb744a8870ccd268e2b5728dfadb366993d7ee4626e5a71101e6f8dae63abd0139eb4842b9c05f45e27ba037b2006e29a0845f279b18ab94a191cfc5594fcd82bee7405611748f800b396909e9481517ceb43d7e2fa734ef120e8a5244dc9e76c92d002521156158c56ee9f23588250ee8db22205e7c724d9b5c403dc92bfb1660f48e483a13ef4a299ae747e5c82f6733bdbcb9b65d6c0faaf1aeddf385412cfc23c61fcb54803958423df1fd5e0c3ad88c22731792a988f764516c94e7570709ff55024065ec8a6b97224387bf6344dd5dcaf0bcc9ff575788765d31b6cc4664d23b68eaa91fffe5078954c45b7c5ed7a734c58f63a529b2e45463dfbd68850c3f5210ecb1339bc549c108e3905813037f94fdf63db435b4af5e50429c77a4f28183a136d477b3b06f731038b563b3b952cb074f68e3cdabf9362887954292ed3af40038547bdd791b3d62663f02159a4fd5775dcc584203a00ea6b4f2807735c12ea9061d4dabc93586e8c3d5386cea9037bbaa450b5100c860f482409e38295c6da43d7039086a22287435e5e519544d30d31af4c20256fac222f4d251c06fbc23a47d7e500cf15f9a4ac361eceb2fec68a8d0084a210e1fe161e7ef6cd3b856d39e3a119335ccfeac210f0a3933d2e8957b5ed896c3c6dbbc1bd3cc49d4fcc9748c50a238d15084f4137f1db4ffb71f33750994f5232613301250514d9dc1f7dfcd09b0fa31d949dec092b424a680a33cfbcc392ee467ec0ee7aac44357503400ea0c69253dbfa4928bbd8185ac747226a55efc21a40fd9da5e594fde0adc6fa19ae13c2b6dfc403eb2e00d0e3de7cf2611a9db43dbc3ec73663435033acf143c517e44277659f72777a987219144ec8591f2263106a58503aa8b64300005f62ea92e11129062b0b94315c09ee3b0623ed80346b3434a4e23790a8224260116fb5bb78dbd5bf63c04aa7f7f49f755da68cab4ebac5e82901922e3cc5cc9b3537054621f802621d5bb8793286fd337c56bd261875a45727171d77436941c00f705339666983750bb5e1a812274c15fecfa94d2fd409ad440d7c75997db3c06504a17a4ce0a32a780ad5487b302e7d498839bf57f41bdf153a20093271fb669f9c1a8eaaed2a7e187e75b7d238818c7d54530d9902806e6db4e272c9a481e0a1c854e73eb526c8d06fe4990bf13998b8891e4ce30ca50c3b9d090ef70e955f1fd73ed82ab053a9981e4c89d3cad96f77269d471cd19e0a641269e32b75c1fb1959844f7c8fca512eb5d927b9019b362bbdb633059c044ca77e7d3d37cd2b24e5d46c3034f52cbf863afb9749178c0019d3b53ee9a5a04b63bc1c17dbb3e6bbfe85684b05a32e18106ab06d7db8759447abb6b334fb742f95aeda71cac957479c126dd9df6694d60fb8f546ede90344b272229eac01e122775913d643960423bc49ae4818f1297a32b8bfed60395c9683519d01a08262abc6c163d33dff6515bd371659278cdc557c22376a6ae180931771d0c597f1ee10ea5138d9d9f7b4fc282ad2d5d04ba64f1c93dcd5ea77fa48497a7c23c650a107da469afc1f25af48b169ee7a4d798f6397c3d8bcbfc9a00f54fab1e5a6466102887bfe2c08443731dd4a929be9c7b108c58560804174c8d444aa9345400110fafcb033ff5a01444f94568a85e2ec0d9c39f1dc9d88d93c5768e6c00a16ec4ffb19ea2303f41f7d8a30dd1617ff35c2df55b366fb3d97dcb6a2746fa275483fb1ff1470ca0721e273e75c684c350ea11118a8da0059889350238a5d5b5cc7178f4b9d3a33233859302885d899658adbc20a758e2373994a1eae538995ed160b029e9ac3bb9d0dd62813bfabce3e3a45ac0b3372bdc707ca6f532f928c3d623c24fb820865df970e58fc017585688055e0c0acc370d71a6bb132d2e24b4631bde3830b83563a7ff72fa648d935db566e092a54641d8292c6c364dd5a531b494a021f62a8a59d02234159389b523f0e2b9d7530d84e7597c5a4cb6da2a307f2bdbefbec2aca580e53dab43e6972dc802e176705069b7f836f263e42c332628f0167a08c38cbbd4d8cb6368d6884a00391375e9f3004dfd6f0d1bb385dac45a4652cc56f8340affea585127596bce92650361d2fee254859990dc6f0a9f8138f007aaa71cb451f236a3480f153a73b05402876c11cf4dfa231a6e717307c46d28626692dad2b3e6e0d90fd2dc3f249e0324105754f0013bbd14003b7a917334f0c9f54e0f0b54ff24735fc226848143bbe6f835b2cf6b4445c54547dce73e6eae9e9beb00bbbbf244642ccfc69b47523a42dda5913f1bed1aa7c5040de4fce431770c7da2d32dec54e116eb10a0e7a3d6f8a53aca3827e3569695f17fd4e712ffc7d19095c09d580263dc098414ed995b0982e39a5f25b087f8c1bfd64ad057beb6a87207400c98e3adb98f4a5cff9309b149d098fbb01fc44b72ddaed42f886aa9b2977d413b2f20588e4bf109002b72ed6d428f1ee97cabe388059c77d13d7fad529201d4bf8e7707797f01d640e557a5ad44bfa53c983b6dbace0e93a5bf43668712816b052e864a9945ecacc40ffe0bfb686902a0a5b59b1f960e5a5be30acd52f685a0bc1b840cfa9f60ad9f9c3806080b2ded718b35a3ad3098aaf07916ae5e55651ed92d3f36b23bc6d9f464e569d81d7abe5d23cb5bd324dde9ba19105372f10dce6964d1f9d97e9413d65889ea9e8872a858454ed0a26ed7eb0d7d211285dc02109d3d79f901389c105b1ccfa33d9f3a789f2bb575fc8cb5dc184a0953d6dd6c458d1d5440dd0e89c1ca72ef4ba89083aafcbf7e8e9b3d5f27025cb85110cb0d2ab3f17bbebc1b1b9cc49cb957bed2ec0b159a8728d4c6531cd324c12fe8e82883ad96a1e3d27b49b6717c2cbb5d4708c383f2dba74d436818f48cb5f3840e0172d7f781bf45a240b8cb6e91be0e120bd5019ebb700baac0e8bec9d803e666f9e50e45ab40be2df2e4b2547279870a1b0ef4487aba7d4015a25d70c8f8dd1961d1ae3190842352cd824b0a206848ffaf15d83abdad2a473e64aefcf654ddec907909b014650d4d5e95b937cf882320c63551ef432e52a0710947f381dd5656a1c4eca39cbb7d4c08833eec3cec5664594563df6e274123b85f3c55033873089fdb3a478e5e9779b0dc9848d5259bff77be992dc562bea3257c8f755e766c196520f9a987b8fc955ffef9f2ad9810baefbd6c7b112172b040822d46d95be376487afe4714a8423be1234882d64a93466095b9d5c2ce7ec54f8952491734212b7dd8f0a8fc7377a051c1bb643d9388e8fcb887b1d2a8a0097b5ee30409687495e90301d0c2e4f092ab7ff36f48b7c53442fc95edbb16456b6b0251a85365ff10da25caea4015594c9e8b7cd59330e223dac82dc6a8658ecf1b6a88055761d89f166f44c8375dc7f0c635c21f67fde1c0b8941263ab3f2a2d79f14af83a666697d677f11f83b7a084eb1e46746c6a3235c7493070d5205daf00500ed073a41143694dc0fc0b269b60c783f7006ff478e171b10d9e8e7004090198423829a4d9465499056b6065f710008ec3630ca6400716a79bf53120df8ed9ef7414ad155070d8573990e34b0528d3afca3d3ccf5a8e767e22c4b4df4fdde11ef9c2652fc4a21dfb70df10e27633149475a1a1fd4e84303212438ff4cc8dbb62cfe8207c7fded656f07478a96447f22fbdd723b8f6b081f620ba8357f5764f58e641fa310f621879f5c58c64b3f5f1bd385c45f0195c40685f49dcd16a2138d611ec0d45a15feaf2865dc7c1bde1cb2b76c9555147b5956b035326d75ef5b349fa2c26569963970c5d2c57c8beb07cf2ee55b5e74a88ed1934d044aa17d9f98118e3f5366d3239a98ae16475dae6bed269d5262f41adfee64e0e401ca276e0d413cac1214b4efe402d5565c81f11217a716366eeccfea381069f564064c0d1e7307c7b9201d426c361edf1a6301c9c73c13419d1d4c74d23eb813dfede6d0b747b33c29996c7622580cbe07be9ac54bc58ecd762c9dbcbbfe61a3923ebcf2a8891eb228ee86124966b302d28f00ae0a20aea56ada3fa223a484c1ddc47d9af2704366e151b3a73b882c9b61d6679682bb80560072065f00323a83c34861a20dfc0078d80ff23b80bd08cf92acb45307dd93e2a272ecd0dd26e81d52d1097c21d7c35e3c8d28bfa730780f2011c6c619830b860670c37fc99bf91e5cf117e4adedad07ac59cfa1b4c577a38ef1c572ed2f2f8b19ee5ae9bb5db0e4509ccdecbe0638cac7fda4a6bc0d1b76c7653e1068cb6cabe5c14c3d3e3b3f5a0c71f1affd23c2cd22fcbd8604c2e48aea52b31c87dbf6d01afb88032293aed3d64d569d5026a53a22036cf0e74d8e050ace61695ddb14c96dbb682b58ba620aa951a961202b6f2ff1501fcef8cb403457cff719fa538e2bde80148b8fb56c46813fff9c402a61566f0cc65f146d626c8c4c2159fe3400624b36be5450eb26793602aa43728f568db518d1a44740948d7fbeaf89d2f83e1657aed76110a7f580f9ec5c38f4fdc37aefdd57fd61237e7cf16b315e8833dae142d1c3aa173ee52ae7b5f12513f8a8f4fc2b43384a624c2fd1e84ecc8569d147439244fa7d03b9feb3cfae01fa70cbcbbd30614a47b437ef4e95a73a2f84424a0c71e04b57d742a093c4c259a48c02f89e915e8063893fb5f72ffa41518b6f8dad03490ac90e468c66eeee2a9d54dbc871c3e090ca271ea3712fda73954a210e496f138552d589df4776a3724ce21ebc0f68d0b06fc01a4432c9a9c2a32ab436e93c1408268df80a9f2e0f569f9bb9186859d8294dfc381802b048a9457ab57417f048bb3d02c0428c649c6b1f92e4422d219b63fb07aa695467c4d46b689202fb9703d75a051cb23b82e5c7452cd8de86b8c8f9e1b48ca2e8951acc5e754c26a096d2cf83d3493281a3c367dfc8aa3d860f005cb0a7083d338f1a31f2451f82903e4d9336b4bc39e9e82bff7e94442dbc87cf5b03a47fb429b95d05e60bcd923eaa5ff97e2cc4c4b12ccbfab3d7e19109ac3634a57b4cc924eecddf5b8fac304fd28422fd0a41eaa779926ea89bac8062876e2864d6e011708c3794851a22dc65fc99c25da713bd0027a54ec26711bb20b5993ba79979f959affe4c0e8f433023b267d36870dbfb91b9c41ad528b3b19368a1a36ecca257a8dadc6c93e72781d318e0fca097bfe4aaef5ad8fdc5f20363b9ec2de4c480006e80f2014b791f644b890fc3ea1000942edd46974cc878d6f5ec5000cbc18408834b393c52f0ad3d4bb667f9b1f155957c2c9a8d2169a902b78d64c7185e58748e62563421e1a9ffef08cb36d1e63342e5f4768c675e07d248311f17e8972f2e3685220c1842f21be2a99c7ea2d78bcf1dc7112ee20c11875dfa53a763c017af0cc25039f6f26804a7bc4ee5624034b8ea3f4c8d3d798941c7c92127852abe6a1045fc4ab1b617236b1d3869db9ba09ffcafe5829fcff5d8f8e85ff6a8d657c5b519ef7206ef83f5c11fff2bfb06ca9b95964fcb1425fc79ad83540c2b4cbe072e9e8ff0a96ce6dc295e0a5763908045e9d0203c8365c72b1763932bb3ac0cb07109ac6bc0b479d595d69033f3493f8f82be7d6400d21d3077b3325dc2170bfd98afe02dd8f68a15cf859dea4e39e8f7e20666867408f6accae4c8c5e5cf22acfc7748e80c4360a59ee1cbe7e9740f46e50af1766e1366a4a72988fe787ab01b604a91e3363c770a809bdf11fde0e8a24bfc7f05fad1f4a9ab8bf81cd4e4c02f17bdf690184cfcabdd672bc37e0f2972e3278d16717607e0a1e93ee9ed846338c2305c1b31fa64cffba0d07e76e5b123f2f3aee150370602b62a4b489f36adfc9b5de312634042a6644c50f5996e3cc34e6cfdd56560164d73659e833b865c8706bae83365ea3563de1fba2d055847cd0f0af95b1f35e6631ae0bec7cc50e2ca126e3bbed304275cc176c53148eac625974e78b0a058685315565da4e8193d00cd80ee0d8091fa5e6553d4e39b932ab70d90cfef768f9fe2e5ad3bccd2f21d3301662382eae932ebb407b1980bac067cca00d4d342e97da82addda7754be7ddee6a41c43600545197c0aaeae924af74b98f652c36504e8174ebd7e82f02a0685e5cb3499b6eea696dcee2ccd671bf7e86c5db18f6f00c4def546c8ed2174f740aafae5c3d118419db9edf0fb6b2a32c95c17d64144c1b68291055ca109aa156aad4f3d9858e19b4b1c7baa706fa8bd87eea38db97f784dd4ada2cc0fc868ed0f60e66b3c3a78863dc08927c6eabb5ae77f240b371593d6cf3b107fd4dd6b679a3a5d78283411b91b9051822027aaaf03ed7db4018203d4d41d617d36b08cdfde4491dd2f041a2adf2d0b7de8edf1a352938045853a40e2c95d0bfa01a7a32c8d0a93b30094597b00857a23f2297acdfe5feb4769353b6654e4a88a333c0b26abdbed19c9901d0ae6c03eff12ab740b135a013507e6f3ed99034ea0bf062c6a78ce2919f82554bc77d1bdb28163127e81f0bc2a4dbe9432d0815c1678be989ac0168763ccb94265c7c12853f9719dadacd87e73470a439dfffeed502a8b8cd2657a71a5e1ddedf34bb55b98a1a9a2ae387614399a5d32b4b18966ee0804fab4a70ce6fcd10ddf57d86020b7a8643af0926ab727a0be8971a8c8240eb1aba4d25258e7b4b94ac1ec4a16938a18a772375f49c2e7f5059d56be417bb49d12af3f8d422b9c370447c266de42feacaaf62e0560fc3cd329b6385852f50a009459ae86c7baaabc5dbc6f360d8e24c2e81082f4f29f4d6db956ee9ecbcad14556e957ae703be5387fa72606e493032088a4af99fc51fe467683dbc93f4d7a0f011f30f7ade9e022ec0eec10856172f27e417ae64fbc68600e5a0fdfb353b34b54d3d2d3b13dc423155c419615b2ee1fefa7bf172e72ca26bdd4598f9986ff725da41505b77d85ec93679ea0d0a8bff0fad572116e80a2072d83633b0e033f63dfba5d2872e17f98bc1742cabc65f3e7adecef4b356ca894907abe73d0b773ec5ce2db7f0069d960b97141a111708842178316d89ee616b3e7b28071b00baf7f23d2a0c128d4d5d26494600afcd7949b9cc4b0e36f7bed117fa2debbd5b9d1fb0b1d2fc2d9dc5507beb06d6f5e02bfb43d2731648d1775bffb793e9a983e33f77b40d85745f4fbca71d13c5ea62949f9a8617a1f61a53201333d7d310c90ae1ff6cdfc598f8c22b11f0f5d7eae49eb59673241c55c9c29ddf5aa0b7bfc1b8cbd9e2569a8b505acee2fcb52b9e164cecbac4620648819d91f62ae7ae5d809fedc654800bb6ca3338f127179ed85a6c1006d434672bd986e593c2d06ceea22d36d2bfe210b2684384c2d3e347bf31991b30e0de3a3e68358ac86614733a3ddc74d4faddac75f7caa694abf5605986356ac3fc6a6ab58591c90d294e94c48be1e45d31f16ef3e908d67e1da8c4af976ef3e5d091a1eb9debc6ff9089ad7a9ba6e50753a2595e410c802302fa87f81e9bf31376a622faa27e456c5940017c09c94a1e40d4ed2e2b88911281f39d1a2adb89e2354262665fb15c5d17cc2397a5a3b7a362e29f864dc0fcdc4ba59211470a1787040c907477c29e62e7f4417c27e25e0d8744856473dfc9c8a0b4f4c2ea47118fdd3defa2eb319ec189ad0ab460c02bc30fedddea0d590eebed33e36e06e6b0fe6e3825211352e24b8a991e08f106f8698d2f29de323f86e53c57a1ff5fab7c3dd460316e249ee104e6fcf7b2ebb417fc7642994d5a859d3e17b56c8bb817c434c5a467bb16c8c5f0fd3b794dadbc8967d00f568b53e43c75beb283fc9baf96ea9c081c34e2548a6244b64479060f300a50bdbab89e1a1ee63be723642924e4d20ab460538f26f10560f9ea34a65309cab5e8fadcc49a94626b4b47d89b1b64f82d0c272d78d7dc44b36aa11cce4fc527e84f015a0c1d9d04de20d0523aa3b7aaa60deae3283f9b65cf5adaf79bd17bab1ee901fdfc20d45223ac5cc1487411fee0178bdb022c8e14b9b1185007470814b306f42de89f292b00a34709e2dc81a52ef4acc503c7f2e99baa33c9fc441071b848acd842f769fa38a77473df5877316fd31ea6a4b805e2c7aad033000a5c9b48126c692a8d2856855e6af6330c71acfc835bb389e03908a32954afe11dc65f8a79e724c8a578f9db46b4ce99389e80d9b0041a141ce590e5007d96346207fd8bd9ee435eb566bc4ad8dcbb5b0fb3a54f354696770a7e4e6ccbcf90a3090cb6022ab1155b204865763077924070783f8120526267d8fb6d0b6feda500974850f2448c9c1a0504e137703b096d4afad6d816f5b2650f8afa257ec37a1d10e8abd00b67b935b7adbf0436b1d5c186ab2897e93e1631652a21e094e33b1d5a0cae6e70e1014d43905d8d0ca68ca49ba37965ed38c9eb7211f6939c1b2989095a4c9a70fb9b4ac7f054eb00f13a6f0232edf444a45281bd5c35a0f288c3e372499bfadac112d1ce096f7afc7a3d68f918f41b6ee595a7853358a14dc70cdaf450c24f0c42e6c8fed448f6c7338bdabe983e43a4d45a2ef1dff0478c69c53b64987eb1ed47c657ac0a756f5999725d0f302187a2d9fdfb258389afda79f749f6dae38a8338aa7574e38947a58747b5b83b2844f377c22280425394406ffb99bce1923f35d6125de9630fa4eaa9222a0c42d028479c74b73d067a00e698724532b0523af1adb36254cc987929029ae1df18e496fd831a30b3174cd97529132ea09cd6d19009fffc68eb68ac7603a9a933702f58cbbba0c1d61c3ffc5fcbb4e6ec00cd7b8ed1d2cbc9e810c04688e9e688f2a7b442e518c42859be6f16c02c40a5478da86fdfb0f4af59e704fd50586b5126ad9c7f022e6e120a25ce3deb5f50c15064bb9a3e6dde07a41368d57b583c4345d6d2199e79ca9f7cb9a49091a08a3160623c6c41f050e54b16be21ce916aadb28819bb648a4538c60f68b5a516d09a6de4ebae0909f97f8c259f3b227ba43436de6643b12ab5827435b9acbd2c398c59d3f818e92e87cb1a8f354b8f48a81cb34db91810ebb0c69800c0713ff91b1562fe89c2bb0f937f5cb348b55b4d011a5c75960c49de2b79c75e315ce8a868d10cfb7f6d61c4052d9c32dca69dba9d0d949b8897d465b9e7c3a483629491d7f2f4762e812ca8e5195787822e7e887a8bfbdeb8c0d06aedf19181162fb9f59eab9ca9aca26604cee5ac24b58d7558639900c811ba65357fd3411b72b35818d556ba8729c388f87e79190a0baa0b352ac019d512a129a4a5092423e457aa0a2e63ff3231ce4fa056dcc48bbf42d9b345aa14d217f35339d02658db3a925f665b2de9812305db837b0293e01027a9b40e8856d43605edd7ed917c4c17b561645d2498fbcdaa376f0a56fd4bfceeeb9f8258cc48a7a93fa981db32716dc62408e483d42c3947c6053bd758e76f533a694a89a60087fb1454904f215fea009efd0b9e7354e86f8d1771dc4a0ca4837738ebd932517073625a5e5a13bfed1b7f54e346e5defbe586ef1c85724e0abbb92b101573708f4268f2a202a1c70bd40a8a9b779d7a68ba67812b5a04f579b59a05f27cc99dd3ac97de6260c335a2f87fef3d16bcac1c0164ee993b3fde93ea93036494bf72eda48b661778b73e0eded587c8c7436522f7ce336aebce41d95d0f9f439adb776367c8bf119ac4113627558022aff8b00d1d4724fad3983895a77c82fe09e7b1fa441e37a83d2bf443204de76a06c4318028e4c0016cf30637c8cb531d45ecb906d33d0451552b6c2c0d8d86a62563d833ca63eb3c70dccf6a1f9acbc6c4b0e85ef3fac83bb3acdd3648570222ce583cb403a2dee151df0b439a5eef33cfebc9320e2aab002a225b100712f5ceb0f59d2e4989534fdbd1b987e6c01f707b964682291eac5b513887e8944477e38169fa213cbe5da816b57f20ecead940c1fb8066522986ad3600466635e26fb2e482e4ed5b8b113993aa62c370b3d36050bdeff6c852964b0f7511cde0a09163559e432331f377962a3da830bdd855a4c3f8a84d896a2d0351a547bdb1a3ef39cef09b940f68c438bc45468d9e0cacd55df2e536f38868fd36f8e913e4e2220495fa2d284553442357b07b3fea6115b42666746091f8ad55f050f7c9233bda10e9f1972247499aaad4d9515c919964d1846d8f915d3e67209d1bca575504b150503d465e29a08aeebb2cb16dffac56257ea03d1a8c2b99567ce23ead111263a94c687ee2f64d27b3f807a0eb32277b96ab611f537702ac349b04c412c5ba899a896a40a9275b6943ab633ef975fc1296811c22a1c1c12e9d5922563d199e5c1f08988db2fe46f940f45364f2448c30992f17abeb7308022e1d6ca59c58e2965377e5f7368c2862d4360bb5bfb688cfb2cff05839427ebc094a5a041ee0effaf9746c05c123069d6aac6cafcea28ae8d341a27083ded1125180c97c185813b57fae221a82403cdee0e15112aac5b394c5e6fae3cef129bae28ab171d334ad34aa4dae4d7101bf678bd4a48c97bbd8b7b1fd6572aa540c58047ce7ee393de140584912bfb5e2c3cc6a51421571f1e4dc5bfcdd529ce5deab631c5e07b7cdb1ae1ad80b384ccda784fb308a7eaa018858d7c992368f8062d4cbdc38cce32a53a8f086208794783dd5a6710d77119821c34e83eec50dc633721c993858fe5f9b4d5f6a592f098f26f4fb1998f6ec47ec9423de1bd810ca6a401dbf533866ac45b85509bf692c44175c2d3fd87b5581980d73a70d801abf99c63d16fa57b4c0593a4dcf794c3d1d8552df85bcdee98d120237f15267798e2469a5718408634f75416ff44105c1e20c7b2558587c6b402c441a80f1027332401346172a57c0c1055c9e355158731432476cba508949f5926461bc99cbe9670dae82ed82dc92dfffc3fc2e61786789b11178f3c5dd562629afba80e84f3c55a2c7c972e21452bc30ed754c9b41bc66808d72661278705e7026bd1baedb9338e268355994333f7b6d90cfda7b8ff0451e573f897bbe5cf47aaf921233f9654dbe599986a0f6bb1a0fc7837179a6776e400949be71403bdfc554f00fbc28020dd9f7c033279993d119924caceaa6c5f71a94a82701b334d82d7cad6f44fd4d288752ad508a53eebf2e0652f8ce6b625bb78421bc92dd5fa2333b7a1137c61295632c451ec93acf8a7f278ab91fc2af1ec16a76555ab653268ecc654d46ff90a413196c1adea1fec7f66b5a92a06920e5744c2af1cb032fe61760b7f33b05159c7d267089e6470ae7bb786bc5a47dc406bf01ba33637224d1fede3acb7e44408ee53f76969ae7f5fbfb1f750b30e2bae6ec246300d2665bd97a21ecab71735df804098956f58162ac1adf42243d5b1406379255bfb78889c820c78784c69f5632fca15a2a423b22bb12a0c050d2c5fbb0ffe9e7acb9a7d55fa9cd74a4dd12b65ff73bcc6702031a590003845d2e5f20092cca4a25e368a4491a36f584102ffd1872be40a91ac803e34e390b9ac00b276b6c20d4882923eb35ae29ff871f9c5697d7236018926dd93e0f554d543066e0e813375135e160ca0c1e340a090322f54f4cb5b18a04fe52f776258446acf5cb763f02275fdc4e8d69b5862d5252faf18f3c4353c331af40c2c323a30a4880b7de9bc4ffc0e1bb6d9b358c6b13681db9a1bbf50726688e236e0d6703c4e8e5b1254e9f6b42fcc60c3c5f79737c7e493de4a7f8201510f3faa6008d6ee58193ac4120588da4ef91e0c0f47b2dad9b26c04d194dc107f46daacdce2b20b71c4a1c386410debb48650ad522e5f816e0786899aaea1fc14eab105553a5ccb51a2fc7076f544f137e38110b5d8e4a76ea07fffb1bb75e8ce38b5ee2f4f77f61da78799fe8e479f6e2374b6ff51044f3e5dafce10bc5a805111fe36deb17fa61cb3803df04b133ae995f44cd3c5046d550ad98d02bd549b3ce702f1cc54424c4ad50258cd81fcac664e1a0d19a87ae7df3157a067f4c51385fc714afc41c5e698d9941c1bc58ba0a796ee5dc62673718a41c8d9e0036395cd8b525488c1ccdd4de32f28aa8d91c96439521cabb1119f5b77547bf78e134f092719254e8910d2b3d55247c8236e16b801cd08df4459fec711e61b3e422ef73861bb9c930b90f7d91895168b5df4d80e1e510e91f6e5e3b6e778f275db913c081a444fed1803cfc04a65804bbacffe832263c76075897f11283c11458dfc99661da5575a69e957825014505e6b07035354d1360575a46024c50af7e258cee6dc18c139f3995293e5a2dc5a600d021f870c053a671b7c49e4e88ac2cc13357d0a7551d6f42e2d52cc5273a15b898884387b68f3ed0f54041609988d3ba424c2f5070db5bb6af9289f01c3c463887c5760066715185089749cbc647e85d1abf0834b79de0c846e6c7c56f15a361331d490c88c1c8290d3d0097a0314298364fb8b662f9de547afbb0ca70cb0ae0a0cb2dcfd3056ba8ea3414228aa07cbeb3adc761e37583814d5ad8c003b1983df1f7dcd169428d5061a259ac65cf5b787c8fb39a7f59829f6673302b8f6ac68986133a41e39cffa07ed2fb4ebc00809d6694788f542c4e2d62101d526af12b395e8131c732251f3f807e8b30d32204204cdd64b1b8daad473c855e94ade0996755a4a20e6a3f8b5525da4a667f7afa7969428a6c8f49d5622283a52d86f1697a7cdca90ef15654ce59e997b3793e818a23f5dd9dbfb4dcea8ebdad679c008d1efe43fb776cc31e3eebaf0d017d5554c88c6f17c604589e8015f820189d357d68354c16b44f548b06bc304dabe0f0f6878c133ebc6dd02f0a5a931a5525d2f4886a647c70b68dd7628406f89231c27ad4b66e39690039937e1ed3d33ba911712a6d9cd2c21831a8a0542af1881bb7e29e33e58659dc782ca52f1f65da4e4d382dbe11f48fd763f112ad7691e942412462bf0c16eadf6bea806473aed118a951c93b1679c7650a89294b9811ca46344fdf5a42f1cb02efc12819e08738b856c05b3f4a157cddf4444c227a3a3e879dd4fc8331b1b8a04c047d606320aae484c024c4f8a74019c985a7af2d5eb89ba0a3696b93f1d1ad23b0b54035a3e0a2fd1b913a70ec63ee68030a8037b4aa6bccce6193b3b74db26d15b5bd56514ecc065f154a36a0889b206c265f034e990a992194b2cb1ac242c6f2901f8fff7b8511e3670a5cbc8cd922128fa141d28480222880273e5cd53d4de72318e7741e32acfea6f90afa9c259b77392c19d9c91a2cd3c99542a83acd074b1ba4b3727d528d99ef7ad5ff0e28ff516311acfef3b5357033830f77df8b98e7d3bd08bc240f23ddd16954358169a9b4a825a1ece6035d07d195c61a3434e2ac49ab2dd16dd9f77396f4c86077e696de511cf813626f405c71140c1b01259fb393e2352daee3ee14ff782e6ec14445b3457839d90c0e987be9cf1c3e031f6501033b9d2cd0cb10360a427696e90aca312e0b234040a3b46c740fed270ad4cc55a901f85ff4b9571f9f632af91d681333ad082c5e3ac9dfd30557c9f9bb07d1dd436b699fc7b84f836f58a016700739ebba44292ab00dbc3ed33aae32a924b8f75e415b895ab40630c718f8ed777aa6f94bf40a28f1d354d02140ce750ae2f7916c65a5d450798a005204102d850fb07de843cd4a29bee1420d0bf763ba27bf5809534e7fd16e9a12ab6d73573abe81252f2f8c6fc77e572df2562b201d9f1efa99767863b4686b7eef07fedc03f281a5faa53b4a004723b67add7fa4f3d4e74203fafc3012a5a695e67890ee76550cb33ce8a5a77ac0d40e701c7b50aab5e4c8c2cb220d56eca1d50f25d7951e9bc2cd6fb270a208087d37912a19ad6bc91a81e334e595d031f8d01abc277132a07b05af4ad31eb0c0cae703febd51ff3f3846ce0349b0e829abd2f51c7b980c04530a381977e6fb4563227ab5adfdcbd3f75a2ec9415daf8f59bd41b845140f67cf42333632689fae8f51b057faa82401ee7e4dc5cf9dbd54353adba50a646c28730b8941460243a992cdff43c4053355e4d8e04c770fa104091c1d82468531230737de5f7a0ce124c5da24008d4f9592ca13f3e454427e3b0bb0c1d7150a96513a32418d250eb18a890cae9cb3fbb8edfd2b96a79727044131925d72a5396c3c000c7f3932286d62b9a71c2178113bebb6876c4e79b37b87ad2a8d60c86597c508fa84623dab67b0050c84d413ee044c7121949689f114d7640f57082760d5ef8618264dc975b3c534116245d471401338ba7dc53e6898e795c6c84330262c99af4f26434e85bc0576ce064afb598ba5fb0cc6c3f3b0fb4be4c15b9327bf2ed23bb87da76ceb43b25bb28d78da1a028ea3f512fe961c31d51734f7aedced3130a64cd13e71b8f8ffc24b4810b4e975d536290169e788b4da3d444b895a2fc4bceaf43bec1df11638c49f0824c7560e75a112c0af34bafeadd90b1a1aa87ba8218c2466a95597ed0bb49671b17864aec5cbf499cc0b941b2a3d64109ee1c4b1a982a373f0850ce4c1d297266db6c8b5427532aea34c0c54b386562c2a23451a886a8d55431a3cac5b0c801961261138df7591a8f948ac9ed14d27535543f3d81b1015222a67934d79bf0003aa45fdeeb65f160ca24db33b49a036afcc377708a65799854e1f3ae159434ad474005f175706888e924d8e8e8447a5e04a015cc5b599079523b1c174ae7bacf2e703dd75deca46922e5a7a14e2019c740d04ed6556593c6c0db284cb1942bd41fd772fc4e7eba402510d4c6b4d6d723b13ab1d88cb387ea5a5363a1b199c84f7941793b0302574c69fdbe2e28b86f3d76c4a1d35ec0718f869e076bf88387ce5fcc02f888b56a554973c7cd1db49ce74a958a578d0b5bd27ab8c6dc26a2656bd856043689d7469f3ae80b473af7667a01f6d167d27f214837e2dbdb8a7398fc40e7bad24a8a932e769b27eda508758e91af59a048c8feb499b509b171f504c381fc320f62d81cb49b7a6ff754423c42e519cca69f46689fa9902f813c563f3a74f8621dd4f23a549a1384d632a2e70917b74a5232481be1b1e0ec736ba91035426f555ba763a67802867620201edcc1cdf1453f260609b6955376189a952b54374372b0d57c279924b7d5ba31ff425cac06be41a9f1384a157d5fd6b5e9c9e4cd8e91350b4053340e8955c7900fa08a81ead5a06560794a054069cdaa92ca610aec63a540018728d3eed619ad144505d1567a19f9c5e9363e1d8293f179c8e25e59aa103ba31fe2f64f8c0082bc06f08ac3e5611c19a65bf3ae0d05ac68f666636201e910f9f84c34613bde30725a00809b1bd4be921518541dde5c5142b02630f5c41192c4bad743d1de29aace5e90d9e0b6c3e320fd453b5a41e12935d5869eb9d523042ad3c97733a8120aef6a38f612abb7ebbf7c8f516c83b22530e5474705d66a0025d88c40f39c9ed7f54c1446910e5076c402747fc0b12db30805cd49f82d67bf2822810b127fe54d4eb174e884cb5d71eda12c93da39036d92143788d3ccf7e54d3bed7272535c39d75f675243bdac184d7b8d013bb2ecdb25d2864a44ea8471c3c556ab694f05f08fb88d8020035218065ca71c9efc9e4de383a2740f43425f036a444f9df8fe99f51eaf6c208bee7ead802e7b786ece93d4ab779ea6d394c8c353f5b8088366d271bad259030b613a267078e740c0e296420fbced181b7a0064682dbd5f883aa9c47e873d5822abd4d4ed8a7c4ee97116591ec017acb1567abf51aaaf466b617b350d6402f0ef97b5910e0f5f3ecaaf8a848cac2cd206fb3c3e79a3e6106a7a32d5d48c3c930aa6015ea4cefa222436a4f9ee854a590525325ee4acd43415aba75b0af9f1d613f220050155fc20478b6bc6f553c1203c7b92d37ac47b7c8ed1855764943a305ab42427668e8caffee01abe0265431abc4a39fb6d6562ba02a64c8fd45c142eb951286140383b61dff8abdda1c6f1aa16f4f36e35a2f5fe4c71d542c8524e37c132f1f11c4f4d8d019318705cafcaeba2e874b71fc0acf9ebdf41084a881ccacc169b7adabd4a693fd16e074bcfd75ee5809d4625eef22741928f151c32c1b2d71c57f2a2259d4f12c4122841901f737f3fc24904f282db39601085134c1fd3f5ac574c1f3635b6e9fb609dca2524842d326d77bc6bad526aef1f8e307599117b7f01909501d6088a726fc17dcb0a4d098165ce1f07596238172a840f6ea6bc5b3b944ed5d96484bf84dfa29ede170f6af41137a63262de208841b20008072931c490331d88fb82423d28c9ad99523ff277d8eba39babc68d13079d923c1c22b62f9d70ef12b85432a3844efccd330d3b7a2c4903ef5378e092035a521adbed29ad1daf989ab378fda625aeb6abd3120de27c7d2a528c920221213fe36a4d9d630a093d87cf0021d794e75483d840a693e0b22a0457848dce5be7ddc218926ba6ee0562e539b0899b82adbfd00a48a19fa33b9a0dbc48410042bbb090ae480edf6cf98153832e5082d415a3bd595e404b74e17a3bdb0c48630dc8702f0c221f44535d598c0c760feec9866b6a2b3d878638396ab8e1bdf434228a7bc1f21c67bf01cc94719f09d1785ba0ab56a9010973121882745b6c72ccc5b107209b692ae894a02c3f82f035e5a342ace22ba0fea4a80b2c86ce9cdcf58d16d6f15dc84afebe3f161bb513c61953082e845d5d9d03f07ecef81f3ab47c2cf7cb7f857f738fea33b2677d1094e131771e0fb3f4a4ebfbc267575941fedecd0f588cc42fe90fa781e32492a6287e66fc1ab957cc35c4350fc40ce46aa22f967ccd30801b5575d877385943f0fa0c4530190ca9d62b0902a71cad6b65dd076ee718ee84d2020b7b2e0282991aae66f24ed205689230453045322a731c8cb34fbe1e319b31b4f0063e3295dc6276567f5114c0e0ad37640d3e88a7ecc02f6c9dd2eaae80e51c7c1e39afecd36b622101d04e8b66a28099dc03eb2e8e05ced173afd3774fefd9d5e5b36fb159367048d8ad6964b11f08c37f618a78637fcef6b0d5c5f7899a05f97b71b672a9bf179f321daa4a524e169dd5a820dbfc1ac5ec4c3703054b8fc45ba13c500e06cbdbb274bcbd96d82fd7a8d7fc73004d63e52a56fa11ac723f3d627de309976d0b1a200fb7e748eb3ef74f128766a8739424268c091861f09f92743f33eb9dfdc6ac1896dc58e5a8bbed6b576b138f9821e248cc2763ec04251bd05738de9f3749eea955eb9fdd902ebbd7ffe8c5cc05b564d1e3f4078cab5355d4cd7d586feaa1837bed3732b27998e0c2571c3951606e47d9c9e055de17a4220a79e3f39386b4ccdc450d03a0834c5bc33dbff4cc8a4c1ce576124da4dc172514b8d0c14406735f39a33ce148f1944fca9f6d427d7e660463b783747ed0a999e5028675ad406e1982e5f18bf6de6ab6e54a5741c914b0b58e4884b7071167a05faff881d437f1fbebc115a039337e5d2e58fef1fc8544044de3f6865d1d9a2fd23fd4197d46cdadd2f60daa770caaac847854eebac3ae47c17abfc985455c0ed79dac4e6c6093b7b5620ebc30ed054daa8294a1a2a67d12939e9c2c4dc4600beae57d6f57319aa78feb2b95d68bda8ee4b24e08ee2c9aa6d807e801efb690f10ae8efe86e8780adfa031740eae118ed41b21ee8cc1936c337ec0eea1074470ef1e63db08e87534112b39f587f83caae5bc7db5d44c2e369a881d33fe5bb62285b4652f24173f24bcc166f3fa6ef06266238103bf3900a2c19639f339258e8f0ed493994f82ffb351dfb2088e3dc66f16606e2e5af48216e425f175daf97fc7496d61c7a57644b0cea7ae77a1628a4c488e3f329dccf8fa908a0cdd1b963f96b2c8fbf9cad45515cd976dd782e3906101eee75135834483ca8eee4a035fcbd3217c7e60cd07b14f21d1ae408cbdd582ef7783b366e9c447315fc9dad84bedef3cc5ede7b82ff67e0127d33a89073cef25428bcf74a77fcdd4df868550c92393fad109f8ec9de84262efd9f2be2249228ac5618095075241319effbb98f35253ad86a2814a147406552ede378eab1440813ab4c4aa88f117c7862d33727efdd511fcce4f01cbde798c27a842ec09a96016e00d1ecf7937b045bbe0bd84968fc0a052bfe0bbf10677df790d08bcffbf9f5c5a07eb23c0bcf3fdb1031053f7ce7964fa34c82d10c38e6645259e9b47072f22382685980114dcf7a7990e2ef78eb8a3d7d385eed35e310a0aac1f3ec2e4bb1b3d4cc40866dfd0f513f080f3e99802ff8742c870f2e205be061a3341d69d3f9277f5dbcd982c66ecb4c2215a623fa860ec4fc642ce0fb9fe739c15a75c39d8d95feff6a2b6da686410d25480c50094e9632c9858c2dc52a7a4cc2d58b1a583c566416428e63c2985aa80aa4d61b7ff140209380ec1b5a3ce1cb0c023c56af48bb31bdd9ece7ef7f0b319ed95b916c1d982e6e8501c1e74212d42ec632555de8a55052c9e32928c4ebc84c1baf32f56068b505d47f0e4fdc7a817429a59adfdcf22c1b6dcc9b3a5756ca52fff56df24592e4f4574571bc85c9ef8434282268ec877ad5d5250f37905cb9b9f330500c76a14b7ca71b6a555361ab5059b33995f983dae855a7c4fb026415a3b4fc3389f96d58d8e307451c9685184ce6c2868c3ace1ba541d1ea57a5c87c6622d0641c9950539a17c1d0970edb09ef36c82b81db75c69f55d8e608baa4dfb087b636975bbe36cbc25c7b253715a802a05dd410b9f8a64cb480d52122999499e38e181794849992967cf560af127e7bfd8fc2353ed5e312c29fab7463015cbd3691cd71a10459cddadc0ac5f2649029229c18e34ffe8904c350b631088f5e9f913a801c7b90d8c642a1692a855ed18b85772585730cd1710998ffe3139622d153c44a2664e00bbf3f3ea40ac2d1370408ac2b65e8a79b8d295090c8b8e89ca2131129dec1879527d49d2ce45268982b8b6eb609442aaf18f585e3b8a5be035c1c0dfcdc07bd4205cf6e7c73ff524c191019fc3e31b51c843fe100f12f7788462eaacc335f5d8bc7707ce56d571d5d229574b5a22b3cd7e201e090d42f560f93a74f3b7ff63d6cfd507e03b308d0280f5cb78a6085dec7800866f93467f7665dad839f1956eea8a10c64d81e2466f09374d5d21d4275372065cf6290ddf2c7685ba19f9301ac9aa1079c17d15f78356fd3b4807aab8f015fe9abc619be01ced26e0f650da4a55d24afd800b6db382953442e36e640e7c689ea9366f584e409e104a50024a5a0994fa030e5d2d869550092995030ed0c621f87c5e2318a98e466e640e7c583532a972c77c85785c42bd80232642692b81125642a57fc007b49d0c8334564e6c48ebaa04bd2461759044236eacc6d188e0c01b6ae8ab19bfcbe012ad8cf1491020a346b8f198ff8dcaf87cb4db88acbd1775e7a757b06021d58807e8dba8d0b46d4d4816447ba093ff2bcb6ab3d48ad6bcea6386509a84a6be1a2556e6787d9f1975423893b14643821d19076bff63227427e91aad539584e58812080e87575ab738cbf529321efc0312c112618d2a13489006ec6201eacc042a5150dcd57405fcbf5a84a45cb9bfc47674404f11780d37873fe197014f709c38e385cb50ae8957450c5216defc35aaf4dadab52a5ff4a242d21e01f003c70f1d41d54631d7993122d6daf6df7b4b29939432930286028b0276c8e3410b903edd63eefaa1f0d4de000f3421f799a9b90f58c725cfe5b1d8028fa063f55c46fc68d08c3fa60613712fcb0a4dbb64ced98d11c38cf931b1704b2dadfc70d288bfd1e624e1fa73b1aad987645fb54ad5cd9568f5abbb9b9b5591ae4101a54b3683db344ba1a10d35381d4cd9a1db287f785b3e2da708ceb00537c5af3543bbddab6ba7ebedd92a96d7d55410077cc805b82b2c8e89be28ee0a3a6575956d1120794c250678bd9c7ad6b26cce3eb98d6259329649f5f21d3b6459dc148ba88fed426138ebaa29ad2dac822c1f0ea34d62b18f7e501401d046d1ebf039bcd0e3f037bc0dbfe46b781a7e86a7bd0c1fc3c3f04afe8577e15bf8a067e193fc0a0ff4485e854fe167b94c6e23b3d194c9e435b21a398d1c95c764a88c862b57bfe99b5ecaf7f0499ff4571ee99d3ef7b977fa9abb3b0e5f0c9ce3a5bcd3277d0f6f7b280fe5931eca3b7dd2277dee739fcb5f3ee9933ef752de09cf9add5f054f5df754c275cbc2d0867dcf28d886e99e54e0a9b30dcf3d6d58eea9dfe96d9f5b407c2b8fe57d78dc4bf1637eca4f8f1bf3d38ff92923f14d2ee4a7c76d31b3142c9359b675cfe3759f5e9ba4b4a734383fd00b5534483826f0fb2fbc99eedb4768582b87d1f7a24a69aa83d40b368811d0eddb9a331f7888d8f54c4920c7ed2d0668cac700fb6349f9aa757fac262aeda934a862cd99c394084d26100475c153cbe64c4efaaa3b768a356744339f09446116816fba12b62370186de280c3bc249189f219d98cec9473b98cec253765a64c461e238b91bbe4a5cc256fc9b8acf45e7252d692b3e4303218f98b8c94b1e42bd94aaef25f642fbe2977918ff22d53c95ce42db216398b7cc4374929a5943bbc686a1ec7982b8f79b462e6579d291f9301045cea3ce611478d1c2543d9015e181f52dd9e145261180fc380335fcebbb156e690709f5c9c0ff361fc042fe3b58256bc80a7f6aac1caedce095ec675784ef032bee39ecbf064aa436956f7322907ab2a13a9db77cf96df248fa8a84def15db9ff8e84e6c990e7618fd71f18ba22f36ba19183777b1c8c8aef6eb7aabdd331bc771f4eaabd52d8a9a4f4e4a2bc645a27dba352b86fa7c4057f4da15bd7645af5dd16b57f4da15bdf6cdaef889548c5e18d65a6b75ec21d178aceb84c2868486b6cf7d52548bb690c774c3dba6a213d88bfc898b7e67be84aaec942e7e55fcbbf947fbc66f09f50983f86e77fc6e77fc6e77fc6e770c6b58fc6861921bfaa252babb7ba538fac296e90a594b460c9025a30468cbbb44c9964cee1217b24f35e9e1d5b64f3db35a9d024a97ec8aef0496bd36f57a815c7cb9f87ab9f87ab9386bebce2c4bafaee8337cb40c3a0b78b6654ea3591c7d7df12fe7150b39c6f48def04ced9956c59b61ea46f9423701875a9b664a8cf97254b972eb99c983b76ad8f36de27378a5b44b50fd11e4ea8cf8d72c79b26fc8d975a2e3c539cd3e48feb43c55018a5b92dc5e4b64c4b2d469291b3459ffe8b0eb1e73eab9bedb51a98339d734a39e594795b0d6aa9b3ee7a38f67a620830d0f16cdbf251059e7977ac0982d05704b198cf1021ab159db256154f7d79d1b80a45670ce7d0e593f31278c210cb4db83b1378de0ea7cb9e672cf0bcd9454b28f149e0a945248e207262c44ddedff4453c1178ea213cb56c82a7fe21f0d4f285f820f0d40f84943fb28ad628b09500759f544ae964340285b2ed8f3cbbe3c23a28dacb83a20930f34d792a524a19aef4501735551b7937fe86a63466db437dd447fd91d091d4fe514eab3faa1d79ff477f6484a706bb7842bd284a034f2d75512a1d9a15cea9c1f1f1bc0e87cdcd8d0d8e1b3835397845a3a3da49f18419fc999e2f87a73bd66d591dd5e500d000a600364a42006cd0769efeae7771b5370b9893901d719fcd5c89bb144b4bd949d2a805974568688372a66a545609232a4b9096db6fc952fc162d352da316d153007c243ca5f698b6d4dd95dad6a12db53f1245c253cfaf5b92328bc7f26d79f359a5bc6dafcadbac553a6b76d6b6b7655e90fa58aab71c903ca8c6326bd5e7d30d9e4f98a96e3aee396b5e3d1998d6599a4bd3b4169ae3bcebb8015e8e7b65715c15bf0e815815636154d1007026fcb6b7ab4ecd1603f4b500b6b3ed3d18b8563bdb9e9cdfa7aa2d55eeca952b57ac58b1622587019f870c6409e053a194522a6ea9a5eeaed4b6fa53792ab20e0a7052aff676de37038629150daeb9617383032767a5b3c3937f86abcd32cf8ece2a07a727c7d7ec0d8783e3c6e6464d0e9d6b4eabc134aa9466f14cda7d4a85e07dda54cfb05a3bb46e7c9af9ba968e70ef00c255ef4cdd9a358fa6c3b5da528a9f988200677f62cede018425ce16e58f1e1c2d4c792e3e8b77fa293fe5738fc56bf1653c16dff44eefe58f3e29dbb0ce5cd19c3d6d18674f25ec79386ef6d4366cb3a70ddfd8d3866bf6b461bca70dd3ec69c3aa3db30da7f6b4e1704f1b06f7b4e1993d6df8dbd386bd3d6db8dbd386ef9e1abc09718438614eb80a75747654aeedac75677b6b0bf862e0a925a5944a6fbe80830ea074d1219ffbdc27b9b5d682f46d3e6aadb7c94ff167f8bcdce5ee49de8c37e3934039a9d7cbc54ff165c4a68724a594524a29a757153ef74d98f608424a2973957adf5df283474aa7ba23a5938e94755573a4bc16a7d312472793524ae9347df43236ef64efbd01ced48460586b18a7c2d40352cb9e1454291cee184c614513506ab0828914b964658a24be80b21205ccd218162a65b1bc22a85563db45896dadb55abf3cd6ca4f621ece96465d9874b80f5c5be6daf27a8dd1d77ccd25265c8c706aa1ceed9a3594d5a53276ad1eb83c3f51fd526f09b775b634ea8264096acb5c6e2be16645282b5af0470ff563b8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdbeab5d6ea135cad74adb5565b2bd296527f4afc971e243d487a90f420111fcc8379300fa647851e15c8d460b4e589d11625a3bd92917801fce48cb9206ed90a57db326f95b58b2788a7e51642673f9fc641de4f121f3f9e0ef27ebe6f8605c2f7e903f67769cd2d40e99c3ddc9b781a943d2d606984a36d99a7b471419b52a528361542673f9e963e83bc1f4f4bf9a389a765ae3b7959b2116457772d79efedc008f5b4cd9629217536132247a7b287940b502d9354169946a8aed56b4ba666cb5900a58bc886a087449db69d9ef2654db0a1651501b5d5e007e4dbae7f4296fc991276b50d5bf702a9d9ce53414a877a48aa3504d43b52c04464d4a725d33ef273e4876a4dc4d3200b08d4d861a1960e9b81a98189c8a86dc4f7d5f2474a033a2dc39e9e101c09e5cf0c4bb6da1485edba6e4f83924a293da5fa5a21d0ed69707af75e07a9a78d78fbb2889c3a40ad3d02a5fb1055a2ab2bab48952f2be46b4d09b39f089b7e43828888f8d81d18f1ede9d73ef2f36950a680b8fe58409f4e4244c4c736e206dc7b646f190a9e92c0664a29a95579ad10e8ee9c82136aeda80d2b510874775df5d6140225106b82dd74ea395bac22bac8b51648dd7742994f4f40f3697ea9cd1f8268993c7a2c58d2da2a32f7947a4ae0d1232cf50924c00c8002e0d85575d8d66575dacbf1f5cc3c98439ed44e8ed3a82490f397d411060080220023170000100c0c894462490ac349168aed0314800d4c6c2e6e423e2a8d04229138300e8502a1501084000c8300088241180392248773d4d4019d1820bc7f92341bec6ed4e468eb0cbc22fac7144473179cef102c1b346a6b4963df0e49e9877cf5a4e5046ed62ec75442ae30d98ef32cf3dbb444bff1207aa94fdba0aced08fa6e76654e73bb4e531288a50840999db2c70ccd6c69efea8bf1b8a391fe892c35d34d0c8380a7b05ee6f8c138698efd77647ae375e6988c5614c68b3b88d5509a22e6e029017bb0d0913956f14ecfce28dc8b3fd5ee9cc9090c1de38a51df355431d1e1665a28d3266248d32c27884deb3745e8f809504d4b1c48f48e81ce0117a2c29b7d9bf768a63a31b6e47fcb82e11c5ec34c760e4f670e019e3700a8f39c3a514a4e99a465e4b4c8d20653f071029f94d22668a21de6be6c354ace4117fa5b84014e432df058f6dfa78fa4b733a3636f2d6d7beaa9901dbd6e54bddf2ec4c61bd39e5728f6021eaa4ceda39f605409d370e51fd5cae917f5e291da63cd72430c967658fad322119636567d63252646c38957041ef68c1f0c32b78026c8ea8cc26ed4182c9535b39a1c2ea6dc951efd3a065bc0477f063c18d6f03b86ec5fca66c90f0aaf6d49c22e96b49ecc520ebecd02b961b2ee8284877da5c5da130eeeea9d4c347ddbfb98d4f4e96c497f1cc8cbf4293d615d3292af07a4a82b07edfd6ffaf97fd75c3311914249e4d03670682c235c2b8ff39e8657ba50c324075758ec9221d1638c3500ff87997e20b9eee3945500dac0f88a6d4de3bca6b470afcef9f989081d80c4b4bf864898e17dc9e952b9081ee47dc9e952b9080e8cf601721a057a100f08ba370f9141c93f90d4a570912dc8ff90d4a570911decf6a7d2b588f72a44a49ea48152bf084ee465e48052bf0826ca9451a3d4f14424a652cf8840e467248152bec826b2392315d0cd8a37e1e46912c3a68a23e190a1a10644cf3b92d3a7f2111c68fbad8ab4d9bc244ed19dc0ed4cc48c130dea4d8a9038d76fce8e167e3d9c86b7b346b60309d88d67becd4431f423e101c9895b33fcbb4df58ee7a28c133e2806024b5a81d8d37553a84d9c2c2ae0e6de05ad1e580e8801fd020f57652f0ca48ca30523f18ddd0d94aeb5c601e460986aa2fadff45ec22a39a4124f43aef86904fe805d2742397b0919908cb49ec8c362b8544d8a8697ceede6fd02de87c5c677e3223e04f8c103d1862d9b61fb858e696db89ee8b969feebd80ad9b0431ab67d78fea541251d2f76cdc0c40ffc2c60338c8a4a8a0a792d612fc4adc7848eceb6f71d3e9f0d33c8e893b228695811f9245d8c38700f6037e2c6cbe49ad06024627aa0de19fc791a7f720fadb7311167d62e1a3721aacc385b84567b2f474c7e1cb5c98eb4a0dab6e67381566302745e9048ca81b3b28092b52e25f4011a6e8bde09288073d210930791cfc1af579ff2136776fcc3b6e7d0706d1fbfa933ed708cf27e5cf9ad7d610b8c9be9913da8d5795550220c244e1ff117e42377c8c2c505e30383b629212c494f4b129347e82c78fcef506d591e4675d0d22490c6c38d388fb861e4870903ac220296aa674f7bd92f39b6b520b76e45d2830d8282ed636bb9f9f1b51a11d3fb9b458636bf604c36f910e3fa427c0eebf2cc352e9fb700c92470439559803c79f7df59752bc1d199c678ea65a2c66ab916b9c50acc605d0264bf70c8e58dc7071c0f1d386a84384e6d72e59a6dce907f1bfb32cf4bc503c18fedb29f7a7eab1b706d1781ce0754ecda84e9b5aaebbf56621d4040171538516f8e4400bd179eddbf93c4149e93cab61ed0d557164b21dc19e5b1b2bb87c2c8277b22b5656b7e4ad1b9a4e5852cc9ba669358a556a1b05984bcbd9eb3514815f5641bd857bd027b6bad268f20b2d012371ae85a14f6b7d445bc635ee408b0416452de46a9da6774c7e195d8b8310a56fd786c55b2cf8d5de8763444b500ef608b6fe5f80563d5ffbdf28eea32ac69a45e114775a900618346013055ac024a9f5fd001e35b4f0b042482370f855e50425be08d8ba4727a70ed787a2126108ccdf056773db64cfc4227c84d0fdc5dd916e7dc6d23a17b16e8559cbc04a074e39551879f15c3e244625812790b8e1578c118424966e3c72eab688ad3f3852933616ed2b0ae7b733eb9e91e6c8b38990dd966739540dbeb3d3ba2baed3e8c0946657d708a4e65fca8222c6f73bdccf02d89948fdcc8ab8be4f25721c3a910030940c20b8037b8a73375fc1c11cffd264b7e7ad96c7530697f65a3fca57eeaf0f09725aee017268dca6116988322011d38b21da810d166085e1c7179fa78cb767ebe743f53d29b684eac50cfc52234e56de0ddd8d25ab6531ae927103946b46303208f4fb26044d1a31e5846edee4d1ccf3d39073a4907f6c7c0643bef169e33c95b33bebf28a49603a2d605c185ba70a24d9163e12a397b5164ecb0cacfb415081b26259e03843ab80556a03ce30fc4e8e53de5843dcc93ca476b06df33af0a4287aaf4ec8b2263212a39f7a250c8908a4eb45e142254e513ad0b1d0977d1498b226384557ea68d2fd003fcfe6060964c55f3bca3e462fea0ea46d7bfa4bba878ddc5834f3591e9eed1991dddeaf465bc722f3bb297aab34eae834a5361968a74df0055c280580e7b0a2ddad25f43414dd80cd141ad9217d0d8f9319dbb8baa549e55fae7dde6f9b22321d0810fea73b26ae2ee743c6efadc4ab38fd28d07c9e41f3b25ab2d5a737358b0d076c9d7a1eda36fa25bbb6126ce117e845b728c631fbd02878c16e5b63eb37d247b66163a37ad4286b59bd04cff93a0e06ceafc30bcf04773df482875c195ac8004cf5f733cd57755f8fdef4246183458daba369e74d70d898dda81893222c472bb007b54971bca53e907392aeda72c65def250033fc67b020a1c295e950ed748d0b1cb807a108d30ed42570bf1c8f040b4eb8e3b5a4637f864cbd0a8517b8f5b09b34f54a2efcac01f34670f460ff242b6ddc5624f8c8b7d80acad18597896c6454e5233e424e21807b47470b674b96d8ad1045e05361ba256e3190abf8edfdcb1e8076ee2e654d838f5e342275f33c18f8bb881f2ec0b957bf775d7a37f0b338cdd4ad07b7f53203b47bdb9386c04986c9b40a12d1324ac8c1b0b1ac5bc37eaec188f5744d9ed50f9179fef65b645abd05be8842f67a37e867f83be94619ee0f549a93124def038509e31b246248f2ae4e632a52ac306dd5633576190aaa4680b210b1116059c3ab078a7abb2ea9f91c063db22bfb0e110b790a478a9f0be585a6a8dd150b9d65a597d7da8d6529c99e4df2f1bdf578d80c06264d1f442ee0b07ce7ec2daf6732560f48d884bab129e2aa0514e33740935ac9c46655e5eccf2b4bd67a17b46254f62b4434430148debe518764fdd5765946c1be97293ea8f75683ee875cc6337178753e74fd9dfaec5d1250b51e890a1ac629f6ae771fcbc27e77e4445a33339cca89495059d0b61c3cc3793da7a36ec3f704e8f7824885127d3a2a2df7ab4094b8a14662e909226ee4f2909019e91b867a48f4052ece88dbd679d89098d25d9ac2626d4e9248448f3e9d1aa330c19f72d7cc39d53a983d08fec86037057d6471bb74026b6fdd4054385b6f228a3039df025139c8bd347c93de3d3e312e1448debf45891ba81470e6db6a7c8b1183d56456078ed988cd1b2fd0cd9afcec73a538f75faa019ba2272ae9c2fcadb7a383137e2e4d43bf00e165319d16fdf596cba210eef970d5dd4caaac2371c128c0f56d9b537866edd806c8d9f90d5cc82d99286214f4d9246c1d8b435490eea709613c8d94491a6005cb70a813c1a289cdc3ed170dfd2fcd2340293a58f5474b25d479a1b5737b46eedef0fc49bc6b3fdfcd7a0b1ef9c89d8cc6faa0fa17191408fdeecf0e455ab8869f49fa3fe0beef772f2fb29523baf4e06998d2b51557e6e3a57cf4a0ee4bec736798d162f983b404ca6fa6d6a6f105e6cbed68acdb3e9e6f867c57deb8633f19b4490ed88382d597bd70f43c766ceb4f4f7dcd1cbbfdf827961c104b68751e5a921570a1b1077a45b3ca5ae03e5ec5cc754d55c75f11f8fffb3b00d50aa8a55df096eefd2d1d0e694c22c599f3bf7ca90992d02787623cdc79e6724ba30b260ebb7fbaa3d15fbe7a64959db086719ab9647d5e56c6f51a1e36851ec46cdb29b07203e2148a95a3565af363332568600a7f8522ab84083713700cb8b72ca23360b73d841c802af089ae19186ccff07e8d626878d063e2f6f239529391eba7e5ebd76a3be5522d2a91d048d01fa4d1be9c998585da96b057b3708808d423713adfae7545fd98e683c9eba592a7c70ee83e01dc8e738b6301422ca432ae6416a0019f76da99232cc8df8732d94323295fb1f189d71bdd8f51a11f3072589f7dd4b88b5cb2bf954b618fac34985971f727b1dc9ed84b7aa427d95179b69e5294a8d13d94efb80acbfb94bca740c682bc6077dc63994ef7bc8abcf8676cd4cbd6865d31a03c90415b3a97c670e5f496f3f7d61ac72f91bb1bdde018441a16e6ce806a953e3525bd86e1001d18878850a2574dacab16ca3cdfe3a2c7b71e8171f72a4aaa4106dbd68cbb6ea959a22b033fa7ecab71e5e5a301c1ebe46d77df5b52af3c0d0f4130963d73c858d7db7a2ad1e8954a2a64e7e05506c914a70059675a8f5ac6c4c77c6a9106c567a6d4965eb7ca1b1c3819dfd17ae99a7006a150f6484573ae86ed39f1ebce0c7b581071faa46327ca88d829e6b7cd48e63a4173667fdcae0418b666c322d00b0a53d1b50b44c55c0507ef1f2020e1d79aab28d2a67d6a371848205d8f16e07e4fcfe066b9778c650eafe419f54ed66f5f1e85f34ff52c10c23a18b9a13c7d52dca76dcd1a91fcf3c93ba411110378370da21e13eca4689d984f0799eb13ac3550c1ca06e3c9fc1c1d9aa4740e6f5f3c27c7f4c52dc0628ac7738dea59f0d05f2614514cb5c1d13262244c15ca9da425c5ebe8846e479752443ad1c6d40c914baaa5cbde917a0d415eed9c2812955d55705ea698b04c37751ddc0404f77ff317c05dc7255374960a3f53dd18d59df9f5c79f60baefb95f0addc3a50adbae166b649c2b6107d4df808514d51b8248b55af7950dad54caa6a9ea13f14c8ad504ef467476a701cb33e4d21c4acbce840b2bb5259a09d8d9e1cf8369d44c772d109ad921c516e15c66ec46ea7d44e5d48bf539f5920d76c875ab86bf0f5bdad30f518509a0021fdda052fc2cbf2aa30093d39d39c5b998706bea9b870d887127c976dda05f6b53a6ebcb1c3a5dfb5fd26cbeb3bdace7764524be44d0948af537986a4159c23877f89a8c602f70b3e51519a4fe1dfc9e8281bce4400e41fbc90541e423d4262f7a9e0bc7b5a56ab6d502fb9bb764b03eaae3454827791fb842cee332bd4468ae07198da0850577dde6ab8ebc523751c9007b853990a74a7c0704aa111eff46c5e3924122a6a999f90c25e75c9857c33af1407bb87506357eef2e64a691b7d53df2b379f71318fd1d3ec3c674c27f55486328cd84f44e0c158b059f6130121fd8db2b0734db1aaa8e11fede24a947d049e9557c28696352dda02210643b4238d8ad033da3646abf52a7578dc081c7995467d28e7b81aa641976acdbf4d11172764190e18a2f68f6fe6cdc75c9a1985dbb784533efb5646a203f38906491dd7c700b7359fa4b3bc5252a61e146c6524d547be98f814ad41859a8f91873b093ed0c768ca83127497d907b06dc0d9c3f3fdb1a09d0ab68f18353459073c0c295a97434f7fcc6d979eb3c475200055dcba815fd711ecec47705ebeb225aa54129baf884cf6951220fd4575f86442a80a9fe36b33bb8052fea509adc6e61ce52468fe6390ecca478aa4ad6ececa92e32f1d9a9c59c5b0afa5bc44d73b784cc0b3552b99994a66f9ee9ebc370bb44507e20664f26cd521bf78b5d536e0c0bff2bb8ab1f10236a44e839ca00d4ae6ea3e3f9cb824ce1168dbc0d034fe634b2ac859f5f0d8692c539d1607a9cbb8131bb7e8930c4570d597081cddce2843ccaf08ad325d523887fbc750e47826eab8c928b6638649511aa37d6b952ece5bd40f1e9807816aeb5f51cb25e18c8b5bc826b6cc0bec5ac6f7ae4462e28504ccbc52041b33f8f49519b942ce4a1133d395e0e5e07b2b5297633c5576d415e56222d19497e5e0f4a5c7d4e4f285d9a4de36288ed251349345b869a8de895cfb726bd6f5c5e414256fd967502f10ba35cb19f910ff4b9baaf1fdb6f801c29433e06b50a64b54468897754442c4ae0d93b9a6ccf81a2b61052fd0a34838538de31a0180ab1095a009e1be8fef5f025e6598dfc98c6c5351d0708d2d7a1816889cce5a42ea4b6e0bc54ca6d81f40363f23c8d666143d799e37538c047c816b15751c727356460f9b1c98efed73afaf9a08ebec32e020c3b1e0445bfb3eaf23ff5ded1d1161c81e7350eb8741e2b18688667b9c21ecbd748b125ae3abddc9fc341ecba33176201a42d4f8d1a82d3e9c8adc6126e154df472e20c8a48c20c34b2cf04b321530483c14043df609d6428292a233b8c76d11487278225ea80c015acae14184a53983af4583ce084068f889193c728ab9626972234c7a85d490814621fe901f3325a0354f48d2d6df38499b4f43638b753f0261c7302e5e5a14f2866258fda12200f13bebb09a19cc1710d19f5f5e5a8ddd27dbb16c869f7988c9c387fd97178643992f72cc498480a33c6f1b7997e986046773cdb5b2fe43cd808a97967172dccffd69e2e82cbd1b66ff591bb3de9c56e4837e1aef6f94a39ff9ed0b26690458295fefc71369eb1512ba338d09391636b4ab7623c6821f35f3fd9ec576e7b12adb8003ccfa6505e1918ccd84d11a007512dcf15bd23b6808cfbad7942deb278578902ecf1acd89e808dff330fd405a8e24f45853b7b4817abe48cc7fe4f1440ecc3e5d87189b905837f901bfb9ad6606198f9ab4428e0f54c4cb33a59307a6a0f0f70ae0a0e01477eaba96ba2fde62b655cb5fd1362041910964a2e3ac87fe7921367d385fe716ab49f3dfaf242b2c265ffc9b0289c3ee9c2b6b6a9d6b7343190caf6c7a2d3c8aa5d6f5cb186195414f889362e396f032a653fa771503444c66f7fca1fd6021b1729365dc1e27591ab0a7c58ddd7a009011bac8397e1567c6683f82a60c5979f69916c4c28be511453b940e88c44643e88a6caabc6725103f54389ecdbec3c3aa63b8c4a4394057c5391c180e18de80201bb0213129dcf3d2e5051fbb46e8969282418c36846564c9c85080c80aedecc4ba8944821efd0610a92c4678b12421a938709354fb6451a58f1dd3951080ec7224814125e6dee995e7bc6dd20470a24b10188d7e03adc70974f2ba455fb57ba87dfd22efc080e4e5f155297e89f03bdf639e024416b210de5f90e04ff0664dbf2221c68c95f8e7eae3042f1625338228f9d83a3fff56945a0dc4b6c207546d236aed277143a78e3d9da9a5ffd981bb410355ca2d2bca7962c55ef9951bf784fc86cf8174e79f2005bbb60b59876cad729b7cec4540da77a9c5b5277b1e38bc34c67ccf8670e1e3c52d7c7201caa28fb54ccda360c97a4c4798d3e95bcc70b13f2788bbbe5fa33b25499636ae98c87445a7d017492309dd66c13d4ed765133671d5cc432fbba7e95a51aeb5e8b4df5a8a01b787f2b6698280a14f58a82ee785589ee180a05f17228d6f6568012c5992cdc309f503571c934b6003c1878b99c9b1825727a2cb5313e8b355fc1391171975e3a47faab073d45a4cd9e6fd44f8404df5730942954270c2c35bd5d0c50cf6f8c5531019bb5111e6b23866164dc872a3b068e3e210f7d6396d4eb1f0e09ae38a529be6f0ad5cf5cf28a16d110335dc1f88b2dd04c5f302c235436f6bbed544acd989b0b11de2317a112e89f82ca8993a52651fa601ff57f8243d9820f296e82174e64312171ecc0725726b6a1adb532a51f4d05da61a150c1294ba076fe9f93af07775621d96d4c214d5a0cc5f0094f048e7175286f155a2faa8e7eae2ac7dbface32fe4d9f65c8a97e59b364cb02980151ae2e3a4235a189e72ee25f0caa941fe169d370243e961ba66484d7dc6c98c10db039f3a4fdc4ceaaba9fb0ee4174b794f6b8039e730db1c3a1dc2ed7d740c54eeb65825ce528953abc7bc96ce7884e4d5f81f7f7ed51c8dc4fc5ae1f41eaa45b9117998533aada565516d7e33c48d14c68b7399dd2147623b1de6306bb88e4e81ad7f3756848aad3488bd68ee2efe62c7417d5c9d6d6b83c2938c3daa8c0f931716ac2af32f280caa26331cc0e59963460128143b06a88fb10fff9ae4008618f38fc696d90014949f5b0d833ea05c1d8f9f142ec6d5a4346523b5e81c47d9f709c6460cec452740ead057efef15acbddabd6697a00a3e58c688f805ab0194b21a037a7e691e8d54700a9d237303fbd4bc313da7cf72a5a340bc86a0e8e8986bc591da69c2914fddf6d31c6e5740389c8f42e92bf7b77e6528c68b585116c61044cddbe90469bb0491a1e98472aa992b40f3e47da4130ecf588f17ffcf53f5e814d02c179d1cee009bdf6c26703927919b97a30bdefd1dd03620509ca6f19dda0f9993a2a3b5aa0041923c4c0ca2ddee43fcf475db921adb9591d9383458134f35e7375fc8707111854b4478c565d4f1a2b56adf818df04db5bb8fe73a8a23c54d6fb07487b86138894fc8c3aeaade0d5256bfa498bda0d80e99b7f133c3202411855f9271b6d20bde5439c61dc2570e7ae2533d50fe8f0b57d7271c05034012d735e00a30379fe5d476a4d7f4948b2351d6c02e50cc45d4b5566d66b7b7d9157fd582d99fb0af30556d0d40a000502752a42cc82f7a9bd8baff369c13f467e1c252a184d8eeddca334304656e41b33aaf105b61abd679c5bb458c53fe912c749130c5b4aa47ab9e1ea5f6aea6ee201a71e0b845a8da0a619c51e1cc3733ae625a756bd922d9c5950b1cb12374832192774b3432bd204bd1b6d41c21c5066d2b395224ad0aff6d899ba6b7aa031ef6a6a98536f22f2a9b9e3fec98e0272431db0e40db02a5f0a4250cea9b04aff057032e44327503ec23fd652c6a13b6647f96fbc76ead66b53b1fc71f8fa04197663eb00c59b72fccb09421b819adaaf86ed259401ff3ebd3c789cf70b710d800649a00e9160a3b0f1732e3449f394e1af76dc4e67fd5ecb27e3a96c2ca94516324e118b85bb1f29ca4acd3f0cc4646dfb08b7543ee769382c879e8e05f91bb1d32272c209da0f4c665a0ff13c5ddaeb956a6a7a44ac95544e04549369468a9c7491689d3b75b983fe8a3014383de67e71e31ec4aab86ac24840c25e2f135080349454f58541f389bff6c5e1a582a8d3558408fb9e990118d66687827b813d90fdda00a22ec3cedcdc2670b4f446e2302f8f957abc132b098515e9d601f0ebd2e011e1292851c1f272fcb4e05ddb019a231fdd6ee567e16ec64f601aeb6aa5a05dcd8c5dc342a0a16b2a170697ce83a664f4d8b312d1d9059ea98f72c3df5b05589e794a7500eff356a494c5d2036ef477168da76593d2e7cc6af603d9792e583d7468cb4983005366205fd498a8147329af01493f6cdbb02c76142e64f24c088296ce312ee6883301c9318c7f764b2c47ac283f0069ad24ab854e54834597546088beef24e73fc3d5041cd5215443e4825c0739732504c270c2df9cfe15d271880ed4b51e272534fb04d8680553e56f889f62f2b525a44f125ec42b232b840b7601e2ad7fb55deb52dadb2336c3318fca8c37a1d10c13bea8a90597bd9e2c8d805680daa9aa979fdaf8956a88e640807aec09c158ee68f4335a8d15575ddf9dc0b5982c1551e85f187643c81f070d737cdfe782e4e49f260af4b57494bc69a8fed6f0d43df62f335c1971ad747a5b301b84ce96c13a91f0f4f36c04028008d4d30f64e1fbc389082f74f11bf6a2b1dfad3eab21f978e9b2552dd6884a53aea66b40c3326dd5ccbce499f834d3150ac35428a4a80e08201fa9a36e90d14d3d012d33456ae98712183dddd30174d563139ae5838cf5a5ac45f42284ccbdd96557f862d67c1e93c4decef0b23595d3944613b5e412186265dccd398af5c44ebe47d2539b635a3d08ebd0175ec11da186553bddcc6895457f241e804df37dffc06e45881d30842d6d2b485949f762ec5eff79b2a6badd88a267c90007da30635958661d1717296901d8e45354ec6551baa43c69bd148fae2caa5484c9c80968354ae50427d147c178923958442c387f889cabf9605e7f929716f622ed44df41b65263a279df7c9ec8f82a98332d30b1c0a9012877e0c7cdccdce2a8c7c85537f561b7c26f03b5916250a05d9047cfbe38c9f6fd609f7fda275c29bdbe7a673e6d4000c88b8a2a8f2fb0093aec388e756a62df94de7fb1734bd1d9aa9e75c7989013761df397c199c3e597bf2f9e3d9c3ba19cd0cf96c249c745ebc991ee8d7a7136a97cbb4947039f2a84775132fe637cf80e074afd34bc6d35b782fc7c140cf932ce0e1dce74ccf53edbd582dacf14036d0bf65a33516aee24726ffee926bf0259886afd00220bc1ae529855a376b4868d358b9067d9c4b198b5bac00cabe5fbaf3bccdb3986910be7636b358f4d1ba0feb01506f7da65f56f1ac5582ae5deaae564c10b0b8db8e001c5375884a42fa72f13b11724781632cfa09008400fb4e92f8b04181a4c65502c635a0b024a030b0ac18ebb8d50a9d06e30af50e417227e27f4badfc97d4fc647ba4c2f0a56345d1f84ee42a9c27db211ba6a5e01c26e119be4b29c293b299f6b047c0de4a89defbae2d2be11338c10d620ae5bec05cd87084dc469a6f667d223612f13c6c595bc7f99bc706d1c2c4a2049e1f6b41e6e8e0f3c5f04891cd4d6ce791a2ed8112457e640e66d49afe1da3e2ea92e385dd901688304bd58c73cc13f203c1cd4b11330d246a1258bcf8150586d185f99b32dcf51032cc479700dcc9c9043b934551baabd13c90a20656d4194924a6cd3d17f5d3d9f468afe61bbe100cc0598369580ea261194f8c573b8f225bb86028bc01c495128433b18ed3280bedbe74ad158b791df6f7da17418a4a949c782b153e057160cca019f9cead0754387f95cc9e5eb660691f0814c2f68de3e313fe99abd7e6399529b5e9c6742344a79013a22bf853587cdd3c298f23ac4ec4082875a9822401302595bdba98d002078ffd9c74b8c5ff898fadf85b27f37b1d7d341a6507c5de39a88b43644ad777945754ee5d220e8e4ddb24f3c2badb480a1054c013db800f052788ab90986fce4f564419246b070c668fdee3411c25a6cc48d640cac702eaed6c042bad74d97903a5b51d482b4ac2bd52ad7797a11b6d5f368ea50eacb07a3255633a6aefa1435acc1d900a28f23ca2aee3593cc0902687a65f384fa5ffd8be9ed04dee65a11d344b3f70c4291ca9c2cca59e2a8702bef49e23c7788fcd5570c550ec168522d919217e7dad8819b7449ad622cbd528af7e69fc2ead320b04800382dff1e24b908d9689c6fa265057a5d6e79a6306253dcbba3b08e84d56e71910d5d51876330854742420edea075d98643d7a8097a92fa751488a265c9d5094e9e27bf1fa87f5efe028a2329615424174cc8ce365c995fe122c84102f8250ccfdd6bfafb7c1c2e9d2162c697cd73de0bbcdbf25844a3d04c565827c7a9e2923a85c2b81fe1316030002abb831cf812a84ba954f694e2cc2927830e998353a2471e3b9c1759459f3bc48a3bdaa765d58a1f0d7336d1f8cd0a0d3c0c8fc03f92260e001c90129f67c1401b09a997ad410abb272014b297a48010f38dfedb5a739548057fa495a471e1d092812f541357290397531bfcdbc45f2bb8710f42a91ce61c557e91a80bf1c2fe65023f9777e03b761a746734ff1d356e162b40cb8a4b0a3a7cd64640b084398b9de83011ebb0b6e96d05e0a091f0b5c301c0ec6bc0ccddd78e0773a6dc9cc6f60603fc1f900f647a78e22810bfacdf901959288230fe828d7b146a25dc3a109b7a1c855d50cb3566c09d31ececb440708ea404c994d9aa45709aa5c7e1acd161d89ca39c91f601a80ae49aca95b8ce623512430222d2e9033ced9a0256c2c09442bb0231cf6368978d890c79a8a2579188d7e0d20f3891a6094a46d8533368d4a82d4e4cc02fad931645d408a59361db0a263280ba7b6540baff216efa5a6f3a30807de336ba040127337f02e1b853335633ac60e8a512bbbff18acac3b5c37d51f40736cdbff6db81503c087c2c69a4b38565c5bd99b05c7d3602822126a6477db724b29a594324606ba05510662a05075f3c4b54b3689c93239c19d80abc29d809b5242b517675c126e15e29cf3ae8f3381d3123784d3f2a3991a742bbc7e4eb229913d3540214cd4f5fbcaa35edfcca0871f86b81fffc4ef6b60bf5affceaa48c13a58414c825703f54b9a9b34b90dc3300c3fc4781c00152054fe896b7087c25e5686e9b2346957bfb634695991c04480b03bfba70676671f8742982c46087ece3b14c31275db9da6ba34658f4f3cce15638b71a5f9b15873117c522b5eef7af029323831f6b73bac35d5e013cddbd5b6697a1a2703b71f5a1a3600179c5e75b326e19af0fd269799e93213b88b5fa6571b7010a34958cf950cdf9224076858cf5a091f638cf10933a8e34a7280d7c31224eb10efe2aeb8f8e2e6f86934d74a5a49d9d3487317d47369029b20ba2ce4e591ee01299090e932a55f46e28db342aa4391bb910ec55b07c3b024b2bc1e7e0a9a410f4ff430b43bf069fe3e2351e46e941fef306ffb7de2ec392e0837818082848e78bdf65bb6c96d77e1d34871b43f61b89fe89a1b97a2a5595c026eea681c001520558e46dc146eef5bfdbadc78caad2c904037c5df75d2b5fe4f97e62d8a5e5b8a29f2c2ad8316575a4e888475098306478cd24ff6928014d58d1d4fd8f0d88a7212a58b0c1a232db60809f122ca09170a51aeb48471c116060b4f579494215036bc62c850438402634400e1479616624a0803650b7324a7e718a2eca8a46648520b213300098d8c39f2420d5b5d9e7c8d85a1b1f3254a009d627d5de9399d627d5d8d4b1ec7630802f4c7720aa050aaebe708b6ddf15c721dd452f1527d15b9476a5f3cc0b0f95ac115f585e3c3218c1ac1e01082a1a1df748a05e6fcfab4feae3f423921755d412104a5215ea817196a90a185075ab08cf1fd8108049828b1c8bd6096004002dd32febdf9de108633a3df177720eed5e5bd608ef47bc158f57befbd392a076d9d90b9eb1607baa54cb7bf75405758bcae3cc01fa69bdce0e32dd4ebebacb3e07fe09bd8087cb0b46306c512dbc73344800b13a6242c6656c8d8a235e9d102142952aec068a08c182e2af4bebe3411c95d96386181218407151c388ae85312583774617999a95fe6e2c53470e0a2238727685c442932c71c15153118585a78b2a528db10f5e51abac9f3204a8948982c3cc08ace103c5db171b2e5d4c3ab899713af2441d545155c5395fb658e5be2b8532caf10745da4638c31d638b0cef86bc6e245a4dbc7f20a72bf64ad1ed6778218ccd070440b9424438accb17791e0f5d4f3ee790593134a08a2e584258433451f72dc2e1db419e3f8187f59c3e39544b49544a9298411c26008d5805a71654312293abcbabc0420735142448798172914a858116e3ce98232616bab0a4e94714b8e1d292f8034d1e89185890a241d3868e4f0a42442992f58b52e156eb296a0dd65c21eebed2f493afede7e5f56e818638c77d01d22bab7fc6204c607a25c7ea1728163ef9d4eb1be78f0a5452fd380c59584e5a5e98877e949bfb13fe997ab855bce92df0cca4fb5f4fb78df7b332ec72575f5965c4ebab563a7585c413a4ea7585c4e3d29a43a5db5d61a07d77aef764f31a8194245cd12225a49890c295b8044d1b155a49f6e2c5d6b9d4b119783d38e7b8f14b7cda5b28017171a70fc176bcb4cb79689959292254fe8531d256fce546bfa7d545f7d5f139192b5538a5f246dd60ac254b58f0ddc04ea95005402da2b26753d26706c77660ff5e226ef22a5b5d4f57c0f198a6956b51eb092597d20bb322bfb815eb73e48b8d9c3f1d554dd3e89ab4c878f0a3e29f888f109e353e553e5f3e593c407cc078c0fd89eddebe3c557f029e3d3a6de7befbdf7de7befbd63dcbc4e216cadb55a1f3357bc4edbf6ac8751a4da537cc4f89491f5424a29a594524a29a594524a29a594524a29a5949234289a904ebf3605d5e973d2b4463a7d1bd50dbeb9b9b6de581f76473fcccc9a3033d68545023bafa09c57537a56d5a9a9f2a84eb5a942551fbd32d9adaf4eff76d12d33601808f16a3785e8555058adbd17e35cedbd18e7ac71d6fafbf606655826bbb6cabe0d823259188ab3d9b575260b457136a3d16a335aadc6b9cd7683837170aead383738b75b4e8e8ecece88c7f1da3ada6e70706eb79c1c9d1dbcb3736dddc9a91fc218bf3d27ce39e78b31bef862b07783c0c0af152d771162eba244a8b6953fe8f1bac9eb9a7f9bd2202b4dfd3d39d72d28e196954bb38c5b2accb64e4cbd8c1f0f801f2330b2811fda48da8324af6fcbd26d2f42b995a9ca227214b91e3e7f3d9bc0cddb6c0f92e6d84110acdd6eb79b8ececd83b4bf791b6992e8b6d7b308806feedb9bfc763b1a8fc6dbed76dbd54a93c4ee569a24fa0dd1b5e9fc0d69de3cad1c800b6e7b3e019db795463a5f2b8dc4b71d8d4763d7991d8d5d1cc5d90e00c671676767e7b673cbc9c9c9c1d18065c60c969913ab8c901a698ebdf637d224d16fb7ce7fb4d99e3fcf535b9adcee744a1c92e775746ca599f3b7c7791e1c5e9a620e691e8dfde6c5a7ddc6dbedf9df663f0a91186b3d702d70135713f769c913829ac0bd529aeda7bbbd38eb3177bb5013712c7053a4d9d9157198651abc590fb7bcf17ac3dd1439ee19b03b91f384b85fe2b2b84c0b4e08b724cd17a03d63c59f6dad96841c06d673b628be6e649f05daebb5d66a06751125adb5d65a6fb64fc3be7aa3703370fb9a2c9ff0ef4dbe69efd3a5482b19bec513d88ef9beefe3f1783a0aecce9afbc97fe2e183cf49d1ee6a95f6d56a8ee62edcdf751847f87d1fe500edea46dfefff489376f5ef432bd22edaeca592be2807a80709d5382995f3361cf29fb86ce7ed07dd219dbfbd0de7addd7d6fff86c43d76f7edbedf2177f6e77b1df286a45db60bba4337dbe7bc8d1c89eceefb1af9c1eebea7cd4891bc0003dae1fb1c72db9f0fc4f7b6cb3ede8dcd59f0fe28ed9fb8ecc1a791d8eeea177e2f86bc8f3d9491e63edaa5f83607a5f4ea2539afa0945e9db310100478bc5da308afdfb350787677f5429d52f55ee8ed1342faf404eee67eb3ac65c813a2e29edfb46fd6bfafa287254f08f1c30f4b13f7f088d7c557d1c33d9bfd9e95a018ea50872525cdefb70f84dd85187fb03b9a6bb7165b4bdaae6256b118a5988876a0b79c33bf406775a4eb398b1b638c69883b79fd6c0201d9dda8690092e2f5f18f8f6915bdc2a2571de342b0b3204025a8640adaa19a42145c607f2a2fec0bcc0c092121d5ddec10102e0a37753dd6437e59893b9d81b2f248ec1bef4808e901f8ec71af15b043d5eafe804375c9fe804f9740d86808d61d1d7c73a853276a00f0cd04c868352110cc2008fe3bd1d9cc8260ad2008667097486a153885a4825848a5fa3e9b26ff897f8f03c29dac07cbc396e067b28620885e68b30f860fce6618d89d7d1bc9e9590b1abbec8876b056f6077c107c5c0c20f820f818800f822078e2ebb3cf0f7ebdb21e660f3e686e502c8f686f867b26290882571d7c10046b4fb685884a9001ed50dfd6847a82ba8da6c9daede66d592985f37a78adee013d48c874c9ea22ecaed6767f7faa2c68f64fdcdefcfdddfd1bd978877838184084bfcd23dad565bf29e085e215657f657f81ddd5bf35fbcd08e901b8f81848eba7e18f3f848b232e782a6ed2adaeff9d9c9058fd15ca88f5604b4dbb90520aff08fd7b739397d48a7aff13077187ead2eeb89e1e710106d6c39476f2611ee92a9627b08e76b06f7155b8d6afb343bb483f90e5d921ba755d8ffaf91dc2d58a5e4d290d42efbdf7664d73ce3ca46ff5678c31c6a62e975f8568074a9172ce99524a714a78fd295128256fad63980c14325038b82093a5e3408543530faf98a1b08fcbc3765d9696c9ee3e7dc2561c5d9ab4ffcc6d7badf5be43387f67cf1f87bc21a9fde1a5ae81b87fe6aca4f571ff4a23b1ac54b983a591fdef6d69543ff75c5ecb24bbfb2863125d7f88b19b243a7e22fbe9920323c6649ac09c3aaeaf2dfe70369b801593d59088fb73fffb2a55afd548aff85baa6199926c8d24bb44993e9d499cbf52c41d0a4b5de252e0e6de370c53f4ef3159d6a806c09fbfc4ed39d32c9fc234e7d0b3a71fd607492babc03ec8006a2dedb1ce7ac06535f98cbcd99faaff96f4be6eec58939b946152f696a45b3756d1e4b7be8155115a3c06a7427d5b0377858a9b2f169685736026cc849dbe7aefbd39056ecabef7e2a678c0f8daef02dd11fcbae2a8e0a6e08d337202779cbf7edf87f5c558286c824b4a29a598528cf1d30e144785d7c77705dc145c2b16ad8c315d58604958013282f4f8ce98e80308288e3b3d6548757394f104e49411820b1d104c767a2a8d755aa7a7125837837a5e4b575a0d04b650e9b86105275a7c88b1ff63bafdeff45482a1cb84a0c83c5e90bd7956542888772e193967aca0e283ad2d1a96aca055e40b0923cae9c595274c5496dac86828d1ec8c1f1f604383eb2b045c8298e9c00213a9ac0e68b0a0c25219bc9466d7a81f5ae94ac60932b85825c96edca0554d46929a703a3d9544d8147f5709049c801c0e59a7a7d20dac1eaf17db3329cb19a38ba9c5928b5349a945ae49a7d333a94cff3a3d93be2c8fdd3c7a7b20faf44d7a8232bbed8ffdfd9d31b23aa5d269af7ee91487d44ec0836aede30ae560fce0dd7b6ffd6b6f162fe74237be15400b1c5e43b068f11146858f2e251c2841d3d4e505346d483361c02e32d186a91c12b539246ed2ac1c928a5ecbf0deeb0363ec542bb5d626b5f0810884dd552c9b0804c6328018dbc2e090d5c64c0e4ea82a60021634362869e384020ab32b5a2ef3442312b46718750ca149df34edd70c624d5a59be3c4a8a08ba3ff449f35e59bee41def0f3573e65886c94cfbadf5fbbeaf04d13b5740357179a186534ccd6692c534a566042e37b498d1718d1c1c6e9d9e2b98f4b0d37385112c6b90dc211d6a9da5a52b120c285847aa54611f05681106f7c77e63112645dc9fefbf12047a97491e0bf07e5c6445ecfbf37dc6ffe9c7f9f36ff2f1a7d840fff7bb44da40ff677ddca1a93b841370b34454d24ed547a2f7c782fc3c16e0d722fefe58eb638aca5a2d26dcb5fae1cb4610d96a45557b6159605a5df4fe34a0f25751143eade4c13f0bab53b814615230245e9de8fdb12045f8b2b7bd05296a2fbe7e132546d8fefb11fa675f81d4fe907f1e217e7e13253a507b891214a045b4a7002dbaf91223782c0069116d048f05b4c7799c120445376503acd3fda96f2339c953145e9202b4c8056d6681ecc1bf234684236a25ade4b93ffb906492913cb714b1cb71a7491e5b8aa0f74745d1063515d77e3984d4e69274ba5c0c8c753468470c5f8c85ec8e62459df0e54898165a1833c379456bcc922d688434015f00401581978197980b2c98c8205364031b36ac20e6e403a82d45f8e953c7d8c5908e713a2623ee746db476380e1d12b236e372e06feaba79cb235d1f65a861f9c86f4242bdb661a1dfa5a27bdf14e2519053e2b44eb1da20e926e740bbdd4648cf9d62b591eae6ee0718ea0c06a60285135e844541534585895882093564f450b092c5c2e24fece67b2b8b34600ba4104c9f2cd0e460541d501ba6ce3bc56a8324a64f165ae70a31b46e9e5e73c01a0bf1648cad11e2f61b0b73f182d3ce81763d138ca5e9d18d91e943c2efdea2be3cc6cb97a58f795919c6a6248df55cd800748a3556c2988f6b73001023a2f560aa08ad68c11b1b243d36561f1b2f6c04a0268bbd564b0d143555bddab0a690c4c9e54078ff8e76f7a6ae5bca43e9bda08a598b91c4b8acd65203c38a55b1dc5a6bedd34b815939e8367f757b827d9c17af4fefbd379752fa892c267becce014096eceed36c8a006ee6e70921ebb92c51ebd8cc2870c7cf13a2fae8b8a4567a5027ba745681a55862553afec2f363537f36c58cebe2fa737e4cda6ed551e81906eb286e6aa19da1d02295c044d5ea28c4aeff3e8d4a60a222f5120ae8fa3952d7df3503e8151252d7a58912140c09a99fb8e9fbef3d21d2d90432e8da440905744b3be03fa1fbf761d5a489ef4fe80760a097c8955040afb40306c25862533ab51e36b6b69fa8ddea24d7de7bef0b1845ae7740c5eef5776d415765eb7269adbd449d7981b9caefc2659d628581e97768d49aa1e30abb12836b0ceb29e9b870ae53ac3022613dba191425b41ad3ac8069eef99b2f2398a912a1832f23c4d022064292121a5290dab147e8f8c51b3ac6d80bc662491d63311a1de31e3c2f23c739ac1163d1f1045eb4d82956988d5c428fef1f7bc294942c579ab1195c639a30185c58a701d3cd20233587af30b1344c5870a5a96a81e3b2fb0c589a291d778a95a69782be2159228599e9fe0ea8e8b79646f777a654025d9af5f1ebd2ac1f3ed1ad2169e6ffd0f4c43442ba399e01eb67ba2670bb59bf032a6ed7a511ae8fbb38f8b49b62075f87c8f9abd1fdfd5ff8cd2af8b40e918deeebfb9934c26f876a900673e6aa17a053ac334d9b92b0dfd7cf9852fb192c670e50d1f77e703f899e5f8b381a65f6fa987a1c0d4e675cf46d06095699af9c9dc7f999a913a0a614c85437c71fa755aeb30a34ad207f9fb7e65ff891fa3319e2d36fa4f9471ae5cfd9e8fead8c1ab130dd993367cee86c65a3d3457d9fee13e185cef8da1f37baa9b362135ae15430c746a7fbce1f4bfdfb5efc7468f0184fbcf54c8975536775c5aa7cb045ea0c8b2145a2dc68c324480b2b76d0903165634fed1095a6926452a0d4182e55034d8a8a88f34556833d49603aa17ae4d6f042385528432687d20d3a552bc6f088a1e3498bd4909b931b9d24a5630cc9f0482d8552caa820baa8a69ddd26c74bc7ca8d0b270927e4d2e4c6763a3da7a63a003a3da7c61a183469a03eb09d9e501eb410561bbc0635e37c32728e607542b9f8748c1c97eea7d3f309cbd30c2598d82640c8ee68ede58d077de1a678ff96b79c3fe772db9d26ef2dff156ff56fb5d6ce6f2fde4c71e409f195b8e409817974b6e57f4bfb73ce39d775eb9bf5eb572f2fbcdce939820a61a7e70854a68dc03e9dc044edda84ed9934b17d7cefebdbefcdf75e1eb776434c25e0f90136842da90414df6a95ea29c254b72610d0dbd34755b7bfad86d14d119bd928ffa67657b1558cb3d387f1d5e9ef3b6340fe4b23703f63a3bfe52e29dde18b459b33ce240a1b819e6a00fb7a96ab81db9e3a01d86dae74c600160bd0623d545a6b6823b8f6eb18b0bdb97256ac58090b3b7b742959f2848995524e4a4e4a4e0a0c30c00043aec9b6b9269fcef83669926308734bb6cd2df974c6d7e696f05b5e24dae163a1873326897a58d9f773ceb9347944f94d1e05198f39df2b38f7987287c09c6f16987b70c939e32b370848777af680e196b9b4f6b63d48d871b3c61e1ddc6cd6879eb307938e358bdc95170182980e1123b27a1510d103a18b8eac7dd3e909420c3da7d31384aa514b2be7a4f62fb02f33605f6bc2aaacaaaeacaaa09c48b26865d9d2caf2d5957531ce59eb6fe3bdafad3b6bfd7d7b83dfde202893855b0465b23014c5190dd368d7565a288ab3198d560331b3b25fb3aae250ba3dbbb54c76c93631d925cbc33ad924ad2c5b5a59bebab2a8aa102354554815923a5591d6541715694d5daa491a605c1369f6629cb3feeef7615bbfacbf6f6f507665326cabecdba04c1686e2ecce66d8d699280bc5d98c46abf1cb39b695cf6835ce6db61b9c8b83836dc5e1b61b1c9cdb2d47e7eae8605b7564b719036aefc9b9a4a9b3f9922650b73bbbb337820830feda712e47bbb31d9bd5da7b31ce59ebefabc24de0ddb369b92b7e52091e014eb2f48a2b3d7254383d794c9d3c9c7253383d79249d1f2039792041c91b47b956cc41e119cfa8adf4454de69b73dec5c0eb87f5525c3e5ec234e47adcc4e27ddb8f688fe756c859e5b0722ce498e498e49ee458c0f5e69ee49ee49e540cd66dbfab71ceb8dfdc139be3927b527b8e0bafddcc61e558c829c929c93da921e59e9c2008397920c99de70eac1d4e72597ac5b9a3c9b9a3ca6e000992bd6519ef6c4b1073102f488817a63b4441093962029418bc3c40d21ec0a045b00384155a391c8162d248414f328c3f80c8ca1f29ee5d72bbab1b470e1ad72909138d39d9143e8040727f68cf447768df0dd6314e41e4207420dd1fabc531c660199442500e1f39a63e102151c23188bc7350e51c472ec67b6807f19ab25ed72442138f5e0312d60f2baddfb29e3b5cf4b08a50fa25afc51d9be8f7f4a04905ae1f386fac396f28710d62221e05392a8fe8bec9a3e0ea9bdd55a9ccbb431f6c5353bf43b2143895d12ab7249c767a1504f455cac784a5182f4019a215a6c8dc69f06484cd112c165d382832839a6c54399b7e77362dddbe189fb601a5832be78d182cb478da1853ca9df09d364e1b5b36b274f3f64c4586f086e838c8aab105ee6c3d00e94e26aadb98ff8174547c7780c4feecfd59cdce0ea2883d3fcf9b08c74c53f767ecb13ff53908d373a727075ddd1ca3a6a674533c9af3e0dbdfb737c0614c9411b2540102a508192c4e594a72984253f4b9b0f3586e074fbcf58c9d9e35a0d4b87aa1b5fe01e4c51495146af0983ae3454347d79acd8f29ad8108e57ed4102283500da95c2b225ae11722e81f55be1ca97241c618224c3d2d5e609012030a5a376ab8f86accd0c96ba2442b3ca3c777cff8686834b68c607961e025d8682ce1349e6433767c62a850108f460898f7e3879e2143b4c2313e7d6f638e04be3b3d67b0e8a6502e8a7f9d9e32d400ed78a7a70c30dddcd5e8199ff70373e11b7e2c65e5252da0d00f2b42ac36dd326478014406d60c20bbd3339b05a8b42eb6352d64332508080410004316000020100a8703e2704894a561aeab3d1400096b943e665034160723914890a328088228088218c610630842c838a3d04d59017debb7716240c46e6a0eccc80ae3a432422ef572eca94e4f1eba0b0b5a15525d72a92ec6ae6e1890796ba6180fa03689c55555785cdfd62fd74b0bc9510376521ab0d9b1e76761cf1ede0c23cb7709b0d6bac4e5590c8fd157e82058f8273ec4657fc58876fe9348f011cd8b4338cb4ac21a70dac31cf89a90ab308f59559a43775e85aca75cba18ed30eece29fd98a1a68831498b6c94fe2344649e46f797f5b29f48bf2c864b4a959674e40fd8757f689d08b2725c4fe292aaf629a17a6a222edb714f9d45cc9190015203d55c04b569c85aa19da7970eca1916ac3a541e9b6abf47688e14864b14b733a5c8419baa3955dc8788118afd9539c82fcede95555b8c2be5dba70219251a02bbe581140280a1c3f8ffbf6371eca98efa46f8057e307ee621db776a1fcf3d04e8a1846b29c8286dec3424bfa708b4dc8c0302f15161007989c8780c9ac5acd6790c31fc44a44c5d051f6be200105c73c680671657e4bc9b6441dc17c26dd6092737643f455464b14a60ab8ed0b480847674202dc66879c7e3e616f08b0b42a25558c6035e2c4f8984d8f06310832181624488a746c4ae62f4bbb7d9af3c84361011e29d9e7c8f7781cd201a1e1c833b819096ddf89478de50e37049f1bda5e6ef37815091725433ff1a39cc513c209d4c0d2a29f169e35645f232c1696478ec6bdc651afe079f190dc93c14ac591e8e90812f95765944258bcdbd19cec1f61802cdfc7fa47d619b99a88b6babaa96ab2bf700ee316cabcfab6eb95d02ddcffc8556ae3c2ce49e7547fda1c20ebba8be80aa5fa7b8a9e8a82f039208c20c62b58bc446dd2d3280e13409315bda84b2ad190ff65df683efa83bd7078802303d267155b77e31dfbdf20c6a74238cc912ee129271fb01b93177aba269846ac80e32178efae7d83a12d5d6a75d33a466c3329a363cb28de8ca21501cfee551844ff52ead0242d3d6a32c144655cbe67113feb90d70872ca33263561cf7376bc0bcc4e178498a28a33e6592372757ed3f0de79f7c9c6a49ec7110a7ad1df37759ff4315c4622581fc245df1ed293850e11d6298e7fe948003b9402b673e73c1d8a422393a747a32c6866748f4dfb5e4f620115c24c7e0628d5b5271ba9e85596cf8330b8b66850e63e427ca188e76e1056ff7ceba4ff903752268b35c647ff37b1423648972b54b74c2d9c40e97376c92176fe4e39f77f87a8a460ff1ce304f63cd52f15124a032fb9df6641dae8ac5b123ad6355b55bfb2c8d3c15434866e8d91c6498a368ae8ba14ab5c583e3e6467dbef0e6e78f683b293dcedcae478cb76bfc476a5b9552d885ed94c3e8972a4004711c11a47ded18adf58e55eb020743e0b68144c522e2c611466725a35202da3f1a2454bdf704319e1313cf76b1fa1bad9308d16ad255ce701950ca2111e38a4817b254f69e0f2d2b1011d7c879dedb827b8b740054edc143abf8fb1ae4ec621e53d92ed8f5e4d8fd2ba8090678829de5f0772eb7cd7008223fe2e915abe3df312a8e3f3b5ae43e27c22b9ad0030b2a11d863edd7fd8a3ae5b6c45546d85606dfca5b38efb65b8e07f2635ae243c50551f8a7ac0c873799585a140736040f10b595ff94fd58dca01fb6eeff111cc6393c61159cd696691ca36a28b6011f91666e5ed31a9eb69df663bb05b2c8af8edb12278ed2ba74f0fde448537876194cb2aa8420e3be4cda9dc90382145d1fa0343f32d5d70be8f0e75ff1db27d3b5592b1c7622c16c8608bb25f2532b04716ad825f97ce1ed12d4601ab190412236f14dd986fc7f6199e3ab54c34f17ee76e281d02e3bbf6b49d146d1e4eba8d0c514ed779361f0bc705057957ce0fa670f53a7db260f35fc17063df3b2c75c14e1ffc211e88a3cfd56f7dd82847d9e37ccde265f34515cf9fd3e2aefd932c8db6831e075eb78f43595c553596e0cf205b9fd33c1e1a9296535192503de7a073f78b0043a4b21072693a03712ba71a101af6deffd270bf343dda73450d63ee9eaab1d531e8c1be040575be10ef0abb821f489d142c53f018d877e4e9c5513b95770e50248ccb2ec70932defb3d7f248c99279027f10dc1041bf4f478a1813352716ef481585085ce55130e30fa0ea1ec5f53aac01ed7842490d1eddf4243c1802eb43c173718ee0969bd04c81a452a024fcb3209132db17c1fae231201dac4f2411e7ba8ba7d46faa7325a8d4b112b2cdf3cdd407563381a7a040d4d117d261d1c13325812c9a8be68173a5f23f1e91b61f5585563f239b401c3d8831ea7c5d801cdcd9ee3795387ce131ae000d47345674c442e552c09004ecd0fb5fb5c290b8dd02dfa2975bb00c74b31eb5a2d5fcca3f43b898a331ac0b86181aefd4880bad0b6f6e2eca99dfcb4d128d8b51e365c21226e84201de342151fb2ff8a8c522e1b4c548de520bddc6190060b2f7739d6dae85dc45ea79c1230ed965948337cb1db99c7f96b06abe42c8d098603823412d579e5b579d97d5dd8fbd25acf311da65b31254eb5c817b744082759d2fb5727d250c07caaf7305062115afff2bcec01ef0debe99a3e2e0e493199d2d970caa0ff573710d94a79da943b73c98224a90bb9697dc741412694282e0c11aabae9917368a57bbfdf53eb5eec663a9aec59823f33f7e0326db33c2b20d2b3293242711068548b4bd4acea6174d22c38aa7d74fde7a880dd8369e614f5f3686d726c9feeb3102ccced732b320ec31573a1a94834dc3027d342801496e081bbfca199e140d620f0c110d0daca7b24f605096385d99f82726f8adc5461a3b2e029ba327b0c1f7182b1da39b3d8bc4ebf1913cc014352c6c1f3b930e84cf2da705371c1611a87aaa985da79c967f5728f19850217b41f1ae82a6cbb115f4d86b59cd81b5a6898b6646f4e3c28622fad3131a3cc1c20a95d3e8ac188294d15304ab1818689bcaa24464dee8601d5e71e85e05da9ed46941cbb4c76d7830bd28211aab256f5f61d6a11880a905966656adc85bb0db705b3ae836ab259a31045433ba7fcf075dba257e40d4e1f480ad0aa687b5d1f95649f02ab213c5f63ef055c950c2bcb063500171bcd6d1a1c4651619e2e06ccaadc90e2b07ba90bb744f0eb8b86da9afcb871719a59320e530104c5fe65a28183bf377e2b349f41a28cda01763316f8dc18750573e3d690bc0ce7ef057ccb92cd6952ef4ad542c1269a12d02deac5a23f5f005abe810a40b86d96f397fb519aefe87465a39becca40a855b24cea86e1a709b750269529d8c7eb05a2dad4b10881af89ff52eac4bf276e77b31f375beadac2a0bbf01b94c870a6e4edadcc7acdedbf7b8bc799ea2a357864a31be58678bcd340203583b916029d05c759ff25c8608a5d5a44375e0db76b935095300c2563be15c961af1e184f5e678a74b363be739563e2c84f2820c78a9b28facaa839a88c2a9c61883b6a1147394c97d1d21a92378435135c36a45729e416f2aff9dd52efb88d92a74e24173fcd07da30561008f2cabd69074b6211f98e014b6d735e075c4a2678235309104bed1b5f6eac0c72aab00b502e5c47bf4a6de4b7e8388dcc3dfe2ba61b384307620f9ce20ce8605e6b0bd46b358f574c55a69f1b2f48c4c7947be981686663bf095adbc2a0900e1a341ec352135578022f6b888955770e059c55da92fb09b8703ea794b0f3bef772b9f44ea8ae6b0cfd0477d8bfd4137af4ed606cda0905026ab754f334d5096a5b5a7258349427cb6a3d4d2322371be6d9bfebca80cbbe4263cedbb632b5714b64569bf6283ef1c692a9b845038150421e6ac711c3a8424b7d4d9d106ceebbcf11569c3795ad70c49c276e6ee2d76c3147183ca241dbf50428c9cdc07c782cf78be356cd7edf74c39360566b9c0190a19412d6347cc4f2d738c6a2e148f082864794a8841f1fb45c782a56506b15bdd4b6b485a29e30e3c97ffa872977652043b2a6f186848f85d686684596b0e05a4a4f54d766f207222f130e321f998b536b3ea8fc6280280825a86ea3fd8147592d426588dcff1328cb3114e412fb29e223a54c40447fde4acfd8e788b65ddfc2b2b31a61d4d346b173b07a9b6bcb6075d044ca8ae3706905ab54f481d8ec1bf446bf44429d7087a12a65f2485e891875e931b15a1b57741e6ba816906593eec4c476ea73fe4a72a7ab9c16aac91fc416e83521c261c8af7dd0b4bc76b37b7cfb20708850e4028daa54376d1ab03394ec2407354ba19e387e008ce87b6a9bc00fa4642e333087105247c830d145304584ce0b4e5c6d46f78908fd618e66083f7e5556b928dfceb1307702deae4181ebfb47f63cd1206c192b1f48c21c738a1c6c3d160879319d285b715fda77ce0b1fe89dd1387b00b841506d43eb1d803ee16261f6d1b893e275037cdfba6bd81b33fb409b6ffa1c359ecbdfba7862564e0c25810d5ada8092791b165d7757372247a0058c48f848732e3fe1a39981f64e11f622eec2cb1320477a4a583064972ef9e022aabf624096d34af7f2a5ae0c46c66e36daf723de988af4f01a87470f1a408b175c420a0cf2e789fe395a3c898251fa5525705db4159697f24dfa688a309974fcf55a64a2316d677ee7ab5e7bd7c3465954008fb40f850a3d67a69e94b66e985c2ab4862d05e55f0c8ef2486c9412c4764400a418747bb69ef618721d45183997618f79b47cade8446b23305ad8aec91da691ce00143fffbddf0d31268fb6c7e7b148f08825c771cf35b4cf5912f5d4eef0c1c3297bfe51667485a06fd3121a85b4c40e9f18cd6084ba5c7e098454f714f78d5e68e68680ced6395ccf20477f74e03fc65bf0705827bc3b766ed0a0a0a0635a81e80ded646be26726be7530221dd7f4573b536529db5607178237975cca3e135c742f7386a9f193191fec745c978a9074f3142a1b8b8df70eec072867d3d99137e5167597b82baa4fe623d1377bae15b89be52f0a3369350f237cf9dfcd7ec9acf45a240718c8ef4cf7a3374d941122e7e0f42f8966f7d865d458efef075e9dd53bf05121f301d3c63f1d7f95474b91c84c0f117a20102545901a28d188883c504b808a0d25def07813f40d8ab0e94a1d1f34ca0115873d94550435c9bc0362bd3c87a66441237bfc6f0ff166e02b19d8390e2b004629df16d65bd1af38c3fc4e77507eb67c8dc21680d3a5b1ab9185cc11b061d1acb37ec1c5865e751223eea959d9b83eb22a6509fc15e0bbd90cd64e603ff18e1b30cdfce38cd189b294640bdc8d52d08a618a1ebee893deaba8a661a746e41307e9d90144f58146f831ee253ad14c09ca2d81c08a837a9cc55de43b2e501988b844fc21d9bd9b2e586f32451dfaed796f61186f19191e238dd9c5b946f28dddc3e37cb84f974333ebf549fd3723a5443dcd98940ee423fef9110c768d75d36d0093bed7a8560bd8b5ccf6953dd09ad7e8a1d2e57919e6a55498187cdc4a663d85b02ce4d8a46bd0494193e10d293d6d2a0c97f6bd01ffcb292ba0ac8d7fcac55525ff961ed0c981947c0a98e49d9375b8a79899930773b2b5c99ec9d9f12c939c499f5a2f687cb3b8e34048ef7e3261c95afa786ad946d3b844be5a73904a0074e9b9a256ba17ec35342ee2f4e56581942b81237e84b3c98cccf4dc5be03751642df8e56384b9276c09a3665e48bbf14e21406777cf4b9749eee940eab73ca2662b700a951c19d4fa41ad26ae55bd9f86a28166948562dbfafc4ba140a05db539108ba858fb3ca81e00546d41ddf3d9dfd2009f65df7eaeb166928a6ce3e5f6380d80d1aebf1998b4f1ca31a4c5aaaf1ae619b03b4d2cca9080286f59e41212a1d7db04661a06d67915a5f4a2f4eec2c70cbc1318fde725c002a9da96c462fc95ec1622de2481b8ac744ff953e5cc052c85e6ed8f69edf3a03b0c7e4f0b43b07054cc6026661cb8183fcdce9bc4e168a544e970067ad263f1a460f50b3185dd880ac41eb2e0fc95d7a6d416815756c45939503402bc928bb497fada8d43aa7b76231da22aee84a4cd2d29b99114c79b762e0353618931573365fbb0a31289a42c27b6702f02e0c6ebbb7cb99844cfa2d73b74e204887bea0168d85b333c703cab562c43b9d4d70bd33c7bfb9ad7cdf62b03fb01fa24c517ca6581ae5aa354e6774b68f1abf62f8603c3748d892d3babc64d6694ca9a7759f4db988e2ff61818d1fd409be60b26c9ceaaf5795f4f9cc2f757a6c551af0a0148e2f977f2b3d3dac8322665fe97403832f06ccac3db0f1b238ee6efdb8cbf294b4362108db8c312883612560d00f1d6dd952c33b09401e190616ea58d34fd116effa103d88e97cb03532d8d3abef82b39470d6aabca9823be9435ae801b38ab74b587e072843e71a0b071d0e2f801728aa6ce59ec6240752eff5fa707fdf70c5856fdedb7bd8f85180262e4529a7d9c904e2103c9f0165b2bd2a96db3d4ab8778b30fe5895b09bd64d9843f7951518f198ee088349b123cc352b3c93173e36806b2dc0c08a0afed6bbdba4880ecadb852a5f0ae2677969d9b40630eee20b8c7b039885f63006c27abea6966d1306600010a8e8c9d231c4414bb72055384771ee00084d6158a17938d1e54f84e20e14986b9f9a2cd28348eb0105241b98aa4ea4803b482f5bedaa2f269fae3757071143e6f0d691b659b168eae4705f7e3012abbcd94b0dd4e10699cefa82579824f2e3e0a74bc77a20729ee5af00a0913c0894d5bf2b4a88731c72147563c3f1f7e4c243658236dec0656261270693d6dfe536d4bd9213928e5a21b96f8e73b1d88497a3e42f4db823382c24a5c7a80609a3ea61687308d380069e4826ad0cc22293252e0b15c0764f17c6defd54d02e0c099cd40fc23201a4130ce06af1066d12be4b31358039ef098b04fa08cb68b1d48091156d12cd29d3303030db042f9a1b169d680769d7365eac54ba9b11da1267db81e8a448e1bec4cd0289b456edccb1b3d87bceac950f1f7ce6c4972d27b1c3173258eae36ee6e83509ecc50e7783c620258024e847e38213acaba32ada1a68cf8be586abdfa53ffa66feb851a3d9e2480e2b82e48f679ab298844138442efec3862ea5b0bcad4f3076d527d01082e97dfa1504a4195269577b1fd20a69f4300479e6180b49b742a79ad17a39cdb2e417622f0234d9429af70839c6af87e0df5aa5f6ebf732991ba88936cb08c4ef41f9a0c968516f5e667b06e62c0edd1842194fd4a57b68a5fc1e6e5a2f92ab500882eb019a199763cd4ad6df21da8a463a4d315afd4180574c9e1f01a79c9aff3fcaf065c2ced236c1eae1d0e516401145ed67a77f426b470bf5a41c5dc625c873f843237e591e26b2a6b559290bb148679c515a9c84d9b767913d383f1ccc4d21c42bd30582f7664f2ca3e8e66d5728851988feaea47c5a0293c21e3a1229849142876224dc511e767bd8d2ed98dc2951769f71f33458c84814ae208392d17a20610d67ce45a280929fb271b778f904650af409d60f8c477f3351223d5b1c46ed962c7a162e6c883e8a30e444e8af7e7ec549840be1a261c2258f2182ecabfe20f1762202ba8c8968a09c8ace18d0185f09019f70f0b280ff951bc73ef2a722ca2ff9b0e0e9d9b2648a34b1cf9164ce88cbd48d01925c7836a45262ededd139c34da6437eb49e28187e5dedc98b78d7e275bcc7cd7c09389772b7c19b1909ffd42c0fb66afd512f0b9443f7e88885161b80bd90a6bb70d4a91537917572c8ec2c24602c67c1ed91f6f2c70a06986c3fad2793635e3bac9cf68dc33a98b398ae6ab317eb268be519ec0a02892f3de92640f4ce5b445003abc58b12e1c063937057a9211434a324e57979a250e7709c0df6509871d626b24de5a6389e318d1a88af22075ffc200b38ab0f1118174cc51a3857ad062dc1b4c83139290f5d2898f1f235c124bb893c22da63ed4e63fd2327c884105f86d0352b7fa0b6e503f6472b496c3441dee7b8c107b8656fb894e98a7acdee48b72edfbc705b020c263c9ac6fa1c6fbed260c00df46cf09bf0ca4ee5e7fe8ea82b12922cc3e9e983226c3051c0e316bced25bd8c4b4cd8d0ae7e497d17b545dbe0d2c7b2a172d73891e887f3dee642e31c9aeda2e9a5b9d4967ec104ee68e4656f0f710db78c82c2f8ec35a0d593bad15e05d324dd2b71d7c0cf5181142ce514560b94c8ece153fe2a7f0db433a8b89ea0634f115aeabab6759b146d32b7cce1274d13c0a66d38e6ba30c1786717d1070178372a8b338652ff7a4075da351358af0292fd15b04c40f82b48432dc7f5a66e431c517c5ab4c4637d9dc0493e799f434a7e0af2e2e75cacaec3e1d3309e1a0cf1426d74374dc39674ffdfdda70edb90cb8c6a2470b2239a80042512e2b19a70f31614d49fdf1e5114671b0a5e0139c1dec113a0c3e6ece59265b14390071b4b011065561b2a5c9315fd478f1c626b49a2611a94838b9ef503808d90c80337b67243a50178ca70a709f030dd35a5c58481cccddb8cde03ee4a82d204c27c0d588cb57e5fb646282572b15ff071aacc25d25e040036a780ab4622e9f6a4d99a6f9814dddeb504005e6f76b04734a64df5eb7382ec5a64175c1e3327fe2a6f5575906f94fc12416511730dea1d9ded0ac0df9476d80e3a70e1ca8a1d52db7a03d12b8fb403c904698906295bced3be2378d82aad64e7a4f77059fba82b1671840dfeb969bc571e60d306b03f9003e6e229f8b2933edc4d53868722c9e7e8bfb04ad04198872052ea4e0ed619a35c2cd7d3051b91e04d0fca60bbc709dc396ccfdc080cc0c7fa3fa8bfaa949ec2a22fbff08d11820bca49378e7d29825dfd1f30d2bc09ed8fc49aa1ff1daecc448f46919d1901151dc4a786071501cf12970223b67c9dd3c1f5e507b648d026d989eb44cca092238123c5a67d2a2ace788279ab54689496a84e70a03c95459cf9482e73bd9f06ba5ec7eccd93ad1ce3920f5ee25f6ec161a120d58b950a7df3e2a96131b4429c353951e715abddc5c81bc6483499400e56cd809eaea73567748b16c76acca53da77c8a9712301a6f2def4c9544bc15d4698e37ec7b950856b8898f3748067a87f72fe34cefb0e6bd880f6d3c7bb8aad40905f085e5b68ec9e2b9b9eba689ed5ed4a4929af0f606d41ef11a9ef35a36c8c8c1f1b2b109e0c2749cef9d2d0206a549ca389d3f61a553f373002e634a371d44ce4e44edd488a6232f0a1f81c33f468470f8a323e9c895d135436bc501e11790223191bdd7c52d584e31c44dae120515a0e61990a29aa92b7d8868d41a8d239401293ad6795b0fa77dd45f4ba6744a2fdca535468413172b9500ff0a013ef38d84abffef2101b332acae7ee1009074d001f0a92c97002d9a2315233c0a400bcd71fbaa1aa5f24e0df1388be26cdaa3f10c6f2dbad9e7a6fd1e7cc0a71c6fac84dc83251cc8968c52dd52295c614f6203cd12491fc7d116c3ba97ca765e8945c587256376b54d9ddcd7a912ce22431912a32c47f64911788b79fc45ee20597abd127bae2b4e8240b620afe36b6ce41693d63e4755571486642a2beab05f0c93392d3f6e19eebeaf8c344c423588d53393f5e0f785044a7e24d0af9c040c945f1452cd2e259a345572d4eabd738f03e7098c7d93c6deb50ed78632d610f318c4312398e5ea452afdff51ff7123db33859631b80a15636bb06d8ec7081c726ed49d4b37907b00d505e40219dab3ef6b4ef1ca4c35fc58b43923c44f162dbbd3a96fc58152d657541235d26a2584e305f9fb55cb256c766367810083d5169e4b3c39fd09be674adb1025d6da3658f1f84df823ad5d051c8258b4fce634d213d05831537985795d287ffd3c530ebfc4360ffd76d78e7fd5ad0c6e8c4e3b818ccda6cc080e631b1cadb5f289e1698558f9044947a20a63cba4dc260328d44e287ca6c1a807b4c5f0c655287b241a9555d1ce7f3b0fe845c53f55e27b62d68609f8ec7b21e8a1aa449d51af617ebe1e1c84e09c757b8784a111413b7712cc23893baebb7e813fad104823464431827b416a622fb9d495973cc3bf47c762cc60b02fedb69a5bce04cfbc7f9fee9ebe15ea482db8665a8f338775fa96da7327a41a69b7f8c63c3694db0b897b8c97fedf40242410788c784e7404591897d3fa11a941049a97615200afaf69adb02cf8037ec18fb5692b27ee75b58a84b0202dec69ca725bd9658dbb09fd6318ffbb1580625fddbd8f96903b3ceeba3ed75851d83825def415de683b84e676046d2d06d96ac4a7ee4d4035183ec5cf3a956b44551089f407a49a4eed8a9eb35a63216c53142d37b6e23b89f2ffa95adf5aea72130e4416de0153745f2140b1c139d8064c3976639b4317057a241e640950543782953cc29604357a1f04eb495fc3c5c982bac67ecf2c67f2c5fea65298970f7567ee91af6d5869152a68c6cce35304ac9c191d2fc756f15582c2df19ab54641d3b92cb2c58199c6786451b912890756cac1447bc744b73f362b1d0d5787442bd1c8a2a86f4fe45871aa118d601ae41b81cf4a022a39e9a93cb5878ed813670b23cc00c71ad2b8699c6e2001f019f102acf7bc502e24bb7372e125a93b628db440a5a2290553a48d2251c60f32d559ffd1aa4a493443fdc5eb7f6c97ca7b94a6748a861476d3cdc1ca248803827e76159d9274721f6dad43a8a85a039d5f9e0c73f08f816314c70ab3c08c9d8f1d5d1dbec9a9b09349c6e5cd038be81aff730f17f5a9d956d96dd546d491c2b938cc8287655e91c149f8219e1d2aaf6fd9486fda517e09ddad02b455fdcc6c629d56b95ac92eabcc05a95def46f196f3e53f183f57c03c2325b18a05c576ff604d06a64353ad3c44ea57892e0947a2577477291bee52362e714f1482888c565b9c181113efd769b84f2d329acdcd6571528f9da1d7f64316eaf818f893b979ac48911930966411fb91483a02f7152e4e5cf3d3d2080a0855bc270ce60c0295275c320431287721e7baf886ce8e46788c3b2b7b985c163a0382f331f79fbdf4e0d0c6231c106b1d8f2caa9fa504f13e51c81a5ae2b7c3829728400f720b205d821bc72430a36c2e7eb8e12e5dc9de590d93a27a455271460361ee0917289e4e8898054c94abf0652708a6a10eed2f5c5465023285245f8ccd53b860c6b381d693873af5ed7137f804ec216183c41b60901d3f4cddc14794c04898aacc7e919eafc835f21582fb2561b7c620b0a2331a39f14b97e4d50ea3e58759529ea58b18b8c85bbf555d1a58e73f38f0fd9536df6ae4f61ae45843f6f0a75ad7c6c5a1db9cb51ab437cf70993e7188b43f7a4c87510162331c853db06c0a5780436854ae9781ac320e283af156db4ce175400bd7583c06e7b758bb27d18c60419964797c60687a02e071b9ea4da793a4ee414061fd5f5ce83e00f4d02ac4ef6f2f1aa56f8bd2f76834f48d6b50cac64a1b1e879a9c5e5afb8772aeb91b87279ab550c57b79ec9e568cc045baac0b681fe21a92f995fa4a405fe3e609b23a671f171008dab10da2fac12c6979b34c60cd5d6520be8f96e0f93c2db460881a05ecd9fd4901a47fa220e87520ffe1ef7e8020d7228844a5b440247590378057b0d83c5328845d06dd0778c49df45fbdc18ad74105ab199d2bf764c9b5261473a393748f43c49951e922fc96976d20f95b9d4e9a1593d98da1a651d22421f8006aa55ff1c1de10cb91f92e1987eb69113c53dd5faea794b92c9bafb35f68743389d3834e1257f7f917bcd525547d85cfc4c6973bb62180699d602ae8c9674c3b88337a2c0a6439e1d9ac64b19bca033defd87c22f35f5292b659c41884a66b230f676bd7b6f05f7db83e98e4d2709ea4f6543460e817d24325688c8f67425bef63918b8cc2f8ee1a6dfc302c0ccebae53af71f52313f4191f2321d5d025506f5f6ea951410f3093cd94b12d5379dd5400c1c995a8a3a81d3d0517490242666d266f097e06c712e4db0a06f216e8e734d450895db3a1e37595cd3ef525b6840cf8b588068c2030c8e4612ed20462f5bbb18e049bf62e16e923edaac2dbe32ead92cd15eb852529df66b3e747d7382ddeb74b34b8f674e2f6baec688c76b45a014280152112ce3d49bdacddcef8a5640d19270bb7a2ea3b4aa8fd5853ecbdb61751a7be630a3434eb8ee24e14c7c28de1a6a96f8795e46ea025f2d04286dc9843115972fbfe411f553a80febaa26bbc051503fb2bcc2eac763b9ca513d80360d55da05fc49ec2b5e2257b2258d0ee3014d2fdf43829aeab86bbd1e05b593b266bc46a51a72762004226ae92b33a439c14119a7a91c533a235253a181ff419502e5ee9dcf3dd7e8a9bad20693073599fae02da21d9efd9b5986be20a01d052cbd6e9bf1d3fa034b95f61ae2be05ed5e437d196f8b28aca0c9cbe1b8242049d2bdee901258780ff3a54a46fa26ddcda2269f0f0e0a278632875021145da3904e21cb3bd2e5d4b4a05d58026b6efc6b86d978afff4b1b7c911b88dbc1c43a2aa0ea96be926bddf684287f71d1dd1b163e42bf3b4e50533e08f493d47d8cdfc0663020d4463b7c6b9f2e0cc3b2ff2ea16ef7fcff21f880588c4901daf4c4e34922b8d36fca6c4ae8af4d84d7e4333ed683d86ece76c8ef482de44bb11e89f980b830a857a8825e566a54d5c693a5fa655597de93d5a8fe6b1aed7812105c5bbf659c7991d898d4f39127d8bac6879e9a7ba34411456d12af1d2597f965e31fc092068e6c13584eb94978edd992d467e777fb17cbcd338d03579011405d24ce6432fdda3d7e65691d2b6c2d2899c62da465ab49a49b32a807b3f73c14e6c7968a1ac8b9b08ba77004b5d6739f9c83fadae6fc546b09dfc860c62698b7de819933bcce4e65202ab4f131424accef94701e22a42d4d710f21f99455293f9805067dd77b6a74d309a4c18190101fd58c427bf81ac9b950604410cf7500ec710c01542ede1bee5bff4cdcc97eabd867ea3224e2225eb12d399d4069a3dca583c42ef9157eaf7938879390bf0e2bc93e570059b46688a1e1b7703081981f0f119c7aa4cd89d287f4c700161d199e11126e1bb3c47fd14784df8453b5e63453b0298294af434c5289dd251565c870832f34216f53a31c8b5549fc5b807935e4d310a97308ea8419e2941a66f483dd56cc146afcd2400a1d710a1b7d27861a43c45e90914aeb665b3761e82c7aac9af98e7aae9fee0d84cb1630359ff2b8d7bdf6030b5b40c50b53f313a1920586e9b756c4a1307d99851d00ceb104e8d16c54a4e87e01a7bba15ebc60e25371b5f8c4654758c18f862de5bfe6f12d770df198e1283240650f374b5294b709fdee51c569951836c4ad9b77a35aeb2460f60aed729aab9a4c505eb404f473315c368be3e21dc5c5a0439c16032bb52691712f07d3323cbadb41863241e66f25a21f0576eb80f93644d60a0f0125762614332ac98e5f6d37880a94d763a3699222ddb36561cb5ac9dcfabf1f6b635be302ca14dcb06af78792ef63141993c6ca965f813ee9abf7317131cee4ad731c888441e2d5267430efcf6d7a94e117a0132bbc449b3afbc960e776671fb13c28a133506a4d491482cd64811fd085b0c4d4d294f1d4aeb990ec5917b7f16eeb7298f9c2e924f903c70b249ecb370cdbb316d45d15d1b21bc968e982c84309f295fda480642fddbb3cce8f1ba90cf86ba4ead60d30b2d2f0974f0d098feef3fe491bde0892b16356ee62041e53bfa97f75708b5d3a5a3112cb0b81f8f9a420c44963dbba94d2f7c1843462bf208ff5080caeec26f4654048e9cb54983c70fa435adbc596510701ac6cc9526750e52c4160b78da81411d3e083e9118f47cb200c451987223577642874872d229907e7b09a75b2d6b39dc378c2b94735eb5c4def66dcb43c8e4dd32b5c099c2f49a243c1b3e881ba7394bd784dde2ea27886974b2c5348a4809de288bf4c7f4338ee589ec4cd7f54be60455b50d5778455fe597040578bdd25828b3e684dde47b473315186103c134859ed260084c102ae5bf2a713d0f46ab4ddd6a12749d280b9cdab05361c139e7f3e4c163c366a21557a12a0a4b261bb7d5de6fd9b49628c6b5d295a2d0cb6e32dd458c3720470ada95cc8c307be32406775b58a1817eab0557551f92c3e842a76f098e9ada6c8bf3ce90724b388f4e9c797cff3c10422cc0bd81f73db38027d0c89bdc5e22f703e5110f453913873616aa98ae61ae26f593e419e1a25a588919011064be2ee5dc45674ce0905244b330187a2cf38598b779d45199ede3fe8c3d6f6c758f1be16780be569986923f6166e6af2a96a0ac8ddcfb80924b20e458b3589470ea45f34c7f47c99322e0a419552f2f98254e8a0fd075e50661001a15e43f8e625c67b6cbee561bf8a652773ae1fcf290bb7e278ef816e8f2de4eb9d1c10f3c740c8e23c57bd7222adab28e17aed1ecf1c265bff2646bd8211bef58807b7524efcd69895ec39adb39995be8a72c90613152137f36b8ba554ef6c1ad0b098801853d541be2d497dfbe3e38e5495b7ce5ae6d09703c1e6107944b3e4000ceb6de3ca5e18872a1aa6e92c6c5e8da104111f22d5ad41fc0a9dd4f0e15036e315d29c0305db652602d9443468592110a832a850389c68d1e3c1c1d3f8d0720d6e3c94421394f1af15e087c2fd7d9143c4d5c048a8241d6e650f340713cdd49f569ffa98edd2abfc223c08354f2f48d8d6a651ffc89e4042bb89942f59951aa9653d0d294d67ef467471add6f8e61cf0643e1c4af4e2bd1bdac11da9b27a102a4e89001b9143ce80e9cbfee8a868f33c948d8a46ca1bc01c246061a16faf6ac3d28ce49f407f252933171a598bda958562cbac75c8148995146f21e9895377c763fc4f5f47448a52ffd1de1c750299cdef603e69ef75e0e86050e3fab275f1f7b88a71f36483226683a81d030d4d804aa8c3b6c4e68604a13e73524ff40d9e64e8fee1ed782d2080335862b7c963fd412110a40fa9b75e5c622cddd773695e8d7c735d0e31d6b42fc6393fdf1b6ac74b45b83362a685b18c77ca4bc1e0f974b15dc03afbb92e541f33ea292ee0314962b49629489684f1c388c0976844c19bfb4d53eef3242d23e2ce8765aa0e1c8c10d7a829c82c42d33a705a4518b772b0ea2f1b66a30729276585067e2ac54346951fd782a9ee781f8dae22d4106188d617faf0116eb3ec0d1109d8f39c5d99f41cabaf847f3e88255ebfe083e990f846082ac0a26db2f12c9cc98a4304b3dfcbdfa6c1dd56711d158781c48120255a1f9a2b68d6154db28ce6a8dd6184718367f2e88cdbcfaeb496a02b21746149dc245eb54ea675457d6286a656fa1aa67ec3362e0a7fe45e15f3e82b61baca2d159e2c19df91dcdc1b6af33e3ab62cc919e10e1556af11bf72a2bbd98c434fd0fd426dfbb5d8840ed6b813094c475990740b880fdb78cea32e9ba04d4dff882be94f73f67d22489a46fb22b8700e8936cdbd065ddc8df9acfdad484717ec20d015b53270b8e0ceca80660ba4218109b1bdb2bec10d5618bb278a7bcb00cf89aad715c5d0367f4f24add40edab548d64804cb388a3735189702cf2f99f1f3359a3c5691f2984f86b9d5533159c2218ee954b02ff159a9d00735f18140874007e36ffa79a1d5e8020c6c246318de7ff842987147697175902050bfe71b40384f693ce31d87e17f0f3cbf46c974eb63eb81c4c2484475b029a194917a0bace1d88282848595ddd56f90caa91ae6daac9a7548f39a6a1372a0a3112c70ad3e32e886834438a56eb9d33a6d716e7af290789f66e0e425708456f7ab81feca41d4fa8e4b603b95175c30b54ffa1c2c139d417b8e79031cec161cc5f3dbf3bcca9beb1c5c43abe98bc941840ce48008a48462b01070b607480464a172c538419183595f056034a6f4b1210ad3307652393a004412ef44cb1a9f0d0d8578d7a848facd693ae528f82f38cda9f32886b18e502eeac036895a263b06fa374fc22d8165c958df6b1901adf13031f2f8fb24ee637f8aed3c12c980ce4fa43bd45fbc76e58dcb9c9be91db8ac4ae0f9da2fdfc052b309ea3c08cb8c147320f44563de3a6ce47560ebe1f35e5e0dbd562e7edbf46b41f0c26faac1c58e756de2638b4cfc421ab01502b4ed746a8236f6e28fbfc9f38488ddde1b63a37544bd692872fe8d796ed391d343ee822bc844211d7205f58eb5bd17072abe8aa9924887c3982308399cba1f6c14e8d85cb0c0fcd823010ed683fa8c9b64144c537cf715678c2b4a9018ee666d571ff603d9337583e2dbec212dd87ae9cfdb99cded26d7918019956e72ae3b34eea9cb609e280acb2cc3a8fe71848742a44d78bdf5e2f56a159cf7e4e1ba4a43a5b8e5dcf201f8f643625b0d6200260776d3a3a67ecfd412f54b50e87b29f1c9c9ec4afc077f095972d9896fae93ba61a5be99edb505d9148e0fd7cdddd0c7a4073b65ad219a25a381b364a52080af893828eec96804fc8085f11f5ae9666d57dbc78acb7616b9beb6c916d6b83c047aaa4b98a78468d96dc4aa699be85c37b99527ae2a608292b8a7dd05c82a1742a6109fed843ce3a6e93cfa72268a02b0ab09e6ea9f0faf94b2ede569a69c3babd1a3512245c33ae73c6d0a32f4d1b730483b233410625f561a1fe0b39b20f17de5b500af494d38f212bb873a2fbd9fa428760f0f48609163b9028c9627e7f6f6319b08875a820f0130f6dca063b80c71199416dc019de61499381aa1af9a1074b727ee83d8b3ae9abfbcd865f77c0db9533416df02738fb6d90130e9e3075fc73ba5580aa2a99660f2275b706e0d31a9f3435e7e6fd6343e2777a468fe5e69fda61f409f7af30833d4168f143f81b30e27266133301d9023c5285065997aeeb0848e7a55f030b28d0694940acf0f9a4f138a1871e5267b1f8fce79dc2f2e24d26707dc820afe70da1776084cadf9f8e28dc9eaa9db6338dd1a65f9ea15fda30036cd60d993432ffab97c574bb26b4ef8826e078f4ac6d531c40365e418f6fa48db3ff711082b0cc3e03dd0fcebda5461f46752b88657cc3fb81dc08f7068440c4ece644d09689e9e46654118d256dd9cb0dff128ecbbb8a39c84f1ade40095e0978bb00e0f087f5abe4c841b21ced7813dc3e34079d6ef3959196024277f16c19442b61f837ac278acb2a08287c3c0b4ab145bc2c6c2da2c36dc006773159335b3fd0992ed70ef0de153787f69a6d8d66fc2293dc29b927f1065e7c0ef08b0e3d49816a61d8f29e23a4c8951820c9f715bae449de5e12047caf3fb2a05af46904ba259596671956cb70ab045a278d6aa708d39c4c2395ac00bdac1bd68b8b78ee4464c47bf0c6edfc6a091943ee0ba15b3d36f630bbbd1fbc7d38fa1dfafa32e4be606164611815abbaba0830c7ca41819a9f275d159441a6fabee72a0216328e8031796b4d2e101d910b6164c9e3a84431569d28db0d6d420365b8b3ecf26fb66e1f1cad96b55eb0e692027494e5bdd9aeb13b33f49e264a191e4b076a70476c7bd8656d96c10dc7357ed81be15508dd109c00d31230987f3bfb40008241e340c9c24e35a3f0bc3a814dba62edf6d1494d9a1035e21ade99ae641e9b6fccccb9a0f9a397bede62df0596347f842e505566583a85402b8910630e22a742ff21974bb8a85916840aafd2407af57239120ceb4674c88679c6ccfb11c9493df92a0815c89bf0f1d1a5e31087913df04dea912436187aa888445a3197a81284447ddf14e8831aed7f31385303373d03b5a65c18569080840557a5f26df9fb8816543d450c2c2c12578246a2e1873d261baa5d65c76044510bf69d09597e791b4d4373e95edc9f09d47f54df0a06f7d0c55ac2054835c8551a813db388d7ec2c685ac128c98071e234b4890624a07b7d1c7a535ee440b50c5e2a536dd9ab6f1229a7aa8fc76f0085f9807953c77dda8f4c7daee0e7edb582645efeea892d96c2689fe0f263cf4d68edefeb2a5f091498aab4246d3076d5ef3655a061fdf941582eed74c6759880b8bc5988eefc2804136eb0b3dc653b07e924d6483f24ebd90892e98a8580c2c4bb0546a913d30c4ed0dd5c29aacfa92076c64af7b1142a9fdebffa860d47498f95e8a3f3c761ac5aa34593793f942fea18218cc15a2159ca73aaf661d73b8345fed3a61e9c32bf1d9906258c3e6329e97b6ac4b9f6bc11c94b82d2457a54eb23aa00c4ddc60c37473f3d09905520b48e42155c60df470b433c7c218f8ed12ee6a90aa33314171ed23c63feadc38322aa31eaa585e7abcc11d5836313bd3c0d03dbaca51461b8d50c04740eddc02002e61ed43e926bc8384317dbbb6b02d4916c732d207e1dc1274db08524ea8dba73cbe6a11a1e19adb67b91c7c5361b2935e126cab424c350c853b665cf66416cebbadfe0bdb1ae5e427086e048d09b999228e2f9a149a92b8ad80053cecce66ab5063cf7a40401ec15ea1590dd62b829adf08f7af1c28fdb62395cc6d01ece858f9da2e987dfbfeab88e4efaf04f17243e9eb3b0455c0f10ff419661073d275401679f41bba181fc2f6853521937cea92c71ce9d5b6ca7f869ddd9eb2c609349e011432010fefce0cc66c5ea6669bdf9fac71536b7f1e07d7d012aedde00d189b5fcccfb10b0a1af6b1d8bd07588849f63ac04377976c69d0b6d599c19fda522d61c6f952db37db06ae142989c1300600f63011a8f8c398152638bb31bdc12ab1b54906d92f0fc25f25ac60bf5e2197eee9334ca73fa3ec23021c4f8b3d89e98f920393fa61406b7e2e0347907f8c88934624639823c6275f9a11252c92bdd12503a0b2cd627fe65fd7c3c7801c355b310c232196c8f8d546ae30629e0f864a1da948ce1055d8f5c40c04d942a6859220c404e59b9b43b051d083b4ee2cd8f5d7dab678bafb930fe9910684e8fc5b57aa290847bba30db7ddce48372a10b46bd283ea5690a0aa49738e81bc370322b73987290e1a1712f2f21ea0d86f5b125ad6025b88407caa570ba4d21508e33af309107cb8b7034e15738ed3ab6a503614abc2cb88ef6508063ec4abc935cfe77b8e57e4499566d6074c94463b55124d1eddca973d359d577afb0fea17facd1428a42d460d08774525828bdb9cd4d27de4a6927d3fa1e2ea15361d525de2cb43c13714d5a1422cdb24ff481c9fc94010a8ee4cd37831ca15bfc296a3d4ce336b849b70bb97d308f0319de7a30571f90d60f7d4020e113043701fc057396764cef9c828add6b7d3df612c0c4b653aa4b974e47e9f63380684cfe126546a324abff437e384cd9f74a37a50bf9a5e088df8248cd5ed85666fb74332b301cfbf9aa2bca5dbbe4ec16d2551eb226b8ed8f374b97b379839f08e84dc847e47b105cc927de222b09f132bc89b8fededa444a828f6855d66b6f2250e5fe6ee95bb0d1dd3a5b31896bc67bc98de98b48f81826605790dbcebc56c5c10eab0ca9850729bea03d4b926a760f4ef41579eb50f39d75cca39edccb16bb927b9e33bf2309fef0e2d31f109bb95814747beb9b2a152325616c43432654834c97ed2b6e446f10259e2a3dda5308c23e2c095593d6a0b4ea619d6ad5c5d1a854a36fe8f1229a6b940a7a875de6f7d2f1014d5dc2177afd8b7a8b41b4f1ec9179029c881d03403b23542df429124310ab92b89f9192b2c87f8025f6a213c6c8b33345e6c3466ff63e82fb8fcacc85b87457b17dc773e2c6db39ffdb9e78cdc1cc0ab1e1e079b1d63564a04ec5e115332eb58179dbd4d738c21b8a03687e5d42f094423160fc18e8dc3dd70a463c628f8e3d0f00890a17fa0eaf94320bceff7c656fb642fffb4e1e04dc42103e840dd12b1d22f4acb68568792bebe7d388ca9f58cfff4eca96e83da9603d94fd4ac82bc954b70ed07879a44743bd6c60e14a1b4fb80a40c2170be49079bad5bd8dcffe554a0db56d41d1de0e092c354f85dbeb226d2c1bf39a5cd73e0f333ea9c25900c0aed5cf184b347b2bdc344689c793ed5a574097b35403c190b23040c441393cd486c8629f8138be467543849abac581fd6b36e876929d779e726feaa50932d4ec433b5bc45174c24804a8914d95c0ea5b68dfb29e4c3641514a753d4b8f5dcc539447dc8d6c43f2a234411f5bb0525007154d8fdb8a473a27ea839abfc0e6931b4d662a2771078c8ce914b4cf405dabc74af4430ba4325d964c74b0994c248f045965cc7f27cb49979a5f60b20cc43712e97c9a6e77a912860678ec70b32709ada5d09c91d13c19b12377248e368f0b138d92ec6ce7604d0b1b5f01b45286ca3d974d8392fb686a28cd11602ddfa67b47f2ba5cd89f79617a8e3ea39922dc92e8c21da2f747c4e02c97c5f60e9b61a1d18c2e28c692eea7c59f6b0fbce85a1fada95e9f4145d1ac01e5c81a4c56cdf5201f2bd2ed2227b9d2b25be60e377f43e2f5dcd11947fc3dc853d511b59cf1a07fb9c047923528331b81c500378bef20e8f53d87828d8ca6c125ee1bb3287e10bb42285433194cad5bd64e21c9cdac302ae70953d8a0fb31135b049254d0b71144d7d9baa1ca0e544a2d9244f10344e0180d4de190abc54d3da0ed8b612d4f306034dd167e633524bd0d6d1d2f227689f7c220509d5cc8bcf69dfa3e06ffac33942ebd76d09427fbfa1afe082232d304a595a4173c54c4efe8eb7dd985f07bfd669c1530f2d71aac25a9dac001e264176af14ade816e58d8c2e3fb956373d0e3679189a283074cb06f91628861cac9de74db8415a803daec2786b0827240cffb1478d779bd03552b92dc3ba5708b8400a9986ff09cc236e9707817064660406d4eeaf79df1016dd7c23361b91a9ebb168ab26ac282d78f050e605699d3c5d620863541996144d2a76052a4c41d7fc570f08cc5a41c62df29365801203f721599f54a88cd17a2bac5f9a25be4bdf94212d49ab137aa34863cb2485c761b2145884b4009317263bc85d15c22fc3019240b52102e734a3956c3dcb485f2f951807e9bddca20ddc8c6c08d60c09bb980f45e1cf490c65c3a784e55328069ea35023ad2e350a63a831c4356a3e85407e27ca40b6fe4417ece23bf8a1ca04e0450d72a87f426229a7186cdb8700e632387d14bf3760ad02358d9140b905247cd09ea088486f9b8ba412a3e9d3a844710cda8a3a32287806a92e2d6cd37bdcd91151fbe0211a0ac974b6e22c71ce18507ac1255b6082a53d969a798868ce37e2230a01d021ab81516621522f0d2fc083939fe9d42346ee2c802322ac32e048ed13531234d210d87a7231a594483a38361b30801927fe90af357badb3618297980fe6436dd4a0ab1181e05e3e9d6cb46311b11d761f7254d619e1f2854be04808c164249299a6a5f389ce25388958730dcc790b6b1bc3d3ff2ffb3dd619ad7eee3297836a063f2c366bc763559cd9b3aa0d92a8ea686e6ee3c08a4b68f668e1cc49f22b70baba754ca08c036ed3eef23f1b95c2220a9e16423be4b435b64846eb173f4a46921d3909598818b62699a1f8f9e027d5536b699a589eb755d6e10fc8d5617efd733542016184e4285bef7eba36022ed8deca38a66fc828a91b96fd007181de80e060697a90c571a78ab79828f12a9047d504c61f93e7b5355bb0cf827f06c6a1415b669081d6db00df329de9984f2729eb58860c128d310a087fa50e9f572e914b0399d4922ca8f1cd12f43be7ee414419f0590d30404aaee3ec522e6aa547fab25b717b17609cfcc7a45f117b04f61d3f5190297bfbaa29956d8ae09d4ef28ff8a054b778066255e231d236ec6ee4a79b41c63e9b8a645919742b259b608daacbefe287ed1a006d1f79585319f45aa37f4ef80cb5ccc8655b28e6f6ccb386e4401d2bb6278fdb1e64ca83b09da1af5fcd679b53b1ce1852c1dd66c6db81f9d186c1f32d04bcd431245bdaf5c6a4b0801dd83112defde6be5f043637533be372e836deeff73f36b9eaf70c845116209213877f5da31716d356f2174f6bd362e1afd1de1a19246c2c55426753a92938505f3025fc869995fd537b9b70acd667efd5d0d4b91097223d6f24a6d077e91a2b1164373c9ea81b366d8e6d8602b8e68f55a1ab24b477a55c41454b9534224862423637e1d1c5e2c297550260e8c8176a908ff14ac7ca1911839057d83d2144494d085e95364956e5b751f4187ca231dad721fe47bf84c3b06447e5458dcada56604b8b454fdb83526de79c0dc6efbc5d0fbe29cda70a7394584c431a71bccc645baa667d092e1384987090d78d86d3ea0f53e31769aa2d6e62bb7820bb56af403134085007e1244be44d81566762ad602b2fa62b4921ff81c05dd21118a2835eae6890580dea4140ca0f7c5cca768b1e2428ec8e556965979cd590edaa49fb49853af223d0c5804453861ccbc29c5dbfbdaed50d2be67ca2d15d0cf32a910e9834055583b9472fb62125d55755840cf2a3f20d448c19476a96102fab9844c81876989095ce9c4b050ce498206beba47d9ef63bed3c6142bf0feffab403dfe1b6925fbe7e3e3c6c93a923cbcd8a52d7f0e1bba5823332279e66fab528498880015224ed74f758d633d125ee805154719c85cc6a0f1ae89e8f7dc14f7da88aa347fa485568b57fcf44d2b0d2d09ee57c9f0e652de1f0084dfb83613a32c0fc5b10da77c7d6eb2d08a2535b808ce0fd40534ff66804c2964bdea41ff5543d7644c01488dcbcad01d84fb98e10fece4f1fce80ff5113055c5039bb82034b721d73805d834a953e1ba2a0b1d3a7b5b2f976ac1e7f7ee6d86cf8b5ae86f14580e9832d0c7127b5128557b885449d7d89e91e7de1c09168c105ce49d22443dd9a318f4296784f8b466f54e1098b88e995391cc32d42a6660b0c03ff39aa2fe32b85503ee92239653e404a061c18141c2e735e1404963086f850608189c6757d3f54f86b5b5e056864671e8527442e0e875a09b4e5035251f11ac7e854be375d5f3834791ff7b62f533800b707f2191640aeac57c5275daa8ae4dca495b25e0aa919eacd138d7dd8ba52fcce76852c255ae85537c17cdf90214f4628efb0c054c1beeca595aa6b42109c904a662ea5952471d9b22be7c7250f7b5bc67c9f4c806e9a12ea907215951890c0fa7e8c417f25ac76eca5eb91ba6d19cac8e844820dd19e55db035f818dae87588fe51fc5580ace9d7fa99e86db977f6aa8d495daa94605bf9dbab7b4b7fc3eaec1c088e4300c947923482a53592505a5ac753c92403a3f520c15012e864342be60e66071e43c0c10fb3b9362945d596a5291ce1bc92f92e0a909f7c0f037a4f92755c30503a4a738be37dac1d1daf5ba2bed79a4a76d5829d265dc5f5967f9dbc9c39c9f88529ae4d1727a0d022a432ef42e8e8e63ebe877931dc793af298bcca2e2fb035a3a311b9ea370e8d50a3f79963c01e11b1b6b5d6464b4b4b9b04f50609075807311b11ea2db69b0133203cbb4acd7e8ec7733722d4615dc5fe9c9cbfd70ab137161abb72106c6b2b8d8d7fc41dd9e37b328c6634e4d4cf70379ede8af0fc78f8d12582e263452cbf41c2df208364144f7362e3c11d0b3c872139ab1fedefd9359c3d270ec21d98e79d79e66db5da71734e0bc4e0e238c9dee4620f9f237170205c499c599b37bbea5a9f7da0679c70b3964e8c62afa50b1d7fd94a818cb8d0f33e8cbc0cd2377fe423d0c592c4d9cb109fac798b392fb8aac752bdbf8ed836c5a87b19e6857fea32a4ece9b29db4a68b73c723f597b72239179f40743ff196537ad57a64a73928067165a4c479c7a8997cafb53735326c68661cf5d4bd3603cb57ad47ca5a6b6760d7292cfb46860daa86e6fe64faeda19d7e329d9ab2264b189ec25824f526171236b559df96d8b714d13dfd3a7dc4f253eb6192b1efa516c4952953a6cc6d538672411306cd1a65ca9439bd4803e67280e469a9745def81d7aa5fa63dfca35f04fab9e0923ad3326ce1eaca57a95d07721a68befaf43b3d0caebc3c2cb9392b0b1f7db3853265cab47abee20b573c90a503de6a7d7c4fc53d70258fd1009242c7986bc47908c440f132e6dcf30388e3f11c8fd74bba5e15e4bda2b292c449d2ad1bb0ba74693a0604bb4ef3696ffaf5de5573e76e5c286cbad380982e8cba901e2621435e783e16b319cc6f4862d390179ecf505f404a9740a6978ec29be9d76bf066da8624607a093b6555d6c7e7ccdc47bc698fd46a42ea6ddea290d265de3421f298e9d883ceddd8e4af679a907ad42ddee2292aab8260371dbbe9d87529731a90fa2d669a175f731b4d48fd967d2b3d3ea787189c2bc0664f6f83e1d36bf04693b7a10e7a5d3afd0c0632fdf5c540da4ba76c836dd4b93c9a909ae598391eefcde0bad60d905a85d7554361288999ec693eae0a88941ddbda3ffb190db3187bc47e5da507d13e723d880a4901c357e94e43c1d928e54d9e6a3dacfc8f9e76cec6264f7fb0e6dc8dcdb4035737de0fece8c21baa819a1397854ada72694b3373ec2165a76fef0997a79e25cb4d7a4859b84025f74a1a124a7a48491706d2ee425b67edb3b9b2a04cd76a6ef3065b7b4dde609bb8f4bb4dbec436fbac9519c874edf625d9a4243026a4eb2fe9ea62da1e972bbb7cbd5623f3a6e520d859ae529a32d07b4fe465b5fcb258bc0549954dd2756d325ee5eff5974df22f8b8c1c24ed254f4fae2c6f2a64814a5e161b5396a48bc2d96bf0e910df3ce2d42dae8d04573333a7d1decc7bda4c967afdd855e30e996766b2ea7a599e02ecb59b4d29eca2ce0d7651479eb6f7244b968794854b963b0369b660d9600a105f7aa75d0b8824da064f7a00ed5a484cae74d6f4f1ad27ad205a41b482680561fa924453b3d78cca7e8c31c6183345c53bf925d37ad097e50c7c4fb1e91946f1c41d2e45038704fa120eb51f2c6f37f5f2f698b755d3bce1c0b2ec3dcb3010bcc9b2c7188ffd2fbbc933b6205843c88c1a7983ef25e40d471209795b61c1c9db99a091b7140f54598914a519ce8ae4c00e4fb36a1a12a27fa6b7991e0481a2c46acdde3309a64d9a90cd94dd74ecdbf55af326bfb4e9f03b3a78c6ccabcf516da6bfb2db929484e50a13567830ab9404f014a02bbd296330a319cb59de68dcf4fac7e452dd6a279b5f463ccd1bfdd384c42e4f01f43db1b30de7a66ff24bd7a3f049f3418fba89caf8738d1c4bc8246455de2ecc115e85934df49ae6231e08e6fb2cd298371586aa0c7a9b9346ffaecadb4e9b7e615a8f8ac28f067e4ee043a2d1f487a36a3e50cf6ec2f13d7106be4fe199dfe096816df0095fd8de2db5f1f4c4d11b0fada1b951679e06c7ea4f99f2967a79bb31bbebad488e97b79da6a171c20b17a874ecd6cb0ecf4e692b92632b9263a76986d5675856b12cbdc80f441c3856ab3f957a4cb0d3e768d3b72239dafe34835fbed86a3e4caf9a90ac4d7f4ce2dc8942c70a6ecd9012f3562447633f656bc2ae65e9e5e1703dea73a2e35ae9e525e1bc1ff114fc043fc11077b31ec9ddb2ce501952921f74901269494b9a82a3a0fa00c6872f5ab66bfda0840accfd611acbc7e650143c300dbf93a4e1732ca086fb1c51a49562e753877c6a8c31468a55f0d3a76215cc3f5c0b749ece7855f072618c515e05191d10f0d1276a280cf9443ce4e383961451cb960f4b5a1e25b1ea9dbe05a1230a381f787ae713b1eae51fae0578e783450a381f22177574e4efe50496f339029a57616610d4e8f72144f17007cc411d73ba396dc52a56b18add6119085ab8caa8084bd0966c8411f6c458f17661139bd8b7d7533ede8850cf4b6967068a325e8faea0dea01a63d4f8d26a128513ad264da46e132a77ea00854081b1a486070c1b8076ad26336852c3497073f02c003e6765a3446b88b9d66e2820edd99b1009c96e3520d96de9d99f668f6558bbc54080ea2f0b046dbdd3b23fad3ec340ef18762db57a0dd767d7707c2603a56fd7255661aa926adaab9e9546845a664662cbecfac98d4dd1d8af4beccaefba6b51d1a5b15cb23d31cb886bc0a2916c1f8c21690cdce623564d394bdfa8aa947f2e95cd3f57ab6afeb9fad1fc73756cd8af5df74423420de3b3843424212902abdd0ce6e64c0657286de55ecbc996f14049c7961062e818630f9e88d2f30ed5523e2bddb54a5a08913adec64adb5c7b3c4cea9adf3c1e76e97a394567a7513afb7d10a874ad9faf4b3a3e7e7b4f3809c2989ec1dbeb995bc2db14523abee6f1704e6c5e2d486cabf9b83d6ab299b6c7b4ec693f1eb8f1405f416a7ef393f682b8f29b76ed6526cd87a669da7ba016c4e6da4fb462a099c7bfedde74a8f9b89f353d6af256fa9cc137799be9eb260c34ef1a35e38106c2db0c053e440e3e4051c5113a58e2e39ccee99806c4763dc51876d27ee24f782bf5363f6383b71285a5de4a0f41675ba9af10f494bf58cb5ba9e72550049a0f41f7b8ae1db3c1f212cbb8868164b81ba599534c3521f555fb91bff6f40b5bac0a72facd4fbf396635d9237589b36b18881ee874cdfe7ae946e91653bc3d21306e34a75995ca3f436dba0a88cd4fb7f9e9a76737983e33d1f80db681f2333e31cd40d967cd4ff3a8f7444c663738a331f4c28c97c0b9023c9f9e3803cbc0291406caeeba060365c7324de60992fdf4eca79c232403653450184ac2051486865ae6cda4456028092785ba05ec429786fad24a87da853d8876e958297b106d23a7ac85b6256ca446979268cc85b6570641aa8352436df3364d3987f67cb8a5762d29581db34dc1991438fc1edc4d4627e1caed702d27ba347cce7b4470628a762d2758bda35dab8956afa468357441c3ef44155cd76a026906cc411bba25450d0d77449febe0be0cae5cce01c3c205a75d0b0b152c44606992d3ae85c504368490a35deb4a0fa876ad2b2c68c013348ca86162c2210a2451b0bc306961a206890c8819ed5a50dc40a35deb8932ad27b4340d37d5aef504151aafe266988678281d98eb1e98eb5fb9efb9a73d8a1befa26c23b557a0d14eeff01dbec3a701b90f0e01b3f0f1528ccd4218e77d70a18d0990f112db380077a137f4e21302361b091d8e406a0ce6290db7ccd1163494cfe74ba358e8a0685192d68084137994d45a48687139304b1ab047c37d872b26f08824318c4c9a54a894fac4c6a01405c915062bc28a115a32164cf1349c297065a28149026c1cc089ce7025108c88c1dde1c7826b2f4dd22aa20b7434251360af871eae9321816498b2cb521832f01430fb491e92e4c2254bd27b5f8eee12f94576915ee41796cdb04ba8e9a7cb7943399ca6df9ee52da7a98da679dba1f43b2d557037d932252fb1099fdd077c267334b5172d92a34bb2e7cecad51aad2d3d27c2584fe773a4c4192abd4984d42a424a8b881a72c82e4e7e99a2e4ba1611442d28637aa65d0b8a1612ca140e0350a8f496ea2823928e2d28513ac28e1152f8e66db85bea3dc29d245090a4baa6706aadb5a2ee829bc249a16e80034f01afebeb59a8a9c77232c49876ad21626803b46b090145cf2925bcd1a58fd95b308488059e025ecfa7b0811984ede6208399587a0e1ef7eed2661795c2813aeacac675e1588a314208772ed933788561275c316d6df69da7715d2bbda4f5d04a1a967de54b0bf24e58f6854f389a606b353be157c3c984a565c195f7367c3d0db650a774ed3048ca2e4919c40165ca94e90c7b09cb9697944d71bc540b1e54966198c93dd716ae302cbbf692865529cfb9b457ecddc6cecbd78754d175b715ca597aa60bbbaeeb8f494379f0d042edc2f6252c9bcaae44882092990b57cfee70ddb35fc8eb57be8ec3bdae6c73ce0aee96d35247477e15e58e7bd9f9eb0fa9af78f90b1b101b10c37d6003e0e55338cf813ab2a79a40fce91580278221fe945d3c0cf0a63cd417de6e5fd7e9f5d243ea4b5e70c37cd46f58de721a7bdb694d9fa7cf1a6bb319ee66a3e74bb7b143f349f3399fd1cc600757f79d9d707613ceb2c536cbeff1d9845c68d98af0386c830e3be734325dbae3e96d28496bef81af94b50c941da86e3c526e59de783afb965db751c769cebde4a096b7ecf53d31c3b684371bb28a10524ef3467a5cb74634700de9f9886bde5ebc94515a9c048d1e32ea76da8f2b42c74d7e6215907a5e15247e3ee699a4af6bb7c140d76d4e83b7eb34422ce9795b6b666077ee469661bc4921335a8cbad37adc600c5789d1bcda2138106a14703e3838f805d54bf07c4a9a0f9bb2f6a9d32d9e313521f3b6a409b98ed50c03527dd0dcb91b9a90f92dda675590d79e48e3aa63d91dbdea97f2290c6473247519189e8137d3e55358c8bccddb25e45e66ed3df1061b790de47acd65601faeafabb0c540da6b8ec25b8d765a3a61a00baa7edc1429338bc9ec18661f441b3b8265f298bdd3507036ac0ad662a56c5e06a9116740f559c2558e1d1f2e6cd712c24abfd73b21326f42aa1f27bb074ecd080b44241801ab9d5b39e76cccd84302ef3203b907f31a05fe428d2f361feacc5973eae2a0e63fc6958f6f074ae181125278a044114ad0a09f03949041aec04991e161ac60071c84b0032423a66c41e6053f281a020a2b8ca82106576a93304874a0a530c4d45a6b0d03a9c700c38b1c6892deb502b11c4428526bd702a309183af43b46b3161848ba5deb0b2ffa3d73587c91d4ef0f87fb962facb46de78593762d2f741840bb56175f1accc9b5369e2fa034ac4143db90062beb6c50edc776ece86e2345e70aae6b81f9d29b6b7d85d79b9b12010a01b8e3f938b7510ce9a12681874970a5732ef7c4cc93c3e5e9d8031d8f232bd89e0fa20d9bc23713ff9a067140cf6cc4a1c0448787c3d11144f8dec562b158527c9c95f8acc41d72c9afbcf1bc94b71dd36bde56da67de9ec2f269db3165179f6517e3e592b803f2f0ecac564851e7a9d4bd71c807c62b8e87eb78ecb1fa075e69a1a397afe83c2bafe578c8c7c97aac27f4583e3d51273e06d5a6b4e22da836cd403d6036e2ca34fc0ab561de864cd0d7f9389f991fcb273a31fd2eb7da308813f303eaf8f1450ddcca75fce2c8ad5c6fce071d5fabe325d629739d9876bd392074cc82480c95322d008386964f3422082dac5c99410daa18e1b38021a278f9018c135f9af80cc9a087317a904118305b74e1e39830410d4008830b1c6060c2672807124831c31258c45c31f289df893b6a28f1c50d9290a2046168e11385f025081e66c821cc12627ce27be20ee7b3c4da00737063c2075de4604a1357584186c905c6548141134038c183146066d0032348f1828a11acd01eb00bc0b4c0d8002649864d30a7c02c1c82d0858e9b0a2e34fc7d7677c3e62dc2fbf8e0cd2bbcae4060d7256d5f919a47c71f982babb7781924011d33bc096231ab2d2fa234fd94d236662f833fb52742ad0b2b5d24b5fd4cd44a874078348cb9033cea67de03abf6536f5541ecebedeb4c29c876dcea4df874e7780015e1d130778047c3c37c043ec874d34daff74e0b62e3c5d9eb863dcb0e689e5e94629cc3e59cdaab48600e621cb0121e1e7c83b53c191197da2f2c2e64687d1122ba2e36a6c08186a226556021039fec47f81283dee49d10323998b78473b2462f4fb69022021838b183151cb4604b18ca05ab29e5c104247cd0c30f5228090306ff2280762d2f58bc38c145bdbb5ab87e3a865b382272651775ea4f6f016a7fd7e5895c97f0d963167acfaeddf4135897be7ca34efde1cd67ee6f7ea1232232b01379c7b0bf973291d44f59e8f4541e1275ea67b2d0119199bf2cb4136ff24c16ea893af5c22cf4a8532f23d788b39bb0267f9d882c5de85ebe9489c46759e825893af5e59b853e705d59e888c87598851e2b27113a22f20eb3104be88808fccb42da614e21ead40b99eea28e3dcd4ecd9d8f4bda1ecbe6cee755d45f126983858e86a0b2d0f3b99f790d6e01fb751adcc21111ec7a0b27b066f2cd0f29ead4fa1360b8795eb98579e7b33da4ae3f611f0d3b22f8b7450c5b2c69cc41074d3da886d057e0f9ccfb486967c20d8e8839b258728316536ec0be68f1440b56f7002ae24effa83d166c393836bbffc0d53477c07d1e02b93a70757c3c59386908818ac4673c2f8b25b00332cb11ab5d86f34527dfc2e5575491f32adcb1e0e1a9750b22240278aec5a2b674c1228a2eb56b6551830bc06001a6bbf4fc0ed4e15a5cacf47c8e79ecf4ee8302cee7fdcf65a1a321efee2f0b3d9f2144f5563581982ba09a407c3d9198858e88940e1fc3f38959e885e7035fca442570804f7c0ccf07de4a284d3dd8e19da8337f44240b1da926f0ee4ee4dd65a1237a21177588bcdb2cf4f2e3fb09aceb7c9c8f238a49f208f85a5da2b4b4f0c1b5c8c991b202b8054a0c395b7e3841cead90cf426bd7da52842d567adb9964ae486a5d71e50a2bb75deb0a2917f8c176df5deb0a2687e25050e928df9c314a3763bc944e4a299d8b0e462833088dc40cae1c7c3146082184114a079d6a4a28a594b01d6d18e3a98c51ba28a593524ae7a633127bba197f328439c3cbec6276a587156da71b1fbbd8ec3ae79c73ce39e71c640283598c114ed1f2f029e029607b0f9241b6eb83645f3faf4cbf573a16238677743edadbbb8bda57caaa2032dbe2df679c120ac97afa57a7caceabe894703e4bcb74cd5a98cc5867400592be9e5d94d64a6bad95d67929b3476badd96b0c487e6e0a88235a99cd8b6adb7520094ba5286ee91008b3d737949412cb46ddc8579c3261a7931664288a0c657a1b8ac262b50ad15d79932c56d77ca4c56af9a0cb0aab2f54c730accd4961b5cc321e8e887a38e2dd11babe074a1b7723de692877233e0817caf1b81e7f695041ae3b713754df743c75ee062ab60d4ac68561a36c2e6b5e47a154efa2b5a5bdcaaa3055e9ba6a5ef4c97ab9a905a9af5a9454d1a59f2664284a1256ab30940412abaf4bed274261b54462b5cc466218565fd9943d1cb189fbae034d4d441da13a4a379349b823054b4e3abbbcb45e7bef2dddebde8b5ded66d744c4b465a037fb6cc6495eafb7525e97bcae96d8b177954eabd6e31dddeb0e5e19bea694d259a2224a679797746a40689c40845db9d7b143202cdb20d955f25e5a8f7ae18dfebac52afa7a62c54662d74bec5b4627147519c3e8aa91220a298c708a88785f8c1c155a54e1210b24b490e08a1c74b8f06004152040c1c196115841bb30f16284c516464cd860c388d78353d14d0c8c5c17b7680c235c444251038260a485568485110e53e460c40054d10c8a6024a36804482f08cecb124ae603838c7be2065188b085910c8ca2aa688b9c450428f2c2281761792fcc99d19e73ce99d9312a16b7145f2ae59c65bacc502d1ccaa11e75602ada297a55ac783368a1c2c31546618a0083910e8c0e90538415392b84784f94311a6384008963a6e8044462a4f430c5e80da10aa31b3adc0f9c30d229d2c208464ec018b92c460228aa3754c69420892a35481aa308313738616224051438f8d09466c1a484a21bb2307a91109874f18ade0eef8b295cab545483918bc2c888a74807ce0a16231a452a302ac228893382018a28a565c6684ab1684203a31766556482ab62534485d1116e09288c5c13ae073a7c61d4554c1006111064c25062db52849431b454e10409583ba05c6a30a328082ede10ce0ba305f45b92c3ca00142292c80198255550a08329587c40022c288841133e3c1881134db79850a68973ce39e7dc73ce39e79c73ef3b1ecb27e20073391b0e2a8cac35c61a638c31c65af3362505a3c47570f562883a29c8c35ba0fe3fda7eb3f5b122ae9905b87a42f2722d220eae6284ace902cce5b8978083bb2da0e3b76785e6241c00720139f171893b4c78cc5f49517d3b0fbee26e367e5dc78fb7c07be278d45ce7f129c41dabc7b3107708e03100a5c33f262b27253af9b76f4f49e91517e1e9223c8dbd8495ee0a95622f77bb331e8fcdc0db6361ef59c15a5cfb230243a28455e28b9a91b799836ad74cb3145ee11e0b7c800521560d0d7e4a90ee09bf24302b2c8000e014a2ce3b00f0cbc16f49d4793701678c5fa3048c63a373628c3fe006a00e9a02267ab045bb7694bef75e2e41895b3a8de37c7b56548fd9e31f986705fe71795d3afe07cd4578e8b196bbd968a8c12ff95d1e8b852bf488dc8df7127e4ae0ea3dcc1b8371f08b41659f8310420883b69dae416f0c18b87a6701ae5e17cce5c2d7ee48c3c33b74034f3ec09c1621c703031c50e13c81d9b883e8a83a22f047dcf163bb3bd9c49e82e3114df29af02aeabc67e77995669bea81d8853884410885cbae1dfb80e321e46e04a1697cbb3378e20e1e0c0cb1d7b0ad9587d67a93dd7eb4c934435363636f527790be044c027650e726cbc00eae6c322a6fb2ed77f286d3f630a7edcdf599ec6d476a7833e52c0f51e1cfc07107c45b6a984c3970036914523a2bce39e79cbce29cb33d7072a0768c3b608ecb9c938aed11b17989619fd385c159dc2bebac62d4c35e9c21fb75cdecda5b2a1d5b614df64578fac21576ec100575b043ecd9cc8132073ced30cc5a2ce3b1cab3e5d83173aa65a77f0ed4819d7e45a9b5d7329cc23970656d0a8de5a0d9d8afbc822b7be5c395fda954737dc51a8c449895622eee661febb1c2740a63912a5dbe23121e35c3b0306587195816429d152a4cc204d602faa19e794efa49b38703da234056802f7b38cb95b607ce9265b65620f4311e81bd8f31a65d6b0ca4ced1ae35468c361280eb1c8001e03a9c63c2253a92f359422873bef10fb0745a8ab2d47e5a3b6fad95544e7ceb898ea6cd4097e1299873ce8903571888bb65fd4961ac71775280fdbc2e41555170556feb65cd2e2a75e4b5bd8551e64cc2253a9a2f95e6b4a5493fa79db6c612603849c870b9407c4da090d84fd2c32019c8407003c97920f2f035aec0010d2fa4c75a6490c0431b4977b3d1aa998dbc8e3ff23a3ef7f0d5834d602a1a33eea6b5f7ba9cba44471322203eda1f2e8c62eddddedbd5f1723a97bb116dc055fc8e1377b37175bc8d9d1c3c0bb8b901f6f35ac68db7a133bb9f73f6506daecda9887db8d91d52818100c35035d7dd519a9c190238e72e11e7f28900262dbb4e62d7253a82f129ccc106c01d0fe6d27d09c0ec7592521495184b1bdc2d6b0a53f8c86bf9430963bdf34447f110860ca7630e5c6120ee869373e435bc84b9072ae90830dcc49df7e7b59cc1e27592cc95762d32ad2640bb1619254d7f6053145ccd53889f5c89517ce4353dfd735a8f875d8b0c92761abc4447f1f558c5f7b515c7c3e9a09f4d3f067bd7845da223a223e77460ae4a80d2acd6fa9ce6031a61ced9191468f004ae5c6763c0f46bfc8ee8f754880f1fdd0c5371659431c62bef0fbe07df7b6fc77d7763baf483ef1981ab983783f8e52d6b183fe79c0f1e5701658e100b7aba923e6a40e4e5b1392790cc370ec9f774d06408284b601e0200031149556ab0840d59dc10ae6882821964f1c31739a045381973853e1983a535c64a8e0e592d8d708ee9414f31b264920b964a425a018790ad3146d83a8687a9431863c2484189cc0b557010c30f7840854f184dc520030021868a9650a962c30b4d5441c609122f626469420c2b45991852daddd4ae25065267d81263496fb62506927eb7485a63a61083456fd0f449bfbb777bd345125cc24c5102871b681037bc20c2862e3bd8420318e4f0601bb00d3c928c6572c5287291469c9b6a411454ec80c40e869842c30e55bc1082881c6011a28c8f94456859a3a50d5a4a99ba36565ab488f1222ec1de10efd58b81aa6286d05bce082a9812d41fc4481103859896944e88d5116f8f861bdbb5c444e9c78262a4f8e0ec0b2d8dc43869d70a23a5379c9e615ad038af3bec039a34a844ad9595492c19aa11010000003314000020140a8644229150381a11e6a4ee0114000c96a4446a4896c7a21c46511003410020c6104208000018620000c62090d20112f025231cd17cca3aec304d174d2ba2afe627f9d8814b87e87518220d6d71901109b4384a421c6dd529d4ef8ff81119a839b9aca92d6681ae4a99e27cc30ddd07838696aeb53aa9bea4bf0f8d4fe9071d29d2456351f677e05ed34af4ebd82507eee328443a6d0e32893bcabea6350d297b0e22acd2f0b424cd3d1ae0262310698083908548839b10624759d6694c14b2e320c23a0d4f0afaedd370cb041d74d5811074698811a8e018218e76b6a8001da77da3d10f32f201eda774d0912b0e7be390d6f14a8d49718f43adbb688cee44658731e4e0d6d1b5a35e47a5eb20718576a6a511edd3e2904ccc31d29af674799f83afba1ca1f46404451a10254450d0b078298b9286c5c9084a1ad1b2220e1a8f9b3149cd75a4b3a0b29dd8efb2042af4e7448f1fb650c56797e090805a59d24da1bc7db500c82c6f8066cefd445d982abf889973d132b8225ec80bf93aca3ebf9216a732ad837b0d57b8cdab092ea3512eb452702adeb14d3e92c25a59cbc3b15b79ed95534740a2d6c5cf0fd3f5c625c85650c26ac26321114250a02522a9ec188dc5d2c820077a7cdf816b0e7b3ba477bcdec1f53b0e294693510ee8a0371de7efa67deb42083eb43e241f738ce4a22d6b42443f8d8f48c78e6e72b4fe0e92e8b4cf51d861226d0e2281e3550eaecf31428c26a31cd0c14d87ee0efb3b1e4d07092bdad310b5eb28e4551a13d24e2d874410475d75945597f6456464251a22252b3b8a144f3b5354e00e68bfd3f896f4c1f9300707c84913f7697313436cda850f2e867ba205247d13dee4d9a6903db84e46f2a1fd2df3a0e3345cb4674946f6a7fd940e3a72d5b42efae7d8050efa1cd276b8ea586f0e1e8bc390ee34f0598a70a4f11921c70ef473a3010b32829fe6a37c8083ba769c769ff64bc9c80d1a6525371c490d66e1cb6c2fc835b18186fac2274773e190c43e9a2da57db08d8f7c98b2d21f8faf7a2b873601a79014a09d1599011c64bfa5f5218fe668ee1c44ead36c49316b937649b111c7a3e9b091abb4271177e778ddc1753ba62d46035969fe028303ef86b68941447fbaa1b36464a45dda9c2241c7eb71607d1c24251e192973bb865cdb2fe84df4ef5cef3cce4c41e75b36460602738d97c4ea3a85cd032db32f3c6249622d02f671709f5914db97fbd188ed2be30b39dd30d8127e19561accd796fd61d89da5956109e1c2049087a2be6e2fa7118da7c16162b35321e3098a8883017132ebbd3e123cc0368ac73c90189e3290e42cfd29268e0f321f81846fdc0f897ec5d270a209b6640939b3f29829e13a1a520a73d92289dd5c9f804fa085211a66da6e56956243931d6e3dab0415110815f4d7a354aaa4d9dfb7c530fc378df9cc1b6b3849514abbdc93e89a5467d6c59fdb837414718d4853da856b78c1178a6ee63a59ea7b7c0c9a8c650caeef34f3e9618608f5ecad291ff8b74e7f36fc5a9040c5df3fb0be17e1234f6d2dbe8aa49d4e7dce71ab2fbe9e87b82fe4c76b35f09d4cb9f560ba47d439bfe30446f0d0aa987fa5374b9b23c8d0e32d74b343ccc7be61fcea1816d0b823a5a2ae748501ba27df171f1c80d568a07bbd6110a7206c0bf8e9601e22ce61201d9d037405728561aa83e25214a69ea0d107b20920d2b16b1379c4ee6c69bc0bbf944f23809b8feb4efea5b755a18d8033398e0c6762aeec0561ba23a6df6eb84a98400abfb33809567b239b314b52cc72af8c139382a957b840c78c60e0f92ab7f3713ded39c9a0edd447d860deb07d036a21eab127b6bf3637b52961409d2746e768bd69e08028057ffa405f000e75e97ca4c61a9b68cbb756aee060f7357bb2aaaaaca20000b411603600bcdb3319c8ee4152da1944631297ab3ab918df0d62a4a5cdb7b1773539394026e68dca1e357135793241a12731918f537c6d98147011d2c3d53b9166220b9c98b87135cae02e17db9e04fbaa24e46c4d84a85b44f29a8ec2576bb5f73b2ffa80cd3e398156052c11ca6918e6d85cbad1d9d1d7d01bc3e9f18cd5fa3939bcb06586797614b34799eada711704da6c807061ba529a2d34d5e6caeeeb97f6d53ea8636704e0133dc180a0f41f8250905177923053ebcf1402ef683bdc33b2d7c609fa15b69e34679f4471534cbc1472d496ba964dcc9cc4450115453a5fae07fabff9224d41c6bebe5e13041f0d31723541166a69769de6f75370ddb30e9edcebcbb33c4b5086730304edcb22b557265350a3de5625a992706fa1811a491238b741a6f08b7ba653f8c9db7ff162cd9770d039efe099faa5e7e7afe600c8386cf38b663e9380d9c9008288c77f297ab612df9deb74d9a3e14caa0f44affb753f9b28e0836c24efc0792d34fbcc0092f2dcd6b10e003de3d2176bee9f94a481f811fac2da21c978e37f9e67189925cd4041db1bb4e0fc74d3c7a2e9347536b4c8d29c5cd5571e5e1aa7552d1eb5e00f35cbb300eeac3a40812200f554ce3d81b59c6d55e29c53b4bc23432af5c10fa053c8589ccea38483cbc59e98ade4ecb26000f5e30c09b80824b37a037886f3acdab5004c3b55e5f70643e20a06b6f2f844f9fa3405918f0e6e4ecf24c83a477858f64c0dde4fc9c011b3581ad75ae5f5700463f43844997e680e1386348ab6e2bfc68ae577ac988080c0b7c4d711f7b421d2e1cfc4d4f3b98164608098a37b3c965a0baa5a07d4b8810af1bea2edf9b7b08ac8e9a0bf5f5ec82175cf6943073a801c2eb51bdb0d9b82f82bebde1fc7d66d363ad91f3e5a70fbff4176812dc7808c990ce210cbae71ee166f8271263a2e8e3d7d43f6e349643fb91b010629412262160245124a426782b3ce17514ebc1b711dfa25893d66ef9579ebbacc157d2b671f7ab135ad92c8797bb9563a3d73f6daa671ff9c045f4fd9259dc82d8f929e63d4520dc2f0f8177787c64ddffe2e8feaf2727e4f28c233055ac469b1f10bc2d05b6887b3e29326e0ed31c8a5d4184f5b2330360ac18b424620c6dd9e66373e379e6a8701aa3d076fee29aff73cdcbe0ce0a54216fc397bab166b499cc81620c35bfe7249a0212ee05061da1f1cb9bba0f2376b4cd86441e87a8ff99c27c865225fa0748a3f42ea848a0c163c65a5c22229f230c196780a701d0a6485fca18e6642930ebbeb6bea7428710a9d7036428570773031bdfa8382e5dd56b825a6ce752687d256d739077ca0fd54819e3557da5f9b219def147fbd6841969898161be0c6dbe033c6fab206e27c08efd0302d68777b4de616adc9d50f4e7dc3fb43ca2182fb47607a6b461313f68fed5de0efcf288b53a71fc5f6977b257771398fb2d546fadc25959eb304b4d3aee6e7aef90f66453e1355b7635e52bb81d3ec5be56acc7357d2d8ad26439fc2cc430601b6fb470e2a8dc82e134c040ba1ae84b91576c0a6707d4cd9e42e7aa5ebd53380a8bcc47c808a270e54b88b9e9b9800d54489a42edc95edc06acd894aa11a8c0a6eb8aff01500538ac453356e1580f93d07f9a15cffd2a69b3f17224f8c77d47e92d9a564117c2574cbd08f73720116f40092bdb326087dede370456a7a137231716bce008a580bc3f687e2623f4ef0c11b4c1271ea831a2683a2af8d2d6fb23cbae533a33348c160f7dec8db61f3c0e6489795c970e4c548741558b40b532dde65f44681736a442a0f03d19034f0dd0125b1f8786dc4f1b144a71e0abdf04c5bd77c9a052d947032752280758965183e1a243c0b8f498c032a108950768cb0f95230dab3086fbdd9dad518b73acab452b5d0106273b4faf02cd43c5df9bf11ea2bbcf2ac9670d807c3eebbb0f73360e54ccbb269808b5051ca4993df434d768f88cb36bd3304f31f3b9677c264a6da17b55847cc9d0ebf18d9fe2a9aebe51867977f98f83d495f530457afc25a64e556317583b05228d67b7607717ac193ca92f0bed673d4f489a7118b4176d94b6564d35be28abda91e6fd4edfa9227146c830675e1f9c9641b451aa8857762b7b77499feb4c3a3692a20bd80d70102e0670afc174cf98554896a4b03ae5531e9334a71537f18364fa6f09375d9b3a428bbe4aa9444f9bffcd6219ab7c3247eed8f1dbc57fd5a813427a555b8905f193c91339352700958e9f244d9f92717bf9c12cba2f7f7344c725a8c820fd003cf2f4029910d8c20607fb5ef89cee87c64185da3694df063547021fe9ad7065a5238cb15cb5e92483a23dc3876b09a08c23ee995a2515e6dce56eccbb79567d127c97c98e87c052c2afc3326cac0c36f67dd07e6e2e1afa7df2a3fbe33903290e6dfb3e7af9cc5ffebf17542ad0189578566806a621815575f1fcaa79df4b8aa0f9ca6999e595f9e3d3726739dbfedc42085df84bbb8a9428201248d618f4103bdbe378853fdf98cad13f6cc84da980c218454177b78d17fe4a5967ce0812234298181f64a5f27d4c67042705b031caa5eaecb4cd933f4080bb0a81329fe20633b44375b971eb23d16df0670b67aa341973f49d77d0360c293badfc27a93b0a581e3ebe7f5003357379f922ab032f1abc40b13bce7117f3d7b7639e966793d369d84ce7e6fdf56e21270c9809141226ca614e827237eba3f6958aafeba242371f14cbcfeda4fcb7d9f1a25431d0f842052f79668436b563358c9fbe0e1afd6164930ab4f86d6eb804bc7f1e23addb9b961bbf81c2c8772cbb485f5e1d114a17315200db1d4812bd68071bf16918ccd43100e51a057530d0af1653c36bccb07ec3a84f75af8cc074e814ba8a918446ed460381532584add66c514c7313ecd20b1ca0131999e6364076da0daf20eb0b6a44f1d73fc6295826948dce730fb70e8a90efdd800a3991c286af051c34ad7f68def9d7599bdf3768230482912223cf858c55bfac7d1ceee28634fe15b93b7173e11dbab6df3dceccad7e6015ff3626f4c5de857d042395ff0b256a181f8b68cf5fc81089bb371e928054da8ea422d9d882291b17159a5243407296aea1c7ca3090ac5621ca69b623fc6d5b72b10bbec913a8d479fba9378a411acba5d61b3007bc32713b129009f62d8a8b1c1578a1f53ed91c7b72b3c98c492983b60c481133a1ce46760c473717f4d8736c9bb152661d1d2913ebaf757855f405962dad3fc2c8dcfee2c2420543ecb05d30f22baf984ad5c119e71634120e79d6c46b3e454e6f8f3ce1ecdffcd5eef54094c94284b7eff6a118ecd30c5382c9086dd56b018e10b3a3d230b28426f5b8d9c696a3d0b0638e01996c0be9c32a2de999d15e47c8f963ed7076717e713fba7a071e2b50c953772eb9ad10559c1d1e56fdd59f16074989b0a2843c9ec0e4b87035e1e8330c6b79698f9784f4ba013f96ad5de2e961a6cd651d740c84a2667d9bacc0a4e451d903e412631f43bb3c432a275c731b1f21a4da75e5a2ebfc245618f0912414bb40b55696ea80e6031bdd42176982f38de8bf64b05cf4055495af246adddc38638e4468849d569b9841570a76deed0ccf7960d6f254c87d1e62767d8da3c4c0520875a3ca52b84a23db139af6302a3a76ffbe365bb610ceb3f4b89f3165730aeb1f6a74e62b9e7cf10e5f07b0e184e31be417a49ca5bbe8fc4c2ba720902ac032de73e4c3d5869bebd6ebb980cb5941ca7486a8565f85a56cc6b2ff12c7fe8cbc58929c99460aa401fbc693b5af71547d193dfbd52e78d60abb28128070657a1433fa0d31039a610d9e66d1b90757ecede92ed5c324c65196146289340ee74382d08eb86764e20712dab2d17c022b0cdbbec10debc0f0e9f0285f4b1410c11756d6113cd269990ccbbd4216a9908501112525bffb8e6904975b5166ae9211d05340c0e1c75188b5e1ae53830d7c4a09f826e1112e594dd616456280e89275f00ecfb29c129ea8413b77db7dfbd226a8b2ebbc80f4d9446f0532bec693a20bb6243bd17a3d3bdf76faf10b9a4a368d4a1bd1d6cd9e3e9bea9be820aa2681f748687d35682332c419a873d257104474d04ff45cb350f9256c0b6fe64fe6728a2622985987cba8470d42e321ab64de3c73941df17606daf837af3bb6e174e0c6cb90ddd3bc744dc0151fe091e77d09551aca295028f069dcb6e210d3126148bed21392556ac1c5a96fde6c61c7eb4390417ce66ec56a79595cce0706ebe54245bbfdee4b439395c38479582ef696a112cc7aa6055f283f4c7b2254fc7e08dba8a8b68eaa6f209d76da541f85f122ed295de2b1893a574f12792666b2e01053af87210af24974faf63017d040c60f65f301f59251344bab2ad4e8f52a51a59684835275d96758207cae1d532978746f4db2a0d90c16bd57506de8dcb82e9d70aaf680b1607f069da73a53613c06f7daba304a984d624be4a124d12ca04bfa13bf1016e49de7e475bef8f9e34c90387e1f477bd7f87b28e58aed7c5051847c379f9fe7c7d1385824b2d85f952c41ae8594be6ff33542c5fcec0283c5b665842d4439ba238b3248b8d3c717e5a5392021fd56cb7fd6eb118115693090f32fb98f627e82d20ce6b87c3712c1d8b6b016709e02d09438a9876259fb3953b2eafe52b08142b130fa5057ea5499d64bc932622d2eb1927393ba4a032236823fce7b8c024919e1734800a3291e9fb52aa8f48677addedc086ef10c3ea547710b8f71bdc7e3936cad75279d5a9fc8ff28bb1b692172ed89008e82faabaf59ef0cf379e74f4468db3fd59c726baad5493cea32ff1167d45ca0a0eae7ba9a9cb58c1e046651d3471a325ae6f05d509dca3a3fc93c8981c758dc3da3110a3455eabb275a9f9791797e0860741cd2a167971e4e7c16cd8d39a3c42349406f936afa94d669ce0000ddbb5d57a0a51b0d8c2cba92ba16b7ec1ae6b96962774b2afdea4721f1e4536d13f2124a13e29917cc06b25745ba7542a5c758e01e02c5eb4e478cb27ad504476827559fbe74ced118e282bfe6718349004233a00d564648ca56dfa3f7e7afb6a425c64d5a5080f95b704f6bc56f2da67a1bde0c03aa49177d5e4451ff55ad165519ca518c83b09736747a7bcdf72759bd027d6c02409bce0e62a66738d24ecb8e195e097bce8a299bd3673d58d584b7cde0f5a89d8bd9072a9b0f2414747e5efa43aa843e2e3267e8fe0ce9573dd2b3b6520407cd508ee08b2f4e444cc496cf11ff4af2dbf9c05495fffa80963b15438f7d8c4f7f337e7241e38a960a858907f879e8d73b2bdff6ccba6377738f11271aa93413dd79bce3ce08a87b464fdb5e927034f9f76138cbfda1420b0886044ad97a5be1742acc25890ba2818dacf20dfea0ae2b6c96a176879ddc9c191848508884d2c8a900f294d519c9c67d3eac01dfdc9b460671736fa0dfad9347b9216ebd385750d860e0ca57798782d24a563f63104eaa0307224a4f7f4011d421703812c05a894c7c10140b88008c04b8bc91c3e3f291003091e8134a10987402c8d99330d11e05200b37c5035260903f63408c4945f41f8b85b2fee6382801146f9737f48054794d63ff890e221f36dd4fbc8f11e3dbe2894e13ebf3bbd38af07c5d878039281a0d7605094cfc53bfda4d85582fb15c3ce11d4b3b2b239eb70004ca99f7b7b273c1fbca84a85cf0c72db799ef56e20350a401272fc5b778912e65f9954f50b37ab38ece4fe2b4e5bfaba78345524d97d1ec809a0440582025a681b4607247b33b9e904b3b2402a0f409715674c110749e5314da4cb9f5a4ee8e820e5965df00d1957d7764a11ec8e5adad2b89f1efbab3e4f5efba388ae95670c897a10a23f9069a8c4e578ea118ae5b8b6ddeb6dc4e412a8b43c338532e1f703d336c08295224a4b7c2459ef0a10bcb187b0115f8febd85ba4abf5ccccea00aaac9e68956fd9fe0ce593c7602aed6f61dbb2ea96780f87603411f66ee13974b3ccfc2e3b502395df4be6bec829b0baaf35f66f53f2ea51987a7ed760b1866431926d9c8cf694aa6bf505330f3cbe773ec90c3ad86031c0238f01e75a125649b1513cf0b6be2d36698ae291ce57045bf0de0371a90628f98ab98b01943129f2775fb0b05b016e0cea20e7187bf497b6e4ab3e35eb7b56f144338488b1cde2adc46f856948542a350125b3bf42e33393657811c3d6fb7b271c34dde65bda8896514bcc7310116c4dcfb5315f689b87ad2078c39bc41487e8855df6cf09b42dd40707827d18afcc29c607ac2f573fb933a4e43b8d0a592ff20d63207bdd3aaf0ca9318ae6578b970915a452d4df05e119a788879aa813ab5a4734ef24d07b0deb0370cf5ba5f6bfec3ac9c4e2321319463c6fb76ed5e70486a86b26ae416e0ad356c0c1eaf56211be5e00118efe3a03a7b753c8a66f2f34dec8784c9d5ecc1759efe61455f8d7dc1a5a88dacabc735f203e157509f9984f1e259d2f6ba150198cf06aa99df89fde90faff8814437541f1c3422d48f962743386102c56bdfcf18f1e39675500beb8a2e2f181de95baeeac00bc74f945cfa6ffbf5b69fd8c8a7d9dbf90e1ff5d6f4a28be00c9fbc7894717523960b0284aad3b22d0c3f2971870d852b32cdfa51d5b1846c124d824ebfca3300383bf2e053a12e8a63b1001af4344f18535c2510858fe6d2db712098ace7b1ec697230992744f055757e2784dd1e9ab6b55bf2137979a1cb9654eeaf966a788d3cd8eeb9d0f695dd5f4aab5872a2c63d3ae2db0d948ba218893f29391ea99c60b16289d9648722fc5bad38bb236a67c8f32e6f757218b8f53eb40b8304d9b54650dcdd14d07746699bfd127c0d0df715b93b80b8658160ce7bd0802db9a0b0f511ce335d9d2cf10a912a084714c4394282a390ebd3bd7f329c08b0fe88de15798928bd400b77fd8a3604b34cbd7013f5f2bc04f951aa5a99884314405cc7b86d3c9c97b6487429776461d321970e572910ba81f7c7d2ce959e77be42979539a4a1708a40caeaec04bb5fa57d92193050debe2914364346fbbd0bf997066e3ee6a33cdc959068b8787cd8dc168f416a56b3393d1986546f55f74545cf96290357f4358b79f146e722034b14dfb32a4853961989492076b569cd71e177a6ba8963edb38a1c283a03fd0aa80f671d728dbfa35b77475cd59370cf802820433a366739610cf7f6b51f88cc9cdc399e56b8afbda0111625179b097bc963f0c2d353b5018320088d0f977d2b4420a29a4802c7ab70160ed1b9d325cec2e117c468b4abbe2499985612b3fa4af56000d73899b2e4ea35fd3cd59edcd0ed3437284fb85f188d437840e9d7fd138ed301390b21e097f5265d35d56751c482be650dda09c873904349b6ecc99aed4b56068fffd282d905521a66b5f3596ec7fb335c5da2799292a777f634650a0e37abea33df4d351c891696f882ee775f45c4ecf38d5b0c50a3ab1340eb3b3e6083c1316a118cdf79f8b200c457e48c3adf94412bbbabd4af89e88b50f1d555bfe69f12e65606526b6b48cc754e29de4c39c1e06a2972045e5f8d3081608cb89467d936610fc8d307a5b0ec3d04ad80b9b8c4ecc2d023ef2ee0925e36e6acb7a9177b558a3b800fc02d737043332348b9b6300b7f7900e8c6e43d49c6145dd3b8c2e61e49973f0679b62168b70ca6fd602af53bfad76a586aa7d8911f5231f4410e52d02e027995f46b9185dc039a2ba3c13148a5ddc01bf3fc23999d7192adb5fab1adb50dccd68f6482d3047461d4f946ea89743f36a5099b3b0bf7ec9828a37b4dae579f435747c21f7b94633fd7058453472ef80ca5b72e6230a6d132ab73703a7c464f5175538ecf94005979afdc218726558d86eb298ac4999bc00958ae29be35ff7b71903fb68907a8476cdc5b6a06cf9b180dbea925c0b3206593ff4cecba74afed4c270f8b1efda36f82986aedf13edcc3207492b92a0a1896f0a9a26d3372be6ae08d2762a5a2955ebef234b747be20201ab436025fd953eceec51fa1ec107ecd0468c3eb28445db598d8943a426bdcc821a7abbc8fc111a1e9f182cb4785a09330ee72a9c301f2b0ad96a313867fcc1234b4928411153f9a4aa2437bcc020f84a98930abce6ad554ed84ceccd0529d2b84eef63bdc50a28d0ee5788df460685c432e14f5b93026fe0d4e928bb75274ded636f60117d1819f29d646028aee3543f74af04bff946be69fa0bbad0a513a40c4b7b194f4b3e64583676f1b2c421e25946af9a4ec48a1b4c40106a7758577e1a929353ecfd44699f871b96ada12c384652ad9b319c9b3185146b44e149684f59f5a32424838463d435049eb04c571238eb7eab9418cea7ed086d667286405b175a41a480a1ad0586e3160c5fa1e39700a531ffb4e64dbbf79768966232a2b33cbc1198a09f5f75b41026a7562d747cfdb1c1d789bfebb66f09e61bb68d849cf32687be017945aedd9ac5b90c152baf84a7e87a454fed76f677bbb6949305dea35ba267b1deb0e74545792c9b10199038dd34b4bf2f78315def239c8174439f909fb066b28e5284fbef9c10063c9283a9ae85d0318466e109acd06bfe22c39a2dd486bdad6bbbe3a0072afd8f1d44c646a6ffebe26f175ad65be69ec255f87d03123a564a6097872d7c45a096dfcb1197408e5484444107d0cb7f0b691fe22caffb053de4b2afc7e01b77e4414ce2bd286b44d78cf0d9d5715e0d8fb7e58ea1329e0fa481398f87189206071af5ec54e5f604b99fc43272e28a2b4d0ff063157a84a49121e004fb4c952a3a0160ec0e46f7ad91d3bf189542e425251717041b9ee932a6a14df38a526407c06aec5b89d75db153a2ae575b0750a6e370992d4a547ec07e1348f0e55f7e7fc3b5748fe4d2374cc7dd856cdfe637e58a251233e5f4ebad279115fca9a59d9ba9f64335a3a326bbead8ca8c204ea60ee764270e05045828ce4ed001512ca982ced54c8b53677161cced4db004add00600f736dd64f9e2396def65b2ed4fe51476649e67e1f3c8ca625eef6d697c948b41f038d1352a7ac616187615eeab12e3a79064fb3fc1cf939e6a3bcc7445c7d2855f66fd1a307d00d9b93021698807b549b00760462dd4bfb08a05685e9ad6371d9e167436e7f4d182fb47a751fb4f284ec8044dc4d7043a5f058278515ba8043197ba5511566039db0d1abd0259c1a5370b5c500a5695f8136fb84105495d8200f64f1c946424a21b99c8d0c042b43c2b22b129f15cb96d073fc508a47517a366ae62d657bca5135921b11c1545c5f507299129788041d3082667ff9924768b23ee35cf02cec4431e7b6299158f6bae9f09a6e67a2f9f9e03ea6eb893c34410a7389c5a9184be4b3753a9828fb9c4a4d23d84491612ee8fdd9a36e3e8be967c35899dd6b2833c9beb70ccf4e30d23226c0a26ca0583811eab6cfee702bd28189b7b85527cdd48a4def95f39d0ba4c92420795e993e4f56468c7336362176692ca94df00ac2cd523f4dde735f615596f5e8c0cda37f731d9b2407b509afd58407bb4a03e7a101e19748f07c27fc41ec323a488c582cdc6482a9c4ad73b6982c8c3942d2ed450a4dbbfca89689605ec22a50cfd192a8f2f555840d02a4ca6d167381c8e6d184ced95d4c3f38fd1e6f6d7fb835f1590be8547930865acb9ef237f5ff9dfc27c76e20e3f6d87e7cc65d1f75e4d4fe8e0cc5f5ccb0e62021bec245d18a652478b516ec405275bf48edd854d1e2e3595f650e0084aa149d9f91b4955dca90030429b5988a9a41b39fdbdb8f883dd64112d97111888e81d683c09a950ab1ac4778bc90fc88956c7ab07409b0aebd5f980e762c6aa86b478de986a05ea4eb58f44934f165b5e419c946577b2e2d647f5bf0dbef8c456ccc53958d34aed34e1bd9a11a4e7e2256dd86327fa6f29cd590754420ea328b94a5d248359cc6c851e63aec1e21c7ffc28a172d82be6ead7d9256e23cf8a0d9a8bf31c7d02bf854c99a6fc07c1129bb81a37ae483331d95db167888dadbb27018fa9b144e6ec9d0f063a0f6d08d9455a6a3bb01b5d1387fea1f42ec97cd7144cbb0f570b2ee78852638f1f9b2143608a6f7927afe2bd7e6feeb22c0d0d135531176eb40f60d4015a2ba811af000d6c8595d409daa9100c5120e2e0b0281cf8627820204574492be191b1ad9db24dfa0449d4192c272789a2500b83c515bd2576f1f07aa5c1b916a78c6e8686bf8f555dbcbf3508905c08e3179d18fbb2ee6d52e6900056c0b52e2f864b544611470c6a81756ffefbeb7c9d35b7442c85116e7b883948ded276d81fabea9f4d54c86451be502a21d885a4a9156ff75020d83c78566ad9a0848c1e2656ee97038da58db9955842e60cda322a689915a6f47684d8c0a093f4c81f1bd044021c36f031a8d4e1c2e79dd4d57921f23aaff771ba9efb3f4397dda1fb15929a5d92fb9da30c935babc78d58f57fc117a8a3b5796ede2cab78e081a6ed319eca3a4a6b87e618463d437e7825c5a1ca8496c550e6863fbe4a093de72557246496908582d795861a777683c669c3b94505e3bf208a40f933cd96a60ead4eff8fc22501ed5cebe0ed3e91952bfd35678ff6007ebb5fef7183cefe3da8d8ba0ac26ef8c7c73133279671f47b38b2ed5ac6ba45bd16fddd5861324e498dbcc4f198a45ede1414683b233adae93cba355e75ed82ffd90b3b785075eb1aaa76689040b97b17b26830ca00b7c8745200b10abf4e1289b1c07b3b51aa5f03714cf0c3069502ca5404c5444fccafe365cee81b13f479e886299bbdd0b4b364db2f6e240f122708be9ec1209b85c4cecae61ecde190da835f795cd6296e3a624b29649d7c00ee181bb7fe126d89d984c5963c3bbbd60676da50be5db2b85b2b913eac8b1c0689b02b1cdd4dc54ec9fcca0c1cfe4621e366b5512f836ecb750fb6c782a9addf083334759371aa5e9355bfb601235791be596eaf22cfa848db2803068b12635d3c6225b944b209207b4cf398ed1059130065fdc29eb504d0e293110a11f70eddc8a3a28e6ad6184889745282237b011b9bb40ca18cf7b5ecb02e7c2c69e6d44fcf0f4b62ca353270a838b7155b42f257ce85c212250589aac320621c9771863f50c5a6cf5be24647f8fd23ed0a4c5dcc25d65903a8dad0920d594ff48f9a202f7329be4f518092c121985079f42900af6494617476c09437d57440458126b1da94d78e1c036f952987b01403879e444a2354ccc526c193bbe11461ac7c83a4c9e5fd11a8e17460f46baaba67a69e58eba555d6b43229e36b1506adc0f3288e72eee780bccd5996ca1bbed070614d6571797bd8dee835e53d0dd55465a95a536bd7673e1d0f662af232622ffecc9b8fefa4439c9b6c54c9c0c24749ba1b017bc15b54b71f37291c7264813e368faf07af42dc0bd9375a03cfc8d73545623ae3299b823d4cbcf52be973baf62768a6ea1f271c09d44a6ec94d6e196072b38fac80f9d5fcde1efb08b6a6a694dfac4d0691dce3a428f9cbd21b6759228b3cdebdb9810d2d7766d44218ca53bfb1e3a36cc09dae3ccd9ccb815f1aeaee22e1fa5dec0942ac0c3a0d9c28394c07c62744eb9722ee89b2513315fa79491052adb1414ad13661abcd71af0aaf558cabc70873e7127b041dc6edc9d82631b5545851a3ccff124f359c15512febb237f46f949b2bf82b3f0af316ac26abaa5fa07abeee269cc05450c9a2575b8e2df0a3f0799cb935ed4f38cbe7ea1ef9eae351bc7ea9a172c9432c45c24ad554808b52efa897a207860229908c8730964240c6c8b195699786565eaef7e9975e53320e330e6927555bf0ca61d54a51990912e0d817a58989e03417ac967d7e6bf958d6ff94b5612ffc3eded6d5e1f2d2e7a2e13144101c6ee5b6208e810f047e766e3beef0d18c1c7ed8ef49695814b575527409c27eac8f3bd43e991d1060baa30f3157ffea00d2e58dee08cfceec268b52f0c08e39b975445530cddd2f2ad34e940ac4f494923c8e1ffa54dcc8df8987c2bedabc92ecfbe0e31c8659c0ccdd09cf6779e9bf290e6b048972b945c89c6c57ee6673289ab39a3939b68751c063c922ed378f6d7c3e7986e584b7f32ccf34d3e26f8b471c615715eae8a6655cb174c446913be1d87b83bb6ad203c5308cb66d22e619bf24fa74ac2353fc2225edebc54ce54f55212cb71f511492a1b31621fd9efe6ca9e2ea95c2d103249534f892b5ea91a18de0bd9fbc563e7e12263719eaf9d734ac1134302c050aa5c56004081b8244f8996788a790b2b3ba7579bd5a913e0569ed0371ea0ae897e0a608e4def86854c750ec1577ac6a38787b079f5adcf23271e98520f9e144060adde7ebf732cd2fe06af8f7cf14180bc80492ffc982a96c84da9193352aa475724cbe5d24787582d5d2e22588ad8d1e7f6d58b29ca2fdab9e39a8683ffa0ef188b9ace1fecbfb01207164442353eb6bd02ba43a0535d9fe64f42f7b0206240d76fe37557b910ee0f651d3961601d2119b4ac0941244a8bafdc5f18ef510a0bb244be532038fc6b3adbdae578b767941d31c9e18c5caf344e348ed6fcdd0cad169a50f5bba5b613d7b55fcef699623794814c8246f7c77f21e418edd75dd7d273b718bb8960b1993b18a7c782894ee44599c047233bafd48b87294cdc28ec889519dc55199f5a8cdba289cc5a89f85a89d751d9d3317c5ad73e39f8df6d89c9e724ebec9f54655f7f4fc80d28ad615d32202616981688d65eff59d7fb989de139b9a06045ceb5e52be773c098e361446e18118383c67fefec0565f941a973ad1c4d249b6d1173a2c93594372ec33a44e11b1a94e72f19bb56c35443e032ad055b3702a40e3eb995f0325a79e9b7bb816b0da7fb77fb1cf16f6b1a2676f85bef454588df0618029774fbb78c3a73ab5c3d300b6b6fd1aa9bcf9aa1650547fe8dc7c4137e2171366c455617bef9af20de25c264d66c6ffbbabd505418c114ca9717de3694a3eaa6a6192dc02b165fe4376ed6bcb89aaeb5d895843e9754a08aba315ae48a4891b2d4fd44798a63a79ce5a7d97f0675307e4f49b88e14fc6ab7314694c486eb544ee2a098e7a4bfb6f48f48fbdc057700f97918439541b0ff07702cc5315ffd83b312451236c3bf30f590eaa34c2e2ffa5944a923cbddd26d18664f840131382330a45effbce1bcce53c34f5cf11658a8ee951dc92013cb35f110be3cbfe869ad331febc79058587f3531f7293bed1538cab1e10653f2e9c9e40398ed302d22db0d49991698669cce7360fcf5ca5ce3cef00c994b95d45d97d9222f1eba1457924253df0dd8973a56b234ed5c5f95d28907f9793062980bd5e11b3dbf1357c388b5b2f653e3f5efc01c1dc8bcb9479f1649f0645cc62ca70df6b3006b617f33b77b20f74086312d8a644182a2885fcbe4bbb9983ccd3712c2740252799cdd0fa19d7ba9ac71e61f8e6ea16cffd4b088a5008a9d9809b24436a724a3ad32ade98c8475b674be355c17d191a5a389e331238117bd48e4b281e1c4333d520f687335074fedf218216315aa80485724c542c315bb450c5f89cc0d5e238a96d44d49a3683875c535bbe27550dded8209b866964cd83a2126a6018868109da909060a5eb34fa7e68596878af9a2284fc5c187713683cca8a664d7be0729f469bba9f216b82c69bf89d0a86245e6525deef9177a3dea919df1994a3c68c5ac6f234650ea79ae89f949a4eff2602a3fbc1695823df60a8bd449e58b33372d6009a4e5533b097eb98a8bf832430bf912c0109c20ae2c80d22ef5b3492a03dfa508bd02e364f22919e93a4e679e953f662e156883147255384dc628ed6190d110bca3ee6ff43b9315f3df503697ce195a986d9417450eee553ae3c3ec70a368c5964c79ba281b65e37c3bfd1f485eead1f47de46819060e57a8cfe4586232418acd42b855e3444c95b06134bb87002e41d5c106bd29c1882ecfe52a94689218413e410092cdc6fa725c7194adfaa2c5eb61127c9e05944d9e309c8bbd8267a0b2e811be1d2b1af914ec128cb08a2aa07f5b25777238ff89aaf00f1b13ff3232c24c2dfcd1577e264eb6bb911f81e4a32250d9d6c14c71e28c31ec20b75c58e5a63151b8a4213621bb94a6938bbd55423e277b8b9b35a5ebc3939b73aa7df57c02def32bb590e615bfa144103ed78ef54f215a667a4d8800c0e2a7b11acb2a63e3f2c088a468238dd70e007733b61bf9ed3139368d39afd0e6006422f73749bc60e3318e0913043dd115462a478b7e5febaad7d5328cfb941529f0f5d9de462f4f5542a76e39b40efc605ce8612b5cb57a621025db5a44585b73e2564cdaa6b72ce02b684118351e3767bf9bf03612b2cc5a3a54d3483b8f184b081e48144370761c196e735f7ef0a5943fc32fbb52fafaad4a55fda54a71c1175492abaaa32479cd7ab1f1044b05b79f5e8d597209877953dc35db4849383bcbf1646ec0384f78eae1917bfaa10c61ef6f1073b6e858d1f0a44dc7235b6c3c61004e7939f1fc3135861e1386838b2557f9bde39e7964016dc89069a9594f3e959a4de6e6d202c25a923394f96cc2a0597b4d1167cfa9609597eb9cd3717c56f850cabf1d370322a2088763cff3a51a82d66f9d05788f2df9fbf2d41d3aa3f1cb6f073602bdd8c80e7b53a7294dd27b583fdfdf30d40b289f3bc5866cbb4e05cb20f5a71f1da1ec77fc435383c2098b4e32d865fd774ef81a57a5ee997672df4ab825a15916a4a5a7580e7b871d54b1c1c243a8a536dcbf90b8fd66a297d208f01fd757315ea7777e56cff263a4f79a8de73b0d708f92ba0e9cae5ecfc07321ba1f5920161e0ba2f2456eeda2b496fe7ab29e28a19819bb39394d183136195ecbb93550b74c3614f6c8b344a9ae74853c189d31a340886eef9ad7b472c423238ca6e93f81a7e152da2a92e0068154c1f00da8b71524002dc751a2dd523e5cfef51f58f390c4ba58ae1d7176c88c9d31bf650369da5c183f92f00dd3371844fc060a050d79357ee3f127a195772c6776d116790d67070609b333a41a74d9de72a754896eeaea8565484b994aceb47113fe74c6c6942a3dab3b6374688fe50c0ad2d3ef37ca41d774301780c6cbe458889488743102f28fa4b16088cc67814c6c6c573156a123be172138498f8595da004b9fbc6bdffb153da25e9317a977ec44e9fd9760677759dc5582593374be299d4176f0185caafb866d32bd40a888c4ca4d97288bf9e9e5b93542fc66d99229e405e4f7b9213c201f44feb4e564e0b5d0e724bf0007af94249ee32657059a41523c586920817d19f636dfc926025d642aacfec3de4c317f583e4d65ad11185c54dff32dbae0970761ab5d45de1da1455765fe15f65c02e78e7597c392449189f23b9b24d9dc39a033e0aa2b2f276f9d1e761f6d2a1159d10053bb32475f2f3993d7bcde65e7ddf3c60803cdf2aeec9660776ed493056090bae94a95477c902a5f381c0cccea55d7222453a25a570ba9a609ebc217a373508a3f162c134aad15c880f0abc4dbc1edd5e966f9f73aa34c16dc388e182885130483a61c9504dce22321e876096ff76544de37e2aed3ee32307347e5853ec1869398ce4cbd087a991146eb638387a3168c7cf20b4fdd20492855c7f7a3b19c86de60bf108e40ca755d8a2b16fc22101a63edba7f755090cdec8c7e434438201651c09ddfb2f5d7833d4bbc394262fab4290eaa295e2acbd7204e4bedf8cee3f4fe9c74493e626925dec15be876d5b79a86aa96070ea7e579434515d78f034188692f6d08c658eb3ab6f5065cd4e25e014da6fe5feba05a07d6304983da72bf07c12d93907e45272bb034326c6aa0cdf12150b0d0449d5009098165c1ec185c4698c28a65094c74054fcd773f7055434272ec7c472ebba6302ed36c992b26dc7476f6017b21c58ced29eef1f6b67b7b13d581ad4bf6f5878eeffbc9edf9c9f1df84a168a1ef8c9d73c5810910e3c6c3931610622e4306b93f584d7ee734efd06a8bdacaeddca4cdae0f99d65f1ac533931f98062d2da5dfadb7aba8e5a2c721e10104d84777f2db1227dc415adcecfbc398e0dc1c0e3971db26f6cf3b71bb428d44f1d138247ffb929a59b9d4cbdc3d0fa2dfa8033f19b39f1acea9e7bc93235f5d3a8fdacb45a69deee8385888213f0d270e4d9cc339cb7f053cc0e593c0a8af6eff9297d13e8f3bf94a228d6d5618813481e1d0d2dc74a2d0e792e6d1523ab0dda58d090925f7532025ac37381475d49977c7a7b97abd5ab36eeda75c5ef66521e45225d01d8ec246448b96c7b63a23412f58f3e31f52f4cf503fb57ba7dc86388e654db87d75dfd03a63cef9883d371028b3a4f2d4111c4a9e3f0b90db9c283cc5aa7992a602bcd677b9f2fc8ca6657d92a61f87f22c50d937c85cece46b433f42f9b593ad16788fcf7b9ee7b220abdbcfe083a2e03ca969a2b1c762f4f3b85f865b3ec93376e421338acae8d0b9df940f1e65407b57b9e669d19b17f2199890a6af3f9581214e133e503dc9fc1e87e2d6716ce312facdb7dd0c1550afd974ef7fbb865b5872faa704dc72740fa9e929d0068359a45b78b3a1910b7d4604e9a8a79795b62aac1d7789dfdc1f7406f6d61bf311dd8729f4a09ca8e0cbbf68dcec65a81e36f9348f5ba417786a6d8cf97c442f5fe56965cc442944e8fa644550ec7ac9752585869df21017df39b2e8590a1e842f92a0013e0c619c4306c0fd68699964f66751c19ef17cbbacad6caeed6f7e568b55e42c9150e997648db8af31cc9f46bd05c17890e993b4ce9e372de63bed3a9df18269391ddde81d4a5718a07bb510244a2b286c07df865a63c8a8dfb14206388ccb30a8e2825e8c5f2a2b6c934993fd3c3fe1327d9330404980908b3c22bff72b6c610642de5dfb53b33449462d12a7e6e3531e962dd5aef428cdd9ee8785990d0df641d86c840afb81e78348129e0bbb6c8326a0882284bc0370aa031afebec41f70d6085140940bda09bfa0def992b7d39e711a3266e3a71fd3a3253c5a439271c53e7095d2cba8e489e49437191442ec5caa8b64c2b0e1ba001376e37614f30a4c963b34e19b45f7dfd47e26451814d456469b11b132e0394e38613a74dbd4960a186fded771ba449e1795ef2a0b687fda50b1043e9b303403bafbbfe2fd70a792d4239c6240add797fd8fb36e2d628a86f6aabae9662b3758bd6e4ede97f1faf2495b67d9e464ad637277fcaa6d5f25e12a4a4f0260ebb8425ff17c7f5af7999a9df1607b003a01a64c656823bacc4626865d578be76d83da2fcf2b49f0c07850f1abdafc00dee54e55c7e4acb2d3215c2cca6d2d82dc04b1f8c69120f988ee1a5501dae9bb9dcaa558dbbb0a6aa724f0dc98c80cb8a97c7ca7366067ecdc51cb45787ff01a6851e76994d7b99a6005b823284f041621acc3a2b48739d555f9759430b9616003ec63cdee19499ed19705df9818c7f4d6a7731a3317ef707655544445773fac11bf7f4b80041b02c1d8fd08d41eeaff5e8a86da6a0b8198e82c71c537e0cb8ee0d7a7f4ad8735200df348bc5eddde8e7aec1805863493d4e4aa0bc542efbc38837ea55d14be82bb248c90e58c6435c720a308eb7b95b6a6b64f585eaeb5854ab63be6c69c94f3355722e20fb6fe255938c9674ae7591c40e2cf9d5cd07c84f46340108e26ccea0be11d192054d55e053929edfff88bba593bb09b6e0186fe8b43558e86714a4f5f9a1b295de67ae4be84255c60b479ba4ba3d6a6cf8972b6b190a59c53a79090a3215ae129aad656ccee99b52fa827b003d7fb910c3e43d5eafe343338d557c6b0fe029b97c325171b041b2061e34f6cc35b9c4cacc1861d3449404188b06e1449a5052ae7aa7bf7a6b864008a828b88bb04f63b4624390ea100f63e8c46b5db92672654075f2fc2dc6bf8a319f90fff51b5c0ef19d7f9e00b2251fdd6db342f7a8577a699f4d6d54f92ef308562eb04b8cdc7c69ec4f663ae7fab8b2b40ae7c60219b952f36c50cd94aa4739c9aceaf27e98660d6494e601763560d014e7276f73b764d2c672d581c2a2e850bff9443b0dbcebe76203e5eb99a8c97793444c0103b6873b4cd835c9076667014eaeecb9dd8b3f1d0dea86932b8815b3f4d66ae9997d5733791c274d1790ee3dc495360591190afead67aff0c5b927c6c939597419f7afcdad576d20d794ca4b3c73f6a0fe263844d878107a116239c4f95c2b4070820ed36a5105d120b15138d2dd107b399651a4bfd4046891ca18d122506dacb83a3d8ce58118472a3dd5bdccb38756c3e53e816fe3ec42f1b67eb58e24e15426380c98ec8d1b3dafc3386551296a7176c48d07fa046059951b7f058b2b862894315a3c450981cab87c26b930983f678f6047047c46e0151ccc1464557e18b8f8c8facbf8f8e7982650981010bb4901f1c9c0e61ccc9b10eb9321d248143f7dc487d9703735ab4805df31e95cd878c67da8b1eb062be001543715aa4d3c3b2ba6cab6e4cde4e309e038d9c3df278c1e4fee86c966341b35ae1663d029a10ac62c06b0699e4e273e891fb4b4eb41f4635a8ecfd9bb2d78445e5ab42620bcdd01391b73078b1a7d38b8bd8090ef39ac764b04291bfe30365992ae3089ffd9194a703208b40399037c12f1f68bed3f9bc2a135f1e377e670e69c8b5caf30fb054df086b77a635502ab46444d57a4014449206c22a284dfa75fa1cf5349aa1bbe392f3f808ba1f402ef10ff2683b62782f400e72e41d10ae2a0413a7aaa95f5bb903b527e48a86cb6d267114003549032eb296ad2bb8ba1fdf27c3df8b9b08a515880ad65f8a835713ce5cb0c2be0e0884a069e05fe9951516224669a9fcd364e63b1a91af5ecfdd80285ba0e590ba83a21af4e074e32de33e43661773e26c0ab233d4a1aeab73882dbc354d9d379fccf63f80fd848bed6be0c1724f214d28cd2eb283f600c0f7a5cb85c17ce33b214d9f482b3608cbe9d09fff2e3559991955299ec769445d8a41d691b908f28fc286d503e6962af48adf59f08a8f1e35df8b1ced3d11eca46c1c4b0913ee422dfed09e801721151811fb17d428bbf445ed31cb455d4176f1a223b6a4272709a6e822c09c977564e6c1fa54c836c9b2b2a4c3f194d63a749b567814ef741c795b851b60f6d1a1c87a753150141a6237bcd7e0a31865986c640f57046081aaa4855228beeb61dbb8cb7535433dcdab9041c76112ad98f48211f7b061051172732385e0017ec332b7e4407d3a7c29652592b361a7f4b18502ec7b88fcccf483b3f91d6023b079ba46ee7406a2de710ad4d242b577e118c16626cff8434a4b58915ca50415a4b37b24d73fed1da1cac72d03c5a9b8429744743fa699bdfd1da7512e11e06e4365a534c9843e6abd11aa4b4f217bc9ad1daac55448d458dd988ae3e268f395ac3d2502153ed23e511caf7851504096c34f39fd6ee679933eb0b8b2b95111d4bb446c90c5173f3980a750a8f150cea03ca1b78db358f217473b1f26cfc2f4787d6a096a8326530c1eb686a6a3db6469ae82e0b1b5a2bb044f25becb8ca36ad6d605705ff8aa26a57ab50fe94bbf4d6b0143ea149085cc40625c90f156b09e24a0fb4e6d92509c7144b219f35beddc58908c149d01a79a5464c979f0e0cad15c45a6c63614e165a4b61897e2afd23912631165a238e22635600e039e39d5ba22acb58e4a3b58e869727db9e179171dae4f49622610f19aa426b126ceb489fc8da70f89b13b6b28e73f339047c19dfb953cedefaff081427b4a672bb4da6d8e36cb36ee786fb03aa6a021b6f1b5a5bf8c283519b8bd3a854a2008306686d663e05ca365ca9e21cab37df0aedadc69b34252d005fce4ae0b3666ed44cabc9934ecf4d5c19cf5ae35e69aa85ae542d5508b92caeaff0df56a7e6d725a88267cddb9c673feaadf1ae7793aaf2152166f4aca46731c1f4b91814010fde79d65a185261f5ac0d6d91ffd1b33686b8496c928f676d5fb3ceab724977ecc893d0035a94f40db06fd4ed74ed5a3b1f67993a3139f84f269f85c7516f18aa2c9c51153350f481dc893c024e8e440babc9e7d05a7931668a2e06bc5498ad68c51b30bf3a4ed562520be4799fdd1b6003637a18777b04f72d510fdc5afd2107096672fcc8de0f14a4221f26a3804b17ba0fbc65b937169b37cad5ed8ed3013c1692a73dd26a2078c26d51a915579a96428d0b457a4229c3f5c14194c59822a469d85ee81b37935f6842d945bc8a3baa086e60ccdb08118339273bd25868757f71d47d901cce6495f62a430d6f4f198b9d01e0d2345f284c495318473b3c0314589ef1955691502ea2210b1d1151c92d73d9826c26922d3da465c52614ed4566827aaec164e266ebbfa33f0af55644f54b9bc76bebac3a1c209eabb691dfa58345f2490b0330aeeb3e294b8fd36cb476d39b0e7e9a18d0f86c54a232cd5b98b12fb99f73b319a6582cd97a542df95aa8e8363f31b2647591ddfed90c8b44b264530ea93a9cf64f2ae8bc4a7a330c475625c642112392489b60da1ed7f4826124ff9273a5dd5bb2b6e943560d5530b41898faf2baf1d37475854d90e14920aac08b0db0f14002cb8ad5c60627775a50b189204f92354d292c117e43086a36587e36877ab5e76a2bcd2f5f58ad42731c5aac4e9305128bba8f2e43b36a8f5cf4095a81a91f42fd6e6cbb2842b863cd480b658f4e1120846bb1e3578433acf270b02e0630d4af50cbdf1b4c216b84bd3e37718202e60eb0422596c8815c42f4742be11bc77a0baf991db9691b8a6bece9ac107b8267a26c872e19348e322be375f70e577757e8bd1dac629fc8e91e9d05b086415b1982fe6f58c3878a9c18ca8f88543d4151c132ed2df57e2b6746c70e89e3102775cd99e5e804ef7924ba9b0f0995292c2667cb89d72b069b1a18d92f9dfbe5481bc9245053efd31fd1f37508dae36d6f7cb113fba55962fb451af4b4cefccea985ebae40a40295bf54e018daf5a9e2a687601cad5aa578bc90c9c1b04ae877ebe84f56f78f818f4153bc86b2a15ff664975ec4c4aef73ba9c79fe9a3ce69f065867c4d25063dd754d46cdb620bf0e1dbfa88ebaf59bf2a2760fab0a918927cd061ec761bbf312ec132aa2e323b19c336d49b70f563a21ef4bc4da46cf2442ea4cdd59457f167a75232aef08b61dba0a1740ee18b8d17b637019ad7b110a1c8c7c491cd5e9709aa99b32bdb83d30888040615974c7a3865e4a2296da316cfb0278f6e015a7b73bf04b0ce43313fb70afbd9f8ec28d24c169acda6e4040db6eb85706b8e9eff73d8eefa9a68a33c276772aea99729bfeb904927f63e75d04adc9a41e4d658e91b04461a86a7ef58722ed54dfa1378fcedec08f60a136b1845a39a258fb732e0cec8c3cd1cc19d051b4ea6af2e4cce1530be1cafe2ef738deb4d6e1cc24491c2786c2ad76e8d6349f36e1eb8c9c7a705ddfe28cec1fd0a063d92ed2bd3fc8091017b5d061eb7c9bf7206db04c7aee77bb6bc3043a4d6b7c08de129a204896c314c4b9cf08027bbcb54e77b70e850f6da1796320645d6bf4844809ca56ccb489027214f0829579f5031452b669f2a4d99b822d450787022d313c2d0f379e4f995856677703390aef2a4865a757cd20cd74785c4f5197c19c9d11cadbfc6601070f59cdd9d6048bbfeeee0cddc73d9759468e0f14ce6b8acfb0b19ca390a362d012f3998a97bd582ce77d8f5635620196b12c07332918cf219f5189e278849a31c83bf56c0c97f893f608e211c13211c10601bc428cb91b0d608228117dc25513a420302864c143bc43d9c66d1d039fc0ef7845b2d41dbc23df3c16c91ba3cb827b05e13f6c20c9dbd316366a40ffd51b06668641181316b6ecff61c782784fecdfbd49f5e96107fb9f1d5b9aa25eaeef0d71a35fe96a9891813baa5f9bb6e180f1152b20cbbc418879b550a90583ef138496f5636c3d06c3065254c21178766ebfd665512917e6a48d21d4a104fdc198d5aea7bba60bf2a387d12731770ad3bd600b4e8d7081426d7f5ae6a59c05388301e5567fb1bdff1d6b2fe793d3840a2464171369a8c618a32138e4b585b2680eadfdd16b7498d914e50ea3a48380b0dbde00849683af14f220a410f991bc3314d4224db9e7aee16cff75b9f10d816113553e6d5aa754923e83bcc24a8f809584c00963239ffa63230887774cb1ae38a00c907801670dbbf73bea337b8a409bb885dc8c190778afd4715c5e696456e1cf084511a82275d1f0235c0d83bb6d32a3da992c62574213f6aae394917023c6e34bd1889193cc2ffaef1d309d1e1e3c6034206da7fe0adfaf752238cba2c21e6a94f401f05dd646fe059f2e11d748da9b27faefff1ccaf5c1e604960b66f68774b64370a2af69e6257172150efa9e16925b8a14b5736849cdaaed1c5325831108c302f8f2eea5bf5bc10746144bdaa6d284e5dfc1d9a892665b4b708d9a8491878092c79088a72a8642e4e1cfb2ed978660ac68730950cdb4e5de1821750d058005a9d37c6e3fe9f286b569d89391250a857eb3efef7156611bb9764803d44bfd4d505bc399d44953e2ff5efbbaf629532eae8b822a930f14238619cc6dc292f9aca6c408d17153afd142e6e46e9267fd1bf1656ad1bd10ff30931c38d94ecdcaa9d8fdcb9a3e01248e341bbb60473d24deedc485a6efe5934596bb45ac9194ab00bf60cc4c3ca088c88a1e53120f05cc04d207d284f1bcbdf5c3927c162ebcd31add7d295e68d412051c722f98bc57b7e06af86dc28b5c4fd80901e5c08029a934d5a58300edd4e07487fb045d04d939ba6033c200b461718099aa4d6a342f10970c10b414916602da85208b55c4cea64412ed3007510a09bd40353f2de4f90286becacb8bc50ce06fe469b88580f53ee57b7bddbda967b6f29a50cbf0532059c0546463cae0f239bb225c51045e4ecdc9f1ec51d398a48355c1a612e8e56710a868a15ae4e520b42c5209f12491cf74512e6c791220dbca20e1f4b37770acd060dbe197554c53616ee958a38221f20d7aa4807ac48a6580449519eebf503c51b912fadd83756245ade5811f55ab87e4c45bb245e90fb83484491866866abaf889e1288bb07579cb1566c51f6e5e566b540acb7054a8d29dd24a4228ed0de8f6b37a453541b0a31a4cf0dda2b420511d143449b0b341fb122dbd2e571e51a91a25312fb906e088df8293e09e9b83142215c65df9e511062921e666d25d09ac45caea58fab2293149992a804c94fa8c8f5538c4ba1e31e4911c215d2471cb20717a418d6c3441b0f5ab18798fbb4acba2abe49b933943648452721b39f1828745c351421dc157d327b45a52045a01e4d6d6e08341749ac28b62c5a5d6d49c9286f20b948848a7e8a5d2774dca31321f8fa8a7b6e8e20ee4e0fa291b6e2126d895890a50b73e5e249157994c52c2418427d3f7f2674dc33132188f421d9d36ad3203d6dda03c82d2fadacac2804b6b6e55255b5b928ca5076243e734f0425f5bcd33768a8ef37f7defbe1101006a46603047a45a440553c1c084a070562aa398186764c209e9d0d34b3f3c3b7f343c7639a30fc23908e8e5459f1b2d1986304fb63a6b3fe78e5a83f38c1136df1ab755b684f3a1f361ed4874ce7f4c1ea3159b43e43383e3c3b3d486886c33fb21dbf3c6f47aa9cb160ef4f8e1fb31faca7f767e8fdd9797f60f27ce85e1f36950c7882157cf25132d5d767a8e7d787c7070649cf5b48e721477be06c7a6eec983d3f300b0b2f3a656cfeb889ce39e729cb8ab241990d1ebf8e6c8b373c45da3c1ebcb61dbe8e1bcfcba3d691473c156b2a2e20ba123cd9a74dc1bf76de53b08bdf9e1b1dbf3d70c59e2e8ef5d4916952c76f0f10eed9e96ac76f4f4ccfbf8ac06c08c85f922d6b66a9c786161990b9d7666dc6bc31418da01dbf3a6a48e63acc39e79cf3efe823eed84dd162bdd2911bec8ea257f6f0c47a9ba7c975caba6c1378a6ac8b46d5a0caf1d627116802c2af0e57df6c09191ff3677cc41f64f7abeafeedb2e0a0e201e660d47aeda6310495a13e6615e3abd384225521b268edd6b467925c6b1ab96c2db0e357c744776bb4c18e5f1d111d694ce9f8cdd1cba1ebc8e1ded339cd38d5e73651b03ea220d7de66774d3936eeb0b0f02fb3ce794c1330c605cad1f0794c4dcc08748fc50db287c1aef897db8e5f9cb3217089137683b3a4c1c1b239e9f8c5519eb51dd0bdcd8da63c4d795412f148cb3b7e7188da76f8c38e5f1c9a7ea643c529ed062dca90735ec4da1c3aec1bbd9fd6668a737e9a5bb7edf06fe03af2f8f4e7b6a4ce98b386131c3ee75d3fc00e2650eda4bc986cd18edf1b17473d284fcf79737363de84a0d94a6a8cd8bc367a33dc681a755e9ba9fe1dbf3636fa858e5f9bad8e54396369549d9242fac04fddd44df706dca2827e17dc8f1b003ffdd48d41fac07f79007ea3916f60346ed0f36e91e89f6b9699817e39f526a1e69cb9e11787afeb3727a96b15f8df929c57a062adc200eec5b193e992d97a65b0360080102c461ae30c96c391139b734bb209a1f7f2445457038f2808afce04281023945b71098815a739a52941ac281ea018fb81622c2e39c3337091b1e45a2493a9e69224a7e0924b923a8a72122ca42fcf2f093703a3dd14f14a96487270c09b0ca1e8e783a25cb34cc210b1c06a4afc4657231be9ad9a371fa5538a3b29ada4a48d9e11a7165a475a74489358fec36f36b14cf64812a0c826b922d952d4972631ca71f11424e5e63502241d5f345d772a5eaa8ea2794c5ec901140d8bf016a6b34b6a252f1469261bc90e8ae2c8924761c9cc46220986d97efc8dd0599156a44a6be9450a2f6d86998e15d9100c0e5448d3ce4092af1e73c92caeb197177a5e7535667d256a207514c563c26dd9cc0cd729a0082cd995744a8e30405156d267a785d850674292c444a09268918b23c097a7197743b6ce60fe72e02813c283c65f18179e8c137ad704824e8e0efb21a08f1370d3fd26e409e82308d628d6ee3fb18937c6287973606161312e5be4ca87e6e38ddbae47a858bf2da83a7e5b30bd517eaf8ba1b7859fdddbc2ed6d11a63fa778d0dd7c164a807cc1dd05fa38a762ad5de46abafc83a9d3a9b37ef2fcd4e2770aae033fb739841564dddf289e7eae67366a3dd2f4c08d626dfa48e0f9ead9e296b3d5e999ae40e2d9db13bc92b2c0cfac4b0ad04d53feea099cf30d9357c037fa26ce5b9360146bcdcf67c6196bc62de38c15f4aaba5a055df5116f41c084fc6a0b24e2de616f30815027a07854b88e5cc1787ccd7a08baab3e6e2356bbdd552525b69c8832345ee50f4cbe910788800979d5ec04703f3f3dd3471f7866530bf9bf2bc63a72359fe4baf9ad39ec66faa8cba9b316eedc79ba9ebc4cd52bdb04fe07fed2734e53e4f9e17441ac7d7f0ff575c7af4d930d04ee0dcc664a7ce99c68aedd74d537e2165be5d63815afe907d5340b5f15be4a0c0a892eb857c0345fe80adf7ca12b140a858f0a1f7da19beae3bf74f581cf1283df61bffad2814410c39ea244f345cdda0d456fe517061f77164551f47ce2078daa287aa227abe293ffc9b22e52f3f4f357d03db3acea7e78966559e2aaa2af6a6dc29e905fc8ba983cfba65ed119eb9c49c82c4a34f99465f50eab51f4eeadd63b1a886d5dae47e4cc5156a4e6c9e09eb8c816d9667745591f0575534efe7c96e50206f6d1d73b28e7ec9b98fcca9e404466f63564c8af7217e5831aa5bd00dc32219e10452076d047f357dafc3aeb1d9347ff24e66f89ea6be2197c9393bb27c63add1c4ca1ab7e481f7de10b856e0af0cfcf6fb62ccafe7316ad410c390b5ed4d7372350960435c822823e9ae819063bbfa62d27bbaed2f1eb74d59ba2ba013a7e99926ef4b5b7af59a0f769e855cabdcaa957b9f32abb56b90d67a26f56b91cd4da5d7510dfac71ab9c5e09721dd4fbd5f603b059fd5c3f0fc2e9e76b5e47aea059e2870c3f83ae3106ad3386b81c02ff8cd41a5e67780d2e5277e12ad7f305a1ca75d65a73f783f033bcce5aa341e49af741ff0cf607f5cd0a875ce976907d54b8ca75f4397739df7c73a1fb21788173ce6e9dc15d831ae5d1e75aebcfaf6ff44a906591e82357b9cebf65db0c2f6cf9c90e6b77d61a70fdfa3f045d645eb506fb7798e6c1611bd6f7eb9b9505ca40cc441da58ffb51887a4aafb46ad72b5cd7eca3c4fc18d0efb0d90bfb511336fd6c9273664dd6b8bd065fbff0f5b0afee87e0f3671f0962d88569cab2423668f2dc8405ea481d954f0cf11a07020902fb21f54e67dd02ccb420863d3f674160bf9d8eae71acbbc6e504a0bf6a0e62a86fd638391da5a7f4f1d43a6a8ddb701a0851b156b1317d24f2003d7399ae7fd5dfbe50f3bbe466b4d162ccae4293dc57dca273561a75cc77694cc3e1331430803940601fa06f13b79da1c0011158504881031ed80065a857ccfec1b0e377c945df1dbf4b2cfa998a8ea247e426010ab2a453bc5b92290a41229537f3c3edf16622ad3837a6b45cb272d35ab8424c39492291201b152274a89829fef4b94c7a6e1248b1062e6d0d4759d1889718964b26e54239551d11ff4e14eb94487429d5bc296141c41d1284288e040e17edacf802ac38228be8f702ebc9a543729d525c25f5cc24dd1495fcad3032764725e786b5d5b001f3f2d82ac28821054542c96b28eac70d29670e2906089feb4248516bae98a483b8e4c60eb1182e37a7aaa813553cb244dc2b72659470e15ad821d24208629124c514438ee85c203b70a81587c8de2e57c55571cd4571a989c668c8448ba3117747346e7e4af65cb03a17058f620cb6229a99dbf3e5a69051cc314544a2cc6b410f851b288aa2a999f5f475c24985d75c1beeca8bd251f6cc61cd20c093064a13786d5c3e6220e1809223582e905e1e3e4b9ee74a2e19502b8ed8baf08a4000c38708b4afb0175a297762d25a644c48c2b2eaa2d67c48d4488bda79334a9b5660cb8392f2233b723b6186520c10a39fb8bc2894e9379b0f15b97d8dd0667488c4d570090196e451d3676494755a4d944fecccf958536223a4040fa9caebfbf9fba1d903737161c9c882cceb85981fdfb28388f7adc889b12827302414e32de993eb889f0a09acb9c8b9536a31e43482913b311d76304a3874702fb513258d2eb9785213371f18a49a1a20da43d3fbcbd13763a2a7841cddde138d9e2f1b0440daaf578f900438fe8a2879ee7ab8e1f72cb94c6b6bb1a4c62b6582b79426dfb98b8aeca2b5ec828363274ecf4a4846969c138f0d0c56de0d5813b005a6fd069d84a51d58abbbe3199206276447030c2c272ae60f6b09c35c5aa5477c895295dc91a6af9c97242d258db63e19265044a146e632a7a3a790349939e79cef76d8a2e07d728e53b365e1c81217673c706e34e11c7424c4fca658c09475e9c84a9d73deb6ed0795b8726c5e9401c0a373a603c791177d02a8a11461048990a4aaaea7a5f33ccf353d5272a3d503ad6aea46043fa69f9b1b3523992e98fd76119970a2161b4921222d2c256073474670dd88190d31c275d7cf778984f3a1df388e326f34885405798b94a13921e0a67cecdc6c84a9091d6396540ffb6fc9cb120ae25ae0d7f12359b8d25aeb9c73d63a6bad172044be3001bb3a2e018f999580bff23a2e01eb01e9780eabe3df917ceb9bbbc26e4f1de378c365551d9399758c43c75b921c965909a058bd81b003849cf3e442a407af234c569680323d376e002a8e43024053766262004a24a88067c0c4f415f4e7ae5d640a6e982a42a40ab44e8256deba6ad7b1da0e0a912d38e33d4a404b74ec72a6e8b8e3c76c0450a034cd652799e6364d739b7b9be6dedb34cd6de63221b2ef181dbfc956c72d3901990f592d83640a6c190956f1d995d1189aab636166a777828e07d9d9cd03b682a41ca8efbdb74ef40c6a352a7eaaae91ab0a32b2bcb6527cbe5b37af5689a4d33ccdd37c6718edf5ddb21c8d79ebd188b3fcba49c7af56cd12a31019595e28b62bc11d9b446f1c96034306ced052f86531f9a0e1483bb246374494776766a2f19fe53a8cb1229d4ca530cda1699ee7e3997ebe5922b88c2432ec54ab94fb782d69d9f841a2a68bf7e8e4f0d3a369a1c486921f32dcb0ea50ce63a4e33c2949335431c65e152f5e2c201c6e901d01d93a13d2da1dbf587359dbad0d47501f1858961491201dae8496d8f584050959d905ad8c9c70126e1a3a7eb1cefadaf18bd5d5c29bf18d4dbfc6373563dc7bc5f65eb9bd56633c7a94808be0afb9d61ff47a1ae34cb916bf69be51474b8a07fde8af803f1005b704dfe4afc20cba7e733dc12dea9bfabfa74407744ee4a0e3ad9e12f15fe7c4140f2374fd238cb0b74fd513d5950ae67cc44631ee48b9888e1c765dd5d5f59b6f15968a95a975fe539bab06fd7c4d9426e10db8234d3c5095c01b70475de4495c51635aab41c727e859af1af4bc350ce38d1a54343188c490ebd8afe317c64ecf1d08cc0fcc2be8d4041d99923be894ecba401f814117c4b0e3c7bfbed1ab05e2073e4ec1cf3ff829e6eb6607e2fcc0578344e358f0f1af0f62eda9ca895b17e823f039500da604831f4cdf18a345aa60e5a88ca557b672975e09cb63f92c479dcfba48e37a9ee7b9aa7bc5a60acbb22ccbb2b9b3cf591258766d51861be89520664dfefcacf2018b08fa78be90d80102c4d4aa151283cfaeeb1a7cf5390f06553e6009021fd540e09f41adb23887ef0bbf772a4fca4865ac5a31cdad43d0fd437e8c57f223cd3f31d61b6bcd7a306160320c8c5ea24a8df5fcd8a85f29b6aeb58bb1ace5cf1fb9f6e301d03f5114a8d3d43481b91bb9b692c2eaf875d1d591c7d7c553efda44f1de3b1f8fc709e00f26eb726e0fbb7d93a7113efb994db140fb269f42eba6e0a67b838b0bdaf7e33f17137047dd02c80433ff33e79c733e41430737f8b5d65aebcdbaf0aefea6da547474db645dd0aebe6aae26918ece344fd6e53c33fef33c5d4c14453912f3f1a990b22ebaabdf019216f8c107b22eb9abaf0651347dd5453fed28aa9aac9bf5b0ba1823407599fa238ffdc8821238e8e9bb708072d05397adbb4853a333305f54bdce9c30b945cf6f46e116f3b37b72d051d81c4e18bc417f9ab6fbbfe38edc27ce305147b4363ff284d9301801393f6f3fe5916d3ad4f0f8550d440e7883fe0c4407e00dfad3ada28fda037cd4afd16ee66c12d1ee01edfb1d304203708bc9eb1701b7b427afbf05b75c78fd20e0160c28af9f036e01befe15dc127cfd0cf800b7acafdf830528e0370714b60a6e49bf2bc323f1d70819be9b68782171e986e5011ada130d8ff218882cb41df817882c0c3bd05dd2e0a67bc30966407e359f67cf40ccd0330350c88fe142fe935668d2f3b3edee2bdaf30779cf0f4c4d940bf7770d19881c834eeb54f355937801deb699bd00fae79b29a06eeaa67bc35fcfac4bee262fa6ce38156a13cc0217b5ef77514df25fc7ac8bda751e6249bb2a96244625f694c5927afe1405635d798a3fe9211a409bc6cceb67be76afdcdb860a3bc07beac8f5a893828ca9e397ec4447e68ec976cc3ce6d791e9585ec7701d69a667c7ef585bcf6fee31b69eedd40e820055270ee3051e82533583c8dc8e8f34fc56b4031395a45a6362c1340c33abbf18be22312a3086d889f9b851f103d675e16be94451144d4d1445d13357b540f98a73abc8c15f108513a30ded865d5b8714635a693855422ac28abc344099b20733ec4a602d797c7c40add059faba63a154d385de99e78ffc8264b6f396c3472c058c1532ca27b431b2be28962f9b74ea749e67eaf4db6befa2cc5584a24c1180221254d30211974f0953257888b24228b10afb3abb829cb9ea3c43503bbff8c8828c88e1fdc9806fe78e84121c12155892194bac72ea84ca68be7435785160756a2a8e98b448982599a55803a3bbb0d5b46d94b822b2646feac8006ee4c839a3e1456342a8b5b4c55e30a12f462efa9e96868492652d633a267ac01c29bbc2762e6431857175f4ad11dccb97cb60425fb9648033bd9c3520426d71332bf8d2f8443801411186a465699b651a9315f9bd6d6a6865332d50fad2110115d4f58482c564a24b2b0d5dbcf2ea0aa6c9d38b6acd4f6d2364243569dd40dae211e7276326c411e5178be255fe409d91d791dcd4d31a108c144596476a6c865e9a137c239d5acb3cc9e94e0b4993274a7a177114f52aeb68aba5341492113143c0b6aa70289d5ef8e051a3c64d9712146e951ae7f84ad8d40dfda8f1b4bd608ae959a3a90a8bbac0ab98b60aa0548e0c289abcd139c26b5a31939303821a0d0e4e1fdf5ad109d589eaed2be5f2ca2c870820377553703d6b56454abec2eed6ac65bebd508a334b26e96501c0086aeb4d085755727996f2189b73ce7711e79c73ce39e79cb766102050c6799ee79a9e49a094baa51c1a8bd179e1e3832a052c298b06168d97cd0406f61231c4c8da8811468458e044589b5f1d94ddde8bdd12a44429c5d394b49684b796e30396a2ef6f2f65d2a9172c1b5b6b55543b4a3b28e004b30c72639b81d2c1b9e4805205c083c95a7146a74411821d5b457d42e6864c2ced5817e69f3af85681a2288aa2289a9aca269f246240404a00e1a5db8bb322692d84c8328a4e136a69b9830d733acff35c532d209d1d384841c2f48062769079fdd8aac132697172218dc3c8140db023496292884c69657e6e617676613cd60cc484a2289a9a289ad7d1162b3b754f636633363f2fc8316327076d091157570c61d412fa69cc7eb94c9698f46ec0901d5146c696cee45e80d676ec25594a3af180a95de02c9c90553dba96048d80ea3be6789500437183e96bb96372a8a5bc64e32b4e140889c31194d667c3e6a54a064d792724f7da7cf10993049306a844ac580f4aa70cc208000002a3170000200c0a88e33810e4300a876bed14000954c23e524828104b45a1288c821804422006a1000001100080108661100a252a521d06decd6c3ff9791fa6a9bba3b2a478ffd7726006fa3093a99d30b318691373455ad3625567aa0d2038bbb60a56848bcff57c91574fa9394b08e1a206bbd14d392a8dc5e0cf2a5c454da2bbb609d39e91f61ee11450cac83449490daced7e7b5ff3da60132a1f73f722ca3748aa9a951a7b94e2263045b0ac49eaf03ac802067c5555d88dca2818c1ee36ae1a5539d82154c6a4009fb19e5972eff18238266b93b96dca9cd05dc90ec458016a1639fb89eb11e10b249e2e62cab6d16217144ce41eb9da0a21e7b5f9003eb18214062976726bdeb475969730025e8c40615a897852473452f5167b817d6efed22717a3d0000b744e7e34271c61a5267d655d9878ed86e897ef50c4e2f9143593c8a113c3f821652e2b9305e6cd21e5da9c5e5f8b9a74a80b65582c857549765c1e69f7ecea682fd464cd71b2f17b8d51cf33db4b7941df2401b1b869848ba894aa0a39317afe0040ff3ad331a4a3b69725a88b99876f7eb8076615318bef1fc51df027db6e9aab488394ce89bd6e3b8c48cef1bb3a4acf131e4e40c663bee191fc95d44c94c3041f5e4d35f803f6e620b6d0bfc9ff3ecf7437421c505435f9e06a95ba6459de685d23e2baa41e4f5ba314922d9e43868eb022571abf08002b8949a8be8c76bd630d53a74fa5cc99fe249db95f9dcb70a0b1e4a4ba6bf65595a4ee7f8c08dd085dc13ca16de8e809adfeefc376717fe8382af313d765aed0b3cacb2797e41fad15ca04ec44c4d23b3b5cd2573763876215ddf97a3a2137ce7973c16b331d9ae85f6b4d65329b183ea34c4703c737d6ff55b39fa4eb13fc942f75f565ea0e6e215ac3f7d642c0137fde5b6d4d7648012293634a12984cfdca684ff2adf0e9959605d6496f4eae4fea6f7f0b93ee5cd72a0ad314812287b27362d90dba29f3740be0ea4afeab38579fbd828bf24d939ea455b23a8a83dec8427263233342cca6a51b0e8e1d2be97bc9cf5c52bacd662a62176dfad09df53b3aa923e844e08d9dc05618952514a7ca0eece5fd32aa72b9307a17a5bad71e5aca36dde6cd2c4f350d4328dd0909a5895e7c51b23f86295a825d23b601c49f52a1e1911ba74081524cacdbded277f8c303b6e50c16693dfe65e462a3bfc6bfd74d3758c2899cd52a59d7a2a584b8b02542c9f2309bc3674dd900a430d1412ca4adc521814deb69194459eb29b5c899d14a3c6168f0c11822332ed5fd777fe07ea84cf7ef1e100c27c46d659a041e943853ee918a861188f0b64e464f6a4e5809f2b12250ccc4a4a21ba55f7a3b21142507d2fde961b41e0a95046978dd832acad1bc88c412f8d9cac41b8f420712f8376efcf24851955f22c36809409b4a6bc6ab4a6222c2ff5d3abb178e92ad74c00210c6b4d5ed063c4544e8e5fef147b9ff4745bdf83fdd16d24daed101e86e9714070fbbf1299ebdbc3837c417d86950628bc62a28396fe16766e0a519b4f6b1433b10f604d896a04183c8125428b0b3b92c41463383363490b29e1bad3f1b6dad102752824eb6594e36e4c932541f5c3606d340290a206382774966363d0fbd12e3af1146d1acab4958e5cd490c63217c33d1eb4abf3b127d86e45b14488db1e10423c59e9075a0ae0eab622345528124836d6f040746555ccd0604a55ee6884ac22fc93bd91052c61057329b4626ae8cfef39d30a4e437c41d02f2bb7e94dd51cf144a742952004ce7e49a9b568222859127c579bc7e8a4882cabcfb92542eff91c6296740f0d833945c9763c0184c7eb7e75712144b8ec02e48a9d70b901183295c9229d49f804fc8b4bfc5819b978cd5b6d40e6f93eb9890ac31f896b779f43884a3498f93f9042fef5c7cda922558aa8bc7a80c28c7b282ae793b95645ef2f3cb1b2cdf7a7966c0bf63ac9bc1913446d015a133e1834433345cfd0933212e897c4d31ca42a0c5e44fc0191ed67a165698d51071e40dff3228ce27ee0173d2c67a2751ec3f2cb457a00d0139e54b12435f1f4532d133abca7adc668b6ce3b79ad1dbaddc2a2bc9f0938cdf914a5ca71a4012c34f08c1b28739027c493d697ba776c7813905240160b5fa62acd9c49eaca6bd95596042482d49a11bbaf80fce12258a933095835ee57a91a46c45566c884389999df2eb7332da8291a6cf0a8727194757d9f44e4d64e11f914771009d13c614e1f424e0b874eea58a88b4813d2f7e6fe195dbe3d60889116c8218460101f010a4945f1f1989225da55c3192fec2477dc6300ee972490f85789ce595cc70517738dff0d2e783bcd052ec222699a8c9a34c65066fc3825826599ae8e4c1fc49e533cb966435171eb30202ea4bb0aa0bc7a800a8c7b082ae78bb2bc958f23765280cefa93262fc6adb4ea6e1b76aa55cdce42ce623faecbdd40e033958a6652457aa9ac6d5854671193edd6a82b703c974360c087ba68a050a82478264fada719292cf8ec285cf92b35c41558cdc953056fcecee12c3bc4d468eb52e0e30b09712aa5d6a0f017d128e4c38a4054be442981a688e6748f0bbc3fc072847d66e5390840c02466ad58568369a5cf6e10ff7a81ba8babcfc4a3568daa4c6cdf19325a36e5e38560c6fc38492c1f2bbd4e303234e276da0f1ba7b894ac823e233b4719b0e04e81fb422391bfc8e7d0621aaf2990354cdea48d016ce4c156928125369c843b4b9f018a9997009f4edfc3d18f83f9c0a16ed25ece092d913fe767c982be1ff1b5b0e5f0337cc4b151c4e81a2f10bceb86faec9e57ff811a084b5318cc09ae27384e00340641432abcb72ff895f402e7a9e59dd0bbda4d728277b0f81174e97f2816190c59d5ed341eae031ffaaae377c60f8f2c1fdaf28c2ba53cf06b8303e2091dd1fee26d0af8868ade8c23d876d752cdd7ce8d8b176ec5fe62449b64751426505b97d2faaae293e85f2037f08c4981e02b95ee2a902714c6c187929275432a36525f68bdf054aa38c8ba7f83239778a4bc4c80f69190e08baf89d4a9fd101ffe9c1b3d655b2cd2056901280268b410378cb9a164e923eb320d0403e724afeb0554a3fd868f68ec64498269f4ea81e86af014b20016653a5a57c8a4b500d85f71ec88ac686e6de3e265427e1fb9cd5aca43447bf0773fb132c35213b0d3e94b88aeb9106e422f6e2b3cd0c546f331b456ab668436281d96c91e9e0c4cd0faae9e4d67f90786fe24e14f0700f0f729adc98006ee8b5c86be636f11dd2537674ff4d0a2760e3cac2fb0b92039fc54f33320f8e045f570c8f4b0e964e8a0a21251848ab9c897aa65a1dd57a16ebf295f3279224835ef91ccfdd54f64a0f5b678d39e3890e3612fdfbfb414e73a75d140a9cf858d872c7281cc9af21a10255e6c959ab719eb595131a2f78f8f2fd2eed28a1179096fa1b4d4320099377adfa649ded02498cdc4491186731a5af906069b1987e844200c980cc9364072d5e7b5ccb01132817ba6a5251e40813c948a9b38a6ee5da5f60598d5f0226a890e493c2cacafbe266f81834ccc85007a75750d6425a861d3c1f2dc21e3059de0735663e424e6157e519231e6b28ff10b0853e48bfd1dff9a59622490474b72bb48b91278cd4df565a9a11f79dad4ad9904064d54a735c3560669394fbdcfebe846640ce24eb9eb85e288d909863fe025d4ec042196dd2dd3ef872d06717657e75120af6fcc6c0b964f33a494e3b862f675929a058af1418e6237b28041b1843e19f719239fcfb0d278057adee136aae89f5649d3ca4c71cbabf450e47765d0b9140b5902b330498c3c083d260742fc3c3ff2c8679309092344dc2165a7d4ea1e48673083cbb731eb2158a66e5cf5e52029902b82524d6eb4ee021a16c0c704e3b92c7bcb0820e9a7392b05c85cda1afa0206220729c2dd9ffbbcce660b03cf0e284e8a9a4ebcce514832e212e49e448c2c892e8afc80cc0a08cd84b90d084311e41f7180189ca659ca47d153776ab343c735439cfb6c65cfe17d2404e4672a34a6e076690930b76bee4702eedc06cfa1b1aba10b2a6040b656d80cf229481d31224a4ccbc7ff47790258213e61e41a9cec74f78606fb34a637d009fae71dec156c45620d42ed0f4c2719f9a51da63aadfb8302895de36edd7638d921be6d4082fa3d3d00ab0470a7b9e89535da773843679bf981e40beedc6071159b4bc3322fc5798003d35a8b32aeb2d3c696a4f05a3b136fb553e58f248dbefcdefbab27f13391f5d63e9e2c102f4aa3098037cdc0158529e147a04e312c774fc42d97e7a6596a8e881eb1231875e44f2f8e4064ab9b53a77645a2b08143401a9632233c1bee71fab1585373eda48e316dc2f77d5ffa2ebee67fce7f71bc34a19fc5231f90dfbe42e28859dabdc8c4f2d67e0af8fd5ec743b645996f4c6b73ca187b352bcb1dfcc7a58d9f768d058cda7ac82bc4e978eb0d66ea901a43dfcb1b09432ac9787306ca2872ce3b2db7f38837dfe3b509fe5cab51cf71fecf523518b389ff4e0f62025297653c69b92c2f90596be8285601ff8f3379d847ee179e62a0cef7cab723497482ea97c8c883d6c42554ed530dae30ea907628f4779b2f1f9dce040cde18d6874c737324fcf50bf451846059cf795eaa7175c94749a0a36053adc1d6b4ced44505f021f6878461731e6f9886b03f3117fbfa1bada380bb5750d9d2ac46e549f0a87818ba58849a5c02796bd1ee3e10548d901b1433ddd4481e0b08fdcad21a4a2fdab2749af4c9b2ee6f4487dc53c1237b306037c49a85f3f10616daa6b5cd3b0293d82f886aa7a8e58ae7802337fafa096835118f2492808e4378204abf8919ac8a22438c9bd81ebb68889531e54133f591f6677a434a7e9f27c2a92c98fa0215018ca7d84d06865bd555c18e586c777a04c67fbfb420ee111e70cfee2fb6c6d84c955419e5295b9c7a4a4c77d2b8cfc5f3f9f37b19f2560863eca0d05ff5838454c8e79f40107b60e3ace6159fc3dc3e7f0bae7ccf0177ebd6b08a08647f0421806df210c10ec28953485210c3f8ac0cbd98a379e5ca9d65aad072117b5c100b87629bdd6830aa510862ffa663322b4f60182e13ed6df86cbd10887c62e51a314c220619241e9516587daa7c4ee4960135062dae919bcf2b07017bbbba63e21f3a7eeda3529fd36fed3e6006cf5a9673033cd4372aaaafa0a91c28e9117fe5031c8b8511918e25224f64d37bcc1cb8e6d58476a0195c2a0c20ce55ea1b83168aad4839b7b9eb80a8a124848525a53dc69947214dbd9c3d8181823a1b051288f0a5bc031fc6cf72320da02765383d79690ef1b4741f419de4a63ba638f5c3467aac665caba5018c15db93aa62a4afaf5dd2ad8ea14072aac801d105f41f3c981e7f79505d254d582a7d3cd7b3f83197be696afb1471d842f5bdc32eaaafbe6fc7cc3e660ea6c4cebcf63b80101d0704df09d39eb8332795a1daed33b6c045017a74648bbe17bdc2b4c0a5250957dbbffbac451315d8cbf8c390d0bfb85309212c4d09e16591070a29606298c94cc406b53072390771e8777c54d5e6135e445782f873f9b300552c852197d8ac883ed32180f96f2801e60d79ea82b43b7ab2e1d94ac069757aa13fb357575e9c07dc4308191c9f90f68b4244de236e9791ddb0d0caddade33ff479b10e52aa2ea6d16428b2f011d464c109b2d4298fe36473b128b07c29810018b03dbbb12b97dd9f003fad5c6c4cd6b1f1088b1223a4159ad9367c7bb82944ad304322600a6188baea373f4a3ba60c014634c8a26aeed17803b12c12f8fdf460cdc35fa61274095ead46a1e07546bd800a34ad7a6cf033c4b512acf52116080c0a6c614c07ab8ec43100e59479eaf2ed88800c115bce95f17166d76b4c3c12245654378227154ee01fc1ef59b916fb4600ae60519a466a4a8f44c0f4615831a5001475f8554bbdd14b5b27786da434b274bbaacf62ea605350059e25dfc480bc51d4492d615f8f49d7581ff1fcfab9d5883baf70b1aa6eb8cced293688df06359982fddb76ab37c04b6064fade9d25bf010c9566110aa563b7aa4ad714fd3d86e9bc22e05c1099d563f3ad2b5d16e7108d067b961b936082b28e83f6b00b3ed978af2ec70b8f2c2f0ccf9db1576398c120518f4182ce2eec0f6d2e6017f10883c2da30952d1bc2f2a39b77bd0fa2c4d5aeec475b6456f9ef9e67e1c2d0297e7d5f66678091857ddd3b22d3698f6fad114fd414a80626b4463cdb54f305f0fda58a2f60d199ffb29404acf68efece3e52d2426a5ae19399e1072982b91c2164853dd3b478061717ae708bdf050f84cba4d0c808f48d547cae6f244a02a8c2a6d0520f151cd807d51b39d6d75e32d660850d466c6100f13f4164d7f80883ea003a04b42dad1f7248b7e0114f3fcea36031804a511112a62e52b04b89bc03083e343268fd4d05b61db6f06c4ae488f66ce60a3ac3f19bc6dba15f22bb4879bd1102b44043b8c70b4afe49e4741495f79299a95a2d1d44afd433c990e523f2dfb85de92125d9f451054f886149a06c8a6d529d87908151c1fdf9d9193c12f573a63cdb1124ab3b0c3cf1e8a502ebc31833e64c7623fa47ede3cfacf8480f01480c3088360a5b6a84158862478fdc35508010259eb978a63c0df0fa5e4e023ec86e5865bb62b5fbdc7abdd09ae43d77f5a098bc274a9823ef774ad27b9038dbe0240287cf481fae265471f761f834d8d9a3bb5ef1b4c2c10bb2fc19f5cbc1751901da151512758a738a8b72032963cf202d49240a72e5ccdbc059bba7a3791ce5360e6ec477b7435a823e6881522cba58b4d70cd7dc9b11762220c4c2249ae23a6a49721d5be6b96166d66420942b1337664446ebc5ab3509e66b17d137da72daf29978c9a290707ed79d61a9e76160cf4b406c17924d0eb6bbf6c8662a045ddfd5ab06c90e9a520e24b06aba565d986c02aebf67e3c77c5a80d845f386d9267e4b9497671f529237434e3a85f45f9ace27edb51e9b8edb51de0a3ea401d6558186a2f6ee73c3c1dc33f1691f2c648a6b7a3b600aa31540a72adea9f05fb9f8bd506effd5538c57202fc4bb276fdd263f272a1cd9dedfe0870a0dfc836511aee2d74839a71d752b428a83faa22c1d5b858dc9499ee084008f12aef4d9d30f89eaf8b78a5976d58b3fb84347133ea3da9eb2ac0ab46cc4f475ff9c52e0cc556be15a70d0689023d50b0716f21e85821b2168a8fd064ef079dc82eeed535714ec8929620ce92619a5c8681ff0c14cda98691585475da29a15a38ce1ea60d663a028d0c45c0cf0758e00d0ac1190ee278dc5c0083b901917569befa5c07132d7bb1d556e82a47d43b9fc46b30d8602f3b6c4505d1e1e1a412a5cb481061fdd38cd26582c386e1df9754808c03546b41e0640503d1533f75ad1e74b943a5f3f351f9cf8f46e9c343036a6a0a33d2e589fc035f0316853eb8a1dbb1173ae4b6ddfd3d82574e7f7dc3eeed2e4f03ac60de0b14ffec6eebc9197a79411002b91de88dcbc7a426aa6748cb4f2e14830229ba5bd4a505e0b7d2b0ad1fecb2d7d7057cd49ef167baca5d9e1baaeb419cc6891dc96f63d17ca4d7bbbf577f523116ca4e631cb6ee099fed5779996c6f67bb05574c3b1184010e4232e22ca99dd16c9d02fff15975078c3831b602b3de0dca623cf94e6284bbb79210e4d4a772ac2cfd98cfbe34e483efac0e061064d3878800b71ede0f5c2b98850d3e210ac1e7781fa92370c684019f8ce48cd59fdc980536d96d8c6e0e1b71a3eed8132c90cdb931b604c99193cf659a3f41901690478893cea18b2c2aa4818e91449030f3bed48ccf3f121d16a3d5039b1d20de89abcf3adf7e7e3080e83e4670e3edfbe9410d1dc1298358cb6ebb79cfbba3266284aa80cc283bd63bb3f276f5562b03f13f013922da47f4bb8e77a8aa9edead140930b32d8302d876cf0901916cf87dc5f903cd549253d2b35c4e1fdc4a4321e78ff214317b2cea3b1ee149aa23250487811e63d043a2127cb4b1a40055056282ef2a45481d0500df65b6c327f560fbf82ab0a339096380ae393f6d7addd1d2274214be33148084d340f892fd84af3d75799a54ebcc506a1944a42bc7f93453cf12b31ea912b8c754b0847c4b90b5869859805436853498d5a1f635a74e7234be9874848d305861804c84be1b7518fcc0683733611ef08d24ef465005ed38675fa1b1fb672fe96a7e52ba9e4366c4f20168017655afe9693f85c6a08e1c1553e21dfbab9864e449511c56292a6657e7ea388bcd615e97103dfe02ce0e33121634e1c4e6b496180df48a4e2a70bf96a272323ae58e771d6b97546dbcd6aa7e5cced415cb661d7d9469a186aa16dd5a5fb310020cdb079e6afb8e6dd4ce05a13e7c3282b8b9986fa10a8cc74ae7375c94f553a9680172de8308a4c13dd1e39c901de59a502985d10825471aa06182ec743a0decc03fc06ccab448d7f6f52d603fbfc0f15853d1ab42dcf8a227d51d986acf6f7cc10324cac1c3314b9088794b80b73fd91eb2828e46e195844e4c89d1107b895adc06f4ef9f620758224fb6ba30dba71660e8cb68347fd2a614ba1129b0e8b5d6314f371ae82c827b23dd697bbf7d8eda2296b050b453578625928f187ad34bad1afd68ab38e5ccdcc4f3d3708b458dbfa1f1b4e945920bb60833666c08beda7d1ce98ced4cd47dc91e0e0d9a04fcf2a51968078b5352292f16c081cc82b7bd90efc63899ee3afe35c534f9eaf78b1f0b3e1a36153ec493470fb53575da7b559059dd3a5e4dae1ec0b5c8de7922981e2c6cfe2626c664da2307013804c37c153f109c199b147df6b78382c014673e1fdf178fa4e7554294b4530223bdb70b413534c81c5470b6389be1efdaa1f8ef0bc210f84941f9026a71b5b42e026804b21ab81f39351a91c9d10ed4fba95eaa82b27b91c7e458c2979fc26e5b92bcda7a9c9611a0d2c4b69b45eeb366b8ebe27de233b4d90de0de29e83e803b6b9c6bbcd0172a8b00d7e9e07e063c4602ba9c995b90ebc09c86c7ab9d3bff16adf12ac2431928730675c6d0bd16bbc4ed4e51485fb101a5861519e591d2b322b64b2f2749744e94c27062f1f647946212bc86af6b95910e9fc89dc696207014eb496f01c287388825c8d2ffa189258a1990551f1948f77a6024e685c06ec1ed37aa75a3259a8a4880e0579f2aae4c044325838bb598516c280e50682c4c43bfa9633f5a007e1e746bb610547bdc5d69d23ce0c71510ee3a5c6c7e0ac684977cbd83b8955287db7da12f1499df3143d8548579e0f6c7cf44e4f6e4c4cb5f7f56f02299d29016f404cfc18e9423bf9bf641831556c7882bc6bd04cc57d7480ca2be045a70222094b674eae83983cf76415708f299e3ac0ac786e6401ae34d4fb13689ed68dced9059aca56e5c361ce9ffc2dc2880fa02282832a74e8b319162465e985751b0c2513d77cf8b97a0b40f8310da92581982a37af52b626951c29376f85b6b2414f2b152500b9c1704a5610833ab5fe85bda6815bfacf247b0a0b1d926b015addba8e2aece83e05d4e057409e7be38a736269d9f14c133e5eee2dee92116b6609ad4a36b753443cc403bdba6549fdb8e8bf045cbd3838f2c00153f161b3712e6f7c6a81df88a4b6462365680daff0242022c3b5d11ff3285040f66e5372f17725041f6fce1b29ebccdc60c3dd986fd32ae34d6f85e3c9aaf4cf83f866a5252f740537f317e7d5ea4ed5daf5c00b0cf1ad555a728466fe6280db3e5e53548beeb15fa395ebb4d4d075d22d488d91d0e1077743d27122922595763edd1c1bcf8c52706835b91a3a63d10ce21cd2e3e8b217fdea281590c5ed80fd0c1b0045e5e98b074cdbdcea276bacf82eb25e4f34230c56dd2ad7e7ef91940406c772b188a9efd549f5d2d16e8891f81033245931940c25617fdeed82c65a09b61fd327b84415b805a9946e68a83539c9c52840e503ca2c20fe4f6c8b46200118917cd15ac604b6c21771de0a2530cdf62213d124b5e397373f32b6611dd6a6967662d460ee20e50b2f221df4e04d53240510c53b5e296d8e4012bfdca7c3d68b24f374157c52c154eaad0ea408bd68aad00ac3944d1d3e9ad631a0c1dc78d12aad6b4fe73b561a7af226b120a3ea1faecb38bc6820922d57c8f6b35eef4fe342b702a9c20c19dc9c57ce4b8706c05d35b775c0bb6c0a9a0269802b44616b562eb037dac5a76813d7a79237979f344ba3a65bd81cad07c9b4c74195597689be6fb85984279da42e04d77aea927153010b4721fc1944e25486178020810a556dcf554c84beaacde7c4b218cac5d9f9380ed2aa39380090852eeb38bda85dc3d3029b96ed61270d66a6b08d80341efba7b366ad1f5c3aab7daaa076fc119fbe9064c0ae73e8601811ab7de82468ad78d11fae33b0daa935a0d19b3412a62760851c7bcbd5ce6b08d082e1a00b222389e81765d0c2ddd4c961e534cab9f198a69055503c4339623d83acbef86c123b50d873676595ae4f03e3cb5d57833eadb30e4586a999701475cb858d38c91fb6818d2ffa1927b82f2376a82ff25ef987f3d8a96413d650cf2606115aa7a874af433be0cecc51bea99cbef81b462d24db028a7ade516203c9a568022d0e28706ea24a9771b27674ba2efd566881648722e214b28e79423e4611a9f02708826331a2aa6124985585a8af28756e9d8e723d32920ed69ca0ef511fe32f3d75a43cb223ba72475d097d9877e6f3691f31f0b8c8fbdb8863005fa708052080a6515fa814737cfd18415f2d9939caaf00893d193fe47b22e21669865e7dce092180d9028e0019e5e024ad7ac77941fec01662bd48b3d5eb4093917464dd9f5847f7c53b2a31d83b2214f416b7ae459016a0cc35129a0fb7a7a863d47145156d04c4bfe23845052f7af54f3b1578221849bc62dd420fbe99e47b9ebf97dcd129affc7ffa9156cf08ae8a57113959acd1e20e874a915b26e1bd900356d0515f5cdf0f133cbdfd194561864ceb7125aa12d8dadb0585a010c4635764198afc98da252a185b486fdfde5cb05bddf470d0b3aae0f80de4d8104c0b0888c3ad2374033a852f6fa7279990f0ab4b54a3970a678628ddf98ce02d91160adcc4278da1bf8afb656b6843e4a70dd850d909c1f5a0ebba989ea32c5c658cc5e627a31b550d4470a8cc3659af3b108983e16744c2a96ef8aec87aa7f852da6a5958558113cb81ea80aeb5de0a8c6d24ffd341e592c39cc3b7a117ac7326dab52b2e44aa64eca64d9786a6489890f717c62f750ac4ab2c3baa1d5eb7e4c2015a66baa59c77eb22ce8eb0026d4666d8dfdc474cda70b022b2cc8a366ceddfebe4cbafc4329fb893d2f073ed53641536c6f9d84f2f5d646d87212b18cd77fa211311ba78a112ef8e1a12708f39a2c2b191f83ed6683501c9fda597d9825bac133888e5783fdf7462c23e57379d7cba597b914d351f065ebd41d8e4019504094606efa2cf36996f4301a5817d79413f9dced5153f4092b651775440a84bb8b5dc88a2a92e28c38506ec493f7d9ef6237d03680c29f434cf6e64e5619102216300708aba5ed49de04320028fc4e8720136313667aaa539144b5b016a68535d2dd145090ece8703da4efba40f7ef839ed36f74193da6032d847d1cb79bfa5a4c1612c5207589edd03178669e6257ce0800df96048fd192cbf4113d4c03ebe21a58bb9f333ffbd858bb17001861389b778f70df16c0000cc171bb66137109fbb161c1b36ca346740467fe193eb35771432a502d56092569b3cebac99a1b7bda2c0685c6efdbe76cf5f44d6cc45ee4084ba9b26fdd888c5741a4e4da413d0ca0e4d47ad5a2304a3de56a2a0893e27a86df463c2b838c944884fc2cc6d44cacd19d110ff91866e3dad932cfeb714016c76119c86ef92f17e75b3c0c07e5193ecaa18eb765b66dd4105dd2a65885b6b4397685bd7250aac6e2ba40c339d1afaa17a613412246158cf4b0c79a4037b1364c07eb958bfbd887f932ceb0fc80e045c0a00ccae1accf81b91e0f0479ed7213970924ba692e60218b4364073235803e72164eaf0352fda09b2d03dbbc10bf5256287dce189511378f5e183bd4b7dd733d2a9f38eb2d0408d98709b896060f9f5348f5c497b447769c4f129ca95a2b1c5e6556ddec87e7247b44c61f5dfc4c48d1269eb402885766afe0da0f1ebfdb54bd5c1e0532b31403ebc8c24ffe2ae385d075cb0b574e392be7dccb9ba8ca0062fe371148f6f226430af9d79a92acf7a7c1d3d6daa4ef097b5210e0ac4ae11afe44d6edf204e3e7e0cba31b9a2df3ca501a4be3d004c9e67f9680094da1f7db79b9286c257ddeaa604c241b4e8712cc8ca28939b5f167fdda79b9df02d811c54592ba87382f1d3a8a45cc81cf6c4ec41cf3426cf916e88864ed2bf11e70216f6082bd4007ef713ac0ffe31b010aa4e94a5de06e69b3889bb214b14582cfac6744af291a12de9acaac1e07a2aaabc79cd0593137c7fa393d61e476e24099d2e561c1fe8ef19847d7a2cc49aa1098dbd164e39e44188221acbb9defbcb83fbe074d7008a23f215aa7a1e052ca81e3347356d4347c887e3adf37986be6669d29e08f9e58a81fb6fa45c037f1ad428bd549348738a20752061a2fe7ba0af0b5e54121063fb7e5298910e693f8a1a5c7449ab7bf6994620787438109a6db6b657264faf1bfa4d03405811b367e18c7ad50c5fcbc1d8ae79ae6e00cd4f47a4969ed92b96267bb6b7b2d158a9066217befb4c622c0a4a2ffddc2e40a8adcd085a59b1518f30c011b7986ea18629dc78f2efa92465a83f40b2c5d8aa8e299e455c55d81af78779d82ab0fbd5dd7b6ea2d648b2d6431c454513a26db370b291ecb0a52f208769411ab70e67ce1c245d1e066a0461db3f0957fdfc31bd083e5a58c190921683ba80ac0f0e634fee4a06e0554ac495af5be633fe9278d9ff188d2869bf1e18dd33b29a5e41011b08ae21b3710ffbb1fc24c604837ca89d557f0884b524bfa093bb14f2ce95b2ac0f977962d8c550ffd3e1b943ca477ab9622bf2f42d55d855439cb1d911c3dea6184d064ae174e0d44451e462b36516dd753eef62bc4ed1038cbd55a753a6a214f6b69efdab8a1bbbdcd726ec0f43a6ef98e189520ffac0bd22335a0fc73a08aebbced8b065f2a21922602f7d93bd909800dcb49510b911e1ba9ec4683acbf107f0211247d4fc703fe3dec434cc017c14d81fd083a9e27bcc8b28f6bda40502674d0153d8e42661926e80a791c0fd921ee02b0d1cb5baca27daef83003f879321d07a88629fb63261b9d327fd80e0797dd5f829a8899a7acdc9a2fb632ee50d68e61097fb967f10f79ae3f48005303c4b053593ffd74ddea5370475997660df1d99b9713b48e96e49858c8b0c8ba5dbd8f3d3f5ff545c26c5fbc4d9416467aa72a4f970b77434a54fba472ea722dc6647aa6b59f169812e8041ddd9cc44462813268541fb91114fd3a47de6a65a488528fb262c0a8a0232d012103ec7bb96f70e8f69f16f67f8a81a11606769df53d3552b73cb7cc81c7ea5586ecff45c91603d94b0029d44152630952eaf2ecfc77310221d9925f987061018cbc4148154b90c58f936fe66f0a9f44c77c50b20ee66262d3aea8fec277ab51c3fc967153f89aa425202a43887f24d54d9fe9455fb6a9e98fecc4438b782f75d795a41ea998243d842c1d3c4d902e5abf5f64bfa4d335b7232be9a20411a5bceed109b549eb46fe91b0358013be0d7e8ad0cde564da3b2bc3c9794de68d41c2dfcca8c340b7087f8b45f14de888b9cd0da3cd0b59907b312256576c0688fd4d9da9863b1c389c89c7a7674f5c8ec2c06a81be44bf1a898c2785b57860f12661d23ccf20549cefca2aeba1faf7dd09217d1945157ea7bd36313130008941002fb2934c63fc68858209499e2a31e936509de9fdd5d5e374b2700a7b0e5d680b7dc25c1d981002098e783a3501cdddf2979fd7dceee3a962083c2f585197efe876898c4e23728cf545bc0e6d1678a65bb78e2018b0db7cb8cddba3fe52ea860b6c88c745fa698435e80aa8a8e6e0c7bbbe7a99ab0ab6d0e4f19040ce27094df4ad14a996f64271dd2b5dc57e6a171acacee74e52be5b6f1cf7fb1dc5b86894eac67eb82b4beb199b29691bf91c496a244b29be69520fa8c1966ff28610f543014ca244b57e6402663be079942bf83b767e4fb44afc00db2a9dbe62422e1320560d8bedf32a9c6aa6a1131879db25be37e40c22d1f0f0e75549e1bd21409fc8a612ff6a4fd7e4997b55c1f56e8779a874385d8b004be8288665a7dee09124c9a173340528ee73e29fb1b43f51f143db5de5e86869fba89e29c27129dbe757c1aef4a0b4aea2499664348dd455cc6f96876d0bb56e1f520ac4517c689db730059003167b47444485f4aa9c516eddbea31d6e0daa1c109086d92e160067f9089b485fcd882e8668627a8f0f376303b8a9f68630a003df4742bb353c98d880371b3d46120822e7e0ba576cd40d041d1ab572ea601e08e392d135da3a82355eafeea1f3ff31d3c1dafea82a93a315cc823e782b4cf6e8425dc3a99d77e8f02951d80f9be8d689a296303f5a56febc21feec93518e34d0dc353d6c7df01a6e00e19d026b896a96ee1e320f9260705fd4ceb1270743ef38cd9ab639644483c62acce4e0eee07b1d45d2d31425d43a4ed34b93ac52b689062d01b403eebdd3a090a070bc77e0d6217a1deaa7d3148a7a87be1c683aac3a585d07abc7d39458c892a1e1c8462d0b11a68e42ba69fa25235eb4b2652838ca8ed388132bdbc1a23a9aa924ea68d5412f87d81dee737077e8ef4083c38a83de35a4af67428edfa91d13df749f5745430ef438ace300ba0e92b1b42816a991d3641e19d9a0b51568ba5173cef6cb2371b4582023e568b9a28923d0a1e628efaabce66c7b8e0966c40186314b395683a6d9c9489396104446403482a40c8e4adf71fd1e1bb5cabe6448ca6a7071a48c864b72a363249db640ca0a6683de9d4d29fba0a570322248232859d661b68e7652103aaac53547c3a9423cbba37f743465b93e877e07a2d5aea466b965998866a1c30923e448b202475743eaf233708c91458c6b9510751d7a72601a8721d734cd2d245e6b96b6b3aa285563d25464291b4c239620cb41a48e96a685221368cab2be43bf03bdc36a0d5790c638672c346878700dba77ee5a2e244c1a54924174c8efa2490819014e2396321d741c30e6104358ac9bfe4e9ef14252c11922641d48af438d62e33f5634f4dd027a071a6a5e400ec29a0e8af1514c28cec8ce63f38feadafc106de6cb5b99a17d8dc8265b61ffc8691ede2c47ff8eb4756e66846579686f7b197373ff89a6139da89746805cddd41d204c707c55605d43ec356e58da6432d242dade7b6fb9a5942949196a063f06d1062d248ffbd427bdd32fe522e1a5dcf54b837932c2e8598b615a74aa620bbab2a8330d4b273b6708963184725011a3551192b1a04a0b396856e191e7b74c0559936c69cad314a7294d53987a8a6c8a914f299a02a35388a60cd529415370d8249cb4bea1cb0942454c9647b21c79beeca09b73869e23b11b154c919a70124d50601ff0122c252907c6ae47657994c40e7914b5d88d070894c8110394254a2ee0c2d3c3112f40aaa10a275f40d09eb8a01dfd782c9c182d32ebe5a1594db3374b2c3c51e221868820e418211c7010111292c4488e4e33fac1f183e3fe782c1c293e64f608be4f3730a1657924450794a72860a2308181e9c245142633c68f162589a669f4c763e144b1f2b2a903ee88e011d3cd488b0cb73d2ae2894c878517407894b117300c05bff88147fc62086fe1bd3d40b8f75e4d6707c6ae9f591e4101936fb23c8ad203ca531e45fa52ce49fbdea6d23f1b94b664b43aa5c2c8a3547c4617547d46eea35a7daa1955cde8ff8faad936dbc2b448e7db46d2921a29cfbf73faa55be56c5739db69defd34ef7e1b8a4b6d282ed5614fd5614ff5ad5032724e3aa59cab2933e7b3e8fc194ae7cf396773ced89c47e6d0143293104da129e79473ca69829c724e39a79cf435bb9dd6ea3dab6ca7b556ab7956bb57bbdbc6755de7791f2a95f29e29540a7ba9af0656ad54ab95f75cd17c2f1957cdf79ab1b969dddc78cf1bd55763ae3c09ca7caf99658b352438d39a69b5bc67ab86feb4763a6a3c01fca4f44e7f46f3339a1f8d4f8a916ab602ea9eb1aa6661542a6a543f1a94babbce0fec4e93c50ef3d900330d37a79647456e95d367a49af94e8b6854cdf24cf274ec49fb34920ed7f1f414260c18305fbecc9f51b01f40c396b9506b4680295934264b6fed41dde59d281f72ffcd5c9016a28a244f4a28282594129a2d3d5fb07ee99dae4c95a97ee92179fab9a0a1ee196b8fdc41dd3339214e48f78cb3471899599092aee2a8b2a48d8a8a8333ea268bcc19b90fcac977fa3b8843cd646ad8a8c8ad248f2827d44c8648f2f4a3661e24796a2b735238a31639a3165b0a6a6694fb5340dd330b8372423dcdf72b33a3a17b491bfef55ffef56fe439bbddf35ef4ab0dda547a4f8a1a6fd4af2ef76dabdfd5aeb5d63ae545f5bd365ef5032d727defce72366abc91ebf7baf47b5150f2d4ef656f22a0bdfd1d9bfd5ef546d6be1795f26e1cc775dbc6f9085af8f3e33b1b573b97ab2341b3d5dbb65a3f8539c66df4d7ae524a6bd31adeed7b49d9715f37d869d3c3d8fb46d0de7a1f095a685f32d7ffbe1064ae4e29a52fda7d36ea7f35bace03af2ed32ed7308539668ec9f495bbfbd5d872f7c9fbd550854cc3c63eba2e25bb6eac61f398174e580bbbfb734ce67ea449524a29e55694bb7fce39e7eca6dcfddddd7d879adc9d6a4694d2ea54ab1dad92dc2fc6e0fb9ba77d2268d9fbee6a7fb3a63de8fdcd5efdfe5ede77ef9f0dafdf59685c50ee9a943b1c654e0a812ca350613eb79b1b14214a7f7ec0b4eb7460a84f654c20b8983f47c42de186382515491d2277fab925b2a7c620c52ad422924a4335413d528dd42295481daa43b821f7e16075862a43155263a830d42052864aca58714335493d411fa0a5c2a919968cddaec675a8cf6b7130154ecdb064ec7635ae437dde4a8553332c19bb52e114457d5ec76d576bd1b46cde88d88498621c8c83b9cfc8156db014914b29a379f9234e09a58ca48252432d36f782dce9a701391e7287cb21450ea8c517381738205206fe7eee07e7836b41ca90f97eae0717c4f14805b94f05f21d16b81d9c0e2e0757020e07072465544f050929c0c8cd90fb39a13e40ebfb3b4cf3299a57ad687ee60338db97013fe0ca36cc8126ac59e48e152976162976ab7ea94fe4fecad404e8af4061059a016b5255923cfd3260d52279ea96ba54b9a4c0ba040aac5de8075624c9d3ef81f58ae4e9efc08a45f2f45bb066913c5556ab489efe0dac4a489efe0b562b92a75a0da5bdcd34606d81b58bdc692dac5d244eaa32f799b202c928c8225799fb705ce52a57394da3dd5a8852011057274ab4825d499eacee84e8fabb94f46f97abafd6dbe6dffe4dc32b7952393ef38d3c3f1aad1a6b7888432c79be17abfbdb060d41b0bfd7eabef63bbe9b08dcd79e84ab71e1ddc2ebdf4bf59a366638c3c63f3535c225e1863fe18fef58d46bbe7cedb33143aa7d2f4f66edab31d3dd31626c5ff8a20698fb5ee0362fa7d94a5d3687a21f5a15e6984c027aa3b6e644f752e23ccef9a2d95fca2b25751a065e3b77db795a57a9267586f0c8ca7604d9869e27b93ae747c3378f4a19946ede95f128121a58eedff2cd14fc80881653908b8b484d914a82ba826281f342619c2ac80d57305c44ee97a92972bf4c25c9fd127525f74b140bb95f7e2ec8fd52cb148411c3e30ad612923266d3e611e9a070623826ad20b97f32794d5bb882794472ffeca0e4fec989c9fd936392fbe7c625f74f9b29e8628899980bf60ea1cdb41aee0c11b9bf5de86e2ee45ec1ee10b9bf35ad86dcdf5689dcdf355310c7896b667368b509758206b9df3bf6a3cd91fb5d0b57b04a9dc8fd4e67c8fd9e29c812028f2b188ba81d096f27cbaa21f7d3990d1d89dc4fbb75c8fdd452d00b028f2b984cca985a70e47e192029a3ae603255544c9348c5f4e3d492fb6bf5b0528d7e09057095f5ab62ba52c67cba5231e519ae6051f0fc71b56d28a78165d9c18d6db66e702b9066999a9a2a7d400bf08899c0249184f444062b829dc02fe00e83673a6cb23cb26282bc657964454811429dec523de0d95ead763f0668df02fc4a9ed94e9a86dac8c94926d158ce0f393fe4fc40a783f427b8e59c1eb0d768a1501d68e514480094b1bd914968915e5a34410d4c1393f52313ac6d3441fae434bb3f41b73f95f4d29cf64bb01498a367fffb68d849420923aa0163eabbd94eabd552617b65eac0d185b85132cd591e25d33889b49ad303a6df1334a145fa394db064aaf36915c247747200b806e59b692cd3ef727ac0236d80cde38d4c5ba4d49b35c8d3f0c456324dfa26d4099b3090020181de329f4e0d76ee72c4d0bf41b56d732959def934868f6851930fb59153be39e94a6d468b68d12cfb5fa2392d535393bf26ebae2ee443fe1a903b8db5511b39657f0ba3d4674e4e4ab27fad2d9326d154d2d6c1647f2b7dca241b7730ddd3efb1ecaf4919eb9ebef3ef0cc58329fb7757ca903de02dcb0ecab883be07d80ab25c2d7a0e0bf0289bea674093c9b77dbaf7cfe901fb90fd3b973cfe394db0e7db411d986ec32b79bad2c984a5cb119323a6ade8b0023467607a29c98d9ed026b04a74c42af1438920aa5571aa12e656994dd1e5c0ea3c40fd7e17ae1ef65c2e705cc628820433a992822a4bdce6e2097f5085471522982c07153132a7195c9e68810a723401d2c4087468c18f254b6449a20a9725203b400942a905463ea42081022994f81266c9519125ed88bd957aff782c9c2a4f6022628886602b984a182a60a82c51416202e3249ca81ca1e203152646e47cd12328201df583beede4d44ede4e63c7183f70721f24a0a5a02f4260be30df53a78309a781dcd3679443a406e5b1ce5c3a45b568b206724f9c131378ac33eea9ceba87f650f9bd280a484a7713827ffd97bbbf58acf6a477d310a7e64fe956f268ee5ac5f8decd7de75aaf366adddc31aeff92d9bfbe3fadafcf467f8df992ee35ee57c31b1bdb0adc924ebab09ad6755aa775e02866cd7eed728ee056eef79c22b80b47edbbd9badfbabf61a875dd77dc7b524a29a553d6b49f73ce39db286bdfdded75286befee4e9bb2f694d25a9435ed6bb59ad6853935e4d480fabef740fe507f5fa947bd7f36526152d61ea5759f365a25591b3d296be10ab28c82bbbb5027d43e5764028ff8b6485d1ab0b197fdd43efec385f8912459ca981cb2d6badb25b2c7ad92ee7177777777b743ee63dddddddd5a77afd6d65a2bfd4ad3aaf7e975d81fa21552997288e0517421cb4181bd3dcf531170b93ccf59acfba294865e2ae786ef4559ac96b3bf176e516e360c691016cc1982e7091ed422fdd162ce103c7a50a635a945fb63cb6673ced9006d6ca25953acbb9b7e37c5dcbd299629a5b5d68f868f3289863b9863027c7f7e776bd4bfb96afe1ec895860725c0f3bf570d9ebf5a3876586326d7a056ae61cb5aa4e1fc16da883afb5d2a27091e69b6f946b6d659d2a53b97ebf7666fae3fe7e76f43a8d54a2a5fda0897046b3fb7b6dafa79083257b7d37eaffaed59cd82734cfd2ef580fa9a779b1b8a3e75800d3be986fe09e18dd94e6b37ed0eef3bd47f4f02eaf33a4dd334eda67ebd6fff8adad719f4191975777b0f0db9bbd34929ad45b3a8563b8382ac36caa4a41c2398be954fadbdbf7920dbdfb8700b67f2fd1a33337926dfb09d344ddbda4b3293754f2bcb7f6a89bb91da47ba0349190ee3ce649bf9f63d1c69501ab39d56abdd8debbc0f95c2aa950c6ba645e3aab11969d4f0c6c19b1ae2489eb982455e72ffd7d4e5869f91e4e97f61033c72432a2a94727f46b01679862aa71737c0e34a3672aba7dea9afe3ba90dccf24d71f3f21f5476ea89d5a74587ed9e0d9ede079a81f71fed195bbcfdd07b9ebc4163803b2401950fa8ed7a1de9b1985fa198d3ff5ad54dc86c35438273751e0cc9cfd723fc8f7de0ba37d5c62ff65727ddd5cde1e0226b88f2df2225b344a265be4b2455b68997474c0e30cb241f68b55f5668319cce479f3edf4bc8e033d16f0ccddbcf393be5367fe7e6cc3cdc4723db21199f0067ef209dbddc20ef6420c1eb98ea87b9c6a3b714e307aa7d6d8f2168a1dac4bc2d4b59644b441cc27d8d76add8098bbe9554dc3c9dac8ca9accbb8fcc4a15e2907b2fe4c42570ea51ffbd0f76f1382eaffefc68dcf07652a43a7cb194af38198ee3388ee3f0a7be4bedc033639427138e627ed1a842d4d7189cb9e79c42fc473fb5934c7d318f798c0bf10dc71879c4b7c268f2f61ef37b311ebd7bf458eedec2d4500e0d78b4b3d9ccb586567f865f3b4b1dc9fd9a6cb6823339ec863b5185ddb091ad60d3013187c82385848531424ca92269888603164c86846cf882021b805c6084213f4ef0821fc43449216310020c2ce040c41339a4503212c40e43d420c31020bac8190466a00208103f9ad8005300162650207d71818e235aa36200537080a4ca16237ee800827c79819b2b5da0cc40830f2223f9640224848c14e99001952e4d607c450926454909c6ae6f42e9871c2f78bc59e678c1f23b1ce7bbbe37daf3c68697d056b674995b64ed2d27b42a113184f884134dcc765aad7637aef3542b99232c376081a145e3aaa137e8726c80e77b18982e282a620640e00c4e48b628d9d24948be6568550495e1a16e09b25c159a27c5dda2d8b82e9ccd5aa2785a62ac2c8fb43041c96479a48588162058698c4a6906577ec8784858f3bb99241c2dd414341e142e8e4bcd5116249b569647599cdc1c6519b2627ec7c2c3e23493e51116265a1c61b9f2f33b108b1033591e2969e940690a38bf7be149f0e88a979ca32b4857a48828ed852e670678be97a39b43c47a00e1436de43495c8a40c6661221a892daec42f78bef738701079c4b19940787479f70896591e5d39c2816e0ae1c40819cc44e8e677bd35d899abdea5b6dcb514a1cceb966ad56b905b860fa52d5766457030060409ebc1ca0d6690832f47208c18480c358dc4c3d5a563c07243112c3004510a62338526c50d14f4000490189ec44008cc0d2150406181131eb818c1c11625417492b6c4909384c4e3c58c10b41755f02acba3a422927888a1832d412e8455b4004af1e3a3e8d1a24b0b2c94585811c5059c988f1b26426cb41089a1c6b500f9e1521ae3028dd20c845a2f9a242d41f283951444260987d06a0a175450fcc05c5a4821f5403999e1d341062fcb1018ba2c412f704cc8b061b982e58817b008d143498b0c4a53ae5041e48a97a12b4840ae48094a7aca34591e6df141e9471e638850a6ce8e51ccfdf983dc2f6fbf8b892826e08124e43eb675846817ea47a3439a023c7fa45a77adb556dadb4743c70896e148f3c4eec5881c438cc98982a1e0f9120848f22081e74bfa34749f73874e76a96e903a50ce11e168011b74d2c5d892eddddddddddddd4dab6dadbbbbefd6ddddcd755e7fdddddddddddddddddd3de79c73ce39a79c53ce29e79c724e39a79c72cef99e943d3252f41fab8e2cbfe2903db82b504d76d92f655390eb287a826577f77e927d6c0b1de9a12662d24a1ad6b1c909b5e82ffe80bda4d6622dfa670bead8a129955e922a48cae8f757254919f39daa208f5e527615136d09cd9c227037933cfe35a865644ff531d6649f2de43edabd18bb5cff2aa627d9bfa35f0d55926a49a514e4197946ee1965f7aa0fef490dea1e8f08d9d18013e284704fb820b8261c132e09774493a2c5b427bdd32348f654654a2909729f1493d45211124c8a922744410c654f3511caee7dc9ee4f412f69a977fcb50e554192c7352f898b491e7f0be40ebb59d56256d3eedd36aea35de73dbbbb715cd779ded779df8742a5525845552aefa942a53056a9562b99958c0c8bd59a69b568686a6a6c6e6e707056ac60d182b668e13d5bb824385e098ede956087b3820565c1c27bb2689c8bbab171675a2d1a1a97aba6c6c6e6e606c7cb1183b3e2a25a16c8144661b4c58480a6174658053580b1220324a2ac42dffc806315f39cfdfed51067ae33c41da819058765597b07313e4c718115295680b0b2c4458482ef959a36aded5a9d52ea5e3b67110a9e79bcf265f768777fd119a5b5bbd66aed7c2de7e868363b4a618e09ed0e2323a376ea95525b6bb5d6da27f7a391f364368bc568e43cc15f03b6fb92d5269d4cc66a222299554c3585324ea1b2fc9c17b4ac7bbe2c7f5224236a9ded23a90faba96d56b6f9b58f06adb5ce9738228cac7990b7cdbac7c2b6a7cb6b513bb985ae4b492d07dc5d4a7a937a78e527428c068273a4ee77ee79564a299d7af7cadeb6b95554ade1814763b6383f1a29cc2ac48cc92ad09fd94598c99486f6db57c383dc82e39ca3a8fadc57c3834c5ddeeeb17989993e4ebee0ed1d1bba954e5135bcc975876eced9002f42c2307d11670e9ca024a28c19c1d689a4f91fb40bf7b370b2f49e08cae3e3b8c83f598927c10b920464cccb131d2b18e6171c4de429cba326c0e42c8f9a88a2092f4c3835b1d4c415da84cc864e07ca9197264c3e6cf2c5613250972f79be045e0c9151591e75b17243888526805401f264086c00359801092735784296440b54481e3f3f28c0c1693882c869f02364e81e881f1c38948a6490054a0b4848204ad00cb0142c39e2881134a046d4e0352070380d3f382e13429eb0e18921978c7ba2bb654e964871e2048f143431e3871478a101081f3754717bc45cf35c1e667537ee229072b70b16f65c5e67d9ff4e72b7c7f2986860125277c041882342d38791a422745878c1e588e9051fda9323a6168e987474d34a2b0dec752e9c77413bea6115e8c0417429d2c588076c035e22881c5d82f0f084b4450aa01d3964892226831210f9d114030d4438ed4926c1800483199e4ae025073788782032c3e10a0e4e7c1063e4c763e1f4907643220897262e4be449002e397041c285ca6fc15c9e6c796a6579c46589621d57fb09a630c7805daea0940370ba83476b786fa6fe20507003bf771aa7c5c7c92d79fa6f72bfdf641f426749dfebee3f79461f21b318036aad4804cefc2cd664ddccf271f1392b1051f8c9b5c6d8d2752949ad8e126cc3f1c776f76ada4b6b6badd74a297dd1eafe72db9dad7834dbd6e06c87631d43c828fce47eaf425a95b556116aadd5845a43a8b5d6ba7d7d134400b78fe13b9d0029f6cf0065de065cf137208be7c0d5e3802dfe82fe316658ac16275042dffc7c391f46fbe0703fe74fd923e2c07386acf67470c0e394c54880d8627bdf8ab7f91d2cfee6518f3fd4e36f845901b9d3df6b1291627f05a4d8effa9a77fd9c35a1cd3a38e031c6cdfc157f03041bd0e65b3c0936dfe2e7b7b8f915e10a2cfee65758f1367f038418fefd9e0e134cf3feadbfdfac9749618ea1017368f12d30079c6781f349fbb4f8fe89f3333689dc87c5f74d7d06dce7e61b3da35eca47a95eca1e0c808c4370e5da0a67a414591398e14865c016e00afca0c55a7f05285d058e53490726388978edb2df0f80e6b1cf811fb4681f0784d13bf66fc0eb3b7644e1a7c518e07176c9f7bafa0ba26304077941275d883062d4bc7c1728001db9d381145d0208000f0c0078b023ea4819f767e4bc70d14108c65c3f7ee0fab1e700aeefef58322b154d6be676dce67d388572cd88b164562a9ad6ccedb8cdfb700ac592597956d1b4662e4ba6f34a65c19971cda76a1ef57935dfdde7ee6f37cca1c6a5bdad7169a1cd15a4526cf5ad42b7ab71bffd8efb1af7db93c06d57bb776a5a5bebb5524aabbb6d31d66d62b5a1943c3586ddd2e588d1c101cb975a971f591e2d219798794b302d81f4248f942e31449ed8970822dba0d3b96109ccd29725a425294b412c35594ab2746469c796279d1ab820911d7069c22587a51f5c90e4f9d76e40ced9ed4e69add6cad75ac2c812069aa6699ae6c2d968a59556ba8534bc4058660fef0be6cccf6c65ac70bc915930d58fce440529f68fae439e373f7a129c1ffdc88a1f5dc8e83f5c474f5752f3307a667e8c11769080192df063b84f07a11715197502648f47b917b30847bc22c409c70f471104fd41ef22775aba2c8aec712b45a0cd6e45f6b4bedfb3c89e2db2d5fa9befee47e346bba9594ef75a20cd2ef3e99e5d067676590b9cd9b348ef220b20e500469c99fa00069079290bb0d250373fa35b2d50cbe0df84b2b30167be069c99f99aa7a199b133343335df4d2f847bb1ebef267324d0936a404b5404b79efb1a90655403caf4d780aba61a505504e2195403a6b24e113ccaa41a1095758ae0991f5d9669fe669a70f440a6f91af0cb2e902604479125038efe65f427e40d723f76190afcc0b10ad518912c090ade6e652c3a35c59c33854b2bac3fcf1b2a518bdd5721f74185558950ee5a7443dd3a2545bec38552f26cf651a8b0ea14bc597e5eb7a639622a9420c2c4ecaf82066c2875f78237e0b07c82f6f6b5232d3627b9a17aef57f065ff3e0af55ef7a4c23adec855044a7255fd28821c720d47144dfd8cae31edefc5b8c65a64edc71655e5b5c83854397df8bb4efc9501f9fe1c57e0867018428aeb913f5045456e282cd327c43ebd2bee29f7328afbdc6b344b1e6b4c4631a3d4824191fb6bcc7dbe23b9d35f25e64a5f9bd3a8c44db62635858648008000003316000020100c090402912c89b340cd7c0f14800d66884068583618c863a124476114833114639c01860003883104196588ca800060ca2805e4112bdcbf57f8c9e468645a4ebee14164e3a00d88d866473090a756ac0482e8dab7f3c3542de536800d3e8200753b9a5b62c34cfb79b872a498712fa8ba413326f91c484690fb50948dbf73b175740ccd6434f521153fe70c891477c585cb4dc31ed30d723f4a1ee24f432e506fa8698c603f696c934ca0d94f16723f3e29f0b21c39eb48527ffb6f488f45a14ccb3d0723a521ec8deea3a47c0e2ac4c160041e00644e95f8eb3739de147bcb98d16bf7c9f9b9125b88c40f24c90eb96b24d124629b16e65f50c32246214bb1b8336ccc171fc6476991f2bf5beae347dbd88ea3966c3d6d7ba005b68ec1a71ad65d9da9044737a9af686d93974b2f5a55414c4eecb2b650b463115bc9cb0061266e8d288ae54b991df2ef9b9a206c0ebe7dc51ae10213f15f484abb464ca3886dff9b1fe1d2558936060dc6899fddb132b7ac47ab299670e620548aa13cd675d020f4b16ef77c15ee9c37eb5728632f624f051b2c40b18f17db1e92993ef3c04a241959525f44f9c802479c48780cb4153b75ddcfe51093ba308239198424aa32a5b97cea4a6f459136cb5e9dd753fded85c68c1d63fb7dcb6a5c5e6e6b927aa4ceacc042a6d62aaefacff527a7c6ac6830d7cd93a8cd09ad71bdc7ef5be3bb23b439e083d30e3dfb82eb27231804d19799a1319fbd277881354b30bb5093aea290d97a7c5419535d40f3f6631a2aecf3ef4a9191ce860ea44d86581e53a98f059d9de6563359bdd365c1eedf92c9dd241dd06213320324d6927e0c6c54945d620c3bae9e02ffe8a8a8061727e1392524f29e2a05b02fe0949787c64627a6f8d4c6362e0c711419c2d05f7abccdf5c3575ba0811fae739d0a256043c1c716f098e02f7aca805861fa35c897285772276608b30b50ef48ef1ffc07916473620965eb4ab1ec21903365550ad9bc75d6aece520d807b0ef4352e51c10fd5170bdc7df0f3978125d60c065dcc2278a28d6c669d23c97ccc997e97b415a7f558968a32a6d2609fab5b308b7c132a3e9b1ae7adba398f2cb28971c23e79f6d989da5f6cc295cd632bce5d1de96188b0f2115bbb14ef6e9a0152e34e6b38a7bd5855a567cc82d37305feafb432754f428dbe734a685d855133a2d80c5f8c30b6bee08e3df162dd2ff791f41c5b834162013a2c4d2a05ceae3a4318276b06841b0bc46f068b59b24e4b375b95b9693da445457a9429dcbb4f11486f7eb5eaba0616a106918b4df68fa82ae92cd8eaa337ec6c998b2639e079ecbf5adad84609b7fc29d3f0988e746c4835880bf0bea3e121afaca1745016df38c6c3c894b79b1a2d982a37e20ff8a9cd04b7b21ffd78427c352044a59e40942a90b2abb1cf3fcc3d2147b8945100096050afbf2182e550466821e6e220a7a0e0fabeeae8cd8125504084e6f35ca8bf7c08785ceadf88107828157417f88ba03a612e10b840a8bbca9ae0e7c4646a5e168859b6c888aad4fb35d25ff3c23c1666d4681beef2f6b7fc2d0b6dbfc11db072499dcc140db15726c3ac03341c31b1b8522539b652ea667a2ed21351e0a978732e1d03bf2d34cec22e120736fe6de21832450662987e2767544e4a154e465f42a032440977f8176ea24c61081530cceacb8af72c2ea171d3562cdda7a79b866a9424ed64a016354a7d9a6e367e21706768e60cdf8706f885efdfcf204af9db72810811c04cf5ea7d248c4dfd6d007ee14deaba4275793b3121f152e1322a03a29781886c40d4c2a05d98e37f611a446bd687d3afdbc5dbaf3ff56aa47aaf9860cc07613c5e2fde1e9a60504d019350b2e6e63d2a100d178259160d4a218148ca24b5a5263a7917424040f95146730f37d3700838061296ab76a784f35384b4e40c015f29ec320aff1df2deac5d289b6352e66033ce37790dc597fe7fa9b1de02907083cd0a939dc0bfb63b77962b860935445d37a017ec0873218291d8c113843e34e4b41765fff8a30c3f3e29ccd72f05a8f8c6605698e2ddf364abca32e776b3c26e22b92296e334b9d6cc92fdd458c78becc08c351f8b399258940b9450a2d237e5e0510a235e12522f6195b3e99858dabf8e87b7a03424dd5ea09c5e1c71f02d810d774275c09b170c08bae1122790c580688e75453543b9b863f069a9dc45dcfa865c4938218a15558fd0feb78852a6cb20604bb2ca9d0cd1febcc82c9a6db1044aaa101d3aecb8164e4a8137517eb606660753cb1cfd455bb98a19a18c27825d2942bd20da0ea74d4181838121847aec954a4ce82be256b9c4400054d52d941b515e59b4e75a7e7f474aca2d63ce48c3accfa9dab35d2c3f4d9b6a7c62c2e81b882607217df151759ae02882fc891ab0a0f8f5315dce7252bf15db21e530a1099a235e82d5de1c9bcc05c4eebc40c4a724c01316d216ddb899a907a12c9a7ae3cabe52c39eb4a037af2751ac5ea9992c56ac9ad033764cbf6886068bd559f38faeb4128f950e354f10290e94253c0c0ff0061f454f959537f54a06983dae662cc24b8c458fa8cf523465fe7234095220ef1b921bd2d8bf5604a4c98d35b6f287edeee42898fd8387583caecf0ac0e284e5ef9fbcf4b51ba1be82b40cffdfab308f0198c725e0b745b4f5d7a85b5bac36f04443b55ae6bfc7cfb00afd333b5420a4198d3345c290675b2b0d42be96a76d85b33439642d562fac7082cb46702747b92b797a21536d6e3fb05630891c3b13b9698fc65b35ff7ed62683b713b970528dd56dcbd1433d8b10d6aa77f6348849c2bbe076b92083117e3c6e454454c432269017ea95d19bd813242648d62f8585a8fd92059deeb24be44fa6fc1c2136f47d63d9c835ee4bdf53d856b433792cd719960a5880ef29e12020715c46a89f96e06dbd6932712c4a66835df537649e8e2a175c8319cf8b06c6d227c98b6a9f54f9c4105841e6d764949b84101de421e1d6341b95435e221c857358e6c0024e8a5c0628576a97d495d3dca868e30467b88b72e7e2f7772071ae817271ed87ae71c2922e2b815c493e03a4de8d7fee4c2b74a0534bfb90be6eb361285e6fdc263458b770056ae9d5734b83e47d7bb8b7c8e88ac95f7aabdcbb38947d8d706c80a918021a63f48611963cacb7c619b7a92c6b7d55d26c7704579a562260cbbde879dd27e11e4474d5b69156fdce0c8779add42549cc20db318566ca7e02c2aea5417721aa5df10a3c66bb0b1b085d685c42d6d15de38d35888e75cab2eeb26c44c477b973a6ec6a8f84c28311a10ffecb238909916859c916dbda9a86346dc2f97215af99f5c1b3622a916fa9f42dd1f205eb07bc004cc15c4a387981675173571330df3878a6a49d3dd91ed784ef3f07b2fd92e0e4c9e87e579cbad00e16273bca2ec4e40492542482178833251fca483666dd8be8fc69a69d5d196a1186b57440485d5c0a9667bc9cd2673f3ca53d27e1c1380e56515fa04ddd60c11b3320bc094730bb362b56b94488573910cdb5696c9a0eceb31a136f9acccf14aae7057bc64bfff4f238e44c0db77769c4c9365353630f7bb09edf0d2ca49a12b6571d087a09a110b629915767ef6e9c6f5a4dbe4eab70d6dd8b2638583f9353b333d2a3db67cfc93b18d14276f9cf8bbca0b93d4d2af5bfdc8a3eacbff7a650eab897a637365cf319b5e7ef555e28ddf8857f9c0763be559f46d21c6acafeaa6f7dd0c1234364e75ee459ebceda783ecd2ed106810438b56c03d15d8b19daf08cfe1e798355519ea9d08fda10b5182325c1aef1ce2070bbc1fa4ea3a0e7f769685c8b327831b198c7f7a786f204d6be3dd0449e34437094deb7e7ffc5e48abe215f8451c4b26f5b12af45046a85aa879782e89c91ba6e2255496b1205d76ad1730f2bfb5672316be0ce0d69ebd01d88481254326829501104148ad25d41de95ff4e91bc39474446bc2ef38e0f89a2db9ec8563b9869d27d669aa4ad2685bf00bd2ed7af1af28bc5660a938535c99598aaae7bdb2ad2d23c9f708a9bb81ac4c118658ee419f4f4d56e6ef89eaf6c48a8618300171e716f1f74afdea63de8f3219afc00761507e6e620abf4698bebb79aa7bb861efda4230187538a26bc8203735583b5f120060d5cc9552cff2e4b5e4695363d44c154991f7c2bbb0bf9a679b845022b133534ce40aceb2fc8a8ec7d3fd6c4b6044dc9962c24f4024426330830d041c524208e657dc2be75515fd67a49d500677ea8b4cdad10f8c4e00fd07197453fe94a50893a8dcb57879200a82a9b03bd6a4ae118072200796f2e23402bfedec7c3a1c94201779a14d3ee40914534b74c4823677b3e7e3c988789e655d7f35630c4c03fad1f0f7108ccd8c3523a6feea74e6647879c4c9e2a7aaa9554f5121fd6141f10bbb1f0bb9f55efaf5a574897f3181630b177554acf2a669dd008a15501e65f7d90cdd86a8c421349f30f73e9aa658fc2c9c84949f8f39f8275307ef834bd3113918e0048acc37acde63d52e9ba4baf177860d944d4d7dc5248c406ec900f707c7a92df0d4893a80870625ecbc474b66f41adcb864d01a1eee612569de59bfd321c4a3659a948445852ca3409cf782f03559a3a07d32ed68721aa1be375bbd8c77c0acde0e326b622259d14b9ca43de706ee43c47c4b3d0ffd153b45bdfc2ccfd46526c076f2c5aeaa3515433079d0a670596ec48460f1443595510feab00bfc2af85cb590c679a1131069ba0b1b7a3533874f6dce49b9dfab1d2f69202ce4b2fc7a58b6493c9c6396a98ff608499c8e8467f815929c232fabe621a4e39371147cb60a3992e34dad20298f014b9122f357a567a00a568fd75f1623930f0dd1a8c0de27b214200fbdb01587fd87a8ab7c8acb782069b27901bf9feaa15882e8369bdd85a470e1d0c66f98ec2dfc9a7fb1dddc9607445027a8a4c0c5fa9d19719c0ebb2bd9925ce39bc51ba1f7743121ab6f4bd1858b994c83f1588917f396d1d5fabbe66a2da29d5be99a0596ac8fc35d3800b986da0e006e2af0a48b6f8badb517cecc07e83e46c0cb692082387ea285a6c08bb0e8c3b7e483c0559d15a50c7a949b432ebd621b0af5b18220f415e30cd41c41e245b43459da8bc71b89f68f3e81547bbaeac402776b4956546594bd96dd730c9063820bd52e5840b4ba6d60c2fe8383a5b43c23bd08db0a88758192f541a8aabd46d34ec26cf02892002bd69045c32d785debc7548129495d30aa7cbb4514d6f4097369b721d7a03fa8d1a0002d0763bd02a945c181d94b55509d344b12fd22405d224812020953a589c60d17442af5192c0c8ac8115fc6dee807d1af90c2bcc0167eaa5c1f17f7cf1e5bafe2c6905b2c351f86393d034258cccca5401b5ed1822fad18da83f5ba1048737945155f078f8072057c960b777a45554a654543e0c6a9f821d28ef1fb6ab851a2c2557d0301ab1faeda578774dd644cd78f402bbe49f52b2e226da7aec539db427ce842a22f394a25e5c56248204a5c08fb1d39ce2b072c48321c3e3ba04fc062935c8a0bdad95fc83be478bf31035943b86068023e302403c7c41fb7702d21a96530bcff7106ac7c89a02b84b07fdbd80f642e78694bff054e43c6a7590addc3267ed0b3f91733b93ffe800860c63c9144b4fb6e7b53516c56e85a16aa95b019337cb6390f5c6b075fcc9f312385a1319104841bc992550baed9091459126ca61f7706ed9a824ab0c47d9205b19d4377a4596ccdda941222d8b7cff46aadae3f625cdb149b82a8264adb106c24d97ad641c8344589fe0426b2e3be760622dcc5aeba226d587b54152579b84c7e6a42b9ebd29e1f52412df92c410d99ecc00457ae97127a52294dfc79dfeb34cd1e20f5b3e2a184b226805e2173db572ade2232e353f96be06e74df2c8e7edd613ded9e252f9f62c2efae566fb9c8f79cf362bda8928eb2fd716953fcdd068b295cfc96d8ac22fa125244c1346d6e4eca4725c01a75467d90c55039498d75204a41cc86e91594980856185fd82b61e40d8dad25f899b4b6dc5666155375b8967746fcf63e9183b1b2524b1a26559463f061b7a4c0d9a5706992aa05fcefa146a5ced818932f9df63759c7ac2b234f4550ae1125a0333a8d12ce46cd62432a266c105834da3c83af606a43c45f9b5d6627774830219cee0dc10f706b27ee6d469b68560f0248692697836e2076502353f30642bc75a18aaed3b9a9d1e85a629519e4a383c45641146c21e15fb20d6f268346b3871d6205a8a8616496bbf26ff212ab072cd7bb8ac8bf0faf3e51cbdc84b808f5f2478cdf7969ffbbc751fe70a2c5fdb1da3dbe614ef596eae578e2a658d45bfca7e1b59ff7f0b65e99f13807ed25382de052b334a090e4eda2a2cdf7b5d3622db3586643706d657526fe9df489b6dc5bba7bd1a2f2a7dd66b01672ecf5111681a8b3dbe28a4223374e5b23e05f99e9e6dd944420d55aa83a03695048b69160dd8abaf544b389258b95d55870c33780d3f03d793c72079a4c0feff9bdca83d40331bb5d62da21b28badaa818d2681d026dc3a6d0dd6febcd132112a345898d1a4254424a05e0a50185ac635e426dfce2e15e5db14c9366ce9d56e9700637262d637a75cf028d1a3f40ce78a5948c2e94105faf0929b13c57af7c228b73fdb31f5e36a7d83018e50739d883b5349fd285e180a0dc4537162a9bb9a574386670b149ece19d066ba5a20e3eafb0ba8fc50d45157d458b094956e7e0869b35035a6cf822d4510403f7acd08ad0fa66f12d956234c4bbada10cd2619716ab0bedb98c0d93c82842e185d63f06efecf79a9620406c24c4c4680c95ffc3bdad37ed902504456d42fc00589c21aac450d49a652b87eec30d289969064f2f445944142a4927b3c373cd4ece203b096055dc9e37541d877960ae1c82f8d0348ba45633b5782310d3a0d5aa027897d4c3510baeca6ad79be06ccfd7b5d8e1912a46a5f05fd3a07d39f517890276429b77a5c700485e6b16057625331a4a910626d6b271c4110b4e721909a35be82bd5ab55d825a70fcb6e128c870596b0592cef20d3bc12d9f01582acf2b4f342005a5e784c4d57ce084cc5c9562275a545a5e50fc5a7438d23c96d034fd614a89362815eeb4b13f8c74e8c5b8622b0601aee9460b4dda85fac4c13e8afd68c2d3a2b217d8a2e3cb2b92b46f9074b63c74cbc6a7214aca8ed8e09bacc8b25b272b752b35a380ef60213b2564df5248c572c6792d0bc23d0fc6268bf05e65415a337909cd15f706869abbda9033c1473991b99b7db79e14b12d5c50f9698f1cb9f8ddeb637dafedafe17a85cc94340123c1f86975e254d59c2ca910366032eb5f2a323f20ffeb6b21c5edc7c405ee0546d53ad557f97ad11e6c4071254337f9440504d97a9c64c2d8d6c5aa384e9937d8577071a6560a8e5775e2a669a14d8afc0738a7d7491791ba5117ed1e8e7bb56e80e300aad80379fadc749f0861c499e8eb2e27020e4cf406c8f06bcb602016a67c10eed90146fa06d0ac88409ce9ddf1d244ef554d59a81cf751c7f5b04240fd10ed855b132722fc4b9d91b6dcf03828ffd7a197d4cc2bab2030aca6f95bf5eb4393d272407c161858c108c107b9cd85d3337bf43f2a1ae9e350e3a71163a1005a6717a0baac829659be34b354bb718607e4f4d17ae542c0343042793e22dd5ad7b438a913b9aa8ae1503a40013392ba3c5be7d19b596e6318837aac860d8421cd0be9a108f01273c7576dbc526dbca825638a16436551738e1e578b983d297b87789588bdb405a9b7959009d379df6b5fc27ffb0c3f91096a31ced8ddf949541df476f00751983912114705f2504338d56a681a7deafbe4e6a914728e8d681f0ddc8e8d58039dc3489908a6036b324bb7c54985bae8007028b63a788febd15ba83eba3d32be380cf0e4289fc129abe116f299c803b37c04174a2f1e5c17ada2d4b679c4466457a171b880f07870f3a38ea4082fa60781f3d6450bee8382b1391e46ce2a3c0b8c41a54f53f48e759c9785705a125bccefcc8c016d7b51778d765503ca99b0577762b35809240cba8c5d22b0ccaf8b7f933fe6fc5457eb6609450f823c112219a51b041d64d9ab0ab612ae3d6a8525b275b57d8ad808bdef09d1b7b400ae3e3b5480a5ebd662f1f86a32c0f6bb2470006d0c9916a34c02973ea250d6072c3bcdfe39fa51e069015616b23ec98ec9c989f01ca88c32a5cdfd1520b8487c69d9cca554351a3dd376ac1df314e031261b2195424704cad9a5beaa65b59b98210b71266074fe5f8a4e0e7fffc40c2b61c62aa8aa8cefa8fe9e8ca9a9f39eb9982cd5f7ef13335b2655a6a86d0a9f17f8129395098e44770868d9e8e8dcf5552ef0960a864ba8883f1bcdb36682e2b3704e8fa598fdefc3005e09d7b7064649d1f50ef15b51f309babb8b9878556ba961d7c25cc3787fedde98727943e95450d3023f230524911a25538cc9a4e8f6818a814eece236e1875e6696c65ad92bb91ef66b163a670ae3224084077b1ac9195a0466b121c7fc47496a0fe3329c869c3e78a330bba28eb716a1aa67dbd491999b7c86df7261bb3c57bfda69655405b341a37329974b8cb9b377189dc9b38fe1049e9800edaf030562ea5ecde545336ed67ed481b3773f1be58cf62cfc2dd22bd05ea1525aae60c4739202a700d5dea2228ac081dff7ff09e86d3eb20e38801779e15d77a10c16d4e2b5ffb60ae66e2f372d2de55e3e896489db62223b96debd97a357dae1735bbd87d9d65aaa999a91e8d60e0d8bad7348570110c1c81311acf661d489f3277f4b8d7a27cb2b76599104236ebc978a17c2aef97345cf296107edf341e559615d620fdc089b73ee2d317119089b8e2cc96865f9eca2620bf75fd968d8814cdac34f8f3b42c01f2afdbef6c47a4989895165fbbaa7f203342e8ebc9540289192f82869cb287d09e82082ae6b392934e652771a4a1880127bd3f1efdd2f3b94d628bba13314df9d5f5176c26ba4831a5e38be73208d47f5dbf631331459a291d7eef542681e7a78edfb18998e23db53cb1aabe7335b8947a21ff6d0ec8ecae0696aa2fca54da90de4680e6d047be78927123ef0bff5ea0abfe2f6c1a111545241eb9e57b905d21be8c134e9de3a8f4481475aaaafc249ef88851dba52e12a170a644fd7d3813417ec02b471f12e2a56ab882f5a71ed4872c8e35b704367c72ef3bd66c424c0c3eeff45c7ff0ea36f9772a5d9fd4ec53529fba40fc8c9c553460d2652cbbbefb7210362fc441905a31b4e5e108d1ce2e4337b2e7ec2d5ff5e14d531a7039226ad3ae2f7721454b3aa6dc319e052a94b6c8514dfd5be6fb6ec3f600b8bf7bf9e9eff31bf69daa8b7d9fc039989dd2c7ed6a80ec207e37767ca61b23207463a59e36d84d940fa74851e52a1cec19dc5f0d3e79b1118486a985f07aa17eb2d7d49bc897ba753c9a1cd6a61baccf9f10540da614eee0634023b4f9551399f63e912972d2d6837ff866b904232e1771c4eb1a4875f1d7640b0b7d3d01879bfe16f571d80ed7107955c1c9e6a693d5f0629aef5a7163e9eb1729bd334fd5df9552131f80d9e2686b5fa891a3104dbdd7b39d3fb4bc88ad42a57a3554aa5721a57a152ad557a392b312292d68a554185fe9d0c2255467847a3a557df3e3d49946b96db5bf50af40251ffa538d9f5ecf2035d97beb8568052a735780520ff5f4472498b6ceb8da6bb41a29d7ab51897b059510dc9352d519bebec66a140757fba5f82a2aafafa2f27a052b7357400906793aa50af3cbe99a44096bb5dc3b0c6e52c038a4a934c31caadafd19ccdaa7ae2af2b0821635c957516c2a2ff838d02e95f11c52adfdc810265c3bbdf49e04f58d421832d92bb4d8143e621021bc8362be7b26a0ad7b1dc8b4b456336dc28e7d496f2f630f5e9a69ff3dd4f9f0b58d07cbfd6a69601575aa817791d9931e52801f298a656164fef3aa503f3d4d3ecfba32a89e202257ae8c18268808dd56867b1344b4b856c690cd050ec332e2b070335efdbeee6428e2bcb71cbfe8f433df4a7a21843b5e136f7298c2b740a7a0bf00407e70888cf985a4bb1c1eea868398a0ac4061ad6d3652c19b2e01eb97d9894f19dc3d0f8370e99a53c35d83bab486aadbc38bfb0989b6f1400e4b5860f3f1d2090753247db4a6c7fde21b2b16926d4bf099c1063863d79621c74fa3c2c53190906fadd8b08f9740640e037961c546507ebe9c886322c30ed811862a4f8197f4e340326e5af80a85bfe3f3f0e9e672aba1ce947a5de018e31c3d369b8a814f8d28d004801f102916b67f3138fd21ef237354ff5bf9d1a27cccb19be92db9ebc2aed649607a2431c63e473fba9b5df9a286c76e5a03df1e7de8e3cff05329862200f207e52404b0cdd2b9f2a10f268b1af33f3ebde74240e4806571c1392ec7a765b505bfb2801a58c62a26c17b84a1d99d6033795a3a64fe00fdba017e5cc8bb0b023fc3f1cae3b893fd60b613c4bb29c4bc0e0f47c8e6895cd6d88122e881e25cc98b1a2b4ac05feab897185ba3a152716922115f7dab8c58b28242e8b74e3414a3c6d538d7dfe79217a2ecabb74392db331caaf11ca9a4f067b618609f9416f6501a3f3a8643809c38f5f6e519d9084289f6614d7da7f709cb7e32be85e86b3f165fffe3b3eca2e737a8453ef5f812c3591f36461fbca4f85e0fa2155d9ebfccbe48bd4a5f4d5fa3d4cb494fd43df9e22c2657cf8fed0ab0f3ac33b3ed4f9d1a2ec903775156f95900a23d38d47ba13228e4c6dcfec1f45b45b03962896265bbd544b7588e360dcc0159de917f0c59b1d53dc32d727b77c35b8ad24a46cea266e05106b9459d33ffbcd20d8214984ac9780f24d86809e2868b229d5a431c0a114a534f0d1e95be03cb8dbdcba2f5250b00860ce21d08a25c1c5ff0c172efbd9a1f97851ebcefbcfb1dd546389f65a7ff30e9c1b0d7a9687b2b9ae9790b039e10777f68212d4c8e1d3a96ee1f13dea1e01735fd7182367731f849b2c41c20f0bc5146c75352a1468390fb4cefea930a49b37f3617410744608cc37b6bcd2b1ac29061db1a316edb8a6445f87ef75458451efef8538bbd293b21df778c1c3f7325a1be87fa0e0084fb32f452d58e72510b5b6e79fcc454a2c0202d86942bc1261c6b5dbc738b2807def6cf0c963377eb7b5c6a87bbd36a036402e17853830991812a7ac005b351a160a9c9c00e4d1f6f2a73cccc6c5813744fbe2295932fa6c85d0bd349b0160937fa24e0c883117985a76b0da7f26a941e5cc7785e3bbd127a11f3adaa6f7989be12d908c760a0706533a0752f6eb00cf7876c4a57f1166b79445548ba3e9d58e5838eead1d87bec329315b70dcfcd75a6dfd4a59d98773257b8ac245adfd4efaf8cabf4571644fec226c1e4c274020ef4ba180771087062558117f4490814d4716bc7dcbc89a92069d846158e494875c238efec61d875daf571511f3a5ab597ade159d25d504988d0670cafc6282ee2a56d4f189639b2da89bb37a9cc8c62b6d5813f8f64af12bafd48c64daebbc44294a168f5d43ba6809de26236220ec637ca768fba7854f6f1eb72e58071bcdbae1944be2fbbd5d985f909a03a0c4ef51c6cbad78df43ae0e4c3c3a4d2e8189295d5ecd5a447bb49a3ba2b89199b8e4aecd62c3f3310059336b0f57fce5bfdc8d73c55207f94a88672ed024abafc524a041817d3e49ddb1f27f9d660801b5b7844a30aa42ce2f614352df815ccc14cbea1efd0d4811f4b745d11b7b97424d316a03a5ca586af4473b3bfe50a2be93aa5674a0cd5318251f9a4e498fd5b45caa236e11801d907a006e6e82c2f8d86a8ac2d247b0b0cc74a6e845034041a0264c409b8fdc45d3462865bf13284420b2fea8851b9da835382231f7817f6ad458a8270e615ed39611cf39f69fafbb6ff925cf3e2338332c7bf706bc3c602d5ef35b51ca5028a0255736c063685b9f39b0bf8080bd19a8ace99626ae1af0e4d1f095bd94cd753f3cea2514fdf7ea12bee72d35926419554149735dbb6bb9147050fb10a0c60789ad466dfc302128c7e2b9ca5e1b9bda7b1dee47e24ac3b7a296bf98ed5682c14de6c5cbb86405d41efe66b1d8e4bc501f4ed98a0f79fbd3da5311afb266c32dbfa7e567713e8ed2dab354537875cd934e7e05d98b10077e0184e094e848e7ad7d089f9257e11da5dc2d363031d462131cb57414feb2f24b727b7b8a263e8e3c6ddfa40e0a72b4cc0d3e0dd9ea6932c756fd4225a10b5fca2848c778412949233736beb3f6b50dc571b86d49ba4ab0ae588ed390d37a3f1779c7776c48f9bc438c50291544fb02fdf3c4eae66f416bee6d59579c5ab98809ba7440c15c36238aa0315d232d4ca2d78dcb76effda081063eb712a08dc87029f385e04c5a50317b14e6bcf5099e93ffa846b251b52ebff839db862252d0aab4112cb24dd8449bc0cc6f97f123b5a0daa0099b01390039d71331d0ad72818e3a56db3fffc332171668383ceb91a81888cb7f7dadc76c89a7188d3fe72a04c1d4fd178606fb99265af9b43bf9c096f12d0410f61e0ec7ba26b5252986824a8a5fe91cbabe4e2b5a752569951b4dfcce5b5843087026fb934d2a582a0920e7a84f2430a8da7990b9c059a5811e654a30702454b3957e05c31268e70733df2b67163a6e59bb471357cea7d4a71a10d7f8941a1424b0de024b8461689c055a3bb2dc4221e0789d2fecd59ad6d8bc8a23dc741272451bdad84b26f233a7abb50cb73119075bada038f63a167d8f3821f5cc6895a1eae1a997c8e10290ae8667303f33124709b74cc19879c9d80d3b0ca98b65354d28ded24fa996199047d9c2ae0e95c1d08fe286fbb62f7ae34e94d77881488de2839f1d6f4955cd22d7a426ac0162883a5e6a0493c9caedd3785df74d2e3a21ad822f37868eb9eac03bfa819fac61dd4af9b03a6a03b582d520fbe4587bd6cc512e317cc288457b1a1b2689217170a32ac472212e82d6ef3e20d5a89047af3b4f308f44604983b925b089b94315c1edd220e3aabcd45c0341d94cf1da059f0c7827aefd15f9d8ed868dc18327754231a0dfe8aeba9a62987721549116bcbcccf665603e5b43cca7279ce6a83bf660eb7fbdb0d4a9303d1885d2573571ed8c7057ffdc46d37744d113f369070722181d36305fe5a1c45fe3f0e3bda99a59788459005029617564b10fc357ffc591224f19e642c1ab1998c5292413dfe1a572fb4f00483be1a21157f7a4e676efcd5ed29d3c92a6a6e1dbdf9ab2c4b9803d3084600df8a52d6335cb9a8dc1fad8d243f7aa7ae2bd55cdef8365ee7f5b37c3474862fa6882a5ee3bab067cfb2842c8f54d674f824d28036f48282af00e0cfc2399785f3c41142008f29db2f1d0e2cb2c74ad930e783328e0564046254e6fbdd7952782f3a75605a10d3a52d27938033990fc1702dbd069bc8b21cfae0d48c0742a9395b4b2374b271e0076e3f28d418e16556ad49fd455339936682e67b8d660eb804892ac33538c3f005920859920d290191e881c37d35649511cf4f92a62b97127f6ccc247cf938b83e504b8476423e822035c1a018da90cb906d97dac061c706d4b0fa75c42a73ad7dcb4236a4661ea65bbf2ef41864252c3900e80a47865c465894d3a09ac5044c3e1991ba601303f9c4e02b27558224613b11bc8531708de288d6d739829da6f2b5a0c564a887a00d24e40fb9fd23c0d6a8592892a8453a2c225f1964c3eeba098f1950547ac1cb17cb5806dc62c0c41259e252d187d5f4d5c09684d965dbf56d46b7f302f3a49f99164ea04e01b9eccbffa30891ffed7894ae8a50ade354a3b8e5ff7973a520161d249c1ddcb0e5b2495c59d6ebceb477b6b59dcc714f0a8f3c30aff10e82a1a25c1fb7b9dd9c5c7d8f5b3359f1c5c1a7140056bc2a1eb6f0493fda425896559473acd45817a8d4452cb705516659a9354111afe0e9b052830b57b9e0d7e3b4c8443343c31d714779ee599668e25a8fab27ca2cf6f9a2be6df10824118dd799a89830f31820631847fb28adec3c9260167250a9fc60e452ce924ad920d6a4f093f7713538b123d6da307a9609a2067815ca5eb0eb4fe3e94fe3938461be22ca973860ef856e9c725a4e79b99892fa13970ed17902a59a8da722e6d9b2e15d27572bed868247eb2529b7ed98552c8044d6035376aa92d534faae6dc3d3092798fc2232903421df51bbff827e0b4f78f4a8e1e963d064cdc2da875ffb45280bc4829c9e287199574fcbe648e12377217b25b3028557b9810d6461888285c149be703e936588eef99ed0b6dd2b325829b5615f35858497e6318fabf34f18a9ad4b097bb6ee9bc22657d28cbb914bf66904c11fafe75896e98957d3d2c931c86035743a51e0cc33fc37bc76fccb6ac1a4ba8f31b2c2231e1311f1563b16df6e9e7294a4fa7328eb864fa46ac15e5dce233ff22161b361d66886b6789479dc302bc3ffd9b129e5d398f6a4498182f9a5246493d1d18c51fadcc6dda2ed93811db5c684b9a04f1f688c5e498b62d1e638fd0f42d22dc9d8ad6151061a6750f5c349889e8dd3d26884812eb5859a95d4e3c3e359fba43cf0ae145ead09ad59d8d2b30f21df62ed82517dece5b676338632ac576abacff077a50596358915cecb8d9b8843d4e13a9ba53b5c1523a97553412f127cce469f8cfb2227d3e40a0a7a7e2ebf224e242cf100714607449cde5c0b08cc1035e33e1e16af4b28f275194698530f9c9a1937bea03952a25cad9a605b0457a25ab5483f71037396a2614a79948c2199e26486135934aa1134235088494e671a5a49f44174f647a56e93675c96a71d29719bb42212d4d2749155e05c78cbbdb2cb2527e13f6105ce95ab1cd5610af94daf76e54b266e43e7b597bbb96e16624e51a1741f5276892ba283010c125a6596585738755d1c64308940a65d8541207908000b6bb3006d0e50174d14086a263dce8dbd4469379daac2273a29542dc32577f0cbd919a4dc6ab6cd151fc45f0c82f30afc3bb12ec428636679bec8adbff57edc375f5d8211fa15274ee45066f325253d41577cccbf648c3fb60f7d53e8cc3b70dfbdf2edfdea11bd11b96e217a6bd91621f4aa6678986b948e9ffe7de5557d4ef6b6eb8470c1f0ef43db1b6c955a992d77168bf2d292a512144fa59e1c6ad3b2f81e129270d4bc4ab6705dda736df89654380c2af1a586be23dcf5bdbc8ee81d47a7b4af092046fb9c854e815b412ae625101ac9629b480b7597022ea8903d7454e724e332b0d7099741f10f23fa6ee5296a3962b00b0b1211654942ddbf289198a32c71d8d8c8b4ab4a404b1b5280015bff56dd588fff50416a5f45704b648abffd541644e6cdeb5959b462045ce38c1e94693b9dd2fc60a5420e16c85332ff7234993aeb3ee47a1cc2a95cf416500764f520060b9e91f477310b3c0a3da56b474943bf4d3f98fc68faae5732daf19228140000d48f38948fa21a05c36cbfc5555455eba7ab02ad792fee9e48b3ad3db6c2a2c8d6f0d405310c1f49fcfd22b2ebeef533fc0b0589bc30767fae83a835fa09a281adc072578cda1ce38ed0c1b9193750c33278b202f0236a61c13277b01cdabbf1655ee9e063b14f2282f885a2fd47fee865f266a939df1e75f64868235bb4661d2c6c5f48b7aecd565953cf45bfa737738daae583f1a90b6be50c4369e85b25175cc6bf4dc620471de0823d85ca1c5d1d6c1f5602f79ddd49d1864b1c48498b672ef32720942120942e9adadf06a70eb127eb28a141d78bc9de524ef0cdade3e264912b968bec88cf77a373da423537479af0605325b0d76022e51a26e6dfb988ac96e7454b3bc4eb347ceafc13a46196e616d0131711131da20d2eeacf47f08dca2d130c120395711b488a3290fb7c013b890e61cb18563172482e34923047ec48ce8d07ad79fef15bf3b2069ea00edbe74ae95674b8afcc48dd4516b967be5b372f1ca3e1db2f9917f977301eb6a7e3f9ba255b066bed53c1dc7aa66bb1ee3213e27bdaf8889f24a3753000f7a654c2ec8169f643dabfbe76651bbf211cd4ff82a2cfb4796e21c07a1efae07cb527ccad21a814fc935c9b0f6bb76ee2fd67681803440f05f9ec00af2cefdba9dd0cf108ac6c96833aa531f3e54c1e964e9519081b4931ed73094aac6eb31ab46e27525561813060da18ca9bb066b1bd28246a772c4d659c846903c1dfc88e1d610c508e751cd6fc11d38e99b4db6e740418a98d54660822a60d7b7012d11d6c1f2220a79630edc3cadbcb86024fd7b6ad73900c0e3cfe20a6fd5c363bb0a427ebc969176ee5a38b6b91076e0f2671936b05d5f5ef464e586e6647df90befca0c22f2f0b9d3641cdd6768a11fc399ed1e075d058e874c64d551b6f55c074a18c1cb5f76a32633883d83ab6789d9bdeaabda75fcb221db670d506169b08d8b841ee41936111ccd5c280e5202bed4bff64630205e4e0d3d9dc238c2d76a31347efcb3fcfb569e5051670dc29b8c3b9b013096bcb34c0aa39e039fac5832ded03435122034e64a184cae662315777e005c4abe03984426981bde9c4ce21b8dffbf678dfed7bff3eefd3f7dc88837d5a440190f4e7105996bd562668c7adb9a190717b92884f0302d4b265d2d8b31fd1fa094f4066d6a94a516071d9c023e50429d157ec47cc00a350c584445dd773d4d7808343bd8be1d6a9ab7b9f72185c1db20698f70fb176bb2b52ad427d9ed0fa4d43461c538ed3892b517a1470dc735e7c159244aad567670e6a08f7c809b0c775496b6dd186d36b30a838bc5925bb61bff9a7561840bdced404dd2e335759c6a28007ba23aa157f2100dcfa9281deca0a4dff29080407e1d4fe227c41e5741befc58e28b3a819882bb7d7e484bf3a63e9fc46b5ba5bb3e2170c9b5a3568e411d7af8bdfbdc3e3e320a78b58c25926c8b02c0b8f6ad3964e93e92ad69b38708d345bf9f0c5c0920ee04859ab2008985ff939268949490fd345cd22cf232a697744dd712681d6c6a59d70087cf9c3fb67a984ab4d77dcbcbeca48de7fb5165ab045296aadff891ac65d81ef334e6a939c3ff32d5abb9a93222e692a4149dd0c2e462db44492b8e708e983a12f8ca97a9f6119344c84fdd12d91c0a62387f2ad7b0fa88baa8c756b9b3896a4146198cc2022c7ed5d234cae197ad45e5828b751039bf5d21de1aa0fd64e3d38f6af836b17273eb2826cf8299254e4aaf65a21b8d31bdbc0dd5c0eef07f6edd3cfb879088461887e0d2aef4cc7aa7cf6935b0f5515e8528f7ea7bbc00c6b7fefa8144cf126458f7281e9c48423c6f42f5d3cdfc3193d2a23ea422cf67ae2c3898db75c4f742dcc9f91c8fe0fba0b468f488e59406d2a7d995b3219b03ce6bffe04117ef90b4f473cdbe87b8ac364332ffd44d2ea57b06aac0022f7ba3489470efb8152b40f5e075952a9de4fb944b874ee6b65e89f5a5702cffcc476192b726aa406def7714e10bf4cfb009d7592dd887866dd9ef2ce21a72aba49ebec231e70aa0d5f09b4128173443da5ff407424ae5dda1b2dc70e5de12ac1b76d0433da77531dc4e694905764c16574b916aae06e4f40cbd1d9c24c9435d169ab75d2ea9ef35cd676804e47f938da6b69daed9b38f1b18d3b3ea3ce4e3fd4ce16e0a818a6ac13651dbcafbb63c92d2c3173834415dc88e785b7a11ec7ed1745449134cffab7a2352154f3ccd2abf30072c36f434936f324d5e90265c8f7f08337a42962ceea3c0f6308aed94a7db1ca8f5625264b17e80947bdc9023354bd1696214a76f03e64d7055cb3b65900acb75e16d5f01063469b4eb66e2c4c5539f7f6f7ebe621961974811621c9bafc1f4d884b4b04bccdd8a4ceadff3c91f52dd833650dd927cf0e14e4d22193fa634dab0a494b4806873cf5de994e8fb22f935db2b028e239de01874096753ce3c8c4d66194609aa646f5a901bac95607bf36346102c555354e2e337b4412a5d4ce2fc51a069bd254c32a7fdda7ead57ed57898440d6aeb8003eb5653a91afd7eaa8c5827780d0299abbac7fa01ee0d5bd67775d6dc09e9c9633f93b59df5c84a178a55e5c1a0dc93b5cbc70b09025cd9d6b1e9aae188c59634b15bc49ac7cb6fd90b5f6b1caeadafac17ddffc0b196fef5c50d3b1284df26c1999db97599ad77a75a6c380094b3b2ccf70647a5c3821e4c28b5bb3d75fbd8b53495cfe83e5fa267a1ee13f9e4d64d40281f9f7e109b27bf8e79f65ad16bee810349f2479d035215684877bb42038107b88c08001964ec64b52ca1111a276643c0ef2217e9552d77c6aba34be04a06c2a6c6978303470e1d1f7474fcd8b1c3a30e0e1e7574f0d8a1c3a30ec859c824e76a28dd108cf4c390ecffa972604d596bdccadc0603cdc309f9f3f71fd78889d36a16f81f796d0e409faf176c7392b7c77cf28c159cc7fde02d4139a2073a4b1d1b4b11d3201ae7bb7c3d354700a938e8f8c804d469fef96971f573d8f9b54806af1b7b95bbbf003880db8b64b7ba3921072a9838aab50588b425c37473ec8e6d68f29b7a0c685b70033ed3a30cb6c6d4fc160eb0cd1d6829764e86d5b681edcb850fd1c37302ff96418c126155e51ec37eb6f3a940b23dedb7c167564d55d70615167f6283b5c4b6f36da41570bba7360bdef64b4cde8abaa0d6d3eb44cbde8971303ff09f67fe9f660f4c890733249fdd0f46b34bed505307549df091d7b908edc1a94f418b55053d3add81738a443df08f7904e138b66153e79a17400799048d3ddcd1e019afcce43b2fc1628c57509cce3fedf8504924104b3236f2e0d5d0381056fc2cc6b8f554a27f686877e5ad9434af1836c5817013dfb0c36edb9cefde21dd3ac78620ac66a273410813d3041dfb2475be2859a01ceecaf559d7a08833a20f23a5c7dcc9fe2242045eb06275fd6f6e5ec11a5e2fb7c19e29c6c0c9c30e9fe94a9ba4dec96acf1e87104dc54c8fc8e5d61cf13a7c2c95a4f4900cf0a951d0cab657a0fd2d67d153b2135ce0c018cc496ffcd4559b35e2f78884800055a6ee0efa1c268727db93c32e86d5549341da6212289bb800cd0850a6158a5b71d80712c8947b5419768e87d45b4af4ce690361e2ca029c2112dd3547cd125970c751c1202d1eb2726019b27e8979948800fb0b8280996039c61d6d3782135a0995f6fc19059ca84834acedf7d43bd0932edadcf660a31a4b8789d85568c81222098d0d6e4a4e8a6efafc290000b8cd40ca8e33c76a7c6e25f70433e4836120a2c90f1965adf8bac4c05a0ef95f68ec336097263ad0125b26ef3a2ccf881ce1eea2feb3825157eb0dcc1539c31557dfc2c4bee53b4087b5d9680a2decf713d59abfbe7a3bbc856861ad1b5213eea09a81b19a6ab1c9466b4ed4454de0e135b486d4913262f32c5fdf93ae101fc2fc4ab3e6227c1eaf8684141a2595303003ec6d2c517b7480e6117c3bfd3a2492be2412ed94fe9c1ae334deef1e632057ac017ab2393b8c405a3f9ed6c0fab66578a014ea4b9a3bd6da2a3a3bd3cbeebdded32b13f47733ad5bf448bf922094c9f94e743737d95352c5c8f2ec3bcb48292453579689ba34bfc418ebb6ec4fe348ab75d9af9d7e53620a32d637a7fa0376ce26ba3f2b72e3ce40531d5b8ed4e0464c42ed261f81e4cb26f450f4c52a35317b51ddeb0b81f814fb6179449c0a9ed91873ee9dd41f6ee2b7d326d4b058b59c513ba9127464591b3b61191a67d845e32c051c08d85b0224e96f0398a22fca6fa3579af550f5219bd044cf29d37f863f99f96c759c6a4cd347b220a34a1e9f8a1e14349846096af41ae52c344db2400124584300d0da3198b3ef5f73d6d4751c6aa544722590f2aead95e1b06da56a624e9761e6f05ad769b0c4916935fdcd679b4e0e5ee7bc2b46826bd7a067c74cf2016aa1a52b2898d550f17a5e4333073c09a4b662b598e1a320fe444f15d530508f15a5e6f33584345d1a5ebce2c5296b86cc5d1a6631d028eb16ab71165d2dbe0c727d4b45261534a4aa4db68a63d8f5d4a500a94bc332c8671e535c85fc886c1aec50f48ae69b8aa21e254139d93f83284a528a4205321083d2a561790a4fcd6d52a22520c198ce059e895643b84f40144c11db08c067d448f2c2b4cf8cd097fe77629be2ee734b8715822d0e883ddb171d5bff72e82415f6aae58bad854dfcb5138956e0c4b07e86f741fdfb88a47855a7452a35126195fb83223a7681f8bbfcae2085e1f751591006c1c21fc1807230253ab628732901ce883278acfc23a2f4678ed4771db0eabc93da4f824eb9c9c8454bc34a513271841df63d51b8a0ce060a664da38f68a713a44b6e55192e03d72821293f1954054163094905b8beeb0972ec6d1b05f10f71ea62eb7a5cf4e0608827a404a9214ad41973ad480a16ab2d6e0fb1d806719ddd99fdd8f2f103379f14fa7dce35e810b6587b4dd4cf560a5984660e70ce052b2e995392bd2030c15708828a9a4c8dcace7d66ef4d933dc08c1152d2c3201e9ce19d957f29b795981c54a5d2c34883a803e259cb6068f45cb4c963b27f4b0f0fe4341fcaa8047f439872d93874a1689b6e31966b8bfff182d07ab594327e2a4597e84efac093d268e457c65a57ece89f5289e25e468f64d62a2a62564894f86bec576272918eee50f975363c6f93fed081766d9e37a5f4f3bab6a7e4b50a442f84471b336546fd801383c210a236e3fd040b5563d1ae129b98b8f9a74cde545a3938d318460202e89ebbc17f6c401007c7a50850c82b9ecd221232e4ffe6cd92b7b4840a9a401822af9b93a728389f988940e21470b10d7e3a2d40d4c7d4e0f4d1af44d966f757f3a4ea06606859b21b9589fbed608bf37aaea8b007cb9958c87a38053ccb6c171a9da752c836af5674bd72e194773bb0b05239d0716b2fe49dea0ef0de3492618d18d65aa248cfc5f4100b1f8a0a8a00dc436fe7c24a676a740ddbdb0092c5c9ac8ba0ea3b84196f94b5d6c9cdbafc73200f36c4a4620137f9b99386b3ea6773055d66b127e42943705231ff535e0e3e7950e285a2af601cd56b7f7466d0f58905b0d334a93cf20984a3897ace509ff257c6c5870efecf869f95ed8f73889de3539db9a1cec552ce60aa7f90c7c6319200c6e473560e99d57d35abb05d796926464ddaaceba201b6b12e9c21e6208674b4db4de5c2635bd0860e6362b34540ddccc5d2fc8d0dc9e5d5998b4b293d1092f1536d79f64e47b3f5b5b8c46431b6fa4f33a699f3e8a293dee177a7ca44c5a415b35883855b1b02e9e1486234dfc793e7886775b6d1c35db1b4237b8f23b18587c61c997e0bfa4f183266de02d58235acc884523f06f5ffce13151650970ada9f493b8b212b745e19b2d45da9c1910958fb66e462f3d00ecf699b767f10251e2b702098a71b6b7a00cf8e30a3d4ced30c824d761d2eb758a9337759236fa774c30da95c88194fd51bbcaadb782b77c53a6b519eee6beae266cd3b327c9f0696db5c216065ac081cda9aeee84c524cd762b7b85b9d3bc7872b77070eb8893c8866cce8c8738a7287644b62650b7619db4c06f48d940e30c9b428945965e233310937dd2c6c2019b8d57262503543ac9f9142a25e143c5f219cc56eb70d80bf63b08b3cbf9b79001dc9b7abe9e4f51dfb08da07ec99404daa5a4f70b963b745d96dddcfeff4aa6250d6181eb835c7b88c734de58896cb2f124db0e27d45c62c4ba33a9010b1d2e10979a141aa5cd04cffe1513a7d9411ba00cc3308927485b7bf760cd99fa964afcf0d0244521f21780001c14613d2b45c8cc3a6969e235d510693bfbd972676d3f10d817b9f587c1be8214f96402861d9237a4937d4b33cb5dedf76ebe2c16fd599063f718c1d8f9ef3745671489acf8b24f4a66098cd10e9509a4aec897f1471019466a974d2d85a08f47ced0748af8cbf03569280f5d8727c42caa858189aa71c32b67eb4fd9cf78c4301276623ae83045d6cd1f3c3b1e07b5496b319c90cab20b6a01f22426958bd5d44e14eae3c88b797d872f57a5647d2b98760a7fb55d5613637b6aa16f1415f4b4006737c997cec8b04067e00ea03b4aac50e76a55123f2d55609d8db39b6397c9f5cb8bba9a7001ed6b55c7f3d24c87af8ec46f70a38a8eab70421b52643528c3637af232d612dacbca6b9640f9ae63357ecf9152b2c88d330dc6101ea7be5c898dd0bb703e8bf8bb4304446da411e912b33fbf3a939b7a61ce12474a161201102ed5c0304a3b15f64217fdf66dfbc0b0513702658e83f0f17672ca5455756745776c99d3177169cbfe6a107ea12850573da982a2b5da50acac2a1429c659f07b11cb76b62dc1e92cb2849fe0e261f7acb8e25aad23bccd32c7b869870a021871498504fa428cce4151ab2b851a61866a9e36abe82c092bce38e0be9bd9a4485225c510792c96262b6ba84594d5be3c4b8a24c60b1f8b9a62a08e9135a18c1b4a56ae01b31813841010360349040ee060db388d5f45cbeac32437c4c3b19257a2826d7121ffedda13697ea577e73bb8ef0ab98f286d4add176347422b057dbd9fb4db3e460131fec163711949f091679d94ec0854d78c8adb90e33150184a35043f73357f16d636503e81b8c9aab2b3db09f29e41f3f081f6268f8dc2f60b5ce457840f5d10f627fe06a163283ab59e4421c2b6c5f5e4566ab828b952aac2b1579ac6274ee822d9643440e9586a22ca1f8bc76102556b05311f628450449406777fd89982217041d44abac287a627d71eb44a97144215426b8cf7f59c8d1ea842d8722f21a4ee16d371eef36d2aa0ecf6c1e47d2517ad9fd8fa3a658e6ceb76d5a33a4b73c4ab8c6c91482b42101faf9d984036c5570e291e42fd0353a75ed3c1a44edc45c788d993e4fe747d51ff2f858bc6c610bf275e63774295f208019ded05182c570f6ba115b0cf011b93dee452fa96005e3d57135d72efcdc021264c19a53008b3887bacbce2c49cea39d70ca6a08dfb8bf48589d6bca93a5b12bb4b9eaeba8ea3b05aa45578569a6bccd6f66ba7455de61c69962faf947dd69273f9f1c69b14ffa6c93269b7d21190415d161fa2047c15072a16929e3535d6afeccc9ee6d773e95afb0d79da1220614abb0675df431ac72bb4cf37b5eac2c779c0646cc3b8957bca10185ddab63180a8798b64f3b817e2ff2df92ddd8cd9a7bb0506901ddfa887668c5191578d675256f73395991225126b651fd587f99c061e41dc2b7231377c37f076a8c0374102a1b78d4d882607d1ed45f8d4509fc3602004b724dc273b47c03d71c6ac9b8de203db4890e822238e372847fbec42c7f149e9ddb0e5bbaa76e92f1cc2318636b2a9c2da69c00bfc0ab2cdbc8c2849f59f03e6104c3f7b80ca6395039d652b840eb69d2af613268d91252085596393d122dda7b3ce167401f8380ed18aff8da6aeb0c748b5ee75a1bda1e14104791347d0abef308ea850ed1ef5b1b2190c1f05d61acb62421f43b040dd3770fa569acb472034a82967f5c40941da70d0e7da867cd8e79cca79c348fe1448b2d069ba0008b1fe8fc5ac0f5b381b323d8478de064b8c37d8598477110c91c446dbbe726179605d6aab2022eea1c4e35b1d6ceda2d3639d3f283fe9b99d94d24721389eceeeede01370823089f073487e6bc9eb335a5e0d9ba66ebce969dad3a5b06c80638c001103004012e12c07a9180172f64842fe84f7c61baf107f57f3aed1de3c79cebb9cb6d593fa7c7c72007c1e4a8909873a914132323f3c3511b7f240c9a24a6e22f51d2044888c6d6954bd3d09d6ef4a709ddab4d065af8c5788374f835e20b9ac783e4075a1774f834aa6ea625c3c2850c3933d8950a90cee5528c0c0b17b92587584137737ec01595c14bc103aa31e891d2ea8fca8a5351b1e9dc9e52c59d62a5c31fab33c506e67b3ae55c3f12de57da44c88025e2747ad414156c2076141f4851440a2152fc90e226ca14517c446945018af213a58b1284283ea0484841c66006077cc41286c832c413569d068ad091810c6c6670031d4ea7474521a28909a0184187214bf41cd18a22074620c2942b186912854da784472f0786608207258c50c509ddcb0ca8f460052e10f20108c94d72bd2a3c47e8f6e70c0eb7852397b4d6566b6d10d65ef32ffe6230ac5a5b6bc6e3b2b108fc17dee27603dc3123c4d27a39c717718b1140912d1f15644b89653b589dcbf17e3a64b2bdcf5c003adb7e7d39a7d470521a55d7dcc7146e54d7c87f0ff5eec3877bdbafc59d41e5f3d7e23eae796a09dcf0e0a169c0d7423e7c2dee0c7c61185f1746c38c4cc99fc1ae0b6a1ab29ec09329081fc36aeedfd7e272aa7e1f630d2f1ae0639de3d67a0257a6ee475d0361bcd905ff7a8cf1f597a631b16b31ec311d1d63d8c38e6d9c8e7e6df7b5b01b8ef9f32f9fefbd7865757bf4318d9c9bc4a8809f0c175b5da282ec1e28aef4d703058f01faeb8162d57b2210bca3d78ddbb1e247fce545fcf49bbbbcc65b3ee3a6d7bc645bfa7cfd1cece9f2358f41e614000f764e0c72b70864a84b2e02812912bc18b8546fa137ce42a07212898ffe858f465cf42f5cf4272e12f1fdfb5df8fe16ae6daefd0bd7fec4b577e1dab7704d7b4cf30a9efd0bcffec4b377e1d9b7f0ec4d3ccb1ed7bc82eb7fe1fa4f5cbf0bd7dfc2f59bb8fe12d73f39b637cb7ffd0a1eff85c73ff1f82e3c7e0b8f6fe2f14b3c9278fc118f2fe2f1378faff1f8198faff98687b8abb37a3cc5179493b1e1b588dddaaf5fb7d7630c42790492f35a482034326607c02c9db3300ea89dbbdb7300c551b75b04ccbf0eb3cd2db6ee14f04305d9f0317d4ae964a192ec5a6badb5d65a6badb556fa04c6a820fb955041a20e1251188912248a28ae441145141f9789095080de2823adb51ab153b0af9ef844c783adbbd6255bc8c601c8095b89dd832d53c5be74ca080548e7503f4e600ef688511953f7b5e0a81f36fc1db4c7b8edd782f38e838afa9c74f6de120200f4baa2d753af2ebd65011d5f177ed1e9764d9d9b7326e20ea1ae0142d95fe858c618a1c419fcad3f4d23da9b09d0af00fa95e937f200747ce5c8b78eb11cf9f3e9184b29e3c338938f44b79a06e61ddf1cef7db65e74de1e77cc58abb8f6b2f6dafb89101ef7c950eb5efb640538d7dd68224ac6131c20ee131c76b055702a141f6cadb5d62b586b6dbd4e96586b6de54e43d84e88b841e5309fc40093f264054f709ee0e0a48a13284eaee06489139e798b38b901bc9c10d1e1632f93c40ab3db29a262ae4d093e5c4e743e5c9ddd821e55082a9df0b1c3bcf7de5b7bc4a85badf114515dad486c177a6cee843921c5c90f4e6e3aa7c22c7808a305a6e273282cfffb990dc2d8d75f8c6bbc2ea573523a679d3493dfe3c6c08ff11b9c9918d8cfe48bf94d8f9918796ff92f543411c2003f73157ea1c23a8639628927a619db2be516ffda12830f67f3e3c9d431bfc9417dfc2d104685a9b7f231e62f06ae10c68329aec214e6d8f750c5c0af5f4688ba1f5f755dfad210c0e9397156b7d356f9c4a37e71fe5a60fe17e657a676aed7b535b8cbedbe16bc06f7893bd54491e3783f9b08fcc570ffbd6a2642055cff70a9677e63ffba9f318731eec52dc53cc690a59b1e1fc3f410f0341f75cb9f3f18e2571e80878aaf3387f1529101f7de7925dfddf2f95abc1830bf62dc183606be19f31a035f19731a036319f31903e38cb98c81b3cd5856455df32b535b7319036359f3180363f8229ed9d44a3115a36768199f9e59d635c731f249eb0c6b78c317f7cce663be652adedcab86d98bd46fbc3350125f0a52caf85290194096240690a505107490d849402544278014e1840c6a0ba8104ef870a2475015579094fe7a829a042511f4b3b11954a4c39f49e0010c3d61e2c9139efcf4d7f3a47b3284273e78c27a62832742a840c1e95c7e62e889a026fdf53c91a467c627ba278cf4d7f30491cef1fe9e20e28920b3e7091fb9bf9e276c2eed116a01c12af42397908fb04eb0a811ee6646ccdca04e88487f3d4241fa13fa21c483ca0912219b0e3fcf1ae001484101fc0a1cc1086bed1164708bb044cce090841ed6367144ca0a360f7b2341060bed41a7a78921b909204df8904c5ce95cce9309a1cee54c6f0e3cfba7634287891decb0f7e999a8020f1157b07131050f5700f2039ccdbd0e08bb87dd84c80d145408eaf03354d2b9dbd3e4486fa2d3b9d984959b10e1018fcded28ec17ec9cbdc40f8fbd4fdf444814f04928e5841d6e2b5802099c258a007197c8c10e76891ae0d4255ce0035d02053ccc2570789041d8dc89091437354e8f5ac2a773aa1f3cc445026645a2cd81bc3598d805140575091c3b24c5bd9840d1e107713101ea21677250b960a20427e652dea51f5bc564083ac8e430af995c8a61c244e764980041264a60708449c764085307263ae820a512567890c94109272b991c9468d2432687c9c40826423aa7fab136413f8284f4b7e988f09f0b2ff41bfbb83e9e2fc6964c3eac09800fb71c5190457fa82e75cd8a2ee78c2f52fdcde7808cb20818b02b16814989e1d333bc23badc31156501fac5b10c43af5bdefc1453eb43082194158818f22dc1a21d6c265b088d279b921131278f81524a0971806928689c1c04ec04bf80520e23a594d0ca8521b9b05c465c2bd4ba024a2965ecb977658fa851ace52f502691406ca541927b1f6ca2521ed7e32e239b2ea56c61bd163c6cae85e562d3e58f6cdee5a70520fc64b8979f1e3f4339a9a4d5766f567b2f5a5bd4de0baba4959d93d4d90bc359d7b47408217cf04d086bbdaebf6c707a7c7ce3a3c7cf38fac768155171dae89a964e593a458ce8b0783a564445ac6b5af42868d46424340a8223206e2465d41a01c115901f4180ac84e4ace0cfb8ba233c488e74939484f443ea48432429a42ba42152ebc686d482314a59f135e17efdf58bbf79b3bd5cb2ee51be254a00bd18300e240afa7bb97a3160ef63d703a368c27b98f362b0ef25f062a8ff7e70da810b1317215a784c534c4494a0f056cee75e6ce076da418f5f5d98f4f854ba08d1e35f2d98698a3411d1e3c7aaba2528f4f873cab971f187c562b1582c1b1b1b1b1b9b2d1a8d48a492c9c65caf4eca42df418f9bc91642e3c9a66444c4a738e68876b0996c217afcaaf1c46c4a8f0f332268cc19c2d2e98e745dd775dd8ac562b158ac6e6a792ef9d5d7e1eb138382c6c941c04ef00b20d6d250e8f1a5c6e9f1630e02ec143bc12f680d459c5504d2b174464023a011d00868043402ca301796cb082b402b3dfe053b0c498fbf2f2c3dbe7619d1e3c36b85eec88f12a0a0aeebbaae5bad56abd58a85953da246893d17ae32fb9c3da2c77fb15b8dd2235c412030070e61e9745dd7751d005e3a0d7a4c02ed03aa34a83449d06deda193cda3482d0963e672a416a9c5915aa4568fa552a9542a954aa5cb4336513955a73c240c95cce95847744a5d7f4a8e7497fe62f3831effbdd8bc16382f362f3f162022cbf60128065530c2922152a8c26eb293d8568cb022858a1fa8b8992c0814e54ac66eb682a03027882036f77e8208620a2b4c5ad1850c8f07090b5aa00e9f664686850b192490952aa0543132568e58292287583273a4d0a232b0a224c7ca112b3c3f568a58e1811523ac11308a09d5d62a63ada86085552b3df870d52d8b0fd7891e75097a97ec304f5f6b95246aad35d62a4c389d1e65a506476c803d0410f6eb5e9373ef47da802a55979a86193a43d77bd1c898aea181d5ed3d94a5a1c6e27c71a7095c33301de1bb99bdead38828b9ebb6b19c75d635d7c62570c38307ce72d63466d84b84618c31d6b49daf199686ac6b6cd77ac6484f406730cbb22cdbbae6befd7d4539db39c3d2a0e91adbed6352defa224d4ed788a0482412895ebc225d63ffb29b0d7094354d03f7b7358df89ce85a116c51e957986f301b37aa288434db37739fc518c6d9f21afb18d6ba66e62cb3f7b1d7b48dbb61363466ad66b1b53b3aceb2c62ed6b4ae81dc853dd6a2a6699a065fa66bf463f75e36748dc63086bdc52eb616db70cded36c02c7673b063fd980d8d695d53f7ab41b4335d4345f32a6198c90449259dffaa91589bf6ceb0c759cbe9d4b2b7a66b36dc7beffde2cdb0a789bae67acca4f36bad35766d3470c7362cd23551943551c6788bae61941f8f3286bd0b865d74b6e5c725582a954aa5174dba06fb9cbf45eb17692c5ff9adde68e09e47ba066253b6bc067b8ca44bd796f48661e6ef18c18089c9924072d55fec72de4f24c211ec639c05c708a58c394f4cb58d6b397e7a06be3e4e5d03b517d8fbb2183f4d9458d6dc1fbf3fd23e7b1ea2df6f350c0dda67bf6b106d4fa644f56a1b10565db3ed65b509689f3da66bd0b62753d9cce535da46be2e5d932fa66b4cda0c797baf05de6a4abce675fc99aef13ade2e8b3a625f7f1f6b1ca17e12cbf45b1b60961932ec78e374746c83b181751dbdbe9c6f39cc42e7664362111b614505567aa892a5ca1455a0a8e2449525aa2ca99244152654e1e9afa74a912a43aacc40a84a0b5045180a1a3a32c4d35fcf5091a19ca120fdf50cf918ead1a7109ae2a783952589d8295653f4c05d2a429dcb41fdf5505142a5099d4a11d4109c1852326483a19c29ae4c013445478429727ea0724508547858a04ea51d2353639f9eca902a43439d43fd4881e5440cc8911703fc4d81c4eeb58826b9e71681503f983c3047ec225c0005203c58c1ce8f246c296c21f6142b5462107444147a8c00bac18e4f0a499418f0a0040f780881406670b877269762a620e92d2c59c20f8c39b125bb0957f401a182362a68a382f6a342d806c629279d94c628e7a474d6a129859b57e610bda136d89cb4da2924e7ba8ef0203972716e26f9b1f6ce0ba31363fc18736e02993f824c20dc14327330ce5367746a414d8482a4b4565dab6f3d634ed14d44451f3530ea1ac8c91f3d7e49ae4cd36492119af2bffc38c31c753af658e63cb1974038ce7a61ad8d4a2d2f30bc249aa8d84448fe534522aa584465441579dec79cf802c3b87125de441f3ffa030ac21361c0d50a2259c1242bb864d5040af5f7b0833f220cd8c12050089122fd19e9ef371061c0964cc5bf425bcae9ca4d7f0f5737e28bfa4b3ec909c97e3c2c5ee614b35d44183107c3ab9c73cef989b8dd4fff504e167ed1d6322df165ea78baaeeb4aab1fabd56a458a2f6a130d248130d0057431168bc5a2363637363636362c5ab80104d4ad72ce396795aad4552a15964097966959ba2426753c9dec70d78d56125f57c462c9222c9664499664b510800ca9c3d56ab55a7518638cf10dee6ea04baeae96e92c45f7da1d4fd7755787573f56abd56a2581641309248124900492400578215561d7755d775dd7755dabd56ab5bab1b33a4bacd97b753c5dd775ddeac76ab55aad6068015bad39e79c1376dced70d7d9567d9d25beb27a61cec43a9eaeebba4e9561c8a822dbe6989393b38160ce39e79cb0d56aedde92c2c156154273021873767b17fbcffe73f91964ffb93c8d74913f2de7647781efbd8bcb5b2b7fbafcdce47cefed5b5b5ddedad3cc4c6df9fad265e3a0c9daef16656d7679ec5970f91897877171f917973ff1962dc74c376ddc8e0eefee769fba3d3dc6ec35a194724bbc8448fb4fcaadee38817c388364afd05ed892564a7925e6f2b2e54ba3df9fc5f02761f87de14fde1b4d1b77491bb7e529ca8fa82e519153f58dcb31232f39a14c28138a295a1bec20b4034f9c57d61d8af401f4e7b3838f2b1d01fdf9f438d2f1c3f3c9279f7cf2ce2be943fa66ecee8cfb4fbffb77e3f4cb7ff9afdf51f4f6476fbfb4713adebea66d1c34d2b5ad6ddcc5566f3a66b7cfd59779cb91bbc51ed71a6e879c7f3d9783bec401dfda806fe14fb995b458794cb1153b1229c622305e9e833a39eec0cf7ec61731fe7d3d2366773fe418c3e26357fb1b7abca418e167f7331e9dc59d58fa98695f8a3cc64d478cd88643bff6d8bdf672368b39c6a80315101f8b3cf6e62c5aecede3e774448e8b5db7ff39e6fbf7deec332ec7cbc7871a02da76a37e0c6e30dadbc7628c51e7665967b1c7a6871b0e3843dcc1e2cd6132c1bd372ec7ddb0b883c596a6336b37c88aa8b8d92ff1fd2fbce52577f9c94f4fe2793f4d14bd08c2b8331f6ef873e49e618f6139722612dd1c39ff89bb704ec7dcbc85cf1c39eec49db81377762429b9f1d1bf3f9f1c3a1dae74557f3e3a20e93e39f8e020a4d573a71faf4f0f1d9f1e363e3c7e7c780cf1e101d471d777922a8434325e4f35bcae3a2f1d71e366ab5f2fb3e1a0b5d65a6bb5119fbb6810a4412e80582891f9e933c298d9b44cd7705a21cf30ce3f00bcfecc3f005878987f2bfec5e54d2f358dab67ffbe859815a716ba698c3e26441f9b5770d8bfa1d300c0d7f13537a5740e6201d870e8b7320f0340f5dbf8d4851964362e76fb2e70b3c5b970798e850c17d369a9d319659e6e18e7e05bb8c1b8f0d7d78f1b4d844304f05943a55ef4c28f08f01ba53304c517953ec631ca3feb19ea6f4f808d53bd909afb51031000fceb6bbc281669dcb810ecc01b8b4e7f445fb435ba5d9f7f064d28399c431758d753fa57d7f0c768a40ccf3e005c3fe7af6f9c73ce39e7af074514fd99df0030d3c2c6c2a9cf978d7b978d4399364e557a4a6189f419697e9fa739efddfb74fa47a15417e67a6ca6939e44fa80840187c8d4fc39da38154ab471a8df1bf7a79d376e4f289abf70b373cc74fdf4e91afd382cd533e4ae1f0e9130a612f8d3a78c4eee70e372cce8096506e119180e814486c0211b44c1182718a7a2908a820f0e3f7a01faf3b169f291bbbbca5aebbd353aad99d1a3bff5b19c6d4a5fb437fdeff4a2b61a9d3e8d3b56335a9c588650507ab204f9d824c1e9c962e4c5f4a4e76e3ffda9f42bbe050687f6f8b5cf8f352efacc2ff85916223d7b9720597c54b1eab20a215d4a39fa4bf4d76b35304be6dcccbd7b9f4eff2814a7ea7983116dedafd7fefaec479cdbffbe01a517693cfba723007be96962b665d1e3cf78d510c87f6999966999966999f69caa67da06b31f6f1c4c0dccd235fcd7c6c1ec8d06ccd2b58dabd1afe76ad4e8d773fb6bf654d780bdf49873da8bb6176eb6b6cd50fa6cc3517ad186c365f31c339cb6bd883a7d76f9f7309c9bbde5a57600b4e1b2cd883a6dbfa2a5256793c9943f7b9c99b4cf707eed33537e0d679fb3979797972ccbb22c4341d11e76ed737ef8396b3e363c3e36407aaab8b2376e47e65c8d9eaf4cc9cf2b5a4a2316fe4a182d7ccc8a534be99f54f51cb3c16cdcdd3dfb978ddba78d3bf5ec5d36ee94bd69e3beb471a89e3d49b471aa2cd39f39577f7311c0aefdd43662d71e6a0740edefc6e1d77806758dc663d7360e766dbfccbf376ea6e7a7915aa641433f37d3673adeee7e33356660e236c2c5e39180f7f0be1d64d09843a6d344148432e4b06764f04829a594503ef7786a3ca067f3708fe7069b7b3c3ed8dc7621079b7b3c58966091d26f7f3d5884961840139b9bfdc919213721949dce2f017d518f334e6a821bf7d2e59670c2b9d180fd42de616c70546e9c0bfd71b05b0b6f2f62c61720b001669918f51e4fdb2fdce363ccc404405a710ea4ac177f312ecf7c83a181a3ec72ebb2c7fe3acd98bf6ef2dc2fbfc1308c09905b0ea4084edd3e0874dcfe1e0c3a4e1dbe17f6e7ecf2512fc6c5a98deff37f5efdbab74b2a379aa78cd7567855dcbfc62a05dca491686b99ce18bb202f82ad744a58fa01ee00df60a4773deaf437a39c3f6917258536d65a6bada5d6da3869b5b3da7bdd0bc3139b386b2ccfa927076f26cf6eb2241007c99c70d5e34b12735e922844490c12893c2099968c1494d0953e5f47a783870446907c40468f51c68f3e5fd6803ee65309d0c01fb1c90e204b9ed08e1c6ec7aac78751ca39298c52ce49699db4566befbdecbd2e0cc3386335575d334d574dcb75ef5c45a25c4773724e354d1c8b963c5da8ad9cbba149af9c4472d1e797822ae758f4f92653ae9c93a1cf6fb9a1d5637dd8abb5dc0d405c0ef93492ab9078267802188b112c3afdf5601982050610cb0f2c37fdcad0945782ae2c9957905c3132e9152243e69520a57ac5c7951edca562e864a908a2a2497f3d5424a142095474fb52c1a2a20849058c9ac2dfe981edf2e494524a29a512a614948c409242aac12889d10ed3a4842905252390a4906ad0e5c351125d3e1cedd0e54329390b504a197f39b73bf77986fcfab63d22a583940e606cc4975f13b5f8f85a6b6bad94d239a79432c668447cf1099cfa6163b172df89d8f5ed033d808646c2b81153f2bb1b2f490df145842183a6befce73bca215651b551dc8e34f5c3e666ba4c15b1b977a40212c6fe217992115fccc09b9987f18098922f6bf460d3ed49b9575330bd9ebd36e76e545965d541458f453232918c4c242313c9c8443232918c4c242313891ef6ecb1d1939e54a38b7e74a38bbe728ea68b361ab961cfe4bf165e1faba692a8d78bf31951d6a5cb9f5c0795a25879e0f8385e6b6ba5744e29896c08e50e09a30f7c30b5bc7e89f9d3bbacc81e5fbcf419cf316f9c0e1af50cda5f1ba7835e5996e1fcd9e37ba1b5b15649e99c934a5963c4f9caf7b5b87e6ff7b5c8d78f3887ff7d03626238edafed859b7d5dbf02c5c2850ac5c24597a5d7fee2dc0d0864dced17beb0a67db60166b9d1ef85d6c65a25a5734e2a658d314564e7df40d7b0e8f7630e7b44917e6f2fa246a2df22d2b6471b77a36b9fa5e32be6b5d20c317f6d38627e6f3856fcf598eba09cb6e9a03d265be1b2629b11f5b23d7571696979fd34b145b768ad758bd6da6432f9f0377a2e91467f491bb70130128958d041ff533decedeef73780ba8eb92292900481038492540f288452384b8038f2c3159f1f58dbe7879c95cf0f36393e3e80ecd74200bb28d563b4b7f6fbf75f38b42dd56373356af4fd6fb4477ba77080d001c2878f8fd68c548f548f540f2d0bb6f17b5e1100a06b3034d4a75f43dd6a484d4d83d637658434e3766c2dd6c8786fb456d63a29a574ce2a25d535d2c6d86bca880dfb8d3b64a43bec3d1a8d46db7e1c5f4229eaa29a39ef88d7bc3e7aaa6bbc3e9a5f754decf7f1cca396273d0fd397be6a181a5a9ef435b46c4fa6485f8369137de9eff5afdf1f71d8457f1fc3d1e37ba1b5b15649e99c934a5963ec7424fa4bdb5e44895e7bfb3471c46187b78b36d7ff748dd4648e99ae69d07e1f731a928695a22eff051d86e57c2fb436d62a299d7352296b8c7d669e22821fe6ebc24faf5ced5f4f235fcf91bbadf7b590bfe382d2c9bd7b9f4eaf52c9305ba49717d3a251ceb07cb13b338526940a74e08a36ee06a1fe05659950b2c81def11f093c19104b18b281bbb88b213bf9c8fe5fa62ceadd6d979dd381875fadc68549ce72faa8a1d9f2b9de20e4400c4906faf520e69155bd70b548120e7bbef7ad853c9f03aa9d9ed662956a4ed561adadc03da2829360eeda9e8b5a78937e20bbd472f82223ed2ffb4c83ed4569370aba10218a821bea019061eb552a89d52a6784018882f38889262c31b1be8408e0fe820417c914d2810066dd3e477e0ca8d0d084d28da14d2d90cca28a7b70965e624ede78d5361273b7382d80d96e94fa6a41603ecd7945360bea82a9b53a1a26c1cd7d3cea97afeebef8cd4f7ef3f9d59178db49b0ce62816322816f1457dd44ea1164565d7879d53b188a8281baa248d8cb80361807fd364463e9f9b255dbe0b19e20ec576a3989671d7876047b7a5b4f51282523a5b10ba40b1789953cc2e411c99aa6ff3db6d524a21a594525ae9d35e29acb5ce09a5d2c426a921b64dd160db44548543a921b6ddf256712e0cc29130607742a96ade222ba2ea77b2c22199aa5faf4b6edcd3cd6ef7ee7d3afda350a99d5d9f8343bd72b148afdb0b375be6ec1c08218494e28bc6a967502aa59e412184f8aa15524a131f920fe46c9ba2c1e660912e7f3e8dfc402a67db6d421b45ba9a64739075c11f30e7c92f02e3ed033225ffde5d21abcb4f19d9f22fdf5eb8d9f046cc8361ee76ba9d6ea7dbe976206b3e86cdd0ea5a39c355d71d08216ba76bb1e20e8d3a2f76718752c8a27147731731253f731aedc9449171e7a584524a29e38eb438c5b3ab0868c7511fc346803715cc29249b53a594b065c6af4eafebcaf88c298bb27993aaf802671fef49dbc70f39ea7bc5e85b5e1fd2c0b02e59a064ece28e2e02314a8a8da33eedf07db8e92ffdf9f410d471cc759b50e03fbba1a0d8901b019621c5b3e7d34829e5cfa079efdea7d33f0a35c3cc4fc2b87f5954b7f0b52aa3f063a76b4f2894cea02934832694d9a272064da1d98202619841f36ba47c367cee1d99506c68c43cc8a24e4633fa59cd322ccbb2a8836559962bfd9c5fdbaebe32c3f7b5be1f2f1677e24edc615d754429a5238ee3860ea985a391ad17f6d88e138c4623ece23bdaeec53edfcf581e8d9e26eafc18ff1de59b3129da99bea3fc388ae28e5acca28e19fb1bb1eb6e5c7f3251042d156cd25a6b6ac2b4a66f32ddd776b6666b42796d4d26d34f2193b6d6647a6bf56b5a3399346bd27ee3f4af2841280ac10e6ea697461b77e27e6fd7ea8ced8432a1dc234a7c7a20e2d343101f1e703ad69e4cfe4b428ca9458283c3befe0ddd62183f84f4bea5f557bc70835db46bfb186b8fb1488445222c1261912822f98731d6f06b5624ca44af23c66e526dae1061ed491a765d2adab81060fc131ae978d3f0d3cfb8ced7566ddc8130d42defb8238b37076b5f4a3b31aeb83ec616bff71ebfc7f8a3ce93cfda9f3c18e3d8598c71ce183f97d9cbfe9530486f37aef43adb76d44fe2b76bfd26ad7d66f5efb77b97445bdbeea6f98ba88b34c2361db3ef94f22502ce908bcb4b595d7082703a1c563f407f3e3b5c996fdf9b41cf4e5baf0bb741f6773fa36fa854cd374631646a4600000000d316000028140a07c462491025519c16dd0114000f6e8c4c5e523c14ca834990a3400c42c630840c20040888888cccd046088626532e739645693f3d45ec3175f561dd32dec92c20b81a86fad1ad0bb4acda3585dd40b23e8ecdc49a88dd12fe656b8a2dbe2edbb2a4dedf200bb12c28840d1080e55d3e0c79ce045b1a5ee639c1a60690cb9f60d321ca4d146c7b48ab530aa2ede407a8751fded31e37daa2de87557e6187f1e64f3ac55a2602021fa0dc744fafa03d4daa7579a057f0602f4461c6a7ce09d33bbfe076c82ae314bf97cae95de17125892c2092badbc1ae410f45e34a7d7ef973503b470c8f759d5aecfc78ae1b743271adfdb79e8b6750d483009d5f433f02e6787503fff541756c562a71dc0f414e5afd9d520f7e0f2f3d80454243284935996a1fcda2509a671c1e9bca1813ab9327554b5186d0eaf60c89897688cc73ae8577307eb6ca755e4d481e01ad3ca66a90f11fb1b961c4b3ef3eef8b96dfa9a5036fbd5a7255eb17d988793ddc190fe56e4d68b7b10dd6a4ca1b06ec95557666199c770c0bf06741346e2daee31e1cc8a4ab083f1d3975d9fe2965f1638653e6087e2da6fb29365e22d50a7e9d561431dfbc0133942fe2e8b5ea26c6cacf994f3fc3472465e691d82eb3e9827c217ffaa36700cdd17ebfd09d011296a71d3fb7b9a714bd7f332b574f9cf3e272982ef7041a9688b7ec10bc075f25d166f97b624da8d9a04966d2b3c1264698433437d8954668a609e87d55f188ace997f8e9bc312524eb0d6b8a339d06bcb59caa0588e2915c7124dbaabff556c477bb6d61f259b0d9b4a20acc289b9025e1174e232ac05080c3adbd7e646cf2f3b7f3d9e6d9473e75f36effa8cefa34c7e0edbcefd57c530a477238b029596cfd33c699d164de815a7e0d7b8c68a08bbcf787ee5440ac0fd6cc8a68f43b9688b36060917d955599beae9ee1418df8405e58ca61bc9df2d703a5a8e0b5c3861cf9f7ef3868d3f5f7cb3f98aab4f2bcdcb3895f47536e526f119425f06861b34e82de93f382fbd93bec8d196a3e1cacf2c96694cf0bcb5d036ce15a039ddf0b425423b57808af361abd79c7827d8fbaea2abf3b622b21c41fbb8117c40d23a8778b01f70b2268312de1075fc1228203600fe119160134b9899b48f3cd567de088eb8b6e6b435fe12b6ad615352d8d8744d0d1543eff3f4ee8fb3bfb13080f9963febd54ddc2e2ff98843a67c4372591b84b8ce701f013cb80e49cbc0f8288937df241f30de9d2598b4d83901f23904217751c36b27b8d23704f61fc8ca4516dc85f00798de995256246f4b8a79a0e075e24eb34d6818ee737efd65e5df3e2fb003f6320d8518928f322e889024b92bd00251c9c961cefcc90df4574a8212cde1841d3b14932f1d6a40807fc05609476219b2ad06791926b5a66057dca5d43a2d1852ebf4df0c0a5e6f182d206749d13e55ea516a60b9db6240f71a599c8a143f485f174e1b830a7284c6d349ed2a308ccf9bdbc6974a14f53c1094bb66d375dc4e919cca9e75046173a0e2519bbed66e6feab3b696fb69cc39953047586ab950c77eaea05cdab86f54606b468bf29a834441231ed51248ab291b4cd054a3f4850bb96a6462aef61157f4efa41938eb481a118725a659fa82b8eb78934dc76045b131be78c17dfd1fb4287a0217d7ff26f8cad62f0fb02f88940e7417f24006b898864a4ec7fb41aa32e8bac42bcd4a9f343d1027effe85bba2765fc9e6e0b1c283466dddf513a2ef297d6e3750cda8a7aa4787818dbebf8fcb40706be643f63dfc855b422f0999f78e033b77bc1e740eaf9e0f3b57d4f46b32a463137c8ed591b3b20c084b6026aa9ed6ead03e9fd5484bdd84d82219c00a93de454bd968e8cdb0af21886a2a7cb9dcf3a581d7412bc16a38e31e4e59787e223564b20e557533afe2a89925b391d3c53ea9bf8d55c953e80760f15f6a4959c44b6f983d16e38f695d52282c6b9ca97b1188706f8a5964451b25829148b6ff47c35379fd7d0d581ae41b59b8ff23f7586fd18f7319dc63af64cac7cbecef3edf3f2f414e0ffa32d0ff5b58956da5841be467448c4f4c3b8e1e1e2b02ed9bd0670b0095fdf59978640e2eab9062cce5149f06bb5cbf56b1014d3809889807ad73faa83d73d3682af4ef5e4f781a78a87f751078c9dc6450c6cd04f96f691787ff0338950ef5d8680de36766022002763af2377653da1a8c3fc6d7a040028e8a67f5b3d0bd1f459d8cb34379b68ff979a7e6d21c93e982c24cf9a14d374979cf39d8235d9fcf22cc761c177c745b89fca412b127efd2d9e0806e732809bd1c73026f8dc4b7d20ed46f7ee049a46bad20c32d141a8c7e644124e2d3080cd5abeda3d530e5a635b9818362d9d324097c42b91731a3673981584a29aff601d08b4bbe8c224ecca23422fe0c3016512655ecb94d6e0a8953fb8ff0749f82ac37534971cb062fdc0ae32096663d767ee387eff125a6579d3275d07009fd993b785358926cc190f5c7574bab36195f84e7ea6b18421b8889822ed40ed7253d7eda1ae049b5b47f37acaa2db4f907e4a35ef886c7b043504a56195ed97905cb1253f96214ea8524a6b194ff49fb1380dbe3876406c203f2044a9ec2c9f3b3b007a69c7776aca285abb3cd53ee50888c0f92121f4733228a4e46c885be4095f8cec6dfd1ac023d848faf43d5cf6868447cf3ee147abdd3e023e947db49c6164f3862e471fe75c20a6cf79c94721b4482f683b294611445d009bde5e188cf341c146a933bc67d240ed3c5b9bf5bf8df23cab6cb9a66f0131fa4056875c246b18663576107d55c53fc463a8907d6d86bc9fd31d5e97d7f5ee56c85f86a610b89a50e1d871d2e42294eda86bf64d50fc5915060747b0b4642d61a44ae30d2897a657f7447c901e264768baa6d975ff7b4fd1c1f007921019255cada98968263c03d4e2ea3561da1f8906e735be0e4cc7dc08a5818fc5ee2474d0119964c3d20b4efe8338b1635ba375f9e64c856461e19cd438adadde6cde3e6fb1cda5cf969a019a63376811a5f69b20055c7a49b1a7e4fc836666bdbde2481a53f6689f1179a4b4aff6d8387bc4b3c40a85c8ba766632c087351b587d3deff28c86c2b1d0673cfd676392007b2bee97d60a5bd7752f76f3bd2f9e407686e4c7bb7abc7ca41aee76683a557581e2620536cb775cee25753bbf8b2845c7540f91c3e37e61bb700ec21184780db4268a10718cb3f414910899f4fa7889a4365089d8fc0073b5c11f86fbade6a166d636d1978e80dc19088f83900d3ed4c38e62d3a40ba531f8d3e2ee23cc807491fd16de23701806b5266fbea77110845e179c05570a87d86fe8d04b93f419fbfecb6ff1831f1296822aec3977d83ff3ddc148726be67dd93334f5c49966b52aca66d6b9b04abb80369d4f07ccc83b3bf60c51866f564608999d7cdf728c11d44dde3564d7211d002f52528864c6c0eaffa88d3106128b1b743045318531dd178a7dbf017790699366424c5e99d5000cf9c4c81b927bd047760a4b5ab4a86e55f7ecee3d0b41d8e155676a36f60e6e0544e6c38669dcd70e85774dbcd8b5c60d9455c6926a1018f4ac560d70bf606ce40e925a5ef8a8ac8805c01784b839b38f85e01482204a9854e00e39622c7c14cdf5033ab5f190868e7c437f2b688c9b73e1a3850b1969349501e36ad425433ebc45fd19593b9829b688b235bc70241717f9a94d2bae89efc7d40788ff54308e4b77b8f7ca4aa052e1a3d9680dbd7bc247ada49080ac375746a57fcd859fc1760c1df16df34096b080cf447172178a2d056a64cc65d94776ad909bc15067a0960500087dc4278ade938a146b577af9c231d47785bf147f7f6e01e5e82643dcd388dea1ab49ee33f87642f8280f867e2e7b835ab8e3d8e6d2631f1bf34b558d8188259116edd9b01e7c9483f781fdc6dca4f74bc3e64aea0c60f0d1718a16739bd88fa2e9961a1db1577689a08f524c4b3a4580695f85f7267654ee61eacc89deae96a57c6e32301f3be7896070a523c82ea29a22304c7119af973a881e2202e5aed078c53606e3f806500a39ebbd09ff6624968dc70f99ba4c29ea4e76dcd686a9915fcf554b0352aa7462ef2831a35e5428848229dae4d2e3c2a3dc7d40f6ed2bce24b12f7437242f63e2a70a4042514850c882b62872fe4d330b5823064d1e606d623c23be9c872e5c2a1a9af95952f191b31f8000b271c2896441bdb2cea41d111978d064d643b122aaab52a7dd90e68629db716441096cae562f98e8efd2e5328e5117af9896bce71232e0b22917e972e24ee7f03d5206bb02911a4b3ab83915b62f72990d8a9250a99531b56ea3a3d5f3a49df0becb8be6889c3a17695e175f63f3d346c36e5404a974097f6cf7520a1410b09d3cb4afd1f8e4e8609d1e1d458a54ec8b12db0eb77679c79428b5aa7cdf68d450701c20a4025a73de316fe58fbb6ddcd63aba2cffa23f7537306122c69afde008842e545412c6a29311de30088671fa65f3982a991772d8c7758010af2ab956477558e9a7369f81e81cf63b40baa4c815b66db78f30824e32db5c2ffe4dc18a24bfe797d0f26b1d9b4d11d85d3bda46e9b1eb1c88fe21fe173a94d566114a0d2b99eaa8736051c44b042f88fc093707a3e99873b81ae1e9ec2c0d36c3c9e4233586812adbdf52d61d8e0625f5aad209f7dd367c0c72522c74568cd88e9e0897d785a82dfbcac1255a4ac0c760275be002650bc073ac407a74839cedfb8077c90143c491dbd38c261e213e27f51f233f48b4a7a9d657e4e9ab40a7171e7875d1355718610b0ba82e23f44fa657a1cd5f704f1ad1a318a58efeae3114b270d7e4b52cefbfacb187ef9479885e01d053defbb254827a9cdfd29640143511bb170c7d1b3c3a560b3d63e302fced3e932ce4ec94617ec234520053ab8b0c259c75fa4a81141117ee1b73a699d68aa597ad42c27fb6805d9ee1226e6a59c47b568dadc877164aac4bc347389bff7d2178fd9ccd6e51e32309eafc7e840f5ab571f5fb095fea491367bbc8cc4c99862c98d78b580b3e9de9abfe9d6f7818de0757cd70711d45268ce1f1b3727837e8aa74d9bef4a777261aea44d685664bc74c11717a9085d5159632d0eca70b293bd2ceb3afb32b95bc64b0790e73bf38fa46291a55075f9f8044e4d5f4b915776393a184101fe8397b65715f7b40fec480cf94d3c899d091fab8dff8d5649033dd635d23d57f39c035c028b14b10ece2fc7541d5fd7d5dc3c6fa4577519cbd71412fc2802e6df1f75182a2ff70497972c28da851cdb12b1827b047a7cdc066acb179136f55f96d6def4bbc1474036dee6933c579491acfff87c64b1fbf5a5d4dc4775a1393de8214b969999411c265b4c25ef36c0408b3c3409f47f4ac94b3d0bdc067b7265eff4d9a6a22082bae04b93c9f496199b8848b4922c02523e6ab71b431eee27992882167cfd7b75a38f18f916887be28eb61a48406d6df8950d18374e0029cde8ec3cfc0285239c8cbf2f792889cf503230a0dcbb3161c56ce99e37445c8880a6bfe1dbdf7dda660b010aba5f14b13d1225a51056b3b809fae885b570c9870f152a44545b0957cab7d3716a99bb62fbc9435f7137cc0b3b842cd6f3c65d46c7811d323679d06cf24756f9c74eaaea078d65ca0b9f78bdfeaa5e2e2506549f13155445cc2f7e18a728e74f641bb58234269767a78205ce81fde2677b2d368cb1a9166e8188638854279f53c47b2464483978d18dd25bd705771ae86e501b01e6f8b07bab5ced84f5b34786c33d65aa7ecec152dcd0fb43e10b48835c52bcc04602a8cd16f36cfd2c2ce6fc5b64212b1d946b1ba324666d8da3bd2392870eb3591227b92834f39dca497b2209015db0322b16fc1fd1a45c5bb591736c907b7b5e518aed13f3cfb1c5fba7a911294c3144fecc4348e5cc6b9a68f04b5dd682a0295581fd4a42baa079ea5b2a4233b5ebb4f4da98099fbc4878e03eafd08fe7c7f4cbdc2e742960dabe2fc979b7884eb47196df7cc0fef745a0d4097ad87879113393e48ecdd7357bd61d9f6318de47f1ebc95fa52a216ac08ae4904838bf99c9364b838ee45e27f4107bcfb9d5549fa653f66e9bad07824dd04ef0011f4331928ff467cd22fa427d0330c115ff85b794655b39e9717acf5a6508e65a240f54780e0d507b586c8260915b93411a9f1bd38d6925a95133ac20723f2f71160d63ad6a20f9af5ea835227a63a8c71b496a2e6cf1bd105ee08b6c5580a431ab4dd62788b699003e98296824eb17e8d4fd8233c75f4f53725bb7114df4c1da181afa04192a80406c2cb2921f58d1aa6a2004d080f48c2b63913fe02a1a10de875432a678f7b7b7ffc3351d02466621b89a9768c695de26cf1aa196f7ccbdb5b08e5b52ee01c9447cde52132317168fce2d07f2e1d7857857ecc7581be90139a89ef5e002016875a6f31ac523ce1b43b1d8cc5aed75e76fc98102a42d29e00a72521708db3415de275aa47ed08f17390c6157d0f672dbbf95524a046fdbd5941d6536ec3655c1cacd3f99a46ba5e87a8ef8f1709d52f42a8be6877eb7e9045a58e490d3cadd332a093f69139b0a15eaafd60f0c02fe59650fd48aaecb3042b20449a815fc2202d8197089e30a420d8460a03fc11a70ee6d294604b03c780c904d8a1e238d042e475c21720576780e66d077f81d23056ee3ee1bd409b26123249f0fc79480c87399cbc58f7fd85d81b30ae02503ce9059af503508e3201ba8ed3c9d215d170cc5390c63f9446927a42b6362e7c07b963082af51d3e3f3cc8ce5cc0066e4e22249af711328cec47594ab688d1f751a6301b4a3a96d7fc1bad5028571d564ff7a61b15865047ed592e1dcdc08845f389d707f1e1a9af5c4e8e2b75819e84452f7ae70867e8e1e95d783cdcf856094f94f33b7ba348a257201791191cda6debf75ebe323f3527fd915e98a1ce1a06c04a7c8dcf6f92cf23411afaf734485d1521961f966014078a9d6ebc2a2bda02d2c8026aec4b851a74b568705f34cbf6e800089d97fb6826cab5bdffb13497c2e2c00a54bb4acfa3a5ff863053e13b986e146558510e3ed9a7403a479d7714225b9ddcce013f4498e6a8502203200325cb675b8998292b1029d531749d3e756c4b0e9d892840c03f04448b09a664abda0312717621c79ee08d2fcf23e7db4b43564a556e47dca582618a08f79e086476ee732f0f7638f815aca920c2d6498192bf740a571af47666f2b61d021187db18d83af646a67b252e414f8c726af5b45d45601c82f5b99ccdd95da497ca5708bd7297956088d42beaafb117ad230870287a95b8befa83d2ba7d7b6105d231e320d4593791c0b59dfd0627dd2c3ea1e06c98111c2459e5d623de560bffcb96675e238a594abaa1f243e3506197f8688edae88a88cea9fccc836f2a01ee8ced0a159123f83ed2482f392188909c9bbb15e647a552f011f89c0e40372179632e74faa29ca752aaebeb52a146ea65e7ae33615d9a20286060a55f01deac18afafa69cfcde202caf4fef33480899c291d237a582b1e644f43a6a7b11f489427d5aab896a953f0baf1f51cc82dfbad589e33db08cf55c573b3074f6fc59e6ad7f323151d17d8c10340feac1a039ede15eecaacc4c882b886e0bc9c15bda395040c48c84765684422cdec1fbf78d67d74af569797178f5516198d11d1e66699daec98e8bb3b06bc5d08f90b4442c099b3c2323297c88df7116bc5638707cdc50fa650bdd69b902c9bee77a1720cbf9dd9e74c1c50c88c6d68460497dcfed9506efe358fbb5c64ea792dba1146e7d1891708b6d1857ae47c2af7e6aaf913bb1be00d96f278c221de4196a9b6b4f87a6ec7b85610170340e68e0490cad157fc596e08ea275e613cf680cfc67c9c2f112e7d9c5ca811fb2b2555c565628bfd75548efb65c2fb82e3839891b74d1f2915ba06d9ce98eaa0ecb45157993948623745a6d50ed6b6237071efe1fee5eb557592b2993c01a85433e733ec05f999e4c67bcbb9a628cee91b3fdc48bc21e570f8040ec4cafe3de2a1096a9731cef2d59dbd18820bffcfc11bfd03a5ec0f54bea3103c3aa9d1245933cb5af60be8fd2ad706d66362a75b875d993f16b959bc009f4ff1ca5ba31f65e423177b94b5ae0e6b31197da43f8ca7ebd1b5d32ed878211ccfb7aac512222815b34dd753403a3cd673bfad2f8ed53c19e6772e7599606f2dce25a19554a3bf624369fa7dcf4984642836dbdf2912cc81e4701e99307e3f1237d05600e369a4708e0c10c6d969fff6c204cbf9e24154f15403ed6d23bdb18094722f8038a9a06234d2c6dc7c0660757077f53648d49874cce1b6f7249a5ba4ee85d9459e5e89089183c8cb9037bee453d42b44f16aca30c0375f1e14d87bc389e552d2b18c19cbe7e0bba58b8850093e602ac3fcfb9e3a9e74b24c8aaeb6a0ed5ba974c2e73a302f8c54c519c46e81123b1f796206681fdd0ebfba53b41a71040adc31face03a7c747dc3f5596c1ee1b2762ab92d89de6fab402b081eae9b0e2d9fc7e2556d8a688850ca56b43e82deb49170751447e53c494985084229c10803b8f8e0dcf71a4d0c7f69b1de251be75ef6f4eb87d2f5999442880dae6bcb89c2978ee62b77eda21315a6ed043357c7855c01641eab6482baca5dad0a5b1a846998362945ea803e211dc29304d3eea73a405d0a4d280690ae0b5139145d985eb778d4e3026f084c3e52e8b30d617b56f202c0f10b906c143b15bc21822a8a17cb0adf97a7ea642e06bc82a8f28958d1eccea55242a8594744e001a7f5e5993a7365974a09fe1d45547014192c2074fb6961dec4c85ee248ca194036494bfc8115ef35068ed22d1c044124bd318063dd4d02b0228af7972c9fd84cb7f177cb3b39376de1e471b604bcd49a4b47d22e2359044ee893631128de6d55caed94ea12bd356d692d3ad9f079ec75ea904d62834f2297b3463c142cadfb23590bcc5c88710d651f942ac2d2bab2b9b9ea00cd140d96ab07b3c42db50ab5eed460fa1ba42de5cd7644e6310db34f8f5689ff604a103984881cf0bb0ecac86e42c590b62503fc997b25f29c82fdea08551719b9637d056bbf0c7c9d976195d6ac04b5663d1419a8b5255c9973bef4cb5c6b4f5aeb94d1bd3ad9c49e545bbfab2361b50ea441185bd2b7801413b7950fdf59d7f1f151e98f081d8a19fe0b8a381e432cb485a3f550e10363ada10b48a892ecf00f8d7c1307cd722542e3845a17b24de917086390bcef1aadf7b8d3a755e662d01ad08d95188fd7cb652029e80578bb1c7167bd1aa701c0992b2ad9d92d37acd0da8c0717a94e1be5f43285a228629318f064fd4bd93798ea465b2d397151a1b5f93cae42e36679d8355e82da3e96c3e1503655a6bfa624c6fc93dd91e879b814aeb9c13c61fbe0099fc2e49ce86e8ffc4c12144cf713fc93604e6232df3d0e906282124914280a7a3761d5f8e30d0268a2647fd3cd1384661781f6c6772120c42556a18588f0ca539085466d17b98ee36591e47e48e559d1be7b11a456d65b131aac246c611688c23797dfd00872bd8bef382c5214404ea867870909a59caf8b7642b2f32b0b093899b4177650779ceba5e04fd66f52c976fc4608a6e92b947a9e45603364fac4bdebaf274bb0b96288600f93bd9b0af10a8f9c371b6d59378bb6351f0d127663d0c8d9ab08cd0c5eae7fbe2b3e6b7668b55190bc289664c0349254bd891f58385a745431bb96dad01577fd929dcb7636e318602d5ff1152a4149a2087d5b179033d9366a7af3c3eb3fd718ec9ca276fcf0c1158d7cdf8e2ece482f78465aba5a04b55d49de5d68cf8f54d38e615efe9909c1126dc1cba1a584e2b334d924a9ff8c71043584a0b29fcf8fc809cecfcd48d75b4747634ed3bd73a29a4daeb1027d5d3be85613cae2635f9a15f64f18d7eee91fecd335c0ca4b624252a9323654b6d721df8cd02abb7f9e48c1311f63aa329484ba001bfdbe177e0ec15f6e2f4e8fe28ba8de88836eef5914b22882069a7490f96220e3819a09586beb41f87c3913b5ed9296eed7da14ddec110d9b9a04dc95bd471e4938650918611a7b914a980641950ff31eb9cc17aef7998dbed609212b032e2de302bae5187a7e9156f248a6ccd43b127f115922138b06f0beb1165f7ba0251cb858ca0974245912cb0527e70ccce96526ce82ee04e01115ce3772cc85f93499288d91cad6bd97be49d9ef88be4a0debe777e43aef293460b60ec746200e98ca0b2fa06c2152f9e83797417a4a0598fbbb040ccefe8136494d8d8f6fa25eb7dc50cd59961b615153eed0316f34ea29f1ae1829e0b4a976236a979e6123d3b39f757852c89ee2d35c618bf2e6dc21b77caee950c74811836e1305e846c652eaca3c21c4755d0f4fe41b51695da1314ec6b36e5e22f8300b7b6d2c3167771c5ac2793b3e9632094068e03a9018fa2b886532ccba3ce6ffb4232b7646a2be99dd567d394766bea4d69cb5fcbe4d4d4d63db5380e9f4626537213cb91a79181e3e1772ef8063879578d563a037c3bbfaa872223147a0bf9c909c8d676f10df19b17dd7c6b4d7f93982c193199d91c0b10740d2658a0743817a37fa17cdeed91d54ee14d8259cdcd2242e3c6dfda3f78e0b3364cced4dff5b292338fd13190b3edf9a4ee97932e96df046be9491dd4819d93fcac816d965208932a26a28233b6b06ef74ed808ceab19b2bbaaae3dfde7743980c3139a449319343a3c908ee9003db52f01e792b88e86cc7f1720b0108c8c8f3967eb4bc07f4049561604597929523aa2494064a045f731d12680d7091c80281bbc2fbaf1bd5004bbd3e04446f65bca7c63e98dd7d84b51f99b4b759feb4b74684bd65d402eda93234b76136050787b8eb21f64e69a4b1b89e5e881d6d839fa5de2a09a0f53f6a05f559c8bab92d643420fa6df91e19abc731cbe4a3a71d295c00292ac8e7ec397eef4c820d0ff8c1da13470d8fc9a21732a4e5d80540daef06732473dc6b0987e5427f577790eb3d0ac955d999a36b92b0e5cbccc6f87288f59319b03797f0bcf048cf69eb859530685a2b978aff9294a1e51099fe2ed0bcfd786f42a5a311c4088543082150609b4a563b062d356c6546a75e19712ecd394c94e6dc2e68b1e6b33a87be940763d76e7fd892234c1b19def524f48dc14e4e472f57963b2d32ffdaad9b7c660a33a044c4218cdce81c5bef55df19d629f8ade648383df5a19b247f038f4f4813b53cd0feb13a894d945fc23c0665848e8f9dad5adaaad3a9055148d1d7c08efd817037bbde59507346a0ed1a0b138c599583a01ed0d75c8dcb3cefca8e829df1f50cf4831ee0c965b3da1a19705148b94ff23af81095e2c43784aa9c4f731af72f4d6b2c2ba5c163cfa69d512b82d37e2c7de7635bc8cca688a00bab7d34f0e56afbe856b54afaf6e9c48826f7414f09e7524ef7b90f02a3e3e8e587843e1d2067af0a46b3f4fc1a939a95799971267e67d0efdf43871e90de3e69e306d43a9d7c00a19ca9d75c233d36a2f00321bd1ff9573363e638072312e45744a2d4290257c46a8f06f8ce903e106e9cafe86e7abbc9e61d5be734903dbee8eea82b65f1a9882965e8010884c886107104f92267a6842ae2b05480b48335a4de755e0438ebc28e21c3cfaf5a0ded840c8cb66640d5a051dfc9829bea565ce0116539f1248ea510baaa10fc8624c3baf093f8a72b201cdf52108bf3ee754beca54888d9dc113c4cf51fc47f7f3345c57d3d33e22da8764ba16a705095f460f7652881a65ba7e68c3ef0d07f3c4059cbcc376e7560412b5311f9f3bc387bf54e85329739df9858d818b1345e960e59004c2ce0885927e49695b316d497af4817b8e071c623e569baf04cff59cd766db2630d3c13550a16bd45f16894b5d4fae46e6151a580c2289bf356859d3a468db05dfd0a38849af410a717989f191239f1e4bc3299b91b605d39e1c5f9d7bace3650d0fc75069caf412bc744ac3be03d5f0cd8b977e10751ff5495de0549ef8daa731c37579699f6b7220eeb7b50492e29b80cd141c1301481bc1498f04c32c0800ee6e660dec452e70cce6ea5c26b1568cce4abfcc9581dd2306e4b1e002d5235cb12efc32d834775ae7f97a095867594f5fe2fe62f18aaa77e87c8d75f74b3ced29d05cb8f9cf82ae1d0df521c001d42832b7dce4f7c8a5aa032d4b1e0c0e37ad8931da0d627145e44cfb12629661ca1df83ab87a07e973da801ebe4cc1b49df5089a2130cf7890b79fe88a684fcfcd83062b2e426e5211c52fe21fdba135c389e4e1b74c97540ad8ddc599d62e6f3c455983b76a51328bd656044232259f70b8d034ec89e49185e07bb39c500ca495e52f117bb9e227a8d74dcba6bf56d57746bf90fe2521375e8a0d86d7e108f77b533c60b6637ee529cac1c798b638398cc397ee34b641e3388da3b5bee528cc3ed443d8ed15224394717466fccc8651fd1c35f2041ee9e397094400bf1853b2aaa5cc223d96d945597376fc641037a6856b112f0328f83b283412c053bfb95132893606bf36a72f6f663ec3e2de4f13dc78286f94f02f053ef00a0eb9d4c0f35a07309c2388f970aecac146194ef31ca07606db100deef373b742511c7bc33c958504d8f5122788826c7087a98457f9b16d84c03e80274ed513f51d4069bad380ee84cbaf19cc70f426402825ab0c7eabf5a558393ff7b753baa29e4b2092a9be1d2d705acc54427e8acd9e83b48c8ab92bb6a7a20584a3d3294767fce9e0b3c10cd9b94c35e3181fb80f27423efc41d1318a58a667d446895724ba5dddd1e743d0d5586cabf7e3809619993c93acd96054379451504b916f08fb8b98f0cb03a8f358920899e7a0e6a10ebade903d53989fce966f4c75d4443c271516f6a8f5ad7b5ca7faf6d0c2d6089339c5c3551df0e91d3e0cc34527ab356d29af8ee8d2cdcc6d245890d65aa63fb0474ec9c98b214f1715ef20839df7d20d74c6625fd705b705ed96009c5a208fcc15bae122b9aa6d493a73fb6220b431ab27332901b5c35687d95a2874f96c642daa3cfca498a169d8e3483e64c8d742cb460d4c024cbbcc1afa8f6d0df4cd58a8316d78419aa9306693b81145a4ccb726b5dee1e01450612320379b03c3f08b02bfaafacf976d06adc937350806e317228f3e5b3d1352643c46751900686199cbff6e7ffae8200a54c2384cf41c691c699156c99d704d1949fa6f343e34e0b0085ac7b136ff6c0ecbab65454f29c2b3d648c6461fb44bdda28127815d69271568920daa48b2543647cb95e3029bee8144f9a3ae2927179a94963610dad0c19e75ed8302019d28f1cb2eac21cf6685937239f6a4ac76d89f378ad5a4b7d90ae6b5e54815f0cb6c2aac210606603cb4ab13013497444cb69238bfd9a018dcf72973bb0075e2e37b67829f930f9cde0b588011088cf892b4fefb17bbff82783eebd12d362f7148bb001cd182780c0307fc9a081756f3420dcee247d461fbf05f292fc197dfea93e2d9cbfdab9dbc1b3d72274e581898b0face09f103a8965f46ee88e8eb8a2b86aed3cc66882e59d23861b211643c3395ac263409cb792b8b0ecb43014798acb40240e4c18a6a768ff31b14b340d88962c47640ad35758de28f6c2538ec74b80bd8ba9fc74ea61f69553b425bd7ae7d6d919cabcc7332419684946e61a5bb1bc8fc9aa98dd65ea08df055de0d8d8f43f2d1e0ca08fe32c919a7471c9c3718b50e717806a62071f158a23b26b145782baf44b9a6d824e988f9d6065426dea5103cde1ed265424f573871a9a5f49f6c24452cb186b22e2147b1c5b1032887426e08f2a6c196a41c02a02e3cfeea055eed6d6324513f822353e1580c877553046a058fd5c1c629e6e96aeff9496f4e564b8e1d0824f74bd30d7840d1c7ef3d104a3b38eb584d8c91228a9d060b6dd90b2487f38a5dabc71c8f4431f5ea187ab61b29a87188e1fb4a6c703aa5945e3c2f1ccf3da6d41a611541b4b6f804bbf08e21283cf186311b62f86a14a572fc6b2e48b1b8568c4a9a69dc4b4445b900b61a854cd203a11e26472319e03b3d7f48f6151e2cc733505d90a33a5e698f5429d9c28e54fa463ab5257ac3a4e913b7e48c26a210bf18d73b1984ca44e289af872c5f40129d6e446a29b0c967a0a333a3c49c45b9143bb484e63659047d4751c32b612826c8b1de3c194b124ee4b74253724bae4b0dcf359cfe149d21f6619afec7d6b9010512f2f7e0e2d167321543851ec923b817e32598c664d09fb453590e2e849266da5a38c65793a4c244bf14166ca62c62742ac60b238d3d9cb2989a7d7048fbe37d7565eae023c23a1a7d18817c6c8beb020abf1fdff893ef891ef074d343b93533481a8182357de8741aa312e6dc942d0c77fd271a61d4acf7a4d2bb563a45f719c1d0bec0481fe346975051c13396d6fd787680ebd2647b0891e0823a66e07e3af3c92cbc2c842a7daa84a57e31aff28902bc58b23de5a4dbd4164ac441ae800f8e2b527c9a7a500b32c5cccd689c7585196a6dcdfbdd3c2b4fc5201dd498c05c7d8b7b20301cd07905a8281bb595db09e37d152d37587e619c2096ec1b7156de9f6f6afd3680b68d7e879e1c70155f7ecf78e6563c06c0f86ffc619de6406009dc307a306336e66738887af3ab0e28ead1a612ecb5279af9f402af6d6ba1621de764760fd6957a69b8b3f47c273770d477de1540611b8ccd64168171c0371abe2d96fafec79338b1ef5134805fb5b72b83ffbda9d339208b5a9cabe5e64b1bdbd6cb5b27a5b63ea0efd10954f0137b3398cc67b2da488abd11acc980be4c5a9e115cc114b1c75e3bb5ff4399a42ed0c7b9b118466657b233bb0fd0ee9c9844e6189a3f468a023179d9fb91812221c5e934f84ac6c08cbae4ea19a5af7d1690f97775769eddaf105b33892e92ba407397f8ff12f93d6aca08b8278fd06ade1d803455c30069bee2bfe78ef3009ca9900b4c0ca25170b367dd797c3656e143fda7105eed9ff1552ed8a753118fd8d4bf07c94111b4e8c83d77cd60925f10498de31650d899f2dcef45b5355c72e864fcb8e8b032f2596498db71632759fddbe7ddf65780d3cf67671ea5d3463e9f0e6dc3321d12fc83ef71be043c3e3cf3308946e889f84c3cab7bccb01fd0a99c2a3a176ccd1c33f93696933d55a5b5ae9fb4d03ea5660019e748e747eec046fd416c85d8a3011ceb1d589bfc1eb446168b9fb524b6c9f9183cf3bfe837abdd787295612fab6584d2beb97ed4cff2e1f39325ece4c11ea7499d7a7eb15aa1c40804853446918b32b58f10d7cd29cb0818304eb4800eb6696b69ae2572be199f16d8a4c72fb3e32ce52e874729bb6434de1d6b862073fadd74b09fa38f4392ae703b5503b069764d925f3aa4bebed48f4b5f9db30aae48cc850d52bcc6e975652ae8e8a47747e7e61a8f4af6ebf77f44f10b69a9b2cd1b2a91156661ee8df53e036fc241eecddd9ec8c69746ed6a33690b77f1e9f34150df7b7c08dffc6b7874bfa007014d2d3aa6d3dc20692ad20b2f61f16b9428f49ebede116774f64883f269c6a8c614876156c877e0dc67c4e03d958165ac6de439a886a62930ae80db9063f1f01e79ba30df92b7d887f42adfd15cd39597488bedeff78ad309e30d609caf50af3274250f200b764514b627135e2d18337c082df38578506355ae113f17594b12637974fd6575a5ae52d756de1263c852412ef6cee6ac054b2b63d8e34b9b978b036a5d56a1d4ea8f72d6d19708145ab82771b6656deaba3579ca4e0a36131fa67b84d814ac435b104131de643964e30ed45f870b2cf68bd3b55a3c65df3d31fcdcf919d5a8f7e92f65d900c39d67b64b114ed80cc464c1130b109c874441ebf390df58f11c9cff82ab1d4d560fb792d1b6e3cdf53debb5ad9fce39c0f62522668472a0d5bf683b457fcf1762486e2a3d48050c51f8d5561286a5624c9b0bd3213862d073b4dae0ce5f4d592af1e63a6f5c0a562e9755529fd1e15af008d3647acbadc295c3e9650c2ea3786b59eec94505c8fbe426de566d435d6c085f00b50babf58623b1ad3fdd1086c2d7093fbe9dcf40199c23cbbbb12b0b94c64c7b60a97e40c302aa427959a1c2afdfd0f100c05f8180de9d06fe808d861214b8759e19d48a914ed482ae11fa5844bd2c08b34fb6b30e9d3adb6a00bf0efb5b6ff8ac29ca5c426694e7d4d717afb9b11e5eb86f622555133a993ae56c93683be914d728ac46badbd8cfa896dd7ff347bd659f6c7a166aaa264b486acb248cede20c9a1e7c16588f9b0831783ca17f0656493ef03613e0dd6315e202cea5be726b1398c284c3d9a12110f78c0a72e640876f8891781ac35d106c30e8d80714c4c1657b99dd640c4033e107beb40b00fbfe24db3e481914b695357c183a2fae78c85b687fd6f6de8dd3e284e2b8308d37766c3a3e7b48376dc0da3adc09876b0e30862a1c0ba8692f359ed1ff366e8a8061624588bc079babc0665fb54ace8ee6babe374309adc4c13218d2e6eca37fc9ac69efb03c121ff779cfafe83b05dbff61e088e9378e836cf5d8be9d000cde194e8e2a38f617dff093b65eb62fc0f943581eef14d9a35bb794f5d0ce638aa85c4bb7e4e48c09ddc0a869b64251f8658b32e864edc7001dad3d5b34ec65b902842031e7aa8e811e1fe27f559bce3909762d8444e4a5f7bfe24b66a62ce07ed9ced1f93db5f78db1c0677063d71825bb18587247ebb996b583aa1f4bbf26fadd19d0bb307e7bb7239ebefbd628c09aa2eaefb7ac68223626f35e426dd889b3952b41f28c1daff50f6a123bd703483cba177b512fca26f57ce0c00efcacbc540c9c175943b24338f8164e4c866d472f05fe8bd8e48f8fd3eedc6371b7f12ca27e3bd698fc4935dc6bc6d94b1df0997f129b8fa7983b090bb5a80c2b84a52308a297edcd55ecda6ed9976f51e42ea29828adcfc83a9abe9344dbcc159efdc8ec83da1436ad0f2a6719d8356dd832c00e5c1ca941a9f3f8e3af7edcedb4cbab94a506ca224af4f2210ee97f47ebf430edd5965378d03daa7a79b9f262a7f1bf4b34d2482dbbdb50e20114e2590ab4b8b59e3aa551825b9c664fd8803a0b25c6aa5f74e42f0969780c9bba427cb67c92d17a817b5f68990d6fcae15871460a2c50968af40e503ef12d0a2d39543af2c6ddef81ca3eb6daefcbc85697726030a99181e94e4f30a04d03152af7d12bf9249288dc5150652c0926889299aedacb1451e390a9187fb9207f1359b9a40cbf35055bd008761124f15ba5c88a13dedc14063b03c004f3646bcdae0704db6822e1281fca624057d0b1bff4e688996482d1be8ddf6644dca643ead447a027c1a8abe0dc32fd682eab1849a052138fa33803ab1e3e1a5dcc1ade818c48933398747c9dafe2fadd8e5df69a40f4b1c8871e5a13cfb00e5cd15b44124cb2b7114071ebedb29ab39c28b5c0eda36e014222885d147990b0727507fb4f0ce27b4b449840da2196d63b48e80febb89205e88dcdce1bf91f3408336237330dacb8c3fea2341b6e3842c18ef5ff3256ca838a204494e284fe8c960d226920a27b7424405275c660671fbcc7aa6f8c388e812d39fd3b1f83d44a97daa9e0f48ef27bd6dc75cbd787fa2ad8db22f0ce84627944684d737cae47531a654333dfe6879bf1cd43e7ccffd622100d537e6bde30f41d627d77e52e237edf25353dd68897adbecd7723a2453b8524583cdc5c4474a0ed93daa0e68222808c11dfa258aeef6442d0f4dd78fec2202d6245c5124fbbd923d971ee6ce697eca9a7aec79877243f08a115555a393152d406719d6ffe0b718099a26c0e79e8583120be824cf48ad466e51fdca8a503625e8827c0057a0b423826e900c548062514c7e441a49cbc7778c6d4d8d8a29a1fbc8989d35073f2e8b984a3d41822796b208378ccdf58bcccdb1a700f28bd86bc9c196390e8075d1c288319074dcf9e070f3bed41935a032d9aa1e14dbbc1e25f37001ba1e37f05af8610855d28ccc6cafc2f8d5706f9f37e927786c2e6e56dc666b7b7a543e8bfbe0f6c7332e64a7ab2e9321f1191dcce22bfd798527758e45984389b63b4bb215b28e0d6bc21ceb46b7a8be0f0359afbe6dfdeaa00e192b919da44eadee1e187da209dddcbd5d9dfa0bfa71307a93c30229773b19a152aa9e67054d2c47c018a15697603940e9ebd63467bbf4134bae0ef7469a62d09782c4480a54179e3d99fa77d562f2ba1952d6ef8588a190ab0e44c1d044a720f3305372348f35bfc627a70780a12be49d52c1a582f922b21cbfc567b773dd718156a238e968c370614b06313908c1d736d72984f503c999c92ceb0e93a975de977c6e4aef5eb938516d538f4e93bd2f7006fbc6d008fdd397faaa9abe2c87cde26296e83c17501195233af77bb580cfd3efe12aab0e0960386e961fb99cb76546be11aaa68b842ad6ee28f2a05f7c559aab95662ae999a075831929086e088e8b0acad246a63fdfbe4c205d37dc993cbf9be9f20ce525e4f8638e721bf2d02d07d535e3d8addd9cb993b2c1fc979d56f56158da24aeab22f59c386fe536b58e3fc5d37f3f1e860d41d9c88e0c012ebfb2d08657785084a35aad7343305732ea6a3e736dcb112717c2580fe0207125896a7efb6a1a24b1d5b8c007505af8384c6850fb76ddf5590a8502ab8fefd4211232521e75f20a91c2e09125e0f35a39918c8bf127f1509282d20905467393d3422bc7fa8b97bc9519202bb1adfefbc86cbe3633d4f9257b4cf1c5fc927726c3ccc8f06e9e983a10951a329ea55a0ac989737ded7230470b07c2931f42dc235924b9e441066bff5d562b3400f9a5d94b30117ec87569037b128986b064711e72224409cf0bb87c76fa7beebcf3057ace118522c5de1665957f7bcb51cf64000a68a9ea3f242670ed8149b2585f35448b4acee0c4ac27a104ccd0a65d76443c2f1fab64f089cd1426c52c7c95999271af496bed4decab8b9293671fc108b7456769fd388df1e439560508c68e751c9e20f31bb8745bd234c2fbb57f8ebb562396836e54f848e0eec3e2c30df4d09ea29290a5871a47878bcf79aa46049ef9844254f2d9142fcaa665c4a0716cd6ef5e894aa5505639eef7c442d105968859e273456415678a91dae2d8982905d95e33aa20067aa5d7fe2708590d9dbd0618504ed02c618a8fc3f61a77fa496c4c8ce1f822f21fe06b8bbcf801dcedd340c3267b8d1b0f7ab93721f6870629fc5ed5a0c3629b48c5ce5e133bf2e2de7adb143ec4b296c5a2f2c98339ba8e29eb6b25a5ba23b1964941a372db6f800310b35f91355f28dc93fb3833fcfa40e394a8f33c9c69a29190a7cd13992412df6e40af78ee050689296b02c34ca21ee04499b9fe7e2de8d6accd0aceac3f8baa1e82340b16da21907f4cc9b629d9dc70bda2b09edfff8c59937f0775b6b5d2425c53efd0fd59e616a4940f0bc09d4e0e6f25fc0e432a13a7c1dba0fb1a1c2c6240103f6d23f8d6a0d6261e26a53e9aa6d08696fb0d0282e8f8ab4eea83396f7e444428f2180d863a7dda88838f8d1a76270ed0756ac6c0cfc9d27b36942937738658135b44ecc16e867ce572a8b8d1e7f9a12b6cb957c4cca8cc77f322aabf3005920f44ea72d40d5d8b904cb847e7ccca8424500954c7570971ab02bef8c698e8e2b06caacffd98fc01017a5360608ec8387d4dee18123b72292c00629c3972e0d6f242d118792dfcc981c0f2c64977c236800b948a83e34f65f429ccf13ee4579da761f17b1a653f33245e8cdf140c46d9442e3b565785998051e01a6e88e941731e66526cb0b25ae72abf8bcd3b7eafd1192a56cc857bf803190cf8cfcbd2972cc6916e2ba3a3985fe79a0a4e47bd25d4f9edf461382eafbdd25dec00b621e631a6272c9788d3b3bcffaedd516c9e0c8587a031356ab2e291705f4b4978c2ac2486333ec7703868becec1fbc31dffd2f3a4ce1302ec0fef624c675e604da9df3961ac3591ca2e553a8c09180efbbb53ae2c670eac7dd06361077f510617eccfa5feda64eff84158f446f171d70cf217e338c8e420351d8bbbdf54efa3855bb2834880e25e98b6be534dd726e462b4994d29d21755d7a753dd47c684c21643b74e6a73d09fe9fd87b5b587ae8dc39a0710fcf626718ea621b6e42d7eaef248c3601e1f5b298fc9cdc8b4d449ee3f3cac7a078f0de460f8ccdf222e4e97401d119795aaf13714929f4cdce3cff21e89cd652e10ddd813f0533f99d8169ab90dad4f7dd43c9e054fa8b3c250df7b7a22dde21d45802466b50d7cd15ed31179022721ae843218fa0725d66309b1e049de7f289e94142398214db6d48b84d245c535fac978d2819680790d7794d1bbbcf7c98a4d54d7de2f6278a60b5ac63313ce14032765f35cb263f069a8eb3dd5289b722157ad89b4f3245c4b0568b0061d29359f493ed6f754a399018f06118a57d609b86ce067dff15860c506c8a2e8bfc2a3cb33103ed4c30a37d941f8ec5050d52612ce86e33522661a9b998e284789f33535b541cd9018fdd33e3ac5da2d7805f83ac388b833c07a97fdc17850abcf2340c721faf3e19765abafefa586faab3985ae107bcf82e43785e293c159fcb7fa485498bc9a0541f5363a3358cfea017b6c7f8702fc46cbeed55638d75c0bdfc305a2240373e4f6f779e464fbbf625cf7ab3905ebc89c77ca2ce8466fdf6c5fe5d49cfcc7e4dd462eb51c1fd6b66265a660dfc610f899d25874c321aa027196c7ac5fcd193214fd143dfe13acde2a7d131b6ca1ab61c5dc7ba047d1b578cfe80e01a6912c0663e40e63dc88b160ed2cd2f67058233946aab2382b667618db01e40c6019f7deca76be51e94bb8ea6824bfa979b8c7c3c4d971d8f28d65cffa40b33648acabf33ad3acf1275dced5f643ee50fc4f68900eebb80eb9c2a116960c6d593af8d492c1744b33444d14be5c9d91413f0fd5d8cbeb701d148fc33fc97a0adb31cfce25b0e069a90d88e575c622445d73b587c85074e31e3078af669bad6999ce81042f767f6fec41cad6436ffc4407c1b5136a62b660b67bfbcc151233dc76f4bffd1a29d2e9a74654b3c01ae1d8a3870123c45908b1e0e83c67b7a62969b05f8d442bc45dd3a0e897091f8f907305d1ece25826709f3b8cc37158078345ea27a1f6ea34885184f280150921194963468803b1e6dff3d0caac48605e093fec160b674180cf085b3e0025ed5ef0e3e40c99a8ea31801e96e3d6d58224130ebc123e951f4bac9fb2ffc8e6aefbf503724f0fa52b2a322bda77d421e3b7068abbfefaf8d530db634c7bcf3f6546f54bc3d9ea341c688faf9dd6349b2a4aef5934e23e36b79cc974a1c42d544d711183dda1eaddd6b7a9dc26fc4b254136e1e72f0c2f50759535cd2ad0031316522cbb6cdd1cbfc0f53c40b25db2fc5335a7d4dd2f919599c497dc65af4aed56accddd0a78c0656012074d37dc692b2ab936edb7460a3becba6fc390b856fbc2ccb16042e5ac6432aed6d742834a2d851a494d7bb4e585db33a539c014ce55bd25b41fca4eba0308468b4b26000c2f2ba76e3e69cd2f311952c524afea669c793c5f11db925b40696364aee4d5e3a21a7757eb292b2fdac653aea8ea1c996ada9afcdacab6c97d85576324fa44f82dd49eb917768bdac12d087efb24321f4b02c7e46ee4b7cc60e4ff85e113774eb982de7fbc1ce40d07f6257cc2abd5363561259a8f08fa4786365c9c6c19bfda419fa510518c4cd866ec7892765c2701cbec65f474c8feced9c19d5bf4fc2e6fb2b5eac8cb4704613ae49dcbd2e8467f650fa094313289861e6e16cd1109c93b6f445e92c81a1bc63cf53128ca27f4d36f0c39d171800a472f9e29c2f90e0179c4c159e56ae229e7f23896bc9787304c39797e87fec20478601da575bf8af0f7bb0948a35960642dd9efa36f42e98097c3ea9120c8cea0fa8dbaae33799ce31a90a86613f6a30dbf0dbd517e9878d860d2d4eb314923081fa25d9c8c1227dfaab583f0a327bbe7a223049ca879c5730543f22851e3d797978f180d4d426c89cae0d6015843e21ee53ee6b2ad45e3a49aacfcac3b0625ddac4e8a7833faec8903122448487c48b244dbaa55aa8ac77124b463facdc8d43651a25dd7271cb08b3d0801b146c01fc46d5679d22dbdc70aaf5403e9bed3f4bfe3248f005589d6e45bb1fe2200752b04dc69535c04862b0ec965d15fa930db24e7b997a6849dc48d8c52d4047637b3a0417a128c9a2a0947dd6505b3d0ef09dd2da7362fd6320b09384aab771e2673f104528d04f7de7b2d9e68b63d184620d41d111f2c1d2df88410f317e2de4cd75fffb5d3f0c7a2abb2b781462460e0c8d8f108f04cb4729608c9646587449cc557379cfc595003472fbeb42f39cb7181afd5f5e6037a410f394f93a4566d262887a2d49333746235f4ff8012dcb6213a0592b352de66e832b2bf2ef2916b32746aea0fe2ea6a9ed8e9812ead0acc98a255bd2b92b3f2211a59841db0d8d5b42141fd96e6064c610da6729327ee53f75904bac1de6d5ac4f59ec6d804a6fe8c2da74c5900246f1e2dddcb07cddc8b33296811f43ad59a1a6f7a8feaf4c6857606950afb28097e709694101fa2bae2c1562fac3b6667c8bf4bcebf06e811f6107dad93b2651122f85e0a2e67dc7aab0216b22dad1c29f588426c568a775ef4a0b6f95b905067a5f810bb0a24809197704d14bc34198ae6b31fe5815551424f139b2c2b25db0c44c32c72e93ebfbb529bfb2eb879735db21786a20eae5484c7b48363047e2d95166f9972c46421ce0ca5284d4d0e8883ed89c7852d9769cd5b121c62ebb46b1b8c51dbd2b118767bf12f7b20f27d77f19ebefdaf6750994f1c411aba46cb3797092349a962691220dedf112dcdc1dcfafe3cc17afb485489651129fa6a33f8f0a9942869af4025e6b2ef638a1618c781fabab403619a2798e6e4465aa1aa8640cb30759fb194a96e48fec4edd9d91735cdf2ab6adc0410e7749c5624f97b7283347241c4b9e1f52628cbc541942c8f3a02a837d1e0e4a7fd5951a4b01c8892ee4ad9ae68d5321f3f0f8c80c6d03862888c43df8edbb0832b99e42431de0a92a7d5afad99249ff07921d5d44c1186c50cf97e10acfd48630bc6448f2044fdc57a02f33c7ddf1c4cc02810f983d149d17a27471efdda8b4645a6a54e88937cc0471d73c83730e02f264e5815d00426986302386439212fe37c1eb136bccba89eb596666cbd3b6c9599b9b2fdbc0b6232ef02abcd0f7dbb7ecb5c570aa3770d17396b412efd6d595b90e394e72958af7db572cd6f456b7b0ee8159359bea94bfa2ccec54f179cf353da35ea2236926a19c007eb9fed7a695d8d11d9c2ed917a345019a4e19c78742d2e73064893e3306d1adcb3f1da300cbb76402eaf17f5c63b1a39a9f51ad6d102d7c12d8d9a5da47b60e79908af0b850288736469f10a204b25e0f35bb0c78749a41359b88a8c65bddfd99876e5e2602c810acb9808d4282cb8427f14343bc8588973f6830b52b8dae1c4974fd914ec1f7ca9caaa309b4e03ec8c4e68bfa8e87498155559e1c1f56eead94f27f375ecb912042fb9d4acb7a30191801063506231ad7703eaf6242f2c2e6228173486d22f5c1304155f5461e6fcbc78950e6f63f4ed27561709bee260492bea521dea9ab0e047f1c0b692fd6947075003f2c82515a74c9bb937ce0d9133b7daada0b28cd82ecab155c0935ba6f4d93fd3239009fe632445e5d11cdf37e5db226c87bd0a58cc521e70e9007bf87222cedbf0c4d348bc9577bdf3c1800716aa5428639b0a676071ddf9d0b83564717e79f07946bba589a5048f8c25c12555b6dbbc26604168b905a68a049d4fe115428db9f2035afd2518f22901c5d1c021dc6f2e6182d7917d22307ea24dd5cce1de314903b6ab943c87c5009ef2b13c1fe08451ad6e235839827b20e7d32f8139cea17c7e20eeb5cab4df606eafe54a7a83a7388bb77841aa44e3af95d06331f5c503dbf07c592ffda235768cab126035623065ecb61569efe04235cc0cfce41c9dc702868eb1e0567cdee8ec583a9a5336d8ade4408eb7da2e0b35fbdf79d1146c737dc99ced3410a70bf4cd9c0b22d89db42e6dda319d54b5b39f6ed79f92c27394bf2feceb8868197f21dcf03528f20bbc18ae2370071c11b995e68e28cb296881d1271952222a942b8814051425c542c59272c909a2b57e8213a9c1a59903116726e9ec160814f20865cfaa182856ec5add8702ad1fb39d0230c250ecf9921103fc43afd59aa5cd52169832994bb4eb63ced7f1cfeba71f2d33301b59fd411a0e6f42866b8179e45a8948d6af323cc5c117480b707a3e3bca0e767873554dcbf6f69beb8582c9daa2adba874012fe32473d68a0325e6fee09589329f06d625566e765832d9b3883bd3400b20a0daa2843fdd2b680e9b4dd4ff30eaa1284a95c95b717b7f7b3eabb9c6c76b6b70e8e8ef59394de011f042ab9818f5d0e51da2b4f374bc45d827f79dc0a9715913c895114e4e79ab3b2b938b885e028c5865999adda14b14be1a73c8c4bfcb374877176d425b13d18fdf9c9573a739fe9b9263c364f2a5d6298b4a6f7833366403cd1882cc63be781e9d844efc9e781e6ae354c2a73482cbf0c28037bd3325d7e373ebc6cb6080d257cf7659da597f7b536131787485292662278560e00609f85fe2b202bd7b25185b1675ca5970934463265a6fd42a2235c41dc57ec6f66ae900162737bd599299d2e0b93eedd215b53fe7eabfda6a0b6ec6e0d6c32c4a686c547d87e750a42f05829b66db1ecc9a86e2f5aecbbba99210689edb3549ea5014871000fb6ac679d9f51ba883b0c42812a4935fc2e5d2c29fa52048524cadd0d3b3f45a151de2551e195c7356b179a2858391291ce9a13924eb119dadbc60d5084907ebacf7be0410671a8735fe8a995c57d96e8b616a199aedacd789ea1cedf3bebf39febb23608e37e14ea02aad9790d0bbee2f7fbf6c2c01d606570c5822159a305672138f4b4c163b256d98fba83135410810fc412ad95aadc9fd4cde58d316097b1e0ea92d26585b4bd157286429ea7a339d78ed688e575f31d14dcfa65c64f8afc46efcc2ece9e3378c40b56fac0f191c80c0aea909033c516236ebd6f178d9d349539af26305d734ab1081c93f83d8b65925b1e02c9190d659466dfcc1bc9f843ffcde46d39dbd424ad073ee404d6243e5a8c60d7f9221c401157ed18336239cdb9a3eecc6698e1df6bfe49c0972c00534fdebe32bd809ea00a058f5920485863ef16260daf45f63af01ff010bfc016346b3ad0717d1e1cd2161161b21bb8d53890672668dbfca03bb2972ed43387859fe06e66a9a7067c3b64ac8961ab2d9bb7d72677152848cb8d83a8309337474b6aadc8de58676afc37e20c9726cfd6d4621a1193eae82d6eea9b782787cbd323d3fad579529311ceb400013737eef17851c63e69d3c508f522d38d5ede79c50cd62172206014d4048573796cc3ddcaaf8643931be0a42518eb4472400d18872fb5980006c5691aa05209abaac0f81d25c96485f65cbaea3c2d64e2b3a9a79a7cef657dd58ad29c17590296d15e5ac4b5c6a5ead364688c7eb16ead48e90f4983f90b318f0f3b8972f91afc454f98838d4d33100637b20fd1b175d821e12c9473a80a3c6ecdea56d3bd62e6c7dcfaf72d387d30f1172f6808341131861a031b61d3faea438c867b5b02a98a1cee764b7e96fdaae10e18503a5aadaf066bcd18081996a6faf3c557437c34c6b6850d228e33917b966e8809440d0858e22e90134c8da8e10c1a530ba9c846619c066dc63f6e340c6796d1055cafaae00f88cda1f1a43db52860579bebddaf634cf77c96a6557ce4a30d87702b722919ad809f9adb47b54e841f18cc16badd8e298a67545fbe3acb4156647d4aa6d345e576d236988e4a5893b794d40643c527f055801864bd44041180ba044b9e90abe833077316dfe081650560e4ac1e0e35e57684b9c00229c478ecae732da8d49614b7f15a8c8552d025aeb45e2c3b9ce08ed6f3f8027bb8ac14d064d20fd10d6b82d3f5fc40bdac7d89a919a329f985519869f72ebf07cd401fd714b67dc1525a8088f558a7546ff549e7649f22c45ebf28eebbc0126ef372380d8d7fb02b51f50165bef3eb0d1a96383c807ed58c592f4ec71f5477d25d148f99bf54746476ce4437cbe8f37429f58a7718a442268f4c17c50c02af42c0a716da4db31ebe45540cb6b48a8300d58a414f999e05b2d2ecc39e46d0842048f3abd91ec057afb1677c2e36fbed3a9322035256ca195b273658eb36a1d8ede3cb3519da39eb13179cc1ca2911074c42bb21fdace13bae95775f0f62d78e08a8aa80ea58ba9ff5a2f4d6aab18f03829b761fd7cb4f71ee477d762a4d368b674b37757114c0ea03881bb0efa161aa2b736a1bdbd0dc5eba4610a1b7dc063478bf08aa562c65f6ff05de9dcf9a5b901d0ba47c8377cbe89c52d1904be2d91f069acf43c6c9823ba3e0ece612127ac5cc42fb56b72bd6e2d9562a3e4028e6bf35707443c1f9bdc6e970081104bc304349abfd2762e4918f59fa8646925e6c254239bddf2a9b2eed9d8f2b1fa7d3fbf5580f66c513c5b557d7bba07534026d479723e3032d60a97777cd918dc05a95102173bd1ae3e3fefa0db81eab02db14bbf62e2d6e993b61c345039e3c3a1c88bd73fd0712e8df51c7c015d4899c6f0da6520cdf003904769a8b65beb19c997824bcc67f1ea48f838400cb506cce5513a4adace6bc9c50aa4224b17df90a9b1ecf0ca558c6cc311b7378c6a8884c1203303b1c6ebf6691712fade8dd23be1d2ea5d61b656c26878382e37c99147ad858f9cf5023ac2f26f8b996f44108db8f9dbfa161f37bae2ac8dfc2fddaa9a85e8af6295d24794df7715882a58d392ce84fa07f0d4fbe865e0c61fc6f810dc389fb84d36b78caeff8cbef123402a45b5974408c1c48f75fb03bb36933f600ea69016546ad9b16784d4a1b6574e61582fc2add3afd520f81b3922fd3c7d0584a6cda3dc1aac1a0dabf915d45424a7bf4d0a66e74eafea7802065f4c75b4897f203a042467eae1c17fb3d36673e1009071b0aa098dd8677cc0b0df4913357679e34529b23a413a0033b5a51039a301db31f472f9f515cd5e3af74baf6e2b28cc50ac082f4a6fa0441544eb04e244cc9a96901bd44de00087edc804396c4c63c80213eb45dd0214f8ac1bf0eb728af0e13b5e21e4a6dafb2b650fdc0171d2bb9a55209655a975b7b8523e2ee9cf82ed0d774fd523696026c07e411a67415c8fd81a7e955f726d47f847018fa2c2bad287b87b2485d37d0621401eb1603ae3f3da0aff945c05eda3302b2dbe591cf14db01e5a82fb203f7a0e9e7f96258db36d3520adb007dce5696cbe03a37111e30643e11bdf316ee7e939bbe7b861436df39b4bf456ec351ba05ba061cd1fe546d4a6ccb7802b61d5078702240c2d63cb3f868f4ce883572130c8f8537787593146dadd7f1993bf0bc55a8eb0d6064f59d4e3996b4c98b8fdf7b16c6c234bb365e2e9b08ac5d4822146bf0580576ea2938385599302baa469094d0c8f708d1028bf92ee0abc698dc54cf6aa7b6a6851410e44e348abf0edae237359565e195f89425342bc6655fbec79a60d421a603dea021b1dd1b519a60f9102dfbaf68f63ab312d16b95c6a28a95e0300d7deeb3660eab2b8831e4d68e5651ad63ee55144cbddcc0780fb33ce3dc31de15c06ce97d60c01f54d3fa1c381f0d8a37aae6aa40cef3a82d3606ef96a3f338521196192c8e81bfeeaf08b96a1137cb93d462a4d96eff3bd49a8bd552e32085a35802e42592158e893eee4b02a80b55c99fdc75896a82effbdacf3d5184f383bb4eac9661a6940c06c66e4ba3f237d55ad707db8c3c612962e3aafb0a45291299ce6a29f539e5c30815de15ca668d9c0e0355ba19ad08a2fd2ff58a3971db4062c206e860039037d820867f433a2547e83cc94e2b406c63e3f8f7963c6ec2ced04b060777b3892ac3aceed635f1a05ddc4f8ffbff63a80bb13c5b08f879fb0cf4ab0241fd44b3dc6ee47e24a75e9da0297500c262040f3d61edaf5ccc92bc78d3b07ab4dbf4c4e662c4af6975920e6761b818fa4cfa42f0edc7c66c3cad433bf8080fdb9424e233255800f2d3e0ef99282bc6f0f0681c7c3a1cad5152a7bd552bfd5ab5012846a4b7be0ec79409f883b6741d8e562efa3f6c38cca2051e5f453612581050e9481d025f829c71c379904f0374e8a5b3e7d8e8a5f880fab3fb0188818c809e2649233aa3c84aed20b2b9116b84c8dd943829d34b430c7a5875c47ffada7d58fc47ecf4503ab4faeb3f7de87ff8975f5a237b4b99026212e813ca10557cb4af01560e398059b444512585b17e0ee02b8a2a298cdffb97f16b21562604ff57f53d88d57259b0d55aad1e746501bfb502bd857ae124493d4a29f572a2671ff572a2f77d0ae0ccd7196b5b2ef02d8d1168bfba66607d055bd685a5c995430a41dfcfb8b268a941dfe7904210f8f583ac3da95e4ddfd7562bcbeab57cd0ccab3e68e62bebd5f465998186ef675e4d5f961c3ea8f596e685966b061abe5fbd9abeb7ff217d46332e2d3568e6572e2d3568f52c57961c52089a792d3588f52ceb6a3dd86abd666065511d7950eb35431448acaf3fc3cc5bd7ca954314555218515f83ca18512f25277af69de8816f64bfd2e0ac00be7d2423f07f0b0bcc39b9401538dad6abea7fa7faca41a11e7425497972828dd68411b54f9526090a95d6cf654be00b07f5f55d202c85a3fa136c845958adcf04cb2d12d5bdda776badad469c58b0048c191d4c13628c49aa26c28caa5616e38f0ec6092846d6d704cbb3bed68e13b284a857d94fbd05df4fae6a539f7af04f453ed224b19ffa93cb698862b5c298d8efd3a1b6ecbbecf42714cec985737af0744a724a3d2b857a925a9dbed3fb09f52825d40b07f5a7878da717cc7b7c57b0bc47924aa5c2824955f9f8d110d593aa7542a1bea2be72e112a68af9522b93eae54454676aad33af4ac3e453a550a82d5b9abe4f7da94fa45cf8fed40497f1f4281b1a3636df8387b3fc0bcb5f49a92c51249dde2aa16a50a857b97caca1213aad4eab941d5b373d78882c41a29d4c59b620d1c58ee0b36cb46e589a56536ba5c2d13aa9deaadeae6898d426bc8cdfa7524f43a4fad43b4d68612922d40bb430f0ab6e4f33687e9084e51685aa19bf476174304b6c71022d6c4868bf2e6942893108e832fa27611995404011e387002de3f7eeb22510e74382f3bd68fcab0bf5ee6252c7ba3f158d1327a857d20bc4327e2fff643e26a1bef220dca093d37bce831b1c3f548ac649759284654459f0fbbe57cef8a4befdcf55dffe2909168b656d0af6d917cef7604e924ac9fec7258cd172f962f4d1eb6861e0db3fbd70c01c4f52557b0296db95f515eb6b75b9b538acbab27683e30aa55abd96ea08e230a94a2898fd7114a4ea7f8f42e1a050d6a65c38a9f7c749a1520f1bbfb7a94f29f5b131a16af5e0ab94503ff32b57959ad55757d2cc6bf524f5a814eac1127c317eff8919517fb262f4f1f445701945181d0c114c8c4eec30da8c0e260165c65dc7ef1db572e1d49ff955ea51a917d8fad4ea53a89ff915ead452a1c0577dac6f091c699c9c1ef5c2a9af8a4252ebf7a84fc251aa8fc2398138a7d59f50b13042140a85026988beafaf541f47f5b011f547ec56bd98c046d4a35eb0d5e9853a9d7050ef38a88a3aa16acd58732ccc554db0ece748c1c4e8a38f0610625cfd49a6faa4efe4c2a9abffea4ae5dae0f8adff6890b0686ac625704cfd0647f0fbbeef67beef7b9026098dbb2cfd5ca7f5c26102f37755a96392fdef6756bf7202ae5e382a96559d7ea53aa9be24587d7f7dffbd60df2bc7c25c044888199d95c3c20171c01713d8e8dfd32001ff737d39348c35954aa594525f55abb7384c604a49af5f384c6063fd5aa58e27d797f30d000c12568cafd1c120c16554e2a3fd2570ac52c954a91957b59e4017cee9c1d3c346d5af4e95ccf881a7c7f9fc05b3306b8f60e5e4c8d2faee3a58c6241cfb38f6c504063bc578103a4ec5a90fc24670b439aee33fc6adea95a41e7c2cab5712165b5f89bf50335f5f1febc9e9ddb25cf5224a3da8f4d520ac6a92a0360d8dad5247150ecdea67bebafc695c55aa93d5a75e4bf57b3141bda7725ad58a80f51d61c5c87a55925dc7fab1158449389ffd5e4c602b55129cd5cce32c81a37f959ad1bf55a5aa5e4befafef95a43eca7156afc259bd98c046d50b966361d559d572a00a17a0c862b4b55626d64b0d687d945a34d61fc072eba3f4d9131223f89f113b8ce07f556a46779d27a06be9546b036ce07501628cf6595c8cdf8305c862b4cf428139c016e3e9534a3d51386afb2c96d330a6583549a7df757ce1107dcffaefa5e42202ff54a566d4efaef33db1cf7ad7525daa23f8ac1ad52b61a57ef53855eae8bf048ef5abd48c95e609ea53afa5fae4f4f5b5f4bdf5718628f5dfa75e4ae0ef3aa25ea092fd5dc753953ada179880ea2e809faac205b00050802a5518dedcc0fc84c30436e68caaf1fb2376470c076195fa7d5f9da141824aa1689e9cc0d6077efd0fc6047cfb5aaae3097c150d123036d69712ea556f5d49a86702beea513444a857fd4753a52ed93f3dcab5549dd8ef34a6a64a1d3f1bb11d8449a7af9f048e3667b45f992cd5f17ba932c0f21e7e9a71e1d49ff11ec472d5d4b47ec66b8619df0adf8366603dcdb330f3e16b869a9f799ad70c5120d5fccccf60f32c960ba7be7ec6d79ff1ba12457dcdf8d60ba73ecd6309bad2fa9a17eb6b1e89f5355fbf66e6695e576a7ee6afd83cebb1046109729a1baea37a9cfa58825a2e9cfa4a915a1faa6ea078d08c0f5f57ae788825c8c675250a249baf795d8922c9899a67a1e22002609a826a6abee645c30c47c2b779f17cb1ef5bfff22fa60a5fa10da2fa195ff3493c4d335efec56a5e465fec041c68bef5342e2c412d1796a0afaea5cae473d5a6d6d3bcae5cd17db1ef692fa62ff67d0ac583705fec7b9ad627d5a616cdd7caa56ee1520417223be35bae9a67b140577d2dd52a76fcf2a548353fe375e5ca8c9ff1aa5ebed837a3e66bde83667c52f5327ef64157b53f569a1ae43aaa570a077c0fa23981b494552920081822880043440f6088c802668830c68c0e66882dc00c118211353a9821b000338405c00c11458ed1c10c31c4589f950233c40d2a3004588121c00cc6fa3dca005eed079e5061c010e08b14a383210013a31360887166743004d8618c6511fac7b2081d071361fd568c025aa5f29a4a558b42d9ef74fa4010044fdf7742598b8a3520ac63922a497d7729b156d867551a1c6ec2f103c790068a7bec7b1b57f29d6894b8c3beb77198fdf059a7cf56074fa1ebd40f6336b01a9ff4e3cdcf8d1f1c391efc1c8f23fc1b61f835c2a711be8d2b7c966dd5d711eb13fee9c3e7b1aa0f5da807623dd0f793a366c6d3dcc0f1ac1c385caa97fb84af2420d51fb1b5c6a54ac1136833569598a6fa05046b172eac576dd1be988d3980b6aa5c9a7a08932a171a9ad657bb24ddd4a6ca85e78a8b01551f589bec572e9687078887874b0f618ae6a7f59606f592c2b1e0e16952dd8025458918071e10e30058c1fffca65625c2ef95d4a3de845c621c08550fa85e58960d306764f5e031b2de59ad91f5e02bf680b08ea9daf4d99645a17846af64a480856f43a3d6af537c3f365e355a2a1a36420bc476a964a4e83126799830616ebe6781a8946a75aa2d1b3c926e926a52250323135b22b4b11dc224db65b45d6656eff67bd577fa1ef4626176e6e64faf141cbd4bb5493dac1734b64585ca35c57802bf9cd1bfd563eca13a559eaf4c095e6cc08030432b6d9c021026391836b4b058077800460821c008a165548d0e4688262264c004e102304184b16082b0e20313040f37dfcfa74ea9d47bfd4755952a0c6f6ec21b87fdffa79ea7c90f71a9cf92e205bb79d8a752a914f08c817901e4eeee2c2230209f73d383c714e10a41a02b0547283608e53afd0c96fd53fd420baa401004938058af62bdea9158af7aad5eab25ae631f7c39f88a5520b42716d0177ec5bf7c40dfe7840a082809080808685b0b6461f60b141ba4b241345a8011428c1c304140d1054c104cc4bcc4a2883d1144961baa882da0e67b2c6c05c78064dc2a711dfbbec4c29a88c11306cc066f6dac8630c93bb045c996ef07080890d3eb83410a5583624b84493c29205f68ed971d958ac7c280d86f67fc7ec765cec30b1e16f69d829aa8636c89b0b27064c2241fa3a56a51a9c2f0e6e69fc9ba0dca54d60c9e9fa1a2095b37ac9f81ad784615eed47d3fab1f9fd4a392545fa84696ca5577e051b5dc10d64f59588eb5299bba80cabf1f2752cf8a2d119e2c2c076152c873e43bf1d8184f1ee4a8d357296ceaa687859d5ee572fb1676fa180ea13d59fb89d1244608bec372788000113932b4a48911929429a7a5a22987ca658cd006a18cac4f7d5a89fb53b799ac4f6af55292c5fa68a93bf8f8ad56558552f9d7d7890b0f2a0b0b4298646366b40f74aafb64a315ba4001844937a316310f8449372a5518de7c03204306073600c78063be7c0142cb0f63bee810c60f5b6c00c40148e687324014f1e50b1553542f547c01b3a50a305b9ea8191dcc1624ec1428abfaafd65a6badb5d65a6badb5ca137046ca468dd5aa06c7cccc2b35e365c39542d1b86ab892c2715557b5d6d56a3503f542bd4559313313ba4e10426cc9626439cba6a5040d122590acb01faa89857d340bfba458d83705eabfd1a25e43b127425b5bf6a74eb18010b4b0df620a8d45a3c68d1ca8d60dcaa274f4b0495ab92cf5200a85b261c3860d1b366cd8b061c3860d1b366cd8b0b152b98eb5afd801c224df02f59282632ab78ff675d600750aeb63abf862857d29f111e513a68ac141bd3c655faa978dd697f0082b6c398d926a6bfd5e4b2553c980d5d67ae36badb556f0860543b8f1095184857d27974d615343a386c6874b542b2cac561182dd62b7d8174e874be77a552b7210b6926c93dd6285b045d8a6ef63358d4786a4b0d6da6ac5f7a562918565d970d12cac0a2949fe658aab7eb1b1d812d5fa90857defa30d17cde2d8c7f91d7fe3c2b1bf438af50171fcf7382e1d365e491bdce162edc8910327470e1e3972849023870839728c9023c7e7c8e1ca91a3a615e3a14ee15bd4903285e632f03f95ea716e5c2c009c70e3c6cb6dbc56aad50c1047ada25a4153a7b021045f49b0191fd22099f14a427dea51610d1abec5163663529d0236c395545d27d0488304f502ed67554b2c179818168dfd2336c6439874135340e82ad5cdc7033eabb66c8038a07ef538a45e354314a79f21f52a9c1550bf7a24d4af5ea71752ea552f1e37388429fbb1542ec6c1b7d6be8af5b52a8d12d5db174bc78d0e11fee686c7df54119e078d1c56846f845a47a84c65be9f5ac686061336063e0d1e8cb288f03c9e15eb521fe77b293a1ee7a5ecf89b19a240e2f1373f83082f1c9ac7791d8ff333f078e1d0bc70bed70a38afe39170743cd2eff89b97bfb25818accc5726ba7c7ff3588250df0b67051d7ff3483afee675258aefafdcbc8ed7f742daf13a5ec7eb8a8edff1b2597210daf89bd7f1d60a2cbcd0789006921410f5ddf870fcf77ddf97c5fae040fd8d077f427810c78bc9c2c45cb9e1aa4d5a5c56e6fba95f74d408c14f7dd28f65aa18368b85811f82cb36d998140b031f458362fa827ad530ae037edd62464c016152fd72c4d62d3e9415358ccbbe4f52b15833686c120e033f44c2c2c0afb971e5d898fd1d23ea05ab4920617daac3ac75cdfc8c6fac7925d9c0429b1aae24980d57526ba431f32a57d209c472d6d7fa52bf58170efc4aab1fe3a188d1353a982c618c37afe393dc06624e9dfdd9f1e0db5abdec7055315c3ac0bf71811f6b0a93ac97117cebc5653e82cf046631d62f388428558c8730c982654e362c1f9a07df36b9ecabf9243bcef05277409a3465adf8f22c2c5ca7068d99d6e7a271815eea9095efd80747fb35ac1a34665a5fea183b26592fb58c0dea69becf7ea171b90db0857259d7b1dfcf8d8e771dae96db586c8910e56558a75aa696b979f0ffe6a686f66b2ceb6f5ce02be9c78065ca3069f95cac2b59ea0e758bb56161d5821f4b40a8e36f3ea97e517d12cec2c0afffc5fab01efc6a85fd71fb3df8c52a161606d62fb58c9622ac0f0bf5594f9da83e09f55285532c0c7c319ab2589f1daccf162b84f5a92f298e14bb22848d3330202a5518dedce4d81fcb848da12c1722c047d17c3fe302ed67ab0567a09e956a7d1f3803f54a6a8d4853ec8f07812a2c362cec072c67d57888fb7e6afe53a96c84618d9b1b1aff362f1f53f3f22f07667d5830ebb31a1dd5ca12feccccab5aa1cb5aff25dd8c200d97cbe562d58460abc6a96583a6090ea779c1582dd6ccaab59ac17a5924d6b6b0ef4f9c857dff4a9aa9f5e4e2d2b22b2958ae1a911cc9127aeb86e7fb55ce0baad70baa2fe6311bc29305a760790f2594f8e2add60b04df9b802038f2c4101002b950366e78f480e58029d809045390e459c0a4e0c211e4f9923c0bd8e954eb176322045fb126c2fa6e4fefa7d70f21f8dfe9671e4b90eaf4c25961b53afd15950a69e55f2cf5ab179620141016063e0f0b8b251126791637df2389b0c99493f5b9eaf8192195160694c343e408482695c5862a56cc8a1096c475aaaf0f1f4b1048f3c25981c6878f44e3c3d7952868fe4af8345e2a241a6ff336af2b364fe3852508a4f9ff1e3c40df8fcddb1935ef35af574e0ec2d5876ff3493e0647f3b606fc9a9a9a9a9a9a1c9a1935ac19ae2b3db685a96ab86036165b224cf2307ae47c0c8730c9c3807d8d13347e86cb092d1f44e39d86e6a5dad607a4f1f69dc6a682de93d40a335cebdd9c7bcfcd3c2fb662eabdcbbddbc4c934d13c954b39e86091ea292eb53cd9d494d7d4138e3aa3aa7c803ffe09018a90b22ad509fc6af51c5674eb75053085eaa92127af4155afb59e4e402ccb7aadf5b34b6a4d555beb87b262abad298b034aada89a725b69c4cfa250b099afa0b5e0960ad66a672a0f6c5df9a982f6c3b526d95aad2a8ac5b1d5a2405b6b606b45d9ba42cd51eb67551b387da9191587107b43f55503545b51b656fbcdaa056756406cab1a51d5538faf484d590bf67c33d6d6588d4f9501ec6125f98cac836e9ba8e007034fb67e7e2240754175eb61187af50a565b715055f6845aedc9daafd61f75a7aa6acb5a6b5142d5046b415b793e91a5435542a5a1b1c312514f564705eb57dd1ad99fa9a05b13e54fab3c7ceeda40f52357d8afa22a98aa34f52bc25a95ecab405fb5167cdb5ac1eaa9d6140fd833024fb53328954dd52aabd6aae043d59505bfa5fab2bb5a3f9dba62d9aa4a557bc48a6045b0217cfe79ada9286b2b0d187ea0d561adada80adaeaaa47659d6c3dd5193f389628c55ad95a53b552b6ae5055b57eb68275490dc10ab13c2ccba6eca97ed5565b653504cbb229b07ea7afd69ffa55585dd556ad754815c1ce7ca9aa02abad55562b0a87b551415b4f60156259f5b322d4efb316b46015626ddd6167eaaaaa405b6b95d510ec4ceaaba00aacb5fe7cbd394145e443e154bfbaaae04d5dad642c187044a80000abb5289bc235bcc1c35a52abadb6d6aae329d7f90b582a191770e0ef821263c2b8e0fac2062d5c1730204a0bbc7024570afc480911e0a3ab854351a20a034071e5272b289144c6e80239d244091176b0e4f2e011a2c4cd8e903be3899f16d0f4f871cd549f16d4317c68651cc0e7c9709116041174685d6c8e1709c839cf73020a5c72c01881f6ad113e070de752838ad34e3ff88337b87feeee3cb3bc67baddac34cfbb94e63445e2ee3b4e02cee5238ce6229e37f229c6ed0ce489bcdee6cd2c335aa17077113c1c01c8fd66d676723bd4bd3682cadd65d84dd1a9887597d4788abb5128c2179d8a77df724653f3e17c1768027968029ba04ef89ae2410d61029a4ea756aa8414d4002ad103e66831f1046a004d6093158e191e373734ad5c005ff844606d544b954405849af986dccc9c3ed0a49aa2c9c6941a563f9f0174d0eaa2e95446d3a9b5c3044dadfdba40d3ea66864d0d39ed9c84d07283a583081bc01d9a6a5c35ae4fc76647756353860d2714544029225fcd4744c544d3e7a46707358382a2e963a1768039ac9eaf822d306726c90985940ba81da8195452cb46cbc6ea668523e5c237860da81d9f171b4e5834919004e67e464208a0ac5583b201eba7f3098146ad1c289b6a2185932a3add985151acef04b6beefb35fea537dab191ee0978a92a2516334639342a5ea970281803b6055e9f8706a588d707aa269061435d8a0401cd5900d601736bc66ef65902103146c2182301ec062773bfa610b92a0148afc64140c1cec9810d6a85230bcf0a4a88c108819c1083ef01441c41123ba3881161dc0c0041ae0c48e58c210bbed834e799003325c94400c072280858618a6bc20e54991922423889003471515c0d20006d030c32c041e191d6146172ca8926b2a830c1ff4600a0c3970d89ca00357640003139080037ce84100b3185c68e14911911621b80204162862890a52a045073880810b2480091d70b82107644e50020c5ca08a076469c22511121b1a3230f8a20b0f70a0025448a1801f78c0e52c862930b4408464882c6643a345083c6080237ec882cb1854204a826488c3e00b2d4200e68a0a502185031400440f59a8a840546488ec07484f8c860de865c0e00b1680f1c0151ca800150e5000103ff4c0431617cca84c4961471423084d98d4cc58018f2d3be820a5051e04f0a0438f298a55191f19768c53102c10544f800bb00ab04e541fb6e7e3a9224be743e17b1c23581e1527b5e3d3516fc01c15c7aa060d1a5f08d6b46684343b5a3a583c666c5629152a6551a8d3e9ab8eca3243c2079a52abd40a9ca1f249adbe105a483514bd5430d48c0a086cd554b0859a81bc8fe6b301b7b8c18826d40c9b5a813934210031a06986855609e6cca8c102c13945012b8a88069a8a3419b2b9c182336093980f9a6260c309616aa82799cd08476ab0e08e4f47575d464e2894a009851ac128cb8d9a201c3e253504a049c7aa85968d960d2b496ee8547aa6074d44f64a090d8830b5fa684e4aa00076a84105ce4085506b7ed4200304ac4e3041165519226658d6e9884d4a08c804c3e0c5861a27d84c9299125053c01352426ca080124a6c58800f38429e2c3464930158d1c4dad98eb881c6474d6581c73724a5c3a786999410d48c6ac6f743a4e40b6126490f9a6652443447aa25384892ccac96b48e4a221468ca01e6f8cca064356850332c24af1812d034a35a22424d87a6d3179e2a6a05d4cc6908ea042341acddca04d512d44c4a07cdd0aa0758b4ca618347093b709ca042810994126a985189a91f343f7e888c522d30b5e324fb7c562dd0843f925aa566c09d131310e8483de17cae130e9e958afc84201972a39ac1b1faa941a888054405037b803d500b683a6106387442e124c2aa07c8a3a6e70bc1862671a77eb64f07fc4e32bc6208e202196d0003c27cf1d280053031002c333069251b2669314c79410a0b518e8c9aa8b04406f4035602090078f00a0e9401860d64908211125e3b74a4b880200b2712b00292146421dc68b15056544145145d9c60620925b818200acc841df10823908eca10238cda0b60e00003b36c03840fa6cdc8909954971fb6fcac50b08502bac884f8e4b0c19a51a578e04c40025370f9a44805a2244864407a62346c66d08007e8410001a0e145100b3480010bc0d2020b3f3e3736401184d9010b468af8ac522807e440c503103044103ac890c4010ee0c3932223434c68d5207c40b06540328f033b0694816dc1c98b9a027b026b8255095a62ac08ac16b60aeb80af0156094caa3dea08aa104e3c6874d81cf646b56153e344c3dad4109c6159a719bb52a96c2a75b260fd6a45b9fb8c0f091ede88e167ed6ed44c753bb8cca489eeea7713c66fb868f3511ede54096f4c772fc1c31ba1f006c8696fcb1f7bd83f2e4d8d9aafcddfe60fe9a537bd38f2aff9bb57bbc971930a6fbe30070f68b4bfa126c6f13ef645883cac089147cd1bae511145118692440581f92928ccd1e43a15d114ef7d753be9d60e7ed66b88a3080e186a6e8dcbff9dd1d738971f836132dd658d63e646185ea278dfd737c494280e6f20e0c696c7ed1b8acfb7e5c7a479ea4fb32dbd5baf60ab1dbc4b37d6e7b5b983eeb1540adc7d04f7930dace0a181bb909e37f269ba234f10220448c88f101f2141840011f243880f213d42788408010202fa01f2010a020404e807900fa01e201e20213f403f3f3f3e3f417e80fcfcf8f1f1d3f3c3f323c407c8e7c7c7c727880f109f1f3e3e7c7a7c787c8404010af213c4274890204082fc08e223484f109e2042800001f901e20324081020407e00f101a407080f10213f807efcfcf0f911e407901f3f7ef8f8d1f383e787101f403e7e7cf8f808e203888f1f3e7cf8e8f1c1e343480f50cf4f8f4f4f901e203d3f7a7cf4f4f4f0f408e101e2f9e1f1e109c20384e7078f0f9e1e1e1e1e261e77b38608742ae6762815771767726660b0197e86999c1919fe735e86ffff77771d77146235aebccca8c611f797853544a8910a69f8c05f36f3b26dc3e7ff0c6de36e66fabf6df8a4611cb96fae88ab4811773f21364548a308f7d7e6ded5feb5f9283e65ba4477b9f6b6fc7b97dfdd6126b8cfc87edf2fb11269a25747630a0c321a3c7ed6eedee568dcf09795b8f6bdf2e2f4bc7be724cd698a8497d33407d97049d6ae10209b2edc5d1477ce66f34468c3c429277632c2dd713cb4610a6d807c89eeb2ed759e46c5e7c7e7f88081f6f886def4316e3f6a3e36cfdf7a977bd946cd199359de74966e3dcbe5d466b1a0f864ba391e1b6a16dd221474441ef10240f44eafce02ee264ce131614aed8d3c151e186cbb5a15ee0e000f4324cf4205770b867d813dad8ffd31c408307ee2410bdc8b18a2c96b828e78cd818bbb09a1fb37c6dd4b16aca07281d15c447b349b5bf72406eb6d5d0ecfb429da76351714511ca93439c556be577b5c9a4b66f979ffd6e5f0deb7ce3420d6e3f344887c0fec89fca3c0802716a08019dc5df490a6cc4e9be9ad0958538321308052d1050f05431f0f0ea043054048f1021c0510f95220e6470e0320b1c3142390ef4b0f37247662182ac3879caf090cb8be38f241b1029d1e9f0e4d434c62b0e2841638f0f87450c01e05b52161c46c7c5164101e110425906819a0f50d392264020633f8a12756c1efc38c0046988016b2e488f1a17478a20730247004891d61d81a5460a949132798180a6d60c304e1882b2860c28921225f5809744181088ea6bc00024370618f700197087041c4083c3231b6862270bcb003195c475d64616d597c088e20c114a626acb04db488b1831c3a788d2ea8c2f64c800b0c1c21cde0022190c2e2e8c11546a44b1491c41428aa184d1ca101202a10b203094cd416b020491216280097d90b90a81d20c009524c0102115c20204485028b20471cc062082a4576a840e400430e40c07004073490a5f2a440fa4289de0a6e7857ea141dac9864906206251c760d42406e89021801064f85b39a90828a8372878e071a9495060827664e02706420658aebc00a2a21fc20a84a084620f916377440d5c38e27b00adc2de085051868072ca2e832e408b80189801e315604c10422ae258927269490c3881712209e2bc10880987862064db81c3ff281061f219081065374ee405f8808810c1e48dd8080bb0835a42084a98c10ab2cee5f144c35e00014b5561963c68869222c2304481c11a30c0888600239a4b84007ce09a38c02ec504516ab6ca4250b1b9441431530509540d8eca8c1176590630420023ab8c1c1cce0a20ca205f4e85100530341023165885f4c09ad38018521a82ccab05180145c70451819b0406105193f00811712a2e09e1e09544146170350912983819a1b4a5290610505867441f8d0e5082da0204301651430d342cb9e410826c8f021015d582089296030850f4890b1bbf801af872e4683058420e385218a70d8e1072a4a88b203192978a08a32684032c07394850c1202509b9dce2b2ae870858c990844208a2e0b28a3698a3d06183b7041992058b0811c94708e5182259eb049a199a5a007e51854a8008ab3c70cb526b24c19834b104a4984a00224786008a43170e0c00a864cd841021050d1640ca52c5888e9620457a43064680ca30700dd8270429007f440648c206000992922c70950c000c818385c72d00204446080883072c6f0136c1c3e40820db20ef41023065b86a041065a52c4131e627c4003a1103e4e82c0c28f0d311c6003111a3d2a1208410b2d3186604016443f1c613aca95018a21030d9078f10080031d3e208618d70055a0f0218a0478200261889184042b4c81f54431e2061b8891c3439811a2d882450c225f88617301167e00635432418b8b20f4a00c9e03c4b840155c3c1113042e9ed09191a303465c01c6dd9d8c2c82908118ecd0e22bb2034917eeee34332a938a6f68c1bd2ef570af555aac3ac3042bcbdb8bc1702eeb9936c518acb6d3b3122b9118478a1bbdb53a103260e508894809d1d04c09d1d14ccae8e48b18f9180cc5a768dbd578a76ec9f50e357de7ee3b1ea276709e37f21bbd39b1d4a988e6db4dcb9dde0def9b6ebd43e2d2c73e6da7231fccb800230461a037e0a2898b0ef8204a15301de01f8091238b28fa0b5906d20c2241247821841b3e3da2b8c2e6a5840754414b40802d6ab45001d529b1c0e80a1a377602b0050a92482f408200c0508478a5ba2d9ac108257c09f88145471520f092022b402e5424d082073600c0fe62a7e606d0185f17055889043052fde6c93783147e10460a9c20e20628d00d4a0c248d501dc4814e6e84010728c0a8010039e82067c09d17440867d540035e7b74dc45c8d2e41fcd11f7a2774f8571772c6c51137777797822da379fb659cf2c77723b14a799e4f92246feee1397f865fa6a7797e2219801af99e74e73ed86205217772624dc79b6b87b16f7cde49e4e39ddd12920cddd5f2188e4feaab9eb702772076dc0ef73bfe15ec3dd69b887ee5ee3ee3380883d3a1aefde79dedc51ce4829b6d9764155f78287d60b5c6612973c62109147072888aeedd87a787882f4dc5acd878ff2a7c7e6a3934d51e37d5352eca55b9b379ceee45b7ac99d1497a7b9abede05cc6edb83b0d1220db8e92cb69127767b90a674bdfe6a9d34fdb9786c3c3eac5dd69786881dccfbb9473383d95a076c0ddab87f50a77cfe161f581bbdbf0b0bec0bd08132e5133c51a35752a9639b743e2a5d86b13f6283e9fc8e3da97e72ee3f4c7dcdd877b45dd5d8787358bbbeb5424d19e9ec1bbf32e3d2d4d91d01ebd8fe6a7c560ba9bf2d09d4cb6cf9dc650685fe2f3bca4bbbfbbadb9fb8d87f5752aee9c6de5ee2a4f739ae247ef97184d771ba71bc5397cea72e66ff3a6fb6fa8b9f7cda5d9f6b7005e8f70f71d1ebacedd693cf424dcfd86873e80129fe6b5bdc6bb57fbb7e547b3b9eff7d29cc3399d8a1687bbf3f0d061ee6ee3a10fb93b2f9fb6142fe5ef6d93bcba4bea5424f1d28c77d31d32d53b643ecfbbd34b6f5a334f774fb93bcadd4fad0f74773be304172cffd2d434fa37f3041996ca634110234646ad2e2d5ac02c5ac0570eaaefe7ca151aec7f4f437df0af448194fa99bf9245cb079d90507f7a1a56ffba92c3079d5e576858bd4e8f7ad1807ad54bcb077d9f7ad1807a9d5edffbd380aa282b3686b5f5fb98b0bc0acbfee95465b1a106fb16ac0f825f8d00cbb1b894ea2ba1aa7ce30a0441d0053ef8bd4af5f54bfa6a7df0fbae24d547815796ecabf6f7aa577daa2b29d56bd32ca949a9ff9450ab97120afc93eac1d7a7b2af847a3968df5fa99ebcb0bcd624d5afdeb258be7a3fbdd20d3eeab5648f7ca0abcacda8847a1f675e55ae54b91951affd2da9a3eaabaf2a57c6d4eb747af015657a496d962f80192de599d1d75d9c6aa169b3c418d51aa7b79cea1ecde8a805e601e0c2fd16002f017821003c8ed3dd46f1d289750ee3764e89f1466f7a77b7df19cdc191bb2374a7cd1ccaf80eefaabc367360c25c4ab17e8ce2de69a637cdba1cde3908e5a072176fa8d94bb70e4ebf57dbd996ceac715aa277df4b6eb344efd5ed705881fbed1ae1dbd5e1dbd5612be6914c86840393a77829cf7080e2e9124e75d88aa96fd7c8881322453adde51aee666274d62b2f4eb7bea71123d88a79f439d88a79a4bb7809111f3fa2e48d127d0ed04e77310e5b318feebe43e5c54bca8b8b96cc7af7c4e952d6cd986ece0812918d8f304e9182b2a8442742526e00e3862f4e65ca3b11927283016e38dda94cf99cd27c18a688657a79282f67273219122fa3378c58df50f3869a387d979ce715b2210c9a712e6b1bb8b868834e44b10d47dcdd867cf26ad8c2c534bf8fc7b51a9adcc71a7886a4b84c73d318f78c8e4d45b8d8bbbc1497e82ee7735113cec58fe134e760df43ef36a189c779d8f6396f2c5eb8d77e9b803e162ddf3bd12746a7c626969bf3bc42348c1169586267f466eaadb199e6194e5fdc0c657a66e599711aa7b83d9b010bc7337099cdc0e4e63e77b999be3823e3799772aad3dd341b218273265279e65c4e6d3c8ce6ad67338c535b182fe215da11b1bcf262897917b765f0c25d8c5d191a20034de7f3f72ee70c21795e3e71edf7ae26c34dde64ac7c41cd9c152dd70acd734ebf57fb8dada850b327a6d6d3a107a4ebeddd2dbd2f966826b1662a23c64abc615f3271a9d570f939341a13ce5dc438b531bd184bb3b97471b8a6cddca7d9c60b93e27dcfad61af6fc8f3c263ea9de7ce4674de9d9168bc25ee222d89158c531bcf71f9e735378a97b07017699ae93ac1b98b2e1581bb99b81a0b4b46ee22cd4879e62548446c4fd06ba69705da9767c62d85dabc2dfdb81b93536c65d7c5ef6b349bdb2ccd5d15f3fc5d93bb8877282f9f3b20b1dce4c5a5894d9b59e6dc4ea583535dd02e81bbfb799a4a3b4818a7b6190fa3431bc5b911a766698408895990c990743ba59c9bec23fb468f71f7999ec2f7c5e56ca69b765a9bb3d9d6bba4f26aa3da6c63ad612e52a1a54038ef4c87b1d20c4a490445a3595f9c2a91b94969a2d84a4e6d254e6dfa76cf2437dd4c48f4ed9eb6d406f737a2653224224c39485fa61ce4d3b33bcf4b9271c76d9357058bf3bc332eee5598964c5cceaa18dd2a3c3872df5a15550f2b41d16d31445416e9b4b88b39f3b509dbfcac3f072f99e76facbf8799e87062d6017177715f93cc5a5fddc34a9f8b740f3391c9906c39a73a5ddec2dd7b5869b6b1ce5c3216f3a33dc836948bf330fada447369163212e3e1f1f909627ba2777a8d3e67c4291ae6c2b8e78ac8e95cccd9b897b8779e62dccf254e28a78b354ca23897cba4d217b134cb9df334794a4c4a40ca9b536a255d2089c95d8cf54e9d2e63f34caf8eedc4fa3c4d5e1290ce49eea2364d2c4af49a5bdc455e46cd9a8b38b74d5eec73609a87511c0f5cdc24b6d9f063a5bb4d1c4e8abb8bb8d0dd71edf6455f1e5279e6db16b1bcf9bc49b9c15c44b295b185b1f56ea8a9cdcd83bdadf6a8f94844d29cc4b6e3ee1ac7c0ddfdbc3b6fdc347a887f44a36f52b2a0dc1213cf7c4f8c3e11f1be8f7729059339d5e697efee9ecb413c24bdd82922491c5923e32ee2c8f4625dc3c25d4c917858dbda3cb3c6e9d55ba748b59a4a3c73be53f8ad8295ee3efa9dcbab7466a3c7b87d63b42ddcc5cd84d18ca2e6f3f0d18bbdf373ce13ebcb7bcca3797117695a441a4d9b34203f75656658ccb4cc8c66b01c7a33cb18ac7017b5b943cd8d667233211961c2a816e76134065a0c412e0f2986eaee62799667a642021753ccc3686f671ccfdd651e52e1c1451a4e334943cd1b8db6ef631c121523f1bca899cb2815176928c6f16829bea1f9fc2961dc457df7142e8f5e52e74c4e619a72a3b3a3a4ef7e9ce6dca7f9319a3fef110632230c5c60c031f1108610ca0f0f5f08e3be6ff9c27e01002e608c7eefe6943cefe682961b6adaf2bb407301a674ee82de96a578f15d44257da5e8c4129f2726d13b4a81e9a0196d81cca7f7dc91e6d8c2177711dddd6ea849b6c0452c51fcb9765f9b630b52ce9c8dcaf2b6f0a9cec4e914a3bc272c8c71e76116b4b86f33bde74e6b1c79f1eb1b6aea74f7ccbc9dc91f43168cfcbe2d4729b3519ceb5ddd794933ca14ee62695e5d0c35bf77a2f961463c8ca273d4cce5ccd7e6cd6994253c8c8e5152a430ee48e65dbaff19c5a9908748524a143fb6dd4f3312eedda3303b3cdae2aeb1be484740eeeeb5abd15d69e625285becd23493af4db8a70e0a93fbc69b34d3adff7e9a6d38857d120fa1c0fcccd9088abb3bc9845f9bffbfc21747c715b6b8bbd83b6fa8b9024ecce16dfe631c52b80213a3322e92e62d9ff7f5fd231e1a79116fa8896ed4cc3a2326f7dfb8f73ba346372e626d428951f36e349337d454d237f78f71484fcc53a7bb45687d75bdd4d431e12734bdb5c9847fe7a227404f542ef6769117628971e47d7cdea22a448c66a534172d51d4049f576f268c3ece6512f6452138b9c2c5126332a73abdc36d949751a58f6ddc836533c5bad7263eefd1e3340bbdde658cde8ddead51a2c738a43387b7a9996eeec4282fcf7818358244c4f6a44c3389a1a018c7db79c9d4e13493f8dc9518471e95379f4a69e69544504a9348495f24257d7792de36cf8b9ab630f89c6dac9b78d182ee9ad0b6b9d3ab6bc2e3bd2661ce3a2663d21dc9040b17a9d0987061c2e4cec4a897e6ad4d1c13d86ea64299990a6154e82252a16db3c42939c3fae2c874a753a1490529e45581c7ddcfab672ae8725a12b10097bb33eb72aa9b299dbbdaac774f0c05939844ef52798dccced3aca5d84897dbd566e7e561b444f10c35cb8bcfad53d356e6193eaf9eed5d3e4fac6768366f38c569162acfac671aa7999c9d179766695e5d89e21cdebaa7b719a4a4af139c66a15dcd4c379ac919de77c94c75467438672261dcbb3a4ca2f7bc419834539ddeedbb8f84d06c9eb797e67d4bf4e690ce9b4b339a6d28d1904c86a4c40b39864a9adcdd95d07497372a0172acd35ddceba0f9d1274a6edcfd96ba1d0d979994d1929071777d7949c2f8a363121dd617a7242e79f93c833c4c5263888748b6707731cd4f4bb3d0795ed24442de7433e1cffb9320e9e2ee222e33b9cd5c0d89d12ddde9fe869ab8f6b99c7e4eb1151d344412a278c806435d441e464bf4a63b1263dec6b527b14ee72e9a8287433417f13675d6e532f93a68464b8c4b933477474345dcff739111bc3b2f0b39bccd1e6662a48799f4b0122e33a97b58a9b6d3425bb84885a6f1edea66ba8b71b38db5d0142e52a1096d61326d38d50919b90b7d50190fc1c3a030e7692a057599ed2026910a4dcff62ee3b6c99b6dac8396cc94d29bc369186483cb9bde74bb5989ad9869c6eddd9df54e9deee2f45d229321e132934690747923d9285ec2585f9c11d617a7c3249a51d2b4d976faee3b74f72d37bacb42360c45a7374582493417c964489777d312b74dde6c9b491bbd3c23b1cff1f103e72e8a73179de17357e65c9a5173869ab96c4389b20d3567256a2acd6644454a69e66d8ca639e9e666b8cc42b8d6bb4e54c0b7f46e23b844b1de283675268953bc64deb6ceb9d9be33cd7473256a32c1bade9e21c1676d9787cef3921b2b91e6c6564c3d9b31dd1c109f1dd3cde9349b29de285112739f3b273992c9c82edd1a25224262167ef858229223a5c8644096508cf7dd1aa72f6eb6519c5eddd0d6f866967ba7c3e9bb64e79cced025292e2fc619414284c42c68d2d4a8996e266c04684784c42c50e1f15132e112251aa9f8f8f143807630b83fc0dd691e16998197a7b9e3e9117de8641b6aeae0f392bac4e8cdd4fae2f6357a22936d8df73d65b21cc9847399dcbb9cde50b37775e7dd6df2c6604472a4148db16deb4cfa04f1b104a72f0ea7ef921c29a53467b319d3cd9de7258176335d8e9492e634c5255169ce88f8400428a3b41956ba9bf637d454babdf20e1143888743ba0c8132c4cf5d8937be95bbdc2e6732dddc5fdcfe14ef8bd3db6bf36557c87462ef3c7736ad314996fae2803c948520444c0c4d730df6dbd448223a0ab109097d5fa026770732729c3651d3f6681e8142f8c1c2c5f3bce4fdd1e2e585f263a4bbe97e14e7cc7b92b97ce4ee4a3cf4e9810f17f7dd49a2a6cee4eb6de6306e9b6836517314b7f9254e339a713cbdd317e7837347efdb32121f180fa39feef2e7bd6f7a3373416810448820475ca4c53ea724fa1c180da7bb7dd19ba9fffe0d35972e8e4477b65d3984769e5788f6f7f346cd477738725f9d36cb256d069901648ae7e5d79777bfbcb811c8520804e62ee69e34f7cde5bdbbe87ddc6d9753dcc6ddcc12e37eece047123f96b88b68d6edee97e8ade5fd399f93e27dcfd738974f3cf441e64b14fba880bb47f1d087cd47e8e2dee52fd3fc39785fd23c75bbf2afb9cdcf49f112466fa88923f7cd5d5cfbdecdbd7857e809e3ee2c78d863a5c7049e13f09cc0dd8f3ce4c912f2f0b858e2126314d73ee75b2da7bad7268a9752bcd18ba67829dffebc3bf43a79b1c43a5d4ecfd3e4f5ce9aa975d63dc66dbdcb29bea1b7fc12bd1fcbfbd36ca6baf2e2f486de47cd87a1bbd7e66b1387249638c5af51f3e6300ee9c5b277ea5d369770aafbf4b589dbdfab3dbedd346f8d9a5f62f29e3a5ce225139338fd5e8a7755bec49bc43a26243b5bc46839524a89e223bdc3db966137c5bcd71827d9a140b8a373519bf8865e1ca977af7579eb5be2adf1ae0a4e710b3b462e6e14eb7107266e21c69c0849d138d531e16d8a3711c3442e252671fae3a14873b1c43bc54b252ecda59cea9e4613558e7369ded7dc266ae24813c7fbd2bcba5d159c22e98411d1fc5a89cce77e7d71a10e530fbd9f66dbcdc4295ecada7c74a3bb4c92f751bc64de1ee390748c5cc41b9fba5c2651d38990943f2f999be8c0dcc52739633ce78be3944433a9836634a70871a3586ba41c2347818c1299cf3f4f73ebc7e92d312649f492e86e89872870114bbc71aa4bf146cdc7b5cffbf1592bb1de3ded310e0985008428f0b8bbb8cdcfdb96bf77799fd1bfafcdc7a9de5d12d7be779e37c528be7d0e3e6b17475e9ca2bbd79757e2d4d4b87c8c430a4ff062f7b99cdafeefeb8bfb6d3e265153e33c3cc18b8b29dea769d368361f8a87271879891fc5b8f678e9e25e7f0fbdb9cf19c9dd9d78182be3369cde93c4e5e79d661c895e1d9c16f130e6c5ddc5bb6ff937d44c33f918b795c87c8e616c8abb889a17476e9d7584c2d80d16ee35f2eaf4dbf22b91f7fcfb5a77712936124b14eb47cd8fdd5073ef722a62d1843dde696de25cd6af2fef718a7148214c27a239c5ba9c71dae47defe2366e9fe7853dcacb8f7148210cc8ddc512a3662ec51b978f6bbf334afe6b131ee390dc7d050f4d4881c6d8f61bc529b6d9ccf271dbe4bd7ebc730ea7bfd3acd3edf4ef8c7e2e3fe751a21297468cc460449872d036736693991123e7798570cedce80e8a4c66e466dad02cf4fa3e12112239520abe5dad71ce8891b1858dbb99983763bab9ad719923a530e5a05e7a4dd2b46114d77cfcd8e5f0366718dbc212b6ec73776db82c4157a2589770a4849b9d661d3ef126618ca358a7779a842f371e92c045a442c37b4762264c028e0420f71cc944822aec1146a442d31b6b7d6729b669f4a67b869752ac713a2bf13673e619f608c2dd5da4425b9ae19a9992bab6d3e86e86c934db64b28dcff31ad160788c8f528cf232908f9edd467739778d68a6ebe47362403e7a769f9362d3666e1497444230222466a1c425bacb413ac5b99d4633996225329f67ce35bc646ef2ceb499e2120ad668d6699cee6b2406b4fb1ca01dac6733774651f366a639c5b5dcae4889cc67d0de659c33b791262546b3a953cc0291149b29de55990538bd24fae477644cbf2d8b5468b627286ec2749dd060f85ead77eaf3e6a4a60e978f7739df7e7c71a358e74c7cbbb9e7c574d08cc2b43904c5280a52998bf0ed1ae522283b17d9d8d009d9d85862a24a506c6a78d143efdf7447e2749b35b8b87befd2683635ce5e794f9cc669129b1a4464a91cd4086d5237363450e0b848a7226ea631b6c96439dc86860a363486d0987177b1f7328c6de92ee8c597a5394d314d264b33ad8799e84ca4750b78ebdb44df7d87d29c4429e720bd82ee61269b091745b109a3b009a7b8ff545cbd29b1a921e3ee3b9f486c6aacd066ce6535575c2967dc8ccc3adb763ef5c569897755dc1dc86d66c02e2e65e0ee363469ba2b79f9b4bdac49b6a136343436ad226c5a5adc6bb06919ed0059da21536d737755c525aef2438e8a9e1021091545516126657492d128473a26ddb8f1518af191de9dba747704a5b70bbaa16679e6257833218132945e7ce65368d4e5d22528d63d11e2e1225d2e5d8234e4634926cb91526c667e6c6672dc73249383516c5654d8ac6a50e6584e0f2bc16c5655dc7f5fa567c2688e64b25179e17e4b2f4e284d91283d4182348018dcdd749b9416152ca7d8ca797edeafb7597b9b89d33a3a15331944f4117fe8e890daccedec90daccc9641a5504ea8a0dea8aabc70565859e6a51b58295550203b815f72a5cd89c409b938f1791b8cc68f5012a42648857991020f7fa537dbac4604af9afc4669fa361255cf91c580f2bcd7ab5d9ac975ed4ac891a3541191429f2e98eb4f9c4f80d3587dcd0b7f9906cbe232308e3fe3270872136193d7d36b68bbb8d8d8dc17d6774a3f868684c22de7dcbc7f888f6b6acc91f87306e672129bc12bd3c2327516ae526af2dcac65a7c3193a8a9d3f492af4dbc74f597e82d6f3e6ffab913a719a77517d5b56b9eb5bbaf7813f1e7e420d56a57f7f8bc2f6eac71db7c8d5e9c3b6f8a7318a733737fc95d99532a341a0c5fa2f76a333f3abe48c356cc14be8517b199ee1225d1bcb1a642a3c1304333ede293dc28de55c11acde4e74b9ae82e97cbe4eb4cf230bacff334a920197a222493e91e569acdce13f36432994ca761a0028399f2ced3cc799d7cdbbdce59bf1e5fccd11b356fba49acd338e56134ca8ba7d661ac4485a694f312bdd328d66816a2c1f058ebd4f2d6ba4c33ef51221d26c97b4b14931a97dfe4cb9d8bc69d8b6e6650ce48bda0121be927b31d48e1eeae149e369b6d7cab07a841dc1d04e2fe75e5eaa578093f6abe0ec294e8ce76840e02a0039abfac4477369c12d1418f0e68fc659b09e7fce7bc6cd6dbd564322d74434d1da5c4649a9320a1d84a4e9394514aac54a226a93114a2124a5089376a3a29314edff3a88512ef6ba6fbea3094281b1f6d9d9b6cbd1b2a312f972812a21ca4e0e27977a89933b7c68fe2ef9dbf73d18b2e8c66a59b4b7779e9dafebeedc43a0fbd0ca3e6f9b9bc770e7a31636d7ebafb8d6fbd9bd38f77b947f4e2511efabfafdb3d8a7559a6a4cd34dde59efe5ca67809f3f2921769259ee17447628d9a34fce82573728aad9827a427fc2ef7de46f4ae275090bcf045e312a487820469440245c923097aa4a3284e5e44b12e7f3070fd952b5fe22f62e4af5cf9184c9b22c6a9ade8c512cda91de91d8df6f735d6bb1c5a2fbdb834f50dcde77d1aed758eb6d9322e6fba73414fa3edb20b33fcdf7d873ea74c8d3ee78848cec41de9ae137cbb45feff65f834dbfe334aa3d1903c00fed4e52e13be5bef5d7e5bd6db44773308c10c6e104d30e165a539cbd1369c7e4ef10b779f7868a83cf3928b876ea859bb258a754c485022dd44a7a24e8e64dac9914c3b37d4bcddad65b21cc934c6c8fd65d98612e95404b22493c964ee7e3d94810d0e64e9f37e7ca6d99662dd794bdc2b2f86a2cb21349b37a1d2bc4639bc89d0dd0d07e5cc7b0a1d21c1e57502e5ccd9a8c42551898d4a9c96184a6904833130f8226acd749d885f128925ded908bf58e22fd3cbc3e9f9e979434dadefc6375bcee563de2ba599174377b0174dbdcd1c7c5ebd7159dbe9f3c4a436736fcbaf718fbc377cc6f4cd29e9bb61af5353c784e4bc3c8c2ec945e769ee2322349b4279282395689471e7a2dd93f2cc47689432c54b4f72510b42a599596042d23bd38b04ef9b974ca328ee164a89cfd364c2c345252eb3d0511730e8c2025d289de71532020a7274246e25d4c4385e46955eeced14eb72aafb5712a0205abf1e28c8d1117a7bd104252dd48644ac949750536f9d9abb54f75be3f2ccb0d761ac947b27df448b5aa64bf8379f8673f988f6224e8753dcb6a13ba36f333f067baccf9be4459a8f1f3bda6bf369b4dfb7dca5484fc3b98b9258a7692f6e9dcf8c7e264ddc7eda2e6d262d96437baceb6d8d53bccd73639b2d6b13bd3852e3257c9a27a9b37137f37c4d9a5b9ba4a91b89d8325efafbbdda6b9cea700afbbf4e9e66048988ce9517a7585f52ef926c5967eff2e7db7ea61cf4629a7338a7839a7f59d8f7b4a1e6636df2ae0c09d293a2175f4c71ce8b4b5ea4d18e3eb699709983bd7e51639cbe387d9de4dcf2febdd39d37869afac521a29dad445113b7735189cff30a0da199cc484419499718179158778473b788c4ba2322d40c4270e35e7f541fb8abbfa80436628280031b884181982cdc3de5a1180e88a9428c13629a10c384ebf2c74880fdcec748f8cf21e165bafc39243cec73f4538101979914838a418ac1f6fd9e2ea71bc5b39b6ed49461a414a7d8ca0ccd280996acc2117881cf5a0fcda1c5981138616ad4fcaf091100b9fb967927a1e6ebdee59d57e76e7a753f3f7ef498baa7455003112c89c09653fc029a8bcef386a1e1facc59e7a723ee1f8f1b29b14e67a65bcb72faf288b8d799ba3aaf7b55b9bb2ae5eeee4edc95782d62a5c7c45bda6d9d56d16534a79464e26e2fb820a50516a2201d4159c1a8c84913262a2c519204c99050d0b823eae494514619376cd4a0615346b5652144deba5e4a2e6561e43a1567d84cb799ee9d0f9071473fcd36327771baf56333dd2fc3a2e903577ca0cbcbf0ce79190cefa30f0cf181268fc1ca2099ec869a6789de251ae3246ee4fee4035e12e1349355ae70011829eeeeeaa548454b7c9664b2d9d6a63e6be60ddd2529b112999b40c1682e3ada3943e15d28b3141b798009b12cef97f83c470f5cf10026d1ab93620fecf040e8eeff211640c06205eefe46fe6548901a80c502b0d8f2b2870016a5bb77712dd1811df866c23a37dd5dd79f17ebbf5076236ada22f0329d8a3b3b4a24c6913299de4cb80325b8fbcb4aaca3609c904ec59cbe3cf2da7476345e32b79933539d6e3321b9e9ee1a651b4a74748591e6b9e9ee5ef1ba87993c07c4b87b89f58e68367b590e7cc0831ef0200c1dec400739d0410ec8801184155270e0eee00dc20d9471f72d7c0355d036104446f7e76c409734da9727366ddacc6d40c90680dc3fe765be4242035cb84ec51b6aa65bf7cede3d75da1cfaebee3670f71ab83b0d66e0ee22b89f44d8e130ee6f7a75a71334ee73b609a8bebc4faf4e676000ae5331c54ae44d9196e8d94e269b91e8e895852f62b8eb94e635c2380992a8f3d6995b97336de6a9cb5dcca3d14a5d6e57fb9c7cf462d6e77dad778f794fa3d17e480c851ff2273c0a8fb16d06fb22af33f76ce373484c676e18795e7256e47fe333cd497e67e773b6dea5341aed459db9bf57fb9c98cedc9f51d85b31b54eef4cef69e6d05daab3d14bd2ce9b4bf5b923cf4ba25967eebf77d768a5369fd6db26ed5353e7fcd2993b35975e8ff74e97cb24edbc4f68d8f6e37f2c0bdce88c717795bb0cbcc4557e4889378a73b5e6eea887554c71f721f2e8e894b88a17959e5461c35d295740087771f34e8cbe4c4911511194b1850a34b97b055ce348bc7bdb4caf6e279749b1b6c36df3b6b323d3babd71bfefa7ba1f52e214df6e66ba7778c9d428b6e1f26f48c5170c80c2fd653aba1cc9b4a377a8e0b9fe5196680ed22115b25c49a47b58e9ba7b0c60408114b83bfe9e57f2daddbfc8359408a75908dbee918f1f51363e2acd21ddc34cf4d6b74979f1127cb435be11ed7c1ef17091e6e5a292284a690e193541893414a1f284a2cb9152f4d0d67989eea5782993da1cc24df4798e1a9bdb093e75668a9491caad4d243952ca4671ee868fd02825def786d3db6dd4293eef5169a6295e7aa2cb0d9550304ed24bf19279e2a1f14897e65089e620a11b6a6ef42ee11343d9bb27ba5cba64ebdb4428c5465a974b97949844771945c79dcf239cbe4b8c6c39a74678df7d8451a2a3149ba70ee3249b091795172f21ab38a9921bdafa36c165163212cae5ad77422de872e992bbef109acd17089598878ba4b881bb3bed310e490a0bb8bb98dba1f86934296e17b7f4f7b510d2795e21a6eb44fc346ff226b9a1a68f1fe56f13dd7d8a758ff50d127dfce861255a2cf6399f0383d9320a0f8466c5d47fdef29627c6a5d9763fcd36d296f5ad6914ebf2dbf2ef235e6e9233520b4cd749941b9ad31672d1ce4525bacb49762e42898e525ca64f6c4ff6ee490fbd4e72463ae2e124392315e5a2128f4e64b7b6d33b3ddd59e69ccb0b173a1553ac3b316e96e299363546f1cd052d70b1c4555e544ab34eb2629ed74cb1f6d2e42f2b7a72a482175bd8801c889ff76754c76bd8801a0694e12fb3e112e3f2267a91878bbe892d4990c1e9bb442623cd14a3f8a6d35e74e1e016078d70f7183c64008ef7f4f4883c628f0e9a75d20c9729c6fabc78979ee74e278a243ecd3820e18231a280edecd4767ac7f6b8fbcc4328a6d8f7662edd8a8b78183d1d31033a2527a78891221f632a4dd8b6e1263228e9936dc34dfeffd129323c5369cef0adc43325f2a63b8712b242a9fcf40330458cd8f0e98122445078149fb35979e66f0520172012291e5b975fa40899eada8d028eb83b2e332a43f1cbb820d1f33ab613eb2f52e4457cb33eb83b0c1e76c982ba804424341a8d424fe4994a53e7440ebc976e14efe86e6e07e374d776f499c91d1f3e42807c7a786c3f808290d7a7a7f603fbf8f8d87a4a5b8de727080f9010d2a7e7470816f2b3436ebd4b77c88de2a58de2a5d946f1526d37d3e54826ad67ba34330b32d96c363323674664b219ceddd49662a59c06f5b6799a3b8ade9dbbdc4e28474ac13bbc5b419723a5688d8f4e7c24a4b30d25d2e5ee36f23944d29ce43cafd0798564b219990ce766999c695cce505c96266e66eaee4ca76532db89b54c668434cfdace48a71b33924cd6e3f5fd9bee462237dddd2ab912352f4e28b743673323387d971880a7446f5a22414644628bbe48487117ff1f1d91f84f8ff0c275ba23b6e86ea83986475439e24574977e3679dabca5f7fc5a3e6b7f9aba5c268df0c25d2cb5998b8ce86284d1d6195700327a9b35139753dd17808b8be82efd9d59803417dd7d877028519402dcb897e9e5e976b312c57a96cb22bcf04d5e1417e180d9c65a29bdb914efcb9be972292ea8c43a0a6e9726af446f6d5662bd33525e8c1bb3922832d97967f9362b49234bca8b71b39b5e1d115f10d1838bb48ce3d1be57cbc1d8f6349db969b6fc39348c6d33da8f18db66e7d5fa9e425adf93c4d836c3d836b3cdb0be359db98d609c8408db986e6ec6c36889e6da105ee453cf86f0b2b11e42cb1052dcbd2cef10b38d3501cab8e7daac4712000bf79c234011face0870fa46cd195ebaf89ccd4a34d7ccd29c11e02627bd105eb8eb2984d8e2eea210383407097144f370511065eef76a414420089a8b1bebdecd3d79b3d441047103c41820a670206840c0dc1d2fe5dc0f5eb8bb889a3c1e2efaa10b6ef20393c9c20f4b5cdc42469bf018b787b67cd9b2c5dd7d0bcedd7d0bd096d045bcaf0f5ffcbcbb2af8b559fa5084a7183d774b28d1a33e1cd9f9d4e799c9abc3279a6bdf236f6996282f9f423e843d6ce1ee3d74f1598e64ea81a987a1194e5f5c0f2edef4ea6aa636720265c9a3230f6178e0e29edb699cf280a46dfb482623f1924c566e3dd3691e7e87277608c2dd757ad3c1a8697b9c66272fe25d95abcbdfdb1b9764c638a41d8434b9cb3d2f2f29b1decd76b8d1818c0e4be860a4c33700070cc0077717f5c58f6b680f1dbf443389d3bd82138d5e9cfb137a273eb33ee137d18bbd13e39d8b448c6b610051dc457cbbba01c06e5700387d714ff309e263e6032bddb346130016dec56f02d022801eae77b0a7a5d98816a9fda8d4aface5a11c069942ce08c00820000000731200203024128d874432b164d77410f20114000580be627c529c886194e314328c186300000000000000000110006407e2caaf031ba6634736565d9eaf727125db8d6258742d94fbe8064855b1de6f1eac49e287ebc0ca6c829b5b6a97b61b3f1ecacef8b17a64852dfaabaa48c366a4c2fe21d3fadaa2685f737317afe8a1cbe88a9b0f314ea6859c8c17ba8e30cfa2805557720680a991d7fd1c6a13b182f0bb669f5c05860f100d58e95a647cde5f8abe79e65caf443ae83bdfd90aafd8b1b5a3aa47b4ffb34b143468c197626f303e2e5abd3f1b291becce09bc42292109c488bfc5837f366673c3b430c71d2ebb960463819a945409d89987475ca92f306ec17801f328ac89b57f1faf8ee43ea75f59af68fa570515fa190003bf31ee1b62a73cb782dea5c0e3efab7349e692979f1fb3b9ff2d580c19b2c4439614772c4bd95cd6b62714ef657493dcc60ccd7e5f6d9f043f5f4d072624df28798e0c17ac95e146ef98221b551ad98815dc33b8b8ef1d5d292a65df9298a2c8a2a89b7932f344ae3e762aa6fb86f7a1bf8b32029f8ef768305c5e4618c89e70f70785b28ea32b873686a12907a49e54fd1c3a6ab284471e2606a4c530b190c87858baa485ce404af3eadc618e22595c14b1fdd1edd1af04a42f60d93f640b83a5f0a9b923c9e0a631be86639df42d2f201dede038624f3706fb4b36cb271ecf40d46fa74520ac4d7e832f21a78493d91395bf7df274139f4d0af3bb215a27a6148d6544b64d6c519ec5e10d8701dec82b7ad8c2fc0c3720c95a03d3d3a1de82a4d61d9c179ca6ce324ba6dd25d5aec54182cd071d2b807e174ef43e0c5261f56f384efeb3719f9cc1a700182a3ac6032dc2ceba4107c470a7af23621c1eef0625a4fe57643df9dd4ea01932edf83738ea80eb10f1f557c42ff0731c02e6ea01c353bdab132020ac074eaa41fef1cbe4e9dbc72fd3a5979cd94883f4a5ea7255746ae3db5d73ad9a4b699f82e7ab8c40f53b06a54f66c617cfcb501d04eaba3fbbe9a79f4aad5ddd83ad98c5f6a4b68272b7dcdc52e1a9ef6c82d08accd673694c6112b7b5a71f97dcdbb2fe9808a7f6a008ee552eb37f09670c4af6d98d3ce3bcbb0d94b13552dadc715e7e3a93f10c505b8623ae51e82a3f0ba6dd68128e5571d6f9dd6003f9e85cdec46724a31f431c832ae32604d05f83362ec6499aa6403b29bd933ad5005c9eb48688fa3048d338632187a2b91ccd57d271e8a00aa3ed5026c810917062913d99eb4e9fb92477949ea10f520b95071f7ec095a3254deeee3477ea918bdbe020a8236e974ba44b1ef95a1b198d5992a0d7b210639afd09dcfdfcdcc206c6febd22bd7e7d189ecd498e69a40dff71e4d3fa480cc479b52685634e9379ace687ed5385ae738260afbf15dae8e0833120e8636639cb7320820cb75d158e151b8ccbefb284162cbca3a8511c6ddc3f1a77ad087c1e06e6ca3f9168125bb5fd5f34143e3470023ff2a65ffa76f317dd5b8d25038c7023e7cb3da98fb42738e8d16306aa89c40ad71281f1fee8231df702c9e9b81e01a00d736a32088e91407c9520bdef5dbed5965268f5e706dea53c22242b1f88ea19e5a17ef3ba1e35abe9d0d7fd276711595decb6c535c691baea15f0a0542b33309721fad985f90c9607811e7c701446e109ef0228869f80b64d90fb7d092b739ac3ed5921f05d32ee9a94db5a0d34e954785ab7f8dcf06e497b90e6ffd3a4fed53e584d2e39be1397397e45224ea4ee1f3fb97110c791852e688140feaeb759cd9e6e4c9518a6cc313b848c2ea65f68f6a289fe88217ba04a430ed8d1dabc9ac16dc13b44d42bc4eb0187f6d7adf4fd943fb5b04f93d99b7dfee3d68c2a5101bd4e55507fef2f6f767af335bef360e51945caad42a1e11b87eb0b60736fd92c164a09c9f1f7b6935e5c9587bf494b39323e482c122708b02576bd4b27931875bcff50c0a832c1d6a97bdd7b7bf9e8894a82141c2b89dd4de7ef0a9597b2578ede724bd0cc38fc5c44a5e928b9a657d09b6a006c31da424a70959110cf93bc18eb0e76a020980300d8132d6bae1d38bccf8b24cc3909d988804dfeabddddbc04c0e965e33e3b97f52584a04d8b157f0b17612da09d8562a04d67c477b30972c4e38fb99734e7bad051ed1f9b19ab67c3f54ca8771d4925c3c84bb31d602b45c3a2f856227fc4ef766392f4ae2257ee582982114ded8ebb835f16818429f95c0ee25605285a1f66149a1f5a86e8f48ba1245b867eb87c2740d2238cc0489204e594349fd7a23f8500947f39591590087724453ba763bab0159f9dee7602183f3bdcaf0229a6e54099300babba0fb70ccb65c30da9b046296b8f99002c02439307acb842368e79e931a3c0b6f56b88818ae7151fe6383aaeb4a697d281119f4490e05be701d8191e9219412b46349df28cfc8a529dcb14dc9ec85d0333e46533b98c8d280e982bf0ddbcd5c6d94a20983ea58aeccb56bdd6dec1d259a7e6a871070b6ccc9e248d482e1c0c9bd0d7d466695f9a0022806b3b7adb34054cb03a679179cdf481246aa549ed47f4ae2a2a612e4578a2782f96ff401f5f1c538f9cca58affb4a705e43070ef77671f7f4f3e3ccf1cad001424074b780907119f68e5028a061bda5d03a7d77c623593224314b7ee2394259940185a20eac9859edc0161f442f98a0679638f8c1191106f6453358c95f1200c38f313e15f3f4ddb46d36c2d8c8495b06ec95c125bbef2eb3018306f2bbd0085aec18506ce598b7f36f86226debc3ac1a2541c6c64e74553f50da19629100b219f5b591ba95b914b260c8b145edc3cde32c5a520d84ced4d5994990a3e15bc4d9b765ef9625b01814c6f9b1db5865651d3fc602f735cee3cc1ac1650b22dc91a0f315c1937beb9f41f477e12bd96a1a85ee7af8ecbc8f412cdc1705b8892c095ecf0cbe14296a39e78513f06de6a541b251ea8bf702f9aa01bab145351f95782c750cd8a9bc9cab59d36db1007b2ced8d2de39e387524044e558664c5ea1170d6e0026ad1aa61509255ea61fbfeb50f841dd1a82905522e10d985a44cb718bdff56deccf72a3f222688a2b0597010a431c2bdb9b5ec540bb4d510cff9ac9ab331000148601a5e4e609d5033aeb7371bd5c5ed1b3368e499d43acac0444dd9225f011c1ffd0a8b9aaa6b6eafefb66d870ad85cba06d725e72e17d4f21d84a9cd6e38a9b4fa37d6f8b0225c8cbd8772f9dadcd6222dcae3aa0b41f1f0813fde01f6b42d33f3a832b9314c42b0b14da85a1b02309d37cebb3717505443e1349208e5a2610113615cc7f0312cb096c0ff78a7c0d25b9fd649fd90c3c941bc7537caf3cb6f443915fef25c78f4422226666a526fb47378bbf721b405c9f29e4c2d74cfa47bb06d7f01a0d77083ab37db4907ab044296d5f5bc944ec1dabb727c90312eda037657ebf60d6b502b932d656b299e987bc78b5f2f10f089f8733194843ebbdcb2d2cf769158b88deb005e8b6949887302e91573a12a0861a474fb36fa4e1ebd772d361eebe9b7794ec25a52e3b4b3d6a333fc6db4098fc4b4cee99fad061ab46530af220c98877876884a4a66cc1488e454c4ed5ce798fcc3e7f1242d16c232be8dadee82ddc0102ee61e35c6c12f13bc2386bb88ce16c7252638484108e37e36b3cda8b4fbf9748422ee946e52ec2008831b36068d3ee242ec2eb1eeec3d0c93fe5dec409b566763247a5d3dfa34f6ba3366339016232a1ec8d145ec1a8aaf769caf4684a727635c561a5e901a5f4967d40e39be9858c858397b20176771d8e3e18851be0d9be95b0e85a930e0b609dfd1b45272049bb3ae3716067cf8c803ba51c5bd81c4b6ed674add669314844836fb765607718290bdd854331491757dffb54bcdc85c598dbc84f17b0a836ffe3c5d5805312eb2cf0060556b1401e304dd5f44f63bbf036e77f6349cd46ce0cd5a055785a4084b8323e690014ee0dee9933df2772ebf48eb187e47a998c16efa79ae8295f96e3115234073e6987a77527286a02e8db556a3b62cb284973209ffd228ec70be782e88d7e151de25961e3ea21990deed12780c89fa7638aeab257f727c1a7744715b8e257bebdb98b26c5d4c60838a728e62a94d9a98959ee2c77bab6b19a61dc828a149d7e48bc73b408582910dad47f41b428bbea64c06fa79da63384c98f637e5d2182d4f90a289cb1efa06b4cd72e546526255547cff3262d7dc99a6827d574d17421b042a1e0ea5c895845f31f216c062e7407e15d967d4b6570ddb868d5502e5ca0b7b47b38a9b5b1e1dd2d6bbeced2f5718d08fbea9ed1fdf9709048faf583d00757888e76f9619e4117c235f143c598bf41a3a96133358d823ca1cfaadfd29238772a2b68622f8b93c4f6f64ff0f9e7978a3942c73199b66be3fbf5f42b87ecac610e8a13de2b4e62e6db7f3d9be1095637742d649f68bfbf0567eca032c8c04373709b1add181085c07d20f47e313035c43c7d436579d5af57f3a5555456e4c6148050b1a4aac5d25c3a74162eb054e1e0e9913e4348c3b6f21836b0ecd4e2ed8722247f8acd83508453e85dc338ce5d35f8a2d433eb2d4e288ab60b62f291686c047cd9b738857442f3923e92aae754aa1fae01a6980bd31cbf587cab95340211bc49b35649db9a3eccad6138aaf5ca979e5286a89986e4c51de27edffafd6bb623a8ee932c91dbe8289b7f9b5ca23833314ae7c975b04c87622b9ed8fd56220fdbbd03cd2c1d00f95dd4fd3e289a9ae52be864f1eb58816a00df43372c1299cb1fb91ebd940c00417599aab3c3633880e29757c1210b746dd20b8d28104b8b5512f9ff9b0ca5265aa1514823ff3f6dc10306033292dd0c9c73331a2d373c8272c04e8b64d352ccec4075d10b837191950e85a39d20bb28d50f59bab514b50faa5aa18c5854d3c411dfa5559b1a2d15cacf82b38da102479553f69e96bbf8178bf6973cd86b4f4a32a6528577640b1f0f2ea9b568e18257ce5743b3d0d2ca345fef43e78d72623a49e83965d458088c2224d8dcf833d2a453242f476fb9882786e12405f2b6aeb5ae8520889a51b4fa7afc79c0125481519da95b1bdcc9eb3a66164a10deb8d86aff31d7cab170001449301378d33ad786565993973131f592ed9cade581f6d14b9ae7018db3725b0c0423e9fcecb2ae8b8651d669f6e2b3bf30011cd65e2a710a711dc9af4522e8fcd8e168e33d354602ea68e25142343e0b49402617ae20616d2ffeadcc99b5c38f82b880dac2a9a72b0c4387863a3a34d1488451b4bf84fbb2f760fa077bfaa7f3b0638c863ed4016a88c9e28f6f4cebde257439b7518484148f662f45c9a319177cf0ffc4e94168a3f43e49c82074f63a811138b9aad95c32eefe4dbb3e1685b746ac75e145ed14fb98ea7d70a64f22501b6dc817ba70ccc7cfcd15eb2f1fda3f08c0f1307329a0c821d642168b40926d45035453c8067c048fd09cb61a630de9574b798b4f41cc438d5a3a62b76a6e633622e312a49f8f14b88794c6879660d48e4809c4274bd7870c8eceaf596035119be0df4956a77556054e803126babcef28b868a0188aa39b0448b1d836028b859537a75ffc6110baf7177be5cc0418a9068feb50be2d2163d12084ea9c94ef75e6d3f1b2991002d92af44a6404b96e1906e20b2a5c55facb9db4e7f51009caa89a28a58412fa9b232a6069e0d1fecd79ee4f138ef48166704a89c056a007a705533fe6cecbd98a808c1fd371bb020e5c7644da226eef725c611ab783ab29d189cfa66671248b0ff8fe8f053c5c02ff3c48c689ad7893d9e87a5a4c2cf19d189571b03c9da32d0e6274f30925bcfe3688163e28db85fc06bd2677189c643192ef3e51a0c73ede5cb169b5215788119452881271901a10ebf4c4c12ce4a0a62bcd3dde7839ff93bc20eb23bb24645439cf130d399588ad7ec5bc2db5a2ef7ce2e4bddd7262de2477cd58ea50127dd6c2ee8c88374662a75432f1f549fef6912d3de79a11cedf5b48ad878e3ac1490593649438f4ccfd414cc4c1787c34baf134cab0a11c96836ef7be67c5a9b4f4c06aa23ed75125fe5cd506c8225b980d645016aeeb83eeaa84d02fa2bf5559c8386b99045d35cf301d4fd88499450d8dc73d4a440c0ef4500fea80e81064f392253cb6c11a89b3386112e70db68bb15980de23136061392f41d2520c48b5b1d593871f74f5936ba801996a34faf1d276c51b45d17063583e7e60ae0c5bf97b50573d14b93146a31b214ee55fa7514d0919f46440ec8208fdefaea0000d9d3a49069e9508c7390cb6a0a181fe1a2aa519367704a9cfc987fbd25794e04b3cd7a63debde0c4faf1d360348dff6797835cfd269b66bdd4be98fea721df16d11ac9e8bbd7070cee7bea8ef1c60586dad4dd30d91d6e3b5d4bba980987af72b40b971f54e3f1dd9fc6a4af7da6925ffa916d2b6093341a45573b44f8b55dac3c64dad150ffbad45ef8bc7e63bf7df00d7141066785da4e67e4eafb1e15f4fde9319f99c86b70d18188ecbbb587ef51ab5135a221b27e95a4d937a832dd40cdef13426f2e7ffd09be5fad4dc9a8965a624774af96667c183cce84ae9d88a1ed5713226f07e889a52c88c2d8f7d742291d2c80ad74b5de371e74bcaa4b47d7d5120762e620fb466e2c434982935a483c572d7bec392ba73dc4837881a93f7e220cdcfad8630ef84e903f3b2104fe7ec3b494f2e98673401f614211bd094649110e38b2a081f960fd33614e50ef84080d29e07523565ae9bf1b6cb9c0d8de8026ed4f18a812d40550ae685c7e292d7a649dcd4b6bb7bb8c763f7f4d8ad85b4875cfb11662dcf6aed0cce86229e0c538f88a72996cd4828b3145ad7d61130f2e40d4d83bec8b7f8fd4eb796b36c085bab346e3f17b704a5a70a4839d194cfac3ef4eb67cca27592f823beae1398e2bae5d3c70385e1ae208b66b8c93b6c6b03549686b488452bf40c55bfd10447e60e54e404b5d183a9079cf84bf18c7793b270b1507e8d426e0274aba20da3c5596219dfd64b162214b06701a0d44f3a1715360de7fde8e6211fc3febd22984abfbe3b5f2fcbe48282ef62d273c754f9df09359f288874a8c2b788d49093afe8247926952b46de86ce593ee9ed9f6f8bf389bd94f37918f22fbadc8e94e415839d75243138139e77fdc3f6bdca8d1ad589bed6a11049793d7c5863c3a55445c2e59a1732a765cd2c180092c8e903f12cd8237395ad6133036d35bd7039d4be1381f4b59914dfb9da543f0dfa2db4e558cd9e035059c03c9fb746ebeecb6f14b48df879aed2a0e8a5a9e4d07669a9eee3d358d0b68bbe2f8ed91bd03db8fbffe869586f7fc12f2737c7377956ce9c5b7602c8da18d0f01fbac6410f24645d80c791c8939dbc64213f78b1ff2f12db4e41df0367adfceaab5ce22cda3f601fe7516a29027f49937df86cd578d9a5002dc8d5d976704cce1dc22ff55ca1039e518380445d6fd996b8c22636eaa8dc7274fa0901a3651d5eae18b3219c88101d913ab986c823b7e682447a4e7523a02a4e13ed10e068421446faec6bb26aea908a2d40625aa8120858394aa20983a83de38bc8d013577710daef6eb1f6e415118b117dcee0d38495f0bab6671b08ddd7e800d328ce8d272fac236eb168cb89c93177ddccef51edec077f34168178dae2422c2200b890b3d983298b11bcb3071ae02e1f960f075e27c03f9f1ebda58cdf87b7035981363f713a1341ef16091cb06f1f6df5dba3ceba095f2e301f1684cd022831928ff719e2d60d3246f52a5cd0b66a460192a132e7f57ba808990fda86789b1fde6602b393bbfe20219680f5c453248513621da0410613c1fe80aab56a024a1d39869875aa3e52f17fae8b97bfb1e491c1321c5c88abd046908ff8d920182a26fb4d90f414e27228a9c5938dcaf47ee2d3122cfecbdc12beed13c5e6005cdd46ee3f10915d005b38c0289522ef4cf42ec6fe4282f0d71a26d9a6f09e85fdc2954618afa579a3c217f6fb73192b4add011288987c3d778a90c8a96e0c52210964eb31cab192a262a8e9d0618d621885fdc26800e8e31185b724ab3ab0665ab0b2b62dc17e97adb4660ef325a67fe0c4dfa18ed38c353d8e919251140cb62a68a0418d64d5c8846cfcd1916de30013263ea701c9f0325c492c0291e5df55f750c81f57ca159bc1cdf8623d41e0a600754f60d62ad32d7fbd84ec1f749f91e9235b4bcf759d76324991ffeea6648e4113131c5b068ef8e602f53c842ee28912787fb10efe73b673bb26c18464ee47ffc01761aa2757bee8fd218a1918f8994462a5ffa444cf061322f78969f0f606f6aef921f1784ac9ef54621807c76e7976693657d504c89de63e1b10ed3fc0f3054850a58c9024913dfeb29a20d4227e05a8dcef1d6bcbdb6c552fffb27a7e25fcb0c9277103cf03bd0f8a99817a0253772a4e393b0078e81af9241982a40c7647f4b7296f1d117428e247c3df1d394030b548fca5bc51e9909b3a4d3c81618a6f9ac02a9c10cf08b0423f9009f8e3b575e954c9f689a7903e89658d0659fe82cf1b53c78c73a3aabf7b44f1e648aa1c3f71548f77e7eef9ebebfcf68fc9a10471c90cf3df56f55b5c948688b897f0aa8a26d3cbf1f50a7a0b0000a91831e413dc148c74ec0af135b7f845933e0ea00856dfad0b5e1d3fc240bb98000b8ca20539d3ff7be2ed069b660c9117d288e226ec0623b0033212a365f21c4dee2b9e79aa03f4f0ebff6878112770a0fde1173ec2cb6d9fbaf4fdf5c24e52ec1523a1152b643943e268c940e053d0d7ca0c5838425a35c39206f0fd98ee101a5482541a0789a6ed4a82e0d2877ce8500baf95b3e802ae71b69dc4b971a2fa3fe7dbbdbe60e68143110d912928e8a678ebd0f5a76f564ba486d5cacfdc066bb38424db58ce1ad71e2eb9a33a2ca674aaabb903712568722fabc9216c4f22ac1893ab41d617277a969d51f236408990a7eac066fe52824e312c5039dfc19221cf0c3bf549a67d377c502ff38339587693f61ca77eb65103a43dacce003cebce6334e5dd583a2e22c67e15053c54ebcc4c4c8440a2a194f2f8f94afabde8bb23112d7eef08b8b132b8d0191cc5904adbc7057ea1686aede73bfc315d1022092a15aa6ba6566e496e56e464eec2950304cc15c28438d3543160cb505f31c8131aaea642f2664060cf39953a2c08fbc88d0b240aeeb77d464384004320363a9d41c0cb11ae207b3f11c3967cb8f561dfb138d72b31013d0b7fe2489b38ff51e77d17bed0daba5a9015b1bddaf42779f6524fd93e5e10bff95f0fc6df1d5f9ceb5ae88764a439ab97831c841ed67bcc39d626f00bc9fbd207299528a319065c2cff7a2315c2a48174ddc0df9d5b14c2730e9264d9a17b973b93d51545baea7e812e50f1f19d96baf73ebf4888f26f7b389b8ffe523017aa1cbf764f38b19a502f2bd04d008ddb4cf975edacb66f69c137b3a53b09b7a8f2aec954bc10e5a96c207d650e0de6813741409bf8e10fc5e7016bdb04c1d5f71177329b4d262aef03d50038bfb7e8768c85d97b4193f811fa333bffa0b2267a98e6078865efe1ed5fa08bffcc35ecc2e5306ffc9190f6a71787b8aed45e4ab329a648c544aefa8314f3bc86eb961dd9cef48d698eef63e94bc71473d9d8ab67395733d89c93cf9ee52f1bc0334bc040663378c191bb2bbdd1e86c76bf7e91f21aedf7e443e14b5faebd9c63e98171cefcf80e86a869636d4478c1bd185db1e4acdc1140f803252da6bd8bdd7fe3ebd54e279d559ec0d593d0e190d1df3fc56289892272a5f361403e21e91919b7fd3bfa75b5140394f93a130764539b6c5774b45c7df0f767954ad557cf03920c4afc5dc713c4e46cd19ca9de214e63a9fa9e3d11ca8a592e94d58aafd6741fc2ea85d70170de8523f83ff241303ec08fd413eccf47d4021bde6e1db271f3977b07acc4b2214179b3051778313dba9d0817c0896e3909069669f5b2ca38837c6e46fb712cfec4f29758c89abfb144047105d8a0ee2e4cf29f89142f606e56e9759d021fe994306e23467578cec04f824be3496e94bee4fcb61231850902d0cdca51d4b0a62f17e827b935ec1313d0e48ffdd2ea0f18977ba9efd03f79433f386c270857cd1e03df0dc59fcae8045c02a7912a681e2c97b395356626197fd22975e40ccad39f9b486887faf2b2fea55e418612f6729e28859c0dfd9009781a8632fb1ca4eb455724177f4b57e0cd0153dbf8c937918442646c89958da3438420e4cb9173800a442fd94eddbe1e19384eb2ec79fbc5daa6ad0190241909abf94a0b681e513428cadafa9922f2c0d074892984bbcee65258897ecbc3f74d8a0ec315d035ac323fe285eed5c47ae6b54a39f6d8889887e49338158a559f1e58e0acfc86a5946dc6462af203eb482f47149de2882dd94e8374ce2dd53eebb0b6ee58f1f4794efc494b0be94f1b5f380468256eb14d0a41b21693e93d87b7145f32689789b625e1bbe08e1d7cdb19bbe9ad73a4c7f2416163c85490818afa4c0e85cc9a963b79e944d2be1d1b58e9b37f0e3a62344d3393955801a207746dbb38afc63b6de8354591acfb8591b56151ce9c8226fb156deea9c432530ffdbfb22ad542d0aa1fcbc9f799a8786730ad4dc2fee6e30400aabe8c849985b6f4910bb8185cca86a188aa628614c78daa82afed3d99e9d190f72bdf929c3f7e6b2dffd0b5aa9216d317feb98bde8f079441f9745335471b9c06f2f53f371f37fc0374925ad88f63d62267087e364622cf5bc63328a653c7bb88cbe86036640637821771535dc6e962334bfb1efc8c10141e10407feaa98af17e48c288a93065bf3d8d6f54e747e9d1abef8f1ab804eee28fe0bfddc89de8d13f3fbc636f5b4e5f8d8286316d2960d5bed149b3214575ec59e44b70ac2cce4388c85e1143616e21858729af9d66ea88109f0332de6e209a55cc0dfeb912df47eb2499e7e126e544981884c6538d6192aef4eb1854207d0e2a22f9382f966369e4558f0eeae6bcff3bfa992cbe821d3f63db3bbeb6a58ace1bd590a3a927448bb9438aa99786e35c7fcdd9af099b673c76022e39143bf938236b0dac440639e16184979bb676a1db4c99a10cf561aa741ad9958a2608e7dd0296e6219e632125f800313a240c81fa4d5bb1ec400134dffbd39909ce37fdff8791bf3a2567600abe0b81220f6804e067242d28e732abfb650c8c3da28641c8cf0e0cfd2acb7fe1fa1aee8a324c615239923d67692b4c227b717c8046a5836839a8298fd372be6b696664a2981816603b459adb0ab13882c31fc33a19fb208587b8bb34ed9cc8f6e213b102c482528e602ecff81fe1960b6a3bea738cfdf5a8e21a9fdfd204ff981dfb7a0e00416237e4947739455dd41d062ccc39ef54f69d0af45db356087d7eaa7a98ced08726871d1f0220385dfe6f2bc7fb5edc55822c5c24e5aa2c9d44b3c7a3890522af291d7e7493a27528218de70433f30883212f6f9543d3c960bd06720df1ce984bfb06a64b661bab5ff3cb249f8a352c9cd95613fd1eb9ecb0c7c60d20e32c737c779a5954a0f9e02448213c9cccd57b52938880b4f6e9e677a910d8402158832b2638ea27b3d64e63bb8bbde7743091ded6455b95acffdb516ca0532ea04837977a689b93c8d46f14e4a237014f83951c1b117e461c11f87cb2226c597078e6ad2c82bfb0cafb6b3cc2c4ecf0e8deb0b3ac14c3edd43411ac63a5dc9564a9fb66916c9d9297be4dd35452a1f2673895c837347083bb57509f8be23216dd02e5a1a653884ccdcc57aeeaabeaecc6669288ede3e49c2307b5f4f64cfa17c886413f1d62fb103366609b712cc7b1d2708107ce70571c26de126fcfddd3a5de2538d7fb8b7cb3d3749e067222712ea20568848d2f2ee6df0513d2959d65c3167472552be590516969d27b812ebc32628fc074d5caf2f608ea1b1800428bd5067970f6d897ec8902ec296bf6c3d27212c789da199f5982356fc32c0e8704ec55860d2309d0abdc2457f627170837f58e143ac24508685e0997cfb5ee7db2832edcba553b8df27af62fada09893084c330998e693dfb70d7a160a72737a076fd8b0b738b6dbfd2c7a1dc06ec44a6c9fa872382512e713bfd4470dd1341131f092abcddf56452195f466ae4e24861e766589c0482d47cabc6038db39ccc2801b51f851a3355305c4731d1658129cedcde3c8ff916a2c7e68b5af38d097ddba1d3cfde4f1acef85276d684ec6b04c810059af2cb5f9a19181827349c685e5c3e3a7d62d472f5465842387c6a2dd8d672492a2e1c9f5bf708c695fdf1c2f3e14068947077b4c9aed241dea65a66f70123ac5c736c4a72540300bf4fa62e3dc28c3700fc2654b143ea50606b334841df790d1ba3ff5675898317d71f8c87133d03094ee462d6986d8c0c2f2770077914e2f3dd7ad08285f7d42a5cbbd71c72600434d8098348ce15038ec93cb64b9d9f17d08ac2e2877a3203d98ae89678ebe4e145a5dfd01fc9195ea1b69d0be952f5c164283c82d97dedaf40749f9906d48293bbd46b0f7e8523d620dc018ba28828715333a8cdbbb166d54b9668168ebdd5575688732b22d2fab82af4ed4b32630309ad96cd51706f172243daef337e42926763c8b7adddbbc25d31c53d99ebb907b220d9f8ca535527b309b72b2d2b3a9222b9796ca6cc8eaaf3233233a95b1c05e9282e7ef06fa5c106549d40743865ac1bfc25e6d26389e27c3a3db05814a6ffb89a637e8d0a29cd30ee843132a7e6fe17537312cf8c9410ef07211d9a72d979534cb389907b9a3cae80b19c96996f960266d1f0a8fd60238e91c4bc89a29ba5b0da0e5fc7aa1495f63728b4636998e9c4761440561b360ad59b62ac6a8661ea7d139c65c586983e69b6b80c56665e6f1fdd13e494b4275bf9847d5ec244cb4f188847069bbe8997f9aee1950c5457c1f220a46bd6983fd8052e5c96636fb6e13d257c87b8911a595e54baaa1e1e3e8d1263160cc31bd254354e8590db70a7b081260c8c0458348d8a6097a0007a164ade31eeed31974af34eff84e0156c697cf1486dce507f76d371609b7f4ad9cd682e189f35f942501af47626c11786b4053ca45121bdf1a035bd71a69c640517932c3227bdc4a139ce0511f57a3268c80db9928cc724ea41ef2a90bdbd296c12c58bcd706a63fc612aa3977ebb01c8e4ae912e5c71e3009d71014acde4c68d15a8a040947048459cfcb399a4eece80668c212e306accd7ee6eac95bae64989be891f5bc02db207d0200213e8e1c9779c661239a70e48d38bdaa35f1cb87e25f5c71d04061e6dfe8639461a75cb856a659f80275b4f7d44c61820a25ca210c0f0f001aa09010924434dabbb41ae4900c38618f1061b23af3e0b40689764e2a32549ece2109bc82d04e748026214f6dd2bb6dd743b1133f1af2d3eb43ceceb083c9fe3ab1c60c4ef61d4d8df30b30b3a372ea09a91e6f63cd0e2c8a04b7ad2214c41000996abe58bf46e9be7cc737da0f44e8b19a73a0658241341c0176e0f467a5b66647cac0b872ac62c011302152ce7de3d41e7d2908cbbb45d99e4ccd153887f6a1f2f1b044e336521f2ab5dd77a4aa46057d22379038198981ad9bbf5ab420b8f20495a30f3486037d51f003b09688eba8e199e3a3fab572d856caee18da523303f2cefbead8c41bf813cf53b78dc5b9509ffbfdc8813f69a572ab3fee51cf60f4c7eb6ff02ef68697b57cfa601f225286f4a63d9d7180e1d4e42a3911924dc795bd640cd3df41d4c0fb761802287e7e4be599b0a9c54f229308804aefaa5f676b7652b96c67192b585c1ee4e7e3f37a3955c0b1f15772bf80d0cb1e457840eada1054e6a81e793cad3c384bff38bc44e5350dc66df52630ad00e00b0a77f86c2e65fc01b49b1eeaec794667c1967618f3fc54f389481272369cf8fc2627c8f4e4f8765781eba21a02e3231a3d0170613a70046384b71c7e686ad2ddfe2199f4bcf338f2ce293bf0c38b3a026ad90d7a099f89c684b9cd0241fd46a1ba05aff32b9b49cae86d2198c775d2327d9e4672596f52f8dbf1bc63b479c45f8b98bdf94c961039ec4dea2b541897cb839ebd8fa64a7878d60c680fb0cedae9d458d35796aefc62809e379006cc59892be1658c55aeadb1b4eb52443ae63ba474abf8cee13dba2e0c9b4847c37b1d00f53e6fb6591d31f3d5ab3854abf585da9b10789dcbec83d6ea60d93cee23c2379148fd9d02f15e585002b50dd77caf93bb86eacc82cf0186f59215d758be0986a66b8be0f2da35bd5602a136702b06d42d1b0a5e8082001807c341ca961be5fbbee2923688c5066d4f297ec28c6cd82caa4735b1763d3f7ae3df1f8d8de14898e51db45dc3e19e4ffa601aaf270fa670a14ac55309b55723ac2d624b7e21d78a015d4c7f0cce722a3b8fda35bd605ef41855c85c93743e1fcbcb9f7a9c63310c0fee503e03490c6d3ed93b854d2b3b9d8710569ca9d7ce09bf1fbd60485a78f3b39785f5b72dafdc174a444434a3032b326da3d9fbe619a89c3745b258c3bbada45bfdeffffa8ec887788f9c330ae9587b137e2109c4ad40d5c84fd266833b7fb4f8ba94a6fc1ba7f43f95de37afe7f3dda23b1e2f496ab60086dde0d840231a871428afb7d730c3590ab230cc7420f0d78b4c8643206cd6a16860af399f25e9451933b5f5de13a97ce8b9890c4bd6043132d5e8dc1b7ce25ba140ac26ef72247328470f26b5b593058757ec4f8eebea0fbc401acd8567da8cdd58eb653b7e4038a8a2a386427265c36ab9d9f77a4f7d7abf9cdac04e2d49113bce36c605aa03b14ab8c6d5264667106c880043960d2a3ab73d1af6b8d034bcee55fbbcb9a6c29180a19ad883a1f90eff4e9bf76acc1247bc22d8cfa50750b15f4a5f8b1b12ed23aad0821ec5f4aaed07d385d20c04f4e61d22ddb153abd923590cdbf87dc02f86d4f591a4f407d24df8c2643b10067ecfabc7323d7dafba3129dc8df2f7039b65ac368514839351aaae561e85010ac246ee9d9617e87bc839a0d2a4af5368485fdae795251bb148215e85ed3ad659a3cc6f485c3f99a8b8a2c96b249cf61d0d960c45b61bbac47a63c25650362c914459a1c0fab2fe51cb656d1bce901acb6d26fbfbb4b2e340df7c4739b2e0ceef1a2f91e4efc029667e27263300050219715755ca1bbcff112516b50a547ec87773cee98dafc9268be23140c2e064fde056ce2d0c93d1d16e42c17e55dbae6dd5a105b1921e93541fce243a1bb9af4eddb151aef20e81ea0ca2daaad77cd071bd8e054febb640b8b7858c9078a527cd449fe1903fb2023dc6fc7744974d7060d07fb3b4beb9c82a1bfacfbc6485b3de6f585de94fdfffa1666d8abe750c6c035bc073b19663a7f69fded9e8584e5b6d56fb32c235cf93c1b91cdcf02f47fa1a8003bfae69cfb242ec3d52676183eba40b4eaf13624b3c9bbeba42037e84a497f7c9a77df69e3afd59cd9388739bb07c567f828c50875b17a81474a637c1075cc8b16c857e42b52c3a780e30134ade1880117d76b9e2ca7255322c2ecda9c13e54343a2ff516274f2eff11ddfc6f130f24e7017479b55889ab378cef755cee1fcecb0e2861793350eeab61b611b7a84706852a217aabcebfb1fdda6db49a6f74c3753784029763fed634ae5e5822f6bda39589958437f484ec7ac413b4c15e5d9fa5ddd17378acdab5a6767ef9b9f298ec3b798a50276a9b6f05d3d2a39e31d3c90d90ab96d2f1c9fa8a56942bf0ad46ce0a4596fe7ccd8aaa5b0baba0cf52afd00dd0cb856937059f93fa688030fb86b6b72b9c179d15f2761345ee4c656b6b854aee0d90f78044f5cf231729ad9630fbbbd132add9a2c1d41e29dc3989c12161988d388026820521721a3565183a6eb786af617858d83f7a5b667db65d9a3303dce98086273de78a8adf15963917c55ca8d2d2ef39dd706cc4cb6aa913317c33091671bcebf7e0ac55b20491e0ca5f9f8347b90cca84c7a99756ce46de55d5d3416b1ec713c602869e725635901b7889993db076809d5b46a459fc92e696c4daee0a2b6f89c0c8086b9bc1e80c1545cc76a4acf972fe7557ddada4d38a28ca1fc1dbb13abdf2e28b1b5d031f7c7ce8a150dbdb2179fb592970b299355194d219d88341c8b55f5566a728cbf3a064548a4d03032fe31b35f560e4bbfd27d7001bd730015454cea410a4a4bc129428e751994f19db35d5e5eb7d49188677f6b99768e5c53720c391de33a41bb04b28d1740458ecfd948665f40a4739031bf0caaa61ac6e0fa8bbf0c421cfbaa8f78913381b8a9757d7be7907da18c1fdc257aaab0e6478affaf39fddede71f44ec6ac47717ddea8ab52fbd1000685c3a65662dda1bca58c0b7fbcf693c3fedb2023037e148af309450f97ac44ae6602940cc5d75d5e0e814b2f79a90da468aa631c10b2710d2dfb0a456943a5dbd88cd5249c9f4a1eff7201c0456e26f0684a5fa9482c991c8aa4fdc62c7421c00c023d2d5d50b90c38176ceb06667de3f3b62d12f6a2e1548368784cf382bdc50f1cc1008ea764b2eae2638531179f4e23242f229d201e95333fd42e58174a46821d556f02b07a50eb539a89bf7c49b462e9ef4d4372ad1f4441ff886e1306783322333c92d73430450651bd94c49b61577e68d67670fb5e5e24ba40b7358034013e49bb5dfffc1ecf92d58a592fea7e01bf0581d23a767c54ea86a580e6313f7cec95650535ffb085bc47ad54dc0c6c6969f61775d4d892f94ffd9d8c9c65920830c9de8b8da7acfda7999934c636630ab999e947268c84d5c022596b430814d0b4ffec25dc687e581ce8480e472c73769c71f7149650d8c49b90f7fde16f11298678bf312f84bea618382f1d87ba285125de840c6f12b1b774704202d5b8277c314bacead8e2e22de62c2210e868fcb6449feb2b3a4e9d2c2176355fab8145ef01174f97b4cce0a4d3bdc47b580741a9eb74fab22d85558117ef20b16749b18c9ae864cea4eb8445e1ae18c7c2b2cf5b460a178ddf486c1b1db8a17d1473bebd4773dd65fbc471a8f8ae8ee75d224c413f6b057e42f11ea972c431d7dfb010edadfc88498fb40fe5995eeed57c609d24083662786addb062ea88a05d8a4a8ebe4b0320e32e213c55c966eb0cbe6d7fa067ed403c18ab93861f20e19b0368c3ce99b708a92356b55c230638b108338063ee004c55ef3bb1d14edeb291a49d41a486004722e463bc3f7712a2a7e53d0f6403f9d7afde9b39b36e5d848b4a6d9cd9e5a781f62688fe83f78f7188d86af6c110d9dd2534daa51d01fb081cfa4810db22898fcc06c328d60aec9e64603f33b0cf33dcd68eee1fe81678f4edc232e1008617c5fa3a99540a4f310837291d4907c1c3e45e38975c460fd2652a4803c75b31725de415bd72fa0b3e0d7486999dd199e828ae64451d2d8ab673ef8c3399f68f780c4c80c26f1fc1dc31c798133539efecebe49cd305a9e5dc8076d7ce01f7e44a5d39757a0e7d45bdaab9151c61c6a31a23377a1e00e015cb127bcafda349c2995efc735d3e4cb2f475954ded60bd11b61af5f758de7626ea71ff517c32b6067e4304dca4c2358d4d902c47289cf27a9c046e1b10192a3662587cb960f39a7487eeca53b190fc7c0e036a984025511c3d3a1c967018328010903b9a307fec4aa8fb211af79e68dc4f7f1db82e1d8e6a63bea43fe924e0133ec390d24167e409788500264443834c6bc575c964583cc75dc835bc52b5128c1cbac52843e60438e521f6add0605d6ce08971cb67159d7606e6911cb85315c3babb69ec6a8929ef62a0d6117b2e64e98d89b9078298f503f20299d80203634e89687b006378e069626f806ba2f76e49891f421ec6df476e5b7b942a3461e220e7f346c7d2b0788f1708ca601581faa82cceecde70a42a13ab3d946796d89c3d51d09a488323d7311888dd5acd63d3f0dbc791555655e3769c1a36217ad63163f5c6ccdf1cdc1d3424db24c539322b411f275dbe9fbf706ade17626c2736ce4885bf80e031b6cc9a6914349a185c626df827fafa4ed25930d284acd579de2a02e12e84065adf34c6888307b6b9f419bda9b661136292718f482ceb85878a8749ecac0c7bc6761ab346a4b9b6e33a3d7a5ac3018261f7b29e22e44a3c67ef5bf95cc0ce709853857b410195d180b3719682c98e60d135fe4a3a5fdc4660801dae7f1665671eb4890e36e119d857e15735b5614fef0c0d334cb8efd74595132cb528d4e55786beafca5f72e252c1ffca284be399c26106d887cc8689f5b6bf8ebbf4e532db9e5ed09ff253007b85bc424420de0444d6b0b0ef3857825ac61e4efae3760e20de095c5153986d13fc5c73b891287e8dec78412f560dc0afbe65fb2f9fcc782414c23258f93377d91e03321cc870e0e0571ae114e3693a22099c8f93b05b70a6e00320a16cbd473c63b12889d8c037e4589a78990ae9c10fdb7ef0c88e65cd0812408f08fd4b62c2bc951a5bbf072311b8a806ac8b5fab948373f0efc37d1c0cf5d6f07f25c37b7e9541cfe9abf1cb8b34c369eb93a86959e1827a57fb4be26bc35b2ec6b4a8172795c45e71f03580dd90c16876ac49761562c2138684b6953fe8a22a90977901a6fcfd4ad900a8358a75734801b4fa8f38b5505bb703675b7cd979d75b40b273fa0b33e3ed3f567025051ceb9578feb5fc044d8f853722471a479e667633059c34b8ef6bee3a74bc4d2306fe69e57131f2f25935ba6a52f51795ed77aa3006b9eacd7d4323fee62e9d8162c1ec53c3efe517cfa968a6af832f3963f0df43e2935aaf8d906b352264d3e811fde1a9d205afc915d3e08e598edd7d1e7827a13b8738fcda57efdfff9923b65c7b30b647ca094f1bbd8e27047f030be446fdf49741143042d9cd3b09b526c7f1e2cf1c21ec8a2fd66540c7ca804550fd5e3037ded21a4a56cb3f9d67325ff4da37cf551a8f5c6dc81f5bbf53e7d39ef64caa09aa786067da34f276e69517d362614a880f791da1b37e872cf7906efa44262ab31bc638efbc186e02c51307799c23cc73d0ed77e22405c00bff94df884890fbd5648537572315538be45dff9ef6f8672debcb69ffa7e491a5cfd862ccc7809cf181690e88449e570f14544cc3476dcd29346f10b7e5d2e1afe8f1bc448ab23b06ef9e896bf9d1b668351af4d9d1d0400c010a5a3b513b2b208efb16543e4d585212032a062dcdac51fe112ac0b3b0efc52ecd9c52a956105d084da55c1c00fd54472784f30f491a7a8a21ba78109832bd7f692b9e27a9d588c1b3560049efb4c37935d90af980b5315156fa976c6963ebbfb7ba48ef9762f00719865ce8d6a2bff7706bbd34474646dbebdb4a3e3c342e0ad0a17525be0fb3c045ec3e91df48fefbd179baf6c5bb0216b35b66415597af59e92ec821419c882fa4f6faefc57a739dec20228b7681ae9c33cbc4b20c081f483f033edeb35895c9b7e8ff2e7426cc8b3069dfb342c1eb6fb79f052136d8205c9c063729c2f7c01ec921eb9a206d6772c435dc83304cf898f75cbdd700c9ebd66c00dd234c6d109df9c3eeeadebdd12f2e555e22141e337ee1b0568489b3d4a21d31be6e477ee86087b919c8eb38de7ff3c98cfa41e3791cbe2f2be3b25bb1b3c1e4e55d6d02a81c0a40c6b15e3b992e274c7286e4a3434f3aff695aac05a6159418ecc40c149f69a759a0aec7049dad1ed812a3f4f957dfdc6f7285c840489d44a6fbb29eb488eedcec0e6636f0531b33935d9db36c2648eac3487ac3b5e91fea57e63bdb43dc78a71561764a52b6b036d003739b52df8f8e1467cbe6cb2cfa41216fddc107076882056a30b22c01baf96923bcd3525dd9dffc76f3f8a32ea3060d4120faef43b4077d5077297320f4bc1fba7be26453296077ae162972adecb730ebf06088dc2e1adc08d3457d3c65d5f43cc80f1cde8b6b59bb4b8df3198d48b904d857bdd1902047ba9738a287ce9e829e1faee1ad25a17c37ca690b6d4855a53112fd72449599090380b4886aa5faf0a101d14f10e433201acf9cf4f7d2ff5bda94349ac8e6535696973bdcf144b6667106c6484cf87a60fa180035b25db2a0074730576bc00459d1c08e1f9269c5a6a4f8aff32e33992d74449d679597b8309ed101b9fb081bd203512cd3d14ed3cf9cdcc8d8442df69da05579a3458185467ab52f08d8cd8c5b03fae7ba383c0c364c4f6e0006a30f049d049b7e69498394ba018e898715ab048ad0b703ac240c2bc89471509feff161a7b1d31573eef347e09abf83dd8ee79e24737fbcad8cb7f3e193c525b0c59181cef806b9983c756c07c03be6ddbaaff91662ad7c73f077e79d61212815559df61dc9ecd3fab715a1f3905ae01ffcc027d6334a5f1ebf00993f317cbc35f970541fe8114a0c69c6d4cebdcc3019303a76b09e72773e92ba10fd2de90f2be97eadb3fd8450ef0dfdfebc46e614f66158ef77ccfbb72b652f9a549cdd447487ce55b71994d6189d10d01e70676fa6668dedca9f0270a984a7cd2bdcb6b1c0c973d7f697dfbc024bb88fe5f0a9bdf4153f33e873691e05ff71f2f5efca4bf4990c1e44fa4dfc9a7a9da667ebad287dcb7fb8529fc2320d5f256ef82a60132c0dc11ea955266f7ef14d1d1fc092b487cd5903f530f2dbeed647ca58f623577469dc3ad04fe0d97c13049b21c6cf13c335c04567bc4cf6031834a929b9fd79fafd9caf5ff6bb4de4fb289d4bd95f45a46d4dbc97c47eddb74ae9783806155655a8fd0fd10e48c73183c809b2f73cc4e07c7ea2d710420033aa3b7cfcccc85fe34b5cb97a8dcedfb5f39978a85ceb141d2d6c5ef2efb7b69af6d472a36c0edaf1cfe316c91140d0620bcf74c7afc3309ceef49847d6d45c1b7f9bbdb6e1ed815be4a3b05750bdaf6f14b73e91bb1863ff10c5bfc5f58bce1ee3366bdd0715cba579cd67067a110e33a15f92ae4bafe43a191cf966be57c6efe3c604aa238c602dcfc4e3b4003b952d7c3e4db66af665e8759f4b185223fd87db7e627efcd2643899a5cebdd36e0cd6c5f4d70cf73eed4bccf25ee19d92f4d1f02eddb57ed1dace6f96115e054529d4c63eb3e0c7aa2df6845306f332a9da2ebd0ab7d0a85bf7b9ffdd7f0072a644b6f979130d8f5789ca0414cfa596861deeda3bf3baa24f1dc7d5752281d72bf623bcd328ddd8154d9c3ba5edfc820f6f4f60b2b8c8cf443932ac7c192d7bc694d59973c5bec309ba973ed3e5cef0f267bcec195f7666973cf34bcff4d267ba9cce3c782cd0aff46f73cce8278e95fccb98cd99fac24c29c038231e5f6262fb25b7d1bc3c19294bc694911925c99c5232a5349928478694272365c9983232a32499534aa69426135a3a5c2abe9f710ded51ea199be1037ac78063c89f89d63873623cdc85c45d39d2f819c550bf7c838288fa5ed1d26572df9132893bd673eaa2a848eb2d4e06931bca4ef2da9afaed68e67dc89687b66f4c154705325491bd273afb2b4944a3e9b0e5ad13bdffdcf503689f7fa91ff86bd61ae0f8c14d086214163f93090b8b276911538adb030fb4d214ebb1522eaa4816c39ab92280ea4be52d7a4744cbebcdc961eda166837c51addfc3845f359dba7692109ec7716feff17af0dc9037679f3cca7f01fcd43fc8794ac0a0d32e9e8017d8002893398cf317a52018556689177d0ec48c9d2f12a6b3a0965659f936fbadcf5f901e59f6de52c44a09a46448aa37f80f2d13031017056791b93326a0ff0505e8a5c4f7618b6276f8e251a1d90561c69900ff1a9339987e47d9dded209a5c40998bb42a431f626e149d0f690d1499736bbe6d3a719ba486e7e87d00a8da453652477f7a8b86fe72587ea6635a09bb074e696ccd552780ccf312e5628b400010e1e6ccc3193a11ac7b4d8cc8b18f17cf2ea53f85d078a412ae8cc6e78a3540cfa25f3121bc2461da57d3fc875dff6f78de755117d210ea18e3469bd0840ec70cded065b78fa47b53aa3c8426294ae505174a362a03753a0293d1f96eee57c85fbd4fe80e52c1bdb689409aed5cd2df04ed9eb1718d671b1caff6369a3f28c4b606874004e7fdb50c1664c90e0e84eec20a548743a7fee038b8fb04ff182208e8b61e0406f220eb2f1fe39c0be4a0b970b3cf0480b6ed82f76ab6d34ef88572744d8c618d4a6f8883a09864b096ef7512e21b2e8d420fa3952fb46369ac1fe8645927b8c0ed8129b636ca70df76787e5f8e93c96913ebc67b6c826c6ab9dc90ef3782613b2285e180195510412af084a47abf12c8274551780f7446974c4c9c0d4905b067c2728328d82a37cccf6413a301ca2e9bc0482ef97c02fcb9031ded25eca9d51d1cf91c88851ce49a1201cafa1130ab12e3cc1c1dc4f176a51b729643dd1967156242e628c731f8d6f0950a0af093435f6ca6b0b3018bf68e82bf3a19d89bb66f41ee1578bc8f1c3d8cf967a57e904d1bc7984a52fa7136aa54f3297006b3b9ecc19c4ef12c68fffbac81c024aba7066f38ce07efe6cbe076353d93ce1bf7c05cda5a998f1483f22523c1a1e0fc9a894c68f00a5b7b5ba1be9c9c7be001aed206edfd2999af30f1c49f5ca47594370800ce382d1cd065d7d36c2878cc2dd5c83519b46dfb149a105254352cd45e8b2862777bc536197cae151d19931fef239965a5e65e347325eed2616f8117323612af702e2403062fd65cc45435968afa1e349c2b72e16c657472f0bd79f47f375b8beb0792b6a6ba811ce1c1a10ef64513220e27e95cd98a8d785f28aca6db745dfbb1b0799caae79e4ffdae5a24715f5054cc21a72c102d44f2ea770eb1a21086a2e501e00b3ca71e3617b62c5ca0f96fe97cfc7b18bf255ce36d93f6c6562c37bd9d8c60f1e590e274bde581f0585304a035d19d744f63f83c6ec4411403dfa7453c6e10fd72fbf7801b29f58e7e4131063ab1f19874d3383c3d374b3dad4a0a735040a024e7fca586b56ad7d4e39711cda9ee89944366d82c8b6d5f33e497fe474146a2c90da8c95af0c5f88c5db5b96d725b4ab02a461e6613d48e260089d0d8a3b0857ebf706f71f5cc4361d8ee0974800fba6b76e78ed8f039f5746c0a0f47792f9d021424007a19f0b34f84d14bf8a0a724109496ca546b742e10c6ec72fc8abd32f0b76abe682bf46ba7870afe2aab1c090b260a1c0a20a0a4c0b27f0fffb190c2832eb30aaa53a7bc1d6f4c82de8ca53d0817e0ba77e80672d22859a7a8924876d1f1a0c0a4dae120fa435ac8bb0c4fa81f9dc5484704c433c7a76171a65e8622b6fd626a3ac56cd63dde855707c5d04e784d9c24e5ac4ffc39f10486299a6c10ebc28926cfa3a0ed06ba52bb7e8746d27086c9f21b3e7932ea18321ae0c531e7c5738786d0106faa29e1a836e4a285c69854510fc85c2eff36be03b6e9ff3cb9b28e0eb16ce077e50ad265e6dda249b3de252ba15c839e57d44f4f04ebb2264e80454543d08160dfa1f3a9031e0c85b6e3ee3eed77c809aca7d65992e51ec3e427ad12c310f33c3971763b5261d1ba127d03a0c26fe2e9c060f176329776d96a3b3a8f2f7f3bd329ca77afd75caa2a0b56df74f2ae2cc1d291157ca6e255fa3053b5b5256e708a8179126035bb68fb8ec5ec347b9daa121f9062711c0fd5138a4e7871139d6100bdacf7828329b2e56b99be197c86f229e164014749c323a6f072f6e9f00561f895bc7ae80eb9e670207b4fa5289b3e17747df29aec0ef610c2e1b915a57440742cdd2445c5178366483056165e9f1adf2c7c9780f45e8e7bedd9314ff886ffc994ac44532130cb087374d48f353170ebdb2af188661a3fd8904e7f25d799479b04475b327ccbac646c72941625986155f064bd5fec4ad59c1602a6cb459a1de1cbec2162ff8400deb4aa7cd0f715194ae4f253bf36a9186d383cdf13b9be78040e0088e33f669d625cbcc1716acc6ef2a60acae6d623616b9d89962a4c1e2ad44165ef2f627cd818bc95092529faabeb5338cac761b7aec4c574ff205881b10fe9587eb6ad5346a93f3cba8a947f43321e9a2fee451a67658fe53890cc33bc534f001554c307716a0f58bf251afb0610d1de56d3dc9063392acd3fbcd02793c516cac3e55a1449c689c7f8a4df75d67ce7009a5c47a9fc9904149f4fb06d00f97ad35d1d9f3746ff0a7b53e1aa856d90eadffea5d55fdc20c607182ffac7128f9152f55c6bb76cc60a0203b347344b3d700b7dc0a27e170b77e09db32d5bfc09207b5e126ab26a7d50f23293a1013c1414013005e5310497c51c30bec27be1f2219d9c54b8444622eab2e3751960ba59efa329fa78a1413c2900fe2a4bc0c9fd33d3ab872c8f20590808d25c75a6a0d85a15c498926fa4712a98bd304cf5baae436237d35ea97ee099965db01f6f18af59d44537bcb0222fd6bb538c30d8d73533d02efb25daf8e3faed00b90f948cfd3788389f1fcfa38c819c010e414ae87231709fc4e462762aecaf8506e2d5b43c984a013f3979076b3a9f3ce7f0875da62b9dbda339f0938e60a7b0d3159c19dfcf5ee17e02ff0dac1c17991655e856e649281a1deeede86d2c3c679a42fc4e7d3c8a7def669787f0e872e219a64b49c6c4c05101c48aa8e53e9608e8bfe8d126db45a5bef2a25ba97cf1657467aeaed3554e9d70ddaeb9222182ce475c49a5dd940d0ece9d5044cd843907f56c02ffe993318c9ff92bba948a88dcb0bac0c7fa0ef757dc67b63d13f4004de08345e78ef13ebd895c84f59033dc05d12e21c885d147eaff538b6b71ab77713e0a24885316ea0ee672a2f94db1683908c9081f6c5058989daf0ed8cf22df1f1e6815c6e58f1a671339a46a52ef24571d2d75c098a60c4767cddc1988d8a60e1f3a1a8df49106511b71d34ec44aeee2510cea68ff8a5b612c4ee00e01077dbe7e64c663a1a53dfd8cb8ee99f462c5824a8d1d59a4b049042124feddd4dc83cf884392d085af94602ca8ff95b313c2ed03488dd9ace860beb6e5a9171ad6fe27b457a27ca18fdc367b9e63c4e6977cb4c7c62912a10c9654587ab35bc2184cbbefd3dae8db46e7beec5465c6ae1adb4cf8e0040eeb1a8e22af6a68ad216470533115fb37833004ab7c47ddf7091ace7964471b3ccdd87308acac50b20479709e16ac5a9aef8784137f80481d5b76512e531439bbdf14202ce24e70e833de396596375ec630e51a6db212248ac01af6c209b0a2603b7574ac10470ba3ae42676ddc86adee6de4ea85428e83a4be28f5afbb68caae068508e5b434abf2d381fdb02d85a680a44e3d638b3dddce36be318394ef910f1ebfa7db53e142b288fb2eab16703b22126cb0c696418bcb48e546c2d2b15877aef80442fb892977a69f995f77483daa071f9a1af0e801f4b95f158da219ec513c845a023a6a557c4d492cbd53ba0751f3c44b4bb29e74c6282c8d55e767e04e72336de674cb55330ec72612717dfe43721c0db05c0dbf0e29b8f01c18519d6d4b2a64cae89879c41071ce1ed7864b38ba1b7b05b0d06f555f85f8b02458ba44ae33bae106abdf3a652c681973c2772a50f9a319059966ed275d31dffcce48c438aa8c1b9350ccec3333c7cf518eb3b66f8b46f2280a334ba74e1b7776e8dd53ae18c7ee4c48352d3780bfe45c733e49b4266d36d3a11595462e4c12e3d1859c83d6fb8883527b9251ce0443a5a2db4bdd68c8d79c8ca5c1dee879e4706e05318ce3955dc5e20fcff1b784add7eba3ca0aaa2b8a79c34ddb836a145cb244f0adf1b910a0c759c6ea2b8c2beda2e62e927216857f74d6a193f0028fa4f978526d6ca24c3e39ac7dc7c1887487249ee5b8c104a089e4d8118ba283c15179a04ce7aa6cfaf54f574a3c1d9218e85cfee774c054ca6d0defaee075070bfa863d79f470282e716c0c9c54a40a235f5e94510b4e4205910275905c95a2750f9ebfcbb24c6590164cbaa58049705c010695d1ddc6157f9d4432ef44cc348dc3f472b43cde94617484d6a3ccb8974d94636ce3b4468e59170e418db7dcbcff5b5b0695461cb1845147968faa49013ccca7d3b519e525fb00721ba7214f10465649a7aaeb4a2d6e0a897d880d870674d8ba7503390ec2ce91b9c39f3e36ece79de2945e7d760f9c5d4caa010a47e083700f921ee2af2f3617e1e995aac64f76e6b9ad55f97604b4c2083da1305732f13d09fbab4521b85363d6d69508e3fc9ba7382a65e9a566f28f2a6ecf510266255570849d15eb4a392d4368e0a4d5042be00287fa7a744efa3471748d4cfe8e1e826efcfc80343e3790defec4017832518aafc198a8a2139e733a0354aae63bb7eacb54dac962b7ae0d513a7e619868d7806f5d96a05765a4650acfc538d0ab6a30f239520d37a6c5b8348b63334bc0e4eba81650dff0b56c8047469edbb36e304bba7b267000b0188f14b782b3d97ac00ea0186c2710bf742a2efc32c0c981fa97900120c5e392faed2366c5f1726088a214e38354b79a5f35568db81e861054cd66144d14d7a612fd0ca32786c8e8cb79d6f62a53a5bde209cd3f912070d786258b81410a1c04574f1b9abeb0fe68c1f38cdd34d50ee682a6f0865dd3916f11ce15111293d5842623b0af41e915a857ae7704b44e6edb300b318f49ebf0e8d0bd5c021f0c7531ae05850660cde6fad0090540ebe023264cd6ad47889a345a01274e216b6eb09bc211de2526dc20d0d33956510787365a88dcccfe876203ff8040fe81ce86552d751beb6260ad5257500a716ad0bb09bda58437c1f9e31b443a435e210f2747e533a4dbc8613c26dc4324c235041f2f45327b99096002bfb354b762e2bd139d010d4508a4f93d7ccddda3514176990cf238ae1c5cd426967e3d7231152e7ca1815971a3d8c7bb3e0b6828c3e6db1d2749eb06828162c0a08f799519f2e0ea7b2e4304cf2204b243abe3a38db3fd085b2357f069843b28a14f30f3883d8c7d6c2aea57abea3bd3ae884f7d5ce2a694114893f3fae44038e5462cd33ec98faf653aa36ead81e076889e2a7e29d4de4667a71f240ce338180768a3c3928256d6ab026d2aa5a94cf4b53f62c87666c412690c730d2f7628a0b20d95b707af009dcbe4080ed81b7ac07112ba067a78e4dc477c98e631721b503cdae9745c128b240c87a8dacd7d4f6269facd7c49deaa732a2feae42e66db8b35acb6a9c9903f90d9977d5d90652381a7eac5159808e71cc6f3dfe5e7fa29d31d297d8868edbc6c4478148c519a862816e85cbcc56fe79f9e260be215c8c021727b5dcb165a60b422cc579af49ddd7589d0c1381617a2b7ef242f800ae518c55a748201975c1207d4be72e3768d088109ba469af207f7ce1bd21c53d93a692184addd9fe91eb2021e5240ff6455313d2881436407c0ac723c7fb7753ba2f395d1cfb9b79479124c345fda1d82fa19f12c08d7903f3a857bd316dc149bc1e6a6033f70a69b969b88f0bd2d8cc1d7a14541e05afffa973181871cfd28a09111e27e985978332cb1edd1ed1a5a0ada114dff0b5822cb971061119f98fe4ec5d84e97e93f0ea6022f2308f9dfe0fa12f38101f14d07b9ab6fe4ea7770d88b527aded7873e02e59f7633c662f26ac78bb4445275c4e52c2cb61f5fd917ed066c91ec959076c06f6544e3dc410fdbe7d45bc581164ad3596efd9c90b12ca7280b280963a11c381f7f09ff994b438df59b1cac088afdad482cc73b5f5b70aea394593a4cce9370334114b475b73583dd3d10224b7b997adc4aff2b7ff9f54da974d11699258b6029ca28f0ce023bf5a9c2a10aa11601dbdaa54c8ffa200411c0ad0560ca7adf145d6fc81d4eb71574dbd905657d1c5287b52612c2bae40c2a63dd484254386b1ec8036ad5b7731b15d05ffb4d6df151f504542bf504ae5ed7dc8642dcfa8ffa04f523846b312f5ba0c913716a31675c913049c3fb3518bab1aaf0bd5f0037611c286a87e2edd221d48cc7273b47b7eda2c6a593c89cf654d7f05416d0c9b9caa953ab30f0ad7c7d63f6375e8b837088e0b3d4c1145d417f112d7249e6026bfd61a5205f5489deebee5e982b8f1e8a76a561a5ac9237f2106d96da116fa104aa47e706459e7dac6d578a32df3fa250b8d54c74ab019453bdca8b8dff693912e06a5b9e9f5d156a104b1da89c80321328b1818c08cd9a14a25cdd8ce4397621069b0baeee4f16b5c42c9c6248c05d945ce8682c9ad74572215943985726b06b40ed6448b3b2c5913d0f9518a1c138f3af53fe55d136da4b22d001233edffb95a37f6c3549f4730be9ed1f49627c350202450b5b422d2d677d3eeb3accadc2e5042901954e6a81a1fd5ab38fd27f0429aa57d42c24ba08905df8d2f144c299507da7c31fd0d84dd63200e0ec69289e4d32b08fc12bf4789f10a66ca3d87f518ec8e096a62dfce5a9dac39c0c07c601d1f3f28a80309a7293517be3a9216ce073010e198ed0816641bb62dc19ed8fd028fd69b131598cd5b91e86be333bf74d73ecd627dc7746b6042e40236252384723024b384c23e243385223a2cf26ee64d262ef97eb7b64b83f49eb7d686255d92891436bf258c23ac31403e3cce61c04c9d27159de8951003d8f9afcc5f36a8d248b2a3aac68dbdc087ef64680e13a8dc41933d79fff4e71f67e232cb942397d23a782b2da229e110c388f9f9c429f01f0c998c99f3dd0604d5bce0bfdeca11149f49e099b2522fa589c1b7c0f2c42d1f6409c0a545770ab53f64f9e7b487cd74ef435ebf9960f489146b6d40840d2a313a630c6c8bc00ae8e36ee8b104a0d52421a77b734af66d6a9ef5520c29fb66c16921d929141371bbd266b90fcf7ba69d20027e659b5699d91a66f512b58181f8034a9187224d85fe578ca9741dba413e33b289c523ed3666ddd3c886b8c92251532a6d3118903c4c8544326a2db7d768c7c8483e99c24952592269a494cd651b552b438b50a9aa0877347da965258623198dd48ef9e3275647dd55271ca4f54c74ffb620af5d6c86ff1bd3b1672da4930cde99c52b80f5b993e2681b64dca5d5de618fbbe4a5d96f61918b612dba8f76a24682e54f606d6cb04b692da83e477af15d0d5aadbbaa8f5377729292c0cfabf4f030e08149d81d5d3dafd4401dcbf3433451ce2a56cddaf8911ce30fb43c366f1a7056dbe7f79d452edad244fb51a0be90ab9b6a05f7ac3e332a3fc7babc48f983dbba005fa3b8120eb1ad08c0c2e1e8df077939fbbbfe175275b37b70d8e2791a7147725ade70d2e13688ab44a8ab321060a2aae7712dc1656d05094ecd4b81d26a12862470b4f4f349cbf927084b2e7f4943087b8481281f95d2983f1e9c16613bb1bb65a692016414d3e5b75adc228a35c6010f84661ac9f3e941f1cecc620cc52652a4b9520510b32d77f30895688d27d1ca33a5c05085eb727555dc1458dc1601e819d44e8106c7cea2f824bac1c918be08ad060af31bae4c91f9a138ca08f56d572739f16a3653ee219d1cc8fa183e257ff537c9ec7f6a40ac49af701f521601004e4c4af668f1daa0431972b1dc9eda601d57b45665ab786ad428b18f41b612c15e2ec7f4296c24a154e64e1c7e79fc6f684289e36619ba664bc1bfe1508c95b79fd558d561f2d78731be863dc385364dcff4861d9f6689d16f4e131684e6178f1df6d9d258ea4d73c93470ea871d6beae631b6446aa49814cbe76521707c2759faaf2548b1b279a4810ba4618b2014034268a19d4345a2929d52159644170af35d4364a0d144e85a7dd35c28646ecd5a2d6dc2908a2e58380678f88c67563053a016556123be69728bbf16b166b2730ab6257fca41a479afbe76fee8cf50e61f72f8d2219d0a6ef062bcd55ffe07e5f83b59e725f661e16c33009c884dcdf30c628073e2f2e8e8af6a3cae6ae2c099fa7a9d89cf5ec99719782dc6b0a8a4490727b95a18688813886735ddf1233ea82bee467965533e34745b85dc0eef7bbc888adcf93fb2bafcd9e196b7dfcc5f7d34c593f5b97691a40d2413b672f2c446ff468be24831ad9c5235d96328047f825b50fdd1ee6e847d1853acfead050b8d6a0fa776caa8ac47edf5240ba00c4997ad4de94d58e0b08a99b10fe2e7f6489d997f919bc3db689aa60e3fab1c16a9b15698b0ed9359fc9f054db65aed01fbd449d780a19bb014b0b2023d3c7272e278c0eaa00689df3f382c13717640991dd5a7a866aa11d408f93782088cbffc9ee1a077c431c87e4546407fbad82774412b75b1e72d8572b6d4f4805f95d6c6431089c24c4fa8f22fca2b59b05af85877764ce994ea05674b84eb057cb47313800de70be7f59efd46e7b6d6e96f960703c6edfb5367ea4f78fd827521adc6593df997f7258cdc3dbd8af922debbb3985d90d8c8eaf36a2b1a8f5d2f1ba3b35d8d3299955389fbeda64599deb43ad5eac8874793d787786f1398a01dd8feeb3f5010143b6106b2630ed707b2f2c3f4859ad8a2632f2966f16d17457436e1da542857907b6d1a1bbb6a456c6372d39363803fcc093b66d15559fe13fc6938e33c8dae1450c26465a90dbe05ace8f94f5ba454901c97ced60558a0a7001acad3e6f487499138234b414ddc1845f5f3f08a0e7b195653e8e1f7882a19c137ccc5788b46504142c9f20edeab3e6580021858120c50147579822556a432bb7794fbb250456856ab668fcd477d33cce7642f393bdc73d5c9a3fdc88bb460feac433778c816b7232e11c1af111a5b4f7cb4f1bf322e6a9baadc34f0144b2bd1cf6200e1c6934d586bd8e1ab56ee8ddf22f82e49849d9bc0a9ac0d53f6a15ee0fdc149eeb1f050327a694f1fa2e1dfdf84ed950b1c54071d512032a9c78a9e7bcde4c4cc0f5ef731a7c34283e61c2594131bfef0ae03f68ca211b35bae01faee7eafeef2a5c303589daf9b5972e4c22640a9f992524b04393084f4b5d19127289e1ca25ae45a5725c029a2a9c8d610d8a943193f5bc4d70b0350f790a02f935d28eb7eba1d961b53a86fce7e4cdc4ba27d2bad64595f4171848d57f012fb4688bb1b27a971a487ae8ffbed70d22c8754609a8ae14a016264a8793deeadc0cee9878ed067b82f3d9c77f867f2c78a18dba947573cb3e4c76d0ce794c755c6bb7611c54734382ba1c3d8b383f3a6baace6d2b3d3ef308d0e211d8ea551f84836eb5b080c134edc6e3d9c0483eb6d20d2e0aefc255c86d581eeba295916d5710fba55511de560ec3c56b91ddb808b305eb23ddb630a6e56a22bfb128d6cbea21b87929a60b5721b661798c8b5646b45949cc9756457a1bbfc84e377e4d99fc4de9721e804676b24e50f7f1fd2b6e62657407a44a6f99211c7f0832a23d65dd5b4eabcc7d76f75ea8dd57981cd9ec15e43209d71379d126e831c372628ff5fe518ff633d94f5b76c2e8f1ff51b4ab7a2a4bec118eff4ed13e5ebfa76eb8a85233f855737fc17ceff98b2a771705ec80dc619fe22eb0fe079db268b5307074d61a61a2d585df7289dcac9d914c56fd69085615ada4b7950fa8b095effbb0b7b7c54d5d2ca3b296474115cfd4ab6edcc15bc5804f24e6df2d477833c6170d5968abde4ba6f110b7e6413e3ac103da08b2b4b37c3e0913acc2aade2107067c18b74c540b4b68c57feeb7cc21ffca23a86d1a884d69f4a5e7ad79803a3b8241e400bd649f525046e1060817cea20b01e02c97ea1a70394e7323eedd947ca579e48592358b09e4f6d4d032006eccec5b0573287e1d93f6a6a2c16c05b4390e5f89381c51b589f369b5ece87c631342d1028017357862ce7eef8330afecd0dde8c8b72336ede30133ce192ea9ad6af2fa5d1f7994302cff9145f9b73977cd4398f5147d646e73d3bdbf9af82d30ace0b338fb19b4cbc13112865af520d9f7efd4f4fd4e78fa9cdc127ff84ecc529e4051c6c8e04f446c34d771877d41d2c9c0571e301d6c98b402e207d585e339ab8b7a3526676b7a36856d0a1308ba945a17501d06f909a5a246afa874826c3d4798e5f34efc7ecdb6a69e71914abd1901127bababe9af558b7fda969011c9433c70830d75e5adaa37cd0090d09a0e02d12e2b00231b0d137617dd2cdb963ba024b36e28da7056c6cae001448271cfc6efa310d75116b50049a892804460b9bf5ececa73ac055dbd271b3e52b7f51dd510a08c8b37e410ec0d56d4843381019ec396e6bd241da0739d889400bcfda8b9a5147e0e617ff2ab32779cd0c850d4ab051c7cd804ca046ecfc78ae87e5b85b88ce4e589229c68c735d2adf097b717c03bf1b56f62b5951fe5dc8620d63dc597c636c9ed25c96815aa2170d580962792d8a1a9406b6f96998ecbd9a0e2ee3681bae3c6dc98dd03826cab043f10007d9ae100bc9ae368295e18cb50945fb1161d14bb4d4ff45a80324a0b2a8b219bbdd00f0dcb76e391a8290c7faa5ccdc3a0e0efceeef827daa1fc2b3ddb284e10ca8329c1f46fd989dc621873b9baa0a1a692ed42654a6f92bd9b14c3c58a2d56e3276541ad1540120afc4879902a44958d81eba7bde56d2ed0259cd44019c3459b23da76b2190a1ca506f6771b68b7228e07880fcf41f942aa8348e0a052789c5e08c9cae225ed6d224e8bd356a8e75a2eccd5f46d0024f795524229d943928c0ba7e177558b062498e07cfd9435fd7b77d7d1b1a5a74ebd3b8d85a01cec24f49b3433efdd48a526efc7b8bf3750649c40782593cfa1acce07926cf49ffcf48a4578e01985b3b190fdf0930e10c8a35ec995fb282b4f42ac674cb5d8a9a1b175222a1e295fbdd5da6b127a83b7057e7466141435027826514af24740898036cd3dd97a6bffa051f636267c07c455533849b08b34f1fcdf4216f2cfec3c01011d478c76ed1848acf25d2c448ba5fcac4de1d7b2b3015ff8e1deaafec6496daca7adbc206d7073afcdd6308f095f52f4458abfc48982abd6bb4d18185c48cb173141a08672fdc6818b0b5995eb3dc3f79bfe106a6e422f2dd43b36b65173c3caec29fbc357e4927ec2d3fd47b0b74edfcb49003fd3308c3d1fef8bfd13f57ade3a5589349f8ebd804f7c7ffd95ec1c4a6cad03f75008d1f4016a9b84caa6c448f3e38e345cffc1df3bc4cf82b42a7a3e5dafa8d87b46b8fabe0a6e07c4bf9018e65feea4aad9a33a67f80659ee6ff91719ac67a6bdff0f19f4f56df17cc274aafd9ff117f83ef457d50ba63fe84619f7c9f5de71977fd7dfa6593764e18033478384e658cdddf8f24225b9ff5a4da8e8dff46b07fc50edc965bb823a0140f32b0330fed57372f9f890fd8f46139dc5639cffcdf505ad4c07282d93ba9df1735c23c209a7c274fb8dd7dc5989cff62cb4ebcebf66904e8cdbf4338f8e85b7dbc4dc1fa486cbcaf0596362e33e7e7226f1eb792176fb97b38ed66090789cb5094b907fa3e7547c41dd3e31504a32a3f2af1f6e1e3fae9869ae51b2a6a1e8a6a9f3035aad11bb378ace4e3ee1635a64b22875c808f7c2ff04021820d4f55136e3032d57abac1759eb9654930edab9b031997f0727ec4e1e22bc089196641b4e3412151d3cc868976eaf997f9aba219792063d8c412e87b373db63170a1e05303092032c89e3738ef62983f6a57544907cf9a4d3a9fbc259b88b8313675e9f55bf88b8a2fa90a6192bd998024cd3aba2fe3526a7173b75e9718db3d759572f532b4b373a3f5cbf24ed0fe1ee1bb2ba51a0b88161d759de596419257bf5dccceb321edf23e95e9a8f299818ac907edefcf2b9b1b10b7862d3662f45e2bd6cccb6a33c344faae5e3104c59e7c6261070080cdba013e8db27a706a05abb493996e0229d8a4f5a61ac374b388b11d68d9cf078204eb363b7bd5f4827c5cd83316ac2986369fe0a88a33db5a43b050f16ccfc2d2be790880004f582a389e8e63d0d5dfdf4e327967bb6f1199d5c450d5c6576d21cb9b5cb5467b9d30b77adcad6683e69177933377961f4c470ce2859d3abef737683fa7c2459845dfb403d49ffc2d2398e466a4aa215795cec02761b71ed9a670032b6891633dfae92f74aa70dd2431ea809000440b284fa98b1a6ae2740012781dd33e691257e6ea5b02a9554d9fedcf73c65578de8266e0e88f189b259908f0597280991dc239879c88b2a7b1a199bd34e539a62857c36014e65004f0d64f328594f58707b6314073c991f82dad19fecbaf471d159d65aa7a667ee996675321bf3d16be98e6b2b0e7a3c15a045f142c2fffd3e8625c9e8c17a6b2ad40466aff5082453ec74fa399fa3fb17d6a95cb90ff50e85b038264002d7419c9412295e228a4703e2bfcef5ba5a675154408ae198da0f561af7f7bbf215d4786adbb4cf2b8ec579d143f3eefb7f3f3a5c7b6ecf82b28ea9edbdbd9f8cfcea66f9554657d1529392d85b0511413d8da13e42958ebfa7772f991caa7311d3c993634ce2ecd9dee35f89ebc6f623a401f7b451748bb703407955e4a3dfae2e4e0aa5415bf6d7eb69359a541b8bc268d233a7fe512047c1ce6de6ef54efe5a2344e13df91429666acf02f2e3853921a172c354cc3fadce9b4aab453d6b70ef34514c51ca74c4c5100a1616755f2747cd34545df5c8c74b9caa5af87e81fd591b488f559605adda23b4f354743b8378e6435e143e51d491adfc7680104e4dd792a6c638a1f51d4001f010745e36f8f5a785ba6b36b29a33a6d32187e2f8b225c80a979ec0d2c281916d115b270619d0dfe35076188cb8ea841b199b7764d87bd69109cbe9ebdccad4c6170332a95e34970e9acf1e29dea6fffba3ec0645a3b866df1170be3bb303a267e19fdffcee35480ff6f109a58917dbd598b0c99d1ecb935250a84c40808d70269e924f79b06b34eb57930f535864e8a358aec458db04f9df7dcf0fefc58bb0aac700decc2f4da6dea340d83ea6e234935425226045403d2ac54c85a174cb1b91bdf9c76213e1d291c274b9a06c43c2d3ec32ed856f22502a5b3eef5f0d24af4f407169abc50f50a78da68bc473e4811bb50d064b0da00f8171c37fffab641eb5ae79faa5f8986854e6fbd2efbb56df570befafa3f8c024dfefb26b870473dd36b7e11abac16b132c5fcfad69f54b57e1583616c6d67e2f7171d606fae64b687d526677339fa49ac198c22eae667d96b59fcb09fbe34aec55e8ff5c378a24c22f497c09978680bb98a10b5639057c48e4deced7063205baba066e11f88f0c5b50f40d3cfc05b70d8d896061b81a62bcfde8f64877c1c912b39bf0c4e21c60f33058990ddee52cb4b1a91bc3689a59a110015f11e8c515af2a4400ac02ea68ef25afa15459d869d8a74df2eb4463dff5b54650426790e11916b8e60522b87ecee03a9e773df92e1f2e34e6231296d7ce386e2f9b0bd1f7aecb659272d640bc25995842eb51a36c0949779d1cc604dc3bc36b5bc54bd3ad97cf13dad09fb61c8fded303c37091e99f554cfb2d107f8e33fa0f2614e8ff4c665b83fc59dc7552b5900e57a69d098b6954ec5bbc8e6861bf33c68d31bd2fd60066a4fc2bdc7819615aa6aed73c1b5ddba870aa77d9b48c6528694304c0db7595828c421c71f27a4689759c0d483fede24a6e4098ee3bc5405fae91bbc97eafddb4c898f9f629d581bab017a34c37ecbda14739c5abad7696c1c66a0d2c2dbc1c62610d0acd868eb80182c4096f431096fc1181f8e8628b1c9f1990a4897a5a24a316730bebdc0d3be93b9dc0aba4c9fe1375d84339527bc6e7786ed2697a6029a10a4f0d6d7224ebc73bd8e88284b7de287bafff6edb4afd013e8340bf502452f958e53fb255df63facfb021908b5fbb1deb1e23600ad116e2716291ab9534fa80b71ac088cac8d464ae837d76e1b2bf7b36449f7bf2b551e10d7341bbdec080c7d1e2e2ef23d2a38d3cbb954422983a8e4601e663e991f37db3cf3e751050c11af2b3e5217907c7f3e0cb919575aa2035c9161ff541438029560b7b4570d3cece0393395cd599e48d37a06e9c6a269350dbbc1ec9012e2de84c87b43affb2014498deb07da02a48f77031dd7233e7fd896f48866d68d864162de8527e7ff9ee7c290b00a678a6e2c0642591f2f1fa2e365e410e86a4d5e69e8315df70c8d388a59c767aa25b69b5d8e19c64b741491e4f23e2bb8a050a4c3e9330b274aa34b376d1b2da884b8489499df08296a4182a6ff02ec877ec6f2af6ddcad755df8aa8e4c9c5c9bc1cd06f44642c8cb501ffab8c20e878b5a7f560d13f132693b2570ca1433391b578606f4dd5e0030122aeef186859ca5791f8c7b5a5a83c10258d309f969c3a07d526dce822969133c100b524a03f9fd48cf850cfe94b4a8a148dd298a0f8866ecdc65fabe4c66b734ad71568472406f391ca439bd4e7919525359fca3c1da3474ace85fb8f41551bb8d0efcd09c08d2bfa87548868c16da4762d872d3c49454254ce9f98b09ec4f4174c310484252403dc739a6d892647f1337b5d58434e5abaae4df3f0db60614766c3b299c5a2d9155986629a2239976bb67e5c635924fcf599670a80d1ea147ce1dadd41cb2367b52c3820b16d3190d57eb132e501d4220d3f31082adda399eec2bcb9cc843f8297f2a5dfe0a2038000c092ad97835ec2ccf0d1a6fb3be41cd522c3369131e95ce6cc2b8fbf9da08dd7b65a5a4820aca36f3361ee825b7fa89e85ab767e51ce811ad340b7722a186a31ccc6091411073d4165d94278179fe1fde8f9aee95ed28a4b0c42d63127cd7ddab72d80b554d08587028633477a53df14441ef3c2cd2ec8870f8b804ffb4070c057662c45f5e7ae16e656db34b889b0846bb036bc100bd70d5f45d6c2fa325deb839c3bab9952a0a7bf175aec3b1c16997df2295d884a42625f1ca1b1c01338aa48c42353181ceabd9e2411236b56849e86c029dba9d7d75f1a0f6888e277d3c882da0e10bb2ef9fd6dcfd9f7e047767d27f477fc1bd5b60d396fd74a566368f694b5ca1b34707cdd47cea10d308b386c3574938133904d5c3e4d7f9e20afd00ed5b9eed4f219c9b3247b795c1ccce936a67b7fabc91715e7ccf0826fb85a0a52e94843d608623d7df4ea9077f5c13102919f2d765a7bd808061c03808b1b51dd8ddbafe2980ac826bd141ff063c70c5164a5798f2046a7ec2146751255af8b5d506dadfb5aa1587188c0b907a14c91e3956031f7c620a23c5c2ce025895328a0cea5751ffa9d7648d98720c0df574830a533a01b1c590811e0db5c0b7ea77f4fd44b699bf7e76c04d96ad1135dea01bab177d233bd0c3857aaba95e00192b867ff054cd9e62ab997823a0841fd9d69d067a710d362ed20d98ef634ee39ce7a57c406b053d1e0ecddf97ae452c0781cf2b673a264300eb93deb956eff30d9a9a27a2282cdb3783a8ba5f0764b51717cad5463f6bc7906b1d7fe88446c4f01d043fd722c2d9b35b0bac942b53d38fb86e367b8aadec1cab6e38d8b9d9870b54e58885238b17f4c15e16ee2a39c85566c4cca61011df4bce2e472c8d911918601013cfba2baa1456e6bb3009cf5bdc1e6fbc010f6977a1546246fec9cb86daf49a4c9e27228616894edbf49720044dbf031cd1209c1529cbf65fd17ac4a545e2a84f536905a9c3c94b899e59db7bd64318fefb6ba36e580fa6e6273e00c6db83704ca64e742a444a4a6ae89003239310d7e33f8b168e191224ccdad34566d24c10c5b1f4569f98ff836ed667249ce12627a3312cf18b7ea47c35318e1bb27c655bbb4501fc699252213840672aa53aeae15fd04f3216eb6278e7762ee21edaadd8285a0a50e6b5ea48edc55eb2b7fc2694e53e54537d46c3f9387a3d3ea64b3af12a04c9e746653f56d40330f0e14de211ba892174bdfac5661d45d5dcb455a5deed1a35cd7101f1ba9f9aa2c23fefb99848b165e51175246536a29bce56c34b98d38d64f98d9fc7359cbc61c8aa97574f384a762dbc0448269a29064652b00a0d017dd444d1a584684c64897ed5adbe88409614869bfb00d3511d7bea49e7be27d3b93b93bfddd3ac83e6d6d10bc5bda0a080d1bada78ff932206e73840e98a554be66a9ea1513c080e8d3512a9c958d410348d4308c33bd7787b3708dc1016382d3f0cc85137e84c57c1fffa93b156d06b3eb3fb1cdbf97a54d537e41164c011cda65e4464d803666f071800e62a58d4941a2eea85c196194f4f9ca69e8c72f3d84b11f96f0019903433d0c552b6aa035a3ec79c1fc82273b7cb419fdcd96d63c055c7f4e71c48374a552bcbef5da795ea1203289b1d1731c212f4c94f976df98d3ae0f83202c682dc53b114679308163a584b4a57daeaf15bcff21cabbb9183381202182dfbc07168dec49905bc2a683a6b583d002157a3d5ffca3814d84b8baffad0c77123af596b50a2408a7a91275807bd96ebcf6774e2105d9bf226abdee11468110fab3c17ce37eccd08f984b184c1b52b3306dedffbbd144968ea57cd7a8473daf0dc967ec1d4b61f8f2f08c4865116ea41f60abacb6526ed25f5637f11e223e28f77165d370077efae6720b3cb9f0b86fb7c52f33de0ecc9627f4336ab87bfe524363e4900bc4021b30f4633c5f546d740d07f04524a42c8d5138bcd3222208b6e8349af4966b427ce46b82e9718201b914ca19623d11a7bb26b7c0fccd92e0df2046faa4e54429870c36673d8a0f8d6a8fafca0b4cef67ac79b467b8e229c8e5fedea2ff120d447c0717000bc92628a80a8c5e7275c92daf615562d2c304f5193ddb079de14e3d61e02f6ecaad1f367b6a745567361992757927cea022e1f39e4cb8de711a7ade97603758606d99e20d382be754643ffd90ebafaa1430f8dead3da5f49e4ec7ccc36fa98d0aaa7106ea6882f25da0a2ccb04be5845f9114c1028b2b66d0b0b82aee8298920b2e149aa59abfc4b2a156081412a808f2ce90711d12881759be07e5cd9edf5a1298a856016d019169b918d64365ad9ed956deb53a70baf57f16225fa81dfa6bd124f1581105eeaa5d96461a3b139e1ff3757b1f228aad860e49f876cc4c1c60430d528822e3646363c36b0bc8a953f2f046243c3526c34a9640d0cfc7f142c85c252247ca0c777d6105a03cd1a40582a8e323ad31a4c7856a347e685d0fc7f94ff2d6f52638d18a2b8178e2b0c6990d5a981e5ffc5af08fc5c5043843412790c1c313144a54194864f1a5dd24099d228e151d898a95c99826158134d40c1c8dfa071c7ffe8698fe532e2d050a247d411fa3c9697432034ba3c181627e25d5c20101affff62ce30434ca9543e5f7ac2fc7063549c169d9f2f406154aa1409263c07fd4d6738f9174356d6e08b016f85110230636b2889925358190c3da28f9b51862065cc21862855194960a2985d62880ab91786b9339511011992907188898c399e7b29d11bca402632942083e7d5d302c72fafc0a854580ae8111c3ee791ee731ed9f2398fec7cce23a8cf79e4032626333a6f3223df6426843799b1e04d651e79539942de54c68d379581c09bca30f1a6323bbca98cf8a632416f2ad3e54d655a785399ec4d653a78539926399f63c1071b38d920c78bd29bbae0c09bba18e44d1aa0dea481096fd28082373991e44d4e1c79931341dee4e4026f7272c79b9cb441c1298782085cf0395a5cf2395a28f2395af8f1395ad8f1395abcf1395aa0f1395a64f1395a38f1395a04f1395a187d8e16429fa34599cfd1620b064d4e181ca2e6730e11fa9c43cc7cce215e3ee7902b9f7308fd9c4340f89c4330f81c432af91c4310f91c43f0f81c43d8c0608393063fac79d30f3bbce987d79b7e88e14d3ffc30d90090097c0e20567c0e2007f81c4086f81c4076f81c408c3e0790fd3980bcf03980f07c0e20f6730031e17300e1e07300c9f91c406e3ea78d249fd38691cf6973c8e7b4f1e373dadcf1396d2af0396d28f0396db4f89c36547c4e1b273ea78d119fd36687cf69a3e673dab44e135c9041930922980003092c68928797cfc9a3cae7e4917d4e1e247c4e1e177c0e1e8d7c0e1e7a7c0e1e6cfebf820a32780fa214e04d5180785314ef4d51bebc290a7e5314faa62829bc294a086f8a32c19ba058f226288fbc098a1f6f825281374121e34d50b87813942ade04e5006f8282c49ba0ecf0262836bc090a7f1394306f82e2c29ba0b4f0262815030f20c860021305165cc04193a7400449c018fa92134dfc9798f82f19a05480ff1201fe4b4afc9706b066cd1a01ac4962cd1a12046b48376bd6207184116bd6ac5983d3d2462423d614b1660d116bd60c214429883552a4f0ff6c6c5032438717405354f02207832ea8e8e962f5637411001eb890c0ffe73163f9d1c963c6c262fd135c70f0ff2512b6e0e349502ce026842d58da034c59b490cd498bf2cfee31e2fbcb3fc60557f602f44fba02841a4fe4a1211070f2ff432cd7d0ff8f1f27da1e3886e701288c5005cf03387840045840828506b06883051d58bcf1ff5aec5f8f4e0e5f3a3a2d5d66c7061bfe4b35fc97685073f45f322afa2f11558104066998f87f521539fc7f1e8b68d16302456a4cf2ffa5ceb3023622c80010f9ff1b33ae1f2bf070728923ff8f4385140e009341186ceeff935810000c5f9008e20062fc3f6988444688124250c58aff2f75e1582064881486f0c0ffdf6870c41c1b58c20062d4f1ff4c9a204024a43b5296b4f0ffa5412049a2064a9610e4fe3f8e13411130b17a43863ffe9fc42d702394628f31f4f87fd21ddd54034d1133f43105e505151434e6f10562fbf3d2048941306052bca03c725498201e823c288e42792e1d162bf3a0c697eb0be23c4bd0cb35be7807fbe7a178c73c97ce6a140247958af37e14b246a397e7d2e1fa43f115102aa8831fd7d2bf1e9e1f950a15821d0b0acae14b8777de6acce16bcca210c8ca181fb39431045722dfb925965002167a2fa129620f189daf07431d812eac078c0e90eacb1ad5595c1c3d8c8b9e988b44504ad098312b56b0202fb33e50b3c491e5bdba2962c678ebb58538cb43857cec4242192808f5650f95c5a020f10b5fe21766ce0241d71048c48dc69087a3ce211f47d62b4bc91f180c150a81ac2d7a524496977b0862e3cb85ed2ff31d82475fc63e509441a54205056179ece1d8feb2377e3908e3c13c974e0e5f3aa81cbe32068a22d7fcc37ac0e860aa116489286ccc528e58397ce9609e2b63413b431a64ed0489a8cf1b8fc24c8412b94b07f35c3a64bea03c57c6f8500816a942b0f70e6228cf9551dc753406002bcaa178c487502eae8b0e4f184cfcc1b020dcc7575087ca1e11b6b908ba8c3876c487b898b5609b73d186ff2ff36bde28fb274170030987920e3faffb2fe556fe488afc9782545f8a214d098dc879e744212fc1c0caad2f1771224fe8bf74c64c99fd79432532a531623ee41e698212d03fc92b85f929bdb0bdecfac4648fa5a33d17c8c3f00d7a1139cf32f2309cd5653ea4c1310459590c3991f789093367f18c9abb8cb8f0b543b53174cc1ca34aa58e008fd8a52e3825174a5c2088c364c4201ff2d1d3311961d4a627024b8c90e0ffa340492208531157fcf311fcc214d14311658a98520407ff44443208115efc7fe0474407449886a0840e91c310744c4b97090a6ae932e3104f5c3c74094189108208c185144f881ade24041493101f8422538248230809fc3fd6d265b8ce13e36abcd7886553104888477c2808feffa6204cff8f0a0016b47bb8eb08e888bb1034048aaa0f0cc28a46171fd29c08e31bebe0111f125318377f792bde61429fc73dedb5c00fd45c047d8eb80bfb070efe1f13fabe091ff8f8f7810bd187285ee41e2a101f7048d85c8df78394b634f9ef210794d0e7e53087afdc03971ea0fc4b898287438282c090ab113d226f877c8b39f020c1835282422e94bfec05f419834442498bce63879772e400a403174f42a603133a3c31e9e02487397ce51c8ec8219126c1811231448d469c892b982b6b5511ffe2d0f97951f92f8d70431a429f97e5150261297da5cc248918ca30c9bcee61ac8f0914d4f8f1228cb7c030184aec5ee24a7f39dc61d280d38d08388f1cf12115092511fe4b21348900ccd19737f88de28bcb7f0984ffd207a50e2ab9a28635461c2be250d4f079accf1b038689864750472cad83e697f072385261652f1c31be02b1aef50a359631916ff05381e3e7a97600435c8d61c63811d885600fdfa0cf0e16ee8540d8fe722ad5b55e21cf02585eee41e3aba51ac5cce24429f022354ec41015825cc0d17394c33fabebbcafc4c17f69035225a5398ea0fc632934de6484e68fbe9c41d65187f1f0681cf3e6afa0546af3afe80bdf429a93296950094901598abab080f0769ec449654ae0a10bdbe06ae42deca893a274818948ff7f2a95fa2f39f92f65f0ff3827473800c88de14daf25264a4a949ca239d11732c2f13c3c2987332202fec7a0ff0ff45c99afb094d846ac800c543c96e26abc3060c410158e1132f1a8cd31ee02b5887d5e18cd89544399170d654e06cb360c75a319d22899fe4b39ff250cfe4b17fc972cf82f5550a2e0bf34c17f49829c5293ff52041fc57f8949024c333879fd7151e42a1247853904bbe849914002092498e0cb3b186ae44763e8ed306320a6930168084afcb80bf4728805015a84e18502c02e628812b304137c20f68147d8a7930a527da0a7c61b7bf441f20191e7e166792bbd88a9d5c2bfc88536efa104818b8fe250efa00b042265613d8112b10fdc397ce94861759ef871918be04a63e10b3483a1c430bb5c19db2aa8207a612eca18dfc15022d683611fe87a6141fc158e4118383a000b9cffd2cd7fa9f45f225df24fb2845409181e161f1fd2b9033fee612b2b564ebcb712571470f1854c424afcbf5adde65e38a600c5ca3f8992ff274d72a58bb1f906351fc12f73e5516c796357a2affe7bf61f093862bc0c2cb53f2ef21eb896c752a118a2547b0d38625fa2e1028698f423623851d9946e9320b979a4044738d008238b6c40032445d4b0424ae48c09c041c793e630e5b124812943792c51744a7ca80492562530ffefc29b547b3c2605f4e951150003853c1014443ae49f64c83fa9907f5206fe49849004f92705426af34ffae39fe407898f7f1206fe4917f82759e09fb4c73f498f7f521eff243cfe4977fc93ecf827d5f14fa2e39f34c73f498e7f521cffa40afc93e0f827bdf14f72e39fd4c63f89cd3f898d7fd21a6a04bd4e8328869c47759e95290d1762885a21cd12ff2b31af324fe3c1eee95fcf50980fdc29e23e45dc676f9d22ee8326041836f00f6482410af17b8554ae6046b9853a0ac2c48fafc8e4cd8dbc2c186ad43c0cd30879fd13396b7b9b87de9198bb60a823d651e7891e91117fb582443108438951a41c8d50c4cd3f30e42e4c8aa7c59efd813b2d30ccf2338a5e1710680c7908f6e8803e23e8237a2c8e8c1b6390c9e27310e79d4ad018822e23ee6dde6a794141118c89448231628c63f6bfe85da9f279ff0fc19bc6741963c2ff9b808d19c35a5c14731891877beba85e9b7f625c99af5262aa8891422a91d03883740314462a0534c90b200069518561644413a2f9d074341d1a34684a40d3848426282614987ea8783144855cfc29c34d3f26fc7c806152542fe0f0c2eb05d60b31bc40e60530270ac59fe89a3f51d7ffe770f0272d41fc494b0c7fd202e54f5a487fca52c89fb2b0f1a72c0ff85396a23f6519f3a72c3a01f814b0abc19a67d2410459bcf1c5430009203742a62f81ec8f8f222f9ac25bde880a3a21019f80277541a4232a2d44f9ff346ff281e3ffb144655346100ca7702f1c4d5eca90e2e89f948028ba6cd145897fac0a86a1386b7baa0fc442b07fe922f4433a7718d8bf9e1f2c9c67193f1ee667e71da3ec0535a6090a8201fb3cbe7b07a71ce52130cb32090503573c186a7c8147b9c5f272178f58ac57cb133bd10b4f196fc450da6be5ef6883622e1abb29990affc0b0d25a6d11180a6162eb45064305056141435f82beecb1f26b06b0a807e31dd6418cb358b9254ae153424e048e01c1607ccc20eb28bbf290c87b10f7c5ffb35819e31895ed4ddcf8ff42e2176ecb202bf1e5a292c2973516365894f8f0d5312a54b00861014385c57a752c188f2122cf17cf595dfec4f0acd9fc7b75206bc7c4b3850a4fceffdf98b95d7e0709b07fde4e99ff29d8e7a9014d3b50fe9f0b6557d7a1e45f47902741c7c80b4f190c3bea44209d2eff2d6d840214a14fd5d2462dc0d10204501f8861a83c06041372220c1c370b04bd2211e345230ff3e7612b9d3526664ce444210f83b1e0e4ff15b9c2e60a15586a7c81578ab862f45256d046a9d407b65aa056895614792cd502c32c56f6f8dd63650a2cd5257b63c41015f2d1957756f52a94fc5771234c98918baa546af32a413f6915edb1149137661f51470c51262a397c6a144314e889aa14074ca93dbca1908baf90bb5e61449d1d15be84d4f8ff190b41cc0353aa051418284ca97f2c0d306d69a0b6d779e1145b0f2308864bfc7f00deb46d79226f85cd82476d2e8a2c56e651a144168b44a1f87fa3b1c5f5e31c51324cd7e89f85201282b0cf13fac0b008b37bd83922306252b0540b0cbbca64856c8f65520f0157452317c5f1059aea1462880ac110fc3c95eacb3a95d224d116d1f6c8ab222ca58da16d2101a259f02f1ef1214c62a31157e5f0954df38b69b230a4b911d090e6ab3104bdb8725864924f3c9672e5b0884b1f5ecc6044ceb37c628e429697b300897c280371f1e7681fedccc3e8aca0c60a0378717f5c44fda0421063e50e6469c1509b8719431d7d52b8909873c8a560c15045e03734e4b1781186ea20d6e21a6cb158bc08133326450c5f212862e20bf4c24cd4258b5fefc1501dc4505158acec617273972b875ee86517162548c4886c9d1877017b94f8813c23e8c345315fcf9006bb8cdccb08fa889c670933823ee290068bc02ee3ebcb0e03fae49dbcd323f21f9ea3d1d5e238abf37a46d0070b6775f91393657f3c8c989636823281090ace8b7cf3a1c5a4c00576421714b4a4b06a6923d5feb26a84143040e1122c25625254e07e151fc10f851754e03e4110d30916309d3087e98433c2309dc0c53f8649c18cb63785f38e4991826152b051e41b93824d390127abb2d85e498c0f282e822c8d198d227f09612e1064617c359291600294886163ce22c10428f1e32e570bd439ccad95fe78f7758c95bd6c264d0f456097b12312100b849207ff8e90a43c89e7494cfefbf877c384f17f120f3b80a3e7040a2860196685046ce462ef200417051450c0ac5cc1f6c71f278cc7c1020788c7e1c1d9791c1d1ccd84311182e20a62889a321af1548a7fde4ae7968a8f997be188618ff1d11343d4f74aa58cb211163002128fa58282466e1a61fc1b9c26a7196efee4fac09f5c88fcc935c84bc0a494c53f298b2a388c7c487be33fe9061b4835889c771ad41cfd938cf43fa9e89f44348a7cff935edd3f69fc2789e10c2ef06bb1866420793851a0fc7f0affa54b40d8ffef71d3078afcf30f14327d50c6a78ed4e42e1f24f1c118a5a5cbf031eb0fe4c146f083827d802595dadcf4c11321af4b6dee81121eb014f9ff29260fd6782cf582170c1c37a7260f380f3af87f51486c6152bc74e0c4ff83b9957744a02351e442194c185197f9e142190cf7050c07833cea881524a652e20b3c3a62714f88b3c4fdf110ec513c28ac3c6e7e24e5f3c4537a69f5983971f9e2ff73c857d05e78e232e64f5c7afec465fb1317d49fb840f9131710fec425e74f5c70fed413c99f7a0cf9ff08722ad89c05fa6c16b8e5e36a8a4016df39627d5fc470ff3738b0f20d3736bc08fad4b0f3cecdeb8f7217d0e767f45a5e97ff1b1ad4fccd11ceff8dd14dd1ff67716b2e0c07823f5cf4c93baed710287ab90562afee25f2a2eeed2c9b7f45d925f2a3ccc258798b61651e868760b6ce8967007fe2f9f9138fe9ff37782a8fc206db69e7037fdae1e24f3b477fda39f3a79dec4f3b13fc4927913fe9dcf1271d2cfea433fe4907e84fb48c3fd12ffe4421f0ff1748d013e668f4c688017dc29d7776de11f98fc8adc6d0dbd9e1ac3c86e1e16b7c6519bdef25e489a84dc5c35062c6baccca2b23be1a43afc332f67558142898c8793ffa7afe6f5e23962f3c3e9ceae7cbcf0fa7e2ba88c1f2c58baa8c8e97546acc42d90586ae2e4457843210988f0f8140acd706bde02b2d3ef60881acddc33f4fe8e358a0e149ff4fa21f5271a50c31441d752a51ccae1b305f3e7f5ecb0bb5e86db125e6a2f00aebffa60a95ff312e882a561e731f02454c4a1ef3165de2e6ac3ce62cff24bafd67c1f27f83efbf6805849dc5ad792ac5c3df1e38e64b95321e85429daa80b9c926dd3b048b443183598dd9c8ebbcf5126fa648f91f732b04b3e02a86d97907572ee69d9d77be54860aa522ffffa3a050282818f7734a653cfcff349ae5cef4845161e1cef474d1e9f9e1c2a8b81f31403a3e5a7ac2685b9affbf4101735846901546b3afff9b139e8c202bcc8d42e572e5546a93b5ce504ca5542e4bc73f9ef2f5e564bd0e4fa9122b1f11583955ed9ff5ff2788214a5485dc55a1fc3faa62ff8fa71413459322fe7f4ba5302c043b5544abe1b532ff27ed8457b5c050e51275ce1ced88f1b07c3ae14b6bf0a60412fe5961c86990c5d2c1fe6f46b811e112096e1840fad295120a243c9e24c71ba4351290812086a89d79f8a7d30aa087e615f6d737762f520738704820243fa688719a8280294884f0a729abff67b15e1f96e2aea3710a9629a939458a142950a64830e5a328d27ab1408d61fcf33e50ccad1668c457426018be44fe81ac28fdf3a6ac10002c04319fec838da010148c8fdee7754ce423c8878ef850942c32cb5e68a6645996bdd040c158a0d097c3a0126c4823252815a4b90c1d887d39ccae2c5fb821fc88f241390ab32acc2bcde2963099208a22a72848dc847003c2ffcd07ff371edc748003207038e243226ca2c0e10a1a46a872c54871206243881b26201105879dd40bdf0e160003070b54f860109c2390b8410f2041409823bcc00d5254f1854b0b30047243ab0f17aaf801dcdc7007a9b2e65f4fdc700342251b131ec8c28638946083260a115d6c304011932c48b032810d5a00436578b004151b5600734547a291486a90c482315f888fb051431a1358b1061a2ae01a906812c4901c401fa8c14c0376b2700044076a78e2431c0850c3444503234314683109ee42031858fee021004564a1c1879b0c9482b052070d60e448a4a696c0000d1e648023c10366625043880f381f48d182831a2daeadc104165743c3104204612c40355924a092c3143244a83145414917e20ad7111f1a1b1314a9f172c40024a0a0220036568e420bbaac404514f2a84a2019e030d21d8e22f860070e341d2019d5514375718941c8e8000358b34515d91446420e48450172e40ba3a9013624e49c214991253d2d688520004d119b141368287243164503e89c3c31c644a14875033465121e5c4550e4201a628527ce10396281004841840d1a109161c61f32a8c96110052144c806d8f8ac400414468432515c218208dbb9018b2e96e4f142e4a692409cb8d1c88b0b392cf1296a52c62b0706700a04d0127a7151c01d7f808100052f0dbef4818348469511900ab498675ca2472bb4e880081d0640c0f8aa230e31a31a0e469dd70c7d74808d939182454028001906fc20e65146102b8e7e70c404b4f1a20436c63944561363e084403a8836906c0e18882a20967c8a106b50511056800b0d897c405a112ef1b51184128f041176644490009e0edc114eb12474028a46c2cce0014bce00400745f031031a53f3438d1e82cc6084176054928499a119ca086005a9e364cc0c2650718219df87c9b588083aa12971886b025150d0008d2a805c3d60896079620d8f0b8c0762e6b06193c3d5011a62e4c0060d0ee0204f14a0022d00c00166610337c3939315504dd7a40b263c2801721fa454187a8208cc0944890d886183940f033824d8f081fcc737c513852c31890e387c3370f9f9831221263e2a558840a784477c4c48d0e20c21cea8a045071746542a71b85a4f0001200c01089281567780156dec7c49a435491893724234815549c9042422f9e2b2d8d00e69610d49d660090009309ac06cd0c14ac3451a348ca86007560a68dc6029b052871c29bd1103b85518a2800c7e548203932a86d688c141cea13086c21419e0112863b2a1114e30e3070630d24506431221611f8a9840862d16d1a6dc053491010742b02d66988832f4f4d0811d6a868e90c189f7801d36632af0da000db18a024b1e5e15d81f165420d5883716c2811e7a7467bc16be6873c5153e10f0263043480d165cb458e121e4c32463e0716695801110d0598265cb6a88bb712b4959b0aa893491e286127aac486bec20d3a4280321382e10a191063c4944a8003344e08c22210421941f3102022670b943480a1836100201961f7a0726590211190e80fb19dd1f1b08230b497a11325c32884a87233a993570781181382ae825d8914724475060856f60040a9cb9c128f32f3ae01a48490ce13c68e921e7113614e13e7bd8e103243ae11c60f1da83e7089e2dc8181e68632bb01b020530d3c56833661f714e7eb22d8c3696cb333a72c61a3b474af540941514da026dc694d969c112bd802d8cb2382134b5eb88010c41c00733740a871f8e1029e4d0104c2a2e22864cd1cd014918d522580f9d1325887009217f47c7b92084891ede019d4cd3839a391e883c091b3b3cb08556415e23c523e5083479e424d8d43158b0c128a3118340bd31c923198533ee3801a4048d200e54a21ecc2c510a1aa3043229288d1c2008089f2b5040a4a045d0cf14638680e2e791a010f020c3bd21010da80ae9e38f2109548da8b6d8809045525ba4a0ba8108057090269150b5c5b5c3d12062c0a0caa0888a02530008c4f0c716310028840c0e3138804915461338c111c3088198294118c823861670b03cf1528315314810458b4920f994e68e1cc8e02e3c78a589a25f3125cd03d248238395365b0830482b8d668917267e802193e6df70d5e1827884c68d3f3c40e4f403031a02e0b0250c1d0cd48026cf280810474f8626003d682508428517183a7083c6102f302b3098f104973069646009188898c233012a131001c318219e5062052a0a80a1842636f094a16487338a5ca083008629083823010d3eb8353cb9e1cc0e455b24c9429867bc0cd100483a60f3e5cc0652289286932858301388216378557400879907e8ac8001d111589831d24e982292b011333c681049640a1433cc5c3089c9688b4e2db3079b0bac1185a6421905d81108ce214a1865c009788ffc8e1a65b64c02125f87cae078e142a469162133c79528909ca105116498a80483450e60481b32db0d11850d349003992c01907c91c1933ee6034f8861e68b9495316a70c9b4aca1842563926882021475e4bc31068616acc0c183cf69cc09705862a5ee718198465c6fec0064c08298300ab1c20e2311dc10f3c3cf227c04712085981730e00800b6c862881810062082105fcc200265800f0e3e491e410314811e2c4e50020d1b806cd879801440408102d2b21aa28badcb094027302448c3864b84307d74e0d5edb1850ed38048d21882059a2f61c4291b68808c0d1584b9f200221ec0818c4598264328800c283e4f7eec90e2103e813d42f88162080309a29861e3c72b216c81f180ed47fbc0080aa01a61e3e70140c11b65cc60bdd086187f58614655f382124b7430465381cd0b41468098d010d37a410518485e40208606309068d0442438583a3065f8a1c100ec013e002304258bbcc0c8242a30629638010329ac80031812380904e203194c7c49440a32e6c0e31218be78f1011c0d303101802f3a40200f452e7062fad2a5cb1019f44ca08b2f00508014af3b9448c227101074f0401f3c0df0b9e2031e1104d82af029428191471f6c58f1d919828d158340b17d2cb862cc923200145ef418230a1d6cd474e0458a4ac2b8609b448b97af4b4390232209bde02f3787b02009245e6e420f889e93e8224708af295c4f7cd0c50083c41082235968d0451f32c820673882a5cb0a7c24e0c3f9a2e58224367869e40041645c48635f393388970c5c40028c0c048c1507b870a6834364e880121d179e0002c41a276dc4c185112e58ac3ebe2b5cc000438b1302d89071f121d741d44725657079e1830bd4b86137c1e583186e68e246ea881e4208915124808dd8a3450f296cc14514687a6a6032852240748d9e2c43126084070d027a4c8bac61460e1bba6ce123027974800336475b180089511a6da2c85bc2341fb81e7450c8162b3a4a2368a099624b043b00f1c8135c42d052471c76a800800468a105011b7cc143c6fb40cbea24891c4798205ae84e0ed9f0b243164be67002913d6ab8c9c28601829490a58d27b20ce08b2eba80e0db208baa8845d634c9e249962857cc6c51108108f7886cf3810ce8410257460a85ac30c249072e080bcc3088253d9438202c1e91c0bca91c36840a52a09492c0828811cf91050ec005962ea694d800844b07b0e4c0881c4cbce021002c2e7830481e812c02068b061008c1a80b225ae001a4cc20414958791e2bcc6809f9b0791e228d8c0f7b44f13c3a681cdd36393c4f054390a0a0fdefe421461a2220f1bf2345f604212dfcefb43c50428abb85c890ffdf2945a1c22a5001be841e3de8d31c3aa81e7288440634d61cd182996fe1c9b3c0c8a4af1158ff989411d0fc69842a41df8490ff4ff32711285037be43ce0d70122187ff9308ab073a89b0a5f5faf849842aefcaad9308524e2238f92fe2df490445fe4f2194398570c763a710be789dc3bcd2a7100c1084855c888f9f770a61e814421714847d99b340b03bca61984f2090f17f838105ef05091040f800e8c33f73fac000ab31bb56381a6f2eb8b140fcc1566376612c964e2af56f42584619659481731b4002045b9c3cc84e1c0c72e2204c9a4937c0c2d6b9e5b4c1b8c1f8ffa25136c46903281ee8526d6ee475ed8dbcb51abd30af70da00e7ff65c0030791c9881b300460bb4a59c2a0810376686025e88d3cfa50e38495422069a10c251040481523ae082730848c03a85145933416c1c14b05175884028e5cf91191e0c34a6800223ca0b132a4b8dae081ab3c864c395a63001ad962064b046a8865403083142db280a4831b8215521c4da8c18027c8b0f009b1c3255d82bca8a1e3001210682ab018193031059a282c902175650455133c5849ccb863043df22064e8f8c0004e907281cb494ef603217cc871830306091a4458fc20c4ca974b8cd823ca1b2b9c50e5032512a046070538faf8b9b102354646c10c1548a54e1ae09313362727588c21d8e980b144a9d4ff959598c1ece8ef48d4115b429fa7bf23311f6516df5f11b85920a855add7b7c18fb35447a16af3495fabd1c5f76905fada3da994e9052c4e395ffc09a79c2ea79c7acab15205c3524141d8286617868de077c2600d8ba5f37f03c1dfe0dcdc7c4b97b9c2fa748ebac73d3e86b9a0e74f1674395950c64de9268252044c989c2a08d2395590a3e54f1494f1281696f9e8e99c2858f3ff28f1d59d28e0b2277d9d28b8e0ffc5fc698209a0c48c49e9c1504722177536e7e26af4c84c702568628e193feee94f478ee13f4d10519d9aac3935493d6a14b990d04b94c2c5bcf23935b9f927e22278fd90985b975c3224e691e58ddf6b48cca14e93c92b10d4d1e2630fd813827af78069f1b187953fd01387c40cc40241309cb57b54ad304058f0942f2d7a5e38cf7223c9bfe9e6e5b9b22a9b280d954d11025941e2f752110d05617c357ae194233ef45ffac07f4992ff52241e38732a295194432a51d994af080cbb959857a7920b5b8979cb9f4a3b4c2aa5f906f5a4af2baa90bb8634c8aa62945b57267d5531caadd35f2266d67371fa26ef51095285a02a28ec4009927fb07f3d625e81e161968f0f81e36bcca318a25899952d39f38f42a1b011e42c91c589522c4ec459aad44acc2b4b9880e098e262c889788885a8959857e20634f05f894e255378d260f12883e1bcabc4234a86d82cee99f9c4e41db07f3d5ec4a30c06f411b9190abe4c1244e5038c7caa908f9e4a7397b882e6441fb8e3ff254123490b1f852912ec81a2ffcfcbab2b57aea452dc0b47954bfc40954a486c7dafac1a3f2e4efa820482312e4cfabac237c873a58ae663542abe415dc43f95b87b78c8b770d087c57a65c963de1c98a274812f59608fcf63c662e58b05829d4a1cfab27b267d355285111918c9b2089845d82c12c206b0060ca0c8218a78ff4928d2e47f911d940213a644705ee56a852b4458ff5f9ec40535a4730786a00f9343b8281d62f4433a04cbff7f6688212317b9ffd21dff253b6af82fd5f15fa2630e394a7114a208bf1245687b61a703a58ae663547a86966a86d6c6a4ec9e1c663055c617a872b5c295cad552b95aaf495f4262cb9481179e01842ce0ff7fde44481e24881f38821fd8fa71a30d423810248a17a407419a047204c81c8010d1a68cd21aff25359ca4f15f42e38c3fae30fd31c49bfef0e14d7fe4f0ff4c7432df92c30ce69fd4fd0b71ae56b885a7c7d50ab78c202b0c2befcc7ffe4b6590f147e1f8713bf31f2f2308fe8451a952299e1f0d4c91011f78b020c52184b4418505aac0a001131c9a743181a90b3992c0c25053fa1cf102041f04f9e2040c7ce8c204094858300970002eb898c2010e0ac0411c783800cb0d75000961c50f267dd4c00725260bf460da83cd1e388226f478630426ff4df4c0ab37e5c18688c7527980792c950708586a102c85c715580a8ff11f4be181e5b1141e39ff58ea8e3db0d41d4ffc63a93b56584a758714d08b044bd9510696b203e8ffedc0b0d47e531d5cd451039632d5c1011d6de8a0630b1d4e1e4bcd71c53f29037300c9a1084a0cf9c85be087a576de793970d068e159ac179938c6080a7a1d06c90830c64ca0f485042ca0a60a601105cac75998952ad8f65a51a04480b492c03f1bff252fbaf837e080430c515c6cf15d74807cbe9ce9f9f9b265fc40355e08ae82bc88f151715a74c284e1bca8b0fce8900123465546c74b10e7fd0d40dea8e38d32dee0024be5964af4302cd579a81aca6f44f158eae7cbcf0f87914002096fc0f0c699375a5085aa155678a3c93f6fbdbeac553c7c891f77631037e0f87fce52ad545ccc60911b59fc833e6eece0c60c2856de9f3784e1ff770373e3db98038eff1f73876146d94b1b54fcb751c3ff07f5bc46166b3cd1460bff6d3c51b5f198152b56d818c2268d2f02599df502c9087d01b77659c6d9d4369ae515155cc6956e7e7795946f7763520338ed6a9de59d595679ef775b198e222531809b956a95f38eeb6675a6f3284ec1699d55ad665ce20d04bb057038eb99b67a9b59b7e1cbbbc7caa402b8db71d574dbab7559eea616a5e0343c29ddbdf3deb1bd1aad09e0b44ab5d53a9bb5e67b33ae45c169d556ab7395566ef86a4da1e028ef28e5da669ea5dc349f08e0a4e4bc5f7ea5e4da866d9d07e06cb67d629a6d93f29b59bdba52ca4b7a82d3aca6d6e68ecbae65cfb4e804d7c99df3d69a4f8da7969336c1719e6bb633aa55d9b5aeddc904d7d96dee3dbbc63b4da7c62a959d06e0e8cd329debb5f7decd62b9ba72090e6b9cf7cc5a7c7957e5365dae5701389aabe4b3ebbe75186323be52a92401b86a73ed9c576cafd497d2d5957c646525382d25df36dd342dd36edfe40038da4d2dcfc9b39ae746e9a69104c0d56aab96dd6d579da7e5564e1d52125cd63bd39e332af7ed3deb5657eeec4282ebb0ccb28db9edeac6e5241dc175da6a58e3aa61aebb66edea4a11dc9aace1285a92115cde3793b3d66e937dde22fe5114a9082eeb785f49efdef9cecb2789088e6ed966bb5cb36c777b66beba9295c33944cf1dafb7726b27b799b6ba124ff9528dad8e49427077eb24e75ad6b65d35ef1d9e9256152908ddf1b6698ab76dafd6b9665f2a2e86d9a5c9d6eba33ea4355c46f7c67bd77177b74de23ae91612101cdf34ad67b2db7925d5c431cc2ed5e6a36a67d726433e6e2c907ee0faad16db8d4bca33bde1ed035bad96b7cdca917ce07ade66a7b7d6fba65be26c14bd2e1ba9072e5f4dd3b6ceadf54ceb3a3e728c493cdc296fd775f9da6e6eb766ab2b6bc8bfaf470c6907aef22a35bfd9e676632de78ca403a7bb4dcb79f7cdb3bc75bd9ce57a0165a41c3899755ddbbe37d67ac5545b5d69ade0c0496dd334cd769db3a45d0d23251e5b9ddec065fb6aa9c999fbf56ce06cc7dbd4b2aed9c4d996759d9dc1a0506287adcf1995500d5cefd26a5dee586a79bbbc73e5b0e87b5169270d5c5773d7699a6619573c3915b7ea734655468ddd2a9f984ed93bc65ab7f311d771e614638db5dae1cce28d8a91ee322c65763b4aaddef44b11b7d52e6f1dd62ad7b68d65df9f57f4bdc08f88ebb89e35677dadcd5a530cf6af477b5f06834281fdf354d7e78caabf66bd9adadbf16c6e5b27b9a6f99c51e991ab986f5bb659d749aeb56ea9b453e42cc6b7ebeaae9a7653d3737555a237e5167252ef7ab74c77b452dbf3b6bad2aa722854e5947706aeef3cef9d1bc69add7be7d595a31157a96c5e4df1867915c6c5751dc7595bbba736b37bfbea4a554675408e4b6efb9c5df7bed9cab3d5952ad59c2c16d7dac765db5dd969d6cd6b37cc2fb53e6754685a1ca5b2ab9d76a5a6aba6558942b1585c9b61717d5bed623aab9557f7a9adaedc9e8abb8ec6a34f07853afa3210f539a3e286b85ab37db5aceb33cbd3561464a894d3badd7ef5de72db7656893cceee6d6f926a5d9f12679d35da3d425ffe41a15028d105aae4f539a3da2b4e6f8cb1a55bc5145b9aef960971b6eebe49aeb7ed6a2deb74d2d7acae96475baf8f8eadde75cca9c6b7ccb9eb249796afaed42417ce6995eb2cf3ababd6b7dab7e65b058eaa2ba96c2e5f9d65946bdc333cb5beab2bf7f70243d591664673f86e5c52b9b7d45657cc6dc76575669a6b33e3d3de6df69ab99b2fd7b36add7618e7d105aa3659bb0471badbb4cb68be16e3ac8d3a1bf19945c5553a67bd1dbd9766de757775659512039737e536cbb6a6699b96f35d5d39f19686cb584a9d6787f9762bd6f8eaca51e455a2e1b2662dbdd84a6d9b36abdbea4a15ebd5ca64a6194d2ae4ed60db7a7d307059cd6abd9d96694b6f27f3ea4a114ba1cf1bdace70b6cf8e6bb9ab92f70c6f7275e5c759fbe32a6dd66a86b39ad6d17eebb53dd3b3eed595992c025d2ad6abb5597c5baf6fa70c97f5dcb0a6d19d37ad77aab590b1724e29379cdd2cbbf8ce39a7ec6eb6f1a639d572d737de211f7b7850a8cd3d17a8512854969f27990c4717490163389d4d6b7b77bbac93b472fd811bd7df972f8bc5a20b4c89e1b6bdb3db4ddbb46ddabee9d595630876d904206ed3b6e35ac6bacb3a3933dcf98fc8c7a39dfe249b2f96abe55d2bf4eaf87685d93d6f7d67ac799dfc5ad9f3832bc65dcd5cc31ae769575752175e98b4ce29b78a6f96596ca7a458676cbbbbf56bf9b6199d920b182eb31d979aa6f96a8eb5c67b839bc45f38d965ce70ef5b97554a6b5f5db9c1940f87a79e5ae6aab1d66dbaeebdc16cf3c2ed8c761befb0c5b26b5a73b675e1b62e534eb1d6bb8aa9e5747525cbd5f2549b877925e2eb0297b36dd2ae7bb75d9bedbc7352e1c2e56d675d36f3bdb69bfde695786d0f87b59678935daebdebbdf38f137115d80243979ca92d5cd7b56673ce7ae75db77b5d5de902c32d332d5cde92d62cc3586b736779aeaed49c48058e7466e13acef9b515df8ee79c7559a5bd318fe294d5e78c4a721cefb49a6937674d6b9f1685a2351c5d64b0703cbb9ada8e4a896dae1b0a85abd8d9ea7346258687db986f9b6bb65549afdce8ea4a292dae62a7f99c51653b1cbd99cce4cd19cf89335c5757621e823c611ed3ed68f2ea70b9d36d65ee995abd7558b6c065bbbb5be3bd3ba75a66b75a9f33aa1858e0baaee2dd6d3b77ba35bd67efa06aeb7285ab925bbbc9ae75adf2669d15aedb3acb9c6fdbcc33a393aeae545169add49c88f654e1f0963399d56d6f576e5defea4a950ef3a4fa3b12352c5438af5def36b65b959a56f1eaca30af74268d525bcbd5ca2da33acb3a4dabab2bc351c4d5e78c8a68e370ede4d6499be9cab3a47b7565fd40160f0ac53f90a533eb73460503e632aee9d6aebd73cfda51bbba5235a51d5b9ddacb751367dec98d5e7deb26efeaca4cf20d7e5a9552c7b64050c5daa14a2541e5aaac7ae7cdafd45bc392afae1c43dc82c66dd99dd9c699d669b867bd5757aec4974b1582ddca2e94eb1ab67b4fad9bd8e6abe1d595aa8b2747ef95dbde74ebb9d3b66b5757627925a7f58cd2ab5d8ebbedb2de56571681a1904ace8ce3dd6ebca9ee9addb89e7775e5feb29cf7099096273a4f789e0015e59f4ddeea73466566050e63dc3bbd718d37c6d7da27405a9e60cdc7a0509a135930289dddcc2fa538c35a76bca15028d4d4f814b951be697aef9c72525957575e1a2485ab5db65a96bcdfbad98ce3d5954f80b43ce16a3cf1f504a8cb13d5e7d9198eae2a01d8f0c5b3d2bb4d2aef866a11b158284e4f592bdedde6beedcc32d5c6472f2453c756a73c04758f0a9c96d9cd3dcbb7539dd16d47e1b68eb7e41df76cd377676d75e5d8eaf40990962742af8e6341a1c65647a178086a313fc9e6eb09d0932c3f4f9e007579a29a9acf199516282af115aa541ea4208d726b0414a40a640981ac0e4ea0d993144c8052021412a460298ca0820859085140c03e28c183199ad081ea040e660bd42a1eaa5472c8d6e9d25cbc1b1ca95426006073950a1ab49ca84270cc1948cd867032028b46114cb64e2c674a954ae8f356625e6118947001eee32b040b8cf84aa5d2a2d702a1821428103b98608260e88104433a772c16a842939d79e841042230912a2ed4010452c4915eab0542b9992594c2cc59fa0392541971105e851fa030d10589b7f287038589442aa0e0ddb0828dce77381b4e079d6fdde520550c69d0c070c64c193263c40085f979c1c74b1717b8f46cd19285c3c2b3a373d3c20d0b57fe66febf0a97fce9a6c99f6eb0446e14a1e0dd68001f3a3f800a469881b4812aff240d5c803406490c013c49e39a800f2651926025b1e594c4161deafc3f1620b4dc80c31a247ac86982e584c44760c5e988054c80039e8c78040a04fc3f01c496f761cb187aef6527f3eff3302a133b01fb38ebe3ac7a2a020d51071ec1e22120ff9940e288310fe63f90e14f44644c3630c419ff3c0bcf3fced77a1cd6d08f21c883238387b37a30ff3842fd1f8777f9ff26385b85a33fffe3043dd862fde3a87062380501654d114e1a34ff8f04107ce0c0f0ff4d70cebcd0e7f1a13caa58f907ec079c930f4a9c7600e1a443d0e720c7090729271c72e60d7adc0087993264fe710000040404043406470c90680368c3fec709f3f3ff02982f2ee470e1e2e385cb23727471e1b9fcff8bb8cc2d63cbd4325a195a6696f9c7e9996ad85023c6162d59381c2c3cff383bff8ff338f57f7b2a9c1658e069e932ff3791e05cd184b032a50a08537022d06ef4f080ca3f4eca4802461130c2c2480a23041831914a6918e7fee3d87a2abae31f070a9d325be11f073505070356ee4096967f1c2963985dff38016812be586036cb5bf11cb1be2f79ccfb1f47852847dc859b37fe71a0a470c487b279c435ebfde3a08063c20925c011f221ef03bd8cde278612a552632363065ada68e42cd66bc703f5e83dd27a7164400d6af087cc3fce1313fe874230cbd1ceff8d1c25fcffd1ce4d2424e08cf08f83e18cf08f23c23f4e08205c60ffe37c40ff713cb0238e36420a84609cc22e6e1ed97cf34f0c0e071f58088bf5f2f9b21730a398799751e71f678316f88559c68f772dee856b0f1c7314bec4cc43a00ff4bccf6b6d1dceca9ef6f68b0b27f284789795e6409c488fd1fbc4e456165f2ee000e08f3e9d39836906262d6df48fa381930cfe714e38a61c3930b8b9248ce6448f73018e05ff381550f0ff4d2ac189a0121c2638105482835309ce8d8853aa0487b4bd918be225bf2d49a5b44a2aa9a4924afeb3e8d0219dc730917056973f3160401f56e658401f238eb7321759390b8bf5cae2d219419f2f932895621db13e574b8c2b87409cc813411048e43c8bd0e7f9f09697592f30fcc48c21e852e38511370bdcc2595d1e419fff1b0fb058af2ce22b1c3f319b7f62fe6f3a00891e378f38f2ff250ce6186f0d2f8cff6f69236d0c1d23ca904a6944a9d4ff4d239891bb88dd40d580a6084d6496f9bf41e4907f598687dc90423240881f83f821c89aff9b407e7b807c9b29b48410117f74206bc70f3ffcf0c30f0c6c9647e6ffe6021ccbff8d05f6d8fc0b8ba5c7e62c561efd0b8ba58387f87f73c7f7eaff3776dcd4f1dbeba1e3ff668e0ff419b92852a213173a713437724c1a879515c0706847397cc38dffffc488622aa551a2546a6a9428959a3da914254a83afad1a9d57d2488c0a0c5574a85345f33157c0fef588573ede25043faf158232e4d6958f77e1adec6394bdb4f297c7bcafec9eeda942deed9e542ae4ae97d8023fbe5be0278ae2667964ae54d17c4c96efd5778ffeacc46398c11c6d70670cb36bf71c6d70e7ca9549ab4c2a66143f900793825f55f0942ffc4a93cd233ea4d9d7f6267d050172d304137cf4c2a29d79f87f638025fe6f0a704300cdbe26912ae8064509e2200a96ea60e721141503be135d227e693e46f45c3aa2e7ca421f6fe54fc572b53c31061a1ea808e451894f8a2406094e6980f82a22dfe0774ac381962b68d440d3e5e3ae2fa0cf098635679cc02f336dccc081e571be9801225122fec55319ecbf34c210efc3894c14aa1c74a0a1063250fef3281a9dc6f0f1e018d61829ff46fbcb406d80be0055f07f23c4ff4d10ff376b80f8bff9e1ffc687ff9b1e78f8bfd12187ff1cf21c72b5d65a6bd5344dd3344dd3344a29a594524ae79c73ce39e794524a29a594329bd9cc6636b399cd6c66339bd9dcb66ddbb66ddb368c31c618638cefbdf7de7befb5d65a6badb5b6d65a6badb5564dd3344dd3344da394524a29a574ce39e79c734e29a59452ca2ccbb22ccbb22c93dbb66ddbb66ddb8631c618638cf1bdf7de7befbdd65a6badb5d6d65a6badb5d6aa699aa6699aa66994524a29a594ce39e79c73ce29a594524a2933b9e16bab460318e066cebabd7d76d5ca2969be00a7b3ecba677aa3db9669335b80eb366d67797b27bb89e9cc7b705da699a69abeb5c3bdcbac07577bb6592bbbdd76955ce63cb84ceb5a6e3777adf9b6c98c0797e9ddb1ccb6ac4a2aadcc777059dd5bd6f149f5bdfd66d90e4e5bdd65df6cef757079ab39e72d35d9f1ad753a3839f3b619b7ddd2de6d3e07473b9ab37c6bc7efc6319783d3b6bcd90e57cda5ee57c7c16d5b57ae5d9e6939a5a6b70a707267be69b2ab75eb4dbb0d0e4eb395529dd5a9352c69dedee028cd7ccbb8ad9aad58db3637b87927c51b9e33bbd972bcb5c1e16ecbeecd5d6633d326ded8709a524cb1b59b6eb7db7763839b58dada5dae499be9aedb1adcdcb6ce566bf56e586edaa6069731963667ddb6b5de39ddd2e0f0ecb69c699657aa75463734b8de6599dc74b75aeb8ccfed0c6ecb6cdd74df14f77bf7dccce030a67deea975dbd5369d5b199ca5dc72dae14d37dcb9dcc8e0e6a6f54de74cd22cdbb0dc28c0ddd9b156edd554cb3096db189cdeb2945ab6d53ae5ec5d0c8e629c6dd56aa71be5b887c155bbbbcb73d637eb7ddb60701ab5945ad9dd7b6fa57b025cb76592f2cb35df1867fb0b4eee3bb9964dabcdec728d25c0757ceb3ad34dd2cbb5d4d80b6e4aabfbcd73eb364c7787bbe0e8e4396b936a92ef6c76980b2e6fbdbb3cf7cc55e76ec35b7055db9cf55a75ddf4de174780db946f9c49cdefdc38be580b4eca0cd79bafcc53dacce22c382d6f9d627a7bd7f5de2a8600a759bc352df339b5dde18a1fc06d9bc6f5a6946ab3e64b31169cd6376f2d4f9d759a6d135fc1cd5b719658df8dbba94d6c05b7e94dbb399bbbd6ac3789abe07495f2f22a65ef7ab3123b809372e20d6fb74b9bd99e612ab86d2bed7766bbd352c3196e006737def1ae6bdd96bb4deb0ce0b6b933dae7bd35eb6ade53709ac6a5ae35bbd2dab9ed057035eb7ae3cd6b26bbad6e05705477b6f78d73cb6d9e5b0a0e6f934eda6d7a37cfae4e00c771c57c6a7a37af9deca2e0f4cd726f5a3631be5ddda0e0b896d9deb3dd689731dd10c0d1ae719bfbee5dcecaf13d0037a5ddd7f22d6b999df6de27b84edb79ebba6d5e27b8dcf54def5d7967e5c66d131cc69ace73a39273d9676582b355d335eb6eaef8d2cd009cce7a5eaa6935bbf95e6d090e671995176f38d3b6566901386977766f3771be1ded04e0e8bcf5ca5e2fef56cfa904276fed1defb2da5d4efb00389e31b7b4ead94d9eb500388ebbbe1aaf99f76e7649703d9b5be5936fdde16c4382db99ca6d9399b6b599a93c829b96decdb73bb5a95d6d044731cd18732de79cb599dd2238ab759dde2eef1c5bd989e032baada5fce23bf3d64370b7bb1c4bac717b2304d7ed2ea5dd7a6e4dcb346e6f82e0b66d2dd65db6ddd6f1bbedcd1aaea398667ae76c4b7dbbb63740709de5527659b651bbb3adedcd0fdc9db672dd499ce7e4b7b3373e705d66f7d2aab5cedef4c0ed6ccb72a737dea7b43ddb1b1eb89975726bf9d29a6daa657bb303b76d384bddf3ce36dad9cddee8c0f59d3bad4a6dcf8c5b6db337397033d31bcfa89dd26e3a377bc9dd64b6b186b58d2de1a886b194dd6eed56acb54a38492fe61bdeb4c3bb7694128e565dbbbcd672996d9f84b339ab96669ba6f52bf5fc0077336d6bb6f6562d379d92703debac4dbd377d71ed32126ecb6c77e636eb9b67951ee0b68eb36cca4eb74b67ef00a73bbced59bb2d2def1b245cefe89675d69dec12cbcc3ec2557bf9de68a7ad9659965947b89e513e6f969de33e39073889a7ccb863ace13d7123dca41c5339f5a4bb4adc19e16a96dcf29aed964969fb221cb795f32a3b29b7e67b039c9d536e1deedbaeb7db34c0cd99713c69d5b44cdb4d11aed3ba27af55eb368d529e08d7ef9552eabd5daee7e48870d86ed66a54cf3aa9d60fe16c9675bc5a3ee9cdba6e43b829a5e5d66e1bbd999cbb10ae67ddeeabb3a9b33abbce00a779cedb36358df1d66d1d219ceeb8b4d9be5bd7b3a6dd20dcd5dbce9a9cb367dba66d827056b35ad536cdb4a6d5b905c25d9ceddb6d7bb39ae70d03c2e9d95d3cb3ade3d9a5196ec36df66ab3cba9f3b59dfe83cbec9e77cb769e7552ed075771dfe6cd7857b5aedddb07d775c6b85e4bf14635bd7c7032ebb34b4a71c615530c7012d36e4e39355e6996f6025cdd5c6b9d77b5f7ad336b018e4e9e4d5e379bedc9376d0f4ee69cf3cd7ddb9c655a0faec3b94f5e6f96f39e95e6c1ddae6bde32de328b6946f1e0ba6dcbdde6bcb37df67e07a7e58df699354e33cbb71ddcad53d36ecf32d7cce6590797abb6cddaf79c950e8e6b5a97cd6c536ceb95b2cec1758d354b73d559958393756eb36b33935d27b7acc6c1c92cb336ebe49654e3b75780a338ebdc527d67957d6e38b89a7bdebacb6f3625dddd1b9ca6b5cc25de9276ba6ad90dae6f5aee76ee2df5ec66d6dae076b73bca7bdd9da4546e1a1bce624baba6b7ba33aa69acb1c1d92ddb8d6b58cb8ede8bb535b8ca6d2737aa77c6ddcd575383d31ddd709dbd6f3ae3d86a697099cdb8a6a5961acd19b61a1a9cc6edc455673df194d56a67703ce32b33dad13c3395553383cbfa7633e3b777195359b532b839338bb3a665536b8d4f8d0c8e675a5727bf39a35d66a546018ed739795779c65cf32ab531b84badadd652aa6ded32d3c4e0322d27a677cbb0ddd9ee61705c739bcdbbe96aed963b181cc77cd3a6beb76a5bdff804b8aafbc4dacc38be5bbf9b7ec1d53df196f1ed5aefa4d554025ce61c671dc699d6b74c3bea05277b97d9a9efc59a67bcd12eb8aecbae7667d76f8775b6512eb89b6d9ed999ed5a33db31dd82e3ba6ab9eb2d67d52cc63402dc262da7db9452db8db74bb5e028be5b675cdf2eabc49666c16dbdd92ceb6637e9d6ed4a21c06dd4eedbb5b6dd9a71d7e803b86933596bd57066afc627c582cbf26af3d69cb3997597f40aaed3aadda89639ab97ee8c5ac169ddb74ceeda751dd6ba57c1757de20df79dedae5edd1dc0653d93b3e22df32ebbec5470d25e796f2625dd5ad6f106709572996d5bedac26b78d3380a399d5f8e59a66e726279f82dbb2e34967edbaacab782f80b372eabecdcd62ad6f762b80cbb56f586ab8a3b566754bc1d94c6ff2ce7df3b47cee0470d26afdced96d996f36eb28b8aabb59b58d6b7df3d67550707a5e2aa59675dda4951d02b86eeba6be99ef3c3b29bb037056cbb6eb18f3dbd5aee627b8aef2aa75daada59d9ed9096eebeebdd856ce35d6786b82dbd9ae76579bd1ce4ebb31c1610dcf69bbd4dad6e1ba1980bb7bdeda75d699d6f2dc96e07836abac1ace3aeb756e05e064b7946a3cbb6eab966d04e02ecd36c59dc637d678632538aab9d6b36df3eeb9673c00aef2d95dad6d6dd6db2f1600a775b4da8bf5cdb3e3dd9d497059a632677a6b7c6f9bbd13094e5bce65a775eb78b7dbce23b86badbef96eb976dded751ac175deb7c41bd72e6f18d7592a82e373eb137756726bb756678908aecaae4b69b34c6ab94f3a4b4370ba6a8bb9de3aaddad6739684e076e7b3ab946e5965a7e52c05c1c9de6f87b78d377af52667690dd7b36ccecd6a6d6fde59394b407072db9dec5dc519b67cca59fa81eb34e318d7aca35b777b364b3e7052f73975cf324b336ab359ea81d3b6acf14d777b6696e35ee281e3bdab756a56df3cf9eca51d38adf1a6dbeeaef596b39774e0b2ae756dea9a6d97edca4b39705b9d9df2a969dd66adf525dca5566695de4d77b7c39d25dca5f556dcf5bcdd3d6d5709b7a7cd7096f3a43c9bb5a384b31adea4d66bc59d76ad9b84db5ad355e65deb366dcf1fe0b46de39d4a8d6b2dd32e4bc2651ddf4d5e8ef7a6379b23e17637b7bed979ebcc3abd7980cbbdb3759392caaeceb9c90e70dbe476d68cdb9d6d77af8484ab53bbf56edaf9ec32bbf2112e937377bde16c5ed9bb958e705aa3dbbcdbcdeea69a57c9012e6b3cd3f8eacc52acdd2a1be13aedac56ebce58d7de51c9085737cdaac45ad357634be5225cddf2deb64a8d67adbb2937c0dd3c79363bab7937bb3ba50638ba51dabb9e657eaba64da908a7e14de9ec66ae9d96d9948970d46e35e3184f8e2bb7994484cbae86ad76f3a438d33293877012ebc9b70cd78b75b7dc102ebb7dcbb49ce1d9bb4e79219ccd327badae74e35b963c035cd6b3db517b6ba51ade9b106edb6dcb95d6aeeb8beb1e84d39ddcd4de9b6d5addb42d0887b73e27cf78a5dae64e07c2f5dd513bbbb6f5d4eca601e1a4b69ddb9a7bd786ebeca5b7d73a2db55beffee0f2cc946b75675a26b399fde06697338b279fdb66b96d7d7073f7db651bad739bbb6e7c70b6663ce34edbac96718b31c0f5acd36ebbdd33d592e20b7074b3fad2abf7ce5d66d8025c86b9d5baa376ef8daddd83ebb5de2eafb4fbcece563db85c69c73467b8c37367350fce729a352d77de6456b58607c76b97498ab3d69aec92dec16d566fb593b8dadd6d46ede0fad6328a6fcd7570f46eac7515535cb3bc493a38dbe56ce26d66b6dacde7e0b8deb66a69e63a6b9d66d9eaca2c4f80ba3c11c75c5955999483937ba3575a2db59d99c63d0e8ed3ab51a9bbed76dbe67aab0057e7ecf8ec776a9b6f9673144ab33e6754694870f48977c76bd6abd639bfc1e54ae9ae5a6a56d32496d80d2eebf4cdd9ccf8a5bdeb6a6d83e359ae1c7775674eaf76d964594729be75d3f76a6c27890d8ecac937be353e2bd619add5e78cea87b40667bbadab755259b7a651cc5f6a709b9c985fbce53dabb6694f83a3597627ed7aefa8b4f26a121a5c97e93d71b5156b9c35953293cee09bdb3e77966dd9d6bab6ba52b3976446df19df55cbf2cebd0cae6e724a3e33bd3b5b69af1a195cef7abbf7e69cafbe15570a7092f7cd6eb2d24ce22a311e83b313dfed7619576eebed5c0cee66dddcb4e376cb1c5fad85c171adcbf36a6bb76cf19e6070b6cf69a7ceb8f78ef9ee564e80c3b493bce3986ab6ebb674b9c47cc151cd52ad4dde3b35e25b425e047699319024c061b9d15da7d6e5966baeb50791bce02aee996b2de70ccbadab952b52179ced59bd9d9c5aef9bd6d5cc0527ebecdb66bbae62dbb5a9b7e0aad62dae33d39abc55667475257e02a4e58966bd302d30cc8242a150e1285a2152043899c96c3bab2f973a5f2d06850243aee6033f14aaf5faaccf195547d282cbf26eadcdcee6daed9a0537b7aa69b961ab67a62b95aa4d561204382debf6a6bbec5abbd169b330e11846ca1b8e22253d80db2e9f966a1c4f8dd2be65ab2b711f5f9b88c351a4948405b777c6bb3c67b66972bbf3aa3ecebaf2c805eabaebeb09d093aebe9e6c937405b7ddda517cb77d6ba75da6cd1648567059e25ef9c66d576d77a5b658485570fb76f4f63cb72d778aab740097e9bc03a074a864f0aaa6a198a41432840000a8361200900003130030482c188c0583d1a0585dda071480024f966eb646984ca4d130475110c430c810630000000040000186a0b41100a84f064edb105d0b793131399a8b74cc223ae3d69f0073ec8df9cbf13bdecf59cd26d2ded26125f727564e1f444f85e031698ef8445a593a5909fbe4e34405d1a4d02d2ede7eaafa48ce6dc1fd6d43a12429448be9e7e84a2488256d25be4f2b4e271061857c3151399a8874b3c45b49f58b24b86dbfbffa50e724c1d1f6fbd577750e1298e6883db17481b467da271eee4f8cdbe69eb5fafec70c41ba775badf13b680a907ccfadb6bf83288e2435323b50a13bd0faaa2141072be38b87cacd442ca7b191b0702cffccf8e8d0109779465761ec25be97a0dc9bb0d73ab973a6e0c8f7db6afb3b880a50e28826d28fc0ab3a21e7b4c0f8765cd57872120bceb71b559d9373b7e078fba9ea2339b705f7b71faa3ec9315a80bf3dafea831cd382e4db6f55dfc9312c48df7e54b54d0ede82c4b76f91d15f5a1fa2725198a2c89d9b594434abf8e70ac17def67ade689b46469be92f6898fd30aa29342584c3e4754224d2cdd56e27d52714552df76ee2f3094238948344bbf95ae3e41386910f1155ac574e20813c967896aa5c9af5b70bcfd54f5919cdb82fbdb0f559fe41c2db8df7e57f541ce69c1f1edb7aaefe41c169c6f3faafa26e76ec1f1f653d547726e0beccf114a248925da4abf3f577c20d06865f8e259b9391107b3e35b87eacb84d30d225e21554c24ee6de7fe8243ed27a98f76ee17dcd57e906a9a487c96562b9d7cc238f920a22a3489e9e6882792ca12c94adba73fae50f549ced182fbed77551fe49c161c7b5bf9d3aa92f8a58713475c5ff3dc5b70fe7e86fab6cf5d83f3f663aa6f7eee161cbefdb0131139003d2777c22cae4be51bed9ad641f7dbd4558c26063351566cccdb684295b3c67d4b1a972bf42e6628b50823d7a1741067cbc0f0b06a51a871d6cc88fdd55d977ac1bcaf45aba283c4dbd4aa220f0c48ceff83289e91a71fa2339f6cbb2c05375468382182ee1591245004ddced938dd257acf72511b06b42dbcd7cc5c9757b97017b623c10020db63ec15d34ff3a70487527f119a0e0e7f927b6456ef440fddc74a4ccd8063af5f6da1b3826f2c9d1a00c7593de32c3862562a63b3335a8ec532809e7434459c054bd9c675df3f240b64dce48ee6fc2703b22f87b26f654a8b00591936ce8dcd42ff068363792390e08044ec88abd2c2cc41a8d6679f2d2d4b4f2edeadd6147edf4b20c57b01d1e4cd8a04d9d00ca5db0a6a44f3f501fe694d41e4fb92745058d05b0c300fc77ad6be383c05bb5eaaa935db547d21a58c5a27aa56b37642267145578a58eacb4fc82cc621ea47675a04a8616bfd866cd742924f03917e93925c96029ff0acf18c5690d8cec08c20a06592b9ef79ed8c7b503639bfde91499940a2946a10c0042a0da05395091c4e62ab5a85740bd44fb12d52a8217a272861e64b9636a82c36a5d816c5215c04e3f919e566234b03d23500f0506303076838c0008d074e80c652636071bb428018fca2b9447dbd03978429e60741f42101b11cb280580c19215286d25fed3bcc6387868ef3a224489703824859b07796127cd28a4ee2520ba528c7bb0aaa58b7a68aee46f76f03d0c3a7ca8d41ed04df1e8e616f6d7dda92afb667d3213c85cceff1a540bdeddf717fddc84f057592104185d5a0ccc16b5682d8333bfc5f5c009b5e6bfede6ca3736ab277687be4f3c70370d5f6b2a8a7c8c893517760c6792ac52bac5ebe7ff6f4ece7ec81249585d0c86856b0ad7240192b0d425daf85d6d0f6a47689d7b6d1d71d062d00178acb021437250da88f8582201258525546c662596a1fb8de0ab92f251b66d51f801e2c8150fce3fc01a8d706a06a30e82a595b0a836cf1e994a016177492e953f2c9af9f1d15a2efbe15a67133cd45637f25e0e49c9ca7406969a83331e20d1850566f110cfcca4ee3338a38bbc290843d0c19203071f7c15c0971f9c0dc94c62c2b4c50d4a1da2d0115ab0651043a974629b3d409e6baa08c015f705810faeeab2bbb6a16f2afcd223ba1fba120b3345f7b7bf66a6ec7467e59b1951b9c08f2a58ee7095256e12bdb162c1085b9586b819f1ba9cf98002768be53cd458c3141bb131a433c7e523ec2d83843d3209b3be53bdb8fea632781143bb52a0cbd398bb18020489811a6e2d5495b5ed523acc69861d35630589943277d4466b729e0477e350ceac633d26eb1e1cdec138b19a84addddb5bc649b08dbefdffe33f818f143019080f25a898a406c5d32e99e6e9f90a71be9443a43acb4fe442fa9aaba68c089577e82fdfac75df2b9a9550e3c702da81d17426a5baa8f9c0c868223976f58cba360fad4f91ca112a0a6f4b7a0bdc183306086c9f122e7b7ddae4e2ddc5d9ef13ee149fa5bf16db12781f1073a0bf52d210d2ba3dd4241a1690da267ab615aa0af49158f4c2f5651d73eaea80bd0d09a494d3799fdb38aee8b4c6dd4694b250b4c9b0f1c002fb54070d8af243e24725994aacdfc6b5393957903b5a25e30506274d303d4328dec9856e98fe17cc122fa8b41ff5aab83cb300422b6d9416d6503b619762441216569998685a2a6db56dfb0482c849c1e0cff2a379a83771b807d340b6067b3ecd53ae9345b66fd0bc0937d07aa678a9652baaec54d20e88d272a47165098a11341230fee27355ed5a130b6ce4fe19375efb58e51b4caefaa6e1b7bd7d67ac79c757743538e2993eb21784e8f1aae94202fdadd01b195ea3810a0068eccbefd77a05bc8459006c0934f418b222201f50a939b015acc2a39dffea1bb78e0010cc0cc96fe21cb26d69208dc5d261150e66f40cf63663ffc345acd69d188b2f87d438da91e05943ecc52461a4305c28546c9fca24162061f32dca3dda62aab9e6f07da2508813ad491d7083e184f854368214f2d6f968408498944de58121ab8562f40282a6ab59e4780a830932aeea09b522309d47b1691e64bf9fb78205b6942d0984c8701c67fc9e83e0a4c63631706004284a5a719200141d970d40f2dd8883aff3c927f811cfb77d542fcd86324e2e3b7c25b006a30faa5148322d432376d7c74f592d8985231d31a7c994678628e8007695d69cda5dc299f2a1c6f067868d74ead6e43d0ba4b46eed8b995e742b1a15e5fafd971893ecfd227677661e9d18e47ca6486886214f66f41b47d7f1635ad3a77723c88187fcda7e5a5eecb7d304a95cc81fa114279a0ff888621c1307fd2b91338fe6638b1c1382465e8f7b765b6fbe4204c146c239acd9a27d24c0b5f035ef7a96333bf86c9533acb0c63ecf6665e39dda1d649b366f63126f777bbfe7993d035d02e98de47a5d150534d87f506c02d86747ff8893722011cc17610e3dc8b44bc181218a0dc1d41408d4636e1243205660cd1d218a4ca2cf9701230e799ebe794b872d124fd26621d28f7ec2ef626a80d86379046076b130a3734c8ae82b3ba3b8c86d55cd602cfb4f88002ca6390a42dc8dbe0eb6503a7bfd633d2244484dd10ee67f0169fecce4f9ccee423874a8a21155604f487384016f2c648c92d173c9e17f88a552304ef546003b10a12bdd0439f37ca4090384e4d0dd040cdb24797b1ca2313055092c137d30efcf7d243067259794450fa30639a53906a3edb0f0885349f1f70bc593d1f025b715dfec370a33c70cbeec4fbef8d9180c8f50b8c38dc7705fd81c00bb70c77f49426cc8dc2b4f9a269e6b63bf0f4b37ae56f5a5194c70c592c9bcaf0da681fc3a389fb367471ebbf6aa6069d77243c66c7b7e47690273e1e00a47c73087e19460f168b862f3bc88fe0c25627d127ea1273f9cb29d937f3bbc5a684b27465aebaa78c3407de98f4a6a235b9fc6407e0d6797f9002ced1df50284a797874290bfd8285e54c39ea1ee45bf7ea0e3941f86e5b3f6ecd9396710ff8266c7687d78cf82858c639033abd7ff1630f58000d7334af193adae6e8ed65dbbcf184c13b3e5feb84b8a0ea81f0e8db8237e9692f3b682b9bb8d5c8b1ac9c61e61d6d5b7eedc00a62e04b5bbba83c00fbb27db3bff4caecd58ba741e7e9f8c4f82c1b74713acd02077ea690cfcfc3d1988bcc195fd032cc0c793a1abf71dbecd82ce498ea8a0f59d8445d7062b536e742e9ace3cb63c8e33465441b75f882c5cac3ab8d33a127b343c7f67843f9f5286f0fc05f0a80aa5e5eecacfa6799fc3b9599ee35ba2c802d0ff9411119a7c44fd970a2dc3b36df032afbe67508436c6f86caaef641d25b6c6ea32a6ec250e36a60fff31d1d2fd89b01a4c799bd06a11d9a9d8ba4aeb177a83e1e40e717460b735d85b7c554c752af5b855e4386c2b1955f27813d8a84b830ad453948e994346fe63bd886c1be1b69d7af50ef7854062155e25f726c4345ae66f0fc65dcaff0a09687a08d4e8b3e9740f2487caccafcb0c53d6fdccb029a7535c37c286c94494756f7fc08036a250cbfffcbf30907b3bc7ddc2cb0241ecc502ed711ba7b7f0eb48acab3fa5ebe350e278c9a419ef175a34dc3470ab3605bd63b85bb3a5618cf725bbd77cce97cb6a5e57d5598c376cd1bea6870d941c0b2beb12b45c3d906194562a6fc77694c69bb0e2fd010bb6f065ff8d50cfcf9dcd71f293b20a8e7b303be4022fae9c27439d65714ad624d11a4b5917c92a0dfd7e7ea5524ff7aa86ed9db6546e9f1f7fc4bc43f9e38c4e087ffb07a001a04116210c3b2fbd36cd32eaeaaf0fcc224a9130ef38410ec861312502f85a0bdc19d7e53589463fe51052ad0a0dd5c9a395df2b1ea69d3f0eb26494e17e743b7b063e979448be503e3df81a8ae0ae08ef727c1592bd6a7bcc7c3bf4befceac5958491324f0a7ebd1202850f452a1f9b7fb240880fa20e1cb56ffcec9ea0acdb93664ef69bcb49dcc2b759a83b1a9578c9bdc21c90fbb5f83bf2cbce78f255ce4f2ce6f51ee3e3da43f0d1cbdf7c3fbcdbd9f62aa3932efc250bd2fed8ba418d61e1ef8a3bf6fb11ddce2d7984cea7e94b0d5d479aab6b1864dcc5cc0e3962f6a282c953ed9c3bb2874e1dee7d5e3bbfdeb52063b3b4e400ed7b0aeadb4dc90700da3a330e707978eb53309cba8c326e2f527b8e28297fe90ea9f34a84437eb330f90f0dccbc077972e8ba0ee5674328d4702bc169d3fd11e582ea0586231a8afcfd8c28bdc73206c86865eae998541151b53ae67ffb030061aabf919f377ce37379a28fd42a7bdf870782098a492b3f4e629c3706837c24f62aec21dbdb75644f5a661eb83a25226ef6c543d33c1a3d0d8480b354f569f4dfb5433776a32bb9b93f2dc592ba8332cb00c6d182bc5c0a00695788311c0e9edffeb2afac896fecb847586f4402d80720e4e978dc96bdd0f05dfcf7092cd8c1c7c379bd71f6f5e106b095386f50ad85d1100803c807a41e392b0fbe9de67d042815f7b88fa110678da3174726f28ea87af1e6edb0003569e6729e812e90845ab0f6f60b90925363f345f362dab0e30de574902915dde25fdc42dd65cf8a55360c62339dd8824b55046dfd0da75219d32a27690c27813812246f9d4b309542ba05687cd7930cc8f34c3410b3477c614df311f52ab4486954e788601d6f3785f7f7a9c19e0bc155a14c73fdac54c9b86291d4893dc7f9d9f33c7c8736f00783af8ca85937779d1c4d064044bcff3059ec3d58e6279beceb10e777b53490aee47d357be9295ff70dca766ba84c2ea8f59361050d7ac7c4ec0f9b0021eacac5a954470846d39c72b7f7791d28f574c619c3c0438f53eccb16f2c77aa7b310f9fad31272802939db3222204f47463a2ba2d88592d19a994254af629e3b3c173be93a355b49b219586824b07215b30d61a77a3233a699232339291bee5618865239a4e6b3b3224bc921950b372b1e561051664489c11f94a9777a3ea538974ef2b7b62982879d0ec5089fcfc5877816eede4920ea59fa968dbfdd0b736be67d8eeb8866a07a63a1297a85e5a68a1269c42804dcc1b0791e6bed355ea58cb7e4a16b35165353049c470c342843234d19ec5f8c50c211e3bc0164db7054d2bdd399d8b1bbbd6b1d894af2e9794c1f00753eb58cd753be90958a2112b311bad3ce423614b68ea0263e8f41a5eaca813f356ca7383e710fc30cc1d033aa5c63cf680ef06d81498f1ce9ee2c0fd2692fc5eef2738afaeb5f680f3c9aced0799643cbeb507a70db022bb8f112073c82f0079306d61de771b2dbb9f3cb6f3508d49f5bad62313bdb066849d0ffccd277f0b70f80f983f422f88e3ab93052f18231fc1fefb535a0b7908f10eedf2c0350a9d584497e8e442eb07e60694bb6adac2f03f0aa963753777d961456f7a461c95dd40b7b70cd75efe0932e53e63aab3f9e16f0cd9139cfec5b51f434d26952bd8b75f154bcf23dc85cc6c422bc5df6eb1174a021accf49b72f81584e707b464209aad63d54988662be024f537165149dc90d0ab848e97cc4ab748f7b282c2b4cc90c94c9c29667fd549d7f2a0ac33bf8b88ae418bd75086bfda5c64dd2ca213fa6d3bac9ab90903e8be0ae74eb4a26180349513d3d0710299414ae391c899643bd8f7d8ddb69a84ed73873c28c5af4f7e9a5c1d43fd699dd49c24cf2844d7de803eba26b6f03129eab1add8102a75acd8a9043796def1a0c76170cfe02b4247094a6833c94b0492a4ff2736f395b85521b2f3561f8e8bd87e7ef65dde94a6d5dd118ee8a97a3e134465623347f0330f8fd64f163410cf7d67ca30ff31079a18d1a08e7c1e63f41da4ddc6dbe1c752fa523a656f2964c2f44a40e05ec9c433cab8977bf95c620a76ef3b1351ae4d90010b16ccf0136c5089a9e36540b9e8b098d60ac22f47c55ceea8986bbe424bcbc662e897ce9004e5645c09ba48187b9e9a269e60ed013f46bb7218de6d443f7e03f3fd6e96536763536777bb41d2becb3afbdad72b208bbbe0885f1f333bbe0c4996b33072d7e3ec0d6840ee4586c225573ea0e54fb424cf69a6bca3e6daa9cb339789d02c39ea40916831dc17f6e210fed7428b61f1876d3b54bfbb36133228162fe54470ceee6efae0660831c68fa69e378a38fcacf66efa162333fe8908fcc40d5d3595d291cd3e0ef4b11f1b270a1d1958eee069752eebda237d3fa1c6ab5bb406bad81deffd126e94c9316427e68e7d9f8634cef75e294d7f91a1de5dd08cff1289331a8be7d8f0c7d2e5cae67a9a15aca19fc807bfef99e87321f82448c7cfba651bfa9ea1f796613b7b095e513dce29e4e76ad0c2fcffdb7aeb207bfaadc5bfb01800dffaa2dfef8576b266bd9f40f2ecc287b29be57834fbd2a39a2b9800dfccfebf1de9272f524f9e9e46160ca3d5f5ff4a65fe5b40441ad5fe21c70251f3be35d8c86b460bc2eb2155c0327e055b91af8ed4128bd93bc04a7e3ba693580d6f400bc2eb2155c0327e05598bfcd39b69e409fd73c32262f81c63b40c874f4a93c5f19a6467ba3f51cd6c8777b405f3fe456d633bbd50ac0c974f4a93c5f19a6467ba3f51cd6c8777b405f3fe456d633bbd50ac0c974f4a93c5f19a6467ba3f51cd6c8777b4054b474a97092dbdc12d619b8fb5e5cae631b654d7bcb796a99a8fa995ff46143ff2492c37f6607d7e046cc76de907c749c1e02da3238fcf6b466242f7af90de740f978135389fff71db711a3aa071a5f079cba84860f399919fd7fd23a030db8b6b7c0dcee547d86e9cbe7e681c19301150ba1aed2574f2d98773a6d93373a265f361a33eadf2a7115add60412b9728c2b784f5dcbebd3697bd648a7c576a469267d6efafd384e41681ae8d89a8f1beaed1235df70bf8cb883c6de4b20d14463e66b9d10d5c86e1d0cb5dea1533706b05bbc18665270c134c3fbf11438a885513e3c376dcdfe54b48cacbe1d9da8a39bc27c366fa0795424fbc84c35555436d0ee68b76ba347596425fe5f1ef86f518e0951a00a2a0ee7f8ae6e0a87e51e5d65ccb9d117e7d228578585e8721c0e4d3b44ddf7ae4808d954d7478cb196fd2c7daefd884f7cca906dd403df240410a9d7ddf430b7744af340871c76be5bf2f2d35c3fa2b03c2ec433a502a48bd27da70db081a25d857a5c22447492fa26483b25bafc5a78cd400e0f5955c6ca54bbff5a1f30fa5c7bcc74ace93b959451baf420d967c8ff20260d2a0532d4d09fae3d99e54e811d41293d1d08f2382755d10773405613c407a572d9b2976b9caf907c840b2ba061662047426cefdfc8b991539d30ad44eeff2f8a733bb5dbf978f88b8eecda197560ca0ab914ee6efc07dc657707129a0ede8b27dfd73ecd295af8483de39b3aaa1a01489998fa7d05b16b7321e24b654fca8e3aff832a7b11b6cf6451b758c0367e8df767fcd810b733f8cf2761037e11df8978a0db311bd26b2e948179d3e1ebed69b4cf6e0044d7d5422c24c50c1a1efa4ba1445b27a88a71a64afade3dc19a6138b7ba4c5799cd477634732945fa1daae70060089c8c42b06ab2aad81e9d97b477b581e8ad4330244a3e19fdc8e3101d07974ad0af7c82460aa28d56def2a13a6ea98720a7dcb4afe7127cca4dedba9b6dafa6575db1562e73840c1f54723f25d5856f31daedd1dae2e1a88d823ffe15dc46d61844f27cd61cdb330568236cb828062f049cef86e381b1036d5927e35703406c5b5c24673ec7df8eda0bd583a4b6806432845b794a5975798d3c1680d82aadcdd143672e36a5ddf40219f22bf1b0da978e54c322cd95461ef80eadf349758f71108622b9b77df16e16585ccbf1fd10614a952be44acbd1cd2cf8e2a3974ff264a0800ad9e44703aa0a02684b2dadce19bc8eb1e45895647530785b010eb59e362c278ea5a04ad6b9f52a7880783751cb83658bf5a644a73ae3149e8c4b8fcc550d3e7189b5511b50f837d37637b990e9b2b08611ced687b940b52b903a109bf5b397b87ed3ae7a15c27aad144be162818cf48b7f96cd85e88cf78f5577e39544c07a49d81a9dcc0e10b5ea437a894d8ce19348441d2836fd0d1efc85f190dc00ae3fd43fb9c827678a616feee4fac0bf77f5415858e13f55be29df0231ea6b0563a9f79048751e4f29612a40bb0d470741628b3bd7f87f8e3dc0e859fedc222559099382dcc958912080c32ceb2055c8b52cfc16e8ce32a29e5aa591845b0ba727565f69e06dd225975bfe9c44f12b903bb73c9feb38c4eafb8b80587de5a9627cafa6c0b3d7f1ada1c99de151d4c4136025b871d588415a0bda204ad1524ebcd8641648f42c1014bd654c5630c3dc1fd8ad1fbebadd88d316d2564ff6ca5b62610b41a1ac241c1fe7e0285d25136152734f4165212be3ba69aa93acec6507653ec416fd9eaca4c36c228ee7b4819594a885e234eef5bfec41c85dbdbbc117cad704f6203e3af03d93919975b63277396f8bf5f19197216116dd644cf3900495fcf831c70b7c0ce83f3e8e78b8a3f299f6d2139ff517dec292c962dc4fbcf44c433a8ae338c243b4b4c8e6ec0e4d9bd854d1f8989fc4f8be4aa8563e15d47c0b81fa6a9edfa6fe88149d25de6988b2bf2e72854d20be5da58bb7f64351d3a893e6238effa9d4f26d19d9673857613b158a331b492bdc4d38dcb2cfcb6e045b83a41a79aa8a93eb89f518de329a0bcdfebd0fb7cf184a95599a2977b2c5e917bd58cec135cb0551d3f487f971b6db3c170d6ccb36f02e13f4877a0778d6ed3359564f18b83567931cf18a0d3dc5573f9c2c498fcaacebe869de077d0dd782863830c5c2cd40e5a3910dc2e159dd3711b16c08e9dc3223f08612bbb84111d6233aa9ddc0eeb0083df6c560699c151bca4fec73c967288e6c516bf985657bd2d5ef3e36ca94eb6d42c2881d3ce1151f4fe3def1ea967cc7f433f66e12a6dac1e1cfacdd9bc779e7d3cdb503caf9b660d3e2e2ee74824c2523786c280ddfba7d7a6a21374c918e20e3a4c103b2814e6c57e9969210fc9614672d2d8e3f1348169e45cf984fda7f3a77277c3bf1b4fcaa9d18fc7d104061a79c05cda510c38fc21cfd8f68499e4ae9d81ee27234442c0108653216558cb8f81ca6ecf3846ef85d5f90fc5fb9900087677de698712fc69f26b39c4ed28b97ff585c73c39cb4a86b85605261fa0640a33b0baeed4e2223f0ced9d83666a8382bf43421bb4fdbeb5246b8c207974efdf7f8d2fec94defed6ccc5ea2817c94bfb1d4a46220873937b91ec75d85c557abe52f8e0fc4a64c79ffc8b54e591e93a97a200230191ddaac18ef905fc0f9bc285c142289c7d8294b55dbea8ad15f2f0432effb7fb645559ac964c3327bd06a8f9c045487fdabf86cd12b5e34ad001290a5ccb1368eab8f2c1d2f33d45febb01735c1d5ae15e6a9c3d0d3b0b51281df8bd695702196246a07f2903df4b9db9e6807dae1c0cfa79fcb83e0de8f4931cbf8cb85a65c8861b3b25edf04f295c2a7a25694beeef896e7a680f99130a8b376e3437aa6bcdfd429bbb65010e65bd47076ff9c50d955a60c3979f2f9dd69175760fc172b6fd9cb464d9eecd598ccb6b9e5a1c4c9b780dc5ce47b8f2dcb9dfce4f7cbba97dbe642d4f8088edde9553f0df0c5cdf80da6fc25be3025a4e5aaf9df6e377895043cbe0373e787977f73b9033543a3290076068ff86f68cb78c79bcb1ba0bb03454f800622b83bbc8f0ee3a9a1d62f65a19bfcc615457f6a4b6eb6fc502dedec439df46b3c5076b8c839cf642fdbe1132fa23553cd845423a84d1711d58cf951b5d2946dcdd1ff075de4de27e3c214603b50075b163044ce9556c27f57067e748695cc9ac842e53dfe46ad0ca68484ed7fd8763224b0cfb799a6e823d1476fededa66e666fd1a81f66945376c60d1473809f98f5a775e5f2843b8dc861c762a6ea4064d677f808528db338b173729a775775e7c6853e0293a15f5951479a37a75e098644515d3bf7249ca4b57e372bd5f553a95697038de3146c7ee34ed1e738e0eefce67ca434c84beb26b83b14e6f9deeccdb5ba104dc99e9aa46e2a893202eacedaa789d863de835470b973949f5d81732236f91dfdf944756477eed28d138831c3ea23dc1db3f70d92e43dab245177e69bc84fc1935cd5813fe84496cc86aafb225325a5bd45b3c1ababdac80d5e86cdadec1becbff3b5b8314ef3d6c9d3b7c0b317a8fc0560444fd6587af82b83b5c97b28f37eaaa98d1f3d460b73942aa9ba28f25eca02c7dde408db1c97976b3804afdbf69126ebf3ace95e596db028c86452836f0bd9e1303eecfb3a24b27e9a4c6d254b8dccea78267be378fc50992f30c77823984b1c68cecb7259ae0e9da5aa392680241135ae10c0f92d0d7c9d436e377ce1d6fd8b6ad160c9f07ad9e3e82fd562c0325c9c61f8d07c60ca0fce42ec3f073bd334edf03fc397bc25703229ae80c2e4dc819f095e29c64a5ae9db16a04686d80b8ad66614abb605c0693a7a75280cd74664026dc4a6f5d880cf6a84d455b3d4c2149acf6e133434676e3c497373d76adbbbc00aa0019753aba70761d1783da4b53f434f15fc7ae3b2a07f1bc2eefb4e31911f5663f42b41cdca81e453ada9201c503e1331fc8c358ae137114108aab2a1ffd9c40b0bbfdb19e24dc03d6573359c6d435f506339077d83c3dc283a6517bf1340e885bcf5ed1e6c3324886f5014b4e69f23d23a5777b2917ab25271dd214c34f876f00a51236131ddd09e66e4f957b6087029cf62b4079d2e41e360a4c24cec4c721e494ed5f3b29d7ff49d4b291fe749bd626c8ed9ca1083e7a1b5ea703ebc3cae629c7f379839a6484fd36efa0d27b000ea10de0242595f143233068821d4f62d274681935e1d3cba477acae75dc7591836487c76e8fff2ab81bce2e7dbaf58fb7e9914a3a34136d01f25e9fc252063958b299fe937e5ebfdf070777c71f5c444e98afce9157559d6c98488112bd5e52ecec74143559c8b1c1ada44a02baa1a9b3571eeb297466ee3a19041c3cb0519551da78d9d548bf7f871446119ac9004688065bfd4abf4a556250af0c94131841e97fcac453549c5348a928327910c8150b0cd389683d99706267174e70b0f571ca84b52ef6add159ad448576dbd9bca8a3b0f14acd12895077a972eb22fbf3489731bcf54632afebab5275430c17c04c6942d6aa31a25ce6885ef0fd312b7777c77e331da6c1c8bb445c447a06d99eb26fb603932fd421ce2a2d18d11b191b4554348bac6392fbb6bfc8fd6d08e6586e7435fad99a335d69dfc730709e6a3c3e5127edb10bbaadc95db2f84e1a42e40cb75a9581cfa9c601cffbc7ea802b58bb0dfe5fd62ef5f8ce70d2e1a31e1302d38bca8fab351f3fca61270eb3c05de752b6e4983a5f97e9497dd1f3f96d51edea57bdd45a1bce555bdc15b29f186c20cf149c399476935da374bb874cb66a5b921cce295906ab0777b0325b9a32f423a7bc3ebbadbe90d441bb6193873d91b0be85a4596685bd3aff4c8dd9bd97f80f1113956349c47e3e7db77e90073b9e0c3920a380d0379b826a3e3bf265c724cf738347a6af6ca93faa30410cad8c0584ba2aff31eaa3198132b30fcb4ffd5e3619a07e9a9745be82fadabbc2a75377ff4b704f494ce56a0ccc01a312075efbc5d98cb3199f3f7e0648b1cfe7d797fb88feb770aa07f722d36f9bf5ceeaa6ea352164cfa439afb8dd78db2939c7ca90cfed937b2255845c870201789e9478a0987d4340d5966a455f39f486b5a491a593c0160188a4b0492331a45178cf76521e0bdfe02cb6a7c755a0bd391c34e6cbaee909a36dce531c8fc11003e82e0b7ed5b948c1007f5d8ca22a39297f9a479eaab66bd7ae368aacf814d02faf66a042b9a189a3ed2bce584e5c361f8e2cc4ad2ad9c731ff4f152b8c861a6686e5c117724b3fbd4abff82108f92f49aaad76534d1cd1ebada27940707064d46ec2fa38805267144f8ee1cb2d648e669d6c8b601bb324773a2bed1caea6d68a3ccacc9c0076fc04db2864d7916d9c7011f114037f3973fed5156ab8a05397a33a063324c31c8a905cd658f4d5f9dbbf7c04b403557d3415a6ae8a5deffb90d6e6ed98be9a47b6f2a7a40d1377b25825dd6d1d4a6b525a646a000313fba5b1afc947e28dadb63663f7213ed16a5f6b5cd7e0312baa83ba1f5c5f23fa88bf0f098aa6640bbcf639aed67f83ed6da0b6b31f20c24a0833a367ad36e2fc5a3eebae6f200e77cb18b0acd451631d1f8e72c28059a3b2fc27c533cb27f1a9dd30a5acf213607f90314b9d72ba30ba036c0b223a2749718dd4b869e9e4b1c6d387b2c9012621f828aa5f8210ac97e0dcbc29726e65ac8a3a6a490bdb8ee427c619852306c3ce7c72fa58333d75918069b56b9be6fac612436eadb15173dcb1f9bd43d41f70a51d27aec41d8b6862de44672753a70686e57b17957fcb6a4a334e5f94a3ae95c77c78789a539fb9adc24dfb1ec71bfe6a84542fdb0a9ff3534dbe6ba68f47809f33071b2ee608468acafd2b1f3a7fdd07f9affefee5403593eeb90bb79dd02135e9ebe5b24397146bc4cd1040d70e5ead0728014d026e162a794286d5135f3e69b021e4eca00d2ab04b0fd2df409b747924779eec44a6fea3f9b0e49651ae253d1ab629e6fe7edb2f89c6aaab9b4056d05a0e2d460dd0068499910b45cda4e6459251adbb62bf7e89a95f1cdeb40217d430e9b0d213fbfe44e924be1940442e9a9361b9e622df1aeaf40116e8d0587084b0b679636d9fddd2c9b1fcf807eeeb0d22921c5f111efe5e42c7363edb8b39e940fb24708dc9c32a67a91231e0ad9cc1177e35833f3fe7f547ce0648ead9cca98561a0301cad7d11eb46d1049c20ca5693a29fc80e68e0324d6b34c366c27706608801e1bb50373c56d325e5ee899b4291a39c20a74f963ca9b609a333540d9484a57e26bbcbecb04c5a04d914caf75bd9b1cc18aa1f18ea79678419bcee7d2fdc8bc1a3c8e24c25cbe527c58c10a67187fce8b13c35faa2acbd402d85e8213c9fdca4189c1a9dad28a1bbcfb2cb33546fbe935082b8a50e4ad87cb81368be7829465b3b0c56e31338acca17f3751e2a8d012ddb63b3fdc971272c6b5b594d726fb1eae7cd557ff7cead9497bf8ffcd40c9c386a067842dfad36b98f2bccb96da13714ee754461328b6cf5ea9b83cb6c94d6fb5e61015b346f28a7c9418781a5504ae00826184fa056821111d02d5846e9b78edaf56fec6932d6d2c21d8f7a071686f0cbbeaeb3117b9576c78c54e00da0ab5f511ab10919e353635c0439c6f5b71513d05e04d237dcb55b2bb88773e3ad5db6cf42beabea2f1f8d745bf58b8230bb97127efa7f3f28403d8443387bbdd7c391c606e9abedbe690e5e4b2e3de47bb7684b1188608ed3accaee66a27d05be7cdd20b554daade65135cdf68ea0876bbc9a5eacdeff5d542816a484b6a65a2338da8f173e6b5a434fb02faa141d3d9577347ed9dd1c6931eebcea322524a6fa140800dcfacf0eaafb07e59cc2ddabc78650e72d1c894e2c9cebd9a0f08d7115efce4b6f9e2f61b85b3bd613362eec0d2a066509ed10c863f97422f49c3a80642ec16ed4d2d6b386f856bf7574c2ae7f705a5640be2fa2d48236d0179d8b5ba7d63150a2121f37804e349af3076a85605feda47b11b96ee153c994dbebe664173dd996f6d049876a7936c309f33bf32ee8be266f16ab1bab3f2260f78a4b894e26841dd0f43b3bca06bf31cbb4f81b38e408f2cd9e44ee8c82e1adc067bdb441cd82d957262d9aead55ac844f7d6f9b60488520d5d5dca02da59233df8ae0ff779a94ffce377d87b30200646b29ca385b23d3bec850de659f18c07c65a52c850af41318d40a4856ba88aa468415672cb2f3bfa3079fcbd942ca7a3b8d1d4bd7173a0f4bac1a37a22e25dfeccce29e82800cbcb25f9fa4094456c7131618b5a7f00d83fc66d06a6940fbfec01b0b9587159f821fb3b36b48da94cca53e767e9e1b94ce4dfd7cb3fa07cd9ccbd122e1e2c6b0da46563024a6da0a706d21f98598b7cad58006893650e74b57556cbdeedb51a4f7b27be54c7c2d9a68e65824970de3bb1b3b8d3d3346c2df136c6f1f952b7a41c99b36fa978d2faa1904b3dd689e2de88d780ea118856c3aa0fd13bb865f35f86569ad386278db07d9174d71da5769447f816740314e1ae89df6bc1ace8c31ac75efb0226c598d032f209a3e9912e66bc9addf58b5bc6befc2eb68f7ebd45e5614a7ab5d9ed21bebb67c20a193d842b29c38a1fab2e58833ebc7ab430a1c43b4bb010c9b11c547012e76ae12b62b11ecfc1e0354adae7834f769f6ae2a85c642cf759acf26b5a086a6aa4937abba9b6dc0db97f2a9a71a9e56bd8c73c39a99bb4d541cc0810f3b5a7b6a1d7b53abefd03884a0d689ac9a5dfe758372e66701e7a6fa38f051729e856003dcea9b16c616a0a46beb497196ac5e21cca220de1e34d570310c207e8c817c441b7c3985f519547ca55fe130bfd159ac78acda5fd89c775eee20f81b7b28072bd7be2d69b66706aa06719485bb9ae83173f71f368ddbb11b0d6788eb3217f959ca5ba4b73a64e68cb76fdd100d8be66f0f82bcf21c23c16382f48879a144ccdbcd5c5cb9cac5f78666cba514dfd0f442e7f7ba64b3defc2033a541a4ac55deaa4f9c4271520aae058f29542ba7ea6e4777f16f8f714d928726c8b8f0bfe7778dbc45a13d8f4a049c75c14498acb71675f222c5ebbe310c8518885215c0c30e4c1dd7c4341dca7b5115716f7d643d6b402ce683c4baea2f119405c43d0489a1eb1a39c5b60fb3fed4c2f3e391adb3b21a4e0acb003c9afee8a207f109b227b8f8d1027d47966a45b8306a722b188f4a9c3ea8eeabac1edf0179ff632aa0b9d7da1c15af98c070828f3045e55cc70655e855f38b1b9658641705118b1bb827822ec39be7cdddda8f000ebda0cb7229eac79ac3d3b3e4e2436a8974c015f147e6437d39edd2add2ce0f8ed6922c76ee86f22e9df24ce289b54fd8b1ec7de2d085b93bcb66df30eb581ab849fe71a8f8248c9d133f3349fd070cb1bbbd5099738a465f3dbe1e666f2cf5bb815af6d57435ebca6a197aefa96b887ae1b336efd16d0f60d74adc801b12b900339949d17ffb484b99f815b76c597e941bc188d8c73dce8b4e09019630b84bb7c1eed71dd7ffcdf96338740fa0e38c3a5ce087d05320a572450bc60f9be1b956be7bfd82b0656b3666ef33e4e5f463ed5884c89737561e076a93166f0474d9d0e778abfe62320be5a3dbec9a15dae59e6b954ee2bfda7b7ac27f7f5b8ed612871bf46cc60813e09afccfa85973142b9df22331173fbcd7b961164dc2b60c473f264ebcffc0fd47d6a561f2c1861af2b22c536e48e9433c6247bf9b9710c8ec630eee68a2c2426c6f62d762a16f9ccf78a3cb19ee1ecaf4fb94fa2881227e0a10d32738aac374c98c2b616b215770d1b3520699aff0b94a6fb413987771a3a8f4640ba34aef1f580f7d4bcb046ce3d590d8ca809935019605e3c5c1cd697df70cfcfa993117b551cabd74a4501c30513377d0033790b7f19288ff6c6e1e3509d875c2e74a664de0dc5f9760ca5cf07b91fac089c0952b2a385193550a22dab1f07717b971b9350e7a3c3dab216044fdc63a1ffe3827464c57de949966122e93f750ede5d6c5cb232ef4bcb2d5257d288aeca2938374ba85345970ec8de5486c01960a81c1bbf99e16c6b0159a320ce364524b1ceda394dd99ec57db30b565feeb12d8a59e05b20d27fb6d6d72e99994ef9917ba17463c3bbaf8aac6362ca5d8a9b532d1297130877534466db5076f0216ffc99640f7fd133cd57889c18a0dc96eebd7d83666a49903e79dc263e1f01ef87b098bf7d62166cfe5da411c4108eee4ba4d4592d738d255aa0d239f3a19b336d08472eb35cb077a4791607da72ea6fa4444054278f97305c31501d6d3ec62572d26cbc9108fd29871617532893e4ebb36b70e043b81435320f34fd0604924b885dc9e3d91df478d99873f34f4b5c87f801df7af9076e3649c46054929549f5d3178f73eb94d99aa930e0bd39344da86425c17316219892f40de9fcc111a4e53ad3df04ff93b556f8df939dcecf0796f117242bd17207fa6c6d263119ccbcc61facbceb726935581c418a8ee3b2467f2cacdbdb86d1b6c5d3510dcdbb40fa578e40f8f868401cafb510af478bc03ba0b3d83bea60c3a2dfb058e94c45428d8ca400f99849cc4622d0c661ca0c5f05a888f798ffb2ce16ff587193e9dc85e21f58e0dc4e65b1c5ccf642ea02d464d7010f763903b224cb5b7f6b8b3f1724cf40187e1f148e6bdd0ca43b89f9b36f0c35b075b72fbdf8bcc676d9f70e66bc3f8fd69c90b7cc445e0ac72f0e521e40ab1e3300a17656fb284b0e7e020c4dd8c5cf4d77178056917c27b5fb33243c273700a4fda71e6ff2b548e5c0cfbb336a970b28fff2e710632c795a450921608fc42108bfeed10b79e853ae21be00f59d2e1c36f0e780d5b361b3a5b9a61b6acacecc6b32e5bc1334d97ee51de83d44cda70e4eec0d8553b840df612208c59e77517c556b63072c0504964cec4d884ab1d44135da69b826fa7d4e6bc933786e75d8b8194e5bc3a995737c1d3ca7933658caf9eb87a11a81fd94d6f53ce9cb8fba9194104c78a4291902cc185a6f540fba631d970c0f12eabc48c3c73f8a9e0171a3cdb18ca514bb590ba8794e3a7af35ca16a1998b3e7a339c469c70d42984e04a8a6a340f15160a279d939265e9b29effff76d519900e866358af705203ddf9925d507863992fe3699e7403798ca88ebefb3e7a2be8510b4e057fcf177f224eda69cf8c5b9b7294938b066199edd569c368a4ce7984b6a2c4843a3b46b4b60c0673cd65b17798445a0b1b218d0e769c4f630b9097571c3e520fcd300675fc5da73c7647f3dbc9afaa9e3ee55de84c74099cd2e75811fa922df7cb84c862bc09106fb4fee4d20e9b2a858ab3137c921b788d56ea85cb1944f0a15fc0fa5ef62d607d8ae9312bfcbc9256259532cc8a0de5572258182b2fce5c505a7d33f0ea124cc2b8cccd2a81f0ce98b275426c224ee5bc9668e139d4931930897bdb09221e702662e91a5790147ec682667553653952badf1a487546bc501319ed10b583f6d8b50c7894e9f09c1c7f1f3e3d74d39657b301880b985361692c327f0d2e41d74bbc9eecc68cf470b4555ec84d1b95891950e16d30fe8691edcbb485cdfdebefc0f0135982ffa290cb27454040634c4bc9667f25bcee0308edac2ba9fdf328244cc0e44a3a3a0f254d823c46018dd40223db0481043471250ed4de1e14f40a2185c7581cd4e4fe7d7b0dc0b34497e755d32c870c22c70065603e33e81a5b611db3eef2e07d28197ccfbf77a148f2099d5b1b60b72b5ca4c59328799640d0c5ce64be2d37555043eac57b922d1d8dec3f04cc6e7f2a251d4f6f803202df30662b6a907da6220cb4065a59751eca9344a8740b54b851e710762fcd4ed6e507833cc4c91368e014025b23a1488093e26587603b7cc483a02ce5ba7514eeb2bb873a9251343d8cc35342188eea69bbaadd4646fa137269ede0998ecdc19ff20b0a5d3c1d8b98406f4dbd03fbe606f60b6372c1353ce40ae6f1c87a26306d9f352fb287e8706aaf9852119080b26ee6ce168e3766c2acc076dd06dcd18c783d2007250b4c9cf6bb9a430ef014934acde48b64bfdbc7378ce81b3a9334519972d95bad65373f9eeda37f1e008a292e8a518cded91a04b082346093078880294843734f466a6db7b3a71dd4d0dec391679372a29f2b8f4ac3083fe7a97395f2112b73bb9c2506e37b8ec724dd53f69eb50341267dec89d31274260745911776ac1d93dc7efd198f55f9bec503762fff93b352bea1b00a3285bbd90c17bf3b48ded0576c3446d4c7eb3d3131990126100f041e07c33e9be5cbe3a064dd11f0548f03f0b84fbf856e5c0f9838d5e3262d7daa31ac70e5d63149cd7e97933498e70648466e166d4c3471831b046c0130e9cb21dbd799062f30880b9ce0df100b8e6b21f9109e342ec0164160eabd9f723d5713bad1a2ef793459627bb0b7c56a21ded0a6026b1118d41a5a8ad14863b704c9440ba3c5fdd9e580ee8f17ba665e2a130f79363749f829dcf6d9a13095e3e618e193e4585ca67fce1ee9a690d7b15ec0d7f6d040027373db908f2a3498d872d34fab8128f8d563074ba0630e5688935a50753a2c378bddd927609b9aef24efdf5e5025f9970b4a49e293af2137a20dcba94f08f419f126b763eb5d8c67886af3a3f3787fd1b50d9a2623f66ffd2f23e1a9ccde5f203ede6bb1becf4a1a89071b5bb3d65613054872c09aaf6e73ece29bffe5b7ed48ae454b656ebdf3d88f23f6fb5d6caac04d1882bdddc790611e6ed17a0cf7753f20bb2214772bd65cf97e7793bbc30705947fb9abcacb1f81754d6a967cb6faa9ef5b1f25a0df01df68dc6c532ef56cd596b38d937035772eb97a34c41fec71cfa394815984d652864fa90681ee51b81b098a11434d90f9bdfb48c21b65b9966f5564208cbc2968fdaa904767f8d854b60a20d80141595b10a088723ee153eecc28bd345aff03a1258b8e9557860150093c8a5cfd839a42ce204f8cd2fd829b854704ee97d862f7dc62dcb2d61937072f9870236d2db75f3e0b13d3ca2747a27b83485037fd543eb189f6e986b74aebd379477e090ba7fe23893e72a583f7179cc6c89c18978f286692bcfe6df0eba8c2487fbc801d9c9bc03ab7c0031d972c217a798c97f83e6deebadd4fd709ebc12586bae2a9c47829c1ea1c9bb578886b5cd0e0fdbad7538f05341fc9f23320464e4ea1c36d5ef0cf7cf1632b3f43cd612f9639e4e9e40b85d076431a1f34ed9b2cdc47aa3008a050af58c14a955bf5e42cff7e3e311244d1290b49bb6107871f672bca64c47f120b438b54ec23464dc2f8c327dbbb45434ba3b382650bb1d2ed02bdec188b0b7b8656b93db52c9eecb42ebfdd995335a6fd3bd0cd7c55c46b01f0f0a7740e93e8b9bc6bcde581d874a0c481bf00072c0a022c97d98b58c84886989980f45b4cbb0fc133d51a940fcb657539171957a4b373c96ab0b0145202828f3977bef7c8ff5434d56f152133c5e1d223983b7494758afacefed4544e388b7657706731ea2bbac0f816540ea5e52e5abddd5d3832068dd396bb7dceb9ebfe97d87f0e83e9a0994cde615eced1b1946d67eda628cd252ea0985a5a89843b68c5424e542fd232518b9209a0bba1710890535572272ebcdaa748145d9e4608e41dc39737b166c39862b37ad0126d0c09e24f43490c95a6b6108b486111643ecbc1d85c1d4513e6b64df501cf0c7258fe12bd5684686df9e20604647b8a210481df81c0c0bb794571617f6d9cbbf5bb40b6a795f46c194b0b6ec3e9bd890fb3767044461ce1eba5edcf25040a4dd0cd3bd6de43722ef2932fa4ffc942d2e1f27ac982918c3c9c60374830028c31b4f03febf0aa6a0b0f755c60c487feb6117b12c84b78c0c1e368ef1cef5d07e4151b96627a4595a81038fa7e6faaf552a294eb885a442f99b1cd010bba4d88983a1e97414117e6efc1c076ade9fdb7ed5857c8e16cd2893ce846914a5ef87140da4584f56ce6f9a794f948f37de0372c24f1284ba0d4f7cbff456e68a4ad281f7877d79b2b769ea37371dfc3fdd88b76e8a94f8951795aa7c659fdd393b90608a9fc20a006ab935791f73d51b6fc5023f959f6ffc19e01f589e88069c0a2a3f0d3f073fd776109b162fa042d92cdce04bb1e913ebf117f073b5ff0802c85321bd16ce6ef4e0a1d65dfaa41ef43a9988427b98ad536e7ef990f45d5f68e7e244dbe68de06cadfca721f40f351e22407a103c4d88232b314fd88f4cf74be8861edf8227e95695cb5958ff30de78921f67bb5ebb5a677bf5dcdbffddc31e11e1d24b3b5c1b57f5b9e1e28faf3d718c476dc10744711cb88a6f8f3529a0e4ab81190c82fc251bdd3a63a878d8eecfee3177db7a0e938b5b6d6afb7da1331a361f3b0f70e0f5f917d0ee90c5f53964d9d9b272af4cfacc123783867c53b688555eb268b20b5c84a966d9950d333ef8d619466b9a33dd66cc29c7eae736e3e635ae55fe0e721a4ead3a6f2049de47c267185fc720d46dacbfaf8d1ddb410152734add9e6dbdde993ed3a3692af6f978adcd2a7f1c11f1ef54442ad17a5027f76fdf2719cdf57882403cda5acfcc45247b9ddbb6fb3ebac6b8d689392d8ed211ab1f3b6316192bd3bc5374a317f43d1abfa22f9ab3629f6ed849ace5fbe76b826a647ad7b2b6d186058e681aa46f0c65fe77f8aa412ab85443298d8754e8a54b7b7b3e8a0bd15986bcb5352bfd3de2c046eff1952759a5373d43f19b3d8f2ba6d25462f5834a9b975375fe24d8596dafa0346e4c0890f66de522f4559a925ea5fc3dfe7b54ac6d97eabf1c20a41b893aa9ede58e80134e3fcd90b465d590b57ef19e2df169fe657915853505b405317e199b7f00dd9486b39f3d98a745d399fbc6abcfc90d36d2fa7bce9cbfdb1aac2b6c55f4d9ad7b958530293874cfb008de89b456cf598f0d73b20c9d1a7228a773366a94df83d1b2a5d9bbb8cadc44e47587bf73dedd6de42a0570ffd4583133ea161457f3cc009d266c44e671647f8b7e9b629b64f26893cb910b7278e817af547c87486481529cdbb7cc6efb79352919cf87d9d348798ee7664a576161fde49a651a8730b83a19c504f1d61177642a32f01a4da386f8476aa6f904df80d9af1cfa382fabc75eed61b72cb928dd4c2a0250da55b38dd4505dfdf2fc583852787c081f302d1794b3eb212b59635ce0bead5e4c1b20d88ddf6b969b0facced21dd09df4d14df4cedd08493c858e334b14d84bc7763209efbaa34dfb1f35a04412bd0c71f93a9ebbf12d9702c66e631cec35d4cf51699ffdd2742df0ad9e86dca6e0efc10078ff0b72a6ee6566529801e3a1f91c0fad0040d0347bd3dcae827b6140cbe11f7e7ad87a3c06f6c222aecac2fd6a07d88f5aae217fa259a72c4ca7f29d3411a98d6dbd92e203f3ebdc32d226945cecaed8ef59b9be19f6f42457e146c2f0c83923bc163e46ad2dbd3259fd7d0e434a60d85e6e6f53f3f2907f761a1d178d170ddd0cbf3a38d0d6cd5e780c193a0be57a5fa083d5c20023cab6aff94c7437e44f1e6405a2dddfba4479750e07a12536998c4019e512109ddc9ef505ecfa72dfa4ad4b13bda76a97dc4d02dc5dfddbe5de3dae8d49c0842b1f31b900ecad06b29b452d6d5eb176207f6e8907e97b6509cb4e3ae21cb03123fbbccd3247b94d9be9aa7d96e09be049265a3feb46b0af2a616d6c9c8fe7a828217dbf292dc8721e6ff00fffb9663058555a9ea14fa2bc5c3b2889ccc05f2e7cd8ce227667b91f3c45b2264feb0c9886e32f8dfe843a771ede012b69fd019803024083e2151d48dfb4763bc489b81fd1690d43e411de9fab1127ed89678fb2c3b7fd2e9b31f5ae86b5c40ca9128f07b2947b683f70b004d60ef782832bb71ac22761db267df9e68499e6572e0e8ae79f8b6eddfbe2a202a161e33edaa816242d6ac3f1ee0ab1bd05b59c5465648679b4738a308c86e085034c098d3ef7172f757c751c9fc72879f1fdc04095cca4fd39592789271603812fa2d5b57d14eb1a6fa0fa7308571a39fa3f05ec80acf819c873ad525df9a03d1f867d8f74a592d6e1ff5272157efbccd498b72dda4154d6de12eb1c325fe16754dce62610ae19edd8cb4dafdb38cfc65edbcb8b696c4f95072e4193eaa2226b834ae31ef4d0c0b1850b80a58e3844958c6ef4b38279791606515f55fe20f6bd803de2698f6d6feae19aae4d93f0c2a02d210059543afdf32a0370d6a71a6196eedf80c9808b955c30f88ccefd404e4ff6f1036a5be3d5bf171c6842c3b5929b20b0ec9859abfbfa69bfa284dd978079c4547dff3d3ef9ff8441f8ca1ced84627295fb8864f8201cc5866bd00f5a903d01c20f70a6e906f64c03ddb8daadc89cb159b3b3646bc451e5208c4ce2c2ab08aa19955eac7c11094b61aec410afcec7bb2ae20be88e24a50456aad87f8a42ffe399dd7e197210f2f97fb1ac358a2e753207abbf49a1ef5ccf4e0fd0fc0f3c94a7f7edca36bab75ed6e752cd4a72e5221f2a7f8c28f21f1dad862ebd4e7c0478ee1f7a087a709476121ee678fe3009a9f29d0ff7a067caf07c2fd0be41dde9b2e2c52353325b4db798bd914700f918d6c32c1278db0034e6175dce3f402993324537b2e03be6c59c56a926a0c343e0cc2c433dd6ed1232816555afd8d9626bde84220d94e02c9a5251a8c995eb904234cf7a7f8583c5a31012e8791d03ef388d3761c82fb0b7a58b900b4c562b4a71eb375145b3ca64a5bd3cf962c8b975615932a47e28c0e1e8cba2c39a56d976f35e8036afa5da2fce99e6258b1536c083c90e0c13e826708830f74106171e6993fdb6ba33f2c4236687d06f934fc3a327a367e95aa6f0f8e65e6cb5ecbc7d5b9490be1ccf6296ce1d737b475495bc46c6c5f8dd9b9cd9b1d7f50fd28c284036a983ef848954b3472a775d91fdac923451878fe91d3c12076f318e23c6ae495d259d3170409ef6b2253de305ac234c6e880ccfd204b481412e376455e78e0fe15ed0a900f64fe55b72901588efac596c82d2ca3c2aca517e500e1b23aa9cea8b3af12125845ad9a71718e8acd53f587b354c987030de590323b542732494f1eea11c4bf09bc59cfa49af15eab6696c70c90df66f7ec488c02f43d183fbb60d6c42369a51f07b65f52304ccf0b4d80a766ba8db3d53d24d1707179d4382de1bd727b038e2d54e583eb0568984a8193a19bc0a58af3ebddadbfacad2f812853ec0c063c3b8fafd96d16714b45bb7ec162ad182811e1f2a64fe826830a9d686cf7453e56923530060e62420a343f1d75dcd1dd0359a967bb8a4b50ca212b70f7ce93743ba014ccd7d99fba0dee6606a1fc2a21c100fd2db3fd275486a52a014a1f0cf74bd7a1fe32e1bcc9e2690cf3adb3fc9d9277a64fc38384fa00d9eee72eba8323a5b1ed8705eafee985973ecf60fe51202634b41cac26493e2bab462000a76785f38368b2e107da77eac5e46a5bb4699f7d4ded25005f48cb7e7bb9c9890a2d2d367e2b484c5fc65aa3b9c96badcc42e1fc2bda51526c7a710cf77115494b468a200d67a40cefcf8acb0e7d7a9a664e194b640c256b0c9bc8837fe2e96199e49aa002fec3da848ef00f49ac00b5d60f9e841be391ac1772ba36fa2810459c40eff8a4e30a4bbe563ca81af27820d8b1f706a55a3064820688a47033db08cd4f4dbb3618defae88e5795a3f3dd065ef7d63eae566f1b9ed7a0f70793d639de9214793d7846c53e1e7d771a7b00ac726d8ea47edad1ea00e496a02fefda58752593a7858a5c053f54790c3e8c9bc12127b0900eb18687ef03a1c988b8d4a17b4ef835219e75375897365d96b2cab52a3205ee9c8a2fab98cee803210c96ec0ba5eb75bdc7230e0eb9f839e050d5e1749684edec85c44387bdaa9c2c7781f0af89a76fae3d77e06540e80778f28970e001605074048098945f491c17b9f214a8759891e59086463cdc3520936bac043cd0f84e81efff1a7027b5674b8c561983f38a0475af65e3786b6eaa82661bc1f1db7f85350ca38eb2560cfa195f56aee6cf3e0b9806e81fb88f6e3dd5a62338e98271aa1cdab4dc32b567df90486091b47bba4043428ca314c795e2dce9507efae44c11f6a663fec4350fe55085adae0ad3c26f2519ffa102663984709d9a8b65606711fa012b47717afc664e6e51988ebbd98837e60c0a8adc7af4484527a6f44f8cd82db2352e705386003e2459b808a98500ae4beadbfc4a505192380e1004abc8019a9883a0eada09f7fcdb349b2cddad2560433d032d1675aebb20216831cffb29ea9021b4dcc40968b5df1281614f06d0c7fc28b50049a34d239b3e4430a4fa970000af8aac925734572409d87d15d633ce5ace38038cc6cb345bdad87816aa669f6b5c13b0c37da3fdd76c19ef09f5e4be4ac2e4aaa8bd028261de630a3076f8775ab1ff7a947cca659f7c6b84d08e050b8059db1850e3265e4f3f914f8bfdb14c1eda874dcdcc1d22532bb57d4fa3079db678f572a9dfbebca979f0e4dd36b3a5145d3ba5da082caa401a8636950fda65c6c35e5265e6c5e3e2b011a735c0240895eca28d81e0988e4948738c0f013d5ee5752392e6816def22350495b936c84b653febc7507684d93a3b80fbba53cbd283b9ccd1491dbc41b6d2b57f28d97a176fde8fb3069aeca915661d0a0652e8002c073112f93732d07401af4e05943dce3b079e7ca48a4c140088ed1933d059c7528f8af7385f001eb24cd0a1319d6ef740379156b83966ab73338e9ea5eeb05298864d4c0b1679fdfaca17e94b44b8f63d3d20999e3bb8a43e27c7fb17d62a08fdf440f5f748b0cb027f6d9918c0c626faa0638c15551b10f5bcda98f1ceec1bdc7fe66d07caa8f75ebbf310b7d6f6328e6987c0f11ebb5fa8fc9ccf16dceae90bee4cf735f1bd488d16fb0080c6ad786396e09f6411df2dba88ed890a9be9cb08aac6a9cc31fef31391b7a8b4289780193749664e40462160f70441fec5c6d89c54840c499e14acee9e09b39188c861d478492d0f0521bd0dd236ee8b5a40c90b4f9bbf4a372130ecda37a42c5b06aee2e7ace4408ef15f107027841ee4119836ea79a6fa7aee9115b93952ff43119a311bfa8cebd56b363e0626742e2cccb85e9ecf9a7f7f9ed6fdd9c01f7eeb0f94edc38b697702b7f86a4ca44b5a5117844a5db2e3c40ac7351eed9f38381e22277607f50d5b4ed68e0bf298cf29dceac9813a7e46d4cffa734556ff460eb67d58edf27e554c0a88925eb93a8a405f6cb08bd71c4ca242f86627614917a45e4f7d050d2bcd8564fd9fb21b51055126d2ebd7cbce30bbb3e94d56c07386e290d3fe11e6e355e981f1c7ac088f303b89ebe17ea62e7d3c89bc6f2e89b67c8d4ff429e41b80b4a6922e1f3fa022306afe3990747e1ac8d30b84e5b78eb58c2fd332c7bf3c715fe1fe16eac3d3fe7bf4ee6178be9c9d12afb7e2622315a4aecc6e758094ce0ccf30d317290cf43acc3439d7307d65ad942787925b687ab6a8e64e90c335412c5ef0aa212d0ef3987fda7e08be228d8460d1b2589eaa53b0569a3d811502b15b5421b0d75ccaf57e45ffcf8e302da623d84f5cf71a9f5580b12823abdba68420a5b901301200e03e15fb8af1e0a7c3dff1df0435b7f996da225571a6e6e28da00e6174c735f244219101514c88a6041168a0d552fb0112a608e5afe4a4d100b724ded811a9a886ff67f588ddd78bf38958322e9567ac18164d11139ff80e58341beff18e23faecef1d77c636ef2c4b53b4b135c0785cfbf8f90f606256dc9eabcac8f944aa868fc741e3377dee41ae12bc14cbef0307140e0f17a3ea5b1137c10f1b0876dfffdb0f32aff150dc9d39282d9c4d467e0ca14a1d8c3af99817408dc971eb0e3dd079cf4bf563ce1733f9923594ebcdc5eff9b209675429750f123f5a54b8c0dd2a7a7be9f12e9b92704b69927bf4fd71ec42ef48ad165cd9912d5eecc10be0303b9dc55d5eeb8279c96d938e49f5675dd6d8905683bf8f883503e8f575af5f3b85e1921d34525421b33208d878956e11bebc5a25de8e17d5f94621c8b7a20c7c7796ce658260c15911f85c73de4f6cf6da7e8cc89354c8d9a839bcf1936f5cf8a6654134e726d6876c2a621d4f8ed729042f2dc637184e811471f4ee09331c5fad1b3688c43904c4bdbbcc38079674d2fcecde9a6fd1aeb4e8ce2867315ac6281c03b4097e88ac3a12b9a5e127ea321ae94ed232ba90dbce7d26b0d7852b80716d2800e34a373d9306c9eca0bf3ecb1b7af44aa45a85803a182af2e321f09457bb55cbad0d375b0b17ec20f98b65eaf40bfcdbbe2b8031811b689d17d719390bf973f56a7d8ea77716c74248ee2abbb3db47581193b2c06651d4e121f4cd25a829056c4439afadfd1753f81c186066273938cd42dbb038ceb4f44dabfeb6b47d84c848c1a945f3932335703b87bda8ec135c5f85ab0b7bf571380835f12c34bef5bfad0c91de1bd57634bf4910f089862d9b724e8d25b5a5469336d25cfefb99429f186b4c184263bf1f48799c6a94e0e889babc475e7069760827e661dcb212ea3c011209308e5036f55f69ee799391c0694f362f3dcf8a3c5a7d99500788c013938119c7a1f445f0891c3f4dd474ce173432b78db066e7c2efc5d0d246bf530c312883eb99df7297e7ca28b4486ee08e5ef30b279208498704748d0e38e10687141f0d1f9081253c6cecf1fdfa0b2fdee1274cfe334275cde3a41011b34ba80acdc1a0d4b9a4090d5c2cfd2bd1eb97e324a20d644f08ae942883b2ec607fd0b3c99be57f33f107b3fad4bf708f9b7f47b74d87e4312652c29037586b4b8cf224d695257c767ca2d7b1d00ae36a0585d9e0a50ec7176866ab42bb41ad73522f2e39804f94d8194564eab123ccfb8a6a9899fa660a791989d8fced2f1fc0388cba6fe1370abb42d635bfd5930bfba52979be01f9e1606eeb088c77eece7657096410af87440b80ea823fc630a5f335fa677f5225159905688f0272c66e00a1bfcc553de04e5015fb4c4e63563ba3026bc056f36d100f551324b3879c32179e1fccb0e75471377775c19d0c9c2ea2235b9eb61de4192ea34a4e84ec94cb58c1b2df67584701deb55ab20169b8660fb0aea96750e492fda93462b787c686014867e2d11a08c04c85d4bd9cd5c2b1b50be0be398fa0b423a7aa1024f37194bf7e7f68c00a446fa749a48e5f0108294dfc6247ef94c8070803b89c2eb8c7da2838f6428a3fdf8da2b85c3e32aacdd98b7db4340ccb1978fde0de95ac2bacc7ee5ff32d82c12b69b0f3e07227c82551a6aa96bf19a48fed1ea461f4d44ff27c04f846c3b5a3e81beae7c980e695a1a52baecb4d70dee370a1eb287c8cefb8807b2caf523c95d97a4bcd42dc828e41bbc9273d0b3153bab91b2b195d82b4cbf7bc26d3c4929f7a2116d275c23a4db332d96918b8c4c196087f49feaba1f4bf7682b27a8b5290b66955a560730dff498f8ae17ac102c63645323e713f3da9b947329f50ffbf74f4df5b19f0fd18177e9bc7cb643fb12681ccfce90bb5d49be2b99532c7a4d3a0c891748b3ab9cfcdff41b6cdfffe53912b56468d27e2c9ce2fe08ed085b6c9bff11a90f5ebcfb1753e5ff0903f19761364422d4745f93399895579a9574642d0be5503c79e39ac525db8a43aa326b6812f8569188ce2887a36a8ede141f33488359d14eab7ae8fc7b5d3d5e036e8b77c17d0c616d2f573959b025bfe4c3a63281f259f1b68d66eefaa5b2e22bbef23c493a8f382faaabfc23c1ef4dcb3522b401f1f33d78233c9feb0c289a82b3c12652eb9a8cc55512d2546200fe3473a84ccfeb252bd8be2ce819e2d6d84c96bcd4c38fe1ecbe22ed72cf9233dc6e3790fa64ca36192b419af4284f38f8b84e26ae0bc7a3dac30bae2e46f83f108d8ebe480d3d5b9c2fb721ff8080f647aa1f2d66d8c5b5a6c2b4b6e2efcd67fe9031efdf4e3abe065296b3d3c0085bb637117be75dd607a59b90e37927a28f6f588c8390508abfc0dd651fb372b452f3205b760b04746162e24a63c0f923871ca84972840a26dad692f28c555d2cc7e2bd6757222532c564d2abbad134f9ad74b13f766a16a6d519f891f1327ca5c936b86a212cd8c486d8e021f323764f8f12ffb3a636d8f6b18dfd8afab6fb9e92f3e6366a330123cfeeebf8fe49a4cdeb61e240ff13a0f22606b8fbc4f0aa71c4e67f8c7f433da937eabebb81bb3b8bca4ca4c7db511bee48f7b2fd65eda386093b6e9773f31bbb5d72ef0381de285f44f170cc3da0b35731dbb5713f4704e30d5c2171f697afb1ae284b4ece5c7a87ef71cf8e1115ab6c177655e9a09a7c02574783dcc54eab5c3750b2605c19cb8e23adc7562c7a50dd8ec1e2d9af79996a63774dc405705da9d81306e425da68f4f8ee66a12e7f5222fe791d37ee00c6c95ffc314879148cbaa3e8e320c463bbfa5704ed648965f6995c3acc5f42e7ff53f2d453f3f69f192b3079f818795206b3b11df58ee3d73af915a49a5d2bc218b0821cfa0a37a97c1377a51f6fdccb01a5a38bd2299e7ef44b309ca463c102090daeb76f1655236fd138a76eb3d056491acff99078893a42aa0db3a50ed402411d46323e80c0b5e7390997038f1ef3579b373e4a215c8584be5ecb34c9ac435e65ccc71d2923f219438aff32180335efba48b4b9a11a09eefe1fa2c0cf0f644d6e5b5efa912f42661904a157b4d82a616171b188d0f95a6f29ae6a75ca9464683a7ee55ba28e10a755f68a0774def914bce2f9ac7078173cf2b239c219e0f65e71149499023ebcc44c0c3c60ae26e900c3300cc3300cc3300cc33074c4a0f97ffb0aaa0999929cdc43468ed091755d5e534a52269992e0ccc4979ece4c7c3a33f139ffffff91ddea0b7c0c8c0c076179e94b45445270f5aead0e7bde9a4324ccc65d868e314482d04ddbddd01739964224f758ba98bd62e972234492c5b0954b1b6db55225c8918048c4bf04393a12c904800b600c2249c78f2a9ff9f2553709f8b0f181800f0960e1050c4124476df34b42cadc7c1cc28319f061e3238b0f2db0f888442211c445a40cc40ee222b1002310c9d933764e13516f970b49cb9170ec87165744223c6ee8e8828b2e6000228b1ddb6365b977ac70b3aed0a4322dde9821f921010a7c7c48008b48a48c201221418e1b0bb891231221418e1b0964a900e30f49b536f29352f261730b49fbd0e28a8f08053e6c7c340087172115405c1c00d400861f92b47c3484a98ea7fed487c4d26ad19cd4759af990944598d97a68bf94827b48d43cf25cffc3c9b49e1e12fdaac23f3f95c5479793596c01b203461e924e88ee533206f129966e01030f497a2f6679cea8b8e778ee90149336d1d9fba1e3ba2b76482e7539c237bfa4e61e92c7bc805187247d516b29c65ac89047229148064c165b8080c0a043526511954dff7c45ffcc21d933cd3d07e149ef5a42d2ce0160c82131bec6db86be0fd965485ab1e45c01230e49b9dfacf23cd4dddb2f7a7b78a10347030c0260c021b1359338351f9f22436f48aabfea17cda3b7977543a26b0a72c388bbeaaa6d48aada1c83d4bdbf90230c362459baa7931522173dcb8103640d495a64deef79ce20ba1a1264e68eab5bdae3c26948eeca146f62a3a4bd684872f98c39f62b6fd73324ef27711daf6be163cc90182ee85ca63497c658860495348af6d6b30c3a32246b6652d2c365ceddc6906c5632643f5c6c4c25312408f5789f37ac66f33024a5f193bab94bd4074352f8ffdf5ce5bf687f2141c664a9ac3e95ccf65e4874cd1a7e83b6d454ea42e25ed89293e184facf85041f0db275664c4fb885e411a57b745ce7ff722d2496b69caad1475948f64af576116a2c24fdbe76eeb46f41db7d85045dbe95f543580e765b2131e5e4a26dd55263b8ab90f81ee61ac4aee96b9b0a895521e28212e9a6624f2141e4cfef2c97e50c5b0a495e4acc4c27d32caa8e427238db8caab9629590a190b82174c69db77958f8094959a2972a67c37c6a9d902c9ff9ae479caaabb609c9611e4586093121c1cd844ee9d799b95d4272386fd3f334632aa34a48ce6cb59552df87bf68121236bba66db9efa620242485d2b70c6e9d59c7232486d1cdd6d93c9fbed20849bb1994a8e7f591a92c4252e5187b3ceb8708c9fb394675a896ca9633842495829eb28b1d2124fde914eb4d3ae85f320809b3d12dc85e35d1d60021613d3da894e436c8d5fc20f947e5ba11a74d69d5f820b12e6ae80d16e376d2f42029bec88ed174429fc6f02071a4a57c9774c62463cc0e122daa2825ac4c3a48cc8bd62d9682b434e520e1e45874139b948a493848d2af34ddf4fcea166f909c7afb662a9dccddd006891b355b949329c5e0d720612f77bbeffabfb53448ce59ccc4e8b5cc20c94505d5a192bba74c914162899f8db5a71824fdb6a739b94e1e8241f2a9cc9c936c66aaf40b92cf7f4b9d34eb4dbb20c1536bfe4d328c16246fec73919e1a359d30589020f62abc8f1891f5dd2a12e37f4c27478b9865a78a6411fdfe759e56749e8aa4ec182ecca7d2a7332a9277376c57a74e95d74e91e81ebe2ee77535ad99291274dd69287929ee95aa1449a74f28194f64a8ca9222592ddf59e8b5ec54b25124073df55d59af427644911c847f586b2514c93997caf795773c0914c9315784febbbbcbe3f81b20e847b086de73200302a296c516200bb0e21389a9c33cad099952c61b7c010484159e48f6af5c42a7953a91989741a7b2e4a2f33b2712e5f754da58de269234ae99a69841ee6f0a0708ca911fc0c1c5170a38a782b04213c9651927ac742c13c9e97b2a54a89c4c4dbfa30788d9ebf8420b341e00088415984814297f6b5953fda9a04b246f9d8cf6b96d46b36889049d4a87160b2d246d0421d073082b2a91a4b5e683584ed1635e4a24e5afc6defe7ecf519944629996521ee6d5ff11840024f1df0041bbc003cc05200f564822293db626993bc6fc4e8a4452be117a93fa1883e710249282101bcc36d4cdeb472479c79ae76b7665b71d91246ff93a69ad0b3e6e2392b2bcfac49f8c419e8c48ec30da32ece2c53db9880421734a2fe61bf25adb1b8c63470434ed004014562822f9448474932aba54502292d4d7e575fe1caf5a4424a58f9f29a78d29ad7842d24072b876c08a4324e6cdf2a343e88a9e6388242b338be9d229a1635c88c43b99b19da5a774ac42d2fe03fe1fd8f15d98cb61052112b4ace6c78659101e3488041d634a5aa184acfd2c8804cf0f1f4447bb701986a49d644520123fc35bea6ced523b024492d04aaa6379fc87e498f1a1fa3c5ec6510949bbc1df830748086e78d1c63eb4b8420b2c3ef6adf04362c5a8ac393d6f2a4e7d480a0fbd2c51bd29899c0f494a9452225d6f84ca7c0f499553cb5d53ebce8ef49018cb64d5c4f56427390f09c26d3c8ae5b1a0f38a87e4ccca1e192bc8ad94de21315f662625eab143e207e1c13f954a76725387c4d0d3dc1525f767a143c268395963c252c5abcc2131a7d9f9d2dba5d3c942d23e8003c4fe039ef06361e3e3021f363e1ef061e3a38717108844767c08767017e929f09e238b48241289945d009060851c92347bbc7842655c37c521c994878aa8656a2ac121e1732dc3e352eafddf90e449dc754c64b57a3724d606193ffd9ed08fb72171647d792599c46294d89014d6154ac6590b954e6b480ca2bf59eb3e5ebd490dc9174f2f3dba892837a521417a6c6aa9fb28a32634247f564fb10d17849de90c499d475787c6d11c663243c28bfb97ead69b90496548502564485319eb3698c8909c4f552dc831ada692c690581de2229aa1b27b490cc91a36bc7fc758a972290c09fac44fca49df3b2d8121b192deca78326e28ff0b4967dd1d2b8df04abe179254d0a42d8fce73b1ef42b2a512a1bc3c2e899c0b09a7cd74b79c8a6ee8b79024df3c56fed8eb68bf16124505bf3f6d1bd5f5672139a45f8aa9b01a133f16923e297da5f19d9398ff0ac939368a6d0cbf75f7ad90f419736177d9c9b35f85249d3dc71362939fd0792a2458cef4ff73298feafc14123d2e9d4cb12cf96c5e0a4996151fb3a92d8ef0a390fc21c34566de67d21e0a09aaf36ce7c7dec8ea9f903c9b496bdefec94a724272cca72083de3bf352131253253b4d0d13d79f0949f739a8ace761a4f94b480edeb9829a3ba9bb2b2159764f29fd36d1b52721299d650d563a4b9c8e84e48fa6f2d6cdc354fc0889a36288ca237473b48d90201afd3efb62e9921621e1eb53547bada94f9d0889957544ca660809d6ebb9ef62cace092139a797d166397f6a198484f7ce9d25bcec72070849af1e6331d67cd29b1f24f57abcd471991af641d2cec86dd1fa2617f62059bb65fb335d675778909c5e3bf67b58a8f576909837738d7af5105d078931cedf8e3ef5fae62039c72e26d933f94f390e923aa7e0ed7baa7b946e9034a242c9147e64850d924ee36becafb0d942c88a1a24fae6ed4a0d16df6d64050d124c8a69a79c97a66164c50c12456c380bfd8c5d17592183c456150d2ae7b31c2ab2220609e7a57dd3d4659a0fb20206899f82761f95730a4283ac7841524a32cfec59a62c1a64850b1284f6b724aa458aa5202b5a90f862bf3ba2c4c7c790152c48d2b93655f896af584800abd8539e53fd0aa922b18434e9295a44ac291589416bac0deff2573f2a92b4f79eaed1eb1dfb291253fbf53573532475c7b9eb5a121eef522477abbaa82d1d744e9322e1d3dae2977e2d937b14492e22daaf671bb46d5124a9d5a7d0b90e45e2e9b0bb939fd5a4daa04812729643965042d9d99f48d29bb152bf527930db1389419af8f9674e297aba138971af9f266447119ee644d28758da7ce7f17cd39b484a29eda75c715398d89a484aa74389cfd3cb6be94c2485b3fce8154aff7b8d89e4f5fa710dbbb494f52592e2df2e74cef2ad6a4b24dca8b1509b32fbe92b91acdd49684f7147ba881249667b722ea9791249e253b2bde441c3bc2c89a471dbce20e66299cb9148d2aa55fe29fdbcad0c89c454626389acabb4243f22f17494eeca525a4f871d91a0bdf9392baec9fd702312d544fc3f656cda0f332259335ca94e21534e0f2f22b92aa875f5ee06b9614524ac59fb75a9a6998613913473428d27d3385a64442476544da6e25a8e06b903e6973fb4c0e2e3a020230e496aebf1956a429bb864c021e9d462b45119bd224a6f489ccbddd9563659ea7024f204196e48ca271f1e3bf5065db90d4979376e3655e9745d2c39d30ac86043b295bc921fa1b2785d64ac21b95f64f251756e966342d24aa4011f366e0a32d4905ca1622ac275b7e6bac16307028e211c5ff04003c84843c2a5b84e0b4a4d6b500a490309e1e1858eb2bd42061a12b46438af0b99d3ef7c86a47e2bad1e5aa7d469a320c30c09e2fd2976082dfb6ecb90d8517436fbbc66397b6448d84e19214f0651f56a9d20630c096e17639596e7b9cd90b4341b1f957c48008b8f2cb60039810c312468dbb09ef42915a39632c290a05ad40661a64bec8238824864419020030c5f4898cf6b9d2f256155b18323b802c8f042829acc35ffcb2cb153489a0e414617d299477db23124ed4e0632b8b0fecbcccdf5ea02195b482cfdc9b4634a57aec19034906f820c2d948c2c94b2f5abe8b45752fed42cb60001400e646021312c63e7ee988485e884a495c49320e30a6b8e1b64778a69f385a4fd0e1d2039dc58dd0a645821f14c45533243fd05654722914824725676699051853d77ca5ab13aaaa5ee8ac14e5467d827adac391b1f36f4f8400615926dc745b55f8a135b4f21c9f268f9ab2ccbe9974252af78086da23a834a461492f3e9f4f9423dc8cb18a4c77fa0c75f1189ec0e644021d1a27c75f6d8d02fda2724c94a5add6d4d7735c60949992ad6a9b4fead7c13122cb5a99171cd91291392475f3e2194c5969074b3264f4366b4af78a184c4f2115a47a9d5dab64948f8dee0b1cb93ea6e90909c35749df55950a2324748109e529fb9dd9cf4abcc40861112630a96299d8cbb97531112d6dcdfb26555ac58224292989c98daec595ffb1012f37beed2a24db33c4808c9e71e573c75f0f5980e42a28a2995cf74b659110321d172446d998a7f902093e638f517c592cefa2059364707ffb3b87b393d486c3ffd9744e44d8a4880f63abed06207060c063278907c7a749215eb1099e91d248ecd5ec5128d7d2624430749e549ee997fcaa70d472291c8ee909183049577a7d72d28517fc241c2f7789f18dd603e9f1b248f0e9e4b68bbd820d9a478bd5f8c18ab5c0d12b6b26c978ca3ad7e1e2091c8bbc92cb60021810c1a2485c71a7d9d2a7b076d165b807840c60c12748efe33a7e232481895f6efc35d4b331b83e48e959577ef62e729810c182428b1cf95f77e224e7f41920ed7d571597537d31d90e182c4b46fb37cea4407437a7041026b192d48ea8ba3945063e19b4e32589098e2ee8c86c78bcbd12a12536f5e5b3d61579b7d05b430554582b8ca5815d6c2adc3a94812d2c3299d5355396ba8484e2944cfed75dcee4f919441a7cfa8be0f533aa648b61f9556bce276435b8a84df2d0f9afd4bc7a563902231ed6ad0aa72f7b1330488e02bb01a63148962a3d492967b98fc185124d6c90e9522963eef1d928627462812743c26d54b9b0b1950248dfcb74bd32986d014e40338627ca214254e43e5361187a41d4f74820431389198f459523a99eb6efb9b48506d153a6ef6b64db22692dd3f7f529edc4c24c5f6c75341638b102a26923bce7c8a33ddd9ba5c22c92dba558eb92bb9e92c91b0c1c3c47fbab2cb2c24ed8b1d3dcc74c7f7e002635422592c3eb57a558a6717f522062592b6b2b54ce78ff3f2e8f13c78200073120715d76bf552caa737043124c1d6e7f5a0524c35670deee8a12346248c69ef635d26b9c1c41e03129a250fde9eaa317d623c22c1bb4a084fca2e67d82c80021f6780188e48dc191d3b2fc598d37b2169e3e38a372012897c688185d90f6234629dd19bb289d97c311b831109a3dc66bdbf37b8e6622c22413fe9acc93c6c87ff2611431189f6322aefcdd68e25212462242231e5f9aebc5c47cb551131109198efde82fa7499bb8f0122c62112334468919df4bf5529242d3fb4b8e2e36f78037044202f8b2d40024086188648b2978b1f65d6e13b7f861885483679e2834cf94975a643d22a1089ec781e63884188a42463e944530e3a056119880d225174ffdc47fba4188248b08a8f29e8c5921733c73a10231089a552acb867a9410c40682f1e33c58bbf43d248c03b1e99187f48d00e55ae56e9346d3e24adec438b2bcec920861f9246b606cdf1ff3db512f0a105161fdc831570b810a30f8949698977795ed1a9a285187c48b24ec1f28e9e52bd5524620662e5b2d80204005788b187e420f2378ee769e91163e82139df7d2a61c1c59387b13422461e92621ccd66629f73e5080f09a253651b317ea6c3e80ec9e9314c94e98eebbbed90943f27edaef650cd751d923786ef52df613a24bcbc8f860afe9a7bce81b5d8e017345c057d2412899c81989a952cb60001802762c821a9e3eb43e7521c924b45644fedc614730687c494d35cfc5097aa93de90202a98568fd1c22f7ae8e822c111810f1b1f8e838b077cb1e3bd98c0878d8f0f1b1f23884478bc8e2f7a0c22861b123b5d760d4aa9515bd23624e85c931f57c9b2a6d4b221395f68964ae93fdac715440831d6901494de18cf31c520cb53a210430d09a7beab9e9ad7648d4240de045cf0484352eeaa323d661be2b45088818624db9c2ce7cedbdff93f43c296c8dc369fd333e966487ccdff4c1f6fe3622e030140136294214925691efcd294494178f4c0c167ec6c2006191294bfc9abd4b3a04b1d92b638628c2125539e16195378e3064722da238618123e495f3df91d192b179276628421e94aa71837c858db9e1c7923061892530e1bba69a74f5c3e24f13eb4b8c218cb620b1015c4f842d2ab75ca56665ebae237760831bc90d451f3530cbbd183287521498de8b1d5a0662efc5c48d4aff82ed9d51d8d6f214198577e8a95f9378bb49028aa424d74bcbbf9370b09f24fd46e9daa4aba6221e943897938bf5c9acb2b2475eb67b88d39c8868d159244878b23b4e4c449b10a492a67d7df6f490bb15021297d7c3abb14f3c8cd4d21314673306d2f3fb53b861492c6b6e32e962c0a49ea2986b3f1cc2017141236a9eb78daa257cd7fc21d35c9ded67cd4dbba88e184e4334fd2abba4aba5b423212e11189a09a1d3d747020461312cc37cbc6790f711b9990f8a9a40875299408bf428010630989ebd7aa96db0d2e42803e88a18424a163904968dcb01ed3e791848453167a39b65e558f8404a1b264af259583cce908495aae4bbb36e5cfa61192d3becd7da877b6d417213905956a614622689a74eb88a587282506620c21396f30b124eee4e82a8590347aad627f3008c99965f3d7f8a526ed0021c167cc3fc7d99357152f74fc20d1bf3533bf443e48eacb0d1da24ad55fb4074932dde4c6645d7af81f283d88c183a4b2bff29316eb3abf8384d323d7522ead3e99749068997450eaf2bea1fb1c246936b72842f9ad577090204a7f122136ad8daa6f90e8d7a6dfa6f267ceac0d92635a2e554d31b3b73548527a964ebf7a72bb940649624288f08a4957e7cf0c9247e869b4b49ddd622383641122f45c4c423b9db5a3475d1189442266478e1ebc45881183e494d48679ddc8c9a08741920c953e9310b9717e7f4192cdaa5ba5f8599b6717247f8eb18ffe7b29a9589d250362b420a9c3829219f31fa7372169761688c182e4a03193a76a7c6dda0bb50f2dae8844de23912eca66b10508006c00631549caf406958325594d5e48dad95d5145520c1b136dd1d62b038c5424c6baa096ffd6f531848ae42cd61a4c9dc8b849c7e37b7cd1cbc32c8b2d4022304e9160e9a468146162bd02b9718341f63eb4b8e283c78d1b088844788420478e10094422918899e2507b27638c9961643dc02845e2afe88e1e43683395d58901062912947bd2125a39073ba50f1b1f5a60f111310331a30518a348f64afa2ec69ed2edab2892e2e952af3d719be2ef0618a148165bbd64eaeea3390b49cbd10324123103b13a33c000457292dd167eb9825d7643d244f03c3e91f4b2a2f2c59cf2d48743d2747017208ee33381e18924ed9564f0b1533a2f0b49c391e3460e04d289fc2e7d4cacce02290117e90c274c9a84ffc7cea1623abee80112827f1d190263138951532839b9b178537f81834f04606822c174cea961e365de4d960bc0c8447248cb0edef29bb26993050c4c24acc6d6da4df227375f22b1e7ffb4969629952d1fc091e3864e0086259252cc245b6fff3b6a2724ad12c916e2bde1d13fd6867716804189c4a029cc59759cdd749a44d29c0c39dd34eac208e14908011892482eb9d9f2bcb44c99b40b2ed07b24922a4fa7eca931c39e1e9296723cc88e1c90f8f23d9aea60a990b4127cc102c62392c67256d9cf3199e388c22e6393a6ff2169660ab073008c4624e7fa8b72954773dc2824ad24e6fd0c0c4624659dbda815df3c9a5cf0e0620466c7f3d8c11220008c45248cecd20f597379d55544b266d0cb9694ccd39e4d44a25765255fba0c640c34b490389b9ea4acc9decd310b895e9f736e6ab30834b090705aa4964cf1275e63481a0fcd038d2b24db6e6727b5ec20220b493bf6a1c5153dfe8ab3c2a1ca4be9fd945b77b3071a55d8949e93e1f36b44e5692fa0418584f17c7d7ee132a5cad9f828097cd8f8a8087cd8f8a82c3e6c7c14043e6c7c14161f363eea011f12c0e283c614126c64760ca62f888c14122b79f89439979b6e2b23684421417fb018751b4a897a43d272470f1d5cd0804272ace66c257e17c4a54f484ab14eb3599af56817485a82861312e7a2e7e5703a98855e12349a907432e6946515db92a9e431582e12349890286a94ecbe0c253534a3b184e4de28a6bbccb2059f43d20a0d25246e093d5db2d5f3b7a311349290f06759630ca3a3c83c4242d25bba0a212f692ef1ef11681c2149f4c4d6d85d52abee1a818611925c64ee2cafa13399b80889a2e49b66a98e69849d55a04184642d753961fad356cf212475df8bbeffb8eed7508186109284fb966ecf3d08c9222dc77e110d1012b7dac36c7f4ce79a3a3f48eeb7f1ee9a51963c46c3078915f4e9ec1662214e1e5e380848177a4da307491a3afe77b6fb8efe19101a3c48928b31731e4bd963ee17a0b18384d3d9da2f97455f2aad1168e8202944edcbc9281089fc8dbe001081460e9233697adc98d945ade6c3868d0f6fc0878d8f48a49081060e12b793bccccf9d9e5396bb038d1b24478f93f55435259440ce7044c00211d0020b36d0b0813d7739e9fe8fada690b47210f8b081810f2db0f828e56c0d68d420f12ea664a926d34c551a2429d369fb84d87e40630609f22be328e5393d78ce073464909439658cafbc52a9550c92a3583aa1162e0a7cd8f8a8097c48008b8fbca1a30b2e763c0e1c5c1c25d08041c2e595cfb135b22a684da0f182e4ab7a75ddfb58614e172459505d4ab7eb767b121368b42051357a0c672a8dd506d16041b2e99a6fcf9a625a6815c95ba1944a327809575b1549272eef661c99f308995424e7e79b699d6c0bb584a4e50466a0224929754a7de35e34972430e314095a721bbf512c37f7a648b07c1bd3d539938afd952229cff6655fd59f976a1e308314497fea77ac72ce1002c9e13b8aa4e06a156436796d5d525124ac7b7fe84e8dd1608e1c3a407278862231650f36e2c3858b3a1bcc004592ce7f174deb668adb84a4290d667c22b9e4dabd06d96226a39034901c7e6430c3134955967539429fa628091ec7efe844520e0b1754eadd92bd85a47d6162308313496a32dedc5b4adbd82612f62ad9a59cf3e81b2487230c666822d14a93fa89d8ac56f290342f3291f8e1df9256d3d13326923c9f0a9b96e3bae71289f33aa67ea753fe7521692039fc5c30c312092a99184fa9520e3a3d9548bc50b3e17e955a5c0e49d30192c3ad05332891a4b6ac5296e9e59b3624ed6f341b0b664c22e16aa388c9399d636ccd304312096ef963e62df19d5f8c44221110d3199148ce675f42861191a23121696f8224c10c48248a5a8836f7d98c5686a49d11cc784452cc5a28f15bd5a9bf90b42a2798e188c4127a47e6ad8b4e1a0b49fb1bcc03c4404280b8e851b6e3439063c717331a91a473925d4d7953379d1149dd2273286171943ad12212b6ae343c28bb905e86a4993d6a80b1198a48cea235a6101d6ce7737ae0e0a28b4424567a865aec68b983638717bd17988188240b4bfee14d7d0ce286a4150f661c22e1cd825e8e3fb5a242e0cc3084193ef6b3e870d6c9425c1ae42b2c736bda94a285198448f678fa84b668f2da4637ba47032291482412b9620108c28c41a06e2fa58a921ae40f49f363382c509629982188e49c6df47d55da0fba0f4973636b811981483e37d37fd9964999199236821b39be40aa0510892194ee24426d2b7a5fece8a15fecd0c28a1466fc21d992e6a4b34187db759230c30fc9de371e97a63e2467d56b562aedfd69c487a4fce9b9fcff524de6ac1166ec21b984c99d13a69485e9f490982fb86dc6aa320fc9d1cb6eb35f140f49f76516933a2ff996df213969c9eb1237ca72ba94812c61861d92ce532c7ca63e211e05cca84382d09a9e848b1eab14ccc00c3a3442a886ea1263ff86a49d197348ca3146b6da9a4add3a0233e49018ae3246bfc3452e1b92a61f981187c49b51a9d2834e42680e480e3f26980187c4f2ca74357af45cf043d278600966bca1dc9060a3b51eaf3742e6e2468f1c38ee4630a30d49a2222e68d2bf502a6576f4d0d181196c48d00d72a5b4e51c3dbf630d89a636b7fea67c3766a82169332813d5f3def47c481a17652098918644d39d5410b539c59423c20c34249c8914117a154ea45198718604a5949f909b66d7926e86e450fdf1a37cb2f338cb90982bebbd97c7e89f62a5cc2043c2967f72f7f92474e72169261261c28c31247807ab7c42b73a72ec504024a223c70e6fc0073e8b48a40b2e22911962481a556e32ab2a7ede38e4438b2bfc06175ed88c3024a56813611ba488b6e8d9058e036680814ff71973fbb4f885a47d2131b45a37fd64cce6a09d20667821d9de34468fbd9829cc0cf8b0f171c5878d8f057cd8f8483e6c7c28e0c3866a1712df55b37d2ee175f1c485e40c63dfa3c22c4fe52d24074f97325632c718a50a66682195533e939eee2c24872719e3422c735bc54272cea083da50a269b7bc42d288673eb597be82d2592141254f3199ce28329f6c46152c6dcbcac12aa714538a29337abb5db80abb9bdd871657981dcca042e275d8b8fe297ad2610a49d30f2dae78cf61a3c757608b0733a6907023cde4854ababa5433a490a0424e35a6be13261714cc884282caf699f1a2af74b341980185c4dc0e6db1b17be38d16c44ece7842b2e612535a1ff27e2f663821d13bac64ba794dd567485a08c2c13b1a0407171ff83efbd0e28a1e3972ece8827f478f0a3c173bb488442299c516202298d18404fd14d53ef8dac50c26247a5e66f7d0bfcc29c7c58c25247710d272c47f97927266b105089aa184a48b8d717ab3b369f9242468bdbcaf9637c9c51a0949d94b7e974cfff1527c84c4bc0a1115ec354282289967eac13795a908091f1efe59de7acf2742c207a5d288fcf3b9740889db5f32db161442921abd993cc53008c9d984b2acefcc2c2a0442b29ad4bad8a23bf45e7e90947d74ce6ce7830411f61b36ae5d4dd983a468af266ee37fd618e341621a9d95ffa41d245d0757b3b00e12f36ac7da17d5d9aa34302307092ebb9d9be637741a0749aa42abbe798ebf79832475a35ab529c472f536488ccd25a754cca43a5a8324cbd96629e5d329e89c0649f23c9857b438911dcd20c1d2a767c5b93291b30c92dacef2c566f37ca118248ada1cfebeb3754e2e0c92d2cdb68592ea8c1724ff5d0e4aea65190fca192e48b0be98e4e34f7b8e39335a9024449f9299cbbcff9a192c48929e4dcfaee550e16356919851835ab098d12d8eaa482c0fb23394be97853115c9a54c73ab2ee7e0a2828ae44e753bcb683945f2eda6ef4ebd1a231653247ad850ba44673be54a2992c4fe85feaae829f72345528a51bc2ca98bf182378a24bddedc175d1489aeae5d9a39e6dcb68522290919458d322b191b1449a133a90b1b663f69ea13892e4a6492a9844c0d9d271233c98a5529c46d0c071720df83cd3ae18e8c5bbbbf6d23f300199c484c6225f32bfe4d24fc893f8ddfd5ca18d544926a6829b9f031648c3391b4de1aef73867e3e0f2612b3f87525e9e125123c8c341d4b755f9880b130698924fd9a2afae6dc257141ea3eb4b822f163614a0f199548dabb78a1740ec2372d831249325cfd7ab4289d278724c88e1e3acafec89884513b6ae524aeaeea0265e0ce17322491249355eba5cc610664442231b9464d33ea9a269442229148a4b4900189c4baad6fbf8b3d22d9b5938bfbbfa698b32312bbf38d1c6fddcc1c6b44f29dc69eb55bcaaa970c46249575adddc808f73c1791d44977c43fba7790110d3214919cdbaa3e45116b639f0f2db0f88844400c2f11096a46d59a9bdc30eb87a4b13f20758848cca2d4c7ced70de1fd19641c2241460979d1b796b4980c91fc22facc76f363aa6cae1009af2983c5c8fc241a2112bcfd457e7aa5eeb866f161e3069194feaf2bc7286b59418620922e43fe76f4fbc2d74024f66c12b929850191d4374249b1ddb4c1ae48e48c20123923b8fd4352b81513d9799f5310fd9058fae64c8a8f506d497d484ae253e8cf4c69367632f890ecd1aceb3d7decda7e0f8643861e923cb7df8285f2542ac6a3040c810f1b5ffc03743004b2d0c15de0301979483669aae7c3b7621e914824823c7070c15f788e04a408f0684024d2052a810c3c2466cd2932edad9bb22c023bbe871711f8d0028b0f2c1490228064907187e4fa77ebdcf39ea4694d1764d82179bff336af4ce73ca3312dc8a843e227b3f27e95d3ef7ee1820c3a24c5e4f7415fcddb624c1664cc213176ff2b898ed5fe17392499b4f95471330670180017fecb5aa954ea86016c21194c56308da9db42e55a70e35b43c6187e1747223abee861d830802c245594ad9cf1792c24970c2663da89e7ebfc0a49514673bf7b66be85ac90709a7f76b2765afea942825bc88fc98390ddd95321e134d8989c174d21b14bb3697a897ebda414924d88aad51f1b39134621b1e3668f8aa9ce190ac9b9726955b8acdefe13124c8cba53db4d7dba1312e54c7c3d868a418fd784c44e7284d2cb1bf59b094952d4fcd5aca84a295b42528c8b8f97f22b21795b523ca82b19932167470f1d2618401212538db2f1ca9befb208098929ff097d1d439bab7b84e4719bb93fb9243c3542b269cd9d64ee8d29a61621795c4e567f5eb6ae9308097e9727674b865bbd0c21d18412b1fde661ac3a21a4d2ceef6d3b6cad20247d30193b664148130261004048562b21635e0f9d9bcd01fc20f1bd3d7dcee3bba8a70703f04172a6e6f75d3839ed2f9930801e24be7a4ada1ae36691a990b4828401f020d94bcda57f9eea5cff0e0e0d257536eb89c83330001d249a3a1d742fc8dbdbe8e02eca007250b08cafa61f2a963ae1201db467f9a47446e9fc0dd813793a98fadab80deadf34631a5ed942a8067b30153fdc69afdc300d0e739d3aba8232f9f10c8a1f453bc9780d23b4039041418bf9f8dd55d93b8018a433851f958581495beeea144f78d2ed0b0c66a56329753a6c5517186ede42866d533a2d48b8a8111fc4cac33a0016f8f1f7d28cd00bb999551c36585c853cfd5115e69a13fafea5c25c2a79a891a1a228a7d2a8fc3cbdb93a056b2a67130f0b993445828a31d7bcaf45c5684bd1767c85eb3fab8c14099b5bcba2b58c9e7fa370e6eb3eb62c840cd71045b2dcdf6ccef1b576ad1ddf830bb3bfd12b861aa148f4cd5dd61fee2c37098ac4acd92d2732e6ef793e91242ef365ce92a1349e48b6107ab3d426b1fd7d27923dcb59a9a034e61c744e24d7754a498c369942bd89c43442e8d724b65392a92692cedbdab4726bff66870a353291207ef76222a763ce87807b5c710ff8b0f1612b851a9840869b9da97cd4d225924525f978ed32b7fd5b02954f5e866bcb551c5522c92e3b3fe78c7ad4a004236e3bca6b335fca933865b95b57cab0efa2243ef1b349c5d8a1ae48a05b5314a1c3a75421519041e9cebeabe5a87a3ab88bb21cd4784452f08a5db1f98bb95c1d0c81bfd15ee450c0b182450d4724e7d49df2595cd228eec80162b7811a8d484e95b3b8a9f775ac54a00623f8fc0df1a431af6e42d2bef81080e860bb321ddc4529418d4530fab2d3c466cc40729cba1a8a48ce6059c572c9ead9bc711ccc800884f4e002039148952cd448445210e6a2b973ceb6dd35109130da32a551669e2975882fbbfa8aec3c327a84234716050c350c91b01ef3945026739a172db0f888448c50a310c9765254d8d0d469e552b0062112337d8c9752a325a11e3972ec1844a2478b16d325d1f84ac261763504919c59abbed762928f7319c142a146201284afd6767cbb54fabe2fd40044e2e7c80eba5f22734b5da8f1871a7e485041940aa77b653af49034335d408d3e24e83d5777fd9e0f49d22d3729d5b3d032f790d89db4f5938ea2126ae821b152bde6d17ba533e47948aaa4a62e7b41a8cfa80ea1061e12af46b35b903ad2e3e50e4917be2e34bed4886d764810b5b6fed5ef0e6ad421418356984b5d73214e74488e39c6bab4179499eb7348b40f179ede6c3f942a87a44d42838e5015ae1d8c43929ae94c5f55beaa3138a8018744379d6ff684eea04c266b50e30d0916cab4685c891ddf0dc99934c510bb516c64ad831a6d48ee1f351f3bb3fda5320a35d8909c74defdac9b2bbba5ac2131e5e866e8dc1999a61a9253e8dda421e1b3eb4a776dfe90d190a0ddbd95c1c488967e8604f9a1a2698b69ffe1364392ccad2ed3ea79c173ca9094fbaa3776eecaaa890c49619a6468a98fd39a1d4392d26159f53b848e2bc5909c254ce797ab4fe66718925774d0b1650386c41759ba2a9d1a13b3f9429236f1fa58ea72b4d2bc906073a5c305994b23b52e2408a1c39fac8f594468b890b827bb41a9cd1612fed2849a90d79e64d442828e77ea84fa11a15dc942b298284b11f116fe332c24655d9296332c4da5dc151264e54c3a088fc9574d2b24a5911e9f528b67279d2a2448739359d5727869900a095a66e6559be55e54a690b86fe15ca46e76084f0ac931549ba95259a14d1a85a475abd8b55da190f06e632a9bc7176db127247b5fc69648cdc154e38404f5e172b9fe0611a969c29ff1b64125128944eeece8a1a3a4a0061312845d99071935335bbb418d25248e90d164f8f12c4a4a484c6e4a4bbc4871bf38098917ef496b89aca6b320212993c71bfda6cbe4a747481acb7556ea9f726c23249ebde510a73bfbcf5b84649169f496a8933a1f2442721293a2e4f209cd5d1942e2ce8a68f470d9dd8d109254b5c9504275caa5ae2024c954ded6a3ee7e39034252de303556d23e8bcafd2029e6c609cfb9a563c93e48dc14b4a974f9dcf6f33d4850eee25ea65b6749850749e365e2ab37b383e4b31b8d166e1656733a48badc64d237c8ec24333948107e5ba542c53848dacdbef9983b374812b3b931e3558c5d521b24dfe5b9dc1c27d4a841b29be9756bbb29b59a060926f3930e9d9387fe76060963f964ae538baeeb9141b2988cf58d956947b431483061214b6c954a1e736ac020b13df6bb05539df5f11a2f48ca14d7d53c8852a73c355c905c42f594dcce76d1548d16249985a8d70b335722afc18204ef4cd3d06c3122e755248a7ccb4e9e1b4766a68a84cd74397f3afda8234c45929fce4cdd2963e6cb50911cff39db16a4b7c7d829923707f3504945a648debcfbbe501fd15f9622c984d86713234c05d14891b45935977149890f975124c64a51398f56b936a12892aac3e5cc6aa24cfee1e9c5c7eff082478e1be8a3d38b0f10bd5024ccad9db4d3cd3ebedd08ce093440911c224c788c53b31893280391c88d2012e922c78e057c81d03e913c1ac7dd93fdfcadec89c49c9a6396f84daa9972837144e0c34627923bc36bdc4e27f75424020d4e24fcabe998dd6c2229aa8fa6ebf7303e1f4d240759a9396a07e155aa0422112fd0c844e269dc4db5f1ab714b4c24696fc5b2fce125923283c668237ae4a6c512c99e9ed47aced34cb18240a312c99d5247a96ce788d8299194fc472695119a44c25d8ae99d4efffb4c92481c3791e67174ca958b9148501d841895dccf4aa501894c7d12aa4306dd69e52392e6cb55b3887c54113b22c136a71026ecff63ca368259f712e2c27821698506231236ffaa635c994eef952d2251b4ab82fd07d56a15452478f8a6ea6e109d3f9688e4709dc2e7d1d37262cd80062292fe672f99305d3266cc2192e2a8fb18757563474324c93f69afa7c16455104f2192fa53e46b6d7912dadf811f5a5cd1c30b1d17031a8448fa1bf9b392a53b5a1a445252b37ac94678b2d00b224925a59fe71d26c2cc4024e694c52fbccc7ac78a16587c44223d10ebe84206340051ce15ccdbac53271d59593dbfd79d1493a1f1874439cbc17cde2e080d85a46dd2e386173bca0f4939b6b69312fea24dbb39a0d187c45c5fa29fbd69ea530e0e68f0213986ce49eba51c99c68336a0b187a48c75bae59d620c4a9bf86f6c0a68e821495baac8a6161325d379481053b2b6fc653c2467fb9cdb43b977485032c7a4e99256161db443f2da58aa8889ea195d489a832c0d68d421d9ad3f9afe8709af28d221d95236a1694768cf1c92429aa65042be482412c9620b9012d0904352a611be66c2c13774b433c073d8884392858d7453aa3579ce095816d080c3779e52c5c60d7aa73222a0f186c4945ae3b59ed208d95d41c30d49b9849c7db598b73246a30d891973acd09484ee546d485a2954a0c186e4334b6acdc533783685a49999800b85028d35247dfc8e231777556e4c017c81861a927295fca714a39c3a73241c5c5c0281461a1244ad72ea8dbf8814e1c8a1aa041a68484c4294c937cbb80c3b248d07173c70e4283333335b3501ec80c61912bdbc746b18533a0a628a1912654327d53bddfda613920602f25e9432051a6548aace7e298c6fb6745f481a081992323e654c73356eae86a4ed13688c21793343c8de7443d2ee9040430cc95671b5b2c80fa22c672d0e34c2901c459978efedca5d1d0e34c090e8ada393afe9bcafbf90687a336ab820653fa5bd901864e62851955a1b68742131445ea47f0c4f59e242e2c7d12f9a5b3de818b485c4984264afff4c278c3da8242ee643d2501c0a85028130100c0a9aeb9604f3130000001016920763e17848d495791400045642324a2a261c1e1e141a8a04627120180a03c2a140180c0683c2604020180e86d2d21cd47e8711bfa02112f9f428e6cebbdba83bead515e25eec74668d33d28860f9b1e8258070f9198d5aae8f2f40e0e8acdd75b9f811a710c019e2985bbedeebd58043b035b8295183acc0874315f4e1ecc50fc7538003528fd297fc8c0e741bdc3480bae1487a863106ff9556bd3018e643c27dbf012a03870c1c5b7076820304870f7c80b011133216577a137ea717d0ed00821f8f1c35f53b53d2ba2f8fedde3ea3f317d7abadf461e8247511b6018c0ea620dc185c3038bfc126f4061669f53851576c75c498fbc78952622a982a1f4db891b8323b6d14db17dd0f4f7d130723351e453f50a31b0b608261507211fbe03e8f4faf99c5887d8b465a3fcb4d6fe00ae5393883fe26d21ea0cd7670962db41f5e2ea5d40a3533b1e3e62e42d6e9d2a51c9fbca65f0185baf2e87b178f047483d792dc9add7dce28025811fb52abb1a707cebbfb4884ce139d27608e6e56e245b4e5e6471bab6a486f4152da2f112132ed288ae8ca487863fbd6776a4ad2a9243edaaf0ce66fefb5b864e30078a93aee3146c84a00f0d284a93bd61d89b432f2da0989de26d90bcdf68881aa105f0ea306c9de565c4491cbeb9245844622a501c72ceec0616b38067e210f93319797137189dead2dfe882f61c958f37faaf5080d10a361c066a56e259d5b55273e3ab849102aee39c4f79d45ce9e54e7cd27943632472c303fb45406a70d26147a67b079b4c67892f91afd50fc0e8a7fade3a424818c07f086fe560fd2128c010b5c0e796a4248dc49fab0fdb7c65a306512b140926b99d2b4d421c920323996b029d40987c5c99724b149759c53b97af4278c788e27447592e8681302fc60ffcb042c2144a9edb21ed5a17cb2cc4ff9d522472c79c8aef196abcb22c251b91e34659a5d40b15e660ba2b8e07980ee51560ca5ef81b98e814757ce38c66b50a0e289205b8f59e8f31bd15eaf06200b0f78c11ee3080fb6f74c8922f25018f9fbc065919e4b658a8e25e9a6ed044c0be6b8390e1005d29d41283d9820ba9623669ff9c63a5f1b32b2aa4d39ec24160ad3ea9096204f9ab7e0307d6958277bb3203c736cf18eb9490046afcd8191a1918291c22a0aa3fd9786b52ded6304fb547b4410ab7d0c8b074aa84791d007f15b7cb966fb35057e8a570cc02340a77b913725631b359e5e4270b72a2c5f6ebbc9daf2b32047cd61e915fa4ddb12782e3f8edac192a8196b5574d8351b95cb1856ebc6c48bc0996c86ce680cdf33f436f0c69fd1e0071a25b953d4b7b4632094785bdd5627e496a5ea47561c418ec7e0b136c6921d7e1aaed30547dee2e764369801df9de57dc7abf88376cb15fe53b4636e5fea08d300a58dd54cc514ba4e123e1004dcd3dca5259fe6fa4e630b64b26d923581044497136a4f7b153054f7e5c8107bd2b1c9133af84505b4554cefa5e2935f2503325d41a8bc03eb2821c19279435ec789d797dea125b521cd6ed9ef1ce7a7a346f9da0b162dafe39bbc366b7b8d55a46db3573288e47f6df3ba4e5e31333498492f025ea9bc4244af37d4be7d7ba35e6f5ee8bd9a7b35f06ae4d5ecd5d0ab815713af30fcf5442b37d724f6dae885969745bb5d0437b6e0c4bf6469947fa0a5c7f3d07fe446486a7e24934c5f37060662f9ffccab3f3aa27ba4043bc96da8bb8c4df25fd6abb017895fc5c0d27b25f16ae995e6d5d4abb15703af665e0d5e79bcaf279ae3ae25b9d7b617dae375241de972a1abd72202b06bee5ea6f5d54724a743ce33c27c30e2784df7bae775e5b5bbeb7ed7a6ae4d5d1bbb36bc36786df7dae6f54d5fd723813e1b929769af035eebbc4ec8eb384ba3f2181836e0f94ee5126df9c36591af40578dc46f4418c2805c9a9ccad20ed388da89dfb1c2d80fa22bf394ddb463a9ee6b85ae1aca6a419a1bef25250515dad078747f013d52888b20897a4151744bdba41f5341e0a875f690657e2410f9179426b8cd4b4366f72dfad28f3262617819e583987436c0933672a9dc64f3b10b54efada4c08a48c6a6887521c78074e5ccfe1895d15ff3d239782d7f85d009a57c8f1025230896dbbe2aab14634e6d5b03cd7d835053f73ecd2e04562170847c080c017970330f3a431f141751f7187764b5b2dd0a3e0c18424b2f17d9d9034d531413953c2f8f2965908c8d3b8c5f38eff21fad1b2bca6a8f31874567d77b901f3adedf2739a7f9e48745adc7d34e7c3cee08f1dffbd65d5577d37dc3011cb62fd0d80b87e70b4cc7b3c012ab7680b4d96eb08e3a359ee99b0216d8286be809449e55b66f0042824961a70a331c757df6b82d6852141af579f997e7e929a6a2d96bca5cd5bdaa4db57127d87d39c596cbe223352e7f67af42fb8025fd03f5bc3528e9ca0f5388f5fa83426e469ef0d5616a7db5d8a123c9b465ce44644473a1fc2712ee990be40cdaf4f35b46a5d9872125d601caa1af885f73ff8c654ffed36fae06142fc08eaa687afd88344248409d872310061a45c1a6be35213ed5e9d30ce677d42e66f24f2f62ec4b0cf71176d6c75473b81b045b1d2fe87adedc474fc31d963487bac2b91515c2eec39e1abe697c7596a1b86a8d8a30d26725671018b0d74582b11c1194828329b58d98186815693112a5aa26cc4481991aa2685933f3912e2bd11858eeb30aa1cdbfb5eccc10163d37e94295e04d97c9e68e2a295838946f8da3dc0524343890e17b734cec52b70c6b9b6f31f492c65c1d3989831bf42e2f81745bb314ce56fc557a0c75484d3a379ddc303e8656e4340909f4561c7dd5185a14dcb82daddfc8f56975b80e2d1fbaa1545e7e14d1ea59ef98efca731d15662ecbeebf401e03b06f0b7151a99f943eedb7c41a1273412797451317e32da398961b1d313e40264ead1df5c8f2e024a9a936526252a40e98906a8d41aa587dd4741e251751277a03b39849e18c9431c2e4205b86593a3bfbb122c8559de532a23811d30bb1ebf30c321fab5e1e6c3eedb4d0a92e5410691193e12db0c0b62f558c780f1e731e703094fc9fb7cef03a3ada5bbeed29c9f6440d0b15c31363063b1e3234a3daf839802fda6589901ad58a9d218980462c9fe95e3fe054ea9913eb2dc29032a693a094fa60a721ba03f1e2739806144e9048e6f08d90f8450d97479ddab9fb22df6e0514d77742621853994328634c35c4fda6843bfcba4fe751680ea214b38582e49254657c194f58a89c7e742d82f4ad643398773d0ff275a7c9edc9278bc2272a5392d59c74882549895bdf5936eb98eaa511596d12065fa876c6b122f923a6e29cbb1257ccce7ee51344225d6a8ac5d6d60cabc3a3dc891fe440c3ef739a59d48f2222d75464deec75a7b9915d6163a01b8f79027d4a538a953a4b103116f1744e7007431ccc6a013d237f04222de23952799f40fdc12e3e4a8021f032510114359e346f835e157f028fdf91eeca388de4558c1799ad8e682fb67bdbda08cca953a224bbbd9c8d5db7380a006855dd291b7561dc3819d5898e467d50f3955dc6d9f482363d48cab463d0a5a1a0fc33159a9e92d71b59fc784db73eec55270b79e80719384650b4e46fcbfd5cb0e5e763d107ef5dea5175d4d82a79430dd407a811314118962ff8c9d2ba88f1a140b5d359bb828a1411b4bdfeafd67828fdb37dc5e338cdb37513684e45629f5c975bef42b2ce62a26f572018b0205cbb8019fd7aa7773cf28369fe9f3fbe2d5e71eaeeee13e07ae1ecc72ad98f4b87c20af19cc5e1a3b07411eb6a88fc9e3103fde5091f2b1a4d7d8c9cbba5481a4cc6cc13626f57fdbb569d0b3b6ca620a5ed663dd43b081fb6cad749bf12df880a03ba0e78f4b374e20370f5dbd4f900d62f650624fa0f22fbb51ee32d3d68c2503b7d1b9d66cbfd5e7f7309b4addd4ecd9beb6b7bc0299e3ff3d6d5d269744c7e150c0f9d4f859a405e304d45aab362a191e737fd7f45f8d763555270ef466681999df24b910a503ba41f47f5c68005460f338cc11c31437ea1ffa204825c293c0376e2690711619e62acfe9a9aaea3cf45738f76e5f49546f2f12555fb649c99b24a148c969e9f22d1dd8c3e695adc087de1fd1bfbf67fbab5728a49e94414fb9a8d2c685cb1ef0ba2092fd7c7d422d88b4ca6d4057094a4d05670623843f769b2234168ff9eef4a50fa907932c0e4c0d27761ee398094251b77c18d2ea3c53dbf8e18711d1ca034c23282ebfd199c7c2e2b6e891db9d20c15d562b606093e8a4833589b37ce5fd6a69788682de3b6527a113ae44cab750fe9ad3748df6c277bc0042b8013096b877f17d30748a99b205e40b2b889ebe81bb155aa11805c550f2d26e24ad16b31e587104ac5a83b9c1d61420e3aeddb34bd520280fd00a1a013496f14d0dc5888eab677ba57cab7a853966bd835edd965b9220aa23ff0452b6435a2f397f98d313ef4de5dfb1925826d51c44f318e5fcab4fd04073ec991cae975d9ca021fa5c72b9977ead24f5065e46a0755124d401f859cfd1183920840319504076f252f098350ea5d77dd23f0f9fb895910fc8fc0d769c98120d520f618c048951e913d3eef388614643ffa247fcc2622f2c862755ffd22a01641aa0cda634fa755a6e142646fcef14d73d2b5e5db1b99660cf1040200ffe942f478235fc6908d6ea56300e136ac3989f9cf63c1114a383252097ffaaf5aadc6e5666db56c4cddd4a788f31f472c8f02957f64d638eaf2ac1e7a9342822738c22fb2ab14ba9cf82328b1fc12b8c1c6df798febbeff7dab6b2c6b9b04ce3698e458f7681bf0320dd77c76245ab1b6383d6993ca07701c515ca23601052961711f76d69a9c646b4260383750e4a485fe3f1082d6e1ca74e4284b36fa405e4248c289b37cdcf11b2aef5abc1143161687adc9582855e12341c0ad376fa55b4c6e36c2d61d8d71673779b411f37dd246a3cd7d430052ad5ec10297d330d90667df14ce7c3198b38fbc6251165b93b3406305e24637cab9b4572934e4023a06a22d07c661a2b53485375dc07485560ec062da0e7c35686ece1e0677d3d99094adc45cdb4e9e40a8b4cf4d235ec795cd811c7195c00e702edaf3a33153d8392a711b89900dddcf4eceeffa1fbe1b9141bab124107905904f1608730acc47a738297c165451aeef620a7162f478f8202fbad29ff814c5ce6fa674d739c96c2f109686194938d03b8129f7d5bba5a2524fae0b875695f3811405aaaa4c51245a5bbb193d6ed98e312784f606beb7cce6a4412ed862c19450a9ea4e4464b2cba95a3095ceaf1bd6ad7be3be5c641ce0068388cf20c7729102e148b73334cc3faf4e6cc40857360df46326f580c761bf694fb75396119fd5ddcf03dba0bf3df6807152ab7b8c9da92a6ed921137f59fca4fb2bcf8528a7d507db10b7329ebdb2ca2ba547a56460af4478d6151d6aa90e794fcc3a8280f03963e10799daf9f6e8320026be84e50f9eef877f7432a5b308164452b85da159241255983209601d36b8dc7b686b1f4a5432a9430d74d881a2f50b13cab720a471a98e4c6a9daeefbbf48c0e06446a2e2014aee9ea4a934daf17537295bf90095e33774968cb2e62106be15420323ef671d355f5447213e2c0e1b30e202f5be206befda42558dd6fc7ca8faaa6fdfb4416a5efcf58e5551e82ccb507d8cb2dd8174f14d0fe6b62add66b4cbd7863bb9ebbeb82e839028147c381ba0217c1be6a09ae0beb38ebf730b730299590c0d232a21abf315e5d069340a1e0bbeeefb1eee09d8454f6fa69319cd16562b33b23449b63449d8f55a853f27d8e851a8738e1aa8490dac607c60d33f6993401a0a93eeca3af6b21e9638c241769e919b554a99a2e29f75a94e7a8cb7bfee8663a477fa9d21a0df0ce3662ec1b8967353c685315ea5f599258f3c648ceb8301528e2c0528196899ca2a4a3a8d9b6b3867ee437f7c1ed11ede35a1197bfa906a86a0ab89c555428664f8710d3bda35adaea98f70c36be3f297ad09a5ad31986bd89ba6c5baaf8b5f4f34d017defa327c030bd5f6dc9423d0907ce049b9be9a5b90192d1d8c40026dc17795fa2b2389a530a49b9cfb66e44e173b7e4924a9d3fb3e869b354cb9248042abba8ddc4af9b7b2f7d65bfaf9ec645c4e33efc38edd1428afd5d8088e93bc72de8d205de454f5d8e3de77113ea7af725068e308bebf82136b193f1e46720407a2bf7f31e072ca52a72eb18a540115f1db0a0b024731f8d05d23c3b5898b24d5cd421a598828472d4f3b7cd61d20d0848d49cc2e215dc13214629e3f3a50a4c4a0241c1bfaacd3c0cea11d2d84d98c6ed16c9b2e9094050fbe1b854d9a461840f782ca505043f817d5c2250e703198212bff0d081cc4e81070f30e7765cd5ea04588ed02d2baaba9b4d335352ffb8f73e6a148216e4584ad03180bf96cde48168a34c9397445dd9566bad4d5e6ee5fbd5769ba420caddff14427fdf71dce8a46edbace525b6c91b6dc4db8ce9280d93747883acb365062f51e6ed87e0a9313d0dadc6bda4be55c753c700136dacf8d04b2a4551d968f3bdd4eca1c76a68a39d74fa06684bc6e98e48508b4f395f1f878c1e0583d8f2f1ce721bdf12ccf96466b0806989acca2a56080f01f63690267f07f56235a3d62a3303e6fd54d26fa6999f25c12a476130b8b3e1b29813e0d4d24f5c399718854c1aaceef4c307d0651a05a6c272d2ac9ad14b4b185f650df4ef114c0ce8604f910f7e527d9cb7e3f1845293e4342295f410c1e35e049b09690865d76bb40f6591a901c813037a55b45742f8d9c9a5f90666b909be043c0c91575460f8853e68b47e63f51cd9081ecda79c25d0b84d0ed1a9175431693626fe8d06fa7572b0ecc9edd47222b27e157e6309d1c7897928f69833cfe8b7c4821db5766cad5c188ceeb80e532f6d1939b450ceec08ec041280b861346966ff13ea568847e688fc455e2454a0f6e02eb23a6b95ac04636f80bc86e9de1996da700fd3808c59eb02236bbc34dc6e51a3727b45060f79530d3b76afd60c6bbc2526a8d8d2aa836c8225b407e40f95868b56a9fa13d38a1b341f22e58abb5511528059e9761c5c6ab9fe63cd3055bf981cd25bd6940e125a6efe9abe34a552d7e58f8f406b948f963b1cb612b8c21e9d8097063fd53ea36ac9777360b347050c24d75cdef5ae69c995737e4e0542f546d399c838f8cc287d3ee164a67cb717821696d2308a0ce62c320dc8828a0397f7d3754998c8410ea37b9a0a6f82952c029b767736307262dff20cc6909f09259451f355227bf96924518d3cdb4c89147363dfc7abc74c28f6a50c15090f26b27d4a6ee60b759910a8cc82c272aa3c959ccd1c95e9a543912e8aa4c4dd469139402ef31c583a3179baaeae44978dfbcfeca37ae3568260d6242ded79f00bf50e3fcb11381a7ea61fcd34cfc7148c31f1902d23bee6c8092a61c92415cde24d1a330f1c13b491500271a6aa8b79ec6b4e443f20203c4d175affecc1bf29c92ccc06aed21557a37bdf785c25d7fc03f6eee454987561f02d71bb98a7981f868136c73bca60b6376e8789b9c1065e481e3652b51805fd8b4dd6e2ecc99d50d508f9af32d4a55531f2706c8becf3567126fdb83db7fc28ee9cbdb790f0d87929ea689f1c7a32c622ba78aefeb446e005971640d248d4ea418c27c14c3ff78db05f5053e85658d2bd24c9e3ab5a1816c508244da3701bcc139fd4fb8be13b50964a4666cf5a22b98e05c2bcab00f7c68e9debdd638db466bf7a9349472a2e91ba2c047a3e84540968bce209afa11c79a69814bf47b798ec8c7d64a95909ebb2bc69e1763a3ba7effe74fdd9a572bdcd127b41eaa2a9bc4a7127c767ce699d30a62324d370a9c15a2d6e4e4c7c0533f020adb3aed659c77dd28c528fa51f810ea0af89ddbafd7c0d4a96d00f18a306e2e92161ccb9cc5de6707355f3b9796e8af53e8d85f9be891de7827c024424680cf234220d52604eab982396854e9eea57100ab470268ac803cdc96f9a74c293a85ce01fd7bad9959d4f2fad381d309b537dbaa062ee7a9aa2f49951cac2ee20d35927fbb190945ed484a8d4f361270f3233a60ea2c993ec150010d76da1c180af03dbc4ad7bf6de02c6008ad1181a634b647cf78dcae98c1a8a5933c9c983531cfab4f85965e57a7d89bb278b4db1c2edec071c595c6098d8515248ce34337f9be637cc5ce075ebf1828060b49821deef52c33500dfe88948f16b1525219093f42bad306f4c3d2659b47dffee94e9501dfd0a1d821f2b7f98870d98633b1039ffabbacbca0fe657c51b03e58806031c2c558898f392f92597fd611bbb931f3f563c0ba808d4770baf1c8a23cb23265a3c4bb25b46a703a78e8c8ec4e6ac87d0d7799924b7fb1bf95f69e4aea5425e4365ce0dc647bdfbc53161165e34edd28fd7f4e561381c5a7ac0c4963d9895c1dccab5467c5cc34c8b0ce5ef1f44bc869fbc83bc4207540548261ce628a11752850e9c81abbfb2c862ed90aa02c5be292dbd53d7c4941db7d419ae87006ddadbd809669531e2b7a236bd53836d3d6cd3652e97389651772e50dfbf7e02f14843c29c40be501aa6cb80abd3a5f68c24f86b01054a4ed83c03388ce66f13992016ad6e4110d2105e8285df323652da9132c549b4c395e686c4d4e7a187ee2bc48511b57b9736df805fce2e846fef32eb41145a650fa0641ff91eb6f6257d7b5fb670a419ad23026aa1e0913ed655d41069000902aeaf96eb1009b30ec595b58f3e0203ad0e3c4636a290743bc05697d185e4510f27f1cd840fca61bbb1d7c5d8e35e1ff1a2763eacef9af3684c1dc0f651ab0ca675a5ca730cd5b4ba8bcfdebe60e9fd5e3b2d3483e1748ae4619a327a99ee470ddf619f72ae0c78a63b2cdf70e2e85300940a22df9c68cd86b94c1c373cd96a09ace0529fb664db6a18429666c00cff7d93397a81be3a7f6b174de297291554296f54647b0a3f5eb3f73b2350f58f23560c3c5c1594f417a53b997d2c2147b05dd967424dbba657544ddea3cdc6a028d6becef57d557c4363b01566947f397669d52bebdba860dfa090c405d74793126fb70706eb483bf1a11aa34f9a7048da48ad4096098885b9ee175d6f7780322ac91cf0c811b3da6e05061499f6edf0c89cdb69dc0439c3ac46df6b1ba0193c8ed1d837891875b31f9743712bbba5ded65acef52f23ee86a4724046f18c0218053363eed13aa00064ac0ca362a07aecfa2587999e9a8dad738f56539c92ca14aa2b9bdbc97d2c8c1913052eaf2253655a51700c0e06342668e7786263c6387a4da85e0ffc96baddb4687e09eaa10baaece191a0ac95346b7bfbf52f1527eb8184c6c963534db115ee538ce2668501353b507a3a8368109e6171b4c2a0ff1597ef281bd623d9ac4e8cbbdd601d3ac6361e528932461d7143988fc2183ac30b00e9552594e0235879037312ff7658712c71eae96e8f63194e18040520285b5985dd13f0cfdf652123febe22745677b9c58a5e200704c7939bde60e37cb538f2a846c8e64dff2062cb8869ff244abd04218706b8215204cfa7d0d48647429417d5e76cd3ddb50022a4f2e85bb6f161e9c7096b2b43ddb98934013dc842c0d5c1d4cb94e8516af8d0457b2d04d8f525697227ea07ae9d8d8b75b1b9bb06fd06020d441808313069606460d6c0b081280c362f33287f2967dbc1e2778125071b3ad591f4f486650335de73e967b10d9b89cdd65165e2368edeaccf274e06412abe282965b91629ed95aa51535eb7a28b85f060433f7e86cc5fc1bd9ca504cb4bf78fb9cf1cd3da9d80fd032c05dc8d008ce4000ed13720ef54ed2f3eedbd70dc87c10001842dc1ca8424e00093296d182941ddd23a91cd1000f71280694122cffb36495085885c44ad446588fc44d44a6486c80e91160810296993a89b584f168de2d237a701910a3263a1a6a41b8c8280850b1a970798ad280337210760f42c033b42bee1f4dbda81af8e8e64322745eb6903376cee30fd3031d881883273f5303581d43c6b7f7a28e0c64e4f92b31373f985f8b26102c648bde3fe7c3c1d9ad45b556676026e69f18f8fb9002274eb1516b18b27b6e442feaa694b4f3ff52775d81c18713a59751a3728d3cf3aec15c80840e993b74f25521427b683f08cf1a2a8e30cedb38fa4edf14728c480baee8cad3b57bfc120999f0c4578a35e7e38a8f98466f98625d04958d6d3b68b07bd652ea75a84bfda7d0d31021ac196fe0af988c809d4e64d5b500a5a87282501965c9827b57b8005eb28b7c96a92783ea3d3971f581b37322d69910a8a582a8d50cad0b922ac4932e07ea82a16352bc7e4458168fdd17c22152dc5b07cf423c7b729bdb5bce47faeed0e5ec70833c7be19de42457db7a1358c8693be362a6d8e87016a4d828b4d455ec2cddccde2dc8739e892d3d5bdb16a420c6ac83b8274ab4f6499b31b4f89f18f891c3aed015c2786515af29a8d86fa5623686ebdd159ca92cf5c86f6734af45439a5857482b1227164c18f64f532b1e70065ec00f4cfb82d7469ff93b1862004a313e61285471347e1ec2b9cda3def4208cf905ac66b58fe9ac8c479f6965eb772ecbcda2f9e4089183fd74e11a04206e883b21017f972db7d82247a66b6d4ba5ba545f152d44fbcf98e95b999aad592438aa91213c617608aa20a157027c640316a5ebe7b4d3e5688a499fe3932232acd421f6a326d302944ff9ecdb924abe3ce910e5534e0817cb2ba78e5eacaace45e2e2a354db2c2afb9b39db80c4e64b7c3f512ec547a43bf602fa4aba25879b67d07a87e2a3d4d4f3b1ff75554c339ac8ad32ea5f75f0614f9008e99359315519631cf075435de564651fd2e9c31763280da110129257491cca783bb7e8515678099345d80d8d569985b60e31a790218909f180a38ca017042bf1912165b1f4c4f67847284c3077e936b50f51243eaf241ebb12bdfb93618137930be353e125295940c601948e3385d9ba116e7d4a62a8d8cc4622ca32308eb0b84cb58b95252915a8556bdc2dfbba81cde3c703ee77b4117049cba053bd66e63f3888437eefe471b12c304e6ad8c1bdc4d5f8b8d39ad998394123fbef4da3e36b42df736dcc6b69ae63f0bd5f2ba511bc96a6f8e5b58438a64bcddcd096be2db838dbb2164c229a885425134aa40379440dbe81f35834bac9409fa8af5a16058da6cb04db5128667563c65342e1891492e31ff1889dfe8880c5801f363312c85610e284077ed54cfb2c435aa4ae86ada7df5a5569ea274cb1dbd9727e3dfdf4e2a8a7e4da5b719790152611819530ab59342ac6855858e2851da50bf52f26ce5b5e69aea74bd33b3588325b3598af4e39ae4a5b3dd89b30c769ee9ebe904f6ec143bd8d203216772baf812cc5bad767ea23d45d7538ababa4b27d444aaa80bc706aa666674a875f8cfe476b33a43dcf11a20673ece18dd9ea8dc1031faa0ef7fc21c9209cd8793c88d43c042b01c64c8fd5e511aa5c3f1ef185522ce660240c8322f5d9a9444cf95c4ce329735a643f3c7710d80bff1f4af7fdfab47eac3d6e1d1dee0a142e90aac1613913c12487c6c531133bd881677349f6cc122b7284368b996b9aa728e966b99c3aadcb7314b1115612f8955b58bbf4768e90480059a2772323c1f4488bd1d048f3666208a28501945cd23976de567e1cea134abeb1aa92b46be0db20fa783530a1714a44fac8d5f2815d7c9504967ad9f4eb9bd78563cd1d212fc02d34136068d06503a213ba40d49b0366441fa2cd493057b5bb0470bf660c1f5b20b837c9f5c1199056e7ae11b31fc4db83022b747091a0252760e014057fd001c13d21a6ada3f71c5e3547af5008cdc1bafa8f5a6bbb0ca648b4cf1b1b8959e0675e2c5637ace9d454fcf0c92097221276449fac838992227724816a4838c2b539f8373fa599a5e324026c8899cc922e9202364925ce4942c930e65ac33e39c9db3cfc2d3ed8c7d269eb37366525f7926a30a5b66649193ddbdef473b2d8f7d0bef35bb37fec093eb073c85b59bb1de23704026c5b44fb4a935bd537a8a4ccab49bec85ee12aae2f14c3b8dedcc50675ef8eb06bdf0a68a640e2a4f24b6d4209af44327e838add12bbaa5df74868ed0da7aed7e6f675d5c3efc86e44cd0291305a29094420d149dea512f2aa5c2ac61529aece6024ebb44460c3650b4960efcffffffffffffff8fd2887c6ff27f2b999294d58fcab9ce94924c29a5c87abb0580000000000000d04d4484b42d80080001360c380c950c32be6008595bbed76e5cb23bbc604e3975b2d27e51818c2e183cd488385d97dd846c0cfc21830bc690d5415e4eab2c22af438b87990f195b308fd7285111249d3a6f0632b440b260f89cd7ed3d96b8fe32b0608a20d4e79a1094a896878c2b982cae44f2d8a7ab94ead01e3f72b8775ac1e82b562ea7f3a7f7fca71c2690510593ef7e8a4b530c32a860ba0ed3edd8bdea49fd2106be0978f48541c614ccb93f07b17de1397e1e8c1dfd053230c8908271cc5e45ef9e8c387a13f0d8b165901105539989a4bbfa74aa5aa0b02290f1045390175fe36d363b270a64384146134c5931a922a5a4f5ff924306130c7b5a3dabd7e5d07d410bc858820c2520c962928c65d58ae21626e7eac8f7152cdd29305ff48e1f6180816424c1a45dfcd7a1c5a3023870f0e88104e375c4cf6939c7945a8c307af87030100bca0b328e60102968c4ce53659915e204194630e4a477b671f2928629a308265192f26d574e87160c6410c1a493f289193ad2a1555e206308063bfb522ab25ae94a76ac005b204308b6984e6b59e2a2436b05cfe3eb9220230886f19222724ed64ad708328060ce733a692dc98e7621828c1f986329a5e517238aee8b0e2d1f984e9d854d51917f97d2a1c582771e3b7a603e4b27224fd27aa1ffc1a34d904390c103636524b57f7d0786f1bc9274d0ae25c602820c1d18cbf4689113ea3284ff62e4c0604a4cc850fa617a2f311fc8c081215b12722ed3e31ec8b88149743e7e07ad7568d9c09421f53fad5c1621bf1a982688f4707d8f9997f040060d5e1391b28dacdcf2608b5998f3c4a986b4a85a57ebd09285c15eb5b7526e9b66c8a3e8608b5898622fe5c9493c5a8a970e5818a496e7f190b34e8e1d71b0c52bcca9cb4336579f54278be10af349af9b70a163b32e1d5aa8bf08a315e6170f22e56bb7b2a7fc8f1d3a5690e3c79b8015e652a2848cbd0a5ad1bc1b6cb10ab3843b397d395bca35abc2f49f42e860fb2162e55361ea97d5920927edc46936d8021526b12c1122c9ddd6f33ab4c816a7307cca99543d97bdf42800821c15d8c214e6f9b99bb0676d396ca53059cfacfcbc84fd3e91c238497930d5151a8541eb25191f2c6ce6e4a23067d97e086abbd65c120aa365e59ebd3c16d6c4a030c77d6b6fa79f982d7ec258174d84890ae2ba2e9e30db998f70cb35298bd60953dbe92a31312d54789c309e6e7a8a272e5c3db50953f786d7898e2da63b4d984d727dc82e52ae2d970993654c1a1326d74aa329a6730953522b3167ba92e59825cca13b9fe167a9ffe54a18fc92d249e1514c871825cc62db9d4a2cce8a8b26613075b9b5151d7f594918f5e285a4a265899d239130ff8e8be452da729a14240c9e5c256ea5f434e9cf91b6c5230c27afa146c8890e25f5c3470f31f0b6708479dd46ff8292fa398503c7168d3079578e9714bef46584b153b810364a7aa560151c3836b0630337bc78001eb6588451f742122161357b2b3ab47280d1c347ca91e3c70e30767c9156ea070bfe40b085224ca63eaa8d2a199de3096c9108f3a7ce218e85b5dc35025b20c278fbb12e4eba849c637708938ab519b3f5d1a1f562545d952d0c6108ea42e70579218c372a7b50f1720e3b13c2a07a92693b1321727610467d2d1522ab4910262dc95cd28de7b69502611011abcd523e01c2e4e9efb3e45c349dff1fcc714d474e426ab609bd1f4c232aee7605f5c1a4cdce93aaa550d17e3e18430825673fc410fefd1e8ceb414c6d248788caf560d2ac0ad92d6769d9511e4c26e952505126553e840753b635b5aaced9b27eee60eae83fdb913c97d01d3b18742f8cac1362ef731d0ca77f3b588ef85eb8e9602eb92d2741db5850f61c0c7af44fa8d0feefbc1c4cdf61fe34662f54380ea6142684f59451eb398683c92ee5c5ce65926bc26f30a42417e487707254dc0de6f4a01e414fe47c396d308712af949c1637b46c30e5f26cabde497554d7602ef99175749ad09dae1a8c15f2ede4aa4d83c1554d3b744e75bf2a1a4cb6276b6694d2190ce1fdb3da6454d2999ac16c273bb1d3da2799963218b6548c45eeac5a9742067308f7d913299733cd3118f48e6a68eda9d1498ac11ca3a5564444f52485c1b8bf3a218c12ffca83c1ec2a33c9d7835f3087d1989c9277f6f5d00ba67496ddf537ec82b943a56471bf37fa73c110e43a8bae2569aab30593aa5c6acbb916b43e2d9856de4ac50b1fba542e0b66493bdaa2164f8fecb060d2dd117ee296a13dbb82f9fd7d4dc65356305c081682f68668b55505832c95b774b0d4eb6a51c1289fdb838e9e29987405f54e5a42f8a9a5601c0b97d2f327319e8d8221ec98eec50be2270a0583fc1693b150d7957b8261262895b3a2bb677382b15286d787c97d3921c0164de0524a31dc24048fc96d593917534772ca703131c1f01f7cd7ff4b8c8ca00e2d1f3d72fc781360ed12cce129b5f78770d7955282794f3c8c1aab2c1ecaf15e77852d9260128b9ec3cb2a2de5500b4e0e317ec78f1d39c21043013f586070e0781e3b74a0068330610b24184290b1133674eeac901c373420811b473055face33d17b4fe81a070e1c595b18c1702da23f8914fe923415c11892d36be4082148fe8960d2185ffb4a6dfaa11d8239899e2e2513e286da72f04839c010a3579023478f1f770ad84208a6097ae6c42e547c82d01641505494afc69b50dac514fc02d073a0ecc748c17b0ef42ae01d5b00c11cd73b6c5f6559b7fd03d3ad09f16aaa4cbb5b1f9843bc8a3d3b49ea57d9a207f59d505555a905c2163cf8e2c85d512a4359c4d86207a6ca695fce3fae0363e71429ac7e69470ae5c01c4452eba52a7b75757060fa306a49dedb83c8e506e6318fa192d67bb6326d60104b699dc74499a9af06c65bd950cbda163430879295d729b7591873d3237fb04a0b59982df6c8cff1c3788612fb418b589842a827d1f5d3cf237b8316b030ec8e97522d95da93a3052ee82f8a85408b5718ae4a7890366695aae40a73fc6c4a72d23ffa766e85f1830822bb649315a6dc921b2e21e4ce3731ab16bc09418b5598527ef1d423fa4244548571c6cbcc63775cdb3faba0452acc9f44326dfd11265a04155699502dff259fc230a34cc88f2915d6f6358521cfe75628fdabb3b018b42885e1b2f7fe45f49a14a60e134cdfa828b1bb1c85395c5acf339eeac2afa2308eeabc48b9d3f45c96408b5098cbf7b44539edd65c3ba005284c6ef26974c3b49b7f72f470b51f68f109834ae9f4c7ab3bdd73da400b4f1854884aaa840a75e408038c318e560db4e88471945fb2244509a1d3ad43eb6c075a70c2a0539e74d5c5408b4d18f34c6507a1224fae980e2df5e2878f57c18f1d1eb8e1c58d09dcf02203ff0cf027560a2d3461485a232f5aeb3cef291346ff4ff2d292e80afd63c2d8e13c4493f1b86df94b98228994093bb2342b6f09e35616190d0f6779f74a9823e57a4fc152840739250c22e798a0746cd2567c12068b2fe5a2b327fd159284c93e0459f3ac643ae648987278ccbfdc25344b0b0963abb88b2ad13ec224223c789824bd76af23cc9293e571d1e3e6d1d2084352298e899810234cb225b65ef8ff7cf5220cc1de3d6878fc34a529c2d41d41df52b8c48f13618edf9cf3e01e57928830756d45cecb8d649b8fd721463b40145a1c82331591218c75d9794cbd2d43d742984f4e7b24091129e8130e1ca702e3342d0861c86be1ed3e9aa987e8200c9eda4f5f5032e2cd220883d67de9b3e471443c03610ae6e9df2cdc9ef65a00c2dce15762ebe133299482167f300735c1652e47889cd65a410b3f18ab2a64cbf1d64e418b3e18c55475b6b353d2adbe2868c10743f6d5135aaf5647655aecc1a464d632540c1145490a62c17e410b3d98735bcd2f2c678d11c181038c1cffc9c73f036e7871430c15ece80ef87813348f9ec03f0370e030841679305dd073aa53be893e3fb5c083219a051341854887568e1e583734d0801c28e8d1c3012df8b103f5f8e23980821e3d74941677305630154b295d314f5b1a9000056e7801868f1e490b3b1844ba9eb7904f7950578726d2a20e66f393d153a254c4d3820e26152bcd89acad2d212924d0620ec651423fc511f25da43f5e0c315a0b391894dc91266cdb93fe6c1c8c93bdd2e73c3e726d32410b3898c22ff57a7c9af0277f83f9f3072574bc3531be26410b3798472895eab3662bbc7c072dda60c8273227a94fdc15291b8c19f7a373b0aebd11d7609611f94295a67e75eed460f408c254fa9a4e923b70d0220d46afed30f6975375d7dc062dd0603e35ad382af94688941b5e9ca9418b3398f3fce5152dc2a4e792a1410b33183e524fde3199ddb6dff0a2c6a0451978ebedb2f2fca2f722d6879324a61406c3a7df8df5090c061dad4252b944ac17a52f18bf928e233a0891523679c11c93c4988bf6d1c9a62e1882c867852f53b2b4c40563e918931afbf3c1b40593d0f13e4a4584da5c0be6f68fa3a498c58aa32c9853d788d2794f941661c158ede37eef5e2a055dc1146e3c7ddd862419b28279fe4bcee8fbab6090e62616744c8da07e2a18c6624c2edca510d69f8249e95dfbe0f7e1ad7c29182e8cd26b23cf46463e0a269dc3c272fe50302825ce2b45f34bf67982414fc57b0f2a77dccf0906ad274fe85375555a134c3a8708593a449e5039130ca67f365b72eebefc124cd992ec7f8d9460f2fb5825314549307ad67a4a592349bc2024984ebd8268af9d8c0b3a827945a276cf858c609025eada84451e9b8b607899d4bee7fe612b870826bda2fd272c4dfed2108cade9ae2573fd742e0453ce2794d899dc9377108cb91fbcf64a290f910682d13d4552232d5e9fff03b345d5105bb99377da078664559354277c52ea8121db0753ab5f7d7df2c020bf4c84a8d7eff9db41e168020d1d9893105162890fd1b9710e4c49d32575c8180786a0bef44bd27e55a51b98c5f75b6e926c609291c44a50313fde5f03f3e59cc2e2072d0d1a9892febdd42a1b4feecec258b23c4cc55b656150fa21251593a4fea8b1304dcaa3c4228ba70b515898267accadcf6c0f7b7985b9f5f23ce8595d798a2b8c1d5b56afdee1e45b5a81865e532774921586144ab4d8d8f6e55856618e89be267a91fbb4a20af3e851a1c3ab877b2815860b8b93f20915d209156635792ab77592489f5318b28bb689acd3144679718fb91475bfcd52148e19a4308a57aa5b4a99fda65198440c69a1f2c6960e89c23041b25ef6271426cbb1333ec2a030669c5afbd04974c99f30fa6ac88868f93e56d0139d309abc4bbb3e274343e484410961a3da356ff2869b306875abfcbd5013e690f357055342849cbf4c60c268d13326a99cf025b74b9874a6c911fd99b6bb59c29c3c884d0d9d44866e953068cf9de81991db4246893c5e658859cbe973d608332661d69850df7d41a77b86244c29528f4d8e15260445c210f4225f277123f503095309b9b1a011b7acb247983d674f743d0f92e208f3a6c4d4fa485e426f84419bd7d68ff43f1f19610a2a75e53849c482482dc21c16f4eaaa9a84982bc2b45d1e5f553ba2a60dc68c44983be864e796e5647f1061ccf1ae60123dc40c4398a4e50e2b9ef6ed82448756a92f9a478f1d5785306fa530aa3bbb88b7b00eb531f80433086188a35ea4c596a7e8fa8c4118d5238f05bd386f396c8230c6cf5a85b6331046ddb018eaf3497f889e236df978318e0033006130d78bdc414e8964f78c3f184b4f765dd6f51f7df4d0f1638caebaa10109e01066f8a1cf4962e75e32ebeeae0f26ff537aa7b9a227e5834969f3ab1cc242f86e0f065552825f0c911ecca2cb528544d339964e1e8ca33e495dfef849780e1ef89a64415a4a4ab98b7807f36ee4135ac4b2f34d3b9cb25809e9fed5a190b6e4fd4513f25266c20c3a18c3b2c7cef924fe7c7330c90979b6b4b7f8596c861c8cfd97221d5ad21c8003c70d0d4860461ccce51546ce2819a2b28203b1cccd2a588879a787a524ad9c9b5e1f9b958f1f61a861c61bcc2985c91c15f2575ca243cbbfe8f1801ecc70c38c196d306a4989db7d2ada154d9bc106d35cccfb1625f2dfd83ab44831054090430033d6604e9e7447f850b711e42c84196a304ffe1cb23f3c92e7c98c3418ac74b22e11ef4907090e1c3870e0c0d13ec6e81968b825857dc81354b2a843abc78f349f7106f28e9bd05f1dcab34d3136c5c8007f608619cc55e925f731544a1a6ee086052430a30c85635730830c61cc188329d9c87e49d2728820d7a16580196230e4122a7867ee684e4a0312b8810307f9c18c3018dc3e3eafce25ffffc1601a197eeab79d2f18d5cab3d28cbf5759bc604caf53714dec9c2565170c22db079d2a642569c805b39fc6dd64b336ff666cc1f849747a74cf2942cecdd08249665859fe7e551b9314db8c2c98a45e557f927ce1546233b0608a10473cd89c55a490fb30e30ae6d0bb6a9f99f45d991d6658c114a6b3e73af9ecab38d36146154c492fe5d8ab6fa9a602e00c33a8608c518bfea5840ef2f41b5ef461c6149890b44e4c8f4f1b6648c19cfb2147496f414d4e6b46144c41656fa9b0154a07bd176640c1f81d7412499ea494ffc486194f30c49617d5dafbffa934cc7082714d09cf15d76b82a9b2e699fab0a4df2530cc6082f14e8c89bea518c2f59fb1044338d13a22ac2d82194a30e49cc6248884a01742c418a3c3c821c60e6e818f1fe30238706ca9624612cc1d63fb268b30155aa4438bc78b5139988104534ff28bb0358f1ea52aa0a5987104d35bffa7ab4776f1920e2d6bc40c23983f6ba86d9365b6629addd040038c0f338a505cbad89e2b29443045ba2472382192a537cd1882d92c9fdbe4dc0d2f1e8f304308269b3def5c5d62b456403023082691a2f6243523449c3e030826554b428a89084984cdc630e307e69e113ad9dd7c6c4fe1c0e163475b400c31ca15337c60f252b1fc3ea558e961a1193d307bae9945ce7b29f97c43030df8b1438c1d1cf02f7e070372e4c08103078e1cc452318307c61256722a5f2f57590670e0d0e2cac38c1d98b30891f4f3bb622dae03935cbe4b672aa8bab6941939305b75bf07ada2c2b636030706a132421223bfb43a35e306667349d739445fec6e0d98610393ce7b9769840ffd7a0d8cdafa63914eab9bf6cca081b176db364e2779aee82ccc4946ceed8dff08a693210be3c559172177413bc5636194f38e2b278260613cd7ce31d2e39da6fc0a94aef8fb9083ee09325c61ec7cf23a3ce4e6faf60919ad30658f9c96142537bcc0218315a6999073e4ea910e2dac406291b10a83acb7549adadb2e4a2f9cc7175ff4c0814355610e42c7f149e6e9d022960ae3d9e892a3639f305161167db7186b61daa7c5ba828c53a83e76aa4445047bf4d091e30bdf61860719a630480ecbb94eaf2c4e2cc8288539629c6e8f16d7a1556490c22017a9b3e4725bb3d50b1d3f7674408c2fbe48c00d2f6edcf0e286173732a0821d482a4761b4b2a0c4e7876c42045114a694dd43540f391446c99e647fdc49ffa2224106284c21e662c4c5f809835ac8ba30df19d5db138631b7ce412e44cd8e77c214ea1be1229987105e4e98f3660469bb262f4b043761883ef239a424e652fe6568c2142ff2b9cda92492ac3361082a4ac7896b1f3552ca06199830697eafa59438bb9294a606199730a91472393fec32ce34c8b044f1e1b533934483828c4a18238454562a4d2a7d6f0b3228619654df9fb3f6c4c93f13644cc2f8a3a299ac1b99f2519c820c491874685132e6237f098b181d0973ce9582ba7e114ae78a9546410624cc397b4fd01e27be82e8112699a49fadf493490871842927c7aa30c16b84c14d9db814132e7d106530c294535055f14347c78f1d86b508638cfe6aa8091b1f725610321461ce0f95cc278be534aa1f850f321261fe380badf7269a21727490810843184f1b2925ed8dd172d4231e3d78941b641cc268b5a293fe9b0b13711c38725496187a906108d38eb85162e96154f40b6152295b596f48ca1631214cb9ae944ad4532bba0ec2587b29b2abc3e5944710864b89912bab2d4c326504c2f4a22599c908353f512b62c8008429b279e9cf130bad5518eda3037f307fbcc4f8bbec74effd603aad350b31e42751ea53c1a9c0fa60ca29fbba878f947416e6833182f7972a9d2e9550edc16821bbca7e9c6fab8f1e4c25f78456ee4aa3cc9307e3bac82d51425b3e7bf160f4bcdd799797aa454887965a828c3b140e956107930862593b5adbd529ad83412e3dbca9cedc7a4a87961264d0c174f165a636deee1fcfc1b4e1bed6d1dcc2a5580e46577d8bd955da3ed70641461ccce715a247e4a685d02e1c4c55791b3176bf2ac7379852f2527d13dbb4c99d4508196e309d5e9494c3855cefa71b1a90c08dbd3bc8688329447789134a847ab4d860086e369643aabf20630d06d3ab164fc505196a3099c8113b2ed7686d4f122bc84883410909e95208299ee98c06438af4a9e4d9a4d39f9d808c339894ead8ef3956e7b0350f1e0fd811460f0dfc1829d8c163033a7eece8000e1c26e0b1e3c7183d7ce0c0e1e3cd60d2a3eb391e2b88e43c92510693a84fead6b1db316e9c1ce3c60d2f6edcb00006bcb8e10fc081c30212b8e1c50d1c3878f4d8f1c3021240934106938f29edb916513c3e7c7c8e301a0c6519633045fefc7471afa4fd9f500632c460b4cb712409157eb5727561307b2ed91a592343c74d9001066307ffb4506da3723e468f93429bb04cc1e05993432f220593fe892ef1588a8249f642d5758a9ccf74a5810d2818a2de256dc154a594631b4f3005a57b2c7b6ddca897c70e1b4e3098fc29ab0fbb8d26186dfc4ae297a7581d31c138c1db5358a99760fcce5e5bb335a244af049338bddfd5585127e6eff0a101fc612309a638a722e85cd9ab944782c1f34f5a374f5727130a8020070f1b4730a7cab6b09775c17392114cef1527b6adbc2b45304a94c97fcadf3ab8fb3688602abdb1e2794255bd3504f3e587cf0895297a9484404e9e93b9a52de8e1e38718ef6a413067567ff6a9708fbdb16047e206d80082c14c9427b359316dfd07e6d841c7eb493aa77d30bdb0e103b2840bbabeee8139b57c643fa52b6b877860ccddfdb1dc9dd424ed173d72bc0f2d0bd8d84127529410baad723d2e810d1d184bdf4bcafde1937e6e47056ce4408b91f76534ecb64f3858dbf7c4247bfa8de8d0b27103636c97c43711d361c30668966f7687168997ad816b7fb927a6c2290bd9a081297c7cb88aafcec254c12dd8cee994e654b230aca82c51ecd4669f3c16a60d153a9fcaa8e4e8ead022b030eb6eea878c32a52ee815a6d0f31c952fe474dc1506e595e553fcd22263b6c254617977d262d7299d1536a0062b4cebc95a553e45e598afc2a44ace6304f56872d6508539f7be5498e3cef87d4950117c820a7399d5968aa4e4f97fa7305c482747e5689bc254a1bc8436292985294d8638b7ecadaa954981d611a7fb4dfd3e7ae4781e3bbee0b1036b8cc260f27241e59cebe324573fc600831950431426a554cbdbda6d49d2c9e1a3478e15b48fac5098449b2559417e50183b55de587f3491c4fd845172f04962d259a41250c31326d931a25cbb69c4c0b8ea8441cc8b2c112972501d7528278c3f27a95626a55d52f5f8811da8b1098330f93a3bbaefc953ec050a50ffc8808f0514d384495f859c773f972ad5eff0f183003532611cd7ea1ca93e4eaec7043530611222561a3d218a0ee9f4eef145a97109e3bce87a90fb31a1a52d4b1872cab57e1debd2fe54e2501734e4828a52c2202254949b8ee63ba3496422ea537c477bbcf0a22c06c00a3524916d5aebe54b8e732a913067e6ff08d9134aa4051f011d62b403bc68818f1e60fc04b4f8a106240cca6544b8bc9fee0f070e1c38321f61489dd536d4e8c7d1d611a58fa79c92295d75a0f6a21185a30623cc1e3d7cc5c9c832a754eab250631124255f215b96861a8a30d525b3b150154a525e65093512619c9c3b251135ddc583a48a500311467def2c2acd64fbaa09077e8cd1a3052d0122d43884e15d2df4ed1a400d4318a28ef2583a7cc84a6f214c979753d985d850ca25844195d0124b2db509f90dc27026d2fda739a5c65382307e8e1df2fde7304ae8cf63053d72f4f841017cefc103b74620cce31b26522701c22cf6f15a2caf1c618c9103053d7a7ca176811b5e707d515450e30f66dd3adb4a7e5af22460ecd021068effe2f40f151004410d3fe81d3f89f5b20f47b90f1f175d7664e4033162ce45313ffdbfa0c61e4c2f1f2f4d5d7655317f50430f4813f51a9d9da72b561298c016dba0461e8c966a67cd2f88ded58d900703ac6ae0c1ec17c9b2ea5ef6e756f650e30ee6de8a92fa849957bedb50c30ea68ed6692d8cb6e4bdcdb8461d4cf2fd3cf578e5a778c3c8f1a3ad6bd0c158135b4e4c3a8d144e1d5a24a83107c3aae5a0b4940e22a6c5e460f612e27954362f754b183b5020461c0c3bb6292a4cb669da3ab448a971a0061c8ce731474f68fe88f9cb1b0c226f259554861ca99230d47083210825c67334f39c7bc951040b35da60f6dc972da4654922a86f6840023770e0b8420d3698fe947fb8e5939aa51346bf036e7871e35db0c3046d811a6b307b2959331d961a6a2852bc53322aee23a635d260ecfc9a10b4b607a88186f5b4ee44cf609693263e27a3066a98c13462165e91b70bd42883b1b3c8f458a16f3984a506194c39e364e9143d8c4e2a5553630c069d464e87b42a492aa544a08618cc22b2562276c4132daa11863c755885cfb2256ea006180c723e399af652ef59d7a1c563051d8618955f30bae7647617494504690d2f184ee879b40ff69d1ee9f183c70e52d70573c9897cd6cb95c4a23a34470d2e98f284247b47f6c47d7e0c3072fc185fb40ad05250630b85cf95bce59a5c122b6b68c1f8396f94d0398f1dedc38b478d2c982e549ed9f54bea3f8885add37cae31bffdd2bfd8a1c30b1c382c20811bc400a0831a5738a5895421924e1303a386154cf9e2b7da8b5fd4a88227bf4274f79c1692a840907fe1fe6905a1c6140cb2f44d92a87f22844f0a06553a9f08a92395582b0ac6ff9ccfc9be46574728a8018582bbc5d6a83c411b396216d2ac091daae104a3f5a589a182e59ca46f8231d26d8b2993ef9dfa13d4608241fefe856ab88b1a4feab2a3c6128c926e945e9db80e2d1fff83c72bc19052249720440c912c9b6a24c110fef6e4a988842bf3336bc289cd521d0c6a1cc12445251d2392fa358cd099b04bba222d656419d42842fa5c5e539474bd356b10c160523664a81c77d6f7342daf310443163b7d3a89137e419aa330a186104c1e26e88533fd7fff8aa046100c3a98ca76aeb14f5a0404237af6783a45ba3ca2c60f5049693e7aa41c39c478efc1c37e8c510107e0c0518a1a3e30e58aed263f6ef5942c871a3d3088912d572a44b20d3578e027f17493dfabb103735ced77a8990a57971a3a3087943f5458c9a73ad960e4c033d4c881d922fda928fd19e1e339c46852622440af060e0c179e3f9b87d00b356e600c0b4a4793f74f178cd72ed4b0812949df1d6d6abd638735307d8f4af9ae4e09c9162ed4a081b947c50e2a09a15e4e3486037488d10ed02146ebf8a27d3c18af230c830347de2c8c212c52775bd29fe6781e3dca6461f6bfdc2a7b3ba1b5d6520004390a402316e634e194a7240b18edc3470212e03c3e011cf8e28b0623035f7cd160dc6981062c9c9c73ae5675a0463edec7991368bc829c3346992e9bff920e2d41d07085e94fa7cb2e73db772740a31526954ed5a893af0e2d14fc8b81ac30975c18d7ac6c17dbe34a0110e470008d551872cbf9dd4fc8e621c80c345461521f4f24d1e5a9fb16038d54e4a73921c9ca5ec1da916305ed637d40031566af12a7d3acfb76ad5314219f09917c6cae44caf0950a020d53986754b9e874fb90d26c5da0510a1bd02085d1c3a5df13ad9fafd71f6f82bda18106fc0e1f1a50c002c0a0310a73c8dcd2a33c2d99ceac1c804301bfc3870670e05851983aa66d7fa5a450183365c2c5abda9af280c2f471d7bf72752539190e1c9f30296d4a694790ed1cce22030d4f18d47cc5778e36a94d3c061a9dc03d4dcb2ab897881c6d25f6f5d161bc8f30d48b0caca031b0464c063438613c51266e4d45a408343661f059f5a04bbec4451b8940431366fdca7dcf7163d2c45a9076fc48cb8471cf52d4fa6c75685565810626cc394bde3e1124df098b0b342e61ca2effeda87ba2758e1e89c7abe07d9c95818625cc2112c4e8add7d27d791f581a584163a06c05ed4302342a61ac5ac92a172f57e93f0337bcd021463b602d8c06e3980220c801011a943097cb9588a6ddb144476312e6cf27c9ecbd52aa3449c258d9fcadae258ae552240c97826765dfaf58c9878479cfe244ae6f8d6efd11260bb3fc68e97e270539c2a0c2c9bbde11a722db8d30a8a042f3fd466ec66684499a276d62b45e84e152d85af913492d164518ab5256a7f510c2f3980853a5892343a99e5069448439dae8ee0f3d514be50e619e496272a2ae6e9e9a28a061086388fbcb56915408d515c2e8b3d55797231f4a8430e90a7d4a6245cf6ab11c5961f4040c01340661fadcfe97453541d457f24f4ad8888f1e583f7818b3301a8c3b340291eee4654ad51bd00084d1f8c37d4a45be7ce7eb29411a7eb0e3c8a4cbec35097a0660e086d51a68f4c1543ae5d3c9084a528ef1d841830f26c9a52dafa6dede227c008d3d98e2c76c46ea91e2f1530ca0a107739e144c04a1b742d7d963051a7814f4170a1003078e1e87041a793049c8f1b3766944091d1d8a85a583061ecc3979b476ed088d3b9852d4515d794b72cbf3020d3b98279e14714d6fc9b9a4071a7530b9a5ad7f38f921beb581061d8c2a2a74a9eed892b3c6061a73305c5e5910e97477d2b71c4c171a274aac2ecfaf71308c0afe156a42e868270d389874aadd129d722a914f1a6f30dadedaa7ce6621f97783f944333d7679d2f15687968e80461b0c13b52eeeb7a738f5d16083492d788ed12372f2d3d660caa1dad4472d1a6a308e8ef20e1d9d278eca6909288d34182b9dcbe98d243ddd8206e3298b35e22e2bb682ce60ee971ce6eb7ed5e5651c689801d1b32e4abd153c8f2a1c380a216894c12cd1265c96d16c097fdff0e28617371270430312d8000e1c8767a0410693b8ec89f82989bff60109783076fc00038d3118f4497df3eca81da72406932475e26830f6769a9c7412f14e5ece60b42f29625732e24ece0ca690b29f71225e7dca3298538de796bc351a9223836947663cce047dcbec6e0c66f118ad58b1c6d08518cc7e215b5b1669921a06431a4b1f8408263018d62c249e2a1171dd3318baf8824956f4641db7a0dcf35e305f8a914709e5915474bbd005170c237a3b692451af276aa18b2d987e4f462959d182297479da6e87859c9845e8220b8b09392aa6a89073ce16bac082f1d4af0879c1fc2cd2573055f80ab25348ce1a522274618552b8d8ae4a51726433b79817a5e73d5cf9f758e8a20a260bd62772b96fb1bc2074410553f9dcde958974895ee2144ceb63d944d8fea04d772705a3c987dbeff93dd7d754605130889435bb6bcbda4edf0514cce27aeaf489a4271845899c62891482e53b39c1a89e7a4177a834e23522852e9a6048df1a263c8fd08f6d0a5d30c160113d89b68c4d7fd112cc23ef849a70093a59100a5d28c1f8923ccce8709554971908ba48823164084b62941a9b5ce1c08103075617ba4082d14aa43eaf12d94545f108c60c791d215e756104d3cb7b5aaee9b5ac1626745104a3e7526aff3c448fe5218239d889a4df7a1e82c9652497484229953a672ac8612ac8c15d08c1a85a21e1430a6f0bba0882c13da478c976ba501110cc622162440941a5f6f507864faddef20e2654ba74e103a38eceb994be3b11d6d403739bead57c51fe154d3c30e9f718bb8dd4c9837660d26ef7163e483b9f5f0786f9911797eae7c0d8ea4959367d5b36e3c094ad73775ec46f609e28b271fab781d1940a69e369c452e7d4c09825dc93c7bfceb4ba0b1a98f4c94792772be22fb3309d1615d5aaf2e91861599824e989d99525457f636196cbf20c4b49bd220b0b739c5dd3d77b798571267bcc15062194c9b8aea6c50bb6c21cefd2c87cfc5861fa709395f3534eddd92a4c32940911e53a92bda50a934ae192d8a79718499b032e5261f4d06149e9adfab81415c619cd0eff5449a5e49dc2ac7a1ed29ac8712757cc8529cc21f69d3aad3ad1e7be808b5298d2bec73f5f8c49f196643515df744b0110e4500117a330b748f068c96747b72c463117a2308499b9cbe9e4a1302479399e5bec10e3d4a030f929799f438ff64ee94f984fe6a9b394145722f48439554ab1546676c2103c54f0d4f1d3a4cb09a35790b359d242ca966dc210a48548d9b3ab54b65613c6fd8ac9219b7afd138d910963c88b31c9fd8409f38920b27b07a13ae87e09431ed7b81153f13a792c61ae9336412cecc70f57c2a85bee4946357e46a684792dcca8d4900f6fdf240c2e1639b6ed250953ca3946a56ce1f3ad2412e6544a7c72d45232250b09c38e8f78d1ad5dfbeb234caa2bfd848d312174ea087388ada43b5a8de494d208a3eea8e7aa161961bc58790f134afc9db508639f9d56c826449e9ae8e14211c6f288b5716e39592425c264712ed6b79c4e297b106174f708fba52fcb5cf210668d1c52ce25cf10a653b1df26e4b417c285306788701b7ad2955eb92084397afed5cf16e71f498330eea5d39bd7e9f17358102639efc17d74ee4f6803619212948a15f92cc4a880305b74499252292df7297f30484af36b9bd30fc60ee27127680fbd2df6c1182284042149e3734fb8e083714e9c1cbfb0ead0dc432f2adcdb2c25055ce8c170156fd4a98c502ae7c1d4c94b5faf96af248bc78efef13e481d800b3c9827f887ca8cd0154ca8035cdcc1d8d559bb2a516bcdb68351a469aa44d3c147c975684d7bd221f62d74307d4afa82789c6429b23918d7dfbb3c3f85f66cc9c1fc6a2e323c290e868b6b11c994ae3093c0c174226d85dcd8d688578716c7808b376c23513bf54d0660e0866d10b8704351f5730ca582d68b29b6e1d8503812176bc84f3b07997b22cf05065ca8c19c15c53b981ef352b948c371818633983decb53cffe80dadf2e0c20c65e0820ce68d98135cac432ef975687106b818031762281c18e0220c25792247b780551bb800037a629f4ab22faefb8239bf54e948d0293eacce85170c07175d30baa756a8ffd38ef51817cc17e14ff6bfc52e9d70b105a38794bb9e4ba405439e90440eb29e2c982de65dbcfe0b93ca830573fd88f80aa6129d645f1172288dad60ee54c2f3daaaa8986e158cafe29e477dbc934ea9600c8b1a663a2a533057fc701fa6c4cc868a140ce2d2d2ccfc1905a3e59517b97c49de07a16052e1f465316d7982719405afceb52ab19b130c9e53d5bfee4fcaba0986a433d573ae3b7ab4b86082b94ca99f90de5f8249bfb25ea7da9e7b4809c6fa3ced2784f810469d0473a4f86f3a799f4a949160fa2064e7a0bd83be6f4730ede6967ace13d721c58511cc77a72bc764cc45118c0b2218bc6b573dfcfd2c7b968b2198c35eec8a33cfe9b2bc75e04208e67893f3ce635ce93c291ab80882d92b299d929a4b3f1ed601174030598821a394ce5ebef20f0c93948af390fcedef5e0eb8f08129eb26754851990978ecb8e1c50df4a32770c38b1b3888e1808b1e1824c44f4fba2d073dd31909b8e08129e9285ff2ed4e52f8eac0c50e0c672a764e49fd3a30bddbfc05a5e7c090534e77eaf6ff727e70602ae927c54267d50a3937304ad239b6599738eddb06061541c83e59f9ffb9b9a88149ae7bbed694a0b4ca5cd0c0f4e3ff9f575eab559d8541e6df95672a0b8327557af26b5c8dcac5c2705176ee2e8385e9fe4f6419ed10f373b8020ab0f10a539dec59e7fac58edb1526b5ec7a5e210551e96d85d1934cd35d174195b4b0c29ce3e933da3c26a5e02a4c71db9252e496111d55984e2bb9ba488bf7a5c220397409e95a4187104385f1f25772d01f611faf531852a98d105b21bba6c914c69ba472309dbac472bc14c68a687be31f77ec6a5218fff4c66c844effd78dc21425988cdf505a73335118accd2c34dd0a8549750e1a4a85adcff10185d1447d1c4fd9d1fec4363e61b4937da7bb69199e270c3a7bfad4e2419212f94e9863a8ce06274ca7674936376468d7c6264c2145b2d86225b7c3c786264cfa797cf7bfbb53884fb09109574434ab1cb53ddbc08459edb292240faa335b1f3d6e780106f2800570e0402c281b9730493a9597b98f258c22ff734a1b63f2835a09b36f29214feee83115a2432b058f9e035afe60831226f1523282aa1c361e96828d4998d3e8f8a444885f22846c48c23cb9245d58b687534148b01109c3d59c641b0f3944cc06248cf6a5effa3b7ca6866c3cc2d49e2fd684485dc086234c4a9f14217374e5d0fe4d7029b0d10893d0aa8bfb693b87d57a78f5b0c10883b0c8de651fb63e692810a327c017051b8b30fa25df09494b844abb0d4518bb4cbf77457c885c6d24c2181e3cfc9dd4309f30220c9e93f0121a56af351ec214a49a16a5661f0c3084e14347a4aacfc536dd033210c618fe0bf00f630c7fe7828d4298a44dca4e77499d5cab43ab7afce0f1000da00f2303387078516c10c22ca3e457bed69860631086f8492b7f88916b5b2208c3df25f716217ecd4f360261d4ca6142994f1821260ad8008451764d48bea0cfc6471b7f3065fadb6e79aa84c1861f8e204f46e54c57d1c7f3e0b1237f60a30fe6af93277252a95492bc8e60830fe69195543a112dee771bf8d15f3000070e1effe3477f918a60630f0679baf37934c5d2c46ce8c158ea259aadfd44af78d9c8832917dc2359aa4a7a4ad99d69db2fdb47d9c08349e5d592488bef6034b5b5bc30aa938ed20ec64f39443a15456997570773ac65ff7277f3942345041b74305ccb5958fa768e31cdc1282a96eed6658ba54a7230ffe6e7cb895e39f58f83b9826ffae532eb4b3f1c4c724aa9da9b90635efe0de67c237de28f2e7929ef06e3bfc90fafdbb5c1f4a5b26548f2cf2284d8608a286a4ec70b21cee5acc19cbd7f42e85822df78d4609e09e7c9f39bf4de4e1a0c263b5cccea4457d3a2c1a0ecc7b44cd4c7dc3d83293c8fe4b524c7fa5c3398fdc476bcd1b50c267579bc275e8e98aa643068a5d293214b8aa59431984fc9180b693907991531983c5bc7cf8c4db41cc3600cadaee31b1ff252080643c82942bce4ef0ba6ce9e161f23e8657c5e30f99ec452ffaba6dd75c1704ac5da7d23a67cb86092cb3aa3feb52d98cdbf3c5592acee6b5a3057c8dea698d887ccb260ce93375b693145898d5e0585061b5830e828e163e59b5dd05ec114b4355594a79d39cb0a26212b4fd9b5dfa57c150cbe3d2a053d51ca83a8601a917d7af653018f54a894d025335924100743c15010c4403cbc6d13130000100c1a92c662c17848268ccb0f148003543e2a523e2a1e262018890503e140180a8501e180180c06034261602806c349a28751d20310f8b499e19b9d6bac49a2423657152f5569d5e3c380512ce05266d1bddd8312c362bec94777db72661ea541b2ac36b3d306f1af80a70ef03dba974a6be65752e034a571120bae82e15cbf6eb5e1d18b8b7d6154f19942a700305298cf2e3415a7267fb2961ce7e5e56f2d1d53f4edabb0d3e32e066c7a12232aab4beb137361bbf130dcf1390fe5319d83bdd9537b6025b91e92be782e07ec432bf114e140259fd046b6fab2a22f3a7e06f1d24f435a69f112f44f9e7f906520d8e9a5a2dc07813d937072cc0fd3199897d87328dfce1f6e1f6a861254fc2ed413e7c7a5e1c5229016c2f828b1a9a733b70de650a90d572f6c73a196b17fb05c8db0e6402a0bd6f05b872ca89b00dbddffd5b68524d40cb97695066edb939aa4f00a09e6f1c10a9c856e64312afc5e1adb219956da22a50ac3a67a477f99a43a9ef7178bd5d75a361e08a77379d190bd810b622ff7c671dc052494eeca73c9adc552ebbec6368d3918eca3e642911cc289dfd627fece88620e53aa2f87fad6bf6fc919121d98234ff8b1515ef24a33fae3c07196c2ff76baac70d578fe92040146a16e358123ef8df6c684c0b16e420346674d8dcb9ac45af489a6189a4832f5a8bc3bb76ce929b07e326f372bf3f21f455e5b6b0290e1eec65e6cc5fe8d645439538fa8c1465ad14a6bf2c1620a41ae8c6a00553950493d2386564935d5e9a8f2cd06d5b1965aa59a6729631f7807769268858738a4c4a6dda019a84a0ef5e459815517ed0d05ac5346a7cd294869dee33e01d11e8d836296db82495eddec12b10e683e3deb459a05b37ba42c79b44f1da27cbaeabbc54e4706099767991557a93dee0a07a70d058ff86ec48bb1acb54a39af575cfe598f2d6d61e9cdc773081d13b51c68340462a8c5bcfc611f15eb58c8c7ad925e962e938f58e5a376f9231bdf712310c02cb86e384438b5385bc9284f3d7db9d73445c3b40898941cf78d6ce1ffa37fb214c5de568c3dfca68a7db66adf664c7e84dbfeb905cd1ce96a5264ca05191612a54651311f061b891055b591c0fb2200155011b0ce43134029fb3f005028019445046f5705712b21be2accf6e52206e0ef8e38d9fc78937c467586bbe0497c57751f58f907c76d3a61d3349ba66dce9ccdd8df06e383cf6dc2c566c6aeab5ea9c7aca650ead4c37cb60b1a56dbd09bae6198241209692381d0b761954cd2fc2b6a478d8483db0edddddf3f0b8d5279a71f7b3a4c1a3f3a0d52d0029b5bff1a4a6fbbcbc2a5b3d15cf7261daa45536d6427c256a4b7b4f2eb147e3e71b59535edfae4bf4b5256c21d7a79e966e933983921f1be0ec1e111ea3cae8132e18f5b8181c0b5687972090bd9c5d9722346b2be794fc7c3879008da71cd91f6564c9258db5f40b58bc9d26bab985f46598d0cd6854fdb0ee72bb560595aedb7cf488a53d5bbef2a852d0673b671f076a6c4af980a645b4e8c863a0e96640a64c78e2232e174434370784c61e35f96174dcb54f7a823385adcc81a612f4636a9143b54cf050cbb38a062ba61c1648392636b97c5e2972ee0fe25c01c0f4e2c871d25bc73538d344e653042bf476ed8d971368b34e5739ad260e9988434bd9beec966fa7b9b0c62ec605e991c665ebc3901172e8e862f602ac89cad4f46647d5be043e51074a76d8b08fbb52c80d6af0c78cfa4e3b1a4db49896b0afb556af762711aceeb0a45e94a8bbce25085ca965cf59432af3a11bd7823d4965a751b7b915db0c63c52c9dd869b642d9f5051b4468b1ba404c19c80e2d28b0df68d3b440ed19d21d3239d64c90fa8832d2566e7d55f9715a9346e58b23f6727f5a6b5e0ff2e6789c3bb9a60037b1526127773261668a3aae671275fcb71fa47779967121f1a97b2cae77456676ebc1b4639a50153a22b0c8cd0b27072ea0a64a430036c5b451c4766100855e6269830bc00c351cc090e892760078d2905276bb91950c0065c91ce663266ddd5931585a07a827432558fb4464a4f3a4181466333a5b0b810d0993edf6e0a9a2a40020e3184b2cf5184f54e37f76ad388e3b72bbcd47461b339d8a51a92d097d5ae450fa4e32c8d36a703c96e61a3298c4c61047d23298b4e5b44b48fe64ad9b34c2023813d8e76f522c30c3752d77ddefc93a7ddc199c30fddc4a2a7bd1038e4e05ac166817c02aae06052aa4294396ab6965b5736b5590281e384a690516894f2028399cd7d0bbea4106db01981bd891e648a7a2aea9ad87e40d7bea36fdccf3217bc0e994ff290e0c0cb60c941029a8e06e238a0e9665d101bdadd546d0baa4a293528038af25c73199f45655bf1d8734a5e060cb55aac4f0d1b93ca924d2cd78230eefcf001b51970671fdc72535e08cfc99c72a66bd357a1745a4c7552e883c3c0abc67b343e457b3a22c29a61ee78c3a7efbf8fd3fdb37618088746246e03d1ec6607188179121cd9d28156b122a69c0952d52f7eda3c751c75b331d6f3b82953b7dfc41f57278c09e3bb74994b5cf36dd8189a35ca092b8893e22a3919e666ef430afe86b59c35b85e085fc03f85650062130cfd45e46cb653278e54f94e2ed4aa4f05897762437f48032d1f1266e014e5c341a77368f51855ad204e99c684388f7db0b9594800e090c6d145d8ef70a71c1bf8db19601e19819dd4b11f28fc2222da55eb04cd7489e900d649c0ed7e1178e75d505f6fe2f69afd762ae9577914d638f593f6cfac05dc0c71d36d4e0c156175f171a57c5272f426bdf93b6d5ae6b630bc42e51307f6ba7d72a704a90367b59fe738f3630d699e56d84d02da54b304f8498bb5334aae74845f6110cd0481c198e712298b37298657995ca0d4353af8f4926d114a3865065b4f7dc57afc1b4842bbab1c21dee2807495908c6fa3f6323fa7047fc0fcb0063399ea6d72277a0790dea6bd710c09e468ee9f3606679f76314f13bcd7ed0f2f714ece14dd65e85475e7a3d63b9d9af7194cf8f543d8300f202c8d68dd9aa20837c14b2bfb3c2904752d403a5387efe5252d7af1ccd30142e6008bf107ef3f65e697ee696e7c75f860cebff4b865a2ede1fd93a222f42c6720729d7ad59bdce495ee5d9ac7b0279b77aae28d70c11162ef21cbfba007a3679d772f8f8ba770fe3626ef665d2079aaf5f0f47cf3c2f25242bd09114008e5dfd3cbf3c52fb0091f1b315214a07b5479cec37b7a47802c5c01e668960647181d85101c078c47b833a25347e5e55895076977d9a081fb483a54a6f0c6051e965182688cdf9f89a98aa2bcf117908fef69de2acfa7bb66025efc100c2d46c03055452898b990ec20698ec4250e06126e23ddc76a7720822ac22ce6d1a6b915c2ebf4555fc69d4743dc39f9040664a7247476c848dc11dbec2386721713445d2b1f611a1dd78216409cc6e2d3da6df6a8aff2d1e4c3e59a1a2f7d143c2b35ebdfecaa10d690eebdf4c7c6a03218d74d0e53f6e227d72c070dd329421c9070f100083d2fed8df7a627a46a741a1b06d106be25b990de10ad8713de0c783a66b3fc6a20e90234318ae816cf0f02cccf8b5e6fde327459d8cabc8117f5c640ce521091494731dff2ae38892a89c34b9c21dbab4d999fb699b077f2562435f17d42a209a93cc046ede4c80cf046f6166945cab259d56a0a477a78a3e0b009fd23e219bb513fd462b9db0bcb2aae456ddc06dbd38ca260a65ac13b50fc71e6a08f16e6b81bcf426a3334d41c7f5524526bdb3c8430bd9c50ac2eebc7fc0b61aab2cc5e9c16ac485417bde40617814c905bc7bad1b9f93b30f705e8cdbcef093c25c19fad8d1885694a4df2615a5bd0544c481c611ca3d6dc08f0b1980fb1178412706435437b95314ff7ae0225e4f7f9a893d98c95460acfa0dedb129b3edcbdeb64e298d294fe9b9723734538d1a49a26543759017503da47c2f2e402c78d9ac5c721691f292ba2433df915acc2a5a652d435ff226f89ad82d4a878246f4ccc279c4be5ad07949204799bf328845ea2ad218cd4da172d75168e4ab2be4adf32f50a5fd6b15027a5db36b343c4db31edfcc996c89ea87c29689c58b48cf46b72312f416d11954b3626d98005af89cc2bc2ca34d7ec882407214ba12a8773f95dbc1c0ca646295a83f76c6c3019e03854150e5ba115607a56b05ea97711bdc3d805482284d5e74ff2abe116eab0e4a943b9351d39e41c3f29f6456b105507681b5d6a5f780919b977483365040d368855885f2ab46e5ac8f29e11e7f324bc82a1f64ed01ec4f7f3888c665b34eb275bf068f9d3fa06b1e77dfe69222412dd15bdeb2b08739aefb9d10e5017405806fe1a3a6484b20b6776ac3db078a2fb7fae0be339fa5c6cd936e2ec390fa42de5cc339d5450dec9f586acc1206e6ff97819e2a30ca29a71be3afc18e036b83975a802f0ef03a90db6fca0b9864a36251679b5eed17f5f37fe0c8726cf9df2313c6a54460a39cd6467e4fcfc0296a78c692a0c47b825165097ffbb2a53de36693a049e6adc7523eca6737748601e4c35cc8033909ee502852ed838886048b2664f4744bd9d5809a486ba4def9cb6be5021e71775e544a7e060e8dcab300ff427501a1e39f0cb0b823e73dcc07eb714820a686ec76795ad0dea05105c66fba636caccede7c3a0863dd0688dbcf92de66f7b2c33d6f23dea12042173887d0b43dd1676850d0100dedd02b3139eca4105b64c168f2005514910058144e34780d62701c2c925bc30cd85fda2e93fe37b797630b91fbafab1181630e70e45ce4b7efdfec087fe31e5134387e865fc4e47773629c01e90f477414134466d0a98ad5a0ed8fc41e8c81eb162e068c4165027681f08d9a9e0e78e3f25000b2b0cc546cecca2da9eb81bea0d4dcacc814f073d9e8efad6e26dbbd7343421caea293fb2aabb615866d670049283ded9b6397c5df0c339c84a72e2e29ffbf6148522f74febf91f269490a1ce31902810967cb0c6508b81b068ec4b280bdd009301807fbddd0f84ce622a80f094dbd5868dcd214d55414f685bb6976eca808cd6c42e16d80ec3916596da24c31a731cbdc80aa51caf6d1114aab44604657cfb633ee1ba910b4d314304acf879c3498924d1dc7c48bd986307866a9c9e3991014dd752883c08c932831ac49eef518c87998a08240c44a27c596959d9b090c6056d8744f4c605e49f31c38fda7a3c62411750d95fe157748ce7c868c66cb1025121e0645dba5cd1a8b6a08c5b8cb5e3939d0adc4f15022c4c318065463dabafb62b9a8f0f20a7cbd44c86c0180b187ab22f98a7d35b4521367ac03732761288bdc36912a2bfb8d0c44b9d99d93406276b558cdee0d69990c94f0cbd866d6dae13d2e6ece317134f00e66fd08d0dfca7cb3d1ddeba20b62627799ce4547e1c99a62c6f1ea680f34adcf213c55cb5d5aab3497d90ac89d7fe461ff59fd3aa7994240d9860e9b4dcf59a8fed33825f8e4556d95135b69f7c046030c5b4c342ac20a11a5da9d601a45f1db42e503327c86ff24d5b0f97ce45d6545892926ceec0f441b11711f2d098024833dab0f26c43fd535aa41310754df2b0137f55ace97e0a2b113b52ceb98a6faf6a6835c2e0091ee48b0aac5f046a188e9447e8bcbc476914c73a9712d4e41b155cf2e1d82315efd519328c0a5528671198d360fb249c04db4c0f328a583a0528394b019ab63a8c017ff0864cdd3cf53b3ab76d5443dd1750cf806d81f3706580d1f107b29e0f37347909aa5e26ae36c1707c8cc1167feaa1834c25ffae7ab1411a07607604c2b5d978cdf51d03056e41e0ac6e3700ebff0fa2fd008f6497133f3edd116539f66d296f6cd25aa5a284d1e3b643541bb18812092ce7cadbdaca3189ab97a601317a12b3a3f29e47f338c950accd3e1079629d5dfcc40b06c87d014747413c83f02a41c59bae590fa251982e974e0a11451b1219c7a3c605dc7287253e29ec039d5127be398228ebb4c2297907c0587f366a7d3406589ed8b3ecce89df6803155098f5bd3e40692a99f0a0c4ea0ebb12a66175b8b488f1436761ba9336a2b80974cc89af07fe9cb0b53c976c588c12b69ce5bfbabd172994d4086b48f80ad80523d82f70a73028ef8dd542164bcd118a2685d650585cc11a8e57cd63926981851f3b0d49559d7234d3e61ff8f3859979309be98a9cddb78d2b571c5c622b12b2b151c6bea4adf23a42ff101393101ac4052628486987c0ec47df25532e40e9f931965e3b135a1c3f543752febad0d5bf38e6eaf8bd584a9d5a348bac32b5d5939ea50a29a97ddb46a2b52bcec65423495533e15c0c75f35593aab8ab1259031bb07671c8ace71996be3668de2c1a15305bbce4db594fd410d27c9cb89ec304139d9d168e2ade362a8107e8f9f3cbb63decdf4a23f875d88e9625839de55465783bc6d9f7c721e4dc844b60e3a09723da6e3c0a76a2476027ccc3b3ef13b8fe6448e15141e3320369b440fa52e344844a03218067a5a0f435026870307a8458508fb9e15dda2bd52d9dd6dcd2312606bb544680847e57d571d8e71ffcec94ae7dd6097a2cb3716a4fd90bf935960d9a44b175a60d794ad78b757836dc7e865baf5e4f531de949e70a1917932503588819d246b17e5b798d75078569fff3a30cc8c9c52f265bca384b3eb8aa02fbd3e85e110c92010321d72040ba517f729be6cc8fb743e4d429855278b501c285a453497fa9fdd91425f4ae34f18dcc0903842b425cbf862511985892c0464bf1bb2d5cba68f5972992f0992d9e50b04cf10dd7904942214bc8ac2a727dcb8740d15e0f3f14f24b5c88684c2413e847c330d2cb6948a060bc7894da9ec75dfc80ef3105a05d7e8020f30096af6297179d876c20aed32ddb4706c4181929e9118fd7411d03afed58e614760625bca38e3f60eb0cdc129e6c134c541d6934b4b52487d7d570b023d257901b82a8328338ecfeb2097148fdb0863e38820dd0a3f5b60b0af72e2b301a1e3c030be54dc341ec81c232920ae897ab12e9d037967090a55c0f033d0a2a7ff9945260d61f7bc5f0f08148706a80643ab38abc4510424c024c48804680f7263a0fd3906446323e5a6500f3dc922a6a32dbc0712c2e478ebe597a430cc7a4f29051a2040b4c88210155970d549a4e0e891ad23cda8a26a5a1f0a4d4028a32f9c0cdec6ab8505f2aed1d01742f20098753a60c990e6118092870418961c58ab965b706f4d7d3e854407eed61b33149e9457b2aa8e2c451f5eb4781488dbddd5a7193934fd0c159740075b7ac5492f841473d86d18d4c1b4e3d6692e6d0841934e7836c80792d548edd89a8c72e5a61c81412195b586063ed5247926e772f5e29300fced024a689d8623752c4899345af69b5e819af4b6add870d90e8632d4c1f87bd25163e99b7bbe57acd0e352e6a58e652af0660fe91e25fa0104632df225ee104899b219d7df565c78e5bf8e7274153dab03d1436760185990dd1f249dbafcab550b959b4351c3c318c638e2f3b48a415a546d222249ebd1496c4ae90efab227bacf0050cb8f1b59f2b4cf8c21ac9c6884756dc49db1c7d63347a110b2dd7c99e41cc686373898a7e4bf39b319222f43b5d2d9a7bcd364949940269f98602980cd64bc63a259d0935407463267a9f392da6a7a6aa5702eeaaac624efe27d360d4158b086e24ee243c3bb91499fb3dc7bdbf2d43c22d42377305136f0eaf1f2120615999034c798744bc8073a4ea92ac45118dbdc0109b51570c7aa8c68f7248ed8248d92644e4cd19f80d5424b78144392b80bd0a6a2fa26647cf958da8230d6a49751677144f6b8e0291f8b0e90446794ac942d30d04a1c7c138b5975183c9501e9b072e9917f4c88fd9e8d2bedbbd26f1cf42a1c1cf6dddd65478a22ed63431fb1a4d9746ed8096a03da7af7519ba4da9ffdca676ed187d92dc9dbfcc10004c8daf7aa631ebd253df0d88b622126d13db3dbb57c50eb6265d5cf16fd9062cce13f275b87e5abad6c7363112484409f79b518e957bbd1b205b0f7c16165db760f03df9c44057a28de50c200052136e6a6ebf7b5a1e2e527959345e9f1cce5d92c4e6755df175fad643a462aaef562b3d013bda9896a47fa054a80ee8bb84237d6200ae9482d1ead57c369e5aee13c2eac391aa268d22fc0938336a1aef967827095c2db4dca77fba0205af57055f27c7c32b7886d72bd513961c7a274909a428d7ae788e72661d24a9a9ad47d42177112fd570ba2cc62907940a6d989ee44efd47c69ff38da1e400f76f3b991171de02459eef153ad5c8d574cdc59e46c0fbbe4a3473b1ba4e326fd71b538f07ee2ed20cf112551efcaea61bcc99f856f8888bab78afda2bea0ea28436dfd2705efd2d503c32d95527640d46b809c69ec016019e6f0f79e0335861592e6b90bc1be1600123150ccbb6267e5d71af0ca5eff72a282acb556280d979746324e182b71b1782e422b2b72c0fcb2c7690b8bc47b08843c1af11074c7238d3caa689e69743b9435e87904126710f3795b4ac6f25ce0a66486e86d7c81618832acb52cdcf162c34c336aab183cf12846f1b5e4fe956f250d0ec437e55874e21058955ec5f22e89b03b1530ab68f1c4245883277d53224f2dba9372d4456a3be07cf98cc32c414f9d70f18d2de61bfbc6270528776d9542c12e1edc37c9df0d565c818bca6ccaa8f5467e49498398ac4b6b1663aae2f182b2b61e4b51a235f880ec5193976956cacf33ff4862d521f6903838aa32e590419315736936f442f74091e9312d1df638195a0a191f2752327efa417d1ae2f7276569be2b4e28deb8b4dd45dc121f9ba3d142850a635dd5654ee704d0e3a295bf69a7ed65fe79077a7cf3b76b318acb024097520d505fe5aec52d5c08d490cf4d613749a5383110aec14c901761cb056fc74f3abee5d20208798181aa3e28d39e4f18870e9c6d1d6cfb4782d672cbb3f8ce2b36cceb7201b7175266ce1bab62761aaac623042a67c49a5a2c1289f1faeed37ec88d57cb158018ceb9eb4d15f64fbfb4d4d64791de7432c0a1220851797cb4a632813b94f088bcbcac4abe06e610a4b1ec39659868bff25fb8de228e71f5ea540c7f8a1fb69a0550a18dcdadb8a0ba611635627746b45baf1620bea216b2613641ca3ceea465e215953679768696de92ecd38aebb2c1805bfd615c8657440b4ea5975566bf26d3cb19eb9eead9b91cacce90ec5c0771755250d1cd2fde39803648d81352e3dafb90a652d4b5af7c3190fc82873835b1a31d4a36a5d3093531ed615850b66aec3b5449423be54f17328e7a16127bcd1ad4cb304dce5955dd1d011d8d15d46ba4892a263dcd9be86562fc78151d32e491bb4e3b3a3596efa1344211afb9d5d0f205e69840f47f37e12e7fb7bf5735b8599d722ec8193241838f828799d5f5378e43d06aa7aa315f2b0185219ba08e563f82f4a50a32c8c6d7d48d45064b794a0c6d1264ccdb9712d39e33aedfc1d82c94a39701ef9f183c1239a03b012bc8cc3e8a40405cf5ac115898ff029d682197c7c349bf1cf9642497dce19a9cfe8a032e35b782efc8795741e9cb934ff07ed0358df888992134bf6d0b2d9feab05c67fd10dbd24d55148f0e825f673f3958282f7235e839dd1ee52e6d43623a557ce0152886f01bd65f309494ef4187deb2c6134db3649c5605c9fc5997bfe7c2f39f37f40b9a2ca0706008576e1c240dcab0280da226e0de697ee9a2de840bca9ac507894f1302b18c2de24bc77d8c0c3108289e403324fbc56888a750a0bfdc2a3c21c2f48a21bacd74e4fcda98033c5fa2daea57bf3af4f6c1e0aafd056c248f1beb48f17962e6a8113c22eeedb15997d2bc69467088dd4d7edcc519fb36d93fce07f13f53d32ea5ec781d7679f349ffe6490b0c1b3cd7cf822933b0383e639b145f578cc619fb98be6d55e373b4488441ce087bd78599ee1644ec7e0dc7c169c529e86cb4011698530e562910cedd840a9eb61849406b6a27b594cad486c57f50a67dbe456a9b7c1744e3e60d72891464770b7a50436b8115031fa227ab70170e960748fb05e9fc3fab51c78aee9d805654f19ac225807b5d3bf2fddc30e49361b576f11ab05ddaa854a816c04dc14376442d21657983b097d68c2324a21f2f0af304d378edd082e22c2609984ca96617e8c43cb5f1802cc2b350e0b4b0b768cac2f061b85437bddcef9c48f6fb9c6fc6b005d895fcb798bae8bb604d2a9fba5081c55cc11b96697ced4142a706945bafa111ad687215b4aa407aff3cd0fdf98cd56f5e8c7eb7768d63b16f20a5dd8f6d8a3951658b5ab48772a3876b633248872b2eb8341ba69489121f4354a4a9961a941da2a8246b2304d340ab1af105502eeaec973f5db55729d773430c14a00200766d175d1cc51d9768384495e319709320c6efe50e9acb1703ea79237cb6d7cb85ddfb6ba933498d8fa36f936ccaf8c85a82a28f214b04c0b419e444a856fb2fcf8c20641408e26a67f18c0a68132574c260b580b429d8740d074a3240bd0182641c8cea043e55bee357a798aae228a55e86924a1da2509a5ad0f7a243b81caeeb662cdb0357fc2a7feda430df1ac4c7d3716d680510d9faecbe152e64a8ddf65baafbf10c0ed442d02423ae63b17c88d630029d458782ddf224a28cfe7e9739e7fc5026640609806721612a47dc91e5a5d8f8375f4ff0eb186fb50000d4e5ba3af8a487ba8d46cfe65deb392ee628f30ab7c320b45a3724ad487c03fee0887a36a9d8f3950e84bdd6b802e01ae8e3f5459c73403e88624a0b3931eda800fb690dc0fbdc741d60d7de7b8f01031f8734b34041eaad2338dcd6cc544a2682ffe6e7c02613a8ffeec91fbf83cbfe75382f0dc6e7e9590eb0c961c6aaa6e78ec2760a0625548e59ba705b1c31f91595841541cefc2612ae76cb9a01f4cb38edff797470a414bdab7b28cd4441c5a960ecec11df7a017863bb7666d401e70918772c58269b6e7dd5ca4ba36e08d534df440d75f11400714bdc27af48093469a78e7c21f233bdc260b7ad3507622571f5b8c41429276582640c9348208ea080efae161ba1210237284fa6b4f19cf64d92aab1d6bba2bc4a0701a548d53fd997ee4505967a16ada03368236222bda00d6f961d33809153854b6a22f2ed447867a2151ab4842ee125be7705b4c5cfdc70a36d7d6244e20f9602f834194fe8c626881b6aa3335d7e000d767d3294ce945b7b38485db47aab142485dcbf70702649ee8cb84c3ac54bfacd2f309c5c6533dbece1d774ead1a857194239b07dcb14d97d3f416b7c942f1eca0b1d943889995dbb66878f50971be3a387e62325fe081ebc2f372a59043173e5669dfc34a61c7903c564eca80a04dda0d90276be186f89cf6f895e33638690d16d2e1df7b53ca0cbf1f707dbc078f38d171078c1cd8c759cb5914ba7711b6b3e5cbe1a404377a681b6e14bb2421751a8d908f3234766e34d2e834da4c88c3a9086818fc28d6e213a73025b0b4576be2602193e86e38c88c443e1b8fba8100e62aae84bd8e7af742d27f9ee1e680ef10d7f597532144be142e53d90f78c7f979c0fc1172d5ef066eeb71b60b7452b2fc6a87f81ed9defdc3418abc4bf9610162b2380c113f75143a4d85422a93b44e2d50ede6cf890b5b2d39f3844194d2e3f96c70907449568efe72549e923289dbf9f3068ede75ea5a07e051d54e91472efa8a006ef6894aa5f9704d1f536dabb8d6d7156e249b232b2d7e865b2f5a8c11d1e82805afc030a780c3a9696f4d88e8d81a7d77df019ba37ae6152715edd15144abe8c8e484c7ecfab0d624aeba8a939ce0fac1afb7465588012f91196172d62d10382635cdacb58af9a9b0b8a5a14955fdf0510b5a65324434d421abba887bc00f47ab805a85084e282f788b83dd9a64620358e0d7316590585d28e5f563bda56922a8749118364b8910204a1ab9c1e5e55647247abe63ef13d13413a667c63639d7c3394e65fa13de103b4f36255277c755d3fcc9ded573252b54eb13976bf39bab33d446edabd303d25d49e448120af5c23ae3b87720a337af52500d32341af8b8300c7c1815647a3974455f3b2836ee48ee558d1a6e596428c6df716f7f801efa11daf5aa33a4fe45705e395eae28cfeb29e4573bf14d5a6be3e8711f50c3eca66f37432b66e2c98423b4de95e9a61f18214ac8de1273149816185a5c3c797b877e41b0c96311f9187f0d1f4201b06e2520ca2187407381985e1b403bef07d9db6a45804fbf824c24bfe0454c733a71c8113e0961e1dd4cee765db8dd585f5cbc3d3e55b1a50a964a3a3e8a0164a4e74293746d2a7950728f8b38c7259c9dc62f78ff78ac9e632f6e6f0a8fa1db2508ab0c7bd85d7fc6260508106f8a2dbedd7edf159327d990b253a32c44051d2a41ecca4043048a974b0027acd2d736e67f4e5f0b9909b5a0f37cf440280e57ff2d5ed824300df6234d67832768308877416f5180f61c96a1b9a427f604402f3d031cf4315d01c62277514a8698b9476139a3e459ce0740ecc4e13f7351e46da49dfede9b6bf83b08f1e76d917e3a51c82dcb66dbb31958f3f086e70ab726f1db8115b77469a163fe3dc98c88814115c78551c011ec66da45ceefc07a8dc1fa724606c96d8020aa47e09d6bde8e3e29f99cf09d61fbe3fe2c0621a2f7c73e660c35f492775b6a13c4caf399d1e3070d11275c92497602d4eb05734730b76453e0033a1f31cde2c7b87989e81084cbb84b3a66d1d059f4892e2c6be2976db7b981bff440800246ac1062f9ae5ab8edaece3bba3e31683b14986dfee2d23e26b0390c5408c6c6b593a5b5b4bf9ced44ee174286f5a6ca21d9109b18984aea0c11f4bf091846d1db507c523aa42cb103fb2dd60c3ce030f0ebffd64cf129dddef80818efbd6506f053c4445c794677891ad3c9e864e7347fc1f456e3951a9d14d9a9be058b9f73947b65f1f6cd001c2b970d090bef270c52da18128f1c73c791a6fa3f31d2e527e6f3a3ffcb145c88899b3d04edb0214ca5ee5eb181a888d31141620493468e1b97430a0927150220925a16ca59ffe804af08c1ea12f706f7c463f379c3cb0af1311e356e25bd47eafd3a35fc8f0e6455847908c7ce7ea337c69c2b7cf1cd00648968acc669e0680438b9f92fba685ee09ffda9c4c21309dee92143d9772422fe51a1708a1ada5698503465f36cc13aa1d835f7379f7ce223f5c631538916940c440e375d22f1ec7897fe07cb80f40f5d7d8d3813694cc7729ba4299d01027f81ad0d6f3d628e412aea186e5508e8fa3071f2724cadb1d808baa27d20880d375c61090357f91da029953f424dc06f3efe744ecb7eb72f4fa150686d5407d326c4842ef146a0eff4852752c068e495487dca50a8e5b2bcc81612061d58de41baa3066642f10cecd173d0cf619d1b511915fe15800d0914ae75b7ad69591694ebd273d5cbe6680206c8884f9e41f948624aa762b88412dd3d99e1b11e76783effa23e5696002f44fa0494e94a5cf9b60ecebd5d9d64f0681b91ad0c9be5c58e4157020db5de81f06c2d76d3da574547aa0687a3913b69e088a97a1619651f5f2da1880d9aff817b8a33e16a709e255235692f8cd61dcbb2ca36b7e07d9b288506090a286a28924107c10306e14d2521402b54eb5142b2bf0cad02378cd271d4f3564dfca9c84ea1f5b6695892ca52498a72acaf16d1f24ea7247498e8bc09b1fe19477f45284a3d76648ac9d6b5fb28d265fda2cdce0977055da5f99dd39e0dc4821e21ee4b8aba6e03c9aa09f6c71f6cab0301bb565b7e20ee711dc7a849cca1b6266364375d6f045a57f55e74d21971778fcf4debb85671d42b6b0a146daa886d80ecbae7a51a3abd9b15b37832c988884f1820975d824c7d7cf71ad4af38f46befd91649141d65e30384181599e1b2543250d2cf37c8e0808f9a3813824f43a548bdb33b416c03eb8c98a12a8a21d54ee7632ed553a7bd84da1a6d41e3d42352ae15092c91bc743309fa4dc8fbc8ccc962949f7ecc45902e399633ae71a98b78ce48f9cc3599ff9731ef83f15529e040159491902bf1d5e1805c13aea94186e5bb0363dd61c9055574acfeb2a9a1d6ba80350f7f403b0748ef3df466d05c585d1ab4da3888cd48c66454306c9eb99a07295635523c9513759c9861ab25ac6e2d3ac4822f941e990764364a2bb81df79c055657f0a36fd9a8ec3020c0615c3747aa9fcbdf6e52c90c5d6074f6369e14c5d92d0c81f75acdbfbd5da7d8b49949152619d5ffd543ed5477aa76a8367807b7153eabbcdc2a01fb09da5cc46860cce790f2344625f524fc504e16595943c0f890ed161a89a037371011ba0a1f0f0e1184e3dc404c08353fae7c73f64f9aa0263da1138a6377b657e8f11eeef625f021a52f3fdd502cf02048d3c70ef42ba2c4ad48baa15f915f88e324dec2481080f6e2128ed1ff940ac5f412d89d693fc6cd38ff944b7f5a297b453dfa24aa326ed4e9aba97e72c8f48d346da6c1fe46c40f03ea21668870dadc160714499c8aa9e2f1d192d80a1d6165d471395b8b70af40d8f16cb3f27dd8165cafe11e089ffb06a5e11f3532fc512b2b36583f5f70cbda50894f97333a1473135c4ef4adb28e95dd8828a01b9d4812c91efb92026f71017c0ec004b8eb460391df501c329f4f9608d0b3ca342da857199e02d71d1d8f3ce3af7961f982c56ea62efcadaa629936ae0d94ab3726a0da893e9554233e8fd96f86918f98d9d416624a180092463d5597f391a4c234616d74828fc919f86fe6517321200988891736d76da16e7dba75e9be47971e5dbb75df4d976e5dd5e96eb6b4e7546f57b73fbaf42dbbbd74c4b034701721de6bb68bbebdaabbdebaf5f8386a81cea7e5f34f4bd1d732ce5662e4ba44defc8c2c1619dbb127f9d1156d912a8b382a13962fce0a1ca663dc3a5ad7d455b725d9c999638c0201353038ef1fe174ae0a317563ad7d6b9db98d9f2c424a8eee521b1c471730b30d1c9ecc04ea4095d5528989a6a133a4eedc2525f8d03d01c918954cfa4277092fbfbc68cb63838143b244d7a0fd1eb560b1e58c139d19ba49f2f254e286cfd42c6085b24c81cdcb65d78a1a6d47fd3049cab59c886aedcc9904447ce68aa98bf11bbae24db2134ad52327c04be2bfbdcc940653e8cb6577a26e53d282916c2a9af1faaf974ba9beda2e1f01c1d3261e702f53f6395046211f579201c3b6a893091d469b86b57c95488837a69cf84b38017e5fdfff7f2d5996ead893951120ed28cb083ba9b1162fc6628e805b4791f2e56dfeb21b5ba2bc2df9c30f7f0e67a417d37e9e19d252ec3b9f31c89723d5e75e4b2d49e85736253e3090558a4a505ab52292b8a1364d6e234645436825ff92ae0815101052406d5fc16bdf9a58d06ac7880bb791b18612a4aa64fedb6df61f05a751d944b93379ac178cfb1dd67bc42581d513eea04b25e1303d49a3cde3f4ad7dcea809780e473821667e2cc18238ce13020629eb8c2d8c8ac36deef0bf92599cdfa6797811d0e6bdb4d7eb68fd61d17604b8006781bccb5e5ad7c87d1e977677297828bd17608a2c37c122aa9cc7d4b8f6358aff73b490c06adda264aa02babb5b7e73a9808c4b8b84806c9b24b804da864ca9d93fb369406eae6845b622e7d5eb44070dd8e1974bbbd82934410812cd353bb909638689d11e15395487272806055a55717743284ccc017a5b27853070b4940e000c30c000030c30c000a811c068cbbdd901003441db006c4a4a4a8a1b17c34ad606c04204d64684c6c5740111088a08eb07ba67a01127375bffb5e871f44ea8a0a1459fdb9f1b52fa351ab6c52c1acd182219c3183da7b5852cfa890cf3c78c2c3ac61b046c118bc62c74fe47e4b895f1b0e8a17a8efe57cdd931f28a1ee5fc2ecd96e18a3ec4f1c63c34ed49af156d87cc91bdf8cd2503b66045ff185c94d2148fc99d55f4e92077a7f19f7cccaba2d90e19b224f3e81049451fc3587769d0985222a868520665e6a153eb75f0147dfecc1ffb28d43fe54cd1f67accaf7a6652cb4ad1c4841822a85f4e1749d1ac44c5909daa1a83a8b718451729e4603e7aa1008ba2c91ece73d60c14c1960c78052958c002104080048c60051430818fa051f02cf8c316a1e853c393c4c99bc75125822dedc016a0300b6cf189c653ee144692a58c99819ee884d241cf63b80a27ec6cb189365c8a7c1015ff93e8c7852d34d1447a7cc6282b3b8e99e853c51909513a4c7429c6fc29b8bb97e89159ebc330329567b444ab96730c21836a54a2efac12b9ec5aad1975c21694e892e38dd9ad934de4338926c4e48dfcf954743692d82212bde77448c6a1d3c5f2256c01897163b18a38913ea28b9396e3fb6a92613aa289ede338b998ef65f6a891f39f0dc262446b2da97b324216d1e588102b12b9a13bb78522baf8225912a721852d12d14aeede8f5d0d611f8e88ae716758cf980cd31c87682c446a06cf0925a9628836c7148b50113d32dd2c44e7fbc0273ea892499310cdbf4b947dfc960e7206d145cec6287c8cf078ad236c218836a5fcf499903a659203d1698e8862bea6c1720b883ea34e5afe383a3f86ffa1f5fe7cfc9fa1cea5e8875e4ece232e57ca167f1f9aefb290e52ce3f9cc7ce82c2d4af4f929cf921cc2167b6854e39f06f79f74c919610b3d34e12cb87b9660953a9e875624af7ccceb7ea07cb0051e9a9f8d7fbd9c71ce5c66618b3bb455d92f698a595a2272852decd066ef24eabe2957c8963a34293639b08a7e10424587be7189745f65af3ece0a5bcca18bfceb7d071233c62b87ce71b82c9a2118842de2d03a1899f76d89865ac2a12b093f6b6d314ce390a9b0c51bfa9c7e292219e63cc711610b377421a358d9b26b869a32256cd1865ed73ae7c773f605890dfd86096a95dd71742cada1879da32bd3152d46aa861ef4a71c1bf2581c6fd2d03cc6f173fc72cff80b0d7dd8e0bffa7032a34e9da14991c2f563cec5e766861efc7f5e85bf0cede543df2c0e3ee64232b4197f0e951b1bce9e3c862606cf3b93b269c8be8aa17560213ecc181286d63d76792a09c9424660e852b4c3aebcd12f7429495b876a6fb6895e68534a8ee54da1916ad785f6a232ca7b588998a25c68cc349557b098541cb6852e834832edcf0b39635ae837c4efdcb3f9915b9a854e7b7c2df36ae3980f0bed46648390e921c9e22bf4b94f5fc343d10a7dc631c404ade89e64af4257de8d33675648ee132af4e220f959d0065b294ea15db56425c95229f46b793d438fd98e411a853683bf9ce2388342f39343d47ad5b169ee094d7a2e0fea9ed173684ee8c2a9e6a874bd09adc588a771c2c387d399d0c99bc857ce7d315e5d421fa366fc95a1435d5395d0777c24daaaa924b4129fa2f59e67cfca21a1772c71f2a923e68bed08bd84135735ad8cd0869858b4905166fcb3083d6a14f3a470392a894468265b9e0fb9cc81867d08cd49528b214b27ff09854045c72bd2132c41e85385f9941971924309103a7f14a29784fc8362caf78933a3b0850f9a7c904365ca8f2d6edb83b6f4332f1ea778d086489ddce4603ffedc411fad2169c610e64fa33a683cc85f548bd9283e2a07cda47a92164fb7c0419f632aacca477cef788b1b749a23c59ebc3851f2dbc2067d4a9919966557d41fb5e8a16f2cb1b48e75cf68d15774982b8a9986d8e82c9a3c3925cb8b5316cd67c5f1903ffec6ecc6a2b9d8680c332addd0102cdaf1d070d16c7945a37b59216a85ae68834ad6d4973c71e3de8adee25e94b2866592b3a24bed712efb649595761548ec8f57495a51451fbdf17cc61ea5a1c452d1b669650921e555ff0b156df89c71589dccff949ca2774fcf3e0e3d486bca149d07536f18e2a77c9a2d459f2304bfbc1d1df385a468634a19b9a8e5565e3f8a5e7d3b2ac3208aa2539387a19ee43ca75828fa8c7bbaf1977be1018a4e826438a3a7394c353ed1ef34b43cf259ea31e589d63322bb3bf63583db891ee30ce9bed93cdb839c6882a49768fa83f1dc6ea2ab2e8b1957eb5aee144db415c28694f2cc99483c7a829868a25a6494bc217527f912bd3cd269733f13b1de127d6ec8e6293956892e6e8e6ca2aed5d01c946892374cfa59a911369d447b9acb53a84e128d4ca3b0ab55d6dea1481ca65651a53248746255a119eec13d858fe8e225c78ef3c3ca2bd3117dce58f344c78550f236a299d0911be69aaaae31a20dba0fa7a15f8b6824e6ac67354d116d9caffc72a2aa9ddd44b4e32ebec144cbb38c8826365c2ca6ae58640ed1b7ba054db9c6ff2965883d3c4aade22916a2e9503185107de4642134677785300fa2cde0d3f02a4355a4a820dab896c2a5b428104dce30ae86dcac267b01a2f5071fb4f16cfed0e5488a1317e2391af343730ee4e286870ea7217de81faac80789d39973980f7d66958d6f7c1abe617b389c7e4e21038beba1d55cf9f02776575e9587aea13c508d241efac7eeefd6f9d79fda3b341222412643349c4e7668f5d27a354e4ee88f7568d41bfff707df8f5fd1a13dcbe8193d27c544630e7d6a903a462e4e475972e8f5636c8b1e322a8b2f0e6d869cb099a3b741dcc0a18f9807b11cc7af56f00d4daa14afb9b22d69446e6845b52a5ea33bd449bd21006de8b256489f94d2f3f5168500b0a1effc986415412c4eb3862e83e01d354e7838ce8c206f87fe81789f47920cc6e73a74a12beac48971533a8a0e6d568dfe9f2d5812499943e3e11fab727ce4d843e4d0ab68ebffc7d37c968c432779a91eba7c67f385436f41b3e9594a6f685f3f439f881d1fc7e486ae4cdb3bc3dc86def5655a1f57ee87980d3bcaea9e35f45ad9a38de14b840c574363d528945e76d3d0436fcd791f5987103568687522ae4fde0c8f816768d6db1d26f70811276e862e455586af61bb1aab65e8413cb2ee910ab30c2243f35932632652a4451f43173dc44619e53f5e85c5d03fcef961f3b4bb832f0c3dcede3fbe2176082a83a1c92093dd93e867c8e30b3dc8aa93175ac79ae3c6771251005de8321f85594a898f2524822d8c8200b8d077a45839c7901b52dccc04b085f6a344de90cfd14227d9a33b33e452cd4b161a710939c429077a591b26002cb4f9ba71b8cec61f257385662c2442773febc88b060158c1aa18e42b3447f89855a11431a654546663eda8503cf62e491935f69ea7f0895c9491b8259659299017d753c8339a2838953dff57544bc6120a651c2372c33d66ff8462cac03a23cf09bda523cd8c8c4a2608a009bd54a360e9ae2122250026541d9fdf1a5eaa1d670967ac1123431cc9f4a98461f846742a034375803417082009ede5d8b1fdffa1537924343199ae04990f319a2482008ed07a945897e84c35040118a1d74851f14b31698e3110045084662ba74c1ec7a417727c1000117ad8539e53267f087d7a6811b5e887ad4121b49e83244be1435e574579104010ba8c7d751a7b4ecad4c6e00d0a3e50566f1000109ae85c296feb07a920801ff471398478297e5010800f7aef8c9a1bb1ec82007ad05b9ec8f1e15846cf1516000f4c25399ae738612e5b10c00e1a0719edc87238778975d087c951f54da61cb4256116727c71d0e30e7296d338e3b884057083b63bae925ee3c6552d55110460835622ee766ab1166d0c89526a118df14669d17810ddd6fcffd92a3e66d14399378dd10fc99349010810e07f052d10c30b59f4f8a43b7f6587a09f7906582cdaec2cf7e94f0dc388b0e831dc4d629553c6ef06e1c52b8cce065b0ec6b2cb22bd62f3ffaa75a5acec8ac67b35b664aaec9f4a2b9a90254ac8ab6156f43055ceb8dc234f625d05dbd132b134265c78a18a3e46caa951f87c3c9a118217a9684b5336980fe6d98df281519d78818af6247bca39c4c4245eb9c28b533439464bd9c53536335378618ac662aa6c042a700400195e94a2cbd0fa3346f85513408004c8e08c6005af8108d8b94035b0823741795382920213907100c8c20b52b4a3b2da99611f47596ef06214fdabfce4bc709a49aa17a26872bee885128f7937065d8002b040072f42d1eac36fd1e4a963ccf402146dea98f155d66e7ea14ff4215c349c2a4b95c7f5441f53e4093b318996981fa84e741ac9a7d362e304d79682ac964679041fa8da4df4919b935ec453045b32201f304341060a50009385179ae81b6a78c70dfd235c26822d05d08b4cb4914534062fed3832bfc0441763eefe0c834a5be97589e627e843978896c53f23bcb0441791b36c437ff06fa14a9492bcc295f57f3e112f28d175380e29755749caae400a2f26d18fa54c8d0ed7d7a03759bc9044975c2eba7965fa831791e8cd813d780189c3c6286696e747b4272251b7d4252cae8ee8c483cca8a4e30dc9042690c10a5ad0882e545c8fd93da77497e5032d781664125e3082bfa0163fdafce3b7884cbb377a5fe5caf6f042117d37b418d3332bfafa44b40f723d7262c590a888f002117dc6cda09e521cc18b43f4d93b3e4644b8decfbbe05d5081340abc304497bd77fd31300d934108072f0ad1676cd08b199385dcab092f08d14f0e3942a724837f80135e0ca27f8c41a444d1d8186604d146ebfc1349dd22791e883ec4d9dc38338c19a718107d8ad9a1e768f8c133fc437331a92d777898c5ca0f7d652509172b5a5cb082165c60032e78d18756b733fa820fb987d3830b5ee4010fb581177770fb736f5f8002f80a3050b78a1776d833b2e614d9a93cbf827f41f316295ed4e19827c348615bc3e274d0cbd5d21fb4aec77c0e85241242438eeca7be90437a713a4279ca97ce0426f0c22d70410450b0800abc88430f7562b53898ea901c0e6d3b0c0d7973788994bea173a0f966a973d8488d1bda98a12f8cc7ebe7e55eb4a1c98b9c98bb5286e8d00b365895691ef125d19e650de234e40fa99d2a075eaca147d13535344a08b1c2176ae8b22297a5f893c36f9f863ea7896cc8c1a3ba945b7881867ee432cfdc9349709033b4aded69514d6386d65ac6a28547e51b7e199ad78a551136a3e4284e863e7bce96d02ea2eb2e62e1c518fa2817b55f73a7f5a75e88a19dd4be1aa2a1b355270ccd47868e34af9ac859c0d045b9e8554b1633ce78f1852ee25f94d98cfdf1910c2fbcd087b0cc295e332cc478179a8e9faca77371a14dc957e9595115757bb185a69259c40d31656121165c59185e68a10f2172c4c73f29436fb2d054af874879debcb863a19df81e3bac34889fc9021480062faed07af889112722ff94d6821756e8f244c8679e8fafa00b50005fc1b3043af00116bca8429f2148f29e7330155a8b1f29fd09c1ad2322d84a410b50e0801753e8738a7e72eea1d12662c0bc904253ed2177c8a2d221df1751782b8b8956c6b2922dc0023496f0020a7e9271949b65377c022b9952e47811fa9e13f4cf9de2af8ef77d5a1ebc6802be608215005f2ce1f830e3f43cae793a25507933e38821fa7a4d2a2dbc48429b3d8e82684ad6d2233ae00512bad0d981a5a08d2258ce119a6e146172b538eeef18a1711cf2f195e5bcca7c115a1f0dadd25973c7f989d0e5bc12b9722c3a67cc10deea4c0dddc9170d085e08a1d1fc9124664f49245e82d0868ca715733c53f70910ba4c21c78c611a98c47fd04c726fe0193de2317cd069d4369f5207929af7a08f7290de604336fd79d05f8776f09508e7baeea0f73469ac66ee3184a983d6e7311c49e19fb9e6a0e9d8a92da2486fc6210efa5021c38b9af2556adc0d5a738b16b2725ed8a003c72315e98b412da95c14c10819a52442af1ba2ccc2f2211c1742e83aefa328baf2f0f4c7175810b808021740e8818410ea16278ba9fc833e68789037637be3f73e68b2667b7fbc637c0eed4163595d3e233c681de67e716439e939b4831e7fe6a00e72c22a223ae8fc1f4f893448a95225077db0ca9c1ce928e13a3868524a68387e6a2125fd067d230ba29171c7850d9a8cbba1697a9c4e886ad144b8c5f58b9c3b3492166da485a0ab39c54cd12cba6e20939232beca0c94459b9127c4a7e610b632b168633ee34fcfb08a8c8245df5a5182e536ee4ef3154d683f4c111df67e5c5cd1f9b8f86eca1eabddb5a2cb31b89ca6ee99d02e2b3a0f25b3d1718239ec57d189548b449bae8a1e9679bce66b53d1c5ddd988ba1df11aa86853caf27992cf3288e6299a38552ab37153347da1f35ef1bf3b4ea5681c8be5e4a9d318849e14ed9f79eaec3b19453b2a151df349249f90287a601172c3bf4a61cc118af6ad3b9c88ea4fe81c287af0913c65e74ff4d6efb9215ad60b21c4138de5ad488f90184be39de85c3d780821c4c5fc83134d499f74cea94d34a79f3dc710323b21d5441f763c895af846c828136d8cf951e6e82f267af3209372eb76896682e78b18af32498a30113725a9124d0e1d52ca30a41c9d90124d4c07176f129a442b9e4386cb8c24dabf6ce12375ccc92d25123db858e19366e7490c89369ae6e33857b54e9a47f4a92791376e46083f3ba20f91fb1abb1d3fb478235a0f79232b55cef6d230a28b1b91fb4c5652fc2ca22f09df71a2c4aca93145f41ede1523e88598e825a279cb7810c91be5184388e8b1c55ebd1caa734bf810edfe684e5e0c165763883e63eff75cd179bc55219a8d216b8964fd0cdd428856c463acca9c83bf3c883644f335478e1dd59420da4a19458d31ab6b2507a2d19d9e8cf462c7cf2840741e9a514453428e2effa10b33b1cedd28dd38e5873efa74849042b333c7fad0a6141aaf328aefc8333e7439775ae71e6fc912df43a37f792dbe2447dad4439bb14c74c61062ddc379e82c344caad141d47f141e9a88d943f4412aed68dda1edd37c49228fa79c658736a8c79aa68798fa31ead0475506e153e65392930e9d690a3d0ef23887f65f3f6c989ceb930e39349b1ae68168461cda8fe3197a3e929cf387439b62342ffeae4a0abfa1cb8e672eb6916ecad80d6d87aa6530f19ce42c6de8e364fe95e4316ff09e0dfd7f0817f2641c1ebcb78676ae420ee929357732d4d068b5c79332c7395c340d5dfc0e9e19f31c87dcd1d088bb9ab4464aeef93f433f9b3d787ef64dc7a319fa30bd9347d72f388696a1f398eb31c1a4bf572243ebe162d2f7bf4acbf818da8c8d34c54b2962682be49c817bbecec647189a4995b353bc08863e63c43055cdf3cfc92f3461fd73d44c122fb4213b2efa4746179ad020f89c7c0e49c2c9857eaefa5d2cc30a916c0b6dc8127363a94c0b4d68cb94a311fe17bc2c746e162242555bea14c1425f1edb8357675ca1ff90a81e72f058a1d51f4d92be39438b8f2af4214163caeaa7a77143852e83dc22392bb7418a4fa1cbaf99e69d53293431c3ee90518617ad5914daf5fc29a3cb50e8b378aba49b674e8a3ca19fc87a1ad95d62734c045b51188013fac89afe3197e590f135a1c771529e09ed37cc0ea7716c90d45f42bb9633eae988081e2e25f430e67778174b999493843ec376e933d94cf23148e8a57bd7a16c6cee851ca1ed8c3316654dff1d67842e3fca9974ce1f895c8ad0eb83dca01c7e86f87922341be61f0407b137b61b4217de11fa326728a6a5109af9141556821c843ea6486cf02c19f9fb40e8d2d7d73b6bf4079d37feb238417a32321f346995fba83d85ce690fda14396c748ca69c7196078da869dc080f3c553877d055598e5fa17374d0756f740c2679eea7680ebacce42fd59fb91ce6e0a0f58e8faf5937860cf21b9c217f4c5f42ca006cd057bc6c8ea1cab568559286a5ec2d2d5ab3c69132aafcf27c66d1c69f1491837ab604872cdad78f8d93cf63a3a4c6a2374fb9d9dd8fe2270e8bd6424a578821c4f490f28ade4198f611cb0ed263aee8223a8c39e60f392d525ad1768a0ccc333caed0162b9ae8d37e155d6390f4fa53aae8a7753d64734e45df30ce231deb64c1345474593de6df48de50937b8ac632be1442f08a299a721c191e4b8666922f45d7317b8a1c723b7f3b48d17c66cafbee1933166f149d03cf9a51589c69df44d1a514bf1b36cd628c53283a8dea2037bf7a68c780a2adfc1f2fb6e4299df8273acbfa8f634e33debe3cd1e5c9a83e97fb3f1aa7134dfe8c49bf62f88fcbe144ef48e6f3a7946ea29d8c33248bf9728c9b69a28fce2886661c3f99954cf49aa3668aeef9b935c5441b216c7a78b72ed14567e7c8d2184b745126c7a41d231c6f5a897e5b525c6d901be3a69468ce73d4af709e1bab3389262e58079d183733d624d1669e6821bc5fb8ea8c48f4e292819af90312cdab6793890dfd63668f68e22a672696c6114dfecc949672a6118deb65c661ed9ff960443b9617394a5ef5fd16d15be4fee5dca422badc334dd21f5295e24474dd2186bc55952d328c88d6c1e68c2f557b88a6b3f1bec6f74a8d7743b47a3a1fa288669c520bd1564829e479fca8c32d42b451331cff55f79b9c8368f2a8e3ceef7b29d11244b3f94942786604a28d77bc61da16cf2b02883e88be23891c4e8305ffd0bcc6caace3fcd05fc6e76b6d621ffad0ec31c8c71cafccc387364bffc78410c2e61cf7d0a9c72491f35e0fbdfc9a68e8966439b379687db24f4b7ac620d1c143334935f5c3f4f0bac13b349f0e274b95cb6a19a9b583e8dcc828a2c9269961d0f7d2f8a312ba4844bf7a1a5d519e1e2588e8bf7be63c8e26f09202088c6f401787e8253354c8493454340e115d18a293d89d1a49a253632a449be9c0e3bd2382e882104db008dd92f906d1ae07f91e6f1452f376852e04d1bf8e6af7484eb75a02d16f8a9443638a03a2919d5839aa577752c22efed085698f255a9a1fba904c26488e29c42efad09f465407f91c3eb41e3eb9996ede98dadb432b53e153f69e8a6b165aa10b3df463e6fee9b06284ed4330828382149497441779e8b26bac637fd4090a07a826d0051efa2099a3abe2458e29b9ebe20e7df0d78e513374286fefbab0439bf95bc9933c9c0ae295a18b3a349f752fe722c70d5de9431774e8439e6f92289752cc1b1fba98431772c83b25a2b1498684b46c39e8220e5d9c6b309e277a8aed403c7401875e1b9f38ec8bd16bf10e5dbca18fa199f253fe324b961dba7043933be42f4971e81f5355872edad088ee6c65cee7fe1d3634153de4e5d421ccc235f419bbc2f3b7e5154b0d6d8629e48c3957529c4e1a9a559f0a792966cce5a3a15191768df8bdd69b33f4287ca3ecb9b01eb3c40c3d0c9eb3562ccd19f932b4b964757e839856cc20438f3ba520d92e92a2c3636836acab72c21543fba922bac38e86a1f3f81b3a316e2e130543531ab63a9ee70b9d749aa954c86fd1f742f31353d685364455ca3155d7e4422e349125632765b685a6417955f6bff05995167a6c21f10ebac8429fc26590bc8dc475dd3974818563c89e41e6f7d5eae20a8da68877a02947c81d267461854eaea2e2837c56a199fc3719dddeb6dea8d085f4d68c634ac9c263a6d0e7376818246643e78e14daac11953334f158311285b62b9e9883568bf71c0a6d0e4155e292e3096df2eff9b8972ee16427b459b55276c7acae689bd086945f3ea5993ece6b174ce8416358799aa34b686582c34f35ab0b2518b12693a29a23098de99f9585f3ee6c31116c0dba4082e2a79bded3a39b2f47b02abbe58a1961cd95db3a3a772749045b2e60c107ce0b5d14a1ddec31f31c4cf0065d10a1f3938c2956c434e86208bd69f438aa3cb99d1c42e83c3b861216d5372a82411741e83f6eea74f2a80c930584fe533408ae979e3246bbf841ab250d27aaacfcc4e583d624a70665d98c41173d38fafd522f28822d4642173ca05386262167e59cfc10bad841133363a554897c31870005e8a03d5fb7941c37fce3a4052c48010544d082116c80055de4a0ddf82e9d8267cd9b5d1c343bb3adf11fc75c3e3728f47abebb1fa60b1bf4fb41437825c91ed36bd145ea18d23be7a4a132e582167d3689251e634e2eebcca2ad081f77ab8286fd03de0213bcc9222e64d15e1691e4e9172ebab1a802c0a213f1d9941e62d24979045cbca275d1ce352bab50b941b87045a3123f06cd8fa1638b6945ebadc182668ffe215758d1794a67c879b3abe85b538c41443f935e45156ddede7c31261c64bca7a217cd9d62563f50d1e7f8eab03de34adef129fa141737f4c3d41da4550d70618a26eb666456a570518ade43498c8f3a04ebfce18214ed75f45845cf113aca32f015acc0f3045c8c02b9108572110ae4021455005d2e3ea1838eb953e26211acf9e22ab8800cfe018c79092e3cd1673031cfcb8430a26e27fa94fead21eae8b85938d1540e96926e996748392e3691052e3461f0bc992506cfb14cb4bf7ee5a9719a37f660a287f1c28154e8ec70fc257a141ac7dc2198264f164b342986389eba65ab3cbb12e8c00748c04525dadc11729c980daf5d4a34152ac557999cc93c9f44fb0e35558a19368e6848a24bcb1d427b78fc9647a2cf0a8f53d0df98543124daeb97acfe1f7dc4db171a73e629b970441f5cd671e675e32c9a3f70d1884e5e827a8a8db3354e6144bf9ede61b52c67941f8b687bfd3464dd8ad3f3ac3e507b7ce04211bd5ed239c97f94f3337c082e12d156fbb57778e8558f83820b4474197d4adfcddb53ae021400eb0345c6c521bad23c21b5c7ac21e7e4097c054e0114a460043318c10946d002154ca00005f88026b830448f45726810553f2ea60ad17b72dca95274f7c00521ba96bc10dd3b6507d1658f7a1252bb68c31441b42fc1439e73e412b90d4497993d21c6c999717280684df79206c759c662ca005cfca1b3f889ac21ca51dc1c3ff4d79d25f127c44989f4a155911c99cea69127ce87ae1cba5476871d55dc3d742165ece39ef3d812d543b3218a87df18eef2e6a18b7ed987fd7933b2e2020f5df46f13a914d7b2f959e0e20e9de338f963e6d64ad5d8a11f4d294ef020a23daa15b8a843fb302a72636673e0820e5d85649798d9433c0f99c0049e8005e8818b39f432bf39538a5bca8eda19b89043e31a3e7314c9cd0a713f70118726c5acbadf6072c3b94ce0020eadfe064d7ee91733242c8881b18b03176f683a857666947fcb3482810214e00a5cb8a16f07f9e7416b65d49dae818b36b4924dfcba5747aa418c0d5cb0a199a0297214efdc4a4faea1c9151e579cc851432f997b19a63a5a5609388071c069818b3434526ee618580e9fa25482920208a0a1b914eba8baf16124242c707186b63566338adf0ced6492b5d0b82a031765e871e8460e894ab9d08e810b32f438568eba951653cef931b49631f665cf8c2524590c6d909093571975a36086a18d934a3fc3ac21b5886068542c87b0a41a527fbed05e4a9ec9b18c0b2f14a707b8e842ff2f1f3f3357e8f20e17aa00cac516aa00a7855e92846472b971c8052eb2d09b24cf39a4a2c45ccc0803175868c37fa86cacec3ef15ca18bf1dc2a936c6ed9d80aad8ca7783c1bad42dff02acf635589b21f15fa0f2fff383b0c3ac19a421f297986ac5f7444090326052ea4d06f87caef74ced5fd89429743f9887b83a1d085acf0a70e33e74b9527b43166235c8e2924f775425f19840d1e37725d73885c34a1c9792f348639c6085c30a1d3d41a2ac4867e86e2127a1c6364678811442e94d079c528a1fde32209edf9a348d11ba3be8a20a1d129cd31db272332e3089dac7cbc181afec57d10810b23f4088891a8f4ee4402b240241088c3a160300c068db31fb31408001824268b0663a12007b4c10f1480033e1a1a362628162a20141414161014160c088442c1701808068302826020100a0983c4f02858f8006e46c45e7c8595611e80f12b06c584b131749360fa200db4e50ef80967c1e9f17799a7984f3f49c546f61ccd8d0b435a58cb82c69a6e226252af826c9fc08905a2303efa85c4ee29fbc680b72b10b5077e5192d40c8452aab118ce40b481a02ed5d331c293563d67aeca8d920c09068da55142f5cedac8a5eab088cb48507ea827c7e6544a9bea023c7f892a04cce976608d588fd98724796131c5e171dee344e53e0dde0ca4660489457b6b9d67bada961f2b910d0211bd03159f02df3fd9dcaf5a614b0f0def0ecd01a810407766e2b218b6bc65c8071001e1d640c3c93c5d609d5fbe97bb7851eeacfb74ca570512c03f65f07ec9177e1bdde66c087f62eba911226e20240dad162ef765304ebae279aea8eb1597d0d72475814aec822f57012ca443f8704d01b9eabcf5da6580e69287c18bf139725b84310e6dcb6b35b697129850d2827d548722f904e880d9854f45c12887c61007c6d40417383e043efa36fad4363823183d4a8787961e5bb5d5acd0db3eb3b46989e366c6a8b87ecc8aa2d6839a353554394d4aaed28c0a05ca88e3c562205a5a412c33771b5da0680559b74a07f3d7eb8d5ae1246cf3f9f98fb828981b5a111ce9873bca6c941143851d9e384f0795a7aeea2e825b457539f9210b8ae1dbfd134a0b3f41bbd017fcaa886e8a3e2e8cf2a7cf05e478630db7cb184219a4ea23df306a11c4c94d0fc0397f32223a63abbdeb2e81d0a966a78319653ba2d4e7349b03e51c441c4cadd49302124624d014cccd0825835b013b70b0026e378820a577c6f8c51242447ad1a19573feb953852d44845d6905982519d8205d6b7fbdcdb96bcca4422826010cf5fea2551169e760086feaeb07f24f30fed84afe4058041f14c0518c6b2b1b34c8cb8ad2502184f63f03c6976f79dbf811144e47eeac102866348356b5a0c431583b7a531c814943d56aa700f2ad1dde01b67cd6491284954fdccb8b8eca7f92360c14b82a1c3ca0d94a273fc6868587d1ca3723183c3313e1cf0d30f58fabf38e4c034de97aaf652231669fa760506d4800e9a5376371153ed9e10f6ced99a9d2c7a4762df8ff25d8ae6c31aedd06bdb6293f41d802ab0128efcea22354e58a8802e11a54fc6fea5fb95ea646b6b629b0879a6bf7d340249e480c404edb0d238607c69b217de9f87ca3ceb259bfbdbf7cfe3fb8e4c1ab85f7245387eb1eccf1e7e0953ab7ebd71638b69b6a3e0dbf9d90b1351d60c809f4b522d7835befb5139e04c7a3e435501c7762f4c52c523a3151e0b8d3fb3974b4cd35413d60be69fcd1b64c351e6ff3007750c8208e88db45541272825f561cd0747d462f5338b3eff447ae0a2d3c0c0907c734fe7d2c6cbffb7b128efdd72ea729897d3c832c5901922671d77a90c2de4e11e01dc1395e4aac381bde312b5d6dcb99c460765da04b4aa6889f7665c16277e7e78690ee08e3e0e40bc837ba96403863e1dee0cf7ca899ae9cb77e871e078d700ed8e4ae2ad047ee22c01941448cf370e640568164ac81e81af1fd8f07d6014831ad9d5e6637a1b0765fcb1406e25244e5b8d48824a7b86aa57e49c5d75469cf6092e320398e2b4738e6a03cb6dc3203559b864defcc146e43d00785459435200d3d8000170405cb8fb9d43ed59a48370c15436880b61f83c576929ec4f338b84ddc524c040070c52b13b9d24192714800932d1ac08de6087af871ab6f0486b25b6cf2e3b5c9041ef096f7cdf15fd5c5b185088cf792b5ce9eefbcedc1244008d1c534b030c88762998d4c4cc1b5b886820a67cefc0baed7b25afc37154cdcc90dd0cb0b3025ac737543fc1048cde4b9a20fad0a29402ed31fd40d026a04f318813e72324a7d9324d325c8a8455adb2f90fbe67b43dff0029cab32162b487e257e555ca4bc683ed455ef1aa08aabe82dd67193cbc4ae4894e96676120b68139ce6718a5b167036ed23c90d89733124754ac8b95c2d90af0c881a83bcc8c2e0ce70e4f8d338f769a19b7dbb93850daf236c02bf1189b9f0ae066a91ddd136006121a50f2313284d51a5607ebdec64d00a8cae210cb3c21eaf40f45ad26caf20844e0990a96db5128df48744a74e74cdaaa862c516e303b6b343048046adaa1f88e5ae039ade3660302eaabb0296d394fbebf0fa37ef3d3a88b26794b8c75980281887d2e07c75ba6118032b454e3959a4cf9678fdf9b406474296fcccd32e291638a08a5cfa33b2e8a211568ee5fe5c18c1f18c4102110d332ec40eb5df374ab6fa60405e9ef928b00a99898eb42c59db25636ef6568ef209a515ac89392f759b604a5ce7c90a59cdc36f66e5cb3327631b2e52788567f292506a096bea8a58663c312954b19a2b787ec21d5ca533aea07a5b739ca1fb6bf1dda747fa094cc4087a6d32d48244229b966ddacbb0b461d9332e7e4fd6b6aaf770809b6fcf8cff335a1b6e26ef784e5b6b4130b136a10dd5a6c78dcd7066f78a06e78a34f34f6224f2b942030edf760e86ca553cc2d62b54c270bf4cc891487dfc9425d24baa3f28ef130454ea0b9fcbc2646b3ef59f6723ac0cb69f47a5ed7b3d1d3a8f3be2787f9364517616e372daec1464612dfc2c468c06526edb747018999461916d09acb9d2b5d20b6bbe612f5203887f49e9b1c978fc04beb8192df45e2076a91298f8d438a55ed5b52a31862d13ecf9419fd68c048b3577285c800747b1fb70f1a4b3ccbb6225560e17e9d9aa2b9b25a6a0bf46a9a4c64d502bd2ef24319d53b7283b8055eae090db8d93f84bfd8d1f1d5f3a81391f709d9610576e0b2ad167ab3ed0c49e942ab4a2ab61d95365961096d681c37a8255114a09e29c0f24568f56ec23863dc4f03a40a130289142c5416844f47149b9501fd438d485420f149092d252209e7250184521290e2a134a8382a058280c14024aec28609c85d31c304a07ec05aade4c144d6a0a513a62300c0f0315a13a52d43df1dcc5090eea80da50bb58940e711e91ba42e54101fd2a8a8337afc37182ba16c5fcce040a88d97f361a0fd5370cc8f572a8f1a8beebd3f3329009cc2cd4f0a280867c13b6c5102584fb3064a98994f5a8578a06c540c1a1b8a3a2524828118a0ee53c0a958a42598d724ae150001411ca874a430150361407148582a1aa505e2806058782298af9dd17af3828a86685b2d8d6eb8b26a1c489c2a42828209407558262a11c280c280c454005a1fc50b887ea63b80f1c2ba8060dd507323deb0354ee45017f8626cc71a0741823967e87d2a08a07a5c32171ce504115504016054c044a26e7a0ee3d5f82d538a13250009494a1fafaf57e407250ee88027efd90a79714b004467dd514580a4a040007bf256bed36a816ea0d3541c55008280c0a81224355508408d577c0c5580328461e457bf37a472f28c35196140f4aa4a2ee715cd2a974a80dd4196a110af83090e4543427e0c90144b9c92854e9d561fd02c5a094a04828010a0005433150290f05b49c35585dcfca227e14d036bca7b166a038545a096f01d52cc5fc2e194a04243b8c770215400da17828311400854321a050281a2ad541dd035ed262f05009d4188a975040b6cb0f1417541cf1fde08a1b0a856aa170a814285305d527e260c80dcadca080cc8e6b9d80d28111e9d24f8e2a985262a474408de38048a830d43ca21485f3a6a11d8a84d2a070282c14e450ccef06b6726201e5818a360a1832573e7636503a90959a7a8152a0121b258293255a2605d40935801aa122282e14028541b1a13a28d245f59d6407fc1a8a2184ea4301fe1a1f5402a2fa54bd97fa59150742dd782811d0261b7813ea54d42e8586d24279283a140d450d45a0b28d022e404c4501ac39b48202f24528c41150f158373127187ade58cb2c440c254b8fccefd18be1bb791e301dab06c8364a270bb406c8235cfecab3a43286acff4b0fb08eeb87506e40f210fe30f45d79002e291c91c4000e03a373c4999671ee146b34f7d6cc536bdaa40c21244d5616589295440a150691720d2bd99c1482f74d8d8649569e29f210a70440df052c810ca134d69c1a6c510ee3d0f2cf55bf5af9ced6170351c0c9f49f16224bcd25e548e2827cb247f3a14f95e4f748dae0ac7eb9378422edf8ece4a50152433393d744ea92ec4c2de3dc2904f2ce552843693d011824d32b2b2432c9829564a530057401d7cbb2d6cf692768f599ee84b3de535c6332c09dc85a879a4a514dedee97aef6e95044c4f307bf175d5fb0629b91a92a737f221b44a64ca5fa37529bc9e70edd3c722f6f316c08f81ee95a299ce2b661a1c5e1feefd489765d9aad67dace086d92c5fb5a822a8076118a60a84a486ce89e453c079da505ee5fa047f133243e700c182eb5effa9a308775529ff6b24061e3121eacbfd3522017a610cebf043f0a2f18e7ad60b216490b22a95c480fe55e7d483a68575433a91fc006468d5f0e2cc8fb39706300d961cb4a4a33e479bcc1ecd40f5366b424bd0b70577eba4481d5d4e0157ec9aadda368e82140ffd1b95431a003903f6fe329f7d7c18cadcd17e21ba715e2bf79653005771b68c506c7c393ec438b8cf03244b020db82d22196df1b2d4311e6556b6512d1c0965555dfc8767ac0e97b1fe3ec40fecb31e0a43639160191b98f7da0dae06326df0a16ad4377209aebee2c8f4eb26c3e89731d59af75a5f85a9c82310c10429992f62998b7fd5de8b6ef68f3a6f847cf9945bd97ec285d5c573e48ad88ca573322e1eb3360f3ccf188e5a290b4f4ad9fe088d49b974ca2358cbec20c2707a9cf55b80a77d61538c90c9c4f8f5668310bbd7bb511974600120a8d2b0f3c975c1f417b8fccf8280f2e39ed117b8752f3691958c1e3210a0c206ed990a6c671bbf665f9229b6727ea76539641ff36789cf131b2fe364119e5c80701761cc2319a7fc461a77bac8d780781236534a374662726e94012cdd3272990ce6bdcdd04c0bcb8313c130d8194d60d445734b6ee1b835fa4c56396604912a4bd771c89f52d854c0fc49f5f33c0d5fc22cd8dc8b7471a75e419e93f025a2e4572e1916ab2e8a45bfa15ab7373536051bb5ae84aa185023186ae8bec41e80bbd565a77742ef4566d1bf2b4d0ee725539d6fa61ae731d4833a23f1ab3d684585ea4631098d3582b50789d1bbaec7cccbfaf06ab5078e8a7d20f293cf3a3622d633fac533c6dde632ba82e6e1fa0c1978a0cc5e8eb3277f566005eb90102968c64acc19ad2015f2dc00050b3c01429510a8b8f1d80172b1208f9f2a4d6d41b61ed293b0db5968dfb182b2e13f98de895cf90d080ca3a7a9a3803d9e1205f048811885cef0ecdf94ef678e22cc55b9e695dbe3a2261601e706ca9753dba1eccdecdf24d34256732c9928a48cd550c0e17fa38446bd6462f31631c3a2e712fa386dcd55a5607fd2252e0a29e0be055d3bb3a45dbcd961a5e04c0dddb9bc466437fa119095db04f40ab51391b691e29ebb13a437e8e9d7fb57b31ac6a8dde832384f56dbfa187f53a2aafd679652acbad0d9017b90cf4cf381f46068b17c80ad87b1bdeb4b254acda337f4fa5bbea2e27df8abc2f1479fdbf255773b3d6c78a6426b39b38f87c020f9f7da074ca6646023891a7327755400b37db0e463c432253536572072964babc600205bee47f5d906114ab642b3c2411719381a8a5f9f97fae5ca4a13e8958457400d495292b0906a33f390a7ecc3d574a339842c1d7d84485fb8bf2d22302300b4b8d8c755143928266878f94de5a3708370adb69a4d97e660c20d2bba533a01ce963e1178761802905e0f502592f380999fc22e096b5def281653d22c4c9b44f04984fc385793dde3f36915a9b526e2ea1d506371d61c29c1e01ed146b1b15fbb248a6c44be240d0cce1115327ece8cee03b4524f1cf62aa9d95341c817c1568ba2f7a82100619d633a82909543899045b18600d9285811be68f33fcf7e4552a04c198a85eb145e361d99470a56936e2bb96539f7c0be614b6a81c7d861af0a6802283a1fecf8e57db5d304ec151b595d95c3da38d48cdb29ae1f944ddab4a6d9880bbd72b1842d56ac26bfbb29e6ff881e1ed0c9e6e61304dcf84306b76fbd291aac7ed99b64d6d9c430e4b67d8f5e52b3f253d8e9eac57db23ccbd83471785c494f5980d2c094aff481d4d909735204ab8090836c184fc2178ccf5704812d38c18521219924c1691efb3765ade214759d11cc680c2b84f2b9603b657e4d3d84e715ffb982b518156210ed79f78b6e4d166110d66ac0a32c1eaa6aaf365c93ae103b0f465d6499084f86a0d0b86ea5080ff9b2249203259af40d4a0cf84559252ee0251123393961cab5269904d88adbe18da72bb050e9d8ae0815933ced2ae52fe79b6b0659ebc478a945ee20d6ddf0a13a4343939b36b54366e281501354c40778b203b5cebd340d451cd1cb7032de304ce290254e0b7595092cb95a0bab292825e14a59c48352cb5679aa4b07651951dda606efb851afd483e5352d0129e40f5c101d111d58e9a3a647afd10d54651d0f79b0dc89af5133fedb1e2ee499b1407c709102cf8c7fcdfe3eac94ea44c15b991ef90ff1985857d4482b6b890ae64f92b7002b8e55bbb3708e880c4cc968ae54bd1629e0fe3b181143af0276637f1b2f51f11e07a856872b26b4994e233728fd0c896ba884c6da979b04517161185df790baf7f857ded591887eb64cf1065342689ac84d6d40573b8eec7e9b824a53bbde1492216cbadc1db42ff88d4c62a58e758534321bb12e7402237c6b54d3669b305e320471788c864f3df66c8155fe44942b40701140edb4209d3a2e024d4b7f364e024c5edf5ad7d49e809889fd1ead6da7f06c3180dfd6c5d40379d00c4e7653d47a8f1b3998788ea1b9e0b1124cc2db4d0350b070d616c1143ca9e9f965c5c60824c277b5afa243a068b226a1b6131af8c39160abd76c0dda2ab18699609ba487d357c13750631c73027acd5030d7139496e2dd18834da59a5aafac6e09b1af23679132152d373af232f8628e88fda6df5e6348895d851ca5a4bcebd417d184a54e78f38f04749bae54b475943891f696f5da7b59eb250a895bb4ad9992a28597e782215abac9bbcddd0bc99eb2f208669915af4ecb2422ab4719dee7a7bd594c7cd99e7f79d13371ae538529e796f7a74b97528dfdccbe3fe724a7ac52868f3e8f4e922182c938dab8a161aa3e16e3c7eec8a4b413a738ec8a0e6f02d65fef0c3c408e846a3480afd359e2337cd6f938d42d5ef55efa6f9c9a2f575327484b4fa2b8ada9753cf94010143135af37fa3ad398d962ca3a5e7f815c2ae2e5b660945102d53018ec62f03b75e944e13d7feb6466aab40e4ecb4e0d82d35297117bad252ed244fc9499eccab16e05ddaf13df64fb69d87db78e18a85879a116f5346e1d45990d16556d1c356bce4fe2c6fecf090d7fd5e14f7827ff09b75949c596492ca829257253ed312f07add55a7e27b8687a2040a91a86f6e78fc16b9e1da8be2208c0e9bb1c332fc6b21d9d029081ca3081461c6ad1f2998d27152e52540cf2162aa045d4fd4e30fda3005822a5e230597c7b621557dced1438d12643b7a59e7c45574b9cf3e8db7fa93945bbd477369f8020cf67e060055503bc8aae6dc719b89f7b868a4dfd3877793b7c530d814f41d89a8c8ee5d809dac870088564c5f581bb9a50a0aba88931135364bdc67ac0900600e35a4e99e69091d6041f40aadd669b5b07fc95d6d2428b4cbd7c50adf2a398d1f344e79f1cb39f79a3df5bae0a15a7167278f832123894b7a34289ddce8abe8c1ef23107af300601b2cd707fcff644c357d1eccb000af628ed655bbd43b1549fd01128a796c7f4a8aef258d252dc6134fa53df5e278d8c8098d5d859ee5ce9c4a677990790f61753c51805e35687a6f6e2ed6a4d34541f31aa5c4e67e14289a909e236fb4099efaea8a7ef128d883ca0b5a054ded235f429be3af69d3b7a3209ced2da1673f286bbeb0e69b6b1e151daa0dd583f2504828060abe83cab61995351516b0ec81baa1c445545fb024ab19ca4951a84653f80f0b5409f5873a50582828140505a1d28da87bb0fdacac40bd500a8abf45f5ddf01cc3018ac9a3744c43deea1d4a41a92fa87bf0a7060cf50b6541812a4a8711312d4841f150a308459bcf23d720a04e18b5499d5009a1fa78cc07fd375407a0802f98b26947d497e626c183357fa173053b83ba97b408110a350215426d040514f0a2eca427e96979a243c9507880d28106ffddda501ca8b9400155dc04f2a540dd4bd97d1a00144414ad14a4a8f54686074d38a81f5407b58102a11c282e145643a12a52b25e08d4f8a222a90705840a41a9a02814ca4201a9d569c182e2a0568a7b1f41cec6004a93229bf31965598f81ef1cea83929f02968f45e98a94d49850b4a530aa4817a8035584aaa1ac1e14f0edfde77e146d0b0f440e050a8028d1a7bc14e2282081761a0618281db233a47871eddb8262a02618d5d72f30e5a85054358a762e150dd4a14e501dea008529aa2fa7eb6b684145739408a036b0ee02f581b2a14828351406554309a03c28020a3614b05c2f7ae45204a09d68771ee3be3e7ba2a92423373db0070724083178720252ac0cc502fee144760c3750b2a4d001c33f3f3f3f3f3f3f3f2ff0bc6dc1362b0a5bd85226f2ea3665f3de94524a29a554068005462384b4c602c20680c3280007029f0c1e0c5e0c3974e0a8c11683183f2846f70c426a76184483069723860f8a7153b5c3439cae57174870e0c8f1014b70e0c80106aa22460f0ce36d5a638550cd6d0c1e7c3e27bb89f661a2bb48fc606237e6e4a1f2c0872e968f9b73874f596235b8389ff0918ba2499b11b22796e8b33f705192f74caaf012a14f076f715529ef51da47a794f8b04561999aa155a5596b99bd95f6673a96d0ee79c4472d4a92a6eb2f61539592c4e5f0418be2e75bf334a531c8963e66518eda316f50f2c6b8199445e98495e65d9b143226f98845416b50ab175edfb7a28e0f5894367827f956a4f9092d31f0f18af28fbf89cf3933b5665d513eb165af94f2d8a283ad286f28b983e7efd71bf5072bca277d492d257eae38ed6315e58df3796327ee4c92f95085d99af655f2ba23966177d2a4f4eb6bb055200408e02bc1472a4a6772c939b4866c13c30f54946fb53da9cbbcdec6a7284731399f464567fbfb618ae229a173ba7e75ca94e58d8f5214a48930515b9b55922c091fa428273159a9def07e8ca224f4cd97940d8dd1ce87280a6f3d424eecbdaf1285a23c2a359ef897e94deaa02809a7f526e7fe5c9bdbc727ccd9365713d93bb132bdcf356a1e4f4c2ba164c7f0e189626cd32499dc98573646c34727ca3173c97af1fb41a64982011b94a3c207278a1d3dff3b9bd69b5bb5f0b189620cdff145dcc5c91e6aa2fc9e6c935013a4783ef9c844617468cf13748e3c810f4c143396f6d0d6fbb693f9b84441ebc44b8dede77b630c3e2c51d09afe5a4b64eae6eba312a5df24df93b2519e8f8cec482fb09111005ce18312c51651da57a1793eb993285f8e94bf3549a298c93fc713dc4795d47f44a224b498feca093f2051feb8a369b4c66ece691d7c3ca214324b8969339c245e868665e2c311c5f8d8e8f1eb3332628a8f4694426a9824e660c2433482f0c188e2bae9df9dd12a4ffbe06311a5f1ff924ba78512ccc187224a3b6a2c6bfce492e3a9c1e685df0f3e12516afd64b2acfb40c49961937aa7f410a5cd135b74d64726e5a8147c18a2982493dd39c9a28f4214f4fe9fa8ca9f244611214a6a94ccb5657d0ca260f1a9df6265b5b3461025c9c3a70e9bc31f8128e9763a3549d43cf80044f1ba939af2e0c1ab3a1f7f2809c26487dedc96e7fa871f1236e98b6509267d28e66a499126e7689d4af8503031b5f43e7b7787d31ecaf9e22628fb204d68480f25f1bbe745bc6ecde7e4a134fa6173c8efa84d62f0507edd6b8fd9aec4ca983b944c4dce76e9de6dafb143398ecebae32a2a37df752867f79b4c22371d0a32a47649a194483df11c8a2f4aebc92d5ad55b9543e935549e096fc6a1a044b76d7ad072ae261cca5df5267f43399f1862abf7c483921b8ae19ae4bed126fabc6d28a8124767c7ae8c3bb221133f61a619aea1bce5a6dae39f546e721f6a28c63229a32655d3508c7255a3624dce248428f84043491233a566d392e6d3c9198a73fae7a727d5c58acc5092df74cd68f34fea97a124aa8611136afe204339b9fa8a38e184c9723fc6e08cd8b9cbce76698ba72bf92697bceb7c88a1a04555345f357b898f3014c3263d4268b715a5c2447c80a1604289a34207f5e30ba55332c449d9f1041d99051f5e287ccd7ba6ea99a04914c747174ae3e22736a76912c6b4c907174a72eecc310962ab57fbc716ca65720c7ba208b550fed8fc68aaa6ac64ed230b1f1f58a081808f2b2ce0c30af551051cc917366850610a0af89042023ea240c306181110f980828c8f27847c3881c68c8f26d098f1c184057c2c41093a443e92f0858de4e30309313e8ea0c3e3c308313e8a20800f2214e0630803f81042003e8290800f2010e0e307340af0e183f259c853f25c8743c71738de6cefa30725131f111ea3721a7cf0a0989a35e38ecc5ea3361ebb2809257f92e4777451dc121e946ca1af81472e8a69374b8811a573aa8c8b929f204caaf0a8c72d4a623dbf624627e143ec618be229b137d3ce8ec9985a94a4d02254fbcc4394092dca37a746f39ba459945634a86831a92c4adfee73aa34e6cc61138b9292d3eb7d1ecf9a1f58144e50aa4fac737890fd15e5ce38a2b4957e86125724bbae7bf3d76e45c1daa44ee277cd8ae269d3a0f155dffde72a4abb73324b9f3acb58a9a22464ee4937a95349532a0aef71e2e6aabf5d4e5051fa0c27d4670db92de95394547ecf5849f32022539453cfcf58975b8a92f67c3a75f690a2f09ed386e739259faaa328a689d19c46aea7b94e14e5fcd4a737b6e9f0084579a38ab0f3a0620f50144bac95b1f43f71c79dd331e7853c519031797a69f79d2875dd99e58cdc8dc9e144726e26f1b4ef9b285b6dba65fe3f6192ae8972988d87926da545da9928668ca80df7f149f38b89b2e8a876b63e2f51b2912597922126d57e9628da9bc6ecbb17911bab443906599ec7a42729cecde141897257084dd25a096ad94fa2ecd935b7e794cdbf614994736ecb3d99838a6a3712454fcab6752ddfd30b89e2c9f1329c2c329bab3ea218cf43499ba7744479c64f2c5525265964641a3b3c8080116c0001322861ecb071c3cc40066964240c93238781814723caed66a3cc54073f25c38882f092e4e826bd069b17e946ba51bc483752075240c3782ca26c6a62d66479be1e8a608f44a80722cee310c5563749be13356b67507a18a2709e41b489a1bef5b5367814a25c77e3f2334abccbb5081e29692f2d1c44c1a4cb94498e5d102531ef093d9723eb9607a2bc27e3565b2bdf641210e568f7fb72728cf79ffb434193d4493ed93a3f14cfee479baa4d95bf0f2579bdc3247dbaaee4e6c187a299ea49659d79b44f121a683a72a4c48b3f015b261e7b288712cda4915dbddcb741e0a1875b81471e4a729de8ed9ff66e27091e501d4553c8f0d89dc71d4a1f93a053c8a81d4b7e6bb07d916aeca081be30ed61871c498f3a1c1d4a72c796aa5fa6065bd9123ce6502a71cd3e6164b4c94935d8e450d40f6ba3ba9471380f3894d4e724ea26cb4cdfeaf18672bcdde68d1d7c33a46e285d5fbd670fa623cec481471b0a6ef28853d2d4c88662bcbdd4d56af924de494772c0630d45afedf5202dacc186f8031e6a289eee7c5692896ab09d471aca7e9fe6c2c4d618b6e12cf040838d9c0b3cce503ecf72ea9b83699c2032818719ca23f4273967b030c99494c1830c4553db64e2c70ba5a311468e1d57028f31946327dd9fb226958a654003c70e31943fdac6f7936fd27e57830da51a8147188a169faead73d2ce7c028652dd67d3b1a3ff8592e7d5f7c9b16dc2425e78946c25d639c977a1e8a22b93fcb9f927e9b950bc12f4a8c8eeb750fc2e8d53b276fa6c1a2d144389257fdcace5b6ce4269be447a149bb150f252999aafdc2b94cc2fdc24bd415e6c6b85727af7369da327cb5fab501af94ca1268918d3930a0599639acd9a7b5227e1144ab922f49b8ebe6b31a18187148a7163126e37a75f6f1885627293cee38d8e85060a2599613efe868790b5793ca1a8e97c336e07f1239a8713caebdfb9b3c4a38cb63c9a5038a183a7cc1831692c087830a1a8d146db7b12ed37c9f1584279b4cc49d3e1342859bbc14309c52f53d731271dc489198f24e08807128a3e5f92b69e93a772aec1a6630637c0c831031d6927f0384249b63e693d36f3069367bd2f3c8ce03cf0284231c6a877256544c38308c5dff97513738b8ec975c26308259d392dddb7a6613b427808a1246dffee9fb8eb22e1118482d29cff79f5727e42878407108a39c969683eaf175d5a8f1f94b45d4e54bed66692a271100d1d666dcdc30725fd7b676da5d37bf735d89c032323347460606484860e2f12101e3d286e1e0d21c2e3e998ba0b544003089800022ee06ff0e041399b8aca3d498ea977ac5de72e0ab71b9e5bbe4388f97451b02a1d3a4a3cc63ab928c8b41a1af4091d64c645b9e4fe31694eb86f32b945316747ffbb098db649b628d90725a679f82449fd602dca26095a671b935c722ad1a298c1c4c6344dcfa238f284b398ed6cb208c9a2b07173728ca11a1727b1289e70a3e3bc29f17444b02898bde6e4e163623253bea2389e4f49f275d576d4150513f98c7766af26795a514a3d1ba5a164585112231a36868ee9f36e56513a17f99bbb2564335745398bd8f0fd18b3a990c15494f3b56f4c327936284145595b664fcacca728864eff6f628a2c394d5190ff50a3d4272da324b314a5ff2034c6cc31294afaca32e357ab4a7e475112ec4d7ba4898a82d1941e4fd2170a4541e9fa189966c24aa3a028e70e272e4cae2a57fd44b9fc9490a74a14cd58f244c994d04ebea14a8a5076a234a2ee4cce1742960972a21cfbf6fe66bfaa3fb7899288cf41ddc7d1d4b3a326742bd97e2af5cb44f1547a7d4ea2f39912c344b14d4ff674ee319420bb44b16c8479e8f91fcf181ba41d33f0a2c60e1b3568a361004b946e44bed73bc8e0a355a2584ace91d125fdae579428471332fbbe841da5348982d796161dfbc4644aa2681bc424a557278ca891288e2a858ae6becd251e40be50fe1c3be72462d76003c38b1c356820b32f6c981a13e0168078a13097a5c524f7185476e400d285f287ce583dbb9a199db901840bc5ab6b4d53e7569fe4806ca1f578f5f0baf46e1db19ecbb87c173fb525102d944451a6397bb24f0d205928c84f8212ce7e4c49b2090b4513253489a9a32b14dc2d74d2192d959963856275a9d3529dee66e32a1464ed548c8ca6e4b01f0815ca9f44e79960029942d994fb7729afda4d5f0ae53d614c129b7d724e2a90282cef3557b76e9a25f3fd3996c56e205028e90eb725d7aae4bc19c8130a267e3815fac49d5056175dfabcd489e1bc269436eff4c9ac291026143bc97ed895d304a600b28482faa78ea5d5a4149303a28492ec59de6a3b8324a19cb5f24fbfbf54f30b8284926dd665d261ffbc24811ca19c4d4bb6d062f2349a18a19ca5afd2aa4cca248d0a528472de9aecf721e420442867d1f462e7268901902194aa9465cc70726aae580805fd0fd2d44bab5b29418250ea3f294f63ac5ebd1b04082559a1565726f64e3cbb00f283e27d105e1f5ec3241182f8a024dbac4ab215dd81f4a098aea7a19f6994977807c283f2e899b61a0d426ee66e1705d1cfe6a6e3ca4aaa580532745172517ebacb45394b4e9dcfc6940519b828af686c9f9e26d5e1b94579c3e726354aeee4bedba294e95edf47666b51d2db1ca3e9abb64c8d1625371193ab7485286316059329c6dbc32e4316e54da5745f3f4531c88845a9454c3ccae6686f2219b0289df4f93a9a12eb9b9b8c5714cd45dfe68d0c5794946d3a490cd1e91964b4a2e49ed4f6c5c6cee9560519ac285cb6c764a29eb03eaea2247f5ae9249c36d3b165a8a2d4d946885ea96c7e2f15c53cbabda24db59598230315a50af78c491a5d1be4bb21c83845e9ca635dab29bb3c4fae0c539404a1fa4be377b94908324a51d43a613b87f90db75f06290a23ffe54ed88a8c393b10648ca268e31f26bafd3aa8900c511473fa1835898cca0845f18350a227eb1cbdfecb0045d9a434f1dc38f113c5b44ee5a7d751836f3c51b8f594677aeededdea4441668893417adf7eb832385112e2acb4c773fd0e4fc6268a55a3dd257a902236265313a573d31ac63d6a09edc944492db5737478fe9c4a30516a4d6af28afe5ca2d4a9046919c44be5c9c8b0445957e39566ff16175d6625ca2f636b62862ed19ecea444594e8635f946ce9c44a973668eea26f1f2e34c4994e73794d8ea2945e73e33122555a74f8e7ac209150d248ac9e2648ee2f6238a952332c8242fc311a572b54eaffa1b517055156d3a9597bc31a2dcefd5277aadd996691125d1d119ff2cb4cf9e88321451dc1639b965ba44144dc88779ef9c46e5c911511819fa4bc98c5b907188f2986ab4bf924b33b788320c5110f396a14d5092a41e23ca284449b2919ef1b22444d932f6ffbf6809d3a24114a49bc99bdc4409a2398228e93c6679aa71198128bd959fff6a79723b6500a2eca5c48ec93e7f7628c9f843e98469bb91f1c8f043496934994d468ba7967d28a9a9b23b53afaecdb80c3e14e4dad589a2a1ccc5a6828c3d9494162d5561aa6963848153197a2895d99be8498c51909187e2ccd789419c6b1e25b604197828071dba57773bc997ff4ec61d4af2893186933b47861dcae6a743c9134d96cca78c3a9474ccd164cf4e22830e2511199d467dd494d1aec126630ec5ff24bdfe733e4ed0caa124671ccf10fa8a437175bcecdc4a666f090ec5ab389d1ff4fb8672dec59d1c5e7b7b536e28e9933732c6bc1a3eb4369494ec34a194c72463ccb1a174379a94a434bf8652754753cd93da4f5743492cb973b8c8d422364a4339c7674ffa3a5a9ed4a2a15892f44dd1d99ca15c72f89e13cacf5483cc503c513c89d7f15f829dca5092ba4c6b12e9b9276cc8500cdff60fbe4193a4248da1d4b59d2fda8aa1b8315d76aecbc350d2f03945ec2c030ca54dd649da2064b68c898c2f14844e25270d6fa9a11e195e2829915de2b376968a25a30b25e5a1a38b8a51ba1e86820c2e946d841077824942999a750ba5ae915a279af426c8d042e142b45fbe9bef4972441959286cde1ee13185300932b0501cf9314d4e733f4c9016645ca1f89ec3956d5677ee8c150a22b398f250929f4972aa5012fa73fcff91e1cad4543074c4690a29ea299457b3acf6f87552af91424949a13e4267eaaf3f0aa5d50d9a2413b47a3e29148af1996a4bfe89257bfe09a537f939bf3e57764f4e28b79f9053e33937ebd78484f73631a1e47effeb7a270923cd25944e7f9c66123ed5c7aa8462c896b4a19e4d4269b764d50d5d8284829df420fc4b76d8701ea1a4eb2f37ea4e2394b59372fb24660ed37d11ca172626939fe029534c8462a8ba87b8bc10b1dd10cada269e5ffa2784c27627993e2e5f69c241289c0ea1744f4e13358e8050f8cee62d9b8492a1df1f14740c9bfb4fda07c5e0a5946062667a5076134dc97691cbe041499bb42fbe1da7aa25bb28f7bece8ae6eeb0a7d74569ff772c4e664f77712e4ac2ee4e858c372e0aefe9e9d1e47e8baf47a76b87756c511a6592895d7ad7a2e059b2694f4a981605a5243b25468ff5ebd12c0eea374c106551527af494e46a23c6a2b05fa5cecd3a8ce71c44589c639f2c32534e315e512cb1c7f3efa6677d4f1262b8a2e07135e9abebf220f318ade818ac28f5fe78fc90711d637c15e59c4e8e41c55655146ddf64cb7b7b12833c1525196192e0b7a98350a2a2985b723e6b87114a7d4e51d01ea6449f0dd79e39539433899fe37bee1cd4a64b5132fdfd277a92cc3c4b8a6252d2aa5a6bcc49cb388a626649ea4df81266ca248ab28c6512c27d3d683c43519264d5566b5e415112133a09a546346f27f944495ef772cfd7149f644f14f443f79820bc189d28d665e8a9501d278a21435f9fa04edff66413c5faf56c26af26dd9b26cabd793366e9ef9ce32813e5cc234a18253c4bd02326caa149a68ed194d2f8da258a9bfbdc8485c6ef972d51ca36ddf8499b54a2243a29ed4a8f23d43c9428f7c85c929ce5bd32bb49943b7318e5a92949944ffa6463f267ba3e51248ade7bb31a350b89f2686811a6a7d442ad8f282619f6fdb64a8e28e993694bace3334ba811a53e25b6d586d737b16684a7a2335d44f1c4a8399850ff1983ac8854ad669d3aad1351ea581e4ce4dc9ccc258828bddde792d496621ca29c77931c4df29f7b098a6188a28e9039369e14a2546922cb5326497eff310851d4e0ffe2b2699289d0200a624ba8ef518fa39ec51084f96fc64498307e06a251e53b621902a25419a388ec127438298df1877de54caa4d3a64c9c7f043152246084bedf39c1a6c17a30fc5d55d6e9859dcb8a5b96bae1f1b133bc80fc7e043c9b39c1893e469be598ab1879250f91e74c9223d1477d3cce68c9f3bedcf4369f6e48d9bb388cb128387c27a9834c983e59cd21dca1fe38970dbd81dbab643d9e663ccd3b3259ab37528c69b0e9364eefbdb930e59c9d4bdff1c0a379ea3f664e5a0598c55ccc8e8b556cd28d1e492d46a3a0ea5b714b9fff81e9a4ee050d2240916269b9244e992371443ef98463329277b891bfc244369b8d69836944a949f1cb3a82741cc865207bf101d54e9f053d750bc92734e77da637b490de5301127eebfa4ff36d350568f9d5f366631d0f08693995bd3a8629ca11cd566abe42b71529c662809cf3339537c31ca50d2294e267dd5c520433974aa884c952777b43bc618ca3b62929c73fe7cda670c31d8488c30149424f24cadcb8ad6135a186280a170a3ed253f4e31bec096cd557ec7eb16c30b4593255bc2eded85185d28e789f7b9fb6a262a1b1961440c2e94cacb3baabe9d85a9636ca118b2a4bee7248ea1855249a624412e565468c79b4631b250de16257bac667c743c31b050d26e9258f7b75ed680185728e6b0994f940e32f54c6258a1984c990cde7756a1acb98492af9f4baa53e3c92006150a42a8dd935b62a6ffa631a6e05f783ee1779248a16067926442d865d694a350aedbd82ac94a4ed50485c229bd7b9d3b339c376ed88801f620c6134ae29c184dc912fd20a7bb410c271483f856d66f4847138ae7ff9a3b870d9e8493098513b4798c2bca1b333049424b28aae6d4efebf969e65a183194500ed2844c326c94a7ca8883184928e9c9c613b4bd55735368831848288a88ae9cde6c92b3c311e308c590f2259fcac4644a3742a994542ae276735e534f478c22945c6f3cfe83aeae5012a1782384cc23d44e36874937be6fc41842d14d12afdb1b474c73f72286104a9b93cef14a0a0ff2e4bc2008e5f89c7973f22e693aa363611003082579f3e72cbde9481ba5de467c694e17ca49a7103949479446b950ba0e2d1963f292771d36da74dcb0b163060e10d942f9939ee6249a4a12056820a28562aac949dc468ecce9ce4249aa90f9a5197b37c7582867eae9b0b626c7daf20ae50e6b9dd4a42695ff5be14acf954bd3ba7bd50c72c32a946eabc47ffed3399550a1aced26327c7fa650ee346b72f8d448a1a04d7c3fbdd255d1a14814ca721b4acc3a08d18c43a13036235f236493c62bf284920cef9239c89c2fe389130a2ef6be75ff19f763449a502e6f4f2df798cc348709c5cb5ce7a72bcd52c72594e450629a7b0d4fa289514249aff30935fdf897b948120a4ae7f9acfba5de4716414241b50825e652f2224728e6f18cf13137c67b2b62844db74bce4e6cb75cf5f4fc735a7fc8ba458a50bad5ee241ecb800811cabb49d4d9fdd1ef71141942f9f336bc33496b828e611aa62242e0ae3bcd3575ccb5ebc35e283d42224128ef08a5277ff3a3230242f1e54f68d9d56bb0991c33d86174e4edf8c2068e0fac99210d911f94735266f27e624510f141f1ba4e378c7811651f911e1464ffa7b6938d31453d32827610e141417c5e6e8fc63cd14cc82e4a17fa2faea4f35835d24539c642eef45a673521b928989f68e6a7eea574878b82afa96cf19d9bf5925b244753f229ab726d51d8d3aff9049dd49d94d5a274bf7127a6ba79d3295a94eb4e8e5776f42e9b675150bfcd4cc296b2b063cf754dd776e5ad2dc3946f3a9da3e4d46351ecf02a5f1f7a766f9a542302382c5053088145c1545a96183637c97ee515425e51d07bc29c4cdf1e26d3ae28a6cc2686d2dd0b21d3ad2858085582865535e14fac287fbda9fb953969c55b45399fdc6db2d99f762f5520441505fdb14a6ec80dde31c3869054942d3e5f975882810ea480068e1054146f378406258c8bde1ee19840e721e414a5d3204784a84ebd6914628a62d0d0719390c6990d300c0d2f74981a3407424a5190316689e68e93a2fce3f9437309fa356821a328655b7d96d62e3d9328d8b2757b17b1b78c1f694abf9de4261a8a82d83cf1a25386f4d98480a2e8f6b9514eba219f2807f51bcbf8d9cf7f433c513825db58e8d589a228ddf1c2a385d637cc1739d20e3e1d3a72cc60023ad28e3024e09c289d980ea31fc2f2541bb209e77b444982aa93261d219ad845c4dcdddcab624f3b339e52675d6562428799b8104cb49fa771f566f2a199c5d4c90d9b904b1484963e379974d4dd6e89c26997151d3d5c4b682a51facc0f639bd3ddf4a2045ea376a59db6e2597726089defe4924bc49c4461b38e1e25ba9960298648a2983b6927ef0f25bea190481444ce448426314e928443205178d33cb2ac67d466d203421e51927e55367237e7f02347946466d9664d0d25c81ed28882550665a22991ff358730a2f46153f3da5d791e6de6224a82f8dfa4398bac0e934c4594e4d4e8662685d02e32c44414534d29314676f21d250f4144d9e3be8a9efe1384ed87286e6c91351eb5f32879288418a2e8e16cc65c733734ee3d08294451efd7a4dfa87949720851ea2d492b7be389ed1e32886297b97ffac6d4b65510c5ffe0b91a36dc64dc8404a224dc2895fb61bbb64a2180289a2475c6f0491bf28782d6cff7c850881f4ae2e59bdd270fe23f8dd887d2951c4df521848c5b08e14349a84ce359478ae9b4410cd94341c6c9ae351e0fd143e974ba3399531f9287921cef0f5fafadac31082178285b85759b7d521b97ef501c5bbf19134fd212447628c8ad8ce727ed4e50e23a94b4aa4eae1b910ee5340bd99a4365e376e6500cbb7fa55743cfcc54851039947aee73ebe383ccd81421240ec552a35ac2dd835e4fa9100287828fc6b0fd9341dca76f28c7125fed4b7e4f9276433983aa3a21bdfb34d73614436df63e8ecc6ca76243d9ebac640de50cb7de4f520809216a2846d3bbfa1bd31e084943494c323267ff0d32ce090d651f194deabc4975533e4349c7e8e9a7d733611d33944346e8a09d3a34fe2a0326baeed615ef5a756d69397b4227133c870c8c503a68faeb3b8662923ec6a4c397ca18436228ba5ca8a75f3f0cc5f864f287e9f89a660386d2cc082d3ae612f24ed5215f28f868a8fb8ad80be5eb70e25ec649178af1c43d974cd326b5e642c98496933b7c49a6e4e5215b287b92844fdbd2d142b1543c8cac7337f9b31d21240b059d7a9ba945988ca2c5427964ee1979671a33ef902b94846d9518647718930bb142d1d48fc611b2e4fbdd2155287aa8f8dc768d9b5e542a94c4fca3b3bccee6ff35640ae5f64cba721fe5cdd31029943cc92685bedb8444a1f4254f9fb8957d1fc41028145bdbb36d4f5a71cd439e50ba93df395274520e1b14b04288134a2174468dbb18597aa24d086942a9c47aebad924129796d258430a1a083f0ccae494f67bd6a30762005344642965094b70cfa4d946ab0d5d031831b896620440905d3281a54e98b9067d6607b018e1b189284620e6d25c9d1d4ebc48cf922318e0d20a1abb84bd52f57ed7477ed2f4943ebc4d231492f78018e0d8081a30323232fc07143438e50eeffd5d839978c50104a2893791aa54048118aa976763f9c44285d8fce12ad5dad2f838290211473e3ee663511f2632e849228b14b84ce0b13f50e098297d662b1dae6d696997dd2768a7febec307608100a9a3a8e0739579710f283c29d3ce19d94185d4cea5808f14149caf8892729f9fe39ec84901e94048df13eee94986ccf85101e944bfe19fb5d0bfb18d500d94531ae96c6ec989500a28bb2c91a6312dd7cb6f4e7b928e620aea683c7677bb868f33ec5f36b64f4d23d5433eb6f79265121f3050e1b5ed8b8e120b728f5ba8c959c45ce44df16a593ac2499eb37b52849aac3549c8b12ed3b4600a145498999361ad24e3a4c8d0f300d0b03641685b5d2ef41b72a8ba299bc994e9ef10451e6485f00894531c86a198fd1438735018145b92f45ba8f277ded5f511a4de12b52abbd37c334cc15c5934ff55e061311bc0e1b5e24482b9283501bc62cfc1c660616b8619c15259d4d7c338d56e357ea551494524d22a61642d3abc1a68ad297d00ee2754c7335a928cdff06add9e52276b20e4050518c614ed0a15a66f7740a198098c234ab16d5dcd1b6b85c93c4b04b51da68723cb9c40a1dae2245e14569937f4209c828ca697e3e5dc7b5d4b841445112a37c8d4932588bce86a2bbbd31cb5cbd7299570f26e79b4b0ba52350147e4bf66cd289cdcee5274aa2bbaedc98209e287612b78474fb309e459d28a77bc6f5bb5b838dc66e200212f0c0062cb03b6c90767c81e369ac71a2248fdcb425c9de0e1d386200b28962abfa08796f16ba7e4701441305fdbc0a694af85cf24787b9310197024826caaaa941296974734991f9c0c8080d03c144714cb579f6a46aa1f49728766e36fdf3b6f145098825caf21faae4173d8254a2f056f623fcd449aa94400184124513e166268a6ab609029944297694304ace410b2347f7054024515077214a9b9cba9478a11bc946054646746c183bce0b209128e7b71e53e9b1a4062151cc2155554598f586e811e570be994aeca9d37103e288f2094235ddba8c4e7dd2888289cd7ca8944bdb9f11c538cfb6d1d3f6dcf9220a4a6d9d3eabd5543a2ba218375ed207b9b81373798928f9865c784751d2c37a8a8882cbefe92679276812563d4471ecd3a49b983a8f980d51d87e8d9a4449a96a21ca2574ceb61d97ee7a6a1a4008512c51212343dc54add783288c69d90d76662686ba0451928412f79817d3a9633410c5922b931fa46f8edf8028aa9bdce3eb39893f1dd53f946563efe951e29d0e0dc78e2f30303212468e1d3a6cbc20e5f001881f4a26eb4e12a4cb75ec3fad046d3901903e144dcf4ede58e7e7beedf8c22c1f8af1393bb2346934181949a301eea1186bbc44d9ca9247da1cb0014604d468a046036403881e4aaf3d5f1ecaa2745e7cf0d4776e0282874c7fd32c766d903b94ab4c099ff1be1e80d8a118f79d444d3491ef79903a94abc64e7692322074e0e4ea8469de8f39d3207328a8e692b9a4aa92ab1518618ce0032323378ed9910307188d0510391484899afbe1dbffe4d1c8881840e2509017699a31ac9c5af10361ec0863c75d01040e9ccc6c6db8a86dd6568d8bfe390b4d2f1d1840de50d48da3f9c4dea83e017143614ebe927d93aba1d394e30309d286d2ee954ceb7e15669240d850f224497afc3f644cf2bd86c27ada5bdf2e13b6936a28b9ade9f4df2bf9b408928672492174dce42428b184d150ececb6724f7286527a68f5384de2a8d640cc50fa5c9da579739224e945014819b22e7db396cb9ccdcab60ab91b435c329483c9c5472f41a6f0fc1709640cc53e416ace4932e95d8f183c133911f3b894176ffd2cda774be82409da3094e48866feb79e3eb1beb091e80002868214036628f69a9cfeecb1392f4349cbde079bf1246a92a12c3ac58927b66994c6504cf5d9caf327f9491443f16343ff6a34f9d10b43418c8d7f55cac61018ca416b9650d1f00bc5603ac953ee412f143c6e4c6b0d73d5a42e14d46dafe38bd5671217ca9fef9318634cd361da42e1c2c7e563490b2541ef62ef04cd12cf42d13c2613a733c81863a1709acf4b26a5f55d7485525f27dd21f3562886fb15b59a5385d2fc86aef6b0a2939c0aa577d7ef6aaf3f319f423183b4d8183a278572e5ca565aefd6b517859286bed3f4f3db3f0f0ae535d1d3bfc94dd7dc3da1a04c18ed9dec5ef3764e28af2641fdf5f3a6d8ae09c9f5b16342514fcc6e6727f163b484a2897a8dd149899e7f2594e47ad73f3d6523f3492869f66bcfa5ba44ed91500ca54fddca8b34217384f249a2e77f527c148d110a4a66f9bd986c21572b42396fdde9188412b755234271e4372971b65f42694328886c759bc999108ad19424e61c93582a0a42b1e37f480f72d63c09100abe3a3af4930e17273f28284126997d261f144f932e31714c9f7f801e9484cf415975d4fdf7003c28675f3de9a4162bf377c1ca09be2e8a1bbdb54a2e4974d4e4a2e8d77165548e8b92e49bb673ee5b9462f49489a2db99c4b628a9eed30fd98d49676b5114dba0e46aac6d132d8afd7f2f22c3f8c8cca264c266917d22d4c9aa2c4a4a12d3dcd13fbe83b128888955af76b028fe7749ba47af28ae49b37152349c1c734539c878b092561453ad4e6a69062bca412693ac24c964c62acaf3a394789a255baaafc1965e9003477268e7aa2856f95baf9bb8561eeb9a918a927a9cbf37cb74062a0a329c1275cf2c3d8612d55394b3e77ce2b57ae9983e39c314a5971367e489210c600e1b34c0d091368719a5289a28414613a91e834e328314a59149c75c7d255c09f3288aa73c6cfcd56d12bc1545418ef9ffa6e7a431d65014c7d3d9fedec8e634ce0045d9cc63da8d267fa29c6378537b0f2f08333c5134b9d94f9cce933c9c086146274a5ba7a93fd5a91739274ae2c27ffb45661345cd6a66829c68a2549e644e63b9759d431d99289fa432c79c3b87cc66c244c13c9a70728e9de424d34b94cfe478d0a9d34dc7184b143fee97246e095fb2a54a1474fc28254af2e329a52d6f6338d3248a26a8d52cfa45a937257692a8744c4aaedff845c22caf8c8bafcbcc6640a2a0252725ba4b660d763fa2ec26423f9d876e80e10506c290800e1de6033f32628e28ca261d7f54f56473dc88f2bce64d723849ce378d11054b39f94435661125f5257ccc3e1ddbeeafc1b68a289d34e55b5b42a912ce1a6c896846224aeae733c70fa74f4bfbc00c4494a418956d2b0b59265a830d2fd9c8b1230f51ee12f22491bab6316d88a249db498d495b0d362fb610659fdb9884931673926c924e8882892935c9f9bad2cfe54114c634adf4478931ef331a36c051831d2d88e27a8ac664258fb867f502512cafdd76ed58191b5483adec055fd8a8a1c3d4600ecc00444136a3ad7fd49c4cbcf50fe530a3da4d78925525a9cc0f255b2f0ddd1dcdf2ad1a6cfe3a6c78917d28a6d298a64c30d9b0976d0433f85012a72709a34e7c2891ead843a9af3bc913e4c44c92e9a19877264f4306714f661e0a3fa7a49244d66c4c8235180f25bd262664b0f90e2571691ee5e490f13b5866d8a1a03bed9efe2872b3dd3a149387cf5869a1379ea543d94af3ccc3c8e8756a0fcc9843314bcdc46fbad5242b95435937e7e6f2f7d62c9b068d2f72dc0566c4a16876b2ac7e9998736f376ed8d0c0c8080e1b34c0d0a170405fd8a8c11733a0f1858d17a42380196f28ac26a1ddcdca33fcc80da512a49669d28c2d199cd106adb3765c4ee3ebf6b624e126edb406931a2c81196c28789589b44c4d621499196b280793ba57b23769c56723497206374c093050810a8c8cdc0c359474d07792c54913a1d58c3494eeb46c940b1d0dbc75a8c766b85ab57bacdc76c7757bdb93d367f0247d628dd68f3135c30c5adc985e6dc7b7cf9aa0d3e9b01b3c2e98518672d75e5dedc9e1248d93a19449db9fd01babc3555a983186624c279949426aa8927a1d4498218682b5bb991c113a97492e0cc5d1e26d2eff926006184aff497cc6541d4d27fe4241cd49f7dab02e1af6195e2828c9afb232fc6a841d82195d2807d5a964f3dfe7b26770a15c6a25535dfd334fce164ab277ed33b4a0cb8abba95d768cf7e954219394a1429784195928799824a704b5f179923768243070bc0eb45481195828d6edc94db6a13e06255728867cd3d1f74c073193154ebad7375528099d33a375cee0c1cc195428c694efcdb7dda227c98c29b81a5aaeefaab55b3bf7c17474c96ef26d7c86148a27fb397614e5aaa504258e4229e7347f6dc93de97fa6614607faa43b980185c2a726a957a3abf5d68c2794fb241bfdb20e223c35c3090539654a12219e0d1c25581ad684921457ba69b1a9c10c2614e3ab986cb9bed924c33037d2054646900d528d194b28cb891f19b92e66b5cd5082b7367abbba79e99d1b9b1f4f3213bb8327abd28b1949288acc7ddf70531eaa9c8184820eb293f5f999f84e728472ecbc61da24f1f58c5092bb214e47e69358a72214d53d93189359c2dc3e8308e5b23053da3c4fd02447c28c2194e4e851afee5763fe720f6608a15c32cec5fe974128690f2243892598b7d84028c924aeae2773db2fd98c1f9447274bab0f194684b855878ec6b1038c3072fc3260860f4aa263b29b4fc2f95a23233b8c173970243474e8483968b4ea408ec18c1ea04e77f860ab5bff60060f4a2f2ae36dcef50329c72e0a57c29bce6c2ab77545745136493e88589b45725166aa5c6b68bddcaa665372cc5662a5082e8a26863ebd9fe46f518e6de99a525f933cdd16a5d09251ebf67115e25a94574bd2923eda9c7f9a0722b42826af3c99519e26671f0922b32829b9d03a32f20e1c37443032c2344cc70e1c3774b489c8a27842a9c7c660bde3ab482c4a4a8c7bf8acb2af994404169e7610defb723a2bf20a342e652f3fb52edc56664f4e3b3ff94950fe8bb8a2241b94924508a519b3482b4a6a4cecb4666445e916ab57f7f2221f931c4a64ab2849d2fce6cfd19f04d1c006420a789f79aaa2e8663da6c4841505915494f6ab75115494ee4deba67dfe5a6e4e5150326da9dde7a62896bb8949d4a41a4e264b5132bd762f6f9d14056d576272e3f7f9dca3286bda98247b6ca2286d12be2773cc9cb39484a2a0325567aab7cd730b14bda5cee88e8bc5dd9bcdc9cb9c7429ad41fd094d90499df613634646aa20e289621a159367279cccdd1736d01733c0c197db9d28996bad7f4cd169e18d8c340d4e944da59670ebe3043651925475bdf66755359188268a79afc494f5fd8a934cd808264c2f6123229628772aa953995279caf42295289a4a134294d49121935062d35ae6159b9426c1ea6668ea55c678d5e59ba45398364d566e9244a9475b7cadf2b2486ca1646e0689e298c91c79ab0aad311d11794449b0f592e4ef501131d760a3618354832d8788234ac2df8852ba57b33b74b84823ca364ae77ebc0e8d18a40406da17228c288bee1863c7cdacd9fd46c0082c605208d2dbb82182911a228b28868a5f8f6b9fbb9583e1858d91111c364646bec0a1030634b405228a280977dff91f5293683d1105214bd89f0e6f15999374208b20a2fc23deff74fa523a260f510ef11ad373d0a618182fca54c410ec5dadd88a58ebee7ed7bc0793ae329a8a931a6c5ee0d8a16306bf35102944c1840d1354644efef2498108218a394a64e4751c573f890ca2bc1bec4e7e9f2cca4f228228a63c65db1b736ab0e1b07127120816880022edb091c3047f285b9727a9276ea3a13581881f8a9a42555ee329d287a275566cd49817e1c36154ec9c52377e61e36c0fe5fa2469773ea9ea7ee481f485074620a287e28aa692bb9392c29987b28a2c413de7524ad398be2081081e4aba2fe2624d4c3207e51d4a97fdf9a5ea59262f0c88d8a12c3a5c7f971426326d7528dec8ee133d4b9f7adae9508cdf4e72963d615f631b7328a717d95a1dda47ea470e0535cd7f259e395ddc3814f4ff860eafed24281d0e997ab4366320f286f2266d720c278e0803113794a35f87f7283f32657403078ea37a43a40de5f513446689d85012a2836937c14e67c3226b285a6813b3848786306d0944d480756dd5c8d957b5876946d951557991034705bcc88123e11c3bc04843e1c62d94dca413414341094aee5eeb3db34e32226728fbfd8e8e8977a208cf0c6ea41c60708b98a13832c9b2a14ffa128498460dd2176b1210294339469ab9072ff17a823576980f648d1d068c0c63c7a50819cad5ceddd4f48e11d797b992c3acb16387c8180aaa969fd4c888a178aa36dfc9d341aafe226128e68b29b935f747100143e9a450e246448928b143912f94637e9334447cd7c639125b8ed41d10f142f9248cf8a8342ee622e25024120704c280301406087817631308001840200bc582c1603c8fa4e5071400045d2c1e382a28202222141610200e0a05e26018100687c2603020180a07c3a07060889848a13cc84a465924120ed7ceb69ea9b4098b722d7dd09432be77f5e1807c0cf37d9cabd472e45825b01d523c9f625f3015d69ec37630284183e3934398af55e45ba2c7890e55515dc24dcd41c9f84d1712d2c96bd9bff8e486f9f87580baa23820464244a92aa08ff6605deb60c245f6e102f245f86bb530e918290e8e6173112a6c90ec31612c66379e4f79b63327ad8dfeffe8a6d3caf1eb006545814348321f473e05797a7aaf45c4eee9748072549d72bf545b9de82a852bad73119274e0bdcd53daf917e5dad18b328b5014ab95d61e3a8254a13a29aaeb9e35d053ee0e780dce9cc2202c11ae2d7839e9cff3ea112d7fed0fceafd7c90fa108b3db0353144633c05308fa5226915d5774e21ccce31292bee1756da22ac3e93fc8b373d89970c601c2aab0266420fce15b02aaa558ffe3f174a17f8ebbcd1f56ef873933a82c69963386035ef8c846391bb4dbcc6abe3441d2ce5771c476370a6d6f79bb365bd4b0927fdb1876b1577ae1f46a73b69b320fcabbc0cb36e4187cda4229b0259ff42178e4651e0b0f7145aea331f45a2a46c09e9092a6666c3d523cadb41dcde8ee927cd38d04aa6ae07310a2878a4c6d074d2b08e5d82a0a3f3a5d4f594cfe2c8f439ed132eec9052460439f23106b1b386904f6de602d2aae1b7f959ba5c9adbd8acf26738e272adcdbc23ca535de78a5aa8780edab7a37057da4bfd9e40a3fb22259d3fb0e7ba4139275ad4f3fa5272ec2572dfe1a3c2fa1872f6c88bf00f377fcb2dabc820bae4a30c6b880dd6fed190455f501db1421240fab0ba7fbce9d59800ff6f4819111f35be8aae8d0794f410b7e88dba392d6820eba407a34b797c5c7ace324961cc0e4acc67bd0f66c3e924107ff53ffef850822ebf57c21383ca3bd40bd5f8c6eebb46c4795512fd0e2a2da14154d0c57cd84e761a410f7cc68f5445d998e8973b6e4ae0330d0e72f0877a15bcc046ceb0b35428302e7138eafa5687a8bf9f1b5034ba1c0906d77ea591c75a1132c7c9155b1096b2a6e726914721e60b31ede0c2eb1a7774ecb684256dacb05c791aa73819be13f0cd744c422b8146168522b818ba6ab5c8c52ad333d888e2d54158987e84544d36c760abd0d5c16a1a27e949e422b02eb91ce192456d448426eaf1c652bf8ec0bc3d39442a53b62435662298cd421099c3930450970c4d62a37d460c25c512568d94213521e0c9bd41165ad133c04cf4102419482d6eaa4523b25caec01e398d97027c0890aa113f29716c046db743c285addacc176277816c63a73ee486f346ac2b7d6428e53084937d8d6a68365b82105ec37914bdb042becf5154d92646d13885f9ff1fece955ded89c7265b950ad2774d2310476e678984e0562f731f56913a9919ac2ad81c57d8725694c04818e64c54303e1abadc864f0d694d912894969298e8f1bc078876677518e38327911d46b9c559e6602801ca4181515d810ac477eb7ee99ef8ff24c2380c3f76c3b1e19e1f3e3434018683fb0054e9c6ae238f69728174e1188d2a6bb46ae187780b987abd86c3271b9502660f34b9043a6b6092b0b0762910ae706332b35abb588a0260cd62ff5ca4f1cf210758749902e447e0e1b6de7d0d8d2fb4df7bd98cbb8fab0dbb7aedb3178abeafebf6ebbd3080a146a46111b19e099e4c2d83de232888aa25f6501121a04a0c94fd9b9adcf151bfe7fe20dc2c908696fafd38f7e2ab03e2635cb81786eaa225be282a45741dc820ef176a50b6dd1f8cf99cae3a41901f3c1f3580b6212039d7180bdfab291cf6eeb430b183e69924ec706c148f92aa6e038b3cb76c18a18a5797fd959a493eafcddc0971837f22089ce69bf88038170acecbdf5a8105706b1ddc15028571cb4c9b09bd01d90f384c067e78151fd87db988dd50a3493a1a898ef43a621f1053d427e8b20198717adf2f7700407008a0b8be44be5cc54afb8fa43f1ed028b253f4c52745d5e3050be6f8be07afd12a011e07c2383614bfece97e44e624b5e60bcab5756867fd2311f1db8804c87d5bdd1f846edf8396b2a324ce82774f1bdbb8772fa3da6376eca4c7a5c5b91b0e2d1038321eda4b598d45c2afbc285f48124a2d2687647822aaf11fc23a55b54501c4fe13b8beb0f390676a09687ae6f9f890ce357359d51fc5b1bb1ef3bf8314ec4115336adca10e3fb7689ee97a9203915eb59a3b0c70c41341b34fda31831d8470875101fb1f3c819c9e88f8af550aa36d5e20b54a78d21999a066502a6791f0ea67564afd6205a578b00927fcf6e07e9a45583d9f981a6d640e20ba6a99b9a45723051a9bf7b85becfffb0f652525a046181dd930370e0debc158de1387e6b738510219a3b331ca5905eee3948fed9bbb8d6662926a0e492dfc8c9a92b30a33d9c05d76ff32e38a48bc0041bc960330881d04f7229a401c3a6da64863748dc1f40ede9c25997558b4cb268033eb420264806441d030c47235f008135c20db999d028aa38c8df7f1f9b5c9304c38518c52be922d56cebbdd281ad2803a81eecb38bb2c025d0be123366106a2c82cd8ceda11e7e7375fcfa93634240c41506f9b1a2947de44a04d2ebaca6a11f930412771d67a241aa388a9db2732da1974606872240260881329cf23ea6233b923acab2565438fa42704a9a873f759fe845a6e6c96cb53692fc526fa20b22a33667c5777c71a91ea814e1fbe83e324902d54713dd6a9cb15ed8443b77525f75ce922be2b61902cc3ea5682932f925ec05882610e4434a0d2a8a3cbf7874bd0f2d95dde3c9b68322560576ba8c40166fa6baae98e0eb6d211705d25f0a3285a53bd212a7e5d5299396fd8200066a88ca9d6cc66ec4a43f49e8ec9c2465040ab5ba90f346a08230b0a02ee10379d2ecce245d85c49e75d62bf865588c021c408206bb18cd67742306b9d44c786b6ad9a99da2b319f78d19a3da02b5ea597e208c0020521653a050ed39ba49b24e084e4c98d52ab0c946086387b36b320f8140963fd9477b9271a60323a7f8928c5fb44abb6a62f9998b0f672fc7f28627ab761b6258095861a9e620d50dec416d2fd0b8f0d81c8fc794364232ec93f557adda65681487724ac6445c56dab03e584a755bc1242ae5210aa77c9ccc16250c612ff66f1efe3af262d5c9ee6b89bc62d99dd7686e370052f1c6da49e500a5cf9aa90c7dbc2c65dce2f74e2a754974b01bf8dcc066429ec044494f8c7332fcde675a8ab36b622fddcfec0989740812a3456443a70a274ec6abd01e9aa64d0246915b93443e7a516db548a0a2071b1fae220d337c0a444ecfd9769c7a03c251839300210f2aa621706a21d110f8139241fa0ded38f88fdeba549e0ac8d683fc8921fd3342a91ae0c1b30a15dddd32adcfef6497d264564c6b595143ae972033c71ccec92f90c4f09769a24d6d225dd4b958ceb044db512e870b73c84d5aea636e98926b072544c8e64582bb76a2c259aeae4d0674c85530cd021b234957de14da2998278baf587b19fb987909014791e133a5bc03170fb1e474b1a8c12b4794f0038b912c5883b3190ef07cc1ad14b1f86989bb8cf245c7596705e52ca4bd0498014f880714173b3610d5279110895611f9a8dcd3b653012d0523d411993e164a11401285bbe007224d9cc86aa01763f3a4ee02a13864984482f61050b23f57f15028eee973784aa03b4a6a6f6468e3e9c03f26ae2c390946c736dc50832dbaf9740c59f954552c497a61784629b5c09decbcb91cc7005527bb38144443a2019e46e0729ce956ce4f7b9c56a012716183798de658daf4f82f9337260ad0f8f32805b2d88190a19655b74ee43a6d8af6aecd71ad32f5d0dc2312da6a2d446623c01f687bf9fe0c09a73f3bc583d22a4ee9d97eb78c8e18d44d8113173b553a8f9ac56407a39b49d394129fffa20a25953e17643c6d9d97aa7a0ee6b9c23aec54e3a842e96ecb6512c8b6c43dcacb4a03792d555018a38ffcd3c915b98904675e09d177cfe7590b2e469b16755fcd8896c462b8038bfe94e3668f4a8d0a1fc44ea95a2009c0e0e5846df663fe1eb0d1374800b4f0c87321c7905aa5c61be0c0a934206ed412a4e9b4d08ee7215b933fe0807d8b95e174297d4787d26d12df268d20a1500d787d2080601e8a2c1a7c7a57c889d7d7b08b71248825eb0297e524ca3f6d5935ce4749f8ed09c52718f8008bdd8a5581308e6638d4b97bcc8e10401d97257730998c366d829fe96f82b339854553f430ad419f89742fe06115f757d742bf6d3ae630905a9106d2addf5da2a9dff21e49962f4b37b3477e2308914605bdf7185733cc0ec50cf9b9c02d55c9e520c07ff8ad4941885975bdde0d48e9aa6539187464c02db071d575a9f6a95a9d25301e12cf88d4141241099ff670d1d1a5b1d152ccd1f1c0ad9ec0d72ab68cf6b53244f00fa33140059409d4a1f1ac604dcdda0ee6cde7259f8ae32255209c19b59c8d8b24349e02e092a48427192a2c3b8e2ec6b5a1a8acff40ad186f4a7026b17955db315090237b0c5fa68538b5c7c313b91cac115d0a76855eefa8ba84ab85ac03be9e1220f3942b518785f23ed330f913ab13bff1a092c85942334e69c2edcd117d49b785d935340fe60099e648edc43d3b17c54add1097f678362c7e2dc615c513a4942bf4e65e57f6105d6b0b44e8a7c34d6f15f710be6ebf3019b7a4af447fc6e77fb174e2b9a7c922b6b7b391ac14c344db53ea90a0e47459a134052c822d2d853652d1a2a26efda3bd70f5db50934ff96480df5e3317852ae1e2866bfd5920911dd369c33067c9065c6569c0c750f147809f6e05c09cc2d17ea79c19491443f476133d22b88db39235966be720f040a30c0aebb7124119069c7acafd02338711f1342e2f16f8a531bbc2af5fa58de49ecfeb1d4a2fb59e785ac7a076627a8a83406592d4e8156304be368b9d1a5d4d566d18a4a24bd8c520886a69b4149b7ac08215356b8db0eb952aedeca6255f2263cdd8694855eafbbd2a20b2e7b42b418a7828ae37b5698b9afc35cf00d28496955768a3d9d9450cdd6ef514f9bc781270c635c8f0e1d37c75b2f41c35c218b55147feb49a695d9528a2d406263919471a163ee93f1a0b43a245bc676a5d09f2d5171de63844ea1381dee42c152fd26d2d858ec2b4ab34f1d5713ad35419169170656e90a1ae2e2feee208c9a2063083174c9222ee2e33ed0210858992c417266d729ab583d5288093b29a1e530a7f3a95626cabbe8b010f694d01ac3afa60f54fb3222292c6660f3011116ee96720aba018bb695ee373c9bb60dd6671599078db77bbcd3f365c3ce2b88ef01b89be8568c17b384f0ae388799d0489974679e365d304342a3f278249f024d7715b26292905070c1a8e4d23f47d1f51f15a2671c6c466c2003b3af8daca0eb678c78d15c70f0ab61e967e6a7d8b35addfc2f2f363887cb9156ff632e790e6e2fa1d422f6135bd63cb0318bd1a63df3552e40793db508f7fa0ecada2ff7cf0825ea9980158aeada8600406a292f7db5d05719a1d3c42629840424785f37fc9c9417e82135449260ed2c3688fdf1e08958a4e764e30b0b05a5ca6a480eed3e1a2d78aed03383e2f07aa6976faa70bb4ed36a92b138890662f8844946ec6d23efba0689c00a2853641981fccf52780fba03a24fd4dcf40011e2353a88ced001ca2cc6058eb72672629474bc5d267c3aeb615cf5f1521607192fada480a670e2868364996802e0559e1ad0b07b2a05601a11fce3d27d095e99eee507734ad6dc2cea8e56769768726801fc13f3d01c40c1212efe19eaac4990350275eac36100a511ed82a60ca88e3bf731a257f0b66f10390c6b067a43635b6fbbd7308b2dda078189359c70c9bcefba6c0d6b0489c2490248ac9e053ed00289aa6ce618d7160958da7f66f9fe59766e6cf246f585e583082a51cf29a524847386ee83d3436a13c49f0b6484932cf2e4a69957f41f218333ce0dc5c905208dc0cba181eb608aa23f9fcf07893f8cc9434f7d163862e1aacc0f79855f20f122f998a2f634c1a88e0126c79e4b883135c41b4af65164292839e200d83f73ff1505e85a202fd9401cc2451e036195ff860ac012409911044b5a13673bfdb10716b10300f758ddf91d9eaed1287a9f94aae6b54465f3d35468b0d78d89257a36a969bc25155450e900e0ed8854753b16a8a743b89583fa24f52a25028dbe8fa2714396dc6610b242f1c1d362e44b39855667510555bead00be5a5baccf99073870d1367a527eebeb32d0c40da80449ca951e27d41cb80ebf4c53aeedbf0f2a008ee04d3e990018e2d7092c2a4f4110a148496bcbe8fc0f043d352de135b9675ca06c9e473d1640c7534f36325f54b2d71e2d54eeef8c58984cc9b5f3129dcb3717b87e85e0ca743c235fd540f751f4b4836040bc138441f88c9a8bd21ae2329604646c7d0efd663240839e1d143f660058031f85cb363c2d00812aa084f4e6392570a757417276167c498a2af66f9514f1295065ac794073d238b107c18ac480f83a988e1d782c6f21634a913cf3e8e1c8520aa8decaa2c853edf1b8d604fc9b69b5ea11ae217084d0b336e4b2da8b9a8c3680761dca802bc61aa790630d1cb3d80e1684fddd43720480fe6f21c011d8a0e0f7b9d8749571b77e050dab18587b43254f4fa2821350e4946d5891803639da942810f20c98165a5614d77a1c622067843bb40055d990ecba8122fe18b5ab50be26d006b4de9c0cc1f096cff2437443b6005881a0e534de1e019410311401761578cf93691d9fca5c63727a949bf5a09f3d9f8805715df84a735aeaed726e00746913dccd81f35495d44a28570d863496b2753d7d7432f99776a6cf4aa2fae3373ad8f1e267993c2b3d1eecc299cfe272e64b9250e3e05b3639ab648b510b2ef6f92a2ef428d8171364b63feb31662867bf5f80b464ce79e5cc2c914c2b5a18f496975f232de27ab265d805ae906dd5fa973569a56f39261d9a01d67fd14c0fff1fb30359af2428e869067958be1c4349896038db61cdcb84a87c5ceae01fffa6fd4f49b6383bd1dd72dbc254a907054ad4c34c4ea2ed96b48822f0f3451b5b8dc88c3b87062a2e2489aef45efb86311c2ad0c9dc362861e347f41858f84e79991069b5a2662f826d56f5e7329b70135759a6b106b2b8fd80ef962831994c701c74d4e6ce09433437022744cddca30112e6dbcc9e07cd078a716613abc9de5a7c92a30652e5c74c4f14b8e327966c638e9a630112abf1139912c12259f6ba401de71fbcc94c09bf680d06118354c1412469aff8541aad5b16d39bb60bd08a576d53ae1cfc32a34e3838869208239d99808caf56cf810c29bac613e940aaecfbeb389e78d95db8f410749bc3aacce58b842aa765fc02d820fc0c62317201ca1f9b3686a08d3c2f6e5ae5fb4a26dab4b215e0010642c8a08f02ce5795be2da317161b57657a9f0ee14b3fb5b52016e65c9799e2c85b70af4ec05b50d19b0b4ff69009437df6db762f847fe47257c6d0b435754922118de5810b2279664d67ae0898ff235e1073dca5c36b533ae29983efc0f08d67dae85c97ef76f6051aee294081849a04cfeebf89cdd049270ac70f3d88751f9c354aebd9caba94120ea25f81e2a2ca9bb910dbacceac207dc7b0a1a87802f48d664723166abb052d9db3b811d98238f0d1d4a504007ccad3e821ce1a4750bbe0c05f9bdea66bc207b9bc678bac4da816bc2a5ec9079ce995e884b4107194051f6ff570471dee9fd9e628ac8858499056fdfe752282632dbdf84a70be8ed265966909336f3c5c86c38ba4887c4f198cd9d7d04ced89506d039e5693db71569381f028b0c7aaa722c0a9e5fad662a4347006f314a90b69d0139dc03603db6ba5aea88f6b515a32bc7e86dc558a9c7f94a663da13862447004266b6ad1fec40316b5cd40b7a3f3f7b7532acf66c9d887f4419f421aee2e299c27579b5341619428e199861db1b7565a0139a198e0b179223bd765023479d4ec3b7e29ca85ae8650245e8bcc799b3e56f2960cc40c040318a0afe517a7ace32874a1b27f66219e06fcf0ee3909dddad8c050e2a0708fe007c23d5e6b64b2c4ac0e3eaf77dd25b7d9c1829eb1ba0d5a6f065d9a923d697d6ebdbbc885f1b242feb9472654920750a0467883c42416cddbbfd40d8d02a9a9530d9b547e6669b593c4e35f84d0be638732908643a86105448d39fc9602f890eb4503b0547004727b06033c13ccb2c6e6854bc1d35c5483286d34e13490089381b59eaa681b9a27b27d70df81da0b4c05414e03ef8ffcd08005ec5fffff036b5c8e68534190542ed9a5161f90ed0fe7da74c0edb685d34457bed81660b67e900af064302043f3b410d8664da67e78e1ab6fd54bae558c6520ab154c53d6631691bf7eebce1bd08dadbcbed946ee0a23a8a48f1240942bb6d931f3067f9d3507e3960b73404b6971a98b6a2e288c726ac3cd03b70e4760b46e9562a911a3cd98234a168aa703f28486635ac54e37d817a8bb63d362ff8db5d41b7cd235663c2c37667e7b10e3a473c0d5c620c3377f5481dbf59ad255a25493ef846e43088f22ea66cee369f488bd479e6fa718052155b39ea498f3daa6bea009324465860318cad62f5d5c9f1ecbd5dc8daeca28079f33c82e93ae9da915320484d7524f59f0733e1ce5fbb140bbdda5bd289f03aa3283d5912df7950890e93ad9318588dc242d3eb43d719712bbf16235f7575f3c226784dfc1e3795e55e70bc8ce3853e5e13655cab5ec77e41072b9297d4febabef0826b1ff442e9d5d52b8217a1e5eb180536c6b57dafa3fba0e338dfeb388d42fdc9d645a32b4221a13f91afdb9fc250e7b7f8f61e734fc45e030e287e46696200702e6222c639841876b35143c659f85cad1aad1e32fa178b0cf1ce6b243971f3d429ef7c604baf5437d174296a71b988507c410915e55227b9c5e108359da2764b41820211aa15140642e990caaf94afb7562a9062094508460143324498cd45f5a1e6a46b6f287f15ea1ef05fb02880920c550c54f780bad79b6e189a424d812a12d45645011bc22207281a059c2cecdb4a100a381a38e10b7b7201aa1f54538702628c3c02ae8d0252214000d1a280324b0cb38c1e052c7342331416d577fa40967443511305045d9e9de2c6a29abe0c8b17ab85e09fc5d70e2048e024a4d358e166f40c8f6b43ed817ef0ec4331f4131e2baf19ac9d057914a7e0e3eeab7c3db2b208e2509d71c147f86a9f3be50282713d55645f88af7a15be87615d4495bd2dc6a8fb427d232a8214a850ada00e270a9b14f350bd4061a7281d922af14b09144250ed3a0a083a032e3d4e244640b534a921543ea35890ea050509d568a87507a54326e8fe690425010aa125aa0f438b9506a8eb1054df0f7cd40154a20fa583f18c1c3d8132172a9ba1fa3eb58b9222c24b7e84c248a8becf3d2ada0475eea38072f268647c72a212fc25469197164b81e9e37f9b83410b516967b8132b6c44695f149f812806fa540762448b86a5473e1ce550e7f966e453b4efe4cfa52a7d84f6626b1ebc250b45213da1d7d8b04a35e5020c9bfb946b1bff1fe0dc47834ac60ca55d7adbf957100aaedefdcb5384b1ac4a1138699116a4586a672578886349426e834c431650aa69f7f045a5c2f5aeb25d3bb7201d5e17355380e5fa78005fed18818d43382afdac8c11d350533b9bab8845c66afba0b9f2cea83e66273351f5d5a20f94b34587d71fb1862fa036cb7e001b06c088b8a4029187c1896742172d1ff9cac5be32225871dfd7f9a7d86392fa09f3fb413ea229255274d6c46d6ba7b8db43d7f7530a7390f9112461eaeca3dc10077f49bb8f67b43176a4982c2e7b6eb905ef2a864f6781f4c543880ead514f03a783dbbf3e29852f99d0719dac27ab5282e66b3933d858a0c627d215dafb89895430da6f8620b9df8e71d433bc68efac086cd490cc331502c822bb1cfe12104b4271fdbdf3fc83462865c5ec3465acee9a0451429331a115f63509b5ecda5fadb866df81dfbc3155110201537fb67b356d823cef5c23465362e24fd1221039ba7531b49053173ae3427b5305bd7d7a78291cfd00b3adbecc73051fd6e38f133060ea4164295a43debc0dabfbc2bfe144775e04835f3fa5e3431e85c66dc4677201ac0d497898a8c126ab3ca41388604b4bc2c6ad24feed9825b2134a80de1df788e6bb61a9fee2dcc10f6232682ce7e0b4513cf63566900ba69604ce80420a46e04cfc784f3200145212b5b9c87f37de88c8c70fc4dc100004f64d31561b7984cfa6a144dc8daf0aabc5474542afa664f6b227606de89004c3e9c81d4d1098e1b26a6bd20689ffa36ca8464f4f410fa371b0b51538053cf6c0bfb83f6047875a37ba0d979909c4b79549098057a953f6168df99b8393c5f1b4a925830eb7ff085f58340fb5b7c2291e86c35f9012282b6e7b4ea26926e13f89eaa960c2a3338634375f55a0bd7141120b004bb767106d0174c5d5a129481437e7b381745ba094a00548ab30bfa58457319333410b318d92934977c406166522750b523d17b0b5b76f6cab8d15d6314ef8ecea61506e1cff1547e14212a045b86aa38d98d2e3a6cc932674a4541daf848f998421bf044ff8bb4e0cef0481d1c955155939525f0031bb15e9a7535bcb75d7a6f48d3da0229a5a9dafa1114c46a8d0b960024d461b50d03b52df4d4a61988e799b62328d20c0691c3b2ea93d990b60d1d357d02fef1a1919c4df20a36174ce097c124952e103517da305901d0cbe73d9e5721244e1ebef4a1f1e564781099050400404f9c730c50d68040cd277ab6250e2d8e6c1cdb7cd0f6a12c5fef0fae0ae31e9f1cbaf6ffaebd1ac9e9a5ee3d4951b277a5ab3752ec69e551f6d0265161d1ee233c931be066c7922f1b2c0d7728f36ac95dfdc62de49f7d21206d9c2b237c096c93305d6728114958856ab6dc3cac125dc1b9ba148b5860ce98542645847757e2a6b25830b864de20be2d971ce56894beb10e91296872a22e1294121c5b7438601f62c6e374e880f530da3b8d34fb7ca0ddb79bd8b23d8cc4b6ded4d5f840bc5831ced1814178e774b706ed296e0516529a6a8a6afb31679a5bb6ed0f8f58be9a293f1a9529b26675da05c14354111f0c49c5307abb8c1413596dc183a54017a9c8f2428b80d4d8277d95cfe3124b17aea25e28f0bdaff55e443d0d97e7468477d4b89edc0808da7e2f167182090900905a258e42fc32c9af6513789e6fb6f3ca792bdf691ab00838b79be43d6e537485af9934497d4138055c49c24f94caef989e2b738af4dc34b67d95a65f0a956ee4f81a1db335e92682ea6bcc7c72aa1dac88ebd3e8b1334ee77a0aece4e992fb6f4d822f6e8108eba420df73d185279a8559df1751521b8415875338443f5c6566d5e8246b19c690ca1771c1aca226fc26f372cd0fee2e2a87067ce6ef3c6796e78c61f491860bedb2f699699746c9d37302ffc9381982f17cc88d0e402d84184c417785a7326b8fd24ace351ec97b0f3491612d5a33e11df9b3b37b740658067878f696fa1fbab6c2eb5fb50f1b759cc8e888c3c2945314749fbfe1cd7d2c1bc62657003e5c7cc4dad6e00f5b3d2b9b6a6569158805409bbfc87eb385121c3e3992325b1a8bd50fddc237a40e0994d78c39fc9b517ee5ffa70ed8a785e40b453250f08d1c5fecd28eb121de2005a0c3b030b23fd5f0ab120eab7932e316e752ce4a2f328bb715caf12d82113ec7e4e60ffcf1252d48a78d74cb7e7739efe3516d7ff9c7b22bef729617727b4de7b309e39a1b0dd7954dcf9e3e6ae387c66ac4d0c88627619ec25e2218a993e45aaeec23e257fdbfa3c48c5c05e46d2b02dc890de9dc69027e5bdf8040edbdb7007f00a3036346c8db331162fee34b4d61f244497e9e611e2316cc5be715a1206dc1619f5bf2de851dc5cccb8b388e74d0f36ceca4f6dcf5061ecff616f1e743e132d8a55f1cf06c788ef50de070ec446dd48f70b278705a9b45e2f8c788a807df8dddf5e5d8b69f24c4009b69f7f5366441049a50099c6bfbe51cf8397458e7e2c0c058fef0e6ccaca709474eda08f08f458d76f1b43cf10e1879a1905b7ad3d7f70247bfd76583ede143cd279d19252b97e912b9f62c2e3ba249f337c0246e6ca358ce825698af2e00956159e4559d61f53526fd9e3e03621b182f0d62aa10dbd138a13586cc00e22e789f28e76b64219ea2c0a744acc12a0fa90eb06249e82680205e4db92b08e2d2ece25f6fa49d5be31b780867860c0c3cfaff76a8118a167053c1343a218e981760f90c69301d07a70dd3b5407a91040359003086903c43ecc9cc8005802206306b82b49da5c724605dae7a572e2db6fa63a3515c910b81015c4f295d82e84db335e190040b8dcee04d6ba3cc97cdf3e1021320cd99f8158116c03c593f69caea5ecea96c9eb5e07862c3c0b0a535b282788cfd4945a2ae7b1e4ef5db7210d5b42e3653d92b80e18f375849bff5c18855e0da1437c89e0b6623271f3b0026abca69fd44f606d9b07a38fe6b1c3553745062939c4eb7d1f314eb3012f31832801fd74b4437635d5504cf515a98a7a2e47792c19e511dda055a51227097872172d09dbc96ebb02348376922b433d56a4af9772e1b6898a2525372f9f0e69054bc45c3ad1ea97fa3d5f70a013f687280ede6a4b24979f954efd14695ac661642f30a7b841334365f3f4cc95e74d3701de1b71084f60d40e84b9b41931e0a048ce8c742d0c2f866e93ce3dd079219f251e74d4c36d9abb4df5ec7086c0b19e92efaf1da85d777cca341b0726d43825d1a57d8b8b3e611bffc8416075b2691e7f0123d7668795c4b5eae2578f562970637afcb8d2df4372e5edc646d9da6447b39a6187897ecc4f4dea88f93472040c443165286281df7b729579977da34c8a4a8ee25b9c7e7b93e17f6684bff827e48b8b3192fefa8edc6a276ab44d11f0a3c76a6021989eb788bec353c7bb0a2513370849c825d7b0c4f58855ece67e308c5a194bdbb9a5fdff961350c4ad163af8dba19f54079de9b08274513969251d2ac63f10eafad0955c5946961d95ad84a1aae0fe9a880c5472b5742c69406cd586ea500bd530017c1b40231523801d69a773518cdf0759d03a85876d22219e49426e48065736c081b086da99ee171ad2da88bad8b141215eb9958af43a91bba6d0690077ef1a49ceeef7942954a1f9f8c2fa15aa2030d463a8b940b3394f0ec413e86ee6d5dff7b688d492e05a996c8bc5e99823b81f9cd19f3531273dbdce2ae5067b3d3a15ce618001ae80f71dea281320268a3a941457c2f4c4d1b8f69b11ebf27b6b0492dafeb83a8770afb038874a9b54ed2d205ed8bee5d893896dfb9814aa713731fa3bf6270126108de6fe9c28f46a30a69b881d7d10cc0336ad1a72495c89c7cb13bb50b7fd229b60921e0dbcd5ac0b8e955fefead53ee03b12ab3a4b0db9a477296c753ee200d2417797d29a7389343fc4a7f306ee11793cb080d4c925aa7e695f17be035c45289a572daaaa42480000d732eb800346362af97d2c5d772497ef5563ee9987f9405ac80605ba82ffdb7189d1abc4ad5787eb40d55bcc2511c9daea03453b0aa150e47365cfa1b02f61ad6fd03a9faf4c089bc0360e08a0e15bc29d18e298552edba11352b4810c30f67344350c0e27aff690064ec5590d5782fcdda9b17cbc6b716eaeb0710ec04d3a52b87ccbe40e006acdf6e5a630d66f7d4016ccbd8e1f1cc602210333fc45908a37856fe6f77e6d839874988c93d4800fd5ecd8836306637e0fd4a0c3703dce2d747618cbde702c9b67b0d48d397bbee38717fb4cc08350e2317106ec3d5899442e4555026e31c49a0a3040f26439ffffffffffffffffff68ddd3d6eced97494a0107a00494c34d29a594528a54ebdee4ce7c86fd0cbb98cf5c85220a6b0a840a8ec614aec1468e363ae25098902fc64ee65822d20e3854b6151263c2a95aed49b6bbb43469e2e978433985f966f32444103aef7043f9ef3289929f3f1f74b4a19063b0f38cfb39c682d0c18642b44b35255dd735944feedf7e08b7430dc5ff91316bf6c78f49c51f74a4a1187bd63ff5c734a3b7103ad0506270a0e30c253125e4e36dcc2544af3bcc50909e5a4aec8394b352a30c3aca50bebdcc21851295f163c8509239dacdea32ffe93d86c295c91046e994aa698aa118ba3e36c61cc404fd75b5d01186429435dd1da2868c2dd3460e4a07184aa57c5683921e3b843847c7174abda5937fe63105ed05ed3578a6698977a1dc72a5aca4ec678d36d25081c9a1830bc5edb0109751d479e8d842396af893fbf2af3b3a2d9474f29dcf10fd1c3ab2504c2173d431314add8646197e461ac70a0ba5cc5c265574a8c8efafc000d17105a4ffa6a97b8bd661855244cd89216f8520e8a842b14bb375279f9a9d34154ab11ac145783c8572b610e937885fa76aa55050be5a969b614714ca1663fa7faa5aee35502809617ad4b65ebfca2794ad747ed6908b4387134a392ab3e4d0a33be49b5096fb1acd907d261453d6ac88c7897c553b9650f624b664929e3b94505af56d97905eb3ea2809e5644aa65b9173b7294542314a0ecf96eea7b573472899cb66d37d9af396c1a0c308c5cca263fcd7cf31c85884924819d4526e420aa97510a1183e889010f53dc41802816158d6e2915acee65b5496951002c228664f1bb3348e4bfe8351d0a36406cfa571ea074629364bec5af38be2686797a55cc9d5be28c69c1a654246b2cebd28c62fd91b722ed598e545495706f5e91ac36976514c7f7baefe7153a6ba2807d1ab9539cc4531472bb5481b2e4aa26f2447b7165d57ba456994cd4e2ebd4c62255b143fabc389df1bd393548bb259a78b9dacae2392685108b9cb46e424cda2985793781579d9509245494b4822123d4c73522ccae14b820cda437c33c1a29c43870e6293d445ff8a52ff781e8dbf2bca3123674f93df4ff7ad286cdc6913fa643345ac28e4fd0f317a3fc70aada2a0b6b466888899095245713347cb2ff166a5938ad2c79032530491d51d5414de32e78d161af4daa728c59d560f49dfaf98a6286ddda89b91396784a52857793e2136aa228ea428e8dcbc0f9d789ac151944cf47c5367b748de8ba22436ecbfe82d14e5d27ba7746e5bf1d483a218b3e4147fee9234e43f5134395e3a889c8469e43d51ce342d39226fbcff3b518ee18426a1399c28c8b8eb52b5d1a3f64d1474744d3699d8bc194d94c3c374da70268abeeb9ac22cef56c64429432edd2c6d3ba54b94b6473f09bd6ecdd71225cfb3ae08afe94f9528beaef5b5afecfd4a89d2d866473b731265efe4a3a54d499472eb3cfd885c9f3012c5f3d22aad7cd39e834431527eda9c31dd9d087a441f26c848724a8e3062ca53230a4148751f8949a8f31951aab198bc3b9a45072da2741974fbb4e4a02ba488b27e18d7ed9335cf494479a26cdecdb62cdf20a2205e363b825fe5c81ca290636cc7fa244c89b0218ac1c342d9bb461db710252f2d93c38733d32921cadd3362cace4114644cdee7d964f53c41143fcbb3e8d0bb480a44e9c3c691a94ab6ff80280815bd273f836ee70fa50fb1b5ff3698a7cd0f85a0b55dff3f8910ea43612d536efe1a1d3f7c287cfbf5e8df9424ab3d1433940ca925470f852892e45e270fc5b8f58e61466435090fc538325769758ebcd11dca1b42dafe14214bde0e05a16f22e4dbf81de3752895663febd74c0bd1a1e8a33b954effd1d63914c469efd8f9d6d468e450caf1384ac910fb6fc7a17cfae6b4a73a4dea854339c9b49e5a557f43c926071d1e71754329638ae93da1944dd2db507a0d416a529963f6cfd950d2f9e123f3769ada7c0dc57513f22bc33c85cad55012724f5cec0419a2e569280619b663bccbf960391a4a42ad3fc62c32bd267e869226dbdd1c43b6e9c90cc59f53e9df57695b2a43215988ae293d190a765e9dca2fdf3b3f8692104dd2845c75ecbc180a327a630c51257977c250cc63a249d2959eee6028a88ed21395971e355f2824d5133944cacca7f6424967c9ede5eb9c5077a1e01a719208ed124ecc85c207539e4972caff780be5b0f5ac9f25d5e9ab8562e5c6e81a5b3567f793a7f35d64ad5828bba8bcd91d9d33d32b14e2c40bf5971b6244ad50b8ce263be909a7ffab42d93be9b83f5bcdda51a1b81ff446e579a868660a25a574d22ab78c140a7224a6afdef4592c1385c2ed874df341c3a4080a8538c14275e43ca1304269849139b683dc09a5fe984c88903c41f72614743e51a1a13b9e76261473cea52175b3847227cd71547ce874b6128aa5f1c4e496104ed249287c5f973875915030b5924388a66a847a8492eeb93f0d427c9025048c50ae944f6db31e378c1028424963ea07e13242930e024428eb7a9de6133e1bb61e4641b4e5071d542d8c826cf7bfca6fc74a3a18c50d754ac66e9bbc0f8c82683cff0ff52f8a1b43740c4a72d4185f14c46cffb675d0089e5e94356bd67e0f7951486a5fdeafefa2f06a23364f2c99b3d18596e952c69496735112274992c4f619ed322e0a9fbe448a49f916a5540be9631277a2c9b628984d909d437f5222c9b5280669daa6f7bc3f089916858d9925b3e32951139e4539282d1bda93888b902cca97273a3d8ccc86a05814ebba9476f99c2b826051fc2a95c9a5834ce9794549b76ddad09dadade38ac26a455a8f1d74ffde8a82975b6d6dd69fbcb3a26c726dad2c6d3ce8ab2884f0ee37e14ae868545170dd1f5995d95414cf4bee6d5667897a5151104d5e1e6f94eeb97b8a72dc7e31b139040dd93545d973e26651dea699dc521453c5c914d95c521454aedcbe271174c61d45a96398bc1d39a1425c51142dae94d6b34addd486a224ef3f71b4fea7bb161425911bb4f7e87c9cd0278a2e26fa749e06553a9e284b12d2df44c37dfa74a2bc1e92c4242386248d72a27c9e5644f82053a9573751ca78ba66bf3d4aaeaa8992953c6de239a685a999284fd05e16a362a2ecff2f7aa2e78ff9258a6922a4fa38f968254b14ee75346c0e19edff4a942376b43ecf1e5f3b250a5b2164e8b127519e1c9b240949a218e46a5cfd90c3e670240a4ad5a9c8935ee68144b93609691e57d9e94714c2e5defb9b8e287775c70df935a21c747cbc5f2f1951c8db1b4fc9f6a064968b289650ed6f114b459452b34c2b72fa52aa4c44e9f37ab7a814fdda23228a333327bc54cfe81c3c44e1d6fa5f53e38d7ad0100599748998ddb93ce460218a15bacaba640eca5b244431b8490f51bfce398a83289a8a4832c78782285c7e98dcc40e371906a258ea35b5a6fa0d9f21204ae284675e174f1613fc43e1459b926ded414608faa11076c36587fc1cc2a90f65ad50e222339bebcf8792e6add166be1f25bf87b2e5c49f38b77722d243419bc71c26c4dc9c4279288451edf8fa59dd3f7828c7cc25a3b9e90e458b305a4470ffc8713b14ed7e339ae4dbcdbd0e85382266984c7bf31b3a947653ce3b26cd19f51c8a79f5232e9743316445be516294693a0e45978f1a3bd2a49c150ee5d2a963ca37c6ffdf50cc596f2547c88734cd08b58ef788881b645f2885652cff51db6e78a1206312399c9cac31ee7376a30be78edf525ae5a35d03c20d2e9444bd77d0a63ce41c3b32b6d08d2d74b9a674762ca99ab171430b8810cfab27d3fc290bc49831710f2f319fa4bffa347a030be54ca3eaf3dd154a42c4bcc8ad9a15ca5a1faa1183da6e54a118bb753f414c6e50a1a0f2db1b42e4d48be4c6148aa5496d124ac7e6e07a8e3adc904251c495cc5144c275d26a196e44a15c42a4d451b6de1a8238861b50286592fde9db4f87db9e50cea0646ceb56f373957ce186134adaffa24d856a7dfadc6842418746126966936e30a174d5a321df664fcff124c38d259443b756ad7eee8612ca39caaf7d904105861b4928c8d87bed21f462b88184e28790b09b3d7df2fc8d23946377e4911841b4c67a037682117408e28611ca9a51dfdfb3b8c9b06e14a1149ecc35948a521e4b48871b4428eee78c7952bfe6321c438daacb1181619464c490f93f88ad68100161946c924d1049080f5e3a608800fa64e76c3da5616014b329ed75ed73ea6ce38508fca2789fe5b661a6436c17015f945d5e55ce3a4424c723d08b729b70d35f1d23c08bb29c485254966997392d02bb28cfcc4a947cb2a93f480d11d045b1fe47dc446e79889f5bc3043388402e1c397ae5e2df13015cdce29fb16fa44188c02d18dbde9c5d7266feda88802d0a398b7cf52a99a284522dca23eb35b3356ec2e7694192d3289b3ab7c46751b40e5b9fdae2535976cc88802ccad9343c9dccc5a2782f233a849c8c002c8af7516c3d651c1b8d9c118157d835f7f2e961716e123cde766ab65dbf48045c5190bc9ac4874f6a45313d738f3e0fa9a74aac2855083d253b4f0e39b38a529777cc3d5913c57e55e063b15957b2a928d78b300de9b64b464521868c938966652a3d45310865a33a53e893a3290a263eb54f1231a2c84a51522e31a479d4ee0f795214237c349d5736c73b8da2ac9f3583b80889a268b5c1aa430f45d9b2344848971d7d501425286127b35bfab34f944f878a8e67503b29e489925575f8a07ba4264f270a1fa9326950cd8a3027cab133af4f5c7d9d7513e5bceb1bfbd3e690a389b2f5dfabaa690ff665a2a022c89da821e669eb60a23caa52644c86e910bd4431775d6998d012654f6df910e49528da6d456e917133274e8972081394cebc3e8962292543efac772c5f12e5fb181a84ed9128074dcd9ccdeb3be290289598d7c60fddddcc230a6aa1279a8cf86f6a4714db4255ed29b99def461474d3c4c8ce25a2938c2887fef8a5cc4514d3dfdff376883175461125d350fa1e4d836f661251aa789310474f882869fb24bbb447243f1da23442b8767ca62ed31ba2b45d91743e85289968d9d97dfdfc31210a329f2d7f826e8fc14114a389bd1d6d4d22491005a94977decc7520381342b6feab064441698876de6e1272fa434156f9c651b26264dc0fe58ca3944c267242b8fb504815274ea6e94d36f95052eac38608713de7710f65b7132d6ea5db3d643d143bcd043f551eca214e6e9d84f0df12c643c94de67ca414d99b3b944b86cea1ca3fe9903eaf244f1d4a673a4810f725eed34b8782e62c7a738cedcebd7328cf5a8527edae1cca9b71528b87f8ae228d43c1c243c78f7982ed46e1507c312124a6fa0d25312187d3cd1a7c5f3714e3c4d48fc9e531576d43418bea093f496a4655d9501a95b549c5c850b25f4341a793a0536e926ebb1a8a397ca4eef43f22cd692848c81fb63d086b75d150ec506a74b75567a467287a38954126e76042966628e629d9a3fe3fe9916519ca1372d7aceecd88243214735093444cb2fd161a43b94c4eaed690139e8ba1b82544fa2419d4ba06c350f6537fbe9fb7dd2f0443415675ebd7499e24ff426944c6dff18d170abbd93fafe613b9315d28cc7aa83df972a12454fcc907cf73b36ea1284a44d0f1a5168a7ffd917aafe46c2c0be5ec1054798e1c168a490813b2637a85a25f6a966e8dd1266985824892948acc5bbd477c5f0db22532a34239d4e790fd42080f4253288bd56e881b7672f848a11082d891d3c9e7f5289427c9187f93eedf8843a1ec5a6e3286ebfcb97e423974909326896821612714c434b54663dc9b503c3fa579993af9499950d2d13759949aec49b984c2a40f1e27e69c44664a287f765a3f534a4212a92494bc4fd72ff6fadc8484424c93cbbba3e7c9e808e5382ad3e8f18d103f46287aed88ee3939d375118a49f2d585dedd106d048850124af95cc889494bc3285ce668a7ffe9f4a6c228bbcbebe64fd1486530cae99fcb237e10fb1018658b5425e3bd360715e9f845418698aa79643c0fabc6c85146ebf9c20e21b58d7dcb9bb817458e249a311f325b991927f2babd3955fae474f0a2114d32bbddec8e5d14dd3c87a90d21cefcf9840e5d14334e3695f7ab2317e88d6936efa40329e8e01774e082bcd17424657e8ac8d842ebb845a22d1e1bdfa15e2172e3c8ae830e5b14e67388e1ec74c6c9cb3930848e5a94cf3594dcadf0493a8816869cc69877db6c7b9d6d9abb933b4137f3338be26a9498e366f3111f248bf27e129dddf4b3d0118b6276d77042453279a35503160531f133c6b98e7d59c72b8ab9bba972ea4496bd3a5c510ef164fd933bb3fca615e5fd2f91e3c8fb17ad5941b6ac9814c1be3d2ebe462d84aeafd95c1b5f4571d3241753dac11a6ba8a29cc4248fb964bd88a05ea1231585f3db58cb9be0fd2f2a4abb1927f1cc3a7beda7287e30e5a9426a8af2c556a528cdd56d12bfe65f1a2245c14fc9a844eb46519d96a68779d6243bed124296a43e89a23c22c8f591bf2314e52d2fd5c1c30e8a5227fd90476f9f285dfec73ddd1dfc64f34461a247f4bdbf34f964274a66e263f8f4f426f366a8d1c189bbea4ee36653d29a7b6a7f8610b7e5e94d79878e4d10c49d1075bab4646cf5e071a006b1269a38eaad88a0df4a9d08658297affad4b0b62c9370f61184dc858992f978f0ddd4fe67327589ff2567ffcc1a1d96c82e52a4893139b2758d6889481d42262d69117454a25c5e9bc9357d1938420725ca3926a13a9cb9a4f01831c44042c7244a1b49a6123ac393d49c240a22265a32f3d9a37644a29884c9983d4634899b3a205152c226be99dbdadf48c6561a6c8c81c69b3146d9073a1e51fee4353ae6cf92e3d50e471425a9f7c7c8fb2849ec6844b93e6af977690a397934664479ed5a2b248c32a0822b3678f0b7818e45eca12639d56bbb458612932b37e215ca418722ca95125deeded39108a2aa8433130d8dd51aafd638923409a5317623a26ced499b52d3f09b443b7834c658238d31b8ae4ad07188d265c4fbcd9c4e79a686505398db281502dba83f62ce4a673b11a21c1274c7ede4e918c432496bb44ac6abf3c4c40d79f3a8a8a4431025912468d2bf0dd0306b8c61d4c831468eb701fa319279721d2bf0001bc60665e8c0a0c1aee80844f135b8e95ed1b14d66d450a30d4014c39af49ceb438a30331a6da4e163acf13b48c38c8e0d74fca19823a99724b2c90fa5d8ebce16dacc44471f0a314569f9505c4fef3946e8d41ff18e3d94f3c92ca6d3df9c909fefd04349ceb55ae59bd7d5df918742720fddb959b6466936e8c0431a1e51ebe64e443fa752dd0f2266e81d0af9c3c793e33966e246a5c30ea5f5a063af696bcda61d75387c27bd928d339275d0a13041860966d2c3935076cca174aa3225c8601dd388a1d02187e27f4c4a8d0c061e6220febff7cc664979e229788481c190c3e30b2af0f0824717dc13b149d926f32f079e223cb850543b530de77983fa881e5bf0d082d2c0230b6c786041cb2a53396bad8c8bd5b83cf14c22798cda35f0b84259d3cfe79f88b34ed50a851c6d84491f8baddb3caa50ce1141d3f40451a12c693ec49b182d35bd29146692f58ac849fe7d8e62c1430ac5cf692d841a8d47148a7e25acee268756f0804241ed7734488c1133a9c713ca5ebe2554c37959f07042e9b74bc96c59b2b51e6c42b145c6ce301d31aa3a0f26944644fee89e4bdcc62ca118ef45dc9a899450d2d89a3b7f699250bc8d94debbe9de348d84927c92e95f8277778a3d8e50126b53726635424908a5494dc6e85b393d8a50384d9dd3ae6a3ce04184831edf8eab700c03f131e7d2b954cebd2dc0210c15e008463a740826435685c6c06803c72fd217ba5c248bb7129bdbb61725a5730c727c933878514ce741fac577765108396a7fd9aaba284d180d7eba9994569b8bb2a9f967cc49cfa3dec7818b8238fd99c6fbba4549e3a9efce16e54d4a879e9cb1643aa15a943e6dd86c9ab373020e5a14ef730cd3fcd4e02bba9b45c9b4a42bf1349e2ccaed1263be451c993f6f093890b9b3c553a7f22e71c0a2904cd6e9567f0cafb79180e315252154c9a025f77d89da15e5d3ee210711a2e7a0dc56144fad936b44119fe75b1170b0a21c23627a6c844d323f52c0b18a82694b92eda3220825c221e05045b1c4a9667d1b254ac871a4a2b8f1d9438ac42c0e5494a486d1d9c3341ca728c693a7c74efa8b876ca628b56651a396bd1962689421022b4539b53ddd64e89b1cd4468a826ed00e22ef8e378a92b65c9a85a67ecc9e2c0e5114e26ae8776cc9041ca128c859bbd33d39384051b40edae7afd24f947f833ecdeea9b141e5896210694d4fccba13a5d0b2595e7af2468c878313690a1193129713fa0a706ca2f29c21284f9ab4d5d1811474f01d7068a294d157fc7b7f3c4f08195b6288713ae036aaac802313870313382e819628c6a89fd7c354899246cd3f2f75ea92744a94435e5dd1d8f1b6f33d8962be8ed0f85e133a842581d01dcd09b2d94894bc24d9bc6b87e8101285ac59d2e2a467da943ea224334bb0d17e19ea7d1c8e28fcc6f7583f214fbbd888e5347e7466949c630472563536735693229bd7a70f2f6993887c882da294b1c4272594aed586c0a18862a80832abb9c95ffb89288d7d7e493561e384d21d010722caf6aa9a515682eecf87288be88fe1e757e33bc5618872ce7963981215a224d994dcf48ea15d1d218a97e53596a74395781005cd7709bac2df35523804519220b37b8920e2084439d28e5abfdd0051d011e52641ad697811ff509ce0a9374813151470f8a194311e6d4b275e7787a30fa533b7aff0529327e0e043b14444194d991c03c71e0ae163f492fd61c3a187723e1965c39418471e0a326889e11f9259c0914c12825bbea98fe30e05d9d9f43a3fa162531c7628c9cd103dc78f4989ff5c01471dcafd13354428b5b7933a35e0a043319386d012d4ef8d1e6b0a38e65012d3f21d746ed80c3729f0010e3994cfe4d3c55b8cc9fc6aa600471cf00638e0508827573c2cdd5b5d52061c6f28e87c95d09c565428251c6e289a0e12545644f89e0f8e3614929e989a44bdad4c0e071b4a918459773ef1d1e521634b71acc130df7415f96da6cc81430d79f0fafc8dafbd91b1956ce0488349c664b8968c2d1c68289c47ac7111961965b800c719d80cb623b3bf946f86f2a4d18c24c3a8f435d90047194afa9ee4345637c9242243e1a4085f1162196e1a4349d43fa5d98510713e62c04d2cdd2ecc3e7c378edd66d3e008434989bc2459ebd3ec1c188a41262144c5bc38be5062180e7078a178bd5173dee45a7aa60b251336271efbc424b972a17c2263b6971eb750901d4cd86a66add8d43a0e2d14f4c93cf518268ce93f0bc551aeafa9f3cb24a562a120b433662a5da5d64f1c572878ce990ee623e3ac638582d4b2d5ef72cf90af423999aaba7e4c3a6962a8f0a89a9698f2e0984221a91919f245676f7738a4500e5225c63e21bbb7c41185237aaa45ba332d8bd7a4627f0f7040a11c44828c7393b473869f50489741f96c4c2325c49c5074096a3478b6fdf7ab09052b15fd9e31e34126389850909e43bd4f3e2da11c224ddc3053e250423987741d722879752d8e24149212aaefe7314868547dd0133eee2314931e37f364fa3a723642216586fe20b73ad4af388a50ec1cc66af3b35a5f868308e53989fe7baaac4c88dc184631839e519abfbf7356710a37845188a974cf9a860a1531c128db96acb27d9d2fa54368bc0a6e00a3d4721e9ffd4bfd07911bbf28f5c6081ef4b68650fa0bdcf0453979d0dcd3d350a65e9425bce8ccb7ace8cf8b52e74eefc174d22e0ac23ee6a0ee41a68b424ced398da033b37e2e0a3a2d463b4352c91d17a5ca74daea47a7b4cdb945496ab69a9c7d4e9be7b6287a6eeeac63a5dad4a616c5acdc3c1bbb6951483275eaa0c9d486cd6fcca234775ae2368409a665591426c947f975bd7b7363519074a54c6bae4fd781453169ed1cb74ea8b5385f51d8205ce28d2e35d2465714731435a544e738fa6945d16cf3a585cce872b3e2186fd4487af356515c116ba3c2a48a422eb3d4efd2b671572a3a21e142dad7858a624f5ceb3aa1ffc3e814e58f70c2c4ecc414a5fb70ea8389d7cc7679e146290aea7f669d9b365f45a428cce6883326836cb66714a5b6897972892039ff2e8a82708db921323a44be43b1c70e59a9a50745d9e34679a7dafab13f51ce253bfc52d2881fd313a5f1c8f93e9c30099eeb4479be5227648927b244cc38515ef5c9a3426f44df7d13c5709d12c36d242d9d9d0ea2704313e5798faab339b991899248dbc9e9e42fc20d4c94a3969e95c4dca971e31265d1f5b36dd9b2fdd912450f26e759dadfa844692b4492d31f53c60629513ea1b1ba7bfdc13d796312c55d7bd13f26ffbfaf23dc904471efb3e8bdd2d5ee2a156e44a2a07f6caf242471ea2748e023fa99abdaf06e38a2a46183868ae65c234aa1846be81699bbc18894f69db990a6faf28d45701e6a275f21dfd35ed54e965ef986224a8c44941876831b8828c785879c0f9ba64575e310254daf29ac6f4b686acd10c51c52bcfb568da58cb810a6068ff712a2a875e7b5217a1d4449948ceb3f273c7dce53100521527b437573732e0c44c93d7ad5e6c60f1f4b7703109aaacf7fe30f2506e2e0861fcae971a55746dd87a2c9f44d6f69be39c8eb0337f8b046b8b18792045572629639e149ee6998618331d028830c36cad8811e523237c9d40d9e79f464e468438dcd43e93aede659d1731833c6683c943fe57c4fb6576d63947195a34370e30e6cd98911f1398937ecc0577858b5a6565b8d7d6585ce4fb72e6974d0e8410e5275286ea9f8b4a9a195234287e298ea3f493f1efb437328e418fe9b1ccab6b964460f1e6763c72c0e2529bad6456efb88689bc1a13437aa73c7999c413d666f28fa68896da5b953c868e68682a9cdeebcbb897964ccda50fceecde82933ba556dc686929c9cd91aca31e4ac6e4f93a9a1fc1f4f288f27374b43419e6f9c244fb3c4d46ea0a19827cee9e7cce919ca13e45544c722cc46cfcc50acda8cfde2316f67c96e94a1983e8d4ca339758ad0da0d3294c389e6fb9c105472888da17c6283e74ebf9fdd1043715427ea8c58175d8b8c1b6128e498f24f9d68a481c61839cac82276821b60288bd4036cf0e02b80860dcac8818009e4a8c09be1868d36ce58c0040490e3d768830df3698c055000001f5715a8aac019b107f4d1dafe5e8f07103170c528071c3672b4b1460102d003af800200c0468e3652408000bc0d4c1b3640c398810001e458e34f1b288d35d44800097294a186514000da0080000000a1f16ce4f81d5400030be0000072e47031082000c3864162142000860d83d258438c030060000d48401a6a8c818363d8c831c6180b0040001870802b830d1d9481da18638c044820093d5003090790c011d438e39cf16718400246b832d858838c1c668c3146022450841b630c044880086c7c8e33fe0c037c0ca31872b01ddda5f487241546f9f7439f48224c9c8f05a3b4693532564c7650b233fe8c3de3cf0046e1377b5052eb811ae78c3f43cff833f2e31757061b669091c38c3146023e7c7165b081d238a38d31c648c0472f8a9ab34f4ca23f23ce2363d9aa0dab337e8d317450061a0395715e8d1c5d6ca051c6e540a30c36cc28a30d1f638d37a38ce2453128eddb318a67a8d211dcc00946d0d181147458e26317c69f9841578cd4b02ecaa6b494a5e610c6e40ec1472e78172f2fafb5d74eabb3f2972443e726b963e2a2b4616467da399911823e6e51f6c9b32525870c348c192778944609d816c5121db36869994ff7d7a28b7955af3bc961631ed3a29833dd49f37c612af62cca1b24077dda22d88e289541468ecea1461a63900f5994efd2d74568927e2d2afd884516176b5722afeffbdde3213a58143c795755ee5a9a675f51ccf3b5516eb6d695b9a2983efe49f6edc6994ac6d618080d6386b123c2472b8af174c65bf3b9eac7c0624539c8cc6cf52082dcbe3e565188b9640c16e39b3d44398c1975830f551494f2bdd538ff4845f1c5c773f21da981e775d01fa82889ac1fe7455bdbf4e84cf0718a62a8d8dc5d6fe618363bdf820f5394bd4f899c2072646c8e5294ab675486904da4cb467a33f083146551539b4d6f928f519473d27d5635f9d5639ea2e02cb28964bae15be32314a5c89921537f5216280a3eeaaaba1e33940e6123c7186994400c31de0cf732aa3e3ed19a85bac8d85b6b6b99d79c0e4ff93879d5c1631967a481460fcc48e304ff1d60f344f1c7d55e5e44e6ac3f3a5194d87d523d74be36ffe044b1d327c4f02673305d7d6ca2ac65123b7ba51f9a28879fe7db7c42992854d54526044992236b576deed01bba57f48189c2be2899e7645077f18a21860f3e2e5138a5c397c7ddce0d131c9c34cc604be041c8646a9eddaec1861a9530d5f5694eda84849950a2a4f2f79374cb842c419328c868fa8c04d33907591225ed48717ae39829cf22619c207c247c08a15f32b63e20d1b876a7e496d85d9d564bfc9d901b46c35a8d33cec7234adb279310f921e786b0e1c31105d3ba31c77f9124845023ec0c21d3a9c91c4694d4dd640ff1219cf6f563118d4d3297d312fb3e14510c3246489b9912ff438940442292da6ac32b6d4b8ff5ffbd8bae107f1ca2fc2753c7641d43143b4bb4d53d717a1e2b4431b47ba368fd48699e0f4294a4cc060df7a9053e0651ce6c9a66af564b659020ca9bf39921e7d31a9f1f8128c765ca09f771669b06c4c71f0a623d94fae8f9d3c522632b071b3c7834728d6703046f83359e04676b3c1b65c8e0c30fc5d0216f852991333efac007bd7773b469c8d8aa3d2867f51bd207b95a42157ce8e1230f25ff1a7b91928312268207336ec4ec26898d8a786a2aa563d4901c84c8c71dca51c29a86116f3139fc6187a2ea6d6bf68cab92e0471d6ad55a512f4f5b395da6218ed894a10f3a3422a93a3dae2d6564c2f8e65e73db0dad081f7328c43c217bf698ff904321a9c412951e44d01cff8843e94aed830a19ef83287dc0c150d58de52963c918f38693a6aed0968b587babe1c30de51c944c6a1f4b7af4b90de5a4574d49562da53e3694ac4359a7d0d512a4670da5afd2412999c34612b61ad06b21b369289abed788fad9bfe93ed0f07186f2462aa14aa699ade43ecc808c89f1abbd759a3258dfaad9766676d25d92b2f598de7f90a1142a3dbbc45f7b0e570a1f632827f1e1e488a473ae4a0cc513a9c49d28dd47180aca22fc474a157d1ffd0043c1456ee88728faf84279bb7cee23e731a57d32b6d028e38c03c187178a953ff236728c813e051f5d30c9e8985482cd5d646c3d0e0c1b639861cc8c0f2e9422beaca989570b3eb650ce5affea611992ced142e136a34d921d42a39e4af09185f26a45dc1c6212195b58592cf8c042292fb2e94e7ae6ba5d64461a1fa0c3c715f8081f56b84fced429a1f05105b7bf8403b30231c4c8f1418582eed0acebda1054842ff89842414b628841eb35ed8d52e023adcc668dc997c647140aeabc376c27af091f502875fc8961840ee7aaa3271483483a6fe891f97042612bcdc36a849a50180dd3900da1fb44fec184b2e479bfcda59eb191fb5842c9d55c9350993f9450d226ae1fb1c44b927e120ab6ba39e536089d4f0809e548b761c4feb68a6c3f8e5050dbb6993ded9ad8e4c3088c66cbb04f6d7e14a1a0f523368896edd92602fdc407114aff2fbaa744b4c4581a46795bb5652cdfc4e4d9431805d93a1f53deb36cc8c128c7b4a69b7a3ee4371518854fc276ed639c93d4fda26cb9eb7daf79f8a26c13fc2509ff7b51cc1c4cf7e0456945627617e5d249e910c44d8738395d946283f639dd3517c5338d410f5c143e95a69a48ced324bf45713b8a8889253d9e981eb62806a5a324e1aa7ba34c1eb5280819227f0ca225c8b0072d4ae119723627e9dee979cca2301357cfafaf43c4d44316c530d95427fdb24194dc231665dd51b133e29d1a7758948466b72ad1f21585682392ee0d56bec1f3704539cbe6f491971b7c3766ad284c8c5632ab88a0d46f66ac280617d39dab378e8e6c15c54f9bfec48aad8ab24419db9b7f5351cebca123b5326f574545418968d9c136b7e6bd4e511e5396412c440f53381a43d2b759bdd941a5c84ecdee3c4554c37990497b6e73abc9d8da8107294a9aed45b6388aa2276bd75ad1431465917d61265a92b135865dc70a46d061c782ab367884a2a0e7ebed3555e9a022288ab92bcd3387d63e51d8d1f5214a10f144d963923b19f3dd89f2d6aaf5e69fc6e42f27ca793c7604d3e8260a5123ab281511ed9cd24499b6e1a4954a3c3251f4bcd2161af355b685e78189828e49261193d07eb23c9728c94f3192d227478e901cbd050f4b1443bafbac91ffdd6fa6c1a312c53631d9124ce4e62494286a943ba521b3314acc4da29046ee6bc367f7a49d8724d2d0736d76d946a2783d9aca4a072142a29c2e36a64e4abaa90f1e3ee25d13919eaa537c506a20031e8e2809eb09fa4c7754174f1a8f469494e40f153ac830a2a0737f28eb95902ee6333c16b12ef05004aec0231188f038c4b22e31b33c535286289cb80f15273fa8896b218a7b327cd2e499e447888cad5f6351e04188d2a4fdd5fcbca32e52973c0651ea2fd1e7deba3a121744f93c42f8d872200aea46cae8ba1c1d3d0344b964ccf91f4aba1a6d3e78f8a1d8373a8fad6cb995f43cfa50ce392893d9ea1ec284cdf850c84168b68a51923699720fe5ec22742e6d3e79d3f5d043a55deb33db4ffae41182471e8ad6ddfb396709e5ad93b115030f3cf0dd216dc9a665261662d9a1782f7a5bc3485b931c8f3a9493d050b2e4860ea5ed1c244e4c2268f46c0ea748ca1ce1d400c8c1430ec591ff6cdb2e71061e7128897bb816d712f14c8be8018762ba13e1b3651ebf51f4784331f665a4bee98a921c0f3794e4892472e977108fe0d186724cb2ce50b5fe1a71f4604359f44cba58a4d750feccd94fdb720f35144ef6ec94568f471aca9b4b359b2ccd9c51f64043b16eec3aa8c65133efa1c719ca1e73278f6fcfa896d1c30ca590dbfd575122083cca50bed8b89517094a5c460ed68007194a7274c964e9e135d4308f311494b687a89e27e524a9218b50a824ae0622692c128845e28030180ec469a78701f31308000038228c8682c168a06992f201140003492218362a2822241c10141c1e1a22120b83c17028100a85c1803020140c0402e1b04defac0f40a9a01f865815f2df8f9132ec9a4e7c45842a9d367cafaeb3c90bdb573869bec39f0e0271ac5e95a4becd9a1767d3c1fcffd1fcfb368002da7d0154f37368894c17e1e0d38571a8785c2883e7fa066ccd2f13f20f5f18fc82babf04bb9ffe573d58f69e3526f7b118ac8f26f3c751aa5d099966a32c3258d7ad2fc8da566035aa7f981f28f46542d8ef62c910b509356ec5ac4a81dcd64d5f19b43fa2a8929c944e31e7ee4b771ba6dc12c673c7883022a94ca02fa034dce32e76d84dedc34ffc92b4fa439c81a62f830cce980a02ea56697b7bdc335288fbdc823b395c3042d5c31ff105115dbb096a4594c46dd298ad2177184c4839d0f5afa5f83681f7f581e83cd917da019c01adf5208384321c164aa32a6f4ab7e801fe667eacb6f6a6b4a02b565079055ce46a99d074e168bca04456c384d19aef20a5ccf570eb03e756132c37b7a725d6660c51299495448a3bfa112743def697da199f44dfa11911e230d52b5fc7367d58a4a96140f711cf2d9bf61f05153528ed3d253b50da219e91881ad6aec3e2bb936529227d1c6d580ddb0c0f1c284889f1d8364a77f6143993d5f5e0659d533a90c8e9cb5d8d9a0543d8669a145966ff4a2c584fa1e1d648e8da520d8e7dd76d9c8d1a89000e0db7f5c8418441bf941ace3ebacb88e8e825e3b0c4740b19b3a137ccc9e08dd4e7222fad61fd3fec18524c44d00500883fd1ba044a982be7ee4734e3693761e6ae8b515cba146fd87225bdead0c2bb54f9ec256478ae2973eeab34a660df0feb2ec0614d57911c1ad4cd125b127f40f27d7f5b6ca5ac540d886b227050e64bc50a006dce22d8dee6f64c50333f33da7068499c12fbe0ac5d76d339689f50dde3c674ebcab985e90304b2d194a0c682f9140cfbca6a0bfa5432f96cc7fb66cd7bfaa84b1ba13d086f20e7bc8416bd01c342b3a4b184cfb93ff85a79b8135640d3c1a5907e259836b63434d21d36115c33e6e10df209dd1fe30520f664b65d0c0918e01370e19da3a72add89653b28663860ca2ce7cbe72af59e460ba1d1a244120f5e977290cd8522a98a2a401b170a12bfeb0de0bbea128a53cc03da8c191569ccfd82562b8c2a6a52fff20c7b0aa604a4f9f195f17a5d3fad581e0cafcfef43bdebb0ae6792325cf9c46c6820cad8ec9a4bda07a94c5e9c6637a2edd891543cede49d9de3a75c6306d8e57176d4d4c346d964524865f33069c22375e1735709030f4a80362cd5b2849b29cea9d3a9730b9167795cf17da83271358618cdfca307829d3a321ce42119b3aa2be752fefdae278f180778ca66349751aef60382fb714737fe2fba9884c945876f675e516d7a157cee9093e8213c057495e16fd49582a373de85f01af3c7ae779f96d94a9e8c4886ac2ed8efeac17f43a5497449b4d790b806a18241e9143ec1fa5f232bd0dc4da423eb821a9849a66691a5a0115a2b90019b57d11bf49036ff9935cdf9e7ecadd9613296ace0f43b077f3240cc010aace39ceb4a2c97e8c84111563b6e79e32ae98415e76a5d37178e673cb0b75633ac33b0061379803004b104eb55c8c5a7ef2de8324a9070dcfe73b5b21544a6c51e11e2c46f115b92e31063ddc2ac9ac79f21ae64aac664929e0e6449423d6da371fdd29960a221b63185fadb7c662965eef9ed0c560844006dffc9866b29a4c4b65240974e0e8181aa1e1e1090ef576e2a7d92c62e6958ab308d9b158245f1b27d03f17b68fbba16bf3f5c6df79387ce4c8dfc7157e6e0ae93450e15b813f78b09bbf947fde627120f1b200363536dd96d402e32323c8dfd1c09bd9ecff99b4699f7996e40063d47956fcce4c95a0494ee22b1f6a5894165eae66052d7bed3caabf2c879bddec2298b4f4dfdf8efd8949a7047450cbf651cfe5e6e051c5dbd1d00c3b1699a48e0bd9cd80f1d000a172a8737412bcb2a4acadf2700b2f3df64479b3dbd333e4916600649fcb91c477720968733047c39f1a273aedc71599014bad6296bb50754bc895a1a459d942924664a99291f7c24ddf83fd2ee93adb54412bc06aa2216d011e6776ef52ce5d49540cfc736410600893ecba510979a74c32a5c01f62a1e0ca5785eb78947f5561f87d964892a64966b9f8d2151bfb1be19a11826fef43d196d8781207631bf9ef7ba6d5ac011f16346e47ef770ee1eee002c477c9f79caa4aad7ee225e0756c7f3d0eb05ca56bf86232ee6cb261eaaeb853b1501ef4199f5ca12a3e5be5d0b3937e740a6e440b3d8eeae9877c41a5fadb672556b5a8ffb85ca9c4dad2667a23274ed0754f59b3fe93fd200e71cf9062e191e9d6544944ecd5762f932376ab350edae1d767e7bc4ac43bc10f20bf751f85ebcd03a471dde9b47a7acf611411a9cbe29a9e739af560656e0cc81c0d9b575509253044eeb39a0b78e7ed75d91b813f12721961cd5fa910c57a8645d40f505eb8ee328caafc0822f3b17812d290de611549beab9408481f2176221a4a7485653107fbe5964f97b37c49799946c393370558ff1511678fa3d545ee604b4102a2ff9d2fa7d67a241bba2f50168e19d5ae33435b4c2489c71fa66f9fb4fe7f131eec05591fdedce8c4f0a84717ac6506babed27c915ccb5cfa331e7f5605deadf2372aa60f0fd5e9e7be2d93c035f83f4d636e15820397009f36cf7faa42dd62afa48c0d57e8fbde5ac20429fc1717dec1a3242ed76d495c22f643526f6c387e325dc59e8b8acc478a068b8a315a756d98bd371135921ddd870c29d44c53289a5952cf4943f97191c047c0d891690da29501ec4aded5879c32841191388a4544185cd6c87453de28152709a0cbd659ae17e2802519ca35efeb0b08addbc3a1dcd8c97f96de1b4b1a1433477704630a3b264fb177af952f43ebad4ece29339cdde9f316fd21d736f3f2e13c017cd23948bac849d87100f62617364b75486c0f259450ead543dc3f4bfc2f2480c1e1950e73b889597c21d52a4ddf0e2923841785762cec61d20423ba476812e2c3d0eac00c51250d0c1e81b11d010c4c8a07d7532459900f602b0a24b0e74ad91c5beeb94c3ac3f98b79b818d8689f9dc735963fa1b2efa5e1b2bd327420c55b589c1f6c440d9b5bf8850214211b3966286c6b67721a77e02b220b43c6c0b65b926a2b41ddf53bca345559f926a751bf03895cbafcc1e23cc7d3b59634ee35ef5e0e30e09c1002f92444eb7dbd202ae6425cd3e67fc51118e54a2f7072008a162983a1dd804085ab6870b626d121a81553003708e6db86146a1cf7d4d3b7719e733a1d71d2ae82f15b32fede9a96f6f85d887e7362afaedd4fb2cd74283bbf4cb9b80624717d146f69de93cbd02dcaa56448b516f04455e079e04ba24d0358a0d1747e25d0cf8c5f57e2efa98e2f1be52af0e7dbc48b813f082b1140fa131faf0d0c34cc89026f504cdd619c4946cbbabc400983d73aff9f35ca296423df2d34f415bc7e6365abb8519f1f6c6c9f7b018eb2612abd827c96d1c14e27ef0e0422b6bf3823b66f68c10139a7692366522a999f9ecee27cec29be7a0358fbf6671dd9498cbf6c50f7a00b696447c51293f8012d24cfcb5b49937ff9a61f169c837b68848404b47575957bdf3c12373ce7035eaf15817490b2b23fbe8bfd6f865a9d70adf9372a3bcb3205a311f1868a974144059d936d374cb14d2b2024bc1fbe88cf48cf023d0def31c6abf80614d601b635c379d9f8739700e1e8b2796ce2e023f790cfca2ff0c742cae0f24a47d31782cb229fb98fd0a3e4106ec9d0f711c8f8aea7226c259792e8201da1aede1e909b63ee7fda0a4a71f0b36556d3a35431893bb48f3e3dee81db5256133d6d205eac2a799d94c6a4763fb7434512d899cfb479c2e318b947ee81fade83481fc504c0058c9d2593d1362f28ffe0d27705272236316735bc3f930b4d9a99b5e23ddd52c61f6917ed347ecb59b1910b39f4cbc22797c12a3fb572314dca1670d8d701d653ca4278d8a27e59bc2aff298882aeced3c9508f402f7a9ee9b77b3c0eb9ba2a321679234453429a567f6a1b7a0de0dd0487712a53f1b499a756a6849a5c1d7fd8c2c2e8e1888e0ddfaf2a2b6cf8d2fb856db04eb4e4acc40e4d87aeebf8e08ddbf2445c69d79b04ae2d4c0eb25ec1ddb8c9fe90616aaace858acaf8686adadbc81e0f3ac2314e4bbf5fdbb97d0da4d8b4eda5f951dd553bad64a19f754004c49f8a074b0f90d9d9a3451d11c4cfbfa5b0084edda65aaeae2cb5010904ab0f46182642a6c421b558f6ddc63c6e902cee0caf72bf2a503e286311e1cdc8e21ee2e685ad8fab9185857dad90e3ed9465db49b3bc5689a0dbe0bf801be58e0f3e8f7299e1572499f1a06ee2f3384851e9e6ec999dbab705882a1883e18fe05c56b992a6d8a220fa7144b4cd620a77cc03f201a28fac0cb4c8fd5790e9921d32bc4c3e1cf662965d81bd0d47e5852c9d27452a208057979a28dd358b9f8c6c2c64aa5f196608782286086fc638e5c1a255085bff3b971b93ce87f3a3e9ec85c2f6efe1e1659b20f8c32bd8e53a94cb1e97ea6a6484e7117411b5465cceaba0033d1bde642216db596e89889be57c2b1cefa6aa74f23ba04c2dc2c1683806bb6b8c140a52151cfdb98c965b2808cbacba3d9f9248b684ea4740bd9ec24792527998d31c61f84072dd846f3be0e895baa3f89506d6edb19038b726059c136de7bbaa11808e8589962131aee81674b47a6e309d396c0aa7a4256896c76e14947e5cdbd81473fa5a907d2a49252fdb210193285b4e9a17b7c4565aec10a2c3b0a143763eb8a6cdec235448fd9ddcaa716ac794dac9ac540512768553d9514fd3b3c24a53579014166b7bf265bb7e13f79453b71f4339ecd48e8685b9146ca24a21380e7655102dcb8147f60b483ce8914354964df71213fb4495269a432ec2f73242de2f7f245335814d23e8012b568700d46728d000161a8d6c80a3670d18ae3d7c2a98124e4628b1a36346a228fa7c9dd43404a2ed15c4038902649229b2e5f1772ce96a3cf141c153cd184043d7e7fcd4d17133e67af4c631841699e1d0dff123252d67f6586b386b307cf842a0e125a00f9063a9abe0e9d201440974fc4cfcf12829e444f2027b1c5735de713cb8af5696b40949b5e265319496f61039abc62411d0b5112d0946bc4e324ffe423739a969f3a83808da538f4ffd05d8c37d747b104d80dcd508bff88575db324bb96752da9c13961710abaef7ae5e29e76cab698b358d61e2fd995886984d40208a2ad3d69553eae2e9aab1989a32751b9d4ec645d89407d801b41aa1148054684e89264775564b996071964870a52c4ab2d8d64158ebd3fc948b0770d2b3e3c1d24051e39d9a8ce88df9dad46b5228a3eb1a5d4d16b2bcda1e9daab28797e12280306f8c8544f961651dd8e80260324df1fa68a6862af3fed9014538d3e28e5e1f039285899f396143076a46112e6cb0b89028638efb8e3a3c9062570c355f56e35dc6f2d4e98468b067964fc9a11e182b1796da16a41287a70ee27c1ba54002e4c0a792861b1ddbdaf61ce40d8c340512ff4a642c29e157c03077d8ddc8da54c46a28ce44a923732c44b32377ab3b1123de07c0638840871c7cdac663521f43d420484c8d14f7c972985271bc59712f17c2fbd401e242ee0c7095021328365c707e8ecffc630843d8160d71b934e6503acdd4cd08ced11b5dcf2d08137d1c0113d36b3636aceb8ffcde8d1f44d519691385f33b282750771da65788ce759d31ca158d85920f1e33af5232b90ede49caef50bca28430ee310a46ea81a6d58cba45c62a10cc8cca181ded9093db489f5e0f8d626c74a10b499b79092f58a2be97d76de018e1d961d64ebd2348dd396762c3f99c7a9cfa76420f71b79ee8a1e4dfbe7558bb86180593c525df26213b739a919d99d19266eb2fc6a4fe4a88a1d8dabe19b82fd6c2043d493db5d911fdb6ee06305a1c8141bc601a04258ae498477d130550b3510dbfcdb1408a52a9bc9876a25cd47bb4a50c0338cb91d9d5496fbe23b5dba137d21373586024ec55e6089bb0d1e7c772fb564f25d65cfb59b6d1761aeb47e67057890ae5838a2d82b4e80c20cee515065e0a5c0ff014a9e569772e68f58899a114420971967964382aeba7c58d7ecf33e4d8d894b3d326d3718284381cf29e0ff6b5e7370ea3e1e5c02584cffd6098a427c1bc406f4b87436845017cabbdb4d6038b89fc1984fc0cd95d98afeb46c817471b62cdb7857890c2968e34de6f377bac138fbae7adafd434d67dc792bc0f15914f12e4c3b2bd98569b486094a07a51a453ac8e0bbd2556cfc707a57c5f9eac500e16e1f89343e2bd3ce98042df87feea31fe3053b4307b4478d6dd30074366af05fa1e1212256bbb049e5c72115f9b7a8d5d5b052b4cddf5aa02fd6f8b7eac483452ad0b717c3d2ae683b313421ba5c767fafd53570a325c4020141f81065ef61268dda35e727a3a284506e15ad958270cb42cc4e326f61301a2a82b5696462e62169d58296a4c088bf1465a48a268201e791a88082dc75fd1f10bd4905eb0a8d77127bdd88070d6c69ca8df2470d7e04d2e37a047b4345a93139efd0f72d66c64d5f7985d90a62bd406ff778b0d1d6a330011dfeb55a14dc0aa2a51205638a7c8599e913ef4afc6986c40923201a5def91295c94985e3eb77d64311c97318a84483118ce2090a9dac25843c08529965632e0302634ab7906e46d2db4002197154c95c7d97946048fa887b7ef2ae3e14e9dec7a2552a57ad95a3fa8d773b192262e6136a3df5aa3bf8fb2b42a9d815fbd42a47e6b443a88d7512ad984a7975000533f7efca0dea7316afdc9c9ecd74183fb3a1966c5c478dc4907bd441790fbebea82159013780508cf5d523972882903dd7a893cb8e77492c64c89d9e7858637384b3c8c2dba298a77441c13b0d1d73f46fb72f32f3adf1766782c9c424fa990f93129642e4fc9ff25e04185459bfa12f0197d346bd079f3516d5ccfa37df9523edbc350c0b93835cc24275b7d8d704e244594bd3ce6d2af58211c4e0f25aeb7a669af938e7cbce395a8868d771fe91d41e1d144123d0f149511582e141c10501bb7a694d95b81c655338934f1a5febca2aae60d96707334134a0f051fb06b5b2c22b23096199a4606be514cce6a1e82e4d589aa4f876d7b7ceb3b386684c9fbbbe1995973f7ab54389a087c1617a9443d144dff1bda6c2e9f6db64e9ca672523fba4745a54d88c854bc256935e5e0ece66bde507a65862fd3bf9059458268492ada6545cdf53c5e84c169784b21a01a4b2273d79036d44b13ed5a1e8a439625b3245e4be58a5919bfde7f5aeb76a012ea9fac83cb08d1bf444fee572a39127d3306630437b986d594581278e34fbc427fff2dd3069d294a58b4b86e10fbf6d6dca43c13399247ea6708b54bb48799533ccfce2ef1b46d08dce79cf127c23d06b2e5c41cc6378d2b187afc36de1bd0fa8b9022da88b732ca39a118c0609ad68ce0315f544fe633e802368a28bb7a5c31937af3669174b23a16ac092034ae052db9c4c3894f08929d9d5a4f951a2d3c042b6dd11f550e5e726fb806d7a61e99528c158a58e32d834a181def489bc37f00e2733d547fc08cda12051006c77b0a2627b0a55b93ddc9003eaca6296e57a3b7d74bfd2308d835637187969c87d1a22969e41ed629a114091e0dd82397c0c0f0098f1a411252e70871c2a3ba274c0df305e43b21af1e6cadc78ca0df3eb32e6cae0b1d113cc50efc3d53c65125f14e1b475918c65dd4f8e271a47c5d3c2cb452b8ae5d508cd80fcb6820b18419115c774233400f8fe410fbfdcdd7c62d16bb91264601cff45fadaeb0a49e9697b75f5c9bc2a7a3019415eda76e67d712079bfce8189f918db213de05504d2b08bb6a7e9f4643f12f4ef66201393019c79df380d3ab0eff405d3ab8ec7aa5229976b4c912f2ace265a86c8bfa4ba7093ac9bd3c5d2bd04b4a1700b0b3a19c1b07d54a8566fd4fd5ef5e18c8d808cfe93d29393bf476d1ed5400462d1f0ef34276b85b1908a1c7c7b403bccbb9a5702e8976e8f2228f235b7b14002abc1b0eeb9e8ed9e0675e87ef4b9073291af44ecc8387b86d6cc5acd2981a7640adaeffe5144c94e8ffd33669fa90675f71785074cf1c0075a155e660d0c2d83ee9296d4657906e46c805ee54551b6ab5a6985e833ef3fd82a143505311125de4a885ae64926fdea61cd99d4b242ae26ba063e6a71cca9da6d2d95189e3bf885fe01a4ce13c19949f91786c0138e07932c25f7213a4e847e4f02b7a13caebdb7f133b5a67618ae10a4c87114184cd198c7035affcb08ffa78bffb6c7ff9639ff5af4cf1ce0dd6485824b1d282b6aa4e11a58cb1c67a8ab5f206101ff10ff0d22ee7952e311fb6136db8e53430f2a3c2a6850988af50173cdf47d2be89e44c9d6c935e2a65f705108be889fa63ae6b093f8ecbbbe40bb70824ecd8d51144287451542a82819b5d8677e7b9b48fd43bc2e6cf3031e1894b01b3744901d4cade23321ff92dcdd33beb337922f3239fd272ba133533d22dc15fc421679db49c2f5d640fc2be813dacbc48c3f613d1ceb0534047f7fd7728ac87754debb37a75ee2dc09d53f3d7d37a99f9f22e448bea8ad7bbc096ae6e977f3d2f77bd2b33bb2ee7f23665bd09c776cf9d9178e4bc08b7d44cbf9c68ee5129fcc4ebc4fb0aa2765d1afa610a932229ced27487ecd542c58ea27310e5e9ad47ff6705117d62ea21d891cc80c5c044b62117100ac5f0044e8d2953e3047aed3eab19914ef0b831e9a7bb35529548b26b01cb4540aecccadb429a7b0efe1f429eba0c80927de754759cd0c4d67f24709339c8d37ba12d5c2b5f347f2158810a81cb7727046a16782c5c34d7db2135077552ac948130974cd417428c13e9c5c3aeca02c970c159d45817605f6cd8b13bfa644b0f0708e440c316dfb6ce9904e365dab8f0e39c5fa1e69515684d2f8b7d4017ebb22e5a46ceadcc26e75e01020d15652da53ca490106d751ea68e0879bbbd43911b9c8eefadcaea005a72f6de39ed768a651b2b69b2f74d02ba2c945defe38b5ab8d3acefb474f50bbd9da4da712622102db1518f152434720f74919e8ed000b56c34a7aa3523fb3b31070b6d8e8e91ebace4ccc639d79e8e31a89a55c8d8ec9138fc19b8813f654f91e97234c23533656c09a97e2b8df4d21ce20e61aaf051965f417da0cd709155d424eb0bd93f2d6f9eb3abc611f738a58db55a9b5bb2ef731cb564bdf7f15613c2ca913c28349e8f1fb9bb4ee6dd20d903cebd02b9b8530dd908282e7155fcb30b9f0f8aee2ba56be3ea2f51f99bf0ef5ab871c140af2cd5b3041d29d4d0c6919b348b8cfe69e1b9a0dbd23887da0752ba1482ca7e232fe79d74e05f97f12c9742dee722369091142b2dfadd49c4c31669992edebd3db8e991c330ebd23db4cdfce2b551cdca08d061d97ace56c0d2fae80eb6f27b8222717d10d1ab8495fbc66b22cde1740a7a0bbe0fd1b449ac39abbe21d226d38670c50b01bac44490c9af9cef697214dceb1110f3a14579cb62430446a53688cf0c7f0009f7ed7598bc9cf4bdff54fe98682aeb2866124b6b33a22d37ee9a82808c0c5ff9c6d5361f917c8c20e338849b2ae32fe4692f012ff319a50fc9092fbbd252ac32804a08490cbf5fa76a7bea9edc1c14f20b12c57cce0a641d8dcc50670ff87c227b031232c266b0cb36684a0667bc1e1c667ade5a7e089ede81c7450d7f6752813b35366c516e299c742bc01d5c181609946af531191611af0f1bd54ae4d67b56fdd64adf15f06b082f310cbe366ea3575a7da1dc838511ea950a9cbfeef9f5ef2738b57e4c1f6f0bab3bd5e62349240b63381dd10ec6fa5fc8de2abb8a4a58660a7f28ece32c136698a7553d074c4e7d164681e63215f9cb75c5fb561f5b3b114ef965636d90f4e4441b7fd087d6d98cbd9f5c1e226862114353234e7aa9bea849fa448cfbd02e9e81b152fc63191874bbe0fe20c48e1cea50826d43afa2a72a96e9f24fa97a9d69925d554e663adf65a9fa24c4a2b1b67bc7e4b76421cd5ffe9e20105dbb302f20982c0957814506053b543233a2c7590d0480cdb0b3438e0b548520f0fc2711c22a1f767d354d8c38b425774275df6de4b407b91ff8b30dccc9c6db9b851fc660e1e21d0935836b4728cfe45f51c3da2c39898de35f0e5b4ecd0e0c24f98d9d6bf7a846d078e2e7c2ef0fd14d0e31bbb6e4554d6d9853caedf08a224240acdcc67a8c28a0062623befadc038060588ecf6de5af498784f5e0f8fd2bc5f6e6baef5831ffd27ab1dc35f09b0159ba74bc834c30c88e8ebe0476b38535e78cd6837a87471d1e9ac0b684b70a04c7f69ccb0acfd4ddb2a7e2613cfbb959fa87bd16461c6aadb01af448f8527b0bbbc6395a084aa6c1bd3c775d413dbfe95713d162017846aa7f05493869d34f0114c24833b46daea03705317963e662df4824ad7c4c6b46ecffaac0415f496639517c09261cc67fd5e098c341a8c2225ad411379a938cc0098fc30661e1bda1459c50036112b21a4a90afd0dc7ba085678d9c3118b0fa1197105672e8579866656240a2fc3a12ae3ba44ea1fe87a40c22d632402afd2eae70127748badf13f7b2d2c57856ee6e8389910e639806ed00e4341a76687c65207a6795855a6e13303c94c4bdf8caaf77404b3b8ad6c2d3e9a8a6789ae5393b1dd2b86fc7187f6aa6d6482078a4ccbbd09590f1cfed30e4529c12db2cd681ae2c748bb956132becabf5bd1edfb559ff8cf3947c23e61a11cf4ca7e82852ad4f1769993e5e6687e453125d5c2773da4f0fe6d9bd881f349fef305fde85b8d855bebe0b71b9eb7c790b07c92c428bdc9301a73b6dd6510f2ba408bfc0f3da9d1cac75ea1df87e923fe39c7dd623e339255f232e430cf601ffe425ba6b382b53510ac1ae9337dd25030dc118c89809e079278f779d2fef725ce94a5ffce5f304d17acd19f3ef69d0b0139b32c7b2634686e794d4351368542899f2d1fe8448c97141969dfd2d13fb32fb7a5ea84dc9e33e03e86b2523cae600732a910cc9491d299290c494eec4965deda04409f99617ed2c4feb644efa799cdb299fbed3738394c69277af037df9d763692c6cf9224d5d5456f3b78f4e990846d6559edc29edb010a1bdc9c9759e7c1ff63b1b68fa03767bdaa1f33a1cc2caacf3ad8db2d4a51856cd6640ac5b5daaed29e479768c95453219c3ac7b8d5a6497b35d41d2fb5279511ecfa62510099186fc97e2f93cddf93ecdf33a9df37ece3033bb26bf7812217dd3998e6b5d0d2e39711792d0325aa5698dbefddbb10b1dd0009d1f99dde87aaabc1156ca9cf193c3148556ba93a9dc071501b81ba5cb7863fea8c095bf7d97f5149957dc35f18bf03dd60a88bebd766ce8e1e6a0a050d2911e594838f9494c05b1455d0d06d308f1293fa52bd8d55fce8b5de98b676706bc5940fdec6ef54dde811ec4b042cda949ced3329fd4d581a2c120", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x084acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28bc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c", + "0x3f1467a096bcd71a5b6a0c8155e20810308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc": "0x000064a7b3b6e00d0000000000000000", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x4dcb50595177a3177648411a42aca0f54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x084acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28bc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xb8753e9383841da95f7b8871e5de32694e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000000000000000000000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb333f71360101c6556bc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c": "0xbc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3fe54f28cc5d1fc584acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28": "0x4acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950133cc54b617fa6116175726180bc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c": "0xbc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b8cd4c3513e0fa5761757261804acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28": "0x4acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x084acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28bc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x084acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe284acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28bc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252cbc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x04000000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000" + }, + "childrenDefault": {} + } + } +} diff --git a/cumulus/parachains/chain-specs/people-westend.json b/cumulus/parachains/chain-specs/people-westend.json index fa29853c70b05e47df50445935be42a4637f240d..29fa0c9cde79c0daecbce029c8fa377ba1ff6918 100644 --- a/cumulus/parachains/chain-specs/people-westend.json +++ b/cumulus/parachains/chain-specs/people-westend.json @@ -10,7 +10,9 @@ "/dns/westend-people-collator-node-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWGVYTVKW7tYe51JvetvGvVLDPXzqQX1mueJgz14FgkmHG", "/dns/westend-people-collator-node-2.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWGVYTVKW7tYe51JvetvGvVLDPXzqQX1mueJgz14FgkmHG", "/dns/westend-people-collator-node-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWCF1eA2Gap69zgXD7Df3e9DqDUsGoByocggTGejoHjK23", - "/dns/westend-people-collator-node-3.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWCF1eA2Gap69zgXD7Df3e9DqDUsGoByocggTGejoHjK23" + "/dns/westend-people-collator-node-3.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWCF1eA2Gap69zgXD7Df3e9DqDUsGoByocggTGejoHjK23", + "/dns/identity-westend.bootnodes.polkadotters.com/tcp/30532/p2p/12D3KooWKr9San6KTM7REJ95cBaDoiciGcWnW8TTftEJgxGF5Ehb", + "/dns/identity-westend.bootnodes.polkadotters.com/tcp/30534/wss/p2p/12D3KooWKr9San6KTM7REJ95cBaDoiciGcWnW8TTftEJgxGF5Ehb" ], "telemetryEndpoints": null, "protocolId": null, @@ -79,4 +81,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/common/Cargo.toml b/cumulus/parachains/common/Cargo.toml index 61ac91aeb06b492808a61bb365758b6e08112f2d..ebc9f822beb2c0069276876161158243501ab288 100644 --- a/cumulus/parachains/common/Cargo.toml +++ b/cumulus/parachains/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parachains-common" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true description = "Logic which is common to all parachain runtimes" @@ -14,10 +14,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"], default-features = false } -log = { version = "0.4.19", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -num-traits = { version = "0.2", default-features = false } -smallvec = "1.11.0" # Substrate frame-support = { path = "../../../substrate/frame/support", default-features = false } @@ -35,12 +33,8 @@ sp-std = { path = "../../../substrate/primitives/std", default-features = false # Polkadot pallet-xcm = { path = "../../../polkadot/xcm/pallet-xcm", default-features = false } -rococo-runtime-constants = { path = "../../../polkadot/runtime/rococo/constants", default-features = false } -westend-runtime-constants = { path = "../../../polkadot/runtime/westend/constants", default-features = false } -polkadot-core-primitives = { path = "../../../polkadot/core-primitives", default-features = false } polkadot-primitives = { path = "../../../polkadot/primitives", default-features = false } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } # Cumulus @@ -65,7 +59,6 @@ std = [ "frame-support/std", "frame-system/std", "log/std", - "num-traits/std", "pallet-asset-tx-payment/std", "pallet-assets/std", "pallet-authorship/std", @@ -74,17 +67,13 @@ std = [ "pallet-message-queue/std", "pallet-xcm/std", "parachain-info/std", - "polkadot-core-primitives/std", "polkadot-primitives/std", - "rococo-runtime-constants/std", "scale-info/std", "sp-consensus-aura/std", "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-std/std", - "westend-runtime-constants/std", - "xcm-builder/std", "xcm-executor/std", "xcm/std", ] @@ -102,6 +91,5 @@ runtime-benchmarks = [ "pallet-xcm/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] diff --git a/cumulus/parachains/common/src/impls.rs b/cumulus/parachains/common/src/impls.rs index 69da325dd4fc541fca16eea3bbd29a710a8f54e4..957538b7cdadbd68ed298a5a867cd1a7754e083d 100644 --- a/cumulus/parachains/common/src/impls.rs +++ b/cumulus/parachains/common/src/impls.rs @@ -257,7 +257,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } diff --git a/cumulus/parachains/common/src/lib.rs b/cumulus/parachains/common/src/lib.rs index 68ba10094c031131de377b45f19dc3454cb076fc..b01d623d2b93da529f37bbfd8cf139ea5b98d9ae 100644 --- a/cumulus/parachains/common/src/lib.rs +++ b/cumulus/parachains/common/src/lib.rs @@ -17,9 +17,6 @@ pub mod impls; pub mod message_queue; -pub mod rococo; -pub mod westend; -pub mod wococo; pub mod xcm_config; pub use constants::*; pub use opaque::*; diff --git a/cumulus/parachains/common/src/xcm_config.rs b/cumulus/parachains/common/src/xcm_config.rs index 15b090923d501b3769a18efe1916a146d4474f42..a9756af7aed245ea792d12addbbb2fff529eedaf 100644 --- a/cumulus/parachains/common/src/xcm_config.rs +++ b/cumulus/parachains/common/src/xcm_config.rs @@ -45,8 +45,6 @@ where >::AssetId, >::Balance, >, - AccountIdOf: - From + Into, { fn charge_weight_in_fungibles( asset_id: as Inspect< 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 ecd0059057a88f8bfe8eec26ee8181e21e2b00cd..f4f8b3603ba61e44195d6be8151b296f002ac720 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 @@ -11,18 +11,15 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus +parachains-common = { path = "../../../../../../../parachains/common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } asset-hub-rococo-runtime = { path = "../../../../../../runtimes/assets/asset-hub-rococo" } rococo-emulated-chain = { path = "../../../relays/rococo" } +testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["rococo"] } 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 c19d05ec73075b1a2fb8e483bd7ec783c93f0b9d..80db56444696a557d5a11fae47fe2bd72d3f00a0 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 @@ -23,7 +23,7 @@ use emulated_integration_tests_common::{ use parachains_common::Balance; pub const PARA_ID: u32 = 1000; -pub const ED: Balance = parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; +pub const ED: Balance = testnet_parachains_constants::rococo::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { let genesis_config = asset_hub_rococo_runtime::RuntimeGenesisConfig { 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 b49b16bf8558fd78b0519dfd26a102ebc70c7265..d4764f63bf64d3cc650d53ee8c637129448121a2 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 @@ -11,18 +11,15 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus +parachains-common = { path = "../../../../../../../parachains/common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } asset-hub-westend-runtime = { path = "../../../../../../runtimes/assets/asset-hub-westend" } westend-emulated-chain = { path = "../../../relays/westend" } +testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["westend"] } 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 78d6478fdf1ea0890abe2f081726a130762e257c..b2e4645ee076803accc14c3fc32653228c61ab98 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 @@ -23,7 +23,7 @@ use emulated_integration_tests_common::{ use parachains_common::Balance; pub const PARA_ID: u32 = 1000; -pub const ED: Balance = parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; +pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { let genesis_config = asset_hub_westend_runtime::RuntimeGenesisConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml index 2bc57bf25483106109fc12b4005f62f578be11d2..322d8b44e6ea6b293fb1c2f52e0de80fa9cd29f1 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml @@ -11,25 +11,14 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus -cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } +parachains-common = { path = "../../../../../../../parachains/common" } emulated-integration-tests-common = { path = "../../../../common", default-features = false } bridge-hub-rococo-runtime = { path = "../../../../../../runtimes/bridge-hubs/bridge-hub-rococo" } bridge-hub-common = { path = "../../../../../../runtimes/bridge-hubs/common", default-features = false } - -# Snowbridge -snowbridge-core = { path = "../../../../../../../../bridges/snowbridge/parachain/primitives/core", default-features = false } -snowbridge-router-primitives = { path = "../../../../../../../../bridges/snowbridge/parachain/primitives/router", default-features = false } -snowbridge-pallet-system = { path = "../../../../../../../../bridges/snowbridge/parachain/pallets/system", default-features = false } -snowbridge-pallet-inbound-queue = { path = "../../../../../../../../bridges/snowbridge/parachain/pallets/inbound-queue", default-features = false } -snowbridge-pallet-outbound-queue = { path = "../../../../../../../../bridges/snowbridge/parachain/pallets/outbound-queue", default-features = false } +testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["rococo"] } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs index 3dd0cb10ab697af86efd71b9aa1a2df0f35fa3c8..12778215b1320f591bec787198243c825313e858 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs @@ -24,7 +24,7 @@ use parachains_common::Balance; pub const ASSETHUB_PARA_ID: u32 = 1000; pub const PARA_ID: u32 = 1013; -pub const ED: Balance = parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; +pub const ED: Balance = testnet_parachains_constants::rococo::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { let genesis_config = bridge_hub_rococo_runtime::RuntimeGenesisConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml index 8fcf4fe5e8069fe93019c639504a5fa1bb88f009..ec1386b7f6e2b231e7f9ffdc925b3d0c802bf4f2 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/Cargo.toml @@ -11,18 +11,14 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus -cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } +parachains-common = { path = "../../../../../../../parachains/common" } emulated-integration-tests-common = { path = "../../../../common", default-features = false } bridge-hub-westend-runtime = { path = "../../../../../../runtimes/bridge-hubs/bridge-hub-westend" } bridge-hub-common = { path = "../../../../../../runtimes/bridge-hubs/common", default-features = false } +testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["westend"] } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs index 2eb7e0ddbd29a92f21169cb1b70fb1c803ea2623..4be68e510f4d254dcf645db682b6cf34ff69e634 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs @@ -23,7 +23,7 @@ use emulated_integration_tests_common::{ use parachains_common::Balance; pub const PARA_ID: u32 = 1002; -pub const ED: Balance = parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; +pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { let genesis_config = bridge_hub_westend_runtime::RuntimeGenesisConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml index f3fd5da3cfc130b9dee3e25900bcb2319266c0ae..03f755b666afbf87e876a7f7d5111aa98a1bc727 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml @@ -11,18 +11,14 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus +parachains-common = { path = "../../../../../../../parachains/common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } collectives-westend-runtime = { path = "../../../../../../runtimes/collectives/collectives-westend" } -westend-emulated-chain = { path = "../../../relays/westend" } +testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["westend"] } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs index d79ef55072ae481b4bba37ef7a97e2d3783668f2..6a28b1a9dddb8ca8b118592d85ef11367569d2a0 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs @@ -23,7 +23,7 @@ use emulated_integration_tests_common::{ use parachains_common::Balance; pub const PARA_ID: u32 = 1001; -pub const ED: Balance = parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; +pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { let genesis_config = collectives_westend_runtime::RuntimeGenesisConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml index 2f22d9b7a842dc3a569c87f41802fdebc39458aa..65a358d0ef2f6b697453f2c1cbef20d3b2cd93a8 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/Cargo.toml @@ -8,18 +8,14 @@ description = "People Rococo emulated chain" publish = false [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus +parachains-common = { path = "../../../../../../../parachains/common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } people-rococo-runtime = { path = "../../../../../../runtimes/people/people-rococo" } -rococo-emulated-chain = { path = "../../../relays/rococo" } +testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["rococo"] } 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 27d5531a0c7d3315ff71ef13cce41fea6b760375..b14009933029bfbc5cd5eeda9263126cd92a23c6 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 @@ -22,7 +22,7 @@ use emulated_integration_tests_common::{build_genesis_storage, collators, SAFE_X use parachains_common::Balance; pub const PARA_ID: u32 = 1004; -pub const ED: Balance = parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; +pub const ED: Balance = testnet_parachains_constants::rococo::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { let genesis_config = people_rococo_runtime::RuntimeGenesisConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml index dba6e6f6d4d6afb82707543bfb4fe160868251f1..075698848bcf0705c00ea46e3443802ca2fbe6b0 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/Cargo.toml @@ -8,18 +8,14 @@ description = "People Westend emulated chain" publish = false [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus +parachains-common = { path = "../../../../../../../parachains/common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } people-westend-runtime = { path = "../../../../../../runtimes/people/people-westend" } -westend-emulated-chain = { path = "../../../relays/westend" } +testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["westend"] } 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 612580c2b160efd13f0c5c7c937b638660c76226..d385fbebc821c05205f55bc5ea5f04d57ec9af21 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 @@ -22,7 +22,7 @@ use emulated_integration_tests_common::{build_genesis_storage, collators, SAFE_X use parachains_common::Balance; pub const PARA_ID: u32 = 1004; -pub const ED: Balance = parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; +pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { let genesis_config = people_westend_runtime::RuntimeGenesisConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml index 170d0b1a0b42a15fcac53a4a7c25181fec3dfb56..a853825d8ef626aef9f7a7d1c013667aea9aeb89 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml @@ -11,17 +11,13 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false } -# Polakadot -parachains-common = { path = "../../../../../../../parachains/common" } - # Cumulus +parachains-common = { path = "../../../../../../../parachains/common" } cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false } emulated-integration-tests-common = { path = "../../../../common", default-features = false } penpal-runtime = { path = "../../../../../../runtimes/testing/penpal" } 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 8f586a46a75cb68a7ccdf62d2f23a1865dcf5d9c..c1e030466559e86f126640a3ee2a97e3fbf5a8a5 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 @@ -16,7 +16,8 @@ mod genesis; pub use genesis::{genesis, ED, PARA_ID_A, PARA_ID_B}; pub use penpal_runtime::xcm_config::{ - LocalTeleportableToAssetHub, LocalTeleportableToAssetHubV3, XcmConfig, + CustomizableAssetFromSystemAssetHub, LocalTeleportableToAssetHub, + LocalTeleportableToAssetHubV3, XcmConfig, }; // Substrate diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml index 2e8ea60efd6634e9ffe597e84615030a1247b0d9..2d27426cca7599e3284caa6c8ca69a2d546a8da2 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/Cargo.toml @@ -11,16 +11,13 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } sp-authority-discovery = { path = "../../../../../../../substrate/primitives/authority-discovery", default-features = false } sp-consensus-babe = { path = "../../../../../../../substrate/primitives/consensus/babe", default-features = false } beefy-primitives = { package = "sp-consensus-beefy", path = "../../../../../../../substrate/primitives/consensus/beefy" } grandpa = { package = "sc-consensus-grandpa", path = "../../../../../../../substrate/client/consensus/grandpa", default-features = false } -pallet-im-online = { path = "../../../../../../../substrate/frame/im-online", default-features = false } # Polkadot polkadot-primitives = { path = "../../../../../../../polkadot/primitives", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs index 45e1e94de0100bf8a765b22163f519d0a54ced1c..55437645b0523b577c2e9d455952f5526ba9df0b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/rococo/src/genesis.rs @@ -25,8 +25,7 @@ use polkadot_primitives::{AssignmentId, ValidatorId}; // Cumulus use emulated_integration_tests_common::{ - accounts, build_genesis_storage, get_account_id_from_seed, get_from_seed, get_host_config, - validators, + accounts, build_genesis_storage, get_account_id_from_seed, get_host_config, validators, }; use parachains_common::Balance; use rococo_runtime_constants::currency::UNITS as ROC; @@ -71,7 +70,7 @@ pub fn genesis() -> Storage { x.4.clone(), x.5.clone(), x.6.clone(), - get_from_seed::("Alice"), + x.7.clone(), ), ) }) @@ -79,7 +78,7 @@ pub fn genesis() -> Storage { }, babe: rococo_runtime::BabeConfig { authorities: Default::default(), - epoch_config: Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), + epoch_config: rococo_runtime::BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, sudo: rococo_runtime::SudoConfig { diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml index 61174e2751b8871a09d77e01cab9648de90f9b9c..abc40c2040681cc4cb41246a9872d557134b6dde 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml @@ -11,7 +11,6 @@ publish = false workspace = true [dependencies] -serde_json = "1.0.111" # Substrate sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } @@ -20,7 +19,6 @@ sp-authority-discovery = { path = "../../../../../../../substrate/primitives/aut sp-consensus-babe = { path = "../../../../../../../substrate/primitives/consensus/babe", default-features = false } beefy-primitives = { package = "sp-consensus-beefy", path = "../../../../../../../substrate/primitives/consensus/beefy" } grandpa = { package = "sc-consensus-grandpa", path = "../../../../../../../substrate/client/consensus/grandpa", default-features = false } -pallet-im-online = { path = "../../../../../../../substrate/frame/im-online", default-features = false } pallet-staking = { path = "../../../../../../../substrate/frame/staking", default-features = false } # Polkadot diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs index e2297100a4525e6d26cb469f6f1458023a12b82c..700b80e63f6cf68e6095b7e02d84bb285ce720f9 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs @@ -26,7 +26,7 @@ use polkadot_primitives::{AssignmentId, ValidatorId}; // Cumulus use emulated_integration_tests_common::{ - accounts, build_genesis_storage, get_from_seed, get_host_config, validators, + accounts, build_genesis_storage, get_host_config, validators, }; use parachains_common::Balance; use westend_runtime_constants::currency::UNITS as WND; @@ -72,7 +72,7 @@ pub fn genesis() -> Storage { x.4.clone(), x.5.clone(), x.6.clone(), - get_from_seed::("Alice"), + x.7.clone(), ), ) }) @@ -94,7 +94,7 @@ pub fn genesis() -> Storage { }, babe: westend_runtime::BabeConfig { authorities: Default::default(), - epoch_config: Some(westend_runtime::BABE_GENESIS_EPOCH_CONFIG), + epoch_config: westend_runtime::BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, configuration: westend_runtime::ConfigurationConfig { config: get_host_config() }, diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml index 2ee91e83585c191a8331be20a8ac0946ffc0a0cb..721c58fd86481ca7269db8567c1cc0fd3507a92b 100644 --- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "emulated-integration-tests-common" -version = "1.0.0" +version = "3.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -12,9 +12,9 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } paste = "1.0.14" -serde_json = "1.0.111" # Substrate +beefy-primitives = { package = "sp-consensus-beefy", path = "../../../../../substrate/primitives/consensus/beefy" } grandpa = { package = "sc-consensus-grandpa", path = "../../../../../substrate/client/consensus/grandpa" } sp-authority-discovery = { path = "../../../../../substrate/primitives/authority-discovery" } sp-runtime = { path = "../../../../../substrate/primitives/runtime" } @@ -24,11 +24,8 @@ sp-consensus-babe = { path = "../../../../../substrate/primitives/consensus/babe pallet-assets = { path = "../../../../../substrate/frame/assets" } pallet-balances = { path = "../../../../../substrate/frame/balances" } pallet-message-queue = { path = "../../../../../substrate/frame/message-queue" } -pallet-im-online = { path = "../../../../../substrate/frame/im-online" } -beefy-primitives = { package = "sp-consensus-beefy", path = "../../../../../substrate/primitives/consensus/beefy" } # Polkadot -polkadot-service = { path = "../../../../../polkadot/node/service", default-features = false, features = ["full-node"] } polkadot-primitives = { path = "../../../../../polkadot/primitives" } polkadot-runtime-parachains = { path = "../../../../../polkadot/runtime/parachains" } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm" } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index ad69d5576aae514f36a741e3055f34771ab437a1..1a5cc1f6fea6dde1882180d8389e9bcc7773d023 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -20,6 +20,7 @@ pub mod xcm_helpers; pub use xcm_emulator; // Substrate +use beefy_primitives::ecdsa_crypto::AuthorityId as BeefyId; use grandpa::AuthorityId as GrandpaId; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; @@ -34,9 +35,8 @@ use parachains_common::BlockNumber; use polkadot_runtime_parachains::configuration::HostConfiguration; // Cumulus -use parachains_common::{AccountId, AssetHubPolkadotAuraId, AuraId}; +use parachains_common::{AccountId, AuraId}; use polkadot_primitives::{AssignmentId, ValidatorId}; -use polkadot_service::chain_spec::get_authority_keys_from_seed_no_beefy; pub const XCM_V2: u32 = 2; pub const XCM_V3: u32 = 3; @@ -100,7 +100,7 @@ pub mod accounts { pub const CHARLIE: &str = "Charlie"; pub const DAVE: &str = "Dave"; pub const EVE: &str = "Eve"; - pub const FERDIE: &str = "Ferdei"; + pub const FERDIE: &str = "Ferdie"; pub const ALICE_STASH: &str = "Alice//stash"; pub const BOB_STASH: &str = "Bob//stash"; pub const CHARLIE_STASH: &str = "Charlie//stash"; @@ -130,19 +130,6 @@ pub mod accounts { pub mod collators { use super::*; - pub fn invulnerables_asset_hub_polkadot() -> Vec<(AccountId, AssetHubPolkadotAuraId)> { - vec![ - ( - get_account_id_from_seed::("Alice"), - get_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_from_seed::("Bob"), - ), - ] - } - pub fn invulnerables() -> Vec<(AccountId, AuraId)> { vec![ ( @@ -165,7 +152,18 @@ pub mod validators { ValidatorId, AssignmentId, AuthorityDiscoveryId, + BeefyId, )> { - vec![get_authority_keys_from_seed_no_beefy("Alice")] + let seed = "Alice"; + vec![( + get_account_id_from_seed::(&format!("{}//stash", seed)), + get_account_id_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + )] } } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index 01a376e4dbf8cb304b55951540613b167996d4c8..d3bb3238a3b4308ca010cf1fa5fe8cd65ab2b74c 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -76,7 +76,7 @@ macro_rules! test_parachain_is_trusted_teleporter { $crate::macros::cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. } ) => {}, RuntimeEvent::Balances( - $crate::macros::pallet_balances::Event::Withdraw { who: sender, amount } + $crate::macros::pallet_balances::Event::Burned { who: sender, amount } ) => {}, ] ); @@ -90,7 +90,7 @@ macro_rules! test_parachain_is_trusted_teleporter { $receiver_para, vec![ RuntimeEvent::Balances( - $crate::macros::pallet_balances::Event::Deposit { who: receiver, .. } + $crate::macros::pallet_balances::Event::Minted { who: receiver, .. } ) => {}, RuntimeEvent::MessageQueue( $crate::macros::pallet_message_queue::Event::Processed { success: true, .. } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml index 445395fc783075e0483b6f0319b467ad4be90b14..0a397c2617b4bbb4c9a509b8ef32dbcadbc33228 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml @@ -31,6 +31,8 @@ rococo-runtime = { path = "../../../../../../../polkadot/runtime/rococo" } # Cumulus asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } parachains-common = { path = "../../../../../../parachains/common" } +cumulus-pallet-parachain-system = { path = "../../../../../../pallets/parachain-system", default-features = false } +testnet-parachains-constants = { path = "../../../../../runtimes/constants", features = ["rococo"] } asset-hub-rococo-runtime = { path = "../../../../../runtimes/assets/asset-hub-rococo" } emulated-integration-tests-common = { path = "../../../common", default-features = false } rococo-system-emulated-network = { path = "../../../networks/rococo-system" } 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 155c952327aa42b93f369adb52ea04267f506973..1cc25cb54a14bb757ae844899f2c64e55b8c80bb 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 @@ -51,8 +51,8 @@ pub use rococo_system_emulated_network::{ AssetHubRococoParaSender as AssetHubRococoSender, BridgeHubRococoPara as BridgeHubRococo, BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, - RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver, - RococoRelaySender as RococoSender, + PenpalBPara as PenpalB, PenpalBParaReceiver as PenpalBReceiver, RococoRelay as Rococo, + RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, }; pub const ASSET_ID: u32 = 1; @@ -65,6 +65,7 @@ pub type RelayToParaTest = Test; pub type SystemParaToRelayTest = Test; pub type SystemParaToParaTest = Test; pub type ParaToSystemParaTest = Test; +pub type ParaToParaTest = Test; #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs index c9270934ddfe87c4c969428facccda71b9401588..21bed234304e2fae3eb285253e528d02e5441854 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs @@ -24,5 +24,5 @@ emulated_integration_tests_common::include_penpal_create_foreign_asset_on_asset_ PenpalA, AssetHubRococo, ROCOCO_ED, - parachains_common::rococo::fee::WeightToFee + testnet_parachains_constants::rococo::fee::WeightToFee ); 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 dfc0bc6ff392b56e60e3d226bb019c1dd3e603a7..d2c3a323256c193a3af6230c0a0b33ae3f54198f 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 @@ -66,7 +66,7 @@ fn para_receiver_assertions(_: Test) { assert_expected_events!( PenpalA, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -82,7 +82,7 @@ fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { vec![ // Amount to reserve transfer is transferred to Parachain's Sovereign account RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, @@ -101,12 +101,12 @@ fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { vec![ // Amount to reserve transfer is withdrawn from Parachain's Sovereign account RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == sov_penpal_on_ahr.clone().into(), amount: *amount == t.args.amount, }, - RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -143,7 +143,7 @@ fn system_para_to_para_assets_receiver_assertions(_: Test) { assert_expected_events!( PenpalA, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } @@ -152,6 +152,69 @@ fn system_para_to_para_assets_receiver_assertions(_: Test) { ); } +fn para_to_para_sender_assertions(t: ParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcm_pallet_attempted_complete(None); + assert_expected_events!( + PenpalA, + vec![ + // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == t.sender.account_id, + amount: *amount == t.args.amount, + }, + // XCM sent to relay reserve + RuntimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); +} + +fn para_to_para_relay_hop_assertions(t: ParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_a_on_rococo = + Rococo::sovereign_account_id_of(Rococo::child_location_of(PenpalA::para_id())); + let sov_penpal_b_on_rococo = + Rococo::sovereign_account_id_of(Rococo::child_location_of(PenpalB::para_id())); + assert_expected_events!( + Rococo, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_penpal_a_on_rococo, + amount: *amount == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Minted { who, .. } + ) => { + who: *who == sov_penpal_b_on_rococo, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + +fn para_to_para_receiver_assertions(_: ParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult { ::XcmPallet::limited_reserve_transfer_assets( t.signed_origin, @@ -185,6 +248,17 @@ fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> Dispa ) } +fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> 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 System Parachain shouldn't work #[test] fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { @@ -493,3 +567,51 @@ fn reserve_transfer_assets_from_system_para_to_para() { // Receiver's balance is increased by exact amount assert_eq!(receiver_assets_after, receiver_assets_before + asset_amount_to_send); } + +/// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should +/// work +#[test] +fn reserve_transfer_native_asset_from_para_to_para() { + // Init values for Penpal Parachain + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let beneficiary_id = PenpalBReceiver::get(); + let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; + let assets = (Parent, amount_to_send).into(); + + let test_args = TestContext { + sender: PenpalASender::get(), + receiver: PenpalBReceiver::get(), + args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), + }; + + let mut test = ParaToParaTest::new(test_args); + + let sender_balance_before = test.sender.balance; + let receiver_balance_before = test.receiver.balance; + + let sender_as_seen_by_relay = Rococo::child_location_of(PenpalA::para_id()); + let sov_of_sender_on_relay = Rococo::sovereign_account_id_of(sender_as_seen_by_relay); + + // fund the PenpalA's SA on Rococo with the native tokens held in reserve + Rococo::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); + + test.set_assertion::(para_to_para_sender_assertions); + test.set_assertion::(para_to_para_relay_hop_assertions); + test.set_assertion::(para_to_para_receiver_assertions); + test.set_dispatchable::(para_to_para_limited_reserve_transfer_assets); + test.assert(); + + let sender_balance_after = test.sender.balance; + let receiver_balance_after = test.receiver.balance; + + let delivery_fees = PenpalA::execute_with(|| { + xcm_helpers::transfer_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); +} 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 d6aade68086d5452a3deeb657c07f796c11cba40..c6a10b252901c03636caf2a6c8291781c28a8b09 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 @@ -14,7 +14,6 @@ // limitations under the License. use crate::*; -use parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; use rococo_system_emulated_network::penpal_emulated_chain::LocalTeleportableToAssetHubV3 as PenpalLocalTeleportableToAssetHubV3; use sp_runtime::ModuleError; @@ -105,7 +104,7 @@ fn swap_locally_on_chain_using_local_assets() { ::RuntimeOrigin::signed(AssetHubRococoSender::get()), asset_native, asset_one, - 1414213562273 - EXISTENTIAL_DEPOSIT * 2, // all but the 2 EDs can't be retrieved. + 1414213562273 - ASSET_HUB_ROCOCO_ED * 2, // all but the 2 EDs can't be retrieved. 0, 0, AssetHubRococoSender::get().into(), 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 218234cc78eaab2801d08f4fa34b99eaaa5935b9..dfb5061b55f053b51e0ceedee3f8df0edbb7b8ba 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 @@ -28,12 +28,12 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { Rococo, vec![ // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { who: *who == ::XcmPallet::check_account(), amount: *amount == t.args.amount, }, @@ -54,12 +54,12 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { Rococo, vec![ // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] @@ -88,7 +88,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { AssetHubRococo, vec![ // Amount is withdrawn from Sender's account - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, }, @@ -105,7 +105,7 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { AssetHubRococo, vec![ // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] @@ -122,7 +122,7 @@ fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { PenpalA, vec![ RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, @@ -149,12 +149,12 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { vec![ // native asset reserve transfer for paying fees, withdrawn from Penpal's sov account RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == sov_penpal_on_ahr.clone().into(), amount: *amount == t.args.amount, }, - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { @@ -221,7 +221,7 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { amount: *amount == expected_asset_amount, }, // native asset for fee is deposited to receiver - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, RuntimeEvent::MessageQueue( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml index 3b2d3367d40d1984ee91fac96c37b524a5a97c27..0c920730d0fe695e84c77560ae9755ec65d19d46 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml @@ -17,28 +17,24 @@ assert_matches = "1.5.0" # Substrate sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../../../../../substrate/frame/system", default-features = false } pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false } pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false } pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false } pallet-treasury = { path = "../../../../../../../substrate/frame/treasury", default-features = false } -pallet-asset-rate = { path = "../../../../../../../substrate/frame/asset-rate", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false } # Polkadot polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common" } xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../../../polkadot/xcm/xcm-builder", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../../polkadot/xcm/xcm-executor", default-features = false } pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-features = false } westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } -westend-runtime-constants = { path = "../../../../../../../polkadot/runtime/westend/constants", default-features = false } # Cumulus parachains-common = { path = "../../../../../../parachains/common" } +testnet-parachains-constants = { path = "../../../../../runtimes/constants", features = ["westend"] } asset-hub-westend-runtime = { path = "../../../../../runtimes/assets/asset-hub-westend" } asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } -cumulus-pallet-dmp-queue = { default-features = false, path = "../../../../../../pallets/dmp-queue" } cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../../pallets/xcmp-queue" } cumulus-pallet-parachain-system = { default-features = false, path = "../../../../../../pallets/parachain-system" } emulated-integration-tests-common = { path = "../../../common", default-features = false } 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 018e948e5d763022150e90356206027d49260455..409369df7bb0633a7137c025cb6d28f6f41f8f49 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 @@ -56,7 +56,8 @@ pub use westend_system_emulated_network::{ AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubWestendPara as BridgeHubWestend, BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, - CollectivesWestendPara as CollectivesWestend, PenpalBPara as PenpalB, + CollectivesWestendPara as CollectivesWestend, PenpalAPara as PenpalA, + PenpalAParaReceiver as PenpalAReceiver, PenpalBPara as PenpalB, PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender, WestendRelay as Westend, WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, @@ -72,6 +73,7 @@ pub type RelayToParaTest = Test; pub type SystemParaToRelayTest = Test; pub type SystemParaToParaTest = Test; pub type ParaToSystemParaTest = Test; +pub type ParaToParaTest = Test; #[cfg(test)] mod tests; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs index ee720c2448041c085ee53c014c8d8125e7ee8caa..a56cde8f2a2cb2263cd20c5fe4255459c94a084a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs @@ -26,5 +26,5 @@ emulated_integration_tests_common::include_penpal_create_foreign_asset_on_asset_ PenpalB, AssetHubWestend, WESTEND_ED, - parachains_common::westend::fee::WeightToFee + testnet_parachains_constants::westend::fee::WeightToFee ); 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 a2934be97580f848925b3b1b553c79506259bf05..a29cd10ba8338c9ca560bad29b18fba3a94b4c4d 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 @@ -70,7 +70,7 @@ fn para_receiver_assertions(_: Test) { assert_expected_events!( PenpalB, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -88,7 +88,7 @@ fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { vec![ // Amount to reserve transfer is transferred to Parachain's Sovereign account RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, @@ -109,12 +109,12 @@ fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { vec![ // Amount to reserve transfer is transferred to Parachain's Sovereign account RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == sov_penpal_on_ahw.clone().into(), amount: *amount == t.args.amount, }, - RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -153,7 +153,7 @@ fn system_para_to_para_assets_receiver_assertions(_: Test) { assert_expected_events!( PenpalB, vec![ - RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } @@ -162,6 +162,69 @@ fn system_para_to_para_assets_receiver_assertions(_: Test) { ); } +fn para_to_para_sender_assertions(t: ParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalB::assert_xcm_pallet_attempted_complete(None); + assert_expected_events!( + PenpalB, + vec![ + // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == t.sender.account_id, + amount: *amount == t.args.amount, + }, + // XCM sent to relay reserve + RuntimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); +} + +fn para_to_para_relay_hop_assertions(t: ParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_b_on_westend = + Westend::sovereign_account_id_of(Westend::child_location_of(PenpalB::para_id())); + let sov_penpal_a_on_westend = + Westend::sovereign_account_id_of(Westend::child_location_of(PenpalA::para_id())); + assert_expected_events!( + Westend, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_penpal_b_on_westend, + amount: *amount == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Minted { who, .. } + ) => { + who: *who == sov_penpal_a_on_westend, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + +fn para_to_para_receiver_assertions(_: ParaToParaTest) { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult { ::XcmPallet::limited_reserve_transfer_assets( t.signed_origin, @@ -195,6 +258,17 @@ fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> Dispa ) } +fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> 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 System Parachain shouldn't work #[test] fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { @@ -503,3 +577,51 @@ fn reserve_transfer_assets_from_system_para_to_para() { // Receiver's balance is increased by exact amount assert_eq!(receiver_assets_after, receiver_assets_before + asset_amount_to_send); } + +/// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should +/// work +#[test] +fn reserve_transfer_native_asset_from_para_to_para() { + // Init values for Penpal Parachain + let destination = PenpalB::sibling_location_of(PenpalA::para_id()); + let beneficiary_id = PenpalAReceiver::get(); + let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; + let assets = (Parent, amount_to_send).into(); + + let test_args = TestContext { + sender: PenpalBSender::get(), + receiver: PenpalAReceiver::get(), + args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), + }; + + let mut test = ParaToParaTest::new(test_args); + + let sender_balance_before = test.sender.balance; + let receiver_balance_before = test.receiver.balance; + + let sender_as_seen_by_relay = Westend::child_location_of(PenpalB::para_id()); + let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay); + + // fund the PenpalB's SA on Westend with the native tokens held in reserve + Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); + + test.set_assertion::(para_to_para_sender_assertions); + test.set_assertion::(para_to_para_relay_hop_assertions); + test.set_assertion::(para_to_para_receiver_assertions); + test.set_dispatchable::(para_to_para_limited_reserve_transfer_assets); + test.assert(); + + let sender_balance_after = test.sender.balance; + let receiver_balance_after = test.receiver.balance; + + let delivery_fees = PenpalB::execute_with(|| { + xcm_helpers::transfer_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); +} 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 01498f7bb4e3b525d5edff164a7fb18a035517e4..0dd1a1533b55904e4fb99dd0d9e3c6face43b19d 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 @@ -28,12 +28,12 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { Westend, vec![ // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { who: *who == ::XcmPallet::check_account(), amount: *amount == t.args.amount, }, @@ -54,12 +54,12 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { Westend, vec![ // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] @@ -88,7 +88,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { AssetHubWestend, vec![ // Amount is withdrawn from Sender's account - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, }, @@ -105,7 +105,7 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { AssetHubWestend, vec![ // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] @@ -122,7 +122,7 @@ fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { PenpalB, vec![ RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, @@ -149,12 +149,12 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { vec![ // native asset reserve transfer for paying fees, withdrawn from Penpal's sov account RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == sov_penpal_on_ahr.clone().into(), amount: *amount == t.args.amount, }, - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { @@ -221,7 +221,7 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { amount: *amount == expected_asset_amount, }, // native asset for fee is deposited to receiver - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, RuntimeEvent::MessageQueue( 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 da09c674a03dbc8551cd8a3dbb8125718e65527a..89f0d2a9ca6dacae72e73d5b6e8c310347389070 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 @@ -13,7 +13,6 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -hex = "0.4.3" hex-literal = "0.4.1" # Substrate @@ -35,20 +34,19 @@ pallet-bridge-messages = { path = "../../../../../../../bridges/modules/messages bp-messages = { path = "../../../../../../../bridges/primitives/messages", default-features = false } # Cumulus -asset-test-utils = { path = "../../../../../../parachains/runtimes/assets/test-utils" } parachains-common = { path = "../../../../../../parachains/common" } +testnet-parachains-constants = { path = "../../../../../runtimes/constants", features = ["rococo"] } cumulus-pallet-xcmp-queue = { path = "../../../../../../pallets/xcmp-queue", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../../../pallets/dmp-queue", default-features = false } bridge-hub-rococo-runtime = { path = "../../../../../../parachains/runtimes/bridge-hubs/bridge-hub-rococo", default-features = false } emulated-integration-tests-common = { path = "../../../common", default-features = false } rococo-westend-system-emulated-network = { path = "../../../networks/rococo-westend-system" } -penpal-runtime = { path = "../../../../../runtimes/testing/penpal", default-features = false } rococo-system-emulated-network = { path = "../../../networks/rococo-system" } asset-hub-rococo-runtime = { path = "../../../../../runtimes/assets/asset-hub-rococo", default-features = false } # Snowbridge -snowbridge-core = { path = "../../../../../../../bridges/snowbridge/parachain/primitives/core", default-features = false } -snowbridge-router-primitives = { path = "../../../../../../../bridges/snowbridge/parachain/primitives/router", default-features = false } -snowbridge-pallet-system = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/system", default-features = false } -snowbridge-pallet-inbound-queue = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/inbound-queue", default-features = false } -snowbridge-pallet-outbound-queue = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/outbound-queue", default-features = false } +snowbridge-core = { path = "../../../../../../../bridges/snowbridge/primitives/core", default-features = false } +snowbridge-router-primitives = { path = "../../../../../../../bridges/snowbridge/primitives/router", default-features = false } +snowbridge-pallet-system = { path = "../../../../../../../bridges/snowbridge/pallets/system", default-features = false } +snowbridge-pallet-outbound-queue = { path = "../../../../../../../bridges/snowbridge/pallets/outbound-queue", default-features = false } +snowbridge-pallet-inbound-queue = { path = "../../../../../../../bridges/snowbridge/pallets/inbound-queue", default-features = false } +snowbridge-pallet-inbound-queue-fixtures = { path = "../../../../../../../bridges/snowbridge/pallets/inbound-queue/fixtures" } 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 0039eb087fe0901e3c75102cbc22faaced5dcc73..9ce981b074c5b671f7a7a4005b1b1e4ae9b1d98f 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 @@ -43,11 +43,6 @@ pub use emulated_integration_tests_common::{ PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, }; pub use parachains_common::{AccountId, Balance}; -pub use rococo_system_emulated_network::{ - penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet, - BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, PenpalAPara as PenpalA, - PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, -}; pub use rococo_westend_system_emulated_network::{ asset_hub_rococo_emulated_chain::{ genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet, @@ -58,14 +53,17 @@ pub use rococo_westend_system_emulated_network::{ bridge_hub_rococo_emulated_chain::{ genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoParaPallet as BridgeHubRococoPallet, }, + penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet, rococo_emulated_chain::{genesis::ED as ROCOCO_ED, 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, - RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver, - RococoRelaySender as RococoSender, + PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver, + PenpalAParaSender as PenpalASender, RococoRelay as Rococo, + RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, }; pub const ASSET_ID: u32 = 1; 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 a203de0f8c930a43c1848bfa9179e0cba40689a9..787a82ed32f7376f1c94c584711098c15d4da198 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 @@ -184,13 +184,13 @@ fn send_wnds_from_asset_hub_rococo_to_asset_hub_westend() { vec![ // WND is withdrawn from AHR's SA on AHW RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == sov_ahr_on_ahw, amount: *amount == amount_to_send, }, // WNDs deposited to beneficiary - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == AssetHubWestendReceiver::get(), }, // message processed successfully 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 a33d2fab7536cfffe46a57548a21fb5e7f0391e4..6d7b53c8fdfdb3660a4888df5dfd380bccc9d0dd 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 @@ -68,7 +68,7 @@ pub(crate) fn assert_bridge_hub_rococo_message_accepted(expected_processed: bool BridgeHubRococo, vec![ // pay for bridge fees - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Burned { .. }) => {}, // message exported RuntimeEvent::BridgeWestendMessages( pallet_bridge_messages::Event::MessageAccepted { .. } 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 fe0e479d8e5c76c600e0c7951d528c911eb7aa89..ff069e2d3443ec13d8cb17a18c7127f48b6f34ef 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 @@ -13,17 +13,25 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::*; +use bridge_hub_rococo_runtime::{EthereumBeaconClient, EthereumInboundQueue, RuntimeOrigin}; use codec::{Decode, Encode}; use emulated_integration_tests_common::xcm_emulator::ConvertLocation; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use parachains_common::rococo::snowbridge::EthereumNetwork; +use rococo_system_emulated_network::penpal_emulated_chain::CustomizableAssetFromSystemAssetHub; +use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender; use snowbridge_core::outbound::OperatingMode; -use snowbridge_pallet_system; -use snowbridge_router_primitives::inbound::{ - Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, +use snowbridge_pallet_inbound_queue_fixtures::{ + register_token::make_register_token_message, + register_token_with_insufficient_fee::make_register_token_with_infufficient_fee_message, + send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message, + InboundQueueFixture, }; +use snowbridge_pallet_system; +use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; use sp_core::H256; +use sp_runtime::{ArithmeticError::Underflow, DispatchError::Arithmetic}; +use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; const INITIAL_FUND: u128 = 5_000_000_000 * ROCOCO_ED; const CHAIN_ID: u64 = 11155111; @@ -31,7 +39,6 @@ const TREASURY_ACCOUNT: [u8; 32] = hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); -const XCM_FEE: u128 = 4_000_000_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -48,17 +55,34 @@ pub enum SnowbridgeControl { Control(ControlCall), } +pub fn send_inbound_message(fixture: InboundQueueFixture) -> DispatchResult { + EthereumBeaconClient::store_execution_header( + fixture.message.proof.block_hash, + fixture.execution_header, + 0, + H256::default(), + ); + + EthereumInboundQueue::submit( + RuntimeOrigin::signed(BridgeHubRococoSender::get()), + fixture.message, + ) +} + +/// Create an agent on Ethereum. An agent is a representation of an entity in the Polkadot +/// ecosystem (like a parachain) on Ethereum. #[test] +#[ignore] fn create_agent() { let origin_para: u32 = 1001; - + // Fund the origin parachain sovereign account so that it can pay execution fees. BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND); let sudo_origin = ::RuntimeOrigin::root(); let destination = Rococo::child_location_of(BridgeHubRococo::para_id()).into(); let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); - + // Construct XCM to create an agent for para 1001 let remote_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), @@ -69,7 +93,7 @@ fn create_agent() { }, ])); - //Rococo Global Consensus + // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { assert_ok!(::XcmPallet::send( @@ -79,7 +103,7 @@ fn create_agent() { )); type RuntimeEvent = ::RuntimeEvent; - + // Check that the Transact message was sent assert_expected_events!( Rococo, vec![ @@ -90,7 +114,7 @@ fn create_agent() { BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - + // Check that a message was sent to Ethereum to create the agent assert_expected_events!( BridgeHubRococo, vec![ @@ -102,10 +126,13 @@ fn create_agent() { }); } +/// Create a channel for a consensus system. A channel is a bidirectional messaging channel +/// between BridgeHub and Ethereum. #[test] +#[ignore] fn create_channel() { let origin_para: u32 = 1001; - + // Fund AssetHub sovereign account so that it can pay execution fees. BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND); let sudo_origin = ::RuntimeOrigin::root(); @@ -113,7 +140,7 @@ fn create_channel() { Rococo::child_location_of(BridgeHubRococo::para_id()).into(); let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); - + // Construct XCM to create an agent for para 1001 let create_agent_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), @@ -126,7 +153,7 @@ fn create_channel() { let create_channel_call = SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal }); - + // Construct XCM to create a channel for para 1001 let create_channel_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), @@ -137,7 +164,7 @@ fn create_channel() { }, ])); - //Rococo Global Consensus + // Rococo Global Consensus // Send XCM message from Relay Chain to Bridge Hub source Parachain Rococo::execute_with(|| { assert_ok!(::XcmPallet::send( @@ -165,6 +192,7 @@ fn create_channel() { BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; + // Check that the Channel was created assert_expected_events!( BridgeHubRococo, vec![ @@ -176,25 +204,18 @@ fn create_channel() { }); } +/// Tests the registering of a token as an asset on AssetHub. #[test] fn register_weth_token_from_ethereum_to_asset_hub() { + // Fund AssetHub sovereign account so that it can pay execution fees. BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND); - let message_id_: H256 = [1; 32].into(); - BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type EthereumInboundQueue = - ::EthereumInboundQueue; - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, - }); - let (xcm, fee) = EthereumInboundQueue::do_convert(message_id_, message).unwrap(); - - assert_ok!(EthereumInboundQueue::burn_fees(AssetHubRococo::para_id().into(), fee)); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + // Construct RegisterToken message and sent to inbound queue + let register_token_message = make_register_token_message(); + send_inbound_message(register_token_message.clone()).unwrap(); assert_expected_events!( BridgeHubRococo, @@ -216,46 +237,48 @@ fn register_weth_token_from_ethereum_to_asset_hub() { }); } +/// Tests sending a token to a 3rd party parachain, called PenPal. The token reserve is +/// still located on AssetHub. #[test] fn send_token_from_ethereum_to_penpal() { let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( 1, [Parachain(AssetHubRococo::para_id().into())], )); + // Fund AssetHub sovereign account so it can pay execution fees for the asset transfer BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]); + // Fund PenPal sender and receiver PenpalA::fund_accounts(vec![ (PenpalAReceiver::get(), INITIAL_FUND), (PenpalASender::get(), INITIAL_FUND), ]); + PenpalA::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]).encode(), + )], + )); + }); + + // The Weth asset location, identified by the contract address on Ethereum let weth_asset_location: Location = (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + // Converts the Weth asset location into an asset ID let weth_asset_id: v3::Location = weth_asset_location.try_into().unwrap(); let origin_location = (Parent, Parent, EthereumNetwork::get()).into(); - // Fund ethereum sovereign in asset hub + // Fund ethereum sovereign on AssetHub let ethereum_sovereign: AccountId = GlobalConsensusEthereumConvertsFor::::convert_location(&origin_location) .unwrap(); AssetHubRococo::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); - // Create asset on assethub. - AssetHubRococo::execute_with(|| { - assert_ok!(::ForeignAssets::create( - pallet_xcm::Origin::Xcm(origin_location).into(), - weth_asset_id, - asset_hub_sovereign.clone().into(), - 1000, - )); - - assert!(::ForeignAssets::asset_exists( - weth_asset_id - )); - }); - - // Create asset on penpal. + // Create asset on the Penpal parachain. PenpalA::execute_with(|| { assert_ok!(::ForeignAssets::create( ::RuntimeOrigin::signed(PenpalASender::get()), @@ -267,27 +290,14 @@ fn send_token_from_ethereum_to_penpal() { assert!(::ForeignAssets::asset_exists(weth_asset_id)); }); - let message_id_: H256 = [1; 32].into(); - BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type EthereumInboundQueue = - ::EthereumInboundQueue; - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::SendToken { - token: WETH.into(), - destination: Destination::ForeignAccountId32 { - para_id: 2000, - id: PenpalAReceiver::get().into(), - fee: XCM_FEE, - }, - amount: 1_000_000_000, - fee: XCM_FEE, - }, - }); - let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + + // Construct RegisterToken message and sent to inbound queue + send_inbound_message(make_register_token_message()).unwrap(); + + // Construct SendToken message and sent to inbound queue + send_inbound_message(make_send_token_to_penpal_message()).unwrap(); assert_expected_events!( BridgeHubRococo, @@ -299,7 +309,7 @@ fn send_token_from_ethereum_to_penpal() { AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - + // Check that the assets were issued on AssetHub assert_expected_events!( AssetHubRococo, vec![ @@ -311,7 +321,7 @@ fn send_token_from_ethereum_to_penpal() { PenpalA::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - + // Check that the assets were issued on PenPal assert_expected_events!( PenpalA, vec![ @@ -321,37 +331,25 @@ fn send_token_from_ethereum_to_penpal() { }); } +/// Tests the registering of a token as an asset on AssetHub, and then subsequently sending +/// a token from Ethereum to AssetHub. #[test] fn send_token_from_ethereum_to_asset_hub() { BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND); - // Fund ethereum sovereign in asset hub + // Fund ethereum sovereign on AssetHub AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]); - let message_id_: H256 = [1; 32].into(); - BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type EthereumInboundQueue = - ::EthereumInboundQueue; - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, - }); - let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::SendToken { - token: WETH.into(), - destination: Destination::AccountId32 { id: AssetHubRococoReceiver::get().into() }, - amount: 1_000_000_000, - fee: XCM_FEE, - }, - }); - let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + // Construct RegisterToken message and sent to inbound queue + send_inbound_message(make_register_token_message()).unwrap(); + + // Construct SendToken message and sent to inbound queue + send_inbound_message(make_send_token_message()).unwrap(); + + // Check that the message was sent assert_expected_events!( BridgeHubRococo, vec![ @@ -363,6 +361,7 @@ fn send_token_from_ethereum_to_asset_hub() { AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; + // Check that the token was received and issued as a foreign asset on AssetHub assert_expected_events!( AssetHubRococo, vec![ @@ -372,6 +371,10 @@ fn send_token_from_ethereum_to_asset_hub() { }); } +/// Tests the full cycle of token transfers: +/// - registering a token on AssetHub +/// - sending a token to AssetHub +/// - returning the token to Ethereum #[test] fn send_weth_asset_from_asset_hub_to_ethereum() { use asset_hub_rococo_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; @@ -391,31 +394,25 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]); const WETH_AMOUNT: u128 = 1_000_000_000; - let message_id_: H256 = [1; 32].into(); BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type EthereumInboundQueue = - ::EthereumInboundQueue; - - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, - }); - let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); - let message = VersionedMessage::V1(MessageV1 { - chain_id: CHAIN_ID, - command: Command::SendToken { - token: WETH.into(), - destination: Destination::AccountId32 { id: AssetHubRococoReceiver::get().into() }, - amount: WETH_AMOUNT, - fee: XCM_FEE, - }, - }); - let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + // Construct RegisterToken message and sent to inbound queue + send_inbound_message(make_register_token_message()).unwrap(); + + // Check that the register token message was sent using xcm + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + + // Construct SendToken message and sent to inbound queue + send_inbound_message(make_send_token_message()).unwrap(); + + // Check that the send token message was sent using xcm assert_expected_events!( BridgeHubRococo, vec![ @@ -428,6 +425,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { type RuntimeEvent = ::RuntimeEvent; type RuntimeOrigin = ::RuntimeOrigin; + // Check that AssetHub has issued the foreign asset assert_expected_events!( AssetHubRococo, vec![ @@ -459,6 +457,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { let free_balance_before = ::Balances::free_balance( AssetHubRococoReceiver::get(), ); + // Send the Weth back to Ethereum ::PolkadotXcm::reserve_transfer_assets( RuntimeOrigin::signed(AssetHubRococoReceiver::get()), Box::new(destination), @@ -470,14 +469,15 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { let free_balance_after = ::Balances::free_balance( AssetHubRococoReceiver::get(), ); - // assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender let free_balance_diff = free_balance_before - free_balance_after; assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get()); }); BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue assert_expected_events!( BridgeHubRococo, vec![ @@ -485,21 +485,64 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { ] ); let events = BridgeHubRococo::events(); + // Check that the local fee was credited to the Snowbridge sovereign account assert!( events.iter().any(|event| matches!( event, - RuntimeEvent::Balances(pallet_balances::Event::Deposit{ who, amount }) + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) if *who == TREASURY_ACCOUNT.into() && *amount == 16903333 )), "Snowbridge sovereign takes local fee." ); + // Check that the remote fee was credited to the AssetHub sovereign account assert!( events.iter().any(|event| matches!( event, - RuntimeEvent::Balances(pallet_balances::Event::Deposit{ who, amount }) + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) if *who == assethub_sovereign && *amount == 2680000000000, )), "AssetHub sovereign takes remote fee." ); }); } + +#[test] +fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Construct RegisterToken message and sent to inbound queue + let message = make_register_token_with_infufficient_fee_message(); + send_inbound_message(message).unwrap(); + + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {}, + ] + ); + }); +} + +#[test] +fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() { + // Insufficient fund + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), 1_000); + + BridgeHubRococo::execute_with(|| { + assert_err!(send_inbound_message(make_register_token_message()), Arithmetic(Underflow)); + }); +} 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 e9b4f8fb180016b2c9a69d5b2be3ddecb6ef6f08..9d55903c858308b9382fda0fc03132d95b9f1028 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 @@ -11,7 +11,6 @@ publish = false workspace = true [dependencies] -codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } # Substrate frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } @@ -31,10 +30,8 @@ pallet-bridge-messages = { path = "../../../../../../../bridges/modules/messages bp-messages = { path = "../../../../../../../bridges/primitives/messages", default-features = false } # Cumulus -asset-test-utils = { path = "../../../../../../parachains/runtimes/assets/test-utils" } parachains-common = { path = "../../../../../../parachains/common" } cumulus-pallet-xcmp-queue = { path = "../../../../../../pallets/xcmp-queue", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../../../pallets/dmp-queue", default-features = false } bridge-hub-westend-runtime = { path = "../../../../../../parachains/runtimes/bridge-hubs/bridge-hub-westend", default-features = false } emulated-integration-tests-common = { path = "../../../common", default-features = false } rococo-westend-system-emulated-network = { path = "../../../networks/rococo-westend-system" } 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 c2a9c008902222f9d9a1206b59b3f510147ddbea..5b0990973d2103f7fa606c4abcccd41a893067d2 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 @@ -182,13 +182,13 @@ fn send_rocs_from_asset_hub_westend_to_asset_hub_rococo() { vec![ // ROC is withdrawn from AHW's SA on AHR RuntimeEvent::Balances( - pallet_balances::Event::Withdraw { who, amount } + pallet_balances::Event::Burned { who, amount } ) => { who: *who == sov_ahw_on_ahr, amount: *amount == amount_to_send, }, // ROCs deposited to beneficiary - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == AssetHubRococoReceiver::get(), }, // message processed successfully 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 186b96b3976926b6f75995cc47e8e4c9c63b6f48..3074435e8e4e03e561388e5fb0bdf00d5b3f511f 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 @@ -67,7 +67,7 @@ pub(crate) fn assert_bridge_hub_westend_message_accepted(expected_processed: boo BridgeHubWestend, vec![ // pay for bridge fees - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { .. }) => {}, + RuntimeEvent::Balances(pallet_balances::Event::Burned { .. }) => {}, // message exported RuntimeEvent::BridgeRococoMessages( pallet_bridge_messages::Event::MessageAccepted { .. } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml index f6f1e24c550c79f3a13400dec79d55ef32f5c21f..609376c1fee606c6d3d284c0be8f5c30997acfb6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/Cargo.toml @@ -9,24 +9,19 @@ publish = false [dependencies] codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } -assert_matches = "1.5.0" # Substrate sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false } -pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false } -pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false } pallet-identity = { path = "../../../../../../../substrate/frame/identity", default-features = false } # Polkadot xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false } -pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../../polkadot/xcm/xcm-executor", default-features = false } rococo-runtime = { path = "../../../../../../../polkadot/runtime/rococo" } rococo-runtime-constants = { path = "../../../../../../../polkadot/runtime/rococo/constants" } -polkadot-primitives = { path = "../../../../../../../polkadot/primitives" } polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common" } # Cumulus @@ -34,5 +29,4 @@ asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } parachains-common = { path = "../../../../../../parachains/common" } people-rococo-runtime = { path = "../../../../../runtimes/people/people-rococo" } emulated-integration-tests-common = { path = "../../../common", default-features = false } -penpal-runtime = { path = "../../../../../runtimes/testing/penpal" } rococo-system-emulated-network = { path = "../../../networks/rococo-system" } 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 index 58bb9504dbd6bdaffc25c4f8b96424a96309d20a..87adb363e022b3c21fdfca052bb7a3019658760c 100644 --- 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 @@ -366,7 +366,7 @@ fn assert_reap_events(id_deposit: Balance, id: &Identity) { PeopleRococo, vec![ // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Deposit { .. }) => {}, + RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, // Amount reserved for identity info RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { @@ -392,7 +392,7 @@ fn assert_reap_events(id_deposit: Balance, id: &Identity) { PeopleRococo, vec![ // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Deposit { .. }) => {}, + RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, // Amount reserved for identity info RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { 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 42c7e310b2628eb97011ff36f6c8db2b3dbd130c..0a12277395d739b032d8de032dcdf9f405eaf158 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 @@ -25,12 +25,12 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { Rococo, vec![ // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { who: *who == ::XcmPallet::check_account(), amount: *amount == t.args.amount, }, @@ -51,12 +51,12 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { Rococo, vec![ // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] @@ -85,7 +85,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { PeopleRococo, vec![ // Amount is withdrawn from Sender's account - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, }, @@ -102,7 +102,7 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { PeopleRococo, vec![ // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] 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 a5d9d6c0e30ba65269c7b56cda4857f4eab4fcef..f2f3366798a0aacc77fe279efd1b47032c62c7dc 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 @@ -9,24 +9,19 @@ publish = false [dependencies] codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } -assert_matches = "1.5.0" # Substrate sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false } -pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false } -pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false } pallet-identity = { path = "../../../../../../../substrate/frame/identity", default-features = false } # Polkadot xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false } -pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../../polkadot/xcm/xcm-executor", default-features = false } westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } westend-runtime-constants = { path = "../../../../../../../polkadot/runtime/westend/constants" } -polkadot-primitives = { path = "../../../../../../../polkadot/primitives" } polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common" } # Cumulus @@ -34,5 +29,4 @@ asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" } parachains-common = { path = "../../../../../../parachains/common" } people-westend-runtime = { path = "../../../../../runtimes/people/people-westend" } emulated-integration-tests-common = { path = "../../../common", default-features = false } -penpal-runtime = { path = "../../../../../runtimes/testing/penpal" } westend-system-emulated-network = { path = "../../../networks/westend-system" } 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 index 31c3444f1a32f8f66dc81b7b04d20e3e53942b99..8d63c8ceff6efea9825cb0d009da74d2974ba40b 100644 --- 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 @@ -291,7 +291,7 @@ fn assert_reap_id_relay(total_deposit: Balance, id: &Identity) { assert_eq!(reserved_balance, total_deposit); assert_ok!(WestendIdentityMigrator::reap_identity( - WestendOrigin::root(), + WestendOrigin::signed(WestendRelaySender::get()), WestendRelaySender::get() )); @@ -368,7 +368,7 @@ fn assert_reap_events(id_deposit: Balance, id: &Identity) { PeopleWestend, vec![ // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Deposit { .. }) => {}, + RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, // Amount reserved for identity info RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { @@ -394,7 +394,7 @@ fn assert_reap_events(id_deposit: Balance, id: &Identity) { PeopleWestend, vec![ // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Deposit { .. }) => {}, + RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, // Amount reserved for identity info RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { 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 e9f0158efbf5e7c344125463991072f3fe77c02f..345663be99baa66fc3e871abc43695f8c681308f 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 @@ -25,12 +25,12 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { Westend, vec![ // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { who: *who == ::XcmPallet::check_account(), amount: *amount == t.args.amount, }, @@ -51,12 +51,12 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { Westend, vec![ // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + 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::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] @@ -85,7 +85,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { PeopleWestend, vec![ // Amount is withdrawn from Sender's account - RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => { + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { who: *who == t.sender.account_id, amount: *amount == t.args.amount, }, @@ -102,7 +102,7 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { PeopleWestend, vec![ // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Deposit { who, .. }) => { + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { who: *who == t.receiver.account_id, }, ] diff --git a/cumulus/parachains/pallets/collective-content/Cargo.toml b/cumulus/parachains/pallets/collective-content/Cargo.toml index 9ed2822fa3009e2ec014a8065e00824c49ede83e..691be02f5b8e390bf150713bcf127af1e2ce44b0 100644 --- a/cumulus/parachains/pallets/collective-content/Cargo.toml +++ b/cumulus/parachains/pallets/collective-content/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-collective-content" -version = "0.1.0" +version = "0.6.0" authors = ["Parity Technologies "] edition = "2021" description = "Managed content" diff --git a/cumulus/parachains/pallets/collective-content/src/benchmarking.rs b/cumulus/parachains/pallets/collective-content/src/benchmarking.rs index 943386a842766129c2b5d429dc3a9648249ea73f..3d6bf073778a294e39287285ddb8379d57d36c8f 100644 --- a/cumulus/parachains/pallets/collective-content/src/benchmarking.rs +++ b/cumulus/parachains/pallets/collective-content/src/benchmarking.rs @@ -16,7 +16,7 @@ //! The pallet benchmarks. use super::{Pallet as CollectiveContent, *}; -use frame_benchmarking::{impl_benchmark_test_suite, v2::*}; +use frame_benchmarking::v2::*; use frame_support::traits::EnsureOrigin; fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { diff --git a/cumulus/parachains/pallets/collective-content/src/lib.rs b/cumulus/parachains/pallets/collective-content/src/lib.rs index 7a685858accb67868837e734ba9808741a4f7ea1..b1c960ad6a0d337d6b84aaaba59f7fa0625a8124 100644 --- a/cumulus/parachains/pallets/collective-content/src/lib.rs +++ b/cumulus/parachains/pallets/collective-content/src/lib.rs @@ -59,7 +59,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_runtime::{traits::BadOrigin, Saturating}; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] diff --git a/cumulus/parachains/pallets/parachain-info/Cargo.toml b/cumulus/parachains/pallets/parachain-info/Cargo.toml index 31f7b8aef392f02d46d8eb23cb58f780b738a143..0e2f965e1cffb114729b16612b12b146ce61e0da 100644 --- a/cumulus/parachains/pallets/parachain-info/Cargo.toml +++ b/cumulus/parachains/pallets/parachain-info/Cargo.toml @@ -2,7 +2,7 @@ authors.workspace = true edition.workspace = true name = "staging-parachain-info" -version = "0.1.0" +version = "0.7.0" license = "Apache-2.0" description = "Pallet to store the parachain ID" diff --git a/cumulus/parachains/pallets/ping/Cargo.toml b/cumulus/parachains/pallets/ping/Cargo.toml index 5c1099a110a4bb54c39fef27df8ecaa01596efd9..1afd55eb0b9273d37d025eb3520c9dd8be122048 100644 --- a/cumulus/parachains/pallets/ping/Cargo.toml +++ b/cumulus/parachains/pallets/ping/Cargo.toml @@ -2,7 +2,7 @@ authors.workspace = true edition.workspace = true name = "cumulus-ping" -version = "0.1.0" +version = "0.7.0" license = "Apache-2.0" description = "Ping Pallet for Cumulus XCM/UMP testing." diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 254aca90078298c5f1481100d52ad3fd3838c5ba..3eb63a24b74e9eee4ded63cc591ec6ba8adb136b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asset-hub-rococo-runtime" -version = "0.9.420" +version = "0.11.0" authors.workspace = true edition.workspace = true description = "Rococo variant of Asset Hub parachain runtime" @@ -12,9 +12,8 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } hex-literal = { version = "0.4.1" } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -smallvec = "1.11.0" # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -64,7 +63,6 @@ primitive-types = { version = "0.12.1", default-features = false, features = ["c rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false } pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } @@ -83,6 +81,7 @@ cumulus-primitives-utility = { path = "../../../../primitives/utility", default- pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } assets-common = { path = "../common", default-features = false } # Bridges @@ -91,7 +90,7 @@ bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hu bp-asset-hub-westend = { path = "../../../../../bridges/primitives/chain-asset-hub-westend", default-features = false } bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } -snowbridge-router-primitives = { path = "../../../../../bridges/snowbridge/parachain/primitives/router", default-features = false } +snowbridge-router-primitives = { path = "../../../../../bridges/snowbridge/primitives/router", default-features = false } [dev-dependencies] asset-test-utils = { path = "../test-utils" } @@ -120,6 +119,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -131,6 +131,7 @@ runtime-benchmarks = [ "pallet-proxy/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -225,7 +226,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "primitive-types/std", @@ -247,6 +247,7 @@ std = [ "sp-version/std", "sp-weights/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", @@ -256,5 +257,5 @@ experimental = ["pallet-aura/experimental"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] 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 983e250d0cc5f7d6bec67444814e977e1f662b8c..32966ab6341d7bf4b2efae0cf008cfa6dfb67275 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -35,7 +35,6 @@ use assets_common::{ }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::AggregateMessageOrigin; -use parachains_common::rococo::snowbridge::EthereumNetwork; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -44,6 +43,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Permill, }; +use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -62,7 +62,7 @@ use frame_support::{ ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Equals, InstanceFilter, TransformOrigin, }, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight}, BoundedVec, PalletId, }; use frame_system::{ @@ -74,12 +74,11 @@ use pallet_nfts::PalletFeatures; use parachains_common::{ impls::DealWithFees, message_queue::{NarrowOriginToSibling, ParaIdToSibling}, - rococo::{consensus::*, currency::*, fee::WeightToFee}, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, - Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, - NORMAL_DISPATCH_RATIO, + Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; use sp_runtime::{Perbill, RuntimeDebug}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::{ ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, PoolAssetsConvertedConcreteId, TokenLocation, TokenLocationV3, @@ -114,7 +113,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("statemine"), impl_name: create_runtime_str!("statemine"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 14, @@ -127,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("statemine"), impl_name: create_runtime_str!("statemine"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 14, @@ -140,28 +139,6 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -/// We allow for 2 seconds of compute with a 6 second average block. -const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( - WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), - cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, -); - -/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included -/// into the relay chain. -const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; -/// How many parachain blocks are processed by the relay chain per parent. Limits the -/// number of blocks authored per slot. -const BLOCK_PROCESSING_VELOCITY: u32 = 1; - -/// This determines the average expected block time that we are targeting. -/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. -/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked -/// up by `pallet_aura` to implement `fn slot_duration()`. -/// -/// Change this to adjust the block time. -pub const MILLISECS_PER_BLOCK: u64 = 6000; -pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; - parameter_types! { pub const Version: RuntimeVersion = VERSION; pub RuntimeBlockLength: BlockLength = @@ -201,6 +178,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = frame_support::traits::ConstU32<16>; @@ -241,9 +219,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - // We allow each account to have holds on it from: - // - `NftFractionalization`: 1 - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<0>; } @@ -260,6 +235,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -409,8 +385,8 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v3::MultiLocation; - type AssetIdParameter = xcm::v3::MultiLocation; + type AssetId = xcm::v3::Location; + type AssetIdParameter = xcm::v3::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< ( @@ -787,6 +763,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { type Fungibles = LocalAndForeignAssets; type OnChargeAssetTransaction = AssetConversionAdapter; + type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetConversionTxHelper; } parameter_types! { @@ -974,8 +953,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -987,13 +966,15 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( pallet_collator_selection::migration::v1::MigrateToV1, InitStorageVersions, // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); /// Migration to initialize storage versions for pallets added after genesis. @@ -1010,37 +991,37 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if Multisig::on_chain_storage_version() == StorageVersion::new(0) { - Multisig::current_storage_version().put::(); + Multisig::in_code_storage_version().put::(); writes.saturating_inc(); } if Assets::on_chain_storage_version() == StorageVersion::new(0) { - Assets::current_storage_version().put::(); + Assets::in_code_storage_version().put::(); writes.saturating_inc(); } if Uniques::on_chain_storage_version() == StorageVersion::new(0) { - Uniques::current_storage_version().put::(); + Uniques::in_code_storage_version().put::(); writes.saturating_inc(); } if Nfts::on_chain_storage_version() == StorageVersion::new(0) { - Nfts::current_storage_version().put::(); + Nfts::in_code_storage_version().put::(); writes.saturating_inc(); } if ForeignAssets::on_chain_storage_version() == StorageVersion::new(0) { - ForeignAssets::current_storage_version().put::(); + ForeignAssets::in_code_storage_version().put::(); writes.saturating_inc(); } if PoolAssets::on_chain_storage_version() == StorageVersion::new(0) { - PoolAssets::current_storage_version().put::(); + PoolAssets::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -1059,18 +1040,78 @@ pub type Executive = frame_executive::Executive< >; #[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; +pub struct AssetConversionTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl + pallet_asset_conversion_tx_payment::BenchmarkHelperTrait< + AccountId, + xcm::v3::MultiLocation, + xcm::v3::MultiLocation, + > for AssetConversionTxHelper +{ + fn create_asset_id_parameter(seed: u32) -> (xcm::v3::MultiLocation, xcm::v3::MultiLocation) { + // Use a different parachain' foreign assets pallet so that the asset is indeed foreign. + let asset_id = xcm::v3::MultiLocation::new( + 1, + xcm::v3::Junctions::X3( + xcm::v3::Junction::Parachain(3000), + xcm::v3::Junction::PalletInstance(53), + xcm::v3::Junction::GeneralIndex(seed.into()), + ), + ); + (asset_id, asset_id) + } + + fn setup_balances_and_pool(asset_id: xcm::v3::MultiLocation, account: AccountId) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + assert_ok!(ForeignAssets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + account.clone().into(), /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = account.clone(); + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&lp_provider, u64::MAX.into()); + assert_ok!(ForeignAssets::mint_into(asset_id.into(), &lp_provider, u64::MAX.into())); + + let token_native = Box::new(TokenLocationV3::get()); + let token_second = Box::new(asset_id); + + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider.clone()), + token_native.clone(), + token_second.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider.clone()), + token_native, + token_second, + (u32::MAX / 8).into(), // 1 desired + u32::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider, + )); + } +} #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_assets, Local] [pallet_assets, Foreign] [pallet_assets, Pool] [pallet_asset_conversion, AssetConversion] + [pallet_asset_conversion_tx_payment, AssetTxPayment] [pallet_balances, Balances] + [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_nft_fractionalization, NftFractionalization] [pallet_nfts, Nfts] @@ -1079,7 +1120,9 @@ mod benches { [pallet_uniques, Uniques] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] + [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_xcm_bridge_hub_router, ToWestend] // XCM @@ -1119,7 +1162,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1328,6 +1371,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; @@ -1362,6 +1406,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -1381,8 +1426,31 @@ impl_runtime_apis! { Config as XcmBridgeHubRouterConfig, }; + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + pub const RandomParaId: ParaId = ParaId::new(43211234); + } + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >, + polkadot_runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + PriceForSiblingParachainDelivery, + RandomParaId, + ParachainSystem, + > + ); + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -1391,7 +1459,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -1399,17 +1467,13 @@ impl_runtime_apis! { } fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - // AH can reserve transfer native token to some random parachain. - let random_para_id = 43211234; - ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( - random_para_id.into() - ); Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, - ParentThen(Parachain(random_para_id).into()).into(), + // AH can reserve transfer native token to some random parachain. + ParentThen(Parachain(RandomParaId::get().into()).into()).into(), )) } @@ -1461,6 +1525,13 @@ impl_runtime_apis! { }); Some((assets, fee_index as u32, dest, verify)) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } impl XcmBridgeHubRouterConfig for Runtime { @@ -1495,13 +1566,6 @@ impl_runtime_apis! { use xcm_config::{TokenLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; - parameter_types! { - pub ExistentialDepositAsset: Option = Some(( - TokenLocation::get(), - ExistentialDeposit::get() - ).into()); - } - impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; @@ -1604,6 +1668,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } @@ -1678,6 +1749,7 @@ parameter_types! { impl pallet_state_trie_migration::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; type SignedDepositPerItem = MigrationSignedDepositPerItem; type SignedDepositBase = MigrationSignedDepositBase; // An origin that can control the whole pallet: should be Root, or a part of your council. @@ -1716,9 +1788,9 @@ fn ensure_key_ss58() { mod tests { use super::*; use crate::{CENTS, MILLICENTS}; - use parachains_common::rococo::fee; 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] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed2c5f3056e3c44601fee945ccda3eab35444e12 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --chain=asset-hub-rococo-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs index fa9e86102c619c9ff68316cae2a27a7f79fea2e6..134b5341a401ca0f0918d3f04a79d2a4d1d9622d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs @@ -19,7 +19,9 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_conversion; +pub mod pallet_asset_conversion_tx_payment; pub mod pallet_assets_foreign; pub mod pallet_assets_local; pub mod pallet_assets_pool; @@ -32,6 +34,7 @@ pub mod pallet_nfts; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion_tx_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion_tx_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..0a639b368af2248bdaf1efa7538d58a935dbe2e0 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion_tx_payment.rs @@ -0,0 +1,92 @@ +// 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_asset_conversion_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-01-04, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Georges-MacBook-Pro.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_asset_conversion_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --chain=asset-hub-rococo-dev + +#![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_asset_conversion_tx_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_conversion_tx_payment::WeightInfo for WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(10_000_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 209_000_000 picoseconds. + Weight::from_parts(212_000_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `631` + // Estimated: `7404` + // Minimum execution time: 1_228_000_000 picoseconds. + Weight::from_parts(1_268_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs index d373d0f8e651137360382a995db368db8eac1fed..299a801ebd5964829271d6575a60a123f156b7e3 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs @@ -16,28 +16,26 @@ //! Autogenerated weights for `pallet_balances` //! -//! 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-01-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("asset-hub-rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-8idpd4bs-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: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=asset-hub-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=asset-hub-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -56,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 55_040_000 picoseconds. - Weight::from_parts(56_106_000, 0) + // Minimum execution time: 42_706_000 picoseconds. + Weight::from_parts(43_378_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -68,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_342_000 picoseconds. - Weight::from_parts(41_890_000, 0) + // Minimum execution time: 33_090_000 picoseconds. + Weight::from_parts(33_703_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -80,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 14_723_000 picoseconds. - Weight::from_parts(15_182_000, 0) + // Minimum execution time: 12_678_000 picoseconds. + Weight::from_parts(13_068_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -92,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 22_073_000 picoseconds. - Weight::from_parts(22_638_000, 0) + // Minimum execution time: 17_336_000 picoseconds. + Weight::from_parts(17_824_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -104,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 57_265_000 picoseconds. - Weight::from_parts(58_222_000, 0) + // Minimum execution time: 44_817_000 picoseconds. + Weight::from_parts(45_453_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -116,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 51_485_000 picoseconds. - Weight::from_parts(52_003_000, 0) + // Minimum execution time: 41_468_000 picoseconds. + Weight::from_parts(42_093_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -128,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 17_460_000 picoseconds. - Weight::from_parts(17_849_000, 0) + // Minimum execution time: 15_344_000 picoseconds. + Weight::from_parts(15_878_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -141,13 +139,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 17_259_000 picoseconds. - Weight::from_parts(17_478_000, 0) + // Minimum execution time: 15_067_000 picoseconds. + Weight::from_parts(15_281_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 16_756 - .saturating_add(Weight::from_parts(15_291_954, 0).saturating_mul(u.into())) + // Standard Error: 11_009 + .saturating_add(Weight::from_parts(13_050_024, 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())) } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_139_000 picoseconds. + Weight::from_parts(5_511_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..035f9a6dbe5167cb651736f705b817d4ba9027c4 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --chain=asset-hub-rococo-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs index f8820bbb58cb24afe1afe034e131414368089444..51b6543bae82ba496998706ab6c2aaf6e0ff604b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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: @@ -64,8 +64,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 25_003_000 picoseconds. - Weight::from_parts(25_800_000, 0) + // Minimum execution time: 22_136_000 picoseconds. + Weight::from_parts(22_518_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +90,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 88_832_000 picoseconds. - Weight::from_parts(90_491_000, 0) + // Minimum execution time: 92_277_000 picoseconds. + Weight::from_parts(94_843_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -118,8 +118,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `400` // Estimated: `6196` - // Minimum execution time: 138_911_000 picoseconds. - Weight::from_parts(142_483_000, 0) + // Minimum execution time: 120_110_000 picoseconds. + Weight::from_parts(122_968_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) @@ -148,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `496` // Estimated: `6208` - // Minimum execution time: 146_932_000 picoseconds. - Weight::from_parts(153_200_000, 0) + // Minimum execution time: 143_116_000 picoseconds. + Weight::from_parts(147_355_000, 0) .saturating_add(Weight::from_parts(0, 6208)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) @@ -170,8 +170,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_081_000 picoseconds. - Weight::from_parts(7_397_000, 0) + // Minimum execution time: 6_517_000 picoseconds. + Weight::from_parts(6_756_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -181,8 +181,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_007_000 picoseconds. - Weight::from_parts(2_183_000, 0) + // Minimum execution time: 1_894_000 picoseconds. + Weight::from_parts(2_024_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -208,8 +208,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_790_000 picoseconds. - Weight::from_parts(29_767_000, 0) + // Minimum execution time: 27_314_000 picoseconds. + Weight::from_parts(28_787_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -234,8 +234,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 30_951_000 picoseconds. - Weight::from_parts(31_804_000, 0) + // Minimum execution time: 29_840_000 picoseconds. + Weight::from_parts(30_589_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -246,45 +246,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_164_000 picoseconds. - Weight::from_parts(2_311_000, 0) + // Minimum execution time: 1_893_000 picoseconds. + Weight::from_parts(2_017_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `162` - // Estimated: `11052` - // Minimum execution time: 16_906_000 picoseconds. - Weight::from_parts(17_612_000, 0) - .saturating_add(Weight::from_parts(0, 11052)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `159` + // Estimated: `13524` + // Minimum execution time: 19_211_000 picoseconds. + Weight::from_parts(19_552_000, 0) + .saturating_add(Weight::from_parts(0, 13524)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `11056` - // Minimum execution time: 17_443_000 picoseconds. - Weight::from_parts(18_032_000, 0) - .saturating_add(Weight::from_parts(0, 11056)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `163` + // Estimated: `13528` + // Minimum execution time: 19_177_000 picoseconds. + Weight::from_parts(19_704_000, 0) + .saturating_add(Weight::from_parts(0, 13528)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `13538` - // Minimum execution time: 18_992_000 picoseconds. - Weight::from_parts(19_464_000, 0) - .saturating_add(Weight::from_parts(0, 13538)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `16013` + // Minimum execution time: 20_449_000 picoseconds. + Weight::from_parts(21_075_000, 0) + .saturating_add(Weight::from_parts(0, 16013)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -304,36 +304,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 28_011_000 picoseconds. - Weight::from_parts(28_716_000, 0) + // Minimum execution time: 26_578_000 picoseconds. + Weight::from_parts(27_545_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `206` - // Estimated: `8621` - // Minimum execution time: 9_533_000 picoseconds. - Weight::from_parts(9_856_000, 0) - .saturating_add(Weight::from_parts(0, 8621)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11096` + // Minimum execution time: 11_646_000 picoseconds. + Weight::from_parts(11_944_000, 0) + .saturating_add(Weight::from_parts(0, 11096)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `173` - // Estimated: `11063` - // Minimum execution time: 17_628_000 picoseconds. - Weight::from_parts(18_146_000, 0) - .saturating_add(Weight::from_parts(0, 11063)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `170` + // Estimated: `13535` + // Minimum execution time: 19_301_000 picoseconds. + Weight::from_parts(19_664_000, 0) + .saturating_add(Weight::from_parts(0, 13535)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -349,12 +349,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `11105` - // Minimum execution time: 34_877_000 picoseconds. - Weight::from_parts(35_607_000, 0) - .saturating_add(Weight::from_parts(0, 11105)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `212` + // Estimated: `13577` + // Minimum execution time: 35_715_000 picoseconds. + Weight::from_parts(36_915_000, 0) + .saturating_add(Weight::from_parts(0, 13577)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -365,8 +365,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 5_370_000 picoseconds. - Weight::from_parts(5_616_000, 0) + // Minimum execution time: 4_871_000 picoseconds. + Weight::from_parts(5_066_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -377,10 +377,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 26_820_000 picoseconds. - Weight::from_parts(27_143_000, 0) + // Minimum execution time: 25_150_000 picoseconds. + Weight::from_parts(26_119_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `3625` + // Minimum execution time: 38_248_000 picoseconds. + Weight::from_parts(39_122_000, 0) + .saturating_add(Weight::from_parts(0, 3625)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } 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 9e1affb533a3df57c562e07e66e77f18e5c82543..2584dbdf31062f4c75f03714ce803d234ced0be9 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,6 @@ use super::{ XcmpQueue, }; use assets_common::{ - local_and_foreign_assets::MatchesLocalAndForeignAssetsLocation, matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset}, TrustBackedAssetsAsLocation, }; @@ -36,7 +35,6 @@ use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, - rococo::snowbridge::EthereumNetwork, xcm_config::{ AllSiblingSystemParachains, AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains, @@ -47,20 +45,22 @@ 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 testnet_parachains_constants::rococo::snowbridge::{ + EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, +}; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -113,8 +113,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -222,43 +221,13 @@ pub type PoolFungiblesTransactor = FungiblesAdapter< /// Means for transacting assets on this chain. pub type AssetTransactors = ( - CurrencyTransactor, + FungibleTransactor, FungiblesTransactor, ForeignFungiblesTransactor, PoolFungiblesTransactor, UniquesTransactor, ); -/// Simple `Location` matcher for Local and Foreign asset `Location`. -pub struct LocalAndForeignAssetsLocationMatcher; -impl MatchesLocalAndForeignAssetsLocation - for LocalAndForeignAssetsLocationMatcher -{ - fn is_local(location: &xcm::v3::Location) -> bool { - use assets_common::fungible_conversion::MatchesLocation; - let latest_location: Location = if let Ok(location) = (*location).try_into() { - location - } else { - return false; - }; - TrustBackedAssetsConvertedConcreteId::contains(&latest_location) - } - fn is_foreign(location: &xcm::v3::Location) -> bool { - use assets_common::fungible_conversion::MatchesLocation; - let latest_location: Location = if let Ok(location) = (*location).try_into() { - location - } else { - return false; - }; - ForeignAssetsConvertedConcreteId::contains(&latest_location) - } -} -impl Contains for LocalAndForeignAssetsLocationMatcher { - fn contains(location: &xcm::v3::Location) -> bool { - Self::is_local(location) || Self::is_foreign(location) - } -} - /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can /// biases the kind of local `Origin` it will become. @@ -658,6 +627,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM location. @@ -880,7 +850,7 @@ pub mod bridging { 1, [ Parachain(SiblingBridgeHubParaId::get()), - PalletInstance(parachains_common::rococo::snowbridge::INBOUND_QUEUE_PALLET_INDEX) + PalletInstance(INBOUND_QUEUE_PALLET_INDEX) ] ); 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 e7ac37b2d5c952d62c6466c914956cd327f5b294..38c118dbb4b25fe2e345e9247a2a253d0becd613 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -22,13 +22,13 @@ use asset_hub_rococo_runtime::{ xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, TokenLocation, TokenLocationV3, TrustBackedAssetsPalletLocation, - TrustBackedAssetsPalletLocationV3, XcmConfig, + LocationToAccountId, StakingPot, TokenLocation, TokenLocationV3, + TrustBackedAssetsPalletLocation, TrustBackedAssetsPalletLocationV3, XcmConfig, }, AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, - MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, - ToWestendXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, SLOT_DURATION, + MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + SessionKeys, ToWestendXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, }; use asset_test_utils::{ test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, @@ -46,13 +46,12 @@ use frame_support::{ }, weights::{Weight, WeightToFee as WeightToFeeT}, }; -use parachains_common::{ - rococo::{consensus::RELAY_CHAIN_SLOT_DURATION_MILLIS, currency::UNITS, fee::WeightToFee}, - AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, -}; +use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use sp_consensus_aura::SlotDuration; use sp_runtime::traits::MaybeEquivalence; +use sp_std::ops::Mul; use std::convert::Into; +use testnet_parachains_constants::rococo::{consensus::*, currency::UNITS, fee::WeightToFee}; use xcm::latest::prelude::{Assets as XcmAssets, *}; use xcm_builder::V4V3LocationConverter; use xcm_executor::traits::{JustTry, WeightTrader}; @@ -87,6 +86,52 @@ 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, + Balance, + ), +) { + let existential_deposit = ExistentialDeposit::get(); + + // 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 pool_liquidity: Balance = + existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); + + let _ = Balances::force_set_balance( + RuntimeOrigin::root(), + pool_owner.clone().into(), + (existential_deposit + pool_liquidity).mul(2).into(), + ); + + assert_ok!(ForeignAssets::mint( + RuntimeOrigin::signed(foreign_asset_owner), + foreign_asset_id_location.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()) + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(pool_owner.clone()), + Box::new(native_asset.into()), + Box::new(foreign_asset_id_location.into()), + pool_liquidity, + pool_liquidity, + 1, + 1, + pool_owner, + )); +} + #[test] fn test_buy_and_refund_weight_in_native() { ExtBuilder::::default() @@ -1058,7 +1103,8 @@ fn limited_reserve_transfer_assets_for_native_asset_over_bridge_works( mod asset_hub_rococo_tests { use super::*; - use asset_hub_rococo_runtime::{PolkadotXcm, RuntimeOrigin}; + use asset_hub_rococo_runtime::PolkadotXcm; + use xcm_executor::traits::ConvertLocation; fn bridging_to_asset_hub_westend() -> TestBridgingConfig { let _ = PolkadotXcm::force_xcm_version( @@ -1083,27 +1129,135 @@ mod asset_hub_rococo_tests { } #[test] - fn receive_reserve_asset_deposited_wnd_from_asset_hub_westend_works() { + fn receive_reserve_asset_deposited_wnd_from_asset_hub_westend_fees_paid_by_pool_swap_works() { const BLOCK_AUTHOR_ACCOUNT: [u8; 32] = [13; 32]; + 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_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); + asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, - LocationToAccountId, ForeignAssetsInstance, >( collator_session_keys().add(collator_session_key(BLOCK_AUTHOR_ACCOUNT)), ExistentialDeposit::get(), AccountId::from([73; 32]), - AccountId::from(BLOCK_AUTHOR_ACCOUNT), + block_author_account, // receiving WNDs - (xcm::v3::Location::new(2, [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Westend)]), 1000000000000, 1_000_000_000), + foreign_asset_create_params.clone(), + 1000000000000, + || { + // setup pool for paying fees to touch `SwapFirstAssetTrader` + setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params); + // staking pot account for collecting local native fees from `BuyExecution` + let _ = Balances::force_set_balance(RuntimeOrigin::root(), StakingPot::get().into(), ExistentialDeposit::get()); + // prepare bridge configuration + bridging_to_asset_hub_westend() + }, + ( + [PalletInstance(bp_bridge_hub_rococo::WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX)].into(), + GlobalConsensus(Westend), + [Parachain(1000)].into() + ), + || { + // check staking pot for ED + assert_eq!(Balances::free_balance(&staking_pot), ExistentialDeposit::get()); + // check now foreign asset for staking pot + assert_eq!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &staking_pot + ), + 0 + ); + }, + || { + // `SwapFirstAssetTrader` - staking pot receives xcm fees in ROCs + assert!( + Balances::free_balance(&staking_pot) > ExistentialDeposit::get() + ); + // staking pot receives no foreign assets + assert_eq!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &staking_pot + ), + 0 + ); + } + ) + } + + #[test] + fn receive_reserve_asset_deposited_wnd_from_asset_hub_westend_fees_paid_by_sufficient_asset_works( + ) { + const BLOCK_AUTHOR_ACCOUNT: [u8; 32] = [13; 32]; + 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_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); + + asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< + Runtime, + AllPalletsWithoutSystem, + XcmConfig, + ForeignAssetsInstance, + >( + collator_session_keys().add(collator_session_key(BLOCK_AUTHOR_ACCOUNT)), + ExistentialDeposit::get(), + AccountId::from([73; 32]), + block_author_account.clone(), + // receiving WNDs + foreign_asset_create_params, + 1000000000000, bridging_to_asset_hub_westend, ( [PalletInstance(bp_bridge_hub_rococo::WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX)].into(), GlobalConsensus(Westend), [Parachain(1000)].into() - ) + ), + || { + // check block author before + assert_eq!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &block_author_account + ), + 0 + ); + }, + || { + // `TakeFirstAssetTrader` puts fees to the block author + assert!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &block_author_account + ) > 0 + ); + // `SwapFirstAssetTrader` did not work + assert_eq!(Balances::free_balance(&staking_pot), 0); + } ) } @@ -1263,6 +1417,12 @@ fn change_xcm_bridge_hub_router_base_fee_by_governance_works() { 1000, Box::new(|call| RuntimeCall::System(call).encode()), || { + log::error!( + target: "bridges::estimate", + "`bridging::XcmBridgeHubRouterBaseFee` actual value: {} for runtime: {}", + bridging::XcmBridgeHubRouterBaseFee::get(), + ::Version::get(), + ); ( bridging::XcmBridgeHubRouterBaseFee::key().to_vec(), bridging::XcmBridgeHubRouterBaseFee::get(), @@ -1289,6 +1449,12 @@ fn change_xcm_bridge_hub_ethereum_base_fee_by_governance_works() { 1000, Box::new(|call| RuntimeCall::System(call).encode()), || { + log::error!( + target: "bridges::estimate", + "`bridging::BridgeHubEthereumBaseFee` actual value: {} for runtime: {}", + bridging::to_ethereum::BridgeHubEthereumBaseFee::get(), + ::Version::get(), + ); ( bridging::to_ethereum::BridgeHubEthereumBaseFee::key().to_vec(), bridging::to_ethereum::BridgeHubEthereumBaseFee::get(), diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 1a1ed0465a34ee62bba6c2921df08dee27cd56f2..c10e02bd8a6c54d36969ca1f259892ce685b2665 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asset-hub-westend-runtime" -version = "0.9.420" +version = "0.15.0" authors.workspace = true edition.workspace = true description = "Westend variant of Asset Hub parachain runtime" @@ -12,9 +12,8 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } hex-literal = { version = "0.4.1", optional = true } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -smallvec = "1.11.0" # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -60,7 +59,6 @@ primitive-types = { version = "0.12.1", default-features = false, features = ["c # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } westend-runtime-constants = { path = "../../../../../polkadot/runtime/westend/constants", default-features = false } @@ -75,11 +73,13 @@ cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false, features = ["bridging"] } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["westend"] } assets-common = { path = "../common", default-features = false } # Bridges @@ -110,6 +110,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "hex-literal", + "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -120,6 +121,7 @@ runtime-benchmarks = [ "pallet-nfts/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -176,6 +178,7 @@ std = [ "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", @@ -210,7 +213,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "primitive-types/std", @@ -229,6 +231,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", @@ -239,5 +242,5 @@ experimental = ["pallet-aura/experimental"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] 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 e642475698ef4a37b8e274d3e85f20335c3fdba1..5246828da310ad51e167756b0f12fe814acc6fff 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -32,7 +32,7 @@ use assets_common::{ AssetIdForTrustBackedAssetsConvert, }; use codec::{Decode, Encode, MaxEncodedLen}; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ construct_runtime, derive_impl, @@ -56,12 +56,9 @@ use pallet_asset_conversion_tx_payment::AssetConversionAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; use pallet_xcm::EnsureXcm; use parachains_common::{ - impls::DealWithFees, - message_queue::*, - westend::{consensus::*, currency::*, fee::WeightToFee}, - AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, - Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, - MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, + BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, + NORMAL_DISPATCH_RATIO, }; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; @@ -75,6 +72,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::{ ForeignAssetsConvertedConcreteId, PoolAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocationV3, WestendLocation, @@ -112,7 +110,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westmint"), impl_name: create_runtime_str!("westmint"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 14, @@ -164,6 +162,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = frame_support::traits::ConstU32<16>; @@ -173,6 +172,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -201,9 +203,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - // We allow each account to have holds on it from: - // - `NftFractionalization`: 1 - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<0>; } @@ -220,6 +219,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -609,15 +609,17 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type OutboundXcmpMessageSource = XcmpQueue; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + impl parachain_info::Config for Runtime {} parameter_types! { @@ -702,9 +704,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -735,6 +737,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { type Fungibles = LocalAndForeignAssets; type OnChargeAssetTransaction = AssetConversionAdapter; + type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetConversionTxHelper; } parameter_types! { @@ -920,8 +925,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -933,7 +938,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -949,6 +954,8 @@ pub type Migrations = ( DeleteUndecodableStorage, // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); /// Asset Hub Westend has some undecodable storage, delete it. @@ -985,7 +992,7 @@ impl frame_support::traits::OnRuntimeUpgrade for DeleteUndecodableStorage { }, Err(e) => { log::error!("Failed to destroy undecodable NFT item: {:?}", e); - return ::DbWeight::get().reads_writes(0, writes) + return ::DbWeight::get().reads_writes(0, writes); }, } @@ -997,7 +1004,7 @@ impl frame_support::traits::OnRuntimeUpgrade for DeleteUndecodableStorage { }, Err(e) => { log::error!("Failed to destroy undecodable NFT item: {:?}", e); - return ::DbWeight::get().reads_writes(0, writes) + return ::DbWeight::get().reads_writes(0, writes); }, } @@ -1035,17 +1042,17 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if ForeignAssets::on_chain_storage_version() == StorageVersion::new(0) { - ForeignAssets::current_storage_version().put::(); + ForeignAssets::in_code_storage_version().put::(); writes.saturating_inc(); } if PoolAssets::on_chain_storage_version() == StorageVersion::new(0) { - PoolAssets::current_storage_version().put::(); + PoolAssets::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -1063,14 +1070,77 @@ pub type Executive = frame_executive::Executive< Migrations, >; +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetConversionTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl + pallet_asset_conversion_tx_payment::BenchmarkHelperTrait< + AccountId, + xcm::v3::MultiLocation, + xcm::v3::MultiLocation, + > for AssetConversionTxHelper +{ + fn create_asset_id_parameter(seed: u32) -> (xcm::v3::MultiLocation, xcm::v3::MultiLocation) { + // Use a different parachain' foreign assets pallet so that the asset is indeed foreign. + let asset_id = xcm::v3::MultiLocation::new( + 1, + xcm::v3::Junctions::X3( + xcm::v3::Junction::Parachain(3000), + xcm::v3::Junction::PalletInstance(53), + xcm::v3::Junction::GeneralIndex(seed.into()), + ), + ); + (asset_id, asset_id) + } + + fn setup_balances_and_pool(asset_id: xcm::v3::MultiLocation, account: AccountId) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + assert_ok!(ForeignAssets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + account.clone().into(), /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = account.clone(); + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&lp_provider, u64::MAX.into()); + assert_ok!(ForeignAssets::mint_into(asset_id.into(), &lp_provider, u64::MAX.into())); + + let token_native = Box::new(xcm::v3::MultiLocation::new(1, xcm::v3::Junctions::Here)); + let token_second = Box::new(asset_id); + + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider.clone()), + token_native.clone(), + token_second.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider.clone()), + token_native, + token_second, + (u32::MAX / 2).into(), // 1 desired + u32::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider, + )); + } +} + #[cfg(feature = "runtime-benchmarks")] mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_assets, Local] [pallet_assets, Foreign] [pallet_assets, Pool] [pallet_asset_conversion, AssetConversion] + [pallet_asset_conversion_tx_payment, AssetTxPayment] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] @@ -1081,7 +1151,9 @@ mod benches { [pallet_uniques, Uniques] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] + [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_xcm_bridge_hub_router, ToRococo] // XCM @@ -1095,7 +1167,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -1103,6 +1175,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -1112,7 +1193,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1367,6 +1448,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; @@ -1401,6 +1483,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -1415,8 +1498,31 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + WestendLocation::get(), + ExistentialDeposit::get() + ).into()); + pub const RandomParaId: ParaId = ParaId::new(43211234); + } + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >, + polkadot_runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + PriceForSiblingParachainDelivery, + RandomParaId, + ParachainSystem, + > + ); + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -1425,7 +1531,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -1433,17 +1539,13 @@ impl_runtime_apis! { } fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - // AH can reserve transfer native token to some random parachain. - let random_para_id = 43211234; - ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( - random_para_id.into() - ); Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, - ParentThen(Parachain(random_para_id).into()).into(), + // AH can reserve transfer native token to some random parachain. + ParentThen(Parachain(RandomParaId::get().into()).into()).into(), )) } @@ -1495,6 +1597,13 @@ impl_runtime_apis! { }); Some((assets, fee_index as u32, dest, verify)) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use pallet_xcm_bridge_hub_router::benchmarking::{ @@ -1534,13 +1643,6 @@ impl_runtime_apis! { use xcm_config::{MaxAssetsIntoHolding, WestendLocation}; use pallet_xcm_benchmarks::asset_instance_from; - parameter_types! { - pub ExistentialDepositAsset: Option = Some(( - WestendLocation::get(), - ExistentialDeposit::get() - ).into()); - } - impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; @@ -1643,6 +1745,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(WestendLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..46f4f2bfd9668ce7316bf1a4a7a0c71833fd7f7d --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --chain=asset-hub-westend-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_206_000 picoseconds. + Weight::from_parts(6_212_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_851_000 picoseconds. + Weight::from_parts(8_847_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 631_000 picoseconds. + Weight::from_parts(3_086_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_446_000 picoseconds. + Weight::from_parts(5_911_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 481_000 picoseconds. + Weight::from_parts(2_916_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_927_000 picoseconds. + Weight::from_parts(6_613_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs index 2f1fcfb05f39151e018d74e8587faa0e79afd8b6..691ed2e57304a1829de76b030a4ef5ed4b3f436f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs @@ -18,7 +18,9 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_conversion; +pub mod pallet_asset_conversion_tx_payment; pub mod pallet_assets_foreign; pub mod pallet_assets_local; pub mod pallet_assets_pool; @@ -31,6 +33,7 @@ pub mod pallet_nfts; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion_tx_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion_tx_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..8fe302630fb9510d0e1c01ca9de37a1bd0be3a4f --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion_tx_payment.rs @@ -0,0 +1,92 @@ +// 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_asset_conversion_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-01-04, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Georges-MacBook-Pro.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_asset_conversion_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --chain=asset-hub-westend-dev + +#![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_asset_conversion_tx_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_conversion_tx_payment::WeightInfo for WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 214_000_000 picoseconds. + Weight::from_parts(219_000_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `631` + // Estimated: `7404` + // Minimum execution time: 1_211_000_000 picoseconds. + Weight::from_parts(1_243_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.rs index c98ac75ff662c35deeda1e98eed7b99b5fa525ef..68aceca14c1589825e588e4a5491529120da01f5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_balances.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_balances` //! -//! 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-01-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("asset-hub-westend-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-8idpd4bs-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: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=asset-hub-westend-dev -// --wasm-execution=compiled -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=asset-hub-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -55,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 54_422_000 picoseconds. - Weight::from_parts(55_477_000, 0) + // Minimum execution time: 43_122_000 picoseconds. + Weight::from_parts(43_640_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -67,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 39_850_000 picoseconds. - Weight::from_parts(41_026_000, 0) + // Minimum execution time: 33_636_000 picoseconds. + Weight::from_parts(34_571_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -79,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 14_554_000 picoseconds. - Weight::from_parts(14_800_000, 0) + // Minimum execution time: 12_101_000 picoseconds. + Weight::from_parts(12_511_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -91,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 21_586_000 picoseconds. - Weight::from_parts(22_297_000, 0) + // Minimum execution time: 17_077_000 picoseconds. + Weight::from_parts(17_362_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -103,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 57_042_000 picoseconds. - Weight::from_parts(58_251_000, 0) + // Minimum execution time: 44_352_000 picoseconds. + Weight::from_parts(45_045_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -115,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 51_587_000 picoseconds. - Weight::from_parts(52_275_000, 0) + // Minimum execution time: 41_836_000 picoseconds. + Weight::from_parts(43_201_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -127,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 17_201_000 picoseconds. - Weight::from_parts(17_613_000, 0) + // Minimum execution time: 14_413_000 picoseconds. + Weight::from_parts(14_743_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -140,13 +139,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 16_608_000 picoseconds. - Weight::from_parts(16_808_000, 0) + // Minimum execution time: 14_542_000 picoseconds. + Weight::from_parts(14_731_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 15_291 - .saturating_add(Weight::from_parts(15_154_407, 0).saturating_mul(u.into())) + // Standard Error: 11_213 + .saturating_add(Weight::from_parts(13_160_721, 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())) } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_208_000 picoseconds. + Weight::from_parts(5_619_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..b4c78a78b489073648156133730bd39cd3c68497 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --chain=asset-hub-westend-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 40_847_000 picoseconds. + Weight::from_parts(49_674_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs index 504731f4a9ef743e62090582901a63f7aee78829..71facff87d35f350114850653acfbebeb5c29529 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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: @@ -64,8 +64,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 25_482_000 picoseconds. - Weight::from_parts(26_622_000, 0) + // Minimum execution time: 21_630_000 picoseconds. + Weight::from_parts(22_306_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +90,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 87_319_000 picoseconds. - Weight::from_parts(89_764_000, 0) + // Minimum execution time: 91_802_000 picoseconds. + Weight::from_parts(93_672_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -118,8 +118,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `367` // Estimated: `6196` - // Minimum execution time: 139_133_000 picoseconds. - Weight::from_parts(141_507_000, 0) + // Minimum execution time: 118_930_000 picoseconds. + Weight::from_parts(122_306_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) @@ -148,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `496` // Estimated: `6208` - // Minimum execution time: 144_241_000 picoseconds. - Weight::from_parts(149_709_000, 0) + // Minimum execution time: 140_527_000 picoseconds. + Weight::from_parts(144_501_000, 0) .saturating_add(Weight::from_parts(0, 6208)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) @@ -158,8 +158,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_392_000 picoseconds. - Weight::from_parts(10_779_000, 0) + // Minimum execution time: 7_556_000 picoseconds. + Weight::from_parts(7_798_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) @@ -168,8 +168,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_088_000 picoseconds. - Weight::from_parts(7_257_000, 0) + // Minimum execution time: 6_373_000 picoseconds. + Weight::from_parts(6_603_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +179,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_095_000 picoseconds. - Weight::from_parts(2_136_000, 0) + // Minimum execution time: 1_941_000 picoseconds. + Weight::from_parts(2_088_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -206,8 +206,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_728_000 picoseconds. - Weight::from_parts(29_349_000, 0) + // Minimum execution time: 27_080_000 picoseconds. + Weight::from_parts(27_820_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -232,8 +232,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 30_605_000 picoseconds. - Weight::from_parts(31_477_000, 0) + // Minimum execution time: 28_850_000 picoseconds. + Weight::from_parts(29_506_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -244,45 +244,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_137_000 picoseconds. - Weight::from_parts(2_303_000, 0) + // Minimum execution time: 2_033_000 picoseconds. + Weight::from_parts(2_201_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `162` - // Estimated: `11052` - // Minimum execution time: 16_719_000 picoseconds. - Weight::from_parts(17_329_000, 0) - .saturating_add(Weight::from_parts(0, 11052)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `159` + // Estimated: `13524` + // Minimum execution time: 18_844_000 picoseconds. + Weight::from_parts(19_197_000, 0) + .saturating_add(Weight::from_parts(0, 13524)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `11056` - // Minimum execution time: 16_687_000 picoseconds. - Weight::from_parts(17_405_000, 0) - .saturating_add(Weight::from_parts(0, 11056)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `163` + // Estimated: `13528` + // Minimum execution time: 18_940_000 picoseconds. + Weight::from_parts(19_450_000, 0) + .saturating_add(Weight::from_parts(0, 13528)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `13538` - // Minimum execution time: 18_751_000 picoseconds. - Weight::from_parts(19_130_000, 0) - .saturating_add(Weight::from_parts(0, 13538)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `16013` + // Minimum execution time: 20_521_000 picoseconds. + Weight::from_parts(21_076_000, 0) + .saturating_add(Weight::from_parts(0, 16013)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -302,36 +302,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 27_189_000 picoseconds. - Weight::from_parts(27_760_000, 0) + // Minimum execution time: 26_007_000 picoseconds. + Weight::from_parts(26_448_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `206` - // Estimated: `8621` - // Minimum execution time: 9_307_000 picoseconds. - Weight::from_parts(9_691_000, 0) - .saturating_add(Weight::from_parts(0, 8621)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11096` + // Minimum execution time: 11_584_000 picoseconds. + Weight::from_parts(12_080_000, 0) + .saturating_add(Weight::from_parts(0, 11096)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `173` - // Estimated: `11063` - // Minimum execution time: 17_607_000 picoseconds. - Weight::from_parts(18_090_000, 0) - .saturating_add(Weight::from_parts(0, 11063)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `170` + // Estimated: `13535` + // Minimum execution time: 19_157_000 picoseconds. + Weight::from_parts(19_513_000, 0) + .saturating_add(Weight::from_parts(0, 13535)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -347,12 +347,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `11105` - // Minimum execution time: 34_322_000 picoseconds. - Weight::from_parts(35_754_000, 0) - .saturating_add(Weight::from_parts(0, 11105)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `212` + // Estimated: `13577` + // Minimum execution time: 34_878_000 picoseconds. + Weight::from_parts(35_623_000, 0) + .saturating_add(Weight::from_parts(0, 13577)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -363,8 +363,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 4_513_000 picoseconds. - Weight::from_parts(4_754_000, 0) + // Minimum execution time: 3_900_000 picoseconds. + Weight::from_parts(4_161_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -375,10 +375,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 27_860_000 picoseconds. - Weight::from_parts(28_279_000, 0) + // Minimum execution time: 25_731_000 picoseconds. + Weight::from_parts(26_160_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `3625` + // Minimum execution time: 37_251_000 picoseconds. + Weight::from_parts(38_075_000, 0) + .saturating_add(Weight::from_parts(0, 3625)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } 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 0f4ce360ee4860bdca1056e7320352b5fdf20229..50865c0006117ac619a757fc48ce5cf9db4516b9 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,6 @@ use super::{ XcmpQueue, }; use assets_common::{ - local_and_foreign_assets::MatchesLocalAndForeignAssetsLocation, matching::{FromSiblingParachain, IsForeignConcreteAsset}, TrustBackedAssetsAsLocation, }; @@ -46,12 +45,11 @@ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeFamily, DescribePalletTerminal, EnsureXcmOrigin, FungiblesAdapter, + DenyThenTry, DescribeFamily, DescribePalletTerminal, EnsureXcmOrigin, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, @@ -108,8 +106,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -217,44 +214,13 @@ pub type PoolFungiblesTransactor = FungiblesAdapter< /// Means for transacting assets on this chain. pub type AssetTransactors = ( - CurrencyTransactor, + FungibleTransactor, FungiblesTransactor, ForeignFungiblesTransactor, PoolFungiblesTransactor, UniquesTransactor, ); -/// Simple `Location` matcher for Local and Foreign asset `Location`. -pub struct LocalAndForeignAssetsLocationMatcher; -impl MatchesLocalAndForeignAssetsLocation - for LocalAndForeignAssetsLocationMatcher -{ - fn is_local(location: &xcm::v3::Location) -> bool { - use assets_common::fungible_conversion::MatchesLocation; - let latest_location: Location = if let Ok(location) = (*location).try_into() { - location - } else { - return false; - }; - TrustBackedAssetsConvertedConcreteId::contains(&latest_location) - } - - fn is_foreign(location: &xcm::v3::Location) -> bool { - use assets_common::fungible_conversion::MatchesLocation; - let latest_location: Location = if let Ok(location) = (*location).try_into() { - location - } else { - return false; - }; - ForeignAssetsConvertedConcreteId::contains(&latest_location) - } -} -impl Contains for LocalAndForeignAssetsLocationMatcher { - fn contains(location: &xcm::v3::Location) -> bool { - Self::is_local(location) || Self::is_foreign(location) - } -} - /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can /// biases the kind of local `Origin` it will become. @@ -682,6 +648,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. 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 381b2867c9c655ba1259bcf3c21d8f0e333e08e0..aa8c3cf2f14db33e119a355bff2e61ccc9a2c23d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -22,8 +22,8 @@ use asset_hub_westend_runtime::{ xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, TrustBackedAssetsPalletLocation, TrustBackedAssetsPalletLocationV3, - WestendLocation, WestendLocationV3, XcmConfig, + LocationToAccountId, StakingPot, TrustBackedAssetsPalletLocation, + TrustBackedAssetsPalletLocationV3, WestendLocation, WestendLocationV3, XcmConfig, }, AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, @@ -47,16 +47,14 @@ use frame_support::{ }, weights::{Weight, WeightToFee as WeightToFeeT}, }; -use parachains_common::{ - westend::{consensus::RELAY_CHAIN_SLOT_DURATION_MILLIS, currency::UNITS, fee::WeightToFee}, - AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, SLOT_DURATION, -}; +use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use sp_consensus_aura::SlotDuration; use sp_runtime::traits::MaybeEquivalence; -use std::convert::Into; +use std::{convert::Into, ops::Mul}; +use testnet_parachains_constants::westend::{consensus::*, currency::UNITS, fee::WeightToFee}; use xcm::latest::prelude::{Assets as XcmAssets, *}; use xcm_builder::V4V3LocationConverter; -use xcm_executor::traits::{JustTry, WeightTrader}; +use xcm_executor::traits::{ConvertLocation, JustTry, WeightTrader}; const ALICE: [u8; 32] = [1u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; @@ -88,6 +86,52 @@ 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, + Balance, + ), +) { + let existential_deposit = ExistentialDeposit::get(); + + // 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 pool_liquidity: Balance = + existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); + + let _ = Balances::force_set_balance( + RuntimeOrigin::root(), + pool_owner.clone().into(), + (existential_deposit + pool_liquidity).mul(2).into(), + ); + + assert_ok!(ForeignAssets::mint( + RuntimeOrigin::signed(foreign_asset_owner), + foreign_asset_id_location.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()) + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(pool_owner.clone()), + Box::new(native_asset.into()), + Box::new(foreign_asset_id_location.into()), + pool_liquidity, + pool_liquidity, + 1, + 1, + pool_owner, + )); +} + #[test] fn test_buy_and_refund_weight_in_native() { ExtBuilder::::default() @@ -1073,30 +1117,133 @@ fn limited_reserve_transfer_assets_for_native_asset_to_asset_hub_rococo_works() Some(xcm_config::TreasuryAccount::get()), ) } + #[test] -fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_works() { +fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_swap_works() { const BLOCK_AUTHOR_ACCOUNT: [u8; 32] = [13; 32]; + 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::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); + asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, - LocationToAccountId, ForeignAssetsInstance, >( collator_session_keys().add(collator_session_key(BLOCK_AUTHOR_ACCOUNT)), ExistentialDeposit::get(), AccountId::from([73; 32]), - AccountId::from(BLOCK_AUTHOR_ACCOUNT), + block_author_account.clone(), // receiving ROCs - (xcm::v3::Location::new(2, [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Rococo)]), 1000000000000, 1_000_000_000), - bridging_to_asset_hub_rococo, + foreign_asset_create_params.clone(), + 1000000000000, + || { + // setup pool for paying fees to touch `SwapFirstAssetTrader` + setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params); + // staking pot account for collecting local native fees from `BuyExecution` + let _ = Balances::force_set_balance(RuntimeOrigin::root(), StakingPot::get().into(), ExistentialDeposit::get()); + // prepare bridge configuration + bridging_to_asset_hub_rococo() + }, ( [PalletInstance(bp_bridge_hub_westend::WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX)].into(), GlobalConsensus(Rococo), [Parachain(1000)].into() - ) + ), + || { + // check staking pot for ED + assert_eq!(Balances::free_balance(&staking_pot), ExistentialDeposit::get()); + // check now foreign asset for staking pot + assert_eq!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &staking_pot + ), + 0 + ); + }, + || { + // `SwapFirstAssetTrader` - staking pot receives xcm fees in ROCs + assert!( + Balances::free_balance(&staking_pot) > ExistentialDeposit::get() + ); + // staking pot receives no foreign assets + assert_eq!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &staking_pot + ), + 0 + ); + } ) } + +#[test] +fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_sufficient_asset_works() { + const BLOCK_AUTHOR_ACCOUNT: [u8; 32] = [13; 32]; + 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::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); + + asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< + Runtime, + AllPalletsWithoutSystem, + XcmConfig, + ForeignAssetsInstance, + >( + collator_session_keys().add(collator_session_key(BLOCK_AUTHOR_ACCOUNT)), + ExistentialDeposit::get(), + AccountId::from([73; 32]), + block_author_account.clone(), + // receiving ROCs + foreign_asset_create_params, + 1000000000000, + bridging_to_asset_hub_rococo, + ( + [PalletInstance(bp_bridge_hub_westend::WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX)].into(), + GlobalConsensus(Rococo), + [Parachain(1000)].into() + ), + || { + // check block author before + assert_eq!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &block_author_account + ), + 0 + ); + }, + || { + // `TakeFirstAssetTrader` puts fees to the block author + assert!( + ForeignAssets::balance( + foreign_asset_id_location.into(), + &block_author_account + ) > 0 + ); + // `SwapFirstAssetTrader` did not work + assert_eq!(Balances::free_balance(&staking_pot), 0); + } + ) +} + #[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::< @@ -1209,6 +1356,38 @@ fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { ) } +#[test] +fn change_xcm_bridge_hub_router_base_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::XcmBridgeHubRouterBaseFee, + Balance, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + log::error!( + target: "bridges::estimate", + "`bridging::XcmBridgeHubRouterBaseFee` actual value: {} for runtime: {}", + bridging::XcmBridgeHubRouterBaseFee::get(), + ::Version::get(), + ); + ( + bridging::XcmBridgeHubRouterBaseFee::key().to_vec(), + bridging::XcmBridgeHubRouterBaseFee::get(), + ) + }, + |old_value| { + if let Some(new_value) = old_value.checked_add(1) { + new_value + } else { + old_value.checked_sub(1).unwrap() + } + }, + ) +} + #[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/common/Cargo.toml b/cumulus/parachains/runtimes/assets/common/Cargo.toml index 22729df5ed5ca845d7a554758e3f9e3b7db8aaec..c9252375cfbf019c27a1ce0d5dc38f11db30cac5 100644 --- a/cumulus/parachains/runtimes/assets/common/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "assets-common" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Assets common utilities" @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } impl-trait-for-tuples = "0.2.2" # Substrate @@ -21,7 +21,6 @@ sp-api = { path = "../../../../../substrate/primitives/api", default-features = sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } pallet-asset-conversion = { path = "../../../../../substrate/frame/asset-conversion", default-features = false } -pallet-asset-tx-payment = { path = "../../../../../substrate/frame/transaction-payment/asset-tx-payment", default-features = false } # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } @@ -44,7 +43,6 @@ std = [ "frame-support/std", "log/std", "pallet-asset-conversion/std", - "pallet-asset-tx-payment/std", "pallet-xcm/std", "parachains-common/std", "scale-info/std", @@ -60,7 +58,6 @@ runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", - "pallet-asset-tx-payment/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 a0c912b6f0fe3643f1f516b311fdfd5d401db4d0..fa2752179eb6fd238eb8596d8e3ebddf947680d3 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -371,7 +371,7 @@ mod tests { for (asset, expected_result) in test_data { assert_eq!( - >::matches_fungibles( + >::matches_fungibles( &asset.clone().try_into().unwrap() ), expected_result, diff --git a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs index 7c237660610fb3037c04cbaf908d2d2741445c64..58f5d2d57a7669b73876897f90e0da3e20730cbb 100644 --- a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs +++ b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs @@ -52,8 +52,3 @@ where } } } - -pub trait MatchesLocalAndForeignAssetsLocation { - fn is_local(location: &L) -> bool; - fn is_foreign(location: &L) -> bool; -} diff --git a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml index 1b863499f04d574a50bc35f3f1b4586bad2d588a..883c93c97b4de6774e86ee83b84d246dc1427f7f 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asset-test-utils" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true description = "Test utils for Asset Hub runtimes." @@ -16,24 +16,19 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = frame-support = { path = "../../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../../substrate/frame/system", default-features = false } pallet-assets = { path = "../../../../../substrate/frame/assets", default-features = false } -pallet-asset-conversion = { path = "../../../../../substrate/frame/asset-conversion", default-features = false } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } +pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false } pallet-session = { path = "../../../../../substrate/frame/session", default-features = false } -sp-consensus-aura = { path = "../../../../../substrate/primitives/consensus/aura", default-features = false } sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false } sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } -sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } # Cumulus cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachains-common = { path = "../../../common", default-features = false } -assets-common = { path = "../common", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } -cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } -cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false } @@ -42,7 +37,6 @@ xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-f xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } -polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } # Bridges pallet-xcm-bridge-hub-router = { path = "../../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } @@ -56,28 +50,22 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder" [features] default = ["std"] std = [ - "assets-common/std", "codec/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-xcmp-queue/std", "cumulus-primitives-core/std", - "cumulus-primitives-parachain-inherent/std", - "cumulus-test-relay-sproof-builder/std", "frame-support/std", "frame-system/std", - "pallet-asset-conversion/std", "pallet-assets/std", "pallet-balances/std", "pallet-collator-selection/std", "pallet-session/std", + "pallet-timestamp/std", "pallet-xcm-bridge-hub-router/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", "parachains-runtimes-test-utils/std", - "polkadot-parachain-primitives/std", - "sp-consensus-aura/std", - "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-std/std", 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 4007d926983ed97a8cae705428c1d61184173cb7..53e10956bd0d1d233555a8e4df1ae23fe351c678 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -70,7 +70,8 @@ pub fn teleports_for_native_asset_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + cumulus_pallet_xcmp_queue::Config, + + cumulus_pallet_xcmp_queue::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, @@ -350,7 +351,8 @@ pub fn teleports_for_foreign_assets_works< + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, @@ -701,7 +703,8 @@ pub fn asset_transactor_transfer_with_local_consensus_currency_works: Into<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From, @@ -826,7 +829,8 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_timestamp::Config, AccountIdOf: Into<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From, @@ -1093,7 +1097,8 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_timestamp::Config, AccountIdOf: Into<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From, @@ -1422,7 +1427,8 @@ pub fn reserve_transfer_native_asset_to_non_teleport_para_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + cumulus_pallet_xcmp_queue::Config, + + cumulus_pallet_xcmp_queue::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, 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 905703a9d161b324715ffe08eb6aa338c752b0b0..1cce3b647cf0446a2246417b5383594fb501e600 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 @@ -30,7 +30,6 @@ use parachains_runtimes_test_utils::{ SlotDurations, ValidatorIdOf, XcmReceivedFrom, }; use sp_runtime::{traits::StaticLookup, Saturating}; -use sp_std::ops::Mul; use xcm::{latest::prelude::*, VersionedAssets}; use xcm_builder::{CreateMatcher, MatchXcm}; use xcm_executor::{traits::ConvertLocation, XcmExecutor}; @@ -71,7 +70,8 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config - + cumulus_pallet_xcmp_queue::Config, + + cumulus_pallet_xcmp_queue::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, @@ -244,6 +244,11 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works< _ => Err(ProcessMessageError::BadFormat), }) .expect("contains BuyExecution") + .match_next_inst(|instr| match instr { + SetAppendix(_) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains SetAppendix") } else { xcm_sent .0 @@ -318,20 +323,22 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< Runtime, AllPalletsWithoutSystem, XcmConfig, - LocationToAccountId, ForeignAssetsPalletInstance, >( collator_session_keys: CollatorSessionKeys, existential_deposit: BalanceOf, target_account: AccountIdOf, block_author_account: AccountIdOf, - ( - foreign_asset_id_location, - transfered_foreign_asset_id_amount, - foreign_asset_id_minimum_balance, - ): (xcm::v3::Location, u128, u128), - prepare_configuration: fn() -> TestBridgingConfig, + (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( + AccountIdOf, + xcm::v3::Location, + u128, + ), + foreign_asset_id_amount_to_transfer: u128, + prepare_configuration: impl FnOnce() -> TestBridgingConfig, (bridge_instance, universal_origin, descend_origin): (Junctions, Junction, Junctions), /* bridge adds origin manipulation on the way */ + additional_checks_before: impl FnOnce(), + additional_checks_after: impl FnOnce(), ) where Runtime: frame_system::Config + pallet_balances::Config @@ -342,14 +349,13 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config + pallet_assets::Config - + pallet_asset_conversion::Config, + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]> + From<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From + Into, XcmConfig: xcm_executor::Config, - LocationToAccountId: ConvertLocation>, >::AssetId: From + Into, >::AssetIdParameter: @@ -360,9 +366,6 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< + Into, <::Lookup as StaticLookup>::Source: From<::AccountId>, - ::AssetKind: - From + Into, - ::Balance: From, ForeignAssetsPalletInstance: 'static, { ExtBuilder::::default() @@ -377,88 +380,31 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< block_author_account.clone().into(), ); - // prepare bridge config - let TestBridgingConfig { local_bridge_hub_location, .. } = prepare_configuration(); - // drip 'ED' user target account let _ = >::deposit_creating( &target_account, existential_deposit, ); - // sovereign account as foreign asset owner (can be whoever for this scenario, doesnt - // matter) - let sovereign_account_as_owner_of_foreign_asset = - LocationToAccountId::convert_location(&Location::parent()).unwrap(); - - // staking pot account for collecting local native fees from `BuyExecution` - let staking_pot = >::account_id(); - let _ = >::deposit_creating( - &staking_pot, - existential_deposit, - ); - // create foreign asset for wrapped/derivated representation assert_ok!( >::force_create( RuntimeHelper::::root_origin(), foreign_asset_id_location.into(), - sovereign_account_as_owner_of_foreign_asset.clone().into(), + foreign_asset_owner.into(), true, // is_sufficient=true foreign_asset_id_minimum_balance.into() ) ); - // setup a pool to pay fees with `foreign_asset_id_location` tokens - let pool_owner: AccountIdOf = [1u8; 32].into(); - let native_asset = xcm::v3::Location::parent(); - let pool_liquidity: u128 = - existential_deposit.into().max(foreign_asset_id_minimum_balance).mul(100_000); - - let _ = >::deposit_creating( - &pool_owner, - (existential_deposit.into() + pool_liquidity).mul(2).into(), - ); - - assert_ok!(>::mint( - RuntimeHelper::::origin_of( - sovereign_account_as_owner_of_foreign_asset - ), - foreign_asset_id_location.into(), - pool_owner.clone().into(), - (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), - )); - - assert_ok!(>::create_pool( - RuntimeHelper::::origin_of(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()) - )); - - assert_ok!(>::add_liquidity( - RuntimeHelper::::origin_of(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()), - pool_liquidity.into(), - pool_liquidity.into(), - 1.into(), - 1.into(), - pool_owner, - )); + // prepare bridge config + let TestBridgingConfig { local_bridge_hub_location, .. } = prepare_configuration(); // Balances before assert_eq!( >::free_balance(&target_account), existential_deposit.clone() ); - assert_eq!( - >::free_balance(&block_author_account), - 0.into() - ); - assert_eq!( - >::free_balance(&staking_pot), - existential_deposit.clone() - ); // ForeignAssets balances before assert_eq!( @@ -468,27 +414,16 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< ), 0.into() ); - assert_eq!( - >::balance( - foreign_asset_id_location.into(), - &block_author_account - ), - 0.into() - ); - assert_eq!( - >::balance( - foreign_asset_id_location.into(), - &staking_pot - ), - 0.into() - ); + + // 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()), - fun: Fungible(transfered_foreign_asset_id_amount), + fun: Fungible(foreign_asset_id_amount_to_transfer), }]); let expected_beneficiary = Location::new( 0, @@ -505,7 +440,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< BuyExecution { fees: Asset { id: AssetId(foreign_asset_id_location_latest.clone()), - fun: Fungible(transfered_foreign_asset_id_amount), + fun: Fungible(foreign_asset_id_amount_to_transfer), }, weight_limit: Unlimited, }, @@ -539,19 +474,10 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< assert_ok!(outcome.ensure_complete()); // Balances after - // staking pot receives xcm fees in dot - assert!( - >::free_balance(&staking_pot) != - existential_deposit - ); assert_eq!( >::free_balance(&target_account), existential_deposit.clone() ); - assert_eq!( - >::free_balance(&block_author_account), - 0.into() - ); // ForeignAssets balances after assert!( @@ -560,20 +486,9 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< &target_account ) > 0.into() ); - assert_eq!( - >::balance( - foreign_asset_id_location.into(), - &staking_pot - ), - 0.into() - ); - assert_eq!( - >::balance( - foreign_asset_id_location.into(), - &block_author_account - ), - 0.into() - ); + + // additional check after + additional_checks_after(); }) } @@ -597,7 +512,8 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config - + pallet_xcm_bridge_hub_router::Config, + + pallet_xcm_bridge_hub_router::Config + + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, AccountIdOf: Into<[u8; 32]>, diff --git a/cumulus/parachains/runtimes/bridge-hubs/README.md b/cumulus/parachains/runtimes/bridge-hubs/README.md index cf617db730dd7faa19d932dbe6a0406b7a0c15f2..c858532295ddce7ad1fd9a57a19d752201b78abd 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/README.md +++ b/cumulus/parachains/runtimes/bridge-hubs/README.md @@ -89,20 +89,18 @@ cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-paracha cd # Rococo + BridgeHubRococo + AssetHub for Rococo (mirroring Kusama) -POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ -POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ -POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_ROCOCO=~/local_bridge_testing/bin/polkadot-parachain-asset-hub \ - ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./cumulus/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +POLKADOT_BINARY=~/local_bridge_testing/bin/polkadot \ +POLKADOT_PARACHAIN_BINARY=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml ``` ``` cd # Westend + BridgeHubWestend + AssetHub for Westend (mirroring Polkadot) -POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ -POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ -POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_WESTEND=~/local_bridge_testing/bin/polkadot-parachain-asset-hub \ - ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./cumulus/zombienet/bridge-hubs/bridge_hub_westend_local_network.toml +POLKADOT_BINARY=~/local_bridge_testing/bin/polkadot \ +POLKADOT_PARACHAIN_BINARY=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml ``` ### Init bridge and run relayer between BridgeHubRococo and BridgeHubWestend @@ -114,7 +112,7 @@ POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_WESTEND=~/local_bridge_testing/bin/ ``` cd -./cumulus/scripts/bridges_rococo_westend.sh run-relay +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh run-relay ``` **Check relay-chain headers relaying:** @@ -137,10 +135,10 @@ This initialization does several things: ``` cd -./cumulus/scripts/bridges_rococo_westend.sh init-asset-hub-rococo-local -./cumulus/scripts/bridges_rococo_westend.sh init-bridge-hub-rococo-local -./cumulus/scripts/bridges_rococo_westend.sh init-asset-hub-westend-local -./cumulus/scripts/bridges_rococo_westend.sh init-bridge-hub-westend-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh init-asset-hub-rococo-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh init-bridge-hub-rococo-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh init-asset-hub-westend-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh init-bridge-hub-westend-local ``` ### Send messages - transfer asset over bridge (ROCs/WNDs) @@ -150,13 +148,13 @@ Do reserve-backed transfers: cd # ROCs from Rococo's Asset Hub to Westend's. -./cumulus/scripts/bridges_rococo_westend.sh reserve-transfer-assets-from-asset-hub-rococo-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh reserve-transfer-assets-from-asset-hub-rococo-local ``` ``` cd # WNDs from Westend's Asset Hub to Rococo's. -./cumulus/scripts/bridges_rococo_westend.sh reserve-transfer-assets-from-asset-hub-westend-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh reserve-transfer-assets-from-asset-hub-westend-local ``` - open explorers: (see zombienets) @@ -171,13 +169,13 @@ Do reserve withdraw transfers: (when previous is finished) cd # wrappedWNDs from Rococo's Asset Hub to Westend's. -./cumulus/scripts/bridges_rococo_westend.sh withdraw-reserve-assets-from-asset-hub-rococo-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh withdraw-reserve-assets-from-asset-hub-rococo-local ``` ``` cd # wrappedROCs from Westend's Asset Hub to Rococo's. -./cumulus/scripts/bridges_rococo_westend.sh withdraw-reserve-assets-from-asset-hub-westend-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh withdraw-reserve-assets-from-asset-hub-westend-local ``` ### Claim relayer's rewards on BridgeHubRococo and BridgeHubWestend @@ -190,10 +188,10 @@ cd cd # Claim rewards on BridgeHubWestend: -./cumulus/scripts/bridges_rococo_westend.sh claim-rewards-bridge-hub-rococo-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh claim-rewards-bridge-hub-rococo-local # Claim rewards on BridgeHubWestend: -./cumulus/scripts/bridges_rococo_westend.sh claim-rewards-bridge-hub-westend-local +./bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh claim-rewards-bridge-hub-westend-local ``` - open explorers: (see zombienets) 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 849a4cf8cd6f5bf4e6fd9fd03876c39c12c32013..242627815d3a5ced13a99d9f7f7c075e55c63200 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bridge-hub-rococo-runtime" -version = "0.1.0" +version = "0.5.0" authors.workspace = true edition.workspace = true description = "Rococo's BridgeHub parachain runtime" @@ -17,12 +17,11 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = "derive", ] } hex-literal = { version = "0.4.1" } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = [ "derive", ] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -smallvec = "1.11.0" +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -61,7 +60,6 @@ sp-version = { path = "../../../../../substrate/primitives/version", default-fea rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false } pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } @@ -78,11 +76,13 @@ cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = fals cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false, features = [ "bridging", ] } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } # Bridges bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hub-rococo", default-features = false } @@ -107,16 +107,16 @@ pallet-xcm-bridge-hub = { path = "../../../../../bridges/modules/xcm-bridge-hub" bridge-runtime-common = { path = "../../../../../bridges/bin/runtime-common", default-features = false } # Ethereum Bridge (Snowbridge) -snowbridge-beacon-primitives = { path = "../../../../../bridges/snowbridge/parachain/primitives/beacon", default-features = false } -snowbridge-pallet-system = { path = "../../../../../bridges/snowbridge/parachain/pallets/system", default-features = false } -snowbridge-system-runtime-api = { path = "../../../../../bridges/snowbridge/parachain/pallets/system/runtime-api", default-features = false } -snowbridge-core = { path = "../../../../../bridges/snowbridge/parachain/primitives/core", default-features = false } -snowbridge-pallet-ethereum-client = { path = "../../../../../bridges/snowbridge/parachain/pallets/ethereum-client", default-features = false } -snowbridge-pallet-inbound-queue = { path = "../../../../../bridges/snowbridge/parachain/pallets/inbound-queue", default-features = false } -snowbridge-pallet-outbound-queue = { path = "../../../../../bridges/snowbridge/parachain/pallets/outbound-queue", default-features = false } -snowbridge-outbound-queue-runtime-api = { path = "../../../../../bridges/snowbridge/parachain/pallets/outbound-queue/runtime-api", default-features = false } -snowbridge-router-primitives = { path = "../../../../../bridges/snowbridge/parachain/primitives/router", default-features = false } -snowbridge-runtime-common = { path = "../../../../../bridges/snowbridge/parachain/runtime/runtime-common", default-features = false } +snowbridge-beacon-primitives = { path = "../../../../../bridges/snowbridge/primitives/beacon", default-features = false } +snowbridge-pallet-system = { path = "../../../../../bridges/snowbridge/pallets/system", default-features = false } +snowbridge-system-runtime-api = { path = "../../../../../bridges/snowbridge/pallets/system/runtime-api", default-features = false } +snowbridge-core = { path = "../../../../../bridges/snowbridge/primitives/core", default-features = false } +snowbridge-pallet-ethereum-client = { path = "../../../../../bridges/snowbridge/pallets/ethereum-client", default-features = false } +snowbridge-pallet-inbound-queue = { path = "../../../../../bridges/snowbridge/pallets/inbound-queue", default-features = false } +snowbridge-pallet-outbound-queue = { path = "../../../../../bridges/snowbridge/pallets/outbound-queue", default-features = false } +snowbridge-outbound-queue-runtime-api = { path = "../../../../../bridges/snowbridge/pallets/outbound-queue/runtime-api", default-features = false } +snowbridge-router-primitives = { path = "../../../../../bridges/snowbridge/primitives/router", default-features = false } +snowbridge-runtime-common = { path = "../../../../../bridges/snowbridge/runtime/runtime-common", default-features = false } bridge-hub-common = { path = "../common", default-features = false } @@ -127,7 +127,7 @@ bridge-runtime-common = { path = "../../../../../bridges/bin/runtime-common", fe "integrity-test", ] } sp-keyring = { path = "../../../../../substrate/primitives/keyring" } -snowbridge-runtime-test-common = { path = "../../../../../bridges/snowbridge/parachain/runtime/test-common" } +snowbridge-runtime-test-common = { path = "../../../../../bridges/snowbridge/runtime/test-common" } [features] default = ["std"] @@ -154,6 +154,7 @@ std = [ "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "frame-benchmarking/std", @@ -184,7 +185,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "rococo-runtime-constants/std", @@ -215,7 +215,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", - "substrate-wasm-builder", + "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", @@ -242,6 +242,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm-bridge-hub/runtime-benchmarks", @@ -297,11 +298,9 @@ try-runtime = [ ] experimental = ["pallet-aura/experimental"] -fast-runtime = [ - "snowbridge-pallet-ethereum-client/beacon-spec-minimal", -] +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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] 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 7630c2bf99b3f0c1534950a349df0b3a61954591..323e6ed65e69c22ae6be6f80bb26b4e6455bbb94 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 @@ -40,7 +40,7 @@ use bridge_runtime_common::{ XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, }, refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedGrandpaMessages, RefundSignedExtensionAdapter, + ActualFeeRefund, RefundBridgedGrandpaMessages, RefundTransactionExtensionAdapter, RefundableMessagesLane, }, }; @@ -168,7 +168,7 @@ impl messages::BridgedChainWithMessages for RococoBulletin {} /// Signed extension that refunds relayers that are delivering messages from the Rococo Bulletin /// chain. -pub type OnBridgeHubRococoRefundRococoBulletinMessages = RefundSignedExtensionAdapter< +pub type OnBridgeHubRococoRefundRococoBulletinMessages = RefundTransactionExtensionAdapter< RefundBridgedGrandpaMessages< Runtime, BridgeGrandpaRococoBulletinInstance, @@ -230,7 +230,8 @@ mod tests { use bridge_runtime_common::{ assert_complete_bridge_types, integrity::check_message_lane_weights, }; - use parachains_common::{rococo, Balance}; + 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 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 d633da2a8e7c1ffdc4addffef1288786549b15a1..01a762d4b99f94f39e7a6050ff13a720002063bd 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 @@ -15,8 +15,8 @@ // along with Cumulus. If not, see . use crate::{xcm_config::UniversalLocation, Runtime}; -use parachains_common::rococo::snowbridge::EthereumNetwork; use snowbridge_router_primitives::outbound::EthereumBlobExporter; +use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< 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 697386b6a7d8323e536eab51c86791e12c89c6e1..05d1aa188d2d3abd114431e6ee388473dca8f982 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 @@ -39,7 +39,7 @@ use bridge_runtime_common::{ XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, }, refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, + ActualFeeRefund, RefundBridgedParachainMessages, RefundTransactionExtensionAdapter, RefundableMessagesLane, RefundableParachain, }, }; @@ -173,7 +173,7 @@ impl UnderlyingChainProvider for BridgeHubWestend { impl messages::BridgedChainWithMessages for BridgeHubWestend {} /// Signed extension that refunds relayers that are delivering messages from the Westend parachain. -pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = RefundSignedExtensionAdapter< +pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = RefundTransactionExtensionAdapter< RefundBridgedParachainMessages< Runtime, RefundableParachain< @@ -252,7 +252,8 @@ mod tests { AssertCompleteBridgeConstants, }, }; - use parachains_common::{rococo, Balance}; + 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 @@ -298,7 +299,7 @@ mod tests { >(AssertCompleteBridgeConstants { this_chain_constants: AssertChainConstants { block_length: bp_bridge_hub_rococo::BlockLength::get(), - block_weights: bp_bridge_hub_rococo::BlockWeights::get(), + block_weights: bp_bridge_hub_rococo::BlockWeightsForAsyncBacking::get(), }, messages_pallet_constants: AssertBridgeMessagesPalletConstants { max_unrewarded_relayers_in_bridged_confirmation_tx: 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 4a3ef8ac4e8ca8b0afa4daf2f8773ea0540e3be8..16e3b2e8600f6df3721926b3ab597023d443bccc 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 @@ -35,7 +35,7 @@ pub mod bridge_to_westend_config; mod weights; pub mod xcm_config; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::{ gwei, meth, outbound::Message, AgentId, AllowSiblingsOnly, PricingParameters, Rewards, @@ -69,9 +69,10 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; +use testnet_parachains_constants::rococo::{ + consensus::*, currency::*, fee::WeightToFee, snowbridge::INBOUND_QUEUE_PALLET_INDEX, time::*, +}; -#[cfg(feature = "runtime-benchmarks")] -use bp_runtime::Chain; use bp_runtime::HeaderId; use bridge_hub_common::{ message_queue::{NarrowOriginToSibling, ParaIdToSibling}, @@ -93,18 +94,14 @@ use xcm::latest::prelude::*; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use parachains_common::{ - impls::DealWithFees, - rococo::{consensus::*, currency::*, fee::WeightToFee}, - AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, - HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + impls::DealWithFees, AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, + AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; use polkadot_runtime_common::prod_or_fast; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; -#[cfg(not(feature = "runtime-benchmarks"))] -use bridge_hub_common::BridgeHubMessageRouter; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -118,8 +115,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -137,7 +134,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -151,6 +148,8 @@ pub type Migrations = ( ConstU32, ConstU32, >, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); /// Migration to initialize storage versions for pallets added after genesis. @@ -169,12 +168,12 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if Balances::on_chain_storage_version() == StorageVersion::new(0) { - Balances::current_storage_version().put::(); + Balances::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -203,7 +202,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_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 4, @@ -263,6 +262,8 @@ impl frame_system::Config for Runtime { type DbWeight = RocksDbWeight; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = weights::frame_system::WeightInfo; + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -278,6 +279,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -306,7 +310,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -323,6 +326,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -340,15 +344,17 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedDmpWeight = ReservedDmpWeight; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + impl parachain_info::Config for Runtime {} parameter_types! { @@ -362,11 +368,15 @@ parameter_types! { impl pallet_message_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_message_queue::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] + // Use the NoopMessageProcessor exclusively for benchmarks, not for tests with the + // runtime-benchmarks feature as tests require the BridgeHubMessageRouter to process messages. + // The "test" feature flag doesn't work, hence the reliance on the "std" feature, which is + // enabled during tests. + #[cfg(all(not(feature = "std"), feature = "runtime-benchmarks"))] type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = BridgeHubMessageRouter< + #[cfg(not(all(not(feature = "std"), feature = "runtime-benchmarks")))] + type MessageProcessor = bridge_hub_common::BridgeHubMessageRouter< xcm_builder::ProcessXcmMessage< AggregateMessageOrigin, xcm_executor::XcmExecutor, @@ -437,9 +447,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -490,13 +500,6 @@ impl pallet_utility::Config for Runtime { } // Ethereum Bridge - -#[cfg(not(feature = "runtime-benchmarks"))] -parameter_types! { - pub storage EthereumGatewayAddress: H160 = H160::zero(); -} - -#[cfg(feature = "runtime-benchmarks")] parameter_types! { pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); } @@ -504,7 +507,6 @@ parameter_types! { parameter_types! { pub const CreateAssetCall: [u8;2] = [53, 0]; pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; - pub const InboundQueuePalletInstance: u8 = parachains_common::rococo::snowbridge::INBOUND_QUEUE_PALLET_INDEX; pub Parameters: PricingParameters = PricingParameters { exchange_rate: FixedU128::from_rational(1, 400), fee_per_gas: gwei(20), @@ -565,7 +567,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { type MessageConverter = MessageToXcm< CreateAssetCall, CreateAssetDeposit, - InboundQueuePalletInstance, + ConstU8, AccountId, Balance, >; @@ -592,29 +594,33 @@ impl snowbridge_pallet_outbound_queue::Config for Runtime { type Channels = EthereumSystem; } -#[cfg(feature = "fast-runtime")] +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] parameter_types! { pub const ChainForkVersions: ForkVersions = ForkVersions { genesis: Fork { - version: [0, 0, 0, 1], // 0x00000001 + version: [0, 0, 0, 0], // 0x00000000 epoch: 0, }, altair: Fork { - version: [1, 0, 0, 1], // 0x01000001 + version: [1, 0, 0, 0], // 0x01000000 epoch: 0, }, bellatrix: Fork { - version: [2, 0, 0, 1], // 0x02000001 + version: [2, 0, 0, 0], // 0x02000000 epoch: 0, }, capella: Fork { - version: [3, 0, 0, 1], // 0x03000001 + version: [3, 0, 0, 0], // 0x03000000 epoch: 0, }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } }; } -#[cfg(not(feature = "fast-runtime"))] +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] parameter_types! { pub const ChainForkVersions: ForkVersions = ForkVersions { genesis: Fork { @@ -633,6 +639,10 @@ parameter_types! { version: [144, 0, 0, 114], // 0x90000072 epoch: 56832, }, + deneb: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 132608, + }, }; } @@ -725,7 +735,7 @@ construct_runtime!( // Message Queue. Importantly, is registered last so that messages are processed after // the `on_initialize` hooks of bridging pallets. - MessageQueue: pallet_message_queue = 250, + MessageQueue: pallet_message_queue = 175, } ); @@ -752,12 +762,14 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -783,7 +795,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -791,6 +803,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -800,7 +821,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1049,6 +1070,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -1079,6 +1101,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -1095,6 +1118,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -1103,7 +1132,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between BH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -1126,6 +1155,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; @@ -1216,6 +1252,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } @@ -1275,7 +1318,7 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { let bench_lane_id = >::bench_lane_id(); - let bridged_chain_id = bp_bridge_hub_westend::BridgeHubWestend::ID; + let bridged_chain_id = bridge_to_westend_config::BridgeHubWestendChainId::get(); pallet_bridge_relayers::Pallet::::relayer_reward( relayer, bp_relayers::RewardsAccountParams::new( @@ -1442,16 +1485,16 @@ mod tests { use codec::Encode; use sp_runtime::{ generic::Era, - traits::{SignedExtension, Zero}, + traits::{TransactionExtensionBase, Zero}, }; #[test] fn ensure_signed_extension_definition_is_compatible_with_relay() { - use bp_polkadot_core::SuffixedCommonSignedExtensionExt; + use bp_polkadot_core::SuffixedCommonTransactionExtensionExt; sp_io::TestExternalities::default().execute_with(|| { frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( + let payload: TxExtension = ( frame_system::CheckNonZeroSender::new(), frame_system::CheckSpecVersion::new(), frame_system::CheckTxVersion::new(), @@ -1465,11 +1508,11 @@ mod tests { bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), ) - ); + ).into(); // for BridgeHubRococo { - let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( + let bhr_indirect_payload = bp_bridge_hub_rococo::TransactionExtension::from_params( VERSION.spec_version, VERSION.transaction_version, bp_runtime::TransactionEra::Immortal, @@ -1480,8 +1523,8 @@ mod tests { ); assert_eq!(payload.encode(), bhr_indirect_payload.encode()); assert_eq!( - payload.additional_signed().unwrap().encode(), - bhr_indirect_payload.additional_signed().unwrap().encode() + payload.implicit().unwrap().encode(), + bhr_indirect_payload.implicit().unwrap().encode() ) } }); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..99e3d6aeba02349af3f4413a2e35d5a81a2a5073 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_136_000 picoseconds. + Weight::from_parts(5_842_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_771_000 picoseconds. + Weight::from_parts(8_857_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 732_000 picoseconds. + Weight::from_parts(2_875_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_627_000 picoseconds. + Weight::from_parts(6_322_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 471_000 picoseconds. + Weight::from_parts(2_455_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 491_000 picoseconds. + Weight::from_parts(2_916_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_798_000 picoseconds. + Weight::from_parts(6_272_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index aac39a4564fb600d9c4f623aa3ba27c78fc8f5fc..d97a30db9541bb01919f0a7a054f42d95816e875 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_bridge_grandpa; pub mod pallet_bridge_messages_rococo_to_rococo_bulletin; @@ -36,6 +37,7 @@ pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs index ba8551a5ebb96948e46598bc8325e9949080388b..861ccfc51fd8e9f7bf8a1367d4ab4ddf459891a0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.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_balances` //! -//! 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-01-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("bridge-hub-rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-8idpd4bs-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: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=bridge-hub-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=bridge-hub-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -55,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 56_219_000 picoseconds. - Weight::from_parts(56_763_000, 0) + // Minimum execution time: 41_696_000 picoseconds. + Weight::from_parts(42_201_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -67,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_515_000 picoseconds. - Weight::from_parts(42_186_000, 0) + // Minimum execution time: 32_855_000 picoseconds. + Weight::from_parts(33_554_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -79,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_274_000 picoseconds. - Weight::from_parts(16_898_000, 0) + // Minimum execution time: 12_977_000 picoseconds. + Weight::from_parts(13_473_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -91,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 23_847_000 picoseconds. - Weight::from_parts(24_343_000, 0) + // Minimum execution time: 17_617_000 picoseconds. + Weight::from_parts(18_234_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -103,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 57_564_000 picoseconds. - Weight::from_parts(58_172_000, 0) + // Minimum execution time: 43_174_000 picoseconds. + Weight::from_parts(43_685_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -115,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 52_131_000 picoseconds. - Weight::from_parts(52_662_000, 0) + // Minimum execution time: 41_125_000 picoseconds. + Weight::from_parts(41_636_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -127,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 19_005_000 picoseconds. - Weight::from_parts(19_594_000, 0) + // Minimum execution time: 15_749_000 picoseconds. + Weight::from_parts(16_163_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -140,13 +139,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 17_275_000 picoseconds. - Weight::from_parts(17_901_000, 0) + // Minimum execution time: 14_238_000 picoseconds. + Weight::from_parts(14_469_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 15_775 - .saturating_add(Weight::from_parts(15_448_147, 0).saturating_mul(u.into())) + // Standard Error: 11_818 + .saturating_add(Weight::from_parts(12_621_051, 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())) } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 4_904_000 picoseconds. + Weight::from_parts(5_459_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..71d17e7259f72c023be3d75f37300c1a45c41d6f --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3593` + // Minimum execution time: 34_956_000 picoseconds. + Weight::from_parts(40_788_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs index 5faded42aa82df52f403b68de2a470ad4a5a17b7..a732e1a573439c4b658191697024ac3c396c9de5 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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: @@ -64,8 +64,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 23_683_000 picoseconds. - Weight::from_parts(24_199_000, 0) + // Minimum execution time: 18_513_000 picoseconds. + Weight::from_parts(19_156_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +90,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 89_524_000 picoseconds. - Weight::from_parts(91_401_000, 0) + // Minimum execution time: 88_096_000 picoseconds. + Weight::from_parts(89_732_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -126,8 +126,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 91_890_000 picoseconds. - Weight::from_parts(93_460_000, 0) + // Minimum execution time: 88_239_000 picoseconds. + Weight::from_parts(89_729_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -148,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_152_000 picoseconds. - Weight::from_parts(7_355_000, 0) + // Minimum execution time: 5_955_000 picoseconds. + Weight::from_parts(6_266_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_081_000 picoseconds. - Weight::from_parts(2_258_000, 0) + // Minimum execution time: 1_868_000 picoseconds. + Weight::from_parts(1_961_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +186,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 28_067_000 picoseconds. - Weight::from_parts(28_693_000, 0) + // Minimum execution time: 24_388_000 picoseconds. + Weight::from_parts(25_072_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +212,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 30_420_000 picoseconds. - Weight::from_parts(31_373_000, 0) + // Minimum execution time: 26_762_000 picoseconds. + Weight::from_parts(27_631_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,45 +224,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_087_000 picoseconds. - Weight::from_parts(2_243_000, 0) + // Minimum execution time: 1_856_000 picoseconds. + Weight::from_parts(2_033_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 15_142_000 picoseconds. - Weight::from_parts(15_598_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 17_718_000 picoseconds. + Weight::from_parts(18_208_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 15_041_000 picoseconds. - Weight::from_parts(15_493_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 17_597_000 picoseconds. + Weight::from_parts(18_090_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 16_624_000 picoseconds. - Weight::from_parts(17_031_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 19_533_000 picoseconds. + Weight::from_parts(20_164_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -282,36 +282,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 26_398_000 picoseconds. - Weight::from_parts(26_847_000, 0) + // Minimum execution time: 24_958_000 picoseconds. + Weight::from_parts(25_628_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_741_000 picoseconds. - Weight::from_parts(8_954_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 12_209_000 picoseconds. + Weight::from_parts(12_612_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 15_306_000 picoseconds. - Weight::from_parts(15_760_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 17_844_000 picoseconds. + Weight::from_parts(18_266_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -327,12 +327,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_127_000 picoseconds. - Weight::from_parts(33_938_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 34_131_000 picoseconds. + Weight::from_parts(34_766_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -343,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 4_290_000 picoseconds. - Weight::from_parts(4_450_000, 0) + // Minimum execution time: 3_525_000 picoseconds. + Weight::from_parts(3_724_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,10 +355,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 26_408_000 picoseconds. - Weight::from_parts(26_900_000, 0) + // Minimum execution time: 24_975_000 picoseconds. + Weight::from_parts(25_517_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 33_761_000 picoseconds. + Weight::from_parts(34_674_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) + .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/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index f641b428d4c68a57458743365147d0e0c0c85cac..55c78477b5684987fe07eaaa87d45d8900e74661 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 @@ -34,12 +34,12 @@ use bp_runtime::ChainId; use frame_support::{ parameter_types, traits::{ConstU32, Contains, Equals, Everything, Nothing}, + StoragePrefixedMap, }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, - rococo::snowbridge::EthereumNetwork, xcm_config::{ AllSiblingSystemParachains, ConcreteAssetFromSystem, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains, @@ -52,16 +52,16 @@ use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_core::Get; use sp_runtime::traits::AccountIdConversion; use sp_std::marker::PhantomData; +use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use xcm::latest::prelude::*; -#[allow(deprecated)] use xcm_builder::{ deposit_or_burn_fee, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, HandleFee, - IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeToAccount, + DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeToAccount, }; use xcm_executor::{ traits::{FeeManager, FeeReason, FeeReason::Export, TransactAsset, WithOriginFilter}, @@ -94,8 +94,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -160,9 +159,12 @@ impl Contains for SafeCallFilter { match call { RuntimeCall::System(frame_system::Call::set_storage { items }) if items.iter().all(|(k, _)| { - k.eq(&DeliveryRewardInBalance::key()) | - k.eq(&RequiredStakeForStakeAndSlash::key()) | - k.eq(&EthereumGatewayAddress::key()) + k.eq(&DeliveryRewardInBalance::key()) || + k.eq(&RequiredStakeForStakeAndSlash::key()) || + k.eq(&EthereumGatewayAddress::key()) || + // Allow resetting of Ethereum nonces in Rococo only. + k.starts_with(&snowbridge_pallet_inbound_queue::Nonce::::final_prefix()) || + k.starts_with(&snowbridge_pallet_outbound_queue::Nonce::::final_prefix()) }) => return true, _ => (), @@ -222,7 +224,14 @@ impl Contains for SafeCallFilter { snowbridge_pallet_inbound_queue::Call::set_operating_mode { .. }, ) | RuntimeCall::EthereumOutboundQueue( snowbridge_pallet_outbound_queue::Call::set_operating_mode { .. }, - ) | RuntimeCall::EthereumSystem(..) + ) | RuntimeCall::EthereumSystem( + snowbridge_pallet_system::Call::upgrade { .. } | + snowbridge_pallet_system::Call::set_operating_mode { .. } | + snowbridge_pallet_system::Call::set_pricing_parameters { .. } | + snowbridge_pallet_system::Call::force_update_channel { .. } | + snowbridge_pallet_system::Call::force_transfer_native_from_agent { .. } | + snowbridge_pallet_system::Call::set_token_transfer_fees { .. }, + ) ) } } @@ -273,7 +282,7 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = FungibleTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; // BridgeHub does not recognize a reserve location for any asset. Users must teleport Native // token where allowed (e.g. with the Relay Chain). @@ -326,6 +335,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index f32c6cae69c63bfa7bc95d9ad2726254a83d698a..46c5df18a1586e77d0bc0f9eb5f9ed19fa689f59 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs @@ -16,16 +16,25 @@ #![cfg(test)] +use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - xcm_config::XcmConfig, MessageQueueServiceWeight, Runtime, RuntimeEvent, SessionKeys, + bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages, + bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages, + xcm_config::XcmConfig, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, + Executive, MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, + TxExtension, UncheckedExtrinsic, }; -use codec::Decode; +use codec::{Decode, Encode}; use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; use frame_support::parameter_types; use parachains_common::{AccountId, AuraId, Balance}; use snowbridge_pallet_ethereum_client::WeightInfo; use sp_core::H160; use sp_keyring::AccountKeyring::Alice; +use sp_runtime::{ + generic::{Era, SignedPayload}, + AccountId32, +}; parameter_types! { pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; @@ -42,6 +51,7 @@ fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys( + 11155111, collator_session_keys(), 1013, 1000, @@ -60,6 +70,7 @@ pub fn transfer_token_to_ethereum_works() { #[test] pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { snowbridge_runtime_test_common::send_unpaid_transfer_token_message::( + 11155111, collator_session_keys(), 1013, 1000, @@ -71,6 +82,7 @@ pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { #[test] pub fn transfer_token_to_ethereum_fee_not_enough() { snowbridge_runtime_test_common::send_transfer_token_message_failure::( + 11155111, collator_session_keys(), 1013, 1000, @@ -86,6 +98,7 @@ pub fn transfer_token_to_ethereum_fee_not_enough() { #[test] pub fn transfer_token_to_ethereum_insufficient_fund() { snowbridge_runtime_test_common::send_transfer_token_message_failure::( + 11155111, collator_session_keys(), 1013, 1000, @@ -93,7 +106,7 @@ pub fn transfer_token_to_ethereum_insufficient_fund() { H160::random(), H160::random(), DefaultBridgeHubEthereumBaseFee::get(), - FailedToTransactAsset("InsufficientBalance"), + FailedToTransactAsset("Funds are unavailable"), ) } @@ -107,3 +120,89 @@ fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() { max_message_queue_weight.all_gt(force_checkpoint); max_message_queue_weight.all_gt(submit_checkpoint); } + +#[test] +fn ethereum_client_consensus_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_extrinsic( + collator_session_keys(), + 1013, + construct_and_apply_extrinsic, + ); +} + +#[test] +fn ethereum_to_polkadot_message_extrinsics_work() { + snowbridge_runtime_test_common::ethereum_to_polkadot_message_extrinsics_work( + collator_session_keys(), + 1013, + construct_and_apply_extrinsic, + ); +} + +/// Tests that the digest items are as expected when a Ethereum Outbound message is received. +/// If the MessageQueue pallet is configured before (i.e. the MessageQueue pallet is listed before +/// the EthereumOutboundQueue in the construct_runtime macro) the EthereumOutboundQueue, this test +/// will fail. +#[test] +pub fn ethereum_outbound_queue_processes_messages_before_message_queue_works() { + snowbridge_runtime_test_common::ethereum_outbound_queue_processes_messages_before_message_queue_works::< + Runtime, + XcmConfig, + AllPalletsWithoutSystem, + >( + 11155111, + collator_session_keys(), + 1013, + 1000, + H160::random(), + H160::random(), + DefaultBridgeHubEthereumBaseFee::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event), + _ => None, + } + }), + ) +} + +fn construct_extrinsic( + sender: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> UncheckedExtrinsic { + let account_id = AccountId32::from(sender.public()); + let tx_ext: TxExtension = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(Era::immortal()), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + BridgeRejectObsoleteHeadersAndMessages::default(), + ( + OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), + OnBridgeHubRococoRefundRococoBulletinMessages::default(), + ), + ); + let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); + let signature = payload.using_encoded(|e| sender.sign(e)); + UncheckedExtrinsic::new_signed( + call, + account_id.into(), + Signature::Sr25519(signature.clone()), + tx_ext, + ) +} + +fn construct_and_apply_extrinsic( + origin: sp_keyring::AccountKeyring, + call: RuntimeCall, +) -> sp_runtime::DispatchOutcome { + let xt = construct_extrinsic(origin, call); + let r = Executive::apply_extrinsic(xt); + r.unwrap() +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 9e46dc086548cfc7be45b98877f0804a2446b1cc..565343c55a5b66d4e2510cda01ba521ba6eee850 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 @@ -22,22 +22,21 @@ use bridge_hub_rococo_runtime::{ xcm_config::{RelayNetwork, TokenLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, + RuntimeEvent, RuntimeOrigin, SessionKeys, TransactionPayment, TxExtension, UncheckedExtrinsic, }; use bridge_hub_test_utils::SlotDurations; use codec::{Decode, Encode}; use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8}; -use parachains_common::{ - rococo::{consensus::RELAY_CHAIN_SLOT_DURATION_MILLIS, fee::WeightToFee}, - AccountId, AuraId, Balance, SLOT_DURATION, -}; +use parachains_common::{AccountId, AuraId, Balance}; +use snowbridge_core::ChannelId; use sp_consensus_aura::SlotDuration; use sp_core::H160; use sp_keyring::AccountKeyring::Alice; use sp_runtime::{ generic::{Era, SignedPayload}, - AccountId32, + AccountId32, Perbill, }; +use testnet_parachains_constants::rococo::{consensus::*, fee::WeightToFee}; use xcm::latest::prelude::*; parameter_types! { @@ -49,7 +48,7 @@ fn construct_extrinsic( call: RuntimeCall, ) -> UncheckedExtrinsic { let account_id = AccountId32::from(sender.public()); - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -65,14 +64,15 @@ fn construct_extrinsic( bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), ), - ); - let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + ) + .into(); + let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); UncheckedExtrinsic::new_signed( call, account_id.into(), Signature::Sr25519(signature.clone()), - extra, + tx_ext, ) } @@ -222,6 +222,72 @@ mod bridge_hub_westend_tests { ) } + #[test] + fn change_ethereum_nonces_by_governance_works() { + let channel_id_one: ChannelId = [1; 32].into(); + let channel_id_two: ChannelId = [2; 32].into(); + let nonce = 42; + + // Reset a single inbound channel + bridge_hub_test_utils::test_cases::set_storage_keys_by_governance_works::( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + vec![ + (snowbridge_pallet_outbound_queue::Nonce::::hashed_key_for::( + channel_id_one, + ) + .to_vec(), 0u64.encode()), + (snowbridge_pallet_inbound_queue::Nonce::::hashed_key_for::( + channel_id_one, + ) + .to_vec(), 0u64.encode()), + ], + || { + // Outbound + snowbridge_pallet_outbound_queue::Nonce::::insert::( + channel_id_one, + nonce, + ); + snowbridge_pallet_outbound_queue::Nonce::::insert::( + channel_id_two, + nonce, + ); + + // Inbound + snowbridge_pallet_inbound_queue::Nonce::::insert::( + channel_id_one, + nonce, + ); + snowbridge_pallet_inbound_queue::Nonce::::insert::( + channel_id_two, + nonce, + ); + }, + || { + // Outbound + assert_eq!( + snowbridge_pallet_outbound_queue::Nonce::::get(channel_id_one), + 0 + ); + assert_eq!( + snowbridge_pallet_outbound_queue::Nonce::::get(channel_id_two), + nonce + ); + + // Inbound + assert_eq!( + snowbridge_pallet_inbound_queue::Nonce::::get(channel_id_one), + 0 + ); + assert_eq!( + snowbridge_pallet_inbound_queue::Nonce::::get(channel_id_two), + nonce + ); + }, + ); + } + #[test] fn change_delivery_reward_by_governance_works() { bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::< @@ -333,53 +399,61 @@ mod bridge_hub_westend_tests { #[test] pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer() { - let estimated = bridge_hub_test_utils::test_cases::can_calculate_weight_for_paid_export_message_with_reserve_transfer::< - Runtime, - XcmConfig, - WeightToFee, - >(); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs` value", - estimated, - max_expected - ); + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs", + bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get(), + || { + bridge_hub_test_utils::test_cases::can_calculate_weight_for_paid_export_message_with_reserve_transfer::< + Runtime, + XcmConfig, + WeightToFee, + >() + }, + Perbill::from_percent(33), + Some(-33), + &format!( + "Estimate fee for `ExportMessage` for runtime: {:?}", + ::Version::get() + ), + ) } #[test] pub fn can_calculate_fee_for_complex_message_delivery_transaction() { - let estimated = from_parachain::can_calculate_fee_for_complex_message_delivery_transaction::< - RuntimeTestsAdapter, - >(collator_session_keys(), construct_and_estimate_extrinsic_fee); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs` value", - estimated, - max_expected - ); + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs", + bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs::get(), + || { + from_parachain::can_calculate_fee_for_complex_message_delivery_transaction::< + RuntimeTestsAdapter, + >(collator_session_keys(), construct_and_estimate_extrinsic_fee) + }, + Perbill::from_percent(33), + Some(-33), + &format!( + "Estimate fee for `single message delivery` for runtime: {:?}", + ::Version::get() + ), + ) } #[test] pub fn can_calculate_fee_for_complex_message_confirmation_transaction() { - let estimated = - from_parachain::can_calculate_fee_for_complex_message_confirmation_transaction::< - RuntimeTestsAdapter, - >(collator_session_keys(), construct_and_estimate_extrinsic_fee); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs` value", - estimated, - max_expected - ); + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs", + bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs::get(), + || { + from_parachain::can_calculate_fee_for_complex_message_confirmation_transaction::< + RuntimeTestsAdapter, + >(collator_session_keys(), construct_and_estimate_extrinsic_fee) + }, + Perbill::from_percent(33), + Some(-33), + &format!( + "Estimate fee for `single message confirmation` for runtime: {:?}", + ::Version::get() + ), + ) } } @@ -527,55 +601,43 @@ mod bridge_hub_bulletin_tests { ); } - #[test] - pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer() { - let estimated = bridge_hub_test_utils::test_cases::can_calculate_weight_for_paid_export_message_with_reserve_transfer::< - Runtime, - XcmConfig, - WeightToFee, - >(); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs` value", - estimated, - max_expected - ); - } - #[test] pub fn can_calculate_fee_for_complex_message_delivery_transaction() { - let estimated = - from_grandpa_chain::can_calculate_fee_for_complex_message_delivery_transaction::< - RuntimeTestsAdapter, - >(collator_session_keys(), construct_and_estimate_extrinsic_fee); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs` value", - estimated, - max_expected - ); + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs", + bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs::get(), + || { + from_grandpa_chain::can_calculate_fee_for_complex_message_delivery_transaction::< + RuntimeTestsAdapter, + >(collator_session_keys(), construct_and_estimate_extrinsic_fee) + }, + Perbill::from_percent(33), + None, /* we don't want lowering according to the Bulletin setup, because + * `from_grandpa_chain` is cheaper then `from_parachain_chain` */ + &format!( + "Estimate fee for `single message delivery` for runtime: {:?}", + ::Version::get() + ), + ) } #[test] pub fn can_calculate_fee_for_complex_message_confirmation_transaction() { - let estimated = - from_grandpa_chain::can_calculate_fee_for_complex_message_confirmation_transaction::< - RuntimeTestsAdapter, - >(collator_session_keys(), construct_and_estimate_extrinsic_fee); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs` value", - estimated, - max_expected - ); + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs", + bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs::get(), + || { + from_grandpa_chain::can_calculate_fee_for_complex_message_confirmation_transaction::< + RuntimeTestsAdapter, + >(collator_session_keys(), construct_and_estimate_extrinsic_fee) + }, + Perbill::from_percent(33), + None, /* we don't want lowering according to the Bulletin setup, because + * `from_grandpa_chain` is cheaper then `from_parachain_chain` */ + &format!( + "Estimate fee for `single message confirmation` for runtime: {:?}", + ::Version::get() + ), + ) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index dd02faefc1c81434b891d51674d4358f638fdf19..b5fe093b8be79c21e3298548e07b8327004d2a26 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bridge-hub-westend-runtime" -version = "0.1.0" +version = "0.2.0" authors.workspace = true edition.workspace = true description = "Westend's BridgeHub parachain runtime" @@ -15,10 +15,9 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex-literal = { version = "0.4.1" } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -smallvec = "1.11.0" +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -57,7 +56,6 @@ sp-version = { path = "../../../../../substrate/primitives/version", default-fea westend-runtime-constants = { path = "../../../../../polkadot/runtime/westend/constants", default-features = false } pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } @@ -70,11 +68,13 @@ cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false, features = ["bridging"] } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["westend"] } # Bridges bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hub-rococo", default-features = false } @@ -126,6 +126,7 @@ std = [ "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "frame-benchmarking/std", @@ -156,7 +157,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", @@ -176,6 +176,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", @@ -203,6 +204,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm-bridge-hub/runtime-benchmarks", @@ -249,5 +251,5 @@ experimental = ["pallet-aura/experimental"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] 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 c0c0976b52fd5618ac0c8360946b3d1f7bada6d6..934dce5c2c481092fed1a73162e3f7b1f0967a11 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 @@ -36,7 +36,7 @@ use bridge_runtime_common::{ XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, }, refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, + ActualFeeRefund, RefundBridgedParachainMessages, RefundTransactionExtensionAdapter, RefundableMessagesLane, RefundableParachain, }, }; @@ -190,7 +190,7 @@ impl ThisChainWithMessages for BridgeHubWestend { } /// Signed extension that refunds relayers that are delivering messages from the Rococo parachain. -pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = RefundSignedExtensionAdapter< +pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = RefundTransactionExtensionAdapter< RefundBridgedParachainMessages< Runtime, RefundableParachain, @@ -287,7 +287,8 @@ mod tests { AssertCompleteBridgeConstants, }, }; - use parachains_common::{westend, Balance}; + 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 @@ -333,7 +334,7 @@ mod tests { >(AssertCompleteBridgeConstants { this_chain_constants: AssertChainConstants { block_length: bp_bridge_hub_westend::BlockLength::get(), - block_weights: bp_bridge_hub_westend::BlockWeights::get(), + block_weights: bp_bridge_hub_westend::BlockWeightsForAsyncBacking::get(), }, messages_pallet_constants: AssertBridgeMessagesPalletConstants { max_unrewarded_relayers_in_bridged_confirmation_tx: 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 08dd129d84b663c7709d4b9e544724b921872c38..86ed8b2f488c1c7d9ac389582754d05019a3e45c 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 @@ -32,7 +32,7 @@ pub mod bridge_to_rococo_config; mod weights; pub mod xcm_config; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::ParaId; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; @@ -69,8 +69,6 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm_config::{XcmOriginToTransactDispatchOrigin, XcmRouter}; -#[cfg(feature = "runtime-benchmarks")] -use bp_runtime::Chain; use bp_runtime::HeaderId; #[cfg(any(feature = "std", test))] @@ -82,11 +80,10 @@ use xcm::latest::prelude::*; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use parachains_common::{ - impls::DealWithFees, - westend::{consensus::*, currency::*, fee::WeightToFee}, - AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, - HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + impls::DealWithFees, AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, + AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; +use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*}; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -100,8 +97,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -116,7 +113,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -125,6 +122,8 @@ pub type Migrations = ( InitStorageVersions, // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); /// Migration to initialize storage versions for pallets added after genesis. @@ -143,12 +142,12 @@ impl frame_support::traits::OnRuntimeUpgrade for InitStorageVersions { let mut writes = 0; if PolkadotXcm::on_chain_storage_version() == StorageVersion::new(0) { - PolkadotXcm::current_storage_version().put::(); + PolkadotXcm::in_code_storage_version().put::(); writes.saturating_inc(); } if Balances::on_chain_storage_version() == StorageVersion::new(0) { - Balances::current_storage_version().put::(); + Balances::in_code_storage_version().put::(); writes.saturating_inc(); } @@ -177,7 +176,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_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 4, @@ -252,6 +251,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -280,7 +282,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -297,6 +298,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -314,15 +316,17 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedDmpWeight = ReservedDmpWeight; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + impl parachain_info::Config for Runtime {} parameter_types! { @@ -403,9 +407,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -508,21 +512,20 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { BridgeRococoMessages } -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] + [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] // XCM [pallet_xcm, PalletXcmExtrinsicsBenchmark::] @@ -540,7 +543,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -548,6 +551,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -557,7 +569,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -752,6 +764,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -781,6 +794,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -797,6 +811,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -805,7 +825,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between BH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -828,6 +848,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; @@ -918,6 +945,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(WestendLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } @@ -974,7 +1008,7 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { let bench_lane_id = >::bench_lane_id(); - let bridged_chain_id = bp_bridge_hub_rococo::BridgeHubRococo::ID; + let bridged_chain_id = bridge_to_rococo_config::BridgeHubRococoChainId::get(); pallet_bridge_relayers::Pallet::::relayer_reward( relayer, bp_relayers::RewardsAccountParams::new( @@ -1106,16 +1140,16 @@ mod tests { use codec::Encode; use sp_runtime::{ generic::Era, - traits::{SignedExtension, Zero}, + traits::{TransactionExtensionBase, Zero}, }; #[test] fn ensure_signed_extension_definition_is_compatible_with_relay() { - use bp_polkadot_core::SuffixedCommonSignedExtensionExt; + use bp_polkadot_core::SuffixedCommonTransactionExtensionExt; sp_io::TestExternalities::default().execute_with(|| { frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( + let payload: TxExtension = ( frame_system::CheckNonZeroSender::new(), frame_system::CheckSpecVersion::new(), frame_system::CheckTxVersion::new(), @@ -1128,10 +1162,10 @@ mod tests { ( bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), ), - ); + ).into(); { - let bh_indirect_payload = bp_bridge_hub_westend::SignedExtension::from_params( + let bh_indirect_payload = bp_bridge_hub_westend::TransactionExtension::from_params( VERSION.spec_version, VERSION.transaction_version, bp_runtime::TransactionEra::Immortal, @@ -1142,8 +1176,8 @@ mod tests { ); assert_eq!(payload.encode(), bh_indirect_payload.encode()); assert_eq!( - payload.additional_signed().unwrap().encode(), - bh_indirect_payload.additional_signed().unwrap().encode() + payload.implicit().unwrap().encode(), + bh_indirect_payload.implicit().unwrap().encode() ) } }); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..06f1114b4dea90cc9d830ff1f492428f3672b202 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/ +// --chain=bridge-hub-westend-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_166_000 picoseconds. + Weight::from_parts(6_021_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_651_000 picoseconds. + Weight::from_parts(9_177_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 601_000 picoseconds. + Weight::from_parts(2_805_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_727_000 picoseconds. + Weight::from_parts(6_051_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 471_000 picoseconds. + Weight::from_parts(2_494_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 521_000 picoseconds. + Weight::from_parts(2_655_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_808_000 picoseconds. + Weight::from_parts(6_402_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs index a65ee31d3e55ff8135fdd7dec35120e0a463409b..e23033e0dfd313c60fe53f64a49ad2237477ecb5 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_bridge_grandpa; pub mod pallet_bridge_messages; @@ -35,6 +36,7 @@ pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs index 26a188a98610a7ecc822eb5e7e448ad7f1867eb8..3afef6564bdb8fec4e435948cf8cadb928bbf773 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_balances.rs @@ -16,28 +16,26 @@ //! Autogenerated weights for `pallet_balances` //! -//! 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-01-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("bridge-hub-rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-8idpd4bs-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 +// target/production/polkadot-parachain // benchmark // pallet -// --chain=bridge-hub-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=bridge-hub-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -56,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 56_219_000 picoseconds. - Weight::from_parts(56_763_000, 0) + // Minimum execution time: 42_912_000 picoseconds. + Weight::from_parts(43_690_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -68,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 41_515_000 picoseconds. - Weight::from_parts(42_186_000, 0) + // Minimum execution time: 33_823_000 picoseconds. + Weight::from_parts(34_415_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -80,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_274_000 picoseconds. - Weight::from_parts(16_898_000, 0) + // Minimum execution time: 13_226_000 picoseconds. + Weight::from_parts(13_557_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -92,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 23_847_000 picoseconds. - Weight::from_parts(24_343_000, 0) + // Minimum execution time: 18_055_000 picoseconds. + Weight::from_parts(18_407_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -104,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 57_564_000 picoseconds. - Weight::from_parts(58_172_000, 0) + // Minimum execution time: 44_442_000 picoseconds. + Weight::from_parts(45_101_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -116,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 52_131_000 picoseconds. - Weight::from_parts(52_662_000, 0) + // Minimum execution time: 42_485_000 picoseconds. + Weight::from_parts(43_157_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -128,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 19_005_000 picoseconds. - Weight::from_parts(19_594_000, 0) + // Minimum execution time: 16_002_000 picoseconds. + Weight::from_parts(16_425_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -141,13 +139,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 17_275_000 picoseconds. - Weight::from_parts(17_901_000, 0) + // Minimum execution time: 14_526_000 picoseconds. + Weight::from_parts(14_825_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 15_775 - .saturating_add(Weight::from_parts(15_448_147, 0).saturating_mul(u.into())) + // Standard Error: 10_967 + .saturating_add(Weight::from_parts(13_376_293, 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())) } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_151_000 picoseconds. + Weight::from_parts(5_419_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..92c53b918792257ddf932148fa10a4ba8016653f --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/ +// --chain=bridge-hub-westend-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3593` + // Minimum execution time: 40_286_000 picoseconds. + Weight::from_parts(45_816_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs index 83e4260e77198355d23ea0c38481d7b8e68267c7..a78ff2355efaf06562e44828a8df0730481d4098 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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,8 +64,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 23_219_000 picoseconds. - Weight::from_parts(23_818_000, 0) + // Minimum execution time: 19_527_000 picoseconds. + Weight::from_parts(19_839_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -88,10 +88,10 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn teleport_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `70` + // Measured: `107` // Estimated: `3593` - // Minimum execution time: 90_120_000 picoseconds. - Weight::from_parts(92_545_000, 0) + // Minimum execution time: 90_938_000 picoseconds. + Weight::from_parts(92_822_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -124,10 +124,10 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `70` + // Measured: `107` // Estimated: `3593` - // Minimum execution time: 91_339_000 picoseconds. - Weight::from_parts(93_204_000, 0) + // Minimum execution time: 90_133_000 picoseconds. + Weight::from_parts(92_308_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -148,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_976_000 picoseconds. - Weight::from_parts(7_284_000, 0) + // Minimum execution time: 6_205_000 picoseconds. + Weight::from_parts(6_595_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_044_000 picoseconds. - Weight::from_parts(2_223_000, 0) + // Minimum execution time: 1_927_000 picoseconds. + Weight::from_parts(2_062_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +186,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 27_778_000 picoseconds. - Weight::from_parts(28_318_000, 0) + // Minimum execution time: 25_078_000 picoseconds. + Weight::from_parts(25_782_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +212,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 30_446_000 picoseconds. - Weight::from_parts(31_925_000, 0) + // Minimum execution time: 28_188_000 picoseconds. + Weight::from_parts(28_826_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,45 +224,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_037_000 picoseconds. - Weight::from_parts(2_211_000, 0) + // Minimum execution time: 1_886_000 picoseconds. + Weight::from_parts(1_991_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 15_620_000 picoseconds. - Weight::from_parts(15_984_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 17_443_000 picoseconds. + Weight::from_parts(17_964_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 15_689_000 picoseconds. - Weight::from_parts(16_093_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 17_357_000 picoseconds. + Weight::from_parts(18_006_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 16_946_000 picoseconds. - Weight::from_parts(17_192_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 18_838_000 picoseconds. + Weight::from_parts(19_688_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -282,36 +282,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 27_164_000 picoseconds. - Weight::from_parts(27_760_000, 0) + // Minimum execution time: 25_517_000 picoseconds. + Weight::from_parts(26_131_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_689_000 picoseconds. - Weight::from_parts(8_874_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 11_587_000 picoseconds. + Weight::from_parts(11_963_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 15_944_000 picoseconds. - Weight::from_parts(16_381_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 17_490_000 picoseconds. + Weight::from_parts(18_160_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -327,12 +327,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_826_000 picoseconds. - Weight::from_parts(34_784_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 34_088_000 picoseconds. + Weight::from_parts(34_598_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -343,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 4_257_000 picoseconds. - Weight::from_parts(4_383_000, 0) + // Minimum execution time: 3_566_000 picoseconds. + Weight::from_parts(3_754_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,10 +355,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 26_924_000 picoseconds. - Weight::from_parts(27_455_000, 0) + // Minimum execution time: 25_078_000 picoseconds. + Weight::from_parts(25_477_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 34_661_000 picoseconds. + Weight::from_parts(35_411_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) + .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/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 085608acf5d8dba5a9b477d357c5f89ca76b57f9..e18df6feda82754d83db711bb2c6ea813c387fca 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 @@ -38,16 +38,15 @@ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::AccountIdConversion; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -76,8 +75,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -233,7 +231,7 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = FungibleTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; // BridgeHub does not recognize a reserve location for any asset. Users must teleport Native // token where allowed (e.g. with the Relay Chain). @@ -265,6 +263,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type PriceForParentDelivery = 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 91d69b8042086b247ecccf9006db40d159086f67..7ea22befe95f88233dca4bf95d77e3a13ac8afa5 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 @@ -24,7 +24,7 @@ use bridge_hub_westend_runtime::{ xcm_config::{RelayNetwork, WestendLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, - SignedExtra, TransactionPayment, UncheckedExtrinsic, + TransactionPayment, TxExtension, UncheckedExtrinsic, }; use bridge_to_rococo_config::{ BridgeGrandpaRococoInstance, BridgeHubRococoChainId, BridgeHubRococoLocation, @@ -33,16 +33,14 @@ use bridge_to_rococo_config::{ }; use codec::{Decode, Encode}; use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8}; -use parachains_common::{ - westend::{consensus::RELAY_CHAIN_SLOT_DURATION_MILLIS, fee::WeightToFee}, - AccountId, AuraId, Balance, SLOT_DURATION, -}; +use parachains_common::{AccountId, AuraId, Balance}; use sp_consensus_aura::SlotDuration; use sp_keyring::AccountKeyring::Alice; use sp_runtime::{ generic::{Era, SignedPayload}, - AccountId32, + AccountId32, Perbill, }; +use testnet_parachains_constants::westend::{consensus::*, fee::WeightToFee}; use xcm::latest::prelude::*; // Para id of sibling chain used in tests. @@ -67,7 +65,7 @@ fn construct_extrinsic( call: RuntimeCall, ) -> UncheckedExtrinsic { let account_id = AccountId32::from(sender.public()); - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -80,14 +78,15 @@ fn construct_extrinsic( pallet_transaction_payment::ChargeTransactionPayment::::from(0), BridgeRejectObsoleteHeadersAndMessages::default(), (bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(),), - ); - let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + ) + .into(); + let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); UncheckedExtrinsic::new_signed( call, account_id.into(), Signature::Sr25519(signature.clone()), - extra, + tx_ext, ) } @@ -295,50 +294,59 @@ pub fn complex_relay_extrinsic_works() { #[test] pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer() { - let estimated = bridge_hub_test_utils::test_cases::can_calculate_weight_for_paid_export_message_with_reserve_transfer::< + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds", + bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get(), + || { + bridge_hub_test_utils::test_cases::can_calculate_weight_for_paid_export_message_with_reserve_transfer::< Runtime, XcmConfig, WeightToFee, - >(); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds` value", - estimated, - max_expected - ); + >() + }, + Perbill::from_percent(33), + Some(-33), + &format!( + "Estimate fee for `ExportMessage` for runtime: {:?}", + ::Version::get() + ), + ) } #[test] pub fn can_calculate_fee_for_complex_message_delivery_transaction() { - let estimated = from_parachain::can_calculate_fee_for_complex_message_delivery_transaction::< - RuntimeTestsAdapter, - >(collator_session_keys(), construct_and_estimate_extrinsic_fee); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_westend::BridgeHubWestendBaseDeliveryFeeInWnds::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_westend::BridgeHubWestendBaseDeliveryFeeInWnds` value", - estimated, - max_expected - ); + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_westend::BridgeHubWestendBaseDeliveryFeeInWnds", + bp_bridge_hub_westend::BridgeHubWestendBaseDeliveryFeeInWnds::get(), + || { + from_parachain::can_calculate_fee_for_complex_message_delivery_transaction::< + RuntimeTestsAdapter, + >(collator_session_keys(), construct_and_estimate_extrinsic_fee) + }, + Perbill::from_percent(33), + Some(-33), + &format!( + "Estimate fee for `single message delivery` for runtime: {:?}", + ::Version::get() + ), + ) } #[test] pub fn can_calculate_fee_for_complex_message_confirmation_transaction() { - let estimated = from_parachain::can_calculate_fee_for_complex_message_confirmation_transaction::< - RuntimeTestsAdapter, - >(collator_session_keys(), construct_and_estimate_extrinsic_fee); - - // check if estimated value is sane - let max_expected = bp_bridge_hub_westend::BridgeHubWestendBaseConfirmationFeeInWnds::get(); - assert!( - estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_westend::BridgeHubWestendBaseConfirmationFeeInWnds` value", - estimated, - max_expected - ); + bridge_hub_test_utils::check_sane_fees_values( + "bp_bridge_hub_westend::BridgeHubWestendBaseConfirmationFeeInWnds", + bp_bridge_hub_westend::BridgeHubWestendBaseConfirmationFeeInWnds::get(), + || { + from_parachain::can_calculate_fee_for_complex_message_confirmation_transaction::< + RuntimeTestsAdapter, + >(collator_session_keys(), construct_and_estimate_extrinsic_fee) + }, + Perbill::from_percent(33), + Some(-33), + &format!( + "Estimate fee for `single message confirmation` for runtime: {:?}", + ::Version::get() + ), + ) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml index 0d75bb2213f8953af88ae20dadbba0858c98cf13..a4dcd19dc9e8675599eaad9c2d340eca5874e63b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bridge-hub-common" -version = "0.1.0" +version = "0.0.0" authors.workspace = true edition.workspace = true description = "Bridge hub common utilities" @@ -16,7 +16,7 @@ sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-fea cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false } -snowbridge-core = { path = "../../../../../bridges/snowbridge/parachain/primitives/core", default-features = false } +snowbridge-core = { path = "../../../../../bridges/snowbridge/primitives/core", default-features = false } [features] default = ["std"] diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 3049182cd4e269bba76786c3d18b355103f61d88..5f2a6e050d83c3db662f8ff4896d32dc8a28fde3 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bridge-hub-test-utils" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Utils for BridgeHub testing" @@ -12,11 +12,9 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } impl-trait-for-tuples = "0.2" -log = { version = "0.4.20", default-features = false } +log = { workspace = true } # Substrate -frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-executive = { path = "../../../../../substrate/frame/executive", default-features = false } frame-support = { path = "../../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../../substrate/frame/system", default-features = false } sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } @@ -27,20 +25,16 @@ sp-std = { path = "../../../../../substrate/primitives/std", default-features = sp-tracing = { path = "../../../../../substrate/primitives/tracing" } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false } -pallet-session = { path = "../../../../../substrate/frame/session", default-features = false } +pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false } # Cumulus asset-test-utils = { path = "../../assets/test-utils" } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } -pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } -parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false } # Polkadot -pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } -pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } @@ -48,7 +42,6 @@ xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkad # Bridges bp-header-chain = { path = "../../../../../bridges/primitives/header-chain", default-features = false } bp-messages = { path = "../../../../../bridges/primitives/messages", default-features = false } -bp-parachains = { path = "../../../../../bridges/primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../../../../bridges/primitives/polkadot-core", default-features = false } bp-relayers = { path = "../../../../../bridges/primitives/relayers", default-features = false } bp-runtime = { path = "../../../../../bridges/primitives/runtime", default-features = false } @@ -65,7 +58,6 @@ 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", @@ -74,8 +66,6 @@ std = [ "codec/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-xcmp-queue/std", - "frame-benchmarking/std", - "frame-executive/std", "frame-support/std", "frame-system/std", "log/std", @@ -84,12 +74,8 @@ std = [ "pallet-bridge-messages/std", "pallet-bridge-parachains/std", "pallet-bridge-relayers/std", - "pallet-collator-selection/std", - "pallet-session/std", + "pallet-timestamp/std", "pallet-utility/std", - "pallet-xcm-benchmarks?/std", - "pallet-xcm/std", - "parachain-info/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 445f001f1a4c111920fc513be95f88d73a25a634..1874f38de2df17e85c1f49723271d090e962eb70 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -21,3 +21,60 @@ pub mod test_data; pub use bp_test_utils::test_header; pub use parachains_runtimes_test_utils::*; +use sp_runtime::Perbill; + +/// 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 +/// actual value is below `margin_overestimate_diff_in_percent_for_lowering`, we should lower the +/// actual value. +pub fn check_sane_fees_values( + const_name: &str, + actual: u128, + calculate_estimated_fee: fn() -> u128, + overestimate_in_percent: Perbill, + margin_overestimate_diff_in_percent_for_lowering: Option, + label: &str, +) { + let estimated = calculate_estimated_fee(); + let estimated_plus_overestimate = estimated + (overestimate_in_percent * estimated); + let diff_to_estimated = diff_as_percent(actual, estimated); + let diff_to_estimated_plus_overestimate = diff_as_percent(actual, estimated_plus_overestimate); + + sp_tracing::try_init_simple(); + log::error!( + target: "bridges::estimate", + "{label}:\nconstant: {const_name}\n[+] actual: {actual}\n[+] estimated: {estimated} ({diff_to_estimated:.2?})\n[+] estimated(+33%): {estimated_plus_overestimate} ({diff_to_estimated_plus_overestimate:.2?})", + ); + + // check if estimated value is sane + assert!( + estimated <= actual, + "estimated: {estimated}, actual: {actual}, please adjust `{const_name}` to the value: {estimated_plus_overestimate}", + ); + assert!( + estimated_plus_overestimate <= actual, + "estimated_plus_overestimate: {estimated_plus_overestimate}, actual: {actual}, please adjust `{const_name}` to the value: {estimated_plus_overestimate}", + ); + + if let Some(margin_overestimate_diff_in_percent_for_lowering) = + margin_overestimate_diff_in_percent_for_lowering + { + assert!( + diff_to_estimated_plus_overestimate > margin_overestimate_diff_in_percent_for_lowering as f64, + "diff_to_estimated_plus_overestimate: {diff_to_estimated_plus_overestimate:.2}, overestimate_diff_in_percent_for_lowering: {margin_overestimate_diff_in_percent_for_lowering}, please adjust `{const_name}` to the value: {estimated_plus_overestimate}", + ); + } +} + +pub fn diff_as_percent(left: u128, right: u128) -> f64 { + let left = left as f64; + let right = right as f64; + ((left - right).abs() / left) * 100f64 * (if left >= right { -1 } else { 1 }) as f64 +} + +#[test] +fn diff_as_percent_works() { + assert_eq!(-20_f64, diff_as_percent(100, 80)); + assert_eq!(25_f64, diff_as_percent(80, 100)); + assert_eq!(33_f64, diff_as_percent(13351000000, 17756830000)); +} 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 acf0f2c71620a56af93fa23338fd498af5ab19d9..8aaaa4f59d7884ff211855a925638317a3b722ea 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 @@ -36,7 +36,7 @@ use bridge_runtime_common::{ }, messages_xcm_extension::XcmAsPlainPayload, }; -use frame_support::traits::{Get, OnFinalize, OnInitialize}; +use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use parachains_runtimes_test_utils::{ AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations, @@ -358,16 +358,8 @@ where message_proof, helpers::relayer_id_at_bridged_chain::(), ); - let estimated_fee = compute_extrinsic_fee(batch); - log::error!( - target: "bridges::estimate", - "Estimate fee: {:?} for single message delivery for runtime: {:?}", - estimated_fee, - ::Version::get(), - ); - - estimated_fee + compute_extrinsic_fee(batch) }) } @@ -427,15 +419,7 @@ where message_delivery_proof, unrewarded_relayers, ); - let estimated_fee = compute_extrinsic_fee(batch); - - log::error!( - target: "bridges::estimate", - "Estimate fee: {:?} for single message confirmation for runtime: {:?}", - estimated_fee, - ::Version::get(), - ); - estimated_fee + compute_extrinsic_fee(batch) }) } 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 8a86c5bb72f5885d69612839ea32197280f62f6e..72ec0718acf7759aedb02e91356fea73ee73e7e7 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 @@ -37,7 +37,7 @@ use bridge_runtime_common::{ }, messages_xcm_extension::XcmAsPlainPayload, }; -use frame_support::traits::{Get, OnFinalize, OnInitialize}; +use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use parachains_runtimes_test_utils::{ AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations, @@ -446,16 +446,8 @@ where message_proof, helpers::relayer_id_at_bridged_chain::(), ); - let estimated_fee = compute_extrinsic_fee(batch); - log::error!( - target: "bridges::estimate", - "Estimate fee: {:?} for single message delivery for runtime: {:?}", - estimated_fee, - ::Version::get(), - ); - - estimated_fee + compute_extrinsic_fee(batch) }) } @@ -531,15 +523,7 @@ where message_delivery_proof, unrewarded_relayers, ); - let estimated_fee = compute_extrinsic_fee(batch); - - log::error!( - target: "bridges::estimate", - "Estimate fee: {:?} for single message confirmation for runtime: {:?}", - estimated_fee, - ::Version::get(), - ); - estimated_fee + compute_extrinsic_fee(batch) }) } 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 4f634c184aa84faa6466102ef5fee30f254bad43..2b48f2e3d515f625532d9c5f50fabadb9a89517a 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 @@ -197,7 +197,9 @@ where pub(crate) fn initialize_bridge_grandpa_pallet( init_data: bp_header_chain::InitializationData>, ) where - Runtime: BridgeGrandpaConfig, + Runtime: BridgeGrandpaConfig + + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config, { pallet_bridge_grandpa::Pallet::::initialize( RuntimeHelper::::root_origin(), 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 ce939692644901a15c375079674d111253dd5b9f..bc1c7ec5e032c08fb36b3005d4abcaf24bb43ff4 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 @@ -72,7 +72,9 @@ pub type RuntimeHelper = parachains_runtimes_test_utils::RuntimeHelper; // Re-export test_case from `parachains-runtimes-test-utils` -pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works; +pub use parachains_runtimes_test_utils::test_cases::{ + change_storage_constant_by_governance_works, set_storage_keys_by_governance_works, +}; /// Prepare default runtime storage and run test within this context. pub fn run_test( @@ -576,6 +578,10 @@ where fees: Asset { id: AssetId(Location::new(1, [])), fun: Fungible(34333299) }, weight_limit: Unlimited, }, + SetAppendix(Xcm(vec![DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: Location::new(1, [Parachain(1000)]), + }])), ExportMessage { network: Polkadot, destination: [Parachain(1000)].into(), @@ -612,7 +618,6 @@ where ]), ]), }, - DepositAsset { assets: Wild(All), beneficiary: Location::new(1, [Parachain(1000)]) }, SetTopic([ 36, 224, 250, 165, 82, 195, 67, 110, 160, 170, 140, 87, 217, 62, 201, 164, 42, 98, 219, 157, 124, 105, 248, 25, 131, 218, 199, 36, 109, 173, 100, 122, @@ -636,13 +641,5 @@ where let estimated_fee = WeightToFee::weight_to_fee(&weight); assert!(estimated_fee > BalanceOf::::zero()); - sp_tracing::try_init_simple(); - log::error!( - target: "bridges::estimate", - "Estimate fee: {:?} for `ExportMessage` for runtime: {:?}", - estimated_fee, - Runtime::Version::get(), - ); - estimated_fee.into() } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml index dd526a9e044cde5db6ad47084d14ac146c5ee8d2..32d7e97bc45503793672889e67177c249a3e6e7b 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "collectives-westend-runtime" -version = "1.0.0" +version = "3.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -12,9 +12,8 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } hex-literal = { version = "0.4.1" } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -smallvec = "1.11.0" # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -61,7 +60,6 @@ sp-version = { path = "../../../../../substrate/primitives/version", default-fea # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } @@ -76,12 +74,14 @@ cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } pallet-collective-content = { path = "../../../pallets/collective-content", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["westend"] } [build-dependencies] substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", optional = true } @@ -117,6 +117,7 @@ runtime-benchmarks = [ "pallet-salary/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", @@ -170,6 +171,7 @@ std = [ "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", @@ -206,7 +208,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", @@ -225,6 +226,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", @@ -235,5 +237,5 @@ experimental = ["pallet-aura/experimental"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs index 05b3427ef431622a8a995e316eaa1e540a6a0594..0c9f428c1396bede97a67002d0554d98d62dbc39 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs @@ -34,12 +34,13 @@ mod tracks; use super::*; use crate::xcm_config::{FellowshipAdminBodyId, LocationToAccountId, WndAssetHub}; use frame_support::traits::{EitherOf, MapSuccess, TryMapSuccess}; +use frame_system::EnsureRootWithSuccess; pub use origins::pallet_origins as pallet_ambassador_origins; use origins::pallet_origins::{ EnsureAmbassadorsVoice, EnsureAmbassadorsVoiceFrom, EnsureHeadAmbassadorsVoice, Origin, }; use sp_core::ConstU128; -use sp_runtime::traits::{CheckedReduceBy, ConstU16, ConvertToValue, Replace}; +use sp_runtime::traits::{CheckedReduceBy, ConstU16, ConvertToValue, Replace, ReplaceWithDefault}; use xcm::prelude::*; use xcm_builder::{AliasesIntoAccountId32, PayOverXcm}; @@ -99,14 +100,25 @@ pub type PromoteOrigin = EitherOf< >, >; +/// Exchange is by any of: +/// - Root can exchange arbitrarily. +/// - the Fellows origin +pub type ExchangeOrigin = EitherOf>, Fellows>; + impl pallet_ranked_collective::Config for Runtime { type WeightInfo = weights::pallet_ranked_collective_ambassador_collective::WeightInfo; type RuntimeEvent = RuntimeEvent; + type AddOrigin = MapSuccess>; type PromoteOrigin = PromoteOrigin; type DemoteOrigin = DemoteOrigin; + type RemoveOrigin = Self::DemoteOrigin; + type ExchangeOrigin = ExchangeOrigin; type Polls = AmbassadorReferenda; type MinRankOfClass = sp_runtime::traits::Identity; + type MemberSwappedHandler = (crate::AmbassadorCore, crate::AmbassadorSalary); type VoteWeight = pallet_ranked_collective::Linear; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = (crate::AmbassadorCore, crate::AmbassadorSalary); } parameter_types! { diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs index 273fa6a34150d8925148ce2619e25745e30bfc98..3816d2ed848ed51740283ffea31e9f7e53c01f1a 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs @@ -38,16 +38,14 @@ pub use origins::{ }; use pallet_ranked_collective::EnsureOfRank; use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; -use parachains_common::{ - impls::ToParentTreasury, - westend::{account, currency::GRAND}, -}; +use parachains_common::impls::ToParentTreasury; use polkadot_runtime_common::impls::{ LocatableAssetConverter, VersionedLocatableAsset, VersionedLocationConverter, }; use sp_arithmetic::Permill; use sp_core::{ConstU128, ConstU32}; use sp_runtime::traits::{ConstU16, ConvertToValue, IdentityLookup, Replace, TakeFirst}; +use testnet_parachains_constants::westend::{account, currency::GRAND}; use westend_runtime_constants::time::HOURS; use xcm::prelude::*; use xcm_builder::{AliasesIntoAccountId32, PayOverXcm}; @@ -55,7 +53,7 @@ use xcm_builder::{AliasesIntoAccountId32, PayOverXcm}; #[cfg(feature = "runtime-benchmarks")] use crate::impls::benchmarks::{OpenHrmpChannel, PayWithEnsure}; #[cfg(feature = "runtime-benchmarks")] -use parachains_common::westend::currency::DOLLARS; +use testnet_parachains_constants::westend::currency::DOLLARS; /// The Fellowship members' ranks. pub mod ranks { @@ -115,12 +113,17 @@ impl pallet_ranked_collective::Config for Runtime type WeightInfo = weights::pallet_ranked_collective_fellowship_collective::WeightInfo; type RuntimeEvent = RuntimeEvent; - #[cfg(not(feature = "runtime-benchmarks"))] // Promotions and the induction of new members are serviced by `FellowshipCore` pallet instance. - type PromoteOrigin = frame_system::EnsureNever; + #[cfg(not(feature = "runtime-benchmarks"))] + type AddOrigin = frame_system::EnsureNever<()>; #[cfg(feature = "runtime-benchmarks")] + type AddOrigin = frame_system::EnsureRoot; + // The maximum value of `u16` set as a success value for the root to ensure the benchmarks will // pass. + #[cfg(not(feature = "runtime-benchmarks"))] + type PromoteOrigin = frame_system::EnsureNever; + #[cfg(feature = "runtime-benchmarks")] type PromoteOrigin = EnsureRootWithSuccess>; // Demotion is by any of: @@ -129,6 +132,7 @@ impl pallet_ranked_collective::Config for Runtime // // The maximum value of `u16` set as a success value for the root to ensure the benchmarks will // pass. + type RemoveOrigin = Self::DemoteOrigin; type DemoteOrigin = EitherOf< EnsureRootWithSuccess>, MapSuccess< @@ -136,9 +140,17 @@ impl pallet_ranked_collective::Config for Runtime Replace>, >, >; + // Exchange is by any of: + // - Root can exchange arbitrarily. + // - the Fellows origin + type ExchangeOrigin = + EitherOf>, Fellows>; type Polls = FellowshipReferenda; type MinRankOfClass = tracks::MinRankOfClass; + type MemberSwappedHandler = (crate::FellowshipCore, crate::FellowshipSalary); type VoteWeight = pallet_ranked_collective::Geometric; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = (crate::FellowshipCore, crate::FellowshipSalary); } pub type FellowshipCoreInstance = pallet_core_fellowship::Instance1; diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs index caf0cddec664a55ce080ed6052a9cdf42a5e2f28..e5b176fc77873805fb0e4ed6dba74d720ea3479a 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/impls.rs @@ -81,7 +81,7 @@ where } fn proposal_of(proposal_hash: HashOf) -> Option> { - pallet_collective::Pallet::::proposal_of(proposal_hash) + pallet_collective::ProposalOf::::get(proposal_hash) } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index be7beafc89af78fb84c4fc49417e795581cdac5b..3887a1d74ecce047f02154721414bf554e3274e7 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -44,7 +44,7 @@ pub mod xcm_config; pub mod fellowship; pub use ambassador::pallet_ambassador_origins; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use fellowship::{pallet_fellowship_origins, Fellows}; use impls::{AllianceProposalProvider, EqualOrGreatestRootCmp}; use sp_api::impl_runtime_apis; @@ -83,12 +83,13 @@ pub use parachains_common as common; use parachains_common::{ impls::{DealWithFees, ToParentTreasury}, message_queue::*, - westend::{account::*, consensus::*, currency::*, fee::WeightToFee}, AccountId, AuraId, Balance, BlockNumber, Hash, Header, Nonce, Signature, - AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, MAXIMUM_BLOCK_WEIGHT, MINUTES, NORMAL_DISPATCH_RATIO, - SLOT_DURATION, + AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; use sp_runtime::RuntimeDebug; +use testnet_parachains_constants::westend::{ + account::*, consensus::*, currency::*, fee::WeightToFee, time::*, +}; use xcm_config::{ GovernanceLocation, LocationToAccountId, TreasurerBodyId, XcmOriginToTransactDispatchOrigin, }; @@ -116,7 +117,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("collectives-westend"), impl_name: create_runtime_str!("collectives-westend"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, @@ -174,6 +175,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = ConstU16<0>; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = frame_support::traits::ConstU32<16>; @@ -183,6 +185,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -211,7 +216,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<0>; } @@ -228,6 +232,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -386,15 +391,17 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type OutboundXcmpMessageSource = XcmpQueue; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + impl parachain_info::Config for Runtime {} parameter_types! { @@ -477,9 +484,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -702,8 +709,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -714,7 +721,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// All migrations executed on runtime upgrade as a nested tuple of types implementing /// `OnRuntimeUpgrade`. Included migrations must be idempotent. type Migrations = ( @@ -722,6 +729,8 @@ type Migrations = ( pallet_collator_selection::migration::v1::MigrateToV1, // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); /// Executive: handles dispatch to the various modules. @@ -738,6 +747,7 @@ pub type Executive = frame_executive::Executive< mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] @@ -745,6 +755,7 @@ mod benches { [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -770,7 +781,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -778,6 +789,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -787,7 +807,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -939,6 +959,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -956,6 +977,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -970,8 +992,21 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + xcm_config::WndLocation::get(), + ExistentialDeposit::get() + ).into()); + } + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -980,7 +1015,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between Collectives and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }.into(), Parent.into(), @@ -1003,6 +1038,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } let whitelist: Vec = vec![ diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..f3030cc4f6aa5acbeec612ae17eb0cf2b4663383 --- /dev/null +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/ +// --chain=collectives-westend-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_497_000 picoseconds. + Weight::from_parts(5_961_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_240_000 picoseconds. + Weight::from_parts(8_175_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(3_005_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_426_000 picoseconds. + Weight::from_parts(6_131_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_715_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 491_000 picoseconds. + Weight::from_parts(2_635_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_958_000 picoseconds. + Weight::from_parts(6_753_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs index a9a298e547edb49738b9a0612d04d4141301d4ba..00b3bd92d5ef9ce0081ca9e95e43bd35098c6aa0 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs @@ -18,6 +18,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_alliance; pub mod pallet_asset_rate; pub mod pallet_balances; @@ -39,6 +40,7 @@ pub mod pallet_salary_fellowship_salary; pub mod pallet_scheduler; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_treasury; pub mod pallet_utility; pub mod pallet_xcm; diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.rs index 6c1cf072257f0e0e7033bdbeaa1af5dcaec5a5f1..602e7ca50c136c3c862bbf2f43fa452b9518116e 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_balances.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_balances` //! -//! 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-01-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("collectives-polkadot-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-8idpd4bs-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_balances -// --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_balances +// --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)] @@ -55,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 55_696_000 picoseconds. - Weight::from_parts(56_582_000, 0) + // Minimum execution time: 45_085_000 picoseconds. + Weight::from_parts(45_772_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -67,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 40_885_000 picoseconds. - Weight::from_parts(41_993_000, 0) + // Minimum execution time: 35_447_000 picoseconds. + Weight::from_parts(36_143_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -79,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 14_565_000 picoseconds. - Weight::from_parts(15_080_000, 0) + // Minimum execution time: 12_314_000 picoseconds. + Weight::from_parts(12_679_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -91,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 22_158_000 picoseconds. - Weight::from_parts(22_715_000, 0) + // Minimum execution time: 17_455_000 picoseconds. + Weight::from_parts(17_902_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -103,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 57_957_000 picoseconds. - Weight::from_parts(58_618_000, 0) + // Minimum execution time: 46_785_000 picoseconds. + Weight::from_parts(47_436_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -115,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 52_018_000 picoseconds. - Weight::from_parts(52_795_000, 0) + // Minimum execution time: 43_948_000 picoseconds. + Weight::from_parts(44_680_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -127,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 17_469_000 picoseconds. - Weight::from_parts(18_030_000, 0) + // Minimum execution time: 15_267_000 picoseconds. + Weight::from_parts(15_499_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -140,13 +139,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 17_223_000 picoseconds. - Weight::from_parts(17_587_000, 0) + // Minimum execution time: 14_817_000 picoseconds. + Weight::from_parts(15_287_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 16_201 - .saturating_add(Weight::from_parts(15_360_967, 0).saturating_mul(u.into())) + // Standard Error: 11_738 + .saturating_add(Weight::from_parts(13_511_800, 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())) } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_382_000 picoseconds. + Weight::from_parts(5_768_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_ambassador_collective.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_ambassador_collective.rs index a6372c4b89dc222ca6a5a6acd36306d79ea3ffb2..8f6aa583a41c1685ab2d46e14ef1266a79452291 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_ambassador_collective.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_ambassador_collective.rs @@ -174,4 +174,7 @@ impl pallet_ranked_collective::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2550).saturating_mul(n.into())) } + fn exchange_member() -> Weight { + todo!() + } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_fellowship_collective.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_fellowship_collective.rs index 9c773c56ac398a7563427d5d33f837d3977028e1..6dfe9b88ff63356123e7c7faf7fbd2c2e4b81dee 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_fellowship_collective.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_ranked_collective_fellowship_collective.rs @@ -173,4 +173,7 @@ impl pallet_ranked_collective::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2550).saturating_mul(n.into())) } + fn exchange_member() -> Weight { + todo!() + } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_scheduler.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_scheduler.rs index cf5610df66574a38a388b7e36a56f87e41ff59c2..42e37b967e4c88acccd13c8cb71bd22b1bc2d3dd 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_scheduler.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_scheduler.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_scheduler` //! //! 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: `[]` +//! DATE: 2024-01-25, 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-grjcggob-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_scheduler -// --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_scheduler +// --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)] @@ -55,8 +54,8 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `31` // Estimated: `1489` - // Minimum execution time: 3_441_000 picoseconds. - Weight::from_parts(3_604_000, 0) + // Minimum execution time: 2_475_000 picoseconds. + Weight::from_parts(2_644_000, 0) .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -68,11 +67,11 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `77 + s * (177 ±0)` // Estimated: `159279` - // Minimum execution time: 2_879_000 picoseconds. - Weight::from_parts(2_963_000, 0) + // Minimum execution time: 2_898_000 picoseconds. + Weight::from_parts(1_532_342, 0) .saturating_add(Weight::from_parts(0, 159279)) - // Standard Error: 3_764 - .saturating_add(Weight::from_parts(909_557, 0).saturating_mul(s.into())) + // Standard Error: 4_736 + .saturating_add(Weight::from_parts(412_374, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -80,25 +79,27 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_172_000 picoseconds. - Weight::from_parts(5_294_000, 0) + // Minimum execution time: 3_171_000 picoseconds. + Weight::from_parts(3_349_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `Preimage::PreimageFor` (r:1 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `213 + s * (1 ±0)` - // Estimated: `3678 + s * (1 ±0)` - // Minimum execution time: 19_704_000 picoseconds. - Weight::from_parts(19_903_000, 0) - .saturating_add(Weight::from_parts(0, 3678)) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_394, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2)) + // Measured: `246 + s * (1 ±0)` + // Estimated: `3711 + s * (1 ±0)` + // Minimum execution time: 17_329_000 picoseconds. + Weight::from_parts(17_604_000, 0) + .saturating_add(Weight::from_parts(0, 3711)) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_256, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) } @@ -108,8 +109,8 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_359_000 picoseconds. - Weight::from_parts(6_599_000, 0) + // Minimum execution time: 4_503_000 picoseconds. + Weight::from_parts(4_677_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -117,24 +118,24 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_217_000 picoseconds. - Weight::from_parts(5_333_000, 0) + // Minimum execution time: 3_145_000 picoseconds. + Weight::from_parts(3_252_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_406_000 picoseconds. - Weight::from_parts(2_541_000, 0) + // Minimum execution time: 1_804_000 picoseconds. + Weight::from_parts(1_891_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_370_000 picoseconds. - Weight::from_parts(2_561_000, 0) + // Minimum execution time: 1_706_000 picoseconds. + Weight::from_parts(1_776_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `Scheduler::Agenda` (r:1 w:1) @@ -144,11 +145,11 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `77 + s * (177 ±0)` // Estimated: `159279` - // Minimum execution time: 11_784_000 picoseconds. - Weight::from_parts(5_574_404, 0) + // Minimum execution time: 8_629_000 picoseconds. + Weight::from_parts(6_707_232, 0) .saturating_add(Weight::from_parts(0, 159279)) - // Standard Error: 7_217 - .saturating_add(Weight::from_parts(1_035_248, 0).saturating_mul(s.into())) + // Standard Error: 5_580 + .saturating_add(Weight::from_parts(471_827, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -161,11 +162,11 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `77 + s * (177 ±0)` // Estimated: `159279` - // Minimum execution time: 16_373_000 picoseconds. - Weight::from_parts(3_088_135, 0) + // Minimum execution time: 12_675_000 picoseconds. + Weight::from_parts(7_791_682, 0) .saturating_add(Weight::from_parts(0, 159279)) - // Standard Error: 7_095 - .saturating_add(Weight::from_parts(1_745_270, 0).saturating_mul(s.into())) + // Standard Error: 5_381 + .saturating_add(Weight::from_parts(653_023, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -178,11 +179,11 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `468 + s * (179 ±0)` // Estimated: `159279` - // Minimum execution time: 14_822_000 picoseconds. - Weight::from_parts(9_591_402, 0) + // Minimum execution time: 11_908_000 picoseconds. + Weight::from_parts(11_833_059, 0) .saturating_add(Weight::from_parts(0, 159279)) - // Standard Error: 7_151 - .saturating_add(Weight::from_parts(1_058_408, 0).saturating_mul(s.into())) + // Standard Error: 5_662 + .saturating_add(Weight::from_parts(482_816, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -195,12 +196,91 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `509 + s * (179 ±0)` // Estimated: `159279` - // Minimum execution time: 18_541_000 picoseconds. - Weight::from_parts(6_522_239, 0) + // Minimum execution time: 15_506_000 picoseconds. + Weight::from_parts(11_372_975, 0) .saturating_add(Weight::from_parts(0, 159279)) - // Standard Error: 8_349 - .saturating_add(Weight::from_parts(1_760_431, 0).saturating_mul(s.into())) + // Standard Error: 5_765 + .saturating_add(Weight::from_parts(656_322, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Scheduler::Retries` (r:1 w:2) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(155814), added: 158289, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 200]`. + fn schedule_retry(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `159` + // Estimated: `159279` + // Minimum execution time: 14_069_000 picoseconds. + Weight::from_parts(14_868_345, 0) + .saturating_add(Weight::from_parts(0, 159279)) + // Standard Error: 425 + .saturating_add(Weight::from_parts(33_468, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(155814), added: 158289, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `77 + s * (177 ±0)` + // Estimated: `159279` + // Minimum execution time: 7_550_000 picoseconds. + Weight::from_parts(6_735_955, 0) + .saturating_add(Weight::from_parts(0, 159279)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(155814), added: 158289, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `513 + s * (179 ±0)` + // Estimated: `159279` + // Minimum execution time: 11_017_000 picoseconds. + Weight::from_parts(11_749_385, 0) + .saturating_add(Weight::from_parts(0, 159279)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(155814), added: 158289, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `77 + s * (177 ±0)` + // Estimated: `159279` + // Minimum execution time: 7_550_000 picoseconds. + Weight::from_parts(6_735_955, 0) + .saturating_add(Weight::from_parts(0, 159279)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(155814), added: 158289, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `513 + s * (179 ±0)` + // Estimated: `159279` + // Minimum execution time: 11_017_000 picoseconds. + Weight::from_parts(11_749_385, 0) + .saturating_add(Weight::from_parts(0, 159279)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..5d077b89d56421a05cd64bec99ecdad445049528 --- /dev/null +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/ +// --chain=collectives-westend-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 39_815_000 picoseconds. + Weight::from_parts(46_067_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs index 50dfbffde01f21d1138f0fdaa27649f962e245e4..5d427d850046ff030c6c5b6247426849227e7ea1 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_xcm.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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: @@ -64,8 +64,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 24_540_000 picoseconds. - Weight::from_parts(25_439_000, 0) + // Minimum execution time: 21_813_000 picoseconds. + Weight::from_parts(22_332_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -90,8 +90,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3679` - // Minimum execution time: 86_614_000 picoseconds. - Weight::from_parts(88_884_000, 0) + // Minimum execution time: 93_243_000 picoseconds. + Weight::from_parts(95_650_000, 0) .saturating_add(Weight::from_parts(0, 3679)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -126,8 +126,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `214` // Estimated: `3679` - // Minimum execution time: 87_915_000 picoseconds. - Weight::from_parts(90_219_000, 0) + // Minimum execution time: 96_199_000 picoseconds. + Weight::from_parts(98_620_000, 0) .saturating_add(Weight::from_parts(0, 3679)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) @@ -148,8 +148,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_872_000 picoseconds. - Weight::from_parts(7_110_000, 0) + // Minimum execution time: 6_442_000 picoseconds. + Weight::from_parts(6_682_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_009_000 picoseconds. - Weight::from_parts(2_163_000, 0) + // Minimum execution time: 1_833_000 picoseconds. + Weight::from_parts(1_973_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +186,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_858_000 picoseconds. - Weight::from_parts(29_355_000, 0) + // Minimum execution time: 27_318_000 picoseconds. + Weight::from_parts(28_224_000, 0) .saturating_add(Weight::from_parts(0, 3610)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) @@ -212,8 +212,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `363` // Estimated: `3828` - // Minimum execution time: 30_598_000 picoseconds. - Weight::from_parts(31_168_000, 0) + // Minimum execution time: 29_070_000 picoseconds. + Weight::from_parts(30_205_000, 0) .saturating_add(Weight::from_parts(0, 3828)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) @@ -224,45 +224,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_090_000 picoseconds. - Weight::from_parts(2_253_000, 0) + // Minimum execution time: 1_904_000 picoseconds. + Weight::from_parts(2_033_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `162` - // Estimated: `11052` - // Minimum execution time: 16_133_000 picoseconds. - Weight::from_parts(16_433_000, 0) - .saturating_add(Weight::from_parts(0, 11052)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `159` + // Estimated: `13524` + // Minimum execution time: 18_348_000 picoseconds. + Weight::from_parts(18_853_000, 0) + .saturating_add(Weight::from_parts(0, 13524)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `11056` - // Minimum execution time: 16_012_000 picoseconds. - Weight::from_parts(16_449_000, 0) - .saturating_add(Weight::from_parts(0, 11056)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `163` + // Estimated: `13528` + // Minimum execution time: 17_964_000 picoseconds. + Weight::from_parts(18_548_000, 0) + .saturating_add(Weight::from_parts(0, 13528)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `173` - // Estimated: `13538` - // Minimum execution time: 17_922_000 picoseconds. - Weight::from_parts(18_426_000, 0) - .saturating_add(Weight::from_parts(0, 13538)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `16013` + // Minimum execution time: 19_708_000 picoseconds. + Weight::from_parts(20_157_000, 0) + .saturating_add(Weight::from_parts(0, 16013)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -282,36 +282,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `212` // Estimated: `6152` - // Minimum execution time: 27_280_000 picoseconds. - Weight::from_parts(28_026_000, 0) + // Minimum execution time: 26_632_000 picoseconds. + Weight::from_parts(27_314_000, 0) .saturating_add(Weight::from_parts(0, 6152)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `206` - // Estimated: `8621` - // Minimum execution time: 9_387_000 picoseconds. - Weight::from_parts(9_644_000, 0) - .saturating_add(Weight::from_parts(0, 8621)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11096` + // Minimum execution time: 11_929_000 picoseconds. + Weight::from_parts(12_304_000, 0) + .saturating_add(Weight::from_parts(0, 11096)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `173` - // Estimated: `11063` - // Minimum execution time: 16_649_000 picoseconds. - Weight::from_parts(17_025_000, 0) - .saturating_add(Weight::from_parts(0, 11063)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `170` + // Estimated: `13535` + // Minimum execution time: 18_599_000 picoseconds. + Weight::from_parts(19_195_000, 0) + .saturating_add(Weight::from_parts(0, 13535)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -327,12 +327,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `11105` - // Minimum execution time: 34_355_000 picoseconds. - Weight::from_parts(35_295_000, 0) - .saturating_add(Weight::from_parts(0, 11105)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `212` + // Estimated: `13577` + // Minimum execution time: 35_524_000 picoseconds. + Weight::from_parts(36_272_000, 0) + .saturating_add(Weight::from_parts(0, 13577)) + .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -343,8 +343,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `1588` - // Minimum execution time: 4_527_000 picoseconds. - Weight::from_parts(4_699_000, 0) + // Minimum execution time: 4_044_000 picoseconds. + Weight::from_parts(4_238_000, 0) .saturating_add(Weight::from_parts(0, 1588)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -355,10 +355,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7740` // Estimated: `11205` - // Minimum execution time: 27_011_000 picoseconds. - Weight::from_parts(27_398_000, 0) + // Minimum execution time: 25_741_000 picoseconds. + Weight::from_parts(26_301_000, 0) .saturating_add(Weight::from_parts(0, 11205)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `160` + // Estimated: `3625` + // Minimum execution time: 35_925_000 picoseconds. + Weight::from_parts(36_978_000, 0) + .saturating_add(Weight::from_parts(0, 3625)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } 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 aa7dbe991e4bd91016908bd1ac60246450cc179b..87f637d5b59a4c56880fed680eace7f5d108a46e 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -36,17 +36,15 @@ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use westend_runtime_constants::xcm as xcm_constants; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocatableAssetId, - OriginToPluralityVoice, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, - XcmFeeToAccount, + DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, + IsConcrete, LocatableAssetId, OriginToPluralityVoice, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -61,7 +59,7 @@ parameter_types! { pub const GovernanceLocation: Location = Location::parent(); pub const FellowshipAdminBodyId: BodyId = BodyId::Index(xcm_constants::body::FELLOWSHIP_ADMIN_INDEX); pub AssetHub: Location = (Parent, Parachain(1000)).into(); - pub const TreasurerBodyId: BodyId = BodyId::Index(xcm_constants::body::TREASURER_INDEX); + pub const TreasurerBodyId: BodyId = BodyId::Treasury; pub AssetHubUsdtId: AssetId = (PalletInstance(50), GeneralIndex(1984)).into(); pub UsdtAssetHub: LocatableAssetId = LocatableAssetId { location: AssetHub::get(), @@ -85,9 +83,8 @@ pub type LocationToAccountId = ( AccountId32Aliases, ); -/// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +/// Means for transacting the native currency on this chain.#[allow(deprecated)] +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -262,7 +259,7 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = FungibleTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; // Collectives does not recognize a reserve location for any asset. Users must teleport WND // where allowed (e.g. with the Relay Chain). @@ -290,6 +287,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/constants/Cargo.toml b/cumulus/parachains/runtimes/constants/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..561e8276b5f0543001e10fd21345ea5d3a65fee5 --- /dev/null +++ b/cumulus/parachains/runtimes/constants/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "testnet-parachains-constants" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +description = "Common constants for Testnet Parachains runtimes" +license = "Apache-2.0" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +smallvec = "1.11.0" + +# Substrate +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } + +# Polkadot +polkadot-core-primitives = { path = "../../../../polkadot/core-primitives", default-features = false } +rococo-runtime-constants = { path = "../../../../polkadot/runtime/rococo/constants", default-features = false, optional = true } +westend-runtime-constants = { path = "../../../../polkadot/runtime/westend/constants", default-features = false, optional = true } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } + +# Cumulus +cumulus-primitives-core = { path = "../../../primitives/core", default-features = false } + +[features] +default = ["std"] +std = [ + "cumulus-primitives-core/std", + "frame-support/std", + "polkadot-core-primitives/std", + "rococo-runtime-constants?/std", + "sp-runtime/std", + "westend-runtime-constants?/std", + "xcm/std", +] + +# Test runtimes specific features. +rococo = ["rococo-runtime-constants"] +westend = ["westend-runtime-constants"] diff --git a/cumulus/parachains/runtimes/constants/src/lib.rs b/cumulus/parachains/runtimes/constants/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..40d799bac7bd354cb9f593a5c0340f691194f371 --- /dev/null +++ b/cumulus/parachains/runtimes/constants/src/lib.rs @@ -0,0 +1,21 @@ +// 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)] + +#[cfg(feature = "rococo")] +pub mod rococo; +#[cfg(feature = "westend")] +pub mod westend; diff --git a/cumulus/parachains/common/src/rococo.rs b/cumulus/parachains/runtimes/constants/src/rococo.rs similarity index 81% rename from cumulus/parachains/common/src/rococo.rs rename to cumulus/parachains/runtimes/constants/src/rococo.rs index 9ab57f0a6c89aba445b694ac17561b2c9e8f86a9..d10b5e7d3af4368d9bd50664b8be029a3f812795 100644 --- a/cumulus/parachains/common/src/rococo.rs +++ b/cumulus/parachains/runtimes/constants/src/rococo.rs @@ -108,14 +108,42 @@ pub mod fee { /// Consensus-related. pub mod consensus { + use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}; + /// Maximum number of blocks simultaneously accepted by the Runtime, not yet included /// into the relay chain. - pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; + pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; /// How many parachain blocks are processed by the relay chain per parent. Limits the /// number of blocks authored per slot. pub const BLOCK_PROCESSING_VELOCITY: u32 = 1; /// Relay chain slot duration, in milliseconds. pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; + + /// We allow for 2 seconds of compute with a 6 second average block. + pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, + ); + + /// This determines the average expected block time that we are targeting. + /// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. + /// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked + /// up by `pallet_aura` to implement `fn slot_duration()`. + /// + /// Change this to adjust the block time. + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +} + +/// Time-related +pub mod time { + use polkadot_core_primitives::BlockNumber; + + // Time is measured by number of blocks. + pub const MINUTES: BlockNumber = + 60_000 / (super::consensus::MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; } pub mod snowbridge { diff --git a/cumulus/parachains/common/src/westend.rs b/cumulus/parachains/runtimes/constants/src/westend.rs similarity index 82% rename from cumulus/parachains/common/src/westend.rs rename to cumulus/parachains/runtimes/constants/src/westend.rs index 2bd4d18a15eba8fc04f0505439d55cb56062f67a..607d91e8808d7d9c0aacaa8d1f4056ba5ff06821 100644 --- a/cumulus/parachains/common/src/westend.rs +++ b/cumulus/parachains/runtimes/constants/src/westend.rs @@ -131,12 +131,40 @@ pub mod fee { /// Consensus-related. pub mod consensus { + use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}; + /// Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the /// relay chain. - pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; + pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; /// How many parachain blocks are processed by the relay chain per parent. Limits the number of /// blocks authored per slot. pub const BLOCK_PROCESSING_VELOCITY: u32 = 1; /// Relay chain slot duration, in milliseconds. pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; + + /// We allow for 2 seconds of compute with a 6 second average block. + pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, + ); + + /// This determines the average expected block time that we are targeting. + /// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. + /// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked + /// up by `pallet_aura` to implement `fn slot_duration()`. + /// + /// Change this to adjust the block time. + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +} + +/// Time-related +pub mod time { + use polkadot_core_primitives::BlockNumber; + + // Time is measured by number of blocks. + pub const MINUTES: BlockNumber = + 60_000 / (super::consensus::MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; } diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index 54af73c3d03dd78bd21affd35bbdcae8d1be5664..747f75cf2e26326afcce5f2ab471777358a05a8a 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contracts-rococo-runtime" -version = "0.2.0" +version = "0.8.0" description = "Parachain testnet runtime for FRAME Contracts pallet." authors.workspace = true edition.workspace = true @@ -18,9 +18,8 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex-literal = { version = "0.4.1", optional = true } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -smallvec = "1.11.0" # Substrate sp-api = { path = "../../../../../substrate/primitives/api", default-features = false } @@ -58,7 +57,6 @@ pallet-contracts = { path = "../../../../../substrate/frame/contracts", default- # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false } @@ -69,27 +67,28 @@ xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkad # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } [features] default = ["std"] std = [ "codec/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", @@ -117,7 +116,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "rococo-runtime-constants/std", @@ -136,13 +134,13 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", ] runtime-benchmarks = [ - "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -160,6 +158,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", @@ -172,7 +171,6 @@ runtime-benchmarks = [ try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-dmp-queue/try-runtime", "cumulus-pallet-parachain-system/try-runtime", "cumulus-pallet-xcm/try-runtime", "cumulus-pallet-xcmp-queue/try-runtime", @@ -203,5 +201,5 @@ experimental = ["pallet-aura/experimental"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs index 94f2d34b265a8e66feddaa01d524ab7c02cc5549..171ac6a9528f134d9c22548500805ef36e9504f9 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs @@ -21,12 +21,13 @@ use frame_support::{ parameter_types, traits::{ConstBool, ConstU32, Nothing}, }; +use frame_system::EnsureSigned; use pallet_contracts::{ weights::SubstrateWeight, Config, DebugInfo, DefaultAddressGenerator, Frame, Schedule, }; use sp_runtime::Perbill; -pub use parachains_common::rococo::currency::deposit; +use testnet_parachains_constants::rococo::currency::deposit; // Prints debug output of the `contracts` pallet to stdout if the node is // started with `-lruntime::contracts=debug`. @@ -65,6 +66,8 @@ impl Config for Runtime { type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type MaxDelegateDependencies = ConstU32<32>; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; @@ -72,5 +75,6 @@ impl Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type Debug = (); type Environment = (); + type ApiVersion = (); type Xcm = pallet_xcm::Pallet; } diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index f1c5acb4952fb5de3d4cafdba856c2c539568e88..8ce46ccd34f108cbc1961a0f1c6e0e0482f0db23 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -29,7 +29,7 @@ mod contracts; mod weights; mod xcm_config; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::AggregateMessageOrigin; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; @@ -50,20 +50,18 @@ use frame_support::{ dispatch::DispatchClass, genesis_builder_helper::{build_config, create_default_config}, parameter_types, - traits::{ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8}, + traits::{ConstBool, ConstU16, ConstU32, ConstU64, ConstU8}, weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::limits::{BlockLength, BlockWeights}; pub use parachains_common as common; use parachains_common::{ - impls::DealWithFees, - message_queue::*, - rococo::{consensus::*, currency::*, fee::WeightToFee}, - AccountId, BlockNumber, Hash, Header, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, - MAXIMUM_BLOCK_WEIGHT, MINUTES, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + impls::DealWithFees, message_queue::*, AccountId, BlockNumber, Hash, Header, Nonce, Signature, + AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; pub use parachains_common::{AuraId, Balance}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::CollatorSelectionUpdateOrigin; #[cfg(any(feature = "std", test))] @@ -82,8 +80,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -95,7 +93,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -105,6 +103,8 @@ pub type Migrations = ( pallet_contracts::Migration, // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); type EventRecord = frame_system::EventRecord< @@ -133,7 +133,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("contracts-rococo"), impl_name: create_runtime_str!("contracts-rococo"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, @@ -193,6 +193,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = pallet_timestamp::weights::SubstrateWeight; } @@ -202,6 +205,10 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (CollatorSelection,); } +parameter_types! { + pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; +} + impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; /// The type for recording an account's balance. @@ -209,7 +216,7 @@ impl pallet_balances::Config for Runtime { /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; type DustRemoval = (); - type ExistentialDeposit = ConstU128; + type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight; type MaxReserves = ConstU32<50>; @@ -217,7 +224,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<0>; } @@ -234,6 +240,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } parameter_types! { @@ -276,15 +283,17 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type OutboundXcmpMessageSource = XcmpQueue; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + impl pallet_insecure_randomness_collective_flip::Config for Runtime {} impl parachain_info::Config for Runtime {} @@ -340,9 +349,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -415,6 +424,7 @@ construct_runtime!( mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] @@ -423,6 +433,7 @@ mod benches { [pallet_sudo, Sudo] [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] + [cumulus_pallet_parachain_system, ParachainSystem] [pallet_contracts, Contracts] [pallet_xcm, PalletXcmExtrinsicsBenchmark::] ); @@ -431,7 +442,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -439,6 +450,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -448,7 +468,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -668,6 +688,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -685,6 +706,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -699,9 +721,22 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + xcm_config::RelayLocation::get(), + ExistentialDeposit::get() + ).into()); + } + use xcm::latest::prelude::*; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -710,7 +745,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between Contracts-System-Para and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -733,6 +768,13 @@ impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(EXISTENTIAL_DEPOSIT), + } + } } let whitelist: Vec = vec![ diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index e2cf2c8e51a08e212f85dc57a8a8143aba26d769..e8f3209eb67f627efccc9e8a798da03b6a2280c0 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -17,7 +17,6 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, }; -use crate::common::rococo::currency::CENTS; use cumulus_primitives_core::AggregateMessageOrigin; use frame_support::{ parameter_types, @@ -36,17 +35,17 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::AccountIdConversion; +use testnet_parachains_constants::rococo::currency::CENTS; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -79,8 +78,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type CurrencyTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -198,6 +196,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM location. diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml index c4544f525a217b8ea373cc9103675ff56f6485b5..70a6a81bdcfb97c96894b07a880bdadf0f39161e 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml @@ -15,10 +15,9 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex-literal = "0.4.1" -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -smallvec = "1.11.0" +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -57,7 +56,6 @@ sp-version = { path = "../../../../../substrate/primitives/version", default-fea # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false } @@ -71,11 +69,13 @@ cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } [features] default = ["std"] @@ -86,6 +86,7 @@ std = [ "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", @@ -113,7 +114,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "rococo-runtime-constants/std", @@ -133,6 +133,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", @@ -155,6 +156,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs index a46051212dee81eae2fee57792e949f70eb632db..742dd50f6fa1f421d6ce4abf221e05f6902cc2ae 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs @@ -30,8 +30,8 @@ use parachains_common::{AccountId, Balance, BlockNumber}; use xcm::latest::prelude::*; pub struct CreditToCollatorPot; -impl OnUnbalanced> for CreditToCollatorPot { - fn on_nonzero_unbalanced(credit: Credit) { +impl OnUnbalanced> for CreditToCollatorPot { + fn on_nonzero_unbalanced(credit: Credit) { let staking_pot = CollatorSelection::account_id(); let _ = >::resolve(&staking_pot, credit); } @@ -80,7 +80,7 @@ pub struct CoretimeAllocator; impl CoretimeInterface for CoretimeAllocator { type AccountId = AccountId; type Balance = Balance; - type RealyChainBlockNumberProvider = RelaychainDataProvider; + type RelayChainBlockNumberProvider = RelaychainDataProvider; fn request_core_count(count: CoreIndex) { use crate::coretime::CoretimeProviderCalls::RequestCoreCount; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index c5843c1ef293767799d79954eb90798a23253695..9913e47a93ab940df330c9ab0a6bef9c90da6f2d 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -33,7 +33,7 @@ mod coretime; mod weights; pub mod xcm_config; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ construct_runtime, derive_impl, @@ -52,9 +52,8 @@ use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use parachains_common::{ impls::DealWithFees, message_queue::{NarrowOriginToSibling, ParaIdToSibling}, - rococo::{consensus::*, currency::*, fee::WeightToFee}, AccountId, AuraId, Balance, BlockNumber, Hash, Header, Nonce, Signature, - AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use sp_api::impl_runtime_apis; @@ -71,6 +70,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::latest::prelude::*; use xcm_config::{ @@ -89,8 +89,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -103,10 +103,14 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. -pub type Migrations = (cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4,); +pub type Migrations = ( + cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, +); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -129,7 +133,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("coretime-rococo"), impl_name: create_runtime_str!("coretime-rococo"), authoring_version: 1, - spec_version: 1_006_001, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -188,6 +192,8 @@ impl frame_system::Config for Runtime { type DbWeight = RocksDbWeight; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = weights::frame_system::WeightInfo; + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -202,6 +208,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -228,7 +237,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -245,6 +253,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -263,15 +272,17 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedDmpWeight = ReservedDmpWeight; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + parameter_types! { pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; } @@ -360,9 +371,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -487,7 +498,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -495,6 +506,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -504,7 +524,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -698,6 +718,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -706,7 +732,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -717,6 +743,13 @@ impl_runtime_apis! { // Reserve transfers are disabled None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } parameter_types! { @@ -804,6 +837,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(RocRelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..aac73680ad1296eaec9073277b1779b3b9f4d898 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ +// --chain=coretime-rococo-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} 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 f1050b3ae636261ff21674c3bb34c05bf6d232c5..7b6ab611e6f3bf48c69b2945120c5f5118854808 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs @@ -22,6 +22,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_broker; pub mod pallet_collator_selection; @@ -29,6 +30,7 @@ pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs index bb9d7b3fe8ab01dc06b5358ac6ecb84d8b5f6270..aac7e10936615fddf39f9c306121dd9eda826ec6 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_balances.rs @@ -16,26 +16,24 @@ //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-8idpd4bs-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: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_balances -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=coretime-rococo-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ @@ -56,8 +54,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 45_258_000 picoseconds. - Weight::from_parts(46_265_000, 0) + // Minimum execution time: 41_557_000 picoseconds. + Weight::from_parts(42_618_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -68,8 +66,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 35_639_000 picoseconds. - Weight::from_parts(36_170_000, 0) + // Minimum execution time: 33_046_000 picoseconds. + Weight::from_parts(33_550_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -80,8 +78,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 12_342_000 picoseconds. - Weight::from_parts(12_736_000, 0) + // Minimum execution time: 11_804_000 picoseconds. + Weight::from_parts(12_007_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -92,8 +90,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 17_150_000 picoseconds. - Weight::from_parts(17_764_000, 0) + // Minimum execution time: 16_261_000 picoseconds. + Weight::from_parts(16_655_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -104,8 +102,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 46_745_000 picoseconds. - Weight::from_parts(47_693_000, 0) + // Minimum execution time: 42_967_000 picoseconds. + Weight::from_parts(43_870_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -116,8 +114,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 44_553_000 picoseconds. - Weight::from_parts(45_113_000, 0) + // Minimum execution time: 41_022_000 picoseconds. + Weight::from_parts(41_475_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -128,8 +126,8 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 15_439_000 picoseconds. - Weight::from_parts(15_832_000, 0) + // Minimum execution time: 14_339_000 picoseconds. + Weight::from_parts(14_641_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -141,13 +139,24 @@ impl pallet_balances::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 15_017_000 picoseconds. - Weight::from_parts(15_286_000, 0) + // Minimum execution time: 14_241_000 picoseconds. + Weight::from_parts(14_463_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 11_887 - .saturating_add(Weight::from_parts(13_536_178, 0).saturating_mul(u.into())) + // Standard Error: 12_290 + .saturating_add(Weight::from_parts(12_903_900, 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())) } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_116_000 picoseconds. + Weight::from_parts(5_345_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..29d48abab8956c79724b9544b99f06ef9303abaa --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ +// --chain=coretime-rococo-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs index 0e34cba4aaf5d9879dfecc97ffd736b985c98f23..c5d315467c1ed8b2aabf7ac18abe10931a02951b 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_xcm.rs @@ -16,26 +16,24 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-rococo-dev -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=coretime-rococo-dev // --header=./cumulus/file_header.txt // --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ @@ -64,8 +62,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 22_669_000 picoseconds. - Weight::from_parts(23_227_000, 0) + // Minimum execution time: 35_051_000 picoseconds. + Weight::from_parts(35_200_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -86,8 +84,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 64_486_000 picoseconds. - Weight::from_parts(65_247_000, 0) + // Minimum execution time: 56_235_000 picoseconds. + Weight::from_parts(58_178_000, 0) .saturating_add(Weight::from_parts(0, 3571)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) @@ -128,8 +126,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_020_000 picoseconds. - Weight::from_parts(7_300_000, 0) + // Minimum execution time: 6_226_000 picoseconds. + Weight::from_parts(6_403_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -139,8 +137,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_022_000 picoseconds. - Weight::from_parts(2_141_000, 0) + // Minimum execution time: 2_020_000 picoseconds. + Weight::from_parts(2_100_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -164,8 +162,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 26_893_000 picoseconds. - Weight::from_parts(27_497_000, 0) + // Minimum execution time: 24_387_000 picoseconds. + Weight::from_parts(24_814_000, 0) .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) @@ -188,8 +186,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `292` // Estimated: `3757` - // Minimum execution time: 29_673_000 picoseconds. - Weight::from_parts(30_693_000, 0) + // Minimum execution time: 27_039_000 picoseconds. + Weight::from_parts(27_693_000, 0) .saturating_add(Weight::from_parts(0, 3757)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) @@ -200,45 +198,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_990_000 picoseconds. - Weight::from_parts(2_105_000, 0) + // Minimum execution time: 1_920_000 picoseconds. + Weight::from_parts(2_082_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `PolkadotXcm::SupportedVersion` (r:4 w:2) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 14_819_000 picoseconds. - Weight::from_parts(15_180_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 17_141_000 picoseconds. + Weight::from_parts(17_500_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifiers` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 14_935_000 picoseconds. - Weight::from_parts(15_335_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 17_074_000 picoseconds. + Weight::from_parts(17_431_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 16_278_000 picoseconds. - Weight::from_parts(16_553_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 19_139_000 picoseconds. + Weight::from_parts(19_474_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:2 w:1) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -256,36 +254,36 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `142` // Estimated: `6082` - // Minimum execution time: 26_360_000 picoseconds. - Weight::from_parts(26_868_000, 0) + // Minimum execution time: 24_346_000 picoseconds. + Weight::from_parts(25_318_000, 0) .saturating_add(Weight::from_parts(0, 6082)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:3 w:0) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_615_000 picoseconds. - Weight::from_parts(8_903_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 11_777_000 picoseconds. + Weight::from_parts(12_051_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 15_284_000 picoseconds. - Weight::from_parts(15_504_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 17_538_000 picoseconds. + Weight::from_parts(17_832_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:2) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) /// 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`) @@ -299,12 +297,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `148` - // Estimated: `11038` - // Minimum execution time: 32_675_000 picoseconds. - Weight::from_parts(33_816_000, 0) - .saturating_add(Weight::from_parts(0, 11038)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `142` + // Estimated: `13507` + // Minimum execution time: 33_623_000 picoseconds. + Weight::from_parts(34_186_000, 0) + .saturating_add(Weight::from_parts(0, 13507)) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -315,8 +313,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `1517` - // Minimum execution time: 4_058_000 picoseconds. - Weight::from_parts(4_170_000, 0) + // Minimum execution time: 3_363_000 picoseconds. + Weight::from_parts(3_511_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -327,10 +325,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7669` // Estimated: `11134` - // Minimum execution time: 25_375_000 picoseconds. - Weight::from_parts(26_026_000, 0) + // Minimum execution time: 23_969_000 picoseconds. + Weight::from_parts(24_347_000, 0) .saturating_add(Weight::from_parts(0, 11134)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 34_071_000 picoseconds. + Weight::from_parts(35_031_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs index 8815312f3042801306b830a7a48ad659c12100a8..9f79cea831aed66a0d073109233731751cdf99ed 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs @@ -196,7 +196,7 @@ impl XcmWeightInfo for CoretimeRococoXcmWeight { XcmGeneric::::clear_transact_status() } fn universal_origin(_: &Junction) -> Weight { - XcmGeneric::::universal_origin() + Weight::MAX } fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { Weight::MAX diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index a11d049a3fc5c6e66428063a9b6c0a3626f1f6ae..719e7543e8886a803f773126eafbc77f34749ddb 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -312,16 +312,6 @@ impl WeightInfo { // Minimum execution time: 2_082_000 picoseconds. Weight::from_parts(2_144_000, 0) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - pub fn universal_origin() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 4_275_000 picoseconds. - Weight::from_parts(4_381_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) - } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs index 927d8f0a78e6353ed9cafaab5ac6e77978dc23cd..37bb8809dabac0ce52192dc64d0aa54ee136dc25 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -15,11 +15,12 @@ // along with Cumulus. If not, see . use super::{ - AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, + AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, Broker, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, }; use frame_support::{ + pallet_prelude::PalletInfoAccess, parameter_types, traits::{ConstU32, Contains, Equals, Everything, Nothing}, }; @@ -37,15 +38,14 @@ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::AccountIdConversion; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, + NonFungibleAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -56,6 +56,8 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); + pub BrokerPalletLocation: Location = + PalletInstance(::index() as u8).into(); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; pub const GovernanceLocation: Location = Location::parent(); @@ -75,8 +77,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type CurrencyTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -90,6 +91,23 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; +/// Means for transacting coretime regions on this chain. +pub type RegionTransactor = NonFungibleAdapter< + // Use this non-fungible implementation: + Broker, + // This adapter will handle coretime regions from the broker pallet. + IsConcrete, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports. + (), +>; + +/// Means for transacting assets on this chain. +pub type AssetTransactors = (CurrencyTransactor, RegionTransactor); + /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with XCM's `Transact`. There is an `OriginKind` that can /// bias the kind of local `Origin` it will become. @@ -205,7 +223,7 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; // Coretime chain does not recognize a reserve location for any asset. Users must teleport ROC // where allowed (e.g. with the Relay Chain). @@ -238,6 +256,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml index 02e130613f9c63896c54c075cc4c326586c14e37..549ef7ce4d78f54ea8fa88711a00e25966179dd0 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml @@ -15,10 +15,9 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex-literal = "0.4.1" -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -smallvec = "1.11.0" +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -32,9 +31,9 @@ pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = pallet-authorship = { path = "../../../../../substrate/frame/authorship", default-features = false } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false } +pallet-broker = { path = "../../../../../substrate/frame/broker", default-features = false } pallet-multisig = { path = "../../../../../substrate/frame/multisig", default-features = false } pallet-session = { path = "../../../../../substrate/frame/session", default-features = false } -pallet-sudo = { path = "../../../../../substrate/frame/sudo", default-features = false } pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false } pallet-transaction-payment = { path = "../../../../../substrate/frame/transaction-payment", default-features = false } pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } @@ -56,7 +55,6 @@ sp-version = { path = "../../../../../substrate/primitives/version", default-fea # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } westend-runtime-constants = { path = "../../../../../polkadot/runtime/westend/constants", default-features = false } @@ -70,11 +68,13 @@ cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["westend"] } [features] default = ["std"] @@ -85,6 +85,7 @@ std = [ "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", @@ -98,11 +99,11 @@ std = [ "pallet-aura/std", "pallet-authorship/std", "pallet-balances/std", + "pallet-broker/std", "pallet-collator-selection/std", "pallet-message-queue/std", "pallet-multisig/std", "pallet-session/std", - "pallet-sudo/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", @@ -111,7 +112,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", @@ -130,6 +130,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", @@ -147,11 +148,12 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-broker/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", - "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", @@ -175,11 +177,11 @@ try-runtime = [ "pallet-aura/try-runtime", "pallet-authorship/try-runtime", "pallet-balances/try-runtime", + "pallet-broker/try-runtime", "pallet-collator-selection/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", "pallet-session/try-runtime", - "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-utility/try-runtime", @@ -190,3 +192,5 @@ try-runtime = [ ] experimental = ["pallet-aura/experimental"] + +fast-runtime = [] diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/build.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/build.rs index 60f8a125129ff1344a1799246e931acdb1d139d5..28dacd20cf305ebdbc57eb2a30e3c98e4f8853d9 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/build.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/build.rs @@ -19,7 +19,15 @@ fn main() { .with_current_project() .export_heap_base() .import_memory() - .build() + .build(); + + substrate_wasm_builder::WasmBuilder::new() + .with_current_project() + .set_file_name("fast_runtime_binary.rs") + .enable_feature("fast-runtime") + .import_memory() + .export_heap_base() + .build(); } #[cfg(not(feature = "std"))] diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs new file mode 100644 index 0000000000000000000000000000000000000000..41cbc62fa2115ff3828e6910b750622a91ff0251 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs @@ -0,0 +1,249 @@ +// Copyright 2022 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::*; +use codec::{Decode, Encode}; +use cumulus_pallet_parachain_system::RelaychainDataProvider; +use cumulus_primitives_core::relay_chain; +use frame_support::{ + parameter_types, + traits::{ + fungible::{Balanced, Credit}, + OnUnbalanced, + }, +}; +use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600, RCBlockNumberOf}; +use parachains_common::{AccountId, Balance, BlockNumber}; +use xcm::latest::prelude::*; + +pub struct CreditToCollatorPot; +impl OnUnbalanced> for CreditToCollatorPot { + fn on_nonzero_unbalanced(credit: Credit) { + let staking_pot = CollatorSelection::account_id(); + let _ = >::resolve(&staking_pot, credit); + } +} + +/// A type containing the encoding of the coretime pallet in the Relay chain runtime. Used to +/// construct any remote calls. The codec index must correspond to the index of `Coretime` in the +/// `construct_runtime` of the Relay chain. +#[derive(Encode, Decode)] +enum RelayRuntimePallets { + #[codec(index = 66)] + Coretime(CoretimeProviderCalls), +} + +/// Call encoding for the calls needed from the relay coretime pallet. +#[derive(Encode, Decode)] +enum CoretimeProviderCalls { + #[codec(index = 1)] + RequestCoreCount(CoreIndex), + #[codec(index = 2)] + RequestRevenueInfoAt(relay_chain::BlockNumber), + #[codec(index = 3)] + CreditAccount(AccountId, Balance), + #[codec(index = 4)] + AssignCore( + CoreIndex, + relay_chain::BlockNumber, + Vec<(CoreAssignment, PartsOf57600)>, + Option, + ), +} + +parameter_types! { + pub const BrokerPalletId: PalletId = PalletId(*b"py/broke"); +} + +parameter_types! { + pub storage CoreCount: Option = None; + pub storage CoretimeRevenue: Option<(BlockNumber, Balance)> = None; +} + +/// Type that implements the `CoretimeInterface` for the allocation of Coretime. Meant to operate +/// from the parachain context. That is, the parachain provides a market (broker) for the sale of +/// coretime, but assumes a `CoretimeProvider` (i.e. a Relay Chain) to actually provide cores. +pub struct CoretimeAllocator; +impl CoretimeInterface for CoretimeAllocator { + type AccountId = AccountId; + type Balance = Balance; + type RelayChainBlockNumberProvider = RelaychainDataProvider; + + fn request_core_count(count: CoreIndex) { + use crate::coretime::CoretimeProviderCalls::RequestCoreCount; + let request_core_count_call = RelayRuntimePallets::Coretime(RequestCoreCount(count)); + + // Weight for `request_core_count` from westend benchmarks: + // `ref_time` = 7889000 + (3 * 25000000) + (1 * 100000000) = 182889000 + // `proof_size` = 1636 + // Add 5% to each component and round to 2 significant figures. + let call_weight = Weight::from_parts(190_000_000, 1700); + + let message = Xcm(vec![ + Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }, + Instruction::Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: call_weight, + call: request_core_count_call.encode().into(), + }, + ]); + + match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) { + Ok(_) => log::debug!( + target: "runtime::coretime", + "Request to update schedulable cores sent successfully." + ), + Err(e) => log::error!( + target: "runtime::coretime", + "Failed to send request to update schedulable cores: {:?}", + e + ), + } + } + + fn request_revenue_info_at(when: RCBlockNumberOf) { + use crate::coretime::CoretimeProviderCalls::RequestRevenueInfoAt; + let request_revenue_info_at_call = + RelayRuntimePallets::Coretime(RequestRevenueInfoAt(when)); + + let message = Xcm(vec![ + Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }, + Instruction::Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: Weight::from_parts(1000000000, 200000), + call: request_revenue_info_at_call.encode().into(), + }, + ]); + + match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) { + Ok(_) => log::debug!( + target: "runtime::coretime", + "Request for revenue information sent successfully." + ), + Err(e) => log::error!( + target: "runtime::coretime", + "Request for revenue information failed to send: {:?}", + e + ), + } + } + + fn credit_account(who: Self::AccountId, amount: Self::Balance) { + use crate::coretime::CoretimeProviderCalls::CreditAccount; + let credit_account_call = RelayRuntimePallets::Coretime(CreditAccount(who, amount)); + + let message = Xcm(vec![ + Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }, + Instruction::Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: Weight::from_parts(1000000000, 200000), + call: credit_account_call.encode().into(), + }, + ]); + + match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) { + Ok(_) => log::debug!( + target: "runtime::coretime", + "Instruction to credit account sent successfully." + ), + Err(e) => log::error!( + target: "runtime::coretime", + "Instruction to credit account failed to send: {:?}", + e + ), + } + } + + fn assign_core( + core: CoreIndex, + begin: RCBlockNumberOf, + assignment: Vec<(CoreAssignment, PartsOf57600)>, + end_hint: Option>, + ) { + use crate::coretime::CoretimeProviderCalls::AssignCore; + let assign_core_call = + RelayRuntimePallets::Coretime(AssignCore(core, begin, assignment, end_hint)); + + // Weight for `assign_core` from westend benchmarks: + // `ref_time` = 10177115 + (1 * 25000000) + (2 * 100000000) + (57600 * 13932) = 937660315 + // `proof_size` = 3612 + // Add 5% to each component and round to 2 significant figures. + let call_weight = Weight::from_parts(980_000_000, 3800); + + let message = Xcm(vec![ + Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }, + Instruction::Transact { + origin_kind: OriginKind::Native, + require_weight_at_most: call_weight, + call: assign_core_call.encode().into(), + }, + ]); + + match PolkadotXcm::send_xcm(Here, Location::parent(), message.clone()) { + Ok(_) => log::debug!( + target: "runtime::coretime", + "Core assignment sent successfully." + ), + Err(e) => log::error!( + target: "runtime::coretime", + "Core assignment failed to send: {:?}", + e + ), + } + } + + fn check_notify_revenue_info() -> Option<(RCBlockNumberOf, Self::Balance)> { + let revenue = CoretimeRevenue::get(); + CoretimeRevenue::set(&None); + revenue + } + + #[cfg(feature = "runtime-benchmarks")] + fn ensure_notify_revenue_info(when: RCBlockNumberOf, revenue: Self::Balance) { + CoretimeRevenue::set(&Some((when, revenue))); + } +} + +impl pallet_broker::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type OnRevenue = CreditToCollatorPot; + #[cfg(feature = "fast-runtime")] + type TimeslicePeriod = ConstU32<10>; + #[cfg(not(feature = "fast-runtime"))] + type TimeslicePeriod = ConstU32<80>; + // We don't actually need any leases at launch but set to 10 in case we want to sudo some in. + type MaxLeasedCores = ConstU32<10>; + type MaxReservedCores = ConstU32<10>; + type Coretime = CoretimeAllocator; + type ConvertBalance = sp_runtime::traits::Identity; + type WeightInfo = weights::pallet_broker::WeightInfo; + type PalletId = BrokerPalletId; + type AdminOrigin = EnsureRoot; + type PriceAdapter = pallet_broker::Linear; +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index bc6b6b3caf1572d3e1ea640d189a08497d920d67..1a330821d3fcac112426bb9f8591190ec0d6428b 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -21,10 +21,19 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +/// Provides the `WASM_BINARY` build with `fast-runtime` feature enabled. +/// +/// This is for example useful for local test chains. +#[cfg(feature = "std")] +pub mod fast_runtime_binary { + include!(concat!(env!("OUT_DIR"), "/fast_runtime_binary.rs")); +} + +mod coretime; mod weights; pub mod xcm_config; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ construct_runtime, derive_impl, @@ -43,9 +52,8 @@ use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use parachains_common::{ impls::DealWithFees, message_queue::{NarrowOriginToSibling, ParaIdToSibling}, - westend::{consensus::*, currency::*, fee::WeightToFee}, AccountId, AuraId, Balance, BlockNumber, Hash, Header, Nonce, Signature, - AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use sp_api::impl_runtime_apis; @@ -62,10 +70,11 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::latest::prelude::*; use xcm_config::{ - FellowshipLocation, GovernanceLocation, WndRelayLocation, XcmOriginToTransactDispatchOrigin, + FellowshipLocation, GovernanceLocation, TokenRelayLocation, XcmOriginToTransactDispatchOrigin, }; /// The address format for describing accounts. @@ -80,8 +89,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -94,10 +103,14 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. -pub type Migrations = (); +pub type Migrations = ( + cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, +); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -120,7 +133,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("coretime-westend"), impl_name: create_runtime_str!("coretime-westend"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -179,6 +192,8 @@ impl frame_system::Config for Runtime { type DbWeight = RocksDbWeight; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = weights::frame_system::WeightInfo; + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -193,6 +208,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -219,7 +237,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -236,6 +253,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -254,16 +272,16 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedDmpWeight = ReservedDmpWeight; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; } -impl parachain_info::Config for Runtime {} +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; parameter_types! { pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; @@ -291,6 +309,8 @@ impl pallet_message_queue::Config for Runtime { type ServiceWeight = MessageQueueServiceWeight; } +impl parachain_info::Config for Runtime {} + impl cumulus_pallet_aura_ext::Config for Runtime {} parameter_types! { @@ -306,7 +326,7 @@ pub type RootOrFellows = EitherOfDiverse< parameter_types! { /// The asset ID for the asset that we use to pay for message delivery fees. - pub FeeAssetId: AssetId = AssetId(WndRelayLocation::get()); + pub FeeAssetId: AssetId = AssetId(TokenRelayLocation::get()); /// The base fee for the message delivery fees. pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); } @@ -351,9 +371,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -409,12 +429,6 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } -impl pallet_sudo::Config for Runtime { - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_sudo::weights::SubstrateWeight; -} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -446,22 +460,19 @@ construct_runtime!( Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, - // Sudo - Sudo: pallet_sudo = 100, + // The main stage. + Broker: pallet_broker = 50, } ); -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] [cumulus_pallet_parachain_system, ParachainSystem] [pallet_timestamp, Timestamp] [pallet_balances, Balances] + [pallet_broker, Broker] [pallet_collator_selection, CollatorSelection] [pallet_session, SessionBench::] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -478,7 +489,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -486,6 +497,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -495,7 +515,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -685,10 +705,16 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::WndRelayLocation; + use xcm_config::TokenRelayLocation; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -697,7 +723,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between AH and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -708,11 +734,18 @@ impl_runtime_apis! { // Reserve transfers are disabled None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } parameter_types! { pub ExistentialDepositAsset: Option = Some(( - WndRelayLocation::get(), + TokenRelayLocation::get(), ExistentialDeposit::get() ).into()); } @@ -726,13 +759,13 @@ impl_runtime_apis! { >; type AccountIdConverter = xcm_config::LocationToAccountId; fn valid_destination() -> Result { - Ok(WndRelayLocation::get()) + Ok(TokenRelayLocation::get()) } fn worst_case_holding(_depositable_count: u32) -> Assets { // just concrete assets according to relay chain. let assets: Vec = vec![ Asset { - id: AssetId(WndRelayLocation::get()), + id: AssetId(TokenRelayLocation::get()), fun: Fungible(1_000_000 * UNITS), } ]; @@ -742,8 +775,8 @@ impl_runtime_apis! { parameter_types! { pub const TrustedTeleporter: Option<(Location, Asset)> = Some(( - WndRelayLocation::get(), - Asset { fun: Fungible(UNITS), id: AssetId(WndRelayLocation::get()) }, + TokenRelayLocation::get(), + Asset { fun: Fungible(UNITS), id: AssetId(TokenRelayLocation::get()) }, )); pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; pub const TrustedReserve: Option<(Location, Asset)> = None; @@ -758,7 +791,7 @@ impl_runtime_apis! { fn get_asset() -> Asset { Asset { - id: AssetId(WndRelayLocation::get()), + id: AssetId(TokenRelayLocation::get()), fun: Fungible(UNITS), } } @@ -781,20 +814,27 @@ impl_runtime_apis! { } fn transact_origin_and_runtime_call() -> Result<(Location, RuntimeCall), BenchmarkError> { - Ok((WndRelayLocation::get(), frame_system::Call::remark_with_event { remark: vec![] }.into())) + Ok((TokenRelayLocation::get(), frame_system::Call::remark_with_event { remark: vec![] }.into())) } fn subscribe_origin() -> Result { - Ok(WndRelayLocation::get()) + Ok(TokenRelayLocation::get()) } fn claimable_asset() -> Result<(Location, Location, Assets), BenchmarkError> { - let origin = WndRelayLocation::get(); - let assets: Assets = (AssetId(WndRelayLocation::get()), 1_000 * UNITS).into(); + let origin = TokenRelayLocation::get(); + let assets: Assets = (AssetId(TokenRelayLocation::get()), 1_000 * UNITS).into(); let ticket = Location { parents: 0, interior: Here }; Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(TokenRelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_parachain_system.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_parachain_system.rs index 0303151d7f83dfc5957e7346b1c4ef2950b6dc01..1c9119361985cd541ca42dce6eb8d2f355a29c6c 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_parachain_system.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_parachain_system.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -14,6 +14,31 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! Autogenerated weights for `cumulus_pallet_parachain_system` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=coretime-westend-dev +// --wasm-execution=compiled +// --pallet=cumulus_pallet_parachain_system +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ + #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] @@ -22,32 +47,31 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions for `cumulus_pallet_xcmp_queue`. +/// Weight functions for `cumulus_pallet_parachain_system`. pub struct WeightInfo(PhantomData); impl cumulus_pallet_parachain_system::WeightInfo for WeightInfo { - /// Storage: ParachainSystem LastDmqMqcHead (r:1 w:1) - /// Proof Skipped: ParachainSystem LastDmqMqcHead (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParachainSystem ReservedDmpWeightOverride (r:1 w:0) - /// Proof Skipped: ParachainSystem ReservedDmpWeightOverride (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) - /// Storage: ParachainSystem ProcessedDownwardMessages (r:0 w:1) - /// Proof Skipped: ParachainSystem ProcessedDownwardMessages (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: MessageQueue Pages (r:0 w:16) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Storage: `ParachainSystem::LastDmqMqcHead` (r:1 w:1) + /// Proof: `ParachainSystem::LastDmqMqcHead` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ProcessedDownwardMessages` (r:0 w:1) + /// Proof: `ParachainSystem::ProcessedDownwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::Pages` (r:0 w:1000) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn enqueue_inbound_downward_messages(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `12` - // Estimated: `8013` - // Minimum execution time: 1_645_000 picoseconds. - Weight::from_parts(1_717_000, 0) - .saturating_add(Weight::from_parts(0, 8013)) - // Standard Error: 12_258 - .saturating_add(Weight::from_parts(24_890_934, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `48` + // Estimated: `3517` + // Minimum execution time: 2_080_000 picoseconds. + Weight::from_parts(2_157_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + // Standard Error: 33_906 + .saturating_add(Weight::from_parts(196_603_239, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) } -} \ No newline at end of file +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_xcmp_queue.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_xcmp_queue.rs index 124571118aa129e1489aaaf1ebeabbde41ed13c4..0b0524339aa761053a7c559a3a2ab725c0cb6c22 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_xcmp_queue.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_xcmp_queue.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `cumulus_pallet_xcmp_queue` //! -//! 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-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=cumulus_pallet_xcmp_queue +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/cumulus_pallet_xcmp_queue.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,14 +50,14 @@ use core::marker::PhantomData; /// Weight functions for `cumulus_pallet_xcmp_queue`. pub struct WeightInfo(PhantomData); impl cumulus_pallet_xcmp_queue::WeightInfo for WeightInfo { - /// Storage: XcmpQueue QueueConfig (r:1 w:1) - /// Proof Skipped: XcmpQueue QueueConfig (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `XcmpQueue::QueueConfig` (r:1 w:1) + /// Proof: `XcmpQueue::QueueConfig` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_config_with_u32() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1561` - // Minimum execution time: 5_621_000 picoseconds. - Weight::from_parts(5_845_000, 0) + // Minimum execution time: 3_796_000 picoseconds. + Weight::from_parts(4_027_000, 0) .saturating_add(Weight::from_parts(0, 1561)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -74,8 +76,8 @@ impl cumulus_pallet_xcmp_queue::WeightInfo for WeightIn // Proof Size summary in bytes: // Measured: `82` // Estimated: `3517` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 0) + // Minimum execution time: 9_990_000 picoseconds. + Weight::from_parts(10_439_000, 0) .saturating_add(Weight::from_parts(0, 3517)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -86,8 +88,8 @@ impl cumulus_pallet_xcmp_queue::WeightInfo for WeightIn // Proof Size summary in bytes: // Measured: `76` // Estimated: `1561` - // Minimum execution time: 3_000_000 picoseconds. - Weight::from_parts(3_000_000, 0) + // Minimum execution time: 2_394_000 picoseconds. + Weight::from_parts(2_493_000, 0) .saturating_add(Weight::from_parts(0, 1561)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -98,8 +100,8 @@ impl cumulus_pallet_xcmp_queue::WeightInfo for WeightIn // Proof Size summary in bytes: // Measured: `111` // Estimated: `1596` - // Minimum execution time: 4_000_000 picoseconds. - Weight::from_parts(4_000_000, 0) + // Minimum execution time: 3_283_000 picoseconds. + Weight::from_parts(3_388_000, 0) .saturating_add(Weight::from_parts(0, 1596)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -108,14 +110,14 @@ impl cumulus_pallet_xcmp_queue::WeightInfo for WeightIn // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 44_000_000 picoseconds. - Weight::from_parts(45_000_000, 0) + // Minimum execution time: 5_974_000 picoseconds. + Weight::from_parts(6_166_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) - /// Storage: `XcmpQueue::InboundXcmpMessages` (r:1 w:1) - /// Proof: `XcmpQueue::InboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) @@ -130,20 +132,22 @@ impl cumulus_pallet_xcmp_queue::WeightInfo for WeightIn // Proof Size summary in bytes: // Measured: `65711` // Estimated: `69176` - // Minimum execution time: 67_000_000 picoseconds. - Weight::from_parts(73_000_000, 0) + // Minimum execution time: 117_856_000 picoseconds. + Weight::from_parts(119_808_000, 0) .saturating_add(Weight::from_parts(0, 69176)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6b345d8e88afa015075c945637c07e8f20` (r:1 w:1) - fn on_idle_large_msg() -> Weight { + /// Storage: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x7b3237373ffdfeb1cab4222e3b520d6bedc49980ba3aa32b0a189290fd036649` (r:1 w:1) + fn on_idle_large_msg() -> Weight { // Proof Size summary in bytes: // Measured: `65710` // Estimated: `69175` - // Minimum execution time: 49_000_000 picoseconds. - Weight::from_parts(55_000_000, 0) + // Minimum execution time: 52_555_000 picoseconds. + Weight::from_parts(54_052_000, 0) .saturating_add(Weight::from_parts(0, 69175)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system.rs index 46f8113939e4d4fa3f26ff03d665eec6b4120a6b..b4b7cbf05a5ec757bc3a60e08ef62678029b47f2 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `frame_system` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=frame_system +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -53,80 +55,99 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_432_000 picoseconds. - Weight::from_parts(2_458_000, 0) + // Minimum execution time: 1_584_000 picoseconds. + Weight::from_parts(2_117_975, 0) .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 0 - .saturating_add(Weight::from_parts(367, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(384, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_911_000 picoseconds. - Weight::from_parts(8_031_000, 0) + // Minimum execution time: 4_607_000 picoseconds. + Weight::from_parts(14_948_582, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_405, 0).saturating_mul(b.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_673, 0).saturating_mul(b.into())) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a686561707061676573` (r:0 w:1) - /// Proof Skipped: unknown `0x3a686561707061676573` (r:0 w:1) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) fn set_heap_pages() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 4_304_000 picoseconds. - Weight::from_parts(4_553_000, 0) + // Minimum execution time: 2_681_000 picoseconds. + Weight::from_parts(2_877_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`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::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_code() -> Weight { - Weight::from_parts(1_000_000, 0) + // Proof Size summary in bytes: + // Measured: `164` + // Estimated: `1649` + // Minimum execution time: 95_893_701_000 picoseconds. + Weight::from_parts(98_086_094_000, 0) + .saturating_add(Weight::from_parts(0, 1649)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn set_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_493_000 picoseconds. - Weight::from_parts(2_523_000, 0) + // Minimum execution time: 1_597_000 picoseconds. + Weight::from_parts(1_660_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1_594 - .saturating_add(Weight::from_parts(663_439, 0).saturating_mul(i.into())) + // Standard Error: 1_871 + .saturating_add(Weight::from_parts(748_346, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn kill_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_492_000 picoseconds. - Weight::from_parts(2_526_000, 0) + // Minimum execution time: 1_625_000 picoseconds. + Weight::from_parts(1_669_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 784 - .saturating_add(Weight::from_parts(493_844, 0).saturating_mul(i.into())) + // Standard Error: 903 + .saturating_add(Weight::from_parts(561_709, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `p` is `[0, 1000]`. fn kill_prefix(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `68 + p * (69 ±0)` - // Estimated: `66 + p * (70 ±0)` - // Minimum execution time: 4_200_000 picoseconds. - Weight::from_parts(4_288_000, 0) - .saturating_add(Weight::from_parts(0, 66)) - // Standard Error: 1_195 - .saturating_add(Weight::from_parts(1_021_563, 0).saturating_mul(p.into())) + // Measured: `71 + p * (69 ±0)` + // Estimated: `72 + p * (70 ±0)` + // Minimum execution time: 3_306_000 picoseconds. + Weight::from_parts(3_412_000, 0) + .saturating_add(Weight::from_parts(0, 72)) + // Standard Error: 1_366 + .saturating_add(Weight::from_parts(1_138_953, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) @@ -137,25 +158,33 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 33_027_000 picoseconds. - Weight::from_parts(33_027_000, 0) + // Minimum execution time: 7_834_000 picoseconds. + Weight::from_parts(8_344_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `System::AuthorizedUpgrade` (r:1 w:1) /// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) - /// Storage: `System::Digest` (r:1 w:1) - /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) - /// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `ParachainSystem::UpgradeRestrictionSignal` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::PendingValidationCode` (r:1 w:1) + /// Proof: `ParachainSystem::PendingValidationCode` (`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::NewValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::NewValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::DidSetValidationCode` (r:0 w:1) + /// Proof: `ParachainSystem::DidSetValidationCode` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn apply_authorized_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `22` - // Estimated: `1518` - // Minimum execution time: 118_101_992_000 picoseconds. - Weight::from_parts(118_101_992_000, 0) - .saturating_add(Weight::from_parts(0, 1518)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(3)) + // Measured: `186` + // Estimated: `1671` + // Minimum execution time: 98_682_277_000 picoseconds. + Weight::from_parts(101_609_257_000, 0) + .saturating_add(Weight::from_parts(0, 1671)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..0683158a01a646315d13016d019c9f590f10ef04 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ +// --chain=coretime-westend-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} 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 28e708f0e2a33da7419fe821ab1fb72806c273b1..7b6ab611e6f3bf48c69b2945120c5f5118854808 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,12 +22,15 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; +pub mod pallet_broker; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs index 65d5a55c72eab716b3688bdd50fae38c67587287..c4770a7c94381cfca9404a6577c703164f215918 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_balances` //! -//! 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-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=pallet_balances +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_balances.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,104 +50,115 @@ use core::marker::PhantomData; /// Weight functions for `pallet_balances`. pub struct WeightInfo(PhantomData); impl pallet_balances::WeightInfo for 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`) fn transfer_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 59_580_000 picoseconds. - Weight::from_parts(60_317_000, 0) + // Minimum execution time: 42_773_000 picoseconds. + Weight::from_parts(43_292_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 45_490_000 picoseconds. - Weight::from_parts(45_910_000, 0) + // Minimum execution time: 34_023_000 picoseconds. + Weight::from_parts(34_513_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// 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: `103` // Estimated: `3593` - // Minimum execution time: 17_353_000 picoseconds. - Weight::from_parts(17_676_000, 0) + // Minimum execution time: 11_685_000 picoseconds. + Weight::from_parts(12_103_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// 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: `103` // Estimated: `3593` - // Minimum execution time: 25_017_000 picoseconds. - Weight::from_parts(25_542_000, 0) + // Minimum execution time: 16_233_000 picoseconds. + Weight::from_parts(16_706_000, 0) .saturating_add(Weight::from_parts(0, 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`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 61_161_000 picoseconds. - Weight::from_parts(61_665_000, 0) + // Minimum execution time: 43_909_000 picoseconds. + Weight::from_parts(44_683_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// 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`) fn transfer_all() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 55_422_000 picoseconds. - Weight::from_parts(55_880_000, 0) + // Minimum execution time: 42_081_000 picoseconds. + Weight::from_parts(42_553_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// 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: `103` // Estimated: `3593` - // Minimum execution time: 20_477_000 picoseconds. - Weight::from_parts(20_871_000, 0) + // Minimum execution time: 14_413_000 picoseconds. + Weight::from_parts(14_827_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:999 w:999) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `u` is `[1, 1000]`. fn upgrade_accounts(u: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 19_501_000 picoseconds. - Weight::from_parts(19_726_000, 0) + // Minimum execution time: 14_189_000 picoseconds. + Weight::from_parts(14_587_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 9_495 - .saturating_add(Weight::from_parts(15_658_957, 0).saturating_mul(u.into())) + // Standard Error: 10_909 + .saturating_add(Weight::from_parts(13_040_864, 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())) } + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_218_000 picoseconds. + Weight::from_parts(5_562_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs new file mode 100644 index 0000000000000000000000000000000000000000..8727b9633b1f0eaca2bcaa5f7a43d832f6abbe9b --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs @@ -0,0 +1,516 @@ +// 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_broker` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=coretime-westend-dev +// --wasm-execution=compiled +// --pallet=pallet_broker +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/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_broker`. +pub struct WeightInfo(PhantomData); +impl pallet_broker::WeightInfo for WeightInfo { + /// Storage: `Broker::Configuration` (r:0 w:1) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + fn configure() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_944_000 picoseconds. + Weight::from_parts(2_045_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Reservations` (r:1 w:1) + /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(12021), added: 12516, mode: `MaxEncodedLen`) + fn reserve() -> Weight { + // Proof Size summary in bytes: + // Measured: `10888` + // Estimated: `13506` + // Minimum execution time: 21_158_000 picoseconds. + Weight::from_parts(21_572_000, 0) + .saturating_add(Weight::from_parts(0, 13506)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Reservations` (r:1 w:1) + /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(12021), added: 12516, mode: `MaxEncodedLen`) + fn unreserve() -> Weight { + // Proof Size summary in bytes: + // Measured: `12090` + // Estimated: `13506` + // Minimum execution time: 20_497_000 picoseconds. + Weight::from_parts(20_995_000, 0) + .saturating_add(Weight::from_parts(0, 13506)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(81), added: 576, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::LastRelayChainBlockNumber` (r:1 w:0) + /// Proof: `ParachainSystem::LastRelayChainBlockNumber` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_lease() -> Weight { + // Proof Size summary in bytes: + // Measured: `146` + // Estimated: `1631` + // Minimum execution time: 10_280_000 picoseconds. + Weight::from_parts(10_686_000, 0) + .saturating_add(Weight::from_parts(0, 1631)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::LastRelayChainBlockNumber` (r:1 w:0) + /// Proof: `ParachainSystem::LastRelayChainBlockNumber` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Broker::InstaPoolIo` (r:3 w:3) + /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Broker::Reservations` (r:1 w:0) + /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(12021), added: 12516, mode: `MaxEncodedLen`) + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(81), added: 576, mode: `MaxEncodedLen`) + /// Storage: `Broker::SaleInfo` (r:0 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:0 w:1) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:0 w:20) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 1000]`. + fn start_sales(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `12247` + // Estimated: `13732` + // Minimum execution time: 61_020_000 picoseconds. + Weight::from_parts(63_240_622, 0) + .saturating_add(Weight::from_parts(0, 13732)) + // Standard Error: 102 + .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(26)) + } + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::SaleInfo` (r:1 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Broker::Regions` (r:0 w:1) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + fn purchase() -> Weight { + // Proof Size summary in bytes: + // Measured: `316` + // Estimated: `3593` + // Minimum execution time: 30_627_000 picoseconds. + Weight::from_parts(31_648_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::SaleInfo` (r:1 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `Broker::AllowedRenewals` (r:1 w:2) + /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:0 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + fn renew() -> Weight { + // Proof Size summary in bytes: + // Measured: `434` + // Estimated: `4698` + // Minimum execution time: 57_701_000 picoseconds. + Weight::from_parts(59_825_000, 0) + .saturating_add(Weight::from_parts(0, 4698)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Broker::Regions` (r:1 w:1) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + fn transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `357` + // Estimated: `3550` + // Minimum execution time: 12_898_000 picoseconds. + Weight::from_parts(13_506_000, 0) + .saturating_add(Weight::from_parts(0, 3550)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Regions` (r:1 w:2) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + fn partition() -> Weight { + // Proof Size summary in bytes: + // Measured: `357` + // Estimated: `3550` + // Minimum execution time: 14_284_000 picoseconds. + Weight::from_parts(14_791_000, 0) + .saturating_add(Weight::from_parts(0, 3550)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Broker::Regions` (r:1 w:3) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + fn interlace() -> Weight { + // Proof Size summary in bytes: + // Measured: `357` + // Estimated: `3550` + // Minimum execution time: 15_570_000 picoseconds. + Weight::from_parts(16_158_000, 0) + .saturating_add(Weight::from_parts(0, 3550)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::Regions` (r:1 w:1) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:1 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + fn assign() -> Weight { + // Proof Size summary in bytes: + // Measured: `735` + // Estimated: `4681` + // Minimum execution time: 23_329_000 picoseconds. + Weight::from_parts(24_196_000, 0) + .saturating_add(Weight::from_parts(0, 4681)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::Regions` (r:1 w:1) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:1 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolIo` (r:2 w:2) + /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolContribution` (r:0 w:1) + /// Proof: `Broker::InstaPoolContribution` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + fn pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `801` + // Estimated: `5996` + // Minimum execution time: 29_288_000 picoseconds. + Weight::from_parts(30_066_000, 0) + .saturating_add(Weight::from_parts(0, 5996)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `Broker::InstaPoolContribution` (r:1 w:1) + /// Proof: `Broker::InstaPoolContribution` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolHistory` (r:3 w:1) + /// Proof: `Broker::InstaPoolHistory` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `m` is `[1, 3]`. + fn claim_revenue(m: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `652` + // Estimated: `6196 + m * (2520 ±0)` + // Minimum execution time: 54_833_000 picoseconds. + Weight::from_parts(55_577_423, 0) + .saturating_add(Weight::from_parts(0, 6196)) + // Standard Error: 35_105 + .saturating_add(Weight::from_parts(1_267_911, 0).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) + .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(Weight::from_parts(0, 2520).saturating_mul(m.into())) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// 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`) + fn purchase_credit() -> Weight { + // Proof Size summary in bytes: + // Measured: `215` + // Estimated: `3680` + // Minimum execution time: 55_289_000 picoseconds. + Weight::from_parts(56_552_000, 0) + .saturating_add(Weight::from_parts(0, 3680)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::Regions` (r:1 w:1) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + fn drop_region() -> Weight { + // Proof Size summary in bytes: + // Measured: `465` + // Estimated: `3550` + // Minimum execution time: 39_736_000 picoseconds. + Weight::from_parts(41_346_000, 0) + .saturating_add(Weight::from_parts(0, 3550)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolContribution` (r:1 w:1) + /// Proof: `Broker::InstaPoolContribution` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + fn drop_contribution() -> Weight { + // Proof Size summary in bytes: + // Measured: `463` + // Estimated: `3533` + // Minimum execution time: 57_319_000 picoseconds. + Weight::from_parts(60_204_000, 0) + .saturating_add(Weight::from_parts(0, 3533)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolHistory` (r:1 w:1) + /// Proof: `Broker::InstaPoolHistory` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn drop_history() -> Weight { + // Proof Size summary in bytes: + // Measured: `857` + // Estimated: `3593` + // Minimum execution time: 85_216_000 picoseconds. + Weight::from_parts(91_144_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Status` (r:1 w:0) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::AllowedRenewals` (r:1 w:1) + /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + fn drop_renewal() -> Weight { + // Proof Size summary in bytes: + // Measured: `556` + // Estimated: `4698` + // Minimum execution time: 32_331_000 picoseconds. + Weight::from_parts(39_877_000, 0) + .saturating_add(Weight::from_parts(0, 4698)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// 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`) + /// The range of component `n` is `[0, 1000]`. + fn request_core_count(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 18_128_000 picoseconds. + Weight::from_parts(19_061_234, 0) + .saturating_add(Weight::from_parts(0, 3539)) + // Standard Error: 48 + .saturating_add(Weight::from_parts(141, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Broker::CoreCountInbox` (r:1 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 1000]`. + fn process_core_count(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `266` + // Estimated: `1487` + // Minimum execution time: 5_368_000 picoseconds. + Weight::from_parts(5_837_005, 0) + .saturating_add(Weight::from_parts(0, 1487)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Storage: `Broker::InstaPoolHistory` (r:1 w:1) + /// Proof: `Broker::InstaPoolHistory` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn process_revenue() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `6196` + // Minimum execution time: 36_047_000 picoseconds. + Weight::from_parts(37_101_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Broker::InstaPoolIo` (r:3 w:3) + /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Broker::Reservations` (r:1 w:0) + /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(12021), added: 12516, mode: `MaxEncodedLen`) + /// Storage: `Broker::Leases` (r:1 w:1) + /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(81), added: 576, mode: `MaxEncodedLen`) + /// Storage: `Broker::SaleInfo` (r:0 w:1) + /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workplan` (r:0 w:20) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 1000]`. + fn rotate_sale(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `12194` + // Estimated: `13506` + // Minimum execution time: 48_158_000 picoseconds. + Weight::from_parts(49_891_920, 0) + .saturating_add(Weight::from_parts(0, 13506)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(25)) + } + /// Storage: `Broker::InstaPoolIo` (r:1 w:0) + /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolHistory` (r:0 w:1) + /// Proof: `Broker::InstaPoolHistory` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + fn process_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `42` + // Estimated: `3493` + // Minimum execution time: 5_911_000 picoseconds. + Weight::from_parts(6_173_000, 0) + .saturating_add(Weight::from_parts(0, 3493)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Workplan` (r:1 w:1) + /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) + /// Storage: `Broker::Workload` (r:1 w:1) + /// Proof: `Broker::Workload` (`max_values`: None, `max_size`: Some(1212), added: 3687, 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`) + fn process_core_schedule() -> Weight { + // Proof Size summary in bytes: + // Measured: `1321` + // Estimated: `4786` + // Minimum execution time: 30_140_000 picoseconds. + Weight::from_parts(30_912_000, 0) + .saturating_add(Weight::from_parts(0, 4786)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// 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`) + fn request_revenue_info_at() -> Weight { + // Proof Size summary in bytes: + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 13_684_000 picoseconds. + Weight::from_parts(14_252_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Broker::CoreCountInbox` (r:0 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) + fn notify_core_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_718_000 picoseconds. + Weight::from_parts(1_843_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Broker::Status` (r:1 w:1) + /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) + /// Storage: `Broker::Configuration` (r:1 w:0) + /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) + /// Storage: `Broker::CoreCountInbox` (r:1 w:0) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn do_tick_base() -> Weight { + // Proof Size summary in bytes: + // Measured: `398` + // Estimated: `3863` + // Minimum execution time: 11_771_000 picoseconds. + Weight::from_parts(12_120_000, 0) + .saturating_add(Weight::from_parts(0, 3863)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_collator_selection.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_collator_selection.rs index 2adddecab264945884d8b4620b9dff9868bfc4f0..2341890c314ec422aebf30b27111502a01909c25 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_collator_selection.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_collator_selection.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_collator_selection` //! -//! 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-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=pallet_collator_selection +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_collator_selection.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,196 +50,236 @@ use core::marker::PhantomData; /// Weight functions for `pallet_collator_selection`. pub struct WeightInfo(PhantomData); impl pallet_collator_selection::WeightInfo for WeightInfo { - /// Storage: Session NextKeys (r:100 w:0) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: CollatorSelection Invulnerables (r:0 w:1) - /// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// The range of component `b` is `[1, 100]`. + /// Storage: `Session::NextKeys` (r:20 w:0) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CollatorSelection::Invulnerables` (r:0 w:1) + /// Proof: `CollatorSelection::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 20]`. fn set_invulnerables(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `214 + b * (78 ±0)` - // Estimated: `1203 + b * (2554 ±0)` - // Minimum execution time: 14_426_000 picoseconds. - Weight::from_parts(14_971_974, 0) - .saturating_add(Weight::from_parts(0, 1203)) - // Standard Error: 2_914 - .saturating_add(Weight::from_parts(2_604_699, 0).saturating_mul(b.into())) + // Measured: `164 + b * (79 ±0)` + // Estimated: `1155 + b * (2555 ±0)` + // Minimum execution time: 11_038_000 picoseconds. + Weight::from_parts(8_347_616, 0) + .saturating_add(Weight::from_parts(0, 1155)) + // Standard Error: 5_166 + .saturating_add(Weight::from_parts(3_025_311, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(Weight::from_parts(0, 2554).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 2555).saturating_mul(b.into())) } - /// Storage: CollatorSelection DesiredCandidates (r:0 w:1) - /// Proof: CollatorSelection DesiredCandidates (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn set_desired_candidates() -> Weight { + /// Storage: `Session::NextKeys` (r:1 w:0) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CollatorSelection::Invulnerables` (r:1 w:1) + /// Proof: `CollatorSelection::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::CandidateList` (r:1 w:1) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, 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 `b` is `[1, 19]`. + /// The range of component `c` is `[1, 99]`. + fn add_invulnerable(b: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_977_000 picoseconds. - Weight::from_parts(7_246_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `720 + b * (32 ±0) + c * (53 ±0)` + // Estimated: `6287 + b * (37 ±0) + c * (53 ±0)` + // Minimum execution time: 36_983_000 picoseconds. + Weight::from_parts(37_900_558, 0) + .saturating_add(Weight::from_parts(0, 6287)) + // Standard Error: 6_860 + .saturating_add(Weight::from_parts(94_160, 0).saturating_mul(b.into())) + // Standard Error: 1_300 + .saturating_add(Weight::from_parts(119_010, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into())) + } + /// Storage: `CollatorSelection::CandidateList` (r:1 w:0) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::Invulnerables` (r:1 w:1) + /// Proof: `CollatorSelection::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// The range of component `b` is `[5, 20]`. + fn remove_invulnerable(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `82 + b * (32 ±0)` + // Estimated: `6287` + // Minimum execution time: 10_432_000 picoseconds. + Weight::from_parts(10_460_489, 0) + .saturating_add(Weight::from_parts(0, 6287)) + // Standard Error: 2_803 + .saturating_add(Weight::from_parts(143_162, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `CollatorSelection::CandidacyBond` (r:0 w:1) - /// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) - fn set_candidacy_bond(_c: u32, _k: u32) -> Weight { + /// Storage: `CollatorSelection::DesiredCandidates` (r:0 w:1) + /// Proof: `CollatorSelection::DesiredCandidates` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn set_desired_candidates() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_937_000 picoseconds. - Weight::from_parts(8_161_000, 0) + // Minimum execution time: 4_302_000 picoseconds. + Weight::from_parts(4_508_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: CollatorSelection Candidates (r:1 w:1) - /// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(48002), added: 48497, mode: MaxEncodedLen) - /// Storage: CollatorSelection DesiredCandidates (r:1 w:0) - /// Proof: CollatorSelection DesiredCandidates (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: CollatorSelection Invulnerables (r:1 w:0) - /// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: Session NextKeys (r:1 w:0) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: CollatorSelection CandidacyBond (r:1 w:0) - /// Proof: CollatorSelection CandidacyBond (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: CollatorSelection LastAuthoredBlock (r:0 w:1) - /// Proof: CollatorSelection LastAuthoredBlock (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) - /// The range of component `c` is `[1, 999]`. - fn register_as_candidate(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1104 + c * (48 ±0)` - // Estimated: `49487 + c * (49 ±0)` - // Minimum execution time: 42_275_000 picoseconds. - Weight::from_parts(33_742_215, 0) - .saturating_add(Weight::from_parts(0, 49487)) - // Standard Error: 1_291 - .saturating_add(Weight::from_parts(103_381, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) - .saturating_add(Weight::from_parts(0, 49).saturating_mul(c.into())) - } - /// Storage: CollatorSelection Candidates (r:1 w:1) - /// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(48002), added: 48497, mode: MaxEncodedLen) - /// Storage: CollatorSelection LastAuthoredBlock (r:0 w:1) - /// Proof: CollatorSelection LastAuthoredBlock (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) - /// The range of component `c` is `[6, 1000]`. - fn leave_intent(c: u32, ) -> Weight { + /// Storage: `CollatorSelection::CandidacyBond` (r:1 w:1) + /// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::CandidateList` (r:1 w:1) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:100 w:100) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::LastAuthoredBlock` (r:0 w:100) + /// Proof: `CollatorSelection::LastAuthoredBlock` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// The range of component `c` is `[0, 100]`. + /// The range of component `k` is `[0, 100]`. + fn set_candidacy_bond(c: u32, k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `428 + c * (48 ±0)` - // Estimated: `49487` - // Minimum execution time: 33_404_000 picoseconds. - Weight::from_parts(22_612_617, 0) - .saturating_add(Weight::from_parts(0, 49487)) - // Standard Error: 1_341 - .saturating_add(Weight::from_parts(105_669, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `0 + c * (180 ±0) + k * (112 ±0)` + // Estimated: `6287 + c * (901 ±29) + k * (901 ±29)` + // Minimum execution time: 7_712_000 picoseconds. + Weight::from_parts(7_935_000, 0) + .saturating_add(Weight::from_parts(0, 6287)) + // Standard Error: 153_204 + .saturating_add(Weight::from_parts(5_173_626, 0).saturating_mul(c.into())) + // Standard Error: 153_204 + .saturating_add(Weight::from_parts(4_883_030, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_parts(0, 901).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 901).saturating_mul(k.into())) } + /// Storage: `CollatorSelection::CandidacyBond` (r:1 w:0) + /// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::CandidateList` (r:1 w:1) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, mode: `MaxEncodedLen`) + /// The range of component `c` is `[4, 100]`. fn update_bond(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `306 + c * (50 ±0)` + // Measured: `250 + c * (50 ±0)` // Estimated: `6287` - // Minimum execution time: 34_814_000 picoseconds. - Weight::from_parts(36_371_520, 0) + // Minimum execution time: 22_767_000 picoseconds. + Weight::from_parts(25_594_856, 0) .saturating_add(Weight::from_parts(0, 6287)) - // Standard Error: 2_391 - .saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into())) + // Standard Error: 1_814 + .saturating_add(Weight::from_parts(110_451, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `CollatorSelection::CandidateList` (r:1 w:1) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::Invulnerables` (r:1 w:0) + /// Proof: `CollatorSelection::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `Session::NextKeys` (r:1 w:0) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CollatorSelection::CandidacyBond` (r:1 w:0) + /// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::LastAuthoredBlock` (r:0 w:1) + /// Proof: `CollatorSelection::LastAuthoredBlock` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// The range of component `c` is `[1, 99]`. + fn register_as_candidate(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `687 + c * (52 ±0)` + // Estimated: `6287 + c * (54 ±0)` + // Minimum execution time: 30_792_000 picoseconds. + Weight::from_parts(34_485_582, 0) + .saturating_add(Weight::from_parts(0, 6287)) + // Standard Error: 2_421 + .saturating_add(Weight::from_parts(152_013, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(Weight::from_parts(0, 54).saturating_mul(c.into())) } + /// Storage: `CollatorSelection::Invulnerables` (r:1 w:0) + /// Proof: `CollatorSelection::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::CandidacyBond` (r:1 w:0) + /// Proof: `CollatorSelection::CandidacyBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Session::NextKeys` (r:1 w:0) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CollatorSelection::CandidateList` (r:1 w:1) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::LastAuthoredBlock` (r:0 w:2) + /// Proof: `CollatorSelection::LastAuthoredBlock` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// The range of component `c` is `[4, 100]`. fn take_candidate_slot(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `306 + c * (50 ±0)` + // Measured: `855 + c * (52 ±0)` + // Estimated: `6287 + c * (55 ±0)` + // Minimum execution time: 45_538_000 picoseconds. + Weight::from_parts(50_758_223, 0) + .saturating_add(Weight::from_parts(0, 6287)) + // Standard Error: 2_779 + .saturating_add(Weight::from_parts(149_419, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(Weight::from_parts(0, 55).saturating_mul(c.into())) + } + /// Storage: `CollatorSelection::CandidateList` (r:1 w:1) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::Invulnerables` (r:1 w:0) + /// Proof: `CollatorSelection::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::LastAuthoredBlock` (r:0 w:1) + /// Proof: `CollatorSelection::LastAuthoredBlock` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// The range of component `c` is `[4, 100]`. + fn leave_intent(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `277 + c * (48 ±0)` // Estimated: `6287` - // Minimum execution time: 34_814_000 picoseconds. - Weight::from_parts(36_371_520, 0) + // Minimum execution time: 26_356_000 picoseconds. + Weight::from_parts(29_910_328, 0) .saturating_add(Weight::from_parts(0, 6287)) - // Standard Error: 2_391 - .saturating_add(Weight::from_parts(201_700, 0).saturating_mul(c.into())) + // Standard Error: 2_159 + .saturating_add(Weight::from_parts(123_421, 0).saturating_mul(c.into())) .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: System BlockWeight (r:1 w:1) - /// Proof: System BlockWeight (max_values: Some(1), max_size: Some(48), added: 543, mode: MaxEncodedLen) - /// Storage: CollatorSelection LastAuthoredBlock (r:0 w:1) - /// Proof: CollatorSelection LastAuthoredBlock (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::LastAuthoredBlock` (r:0 w:1) + /// Proof: `CollatorSelection::LastAuthoredBlock` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) fn note_author() -> Weight { // Proof Size summary in bytes: - // Measured: `155` + // Measured: `103` // Estimated: `6196` - // Minimum execution time: 44_415_000 picoseconds. - Weight::from_parts(44_732_000, 0) + // Minimum execution time: 36_377_000 picoseconds. + Weight::from_parts(37_121_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Session NextKeys (r:1 w:0) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: CollatorSelection Invulnerables (r:1 w:1) - /// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(641), added: 1136, mode: MaxEncodedLen) - /// Storage: CollatorSelection Candidates (r:1 w:1) - /// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(4802), added: 5297, 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 `b` is `[1, 19]`. - /// The range of component `c` is `[1, 99]`. - fn add_invulnerable(b: u32, c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `757 + b * (32 ±0) + c * (53 ±0)` - // Estimated: `6287 + b * (37 ±0) + c * (53 ±0)` - // Minimum execution time: 52_720_000 picoseconds. - Weight::from_parts(56_102_459, 0) - .saturating_add(Weight::from_parts(0, 6287)) - // Standard Error: 12_957 - .saturating_add(Weight::from_parts(26_422, 0).saturating_mul(b.into())) - // Standard Error: 2_456 - .saturating_add(Weight::from_parts(128_528, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - .saturating_add(Weight::from_parts(0, 37).saturating_mul(b.into())) - .saturating_add(Weight::from_parts(0, 53).saturating_mul(c.into())) - } - /// Storage: CollatorSelection Invulnerables (r:1 w:1) - /// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// The range of component `b` is `[1, 100]`. - fn remove_invulnerable(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `119 + b * (32 ±0)` - // Estimated: `4687` - // Minimum execution time: 183_054_000 picoseconds. - Weight::from_parts(197_205_427, 0) - .saturating_add(Weight::from_parts(0, 4687)) - // Standard Error: 13_533 - .saturating_add(Weight::from_parts(376_231, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: CollatorSelection Candidates (r:1 w:0) - /// Proof: CollatorSelection Candidates (max_values: Some(1), max_size: Some(48002), added: 48497, mode: MaxEncodedLen) - /// Storage: CollatorSelection LastAuthoredBlock (r:999 w:0) - /// Proof: CollatorSelection LastAuthoredBlock (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) - /// Storage: CollatorSelection Invulnerables (r:1 w:0) - /// Proof: CollatorSelection Invulnerables (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: System BlockWeight (r:1 w:1) - /// Proof: System BlockWeight (max_values: Some(1), max_size: Some(48), added: 543, mode: MaxEncodedLen) - /// Storage: System Account (r:995 w:995) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `r` is `[1, 1000]`. - /// The range of component `c` is `[1, 1000]`. + /// Storage: `CollatorSelection::CandidateList` (r:1 w:0) + /// Proof: `CollatorSelection::CandidateList` (`max_values`: Some(1), `max_size`: Some(4802), added: 5297, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::LastAuthoredBlock` (r:100 w:0) + /// Proof: `CollatorSelection::LastAuthoredBlock` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::Invulnerables` (r:1 w:0) + /// Proof: `CollatorSelection::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `CollatorSelection::DesiredCandidates` (r:1 w:0) + /// Proof: `CollatorSelection::DesiredCandidates` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:97 w:97) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `r` is `[1, 100]`. + /// The range of component `c` is `[1, 100]`. fn new_session(r: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `22815 + c * (97 ±0) + r * (116 ±0)` - // Estimated: `49487 + c * (2519 ±0) + r * (2602 ±0)` - // Minimum execution time: 16_765_000 picoseconds. - Weight::from_parts(16_997_000, 0) - .saturating_add(Weight::from_parts(0, 49487)) - // Standard Error: 860_677 - .saturating_add(Weight::from_parts(30_463_094, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `2143 + c * (97 ±0) + r * (112 ±0)` + // Estimated: `6287 + c * (2519 ±0) + r * (2603 ±0)` + // Minimum execution time: 15_761_000 picoseconds. + Weight::from_parts(16_078_000, 0) + .saturating_add(Weight::from_parts(0, 6287)) + // Standard Error: 270_522 + .saturating_add(Weight::from_parts(11_903_266, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) .saturating_add(Weight::from_parts(0, 2519).saturating_mul(c.into())) - .saturating_add(Weight::from_parts(0, 2602).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(r.into())) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_message_queue.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_message_queue.rs index 651f27e10e5c7b5d941d2bec5197fc06ed035fda..e9cdc2478766de88bfd3cc8765ba472bac61db30 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_message_queue.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_message_queue.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -14,6 +14,31 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! Autogenerated weights for `pallet_message_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=coretime-westend-dev +// --wasm-execution=compiled +// --pallet=pallet_message_queue +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ + #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] @@ -22,134 +47,140 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; +/// Weight functions for `pallet_message_queue`. pub struct WeightInfo(PhantomData); impl pallet_message_queue::WeightInfo for WeightInfo { - /// Storage: MessageQueue ServiceHead (r:1 w:0) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:0) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `189` - // Estimated: `7534` - // Minimum execution time: 11_446_000 picoseconds. - Weight::from_parts(11_446_000, 0) - .saturating_add(Weight::from_parts(0, 7534)) + // Measured: `223` + // Estimated: `6044` + // Minimum execution time: 10_918_000 picoseconds. + Weight::from_parts(11_224_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `184` - // Estimated: `7534` - // Minimum execution time: 10_613_000 picoseconds. - Weight::from_parts(10_613_000, 0) - .saturating_add(Weight::from_parts(0, 7534)) + // Measured: `218` + // Estimated: `6044` + // Minimum execution time: 9_649_000 picoseconds. + Weight::from_parts(10_056_000, 0) + .saturating_add(Weight::from_parts(0, 6044)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn service_queue_base() -> Weight { // Proof Size summary in bytes: // Measured: `6` // Estimated: `3517` - // Minimum execution time: 4_854_000 picoseconds. - Weight::from_parts(4_854_000, 0) + // Minimum execution time: 3_134_000 picoseconds. + Weight::from_parts(3_197_000, 0) .saturating_add(Weight::from_parts(0, 3517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: // Measured: `72` // Estimated: `69050` - // Minimum execution time: 5_748_000 picoseconds. - Weight::from_parts(5_748_000, 0) + // Minimum execution time: 4_915_000 picoseconds. + Weight::from_parts(5_127_000, 0) .saturating_add(Weight::from_parts(0, 69050)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: // Measured: `72` // Estimated: `69050` - // Minimum execution time: 6_136_000 picoseconds. - Weight::from_parts(6_136_000, 0) + // Minimum execution time: 5_011_000 picoseconds. + Weight::from_parts(5_150_000, 0) .saturating_add(Weight::from_parts(0, 69050)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `MessageQueue::BookStateFor` (r:0 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) fn service_page_item() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 59_505_000 picoseconds. - Weight::from_parts(59_505_000, 0) + // Minimum execution time: 168_806_000 picoseconds. + Weight::from_parts(170_795_000, 0) .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `5007` - // Minimum execution time: 6_506_000 picoseconds. - Weight::from_parts(6_506_000, 0) - .saturating_add(Weight::from_parts(0, 5007)) + // Measured: `171` + // Estimated: `3517` + // Minimum execution time: 6_413_000 picoseconds. + Weight::from_parts(6_797_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) fn reap_page() -> Weight { // Proof Size summary in bytes: // Measured: `65667` - // Estimated: `72567` - // Minimum execution time: 40_646_000 picoseconds. - Weight::from_parts(40_646_000, 0) - .saturating_add(Weight::from_parts(0, 72567)) + // Estimated: `69050` + // Minimum execution time: 52_734_000 picoseconds. + Weight::from_parts(54_106_000, 0) + .saturating_add(Weight::from_parts(0, 69050)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: // Measured: `65667` - // Estimated: `72567` - // Minimum execution time: 51_424_000 picoseconds. - Weight::from_parts(51_424_000, 0) - .saturating_add(Weight::from_parts(0, 72567)) + // Estimated: `69050` + // Minimum execution time: 68_400_000 picoseconds. + Weight::from_parts(70_336_000, 0) + .saturating_add(Weight::from_parts(0, 69050)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65585), added: 68060, mode: `MaxEncodedLen`) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: // Measured: `65667` - // Estimated: `72567` - // Minimum execution time: 81_153_000 picoseconds. - Weight::from_parts(81_153_000, 0) - .saturating_add(Weight::from_parts(0, 72567)) + // Estimated: `69050` + // Minimum execution time: 109_496_000 picoseconds. + Weight::from_parts(111_632_000, 0) + .saturating_add(Weight::from_parts(0, 69050)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } -} \ No newline at end of file +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_multisig.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_multisig.rs index 4130e05bf7c4233b5dfdd6fcf0df1295ce77db61..1aaf3f4a6fb9dd88f83042915080b08bb735e664 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_multisig.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_multisig.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_multisig` //! -//! 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-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=pallet_multisig +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_multisig.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -53,110 +55,110 @@ impl pallet_multisig::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_337_000 picoseconds. - Weight::from_parts(11_960_522, 0) + // Minimum execution time: 11_938_000 picoseconds. + Weight::from_parts(13_021_007, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(504, 0).saturating_mul(z.into())) + // Standard Error: 4 + .saturating_add(Weight::from_parts(482, 0).saturating_mul(z.into())) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `263 + s * (2 ±0)` + // Measured: `262 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 41_128_000 picoseconds. - Weight::from_parts(35_215_592, 0) + // Minimum execution time: 37_643_000 picoseconds. + Weight::from_parts(27_088_068, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 429 - .saturating_add(Weight::from_parts(65_959, 0).saturating_mul(s.into())) - // Standard Error: 4 - .saturating_add(Weight::from_parts(1_230, 0).saturating_mul(z.into())) + // Standard Error: 828 + .saturating_add(Weight::from_parts(123_693, 0).saturating_mul(s.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[3, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `282` // Estimated: `6811` - // Minimum execution time: 26_878_000 picoseconds. - Weight::from_parts(21_448_577, 0) + // Minimum execution time: 25_825_000 picoseconds. + Weight::from_parts(15_698_835, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 354 - .saturating_add(Weight::from_parts(60_286, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_236, 0).saturating_mul(z.into())) + // Standard Error: 568 + .saturating_add(Weight::from_parts(111_928, 0).saturating_mul(s.into())) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_421, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, 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 `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `388 + s * (33 ±0)` + // Measured: `385 + s * (33 ±0)` // Estimated: `6811` - // Minimum execution time: 45_716_000 picoseconds. - Weight::from_parts(38_332_947, 0) + // Minimum execution time: 43_587_000 picoseconds. + Weight::from_parts(29_740_539, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 554 - .saturating_add(Weight::from_parts(81_026, 0).saturating_mul(s.into())) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_265, 0).saturating_mul(z.into())) + // Standard Error: 771 + .saturating_add(Weight::from_parts(154_861, 0).saturating_mul(s.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_557, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `263 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 32_089_000 picoseconds. - Weight::from_parts(33_664_508, 0) + // Minimum execution time: 24_966_000 picoseconds. + Weight::from_parts(25_879_458, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 487 - .saturating_add(Weight::from_parts(67_443, 0).saturating_mul(s.into())) + // Standard Error: 777 + .saturating_add(Weight::from_parts(122_823, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `282` // Estimated: `6811` - // Minimum execution time: 18_631_000 picoseconds. - Weight::from_parts(19_909_964, 0) + // Minimum execution time: 14_450_000 picoseconds. + Weight::from_parts(14_607_858, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 434 - .saturating_add(Weight::from_parts(62_989, 0).saturating_mul(s.into())) + // Standard Error: 471 + .saturating_add(Weight::from_parts(107_007, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `454 + s * (1 ±0)` // Estimated: `6811` - // Minimum execution time: 32_486_000 picoseconds. - Weight::from_parts(34_303_784, 0) + // Minimum execution time: 26_861_000 picoseconds. + Weight::from_parts(27_846_825, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 585 - .saturating_add(Weight::from_parts(69_979, 0).saturating_mul(s.into())) + // Standard Error: 714 + .saturating_add(Weight::from_parts(116_914, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_session.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_session.rs index d132ef17bbdb2295dcd4ee812ab62ae51fd6ff3a..b0d993d68a6a4e824c6050a3edef87870438e6b2 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_session.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_session.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_session` //! -//! 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-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=pallet_session +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_session.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,31 +50,31 @@ use core::marker::PhantomData; /// Weight functions for `pallet_session`. pub struct WeightInfo(PhantomData); impl pallet_session::WeightInfo for WeightInfo { - /// Storage: Session NextKeys (r:1 w:1) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session KeyOwner (r:1 w:1) - /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: `Session::NextKeys` (r:1 w:1) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::KeyOwner` (r:1 w:1) + /// Proof: `Session::KeyOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `297` - // Estimated: `3762` - // Minimum execution time: 17_353_000 picoseconds. - Weight::from_parts(18_005_000, 0) - .saturating_add(Weight::from_parts(0, 3762)) + // Measured: `271` + // Estimated: `3736` + // Minimum execution time: 15_149_000 picoseconds. + Weight::from_parts(16_053_000, 0) + .saturating_add(Weight::from_parts(0, 3736)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Session NextKeys (r:1 w:1) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session KeyOwner (r:0 w:1) - /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: `Session::NextKeys` (r:1 w:1) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::KeyOwner` (r:0 w:1) + /// Proof: `Session::KeyOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn purge_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `279` - // Estimated: `3744` - // Minimum execution time: 13_039_000 picoseconds. - Weight::from_parts(13_341_000, 0) - .saturating_add(Weight::from_parts(0, 3744)) + // Measured: `243` + // Estimated: `3708` + // Minimum execution time: 11_159_000 picoseconds. + Weight::from_parts(11_504_000, 0) + .saturating_add(Weight::from_parts(0, 3708)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_timestamp.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_timestamp.rs index 722858a3a4655881cdbedbe8e6cae419baefc190..ad8924d9ce7374c285ddc662c59d860707443bfc 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_timestamp.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_timestamp.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_timestamp` //! -//! 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-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=pallet_timestamp +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_timestamp.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,16 +50,16 @@ use core::marker::PhantomData; /// Weight functions for `pallet_timestamp`. pub struct WeightInfo(PhantomData); impl pallet_timestamp::WeightInfo for WeightInfo { - /// Storage: Timestamp Now (r:1 w:1) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Aura CurrentSlot (r:1 w:0) - /// Proof: Aura CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Aura::CurrentSlot` (r:1 w:0) + /// Proof: `Aura::CurrentSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set() -> Weight { // Proof Size summary in bytes: - // Measured: `49` + // Measured: `86` // Estimated: `1493` - // Minimum execution time: 7_986_000 picoseconds. - Weight::from_parts(8_134_000, 0) + // Minimum execution time: 5_552_000 picoseconds. + Weight::from_parts(5_821_000, 0) .saturating_add(Weight::from_parts(0, 1493)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +68,8 @@ impl pallet_timestamp::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `57` // Estimated: `0` - // Minimum execution time: 3_257_000 picoseconds. - Weight::from_parts(3_366_000, 0) + // Minimum execution time: 2_848_000 picoseconds. + Weight::from_parts(2_953_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..f159f877afe77d7b7b98524d726e3867c6f80588 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ +// --chain=coretime-westend-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_utility.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_utility.rs index dacd469ebb7ab62eb7fcd7740bf6bf230aace7ee..0f5340843bd6481da855cb403b2b7d6442ed0a52 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_utility.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_utility.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_utility` //! -//! 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-02-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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --chain=coretime-westend-dev -// --execution=wasm // --wasm-execution=compiled // --pallet=pallet_utility +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --steps=50 // --repeat=20 // --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_utility.rs +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -53,18 +55,18 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_697_000 picoseconds. - Weight::from_parts(11_859_145, 0) + // Minimum execution time: 3_721_000 picoseconds. + Weight::from_parts(7_071_852, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_146 - .saturating_add(Weight::from_parts(4_300_555, 0).saturating_mul(c.into())) + // Standard Error: 746 + .saturating_add(Weight::from_parts(2_767_352, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_979_000 picoseconds. - Weight::from_parts(5_066_000, 0) + // Minimum execution time: 3_631_000 picoseconds. + Weight::from_parts(3_836_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `c` is `[0, 1000]`. @@ -72,18 +74,18 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_741_000 picoseconds. - Weight::from_parts(15_928_547, 0) + // Minimum execution time: 3_817_000 picoseconds. + Weight::from_parts(2_683_003, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_310 - .saturating_add(Weight::from_parts(4_527_996, 0).saturating_mul(c.into())) + // Standard Error: 782 + .saturating_add(Weight::from_parts(3_059_987, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_717_000 picoseconds. - Weight::from_parts(8_909_000, 0) + // Minimum execution time: 5_463_000 picoseconds. + Weight::from_parts(5_701_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `c` is `[0, 1000]`. @@ -91,10 +93,10 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_814_000 picoseconds. - Weight::from_parts(13_920_831, 0) + // Minimum execution time: 3_771_000 picoseconds. + Weight::from_parts(5_714_929, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 7_605 - .saturating_add(Weight::from_parts(4_306_193, 0).saturating_mul(c.into())) + // Standard Error: 740 + .saturating_add(Weight::from_parts(2_800_888, 0).saturating_mul(c.into())) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs index d96ee43463a316d3b8a0c5c8351045d7c340cd13..0082db3099d029c976779af8600bcaf4410e8a2f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs @@ -1,4 +1,4 @@ -// Copyright Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -16,26 +16,26 @@ //! Autogenerated weights for `pallet_xcm` //! -//! 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-02-20, 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("coretime-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// ./artifacts/westend-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=coretime-westend-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_xcm.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=coretime-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,39 +48,50 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - /// 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::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`) fn send() -> Weight { // Proof Size summary in bytes: - // Measured: `38` - // Estimated: `3503` - // Minimum execution time: 25_783_000 picoseconds. - Weight::from_parts(26_398_000, 0) - .saturating_add(Weight::from_parts(0, 3503)) + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 18_410_000 picoseconds. + Weight::from_parts(18_657_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: ParachainInfo ParachainId (r:1 w:0) - /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, 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`) fn teleport_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `32` - // Estimated: `1489` - // Minimum execution time: 25_511_000 picoseconds. - Weight::from_parts(26_120_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - .saturating_add(T::DbWeight::get().reads(1)) + // Measured: `106` + // Estimated: `3571` + // Minimum execution time: 56_616_000 picoseconds. + Weight::from_parts(57_751_000, 0) + .saturating_add(Weight::from_parts(0, 3571)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reserve_transfer_assets() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -89,18 +100,18 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `496` - // Estimated: `6208` - // Minimum execution time: 146_932_000 picoseconds. - Weight::from_parts(153_200_000, 0) - .saturating_add(Weight::from_parts(0, 6208)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(7)) + // 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) + .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -109,189 +120,189 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: PolkadotXcm SupportedVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_707_000 picoseconds. - Weight::from_parts(9_874_000, 0) + // Minimum execution time: 6_014_000 picoseconds. + Weight::from_parts(6_412_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_default_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_073_000 picoseconds. - Weight::from_parts(3_183_000, 0) + // Minimum execution time: 1_844_000 picoseconds. + Weight::from_parts(1_957_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm QueryCounter (r:1 w:1) - /// Proof Skipped: PolkadotXcm QueryCounter (max_values: Some(1), 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 Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `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`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `38` - // Estimated: `3503` - // Minimum execution time: 30_999_000 picoseconds. - Weight::from_parts(31_641_000, 0) - .saturating_add(Weight::from_parts(0, 3503)) + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 24_067_000 picoseconds. + Weight::from_parts(24_553_000, 0) + .saturating_add(Weight::from_parts(0, 3539)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (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 Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`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`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `220` - // Estimated: `3685` - // Minimum execution time: 33_036_000 picoseconds. - Weight::from_parts(33_596_000, 0) - .saturating_add(Weight::from_parts(0, 3685)) + // Measured: `292` + // Estimated: `3757` + // Minimum execution time: 27_023_000 picoseconds. + Weight::from_parts(27_620_000, 0) + .saturating_add(Weight::from_parts(0, 3757)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: PolkadotXcm XcmExecutionSuspended (r:0 w:1) - /// Proof Skipped: PolkadotXcm XcmExecutionSuspended (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_suspension() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_035_000 picoseconds. - Weight::from_parts(3_154_000, 0) + // Minimum execution time: 1_866_000 picoseconds. + Weight::from_parts(1_984_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SupportedVersion (r:4 w:2) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 14_805_000 picoseconds. - Weight::from_parts(15_120_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 16_425_000 picoseconds. + Weight::from_parts(16_680_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifiers (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 14_572_000 picoseconds. - Weight::from_parts(14_909_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_171_000 picoseconds. + Weight::from_parts(16_564_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 15_341_000 picoseconds. - Weight::from_parts(15_708_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 17_785_000 picoseconds. + Weight::from_parts(18_123_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:2 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:2 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`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `6046` - // Minimum execution time: 27_840_000 picoseconds. - Weight::from_parts(28_248_000, 0) - .saturating_add(Weight::from_parts(0, 6046)) + // Measured: `142` + // Estimated: `6082` + // Minimum execution time: 23_903_000 picoseconds. + Weight::from_parts(24_769_000, 0) + .saturating_add(Weight::from_parts(0, 6082)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_245_000 picoseconds. - Weight::from_parts(8_523_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 10_617_000 picoseconds. + Weight::from_parts(10_843_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 14_780_000 picoseconds. - Weight::from_parts(15_173_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 16_656_000 picoseconds. + Weight::from_parts(17_106_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// 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:5 w:2) + /// 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`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_422_000 picoseconds. - Weight::from_parts(34_076_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `142` + // Estimated: `13507` + // Minimum execution time: 31_721_000 picoseconds. + Weight::from_parts(32_547_000, 0) + .saturating_add(Weight::from_parts(0, 13507)) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -300,11 +311,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn new_query() -> Weight { // Proof Size summary in bytes: - // Measured: `69` - // Estimated: `1554` - // Minimum execution time: 4_512_000 picoseconds. - Weight::from_parts(4_671_000, 0) - .saturating_add(Weight::from_parts(0, 1554)) + // Measured: `32` + // Estimated: `1517` + // Minimum execution time: 3_439_000 picoseconds. + Weight::from_parts(3_619_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -312,11 +323,23 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn take_response() -> Weight { // Proof Size summary in bytes: - // Measured: `7706` - // Estimated: `11171` - // Minimum execution time: 26_473_000 picoseconds. - Weight::from_parts(26_960_000, 0) - .saturating_add(Weight::from_parts(0, 11171)) + // Measured: `7669` + // Estimated: `11134` + // Minimum execution time: 24_657_000 picoseconds. + Weight::from_parts(24_971_000, 0) + .saturating_add(Weight::from_parts(0, 11134)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 34_028_000 picoseconds. + Weight::from_parts(34_697_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs index a14da7c7a38a54938bb390d5d2109816162fecf7..99af88812da2be05bd9273585ea7d186be9f8b90 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs @@ -196,7 +196,7 @@ impl XcmWeightInfo for CoretimeWestendXcmWeight { XcmGeneric::::clear_transact_status() } fn universal_origin(_: &Junction) -> Weight { - XcmGeneric::::universal_origin() + Weight::MAX } fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { Weight::MAX 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 eaf07aac52cefa88f524e6f3a2180ab9faf2b088..6f5a52de98c39fb7ff5de96c41652be063f56a74 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 @@ -1,41 +1,44 @@ // 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-02-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("asset-hub-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// target/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet -// --steps=50 -// --repeat=20 -// --extrinsic=* +// --template=./cumulus/templates/xcm-bench-template.hbs +// --chain=coretime-westend-dev // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::fungible -// --chain=asset-hub-westend-dev +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json // --header=./cumulus/file_header.txt -// --template=./cumulus/templates/xcm-bench-template.hbs -// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/ +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -53,8 +56,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: 19_401_000 picoseconds. + Weight::from_parts(19_768_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -64,17 +67,15 @@ 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: 42_452_000 picoseconds. + Weight::from_parts(43_126_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: `System::Account` (r:3 w:3) + // 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) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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) @@ -87,54 +88,49 @@ 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: `246` - // Estimated: `8799` - // Minimum execution time: 85_553_000 picoseconds. - Weight::from_parts(87_177_000, 8799) - .saturating_add(T::DbWeight::get().reads(10)) - .saturating_add(T::DbWeight::get().writes(5)) + // Measured: `207` + // Estimated: `6196` + // Minimum execution time: 58_090_000 picoseconds. + Weight::from_parts(59_502_000, 6196) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(4)) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // 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: `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: 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: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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: `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 initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `246` - // Estimated: `6196` - // Minimum execution time: 184_462_000 picoseconds. - Weight::from_parts(189_593_000, 6196) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `106` + // Estimated: `3571` + // Minimum execution time: 23_569_000 picoseconds. + Weight::from_parts(24_598_000, 3571) + .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_018_000 picoseconds. - Weight::from_parts(3_098_000, 0) + // Minimum execution time: 2_546_000 picoseconds. + Weight::from_parts(2_674_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,59 +138,53 @@ 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: 16_889_000 picoseconds. + Weight::from_parts(17_350_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) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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: `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: `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: `145` - // Estimated: `6196` - // Minimum execution time: 56_666_000 picoseconds. - Weight::from_parts(58_152_000, 6196) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `106` + // Estimated: `3593` + // Minimum execution time: 43_964_000 picoseconds. + Weight::from_parts(45_293_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: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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: `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 initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `145` - // Estimated: `3610` - // Minimum execution time: 44_197_000 picoseconds. - Weight::from_parts(45_573_000, 3610) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(3)) + // Measured: `106` + // Estimated: `3571` + // Minimum execution time: 20_704_000 picoseconds. + Weight::from_parts(21_266_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_generic.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index fc196abea0f5e61d746760e2b2bf5a7d8d0a476b..74254814bcafc7f67dd20d8fa5dcd8da4337f782 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,41 +1,44 @@ // 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-02-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("asset-hub-westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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: -// target/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet -// --steps=50 -// --repeat=20 -// --extrinsic=* +// --template=./cumulus/templates/xcm-bench-template.hbs +// --chain=coretime-westend-dev // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic -// --chain=asset-hub-westend-dev +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json // --header=./cumulus/file_header.txt -// --template=./cumulus/templates/xcm-bench-template.hbs -// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/ +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -49,128 +52,120 @@ 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: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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: `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 report_holding() -> Weight { // Proof Size summary in bytes: - // Measured: `246` - // Estimated: `6196` - // Minimum execution time: 415_033_000 picoseconds. - Weight::from_parts(429_573_000, 6196) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `106` + // Estimated: `3571` + // Minimum execution time: 22_424_000 picoseconds. + Weight::from_parts(23_208_000, 3571) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) } pub fn buy_execution() -> Weight { // 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: 1_194_000 picoseconds. + Weight::from_parts(1_306_000, 0) } // 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: `103` - // Estimated: `3568` - // Minimum execution time: 8_045_000 picoseconds. - Weight::from_parts(8_402_000, 3568) + // Measured: `32` + // Estimated: `3497` + // Minimum execution time: 6_359_000 picoseconds. + Weight::from_parts(6_585_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: 9_827_000 picoseconds. - Weight::from_parts(10_454_000, 0) + // Minimum execution time: 6_297_000 picoseconds. + Weight::from_parts(6_661_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: 1_778_000 picoseconds. + Weight::from_parts(1_923_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: 1_212_000 picoseconds. + Weight::from_parts(1_314_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: 1_165_000 picoseconds. + Weight::from_parts(1_247_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: 1_173_000 picoseconds. + Weight::from_parts(1_275_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: 1_247_000 picoseconds. + Weight::from_parts(1_332_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: 1_170_000 picoseconds. + Weight::from_parts(1_237_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: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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: `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 report_error() -> Weight { // Proof Size summary in bytes: - // Measured: `246` - // Estimated: `6196` - // Minimum execution time: 53_116_000 picoseconds. - Weight::from_parts(54_154_000, 6196) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `106` + // Estimated: `3571` + // Minimum execution time: 19_872_000 picoseconds. + Weight::from_parts(20_453_000, 3571) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) } // 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: `160` - // Estimated: `3625` - // Minimum execution time: 12_381_000 picoseconds. - Weight::from_parts(12_693_000, 3625) + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 9_105_000 picoseconds. + Weight::from_parts(9_365_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -178,13 +173,11 @@ 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: 1_228_000 picoseconds. + Weight::from_parts(1_293_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) - // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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) @@ -197,11 +190,11 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn subscribe_version() -> Weight { // Proof Size summary in bytes: - // Measured: `145` - // Estimated: `3610` - // Minimum execution time: 24_251_000 picoseconds. - Weight::from_parts(24_890_000, 3610) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `74` + // Estimated: `3539` + // Minimum execution time: 19_535_000 picoseconds. + Weight::from_parts(20_139_000, 3539) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) @@ -210,145 +203,127 @@ 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: 3_158_000 picoseconds. + Weight::from_parts(3_275_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: 1_539_000 picoseconds. + Weight::from_parts(1_607_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: 1_317_000 picoseconds. + Weight::from_parts(1_427_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: 1_176_000 picoseconds. + Weight::from_parts(1_250_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: 1_202_000 picoseconds. + Weight::from_parts(1_279_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: 1_411_000 picoseconds. + Weight::from_parts(1_463_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: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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: `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 query_pallet() -> Weight { // Proof Size summary in bytes: - // Measured: `246` - // Estimated: `6196` - // Minimum execution time: 58_011_000 picoseconds. - Weight::from_parts(59_306_000, 6196) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `106` + // Estimated: `3571` + // Minimum execution time: 22_991_000 picoseconds. + Weight::from_parts(23_820_000, 3571) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) } pub fn expect_pallet() -> Weight { // 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: 3_534_000 picoseconds. + Weight::from_parts(3_708_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: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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: `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 report_transact_status() -> Weight { // Proof Size summary in bytes: - // Measured: `246` - // Estimated: `6196` - // Minimum execution time: 53_078_000 picoseconds. - Weight::from_parts(54_345_000, 6196) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `106` + // Estimated: `3571` + // Minimum execution time: 20_025_000 picoseconds. + Weight::from_parts(20_463_000, 3571) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) } pub fn clear_transact_status() -> Weight { // 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: 1_213_000 picoseconds. + Weight::from_parts(1_290_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: 1_207_000 picoseconds. + Weight::from_parts(1_265_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) - } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - pub fn universal_origin() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 3_912_000 picoseconds. - Weight::from_parts(4_167_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) + // Minimum execution time: 1_195_000 picoseconds. + Weight::from_parts(1_231_000, 0) } 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: 1_182_000 picoseconds. + Weight::from_parts(1_265_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: 1_165_000 picoseconds. + Weight::from_parts(1_252_000, 0) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs index f468f29540aa8df7d4371b50bd3f51c0bff8d351..44049adf02711bd3b828f98bc3b90ab330e19317 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -15,11 +15,12 @@ // along with Cumulus. If not, see . use super::{ - AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, + AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, Broker, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, }; use frame_support::{ + pallet_prelude::PalletInfoAccess, parameter_types, traits::{ConstU32, Contains, Equals, Everything, Nothing}, }; @@ -37,25 +38,26 @@ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::AccountIdConversion; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, + NonFungibleAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { - pub const WndRelayLocation: Location = Location::parent(); + pub const TokenRelayLocation: Location = Location::parent(); pub const RelayNetwork: Option = Some(NetworkId::Westend); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); + pub BrokerPalletLocation: Location = + PalletInstance(::index() as u8).into(); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; pub FellowshipLocation: Location = Location::new(1, Parachain(1001)); @@ -75,12 +77,11 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + IsConcrete, // Do a simple punn to convert an `AccountId32` `Location` into a native chain // `AccountId`: LocationToAccountId, @@ -90,6 +91,23 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; +/// Means for transacting coretime regions on this chain. +pub type RegionTransactor = NonFungibleAdapter< + // Use this non-fungible implementation: + Broker, + // This adapter will handle coretime regions from the broker pallet. + IsConcrete, + // Convert an XCM Location into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports. + (), +>; + +/// Means for transacting assets on this chain. +pub type AssetTransactors = (FungibleTransactor, RegionTransactor); + /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with XCM's `Transact`. There is an `OriginKind` that can /// bias the kind of local `Origin` it will become. @@ -152,18 +170,20 @@ impl Contains for SafeCallFilter { pallet_xcm::Call::force_default_xcm_version { .. } ) | RuntimeCall::System( frame_system::Call::set_heap_pages { .. } | - frame_system::Call::set_code { .. } | - frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::authorize_upgrade { .. } | - frame_system::Call::authorize_upgrade_without_checks { .. } | - frame_system::Call::kill_prefix { .. }, + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::authorize_upgrade { .. } | + frame_system::Call::authorize_upgrade_without_checks { .. } | + frame_system::Call::kill_prefix { .. } | + // Should not be in Polkadot/Kusama. Here in order to speed up testing. + frame_system::Call::set_storage { .. }, ) | RuntimeCall::ParachainSystem(..) | RuntimeCall::Timestamp(..) | RuntimeCall::Balances(..) | RuntimeCall::CollatorSelection(..) | - RuntimeCall::Sudo(..) | RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) + RuntimeCall::XcmpQueue(..) | + RuntimeCall::Broker(..) ) } } @@ -210,13 +230,13 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - // Coretime chain does not recognize a reserve location for any asset. Users must teleport WND + // Coretime chain does not recognize a reserve location for any asset. Users must teleport ROC // where allowed (e.g. with the Relay Chain). type IsReserve = (); /// Only allow teleportation of WND. - type IsTeleporter = ConcreteAssetFromSystem; + type IsTeleporter = ConcreteAssetFromSystem; type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = WeightInfoBounds< @@ -224,8 +244,13 @@ impl xcm_executor::Config for XcmConfig { RuntimeCall, MaxInstructions, >; - type Trader = - UsingComponents>; + type Trader = UsingComponents< + WeightToFee, + TokenRelayLocation, + AccountId, + Balances, + ToStakingPot, + >; type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; @@ -243,6 +268,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml index 831e3242766418fca9c6ed4d9a97e6ae037c4193..23c5ce1c7f80d46ed193b37963b96d6fd55243c6 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glutton-westend-runtime" -version = "1.0.0" +version = "3.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -54,6 +54,7 @@ cumulus-primitives-core = { path = "../../../../primitives/core", default-featur cumulus-primitives-timestamp = { path = "../../../../primitives/timestamp", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["westend"] } [build-dependencies] substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder" } @@ -112,6 +113,7 @@ std = [ "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", + "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", @@ -137,5 +139,5 @@ experimental = ["pallet-aura/experimental"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index 9fbbdc61feac66d63202a3def3ccb38afd93a55e..199057c3764797bf1da194c8ede174e80fd5d91c 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -87,6 +87,7 @@ use parachains_common::{AccountId, Signature}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; +use testnet_parachains_constants::westend::consensus::*; impl_opaque_keys! { pub struct SessionKeys { @@ -99,7 +100,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("glutton-westend"), impl_name: create_runtime_str!("glutton-westend"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -118,29 +119,6 @@ const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); /// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used /// by Operational extrinsics. const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); -/// We allow for .5 seconds of compute with a 12 second average block time. -const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( - WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), - cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, -); - -/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included -/// into the relay chain. -const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; -/// How many parachain blocks are processed by the relay chain per parent. Limits the -/// number of blocks authored per slot. -const BLOCK_PROCESSING_VELOCITY: u32 = 2; -/// Relay chain slot duration, in milliseconds. -const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; - -/// This determines the average expected block time that we are targeting. -/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. -/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked -/// up by `pallet_aura` to implement `fn slot_duration()`. -/// -/// Change this to adjust the block time. -pub const MILLISECS_PER_BLOCK: u64 = 6000; -pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; parameter_types! { pub const BlockHashCount: BlockNumber = 4096; @@ -311,8 +289,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( pallet_sudo::CheckOnlySudoAccount, frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, @@ -323,7 +301,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -333,15 +311,12 @@ pub type Executive = frame_executive::Executive< AllPalletsWithSystem, >; -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( [cumulus_pallet_parachain_system, ParachainSystem] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_glutton, Glutton] [pallet_message_queue, MessageQueue] [pallet_timestamp, Timestamp] @@ -358,7 +333,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -465,6 +440,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -481,6 +457,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..e6efa76230823821019e593fda8c5e095e36d4d0 --- /dev/null +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,119 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("glutton-westend-dev-1300")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/ +// --chain=glutton-westend-dev-1300 + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_908_000 picoseconds. + Weight::from_parts(4_007_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_510_000 picoseconds. + Weight::from_parts(6_332_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 651_000 picoseconds. + Weight::from_parts(851_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_387_000 picoseconds. + Weight::from_parts(3_646_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 491_000 picoseconds. + Weight::from_parts(651_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 451_000 picoseconds. + Weight::from_parts(662_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 3_537_000 picoseconds. + Weight::from_parts(4_208_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs index 1c37241563ec53eb1e9815b657492d1ba7439fd0..ad61987c0e7048f2d94b29ff51906e197f5ad2fd 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs @@ -24,8 +24,8 @@ use frame_support::{ }; use xcm::latest::prelude::*; use xcm_builder::{ - AllowExplicitUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, - SovereignSignedViaLocation, + AllowExplicitUnpaidExecutionFrom, FixedWeightBounds, FrameTransactionalProcessor, + ParentAsSuperuser, ParentIsPreset, SovereignSignedViaLocation, }; parameter_types! { @@ -87,6 +87,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index 5086697b0f1ee08bcca101e517386bda0d83e2ce..c9781639c3e42889edb5b6e046fae7319551800b 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml @@ -13,10 +13,9 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } enumflags2 = { version = "0.7.7" } hex-literal = { version = "0.4.1" } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -smallvec = "1.11.0" +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -54,7 +53,6 @@ sp-version = { path = "../../../../../substrate/primitives/version", default-fea # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false } @@ -64,27 +62,28 @@ xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkad # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } [features] default = ["std"] std = [ "codec/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "enumflags2/std", @@ -112,7 +111,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "rococo-runtime-constants/std", @@ -132,13 +130,13 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", ] runtime-benchmarks = [ - "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -154,6 +152,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", @@ -167,7 +166,6 @@ runtime-benchmarks = [ try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-dmp-queue/try-runtime", "cumulus-pallet-parachain-system/try-runtime", "cumulus-pallet-xcm/try-runtime", "cumulus-pallet-xcmp-queue/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 7836208eb6233a6edbccd986bd9def8179ab313e..c5e420785de54e908af65f2d4515d678b9b6f005 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -22,7 +22,7 @@ pub mod people; mod weights; pub mod xcm_config; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ construct_runtime, derive_impl, @@ -43,9 +43,8 @@ use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use parachains_common::{ impls::DealWithFees, message_queue::{NarrowOriginToSibling, ParaIdToSibling}, - rococo::{consensus::*, currency::*, fee::WeightToFee}, AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, - HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + NORMAL_DISPATCH_RATIO, }; use polkadot_runtime_common::{identity_migrator, BlockHashCount, SlowAdjustingFeeUpdate}; use sp_api::impl_runtime_apis; @@ -64,6 +63,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::latest::prelude::BodyId; use xcm_config::{ @@ -83,8 +83,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -97,10 +97,13 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. -pub type Migrations = (); +pub type Migrations = ( + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, +); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -123,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("people-rococo"), impl_name: create_runtime_str!("people-rococo"), authoring_version: 1, - spec_version: 1_006_002, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -175,6 +178,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = ConstU32<16>; @@ -184,6 +188,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -210,7 +217,6 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -227,6 +233,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -244,16 +251,18 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedDmpWeight = ReservedDmpWeight; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; type WeightInfo = weights::cumulus_pallet_parachain_system::WeightInfo; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + parameter_types! { pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; @@ -329,9 +338,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -434,24 +443,23 @@ construct_runtime!( } ); -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( // Substrate [frame_system, SystemBench::] [pallet_balances, Balances] [pallet_identity, Identity] + [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] // Polkadot [polkadot_runtime_common::identity_migrator, IdentityMigrator] // Cumulus + [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_collator_selection, CollatorSelection] // XCM @@ -464,7 +472,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -472,6 +480,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -481,7 +498,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -672,6 +689,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -680,7 +703,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between People and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -690,6 +713,13 @@ impl_runtime_apis! { fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; @@ -780,6 +810,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(RelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..1ba2010991fef1dddcc37fa3fccef25599278d82 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/ +// --chain=people-rococo-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} 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 3396a8caea0599574da40135c74bc19f9cf52125..c4cea99ee5aa4bede0ceac4cece3614497bd7134 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs @@ -20,6 +20,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_collator_selection; pub mod pallet_identity; @@ -27,6 +28,7 @@ pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs index 64d6cf4ece8c14c9dd9c090c00d4666a7d5b52fd..126d816afcdb551e31adc38aaeb54ad3af5b228c 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_balances.rs @@ -131,9 +131,17 @@ impl pallet_balances::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:999 w:999) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `u` is `[1, 1000]`. + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_132_000 picoseconds. + Weight::from_parts(5_467_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } fn upgrade_accounts(u: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..555fd5a32fa874f3f046cbf819a2a4e938b4926b --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/ +// --chain=people-rococo-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs index 0f793524de9f5ef7da835090f9d81008ba756d59..fabce29b5fd9451f27364c38481d2dac98c430d8 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_xcm.rs @@ -1,40 +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_xcm` //! -//! 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-02-20, 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-bn-ce5rx-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 -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/pallet_xcm.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,57 +48,28 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - /// 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::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`) fn send() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_931_000 picoseconds. - Weight::from_parts(26_340_000, 0) + // Minimum execution time: 17_830_000 picoseconds. + Weight::from_parts(18_411_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: ParachainInfo ParachainId (r:1 w:0) - /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn teleport_assets() -> Weight { - // Proof Size summary in bytes: - // Measured: `32` - // Estimated: `1489` - // Minimum execution time: 25_691_000 picoseconds. - Weight::from_parts(25_971_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - .saturating_add(T::DbWeight::get().reads(1)) - } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) - fn reserve_transfer_assets() -> 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) - .saturating_add(Weight::from_parts(0, 0)) - } /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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) @@ -108,18 +80,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// 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`) + fn teleport_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 55_456_000 picoseconds. + Weight::from_parts(56_808_000, 0) + .saturating_add(Weight::from_parts(0, 3535)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_transfer_assets() -> 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) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `496` - // Estimated: `6208` - // Minimum execution time: 146_932_000 picoseconds. - Weight::from_parts(153_200_000, 0) - .saturating_add(Weight::from_parts(0, 6208)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(7)) + // 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) + .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -128,189 +120,189 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: PolkadotXcm SupportedVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_572_000 picoseconds. - Weight::from_parts(9_924_000, 0) + // Minimum execution time: 5_996_000 picoseconds. + Weight::from_parts(6_154_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_default_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_997_000 picoseconds. - Weight::from_parts(3_136_000, 0) + // Minimum execution time: 1_768_000 picoseconds. + Weight::from_parts(1_914_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm QueryCounter (r:1 w:1) - /// Proof Skipped: PolkadotXcm QueryCounter (max_values: Some(1), 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 Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `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`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 30_271_000 picoseconds. - Weight::from_parts(30_819_000, 0) + // Minimum execution time: 24_120_000 picoseconds. + Weight::from_parts(24_745_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (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 Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`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`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `220` - // Estimated: `3685` - // Minimum execution time: 32_302_000 picoseconds. - Weight::from_parts(32_807_000, 0) - .saturating_add(Weight::from_parts(0, 3685)) + // Measured: `255` + // Estimated: `3720` + // Minimum execution time: 26_630_000 picoseconds. + Weight::from_parts(27_289_000, 0) + .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: PolkadotXcm XcmExecutionSuspended (r:0 w:1) - /// Proof Skipped: PolkadotXcm XcmExecutionSuspended (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_suspension() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_960_000 picoseconds. - Weight::from_parts(3_094_000, 0) + // Minimum execution time: 1_821_000 picoseconds. + Weight::from_parts(1_946_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SupportedVersion (r:4 w:2) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 14_877_000 picoseconds. - Weight::from_parts(15_296_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 16_586_000 picoseconds. + Weight::from_parts(16_977_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifiers (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 14_835_000 picoseconds. - Weight::from_parts(15_115_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_923_000 picoseconds. + Weight::from_parts(17_415_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 15_368_000 picoseconds. - Weight::from_parts(15_596_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 18_596_000 picoseconds. + Weight::from_parts(18_823_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:2 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:2 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`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 28_025_000 picoseconds. - Weight::from_parts(28_524_000, 0) + // Minimum execution time: 23_817_000 picoseconds. + Weight::from_parts(24_520_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_166_000 picoseconds. - Weight::from_parts(8_314_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 11_042_000 picoseconds. + Weight::from_parts(11_578_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 14_871_000 picoseconds. - Weight::from_parts(15_374_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 17_306_000 picoseconds. + Weight::from_parts(17_817_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// 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:5 w:2) + /// 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`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_611_000 picoseconds. - Weight::from_parts(34_008_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 32_141_000 picoseconds. + Weight::from_parts(32_954_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -319,11 +311,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn new_query() -> Weight { // Proof Size summary in bytes: - // Measured: `103` - // Estimated: `1588` - // Minimum execution time: 5_496_000 picoseconds. - Weight::from_parts(5_652_000, 0) - .saturating_add(Weight::from_parts(0, 1588)) + // Measured: `32` + // Estimated: `1517` + // Minimum execution time: 3_410_000 picoseconds. + Weight::from_parts(3_556_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -331,11 +323,23 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn take_response() -> Weight { // Proof Size summary in bytes: - // Measured: `7740` - // Estimated: `11205` - // Minimum execution time: 26_140_000 picoseconds. - Weight::from_parts(26_824_000, 0) - .saturating_add(Weight::from_parts(0, 11205)) + // Measured: `7669` + // Estimated: `11134` + // Minimum execution time: 25_021_000 picoseconds. + Weight::from_parts(25_240_000, 0) + .saturating_add(Weight::from_parts(0, 11134)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 33_801_000 picoseconds. + Weight::from_parts(34_655_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs index 168d7eaa605dbeb27dca89eb2a729f3838f8565f..311128a17ca9c16f18adf60e5dbf383161dc3bab 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -27,24 +27,23 @@ use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, xcm_config::{ - AllSiblingSystemParachains, ConcreteAssetFromSystem, RelayOrOtherSystemParachains, + AllSiblingSystemParachains, ConcreteAssetFromSystem, ParentRelayOrSiblingParachains, + RelayOrOtherSystemParachains, }, TREASURY_PALLET_ID, }; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::traits::AccountIdConversion; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeTerminus, EnsureXcmOrigin, HashedDescription, IsConcrete, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, - XcmFeeToAccount, + DenyThenTry, DescribeTerminus, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -97,8 +96,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -150,13 +148,6 @@ impl Contains for ParentOrParentsPlurality { } } -pub struct ParentOrSiblings; -impl Contains for ParentOrSiblings { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, []) | (1, [Parachain(_)])) - } -} - /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly /// account for proof size weights. /// @@ -222,7 +213,7 @@ pub type Barrier = TrailingSetTopicAsId< // Parent and its pluralities (i.e. governance bodies) get free execution. AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, + AllowSubscriptionsFrom, ), UniversalLocation, ConstU32<8>, @@ -244,7 +235,7 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = FungibleTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; // People chain does not recognize a reserve location for any asset. Users must teleport ROC // where allowed (e.g. with the Relay Chain). @@ -277,6 +268,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index 38386779a0e28d388f84211ae3a50294879ac320..8d8421332c13d40c46636f7be01143617a9035a5 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -13,10 +13,9 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } enumflags2 = { version = "0.7.7" } hex-literal = { version = "0.4.1" } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -smallvec = "1.11.0" +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } # Substrate frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -54,7 +53,6 @@ sp-version = { path = "../../../../../substrate/primitives/version", default-fea # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } westend-runtime-constants = { path = "../../../../../polkadot/runtime/westend/constants", default-features = false } @@ -64,27 +62,28 @@ xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkad # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["westend"] } [features] default = ["std"] std = [ "codec/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", "cumulus-primitives-core/std", "cumulus-primitives-utility/std", "enumflags2/std", @@ -112,7 +111,6 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", - "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", @@ -131,6 +129,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", @@ -138,7 +137,6 @@ std = [ ] runtime-benchmarks = [ - "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -154,6 +152,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", @@ -167,7 +166,6 @@ runtime-benchmarks = [ try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-dmp-queue/try-runtime", "cumulus-pallet-parachain-system/try-runtime", "cumulus-pallet-xcm/try-runtime", "cumulus-pallet-xcmp-queue/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 750bf5b2c3ebbb7a802077f82d85fc48790be6d5..dea079a4a98ae6c5ee16aafd0d07f9e2b9dd34d5 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -22,7 +22,7 @@ pub mod people; mod weights; pub mod xcm_config; -use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ construct_runtime, derive_impl, @@ -30,8 +30,7 @@ use frame_support::{ genesis_builder_helper::{build_config, create_default_config}, parameter_types, traits::{ - ConstBool, ConstU32, ConstU64, ConstU8, Contains, EitherOfDiverse, EverythingBut, - TransformOrigin, + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, }, weights::{ConstantMultiplier, Weight}, PalletId, @@ -44,9 +43,8 @@ use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use parachains_common::{ impls::DealWithFees, message_queue::{NarrowOriginToSibling, ParaIdToSibling}, - westend::{consensus::*, currency::*, fee::WeightToFee}, AccountId, Balance, BlockNumber, Hash, Header, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, - HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, + NORMAL_DISPATCH_RATIO, }; use polkadot_runtime_common::{identity_migrator, BlockHashCount, SlowAdjustingFeeUpdate}; use sp_api::impl_runtime_apis; @@ -65,6 +63,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::latest::prelude::BodyId; use xcm_config::{ @@ -84,8 +83,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The transactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -98,10 +97,13 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. -pub type Migrations = (); +pub type Migrations = ( + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, +); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -124,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("people-westend"), impl_name: create_runtime_str!("people-westend"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -162,16 +164,9 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -pub struct IdentityCalls; -impl Contains for IdentityCalls { - fn contains(c: &RuntimeCall) -> bool { - matches!(c, RuntimeCall::Identity(_)) - } -} - #[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { - type BaseCallFilter = EverythingBut; + type BaseCallFilter = Everything; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type AccountId = AccountId; @@ -183,6 +178,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = ConstU32<16>; @@ -192,6 +188,9 @@ impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = Aura; + #[cfg(feature = "experimental")] + type MinimumPeriod = ConstU64<0>; + #[cfg(not(feature = "experimental"))] type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; type WeightInfo = weights::pallet_timestamp::WeightInfo; } @@ -218,7 +217,6 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -235,6 +233,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -252,16 +251,18 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedDmpWeight = ReservedDmpWeight; type XcmpMessageHandler = XcmpQueue; type ReservedXcmpWeight = ReservedXcmpWeight; - type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< - Runtime, - RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, - >; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; type WeightInfo = weights::cumulus_pallet_parachain_system::WeightInfo; } +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + parameter_types! { pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; @@ -337,9 +338,9 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; #[cfg(feature = "experimental")] - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; + type SlotDuration = ConstU64; } parameter_types! { @@ -442,17 +443,14 @@ construct_runtime!( } ); -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( // Substrate [frame_system, SystemBench::] [pallet_balances, Balances] [pallet_identity, Identity] + [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_session, SessionBench::] [pallet_utility, Utility] @@ -460,6 +458,7 @@ mod benches { // Polkadot [polkadot_runtime_common::identity_migrator, IdentityMigrator] // Cumulus + [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_collator_selection, CollatorSelection] // XCM @@ -472,7 +471,7 @@ mod benches { impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) } fn authorities() -> Vec { @@ -480,6 +479,15 @@ impl_runtime_apis! { } } + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -489,7 +497,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -680,6 +688,12 @@ impl_runtime_apis! { use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark; impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + fn reachable_dest() -> Option { Some(Parent.into()) } @@ -688,7 +702,7 @@ impl_runtime_apis! { // Relay/native token can be teleported between People and Relay. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Parent.into()) }, Parent.into(), @@ -698,6 +712,13 @@ impl_runtime_apis! { fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { None } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(ExistentialDeposit::get()), + } + } } use xcm::latest::prelude::*; @@ -788,6 +809,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(RelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { Err(BenchmarkError::Skip) } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..7130a62ab6a9c95aaf4938eff3c8a7ca0ed96d40 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,121 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/ +// --chain=people-westend-dev + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} 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 3396a8caea0599574da40135c74bc19f9cf52125..c4cea99ee5aa4bede0ceac4cece3614497bd7134 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs @@ -20,6 +20,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_collator_selection; pub mod pallet_identity; @@ -27,6 +28,7 @@ pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs index e53c8878dd1762b927815c9d6c55819a9f1a2f74..1a3df158a0d0742d6d721c66f9e6545608e0c88f 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_balances.rs @@ -131,9 +131,17 @@ impl pallet_balances::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:999 w:999) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// The range of component `u` is `[1, 1000]`. + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `1501` + // Minimum execution time: 5_132_000 picoseconds. + Weight::from_parts(5_467_000, 0) + .saturating_add(Weight::from_parts(0, 1501)) + .saturating_add(T::DbWeight::get().reads(1)) + } fn upgrade_accounts(u: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + u * (136 ±0)` diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..30e4524e586e24247f604e82c0ea974d43ff060c --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/ +// --chain=people-westend-dev + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs index d3b60471b850e0fffbad01915aaf461be3d48ae0..c337289243b748d721b60421bccf3349044ef194 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_xcm.rs @@ -1,40 +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_xcm` //! -//! 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-02-20, 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-bn-ce5rx-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 -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/pallet_xcm.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,57 +48,28 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - /// 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::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`) fn send() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_783_000 picoseconds. - Weight::from_parts(26_398_000, 0) + // Minimum execution time: 17_856_000 picoseconds. + Weight::from_parts(18_473_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: ParachainInfo ParachainId (r:1 w:0) - /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn teleport_assets() -> Weight { - // Proof Size summary in bytes: - // Measured: `32` - // Estimated: `1489` - // Minimum execution time: 25_511_000 picoseconds. - Weight::from_parts(26_120_000, 0) - .saturating_add(Weight::from_parts(0, 1489)) - .saturating_add(T::DbWeight::get().reads(1)) - } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) - fn reserve_transfer_assets() -> 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) - .saturating_add(Weight::from_parts(0, 0)) - } /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) - /// Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `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) @@ -108,18 +80,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// 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`) + fn teleport_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 56_112_000 picoseconds. + Weight::from_parts(57_287_000, 0) + .saturating_add(Weight::from_parts(0, 3535)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_transfer_assets() -> 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) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `496` - // Estimated: `6208` - // Minimum execution time: 146_932_000 picoseconds. - Weight::from_parts(153_200_000, 0) - .saturating_add(Weight::from_parts(0, 6208)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(7)) + // 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) + .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -128,189 +120,189 @@ impl pallet_xcm::WeightInfo for WeightInfo { Weight::from_parts(18_446_744_073_709_551_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: PolkadotXcm SupportedVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_707_000 picoseconds. - Weight::from_parts(9_874_000, 0) + // Minimum execution time: 6_186_000 picoseconds. + Weight::from_parts(6_420_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) - /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:0 w:1) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_default_xcm_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_073_000 picoseconds. - Weight::from_parts(3_183_000, 0) + // Minimum execution time: 1_824_000 picoseconds. + Weight::from_parts(1_999_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) - /// Storage: PolkadotXcm QueryCounter (r:1 w:1) - /// Proof Skipped: PolkadotXcm QueryCounter (max_values: Some(1), 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 Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `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`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 30_999_000 picoseconds. - Weight::from_parts(31_641_000, 0) + // Minimum execution time: 23_833_000 picoseconds. + Weight::from_parts(24_636_000, 0) .saturating_add(Weight::from_parts(0, 3503)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: PolkadotXcm VersionNotifiers (r:1 w:1) - /// Proof Skipped: PolkadotXcm VersionNotifiers (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 Queries (r:0 w:1) - /// Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionNotifiers` (`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`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `220` - // Estimated: `3685` - // Minimum execution time: 33_036_000 picoseconds. - Weight::from_parts(33_596_000, 0) - .saturating_add(Weight::from_parts(0, 3685)) + // Measured: `255` + // Estimated: `3720` + // Minimum execution time: 26_557_000 picoseconds. + Weight::from_parts(27_275_000, 0) + .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: PolkadotXcm XcmExecutionSuspended (r:0 w:1) - /// Proof Skipped: PolkadotXcm XcmExecutionSuspended (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::XcmExecutionSuspended` (r:0 w:1) + /// Proof: `PolkadotXcm::XcmExecutionSuspended` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_suspension() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_035_000 picoseconds. - Weight::from_parts(3_154_000, 0) + // Minimum execution time: 1_921_000 picoseconds. + Weight::from_parts(2_040_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: PolkadotXcm SupportedVersion (r:4 w:2) - /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::SupportedVersion` (r:5 w:2) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `10985` - // Minimum execution time: 14_805_000 picoseconds. - Weight::from_parts(15_120_000, 0) - .saturating_add(Weight::from_parts(0, 10985)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `89` + // Estimated: `13454` + // Minimum execution time: 16_832_000 picoseconds. + Weight::from_parts(17_312_000, 0) + .saturating_add(Weight::from_parts(0, 13454)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifiers (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifiers (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifiers` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `99` - // Estimated: `10989` - // Minimum execution time: 14_572_000 picoseconds. - Weight::from_parts(14_909_000, 0) - .saturating_add(Weight::from_parts(0, 10989)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `93` + // Estimated: `13458` + // Minimum execution time: 16_687_000 picoseconds. + Weight::from_parts(17_123_000, 0) + .saturating_add(Weight::from_parts(0, 13458)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:6 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `106` - // Estimated: `13471` - // Minimum execution time: 15_341_000 picoseconds. - Weight::from_parts(15_708_000, 0) - .saturating_add(Weight::from_parts(0, 13471)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15946` + // Minimum execution time: 18_164_000 picoseconds. + Weight::from_parts(18_580_000, 0) + .saturating_add(Weight::from_parts(0, 15946)) + .saturating_add(T::DbWeight::get().reads(6)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:2 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:2 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`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: // Measured: `106` // Estimated: `6046` - // Minimum execution time: 27_840_000 picoseconds. - Weight::from_parts(28_248_000, 0) + // Minimum execution time: 23_577_000 picoseconds. + Weight::from_parts(24_324_000, 0) .saturating_add(Weight::from_parts(0, 6046)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:4 w:0) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `136` - // Estimated: `8551` - // Minimum execution time: 8_245_000 picoseconds. - Weight::from_parts(8_523_000, 0) - .saturating_add(Weight::from_parts(0, 8551)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `11026` + // Minimum execution time: 11_014_000 picoseconds. + Weight::from_parts(11_223_000, 0) + .saturating_add(Weight::from_parts(0, 11026)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `PolkadotXcm::VersionNotifyTargets` (r:5 w:2) + /// Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `10996` - // Minimum execution time: 14_780_000 picoseconds. - Weight::from_parts(15_173_000, 0) - .saturating_add(Weight::from_parts(0, 10996)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `100` + // Estimated: `13465` + // Minimum execution time: 16_887_000 picoseconds. + Weight::from_parts(17_361_000, 0) + .saturating_add(Weight::from_parts(0, 13465)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) - /// 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:5 w:2) + /// 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`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `112` - // Estimated: `11002` - // Minimum execution time: 33_422_000 picoseconds. - Weight::from_parts(34_076_000, 0) - .saturating_add(Weight::from_parts(0, 11002)) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `106` + // Estimated: `13471` + // Minimum execution time: 31_705_000 picoseconds. + Weight::from_parts(32_166_000, 0) + .saturating_add(Weight::from_parts(0, 13471)) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) @@ -319,11 +311,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn new_query() -> Weight { // Proof Size summary in bytes: - // Measured: `103` - // Estimated: `1588` - // Minimum execution time: 5_496_000 picoseconds. - Weight::from_parts(5_652_000, 0) - .saturating_add(Weight::from_parts(0, 1588)) + // Measured: `32` + // Estimated: `1517` + // Minimum execution time: 3_568_000 picoseconds. + Weight::from_parts(3_669_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -331,11 +323,23 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn take_response() -> Weight { // Proof Size summary in bytes: - // Measured: `7740` - // Estimated: `11205` - // Minimum execution time: 26_140_000 picoseconds. - Weight::from_parts(26_824_000, 0) - .saturating_add(Weight::from_parts(0, 11205)) + // Measured: `7669` + // Estimated: `11134` + // Minimum execution time: 24_823_000 picoseconds. + Weight::from_parts(25_344_000, 0) + .saturating_add(Weight::from_parts(0, 11134)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + /// Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `90` + // Estimated: `3555` + // Minimum execution time: 34_516_000 picoseconds. + Weight::from_parts(35_478_000, 0) + .saturating_add(Weight::from_parts(0, 3555)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs index 18c03a968ef92b99442d8f4fe3d466d8f14ba7d4..4b7da91c17e5568bc4ca34f3f4a255f8276893e8 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -27,24 +27,23 @@ use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, xcm_config::{ - AllSiblingSystemParachains, ConcreteAssetFromSystem, RelayOrOtherSystemParachains, + AllSiblingSystemParachains, ConcreteAssetFromSystem, ParentRelayOrSiblingParachains, + RelayOrOtherSystemParachains, }, TREASURY_PALLET_ID, }; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::traits::AccountIdConversion; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeTerminus, EnsureXcmOrigin, HashedDescription, IsConcrete, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, - XcmFeeToAccount, + DenyThenTry, DescribeTerminus, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -97,8 +96,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -150,13 +148,6 @@ impl Contains for ParentOrParentsPlurality { } } -pub struct ParentOrSiblings; -impl Contains for ParentOrSiblings { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, []) | (1, [Parachain(_)])) - } -} - pub struct FellowsPlurality; impl Contains for FellowsPlurality { fn contains(location: &Location) -> bool { @@ -230,7 +221,7 @@ pub type Barrier = TrailingSetTopicAsId< // get free execution. AllowExplicitUnpaidExecutionFrom<(ParentOrParentsPlurality, FellowsPlurality)>, // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, + AllowSubscriptionsFrom, ), UniversalLocation, ConstU32<8>, @@ -252,7 +243,7 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = FungibleTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; // People does not recognize a reserve location for any asset. Users must teleport WND // where allowed (e.g. with the Relay Chain). @@ -285,6 +276,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/starters/seedling/Cargo.toml b/cumulus/parachains/runtimes/starters/seedling/Cargo.toml index 37a3bb4ca26ff6362e064503de8c331b8e973629..1f5bee7784e989ab0e6ed76f3692f33d54b6304e 100644 --- a/cumulus/parachains/runtimes/starters/seedling/Cargo.toml +++ b/cumulus/parachains/runtimes/starters/seedling/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "seedling-runtime" -version = "0.1.0" +version = "0.7.0" description = "Seedling parachain runtime. A starter runtime for solochain to parachain migration." authors.workspace = true edition.workspace = true diff --git a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs index ba077ef887947f6f0f2b639f85c10fcb13a06fcb..023c997830238051f3521932d39e2a13fe095d19 100644 --- a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs @@ -258,8 +258,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckSpecVersion, frame_system::CheckTxVersion, frame_system::CheckGenesis, @@ -269,7 +269,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -300,7 +300,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/parachains/runtimes/starters/shell/Cargo.toml b/cumulus/parachains/runtimes/starters/shell/Cargo.toml index 3d7042ecd49fb0d3e046e136b70f069770848bbc..5a8f2a9d125369c91b9859186866cc62edc1795e 100644 --- a/cumulus/parachains/runtimes/starters/shell/Cargo.toml +++ b/cumulus/parachains/runtimes/starters/shell/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shell-runtime" -version = "0.1.0" +version = "0.7.0" description = "A minimal runtime to test Relay Chain consensus." authors.workspace = true edition.workspace = true diff --git a/cumulus/parachains/runtimes/starters/shell/src/lib.rs b/cumulus/parachains/runtimes/starters/shell/src/lib.rs index 457394760d989db4dcd5ba335e1ff2dad46eaaba..fedac36e48d6452ef2b13e3dd88fa4f17a18516b 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/lib.rs @@ -34,15 +34,19 @@ pub mod xcm_config; use codec::{Decode, Encode}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::AggregateMessageOrigin; -use frame_support::unsigned::TransactionValidityError; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; 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, DispatchInfoOf}, - transaction_validity::{TransactionSource, TransactionValidity}, + traits::{ + AccountIdLookup, BlakeTwo256, Block as BlockT, DispatchInfoOf, OriginOf, + TransactionExtension, TransactionExtensionBase, ValidateResult, + }, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, + }, ApplyExtrinsicResult, }; use sp_std::prelude::*; @@ -275,35 +279,37 @@ construct_runtime! { /// Simple implementation which fails any transaction which is signed. #[derive(Eq, PartialEq, Clone, Default, sp_core::RuntimeDebug, Encode, Decode, TypeInfo)] pub struct DisallowSigned; -impl sp_runtime::traits::SignedExtension for DisallowSigned { + +impl TransactionExtensionBase for DisallowSigned { const IDENTIFIER: &'static str = "DisallowSigned"; - type AccountId = AccountId; - type Call = RuntimeCall; - type AdditionalSigned = (); + type Implicit = (); +} + +impl TransactionExtension for DisallowSigned { + type Val = (); type Pre = (); - fn additional_signed( + fn validate( &self, - ) -> sp_std::result::Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - Ok(()) + _origin: OriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _context: &mut C, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + Err(InvalidTransaction::BadProof.into()) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } - fn validate( - &self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, + _val: Self::Val, + _origin: &OriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { - let i = sp_runtime::transaction_validity::InvalidTransaction::BadProof; - Err(sp_runtime::transaction_validity::TransactionValidityError::Invalid(i)) + _context: &C, + ) -> Result { + Err(InvalidTransaction::BadProof.into()) } } @@ -323,11 +329,11 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = DisallowSigned; +/// The extension to the basic transaction logic. +pub type TxExtension = DisallowSigned; /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -357,7 +363,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs index f5ceabb1eb473c8d2f34ce5ab48c3ce80e3d2fd5..f6af50f76d85bd06ca77fa6e6f8b06e41349dc3a 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -87,6 +87,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = (); } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/test-utils/Cargo.toml b/cumulus/parachains/runtimes/test-utils/Cargo.toml index cd100c472ce58d2dc5bfeb874ffeebef25c91753..eda88beb7dabb41bd4075ec5ab6bf8ec2f42d3c8 100644 --- a/cumulus/parachains/runtimes/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/test-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parachains-runtimes-test-utils" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true description = "Utils for Runtimes testing" @@ -15,9 +15,9 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = # Substrate frame-support = { path = "../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../substrate/frame/system", default-features = false } -pallet-assets = { path = "../../../../substrate/frame/assets", default-features = false } pallet-balances = { path = "../../../../substrate/frame/balances", default-features = false } pallet-session = { path = "../../../../substrate/frame/session", default-features = false } +pallet-timestamp = { path = "../../../../substrate/frame/timestamp", default-features = false } sp-consensus-aura = { path = "../../../../substrate/primitives/consensus/aura", default-features = false } sp-io = { path = "../../../../substrate/primitives/io", default-features = false } sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } @@ -29,9 +29,7 @@ sp-core = { path = "../../../../substrate/primitives/core", default-features = f cumulus-pallet-parachain-system = { path = "../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-xcmp-queue = { path = "../../../pallets/xcmp-queue", default-features = false } pallet-collator-selection = { path = "../../../pallets/collator-selection", default-features = false } -parachains-common = { path = "../../common", default-features = false } parachain-info = { package = "staging-parachain-info", path = "../../pallets/parachain-info", default-features = false } -assets-common = { path = "../assets/common", default-features = false } cumulus-primitives-core = { path = "../../../primitives/core", default-features = false } cumulus-primitives-parachain-inherent = { path = "../../../primitives/parachain-inherent", default-features = false } cumulus-test-relay-sproof-builder = { path = "../../../test/relay-sproof-builder", default-features = false } @@ -51,7 +49,6 @@ substrate-wasm-builder = { path = "../../../../substrate/utils/wasm-builder" } [features] default = ["std"] std = [ - "assets-common/std", "codec/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-xcmp-queue/std", @@ -60,13 +57,12 @@ std = [ "cumulus-test-relay-sproof-builder/std", "frame-support/std", "frame-system/std", - "pallet-assets/std", "pallet-balances/std", "pallet-collator-selection/std", "pallet-session/std", + "pallet-timestamp/std", "pallet-xcm/std", "parachain-info/std", - "parachains-common/std", "polkadot-parachain-primitives/std", "sp-consensus-aura/std", "sp-core/std", diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index eb75c2f7ee0ac9a6d0e8798c04587b02b60cb3f5..e62daa16a1256fa993a97d82d4f6df96f261018b 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -33,8 +33,8 @@ use polkadot_parachain_primitives::primitives::{ HeadData, HrmpChannelId, RelayChainBlockNumber, XcmpMessageFormat, }; use sp_consensus_aura::{SlotDuration, AURA_ENGINE_ID}; -use sp_core::Encode; -use sp_runtime::{traits::Header, BuildStorage, Digest, DigestItem}; +use sp_core::{Encode, U256}; +use sp_runtime::{traits::Header, BuildStorage, Digest, DigestItem, SaturatedConversion}; use xcm::{ latest::{Asset, Location, XcmContext, XcmHash}, prelude::*, @@ -129,6 +129,7 @@ pub trait BasicParachainRuntime: + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config { } @@ -140,7 +141,8 @@ where + pallet_xcm::Config + parachain_info::Config + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config, + + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config, ValidatorIdOf: From>, { } @@ -259,8 +261,10 @@ pub struct RuntimeHelper( ); /// Utility function that advances the chain to the desired block number. /// If an author is provided, that author information is injected to all the blocks in the meantime. -impl - RuntimeHelper +impl< + Runtime: frame_system::Config + cumulus_pallet_parachain_system::Config + pallet_timestamp::Config, + AllPalletsWithoutSystem, + > RuntimeHelper where AccountIdOf: Into<<::RuntimeOrigin as OriginTrait>::AccountId>, @@ -296,10 +300,73 @@ where last_header.expect("run_to_block empty block range") } + pub fn run_to_block_with_finalize(n: u32) -> HeaderFor { + let mut last_header = None; + loop { + let block_number = frame_system::Pallet::::block_number(); + if block_number >= n.into() { + break + } + // Set the new block number and author + let header = frame_system::Pallet::::finalize(); + + let pre_digest = Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, block_number.encode())], + }; + frame_system::Pallet::::reset_events(); + + let next_block_number = block_number + 1u32.into(); + frame_system::Pallet::::initialize( + &next_block_number, + &header.hash(), + &pre_digest, + ); + AllPalletsWithoutSystem::on_initialize(next_block_number); + + let parent_head = HeadData(header.encode()); + let sproof_builder = RelayStateSproofBuilder { + para_id: ::SelfParaId::get(), + included_para_head: parent_head.clone().into(), + ..Default::default() + }; + + let (relay_parent_storage_root, relay_chain_state) = + sproof_builder.into_state_root_and_proof(); + let inherent_data = ParachainInherentData { + validation_data: PersistedValidationData { + parent_head, + relay_parent_number: (block_number.saturated_into::() * 2 + 1).into(), + relay_parent_storage_root, + max_pov_size: 100_000_000, + }, + relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + + let _ = cumulus_pallet_parachain_system::Pallet::::set_validation_data( + Runtime::RuntimeOrigin::none(), + inherent_data, + ); + let _ = pallet_timestamp::Pallet::::set( + Runtime::RuntimeOrigin::none(), + 300_u32.into(), + ); + AllPalletsWithoutSystem::on_finalize(next_block_number); + let header = frame_system::Pallet::::finalize(); + last_header = Some(header); + } + last_header.expect("run_to_block empty block range") + } + pub fn root_origin() -> ::RuntimeOrigin { ::RuntimeOrigin::root() } + pub fn block_number() -> U256 { + frame_system::Pallet::::block_number().into() + } + pub fn origin_of( account_id: AccountIdOf, ) -> ::RuntimeOrigin { diff --git a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs index 950d0498130e553f57b4a03173b8232ad0bfa061..1c58df189b673af60bc3e4a9ae7391294287e2aa 100644 --- a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs @@ -37,7 +37,8 @@ pub fn change_storage_constant_by_governance_works: From>, StorageConstant: Get, StorageConstantType: Encode + PartialEq + std::fmt::Debug, @@ -91,3 +92,56 @@ pub fn change_storage_constant_by_governance_works( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + runtime_call_encode: Box) -> Vec>, + storage_items: Vec<(Vec, Vec)>, + initialize_storage: impl FnOnce() -> (), + assert_storage: impl FnOnce() -> (), +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_timestamp::Config, + ValidatorIdOf: From>, +{ + let mut runtime = ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build(); + runtime.execute_with(|| { + initialize_storage(); + }); + runtime.execute_with(|| { + // encode `kill_storage` call + let kill_storage_call = runtime_call_encode(frame_system::Call::::set_storage { + items: storage_items.clone(), + }); + + // estimate - storing just 1 value + use frame_system::WeightInfo; + let require_weight_at_most = + ::SystemWeightInfo::set_storage( + storage_items.len().try_into().unwrap(), + ); + + // execute XCM with Transact to `set_storage` as governance does + assert_ok!(RuntimeHelper::::execute_as_governance( + kill_storage_call, + require_weight_at_most + ) + .ensure_complete()); + }); + runtime.execute_with(|| { + assert_storage(); + }); +} diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index cc965c154c60133d2bf055776578cff51f85e4a5..5a8b15740e4b10df36609e2a90f9e60f85807041 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "penpal-runtime" -version = "0.9.27" +version = "0.14.0" authors = ["Anonymous"] description = "A parachain for communication back and forth with XCM of assets and uniques." license = "Unlicense" @@ -20,7 +20,7 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex-literal = { version = "0.4.1", optional = true } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } smallvec = "1.11.0" @@ -68,7 +68,6 @@ xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkad # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } @@ -86,7 +85,6 @@ std = [ "assets-common/std", "codec/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", @@ -141,7 +139,6 @@ std = [ runtime-benchmarks = [ "assets-common/runtime-benchmarks", - "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -159,6 +156,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", @@ -171,7 +169,6 @@ runtime-benchmarks = [ try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-dmp-queue/try-runtime", "cumulus-pallet-parachain-system/try-runtime", "cumulus-pallet-xcm/try-runtime", "cumulus-pallet-xcmp-queue/try-runtime", diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index d33f51df5a9a6d3aa270f1e3003bb52455fd7f57..62065619e42f3cb686877cab5033359d97cd7101 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -114,8 +114,8 @@ pub type BlockId = generic::BlockId; // Id used for identifying assets. pub type AssetId = u32; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -128,7 +128,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; pub type Migrations = ( pallet_balances::migration::MigrateToTrackInactive, @@ -404,7 +404,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -420,6 +419,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = (); } parameter_types! { @@ -609,6 +609,19 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = (); } +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_tx_payment::BenchmarkHelperTrait for AssetTxHelper { + fn create_asset_id_parameter(_id: u32) -> (u32, u32) { + unimplemented!("Penpal uses default weights"); + } + fn setup_balances_and_pool(_asset_id: u32, _account: AccountId) { + unimplemented!("Penpal uses default weights"); + } +} + impl pallet_asset_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Fungibles = Assets; @@ -621,6 +634,9 @@ impl pallet_asset_tx_payment::Config for Runtime { >, AssetsToBlockAuthor, >; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetTxHelper; } impl pallet_sudo::Config for Runtime { @@ -669,6 +685,7 @@ construct_runtime!( mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_session, SessionBench::] @@ -700,7 +717,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -852,6 +869,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; let mut list = Vec::::new(); @@ -868,6 +886,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime {} use cumulus_pallet_session_benchmarking::Pallet as SessionBench; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index b3f84a4bb9cbf8eda7e9ffababbd382ce3bccb53..84b8fd9bb0a361fb04ec8cf14fd76caf5b4fcf5b 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -40,18 +40,16 @@ use frame_system::EnsureRoot; use pallet_asset_tx_payment::HandleCredit; use pallet_assets::Instance1; use pallet_xcm::XcmPassthrough; -use parachains_common::rococo::snowbridge::EthereumNetwork; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use sp_runtime::traits::Zero; use xcm::latest::prelude::*; -#[allow(deprecated)] use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, - EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + ConvertedConcreteId, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, @@ -78,8 +76,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting assets on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type CurrencyTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -136,7 +133,7 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< ForeignAssets, // Use this currency when it is a fungible asset matching the given location or name: ForeignAssetsConvertedConcreteId, - // Convert an XCM MultiLocation into a local account id: + // Convert an XCM Location into a local account id: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, @@ -194,34 +191,29 @@ impl Contains for CommonGoodAssetsParachain { } } -pub type Barrier = TrailingSetTopicAsId< - DenyThenTry< - DenyReserveTransferToRelayChain, +pub type Barrier = TrailingSetTopicAsId<( + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< ( - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - // Allow XCMs with some computed origins to pass through. - WithComputedOrigin< - ( - // If the message is one that immediately attempts to pay for execution, then - // allow it. - AllowTopLevelPaidExecutionFrom, - // System Assets parachain, parent and its exec plurality get free - // execution - AllowExplicitUnpaidExecutionFrom<( - CommonGoodAssetsParachain, - ParentOrParentsExecutivePlurality, - )>, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, + // If the message is one that immediately attempts to pay for execution, then + // allow it. + AllowTopLevelPaidExecutionFrom, + // System Assets parachain, parent and its exec plurality get free + // execution + AllowExplicitUnpaidExecutionFrom<( + CommonGoodAssetsParachain, + ParentOrParentsExecutivePlurality, + )>, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, ), + UniversalLocation, + ConstU32<8>, >, ->; +)>; /// Type alias to conveniently refer to `frame_system`'s `Config::AccountId`. pub type AccountIdOf = ::AccountId; @@ -302,7 +294,13 @@ parameter_types! { 0, [xcm::v3::Junction::PalletInstance(50), xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into())] ); - pub EthereumLocation: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get())]); + + /// The Penpal runtime is utilized for testing with various environment setups. + /// This storage item provides the opportunity to customize testing scenarios + /// by configuring the trusted asset from the `SystemAssetHub`. + /// + /// By default, it is configured as a `SystemAssetHubLocation` and can be modified using `System::set_storage`. + pub storage CustomizableAssetFromSystemAssetHub: Location = SystemAssetHubLocation::get(); } /// Accepts asset with ID `AssetLocation` and is coming from `Origin` chain. @@ -317,11 +315,11 @@ impl, Origin: Get> ContainsPair, NativeAssetFrom, - AssetPrefixFrom, + AssetPrefixFrom, ); pub type TrustedTeleporters = (AssetFromChain,); @@ -333,7 +331,7 @@ impl xcm_executor::Config for XcmConfig { // How to withdraw and deposit an asset. type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = Reserves; + type IsReserve = TrustedReserves; // no teleport trust established with other chains type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; @@ -355,6 +353,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml index a23b7558bcec00a1eda4c4435e0545e42844a389..2023d9abf5d4b4ca2be7d5dd0aa9368065833ae1 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rococo-parachain-runtime" -version = "0.1.0" +version = "0.6.0" authors.workspace = true edition.workspace = true description = "Simple runtime used by the rococo parachain(s)" @@ -50,7 +50,6 @@ polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", def # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } @@ -59,6 +58,7 @@ cumulus-primitives-aura = { path = "../../../../primitives/aura", default-featur cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } parachains-common = { path = "../../../common", default-features = false } +testnet-parachains-constants = { path = "../../constants", default-features = false, features = ["rococo"] } parachain-info = { package = "staging-parachain-info", path = "../../../pallets/parachain-info", default-features = false } [build-dependencies] @@ -69,7 +69,6 @@ default = ["std"] std = [ "codec/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", @@ -109,12 +108,12 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "testnet-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", ] runtime-benchmarks = [ - "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "cumulus-primitives-core/runtime-benchmarks", @@ -127,6 +126,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 999cc480ce0a7874f27477d705a71eb73a49d476..469f06a1aa6fe42c08532aab003afbace08af504 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -75,7 +75,8 @@ use parachains_common::{ }; use xcm_builder::{ AllowKnownQueryResponses, AllowSubscriptionsFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, - FungiblesAdapter, LocalMint, TrailingSetTopicAsId, WithUniqueTopic, + FrameTransactionalProcessor, FungiblesAdapter, LocalMint, TrailingSetTopicAsId, + WithUniqueTopic, }; use xcm_executor::traits::JustTry; @@ -83,14 +84,12 @@ use xcm_executor::traits::JustTry; use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; use polkadot_parachain_primitives::primitives::Sibling; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + EnsureXcmOrigin, FixedWeightBounds, FungibleAdapter, IsConcrete, NativeAsset, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::XcmExecutor; @@ -108,7 +107,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("test-parachain"), impl_name: create_runtime_str!("test-parachain"), authoring_version: 1, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, @@ -258,7 +257,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -269,6 +267,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = (); } impl pallet_sudo::Config for Runtime { @@ -350,8 +349,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting assets on this chain. -#[allow(deprecated)] -pub type CurrencyTransactor = CurrencyAdapter< +pub type CurrencyTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -489,6 +487,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. @@ -646,8 +645,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -659,7 +658,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -691,7 +690,7 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 0a40816fc0b9e47ce552f08b195ffba3b96d4a34..a92e8a4c12ae721dc7c6c06b3f705dcedb87e2d3 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-parachain-bin" -version = "1.6.0" +version = "4.0.0" authors.workspace = true build = "build.rs" edition.workspace = true @@ -16,13 +16,13 @@ path = "src/main.rs" [dependencies] async-trait = "0.1.74" -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } futures = "0.3.28" hex-literal = "0.4.1" -log = "0.4.20" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" +log = { workspace = true, default-features = true } +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } # Local rococo-parachain-runtime = { path = "../parachains/runtimes/testing/rococo-parachain" } @@ -38,10 +38,11 @@ coretime-rococo-runtime = { path = "../parachains/runtimes/coretime/coretime-roc coretime-westend-runtime = { path = "../parachains/runtimes/coretime/coretime-westend" } bridge-hub-westend-runtime = { path = "../parachains/runtimes/bridge-hubs/bridge-hub-westend" } penpal-runtime = { path = "../parachains/runtimes/testing/penpal" } +jsonrpsee = { version = "0.22", features = ["server"] } people-rococo-runtime = { path = "../parachains/runtimes/people/people-rococo" } people-westend-runtime = { path = "../parachains/runtimes/people/people-westend" } -jsonrpsee = { version = "0.16.2", features = ["server"] } parachains-common = { path = "../parachains/common" } +testnet-parachains-constants = { path = "../parachains/runtimes/constants", default-features = false, features = ["rococo", "westend"] } # Substrate frame-benchmarking = { path = "../../substrate/frame/benchmarking" } @@ -135,6 +136,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "glutton-westend-runtime/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "parachains-common/runtime-benchmarks", "penpal-runtime/runtime-benchmarks", "people-rococo-runtime/runtime-benchmarks", diff --git a/cumulus/polkadot-parachain/chain-specs/asset-hub-wococo.json b/cumulus/polkadot-parachain/chain-specs/asset-hub-wococo.json deleted file mode 120000 index 30a63a9fc786bd542e38c2dd2cf769dc4ebde3bf..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/chain-specs/asset-hub-wococo.json +++ /dev/null @@ -1 +0,0 @@ -../../parachains/chain-specs/asset-hub-wococo.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/chain-specs/bridge-hub-wococo.json b/cumulus/polkadot-parachain/chain-specs/bridge-hub-wococo.json deleted file mode 120000 index e13ab77265d59d31fcf822420413d3b5240bf574..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/chain-specs/bridge-hub-wococo.json +++ /dev/null @@ -1 +0,0 @@ -../../parachains/chain-specs/bridge-hub-wococo.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/chain-specs/coretime-rococo.json b/cumulus/polkadot-parachain/chain-specs/coretime-rococo.json new file mode 120000 index 0000000000000000000000000000000000000000..6e47a6ad08cc787e2a66f6426b1516769779e995 --- /dev/null +++ b/cumulus/polkadot-parachain/chain-specs/coretime-rococo.json @@ -0,0 +1 @@ +../../parachains/chain-specs/coretime-rococo.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/chain-specs/coretime-westend.json b/cumulus/polkadot-parachain/chain-specs/coretime-westend.json new file mode 120000 index 0000000000000000000000000000000000000000..80dd771db54fc6067f19fadd883dd2274fb21f9d --- /dev/null +++ b/cumulus/polkadot-parachain/chain-specs/coretime-westend.json @@ -0,0 +1 @@ +../../parachains/chain-specs/coretime-westend.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs index f889e05a1661a82e4e21aa94a52a16d474d9e86e..45920cdb6146b01765fb568506ef861c220b5792 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs @@ -24,10 +24,8 @@ use parachains_common::{AccountId, AuraId, Balance as AssetHubBalance}; use sc_service::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; -const ASSET_HUB_WESTEND_ED: AssetHubBalance = - parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; -const ASSET_HUB_ROCOCO_ED: AssetHubBalance = - parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; +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. /// @@ -68,7 +66,7 @@ pub fn asset_hub_westend_development_config() -> GenericChainSpec { get_account_id_from_seed::("Alice//stash"), get_account_id_from_seed::("Bob//stash"), ], - parachains_common::westend::currency::UNITS * 1_000_000, + testnet_parachains_constants::westend::currency::UNITS * 1_000_000, 1000.into(), )) .with_properties(properties) @@ -114,7 +112,7 @@ pub fn asset_hub_westend_local_config() -> GenericChainSpec { get_account_id_from_seed::("Eve//stash"), get_account_id_from_seed::("Ferdie//stash"), ], - parachains_common::westend::currency::UNITS * 1_000_000, + testnet_parachains_constants::westend::currency::UNITS * 1_000_000, 1000.into(), )) .with_properties(properties) @@ -243,7 +241,7 @@ fn asset_hub_rococo_like_development_config( get_account_id_from_seed::("Alice//stash"), get_account_id_from_seed::("Bob//stash"), ], - parachains_common::rococo::currency::UNITS * 1_000_000, + testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, para_id.into(), )) .with_properties(properties) @@ -302,7 +300,7 @@ fn asset_hub_rococo_like_local_config( get_account_id_from_seed::("Eve//stash"), get_account_id_from_seed::("Ferdie//stash"), ], - parachains_common::rococo::currency::UNITS * 1_000_000, + testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, para_id.into(), )) .with_properties(properties) diff --git a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 1f43edf2243c0436bb9564c1724a1860ce404624..15e8a1bf11a055e6c5b4950ad07d78cad72f81a8 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -25,7 +25,10 @@ use std::str::FromStr; #[derive(Debug, PartialEq)] pub enum BridgeHubRuntimeType { Kusama, + KusamaLocal, + Polkadot, + PolkadotLocal, Rococo, RococoLocal, @@ -44,7 +47,9 @@ impl FromStr for BridgeHubRuntimeType { fn from_str(value: &str) -> Result { match value { polkadot::BRIDGE_HUB_POLKADOT => Ok(BridgeHubRuntimeType::Polkadot), + polkadot::BRIDGE_HUB_POLKADOT_LOCAL => Ok(BridgeHubRuntimeType::PolkadotLocal), kusama::BRIDGE_HUB_KUSAMA => Ok(BridgeHubRuntimeType::Kusama), + kusama::BRIDGE_HUB_KUSAMA_LOCAL => Ok(BridgeHubRuntimeType::KusamaLocal), westend::BRIDGE_HUB_WESTEND => Ok(BridgeHubRuntimeType::Westend), westend::BRIDGE_HUB_WESTEND_LOCAL => Ok(BridgeHubRuntimeType::WestendLocal), westend::BRIDGE_HUB_WESTEND_DEVELOPMENT => Ok(BridgeHubRuntimeType::WestendDevelopment), @@ -103,6 +108,7 @@ impl BridgeHubRuntimeType { Some("Bob".to_string()), |_| (), ))), + other => Err(std::format!("No default config present for {:?}", other)), } } } @@ -133,7 +139,7 @@ pub mod rococo { pub(crate) const BRIDGE_HUB_ROCOCO_LOCAL: &str = "bridge-hub-rococo-local"; pub(crate) const BRIDGE_HUB_ROCOCO_DEVELOPMENT: &str = "bridge-hub-rococo-dev"; const BRIDGE_HUB_ROCOCO_ED: BridgeHubBalance = - parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; + bridge_hub_rococo_runtime::ExistentialDeposit::get(); pub fn local_config( id: &str, @@ -242,6 +248,7 @@ pub mod rococo { /// Sub-module for Kusama setup pub mod kusama { pub(crate) const BRIDGE_HUB_KUSAMA: &str = "bridge-hub-kusama"; + pub(crate) const BRIDGE_HUB_KUSAMA_LOCAL: &str = "bridge-hub-kusama-local"; } /// Sub-module for Westend setup. @@ -257,7 +264,7 @@ pub mod westend { pub(crate) const BRIDGE_HUB_WESTEND_LOCAL: &str = "bridge-hub-westend-local"; pub(crate) const BRIDGE_HUB_WESTEND_DEVELOPMENT: &str = "bridge-hub-westend-dev"; const BRIDGE_HUB_WESTEND_ED: BridgeHubBalance = - parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; + bridge_hub_westend_runtime::ExistentialDeposit::get(); pub fn local_config( id: &str, @@ -358,4 +365,5 @@ pub mod westend { /// Sub-module for Polkadot setup pub mod polkadot { pub(crate) const BRIDGE_HUB_POLKADOT: &str = "bridge-hub-polkadot"; + pub(crate) const BRIDGE_HUB_POLKADOT_LOCAL: &str = "bridge-hub-polkadot-local"; } diff --git a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs index dd67bf975f773e8933430c36229334a45de8454b..c0a9f195d89bc1a3b56001e8e60ad1834660959c 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs @@ -24,7 +24,7 @@ use sc_service::ChainType; use sp_core::sr25519; const COLLECTIVES_WESTEND_ED: CollectivesBalance = - parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; + collectives_westend_runtime::ExistentialDeposit::get(); /// Generate the session keys from individual elements. /// diff --git a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs index 87ac1ed2fa18970fbe54cc6bd70dcf0305e98620..4e89b81d1be40fb5b0bc5d5f689a8883ba612fa1 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs @@ -29,7 +29,7 @@ const CONTRACTS_PARACHAIN_ID: u32 = 1002; /// The existential deposit is determined by the runtime "contracts-rococo". const CONTRACTS_ROCOCO_ED: contracts_rococo_runtime::Balance = - parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; + testnet_parachains_constants::rococo::currency::EXISTENTIAL_DEPOSIT; pub fn contracts_rococo_development_config() -> GenericChainSpec { let mut properties = sc_chain_spec::Properties::new(); diff --git a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs index fbce4e5bc21f9dbdccf3da9078402cac3aae815d..42d56fc80eb3eee2810da1a414a13e346d6f8d17 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs @@ -29,6 +29,8 @@ pub enum CoretimeRuntimeType { // Benchmarks RococoDevelopment, + // Live + Westend, // Local WestendLocal, // Benchmarks @@ -43,6 +45,7 @@ impl FromStr for CoretimeRuntimeType { rococo::CORETIME_ROCOCO => Ok(CoretimeRuntimeType::Rococo), rococo::CORETIME_ROCOCO_LOCAL => Ok(CoretimeRuntimeType::RococoLocal), rococo::CORETIME_ROCOCO_DEVELOPMENT => Ok(CoretimeRuntimeType::RococoDevelopment), + westend::CORETIME_WESTEND => Ok(CoretimeRuntimeType::Westend), westend::CORETIME_WESTEND_LOCAL => Ok(CoretimeRuntimeType::WestendLocal), westend::CORETIME_WESTEND_DEVELOPMENT => Ok(CoretimeRuntimeType::WestendDevelopment), _ => Err(format!("Value '{}' is not configured yet", value)), @@ -56,6 +59,7 @@ impl From for &str { CoretimeRuntimeType::Rococo => rococo::CORETIME_ROCOCO, CoretimeRuntimeType::RococoLocal => rococo::CORETIME_ROCOCO_LOCAL, CoretimeRuntimeType::RococoDevelopment => rococo::CORETIME_ROCOCO_DEVELOPMENT, + CoretimeRuntimeType::Westend => westend::CORETIME_WESTEND, CoretimeRuntimeType::WestendLocal => westend::CORETIME_WESTEND_LOCAL, CoretimeRuntimeType::WestendDevelopment => westend::CORETIME_WESTEND_DEVELOPMENT, } @@ -65,7 +69,7 @@ impl From for &str { impl From for ChainType { fn from(runtime_type: CoretimeRuntimeType) -> Self { match runtime_type { - CoretimeRuntimeType::Rococo => ChainType::Live, + CoretimeRuntimeType::Rococo | CoretimeRuntimeType::Westend => ChainType::Live, CoretimeRuntimeType::RococoLocal | CoretimeRuntimeType::WestendLocal => ChainType::Local, CoretimeRuntimeType::RococoDevelopment | CoretimeRuntimeType::WestendDevelopment => @@ -82,12 +86,15 @@ impl CoretimeRuntimeType { pub fn load_config(&self) -> Result, String> { match self { CoretimeRuntimeType::Rococo => Ok(Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../../../parachains/chain-specs/coretime-rococo.json")[..], + &include_bytes!("../../chain-specs/coretime-rococo.json")[..], )?)), CoretimeRuntimeType::RococoLocal => Ok(Box::new(rococo::local_config(*self, "rococo-local"))), CoretimeRuntimeType::RococoDevelopment => Ok(Box::new(rococo::local_config(*self, "rococo-dev"))), + CoretimeRuntimeType::Westend => Ok(Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../../parachains/chain-specs/coretime-westend.json")[..], + )?)), CoretimeRuntimeType::WestendLocal => Ok(Box::new(westend::local_config(*self, "westend-local"))), CoretimeRuntimeType::WestendDevelopment => @@ -120,7 +127,7 @@ pub mod rococo { pub(crate) const CORETIME_ROCOCO: &str = "coretime-rococo"; pub(crate) const CORETIME_ROCOCO_LOCAL: &str = "coretime-rococo-local"; pub(crate) const CORETIME_ROCOCO_DEVELOPMENT: &str = "coretime-rococo-dev"; - const CORETIME_ROCOCO_ED: Balance = parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; + const CORETIME_ROCOCO_ED: Balance = coretime_rococo_runtime::ExistentialDeposit::get(); pub fn local_config(runtime_type: CoretimeRuntimeType, relay_chain: &str) -> GenericChainSpec { // Rococo defaults @@ -213,9 +220,10 @@ pub mod westend { use parachains_common::{AccountId, AuraId, Balance}; use sp_core::sr25519; + pub(crate) const CORETIME_WESTEND: &str = "coretime-westend"; pub(crate) const CORETIME_WESTEND_LOCAL: &str = "coretime-westend-local"; pub(crate) const CORETIME_WESTEND_DEVELOPMENT: &str = "coretime-westend-dev"; - const CORETIME_WESTEND_ED: Balance = parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; + const CORETIME_WESTEND_ED: Balance = coretime_westend_runtime::ExistentialDeposit::get(); pub fn local_config(runtime_type: CoretimeRuntimeType, relay_chain: &str) -> GenericChainSpec { // westend defaults diff --git a/cumulus/polkadot-parachain/src/chain_spec/people.rs b/cumulus/polkadot-parachain/src/chain_spec/people.rs index aa78bfdb76f0f7067580ff3fc29acb3f75057280..67786e17205d86e5108721c6a6ef7209fe9297f2 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/people.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/people.rs @@ -106,14 +106,14 @@ pub mod rococo { get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, SAFE_XCM_VERSION, }; - use parachains_common::{rococo::currency::EXISTENTIAL_DEPOSIT, AccountId, AuraId}; + use parachains_common::{AccountId, AuraId}; use sc_chain_spec::ChainType; use sp_core::sr25519; pub(crate) const PEOPLE_ROCOCO: &str = "people-rococo"; pub(crate) const PEOPLE_ROCOCO_LOCAL: &str = "people-rococo-local"; - pub(crate) const PEOPLE_ROCOCO_DEVELOPMENT: &str = "people-rococo-development"; - const PEOPLE_ROCOCO_ED: PeopleBalance = EXISTENTIAL_DEPOSIT; + pub(crate) const PEOPLE_ROCOCO_DEVELOPMENT: &str = "people-rococo-dev"; + const PEOPLE_ROCOCO_ED: PeopleBalance = people_rococo_runtime::ExistentialDeposit::get(); pub fn local_config( id: &str, @@ -216,14 +216,14 @@ pub mod westend { get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, SAFE_XCM_VERSION, }; - use parachains_common::{westend::currency::EXISTENTIAL_DEPOSIT, AccountId, AuraId}; + use parachains_common::{AccountId, AuraId}; use sc_chain_spec::ChainType; use sp_core::sr25519; pub(crate) const PEOPLE_WESTEND: &str = "people-westend"; pub(crate) const PEOPLE_WESTEND_LOCAL: &str = "people-westend-local"; - pub(crate) const PEOPLE_WESTEND_DEVELOPMENT: &str = "people-westend-development"; - const PEOPLE_WESTEND_ED: PeopleBalance = EXISTENTIAL_DEPOSIT; + pub(crate) const PEOPLE_WESTEND_DEVELOPMENT: &str = "people-westend-dev"; + const PEOPLE_WESTEND_ED: PeopleBalance = people_westend_runtime::ExistentialDeposit::get(); pub fn local_config( id: &str, diff --git a/cumulus/polkadot-parachain/src/command.rs b/cumulus/polkadot-parachain/src/command.rs index a7319b6dfbb25b7d2c9ac3f2125ff03fe6f5be76..9ba7b7876b3ee06f0d61c626d4fa3c234e313ff9 100644 --- a/cumulus/polkadot-parachain/src/command.rs +++ b/cumulus/polkadot-parachain/src/command.rs @@ -23,6 +23,7 @@ use crate::{ }, service::{new_partial, Block}, }; +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; @@ -398,7 +399,7 @@ macro_rules! construct_partials { Runtime::AssetHubPolkadot => { let $partials = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AssetHubPolkadotAuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AssetHubPolkadotAuraId>, )?; $code }, @@ -412,28 +413,21 @@ macro_rules! construct_partials { Runtime::People(_) => { let $partials = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AuraId>, )?; $code }, Runtime::GluttonWestend | Runtime::Glutton | Runtime::Shell | Runtime::Seedling => { let $partials = new_partial::( &$config, - crate::service::shell_build_import_queue, + crate::service::build_shell_import_queue, )?; $code }, - Runtime::ContractsRococo => { + Runtime::ContractsRococo | Runtime::Penpal(_) | Runtime::Default => { let $partials = new_partial::( &$config, - crate::service::contracts_rococo_build_import_queue, - )?; - $code - }, - Runtime::Penpal(_) | Runtime::Default => { - let $partials = new_partial::( - &$config, - crate::service::rococo_parachain_build_import_queue, + crate::service::build_aura_import_queue, )?; $code }, @@ -449,7 +443,7 @@ macro_rules! construct_async_run { runner.async_run(|$config| { let $components = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AssetHubPolkadotAuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AssetHubPolkadotAuraId>, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) @@ -466,7 +460,7 @@ macro_rules! construct_async_run { runner.async_run(|$config| { let $components = new_partial::( &$config, - crate::service::aura_build_import_queue::<_, AuraId>, + crate::service::build_relay_to_aura_import_queue::<_, AuraId>, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) @@ -479,30 +473,20 @@ macro_rules! construct_async_run { runner.async_run(|$config| { let $components = new_partial::( &$config, - crate::service::shell_build_import_queue, + crate::service::build_shell_import_queue, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) }) } - Runtime::ContractsRococo => { - runner.async_run(|$config| { - let $components = new_partial::( - &$config, - crate::service::contracts_rococo_build_import_queue, - )?; - let task_manager = $components.task_manager; - { $( $code )* }.map(|v| (v, task_manager)) - }) - }, - Runtime::Penpal(_) | Runtime::Default => { + Runtime::ContractsRococo | Runtime::Penpal(_) | Runtime::Default => { runner.async_run(|$config| { let $components = new_partial::< RuntimeApi, _, >( &$config, - crate::service::rococo_parachain_build_import_queue, + crate::service::build_aura_import_queue, )?; let task_manager = $components.task_manager; { $( $code )* }.map(|v| (v, task_manager)) @@ -584,7 +568,7 @@ pub fn run() -> Result<()> { match cmd { BenchmarkCmd::Pallet(cmd) => if cfg!(feature = "runtime-benchmarks") { - runner.sync_run(|config| cmd.run::(config)) + runner.sync_run(|config| cmd.run::, ReclaimHostFunctions>(config)) } else { Err("Benchmarking wasn't enabled when building the node. \ You can enable it with `--features runtime-benchmarks`." @@ -695,7 +679,7 @@ pub fn run() -> Result<()> { .map(|r| r.0) .map_err(Into::into), - AssetHubKusama | AssetHubWestend => + AssetHubKusama => crate::service::start_asset_hub_node::< RuntimeApi, AuraId, @@ -704,7 +688,7 @@ pub fn run() -> Result<()> { .map(|r| r.0) .map_err(Into::into), - AssetHubRococo => + AssetHubRococo | AssetHubWestend => crate::service::start_asset_hub_lookahead_node::< RuntimeApi, AuraId, @@ -713,17 +697,20 @@ pub fn run() -> Result<()> { .map(|r| r.0) .map_err(Into::into), - CollectivesPolkadot | CollectivesWestend => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + CollectivesPolkadot => + crate::service::start_generic_aura_node(config, polkadot_config, collator_options, id, hwbench) + .await + .map(|r| r.0) + .map_err(Into::into), + + CollectivesWestend => + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0) .map_err(Into::into), Seedling | Shell => - crate::service::start_shell_node::( + crate::service::start_shell_node( config, polkadot_config, collator_options, @@ -746,36 +733,26 @@ pub fn run() -> Result<()> { .map_err(Into::into), BridgeHub(bridge_hub_runtime_type) => match bridge_hub_runtime_type { - chain_spec::bridge_hubs::BridgeHubRuntimeType::Polkadot => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + chain_spec::bridge_hubs::BridgeHubRuntimeType::Polkadot | + chain_spec::bridge_hubs::BridgeHubRuntimeType::PolkadotLocal => + crate::service::start_generic_aura_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), - chain_spec::bridge_hubs::BridgeHubRuntimeType::Kusama => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + chain_spec::bridge_hubs::BridgeHubRuntimeType::Kusama | + chain_spec::bridge_hubs::BridgeHubRuntimeType::KusamaLocal => + crate::service::start_generic_aura_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), chain_spec::bridge_hubs::BridgeHubRuntimeType::Westend | chain_spec::bridge_hubs::BridgeHubRuntimeType::WestendLocal | chain_spec::bridge_hubs::BridgeHubRuntimeType::WestendDevelopment => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoDevelopment => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), } @@ -785,12 +762,10 @@ pub fn run() -> Result<()> { chain_spec::coretime::CoretimeRuntimeType::Rococo | chain_spec::coretime::CoretimeRuntimeType::RococoLocal | chain_spec::coretime::CoretimeRuntimeType::RococoDevelopment | + chain_spec::coretime::CoretimeRuntimeType::Westend | chain_spec::coretime::CoretimeRuntimeType::WestendLocal | chain_spec::coretime::CoretimeRuntimeType::WestendDevelopment => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), } @@ -809,10 +784,7 @@ pub fn run() -> Result<()> { .map_err(Into::into), Glutton | GluttonWestend => - crate::service::start_basic_lookahead_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_basic_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0) .map_err(Into::into), @@ -824,10 +796,7 @@ pub fn run() -> Result<()> { chain_spec::people::PeopleRuntimeType::Westend | chain_spec::people::PeopleRuntimeType::WestendLocal | chain_spec::people::PeopleRuntimeType::WestendDevelopment => - crate::service::start_generic_aura_node::< - RuntimeApi, - AuraId, - >(config, polkadot_config, collator_options, id, hwbench) + crate::service::start_generic_aura_lookahead_node(config, polkadot_config, collator_options, id, hwbench) .await .map(|r| r.0), } diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs index 76dd7347ccbc35127747e144af7c8705a15022e4..7778d1bf7d2dc0187fe6a0684023a9a4648be596 100644 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs +++ b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs @@ -39,7 +39,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs index 0f01b85ebcf6fa63aabd9115c2ef553c18badea8..880f5d760c74559db87597bce158f8f5e62dbd82 100644 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs +++ b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs @@ -39,7 +39,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/cumulus/polkadot-parachain/src/service.rs b/cumulus/polkadot-parachain/src/service.rs index 61b9cbbd80d9fd2895d2c319f33d8c9fc8bc4b89..ddf595ca70c1d675aa66a94daf21036cc8a3295e 100644 --- a/cumulus/polkadot-parachain/src/service.rs +++ b/cumulus/polkadot-parachain/src/service.rs @@ -36,12 +36,13 @@ use cumulus_primitives_core::{ ParaId, }; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; +use sc_rpc::DenyUnsafe; use sp_core::Pair; use jsonrpsee::RpcModule; -use crate::{fake_runtime_api::aura::RuntimeApi, rpc}; -pub use parachains_common::{AccountId, Balance, Block, Hash, Header, Nonce}; +use crate::{fake_runtime_api::aura::RuntimeApi as FakeRuntimeApi, rpc}; +pub use parachains_common::{AccountId, AuraId, Balance, Block, Hash, Header, Nonce}; use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier; use futures::{lock::Mutex, prelude::*}; @@ -68,11 +69,15 @@ use substrate_prometheus_endpoint::Registry; use polkadot_primitives::CollatorPair; #[cfg(not(feature = "runtime-benchmarks"))] -type HostFunctions = sp_io::SubstrateHostFunctions; +type HostFunctions = + (sp_io::SubstrateHostFunctions, cumulus_client_service::storage_proof_size::HostFunctions); #[cfg(feature = "runtime-benchmarks")] -type HostFunctions = - (sp_io::SubstrateHostFunctions, frame_benchmarking::benchmarking::HostFunctions); +type HostFunctions = ( + sp_io::SubstrateHostFunctions, + cumulus_client_service::storage_proof_size::HostFunctions, + frame_benchmarking::benchmarking::HostFunctions, +); type ParachainClient = TFullClient>; @@ -81,141 +86,6 @@ type ParachainBackend = TFullBackend; type ParachainBlockImport = TParachainBlockImport>, ParachainBackend>; -/// Native executor instance. -pub struct ShellRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for ShellRuntimeExecutor { - type ExtendHostFunctions = (); - - fn dispatch(method: &str, data: &[u8]) -> Option> { - shell_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - shell_runtime::native_version() - } -} - -/// Native Asset Hub Westend (Westmint) executor instance. -pub struct AssetHubWestendExecutor; -impl sc_executor::NativeExecutionDispatch for AssetHubWestendExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - asset_hub_westend_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - asset_hub_westend_runtime::native_version() - } -} - -/// Native Westend Collectives executor instance. -pub struct CollectivesWestendRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for CollectivesWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - collectives_westend_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - collectives_westend_runtime::native_version() - } -} - -/// Native BridgeHubRococo executor instance. -pub struct BridgeHubRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for BridgeHubRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - bridge_hub_rococo_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - bridge_hub_rococo_runtime::native_version() - } -} - -/// Native `CoretimeRococo` executor instance. -pub struct CoretimeRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for CoretimeRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - coretime_rococo_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - coretime_rococo_runtime::native_version() - } -} - -/// Native `CoretimeWestend` executor instance. -pub struct CoretimeWestendRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for CoretimeWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - coretime_westend_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - coretime_westend_runtime::native_version() - } -} - -/// Native contracts executor instance. -pub struct ContractsRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for ContractsRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - contracts_rococo_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - contracts_rococo_runtime::native_version() - } -} - -/// Native Westend Glutton executor instance. -pub struct GluttonWestendRuntimeExecutor; - -impl sc_executor::NativeExecutionDispatch for GluttonWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option> { - glutton_westend_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - glutton_westend_runtime::native_version() - } -} - -/// Native `PeopleWestend` executor instance. -pub struct PeopleWestendRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for PeopleWestendRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - people_westend_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - people_westend_runtime::native_version() - } -} - -/// Native `PeopleRococo` executor instance. -pub struct PeopleRococoRuntimeExecutor; -impl sc_executor::NativeExecutionDispatch for PeopleRococoRuntimeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - fn dispatch(method: &str, data: &[u8]) -> Option> { - people_rococo_runtime::api::dispatch(method, data) - } - fn native_version() -> sc_executor::NativeVersion { - people_rococo_runtime::native_version() - } -} - /// Assembly of PartialComponents (enough to run chain ops subcommands) pub type Service = PartialComponents< ParachainClient, @@ -274,10 +144,11 @@ where .build(); let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( + sc_service::new_full_parts_record_import::( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, + true, )?; let client = Arc::new(client); @@ -318,12 +189,11 @@ where }) } -/// Start a shell node with the given parachain `Configuration` and relay chain `Configuration`. +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. /// -/// This is the actual implementation that is abstract over the executor and the runtime api for -/// shell nodes. +/// This is the actual implementation that is abstract over the executor and the runtime api. #[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_shell_node_impl( +async fn start_node_impl( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, @@ -342,8 +212,15 @@ where + sp_api::ApiExt + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo, - RB: Fn(Arc>) -> Result, sc_service::Error> + + cumulus_primitives_core::CollectCollationInfo + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + frame_rpc_system::AccountNonceApi, + RB: Fn( + DenyUnsafe, + Arc>, + Arc, + Arc>>, + ) -> Result, sc_service::Error> + 'static, BIQ: FnOnce( Arc>, @@ -367,6 +244,7 @@ where CollatorPair, OverseerHandle, Arc>) + Send + Sync>, + Arc, ) -> Result<(), sc_service::Error>, { let parachain_config = prepare_node_config(parachain_config); @@ -378,7 +256,6 @@ where 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, @@ -410,8 +287,20 @@ where }) .await?; - let rpc_client = client.clone(); - let rpc_builder = Box::new(move |_, _| rpc_ext_builder(rpc_client.clone())); + let rpc_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + let backend_for_rpc = backend.clone(); + + Box::new(move |deny_unsafe, _| { + rpc_ext_builder( + deny_unsafe, + client.clone(), + backend_for_rpc.clone(), + transaction_pool.clone(), + ) + }) + }; sc_service::spawn_tasks(sc_service::SpawnTasksParams { rpc_builder, @@ -488,6 +377,7 @@ where collator_key.expect("Command line arguments do not allow this. qed"), overseer_handle, announce_block, + backend.clone(), )?; } @@ -496,679 +386,179 @@ where Ok((task_manager, client)) } -/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. -/// -/// This is the actual implementation that is abstract over the executor and the runtime api. -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_node_impl( +/// Build the import queue for Aura-based runtimes. +pub fn build_aura_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> { + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + + cumulus_client_consensus_aura::import_queue::< + sp_consensus_aura::sr25519::AuthorityPair, + _, + _, + _, + _, + _, + >(cumulus_client_consensus_aura::ImportQueueParams { + block_import, + client, + create_inherent_data_providers: move |_, _| async move { + 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)) + }, + registry: config.prometheus_registry(), + spawner: &task_manager.spawn_essential_handle(), + telemetry, + }) + .map_err(Into::into) +} + +/// Start a rococo parachain node. +pub async fn start_rococo_parachain_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, - sybil_resistance_level: CollatorSybilResistance, para_id: ParaId, - _rpc_ext_builder: RB, - build_import_queue: BIQ, - start_consensus: SC, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + build_parachain_rpc_extensions::, + build_aura_import_queue, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + +/// Build the import queue for the shell runtime. +pub fn build_shell_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + _: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> { + cumulus_client_consensus_relay_chain::import_queue( + client, + block_import, + |_, _| async { Ok(()) }, + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + ) + .map_err(Into::into) +} + +fn build_parachain_rpc_extensions( + deny_unsafe: sc_rpc::DenyUnsafe, + client: Arc>, + backend: Arc, + pool: Arc>>, +) -> Result, sc_service::Error> where RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + frame_rpc_system::AccountNonceApi, - RB: Fn(Arc>) -> Result, sc_service::Error>, - BIQ: FnOnce( - Arc>, - ParachainBlockImport, - &Configuration, - Option, - &TaskManager, - ) -> Result, sc_service::Error>, - SC: FnOnce( - Arc>, - ParachainBlockImport, - Option<&Registry>, - Option, - &TaskManager, - Arc, - Arc>>, - Arc>, - KeystorePtr, - Duration, - ParaId, - CollatorPair, - OverseerHandle, - Arc>) + Send + Sync>, - Arc, - ) -> Result<(), sc_service::Error>, { - let parachain_config = prepare_node_config(parachain_config); + let deps = rpc::FullDeps { client, pool, deny_unsafe }; - let params = new_partial::(¶chain_config, build_import_queue)?; - let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + rpc::create_full(deps, backend).map_err(Into::into) +} - let client = params.client.clone(); - let backend = params.backend.clone(); +fn build_contracts_rpc_extensions( + deny_unsafe: sc_rpc::DenyUnsafe, + client: Arc>, + _backend: Arc, + pool: Arc>>, +) -> Result, sc_service::Error> { + let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; - let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( + crate::rpc::create_contracts_rococo(deps).map_err(Into::into) +} + +/// Start a polkadot-shell parachain node. +pub async fn start_shell_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options.clone(), - hwbench.clone(), + collator_options, + CollatorSybilResistance::Unresistant, // free-for-all consensus + para_id, + |_, _, _, _| Ok(RpcModule::new(())), + build_shell_import_queue, + start_relay_chain_consensus, + hwbench, ) .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::new(¶chain_config.network); +} - 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, - }) - .await?; +enum BuildOnAccess { + Uninitialized(Option R + Send + Sync>>), + Initialized(R), +} - let rpc_builder = { - let client = client.clone(); - let transaction_pool = transaction_pool.clone(); +impl BuildOnAccess { + fn get_mut(&mut self) -> &mut R { + loop { + match self { + Self::Uninitialized(f) => { + *self = Self::Initialized((f.take().unwrap())()); + }, + Self::Initialized(ref mut r) => return r, + } + } + } +} - let backend_for_rpc = backend.clone(); - Box::new(move |deny_unsafe, _| { - let deps = rpc::FullDeps { - client: client.clone(), - pool: transaction_pool.clone(), - deny_unsafe, - }; +/// Special [`ParachainConsensus`] implementation that waits for the upgrade from +/// shell to a parachain runtime that implements Aura. +struct WaitForAuraConsensus { + client: Arc, + aura_consensus: Arc>>>>, + relay_chain_consensus: Arc>>>, + _phantom: PhantomData, +} - rpc::create_full(deps, backend_for_rpc.clone()).map_err(Into::into) - }) - }; - - 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), - ); +impl Clone for WaitForAuraConsensus { + fn clone(&self) -> Self { + Self { + client: self.client.clone(), + aura_consensus: self.aura_consensus.clone(), + relay_chain_consensus: self.relay_chain_consensus.clone(), + _phantom: PhantomData, } } - - 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: sync_service.clone(), - })?; - - if validator { - 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, - sync_service.clone(), - 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(), - )?; - } - - start_network.start_network(); - - Ok((task_manager, client)) } -/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. -/// -/// This is the actual implementation that is abstract over the executor and the runtime api. -/// -/// This node is basic in the sense that it doesn't support functionality like transaction -/// payment. Intended to replace start_shell_node in use for glutton, shell, and seedling. -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_basic_lookahead_node_impl( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - sybil_resistance_level: CollatorSybilResistance, - para_id: ParaId, - rpc_ext_builder: RB, - build_import_queue: BIQ, - start_consensus: SC, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> +#[async_trait::async_trait] +impl ParachainConsensus for WaitForAuraConsensus where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + frame_rpc_system::AccountNonceApi, - RB: Fn(Arc>) -> Result, sc_service::Error> - + 'static, - BIQ: FnOnce( - Arc>, - ParachainBlockImport, - &Configuration, - Option, - &TaskManager, - ) -> Result, sc_service::Error>, - SC: FnOnce( - Arc>, - ParachainBlockImport, - Option<&Registry>, - Option, - &TaskManager, - Arc, - Arc>>, - Arc>, - KeystorePtr, - Duration, - ParaId, - CollatorPair, - OverseerHandle, - Arc>) + Send + Sync>, - Arc, - ) -> Result<(), sc_service::Error>, -{ - let parachain_config = prepare_node_config(parachain_config); - - let params = new_partial::(¶chain_config, build_import_queue)?; - 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::new(¶chain_config.network); - - 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, - }) - .await?; - - let rpc_client = client.clone(); - let rpc_builder = Box::new(move |_, _| rpc_ext_builder(rpc_client.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: sync_service.clone(), - })?; - - if validator { - 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, - sync_service.clone(), - 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(), - )?; - } - - start_network.start_network(); - - Ok((task_manager, client)) -} - -/// Build the import queue for the rococo parachain runtime. -pub fn rococo_parachain_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; - - cumulus_client_consensus_aura::import_queue::< - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - >(cumulus_client_consensus_aura::ImportQueueParams { - block_import, - client, - create_inherent_data_providers: move |_, _| async move { - 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)) - }, - registry: config.prometheus_registry(), - spawner: &task_manager.spawn_essential_handle(), - telemetry, - }) - .map_err(Into::into) -} - -/// Start a rococo parachain node. -pub async fn start_rococo_parachain_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> { - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - rococo_parachain_build_import_queue, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - backend| { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; - - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - let proposer = Proposer::new(proposer_factory); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let params = AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend.clone(), - relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: false, - }; - - let fut = aura::run::< - Block, - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - _, - _, - _, - _, - >(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - - Ok(()) - }, - hwbench, - ) - .await -} - -/// Build the import queue for the shell runtime. -pub fn shell_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - _: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder, -{ - cumulus_client_consensus_relay_chain::import_queue( - client, - block_import, - |_, _| async { Ok(()) }, - &task_manager.spawn_essential_handle(), - config.prometheus_registry(), - ) - .map_err(Into::into) -} - -/// Start a polkadot-shell parachain node. -pub async fn start_shell_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo, -{ - start_shell_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Unresistant, // free-for-all consensus - para_id, - |_| Ok(RpcModule::new(())), - shell_build_import_queue, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - _sync_oracle, - _keystore, - _relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block| { - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry, - ); - - let free_for_all = cumulus_client_consensus_relay_chain::build_relay_chain_consensus( - cumulus_client_consensus_relay_chain::BuildRelayChainConsensusParams { - para_id, - proposer_factory, - block_import, - relay_chain_interface: relay_chain_interface.clone(), - create_inherent_data_providers: move |_, (relay_parent, validation_data)| { - 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; - let parachain_inherent = parachain_inherent.ok_or_else(|| { - Box::::from( - "Failed to create parachain inherent", - ) - })?; - Ok(parachain_inherent) - } - }, - }, - ); - - let spawner = task_manager.spawn_handle(); - - // Required for free-for-all consensus - #[allow(deprecated)] - old_consensus::start_collator_sync(old_consensus::StartCollatorParams { - para_id, - block_status: client.clone(), - announce_block, - overseer_handle, - spawner, - key: collator_key, - parachain_consensus: free_for_all, - runtime_api: client.clone(), - }); - - Ok(()) - }, - hwbench, - ) - .await -} - -enum BuildOnAccess { - Uninitialized(Option R + Send + Sync>>), - Initialized(R), -} - -impl BuildOnAccess { - fn get_mut(&mut self) -> &mut R { - loop { - match self { - Self::Uninitialized(f) => { - *self = Self::Initialized((f.take().unwrap())()); - }, - Self::Initialized(ref mut r) => return r, - } - } - } -} - -/// Special [`ParachainConsensus`] implementation that waits for the upgrade from -/// shell to a parachain runtime that implements Aura. -struct WaitForAuraConsensus { - client: Arc, - aura_consensus: Arc>>>>, - relay_chain_consensus: Arc>>>, - _phantom: PhantomData, -} - -impl Clone for WaitForAuraConsensus { - fn clone(&self) -> Self { - Self { - client: self.client.clone(), - aura_consensus: self.aura_consensus.clone(), - relay_chain_consensus: self.relay_chain_consensus.clone(), - _phantom: PhantomData, - } - } -} - -#[async_trait::async_trait] -impl ParachainConsensus for WaitForAuraConsensus -where - Client: sp_api::ProvideRuntimeApi + Send + Sync, - Client::Api: AuraApi, - AuraId: Send + Codec + Sync, + Client: sp_api::ProvideRuntimeApi + Send + Sync, + Client::Api: AuraApi, + AuraId: Send + Codec + Sync, { async fn produce_candidate( &mut self, @@ -1178,372 +568,66 @@ where ) -> Option> { if self .client - .runtime_api() - .has_api::>(parent.hash()) - .unwrap_or(false) - { - self.aura_consensus - .lock() - .await - .get_mut() - .produce_candidate(parent, relay_parent, validation_data) - .await - } else { - self.relay_chain_consensus - .lock() - .await - .produce_candidate(parent, relay_parent, validation_data) - .await - } - } -} - -struct Verifier { - client: Arc, - aura_verifier: BuildOnAccess>>, - relay_chain_verifier: Box>, - _phantom: PhantomData, -} - -#[async_trait::async_trait] -impl VerifierT for Verifier -where - Client: sp_api::ProvideRuntimeApi + Send + Sync, - Client::Api: AuraApi, - AuraId: Send + Sync + Codec, -{ - async fn verify( - &mut self, - block_import: BlockImportParams, - ) -> Result, String> { - if self - .client - .runtime_api() - .has_api::>(*block_import.header.parent_hash()) - .unwrap_or(false) - { - self.aura_verifier.get_mut().verify(block_import).await - } else { - self.relay_chain_verifier.verify(block_import).await - } - } -} - -/// Build the import queue for Aura-based runtimes. -pub fn aura_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry_handle: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + sp_consensus_aura::AuraApi::Pair as Pair>::Public>, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - let client2 = client.clone(); - - let aura_verifier = move || { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client2).unwrap(); - - Box::new(cumulus_client_consensus_aura::build_verifier::< - ::Pair, - _, - _, - _, - >(cumulus_client_consensus_aura::BuildVerifierParams { - client: client2.clone(), - create_inherent_data_providers: move |_, _| async move { - 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, - })) as Box<_> - }; - - let relay_chain_verifier = - Box::new(RelayChainVerifier::new(client.clone(), |_, _| async { Ok(()) })) as Box<_>; - - let verifier = Verifier { - client, - relay_chain_verifier, - aura_verifier: BuildOnAccess::Uninitialized(Some(Box::new(aura_verifier))), - _phantom: PhantomData, - }; - - let registry = config.prometheus_registry(); - let spawner = task_manager.spawn_essential_handle(); - - Ok(BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)) -} - -/// Start an aura powered parachain node. Collectives uses this. -pub async fn start_generic_aura_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - _backend| { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; - - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - let proposer = Proposer::new(proposer_factory); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let params = BasicAuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client, - relay_client: relay_chain_interface, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - // Very limited proposal time. - authoring_duration: Duration::from_millis(500), - collation_request_receiver: None, - }; - - let fut = - basic_aura::run::::Pair, _, _, _, _, _, _, _>(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - - Ok(()) - }, - hwbench, - ) - .await -} - -/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub -/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and -/// needs to sync and upgrade before it can run `AuraApi` functions. -pub async fn start_asset_hub_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - _backend| { - let relay_chain_interface2 = relay_chain_interface.clone(); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let spawner = task_manager.spawn_handle(); - - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - spawner, - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - - let collation_future = Box::pin(async move { - // Start collating with the `shell` runtime while waiting for an upgrade to an Aura - // compatible runtime. - let mut request_stream = cumulus_client_collator::relay_chain_driven::init( - collator_key.clone(), - para_id, - overseer_handle.clone(), - ) - .await; - while let Some(request) = request_stream.next().await { - let pvd = request.persisted_validation_data().clone(); - let last_head_hash = - match ::Header::decode(&mut &pvd.parent_head.0[..]) { - Ok(header) => header.hash(), - Err(e) => { - log::error!("Could not decode the head data: {e}"); - request.complete(None); - continue - }, - }; - - // Check if we have upgraded to an Aura compatible runtime and transition if - // necessary. - if client - .runtime_api() - .has_api::>(last_head_hash) - .unwrap_or(false) - { - // Respond to this request before transitioning to Aura. - request.complete(None); - break - } - } - - // Move to Aura consensus. - let slot_duration = match cumulus_client_consensus_aura::slot_duration(&*client) { - Ok(d) => d, - Err(e) => { - log::error!("Could not get Aura slot duration: {e}"); - return - }, - }; - - let proposer = Proposer::new(proposer_factory); - - let params = BasicAuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client, - relay_client: relay_chain_interface2, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - // Very limited proposal time. - authoring_duration: Duration::from_millis(500), - collation_request_receiver: Some(request_stream), - }; - - basic_aura::run::::Pair, _, _, _, _, _, _, _>(params) - .await - }); + .runtime_api() + .has_api::>(parent.hash()) + .unwrap_or(false) + { + self.aura_consensus + .lock() + .await + .get_mut() + .produce_candidate(parent, relay_parent, validation_data) + .await + } else { + self.relay_chain_consensus + .lock() + .await + .produce_candidate(parent, relay_parent, validation_data) + .await + } + } +} - let spawner = task_manager.spawn_essential_handle(); - spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); +struct Verifier { + client: Arc, + aura_verifier: BuildOnAccess>>, + relay_chain_verifier: Box>, + _phantom: PhantomData, +} - Ok(()) - }, - hwbench, - ) - .await +#[async_trait::async_trait] +impl VerifierT for Verifier +where + Client: sp_api::ProvideRuntimeApi + Send + Sync, + Client::Api: AuraApi, + AuraId: Send + Sync + Codec, +{ + async fn verify( + &mut self, + block_import: BlockImportParams, + ) -> Result, String> { + if self + .client + .runtime_api() + .has_api::>(*block_import.header.parent_hash()) + .unwrap_or(false) + { + self.aura_verifier.get_mut().verify(block_import).await + } else { + self.relay_chain_verifier.verify(block_import).await + } + } } -/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub -/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and -/// needs to sync and upgrade before it can run `AuraApi` functions. -/// -/// Uses the lookahead collator to support async backing. -#[sc_tracing::logging::prefix_logs_with("Parachain")] -pub async fn start_asset_hub_lookahead_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> +/// Build the import queue for parachain runtimes that started with relay chain consensus and +/// switched to aura. +pub fn build_relay_to_aura_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry_handle: Option, + task_manager: &TaskManager, +) -> Result, sc_service::Error> where RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue @@ -1552,171 +636,74 @@ where + sp_api::ApiExt + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi - + cumulus_primitives_aura::AuraUnincludedSegmentApi, + + sp_consensus_aura::AuraApi::Pair as Pair>::Public>, <::Pair as Pair>::Signature: TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, { - start_node_impl::( - parachain_config, - polkadot_config, - collator_options, - CollatorSybilResistance::Resistant, // Aura - para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, - |client, - block_import, - prometheus_registry, - telemetry, - task_manager, - relay_chain_interface, - transaction_pool, - sync_oracle, - keystore, - relay_chain_slot_duration, - para_id, - collator_key, - overseer_handle, - announce_block, - backend| { - let relay_chain_interface2 = relay_chain_interface.clone(); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let spawner = task_manager.spawn_handle(); - - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - spawner, - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - - let collation_future = Box::pin(async move { - // Start collating with the `shell` runtime while waiting for an upgrade to an Aura - // compatible runtime. - let mut request_stream = cumulus_client_collator::relay_chain_driven::init( - collator_key.clone(), - para_id, - overseer_handle.clone(), - ) - .await; - while let Some(request) = request_stream.next().await { - let pvd = request.persisted_validation_data().clone(); - let last_head_hash = - match ::Header::decode(&mut &pvd.parent_head.0[..]) { - Ok(header) => header.hash(), - Err(e) => { - log::error!("Could not decode the head data: {e}"); - request.complete(None); - continue - }, - }; + let verifier_client = client.clone(); - // Check if we have upgraded to an Aura compatible runtime and transition if - // necessary. - if client - .runtime_api() - .has_api::>(last_head_hash) - .unwrap_or(false) - { - // Respond to this request before transitioning to Aura. - request.complete(None); - break - } + let aura_verifier = move || { + Box::new(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, + })) as Box<_> + }; - // Move to Aura consensus. - let slot_duration = match cumulus_client_consensus_aura::slot_duration(&*client) { - Ok(d) => d, - Err(e) => { - log::error!("Could not get Aura slot duration: {e}"); - return - }, - }; - - let proposer = Proposer::new(proposer_factory); - - let params = AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend, - relay_client: relay_chain_interface2, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: true, /* we need to always re-initialize for asset-hub moving - * to aura */ - }; + let relay_chain_verifier = + Box::new(RelayChainVerifier::new(client.clone(), |_, _| async { Ok(()) })) as Box<_>; - aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params) - .await - }); + let verifier = Verifier { + client, + relay_chain_verifier, + aura_verifier: BuildOnAccess::Uninitialized(Some(Box::new(aura_verifier))), + _phantom: PhantomData, + }; - let spawner = task_manager.spawn_essential_handle(); - spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); + let registry = config.prometheus_registry(); + let spawner = task_manager.spawn_essential_handle(); - Ok(()) - }, - hwbench, - ) - .await + Ok(BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)) } -/// Start an aura powered parachain node which uses the lookahead collator to support async backing. -/// This node is basic in the sense that its runtime api doesn't include common contents such as -/// transaction payment. Used for aura glutton. -pub async fn start_basic_lookahead_node( +/// Start an aura powered parachain node. Some system chains use this. +pub async fn start_generic_aura_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, para_id: ParaId, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> -where - RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue - + sp_api::Metadata - + sp_session::SessionKeys - + sp_api::ApiExt - + sp_offchain::OffchainWorkerApi - + sp_block_builder::BlockBuilder - + cumulus_primitives_core::CollectCollationInfo - + sp_consensus_aura::AuraApi::Pair as Pair>::Public> - + frame_rpc_system::AccountNonceApi - + cumulus_primitives_aura::AuraUnincludedSegmentApi, - <::Pair as Pair>::Signature: - TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, -{ - start_basic_lookahead_node_impl::( +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( parachain_config, polkadot_config, collator_options, CollatorSybilResistance::Resistant, // Aura para_id, - |_| Ok(RpcModule::new(())), - aura_build_import_queue::<_, AuraId>, + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, |client, block_import, prometheus_registry, @@ -1731,7 +718,7 @@ where collator_key, overseer_handle, announce_block, - backend| { + _backend| { let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( @@ -1750,15 +737,11 @@ where client.clone(), ); - let params = AuraParams { + let params = BasicAuraParams { create_inherent_data_providers: move |_, ()| async move { Ok(()) }, block_import, - para_client: client.clone(), - para_backend: backend.clone(), + para_client: client, relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, sync_oracle, keystore, collator_key, @@ -1768,12 +751,13 @@ where relay_chain_slot_duration, proposer, collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: false, + // Very limited proposal time. + authoring_duration: Duration::from_millis(500), + collation_request_receiver: None, }; let fut = - aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params); + basic_aura::run::::Pair, _, _, _, _, _, _, _>(params); task_manager.spawn_essential_handle().spawn("aura", None, fut); Ok(()) @@ -1783,16 +767,38 @@ where .await } -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_contracts_rococo_node_impl( +/// Uses the lookahead collator to support async backing. +/// +/// Start an aura powered parachain node. Some system chains use this. +pub async fn start_generic_aura_lookahead_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + +/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub +/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and +/// needs to sync and upgrade before it can run `AuraApi` functions. +pub async fn start_asset_hub_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, - sybil_resistance_level: CollatorSybilResistance, para_id: ParaId, - _rpc_ext_builder: RB, - build_import_queue: BIQ, - start_consensus: SC, hwbench: Option, ) -> sc_service::error::Result<(TaskManager, Arc>)> where @@ -1804,227 +810,169 @@ where + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder + cumulus_primitives_core::CollectCollationInfo + + sp_consensus_aura::AuraApi::Pair as Pair>::Public> + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + frame_rpc_system::AccountNonceApi, - RB: Fn(Arc>) -> Result, sc_service::Error>, - BIQ: FnOnce( - Arc>, - ParachainBlockImport, - &Configuration, - Option, - &TaskManager, - ) -> Result, sc_service::Error>, - SC: FnOnce( - Arc>, - ParachainBlockImport, - Option<&Registry>, - Option, - &TaskManager, - Arc, - Arc>>, - Arc>, - KeystorePtr, - Duration, - ParaId, - CollatorPair, - OverseerHandle, - Arc>) + Send + Sync>, - Arc, - ) -> Result<(), sc_service::Error>, + <::Pair as Pair>::Signature: + TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, { - let parachain_config = prepare_node_config(parachain_config); - - let params = new_partial::(¶chain_config, build_import_queue)?; - 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( + start_node_impl::( + parachain_config, 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::new(¶chain_config.network); - - 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, - }) - .await?; - - let rpc_builder = { - 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, - }; - - crate::rpc::create_contracts_rococo(deps).map_err(Into::into) - }) - }; - - 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); - } + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, + |client, + block_import, + prometheus_registry, + telemetry, + task_manager, + relay_chain_interface, + transaction_pool, + sync_oracle, + keystore, + relay_chain_slot_duration, + para_id, + collator_key, + overseer_handle, + announce_block, + _backend| { + let relay_chain_interface2 = relay_chain_interface.clone(); - 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 collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), ); - } - } - - 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: sync_service.clone(), - })?; - if validator { - 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, - sync_service.clone(), - 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(), - )?; - } + let spawner = task_manager.spawn_handle(); - start_network.start_network(); + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + spawner, + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); - Ok((task_manager, client)) -} + let collation_future = Box::pin(async move { + // Start collating with the `shell` runtime while waiting for an upgrade to an Aura + // compatible runtime. + let mut request_stream = cumulus_client_collator::relay_chain_driven::init( + collator_key.clone(), + para_id, + overseer_handle.clone(), + ) + .await; + while let Some(request) = request_stream.next().await { + let pvd = request.persisted_validation_data().clone(); + let last_head_hash = + match ::Header::decode(&mut &pvd.parent_head.0[..]) { + Ok(header) => header.hash(), + Err(e) => { + log::error!("Could not decode the head data: {e}"); + request.complete(None); + continue + }, + }; -#[allow(clippy::type_complexity)] -pub fn contracts_rococo_build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry: Option, - task_manager: &TaskManager, -) -> Result, sc_service::Error> { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + // Check if we have upgraded to an Aura compatible runtime and transition if + // necessary. + if client + .runtime_api() + .has_api::>(last_head_hash) + .unwrap_or(false) + { + // Respond to this request before transitioning to Aura. + request.complete(None); + break + } + } - cumulus_client_consensus_aura::import_queue::< - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - >(cumulus_client_consensus_aura::ImportQueueParams { - block_import, - client, - create_inherent_data_providers: move |_, _| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + // Move to Aura consensus. + let slot_duration = match cumulus_client_consensus_aura::slot_duration(&*client) { + Ok(d) => d, + Err(e) => { + log::error!("Could not get Aura slot duration: {e}"); + return + }, + }; - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, + let proposer = Proposer::new(proposer_factory); + + let params = BasicAuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client, + relay_client: relay_chain_interface2, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, slot_duration, - ); + relay_chain_slot_duration, + proposer, + collator_service, + // Very limited proposal time. + authoring_duration: Duration::from_millis(500), + collation_request_receiver: Some(request_stream), + }; - Ok((slot, timestamp)) + basic_aura::run::::Pair, _, _, _, _, _, _, _>(params) + .await + }); + + let spawner = task_manager.spawn_essential_handle(); + spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); + + Ok(()) }, - registry: config.prometheus_registry(), - spawner: &task_manager.spawn_essential_handle(), - telemetry, - }) - .map_err(Into::into) + hwbench, + ) + .await } -/// Start a parachain node. -pub async fn start_contracts_rococo_node( +/// Start a shell node which should later transition into an Aura powered parachain node. Asset Hub +/// uses this because at genesis, Asset Hub was on the `shell` runtime which didn't have Aura and +/// needs to sync and upgrade before it can run `AuraApi` functions. +/// +/// Uses the lookahead collator to support async backing. +#[sc_tracing::logging::prefix_logs_with("Parachain")] +pub async fn start_asset_hub_lookahead_node( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, para_id: ParaId, hwbench: Option, -) -> sc_service::error::Result<(TaskManager, Arc>)> { - start_contracts_rococo_node_impl::( +) -> sc_service::error::Result<(TaskManager, Arc>)> +where + RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, + RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::Metadata + + sp_session::SessionKeys + + sp_api::ApiExt + + sp_offchain::OffchainWorkerApi + + sp_block_builder::BlockBuilder + + cumulus_primitives_core::CollectCollationInfo + + sp_consensus_aura::AuraApi::Pair as Pair>::Public> + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + frame_rpc_system::AccountNonceApi + + cumulus_primitives_aura::AuraUnincludedSegmentApi, + <::Pair as Pair>::Signature: + TryFrom> + std::hash::Hash + sp_runtime::traits::Member + Codec, +{ + start_node_impl::( parachain_config, polkadot_config, collator_options, CollatorSybilResistance::Resistant, // Aura para_id, - |_| Ok(RpcModule::new(())), - contracts_rococo_build_import_queue, + build_parachain_rpc_extensions::, + build_relay_to_aura_import_queue::<_, AuraId>, |client, block_import, prometheus_registry, @@ -2039,56 +987,91 @@ pub async fn start_contracts_rococo_node( collator_key, overseer_handle, announce_block, - _backend| { - let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + backend| { + let relay_chain_interface2 = relay_chain_interface.clone(); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), + ); + + let spawner = task_manager.spawn_handle(); let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), + spawner, client.clone(), transaction_pool, prometheus_registry, telemetry.clone(), ); - let proposer = Proposer::new(proposer_factory); - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); + let collation_future = Box::pin(async move { + // Start collating with the `shell` runtime while waiting for an upgrade to an Aura + // compatible runtime. + let mut request_stream = cumulus_client_collator::relay_chain_driven::init( + collator_key.clone(), + para_id, + overseer_handle.clone(), + ) + .await; + while let Some(request) = request_stream.next().await { + let pvd = request.persisted_validation_data().clone(); + let last_head_hash = + match ::Header::decode(&mut &pvd.parent_head.0[..]) { + Ok(header) => header.hash(), + Err(e) => { + log::error!("Could not decode the head data: {e}"); + request.complete(None); + continue + }, + }; - let params = BasicAuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client, - relay_client: relay_chain_interface, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - // Very limited proposal time. - authoring_duration: Duration::from_millis(500), - collation_request_receiver: None, - }; + // Check if we have upgraded to an Aura compatible runtime and transition if + // necessary. + if client + .runtime_api() + .has_api::>(last_head_hash) + .unwrap_or(false) + { + // Respond to this request before transitioning to Aura. + request.complete(None); + break + } + } - let fut = basic_aura::run::< - Block, - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - _, - _, - >(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); + // Move to Aura consensus. + let proposer = Proposer::new(proposer_factory); + + let params = AuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend, + relay_client: relay_chain_interface2, + code_hash_provider: move |block_hash| { + client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + }, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, + relay_chain_slot_duration, + proposer, + collator_service, + authoring_duration: Duration::from_millis(1500), + reinitialize: true, /* we need to always re-initialize for asset-hub moving + * to aura */ + }; + + aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params) + .await + }); + + let spawner = task_manager.spawn_essential_handle(); + spawner.spawn_essential("cumulus-asset-hub-collator", None, collation_future); Ok(()) }, @@ -2097,6 +1080,184 @@ pub async fn start_contracts_rococo_node( .await } +/// Start relay-chain consensus that is free for all. Everyone can submit a block, the relay-chain +/// decides what is backed and included. +fn start_relay_chain_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + _sync_oracle: Arc>, + _keystore: KeystorePtr, + _relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + _backend: Arc, +) -> Result<(), sc_service::Error> { + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry, + ); + + let free_for_all = cumulus_client_consensus_relay_chain::build_relay_chain_consensus( + cumulus_client_consensus_relay_chain::BuildRelayChainConsensusParams { + para_id, + proposer_factory, + block_import, + relay_chain_interface: relay_chain_interface.clone(), + create_inherent_data_providers: move |_, (relay_parent, validation_data)| { + 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; + let parachain_inherent = parachain_inherent.ok_or_else(|| { + Box::::from( + "Failed to create parachain inherent", + ) + })?; + Ok(parachain_inherent) + } + }, + }, + ); + + let spawner = task_manager.spawn_handle(); + + // Required for free-for-all consensus + #[allow(deprecated)] + old_consensus::start_collator_sync(old_consensus::StartCollatorParams { + para_id, + block_status: client.clone(), + announce_block, + overseer_handle, + spawner, + key: collator_key, + parachain_consensus: free_for_all, + runtime_api: client.clone(), + }); + + Ok(()) +} + +/// Start consensus using the lookahead aura collator. +fn start_lookahead_aura_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + sync_oracle: Arc>, + keystore: KeystorePtr, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + backend: Arc, +) -> Result<(), sc_service::Error> { + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), + ); + + let params = AuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend, + relay_client: relay_chain_interface, + code_hash_provider: move |block_hash| { + client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + }, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, + relay_chain_slot_duration, + proposer: Proposer::new(proposer_factory), + collator_service, + authoring_duration: Duration::from_millis(1500), + reinitialize: false, + }; + + let fut = aura::run::::Pair, _, _, _, _, _, _, _, _, _>(params); + task_manager.spawn_essential_handle().spawn("aura", None, fut); + + Ok(()) +} + +/// Start an aura powered parachain node which uses the lookahead collator to support async backing. +/// This node is basic in the sense that its runtime api doesn't include common contents such as +/// transaction payment. Used for aura glutton. +pub async fn start_basic_lookahead_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + |_, _, _, _| Ok(RpcModule::new(())), + build_relay_to_aura_import_queue::<_, AuraId>, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + +/// Start a parachain node for Rococo Contracts. +pub async fn start_contracts_rococo_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + CollatorSybilResistance::Resistant, // Aura + para_id, + build_contracts_rpc_extensions, + build_aura_import_queue, + start_lookahead_aura_consensus, + hwbench, + ) + .await +} + /// 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 diff --git a/cumulus/primitives/aura/Cargo.toml b/cumulus/primitives/aura/Cargo.toml index 6d917eea270ec2fac273e349ad4fa9521ecfe0fc..21c06ef22d9a13bf8361156ea5e1af1216aa3e28 100644 --- a/cumulus/primitives/aura/Cargo.toml +++ b/cumulus/primitives/aura/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-primitives-aura" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/primitives/core/Cargo.toml b/cumulus/primitives/core/Cargo.toml index 98c3e8ab5672e87f9d63407c058290739e065472..32c5054f359c4102a0c0a5ffdcb128bdc1ffd725 100644 --- a/cumulus/primitives/core/Cargo.toml +++ b/cumulus/primitives/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-primitives-core" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/primitives/parachain-inherent/Cargo.toml b/cumulus/primitives/parachain-inherent/Cargo.toml index 42a425ea176bcbeb58906daa6ca080712b8412ad..f434305a0ce013ae048087bf9a75413efacc1fff 100644 --- a/cumulus/primitives/parachain-inherent/Cargo.toml +++ b/cumulus/primitives/parachain-inherent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-primitives-parachain-inherent" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Inherent that needs to be present in every parachain block. Contains messages and a relay chain storage-proof." diff --git a/cumulus/primitives/proof-size-hostfunction/Cargo.toml b/cumulus/primitives/proof-size-hostfunction/Cargo.toml index 06797f86863265797f59d3f44504168f1549ecb5..dd584ce86b2e3172563848f028730709e1b1600d 100644 --- a/cumulus/primitives/proof-size-hostfunction/Cargo.toml +++ b/cumulus/primitives/proof-size-hostfunction/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-primitives-proof-size-hostfunction" -version = "0.1.0" +version = "0.2.0" authors.workspace = true edition.workspace = true description = "Hostfunction exposing storage proof size to the runtime." diff --git a/cumulus/primitives/proof-size-hostfunction/src/lib.rs b/cumulus/primitives/proof-size-hostfunction/src/lib.rs index 6da6235e585a343887f87931e375b21bec48c20d..8ebc58ea450d4aea023f2a3af218bbd9eb3546e9 100644 --- a/cumulus/primitives/proof-size-hostfunction/src/lib.rs +++ b/cumulus/primitives/proof-size-hostfunction/src/lib.rs @@ -18,6 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "std")] use sp_externalities::ExternalitiesExt; use sp_runtime_interface::runtime_interface; @@ -35,7 +36,8 @@ pub const PROOF_RECORDING_DISABLED: u64 = u64::MAX; pub trait StorageProofSize { /// Returns the current storage proof size. fn storage_proof_size(&mut self) -> u64 { - self.extension::().map_or(u64::MAX, |e| e.storage_proof_size()) + self.extension::() + .map_or(PROOF_RECORDING_DISABLED, |e| e.storage_proof_size()) } } diff --git a/cumulus/primitives/storage-weight-reclaim/Cargo.toml b/cumulus/primitives/storage-weight-reclaim/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..9e8f60783d4dd1e4779e93af3f932b4034a1937a --- /dev/null +++ b/cumulus/primitives/storage-weight-reclaim/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "cumulus-primitives-storage-weight-reclaim" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +description = "Utilities to reclaim storage weight." +license = "Apache-2.0" + +[lints] +workspace = true + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { workspace = true } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } + +frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../substrate/frame/system", default-features = false } + +sp-core = { path = "../../../substrate/primitives/core", default-features = false } +sp-io = { path = "../../../substrate/primitives/io", default-features = false } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } + +cumulus-primitives-core = { path = "../../primitives/core", default-features = false } +cumulus-primitives-proof-size-hostfunction = { path = "../../primitives/proof-size-hostfunction", default-features = false } +docify = "0.2.7" + +[dev-dependencies] +sp-trie = { path = "../../../substrate/primitives/trie", default-features = false } +cumulus-test-runtime = { path = "../../test/runtime" } + +[features] +default = ["std"] +runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "cumulus-primitives-core/std", + "cumulus-primitives-proof-size-hostfunction/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] diff --git a/cumulus/primitives/storage-weight-reclaim/src/benchmarking.rs b/cumulus/primitives/storage-weight-reclaim/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..2980d08271a2c8a50c338c84162fe4fe38a55c2e --- /dev/null +++ b/cumulus/primitives/storage-weight-reclaim/src/benchmarking.rs @@ -0,0 +1,70 @@ +// 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. + +//! Benchmarking setup for cumulus-primitives-storage-weight-reclaim + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use frame_benchmarking::{account, v2::*, BenchmarkError}; +use frame_support::pallet_prelude::DispatchClass; +use frame_system::{BlockWeight, RawOrigin}; +use sp_runtime::traits::{DispatchTransaction, Get}; +use sp_std::{ + marker::{Send, Sync}, + prelude::*, +}; + +/// Pallet we're benchmarking here. +pub struct Pallet(frame_system::Pallet); + +#[benchmarks(where + T: Send + Sync, + T::RuntimeCall: Dispatchable +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn storage_weight_reclaim() -> Result<(), BenchmarkError> { + let caller = account("caller", 0, 0); + BlockWeight::::mutate(|current_weight| { + current_weight.set(Weight::from_parts(0, 1000), DispatchClass::Normal); + }); + let base_extrinsic = ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(0, 200)), + pays_fee: Default::default(), + }; + let len = 0_usize; + let ext = StorageWeightReclaim::::new(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(post_info)) + .unwrap() + .unwrap(); + } + + assert_eq!(BlockWeight::::get().total().proof_size(), 700 + base_extrinsic.proof_size()); + + Ok(()) + } +} diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..4bb40176b78c012ccb37c1bc0f068ff35c63731c --- /dev/null +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -0,0 +1,207 @@ +// 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. + +//! Mechanism to reclaim PoV proof size weight after an extrinsic has been applied. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use cumulus_primitives_core::Weight; +use cumulus_primitives_proof_size_hostfunction::{ + storage_proof_size::storage_proof_size, PROOF_RECORDING_DISABLED, +}; +use frame_support::{ + dispatch::{DispatchInfo, PostDispatchInfo}, + weights::WeightMeter, +}; +use frame_system::Config; +use scale_info::TypeInfo; +use sp_runtime::{ + impl_tx_ext_default, + traits::{ + DispatchInfoOf, Dispatchable, PostDispatchInfoOf, TransactionExtension, + TransactionExtensionBase, + }, + transaction_validity::TransactionValidityError, + DispatchResult, +}; +use sp_std::marker::PhantomData; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +const LOG_TARGET: &'static str = "runtime::storage_reclaim"; + +/// `StorageWeightReclaimer` is a mechanism for manually reclaiming storage weight. +/// +/// It internally keeps track of the proof size and storage weight at initialization time. At +/// reclaim it computes the real consumed storage weight and refunds excess weight. +/// +/// # Example +#[doc = docify::embed!("src/tests.rs", simple_reclaimer_example)] +pub struct StorageWeightReclaimer { + previous_remaining_proof_size: u64, + previous_reported_proof_size: Option, +} + +impl StorageWeightReclaimer { + /// Creates a new `StorageWeightReclaimer` instance and initializes it with the storage + /// size provided by `weight_meter` and reported proof size from the node. + #[must_use = "Must call `reclaim_with_meter` to reclaim the weight"] + pub fn new(weight_meter: &WeightMeter) -> StorageWeightReclaimer { + let previous_remaining_proof_size = weight_meter.remaining().proof_size(); + let previous_reported_proof_size = get_proof_size(); + Self { previous_remaining_proof_size, previous_reported_proof_size } + } + + /// Check the consumed storage weight and calculate the consumed excess weight. + fn reclaim(&mut self, remaining_weight: Weight) -> Option { + let current_remaining_weight = remaining_weight.proof_size(); + let current_storage_proof_size = get_proof_size()?; + let previous_storage_proof_size = self.previous_reported_proof_size?; + let used_weight = + self.previous_remaining_proof_size.saturating_sub(current_remaining_weight); + let reported_used_size = + current_storage_proof_size.saturating_sub(previous_storage_proof_size); + let reclaimable = used_weight.saturating_sub(reported_used_size); + log::trace!( + target: LOG_TARGET, + "Found reclaimable storage weight. benchmarked: {used_weight}, consumed: {reported_used_size}" + ); + + self.previous_remaining_proof_size = current_remaining_weight.saturating_add(reclaimable); + self.previous_reported_proof_size = Some(current_storage_proof_size); + Some(Weight::from_parts(0, reclaimable)) + } + + /// Check the consumed storage weight and add the reclaimed + /// weight budget back to `weight_meter`. + pub fn reclaim_with_meter(&mut self, weight_meter: &mut WeightMeter) -> Option { + let reclaimed = self.reclaim(weight_meter.remaining())?; + weight_meter.reclaim_proof_size(reclaimed.proof_size()); + Some(reclaimed) + } +} + +/// Returns the current storage proof size from the host side. +/// +/// Returns `None` if proof recording is disabled on the host. +pub fn get_proof_size() -> Option { + let proof_size = storage_proof_size(); + (proof_size != PROOF_RECORDING_DISABLED).then_some(proof_size) +} + +/// Storage weight reclaim mechanism. +/// +/// This extension checks the size of the node-side storage proof +/// before and after executing a given extrinsic. The difference between +/// benchmarked and spent weight can be reclaimed. +#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct StorageWeightReclaim(PhantomData); + +impl StorageWeightReclaim { + /// Create a new `StorageWeightReclaim` instance. + pub fn new() -> Self { + Self(Default::default()) + } +} + +impl core::fmt::Debug for StorageWeightReclaim { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + let _ = write!(f, "StorageWeightReclaim"); + Ok(()) + } +} + +impl TransactionExtensionBase for StorageWeightReclaim { + const IDENTIFIER: &'static str = "StorageWeightReclaim"; + type Implicit = (); +} + +impl TransactionExtension + for StorageWeightReclaim +where + T::RuntimeCall: Dispatchable, +{ + type Val = (); + type Pre = Option; + + fn prepare( + self, + _val: Self::Val, + _origin: &T::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _context: &Context, + ) -> Result { + Ok(get_proof_size()) + } + + fn post_dispatch( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + _context: &Context, + ) -> Result<(), TransactionValidityError> { + let Some(pre_dispatch_proof_size) = pre else { + return Ok(()); + }; + + let Some(post_dispatch_proof_size) = get_proof_size() else { + log::debug!( + target: LOG_TARGET, + "Proof recording enabled during pre-dispatch, now disabled. This should not happen." + ); + 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); + + // This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate + // that in. + frame_system::BlockWeight::::mutate(|current| { + 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}" + ); + 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}" + ); + current.reduce(Weight::from_parts(0, storage_size_diff), info.class) + } + }); + Ok(()) + } + + impl_tx_ext_default!(T::RuntimeCall; Context; validate); +} diff --git a/cumulus/primitives/storage-weight-reclaim/src/tests.rs b/cumulus/primitives/storage-weight-reclaim/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..631827cf442622ab2e4f18ee05ac3cac440e8077 --- /dev/null +++ b/cumulus/primitives/storage-weight-reclaim/src/tests.rs @@ -0,0 +1,484 @@ +// 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::{ + assert_ok, + dispatch::DispatchClass, + weights::{Weight, WeightMeter}, +}; +use frame_system::{BlockWeight, CheckWeight}; +use sp_runtime::{traits::DispatchTransaction, AccountId32, BuildStorage}; +use sp_std::marker::PhantomData; +use sp_trie::proof_size_extension::ProofSizeExt; + +type Test = cumulus_test_runtime::Runtime; +const CALL: &::RuntimeCall = + &cumulus_test_runtime::RuntimeCall::System(frame_system::Call::set_heap_pages { pages: 0u64 }); +const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +const LEN: usize = 0; + +fn new_test_ext() -> sp_io::TestExternalities { + let ext: sp_io::TestExternalities = cumulus_test_runtime::RuntimeGenesisConfig::default() + .build_storage() + .unwrap() + .into(); + ext +} + +struct TestRecorder { + return_values: Box<[usize]>, + counter: std::sync::atomic::AtomicUsize, +} + +impl TestRecorder { + fn new(values: &[usize]) -> Self { + TestRecorder { return_values: values.into(), counter: Default::default() } + } +} + +impl sp_trie::ProofSizeProvider for TestRecorder { + fn estimate_encoded_size(&self) -> usize { + let counter = self.counter.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + self.return_values[counter] + } +} + +fn setup_test_externalities(proof_values: &[usize]) -> sp_io::TestExternalities { + let mut test_ext = new_test_ext(); + let test_recorder = TestRecorder::new(proof_values); + test_ext.register_extension(ProofSizeExt::new(test_recorder)); + test_ext +} + +fn set_current_storage_weight(new_weight: u64) { + BlockWeight::::mutate(|current_weight| { + current_weight.set(Weight::from_parts(0, new_weight), DispatchClass::Normal); + }); +} + +#[test] +fn basic_refund() { + // 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, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + // We expect a refund of 400 + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 600); + }) +} + +#[test] +fn does_nothing_without_extension() { + let mut test_ext = new_test_ext(); + + // Proof size extension not registered + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, None); + + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1000); + }) +} + +#[test] +fn negative_refund_is_added_to_weight() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 100 + let info = DispatchInfo { weight: Weight::from_parts(0, 100), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // We expect no refund + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1100); + }) +} + +#[test] +fn test_zero_proof_size() { + let mut test_ext = setup_test_externalities(&[0, 0]); + + test_ext.execute_with(|| { + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 0); + }); +} + +#[test] +fn test_larger_pre_dispatch_proof_size() { + let mut test_ext = setup_test_externalities(&[300, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1300); + + let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(300)); + + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 800); + }); +} + +#[test] +fn test_incorporates_check_weight_unspent_weight() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 300 + let info = DispatchInfo { weight: Weight::from_parts(100, 300), ..Default::default() }; + + // Actual weight is 50 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 250)), + pays_fee: Default::default(), + }; + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 900); + }) +} + +#[test] +fn test_incorporates_check_weight_unspent_weight_on_negative() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 50 + let info = DispatchInfo { weight: Weight::from_parts(100, 50), ..Default::default() }; + + // Actual weight is 25 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 25)), + pays_fee: Default::default(), + }; + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1150); + }) +} + +#[test] +fn test_incorporates_check_weight_unspent_weight_reverse_order() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 300 + let info = DispatchInfo { weight: Weight::from_parts(100, 300), ..Default::default() }; + + // Actual weight is 50 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 250)), + pays_fee: Default::default(), + }; + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + // `CheckWeight` gets called after `StorageWeightReclaim` this time. + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + + assert_eq!(BlockWeight::::get().total().proof_size(), 900); + }) +} + +#[test] +fn test_incorporates_check_weight_unspent_weight_on_negative_reverse_order() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 50 + let info = DispatchInfo { weight: Weight::from_parts(100, 50), ..Default::default() }; + + // Actual weight is 25 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 25)), + pays_fee: Default::default(), + }; + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + assert_ok!(StorageWeightReclaim::::post_dispatch( + pre, + &info, + &post_info, + LEN, + &Ok(()), + &() + )); + // `CheckWeight` gets called after `StorageWeightReclaim` this time. + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch((), &info, &post_info, 0, &Ok(()), &())); + + assert_eq!(BlockWeight::::get().total().proof_size(), 1150); + }) +} + +#[test] +fn storage_size_reported_correctly() { + let mut test_ext = setup_test_externalities(&[1000]); + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), Some(1000)); + }); + + let mut test_ext = new_test_ext(); + + let test_recorder = TestRecorder::new(&[0]); + + test_ext.register_extension(ProofSizeExt::new(test_recorder)); + + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), Some(0)); + }); +} + +#[test] +fn storage_size_disabled_reported_correctly() { + let mut test_ext = setup_test_externalities(&[PROOF_RECORDING_DISABLED as usize]); + + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), None); + }); +} + +#[test] +fn test_reclaim_helper() { + let mut test_ext = setup_test_externalities(&[1000, 1300, 1800]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 2000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + remaining_weight_meter.consume(Weight::from_parts(0, 500)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 200))); + + remaining_weight_meter.consume(Weight::from_parts(0, 800)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + assert_eq!(reclaimed, Some(Weight::from_parts(0, 300))); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1200)); + }); +} + +#[test] +fn test_reclaim_helper_does_not_reclaim_negative() { + // Benchmarked weight does not change at all + let mut test_ext = setup_test_externalities(&[1000, 1300]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1000)); + }); + + // Benchmarked weight increases less than storage proof consumes + let mut test_ext = setup_test_externalities(&[1000, 1300]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + remaining_weight_meter.consume(Weight::from_parts(0, 0)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); + }); +} + +/// Just here for doc purposes +fn get_benched_weight() -> Weight { + Weight::from_parts(0, 5) +} + +/// Just here for doc purposes +fn do_work() {} + +#[docify::export_content(simple_reclaimer_example)] +fn reclaim_with_weight_meter() { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(10, 10)); + + let benched_weight = get_benched_weight(); + + // It is important to instantiate the `StorageWeightReclaimer` before we consume the weight + // for a piece of work from the weight meter. + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + + if remaining_weight_meter.try_consume(benched_weight).is_ok() { + // Perform some work that takes has `benched_weight` storage weight. + do_work(); + + // Reclaimer will detect that we only consumed 2 bytes, so 3 bytes are reclaimed. + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + // We reclaimed 3 bytes of storage size! + assert_eq!(reclaimed, Some(Weight::from_parts(0, 3))); + assert_eq!(BlockWeight::::get().total().proof_size(), 10); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(10, 8)); + } +} + +#[test] +fn test_reclaim_helper_works_with_meter() { + // The node will report 12 - 10 = 2 consumed storage size between the calls. + let mut test_ext = setup_test_externalities(&[10, 12]); + + test_ext.execute_with(|| { + // Initial storage size is 10. + set_current_storage_weight(10); + reclaim_with_weight_meter(); + }); +} diff --git a/cumulus/primitives/timestamp/Cargo.toml b/cumulus/primitives/timestamp/Cargo.toml index b07a907154dfab36a63f900852fc4d044ccee341..59f327b2642a292db56708f5770ebb35b1b82d1d 100644 --- a/cumulus/primitives/timestamp/Cargo.toml +++ b/cumulus/primitives/timestamp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-primitives-timestamp" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true description = "Provides timestamp related functionality for parachains." diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index 1558295b93650b54021bf2eff453c1e5a8ac4f39..1e2c300b9ba257d2c8fb998689ae45847099dd63 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-primitives-utility" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -log = { version = "0.4.20", default-features = false } +log = { workspace = true } # Substrate frame-support = { path = "../../../substrate/frame/support", default-features = false } @@ -26,7 +26,6 @@ polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", d xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } -pallet-xcm-benchmarks = { path = "../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false } # Cumulus cumulus-primitives-core = { path = "../core", default-features = false } @@ -39,7 +38,6 @@ std = [ "frame-support/std", "log/std", "pallet-asset-conversion/std", - "pallet-xcm-benchmarks/std", "polkadot-runtime-common/std", "polkadot-runtime-parachains/std", "sp-io/std", @@ -54,7 +52,6 @@ runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", - "pallet-xcm-benchmarks/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index bd13ee1119b8b1d5fb0d860d2b21bde3a8662431..abc391bdcb8ed6ac3682c761e6a0a8d2d0e7f14b 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -25,6 +25,7 @@ use frame_support::{ defensive, traits::{tokens::fungibles, Get, OnUnbalanced as OnUnbalancedT}, weights::{Weight, WeightToFee as WeightToFeeT}, + CloneNoBound, }; use pallet_asset_conversion::SwapCredit as SwapCreditT; use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery; @@ -106,6 +107,7 @@ struct AssetTraderRefunder { /// later refund purposes /// Important: Errors if the Trader is being called twice by 2 BuyExecution instructions /// Alternatively we could just return payment in the aforementioned case +#[derive(CloneNoBound)] pub struct TakeFirstAssetTrader< AccountId: Eq, FeeCharger: ChargeWeightInFungibles, @@ -758,7 +760,7 @@ mod test_trader { } } -/// Implementation of `pallet_xcm_benchmarks::EnsureDelivery` which helps to ensure delivery to the +/// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the /// parent relay chain. Deposits existential deposit for origin (if needed). /// Deposits estimated fee to the origin account (if needed). /// Allows to trigger additional logic for specific `ParaId` (e.g. open HRMP channel) (if neeeded). @@ -772,17 +774,22 @@ impl< XcmConfig: xcm_executor::Config, ExistentialDeposit: Get>, PriceForDelivery: PriceForMessageDelivery, - > pallet_xcm_benchmarks::EnsureDelivery + > xcm_builder::EnsureDelivery for ToParentDeliveryHelper { fn ensure_successful_delivery( origin_ref: &Location, - _dest: &Location, + dest: &Location, fee_reason: xcm_executor::traits::FeeReason, ) -> (Option, Option) { use xcm::latest::{MAX_INSTRUCTIONS_TO_DECODE, MAX_ITEMS_IN_ASSETS}; use xcm_executor::{traits::FeeManager, FeesMode}; + // check if the destination is relay/parent + if dest.ne(&Location::parent()) { + return (None, None); + } + let mut fees_mode = None; if !XcmConfig::FeeManager::is_waived(Some(origin_ref), fee_reason) { // if not waived, we need to set up accounts for paying and receiving fees diff --git a/cumulus/scripts/create_coretime_westend_spec.sh b/cumulus/scripts/create_coretime_westend_spec.sh index 90996f4a74f47f9783a29ff2ce358920be810641..516dc06ac6660c0c1f9d565d88a9ae7d132ade9e 100755 --- a/cumulus/scripts/create_coretime_westend_spec.sh +++ b/cumulus/scripts/create_coretime_westend_spec.sh @@ -39,13 +39,7 @@ cat chain-spec-plain.json | jq --rawfile code rt-hex.txt '.genesis.runtimeGenesi | jq '.chainType = "Live"' \ | jq '.bootNodes = [ "/dns/westend-coretime-collator-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP93Dzk8T7GWxyWw9jhLcz8Pksokk3R9vL2eEH337bNkT", - "/dns/westend-coretime-collator-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH", - "/dns/westend-coretime-collator-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWAys2hVpF7AN8hYGnu1T6XYFRGKeBFqD8q5LUcvWXRLg8", - "/dns/westend-coretime-collator-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWSGgmiRryoi7A3qAmeYWgmVeGQkk66PrhDjJ6ZPP555as", - "/dns/westend-coretime-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWP93Dzk8T7GWxyWw9jhLcz8Pksokk3R9vL2eEH337bNkT", - "/dns/westend-coretime-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH", - "/dns/westend-coretime-connect-2.polkadot.io/tcp/443/wss/p2p/12D3KooWAys2hVpF7AN8hYGnu1T6XYFRGKeBFqD8q5LUcvWXRLg8", - "/dns/westend-coretime-connect-3.polkadot.io/tcp/443/wss/p2p/12D3KooWSGgmiRryoi7A3qAmeYWgmVeGQkk66PrhDjJ6ZPP555as", + "/dns/westend-coretime-collator-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH" ]' \ | jq '.relay_chain = "westend"' \ | jq --argjson para_id $para_id '.para_id = $para_id' \ @@ -53,37 +47,21 @@ cat chain-spec-plain.json | jq --rawfile code rt-hex.txt '.genesis.runtimeGenesi | jq '.genesis.runtimeGenesis.patch.balances.balances = []' \ | jq '.genesis.runtimeGenesis.patch.collatorSelection.invulnerables = [ "5GKXTtB7RG3mLJ2kT4AkDXoxvKCFDVUdwyRmeMEbX3gBwcGi", - "5DknBCD1h49nc8eqnm6XtHz3bMQm5hfMuGYcLenRfCmpnBJG", - "5D52g9Mt9jQnZn6hwYhv649QYqGwhjygxkpb6rm3FYzYHEs3", - "5Egx2B41PYj8uvuhkNJeucA54h6Xmi7ZH9wqrZLwj3CuvQKA" + "5DknBCD1h49nc8eqnm6XtHz3bMQm5hfMuGYcLenRfCmpnBJG" ]' \ | jq '.genesis.runtimeGenesis.patch.session.keys = [ [ "5GKXTtB7RG3mLJ2kT4AkDXoxvKCFDVUdwyRmeMEbX3gBwcGi", "5GKXTtB7RG3mLJ2kT4AkDXoxvKCFDVUdwyRmeMEbX3gBwcGi", { - "aura": "0xbc3ea120d2991b75447b0b53cd8623970a0f6d98fa2701036c74d94e6b79252c" + "aura": "5GKXTtB7RG3mLJ2kT4AkDXoxvKCFDVUdwyRmeMEbX3gBwcGi" } ], [ "5DknBCD1h49nc8eqnm6XtHz3bMQm5hfMuGYcLenRfCmpnBJG", "5DknBCD1h49nc8eqnm6XtHz3bMQm5hfMuGYcLenRfCmpnBJG", { - "aura": "0x4acc970c28713ec93bf925352d3023418fdf89933227e1e2fdae8481103dfe28" - } - ], - [ - "5D52g9Mt9jQnZn6hwYhv649QYqGwhjygxkpb6rm3FYzYHEs3", - "5D52g9Mt9jQnZn6hwYhv649QYqGwhjygxkpb6rm3FYzYHEs3", - { - "aura": "0x2c7b95155708c10616b6f1a77a84f3d92c9a0272609ed24dbb7e6bdb81b53e76" - } - ], - [ - "5Egx2B41PYj8uvuhkNJeucA54h6Xmi7ZH9wqrZLwj3CuvQKA", - "5Egx2B41PYj8uvuhkNJeucA54h6Xmi7ZH9wqrZLwj3CuvQKA", - { - "aura": "0x741cfb39ec61bc76824ccec62d61670a80a890e0e21d58817f84040d3ec54474" + "aura": "5DknBCD1h49nc8eqnm6XtHz3bMQm5hfMuGYcLenRfCmpnBJG" } ] ]' \ diff --git a/cumulus/test/client/Cargo.toml b/cumulus/test/client/Cargo.toml index 7190172101cb509f7dd7c19ad25dc6d4d54036e7..9dfa6ecb3513e42a4d85615a3c844ab98e451ad9 100644 --- a/cumulus/test/client/Cargo.toml +++ b/cumulus/test/client/Cargo.toml @@ -41,13 +41,16 @@ cumulus-test-relay-sproof-builder = { path = "../relay-sproof-builder" } cumulus-primitives-core = { path = "../../primitives/core" } cumulus-primitives-proof-size-hostfunction = { path = "../../primitives/proof-size-hostfunction" } cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" } +cumulus-primitives-storage-weight-reclaim = { path = "../../primitives/storage-weight-reclaim" } [features] runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-storage-weight-reclaim/runtime-benchmarks", "cumulus-test-service/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "sc-service/runtime-benchmarks", diff --git a/cumulus/test/client/src/lib.rs b/cumulus/test/client/src/lib.rs index df63f683de6b4312a953bbbf9f03862eac7ce451..9239ff0f23eeb062d1eaefbe2341720e38178f07 100644 --- a/cumulus/test/client/src/lib.rs +++ b/cumulus/test/client/src/lib.rs @@ -19,7 +19,7 @@ mod block_builder; use codec::{Decode, Encode}; use runtime::{ - Balance, Block, BlockHashCount, Runtime, RuntimeCall, Signature, SignedExtra, SignedPayload, + Balance, Block, BlockHashCount, Runtime, RuntimeCall, Signature, SignedPayload, TxExtension, UncheckedExtrinsic, VERSION, }; use sc_executor::HeapAllocStrategy; @@ -125,7 +125,7 @@ impl DefaultTestClientBuilderExt for TestClientBuilder { /// Create an unsigned extrinsic from a runtime call. pub fn generate_unsigned(function: impl Into) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_unsigned(function.into()) + UncheckedExtrinsic::new_bare(function.into()) } /// Create a signed extrinsic from a runtime call and sign @@ -143,7 +143,7 @@ pub fn generate_extrinsic_with_pair( let period = BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckGenesis::::new(), @@ -151,14 +151,16 @@ pub fn generate_extrinsic_with_pair( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), + ) + .into(); let function = function.into(); let raw_payload = SignedPayload::from_raw( function.clone(), - extra.clone(), - ((), VERSION.spec_version, genesis_block, current_block_hash, (), (), ()), + tx_ext.clone(), + ((), VERSION.spec_version, genesis_block, current_block_hash, (), (), (), ()), ); let signature = raw_payload.using_encoded(|e| origin.sign(e)); @@ -166,7 +168,7 @@ pub fn generate_extrinsic_with_pair( function, origin.public().into(), Signature::Sr25519(signature), - extra, + tx_ext, ) } @@ -203,13 +205,16 @@ pub fn validate_block( let mut ext_ext = ext.ext(); let heap_pages = HeapAllocStrategy::Static { extra_pages: 1024 }; - let executor = WasmExecutor::::builder() - .with_execution_method(WasmExecutionMethod::default()) - .with_max_runtime_instances(1) - .with_runtime_cache_size(2) - .with_onchain_heap_alloc_strategy(heap_pages) - .with_offchain_heap_alloc_strategy(heap_pages) - .build(); + let executor = WasmExecutor::<( + sp_io::SubstrateHostFunctions, + cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions, + )>::builder() + .with_execution_method(WasmExecutionMethod::default()) + .with_max_runtime_instances(1) + .with_runtime_cache_size(2) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .build(); executor .uncached_call( diff --git a/cumulus/test/relay-sproof-builder/Cargo.toml b/cumulus/test/relay-sproof-builder/Cargo.toml index 02a9750d78ec09d674f37833a61a03bf9dc6daf0..ff5c4bd66b9742383bcd170f0012695d7f0c72d2 100644 --- a/cumulus/test/relay-sproof-builder/Cargo.toml +++ b/cumulus/test/relay-sproof-builder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cumulus-test-relay-sproof-builder" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/test/runtime/Cargo.toml b/cumulus/test/runtime/Cargo.toml index 5902a62512bed772318145ccdb954ff2dfef4c92..449a8b819bc074e0c891d99d6fa2f42480f56ce6 100644 --- a/cumulus/test/runtime/Cargo.toml +++ b/cumulus/test/runtime/Cargo.toml @@ -39,6 +39,7 @@ sp-version = { path = "../../../substrate/primitives/version", default-features # Cumulus cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } cumulus-primitives-core = { path = "../../primitives/core", default-features = false } +cumulus-primitives-storage-weight-reclaim = { path = "../../primitives/storage-weight-reclaim", default-features = false } [build-dependencies] substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } @@ -49,6 +50,7 @@ std = [ "codec/std", "cumulus-pallet-parachain-system/std", "cumulus-primitives-core/std", + "cumulus-primitives-storage-weight-reclaim/std", "frame-executive/std", "frame-support/std", "frame-system-rpc-runtime-api/std", diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 3de77cb1e58116838ff474d9b7b7e7d549675a66..154948a24b48111c882fa76008007c7e7fdb861a 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -236,7 +236,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -247,6 +246,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } impl pallet_sudo::Config for Runtime { @@ -323,8 +323,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckGenesis, @@ -332,10 +332,11 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -346,7 +347,7 @@ pub type Executive = frame_executive::Executive< TestOnRuntimeUpgrade, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; pub struct TestOnRuntimeUpgrade; @@ -377,7 +378,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index dcd70ca97e8bf5dda7e2982049ade959c43c9cf0..39e8aeba433ab52f2bfb151ca78a73e8d30c4185 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -14,13 +14,13 @@ path = "src/main.rs" [dependencies] async-trait = "0.1.74" -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } criterion = { version = "0.5.1", features = ["async_tokio"] } -jsonrpsee = { version = "0.16.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } rand = "0.8.5" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } tokio = { version = "1.32.0", features = ["macros"] } tracing = "0.1.37" url = "2.4.0" @@ -81,6 +81,7 @@ cumulus-relay-chain-minimal-node = { path = "../../client/relay-chain-minimal-no cumulus-client-pov-recovery = { path = "../../client/pov-recovery" } cumulus-test-relay-sproof-builder = { path = "../relay-sproof-builder" } cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } +cumulus-primitives-storage-weight-reclaim = { path = "../../primitives/storage-weight-reclaim" } pallet-timestamp = { path = "../../../substrate/frame/timestamp" } [dev-dependencies] @@ -103,10 +104,12 @@ substrate-test-utils = { path = "../../../substrate/test-utils" } runtime-benchmarks = [ "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-storage-weight-reclaim/runtime-benchmarks", "cumulus-test-client/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-im-online/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-cli/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", diff --git a/cumulus/test/service/src/bench_utils.rs b/cumulus/test/service/src/bench_utils.rs index 4ace894b392aa2393741572249ed35f0a7101845..15128b23787987f22d2da6f409e2a7d6702c0095 100644 --- a/cumulus/test/service/src/bench_utils.rs +++ b/cumulus/test/service/src/bench_utils.rs @@ -69,7 +69,7 @@ pub fn extrinsic_set_time(client: &TestClient) -> OpaqueExtrinsic { let timestamp = best_number as u64 * cumulus_test_runtime::MinimumPeriod::get(); cumulus_test_runtime::UncheckedExtrinsic { - signature: None, + preamble: sp_runtime::generic::Preamble::Bare, function: cumulus_test_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: timestamp, }), @@ -102,7 +102,7 @@ pub fn extrinsic_set_validation_data( }; cumulus_test_runtime::UncheckedExtrinsic { - signature: None, + preamble: sp_runtime::generic::Preamble::Bare, function: cumulus_test_runtime::RuntimeCall::ParachainSystem( cumulus_pallet_parachain_system::Call::set_validation_data { data }, ), diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index 37ea984ac87a44957edc2f4499fb2e5fb30cbecf..f05dc5f180f5a9ec526117fd980042ca7420e949 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -67,7 +67,7 @@ use sc_network::{ use sc_service::{ config::{ BlocksPruning, DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, NetworkConfiguration, - OffchainWorkerConfig, PruningMode, WasmExecutionMethod, + OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, WasmExecutionMethod, }, BasePath, ChainSpec as ChainSpecService, Configuration, Error as ServiceError, PartialComponents, Role, RpcHandlers, TFullBackend, TFullClient, TaskManager, @@ -112,7 +112,7 @@ pub type AnnounceBlockFn = Arc>) + Send + Sync>; pub struct RuntimeExecutor; impl sc_executor::NativeExecutionDispatch for RuntimeExecutor { - type ExtendHostFunctions = (); + type ExtendHostFunctions = cumulus_client_service::storage_proof_size::HostFunctions; fn dispatch(method: &str, data: &[u8]) -> Option> { cumulus_test_runtime::api::dispatch(method, data) @@ -271,6 +271,7 @@ async fn build_relay_chain_interface( polkadot_service::IsParachainNode::Collator(CollatorPair::generate().0) }, None, + polkadot_service::CollatorOverseerGen, ) .map_err(|e| RelayChainError::Application(Box::new(e) as Box<_>))?, cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) => @@ -799,6 +800,9 @@ pub fn node_config( 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, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, @@ -879,7 +883,7 @@ pub fn construct_extrinsic( .map(|c| c / 2) .unwrap_or(2) as u64; let tip = 0; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckGenesis::::new(), @@ -890,18 +894,20 @@ pub fn construct_extrinsic( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), + ) + .into(); let raw_payload = runtime::SignedPayload::from_raw( function.clone(), - extra.clone(), - ((), runtime::VERSION.spec_version, genesis_block, current_block_hash, (), (), ()), + tx_ext.clone(), + ((), runtime::VERSION.spec_version, genesis_block, current_block_hash, (), (), (), ()), ); let signature = raw_payload.using_encoded(|e| caller.sign(e)); runtime::UncheckedExtrinsic::new_signed( function, caller.public().into(), runtime::Signature::Sr25519(signature), - extra, + tx_ext, ) } diff --git a/cumulus/xcm/xcm-emulator/Cargo.toml b/cumulus/xcm/xcm-emulator/Cargo.toml index 0f10221d6006abce96b4dfb69445678957810693..6b45770a8e3df47cb083dba5a8a0eeed1759e338 100644 --- a/cumulus/xcm/xcm-emulator/Cargo.toml +++ b/cumulus/xcm/xcm-emulator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "xcm-emulator" description = "Test kit to emulate XCM program execution." -version = "0.1.0" +version = "0.5.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0" } paste = "1.0.14" -log = { version = "0.4.20", default-features = false } +log = { workspace = true } lazy_static = "1.4.0" impl-trait-for-tuples = "0.2.2" @@ -21,6 +21,7 @@ frame-support = { path = "../../../substrate/frame/support" } frame-system = { path = "../../../substrate/frame/system" } sp-io = { path = "../../../substrate/primitives/io" } sp-core = { path = "../../../substrate/primitives/core" } +sp-crypto-hashing = { path = "../../../substrate/primitives/crypto/hashing" } sp-std = { path = "../../../substrate/primitives/std" } sp-runtime = { path = "../../../substrate/primitives/runtime" } sp-arithmetic = { path = "../../../substrate/primitives/arithmetic" } diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs index c5cc632574bc02e4911684939e3ce993592e9b67..babb318a99500932dd8a2e42a2b443944751d286 100644 --- a/cumulus/xcm/xcm-emulator/src/lib.rs +++ b/cumulus/xcm/xcm-emulator/src/lib.rs @@ -38,7 +38,8 @@ pub use frame_system::{Config as SystemConfig, Pallet as SystemPallet}; pub use pallet_balances::AccountData; pub use pallet_message_queue; pub use sp_arithmetic::traits::Bounded; -pub use sp_core::{blake2_256, parameter_types, sr25519, storage::Storage, Pair}; +pub use sp_core::{parameter_types, sr25519, storage::Storage, Pair}; +pub use sp_crypto_hashing::blake2_256; pub use sp_io::TestExternalities; pub use sp_runtime::BoundedSlice; pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, fmt::Debug}; diff --git a/cumulus/zombienet/tests/0002-pov_recovery.toml b/cumulus/zombienet/tests/0002-pov_recovery.toml index fe42fd4b2f6681154e89d5e7274618a8f307dfab..15a61eba2a0342bce55f805473d2c3ba9b51c7f8 100644 --- a/cumulus/zombienet/tests/0002-pov_recovery.toml +++ b/cumulus/zombienet/tests/0002-pov_recovery.toml @@ -4,7 +4,7 @@ default_command = "polkadot" chain = "rococo-local" -[relaychain.genesis.runtimeGenesis.patch.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] # set parameters such that collators only connect to 1 validator as a backing group max_validators_per_core = 1 group_rotation_frequency = 100 # 10 mins diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index ebad4046d2682c1c04c7fac6077a3a1a52a5c986..4bfb73acda05880ef49570594d0769d1e5e4b147 100644 --- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile +++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile @@ -22,7 +22,7 @@ LABEL io.parity.image.authors="devops-team@parity.io" \ io.parity.image.source="https://github.com/paritytech/polkadot-sdk/blob/${VCS_REF}/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile" \ io.parity.image.revision="${VCS_REF}" \ io.parity.image.created="${BUILD_DATE}" \ - io.parity.image.documentation="https://github.com/paritytech/polkadot-sdk/bridges/zombienet" + io.parity.image.documentation="https://github.com/paritytech/polkadot-sdk/bridges/testing" # show backtraces ENV RUST_BACKTRACE 1 @@ -45,7 +45,7 @@ RUN mkdir -p /home/nonroot/bridges-polkadot-sdk COPY ./artifacts/bridges-polkadot-sdk /home/nonroot/bridges-polkadot-sdk # also prepare `generate_hex_encoded_call` for running RUN set -eux; \ - cd /home/nonroot/bridges-polkadot-sdk/cumulus/scripts/generate_hex_encoded_call; \ + cd /home/nonroot/bridges-polkadot-sdk/bridges/testing/framework/utils/generate_hex_encoded_call; \ npm install # check if executable works in this container @@ -56,5 +56,3 @@ RUN /usr/local/bin/substrate-relay --version # https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:{PORT}#/explorer EXPOSE 9942 9910 8943 9945 9010 8945 - -ENTRYPOINT ["/bin/bash", "-c", "/home/nonroot/bridges-polkadot-sdk/bridges/zombienet/run-tests.sh"] diff --git a/docs/AUDIT.md b/docs/AUDIT.md new file mode 100644 index 0000000000000000000000000000000000000000..008600fd369865fc246466e8d1aff2cd858751d3 --- /dev/null +++ b/docs/AUDIT.md @@ -0,0 +1,22 @@ +# Audit + +Audits are conducted to ensure the absence of severe or exploitable bugs. Pull Requests are generally merged into the +`master` branch without audit. The `audited` tag is used to track the latest audited commit of the `master` branch. This +means that audits need to happen in order of being merged. +This is an optimistic approach that lets us develop with greater speed, while requiring (possibly) large refactors in +the failure case. + +Audits can be deferred if the logic is gated by an `experimental` feature or marked as "Not Production Ready" within the +first line of doc. Such changes should be queued manually before these warnings are removed. + +## General Guidelines for what to Audit + +There is no single one-fits-all rule. Generally we should audit important logic that could immediately be used on +production networks. If in doubt, ask in chat or in the Merge Request. + +## Requesting an Audit + +1. Add the PR to the project `Security Audit (PRs) - SRLabs` +2. Set status to Backlog +3. Assign priority, considering the universe of PRs currently in the backlog +4. Add the component diff --git a/docs/RELEASE.md b/docs/RELEASE.md new file mode 100644 index 0000000000000000000000000000000000000000..e73be2779a99426203e209da846f938c0f73cceb --- /dev/null +++ b/docs/RELEASE.md @@ -0,0 +1,168 @@ +# Release + +The outputs of a release are the `polkadot` and `polkadot-parachain` node binaries, the runtimes for Westend & Rococo +and their system parachains, and new crate versions published to `crates.io`. + +# Setup + +We have two branches: `master` and `stable`. `master` is the main development branch where normal Pull Requests are +opened. Developers need to mostly only care about this branch. +The `stable` branch contains a version of the code that is ready to be released. Its contents are always audited. +Merging to it is restricted to [Backports](#backports). + +# Versioning + +We are releasing multiple different things from this repository in one release, but we don't want to use the same +version for everything. Thus, in the following we explain the versioning story for the crates, node and Westend & +Rococo. To easily refer to a release, it shall be named by its date in the form `stableYYMMDD`. + +## Crate + +We try to follow [SemVer 2.0.0](https://semver.org/) as best as possible for versioning our crates. The definitions of +`major`, `minor` and `patch` version for Rust crates are slightly altered from their standard for pre `1.0.0` versions. +Quoting [rust-lang.org](https://doc.rust-lang.org/cargo/reference/semver.html): + +>Initial development releases starting with “0.y.z” can treat changes in “y” as a major release, and “z” as a minor +release. “0.0.z” releases are always major changes. This is because Cargo uses the convention that only changes in the +left-most non-zero component are considered incompatible. + +SemVer requires a piece of software to first declare a public API. The public API of the Polkadot SDK +is hereby declared as the sum of all crates' public APIs. + +Inductively, the public API of our library crates is declared as all public items that are neither: +- Inside a `__private` module +- Documented as "unstable" or "experimental" in the first line of docs +- Bear `unstable` or `experimental` in their absolute path + +## Node + +The versioning of the Polkadot node is done most of the time by only incrementing the `minor` version. The `major` +version is only bumped for special releases and the `patch` can be used for an out of band release that fixes some +critical bug. The node version is not following SemVer. This means that the version doesn't express if there are any +breaking changes in the CLI interface or similar. The node version is declared in the +[`NODE_VERSION`](https://paritytech.github.io/polkadot-sdk/master/polkadot_node_primitives/constant.NODE_VERSION.html) +variable. + +## Westend & Rococo + +For the these networks, in addition to incrementing the `Cargo.toml` version we also increment the `spec_version` and +sometimes the `transaction_version`. The spec version is also following the node version. Its schema is: `M_mmm_ppp` and +for example `1_002_000` is the node release `1.2.0`. This versioning has no further meaning, and is only done to map +from an on chain `spec_version` easily to the release in this repository. +The Westend testnet will be updated to a new runtime every two weeks with the latest `nightly` release. + +# Backports + +**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. + +**From `stable` to `master`** + +Should not be needed since all changes first get merged into `master`. The `stable` branch can get out of sync and will +be synced with the [Clobbering](#clobbering) process. + +# Processes + +The following processes are necessary to actualize our releases. Each process has a *Cadence* on which it must execute +and a *Responsible* that is responsible for autonomously doing so and reporting back any error in the *RelEng: Polkadot +Release Coordination* Matrix channel. All processes should be automated as much as possible. + +## Crate Bumping + +Cadence: (possibly) each Pull Request. Responsible: Developer that opened the Pull Request. + +Following SemVer isn't easy, but there exists [a guide](https://doc.rust-lang.org/cargo/reference/semver.html) in the +Rust documentation that explains the small details on when to bump what. This process is supported with a CI check that +utilizes [`cargo-semver-checks`](https://github.com/obi1kenobi/cargo-semver-checks). + +### Steps + +1. Developer opens a Pull Request with changed crates against `master`. +1. They bump all changed crates according to SemVer. Note that this includes any crates that expose the changed + behaviour in their *public API* and also transitive dependencies for whom the same rule applies. + +## Stable Release + +Cadence: every two weeks. Responsible: Release Team. + +This process aims to release the `stable` branch as a *Stable* release every two weeks. + +### Steps + +1. Check and execute process [Clobbering](#clobbering), if needed. +2. Check if there were any changes since the last release and abort, if not. +3. Check out the latest commit of `stable`. +4. Update the `CHANGELOG.md` version, date and compile the content using the PrDoc files. +5. Open a Pull Request against `stable` for visibility of the release happening. +6. Internal QA from the release team can happen here. +7. Do a dry-run release to ensure that it *should* work. +8. Comment in the Pull Request that a *Stable* release will happen from the merged commit hash. +9. Release all changed crates to crates.io. +10. Create the release `stableYYYYMMDD` on GitHub. Note that the Fellowship has a streamlined process that combines the + two last steps. A similar approach should be taken here. + +## Nightly Release + +Cadence: every day at 00:00 UTC+1. Responsible: Release Team + +This process aims to release the `master` branch as a *Nightly* release. The process can start at 00:00 UTC+1 and should +automatically do the following steps. + +1. Check out the latest commit of branch `master`. +2. Compare this commit to the latest `nightly*` tag and abort if there are no changes detected. +3. Set the version of all crates that changed to `major.0.0-nightlyYYMMDD` where `major` is the last released `major` + version of that crate plus one. +4. Patch the dependencies of the changed crates to point to the newest version of the dependency. +5. Tag this commit as `nightlyYYMMDD`. +6. Do a dry-run release to ensure that it *should* work. +7. Push this tag (the commit will not belong to any branch). +8. Release all crates that had changed to crates.io. + +## Clobbering + +Cadence: every 6th release (~3 months). Responsible: Release Team + +This process aims to bring branch `stable` in sync with the latest audited commit of `master`. It is not done via a Pull +Request but rather by just copying files. It should be automated. +The following script is provided to do the clobbering. Note that it keeps the complete history of all past clobbering +processes. + +```bash +# Ensure we have the latest remote data +git fetch +# Switch to the stable branch +git checkout stable + +# Delete all tracked files in the working directory +git ls-files -z | xargs -0 rm -f +# Find and delete any empty directories +find . -type d -empty -delete + +# Get the last audited commit +AUDITED=$(git rev-parse --short=10 origin/audited) +# Grab the files from the commit +git checkout $AUDITED -- . + +# Stage, commit, and push the working directory which now matches 'audited' 1:1 +git add . +git commit -m "Clobbering with audited ($AUDITED)" +git push +``` + +## Bug and Security Fix + +Cadence: n.a. Responsible: Developer + +Describes how developers should merge bug and security fixes. + +### Steps + +1. Developer opens a Pull Request with a bug or security fix. +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. diff --git a/docs/mermaid/IA.mmd b/docs/mermaid/IA.mmd index 93d3e92814cf1307fa7c0ea9f290ed21edae58fb..4eb50bcf96a8932de3fa90748fcfeb3ca7f02a5f 100644 --- a/docs/mermaid/IA.mmd +++ b/docs/mermaid/IA.mmd @@ -3,7 +3,7 @@ flowchart devhub --> polkadot_sdk devhub --> reference_docs - devhub --> tutorial + devhub --> guides polkadot_sdk --> substrate polkadot_sdk --> frame diff --git a/docs/mermaid/outer_runtime_types.mmd b/docs/mermaid/outer_runtime_types.mmd new file mode 100644 index 0000000000000000000000000000000000000000..c909df16af1ffbb697e3d363634fa218005a9c32 --- /dev/null +++ b/docs/mermaid/outer_runtime_types.mmd @@ -0,0 +1,3 @@ +flowchart LR + RuntimeCall --"TryInto"--> PalletCall + PalletCall --"Into"--> RuntimeCall diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index 246da2cd68c6e386bbacf039768767aae70d26dd..8b498d407c02574a645420ea3d221b49177d7411 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -17,21 +17,29 @@ workspace = true # Needed for all FRAME-based code parity-scale-codec = { version = "3.0.0", default-features = false } scale-info = { version = "2.6.0", default-features = false } -frame = { path = "../../substrate/frame", features = ["experimental", "runtime"] } +frame = { path = "../../substrate/frame", features = [ + "experimental", + "runtime", +] } pallet-examples = { path = "../../substrate/frame/examples" } pallet-default-config-example = { path = "../../substrate/frame/examples/default-config" } +pallet-example-offchain-worker = { path = "../../substrate/frame/examples/offchain-worker" } # How we build docs in rust-docs -simple-mermaid = { git = "https://github.com/kianenigma/simple-mermaid.git", rev = "e48b187bcfd5cc75111acd9d241f1bd36604344b" } -docify = "0.2.6" +simple-mermaid = "0.1.1" +docify = "0.2.7" # Polkadot SDK deps, typically all should only be in scope such that we can link to their doc item. node-cli = { package = "staging-node-cli", path = "../../substrate/bin/node/cli" } kitchensink-runtime = { path = "../../substrate/bin/node/runtime" } chain-spec-builder = { package = "staging-chain-spec-builder", path = "../../substrate/bin/utils/chain-spec-builder" } subkey = { path = "../../substrate/bin/utils/subkey" } +frame-system = { path = "../../substrate/frame/system", default-features = false } +frame-support = { path = "../../substrate/frame/support", default-features = false } +frame-executive = { path = "../../substrate/frame/executive", default-features = false } +pallet-example-single-block-migrations = { path = "../../substrate/frame/examples/single-block-migrations" } -# Substrate +# Substrate Client sc-network = { path = "../../substrate/client/network" } sc-rpc-api = { path = "../../substrate/client/rpc-api" } sc-rpc = { path = "../../substrate/client/rpc" } @@ -43,6 +51,7 @@ sc-consensus-grandpa = { path = "../../substrate/client/consensus/grandpa" } sc-consensus-beefy = { path = "../../substrate/client/consensus/beefy" } sc-consensus-manual-seal = { path = "../../substrate/client/consensus/manual-seal" } sc-consensus-pow = { path = "../../substrate/client/consensus/pow" } + substrate-wasm-builder = { path = "../../substrate/utils/wasm-builder" } # Cumulus @@ -52,7 +61,19 @@ cumulus-pallet-parachain-system = { path = "../../cumulus/pallets/parachain-syst ] } parachain-info = { package = "staging-parachain-info", path = "../../cumulus/parachains/pallets/parachain-info" } pallet-aura = { path = "../../substrate/frame/aura", default-features = false } + +# Pallets and FRAME internals pallet-timestamp = { path = "../../substrate/frame/timestamp" } +pallet-balances = { path = "../../substrate/frame/balances" } +pallet-assets = { path = "../../substrate/frame/assets" } +pallet-transaction-payment = { path = "../../substrate/frame/transaction-payment" } +pallet-utility = { path = "../../substrate/frame/utility" } +pallet-multisig = { path = "../../substrate/frame/multisig" } +pallet-proxy = { path = "../../substrate/frame/proxy" } +pallet-authorship = { path = "../../substrate/frame/authorship" } +pallet-collective = { path = "../../substrate/frame/collective" } +pallet-democracy = { path = "../../substrate/frame/democracy" } +pallet-scheduler = { path = "../../substrate/frame/scheduler" } # Primitives sp-io = { path = "../../substrate/primitives/io" } @@ -60,10 +81,11 @@ sp-api = { path = "../../substrate/primitives/api" } sp-core = { path = "../../substrate/primitives/core" } sp-keyring = { path = "../../substrate/primitives/keyring" } sp-runtime = { path = "../../substrate/primitives/runtime" } +sp-offchain = { path = "../../substrate/primitives/offchain" } +sp-version = { path = "../../substrate/primitives/version" } -[dev-dependencies] -parity-scale-codec = "3.6.5" -scale-info = "2.9.0" +# XCM +xcm = { package = "staging-xcm", path = "../../polkadot/xcm" } [features] experimental = ["pallet-aura/experimental"] diff --git a/docs/sdk/headers/header.html b/docs/sdk/headers/header.html new file mode 100644 index 0000000000000000000000000000000000000000..e28458c4ccc791d9a72613ffb530b685828ea828 --- /dev/null +++ b/docs/sdk/headers/header.html @@ -0,0 +1,144 @@ + + + diff --git a/docs/sdk/headers/theme.css b/docs/sdk/headers/theme.css new file mode 100644 index 0000000000000000000000000000000000000000..a488e15c36b70ee76b14f429434daf0717bc0320 --- /dev/null +++ b/docs/sdk/headers/theme.css @@ -0,0 +1,17 @@ +:root { + --polkadot-pink: #E6007A; + --polkadot-green: #56F39A; + --polkadot-lime: #D3FF33; + --polkadot-cyan: #00B2FF; + --polkadot-purple: #552BBF; +} + +body.sdk-docs { + nav.sidebar>div.sidebar-crate>a>img { + width: 190px; + } + + nav.sidebar { + flex: 0 0 250px; + } +} diff --git a/docs/sdk/headers/toc.html b/docs/sdk/headers/toc.html deleted file mode 100644 index a4a074cb4f3153cb135da8608462d4ecf59144cb..0000000000000000000000000000000000000000 --- a/docs/sdk/headers/toc.html +++ /dev/null @@ -1,54 +0,0 @@ - - diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index ea23af51ec9d8dc894556cbdf3ca6fceff65c8e0..6a26292482477a712f34b787b5c82b6cebab9947 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -128,8 +128,8 @@ //! //! Recall that within our pallet, (almost) all blocks of code are generic over ``. And, //! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or -//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how Rust -//! traits and generics work. If unfamiliar with this pattern, read +//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how +//! Rust traits and generics work. If unfamiliar with this pattern, read //! [`crate::reference_docs::trait_based_programming`] before going further. //! //! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct` @@ -250,14 +250,16 @@ // of event is probably not the best. //! //! With the explanation out of the way, let's see how these components can be added. Both follow a -//! fairly familiar syntax: normal Rust enums, with an extra `#[frame::event/error]` attribute -//! attached. +//! fairly familiar syntax: normal Rust enums, with extra +//! [`#[frame::event]`](frame::pallet_macros::event) and +//! [`#[frame::error]`](frame::pallet_macros::error) attributes attached. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Event)] #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Error)] //! -//! One slightly custom part of this is the `#[pallet::generate_deposit(pub(super) fn -//! deposit_event)]` part. Without going into too much detail, in order for a pallet to emit events -//! to the rest of the system, it needs to do two things: +//! One slightly custom part of this is the [`#[pallet::generate_deposit(pub(super) fn +//! deposit_event)]`](frame::pallet_macros::generate_deposit) part. Without going into too +//! much detail, in order for a pallet to emit events to the rest of the system, it needs to do two +//! things: //! //! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In //! short, by doing this, the pallet is expressing an important bound: `type RuntimeEvent: @@ -266,11 +268,12 @@ //! store it where needed. //! //! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME -//! provides a default way of storing events, and this is what `pallet::generate_deposit` is doing. +//! provides a default way of storing events, and this is what +//! [`pallet::generate_deposit`](frame::pallet_macros::generate_deposit) is doing. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", config_v2)] //! //! > These `Runtime*` types are better explained in -//! > [`crate::reference_docs::frame_composite_enums`]. +//! > [`crate::reference_docs::frame_runtime_types`]. //! //! Then, we can rewrite the `transfer` dispatchable as such: #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_v2)] @@ -280,12 +283,12 @@ #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", runtime_v2)] //! //! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent = -//! RuntimeEvent`) is generated by `construct_runtime`. An interesting way to inspect this type is -//! to see its definition in rust-docs: +//! RuntimeEvent`) is generated by +//! [`construct_runtime`](frame::runtime::prelude::construct_runtime). An interesting way to inspect +//! this type is to see its definition in rust-docs: //! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`]. //! //! -//! //! ## What Next? //! //! The following topics where used in this guide, but not covered in depth. It is suggested to @@ -293,7 +296,7 @@ //! //! - [`crate::reference_docs::safe_defensive_programming`]. //! - [`crate::reference_docs::frame_origin`]. -//! - [`crate::reference_docs::frame_composite_enums`]. +//! - [`crate::reference_docs::frame_runtime_types`]. //! - The pallet we wrote in this guide was using `dev_mode`, learn more in //! [`frame::pallet_macros::config`]. //! - Learn more about the individual pallet items/macros, such as event and errors and call, in @@ -365,7 +368,7 @@ pub mod pallet { // ensure sender has enough balance, and if so, calculate what is left after `amount`. let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; if sender_balance < amount { - return Err("InsufficientBalance".into()); + return Err("InsufficientBalance".into()) } let reminder = sender_balance - amount; diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index 075d9ddaffe5bd95bafa4ca1d06667c253d4a21b..e211476d2514419a3d0889ef426817342155c178 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -15,7 +15,7 @@ //! - Start by learning about the the [`polkadot_sdk`], its structure and context. //! - Then, head over the [`guides`]. This modules contains in-depth guides about the most important //! user-journeys of the Polkadot SDK. -//! - Whilst reading the guides, you might find back-links to [`crate::reference_docs`]. +//! - Whilst reading the guides, you might find back-links to [`reference_docs`]. //! - Finally, is the parent website of this crate that contains the //! list of further tools related to the Polkadot SDK. //! @@ -25,6 +25,11 @@ #![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_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg" +)] +#![doc(issue_tracker_base_url = "https://github.com/paritytech/polkadot-sdk/issues")] /// Meta information about this crate, how it is built, what principles dictates its evolution and /// how one can contribute to it. diff --git a/docs/sdk/src/meta_contributing.rs b/docs/sdk/src/meta_contributing.rs index 7ecf8b0adfd3a2038bd32b6d6b830c71782cd1b2..fcdcea9934bb6b8cf7ee6ec090ebd0939888238c 100644 --- a/docs/sdk/src/meta_contributing.rs +++ b/docs/sdk/src/meta_contributing.rs @@ -101,7 +101,7 @@ //! * Before even getting started, what is with all of this ``? We link to //! [`crate::reference_docs::trait_based_programming`]. //! * First, the name. Why is this called `pallet::call`? This goes back to `enum Call`, which is -//! explained in [`crate::reference_docs::frame_composite_enums`]. Build on top of this! +//! explained in [`crate::reference_docs::frame_runtime_types`]. Build on top of this! //! * Then, what is `origin`? Just an account id? [`crate::reference_docs::frame_origin`]. //! * Then, what is `DispatchResult`? Why is this called *dispatch*? Probably something that can be //! explained in the documentation of [`frame::prelude::DispatchResult`]. @@ -138,7 +138,9 @@ //! injected, run: //! //! ```sh -//! SKIP_WASM_BUILD=1 RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/toc.html" cargo doc -p polkadot-sdk-docs --no-deps --open +//! SKIP_WASM_BUILD=1 \ +//! RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/header.html --extend-css $(pwd)/docs/sdk/headers/theme.css --default-theme=ayu" \ +//! cargo doc -p polkadot-sdk-docs --no-deps --open //! ``` //! //! If even faster build time for docs is needed, you can temporarily remove most of the diff --git a/docs/sdk/src/polkadot_sdk/frame_runtime.rs b/docs/sdk/src/polkadot_sdk/frame_runtime.rs index c9eba7d64bd46b51230c1b06a96c6b89ffddd73d..f178fc82bf4ba350ffe67b8a237319f8542d686b 100644 --- a/docs/sdk/src/polkadot_sdk/frame_runtime.rs +++ b/docs/sdk/src/polkadot_sdk/frame_runtime.rs @@ -87,93 +87,89 @@ //! * writing a runtime in pure Rust, as done in [this template](https://github.com/JoshOrndorff/frameless-node-template). //! * writing a runtime in AssemblyScript,as explored in [this project](https://github.com/LimeChain/subsembly). -#[cfg(test)] -mod tests { - use frame::prelude::*; +use frame::prelude::*; - /// A FRAME based pallet. This `mod` is the entry point for everything else. All - /// `#[pallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an - /// experimental feature to break these parts into different `mod`s. See [`pallet_examples`] for - /// more. - #[docify::export] - #[frame::pallet(dev_mode)] - pub mod pallet { - use super::*; +/// A FRAME based pallet. This `mod` is the entry point for everything else. All +/// `#[pallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an +/// experimental feature to break these parts into different `mod`s. See [`pallet_examples`] for +/// more. +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet { + use super::*; - /// The configuration trait of a pallet. Mandatory. Allows a pallet to receive types at a - /// later point from the runtime that wishes to contain it. It allows the pallet to be - /// parameterized over both types and values. - #[pallet::config] - pub trait Config: frame_system::Config { - /// A type that is not known now, but the runtime that will contain this pallet will - /// know it later, therefore we define it here as an associated type. - type RuntimeEvent: IsType<::RuntimeEvent> - + From>; + /// The configuration trait of a pallet. Mandatory. Allows a pallet to receive types at a + /// later point from the runtime that wishes to contain it. It allows the pallet to be + /// parameterized over both types and values. + #[pallet::config] + pub trait Config: frame_system::Config { + /// A type that is not known now, but the runtime that will contain this pallet will + /// know it later, therefore we define it here as an associated type. + type RuntimeEvent: IsType<::RuntimeEvent> + From>; - /// A parameterize-able value that we receive later via the `Get<_>` trait. - type ValueParameter: Get; + /// A parameterize-able value that we receive later via the `Get<_>` trait. + type ValueParameter: Get; - /// Similar to [`Config::ValueParameter`], but using `const`. Both are functionally - /// equal, but offer different tradeoffs. - const ANOTHER_VALUE_PARAMETER: u32; - } + /// Similar to [`Config::ValueParameter`], but using `const`. Both are functionally + /// equal, but offer different tradeoffs. + const ANOTHER_VALUE_PARAMETER: u32; + } - /// A mandatory struct in each pallet. All functions callable by external users (aka. - /// transactions) must be attached to this type (see [`frame::pallet_macros::call`]). For - /// convenience, internal (private) functions can also be attached to this type. - #[pallet::pallet] - pub struct Pallet(PhantomData); + /// A mandatory struct in each pallet. All functions callable by external users (aka. + /// transactions) must be attached to this type (see [`frame::pallet_macros::call`]). For + /// convenience, internal (private) functions can also be attached to this type. + #[pallet::pallet] + pub struct Pallet(PhantomData); - /// The events tha this pallet can emit. - #[pallet::event] - pub enum Event {} + /// The events tha this pallet can emit. + #[pallet::event] + pub enum Event {} - /// A storage item that this pallet contains. This will be part of the state root trie/root - /// of the blockchain. - #[pallet::storage] - pub type Value = StorageValue; + /// A storage item that this pallet contains. This will be part of the state root trie/root + /// of the blockchain. + #[pallet::storage] + pub type Value = StorageValue; - /// All *dispatchable* call functions (aka. transactions) are attached to `Pallet` in a - /// `impl` block. - #[pallet::call] - impl Pallet { - /// This will be callable by external users, and has two u32s as a parameter. - pub fn some_dispatchable( - _origin: OriginFor, - _param: u32, - _other_para: u32, - ) -> DispatchResult { - Ok(()) - } + /// All *dispatchable* call functions (aka. transactions) are attached to `Pallet` in a + /// `impl` block. + #[pallet::call] + impl Pallet { + /// This will be callable by external users, and has two u32s as a parameter. + pub fn some_dispatchable( + _origin: OriginFor, + _param: u32, + _other_para: u32, + ) -> DispatchResult { + Ok(()) } } +} - /// A simple runtime that contains the above pallet and `frame_system`, the mandatory pallet of - /// all runtimes. This runtime is for testing, but it shares a lot of similarities with a *real* - /// runtime. - #[docify::export] - pub mod runtime { - use super::pallet as pallet_example; - use frame::{prelude::*, testing_prelude::*}; - - // The major macro that amalgamates pallets into `enum Runtime` - construct_runtime!( - pub enum Runtime { - System: frame_system, - Example: pallet_example, - } - ); +/// A simple runtime that contains the above pallet and `frame_system`, the mandatory pallet of +/// all runtimes. This runtime is for testing, but it shares a lot of similarities with a *real* +/// runtime. +#[docify::export] +pub mod runtime { + use super::pallet as pallet_example; + use frame::{prelude::*, testing_prelude::*}; - // These `impl` blocks specify the parameters of each pallet's `trait Config`. - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - impl frame_system::Config for Runtime { - type Block = MockBlock; + // The major macro that amalgamates pallets into `enum Runtime` + construct_runtime!( + pub enum Runtime { + System: frame_system, + Example: pallet_example, } + ); - impl pallet_example::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ValueParameter = ConstU32<42>; - const ANOTHER_VALUE_PARAMETER: u32 = 42; - } + // These `impl` blocks specify the parameters of each pallet's `trait Config`. + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_example::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValueParameter = ConstU32<42>; + const ANOTHER_VALUE_PARAMETER: u32 = 42; } } diff --git a/docs/sdk/src/polkadot_sdk/xcm.rs b/docs/sdk/src/polkadot_sdk/xcm.rs index fd4d7f62aa702f5808552936404995ad67efaed9..5dcdc9e1de076c4507cb64517360df73e6733dc7 100644 --- a/docs/sdk/src/polkadot_sdk/xcm.rs +++ b/docs/sdk/src/polkadot_sdk/xcm.rs @@ -1,5 +1,71 @@ //! # XCM //! -//! @KiChjang @franciscoaguirre -//! TODO: RFCs, xcm-spec, the future of the repo, minimal example perhaps, forward to where actual -//! docs are hosted. +//! XCM, or Cross-Consensus Messaging, is a **language** to communicate **intentions** between +//! **consensus systems**. +//! +//! ## Overview +//! +//! XCM is a standard, whose specification lives in the [xcm format repo](https://github.com/paritytech/xcm-format). +//! It's agnostic both in programming language and blockchain platform, which means it could be used +//! in Rust in Polkadot, or in Go or C++ in any other platform like Cosmos or Ethereum. +//! +//! It enables different consensus systems to communicate with each other in an expressive manner. +//! Consensus systems include blockchains, smart contracts, and any other state machine that +//! achieves consensus in some way. +//! +//! XCM is executed on a virtual machine called the XCVM. +//! Scripts can be written with the XCM language, which are often called XCMs, messages or XCM +//! programs. Each program is a series of instructions, which get executed one after the other by +//! the virtual machine. These instructions aim to encompass all major things users typically do in +//! consensus systems. There are instructions on asset transferring, teleporting, locking, among +//! others. New instructions are added and changes to the XCVM are made via the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0032-process.md). +//! +//! ## In Polkadot SDK +//! +//! The Polkadot SDK allows for easily deploying sovereign blockchains from scratch, all very +//! customizable. Dealing with many heterogeneous blockchains can be cumbersome. +//! XCM allows all these blockchains to communicate with an agreed-upon language. +//! As long as an implementation of the XCVM is implemented, the same XCM program can be executed in +//! all blockchains and perform the same task. +//! +//! ## Implementation +//! +//! A ready-to-use Rust implementation lives in the [polkadot-sdk repo](https://github.com/paritytech/polkadot-sdk/tree/master/polkadot/xcm), +//! but will be moved to its own repo in the future. +//! +//! Its main components are: +//! - `src`: the definition of the basic types and instructions +//! - [`xcm-executor`](https://paritytech.github.io/polkadot-sdk/master/staging_xcm_executor/struct.XcmExecutor.html): +//! an implementation of the virtual machine to execute instructions +//! - `pallet-xcm`: A FRAME pallet for interacting with the executor +//! - `xcm-builder`: a collection of types to configure the executor +//! - `xcm-simulator`: a playground for trying out different XCM programs and executor +//! configurations +//! +//! ## Example +//! +//! To perform the very usual operation of transferring assets, the following XCM program can be +//! used: +#![doc = docify::embed!("src/polkadot_sdk/xcm.rs", example_transfer)] +//! +//! ## Get started +//! +//! To learn how it works and to get started, go to the [XCM docs](https://paritytech.github.io/xcm-docs/). + +#[cfg(test)] +mod tests { + use xcm::latest::prelude::*; + + #[docify::export] + #[test] + fn example_transfer() { + let _transfer_program = Xcm::<()>(vec![ + WithdrawAsset((Here, 100u128).into()), + BuyExecution { fees: (Here, 100u128).into(), weight_limit: Unlimited }, + DepositAsset { + assets: All.into(), + beneficiary: AccountId32 { id: [0u8; 32].into(), network: None }.into(), + }, + ]); + } +} diff --git a/docs/sdk/src/reference_docs/development_environment_advice.rs b/docs/sdk/src/reference_docs/development_environment_advice.rs index 27dd463860476b80238f0857213de80a40b9c84b..21bbe78836c44b8afd70cab68e4b9b2f929fb4a0 100644 --- a/docs/sdk/src/reference_docs/development_environment_advice.rs +++ b/docs/sdk/src/reference_docs/development_environment_advice.rs @@ -38,7 +38,7 @@ //! // Use nightly formatting. //! // See the polkadot-sdk CI job that checks formatting for the current version used in //! // polkadot-sdk. -//! "rust-analyzer.rustfmt.extraArgs": ["+nightly-2023-11-01"], +//! "rust-analyzer.rustfmt.extraArgs": ["+nightly-2024-01-22"], //! } //! ``` //! @@ -79,7 +79,7 @@ //! # Use nightly formatting. //! # See the polkadot-sdk CI job that checks formatting for the current version used in //! # polkadot-sdk. -//! extraArgs = { "+nightly-2023-11-01" }, +//! extraArgs = { "+nightly-2024-01-22" }, //! }, //! }, //! ``` @@ -111,3 +111,74 @@ //! If you have a powerful remote server available, you may consider using //! [cargo-remote](https://github.com/sgeisler/cargo-remote) to execute cargo commands on it, //! freeing up local resources for other tasks like `rust-analyzer`. +//! +//! When using `cargo-remote`, you can configure your editor to perform the the typical +//! "check-on-save" remotely as well. The configuration for VSCode is as follows: +//! +//! ```json +//! { +//! "rust-analyzer.cargo.buildScripts.overrideCommand": [ +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! ], +//! "rust-analyzer.check.overrideCommand": [ +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--workspace", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! ], +//! } +//! ``` +//! +//! //! and the same in Lua for `neovim/nvim-lspconfig`: +//! +//! ```lua +//! ["rust-analyzer"] = { +//! cargo = { +//! buildScripts = { +//! overrideCommand = { +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! }, +//! }, +//! check = { +//! overrideCommand = { +//! "cargo", +//! "remote", +//! "--build-env", +//! "SKIP_WASM_BUILD=1", +//! "--", +//! "check", +//! "--workspace", +//! "--message-format=json", +//! "--all-targets", +//! "--all-features", +//! "--target-dir=target/rust-analyzer" +//! }, +//! }, +//! }, +//! }, +//! ``` diff --git a/docs/sdk/src/reference_docs/extrinsic_encoding.rs b/docs/sdk/src/reference_docs/extrinsic_encoding.rs index 9008f8f835f5d6cd1705c9dd4e4e4e78fd3aa2d9..be1af50003849f6500fe39d8bf2ad83c7b7dfbe2 100644 --- a/docs/sdk/src/reference_docs/extrinsic_encoding.rs +++ b/docs/sdk/src/reference_docs/extrinsic_encoding.rs @@ -56,7 +56,7 @@ //! version_and_signed, //! from_address, //! signature, -//! signed_extensions_extra, +//! transaction_extensions_extra, //! ) //! ``` //! @@ -90,31 +90,31 @@ //! The signature type used on the Polkadot relay chain is [`sp_runtime::MultiSignature`]; the //! variants there are the types of signature that can be provided. //! -//! ### signed_extensions_extra +//! ### transaction_extensions_extra //! //! This is the concatenation of the [SCALE encoded][frame::deps::codec] bytes representing each of -//! the [_signed extensions_][sp_runtime::traits::SignedExtension], and are configured by the -//! fourth generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`]. Learn more about -//! signed extensions [here][crate::reference_docs::signed_extensions]. +//! the [_transaction extensions_][sp_runtime::traits::TransactionExtension], and are configured by +//! the fourth generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`]. Learn more about +//! transaction extensions [here][crate::reference_docs::transaction_extensions]. //! -//! When it comes to constructing an extrinsic, each signed extension has two things that we are -//! interested in here: +//! When it comes to constructing an extrinsic, each transaction extension has two things that we +//! are interested in here: //! -//! - The actual SCALE encoding of the signed extension type itself; this is what will form our -//! `signed_extensions_extra` bytes. -//! - An `AdditionalSigned` type. This is SCALE encoded into the `signed_extensions_additional` data -//! of the _signed payload_ (see below). +//! - The actual SCALE encoding of the transaction extension type itself; this is what will form our +//! `transaction_extensions_extra` bytes. +//! - An `Implicit` type. This is SCALE encoded into the `transaction_extensions_implicit` data of +//! the _signed payload_ (see below). //! //! Either (or both) of these can encode to zero bytes. //! -//! Each chain configures the set of signed extensions that it uses in its runtime configuration. -//! At the time of writing, Polkadot configures them +//! Each chain configures the set of transaction extensions that it uses in its runtime +//! configuration. At the time of writing, Polkadot configures them //! [here](https://github.com/polkadot-fellows/runtimes/blob/1dc04eb954eadf8aadb5d83990b89662dbb5a074/relay/polkadot/src/lib.rs#L1432C25-L1432C25). -//! Some of the common signed extensions are defined -//! [here][frame::deps::frame_system#signed-extensions]. +//! Some of the common transaction extensions are defined +//! [here][frame::deps::frame_system#transaction-extensions]. //! -//! Information about exactly which signed extensions are present on a chain and in what order is -//! also a part of the metadata for the chain. For V15 metadata, it can be +//! Information about exactly which transaction extensions are present on a chain and in what order +//! is also a part of the metadata for the chain. For V15 metadata, it can be //! [found here][frame::deps::frame_support::__private::metadata::v15::ExtrinsicMetadata]. //! //! ## call_data @@ -127,7 +127,7 @@ //! runtimes, a call is represented as an enum of enums, where the outer enum represents the FRAME //! pallet being called, and the inner enum represents the call being made within that pallet, and //! any arguments to it. Read more about the call enum -//! [here][crate::reference_docs::frame_composite_enums]. +//! [here][crate::reference_docs::frame_runtime_types]. //! //! FRAME `Call` enums are automatically generated, and end up looking something like this: #![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", call_data)] @@ -163,8 +163,8 @@ //! ```text //! signed_payload = concat( //! call_data, -//! signed_extensions_extra, -//! signed_extensions_additional, +//! transaction_extensions_extra, +//! transaction_extensions_implicit, //! ) //! //! if length(signed_payload) > 256 { @@ -172,16 +172,16 @@ //! } //! ``` //! -//! The bytes representing `call_data` and `signed_extensions_extra` can be obtained as described -//! above. `signed_extensions_additional` is constructed by SCALE encoding the -//! ["additional signed" data][sp_runtime::traits::SignedExtension::AdditionalSigned] for each -//! signed extension that the chain is using, in order. +//! The bytes representing `call_data` and `transaction_extensions_extra` can be obtained as +//! descibed above. `transaction_extensions_implicit` is constructed by SCALE encoding the +//! ["implicit" data][sp_runtime::traits::TransactionExtensionBase::Implicit] for each +//! transaction extension that the chain is using, in order. //! //! Once we've concatenated those together, we hash the result if it's greater than 256 bytes in //! length using a Blake2 256bit hasher. //! //! The [`sp_runtime::generic::SignedPayload`] type takes care of assembling the correct payload -//! for us, given `call_data` and a tuple of signed extensions. +//! for us, given `call_data` and a tuple of transaction extensions. //! //! # Example Encoding //! @@ -192,11 +192,12 @@ #[docify::export] pub mod call_data { use parity_scale_codec::{Decode, Encode}; + use sp_runtime::{traits::Dispatchable, DispatchResultWithInfo}; // The outer enum composes calls within // different pallets together. We have two // pallets, "PalletA" and "PalletB". - #[derive(Encode, Decode)] + #[derive(Encode, Decode, Clone)] pub enum Call { #[codec(index = 0)] PalletA(PalletACall), @@ -207,23 +208,33 @@ pub mod call_data { // An inner enum represents the calls within // a specific pallet. "PalletA" has one call, // "Foo". - #[derive(Encode, Decode)] + #[derive(Encode, Decode, Clone)] pub enum PalletACall { #[codec(index = 0)] Foo(String), } - #[derive(Encode, Decode)] + #[derive(Encode, Decode, Clone)] pub enum PalletBCall { #[codec(index = 0)] Bar(String), } + + impl Dispatchable for Call { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo { + Ok(()) + } + } } #[docify::export] pub mod encoding_example { use super::call_data::{Call, PalletACall}; - use crate::reference_docs::signed_extensions::signed_extensions_example; + use crate::reference_docs::transaction_extensions::transaction_extensions_example; use parity_scale_codec::Encode; use sp_core::crypto::AccountId32; use sp_keyring::sr25519::Keyring; @@ -232,34 +243,40 @@ pub mod encoding_example { MultiAddress, MultiSignature, }; - // Define some signed extensions to use. We'll use a couple of examples - // from the signed extensions reference doc. - type SignedExtensions = - (signed_extensions_example::AddToPayload, signed_extensions_example::AddToSignaturePayload); + // Define some transaction extensions to use. We'll use a couple of examples + // from the transaction extensions reference doc. + type TransactionExtensions = ( + transaction_extensions_example::AddToPayload, + transaction_extensions_example::AddToSignaturePayload, + ); // We'll use `UncheckedExtrinsic` to encode our extrinsic for us. We set // the address and signature type to those used on Polkadot, use our custom - // `Call` type, and use our custom set of `SignedExtensions`. - type Extrinsic = - UncheckedExtrinsic, Call, MultiSignature, SignedExtensions>; + // `Call` type, and use our custom set of `TransactionExtensions`. + type Extrinsic = UncheckedExtrinsic< + MultiAddress, + Call, + MultiSignature, + TransactionExtensions, + >; pub fn encode_demo_extrinsic() -> Vec { // The "from" address will be our Alice dev account. let from_address = MultiAddress::::Id(Keyring::Alice.to_account_id()); - // We provide some values for our expected signed extensions. - let signed_extensions = ( - signed_extensions_example::AddToPayload(1), - signed_extensions_example::AddToSignaturePayload, + // We provide some values for our expected transaction extensions. + let transaction_extensions = ( + transaction_extensions_example::AddToPayload(1), + transaction_extensions_example::AddToSignaturePayload, ); // Construct our call data: let call_data = Call::PalletA(PalletACall::Foo("Hello".to_string())); // The signed payload. This takes care of encoding the call_data, - // signed_extensions_extra and signed_extensions_additional, and hashing + // transaction_extensions_extra and transaction_extensions_implicit, and hashing // the result if it's > 256 bytes: - let signed_payload = SignedPayload::new(&call_data, signed_extensions.clone()); + let signed_payload = SignedPayload::new(call_data.clone(), transaction_extensions.clone()); // Sign the signed payload with our Alice dev account's private key, // and wrap the signature into the expected type: @@ -269,7 +286,7 @@ pub mod encoding_example { }; // Now, we can build and encode our extrinsic: - let ext = Extrinsic::new_signed(call_data, from_address, signature, signed_extensions); + let ext = Extrinsic::new_signed(call_data, from_address, signature, transaction_extensions); let encoded_ext = ext.encode(); encoded_ext diff --git a/docs/sdk/src/reference_docs/frame_composite_enums.rs b/docs/sdk/src/reference_docs/frame_composite_enums.rs deleted file mode 100644 index 6051cd534467672b9831187ef5c8b814712f7d18..0000000000000000000000000000000000000000 --- a/docs/sdk/src/reference_docs/frame_composite_enums.rs +++ /dev/null @@ -1 +0,0 @@ -//! # FRAME Composite Enums diff --git a/docs/sdk/src/reference_docs/frame_offchain_workers.rs b/docs/sdk/src/reference_docs/frame_offchain_workers.rs new file mode 100644 index 0000000000000000000000000000000000000000..7999707e5ee018c4bb7634e7a506ff8fee8fa8ac --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_offchain_workers.rs @@ -0,0 +1,115 @@ +//! # Offchain Workers +//! +//! This reference document explains how offchain workers work in Substrate and FRAME. The main +//! focus is upon FRAME's implementation of this functionality. Nonetheless, offchain workers are a +//! Substrate-provided feature and can be used with possible alternatives to [`frame`] as well. +//! +//! Offchain workers are a commonly misunderstood topic, therefore we explain them bottom-up, +//! starting at the fundamentals and then describing the developer interface. +//! +//! ## Context +//! +//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that the node and the runtime +//! communicate with one another via host functions and runtime APIs. Many of these interactions +//! contribute to the actual state transition of the blockchain. For example [`sp_api::Core`] is the +//! main runtime API that is called to execute new blocks. +//! +//! Offchain workers are in principle not different in any way: It is a runtime API exposed by the +//! wasm blob ([`sp_offchain::OffchainWorkerApi`]), and the node software calls into it when it +//! deems fit. But, crucially, this API call is different in that: +//! +//! 1. It can have no impact on the state ie. it is _OFF (the) CHAIN_. If any state is altered +//! during the execution of this API call, it is discarded. +//! 2. It has access to an extended set of host functions that allow the wasm blob to do more. For +//! example, call into HTTP requests. +//! +//! > The main way through which an offchain worker can interact with the state is by submitting an +//! > extrinsic to the chain. This is the ONLY way to alter the state from an offchain worker. +//! > [`pallet_example_offchain_worker`] provides an example of this. +//! +//! +//! Given the "Off Chain" nature of this API, it is important to remember that calling this API is +//! entirely optional. Some nodes might call into it, some might not, and it would have no impact on +//! the execution of your blockchain because no state is altered no matter the execution of the +//! offchain worker API. +//! +//! Substrate's CLI allows some degree of configuration about this, allowing node operators to +//! specify when they want to run the offchain worker API. See +//! [`sc_cli::RunCmd::offchain_worker_params`]. +//! +//! ## Nondeterministic Execution +//! +//! Needless to say, given the above description, the code in your offchain worker API can be +//! nondeterministic, as it is not part of the blockchain's STF, so it can be executed at unknown +//! times, by unknown nodes, and has no impact on the state. This is why an HTTP +//! ([`sp_runtime::offchain::http`]) API is readily provided to the offchain worker APIs. Because +//! there is no need for determinism in this context. +//! +//! > A common mistake here is for novice developers to see this HTTP API, and imagine that +//! > `polkadot-sdk` somehow magically solved the determinism in blockchains, and now a blockchain +//! > can make HTTP calls and it will all work. This is absolutely NOT the case. An HTTP call made +//! > by the offchain worker is non-deterministic by design. Blockchains can't and always won't be +//! > able to perform non-deterministic operations such as making HTTP calls to a foreign server. +//! +//! ## FRAME's API +//! +//! [`frame`] provides a simple API through which pallets can define offchain worker functions. This +//! is part of [`frame::traits::Hooks`], which is implemented as a part of +//! [`frame::pallet_macros::hooks`]. +//! +//! ``` +//! +//! #[frame::pallet] +//! pub mod pallet { +//! use frame::prelude::*; +//! +//! #[pallet::config] +//! pub trait Config: frame_system::Config {} +//! +//! #[pallet::pallet] +//! pub struct Pallet(_); +//! +//! #[pallet::hooks] +//! impl Hooks> for Pallet { +//! fn offchain_worker(block_number: BlockNumberFor) { +//! // ... +//! } +//! } +//! } +//! ``` +//! +//! Additionally, [`sp_runtime::offchain`] provides a set of utilities that can be used to moderate +//! the execution of offchain workers. +//! +//! ## Think Twice: Why Use Substrate's Offchain Workers? +//! +//! Consider the fact that in principle, an offchain worker code written using the above API is no +//! different than an equivalent written with an _actual offchain interaction library_, such as +//! [Polkadot-JS](https://polkadot.js.org/docs/), or any of the other ones listed [here](https://github.com/substrate-developer-hub/awesome-substrate?tab=readme-ov-file#client-libraries). +//! +//! They can both read from the state, and have no means of updating the state, other than the route +//! of submitting an extrinsic to the chain. Therefore, it is worth thinking twice before embedding +//! a logic as a part of Substrate's offchain worker API. Does it have to be there? can it not be a +//! simple, actual offchain application that lives outside of the chain's WASM blob? +//! +//! Some of the reasons why you might want to do the opposite, and actually embed an offchain worker +//! API into the WASM blob are: +//! +//! * Accessing the state is easier within the `offchain_worker` function, as it is already a part +//! of the runtime, and [`frame::pallet_macros::storage`] provides all the tools needed to read +//! the state. Other client libraries might provide varying degrees of capability here. +//! * It will be updated in synchrony with the runtime. A Substrate's offchain application is part +//! of the same WASM blob, and is therefore guaranteed to be up to date. +//! +//! For example, imagine you have modified a storage item to have a new type. This will possibly +//! require a [`crate::reference_docs::frame_runtime_upgrades_and_migrations`], and any offchain +//! code, such as a Polkadot-JS application, will have to be updated to reflect this change. Whereas +//! the WASM offchain worker code is guaranteed to already be updated, or else the runtime code will +//! not even compile. +//! +//! +//! ## Further References +//! +//! - +//! - +//! - [Offchain worker example](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/examples/offchain-worker) diff --git a/docs/sdk/src/reference_docs/frame_origin.rs b/docs/sdk/src/reference_docs/frame_origin.rs index a4078377cd77dad6b3aac78ba6acdddda14251a8..49533157b014d77b3766cbfc8c3353397e0e48bf 100644 --- a/docs/sdk/src/reference_docs/frame_origin.rs +++ b/docs/sdk/src/reference_docs/frame_origin.rs @@ -1,14 +1,260 @@ //! # FRAME Origin //! -//! Notes: -//! -//! - Def talk about account abstraction and how it is a solved issue in frame. See Gav's talk in -//! Protocol Berg 2023 -//! - system's raw origin, how it is amalgamated with other origins into one type -//! [`frame_composite_enums`] -//! - signed origin -//! - unsigned origin, link to [`fee_less_runtime`] -//! - Root origin, how no one can obtain it. -//! - Abstract origin: how FRAME allows you to express "origin is 2/3 of the this body or 1/2 of -//! that body or half of the token holders". -//! - `type CustomOrigin: EnsureOrigin<_>` in pallets. +//! Let's start by clarifying a common wrong assumption about Origin: +//! +//! **ORIGIN IS NOT AN ACCOUNT ID**. +//! +//! FRAME's origin abstractions allow you to convey meanings far beyond just an account-id being the +//! caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the +//! meanings that an origin can convey. This is the commonly used [`frame_system::ensure_signed`], +//! where the return value happens to be an account-id. +//! +//! Instead, let's establish the following as the correct definition of an origin: +//! +//! > The origin type represents the privilege level of the caller of an extrinsic. +//! +//! That is, an extrinsic, through checking the origin, can *express what privilege level it wishes +//! to impose on the caller of the extrinsic*. One of those checks can be as simple as "*any account +//! that has signed a statement can pass*". +//! +//! But the origin system can also express more abstract and complicated privilege levels. For +//! example: +//! +//! * If the majority of token holders agreed upon this. This is more or less what the +//! [`pallet_democracy`] does under the hood ([reference](https://github.com/paritytech/polkadot-sdk/blob/edd95b3749754d2ed0c5738588e872c87be91624/substrate/frame/democracy/src/lib.rs#L1603-L1633)). +//! * If a specific ratio of an instance of [`pallet_collective`]/DAO agrees upon this. +//! * If another consensus system, for example a bridged network or a parachain, agrees upon this. +//! * If the majority of validator/authority set agrees upon this[^1]. +//! * If caller holds a particular NFT. +//! +//! and many more. +//! +//! ## Context +//! +//! First, let's look at where the `origin` type is encountered in a typical pallet. The `origin: +//! OriginFor` has to be the first argument of any given callable extrinsic in FRAME: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", call_simple)] +//! +//! Typically, the code of an extrinsic starts with an origin check, such as +//! [`frame_system::ensure_signed`]. +//! +//! Note that [`OriginFor`](frame_system::pallet_prelude::OriginFor) is merely a shorthand for +//! [`frame_system::Config::RuntimeOrigin`]. Given the name prefix `Runtime`, we can learn that +//! `RuntimeOrigin` is similar to `RuntimeCall` and others, a runtime composite enum that is +//! amalgamated at the runtime level. Read [`crate::reference_docs::frame_runtime_types`] to +//! familiarize yourself with these types. +//! +//! To understand this better, we will next create a pallet with a custom origin, which will add a +//! new variant to `RuntimeOrigin`. +//! +//! ## Adding Custom Pallet Origin to the Runtime +//! +//! For example, given a pallet that defines the following custom origin: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin)] +//! +//! And a runtime with the following pallets: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", runtime_exp)] +//! +//! The type [`crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin`] is expanded. +//! This `RuntimeOrigin` contains a variant for the [`frame_system::RawOrigin`] and the custom +//! origin of the pallet. +//! +//! > Notice how the [`frame_system::ensure_signed`] is nothing more than a `match` statement. If +//! > you want to know where the actual origin of an extrinsic is set (and the signature +//! > verification happens, if any), see +//! > [`sp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically +//! > [`sp_runtime::traits::Applyable`]'s implementation. +//! +//! ## Asserting on a Custom Internal Origin +//! +//! In order to assert on a custom origin that is defined within your pallet, we need a way to first +//! convert the `::RuntimeOrigin` into the local `enum Origin` of the +//! current pallet. This is a common process that is explained in +//! [`crate::reference_docs::frame_runtime_types# +//! adding-further-constraints-to-runtime-composite-enums`]. +//! +//! We use the same process here to express that `RuntimeOrigin` has a number of additional bounds, +//! as follows. +//! +//! 1. Defining a custom `RuntimeOrigin` with further bounds in the pallet. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_bound)] +//! +//! 2. Using it in the pallet. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_usage)] +//! +//! ## Asserting on a Custom External Origin +//! +//! Very often, a pallet wants to have a parameterized origin that is **NOT** defined within the +//! pallet. In other words, a pallet wants to delegate an origin check to something that is +//! specified later at the runtime level. Like many other parameterizations in FRAME, this implies +//! adding a new associated type to `trait Config`. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_def)] +//! +//! Then, within the pallet, we can simply use this "unknown" origin check type: +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_usage)] +//! +//! Finally, at the runtime, any implementation of [`frame::traits::EnsureOrigin`] can be passed. +#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_provide)] +//! +//! Indeed, some of these implementations of [`frame::traits::EnsureOrigin`] are similar to the ones +//! that we know about: [`frame::runtime::prelude::EnsureSigned`], +//! [`frame::runtime::prelude::EnsureSignedBy`], [`frame::runtime::prelude::EnsureRoot`], +//! [`frame::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known +//! to us, and are defined in other pallets. +//! +//! For example, [`pallet_collective`] defines [`pallet_collective::EnsureMember`] and +//! [`pallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we alluded +//! to earlier in this document. +//! +//! Make sure to check the full list of [implementors of +//! `EnsureOrigin`](frame::traits::EnsureOrigin#implementors) for more inspiration. +//! +//! ## Obtaining Abstract Origins +//! +//! So far we have learned that FRAME pallets can assert on custom and abstract origin types, +//! whether they are defined within the pallet or not. But how can we obtain these abstract origins? +//! +//! > All extrinsics that come from the outer world can generally only be obtained as either +//! > `signed` or `none` origin. +//! +//! Generally, these abstract origins are only obtained within the runtime, when a call is +//! dispatched within the runtime. +//! +//! ## Further References +//! +//! - [Gavin Wood's speech about FRAME features at Protocol Berg 2023.](https://youtu.be/j7b8Upipmeg?si=83_XUgYuJxMwWX4g&t=195) +//! - [A related StackExchange question.](https://substrate.stackexchange.com/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin) +//! +//! [^1]: Inherents are essentially unsigned extrinsics that need an [`frame_system::ensure_none`] +//! origin check, and through the virtue of being an inherent, are agreed upon by all validators. + +use frame::prelude::*; + +#[frame::pallet(dev_mode)] +pub mod pallet_for_origin { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(call_simple)] + #[pallet::call] + impl Pallet { + pub fn do_something(_origin: OriginFor) -> DispatchResult { + // ^^^^^^^^^^^^^^^^^^^^^ + todo!(); + } + } +} + +#[frame::pallet(dev_mode)] +pub mod pallet_with_custom_origin { + use super::*; + + #[docify::export(custom_origin_bound)] + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeOrigin: From<::RuntimeOrigin> + + Into::RuntimeOrigin>>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(custom_origin)] + /// A dummy custom origin. + #[pallet::origin] + #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + pub enum Origin { + /// If all holders of a particular NFT have agreed upon this. + AllNftHolders, + /// If all validators have agreed upon this. + ValidatorSet, + } + + #[docify::export(custom_origin_usage)] + #[pallet::call] + impl Pallet { + pub fn only_validators(origin: OriginFor) -> DispatchResult { + // first, we convert from `::RuntimeOrigin` to `::RuntimeOrigin` + let local_runtime_origin = <::RuntimeOrigin as From< + ::RuntimeOrigin, + >>::from(origin); + // then we convert to `origin`, if possible + let local_origin = + local_runtime_origin.into().map_err(|_| "invalid origin type provided")?; + ensure!(matches!(local_origin, Origin::ValidatorSet), "Not authorized"); + todo!(); + } + } +} + +pub mod runtime_for_origin { + use super::pallet_with_custom_origin; + use frame::{runtime::prelude::*, testing_prelude::*}; + + #[docify::export(runtime_exp)] + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletWithCustomOrigin: pallet_with_custom_origin, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_with_custom_origin::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + } +} + +#[frame::pallet(dev_mode)] +pub mod pallet_with_external_origin { + use super::*; + #[docify::export(external_origin_def)] + #[pallet::config] + pub trait Config: frame_system::Config { + type ExternalOrigin: EnsureOrigin; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(external_origin_usage)] + #[pallet::call] + impl Pallet { + pub fn externally_checked_ext(origin: OriginFor) -> DispatchResult { + let _ = T::ExternalOrigin::ensure_origin(origin)?; + todo!(); + } + } +} + +pub mod runtime_for_external_origin { + use super::*; + use frame::{runtime::prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletWithExternalOrigin: pallet_with_external_origin, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + #[docify::export(external_origin_provide)] + impl pallet_with_external_origin::Config for Runtime { + type ExternalOrigin = EnsureSigned<::AccountId>; + } +} diff --git a/docs/sdk/src/reference_docs/frame_pallet_coupling.rs b/docs/sdk/src/reference_docs/frame_pallet_coupling.rs new file mode 100644 index 0000000000000000000000000000000000000000..942717ecfb2a12e44d45173c1631f77907f9dbaa --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_pallet_coupling.rs @@ -0,0 +1,296 @@ +//! # FRAME Pallet Coupling +//! +//! This reference document explains how FRAME pallets can be combined to interact together. +//! +//! It is suggested to re-read [`crate::polkadot_sdk::frame_runtime`], notably the information +//! around [`frame::pallet_macros::config`]. Recall that: +//! +//! > Configuration trait of a pallet: It allows a pallet to receive types at a later +//! > point from the runtime that wishes to contain it. It allows the pallet to be parameterized +//! > over both types and values. +//! +//! ## Context, Background +//! +//! FRAME pallets, as per described in [`crate::polkadot_sdk::frame_runtime`] are: +//! +//! > A pallet is a unit of encapsulated logic. It has a clearly defined responsibility and can be +//! linked to other pallets. +//! +//! That is to say: +//! +//! * *encapsulated*: Ideally, a FRAME pallet contains encapsulated logic which has clear +//! boundaries. It is generally a bad idea to build a single monolithic pallet that does multiple +//! things, such as handling currencies, identities and staking all at the same time. +//! * *linked to other pallets*: But, adhering extensively to the above also hinders the ability to +//! write useful applications. Pallets often need to work with each other, communicate and use +//! each other's functionalities. +//! +//! The broad principle that allows pallets to be linked together is the same way through which a +//! pallet uses its `Config` trait to receive types and values from the runtime that contains it. +//! +//! There are generally two ways to achieve this: +//! +//! 1. Tight coupling pallets +//! 2. Loose coupling pallets +//! +//! To explain the difference between the two, consider two pallets, `A` and `B`. In both cases, `A` +//! wants to use some functionality exposed by `B`. +//! +//! When tightly coupling pallets, `A` can only exist in a runtime if `B` is also present in the +//! same runtime. That is, `A` is expressing that can only work if `B` is present. +//! +//! This translates to the following Rust code: +//! +//! ``` +//! trait Pallet_B_Config {} +//! trait Pallet_A_Config: Pallet_B_Config {} +//! ``` +//! +//! Contrary, when pallets are loosely coupled, `A` expresses that some functionality, expressed via +//! a trait `F`, needs to be fulfilled. This trait is then implemented by `B`, and the two pallets +//! are linked together at the runtime level. This means that `A` only relies on the implementation +//! of `F`, which may be `B`, or another implementation of `F`. +//! +//! This translates to the following Rust code: +//! +//! ``` +//! trait F {} +//! trait Pallet_A_Config { +//! type F: F; +//! } +//! // Pallet_B will implement and fulfill `F`. +//! ``` +//! +//! ## Example +//! +//! Consider the following example, in which `pallet-foo` needs another pallet to provide the block +//! author to it, and `pallet-author` which has access to this information. +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_foo)] +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_author)] +//! +//! ### Tight Coupling Pallets +//! +//! To tightly couple `pallet-foo` and `pallet-author`, we use Rust's supertrait system. When a +//! pallet makes its own `trait Config` be bounded by another pallet's `trait Config`, it is +//! expressing two things: +//! +//! 1. that it can only exist in a runtime if the other pallet is also present. +//! 2. that it can use the other pallet's functionality. +//! +//! `pallet-foo`'s `Config` would then look like: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_config)] +//! +//! And `pallet-foo` can use the method exposed by `pallet_author::Pallet` directly: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_usage)] +//! +//! +//! ### Loosely Coupling Pallets +//! +//! If `pallet-foo` wants to *not* rely on `pallet-author` directly, it can leverage its +//! `Config`'s associated types. First, we need a trait to express the functionality that +//! `pallet-foo` wants to obtain: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", AuthorProvider)] +//! +//! > We sometimes refer to such traits that help two pallets interact as "glue traits". +//! +//! Next, `pallet-foo` states that it needs this trait to be provided to it, at the runtime level, +//! via an associated type: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_config)] +//! +//! Then, `pallet-foo` can use this trait to obtain the block author, without knowing where it comes +//! from: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_usage)] +//! +//! Then, if `pallet-author` implements this glue-trait: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_author_provider)] +//! +//! And upon the creation of the runtime, the two pallets are linked together as such: +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", runtime_author_provider)] +//! +//! Crucially, when using loose coupling, we gain the flexibility of providing different +//! implementations of `AuthorProvider`, such that different users of a `pallet-foo` can use +//! different ones, without any code change being needed. For example, in the code snippets of this +//! module, you can fund [`OtherAuthorProvider`] which is an alternative implementation of +//! [`AuthorProvider`]. +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", other_author_provider)] +//! +//! A common pattern in polkadot-sdk is to provide an implementation of such glu traits for the unit +//! type as a "default/test behavior". +#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", unit_author_provider)] +//! +//! ## Frame System +//! +//! With the above information in context, we can conclude that **`frame_system` is a special pallet +//! that is tightly coupled with every other pallet**. This is because it provides the fundamental +//! system functionality that every pallet needs, such as some types like +//! [`frame::prelude::frame_system::Config::AccountId`], +//! [`frame::prelude::frame_system::Config::Hash`], and some functionality such as block number, +//! etc. +//! +//! ## Recap +//! +//! To recap, consider the following rules of thumb: +//! +//! * In all cases, try and break down big pallets apart with clear boundaries of responsibility. In +//! general, it is easier to argue about multiple pallet if they only communicate together via a +//! known trait, rather than having access to all of each others public items, such as storage and +//! dispatchables. +//! * If a group of pallets are meant to work together, and but are not foreseen to be generalized, +//! or used by others, consider tightly coupling pallets, *if it simplifies the development*. +//! * If a pallet needs a functionality provided by another pallet, but multiple implementations can +//! be foreseen, consider loosely coupling pallets. +//! +//! For example, all pallets in `polkadot-sdk` that needed to work with currencies could have been +//! tightly coupled with [`pallet_balances`]. But, `polkadot-sdk` also provides [`pallet_assets`] +//! (and more implementations by the community), therefore all pallets use traits to loosely couple +//! with balances or assets pallet. More on this in [`crate::reference_docs::frame_currency`]. +//! +//! ## Further References +//! +//! - +//! - +//! +//! [`AuthorProvider`]: crate::reference_docs::frame_pallet_coupling::AuthorProvider +//! [`OtherAuthorProvider`]: crate::reference_docs::frame_pallet_coupling::OtherAuthorProvider + +#![allow(unused)] + +use frame::prelude::*; + +#[docify::export] +#[frame::pallet] +pub mod pallet_foo { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + impl Pallet { + fn do_stuff_with_author() { + // needs block author here + } + } +} + +#[docify::export] +#[frame::pallet] +pub mod pallet_author { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + impl Pallet { + pub fn author() -> T::AccountId { + todo!("somehow has access to the block author and can return it here") + } + } +} + +#[frame::pallet] +pub mod pallet_foo_tight { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(tight_config)] + /// This pallet can only live in a runtime that has both `frame_system` and `pallet_author`. + #[pallet::config] + pub trait Config: frame_system::Config + pallet_author::Config {} + + #[docify::export(tight_usage)] + impl Pallet { + // anywhere in `pallet-foo`, we can call into `pallet-author` directly, namely because + // `T: pallet_author::Config` + fn do_stuff_with_author() { + let _ = pallet_author::Pallet::::author(); + } + } +} + +#[docify::export] +/// Abstraction over "something that can provide the block author". +pub trait AuthorProvider { + fn author() -> AccountId; +} + +#[frame::pallet] +pub mod pallet_foo_loose { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export(loose_config)] + #[pallet::config] + pub trait Config: frame_system::Config { + /// This pallet relies on the existence of something that implements [`AuthorProvider`], + /// which may or may not be `pallet-author`. + type AuthorProvider: AuthorProvider; + } + + #[docify::export(loose_usage)] + impl Pallet { + fn do_stuff_with_author() { + let _ = T::AuthorProvider::author(); + } + } +} + +#[docify::export(pallet_author_provider)] +impl AuthorProvider for pallet_author::Pallet { + fn author() -> T::AccountId { + pallet_author::Pallet::::author() + } +} + +pub struct OtherAuthorProvider; + +#[docify::export(other_author_provider)] +impl AuthorProvider for OtherAuthorProvider { + fn author() -> AccountId { + todo!("somehow get the block author here") + } +} + +#[docify::export(unit_author_provider)] +impl AuthorProvider for () { + fn author() -> AccountId { + todo!("somehow get the block author here") + } +} + +pub mod runtime { + use super::*; + use cumulus_pallet_aura_ext::pallet; + use frame::{runtime::prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletFoo: pallet_foo_loose, + PalletAuthor: pallet_author, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_author::Config for Runtime {} + + #[docify::export(runtime_author_provider)] + impl pallet_foo_loose::Config for Runtime { + type AuthorProvider = pallet_author::Pallet; + // which is also equivalent to + // type AuthorProvider = PalletAuthor; + } +} diff --git a/docs/sdk/src/reference_docs/frame_runtime_migration.rs b/docs/sdk/src/reference_docs/frame_runtime_migration.rs deleted file mode 100644 index 0616ccbb6f57971823c7347a60574ef0c0bef2ab..0000000000000000000000000000000000000000 --- a/docs/sdk/src/reference_docs/frame_runtime_migration.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! # Runtime Runtime Upgrade and Testing -//! -//! -//! Notes: -//! -//! - Flow of things, when does `on_runtime_upgrade` get called. Link to to `Hooks` and its diagram -//! as source of truth. -//! - Data migration and when it is needed. -//! - Look into the pba-lecture. diff --git a/docs/sdk/src/reference_docs/frame_runtime_types.rs b/docs/sdk/src/reference_docs/frame_runtime_types.rs new file mode 100644 index 0000000000000000000000000000000000000000..883892729d1c1fab97f58a69bc44546d0cee5dae --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_runtime_types.rs @@ -0,0 +1,306 @@ +//! # FRAME Runtime Types +//! +//! This reference document briefly explores the idea around types generated at the runtime level by +//! the FRAME macros. +//! +//! > As of now, many of these important types are generated within the internals of +//! > [`construct_runtime`], and there is no easy way for you to visually know they exist. +//! > [#polkadot-sdk#1378](https://github.com/paritytech/polkadot-sdk/pull/1378) is meant to +//! > significantly improve this. Exploring the rust-docs of a runtime, such as [`runtime`] which is +//! > defined in this module is as of now the best way to learn about these types. +//! +//! ## Composite Enums +//! +//! Many types within a FRAME runtime follow the following structure: +//! +//! * Each individual pallet defines a type, for example `Foo`. +//! * At the runtime level, these types are amalgamated into a single type, for example +//! `RuntimeFoo`. +//! +//! As the names suggest, all composite enums in a FRAME runtime start their name with `Runtime`. +//! For example, `RuntimeCall` is a representation of the most high level `Call`-able type in the +//! runtime. +//! +//! Composite enums are generally convertible to their individual parts as such: +#![doc = simple_mermaid::mermaid!("../../../mermaid/outer_runtime_types.mmd")] +//! +//! In that one can always convert from the inner type into the outer type, but not vice versa. This +//! is usually expressed by implementing `From`, `TryFrom`, `From>` and similar traits. +//! +//! ### Example +//! +//! We provide the following two pallets: [`pallet_foo`] and [`pallet_bar`]. Each define a +//! dispatchable, and `Foo` also defines a custom origin. Lastly, `Bar` defines an additional +//! `GenesisConfig`. +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_foo)] +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)] +//! +//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and +//! [`RuntimeGenesisConfig`] generated in [`runtime`] by respectively. +//! +//! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If +//! you explore further, you will soon realize that each variant is merely a pointer to the `Call` +//! type in each pallet, for example [`pallet_foo::Call`]. +//! +//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pallet_foo` +//! which utilized [`frame::pallet_macros::origin`]. +//! +//! Finally, [`RuntimeGenesisConfig`] is composed of `frame_system` and a variant for `pallet_bar`'s +//! [`pallet_bar::GenesisConfig`]. +//! +//! You can find other composite enums by scanning [`runtime`] for other types who's name starts +//! with `Runtime`. Some of the more noteworthy ones are: +//! +//! - [`RuntimeEvent`] +//! - [`RuntimeError`] +//! - [`RuntimeHoldReason`] +//! +//! ### Adding Further Constraints to Runtime Composite Enums +//! +//! This section explores a common scenario where a pallet has access to one of these runtime +//! composite enums, but it wishes to further specify it by adding more trait bounds to it. +//! +//! Let's take the example of `RuntimeCall`. This is an associated type in +//! [`frame_system::Config::RuntimeCall`], and all pallets have access to this type, because they +//! have access to [`frame_system::Config`]. Finally, this type is meant to be set to outer call of +//! the entire runtime. +//! +//! But, let's not forget that this is information that *we know*, and the Rust compiler does not. +//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of +//! [`frame_system::Config::RuntimeCall`] are specifying: +#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", system_runtime_call)] +//! +//! So, when at a given pallet, one accesses `::RuntimeCall`, the type is +//! extremely opaque from the perspective of the Rust compiler. +//! +//! How can a pallet access the `RuntimeCall` type with further constraints? For example, each +//! pallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`, +//! therefore there should be a `impl From> for RuntimeCall`. +//! +//! The only way to express this using Rust's associated types is for the pallet to **define its own +//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**. +//! +//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is +//! very similar to [`TryFrom`]. +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call)] +//! +//! And indeed, at the runtime level, this associated type would be the same `RuntimeCall` that is +//! passed to `frame_system`. +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_with_specific_runtime_call_impl)] +//! +//! > In other words, the degree of specificity that [`frame_system::Config::RuntimeCall`] has is +//! > not enough for the pallet to work with. Therefore, the pallet has to define its own associated +//! > type representing `RuntimeCall`. +//! +//! Another way to look at this is: +//! +//! `pallet_with_specific_runtime_call::Config::RuntimeCall` and `frame_system::Config::RuntimeCall` +//! are two different representations of the same concrete type that is only known when the runtime +//! is being constructed. +//! +//! Now, within this pallet, this new `RuntimeCall` can be used, and it can use its new trait +//! bounds, such as being [`frame::traits::IsSubType`]: +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)] +//! +//! ### Asserting Equality of Multiple Runtime Composite Enums +//! +//! Recall that in the above example, `::RuntimeCall` and `::RuntimeCall` are expected to be equal types, but at the compile-time we +//! have to represent them with two different associated types with different bounds. Would it not +//! be cool if we had a test to make sure they actually resolve to the same concrete type once the +//! runtime is constructed? The following snippet exactly does that: +#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)] +//! +//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is, +//! and what [`core::any::TypeId`] is. Another way to assert this is using +//! [`frame::traits::IsType`]. +//! +//! ## Type Aliases +//! +//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy: +//! +//! * [`runtime::PalletFoo`] is an alias to [`pallet_foo::Pallet`]. Same for `PalletBar`, and +//! `System` +//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is +//! important to FRAME internals such as `executive`, as it implements traits such as +//! [`frame::traits::Hooks`]. +//! +//! ## Further Details +//! +//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of +//! `RuntimeOrigin`. +//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an +//! extrinsic. See [`crate::reference_docs::transaction_extensions`] for more information. +//! * See the documentation of [`construct_runtime`]. +//! * See the corresponding lecture in the [pba-book](https://polkadot-blockchain-academy.github.io/pba-book/frame/outer-enum/page.html). +//! +//! +//! [`construct_runtime`]: frame::runtime::prelude::construct_runtime +//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo +//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem +//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime +//! [`pallet_foo`]: crate::reference_docs::frame_runtime_types::pallet_foo +//! [`pallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pallet_foo::Call +//! [`pallet_foo::Pallet`]: crate::reference_docs::frame_runtime_types::pallet_foo::Pallet +//! [`pallet_bar`]: crate::reference_docs::frame_runtime_types::pallet_bar +//! [`pallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pallet_bar::GenesisConfig +//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent +//! [`RuntimeGenesisConfig`]: +//! crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig +//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin +//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller +//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError +//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall +//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason + +use frame::prelude::*; + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet_foo { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::origin] + #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + pub enum Origin { + A, + B, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + pub fn foo(_origin: OriginFor) -> DispatchResult { + todo!(); + } + + pub fn other(_origin: OriginFor) -> DispatchResult { + todo!(); + } + } +} + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet_bar { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig { + pub initial_account: Option, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) {} + } + + #[pallet::call] + impl Pallet { + pub fn bar(_origin: OriginFor) -> DispatchResult { + todo!(); + } + } +} + +pub mod runtime { + use super::{pallet_bar, pallet_foo}; + use frame::{runtime::prelude::*, testing_prelude::*}; + + #[docify::export(runtime_exp)] + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletFoo: pallet_foo, + PalletBar: pallet_bar, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + impl pallet_foo::Config for Runtime {} + impl pallet_bar::Config for Runtime {} +} + +#[frame::pallet(dev_mode)] +pub mod pallet_with_specific_runtime_call { + use super::*; + use frame::traits::IsSubType; + + #[docify::export(custom_runtime_call)] + /// A pallet that wants to further narrow down what `RuntimeCall` is. + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeCall: IsSubType>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + // note that this pallet needs some `call` to have a `enum Call`. + #[pallet::call] + impl Pallet { + pub fn foo(_origin: OriginFor) -> DispatchResult { + todo!(); + } + } + + #[docify::export(custom_runtime_call_usages)] + impl Pallet { + fn _do_something_useful_with_runtime_call(call: ::RuntimeCall) { + // check if the runtime call given is of this pallet's variant. + let _maybe_my_call: Option<&Call> = call.is_sub_type(); + todo!(); + } + } + + #[docify::export(assert_equality)] + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + use core::any::TypeId; + assert_eq!( + TypeId::of::<::RuntimeCall>(), + TypeId::of::<::RuntimeCall>() + ); + } + } +} + +pub mod runtime_with_specific_runtime_call { + use super::pallet_with_specific_runtime_call; + use frame::{runtime::prelude::*, testing_prelude::*}; + + construct_runtime!( + pub struct Runtime { + System: frame_system, + PalletWithSpecificRuntimeCall: pallet_with_specific_runtime_call, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + } + + #[docify::export(pallet_with_specific_runtime_call_impl)] + impl pallet_with_specific_runtime_call::Config for Runtime { + // an implementation of `IsSubType` is provided by `construct_runtime`. + type RuntimeCall = RuntimeCall; + } +} diff --git a/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs b/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs new file mode 100644 index 0000000000000000000000000000000000000000..7d870b432218e9e9a06c25f4f19e1f07ca72c6c6 --- /dev/null +++ b/docs/sdk/src/reference_docs/frame_runtime_upgrades_and_migrations.rs @@ -0,0 +1,138 @@ +//! # Runtime Upgrades +//! +//! At their core, blockchain logic consists of +//! +//! 1. on-chain state and +//! 2. a state transition function +//! +//! In Substrate-based blockchains, state transition functions are referred to as +//! [runtimes](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/blockchain_state_machines/index.html). +//! +//! Traditionally, before Substrate, upgrading state transition functions required node +//! operators to download new software and restart their nodes in a process called +//! [forking](https://en.wikipedia.org/wiki/Fork_(blockchain)). +//! +//! Substrate-based blockchains do not require forking, and instead upgrade runtimes +//! in a process called "Runtime Upgrades". +//! +//! Forkless runtime upgrades are a defining feature of the Substrate framework. Updating the +//! runtime logic without forking the code base enables your blockchain to seemlessly evolve +//! over time in a deterministic, rules-based manner. It also removes ambiguity for node operators +//! and other participants in the network about what is the canonical runtime. +//! +//! This capability is possible due to the runtime of a blockchain existing in on-chain storage. +//! +//! ## Performing a Runtime Upgrade +//! +//! To upgrade a runtime, an [`Origin`](frame_system::RawOrigin) with the necesarry permissions +//! (usually via governance) changes the `:code` storage. Usually, this is performed via a call to +//! [`set_code`] (or [`set_code_without_checks`]) with the desired new runtime blob, scheduled +//! using [`pallet_scheduler`]. +//! +//! Prior to building the new runtime, don't forget to update the +//! [`RuntimeVersion`](sp_version::RuntimeVersion). +//! +//! # Migrations +//! +//! It is often desirable to define logic to execute immediately after runtime upgrades (see +//! [this diagram](frame::traits::Hooks)). +//! +//! Self-contained pieces of logic that execute after a runtime upgrade are called "Migrations". +//! +//! The typical use case of a migration is to 'migrate' pallet storage from one layout to another, +//! for example when the encoding of a storage item is changed. However, they can also execute +//! arbitary logic such as: +//! +//! - Calling arbitrary pallet methods +//! - Mutating arbitrary on-chain state +//! - Cleaning up some old storage items that are no longer needed +//! +//! ## Single Block Migrations +//! +//! - Execute immediately and entirely at the beginning of the block following +//! a runtime upgrade. +//! - Are suitable for migrations which are guaranteed to not exceed the block weight. +//! - Are simply implementations of [`OnRuntimeUpgrade`]. +//! +//! To learn best practices for writing single block pallet storage migrations, see the +//! [Single Block Migration Example Pallet](pallet_example_single_block_migrations). +//! +//! ### Scheduling the Single Block Migrations to Run Next Runtime Upgrade +//! +//! Schedule migrations to run next runtime upgrade passing them as a generic parameter to your +//! [`Executive`](frame_executive) pallet: +//! +//! ```ignore +//! /// Tuple of migrations (structs that implement `OnRuntimeUpgrade`) +//! type Migrations = ( +//! pallet_example_storage_migration::migrations::v1::versioned::MigrateV0ToV1, +//! MyCustomMigration, +//! // ...more migrations here +//! ); +//! pub type Executive = frame_executive::Executive< +//! Runtime, +//! Block, +//! frame_system::ChainContext, +//! Runtime, +//! AllPalletsWithSystem, +//! Migrations, // <-- pass your migrations to Executive here +//! >; +//! ``` +//! +//! ### Ensuring Single Block Migration Safety +//! +//! "My migration unit tests pass, so it should be safe to deploy right?" +//! +//! No! Unit tests execute the migration in a very simple test environment, and cannot account +//! for the complexities of a real runtime or real on-chain state. +//! +//! Prior to deploying migrations, it is critical to perform additional checks to ensure that when +//! run in our real runtime they will not brick the chain due to: +//! - Panicing +//! - Touching too many storage keys and resulting in an excessively large PoV +//! - Taking too long to execute +//! +//! [`try-runtime-cli`](https://github.com/paritytech/try-runtime-cli) has a sub-command +//! [`on-runtime-upgrade`](https://paritytech.github.io/try-runtime-cli/try_runtime_core/commands/enum.Action.html#variant.OnRuntimeUpgrade) +//! which is designed to help with exactly this. +//! +//! Developers MUST run this command before deploying migrations to ensure they will not +//! inadvertently result in a bricked chain. +//! +//! It is recommended to run as part of your CI pipeline. See the +//! [polkadot-sdk check-runtime-migration job](https://github.com/paritytech/polkadot-sdk/blob/4a293bc5a25be637c06ce950a34490706597615b/.gitlab/pipeline/check.yml#L103-L124) +//! for an example of how to configure this. +//! +//! ### Note on the Manipulability of PoV Size and Execution Time +//! +//! While [`try-runtime-cli`](https://github.com/paritytech/try-runtime-cli) can help ensure with +//! very high certainty that a migration will succeed given **existing** on-chain state, it cannot +//! prevent a malicious actor from manipulating state in a way that will cause the migration to take +//! longer or produce a PoV much larger than previously measured. +//! +//! Therefore, it is important to write migrations in such a way that the execution time or PoV size +//! it adds to the block cannot be easily manipulated. e.g., do not iterate over storage that can +//! quickly or cheaply be bloated. +//! +//! If writing your migration in such a way is not possible, a multi block migration should be used +//! instead. +//! +//! ### Other useful tools +//! +//! [`Chopsticks`](https://github.com/AcalaNetwork/chopsticks) is another tool in the Substrate +//! ecosystem which developers may find useful to use in addition to `try-runtime-cli` when testing +//! their single block migrations. +//! +//! ## Multi Block Migrations +//! +//! Safely and easily execute long-running migrations across multiple blocks. +//! +//! Suitable for migrations which could use arbitrary amounts of block weight. +//! +//! TODO: Link to multi block migration example/s once PR is merged (). +//! +//! [`GetStorageVersion`]: frame_support::traits::GetStorageVersion +//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade +//! [`StorageVersion`]: frame_support::traits::StorageVersion +//! [`set_code`]: frame_system::Call::set_code +//! [`set_code_without_checks`]: frame_system::Call::set_code_without_checks diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs index c16122ee4287b17614f5d61acc9f26df0429c2cd..f4b52208e2fd6c37248b01352fc17cbd41792a02 100644 --- a/docs/sdk/src/reference_docs/mod.rs +++ b/docs/sdk/src/reference_docs/mod.rs @@ -39,20 +39,20 @@ pub mod runtime_vs_smart_contract; /// Learn about how extrinsics are encoded to be transmitted to a node and stored in blocks. pub mod extrinsic_encoding; -/// Learn about the signed extensions that form a part of extrinsics. +/// Learn about the transaction extensions that form a part of extrinsics. // TODO: @jsdw https://github.com/paritytech/polkadot-sdk-docs/issues/42 -pub mod signed_extensions; +pub mod transaction_extensions; -/// Learn about *"Origin"* A topic in FRAME that enables complex account abstractions to be built. -// TODO: @shawntabrizi https://github.com/paritytech/polkadot-sdk-docs/issues/43 +/// Learn about *Origins*, a topic in FRAME that enables complex account abstractions to be built. pub mod frame_origin; /// Learn about how to write safe and defensive code in your FRAME runtime. // TODO: @CrackTheCode016 https://github.com/paritytech/polkadot-sdk-docs/issues/44 pub mod safe_defensive_programming; -/// Learn about composite enums in FRAME-based runtimes, such as "RuntimeEvent" and "RuntimeCall". -pub mod frame_composite_enums; +/// Learn about composite enums and other runtime level types, such as "RuntimeEvent" and +/// "RuntimeCall". +pub mod frame_runtime_types; /// Learn about how to make a pallet/runtime that is fee-less and instead uses another mechanism to /// control usage and sybil attacks. @@ -92,11 +92,18 @@ pub mod cli; // TODO: @JoshOrndorff @kianenigma https://github.com/paritytech/polkadot-sdk-docs/issues/54 pub mod consensus_swapping; -/// Learn about all the advance ways to test your coordinate a rutnime upgrade and data migration. -// TODO: @liamaharon https://github.com/paritytech/polkadot-sdk-docs/issues/55 -pub mod frame_runtime_migration; +/// Learn about Runtime Upgrades and best practices for writing Migrations. +pub mod frame_runtime_upgrades_and_migrations; /// Learn about light nodes, how they function, and how Substrate-based chains come /// light-node-first out of the box. // TODO: @jsdw @josepot https://github.com/paritytech/polkadot-sdk-docs/issues/68 pub mod light_nodes; + +/// Learn about the offchain workers, how they function, and how to use them, as provided by the +/// [`frame`] APIs. +pub mod frame_offchain_workers; + +/// Learn about the different ways through which multiple [`frame`] pallets can be combined to work +/// together. +pub mod frame_pallet_coupling; diff --git a/docs/sdk/src/reference_docs/signed_extensions.rs b/docs/sdk/src/reference_docs/signed_extensions.rs deleted file mode 100644 index 28b1426536bcdf371865db74a104a27d51869c86..0000000000000000000000000000000000000000 --- a/docs/sdk/src/reference_docs/signed_extensions.rs +++ /dev/null @@ -1,79 +0,0 @@ -//! Signed extensions are, briefly, a means for different chains to extend the "basic" extrinsic -//! format with custom data that can be checked by the runtime. -//! -//! # Example -//! -//! Defining a couple of very simple signed extensions looks like the following: -#![doc = docify::embed!("./src/reference_docs/signed_extensions.rs", signed_extensions_example)] - -#[docify::export] -pub mod signed_extensions_example { - use parity_scale_codec::{Decode, Encode}; - use scale_info::TypeInfo; - use sp_runtime::traits::SignedExtension; - - // This doesn't actually check anything, but simply allows - // some arbitrary `u32` to be added to the extrinsic payload - #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] - pub struct AddToPayload(pub u32); - - impl SignedExtension for AddToPayload { - const IDENTIFIER: &'static str = "AddToPayload"; - type AccountId = (); - type Call = (); - type AdditionalSigned = (); - type Pre = (); - - fn additional_signed( - &self, - ) -> Result< - Self::AdditionalSigned, - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(()) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } - } - - // This is the opposite; nothing will be added to the extrinsic payload, - // but the AdditionalSigned type (`1234u32`) will be added to the - // payload to be signed. - #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] - pub struct AddToSignaturePayload; - - impl SignedExtension for AddToSignaturePayload { - const IDENTIFIER: &'static str = "AddToSignaturePayload"; - type AccountId = (); - type Call = (); - type AdditionalSigned = u32; - type Pre = (); - - fn additional_signed( - &self, - ) -> Result< - Self::AdditionalSigned, - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(1234) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } - } -} diff --git a/docs/sdk/src/reference_docs/transaction_extensions.rs b/docs/sdk/src/reference_docs/transaction_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..4f96d665d9bd2745de7b9911bd06cb059c62e9b4 --- /dev/null +++ b/docs/sdk/src/reference_docs/transaction_extensions.rs @@ -0,0 +1,57 @@ +//! Transaction extensions are, briefly, a means for different chains to extend the "basic" +//! extrinsic format with custom data that can be checked by the runtime. +//! +//! # Example +//! +//! Defining a couple of very simple transaction extensions looks like the following: +#![doc = docify::embed!("./src/reference_docs/transaction_extensions.rs", transaction_extensions_example)] + +#[docify::export] +pub mod transaction_extensions_example { + use parity_scale_codec::{Decode, Encode}; + use scale_info::TypeInfo; + use sp_runtime::{ + impl_tx_ext_default, + traits::{Dispatchable, TransactionExtension, TransactionExtensionBase}, + TransactionValidityError, + }; + + // This doesn't actually check anything, but simply allows + // some arbitrary `u32` to be added to the extrinsic payload + #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] + pub struct AddToPayload(pub u32); + + impl TransactionExtensionBase for AddToPayload { + const IDENTIFIER: &'static str = "AddToPayload"; + type Implicit = (); + } + + impl TransactionExtension for AddToPayload { + type Pre = (); + type Val = (); + + impl_tx_ext_default!(Call; (); validate prepare); + } + + // This is the opposite; nothing will be added to the extrinsic payload, + // but the Implicit type (`1234u32`) will be added to the + // payload to be signed. + #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] + pub struct AddToSignaturePayload; + + impl TransactionExtensionBase for AddToSignaturePayload { + const IDENTIFIER: &'static str = "AddToSignaturePayload"; + type Implicit = u32; + + fn implicit(&self) -> Result { + Ok(1234) + } + } + + impl TransactionExtension for AddToSignaturePayload { + type Pre = (); + type Val = (); + + impl_tx_ext_default!(Call; (); validate prepare); + } +} diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml index 8c8e9cebd41028402874849247b1b22dceafcea4..b0d71a18eaa13f8e7d188ca6420effa9dde4bb6b 100644 --- a/polkadot/Cargo.toml +++ b/polkadot/Cargo.toml @@ -18,7 +18,7 @@ rust-version = "1.64.0" readme = "README.md" authors.workspace = true edition.workspace = true -version = "1.6.0" +version = "6.0.0" default-run = "polkadot" [lints] diff --git a/polkadot/cli/Cargo.toml b/polkadot/cli/Cargo.toml index 2efb057ca28e8d43417a3ec919e3afe8dd434526..b9232f95981bf66427ffc67cc98282efb8cf330b 100644 --- a/polkadot/cli/Cargo.toml +++ b/polkadot/cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-cli" description = "Polkadot Relay-chain Client Node" -version = "1.1.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -19,9 +19,9 @@ crate-type = ["cdylib", "rlib"] [dependencies] cfg-if = "1.0" -clap = { version = "4.4.18", features = ["derive"], optional = true } -log = "0.4.17" -thiserror = "1.0.48" +clap = { version = "4.5.1", features = ["derive"], optional = true } +log = { workspace = true, default-features = true } +thiserror = { workspace = true } futures = "0.3.21" pyro = { package = "pyroscope", version = "0.5.3", optional = true } pyroscope_pprofrs = { version = "0.2", optional = true } @@ -42,6 +42,7 @@ sc-tracing = { path = "../../substrate/client/tracing", optional = true } sc-sysinfo = { path = "../../substrate/client/sysinfo" } sc-executor = { path = "../../substrate/client/executor" } sc-storage-monitor = { path = "../../substrate/client/storage-monitor" } +sp-runtime = { path = "../../substrate/primitives/runtime" } [build-dependencies] substrate-build-script-utils = { path = "../../substrate/utils/build-script-utils" } @@ -63,9 +64,14 @@ runtime-benchmarks = [ "polkadot-node-metrics/runtime-benchmarks", "sc-service?/runtime-benchmarks", "service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] full-node = ["service/full-node"] -try-runtime = ["service/try-runtime", "try-runtime-cli/try-runtime"] +try-runtime = [ + "service/try-runtime", + "sp-runtime/try-runtime", + "try-runtime-cli/try-runtime", +] fast-runtime = ["service/fast-runtime"] pyroscope = ["pyro", "pyroscope_pprofrs"] diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index 1e25f6533f04433209e71136558453910b0a24f7..f71891ecde34c0caea9f0b0372f2eb7a1d648e7a 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -91,6 +91,7 @@ impl SubstrateCli for Cli { "polkadot" => Box::new(service::chain_spec::polkadot_config()?), name if name.starts_with("polkadot-") && !name.ends_with(".json") => Err(format!("`{name}` is not supported anymore as the polkadot native runtime no longer part of the node."))?, + "paseo" => Box::new(service::chain_spec::paseo_config()?), "rococo" => Box::new(service::chain_spec::rococo_config()?), #[cfg(feature = "rococo-native")] "dev" | "rococo-dev" => Box::new(service::chain_spec::rococo_development_config()?), @@ -299,7 +300,7 @@ pub fn run() -> Result<()> { match &cli.subcommand { None => run_node_inner( cli, - service::RealOverseerGen, + service::ValidatorOverseerGen, None, polkadot_node_metrics::logger_hook(), ), @@ -450,7 +451,7 @@ pub fn run() -> Result<()> { if cfg!(feature = "runtime-benchmarks") { runner.sync_run(|config| { - cmd.run::(config) + cmd.run::, ()>(config) .map_err(|e| Error::SubstrateCli(e)) }) } else { diff --git a/polkadot/cli/src/lib.rs b/polkadot/cli/src/lib.rs index 35a467146b428e60462ef36f86d65651b03c40ed..4bb0dfb7583543937380d21da249bc612b842aba 100644 --- a/polkadot/cli/src/lib.rs +++ b/polkadot/cli/src/lib.rs @@ -29,7 +29,7 @@ mod error; pub use service::{self, Block, CoreApi, IdentifyVariant, ProvideRuntimeApi, TFullClient}; #[cfg(feature = "malus")] -pub use service::overseer::prepared_overseer_builder; +pub use service::overseer::validator_overseer_builder; #[cfg(feature = "cli")] pub use cli::*; diff --git a/polkadot/core-primitives/Cargo.toml b/polkadot/core-primitives/Cargo.toml index 32ee8d3ff3fbf149b452c7801edbeff876b400aa..d3aef89cb74d01cfe3293c500d981a38b09b6a8d 100644 --- a/polkadot/core-primitives/Cargo.toml +++ b/polkadot/core-primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-core-primitives" -version = "1.0.0" +version = "7.0.0" description = "Core Polkadot types used by Relay Chains and parachains." authors.workspace = true edition.workspace = true diff --git a/polkadot/doc/testing.md b/polkadot/doc/testing.md deleted file mode 100644 index 76703b1b4398a0cac8423183a5d2febfabab0e6c..0000000000000000000000000000000000000000 --- a/polkadot/doc/testing.md +++ /dev/null @@ -1,302 +0,0 @@ -# Testing - -Testing is an essential tool to assure correctness. This document describes how we test the Polkadot code, whether -locally, at scale, and/or automatically in CI. - -## Scopes - -The testing strategy for Polkadot is 4-fold: - -### Unit testing (1) - -Boring, small scale correctness tests of individual functions. It is usually -enough to run `cargo test` in the crate you are testing. - -For full coverage you may have to pass some additional features. For example: - -```sh -cargo test --features ci-only-tests -``` - -### Integration tests - -There are the following variants of integration tests: - -#### Subsystem tests (2) - -One particular subsystem (subsystem under test) interacts with a mocked overseer that is made to assert incoming and -outgoing messages of the subsystem under test. See e.g. the `statement-distribution` tests. - -#### Behavior tests (3) - -Launching small scale networks, with multiple adversarial nodes. This should include tests around the thresholds in -order to evaluate the error handling once certain assumed invariants fail. - -Currently, we commonly use **zombienet** to run mini test-networks, whether locally or in CI. To run on your machine: - -- First, make sure you have [zombienet][zombienet] installed. - -- Now, all the required binaries must be installed in your $PATH. You must run the following from the `polkadot/` -directory in order to test your changes. (Not `zombienet setup`, or you will get the released binaries without your -local changes!) - -```sh -cargo install --path . --locked -``` - -- You will also need to install whatever binaries are required for your specific tests. For example, to install -`undying-collator`, from `polkadot/`, run: - -```sh -cargo install --path ./parachain/test-parachains/undying/collator --locked -``` - -- Finally, run the zombienet test from the `polkadot` directory: - -```sh -RUST_LOG=parachain::pvf=trace zombienet --provider=native spawn zombienet_tests/functional/0001-parachains-pvf.toml -``` - -- You can pick a validator node like `alice` from the output and view its logs -(`tail -f `) or metrics. Make sure there is nothing funny in the logs -(try `grep WARN `). - -#### Testing at scale (4) - -Launching many nodes with configurable network speed and node features in a cluster of nodes. At this scale the -[Simnet][simnet] comes into play which launches a full cluster of nodes. The scale is handled by spawning a kubernetes -cluster and the meta description is covered by [Gurke][Gurke]. Asserts are made using Grafana rules, based on the -existing prometheus metrics. This can be extended by adding an additional service translating `jaeger` spans into -addition prometheus avoiding additional Polkadot source changes. - -_Behavior tests_ and _testing at scale_ have naturally soft boundary. The most significant difference is the presence of -a real network and the number of nodes, since a single host often not capable to run multiple nodes at once. - -## Observing Logs - -To verify expected behavior it's often useful to observe logs. To avoid too many -logs at once, you can run one test at a time: - -1. Add `sp_tracing::try_init_simple();` to the beginning of a test -2. Specify `RUST_LOG=::=trace` before the cargo command. - -For example: - -```sh -RUST_LOG=parachain::pvf=trace cargo test execute_can_run_serially -``` - -For more info on how our logs work, check [the docs][logs]. - -## Coverage - -Coverage gives a _hint_ of the actually covered source lines by tests and test applications. - -The state of the art is currently tarpaulin which unfortunately yields a lot of false negatives. Lines that -are in fact covered, marked as uncovered due to a mere linebreak in a statement can cause these artifacts. This leads to -lower coverage percentages than there actually is. - -Since late 2020 rust has gained [MIR based coverage tooling]( -https://blog.rust-lang.org/inside-rust/2020/11/12/source-based-code-coverage.html). - -```sh -# setup -rustup component add llvm-tools-preview -cargo install grcov miniserve - -export CARGO_INCREMENTAL=0 -# wasm is not happy with the instrumentation -export SKIP_BUILD_WASM=true -export BUILD_DUMMY_WASM_BINARY=true -# the actully collected coverage data -export LLVM_PROFILE_FILE="llvmcoveragedata-%p-%m.profraw" -# build wasm without instrumentation -export WASM_TARGET_DIRECTORY=/tmp/wasm -cargo +nightly build -# required rust flags -export RUSTFLAGS="-Zinstrument-coverage" -# assure target dir is clean -rm -r target/{debug,tests} -# run tests to get coverage data -cargo +nightly test --all - -# create the *html* report out of all the test binaries -# mostly useful for local inspection -grcov . --binary-path ./target/debug -s . -t html --branch --ignore-not-existing -o ./coverage/ -miniserve -r ./coverage - -# create a *codecov* compatible report -grcov . --binary-path ./target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore "/*" -o lcov.info -``` - -The test coverage in `lcov` can the be published to . - -```sh -bash <(curl -s https://codecov.io/bash) -f lcov.info -``` - -or just printed as part of the PR using a github action i.e. -[`jest-lcov-reporter`](https://github.com/marketplace/actions/jest-lcov-reporter). - -For full examples on how to use [`grcov` /w Polkadot specifics see the github -repo](https://github.com/mozilla/grcov#coverallscodecov-output). - -## Fuzzing - -Fuzzing is an approach to verify correctness against arbitrary or partially structured inputs. - -Currently implemented fuzzing targets: - -- `erasure-coding` - -The tooling of choice here is `honggfuzz-rs` as it allows _fastest_ coverage according to "some paper" which is a -positive feature when run as part of PRs. - -Fuzzing is generally not applicable for data secured by cryptographic hashes or signatures. Either the input has to be -specifically crafted, such that the discarded input percentage stays in an acceptable range. System level fuzzing is -hence simply not feasible due to the amount of state that is required. - -Other candidates to implement fuzzing are: - -- `rpc` -- ... - -## Performance metrics - -There are various ways of performance metrics. - -- timing with `criterion` -- cache hits/misses w/ `iai` harness or `criterion-perf` -- `coz` a performance based compiler - -Most of them are standard tools to aid in the creation of statistical tests regarding change in time of certain unit -tests. - -`coz` is meant for runtime. In our case, the system is far too large to yield a sufficient number of measurements in -finite time. An alternative approach could be to record incoming package streams per subsystem and store dumps of them, -which in return could be replayed repeatedly at an accelerated speed, with which enough metrics could be obtained to -yield information on which areas would improve the metrics. This unfortunately will not yield much information, since -most if not all of the subsystem code is linear based on the input to generate one or multiple output messages, it is -unlikely to get any useful metrics without mocking a sufficiently large part of the other subsystem which overlaps with -[#Integration tests] which is unfortunately not repeatable as of now. As such the effort gain seems low and this is not -pursued at the current time. - -## Writing small scope integration tests with preconfigured workers - -Requirements: - -- spawn nodes with preconfigured behaviors -- allow multiple types of configuration to be specified -- allow extendability via external crates -- ... - ---- - -## Implementation of different behavior strain nodes - -### Goals - -The main goals are is to allow creating a test node which exhibits a certain behavior by utilizing a subset of _wrapped_ -or _replaced_ subsystems easily. The runtime must not matter at all for these tests and should be simplistic. The -execution must be fast, this mostly means to assure a close to zero network latency as well as shorting the block time -and epoch times down to a few `100ms` and a few dozend blocks per epoch. - -### Approach - -#### MVP - -A simple small scale builder pattern would suffice for stage one implementation of allowing to replace individual -subsystems. An alternative would be to harness the existing `AllSubsystems` type and replace the subsystems as needed. - -#### Full `proc-macro` implementation - -`Overseer` is a common pattern. It could be extracted as `proc` macro and generative `proc-macro`. This would replace -the `AllSubsystems` type as well as implicitly create the `AllMessages` enum as `AllSubsystemsGen` does today. - -The implementation is yet to be completed, see the [implementation PR](https://github.com/paritytech/polkadot/pull/2962) -for details. - -##### Declare an overseer implementation - -```rust -struct BehaveMaleficient; - -impl OverseerGen for BehaveMaleficient { - fn generate<'a, Spawner, RuntimeClient>( - &self, - args: OverseerGenArgs<'a, Spawner, RuntimeClient>, - ) -> Result<(Overseer>, OverseerHandler), Error> - where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, - Spawner: 'static + overseer::gen::Spawner + Clone + Unpin, - { - let spawner = args.spawner.clone(); - let leaves = args.leaves.clone(); - let runtime_client = args.runtime_client.clone(); - let registry = args.registry.clone(); - let candidate_validation_config = args.candidate_validation_config.clone(); - // modify the subsystem(s) as needed: - let all_subsystems = create_default_subsystems(args)?. - // or spawn an entirely new set - - replace_candidate_validation( - // create the filtered subsystem - FilteredSubsystem::new( - CandidateValidationSubsystem::with_config( - candidate_validation_config, - Metrics::register(registry)?, - ), - // an implementation of - Skippy::default(), - ), - ); - - Overseer::new(leaves, all_subsystems, registry, runtime_client, spawner) - .map_err(|e| e.into()) - - // A builder pattern will simplify this further - // WIP https://github.com/paritytech/polkadot/pull/2962 - } -} - -fn main() -> eyre::Result<()> { - color_eyre::install()?; - let cli = Cli::from_args(); - assert_matches::assert_matches!(cli.subcommand, None); - polkadot_cli::run_node(cli, BehaveMaleficient)?; - Ok(()) -} -``` - -[`variant-a`](../node/malus/src/variant-a.rs) is a fully working example. - -#### Simnet - -Spawn a kubernetes cluster based on a meta description using [Gurke] with the [Simnet] scripts. - -Coordinated attacks of multiple nodes or subsystems must be made possible via a side-channel, that is out of scope for -this document. - -The individual node configurations are done as targets with a particular builder configuration. - -#### Behavior tests w/o Simnet - -Commonly this will require multiple nodes, and most machines are limited to running two or three nodes concurrently. -Hence, this is not the common case and is just an implementation _idea_. - -```rust -behavior_testcase!{ -"TestRuntime" => -"Alice": , -"Bob": , -"Charles": Default, -"David": "Charles", -"Eve": "Bob", -} -``` - -[zombienet]: https://github.com/paritytech/zombienet -[Gurke]: https://github.com/paritytech/gurke -[simnet]: https://github.com/paritytech/simnet_scripts -[logs]: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/node/gum/src/lib.rs diff --git a/polkadot/erasure-coding/Cargo.toml b/polkadot/erasure-coding/Cargo.toml index f174f8ad0cf4b82e2dd48d4d9d602c6a024c4275..677f15c4b9a1446c7828c78f92933f7811733ee8 100644 --- a/polkadot/erasure-coding/Cargo.toml +++ b/polkadot/erasure-coding/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-erasure-coding" -version = "1.0.0" +version = "7.0.0" description = "Erasure coding used for Polkadot's availability system" authors.workspace = true edition.workspace = true @@ -12,11 +12,11 @@ workspace = true [dependencies] polkadot-primitives = { path = "../primitives" } polkadot-node-primitives = { package = "polkadot-node-primitives", path = "../node/primitives" } -novelpoly = { package = "reed-solomon-novelpoly", version = "1.0.0" } +novelpoly = { package = "reed-solomon-novelpoly", version = "2.0.0" } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "std"] } sp-core = { path = "../../substrate/primitives/core" } sp-trie = { path = "../../substrate/primitives/trie" } -thiserror = "1.0.48" +thiserror = { workspace = true } [dev-dependencies] criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] } diff --git a/polkadot/erasure-coding/src/lib.rs b/polkadot/erasure-coding/src/lib.rs index 36847b46371533833c7de5a36e7c9e858ec1116c..e5155df4beba95aa9e7944e3e9a67d4e55a1db9c 100644 --- a/polkadot/erasure-coding/src/lib.rs +++ b/polkadot/erasure-coding/src/lib.rs @@ -83,6 +83,20 @@ pub enum Error { UnknownCodeParam, } +impl From for Error { + fn from(error: novelpoly::Error) -> Self { + match error { + novelpoly::Error::NeedMoreShards { .. } => Self::NotEnoughChunks, + novelpoly::Error::ParamterMustBePowerOf2 { .. } => Self::UnevenLength, + novelpoly::Error::WantedShardCountTooHigh(_) => Self::TooManyValidators, + novelpoly::Error::WantedShardCountTooLow(_) => Self::NotEnoughValidators, + novelpoly::Error::PayloadSizeIsZero { .. } => Self::BadPayload, + novelpoly::Error::InconsistentShardLengths { .. } => Self::NonUniformChunks, + _ => Self::UnknownReconstruction, + } + } +} + /// Obtain a threshold of chunks that should be enough to recover the data. pub const fn recovery_threshold(n_validators: usize) -> Result { if n_validators > MAX_VALIDATORS { @@ -166,42 +180,17 @@ where { let params = code_params(n_validators)?; let mut received_shards: Vec> = vec![None; n_validators]; - let mut shard_len = None; for (chunk_data, chunk_idx) in chunks.into_iter().take(n_validators) { - if chunk_idx >= n_validators { - return Err(Error::ChunkIndexOutOfBounds { chunk_index: chunk_idx, n_validators }) - } - - let shard_len = shard_len.get_or_insert_with(|| chunk_data.len()); - - if *shard_len % 2 != 0 { + if chunk_data.len() % 2 != 0 { return Err(Error::UnevenLength) } - if *shard_len != chunk_data.len() || *shard_len == 0 { - return Err(Error::NonUniformChunks) - } - received_shards[chunk_idx] = Some(WrappedShard::new(chunk_data.to_vec())); } - let res = params.make_encoder().reconstruct(received_shards); - - let payload_bytes = match res { - Err(e) => match e { - novelpoly::Error::NeedMoreShards { .. } => return Err(Error::NotEnoughChunks), - novelpoly::Error::ParamterMustBePowerOf2 { .. } => return Err(Error::UnevenLength), - novelpoly::Error::WantedShardCountTooHigh(_) => return Err(Error::TooManyValidators), - novelpoly::Error::WantedShardCountTooLow(_) => return Err(Error::NotEnoughValidators), - novelpoly::Error::PayloadSizeIsZero { .. } => return Err(Error::BadPayload), - novelpoly::Error::InconsistentShardLengths { .. } => - return Err(Error::NonUniformChunks), - _ => return Err(Error::UnknownReconstruction), - }, - Ok(payload_bytes) => payload_bytes, - }; - - Decode::decode(&mut &payload_bytes[..]).or_else(|_e| Err(Error::BadPayload)) + let payload_bytes = params.make_encoder().reconstruct(received_shards)?; + + Decode::decode(&mut &payload_bytes[..]).map_err(|_| Error::BadPayload) } /// An iterator that yields merkle branches and chunk data for all chunks to @@ -294,56 +283,6 @@ pub fn branch_hash(root: &H256, branch_nodes: &Proof, index: usize) -> Result

{ - remaining_len: usize, - shards: I, - cur_shard: Option<(&'a [u8], usize)>, -} - -impl<'a, I: Iterator> parity_scale_codec::Input for ShardInput<'a, I> { - fn remaining_len(&mut self) -> Result, parity_scale_codec::Error> { - Ok(Some(self.remaining_len)) - } - - fn read(&mut self, into: &mut [u8]) -> Result<(), parity_scale_codec::Error> { - let mut read_bytes = 0; - - loop { - if read_bytes == into.len() { - break - } - - let cur_shard = self.cur_shard.take().or_else(|| self.shards.next().map(|s| (s, 0))); - let (active_shard, mut in_shard) = match cur_shard { - Some((s, i)) => (s, i), - None => break, - }; - - if in_shard >= active_shard.len() { - continue - } - - let remaining_len_out = into.len() - read_bytes; - let remaining_len_shard = active_shard.len() - in_shard; - - let write_len = std::cmp::min(remaining_len_out, remaining_len_shard); - into[read_bytes..][..write_len].copy_from_slice(&active_shard[in_shard..][..write_len]); - - in_shard += write_len; - read_bytes += write_len; - self.cur_shard = Some((active_shard, in_shard)) - } - - self.remaining_len -= read_bytes; - if read_bytes == into.len() { - Ok(()) - } else { - Err("slice provided too big for input".into()) - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/polkadot/node/collation-generation/Cargo.toml b/polkadot/node/collation-generation/Cargo.toml index 366c08a6c6705dedf27d212b284b8b848769d0eb..8df0c2b1edae47cad98881e7243eb1dc68449e9e 100644 --- a/polkadot/node/collation-generation/Cargo.toml +++ b/polkadot/node/collation-generation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-collation-generation" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -19,7 +19,7 @@ polkadot-node-subsystem-util = { path = "../subsystem-util" } polkadot-primitives = { path = "../../primitives" } sp-core = { path = "../../../substrate/primitives/core" } sp-maybe-compressed-blob = { path = "../../../substrate/primitives/maybe-compressed-blob" } -thiserror = "1.0.48" +thiserror = { workspace = true } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } [dev-dependencies] diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index 0be0cfdea25fcc7fa5d1dca38c31636646e5ec8f..2a5b6198b9a827788696bdf5e1eeeb5829a9da6d 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-approval-voting" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -20,7 +20,7 @@ merlin = "3.0" schnorrkel = "0.11.4" kvdb = "0.13.0" derive_more = "0.99.17" -thiserror = "1.0.48" +thiserror = { workspace = true } itertools = "0.10.5" polkadot-node-subsystem = { path = "../../subsystem" } @@ -35,7 +35,7 @@ sp-consensus = { path = "../../../../substrate/primitives/consensus/common", def sp-consensus-slots = { path = "../../../../substrate/primitives/consensus/slots", default-features = false } sp-application-crypto = { path = "../../../../substrate/primitives/application-crypto", default-features = false, features = ["full_crypto"] } sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } -# should match schnorrkel +# rand_core should match schnorrkel rand_core = "0.6.2" rand_chacha = { version = "0.3.1" } rand = "0.8.5" @@ -51,5 +51,5 @@ polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } assert_matches = "1.4.0" kvdb-memorydb = "0.13.0" test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../../primitives/test-helpers" } -log = "0.4.17" +log = { workspace = true, default-features = true } env_logger = "0.9.0" diff --git a/polkadot/node/core/approval-voting/src/criteria.rs b/polkadot/node/core/approval-voting/src/criteria.rs index 1af61e72d7affa81744d8fcdb48ed5b1e80ae4d6..1ebea2641b62198c291951448c8c552d9cfb34cf 100644 --- a/polkadot/node/core/approval-voting/src/criteria.rs +++ b/polkadot/node/core/approval-voting/src/criteria.rs @@ -55,11 +55,11 @@ pub struct OurAssignment { } impl OurAssignment { - pub(crate) fn cert(&self) -> &AssignmentCertV2 { + pub fn cert(&self) -> &AssignmentCertV2 { &self.cert } - pub(crate) fn tranche(&self) -> DelayTranche { + pub fn tranche(&self) -> DelayTranche { self.tranche } @@ -225,7 +225,7 @@ fn assigned_core_transcript(core_index: CoreIndex) -> Transcript { /// Information about the world assignments are being produced in. #[derive(Clone, Debug)] -pub(crate) struct Config { +pub struct Config { /// The assignment public keys for validators. assignment_keys: Vec, /// The groups of validators assigned to each core. @@ -321,7 +321,7 @@ impl AssignmentCriteria for RealAssignmentCriteria { /// different times. The idea is that most assignments are never triggered and fall by the wayside. /// /// This will not assign to anything the local validator was part of the backing group for. -pub(crate) fn compute_assignments( +pub fn compute_assignments( keystore: &LocalKeystore, relay_vrf_story: RelayVRFStory, config: &Config, diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index 9116725c2911e5ae60222f3781f215816284d585..8cc16a6e1ec199776003204e8d9c870fb0f62114 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -92,11 +92,11 @@ use time::{slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClo mod approval_checking; pub mod approval_db; mod backend; -mod criteria; +pub mod criteria; mod import; mod ops; mod persisted_entries; -mod time; +pub mod time; use crate::{ approval_checking::{Check, TranchesToApproveResult}, @@ -159,6 +159,7 @@ pub struct ApprovalVotingSubsystem { db: Arc, mode: Mode, metrics: Metrics, + clock: Box, } #[derive(Clone)] @@ -444,6 +445,25 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, + ) -> Self { + ApprovalVotingSubsystem::with_config_and_clock( + config, + db, + keystore, + sync_oracle, + metrics, + Box::new(SystemClock {}), + ) + } + + /// Create a new approval voting subsystem with the given keystore, config, and database. + pub fn with_config_and_clock( + config: Config, + db: Arc, + keystore: Arc, + sync_oracle: Box, + metrics: Metrics, + clock: Box, ) -> Self { ApprovalVotingSubsystem { keystore, @@ -452,6 +472,7 @@ impl ApprovalVotingSubsystem { db_config: DatabaseConfig { col_approval_data: config.col_approval_data }, mode: Mode::Syncing(sync_oracle), metrics, + clock, } } @@ -493,15 +514,10 @@ fn db_sanity_check(db: Arc, config: DatabaseConfig) -> SubsystemRe impl ApprovalVotingSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let backend = DbBackend::new(self.db.clone(), self.db_config); - let future = run::( - ctx, - self, - Box::new(SystemClock), - Box::new(RealAssignmentCriteria), - backend, - ) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) - .boxed(); + let future = + run::(ctx, self, Box::new(RealAssignmentCriteria), backend) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) + .boxed(); SpawnedSubsystem { name: "approval-voting-subsystem", future } } @@ -909,7 +925,6 @@ enum Action { async fn run( mut ctx: Context, mut subsystem: ApprovalVotingSubsystem, - clock: Box, assignment_criteria: Box, mut backend: B, ) -> SubsystemResult<()> @@ -923,7 +938,7 @@ where let mut state = State { keystore: subsystem.keystore, slot_duration_millis: subsystem.slot_duration_millis, - clock, + clock: subsystem.clock, assignment_criteria, spans: HashMap::new(), }; @@ -1238,13 +1253,20 @@ async fn handle_actions( Action::BecomeActive => { *mode = Mode::Active; - let messages = distribution_messages_for_activation( + let (messages, next_actions) = distribution_messages_for_activation( + ctx, overlayed_db, state, delayed_approvals_timers, - )?; + session_info_provider, + ) + .await?; ctx.send_messages(messages.into_iter()).await; + let next_actions: Vec = + next_actions.into_iter().map(|v| v.clone()).chain(actions_iter).collect(); + + actions_iter = next_actions.into_iter(); }, Action::Conclude => { conclude = true; @@ -1298,15 +1320,19 @@ fn get_assignment_core_indices( } } -fn distribution_messages_for_activation( +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn distribution_messages_for_activation( + ctx: &mut Context, db: &OverlayedBackend<'_, impl Backend>, state: &State, delayed_approvals_timers: &mut DelayedApprovalTimer, -) -> SubsystemResult> { + session_info_provider: &mut RuntimeInfo, +) -> SubsystemResult<(Vec, Vec)> { let all_blocks: Vec = db.load_all_blocks()?; let mut approval_meta = Vec::with_capacity(all_blocks.len()); let mut messages = Vec::new(); + let mut actions = Vec::new(); messages.push(ApprovalDistributionMessage::NewBlocks(Vec::new())); // dummy value. @@ -1381,16 +1407,60 @@ fn distribution_messages_for_activation( &claimed_core_indices, &block_entry, ) { - Ok(bitfield) => messages.push( - ApprovalDistributionMessage::DistributeAssignment( - IndirectAssignmentCertV2 { - block_hash, - validator: assignment.validator_index(), - cert: assignment.cert().clone(), - }, - bitfield, - ), - ), + Ok(bitfield) => { + gum::debug!( + target: LOG_TARGET, + candidate_hash = ?candidate_entry.candidate_receipt().hash(), + ?block_hash, + "Discovered, triggered assignment, not approved yet", + ); + + let indirect_cert = IndirectAssignmentCertV2 { + block_hash, + validator: assignment.validator_index(), + cert: assignment.cert().clone(), + }; + messages.push( + ApprovalDistributionMessage::DistributeAssignment( + indirect_cert.clone(), + bitfield.clone(), + ), + ); + + if !block_entry + .candidate_is_pending_signature(*candidate_hash) + { + let ExtendedSessionInfo { ref executor_params, .. } = + match get_extended_session_info( + session_info_provider, + ctx.sender(), + block_entry.block_hash(), + block_entry.session(), + ) + .await + { + Some(i) => i, + None => continue, + }; + + actions.push(Action::LaunchApproval { + claimed_candidate_indices: bitfield, + candidate_hash: candidate_entry + .candidate_receipt() + .hash(), + indirect_cert, + assignment_tranche: assignment.tranche(), + relay_block_hash: block_hash, + session: block_entry.session(), + executor_params: executor_params.clone(), + candidate: candidate_entry + .candidate_receipt() + .clone(), + backing_group: approval_entry.backing_group(), + distribute_assignment: false, + }); + } + }, Err(err) => { // Should never happen. If we fail here it means the // assignment is null (no cores claimed). @@ -1481,7 +1551,7 @@ fn distribution_messages_for_activation( } messages[0] = ApprovalDistributionMessage::NewBlocks(approval_meta); - Ok(messages) + Ok((messages, actions)) } // Handle an incoming signal from the overseer. Returns true if execution should conclude. @@ -2583,7 +2653,7 @@ where _ => {}, } - gum::debug!( + gum::trace!( target: LOG_TARGET, validator_index = approval.validator.0, candidate_hash = ?approved_candidate_hash, @@ -2700,7 +2770,7 @@ where let is_approved = check.is_approved(tick_now.saturating_sub(APPROVAL_DELAY)); if status.last_no_shows != 0 { metrics.on_observed_no_shows(status.last_no_shows); - gum::debug!( + gum::trace!( target: LOG_TARGET, ?candidate_hash, ?block_hash, diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index ef47bdb2213a153dc7223c1018ba8ec9b341a5aa..b924a1b52ccf49a242adecfd534b498004ec3d87 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -588,6 +588,13 @@ impl BlockEntry { !self.candidates_pending_signature.is_empty() } + /// Returns true if candidate hash is in the queue for a signature. + pub fn candidate_is_pending_signature(&self, candidate_hash: CandidateHash) -> bool { + self.candidates_pending_signature + .values() + .any(|context| context.candidate_hash == candidate_hash) + } + /// Candidate hashes for candidates pending signatures fn candidate_hashes_pending_signature(&self) -> Vec { self.candidates_pending_signature diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index 7a0bde6a55e28135036dbc9b8ee8137a0485aa28..c9053232a4c8752f03134047dbc64b020377740a 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -78,6 +78,7 @@ struct TestSyncOracle { struct TestSyncOracleHandle { done_syncing_receiver: oneshot::Receiver<()>, + is_major_syncing: Arc, } impl TestSyncOracleHandle { @@ -108,8 +109,9 @@ impl SyncOracle for TestSyncOracle { fn make_sync_oracle(val: bool) -> (Box, TestSyncOracleHandle) { let (tx, rx) = oneshot::channel(); let flag = Arc::new(AtomicBool::new(val)); - let oracle = TestSyncOracle { flag, done_syncing_sender: Arc::new(Mutex::new(Some(tx))) }; - let handle = TestSyncOracleHandle { done_syncing_receiver: rx }; + let oracle = + TestSyncOracle { flag: flag.clone(), done_syncing_sender: Arc::new(Mutex::new(Some(tx))) }; + let handle = TestSyncOracleHandle { done_syncing_receiver: rx, is_major_syncing: flag }; (Box::new(oracle), handle) } @@ -465,6 +467,7 @@ struct HarnessConfigBuilder { clock: Option, backend: Option, assignment_criteria: Option>, + major_syncing: bool, } impl HarnessConfigBuilder { @@ -476,9 +479,19 @@ impl HarnessConfigBuilder { self } + pub fn major_syncing(&mut self, value: bool) -> &mut Self { + self.major_syncing = value; + self + } + + pub fn backend(&mut self, store: TestStore) -> &mut Self { + self.backend = Some(store); + self + } + pub fn build(&mut self) -> HarnessConfig { let (sync_oracle, sync_oracle_handle) = - self.sync_oracle.take().unwrap_or_else(|| make_sync_oracle(false)); + self.sync_oracle.take().unwrap_or_else(|| make_sync_oracle(self.major_syncing)); let assignment_criteria = self .assignment_criteria @@ -549,7 +562,7 @@ fn test_harness>( let subsystem = run( context, - ApprovalVotingSubsystem::with_config( + ApprovalVotingSubsystem::with_config_and_clock( Config { col_approval_data: test_constants::TEST_CONFIG.col_approval_data, slot_duration_millis: SLOT_DURATION_MILLIS, @@ -558,8 +571,8 @@ fn test_harness>( Arc::new(keystore), sync_oracle, Metrics::default(), + clock.clone(), ), - clock.clone(), assignment_criteria, backend, ); @@ -736,11 +749,13 @@ struct BlockConfig { slot: Slot, candidates: Option>, session_info: Option, + end_syncing: bool, } struct ChainBuilder { blocks_by_hash: HashMap, blocks_at_height: BTreeMap>, + is_major_syncing: Arc, } impl ChainBuilder { @@ -748,16 +763,28 @@ impl ChainBuilder { const GENESIS_PARENT_HASH: Hash = Hash::repeat_byte(0x00); pub fn new() -> Self { - let mut builder = - Self { blocks_by_hash: HashMap::new(), blocks_at_height: BTreeMap::new() }; + let mut builder = Self { + blocks_by_hash: HashMap::new(), + blocks_at_height: BTreeMap::new(), + is_major_syncing: Arc::new(AtomicBool::new(false)), + }; builder.add_block_inner( Self::GENESIS_HASH, Self::GENESIS_PARENT_HASH, 0, - BlockConfig { slot: Slot::from(0), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(0), + candidates: None, + session_info: None, + end_syncing: false, + }, ); builder } + pub fn major_syncing(&mut self, major_syncing: Arc) -> &mut Self { + self.is_major_syncing = major_syncing; + self + } pub fn add_block( &mut self, @@ -808,8 +835,16 @@ impl ChainBuilder { } ancestry.reverse(); - import_block(overseer, ancestry.as_ref(), *number, block_config, false, i > 0) - .await; + import_block( + overseer, + ancestry.as_ref(), + *number, + block_config, + false, + i > 0, + self.is_major_syncing.clone(), + ) + .await; let _: Option<()> = future::pending().timeout(Duration::from_millis(100)).await; } } @@ -863,6 +898,7 @@ async fn import_block( config: &BlockConfig, gap: bool, fork: bool, + major_syncing: Arc, ) { let (new_head, new_header) = &hashes[hashes.len() - 1]; let candidates = config.candidates.clone().unwrap_or(vec![( @@ -891,6 +927,12 @@ async fn import_block( h_tx.send(Ok(Some(new_header.clone()))).unwrap(); } ); + + let is_major_syncing = major_syncing.load(Ordering::SeqCst); + if config.end_syncing { + major_syncing.store(false, Ordering::SeqCst); + } + if !fork { let mut _ancestry_step = 0; if gap { @@ -931,7 +973,7 @@ async fn import_block( } } - if number > 0 { + if number > 0 && !is_major_syncing { assert_matches!( overseer_recv(overseer).await, AllMessages::RuntimeApi( @@ -944,7 +986,6 @@ async fn import_block( c_tx.send(Ok(inclusion_events)).unwrap(); } ); - assert_matches!( overseer_recv(overseer).await, AllMessages::RuntimeApi( @@ -984,14 +1025,14 @@ async fn import_block( ); } - if number == 0 { + if number == 0 && !is_major_syncing { assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NewBlocks(v)) => { assert_eq!(v.len(), 0usize); } ); - } else { + } else if number > 0 && !is_major_syncing { if !fork { // SessionInfo won't be called for forks - it's already cached assert_matches!( @@ -1031,20 +1072,23 @@ async fn import_block( ); } - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalDistribution( - ApprovalDistributionMessage::NewBlocks(mut approval_vec) - ) => { - assert_eq!(approval_vec.len(), 1); - let metadata = approval_vec.pop().unwrap(); - let hash = &hashes[number as usize]; - let parent_hash = &hashes[(number - 1) as usize]; - assert_eq!(metadata.hash, hash.0.clone()); - assert_eq!(metadata.parent_hash, parent_hash.0.clone()); - assert_eq!(metadata.slot, config.slot); - } - ); + if !is_major_syncing { + assert_matches!( + overseer_recv(overseer).await, + + AllMessages::ApprovalDistribution( + ApprovalDistributionMessage::NewBlocks(mut approval_vec) + ) => { + assert_eq!(approval_vec.len(), 1); + let metadata = approval_vec.pop().unwrap(); + let hash = &hashes[number as usize]; + let parent_hash = &hashes[(number - 1) as usize]; + assert_eq!(metadata.hash, hash.0.clone()); + assert_eq!(metadata.parent_hash, parent_hash.0.clone()); + assert_eq!(metadata.slot, config.slot); + } + ); + } } } @@ -1072,7 +1116,7 @@ fn subsystem_rejects_bad_assignment_ok_criteria() { block_hash, head, 1, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); builder.build(&mut virtual_overseer).await; @@ -1135,7 +1179,7 @@ fn subsystem_rejects_bad_assignment_err_criteria() { block_hash, head, 1, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); builder.build(&mut virtual_overseer).await; @@ -1240,6 +1284,7 @@ fn subsystem_rejects_approval_if_no_candidate_entry() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(1), GroupIndex(1))]), session_info: None, + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -1345,7 +1390,12 @@ fn subsystem_rejects_approval_before_assignment() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1398,7 +1448,12 @@ fn subsystem_rejects_assignment_in_future() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(0), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(0), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1472,6 +1527,7 @@ fn subsystem_accepts_duplicate_assignment() { (candidate_receipt2, CoreIndex(1), GroupIndex(1)), ]), session_info: None, + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -1537,7 +1593,12 @@ fn subsystem_rejects_assignment_with_unknown_candidate() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1582,7 +1643,12 @@ fn subsystem_rejects_oversized_bitfields() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1650,7 +1716,12 @@ fn subsystem_accepts_and_imports_approval_after_assignment() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1741,6 +1812,7 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { slot: Slot::from(0), candidates: None, session_info: Some(session_info), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -1824,7 +1896,12 @@ fn subsystem_assignment_import_updates_candidate_entry_and_schedules_wakeup() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1873,7 +1950,12 @@ fn subsystem_process_wakeup_schedules_wakeup() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: None, + session_info: None, + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -1925,7 +2007,7 @@ fn linear_import_act_on_leaf() { hash, head, i, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); head = hash; } @@ -1983,7 +2065,7 @@ fn forkful_import_at_same_height_act_on_leaf() { hash, head, i, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); head = hash; } @@ -1997,7 +2079,7 @@ fn forkful_import_at_same_height_act_on_leaf() { hash, head, session, - BlockConfig { slot, candidates: None, session_info: None }, + BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, ); } builder.build(&mut virtual_overseer).await; @@ -2168,6 +2250,7 @@ fn import_checked_approval_updates_entries_and_schedules() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(0), GroupIndex(0))]), session_info: Some(session_info), + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -2323,6 +2406,7 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { (candidate_receipt2, CoreIndex(1), GroupIndex(1)), ]), session_info: Some(session_info), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -2445,6 +2529,7 @@ fn approved_ancestor_test( slot: Slot::from(i as u64), candidates: Some(vec![(candidate_receipt, CoreIndex(0), GroupIndex(0))]), session_info: None, + end_syncing: false, }, ); } @@ -2623,13 +2708,19 @@ fn subsystem_validate_approvals_cache() { slot, candidates: candidates.clone(), session_info: Some(session_info.clone()), + end_syncing: false, }, ) .add_block( fork_block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot, candidates, session_info: Some(session_info) }, + BlockConfig { + slot, + candidates, + session_info: Some(session_info), + end_syncing: false, + }, ) .build(&mut virtual_overseer) .await; @@ -2740,6 +2831,7 @@ fn subsystem_doesnt_distribute_duplicate_compact_assignments() { (candidate_receipt2.clone(), CoreIndex(1), GroupIndex(1)), ]), session_info: None, + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -2997,6 +3089,7 @@ where slot, candidates: Some(vec![(candidate_receipt, CoreIndex(0), GroupIndex(2))]), session_info: Some(session_info), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -3314,6 +3407,7 @@ fn pre_covers_dont_stall_approval() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(0), GroupIndex(0))]), session_info: Some(session_info), + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -3491,6 +3585,7 @@ fn waits_until_approving_assignments_are_old_enough() { slot, candidates: Some(vec![(candidate_descriptor, CoreIndex(0), GroupIndex(0))]), session_info: Some(session_info), + end_syncing: false, }, ); builder.build(&mut virtual_overseer).await; @@ -3705,6 +3800,7 @@ fn test_approval_is_sent_on_max_approval_coalesce_count() { slot, candidates: candidates.clone(), session_info: Some(session_info.clone()), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -3943,8 +4039,7 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { let store = config.backend(); test_harness(config, |test_harness| async move { - let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _sync_oracle_handle } = - test_harness; + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _ } = test_harness; assert_matches!( overseer_recv(&mut virtual_overseer).await, @@ -4006,6 +4101,7 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { slot, candidates: candidates.clone(), session_info: Some(session_info.clone()), + end_syncing: false, }, ) .build(&mut virtual_overseer) @@ -4040,3 +4136,569 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { virtual_overseer }); } + +// Builds a chain with a fork where both relay blocks include the same candidate. +async fn build_chain_with_two_blocks_with_one_candidate_each( + block_hash1: Hash, + block_hash2: Hash, + slot: Slot, + sync_oracle_handle: TestSyncOracleHandle, + candidate_receipt: CandidateReceipt, +) -> (ChainBuilder, SessionInfo) { + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Eve, + ]; + let session_info = SessionInfo { + validator_groups: IndexedVec::>::from(vec![ + vec![ValidatorIndex(0), ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3), ValidatorIndex(4)], + ]), + ..session_info(&validators) + }; + + let candidates = Some(vec![(candidate_receipt.clone(), CoreIndex(0), GroupIndex(0))]); + let mut chain_builder = ChainBuilder::new(); + + chain_builder + .major_syncing(sync_oracle_handle.is_major_syncing.clone()) + .add_block( + block_hash1, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot, + candidates: candidates.clone(), + session_info: Some(session_info.clone()), + end_syncing: false, + }, + ) + .add_block( + block_hash2, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot, + candidates, + session_info: Some(session_info.clone()), + end_syncing: true, + }, + ); + (chain_builder, session_info) +} + +async fn setup_overseer_with_two_blocks_each_with_one_assignment_triggered( + virtual_overseer: &mut VirtualOverseer, + store: TestStore, + clock: &Box, + sync_oracle_handle: TestSyncOracleHandle, +) { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + let fork_block_hash = Hash::repeat_byte(0x02); + let candidate_commitments = CandidateCommitments::default(); + let mut candidate_receipt = dummy_candidate_receipt(block_hash); + candidate_receipt.commitments_hash = candidate_commitments.hash(); + let candidate_hash = candidate_receipt.hash(); + let slot = Slot::from(1); + let (chain_builder, _session_info) = build_chain_with_two_blocks_with_one_candidate_each( + block_hash, + fork_block_hash, + slot, + sync_oracle_handle, + candidate_receipt, + ) + .await; + chain_builder.build(virtual_overseer).await; + + assert!(!clock.inner.lock().current_wakeup_is(1)); + clock.inner.lock().wakeup_all(1); + + assert!(clock.inner.lock().current_wakeup_is(slot_to_tick(slot))); + clock.inner.lock().wakeup_all(slot_to_tick(slot)); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + clock.inner.lock().wakeup_all(slot_to_tick(slot + 2)); + + assert_eq!(clock.inner.lock().wakeups.len(), 0); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + let candidate_entry = store.load_candidate_entry(&candidate_hash).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); +} + +// Tests that for candidates that we did not approve yet, for which we triggered the assignment and +// the approval work we restart the work to approve it. +#[test] +fn subsystem_relaunches_approval_work_on_restart() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + assignments + }, + |_| Ok(0), + )); + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + let store_clone = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness; + + setup_overseer_with_two_blocks_each_with_one_assignment_triggered( + &mut virtual_overseer, + store, + &clock, + sync_oracle_handle, + ) + .await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _ + )) => { + } + ); + + // Bail early after the assignment has been distributed but before we answer with the mocked + // approval from CandidateValidation. + virtual_overseer + }); + + // Restart a new approval voting subsystem with the same database and major syncing true untill + // the last leaf. + let config = HarnessConfigBuilder::default().backend(store_clone).major_syncing(true).build(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, 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 fork_block_hash = Hash::repeat_byte(0x02); + let candidate_commitments = CandidateCommitments::default(); + let mut candidate_receipt = dummy_candidate_receipt(block_hash); + candidate_receipt.commitments_hash = candidate_commitments.hash(); + let slot = Slot::from(1); + clock.inner.lock().set_tick(slot_to_tick(slot + 2)); + let (chain_builder, session_info) = build_chain_with_two_blocks_with_one_candidate_each( + block_hash, + fork_block_hash, + slot, + sync_oracle_handle, + candidate_receipt, + ) + .await; + + chain_builder.build(&mut virtual_overseer).await; + + futures_timer::Delay::new(Duration::from_millis(2000)).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionInfo(_, si_tx), + ) + ) => { + si_tx.send(Ok(Some(session_info.clone()))).unwrap(); + } + ); + assert_matches!( + overseer_recv(&mut 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(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_, RuntimeApiRequest::NodeFeatures(_, si_tx), ) + ) => { + si_tx.send(Ok(NodeFeatures::EMPTY)).unwrap(); + } + ); + + // On major syncing ending Approval voting should send all the necessary messages for a + // candidate to be approved. + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NewBlocks( + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + // Guarantees the approval work has been relaunched. + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive { + exec_kind, + response_sender, + .. + }) if exec_kind == PvfExecKind::Approval => { + response_sender.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))) + .unwrap(); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + virtual_overseer + }); +} + +// Test that cached approvals, which are candidates that we approved but we didn't issue +// the signature yet because we want to coalesce it with more candidate are sent after restart. +#[test] +fn subsystem_sends_pending_approvals_on_approval_restart() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + assignments + }, + |_| Ok(0), + )); + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + let store_clone = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness; + + setup_overseer_with_two_blocks_each_with_one_assignment_triggered( + &mut virtual_overseer, + store, + &clock, + sync_oracle_handle, + ) + .await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _ + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive { + exec_kind, + response_sender, + .. + }) if exec_kind == PvfExecKind::Approval => { + response_sender.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))) + .unwrap(); + } + ); + + // Configure a big coalesce number, so that the signature is cached instead of being sent to + // approval-distribution. + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 6, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 6, + })); + } + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + virtual_overseer + }); + + let config = HarnessConfigBuilder::default().backend(store_clone).major_syncing(true).build(); + // On restart signatures should be sent to approval-distribution without relaunching the + // approval work. + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, 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 fork_block_hash = Hash::repeat_byte(0x02); + let candidate_commitments = CandidateCommitments::default(); + let mut candidate_receipt = dummy_candidate_receipt(block_hash); + candidate_receipt.commitments_hash = candidate_commitments.hash(); + let slot = Slot::from(1); + + clock.inner.lock().set_tick(slot_to_tick(slot + 2)); + let (chain_builder, session_info) = build_chain_with_two_blocks_with_one_candidate_each( + block_hash, + fork_block_hash, + slot, + sync_oracle_handle, + candidate_receipt, + ) + .await; + chain_builder.build(&mut virtual_overseer).await; + + futures_timer::Delay::new(Duration::from_millis(2000)).await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NewBlocks( + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + _, + )) => { + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionInfo(_, si_tx), + ) + ) => { + si_tx.send(Ok(Some(session_info.clone()))).unwrap(); + } + ); + assert_matches!( + overseer_recv(&mut 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(&mut virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_, RuntimeApiRequest::NodeFeatures(_, si_tx), ) + ) => { + si_tx.send(Ok(NodeFeatures::EMPTY)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + })); + } + ); + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + virtual_overseer + }); +} diff --git a/polkadot/node/core/approval-voting/src/time.rs b/polkadot/node/core/approval-voting/src/time.rs index 61091f3c34cdab4aed00c24bcbc8a40d6a77a116..99dfbe07678f2f0956565e92ffaf196b6f482af9 100644 --- a/polkadot/node/core/approval-voting/src/time.rs +++ b/polkadot/node/core/approval-voting/src/time.rs @@ -33,14 +33,14 @@ use std::{ }; use polkadot_primitives::{Hash, ValidatorIndex}; -const TICK_DURATION_MILLIS: u64 = 500; +pub const TICK_DURATION_MILLIS: u64 = 500; /// A base unit of time, starting from the Unix epoch, split into half-second intervals. -pub(crate) type Tick = u64; +pub type Tick = u64; /// A clock which allows querying of the current tick as well as /// waiting for a tick to be reached. -pub(crate) trait Clock { +pub trait Clock { /// Yields the current tick. fn tick_now(&self) -> Tick; @@ -49,7 +49,7 @@ pub(crate) trait Clock { } /// Extension methods for clocks. -pub(crate) trait ClockExt { +pub trait ClockExt { fn tranche_now(&self, slot_duration_millis: u64, base_slot: Slot) -> DelayTranche; } @@ -61,7 +61,8 @@ impl ClockExt for C { } /// A clock which uses the actual underlying system clock. -pub(crate) struct SystemClock; +#[derive(Clone)] +pub struct SystemClock; impl Clock for SystemClock { /// Yields the current tick. @@ -93,11 +94,22 @@ fn tick_to_time(tick: Tick) -> SystemTime { } /// assumes `slot_duration_millis` evenly divided by tick duration. -pub(crate) fn slot_number_to_tick(slot_duration_millis: u64, slot: Slot) -> Tick { +pub fn slot_number_to_tick(slot_duration_millis: u64, slot: Slot) -> Tick { let ticks_per_slot = slot_duration_millis / TICK_DURATION_MILLIS; u64::from(slot) * ticks_per_slot } +/// Converts a tick to the slot number. +pub fn tick_to_slot_number(slot_duration_millis: u64, tick: Tick) -> Slot { + let ticks_per_slot = slot_duration_millis / TICK_DURATION_MILLIS; + (tick / ticks_per_slot).into() +} + +/// Converts a tranche from a slot to the tick number. +pub fn tranche_to_tick(slot_duration_millis: u64, slot: Slot, tranche: u32) -> Tick { + slot_number_to_tick(slot_duration_millis, slot) + tranche as u64 +} + /// A list of delayed futures that gets triggered when the waiting time has expired and it is /// time to sign the candidate. /// We have a timer per relay-chain block. diff --git a/polkadot/node/core/av-store/Cargo.toml b/polkadot/node/core/av-store/Cargo.toml index e56fb4f1cdfb5d5ce6fe52cf9601629dc3dfed03..05212da7479848830a8fb70ee4c38d449e9a6a7e 100644 --- a/polkadot/node/core/av-store/Cargo.toml +++ b/polkadot/node/core/av-store/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-av-store" description = "The Availability Store subsystem. Wrapper over the DB that stores availability data and chunks." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -13,7 +13,7 @@ workspace = true futures = "0.3.21" futures-timer = "3.0.2" kvdb = "0.13.0" -thiserror = "1.0.48" +thiserror = { workspace = true } gum = { package = "tracing-gum", path = "../../gum" } bitvec = "1.0.0" @@ -28,7 +28,7 @@ sp-consensus = { path = "../../../../substrate/primitives/consensus/common", def polkadot-node-jaeger = { path = "../../jaeger" } [dev-dependencies] -log = "0.4.17" +log = { workspace = true, default-features = true } env_logger = "0.9.0" assert_matches = "1.4.0" kvdb-memorydb = "0.13.0" diff --git a/polkadot/node/core/backing/Cargo.toml b/polkadot/node/core/backing/Cargo.toml index 16ed11e7eec9a2aa7a97ad9a55585ebbef95c98c..d0c1f9aa4832dfe629e1fb20d3082f27c260a7d5 100644 --- a/polkadot/node/core/backing/Cargo.toml +++ b/polkadot/node/core/backing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-backing" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -20,8 +20,9 @@ erasure-coding = { package = "polkadot-erasure-coding", path = "../../../erasure statement-table = { package = "polkadot-statement-table", path = "../../../statement-table" } bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } gum = { package = "tracing-gum", path = "../../gum" } -thiserror = "1.0.48" +thiserror = { workspace = true } fatality = "0.0.6" +schnellru = "0.2.1" [dev-dependencies] sp-core = { path = "../../../../substrate/primitives/core" } @@ -31,5 +32,6 @@ sc-keystore = { path = "../../../../substrate/client/keystore" } sp-tracing = { path = "../../../../substrate/primitives/tracing" } futures = { version = "0.3.21", features = ["thread-pool"] } assert_matches = "1.4.0" +rstest = "0.18.2" polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../../primitives/test-helpers" } diff --git a/polkadot/node/core/backing/src/error.rs b/polkadot/node/core/backing/src/error.rs index 1b00a62510b7c634b3135a56ca6412c0948ef6a0..64955a393962076bd7b202dd31a7d69d46a4d1b9 100644 --- a/polkadot/node/core/backing/src/error.rs +++ b/polkadot/node/core/backing/src/error.rs @@ -48,6 +48,9 @@ pub enum Error { #[error("Candidate is not found")] CandidateNotFound, + #[error("CoreIndex cannot be determined for a candidate")] + CoreIndexUnavailable, + #[error("Signature is invalid")] InvalidSignature, diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 98bbd6232add4d893293fc948c0a99bba8d46bf3..69bf2e956a0f6bb7f3b9f4a19446e5e7d2c125bb 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -77,6 +77,7 @@ use futures::{ stream::FuturesOrdered, FutureExt, SinkExt, StreamExt, TryFutureExt, }; +use schnellru::{ByLength, LruMap}; use error::{Error, FatalResult}; use polkadot_node_primitives::{ @@ -104,10 +105,12 @@ use polkadot_node_subsystem_util::{ Validator, }; use polkadot_primitives::{ + vstaging::{node_features::FeatureIndex, NodeFeatures}, BackedCandidate, CandidateCommitments, CandidateHash, CandidateReceipt, - CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, Hash, Id as ParaId, - PersistedValidationData, PvfExecKind, SigningContext, ValidationCode, ValidatorId, - ValidatorIndex, ValidatorSignature, ValidityAttestation, + CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, GroupIndex, GroupRotationInfo, + Hash, Id as ParaId, IndexedVec, PersistedValidationData, PvfExecKind, SessionIndex, + SigningContext, ValidationCode, ValidatorId, ValidatorIndex, ValidatorSignature, + ValidityAttestation, }; use sp_keystore::KeystorePtr; use statement_table::{ @@ -118,7 +121,7 @@ use statement_table::{ }, Config as TableConfig, Context as TableContextTrait, Table, }; -use util::vstaging::get_disabled_validators_with_fallback; +use util::{runtime::request_node_features, vstaging::get_disabled_validators_with_fallback}; mod error; @@ -209,7 +212,9 @@ struct PerRelayParentState { /// The hash of the relay parent on top of which this job is doing it's work. parent: Hash, /// The `ParaId` assigned to the local validator at this relay parent. - assignment: Option, + assigned_para: Option, + /// The `CoreIndex` assigned to the local validator at this relay parent. + assigned_core: Option, /// The candidates that are backed by enough validators in their group, by hash. backed: HashSet, /// The table of candidates and statements under this relay-parent. @@ -224,6 +229,15 @@ struct PerRelayParentState { fallbacks: HashMap, /// The minimum backing votes threshold. minimum_backing_votes: u32, + /// If true, we're appending extra bits in the BackedCandidate validator indices bitfield, + /// which represent the assigned core index. True if ElasticScalingMVP is enabled. + inject_core_index: bool, + /// The core states for all cores. + cores: Vec, + /// The validator index -> group mapping at this relay parent. + validator_to_group: Arc>>, + /// The associated group rotation information. + group_rotation_info: GroupRotationInfo, } struct PerCandidateState { @@ -275,6 +289,9 @@ struct State { /// This is guaranteed to have an entry for each candidate with a relay parent in the implicit /// or explicit view for which a `Seconded` statement has been successfully imported. per_candidate: HashMap, + /// Cache the per-session Validator->Group mapping. + validator_to_group_cache: + LruMap>>>, /// A cloneable sender which is dispatched to background candidate validation tasks to inform /// the main task of the result. background_validation_tx: mpsc::Sender<(Hash, ValidatedCandidateCommand)>, @@ -292,6 +309,7 @@ impl State { per_leaf: HashMap::default(), per_relay_parent: HashMap::default(), per_candidate: HashMap::new(), + validator_to_group_cache: LruMap::new(ByLength::new(2)), background_validation_tx, keystore, } @@ -379,10 +397,10 @@ struct AttestingData { backing: Vec, } -#[derive(Default)] +#[derive(Default, Debug)] struct TableContext { validator: Option, - groups: HashMap>, + groups: HashMap>, validators: Vec, disabled_validators: Vec, } @@ -404,7 +422,7 @@ impl TableContext { impl TableContextTrait for TableContext { type AuthorityId = ValidatorIndex; type Digest = CandidateHash; - type GroupId = ParaId; + type GroupId = CoreIndex; type Signature = ValidatorSignature; type Candidate = CommittedCandidateReceipt; @@ -412,15 +430,11 @@ impl TableContextTrait for TableContext { candidate.hash() } - fn candidate_group(candidate: &CommittedCandidateReceipt) -> ParaId { - candidate.descriptor().para_id + fn is_member_of(&self, authority: &ValidatorIndex, core: &CoreIndex) -> bool { + self.groups.get(core).map_or(false, |g| g.iter().any(|a| a == authority)) } - fn is_member_of(&self, authority: &ValidatorIndex, group: &ParaId) -> bool { - self.groups.get(group).map_or(false, |g| g.iter().any(|a| a == authority)) - } - - fn get_group_size(&self, group: &ParaId) -> Option { + fn get_group_size(&self, group: &CoreIndex) -> Option { self.groups.get(group).map(|g| g.len()) } } @@ -442,19 +456,20 @@ fn primitive_statement_to_table(s: &SignedFullStatementWithPVD) -> TableSignedSt fn table_attested_to_backed( attested: TableAttestedCandidate< - ParaId, + CoreIndex, CommittedCandidateReceipt, ValidatorIndex, ValidatorSignature, >, table_context: &TableContext, + inject_core_index: bool, ) -> Option { - let TableAttestedCandidate { candidate, validity_votes, group_id: para_id } = attested; + let TableAttestedCandidate { candidate, validity_votes, group_id: core_index } = attested; let (ids, validity_votes): (Vec<_>, Vec) = validity_votes.into_iter().map(|(id, vote)| (id, vote.into())).unzip(); - let group = table_context.groups.get(¶_id)?; + let group = table_context.groups.get(&core_index)?; let mut validator_indices = BitVec::with_capacity(group.len()); @@ -479,14 +494,15 @@ fn table_attested_to_backed( } vote_positions.sort_by_key(|(_orig, pos_in_group)| *pos_in_group); - Some(BackedCandidate { + Some(BackedCandidate::new( candidate, - validity_votes: vote_positions + vote_positions .into_iter() .map(|(pos_in_votes, _pos_in_group)| validity_votes[pos_in_votes].clone()) .collect(), validator_indices, - }) + inject_core_index.then_some(core_index), + )) } async fn store_available_data( @@ -971,7 +987,14 @@ async fn handle_active_leaves_update( // construct a `PerRelayParent` from the runtime API // and insert it. - let per = construct_per_relay_parent_state(ctx, maybe_new, &state.keystore, mode).await?; + let per = construct_per_relay_parent_state( + ctx, + maybe_new, + &state.keystore, + &mut state.validator_to_group_cache, + mode, + ) + .await?; if let Some(per) = per { state.per_relay_parent.insert(maybe_new, per); @@ -981,31 +1004,112 @@ async fn handle_active_leaves_update( Ok(()) } +macro_rules! try_runtime_api { + ($x: expr) => { + match $x { + Ok(x) => x, + Err(err) => { + // Only bubble up fatal errors. + error::log_error(Err(Into::::into(err).into()))?; + + // We can't do candidate validation work if we don't have the + // requisite runtime API data. But these errors should not take + // down the node. + return Ok(None) + }, + } + }; +} + +fn core_index_from_statement( + validator_to_group: &IndexedVec>, + group_rotation_info: &GroupRotationInfo, + cores: &[CoreState], + statement: &SignedFullStatementWithPVD, +) -> Option { + let compact_statement = statement.as_unchecked(); + let candidate_hash = CandidateHash(*compact_statement.unchecked_payload().candidate_hash()); + + let n_cores = cores.len(); + + gum::trace!( + target:LOG_TARGET, + ?group_rotation_info, + ?statement, + ?validator_to_group, + n_cores = ?cores.len(), + ?candidate_hash, + "Extracting core index from statement" + ); + + let statement_validator_index = statement.validator_index(); + let Some(Some(group_index)) = validator_to_group.get(statement_validator_index) else { + gum::debug!( + target: LOG_TARGET, + ?group_rotation_info, + ?statement, + ?validator_to_group, + n_cores = ?cores.len() , + ?candidate_hash, + "Invalid validator index: {:?}", + statement_validator_index + ); + return None + }; + + // First check if the statement para id matches the core assignment. + let core_index = group_rotation_info.core_for_group(*group_index, n_cores); + + if core_index.0 as usize > n_cores { + gum::warn!(target: LOG_TARGET, ?candidate_hash, ?core_index, n_cores, "Invalid CoreIndex"); + return None + } + + if let StatementWithPVD::Seconded(candidate, _pvd) = statement.payload() { + let candidate_para_id = candidate.descriptor.para_id; + let assigned_para_id = match &cores[core_index.0 as usize] { + CoreState::Free => { + gum::debug!(target: LOG_TARGET, ?candidate_hash, "Invalid CoreIndex, core is not assigned to any para_id"); + return None + }, + CoreState::Occupied(occupied) => + if let Some(next) = &occupied.next_up_on_available { + next.para_id + } else { + return None + }, + CoreState::Scheduled(scheduled) => scheduled.para_id, + }; + + if assigned_para_id != candidate_para_id { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?core_index, + ?assigned_para_id, + ?candidate_para_id, + "Invalid CoreIndex, core is assigned to a different para_id" + ); + return None + } + return Some(core_index) + } else { + return Some(core_index) + } +} + /// Load the data necessary to do backing work on top of a relay-parent. #[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] async fn construct_per_relay_parent_state( ctx: &mut Context, relay_parent: Hash, keystore: &KeystorePtr, + validator_to_group_cache: &mut LruMap< + SessionIndex, + Arc>>, + >, mode: ProspectiveParachainsMode, ) -> Result, Error> { - macro_rules! try_runtime_api { - ($x: expr) => { - match $x { - Ok(x) => x, - Err(err) => { - // Only bubble up fatal errors. - error::log_error(Err(Into::::into(err).into()))?; - - // We can't do candidate validation work if we don't have the - // requisite runtime API data. But these errors should not take - // down the node. - return Ok(None) - }, - } - }; - } - let parent = relay_parent; let (session_index, validators, groups, cores) = futures::try_join!( @@ -1020,6 +1124,16 @@ async fn construct_per_relay_parent_state( .map_err(Error::JoinMultiple)?; let session_index = try_runtime_api!(session_index); + + let inject_core_index = request_node_features(parent, session_index, ctx.sender()) + .await? + .unwrap_or(NodeFeatures::EMPTY) + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|b| *b) + .unwrap_or(false); + + gum::debug!(target: LOG_TARGET, inject_core_index, ?parent, "New state"); + let validators: Vec<_> = try_runtime_api!(validators); let (validator_groups, group_rotation_info) = try_runtime_api!(groups); let cores = try_runtime_api!(cores); @@ -1055,18 +1169,24 @@ async fn construct_per_relay_parent_state( }, }; - let mut groups = HashMap::new(); let n_cores = cores.len(); - let mut assignment = None; - for (idx, core) in cores.into_iter().enumerate() { + let mut groups = HashMap::>::new(); + let mut assigned_core = None; + let mut assigned_para = None; + + for (idx, core) in cores.iter().enumerate() { let core_para_id = match core { CoreState::Scheduled(scheduled) => scheduled.para_id, CoreState::Occupied(occupied) => if mode.is_enabled() { // Async backing makes it legal to build on top of // occupied core. - occupied.candidate_descriptor.para_id + if let Some(next) = &occupied.next_up_on_available { + next.para_id + } else { + continue + } } else { continue }, @@ -1077,11 +1197,27 @@ async fn construct_per_relay_parent_state( let group_index = group_rotation_info.group_for_core(core_index, n_cores); if let Some(g) = validator_groups.get(group_index.0 as usize) { if validator.as_ref().map_or(false, |v| g.contains(&v.index())) { - assignment = Some(core_para_id); + assigned_para = Some(core_para_id); + assigned_core = Some(core_index); } - groups.insert(core_para_id, g.clone()); + groups.insert(core_index, g.clone()); } } + gum::debug!(target: LOG_TARGET, ?groups, "TableContext"); + + let validator_to_group = validator_to_group_cache + .get_or_insert(session_index, || { + let mut vector = vec![None; validators.len()]; + + for (group_idx, validator_group) in validator_groups.iter().enumerate() { + for validator in validator_group { + vector[validator.0 as usize] = Some(GroupIndex(group_idx as u32)); + } + } + + Arc::new(IndexedVec::<_, _>::from(vector)) + }) + .expect("Just inserted"); let table_context = TableContext { validator, groups, validators, disabled_validators }; let table_config = TableConfig { @@ -1094,7 +1230,8 @@ async fn construct_per_relay_parent_state( Ok(Some(PerRelayParentState { prospective_parachains_mode: mode, parent, - assignment, + assigned_core, + assigned_para, backed: HashSet::new(), table: Table::new(table_config), table_context, @@ -1102,6 +1239,10 @@ async fn construct_per_relay_parent_state( awaiting_validation: HashSet::new(), fallbacks: HashMap::new(), minimum_backing_votes, + inject_core_index, + cores, + validator_to_group: validator_to_group.clone(), + group_rotation_info, })) } @@ -1519,15 +1660,16 @@ async fn import_statement( per_candidate: &mut HashMap, statement: &SignedFullStatementWithPVD, ) -> Result, Error> { + let candidate_hash = statement.payload().candidate_hash(); + gum::debug!( target: LOG_TARGET, statement = ?statement.payload().to_compact(), validator_index = statement.validator_index().0, + ?candidate_hash, "Importing statement", ); - let candidate_hash = statement.payload().candidate_hash(); - // If this is a new candidate (statement is 'seconded' and candidate is unknown), // we need to create an entry in the `PerCandidateState` map. // @@ -1593,7 +1735,15 @@ async fn import_statement( let stmt = primitive_statement_to_table(statement); - Ok(rp_state.table.import_statement(&rp_state.table_context, stmt)) + let core = core_index_from_statement( + &rp_state.validator_to_group, + &rp_state.group_rotation_info, + &rp_state.cores, + statement, + ) + .ok_or(Error::CoreIndexUnavailable)?; + + Ok(rp_state.table.import_statement(&rp_state.table_context, core, stmt)) } /// Handles a summary received from [`import_statement`] and dispatches `Backed` notifications and @@ -1615,8 +1765,12 @@ async fn post_import_statement_actions( // `HashSet::insert` returns true if the thing wasn't in there already. if rp_state.backed.insert(candidate_hash) { - if let Some(backed) = table_attested_to_backed(attested, &rp_state.table_context) { - let para_id = backed.candidate.descriptor.para_id; + if let Some(backed) = table_attested_to_backed( + attested, + &rp_state.table_context, + rp_state.inject_core_index, + ) { + let para_id = backed.candidate().descriptor.para_id; gum::debug!( target: LOG_TARGET, candidate_hash = ?candidate_hash, @@ -1637,7 +1791,7 @@ async fn post_import_statement_actions( // notify collator protocol. ctx.send_message(CollatorProtocolMessage::Backed { para_id, - para_head: backed.candidate.descriptor.para_head, + para_head: backed.candidate().descriptor.para_head, }) .await; // Notify statement distribution of backed candidate. @@ -1654,8 +1808,14 @@ async fn post_import_statement_actions( ); ctx.send_unbounded_message(message); } + } else { + gum::debug!(target: LOG_TARGET, ?candidate_hash, "Cannot get BackedCandidate"); } + } else { + gum::debug!(target: LOG_TARGET, ?candidate_hash, "Candidate already known"); } + } else { + gum::debug!(target: LOG_TARGET, "No attested candidate"); } issue_new_misbehaviors(ctx, rp_state.parent, &mut rp_state.table); @@ -1722,18 +1882,20 @@ async fn background_validate_and_make_available( if rp_state.awaiting_validation.insert(candidate_hash) { // spawn background task. let bg = async move { - if let Err(e) = validate_and_make_available(params).await { - if let Error::BackgroundValidationMpsc(error) = e { + if let Err(error) = validate_and_make_available(params).await { + if let Error::BackgroundValidationMpsc(error) = error { gum::debug!( target: LOG_TARGET, + ?candidate_hash, ?error, "Mpsc background validation mpsc died during validation- leaf no longer active?" ); } else { gum::error!( target: LOG_TARGET, - "Failed to validate and make available: {:?}", - e + ?candidate_hash, + ?error, + "Failed to validate and make available", ); } } @@ -1859,9 +2021,10 @@ async fn maybe_validate_and_import( let candidate_hash = summary.candidate; - if Some(summary.group_id) != rp_state.assignment { + if Some(summary.group_id) != rp_state.assigned_core { return Ok(()) } + let attesting = match statement.payload() { StatementWithPVD::Seconded(receipt, _) => { let attesting = AttestingData { @@ -2004,10 +2167,11 @@ async fn handle_second_message( } // Sanity check that candidate is from our assignment. - if Some(candidate.descriptor().para_id) != rp_state.assignment { + if Some(candidate.descriptor().para_id) != rp_state.assigned_para { gum::debug!( target: LOG_TARGET, - our_assignment = ?rp_state.assignment, + our_assignment_core = ?rp_state.assigned_core, + our_assignment_para = ?rp_state.assigned_para, collation = ?candidate.descriptor().para_id, "Subsystem asked to second for para outside of our assignment", ); @@ -2015,6 +2179,14 @@ async fn handle_second_message( return Ok(()) } + gum::debug!( + target: LOG_TARGET, + our_assignment_core = ?rp_state.assigned_core, + our_assignment_para = ?rp_state.assigned_para, + collation = ?candidate.descriptor().para_id, + "Current assignments vs collation", + ); + // If the message is a `CandidateBackingMessage::Second`, sign and dispatch a // Seconded statement only if we have not signed a Valid statement for the requested candidate. // @@ -2087,7 +2259,13 @@ fn handle_get_backed_candidates_message( &rp_state.table_context, rp_state.minimum_backing_votes, ) - .and_then(|attested| table_attested_to_backed(attested, &rp_state.table_context)) + .and_then(|attested| { + table_attested_to_backed( + attested, + &rp_state.table_context, + rp_state.inject_core_index, + ) + }) }) .collect(); diff --git a/polkadot/node/core/backing/src/tests/mod.rs b/polkadot/node/core/backing/src/tests/mod.rs index 1957f4e19c54bd9845e5bb5bd03383985d9c9636..e3cc5727435a3fef3532a9de342cd194ecd4fe3a 100644 --- a/polkadot/node/core/backing/src/tests/mod.rs +++ b/polkadot/node/core/backing/src/tests/mod.rs @@ -33,9 +33,10 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - CandidateDescriptor, GroupRotationInfo, HeadData, PersistedValidationData, PvfExecKind, - ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, + vstaging::node_features, CandidateDescriptor, GroupRotationInfo, HeadData, + PersistedValidationData, PvfExecKind, ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, }; +use rstest::rstest; use sp_application_crypto::AppCrypto; use sp_keyring::Sr25519Keyring; use sp_keystore::Keystore; @@ -65,19 +66,21 @@ fn dummy_pvd() -> PersistedValidationData { } } -struct TestState { +pub(crate) struct TestState { chain_ids: Vec, keystore: KeystorePtr, validators: Vec, validator_public: Vec, validation_data: PersistedValidationData, validator_groups: (Vec>, GroupRotationInfo), + validator_to_group: IndexedVec>, availability_cores: Vec, head_data: HashMap, signing_context: SigningContext, relay_parent: Hash, minimum_backing_votes: u32, disabled_validators: Vec, + node_features: NodeFeatures, } impl TestState { @@ -114,6 +117,11 @@ impl Default for TestState { .into_iter() .map(|g| g.into_iter().map(ValidatorIndex).collect()) .collect(); + let validator_to_group: IndexedVec<_, _> = + vec![Some(0), Some(1), Some(0), Some(0), None, Some(0)] + .into_iter() + .map(|x| x.map(|x| GroupIndex(x))) + .collect(); let group_rotation_info = GroupRotationInfo { session_start_block: 0, group_rotation_frequency: 100, now: 1 }; @@ -143,6 +151,7 @@ impl Default for TestState { validators, validator_public, validator_groups: (validator_groups, group_rotation_info), + validator_to_group, availability_cores, head_data, validation_data, @@ -150,6 +159,7 @@ impl Default for TestState { relay_parent, minimum_backing_votes: LEGACY_MIN_BACKING_VOTES, disabled_validators: Vec::new(), + node_features: Default::default(), } } } @@ -285,6 +295,16 @@ async fn test_startup(virtual_overseer: &mut VirtualOverseer, test_state: &TestS } ); + // Node features request from runtime: all features are disabled. + assert_matches!( + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_parent, RuntimeApiRequest::NodeFeatures(_session_index, tx)) + ) => { + tx.send(Ok(test_state.node_features.clone())).unwrap(); + } + ); + // Check if subsystem job issues a request for the minimum backing votes. assert_matches!( virtual_overseer.recv().await, @@ -477,9 +497,20 @@ fn backing_second_works() { } // Test that the candidate reaches quorum successfully. -#[test] -fn backing_works() { - let test_state = TestState::default(); +#[rstest] +#[case(true)] +#[case(false)] +fn backing_works(#[case] elastic_scaling_mvp: bool) { + let mut test_state = TestState::default(); + if elastic_scaling_mvp { + test_state + .node_features + .resize((node_features::FeatureIndex::ElasticScalingMVP as u8 + 1) as usize, false); + test_state + .node_features + .set(node_features::FeatureIndex::ElasticScalingMVP as u8 as usize, true); + } + test_harness(test_state.keystore.clone(), |mut virtual_overseer| async move { test_startup(&mut virtual_overseer, &test_state).await; @@ -630,6 +661,31 @@ fn backing_works() { virtual_overseer.send(FromOrchestra::Communication { msg: statement }).await; + let (tx, rx) = oneshot::channel(); + let msg = CandidateBackingMessage::GetBackedCandidates( + vec![(candidate_a_hash, test_state.relay_parent)], + tx, + ); + + virtual_overseer.send(FromOrchestra::Communication { msg }).await; + + let candidates = rx.await.unwrap(); + assert_eq!(1, candidates.len()); + assert_eq!(candidates[0].validity_votes().len(), 3); + + let (validator_indices, maybe_core_index) = + candidates[0].validator_indices_and_core_index(elastic_scaling_mvp); + if elastic_scaling_mvp { + assert_eq!(maybe_core_index.unwrap(), CoreIndex(0)); + } else { + assert!(maybe_core_index.is_none()); + } + + assert_eq!( + validator_indices, + bitvec::bitvec![u8, bitvec::order::Lsb0; 1, 1, 0, 1].as_bitslice() + ); + virtual_overseer .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves( ActiveLeavesUpdate::stop_work(test_state.relay_parent), @@ -639,6 +695,107 @@ fn backing_works() { }); } +#[test] +fn extract_core_index_from_statement_works() { + let test_state = TestState::default(); + + let pov_a = PoV { block_data: BlockData(vec![42, 43, 44]) }; + let pvd_a = dummy_pvd(); + let validation_code_a = ValidationCode(vec![1, 2, 3]); + + let pov_hash = pov_a.hash(); + + let mut candidate = TestCandidateBuilder { + para_id: test_state.chain_ids[0], + relay_parent: test_state.relay_parent, + pov_hash, + erasure_root: make_erasure_root(&test_state, pov_a.clone(), pvd_a.clone()), + persisted_validation_data_hash: pvd_a.hash(), + validation_code: validation_code_a.0.clone(), + ..Default::default() + } + .build(); + + let public2 = Keystore::sr25519_generate_new( + &*test_state.keystore, + ValidatorId::ID, + Some(&test_state.validators[2].to_seed()), + ) + .expect("Insert key into keystore"); + + let signed_statement_1 = SignedFullStatementWithPVD::sign( + &test_state.keystore, + StatementWithPVD::Seconded(candidate.clone(), pvd_a.clone()), + &test_state.signing_context, + ValidatorIndex(2), + &public2.into(), + ) + .ok() + .flatten() + .expect("should be signed"); + + let public1 = Keystore::sr25519_generate_new( + &*test_state.keystore, + ValidatorId::ID, + Some(&test_state.validators[1].to_seed()), + ) + .expect("Insert key into keystore"); + + let signed_statement_2 = SignedFullStatementWithPVD::sign( + &test_state.keystore, + StatementWithPVD::Seconded(candidate.clone(), pvd_a.clone()), + &test_state.signing_context, + ValidatorIndex(1), + &public1.into(), + ) + .ok() + .flatten() + .expect("should be signed"); + + candidate.descriptor.para_id = test_state.chain_ids[1]; + + let signed_statement_3 = SignedFullStatementWithPVD::sign( + &test_state.keystore, + StatementWithPVD::Seconded(candidate, pvd_a.clone()), + &test_state.signing_context, + ValidatorIndex(1), + &public1.into(), + ) + .ok() + .flatten() + .expect("should be signed"); + + let core_index_1 = core_index_from_statement( + &test_state.validator_to_group, + &test_state.validator_groups.1, + &test_state.availability_cores, + &signed_statement_1, + ) + .unwrap(); + + assert_eq!(core_index_1, CoreIndex(0)); + + let core_index_2 = core_index_from_statement( + &test_state.validator_to_group, + &test_state.validator_groups.1, + &test_state.availability_cores, + &signed_statement_2, + ); + + // Must be none, para_id in descriptor is different than para assigned to core + assert_eq!(core_index_2, None); + + let core_index_3 = core_index_from_statement( + &test_state.validator_to_group, + &test_state.validator_groups.1, + &test_state.availability_cores, + &signed_statement_3, + ) + .unwrap(); + + assert_eq!(core_index_3, CoreIndex(1)); +} + #[test] fn backing_works_while_validation_ongoing() { let test_state = TestState::default(); @@ -801,20 +958,20 @@ fn backing_works_while_validation_ongoing() { let candidates = rx.await.unwrap(); assert_eq!(1, candidates.len()); - assert_eq!(candidates[0].validity_votes.len(), 3); + assert_eq!(candidates[0].validity_votes().len(), 3); assert!(candidates[0] - .validity_votes + .validity_votes() .contains(&ValidityAttestation::Implicit(signed_a.signature().clone()))); assert!(candidates[0] - .validity_votes + .validity_votes() .contains(&ValidityAttestation::Explicit(signed_b.signature().clone()))); assert!(candidates[0] - .validity_votes + .validity_votes() .contains(&ValidityAttestation::Explicit(signed_c.signature().clone()))); assert_eq!( - candidates[0].validator_indices, - bitvec::bitvec![u8, bitvec::order::Lsb0; 1, 0, 1, 1], + candidates[0].validator_indices_and_core_index(false), + (bitvec::bitvec![u8, bitvec::order::Lsb0; 1, 0, 1, 1].as_bitslice(), None) ); virtual_overseer @@ -1422,7 +1579,7 @@ fn backing_works_after_failed_validation() { fn candidate_backing_reorders_votes() { use sp_core::Encode; - let para_id = ParaId::from(10); + let core_idx = CoreIndex(10); let validators = vec![ Sr25519Keyring::Alice, Sr25519Keyring::Bob, @@ -1436,7 +1593,7 @@ fn candidate_backing_reorders_votes() { let validator_groups = { let mut validator_groups = HashMap::new(); validator_groups - .insert(para_id, vec![0, 1, 2, 3, 4, 5].into_iter().map(ValidatorIndex).collect()); + .insert(core_idx, vec![0, 1, 2, 3, 4, 5].into_iter().map(ValidatorIndex).collect()); validator_groups }; @@ -1466,10 +1623,10 @@ fn candidate_backing_reorders_votes() { (ValidatorIndex(3), fake_attestation(3)), (ValidatorIndex(1), fake_attestation(1)), ], - group_id: para_id, + group_id: core_idx, }; - let backed = table_attested_to_backed(attested, &table_context).unwrap(); + let backed = table_attested_to_backed(attested, &table_context, false).unwrap(); let expected_bitvec = { let mut validator_indices = BitVec::::with_capacity(6); @@ -1486,8 +1643,11 @@ fn candidate_backing_reorders_votes() { let expected_attestations = vec![fake_attestation(1).into(), fake_attestation(3).into(), fake_attestation(5).into()]; - assert_eq!(backed.validator_indices, expected_bitvec); - assert_eq!(backed.validity_votes, expected_attestations); + assert_eq!( + backed.validator_indices_and_core_index(false), + (expected_bitvec.as_bitslice(), None) + ); + assert_eq!(backed.validity_votes(), expected_attestations); } // Test whether we retry on failed PoV fetching. diff --git a/polkadot/node/core/backing/src/tests/prospective_parachains.rs b/polkadot/node/core/backing/src/tests/prospective_parachains.rs index 578f21bef66515e49042d7a11692de67b9642d41..94310d2aa164650db84b78ddf361a9f465ac207d 100644 --- a/polkadot/node/core/backing/src/tests/prospective_parachains.rs +++ b/polkadot/node/core/backing/src/tests/prospective_parachains.rs @@ -185,6 +185,16 @@ async fn activate_leaf( } ); + // Node features request from runtime: all features are disabled. + assert_matches!( + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::NodeFeatures(_session_index, tx)) + ) if parent == hash => { + tx.send(Ok(Default::default())).unwrap(); + } + ); + // Check if subsystem job issues a request for the minimum backing votes. assert_matches!( virtual_overseer.recv().await, @@ -305,10 +315,11 @@ async fn assert_hypothetical_frontier_requests( ) => { let idx = match expected_requests.iter().position(|r| r.0 == request) { Some(idx) => idx, - None => panic!( + None => + panic!( "unexpected hypothetical frontier request, no match found for {:?}", request - ), + ), }; let resp = std::mem::take(&mut expected_requests[idx].1); tx.send(resp).unwrap(); @@ -1268,6 +1279,7 @@ fn concurrent_dependent_candidates() { let statement_b = CandidateBackingMessage::Statement(leaf_parent, signed_b.clone()); virtual_overseer.send(FromOrchestra::Communication { msg: statement_a }).await; + // At this point the subsystem waits for response, the previous message is received, // send a second one without blocking. let _ = virtual_overseer @@ -1388,7 +1400,19 @@ fn concurrent_dependent_candidates() { assert_eq!(sess_idx, 1); tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); }, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _parent, + RuntimeApiRequest::ValidatorGroups(tx), + )) => { + tx.send(Ok(test_state.validator_groups.clone())).unwrap(); + }, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _parent, + RuntimeApiRequest::AvailabilityCores(tx), + )) => { + tx.send(Ok(test_state.availability_cores.clone())).unwrap(); + }, _ => panic!("unexpected message received from overseer: {:?}", msg), } } @@ -1419,7 +1443,6 @@ fn seconding_sanity_check_occupy_same_depth() { let leaf_parent = get_parent_hash(leaf_hash); let activated = new_leaf(leaf_hash, LEAF_BLOCK_NUMBER); - let min_block_number = LEAF_BLOCK_NUMBER - LEAF_ANCESTRY_LEN; let min_relay_parents = vec![(para_id_a, min_block_number), (para_id_b, min_block_number)]; let test_leaf_a = TestLeaf { activated, min_relay_parents }; @@ -1555,13 +1578,14 @@ fn occupied_core_assignment() { const LEAF_A_BLOCK_NUMBER: BlockNumber = 100; const LEAF_A_ANCESTRY_LEN: BlockNumber = 3; let para_id = test_state.chain_ids[0]; + let previous_para_id = test_state.chain_ids[1]; // Set the core state to occupied. let mut candidate_descriptor = ::test_helpers::dummy_candidate_descriptor(Hash::zero()); - candidate_descriptor.para_id = para_id; + candidate_descriptor.para_id = previous_para_id; test_state.availability_cores[0] = CoreState::Occupied(OccupiedCore { group_responsible: Default::default(), - next_up_on_available: None, + next_up_on_available: Some(ScheduledCore { para_id, collator: None }), occupied_since: 100_u32, time_out_at: 200_u32, next_up_on_time_out: None, diff --git a/polkadot/node/core/bitfield-signing/Cargo.toml b/polkadot/node/core/bitfield-signing/Cargo.toml index 880273c0e7f3cc4a30d979edaad0d57dbb6ac523..6ecfffd7249b9213cc4ffb86f84a38e1cdb9babf 100644 --- a/polkadot/node/core/bitfield-signing/Cargo.toml +++ b/polkadot/node/core/bitfield-signing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-bitfield-signing" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -17,7 +17,7 @@ polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } sp-keystore = { path = "../../../../substrate/primitives/keystore" } wasm-timer = "0.2.5" -thiserror = "1.0.48" +thiserror = { workspace = true } [dev-dependencies] polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } diff --git a/polkadot/node/core/candidate-validation/Cargo.toml b/polkadot/node/core/candidate-validation/Cargo.toml index 4f0ad67dbf1c84dd07d170c0ed00b7255ed275fa..15fc8c940d338c9aab1eb4d23e98427a597a2df1 100644 --- a/polkadot/node/core/candidate-validation/Cargo.toml +++ b/polkadot/node/core/candidate-validation/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-candidate-validation" description = "Polkadot crate that implements the Candidate Validation subsystem. Handles requests to validate candidates according to a PVF." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index bf6e09fd1b69b702206eb52090cb4a12812c00c0..8237137fdca015ace87248d062431230a2ad47d4 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -695,6 +695,8 @@ async fn validate_candidate_exhaustive( ))), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err))) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(err))), + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::RuntimeConstruction(err))) => + Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(err))), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousJobDeath(err))) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(format!( @@ -780,40 +782,50 @@ trait ValidationBackend { return validation_result } + macro_rules! break_if_no_retries_left { + ($counter:ident) => { + if $counter > 0 { + $counter -= 1; + } else { + break + } + }; + } + // Allow limited retries for each kind of error. let mut num_death_retries_left = 1; let mut num_job_error_retries_left = 1; let mut num_internal_retries_left = 1; + let mut num_runtime_construction_retries_left = 1; loop { // Stop retrying if we exceeded the timeout. if total_time_start.elapsed() + retry_delay > exec_timeout { break } - + let mut retry_immediately = false; match validation_result { Err(ValidationError::PossiblyInvalid( PossiblyInvalidError::AmbiguousWorkerDeath | PossiblyInvalidError::AmbiguousJobDeath(_), - )) => - if num_death_retries_left > 0 { - num_death_retries_left -= 1; - } else { - break - }, + )) => break_if_no_retries_left!(num_death_retries_left), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(_))) => - if num_job_error_retries_left > 0 { - num_job_error_retries_left -= 1; - } else { - break - }, + break_if_no_retries_left!(num_job_error_retries_left), Err(ValidationError::Internal(_)) => - if num_internal_retries_left > 0 { - num_internal_retries_left -= 1; - } else { - break - }, + break_if_no_retries_left!(num_internal_retries_left), + + Err(ValidationError::PossiblyInvalid( + PossiblyInvalidError::RuntimeConstruction(_), + )) => { + break_if_no_retries_left!(num_runtime_construction_retries_left); + self.precheck_pvf(pvf.clone()).await?; + // In this case the error is deterministic + // And a retry forces the ValidationBackend + // to re-prepare the artifact so + // there is no need to wait before the retry + retry_immediately = true; + }, Ok(_) | Err(ValidationError::Invalid(_) | ValidationError::Preparation(_)) => break, } @@ -821,8 +833,11 @@ trait ValidationBackend { // If we got a possibly transient error, retry once after a brief delay, on the // assumption that the conditions that caused this error may have resolved on their own. { - // Wait a brief delay before retrying. - futures_timer::Delay::new(retry_delay).await; + // In case of many transient errors it is necessary to wait a little bit + // for the error to be probably resolved + if !retry_immediately { + futures_timer::Delay::new(retry_delay).await; + } let new_timeout = exec_timeout.saturating_sub(total_time_start.elapsed()); diff --git a/polkadot/node/core/chain-api/Cargo.toml b/polkadot/node/core/chain-api/Cargo.toml index 32962c9bda43f0c0f0709712930b90d8573ac92c..9aa017ecba3d27c8f85c8055a1fc784d38efbfbd 100644 --- a/polkadot/node/core/chain-api/Cargo.toml +++ b/polkadot/node/core/chain-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-chain-api" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/node/core/chain-selection/Cargo.toml b/polkadot/node/core/chain-selection/Cargo.toml index 8e7029876cf8b9abbe09a864497ec499d3641ca8..96fd42785cdd84ee463146f095b06ea01cc7b491 100644 --- a/polkadot/node/core/chain-selection/Cargo.toml +++ b/polkadot/node/core/chain-selection/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-chain-selection" description = "Chain Selection Subsystem" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -18,7 +18,7 @@ polkadot-node-primitives = { path = "../../primitives" } polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } kvdb = "0.13.0" -thiserror = "1.0.48" +thiserror = { workspace = true } parity-scale-codec = "3.6.1" [dev-dependencies] diff --git a/polkadot/node/core/dispute-coordinator/Cargo.toml b/polkadot/node/core/dispute-coordinator/Cargo.toml index 8ec9bcbe07070cf01b077bf717a98cf835aa3176..1fff0a77170669753fa3fd8d852f936446fe7264 100644 --- a/polkadot/node/core/dispute-coordinator/Cargo.toml +++ b/polkadot/node/core/dispute-coordinator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-dispute-coordinator" -version = "1.0.0" +version = "7.0.0" description = "The node-side components that participate in disputes" authors.workspace = true edition.workspace = true @@ -14,7 +14,7 @@ futures = "0.3.21" gum = { package = "tracing-gum", path = "../../gum" } parity-scale-codec = "3.6.1" kvdb = "0.13.0" -thiserror = "1.0.48" +thiserror = { workspace = true } schnellru = "0.2.1" fatality = "0.0.6" diff --git a/polkadot/node/core/dispute-coordinator/src/import.rs b/polkadot/node/core/dispute-coordinator/src/import.rs index 278561d5d00c1b9f4ec5d4e87287f76bebe8fb70..d3a4625f0d24b2ff0fc3bcc914d629e604909193 100644 --- a/polkadot/node/core/dispute-coordinator/src/import.rs +++ b/polkadot/node/core/dispute-coordinator/src/import.rs @@ -52,7 +52,8 @@ pub struct CandidateEnvironment<'a> { executor_params: &'a ExecutorParams, /// Validator indices controlled by this node. controlled_indices: HashSet, - /// Indices of disabled validators at the `relay_parent`. + /// Indices of on-chain disabled validators at the `relay_parent` combined + /// with the off-chain state. disabled_indices: HashSet, } @@ -67,16 +68,15 @@ impl<'a> CandidateEnvironment<'a> { runtime_info: &'a mut RuntimeInfo, session_index: SessionIndex, relay_parent: Hash, + disabled_offchain: impl IntoIterator, ) -> Option> { - let disabled_indices = runtime_info + let disabled_onchain = runtime_info .get_disabled_validators(ctx.sender(), relay_parent) .await .unwrap_or_else(|err| { gum::info!(target: LOG_TARGET, ?err, "Failed to get disabled validators"); Vec::new() - }) - .into_iter() - .collect(); + }); let (session, executor_params) = match runtime_info .get_session_info_by_index(ctx.sender(), relay_parent, session_index) @@ -87,6 +87,24 @@ impl<'a> CandidateEnvironment<'a> { Err(_) => return None, }; + let n_validators = session.validators.len(); + let byzantine_threshold = polkadot_primitives::byzantine_threshold(n_validators); + // combine on-chain with off-chain disabled validators + // process disabled validators in the following order: + // - on-chain disabled validators + // - prioritized order of off-chain disabled validators + // deduplicate the list and take at most `byzantine_threshold` validators + let disabled_indices = { + let mut d: HashSet = HashSet::new(); + for v in disabled_onchain.into_iter().chain(disabled_offchain.into_iter()) { + if d.len() == byzantine_threshold { + break + } + d.insert(v); + } + d + }; + let controlled_indices = find_controlled_validator_indices(keystore, &session.validators); Some(Self { session_index, session, executor_params, controlled_indices, disabled_indices }) } @@ -116,7 +134,7 @@ impl<'a> CandidateEnvironment<'a> { &self.controlled_indices } - /// Indices of disabled validators at the `relay_parent`. + /// Indices of off-chain and on-chain disabled validators. pub fn disabled_indices(&'a self) -> &'a HashSet { &self.disabled_indices } @@ -237,13 +255,19 @@ impl CandidateVoteState { let supermajority_threshold = polkadot_primitives::supermajority_threshold(n_validators); - // We have a dispute, if we have votes on both sides: - let is_disputed = !votes.invalid.is_empty() && !votes.valid.raw().is_empty(); + // We have a dispute, if we have votes on both sides, with at least one invalid vote + // from non-disabled validator or with votes on both sides and confirmed. + let has_non_disabled_invalid_votes = + votes.invalid.keys().any(|i| !env.disabled_indices().contains(i)); + let byzantine_threshold = polkadot_primitives::byzantine_threshold(n_validators); + let votes_on_both_sides = !votes.valid.raw().is_empty() && !votes.invalid.is_empty(); + let is_confirmed = + votes_on_both_sides && (votes.voted_indices().len() > byzantine_threshold); + let is_disputed = + is_confirmed || (has_non_disabled_invalid_votes && !votes.valid.raw().is_empty()); let (dispute_status, byzantine_threshold_against) = if is_disputed { let mut status = DisputeStatus::active(); - let byzantine_threshold = polkadot_primitives::byzantine_threshold(n_validators); - let is_confirmed = votes.voted_indices().len() > byzantine_threshold; if is_confirmed { status = status.confirm(); }; diff --git a/polkadot/node/core/dispute-coordinator/src/initialized.rs b/polkadot/node/core/dispute-coordinator/src/initialized.rs index a1bcc1f01707c434a7d58eaa801dfb762b573007..54e0410268f1b30f1e6627d4b2c89cac2b741ccb 100644 --- a/polkadot/node/core/dispute-coordinator/src/initialized.rs +++ b/polkadot/node/core/dispute-coordinator/src/initialized.rs @@ -17,7 +17,7 @@ //! Dispute coordinator subsystem in initialized state (after first active leaf is received). use std::{ - collections::{BTreeMap, HashSet, VecDeque}, + collections::{BTreeMap, VecDeque}, sync::Arc, }; @@ -970,6 +970,7 @@ impl Initialized { &mut self.runtime_info, session, relay_parent, + self.offchain_disabled_validators.iter(session), ) .await { @@ -1099,36 +1100,14 @@ impl Initialized { let new_state = import_result.new_state(); - let byzantine_threshold = polkadot_primitives::byzantine_threshold(n_validators); - // combine on-chain with off-chain disabled validators - // process disabled validators in the following order: - // - on-chain disabled validators - // - prioritized order of off-chain disabled validators - // deduplicate the list and take at most `byzantine_threshold` validators - let disabled_validators = { - let mut d: HashSet = HashSet::new(); - for v in env - .disabled_indices() - .iter() - .cloned() - .chain(self.offchain_disabled_validators.iter(session)) - { - if d.len() == byzantine_threshold { - break - } - d.insert(v); - } - d - }; - let is_included = self.scraper.is_candidate_included(&candidate_hash); let is_backed = self.scraper.is_candidate_backed(&candidate_hash); let own_vote_missing = new_state.own_vote_missing(); let is_disputed = new_state.is_disputed(); let is_confirmed = new_state.is_confirmed(); - let potential_spam = is_potential_spam(&self.scraper, &new_state, &candidate_hash, |v| { - disabled_validators.contains(v) - }); + let is_disabled = |v: &ValidatorIndex| env.disabled_indices().contains(v); + let potential_spam = + is_potential_spam(&self.scraper, &new_state, &candidate_hash, is_disabled); let allow_participation = !potential_spam; gum::trace!( @@ -1139,7 +1118,7 @@ impl Initialized { ?candidate_hash, confirmed = ?new_state.is_confirmed(), has_invalid_voters = ?!import_result.new_invalid_voters().is_empty(), - n_disabled_validators = ?disabled_validators.len(), + n_disabled_validators = ?env.disabled_indices().len(), "Is spam?" ); @@ -1439,6 +1418,7 @@ impl Initialized { &mut self.runtime_info, session, candidate_receipt.descriptor.relay_parent, + self.offchain_disabled_validators.iter(session), ) .await { diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs index c3038fc0953c6bcc7cdc979f259f7e9bf80f3039..4b511e7430af655303a7bb6e5ca0e86d0f8e8c7b 100644 --- a/polkadot/node/core/dispute-coordinator/src/lib.rs +++ b/polkadot/node/core/dispute-coordinator/src/lib.rs @@ -341,6 +341,8 @@ impl DisputeCoordinatorSubsystem { runtime_info, highest_session, leaf_hash, + // on startup we don't have any off-chain disabled state + std::iter::empty(), ) .await { @@ -370,10 +372,9 @@ impl DisputeCoordinatorSubsystem { }, }; let vote_state = CandidateVoteState::new(votes, &env, now); - let onchain_disabled = env.disabled_indices(); - let potential_spam = is_potential_spam(&scraper, &vote_state, candidate_hash, |v| { - onchain_disabled.contains(v) - }); + let is_disabled = |v: &ValidatorIndex| env.disabled_indices().contains(v); + let potential_spam = + is_potential_spam(&scraper, &vote_state, candidate_hash, is_disabled); let is_included = scraper.is_candidate_included(&vote_state.votes().candidate_receipt.hash()); diff --git a/polkadot/node/core/dispute-coordinator/src/tests.rs b/polkadot/node/core/dispute-coordinator/src/tests.rs index af384256c4f71e2f78e4ac0baff2558b9ba27f46..0360e357bee4ca70fe9a6ab3a9454d611ddc93fd 100644 --- a/polkadot/node/core/dispute-coordinator/src/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/tests.rs @@ -2645,14 +2645,23 @@ fn participation_with_onchain_disabling_unconfirmed() { .await; handle_disabled_validators_queries(&mut virtual_overseer, vec![disabled_index]).await; - handle_approval_vote_request(&mut virtual_overseer, &candidate_hash, HashMap::new()) - .await; - assert_eq!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport)); // we should not participate due to disabled indices on chain assert!(virtual_overseer.recv().timeout(TEST_TIMEOUT).await.is_none()); + { + // make sure the dispute is not active + let (tx, rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ActiveDisputes(tx), + }) + .await; + + assert_eq!(rx.await.unwrap().len(), 0); + } + // Scenario 2: unconfirmed dispute with non-disabled validator against. // Expectation: even if the dispute is unconfirmed, we should participate // once we receive an invalid vote from a non-disabled validator. @@ -2679,6 +2688,9 @@ fn participation_with_onchain_disabling_unconfirmed() { }) .await; + handle_approval_vote_request(&mut virtual_overseer, &candidate_hash, HashMap::new()) + .await; + assert_eq!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport)); participation_with_distribution( @@ -2710,7 +2722,7 @@ fn participation_with_onchain_disabling_unconfirmed() { .await; let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone(); - assert_eq!(votes.valid.raw().len(), 2); // 3+1 => we have participated + assert_eq!(votes.valid.raw().len(), 2); // 1+1 => we have participated assert_eq!(votes.invalid.len(), 2); } @@ -2832,6 +2844,7 @@ fn participation_with_onchain_disabling_confirmed() { #[test] fn participation_with_offchain_disabling() { + sp_tracing::init_for_tests(); test_harness(|mut test_state, mut virtual_overseer| { Box::pin(async move { let session = 1; @@ -2968,17 +2981,23 @@ fn participation_with_offchain_disabling() { // let's disable validators 3, 4 on chain, but this should not affect this import let disabled_validators = vec![ValidatorIndex(3), ValidatorIndex(4)]; handle_disabled_validators_queries(&mut virtual_overseer, disabled_validators).await; - handle_approval_vote_request( - &mut virtual_overseer, - &another_candidate_hash, - HashMap::new(), - ) - .await; assert_eq!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport)); // we should not participate since due to offchain disabling assert!(virtual_overseer.recv().timeout(TEST_TIMEOUT).await.is_none()); + { + // make sure the new dispute is not active + let (tx, rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ActiveDisputes(tx), + }) + .await; + + assert_eq!(rx.await.unwrap().len(), 1); + } + // now import enough votes for dispute confirmation // even though all of these votes are from (on chain) disabled validators let mut statements = Vec::new(); @@ -3007,6 +3026,12 @@ fn participation_with_offchain_disabling() { }) .await; + handle_approval_vote_request( + &mut virtual_overseer, + &another_candidate_hash, + HashMap::new(), + ) + .await; assert_eq!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport)); participation_with_distribution( diff --git a/polkadot/node/core/parachains-inherent/Cargo.toml b/polkadot/node/core/parachains-inherent/Cargo.toml index 2384020025181d9dd0cae04c54246193fb6278d3..24da4dc1e316f8469704ab57e4742dbc07197992 100644 --- a/polkadot/node/core/parachains-inherent/Cargo.toml +++ b/polkadot/node/core/parachains-inherent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-parachains-inherent" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -13,7 +13,7 @@ workspace = true futures = "0.3.21" futures-timer = "3.0.2" gum = { package = "tracing-gum", path = "../../gum" } -thiserror = "1.0.48" +thiserror = { workspace = true } async-trait = "0.1.74" polkadot-node-subsystem = { path = "../../subsystem" } polkadot-overseer = { path = "../../overseer" } diff --git a/polkadot/node/core/prospective-parachains/Cargo.toml b/polkadot/node/core/prospective-parachains/Cargo.toml index e6b6aa5e15d72e758a5acd30a6771756e6ee78a9..f66a66e859ec0a139e31068f78225597d577d1a9 100644 --- a/polkadot/node/core/prospective-parachains/Cargo.toml +++ b/polkadot/node/core/prospective-parachains/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-prospective-parachains" -version = "1.0.0" +version = "6.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -13,7 +13,7 @@ workspace = true futures = "0.3.19" gum = { package = "tracing-gum", path = "../../gum" } parity-scale-codec = "3.6.4" -thiserror = "1.0.48" +thiserror = { workspace = true } fatality = "0.0.6" bitvec = "1" @@ -23,6 +23,7 @@ polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } [dev-dependencies] +rstest = "0.18.2" assert_matches = "1" polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } polkadot-node-subsystem-types = { path = "../../subsystem-types" } diff --git a/polkadot/node/core/prospective-parachains/src/fragment_tree.rs b/polkadot/node/core/prospective-parachains/src/fragment_tree.rs index 292e4ebe5282b4344853ca5f3247caf1d9af6cc7..8061dc82d8358ae9e7c741e3f785634601eb5d03 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_tree.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_tree.rs @@ -96,6 +96,7 @@ use std::{ use super::LOG_TARGET; use bitvec::prelude::*; +use polkadot_node_subsystem::messages::Ancestors; use polkadot_node_subsystem_util::inclusion_emulator::{ ConstraintModifications, Constraints, Fragment, ProspectiveCandidate, RelayChainBlockInfo, }; @@ -202,7 +203,10 @@ impl CandidateStorage { /// Note that an existing candidate has been backed. pub 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; + } else { + gum::trace!(target: LOG_TARGET, ?candidate_hash, "Candidate not found while marking as backed"); } } @@ -753,52 +757,217 @@ impl FragmentTree { depths.iter_ones().collect() } - /// Select a candidate after the given `required_path` which passes - /// the predicate. - /// - /// If there are multiple possibilities, this will select the first one. + /// Select `count` candidates after the given `ancestors` which pass + /// the predicate and have not already been backed on chain. /// - /// This returns `None` if there is no candidate meeting those criteria. + /// Does an exhaustive search into the tree after traversing the ancestors path. + /// If the ancestors draw out a path that can be traversed in multiple ways, no + /// candidates will be returned. + /// If the ancestors do not draw out a full path (the path contains holes), candidates will be + /// suggested that may fill these holes. + /// If the ancestors don't draw out a valid path, no candidates will be returned. If there are + /// multiple possibilities of the same size, this will select the first one. If there is no + /// chain of size `count` that matches the criteria, this will return the largest chain it could + /// find with the criteria. If there are no candidates meeting those criteria, returns an empty + /// `Vec`. + /// Cycles are accepted, but this code expects that the runtime will deduplicate + /// identical candidates when occupying the cores (when proposing to back A->B->A, only A will + /// be backed on chain). /// - /// The intention of the `required_path` is to allow queries on the basis of + /// The intention of the `ancestors` is to allow queries on the basis of /// one or more candidates which were previously pending availability becoming - /// available and opening up more room on the core. - pub(crate) fn select_child( + /// available or candidates timing out. + pub(crate) fn find_backable_chain( &self, - required_path: &[CandidateHash], + ancestors: Ancestors, + count: u32, pred: impl Fn(&CandidateHash) -> bool, - ) -> Option { - let base_node = { - // traverse the required path. - let mut node = NodePointer::Root; - for required_step in required_path { - node = self.node_candidate_child(node, &required_step)?; - } + ) -> Vec { + if count == 0 { + return vec![] + } + // First, we need to order the ancestors. + // The node returned is the one from which we can start finding new backable candidates. + let Some(base_node) = self.find_ancestor_path(ancestors) else { return vec![] }; + + self.find_backable_chain_inner( + base_node, + count, + count, + &pred, + &mut Vec::with_capacity(count as usize), + ) + } - node - }; + // Try finding a candidate chain starting from `base_node` of length `expected_count`. + // If not possible, return the longest one we could find. + // Does a depth-first search, since we're optimistic that there won't be more than one such + // chains (parachains shouldn't usually have forks). So in the usual case, this will conclude + // in `O(expected_count)`. + // Cycles are accepted, but this doesn't allow for infinite execution time, because the maximum + // depth we'll reach is `expected_count`. + // + // Worst case performance is `O(num_forks ^ expected_count)`, the same as populating the tree. + // Although an exponential function, this is actually a constant that can only be altered via + // sudo/governance, because: + // 1. `num_forks` at a given level is at most `max_candidate_depth * max_validators_per_core` + // (because each validator in the assigned group can second `max_candidate_depth` + // candidates). The prospective-parachains subsystem assumes that the number of para forks is + // limited by collator-protocol and backing subsystems. In practice, this is a constant which + // can only be altered by sudo or governance. + // 2. `expected_count` is equal to the number of cores a para is scheduled on (in an elastic + // scaling scenario). For non-elastic-scaling, this is just 1. In practice, this should be a + // small number (1-3), capped by the total number of available cores (a constant alterable + // only via governance/sudo). + fn find_backable_chain_inner( + &self, + base_node: NodePointer, + expected_count: u32, + remaining_count: u32, + pred: &dyn Fn(&CandidateHash) -> bool, + accumulator: &mut Vec, + ) -> Vec { + if remaining_count == 0 { + // The best option is the chain we've accumulated so far. + return accumulator.to_vec(); + } - // TODO [now]: taking the first selection might introduce bias - // or become gameable. - // - // For plausibly unique parachains, this shouldn't matter much. - // figure out alternative selection criteria? - match base_node { + let children: Vec<_> = match base_node { NodePointer::Root => self .nodes .iter() - .take_while(|n| n.parent == NodePointer::Root) - .filter(|n| self.scope.get_pending_availability(&n.candidate_hash).is_none()) - .filter(|n| pred(&n.candidate_hash)) - .map(|n| n.candidate_hash) - .next(), - NodePointer::Storage(ptr) => self.nodes[ptr] - .children - .iter() - .filter(|n| self.scope.get_pending_availability(&n.1).is_none()) - .filter(|n| pred(&n.1)) - .map(|n| n.1) - .next(), + .enumerate() + .take_while(|(_, n)| n.parent == NodePointer::Root) + .filter(|(_, n)| self.scope.get_pending_availability(&n.candidate_hash).is_none()) + .filter(|(_, n)| pred(&n.candidate_hash)) + .map(|(ptr, n)| (NodePointer::Storage(ptr), n.candidate_hash)) + .collect(), + NodePointer::Storage(base_node_ptr) => { + let base_node = &self.nodes[base_node_ptr]; + + base_node + .children + .iter() + .filter(|(_, hash)| self.scope.get_pending_availability(&hash).is_none()) + .filter(|(_, hash)| pred(&hash)) + .map(|(ptr, hash)| (*ptr, *hash)) + .collect() + }, + }; + + let mut best_result = accumulator.clone(); + for (child_ptr, child_hash) in children { + accumulator.push(child_hash); + + let result = self.find_backable_chain_inner( + child_ptr, + expected_count, + remaining_count - 1, + &pred, + accumulator, + ); + + accumulator.pop(); + + // Short-circuit the search if we've found the right length. Otherwise, we'll + // search for a max. + // Taking the first best selection doesn't introduce bias or become gameable, + // because `find_ancestor_path` uses a `HashSet` to track the ancestors, which + // makes the order in which ancestors are visited non-deterministic. + if result.len() == expected_count as usize { + return result + } else if best_result.len() < result.len() { + best_result = result; + } + } + + best_result + } + + // Orders the ancestors into a viable path from root to the last one. + // Returns a pointer to the last node in the path. + // We assume that the ancestors form a chain (that the + // av-cores do not back parachain forks), None is returned otherwise. + // If we cannot use all ancestors, stop at the first found hole in the chain. This usually + // translates to a timed out candidate. + fn find_ancestor_path(&self, mut ancestors: Ancestors) -> Option { + // The number of elements in the path we've processed so far. + let mut depth = 0; + let mut last_node = NodePointer::Root; + let mut next_node: Option = Some(NodePointer::Root); + + while let Some(node) = next_node { + if depth > self.scope.max_depth { + return None; + } + + last_node = node; + + next_node = match node { + NodePointer::Root => { + let children = self + .nodes + .iter() + .enumerate() + .take_while(|n| n.1.parent == NodePointer::Root) + .map(|(index, node)| (NodePointer::Storage(index), node.candidate_hash)) + .collect::>(); + + self.find_valid_child(&mut ancestors, children.iter()).ok()? + }, + NodePointer::Storage(ptr) => { + let children = self.nodes.get(ptr).and_then(|n| Some(n.children.iter())); + if let Some(children) = children { + self.find_valid_child(&mut ancestors, children).ok()? + } else { + None + } + }, + }; + + depth += 1; + } + + Some(last_node) + } + + // Find a node from the given iterator which is present in the ancestors + // collection. If there are multiple such nodes, return an error and log a warning. We don't + // accept forks in a parachain to be backed. The supplied ancestors should all form a chain. + // If there is no such node, return None. + fn find_valid_child<'a>( + &self, + ancestors: &'a mut Ancestors, + nodes: impl Iterator + 'a, + ) -> Result, ()> { + let mut possible_children = + nodes.filter_map(|(node_ptr, hash)| match ancestors.remove(&hash) { + true => Some(node_ptr), + false => None, + }); + + // We don't accept forks in a parachain to be backed. The supplied ancestors + // should all form a chain. + let next = possible_children.next(); + if let Some(second_child) = possible_children.next() { + if let (Some(NodePointer::Storage(first_child)), NodePointer::Storage(second_child)) = + (next, second_child) + { + gum::error!( + target: LOG_TARGET, + para_id = ?self.scope.para, + relay_parent = ?self.scope.relay_parent, + "Trying to find new backable candidates for a parachain for which we've backed a fork.\ + This is a bug and the runtime should not have allowed it.\n\ + Backed candidates with the same parent: {}, {}", + self.nodes[*first_child].candidate_hash, + self.nodes[*second_child].candidate_hash, + ); + } + + Err(()) + } else { + Ok(next.copied()) } } @@ -984,6 +1153,17 @@ mod tests { use polkadot_node_subsystem_util::inclusion_emulator::InboundHrmpLimitations; use polkadot_primitives::{BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData}; use polkadot_primitives_test_helpers as test_helpers; + use rstest::rstest; + use std::iter; + + impl NodePointer { + fn unwrap_idx(self) -> usize { + match self { + NodePointer::Root => panic!("Unexpected root"), + NodePointer::Storage(index) => index, + } + } + } fn make_constraints( min_relay_parent_number: BlockNumber, @@ -1468,6 +1648,373 @@ mod tests { assert_eq!(tree.nodes[1].parent, NodePointer::Storage(0)); } + #[test] + fn test_find_ancestor_path_and_find_backable_chain_empty_tree() { + let para_id = ParaId::from(5u32); + let relay_parent = Hash::repeat_byte(1); + let required_parent: HeadData = vec![0xff].into(); + let max_depth = 10; + + // Empty tree + let storage = CandidateStorage::new(); + 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 tree = FragmentTree::populate(scope, &storage); + assert_eq!(tree.candidates().collect::>().len(), 0); + assert_eq!(tree.nodes.len(), 0); + + assert_eq!(tree.find_ancestor_path(Ancestors::new()).unwrap(), NodePointer::Root); + assert_eq!(tree.find_backable_chain(Ancestors::new(), 2, |_| true), vec![]); + // Invalid candidate. + let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()), Some(NodePointer::Root)); + assert_eq!(tree.find_backable_chain(ancestors, 2, |_| true), vec![]); + } + + #[rstest] + #[case(true, 13)] + #[case(false, 8)] + // The tree with no cycles looks like: + // Make a tree that looks like this (note that there's no cycle): + // +-(root)-+ + // | | + // +----0---+ 7 + // | | + // 1----+ 5 + // | | + // | | + // 2 6 + // | + // 3 + // | + // 4 + // + // The tree with cycles is the same as the first but has a cycle from 4 back to the state + // produced by 0 (It's bounded by the max_depth + 1). + // +-(root)-+ + // | | + // +----0---+ 7 + // | | + // 1----+ 5 + // | | + // | | + // 2 6 + // | + // 3 + // | + // 4---+ + // | | + // 1 5 + // | + // 2 + // | + // 3 + fn test_find_ancestor_path_and_find_backable_chain( + #[case] has_cycle: bool, + #[case] expected_node_count: usize, + ) { + let para_id = ParaId::from(5u32); + let relay_parent = Hash::repeat_byte(1); + let required_parent: HeadData = vec![0xff].into(); + let max_depth = 7; + let relay_parent_number = 0; + let relay_parent_storage_root = Hash::repeat_byte(69); + + 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 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![0].into(), + vec![5].into(), + 0, + )); + // Candidate 6 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![1].into(), + vec![6].into(), + 0, + )); + // Candidate 7 + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + required_parent.clone(), + vec![7].into(), + 0, + )); + + if has_cycle { + candidates[4] = make_committed_candidate( + para_id, + relay_parent, + 0, + vec![3].into(), + vec![0].into(), // put the cycle here back to the output state of 0. + 0, + ); + } + + let base_constraints = make_constraints(0, vec![0], required_parent.clone()); + let mut storage = CandidateStorage::new(); + + 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()).unwrap(); + } + let candidates = + candidates.into_iter().map(|(_pvd, candidate)| candidate).collect::>(); + let scope = Scope::with_ancestors( + para_id, + relay_parent_info, + base_constraints, + vec![], + max_depth, + vec![], + ) + .unwrap(); + let tree = FragmentTree::populate(scope, &storage); + + assert_eq!(tree.candidates().collect::>().len(), candidates.len()); + assert_eq!(tree.nodes.len(), expected_node_count); + + // Do some common tests on both trees. + { + // No ancestors supplied. + assert_eq!(tree.find_ancestor_path(Ancestors::new()).unwrap(), NodePointer::Root); + assert_eq!( + tree.find_backable_chain(Ancestors::new(), 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + // Ancestor which is not part of the tree. Will be ignored. + let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()).unwrap(), NodePointer::Root); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + // A chain fork. + let ancestors: Ancestors = + [(candidates[0].hash()), (candidates[7].hash())].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()), None); + assert_eq!(tree.find_backable_chain(ancestors, 1, |_| true), vec![]); + + // Ancestors which are part of the tree but don't form a path. Will be ignored. + let ancestors: Ancestors = + [candidates[1].hash(), candidates[2].hash()].into_iter().collect(); + assert_eq!(tree.find_ancestor_path(ancestors.clone()).unwrap(), NodePointer::Root); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // Valid ancestors. + let ancestors: Ancestors = [candidates[7].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[7].hash()); + assert_eq!(tree.find_backable_chain(ancestors, 1, |_| true), vec![]); + + let ancestors: Ancestors = + [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[2].hash()); + assert_eq!( + tree.find_backable_chain(ancestors.clone(), 2, |_| true), + [3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // Valid ancestors with candidates which have been omitted due to timeouts + let ancestors: Ancestors = + [candidates[0].hash(), candidates[2].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[0].hash()); + assert_eq!( + tree.find_backable_chain(ancestors, 3, |_| true), + [1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + let ancestors: Ancestors = + [candidates[0].hash(), candidates[1].hash(), candidates[3].hash()] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[1].hash()); + if has_cycle { + assert_eq!( + tree.find_backable_chain(ancestors, 2, |_| true), + [2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + } else { + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + } + + let ancestors: Ancestors = + [candidates[1].hash(), candidates[2].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + assert_eq!(res, NodePointer::Root); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // Requested count is 0. + assert_eq!(tree.find_backable_chain(Ancestors::new(), 0, |_| true), vec![]); + + let ancestors: Ancestors = + [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] + .into_iter() + .collect(); + assert_eq!(tree.find_backable_chain(ancestors, 0, |_| true), vec![]); + + let ancestors: Ancestors = + [candidates[2].hash(), candidates[0].hash()].into_iter().collect(); + assert_eq!(tree.find_backable_chain(ancestors, 0, |_| true), vec![]); + } + + // Now do some tests only on the tree with cycles + if has_cycle { + // Exceeds the maximum tree depth. 0-1-2-3-4-1-2-3-4, when the tree stops at + // 0-1-2-3-4-1-2-3. + let ancestors: Ancestors = [ + candidates[0].hash(), + candidates[1].hash(), + candidates[2].hash(), + candidates[3].hash(), + candidates[4].hash(), + ] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[4].hash()); + assert_eq!( + tree.find_backable_chain(ancestors, 4, |_| true), + [1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // 0-1-2. + let ancestors: Ancestors = + [candidates[0].hash(), candidates[1].hash(), candidates[2].hash()] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[2].hash()); + assert_eq!( + tree.find_backable_chain(ancestors.clone(), 1, |_| true), + [3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + assert_eq!( + tree.find_backable_chain(ancestors, 5, |_| true), + [3, 4, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() + ); + + // 0-1 + let ancestors: Ancestors = + [candidates[0].hash(), candidates[1].hash()].into_iter().collect(); + let res = tree.find_ancestor_path(ancestors.clone()).unwrap(); + let candidate = &tree.nodes[res.unwrap_idx()]; + assert_eq!(candidate.candidate_hash, candidates[1].hash()); + assert_eq!( + tree.find_backable_chain(ancestors, 6, |_| true), + [2, 3, 4, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>(), + ); + + // For 0-1-2-3-4-5, there's more than 1 way of finding this path in + // the tree. `None` should be returned. The runtime should not have accepted this. + let ancestors: Ancestors = [ + candidates[0].hash(), + candidates[1].hash(), + candidates[2].hash(), + candidates[3].hash(), + candidates[4].hash(), + candidates[5].hash(), + ] + .into_iter() + .collect(); + let res = tree.find_ancestor_path(ancestors.clone()); + assert_eq!(res, None); + assert_eq!(tree.find_backable_chain(ancestors, 1, |_| true), vec![]); + } + } + #[test] fn graceful_cycle_of_0() { let mut storage = CandidateStorage::new(); @@ -1521,6 +2068,25 @@ mod tests { assert_eq!(tree.nodes[2].candidate_hash, candidate_a_hash); assert_eq!(tree.nodes[3].candidate_hash, candidate_a_hash); assert_eq!(tree.nodes[4].candidate_hash, candidate_a_hash); + + for count in 1..10 { + assert_eq!( + tree.find_backable_chain(Ancestors::new(), count, |_| true), + iter::repeat(candidate_a_hash) + .take(std::cmp::min(count as usize, max_depth + 1)) + .collect::>() + ); + assert_eq!( + tree.find_backable_chain( + [candidate_a_hash].into_iter().collect(), + count - 1, + |_| true + ), + iter::repeat(candidate_a_hash) + .take(std::cmp::min(count as usize - 1, max_depth)) + .collect::>() + ); + } } #[test] @@ -1588,6 +2154,42 @@ mod tests { assert_eq!(tree.nodes[2].candidate_hash, candidate_a_hash); assert_eq!(tree.nodes[3].candidate_hash, candidate_b_hash); assert_eq!(tree.nodes[4].candidate_hash, candidate_a_hash); + + assert_eq!(tree.find_backable_chain(Ancestors::new(), 1, |_| true), vec![candidate_a_hash],); + assert_eq!( + tree.find_backable_chain(Ancestors::new(), 2, |_| true), + vec![candidate_a_hash, candidate_b_hash], + ); + assert_eq!( + tree.find_backable_chain(Ancestors::new(), 3, |_| true), + vec![candidate_a_hash, candidate_b_hash, candidate_a_hash], + ); + assert_eq!( + tree.find_backable_chain([candidate_a_hash].into_iter().collect(), 2, |_| true), + vec![candidate_b_hash, candidate_a_hash], + ); + + assert_eq!( + tree.find_backable_chain(Ancestors::new(), 6, |_| true), + vec![ + candidate_a_hash, + candidate_b_hash, + candidate_a_hash, + candidate_b_hash, + candidate_a_hash + ], + ); + + for count in 3..7 { + assert_eq!( + tree.find_backable_chain( + [candidate_a_hash, candidate_b_hash].into_iter().collect(), + count, + |_| true + ), + vec![candidate_a_hash, candidate_b_hash, candidate_a_hash], + ); + } } #[test] diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index dabcfb80e02ee7943885888aef183a327579e3bd..2b14e09b4fb4f56f5f7c6ffd738e4167804f44ad 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -35,7 +35,7 @@ use futures::{channel::oneshot, prelude::*}; use polkadot_node_subsystem::{ messages::{ - ChainApiMessage, FragmentTreeMembership, HypotheticalCandidate, + Ancestors, ChainApiMessage, FragmentTreeMembership, HypotheticalCandidate, HypotheticalFrontierRequest, IntroduceCandidateRequest, ProspectiveParachainsMessage, ProspectiveValidationDataRequest, RuntimeApiMessage, RuntimeApiRequest, }, @@ -146,12 +146,13 @@ async fn run_iteration( handle_candidate_seconded(view, para, candidate_hash), ProspectiveParachainsMessage::CandidateBacked(para, candidate_hash) => handle_candidate_backed(&mut *ctx, view, para, candidate_hash).await?, - ProspectiveParachainsMessage::GetBackableCandidate( + ProspectiveParachainsMessage::GetBackableCandidates( relay_parent, para, - required_path, + count, + ancestors, tx, - ) => answer_get_backable_candidate(&view, relay_parent, para, required_path, tx), + ) => answer_get_backable_candidates(&view, relay_parent, para, count, ancestors, tx), ProspectiveParachainsMessage::GetHypotheticalFrontier(request, tx) => answer_hypothetical_frontier_request(&view, request, tx), ProspectiveParachainsMessage::GetTreeMembership(para, candidate, tx) => @@ -290,7 +291,7 @@ async fn handle_active_leaves_update( ) .expect("ancestors are provided in reverse order and correctly; qed"); - gum::debug!( + gum::trace!( target: LOG_TARGET, relay_parent = ?hash, min_relay_parent = scope.earliest_relay_parent().number, @@ -552,12 +553,13 @@ async fn handle_candidate_backed( Ok(()) } -fn answer_get_backable_candidate( +fn answer_get_backable_candidates( view: &View, relay_parent: Hash, para: ParaId, - required_path: Vec, - tx: oneshot::Sender>, + count: u32, + ancestors: Ancestors, + tx: oneshot::Sender>, ) { let data = match view.active_leaves.get(&relay_parent) { None => { @@ -568,7 +570,7 @@ fn answer_get_backable_candidate( "Requested backable candidate for inactive relay-parent." ); - let _ = tx.send(None); + let _ = tx.send(vec![]); return }, Some(d) => d, @@ -583,7 +585,7 @@ fn answer_get_backable_candidate( "Requested backable candidate for inactive para." ); - let _ = tx.send(None); + let _ = tx.send(vec![]); return }, Some(tree) => tree, @@ -598,30 +600,49 @@ fn answer_get_backable_candidate( "No candidate storage for active para", ); - let _ = tx.send(None); + let _ = tx.send(vec![]); return }, Some(s) => s, }; - let Some(child_hash) = - tree.select_child(&required_path, |candidate| storage.is_backed(candidate)) - else { - let _ = tx.send(None); - return - }; - let Some(candidate_relay_parent) = storage.relay_parent_by_candidate_hash(&child_hash) else { - gum::error!( + let backable_candidates: Vec<_> = tree + .find_backable_chain(ancestors.clone(), count, |candidate| storage.is_backed(candidate)) + .into_iter() + .filter_map(|child_hash| { + storage.relay_parent_by_candidate_hash(&child_hash).map_or_else( + || { + gum::error!( + target: LOG_TARGET, + ?child_hash, + para_id = ?para, + "Candidate is present in fragment tree but not in candidate's storage!", + ); + None + }, + |parent_hash| Some((child_hash, parent_hash)), + ) + }) + .collect(); + + if backable_candidates.is_empty() { + gum::trace!( target: LOG_TARGET, - ?child_hash, + ?ancestors, para_id = ?para, - "Candidate is present in fragment tree but not in candidate's storage!", + %relay_parent, + "Could not find any backable candidate", ); - let _ = tx.send(None); - return - }; + } else { + gum::trace!( + target: LOG_TARGET, + ?relay_parent, + ?backable_candidates, + "Found backable candidates", + ); + } - let _ = tx.send(Some((child_hash, candidate_relay_parent))); + let _ = tx.send(backable_candidates); } fn answer_hypothetical_frontier_request( diff --git a/polkadot/node/core/prospective-parachains/src/tests.rs b/polkadot/node/core/prospective-parachains/src/tests.rs index 7e369245c0e1587b405eb4516343610aa8c9a320..0beddbf1416a7ec94cb84a6893e9c20cbeaf28ce 100644 --- a/polkadot/node/core/prospective-parachains/src/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/tests.rs @@ -403,25 +403,24 @@ async fn get_membership( assert_eq!(resp, expected_membership_response); } -async fn get_backable_candidate( +async fn get_backable_candidates( virtual_overseer: &mut VirtualOverseer, leaf: &TestLeaf, para_id: ParaId, - required_path: Vec, - expected_result: Option<(CandidateHash, Hash)>, + ancestors: Ancestors, + count: u32, + expected_result: Vec<(CandidateHash, Hash)>, ) { let (tx, rx) = oneshot::channel(); virtual_overseer .send(overseer::FromOrchestra::Communication { - msg: ProspectiveParachainsMessage::GetBackableCandidate( - leaf.hash, - para_id, - required_path, - tx, + msg: ProspectiveParachainsMessage::GetBackableCandidates( + leaf.hash, para_id, count, ancestors, tx, ), }) .await; let resp = rx.await.unwrap(); + assert_eq!(resp.len(), expected_result.len()); assert_eq!(resp, expected_result); } @@ -849,9 +848,9 @@ fn check_candidate_on_multiple_forks() { assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } -// Backs some candidates and tests `GetBackableCandidate`. +// Backs some candidates and tests `GetBackableCandidates` when requesting a single candidate. #[test] -fn check_backable_query() { +fn check_backable_query_single_candidate() { let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -896,12 +895,31 @@ fn check_backable_query() { introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; // Should not get any backable candidates. - get_backable_candidate( + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a].into_iter().collect(), + 1, + vec![], + ) + .await; + get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], - None, + vec![candidate_hash_a].into_iter().collect(), + 0, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::new(), + 0, + vec![], ) .await; @@ -910,12 +928,13 @@ fn check_backable_query() { second_candidate(&mut virtual_overseer, candidate_b.clone()).await; // Should not get any backable candidates. - get_backable_candidate( + get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], - None, + vec![candidate_hash_a].into_iter().collect(), + 1, + vec![], ) .await; @@ -923,31 +942,54 @@ fn check_backable_query() { back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; + // Should not get any backable candidates for the other para. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + Ancestors::new(), + 1, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + vec![candidate_hash_a].into_iter().collect(), + 1, + vec![], + ) + .await; + // Get backable candidate. - get_backable_candidate( + get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![], - Some((candidate_hash_a, leaf_a.hash)), + Ancestors::new(), + 1, + vec![(candidate_hash_a, leaf_a.hash)], ) .await; - get_backable_candidate( + get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_a], - Some((candidate_hash_b, leaf_a.hash)), + vec![candidate_hash_a].into_iter().collect(), + 1, + vec![(candidate_hash_b, leaf_a.hash)], ) .await; - // Should not get anything at the wrong path. - get_backable_candidate( + // Wrong path + get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), - vec![candidate_hash_b], - None, + vec![candidate_hash_b].into_iter().collect(), + 1, + vec![(candidate_hash_a, leaf_a.hash)], ) .await; @@ -961,6 +1003,391 @@ fn check_backable_query() { 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_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await; + second_candidate(&mut $virtual_overseer, candidate.clone()).await; + back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await; + + (candidate, candidate_hash) + }}; + } + + // Parachain 1 looks like this: + // +---A----+ + // | | + // +----B---+ C + // | | | | + // D E F H + // | | + // G I + // | + // J + { + 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(&mut virtual_overseer, &leaf_a, &test_state).await; + + // Candidate A + let (candidate_a, pvd_a) = make_candidate( + leaf_a.hash, + leaf_a.number, + 1.into(), + HeadData(vec![1, 2, 3]), + HeadData(vec![1]), + test_state.validation_code_hash, + ); + let candidate_hash_a = candidate_a.hash(); + introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + second_candidate(&mut virtual_overseer, candidate_a.clone()).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_a, 3); + let (_candidate_d, candidate_hash_d) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 4); + let (_candidate_e, candidate_hash_e) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 5); + let (candidate_f, candidate_hash_f) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 6); + let (_candidate_g, candidate_hash_g) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_f, 7); + let (candidate_h, candidate_hash_h) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_c, 8); + let (candidate_i, candidate_hash_i) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_h, 9); + let (_candidate_j, candidate_hash_j) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_i, 10); + + // Should not get any backable candidates for the other para. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + Ancestors::new(), + 1, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + Ancestors::new(), + 5, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 2.into(), + vec![candidate_hash_a].into_iter().collect(), + 1, + vec![], + ) + .await; + + // Test various scenarios with various counts. + + // empty required_path + { + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::new(), + 1, + vec![(candidate_hash_a, leaf_a.hash)], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::new(), + 4, + vec![ + (candidate_hash_a, leaf_a.hash), + (candidate_hash_b, leaf_a.hash), + (candidate_hash_f, leaf_a.hash), + (candidate_hash_g, leaf_a.hash), + ], + ) + .await; + } + + // required path of 1 + { + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a].into_iter().collect(), + 1, + vec![(candidate_hash_b, leaf_a.hash)], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a].into_iter().collect(), + 3, + vec![ + (candidate_hash_b, leaf_a.hash), + (candidate_hash_f, leaf_a.hash), + (candidate_hash_g, leaf_a.hash), + ], + ) + .await; + + // If the requested count exceeds the largest chain, return the longest + // chain we can get. + for count in 5..10 { + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a].into_iter().collect(), + count, + vec![ + (candidate_hash_c, leaf_a.hash), + (candidate_hash_h, leaf_a.hash), + (candidate_hash_i, leaf_a.hash), + (candidate_hash_j, leaf_a.hash), + ], + ) + .await; + } + } + + // required path of 2 and higher + { + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_i, candidate_hash_h, candidate_hash_c] + .into_iter() + .collect(), + 1, + vec![(candidate_hash_j, leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_b].into_iter().collect(), + 1, + vec![(candidate_hash_d, leaf_a.hash)], + ) + .await; + + // If the requested count exceeds the largest chain, return the longest + // chain we can get. + for count in 4..10 { + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_c].into_iter().collect(), + count, + vec![ + (candidate_hash_h, leaf_a.hash), + (candidate_hash_i, leaf_a.hash), + (candidate_hash_j, leaf_a.hash), + ], + ) + .await; + } + } + + // No more candidates in any chain. + { + for count in 1..4 { + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_b, candidate_hash_e] + .into_iter() + .collect(), + count, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![ + candidate_hash_a, + candidate_hash_c, + candidate_hash_h, + candidate_hash_i, + candidate_hash_j, + ] + .into_iter() + .collect(), + count, + vec![], + ) + .await; + } + } + + // Wrong paths. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_b].into_iter().collect(), + 1, + vec![(candidate_hash_a, leaf_a.hash)], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_b, candidate_hash_f].into_iter().collect(), + 3, + vec![ + (candidate_hash_a, leaf_a.hash), + (candidate_hash_b, leaf_a.hash), + (candidate_hash_d, leaf_a.hash), + ], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_h].into_iter().collect(), + 4, + vec![ + (candidate_hash_c, leaf_a.hash), + (candidate_hash_h, leaf_a.hash), + (candidate_hash_i, leaf_a.hash), + (candidate_hash_j, leaf_a.hash), + ], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_e, candidate_hash_h].into_iter().collect(), + 2, + vec![(candidate_hash_a, leaf_a.hash), (candidate_hash_b, leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_c, candidate_hash_d].into_iter().collect(), + 2, + vec![(candidate_hash_h, leaf_a.hash), (candidate_hash_i, leaf_a.hash)], + ) + .await; + + // Parachain fork. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_b, candidate_hash_c].into_iter().collect(), + 1, + vec![], + ) + .await; + + // Non-existent candidate. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, CandidateHash(Hash::from_low_u64_be(100))] + .into_iter() + .collect(), + 2, + vec![(candidate_hash_b, leaf_a.hash), (candidate_hash_d, leaf_a.hash)], + ) + .await; + + // Requested count is zero. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::new(), + 0, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a].into_iter().collect(), + 0, + vec![], + ) + .await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + vec![candidate_hash_a, candidate_hash_b].into_iter().collect(), + 0, + vec![], + ) + .await; + + virtual_overseer + }); + + assert_eq!(view.active_leaves.len(), 1); + assert_eq!(view.candidate_storage.len(), 2); + // 10 candidates and 7 parents on para 1. + assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (7, 10)); + assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); + } +} + // Test depth query. #[test] fn check_hypothetical_frontier_query() { @@ -1257,8 +1684,8 @@ fn check_pvd_query() { assert_eq!(view.candidate_storage.len(), 2); } -// Test simultaneously activating and deactivating leaves, and simultaneously deactivating multiple -// leaves. +// Test simultaneously activating and deactivating leaves, and simultaneously deactivating +// multiple leaves. #[test] fn correctly_updates_leaves() { let test_state = TestState::default(); @@ -1448,12 +1875,13 @@ fn persists_pending_availability_candidate() { second_candidate(&mut virtual_overseer, candidate_b.clone()).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; - get_backable_candidate( + get_backable_candidates( &mut virtual_overseer, &leaf_b, para_id, - vec![candidate_hash_a], - Some((candidate_hash_b, leaf_b_hash)), + vec![candidate_hash_a].into_iter().collect(), + 1, + vec![(candidate_hash_b, leaf_b_hash)], ) .await; @@ -1512,12 +1940,13 @@ fn backwards_compatible() { second_candidate(&mut virtual_overseer, candidate_a.clone()).await; back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; - get_backable_candidate( + get_backable_candidates( &mut virtual_overseer, &leaf_a, para_id, - vec![], - Some((candidate_hash_a, candidate_relay_parent)), + Ancestors::new(), + 1, + vec![(candidate_hash_a, candidate_relay_parent)], ) .await; @@ -1537,7 +1966,15 @@ fn backwards_compatible() { ) .await; - get_backable_candidate(&mut virtual_overseer, &leaf_b, para_id, vec![], None).await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::new(), + 1, + vec![], + ) + .await; virtual_overseer }); @@ -1564,13 +2001,13 @@ fn uses_ancestry_only_within_session() { .await; assert_matches!( - virtual_overseer.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx)) - ) if parent == hash => { - tx.send(Ok(AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: ancestry_len })).unwrap(); - } - ); + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx)) + ) if parent == hash => { + tx.send(Ok(AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: ancestry_len + })).unwrap(); } + ); assert_matches!( virtual_overseer.recv().await, diff --git a/polkadot/node/core/provisioner/Cargo.toml b/polkadot/node/core/provisioner/Cargo.toml index 2d18bd29c1c097cccf5a94515a55d232b9263032..2a09e2b5b2cc83a0596c6f0ae153e66f8f6a1e66 100644 --- a/polkadot/node/core/provisioner/Cargo.toml +++ b/polkadot/node/core/provisioner/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-provisioner" -version = "1.0.0" +version = "7.0.0" description = "Responsible for assembling a relay chain block from a set of available parachain candidates" authors.workspace = true edition.workspace = true @@ -13,16 +13,18 @@ workspace = true bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } futures = "0.3.21" gum = { package = "tracing-gum", path = "../../gum" } -thiserror = "1.0.48" +thiserror = { workspace = true } polkadot-primitives = { path = "../../../primitives" } polkadot-node-primitives = { path = "../../primitives" } polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } futures-timer = "3.0.2" fatality = "0.0.6" +schnellru = "0.2.1" [dev-dependencies] sp-application-crypto = { path = "../../../../substrate/primitives/application-crypto" } sp-keystore = { path = "../../../../substrate/primitives/keystore" } polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../../primitives/test-helpers" } +rstest = "0.18.2" diff --git a/polkadot/node/core/provisioner/src/error.rs b/polkadot/node/core/provisioner/src/error.rs index 376d69f276fc892e92828bf994b29f847fae31d0..aae3234c3cc49d19bdaf20b87fa71cf3f3276bfe 100644 --- a/polkadot/node/core/provisioner/src/error.rs +++ b/polkadot/node/core/provisioner/src/error.rs @@ -44,14 +44,17 @@ pub enum Error { #[error("failed to get block number")] CanceledBlockNumber(#[source] oneshot::Canceled), + #[error("failed to get session index")] + CanceledSessionIndex(#[source] oneshot::Canceled), + #[error("failed to get backed candidates")] CanceledBackedCandidates(#[source] oneshot::Canceled), #[error("failed to get votes on dispute")] CanceledCandidateVotes(#[source] oneshot::Canceled), - #[error("failed to get backable candidate from prospective parachains")] - CanceledBackableCandidate(#[source] oneshot::Canceled), + #[error("failed to get backable candidates from prospective parachains")] + CanceledBackableCandidates(#[source] oneshot::Canceled), #[error(transparent)] ChainApi(#[from] ChainApiError), @@ -71,11 +74,6 @@ pub enum Error { #[error("failed to send return message with Inherents")] InherentDataReturnChannel, - #[error( - "backed candidate does not correspond to selected candidate; check logic in provisioner" - )] - BackedCandidateOrderingProblem, - #[fatal] #[error("Failed to spawn background task")] FailedToSpawnBackgroundTask, diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index 3970b8572612da827e7ddda18f46eadcf12f6906..c9ed873d3c25ae1a0fbe590a51412839e8105634 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -24,26 +24,29 @@ use futures::{ channel::oneshot, future::BoxFuture, prelude::*, stream::FuturesUnordered, FutureExt, }; use futures_timer::Delay; +use schnellru::{ByLength, LruMap}; use polkadot_node_subsystem::{ jaeger, messages::{ - CandidateBackingMessage, ChainApiMessage, ProspectiveParachainsMessage, ProvisionableData, - ProvisionerInherentData, ProvisionerMessage, RuntimeApiRequest, + Ancestors, CandidateBackingMessage, ChainApiMessage, ProspectiveParachainsMessage, + ProvisionableData, ProvisionerInherentData, ProvisionerMessage, RuntimeApiRequest, }, overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, PerLeafSpan, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util::{ has_required_runtime, request_availability_cores, request_persisted_validation_data, - runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, + request_session_index_for_child, + runtime::{prospective_parachains_mode, request_node_features, ProspectiveParachainsMode}, TimeoutExt, }; use polkadot_primitives::{ - BackedCandidate, BlockNumber, CandidateHash, CandidateReceipt, CoreState, Hash, Id as ParaId, - OccupiedCoreAssumption, SignedAvailabilityBitfield, ValidatorIndex, + vstaging::{node_features::FeatureIndex, NodeFeatures}, + BackedCandidate, BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, CoreState, Hash, + Id as ParaId, OccupiedCoreAssumption, SessionIndex, SignedAvailabilityBitfield, ValidatorIndex, }; -use std::collections::{BTreeMap, HashMap}; +use std::collections::{BTreeMap, HashMap, HashSet}; mod disputes; mod error; @@ -77,11 +80,18 @@ impl ProvisionerSubsystem { } } +/// Per-session info we need for the provisioner subsystem. +pub struct PerSession { + prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, +} + /// A per-relay-parent state for the provisioning subsystem. pub struct PerRelayParent { leaf: ActivatedLeaf, backed_candidates: Vec, prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, signed_bitfields: Vec, is_inherent_ready: bool, awaiting_inherent: Vec>, @@ -89,13 +99,14 @@ pub struct PerRelayParent { } impl PerRelayParent { - fn new(leaf: ActivatedLeaf, prospective_parachains_mode: ProspectiveParachainsMode) -> Self { + fn new(leaf: ActivatedLeaf, per_session: &PerSession) -> Self { let span = PerLeafSpan::new(leaf.span.clone(), "provisioner"); Self { leaf, backed_candidates: Vec::new(), - prospective_parachains_mode, + prospective_parachains_mode: per_session.prospective_parachains_mode, + elastic_scaling_mvp: per_session.elastic_scaling_mvp, signed_bitfields: Vec::new(), is_inherent_ready: false, awaiting_inherent: Vec::new(), @@ -124,10 +135,17 @@ impl ProvisionerSubsystem { async fn run(mut ctx: Context, metrics: Metrics) -> FatalResult<()> { let mut inherent_delays = InherentDelays::new(); let mut per_relay_parent = HashMap::new(); + let mut per_session = LruMap::new(ByLength::new(2)); loop { - let result = - run_iteration(&mut ctx, &mut per_relay_parent, &mut inherent_delays, &metrics).await; + let result = run_iteration( + &mut ctx, + &mut per_relay_parent, + &mut per_session, + &mut inherent_delays, + &metrics, + ) + .await; match result { Ok(()) => break, @@ -142,6 +160,7 @@ async fn run(mut ctx: Context, metrics: Metrics) -> FatalResult<()> { async fn run_iteration( ctx: &mut Context, per_relay_parent: &mut HashMap, + per_session: &mut LruMap, inherent_delays: &mut InherentDelays, metrics: &Metrics, ) -> Result<(), Error> { @@ -151,7 +170,7 @@ async fn run_iteration( // Map the error to ensure that the subsystem exits when the overseer is gone. match from_overseer.map_err(Error::OverseerExited)? { FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => - handle_active_leaves_update(ctx.sender(), update, per_relay_parent, inherent_delays).await?, + handle_active_leaves_update(ctx.sender(), update, per_relay_parent, per_session, inherent_delays).await?, FromOrchestra::Signal(OverseerSignal::BlockFinalized(..)) => {}, FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), FromOrchestra::Communication { msg } => { @@ -183,6 +202,7 @@ async fn handle_active_leaves_update( sender: &mut impl overseer::ProvisionerSenderTrait, update: ActiveLeavesUpdate, per_relay_parent: &mut HashMap, + per_session: &mut LruMap, inherent_delays: &mut InherentDelays, ) -> Result<(), Error> { gum::trace!(target: LOG_TARGET, "Handle ActiveLeavesUpdate"); @@ -191,10 +211,31 @@ async fn handle_active_leaves_update( } if let Some(leaf) = update.activated { + let session_index = request_session_index_for_child(leaf.hash, sender) + .await + .await + .map_err(Error::CanceledSessionIndex)??; + if per_session.get(&session_index).is_none() { + let prospective_parachains_mode = + prospective_parachains_mode(sender, leaf.hash).await?; + let elastic_scaling_mvp = request_node_features(leaf.hash, session_index, sender) + .await? + .unwrap_or(NodeFeatures::EMPTY) + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|b| *b) + .unwrap_or(false); + + per_session.insert( + session_index, + PerSession { prospective_parachains_mode, elastic_scaling_mvp }, + ); + } + + let session_info = per_session.get(&session_index).expect("Just inserted"); + gum::trace!(target: LOG_TARGET, leaf_hash=?leaf.hash, "Adding delay"); - let prospective_parachains_mode = prospective_parachains_mode(sender, leaf.hash).await?; let delay_fut = Delay::new(PRE_PROPOSE_TIMEOUT).map(move |_| leaf.hash).boxed(); - per_relay_parent.insert(leaf.hash, PerRelayParent::new(leaf, prospective_parachains_mode)); + per_relay_parent.insert(leaf.hash, PerRelayParent::new(leaf, session_info)); inherent_delays.push(delay_fut); } @@ -253,6 +294,7 @@ async fn send_inherent_data_bg( let signed_bitfields = per_relay_parent.signed_bitfields.clone(); let backed_candidates = per_relay_parent.backed_candidates.clone(); let mode = per_relay_parent.prospective_parachains_mode; + let elastic_scaling_mvp = per_relay_parent.elastic_scaling_mvp; let span = per_relay_parent.span.child("req-inherent-data"); let mut sender = ctx.sender().clone(); @@ -272,6 +314,7 @@ async fn send_inherent_data_bg( &signed_bitfields, &backed_candidates, mode, + elastic_scaling_mvp, return_senders, &mut sender, &metrics, @@ -383,6 +426,7 @@ async fn send_inherent_data( bitfields: &[SignedAvailabilityBitfield], candidates: &[CandidateReceipt], prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, return_senders: Vec>, from_job: &mut impl overseer::ProvisionerSenderTrait, metrics: &Metrics, @@ -434,6 +478,7 @@ async fn send_inherent_data( &bitfields, candidates, prospective_parachains_mode, + elastic_scaling_mvp, leaf.hash, from_job, ) @@ -558,6 +603,8 @@ async fn select_candidate_hashes_from_tracked( let mut selected_candidates = Vec::with_capacity(candidates.len().min(availability_cores.len())); + let mut selected_parachains = + HashSet::with_capacity(candidates.len().min(availability_cores.len())); gum::debug!( target: LOG_TARGET, @@ -591,6 +638,12 @@ async fn select_candidate_hashes_from_tracked( CoreState::Free => continue, }; + if selected_parachains.contains(&scheduled_core.para_id) { + // We already picked a candidate for this parachain. Elastic scaling only works with + // prospective parachains mode. + continue + } + let validation_data = match request_persisted_validation_data( relay_parent, scheduled_core.para_id, @@ -624,6 +677,7 @@ async fn select_candidate_hashes_from_tracked( "Selected candidate receipt", ); + selected_parachains.insert(candidate.descriptor.para_id); selected_candidates.push((candidate_hash, candidate.descriptor.relay_parent)); } } @@ -637,63 +691,93 @@ async fn select_candidate_hashes_from_tracked( /// Should be called when prospective parachains are enabled. async fn request_backable_candidates( availability_cores: &[CoreState], + elastic_scaling_mvp: bool, bitfields: &[SignedAvailabilityBitfield], relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result, Error> { let block_number = get_block_number_under_construction(relay_parent, sender).await?; - let mut selected_candidates = Vec::with_capacity(availability_cores.len()); + // Record how many cores are scheduled for each paraid. Use a BTreeMap because + // we'll need to iterate through them. + let mut scheduled_cores: BTreeMap = BTreeMap::new(); + // The on-chain ancestors of a para present in availability-cores. + let mut ancestors: HashMap = + HashMap::with_capacity(availability_cores.len()); for (core_idx, core) in availability_cores.iter().enumerate() { - let (para_id, required_path) = match core { + let core_idx = CoreIndex(core_idx as u32); + match core { CoreState::Scheduled(scheduled_core) => { - // The core is free, pick the first eligible candidate from - // the fragment tree. - (scheduled_core.para_id, Vec::new()) + *scheduled_cores.entry(scheduled_core.para_id).or_insert(0) += 1; }, CoreState::Occupied(occupied_core) => { - if bitfields_indicate_availability(core_idx, bitfields, &occupied_core.availability) - { + let is_available = bitfields_indicate_availability( + core_idx.0 as usize, + bitfields, + &occupied_core.availability, + ); + + if is_available { + ancestors + .entry(occupied_core.para_id()) + .or_default() + .insert(occupied_core.candidate_hash); + if let Some(ref scheduled_core) = occupied_core.next_up_on_available { - // The candidate occupying the core is available, choose its - // child in the fragment tree. - // - // TODO: doesn't work for on-demand parachains. We lean hard on the - // assumption that cores are fixed to specific parachains within a session. - // https://github.com/paritytech/polkadot/issues/5492 - (scheduled_core.para_id, vec![occupied_core.candidate_hash]) - } else { - continue - } - } else { - if occupied_core.time_out_at != block_number { - continue + // Request a new backable candidate for the newly scheduled para id. + *scheduled_cores.entry(scheduled_core.para_id).or_insert(0) += 1; } + } else if occupied_core.time_out_at <= block_number { + // Timed out before being available. + if let Some(ref scheduled_core) = occupied_core.next_up_on_time_out { // Candidate's availability timed out, practically same as scheduled. - (scheduled_core.para_id, Vec::new()) - } else { - continue + *scheduled_cores.entry(scheduled_core.para_id).or_insert(0) += 1; } + } else { + // Not timed out and not available. + ancestors + .entry(occupied_core.para_id()) + .or_default() + .insert(occupied_core.candidate_hash); } }, CoreState::Free => continue, }; + } - let response = get_backable_candidate(relay_parent, para_id, required_path, sender).await?; + let mut selected_candidates: Vec<(CandidateHash, Hash)> = + Vec::with_capacity(availability_cores.len()); - match response { - Some((hash, relay_parent)) => selected_candidates.push((hash, relay_parent)), - None => { - gum::debug!( - target: LOG_TARGET, - leaf_hash = ?relay_parent, - core = core_idx, - "No backable candidate returned by prospective parachains", - ); - }, + for (para_id, core_count) in scheduled_cores { + let para_ancestors = ancestors.remove(¶_id).unwrap_or_default(); + + // If elastic scaling MVP is disabled, only allow one candidate per parachain. + if !elastic_scaling_mvp && core_count > 1 { + continue } + + let response = get_backable_candidates( + relay_parent, + para_id, + para_ancestors, + core_count as u32, + sender, + ) + .await?; + + if response.is_empty() { + gum::debug!( + target: LOG_TARGET, + leaf_hash = ?relay_parent, + ?para_id, + "No backable candidate returned by prospective parachains", + ); + continue + } + + selected_candidates.extend(response.into_iter().take(core_count)); } Ok(selected_candidates) @@ -706,6 +790,7 @@ async fn select_candidates( bitfields: &[SignedAvailabilityBitfield], candidates: &[CandidateReceipt], prospective_parachains_mode: ProspectiveParachainsMode, + elastic_scaling_mvp: bool, relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result, Error> { @@ -715,7 +800,14 @@ async fn select_candidates( let selected_candidates = match prospective_parachains_mode { ProspectiveParachainsMode::Enabled { .. } => - request_backable_candidates(availability_cores, bitfields, relay_parent, sender).await?, + request_backable_candidates( + availability_cores, + elastic_scaling_mvp, + bitfields, + relay_parent, + sender, + ) + .await?, ProspectiveParachainsMode::Disabled => select_candidate_hashes_from_tracked( availability_cores, @@ -726,6 +818,7 @@ async fn select_candidates( ) .await?, }; + gum::debug!(target: LOG_TARGET, ?selected_candidates, "Got backable candidates"); // now get the backed candidates corresponding to these candidate receipts let (tx, rx) = oneshot::channel(); @@ -737,28 +830,10 @@ async fn select_candidates( gum::trace!(target: LOG_TARGET, leaf_hash=?relay_parent, "Got {} backed candidates", candidates.len()); - // `selected_candidates` is generated in ascending order by core index, and - // `GetBackedCandidates` _should_ preserve that property, but let's just make sure. - // - // We can't easily map from `BackedCandidate` to `core_idx`, but we know that every selected - // candidate maps to either 0 or 1 backed candidate, and the hashes correspond. Therefore, by - // checking them in order, we can ensure that the backed candidates are also in order. - let mut backed_idx = 0; - for selected in selected_candidates { - if selected.0 == - candidates.get(backed_idx).ok_or(Error::BackedCandidateOrderingProblem)?.hash() - { - backed_idx += 1; - } - } - if candidates.len() != backed_idx { - Err(Error::BackedCandidateOrderingProblem)?; - } - // keep only one candidate with validation code. let mut with_validation_code = false; candidates.retain(|c| { - if c.candidate.commitments.new_validation_code.is_some() { + if c.candidate().commitments.new_validation_code.is_some() { if with_validation_code { return false } @@ -796,25 +871,27 @@ async fn get_block_number_under_construction( } } -/// Requests backable candidate from Prospective Parachains based on -/// the given path in the fragment tree. -async fn get_backable_candidate( +/// Requests backable candidates from Prospective Parachains based on +/// the given ancestors in the fragment tree. The ancestors may not be ordered. +async fn get_backable_candidates( relay_parent: Hash, para_id: ParaId, - required_path: Vec, + ancestors: Ancestors, + count: u32, sender: &mut impl overseer::ProvisionerSenderTrait, -) -> Result, Error> { +) -> Result, Error> { let (tx, rx) = oneshot::channel(); sender - .send_message(ProspectiveParachainsMessage::GetBackableCandidate( + .send_message(ProspectiveParachainsMessage::GetBackableCandidates( relay_parent, para_id, - required_path, + count, + ancestors, tx, )) .await; - rx.await.map_err(Error::CanceledBackableCandidate) + rx.await.map_err(Error::CanceledBackableCandidates) } /// The availability bitfield for a given core is the transpose diff --git a/polkadot/node/core/provisioner/src/tests.rs b/polkadot/node/core/provisioner/src/tests.rs index 1d7bdfcfcb89c251cfdeb9c6669a5b07e8b05985..bdb4f85f4009bdfff879cefcb8928899c5b85c81 100644 --- a/polkadot/node/core/provisioner/src/tests.rs +++ b/polkadot/node/core/provisioner/src/tests.rs @@ -22,6 +22,9 @@ use polkadot_primitives::{OccupiedCore, ScheduledCore}; const MOCK_GROUP_SIZE: usize = 5; pub fn occupied_core(para_id: u32) -> CoreState { + let mut candidate_descriptor = dummy_candidate_descriptor(dummy_hash()); + candidate_descriptor.para_id = para_id.into(); + CoreState::Occupied(OccupiedCore { group_responsible: para_id.into(), next_up_on_available: None, @@ -29,7 +32,7 @@ pub fn occupied_core(para_id: u32) -> CoreState { time_out_at: 200_u32, next_up_on_time_out: None, availability: bitvec![u8, bitvec::order::Lsb0; 0; 32], - candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), + candidate_descriptor, candidate_hash: Default::default(), }) } @@ -254,10 +257,56 @@ mod select_candidates { use polkadot_primitives::{ BlockNumber, CandidateCommitments, CommittedCandidateReceipt, PersistedValidationData, }; + use rstest::rstest; const BLOCK_UNDER_PRODUCTION: BlockNumber = 128; - // For test purposes, we always return this set of availability cores: + fn dummy_candidate_template() -> CandidateReceipt { + let empty_hash = PersistedValidationData::::default().hash(); + + let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); + descriptor_template.persisted_validation_data_hash = empty_hash; + CandidateReceipt { + descriptor: descriptor_template, + commitments_hash: CandidateCommitments::default().hash(), + } + } + + fn make_candidates( + core_count: usize, + expected_backed_indices: Vec, + ) -> (Vec, Vec) { + let candidate_template = dummy_candidate_template(); + let candidates: Vec<_> = std::iter::repeat(candidate_template) + .take(core_count) + .enumerate() + .map(|(idx, mut candidate)| { + candidate.descriptor.para_id = idx.into(); + candidate + }) + .collect(); + + let expected_backed = expected_backed_indices + .iter() + .map(|&idx| candidates[idx].clone()) + .map(|c| { + BackedCandidate::new( + CommittedCandidateReceipt { + descriptor: c.descriptor.clone(), + commitments: Default::default(), + }, + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) + }) + .collect(); + let candidate_hashes = candidates.into_iter().map(|c| c.hash()).collect(); + + (candidate_hashes, expected_backed) + } + + // For testing only one core assigned to a parachain, we return this set of availability cores: // // [ // 0: Free, @@ -273,7 +322,73 @@ mod select_candidates { // 10: Occupied(both next_up set, not available, timeout), // 11: Occupied(next_up_on_available and available, but different successor para_id) // ] - fn mock_availability_cores() -> Vec { + fn mock_availability_cores_one_per_para() -> Vec { + use std::ops::Not; + use CoreState::{Free, Scheduled}; + + vec![ + // 0: Free, + Free, + // 1: Scheduled(default), + Scheduled(scheduled_core(1)), + // 2: Occupied(no next_up set), + occupied_core(2), + // 3: Occupied(next_up_on_available set but not available), + build_occupied_core(3, |core| { + core.next_up_on_available = Some(scheduled_core(3)); + }), + // 4: Occupied(next_up_on_available set and available), + build_occupied_core(4, |core| { + core.next_up_on_available = Some(scheduled_core(4)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(41)); + }), + // 5: Occupied(next_up_on_time_out set but not timeout), + build_occupied_core(5, |core| { + core.next_up_on_time_out = Some(scheduled_core(5)); + }), + // 6: Occupied(next_up_on_time_out set and timeout but available), + build_occupied_core(6, |core| { + core.next_up_on_time_out = Some(scheduled_core(6)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.availability = core.availability.clone().not(); + }), + // 7: Occupied(next_up_on_time_out set and timeout and not available), + build_occupied_core(7, |core| { + core.next_up_on_time_out = Some(scheduled_core(7)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(71)); + }), + // 8: Occupied(both next_up set, available), + build_occupied_core(8, |core| { + core.next_up_on_available = Some(scheduled_core(8)); + core.next_up_on_time_out = Some(scheduled_core(8)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(81)); + }), + // 9: Occupied(both next_up set, not available, no timeout), + build_occupied_core(9, |core| { + core.next_up_on_available = Some(scheduled_core(9)); + core.next_up_on_time_out = Some(scheduled_core(9)); + }), + // 10: Occupied(both next_up set, not available, timeout), + build_occupied_core(10, |core| { + core.next_up_on_available = Some(scheduled_core(10)); + core.next_up_on_time_out = Some(scheduled_core(10)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(101)); + }), + // 11: Occupied(next_up_on_available and available, but different successor para_id) + build_occupied_core(11, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + }), + ] + } + + // For test purposes with multiple possible cores assigned to a para, we always return this set + // of availability cores: + fn mock_availability_cores_multiple_per_para() -> Vec { use std::ops::Not; use CoreState::{Free, Scheduled}; @@ -292,6 +407,7 @@ mod select_candidates { build_occupied_core(4, |core| { core.next_up_on_available = Some(scheduled_core(4)); core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(41)); }), // 5: Occupied(next_up_on_time_out set but not timeout), build_occupied_core(5, |core| { @@ -307,12 +423,14 @@ mod select_candidates { build_occupied_core(7, |core| { core.next_up_on_time_out = Some(scheduled_core(7)); core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(71)); }), // 8: Occupied(both next_up set, available), build_occupied_core(8, |core| { core.next_up_on_available = Some(scheduled_core(8)); core.next_up_on_time_out = Some(scheduled_core(8)); core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(81)); }), // 9: Occupied(both next_up set, not available, no timeout), build_occupied_core(9, |core| { @@ -324,29 +442,133 @@ mod select_candidates { core.next_up_on_available = Some(scheduled_core(10)); core.next_up_on_time_out = Some(scheduled_core(10)); core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(101)); }), // 11: Occupied(next_up_on_available and available, but different successor para_id) build_occupied_core(11, |core| { core.next_up_on_available = Some(scheduled_core(12)); core.availability = core.availability.clone().not(); }), + // 12-14: Occupied(next_up_on_available and available, same para_id). + build_occupied_core(12, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(121)); + }), + build_occupied_core(12, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(122)); + }), + build_occupied_core(12, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(123)); + }), + // 15: Scheduled on same para_id as 12-14. + Scheduled(scheduled_core(12)), + // 16: Occupied(13, no next_up set, not available) + build_occupied_core(13, |core| { + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(131)); + }), + // 17: Occupied(13, no next_up set, available) + build_occupied_core(13, |core| { + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(132)); + }), + // 18: Occupied(13, next_up_on_available set to 13 but not available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(133)); + }), + // 19: Occupied(13, next_up_on_available set to 13 and available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(134)); + }), + // 20: Occupied(13, next_up_on_time_out set to 13 but not timeout) + build_occupied_core(13, |core| { + core.next_up_on_time_out = Some(scheduled_core(13)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(135)); + }), + // 21: Occupied(13, next_up_on_available set to 14 and available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(136)); + }), + // 22: Occupied(13, next_up_on_available set to 14 but not available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(137)); + }), + // 23: Occupied(13, both next_up set to 14, available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.next_up_on_time_out = Some(scheduled_core(14)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(138)); + }), + // 24: Occupied(13, both next_up set to 14, not available, timeout) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(14)); + core.next_up_on_time_out = Some(scheduled_core(14)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(1399)); + }), + // 25: Occupied(13, next_up_on_available and available, but successor para_id 15) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(15)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(139)); + }), + // 26: Occupied(15, next_up_on_available and available, but successor para_id 13) + build_occupied_core(15, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(151)); + }), + // 27: Occupied(15, both next_up, both available and timed out) + build_occupied_core(15, |core| { + core.next_up_on_available = Some(scheduled_core(15)); + core.availability = core.availability.clone().not(); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(152)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + }), + // 28: Occupied(13, both next_up set to 13, not available) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.next_up_on_time_out = Some(scheduled_core(13)); + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(1398)); + }), + // 29: Occupied(13, both next_up set to 13, not available, timeout) + build_occupied_core(13, |core| { + core.next_up_on_available = Some(scheduled_core(13)); + core.next_up_on_time_out = Some(scheduled_core(13)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.candidate_hash = CandidateHash(Hash::from_low_u64_be(1397)); + }), ] } async fn mock_overseer( mut receiver: mpsc::UnboundedReceiver, - expected: Vec, + mock_availability_cores: Vec, + mut expected: Vec, + mut expected_ancestors: HashMap, Ancestors>, prospective_parachains_mode: ProspectiveParachainsMode, ) { use ChainApiMessage::BlockNumber; use RuntimeApiMessage::Request; + let mut backed_iter = expected.clone().into_iter(); + + expected.sort_by_key(|c| c.candidate().descriptor.para_id); let mut candidates_iter = expected .iter() .map(|candidate| (candidate.hash(), candidate.descriptor().relay_parent)); - let mut backed_iter = expected.clone().into_iter(); - while let Some(from_job) = receiver.next().await { match from_job { AllMessages::ChainApi(BlockNumber(_relay_parent, tx)) => @@ -356,7 +578,7 @@ mod select_candidates { PersistedValidationDataReq(_para_id, _assumption, tx), )) => tx.send(Ok(Some(Default::default()))).unwrap(), AllMessages::RuntimeApi(Request(_parent_hash, AvailabilityCores(tx))) => - tx.send(Ok(mock_availability_cores())).unwrap(), + tx.send(Ok(mock_availability_cores.clone())).unwrap(), AllMessages::CandidateBacking(CandidateBackingMessage::GetBackedCandidates( hashes, sender, @@ -373,10 +595,36 @@ mod select_candidates { let _ = sender.send(response); }, AllMessages::ProspectiveParachains( - ProspectiveParachainsMessage::GetBackableCandidate(.., tx), + ProspectiveParachainsMessage::GetBackableCandidates( + _, + _para_id, + count, + actual_ancestors, + tx, + ), ) => match prospective_parachains_mode { ProspectiveParachainsMode::Enabled { .. } => { - let _ = tx.send(candidates_iter.next()); + assert!(count > 0); + let candidates = + (&mut candidates_iter).take(count as usize).collect::>(); + assert_eq!(candidates.len(), count as usize); + + if !expected_ancestors.is_empty() { + if let Some(expected_required_ancestors) = expected_ancestors.remove( + &(candidates + .clone() + .into_iter() + .take(actual_ancestors.len()) + .map(|(c_hash, _)| c_hash) + .collect::>()), + ) { + assert_eq!(expected_required_ancestors, actual_ancestors); + } else { + assert_eq!(actual_ancestors.len(), 0); + } + } + + let _ = tx.send(candidates); }, ProspectiveParachainsMode::Disabled => panic!("unexpected prospective parachains request"), @@ -384,19 +632,34 @@ mod select_candidates { _ => panic!("Unexpected message: {:?}", from_job), } } + + if let ProspectiveParachainsMode::Enabled { .. } = prospective_parachains_mode { + assert_eq!(candidates_iter.next(), None); + } + assert_eq!(expected_ancestors.len(), 0); } - #[test] - fn can_succeed() { + #[rstest] + #[case(ProspectiveParachainsMode::Disabled)] + #[case(ProspectiveParachainsMode::Enabled {max_candidate_depth: 0, allowed_ancestry_len: 0})] + fn can_succeed(#[case] prospective_parachains_mode: ProspectiveParachainsMode) { test_harness( - |r| mock_overseer(r, Vec::new(), ProspectiveParachainsMode::Disabled), + |r| { + mock_overseer( + r, + Vec::new(), + Vec::new(), + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { - let prospective_parachains_mode = ProspectiveParachainsMode::Disabled; select_candidates( &[], &[], &[], prospective_parachains_mode, + false, Default::default(), &mut tx, ) @@ -406,22 +669,22 @@ mod select_candidates { ) } - // this tests that only the appropriate candidates get selected. - // To accomplish this, we supply a candidate list containing one candidate per possible core; - // the candidate selection algorithm must filter them to the appropriate set - #[test] - fn selects_correct_candidates() { - let mock_cores = mock_availability_cores(); - - let empty_hash = PersistedValidationData::::default().hash(); - - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; - let candidate_template = CandidateReceipt { - descriptor: descriptor_template, - commitments_hash: CandidateCommitments::default().hash(), - }; - + // Test candidate selection when prospective parachains mode is disabled. + // This tests that only the appropriate candidates get selected when prospective parachains mode + // is disabled. To accomplish this, we supply a candidate list containing one candidate per + // possible core; the candidate selection algorithm must filter them to the appropriate set + #[rstest] + // why those particular indices? see the comments on mock_availability_cores_*() functions. + #[case(mock_availability_cores_one_per_para(), vec![1, 4, 7, 8, 10], true)] + #[case(mock_availability_cores_one_per_para(), vec![1, 4, 7, 8, 10], false)] + #[case(mock_availability_cores_multiple_per_para(), vec![1, 4, 7, 8, 10, 12, 13, 14, 15], true)] + #[case(mock_availability_cores_multiple_per_para(), vec![1, 4, 7, 8, 10, 12, 13, 14, 15], false)] + fn test_in_subsystem_selection( + #[case] mock_cores: Vec, + #[case] expected_candidates: Vec, + #[case] elastic_scaling_mvp: bool, + ) { + let candidate_template = dummy_candidate_template(); let candidates: Vec<_> = std::iter::repeat(candidate_template) .take(mock_cores.len()) .enumerate() @@ -448,31 +711,43 @@ mod select_candidates { }) .collect(); - // why those particular indices? see the comments on mock_availability_cores() let expected_candidates: Vec<_> = - [1, 4, 7, 8, 10].iter().map(|&idx| candidates[idx].clone()).collect(); + expected_candidates.into_iter().map(|idx| candidates[idx].clone()).collect(); let prospective_parachains_mode = ProspectiveParachainsMode::Disabled; let expected_backed = expected_candidates .iter() - .map(|c| BackedCandidate { - candidate: CommittedCandidateReceipt { - descriptor: c.descriptor.clone(), - commitments: Default::default(), - }, - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), + .map(|c| { + BackedCandidate::new( + CommittedCandidateReceipt { + descriptor: c.descriptor().clone(), + commitments: Default::default(), + }, + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) }) .collect(); + let mock_cores_clone = mock_cores.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_backed, + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { - let result = select_candidates( + let result: Vec = select_candidates( &mock_cores, &[], &candidates, prospective_parachains_mode, + elastic_scaling_mvp, Default::default(), &mut tx, ) @@ -481,7 +756,7 @@ mod select_candidates { result.into_iter().for_each(|c| { assert!( - expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_candidates.iter().any(|c2| c.candidate().corresponds_to(c2)), "Failed to find candidate: {:?}", c, ) @@ -490,20 +765,24 @@ mod select_candidates { ) } - #[test] - fn selects_max_one_code_upgrade() { - let mock_cores = mock_availability_cores(); + #[rstest] + #[case(ProspectiveParachainsMode::Disabled)] + #[case(ProspectiveParachainsMode::Enabled {max_candidate_depth: 0, allowed_ancestry_len: 0})] + fn selects_max_one_code_upgrade( + #[case] prospective_parachains_mode: ProspectiveParachainsMode, + ) { + let mock_cores = mock_availability_cores_one_per_para(); let empty_hash = PersistedValidationData::::default().hash(); // why those particular indices? see the comments on mock_availability_cores() - // the first candidate with code is included out of [1, 4, 7, 8, 10]. - let cores = [1, 4, 7, 8, 10]; + // the first candidate with code is included out of [1, 4, 7, 8, 10, 12]. + let cores = [1, 4, 7, 8, 10, 12]; let cores_with_code = [1, 4, 8]; - let expected_cores = [1, 7, 10]; + let expected_cores = [1, 7, 10, 12]; - let committed_receipts: Vec<_> = (0..mock_cores.len()) + let committed_receipts: Vec<_> = (0..=mock_cores.len()) .map(|i| { let mut descriptor = dummy_candidate_descriptor(dummy_hash()); descriptor.para_id = i.into(); @@ -527,10 +806,13 @@ mod select_candidates { // Build possible outputs from select_candidates let backed_candidates: Vec<_> = committed_receipts .iter() - .map(|committed_receipt| BackedCandidate { - candidate: committed_receipt.clone(), - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), + .map(|committed_receipt| { + BackedCandidate::new( + committed_receipt.clone(), + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) }) .collect(); @@ -541,27 +823,36 @@ mod select_candidates { let expected_backed_filtered: Vec<_> = expected_cores.iter().map(|&idx| candidates[idx].clone()).collect(); - let prospective_parachains_mode = ProspectiveParachainsMode::Disabled; + let mock_cores_clone = mock_cores.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_backed, + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { let result = select_candidates( &mock_cores, &[], &candidates, prospective_parachains_mode, + false, Default::default(), &mut tx, ) .await .unwrap(); - assert_eq!(result.len(), 3); + assert_eq!(result.len(), 4); result.into_iter().for_each(|c| { assert!( - expected_backed_filtered.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_backed_filtered.iter().any(|c2| c.candidate().corresponds_to(c2)), "Failed to find candidate: {:?}", c, ) @@ -570,63 +861,214 @@ mod select_candidates { ) } - #[test] - fn request_from_prospective_parachains() { - let mock_cores = mock_availability_cores(); - let empty_hash = PersistedValidationData::::default().hash(); + #[rstest] + #[case(true)] + #[case(false)] + fn request_from_prospective_parachains_one_core_per_para(#[case] elastic_scaling_mvp: bool) { + let mock_cores = mock_availability_cores_one_per_para(); - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; - let candidate_template = CandidateReceipt { - descriptor: descriptor_template, - commitments_hash: CandidateCommitments::default().hash(), - }; + // why those particular indices? see the comments on mock_availability_cores() + let expected_candidates: Vec<_> = vec![1, 4, 7, 8, 10, 12]; + let (candidates, expected_candidates) = + make_candidates(mock_cores.len() + 1, expected_candidates); - let candidates: Vec<_> = std::iter::repeat(candidate_template) - .take(mock_cores.len()) - .enumerate() - .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); - candidate - }) - .collect(); + // Expect prospective parachains subsystem requests. + let prospective_parachains_mode = + ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; + + let mut required_ancestors: HashMap, Ancestors> = HashMap::new(); + required_ancestors.insert( + vec![candidates[4]], + vec![CandidateHash(Hash::from_low_u64_be(41))].into_iter().collect(), + ); + required_ancestors.insert( + vec![candidates[8]], + vec![CandidateHash(Hash::from_low_u64_be(81))].into_iter().collect(), + ); + + let mock_cores_clone = mock_cores.clone(); + let expected_candidates_clone = expected_candidates.clone(); + test_harness( + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_candidates_clone, + required_ancestors, + prospective_parachains_mode, + ) + }, + |mut tx: TestSubsystemSender| async move { + let result = select_candidates( + &mock_cores, + &[], + &[], + prospective_parachains_mode, + elastic_scaling_mvp, + Default::default(), + &mut tx, + ) + .await + .unwrap(); + + assert_eq!(result.len(), expected_candidates.len()); + result.into_iter().for_each(|c| { + assert!( + expected_candidates + .iter() + .any(|c2| c.candidate().corresponds_to(&c2.receipt())), + "Failed to find candidate: {:?}", + c, + ) + }); + }, + ) + } + + #[test] + fn request_from_prospective_parachains_multiple_cores_per_para_elastic_scaling_mvp() { + let mock_cores = mock_availability_cores_multiple_per_para(); // why those particular indices? see the comments on mock_availability_cores() let expected_candidates: Vec<_> = - [1, 4, 7, 8, 10].iter().map(|&idx| candidates[idx].clone()).collect(); + vec![1, 4, 7, 8, 10, 12, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15]; // Expect prospective parachains subsystem requests. let prospective_parachains_mode = ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; - let expected_backed = expected_candidates - .iter() - .map(|c| BackedCandidate { - candidate: CommittedCandidateReceipt { - descriptor: c.descriptor.clone(), - commitments: Default::default(), - }, - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), - }) - .collect(); + let (candidates, expected_candidates) = + make_candidates(mock_cores.len(), expected_candidates); + + let mut required_ancestors: HashMap, Ancestors> = HashMap::new(); + required_ancestors.insert( + vec![candidates[4]], + vec![CandidateHash(Hash::from_low_u64_be(41))].into_iter().collect(), + ); + required_ancestors.insert( + vec![candidates[8]], + vec![CandidateHash(Hash::from_low_u64_be(81))].into_iter().collect(), + ); + required_ancestors.insert( + [12, 12, 12].iter().map(|&idx| candidates[idx]).collect::>(), + vec![ + CandidateHash(Hash::from_low_u64_be(121)), + CandidateHash(Hash::from_low_u64_be(122)), + CandidateHash(Hash::from_low_u64_be(123)), + ] + .into_iter() + .collect(), + ); + required_ancestors.insert( + [13, 13, 13].iter().map(|&idx| candidates[idx]).collect::>(), + (131..=139) + .map(|num| CandidateHash(Hash::from_low_u64_be(num))) + .chain(std::iter::once(CandidateHash(Hash::from_low_u64_be(1398)))) + .collect(), + ); + + required_ancestors.insert( + [15, 15].iter().map(|&idx| candidates[idx]).collect::>(), + vec![ + CandidateHash(Hash::from_low_u64_be(151)), + CandidateHash(Hash::from_low_u64_be(152)), + ] + .into_iter() + .collect(), + ); + + let mock_cores_clone = mock_cores.clone(); + let expected_candidates_clone = expected_candidates.clone(); + test_harness( + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_candidates, + required_ancestors, + prospective_parachains_mode, + ) + }, + |mut tx: TestSubsystemSender| async move { + let result = select_candidates( + &mock_cores, + &[], + &[], + prospective_parachains_mode, + true, + Default::default(), + &mut tx, + ) + .await + .unwrap(); + assert_eq!(result.len(), expected_candidates_clone.len()); + result.into_iter().for_each(|c| { + assert!( + expected_candidates_clone + .iter() + .any(|c2| c.candidate().corresponds_to(&c2.receipt())), + "Failed to find candidate: {:?}", + c, + ) + }); + }, + ) + } + + #[test] + fn request_from_prospective_parachains_multiple_cores_per_para_elastic_scaling_mvp_disabled() { + let mock_cores = mock_availability_cores_multiple_per_para(); + + // why those particular indices? see the comments on mock_availability_cores() + let expected_candidates: Vec<_> = vec![1, 4, 7, 8, 10]; + // Expect prospective parachains subsystem requests. + let prospective_parachains_mode = + ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; + + let (candidates, expected_candidates) = + make_candidates(mock_cores.len(), expected_candidates); + + let mut required_ancestors: HashMap, Ancestors> = HashMap::new(); + required_ancestors.insert( + vec![candidates[4]], + vec![CandidateHash(Hash::from_low_u64_be(41))].into_iter().collect(), + ); + required_ancestors.insert( + vec![candidates[8]], + vec![CandidateHash(Hash::from_low_u64_be(81))].into_iter().collect(), + ); + + let mock_cores_clone = mock_cores.clone(); + let expected_candidates_clone = expected_candidates.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_candidates, + required_ancestors, + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { let result = select_candidates( &mock_cores, &[], &[], prospective_parachains_mode, + false, Default::default(), &mut tx, ) .await .unwrap(); + assert_eq!(result.len(), expected_candidates_clone.len()); result.into_iter().for_each(|c| { assert!( - expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_candidates_clone + .iter() + .any(|c2| c.candidate().corresponds_to(&c2.receipt())), "Failed to find candidate: {:?}", c, ) @@ -637,18 +1079,11 @@ mod select_candidates { #[test] fn request_receipts_based_on_relay_parent() { - let mock_cores = mock_availability_cores(); - let empty_hash = PersistedValidationData::::default().hash(); - - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; - let candidate_template = CandidateReceipt { - descriptor: descriptor_template, - commitments_hash: CandidateCommitments::default().hash(), - }; + let mock_cores = mock_availability_cores_one_per_para(); + let candidate_template = dummy_candidate_template(); let candidates: Vec<_> = std::iter::repeat(candidate_template) - .take(mock_cores.len()) + .take(mock_cores.len() + 1) .enumerate() .map(|(idx, mut candidate)| { candidate.descriptor.para_id = idx.into(); @@ -659,31 +1094,44 @@ mod select_candidates { // why those particular indices? see the comments on mock_availability_cores() let expected_candidates: Vec<_> = - [1, 4, 7, 8, 10].iter().map(|&idx| candidates[idx].clone()).collect(); + [1, 4, 7, 8, 10, 12].iter().map(|&idx| candidates[idx].clone()).collect(); // Expect prospective parachains subsystem requests. let prospective_parachains_mode = ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, allowed_ancestry_len: 0 }; let expected_backed = expected_candidates .iter() - .map(|c| BackedCandidate { - candidate: CommittedCandidateReceipt { - descriptor: c.descriptor.clone(), - commitments: Default::default(), - }, - validity_votes: Vec::new(), - validator_indices: default_bitvec(MOCK_GROUP_SIZE), + .map(|c| { + BackedCandidate::new( + CommittedCandidateReceipt { + descriptor: c.descriptor().clone(), + commitments: Default::default(), + }, + Vec::new(), + default_bitvec(MOCK_GROUP_SIZE), + None, + ) }) .collect(); + let mock_cores_clone = mock_cores.clone(); test_harness( - |r| mock_overseer(r, expected_backed, prospective_parachains_mode), + |r| { + mock_overseer( + r, + mock_cores_clone, + expected_backed, + HashMap::new(), + prospective_parachains_mode, + ) + }, |mut tx: TestSubsystemSender| async move { let result = select_candidates( &mock_cores, &[], &[], prospective_parachains_mode, + false, Default::default(), &mut tx, ) @@ -692,7 +1140,7 @@ mod select_candidates { result.into_iter().for_each(|c| { assert!( - expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)), + expected_candidates.iter().any(|c2| c.candidate().corresponds_to(c2)), "Failed to find candidate: {:?}", c, ) diff --git a/polkadot/node/core/pvf-checker/Cargo.toml b/polkadot/node/core/pvf-checker/Cargo.toml index 274d8ee43bf1338094306aa78bb1991da5ff7477..f4f954e316c0b758a4cb1e94f80b729257349632 100644 --- a/polkadot/node/core/pvf-checker/Cargo.toml +++ b/polkadot/node/core/pvf-checker/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-pvf-checker" description = "Polkadot crate that implements the PVF pre-checking subsystem. Responsible for checking and voting for PVFs that are pending approval." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -11,7 +11,7 @@ workspace = true [dependencies] futures = "0.3.21" -thiserror = "1.0.48" +thiserror = { workspace = true } gum = { package = "tracing-gum", path = "../../gum" } polkadot-node-primitives = { path = "../../primitives" } diff --git a/polkadot/node/core/pvf-checker/src/lib.rs b/polkadot/node/core/pvf-checker/src/lib.rs index 043da00eba15bded8009eb0df47d493381c922c7..ae0fae6b4f9f7e2a4924effd4c3395d60b456f2f 100644 --- a/polkadot/node/core/pvf-checker/src/lib.rs +++ b/polkadot/node/core/pvf-checker/src/lib.rs @@ -49,29 +49,24 @@ use self::{ /// PVF pre-checking subsystem. pub struct PvfCheckerSubsystem { - enabled: bool, keystore: KeystorePtr, metrics: Metrics, } impl PvfCheckerSubsystem { - pub fn new(enabled: bool, keystore: KeystorePtr, metrics: Metrics) -> Self { - PvfCheckerSubsystem { enabled, keystore, metrics } + pub fn new(keystore: KeystorePtr, metrics: Metrics) -> Self { + PvfCheckerSubsystem { keystore, metrics } } } #[overseer::subsystem(PvfChecker, error=SubsystemError, prefix = self::overseer)] impl PvfCheckerSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { - if self.enabled { - let future = run(ctx, self.keystore, self.metrics) - .map_err(|e| SubsystemError::with_origin("pvf-checker", e)) - .boxed(); - - SpawnedSubsystem { name: "pvf-checker-subsystem", future } - } else { - polkadot_overseer::DummySubsystem.start(ctx) - } + let future = run(ctx, self.keystore, self.metrics) + .map_err(|e| SubsystemError::with_origin("pvf-checker", e)) + .boxed(); + + SpawnedSubsystem { name: "pvf-checker-subsystem", future } } } diff --git a/polkadot/node/core/pvf/Cargo.toml b/polkadot/node/core/pvf/Cargo.toml index 73facf8475a4832ec4e2885d6b4cdcb6f281c7ee..9ed64b88ffde857c6abe171a7d1cad7dd66dd509 100644 --- a/polkadot/node/core/pvf/Cargo.toml +++ b/polkadot/node/core/pvf/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-pvf" description = "Polkadot crate that implements the PVF validation host. Responsible for coordinating preparation and execution of PVFs." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -23,7 +23,7 @@ pin-project = "1.0.9" rand = "0.8.5" slotmap = "1.0" tempfile = "3.3.0" -thiserror = "1.0.31" +thiserror = { workspace = true } tokio = { version = "1.24.2", features = ["fs", "process"] } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } diff --git a/polkadot/node/core/pvf/README.md b/polkadot/node/core/pvf/README.md index 796e17c05faa47ceec455125ae29f7943ffa5740..5304b0720b2df614c005ff54eabb4d2c74b0bb4b 100644 --- a/polkadot/node/core/pvf/README.md +++ b/polkadot/node/core/pvf/README.md @@ -13,7 +13,7 @@ See also: Running `cargo test` in the `pvf/` directory will run unit and integration tests. -**Note:** some tests run only under Linux, amd64, and/or with the +**Note:** some tests run only under Linux, x86-64, and/or with the `ci-only-tests` feature enabled. See the general [Testing][testing] instructions for more information on @@ -34,8 +34,8 @@ RUST_LOG=parachain::pvf=trace zombienet --provider=native spawn zombienet_tests/ ## Testing on Linux Some of the PVF functionality, especially related to security, is Linux-only, -and some is amd64-only. If you touch anything security-related, make sure to -test on Linux amd64! If you're on a Mac, you can either run a VM or you can hire +and some is x86-64-only. If you touch anything security-related, make sure to +test on Linux x86-64! If you're on a Mac, you can either run a VM or you can hire a VPS and use the open-source tool [EternalTerminal][et] to connect to it.[^et] [^et]: Unlike ssh, ET preserves your session across disconnects, and unlike diff --git a/polkadot/node/core/pvf/common/Cargo.toml b/polkadot/node/core/pvf/common/Cargo.toml index f2ec505e623cdd5dfcd797c868f3aa3922265361..56bad9792fa0b6accea6460025c64c18e8e1e5d8 100644 --- a/polkadot/node/core/pvf/common/Cargo.toml +++ b/polkadot/node/core/pvf/common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-pvf-common" description = "Polkadot crate that contains functionality related to PVFs that is shared by the PVF host and the PVF workers." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,7 +15,7 @@ cpu-time = "1.0.0" futures = "0.3.21" gum = { package = "tracing-gum", path = "../../../gum" } libc = "0.2.152" -thiserror = "1.0.31" +thiserror = { workspace = true } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } @@ -27,6 +27,7 @@ sc-executor-common = { path = "../../../../../substrate/client/executor/common" sc-executor-wasmtime = { path = "../../../../../substrate/client/executor/wasmtime" } sp-core = { path = "../../../../../substrate/primitives/core" } +sp-crypto-hashing = { path = "../../../../../substrate/primitives/crypto/hashing" } sp-externalities = { path = "../../../../../substrate/primitives/externalities" } sp-io = { path = "../../../../../substrate/primitives/io" } sp-tracing = { path = "../../../../../substrate/primitives/tracing" } @@ -34,6 +35,8 @@ sp-tracing = { path = "../../../../../substrate/primitives/tracing" } [target.'cfg(target_os = "linux")'.dependencies] landlock = "0.3.0" nix = { version = "0.27.1", features = ["sched"] } + +[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies] seccompiler = "0.4.0" [dev-dependencies] diff --git a/polkadot/node/core/pvf/common/src/error.rs b/polkadot/node/core/pvf/common/src/error.rs index f8faefc24e65aadb2c50d2375e30fc7d579d6d37..cf274044456f3ea2db6770fbd61b3319f6420996 100644 --- a/polkadot/node/core/pvf/common/src/error.rs +++ b/polkadot/node/core/pvf/common/src/error.rs @@ -16,6 +16,7 @@ use crate::prepare::{PrepareSuccess, PrepareWorkerSuccess}; use parity_scale_codec::{Decode, Encode}; +pub use sc_executor_common::error::Error as ExecuteError; /// Result of PVF preparation from a worker, with checksum of the compiled PVF and stats of the /// preparation if successful. diff --git a/polkadot/node/core/pvf/common/src/execute.rs b/polkadot/node/core/pvf/common/src/execute.rs index 6b3becf524d71fa2d4f4bf574e0a86d09a7a60d9..18c97b03cbcd6fccc03cb27cb9f9e3aba9c7cf2a 100644 --- a/polkadot/node/core/pvf/common/src/execute.rs +++ b/polkadot/node/core/pvf/common/src/execute.rs @@ -40,6 +40,9 @@ pub enum WorkerResponse { }, /// The candidate is invalid. InvalidCandidate(String), + /// Instantiation of the WASM module instance failed during an execution. + /// Possibly related to local issues or dirty node update. May be retried with re-preparation. + RuntimeConstruction(String), /// The job timed out. JobTimedOut, /// The job process has died. We must kill the worker just in case. @@ -68,6 +71,9 @@ pub enum JobResponse { /// The result of parachain validation. result_descriptor: ValidationResult, }, + /// A possibly transient runtime instantiation error happened during the execution; may be + /// retried with re-preparation + RuntimeConstruction(String), /// The candidate is invalid. InvalidCandidate(String), } @@ -81,6 +87,15 @@ impl JobResponse { Self::InvalidCandidate(format!("{}: {}", ctx, msg)) } } + + /// Creates a may retry response from a context `ctx` and a message `msg` (which can be empty). + pub fn runtime_construction(ctx: &'static str, msg: &str) -> Self { + if msg.is_empty() { + Self::RuntimeConstruction(ctx.to_string()) + } else { + Self::RuntimeConstruction(format!("{}: {}", ctx, msg)) + } + } } /// An unexpected error occurred in the execution job process. Because this comes from the job, diff --git a/polkadot/node/core/pvf/common/src/executor_interface.rs b/polkadot/node/core/pvf/common/src/executor_interface.rs index 4cd2f5c85eec53661fde7625bb50a1d195381beb..b69a87890f6038199f98d5d7a5ab257c13125443 100644 --- a/polkadot/node/core/pvf/common/src/executor_interface.rs +++ b/polkadot/node/core/pvf/common/src/executor_interface.rs @@ -16,6 +16,7 @@ //! Interface to the Substrate Executor +use crate::error::ExecuteError; use polkadot_primitives::{ executor_params::{DEFAULT_LOGICAL_STACK_MAX, DEFAULT_NATIVE_STACK_MAX}, ExecutorParam, ExecutorParams, @@ -109,7 +110,7 @@ pub unsafe fn execute_artifact( compiled_artifact_blob: &[u8], executor_params: &ExecutorParams, params: &[u8], -) -> Result, String> { +) -> Result, ExecuteError> { let mut extensions = sp_externalities::Extensions::new(); extensions.register(sp_core::traits::ReadRuntimeVersionExt::new(ReadRuntimeVersion)); @@ -123,7 +124,6 @@ pub unsafe fn execute_artifact( Ok(Ok(ok)) => Ok(ok), Ok(Err(err)) | Err(err) => Err(err), } - .map_err(|err| format!("execute error: {:?}", err)) } /// Constructs the runtime for the given PVF, given the artifact bytes. diff --git a/polkadot/node/core/pvf/common/src/lib.rs b/polkadot/node/core/pvf/common/src/lib.rs index d891c06bf2ada3efedbdd9420119c61fb96f5791..15097dbd3af5c29d1fceabc8d04f6ee9a395ef30 100644 --- a/polkadot/node/core/pvf/common/src/lib.rs +++ b/polkadot/node/core/pvf/common/src/lib.rs @@ -86,3 +86,33 @@ pub fn framed_recv_blocking(r: &mut (impl Read + Unpin)) -> io::Result> r.read_exact(&mut buf)?; Ok(buf) } + +#[cfg(all(test, not(feature = "test-utils")))] +mod tests { + use super::*; + + #[test] + fn default_secure_status() { + let status = SecurityStatus::default(); + assert!( + !status.secure_validator_mode, + "secure_validator_mode is false for default security status" + ); + assert!( + !status.can_enable_landlock, + "can_enable_landlock is false for default security status" + ); + assert!( + !status.can_enable_seccomp, + "can_enable_seccomp is false for default security status" + ); + assert!( + !status.can_unshare_user_namespace_and_change_root, + "can_unshare_user_namespace_and_change_root is false for default security status" + ); + assert!( + !status.can_do_secure_clone, + "can_do_secure_clone is false for default security status" + ); + } +} diff --git a/polkadot/node/core/pvf/common/src/pvf.rs b/polkadot/node/core/pvf/common/src/pvf.rs index 2d8f6430187b2505e82517a0b93daf30b4e3a504..3f5b4d7ca70c14d53770af2dc470e4a2e5e572a4 100644 --- a/polkadot/node/core/pvf/common/src/pvf.rs +++ b/polkadot/node/core/pvf/common/src/pvf.rs @@ -18,7 +18,6 @@ use crate::prepare::PrepareJobKind; use parity_scale_codec::{Decode, Encode}; use polkadot_parachain_primitives::primitives::ValidationCodeHash; use polkadot_primitives::ExecutorParams; -use sp_core::blake2_256; use std::{ cmp::{Eq, PartialEq}, fmt, @@ -53,7 +52,7 @@ impl PvfPrepData { prep_kind: PrepareJobKind, ) -> Self { let code = Arc::new(code); - let code_hash = blake2_256(&code).into(); + let code_hash = sp_crypto_hashing::blake2_256(&code).into(); let executor_params = Arc::new(executor_params); Self { code, code_hash, executor_params, prep_timeout, prep_kind } } diff --git a/polkadot/node/core/pvf/execute-worker/Cargo.toml b/polkadot/node/core/pvf/execute-worker/Cargo.toml index a0a987fb439ae21e8a577021f0b6e30f63566c36..04a620573b2eceb311bdce67006e282778442191 100644 --- a/polkadot/node/core/pvf/execute-worker/Cargo.toml +++ b/polkadot/node/core/pvf/execute-worker/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-pvf-execute-worker" description = "Polkadot crate that contains the logic for executing PVFs. Used by the polkadot-execute-worker binary." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/node/core/pvf/execute-worker/src/lib.rs b/polkadot/node/core/pvf/execute-worker/src/lib.rs index 0cfa5a786946c7428ea8954515fe03807a90da32..bd7e76010a6dfedb339fd2fc1c17aa7f4007babe 100644 --- a/polkadot/node/core/pvf/execute-worker/src/lib.rs +++ b/polkadot/node/core/pvf/execute-worker/src/lib.rs @@ -16,7 +16,9 @@ //! Contains the logic for executing PVFs. Used by the polkadot-execute-worker binary. -pub use polkadot_node_core_pvf_common::executor_interface::execute_artifact; +pub use polkadot_node_core_pvf_common::{ + error::ExecuteError, executor_interface::execute_artifact, +}; // 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`. @@ -237,7 +239,9 @@ fn validate_using_artifact( // [`executor_interface::prepare`]. execute_artifact(compiled_artifact_blob, executor_params, params) } { - Err(err) => return JobResponse::format_invalid("execute", &err), + Err(ExecuteError::RuntimeConstruction(wasmerr)) => + return JobResponse::runtime_construction("execute", &wasmerr.to_string()), + Err(err) => return JobResponse::format_invalid("execute", &err.to_string()), Ok(d) => d, }; @@ -550,6 +554,8 @@ fn handle_parent_process( Ok(WorkerResponse::Ok { result_descriptor, duration: cpu_tv }) }, Ok(JobResponse::InvalidCandidate(err)) => Ok(WorkerResponse::InvalidCandidate(err)), + Ok(JobResponse::RuntimeConstruction(err)) => + Ok(WorkerResponse::RuntimeConstruction(err)), Err(job_error) => { gum::warn!( target: LOG_TARGET, diff --git a/polkadot/node/core/pvf/prepare-worker/Cargo.toml b/polkadot/node/core/pvf/prepare-worker/Cargo.toml index 7ed7d3295064e03aa765c4b518004185c60b0f8d..24efbec4be7d0552abdf9e7a9324636820347950 100644 --- a/polkadot/node/core/pvf/prepare-worker/Cargo.toml +++ b/polkadot/node/core/pvf/prepare-worker/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-core-pvf-prepare-worker" description = "Polkadot crate that contains the logic for preparing PVFs. Used by the polkadot-prepare-worker binary." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/node/core/pvf/src/artifacts.rs b/polkadot/node/core/pvf/src/artifacts.rs index 78dfe71adaddcd8d917a639591c102e08ebb7e4b..6288755526d497812027f7bf04e11e5a67ab799e 100644 --- a/polkadot/node/core/pvf/src/artifacts.rs +++ b/polkadot/node/core/pvf/src/artifacts.rs @@ -238,6 +238,14 @@ impl Artifacts { .is_none()); } + /// Remove artifact by its id. + pub fn remove(&mut self, artifact_id: ArtifactId) -> Option<(ArtifactId, PathBuf)> { + self.inner.remove(&artifact_id).and_then(|state| match state { + ArtifactState::Prepared { path, .. } => Some((artifact_id, path)), + _ => None, + }) + } + /// Remove artifacts older than the given TTL and return id and path of the removed ones. pub fn prune(&mut self, artifact_ttl: Duration) -> Vec<(ArtifactId, PathBuf)> { let now = SystemTime::now(); diff --git a/polkadot/node/core/pvf/src/error.rs b/polkadot/node/core/pvf/src/error.rs index 80d41d5c64be1a5cec31aee084a0f536ad0380bf..8dc96305eadb8f3ced1231889f5fa6c5ffaeac57 100644 --- a/polkadot/node/core/pvf/src/error.rs +++ b/polkadot/node/core/pvf/src/error.rs @@ -86,6 +86,10 @@ pub enum PossiblyInvalidError { /// vote invalid. #[error("possibly invalid: job error: {0}")] JobError(String), + /// Instantiation of the WASM module instance failed during an execution. + /// Possibly related to local issues or dirty node update. May be retried with re-preparation. + #[error("possibly invalid: runtime construction: {0}")] + RuntimeConstruction(String), } impl From for ValidationError { diff --git a/polkadot/node/core/pvf/src/execute/mod.rs b/polkadot/node/core/pvf/src/execute/mod.rs index c6d9cf90fa289601f89b0aad307483a002f89427..365e98196cae29dcb9c5085708cfaf7ee8b26238 100644 --- a/polkadot/node/core/pvf/src/execute/mod.rs +++ b/polkadot/node/core/pvf/src/execute/mod.rs @@ -23,4 +23,4 @@ mod queue; mod worker_interface; -pub use queue::{start, PendingExecutionRequest, ToQueue}; +pub use queue::{start, FromQueue, PendingExecutionRequest, ToQueue}; diff --git a/polkadot/node/core/pvf/src/execute/queue.rs b/polkadot/node/core/pvf/src/execute/queue.rs index aa91d11781fc625993484fa64ff8ebca4d65aeb2..bdc3c7327b06079eaaeebf737ffa7ca2c8c8270c 100644 --- a/polkadot/node/core/pvf/src/execute/queue.rs +++ b/polkadot/node/core/pvf/src/execute/queue.rs @@ -25,7 +25,7 @@ use crate::{ InvalidCandidate, PossiblyInvalidError, ValidationError, LOG_TARGET, }; use futures::{ - channel::mpsc, + channel::{mpsc, oneshot}, future::BoxFuture, stream::{FuturesUnordered, StreamExt as _}, Future, FutureExt, @@ -54,6 +54,12 @@ pub enum ToQueue { Enqueue { artifact: ArtifactPathId, pending_execution_request: PendingExecutionRequest }, } +/// A response from queue. +#[derive(Debug)] +pub enum FromQueue { + RemoveArtifact { artifact: ArtifactId, reply_to: oneshot::Sender<()> }, +} + /// An execution request that should execute the PVF (known in the context) and send the results /// to the given result sender. #[derive(Debug)] @@ -137,6 +143,8 @@ struct Queue { /// The receiver that receives messages to the pool. to_queue_rx: mpsc::Receiver, + /// The sender to send messages back to validation host. + from_queue_tx: mpsc::UnboundedSender, // Some variables related to the current session. program_path: PathBuf, @@ -161,6 +169,7 @@ impl Queue { node_version: Option, security_status: SecurityStatus, to_queue_rx: mpsc::Receiver, + from_queue_tx: mpsc::UnboundedSender, ) -> Self { Self { metrics, @@ -170,6 +179,7 @@ impl Queue { node_version, security_status, to_queue_rx, + from_queue_tx, queue: VecDeque::new(), mux: Mux::new(), workers: Workers { @@ -301,7 +311,7 @@ async fn handle_mux(queue: &mut Queue, event: QueueEvent) { handle_worker_spawned(queue, idle, handle, job); }, QueueEvent::StartWork(worker, outcome, artifact_id, result_tx) => { - handle_job_finish(queue, worker, outcome, artifact_id, result_tx); + handle_job_finish(queue, worker, outcome, artifact_id, result_tx).await; }, } } @@ -327,42 +337,69 @@ fn handle_worker_spawned( /// If there are pending jobs in the queue, schedules the next of them onto the just freed up /// worker. Otherwise, puts back into the available workers list. -fn handle_job_finish( +async fn handle_job_finish( queue: &mut Queue, worker: Worker, outcome: Outcome, artifact_id: ArtifactId, result_tx: ResultSender, ) { - let (idle_worker, result, duration) = match outcome { + let (idle_worker, result, duration, sync_channel) = match outcome { Outcome::Ok { result_descriptor, duration, idle_worker } => { // TODO: propagate the soft timeout - (Some(idle_worker), Ok(result_descriptor), Some(duration)) + (Some(idle_worker), Ok(result_descriptor), Some(duration), None) }, Outcome::InvalidCandidate { err, idle_worker } => ( Some(idle_worker), Err(ValidationError::Invalid(InvalidCandidate::WorkerReportedInvalid(err))), None, + None, ), - Outcome::InternalError { err } => (None, Err(ValidationError::Internal(err)), None), + Outcome::RuntimeConstruction { err, idle_worker } => { + // The task for artifact removal is executed concurrently with + // the message to the host on the execution result. + let (result_tx, result_rx) = oneshot::channel(); + queue + .from_queue_tx + .unbounded_send(FromQueue::RemoveArtifact { + artifact: artifact_id.clone(), + reply_to: result_tx, + }) + .expect("from execute queue receiver is listened by the host; qed"); + ( + Some(idle_worker), + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::RuntimeConstruction( + err, + ))), + None, + Some(result_rx), + ) + }, + Outcome::InternalError { err } => (None, Err(ValidationError::Internal(err)), 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. Outcome::HardTimeout => - (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None), + (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None), // "Maybe invalid" errors (will retry). Outcome::WorkerIntfErr => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)), None, + None, ), Outcome::JobDied { err } => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousJobDeath(err))), None, + None, + ), + Outcome::JobError { err } => ( + None, + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err))), + None, + None, ), - Outcome::JobError { err } => - (None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err))), None), }; queue.metrics.execute_finished(); @@ -386,6 +423,12 @@ fn handle_job_finish( ); } + if let Some(sync_channel) = sync_channel { + // err means the sender is dropped (the artifact is already removed from the cache) + // so that's legitimate to ignore the result + let _ = sync_channel.await; + } + // First we send the result. It may fail due to the other end of the channel being dropped, // that's legitimate and we don't treat that as an error. let _ = result_tx.send(result); @@ -521,8 +564,10 @@ pub fn start( spawn_timeout: Duration, node_version: Option, security_status: SecurityStatus, -) -> (mpsc::Sender, impl Future) { +) -> (mpsc::Sender, mpsc::UnboundedReceiver, impl Future) { let (to_queue_tx, to_queue_rx) = mpsc::channel(20); + let (from_queue_tx, from_queue_rx) = mpsc::unbounded(); + let run = Queue::new( metrics, program_path, @@ -532,7 +577,8 @@ pub fn start( node_version, security_status, to_queue_rx, + from_queue_tx, ) .run(); - (to_queue_tx, run) + (to_queue_tx, from_queue_rx, run) } diff --git a/polkadot/node/core/pvf/src/execute/worker_interface.rs b/polkadot/node/core/pvf/src/execute/worker_interface.rs index 9f7738f00e699ab981d7fa4396fcd09d5e1a4abe..db81da118d7b5563ea7b4bec1b6c76bd3bf842e2 100644 --- a/polkadot/node/core/pvf/src/execute/worker_interface.rs +++ b/polkadot/node/core/pvf/src/execute/worker_interface.rs @@ -87,6 +87,10 @@ pub enum Outcome { /// a trap. Errors related to the preparation process are not expected to be encountered by the /// execution workers. InvalidCandidate { err: String, idle_worker: IdleWorker }, + /// The error is probably transient. It may be for example + /// because the artifact was prepared with a Wasmtime version different from the version + /// in the current execution environment. + RuntimeConstruction { err: String, idle_worker: IdleWorker }, /// The execution time exceeded the hard limit. The worker is terminated. HardTimeout, /// An I/O error happened during communication with the worker. This may mean that the worker @@ -193,6 +197,10 @@ pub async fn start_work( err, idle_worker: IdleWorker { stream, pid, worker_dir }, }, + WorkerResponse::RuntimeConstruction(err) => Outcome::RuntimeConstruction { + err, + idle_worker: IdleWorker { stream, pid, worker_dir }, + }, WorkerResponse::JobTimedOut => Outcome::HardTimeout, WorkerResponse::JobDied { err, job_pid: _ } => Outcome::JobDied { err }, WorkerResponse::JobError(err) => Outcome::JobError { err }, diff --git a/polkadot/node/core/pvf/src/host.rs b/polkadot/node/core/pvf/src/host.rs index e215f11b91d396a1b9e5fa5710d34acf28ca9cff..8ec46f4b08f193082f1fcf8f6a08cae2ef261765 100644 --- a/polkadot/node/core/pvf/src/host.rs +++ b/polkadot/node/core/pvf/src/host.rs @@ -24,7 +24,7 @@ use crate::{ artifacts::{ArtifactId, ArtifactPathId, ArtifactState, Artifacts}, execute::{self, PendingExecutionRequest}, metrics::Metrics, - prepare, security, Priority, SecurityStatus, ValidationError, LOG_TARGET, + prepare, Priority, SecurityStatus, ValidationError, LOG_TARGET, }; use always_assert::never; use futures::{ @@ -225,10 +225,32 @@ pub async fn start( // Run checks for supported security features once per host startup. If some checks fail, warn // if Secure Validator Mode is disabled and return an error otherwise. - let security_status = match security::check_security_status(&config).await { + #[cfg(target_os = "linux")] + let security_status = match crate::security::check_security_status(&config).await { Ok(ok) => ok, Err(err) => return Err(SubsystemError::Context(err)), }; + #[cfg(not(target_os = "linux"))] + let security_status = if config.secure_validator_mode { + gum::error!( + target: LOG_TARGET, + "{}{}{}", + crate::SECURE_MODE_ERROR, + crate::SECURE_LINUX_NOTE, + crate::IGNORE_SECURE_MODE_TIP + ); + return Err(SubsystemError::Context( + "could not enable Secure Validator Mode for non-Linux; check logs".into(), + )); + } else { + gum::warn!( + target: LOG_TARGET, + "{}{}", + crate::SECURE_MODE_WARNING, + crate::SECURE_LINUX_NOTE, + ); + SecurityStatus::default() + }; let (to_host_tx, to_host_rx) = mpsc::channel(HOST_MESSAGE_QUEUE_SIZE); @@ -252,7 +274,7 @@ pub async fn start( from_prepare_pool, ); - let (to_execute_queue_tx, run_execute_queue) = execute::start( + let (to_execute_queue_tx, from_execute_queue_rx, run_execute_queue) = execute::start( metrics, config.execute_worker_program_path.to_owned(), config.cache_path.clone(), @@ -274,6 +296,7 @@ pub async fn start( to_prepare_queue_tx, from_prepare_queue_rx, to_execute_queue_tx, + from_execute_queue_rx, to_sweeper_tx, awaiting_prepare: AwaitingPrepare::default(), }) @@ -320,6 +343,8 @@ struct Inner { from_prepare_queue_rx: mpsc::UnboundedReceiver, to_execute_queue_tx: mpsc::Sender, + from_execute_queue_rx: mpsc::UnboundedReceiver, + to_sweeper_tx: mpsc::Sender, awaiting_prepare: AwaitingPrepare, @@ -336,6 +361,7 @@ async fn run( to_host_rx, from_prepare_queue_rx, mut to_prepare_queue_tx, + from_execute_queue_rx, mut to_execute_queue_tx, mut to_sweeper_tx, mut awaiting_prepare, @@ -362,10 +388,21 @@ async fn run( let mut to_host_rx = to_host_rx.fuse(); let mut from_prepare_queue_rx = from_prepare_queue_rx.fuse(); + let mut from_execute_queue_rx = from_execute_queue_rx.fuse(); loop { // biased to make it behave deterministically for tests. futures::select_biased! { + from_execute_queue_rx = from_execute_queue_rx.next() => { + let from_queue = break_if_fatal!(from_execute_queue_rx.ok_or(Fatal)); + let execute::FromQueue::RemoveArtifact { artifact, reply_to } = from_queue; + break_if_fatal!(handle_artifact_removal( + &mut to_sweeper_tx, + &mut artifacts, + artifact, + reply_to, + ).await); + }, () = cleanup_pulse.select_next_some() => { // `select_next_some` because we don't expect this to fail, but if it does, we // still don't fail. The trade-off is that the compiled cache will start growing @@ -839,6 +876,37 @@ async fn handle_cleanup_pulse( Ok(()) } +async fn handle_artifact_removal( + sweeper_tx: &mut mpsc::Sender, + artifacts: &mut Artifacts, + artifact_id: ArtifactId, + reply_to: oneshot::Sender<()>, +) -> Result<(), Fatal> { + let (artifact_id, path) = if let Some(artifact) = artifacts.remove(artifact_id) { + artifact + } else { + // if we haven't found the artifact by its id, + // it has been probably removed + // anyway with the randomness of the artifact name + // it is safe to ignore + return Ok(()); + }; + reply_to + .send(()) + .expect("the execute queue waits for the artifact remove confirmation; qed"); + // Thanks to the randomness of the artifact name (see + // `artifacts::generate_artifact_path`) there is no issue with any name conflict on + // future repreparation. + // So we can confirm the artifact removal already + gum::debug!( + target: LOG_TARGET, + validation_code_hash = ?artifact_id.code_hash, + "PVF pruning: pruning artifact by request from the execute queue", + ); + sweeper_tx.send(path).await.map_err(|_| Fatal)?; + Ok(()) +} + /// A simple task which sole purpose is to delete files thrown at it. async fn sweeper_task(mut sweeper_rx: mpsc::Receiver) { loop { @@ -946,6 +1014,8 @@ pub(crate) mod tests { to_prepare_queue_rx: mpsc::Receiver, from_prepare_queue_tx: mpsc::UnboundedSender, to_execute_queue_rx: mpsc::Receiver, + #[allow(unused)] + from_execute_queue_tx: mpsc::UnboundedSender, to_sweeper_rx: mpsc::Receiver, run: BoxFuture<'static, ()>, @@ -957,6 +1027,7 @@ pub(crate) mod tests { let (to_prepare_queue_tx, to_prepare_queue_rx) = mpsc::channel(10); let (from_prepare_queue_tx, from_prepare_queue_rx) = mpsc::unbounded(); let (to_execute_queue_tx, to_execute_queue_rx) = mpsc::channel(10); + let (from_execute_queue_tx, from_execute_queue_rx) = mpsc::unbounded(); let (to_sweeper_tx, to_sweeper_rx) = mpsc::channel(10); let run = run(Inner { @@ -967,6 +1038,7 @@ pub(crate) mod tests { to_prepare_queue_tx, from_prepare_queue_rx, to_execute_queue_tx, + from_execute_queue_rx, to_sweeper_tx, awaiting_prepare: AwaitingPrepare::default(), }) @@ -977,6 +1049,7 @@ pub(crate) mod tests { to_prepare_queue_rx, from_prepare_queue_tx, to_execute_queue_rx, + from_execute_queue_tx, to_sweeper_rx, run, } diff --git a/polkadot/node/core/pvf/src/lib.rs b/polkadot/node/core/pvf/src/lib.rs index 92263281eeab1c5f0cf96dad249db4c110c43114..462498fa8f6b108c6057b378d2a0b07face31ec9 100644 --- a/polkadot/node/core/pvf/src/lib.rs +++ b/polkadot/node/core/pvf/src/lib.rs @@ -97,6 +97,7 @@ mod host; mod metrics; mod prepare; mod priority; +#[cfg(target_os = "linux")] mod security; mod worker_interface; @@ -135,3 +136,22 @@ pub fn get_worker_version(worker_path: &Path) -> std::io::Result { .trim() .to_string()) } + +// Trying to run securely and some mandatory errors occurred. +pub(crate) const SECURE_MODE_ERROR: &'static str = + "🚨 Your system cannot securely run a validator. \ +\nRunning validation of malicious PVF code has a higher risk of compromising this machine."; +// Some errors occurred when running insecurely, or some optional errors occurred when running +// securely. +pub(crate) const SECURE_MODE_WARNING: &'static str = "🚨 Some security issues have been detected. \ +\nRunning validation of malicious PVF code has a higher risk of compromising this machine."; +// Message to be printed only when running securely and mandatory errors occurred. +pub(crate) const IGNORE_SECURE_MODE_TIP: &'static str = +"\nYou can ignore this error with the `--insecure-validator-i-know-what-i-do` \ +command line argument if you understand and accept the risks of running insecurely. \ +With this flag, security features are enabled on a best-effort basis, but not mandatory. \ +\nMore information: https://wiki.polkadot.network/docs/maintain-guides-secure-validator#secure-validator-mode"; +// Only Linux supports security features +#[cfg(not(target_os = "linux"))] +pub(crate) const SECURE_LINUX_NOTE: &'static str = "\nSecure mode is enabled only for Linux \ +\nand a full secure mode is enabled only for Linux x86-64."; diff --git a/polkadot/node/core/pvf/src/security.rs b/polkadot/node/core/pvf/src/security.rs index f62a232abf27a3745e0622094686d56b07e16cc1..733ef18bcadb497020db70f54db72d507a750c58 100644 --- a/polkadot/node/core/pvf/src/security.rs +++ b/polkadot/node/core/pvf/src/security.rs @@ -169,20 +169,6 @@ impl fmt::Display for SecureModeError { /// Print an error if Secure Validator Mode and some mandatory errors occurred, warn otherwise. fn print_secure_mode_error_or_warning(security_status: &FullSecurityStatus) { - // Trying to run securely and some mandatory errors occurred. - const SECURE_MODE_ERROR: &'static str = "🚨 Your system cannot securely run a validator. \ - \nRunning validation of malicious PVF code has a higher risk of compromising this machine."; - // Some errors occurred when running insecurely, or some optional errors occurred when running - // securely. - const SECURE_MODE_WARNING: &'static str = "🚨 Some security issues have been detected. \ - \nRunning validation of malicious PVF code has a higher risk of compromising this machine."; - // Message to be printed only when running securely and mandatory errors occurred. - const IGNORE_SECURE_MODE_TIP: &'static str = - "\nYou can ignore this error with the `--insecure-validator-i-know-what-i-do` \ - command line argument if you understand and accept the risks of running insecurely. \ - With this flag, security features are enabled on a best-effort basis, but not mandatory. \ - \nMore information: https://wiki.polkadot.network/docs/maintain-guides-secure-validator#secure-validator-mode"; - let all_errs_allowed = security_status.all_errs_allowed(); let errs_string = security_status.errs_string(); @@ -190,16 +176,16 @@ fn print_secure_mode_error_or_warning(security_status: &FullSecurityStatus) { gum::warn!( target: LOG_TARGET, "{}{}", - SECURE_MODE_WARNING, + crate::SECURE_MODE_WARNING, errs_string, ); } else { gum::error!( target: LOG_TARGET, "{}{}{}", - SECURE_MODE_ERROR, + crate::SECURE_MODE_ERROR, errs_string, - IGNORE_SECURE_MODE_TIP + crate::IGNORE_SECURE_MODE_TIP ); } } @@ -210,29 +196,25 @@ fn print_secure_mode_error_or_warning(security_status: &FullSecurityStatus) { /// to running the check in a worker, we try it... in a worker. The expected return status is 0 on /// success and -1 on failure. async fn check_can_unshare_user_namespace_and_change_root( - #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] prepare_worker_program_path: &Path, - #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] cache_path: &Path, + cache_path: &Path, ) -> SecureModeResult { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - let cache_dir_tempdir = tempfile::Builder::new() - .prefix("check-can-unshare-") - .tempdir_in(cache_path) - .map_err(|err| SecureModeError::CannotUnshareUserNamespaceAndChangeRoot( - format!("could not create a temporary directory in {:?}: {}", cache_path, err) - ))?; - spawn_process_for_security_check( - prepare_worker_program_path, - "--check-can-unshare-user-namespace-and-change-root", - &[cache_dir_tempdir.path()], - ).await.map_err(|err| SecureModeError::CannotUnshareUserNamespaceAndChangeRoot(err)) - } else { - Err(SecureModeError::CannotUnshareUserNamespaceAndChangeRoot( - "only available on Linux".into() + let cache_dir_tempdir = tempfile::Builder::new() + .prefix("check-can-unshare-") + .tempdir_in(cache_path) + .map_err(|err| { + SecureModeError::CannotUnshareUserNamespaceAndChangeRoot(format!( + "could not create a temporary directory in {:?}: {}", + cache_path, err )) - } - } + })?; + spawn_process_for_security_check( + prepare_worker_program_path, + "--check-can-unshare-user-namespace-and-change-root", + &[cache_dir_tempdir.path()], + ) + .await + .map_err(|err| SecureModeError::CannotUnshareUserNamespaceAndChangeRoot(err)) } /// Check if landlock is supported and return an error if not. @@ -240,25 +222,15 @@ async fn check_can_unshare_user_namespace_and_change_root( /// We do this check by spawning a new process and trying to sandbox it. To get as close as possible /// to running the check in a worker, we try it... in a worker. The expected return status is 0 on /// success and -1 on failure. -async fn check_landlock( - #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] - prepare_worker_program_path: &Path, -) -> SecureModeResult { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - let abi = polkadot_node_core_pvf_common::worker::security::landlock::LANDLOCK_ABI as u8; - spawn_process_for_security_check( - prepare_worker_program_path, - "--check-can-enable-landlock", - std::iter::empty::<&str>(), - ).await.map_err(|err| SecureModeError::CannotEnableLandlock { err, abi }) - } else { - Err(SecureModeError::CannotEnableLandlock { - err: "only available on Linux".into(), - abi: 0, - }) - } - } +async fn check_landlock(prepare_worker_program_path: &Path) -> SecureModeResult { + let abi = polkadot_node_core_pvf_common::worker::security::landlock::LANDLOCK_ABI as u8; + spawn_process_for_security_check( + prepare_worker_program_path, + "--check-can-enable-landlock", + std::iter::empty::<&str>(), + ) + .await + .map_err(|err| SecureModeError::CannotEnableLandlock { err, abi }) } /// Check if seccomp is supported and return an error if not. @@ -266,39 +238,23 @@ async fn check_landlock( /// We do this check by spawning a new process and trying to sandbox it. To get as close as possible /// to running the check in a worker, we try it... in a worker. The expected return status is 0 on /// success and -1 on failure. -async fn check_seccomp( - #[cfg_attr(not(all(target_os = "linux", target_arch = "x86_64")), allow(unused_variables))] - prepare_worker_program_path: &Path, -) -> SecureModeResult { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - cfg_if::cfg_if! { - if #[cfg(target_arch = "x86_64")] { - spawn_process_for_security_check( - prepare_worker_program_path, - "--check-can-enable-seccomp", - std::iter::empty::<&str>(), - ).await.map_err(|err| SecureModeError::CannotEnableSeccomp(err)) - } else { - Err(SecureModeError::CannotEnableSeccomp( - "only supported on CPUs from the x86_64 family (usually Intel or AMD)".into() - )) - } - } - } else { - cfg_if::cfg_if! { - if #[cfg(target_arch = "x86_64")] { - Err(SecureModeError::CannotEnableSeccomp( - "only supported on Linux".into() - )) - } else { - Err(SecureModeError::CannotEnableSeccomp( - "only supported on Linux and on CPUs from the x86_64 family (usually Intel or AMD).".into() - )) - } - } - } - } + +#[cfg(target_arch = "x86_64")] +async fn check_seccomp(prepare_worker_program_path: &Path) -> SecureModeResult { + spawn_process_for_security_check( + prepare_worker_program_path, + "--check-can-enable-seccomp", + std::iter::empty::<&str>(), + ) + .await + .map_err(|err| SecureModeError::CannotEnableSeccomp(err)) +} + +#[cfg(not(target_arch = "x86_64"))] +async fn check_seccomp(_: &Path) -> SecureModeResult { + Err(SecureModeError::CannotEnableSeccomp( + "only supported on CPUs from the x86_64 family (usually Intel or AMD)".into(), + )) } /// Check if we can call `clone` with all sandboxing flags, and return an error if not. @@ -306,26 +262,16 @@ async fn check_seccomp( /// We do this check by spawning a new process and trying to sandbox it. To get as close as possible /// to running the check in a worker, we try it... in a worker. The expected return status is 0 on /// success and -1 on failure. -async fn check_can_do_secure_clone( - #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] - prepare_worker_program_path: &Path, -) -> SecureModeResult { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - spawn_process_for_security_check( - prepare_worker_program_path, - "--check-can-do-secure-clone", - std::iter::empty::<&str>(), - ).await.map_err(|err| SecureModeError::CannotDoSecureClone(err)) - } else { - Err(SecureModeError::CannotDoSecureClone( - "only available on Linux".into() - )) - } - } +async fn check_can_do_secure_clone(prepare_worker_program_path: &Path) -> SecureModeResult { + spawn_process_for_security_check( + prepare_worker_program_path, + "--check-can-do-secure-clone", + std::iter::empty::<&str>(), + ) + .await + .map_err(|err| SecureModeError::CannotDoSecureClone(err)) } -#[cfg(target_os = "linux")] async fn spawn_process_for_security_check( prepare_worker_program_path: &Path, check_arg: &'static str, diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index bcc10749e746d05479997e161329bbc6b4c468ff..cdfbcd8e57855121a7128a0ed616df4f8ecb0cbd 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -21,13 +21,14 @@ use parity_scale_codec::Encode as _; #[cfg(all(feature = "ci-only-tests", target_os = "linux"))] use polkadot_node_core_pvf::SecurityStatus; use polkadot_node_core_pvf::{ - start, testing::build_workers_and_get_paths, Config, InvalidCandidate, Metrics, PrepareError, - PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, + start, testing::build_workers_and_get_paths, Config, InvalidCandidate, Metrics, + PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, + ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, }; use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult}; use polkadot_primitives::{ExecutorParam, ExecutorParams}; -use std::time::Duration; +use std::{io::Write, time::Duration}; use tokio::sync::Mutex; mod adder; @@ -352,10 +353,80 @@ async fn deleting_prepared_artifact_does_not_dispute() { ) .await; - match result { - Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)) => {}, - r => panic!("{:?}", r), + assert_matches!(result, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout))); +} + +// Test that corruption of a prepared artifact does not lead to a dispute when we try to execute it. +#[tokio::test] +async fn corrupted_prepared_artifact_does_not_dispute() { + let host = TestHost::new().await; + let cache_dir = host.cache_dir.path(); + + let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), Default::default()).await.unwrap(); + + // Manually corrupting the prepared artifact from disk. The in-memory artifacts table won't + // change. + let artifact_path = { + // Get the artifact path (asserting it exists). + let mut cache_dir: Vec<_> = std::fs::read_dir(cache_dir).unwrap().collect(); + // Should contain the artifact and the worker dir. + assert_eq!(cache_dir.len(), 2); + let mut artifact_path = cache_dir.pop().unwrap().unwrap(); + if artifact_path.path().is_dir() { + artifact_path = cache_dir.pop().unwrap().unwrap(); + } + + // Corrupt the artifact. + let mut f = std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(artifact_path.path()) + .unwrap(); + f.write_all(b"corrupted wasm").unwrap(); + f.flush().unwrap(); + artifact_path + }; + + assert!(artifact_path.path().exists()); + + // Try to validate, artifact should get removed because of the corruption. + let result = host + .validate_candidate( + 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(), + ) + .await; + + assert_matches!( + result, + Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::RuntimeConstruction(_))) + ); + + // because of RuntimeConstruction we may retry + host.precheck_pvf(halt::wasm_binary_unwrap(), Default::default()).await.unwrap(); + + // The actual artifact removal is done concurrently + // with sending of the result of the execution + // it is not a problem for further re-preparation as + // artifact filenames are random + for _ in 1..5 { + if !artifact_path.path().exists() { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; } + + assert!( + !artifact_path.path().exists(), + "the corrupted artifact ({}) should be deleted by the host", + artifact_path.path().display() + ); } #[tokio::test] diff --git a/polkadot/node/core/runtime-api/Cargo.toml b/polkadot/node/core/runtime-api/Cargo.toml index 07be4d128c25f6f71b8e39803f60857234f04198..2de3a6ee325ad6d08ea3377e8fccee56b7e505fa 100644 --- a/polkadot/node/core/runtime-api/Cargo.toml +++ b/polkadot/node/core/runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-core-runtime-api" -version = "1.0.0" +version = "7.0.0" description = "Wrapper around the parachain-related runtime APIs" authors.workspace = true edition.workspace = true diff --git a/polkadot/node/gum/Cargo.toml b/polkadot/node/gum/Cargo.toml index ccb21f64e6375547409f32b6de5257d2ff39b88d..0d887b9be5394c6b36f882d60f57d4ee2d9bf8eb 100644 --- a/polkadot/node/gum/Cargo.toml +++ b/polkadot/node/gum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-gum" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/node/gum/proc-macro/Cargo.toml b/polkadot/node/gum/proc-macro/Cargo.toml index 1f9c5b1b9186358637c58cb9a65adc8fc1292b3c..70126b4f43367ce11a1a23d462392b513ca1c028 100644 --- a/polkadot/node/gum/proc-macro/Cargo.toml +++ b/polkadot/node/gum/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-gum-proc-macro" -version = "1.0.0" +version = "5.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -16,8 +16,8 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -syn = { version = "2.0.48", features = ["extra-traits", "full"] } -quote = "1.0.28" +syn = { features = ["extra-traits", "full"], workspace = true } +quote = { workspace = true } proc-macro2 = "1.0.56" proc-macro-crate = "3.0.0" expander = "2.0.0" diff --git a/polkadot/node/jaeger/Cargo.toml b/polkadot/node/jaeger/Cargo.toml index 58fb983c84a1d66c5ec18af23b54368fd6bc2cad..23ab8f8421084751fa61d48bd3aa2b346e5a75c1 100644 --- a/polkadot/node/jaeger/Cargo.toml +++ b/polkadot/node/jaeger/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-jaeger" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -17,7 +17,7 @@ polkadot-primitives = { path = "../../primitives" } polkadot-node-primitives = { path = "../primitives" } sc-network = { path = "../../../substrate/client/network" } sp-core = { path = "../../../substrate/primitives/core" } -thiserror = "1.0.48" +thiserror = { workspace = true } tokio = "1.24.2" -log = "0.4.17" +log = { workspace = true, default-features = true } parity-scale-codec = { version = "3.6.1", default-features = false } diff --git a/polkadot/node/malus/Cargo.toml b/polkadot/node/malus/Cargo.toml index 6a3dff726ed328aecf37e4985ed0d7ec3eb07674..ea25b9077f3a7000cef89a328016fd95b80b9035 100644 --- a/polkadot/node/malus/Cargo.toml +++ b/polkadot/node/malus/Cargo.toml @@ -43,7 +43,7 @@ assert_matches = "1.5" async-trait = "0.1.74" sp-keystore = { path = "../../../substrate/primitives/keystore" } sp-core = { path = "../../../substrate/primitives/core" } -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" gum = { package = "tracing-gum", path = "../gum" } diff --git a/polkadot/node/malus/src/variants/back_garbage_candidate.rs b/polkadot/node/malus/src/variants/back_garbage_candidate.rs index aa904c37b80a59c9a77dfdb682c3d0e00b7ff833..b939a2151e2359828da74d317c9e2bc0af2c6e0c 100644 --- a/polkadot/node/malus/src/variants/back_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/back_garbage_candidate.rs @@ -19,16 +19,14 @@ //! candidates. use polkadot_cli::{ - prepared_overseer_builder, service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, - OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, - ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, - Cli, + validator_overseer_builder, Cli, }; use polkadot_node_subsystem::SpawnGlue; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use sp_core::traits::SpawnNamed; use crate::{ @@ -63,13 +61,10 @@ impl OverseerGen for BackGarbageCandidates { &self, connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { let spawner = args.spawner.clone(); @@ -80,11 +75,14 @@ impl OverseerGen for BackGarbageCandidates { SpawnGlue(spawner), ); - prepared_overseer_builder(args)? - .replace_candidate_validation(move |cv_subsystem| { - InterceptedSubsystem::new(cv_subsystem, validation_filter) - }) - .build_with_connector(connector) - .map_err(|e| e.into()) + validator_overseer_builder( + args, + ext_args.expect("Extended arguments required to build validator overseer are provided"), + )? + .replace_candidate_validation(move |cv_subsystem| { + InterceptedSubsystem::new(cv_subsystem, validation_filter) + }) + .build_with_connector(connector) + .map_err(|e| e.into()) } } diff --git a/polkadot/node/malus/src/variants/common.rs b/polkadot/node/malus/src/variants/common.rs index 011fcc80e37331909434ccd2fa1bfa1e96f48f35..eb6988f81811687442a36ae3f8ec225de48dd9df 100644 --- a/polkadot/node/malus/src/variants/common.rs +++ b/polkadot/node/malus/src/variants/common.rs @@ -23,10 +23,6 @@ use crate::{ use polkadot_node_core_candidate_validation::find_validation_data; use polkadot_node_primitives::{InvalidCandidate, ValidationResult}; -use polkadot_node_subsystem::{ - messages::{CandidateValidationMessage, ValidationFailed}, - overseer, -}; use polkadot_primitives::{ CandidateCommitments, CandidateDescriptor, CandidateReceipt, PersistedValidationData, diff --git a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs index 7f83c386090ed9d74d543201c984141e9c9e6152..7a95bdaead26e204bd4cd8b386ae02b5e12297f9 100644 --- a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs @@ -33,16 +33,14 @@ use futures::channel::oneshot; use polkadot_cli::{ - prepared_overseer_builder, service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, - OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, - ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, - Cli, + validator_overseer_builder, Cli, }; -use polkadot_node_subsystem::{messages::ApprovalVotingMessage, SpawnGlue}; -use polkadot_node_subsystem_types::{DefaultSubsystemClient, OverseerSignal}; +use polkadot_node_subsystem::SpawnGlue; +use polkadot_node_subsystem_types::{ChainApiBackend, OverseerSignal, RuntimeApiSubsystemClient}; use polkadot_node_subsystem_util::request_candidate_events; use polkadot_primitives::CandidateEvent; use sp_core::traits::SpawnNamed; @@ -237,13 +235,10 @@ impl OverseerGen for DisputeFinalizedCandidates { &self, connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { gum::info!( @@ -257,9 +252,12 @@ impl OverseerGen for DisputeFinalizedCandidates { dispute_offset: self.dispute_offset, }; - prepared_overseer_builder(args)? - .replace_approval_voting(move |cb| InterceptedSubsystem::new(cb, ancestor_disputer)) - .build_with_connector(connector) - .map_err(|e| e.into()) + validator_overseer_builder( + args, + ext_args.expect("Extended arguments required to build validator overseer are provided"), + )? + .replace_approval_voting(move |cb| InterceptedSubsystem::new(cb, ancestor_disputer)) + .build_with_connector(connector) + .map_err(|e| e.into()) } } diff --git a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs index fa3b0c38bc2f456580fdc41822a47c9f395946a0..a50fdce16e4ec41d6b9bea4f0190616f756d9193 100644 --- a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs @@ -23,16 +23,14 @@ #![allow(missing_docs)] use polkadot_cli::{ - prepared_overseer_builder, service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, - OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, - ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, - Cli, + validator_overseer_builder, Cli, }; use polkadot_node_subsystem::SpawnGlue; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use sp_core::traits::SpawnNamed; // Filter wrapping related types. @@ -80,13 +78,10 @@ impl OverseerGen for DisputeValidCandidates { &self, connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { let spawner = args.spawner.clone(); @@ -97,11 +92,14 @@ impl OverseerGen for DisputeValidCandidates { SpawnGlue(spawner.clone()), ); - prepared_overseer_builder(args)? - .replace_candidate_validation(move |cv_subsystem| { - InterceptedSubsystem::new(cv_subsystem, validation_filter) - }) - .build_with_connector(connector) - .map_err(|e| e.into()) + validator_overseer_builder( + args, + ext_args.expect("Extended arguments required to build validator overseer are provided"), + )? + .replace_candidate_validation(move |cv_subsystem| { + InterceptedSubsystem::new(cv_subsystem, validation_filter) + }) + .build_with_connector(connector) + .map_err(|e| e.into()) } } diff --git a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs index cf0ff5f809d8c31df07a13fb3eac0661f8486917..739ed40db362a2ae330f14b7a24cc77c1ab76159 100644 --- a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs @@ -22,18 +22,16 @@ #![allow(missing_docs)] +use futures::channel::oneshot; use polkadot_cli::{ - prepared_overseer_builder, service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, - OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, - ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, - Cli, + validator_overseer_builder, Cli, }; -use polkadot_node_core_candidate_validation::find_validation_data; use polkadot_node_primitives::{AvailableData, BlockData, PoV}; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use polkadot_primitives::{CandidateDescriptor, CandidateReceipt}; use polkadot_node_subsystem_util::request_validators; @@ -53,7 +51,7 @@ use crate::{ // Import extra types relevant to the particular // subsystem. -use polkadot_node_subsystem::{messages::CandidateBackingMessage, SpawnGlue}; +use polkadot_node_subsystem::SpawnGlue; use std::sync::Arc; @@ -83,7 +81,7 @@ where CandidateBackingMessage::Second( relay_parent, ref candidate, - ref _validation_data, + ref validation_data, ref _pov, ), } => { @@ -113,6 +111,7 @@ where let (sender, receiver) = std::sync::mpsc::channel(); let mut new_sender = subsystem_sender.clone(); let _candidate = candidate.clone(); + let validation_data = validation_data.clone(); self.spawner.spawn_blocking( "malus-get-validation-data", Some("malus"), @@ -125,22 +124,51 @@ where .unwrap() .len(); gum::trace!(target: MALUS, "Validators {}", n_validators); - match find_validation_data(&mut new_sender, &_candidate.descriptor()) - .await - { - Ok(Some((validation_data, validation_code))) => { - sender - .send(Some(( - validation_data, - validation_code, - n_validators, - ))) - .expect("channel is still open"); - }, - _ => { - sender.send(None).expect("channel is still open"); - }, - } + + let validation_code = { + let validation_code_hash = + _candidate.descriptor().validation_code_hash; + let (tx, rx) = oneshot::channel(); + new_sender + .send_message(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::ValidationCodeByHash( + validation_code_hash, + tx, + ), + )) + .await; + + let code = rx.await.expect("Querying the RuntimeApi should work"); + match code { + Err(e) => { + gum::error!( + target: MALUS, + ?validation_code_hash, + error = %e, + "Failed to fetch validation code", + ); + + sender.send(None).expect("channel is still open"); + return + }, + Ok(None) => { + gum::debug!( + target: MALUS, + ?validation_code_hash, + "Could not find validation code on chain", + ); + + sender.send(None).expect("channel is still open"); + return + }, + Ok(Some(c)) => c, + } + }; + + sender + .send(Some((validation_data, validation_code, n_validators))) + .expect("channel is still open"); }), ); @@ -266,13 +294,10 @@ impl OverseerGen for SuggestGarbageCandidates { &self, connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { gum::info!( @@ -293,12 +318,13 @@ impl OverseerGen for SuggestGarbageCandidates { SpawnGlue(args.spawner.clone()), ); - prepared_overseer_builder(args)? - .replace_candidate_backing(move |cb| InterceptedSubsystem::new(cb, note_candidate)) - .replace_candidate_validation(move |cb| { - InterceptedSubsystem::new(cb, validation_filter) - }) - .build_with_connector(connector) - .map_err(|e| e.into()) + validator_overseer_builder( + args, + ext_args.expect("Extended arguments required to build validator overseer are provided"), + )? + .replace_candidate_backing(move |cb| InterceptedSubsystem::new(cb, note_candidate)) + .replace_candidate_validation(move |cb| InterceptedSubsystem::new(cb, validation_filter)) + .build_with_connector(connector) + .map_err(|e| e.into()) } } diff --git a/polkadot/node/malus/src/variants/support_disabled.rs b/polkadot/node/malus/src/variants/support_disabled.rs index 5fb53be7774b3d6093f33916565731cff6bc863c..169c442db25b7426f44cdfbe2c4181233802a914 100644 --- a/polkadot/node/malus/src/variants/support_disabled.rs +++ b/polkadot/node/malus/src/variants/support_disabled.rs @@ -18,16 +18,14 @@ //! to always return an empty set of disabled validators. use polkadot_cli::{ - prepared_overseer_builder, service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, - OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, - ProvideRuntimeApi, + AuxStore, Error, ExtendedOverseerGenArgs, Overseer, OverseerConnector, OverseerGen, + OverseerGenArgs, OverseerHandle, }, - Cli, + validator_overseer_builder, Cli, }; use polkadot_node_subsystem::SpawnGlue; -use polkadot_node_subsystem_types::DefaultSubsystemClient; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; use sp_core::traits::SpawnNamed; use crate::interceptor::*; @@ -50,21 +48,21 @@ impl OverseerGen for SupportDisabled { &self, connector: OverseerConnector, args: OverseerGenArgs<'_, Spawner, RuntimeClient>, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { - prepared_overseer_builder(args)? - .replace_runtime_api(move |ra_subsystem| { - InterceptedSubsystem::new(ra_subsystem, IgnoreDisabled) - }) - .build_with_connector(connector) - .map_err(|e| e.into()) + validator_overseer_builder( + args, + ext_args.expect("Extended arguments required to build validator overseer are provided"), + )? + .replace_runtime_api(move |ra_subsystem| { + InterceptedSubsystem::new(ra_subsystem, IgnoreDisabled) + }) + .build_with_connector(connector) + .map_err(|e| e.into()) } } diff --git a/polkadot/node/metrics/Cargo.toml b/polkadot/node/metrics/Cargo.toml index 90d95a6e50a24a0b018f42122bb7b963712ff75c..c567278f70ea79740510aea4099170179f7e1f85 100644 --- a/polkadot/node/metrics/Cargo.toml +++ b/polkadot/node/metrics/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-metrics" description = "Subsystem metric helpers" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -24,7 +24,7 @@ sc-tracing = { path = "../../../substrate/client/tracing" } codec = { package = "parity-scale-codec", version = "3.6.1" } primitives = { package = "polkadot-primitives", path = "../../primitives" } bs58 = { version = "0.5.0", features = ["alloc"] } -log = "0.4.17" +log = { workspace = true, default-features = true } [dev-dependencies] assert_cmd = "2.0.4" diff --git a/polkadot/node/network/approval-distribution/Cargo.toml b/polkadot/node/network/approval-distribution/Cargo.toml index 6f261ae770011c0933f1775fb8b92f1f455baea7..2bc09c5f42acad52a8a96dab3916ed6314f27d56 100644 --- a/polkadot/node/network/approval-distribution/Cargo.toml +++ b/polkadot/node/network/approval-distribution/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-approval-distribution" -version = "1.0.0" +version = "7.0.0" description = "Polkadot Approval Distribution subsystem for the distribution of assignments and approvals for approval checks on candidates over the network." authors.workspace = true edition.workspace = true @@ -38,4 +38,4 @@ schnorrkel = { version = "0.11.4", default-features = false } rand_core = "0.6.2" rand_chacha = "0.3.1" env_logger = "0.9.0" -log = "0.4.17" +log = { 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 184ccb6b042d21ca8b3fe5d33118f39348a59541..f4e40270160cbf54cfeb516c5d7deeed59e7373a 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -36,7 +36,9 @@ use polkadot_node_network_protocol::{ UnifiedReputationChange as Rep, Versioned, View, }; use polkadot_node_primitives::approval::{ - v1::{AssignmentCertKind, BlockApprovalMeta, IndirectAssignmentCert}, + v1::{ + AssignmentCertKind, BlockApprovalMeta, IndirectAssignmentCert, IndirectSignedApprovalVote, + }, v2::{ AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2, @@ -284,12 +286,12 @@ struct AggressionConfig { } impl AggressionConfig { - /// Returns `true` if lag is past threshold depending on the aggression level - fn should_trigger_aggression(&self, approval_checking_lag: BlockNumber) -> bool { + /// Returns `true` if age is past threshold depending on the aggression level + fn should_trigger_aggression(&self, age: BlockNumber) -> bool { if let Some(t) = self.l1_threshold { - approval_checking_lag >= t + age >= t } else if let Some(t) = self.resend_unfinalized_period { - approval_checking_lag > 0 && approval_checking_lag % t == 0 + age > 0 && age % t == 0 } else { false } @@ -299,7 +301,7 @@ impl AggressionConfig { impl Default for AggressionConfig { fn default() -> Self { AggressionConfig { - l1_threshold: Some(13), + l1_threshold: Some(16), l2_threshold: Some(28), resend_unfinalized_period: Some(8), } @@ -667,9 +669,12 @@ impl State { rng: &mut (impl CryptoRng + Rng), ) { match event { - NetworkBridgeEvent::PeerConnected(peer_id, role, version, _) => { + NetworkBridgeEvent::PeerConnected(peer_id, role, version, authority_ids) => { + gum::trace!(target: LOG_TARGET, ?peer_id, ?role, ?authority_ids, "Peer connected"); + if let Some(authority_ids) = authority_ids { + self.topologies.update_authority_ids(peer_id, &authority_ids); + } // insert a blank view if none already present - gum::trace!(target: LOG_TARGET, ?peer_id, ?role, "Peer connected"); self.peer_views .entry(peer_id) .or_insert(PeerEntry { view: Default::default(), version }); @@ -716,8 +721,41 @@ impl State { NetworkBridgeEvent::PeerMessage(peer_id, message) => { self.process_incoming_peer_message(ctx, metrics, peer_id, message, rng).await; }, - NetworkBridgeEvent::UpdatedAuthorityIds { .. } => { - // The approval-distribution subsystem doesn't deal with `AuthorityDiscoveryId`s. + NetworkBridgeEvent::UpdatedAuthorityIds(peer_id, authority_ids) => { + gum::debug!(target: LOG_TARGET, ?peer_id, ?authority_ids, "Update Authority Ids"); + // If we learn about a new PeerId for an authority ids we need to try to route the + // messages that should have sent to that validator according to the topology. + if self.topologies.update_authority_ids(peer_id, &authority_ids) { + if let Some(PeerEntry { view, version }) = self.peer_views.get(&peer_id) { + let intersection = self + .blocks_by_number + .iter() + .filter(|(block_number, _)| *block_number > &view.finalized_number) + .flat_map(|(_, hashes)| { + hashes.iter().filter(|hash| { + self.blocks + .get(&hash) + .map(|block| block.known_by.get(&peer_id).is_some()) + .unwrap_or_default() + }) + }); + let view_intersection = + View::new(intersection.cloned(), view.finalized_number); + Self::unify_with_peer( + ctx.sender(), + metrics, + &mut self.blocks, + &self.topologies, + self.peer_views.len(), + peer_id, + *version, + view_intersection, + rng, + true, + ) + .await; + } + } }, } } @@ -789,6 +827,7 @@ impl State { *version, view_intersection, rng, + false, ) .await; } @@ -1026,17 +1065,17 @@ impl State { .await; }, Versioned::V3(protocol_v3::ApprovalDistributionMessage::Approvals(approvals)) => { - self.process_incoming_approvals(ctx, metrics, peer_id, approvals).await; + let sanitized_approvals = + self.sanitize_v2_approvals(peer_id, ctx.sender(), approvals).await; + self.process_incoming_approvals(ctx, metrics, peer_id, sanitized_approvals) + .await; }, Versioned::V1(protocol_v1::ApprovalDistributionMessage::Approvals(approvals)) | Versioned::V2(protocol_v2::ApprovalDistributionMessage::Approvals(approvals)) => { - self.process_incoming_approvals( - ctx, - metrics, - peer_id, - approvals.into_iter().map(|approval| approval.into()).collect::>(), - ) - .await; + let sanitized_approvals = + self.sanitize_v1_approvals(peer_id, ctx.sender(), approvals).await; + self.process_incoming_approvals(ctx, metrics, peer_id, sanitized_approvals) + .await; }, } } @@ -1101,6 +1140,7 @@ impl State { protocol_version, view, rng, + false, ) .await; } @@ -1838,6 +1878,7 @@ impl State { protocol_version: ProtocolVersion, view: View, rng: &mut (impl CryptoRng + Rng), + retry_known_blocks: bool, ) { metrics.on_unify_with_peer(); let _timer = metrics.time_unify_with_peer(); @@ -1856,10 +1897,12 @@ impl State { _ => break, }; - // Any peer which is in the `known_by` set has already been - // sent all messages it's meant to get for that block and all - // in-scope prior blocks. - if entry.known_by.contains_key(&peer_id) { + // Any peer which is in the `known_by` see and we know its peer_id authorithy id + // mapping has already been sent all messages it's meant to get for that block and + // all in-scope prior blocks. In case, we just learnt about its peer_id + // authorithy-id mapping we have to retry sending the messages that should be sent + // to it for all un-finalized blocks. + if entry.known_by.contains_key(&peer_id) && !retry_known_blocks { break } @@ -1961,6 +2004,16 @@ impl State { } } + // It is very important that aggression starts with oldest unfinalized block, rather than oldest + // unapproved block. Using the gossip approach to distribute potentially + // missing votes to validators requires that we always trigger on finality lag, even if + // we have have the approval lag value. The reason for this, is to avoid finality stall + // when more than 1/3 nodes go offline for a period o time. When they come back + // there wouldn't get any of the approvals since the on-line nodes would never trigger + // aggression as they have approved all the candidates and don't detect any approval lag. + // + // In order to switch to using approval lag as a trigger we need a request/response protocol + // to fetch votes from validators rather than use gossip. async fn enable_aggression( &mut self, ctx: &mut Context, @@ -1968,27 +2021,27 @@ impl State { metrics: &Metrics, ) { let config = self.aggression_config.clone(); + let min_age = self.blocks_by_number.iter().next().map(|(num, _)| num); + let max_age = self.blocks_by_number.iter().rev().next().map(|(num, _)| num); + + // Return if we don't have at least 1 block. + let (min_age, max_age) = match (min_age, max_age) { + (Some(min), Some(max)) => (*min, *max), + _ => return, // empty. + }; + + let age = max_age.saturating_sub(min_age); - if !self.aggression_config.should_trigger_aggression(self.approval_checking_lag) { + // Trigger on approval checking lag. + if !self.aggression_config.should_trigger_aggression(age) { gum::trace!( target: LOG_TARGET, approval_checking_lag = self.approval_checking_lag, + age, "Aggression not enabled", ); return } - - let max_age = self.blocks_by_number.iter().rev().next().map(|(num, _)| num); - - let max_age = match max_age { - Some(max) => *max, - _ => return, // empty. - }; - - // Since we have the approval checking lag, we need to set the `min_age` accordingly to - // enable aggresion for the oldest block that is not approved. - let min_age = max_age.saturating_sub(self.approval_checking_lag); - gum::debug!(target: LOG_TARGET, min_age, max_age, "Aggression enabled",); adjust_required_routing_and_propagate( @@ -2044,8 +2097,7 @@ impl State { let mut new_required_routing = *required_routing; - if config.l1_threshold.as_ref().map_or(false, |t| &self.approval_checking_lag >= t) - { + if config.l1_threshold.as_ref().map_or(false, |t| &age >= t) { // Message originator sends to everyone. if local && new_required_routing != RequiredRouting::All { metrics.on_aggression_l1(); @@ -2053,8 +2105,7 @@ impl State { } } - if config.l2_threshold.as_ref().map_or(false, |t| &self.approval_checking_lag >= t) - { + if config.l2_threshold.as_ref().map_or(false, |t| &age >= t) { // Message originator sends to everyone. Everyone else sends to XY. if !local && new_required_routing != RequiredRouting::GridXY { metrics.on_aggression_l2(); @@ -2147,6 +2198,60 @@ impl State { sanitized_assignments } + + // Filter out obviously invalid candidate indicies. + async fn sanitize_v1_approvals( + &mut self, + peer_id: PeerId, + sender: &mut impl overseer::ApprovalDistributionSenderTrait, + approval: Vec, + ) -> Vec { + let mut sanitized_approvals = Vec::new(); + for approval in approval.into_iter() { + if approval.candidate_index as usize > MAX_BITFIELD_SIZE { + // Punish the peer for the invalid message. + modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD) + .await; + gum::debug!( + target: LOG_TARGET, + block_hash = ?approval.block_hash, + candidate_index = ?approval.candidate_index, + "Bad approval v1, invalid candidate index" + ); + } else { + sanitized_approvals.push(approval.into()) + } + } + + sanitized_approvals + } + + // Filter out obviously invalid candidate indicies. + async fn sanitize_v2_approvals( + &mut self, + peer_id: PeerId, + sender: &mut impl overseer::ApprovalDistributionSenderTrait, + approval: Vec, + ) -> Vec { + let mut sanitized_approvals = Vec::new(); + for approval in approval.into_iter() { + if approval.candidate_indices.len() as usize > MAX_BITFIELD_SIZE { + // Punish the peer for the invalid message. + modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD) + .await; + gum::debug!( + target: LOG_TARGET, + block_hash = ?approval.block_hash, + candidate_indices_len = ?approval.candidate_indices.len(), + "Bad approval v2, invalid candidate indices size" + ); + } else { + sanitized_approvals.push(approval) + } + } + + sanitized_approvals + } } // This adjusts the required routing of messages in blocks that pass the block filter diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 7d933e2047f26033a3c23cbee2bc82595a62f839..6c88dd53ad364ab0eabed065c9098c1dfcdd81d9 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -130,7 +130,7 @@ fn make_peers_and_authority_ids(n: usize) -> Vec<(PeerId, AuthorityDiscoveryId)> fn make_gossip_topology( session: SessionIndex, - all_peers: &[(PeerId, AuthorityDiscoveryId)], + all_peers: &[(Option, AuthorityDiscoveryId)], neighbors_x: &[usize], neighbors_y: &[usize], local_index: usize, @@ -153,7 +153,7 @@ fn make_gossip_topology( assert!(all_peers.len() >= grid_size); let peer_info = |i: usize| TopologyPeerInfo { - peer_ids: vec![all_peers[i].0], + peer_ids: all_peers[i].0.into_iter().collect_vec(), validator_index: ValidatorIndex::from(i as u32), discovery_id: all_peers[i].1.clone(), }; @@ -396,7 +396,15 @@ fn try_import_the_same_assignment() { // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under // testing. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + 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 { @@ -485,7 +493,15 @@ fn try_import_the_same_assignment_v2() { // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under // testing. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + 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 { @@ -724,8 +740,16 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { 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 neigboor to current node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + 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 { @@ -822,8 +846,16 @@ fn import_approval_happy_path_v1_v2_peers() { 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 neighboors to the node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + 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); @@ -936,8 +968,16 @@ fn import_approval_happy_path_v2() { 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 neighboors to the node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + 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); @@ -1039,8 +1079,16 @@ fn multiple_assignments_covered_with_one_approval_vote() { 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 neighboors to the node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + 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 @@ -1221,8 +1269,16 @@ fn unify_with_peer_multiple_assignments_covered_with_one_approval_vote() { 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 neighboors to the node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + 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 @@ -1571,8 +1627,16 @@ fn update_peer_view() { 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 neigboor to current node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + 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)); @@ -1694,6 +1758,183 @@ fn update_peer_view() { assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none()); } +// Tests that updating the known peer_id for a given authorithy updates the topology +// and sends the required messages +#[test] +fn update_peer_authority_id() { + 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 peers = make_peers_and_authority_ids(8); + let neighbour_x_index = 0; + let neighbour_y_index = 2; + let local_index = 1; + // X neighbour, we simulate that PeerId is not known in the beginining. + let neighbour_x = peers.get(neighbour_x_index).unwrap().0; + // Y neighbour, we simulate that PeerId is not known in the beginining. + 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 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 neigboor 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 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_a.into(), 0.into()), + ) + .await; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.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; + + 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(), + ), + ), + ) + .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(), + ), + ), + ) + .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" + ); + + virtual_overseer + }); +} + /// E.g. if someone copies the keys... #[test] fn import_remotely_then_locally() { @@ -1808,8 +2049,16 @@ fn sends_assignments_even_when_state_is_approved() { 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 neigboor to current node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + 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; @@ -1900,8 +2149,16 @@ fn sends_assignments_even_when_state_is_approved_v2() { 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 neigboor to current node. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + 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]; @@ -2080,12 +2337,17 @@ fn propagates_locally_generated_assignment_to_both_dimensions() { 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, + &peers_with_optional_peer_id, &[0, 10, 20, 30, 40, 60, 70, 80], &[50, 51, 52, 53, 54, 55, 56, 57], 1, @@ -2197,10 +2459,21 @@ fn propagates_assignments_along_unshared_dimension() { 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, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), ) .await; @@ -2339,13 +2612,16 @@ fn propagates_to_required_after_connect() { 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, + &peers_with_optional_peer_id, &[0, 10, 20, 30, 40, 60, 70, 80], &[50, 51, 52, 53, 54, 55, 56, 57], 1, @@ -2533,11 +2809,20 @@ fn sends_to_more_peers_after_getting_topology() { 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, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), ) .await; @@ -2636,11 +2921,20 @@ fn originator_aggression_l1() { 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, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), ) .await; @@ -2795,11 +3089,20 @@ fn non_originator_aggression_l1() { // 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, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), ) .await; @@ -2900,11 +3203,20 @@ fn non_originator_aggression_l2() { // 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, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), ) .await; @@ -3046,11 +3358,20 @@ fn resends_messages_periodically() { 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, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), ) .await; @@ -3190,7 +3511,15 @@ fn import_versioned_approval() { // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under // testing. - setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + 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 { @@ -3292,6 +3621,33 @@ fn import_versioned_approval() { 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; + + expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; + virtual_overseer }); } diff --git a/polkadot/node/network/availability-distribution/Cargo.toml b/polkadot/node/network/availability-distribution/Cargo.toml index 0d52c013a33c3f22e6a8e82b75ec9e6331f9e28d..182d92cb163179eea85cafeb1c605763ac18cde1 100644 --- a/polkadot/node/network/availability-distribution/Cargo.toml +++ b/polkadot/node/network/availability-distribution/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-availability-distribution" description = "The Availability Distribution subsystem. Requests the required availability data. Also distributes availability data and chunks to requesters." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -21,7 +21,7 @@ polkadot-node-subsystem-util = { path = "../../subsystem-util" } polkadot-node-primitives = { path = "../../primitives" } sp-core = { path = "../../../../substrate/primitives/core", features = ["std"] } sp-keystore = { path = "../../../../substrate/primitives/keystore" } -thiserror = "1.0.48" +thiserror = { workspace = true } rand = "0.8.5" derive_more = "0.99.17" schnellru = "0.2.1" @@ -36,3 +36,14 @@ sc-network = { path = "../../../../substrate/client/network" } futures-timer = "3.0.2" assert_matches = "1.4.0" polkadot-primitives-test-helpers = { path = "../../../primitives/test-helpers" } +polkadot-subsystem-bench = { path = "../../subsystem-bench" } + + +[[test]] +name = "availability-distribution-regression-bench" +path = "tests/availability-distribution-regression-bench.rs" +harness = false +required-features = ["subsystem-benchmarks"] + +[features] +subsystem-benchmarks = [] diff --git a/polkadot/node/network/availability-distribution/tests/availability-distribution-regression-bench.rs b/polkadot/node/network/availability-distribution/tests/availability-distribution-regression-bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..f2872f3c72ba47dafa87dadefce96cf35b97646f --- /dev/null +++ b/polkadot/node/network/availability-distribution/tests/availability-distribution-regression-bench.rs @@ -0,0 +1,113 @@ +// 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 . + +//! availability-read regression tests +//! +//! TODO: Explain the test case after configuration adjusted to Kusama +//! +//! Subsystems involved: +//! - availability-distribution +//! - bitfield-distribution +//! - availability-store + +use polkadot_subsystem_bench::{ + availability::{benchmark_availability_write, prepare_test, TestDataAvailability, TestState}, + configuration::{PeerLatency, TestConfiguration}, + usage::BenchmarkUsage, +}; + +const BENCH_COUNT: usize = 3; +const WARM_UP_COUNT: usize = 20; +const WARM_UP_PRECISION: f64 = 0.01; + +fn main() -> Result<(), String> { + let mut messages = vec![]; + + // TODO: Adjust the test configurations to Kusama values + let mut config = TestConfiguration::default(); + config.latency = Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 }); + config.n_validators = 1000; + config.n_cores = 200; + config.max_validators_per_core = 5; + config.min_pov_size = 5120; + config.max_pov_size = 5120; + config.peer_bandwidth = 52428800; + config.bandwidth = 52428800; + config.connectivity = 75; + config.num_blocks = 3; + config.generate_pov_sizes(); + + warm_up(config.clone())?; + let usage = benchmark(config.clone()); + + messages.extend(usage.check_network_usage(&[ + ("Received from peers", 4330.0, 0.05), + ("Sent to peers", 15900.0, 0.05), + ])); + messages.extend(usage.check_cpu_usage(&[ + ("availability-distribution", 0.025, 0.05), + ("bitfield-distribution", 0.085, 0.05), + ("availability-store", 0.180, 0.05), + ])); + + if messages.is_empty() { + Ok(()) + } else { + eprintln!("{}", messages.join("\n")); + Err("Regressions found".to_string()) + } +} + +fn warm_up(config: TestConfiguration) -> Result<(), String> { + println!("Warming up..."); + let mut prev_run: Option = None; + for _ in 0..WARM_UP_COUNT { + let curr = run(config.clone()); + if let Some(ref prev) = prev_run { + let av_distr_diff = + curr.cpu_usage_diff(prev, "availability-distribution").expect("Must exist"); + let bitf_distr_diff = + curr.cpu_usage_diff(prev, "bitfield-distribution").expect("Must exist"); + let av_store_diff = + curr.cpu_usage_diff(prev, "availability-store").expect("Must exist"); + if av_distr_diff < WARM_UP_PRECISION && + bitf_distr_diff < WARM_UP_PRECISION && + av_store_diff < WARM_UP_PRECISION + { + return Ok(()) + } + } + prev_run = Some(curr); + } + + Err("Can't warm up".to_string()) +} + +fn benchmark(config: TestConfiguration) -> BenchmarkUsage { + println!("Benchmarking..."); + let usages: Vec = (0..BENCH_COUNT).map(|_| run(config.clone())).collect(); + let usage = BenchmarkUsage::average(&usages); + println!("{}", usage); + usage +} + +fn run(config: TestConfiguration) -> BenchmarkUsage { + let mut state = TestState::new(&config); + let (mut env, _protocol_config) = + prepare_test(config.clone(), &mut state, TestDataAvailability::Write, false); + env.runtime() + .block_on(benchmark_availability_write("data_availability_write", &mut env, state)) +} diff --git a/polkadot/node/network/availability-recovery/Cargo.toml b/polkadot/node/network/availability-recovery/Cargo.toml index ec1cf475302b353a85021d637ff319dba61bb733..12b6ce7a05715480bc6ca79d67742bc089b5f67c 100644 --- a/polkadot/node/network/availability-recovery/Cargo.toml +++ b/polkadot/node/network/availability-recovery/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-availability-recovery" description = "The Availability Recovery subsystem. Handles requests for recovering the availability data of included candidates." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,7 +15,7 @@ tokio = "1.24.2" schnellru = "0.2.1" rand = "0.8.5" fatality = "0.0.6" -thiserror = "1.0.48" +thiserror = { workspace = true } async-trait = "0.1.74" gum = { package = "tracing-gum", path = "../../gum" } @@ -32,7 +32,7 @@ sc-network = { path = "../../../../substrate/client/network" } assert_matches = "1.4.0" env_logger = "0.9.0" futures-timer = "3.0.2" -log = "0.4.17" +log = { workspace = true, default-features = true } sp-core = { path = "../../../../substrate/primitives/core" } sp-keyring = { path = "../../../../substrate/primitives/keyring" } @@ -41,6 +41,13 @@ sc-network = { path = "../../../../substrate/client/network" } polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } polkadot-primitives-test-helpers = { path = "../../../primitives/test-helpers" } +polkadot-subsystem-bench = { path = "../../subsystem-bench" } + +[[test]] +name = "availability-recovery-regression-bench" +path = "tests/availability-recovery-regression-bench.rs" +harness = false +required-features = ["subsystem-benchmarks"] [features] subsystem-benchmarks = [] diff --git a/polkadot/node/network/availability-recovery/tests/availability-recovery-regression-bench.rs b/polkadot/node/network/availability-recovery/tests/availability-recovery-regression-bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..beb063e7ae0ddd2c158667287a4800509cbbdc68 --- /dev/null +++ b/polkadot/node/network/availability-recovery/tests/availability-recovery-regression-bench.rs @@ -0,0 +1,103 @@ +// 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 . + +//! availability-write regression tests +//! +//! TODO: Explain the test case after configuration adjusted to Kusama +//! +//! Subsystems involved: +//! - availability-recovery + +use polkadot_subsystem_bench::{ + availability::{ + benchmark_availability_read, prepare_test, DataAvailabilityReadOptions, + TestDataAvailability, TestState, + }, + configuration::{PeerLatency, TestConfiguration}, + usage::BenchmarkUsage, +}; + +const BENCH_COUNT: usize = 3; +const WARM_UP_COUNT: usize = 10; +const WARM_UP_PRECISION: f64 = 0.01; + +fn main() -> Result<(), String> { + let mut messages = vec![]; + + // TODO: Adjust the test configurations to Kusama values + let options = DataAvailabilityReadOptions { fetch_from_backers: true }; + let mut config = TestConfiguration::default(); + config.latency = Some(PeerLatency { mean_latency_ms: 100, std_dev: 1.0 }); + config.n_validators = 300; + config.n_cores = 20; + config.min_pov_size = 5120; + config.max_pov_size = 5120; + config.peer_bandwidth = 52428800; + config.bandwidth = 52428800; + config.num_blocks = 3; + config.connectivity = 90; + config.generate_pov_sizes(); + + warm_up(config.clone(), options.clone())?; + let usage = benchmark(config.clone(), options.clone()); + + messages.extend(usage.check_network_usage(&[ + ("Received from peers", 102400.000, 0.05), + ("Sent to peers", 0.335, 0.05), + ])); + messages.extend(usage.check_cpu_usage(&[("availability-recovery", 3.850, 0.05)])); + + if messages.is_empty() { + Ok(()) + } else { + eprintln!("{}", messages.join("\n")); + Err("Regressions found".to_string()) + } +} + +fn warm_up(config: TestConfiguration, options: DataAvailabilityReadOptions) -> Result<(), String> { + println!("Warming up..."); + let mut prev_run: Option = None; + for _ in 0..WARM_UP_COUNT { + let curr = run(config.clone(), options.clone()); + if let Some(ref prev) = prev_run { + let diff = curr.cpu_usage_diff(prev, "availability-recovery").expect("Must exist"); + if diff < WARM_UP_PRECISION { + return Ok(()) + } + } + prev_run = Some(curr); + } + + Err("Can't warm up".to_string()) +} + +fn benchmark(config: TestConfiguration, options: DataAvailabilityReadOptions) -> BenchmarkUsage { + println!("Benchmarking..."); + let usages: Vec = + (0..BENCH_COUNT).map(|_| run(config.clone(), options.clone())).collect(); + let usage = BenchmarkUsage::average(&usages); + println!("{}", usage); + usage +} + +fn run(config: TestConfiguration, options: DataAvailabilityReadOptions) -> BenchmarkUsage { + let mut state = TestState::new(&config); + let (mut env, _protocol_config) = + prepare_test(config.clone(), &mut state, TestDataAvailability::Read(options), false); + env.runtime() + .block_on(benchmark_availability_read("data_availability_read", &mut env, state)) +} diff --git a/polkadot/node/network/bitfield-distribution/Cargo.toml b/polkadot/node/network/bitfield-distribution/Cargo.toml index 5c5bd875a96f82e1aee3d08ad5df73d87913c650..0ddb5f643b89d382855de92fe450a5807e6e21ac 100644 --- a/polkadot/node/network/bitfield-distribution/Cargo.toml +++ b/polkadot/node/network/bitfield-distribution/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-availability-bitfield-distribution" -version = "1.0.0" +version = "7.0.0" description = "Polkadot Bitfiled Distribution subsystem, which gossips signed availability bitfields used to compactly determine which backed candidates are available or not based on a 2/3+ quorum." authors.workspace = true edition.workspace = true @@ -29,7 +29,7 @@ sp-authority-discovery = { path = "../../../../substrate/primitives/authority-di sp-keystore = { path = "../../../../substrate/primitives/keystore" } sp-keyring = { path = "../../../../substrate/primitives/keyring" } maplit = "1.0.2" -log = "0.4.17" +log = { workspace = true, default-features = true } env_logger = "0.9.0" assert_matches = "1.4.0" rand_chacha = "0.3.1" diff --git a/polkadot/node/network/bitfield-distribution/src/lib.rs b/polkadot/node/network/bitfield-distribution/src/lib.rs index 76baf499cad7a63263a7bbd42968e44328c29387..029401e0bd51478ad7144e2790c6abe3db52ce3c 100644 --- a/polkadot/node/network/bitfield-distribution/src/lib.rs +++ b/polkadot/node/network/bitfield-distribution/src/lib.rs @@ -800,8 +800,11 @@ async fn handle_network_msg( }, NetworkBridgeEvent::PeerMessage(remote, message) => process_incoming_peer_message(ctx, state, metrics, remote, message, rng).await, - NetworkBridgeEvent::UpdatedAuthorityIds { .. } => { - // The bitfield-distribution subsystem doesn't deal with `AuthorityDiscoveryId`s. + NetworkBridgeEvent::UpdatedAuthorityIds(peer_id, authority_ids) => { + state + .topologies + .get_current_topology_mut() + .update_authority_ids(peer_id, &authority_ids); }, } } diff --git a/polkadot/node/network/bridge/Cargo.toml b/polkadot/node/network/bridge/Cargo.toml index ee4033b00993218796a31bba852520be50af3370..2e889fc30eb90063f07c05e34e51d863a3d32f59 100644 --- a/polkadot/node/network/bridge/Cargo.toml +++ b/polkadot/node/network/bridge/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-network-bridge" -version = "1.0.0" +version = "7.0.0" description = "The Network Bridge Subsystem — protocol multiplexer for Polkadot." authors.workspace = true edition.workspace = true @@ -25,7 +25,7 @@ polkadot-overseer = { path = "../../overseer" } parking_lot = "0.12.1" bytes = "1" fatality = "0.0.6" -thiserror = "1" +thiserror = { workspace = true } [dev-dependencies] assert_matches = "1.4.0" diff --git a/polkadot/node/network/collator-protocol/Cargo.toml b/polkadot/node/network/collator-protocol/Cargo.toml index bcf4f74132fc0dd0ac85c84e8655abc37d5218ac..f0f8be0f7bab240a928be9f30a18ca10b14eb16d 100644 --- a/polkadot/node/network/collator-protocol/Cargo.toml +++ b/polkadot/node/network/collator-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-collator-protocol" -version = "1.0.0" +version = "7.0.0" description = "Polkadot Collator Protocol subsystem. Allows collators and validators to talk to each other." authors.workspace = true edition.workspace = true @@ -25,11 +25,11 @@ polkadot-node-primitives = { path = "../../primitives" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } polkadot-node-subsystem = { path = "../../subsystem" } fatality = "0.0.6" -thiserror = "1.0.48" +thiserror = { workspace = true } tokio-util = "0.7.1" [dev-dependencies] -log = "0.4.17" +log = { workspace = true, default-features = true } env_logger = "0.9.0" assert_matches = "1.4.0" diff --git a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs index 5b88efc99d8307594c5ac726d1af5ab141e4718d..1533f2eda5a57d332ea899e1dd81764f31c86dad 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs @@ -90,8 +90,7 @@ impl ValidatorGroupsBuffer { } } - /// Returns discovery ids of validators we have at least one advertised-but-not-fetched - /// collation for. + /// Returns discovery ids of validators we are assigned to in this backing group window. pub fn validators_to_connect(&self) -> Vec { let validators_num = self.validators.len(); let bits = self @@ -99,11 +98,22 @@ impl ValidatorGroupsBuffer { .values() .fold(bitvec![0; validators_num], |acc, next| acc | next); - self.validators + let mut should_be_connected: Vec = self + .validators .iter() .enumerate() .filter_map(|(idx, authority_id)| bits[idx].then_some(authority_id.clone())) - .collect() + .collect(); + + if let Some(last_group) = self.group_infos.iter().last() { + for validator in self.validators.iter().rev().take(last_group.len) { + if !should_be_connected.contains(validator) { + should_be_connected.push(validator.clone()); + } + } + } + + should_be_connected } /// Note a new advertisement, marking that we want to be connected to validators @@ -279,7 +289,7 @@ mod tests { assert_eq!(buf.validators_to_connect(), validators[..2].to_vec()); buf.reset_validator_interest(hash_a, &validators[1]); - assert_eq!(buf.validators_to_connect(), vec![validators[0].clone()]); + assert_eq!(buf.validators_to_connect(), validators[0..2].to_vec()); buf.note_collation_advertised(hash_b, 0, GroupIndex(1), &validators[2..]); assert_eq!(buf.validators_to_connect(), validators[2..].to_vec()); @@ -287,7 +297,11 @@ mod tests { for validator in &validators[2..] { buf.reset_validator_interest(hash_b, validator); } - assert!(buf.validators_to_connect().is_empty()); + let mut expected = validators[2..].to_vec(); + expected.sort(); + let mut result = buf.validators_to_connect(); + result.sort(); + assert_eq!(result, expected); } #[test] @@ -320,10 +334,18 @@ mod tests { } buf.reset_validator_interest(hashes[1], &validators[0]); - assert_eq!(buf.validators_to_connect(), validators[..2].to_vec()); + let mut expected: Vec<_> = validators[..4].iter().cloned().collect(); + let mut result = buf.validators_to_connect(); + expected.sort(); + result.sort(); + assert_eq!(result, expected); buf.reset_validator_interest(hashes[0], &validators[0]); - assert_eq!(buf.validators_to_connect(), vec![validators[1].clone()]); + let mut expected: Vec<_> = validators[1..4].iter().cloned().collect(); + expected.sort(); + let mut result = buf.validators_to_connect(); + result.sort(); + assert_eq!(result, expected); buf.note_collation_advertised(hashes[3], 0, GroupIndex(1), &validators[2..4]); buf.note_collation_advertised( 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 48ad3c711a6db9c5e9e09c26d1a6290ffbed60f4..a1b93fff348f2f0621e9859418e5ceffb6829028 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -1883,7 +1883,7 @@ async fn disconnect_inactive_peers( ) { for (peer, peer_data) in peers { if peer_data.is_inactive(&eviction_policy) { - gum::trace!(target: LOG_TARGET, "Disconnecting inactive peer"); + gum::trace!(target: LOG_TARGET, ?peer, "Disconnecting inactive peer"); disconnect_peer(sender, *peer).await; } } diff --git a/polkadot/node/network/dispute-distribution/Cargo.toml b/polkadot/node/network/dispute-distribution/Cargo.toml index f892616107a7c1006f6da1866532b1578682e363..14d59d04f2bff7b838717edb927401035537ff4d 100644 --- a/polkadot/node/network/dispute-distribution/Cargo.toml +++ b/polkadot/node/network/dispute-distribution/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-dispute-distribution" -version = "1.0.0" +version = "7.0.0" description = "Polkadot Dispute Distribution subsystem, which ensures all concerned validators are aware of a dispute and have the relevant votes." authors.workspace = true edition.workspace = true @@ -24,7 +24,7 @@ polkadot-node-primitives = { path = "../../primitives" } sc-network = { path = "../../../../substrate/client/network" } sp-application-crypto = { path = "../../../../substrate/primitives/application-crypto" } sp-keystore = { path = "../../../../substrate/primitives/keystore" } -thiserror = "1.0.48" +thiserror = { workspace = true } fatality = "0.0.6" schnellru = "0.2.1" indexmap = "2.0.0" diff --git a/polkadot/node/network/gossip-support/Cargo.toml b/polkadot/node/network/gossip-support/Cargo.toml index 9ad7292b0fdcf979897663bff03e36eaa36acb4b..8d0edc206d728b16e0448c3339c1719f26d81413 100644 --- a/polkadot/node/network/gossip-support/Cargo.toml +++ b/polkadot/node/network/gossip-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-gossip-support" -version = "1.0.0" +version = "7.0.0" description = "Polkadot Gossip Support subsystem. Responsible for keeping track of session changes and issuing a connection request to the relevant validators on every new session." authors.workspace = true edition.workspace = true @@ -13,6 +13,7 @@ workspace = true sp-application-crypto = { path = "../../../../substrate/primitives/application-crypto" } sp-keystore = { path = "../../../../substrate/primitives/keystore" } sp-core = { path = "../../../../substrate/primitives/core" } +sp-crypto-hashing = { path = "../../../../substrate/primitives/crypto/hashing" } sc-network = { path = "../../../../substrate/client/network" } sc-network-common = { path = "../../../../substrate/client/network/common" } @@ -37,5 +38,6 @@ polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } assert_matches = "1.4.0" async-trait = "0.1.74" +parking_lot = "0.12.1" lazy_static = "1.4.0" quickcheck = "1.0.3" diff --git a/polkadot/node/network/gossip-support/src/lib.rs b/polkadot/node/network/gossip-support/src/lib.rs index 22417795d5ea55ff65d35ffb7d0f4b960582c570..4dfdd1f7208f66ec4a787ffe8bc7f72c41575c9d 100644 --- a/polkadot/node/network/gossip-support/src/lib.rs +++ b/polkadot/node/network/gossip-support/src/lib.rs @@ -63,8 +63,12 @@ use metrics::Metrics; const LOG_TARGET: &str = "parachain::gossip-support"; // How much time should we wait to reissue a connection request // since the last authority discovery resolution failure. +#[cfg(not(test))] const BACKOFF_DURATION: Duration = Duration::from_secs(5); +#[cfg(test)] +const BACKOFF_DURATION: Duration = Duration::from_millis(500); + /// Duration after which we consider low connectivity a problem. /// /// Especially at startup low connectivity is expected (authority discovery cache needs to be @@ -270,9 +274,10 @@ where session_index, ) .await?; - - self.update_authority_ids(sender, session_info.discovery_keys).await; } + // authority_discovery is just a cache so let's try every time we try to re-connect + // if new authorities are present. + self.update_authority_ids(sender, session_info.discovery_keys).await; } } Ok(()) @@ -593,7 +598,7 @@ async fn update_gossip_topology( let mut subject = [0u8; 40]; subject[..8].copy_from_slice(b"gossipsu"); subject[8..].copy_from_slice(&randomness); - sp_core::blake2_256(&subject) + sp_crypto_hashing::blake2_256(&subject) }; // shuffle the validators and create the index mapping diff --git a/polkadot/node/network/gossip-support/src/tests.rs b/polkadot/node/network/gossip-support/src/tests.rs index e5ee101c31d857b2dbd540596649ddaf9b826bd5..6817c85f98d87c03b3c252783c464efed88dfdb6 100644 --- a/polkadot/node/network/gossip-support/src/tests.rs +++ b/polkadot/node/network/gossip-support/src/tests.rs @@ -25,13 +25,19 @@ use lazy_static::lazy_static; use quickcheck::quickcheck; use rand::seq::SliceRandom as _; +use parking_lot::Mutex; use sc_network::multiaddr::Protocol; use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair; use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch}; use sp_core::crypto::Pair as PairT; use sp_keyring::Sr25519Keyring; +use std::sync::Arc; -use polkadot_node_network_protocol::grid_topology::{SessionGridTopology, TopologyPeerInfo}; +use polkadot_node_network_protocol::{ + grid_topology::{SessionGridTopology, TopologyPeerInfo}, + peer_set::ValidationVersion, + ObservedRole, +}; use polkadot_node_subsystem::messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest}; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt as _; @@ -51,7 +57,6 @@ const AUTHORITY_KEYRINGS: &[Sr25519Keyring] = &[ ]; lazy_static! { - static ref MOCK_AUTHORITY_DISCOVERY: MockAuthorityDiscovery = MockAuthorityDiscovery::new(); static ref AUTHORITIES: Vec = AUTHORITY_KEYRINGS.iter().map(|k| k.public().into()).collect(); @@ -89,17 +94,14 @@ type VirtualOverseer = test_helpers::TestSubsystemContextHandle>, - authorities: HashMap>, + addrs: Arc>>>, + authorities: Arc>>>, } impl MockAuthorityDiscovery { - fn new() -> Self { - let authorities: HashMap<_, _> = PAST_PRESENT_FUTURE_AUTHORITIES - .clone() - .into_iter() - .map(|a| (PeerId::random(), a)) - .collect(); + fn new(authorities: Vec) -> Self { + let authorities: HashMap<_, _> = + authorities.clone().into_iter().map(|a| (PeerId::random(), a)).collect(); let addrs = authorities .clone() .into_iter() @@ -109,10 +111,37 @@ impl MockAuthorityDiscovery { }) .collect(); Self { - addrs, - authorities: authorities.into_iter().map(|(p, a)| (p, HashSet::from([a]))).collect(), + addrs: Arc::new(Mutex::new(addrs)), + authorities: Arc::new(Mutex::new( + authorities.into_iter().map(|(p, a)| (p, HashSet::from([a]))).collect(), + )), } } + + fn authorities(&self) -> HashMap> { + self.authorities.lock().clone() + } + + fn add_more_authorties( + &self, + new_known: Vec, + ) -> HashMap> { + let authorities: HashMap<_, _> = + new_known.clone().into_iter().map(|a| (PeerId::random(), a)).collect(); + let addrs: HashMap> = authorities + .clone() + .into_iter() + .map(|(p, a)| { + let multiaddr = Multiaddr::empty().with(Protocol::P2p(p.into())); + (a, HashSet::from([multiaddr])) + }) + .collect(); + let authorities: HashMap> = + authorities.into_iter().map(|(p, a)| (p, HashSet::from([a]))).collect(); + self.addrs.as_ref().lock().extend(addrs); + self.authorities.as_ref().lock().extend(authorities.clone()); + authorities + } } #[async_trait] @@ -121,19 +150,23 @@ impl AuthorityDiscovery for MockAuthorityDiscovery { &mut self, authority: polkadot_primitives::AuthorityDiscoveryId, ) -> Option> { - self.addrs.get(&authority).cloned() + self.addrs.lock().get(&authority).cloned() } + async fn get_authority_ids_by_peer_id( &mut self, peer_id: polkadot_node_network_protocol::PeerId, ) -> Option> { - self.authorities.get(&peer_id).cloned() + self.authorities.as_ref().lock().get(&peer_id).cloned() } } -async fn get_multiaddrs(authorities: Vec) -> Vec> { +async fn get_multiaddrs( + authorities: Vec, + mock_authority_discovery: MockAuthorityDiscovery, +) -> Vec> { let mut addrs = Vec::with_capacity(authorities.len()); - let mut discovery = MOCK_AUTHORITY_DISCOVERY.clone(); + let mut discovery = mock_authority_discovery.clone(); for authority in authorities.into_iter() { if let Some(addr) = discovery.get_addresses_by_authority_id(authority).await { addrs.push(addr); @@ -144,9 +177,10 @@ async fn get_multiaddrs(authorities: Vec) -> Vec, + mock_authority_discovery: MockAuthorityDiscovery, ) -> HashMap> { let mut addrs = HashMap::with_capacity(authorities.len()); - let mut discovery = MOCK_AUTHORITY_DISCOVERY.clone(); + let mut discovery = mock_authority_discovery.clone(); for authority in authorities.into_iter() { if let Some(addr) = discovery.get_addresses_by_authority_id(authority.clone()).await { addrs.insert(authority, addr); @@ -155,12 +189,10 @@ async fn get_address_map( addrs } -fn make_subsystem() -> GossipSupport { - GossipSupport::new( - make_ferdie_keystore(), - MOCK_AUTHORITY_DISCOVERY.clone(), - Metrics::new_dummy(), - ) +fn make_subsystem_with_authority_discovery( + mock: MockAuthorityDiscovery, +) -> GossipSupport { + GossipSupport::new(make_ferdie_keystore(), mock, Metrics::new_dummy()) } fn test_harness, AD: AuthorityDiscovery>( @@ -291,59 +323,65 @@ async fn test_neighbors(overseer: &mut VirtualOverseer, expected_session: Sessio #[test] fn issues_a_connection_request_on_new_session() { + let mock_authority_discovery = + MockAuthorityDiscovery::new(PAST_PRESENT_FUTURE_AUTHORITIES.clone()); + let mock_authority_discovery_clone = mock_authority_discovery.clone(); let hash = Hash::repeat_byte(0xAA); - let state = test_harness(make_subsystem(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - overseer_signal_active_leaves(overseer, hash).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::SessionIndexForChild(tx), - )) => { - assert_eq!(relay_parent, hash); - tx.send(Ok(1)).unwrap(); - } - ); + let state = test_harness( + make_subsystem_with_authority_discovery(mock_authority_discovery.clone()), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + overseer_signal_active_leaves(overseer, hash).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::SessionInfo(s, tx), - )) => { - assert_eq!(relay_parent, hash); - assert_eq!(s, 1); - tx.send(Ok(Some(make_session_info()))).unwrap(); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + tx.send(Ok(Some(make_session_info()))).unwrap(); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::Authorities(tx), - )) => { - assert_eq!(relay_parent, hash); - tx.send(Ok(AUTHORITIES.clone())).unwrap(); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(AUTHORITIES.clone())).unwrap(); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { - validator_addrs, - peer_set, - }) => { - assert_eq!(validator_addrs, get_multiaddrs(AUTHORITIES_WITHOUT_US.clone()).await); - assert_eq!(peer_set, PeerSet::Validation); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { + validator_addrs, + peer_set, + }) => { + assert_eq!(validator_addrs, get_multiaddrs(AUTHORITIES_WITHOUT_US.clone(), mock_authority_discovery_clone).await); + assert_eq!(peer_set, PeerSet::Validation); + } + ); - test_neighbors(overseer, 1).await; + test_neighbors(overseer, 1).await; - virtual_overseer - }); + virtual_overseer + }, + ); assert_eq!(state.last_session_index, Some(1)); assert!(state.last_failure.is_none()); @@ -363,6 +401,7 @@ fn issues_a_connection_request_on_new_session() { tx.send(Ok(1)).unwrap(); } ); + virtual_overseer }); @@ -414,7 +453,7 @@ fn issues_a_connection_request_on_new_session() { validator_addrs, peer_set, }) => { - assert_eq!(validator_addrs, get_multiaddrs(AUTHORITIES_WITHOUT_US.clone()).await); + assert_eq!(validator_addrs, get_multiaddrs(AUTHORITIES_WITHOUT_US.clone(), mock_authority_discovery.clone()).await); assert_eq!(peer_set, PeerSet::Validation); } ); @@ -430,125 +469,405 @@ fn issues_a_connection_request_on_new_session() { #[test] fn issues_connection_request_to_past_present_future() { let hash = Hash::repeat_byte(0xAA); - test_harness(make_subsystem(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - overseer_signal_active_leaves(overseer, hash).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::SessionIndexForChild(tx), - )) => { - assert_eq!(relay_parent, hash); - tx.send(Ok(1)).unwrap(); + let mock_authority_discovery = + MockAuthorityDiscovery::new(PAST_PRESENT_FUTURE_AUTHORITIES.clone()); + test_harness( + make_subsystem_with_authority_discovery(mock_authority_discovery.clone()), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + overseer_signal_active_leaves(overseer, hash).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + tx.send(Ok(Some(make_session_info()))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { + validator_addrs, + peer_set, + }) => { + let all_without_ferdie: Vec<_> = PAST_PRESENT_FUTURE_AUTHORITIES + .iter() + .cloned() + .filter(|p| p != &Sr25519Keyring::Ferdie.public().into()) + .collect(); + + let addrs = get_multiaddrs(all_without_ferdie, mock_authority_discovery.clone()).await; + + assert_eq!(validator_addrs, addrs); + assert_eq!(peer_set, PeerSet::Validation); + } + ); + + // Ensure neighbors are unaffected + test_neighbors(overseer, 1).await; + + virtual_overseer + }, + ); +} + +// Test we notify peer about learning of the authority ID after session boundary, when we couldn't +// connect to more than 1/3 of the authorities. +#[test] +fn issues_update_authorities_after_session() { + let hash = Hash::repeat_byte(0xAA); + + let mut authorities = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + let unknown_at_session = authorities.split_off(authorities.len() / 3 - 1); + let mut authority_discovery_mock = MockAuthorityDiscovery::new(authorities); + + test_harness( + make_subsystem_with_authority_discovery(authority_discovery_mock.clone()), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // 1. Initialize with the first leaf in the session. + overseer_signal_active_leaves(overseer, hash).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut session_info = make_session_info(); + session_info.discovery_keys = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + tx.send(Ok(Some(session_info))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { + validator_addrs, + peer_set, + }) => { + let all_without_ferdie: Vec<_> = PAST_PRESENT_FUTURE_AUTHORITIES + .iter() + .cloned() + .filter(|p| p != &Sr25519Keyring::Ferdie.public().into()) + .collect(); + + let addrs = get_multiaddrs(all_without_ferdie, authority_discovery_mock.clone()).await; + + assert_eq!(validator_addrs, addrs); + assert_eq!(peer_set, PeerSet::Validation); + } + ); + + // Ensure neighbors are unaffected + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _, + RuntimeApiRequest::CurrentBabeEpoch(tx), + )) => { + let _ = tx.send(Ok(BabeEpoch { + epoch_index: 2 as _, + start_slot: 0.into(), + duration: 200, + authorities: vec![(Sr25519Keyring::Alice.public().into(), 1)], + randomness: [0u8; 32], + config: BabeEpochConfiguration { + c: (1, 4), + allowed_slots: AllowedSlots::PrimarySlots, + }, + })).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeRx(NetworkBridgeRxMessage::NewGossipTopology { + session: _, + local_index: _, + canonical_shuffling: _, + shuffled_indices: _, + }) => { + + } + ); + + // 2. Connect all authorities that are known so far. + let known_authorities = authority_discovery_mock.authorities(); + for (peer_id, _id) in known_authorities.iter() { + let msg = + GossipSupportMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerConnected( + *peer_id, + ObservedRole::Authority, + ValidationVersion::V3.into(), + None, + )); + overseer.send(FromOrchestra::Communication { msg }).await } - ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::SessionInfo(s, tx), - )) => { - assert_eq!(relay_parent, hash); - assert_eq!(s, 1); - tx.send(Ok(Some(make_session_info()))).unwrap(); + Delay::new(BACKOFF_DURATION).await; + // 3. Send a new leaf after BACKOFF_DURATION and check UpdateAuthority is emitted for + // all known connected peers. + let hash = Hash::repeat_byte(0xBB); + overseer_signal_active_leaves(overseer, hash).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut session_info = make_session_info(); + session_info.discovery_keys = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + tx.send(Ok(Some(session_info))).unwrap(); + + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { + validator_addrs: _, + peer_set: _, + }) => { + } + ); + + for _ in 0..known_authorities.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeRx(NetworkBridgeRxMessage::UpdatedAuthorityIds { + peer_id, + authority_ids, + }) => { + assert_eq!(authority_discovery_mock.get_authority_ids_by_peer_id(peer_id).await.unwrap_or_default(), authority_ids); + } + ); } - ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::Authorities(tx), - )) => { - assert_eq!(relay_parent, hash); - tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none()); + // 4. Connect more authorities except one + let newly_added = authority_discovery_mock.add_more_authorties(unknown_at_session); + let mut newly_added_iter = newly_added.iter(); + let unconnected_at_last_retry = newly_added_iter + .next() + .map(|(peer_id, authority_id)| (*peer_id, authority_id.clone())) + .unwrap(); + for (peer_id, _) in newly_added_iter { + let msg = + GossipSupportMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerConnected( + *peer_id, + ObservedRole::Authority, + ValidationVersion::V3.into(), + None, + )); + overseer.send(FromOrchestra::Communication { msg }).await } - ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { - validator_addrs, - peer_set, - }) => { - let all_without_ferdie: Vec<_> = PAST_PRESENT_FUTURE_AUTHORITIES - .iter() - .cloned() - .filter(|p| p != &Sr25519Keyring::Ferdie.public().into()) - .collect(); + // 5. Send a new leaf and check UpdateAuthority is emitted only for the newly connected + // peers. + let hash = Hash::repeat_byte(0xCC); + Delay::new(BACKOFF_DURATION).await; + overseer_signal_active_leaves(overseer, hash).await; - let addrs = get_multiaddrs(all_without_ferdie).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); - assert_eq!(validator_addrs, addrs); - assert_eq!(peer_set, PeerSet::Validation); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut session_info = make_session_info(); + session_info.discovery_keys = PAST_PRESENT_FUTURE_AUTHORITIES.clone(); + tx.send(Ok(Some(session_info))).unwrap(); + } + ); - // Ensure neighbors are unaffected - test_neighbors(overseer, 1).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(PAST_PRESENT_FUTURE_AUTHORITIES.clone())).unwrap(); + } + ); - virtual_overseer - }); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { + validator_addrs: _, + peer_set: _, + }) => { + } + ); + + for _ in 1..newly_added.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeRx(NetworkBridgeRxMessage::UpdatedAuthorityIds { + peer_id, + authority_ids, + }) => { + assert_ne!(peer_id, unconnected_at_last_retry.0); + assert_eq!(newly_added.get(&peer_id).cloned().unwrap_or_default(), authority_ids); + } + ); + } + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none()); + virtual_overseer + }, + ); } #[test] fn disconnect_when_not_in_past_present_future() { sp_tracing::try_init_simple(); + let mock_authority_discovery = + MockAuthorityDiscovery::new(PAST_PRESENT_FUTURE_AUTHORITIES.clone()); let hash = Hash::repeat_byte(0xAA); - test_harness(make_subsystem(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - overseer_signal_active_leaves(overseer, hash).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::SessionIndexForChild(tx), - )) => { - assert_eq!(relay_parent, hash); - tx.send(Ok(1)).unwrap(); - } - ); + test_harness( + make_subsystem_with_authority_discovery(mock_authority_discovery.clone()), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + overseer_signal_active_leaves(overseer, hash).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionIndexForChild(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(1)).unwrap(); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::SessionInfo(s, tx), - )) => { - assert_eq!(relay_parent, hash); - assert_eq!(s, 1); - let mut heute_leider_nicht = make_session_info(); - heute_leider_nicht.discovery_keys = AUTHORITIES_WITHOUT_US.clone(); - tx.send(Ok(Some(heute_leider_nicht))).unwrap(); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::SessionInfo(s, tx), + )) => { + assert_eq!(relay_parent, hash); + assert_eq!(s, 1); + let mut heute_leider_nicht = make_session_info(); + heute_leider_nicht.discovery_keys = AUTHORITIES_WITHOUT_US.clone(); + tx.send(Ok(Some(heute_leider_nicht))).unwrap(); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::Authorities(tx), - )) => { - assert_eq!(relay_parent, hash); - tx.send(Ok(AUTHORITIES_WITHOUT_US.clone())).unwrap(); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::Authorities(tx), + )) => { + assert_eq!(relay_parent, hash); + tx.send(Ok(AUTHORITIES_WITHOUT_US.clone())).unwrap(); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { - validator_addrs, - peer_set, - }) => { - assert!(validator_addrs.is_empty()); - assert_eq!(peer_set, PeerSet::Validation); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ConnectToResolvedValidators { + validator_addrs, + peer_set, + }) => { + assert!(validator_addrs.is_empty()); + assert_eq!(peer_set, PeerSet::Validation); + } + ); - virtual_overseer - }); + virtual_overseer + }, + ); } #[test] @@ -579,13 +898,15 @@ fn test_log_output() { #[test] fn issues_a_connection_request_when_last_request_was_mostly_unresolved() { let hash = Hash::repeat_byte(0xAA); - let mut state = make_subsystem(); + let mock_authority_discovery = + MockAuthorityDiscovery::new(PAST_PRESENT_FUTURE_AUTHORITIES.clone()); + let state = make_subsystem_with_authority_discovery(mock_authority_discovery.clone()); // There will be two lookup failures: let alice = Sr25519Keyring::Alice.public().into(); let bob = Sr25519Keyring::Bob.public().into(); - let alice_addr = state.authority_discovery.addrs.remove(&alice); - state.authority_discovery.addrs.remove(&bob); - + let alice_addr = state.authority_discovery.addrs.lock().remove(&alice); + state.authority_discovery.addrs.lock().remove(&bob); + let mock_authority_discovery_clone = mock_authority_discovery.clone(); let mut state = { let alice = alice.clone(); let bob = bob.clone(); @@ -633,7 +954,7 @@ fn issues_a_connection_request_when_last_request_was_mostly_unresolved() { validator_addrs, peer_set, }) => { - let mut expected = get_address_map(AUTHORITIES_WITHOUT_US.clone()).await; + let mut expected = get_address_map(AUTHORITIES_WITHOUT_US.clone(), mock_authority_discovery_clone.clone()).await; expected.remove(&alice); expected.remove(&bob); let expected: HashSet = expected.into_values().flat_map(|v| v.into_iter()).collect(); @@ -652,7 +973,7 @@ fn issues_a_connection_request_when_last_request_was_mostly_unresolved() { assert!(state.last_failure.is_some()); state.last_failure = state.last_failure.and_then(|i| i.checked_sub(BACKOFF_DURATION)); // One error less: - state.authority_discovery.addrs.insert(alice, alice_addr.unwrap()); + state.authority_discovery.addrs.lock().insert(alice, alice_addr.unwrap()); let hash = Hash::repeat_byte(0xBB); let state = test_harness(state, |mut virtual_overseer| async move { @@ -698,7 +1019,7 @@ fn issues_a_connection_request_when_last_request_was_mostly_unresolved() { validator_addrs, peer_set, }) => { - let mut expected = get_address_map(AUTHORITIES_WITHOUT_US.clone()).await; + let mut expected = get_address_map(AUTHORITIES_WITHOUT_US.clone(), mock_authority_discovery.clone()).await; expected.remove(&bob); let expected: HashSet = expected.into_values().flat_map(|v| v.into_iter()).collect(); assert_eq!(validator_addrs.into_iter().flat_map(|v| v.into_iter()).collect::>(), expected); diff --git a/polkadot/node/network/protocol/Cargo.toml b/polkadot/node/network/protocol/Cargo.toml index e683c662fbe78085c192786a2c177e93f8418fae..7efa0b8ca820d651d77a53a95d61e7672c5f9737 100644 --- a/polkadot/node/network/protocol/Cargo.toml +++ b/polkadot/node/network/protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-node-network-protocol" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -21,7 +21,7 @@ sc-network = { path = "../../../../substrate/client/network" } sc-authority-discovery = { path = "../../../../substrate/client/authority-discovery" } strum = { version = "0.24", features = ["derive"] } futures = "0.3.21" -thiserror = "1.0.48" +thiserror = { workspace = true } fatality = "0.0.6" rand = "0.8" derive_more = "0.99" diff --git a/polkadot/node/network/protocol/src/grid_topology.rs b/polkadot/node/network/protocol/src/grid_topology.rs index 8bd9adbc17c1089bdaab9d16b271b9ae4bc8e708..3c4372a27a2c42e20227adbe15d2cc7fcade3d12 100644 --- a/polkadot/node/network/protocol/src/grid_topology.rs +++ b/polkadot/node/network/protocol/src/grid_topology.rs @@ -89,6 +89,26 @@ impl SessionGridTopology { SessionGridTopology { shuffled_indices, canonical_shuffling, peer_ids } } + /// Updates the known peer ids for the passed authorithies ids. + pub fn update_authority_ids( + &mut self, + peer_id: PeerId, + ids: &HashSet, + ) -> bool { + let mut updated = false; + if !self.peer_ids.contains(&peer_id) { + for peer in self + .canonical_shuffling + .iter_mut() + .filter(|peer| ids.contains(&peer.discovery_id)) + { + peer.peer_ids.push(peer_id); + self.peer_ids.insert(peer_id); + updated = true; + } + } + updated + } /// Produces the outgoing routing logic for a particular peer. /// /// Returns `None` if the validator index is out of bounds. @@ -269,6 +289,7 @@ impl GridNeighbors { pub struct SessionGridTopologyEntry { topology: SessionGridTopology, local_neighbors: GridNeighbors, + local_index: Option, } impl SessionGridTopologyEntry { @@ -291,6 +312,25 @@ impl SessionGridTopologyEntry { pub fn is_validator(&self, peer: &PeerId) -> bool { self.topology.is_validator(peer) } + + /// Updates the known peer ids for the passed authorithies ids. + pub fn update_authority_ids( + &mut self, + peer_id: PeerId, + ids: &HashSet, + ) -> bool { + let peer_id_updated = self.topology.update_authority_ids(peer_id, ids); + // If we added a new peer id we need to recompute the grid neighbors, so that + // neighbors_x and neighbors_y reflect the right peer ids. + if peer_id_updated { + if let Some(local_index) = self.local_index.as_ref() { + if let Some(new_grid) = self.topology.compute_grid_neighbors_for(*local_index) { + self.local_neighbors = new_grid; + } + } + } + peer_id_updated + } } /// A set of topologies indexed by session @@ -305,6 +345,20 @@ impl SessionGridTopologies { self.inner.get(&session).and_then(|val| val.0.as_ref()) } + /// Updates the known peer ids for the passed authorithies ids. + pub fn update_authority_ids( + &mut self, + peer_id: PeerId, + ids: &HashSet, + ) -> bool { + self.inner + .iter_mut() + .map(|(_, topology)| { + topology.0.as_mut().map(|topology| topology.update_authority_ids(peer_id, ids)) + }) + .any(|updated| updated.unwrap_or_default()) + } + /// Increase references counter for a specific topology pub fn inc_session_refs(&mut self, session: SessionIndex) { self.inner.entry(session).or_insert((None, 0)).1 += 1; @@ -333,7 +387,7 @@ impl SessionGridTopologies { .and_then(|l| topology.compute_grid_neighbors_for(l)) .unwrap_or_else(GridNeighbors::empty); - entry.0 = Some(SessionGridTopologyEntry { topology, local_neighbors }); + entry.0 = Some(SessionGridTopologyEntry { topology, local_neighbors, local_index }); } } } @@ -368,6 +422,7 @@ impl Default for SessionBoundGridTopologyStorage { peer_ids: Default::default(), }, local_neighbors: GridNeighbors::empty(), + local_index: None, }, }, prev_topology: None, @@ -412,7 +467,7 @@ impl SessionBoundGridTopologyStorage { let old_current = std::mem::replace( &mut self.current_topology, GridTopologySessionBound { - entry: SessionGridTopologyEntry { topology, local_neighbors }, + entry: SessionGridTopologyEntry { topology, local_neighbors, local_index }, session_index, }, ); diff --git a/polkadot/node/network/protocol/src/lib.rs b/polkadot/node/network/protocol/src/lib.rs index ae72230ee43d506549482e9a063aef5412b1283c..7a0ff9f4fa9a26c2ec1f7d97b03d61424e83b98b 100644 --- a/polkadot/node/network/protocol/src/lib.rs +++ b/polkadot/node/network/protocol/src/lib.rs @@ -895,7 +895,10 @@ pub mod v3 { /// candidate index. /// /// Actually checking the assignment may yield a different result. - /// TODO: Look at getting rid of bitfield in the future. + /// + /// TODO at next protocol upgrade opportunity: + /// - remove redundancy `candidate_index` vs `core_index` + /// - `` #[codec(index = 0)] Assignments(Vec<(IndirectAssignmentCertV2, CandidateBitfield)>), /// Approvals for candidates in some recent, unfinalized block. diff --git a/polkadot/node/network/statement-distribution/Cargo.toml b/polkadot/node/network/statement-distribution/Cargo.toml index 7a502436bb5a69268e289b8691dcbdaa17eeee1c..01f7d818c1c52ffd677379e3a0a9d2f543572d92 100644 --- a/polkadot/node/network/statement-distribution/Cargo.toml +++ b/polkadot/node/network/statement-distribution/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-statement-distribution" description = "Statement Distribution Subsystem" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -23,7 +23,7 @@ polkadot-node-network-protocol = { path = "../protocol" } arrayvec = "0.7.4" indexmap = "2.0.0" parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } -thiserror = "1.0.48" +thiserror = { workspace = true } fatality = "0.0.6" bitvec = "1" diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs index 93f97fe1dd6ede274c4109f4ae7a74765d9ee649..e22883f8937606475efc01225fcd6f98e4ee0f08 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs @@ -1892,7 +1892,9 @@ pub(crate) async fn handle_network_update( ?authority_ids, "Updated `AuthorityDiscoveryId`s" ); - + topology_storage + .get_current_topology_mut() + .update_authority_ids(peer, &authority_ids); // Remove the authority IDs which were previously mapped to the peer // but aren't part of the new set. authorities.retain(|a, p| p != &peer || authority_ids.contains(a)); diff --git a/polkadot/node/network/statement-distribution/src/lib.rs b/polkadot/node/network/statement-distribution/src/lib.rs index a1ba1137b5acf119edbd9d228db3fa41e57afbc3..7e91d2849120283c3fcefc4193a6e3d8bff809a1 100644 --- a/polkadot/node/network/statement-distribution/src/lib.rs +++ b/polkadot/node/network/statement-distribution/src/lib.rs @@ -319,8 +319,12 @@ impl StatementDistributionSubsystem { if let Some(ref activated) = activated { let mode = prospective_parachains_mode(ctx.sender(), activated.hash).await?; if let ProspectiveParachainsMode::Enabled { .. } = mode { - v2::handle_active_leaves_update(ctx, state, activated, mode).await?; + let res = + v2::handle_active_leaves_update(ctx, state, activated, mode).await; + // Regardless of the result of leaf activation, we always prune before + // handling it to avoid leaks. v2::handle_deactivate_leaves(state, &deactivated); + res?; } else if let ProspectiveParachainsMode::Disabled = mode { for deactivated in &deactivated { crate::legacy_v1::handle_deactivate_leaf(legacy_v1_state, *deactivated); diff --git a/polkadot/node/network/statement-distribution/src/v2/cluster.rs b/polkadot/node/network/statement-distribution/src/v2/cluster.rs index 619114de9670c8aef50ce129b6c26bf6ff206f70..c09916e56201f20a0fbbbac349681b512f1fd5fe 100644 --- a/polkadot/node/network/statement-distribution/src/v2/cluster.rs +++ b/polkadot/node/network/statement-distribution/src/v2/cluster.rs @@ -55,8 +55,9 @@ //! and to keep track of what we have sent to other validators in the group and what we may //! continue to send them. -use polkadot_primitives::{CandidateHash, CompactStatement, ValidatorIndex}; +use polkadot_primitives::{CandidateHash, CompactStatement, Hash, ValidatorIndex}; +use crate::LOG_TARGET; use std::collections::{HashMap, HashSet}; #[derive(Hash, PartialEq, Eq)] @@ -424,6 +425,28 @@ impl ClusterTracker { fn is_in_group(&self, validator: ValidatorIndex) -> bool { self.validators.contains(&validator) } + + /// Dumps pending statement for this cluster. + /// + /// Normally we should not have pending statements to validators in our cluster, + /// but if we do for all validators in our cluster, then we don't participate + /// in backing. Ocasional pending statements are expected if two authorities + /// can't detect each otehr or after restart, where it takes a while to discover + /// the whole network. + + pub fn warn_if_too_many_pending_statements(&self, parent_hash: Hash) { + if self.pending.iter().filter(|pending| !pending.1.is_empty()).count() >= + self.validators.len() + { + gum::warn!( + target: LOG_TARGET, + pending_statements = ?self.pending, + ?parent_hash, + "Cluster has too many pending statements, something wrong with our connection to our group peers \n + Restart might be needed if validator gets 0 backing rewards for more than 3-4 consecutive sessions" + ); + } + } } /// Incoming statement was accepted. diff --git a/polkadot/node/network/statement-distribution/src/v2/grid.rs b/polkadot/node/network/statement-distribution/src/v2/grid.rs index 19f23053192c12a7e3bcb3ae73dbbd972e1c619d..24d846c840e00c49446a0525b3768353f56ae524 100644 --- a/polkadot/node/network/statement-distribution/src/v2/grid.rs +++ b/polkadot/node/network/statement-distribution/src/v2/grid.rs @@ -527,12 +527,16 @@ impl GridTracker { } /// Determine the validators which can send a statement to us by direct broadcast. + /// + /// Returns a list of tuples representing each potential sender(ValidatorIndex) + /// and if the sender should already know about the statement, because we just + /// sent it to it. pub fn direct_statement_providers( &self, groups: &Groups, originator: ValidatorIndex, statement: &CompactStatement, - ) -> Vec { + ) -> Vec<(ValidatorIndex, bool)> { let (g, c_h, kind, in_group) = match extract_statement_and_group_info(groups, originator, statement) { None => return Vec::new(), @@ -616,12 +620,13 @@ impl GridTracker { originator: ValidatorIndex, counterparty: ValidatorIndex, statement: &CompactStatement, + received: bool, ) { if let Some((_, c_h, kind, in_group)) = extract_statement_and_group_info(groups, originator, statement) { if let Some(known) = self.confirmed_backed.get_mut(&c_h) { - known.sent_or_received_direct_statement(counterparty, in_group, kind); + known.sent_or_received_direct_statement(counterparty, in_group, kind, received); if let Some(pending) = self.pending_statements.get_mut(&counterparty) { pending.remove(&(originator, statement.clone())); @@ -908,6 +913,12 @@ struct MutualKnowledge { /// `Some` only if we have advertised, acknowledged, or requested the candidate /// from them. local_knowledge: Option, + /// Knowledge peer circulated to us, this is different from `local_knowledge` and + /// `remote_knowledge`, through the fact that includes only statements that we received from + /// peer while the other two, after manifest exchange part will include both what we sent to + /// the peer and what we received from peer, see `sent_or_received_direct_statement` for more + /// details. + received_knowledge: Option, } // A utility struct for keeping track of metadata about candidates @@ -933,10 +944,13 @@ impl KnownBackedCandidate { } fn manifest_sent_to(&mut self, validator: ValidatorIndex, local_knowledge: StatementFilter) { - let k = self - .mutual_knowledge - .entry(validator) - .or_insert_with(|| MutualKnowledge { remote_knowledge: None, local_knowledge: None }); + let k = self.mutual_knowledge.entry(validator).or_insert_with(|| MutualKnowledge { + remote_knowledge: None, + local_knowledge: None, + received_knowledge: None, + }); + k.received_knowledge = + Some(StatementFilter::blank(local_knowledge.seconded_in_group.len())); k.local_knowledge = Some(local_knowledge); } @@ -946,20 +960,24 @@ impl KnownBackedCandidate { validator: ValidatorIndex, remote_knowledge: StatementFilter, ) { - let k = self - .mutual_knowledge - .entry(validator) - .or_insert_with(|| MutualKnowledge { remote_knowledge: None, local_knowledge: None }); + let k = self.mutual_knowledge.entry(validator).or_insert_with(|| MutualKnowledge { + remote_knowledge: None, + local_knowledge: None, + received_knowledge: None, + }); k.remote_knowledge = Some(remote_knowledge); } + /// Returns a list of tuples representing each potential sender(ValidatorIndex) + /// and if the sender should already know about the statement, because we just + /// sent it to it. fn direct_statement_senders( &self, group_index: GroupIndex, originator_index_in_group: usize, statement_kind: StatementKind, - ) -> Vec { + ) -> Vec<(ValidatorIndex, bool)> { if group_index != self.group_index { return Vec::new() } @@ -968,11 +986,18 @@ impl KnownBackedCandidate { .iter() .filter(|(_, k)| k.remote_knowledge.is_some()) .filter(|(_, k)| { - k.local_knowledge + k.received_knowledge .as_ref() .map_or(false, |r| !r.contains(originator_index_in_group, statement_kind)) }) - .map(|(v, _)| *v) + .map(|(v, k)| { + ( + *v, + k.local_knowledge + .as_ref() + .map_or(false, |r| r.contains(originator_index_in_group, statement_kind)), + ) + }) .collect() } @@ -1014,12 +1039,19 @@ impl KnownBackedCandidate { validator: ValidatorIndex, statement_index_in_group: usize, statement_kind: StatementKind, + received: bool, ) { if let Some(k) = self.mutual_knowledge.get_mut(&validator) { if let (Some(r), Some(l)) = (k.remote_knowledge.as_mut(), k.local_knowledge.as_mut()) { r.set(statement_index_in_group, statement_kind); l.set(statement_index_in_group, statement_kind); } + + if received { + k.received_knowledge + .as_mut() + .map(|knowledge| knowledge.set(statement_index_in_group, statement_kind)); + } } } @@ -2238,6 +2270,7 @@ mod tests { validator_index, counterparty, &statement, + false, ); // There should be no pending statements now (for the counterparty). diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index 02fdecdd9bb326ffaba306cd370e6c8d1c4dc28b..2c9cdba4ea8eb1155f9307eb01460a777ae9a737 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -36,8 +36,9 @@ use polkadot_node_primitives::{ }; use polkadot_node_subsystem::{ messages::{ - CandidateBackingMessage, HypotheticalCandidate, HypotheticalFrontierRequest, - NetworkBridgeEvent, NetworkBridgeTxMessage, ProspectiveParachainsMessage, + network_bridge_event::NewGossipTopology, CandidateBackingMessage, HypotheticalCandidate, + HypotheticalFrontierRequest, NetworkBridgeEvent, NetworkBridgeTxMessage, + ProspectiveParachainsMessage, }, overseer, ActivatedLeaf, }; @@ -92,7 +93,19 @@ mod statement_store; #[cfg(test)] mod tests; -const COST_UNEXPECTED_STATEMENT: Rep = Rep::CostMinor("Unexpected Statement"); +const COST_UNEXPECTED_STATEMENT_NOT_VALIDATOR: Rep = + Rep::CostMinor("Unexpected Statement, not a validator"); +const COST_UNEXPECTED_STATEMENT_VALIDATOR_NOT_FOUND: Rep = + Rep::CostMinor("Unexpected Statement, validator not found"); +const COST_UNEXPECTED_STATEMENT_INVALID_SENDER: Rep = + Rep::CostMinor("Unexpected Statement, invalid sender"); +const COST_UNEXPECTED_STATEMENT_BAD_ADVERTISE: Rep = + Rep::CostMinor("Unexpected Statement, bad advertise"); +const COST_UNEXPECTED_STATEMENT_CLUSTER_REJECTED: Rep = + Rep::CostMinor("Unexpected Statement, cluster rejected"); +const COST_UNEXPECTED_STATEMENT_NOT_IN_GROUP: Rep = + Rep::CostMinor("Unexpected Statement, not in group"); + const COST_UNEXPECTED_STATEMENT_MISSING_KNOWLEDGE: Rep = Rep::CostMinor("Unexpected Statement, missing knowledge for relay parent"); const COST_EXCESSIVE_SECONDED: Rep = Rep::CostMinor("Sent Excessive `Seconded` Statements"); @@ -238,6 +251,13 @@ impl PerSessionState { if local_index.is_some() { self.local_validator.get_or_insert(LocalValidatorIndex::Inactive); } + + gum::info!( + target: LOG_TARGET, + index_in_gossip_topology = ?local_index, + index_in_parachain_authorities = ?self.local_validator, + "Node uses the following topology indices" + ); } /// Returns `true` if local is neither active or inactive validator node. @@ -283,6 +303,10 @@ pub(crate) struct State { candidates: Candidates, per_relay_parent: HashMap, per_session: HashMap, + // Topology might be received before first leaf update, where we + // initialize the per_session_state, so cache it here until we + // are able to use it. + unused_topologies: HashMap, peers: HashMap, keystore: KeystorePtr, authorities: HashMap, @@ -303,6 +327,7 @@ impl State { authorities: HashMap::new(), request_manager: RequestManager::new(), response_manager: ResponseManager::new(), + unused_topologies: HashMap::new(), } } @@ -449,12 +474,14 @@ pub(crate) async fn handle_network_update( } }, NetworkBridgeEvent::NewGossipTopology(topology) => { - let new_session_index = topology.session; - let new_topology = topology.topology; - let local_index = topology.local_index; + let new_session_index = &topology.session; + let new_topology = &topology.topology; + let local_index = &topology.local_index; - if let Some(per_session) = state.per_session.get_mut(&new_session_index) { - per_session.supply_topology(&new_topology, local_index); + if let Some(per_session) = state.per_session.get_mut(new_session_index) { + per_session.supply_topology(new_topology, *local_index); + } else { + state.unused_topologies.insert(*new_session_index, topology); } // TODO [https://github.com/paritytech/polkadot/issues/6194] @@ -599,11 +626,12 @@ pub(crate) async fn handle_active_leaves_update( let minimum_backing_votes = request_min_backing_votes(new_relay_parent, session_index, ctx.sender()).await?; - - state.per_session.insert( - session_index, - PerSessionState::new(session_info, &state.keystore, minimum_backing_votes), - ); + let mut per_session_state = + PerSessionState::new(session_info, &state.keystore, minimum_backing_votes); + if let Some(toplogy) = state.unused_topologies.remove(&session_index) { + per_session_state.supply_topology(&toplogy.topology, toplogy.local_index); + } + state.per_session.insert(session_index, per_session_state); } let per_session = state @@ -747,7 +775,15 @@ pub(crate) fn handle_deactivate_leaves(state: &mut State, leaves: &[Hash]) { let pruned = state.implicit_view.deactivate_leaf(*leaf); for pruned_rp in pruned { // clean up per-relay-parent data based on everything removed. - state.per_relay_parent.remove(&pruned_rp); + state + .per_relay_parent + .remove(&pruned_rp) + .as_ref() + .and_then(|pruned| pruned.active_validator_state()) + .map(|active_state| { + active_state.cluster_tracker.warn_if_too_many_pending_statements(pruned_rp) + }); + // clean up requests related to this relay parent. state.request_manager.remove_by_relay_parent(*leaf); } @@ -760,6 +796,7 @@ pub(crate) fn handle_deactivate_leaves(state: &mut State, leaves: &[Hash]) { // clean up sessions based on everything remaining. let sessions: HashSet<_> = state.per_relay_parent.values().map(|r| r.session).collect(); state.per_session.retain(|s, _| sessions.contains(s)); + state.unused_topologies.retain(|s, _| sessions.contains(s)); } #[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] @@ -1076,6 +1113,7 @@ async fn send_pending_grid_messages( originator, peer_validator_id, &compact, + false, ); } @@ -1368,6 +1406,7 @@ async fn circulate_statement( originator, target, &compact_statement, + false, ); }, } @@ -1505,7 +1544,13 @@ async fn handle_incoming_statement( // we shouldn't be receiving statements unless we're a validator // this session. if per_session.is_not_validator() { - modify_reputation(reputation, ctx.sender(), peer, COST_UNEXPECTED_STATEMENT).await; + modify_reputation( + reputation, + ctx.sender(), + peer, + COST_UNEXPECTED_STATEMENT_NOT_VALIDATOR, + ) + .await; } return }, @@ -1516,7 +1561,13 @@ async fn handle_incoming_statement( match per_session.groups.by_validator_index(statement.unchecked_validator_index()) { Some(g) => g, None => { - modify_reputation(reputation, ctx.sender(), peer, COST_UNEXPECTED_STATEMENT).await; + modify_reputation( + reputation, + ctx.sender(), + peer, + COST_UNEXPECTED_STATEMENT_VALIDATOR_NOT_FOUND, + ) + .await; return }, }; @@ -1555,38 +1606,45 @@ async fn handle_incoming_statement( (active, idx) }; - let checked_statement = - if let Some((active, cluster_sender_index)) = active.zip(cluster_sender_index) { - match handle_cluster_statement( - relay_parent, - &mut active.cluster_tracker, - per_relay_parent.session, - &per_session.session_info, - statement, - cluster_sender_index, - ) { - Ok(Some(s)) => s, - Ok(None) => return, - Err(rep) => { - modify_reputation(reputation, ctx.sender(), peer, rep).await; - return - }, - } - } else { - let grid_sender_index = local_validator - .grid_tracker - .direct_statement_providers( - &per_session.groups, - statement.unchecked_validator_index(), - statement.unchecked_payload(), - ) - .into_iter() - .filter_map(|i| session_info.discovery_keys.get(i.0 as usize).map(|ad| (i, ad))) - .filter(|(_, ad)| peer_state.is_authority(ad)) - .map(|(i, _)| i) - .next(); + let checked_statement = if let Some((active, cluster_sender_index)) = + active.zip(cluster_sender_index) + { + match handle_cluster_statement( + relay_parent, + &mut active.cluster_tracker, + per_relay_parent.session, + &per_session.session_info, + statement, + cluster_sender_index, + ) { + Ok(Some(s)) => s, + Ok(None) => return, + Err(rep) => { + modify_reputation(reputation, ctx.sender(), peer, rep).await; + return + }, + } + } else { + let grid_sender_index = local_validator + .grid_tracker + .direct_statement_providers( + &per_session.groups, + statement.unchecked_validator_index(), + statement.unchecked_payload(), + ) + .into_iter() + .filter_map(|(i, validator_knows_statement)| { + session_info + .discovery_keys + .get(i.0 as usize) + .map(|ad| (i, ad, validator_knows_statement)) + }) + .filter(|(_, ad, _)| peer_state.is_authority(ad)) + .map(|(i, _, validator_knows_statement)| (i, validator_knows_statement)) + .next(); - if let Some(grid_sender_index) = grid_sender_index { + if let Some((grid_sender_index, validator_knows_statement)) = grid_sender_index { + if !validator_knows_statement { match handle_grid_statement( relay_parent, &mut local_validator.grid_tracker, @@ -1602,11 +1660,22 @@ async fn handle_incoming_statement( }, } } else { - // Not a cluster or grid peer. - modify_reputation(reputation, ctx.sender(), peer, COST_UNEXPECTED_STATEMENT).await; - return + // Reward the peer for sending us the statement + modify_reputation(reputation, ctx.sender(), peer, BENEFIT_VALID_STATEMENT).await; + return; } - }; + } else { + // Not a cluster or grid peer. + modify_reputation( + reputation, + ctx.sender(), + peer, + COST_UNEXPECTED_STATEMENT_INVALID_SENDER, + ) + .await; + return + } + }; let statement = checked_statement.payload().clone(); let originator_index = checked_statement.validator_index(); @@ -1625,7 +1694,13 @@ async fn handle_incoming_statement( ); if let Err(BadAdvertisement) = res { - modify_reputation(reputation, ctx.sender(), peer, COST_UNEXPECTED_STATEMENT).await; + modify_reputation( + reputation, + ctx.sender(), + peer, + COST_UNEXPECTED_STATEMENT_BAD_ADVERTISE, + ) + .await; return } } @@ -1733,11 +1808,11 @@ fn handle_cluster_statement( Ok(ClusterAccept::WithPrejudice) => false, Err(ClusterRejectIncoming::ExcessiveSeconded) => return Err(COST_EXCESSIVE_SECONDED), Err(ClusterRejectIncoming::CandidateUnknown | ClusterRejectIncoming::Duplicate) => - return Err(COST_UNEXPECTED_STATEMENT), + return Err(COST_UNEXPECTED_STATEMENT_CLUSTER_REJECTED), Err(ClusterRejectIncoming::NotInGroup) => { // sanity: shouldn't be possible; we already filtered this // out above. - return Err(COST_UNEXPECTED_STATEMENT) + return Err(COST_UNEXPECTED_STATEMENT_NOT_IN_GROUP) }, } }; @@ -1788,6 +1863,7 @@ fn handle_grid_statement( checked_statement.validator_index(), grid_sender_index, &checked_statement.payload(), + true, ); Ok(checked_statement) @@ -2366,6 +2442,7 @@ fn post_acknowledgement_statement_messages( statement.validator_index(), recipient, statement.payload(), + false, ); match peer.1.into() { ValidationVersion::V2 => messages.push(Versioned::V2( @@ -3245,6 +3322,7 @@ pub(crate) fn answer_request(state: &mut State, message: ResponderMessage) { statement.unchecked_validator_index(), validator_id, statement.unchecked_payload(), + false, ); } } diff --git a/polkadot/node/network/statement-distribution/src/v2/requests.rs b/polkadot/node/network/statement-distribution/src/v2/requests.rs index bed3d5c18ae2b1007a3ee585f00a54d1b98f7c27..bbcb268415e67141ff1c620a134582d06bd8a67f 100644 --- a/polkadot/node/network/statement-distribution/src/v2/requests.rs +++ b/polkadot/node/network/statement-distribution/src/v2/requests.rs @@ -315,7 +315,16 @@ impl RequestManager { request_props: impl Fn(&CandidateIdentifier) -> Option, peer_advertised: impl Fn(&CandidateIdentifier, &PeerId) -> Option, ) -> Option> { - if response_manager.len() >= MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS as usize { + // The number of parallel requests a node can answer is limited by + // `MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS`, however there is no + // need for the current node to limit itself to the same amount the + // requests, because the requests are going to different nodes anyways. + // While looking at https://github.com/paritytech/polkadot-sdk/issues/3314, + // found out that this requests take around 100ms to fullfill, so it + // would make sense to try to request things as early as we can, given + // we would need to request it for each candidate, around 25 right now + // on kusama. + if response_manager.len() >= 2 * MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS as usize { return None } @@ -1027,6 +1036,7 @@ mod tests { let peer_advertised = |_identifier: &CandidateIdentifier, _peer: &_| { Some(StatementFilter::full(group_size)) }; + let outgoing = request_manager .next_request(&mut response_manager, request_props, peer_advertised) .unwrap(); @@ -1148,6 +1158,7 @@ mod tests { { let request_props = |_identifier: &CandidateIdentifier| Some((&request_properties).clone()); + let outgoing = request_manager .next_request(&mut response_manager, request_props, peer_advertised) .unwrap(); @@ -1230,6 +1241,7 @@ mod tests { { let request_props = |_identifier: &CandidateIdentifier| Some((&request_properties).clone()); + let outgoing = request_manager .next_request(&mut response_manager, request_props, peer_advertised) .unwrap(); diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs b/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs index 7ffed9d47d4bdeca1800f7d665970cd1882ea7b2..a944a9cd6d021364d3b9fa7b349e9a649866cf29 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs @@ -170,7 +170,7 @@ fn cluster_valid_statement_before_seconded_ignored() { overseer.recv().await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) => { assert_eq!(p, peer_a); - assert_eq!(r, COST_UNEXPECTED_STATEMENT.into()); + assert_eq!(r, COST_UNEXPECTED_STATEMENT_CLUSTER_REJECTED.into()); } ); @@ -305,7 +305,7 @@ fn useful_cluster_statement_from_non_cluster_peer_rejected() { assert_matches!( overseer.recv().await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) - if p == peer_a && r == COST_UNEXPECTED_STATEMENT.into() => { } + if p == peer_a && r == COST_UNEXPECTED_STATEMENT_INVALID_SENDER.into() => { } ); overseer @@ -359,7 +359,7 @@ fn statement_from_non_cluster_originator_unexpected() { assert_matches!( overseer.recv().await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) - if p == peer_a && r == COST_UNEXPECTED_STATEMENT.into() => { } + if p == peer_a && r == COST_UNEXPECTED_STATEMENT_INVALID_SENDER.into() => { } ); overseer diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs b/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs index 1ac9f4d45e7148e31af731df073985d795ec05e6..38a12cf32e3b37cd111b4404b53015376e749192 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs @@ -437,6 +437,7 @@ fn received_advertisement_after_backing_leads_to_acknowledgement() { validator_count, group_size, &peers_to_connect, + false, ) .await; let [_, _, peer_c, peer_d, _] = peers[..] else { panic!() }; @@ -608,6 +609,7 @@ fn receive_ack_for_unconfirmed_candidate() { validator_count, group_size, &peers_to_connect, + false, ) .await; let [_, _, peer_c, _] = peers[..] else { panic!() }; @@ -676,6 +678,7 @@ fn received_acknowledgements_for_locally_confirmed() { validator_count, group_size, &peers_to_connect, + true, ) .await; let [peer_a, peer_b, peer_c, peer_d] = peers[..] else { panic!() }; @@ -837,6 +840,7 @@ fn received_acknowledgements_for_externally_confirmed() { validator_count, group_size, &peers_to_connect, + false, ) .await; let [peer_a, _, peer_c, peer_d, _] = peers[..] else { panic!() }; diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs b/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs index bb780584febf1bd267ee63fbe6b53f14e6e9c6f7..82986a0330ec25d35f3b75a94be67157283bbd9a 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs @@ -437,6 +437,7 @@ async fn setup_test_and_connect_peers( validator_count: usize, group_size: usize, peers_to_connect: &[TestPeerToConnect], + send_topology_before_leaf: bool, ) -> TestSetupInfo { let local_validator = state.local.clone().unwrap(); let local_group = local_validator.group_index.unwrap(); @@ -481,10 +482,14 @@ async fn setup_test_and_connect_peers( } } - activate_leaf(overseer, &test_leaf, &state, true, vec![]).await; - - // Send gossip topology. - send_new_topology(overseer, state.make_dummy_topology()).await; + // Send gossip topology and activate leaf. + if send_topology_before_leaf { + send_new_topology(overseer, state.make_dummy_topology()).await; + activate_leaf(overseer, &test_leaf, &state, true, vec![]).await; + } else { + activate_leaf(overseer, &test_leaf, &state, true, vec![]).await; + send_new_topology(overseer, state.make_dummy_topology()).await; + } TestSetupInfo { local_validator, diff --git a/polkadot/node/overseer/Cargo.toml b/polkadot/node/overseer/Cargo.toml index f39f0661aa1787526490d6e4fe8ad2e2d08b3134..f91ec80d944064f5cba28275766c0fbcc9231133 100644 --- a/polkadot/node/overseer/Cargo.toml +++ b/polkadot/node/overseer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-overseer" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -20,7 +20,7 @@ polkadot-node-primitives = { path = "../primitives" } polkadot-node-subsystem-types = { path = "../subsystem-types" } polkadot-node-metrics = { path = "../metrics" } polkadot-primitives = { path = "../../primitives" } -orchestra = { version = "0.3.4", default-features = false, features = ["futures_channel"] } +orchestra = { version = "0.3.5", default-features = false, features = ["futures_channel"] } gum = { package = "tracing-gum", path = "../gum" } sp-core = { path = "../../../substrate/primitives/core" } async-trait = "0.1.74" diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index f4eddf1f41ceb90d61391ac5140941bdca8b0bf1..e16a3fd27ab3d920da5c83b3c4ee02ab4ccba2cb 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -465,7 +465,7 @@ pub async fn forward_events>(client: Arc

, mut hand message_capacity=2048, )] pub struct Overseer { - #[subsystem(CandidateValidationMessage, sends: [ + #[subsystem(blocking, CandidateValidationMessage, sends: [ RuntimeApiMessage, ])] candidate_validation: CandidateValidation, diff --git a/polkadot/node/primitives/Cargo.toml b/polkadot/node/primitives/Cargo.toml index 74152b5b7da9a5bd09a741e736c7001a5b76e1ce..b4541bcc346c8ce4282947c38e0d9a2591631090 100644 --- a/polkadot/node/primitives/Cargo.toml +++ b/polkadot/node/primitives/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-primitives" description = "Primitives types for the Node-side" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -22,9 +22,9 @@ sp-maybe-compressed-blob = { path = "../../../substrate/primitives/maybe-compres sp-runtime = { path = "../../../substrate/primitives/runtime" } polkadot-parachain-primitives = { path = "../../parachain", default-features = false } schnorrkel = "0.11.4" -thiserror = "1.0.48" +thiserror = { workspace = true } bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } -serde = { version = "1.0.195", features = ["derive"] } +serde = { features = ["derive"], workspace = true, default-features = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] zstd = { version = "0.12.4", default-features = false } diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index e7fd2c46381499f221913a01e7a9c3bc1dacc2d3..d295c21cce1dc9f609c8068fb806cff69612f7d6 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -58,7 +58,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.6.0"; +pub const NODE_VERSION: &'static str = "1.8.0"; // For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node // plus some overhead: diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index da3f03193774b0aa3249b0abd81d7e915711933f..180de70ad6c5c1a37cfbec46ff798b31c281083b 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-service" rust-version = "1.60" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -83,16 +83,17 @@ futures = "0.3.21" hex-literal = "0.4.1" is_executable = "1.0.1" gum = { package = "tracing-gum", path = "../gum" } -log = "0.4.17" +log = { workspace = true, default-features = true } schnellru = "0.2.1" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" -thiserror = "1.0.48" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } kvdb = "0.13.0" kvdb-rocksdb = { version = "0.19.0", optional = true } parity-db = { version = "0.4.12", optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } parking_lot = "0.12.1" +bitvec = { version = "1.0.1", optional = true } # Polkadot polkadot-core-primitives = { path = "../../core-primitives" } @@ -184,8 +185,8 @@ full-node = [ ] # Configure the native runtimes to use. -westend-native = ["westend-runtime", "westend-runtime-constants"] -rococo-native = ["rococo-runtime", "rococo-runtime-constants"] +westend-native = ["bitvec", "westend-runtime", "westend-runtime-constants"] +rococo-native = ["bitvec", "rococo-runtime", "rococo-runtime-constants"] runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", @@ -195,6 +196,7 @@ runtime-benchmarks = [ "pallet-babe/runtime-benchmarks", "pallet-im-online/runtime-benchmarks", "pallet-staking/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", diff --git a/polkadot/node/service/chain-specs/kusama.json b/polkadot/node/service/chain-specs/kusama.json index 979550c7570643380c5a2e0f0f5de7c049c01f85..fd60cb8b6c1d443d47a85afcb7b885b4cf0593ae 100644 --- a/polkadot/node/service/chain-specs/kusama.json +++ b/polkadot/node/service/chain-specs/kusama.json @@ -16,8 +16,8 @@ "/dns/boot-node.helikon.io/tcp/7062/wss/p2p/12D3KooWL4KPqfAsPE2aY1g5Zo1CxsDwcdJ7mmAghK7cg6M2fdbD", "/dns/kusama.bootnode.amforc.com/tcp/30333/p2p/12D3KooWLx6nsj6Fpd8biP1VDyuCUjazvRiGWyBam8PsqRJkbUb9", "/dns/kusama.bootnode.amforc.com/tcp/30334/wss/p2p/12D3KooWLx6nsj6Fpd8biP1VDyuCUjazvRiGWyBam8PsqRJkbUb9", - "/dns/kusama-bootnode.polkadotters.com/tcp/30333/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", - "/dns/kusama-bootnode.polkadotters.com/tcp/30334/wss/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", + "/dns/kusama.bootnodes.polkadotters.com/tcp/30311/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", + "/dns/kusama.bootnodes.polkadotters.com/tcp/30313/wss/p2p/12D3KooWHB5rTeNkQdXNJ9ynvGz8Lpnmsctt7Tvp7mrYv6bcwbPG", "/dns/boot-cr.gatotech.network/tcp/33200/p2p/12D3KooWRNZXf99BfzQDE1C8YhuBbuy7Sj18UEf7FNpD8egbURYD", "/dns/boot-cr.gatotech.network/tcp/35200/wss/p2p/12D3KooWRNZXf99BfzQDE1C8YhuBbuy7Sj18UEf7FNpD8egbURYD", "/dns/boot-kusama.metaspan.io/tcp/23012/p2p/12D3KooWE1tq9ZL9AAxMiUBBqy1ENmh5pwfWabnoBPMo8gFPXhn6", diff --git a/polkadot/node/service/chain-specs/paseo.json b/polkadot/node/service/chain-specs/paseo.json new file mode 100644 index 0000000000000000000000000000000000000000..8db75ff137b0dbe338859a22ad7361f413c0fbea --- /dev/null +++ b/polkadot/node/service/chain-specs/paseo.json @@ -0,0 +1,229 @@ +{ + "name": "Paseo Testnet", + "id": "paseo", + "chainType": "Live", + "bootNodes": [ + "/dns/paseo.bootnode.amforc.com/tcp/30333/wss/p2p/12D3KooWFD81HC9memUwuGMLvhDDEfmXjn6jC4n7zyNs3vToXapS", + "/dns/paseo.bootnode.amforc.com/tcp/30344/p2p/12D3KooWFD81HC9memUwuGMLvhDDEfmXjn6jC4n7zyNs3vToXapS", + "/dns/paseo-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWADeayZC8zag4Qrb4GosSn65MmfVZztRPMaBdgZnQqXRo", + "/dns/paseo-bootnode.radiumblock.com/tcp/30335/wss/p2p/12D3KooWADeayZC8zag4Qrb4GosSn65MmfVZztRPMaBdgZnQqXRo", + "/dns/boot.gatotech.network/tcp/33400/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", + "/dns/boot.gatotech.network/tcp/35400/wss/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", + "/dns/boot-node.helikon.io/tcp/10020/p2p/12D3KooWBetfzZpf6tGihKrqCo5z854Ub4ZNAUUTRT6eYHNh7FYi", + "/dns/boot-node.helikon.io/tcp/10022/wss/p2p/12D3KooWBetfzZpf6tGihKrqCo5z854Ub4ZNAUUTRT6eYHNh7FYi", + "/dns/pso16.rotko.net/tcp/33246/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu", + "/dns/pso16.rotko.net/tcp/35246/wss/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu", + "/dns/paseo.bootnodes.polkadotters.com/tcp/30538/p2p/12D3KooWPbbFy4TefEGTRF5eTYhq8LEzc4VAHdNUVCbY4nAnhqPP", + "/dns/paseo.bootnodes.polkadotters.com/tcp/30540/wss/p2p/12D3KooWPbbFy4TefEGTRF5eTYhq8LEzc4VAHdNUVCbY4nAnhqPP" + ], + "telemetryEndpoints": [ + [ + "wss://telemetry.polkadot.io/submit/", + 0 + ] + ], + "protocolId": "pas", + "properties": { + "ss58Format": 42, + "tokenDecimals": 10, + "tokenSymbol": "PAS" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0595267586b57744927884f519eb81014e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x06de3d8a54d27e44a9d5ce189618f22d4e7b9012096b41c4eb3aaf947f6ea429": "0x0900", + "0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385": "0x0000300000800000080000000000100000c8000005000000050000000200000002000000000000000000000000005000000010000400000000000000000000000000000000000000000000000000000000000000000000000800000000200000040000000000100000b004000000000000000000001027000080b2e60e80c3c9018096980000000000000000000000000005000000140000000400000001000000000006000000640000000200000019000000000000000200000002000000020000000500000002000000", + "0x074b65e262fcd5bd9c785caf7f42e00a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x0f6738a0ee80c8e74cd2c7417c1e25564e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x1405f2411d0af5a7ff397e7c9dc68d194e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x1405f2411d0af5a7ff397e7c9dc68d196323ae84c43568be0d1394d5d0d522c4": "0x03000000", + "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x196e027349017067f9eb56e2c4d9ded54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1a736d37504c2e3fb73dad160c55b2914e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x10b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a65010000000000000058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe3233020100000000000000facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe4440000100000000000000ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e2455330100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x10b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a65010000000000000058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe3233020100000000000000facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe4440000100000000000000ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e2455330100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x2099d7f109d6e535fb000bba623fd4404c014e6bf8b8c2c011e7290b85696bb3": "0x10f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e876892cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54326e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", + "0x2099d7f109d6e535fb000bba623fd4404e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2099d7f109d6e535fb000bba623fd4409f99a2ce711f3a31b2fc05604c93f179": "0x10f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e876892cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54326e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x00000000070d8aa99f1452f92000", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da90ffdb9747f7a8bfa200ea2291eea6bdf9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94d176c4abc6d2410c43423c2714b909c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000": "0x0000000000000000010000000000000000e40b54020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da965bb58c29c9825c1f6727fe8c41650ac82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9829f3f4d6d7c991993e0c044ae97410b043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x000000000400000001000000000000000000c16ff28623000000000000000000000000000000000000000000000000000080c6a47e8d0300000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e6fb488a1496189393ed0a95dcf5577e7e939ef17e229e9a29210d95cb0b607e0030d54899c05f791a62d5c6f4557659": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x02093d0014706173656f", + "0x2762c81376aaa894b6f64c67e58cc6504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2aeddc77fe58c98d50bd37f1b90840f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2b06af9719ac64d755623cda8ddd9b944e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x2b06af9719ac64d755623cda8ddd9b949f99a2ce711f3a31b2fc05604c93f179": "0x100edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d74bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584eca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b0544606bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58", + "0x2f85f1e1378cb2d7b83adbaf0b5869c24e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b65153cb1f00942ff401000000": "0xcee7559d1a7431ad9a589c49d42b6e5da57d8208d290516347b21415449fbf4c04000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b6b4def25cfda6ef3a00000000": "0xcee7559d1a7431ad9a589c49d42b6e5da57d8208d290516347b21415449fbf4c04000000", + "0x2f85f1e1378cb2d7b83adbaf0b5869c2ff3ae12770bea2e48d9bde7385e7a25f": "0x0000000002000000", + "0x31a3a2ce3603138b8b352e8f192ca55a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd00580c6105dee2866016521030cf2a1d73eebb153057df9bb472cc0a860361c473d10e8f9f75d9071693fda90415453adee41bf641bf24fc4306beefa61e5e18c0cbb36d9bec46d011d098dc9c6d2bd360511bf97b6f42f6de5ba6245368194d145f153c9d801bc50970a58c7bc312c497e3b3cbd8a80c636c54ca9e9d7d081bcd672c609e1dc646fceccb479356fa3cfb8a0bd032c606ed2b7128347d00b7c8b83ea38c7bb361fdf8e5d73f36da9badccaf876c348f9656c27e07006ed2387d7d5be0b2d1dcf9ebe06bd968d9cbefd7291b4d5f0727cf02b488b141fbe05c911de470f76601596460d97e536f9fdea2925977de2c200b1896edb337097638c9f92cb2af48ab12afa8844ef100b567259e7380fb1f1b95ecedeb3c497618d9277355e2da9c4bc0e9fb53fab34ff201e3cd66fb923e3b65a375f66ffaf7f5d1f2ac1c7f7a397ef93dbbff0aa9cd3942b7cf3979b62bf1ec7676eaddddd37b32fbf75c7b98b053e7599765e292e95c4bf6e94aacc4fbd395c97f85d478ce2ec0fd29fdd9c1cd600417695c9695dfb3b7afa8a47d8a1a9860b3d9b07c3bc8e1b2d1f492fefab251fbfa8acb3276256e92b28cfd682cc7679f4e9d1d84b1d13e3b38c3918d9665ebec30369acf1e63a376f61595b42bb11253f24a9087aafdac3d4cbe9ffefdac3d4ca6539f4e6bd4fcaecbb292fefa8a4a662de74f576225de1fae4cfe2ba4d6736e01eeef4f393e3bf8b15139bd7d9d27c90ee3f4327cf6b27d59b60e36f9b16c9d689cdebebe6207d399ba7e2c5ba72c5b57627092fce1263dcddb5fe97e3abbb8494fd3f64e03f8e9ec2087bb49d3cbf665d9fac746e5fcf675b0c98f65eb4363fbfa8a1b5096ad77fd58b6aec4208793a41f6ed2d3cc7a27efafe0fc747671939ea6ed9d06f057c29fce6eb3494fd3f64e03f82bdd4f677f6dd2d3b4bdd3007e3a3bd8e124fbc54d7a9ab6771ac05fe97e3a7bb8494f13cd5f097f3abbcd263d4ddb3b0de0a7b3afd801b5d97eba124f71c968fbe9211bedb3afb864da7eba122bb1122bf192cbb2f5fde1ca34fe0aa9f1e435c06570195c24e3f4f56d21a9833fd0c20aae2431246101490a90e881040f2470202101923790c481e40e2423409207123248be404205121790d480e405243224dd92c4400203921890cc80848624169074414285644b520b48b824a5901443920992ea484241d214198c49a222e904495924e1911446d218495e248191b405922c495720d182448524aca42e92ca40b242920792be482a419215495524199184449298a41990b0904443929924aa0cd264c044064a64f04506502449914112495d3258228330327022898e0c8e48d222894c1216495c2495c9e089242d196065104712144944240991810892de487223294b0623c8c08da4393200230331322823292be90348c020b1010996a42592a2487222498e0cbc4842930482a438924490f444120990a48074061218485f20758184855406e90a242a9064404a026909241a9036808404120f483d206900290c120e483e20fd8054051219242b907440ba42ca01e906242590c0208d412a02e909a40e20058104051211483320d940d4a5888ea23a8a4a506482a2111499513444911045411475a00888220e148529faa1c887a20d14f550c44351154556149129a2a2c889a2268aa62852a3288da2334568149d51d4451117455b1469519445111645658aae28f240511145628a88a041143151b444d19822258a922842a2e88822238acc1495514446d118453814dd50344351068a5e28a2a1a8aaa84b119722ab22188a64209283680ea23a884c400407d11b4423208a830804446a88ec204201d10988c220fa82080ca232888e2082820809a214105d41f4049114445310dd88b420c261480d1116a21486483024c7501b43730c610d5d31c4c51013434a0c093164c5d00d43381c61e3481e432c0cb97024cd912b8eec71248b23681c01c191368e9c397282232138f2c611111c71636885237a1cd1e24899211586aa86b80c6519a21ad232b4e5481947d63862e6089a2363fc8b913b8ce0c16e7811302af80c2328e037a606aa1e312b8c9461640c223378822022c3c80e4379f8e0e1a30696858d169b148aa4a0481945ee2862a6089a221ff02b86ae8672e830806cd0690156850c861f2e300b48e420d246921a4e44d211b132404b00c901c80d449c00028648134498008283634164892176780d18b041240c110e10010288ed87123213f8d8414409227a08b11141027606d01940b4ec906007053c48ecdcc1c3028f179e1676460084ca070f46acd81141bfc10326280515580d3511c487236414c962e78c991b3c43f86061288a9c2c76e2086a8327892010f06c095a63c7044167621ce0e11234c69013b33678c6047921a3baa08bec043e2008a2da39418e16413610a4875816205d7cd6e460c9628871d9b12308882258fca8198262f6c60f167cf6f08165678d217a046de191418786217bc4b0c0000418a0c1400d0cceec7c609666e7cc4c04332966b71919b3286669ccceccd698a9319b022803332b6664664c245161162676c5ec87990fb30de874a958b013c84a20cb92c531244bf682cc4aa605680fa03c80f00052038402a03a804a0094054402a03880e0004a032402a03580d820aa01080d901a41ee08a207ac07d80678b400c221e601201d6262807278cde12900ed102b02e80bd00d312378b6e0e102e80a0808202284c4003484101980c408a101a80821330009212403401e000a42480d42be008501a2828f1bf58c111788b04044059e1c76e0a868f01441c4cb8f16580344b220a245055a783ac033034f161e2d414a50dd702d5504158e223b764240c4852174d430aa173b20e0a1e25921c81e3a566e457d23c8098a80112405414c00c44310140451e359bc0aa88620790081098287db7ccb8b04be826c0659065c059812b024dc05242db80c0e83bf50a4825bf938011ba248999d3d865ad89183c8871f2ffc8041e78b4e0c415408d2a582121cf1020916500f3fac2af85241063823d80b5808182b698f24362ac0c2360021118b0289890c52907303c2e293029f3b6cb010e1425b00d20050123c63141183a70b1e2f7474a8800b101a3f9a187a02e88d1c2980eef8a103e80c902c312d4026c899c28605a035b0334072bc6e365b7eec006407902d406efc946036021b1580da8099817551d7f8e4f183099819156c81810113434808848000e804b00f00d15141085e3bc0d0009aa302157456001ac18e183b5fc0d6f8b104d00772b6105205a407500a8042b003c68b8a236600e1a25326b66516c76b0a205874b0289a63c78c970f455b606e106591404c892261d4361c4b4d03a4444f0e31276250c49e804d019322c604511eb1266249502086dc51cd50332ea60787980fb10d101902c9cc10131c7161878cd80b31ab1d343c61743cc0a343ab21f24264851f46042902c606cc0e58160f0f3f74f82103112b2330f8e861c46a670e2010f0882172044f0a15500d29814e0674b87029c0c8806d3184044350e06476bcd84183c8061d1ca89620297484f0598347033fae606d0cbdf092029606ac8c5e0366056c0c2234302e605a201d11ebc1081a447e9825012443912e5e560461e20799fa058c0ae6015811303130236044c090801d01fb813f000b0303a247075810b00e04b10396c34b8e23627894b0e9b21306ec0a1d1988ec9113460e183d6062591879814815912d33387862e0d940cfd52b8ea23b869c00830c14b9721bd8082433780c03c11dd019036803f388514d9035ce05690c6f82a80c4c8e1e0d38d578c4ab8e318908bc4af0caeab9e145c75016af375e6986d431448df3e04038073c8cffe03ef80686ccb183c74e0a8e8041e405225d76d8e0b94107cc113c8edc6124061e23a612445a401222481d3c553c30341c1d87cd15938b5a066b51e48b222d3644f834e1c3c410962367bc7ae079c1cbd850f1a86093e5081642b4bc7878fda053354405c3a14386102ebc42067a50178e8c11a44383f10389203c10ed4124890a7ca86420f9c253032c052dc705b62369c04af02309202b34968f1d8e8461b3820f1d6061e86800481e485208f940520e5c065115dd848f1e92e0481a41066fbca2c8a0043c030c0d9b119265a70c215886c8bcc8c0b082b0c0d498d1f0da001d434817d81c47d604d921880e41ae74b6e8500d65402705242d8450250d61e4899d12e86881e9b193467f61e345074b911a581a1e2ba22f3a2c2435e155ecd0314443901b908080e161840c9baa991c49587e4620c44b521a19c8c15b6460068897231e602f3f70c0f2c879024855066b9a8b1c366cb6b0e9c2860b1b2f86be04a9222243640d521442561092c2d014478480ad11a202ec8e0bb2f01c21048d8e0b3c38086101884c8fa0282b033a3280e38214605ed0333a579c86c7079d1d605f004981870347be388265c3250332807020a98d234820c1218331846c81c501a48566020647121a1760493a936446100f00d162a80e98088e14c12a00a53154050c043013d02cca048d82de681afa049582be419da070d026e81440476010020cdaa078d03ba81a226390380044066ac7eccb6c06581d3b583a30f06021b282280b1d1b88b020520791087868d0f142f401d90880c000c40a880b4062a8573b667466808d0046079119a2338c98b1b366678c1d35746ed05141e7859d36783c60a4059d1a602480a9e1c9804e0e3b593a5976f480bd0143818e6dc83604c390d5900c47d43862c42bcc11318e1c01e485a12d906e455440588061c1d3a5c84bd10a394d10e901db83a707242f606580ec8081160cb660409583821c353976f8bce133021f38646a6478c8eef8f1c30f0efc0853ada865ea1545c61461a2c812332266625e59bcb6786911c48b205f04c19add007361680898158c0bacea05c64b8c571841e0f871c58f323f6ef891c30f1c8a1051a488226288a041240d22678cc061240e2323a8515429ea4d670f1f369d354134047d09cac08d0c3734dccc60b3e685e565cb39d37be4a4a12387ce1c3a24c8d123674dce1e152451c1980a94a82c54176a0bb3aa990ab32e168cc002392c88a382ab0a76a840070bd4e01258b00699d52120e968326ccab031c3c6cc0f296ceab039818d0980d001c404404a504110231863183e6af0a1011f365430f586aa811baa1b2e375b76f2e0b1edd4c1e302cfd58e1b3b6a786ce0f9c243c44e9a9d37787ee0118267071e20883a7024054331f416445bd01b280e3487a435b48a72e93c908618ba63a8049da6dd681310ad4004a6c7683188f020baa39de824faaa8968311da677e80eb412cd43ebd041b407fa4b83691c7a8846a26fe81fda87d64017d1363410cd81eea18fe81a7a4ce7d01b68235a88211410adc174701ccc06b7c122603518048c86d7e010b01943763019ae6286e129580abe71147c048be130ec0373006907fe8193183a016f807be01a9806fe326402de81abb80bf3403b8054c6cc626a31b7984ccc25a6969965a630b14c9b950640e083f9030833070750214184a50a78b00005342140070940c0010cc0c1061a28a14d27e6182345478c844600d500e7effca1438b7b090a4a059c5881b224c5891370760854969c38710505ed7421b4c345531394284d4d5008b08594a811a82c3569b14b4b2238a912e5244aca0853962c30dbc962a19aa640895a0ac1c9d2920817ec60b15544a032c5c9094f4b2538399962e5a3a82c9170029426be536697a250a942c509942854aa2420ca03d9b922842a51964208b263c5429db0c40475c2121306903b55404d29c0094fee9029daa1e267678a3561890ace8e147b5b2951aa5859ead989629f4a18618a0854aa447132c5ca2f55000a4d67078a0b769e40da71624398e2246a09eaa984283b3b4d2cd4d294a8294f55a4449500c5c9084ea65879285642a002e569830a76985828a8a526203b4be4ec8c5162a3a89450258a1311a02c35e9ec24b14f24904002025e3b4858b073c486b04465842950544a7012a5ca952925382d19ed18b1514b504e3cb05053a04e106169a9024ea658f91344585aa2f9d82962a3a0aa343979aa6265e949cc8a50a5ca93152756a648817a5aa262812a4e4e586222778858a8252950509aa09c84b034a5c98a1528544a08b233c4422d459560056a4a083e768458295050a4d49d2076e9c98a93a5a72925382d3d398112e18a15213b1dd8282a4b242c4541898212e18a15d90e100bb51455e58a132754aaf8ec70604fa8809330504d4b54a8409112c58915129ea43809814a09552a4065ca0f5154aa4471e24442083e2c1409509ea05871f25465035b45049e9d1e162a8acad295251e76290a4a085342a00225ca909d1d7629842a5180ece8b04f5544805a82ba5aa8a812ac44955005682707124a80828262c10e0e0b250a4a084ea8549912c25214d414a8a7252b17ecdcb0504b4f55a23839216a4ad3940a40699a02e50a14df01b350564aa8f23482930a7634b0504f55a2a0aa7000e5094a93d88e0d6b812956fe6989e9a98a033a20776ad8a5a512964a508007212c8d3002140f00b0f365a1445529c1c90914152753acfc0854a63041896280cf4e06b64a94131a76290a15284d502ae0a44a9413a8282a53aafcecccb050213c41b1c0d2939311a84c0981ca92152753ac3c5453952a4da85499120585ca1412aa24a0eec8b0504c5196a4448961a1a2a82c3d2d49a132c57760a03245849e9d1736ca14282a539ea24c5872724293152b509c3c2d85d034a5041a53942a4d349f1dab85e2d97161a138f1d96961497002250a4a0850a2961070b3c3c205a6344199123585ca149d1d2fcb14a54a53059c2c45951065c984259c9d15f6a90a01c29d4b4d15801245a504119c4445597a9a120295294e9cec2c81c9e081a105cd6f0ddc9bcdf6b3d63b1db806b8c6b4323dfaf578cfe75f4fcff77ddfd7bbcc54ecee6e6ef7833bca4cc7edb6b9ebe6eeecdeaeebe672efecbaddae97776eb7bbdd757f1dedba6e7767d7f4ebbaedeeba053d8f76f7d72d7acdcc226fd7f2e8f2ccf5a80f6e71e734479b03993bce6bdeed3aee9a365dba1d04887437a9ef8f8e865e2fb38db74bbb026f9b7638bc61d7d1deae9b9b2eb7d7f570d731d38e763f28bda0a3dd32dd6daec3deee7e703fa0bbfb477773cefd3ceffbe852dae0521b1bda607b1ef53aca0c720882d7dc0ec0a09b3fe80e3b6e224cbd6ec0fa6ec774b9bbee6370bfa58d036eb75e8c7a33c85c0f3aee2006f402767977bbf0dbddde95b1b79333bbeeb8fbb883bac9dbcddd75d3dd21ddfd7a756f0f69cabdb3e3791d8436a5ed75b7473dcff33c4a3da6de647675dd1ed80a601d1d9dd993276dd2ed76c8b3bbfb075b30f67a1e0540f7a4cd1dcc2e01ddbb949bbfba4166d760d39b9b1b6646c0d7cd9383508f7976b72165a6947aaffd983deea9d3cb71dddcea5e6e77b6ba5bdbda566bb7d56a6df74d77377bbbbddddcd2b6e9eeedb6bbebf0e38e59c0b46da82732337f7c00cacc5dd73177dc0c8add819dc71de8713779765de7791ef5769b72bd74297b9477bd8fbb9be93237ef2ea5bdbb5e874cbaebe69cbbe16800ee70c9641a7608807e31dd6eca1caba0b7db6e829cd7bddb2d07dd9eb7dd8b7bceee39a74777b71bc4d9607976cf9d5f476d6c30605148a7016d9edd3173b7bdaed9ec66dd4a9a520ff428a5cccddc71b7bbdd36dd5eda5d1736c7ade59876d3d975ddecb83b2cc0d72debbd809b3673d3a64d9979f2f6f77dccccfb639b0094f9a7797bb7bbebfd68f7c7d4a31defecc066baa1b7bcdccdbccdfdb3db4dd7029ad7b4bbb7db66973d5e5e66ba5cd7cb2d33c37876bb777a3c419edbdb8cd371b7ccb3bdee9a32e5ee3aef6bf6b6692fb8ddafed9e61d38f6977b737bb67f786e118766c0cc39f7d333b3a2773f7722c0c976d7ab9576776d7e20de9584105aff0d51c762f2b51a69e3752cf1b80e779d4f37e3c01785e00c6eeee26a594b97bf2ecbaaeebddee769777777227e99682fbf5ecd9755df7c74cb9bb9b524abb6753eee56e4ab79b6ed751daccb4b997f2d2edb6dbaeeb3adad16ebadb743bcfe3a6db81dbddf6ce9ddd766f37a5db75dfb7dd323377d36ebba674b767ef5266ee8984c7eee6e6eeddedee66cadd761d737777ddcdccdd2575403c8f99bbeef3beae1b27032024203814c81110103ae7ecbe0fa7ebbaafeb7c187545f3fb70e6c7f39b389fd77536ccdc5dd775dd31cfc95dd75fd7ed761c534a41ca94f276dccddddd5cd3afe3ee761b491d4850d03acb273c55216189848ec8088809552c1055011394285196988c8a8e38ec889111a84c910205458a1385b0f4b414c25315274645479c1c596282d25401274b539ea23018ba801c425e701342d31429519c443595004584a51066478e8c30058ad4396189c94994294d1528c18aecc90228a8a5272b24548972e4da234b57aa44597a7202052a84284b4e8c3019151db11265096a881011a63c2d5d1152a3a03c2d51591ac14906468a8a8e84b0e4a44a9495139ea258d06a5abab2d4e4c404282404798d50a5c9c90854a68c304295a69d1b139642a032c52709bb46969e448852a5a9098a0f8f1512a044412d913025ca7d2a5002d4acb5b404e584842a215081f2545f50322332a110a84071b2f404c50a095588769aa044a9d28441ab4a149410a63441b9e2046a8a132a53a0488922820855842c9130258a4a094f35074984a81280905002159f1c1396a43889a232e5c98a93285053487882120575c10dd48ece1ea952e58987290a940a34355579c2a00894a5279e1396989c4499b044a5029fa8a5117660505150534c58f2e1d608952a53a2a24081a202e529ca49d4141248a82223f788095344587ab252739ea02c3545e9b94265a96fd688132a55a6403d2d5d71120295252b56a038591a01ca539528282751262cf95ca1b2d4422f23786b53535fd8a235afc64d4d4df554ed7b529bae27b5393535353555eb5a2d3ea9cd5aeb496dd65e5353cdfd646a4ecda9a9a9599b9aea273b35e5d5a6f8c94e4d6d6d6a0aac7d53535353357e32356bb57eb2b5ee496d4e4d4d4dd5b82753739fec546d3ea9cda9f964a7e693dadca9297eb2b5a9a95aadd675cd7b529b5353dd93a959abf193dadc27b559abd5bac63da9cdadd1275babf193add56afda4366bf3c9d6e69308742cccdd9d244041d9ffb573f77b03c2853a8462335675e6d97d7ef888612c833d692465dd9f6b902fc31840104690fbbdf110c434c2889ead06a8fd3a0a41ac2f4d88ad1b049c43eb6bb34628bde0cbdf5b5598dda452a8c695b496534fbd6c216043e04b7ece9194e4ba882cda576ddb8515f5b2f57d81af08810b5e68827174e4853aa5e4c4f2eb3d9daf96aa8ff679cb3db2eaa379be6439e501db689fb7c82d4834ef0a7dce7b96ccabe7b8aee5544f579b78beefc148fbbaae3abd9eab3f7cb56f57ea69fbcf23bbc631b77004c60c25ab288d7a931e8c34eabbddd5e9f53f504a6ecf9679de50830d1a9845c01cd96066a4db1ad83da37b3be02bf369fb92eb34bf7d7783d6a715b8208cd3cb9065000041a8f9dcaa4cd35b3498f9929fe606711ef5bdd3ae79d057d4207497cb57e4601fac25c43e2af6b3ba949a5be4ea403df4341f7acbc10e995c97b79804b99039f4292e6979286ee039e89304bd45864e45269f4f9f753768965c5b0e9225fff4d695ef9d7a0f5b19359f7d7aad9b3c02ead3a903e069b8facebb090833c4d1b1eee9dc7f644fa4c89086ab00481c1c37e79c9d37072108b12d7300369acfb9101bed6d86239e3eb765ce3ce7cb46b12f978de7ba0eac3cfb4e8fea989cffc3464e73cdd370924feb511d1327cfdeb1ef74aebb43a612a6962b9ca206dd9e831e5962e03b974281bf29d680bd5bb56467fa6ad9be5e765d272ee9dac3a444e1d9f7d9d99d8d986c07273b93d95526d339c6469e73eee3917bab0a33c26e32f4f01dc975cee9b1c71ebfe29a1db2de9173cdaf68db21cbb65f710963e1662b09b04cf2fb72d1d7616ce4f2f5181b81beeee33f6cf4f9ba8c8d3aafbd8b14fd236b20e8025d8943b2e7f3968b6c3c755086a7ee9241cb536f8901eedc61dc5b0c62fc4e1dc6bdc540e6a98b0eb6c28ea40e82b5d99125e761ab4572e00c39703f045fe4d256049da98789e79c7b9ec8843ae89e8dc7e4f33c1b0ff4cfc196cbf75baeaac1e72d6f3171deaaa17fe4fcd614357079cb257640dd5599382fa943d5be734eec00aaf6a133751d50f76ad979585bdc82ae70c5b096521f7a4796f43b7f91cd5b768d4c9a96976fd935d2f9a4297548baa87764fb8b5c1ae80a5fe4d25cfd658d746a26adca243a75d1575492a48651f476256691095d027a574bd1c1da03bae79cafc884ba587bc0cae4f3ae235dcef4799fc7e4f3be562d3b071d6472959d8731d4f1d441073904c932f49e96ef83de913dadaa01e7a083b59cdff93ed8a18b0cbd9c1f82de72b0ee0695a0b7b7ea06952d77b0962eef48d0959a23e777f35de47e6b7e5712fd9649be7022612cbf6e238434356b2e0e7788c14b3bf10443d59cd3bbae73cfeb3acfeb9cdd878d9ee8773ee795cd66c3429bbe22f58e7cfa9ea3cfb9127724577dba39e3b8e029ef06754e7d929e7764399dbd450da67bb59c5f0f93e99e4ff7ea74d74f70c10e39707b929465dcb1776207ae5f776d123b153b00ab6b83d8f3c869fb8e2cbf67c6f2ec5c1d2f18590a707219a783e0ce0dc8f0d329599f9be064f100a133393d24997803f6c9f386716f2ff4f02ecfe35d1f3a087638c50d3e6fb98b2c5bce04bacbe5ec20933d4ba8cdf6a2d3ef5fd199e417bd90ee06bd9c73266dfc45964c450dd86d6a3959d4807d74a51ec91e26ec36ce6e53c91e26ae67773d579e0d6a6feca3a5b533f9721b4fecc0f5a1f36c523b2776d0b341ed61e5b1711b72da7ec50da6ed871b57e2b18f6eeaf6acfdd53913bbcb4b57e203d4a270b99418a422074a3e450da6b333bb12b7b884ddc5e4daa0767083daf6bb413d8191dab0bce8ed600b14ebcb77835a2fb76991653fe836a04d0b74917cb27985aec4afd06d4031f41618d6dd21606db9cb79fa32d9dd7875645957eb731566c3de0ce0e432d2dfdb0b2c4c2e23f77b7ba16a37c80246b2bf5e6c48c24c7691a2bb77d6dd5a9af0cb24543bc72b88a228860eba288aa2e8fb23e60182e0b72414088220e8fb03a2a0d56ab53ef7bcd56ab55abe3fad2ccff3be25a13ccff3e000976476708acc5196713ebf0de27c4928ce3976cee9b7496c80dbda2425ed4b042ed3483d075fedddddddbddd9d83b30357a4b5ac951fe934eecd2acd73ae84761db941e73977e0f57ad9b8e8a183fe7abd5e2f108020e8390882206806dcdd395fdc202fdc20cf27e93476ce95df7bee79b849ec9e8b9bb4ef39935e17e0f6b20b8157b87772fa4ee3157e27fa3e0b0b01dc1d7089e71e75ef5b9d7faec4b4e59f77947e5d0bf4c82bf3297965fef7b9c7be41fb55a6cf3ba708f8bc6bd5b2f62da7dfe74a4dc992fa6c39f8cd39a97f73cee91f65ffe69cff718b3ffed827fb6b9deca14cf650a724074abf3b8486d3f3dc69ae798a0aad2c7962801db2817b64f9391375cf41ea5d9f9c36cb66fb289cffbc3b2afcaf032bdf722576914f4e0378de20ea51ddbbf8a9b7c827974775fff98a4c9c7ccb837ccb5da4d3007e6e10a5adba3b848a1a704e7d8a4aba5ad6be734a7e1f39bd9cbfce91253b7da2f5abb56f7e599a739023f7a7d80e4e7172e4244303fbab65edbf59b2b7e779ee797375925a6f72bfa7934e3d3d77f2f11cfcb8f428cb3cd2f3eab2ecab5ed764d97e65fc266ac3f2e5e74e5e1e58d6fc95ef3f9f64d962a2ce0eba573f32f4bcfc3ecfcbcf9d3e2d2960f9a72bdd4fbfe23d54ed3d9fa212cf95da233bffca287e9777aeea34f378da22a3f8a9d79e25f3f6ec2c2a61efc8597ade399357f8f38ecbce9dbca747bab73e4926f69dd5239bcbf99f7fb59cbeeef992fe12aaf61a2c7d5a99984cefcab4cedb9a1ed5087a80564616adb0bea44f9f8c564626e98759efe4fd151cf135c34d9acefde2952e44f357429be9ec2c76406db69f3ec525a3eda7ed270ac0d792ce3befa6ebf3f5cf57d4a0e5dfe822a7831d79657e39bfe593bc32bfd50279e732b599d537a8235b95e9737064a3ce435f9fa26b3eb8a292ed6e4696b95c48c9ac65ed27d3e7a0cb376986a46fd0fac8464223cbf6fb8ec9e760a52c7b39e7d45b3ec5b9656cafadea13fcc8f2f39683e08aae5ad6dee5932ce7f4962bf524cbe9721074b5beefa7b748afdb21ec1cdd21ec2d5f5103fab34536ef10f6d200b5287eee107603d4a2f8dd21ec2da7fef66d91936a6c1fbff73d32e8d7bf197e64c9f9748fec99d5697e577dc8d86c0f80f76687066caba7de646d64e2fc822fb93abdb06cbf1d9c5ed8689ffad2263d09fd0febb515814fdfad5dd5b22588d679550e6d3af5e9858d604f9d270998b16cfae98565d485be847db96d2860695a3a9faec4526339bd3cf572db78ea52eb63cf4e0046891c941083c666a3754e6993b4d10eb4c0430a3b3c41d3b2258836bd2a87b6ae650bd33caf12006d7a4756098026258407d3a59af48cfac47aea9d4b09e181e75d9552ea1975af4a09e141e7b34a79cfa87755687b567e5feeed36bd5cac4501172c7e90e30c203e00863ff524398c4babcdb0c51338e02085190f9880b6b4f552c803b7d968d30b1b5df0d4f78756a669f52b648a6bccf40a9cea59cfd6570714aabdd6459dd2a96f9f21785ab9b2a3ee552a5d2d3b6fea22b29eab9d97d4a777de24f515996c172e7c577ff840cecba92f39b2e4bca906e63d2adb85d5735e5ee03baf5259369eeba80440cbedb98a15068a22654c514b13ac6eeff37bb3eae1410e39f207a9f5a9d79a356bc0d05a4ebd66cdff6003c2c20664b87ab2e08a8832b6282bcbc6ff602b52c614b62b01d07233c1b65d58d1d6cb0b3c97c2b2416b55dbb2415b2fa59eabe5f4de22e5de5a38f351f37fb0f950a3882c6fd86cb4ad52eb535470d0c10c2c79d86cb4afdab4d86cb4f5921ad15a5a59a7643b8b1f1429438a351fdc7c400d1ff6c8b2660d0df63cc5358e3b215a00f3e36c41869f9361d8da519e544d37697f9d525189182c6cd1751cc7712255d2cc5280ebdf6dd29c62c7227b3e2cf37ebd4d9abf2dce1a58f0f21ef3c2c6b7fbfc7c835d4829d7715d8b012f70c208ae061e569bb3085c2f61640fc1a694e47c4e8fe33a6ff7388e033bd287659cb3386be09057e09c2a719a6b9e7a577d58c6d5a5949c4f99c300a77f9b346bd5190f6359fbb749ed4abe4c926ed0725d967d3de711ebdf2671be0e723dfbf3eebc3b6f27fabaf6eebcbb9df36159e7208794dce794300dd49598a7d8790ff5f9fc3d7486ce39affab0ace3c8f91fc701b2d34d6272c2403769d618de7d36c869aee10df69de61a6e83b6c6737601d739a79bd4f93ab74993146219e7c5ea87b08cf3157b452b43627de4da24dfa0cf3997b1d1c8b2afe59cbbc8e5d9e7de47df8faf8f603cfb9cf38de37f6cd27cce3b1f967dde2c74a47fdebe82998fb1ec9be2e73d9ef7f3f778d569ae79cff7e6a5eaf7e6658587b1ecf3ce897eabfab0ecf3486e83d657a41bb4aec41d397f6ad6386e21c0f9203845252e0f9dba478abe9b043ae720486a50fb1535f0983c5f0fc9d2f3e9e06ca7bf372f5a9e6e90a801c92e929eb7c8f9ecdfe722f9798b9ceb4afc91e5fcade5e7eb20871fc9a0cfcf9518ecf02339ace5fcd0c1d057dc20741748c38f645f7183d04104881ed6f2fbb0e5537432414b0a586cef545a20057aac79a8c50a23830abf0e55fbad1b8221b92f92a5090f2240aca5090f7ab8b59cbf1e3a276ae07a77b952af92b032b93470f95627a92ffb5d0f56a6cfd94596df8a4cd83fa7b9e65baed44adcda27c91a382915e04619a997b1e7bed1d9887322ce63679e731f36e2e7849e8b3de7231bb1730e63a3768ee37c7fb8cab4372de003c61c70bb9b906514d6848d4a1fe3f3e58a419d73efe3fcbdce91d8a88c29b151f971c046e5f8848d3c4fc246b1f71c84156478cf25c046f4bd5e33eff520bde7cb463d5fee16ef71aec43e20939e18138cf79c3f86f59e83cb73dbc669aef91dd9a8dd73181b51f7fc63a3e99e7bbe3f5e65daac0e036ed3b8372b355f7e7be301ccb7efb33777393e7526191ba9702b63319f76dae106bb36e0e432d27dc0e8036cdf9f72b1c874d338f62cc444cfced3440b150a6a506159515da1061bb4ed19d57fe18118b64873871d52d0bebee16af94da4679f4e5d09a94b1ccf9ea4007c3469a5cfceb3fbb011e75cc315670d7f6f5dce3cad3096cdc5ea17c02dd2612cbf9fce1edba4308f67874d2e2365d9baefcd17fbf671ccb77f1178210d4f1f4626e2f7964315cf39fcde6ef0e165bf373064bc83935c129525ebcb9a13dd9725bc60c5afbfa60f634ff17bab41869f647e6f19c0fa21bf371ad44c7103171768dee9c9af6fe0cac2e19da66cbe8c02811ab11cbe8c0a930308822fa3560001f725941948008009ac2fa1c660fd2ef96c367109b5ed9a5f27e7832fffe5198278ae8c9f0ebe9ce6530729c939b8e206b496738a4cdab9963d8c79eaec2c6a407dba5233b9cf9193ec61c295f1d4b932be84aa3d75ce9974da356bd670edd9b931e5b8b71dc03cf9e4831c3219e439922be3a7b8813f3b25afe8cc1ac54cfcd9959809c9c44e1d64b29ccf398b1bcc35ef34d7705d7b964c6fdf607ad728706f3c1cf151fc5c2dd9afcc6faf7d2bb1125392fc26c9494681bf5ee326ab017748f3ae9f0eb26b93925c48761629bf3deae66c939476c06383f6cb45a2b4839a2db2cfb59c3ec50ec09fb59cfa2bfc233ccd9fde82b946300c3ff10ab5d9db0e4ebcd3ebb549abc7b77713c9ee97fc65f7ed200d7793a6cdc9381d9c220546066717cc64a6a8c489da6c8b868ee1ce746cec4432d39cf9759eae8984da7e1df4c21635d833def77d5fc7dfd7f3fb3e4e6633bd5b63f303f87beb02e6613ee0f6d19679a18938de00428a321aa0ad7f7d54ab41032eac81822c2cde9081b6be5528a4dc01dda0aba57fe7df76379465b58cea9feef2918d40f740afd5aa65cb5ddef23c6f0fd9a8e5eda047ce075b64e853dce0c7539685b50cdde51eb91f9251fd2d0727ef6c1b8fdc07c97d578bfc61a3dda0d2e7a7b7c7c69fb5ec9cf376b0e7ecc8fd014cffc828ee61cf5d2d4df8ce972cbf16b7bb01a9b8df4dc8326759bbe73e2c6b5f5189a7c44acd4ec972bed35cb366cd9a2f6bbfde9151fdeb93fc58c63e45255bb93ab28c7df4f961a3fd181bf9b7cb36a9bfdddf35391ec0f5fd29a7edd941b23632b5ef97d3c6bfb716ccfc7c32eead85323f6d4b2696df5a4edb1f2dc8e18a4aa82f7985fe3a25978cbfde6459fbf6dda4927acf12d7d3eaf4faf52de385bedc355fb6f3fe92cbb225a711e3f4f9cb45eb537472a7f1bbfa4c9b1760834d82308a0f18a9735d64789e5f15e66869ec1c0963d92c633f7d45109ece2870195c06c10d6bcdb322b853c6bd557d79eef7c665ccef0d98495d8969fdaa05402663cb5ee064c0b8a2016a4f69adcd59802540260470d25e96979dc375160f507ba6ad4e53bf5eb24f28b880f9bd6dc9836ec17a0a82533c40ed976525fdda77156c5109c7aec4e00c3bce49bac1fcce7b3498df7957bf9fce91f3f787f6e41e4026d399a697ed3cc92438ccb91a2cd2183e7df0c5f563597b3be5ba0dd8d9a84703f6f6ee7c72ced44ebdebfc6323eec2576225aecd39cb80cb80117c7109821ea0f6b151397fd68f65d4a983af25633bbbd3d453a7aec4693ead150097fbd54c26d961a4dd0ece90f612f88524b83fe5debe1dac73faf2fa96302dc4466592246c44bd5bdf5bbe7cf7f8dd927d3bc821a51f1b31b17f2c9b3e7df968ec593bade5f74b6f9665d429cba697e166fd3aa592e16869eb2bb22bf1143560a7f50bd988f7c66480cbe014620e31be4618a79771ce15c621e04619bbbddbbb5b4b1edfdddd4e7ffac98c969b162def6cc44e47362afd270b563f7b197ee94c2bc32f218bd5ef2d05361e00bfb714c63cf8da1fca46b52fd9a733619ab58709fde9f4a79b60b3d96c4ba8732dd9a9afb8a434e1a9d3eac4347dbff4f6a72bd3f82ba4c6736a01f7a714fa6559f9f58c7d1d5ca4b1f3f955c6bda510e6f79682ed877e6f58b67816953c602cc7710cd9889d7dd9e8f3ced93f16957c5de7e00ce78353ec1cecc8a8e9a484b28cfa92f13b9f2207fb9dafa8645956863fb95ad69efb3e57ea8f2ce997d4a3fa3d5f5109e74a0c822b762da5befd234bffcf3bb25c9699f09f97cbb2d2ca7f5e76be2c2ba5fce74adda4579da4bebd23f73fb2f479eae00c3d72bf23f757ec807e73a4537f3b259765d35754d295b2cc9b5d062cbf76e69aec9ec9f9932c4ff8e9e04e669b1203dfe4f41597703f5d899ba4cfe47e2d2d17ea231dd1d9657de4c375433ed920cf4752022cf3fc4582c032cf6d480f58e6b948c658e679482ab1cc73902c00cb3cf7417e3cf35c875c9e6d1f71c0334f037e9747f46513d2770e736b3cfca53613e603ce902c630fd627da14ba389906907ab5449ae3e8257579eb861cca1a416ff9487edef4a6e736def45cf43cf47c2ab50d2992e1fcbed2cb28faed2d7264d9573dee364e9e83e3386e0d8ee3d26db9bbdb1dc7110d390012d2779502317eb1989ff808a2382496ed929111fd8ac182038b2601f62c89a25f7e5e56ee419fd5e7f459cb9e2551f45d3e6bc979c95efb56ed096b493ff4567da2df84f42daf3df8899f8845bac8254312ecc2d0bb708b5f17d922cbaf847d9f7b5fe5603e48690158c6759d73ddce675eee2953ee9e7b5bff8a4bf6f65c27c95f13278705e4e077a6314ea74d48bfbbe34e2ca50d5a18c771dc2881e79c92538d713a6dfa798e0b3f3add18a7734362dd1b43ba18a77f4dbba98c6b4e2c19112c1958932107d304e3f46e427afaa1e17c143bc026496dd02c89f636848f24c0b389a4268eac3aa43c01ee0f8c8f864ca3a1acb1fcbe29c7580ab9f65b3ece461cc7fe79bbaacbe9afd872d5a6f0bf4fe6f39bfc03b848e38dd36f957be6d73de016076cd4f9ee1637056023aed5721bbee5e51192f9658bb4bc2cfafdf952f6365e0e11226ab5bc56abe543672cf31890c9f9ae16bb7ea0ad56cb6bb9b0f4b839e17279cbe718d0f5b1ec6b81b99a414c31df8d57c686b246cf27186314f7d3f90b18ae2dbf2129cbbaceb956eb76d36ab56ebcd56ab55aa3b75a2d9f64d888bde5f3c646d35b37b575f38465ec23fbab8c8d9018fb10249b4ed06ab55a2da46fb9abd59aadd98a6133185d5eca5e6993ac6cd074d8bfbc0c7fdbe785fec6d79d8f62a2dbf8aec7868037e47cfe905c3536c77843ee831dba9a90cc3421c5f8f99279cf4472be8decdbe538de2ed48e437e3d9bfbf591d22441b77b0082ccf3febc91d888bdbd006ce4f27625d0bb09e95dde22932939d05042896fda34ef727086a06b7e6cfc742f8d045886c603e3f46fdff990127664c2cf6f8c8f947ad6f90ea519436fc2320f58d6b9120fbd31967be63b1f3a632ce7972f6ce4cd316ce4657de7b38723d8288eeff68398627ea0aec44cee37e9a358cf3affbeb11c6becbb490e658d9df7d1acea59d7f98ca3ebbc091b85deb90720b05198ee0b18367279e7f38a8d7ae0e9858daad8687a58abbe9b358ad25f91ebcf06b173648c652c6b91435923cf3ac6e965ecbb8ed6b9f7103e5a5ae7714dbdf3fde92ad3bc71536c77bb7b669cbe7c46b7ceadb7971cb7cead3371dbddee2e377b085acbe6dcf3b29bf376ce999a8ceae79c7275418e2c857eddf30de27ac8afefa4be2ce3b8eaf36912e7259d4f7d7224d1066d4929a54d765cc2dfa6900b703f96351e61a5edd44bce979bed9ccf76266ecef2eb4a9dd63dc0fde948ea5afe82df1b11677e8b97fdde88e8e1c588f99ddf9b18191eec90eb815e3518fef29ef75d95e12b5bb1cce3aea41fc525d7287ef6ce7b17ccbb5adae078f9b9d875be7c94e39dc36a1945bf3ecaf15e1f3d875c9ebd1c875c9e8d65543fe8ddabe59c4726d961b4a951f4bd502493ec307e35aadf0b7d39177d8a1b304da7ef55a6b07ab48ca23c497018a3faa78b5e46f514c37dab32b17bee7160e59c2bd374ee5bec80fb59f93de76ac956db2ceb1f96b52c8cbde779e794df7329dff4748fd23ff6a5142ab1a795693aadedb4321132bcffde86a0e1b99bde4d51ece066f5f4c1451af9f6f4411a2e1f75b58ca2ef390dbd9c47fcac252de77be0865c477a5e3ccfd98a8a4abc9b525351c9ecbaceea978daad8cb44b2556cd4c244b24f9dadd8c873ea2c2aa1b7f7c825e96f0720c755e07b955b984592b0acbd89cb5dde5517d9482cdb632cbf6f9f73fa574bd94bbde794ac4bd8eacb287e6e6116a14edd23cbda7b9c533f272ae196d66c7844022c6372534e5492c29844c90da41ca55de7799c472793f1f9e93032c639c718b796b4e368f3f46c4a137e4dad9b9c14e066307e0ebef607e42c63393ec79efac7465de5402f3fa432eab9b2e9b9d20ad628c42c63a4767e1711cbd84de803b06c3e2587fc24cbaf1cc72ef6732c7de9b75afef1d1d872ca46adeef315bb8f73ae4d3126188c01c418cf714e9fab3d9cd7de59367deca3a1acb15c35b2e9268cdf9629b7ab65ecb9557300967196716e570e58c6338f2d33afba3792100d21834548897f7e77a362ef2ca3b2a7ce710e8235a4df2813362c61bf2ce3caeced9158466bd9b458de6c1e401094b18c0e795ace6fd0e59e1253d146b411c96559b8ddf75d2d9b7ede73d095da25d2ddcf3b07c15d727f3eeffc739afafd9ce6af8cab2147cee56c7683a8fbb0cc4796d15a36217df357b3995c1968ee98b669c3c26203cd933cd0604989c0881ad65cf1d090012e1a57cbd73fa794524a5d89859e52417ad02959223552f9f313fcbe394330865eca64e1071b7d623f4fbdf4e7ca214fddc697e8a98b3ca3cebd26e9721bb2943de722590a3d579fe88364c9c173dea212171882b5e9e73b679189d4183a42b0c6739dbb48fa2d729ff448ca322434bf426a36937a015ee19ea973cfbb978b0e7b97afa8242692e57cd1c6575462e34a1c9225d287bea2122506c3a80f43b214fa8f65ae6a4396dfbb6a19c5bd285ee19e8973ea477bb3d96c5a68dff40fbdf4cf731f36b2f97ac622e7375ec6de73d70cc3e532366abd9c5d888d5ace4ec4466513d28fde029d7d24479685de223f9685fe224396857e43c678163a475296856e43c294887559e6a08b2c3f6f7d3fbbca91b4be983ac1399d58ef7d53cdc80e96abe6db29e5c872c97c921dc625f3e5f81f183f07818d5cce75bcab2ecba82bb5e72df289bec77de4c8b2c9d5a62533e502172ae6788efe24f7b78f40a7ce1eb693cb5456cd7395a96bc8b239b15acc654c2fa8734db19fdedfce9151fc4b30180308329a6273fa1910f47670722d2ae9269b9cce93b7805bcbf137ece95c476e609cde39535729cbf018d9c3dace9551f4a7efb402dc2567d568f37b1ba24acd97e1b31071fcf4908d989c318c7b13e28cf87b13a28b9ffef52ce12f4ff80b7c8fcce0ac1a5fbf587a94f909669cceb21d13fa88ae2f159d96cb715db7be9e67e9267d2445eb9649a31ce7619d61cc749d83758a41b365d23c77d53926cd96499b69e62d6b92d30c39b1c85986e346d05d31aee51fe7794739a7be2e6b6242cfa6cd5541109a555f7d82f4eb3bada4f4ac777eb1d2b332b34bd9aa91f5919410a5ce21852e2ab91ce4a08f3e6f75eeb918d66fec2329d025d58a791f49795cbb6a933f26c0fd29f9f6ddec94d6726b29fbe6762cc6725ebd10b62f578d1a3366d0a04993a69c655acac65fde52a18bde522e07bda53e97e2bcf3f61d5f75653db3f152c846acdff6ac440ac13a6ecfcabdb56a8c3d4b77d5cb79c5618d992a018e5aaa64342d5b9084689e6f91d13adf12a36979b9962d1f8dba962da3962cdecde634976bd922a3b55c4b15114dcb16245ae860dd2244036d7ccb109aa8a5ca87a6658bd36efc55b7c468a36ff9a1bdb46c91d182ac91e95a78b668fa16a755b07e43c2b668fa48e6ac91e92f32b643d36dc86f8ba68b64b846a687a4b746a68364b746a6bb48ba46a6b7486e8d4cffc8dda2e91e69b346a677a4b846a673e46b8d4ca7e4cd1a99de24ce1a99cee4b846a64f8d937b003bea5b288d139adc94f3be968bab1ad2dd2dfbd9e11aeb990f900a82d0a693d180fb5372568c2517e6cb485511d1a84bb567a15539b4be55118da1a267d9309eb8434c15118d73a92c5e16eaec5c983ecae265a195cb00685da5a4a4b80cc0e332009a58a56a414cc1a555a54ea062a2aa882c8e584393e232001a57a5a6dc0073c697306662a065f13c8b97c5d3b225a47540fcab82d15a1dd755e02e7a8586fbe11406e35c072b88e360512b5abd459712c203d22b933d0e52bfc59cc73d766b5188fff8f0711f395c05a35131cf9db5e336ecfc53a5441752bd56a991ac524a20e83d558ade78aa148d55a9f6dab71f556a6130d7a95214abb182546a7513735795a215544a81d42d43689fb77c6c7d3e554a080f78bcc7a5c41fce2ebfb972b9d4a7e3ad9e2a253a4f7dd233761f552aa74a0955a92d43689e87558a8ae92c313b95b31c4ba88b893f6a9733ba14bf5c8adda6ad9eadb06c3faa54e73e2a4e95b2d233f69b2a35afaad455cff88ad12c9aac2b0ed6e55429213cb8711c976ab9541511cdc5dd70aa54cb6fea1cabd47c55a96955a5daaa671c56c6b24d311bc613775851b055a5b46c0969ae4aab8868b44ab5ea44d355a98955a5d856f9363b3caab0a30b24b4c032b1e697106cc08d327ec8e24df115471401c31779688046ebcccabab2c2a2376abbf51515d35962b25a4c57b92f68fa0acd155739abb6a25e3296b766cd1e6694918506ac68e5aaf8364940c61921c8a1cc19b4a53518192063092e5cc8415b5a57ce36b39e7d5e314e2f391b87f52b64aad626fb8061a279f6d9be6a16eb77d69274da39e74fd9ede795d3a5b594752d658fc68b9896abf1cfcbc5fabce5a5ecd755f22d0d2c0b87d5e4947d330656f9cbcdc19838bed0f055cf98394ccf580cd6f3989eb1992f67d6623dadb2b2f9125eb9aeb26e46353726a0b9b9b1a5b9a9baa9cabab1bab152337ee9190751f525ad2aa38717349d1bac9b9b1c3339398ee33e7c549d2a631d9dba1cc0603855090727a722e5789671e6dc54a1e9f36a5e95e199e69b685c4e7533cd78be34aa9b89e59148f61867f57c9619613858b38c192caa2ca3df54cff11caa1bcf32c22a0a39f5e6a6ce3450b5e75b99e9e5bc2af3fcaae528d6323682b574572d63ad5acafeaba5b37b24df560d1a35612ca1830d6dd0c183d4c24195c5cc18693c4193e23beaf0c1c6c5c6019a547731460437d8ee688226c565b6c461870a72aca1717093a64a0f27b2acbe501bbda261a89883260322cd97b40acdb3f34d56a68f84b0fa08c94c1f29319a3ee2a067ecb76797ddc4f46cc774b5fc6e5c2dc7323ddba7b58c290581d5b37d31368c03c5edd7a77268377e33060736460cccc5f48c711c965351b8a9f40b0ebd9a586a5e63d5100e9a1dff9126c76f6e2a0a3caee3b057a5caf272aa988fdff88df3d8e3e3f1f0f47cbe7d9453739ca7e5db473b75fad26215859b9c8ac2e82e9f5633745a158a4e6da28db79a9ea9613559dc37eff31ef3b5bcc5b45cde615c2e24d433f6efe334dd68a873dedce8595ea02f8daaeb1c24e99789f5c5cb8935e64bb6650975c0eacbefead9594d1f71989eb17f36b5fc46b196632cac658cab1a066bb96a7e7ac961a62dccaf99b73d87995833ccc49a61a69a325f26d697ab2f5fb2ca3c673d632d561f8d2f33398e134383e33f7ea4b9711f3e1c85d17574bca2f072589582393bfdd24752b1daa467d373aefa48ea87999f8ee3523ed0fcf41b97d249f3d347978265fdf497b30bf5514e95f56c3a8ed04f77d5f2bb41fae9ad5a8ee3eda77f42afba3d2bb7cc4ff784ba5a7e5c2d475acbd897df772dfdcbef6d6a22dfb401ee4f49b39e1ddc3e5293f5eb6926ad5c33bffe9193b6687e17ebcb3643c3f4ccf6558d56b12fbfebd4d6472da667ec7e15fb325a557ddfd8666849b19edb8c18ac3e5a9a193434cd330d2386d16c5657f5115516cea93cf7c2b4c0305760ebea8b5349550dd15a0e7eb1ea23aaceb3705eedaa8a42e75d6dabaee2315ff5ec6d66d17c69c6ac58abe9a32d93e63d636f33568c26cd17ab31cfce567dd4cc5f9ec73613a3b667b682622c5bcc5c9ac7be1cbfa46196366f334d9a8ecb4261dec44c3565c6d8b06e3d63aeea2333657ac652284c343d636fac9e7149c33c7bd9629e69983ee220ac50a30d1b5ac882c64ec5f451cf80471e3f00b1c31434763aa68fe69615cc58a3660d1934767aeb232357d40082355bc8d0d869993e9a4adc1143d50960b0d1f8cbb3975ffa486a5ef551cb993d67ced9adf491d4540e8ddd843e72550ef4f8c2d647addaa467cc3eabfaa882e0ecd3aa8fa4aa86685f45c1ab4f50e0aa949e316f9967256d6683d8dbccb377f76c2f65cfcdfe35ad1b3b5a189bb41caf0a69b19dcdddd9a45ead79a4a3ada446eaa2deb6ef1bdb38dbba530f590c78f3af4ab5edc6b65795fadc43f3cd7997f5edd4a5c22ac5592cc68ad0d8b2b9fafe1c1c5d58df225b7d87236349b195d4c492da69d5448aafc01b46d371556a66f914f3da5bd8b6d68d6db65b28d4ca129325a6e51ea301475f1ae8255f89ae79c57939da78b9e3abba4dfdb0c4b0c6c03ab255ab8e5f8d614de5d0bc4aebaaf76cd6a5d1ba3deb576db28b0b707fca99f5b9e7eb631f75be1eeb23ced7bd8fa8c8b5ac71a45392ba474ae9f8474afdf0a91c9a962d228dba94101e84eeb954077ae75256943a67e3f4f689feb9d4f6ec8a6bb9cba584f060f4d6a2e17ccb7c3987f5edca711d9712c2031ccf41e3ba71974b6d964e95d2b245f4e13fb4540d80a6e35b264d4a080f60aee3523faa948fba65d274da6175da7ad69e53a5269a9eb5e3d42d93e6aa524237d54acfda5b55ea49cfdac7ba65d2b82a35b17ad6feaa2e6553b74cda57a5c43aafa4c2ba65d2ba2ad5a467ed609d563de3b0a8d5772377d5f272de2856e78b46c8db2c9797138d8bf3a5755eeead55a77268d497e6f9e74bfbbc9c62be3db223c79e4de7c858cfa653d27b3627ac36d9c302f7a7f4b2befdeba372a45ec64a2f6548f6189f38ef7cdd65e3dd6ab92a0a22e8ec7281158556e8d4b78fc28a82ebe5d3b78fa842247b8c4ef3bd321bd4be34aad0a5aa8668af8a0268535108bd4e4da7a68fc44a258ab553d3b3f6b05285a1e7b38c574b072b1508cedbac65cc55a95c9ee5732a57dd313d6ba7b51cbfecd4b42a55cbb37c15852c730d4d08ea4ba36ad529a6679e0dc91ee353e7ecf336729832ddde80b8bdb7771dd60c13a68fbe2af589e92371f5c01ad347e177eb23706c79fb2cd347ae564521f655140690456be748247b8cd4c1ade574a28e648f5183dda032a8642fbd32f389713e3196de55a766cdcc30fca557c62bd347334ccfdabf6f1c63b12fbd32df3d2bfdfd6dde9557c6bb5a36beecb03a351dd6912f3b5b17a6b3716238339c18d897140d5745d1b8bea456744c49adb82ffbd669fa4bdf38abbf94c9e22b5b99307ca5264c39d5084d5bf9956b46a8fcbee76d8c629ccf5ece305ed6af90a9afc6b18d1770a7082d4ba3a1413649d5105a96ce83679354511a27a90a7d3649150cc826a9f2a9609354fd7839476859d8c6be65c2364995a7b349aa5e3e3649d58f1f9ba42a486c93545d40eb2ad579d5106d9368a90aa2656935365afb964edc24556cb349aa5caf4d5285336e922a9e9b4d5255713649150634ae529c570d759b444b168f56254463dfd2de26a9e2689da44a6c6d922a1dd726a90242eb245516d0ba4ab5570dcd4d52f5f126a91a7b9354c5689ca4ca6949aa64344ec2558afa540ecdf3b8ebbc1ae3b8ae8e5da5aa8668ed5cfd7a36bd6a88269543a3552a4bafa175dd9e4d9fcaa1550dd1b87a4f7593de8010337b4e71c97083d81b882b206a78a7716f3c78f992fe0231435878b1e546939a9a50a85165a309c13ee52ae30b4d881e266cfc3c5233020d31676c208f2b20e8e05f324e58638f2d779c200d37dea0adef08e0228ddfd3efcaf03bdf8eb2118c8d5adeb96fd27ed775738c5da78406ab67fbadd6f74afc9194655446fba87b638c1ba8c8b0210533df3945d3c6775d11697ce1820d304570f101da18df755fd6b84fddeb588c57009a460d6994f9e2bbbb9ef93c9ac585716f698c4943ccef2d8dabdf346a48230b02a8fc7cf9f3dbcda67ce60b1a3b9ca9a2cb4f1a4fb41207b5fcce205195c61922b6a471260c9734ba336bfcebf77606ab1b438d3cd6b01a1ad56043e7f7a68617b0df9b1a4d10c143f87b53230321f87b53c38b97461e6990e0671a68a4a185cbc64ff8652a903f80244488c60f1a488ef37b43c36ac67e6f67a8217f6f6794d1678cf1f9bd9d01e6a783dc195ec63c3a33e2f8f17b33430ccf0c32427e6f66e4007efe7b33b3e6a783ad30c8efcd0c9a9f0ebac225a346a8716f66b06018777e6f668ef8b9aed607f62c01bfbf6bf9f4dc3380cb9d379be33252e8bac658e6830757020a47930182aaaef3ae23e38c2aa5f91c4706558c655ce531bc9fdf1b1949901104d65704b83fb309d712f6d399722411cba6195b192afc8ab43aa545b422552132c615429ad387e0182bf8bd91c102195d3e5e420c31be40fa82fba8d79b821460f130061466b8bc415b32b07cd769f15de7423e2cc300982e6000a20b174400025ac7550fe373fddec6b0e3c65978cc678c9b185b8c51471630c6c8d202c618726401638c32defbbd8d8145b1e0829b948302848105061a6bfaa70b1378f9e9e20d15681794d230d03ca53f5d503166f8d6ef4d0c2f5f80b36a0ce2d7c570e2b746f1ef0659d060a6f76e50194f8eace7ba9bf26f6cbcf23d5d30c87cf93de7eb312124a53e721ae71c1c6093fa396f02c611cf79f89c6f5fd12817c7739e67f1eb5c992df3e590e9b376bd01cecf2c709e73b0ebbadda0d9d5ae6ed697a6afbe9d070beff7068618df35c90ee3c47a21224650e02478238e12cc0dda00a91d6cc706b52741750d1bd4ce28d8a0e6b84abf6c12fdb25cfc6e906fed2f1b74c588f3fd6522d96f6f1b3669bf198a3176a484856667d348092bcdcaf64b9bef99c4ef9738af8475c6ca2612252c35b6c8fe55d8461e5871fc2cf3ede0ab7dfe404edb7f8b1dcc322f32995774759a6b9c7e7e6af9e9c3cf2fbcfc2cf3e0b215e3debea8e3cbef8b20bef49ebf88c38b3cca4c28c61f36096a6af9f9c306ad2741f9407d785a6693ba96f3a1a8f715df3eb1d8a4727ed7596683d6b1d8a0f599c5264ddfa4ae4edf4fa75dc06d1adbcb6f6f5ec4f194522a055cfe61ea3d5c35e0ffdc73a2f5cec3ce471f19cfd6cb2423cfb6837de7a59743be731739f28cbd457e1ef5e2ccef18bfa2929fae798a005cfea5e0321771fc4af17be3a2030fbec0ed231e20bece81953ea2be3ebe0c1eb97c5d4a1f5de01bc4b7a791fae8e5eb4ffaa8fa8abea4afaf0bf511ccd741e8a31c5f20df1dfff17513fae887af10af31f2a702c089f810f7410aa9543f42dc02c721812ad50cc8656e43d64a4524c87d78489295ca82eaa4b7c80b2a55900f1fe21f19a452c92e700cdc237b2a5525fdc73b1248a5eae9bc499f4a758167a1aaf1905440bcf320d37582b88f77b5cb105a053e9254416a1700d0a86c3c888ff7384852f9d42e3a34aacf7d6a171b9a3b904ac5e3a3ef9054608feb388ca4ead17120fe22a9746a97a651c57c04e22f77bd1c7420b54b108d0ac781384752bd6a9700d0a8420f02d62e3e342ad07dbca77681d1a838eff11c920a00446a971d1a55cb7d3ca876f96854ed3d4e492a1fb50b0e8dca878f436a9749a3d271200e80daa5a351bd3c88ff20a930a85d7a68549df7cc6a971f1ad58e8f0ee4a2ac7679d1a8440fe217d42e48342acf7d1ca87671d1a8721c88df9054423c0b558d6641ed72018d8a7a8fd7da6546a3fae163a5aade65d25cce245516aa1aeda77611008d6af42041dc55a9749c0ae654394e75e3382ea47639a251b9dca756aacea938a7a24ec5deeee364edc234aa1b07e24e52b1f754aa20950a742a9753b59ccaf3cfbb4c5a8f574052c11c48a5ea32693e956a74aa9753d93855e8a2f7542a1ea7da71aa9853f9f01faed43ce40e29ebd97a8c6cd2b3f51fa4093d5bf7b1ae437e3d5b87913924083d5bc759bf591fc9b167eb2f12a967eb36e4939ead8be4acead97ab80e92b19eadbb48a59eadb748293d5bffc869b5de91deb3758ea4a4959ead3739bff46c9df7b605d6977bd3c2eaa3fa99a6c73ef641f35363892596405ae2c727e66b460b4378c1021b19f0e10320647121051fce88414b0e5984414a6185142d2b9470856a7041f42c99627a96302331726524c6f5dad77ed6da83dd7d4529fd32e7729c3357b90a8bae0ba0c401cb26119f0f2d4a29a59436a556acf0945af1e529e5aab0c3c60aaaae4837f175e745470658ae972573de39d8b56847b28b6b247e7086aba48990ccc535d91deb19e720d7a21cc91c757194ecafc9f79cdb33a62bfd3f9ce079eb57f480da6c1e4c9b8de6397bb0379a5739e8d9ba12f37c8a85c55548635d4322072dc69ce9020572fca08019a608e209335cd0a8a38a35ba0a10e80ce0f37a84fddeb068e2c1df1b16626e9c06b83f5cc458ae99ac3e12e23f5eb2159005cece6358d633eef101227376fed24717383b5ff511e9ec1ca68faab3b3983e729f693a2fa715e7e5fc323adf38e338e738c39c7f784c07843e7a39fb933eb27176297d24eef0f870dedb22811790415c4656e0e00c81909326238138908a0250ad1790163800dc410e8190aec4401cbc8084c11c5651f0d1d1c9e989c570c8495b51c90f3f1c875c5a0e2973187981afa8c4e005a4587b3eb8c045172b0a377e41edf9c00700dc878cb4a93d1fc8fc02bfa0a230baacf67cc0e33297551466cee34b5bda8b9c3470863fec7893972bf14c880b71009052427c3049804416361a063e73a929995fe018ccea94ac2e17951cc44f4761ea826aeb332390c146fbf19f3040e4546301c93f644fe83d1fb8fb4fed01bde703d7f19fdae3f29e0fdc87ff5421644f589b90def3815717527bc0dae487f77ce03117527b5cb5c98ef77ce03c2ea456557262f56c3a49ce323d9beee434d3b3e941c879ebd9f40ac839a667d38190534ccfa6fb90334ccfa6f790f3aa67134dcfe6cc1a3bd9f3903b648c34a167d37f90567a36dd07a9434e5bcfa6c34829e4131c128491e4c04654ea23a146ea2035905d5080df3af80af313674510a43c1fbb1b11051c177d695de879e84bbbc1f1a6989b39e6a777f3a6f3c395fce0e3a6c371d1f350f4500c6d26d6741cb6fd74b135cbfcf4cf4c169a344fed62e8398e7bbe34d141e8f900c7733ca7a2d0398e2f2df40df629cc736e1cd6790e082c9b0e237ba8c39ed8a1e63b3d3981f0f2cf4170b98d2bf9a1e54e1f48850efacb3f90127d74106edcf32982d0398e37d1e2390ee89ee3d41ece7fd4eeeaa7df8479ae36f1a1536d30f2cb4fcf217b686d52f5d3ad9abf907cd5b3e920934f4e50f39d9c16b034cf17b0341c7702a1e70397bffc5551a0790e42cf072fefbcab2888fef2a5e1f8067be39d8337be2208a2773c1f87fc6ec81e76cf6b34b017b0342d2e7f798753fc207497b30882e82f6f10407755aeead9f457ed69b77191e4a067d343727bc6563d5d9bb4be2af3c81eae4d847a36bd469bc2a951fe8800979f7082892698b0a28ef79034516669535a953868b2007b850f46b881841c586031070da38c1968e680420d140441a357d0f0945e91c3d39da702784a29a594d2fe01230524a0018b14742083b6a18716476859638d0f1fa0512bec781a3ebd22cbd39ca701784a6937b13e7e6f64c27c1ae0646e8ba38fc644d09f1f4afab0cc8616f88789afc9b4f0ed85d60ee31f262a074190b17d197bca3115753cf77ba3628e2f85e80d091224489ee8f0984b3dea3c4473dd1c19ad987364401907dd751558df557891e4bb5da8964d485c131fe7d15993f840470b7150c1250d99309ea842081f4c2046961c768ca1c20c15653af65200a7cd89214270c3aad2920609a509008d0c7821850b5a6051066d7d97aa38820c1444083105144c3ca1c4ec4010484c0e007104910546e6b922c3ac61ac80fff7e00b4d9329e4c7023461c7a29456252a2a8cf872b19a78158b524a05114f29a53e692d325231c4b3bf122bf10f0fc53176bf372ac2a0a150b4a670828a2f2cc5996729c47866de329642426ce4394f073972fcc8ef85d1e5fbe5f72e6f39272ee1dc554ba666a23e99da3b063951c9d7a2e2016a2bb6bcc5259fb76af931b5833cc5f68f64a23edf2349da91e1c87db7313f3f3250fa4305a90909062a48cb463d419e24388c5d7b187afe393b0ff596835f0ca3e7fbcc7dbf7df4d1be0eaecc9f955ee1beab4cd4b93a99579042ccbfbe8fdc77cfeb9cd6929c7fcbfa0e3f92c178e47eccd56950526f39e730cf5db56749e72defbeae3279cee212cfbf24388c5e65f2c2259e77de2202baea7dd5ab4fac7d003b8fadbb14574f9d93c22a0a331eec18e7b0986b50561f96852cebff58d60bde2c204b1c3556993a67ff1c966487b1f392738e097b5799b8eab5bd2f63dc71de4dbbfbbe6d030a2c3280c5195daea0c20122f8d2431a458461871734ef04de910bad8ea3cd350a32fffddea2f8825e457144143dfcf490c18dd19b0568d1f2dfcd02b238c1b27d9043cade43e9b76ca30f51d64fd84f2c286ef8d29f50f3e590b76022fd34f36592a7cd60f3b36fc0cf20cf651baecc65ccf9bd39b1e69578f21a45fffd7a13a43ea2b45d2e86c0e18529c678a10c3468ab85102f6c91020b72888086c3b81f0b4d605535b185964fc8efad090e340186a338b7d96c511c12b7e0c58fcf183ad8f8e1eedd169899f97beefe25383d6011c34f9f67dc96c06a42062aeae32cd332ba7e6f4da8f06b56f8b162420d971f2b26e6e8f2634583fcde98c0fa9c273c0f7c4a1c14a08b8f0c3a84e44290102748a14c16aa1768d82e7088018b3437ace1001267886ed81021b9f0c5c6df1b13429cb1392021d5a000d342078c480209aa10d48270238b092ca2c0a14c07baf8c8a043482e2c194d9031441d6770d8c20a09b8e1461d37b8616606369ce0a4108f20b920e63d2417bef0f7c6840e2768c5dc87892f5d38e6319f581f2931c1c20a5f6582cb969688c7c75d0c47bbb332c8dca3d81e292de75e261142e697c6d888dba18c7b53e2ea6f7e6f4904f12b72aec47c35c67c3c9f3f5f7a5589838e2c00cbe69a14684a1c14e028363da79c85e6fdc436d65b8978369d2385dc878cf5d10fcfa6330ea312077d346d369bad8a36e76dfad008c6fdbd2991c7fff8bd2d1182a7bfb725d6f8c965801d5cd9bcf958e65e0ee91b18cb7aaabebd87ab1af073beb72e7a78ea4a7dd437760e3c78d2f4e559a88ed7426f5f866f23c4dec6a0f9db1831ffe5a679665ab96a16a0c50cb9002c60c80560b962d9bed0ac7a3622f46558fbae3d4ce6b72f404bed7feb0f9d2bf1f43e1abbba3d1b59c6370bc87266d90a0d08524a414a3b57e7ea5c9dcb455d61975476eeb95273d9de39d8ea662ee753572d6bd92ed579b743d59e3f26d6805d9376994f50cfabe557fabc0763dfa47ecf975a8d9ecfb167d43fa3aec6820b2770a30b1075c861076deca32d811966505185135c8c6983e6f92a91c47befb9b35191f75cd64794e6b950574796516717c6fdb18f3e1aad7e05d8dddd5eb7d7852c5b4e9c5cb75fed76ef5b5d73f2fe9b7acf5954f2d5b03d9023dbc1f6ce39b2ddeb38e7ce3b36a26cc4b909df1cc7b183dd91ec9ea884b24fb1ebcfd988c9ee06b06a4edf65dff649eecfc94d7189f7de926cd5753ef5fbebd35774ce27c95e7e3acfe91c37274f8e9d921f5d4e0360b94e29a594b26c7da71d60397fd38c25fdf9d41e459ff3ad3d4b76d99b96cc539f3b97235ddff2ca30c64db92ee426cff1c6ab8dd18664b1430a72aeaee585637bd731f4f93236e9f7e5c8327efa21cb98d2df350a611ea0d64e9dd6927e57b68dd36bffc3ae36c06523fec2918f465f5a8b64f748eaee52c2ded280f965d37e43fec494fa45da902d4eac2e722437490d63e7ad0d3a675149abcef6bacf458e20255dceadf0878d5cb58ccdb01c9b9ce29a0dd33d7ee81963f9f3ecb202707080a618fb4fcc872343148ca5ec59c646cc5d47969919658c07b87dd47235bbb1dfaf964db2a74e2ecfb8e99ef7757b5e4f35d32f9d73ce7907668b1697501a66118ef326679192caf0ec5354c255a6611661a7fb733a8b4a3866925a99f09c33d9f40bcb6acf75fb8afd5196cde9ceb2e971f78122ce88b93df86a34a698ff3e67f7deb53ef594fb3e6f55ef2339075b9845d6bdbab388e71cc95e9e732a2ad9ca55eef93e25f74df8de632b96714aede43de9d124bc2061fb2972aec474837dea1cc96138521dc05d234a9b91b097cbbfcbbf3e9de9697e127bb9938ce235622fd77777d769dde5b9deb3b9706ab2a444794faf78cfc41a9801822092d037611967869b73ce39e79c6b66d99000cbb8aa013713d759f3662f91f5447e6f47dc1e7ccdce39b28cb1f8b96ca4bcf7b53c977fde22cb980d39bf8f5e0ebec8e59957cbef3db1e55e4f1882ee72d025e53dffb8c997eb4db1b23682dc3e88f19a768c1301f327190e29d8aae35b6e041b0f80df9b115f9ec99cffc36ddca6f4e13b1ee361c271188843e6f80dc9d3718ca87a1c17d578204d7ba0eadb03b6ef06bb70c52539ce5782fc0be638a1e3dcdcbc70701cc773c872f47503f8f02ec421717cc504ccf7e14a8ce3379d88801c87390e594a791cef4404dc387fdfe8e5f838be7cd4133a9710e4c7b227ac06a8fd587b5ece14fae8afd0470feb38fab22caca5941feb4b7998e394a37738ce891cccf7e130b284794e4db2c308ab1df8709ccae4c3c3ca243a8e2bf1483259c26a53ec7dd41bd1d779c8927ecc5b64222e112b65994d75ce716ab953976531a72c8b3102b896f37f20e0c70fb29c1297b0df3878432ee977894c4a1cbf79f2b07621a064c7f19bdaffa396287ca18b649c0aab25e751fd2e2f29cb6e5ca7ea38c7e2127e1d77913ab59cefba71ca46a1dfd4f2c66fc8243b8c51fd2ee7fcc641326abecba7a8e4c665c7f8790f93f9383e1fc7b9daf8ac3dae0ffd08314768610ac525aec7d9d05baec42d12c4215fd35b1e30f32d0f1d5c900b577cd5b2f62f1fc932f6a3c3c852cac360ffc3633737ceb1ea43e6a396e3df78ce8d2f1ff5b80f1fb5fcfea6963a0ef31cef21976739b5fc9e6b29aec3c8ce75c8b229f6382efa8bbc321fc7a9b8841fc7a7c864e278ab96a1e3380ef99aa2016aabc438243f8b1ae0d4723e8ebf7c24739c4998df9065ec6fbc47676287f9c80e7376581d3da7deb852e390fc376429baf72fc721f97b709ce9c6d9716a921ac69e2537ce7e53b9daf8586d96f0bf2a53e8626d8a7d585bbeaed420397a6449544af9f5169570d3f64a6853ecaf4c9d18bf861023ca9300b83f5f944dabc63f72493a0121d09bdddc208e8652f4bb290d3fa3fe7ea66e2ea4fc257d4f023f2568fa79eadd04bf900e4f544243dabd9f8d95318b3efde1dcebbcf43ccfa35dd90ca2f10d7ae27444715774775dd72d772fef8705c65866d69225dd92d2f0e316b0908490907ea472c6b19652c61b980fb2003cd3a95c9765383e2c1be76b7c91b158cc7fb07298de3c2f2a0831efd5c2bc4765d33c93e79f979b66d3203d09e1a5e078d9644500554ef3e16514cc4b1372bc94fa1b5f1d076be9c101402f9bfcbaac8f746a2d3f91fcc187e7c0c8a1acb1e58afdd0113dc79d659eb3e3903487f47191483152f6430867286b0ca4f8a8442a144ba4899e04a153088dd0cc06010dc31460404028168d07240251d5d40714800e9cb64e54a14ae424c8711032c818630c310600008008c0c088b4011baf7c02a02fa1fbe1b4d32f99d7b62afa7fc898fe216df96c1755fda153a462695579f9549042bf752c4cdf1a464bcf8197f6a81e2f02f64c79f312f8f6b33778daf93e9f1525e570f7b648736655a8c5500a32b7c94479e040faf4b24159a64483487f20872d03693850197c8bd432289043476c5404ef57d1af89a2615e4b70bd7ab5a20f3341842816368b8b1adba3fe817f8e13f592d5b9e0c1fb251eadaa7cf96ead5a1ed3fe2061230dba61a80703c0513be8426d6778c535a744b8e2dae994a3195a018ee2da7edc753a170744dd57c4487d7378aec6701581d14c6f1b4af5481745494383a65bb700b770b8de3008c1fe13f3bd73b40a4228de0ac1fb0b029ec83a31b1a022562a5af60580c774a0ba5e183d165e8c8b6eeebf47b58352a943542e8f4ce7567aa111e6a56a5fa17313ab8184f43bd68ecb2e2a2b3851eb84a3084cffe90c135656238de56f5b4da7e5dde85ceffd2357ed623497f49f634218c25bce0ddc478a3f32c5992c25d8ef90350f10757008ba510da1bc3656d52d9cdc20708a953a006810f8a2084f8674832cd98b1aa35d87a3a8c15e4b4c6170b062d3e7425f3263eef65e9a0dbdbc6e61b71e2d87ad16019d1d1db6edcc590bc39eccdceca528aa2a69a8b6d0deaad868162b44c7432db57a93ed6bf94d3c628d032f9d5723c56fbec400a8cadf00dd6b12457c8a6c5fba3195026be1c57c76282c03e0986a83473d40c827baeb3422619c14b5009fd7ef3c2d02443b5cb97e12ff8fd7706ef009b8994fc01bd4c7bffb7322d19f3f3fa8e19d71b09be2770a68e1c10ea417b05f3f8aefb701440aac78792752d7e46f4e72f7db68f6f4040e8065681c6d2c16adf482ac4254a78c59759f2c6ca89de73a5a991252d5e87f86e3448c60aa80b39934c04a24f5debebbc791d45ba714e9233573b9d69749417c471996c178a9ddf1b74ab541d4bf5547d832c24a5d9458d26dfbea58886bd8e0a2a40c1bd4f016577e7fe1dd5645e87e801edc50cec969d834ebc5d1c513ce2db67b15396c7b59dca586d80fc4de8f94b2a13fd22fb17fde9cf5ab55a63c1583fefacca68abf01389f6a236a6aa385fa5f9adc4c3ae28b4eaaab6295d66bd4d71833a564dfd76fff1f9cd46951654d3be5ff9fcca22bc384d8d30858775c75b1accc89d9a9402279dcd0a6ea12ae4c3744eafaf6cb3f4aeeee2f03440eb30217919845b215177db5e8c30f642f484ed0090688382a27e7f7c48a85478904644f79a06833465c8f10baa982f8a4068bfab79a5fe28c38f18de4b041dd5d1d5ae934abef2fe697b54fa2512c874c30e494f9eb3550f64880d9acbff4eb88b4ae27c1064c5cb90dafbe6f9660b2863e8f0463393cabe89d4cc59395a89dbae72c9f7b854b039d0013b5b0c2e643047e26968c3f4ee89803293e6b5f4a24f2b30fbe708bbc2678d55a642c2a5d47a50a1f76fe9e4385a403550622038524edeee8cecf77b1e3bbe80f9541f95b2ec95138819f61a77394a3a727417e468261ba66e6e887b76a9b4343ab9117b9d8a21bf01c898441234329d92eb68eeecdf7fa955e342051e9f72bfe5508fe19f4be0e6eb0196494f65077fc3997232a80f429f2c3f2145fe8a780fddf20dcccb5b4648f4676917ceb2afb60e7947d0d2b0c5792c484a2e8144452259d46753fdcf00d7cc3d56863e399adfa866f04dc0ba07700bd01a2f781c98379d376a2c89b212f1f4bf9e19e84f00c143b2775095549d5599a986eb080a62f7c16b772006fd63d18e2d8a26957648530f3ceb30b785eec719d854b868bb803757a8286d8ef1ed0ffb66b24b7ba60767141007c3788b0504b16c9a28dd9f7adbea23ee54fe8bd15c9fc899681b6ba72d34b9e84012e20e30e32183df021da4fb48e714b6b8d0a9546d226fb2c5878b9fc22cd77e97be8a5d60e5750d72f07262f3e831d8bf19d8594367203c5144f3d07015cd1aa15abafdfc1e694ecaf5516108f59f23f8069cbf5d64b206ddce2322622894838454f8511c4460dd919a62b877987766a50a8cdfa7e976d932de014665d46c53796e6652d1aea77a325473b20ff1c80836a13d56fa16bb47fd7ea0cebf8119207ac18d5500f2f7b01f080f5cb76a79a1f36b0992fabe957f1f500ee113939e61d06c501a8fcf371adef04422891b6f9c7a80bc0f30af3638af7302217e1ee585dc6f352b0c81c449bc49df60a8ac754c1202fb6c1701b21173e9ae400473fd8980a2d3c6b23b1c4d203bd2b3502a2eb9923ac97d346364844dac3c8fd78720d13f70eebb4993d266f99c002c9b0584c960f518e4c18071b67d2bd0eb12af8e0c78736def06c39e71b0adf80da0b68d1ebddd0a706e07f207ad183e167e734b64706c752b9eea98a012732bd87507a414864a71f7e0fcbc3e329e08da9f5c263424b6d06012b46646fa1787413370ef74e7a1f7a7f09835e241858b26ee1914cc4d08b34285aaceeb75737a1a2e2cf7e9960998fe03f9fa2875ce96b2f85e7a6840d2d53ef4760e3e680be703145b0db25bbd8bd3934de5cfbb328e3ae04d5c6e0653b061ff4f7580107c1b5f98289c3d7af68f822aa201f61c03b99aebbc55b730553a5f63d92443b6a41ebf1b935f6c08f23bf3bb0680f711e29f306444ffca262fb740a5059a784426b92f2599f2ed2548204ede3ededdd980f62405f01f4ec935fdcd644c56cfda65d02bf9e87000d4c28138e4ac4540e181b4c19c0b6e5dce1eb9a26731f47b49f957fc3ff79ae72c8be7b66e9c396f8eaa4f49d344afaa00e90a242d730c734649cabb26ab2159580f3728a20c45343361ed152f6892a372771d12c40b6022809a50b032c2447b51ea89cf2094be1b8a4845aea263e9732c8add004aa09113d26a6755c161371cbff143bae25a63af1ef331a0423ac5e17c180d431e457c6dde6f5eb202ead50c4b6a914005f22ddb099e4c23270f2a9a6219453430c2ad048b16d4d87f388e2fec1391351709b62c2c34e0fd2eeb064a6cc37b7829705ab313da5fe80037595e77caf14f6f9d5de8d84b67270634b91528552b4f740331b194a8cbce623fb50394f232cb5ec456dcbfeeb0965fac77d3d823dd7827de0067b6b69202c0695e4db2d6b8d1d100198ca08b073668b98aeb65902b759c66eb30cdf668de06671ee092e6bb67b87a0fda076cf4515663715766714bb5f27d9dd824440acf1c3d37f24d611e1812eda5c2fe77114e149dad5b6a8a19280e4375d7ad4e7a89ac2ea45703150483a85ca69381a511a61330c1bcf1893d55c33ec222e81f0c30ef962d715c746e86eebbcb42473c8e1426381a6d1a6e4582a0de68a49a76a91c39e187ac08bfaf2acea53df28c67ae5b43f61cb05c97f0fde26e6fe19a7c3ed2738b91a542a6eb5185bb35a0deb915f9727a7d81cf9d6593dc2c3f92b72d540588d7691df35895fec4007a3c4d7e37f9a3c955d4eb149adee33d865671a6e3a373a132095d1b79dc04617e88f2330b5b3200c949aa80a7857edaf5678cc5269a2857755159a2ec8bbe02c1d897957a5eed5745669500173de88e7620a20668d5bae6295e763e1d53d62b23112c85dfb116e4386d9b52c203a018e4c6c9dc99c43bb2bd82bf65c4926e0442626e8a0fd60cad45fc94d57cc4a67fdfdb773dfd6cf1928d2e6e5f3b14d2fd795db2c50a35d3192b8b79212454eaa472a6982a88b76b53cd9671bdfe5f3102a31af56c96a292c078ca0c0e42ea7ab32f8d5081e9e8c030ecf3edd201977885ea48ea3ab135a62458b5df9a1a660f49a80f63b1a949edf0d467a7dd7fc63c42c6e532464332f5a45ebe3f7919a17f98cd1e1c4e8afe682250248a81f8dbbcce37b971874807b834f483d4cc5c2ab3b0bfcd627acd5a4d0323268600bd28bd34fa50649ab71904e71a68252cb800795384628c5e11ba9f690a8c042e6588aa0319c6dedd1ee8b9828892ab5d6c8dca33825caec0cea2dd5db37b7a9364da678a704cb7ccfad9cf1cd596a64d44dfb5d9e2dda4836ed27051b2c16814fc7c7f90c6d4bc465c2d52bed066068c6064eb5907380938e0543c781e86c16e9bf55af1dfaaff97c6072d3e9fe57fe73b0c40fc913ae712af8a8f9486154064679f00f46801e259a4a00a0d0873956fb26f4b5164bae6cceede07bd895fb666bbce376e387b3d36e9dc50c7ea4a36268c3e5d087d39e3644310575b0a727042574a57c62ac7cc65b874810f8577b63a513fc4da0a9258881c114fe2842db24749256910d4c99610558a48188f271e1866f700cc6ca0cceff3666dea683b21c6090c29db9203299381956bc68b5198414dbff80985903c3bc0b2838076d8a93bdfe9adaa0d2e9f0da6133cfc62404a9560c75b2c736be45c206bd56a98beb06564f6f77af14e2bae6f009c7371eca13a5bf4114d5450fce119ab3572d6040dc28761a9ab8ca399f67a380a0cc4f78862fd6f81efd5689732a49ce54df24731c462ed9a1855966220d380b32bebb2d53f55d85c98e4b6d46261820f6a4ddf634e9f46e02d66d01a145b6b918746324175159f3eebe5e128c8699ff96c92205cfea63b74476705c13d934aba3bc7cc6bc1cc6262d6ff17f94718c08e2b2767ec6e962450833d4c882d3cd8b8f286c7b1c45bdc74d92a3ce9303a47c4679ec1c8530079eaa354db0909015ada4229ba8a98866da128e95e490d9ff97fcc0c4bca26fda53e5c5d109a6a1103b4640b04f5c7526c98bbb15339de2092f4c6c3db0f29338c562764100491993ea7c6b1f1fd4dbfa304cd1af1be83de83a684e714290f5ca2f7787b003933e8b0e922284e34eee299c651f1d451a359369a7c85578123c198d22e35070abddd6c1156081fd6159519adc90c45f2cbaaae89b56a1188750c9b766a1fdeaf8f1619b53e0af943e93bb8f87ce8892805fd203ffbc2a93aaaef12818436bd5cace61eb43da626fece284452e059dab60ba9c10099d3d324a144c41f5a74e0cad520acb355f7551ee6171276e1ba76ae882f8ca50c23caaf4c2eb312da84c367b0540f4322ebc1f93e8d455dbfaa17e9829346e051066e135cb7d4bf14e697707dd18881a12b370975be93be3142da9219906bbed24a6f18a860560260abf0b22a6ceb71b50239196921643bdee33a3f308c55d282557dcc2983aa44882d1da6597e22aa6e4e865954459dd8873020c6dbc4a65d5da57251bb177f340796bd8da63947c6ee219b58daff93bd53103c57144ecba24fa10d72c7f8df442a84d97fbf44043319f091581e010529b2178920148ddf522c90c99a48aa1625544068c9134bfed707d9152538568a5b2ac296d8a2fd3c90f6e876a02585a2a9d265258cd83c631db8638981ffe150fbaf5b1aeeeb346877d925179dacfea07e7cd3a05cccb311465b66f131fe2ad251f0b4dbed1b87105b0fb46aa16f90a53c294b06acc2cf6d4a34dfc928665261ab77c00be0222362d8baac6dc6a40d7bca78e697390844f779d2c7c81e995ae7eaed4e7bc8cf365458d2a6d60f01c9563e9d48822952ef92223c1b7959e204e95b93e85699aba5083cb8cbf287549c65573280a1abedd45df52f9ac45862b8e1775645d4ab1795f51f7562dac283b58b140743b63783d19a629db97708e889d6f4dd0ffefe56baf939860ff9ed02f33fc2c317da209c0ac21b9ca57c4b7ef67269747dc46a5d9bdf49a291f61f59e02655bd61d2d758277ae74890b4f71e899e450b8a17eca5d642d48496981b5661f94379aac5f51e7420b7a0b621ea4c6687e63cdb8fb89d8002f352125851717137eb5d31e994d3fe3e8d044feefc96787e7d2155548532f92269afcf27efda70545134c0c4529646d45762c697c05a1b06250b68bc8d8ddd5caa031fd4d1fdb1aca5b6043b3d65aa835a3a82cc40d1031c08007c189d837ce74e4c711b0d100049f7ea907c35a396e93d8891a5b18f15ba30ea1ce1336898bc12248ca454d645e3193f1f17080c71782405215ac04ba485daa0ae3a284af7525835e32914aaf22b3673f60629409f1e891032e88c33aa31f4cba1a2d0e8ec67f0a5d3d97ffe6b060e807cf6cb5e516a7b1616431515657b4124fbdf9308b3da1766324fc63c9b62c86d1e80dda3206e78fc8df2431f8ef49f4ae0c3f4aac455ac22527f87ffcf6bd6b7e55a5b53f206c4120f808b37c23470e8299c571bbdb95cb96864bdabebea2930e42120bd8c64b927192b226748e408c181f308bb7667f9384c95ce6e0f0457ff3e1503b05ba4c6653619c012f2419791c0eb5534cd87f7d417fb32fd87703d70bdccff054db10c3df2d5f307f2a11bd5e0ad20e408144b60ed1aee124984c525f40bcb8016430faf36ef2c1fe9dd2392f3d06e9d8cde7dd0bb739a4ad44153e52c9c7033853fdceb77d106151175e2268b26a7b3dc86025c9722cb877538e2a104d54ac215bb1b9f823aeca42a796d1ab9739d09ad0cc1591cf0aed54a5e848e0ffeb6e654b3e08a18b81ee621f28cc05ebf700f525d53beb2bfed0aff83c3cc2de08dc58198cb3c2bf58ce6db1ee5d5ef010a806390455afaad3c9e144eadfcdf7073c04333f147a19a1e90aa913fce2be349183b242ee523cf19713a43f961a7f28bd58b1ad1e688db48c1c16856e3e6377b8a727f71f0c038ad05c201284b19fd012b54274e2c91141a0922154dcc4cc76406b2cb40580600905032db142d7bef036970a3de9ef3c43b2af511b2a69362a15cb4cbe6850d8a7a82c02b8e1b6c4a9d19a6bf22c907fc3e65be6f384ee9cc3cc8d12b338d0522c91de558ae2869d04995846dc69d68beda0b179be7f7b0efa887740c307ce38c2d5beb9990849af8529f2cb99a05c441f322b43f495682372cfee9ff25706e292e4b666e2c0313585d7cfb2c97019cea0949a1355c484ec5708b27539f0d2870be4edfcfd76149ff8bda09b6d555458cce62b7089d48b9fac02384f909658c9937f6694a6b637bf7fd8b66cc1b46d04eb1f610c5069cf747cd04c76139584adfe83a688aa8bc1eee59184b274002ac16c88a31dfd5059827516e1d446107001c5a4efd043565aeba5ecec433ecf7517c63ac965e94d2c684ba45185722f838cbbe7ac9b45491c900ebb3e41097467d1aa3e6388e324ca98b41a61747775c18c6ef35d36a44fa823f25bde957387213d0192a2da7c2c4987a36210baa224d423117432037e6ec3ebdf6427d05f1c1dc8390d40987b2dd60f6fca02544d2ef847d2ec17a1c8ba2fe91f1dacc4411300746ef95f720fe730e26dd37b851a8bc1a6f26bb9e121ca4f980d28922323f5ae7926e6a9b7d2285d43307e843b6d28bda5a1717f663dc9da4a8c675bae683ef53d5b205c17f2267def5f7c53143f9ca230dbc63b07a107fa59a635c2961364486352041e09536b418eb89653e513d0940952c1f508e8a940849d767dfe1d7366b176737977267285bb00e67c519f127eb19a67239dc64d481cf7988f681c43ea2493b27fc412f2037327c30f119cf6119842fb28a3ba6d95415ac83074d41aeadb419a33d945fefd902f6bda3734d1ad524fe3e24513891d2202641ea4f6ca62d2f51ae349597907f44ee910218f0349cea14bc7ef424d73c0dcd77a1dab61e426404bdc28703071c68512b4753c32573c593ee3bc0a0b1af338de17d9a11eefee8a511a4a4c4c47040a44b3743b1ec3b9dac711943b8c272791089566bea429f56322e8604227d66e10fdd96d7f643053c68512b3d00f26a4d95382bf2e919623dcf9cfb6bad386036fd3a5d9c3953e1d03a0d0667447631eeb6e3e6eda4dcb8deec671afddbcdc75bbe961c8d71e4e7957c1f34dc0bea877b7370fef9761d9809d32cdd620abede66e456f32b8f75c15ee88f45472119d1bf7404468d1d920894640d6cdae5217e746d0b67f02b89e76480df1e22e6ab7f0f989e75b3f311c6405a42d0ba45a68d4083f144cec124fa26b22d3152fcd152d3b108a72faea2375748973e21c445edaf9c1fea33e5105ea9a488e353cfdeafe7ff2c0884983858c0af31c34848a6b013ca901fb186903ff36008f90de91c6456f0685022c39ec1400f35904808e934498a6b9eb8898c2a834cc0031332752102a2da97c3779f2eb6453a48008cbccce73b396c941cccaea538df7448cb1125a51fa343156972674d863d182b2fcd24932f26641cd8fac7be568b7967b72b4e8e09b60912ad23a77e30f86fe8c3ddc446f8ea34fa5fc8f3de4f935533f522b694a262368087efb3ead00b41bbc31707738b0a9ffacbd048af586d8360abe11d33df5ad8c767663364a0d6f19eb5144b525d532da304539d7d08f5485ed17cdd0524657addf6f4a89c90d2a8fa24ca7c33ec03f993710f4cb8f4cab643da394a26e966bafea06497c3630c7359aa323b1ab7e359dc74c51494a021cb73433f6f7cf20a35cacdb549a8f9d9a2b92425bc83cf952c6f39c54fee6e133441bec06ab36c161fdb49c0d873c11610c95cd8895761f3ba8e02a7c23ff59888d0050754252421cb43be5641d742477ef4db2e8193dfa1e6c370df547747ee4d718be5a5cd189ce747bb8ad1ade8fab820c46002940e94168b6b986718e2e358da9330f86db5327aaed9803424f6b887f415451f45b7b7101c7c29d3bb4e3e4e5013a1fbc8349bc86cbba571c2dffb583ad39cd469187bfb73dba622612700ef5db9247c02eae2fb0c2bc9b5b9e86a876bcea9e4418a55a3f23b6147aa1bbd06a9b7e71eda11f0c3df93254c6490b5b757d0e58b1518b1ac6e7b6375cc1fbe0cb1b6efe94a087e0acf1d344b3102f06291ba12e072a12275de15adc6b91c101ff71f2903356e87f14113f6a84db524aad5eb6a5dbd1e837f024c3c62ec701b6333ae17e57620f6852bde9d9a94570273254350229afeccd8f5fa13c7cace8665423e825e9b64c6b72201e03e26d631291838bda51bcd58441f79142116b536bc58a046275df4cd03952e6f301e163c03510acb7ac3c41d4a613841b7b5237924bee5d72383bc8b766dfe847dc83cc702013c36552d0913b766f98ef168a37d08f08c56edcd9005f6dfd07c920c4c63be3dd8d764fc1646f0c38c8352dc64dafd77894dd711b836d94bcc6af9aa30111a22e48b210a0ea7db0597ae49ef70d37f90dc76034170686c994c383c3d122e612d6b0c94d97a7e7c8c84df06519c081d89ab3f13e52c81d47e13d9e62c6889a597fba2792e6614851b06d500bb09bf87d24c9c6fb0602b1d074886357a5bd390409878b93169a1a909fe76e96fc2c27bad6395b647d26a0d2f458dda041a1df9d3d35ed519f12c1a421e9483b1a5dffd98a0057cab66c7e4bd8ef53df5aceaca96022d7a29ff31abdbb9945516bf228e21ca59ac18ce8a55ebe6fe722313b1d1465f542f8465f07210518c48190837e4b07ecdace3cf3b58531b2d9ca8bc0face6180cb762a56a1141e5198e264ab4815f1f51ebf66c59199869d2a85f7ee14bef8569145936259e77f47b3df611691d39a2ae844af00ea91bd1c5c3fd8f7147c3e46f6148fb0d7951410030560baafb1d5404ab219e8ec0c44f2815a3ca0cf2862b05918d2b777fa517b07e0e1548dc3b5d7f082321ce9fe86eb199496efa0a5dc3c9553bac8d9937975466862290e7f044856993ae88596df22d1caf2cdd98724e0fe637dd57d43433bd6b010a4cc599930d40e53b0feb6793a28b0ea33bf06809f292f10808f4866b58da8b455865b63edb06ff45f32838a9428a4ae6a79dce62acd946d49f43a52dc583fdf389c5958addacecc847cb13a608678439a85d97c39a6a87a07207ea13b50d128a7629d8b69866e2c9521130194ce6f5c84fa5f60b9441f15cbd4e22029394510f78d842a75010bd87faf40609f16a5f07142272f5c0e197a4a5e9729f93b184b31f12687c2c403143c1aae6daadbca1a202ba251de382a4e02d7d90f2154f40d1692dbdd9a012c9044a67cf1cce746500b98cd2b42b61bea8a40049958aaf58e5d0ab3ae17db0d725677f9283d1c8a2cab632edf3a5deb14ae78b86edf0108a2b7f64dc00faecd93367301957597ba94a24ca0bedcf5d6c36e5af656e078fbc9302d830b764720db1abfb76606710afd2875be909148801ce08dfc7f643799b3aca5b81692168338815d8f6125253f2981f8b9dd693a3e1577060e52c6408b531198835a000bc70b2b502b81b5d274828b23c66eaad03e161679ac5c83cdb77b36b17bbcef540d0a0d851a4e244a05bf8c45de0ad7d9f7afeb9556645d4374efae9f913a839d0f5b1dc8a0e1a0b65a3d8831354be6e336e9ecbd6989644b763ac632d84e739f9682dbde4c6458fc9b430156624e6bf838f2326fa15cc5160b75203d77168461d33dbb20ca0577c1bbd17ea447f9d7e12e2f3645311ca0b70b2a7744394af663df86293463144dca158de1a996f33c018e105b945ac325479e8608ba91b301105204ac08235ed0b1aa7598b69d5edee918791e8ea7d77537a356e4083acaf9dc7aa4128ec8072cdf72a33b0210bfc514f0549f22d335cbe8d0cfd07c06253738bb74c60835c58f8294fe22caae51bec904a7a744b2ae1c50823ee776b918e7294c351ccf110cb47eb765f4f4e0b6f0bd3714bb270f8176bcd88c3b3559b79113c10e36e24e39882081860b5556bccffea4468ad586ad43e32138962ed520393973118011ea72da5cdc10ee56701aacf59b95db19a84b7e24f20094371abbda7acffb6a9cbfbdd8fef07fedc5d7c34892ac2b475ec39053a5df21c8a59007695b25a9f741ea59085c16fb5fee68dde659ee4118df4913a9a47120c581387ac5502661d2a9d5ce8343bb825a7fecd96411da2f63c9765e740f33505f44eb435c984e61b242ac62b822e017e65808cb2828ba65baadce5a2d966fd4b84f212ebaa12267bbb95d197fde2ca7f36d39a1c8eab896e32c2fe381db3b39e9d73fdf85927344a7ff0dc0b9687f2fddf34516cbf4fb2c1bd07f3a300beff2f297652089bf83f232de3d256fa9951a5954d7ea2f653a2fc6e87762d3ea8578f1df64d5866d08d527916c821aa3397e6ee10bf9a5c09ea923c8b2e06dabf1a5ff0f37f80b6a558130d9af1af5c5d259df1b6a13f6d19a17a4fae6c4b79484c26e2e0e8fd6b9353ea84917e060c62b5827c556948d2d9d0bf5b38e10a5b8ff94a63b37ee7c0c3f5900867059ff83c993011164020ae9d40d1080a199fae10275c8860bd8a18a60a29aadcb0576c792cf9d3b42adb02d1702f1e806cda89b77a1a45c6c478812475a3750d84e14f26c4a109ad61ae0ea1dd4eebced3bfa2e54d998cac44eb87e56f8426582aafa8535fcc3415419ff4db51cd4d9019bd92030d49def76e801473763f33b23899d7885a1f0d0d15b0d8ce8d004bb4b5b8a1a5b76bbeadf6b66f01c4623b3e8d20a89b1f13036db2d5c414ff3ab66290511990c1b59e9b8fe4da158bf0eea57098322c0ad0c08cd0991d84c38b1e249257b23c54a2e5ca25876602977b9eb21a5a443403c59c49102cb7492272c6fb678dbc285130d6d9322c6bf57ee5b39899570c1124f5d034705d78046f116944f6d955221792cc43867778022675e92ae111871f503cc6c598c0ba3714a61ae8e046c2a64212addc1f971f308ae2e91f12fb70f8cc14846c19f1d28268ddd324094819620af220875d53b89421fd2025761d7655285a596f32b856031b839a76e3a491f5f2281c3fbaec1b351114e056c50dd0566ed6ba9988061930fe3ec222fc6a8bf4d5c63aec87c959f472e38276c6a54ebc94a60899507951181da3f49d4ad688b6aa1ec2b42bc398664cdfbcb7a44b83e726a3b4e3d4ca1c5fa46cf40ea3b8556561e563a16c2450fd6068a34f712f8943ed60d9666c25e4d296093cfec2922e21228d936a0d1760adf16163bef1f3318906d00147c33cbdb525a274d7883d3394308916445eca5a141cad84cf9d5ef6e6a01fe0b9649e00f517fe94b250e3273b8f7766c2c7f8eeca1c687647d816d9efc640dcf2ed240b6228330e080c1dc28bf85f3399a410efe99c8d1c36ae2f37ca335d3cce4f8b66542971f82bc327e7d3183f38596adef1c6058160073f4ad071034c3dce1bda361608751b76dcb9d5c06816225a7eaf941dd8b9e1de35da3afbe604e13a56a162f6e9fe0afcd6a2adb6121230b4f805abe394d8c122ab03ac24121b1071d33e5bde4e00d14d2d5f133bfbfb5dd2f16bd6ada7821af6948d55206f242bf6b2c0b29832f7965162e537e6e90393785c2232accfae34cc72223ccaca13e8f03b3bed01bdc01a24cddd19220557ca2f35f5bb9fe3faa387450cbd768919bb700d6355613fd0849c7350a02e25bf6821b02a08a0abd53a5f0f55e5c62716e6d0272b63679e334939a2a996d3a8f9f0e5a7efe7401eb40029926d510c66f8ea9214dfa93d9b94532b827fe722f8a242c7ca00d396bfb698a19209ddf157c09a058f11267ff3b63679ec77ff33272612b85f3a22c4891080bd5acaea6612da89eed4961d3d729263f2b1c2a223ca36f2a2abaa25b1b2554f29eba5232617ebb5b667535ec41d6c2ac566417ed9eacc5021126a2ab68e4b42f0e3e03732f3309aa426ee3b735a51c1044f6abd0ea578846a4008acb51f0c9740acb36f193dae9203dccec3845835f3a23f83ff13995b2a1d8b5288642d86c438f7b55290448e027155da101eac9cb3930c921722f757257ac98423a2e416f0f6188fa7cf60de063374a230186ebdf14c8d9748ed0844953d617a51d18afa89f145a5cbee8bf1c3d3971967cdbf1c8fed9565b27c13ef16965f308102f3bd17d6c22dc17e0042523aa25d4156c129a5c08ef8c2e1d7ac3b9f7180ebd1ab507ea46b779a1444067b5366ab8049465866588aa51e86e9509f875e52b0bc14caa2fddf014da58b812621e824664aa22453e71cafd1f8300a51129ab5477c2335c5371c9c0d512bb675ffdf53941b0ea691976dc0655c0f6f22dae7c99459e5b8b4fc2c477a508b942df87e2525f18b0a0bedb1d99192ae452164666f1a6f32e5a1ac5c06db2feaf87b6911da179dbd6a7f1ad28425eb668933c7cc4d8a4581fa5647e3e59fc4e9e3e4ee3374183ed760f24155fd1eadfe4a6333e77338ffdada383a01ef30dd33b9c2eb05b0780732f51c518c652528874b11101ab8055a4d09ce65d951b28f0a4f008aae09998f2cfdc52ec15ca9e53cf745b4a7ae0d43c43e7f5520600addcdfe7359f5ba2e87606c2a5165ac07124b8c1338edc6828c9243acbab14344e46ae8f6c14aa573cfcb76ed122de1023ff1b47b68c5fb1e76bd0d35193342953f22b2f263fab63f185cb32ae9d6501c5b019d03682ecc4ec6408a7ae7eff0064ed68c7241fe8cce7ca2a7f62ec84e8a0683a70f4623682b1cde07c0884a67871981de5c4d1c2d04275eb2154b8113e2bae438bf80aaab5078cae4fc4a883e5c59274c153923b2612b00a4b98fbf91370deee72a8f5cfcca4e2908e56cb2a22f3343cabf28a96aac9d66e783654861fd885ca4504eaea752b8999d40cb7718fb8248abe00300f7064f8c99c316ce225c1835030dbc04cc84e14b51d465e2530241ed554f3d1e7d2c4a6cd26afedc0183ed2ae9a6897063d16b84acd0660604567e9422ae446b72fb5a1a36c3faaca089a1835fa46a9ef8afc79e7be3952a6ca85af2480db54de196ec2d298d0e3c9327ef8413e1fbcb61119b08560f9abb76d2991a5e9f67b65ae674c3d6c7b018620910f2378f44a0ac124f501fcac6e2ebd05ce848ff4c859f6304987669a457c49bc33fd832a7d9e051edcefaddcb69fe6626792a080f4cf48887dcea4920571a5c20d5dd4bb0b8730b29a3e863128e8c1c2c9285af15086b136b7fcf1ae3e92b81a383eaa790482ed94b9a4b206bb6e144fefa62532e613261c4b0b4b90bf963f1931e61fd51e2b160e914a3577c817e8d3b8d43b9d12395b37841d0b0c1abba432917fe56f28cebc2e1cec800e84c30b55a2cbeac96e82e62a9ff5ed034ff3e6d9f24f9accf0c33193c1960d8a3b3c091b73f22cded9b25a0cb6fe2c0c74e8a534b2c1c1ab2f8ebb59600bbf20c6a6a1998ef111ef1e54c3c0c057af7693632ec85e84fc57dc0a6da7510836a032d8329eea382bdfec8aba8e860a8299b91f77cdd9634a3f6ba4e4f2e0d02ee2e9771487a46bc774e004b9bfa5a8f395633d8e0225ac295bebf62da1ff6d1985df0371b7c92c9b2b6d9262ba60c1c6cf442f16777e262a598dfa9936021a21bbc0c00790be5c0106d237bbb9b018786745d498a1826114b0575d75e54a2b0754afd83282f53a3ac8a369e0ccb9486a74c21494c40696e08d2af555409de89d4b8ea5e0ca95fe0428e79e3906f9d61e7443c48e6bcc73c93945df5c161062c2ac426a7e816c333393850d5a6a4a5907cbbbb01f6ac86f1e37ac3175f0f41fbabfae80cad8c05a415ffc48dcc79e64ba5edb3e388261c4ce1bd9337e498769576057da935aa59d180b529882711a4d406d990875023650d1a50ad95ac42b184ac0ce3c641e29ae4d807f07b090d4a6cbee5d6261e922a92c395483f0b5a02c853f1e961e3f4be44fd6289193ab506332c51d69e117eb2ec4519634585bc4d1b8fd79118440a7efae3debfec402eb06e1f45089d03b1c1c339e1f47569b7b494027e96b3e5cd17ada2471c8d59442b213c613ab0dd7a78fdb9f9171d50cac28957d7e0e033477182b0cafde449f6a848a9c9cc0a654211f7a854f5dbde31e18feeb23a3a7c99d89e1a57185875a93ca7783917719dbea5ffc7a910bfe4b795980a54dd4880040278105a54d0ec2e681a5a0a2cea7d536f5c2e3969344c4e8483072809fabfed7b10b7ca023e6c4276aba07f52efdd7f5b6f2d158004922269903b7f761159f42fe8ff0840b66361b1a587ddabb5521123a4866025f02efb1fddf630eb3c20b2cc09c790cc8d2616cfb7329ce27f5272bd24c4c0bcb5e5d1183848bb9b5f116870f818ff4ef63052a8aa17366bb4bcc86ccae389caf37a0a3e351fae2c4e5d87a9641ef791f226692f1b2ae618e57f8f1a7c5105bd173400da6819338fb21c5ed49678c68bfa54a5ffbf90acf970834aaeb34b4fd70be5ce21671f1db6d8b3b232916e44c8efe69290c118729946aa3ad45a076e1e2139c97e4c4b101ae1a40a91cd2062fa323d9eac3468f07589bb5d35eb349b261abbfa0d4dfd3ad2f8b2150f1ce85bb916e05e1ef7970041c6656e730ee282cffb0cc2290361324deb631e8b09986842e28ea120c6e22be2fa9a00c5959a9e43d198f1666d40f26c867c2b9833d3ee8262533571590f8b65c3e1abe20ac665d3de079aa2858088d8adebeef9fd67e7e70474267dbcbb392985d54723562a5d7ae14bbffe730c642a96d752f71e39913ca24e015bf902c0caea90bd16e96ba3a5dcce41c1ac48dfbceb36bbf51b66da90ea66b85e35c4e7694864e8925ce3cabaa580c8f1f0135b50a8b7d50b10e55302c53fb4bfdab70f004d435b4652bac91fb6eb4745fad2b28b458824dd6ab834535acc72f0f0682b5b8e3f1120a1656cdb7f8839e595b620b9aff99704888f746288414abdc93ad24034c4eca3e354cb129713321dc360d1cef032bbf5f5342edc2fbaca6555327c8652123ae728481c08a3d08b4564211a1fcbbab90aa027b88d698ba426ec31bd38d471b41e4414e12708d353f3c216014f991d44288d642adda9ffb7dfbe23810b92c7333c8c3b9536c9dc15fc7599843b3a95bf40242b303234b4bcf5a262a989c719b56aec27ac26f781634796d037a4fcb2695794171b5866cc95820e6433119a5d6d80d1dd9831e6c84936a6491ddd59cc8b5b5ac5be6f3322292754fe8a3184c4e5d9c1d06afbbeae2186e5c2bba68d85d3777a9fb21858d4ba6e3d3d38ce2f8f6286be45b86ac6482606e9aa1ea11e281bf211721f5ee520853aad06ba75105e58cbc87ebc83869a98b53df83cf5efb1c2506d38081ed5f58585b0542587808e57d960f88b00868ef52582c8d0c3ddc9bb00460b808bf0ea502a200d43951875e4f5e30cd374c1e9583275a4246ef945ecc80251407c6db439ead0b4ab15ccca6855a52c95f1956c633a0b4e3d81867363df81c9da8124d84f6605cafae7cc882e5a82316559d7e7eb30aa424a8f2d9d2ace04924cc904b91f09ac7130e4c35593ee207b03274777f0874959977c48be8693dda01a23243d8ecb218500399c2273284e48b8fa48c0da67748d1418d35b92ae87abd8ba873a589b3a3cf4bfcf8a1e2b595b01cef5a87517938950851f7883dc4bfa5459e3be73d4cbcd54bb271103161d2198649922923927c7743928129493ebc139b7948195b9081b95767030e71ce5decb93dd8f374e08bc54db8682a7e05b54bdce51a3130f9d3f3fa2d64d510aa083584d7f689b1ca48d62bf220796d287d0895ec9010f3c435aafc7f08fdae7589e55b2e5fd1bcc0b172263efba217923525856525d7d70acb422192daccbba6628b6966f947f86c9022d7f55e506c67b25f200be291a0b769df303edd8eb26362057a70b20ae76874a513dd9a40b32490b6dd74621b80948c847423fd061cff206746bcf3954415af77b9061c5d8abe6452179e098d24231e9e25067997a5d4ff5705942282f91232b93f19fa27c6ad0370a2cd07d2e5159eaa9bdecfe2067d66675f90d2267efab7e9f9363cacd2d248db60559224025096bbd435de84a48c139e2349af0cea30283608d458a2a385dc43e74fb03c3040fe687a5fd573e9d0192f309a233b0054283e959c41e0c60f538dd0589cc15e5afbd4da26bb88388b9b2a954ff008905871943b115a28a8871478e616125c38e46afc5b8654dab5b5fdcfedd7f0b753b0d5b67b0b940b81edda0867efc61bd15b8c0f929b8a17bcfba94c087302247ad6c617917f26ceff6a74e6ffda3e113ae60e97cc0bc49d9b8ac8503c57985da5cdb5b917acaea8250ec7f3f900f97b7fca882da48c713306c1c4512532fccf326b593a46a05aa9e01b9d30c9c245d71ac4201a064a1e4e7575cf9c900b296389e6698178c4c6a678f4394e50a000ff4f0165b54c21875383cde7435334e0b88d3b763381a213ae2101d04c53ff62f828f91d7337902c52511e0ac0ed1c83694f6330014cfc8d95184c294aba1345806c3667b41ac47ef2b29fdf8d5bce78fa84af518ea7e2cf329dba195f51a89a3612a75930806807bbe0cb80ba6897728ac94eda834124b9720e3443e1d37c991be621c3afffcbd4a9b58688a75f6067be93f0af0d9152c90fe2753da8b8b43d5767ac75da282d6c3fd1b71a1a9489c1bd71711cab4f090e459ecbdcc45cf8007e174ba4c5453fad1a12bc8b58a724c9eecb4e9098691e49c274df2d4b92474bdcafe447728d397aafeb627eba455ec080bf830f95279fc38cd9431755f7050d4cf23491a8122929e31b7bc228d2a2ca273598c48dc13cd941985e1231e2f0ef927e8200934d83fd0ff316409bf94d6c60943ce244d2ce9b8fe652737fd67c53b0099dd10ddb46e0cf2b6c756a75d3baafadbe4a8155fac1ff97c8dd2a312563d25095f254693ce182fadbf93bdbc1d5b54399b0d43b4bce59199683770e5ace855b39a517c35115d6f10863777aa30032b5929ed50955a75cabdcdb081fba7942e929a3af10ae5afbf77c7eb4233c0e08a7369fd2a42023748801dd99d6d9b84414894c8dd42cc2ecd225ec8fe52b13fc092170c3c39be1774137cf614e7d3e117e05e95d648765763b604c143b572c560a741aa96c93cfce9549c875d05e999c34658d54a130cdf863126f24639e08e8d96ba0822b364401fdbac559ee330d81b14a427f3820baa50c145f2efd091cb560d626632730380a63ccf70ef9437092458508394712885d61421d2214da206f9d5f672d206d6d6e3413056d34412eb5a52b0b7c37ffdcd75f3e22fd36825d7897b0b93eb3b554924ac42970eb684e8c1347e98f442a5d044dbb3f56e31e4b1bde268e28f2414882c46c1a0b65a2adea8a5f21d27aa07823bc1aa8c7026b03d56dbb0be649bf2320efcfa21d88d112021f9e262e52c811b1cc61b2d78ffd89382adc9a668cc2510ee53267c95b210a2b6610c6759b9d3c40e90900a4e04987233baf39eb18fbc1513b84d54df85fe28bbda20ae555058f7a6e13ce1519a91f78d24d49f67663e01be2f6b6eba86f93cb2c9f8142fcc1e2aaaad677636af5190574ae9ae7cf4817afe9f2f0b75139b035c70780fa4aa015ce21623559395ab7388e8ecc1763018dc783ce308d1eda0ad44437b4e58fef345608784117675615b8a2db95fb8fcea4ab979d4fbe08a621118af024a1a915246da6c0529c06f92028c553f001c2a03a9f5d0f0bc6eb5cbf712404763a02d5c894742b3601c0c1e93d08adb178de32df03cd95be63343881703955fe50f398c7a72c5548d23a7261927562ad0c212619ea106e8c9613fba38ffebf2684ae42a6d040851b31473bc48efc335cd000a6a15c62f6cdc667b755d0430f768e8b0247f36b2df8610ff969156057b43dc4f6c0bdc910121b659c79265a883b55663155aff06c7a7e5b33e81c157210222a23bae30dd157ae530bd74cc3523b38a582039fe058b53277814cc7ad9bc76e1c65c1932ea76c8d34dcc7584a5cf2ba9e576c22c4e9245a44ba71df510c503ff1005fc9a02d5b4490a5ff94d78226f03ad545a68767cfd4f191e7b1e1a33be873f5a206415200c761da02c066b99298e1714c5178282888bf2d8098fd27ca4bda6a5d3f221e547b75e2bd96652207e9f0740e327e847419a1b241afb3764d6da8b870dd292c4f30d3d454ac0c84e1689941a78c39252cee78cf574a5318eb8706f7cafa373ec469a998b366bfafc91d4174ecb63320689b7f932db380492525070e62612712658908b5c508b9b763a6e61127c8df619e3817eb1073c67da233cd4e870ad97ff2ad43ec007b53f90a0e20d5651e6bd5781169eeb0f6b24263ec4261cdde0c6b6408b458d6139cfd1dd85d4e861844e9dd36f4339bcbb08ccf931abaa74291c715278a53169248d914d6108b91fc8e6196e6ec7f5c2496df3d6955110e420a2a60dd042b80355da97d27978e5902bb5c52e82a049c8f691644b5b78c3d74a85c10a21afcddfd31af244e6d30e14e0cc41f734fcacaf9a091188184693294928adde3b2817c2796db1e05248b1e2ba858eec795817368db7a7c01e18d8208e6197fabbb1443dd6804256dd3ce000fb413aedae5cdcd7c857c78fde2adf47f361bb8bed915aa2d75dfe039ce71c75b559c35c413495c834509930c3a7d1c4567fabee8700de6b9e037c331dd331db5ed94a8b849d0aa7cdea4a167257647ddfee53b1e3e799b164df6b6cf8940e8535f7915fc4941ed328a15049a6bf3b20c6921a6d758ad8a9ab246a184bc9b6b458336656d87739e16b3f7c4395abd36da726028b1800067cc6803e516b8ee0118a4a094b70a9c67ff7178db297af12a60a3cb45c1ca3fe722562b0be942803cc68be0569e4e6690c45f112db041913dd012a50accab971475591b328705e21398d98447eaa288f05c8014128ff10d0cc94252e31a5c0652012457d288220eff51c4b4f37e6cfe624d37c7ef6e9e54c1c21a79c4fe41f4aa1b6b21cbd738e8d29502abb89e5e8f6017b27579a9bf12466c22c41440f700a54dae58d963439f7578ccc4bb2d252ddf22aef1ea7597fb7304eccd43fa02aa91e2bce67b06e7225866fc31cc0dbf6ce18d5ac49eedf32372efd350b368bed7c00668e9102eefdc77efa353ca6c2346931f360315145c5a1814a953dde44b836876d0e115b028f598d0ca5db361615be32ddd2b435d4c7966018872b4b59ce655b07f4b724c7b7e9efc1c36c4c7ce347ce05f22152079ef7800584b9888fe76ca6059aaae34da70216053e2c4e05c31ec1394fc0a5639544bc985f60ef25968136f70035c1060df929fc396bf17a01c36f976c538f5206fbc6e8a4150fc6df0f0bcf3605c7965a96d5478c749f4d0ef8184a429fb6e7e7eda0b2fa7b5b4a610ae09193ad14b7eafd355612116a1a860af494e57f95d4adba5daea4b8600d9955ce2932eab751d155f547f43688d047cb2f52297085c427f63b89a385a61a12442c2acb3c2cf39d39dd5db7492af67ac467c54d945c4b5ede0ed64f6b8bf60769c2210bc2de89f071c4c92584b8dc87229ebd2525c55aecddfea46d4149297410e962527593b096df6539b5022f66fa0dea1d06a2b328efb54f5b77afa866b8a3a08eca964739cbb1993d883b511353cf4c4082484efc2af4473eb49371d4d3e62e4df5440a20104891ec1bf2d59d5d4ef7a28306fa3de97156da72c9560487b3390b2bcb9b0bccbc48059c5fb6606b8e8936fd28c731b2cf4f793b85e44b0035749fc56230db45bf41b1611b891ddb8e74117ca60d078b449b3ecd88e613d8856c6954cf462de8fd809154d0e0e3b58d634a818e91731a6f75749846465110d8ab63ad4b258096a9c9d25a75f42d61a0fef31730fa3001a2c431396413e6c84e94a0af485fb896216e968172c73525fe3effafe00632c6388b94afce032f35813e5790450833d03423f758828bd8ec496c8c4d06c51cec1de474504498927454b3584f02522a8a087a371a8a9167341a849e392febde547ae8f15a186207ba137d7992753789e47960fbd1c644aa5fc1343967830646f4c043faedf2662c0f257198907cee40336ffbe10befec215f367528f082e4f92b9e9435d7313e922a18d581afe5168ceac051a7bf54f0819e5fb3d9ab99103c9c35f4f27f9c08f1b333febe71050d0622340c5e7cdb992b332274245f107267594cbf29c62cc5a51bba591cd1e462d0ce13b51861e710cc9b3d46c3755176f752096852ab8c5da6a4928250f2f0e58a39e31dd0f5ad7a2c15970fa1672f7431c89e6162f81d969d463ed655ed2c91375fc166c1f9560f35485b7972ff2be4855e57dae89a4b5c0f83aa139a6fd2bc4fb41dae61d8b512ed6f84ad6f553836f710a4bd7072fa76de3499ebd8158d559880602ea2959ce154109c60450c861158635a9b713c40ffc259350593ea4d0690c540d37db29bb166b96099104f8b42625f732fe64b4875fe559d2a97cb112aee966edb3c17db99cc8ed3d1cd62b0db7b857fdbc185c9510b7c6e74689abc599d84b7ec3dd3d4543b9288d1a1c92721be961f0355648272c40f04ff2152131a0ca11485933239d5d61d94159c5f8c3426ca579f48e1d7b001c8827fdad92243567900de6c44b9ecb299f3c6d90a8a1468859dca673ae3d4e622df8bdb1a98115e4136ee241ecc9fe5ffa1eb04e13703648d626e6984232be47084d8b8129774aef4be852c475df158b366808a2fe0d809f0a7670bbb2430db6a1333ec375d842c3d04e7a1e9f1fb828fea0edb58e14394eb3735b0ee096b3801371072638558d5e97ea2998720dd3813f1aa2c0cf4fe6267e67405277579c507b6c3d0be72ed44c95a1e0322a7c8e70f97d97e2648cbddb0c11f12edb97c37a52fc7d54a71457b49ebde0cad0e375826ea5e0d563c9b521bf7f1a63e75197724cd5bfc4654983a0aa1b602b3242e6f4887a338896130431a28cbff1a1d6524b5d66dfa219ae426209fa2f2eb24ac563494f148d1c3ee6a5c888543226b399b4f04592222717dcc66083709cf74810b1f75d2acaad3b2ed2f39c2e6a271eff473bb28c710ad80a0c3feec8dcfbc18d5e062a2d89162d15d342aae24ba42e412b1ca5c1e7201a7bb6c9c28c7f4e662f6a3e506e60735732b8ef27def67dbc095336402b7437d97fabbd2aea13907ad5e440340870d272bf00ffa550e65720391695f18048c1ca078765c756b297db6aa02891ec54f923460306639f34464a49cd852669d3934c2e58893972a2714dd96b6ed9cb4ab280c3ed09733ae2f1d10550940577341909caea9fd1017f43953e6af3ba1731822657b9b7e61f7b9cdd6a26e3804e630e68896484a5e63714009cdbdb5201846093048ca86e3aa8c26d0c2fc9c51ec467cc7503508f1f33d1779af8f745bb9d6b21c4825ad7e7c6651587867ac990c2f1d18bacb48d8e971d96c784eeb4e2068d33031e93b40dc5c10e419a2341b988582aad415527199105107436f4e7c36ae33d29cb312cbc2cde6309c0055353d183395898085f6a53756a6d69e012f933df3aebadfff73dfd20c4134ea60cffb8a75dbb3e4f626007020296f63cc545ffbc6b192ef244b38264a0ce542f66f0f923e093843db689ea8819bec1bd0047587f75971b887ac6bf1d72234d7477017b82800a14cb42e599b7c8c00d9179e003b296ab1c42caf089faaa7023a037b0435d3e981e39399a01348b29450f128dc4bf738e01f2fe315d20e9f4f059595f1dccd47e4907093cb017408eeb0dc58e8c880dd0edb56a87c4dc6c2a401041b1920cedcd7046b3470da422c9a0ce7c8e4031255365eb7d4c216c5af1f27414149b64f46e90c1c34de39fcff676dab104356b0245e809ef0991e326e37ce47124141870968c90c9f2c20eb01d313059c927e81f22f0f1e2efde2831944a02136f230424426ae4b89cdddbc3b161f09234fe0b8beb075564a32267cde42d061bcde8a97102a4b4aaca41ad7d56bbe26ca501081577d792dd5b0e73e620cbae45df56d41bd3617ca38a3c4037523e920a2cd434b1f104881fc8cadbb89cf9b7a73f9fc072de521d925df5d352a8e4554c5818d314a9d5dd88893c2a01b7c24fac6760dc9ee024c6eb592c0961611857689184c10c85034c56e82565ec06c84a99d0696fe5156b30b4138aeee8d79163ebd3c569cd3a846fcece037da1a61922347d7d0a96d6e61dfa6c9640142ab6d7cda2a1841af9a2b87eb41187c7d032fe0f0d9aa998d094835063308838fbc5cfd98fa1454695bdd2e7348bb3e789b15f78d9de9c384608a6f2c275d9c2628ea1d973e020bdce13782cfd506197674345a0a820990a6061cd98447580997a1c86aefc511bd34d9369f5a8e1a5d62afaa008ec7a832bb1b416820f11bb16a598bcb44a682696837832159ac0a7b887b56eb2e7d1e4950475e1b9613380509a99a650f80cffd21e6eddd84750288521f8e79dacf4ed14b8e364fec7b9ada27689cb28e9c730d020b1b37c0e4647383cd4e136c202b3c966142606c4903d5d384fba4c4e29951be84a501bea4d941cb1713152b00adf6d216654a2d7cadf3501d631f9aa73c5744eab1b6f9ae6064c9b387c9ce8a90341f5c424a129d6a7515e76eda5a08b71ba25f2ac25e4969be471f8844df1ac48eb29f8390332661357bcd4408063a08ef3544187ba03cfd85d8a13692aa50da61924721f9336a2c2d015ae940e85fed8f4174ac8eda8ac08434bca2881fa47118ddd67930fb014de2894f60f643d30aaef5f0e0087aa44e50113e5af7263e17183193c644a72ba147847cca26f4b944288bcb52c5c0b66cc95f0ac96b768193902108131e838f457fce6d430e3b4bbf3beca269eb0fac9b52719b2f29ea3a2394ea4ce892f937194c1dc846651cecd4acbc3cd5f3604065490896f6142c05e1abbcece1344b5d18b317d996ad4e6e38594db2153b85b28327f5ccf33995dc7808e530dfd9b1e33c68c556bf8f80315e0a102494341c807e67fd8414d335ee2625d82e9dac5a69a22ee289b1479ce74160de59ba31357ab564c270e05753b9394c8958133c7f56de960d64175bacd6bb9928d4c9ebce9db44b51ee4d4613964686eed7ce23c39e351b5d2fd43bfa9c27af7f7ce699859e8819b4c6a847ac791b57cae334efaa152bb62fd67743f57a2bc5b65b0c68358827b206aa5f80aae7176fa170a357afc9ea445cc1b5ff700ec8d7f73eefc97557c54775a518925eea0fb5417c458bc7add07e518dab7bb1660c592eb52461ef555e7de6e5badf06f761433bdb3431f546866cf950e4842944bfb0dfb2b403cf92e282c8d084cae5b3abbaff972fd7f1f3988e6ae66c05daf59d482a3cb776fedac534afe3f5c2cc9eec4792346e69f1ea56fff1de92bddfd481977719619db26ecaaedcec95d58ddd609ddb58865a44b4e687736a1520d2d5337513747b67b7f6ff1cb922d48e77479e536e1b6dd10dac40f62366ca2125be581166205d63aa9dd463f59cef1742e56d58221dfc6ea59d29ce54170d9b3086e40f70fd4836063ea081356a38868694c103031d928449c5f41eca47804534e0fe1dd5b36a5f33ecaff65ffdaa03e6bb4feb5ab77a04d0edae1602e69a3aea3e88039c04aceaad82bff4f79c2782c8bd7917d50eb8d268a7f2069a64552bf2d3c71402081941d12e195ff3ec4b488f34ce1956c0bd4f9b9638ca16bdf10c8f10c6e0a1e96556573b863d4347c798e0617f737bf6db1b1f0552bef43602a81b56511eaf5beb8efff647222f916a74602e6812c2ef4cad15305dd21b0503c4aa3678c5e4232c37a13963ac3a0167942d102e754c6897d97fe910628478f958812f58ada66f147a216f08cb3d4d9d605a0b2d5f0aafa0b5bc9e2c79584d229576994fc4b8cb98ca3cf233b8083ffcef2ec16382c93c5419f8a10f9ca67a15fa517de8a1a87af420aa0d99f3c3851788e9bd76bda0a40429b740b360cc419831c33589b99932a39dacfcee9c1316f6ce4ba7163ca986111efddd2d05c345709b58cadddb963196f5e347cd52c44d00f0a5adeae0504549d543b4c17d4802b1d6155b302a7bd0868da1d7ab8907ed08cef127e991d9205aeeb2f5fad1f13946efd9c0cc2d716da49bb4064f902b7fd25be6d5b5b6b44bc7f7b49b6245ff5101a6ac80f45aed038d2e40ca71783d5e15b7fdc7895122458505f39707a4b48116080202c4ffa659df1d2639a0611d01421462b8d41427d918d39048600a0ea0ca69fd8fc3994316ddb44da49870e5f7ae7b7a674f9615ae2291ba70d05bb59ab01683fab97d5514057acbf8cc2c4d95c7781ace5d7d701f1913f7d13b894055b525ba43e967b74634c302273000fffe80d986ca5758e59e07fba5c5ab6e9e0597b6158eaf059f099f5dac64d2ca2dc6eca1da5056a8f8d7cf1026f1521ce65ade53fed44fdf234880d7364ca0f77aa8426f9334733eb7287d4bbc4c648595946dbbebac8f63be8e928e7a022a346b7fa1cbe472b61a598ca8866f9f9eab70a03f65dd73db75aa4ec3ea5f9dc598b8f28a5e161d94513886722f5a358dd65d20f091d3d84c41cb48dcacdd239802e48453da052154535138f9f63ea8b212c13d5c21e4dcdfb87062162e6665fcdec9ca5c5ccca8a90f57aa03aa32e547570a2ac08f850ba0d48d19eb8129eb9f0c303f60a0f074015890be1fff7ef2e31963a6ca97b75c67a061d22fdf0af0a96af4d9813d1788c9002474ad24cf62c2defbeb305279d632ec9b645e643b20ff3f18617c2e481eac308372de9461c559447bad51babe3206f4831b863b68ccffa767663075236985dfcc74b7c2578891614890caac95d96ea8cec97511a8b422c5da9aaef3a04f2082773f9d312b856e035982e96fe4fd86fe217eb825b13dde4863cebe08505eaa3bc86f84c92702b8ce58308e54ecbdb817fc0561ae6094a50e698da0769f657f68fafaeebe65d3238934d98cd9e3097597a3e8a5295b3a2c39cc92ad5c4b25ad4331f254a7b8f376171cbf4592ba3f6740947284201a11d403fa42ff0fa16377c1059386fb548640c48428cc13591e5f3bc9b3836505ec3eeb09372b227a339c98ae9e3da10d87f0454fc6e0d84386a4648f41dd6f6bb61776376933f40b32568252a870d236d36fd7beb7aa09689317dbf945f6aaaaad5d0965259dc8b3ae16e7011d957d1a09e4c9744b6a49088637c848d4904ff820e6655dbfdca5675e444101d1d84d5b62ae60aae5de4e59853564caf9c5cc5102927bd68f02f86e86036952754b557094881aed786013fd8949e0f533c4221aaeef790b89c06b9856047d287de2f7edfe35fb821003c0941a0b0805f7d7e76eb437ec8b9b5c429815bb7e043b94716b73632ec93c9939986b35aeceef1e8844f27e9bd874dd5ef30a520a4ad32c47d50da8e12217d7cfac93388fff4d8d6e810935ee82c723b258fa32839b5dca28a0916222471285a75a55d23327c58ad2e812918b5828dbea38cb9e437d346f3685e322ec9915011aad59ec14a7d8052e9a8b328faccf983e533b46b40fe37907f2790fce50d27b83bbc0475899f967b07c98145821e714c7c14750c071b3b84299bb66a1127643ae30b8cd096b3b2ff78cb43a951b370b49b39d83a4c94b3157349af264b313090bb0cea754737f1c02f3b6ca0b94581c60805cc21a727f79431d5edca3e0930cd2c293cd2957c38925e11927f0510223dd9f68d1af45c1160f3c964c762408d92436ef4b0ad54a8238165afb1bc55f955c97558d39ba4b1ff78e3f33b436be0bbffbd87359dd1954f65b2ce1091ca2092312f6923fcac1cbcdfacb3f194dabb95f0ee102c1789a58bc0959e9c480aafb76331525b7643e821ff84a8088e610f66c7c3da318b7f79eb70dfd59b18275b42d9de230ff9cd7c2b5db2340e6c97b8c95c7629d943f01d6b6db3a30d575ed40de52139ab1746dfb016fe338ad6be7c3bf9662c276c818a804e65ca4262a2c7dcf20f845c07e5a5e041c1abad7a5d89a1f02ac1b522f88fcb42fc394f3fe799e92a3c90de2a3c9483e23a1d28f48f435df1c0a06141ffe7d1ef8d811226e18816c6e1dc171475f4cea1ac240309ecee067de71f09cc1c4fe8aafe20eb8b38b97fb73af8d54856a052548dc2b3f441b01e9a491474c334440aed0336e33e551a8cddc041cde8fc177b21bbb1eeb998c30fb5bc788103201f2be3ed5b1c5946cfbea1034a925d7918d08b940886a76298a43fe71ac0dad45bd1863160bb3623e31750b14e27a1175cb1241d3eaf2854c353a9394ad861990a8c2484ce3fb3bbd0991d847a615d8de180d0d61122dc1442200796f4035be26547f74f2793b4d139e2162f417dfba9c5acc4fecd4c8a29276784a08abb1c934bebcedf60b1d9bd956232f3f43ffb1e7c92c46fc0e3e2faf928c862dc5e3e9e85a29ca44cf8e1c90b465826245988671f1fa5c1152031632098c3b9fb5276378ab349760fbd1c94684b9bb776bccf5464d380ce0615dfcb6bb7866eeece129168a28ff2a6f42fc617fd3c4d959d2b6d6a5a8c269560a0b26a1c860a17e2493d27e7a955baf48bcabda85474864aa417dbe7878be236f31604a138e0bae5005714e9550810cc00ae8bf2c1e42132d2606ced79b220be67abc2b2ab651d62e0659583d3257a7109f54f2107f72724739182c7a25e2b4e793098f3a8ecd79019a9a4b25827854bd869a1a9d4921edfe85326fa6d21bd120b884ae0547420031ab50679c8fe0d204e36c24328db1c93cb23d4a90a00eeac121cf49ee2d3c72bd6170917b5580cf80030142ac3d208c298d7bd96b9b686a7406d29e74060f5e9cde94ae35f071017665abfde276af7d849148caf77bad0f02e8b0af2914cc2c20c07c6dfa608cc28b7e5e14719f6aebb649a8b8a67c2448f73cd6592ab038970ff7e95d1f0ad52d4244507dfa9afc580e3bd5240ce6f0007b742614824c631b6ab27f16b2774d65dd94cd586729d15b8d3d23d137dcd10f7dc58b0cb26c0c177e5f6f46bff4e041e4830d0b61ccb991094a3c37830e77b0ec9ffc1ba45938f010367a164697b133ddf6b11f0035598760e31d807f2cf257915e3ed334a56ec837c75ee26c3a27876b41ac39f6471d0d38f3ae5de1c5aee64411a62413102d080f267d0a1f48891b89c0dbf15438248dd01f3f20c4b2d071c0cbcbc530562488017c68c587e41354b717828c36fe748611fff3a609333d9034af05706481f8674baf95606cf7cdcf2543f599622df73e85a3e6cedcbce10798e082833b10cbcd26d5ce459b4ecd1abc68744cdfbbc0c0f27b0c307d5b0f132c91f75f08b1eb3d44c256f2563d74f086d4120d189e9a24f4f8d9a94f689f53819e319e987e72ea56f040b23a6cac63ce0ac8f86e6031ccde6b9a8208835812daa944425b58cea11e759172398a153c321d6db503c362d3947032804f9dd44aec42d7f059e190aa922e6520b158d47ed134cdc491fff2482518d688c51c28af6b184b4c2c2ebb456841d02c13a4a2e86d693fd0775c7d5890ab6def00830951908c5a2d84829c5c55f5d5c418a3050ac9ff02551057a38e87a8034068796f76a1cf7a1aded96761b8ee0a81f61df8074a47a1b98587cf9cbe2792be5d903f2fbf94378a20ef84226b146f61d27e9f2476d7971e9ca40e4243871bab6cc465e702b83728c43b0695db5260ebd0e7881ee1064694d3fdbea1ff546c947a100d351abf0a6bbb75131fdd80a1db10d1289665a90375a824e6606d0d3b3822f76be196d087eee472042e46cd323f354d0819cc5a86e0d6002e64bee03c60f8860420466e566b804dabf9a4fe9b14c6b2ed50640abeedb64797c7114a7f4cd3b536539617cf32afa52e3f1a66cd6895fa38226db6abf3f320f089d4941a82003e78f703aadd8a9dabc320fedb729cf5e67e870704b6249a3156863e6b663c7b8567703beb900d81e40d18dd88d0dd7e8dff3d15d28c2912c3a222b6544f1c8218d8a123dd997a0b9785440ae8c2d78653d5513f10dba02d4f8b7c428fc16a196a846003e44c18883b9b3dd03a4426cc4c5062bc6929971146aa1fd27a0292e45085588c7e240d64e51c854a082c9bbcd1e42f701c2a3ac23b309c8700c74b13ac39000077d86b4b1499d09f4c80c7aedc3d29cc140801f4479524955347a24e441897df77cb2d1d7366c07791dc24f78aed86a0c8df944217af9f23aa4fc2c7fea3650794515bf3d1833101e67a30b27de32a451b04f5e4d5f554465a16f6cec0486eadba05c58629af47fdf5c7f3b057bd624dda6cc2a4bd251adecaffe21af3fb5c71703462f10bda32d39c4d9b39788b7be8a16f913d87390a1cc5fc741c17ea0e87f1be1191db8c3e573703b73606c7d1209b5c34773e12c4c397822c1b332805271084d2c0ffe2538a2bfff08ff0284dedd1cd00d11bbdc914ab3210ce003f57204b820e1cf29da35a110a41f4dfb6e161034b53f15b5016ce078cfd74a9242fc71356177c3c73f6a790ff1cfc28a46d113783f39f1a6197b095bb2ba83c35d429cb7155df07665ea65e39c1fc3c39ce3e9f9772354f2f93df8b25a27025abb10f6ab16d9a98da67a44d689e2732cdc2b153c8305124bce193775da12e57f69810fb58acc97f4258adbf7372a37d1e2890a762d0fb9c2a399329d040dc9195a72eb9ae3769f8203f2ff1f445ef84916e460f108cb11e8b4b4fad48a3ea172704225f85b7795f02641168ff9e8c8ae47a2a5486e4195cdc17b0566636ed9dec297c6a2ce9d6c76f55e49151bbfb73cf642ddc5ba81c799e391c2b6c53920aeb1dea6801f206da5eb0e6c5486c4696ca544b006ccb2e5c1b9942c7ed958a0d9f634c58567f219ee4ea7676e57aa9f2dcd187d2e333f2ef2c188190cd2d4e8249667b3e7bc02d7f7d657d965d641f0a07814adf21af807991254f2cf5aa6e5de9ab0cb04b7486474c8e4c537bbb34b96f1f510f8c1ab2392dc69d1f272c0e20aa959d3f321791f6d586fd9f604545a2ad7e5e7a1559104b460beebe964e08fd34cc2c1f0562812234a8156cc7171d929767e96910e555392b02d276321e454d4220b0b29f4536c1a76c54dbd024c1003eac5769459204cd29a0e893213309d6a0407e4ef54cc887b90edfc5ddcb544b64ea506f04f8dd72c20cd06fbfb85d54fcf1a183028f03b83e0f78d15f3dd442157c5b63e1c3b8770897bfc7eaf9734273034d442dade6d4dee2d65923205430ae408c9081946b364fcf58da070cf53a559937bde7eace69ac1ff3a103b9292cf8330330ccb28e9f34655aa4ce67d7d1b2ed5ff6a6765df6ac9fef3405cdfa80aa68e846906b1eff9af431cb11e2bf688a5fd9f8f89a5fdceb1ffb1fff3f767a8e38e1ddc977d6cecc07ecfdb5087fd9e9f2150eceb38abe8fd950105fdf723dfaf6153b2fe7ce720eff91ea09e71f623e2d0839941b80ffbaf962fd0157a3033ac2bbbd107cdb2f7c62bb3d1d6f3623f25896c8ee4d3331a51c17e077b0bf87e69cb4d231670f999da8c92ec178bb96627d6fc3d11a531b113932849fff5f46533aa825bafe4727d1fdaef5dae9af12bb912c5e2ccb00f6a0e45d77fa20f9a5d8f6533dbbdf79f272e75ff75b6b3d7f34173ad5d494481e88b0a4ac7dddd07cdf3dd27c5e170381f340d9d243a44f3ea4c1d3b6b8e0ba8a0a284e88a8a2748aea820e1c815154e49ee60e1a38a12a925aa94e014a5c906ac2b701021ea0b09b2c043c5c3f34275b2738838fd40278c151c1a8e1049d4809b020725a6f090e08ae20d79d121a182a8393be820468e172c7cd20c0922cf111870b0c3039e219f1029ebc37d3d5146c9054a3966a4b9c3252b4a0a239a10010c72a6b400b5244b0bb75a459d5cebdf2aa8c8f5411192ea38d1935407091f6e529db1561184ab8a1b5ad946529d7b072acf72e7eed070270c115d561554c420c6eb3c712634a3b8332577278a5f9db903e50e132abfb366cd144ea4a08273f5da7aaded29acb2b5d6da3676ac68aded5c5d9c3d11bc94b476f0647b870898ed636bad65833b97f74e5135050c5358b9b91caaaa92baaa54077795243b13529c905b02c772e7a470ca65e7d0d4b153bd3a69ea9cc9a58dca85cfbba7e91883446da0dfa3aa8dd6dfd9bd4eb05202f477ddf8236a83be0109e2fdfdeeef88f3c6a8f9dde8efb383faf6dd5fc77c2b7e14f4aebbd2752310d23b03995daf75d35969e704d51262728bdc544be0f2079b2fd236e81b8db6c3fe067db32307b02b164b5b0e2c04fa66c71f33aff595178d92e0997fd07638796d477badb3b2683c3a2babb3b2948cb0d4a8ed5b140d83f18e738e340cbc03da95237fb34fd331c72b760c12b581faf3ed13a031255b6c4a5ec105e9de12a0f441330674eca0df2f25bf19f2c076c445d50f1245d351ff6b5587eb58a82ad827bba4a343c83a30778a28b288c8b9592344ca03f058c1258815b260e5b8743cdc89b34826763535747deb0c15cf64d891dacae0b563ea5e79823852a74c104bb2883a220acf0b35e48172449d17b257e749963fd1b960c6cd24cd392367cd8ceab66cbd6212258d2839bd8a64c3e170436a145172adf53b8992f36d4ae02e436b8d86d62e9d35b4d6b9c4f9fe0c97ee2c7be2054a949c1620224e5494253830e1722b67998e9d4b078bc89439533081927273cb22e98ca523a7d2b9caf5412c72adf59ec1b7da92a268cd9933c7eada3a04700c6038b9385fa6d4385b9ee2b0d024ce932570b0527803274d7ee5cec121e206383824a170683002470627382c50e960bdd9c92913a7eacd1b26eea31225e79b239eb498375e9e38116fb42c7122bc377094bcc1b2370c4124be19014a0f3ac44922829b1e2c0ba8c218918b838788245ab9736fb07816df745dd064acc4b829e341782deffefc9696232d48a902cf9123f75a9dc917966f9e7cefdbd6a011410a0957a8c8dd70bbef605c0ec404b9736f84e42e77ce0aabe25c8c8ff1301e86c8739eebda5811b9f9d2e6481b9c9d65ebed8f2e774acb99bb3a766339ffba3a3be783d094dd03cf1d0efba57defbf9e3dfd552f761f70abd59aad2bb6bac8ebbcaef33afbdd5876e06fbf03ef446b45a6ce55ec3c43cf75bc5d64b3392425f9977b938888fc875a89664744c4c68d111c1b35443a5786842782080ec2e9b21133a44eb1f1810865d3c5c8ac6343432e6d2e183ae7c1bfa1e092ddffdddd7bb8dd151240c38412244da47470c2668606d44055420f6cd4ac3086cd0b2f54b5991d0c682425af5f35ca935228b0a8b12177e8a38d925e308f0748801e953d8832be94880650ee1c9b273638362290805b78dc097752b809f87343b1d4e4b27344d2b89033221c41f10ce148c640f942a728819f40022537d02aa49c890cbee4853b55ae9842ad8042b368d264047f5a73c4c89a2176b23882ad15940a6ced3cb91a22a704ab11d678912a4f8286669dc1a28c3ba1ca6553d13cb8e6ce414126ef707728c46477dfd17947cb879967fe60369b51d2b4d140cc7f2272da683122645a025ce28c65b369a3c18890690930fdf9a598e7d3bfd3467f1121d33fb23fb31d6f4cc1fefef6bb70c9cecffe9ffdc1f9fd5db834f3d7dd2b0f90e7622806563c67b645d0eb1b06e3854f4ff7236bb9c2fb3915a2650dd09a8c0841a20e940d73aeb0304a5125e74411615a786385cbce0b45a4d182854a490a405b6840a10bee0a0d60f0786941b740b9e224a584942c12c80186276d6418038554155d28087ec0e1c81635509e7e682aa1aa08122728389899438597518d179cd58212639c46103215150128a2a8304215284f577780605b4a68d3830d7060582197470d156c84786ae2881d1898e6082d4370c161c509d96091d2040b3a6b9a98a2cbed291daa729042c91238364eb480040c4e767ac06223050f446abc2229264cccc0e608291886c889e2a44505304da2a8e3e5c7822758c860c3130c56cc6460250a33499a844921cf55979997ac1e6e7841891fccf00081293cb91fa8701802083a968514980823b5c3973959a820c40a3924c166072756a2e0410804236200e382972b989062a38a13524c218182133ecce902f699302ea051e34688245a34887a21053934cc412245ce0b099420a284c821024c1a02e04820030a4d9c50e285385d94551b2d261c59f226eb872c459e903283912e60e0f0f01218a2c4942b3dac61738491932655d8a0a509161edec450e3a565353473010b56191c114be0b020c490275688b83387891b183cdc50c31029ea3c5161a504ae0c1b304ad2a8d0c68b8c6ac49815f294b162ca98322db4907067843448b090b0893571830b6a88a062450a1e5a40cd906b824e0f3a5059e3c55d3881fad2831d2d72a40690c4142a52a428c2ce1c28ba886cb2a24832449e144a3083810d3db46113c52a8a1435d3cc972c69aa18d26488150d0891a33a722797e7c9133e63c26831620a8e0857301871c495094caa5068e3a68a871e9f1db494b0029ca9115c683a4ab08c8952020a4820e1c48b11082104578218e0ac51a2853064ae2011a7081fa69af012d46b6498010e9c30504478b2c08c096bdac81993c298345ebe2052d01182053449c440a6863a4c4439e3c50a2c54a0f1227b40922167a0a060bdc9228323ee9c806b0288222630114e12881099c2a9cb1d2b0ab040220a1636dc90c4cb125d5cf814054cc4f044d41b16b2e234a550248d192b155c70424a09efa12a8a1356a6572657bb831ef95f9092348b117af105b1187a1dc976ad55f342b6f62d119964bfb465233b263d11c56b348e9a28f94e25354f7e9f88d6e600e2e07e028927a872f9b91ac5995c9279ae20070c16484a5872c4845bf9486f660053345144144640d981865b9313192482b0e1aaea09226e34d0a4d0640519a078e3c26ddaf2cd1d4e5b0e1345521444335099e6f63ee89f778eb3c96a731d41eeb75d54eb52fbcdc305bc728fddf4e6e207ddffe4f22797f7031f37cfae61dfc72cdbb74f8e54bb665bb8505a28305bfb4e815c7588b3a7dbea41105c76eb944b3bc546a1247d1b6a406fde689fbc465f480eeeb7fe34c42180ce765d37835e3e42f9f03551e9c265fadf3c3275c8fd954b53cd792322972d2d994abdc8736c497511fd1714da45d681e4aede2e3da24ddaef4e04d9bff3c076244ff4db6d0be687798eb38b6edf8d223eb5681b43b238afd18aeb7e362b2daeb3b84c7f9c40f61fc7e948f6d6ff79b6d2d70d991a8adcbd87380490bbffda5bae56ab45835edda8445d383c356891a26186a82c35ccf108775fc1842473ca30a7a31003123060b9f2421417acb4c04215225054545862ce0a2ac471f718a6387122a5c69ca4302d0a78da28787e43c1f3ed133cdf9e80e75b27b715a3a6a5e51abbc8beeb039b1e893630493664c4f7b9d223224a3e256b54f0fcf2c321395f9e4f5de4247c814d8f604c3c4ef2c493a90cc90bd6f66790ec8f416b029e60d32317b9df45a19ea02878be6de2239b89c90a004019cbe407b44f40a02d2108b424cc5a804d8f7cc81c83d202b423b800ed121a6895088136c90bd022813114e270e5b1c810688f3c688d8823d8f4c80563ca18a325cb09515206133188c02693494a8a511081b6880cd0129980022b2486888ac60b39145c47c257aed7666418325c12b44eb50aa8182220132a9801363d9a3630499613124344831c4fc0f3678076080db0e9d10c1a332aa8cd20299840c60ca21833c61933c49f3104e385d00c9a8ba016b319403366ccf891f9cce88961d88c5708cef85cde9a7167789dad7452dcabbbc8fe8cb0738fed4fd8aa7a59ff521253d2bf7680050bb9add4ca3085c452b0ebbe297f16b42289e991f5b7ef210623c836d7b7a18e9a3be8fef3402b8e2025afe00ad2236fbe176630b3f21cc1b05ec1344f1b6ddb82fb34d451f3fd5aa0fdc074daf5e5811775dfbdcfec5155eafb94925cf6b75fb5981eb9fd4ac91083aac3be7d1aeaa0df811df1ad6d41570ae52bdd854f80950a9504454483540e5a6a00d2725d5b57cda7dc1585a29a9d7bb7e5b5aee7148a76eeddee56cda7dc95bb67bdaaf994abb5b475d57cb2ddbb2d775567bf224182e9d34b8fecdf6ca928399f524a2b755c17cd597fd62498beb5df75e37811e27891bdfbceb59c9956bf29d7b1142d580117e00acff7b14a75d17cd71ca7eeeed589d4e94fd467ed6e8b9b977ade556b6b77e7dd5ea557b4a0bb3b2840cc23938e950b3fec30459612663c284fc8a0f0830d4ec0981026a10390ba43022c39986087cd12515646e0ac1015e6e67953489cb2e721a92c18c9d9b16344ca46f53321222733a127a2edb8df9907c5ddbec78ed47a574b3c726ff75da25391a4dbfd23f7f67da3ebafd87a2577892db10303c81d62d06e728f57681bf4adf53f3a770bc1fd10b42a0ec763d27adcd11a6d1db555a68a90c9e59d3ab2e6dea7bc81cf06a5731ef9d70f68f641f395fb5346a6e4f24e197192296d82cf4c11a9aa210f8abbb5be1da9c36e2ce7917b6b795f7fade295d678cb10721dbbebba0e7cd05cc70f78eef7c91d624041eef14a08ee2bd13b7e47eb109dcf84be3ec137f7149123350af6dc53448854afcb3de59465029b14bcd613b02cf79453114a4e248052c5a90528b527f7941393fc937b6a08969af9d342654a5b98239d0925c1d6ce39d2e6cf6cc10ab88e235367fa72aee4d8b9928385a74baecb975c173262b84c4add6bb55eabb55de7dd7aafd3792b68bbcef3ee6d75a0776fabe5727d600541a7136cb95cdf0782a1d5abbe5e4ee7eb8c0bfc40300c5f2f580ef0d5e5f525d387e5605d327d8cc964fa31313db5a7c7e9ece14249faf4892ea14ee8135d42a7d02368aad0aca94223c7eacc949a59e6962935b374c9f42d973cb1604d1cd6749a38acb9641e09810c2d43a6d1f4372975afd576b5eb9cce6e52f75aadedbcea794ea7576dd779debd2d5775b99c4e97775b2d97ebfbc030743a43d7078261f87ac170c5d8e9c4e10b06c33816ebf1a93e3e4ea74f8f8f4cf6f303041434abb399d3398b7d3da08b3e423ea08bb297812e7e507fbe1fd045a0906966b2836a5090d31974ebf8735f341032b5a2247da617b98ae9a229b89c55316dc14fa66fa3b4050d15803b5feb56265eb777d5dada79b747bbaf23d52ffd7ab9037777f7cc2c6955ea4f4ff4c9da576b5eea7957adadd9337da2b9be806b2e632db0be80672e41b02de84cf1adb5bbd65afdc55a6badb5da5a6badb3d65a6bed64329f24afb556af4cbc9d26d57abbbbdddd7d09936b5adbaad5e5f2008d55f9aaedbcdbba2da82b9af3a7aa2a653bef7a17ea8ae6fcc9abaaedbcaea926d415cdf953671bea8ae6aaad16eaaa7aa56239a168d95455497790ab94c5dd79bd7c4d944245e1e2ea757be9eefed4b573af26d8fba5bbbbd72eaadd47ddddee2ecbee933bf0ea4caa143cbf96dddd2d96b319beff43b98396826bbd607deaeef6d68ba6a395fd7fe45b67ea489929235515cad4903b756a9579e46a6a089c4a84686a486e4a0819aba9216d84d422afdc5343ce4c0da1c26fadbd9d6d75df7ab3ceb4b2aab0b5d65a6badbdc22a5b9b35275b6bbfaa509fb2ae6cd69d22726c1622852405892b5430792187874beeba29213ce4ae6301082b4fa0aa48299946f2cc2257d83c93c815b90b82987e150183b9735837e4d2568560c980b5c2082e42ce6d8e742f5691285779f090e079328e8bd786e4b1ca53e5e571210f542e3f27c537ff9b6288e3957dac5d94a9d8392cb6e46f8258d9c58fce7ed174d85c73fd20fbd839ac2fb9ce10645185859cced95a71d873e7f248c9c234772e0f0a958828d322b94cca14872b686083e526874310b71a56704a204788196060e156925d9ae49ea8b02297e5875b69cb4324d3ff280008c2ca095094206705a71bed5c1e26b9b40d61059115495758d1449e1284105591a748152e72e7ae9073c51a21a8f0dad5a805dc8d0095bfd9814dc199905f232a240d2316704966276978192256e070d78892ee3468cd494afa1b59c1ed33b7a0510b787e4fd22d150e8713e16fe4e04195dd7146d9c32a3c57f98cf7193c59dcd639eb788da8e02bcf8a4a243beafb15ac141b51c1017992a23879ea11fb656485cbf9f414658a13da93361a0db269182d994e3d3637b89cb8899b4e343c7b1a24259de62e1ce3d9ec1f8f0d2b048fc61a5cf6550742f8e163367bb2ebf842331978897239a95ef489869dab1efd44c9ea8492f5bfc9d4bd8f2599fdab184aa5057ff25afdbed7777fa1eb63ff8a7d8719b83e0ca64d4ce8236f124a23795d0ba9d4b4014253c6b81b09d6b59e077b7ac29e3a4326cfcaca78549a35ea0efcde482becf158e30d2ebb2ad7af40067a5c801eb5bebe928b6ff1e59d11cd1afe2ef129e92ec6a635ffdab416e42db19c571f542ab97e4c7c89259d92eb7f624965c8f56fd983b45a5381eb6118866118866118866118866118866118866118866118866118866118e6300b9a85e2cf1afe9f0bcddc95dc7e499f7a10a139c3837e55555555559590115cd2aaab923e59c959d3e283bebc42334a862d8228161f968a4c0e1fb15b4828063ceb3a865f625096cb49d542376462f713cd5ca00376cc60c0b2d9e748ef0249d2057ab1d68e279f886cc544109a726cc710e88120181b61cc1af5df9176f4db946af5a79c3fd1b0da9f2655d0d797cd1ae17f22a664183476f87752359a599291ed476c990c06140c1878d608df916663a5b5f06b796b0f190d92e8476c3cc305cb3bd1f8a82f34039a21d054a264adf4ebc01932f97cf8b3d98c18d34cd7ea975ef6a7c1daa79166eeee5e33fe1ef6f89b1ef57c874bb0ef30832f470eb00c6a794f9fbc5663b3f7ef696352cd1af57d2c7b8cd95fc9c51c3160af638ed8cfea4faa690374ffd8b7c692e6725281d0f4b1110c71785656768d3f6b54aa9ef135cebe67e30efffa938a92b5cc0e47a25595560945c1148a1e95b628f4a8fc29f4a83e7542abde468f2a25c3afffb978678dd03e28d6bff561625312ec7cc4b207e840a24dc263a6b9fb1f71669938b38f38735f71e69638b337f36be66fe60fe971dc07c7d98c92b59c9143fc658c1c36e9a2fae52b874f6b245ae5b5fae1c3fae8f5e1fb3452d0870f4e1bfe346bd40f3f0cc3d006030ec371466995234d2aafd5c7e3f7305a95eb7b93d8d7f7a76963c7ffb4e15959d9cb21a4d9c31aa9c5fb7b1ff5bcbfd7482ec2f777217a5d0bbf47f426c36f21c2ba16fe4c0c1f870f0b614fc325d8d894843d0d33081f368294f431ef9836605ff1c7c6927eae8f0d657f4cb3ff4c8475cd1f2676d7f00f12bdaef9bf446fd2fb50ccc041d1fe2796d82596b77e4b2ced52a524ced5a17ce844e28546b6fd143d08423f537a01253ba4ee59d3adc4ef483d2399bbd9f3dd235e406bdd7f0edaba5cbfc564dfd2ac94ed05d94ed18320d975955bdf1ecc2517f8250e5f5f92f84b5beebe9f1228d2e6831ffb50a4f94ba4f948d3e0f53dcfe3f53defdf33fe88f29147f83ee38f9e0fff47ec5fdf74fb449a06e0c79e36471ee0c7683cbeeff9ef7b1e26d2e6fb7cd30d8bb439d234c00f7b1ef8613f1f36fe889a230fd8fb8c3f68f3b1d7ba6fbac1fe076d8e1ff0f079d96fd037a7ebb5ee632f1b7ffc90bdcca7e986bfe9f67debe919fb161bed773798d8b797d8b750ec1b2836dd3eb1e966c5f27d74d9f5345c728d3928d97d4b949105b67fc70b28093403f6a76f73a40b28a844c9ee75c0ae264bef16afb47e49df3bcf9fd51cf0fc9b7fbad075df445ffba4be740305fc4fa27f061c2977cd7fcc5511ad8498adb2ff0c95ba28b68190ff289a30b681902c94eca77f2fc61497ed988392ae245e4049ffb14a3f00dadc80eb2b5d906396dd77aed687c0f5b1fa9d5bdfd1eeb8c3d5b95b6b9c91750bae6369cb2dd592a1022533cb478a3542575090588c09ca22660c224c0d1e3098ee9a0d78be23e01e2097dddd43656ef45157bbd6afb6001f287b92544150a6e428e8880104a54056185803060a300880246404808aa8071c7798e0e78ab1a8098c580a2f5e79847c9470f1f3a6854cc80c8696a0f1c84f6d04990c373e49493904f840d1151c0265a4f9443aadd711de2b0afb624e9da088f6ea2e03c44950a027ae3a3ec1f39d046349ba678d1c452132df9129d907ad60498b8af567b97d72c772cff097bf3a9b0a49416605cd80cb3babe04cea88bcce56ef1200f760042bfba0b95232df1e5373bde077bdeeea68e5f93748cbc9fdf37bc8b20eef61cbf47dfca0e61e3da89decd6da310809cf95923564c0e505c0194cb3f9839a7d7499b6c3fb00e459523a2b404e3a2240feda2b9d4de3616fddd35cce7ca51b7f04b979a3511ebbfce4c1e52d3221260597f585e5c3042eef0f1c5a77511980dc5fcb3d2697b5d9e42ec76419998683e67e9a67ee27f267eb160c8e4949e012d3fa2a9bf2880497b556029661d545e429fb21f7d8d44ec9f925771e0a97eacc6570795f747079bf69e94f16ed4ccc5f753a6bdb57763a6953fb2a9b7277efca316c8e11f298a2e718dd78bd86cfe0f2d270cc9c4b9bc728b8ac65597594b5c7a6fc51b00738c50f6aee31934dd9944d59df57d994f3d7dfa46002194431c851acfd108c17dd4234172d6641403f329f9e1886bd42f073b566bc82d0264b7dc2047533578b0c42530ebf7aef41cd1f7091abb7ee675f301cebf191fd0005cd5ab8a009bd8031f4e277b834be1205bb7c0902ec40286046b6f4972e792a65574e08b11b4535af89a593bae0466ee8c60fad096d9872c34a8ea42f2f9260bc7002bcf29434a328c0080ccd488896e693329d8ce6921d361f5ad85cb460e32a21045bf6461e34c204d198053dd132a1070d3b0f207bf821817ed05c2a1520ad244024c68748e6f3c62b8107119a0afc98d8bb441fb627e6a67bd7bb40b00b97c00ef461a92e2004044c28899230d1d606bb1e7c30e9821c36a370a9f3946457e77d0fa1ec656046e635235fe2d3f2e0b2c72cc946c91eb2ec3d3985e9bbc41eb2ec799ed009b8ec21cbde7b6307f010dfbb441f3587e105c12ffc1a2e7d3e6a0e67e128d6ec03fca1f52eb10798ef75fd9d36eeace175df0a3db19c654f262bc1ec2a6f768163e9fdf72d7bc3fb1e2eb5400c42c0e17043b2174276bd27922abc4bf4e1d927a0c2470fbef7d503bbb73477dff26e06b3eb4b3083ff892ef07bdac8ae2f6b06c7722cbdb79e67f76066f7461f9ebdebbc35895374fafae7b501f71fc015f3cb5995e74faa2bafcd9f3fb1484d2e197024e792e7977d75638427626ef31f5001cac591fae64f7dbbcd4f72a4796b2e74e472a7185b55d550d13cfaf339a8ef23901148140ffffa405e4fc75a7f83bed507d24e6d38dabc762b67afb1c71f3fa2683bfc37e89bc7c2e7a0c3f3248e0a2a88c1e16e4062a392d7bad7d881fe1b54e6886f48a1631d1fd78f7df923ce757bbd8dca8edc5801afd51148140ffbfe403e3602b1e3c479d9f4e748c7f9e4b5aebe77ff1961c1f4e7849a50330735a5625fce1c7dfb11a06f47da2d02f36323cd1b691ad4b7cf417d3b0289e231df1f08fdfa1c38909b12a5ea7e5651bdbe6f55341e218d2d323cb14392a65be8c3330748e138af1c8b23e1e6d524a2c2102b5a5618d201f7192b333d50e10487bb856fbfa438970a979c4beebedb1dca6bdd1b61893d7e3bfe88a269603ff63cecc7c61f515ef7d61b79d8c7e30ffae4b5eeed48a5bcd63d6cecbe74a827fa25859a5f52299a06f5e973e03b9c421108cd79ad7bd71616a0e2c8c982c3dd68963354391a8f1bb7f06fdcc2eebf9748abbcd6fd0d29b86f7872f7f4ca6bdd97b4aaa4571cf49d30718ae06ef695e84b0cafa29220c10e4f38dc2d14a35cf57389e02d318011e18197959585955df75dde57ceec8d9e373a9696e85628d9dd3abca34351b2fb27fc8e732487ea5af7f77a5f629c2bb710608e646f2cb1cd3d75472a970ee5b6c9a330aa8fb6a33ecdb4db45fdcdb0976a1078fe91dc4fdffe15fa1fbd5dd41574ef3b266fbcdd5fe9c62b75a4f1f09bbfdf7cfc11e446c72095522a05c0d55297e7f25caeb1a44fbffb699f7a74f461f350565656b69d1d49224acef9949c2f32c1dfb5f9ddfd2dc4172f998e138b929d7b724f556195cb774d27c174ea94c9abd790a9da5a6d67aded42a6aeebbaceab40e0b24ae5f96595cafdb72acff6277fa2b9abab0935a180c061065e6ef57cd5b1ec20fbd811f9e87fd51632b932657265ef0dbc9bd3dd5f83a4a4eab6b35e95e55a432657c7e4651ae62edc8086b5d65a67df57c8649fd2f176d1eda2d945f3e70e272244a6d845f3c52eba2d4a521c60d954c77a4326b28cb542a6f91fed495f6553f642268c611a78b9fe06ae90a96ce5f965abfe075210044170f67d55a6db45f3eb785d3d5dd4abedbceb7ad9ef463b36d9d90aa2fb59bfacee5f7d94596fe6796fbdd1d301e6fa5fd97dd6ba7f6f00cc752c7d646fec61cb2e22dbaf20c904464c60c4cc6753a13ad703c92cee0c8a6ad5f0725de1523f95eb886cfffbc04e2cbb4ceb5e8cc3257f110469080e21315f200c949d503108d31254a6072c7dc01a31c8400098c173fe80011002081c497083c01f2bee0c2c62d2028ca1505d80af2b2e0df4594208fcb1ba2f2ac8a508c3cabea17693bb4daf81227f3f412e65e4995d8422bb8501c2a8e10e81a391076b2488a08c36776c23628032d0d0c0e4f9830f0df690ab28037c193101f89a7329005f4441821318416b2036732b00814e98018e502a0d30a604631a68c0f0e4bbf7624c3ff744ea599ba32d064cbf8e7716f9be3e53064deef733b4cc1a5af5d42994bbbbbbbbdbaaedbccebbaddb727dedfac0b0c1b06160f7bfacbcb2e47e18166a25f763ba25b7546e0a45c97e3b78ec60e1b1439d705762be9011f3a50c556e467132a744994ea615a8f944d7b8d51a3b571530ba91dbc8f64d4add6bb593bad76a6d576dd779debd2defb65a2ed7f78114a4217dc1422a9697698214d3580ca43db77f808226157d2849451915cbcaf403524cc99e998ee58fec2508798e948a922dd545fd00b0f37ddd1d02568feeced1362322722604b6abf97cf860b229a35e6de7c16ec8d4e59a296d81d8152e4d593b0c0683c1e8173279f97611a51804837b34c24511b8e704bfba440302d39e4d57ce09da49e57e1755b84330988e3fd7ebeccf87e5a573767dcdafffb6aeb219513b4b4ceb76141d5c7a854c14e4db4530188ef524dd303ab3e4534626fbf901ea8080802c50753a811a7c953f2e16632113edb960a593d24cdf710f9df3026bedacfe807b94d129ebdad3a28f2cfca13f3fb3ad0f7ec95a2c7150900705d1a0290b976e5072565f341d359741327dcf6553fe01a2b114349be1162d5cb8304ab2bd78e174b68080828292966636ea6ed1e2024af6bb70d1399a50184cc712a3a93ac3a5cb968e329faeecf4a0ca418388521c2d084cc77262e51e61844b43d7eb6c1df2a1a1a1213afb05bb75ef0a6a410b975c98c1d4826d678d7e1fb0c599acc58f0b1a9d97923f608b25d8e2479b4d5f25907c5d7451b824639ab41083996b0ba1165fb408030fd1a139645f80b410078ca13934d415068611e2f0a17069285c7a1c430ca6a31311115122a249d4b1572902511c63c42022922163820928a080246bb5bee0186312b5bfca20b97e31c018b2d91c693f603ac6087178be4b4421130dafd1da9c205c825130963dc03cdfc57296299d94fadb4ad2c0603a9660ad8219e1128d7009174d0f8697280899687ef00b2e697830b3b27b394790e748d28448f2050c9224875c90640b926c5dafb364753a2b7819c007da44e97f301cad0f70b4f2157f0e5e371f7f84e076610bf07c29d945546c20b20d17e0e51e938e0cc1e52e5c00cd83993fd0ef48ae70d90b1710947b842d7900825c7220e320ca1edcc8edba1e7d951cc8f6bbf77ed7bb5f77ef772ff9ea2fac0ed2eeec0c5baf20b36952aa837badee62e501cf07a9530a7a584326b1b38738ec8fb38bfa3d5c0a32d270f8e4fece3eb9c5ce176c0bea58bb68c8074c9eefac319f29969b92583efa1d1bbf0478be8c92734ef966eefad59f3506da42c9c7920536050a3f5152893abd42c0eae971e555604d8bc7999ba36d463e44a46cf643c90e3f99edc207dfdd43fb60e8c1c42eefe74b47bf7b51592c168bbde2e09252512a7a064d197ae4f3f5956eec4b5cce7a9294daff8e65526b2c6dde58923de36b8c8d78f411cb1bb493592e5bb0d8b0ceb1c1e5cce5c490f9f242887623e985d0c57836fb27499b2d2997f4c9472795054be31b6d7039a5a4b874d9429f485b92918d469faae854ddb00235a563495574a6a8aaaa5203859b2ac755c1a93fa1b064b1224473f1a4914d88e6a2c5a4222715cd55d191139c0aa8642b594b8a9a444aa21100000000c316003028180e8ac4a22489e24cf50314000d7090425e5030164963811c48311407310cc43080106008300818659ca22439f006d235f4e42018eb63110d50aee9340d7f8161cfa29a8831cf1f0fd1bcaba05e1849d4a0a3861c6cdfcb708a891e6d65a05aec2e84ca34c93d9dde9c42ea39d237cb018b75a14debed289c44b19780e090b50e7ca46164d5fe29642fc302c8d86498673fa13c08761fc57d40548f93de19828a3617b2477a7349a10242798c5e20a795394aeb85eea2eb1e074e0b012b8eeb7387bee2dd8eca378670ff03b3817c7008854871f932ec3f11492d0dbe12988f5d782fbb644239cfebeb9bf9d2fc85f3a83ab306f5d31e2f7c9d8818ee152dadbd87e476600397cf40aa5a6be7903c946cceb5a71e3a457833d06909fd0025cab195e8e48f7c1376d82a8f54f010787c73c53abb434984780d3c901b819a9db633229bb178e52b05dafbe815c4ee8a350d6517e9189a6de57768b396f5ffcf09b07d667005a213b986cae91341f3342748272f90237fcf9950cbafd67bc0f3e63224bea686a78051b28f0316675bc16c4c66d864314a809d9f53604fa81befa79a189c6c4be07e78b8cf5969fcfc0ca37550743d5a1bdd4494da23214f9f82d0aba024bce2afa37223fbf16cb6e90c6607e78f4d604eb6f6f55cd7f06e9a615420dd9cf3517afd7a9b90e564427174e4b84b122d140bb1f8628d2a9ba0a6d354ca2e7ef3cd95894938623cb081255be68b4b93dc9511a940e4eac17dc92087d8fc9b75f31dc05bb87b7702f6e74c505dd8d2b58a186f662a90f34f54081cc82a643425059d567ee09c35dc3cc8d712c2cb0bcd12323cdeb48f68e3c576242a0df9bf1721fc29da2ff46c1f362af06a4371e7bc116606fb72e640f63d4d5038bd722e58faa11d95e4607e614c26ab90e0f0a90bf362d6100f1258bcfb041f45653a28f6b8e7cb5ff71c211f90208b22ffcf5853e7ea945ecfca1778c29f0e702d58f0735a1f885d21097d0327da6ccde95548d51a557b8a1cbfba81f6397b4a34b4ae8b71a01c0cfec48f8c10116b24eea14751334fc3e26761e0d900cc817888205a689b65be607a5cbed142dddfae21a39aee34288f82f534887b8a046d55f8bd44dac5620d72ffe76719f468385d8bc69e51077f1e1a6db09036fe1883a483c6f1413db043a9907e1f76bc8f45d171a28dc8d8a5a7261c3cecb707f753495719ed5dd5a714847f71e5deef91012b12adf214fdeba8cbf09a4cccbaf47a918247cb47b3aee35ad5ede689294c582b8e7c9f57bc496fc8d0c277e87bb512c1727771785f73d5e7e25419656641ea970500c7e3e1566485892f86f3ba7b039231c98324676891eec97b4d6f45b416ef8fdf3581b5a47d91951a027e8ea0db6e8df091c242d1eacb0c49e9e4865c9bcc10b3a44196f0bee22f7b9df62245190af2d1c6bdb02690f849622d69407bbf51ca95160d03d6e91660bcc2a9a95693decf7de15d4c3e70d9b8facd39f49a5636f9f5d3e6a2a5383c403412b1548c5b476151bd2221a77670d48aa1c52b42ce2bf0f744b0df0bfafbc24a46d4ac6bc48583d46f3715facb207a1e41c829e9b7a29bffcd1f6eb32b8a4a594fe08dde2779282662df42e8e42424d1dad670803b9f73286b8a2b7edfe20188763f8f0e63d74ee8e8e3d585d5201c4d1abe6cca52de773fcba76413f8b6f07af4aa42f42fb450ea6b3105988b1ec23376b2f52b8780ffb74384b20a0493bfb81edde2040f35e5feab3c648f30a1f3122d62dfd7a425a5924c83f89c40be1c4c8166ba486979809153f11a662d4498821f82b9682fa9d5c285ba4f1013bf81a4d7d0e00f9a60e457af2ca44f9909cd28e7eb8900d84c10777ba55702605bff7cd60234675ff846fdcfabeebf6370788209cc7a581a1b17659857f482cf2665645f4273244ebe81be38209e86fc05e3ff9d831235e0810e7a0319acef41f71f12a0432147e00eaf04ef3799aef4d6ee3387a2b6713067b15a914070f425b3df5c54b0b7c2c4a8e47d84adfaca66f60c89def8c0df2b87d7337ccf5648e87084cdd955edb1b4a5b8e2a302c6ccbd5206872af1affc491aae85f5906ff6f0a5845cb1998f4a9a2283b05dfa7ce2ec5dc6a29f672a58e4e6c2ae4aa2c0d41fc6d1c9f3a5ec5c3a0ec991d82e37ba3dfd12f312534e2039c527f5b082ab52c3f7f8ca8c9fdef8080bbfcdcd1cdf9bd377c8b91284ee3364142bed14a0b4e0cc23d5b75dd268665d74b51f8a74e034bce827f3794342b89370e56ce1127bf3b99822d76e31904f764bdc4dbf0c3bc81be669f6115a6406b74ca88f8ad4d748bc777de62f9fa5fa82cc309914da11537e374087be55cc35ea3ac7148ffda82de4b2e6bc200a322d0d6be9315b424e52126737f22c58a6df52efdb25a4933e9fbf46a57b718e3a97cc2b2a211b41aa20601eaa47eb943e77bb9c0d89db8ee47cd2027e8304454a2d7fd90670cf338dd93836d2d035099956e07a5bd985e3f1ff08b6b0d6026cc5901977c5348e06d9310ebc47f6018e4be32e4c425a1b545b657dd6827353fa39db29c1aa7eeda3ab2d789a5d5aa17be8082990b32435185ca3e0fc9a7aaaa112833ef9607c97e7326ec83c42b3efafbc58e25d56fee2dd98cf884364b473bd956baae15eaf55a4f7e3dbae18ba0719a3c1245eae0943f1351d604b63b612dc36ec436671eba8782af3868e71debb98f6a7fd3d3b51a130a0b0208c643fde81a54f7ebfe9a4505ec4a2a8a68b59558127bc550668ef30ef02041be9b5b6ecf665226e460b48a8df7e135232e237b91243f4f3eeaf8ab6c3468646d54c28f13fc769bf9da78d32e7465b88b4fa8f3f700fbf8003274674cbde63d5ff3fcfc069365adeda0a208a69a4e6c1adbd801163d46b067f41d897d5978ab7107b16a9ae9a049a20ddbf2cf829f211bdd876a07ea2893b6450aa18b75d801d4e6a2100a171706ca6e660c51ccad8a438e366148cedb06f8fb89cd66940153d0e6df161605c280d5ba55aae1e567498b0775a8b5185d1a9474b4cd0feefda6bcfc3a076098a31d2f825a16b635663795a01a6498bcd1d752026fb38fa8b454ba57995cd8f30f736ba1495030c56fc129863727d0c3713521c1501222687c47e6342cee7e8c26b227e7593e6130ef39eeaf5b5743563e7c19d959daab8c28c76365f10015f547974012de4de32909b4777c39353a0d6ce3b7aa41571b2616009090ccfa9588a0ecc4c6330259fc05c33c8cb230cfa1e729447a79c2c8ca5e528cc76aee11c29a74a8c4a96a898488fb3b0ed36b37ea431b04a057e82361e5b0558768d716c8da76cc580c1f5d4e01c4f0f21a78895182d58ad270144d1c824335993fcaae33bbda38e07d1da15484a8cafaa779abd94bde040824afb141338882a28e51c190eabd36c7988d9f72efcaa77b82ed368bc499dedbac06902d3af1d365859724bd9ff6660e54adcbfa8db7148c498697e6c62581cf830cf1a6326c5bd3ce32b73759321f3113b3933ae796cfb3b3da644bbe64871f16e68438ba7e635ce8c65c381705be7760a16da6ddb85d34d6e529632bc6a0aed9cfce162fb77afb18fd5271f061199858ca10336eea4ef52a108ac84863b497564040be246b304958a4cb5e68a54cf3a7cb7942a824d0d571e610d7b8562774c1bb90f0ac5cff493077a6242a498ccdf2182ae9dd64746076a07364806b4c3c6650b6294e4563e0d7541f5708d6416af21319d920aa8906a96ea895a8f4e1368123331037e71eca2e94ba8158d32de5c6d3fa3f6b52f390e04429094182047986c1977fb55b45435894c0e6156c13d94ce3cee803ca838b71fcd71a83e93093c3ac0f207440977b62ab62179041b470f91b4108431f1116bd403f8d967e363874438d9a219e96d84a139d146344855cbf1032153c29caf8ecb994ff4e53a347d16dba473e88d59936a69d69dd13eaf3067c4ec0c0eaceb3ff9908e7baa2794522921ee06280034cb5ccbe34d249c7c3612a5beb1df8a8624b89dcc6b07f394cf9097fe32bc7df3607abdd8c9c3c4bf80b7881bf430bc3b561cf07fe26014ee96d98053bcddb1df79c4d75c7a353f1c94c5c5537906cfaec1bfc2efb147ac407747813873f8e1382e9acf3587f1411a4ba00f02f466cbdf9d6e4d53c2cadabadfc5cd19e7f7540ebf4b3c36488b4ba07624ca774ed7edc3b7d186efc7063a44dcaf5ef846c30c87e9cfd49be0a38f10c6e9cb59f8401b5d132bad5f61978e65a55fece5c8346318c67dd364132e4966fccc9bdf5456ccf5b4f5e3b91e0161e0f282634660ee293c76e8c1a561cb4905a52c4e314531fc772eb056eacf3fd064315ef4d73e1581e0b78b7c2e518003b05de86af9587b746c614e2b836acbd4c17b52f00d7faf55a428a299f937007e4f50dd15de571cc601f842eb9e5b0a9908111314183d8b5ad069ac2f9d28465620d1e098361360f0ff282adb886c4a003fb110edbab70ae0c517687381aa1606d5acbfc88b1fa6c709ae7b740f33c0873bc7b7f962cd6979075286f2e4dd023c92886b4a0456c461ddaadbe545dc2707dfec37f965ae8c396ade30ebbf227e6f87b58bbf5c59ba9e2375552a5a58fbb480a927cf532f04d44e63d273afcc1b3f5adc65d5316bb3dfbeaaa57b8e60dadc23239549c8f25b0fc191cdad6141e72cbb9eada00a91349d7f9932b78e34db3da00f8593fb2e0c7626ba71a80b2497ae139028ab0e2fb97c0a0216e5f6c6c9a2d4bc36392cbb5389d7c6fa37cf66710524f0946d71cfe03ca328632ea35f47a6ff9914959d1b2d0d99d2f0b55cd7e6f4c82ee7b87f14c0bea9388726af93ad52bfabcd13a5b117dfa8011c5062f41ddb3b28c57263b4588b8d1e10ad7ecc98ea25e62aecbd4732952f8e7cb668fdea9fc50251fa5b6793e8cde9d9b84ecf523944493e068b09498e2cc86ff73307df9d5a16dd88db88ffed21d071d9cd7af0d6198ed4ce320bc0582d1e452682405e6368a6429af5064827dac63f649d221b88e2d3e8aba867cd5c4dc6cc1a221251cea2ff846f3601538bd4bd24dff3f3e9b6434e93459bdf4e2e3a516066f221cb6c12998f911c7cb79b4fbfcb45f04990aa545b22a1b40517735d023c20bb0f3a745dbfb2905332ed12e38fe10fed68aa83beffa98343378028c9324c90f4977c10342c23f46759b8996cfe9441aa7741236972951c1a90cf7cbab1fcbf56f02195d0de4f1b451e36de5031f1dc665db007bed532fff4bca1ed199a56d174d67e07824200f42f1c9a96225cee0fe4dd2786d64fb70407ca82bc49fa0c5cd3530ab0705f0ea18972681ebc8998984b900c272647fce1ab2f043e159aaf47b0e9bdbe0d0f246f5ee0e104032af451faae3879dc572346d54ea053a808a919518279aae0f873ccd4231128cba21be6449165bc6e355d310944b3e763a3e2740e7c467d10a60c4a5f86351274171aaee934efec61b6d1f4d39bc97a363b230df64c031fe357eb7e7aa1d4455912945fca038de943f8ce29775b8b48adb30bb60ab5830d770d08d9e375260513f63fb5b7a7626d87afe9be2496e939954e32ecb098d10c926bb02fdb3c8098af60bc883b8ffcb751783a557b7763b4b4b2694e5dd67072fef52102c50724de77991f6c92c87e90ceba5c30bb38ba652c24f3eb7a8debd193b10f4960bc0c3503d8414ff77bc2b518c3224a55b03698da260710e8f9a84fcadfd681bb6019d7ff02097fd23e3b7842ba0334f0e045c8b62d6900088392632267336bd203a2253f4e16b8b1e7ff79f775982c910da9eb0a272eb34149e8a89ba9177328ec6a3656834746222e4b2e668348a8dea5c0daf96e9499217a469a422e90ab1d37caa2975a1b04a11df5c8fb801368fada3b70d2a768d31914a1489be799ba3b11243d06eea310fc43354fb37540f9dbbabacdf2fbe78fdcba568486f9130b3cd8fd5eb6eb59ddca3d49cfce3ed38c13e2f5eb1a91812d40015967c2d7ade9edde1a00f1f40e7e07000a827d860b38e211fe735be6bf018c37c027c00702ea0217e8f7eba78f87222640d0f90a30a14fd7d546b0e6c6c781f08cafb6b85defb75fac0f77cadd5c6e6e008820e1f7c2d6f9e86f1d0b5408cd31d477f77ec936a1fd809950d9eff28361531d6756f2a7844e4c208078fe90204037c1b747182d7699a5afe879e0aea255255d76519d0ad0a70782c802614cbe66e978696bb7bbf7eeefc379a8ba51eef7a1b2f4aedbf074956eef11cab7fc610c11a1eeeb4edff13b443d988e64a041e8a14bc3d9ecad9ec6205074520678417e8581501efb0cb73a8a2ceb1c33e137c4b1027caae9d8ca1f93a381a0e3fefee9b25b51150446dda7db771cbeb514efb8c516583f231cd75b0ff3a6a7e51c2fa53125b0b15a3bc268de007c761855cfb70a92c084557d3fed60c3b43bdf861998bf6d56012b46b7d69e7da8e083e0995ae99f5efc49a08628cceece5bd0be064ca667ef9fbc910993fc76b68335a7695881b1db41b65e765914144869aed2870a874591745cd8497248abfe4c575f18e5a3d0a3a67bc105d316e3b65a06f6bb311544803e5d3e98093e022dc30206364af4cd16b005b99fc89679dcac5bcc8b9bb0b900118b87ffc7620319653b8658960ef38c64bcc5b6d20cbe27a138349435cb2cc5eb40dfb53ad1d0dbe56ac0d0b81f1f6b542a784305731760864ddca1afaad34344d2c4286ad5c4365f869a72ba0351b81035d4bb17738654241a9f6d664d08f2db7d33da1ba2ac9b1a2eb6474bddf62755bbc8b3bd76e28120512ec1c67c26d6b057591382ceace1eaac1393e07c0f9235940935c953110ba1829a88831bbf573e427eedc209d1517a36000004003b87914a12954c47e93cf415ea8ad135eb738ae8069565832e3a7332eec5c41989e26c56e6fc999338c23cd49266a4496cb2721d470232610571806253d8e89c5123014932e3d8c40d49b9f48523834493506634f3c80d5f660ac60fb938c702c8d9ec39b133928a3fb65abdb4595d948e8efed35c4524e3e10a72455e4481704854d1e071c648551392525620c67ddb0bf587fc7d4195021aee2196dc77f4618b96368814653e0e9aeb9c5633ea0722c9814a36dacb854b38c1b871b17ea6b9f780b4e172afd257a4f85e534656a6b64d5a0770d25aa0f4fd180163bb34f0c63528651b0b62e4e603b5c45b4a3f4e8b552a6ceb1b6ba58f24e9634afa2687ef64fa48ad47f0678e1acfd33c1d61de8a89ee3ad2981826a9395149f56f67d20a465a70d5994aa5ae7f4f8a27a5a2bd7f6b0f23a714aff649fe993710a0fb999bd10fb75b3caf368ad6dc7dc186be5c148aeb2248d9b1a7b8f23928f0a2e763e456b3f80936eca387fb646fe0729ceab8901b00628d8569b05339484dd2ab287959a3c95b59448f57341292fac3342e02bca444f72b99d5a6a03fd58958d647d4ee9f2c77a8e70b85fc5182b4d6fcf1d037b1da81e2326315ea3a25c0872e5b490d81422d03f849aa29686f4218afcd1f5cbb5920bb645f60d34e9df122708602e530a8da428ca00f9b535d8a89026359c7a38304fc2570c0e7b121423974553e02391c8b54241505155802bd326f939acf821faa1ca8e98929909c704d715aad6d97f0f1800b9936a51413ff1fecc50864a58f5ecc0c2c22f8c372f42707c3bd3c22e476f7affac852b8398cdb38104da26caa18137ab47389925931a04b353822862f595172ad2d6e3dc3a3f03364140a506d9bb4ab9aeb21f734e1e6cf87442b770ec1d6613894892fc07421409e8e012e3ee23bccfc83d94c5fc0bb5f4ca12af5bf5a7518a23a2ede4041abaf1cc0da1c5a98b3db68ee368a86a96cd45d1181c3fb0dc833f127cc91fe3402f60ac02a16828d0faa937cc9ef1d758088cb16e99eb3932346d53d230c92889fb4b8810862717ff7169ebaf08d46c4dd6943ce5571a866f1f3e61c6e7f21a61a3e170acf439c992ade2db92ed7e6620c5fbd11064551e13a855771f4f98494d8f9e880a02fbdc0a8ff51447b32d454c42ccb26ca90ba7f557a02b8609cf00f673572bde1e7912333835f33544597e89b159c9cab85f0bc022a10c97d30edfae61bcdbb3855e374f786532e4803d5b2e64c2b521f3240cf9a20778d3265efd7f9ec6726cea3170b78c53602ccad0c08a55f77f15111bc625ea2c221f447c7bdbfd303721fc5865d5958026b20cba392d3aebfbb3a0ab416bb579c85c064b624a049a889842f8081b9ebef572751d0a9b2286bdc84c42dc3a48d2d1a32de7d7007f8e953ab534d97028c0aa5780d98449109209409520d0f1bddf860727532dc22d1d5193cb5cc4b28b84804c62588350a9315737adb855c09e7b8938f962cf6764be0ca36a64e8a2a03a0522003d550a8c7d6579fbdce58d90e2a702f47eed847a4e051a9ce5f152536906006a13ac5b692a7b0ee260456a79446f80748874feba1900fed3f4ea2d1cf1a1ea074466a0dc8533c90bdbf2af482b73a840a726cca7437f10a10fcc1bb29f2f7b88a4fa047f739dce1e1064a8f9a1a14c52c62bf5502897986ccf2b7916519c0b45bc27a315ff9e6ed040d33502109d65104b491ca0e397447ab3091ebf684507000ba2f5a00077d5461c7b00e9a21b6a8420099fda630afb240d34079f2f6ae7d0854c36e2d00b6b6aa84bf381dc31d3ff230a8e6a20061b11cd8e9dd757d21560ba5f4ac56d095b132589b83ba19c200d36f7480dc4ca1f68a4249302c06bda4dc68502552038e9e0461fd86aa8a24de716363e1961e8aeddbd8e62d3ce14df49128485e1d493255821681011837dc762a44d2e47f55ee19f22f569a7525d102f19ec3dee9f15e328e7c2ced0f6c753162227e0034933a7fa5051c8362ca196b1fb9c0a16ca2f2be5d17114174cfde8d97065c86478883db8c803d44190b8a11b505f432071a60b4d35403ec245f255a0201c74165fa077bcd535b14a45a9cddd4b610e12801698a611e29d92c921c979520d9fa26d352a24a0d4c2f319f390b5336a2f234cc570e2475c4fdc570566613ab9297c848573fbaa9012148c2c5583aeab27204de3967365a103580b546d8d4c86a5421cfb180bed898da6321741712b0e5ff4c4d82bb68bf95339ab6230a78780f6b50c8508be9a4b69fd09d1c8469bb569d1c71f19a5d54fe9649f585e3b4d492ec3533a58ac7e2ff46155aedfcebb002a310775322ffd5a2a0202a9300f55c243f17ac25d814ff251d6359c5de31d82a0ddfdf0ae6f0a3d3154bc11d7f1351470a81905ff477a084f9780e6d8e9360bbbd89c26182c0ed0973d14698d4b381188312fa005327b1933fbfc2b943af0a058bda70065452b67ed56cb819046114988758dbc437a6c77c4386fef6e5f016760ce1bf7cf97f066edf444d8620e0feb056222130eedc381d0543639cb01054da438790f41684aa14eff21816ded6be5edc6f3b8e3b162fd1949f90d3edc357333f48baf7923418df83f78b0d8398da28f1352ce10f7ef2c1602080c2e7248aa293bcb87d679917e72ee0957e2e09feb786f6aadd6c043451ea5c0d0bce3becbf569c5714e89e8f1e6b841da7454e71af3dbf6088b43cdc765015b6ac5372391b0e0538e4aabcb054cc2510a98eddb9d840f57aca960470198fe31cdcf7d2a39647b58820f3e06ac526a1e2aae91933f79b4f7df9a0731ba71bc9d09aaca51070bc644e77cd97fdc5bad04d4d87a4d482064bde52ca4b3edc60f9dfd53cb6517a975b69da868bac43e46f8c8682da70be1e43bb1d160e52f97c279dc2cbfb430dcc6dd73585b74f692148e3c95da483271db92998bdc01a9cf0cea6531b3de089ff729adcd268030d22bfd9a697cc6338853ad3ae442a2b4ecacc6333f05dcc50ba521962d87069f7628a04fd8d90f3bf618d47d4079d410f60dbbab1f51784c58906d635090805b30ac1b491430ac91c7815c649d51f0bf840d34a4910cef10168732d644f8c8825fc9809cae1cdc4c61ece428424bfbd2ddb36a108ce5bde10cdaea9b495bf3a0f70d54497c96cab80234920287fc822acb426a917237451dd6d74794f00698393b0f82132bd3e7a46d25065c27f56caeef2eed667a51b83c1f1b33f5072067c25bd75abc274eb60e7665f21184d91ee076c6ed067fc020c09e08b79d6f3da97ef13441004508d431a0c246c414419030aa2fc780cacc9f687cc48cca51d86b6eaa02077763ef286c5f9dd036f5096f50c28b5f4b560e5469d9fcb13c20f0e113048dbb727b4968316f62de1159b87eee7fb580e29771891872f7cffc30277ac266463c5b215ace0c5925887782b7b6bbe03c212e10a0cde9ee1d420f1c9559cfda6489cb66d9e9a482dc5b68216a945a145d0566821b515b1b63915e4cd8dd5e8b54d1a732b25137e9d127e50c989d5f9deaa539272608a0359fa5f5afb82a1bdf4de1532c749d23012cc3336df63f63baa579fba82f852a3e5092cae94752a8892620fe8f91a06c394c6fa28f0a6a47622a2d59609c9782e5d216356fc5ec410c041038cc0c2415903b78a99b06c848b4db32ea64318a8501681ab2282a860783b1623d2d65becf7db56f77d3a09a011581020abc05531139d4145cb6f437608801b171ee7b159f338dcda7d796481c505a78a88130768cf76f7f1951d9f8a2a619c9d715d7a953ddad9927c933f0b855a3bbf6096072f24cd643e4d6c9e84ea38880ea513b8705cd0423008e9c831aa4dea9bff9d1b0ce4fba863a147bd32ba48c975916fc31f75219f0a78c9822d8f72c52f44838b10c74cb276ce16ee365bacbfc9e3d84c26d99f312c445ddc2ff6ae51d044e6ddc69580ac247f550bde4172cbc0b0addc2910360c832475b8179b8aafda44e99fef22a47050c36e7566f0fb91c7532e7372a628802eb3b52d3a0f8445dfc48062c2bedee05825fed81e61c9227c648b00461a3705cf833510af2a8be1064465d6c4b91db9d66dde4988acc53cf5144e14afd5193bf1fcba6ce7a298480773967b6fb78157a1c64600fef08524698162e3ee7bd472631c71f08a36ac04ae8a3cc1737443b35637f7850a5a63dc42754834a1612b42a00fa5436b159268452d09e17ecf50364d6c3f2c0aacf5bafc02d5a71b44fe822e9abc0f41a40e7268a05d215ae31fac9e6788c4a12513de736272d3c97c296a883749c6ecdde3664fa0ab722024e1a61eab9dbbb09f6d1311a10ac64ff446c5c7b9aaaafdd6b885ca5b5279b6cf3597fbe09856582368a96d0894a185745522d2c5c172e583c0a3eecb4bab49c5542a940783eefd51e89d6a15950644bd23a0c9aa18b438650e582bda18eb3dfb9173dd13f34206ba59b0fb55905152815f94cab5b7cd9834667883ad4922bcc091f5c09b87f7a565f9f2ab7840504a3886de817e9d38ea9bd67f7681cf1db429e6e094d80056f2fb39200e3bf1638ef29a54ca672ed73f2af5356df709d8e914b5c7c7300743cf76e4b47330279dbb4fcaf11ca1fa89769347814e51555a48950b215bbe86f046a18bd6b1c93a41840199b01b3484ec882f5fab54ea0b370527922f56c0d879b71145e5d7f138ff434512be1e4f3c7d3b5713430ab186d432157378b7d63ae48e513497331efb10e9fa1a75901a3ce08777d631d8239278b27bd215108c225ac80aaddcb75f46cd48f43e606f1a4089c06d69a050d4cf13d97d3edd0b38a85fc002dc045c6a1b4738f1d1a037f5db1acbc410f5a6c2c0650510507f560c9174ce0b72848d22408dadbdd68731bf4ed7bb916e0c88e1e0364cb65b41b5400a606cec5d617dd664631f41ebb152da2fe3323d6e9ee3e42f4cab6bb342e21223856d38a41370f76d20564335544b4bda21a2204ea40d52b1145959311302eccfa4b576eabb4e98690a48a8ce1bf38586e6d431dd2a6994a05e2c8c7dbbe3b4104ce0fd92f916265d3dc733fe85e0f0230bb85fb3252eec7e9f13b622fad64638856dd9e733384ed1297a44d91a355230ece99ef70befc208e43a091d37752d65e16578b61117f5d622b8cef2ea12707a828026078bcac7e4e230223839a3dba09a198711bc98569c4b869f5e736692fcc6f2eb77decb892542816ebc37a5b368ec0af38a7186053266a7075033e4942d95960d50926361149cee5ea33e341ec37d763bc075930d64664fa13602ad289da1697453d278a43b788c61b1a9da66437724486ccc1b4dfe3471cd4e6524a4044e57bce486ed46feeb86cb54bdcc91beb12c214f93924659f81bece4de2e77b1d245deecdbb8f7a88caf01284919866794e05fea807fcbd82303c3836906dd81c09eeda26c7f4dc685b637f3d4c38d35cd32f623080c13172e690bcac49094ae457a51c4df7fb3f3cf07a202868c79f2b01cce4f378c692d25a97eeef7b1ae1bb9df9ad01a7dc76431ed1fc69301e2ca2de0435ab9027553c351f2593768c88ee9998e1752e0f2bb97720a3a711523469bb43d6cf41ec9c4c4e0baf20900f1251c27bfb1f90b565ccf0029497d0796727eb81c25c971b840f6a703d2688ab3854d11ace3f075c510092e7bdb1433b5226725636bdc04cc710a220babacbc1aafba6d03728f078a373487987642d7483719f1809a71bb5a1bba42ef6d5a068e03bcc9c4ca002c02e0206c346ae0e6c384d175cec94c39142c5cbacba931c34fdb90d420e8695d48409701b28b69e1257a023d3c89804272aae3e09d439d908dd1f0f4cf5e8fb439212e6f2d3ece071a445b2218449fb310b752dfb227f2615ecb7446abf0fffb6e7a795bfa032a602da0cf6ec45891ec149346d0c01aaded28d9c087a15cea77a0d9294ab5e48c3f534540ce6714837be81c39701f583f1df2fa369f7111fb67d8f4c5c8d54dd94cfcad498f8e83e2fe2ab1ec857c4f5de33ae4da91a78aa24f305aea033b983b60723ae9bf7b55b0cdd7bd4fe6dd53525a3e353442648c580ba0ba6d46cd20138d0235b2732c0ea59bafafcd4cb28d822b3fadc96b6660c4f5c608608be89d7a5635e13ca3c1c6d91ebb7c2a1f0167a6da16397ec0cebffa0390bf70e48bad90b3fff6f2b42f713ad0bc4591134749986721ce686c8d5fb9e71318b5967e5dd4bce83b94d15ea048dd244cd3e0827be122b09692c0d8072a94f007b933b3c1265a7e61a039ba9fab3ef6481415b57640f76894078cdbeef21a8c068c28705554ea05eda9a0f8be71bacda64ec61c9c2c810f41a7a9696dd65f7accb0e22125cc6e6f168831731ae2110c2d4c5a823ae41e7e518cc84e4802ac93f149490c69f41545fe86afde41b6760450060d80d429a06c812b8285f02011f8378cd4a94d8e02479748e0718d2430182cce2aa7add2456594833453022df6f2736049e4a3cd8100e51a0707588f69da2fe0b109603b0b5b6e6dffe8949564ad1daefc6db6ee5c2978b6e4fd97bdd6868c8cc3d871b922036d478959e58289a11d7be2e0fd4e30fbe030302bc1f0791623a6c82c77ff0f4d16a91f61ff6068632bbbaf5520f0d5f533504cabf152bb9c222e19d5a72b62a688cfaa6e2d54057c2a00a5fe737960dc33726e5494c0cfb1f6553dfe59f29bb33a26aa5b7166701f2ccd0f15cff910fae4b1c755624f887213a2057bdb4ff8c0d6939865a4b7fbabaae3152ee58ede9cf6657dacc74bfc7e690fb5c26e912e5826d53011b3f1a35e7de8e8823c3f1a7546e6048ba49e20fe66e1cc5fc9f30a724841e491e7ebb45250b0529e11010ccbbe2b1537ae289f7555d911b09d23a34577c8fd12dfbce2be68c68a9b750d3d1571bc880907f5ca24602c655c746bc2a5d25bf733e63d47981bc9f5c438ad1e58af68239c67cd79bfb7a72d7cc64c9976146a780581130805aaf099b1d7b0f2e8e45ad58b5a835947e6dbc6c0f46cb3c30f6f3e954fa837ddd0d42a90e09893aa435d40e6c3c4701234f5f6c1436becb6826f2c0d760431744f49357a9091ec68649668fdf4c1b4556894e1f8a97981033a15c84cc5234c810eb0a5efe6f7253cab66dbd24849111069b2d88d5ebb906ae12dcb939a2457f2b2f3aaccfa2a0edd55366887cea29fc58e8c5fb34a5c9ff8f82e6b1ad079f30323e12ac30fdc4992add79c2566a250dc032ec3236427fb90d81b4365403b39e4a71ba26bb9c77aca7546e88199bf07ff0e24cb10efff8a286122b2bdae84016da93a0cf988105e3f57c13d9f90c0684984d58b16df506fb4ddffe1c5921056fe76cee46ba12962652f660504b6940422e578c3d389700c44e90020e1c9a990d90883ad7338023eddb53220fb8cb6fe1784cf3988e439897e1f065bad668f2f33a2cdeb1a3d54455e6d35ce2fe56338f1f01210cb06b41e5c9e3806237d30d2201c19b04d801ab291431a4cf51d6914fa77f8b2712da68ee6f0bb7bb898b720c6786b1fa6dfd9106d8834bf67c6a70d28132a7d707faf862905c79800a27d66793559efb612a6a4a13678d5667a9dfa0cd06ae213e3228037dbec4d9b1a2daeb7aca71478c07efb19fe0607588188f8c4db6688294c5184a96106a8ffca6f944e029b28219aad65b2b431245623baa2c1f83e9cf55cdd3d01eb483f1c16e4c21ad68a9b76f12ae904bfb23d14691a9457e9a6459b25a3e8cd5a12676b5619b7e8b6ab24d59805963a0328387d653765239b861115225fb21d12d156c70285fddb53916eb64912b8e80c6039f195dd348b6c1a0d54887ca96d4844371d0b2c0cf451d0fbc89893f80f0dd7b9eac1a8443818bd72ad9d6fec40644c0733d41791177dea2c1a15dff0e67159c8f3870304191b3a5b90ecff9fa071222b7121d29cf9c853fc0dd01fe14e4a89005d945d3dfb81a3dad0980addb0cb18a49ed538da782457c7fb7f52b03963a1cf6ed3a596041b449acd84107dde660a56c014b738fc54456a7e6487e2d5f955605bbbbb259f13d5a14d4fdc61c04276b714710ca8ddb0644de72f71dfc4c3f8f5413826f5a860cb7bde0b73f866bae86c39d35870ec67c51c43b7fddb165cda23cb93bcf182800a067463cb6cc6406bda7877eb5604ab0656aad275f3906c83fdfa4303b817a02f06506aa9df72a49a3dfca13ba3ac8a0a779bf95b8d0268f21ac262f88f93c8501d5b4b7348aa30e41d9c169b370ec1929117ca3044af96d1ca2a2ee3c8f9810143bf7eb8a2540b44811ef3027d72e263546e2ee2180a5d88b021258b31acf86af95101ed60dff1b0941dd1a991c855a11d6d7a77489831217ae4cade2bac440e4d2fdfec0c5f9634f99d86f684fdb276b2d9bc8a0f55ab7ae8fbb91d63db9fe2782ff669a1a28ac5a18c60c875e3ddcfb46bde2c31aa7f46f264e151270766000f2d0710d6e2203c5a69b624557d6797300f7da3f0d372667e201206f2627c96e99cbd55db0fcaa3f89a2d30d375a70fa00822674351a6b34574038e10f956c5c2c8d2ac6cfff6a459a5f08867a2d0a20bd9efbb042a77e9d0ba248cc21ef690a4a0601325c9f00c1d89ca52c9d95c0c3c057d35992c91d69ad0d66addf42a60a5a8d44be719e3db5e4b7f22ef72154262687dc7fd1bc28bb9111329a396a7227320e7a50af6169416f059dc2608dd2a19d34e17ff0b55a821069b6856392a4529aa4a91fbf1aa60e8f62e4ed6883a254a0e1907004fbff2d3cdf61c14ba666ae3d3fc0458d9d0bd06d1953ccc799dbce98b3095f74ce320e66eb5e8a789296a57af7d458193b1ff821849bb7c0f4135ff4c4e111853211de68285dbc29c881d846bd9bfa182ef19658fccd30107da42b2ac0e82fe0895e976e04089eadbc86e7f7ecda79684f9c5ca51d1e4ab08ef9fe8091ec9eebf9b9f6b45f75f2304ecd595d4ecb636d9fdfad62197b189135e4370ffec550a3db1923f5aaf081c4bc89293ba6ade67ef59c713e0b667c6949dedc0076e8860f5af92e822ce974bf0a5269b9ed8022aa996c5aa0b846901d26a7c7246bed4da1b456759d9d384a96c5c457e8a0ff4970adea44cf2675740dec1d138166b36d7790f6b123c8865d199a6231d8cb7e8fe785a0ec340ea06ca873662863d1d8827e3d04c3f086931c15be6747c89c26664faa7018e7ee9694e40570c85661742b19d2e3c6827b4c827dfa518e80854cca6bc4e31e0b23d12694548ac71d6ee81ec6039b438e57fb3faad22ae4c38dbb58b872a35c0aec4255636f3a96c975f4bc18b07b83edda5dbaeb71d1be6e7c85f50e130b29c474367e0fb95ccf01e45cc9f4c81ffc5cd939a9f6befff658ecc8bb8c45988f935ed5b8bbb60567de857d9419f622c9a3eea06a028b19f75c6d7e367c6e36d448949f9f35eb619b30a015670e5927bf99303a6057c3de22cec8dabb52184c49c3f6159a3f6a067f4e0203aab2f6633b4494390cd8076695986398479cd0cc172a6d3f8ec3a97f8be2d01e940c47d238e21af5da53a752e448f994515a03996798d7a77f8e39c2ed1ffe9c12414fb88438a797dd6bc8df8b6e112ec792f0a3b2fceb2fdc59ac42db9588d10f51b2f0f510a65fe8c1c198593c86947393c7ce68340ef8120e2fe696b5f871a844af54004737fc420f84f5f6041f248cc8000f36ed1afb811e9a75ea823d84712d13e40a0abda53cec532ed77699d660ad90309e41e326cac44814e89accb88e7332552b2bed322dbadf7b651163ade2431d0f07ebf750c62551c60a2aca4ac94897326c30fd42fa3c9a362a46cfabf06237398fb14b9f7af9cd0037bfe8c1f51c818feec37dd3c0f5c1353d1891580a362dbd9bb489833329b68047ba0a6d8c0b6cb7cceeb7b494ce20992329f9272b3e5f665702e74b306592198282cff24d8a8e6aa8cf57c507484f9bb1d390c1dca12d2855c071272309288ad138c8f3c82e618a1b32e6565db5528c5e1ca594695dc596cf0397badde8684ed988de094880ff53931cfba8309d8292cdb5be841f3de82e9eff737c14c2cf3ac5f1fec80fdf9bd231586d99d755aee2c6292b865c6e27f9b648c4136f2e673061720db82dcccb1cb2451d2817ce7395b677ccc5632fd183b433f880cab5ca5a49edf0e0881c58132a1d1ae4232d23386ca5c3830c5da01a3c2609ea41f19e9933cc95becd56b7cecc4308608c4e2989d6b1a52cf9248962be64c6ed8b7e7ffe982fc733b54f92416b81a1ffb5020d01b085a391ed61ca36583fa85cd546dc8d1fc4b1ab3e107583a854dac281b990b046df140b35b23263cb1a6271232f84ed6c16cf5e1cc7d58d978eeb0c2a2fc8a6e0cb1c01f9e447eb5cca109830542ca53fff10c32e8c24d10c7af28830bbd4fecd9e04fb5a5279b3169445e2c7ce095780cae16257171215fa64fb153e9fe24e8efd95050247cfc265a705bf337fb152a50d1ce7ac4c988ac36393b89298cd59aa703c936a881a29862c2e1bddc251b9bb02a3a2744ac1b9e2532cd596856dc8f6a45521f50047e3cabbda5bcc8ef304a82c7c78742e56a218c7a85c0a310ad0e56164a0c91790058cd53c33551614164e7f09e6083242801943b28d271dd7e6b96b08631f553b6e3858c31380d5d9ebc32bb4a0f3e66de48fd861d65284083d5213219a0a47b8203ab2881f2f508293e18da4589e1d2763ef1701f831198ce99761233bcf5b498f22fdbe4d2e4632fb2885c42bc06116b98b64084b42e5543a40f4ac7c88047bac01094201b7ca003d4005ae9d4f44730750269bd7ed36210268bb5b7d958e56a1fa057b9304a2ea6c531485929d6e0561b8d20bc5d08f096429bd6febaf525ab467e964539e8e30083baa8725fd631a13546e333709545739e09ccb845ac3917ab2e5015626d13712a6134c45a9978e369e960252026d4b9a784d40fb09d691fd8e5eb267592054fffacd957e61644fb2ee78beb36362c408dd1298636546d39760dc1b07b48b048b5f6bf0fecd0a5cb12b43fc31ca48bf5cddee43ab8cf845181db0a5cf64f62900239a64ef09137dfb6d69c3c26164ce67b5da10b78744fc684836e491851881014a02c67dcdc9686fe3cd50f8097080897ac274b4c1d0470991f44d19790edf3c33709216ff5dc041e06552b6104719a8f13154068fda512a86a53ae1ffe99eae19190c41468ad3a40d053fcddf96b05efc4cbd01c58e31e0f2a9173c4be95f3427e370f8c1684845147b643687a70d3434acf7fb0b6d32ebb1fb3c3a8941180b090508d677f7b8d642a9b852415f3ec8d0eedde27ff6dc192c961a3d52b1fcb98bf4b937ce41acde6e0b3abc1ae39caec8272f3098af83a77e7d27ad37edc0fd951d52c630181f188e3fc0764990168d181cce8684380dd0492b4bcb1e806a23b9c9eebbc00af3cd89d5f2f00c3981a00eed45a1715da5b506a885da4e047eb0a076ad72c935c6e858dbe9723c35126555a73c5574e0caf19f007177cdf244cc18e8a6a29ed7186cd93568fe1e5950e1e5bacbce62895d3ee762a74a7129a474312062d8a1a1174f7c8cf0a0b4fdddfaef4c1a1cb6cd0349306206cd834b4dde162b54fe552813cc6ec104b68a451b878300221d4741575f216bcb3f1fd2aa0746355dc59a66635c4a5b0a804100c21dc2c34294660f0517c66e21f1e32b64ebccb7ca2b3eb807608ed75ace8ad0be196d6eaaad759b968b431be73afd5e2ef698144c40d894d00944f4558ac62e64a422957d77a40a4b0161b3ad71761028573c7cdc84e1fc6913c5354079d36b06deb03733437d7991c078972e001abb735d3852ef6af57e52c35ca74b0d05a85e6d2ed96d38c511b85a5d9c55b5e7e1f4ffc131520c60fbb4238958001508c59cbf038d34c399e806dbf5a27a244ae5c45b245f1531486f9325bf9142170d79c580c9cd005fb25947dce12a1f92ab8d66e80230a54c773afd1ff22116113950bcf0a884c952282d70a7abb59e5870546acc0256e2229c9fe3da6753f19ab8448de781293b8353c4548c78431858ddd7797be16ce0c3ff0bb6b67b61740032fdcd3e52699b47d663aa46594bf99edd0bfe8a86c8ff12ec4e9aa12fd7efab5db754ca76547548a92af8583f694156182fa06333b3fe34226e100146c4724ed6737723abe574c5367c85cf224b838979b78c0ffd78b058e7fa50e96853f30e4233602902d0a04af42ae5fbef101426af41260b09a244018f2e95d753e392252143c574b9b51227603f5684791ab2b85d932879a050a68e99a05061a162a471a2b477330048363060a5e06c2300294fa23fa9d566a1cc41f8108a7c3a7528c632498746c4a096c2d70528df611fcd02aef9a32ff350b35665329dfc2320533d551057019f3791e6e42b571f04b1211e18f095f0d05514371cc03b71c752e0fa892ec08a11002b76c7280ebc26953e5b01e5fb30b9299f1425c5b72b40acf5c07f2424232d4e4e4663c3b39dc03e32e73a920076aa3c328db0ed1311a71f0326614d0d1c1fedb8365f315e04b3c316c31f7a74517a6849345e7dde0bf0ee4df1b85f33085a6748d1c51fd4dcd7a2ae6a173ec5a3dc69bd9346bb399c19878af728e9891c19786093e86cf3ec4167527aa4345f675ff16ed23da3c648463fe015741c699ecee8fba85902c365580886cd3375800ec80cc04dd6b2c1dacbacde69d5a9cadaa28ab1108150089d7d3c209dbc2201eae15f68ed483bb9b9506ddde585948ad864377acabc830c82ad14296dbef58efb9520c265328d1c3470cf8f0b56b4a88dd2b1340e57942d7b4269388794afb3a452db1ef25b71eb5f643dd05f4288b5dc262d474a67a2e78726e8d0933c99bc8e797d73c1896613e14cef89ab36c771379275e7a00d775a4a7aa085a2a73c4643020273e55d61400adb0a113476537fd8805e4ee2ee46bfee6d7f87434b668904081fa5e0f18701834fa3cb099e5d1f0c90ce5077cb097e9415bdc132f281711a9ed188da63ef84431735a5a7e74750ce9b146dd05e318bd0a3b602c81918892c02856ce6e6a01906a07f4e7b7396aafe005ff9fbdd0b9e2477065820617617abe2c607d75a1a44ab4b4e3b0444dabaee7b583b3695bde2599402533a3b20ef5d5d89264e799be7a89e65eb57dc2214b47036bd99c6c204771a1b453e49333a13de3c5cd21789fd94a0ceac3adac0e8f37e3aaa0d1b81b0b408ba25fc0a21e976f024d6bade56226125831a11ee174653fb68f8eaabfbf02f17a37235329dd73b972b493789197c8ff9a3172e40948c9e057d3ef35f0bec051c269eec734ad0353d532bb81fa0e0710fa54ad85ab317326e86a5543c7cf183587f48fed52ff3f394036d8413bd110b2891e45f681b1b4aa75cec9db3c64ba37259501a31b99efe8192435402a91ef44b0867ee30919fa6dbc4c21b84589d8dc781fa95134fa8eb333e1861cd9e50178d8dbf1d1df7c2688f21530c0d328730130bac02b2a8771b6c0f85e6764595f14407e5b5bf73e9822a0fc71ebdba3e3dbef7c6959ca01ccdb76516c9ecc4462a93c2d01f31f065f7a302ab783cf1c4ae16b35f79a4a98e5195b1d5457521ee49a533f6e93400f772565a59dfda43dcdb09adf099bd207c2cf37148cf1a8e8b1aca1f56745a9882493915cbca373875ef6335b18d271535e3ffb4a0d45818679a5e215b939b243812e8019d32e555c6746c6c5696bac350051d7ec33b3b02c62ac93f3735bc4537c42a262a29c6c876dbd5caa0a28db465575a4b0c1f2cbb0cc65b3b55437983b95ba8383b8caef0fbdad1e9790f644aa5e598d6b17bf55037d641adeada684f36a483821826cb707bb3fbeaf8c72c116fadbd694e6711205b3422abd30dad5bb061bb3e9b6dd2fe1b255395a22b423ea7b4f6783424eab7377ad5e67ebd021797ecf2ec031e91580837c94624c43e404f82b38504625748209c914f41b87b056be5e5fd9dbe4ec29231c923053eb5645b36ce3d5d13194a654a50e0be21a113b2bcb8679f6177b5f15af16c6fccc9d4e0b94d74cf7458e0cc66684aacea0abdf7bb75672825f59b4d4aaab0657f3ec6c96399846b4bfa1b2706a25971aa711b62755d57f0747159037ec30d392a058fbc7f85c193f48980a216a1337928e4988f7f4e31f00c09660dda47f92b5ab673907aa11fb6d762e809c6ce7d3bf837e7217cb9f678ceb6155ca558eb256c415e8ea577553cb4a8eb2a2881574e5a7e256ca0aa772a58815f4ca57e9564a15aeb2528a2a68ca97e25296142ea556883d27b0962b02884a8e95a0c28c1091f70e4af6b7d268d1d4eaf4cc5893fc714b1c6a8378a29428085a74522bc4bb52a240a3950eda82fc5192e0e35bfde6fb87cae9f7f917643c5acfa24f24249718e86f5d4f8ded08b897e2a52de85768a8a058678ab8d6c9ffc6043c8eb1ce63690b0985f501ffa75ffc9fa12b0af24f7c7a857eca9ae257575a0fd267fae72e513ffcd66fff19b446805e14177a439f5287a0ab0e1a0f1a486c51211cddf5f46d261fafaf3580faf4867ea547e34a775a4fa327e867cc57fbfc81487d505b987bd7f34d22e25c1eaba111fe723f09d45c9ce6ccb08a5cfdad5fbc0fcd4efd4cf1a30bade7e933f9f38a38dd17f1af55a3204b545bbffc175a2717c545eb91de13de5208d75d9e71872938ffaa476fe8afaca8e147884eb43c6911b16505faabdb9ee7d032faedf4b8a81bfa951535c4f95ee0ba4c7fb322cc2919d783630d0aa25bbbf8b84523a1f9e1fe004f57195319685d055506d994097cc07b137651605cd8814acab51176a7c68aabb90a91ef5b24fbd5782d0e79e940820f3e26f43906f00a907cc1e9c0d7414c6d28607c7c21352ae5b5c2dd8e6cc8ef7bfd3c4c49ec516c702adb205d1b5a0ccce18c8fda56e418b66972ec0f560f0725f960f70628ea69ef092dff873b66f6579df76d0b119a71f05b6f4dd9f2e1150c372edb745734d6f9f2ee7a973c71b35b5a4dfb4e48d1bd0a6eda3f49ee3444acf1c1261b558713d2526f2b37884f4567ad6caf9b7db7de803c7eb7413154b2a135635b850c4fb2585145cbd935647ad8092f5b7a178436047ece441f11501040c2923a468ffb199af514a9cbf5e765d571a71abd21bc9316e70b0ef541cbd584a547aa549fc7421583fe249432dde7304cf7391898b7dc4043fdf31f3aa14e4ac0dcfeadb5ba8dca6df7de52ee1df510c211aa105dafd9677f91af92df6f92dfafd7eff7f92de87a44c9b225c32adeef159690345eb0c4b0a6e2fd9228c3c46a4957588c78bfcb0c5862a4b13ad3020e47bc5f2c35665c41986a5b56f17e9b52561c61209912622adeaf46912e691475df9bfbece9eeb97deee9644feeb3f7e492bdaedf3d24bf7b477ef77abd1e5990b31460da1881d215ef5ed5979345142346b61c89772ff9d9ca45db0f156f60bec4bbb79481102d36d1aaa91e21e2dd635a3181d166e564c75bbc7bcdcf4de70e28560d1b56e2dd736636bccc8627fccd9b9bcb47aa6fdedbcff1e23e3bef052f4ac9e3f178519e5b9674294bdde74473a239d19c1c1a39515cbf7372727272a25ae6426885f151a726de39cb33272bdeb8b99938c63b8749245d2252f7384d9c264e13070707a7acccecb3e31c71a29abf7194467ee314f98df3fce20345520773ccd59678e3fce4c8a164e1e6e6c957bc71dacef8206bcbf1e20b8d78e32c4b9c388bebd292252cde3841d0844c8d607194638b370e8b2e56584a444c9d88378e33cb4897b28cba77f3d275f352eeb3bb3a3efb76a3945a9f5d379a664797a6eefdf76e4e6e27b79393dba9b57df65d597eca53f948f5bd337bd23e69e9a02db485ca7df6b6ad355b91c695b5250d9a0a34f1de318d634ebe52849541a123debb2591df3badddeff76e37e4dbb956cdabce8ba57b166eb3723e65545e5260deb45011ef367d7344a9c6a2468c2942e2dd7ad98811f684c71ba928f16e971f80d9ba32958666cb8d78b74c0c9c41316122c79b1bcf78b7cd058c3031e3ecf9542325dead73eb77fb1cf2bb15f2bb55b6519fbd3d3e9f9dfbecec312ac86f56a9f59b05f29b7d7e7616ca9ab16a6c9b1cf437ebb6fbcdbe6d386ec900532404da9278b348b3105c6e6c41ba3189379b7c4184068c1b2198c859c59b5d7e964135e479a64cdc5cbcd9b21923c44c88f1a0938a37dbac00444b103837636f5de2cd3a3f57a54b6a55ddcf295755b9aaca755dd71e3dd635bafef8bd2ab5d6a72a624594352da4e06011ef15c9620b9b35415484897989f79a3c41ba34a6ee35081a4ec3c969b5b2fca446e523d5d5286df6d9f5dbfa5b47f9f8ad959f758fdffab9b31cb2e304720e4d8c786ba4037cd09032f38508902ef1d6491488a9522356c28bdc8e78eba605945021c360a12166156fed9440ba44979ed57dce739fddd2729a26f7497549e992ea5ba52bbac115d32f39b8df398ac7efacdcf13bebf89d9f6e9c3da7e5a804a2a6d8980873c2868978676408596a598e5934acb8c43b675131e25942078b3430e29d975bb9a349c694a224de99a9041343c5aacbca5c9378e7265286133146b8acb525e39d9dafce7d8eb76af624807cb4d957b3d720edc0a64baae7a595335ae2c4ccccc450309080a003a3a4cc491c77846a4d9b1c3d8e6cf156995f82275c3fd284d01931136f75c9c30a0b6d69b60549166fd5f931b08478b91961a2b68cb7da64fe569f59bf55ad1cd9d5e3f1b3d3c07513909f829e81fcb4c6d941909dd22f36c916c78d576d7cb6a2b2046d0b6d8c9129713e62e5cacc57979b3566e20dff09ba963ae2aa760c898214238c9b17346b405489a1f453015a34b9546809f3c42dce52ea5e8095613ae98a2f888079c8bc51af3acf1838c806ef5edaab4e5cd30b7bd5896ae0ddd37ad58172f4ae5e75201cf0ee21c57861f0e6455ff52523bcb2577da908bc79cd57fde807bc79c8577d870cde3970af3a052f78e7a4bdea14a0c03bc7f9aa536802ef1ce6ab7e7405ef9ce4ab64448a3cec449975a4fcb4c6ac37b9ecd308f1ce735ee3cec5798d2c54cf14bc71e45e75a012de38d157bd8704bc719eaffa8e1478e368bdea14c0c01b47f9aa038dc11be7f86aef77f0db263bb6be5dfbd65d16df928d697ddb84c78c6fdd7d8dad03b9487bd581aae0ed3a5ff5a51c3d5ef5251cf07695afba121537ea55579a02ef1ddcabcef445ed5567f202efddf3551f1ad97ad5874554f751b39b7ad57de2544f4ad1f6aa27a180770b7dd57dc9b6eb55f7a98077cb7cd575d6b454afba8e1af5a9c6c5af4e1d7e75275a572faeeeaecf346f179ef59d537ab6e35b6fff35ae503e5dd25e751f2ef0669daf3a8f1bb6c7abced306deacf2556791faa9c722c1de650f6e1815f7aa0f45c07b8dbeea4725ca5ef52312f05e9bafba052eeb8d57ddc21678afc9577d2886dbab3e64056f0d7dd52d58d15dafba8534786be6ab2e7c837f4ca8aee3a4ec55d76902efdc7cd5878ec0404e78af0fa404d6716b7bd575bea84efe29196b472aea2758bbab1d7e9d64c4b3189ef5f535b2e9538e5b82057a8c5777d3a7345e5dbfc615ce504efdd483ebc17d521078f75ee5abfe2adf2bd58917971fbed63db89edc908e1eaffa101386a5c051af3a0c857f580c5bc07bfd210bd87784f7faf01b78bbbfe1f8afd6a71546e040cd603fb41ed64004bd86589d063e50c07ed63a5c5d84540b90a863cce6cc8a519510114555c44423a894d83da55a23bf231aa1f53ba211cbdf118da8fa1dd188af332c8846ac389f7e9d31c1cbe2a50e2622ccfd3cdc4991544f85388ff3e96bcc3260f5f7ceab9eeea41c5209f052a2ad56186e10be82abc20dbfca0b40fc193e3001a0d8c112074a1e923a2071c0b0c105a8a327a293d106cfce41ed50d4e4d93bf8a076d090866d759980b3f79a5d01885fb8ba3db8071874222252b109ca4306269ddf4fd0d0b0e8825206270eeb7f60ea80030f1d386c00f5e4b4a1c909ca83da218333b586c0d9df1e5aff0100c2d5edc13dc0a01311918a4d504c459ddf4fd0d0b0e8825206271d0074c041d1c35207250e491b20416178bae074b481d864a4c1b367503b1465f0ec4e6a070b4c1eae6e0081b3ef2c6f00a0030e84abdb837b8041272222159b8a4b3abf9fa0a161d105a50c2c071e96386c00f5e4b4a1494306a56293daa1089554aa80b3c3544a0e3c7410ae6e0fee01069d8888545c52d2f9fd040d0d8b2e28f93a70d840092ae909c909c3860b4d471a88198c983cfb05b54351d1b32b15d50e4b4d9f5d8d9ab282b3ff90531d386c205cdd1edc030c3a1111492949e7f71334342cbad06e0095e4b4a1494306a66211116908834cd4c71470761e1aa8c70da09e84abdb837b804127222621e9fc7e8286864559d8f3e4b401a90983860b198e988845a325cf3e543b142979f622b58385a40f42d00167f701218427a70dc2d5edc13dc0a0132161d0f9fd040d0db370b7a1094306a6e292d2901365f780044c38fb0f0848b0a1498370757b700f30e8182ee8fc7e828684401a32305d281e2d11958c923c7b90daa108c9b30fb9da01c307449f3d0219180167075620031a32300957b707f70083178e747e3f4159e832158f9492907e80c1ec0568c012ce2e21010d602a2e0957b707f7008f883abf9fa025a52422921106cffe533b1479f69e1fb58385a3e067f72000197076a10f0258524a12ae6e0fee211ae9fc7092908c2e1cf514a1a8e0ecc126a824240cc2d5edc146453a3f01c305cfeee6a81d8a889e5dc761b583d1073d9f9d080909671f222261b870245cdd5e514e8ea76a8723cf9ed50e440b4445403f0167a710f423a2917075db7723cfdeaa1d8a2cf454c1d985708f91305597b21709b3ba94dd82705597f270e8697622f2ecabdac182e7e10e210b8b25e09c87ae0561aa2ea5c2550cd6c79f4f27cdc40ac2a1d02908878413841be9b5078542c220e19620dce9f3b503b50369ff11fa68efd1ce23fc09b71ab5d5a91de156afd41baffd15f6b4f3845bed4a9dc062013c80415265eaa735867370861bca99b61bc26df588dcaa721d6e554b6daace57919e4126cccb6bfac35e266acda64e0f50455515aa5bbacb43318496a9e5e1576fa19a3cbcd6379a8030949a54b645a4a6d77a0a89c88b30508f48bf7a4b2472a40f741c11f003a2076e1e248a809abf0ec221b0056833fc41f035f0758fbbaeae57a80f7a5ed300061f0251cf6bf8754f5d3d84dea6c1f732b1f7ba675d8398dec0a9baa47dbb21e887250c61f2210d40fa54d4c3942d3af9500056983a3f08a00009284235115913a2450c163088704df5d002a3498e36b8b9b45d0d109cdd80a291e27a7205490ea499184a3265a4d020d6800162ca5c8b12333a10240cd616313b489435d92d556431733e4db8f0e1230d43420e8e39468683cd12ac74c90c37665654903abca08abc6963458559d447d70c0e9078a21c2283ce0c8c14501271635a48aa2d44763246a06acb02724339630a97a11c1cd8b22e8999f3c7ee4a4ad898b132ab31e6981172a4c92a71d5050c1d0b668c1ad09d0d6162f522461a138cd87ae226e608983735acb28499bd3519a22441868993a926ea15c743c4158c76075ab30c0815254f00c831cbb26ac0a07bba508e10a4ca0a855b11164bc856266457369634718e255fc02348c88511c10389a388973354dc9a785102559e703164a256230574c8893637d2254bd6943bd89c8d5929a3aa8a3b2367a0209330b1e165ea39972684d4982457de50bd2e662254b1a9b80ed32c57565c60390b83c21801479b3151da8030a912126b4f5fc8bdf6353fb5b16ee1ba30b04bd2ed11731645b37265ed4b89279a67cfedf9e2c973f3ecb579f6ac9ebcb9679667ef6d400440082149a5b5958939e7b575737a3af0afe707089789d9dfd7a8c665629a526562f6a19e76d34f39a9d5f2930aa78fb2ab6dffa442c3f293aad5d44f6ab2477e02019aa655a6269d705b50e667cfce1c969dd65c969dd8ecb4c3f1ec192ecf69a4a6ca4e3ecf501fdfd9ccf59dd57234c7777603f29ddf3ee738a0ef2c27c1771af67a2a286b6516d26321dffaeab30b55c13b6bc57df6dc15a67af6bc953b086a6527353ba9aa191dbcf3f27b64a71d427b83474d6303ef7cfc3ce52147e50e8219b9834508096f350a7d836a8bbae954099a83b7ead42a6b0a733edf2d2ae0ad2e7b146f2c993d362978ab47750a292a77103c66271e9e901458a8050cdd1f103f0be971d0793c3b08720741cfee0109b2138f670fe6bc461e21702fe7fd09b384bc884c08e735fa7a54e01e1816660505b9af718757060e027584591224ec5ee3cb0513ed5e176601bd880cd8be465e2f0c466a5f621690179101b1af11470ddc54ccfaf9e1bd813d4065f9f8e8d7c89a81059089d93f64f5788f3087e5a39c9d6f7002de026462f622b2e71167845ff645013bd2038cab1994e443010490828d9896e4338a8cb712e04832828514a713292f72f0bce002000b0d35b27a4891b8d1f5266563828b962b38aab495b9c0b2e3228c87155e43cacbc59a2c176046d8d2f1e6268a6c88153017553a2e6c402553316c44d0805b21638d8811ba4094a09b92c56c0d4b159ce8061531985b43e40a4d6acc589a2125e452a448b37cabaab156e560525340b9c39da75f7e374645a6e97755a08a395c9c20afafa9b77f3ffddad2e2799f7ea5380384e688e1a544e951bb480f5ccffe21bb03e55cd777ea064c83a1e99308c5b2694e814da6d399b314217a74d911963bf68307fe7ab8aa6378699abade39859c739b87bbddb16debecbaaebb765dd9b555d7acb566873e74476855d6cb8eaaaaaaaaaa4cade69245aa495855aac7d50d4ece03fd74d33ee4752516a15cd54feb71754fc0e9675555b55eb3389f073c1e8f209c71da5499acab48f5e881a76e70c39d46d7357d02a1d24d74fd43ea749ce1eafca37ecacef428e7657a941da884934e15e9bac1cb0ffa291fd3a3ec6a34559de95176605a4e9f4028b8b889407837dc70f6d6c98a306ccb686ecb4e6e6e148d0c2f6cbfb37327450606f73b2f5b345e57c0fcce47368c8709b02dbfd5e87aa34533a2edcbaf2b4fe79ce1de1d29abb56b6f9d751aacdeb24311325d57f218afabaf43364d1fb1507da4e6ccb2e1a5449b8dbeea5b3b59f2532793e6a7d14f9d4f1faf932907ed4c93d407e458377316e244fd8481ca9a35ad63ac7a513f8da044751870b06ac2741e637584587535a92af593ced89426c963acbaba54d3321cd0bcd44fb9998f480d4672b8d18a55cfca0c85cab9a5e9a71c0dfa99730d76c79fe9f1a683eec943457c348b8195654bcc1b3171aa9fe02f307e9860e162f926de2954c7b3ee489ef50f4cbc9e8d2165cd1c8e64122c2562960991675de935b2d1a4d708e4fb9f1fed3f3f3f5efcf911f33f6759cf863deb48af913d5bbe760caf51fb85d7f8d3c1fb58997a1f3f7a8d3e55c8bea7c7813d3d4e84949d7adee3c51e874aabf13d1e7a7a3c03704f4f9a9befe9e9e97195aaa7a7a7a7a7a7c7d3ae07c0fb9cf81a7d382ccbb22ccba60d799665dde835b21c3cec699787bde835c2ed267eb143152dafaba4bd760b4fbe8736f73d277a8dbd295832a0359a4a52b869a690239a5b58149692241a9b6fbdc2f035f630795a80781e17be469e249aacdfb913edbcb85b71f53b9d5dceb1f30ae82737deb907f06ec702c4ef787619c7aee7bb9d6fb77395c9e2ccef76377eab4e57cb76bbb4df95e50893453906bd428b772bd2c69c55dc8480897735b68e2ab69c993961b32b71c5668adc0c2127e2ad1e1b00460acd985609a913f156b57e374304da1b2cacb934f1569b2c64fccfd79ffb6bfca51beab5a7458741e135ea28ef13f23e1f7a8d3e2a5c646c754b46f47a36be97467f5c9161e6c708b9b0b8b762c7f77cc26bec053dece98a100f7bf035c26e0a8587157a964db27836197d56c59a675de835b2331041664819189116ceb6c6b7289ec059b9b9aa2a638b5b1564bef5a0d7d8ba84d7b88beeb039f91d07bec69db5bad7d1a1f13aee441d9d2894d7e1d1c94b9dd67574b48e0edb91d7f1e9642c1d5793fac98d755c5566a7f475581d1d77f5130755564bc450a188b2893f9c914aceb59965d1999be286450b246d55749aa8a1359f69b07ce978aae33ace46d99e6559b6132cb0f75a1fbd7658ebb503cdbc9612affda76ddbd65bf7798d6d85dfada9fdce7b5ee38ee735eabc9e26fff5df6b7c753c8f37e379eefac98d794e44ca4eadf3bc089585ec791e783ccf40dac67310b253fab5767c5edcf346fc5691cf7335999d58e7b94ac5bb7a1ecf5dfdc400e6162b492026c68cc43c4ff55304583411336506848813f1869d3c4f772af73cf5f83cb4eff5dcf71a7b566bd844682193e258176ff8595606084c7489ca5813e24ccca6a07bd6e1d7c84af89dd6ef7ce735eec6ac7dbd09f14241e2eab572ee4c0b122e2f5448c49a05d56bd7798ddab38cf8d6dfd7d8be46de6b5c9739c2f70a47b87a015e7fd0384277f8c1bbc411bac3579989aa2bb981f7abecf056a54b6f55dde7a657d9f42a9b5e6553139ab9a618bf9bac7e376518bf9b9a9a9ac6a4d0f0a2448b8d2b2bde4dc81038bc8089127b0316efa6e4afc61ba60e31687472f16e5aee3871d616458e9588771313c5dc0e1d5d6a101111ef261b5658615248668978378dd1304643db6f0d701ae034f4e03468d0a0a12c3fbd516f9406b3cfae21eea841f95bc38bdf1a5cfcd6e05c6362466c2b0795ab786b40028049862bcb91182068c45b43520ab94679a6c6dc58bc352c73e45052e5a51c5226de1a985b9a88c0b116840734de1a2e34a966648995b129e3ade12cc359062ebf33a46548cb909641eeb3674866a8fa9d81ea778616bf3364c890211ae6055b8c2c232b56c43b03528673478e1b544892c43b03b4860c23716c72dc2811ef0c2ad20a44101441a6dec43bc308512db2ce5a18c172a42cde193e60264a9c16882033b4e29dc147bae4a3ee33534f8ba9a7c5d4d3626262622acb4fbdb05e1813d39289c56fa615bf9992bf999e474ed4889c51d13d298b371392a9dc0143a943ae2cde4c492a2b9e6042e8904122de4ccb1a43c8c67278599b8b37530aedf1c479054b0d1434f1664a40f48845049116489ec49ba937a2d81b517cfe2ef690c51eb2d843168bc562597eea5df5ae8ac538ada28adfc514bf8b287e179f3bd06c31c3a54bed4b19efe2545b883219459a5125f12efa940c1b4c80b8a04222dec5dd91a89c192eac2b63f12efebc69a3824794333514f12e761933d6e50ada5bd44bbc8b59d2a52c759f97a24bd1a5e8d2d2d2525cdc92d9675f8a6b2e4dfd5e92fabd74e2f7d2335d2325e42b8a8e124810f15e4a2205240c182a2268e2bd94b43056a48eca11ba286bf15e5a0241d9a4c9128488f249bc97986c8e2b6b64d4e0d060c47ba9698204151738ce9031e2bd44245d2252f75989d754e23595784d252525a5b2fcc42be3952929c53995b289df4a19f95b2997f8adb41c51e3870d363e7254c55b093982b6a418161d0d3722e2ad942cbab1b6848eecc9d58cb7d2726a19834c4d19922ade4a4c658a195c6054b9e226de4a3f5371584c9c69dae6e2adc4a391c4a391b493783792de9e772329eeb32791fdfc4e8a526a2525254593dc7823d225de88bacf483c24120f89c4432221459148fc46527e1ef11b09090929cab5e244931b724faf782321bfea4d324c0a3134af7823253f5f95498226ab8891b3782335bf0ca932a34637264bbc91c6a44b63ea3e63c881c390038721070e038638380c51bf3188f88d412bc46f0c183060887e2983a5cb8b36b078634022717103cb12af3264bc31c420c814158da6d80e2ef1c6e044e5c9c5e549143174f1c6b0e6a3c8549a2a6ff1c640a3485d082155985b6ce28dc17976e1ec0207bf2fa45d48bb907641eeb35f88a3bb70e1c2850b172e44bd8ebc8e3cfc3e721e398f9c47729ffd28cdd45106f1fb281f7f1fd5fd3e3a3a3a7a81922f7225ce9a707589f7919a193e7e640119239622de47c9216b6ef8101345c62dde474b1bd1252e740441ab118df711b34c8db5b0a91d5f67e27dd42c03e39a1195e34a955abc8f9c3938883938881c7e137398c41c2631874924268951442291488ce6a048977250d47d36ca491ae5248d729246464ba337bf8d8c8c8c8c8ca21e9c6e65a6d0b0a26449bc8d9022bc7a64e9814cb122de46c934e9529abacf45724572457245729fbd288d5611ddefa2b9df456e7e173d3f7b11b4a848ada82d65d3723b729961317fc4bb0839033568acbac8526049c6bb2839648b0d29244c191cf12e5a7a9935bb7c509d1d81c4bb8839024c13ad263f966f92f12e62f65881c6ca9a921a51f12eca62218b85a7df16a216a216a2162c10b11065c182050b16a262e99258dd67a227d193e8494404fd4cd4e63711119bdf444444445ec68e3458901d4b9489375154c9151b7260d48644c59b28c9854b8c166acc66803de34dd4a480c646268697b62868bc899c383e2ae0f8a8b0e17705ad0a5a15b42a544813ad20f7bbc29adf15d4fcae50a14285b02029e2d0c4a80a8115ef0ac8205abc346173a5e54a34de159235d80a8bf206e5869a57bc2b2cc73899f1c556b58249bc2b30cb30a7509cb1ea32c54dbc2b342740a7c6c59912501a68f1aee0c4a91ae2540d9b7e0f9543e5503994fbecc33470c334bf874aade170388ca24922069d2a63a2ad19ef211209879613356132c0c2c47b9874e9842e9d50c36f21ce51887314e21c85729f5d7814c6fd162a85687e0b85426154889aaa1a7358626c52c45b88dc9202c609178bb8b5780b9327cc74b1952953232ddec2a609c2d40561f2189366196fa1138c83f10cbf7deef35b7e7bf7cde33ebbab41e1514a2df7b4ec94beab7df674bbbb79a54b5e759f29b84e0aae9382eba440810285b2fce4a6b96914282c79bf299cf94dc1cc6f0a70bf29c46004734df387da1793785318001a29ada794999832de145228556eb6d07dd568e2883705635cdcf32b4a881e63f1a67081341f5f6b5b275a7cc59bc29b12335f669e45c268c49b825b952eb955759f875ce590ab1c72954343434365f9c9edb187cc3efbd071e7f75099df43647e0f8df93df414246e850832cb296ae23dd486eb468e206a90439cc47b28a900265ecb3231559a0489f79007e5982d3367991997780fa9799932e2c344d40733de43cdcf46a2a460296205778cf7d09874694cdde7097013e026c04d98306142597e72a3dca80913b426bcfd9e20e6f78430bf273c776c54ca34bd80e19115ef094804b8e83a61428c8b122be23d21996a512eb18859ab2a0322de13963c14695748d8a0d2844dbc2730b9dc504c1529836623de13a8509993922379046312ef0962e99258dde7e0ee19dc3d83bb6730180c96e5a79dda4e2d18747ef61d04f33be8f63bf8e577f059811a6639c4b06895618b77106900325d6044ab68a90b8b773029c6e6e5edad8a7923dec12509ba9a566d55d01366f10e2ec0ad0b8a1864cd3828f10eae8d01f1d17cc3a2e319efe04e86d04e8650d16fa1dd5268b714da2d85848484caf2d36e6bb72524147d7f0b79f92dd4e5b750db6fa123ca1c595e26927161e22d8464b2044995922c2b76c45b2884922e45ba9c804b33126f21362147404c793177166f21260f279922614f960889b750b3056a39acce54e1911c126fa13441698286bf83e482e482e482828282caf2d36e6a3715140417c4e577d096df416cbf839e1c588226244814971428f10e421ec0cc102e5d6fc4c0a0f10e4a8e00240ecd5493c68eaf13f10e5a4ea152c26a8c978cb7780731353ca7b860d0d8aa3b13efa01b6662c8c94033f18615efa0f68984f68984a1df12a012a012a012244890d0d62641821a3909d1df12b4fc96b0f65bc27304233a78b0117931e58b32de1290259c54a9e0f2450b48bc2524ab9a106163c1e64c46bc252c7f581179ae5d45a9c55b0202e8967ca112236dcd4abc2584797116c6865bb3c996784b70a61d5a1ce9528ba3ee33b065025b26b06502814060597e6abbda2e20f0f8d93730cb6fa0da6f2096dfc0332236dc285d24d15126dec0392597e657162533bce20dfc99a3d14288538b5558bc812100190364aec9991861f106b2a12215034c8fe40e30f10636a17429d2e42b4b49166f204b07c4d201057f03b547a0f608d41e81808080caf2534bd55201012181aefc06b2f21b28ed37d0b3032b3a1a3bb4e4c49c88379013145b536440c8c15d8937d00bbc58980852a7638923de402048aee9e1a5c30dc65bbc81cc90995a9679b1226c45bc819a64c68e37beb8b0498b37100be68705f313f4fb877dfb79fbb79fb8cffe93fca9f2fb07edf7cfd9ef9fe767ff81fefca8fdb4810089d2470b1f43a698c4fb0709050bf40817163a5ac4fb07090b29145ad6403953c6fb676974a644cdc79536376ef1fe612660ee8b9bba1f55d81089f74f33ad42457786d603c7159378ff38592f1fd6cb47c26f1fd6e9c33a7d58a78f8f8f4f597e62d3d8341f9f354a1fb3df3e547efb4cf9edf3a4fa2197a4cad8941f26e2ed83f460cb152f6c516aa492c4db27b905ec899aa4162755bc7daeb6c4b8116469022bde3e5e6bde906186884a1b126f9f17f878cb2a0356e6a64dbc7daad2a5aabacf3dacb28755f6b0ca9e9e9e9eb2fcc4f6607bf4f42cdbdf3d527ef7407ff744f9dd6336a5c89b1c4faeb667bc7b90616d8cb49569f18811ef1e1b6588b0593d81b2d012ef9e0f5ecc48c32204859733f1eeb9e2834d92a935439af1ee693a712cadd848b13113ef1e273b8287ee47d47de69963913c3c729f9d478b07ca6f9e27bf79ca7ef3f0f0f044d768fde671e379938391bc4262854d4c57bc79909fc5cc705ea16b033326de3c3f545964715f6c8841c69b8758a516c7a50d9932ae78f3f4a08bf332048baac99878f33437c0345247e388122360f1e6c9922e65a9fbfc5ba3bfdf1afdfd7ebfb2fcb4c6ad71bfdf1ae7cfc9ef5f93df3fb2dfbf2758d48c1a38a874c4fb87bc3211c7e522c7d59278ff929fa7d2c6d48d7842c42ddebf011cc76449128408579a78ff9e6296cc7066a5507ec5fb976e41f3e58994284a2fc878ff88a44b44ea3efb9abea6afe9f3f97c65653e1fd4f7fced63f2db37f6dbf75443e143080db2113ce2ed438aaded784b9323cc33debee4e7126b492320a6582889b7ef499a176b5cd8ac09b3116f1fd3054d9a44a4acb5e1d2156f1f0a1e4e92b4c09292ac126fdf8a225d5a51d47d86d7240caf491886e1b2fcb4de586fc0f09a282cf61b0efb0d2ff90dd79968e1a6840c1d5d6df1863340f6c3069a32143bc8c41bee809227559c9a123ba0c41b9ea12c31428690326546e20d33e33222736d659623b0e20d373f7b9888a65b911c6586c41b66932eb1a9fb1dbdf3f6733b719f7d678ddbcececececece4e74c7ed49baf4a4eeb30e5407aa03d5d1d1d17173d331fbec3a471db0df3a5fbf759cbf759e1ddcd4b4a93bb1068d9178eb2057d0e5c559a7038c8b10f1d6497ab8422b42e6c856978e78eb2cc7ac884bf214732123de3a4c9c202f629c8d798e51126f9de6e71756702071e3854a2ade3a38d2251c759f5fcd7c35f3d5ccf7557bdb3efb5b969f74573e525d07ce645b52a5b6e62a4a1c8606092b502e6e537171ea6a140aa0de04aee378ea34800108d929bb7dea15c84ef9cba7fe21f5f2a9c3690b3d1e8f445257ddb2535c72ab729f7eada5f17cf5deea3dd9e9f5d581be45ea57a25f8948be9b3e64f9b5b73ed3b25f53330fac34f9b5edd7f56d953b9b53a37e7cab48aa5faf9cacfd7a03c8b7caecf12bd0b7baf5ebeaaa5345965fd53009bed567d9af41ebea6a9409f357b5edd775b9d5b75fd5b895079a5f57e4d4af994ac9acfa35dff895f96b1efaceab672715e5af39ecd7672efb75f574f51c5da6f935b7fdba7aba5a58e57e3d93fad567d5c8a95f3595f257d7cbecb4ea1bbfae3d8c7cebad152dccaf3bab7e96fdbaeab45f5d47af00f955b7fdaadd7e7dfb356ef5f5a8f6f6eb1af5ebba4efd4af5ebd5af9e5afdcf7fbf9f1321652724ff79110bdaff7e7e5c7e3fcfc04fc3eff7fbfd94a89c7ebfdfefb7e4bf5f87dfeff7fbfd7ebfdf4fadc777f00eaece788d1d96969696965c95f11a979cdedec9d5abd7e8e42a292929c5487a0d5874bc0657ad5ea3866030180c0683c160d05518af3138e591ae3c1fc955e56b44d21dd22652d680592be3c5e2cadae504244a10394237824d542cbc12f642575fbc46a18bd7f8b3b2e57dbeb6f7b9cf897cefb3f2f63e9f8f8b4fe8beb4ed7d3e0741842f487575e37d57aedec7e373e273f5999d94dce76a99cf4c557b9f98162259633c94308e3473742989b0f18a7d209aa82092850a13343eb83066c6803979689df97c3e9fa73e57bae2e2959494945cad7a8d4aedd207dd0d5aa1fba0ab54af3128140a8542a1502814badae2350a7507fd3f2b5afef7b3a2f63f5759bcc6df8ad7e83b6af247ae265fe391ebeeee44ee4577d7ddeb1d0d45f17faeaa788d3f9f8f440a47f11add2845da1bb93af51a8d50943d94cf4341b96727a14339119417a152883d140fd41628af40760a3a946700ea070505e5500d10432307168b1949a4a0dc266bc860a559395902953ea170fbdfcf5d38c5ff5c957a8d3f6030c8f641778328d83ee8ea89d71804db414a9b569fbb30dee73096f739d0893e14caf799bdcf5513afd107258cbed05da114f485ae225fa3d04bbc462838b3e0844f4a324b7e92591b15349fe42a89d798e4bc1fd2f24343438e949d7c3ee4c5a1322aa2b4c80203cd8c190f399db860127921862cdeb0db0ff917951aeff3d5e7ea88d7e873a35ee390054f99bc055745bc460b5453cfb26bcfb2ec90b32c559367594f2be0010b42558a67d338645299c4b255679ecd6173169675151a645957a36c905563a1a27c6c4e3eeb59c926b1acd059f6c7b223c454ede87aa202c8593ca58d96175b2aa046d2672ad74c8185220c8796367116185bc8889182438b95a302a30c0f2e6610160b1417744a1a6a2b5896f5dfeff7fbfd7ebfdfeff7fbfd7e3f5743bcc69f10cb6fa8176a35194266449525c878c32f8ca2824d0a33625b421667706c4ae8d4a8110aafbc5068a479c3c48e66142c2ab150e82a88d728ec2d09035be203f335f97c3e1750dee7f3f99456dee7f3592ddf67a5f63e6fc08cf7b97a7c8dbe9e9a820466669c50d68101c266cd5682644aa8a13942c7e62686aa9a7b281fe13542f59c55583e5815e3832ec26b0cc249593ec96bf01a9398dea960bc3b0d5ea30f81f9219fc16b1c92418616c596a41be98bbf9bbe580440b1e82ef4ea8b0e03a1655f7497b2e28b4e9462f6452f2ebfe8501fa658f9a2a751d4bee831788d4528680fc3690fc330ecc5fc94468fc7238918762838092e027b05b253d161cf800f861d0438085781a11c8661380a948775e0340d7663d85568764a1f16c2b0bbb0c3f0541234d132618aa088772a3734c60799256a567e48c19ec26a330af385be66a7a2173a0c5ea3100a9817a455a0a0a0a0a0a0dc05af11cae7f3f97c3e9fcfe7f3f97c3e9fcf5bf01a7d3b6858709cc0214702ca5a0ab320576158c87529b2a94127a58d14311065c5e2d9178b3b44d028b3be82881913178b1ec26b2c32f9ddfbdfff3cddc12848fff35fd992fffd1c78e27f4faafcef0b4a0e2771ff73161457f01a61e10fb3e27ee82a788d431a58dfb666dfb6add0db96c6d9b73a2d95b66d836d6b43c8b73aed94d64705d5b63996dff65a29edcfdbb6d8ae7dfbd34af9ad1e73540bb7703b2777683142a64ec81a15713b25c4878e37487a7445d5681bd2648bcb494c47228d972d3366c0c53b7d6e90e34b045b1b243eb8e29dca8d8023cc0b3a316f8654a1c55b3d6ab5cd2894e41965d3479be24a16091a4eea66c45b85fb168683480f373deca9d6c3309c03eee12c200fbbc34e84bd083b08af116ea3b0d075478e1ecf7a0a8a9eee9e2f7aba836cc5a2c3c82f3af046db17dd7128f9a20d275f7414bcc62291851ea0c798b412636c881511c12a0a139a850b048a0c67baa4a0492365c49adf2f47f99f8db1ff71c045d693311d3bae90887f369cfff313bcc65fefa1a06ab03d548d120fe51fbc462820286fc6a201f73e777d34b8bccf4df01a7d411a591ff4fd1a83ae7039f7422fc16b1492e035b659b9010f2abf818fe0356ec054fb5e0fcaf77abd9ff77a3a5e7cefed3de9f5dc8362afb7b4fa5e52af0753b53d4f7b31a67c2fd82bebf9bcd7637baad6f17894eac17121e391852e7adc8a7b49cac47c5c997863126ff87b2a5c40b37a24e9e1a517c5aa8b738853889c8d5eafb7e6e60bd5962764607a1dc49082d222851b332c7a6c70233ddbe4416ca765a9990bd65d1d3b9e7578c5b30e7c31e5597765d5b34ed401e6592fc678d6a1606479d63ff848bf769c79d61bc083c8b32e82d7c8a6f59cc09041620518166b255ab850aae94a53f339d0e7e2f83e9f8e1beff310bc465f0700f8dd13dc4460bf919abef5f06d7b45e55b192bbe9501fd76c68a6f67a07d8ba5fc16cbcab7572abe7510bcc676a7c96fa814a85d499150c1a2465b08a858585561751d298226434cca8bdb94b61430d9c357c787ddd54f1ba8c275c4871b191a91188e41e661ffc06b848b4c305f740fbcc6a2fb63c6f89f77e035fe3c788dbd28d3989967720ebc46a61e2f5ebb906b2ffe20f11ad64cb40efa4edb400022e4753af65b55d509bed52b550da226e1b72af65b7dea56438fc7e308ed6a744b861af6ba1735e43b4f65e5315fbd8ed2b06bcf5d147ce7b00c2d82cc66d9edb567b82c979dd277df794e238bc4bdd6539a999d7cae5df7106a4fb56b67d3860e5bf25ba7bd761dcd4e43d7aedb2af8d66f1aeeb56b39aea9d71a4cfbaa5c977bbda1c515b76e7d695fa1d9c9826b5fcd8a7caf6a6bf4b5af6e74af7daf6fcedfec11999d8ca688be592a56f9dad9259231f68692df6cf3b5b3ceec74e4dad9b00bbed93216fadad9b42e1daf59b524bf59b8d7cecab5470cbedba816f9dadba417d7eb96caeb5badec84e4dadbad24df6d97f6f69984455bf6ba8dbef6d62db76f4abedbb8dd5189885d54d7ef9df2b5ef963b66764a7fc9f7ae87d66bdf3595b0e9f9bdeb42f27b077dedbbb4ec5474ed3b3526dfbbb69ddb6bdfc13979bcdec51df9ed225fbb9b7495d929fd0cbeddab25d34906e8b7db834bbbfbcc4e1a5cbb5bd6e4db3573d35ebb1b05a3f1da6d6bfe76e55e3bce1107999dd2dfe01b672af9da7194da71ae8c68c769662727d78ed3f5e41b270ce7f9da71a04bdae09815f98de3f6da71e0b213946bc789dbc037cedceb9c2392cae777ce5487df39cbd79ec3ccd1ca4ee973f09db3d57ced394e312eaf73c28868cf8966a70eae3da72dfbce797bede9ce817bed3972d929fd1a3b0bfece997bbdf59b977ced3ca57e7263edbc2507be79375ef398afd78e30b97a16119324d6587e3c71f1c4c613c50835484e6c51231317efd4998a318165c64710ace556bcd3a7049c309e9465c1eaaa11ef542e4b09dd191869d694d4c55b3d86e03924440d2051392bb19499724e45522b196f754d172a296098751d118bb7ea86810857dd0e125959667166e6b41cd549ad7c7d029af5850b982773f1d6cf1e713e44b8c10246ea2cde1a6a850556d890126ea09c88f77a8c3241fce8d21126cb4bbc57a40b204848346db0e1a122de6b338cd408233f54b06589f7ead614f1d6e6879a1821f15ee186b05822055d96692315ef55ee751af2e99c61f675424dbc59e6eb305ba4f8c86ac1c22bdeacd6eb1140c02812044513cb8c59bcd9e86baab52c3ed89a4c25f166dd5e60c6ca121a68d6643c8977ab7c9d62a173322584181a3121ba85be6ed35e47795302a66685ee48bc77c8b43164b6906f66bee2bd4bbef6401632ae8ec4173211f1de395f1f95c815311263c559bc774fa38d19586459456c8488f74e4e0139e0dcd810d1e68954bcdd23d00c4b6bc6992e5da0c4dbd5cac0d75b5813372ed88cc4db6d0a5db46d6933a7264b8a78bb6e43196e4e4ce9a145ea4abc5db8d746545421d364449b2cba4413354da28c399222de38ccd7eea52b2d880da6179278e3a4bdaea18c2239ee28a323de38d1d79eee9ce46bcf51befeb22c52c406599127f1ce79bed6f2724be243069c8a78e74055f022c4da1521483729f1e61d5f57f991b64cb3e2622ede3ca418dd73e02b07be81d7c881ebc1d7ecb4c17bf020b6e33db8065ea3870e1d3a7410d3f11d3c03afb18373f0bbf71c963c0777ab3c8730aae7e018788d1c600a1b6ce0eb06ee62f90d1cde60c98adfc02ff01a3700123d541ef21beaa1945e3190544dbd60c61b7e2819236a44db447cf9ea8a37f1a1be485c9d8181842bc59778171f0a4ac843b99b79a83740ea72405f64d9d126865a12f5506e81d708f5e46b76d2f04f4f49fec92bf01a9f602727073ab9eb0426e49d9c02afd1c97bbfc1d70dee62f90d5f237e834fe0356e8095bec9e9fc2697c06b6c723568d0a0c139e43578045ea386a1cfe044f1191c02af31031393af4c4c4c4a94cfe40f788d4cc062b1582c168bc562d11df01a8b4b4b4b4b4b497afc9237e0352e2929b9929292d794577206bc46259c5f90df50ff9312c5f1a20c9d9325f186ff17570309056ec696977813ff97f565849c15315b28e25dfcdf4feb7f5e64ff7b912cadbcb698412b13ffbcc4fee70b788dbfb1a4a74f4a72b80bcd2775757d922be035260191909090ba8c3c9227e03522953d0624591e8323e03562708dfe0212acbfe007788d175460c5ea4d08972336d8e20dffd111903f72573f3d8099462dcc2bec8d89f808498c3f7203bcc6232722ba569e78e4f8442fc06b24c246464646464e80d76854c485e38b7c00afb1c8b560c18205aeabb7e0c6d768c1899a5f4fe402788d4464bf7b5fc1d70a46ae7c052322be8207e03556180e87c3a1c7af71e8c29e17065f287457e8b0d081c222582ff47f8d42f7f97c3e9fcfe7f3f9fcc36bf4b97b787787cdde8928df1d00afd181624f610bc953f00e5e23057707ff80611172b0b9c84ad2a66d071c00234e1e62a8ae64f101c3f0d6d6c330ec1cbc46b8d7e477cf0f0db92b44cd0f393c84ca0f09b9fa21f7f01a878046bd1d3de2f77abd5e9034dfebf5bcc36bec394929fa4df4ed152d20df3adc3a50abcbb7ee418a7c0be4ec5be7f01a5b628e0f11486efca012c7867601e517a913cb325422a6b0b479a1e4058e303d9e7507e2e40260c49a64c160922644cc02097bd637788d6cba7b3fc127b8cbe527fc70f1131cea354e8077ef834dbf7b3ee86bd0dd361f74d8c7940f7ada83c607fde93506832fc4c3cd0bb9d36b14daf01af5502b4845d80779d36b0c722538508204091224b82b81c84b5021e2d90ccfba86d7c86aadb5f60caf513bd36b9400057422f5402fbe46a0dbf3403c1e08c889801c293b6907f2627e5293c7e3d1440ce4ae7eba3016a38d0dcad88a223190d85514555bda3144bce107f274cd03b995df3d5428fc467a9d6e0f7ff43b0d63c2248809db6b073e8fbc7627937aedc4b5d75e24a37bed1f9a4079ed4baf31732e89a8dd6e37429226488e146903b6034b541b1d65e2c2ea6890858de8d21983414fed4ab8b15560849588eeb400af72fca8d035c85b90940cdab64c388f1972c9edb49412358608312bb9dfed58966505e09616f644eac86979d67b30cbb2ac12e64e0990145d526f5582c0c2ccf0b44b637ace36112dc0e8e2b404b1f294b21e01b26ea802d5e2c3098d2e6efa44fa1c5bb201e6147ab792d9ea12c7e5a50412006ba245dc4819031df134b15bfa68134315617262291b5b56247182c3acac6a4bd79b2899b30672cde59a723552a88d1810146d685d44a07989f2454a0c8dd4d2081117cc88972e718382114c4b8385f1b1abb0eb00892252278c94110135031345c78c317328c6c4f83489319e226ac6a8626e34028999630e2c30423459630548143659dc803d9b3a7ac4ce09521fa52e92ac5d5999b5c7048bfa9daf3d1e753cea847e07e379c25dd500bc51ae3c56603deba0d11a6dacc072dcf8e203069c93301151376ac452bb3db8b0e182ae69f258c058f349133323744e44b869a0f088e80146b965583fa546122ca4a4c19955e530737c963043d7836b8919154bd494d021527775d354188c498cdf2d49a364076908ccc4550021e45816e8e2e00d61ea30ac0cb8134b37faacf7d82f375a0212ac3733b54316138bea4dc1bbdd6eecc8b62ccbb22ccbb26c0082a4a5edb06c58f30bef4d06126277a40b670a4fd8138c85d6eb9127bfd320c652a30ea8988e341649250cdcb66dcbb66ddbb66ddbb66dab7506c70d6fdd8dd560330484492f0028906b7ceca86159966559966559f5637675367d966559d6d825b9ae60da8825e9a2adc51a104ad694e18a37f0c53ccb86d5907b76832b6ebcb2bc89ee90d347b0b0b7ae67bde72cbb26e1dd6eb73b21978fd200481df351fadb857be0155ae076b7e2062cc6cd0a341c315c3ab045b44b0509ebee3c5d3bfb9daf606b56bfeb00a60d401b52cc8c6fdb2542d2be6d1d4e9da86c3cc5c9a54d163336c68e3441116fe03fcf9e6553341c4c2c38a6e0a2b66236d73dcbb253d8748eacd830f6e477675439b2b66ddb3626d3c4c17177ad83c979f9b60dfbb6cd6f5896655996655996650380958683e3ee96609d6159966559984db1e6ebcdb3decb4e3dcfbab32cbbaa10c0d613f8c91d988920f02e8cc80b1e988ab876a9bd1292b5e2c953471b1e4b5e90f520b214e2430b18b2471be683c78feeaa2e1f1173e796ec86383b27d6d5d4dd05204dc9b23842570846c5449a15778ead9df0c655656d859316565232373457d07858cd7d4561b1244a8ce0efb29bdfed763b4f83fe6e073645cdef76eebbddae98a30de3a51d805330115bdfc1a76021d2bcd004863448c0d34a1434d69ec6faa99daf03d258eb217ba52a597d6d5125cc9499e3d1040994ac315c722ad8a47d01a135d5458a18339f890bc534e98209b3231e380d63d9ebf57abd1e0c82eead3db6d7f676bd1d5c0bc7c2ad701a8ef8e96bfc1024e35cb223a859afc849a3544d6da6d1a6074437d1a47a2caa4091e8f7faebcaaeeddaa6b1696b9af6b422d00e58f55d3cae23a8394fb1404694894b5dd1630c84d224203209ea5456aac9649ab4c44cea25900376f1077dc454e6c9cfb0e9f399e8b3a64b8f7a958949491afc7e6210f491dc0c7cea124f950c07bfdc2a2797893e5ee0a23b7170eab1e7084765a093260d0181863de906a4cfd40955243a4eef53fd849374a16a720755932d544db249a89a4ca15b4d36e0480a565dcdd94de5bd87b569af7b5dd7757561f0a1e9f78af47b250aee5cd7757730dc0281acfb4a24fe4ee5762af7c1421bacf5690511da11d4ac9d1c7766aa561ad5f2001a3db61fe4a1462f5d838c20a449d975de60d5770a976300a791887abd5e0f6e4031f8bbf76b8fed655d8416e1b3229c9a330c1e86356bd743f7ddf37bddebeabaa93e721f12567dbb9ad5b50a3975f0fa2958883367d2a5538302c61a2e1e12b0105196fc2e46b178c1315a6bad596f59b66559b76533a4493f5a5fc9128a808eb3eb380f6920422c829ab59bd667e7a956f40514704ed35a6badb576801bc3fa88078d5e77964daf93962605d1ac2cdad1916dcf764040da9dd9c39f87653b97cabaa4c4c599a2779ee0e448c970ce440a68393c68ce3967f7e14cec59c1eb45790f35e0acf61bfe9db3de0b85058af1ea3c71fb0dffce520f9d9d32d7856120d09d482c16617da464650776d232c1c1be26ba58d450503a13875ebe1fd9eee14cf499fbf13ce77892a8787a98b4b13e163d3e63393a743e3f62d975330c671e9eb51fa0301f3140c025c0ef110194009606eba3a1211282be1e7ef8617569782248c8f9958f92424100b830d089458df370265a00139cd0010c0381ee7a68c684210e5cd742748882ce33e5e0a1e766a2f08c4bc13b6437c34038137f4eb890c3007c0fa7493a67c2e106dad5b0066ad7445dd440581f01810d2b403dfc3e0fa7493a7015889e72ce3913279421b2e094a6a9a7fae78585a20dae0bc340a03b71c3c50d7f1ec2286afaf6e110b0514fe7997248af181559202aaa30147a1185a2a1a2a2a2094545c1a2a29fa2b6c8a787a7e8e783778a74de5e91bb2b62d7a222384dca5ef4a69fbe70ca6147ab4213de8514753c6dc1382e4720c880b49ec3274c9cf0d80e940e2854f1d0c3026a076d03d01571c98791d2150b4c668886498a7ce886495543be161374de0479b404f9bc49e889020e6d010da57c8e56f45808c33384d5b3c0c67362736673f63565f33afc917275d076404b5cc70c3b016efd51e60cad549f889c73ce41a12009abeac346228e44da88e8f143508b82b66b9a24a4fed0c5c92a4f9a601a3875e2a743b644b2e7b61be09cd5f4c9b785ad8ff366a60e5df643c8cf4462ea47241c3e8f675dfad180a367fa9da6ece77cce0b21407f76a10164111e6ebffd229350fe0a7955d35667ee43c80f6604f343c89fdde71d804e1a060cdbf3803aabe9ba7efe74b8553096863d3f84fca903539e04ade998c47e88c8700a4ebf56c0fe072974481d4ab5d00ad8ff80f83d6ff4453f24fa2151050b4226f688c9c2f330e4e0e0b8bb1603bbae2b069d5535f5e9a45f7f064e5d8d527e0859e7d321529a24a4a649a9e3fcd1bb69ab93665f149c3a2f1d9a1014220a92506108ef08dddd51b0a0a645455fe1f37027e0759e6952ea597dd5784744e02f78af4c34e024f07627d0b08006ef151e83b7fb2931e590eed8345d739aa669aa0a75760d542dde0a42703f6bbdeabca32d78af9fae60f721b82f3400fda9a7df4b935402e87c3a4c9996f0fb803416b1c3e60aac319b612f78bb7ac212deabc780b3eba7ac44de488576c0bbf7e99a8faf87454d78bb6beee95ddd9be3e7212c066f976d75dbb66d4e853abb069f3f6b8df152a29d86b8525515849bbad3b29c6655d5a990d6d981fa49fd01773c1e4120bdea02c84e29d831ee5555856ea25777703ed2af2dcb0711f5dbc3abcf9cc2d969bb3dd9299379d5d3347502ac9fbaa9abcecb6a4eb54ed69fbfd828ad17342f2b69dc4e30e7e0750a07c58e6d06fc2ecdc4a1d1f1244b1bb12f234d286225945a7859b07bdcb941e6688177492c3c20b0fb64bfe01d9427840796239713ed1909c153924273aec0bd23cf2dd95ac1384b160d76b55a1a30dbdc4181719cbc143970f03ac56bc2ac560e91b629a45d71a35dc2f3e1727173b45fad989d1a1b6dd1ecbe7867ed16db65f782cc140d26bc35f837640bec3edb27bc83ee7c20e179e9f1c049837e487db7fb142cc40d5d22aab638bf817ad5adbb735d015ca199c3d98a3bb6492697ee9538ad1c2f714d1cb438674e142c0366cbc0eb148e17cc5bb66330abe506f7a02c579aa8581a381c37f07b746bc0bce56e06bcf3e9d799e82e0ae3686d81833efd52e3a402bf4ce070e4d4c8f1cc60f698e306bb48d60abc4bb24de09d928705b7cb9c30308e561b8ce3c45902c39f7ead814e59137502bb47164e0e996305ef92386c67b4784560f7c96a8177d09c1e304e340abbc9c1b94a609cdf2914678d4d12bae2401b5001b010567e17a3cef78be96caa51964daddc45eb496c169dda99c6c2a565b8828dd0deb4379aba30d0592dd03689bb03536a31ddb634b7349c2a3d3964d2dad2da7857de7a62afb2d55adf324c1d32de0e9717b95491f098271b5d7d67cfdf1b94478a330dda33e63365fd410304556a318169e49272bf5da58433702d12eeb7ab0cda426c169d504f212c4d285113c9d9f40cc639d3a09ebc096a9a5062f139c4844299968779dbbd0aa3503757395412958bfe769515c4683d7944bd0c57b076a3357dec21b56b7fd37816d8964dad22334c2751d3684913bae623d57bcb1e66508ba889e47cfd7ced3c622fc3156c1406a753401c3ff59e323bedb7ed552db757fd6dcb4efb4d7bd5557fd3de68764a5f1d2a59a57e72d9e50b5774c14b89760bf7da79eb1258f5755d57e0042eb8fded9faa81b79af6ac328d4dba6992500474ccba8ed9210d4488537d945bb89cf395871a309b7c557d13a5b5d63ae79c8112de5d35cbb22dcbb62cbbaeebaaaaaaaa9fd4f448e3689f561d8ba77eba02dd6502144af8547dadb5ce39e7246ecedecb5955553547f152a2bdb3fc9ca6411e6ac0adab9e1dc0c6ad1a076521d6d9380e5ad787d324364de2e006de39ca67cae1e7b3e728530ebccf9e73959d76603efb8e32e590a671eb1f726e27a0aa3b4bab9dacd39c8729f0d377554f63367bd9efde6b1556d1b4fdee3d3cc2aaea1dbcccc4cb2907bc3509bc9375d669d2ab5f02a80318aa6992f6d55756b35aab2f0178decd443dd4aaeae6ea35abebaaa67aeb359c72507d5d5d4de28ab7c326b556573d421af5f09a874336b9aa6a9d1210552802acb76fbcc690310b5c85421158d71848e215adcada325e856b0c2471fa0e20bb97ee192d7d827813a5afdd1aa573d3168b005437953e63c422a85c47626b98f1841c4643e0bdf27af0921eebac1078d403867dc6a22ff7ea3004fd3b3da5216022a01f827e2103e4573dbf6ae54ccce3d7f3253d614a5a3d48602ea05e4f1209236067502fc8e560462c267060851baca070d32a8b3a20ca7815ea6889532649b8b08857a1509a98a77bac306d23030a8d5761d044cc6359372c5a6ef12ae449897943bd543fd14025c1a54c9b5804b58e8e274a945884972d5e8769dee0fa2a74c7c0ebf15921538a28ce7825807e9ef752fd24a47a06cb4236634dc24cce4d48bd50225651ac1ddfba0a3d4f42d4e30ac23bcb9feaa715563bac421ab42082b800738c87594db5ba5405ec18f38a7a124818f5dc09f71c8173dcf0544f895a1076cfab5a6badaa7a755615b2ac5015b240fd23329889fcd4b7e7ad9d7b7c76f513513eca9eb31b9180fdf3d0cd4435af3a7280ae0417af6971eab07eea39b5cee46ef0aa6ff8f3d7ab47e991dacbc42c44d1130a65efb91aeb0885b2bfbec65a28948703d0ef389edfd54f44e2d71322f157adf55047e87a00abef729c15babe7e407cd7b31686a0df0012cabc6cbdebea074409655e86bceb2265628ed07d9efb2e71cb77eefac89acf45a23e521d47089526a9ee5c60ffed12d323d55de2b3c37548e47dfa648c057113797967e4c818f8532855a04483b8e0edafbebdea4ee421ebac737677c3da37d06587eb30859245c56fd720786a9a6637b3cf699f55a803729cea231032514de5d4344d7df50f5aa83269c1d933f42873815070ee4c83be5b848a12b332ed5737eac2b00b7461778144d8d57a46a15c66ea7c37faaebbabcc7797eebaaa50efbefb1b8eefbe5aafba3a22edf02ad325d5d3d7ea55df6a894f533458217260c9525596234e63b810e164c5159522b1baec81b65655e6c493144554340bc60d272fd21f41cc34b172e343199bc18212b704e3034abcd53431690e93e62e29984f3d4da3508015318324da166fff1e1d998be7110dd588382d3ec0459421534a365130e202e8a7a2d75a17241b0cbaa9380da19ea874275b6ca379986256290867040008000318003028180c85591625415072fc1480165aae5a5e589749e47918c330630c00061803000000001802223445350177dcf17c5bd743951ad4b1bdff03051a70ccb0a6308c2d9027f822fb8f7cba32d409d5e4767d4a7dcfe0593c62fd4e903f5514c334ca63a67095d62333a5b1dece423db8274569142c81be022c432ad914ab934fb8264c4481e0c357530bd5f7ea03e64bcdfb97f1339089ff5b4cca43d622985e288dd1d2baeec65b03bc4ccd336f488123d7c1f3245f2ac4e01856003580fa16f27f0309ba1bfa127f73f8bf3503f8b81877069a0b2a1607d93f9731eeff24fba0a8e077a69a92cf54f4ca7cec117839b525330b457909973625d21b145585994c51bbaf496e4948a4527ecb52286ff6c50d172735c4890e32dacd468bc91bc6f53b86708120c963efc48fa0d72ececd0392376e9bfa9f1fceb3fc4aea3f53711671caa89d6ef86bca12776ed48cb43f6539e62e8fbe6f8c4ae19ecc408ec50a5217a35ad941c58f4d8c3e34378f5843e51789b414da345e67619526ab3f1a35577232d37e31f9019739aed261ca68e22ae72c079f556e3d8844e90b715d99b825914de5a4012d774d7e85f05efe16d7a5a09528b047a4c12d5b4c2b0d0265687a9029ebbe2b748415f60c2ec09a07eb341dee168175fd14d715a8c5d6263e7772aaad39875a4d58de7f20bb8cff99adfa939885f5c83ea23ef372dc639dd2b6e46f3428cfb2fef79134adab3d6d2c198fd32cbc54bd3870170f85eb5b252dceecd09819f42c159c6d58669db1d15925ee20622f04b204e5939dfe832455ea8c820dfff4a8c4259bd59a239ed8b49dd7dc84acd4a98b12b635342d71e445b7f4d7c19664ce63eb4c25712feace4d8eb5be0c147229badb11a12a7151f0318978607db308d70adc409e8f2adbdb2c6e2f48689fc77b1dee80a58f1be997ad30969161fa588a9890ee84e46ae1b8928ed67da8ad55ca356b2df35671e6581069f2107aa7aa9a49ecf233b1e1291bd1d0a52b91a38bdd3c08aee2ed6ce3dcaa45fc9385b877497e689ac95b4d145217f4888d41a9c617995b1a3ace0952ea9702c6160d25be345742bf48c422e7622e2fc09db595ba88b900979db93666b6f57f98a9c73a36d459ee15409431540630c8c6f908639ac7ac872667918b09aa3fb7c58c8f0ca429c64250a516a93e05dc62101a930d477d9d725a18f03e23390fc61a8c04f78d92ff3952ff9dbea9771d0e89644edb582195a11d397e132a44103556d0b33065edcbe121733994cfc9b18a02b6429d0ac409a167d9c283bd5669ac0f2d051d7c3dfe2c31ebd3d7d616380834bdb95c0266a8ae9fe6946cd7d34f09d2a122ca594402674d3f7522705a4657747dfb8f611bd4ad48397056165ca80337aee88c6f339172c71ebc6ca575e02df707dd8d4f1c4c035a8029a69aee16b1c7ed9351c622ccea69de4c104f8f635fffb6d6dd08111b4340d846ac6a844faaf1d6e48720098079718863752afa9cde96b90e0b2bf122e5b1724b95c1fca32e665339858522bcbc51e0564441412444447b9e29b972c7f7617952188a5e61d519d3aee6d0fc37dc2ccfd063071353faa85b613890a5db57018ecdc47463ab7fff6de20436911d85f8f8f18e1c290d9857bf890193219980c653a23188851e74e516a0a652a2496b256e2a9cb6bc0b32924c6ad18c78a0a1b91993584ca07d2b7a63ec5725e3a52a7c6f48ed3a4ec3d527e8df30811b583efbae85c4e116cff51d36f8c8126407613a06bffb4ab271236ce0ad3440c93c7afba8a65834704bf2c33a9c407d5e624739332e3ead2c1890312f691805a748febd9edcb39e88eced610202484f4e6c2b80f4ff77649f7d0526a5fbc65d8dc4ccdad7c708d9f5d6094b290f97d6451c4ceabe1fa6e8945baab22ea6383fcdd88e9b81c5a40a59da89d2b5ddd3a0e48668a77be93bff31a2c6912b82c5fd24aead749d183ca348bc4bf7b833edc8f9372881eacaadcda5b56b3ebbf199ecb04185b605462f092bb065b5f42c0a7b3f80dfdafec377d66f6d720cc0224c01f4f7873403b408401b41028d749b61fd7d60f06124329f77d521d8046cc6acaa698289386699298afb82547417d6a0aa2f0cbd28900dd73da97438136078c8fa65df8515ec1ca5ef06108eaab8a92c8857f80395ffdd71a1f34db6b163dfbfcfa21d7e9f7ae8618aeefae6d5833f728a6a63a1fe381dfadaf9902f819d8d6a1fbf49e11c9faea8a6597242bf7a920997ecd97470cd19e12d3a11aca7179056e2d1b5005b339653e13774cad65a083548f2da36c02e352c53a9ecb77917946d1ce8a0278596fd576079510de65efc9f37d2e7c2375a9e42527a45d5bf49d3eb79b9b34373e9f693c5aac7f2e72cc2de4e5f4d7bc123f29634d3b505c5594407481c880acfee7cfcd4e5926ada233da797030873f18d6bb02516a34b96a73be8e9267045084a096121b4d6e67e4ab0d1ff0138c92ec33851e36e3a4baee473bfb98ed3c5c34ad474b0dad543546a7fd119a921054fd0818425dd62e216c478479949aabf09d0229f00ced55bfa4fddda5b6cf1943f33010c30cc658a726e1eb15ad1fcdaec59fb932c5b3b2d1c5c350d03e09d7442e36fc88f1a59faceba0fcc0a916f48ca4cff97a317177a71d493d3e3a4d7627a327a5ab7999b90f53aa6d731bd1c5d8c9ed66de676b9c1582f667a4dd693d15bf4a840bd54e1d0ce423ffcfef3bd0e81cc92f10fadff3f98c86af1ba7f9438dc596d9d881f853988963847539011502b4c7298ba2286913e8e40682bda2ab4cf4252f5ed8931843dd71bcfae3c260e934692f343520251d94eb7bca0ddc7133cd72f6e6417209f45d7787e030c98bc5f2852af834a634b43e02e83a21d69e2ce467f8f0becd5a1ac302b87e4dbfb8a3cdefba1629980b5f6e744aab68ae6a6b751ea2bdb91a06584528ff2a4172ec2aa4b34d5d226d8f67ebd6f4bf5aa86c0c016ef435d431130df076622dc3c267482fbb1f76cbdbd8c15ef0bffcd7b327d7e41fee7ba62f4fff1ee4ff9589a1ddb7cf56138fbf422654fc09b7aab7e95f6fad34099f3014c73c1ceef5090e78f990d1cc7e5cde4e652b36105c60356b3b8ff09e804e6599bc1855bced6839b830653448c39d4a88e36defde487a837833f973e6133e9d3f8c709b72050a4e32c91bcff7ed4fbbfbc5dd8e91502d55186a6ef6d04619160e9b2092ed81bdccf91cbe376c030284ad22cf2adbfa671c88a4eb1bcac379edf4b0cf93b6a824268b94f21e82cfe768bae2a629ab00d7fe7ebd13382be0a1908f6148cacf6a0eb85f42a0574769b91308fbd084e15ed33106ed59eaf875d4fb0633ef3f046c09e88b3c2f58479d7d6662e29a64096f92d0d49dc4770b0be83ab0f4781c78d3391fa54a5149fd6639836f302b433117158e0076ef8b3e1246051db7bdb0238d39b29032b62dbd2e45885bc389a11e151f1b4b18d13d8cebbe061e610176b20b6f91051b7bec15072991db9801f2dfb9699b2b0c608f0f7ae8bc7891d5ccc88d7e85688edc9a6b348042054e9b6f6a45ea51ef772ab071f8e420438fdca9afbf16811ac6fc4be1f3216ddd79078fbc52a362808630ab1515c1bf6b3c3f6e69b8d04a88f30981e43613bf0e7062079eef9a17ac9ff2eccc41080a20c448ff15e055127cb47c6e34e6192acdec2221aa04f930142cdbafc91bd7fe2266ad8b005cd92b9fed089de0d44df92fd9f349affccd1f2f3d3b6315c6910928451a3fd45eef68ace41044e4d5b2e58ee144511a2653eb3094acfdad2a4a63692661e918f5e770d0bddb47003c2db445e4574b12ed763164061b75592ff9dc3a4a71eaef149eb7cc81f56823bd5a18c0c207571f0875478b5fa8810a4238ea2958880b40192f4dfdcff7b5458bfcc620c79ab823dd2bcce6e027d6b67f357ef1ed9892aa5591002939696d595c891b2f01dab3115b92ec63d9c3100057c2a497557a29e2971890283406ccb41e52f71e375d53840e5d2904798bfe69a6043be473c94cbfc4021acdcbc49bf652f60c8e53893ddeca9da77082485f7387ffa464373d3682b56451697b1c1f0f6d9152d5973b834a27673ac4599254ed1b67685347e90bff1a1db1bddee69406eaa021306ae8388fc8c6fc5c0d444564ca0b45566c4b961dc690aa23a47403dd4c8e43f5241a624560d87406b27deb44d3697da14d33ba4b46d066ccf1cb53640488d238623c081b481c8ec246ab57d4c580ca17871501d82402890c3c113d885e188b3a87a500d34364fa4635b8a0ec900999e0e844c97a57bf86fad132b55b71082451366fc20711132ac24cbd7465cbbbb4727213a520fdaa75b2adf0258242e2cab1414992892d8ea8251195542ebb1d4fb038c44f9068f6512eddc1df88b6572e86d61c400a0ecabfe65f624127bb8b0b210006373d6b3225a5c80871ebfa4bc93481b04dc42c627cd724ad69a48fd6f744d494a53a9fc8dbe294849e304bf1dce212b9aa5f657f4a62145f3d4fe88ceb4a44d034ffc629c53ac3543c5dfe94d899466a9fd011d9ab68e1e1e57c21ac79cf476b5b802ab2a9410e46645158bfb1d2c75f5100d101e33475e78c7d3cd118eb1da24f77fe63f2dd69be1f2effcf73416c4253e46e299547c60a6c2e4ed579899ca8a7fbeb4836deb4c533c2675db96effb035bb01f7403c2486ae8dabe3d29d8e3f7794c62146434df6b7437ee77380197d2bd80dae3c192b5f39ff2ca6ddd9836ed03dfd20357384012dc9df5f74df9f6305ed7bad6038023210b5b9f6e712b12402596e30ded09fb872321df858e85f5fbd3f64531c1bac018a302cde70dc5a2034677b024e951e8f673215f705b672a2146f8dba0e490657f72617356389ec33a04eb22b3224b2208dab3bef06ea0ac025f15ecc6fde943a59d8ade6eae3d48a5109720abb9558f0b5b7c6400901e5459e0f41c30d996e05dbf7588b5eda277732d2e365cc18b833c866192da5e73eb0bc193548ff3b87dc4ee0f4f0b14f4010dbdbd1ad8a2fb1c4ae27aa70d2fc8868802b42bbe508dd800a5f90428188dc215d93b704f0302761328207900be35d47f4f194a0d0c42c236585d290636f922e002bd74278660ebe34f2d88a2ef836c7232981d839280033ff0ca6101269e29a0823c0a88ae2a41d9a12b9b0b092cd5f7831afb61eeecf83dc2a2c698400f7f8311a6361d64051155fc014e3dfa6606c2a3882a79c9901b77570a88545c0aa31bc9fa72ee3aff963ba8d4c505d077bd7e67e9b2830ed2fa3b45b706a678192d7e1811c4e923b4ef6992f48b2d33936c081cd3fb5a11543681dd22d45e92fa88f187b61653328ff938f3104e6f999709d9979cc2061aded002086ec38776aabab7df09022864222f09e734da21d387893d6e3c5960edc4405007a22a8e03634584d06cf8d66d219b91a595d40557f26fad2aa6ab6a2653d3c535acc3d86921481316025713b9d793602b0437ba0fd1fad7b9631a46535c182afb7f6932b2850385ee17210bf545d55bba7f0b1d0b4ed75a30f47e28efd29d428d8894eb5f3b499db20f2afe8a2a2259599dc4a79d721ac2891ba68cb4b78477e3970fcfcbe7e072c04b55c18e811162a0cbc10b94824e68b81c3c74afb99d194f559890a5273230cc54a1d86d2693951fe741f6376b7e72a1dd4ccb7055701f072ca1749a2d4474bfd316c2be8ee5c9de48d3a9f2e4b7c9d366f0c0a92b297860cc2179015a1a029946032f2a588d12236079248972106b953d9a556e7aee9d2711b445fecf26e41c225b4e0e936b890facc9666170d04d7666f3214c0b8b32280d6599539251e7dff2546126774d12623795411d820265b407c095a5e7670238aec60e2615b1117635655d8532288d32501ee5a20c4aa30c9447b92883d22803655d0ef49b275ae55c83ff495111a930115917a024f38e249c852635eb6594003e485c6bee8a9b482f71ad791e234d66289f3314c3fe20c3c66ca14049912ccb9602c0936b0d41d9b41634381aa78fa94ddc844676139bc4a33f0be0180e52df5d4ad62e345ce2a90a99c68e5823114d0b50fb19686b2bad9966ea247c03059abe176dd56c9e4280c6eea38ea0f5eda92bd50ecc5c285cff723e3376b66d6dd83468dd9055d0dc80a5323c88bbdd4f6cdf03246d140fcaba2e449b7dd4c55eacab7b8048bb38e009bb3800509b3416d1497b91782245764f94444d0926418dce0d01aad036e855cc6efbc928f12a7edd6b728f7411428d9a7de7d3a1434964005a4f7653f0c00d32704b4a109c7c44b8134e55732741970dbcf6b316ab54b209f7590d1304f6db34f7a102095135af174a2f9c2210cb41576748b881d2f41249820149cd2525f21fe99b0149e20449147bc2f83f1a6351789d4d4cf275e838ce353efa9c680922044a617420702ba443b48ae5189b78696d1e0f3edf86a851e81959a7891f63d2b2778c68cde55632b66ff8c6792e6ba96c0c01dd827ae56c40d32ecee6362aceb8226d03352a73d086c11840237b54b0a05ca88d34a32a7b091e19c7f4b6f46204cb1ccca803fb9b8bab26a9bd0b0193a8000c9215c3279204948abd832105810e65d820e3886c4a28c94610bff0a6c27dce41fcf1a7941454a19d545c42e4a8ede1049f5da1bc4f60d0e08af13bb9ca55c1ceaf10981d6195631809450c2ca322b9d1649431dde5c51f50b7113e2a1ac26009f1034ff086b0f0fcf9e3b50767578023381c70ba861fc7262f53c45f301bef2436060969cc8ff577348079cb3896501595cb19f1473eae97f0f67e8f0876640ef7a24944bc37f1b21537f3ef86964ab51ac2bc56aa30540670ca97bc7b13850e95ee431485d04c568ed3687ad938b5cbf74bf0d1361c51fab353d5e006632e94186ee1238a0f2a58f27c6563c0e276029551fee13e7e61d75a42f61dae5ed52474275940d9078049ee634e45333cb0fd9f1f3ba04ef54906f377acbf80f530b92e97804483de6f37da13a881b67440c3118235c0a478bacc1d9a476e063e43073511af04cf1101dd7fd37fd4e55045e2e828bee4a8c99674c90948c611eec87543d70548e8908468b9f2064e7ce8f01109b9b191c10ad365710fca43ede1f4faa5a22e1f55ec219bb81e8b7cfa8a234f8da3f5358debd7bcf77c3cc7c920c47ed6656f47e3a8bf1a27436e00294169fc6ed427c58bd69ca7dc7bf3a781418ca075cd7db33ad51c135f45a9106bf5b2f62e88947c410aaf9a028d49bb8c9b7415342c1799c57f788c3a463e97df0d89f29abd0918b4a092b73c3f5d88372bcf126068c0092144951b9b86469e38c79429b42501ec29b52b9f5ac55c730405ea972e14b25afd0cd6d1035795c426b5f83bed80f6db52fc11b1aa049ba3613991f9a7fa6cef227759c52cda5a38c95a0cb20cee766c3d6287d4f4fecbc5f92c89b625957a45d686cc42d2833694935cfc0b782b913cbed174f9ed9be4d9e532fc0531581fbe74e23db11aedf77642abaec68530192a0033f2b154b647d0786164ddcca4a77dd4c47c005e3613ccb0a398ba725e37f035e889d249e2e8f1f6a0f15d3af66e6a52530c7340210d7e0ecbd0e88742b47464e038c15d1e1c57ca5752afed88d3c218cd85875d5e58821c455071284eb472f801385a489edc2fa811a1844b500204f6f04a519cb6e507e4631a9cee7ad4ec56884bc42798b98a11e52c74ec3bb8b6a3348ebb6143f49613133cdfb6213bdd96102c25e8352c4eb6e06149a99c5df11d52509decbf6e05944ec5cbea5819d97b75dd462167057c3e900b376c347dee637ecc6a003e9c059b727006f8e61ae62083d48ed3adb154c170817e8dbc52ed1e66ad109b95c1cb5d3b653445191ce20b330d44ca05d259a5a79626db6720f1d40d5a71520f274095381819441c59fd359bd9949b90cf60cd7364a77d58dbb0120fd08c86788174e0702b1d6a9737d343a1ec8ffa4380051bc0c55abe90e48564f32e31f09a4d83f8b622b75b8f48b10c7c710aa023a4d5b87583b186c571c26e7abfdb1030a973d0e285e23e656bd3cbfdc08a627bce527940db9e70c7c08c58431c1b91e008bff1a451c974aec77fa2a19c29ddcf7530e8dbc121755eee11079e9b56c54281bec884ebeab6d651431d37056f9c44248626c07b26bb4b910431f3201374a9a03ca33167fbc1c0282b87b1b96954d408437daf18eab9ddd9d294801dfb815b92381d9aaf765fb998cf75447cf858efe7eaee4375a2b321bda91854e2bc35992573b186c0156f6c375787786c20f346c8cbf8998d2f57e810cd15b492ee6f2fc3f04a8e055eeea40287dbb43c220543253b95199b6b3cf061f94d24e8cfc21244a1605e0857d109efe9bd610dcd499c49200c7fbac96915ed9f5c8155dfc51e64d8f9c23e11ff16ee963f7f530ab309b7d4f6a5b9a825d86a3528ccafd21fdc72ff8aa2a90e402cddd41602505b60bdee89d7e4eaf52174c8ac0227ae8e16caca5d0b4818bc48c1e41fdedb7ca539c483d507cea0e5618bf2bd0d88211227cbe5f269c82575d9ab8b5c807c14811bbd3f7f252d1103e95583c2876271adf0735ca8c031edad056056d2e13635d9ff85a5ccdedcff8f67a00448ea95371cdd9702296431d8f0c660c1088e96a09e6e15767730ff184c1505ac6e391754792af0c10818c8a73cd419b66da285bbd265f8f55d945056ce2b32e2dfe726c9ba471a62637e12ce55317c183390c74d57deea7f94c9d14162c5d9ee6357750de9cd2864a8c3180eba461babd3751e9a7e6fec4226bd6197522a25bf4b6b9e45f8c9853abd8dd4d56e449f38ee12a45b48e15fc17da9bf77b832d8f30d8468c249f6e95984a99988c92de139271a202c3c53a2cc38a45bced1e7377489ff0844f647e096b2eaeec41c336b7ab4b50f9601fe11a43f4f6b4e7d7549c7886ab269b3493cd0fc44e1adc458117e02d7896157f69f23bda7442511a204790eb23b0a37cf06871f04f8891aacf0a3327239655a20950458d541ace8f1015ba15efa9d95172eddf3d576417d10b7de75889402193b311d720436b30bf73a243d824b5b0c2fac95d1048eb833b77f97dfca3ad4286a5a9a10bd0f6b867dccb87d2d0172da6aaf4fb8b7b4db97c5889d3fda8bdabefaba1ff765588def33a07952e89969bf8e4744c5c090feaa167be86c3f0528cc6d643d80cd4831ad0d1515e3d72bb8c42ffd05910788f32ebb206aca16c47ec73c27a6683bf65910427c20cca211975f2d9d0f6e45dbda9c74654b4cbc54d37693d44b6ef3f4b4c7a161a17ac0ba8bd33d136b483cc1a17169d337772ac41d9a435bc0bc02fa0544b4e3c1232a7fe7c7f21e56065592820aebb6b6461ec50856346533e3a0328a0721933ec52447ccf9ef85d4fc6ed719d058d9612fe87c67330fec30aac5a37d1dbb0bde4df5d44121db69030ac3ec9c89520964eaf4a746d69b225cbd6a719ca20c8aa84b905949b26a25f521e23b6f0f84aaecfe2a783971b4f858613c458629933a5fe66a2274ee742d4e715123533cadb83881cdc8ad6bc40ca72ac9e52787ebc9bc7909746001f80ad085e0c65e4daf2b76f207fea6a32c88b7d53ec8e0a99e42d1e84a630fbf5d7de5d9db198b4147bb569eef363872d0bb10aa6e82334293d83b6bde51f1c8e5d91382f28623ac5d7eb91dc484b4e9fd4956324f7f3d6bffe166188126b20b43d4f9806b312ef7f6a96555be4d8ff4495033306e3ce9082b9262acc4c131eb505ce6819704f564633a21a4d7d42a3569b6f0aa7ca0aced32e9d02e97630d987f47314611e3be2260749c8a9f8d8fb1b8155ac2993e928bf72b68cd18fac157cc4b31a2ef25b8b1afb2ba9daaa3f8a6b8b01112e2cd822827fde0ea27611b39ae7d9ddd2983a955bca7e88ebbf049bb8296c3188d9474eec33c9b1505265994e0e5bce6af02474f4cb8d2383b23d39fc87c74ea70ceabdb6d4c3d89c089856e093bf7706fc5fa5a1ce049409d1179866c155acfc12b12a1f41c6e99565a0ef8e709c664acceede5b06b214641103cc0044272620e6c5123f67fdf9e5608c26dafed1267a0a9d55023ea2f6d1c2fc35db80e4b1c8275949a2ac9226ef3c6d99bcfe9ae4df0d2cdb3c663e28a2e58fea753e1b4164556ea6f599a5e08b90b000b7f64a96defa1d78cefe28a18d6f23462d99bc413f5a4914c2bcad00329a3308d84b1c281220b608e57ea8e098d775feb60a3e221b15cf2866f40424b914715d9ddba87e205b0061411fcec3cf92d4dab2311b7638d0af2c49840c64a1da0bc332e7368f6cb0fcdec225cf7c7cf9b6c9c84580ac0a37eafd90c7163859eb6432102c527149799ac441681d27053f4c3a6a37bb16376720a5bdf862a91a79db2160d24c089aa751a76d30870a086a22c44a3a0c639184250aac11d0ef1c9bf6d34fc340419f7521f2a00a0416d88166ce1a86d40ac08d0b117e17b037e8f7de39bb47f6d0a527be6c67a4ec55ac7218725b0a3e874fc29d3c9fec0405ba2284469ab325da9cb1c9d8325e9399c0fcd2affd336638e081b580f7f8aba94e5a038391199d9ec9380df3c73b7f85f3e87a270ac5b39b60bd77c8597f5bd4c393e4826c9e0f5fe2fc07bb0c340147729e16ce4b9862de4454d1f6e811ae3010c41f97b866e4f5eb5f56af9e831802da89ecfb14d7e7437e1305ebfa9095f18a593fd9dc0c246b4a176cdfe452b9000734d7766f10ae766c41c3350a260248e8137a42bd85604c18f86c155fbfc0b5e1340b659663e30c040e978ba7c0101174449610f7cf527078005d0d5831ac22df4cc0ebc65357f15d132701476fb8771145d303fed3acc1a9e1f5f69c4add531fc37d5eea9aa7bc1730bb42b3570942abee35e995f603a81f80d3b92d541fca58d81d1d705b9022b2d0b16ce3f9b70f813a9724ce163ce6b2ae3cf217a2bfa78e755ab66feaa90bb0f2da802acf5d97ab78f3bb0d4ef5e2b72328cb4e948c82b483049a501edff4580ae5ec3a620b7c2a1b4ae8ec7086284928397bbd10c506d8535063fd619b0ec3407bd8b97c9ae0e7d41121457ba8bdfe240d050d687182b3d909c59264df630aa6a53d277c2087935cb97db49d23753c3807ae277f75be59d8f743a7f53a7ce56b0886ac656b082747ff038060411cc6cb57922124fde4e348b692db5ec136346b720c048bb309e4694e886336575382f3a02aaa2cfc3ba889f7e61920f98c0a205882a5659a081a1f743ca7a52a72e1a54b951b0e0f585190912506661f7850c7d60a317be921978e2465f63b8ab06d768ad0aa641960639aa873b112b7d439543b97b41acfb7d13f59f6f90d1645f0b3a1a68b2cce23e374403bfe1f33d1db705561d9a478c1800a41f9aa8ba3386670c3c01aa05560573a6d0c81d1c666a88c0f265c6830daacc930685548c48012db7b92816cfc7439a6a9822280998465778195738c29ad55b6c4fe05a8c9870e206d1739790dbc06b1dd4f1032a9224e295ced223710638a72572f78fa4f7e80a2f436a11b5b7d3b54fd0784fd77098e418e217f244a247634e0c1dda61df72be28c607c2a82b2f0fd2d4c414562f1d3475f089f993aefb0accc919bc6426a77b0ed7c3a01da5aafdb8bc3b0489848eef561572af6cddb232204ceae747c8a7853af688f1b8d0f6fef12623cd82697b442e242982e10cc22add3e55963677dd602589849203b817b9ccffe5440aef1b270552c7cfd2722a7896687cf21325dcd5f43b0e54f1f801d3ae24830224227ae94e2fe1885fa79781bf5799cdeecaf05a53db5022fbb5a6649f24049b0f6fe868bd3a75e7aef1bc721b76e8d5e39dc376481574e2e858b901c729269f829ad16834e02dd084eeee493490216b079373d3d426814a84d57382633231b51e29c7100faca480585ff329b7462a2eb96fce3212e19a478edd276094dac166cfc3ef98cf43dc753234e351b1ee243cae74163085c1271c88f3767d192c6e76604b3578541fd13331aed8c838cc51556df3e61e657754f37f508f1ee95b3d9bdbe205032d82a2bd075278f97ec53cbacdf4abab8049ed3c127324144cc2b1c1eb3cbea1513d7094bcfa4f9ece013d1bc4d4146c326d345c42818f5591fc328e8ded33b88be68f5f225165617f454f91ec0a492bba4610f61cbbd3748a21463484e75a352d9270daf706b43ad225ed68b4d4f7aa5cdad083490e095451a224985cd5b9c063350ef89c3ee0d45ef7301e091033e65452842c101b9cfa8c29d9bb33d63b58f6c408d46cc9ee62bbf635bba2c2caa27be976b38ce9afa1e4d8a627db7cfb3847c16d786f212a8420a2144745eeac357d244abb65d32169da1c78205cd24be470f768f84d1b3a73349ccb5352b903757eadd6784a3a2d2513274882fd744a258d24213040aeb280b63bf13f0c07febba78423e3bfc805d70d6b583a9b23a64670422becbf9d3530ad0501e65d06e94ccac80394e64d9cf8ae88fb06fd236a93d7dfe868f20238b8b1cd89ad62240bf8cd69c322b8777414eeb1bd78406c3b8680933ab19dc1f89d23b376e39f85a92d3880b07457b14e595ec02ce7d909e6565b44bca67b1e925c618347402227ddf76812db54fcacc8a22996d44d92eeae9c61bf8a2593e59eaf21d7ac1c00ee10a290d7cf1648ccff59a51c58c9bde5456ddfb40a9f60342006e3905ec833160fc6ecdf89a83e3a7231ee7c1acba0d2d12e97ecaf437f31786431698d54325cf8b2372f81a362e1dc2c2c9a84168104ceb3db07159e893283e5ebb9a9069b6538a1504d935406d7e2479d7be764943fa2409ec205de93e95057704b4372cdb9c2d6ca0dbe64156a7931990b8c6ef30ef55b44d0c3f3ed9efa4ed91ce3b4ba6756a9790908e35f6edd1de4e63adbc717f8bc4ded451dbb979983f80a4e59a2ff1498efcf1fc17d78d481f1b334816cc5ab521fa86d3a06241c6758f9d13f9a2f34c00bba8a399a60bca38f04ebb762e4c61398bf68c38a63e804690c9fa99e29de1a3c9745b1bccdf4b2e0e783c5ccd7f890905ebc639fedc119a13e2afc3d3d51aefd0d0aab93ad2397f7d442172be0306c90e2e313298abf9f9b1a4fa6aa748bd2f9e3c95c2446f3a6ab32b9f593b0d19e7b8b743aecd1b8caa6c5f971ebccdd18832f4d0f81ec6e3b694cc7afe4267c3d6af2d0fbbecabb8f3503cb04b1359eb74792d8d2569cfca63d471d65806e32297fd27e31a63c02ade72b9dbc6388a0ca7625048168398b186e4fb1c779da27d8c1973a531284572df56b9f8c2ece8f9f4e23a6401d5b664a4c61fdc64cdca96e2815dc87ec5a8808005a7560c6560477d6aa61391707da8aa6c6781b3e9afe8ccb9dfc18090eb7a8c41cadbbc4ff7117e8b6e2a7174f0fcf486c223d32c2361181fe1b642b0b2b7a10cad1233ae1019b0375f49677689125a5735c574446e75bfae7047e56b275ed15db9be11a4f8f2952d4f514d12fdd918b719a697f6156496340d7c97078e8f388f9cfd17c36954f2ca529eff5ffbb4d90ee0d9b794f54702c9fad097c9a6bd6f0dba435a4181c177f733ff3debb7614a81ae1c8279daa58c1e3b1d42eed4b2705fa9858e30253bcab0b8d84242ca22e67ae51e445260aacf5738f89a5a35da961d16d7f652961465a793097234531fabbac357485bfb1037fc61fa5ec78209012096eff061b4880223def9b896d160af185f891b683184b78ce77432c84fd778cb711091ef81528f1495a30815e969f07b78c50892afdf2de5321a388ccb9d9f21b271b3c57e64e746be5695fdf41c8a6631732d4096324a120dc87951f430ffe5eda6ce48a151e2b93d0f83bb5d447294ce12439d191413b7d93434a90a102ff070e01119f41664bd6ad748f3d6736c7ea05b9100d5e4babfac36d1b4f847c62f3da47fa9a71581a890233924bc7432862e7e67265be6c18ae3589fe406756702159319f05c30f85e8f3b8ee75761f96c440142bda0c4a2938aab294469fb9ad8e4cef76bccf02fdea3c51f22faf128caf300290e504754445733be80aec875f5d5cc4bfc2043fa2964400cf294c1b148320dfc1e8f7a741274feaaca78ccd481c6a00022ef4430a11a42cf97af62ea05171ff8adffba679a91c7565ef01e92542a0db0d9d496a61743718e33c8d65345ca0d371c4b70bc302945a78805a08fd75e93f59bede1c95f5be0b1b1349ecbe23b14d3f6288d140b16418e71bd39729ebc82fb9ebdb83b494b5c8bfbfb7088a9425c5a070a3378a67865f7a2ef2eb07eebd257c234cb9ec2c5ece3b970f0cf4c806bb0b7da27dd4b9f9e686a86ab5572446f9fd69047657840bfc5eec0af03c58927df568f863fbef7a6eeee3cb788bf3906cfe4486c4d038c6c1e15747a3c0bcbbab7c2a292bdbfbc4b950db8fb867d71fd611763741586beb7b3c1968d20d5b68eab919bdb73ea5cfcc80e03942033ac093179e370c6d58810a4d92d2b9b0afef0e3650814e4dd093dbcc9e0f01d08fa4aaf65ba0791bfc63614bc6a0e2a60c43c84aae1453e30e7873db027404248a3da76b967cc27c4e9416a1b9f31395006a4e41bfd747f0d8eab36538383daa2125e3fdf33cc3bed766e7c67bdfea30e18fa44d8167c7ce395bc964d1bfd3aa114b0780d83a918c4d3ea2ef33abe4bae88be492c92994cce77c98963d4a9cfe7c9848bd62d88d48f5c59ecfb87886c9bbf114d849b316da6bff12d95ab872a76ef4a4fd2049663ec073fc95b09f71efa2d2cad5121edd631add3779813ab72978b14add0f80de03735beed8c50b05778d203319dd1fe730612f553d57290984a16ac1db12cc82e5c90d10bf00dad3f6013b66afa1c5078106bf4bd6430638542f30cb62199493368804e671d3ec95adc9d80aca10424933171c3054565b61e1674455ed1bfcf35ec1422e430502333a75cec4b73c662fb55d01da2e52e0d88600951cd310bc420a4fd844982a040c344b55b656ec7a8c5025928c7f4ddee5f0504f1af3457f4b833af8f22ae93e79af513e0421f1ba467feeeebed82d76ff3f9ba600ba7bf9d86078f6dc11690846f8ae840e7f03b369600ccc6a7f1430e70c6d7007b0d0796c802718f1556a2d605c71b89e2569207d8573a54b0cbfaaec60ef4cb721e0236422e1bbb6497d5dac28eba29acc9f07fa9969b9484316bc027a0136660cd6960a42ea49a533482ca7daa0e7fcb4a8b4cae21c169a33c7787482c4f24c343041e840dd63df9af1019de8233c1d2806a6e8a4198b42d26073e3b95cf8e66031784ab93e8168c24f4acd0be2adb37b152af9744ac9bc8bf32072ddf1ab63a08fad82e6e414901b6527048070ec7dcb80a83d0f230144ccce32a24233a9dad9068394c99f74f1b5896c32808898f3f57b25042c86ecdd214347bd808a2bf9dfc6ff11ff53f13b43a08ff1afa781f1c2f62dc6a0efd9bd01ebe92e910c6dcb6865e6871ef8bf23e34c0c0c8f39667dc54e0b4cac3c82311dd2cf7227c18e9f9bff5998599351dffde9d9188b83fd1a25fc32dba7658465a1532ad40551fc843c7085f08595844c6e0512107ff2cd702c29c7954097b45b7b327151460670a0fe1a838541f716ef762cb27f38156995bb030d6d97676312f1992a69e01c6785d268a2062f8c424013177d494c5ab8ee1c8679a1ce31e519b27b7d1f6956543f9d6abb66497193abb989b56aa775e53466daa1f71835e524aa3183e67599a26b18cf6fbf21a34f0a3c7087e787454d9f2ae3e3a2b29b4521dbc43a3c4982f74fb36d8bdb104e5189de740048f893451365e3d68f8958523e17b4ca105e568a017fa9389aa2bd915882b2ba8094082613c4a7df77108cc0864c74f0d141fdd24d2fafaac6247077ffda4b14c66bb31e0f146f7ba4ca7725f3403db7b5bed88e3bc02140bae5391b4cad8eac5bd2088a938b498eaa98aebac09cc8d722cc6b83015d57511b83ea0609da006ac86e7f10a51960172a4bd9413726c48063fa93ac9969b48430c313308e4454c3d8930dfe320617f44ad6a6ed62f1054d12953410a50c832e0fff3739b5bddcc4a4c366a52decd84b9e84495277a85e86fd71bd1d62d2c7b262f3f0ef031cbc853a2e4711d7ed0691f0cae477f8681b5ad04b739453cb2f8422a3b8b289d0118a91ea1e859368c3e9f0e42c8bc3bcdee2719eeaf29675d10ef5b083d052d7315052cf7415f8e0a0c1a25f25ae02aeaa801ba102785262190c79e3588e4126ee5024ce82d0f71212a653ee64371d5085490cf810e98595c984ccafef465b90bda1e263e201acb1372c9748e941cd5367aaac1c85246a4280f9897892529d82c48b695a9da753137d703033505132239a7da43bc5c80c99fbb1bbe61424f18040224386304a0fa1f9bff1ef3476abc9f7bb52e67301080ad1f7864ae58969aa0c61f5f83265c0fb77f53777c76fc54956d7b78c8838dcb12e1808438448b3c1dea9e5f3246c13fbc5d56efc612c1435c447db7cbac0b4aa63cd488a355db0d421970b79392472972df2ec0a654c7cb60cc499dd926ac701f5096eda78a4b6bb2a33a19a0b81ea7859a79ca71b390e2f72bd68e824470145a2440d180c9c6250f79125ddacc79b9c18ad199449c9bae23baf0938f69528d54a9764c54db6ae31b8c15f4596a6771c876191e47af9b3e99909a5d2370a53da280ac4a3e29014fed6f1aa4c3a384aef995fba773d517dfab329f4ea8e8a211cae2486ef430de79f33281b855083a014dbce81caa49e7fe6b133f90273b653ec59596dbb0184f2798287ef67b3ae4a7c93c0d49071df0e9ab9a6e53958414a4a7492cb294d53834e40d4088e9f64dd77da05fe93a68538270d5fbf4e852e402f69d261d82cce21604e14a03790044b3d1365637ce6164d8fc9959291434da0be8269bd718363ed6d9b6731760888aec9a055dda0323898765f26e58722945c34907f285777808032e2f25274b9c66f927afe9e86ce310b98f2ce2b6731cbda3938c815d38c72929939425950ced27f16e58a496922d78b128220415402eb30d33f764490df3d086cc04c2e357387062bca6b4945471ab0718d69ad76645abb3f0926105e7b26b601389ecd2b6a7550318dccf6b981aece8feabb165c86db1861331ef118c31821c3b5ba6d570aa8f8166f6e6d3b2415967c2d8bf310ece56c422843300a8a40d4f09612ba63d41a133951f3b9fc7bd72aacb3055bc374e2245959a57fedad0f0a19b487cae13974464f354a824922e51bb2619ac71ae55e009ff4ec739d173f4d8c1bea35a43c03f5f4cbd2bbbbd6be658e9d7f967d83f32c4729524f38d69e06524a4a7fa16f62dba99c5795f172b86e44af762920f5a129b79b279f71ae8b025f6a6258d774c0e7f0b2b72205f55dc7503d96559fbb1c122344a00e1e18fc7c2709854d064347c2d2cd543666ed378de78b0be89fce9b585ead6cb5ca1039504ee88d8a2fa510790b6b65b6d2d31cac0307f918cf9b7351390674004a0968ca9d507574ac02094a31b377a64b3411b0cc188e1f59a420639a8d88fc01342a81239d748851eebc3b10b1a2f9ce7aaacbe2c99df2919696b90bf8879a277180143c9d5ef58d2256d6f67c323d9cd4d512c43b5ca0052e7d1e9b681b85f87c143849ac1a2c4654b4788412c1639c0ed287a1ef3057c7b35845a606926e6412cdec5038d4de2a2147a9da2e91f045a0ee482e518b1a4625d28437ad84f8d14aeeed1eb51d6ff086a1efab35843f4843ce8e6cdcb9e5de33ec1648e7d29bc4687ead6a2a820cbdd6322dac17eebdae4f93828179676e8ea466594e1eca689fbaad4d756690b2446afee138f7f7d29f83c5053aebcaf12f95e57264c1c1e2a8ed32090d99a5c63293725fbe9a3f2d059b7f0ca9a625ea22768ee051397c68def06110cc014c4fd64e2bdd52a108fdfffb138150bf7f554bcea616fab79fb01df2714f5ac076139d9a0ff080b000149bb058f6b5c02b214ad380989ec1fc6d2d7b1c01cdd0efc65a24a2255a445113d4bc1387cb578d355b0c6650bb60ed84c3b49debf91380f4a92dd63fd99a000d300248953a355d2892cb497668220bede1affe787b8d3d4ecf9e9b09e421d7d0e5740119ad50f8750680cb85b4ad67dc82116ac5f7a9ed3d1a6e8e9795394bac582bca50939eaff43fb1e4352081b327984157712be71dd4ee51a5a6ed443471ec058ee94236606b6ff28e83f172d1cc29ed74eac52fd8deec7a7ea5cde9a2a84c380240508995c8ccaf95ae142d753d2848b8886da3bf18fbd7da7fc935b8d087b4c9edafd3de4bf48096c4e02863a50615a5db2525fdba1258d97aa43ca26a2cae12a14c1a69451e57867b4d2d47c3bca4c51973958e887ef19a3772c7ea735064d4aa0cf9346da21f1247bb0895c0115de0b911b5f80f553d75057a4cd25bda9424c2b7c71dfdb103d9c6b9e7cca33dd31a6266f008037a6c418517d22e88f48f6fd1c674205f79eeab54737cdcc32f9221e3244aa29fbf50c250de81e668ca4228e09a2f38ff959f143c69197f852ea7ee8f0ce6ba08d79475e4564ee3dcb77226b1777d0aae77d2ac6208baba59ff05a3333e49d95ff54c26ed6d9a96aca0445a1656a7f2c6c03a9bab65df0b97f1d28d26f1559d1847ce4e672c580271da4d695b7bb7771f4fc7140590fd9feeafc6e13485bc64521d76bcb9247af28da6688742161b71bc0373ecb5092959cab8f743e01216f3605b1378f180cb0dc2f6515b310064734382f2141aa24124cdeee28c5ae0c6fc98713a5ba194da466a326a375886dc06c785972cd7c15fb06d4f72a66d258a37e27bde5e2436905ef5ea5e848b83c05ebb4d107c8c4a3bd70b5e2fa21e9c2d83db1708340e6497e819a1bbb7e241170e08483cb8225064e33816f0302e9f728009b0ccbdf26bb741e741809872234bf82913d6c9b09e6220f88dd9d93eee82e6f5f7bd1736a17ec33784f4e2e08daabe56e4ec0c7dd277088f3d56abf4aefeec04526629b47bd5e6481b63fd46438deb01933ca0e62fc2f651254b62c0c9a4ad91d30500c84c4afd630ae47ce46c604e1f6b5294194d1ff7b96d0307e636d64577c80169868459ecf816099f26953d5d804235ae3c9f43e8bf066525a8d812632fc4c0f10e1b70f2cd63b542dd0e597a9056621f6c20b1c0d49fdd2d58957f427a8044c0c2f5397c1dfd5e60f5a8ed6453755a9397cc5d5fafbe4345309e76c7a1cca63b5b410904b0ff1bae83796367232e45bbe343b5e0c39675c62ddbcc4314a6401c23dd7eaa812ab37c28530cb54161ac35d961a5c0079f848ecebd977cafe7377f8e9f087f772260da84aed11f5f0a3bcfcfda243f11fe3a38023e81e34deb5eab53c64fb9f945fbe0ac3749083a0439c2ee24e24c6572651c8340af25060e20f0c6734f7e326c79ce8d474d7cd1529a561e60e39366854d06404facf1f6b775ca648fd622d88e11cefcbf6f08de744765a3cb7c15ef55f81021eca46175c18201c38dbb961011627051e2bb862cc7625b03b227e33e414889dcc1787c1259ed1918e3e6c12b256b21a362e6da1cc03ac3918296205dafdb1ff2d4849902e6d4b42309d0a3c0fffdedaeda3d6262621798b704a9e56f6b87371e3b7cdfb5fffb1a4fb64154ae76ae77bf665a975117f3160328564959b61e2da34968661d5bc146310e6659dceca2dd0334822dbcf3ec67b7cdefcb3692518683051b6dbb0ab0e7578b98677110ef114730cc1ceb8584adb2a0571d614a019863676eed845e498ca63b489458c0727459b2979824b440bfe8e07101e4ba51d1450a50405268dafb785b4b25728670126a61d550b5ce5279bde96d3cbe1e4fa0e8945f7da6e2abc22f7f9e8ec719f7b628121b60c0248516e0bde1498138b8c429f0110df643838d2c70afc375f21c8d9b845c037623a45562029197f4959d7be1c7bbe18ce814be36aa9fb9dc73db9a3b7e22ba4801ea5881c5380fd81eec921b650cc82cb8e44e5b8fc502f7ee52190267c680f00bd3939566d1a8f5d00e4bb5c1dc197e04ac719820ebd9c3ed92d0432a47c3721c8297a032dbac0a4754c673ff5b01851c99bf2c9c109f02da05be9c3713bba005f0f3351d4ef39cbc7657957990dc8b65832754a2ee0e180b86587edec39de02bbe1a4e54742191c2e097aff9b3694a42a7095af6b7210e36a00d1510f9525e94fdd2020a04fd5c8d07e7d98865f6e99524a395ae872145ade10f30903dae140d0840b6c761dd378ed3b5ddaa47429e8b6a602716e8189060246667c992dd04571b90e17cfdbb7bce9420d1ebb67129f91cf0bd7c23520a4e2987dad66185ee2f9eec6e36d0c9387c5fd312e1aa255eebe27aebb13715a58d75c5b3a0514619000dc4a84a08e6d5a6a80c2606bf2cee6fec5bd3762e38bf1275747d002d83a2dd09a1758d4363ce9f077d450a2040f504066e9ec47f40395c2ae6cfa1f42ec70cd5339b40e28241d9bf9668263b7bdcf795237a30199def28180c952a0247e6c33df6d84c7042cc3af33cb95845d6a48d761e92cefaaa14ddf70f0450da535943b54c9ba5e149ca8f4aab6a9933056afed8356a6c0057d21f258ec1f774c015746e6611e5715b676610e731e3d7e353d386e9edbd5cd76e014bd23528c0dbef0247e751d2f2d8c64e254fa0026f6b4b25030279bb67d19888e5a058855d50d1c93ad67eb08d62abfb1b7179fff214453a9d6425b4da704c4dd08ed428d85ce553615586e81599ba2073bdb02d00e958e55b066a785f1215f2326d84f739eb834bbebce831f69487772b749b500ca4f4b1ef955e58ccad4a084cc9234211811a1045f0b732cc8820238079f1c263e1b475466bc4c383c76bdc6dae3ab111e492a11ed24577382a712c67c3bef4ef99f765382b84da8bbb797ba4a506540e4f2a98e9e74a48a07bb89835a8fa5b9b2e93202d6509a96ed650533d57cb5daa8547385dde388659d3514db4af330ac9b2d88c24d69519e2e13397332ae4262fe2e83dd4dd6e4d3d0b178f0a19533a8012887d011239eba1b35db66424138cc7d120924f9a09e1cfa1886c9555dd0917be4ab892e13b474f3c759eb4b73543885466934591718597800b6b4a61d4cbe8a1b24558f135c18e32c2748f80968b3d4a64c36dcd35dff6dac4e7f4c5aaeaa8308043602c49e925091b0e6561618bb4209e5a049b939ce6acb2d0d1dff767f25e18ec6aca11cea37431e24459a3dbb1c85af414ee5998d77f57468025f45e66c11d4c6bee192c8a8b4955142b56740a084a271a22de41ac424084b159ddb50827d0107bce0736521153d818ace2e7c622c22718049fc0271cd8ccf0c080490b96aa855782ecc0ac07bb33960efcf6677c1dcfefaa3cde667a158145cec7d9464a494808062aa4dcb1bb59de23ad72a4f9c664e53291e9c2aa2516952734c39062d38ecd9783b4d26b3eb3846f2ab3ae29c02e953b52e94d3f5382be3ab232f675bc3be532a1bba5c6303af356d4b1d0c95015d04076738d55c231824643dd2e164ef0488c7b88d68ae8baeb558c1a95d9cb0d6126393f6a0f8730c05278d7f8f2a9d83452cbdd68acd6d20d00fc6e9952573e3ee9da0c2c07a4e8a87dbe460c30c9bb89d22f97d2b9cbd9b38861923861b9fe661825cbf14b5c8d8277ee98602e64191dca3b4a27ba593ad19ba513fd672ed3fccaa48c32cbbfccca5c066594f132cab4cc32b9f72df3d4a0751934601168dbc13c5fd1a14f4265c99f88f2311144c2fd7b3e169a886c620f47169063a103f24f23b31d4d86b3280ecc0df9401d8fd687425980b9862718c1dded008c9930d44c35e4ab4cc818e8bbfde839a6aade99d5702ecad589b5fa922d98c4b177db92aafab6af7f0589f2061fab751a9124da83d5ba11fdaea73bfdec9a878cc85920b67eb2a07c4c4be5bc57b87020d10d17e7df5c1add28301059c96039cddc8ac4a71e5bf286625b3cbcaaa44986912dccc27bcbce313202b307a8fb593d69fb75b1ec1922601599dacc83c0f5f82343fb6b732508d80fff819cb69e656bc565226dff164d7cbf156c7bf06404674dd20884133f21bc46be205c0eab98140daf835a184bc8a828ad794653fd46eaf52a4c1ce7d11fb26af4e71f8ebbfffebd3e4334529174ffc79e702874b3da41f2485adcec2f3f1d896722352a8bc037af540b73c4c82a11fb8a6df69ae2d8ae8cfdb607e4622257814203594267a2e4c9d92cab8c929c110aa8cef720fc752c32fff3c55258677959091c8c7d4c87298d423916ae1b5f97be0e786fb539e6a5e8645d39cec28bbf2ef8d9f813bdb95f578a2e11ef39dc9be87f57b5e8e850945f18e7d71d60d68d877837675e6891bcf4351aa79cb08b748eeadf73a31b8a246ee89e54254a90d6049f7ccca4c8c91a9df5d94944966efb9a5277abfb1567e28109ad5325d1836b6211c9bcd3ec1b8514ed1be34557730a12237bdb7b400e0069f47cccc6f15b1ad6342a7add388cc62cde53e11a39e9a7b25b54ae1b2896305370430328d4f8bf8017df13162065d507862a573f0dae063321c663ea7d54cf3b046fe2f08a0a41333f76e61d8d813e6dbf3101de91aa6cbf118253e7cb7ec515256c6a28a5f2e866af5a18d4c2144b6d851504e2b2d9f4d400f322c528799d8b4767bd0e3504107b892b608e458a477d6f738dfa5eb9893e2d3042738c6a73f284e3cfe4f8d4bd5f62f6b4fb5db66953d9148f857ddbb43ba6f403051e0d00d4a1ea140ce67b2eb4b330dcaad8c11a99fe4876d223b6c82139f0e2a29eee2c3a09ed3a17a5bfe9af52eef3ad617ddbd2d7e73ce0e013d74de47d1e42faf5ef23f221f4eb31f68cd0d848b59d47ca9f638fd6eff2fa12aa8fe2a2090c86f782dd0e7270281472db569d54138f00e24f091673e27d4b8957559c92eb11ab873a0766f5c9f323386de3334d954cc9089f685aa1afaf28fc3b6167917068d2d6928d4eae34285ed0b9474cd7b00124e77df1a14c403fc7ccb21b9a112084c0db73d41c9b018679d6968477a7af3a73ba1ddb51a87cc0a30f52fc152a9a08564c61d463e034847f0a0a0c51d3ea20185f28d0eeda6638d71d936dabdd20a42cfb88034cab0b2c146639ab9cfcc47c019904cf19fd120e9da2e941632922b046768a613abbb1e023974d48b27d17db8c58f0bdc40bbea13dcd017a6141c767e08b7a1a8ab045403d10c94167a96edca6892a9cf4b806e95b2a41af42d28fb2675da7b47f57b20829699f2168bea93172f63944659fb3373c74c091c7d1902409f835ccebbf4e320d3059bd5cb351d9dad000b3ac6258259fdc8ab7665d5dc4fe83d398dcf5cf938106871efb5368b7cca143ddd18d88be5d4cfca439ee7f0f9ecdc185e6a381f95fc7baa5d1464d99b3ca90f455049a8f201119f54df879806c1e43855d22275b6e54f448d485dc9414586c184f7124e3553cfcc1c29df1c51cd461676d98670ef7c79a0292aefe0467d4e20ac2a8b23bc5c27c4da0bef846338f6cde58828cbeb9de547bfb12fda37d29d7b9ed32f09f30d79d70c08b289f189e2ec9a5652098ec95c114822e281b0624f39e6989504c4d22bf885c8a311ba4a271f78b23e41e7430983e03026dddb8450d4295fe6ee74a19e2e69f0938ed6502d7c94e2678bf0c7856c1ca1912d229eb432459dac8edcf0ae23e619cc36e9819cee18aee33a56e0b2070009ecb6968367a7722e55ba87e67852db36fbf3c7f05962a2ea29cce2847a5a41b434b060825a28141c9072eadd5d7ff8c03c5b234cc14192169d24aca042e9481d290103a9cd08617b9f2ae0efa9f974f5c19a36898b6328d70630d62f4eb382e6e384bb98da60a2ad23a9aa33e07543b4856d2d745d2696de951c714517e15569d48d14bbec673d579fe8b3f8bcef923982c3842a2298214044c016af3dc374f50d030d301d4ebbfa2188286a5daa6d0bcac3b085bfc4376adba2cebf55a8164dd35394ca100a2bbdbf407a7a0fa9754c54cb58a0198e77d71350344f476035a08ba547b365412290ed952a6a9880470036003680333c6ff1ed243bd97f23fd36ce3dc964cf2213c73fd9ca8451dcfae5915c67ca049931d9fa351b873d676c094633a21b9169ff1490a5b99b6cad853448a1dc78d733e021ad9991495893b2b0a81974f2a585e6d152acb1f0337511bfcd89effffffffffffc7ccf6ffff4d6a1c94e273219f710c8e73ce4de68f9223d1ae8aab34569573f6629e40ded855ca4c8952d77e8afc3c6ab643f1d3ab70ce79b7b9397c74d75ae41b330759a0261140e5009d06b1477aef233eee980c34ed7b8624a39ab99b870ac0a16e137b41e47a8eb6f3587c670946ef506ba2dc5d7c5f2cae5d2b12be8e17adc6ff3f9216c8e1db068d485c8dff8dffffe74c2544401ba993e6b1ce27d258269d7d18e47acd06cbb28c82616c6c86b59203d3d405ad0803baa9a5cabe41299ff16b452b5fbc47385a819115038320fddd370672f856f0e824f144a365a3c681122c3b408ede31c8f80488d5214dd3341df4a16ea1a24f5ee40721475e18202cd8562ec386e7a49b661266864de6549f61325c9f1ca1e4b46793d71a9ee49cf30e96e3ce9eb2c4896c79e7d472bcb6288b1c019836b1d399a7c444033c29375dc74cf8d25a1ef2081a76a291c209f9c516fe928147e2cc08db9648eae3f20d9b90224b455a2a53596136992d076c19e88f85aed6d69e1865a449c4890a160cba04038ee9a866a041c725ce2e28a9ab75ef1a1552e41c256dad591d3f4de918398c780804fedfbf197a4c6cc598e4f1f5e975c2b9557ac6d54eb98a17102d7f072cc10e4b6747a4e964268b7c22313a31b36553f3ec6d17de7bef0e28c764587492405c449494c8ccd4ac8c45ea9e25889639fcffdf9102faff7fc582626ccc27064030222766d171625e45c8c2595a7dcdac5a6db16f96b498253167d768a5248e68e75b53979f8c33f66c80bd826d02a0ee225be5da6a134768472198a9a5d8de79e1dd1809b3d38aad02f4b0894ae7da66ea6463c0676c83f077afdf1de27e72bd75a1920d945683aa92352088004b50b1a8d20c1923967635aa067918329595da3191a4af2617da7809d053e362c7129b2d7f7bbff7906e890cc0a07c9e9543787498642b255557d979429daad23ba5f7deadac9bc3471f69ecd589dd7befbdf7de9f8773be5d86337b1b5121a45cb79f15075d42c33457ecc8a8181fb637a4b4308b146253a826ac95a6a997c71655a0d0d1ee95937f756ff03d44ba19c428ee1c458640b16f10993c23169b69fe8e26e1f3fffffffffff7a89b6f2f632b2c69482d59c617348a2ad997004ad0297d6d8174dd2c15ffbfceaaf18cefc571ae40080c241a765565167427d63b6115576f945cb6fc831ab37f58b13d1240b6de7b27d7400edfd57b90bd34665fceb02e2f877cec56cf950cc77624ea3198f71e74f975d034b7475d4c5da70d66147befbd43df08256610d32adc3a67aa67218b142964ea4b301f3e5fa6ecbf7ae15735db8b72430c1df4284d223bd1706d878aa4a82749345cc796948bbdf7de7befbdf73e84023dd0686505e6b38cd301d1cd135303365760a08d867f7901f0c06c5c5518a3709c771fb781ac0d98819f1a906af97d58c01b023fb1dfffff1f41ffbf41a1de4f43cf2a96c2ca49a1c23b866db5ecccd0483752df4cd21d067e453388c39b27ddd37bef4666cc7f905eea5d488ac8dac376e58aea4893cc1e132e21fc641fdd09add2bc5cc8a51454d704dad8a24dcb2daeeb8c6225601bcd3260079c3c22e27c1f62b80ec47033e59f276b0db64d090048518acc5c9de3347eb4f8ff288084cd9a3a55291f1fa326341e1460644cc6772e7928da77ffff462fee07c1b84f2ba9344e047e75d008069d7dbdab6973bee2e4eaacc6041461f3f654bc5f5e606a44b039a297b9f7def5e4d69f76cf4e7a42868a84f7f644aee128f2222c13c8c99bcf1e0a4685c4071c497f25941638b54f138f4a8fd294981db90ae14194556b48076ac6c4167afeffffffffff57e9e6f0d1dca75557ddd1cb8ac26c7d59cbb82fb014d5a6d5b883527c1e28d1609d7c0e2b3aa0ca534dceb90457ae8591eec896adc1526922c35a95e13c94f33006c01129e31edf489480689fee069a6981fa47ccb10bbd45d4900998e93af38d426d45a13f71656c7337ddfcf027cf429cdf0772c9f930bc9fcf9f870bd4e741231d42a79e6921802653bb438cdb949cbb901d0844cb6d579876fd2c9d8195d1bd54d03dcb40d5aa6aadc4d9d0ffffff9f996f4cb1ac09c933f1babca824d974ba92c858500c19abe8b87d2c3a50a02acff9ff7ffbffffffff466466c89d14aba0ea3fd196b222395d242d3bc196a110bbe7a970540400aa0000d3d525efdc5de590983863b6ece5d5083119b0798c71d61f336acc247bd251a2a37b29ba3b8b1d4c44b4ecccb2a6dba6bdf35b9166859eaad07a6caf2cee84738e0d66eb5493e28fe498180629fe46541534c5fe03ca029943152c0711e59b25280c42fb34085dc0c7821f8aa97d26bd687ae83515bd9f404d22a139b12e6d01dcd445604ebca89516e811f5cae89e5290a857aee043ec48cd87d08d6e66d5adbd73ceedbd7795a48c6f3d859b7befbc0c88f401c78aa31532f93ac0702500a138cb727457cc2bb81c6c414968e8cd076915b5a78840ef0e83ae558cb88e8c7f6f334b10db13e45896a0cf46fb4119df1fb14b61eb3a319bb1a3e540563813ae6635e0c0179c736ebc6aaf6bbcf7dea59bc39741e4d8655db17fccbda8c8937a20214b3c9737d67b3beb692a59f17a6aeb4878ed25664764f1e82faa4051714222e16a8971a07735c9eb572eba5283f520c4a5c547b6e3cc85883448cccf47cff436fa01ab1e6a1bd5158cc25a5839e7fc275a21fe2003d73b4820b85312d5eafc9a7a598b8188eaf22405f1ee88b3b438ab9e7bdf789f9f1c34ecf3402a671049828e544b3865f589f1058da27428292f0c64888bb594c46b325e69bc2d8302e6b3ad86d0bd100d739b9a6c1da528195b0ac3430afc91e51367f415ef91eb29128b9acab6c4ab8e690db1b4ed28ae40cf925d6c3dae6d6e3d21577e8ffa9539bff8ff2f230b96a505cbc41a9c6fcc4316c70f4bdc3d6b39bf08fb985ae696d955e47ca4bf716d483a02ca3b4a59ddc2e4b7286a5756af6569cc0a14e9fcfb07b9e3e6f0d1526dca4d5fa470ce3955f90a8773cea552a18613dc5e0af59a01886c60ed40e46285449113bf15b900c5f25a33d2995ad950c4ffffffffffff2490c3073da189cb230076d5b9b1fcab9b9577efffff072583c6a013620a8f50058a94feffef086f8341bab7c0933443b9d37bc9097383616ff12528be5ccd9ed0f208c6cbf8d4e2ff7b20da116311493b3a022d9da86d7366e411b6269b4c01d31413217e60b3712bda47a6b2c011ed0f494d0b1a5b00f8f22b81e46643b602b4f098de7bef64d1cde1ebbd77efcf109439e73eb32750efbdcf3dff83b40a5c98744eae9ce534bea0512ca358946b1ec0865c456f9a2d63e3d38c9ed78a8374927cd141465c67435ebe422ee86030b6537968256e2e46399df29c40e1ad250cdeb1190223578120973591693ee0bc6758de6c567869c474359df2c478e9b23ed0750190da018d6747a9332702301ec44e14816b46d5ba835b209d4cc6f83490406b3087bd269559954a565bd776c64e480cac075737f7c12e20df5165fdb718427b1f7a6d70ce39cd916649d38cd9983771c7fc70c84c2f9254f30eac8531135c4d7b0b412aa30ad79ad4b42ed4494145db96b8015d2a8c26d0143bdf18ef8374ef3c7b6a8b38fcffaf667c35b222fc4783322b8d73aa85ad5e4e29593a58d837a14d461d1133e2d7668c8a3237040e40a144c0f93c9ea4f5535928fc172ef03105efff7ff0e6f0d1c227613113b33eaae652006c88de7bef3c61c78fb025f1bfc9854c7e1f3e4b28daff7fc748f3ffff7f9811fdb0ffffff4ba4449bdaa99c28b5b33c37e30b1ac52b8a970fc1fb3161e9322133b219da63ff5fb312f440c11d5aa1fc529e4331563afba6dee35ad0d71ab74e8cf65230ad73b9cb79a9d1336e1375901a065f9a087b1f0f89dbcd9fdb88b67d9a964a1be19b26e9769080897cf8bc20fb4354ec750c48c988a7760994eef79806a09da4f9c28e26f8a707fcba5e00e3184edbd400327f820fa828e79c73ce8d555da71ff2940e9218d26aa5375f216910b4cd961331cac856ccd7240cdf8db3a462aa4bf48e68666b19d5f942697f9bebe3ced097edbec2c8ec8db6b272f1ffff243c4abaf3c9afe36d28304b6ce449c296a08c9ed1126557f9c7e6d9f32265ecfd2a765240d813bbd472abf255d762d76bdedee37c1f65daffdad8534e208947896ba7e295958a49bb385a6ecd96a5b3c262e46062ac13c6484d678c6e85e7a88ccae59be7a5fca23fb87db55add693e85f2da3a56ec7dd8794febd00b35e59588f24bd62d9ae99a20f91c81c4c16d2f19ba6d383b4c21aa34b3b3eb4d98cd943aeef2519b5fb65f4f61eb4a2983f5f596e5827350602121b96d6dae1034df8daa0e92ac9c25bb212c17778b215b4101052edb9ace1c846a1ed78b15b9096c84b2a6c68806acfa757d2b12b1b7245044e0a16403d8b177dc17215ce01e864035c40a385a5ee4d0a1a81e71799335e8d4155d81d790c45b6b8a2135677f1840c977e1232ebd280d40944e44da232c79674de78e29d85aeec3ffffb7d01b8087ed80af6007b53bfb7acf08fa3e73529cc0724dc0a34cc3eb82c9c691a9153b56ef1642a373c9a53257bcf7b969f91362881ca42513e20dab51e8b0ac19cd80e92c6969b6d586d3a286707f9c50af5a72d884c0c357f78c49826424b5ab46dce88d70a320a9b939e7bc5cbf1ce98eb425a0a7b84305925c504a90f5562b03328f89b2a4221324cd90a1543345b8038db8a8446eb8358d01307b040319040001005114c8c24c925c3d14000417ba9ca47c34483038281207426120181006838380000000000381807030180612c3a2d6c03d4c6d7dd0c9832619b11eb0ac4cce745e11bd9739ba2086e0a3e154a9915f26b861dd54573a9e70debe2d88342357061b1b7c670363f9a11b6e0885cb4eb763888f96fd2d9790e77a92094f6d8ded5c52891777879af879094377864c0ca958ab1597730867879c4285593be7831501874836c79f537a00c6a9f7297cc7140c8c5a16499d789bdae48023b1c10246b7bd47de2274afd17abdbd1e1cd8c806fb4af009e14d46f69aa1a76ef1702879c7539c1c83a02c8a83a68b2b0f15ffea10db60183ba696cf8a8c6671275248799099eea6f001636d68078ab302a85a4c456ae28f1f7a66b634ce5232df592e497aac3d13f44e9241496ce8c031f6723a18fc68a6f9174fe69ee029deb47cf5932bd4600b61e85792a5386c2635a9c56b417ca4e0c4559d59a05a3d38508f2320e9917afb08cc594008d3aff5221153a2d39d151da7b8eb36abc3640a1326c35dcbc79add52fd70ddf60ffb74539d43c40c02e57b85803391bf4d777c9699b4ff4bec8479b34f8d5b27e8da782ad465e9cc76c26a30cca5e7962fa97d186c8a82570ea20fb78df4d23195e00ce0396036d64e8e2a12da8b804584a73488d01f1e5df04e32371d8092c9dfbee61c6cc8afe37f773f2b33beff6571d7a618207d0ded80b8ef1ebafa9ffe366dc29bc1ebaba635ee3e121a6ae289943b0292c3d9b536606f680881eaf927ba4ed468d2671dfa692572d2f543d318b4b92794b1e6079dd8dd1be8423d79e2b4a7425fd8c48605e5c66469697d0a1209dc114ca9f721e39c8b5ea1c42566725c2611ec90b6419145e8a4baac6eb62cab81744cb44dd3ec38bfdb0e019570e53aaab3d05d50f953ba02d19c73bb00a242f4cd99f7de9ac084c7226844a6a0d5b71f62a515d02476816bacd56711624bbd92d44a2b8424fe67ca3776bc413f7a28ed21c910a77374018b5b4aa27ccf0ad338da94bfb451b09a2b55ab653b4bd1bbfa45977d00296afc8d224f21e691b1f89de51ce589237b93c5c33a5c02fa75cac43e88b32b660717dd7261f5cb413a251205393be5c25c530a150a42f6375704e673f4a553e603167f23b11c480989eb7a6491375df1265feca7689ba91523aaa5f1851ce5f8523802e6255c8c03813d7458665003abfc64697541851ae05afe5fb474e3471f54c4a39530654d2096b1486c13d460f9b062bb1305696db0343393ff7a088f1da64f66e91e758025f3ddab26e4326c45cda5151ea7759da05f8eaab8ccbd8957cbe8b00460b810007481659eb274ea4f9b6bc6d6a0abd7cae9e316803f526b2f4cb9093140f3736dee1b256933d4a8c729818e9bce7d3c9618284ba6dfb4bc6bd14a783472467d804b422e963c4751aba88922b9835156b90e83a03ee25ba3d1b968d1efb4a0187d0f0cddab30ef45b70aa670b48ffd26df16845d6a896d8aa7026d8c6cb8eae9f9cf2d7dfab847e484562245661d269c3b3c4d26b1085df783e0540997e4634906f92fd4735738438452568023da1e63415ba9f22c49a46aac095bcd2f915e2ee893e20921a4138b4e581485c4bdb456a56c203d7e0eb4045df60b53846477461e3814b938936936ccc06e79b32eb68e0d248be3e2a8bdcdebbd2fc39915530bee6b5d6a9889aa03c18caa7b5fcd270d3ffdfdb7d4da1cee9f1e32a7e0d45ce7ede3e951a6e3a3bde2cf32dde0da38a6854158e99b0804cbac5298880ffce8b50acebb1df4062b1c3a7d053259a00489ef1d337993a3bc95244618ac00419cabdcf265b7ea9aa9a4ef596e71d112a56e150d3cbeb05cb93b39ddd0c6492b6cce805edd24598ef6572edafeea87a6182260c12ac13ed5fbb54e75628fe5feb111d7d1455a9a6277eedae36345e5eb46122428667bbcd06c292f838579d63521ae5ca9bbfd4aa8045d61cc8c3579a5f402cb47094ecb8817ad1f94e0207f58267f648135b11f66b9f4c0223b48633a4d224eeb7ea7f16b1e4d678862cf730b4edf1ab328c282f3d3499556f246dcd3d9f3f245613b30faa91ee1f486b71e608f8169278ade0c3b00ae6b81f5f10a57b7a8f9f69f7bd7c13e01905f5dfdeab382d9c3189473ae77ea94052b1d7d5c7261fe396e02e4b5ee73e13147aaba2ac214a7f9365107808726badb0ed1f514b19cc061ee12ce25dd3c77953f82ffa489902df79d910e0e457915b4b0cb17ce6c8886ef8857e3bb7c234b2c8bb42ee018f998e06fb1a03f6a820a75d0fcd0ccd2cdb87f247e820799c944bbbbacf42bc2964201c9085c33abc18cb1a375abe723e8363a95a132fc02359908e3532ec7aed2cc1a49d5501cb5159ba7e25acb14f05b4f746b89d7f2cd77fcb853b7bc2e3fce3b1f7ae2012899d42660c119557355a88a1a115f85dd078e620bb83e94978f90a2b675afaac669adf6d35d2806db7aabbf534faa981849c18e6bd893682bfb258e10232d171379883c1efbbded23c6f7b808883cfec543bff72adbb4a34e622e0f2aace4fbf0459a36be5555687187bef98305e0b6ef81ae10ae1c65c2a22ca6c9264db164b9d5c2023580cbc0fbbbe10ac6d915707e82553b3a7c02939ba5c0ba3ea0f001b447c0498eca6917bf60a2dc2c3405792eec52f9a79c0f63f9fe026beaf14217db6559389362691eb363f5e00579807fcc0b02eea07934db9b725fc5a6b7c23d2dfe48680455caa5af203178126bee22681afd01c5a8dffed5b493a0fe28541ef10543f876c3e238f7f6a693d93849913564e6fb6a50cb1cdb8693fbbd7d772075d86fc5b846e42f4192c7dcf0f42fe87b20f7b6f5e0249ed00b92b270ebe76e24d4aaa66084249680e1238082aa532eda9efa01517185af981a5c69f2ebe8f8af08d6ba44ecf7dccd58bf20378feed8465448f7abd923c0430b6c95406e1da3f429367beb3f010b4e2f10c80b1104dc343d7de2e3c8006c21ffde51158dea7ec92abd64687c1b56f3f31c5a79bd2889cf8f28a8755c31873dfcc3c094b299f138b8b3cd1d2e3f7504f75ee577adcc4a748f752d32a813c52b5d48a738909afbbc01c9f071a4af8e2eb0f3e178590856a83bc17dc1034a593af0f1b4e8798bfe2488ad83d764baf9e70e0f065731aea5316803ff5012396ebdf0153c9b0fe4c921350bc9d822e6f6184d85d72882a2d740a3ca40ea433dc687bdcfd133328b6500ca833e1ddec88a4434bbea14388c16bbe1763ec95e86b4ace27f6fb333234a7683536d3cb106885c07f0c91ac142cd68ae8f248289734017de9361285392fd6ebadab15ffc1f5b5ee5eb66af05472d4894372a09419986183ae29af9c4c46c89ae4cbb37db360c69f53afa13a0a258fc7df008bb115ca4ca682615b76838c642281440affca1218c068be1a67a8080fa0f2f6fab4ba20c5eae3e5cefd831599eab2b0fe9d52c3ffc52387e6b66651d8b47735b0dbc5b557aca4b4d6163f422d8df39a42d39bf162d98c5bca3e7fd4e6abe1e99821c88b0a5dd90897685654bc2c0a95beba5e9036064f95e5a37d75924c9375a61a147b9a061d4c02deb96adbb9f14a5256e805f6a52d1cacb5256ddfc1a7610f72bee27e63cfbb38c30d9caa1c1aeaa7432b5170cf0aa36e2a406ff8624b778e14c2c4addb2552a31abaa5a1bf4f32b3d9ec51ae32a8099d10d56bd536650394acfeb57a9c02ae08d2b8a7d23aba0f9f35c15510a722a15f046e58b604d96b0f81902eb4b6bb4d58a0aabc77ebb74c13acbca308f9c4b36e71ede7e9e920cd92b1524a24f2e85a9a4c74d6e3566fc9bd7ed8a1f5fa60ebacf0341c556ea4f3aa612365c31ce408f191f03b5e9b203cba76b8287c73412bc120441e6c04a28663e5f61b6a8996a69e6699673fa35ae2d1a4f88e1274a583cea1dd34fff360a34106bca0888b20289d21a512320b24d903c44a00cce4cdd40fe2aafefb1e299257ecba8439a967888b9ee4c81058121fc8d186813947568124c62bb198192e4560e39b22bea132108745416713d10f5890ab7a6844138f5085f39ac98621cf973c8b661ac0bbb69742a25562ec2ba7f15e51ad1f51ef944729ec9f545c310d5dcd642391cb4c57a20c8777917b8fb9e4d432158f222ce86df8973934af3cb3a5a408e87954d771686804b6ef47c3737816403a27155ec9b7a55a8ceacfc338fa25fd5a73a1313c1bd577a217c69cb2af0f1a5ac6b780237d57ac20df625c74f90d4c914aa8bdeaa99998447fb46e4fdcab279ff331a56cc8c6accf81031c810364d6eda58e3a75a7b51d443304fcad926646d447a4d92ef94a8d06945f772e8ebd69c362ddf5886586355ac2bc041260ef41eca233815e50a66a54cce9d4720d9e30134945346926d4f174da076349421bd9e96a05845978c0b8a8d00786230faedbce169a58fb089f1d4d9839b26bd852f945b02858e59fe947216ac00332a44d1643bec6fce9bf6ba63f9e73a89a67141f9d6256f43734c59c0786f59bb321d2ef448e3477617cab081f13dc0eb93b7c96777c854ae5da0dea0a38eab91971e2d84db8d385e57b9af1688b3e5cee60d923b74a5a82dd18765d1e07d1ea5f80a82861a1c60b08c2818ea28898485833306213ee3cb4442c15cb4bbe209a8f165a424577b040c41e9c3f362b52eb3b532051038f98a602e41887dfa902afd43d1563450b1e74dbb3b8c7c8a097d5de2f5946cdc1d17bfe0d5c8feef9c308284cf80be37a4af2e27b714741d6fe1d39fdebd24ceb7d1567976f89788cacccfe48b203fc48ea0cc241902b753fd35eb4354b65e8ba12b495f4d0cd3e2bcad2ad6e91a9a99cb965511fd3f3ac3b95a69fdc57fbd97a84be1ac3bc2693737e01eb2c8c4c2f91681b9b98528b0d376737d356851ef864b8a82b9932adcb3e8e2ab6700fa8b1ed7235b422466b727d2a314175a3224d4fb65e8a0ab937e55685816ca40e067e4bea8288afd77123fe1ae6642f566f70f8b1285979989f8599b7fbc886184a8544901e051dce6b87f8f97cf36e791c5a4c35dde05d20b4b19dd988b024e30f2710e77b3543dd9dc3da34b8b6d2d0746009904b627419f9dae8c9ea0d4cc08f3249b4dae222f94b120e0eff159da29d6fe355a5a2ff4559b7c3834ec2078308d7146ace6988ca0fff6b9936c521956dca6ea4d4ff67dca59e137cf711f33fbbe9aae8d81359416d4a1b978b5e341e85dce83090573107a0b41dc42f6d54358ff1e7b9452141786b5f4a0318f7c4e65b301383999125430065a0d0e6f14245264d51c5798d1ad95234ed01d96cca8e862ed8890413b260468e552e5c79f3a4b336bd18381b55c6a4c1240d460c76c66343082550324f429942a766c7e3648d206ee334cac41afaa3e65c1813c857a7184dbf08b0fdcc015e7aa488d4170c63ec025004e74fb08009c58f0697c377002b9fe96561bc68609d763b541c9f9ced82364b7fab611aa7859aa9752a00a3507bd1c0c1a0d1fbd8d2ebe7acddd03036dcda1500a013b5be6138f15691b96ecdf2968fcc3c9fef6a22fe8448faf1aadf15cc489b091e22400b769647980a29a48822b4deffb2d952f6b5eea062534d7309863340500c312ad505815317bb976b465174bd9c0bfd092e48205816ab8aafe34ae192ac6c8b7104009db05ff50f56f11c159721df017c20a86ffc266ab8605b32c6b3a8a494a5906cb47d66ab856c29c3a257b0d2a7f2fb6eb789f00188b8cede55fb44ad189b402a3759415931084eb34b0dcbf6503d0026083b4b28c1af4ab1823799acb1eadc5a04599784d685797d570a7be4d3037dfed542674b07825caa104a994c03003d826387205e616dd6a2f6bb5f89e9cf31c4205d2010d5e4ed56232880c13ffb31e8689d6cc5558d294eb5980ad3655117a95eddc7fbec7c4049f36895faa0be4c43833c76ce852ca387b9a0eb6dbace9c1393f47c680b9a8ba14c5645b471bb70e3c9b6e33f9845acab7f16279e6edecd9e68f072eb1a0370d46956da19761983f72c8481d62e32775fb1e5dadcb5c423893648838176d048cf6ff5ce3a5df113a10ca7ecbdf600516a3acd66969ff160e42faa192f641c3ec29d0950691d77c98ab92a5ca1403bbedf50fd23c78125c413be12ec5f09613e6bc71e6fd1aef2fa48ba750942ef603a85535c32a59373fa93c8e32d0c5acfcfd1490ae647b546bd8ec96bc03664c68a8659b74b43374d046bb19bdd4b0f2dc4e22fa6338b2a94a53158bca91719936ca3290f3f06033ec3b240cab1d3c27263c0127e98fc35ffd7ec0a0284cb2fd5746604b749c5765dafa607b0f9254fc57706513af645778d2c38b69e6c1cd06f521d1d5df8a1994592b56eb298cb3ae4017e14c19ec873aefb46117024038b81e7b7bc6d3887f134d9c7988fc20c803e2cd8770d5a11c93c17d04a70f1eda600b073e52df54b1af48e6bf7942202257b8e8161d203cf97f42f7cb78297b8ce4c58024fe8edf3f2ebeaf51fee529489079efc5866ef8ccae6b727a3e386c5c07e31544753bf35bbecffcb822da72fd9c0ae72d371aeb487260c1a79a702ad07c184fb12f8c1d2b824bf5be0f9b7fa7b503246d317e767ac91b3dc4fce102ccef703401a29f9a8ba287cb0b05a7874cbd4c54ed41ea786ebe9297c2b5c1d18eca23551f25da20030681222e96b2a93a552cee7fe972d2105d97758d0d2bb0e58e0f29516ed97cb4a5c4b75e06c7775ada7d6061223ee134b014653f76408e07f5b69e0458928e56f58f7200c6ef5da25554911740bacc75ed5b8064bfb4c899e29a7a6af4583a4783d04919f314d9891fba406a43dc60989c48ed82bf0998c8e366ef2a5797003435209fa64fb0ea31ce1a3e6501d3570d7e73087441b9655cd915cf7d391fae9ef7f80d2bcb6382be28bb5afd42258a3db9f9992b9540b7e64f820162b3956d7ed0f9744c2b3a3931723decd0c776f6e46ca4b7389fc64d39fc060caf5d4ae5795a6506140c2844e53985151cab439da4d9c90eb586230b147c3b7a031c3ae0c459441f7ba7fb5860a856f90187ab2b9bfcaafcd697870e03c08d23ed92f84d338c77fabc35885365eba1872a707256945f80143c8d476ff24174830d958331fcea75c990917bad425952772b45e546a01f9ecff29ba76478bb74d896892e9041e4c0fb1d44064a4edda296d5c952328a44c8d4d68e7e55632d4c431e8ca6f4e25b0de82dfa5758850036589b6f94c17dcf3f2e1e33c60e3793d43c46a989718227e2b85a9df20fbd06f2a91531930bdea59f8869cc12260a7af99f03cebadac3562900108920838964e201e702e120a27c84b40dadad6dd396a6e288d8ba626a3309e9bdf13d35940e0fd882a077200adb1902299c83c354e47f7f7f942eb46f286333b9b7e35d6522662f0394e7372503481c71fecc71c07e20fc2c8e59d4fd75e68c322297c9cf7f5c1a2d236fb2ed2098f8ea2477141fa0d9dc46b37f59b0ee1a2860afb44d0d410bee6fec2ad62a5365da0439ee1e5306aaed35aa7af006b103e8ba37ca9d31ab92fbfa63ff918c307caefe405cf847823dbbd10a4577ae11ba1426729751e62cf58260df5ab6b866d8145fd721255351307fd7422f4285d04e3a870728f2803a5b924d7c2a11276685563828fa8a0ddc943bf8b951220480964958049d89f04aaf38726e4a0fd2f9a87aacc47a06111179a7420233153bc96ad7256b165c226a6b576094d8f25e49cc0c4c5e9850b83598c9ecaece40d2fd7ee63042e92c9ec99d7daf4b4ad29480ec3c99795d1b4b00fdb9cf05a7e1f313c50eaf066ccbeaeab4fe642c7b14acad53d3f55e713c6d951d5169eda1325357ae5eb03dbccc56bdfe5221651cdc9e91bcb6d4ec5935add36dba4f7b82f885d6a380ca3467fc96bbf22a7639523c42643cdff1e6e4d0d34ae477f2cfbd7cd8159b3c125ed6d7476e8ccb2132c7f8faa1306464d52f84ef9522e8047158d97f74e67ed8b1745c76dce26c2eec8e7184425ee476bc6452d8cf9b12e3d925e25ab2a14f29348b2942763c54d8dfa02ee06a9a898e32557cd4c2eefd2d960d9928ddae6b1bf94dce39cd8aefaf3952be837d0bab80292177dd34e61867a5e85974b98ccf62eeaeda11aa2399e78bebf1338573170300e87f7a09ca1220228bf7cbcfec8c9c44d77f6b0bea5ba1bfd9ada29131562ec4b23ac038550cde17f3d3977efbf3e973d89b2ad944737c52cbc2c68d30fe806a13c26b76103e182b00b3227956e70f46057623883f90bb20df7e6430afd09e8cbeafa05974a24318c5f68e8e2242d914b2d6ee8e6a24fa079e70e9741199839fbaa2f2921c9d06c548773e6bca2cc9538f0168b2dbc24d96886f807a978c30d4984964a99b51e0a5ec2d12d0113010492e71740c8ddc04df194827236273572872efcbf7851c4a5730b33c2667afe1f8f31a1ea43370438bb76e96c5c6fcafa71468ee3479fab21d24dc2cceb1fdf9ea38da014a5815bf5e837f3c60b53e51470e797f8e8fca1a27e5d082780e290ddbb07f195c4f20e47ffd00cb4be4d0cae9b3d891b8708a9131652a5aa5adf6931c2912978af5c605c2683568fd15d9e64e3379f295ad91929d2ff8b0008b088b5b285f38e8ee6102a54c24961c30f21196f729039d29df4ddadb9975c83c132c7e72a20aa3131e4ad84a2ed88621eda3fcb0cf560ac05228224d39cc6ce0d0c5190d2830e0d39d23bb3138b8b33685e840944c4764e9da232bc54cd8ebf3fab7fd89216565c06d428d15dc001419df1f26cb74e98cf8bdc88ad68d9d01e6e12d56f3c48796073dacff78b7b7c346e35afb731fd18194772989590714e110b80fe45e0cced015f9301c4323c89470418c7ba36ef6d13a809ca495d44518c5ff5e58321b7acbc3d9250a4d0786f4a817c047bbf2c3a155dd79efa7c8b734721234d44971b4483b06975ce2c5f5390b2614848ed57b45e50c5947ffbb400700eede7a54dbfa65091eedeb6dd39bc967147b1254718fd2df7453e88060acbe82cddff371a57df99d7a60b76208d922b755561cf23b99048b5ed4392228917747ceca7e20ecade451ecdda85b27c075bb03e4459e13618e68099ea66969d8616145fc5a056f9813172f8fa43e59b12664a2bab2554ea9f5c19696b4a76981257a92c9507df17f8efad2bab5e9e11d77a354e1566695c256d1b25e05566b643f878123621237f859e9efac70790dc56086c288852d030d2b4de97b74f2711369fcf371d98de184a011b879ec9bd1617548df0cd3eab2f15b348a120715e02344274b9167fa43ed914422432b051f12abe0d628013d514d813e31aed71e998c887a04ccdfe41de2a09f937941a102b1735cb7c23bc4d5fafdb447d77edd1cfd349891ab66f35fddf6bbdd56d20b459107644a2b257a6a7aec6cf814ed6b06c314ea8f84ae95ba14d4d36a5288658c2569d2c02b0bb3589efff41471a5885693a00d57db65f3d45cc0fc7efa7902aaacab591e98b228127a84b29ebe85d773d8d9810d5866d23e13896b54675321a1e1b15742f7580addb55db02d1cb48f919a1d69458c907a24d80269562cd8a5db6c935f72f08e4f44d42d94027a63f0507463e27a92bd31d1c511d4b7a1f6726a44d35f2acd94ab8195faf979d38d785768609ad22f6bd6f5dab81dbb3a60db12008222a60042a0243825aca5ebfc372627bdb065e154098075ed352195248bad0f55c29435a58d84b294dbbf46036127cb76d2bd61d292c7651e8a8cfe917587b3cacd04e8652cf0ca25db392b60f261b1a4a9bbc782cbff92a677a5e907da54317a987ee10b73f78876302eb9e1517163d493f60e96681f6d8ad7ea96f3700d5438a3d7be151741374aa700c0873b711d30e6cb893396f9195c5cfbd8a00b2c20010aad942751600eb1ea545b1497655c847ac9d390f4cc649973f38056c549d550ab64697146a22eeb19d7157ff1c63b28e18d17487e92732ce0d35f800f2c2e8b9909136a49a142fa43a4a7d0b1342dd949a6bdf7de016102610261023278454ae35ad48a660c2f88a6ca48c8857bf245176d76b5d819b66856a0c2ff4b98cba2997b6ad3a2c9b009654596525de3cf755ae1bcfdff0f21b574efd5c3ffddfbdf4d3199b24d96e2103336c64847d0d5f6cd74cd956300d0bcdb594a425bf144e19962d75627c3066374b7aab9bb6fbdd0a0b7cadd6b8fe8da7603939c174ee50c8c3db25c1a4ea1c2ba37f6d385373632715b982e383a5a4f27aee752f8441a1e0045332d714271e16a9f5119644810735a3d63654c9570879977efa01c02c9f519b80c2a5dda3454f4a8b2a2bb77992e0aa05121400c8662ecbd37ca95cc0177a05ceb215c90e50b1cca757befcd9d39e07a6fe1da0276dc9845d4de7b8f1d9788265264c8426fb6f5fd10b5c168765b4dd063e2812ebefeff0d51bb83a542462a061bfaf98f86640ed8e9c6232887149f12f0a5f83ffdff27e2ba91c83b74ba31c2abfb111341e644517c2ccd176a1a2dab9b33a26d67ed49700b435ac81a9b235458727833c176c4a8f905c5313bfbffb932e11c974e22e174859921b59244a6da7c0a8e83564b4de268d1b118b7591b102f1bbddafb4423dbebd5efe2494a77f71967af63063aacd69f62a6e54bb04678884066aa80f0b230cc568ac248a62c732547c092a7e15cb68346322e65745a542a642832e54416ba5387ec6a3099b3c5ff7f64754393cede7befa59b9903aee7f9f3bcb2d285a590293249326542a5a68e0c6d7880a56be2c244337a9bc9841d7755da2704cac141f6de7bb9f7de7b0b37b5fa21c5d2432af2b9f7de59ad9170f6605b564fb8bb7bd27f2348bea9079505e166aa5b673d9aaa620a72a6a29e0235725c2a792b4e248d6d04d3e58723cf568ac5f992a5a7d3210f332ce9a888adc475516ce7ee4cb5145eb803578475d7b01a435d144bf6e173fd12013a9282fac462f99ce998e8115e05e8bac467a76112972c7b1b9adcddc3aa3c3a45df000e89c6a0d33129863c140c5e813e301e578ae71d029733d2518ef595062efd4ac0561c93eead4bc7f4020b56d6cb88a888c9dcbcf7de7b5310e504d7c29a418937af252a09b51a2b570aedebe8482937f7feff89dc7bf5fffffc55f8fffffbf694df0e3e06c464d598fbb4b1ce3d952865b87e51cc80e0cc9ae24cd062537ada9069312975ed583849c0359574b2c59f59c843246226f03548b566922efdffff1b8686e4de89d37b2d6f25e4b6b2f7de1d6e50b73520941bddd6104dce1e274e77f70e30f1b20a835495f04f162a880ee8838ddbe456e96b2a6b5a228494567626d04665bd5a60a5c68c4e6f691e0eeeb0e25a798bbe4163f8246714a85349c0908489fc887005b9b4b1d4dc889d29857d32e6c3cc757747bafb8c5474777777d8badd65f1f1d7f3f77abbbb1f650e185927650eb8b877af06feffbfbbbb6fd04ebd272fc60cb75ce5b985368004abeed17b252245fb8a98788640516d19bf9a33af40c069c9edeceeadd9643d9167ddc1abea2e8b52dcde249862b9f1dc1e122a02ba5f7c67a5ecefe7eebfdf36252e470a33d635e265d2b08bb2f25916c559b94db9c4a8975fb0cd6a6524020f4a2c09d885b2639b92b93962f6826d514c211b05652358dcdc8eb06031981c154e8d66454890adae8bb12ed89921b68ae8d5ac99a1b4d6ec2c0b52dad9eebd9adef252061a2a63a51dcfffbf87f7cabd579f056531b1284db91e56c853935330453b5c33c7b09f6888d512d1d405abf2a345cc90c422c98e60242d7223bce48d6082c6e4189fa63feaa187b9c01a137fab2f1bd5a04b77e14a72fb68d8430e090402b74e5587acdac48bc2eed7a5c77e68ef9f8b7fafbcb2d2aa01e846761f5d826c13b5cbd89984f7c294924a61e1ec5bde08154a9731249c207dfc690022230287a86ba9ec6045d75a0787fcbaa3cee4fd989c73034a4e0c776e8ca65cf30e29848651884eb9e126938943e113b5bddfd76b98640e58eac61a2a0e4eaa86249de07e20c587076f1187c420daad55b07afeff7f669b7730e5862d8e543827492eba2ae30a1c8a631941dcad7d82d9c4fcd8cd458ddfedbbca60f4560831cd97a710e40a170cdd33ae3c9c32ad14cf0874fcff8ffefeff933307dc26f62f1d82ebbde59fdfec43affebdf72693ca1c30d596189a0759db7fa0e1edeeeeffffff1b9f526fdf7befcd362373c0394021d88c363086b34afc51ad85b6425c212f07ade2976ceaffbfd7bd57bff09f05b8e45d7605dbd44e0a2a696e2e53d96cd6956f2a0376177495ffffa1b2c797bb81545c48cd0d717807f4d9acfdff13b50b74b8d128f1f12469aeaf4e4a65cc762a748f4bcfcc70caa336f7e4f8feffff1fb8516ef6de71d211a3b7bc32604262fb5476c71c52485fdbae7209302de706a2428016f4786dd8556ba72420c6c8f7ccf8e661f5441c338f1c8aff0f38b304062dd21f310c983417dba96754d91728ec7e031075e931dfd93ee07321895a0dd59903ae7f183dc36a70bbf7a6d990340428dca4ae20936b9b4e1dcd4e165457b70534e3f9e561ae4bca7155106c2750a49833a677e663e6982545f2b11580170e1131d0cf0ed7e8a4e849c513f5fa34f68343c322482763ee8f0eeb453f70e2b2eabaa9c114fdd0a8aa63cd2cac11bd72edc92a47fac2eed7a5e797c66537d0855223a76bdd5c772fdb40c96f9e504f95894f33048e73b6345561619973d2a68c15adc2c1549a3c52b59d39e09a867bf5ddfffb94f466247083902a41a4ed2dbcc84299587c19f474f3aa1148d9e4ac1da83bd2e28fc904b821d91c7d814076226853fba0c95ab7372a7c291d9aace26cb536a99e804279b5d5245d63ea99dc6df820240b67e2ac6e7d689421d5dd1d2eebeeee3e1fab7244d0e0beb4416975093707dc68bcaa943e7c7a48e893bde5abf94975178235f2ba46b1a25926792d576a923679d2783bce6eb25c16081ea2e114fab3748cc1920447dcd9dd3d28d90b520edddddddddddd6d68b2ec2fa8a18293a508cd9b56a305439897cb8f0817526d9a624897f88496f1cff3c643dd9f6c61e74ff8031fbd258a7b0f3d3e444c4ab840a2bc9c78583ce958a47a3468e45cb3dceeeeee5e348d9db551152356e36e97c5bdb554660ab65887eaa3e413cde686aa8cf4d944c8ff19509ebc723c509bb4320f5aa0b0ca29f960e98c4fc7e4d13409974ae5f556426778dd7deeee0e5cb2d04ee13f93532ea46a0515534c6eeb89b79b40886d2c3925825ea6a886e891019e90f33cb3107a7d557e8d1561cfd4ae8c46ec2598356d94841d13ef8e16f18baf195266461933b39cc126fac5b63001e6d695754687502920228118cd8d5a59af644f2b06e3eb5e25ffffffd92d1b2d69d8881b44faeabb8f9ddea6f0634c2039d276f609c006cdb3ab7e90884a8bdbd22e25618d7c6b1a048023e74c443a9babc6312d759d35d470578c5da8f40ff8443310d08e00a318080400c4401c09e34813cc071400040eb2909c8040302c442c281286c20020180c080ac000000010068341e140280c220745ee615d070d0ca45aeed304d6a2ca8b6926b4d36ee5721865d94b577cba5ada1efe02c85ce3288a7b59c7dec983e6d8038458efbd2a81d9bbc70d717d3ee2d17dd7d19a0a3d2a56dcedf4bef690fb77ecff7b944122dc3d3e2fdab19244208ba2f87369da6f50a230a607fd1f44dde52cf0ccdbbf6f3070f6204b189387cbf681b38bcdf629ea80f8c31aeb38e40bb95c2e3ac4600d00811f0e35762e05816673adb44e0a0cdb4591313b9610e22336d0db4f341a9c8f6c374e0d8b3f39193a068b292f115aa3b9054a51905b96981f2e4365c950e04fae7866db6869aede51568ac2322d54a74f25c7cc1ad94aa70ee18dec76dbcf3cfbf450ff8e32b13530fdba6454a1da5deaaa6fbbde72a9874caa80cb13cf45975d2db86c65a36604ff2795e7b558ba18c3a610d02b48c4170968a89baae10a9b063c583e5543eb4e26e5a8a0c04409a1e1184d2a4b65a895f54a8345d8112e1b3761daaf06f8ab44045fa147d6376c84576da35327abcffa2ea9ad9cb916bc4011a3c9cd17f051ebbf0089d207fcadc924f6ea510efb376d7040ebd5955ca63f118fbfc1191ec4f21169531aa0cec08930420350c4497dc933d7f0de254e04946a942049824699b32b36b3d96d9ec30813d9913d45b349b0e1f3d43db3c77e61b5d58176a6f2f831d1461f3fe6b35d42443d1189712153f40fefd37d3175c19cf01a0b2b699e075c0944690db7860ee59f94c69244989da3857ea3568a48c5537c36478ae6ac14e9f038b4a5aa94a0cd060ab97950b50996b5ab613b57217ba48863fca4e32b39318601e98b29ecb4d34cdfdf24cecb0945ec25c2372043627b53bfd0db58f56d5339126b9858d8c08abda9a77722d78b1c10d50d21d849bb20e05ccece3d28129632e8471c0675975f2ef4dfb810fde6152eaae16ea4c8ebfa42a9fb7ef87e58a268dce9e3dd91c180a3763ceed2def19354ba37c42f9e4a681dd132049390846fb3c50b0235ef219e882b39941e78ed16c5aaea5262bb8f091b865963b75ef6967c3990c1a7c58a665790e132ad267e6b24d56eafaa5e9433ed1ee2cd3efb266a2f61630c94561e5c1bd6231acf0d93ce85c44242a00b41181afe48871d265d79e190f2f4619eecfe76d1a2ad5c6c6f134ac271ef45a26d7783c2f5db4085cb7c1ec0b1e9ac9f0883b45deaf0b711e2452ea5368ba911b25a580976071fe4ff72a6f991e5c11c7df7b9b5f9a31ede42d62aaf6c3f3b51bce751c7542b93d8441c38edd89abd370fbb61eb72321c17b60cb9b92c59eba65e5a1871027db0aebad830b5a498de1775138e0b563860812601e36e4a90b82a9c5a35616710755f28e6cfbf15dd6cda87039cf6095fc070a85390e2243894bba147fd6cfb9c6287952ad7c37d8291b782f24201344c52e0ec0c9c682edf2e8af99ba2252b63f2031f3bc56a8256ce7ccc5f482fe00c9ad987c33ac3eadd9c04218dc7377d4272f7fa1e2270de0e75c3e2ef8991e1ec9e649f6513e9cfebb680e99ec7f447b7ad47242d4819e9bde405922d90ad788e66b7b1f2a51cb4c991a4ec8c3483a9121d104cddb5b84d28263e5ce1bbb3b4f3c2efa879a33e1b8c8370d4e2bd381bae7b7028341fb7d2824360e2dfebf1eb9831a5630d7cab4b09be7d0b531d33907ab625dfcb8aa2f0d5f109ce3631b26e8e0d434c3aa3f0cf6868727f70e7d53fcc21383462cc6d2aef2037c6274d20fdb6ed1c5a48f67f5e445d5273f99475ef12afa4fdfde3a10f7a54b39eb63fe77a4e3dced42c979c744f703f7dfd3be7a01aba782623925cf0601e9d7fd0fb9aed9d0abb02cc5ad0073f2f00acb46298306e89dad930ab3ac59be8c67cf7bd0d1a677eaeddbfd2e8e4c806638407bf094d111016dd5ab1cf86a23a755eba03b1b6569893c308bb65081994c2341db9e61bcaaa5715ce14af4f57650f3b8ae8008098cf48185ebbc8aa89861a05ea8932109feed278e3c896e3ad8aa052f03004b52fe38a71ad66286de852acb2d014aad50a4b73bbc2a780bb455bb415f410d2a5297dcc654855b2db42ba1d3af9a0b8ef4c0b27289a118087cb12d1554edcc9003026d124485f1d24e4951b26a22d6b9c0fb3e778ae03c97075c543c9b5b156b68ce45e5ade585e72c7060ee3dbd640b70e676414e9cd07baf1614665895bbbad08828c563e6f68ecf6a0eb214d687a9a608f0327656971ea64c057edabaeff02aa25ed35e99ea0e8ed029cd6c54fe588576d7400975170499bb848c0a5e4053acdebdfe843023f94d8d133ee34cbf34760b5f9b385f47c005a28cab61d56f1c69e1ee84eb7839b7efd0186e5ffb3adb95d60b44a0b1f19997b97f196932ee0e8422569a740ffd98f249195d7d6fb9039b4b05062f7295dfe3b0705c962947f62d0c60aac5e3dcc7f3cdace496da115287014865c810c28e503ae98594f00ae28788211cde50813b834ec7bb12aca5585e6bec2f264c3087effcf0d1b8e6fb8db8c39b5a2abdfc67934fa39133a16ff3b1bdafe5810999b4d68b45ade46b72d905644ee61171aaf7df4fe6909d5e37e0db9944ecdd496afda1bb7cb26bd88a26043c509598064a422dc151f644583c291fa048641c75065b11139747dda2473c3a378de3d85eb28552861195c3daa1d08b763b762d447e12452d119bf532038079df1fb87594a018c57695215a4e2dcb519e7f989d3ba2c6a33c737f5d7276d41d9ac588523995fcf7beaafb4b51cc623e41988319defff66bd5a4a675f24eaf2602e8deabfed541379ca56ee3e6665866d13f8947755c23b935e52e9c6171c25926a453cd0162d87b66c99a5f1f9263c97a94ddc98783172afa933859264a925b593dbee32062491982416e5caec1c7eae1b6f8c0523e3ec433ca901abff2f4802e1a194f4f02007529a671cf2c9a07523e984e09ecad0078eb5d28dc5da378582242cd59bd2477fc1d1c6e2d5405320f7e668f59635c8a29e717eacd591f8d776bd46c36f36993f253d4bf4a1f5d0a921da07e0504e0c10209be1b596dcbc6401c0615cc18a596558d70e9c03d5b3bb76cbdf72960cb807761771eb8e69916b4c34bec8ac2c013240d517d413184282cf48753ca833d18fd8bbd15a11c7b256e8dbe8f06e9e3984e023228d2337ac19cf6557ddaf4fc72ef50dfd59fa544334b030c2479bad5de6ddf36a8d480c5742c5b2b1bc33d7aa2e80adf978041489aec2eb821ab9ebec98343cb254d6c08a256886a2d5073efe9d6939ae2584dae4c08b2980f5be102c2b2c1e4e834b2a103f8041da1073ffe5ea21341b82503bbc9da30d72ca3cbbf7bb7309c9ae3bc685b3ce9b5dadb372c119caf0918f6047bc190996da57b0e6f2bb92121663b994598790eb5572cae3329251f11a8e372c2947623db33fdd238803853e14c1c8bf9bf7ad3f3e852138b03fd43b0836e59ed68f0b76c37bc5726bd8462f20605681ebf260c501447d89aba12e0728631d5492c2c0a57c564ca626c9776208ab0b5a87d54ee2603ff0199092e1c3be0e05aac5bf457c3d710f612667aa4706bbcac54e12b0adf41bf2c17e8425c0cc3f9337e25c772f957fd5a0889ec20e628812d932b47bc20a756ce96d7f5c1a532a6f45d2146d88b9ccacd64c0d741c2b4444290d12e164e1acfea9fd3b4b6317924f705637034e117df47f5d058cceb8671bd88d29154b4edc1a380ad56abacaea33b1d3f493bedafe67a3305c96fa699fda68435c286585ddb1c8a0581362a32a27f397d9f86c92824ba2fdbbff67ef9ae85009ae55f8a6249541828d4d45da32037f2c7281c001e80b935404781e70bd76f5414397374399174194a82fc9c6562564b5934742ea707671dd39c49080d8c1ba82727c5fc8f240340211034f16d14bca7f45138a15caa7735d6889586d3146890c2b0592480ef2dfd49eace22b9520cd942f6a816ea57cf6eab6290ce6ac91d4f90334c7dd08195081520ea77fc39cb7976d3814e6f8e2ad2a2fa8ef2c54a06a3f891e196c810b0ed6f11d9719eb1072d7abc30698a1d11c0ebc22d6d11d6741e2644ad05c224296ae5899c3e07be59780f4ce68c13a50e240b212a17b0f7a5baa48fc44e936f112295a36bd265968f7b64f48e008cb17b12ec7a7a6ef762be0ceb349f620cb8d79e632c14774e4f636a56d87e9d8ca084e87fbcb68807d1864e35c1eb00745291654eb0e214d1d1727d73bf7ca9c0f90826720824e76f0e6a597771d75b0bc8824bf51e49f639fd10155b26a18daf298666c169e29fb4cafa11631d3cd41d1145ef9723ffee0338b4f47cf0f44f716d375200b67e1f567d222e2709c7f3de272178ee22b60ae7fb711a663a40109960dfa47faa50a59b4903b9c19581a9f2141190d8f40495da44394147e5029a937617dadea3dc03d4662115f393e38173d2e8a0dd151842cb098939f1ab9647c54dfb007477c5f56b5d5e0ed616e6773d5b26bba1ff0fd6c1f1df0974939f13c509980355925580cc7609f98f3232b51b3d19c9685c5ab2ab26015673b3e76aacba37c86c98bdc9e1408d82ca1741ffb92747c27b356c46194c63f4a919a322b52422891f094221d7a9b8a72cfe7b3f4558162233133bee7274e34aac4c8faa867cbe30c9204437731ba53679acac02d8441de30e296994cd2ea0b5f1b476527e3bad1377c103ae6d323bde535ad53aff6649634ba5eb43c044ee914a801c28561730af15c3e991e0a3ad1abfcd108e02e46fb08ce4a118ed5ac9e1520d6747cf8fb6429033a166596f77278137cb66a5b3d4062ee9fc206dd5c5686e928475333356ea6aa86a9c4edf987c78282031943db8b997d7b518a98b0ed56e7e058d1eb846d1987f56d703f3ce4ace613b68dc0145724c011006df741018eb26d3a90e4256be98f876ced0235a25600fd8b4aa3c9fa19b483eb83662651adee001d345e69c6199874979ba9b8e3c12a200b355e0d8825fd6502ebf4c29f0ccaa6a9c5f6a27ac8bfb56436e13f14d9e8801ff3b751c19994eb0753499fa12490d27a3dc8b33b0fb8cb6e1905bf0a140b1b0fd4d62cd58ca2cb7b706eec4ad2bee14454502f46db72d403bc7d9012483f40d78eb3f6364686deef3faef8b8502deda960dce505f87fc8576fab64965c6b41a18622a2f4d0a14d85e221fc1c3c50c0fe334406e56545cc3341756a7be4c1e4ae326cccaf0ddccd599893591b88f841ee2bf5daab39a2412b1ea1a97dad2cc38611531063ae78dfc0b2f823a3f185f1e2a3c0f5e6e2c1155090d73b1105873d803badfb59677b554996c2f0bc1f443fc266a200715184fdb375313f077c5ab4c3b6ba4aab038c2691a3069b2eedaca20ff3d23e286d81b0f134a35d79f5c8c48348a3a57719366a0b237ed724842cc393f674f41a7add14d075951c1d17fabd91cb19c6b2e38d278af2f89f6b63d9ca4d23e15a8dec637e57f5347ee12c1512e0f1557e9ddc3d5a8876ee18120d76f1e8e3c7f1ef8d9a605ed24d02a272de2ae89988b8bb55a12b0a860614b2c1dbcc10b052c0f89d638c88e2278c517db0b05e7c4e8cff1ba0c226882f18598bba1dda5656e286f9d4e152deea4030a7ba3df174f20079493cdc636c458aa84f0cb34640aa41cd1cf39f9aa1a1f06356ed6077a009286102b8b48f46a32cf6ac68864c24aba18b2cba7e61aab172d81fe4c8ca5ee8ce26d2fbbc406e8034482a6b7cb1c4911e45cb5e8113d1728cb38c84fa2c5113244b090f7d56e245e2e4cc216b6c59dd1fc2c0ed89b82ca88ed8d14438c60445c003e238b47cd90ea0c1586498cdb787d1d0fab298b2beed5469cdca5198b93bddd36e479e7966a25c03f065387884e7fb699d3f64b167c2413f5f2730f595ae70f5ea33cb3e83ac97efe5c79e3229e29eb3ad9c111f91de4f4619ccb19213dd4fce36efd4f6c66a3c2616cbb90094cfa705fc96c4b458990ed1aba8af4578368386235899a98dc4d8c2954ea2d497c03f0e1b714c6d842c5643a49e39b1cae22133f6c4e89a38f679b313cef8627fb3c2999b3239ba07c7c3611d9c532f63271cd876c48cf0df6685480d570a5f978f5297d3485dbe2cecd3bab699e6d5db777e46a16cb612eac8fcc1046c422caca3a28553e26ca349cb52ba23b89527cc288f4e06e0231657887a1734a8f430ff9a126d4bc670fc6a1d4325d3222bff9cf029ba18a82db76b42e7b558e8460f5c1c437d2a1a9d287831901a3657e372ed681df847ad4840b47e2f98b23b6ca1a08f48cc0cd2634dd6caf5200c7b4bdf67de499289a96f0108b3293a5f97c394a597a1d59c793a5641f6929fd633b42b201cc1f29a877bb2479c483bb642a55d4a7ca6471338e976b32f1e8bf01001e85835146792c97349c93342c23e0d20aad0a038ded2015b1c2e264dbfd3b5300c9ef2915d87e1caa91b44150e3050439c3d2a19d4242f3db722f7af3a246945c93b4250a5c1d2aaf7a7d5805cd43e873e2a7371735c81fdbf481291e92b3e05d0aa7cbfca57690b35322fd17eb54c58afcab81b411c08d5ad057cd8350d3fe5ee1665f50df4e94073d273e369784d1734359f65be9829bf48d8f9dd9ffcaeb93f8e0833966922158ad741e52c13bf6b202e8db773f13511565fdffcbd9d97f016f87d8b623d0b47a1577f7f16291cf5993b20063d9957277411361a8bcd1d86e20930b1069bfe21016be478f0033e8e04a60ee4cb22e800f049ed078b0406d9d2aa1bbb1d075a556275d61f29c18b93cd98da9e6bf74276c96f8249072c55fbfb6e3af297493a8e36d0226a456e40b6acee6761c0d1eda24c73857dcb5d6dd09b39abf76e12c9414cb5f9bbcd9db12bce75128fd04411c462c801349075c5647339eb1ab30b5bfc3ae3595b3a3af97565d3c717cd7134550ac043016eeafdbc38885f7e2456b7df942bfe65c5d1b7aaecfd8ed19a1046c9e9ecf5cbe50fa74543b3508707b8a55f727f0847e6be9e27823f9b7103767e8e0720ebf67840fce2560008b80c6cd696962994ac076330de23052a75511efe1bf339b33474b93d492f3c94b59de27d438617653fc6d41829e70808aad7a32439019febe8f077191e38b31785f6fabc3e999e97028e0105a6b6f91337034c1df47f14f9c932ef0178d72c3e009556cf7c3d901c0a6ff67023972373718418e07330bcb7870fcc3c96d355795f8ee799a7607ce96ea7aaa3bce9321fccd1d0231ab259a8a9fdcb9072e86e29d6fe0c24d09f7b43d6543f87e4b2d9389d60075b0575ec30d9b5a13a3c78ed0666928439669b01f75a4a1bf7c116a75f8a266ba63139cd72e8d03bffb09ba44129f02ffdb38bb3e9a65d446b0ee6acf3e90d1d613f91d8fde09a7137eef7ead14a6c5887327c603f45c6b47d5dc50966efd7cb1825d8b3c96f46bc9bb8d907aaf9078798b44f5bc35c785da84e389c2afa262429e9b142582ec97038964ccf0268472e526e168179971479c110bd975bc64ccf126ad0c801ba9d6c1f79fb42693ae8103900bb048d188d53cb020037b813e4f13002355a98ff3e7b10b38496dd6268ed6121ab138d62e03e7a1707d5f9643b0fdb6066b4e090b01a95cd9365dfe55c9f77a55983be74a42fec61b7d21aced0e13f31cfa60244015ef5a4d371564056965843b0ef721cc06b0937269bfa5dddfd7738cdc7c7ca422716bdc12b08775668d13c52d1e4c193991e5f7b923fdd0f8ae8e04fe0a7a0ab5de7a0a2e5043148c6451c75c045b2bb5d88a628c42b6f2b8397eb81c8e486af0119d36eee1cc836466741b04de22e42e91c1c79b144f540c3aae95614751c99ac89cd66fb8dd224341af4a237eb34733ea8a087898069f832950d14817ce2019b1381dcf23c0cad45844b4f7be3438796deb015c5878eed36536f69542705263bef8dd9e02559b6ad65db1a1cffe154fc62b22792698aa39b7d4f62abe14d220fc19b7fc4cee3699aa1e101d93a49d3b4d8e7e4dc56cc91fab8e032dca73a09a616613b56215edb1b1a909f4f490c6a60467754366f97bd3722f3caa85d8349f2c8fc71730a3e6682a8ea46e3c33bc32532801f2cc9d18eb0ac41192eb22e47178e33ff75d7db3377f4adbc0cdd82f0100470f5daff73b04786e28b813328d5ab132344b4d003ee5df49828c7f6b88fd2f867851e4a3d4689690393a4882e785e6e562c8f5df0a3ccf52dd76c4d4f6257fb60092a4afad1231cc292e8f084abc90f725eb168e5019fa2a3f0fa20ef383a2d8b9741f081493792529fb6c38d63e4224ed4a3ca65e320bc5c18c44d14c1c404db275fbc55d95084bd8b67808c9901702cd2d3bc23540a2b311b0353d97716a10d9d085879270ca8fc359bb043a2c7831160c35dc09f56a656c8a2525f001b4ca9c759bc02d7f2b39956b2bef296cbfdd9ecc5bd94f47785725cfef1dfdb1c29b41a55d931c21f75997d35c297900dbcbe70fd2cd6f0f62f86cb666d76c4b55c4a418b9b6476a8e134841ee171179a9a19e59ab78b6eaae0ae89a9fa7c8c9dd5e87147f0cfbc51e88af4af929983b3c75f09ae282538f2f211b36eb571c98bd1345c7c28cd602bc2636d54226199ad599509bbba6a572c4bf2f57224a858f55a9215d3ef1bcb5fe25eed5d60d3cefd9153cdf7bde0e82e4cb01568bd9e0c00505c3161f9353aba75d97c3b7b2fc0ec251cbae66867664dc830fac61dc0e0734235e51783443842bb825fe1d61a795af92cc96b13dd2e2e9e9c20c7203b3ab3d4b8899b2290c5c561e017054dd88d5bf0157cce862f98bd4643783854d51be06c4cd4347c5da2c45f6d16f347bcfbff4a4fd674ccd5626c0c0b04d0ead4cabb43507077d7192207e7844758eca5f3ceb8b82d4ec07d30fa66ebe47c0d56ec1d1717b2fe6a7e7b95dd7ac36eb9097c0cd8631116da273b688b21f41fefd29f613953b453311ea97fced2c915f68ef182f4868cb16d2b8b0d566e49d41f941db6c5176a752a5635f70b8a608d2399d393954f93df632f4f9f03b68bed75cf972b3721686d04651f3f0116053fe08b880516fb129e9f79c209833251a401c7faab62758809564c0401027839add4f4447c31a934459904db885bec3c2c76d11ebd43a7245f7d979cde08b69577048bb5ca005184f14c1020a2804e2881015a30b08a9f5dd9572789abc9474545cdee87ba7951725bfa957c31f758a62769b7691ba9936020b7892fb112f453f0e4f50bf7f9180d24c317c726f5c32932176a499752fb5771774cd383849f8262c049e551d4324847eb68878be0accd87edd2afaff8743e4389c89db83f6320ca5b21155ed13c4e6d25828a20553199040e75b874de287414ece8c02ee7cbc7c5e54c3b46d66ee501a4b401ca0905395d408c519b0e8a205e38af5c17d68a17f6bb355aaad015f04ccdc19ce03bc4e11e7015705d4a37404795b8826461d300f8678233c84fed5b9a0e70c0588114fbbb4c623b2a31af02c8dd87e26a1b1902df0b14a18e64b73529654a524a19eb039d0328033c600da1a0a307ab8d53d49420ac1b8268d951a9c192a34241acc834c384648dcc18f960adb539f05e1fe4d0cb12d011e5c4eaa90a8f1254979d08542b84d9aa91c5a8698525b2058403135f950d4c587ea040162e476cc00295b59503017c8874c2097f2ea58880bd176234a71e3634b47489d206033acad4e881ca19a62c5158388acb859c9ce7b33bb7d6dadccea241626709b6a89cb1633123c9069c0e94d020eb2011910a56497a54f899c009905ca192eaa282561b210e82cf8e0b07873012cb07452d92d185b2ab376cac97df18decb8509b4ab327c6ace30e94961fd6068f570e64a8b8fcf2e864944c86e16257956e087f7d21a3fff327578f2f58632c1bcf0dc9a426806e511a164eba525c4745273347237ae5e61700ea7cb231401cb01801aa2260e3d0c96789ad24c2e61915251531154a6b904b21d8054374c21436fac7123bb1c22dec78f0670068959e4952500b3c9bdf776fdf3b8f7aa2952c5a2302526186bad4db2f6286beaec1028da63b646490f26c861c90a2b03266bcc70656101071e416e93cc0caffebcb7cb13c9a6f7e3ffbfcd111c367257b5dd2200deae8c18cbf55736fe47a5f48429d325c5c98a09571215ad204e5b9c87e640604bf06462e1377780cab38b002a04b2b0d2fc08e9116255250743d6128f141ecef070a5b05efe23bc4b6c684591aa31f81ee60670a2390f332505390c095ad343ef5e19916543072818a640f9a2e4304819a9408494102c87d7782f24c0a163071532415236f03285ca490f3c62bcb88f0138a9aaf227854b95b560e8eeffffdf92b05162fe3e60b275effdc9eebdf782f44ae85e25b938eebd37cb2cffc5c94ca25597a629a92524a8a1ca951380bc9063d32385cbc783b03460920ba706504d35784d807c59238124453f45607e80f2d1c4cbbede23440f0d275662b0e2ebf1e3c2e40a0e68aeca6479f282ff6437e14ec4eb7d3f52383f101c08e999485381cbdeee6564f260f7de7b350f5c4567800cf14133c34e8c160f21565a4c6451269c07d5d51c69010bf7de9bab3912efa07e9cd47b435a43a946d38dbd9103e84ad4161a32e820b343325a12c60894a22940840ee03baacc5b93d820c93fadae6691ae2808d680a317688fd4ff3f14100e18880a722f460407890756c10f7884e80945a68a7e7234d07019a3e45463cb162740565ca81cbe0b1ca85efdffffff236007dbe4baf75e211dcd91424138530f9a088153e8def0056373af8309b5aa6653ae2c9997d20cb3890a0910887651a60be50bd0ab53904cd9f9a48bc2c3feeb3093161c973d2589f9c1c29a3339a0352b44598145e920db75ed6ea8b2a4644483ebd8e0e8694696225351b0542c3d8039c150960e304b557c9e43e827fbff7f90cd64baf7de7b3ff3b93e4bff7fedb66b2fae4969b3bead0828bdb6eec8366ddaccf05a71440bfd220a753bf50174daf6706729f59882adba766cfb0ba4e47982b3903109606a9bd55fd034d072a4c5041a6f09a4a9a831de96ff5dbf30bd228ddaaab619e57982efb7628cdf2981c26f975555c93728ab39535b9550a01e53847a4cc078623b09991e67db6b8eab085f7a7405603de21b3dd6eddaf5941d90300df042f6f60d487e4028666aab9a84ab6b2e4b9eac5e7d238e748fd20b8e8923ce395767aeda4e8c26a44b8902535bcd0fb07be6248aaa5fb50c6c80daaa154b38e1439e5a45bdb513e61e6595f842f6b66b1fd102b7a9c6ed9b696fa04155306c45c34ae9dedfbdd7ef5da2d65ac942aba7e58b6951e178121959be78f945916187930bdc30e3888da62527132019724477f49aae4ce065079a22412d4c09f3442a089519178e3c09fa32e4d55a6b6d0fa61ebcbc46aee05599786a33a60368764678d63851218a48d5db19698a72438d23fdff67650de5185680c8f4c02a23464d56578c042e63ae5c18e25b410537c2429d3c3d2a73fc9b52bf8f4ea3c15c929249d92591bd2530314e15e4f7de9bab39f2de7b63cfa493533a6bdb1879ba8002f5663f9e484961e0c4c3a49a4d14ca15ce6ba88b88872847a4230a221222da610140a705a635c7308de9faffffa71131117165fecb6a425522f960373fbe93611374b500ca784025be7f3032e9c198ad1f75ab1f38dc1d61b02331bb485e0f1629179058e9f1b510e573830811b8b4acf87e4192fc1cfdbfce7d9a58f840597baf452202a5c4347a187233e96a0485a12775d7c8ce02aaab415f85d8fc50fdf4183965d9a1378306d696aede0e183030a25d3f6c1753acbe0e97e5c3edd9ac57f253d273a2b404232652140d7269c17b9f5a7d911e2594442a25e60e3fd5f57f6d95ba7fcbf5faf11bbfff7f5ecd9118dc41873435e87260ac2e8e1d429c40c1150f128cc112e5e3bbff7048a727afe6acb5266270e10a0be1c17526c606dd0d8faeb5d65aa42aa42ba4ad2e9edcaf38a2fbc7f7de7b1d8bd644694ea1e0bd5ef8d629494c2094b02c6024840b11263d348ce9f2abb26404c74f942f2ec3ccc807abf7dedb7b0f7ad9efbd61133a15c25318326c928e0cb1e9e04a19ae2d696ee07066c5c53931385e4466fa5485e12ec7f3586badb559319e50b53af86662b207435bb0295914fd7fb863337739803b9d05a08e74e1d2b5e48a56d32f8635638238a541d202e144bd3aeb3520033a43831f5050a6c9488fa5200dfc7035b504074f39e7b1eaa8199f30d3ad6df558654a68341c666aa60a6905a766787cb227791477b36dd70f7b491d59268eb8db2d268e36d7a1dbb74d30e9d85f94f5c260f1d220cc7d3548633b0ed83017c17e3d5ff40a8c406dd5c50658f2d4321443830f97bed8b434e09a74c1964cab1e7dfbf42df14080fda223b1d3e768807ed3b76b920ee3d80defb5ed7b6fb494316c8f99b6d96e371c0e880a60e2a85d549d9928d270448ca58e02ee4b8f1a5b99d5ddd02bf10d2b0f90868f5ef19283d7af0f6cafb1166e5bfc764b634a0e5ecff0c08760fb4f496fa44431dffeb5f7be9e6fdf84706bea7f599d5fb3a2045f7bad9602489e5aee4aa7ef6caae719a15b439c82936d7596b247da7e2d5d3200ebbff7e268981e31a8c74c8be951db6eb812aa0e8e987138d9ed7663b3c5b40e693450ac9252c5b63b0ed856632951e0e9af9a9436d71e0321856ddda902c6e9ef6308b6e3305fd9bb01c2940297bded224ee1d227c3b3f7c6c2cd8a4afe74cc8ff7b79215eb9a53350fabf32e67e998cee9d3b530c4385f8cb338871987c3d196d0709869be3a65e2a7e5bff746be57aba0ffaff7fbbdf36b13ae49dbfdb6b09b76c99456a4ed75687b0542eb0e5a6f50ea03d5a9bc4abbc4dab5ab1e5fd4a4a4ca0bc51183e29869af6ab3edb1f26eb7dab5c7dc25625bb1e63288a1ded855bfd7249a444907ae49b069895c8deb8b55579d98be3ad764b59b9aac39af1b886bb8268332c6612d5f5d41517cfdf2c0be795fed25bcb635b9edbde28b5b88232d47b686b78c9970db2ede5ed47f788e6d2f66b9ec943f4bc72f6608f06145cc6c7bb5f1afff7d66f5ab8635c63a2fed235ebb7ed546bc7e692cd26a4ed5339ebffb8e03dcb76f019dea504debdc533bd52ea60092a996bbeaa947ec7060c48e8f1c90d4a6695524a182ce1e98433abd66f5acc190316d2d8bb4fbd22e8e99da68aec7bc67b4574dc6111f5d63cd01bf1f8ae0daba96bbbaeb162e5ad1a9ed451913402fc682ea1adbc13acf689386db358ded482d475a8e9e4a152841290210d0fd1a1c61bb96246cd7b202fb61c8184f6c0b636eaa2d95188fda828db3fda58373c7559cbf44893de240f86dc52c51548d018f698204530ac298c0012108d33731f15e18f529fc8281e108b89f308f10fec01fecf7fae1dffdd19668341c66772ca06bed557bd5b48a31e8f6aa46ce39e7e7fc185f7bedd33ca385be85c2070a1fded37249a1a8efe90bb98f2069cf1128ede9e3ef780a457b54076b1769d4e783563d12fd5d9b2ca9e40935deaddfd5f177bffbddd474e86ec1826041b0a05dc7dff9aacf8d20df769eb5c183439ce9e3170c74710bb38d768446c361f61b8f9aecb65b02a84e2c86058ac168f896801876f2a8f8600b4c93436aab2158bbb9c85666f796ad826dfb61f665b1b5369dccb1b097c5186f6bdfb65df47a02b73effff61e8e897f8fd02a0493a762a50e27e563df55bd1eaaaa96bea5574a901c89caa471cc3870c405e7d03f0e5fd1da3e04478ffb8090e330ee1ae2ff8929d8eb029186f061814d8d4cd440b13c69360a4f8c07821741377b37d187391625b6db56317315fe8037d36dbed769fa4c0c9411149b1c1e94df08bc4b63ac8eba6c44998a0a26388840f0c48679452ea4e5f5f7d4559f32c2793edd136ab61524a84c32de936a94ae2d13eedaa6dd2119b645b3dda2712fb33189a36690af976d826855b56a86c8966eb9c502fa9f69a446b23dd54c64e06d29e14860c1aa482ca1a8c1a065dccdae76f28cc745b7156058097a71e6795e4c0dddf7f25b822faadc1f98a34d75507d4ae6d27a84e882bd2af6c7506684ecd738ae30b1c7d3da7760f61cf08ece9b24a05bdb7468bbd5802b5551de6c8aea71e65d54ede4e25d7b4cc71db08785bad42c232b63fdf5ac7985a5e5dc006dbe66c5bdbd6826d71b67d6dab2d9108ed8b893a1435288e1a268eb65908eda228e2a4cdb42c3eedab1fcaf6508416f269bb7ed1ab9f5eecf4695b4d633bb22a282a98209fb62ea47649951ce50f5632e0af8a64d78e65981f767d712651542dc391e709bfc77cf5ca926186ab595577415e5b6ddf7738066548f1756b4111f6d25fc2b6faf5bedaedc57eafb59a6a2ba2f07b67f9a463fd35ab69f55fe35a047bdd44d7606dae88d56a39fb75ae1b889037ae61a0e6728235b94262f1e2400c51d18171db454c7fc796d5f48543b01adb5ab63604cf3d5a4ff79c380eb3dbb6dc0550e2aaa6fa45a9dd7dce266e797048cd70c1019182a8048f37d596191904800023170000180804038241d190222749e93e1400093daa90b87c5038228f86e280300c0603c280601884410080410008831880a438c693ad01328aaf397620091664207f82c8ffb94c94ca28f2a22186b8faccc921abb45221e07f36c2b4ae35d6075e817f7cd1d7d5ed3462c30e02f432630371434ed80f71e138ad96722dc99a042cde33cfb032b8df92f685ea1dd8e62dc586836f38f0019b9bcd32db88e44f0e274d264f0d21baa0465e136f67efcb5b2a3a585f3c936e80d5fce88afb44f630c9e488b16f56ac8d6a7037952b640bbea520cb2d677917217ab1f4466f6fc1afbb66719be1519965bbf7aa6fdc1ee0660d8c2c632f97ebcd738946b4fc2bdacd00af9d069c1b36467cb8cc7b563768065360989ce863c19b78899c4cce121cbdf9c4c2f201ed318f087f59d4a61b795d1d1d98a5bccfd4f5d45d04d27dc75a8eb9f7e7ac433184f1a9405d29e22671a93d954c47ee3d7487c12561fa74c0c3b183b408f8db10cd64c56ad8d12ac0ffa52a30064f2bee7185992c4d6645f1f96b048d49f25bd79f47093bbc8932f0bd4ff42955c78e25d5e8ecec10187c5fe22f9f93810eb97201aa55732db3595ecfc6f5c530a0707c1a3fdf7c029c7509fabc611e09f721eb827ecf13102b362fd64985c89ffe03253bd4a8c3064d8c68c81748cf6bcbf3de041d29f779c7f39d35caf1fddd0bdcf625d77f1346f3541a4801d6cb098d212fc7af044b0cd7dfe6bf29f14226299654e33380e9da40743f09aeee05a31f884fb260e0c1703a4cb95db670a050997290410927a38b372aac51b29d2df8ea94daaf3429e68c5b99f496ca53baa8760dce0fb643abf0e80855765b850f43ba013fecc220939941594c7221cd500066b7f5811153f634c79e89a4a73d158da084072923c93eaf0dfe1b0508f90a6d65f10d7232dae76b90f6d3dd288c148863470f32a4810c255908d57669b4014efe425e08b03995304449fcacaacd1ebf3ce9f1635d4dd9035841a5450718d61dc0dee679baf7b18eec316b0944aee2c40c72a59b1e276098225f5fd13bfa26676bfd6df65536e7ddfe83f212a8c8e1b3fc1ea2afe5f4f79daa680e6b30a4401d1ce92300f12e2789e0e7869069633beb3aa9381300ad6f5df8faae2eed5e0256f53d138d67f156a4aa29d1d201fac7db55e5c61e8aae11048034e184f8bf174d3f4c72978969b47175bb9a32484a6a68f6f386041636ac748094e56be01077ff23008e62cf1095e22f52c5cf23f5c63811fc2460863128ce05a1bf80a11d9991fd0428b96d878f1cfbcae64e2eeb770b38033b7c5a57c3e61fb8024798589afb5342f8e1fc235e2b7f9608aef26e53eefc49823ef02690348ca1086f9e2fd48a34f2cce1d866d919b698d801c7fc180915d6eba48d1c623b8251c4951de9c198d4a4fe56dbe0526205427f384fdf44e430f6aa09afa84210a75771a59b7fbf398248240ca5af1c22f6f82fe391f1373dcf50d07fb84540480ea8b8dbf0508814fce04244a9726e6f5664d1132edd91c6272c22e5fe06249ac4746a7c1759a0b7afbadcd1fdd7fb2edb439e30ba1f3dfa80e38f6302e0f3fc4a284e37d9e663d1b28725cad04f9130b144f1563c9498a89b9c2ea81c088295d87199954a988b1dc718228b1cf5044c08428e09abe2d9a5fa9d48be79fa871c430052b268b41383f6c4a31edf76ce800aebb03635ab1c553ad6e35e05cba81a783ad25785a194a733182cd70b76aea02349e3a44e6d9a8ca03da5c80ee8b93bfabf45945d8bbf178461f5ed29644cbe16e1fbbd1da45a509e43992cb14c3cb6208db1059f657bc92c64b0762cc081b5912136885d1f7cf0eca27fb19498f6c7cb9eeab7f0a2f0f99c3c9b96afa3875202f7b7186e443592104dc565c52cba5a0a46f5678ade27f7df7e3608a2051b11f84db4fc74ea16d5228860fe7e63e0f78a06722b004e396441957ce8c41d71479347a710715018002e6c3e52cb15851666b40ec8d663e96ebd02995d54160aea95b415c9751c5e9a381ad742f5014e0037702404fc936a723aa8afe2f8901eaa085c147c8604ad090a228ee88f8ca5ba584ee9418766218dc0dc08e1ca08c285810074e0b528a3eda68820b94e4c3fdfae325973ba9d35890e39a734784399e2acf562822a37918cdd15bbbccccd229d53c22df6cb89597901e7ad88f9d89014dc12cddd35c2364675cf36a0376cba1a19f7f3d451a5a7cc1d9b19d059117f930ae6bed8168ac35d4dcc82c990d882a13ecec1ea30813310fda9f896d00c796827c8f6a8abbce87ad27abbef8415b035cd2d4dafdb47fa28661237ea13133512ab5528158143f2c98d192221fa797a1057c56ae27630585cebb9879c45efd051dc2226798619c0fb5183865ea14ab6b14493ae99c047be64380d4aa93b8d937edd347b49f35a52b29d792bd03aba71acde3f72d8aae259e6ad6802b9c652d488470afe027349fba9c8501b7d145ae0c8060c49a27f6f1e8e469813c9114a05dd5ef2b0f5e819a13bdf7b55cbd09df6de38d44d6b0e65deb1a4a0e4896370c3984bb294948e4c759343cd6721b059239ea8db752511495f6afef090cc6808b968af86e52f32592764e1ece95bef78a2901c2f8087f602a935e51deff865400af364b63ddb96c413cea3a1b0aed04199fe019b95770c407d546a19b37a77f9f892990abb6305a248631dc2aca362f2c1ab7da6090d4ad92eeaedc4fa2efdfa78f1d6f0714c3d7c6a3093758ab9eb66ec764f325993e0f9ac1272935f17fb0596f077b8495a9b69ef61ff877c9218321caae66e370ba96ea88c5bb1d37f41adae41172d190c8d43d743d0a06bac476aa64e89181ab9273d8bdcd7a29b963634e92ddfac13d2f53760f3939b8b70508dadd85b91c2fec4649e471afb7cd8b3231c98794985d6c0c56549b05d31be1111757d4fa778c7e65111720a3ef8f8fa1f6b94c0a62881977f86468a170233d7ed64229276ac57943301c9a89400a791b6a7b1a47e62339f52fc5ef4e7392c50e96af9f2ce54e2e08c7f73386b79cdbcdd41580a797a7221ea46bc3dc6f4c88ab94201d5e363141db5c57882ba095abdf86cfced2218300ad66af020034859221532c665c96a3a1e318e1b491af6a6d15ab43ea92311d3df857b614537553f9c38cbc517122f5945cca1b09816b72012e84489c8267f5347f5c9d25f9e93efbc0e1287f41be474497fac89341c26037655dec10bfd020e7b3d992c91c83a9828ab9c4139f8d4d23db3d44a4b669e3cc1e4c2347ed9c9834aca89f30f07dee019e5049a3bb80fb89746475f10076810ab7c914222a1fa1fc7ecd2c51fa224d8a7e922c4e0857f1f75dc553851ea8fd52912df3b5f853646320ec46907143d320f19b0693f0a24878f515c4f6db0064a5307d908154a97165af06dbb33739f060084f8070611628aaefacc11326cd62040c0311e2132c3cfc43f9888ea5321e01f62f36153afa6424558b831f824aef414b2338baca78c25ba12775ca0258092f7d4ac5e8d7046f98c7fcf8ca6e9dd6339ca97329ce13b8f0c57c54f913149392c07a6fca8a68dedf3275c601ff373f58ef970894cd718731b15cf7695380e2dcc6601d53b8d00fc166620e344efa1aee634efb72b85be6577a2d02745f58170ceeb96bc849647fae47f1bc71bbddc9c6e7432c0c40e24515d943d5a49519d13c879db8a9aa03b2760555830dffdefc5c09e859d9f77fb9a94003ab16fe52b10eb0b68b794279fe6178578489445d71b45fbc4143cc0466e9ba64e906d51cb0534a460f599a043b20986e5d6013355c6f96803510a41b7b23909befc076b68f9717a0e62d1fba28fae413fb963ebc7acf0bb208476d5b2a20ab671e7d14055f73b92da8bc0ff8c1dd2403a7bc17eae46381116b91aacdda11b7b457d4a5d5478337d4c42292642b0f3b60e7fff3c63375784b1b01102876831d9ef60e790baa1ed0428f35983aa4013507302d9918cbed8eec48c6de212e0ea621190385e0616529639548508b4d557753f8b8360f84e46b887f3e9cf9dc79126a4da56db92b521647ce8a5ef2ad93c2c56bd850a18bb1a4bed1e37c2ce4a7350facec3fb3f42dfbd9bcba98a3016de14fe91f8adae815d20b2d3335527e33ccb461d2f5fd269ed883a07760e0c55a97737aaecbf9a80ad7011768fc2bf9a94b9f5fe1eff408e0288463c22a71f5ebf7486b927d392fb14ef2b6d5cb3c48e78070199e4b5eb79bab1791090d7139884155624a5a1f51314b38e3813eb4a230c0382f0aa583dc720705b7b670b4f0038241c509cf94e46254041deb893f890947414600209902fa500b02c1fa82f4a37e471f17cc1def5d60897f4ae7f7b72c4a5de99ba90f6febab6bdeabf1389586144a85a515fcc1e8d4994d59457c6fba34beb541515b1f30f129fcab0ba482b2942a864c5fb101432851099b6072bb88370bd1bda82007214b4c04da8a2408ff33bfa49ae8ffeaadffb6261ddde4303ca44015745a819e2abefac1670e771d607bc0443449ba0603846df818e6e8e4e2621139344ae6950f78f674a0dc51e51ce70ebef4511045ebe0e08818b1cf0f2ed0f4af8c4bc468046ccc440e12843f2cce110ac9f39d6210dbe4367f91217a3f5ab4b29af76bdd114c558dc1cf807ddf70552f17b1458ea929f30aa187c21a6c7991bd0f01e316b03841f800980393197e090b6b966ca3bba3d06d6d6a8164f7a0fb739db04ddbaf3aae9060b445b9f1c108da25448872a6d5d31ea5bb424b0ae9f1a4247d5b93892b1d48740e55d87499c52092a5a13bc3ceb0697b319524a371b795763744be76b6694e075c96120a11b6028d0a068b41d2ac5429da64e47df90f5decac5e0c2d73ceb0986d5a30639e7e8fecc50acbaec856bbdf78156c1c728a82eb89a02b59e8617d1d3ddad0aa91e2fd0a2bdf0f69c6e8b66231a0a7e94174d17c90fe586a1b83b09cbb4fc7410a1c4a559dfede27279f37c8cf9b62182997069b3a030b2c6751e28bfa11d205866ccf9431bd3e893f5d13568f8f91d7447e94032e545db9a8df5f8b6c5bac85948eb4e84acd148f59c58b706fc6d9ea0b25368853eefdb242a81456e813ef93cf6ac76aed1a1193aeefb22823f4f67e82a14c7ceee637152cb30614bb9a36f52226f9033ce608b8d21a800c55926a29d8559ce59d3868f53f9827a01160a30a12fb7947ad05900741251366d18dd31bdd7e0370eca3f65e7c281292701a9ea1cbfa2aa593c6ca9c2795e0eb1146e7fed9535c52545280d2f349700efcf9f9480626ff9df067eef44ca44e28a9bb4435255fbb0678fe9fc1e80edd8ce83fd16446139469f60eb30c4966414cd1c954fddb228f724c1ab993ebccf00422f51f3cf383bd35fe5f3c0324d46b3766a94bde369c0c27c6227662fba5a84511528ff210e0886211dfe777f13a572c24414296978f893144e99c709425dbde890023865f78c02d9683e31526b6efb8b6268a0bd5d3ff042fba895cccf2b4297c32fe725dde910dd9ce4c40e8c78d47b957cb01f2097bc8e3432cf77e62c5f90864c1cb20ba7462d71392f751a9b651c21b3a38d33952236baf851ba88f16707d01a65eb50c6331e6f725194f3170b849eb67c70ae0246be27898362f9bd75bb7aa836fe2d2207a811e83d525be6bb04297644e18b383988e57bd06671d4c887a4a417e611a07204f67be7d7e221408001e2fdbbd802481b3509672c8e1735627694257f342424f833f822cab30d5d0ed2d600812921ab460ad107c57c1d5170053d27121f62423e5451171fb06bb14028e939f5cb8238b2fb03fb5a90f33c7dd406c309c95a450f3a8886df73ed49d2c8e4bc9598a2390054a2830fd4fbede8b9c6493b2ea082a81d41d2b41c96b25504d4a381813ebb623ccebd1041fb34b3799a34348f9696e65e26fc7265f0020ff15990c1f4936b5df656b4d1eb8c82dd111f9347c734c284c68a5f1816e1281ae6142ab2a0433cdd477bdff121181c11d4a902aad3624ed435ba805c98b8ce62f244a5f18f4cd8e39302759aae7f5084621f998a4f3b0323203d01b8b83811341b659e7046f40b496d077b61403a639ae8345f5c2b563d691a150fa3b8372a7afe658cc9ef8fe76a1ee1faaa65b350324606fb939e83be898cd22c2dd9f182d5c83a5023f763f94aa429b0185c535af19b06be46dc8ba93a7a0ffbac931768ce7273ca014b95266c643b42fa81ef81936990549d0b1280d243f95b570570e5105067f18f7c5761351b05f1b45288e7c807e10436a680fe665da6cc9d7c9255a327f0ca2fad39b36fbe7843d1f2d80f3a71543ddfe27dfa1291f6edd60746e18112df0e1ac1260c5da93b9ed2cfc67a47b5f17fe18e0adc8a62880906165d082e0639fec5ff69cf6b8a89eb11371852ff3cde3984acc9f0ff0ded7a281df6dc5dc73f0075c24cad4852ce8b7c44b99e4cc537a2f23589bd2b3653d105214ec010903d60e1f5df8e488e150f036157c58dc7510d6c5ddbafcd65a4741db1283e5c718e3144ff44789c8ec06a6ee293d2c88bfd6cff393cd1a105e40e8e495a0d7d6a8e561b3cd99173958833acc1b626797f81bbd83aa02e3aa19fe313e9bdf534d8c256feb02a3c80c3ce7477bcab7d5059d0cf283c703efb50f171d6a2c0f73b2e1ab0e18dcf694bffa52689e1706e8973852a165ed73cb4427f43cd76c5145287be94df3a2c08fdab864889064f42d6aea1f09d44adff70aab232418839415dca01d18eabfd7a3f108877b9e7bb02973b5991ef3a77410fa86a395289afeedc82ea779453747b5316f2d484f1a4e0200b03e2aa925b5fbf36fafb8db8b9375e1a5b863c3fe7f1109aea1022806d55e5455b041c69b1e5004940fdbbd57f6440ddebe8ecc3060a67f07e8e0f1da83979abe9810e6db6c980785e2a2b480abee610eb3a6610577dde4c5aff87f99953a99a6099fe584c2eb05cca058d42734eb4a0ef4f9e47a5c58179f1c3ce1342550f381941ad50ac12b8dd57ed4d3301c94f2fe085cf9ca03c903fe6515142134dea6740a74d788ee0e0322c86ae99780f55c3e430ad637210f0ec3c045dc5f5121b758cc8344620eb70c7e14d718c1c9032b9ab0a746b424ab814422a64b21c3556b5b8f5e98eae6a482b100d9e9b15b00e22c084a1a07bebb71ec2e3aa80ed8827b54f9d84d1fff19d0a38fb7cb8e892db4f78ff7344074755dd282b0c3433fa93014265f6c86c87215e6e574f717f92eaf513de41a1cf34991defa724c813df3c37a92235872cef766d7d19f30ef901d580b04e63175102ccc105d33fe434345436879eb2a67098cca443209875294482444062672d78ba5c8dbc515f400648ddcc7174eb36115b5fac25d91c1d1c530b163deafc5e8e80b6ef5d4bdb9f2f5132897b3a7f3829d339dd8dd7765d3a61a762486840648e530d893117e3e5afb721220b3007d6966b7c8d5966072bd2a2715e00f39052f41c9d48eb176766ea7fa52883b7d25507a65ab652d763624857916afdc6980acfedbe6ea8bb8c3537b8b87bd966f454659bb33ee31c221742e1eea303d281127693380c08ea0cd422e0613d634f881c83fdf025def8b6bc7d584305d0771c81d919ac971518aa59fec9799966f56a0832161988c67094f481f9f954b5f5e345f29c967520bdaeac58c9530280415c7ead61f2546d696d0af67a7e20c4e3371ad15de84743d7749e65010861ec3155b45bb770df901591c56bed4862dbe5d76ff4a08efabd4e1069b63ccf4f54f549586d86313cbbe1baf95bb2f35dec7dac356d5246fe4261e49f48291195b13d124c751b0c637d8a4923d5c9c0aa231c186e8a8c39a66881059a439dc1978698e53495dcf923f7b7b75ffd0efcb32dfa4780bc3e7720fb4cd0a3beedb1a3f120f5c5f8bdd7ca5377b7dc44db7dc3dd9a411917dccb976aa3b92f6dcc288f38a0fb02a20dd9eeef3b483b97ef0bc8d25597fbafc64d744a569a634db9b81dc081bb535862e80201cc4c1e5cb7555e91f71843fb83428311140cb87461531dc6bfeb3609a34a1236a9620c5414c398caf28475aa1d57c78d3684dd8105ebd9ac96f805ee403b3f26c4ba94a6f01f67a275ccd8ae4898ea2cccabe69a0ae5e495a10804dde43d8630e1342f5c38dd54b8d61059f9a7b6eefc77224e050db996eb53dfda6505fa383b09c9e7493861801aac120cabfe91042b053bf149ceaf37d853b6e58f5b38531aef3e7fc045cf78bc8d5d4f14d1243d37d78738bce0db85618b6c8a6a96dd4800752dc8abb6231202307821af9260036071c764e46d05faf59049224ccef2e20d4250a8b48f347bd2067298f8b8202075d80b37c6fb061fd1bf95caa60d7a53ccc4f9cdb445996cecb01f6e871a1bb138a3a2c386dce9e093adcada350562ccde0f31d5ac40fb541eda1f605e63efa97683650cb65a86ec6f32cb347ba5bf957605dedf0adc6882dc8ecbd158ba4e5541f39e9a49e1ca3c445f08ad9d9e35b9039f7c0714786f3fd0df24c8c221b470cb1d08a395aebc45c1de45e5b293fa7404eccc40ea11b193d692e5bb20ecbd6635d37a461056eaf71bc4c1f4c643d4ad5622509d5b39bf389d8d083d868ecfaa70f26c507a4fae027ebc48432fb0550f0b92a1d189bc6ada460033c66b07caa80d54e34cdd5d34d93a7e7e59c81088beb22378deeed8bb3299b3ff6a1dbb4ce8d3b6799e404f1abb791c0075da5c109a306edbe6c712b48c18a95db7fca39404e38ee4b2d7bcae4d400f88c4ac1eba96aa8796fd6cf22df08491d042711a032b65790298050a42881067d453407bcdff4810edcffef83c83268bdc9a72cda4f46f0b110e20d71f07f06ef64f1168ac700c278f9310b405054f7689e58d74a9dd32d274b4cb0565f2bd6bb27164d11fd9a451003865705ed2273034eaf8d85850728275ff92de70654baa0f30a4945180d26442aca004527b2ff7d86feb0c604ac816996364c454da6467debffe2cf417bdc68b8f8372972df4882e131a5a3b3fd115321af3e28e906053a655f8728f3d49d86c43d0fd1ad74e576dddecaa11d214881f5763a1e66a4e390006606b1bf7f983ec529d67e96356ac56a52cd57794638c711b1cbce65d2e9a151a1599a67e6e03ab0400fd2ff24021ae5a015fb89e70bf92229ddba87043784b53e229cf88b897e8c8cf01c5c56f6d71ccc9135f074dd035349a1ccc51b87d4eaa6cdc6a302336b0b7556bd44df6fbac509b371495f4fb671fdfe29925aacb62243619b45e4c58535949642592261dd8c3552f44d6c5f39f4b8ce03c83b72e90614cced29b0a9811dd6a62468c17eb3951d7ef2609f6b7ea590fbb55d553adc6009d2b905a5923fd192990df7afc4d4c2e3f6924fdf2a414e8bb4c54e76cd1cad86b0dfc3d515e38064fe67f132d40c0b6c51df2df819f8471b592412c0fc16a83012c037bb4229143177258528a297ff35bd72f0ed93910d0f92793a5796f25c9a9440b844dc17a1c2e9795868bf36760b6569ffdbfb86424f86e666b26918df0df129bf1573277a9041fd288106f5ceca00f90f414305dfb2bbff484bf788a2f1511e13cd05cbb8e530b093f7b56c581660fd8e8636b61b37ac97952e9ea318fd7712c76bdaf5b9d02dc75c0b690bac70791587d82aade91de5782ea8d03ca6a01b1a495ff62b3ede97becc5e80916bd9c37bd527743bac64ac2e2b5bacccfc8e1086807722efbbbf1e152cc7e0db00348557c1cce740f3bee8e072f3c6b86e69853c38bd008cf7bb4c7f9b2f75ed1bff79779094484a87c5c279062723334312d8a5940879cbe5a90803bc0ba5cd9349e6351f257268d77d0c541fd446583be874c162e7959a657b05ed630f0869c2acda7d0429e7a795c65b0f70492a9d563c7e38181fce6f33a7f811d5355a8e9605e9e9cbd0530c36dfdf90abdf8da5dcdf86d8ba52582d0d84eaabbf5768d7b16669eadab31a7d13ab1279580cf564b32804ab6d3b61c770510693360f3d6329ed282df407b076856dbe7086dd48bfb4f8728e47fcf21b219a0d06b32048df2f97d7ad8df7da0b969962eff51cb578c115e18a69fa4ef186b6c7a36d1f6b33568f5efe59dff06230dc582113f17deba752f2f398ea453026ec8a454a087126dc72bae4bfede56c940f66c8d83e1e030f069c494af47efb6e5e1ffd7503179cc9655707d100ada4c21fa3182f4bf57584ff02862d350a54a593870627924433eaa1b4e78372a454853bddc14863537d8d5b145ecfb4949fc7b944a163197393a963533bd6d510f6962e7e53481fb18b8979ba0fe1ddced0ca05ab2f05acdab0a3d76c53932505144b0d6034612d1c4c0a4642289f6f325f9c84647bbcbbca6aac76eadc28dcb9ba6cd8dab0d680abae56d5fd9761e4c6e546fe2357f9fda784c1fa513ff007319afc0bea97ef447e0e57b2ea2970f51d26015af89d7a1ebc3e6840aff92b54906de85f83f48435b23da256be2e804170d422de0a2d67850b7eed39096891621c667d05a270cff897452b970aee4e9acebd23eed85d32619338b2062664265bfc5320f116b14f68ccda4f2f4cd0bbfc1e2547d878ba741ec83016a5251b2159215e6491ac41beb36c2889aa52bdc57fde109df7e5840d2841a936ee9b3afa861edc9aff6c87e5295ce1cf062031bfa622a9fcc267e327bc88598b5e6a3d4e262a4c964eeb0b4c085b20fd8c89f0f92e4ce64aaeed3693ba11dc84192433ecf3547cc7f34bed5d2f2133cf5a710f9cee67f50ee4fff45af9c6e824b6a0983d9778d9a9878739a8cfff3e70f8b2105c14209c3f2be88548f55c6c9dbeb2697805378130efa511096b552b37470d1ef41fcdf56042529b721c2653f74b1462849769c14eb20fe400c8e39cddc79383ac8960c5302e0817a90248af05ad1bdec443f604725f0610b401dc316dd3a3aa39e8591d609dc25b39d25addbe3831f5b00297dd7d1664558020adb0b456fc1d6ac833956554305a5abf49e346af2185009587cf47dc41dba855194b96ee005c446a09432548252a29b1fab685cc4a0db2f5818f02f5696db3f68ae3d74a747e17790a7cbc97d32e89700079f488edd6e06e8a19997c80793cc7ff05b51f5907b3045fa1767c7df6f4a9dc3b23ee3b73854474c9b16b2965f42ea24b06ed9eb5d9de3efbfdcb6a44105e26bc5e7da9133e20d710940ad363658138c12ca8c5528e994ed2a4caa3cc156e544848aab42ea5681138266fde494ffdf166286541081f8672c32efafa144051127d71ad6261ba7e4234170c718ef42423585b1603eb046fc5b905c0e899eb0a3537542e50f429f6460324743fcad51eb1fa469bb8c160d3642c8324a393a15321e8ca767a12376c2109736d9e4e35ae5201d8b47a729495c0a52fdc4023e6e1d45390c1a6773ad726f7194e91ef18e03634c1af7d6e6c66425e3a32da2bf847060a4574deb30dcc0037eba8a7ac21cbdb2ff83e56810ea5522de44ea0e7f9f247fe424976643119f0c235a1998880d2f146dbdb8ab6ba3ceb8dbbedf12ec093a22ad98cfac21ae2b530d079db60799b2392169b6a0ad2214f1b0797803363f172a1e53209165163ac5efebc95be47009268d7e2074ef2dcdb9148a7d9f0a0623babe77c8cf940315efa7b7b8b6585d62b1b8d9ca26547b263347ad980d6e3172de7b82fbadf471eda74c347991cd7c16262522d2499d29ff9a60cdc7cfa8f8fd3abe6c2aeb9edfad46c245fc7c527b1fc51da4ac219674c61ef1d95b21ecca8e483cb59f7f066629bdedf2b4c2e6802338872b896dc25438281435bb54800b8a9a46512699b59cc1bcf52f25992fea43c387a47f8e7e862d1895bd0c55d8f213ed5ce9a0607df7f5d2fb4b94122b0263a4ac6a763a84568ce550c9c6adb2aae8bc56ec9bf12a30f0dd9c4b386e150463c7011a183a1e8d3cf528484d5218b89ac08641af049e723651330a46ea6f6b923eacba3d4b4d6f25c6eb32a0ca45eb49ecf318e3657fb8ea65336af723c9bb7b107598c10de2de30a6af0a069710a20e96bc7daf45f2c857ff02784ce6fe249c76998e31269cf748c09b57e87346ce96427c0a82c53004368fa61a84352bfe59a5a77aed67f8de841177b533090be0752fc6b7419a86d1216a22b4b882881677549794bb562de056c3d7bea3589f8462421e15fae46c2da7e8dccb19199f8802a7004a161d1e715f94b8d5ef023009c5928ccf9f82b9a3830dd8929eb85126de4d6d0dcb5594ab1f329eb674ac7d5e2af4e6f3ba179fc4d3080644f9461ac2e48b4eaec28ea752c348bbcc704a54bae61f175ab56452bd78d166ded98b506352e71da23dbc15a5ca4a71b6e0c7c691c28e5434be6c77157ace465b403dc019fb628e33fc3c36a45a0f03b640222b34bca82654890cd6a54028b8b0aa8f1abd612a4afaa5d25a40b4beb86422186df25826c5bf0bb10add2982a45caebc886bd3e4fb3679e2436cf20475d60f14c2dc410e8c0d66b9c0f4c5de74d5c376f8e4fbcf50a57c4ea47f95f4ea0825f8ae8d3770d223f89e1c645d3bad92c3cbaa312ac295d87e54dd0192e55bffd417bca0c3161af5d14418970bae597097a7a6ddcf8ec482c2b67cd25c35ed7e2b1cbd8bd5965367c154b74ee3222f77b7d60d04948acf772f1c37e6df968a67edc617067a2ccb2d7d4069a586e1119b78ac7388acf088bd25788748d199963e704dc8f14e179446be97f7b959721981d93c86f663acf9c5301b16ad54a13b8fb548ec39308020ab1171e421094cd6c54378472b16b0c0b007b0d6f5d346d8fbaa3a931231c6ae07e6e810e595425d232420aad3184c3523fa2365d91968104350d823a4e4a2d855fc204f7cd8a9ac5283be583815588e4e2f55484f6b6bf1f4578f5721b64bb572cebeb04fcdf2e2c4872cb535e2bd55c7d1f97aff3e7652d0803b99449371aff5abf55ad75ee6d2be9dbb062c926c148b168befa8d8ad195157e8b54a6082a3800346eac612e7e49b204b45fa45715f5aafd8f2a34f596398185bce5f998ddb74d1889e351e66de7b52d7d65620d8081c727bda68a0014c34880776ebe917fb7e4fb83d0f44fb3ec0a5910ff352df034ace49dccb53e83ce3e13e636b81e07c41adc5b633f4f64a14680804fc996c0248077b2cd0d05719c9758999aaecfbd95e8b095c239ac0b92e9a4c193bc386840d2b9f626c9a53999a802cc47bfca004867b6dcbfec9b599e26d3e99494551caf9c9d75f46c98d7024b7954eba28bdb9850bca53533dc42741c09f20cc1e2156884f72798e2a72d8ca65ffbfeccf909ae45dd42a74ca7709c0cad3752d8c9caf8c535241984db1e2063d92f9cdbfb681d6830051dd48a7344a57efa028823a98183abd846fb77398cda67900e01b944f05cd511fbc4a015abe8038b62f00e3ec057bb24499d5c7af69f845cb0ac997f4e259fdba66981c3d07df33ae9fad4a2099aaa7dc98193a962388ceb925d62247ef7a954fbd26ce5391b1a80f3d0416d76c5023336ab47479f6a1d117104f0de5f073c116c26102eb895b94448048fd625c516e0be75934924be1b04b3f56122ba47cd518840dc20095b55b623f22114dafebe2ceb523e023b638af0ea6789957f0e7d6ab3574eba68cdecf0bcca22060c20a6851c6fe86dfdf782377755f6ef285bba84e517d7fae02fc7189aa1cca4c6626280351e13a4cf49b75ac9f6ca9a38640a8d8706e6b016c9e9cf571ba54908e4bb52bc459caa9b408df125d2b14ae176584f80c08c7e805cdceebaa55cb265894b9a7d8c0d818d44ecc37191e0ea2e658ecf20310b58c659c72e4b09c26cd8726f4da983ffc913360b9417bb387e928f3c3a0c21572bcfcdd11f05e6bdd81753c38b672c93de6788e0cd9eead776730d09ba4c234db2c3ca0997b973b53d8c97baaa06279b611397b6274e2023426cca5fbb513918992543903e570ce2722e15946ee5f424470b3841b7bcde0767736f6e44337523960dc4c5d9fe4e4c01f8a1dbc5603c30156fb7e0ca1be04cbe183030dcf158da9b668ebc1a6157ff17e380e27d8b370d1279a58043dc9da1c20be56add5e4294f399179f98485e76cf3da29c1074a4361eabcbf732deea27d93a229825c8bafd183cde12fe055f5de88a869f2679c4ad4d1fc3d8c4cb6c88852e9d53bc8ff19393585826fb6c399bb99121cca9abffeea7fd54084351c0a301fa1471c16fc6beb3104a3ee61e11d345aeb42822f004a97412b9db0581e57abe5a7fc33ab469448b6b5670e4389cd4010349555659d41ba9044382f8930e390619af26b42c3d3b5b09bd384186ae104802c3f1220529e79d2009a0de158349bd589dfa5ce390fc59d3b338a52778991b6b7ab6eb38e1867ca6d8ee75b55bf7c36067e5478bc0406ddd1eec63f6ad18e2017c1473a414833dd8292ef8d0361555ffc02902c64f12e4c5bc1fab3d60921a2bdc1328ad279e9161e29b28e61850192ae1328d077c28fbcba10ead32f298ecc373d72b01d58dd2e311d199da0deb58a13a4ba030adeea8556505ad5a4399e04e87e448152e7aa7a3848b0089182a854558b806ce508a24e94086935f219ffa6c1808d32e6158aa3a8a6075d40fa6f17086fdc31678cb07f60d6e177433236f4f6ffbfb5b675a326d444444812d902dd0804081308fd9c7bae3131b88831d61e62ede9ee561b0b8ddd880cc4da64b9d618638c09811d7c7313011020213a1fd6dbbe555b3010e37b188831d7b71880651f00c8b28ff99e880a51597e0b7683ff1c00c439b62836aa3296b0a26807ad1d16ec2d6aadb5f6f0931731bbf7de9bacd6b268ac06bf2258c239e75a6bd79a6bac190fde7b7bce3dc7746837d6302b7c546fe41f1a47b546d7b84e11a69d0f33d68db6b56037f893d59e8369cb0c8b18364ad1dfa5477fd1bb2b82156b33f319df45553cd18c198b6028623baa97551bbdb5d680d8e87df5886a2c5bc177ce3dc4982d3ec33bd71b29b134a6582e3c3d7c06c800961160b95e77f9e818fc952ed9a774c528f2901e2d47d10df728c2b3107cac0cc46cc646f761860a57f4ea04167bf41e8c0cb359364638e25fbdd7b52ccb67218e57c4f5bef3e5c3caeffc57431c11c1ea37b643a47e2dfafbea4a0be163f4433cbc14fd20155962da7cbaebddba522a17d63deae102d4e5a87529a1374f5d8f52d65dba3a00adac57c5ba6bdb8380f61d066159e98d0af1ecab49445958595d3443daf1626d06703e9a732e1273504008217c84706a8173ce39e75c6b1d079a163c1c61b25e6bece958c0632dc289525484127415092cd0294e29b9e7a085e29118fc4163075d6cb4d6dc83aefd4d51609632924c9481772d0a71235363d911b1cc4e88e5a865b0d52aac99010d049c02609d2f7060f1ef5c1201714b6c3fd75e6bb587abb18e91381102eb9e73351b191d4497822524f539e7f51430799a97534053c0ec695ead80d9a6793105cc37c17721aaf8f616feb5808881f511a20ab95b36b291bdae7e2c1b2f218c2c47219a757f56f443c40cd0217eb391bddc42af1f4fd462e42096dd0989f049f2c128a52ac0993733d38810430d7308834b8c202821c66b4e5284a33d463f3c164c56644556448386bd56db571b10cde8ab08c1e2d3169f648d0b907ef3ebb931dd55510222e6c6aa15956322a22357a5176314e188f1acbd17df73f041e86aabf8edc0491a6498d88163cc4be0194f4481cbb4a425592e2e2e2e2e518b3c6307e792830514e02c1440550244320e601d8f68238ccc29a10149b2b5d69a73ceb9186bb09c15b5a8041529962089b1d5a03d25e27b7788221cb426821bb4268376c4452d72149b23293d2865da9df41e7cad4a0d3e2b5cd4e20c1c01cbc4295ac21c40448d9c444b7090819641057b3d18e8285932b1600417c425b6c95801fbb53717dbd9bdcff75a9c1828628ed68ef72267a45f9c2c251089d5feae38fd4eef26603b5d5204a2f671c03a1781debb49f6398e40afbde65c14cd78dc5e4b40d7c4f678506bf155217a1e03143dccc973ceb9f7defb419cac080dcde21623c6dc2cf7ea6b38a0f33050ab2f83354566973108a1b5385944cccccccccc7890e0f1c4c96a48580b9418d681d80072e0e242448c920f11c89d88f60480023e58a03d7a1e78526566dd8f1d8f5957df0e5af341b35105b6b750981d33f33b636667799ce4dce3d91dc6d69a73cdb93a45e139e72c140f335603e6ca0e858af62063118db539b4ee3eeb7e9d9deebafa81ba3b8b7ea077f7b814300be1836d7359577b33dea98ba319eff462d18cf7e96ad18cf779cd909e7d8fd08a4ac4e7286b4798569bac16f9c5c134095a10db6a129d45167603f030d1723483fdd8217df206a010338b2cecf51124886db5f9e80e300b3f5d126d210389d704d359b49e81294370d2c87b41e35cc3223a995273d34544cbe8e96424c2b9cc1031333313b5d84065f0487fd139e7a28f69c2f8a0041f84f039e8dc7bf0819a0a11bfc7de73ef39e7dc7b41f48e44f4e4518b4288a1c28d6009518b4ed8c9d030cd144c632f3fe1278da397b48cbe15fb891516bf9748fdba9774ec27fc84260a43a0601a27d39e88628ef7de638d3596238710ba0a34716aadb5e65a634a641a4707a1a271c8b7da51d404c16abfd14e5690c611754b413a066918a45f102aecf54401815680ac6ca665f43b341eb6e4c11a86c50990c41adb18c83923cc442db6ec789722075927617b6f2e2e916b305689b5260f0482226a3ae0c20fda4ccd4c9118507145cd0eae705295c089931a29ea30859a99d783f75af09084379d210b3c348989375a12555102f71b2d79a2553193823640c85c3841c5d482058b5f0fde6bc143118a48244e886d9f2d5fb05c3424d7d50008b6bd21897e706ff5057142a488c25003dbd60beb6a9301c1e0362038210c053388c0099155cc808ae0f1c4c95201bfd0603f18a88509cd4387289d5cc704876405ee078e0930458aa845878495c004254487443a614d7e90c76a8ffaa0228f019aeaac2e56cb681cc496d1f7c10a331e26034b031d3b5e8f018a92b910c46883385971c8021866e2d422470b197864bcc0e33c71b26682709fbb16a199b1c2098d1022324d9cd0a0c91395203c79e2e4891530e184c6bdf79e73cef1600731718a9624e7449180799385040d0d0d0d0d0d8d47071e4f9cac181978fb2aeb81ec1b807d2ef6cd4f368c104155f55a567deec9f31911d1a2a89d2e1911c8ddc5c5c56586046c3b4b191598354633dc658b40ee8cee884abc47cfd9b0ad3a214a72ea786f51878c8c8c8c0c1106b0f2c41b7c3000dbdee0fe18d3b8d870ce31b35aa00c60f67069624e27608f40ce08fbeee07548194986b585e2796788929c53036cbcb7d8002516102c2b6ad14667891598d8704143d25c40d2608c6d4d0adb185c00c38144cbe8b739510dcc3c8184069a26fe205074d24e8878c184c80ad790401144d0b91358ce3dc61d6058b0509a732e3ae3c8591774ce39f7698f87350612ab991b6b33683d5807273861f483048ae2bdf7de3bbf7a9901cd4a67548868a51ab51d84c12546175c844d02b6b046d10c06b2230b7800eb08609d0ceb2eeb58acdb582759f7e81c454cb8c1caab7a2355af35d5fb592f48aa5c5a51711ce36b6ef1396602654f053172f4ee6f1cd5dd5d6ce4381263f35151ed39f79c7ccdbdd6de7339206b46885e8baaf0c14a447487fc016c23806d326cb3ae0887f51c511525c8d1388a8094f52e2d2b5525a219462c7e11510423072318b5584545022788a10adb188863267e7488ae870706f292aea701683d08a173efc1e7891b4e27077578f8cec5b93657d42cb82a0f8e96bd894c7407a326dd21f95a7994d2d94879dd8e8111734151c958644b0ff4d4cb6c13b1ec2eea65d235738fb177f916c262ec7d67576b26bb08d5bfd329aade2662dfe9a9aed49d2a55a95fcf9b6aaa298ab1686dea87ea68f9bc45108cba948cb1fbe28ffaca75d9aebca23ce41925290e8aca8bea82922ea8a43ec9bbf71076aa45bd9e4f6da95d05a3d702ae06c843ca83bff277b1cbeb9de59aeeb9aac8ae5c73855546f3d8a91eeb2c18290b56ab87507f944d4d2216b188518797d34459a72cfabbf710482b104f5877824f5277511451f542a6b587309a1ed244742c80811cac3dc4b5b0470e50ba27d56794c3724a6cb4ee3392262a9e20b14e1ce39896e1b8f6a66594b01b74474fba832b07e9955f50ca3bbf3926bb36ee458b96535f79ca3b9f134a2965c83a63ccaad750055d9277d5f50d6bbd7d5da7c47277bfebaf3c0fbff03cd4aed5adf72f3bb704e2839d15f63e66ff619bf9ba8e0af6bedf29b1ef308c6aaaa9a62eeb9d2edbb56b67551867690df559c594a42425dfae9d9467d38bdfd8e2d76a79bb247d5dc9f573fd5df670182e3e2c208f4a0e94cfd9f506c1de43b0cbf35d15fcd267d8576461df27bfd9c0770df52d76287f3d96fe5a969ec6380ff57a7878adefa1d97b9094f29093bc0e66b20f5df400e351c7a83c54566b9d4faebe38c875f112d5c54cf42f0761edaae0c1bef9bbfa7cbdff9aaea3d93c0db271320cf4aea365b8c3a837bea837baa8d76a51aff5a9d7d352af87a5de7bea05add40b52a929f5ba5894daa9370787fa85158799998a97e7df8ae392dc984e136bd813cbdee76f75f64b9d85a64b5518672996524aea5615c6b1ac2e09d3e0bc8ae7544172b8801ccbb40c77b7a93787cb3d1e190e52e51f3ca3b15505e209ab72d31fe5513dc495aace3249f8a592a7aa4be2f1384663df6ff4782296a917d6f71b9d8c4be294709025820471499826059463103863e79c57a9cef53e4a85909627ca43bee87086dda03b5ecfe9f29cabffaea9ad252d1dc207a52b44230e45bd4bd2a3389d771905a55ec6d78533f69d992bbdef4dff6a4fef90a77708afb0773a0c0bf17fd877dd0ea2add4fdaedbec740a422bec9d7a88cb38d00afc427dba30ac5e3863a71fbef31b5ec9463f74ded13b4f2b67289daa1f944fef20f86f08fe3b547248057e7987efbd0bc32e9cb1280ffa5743efd42b7a5deef7e93d410a560ae1557a4c6f7aef69a23b660cf2502ebe828681dc81b01e4f0f528107bcfc74b10af5952b052a89a460dddb9cab1548c1ba5b9beb235d3eb2d49a6e31ad216a057e69e9b7473473dfa51a8a87a7987a441f00cf57abb171589fae68d58ff4778a97acc03af877ea02e209ab478ff7f996becf899d8f100af64a967a75c5680645a53c27f61d5a017efa3c5402adc02f919d7f74c78433bc8481dca58baf6020078405adc0349eab211663b1142ab4d0c4de4649ac58b20528ac8b8d9268e10dd60a255600364a92852c2c61c24e963d7ec98c35808d922c2962b9065694840b4fecb4fc1a1b1fe7d1e6739a54561eebe5cb282a84b49136798ecce79a89eee04677b03a67bc66acd4d975ad78192fd959c64b761dcd42d67fd818d11839e54c87a09809bfc8f38c9d21a8daa997591a63656b7d32475943f4d6e36fbcae652304e2e30764f13a3a5ef82b14ff83bd4d529e31768ab1b01746363ac465361e0552879fbfd6a5ac976b3e5d56669d397df224a57cbc2ed33a751e60f1e9e345b97eacc3cbcf19443c0f11cf6724ec863c3bd3f00b9f558a6fcae12fd758f9f9aa86241f5e467653aa4c49a9174652ca233ec4aafc14235f4eba63a22eb6e81519e81d08eb4ce3328d65879447fc3cbcf8ec8247b9a677aeea7da9bc5d2b918d180903c16f38d7477e3eb29bdf0dd3c92666c22f90cf750694747819921e2f27cb4b9cab510fd89ce24bdad48f3c7c3c3c37cbbf30cd52879f4ea7af5857b9a296615d96c2c23fca831fa2e81d1d62b2ccf1f4d3190933e197c84e538d9d57935763a007841519888398365666c234e0b318070b5e20c2dc6198658c7d4188ad61a3245eb88251ec8f2df5e059126ddf7b14f528ea51d4a3a8771dfdfa4d14f5f7fabda99a445ba61e3da2a87b810a5e8082a29cbbd4297a77569d7aa1572f4fefbdce9bb1dec889b15ecf931eab5355be7371aea837977545edec542f102ba58832512cc2e00a273600364a72451512638c85ee7bff477b9fb3b5d93ebb26d1f6b27d0fe8c3db2ce5580fb9f4e9d5752d0bc4c78f6a7ec0f267adaea3abeb3614d5a72b241f59be6edbea110da22dfd9cafaa334b9faa4f7c3b06f64a8761b326d176feb0a1e9b2ba0ec6a7bfd619c659c6ccf5b2595d3a3fe58a9adfe17a615ccb65984d856176ca5fabc2305bd5cb76faa41586d9a946e9ecf2ea214c43aab05e3bfd68402c0669d0052696d0d859b5db45ec65e78b0b351664a3245c90b15e58c216c04649ba30632f576627b6911fb1bb0bc4b28ee650123eac7b649b06d1b69d93681bdd76b9c2b0db4576c027e9828c05808d9274a1889d707a74073c53081f1b07e710d63b7d3ec21aa15c51cbe80a9f72592d0352a730ac3a0ae7da5c51c3bbaa5ed684a23562a0a9c238db32ca3ff7f8a63c2e9090ac17c65d1867790696546198edc75f18c69ad821fc026b88dfb4b587300d56511c908abd19cc7e631b44368231b0eef409ebba58466b6085da590f611ad0620c043b56c723b22dc916ec9083ddb85694640b6fb00c440f611aee4c6f0bb111331a068a8468b6d51ed26d09f313fdc38fec87016cb932cb96d92a9ac18f685427c0d10f5534839d1b30a3e8067b6b1d31f3985184a3d9b9c628623f185bc039e75c51cbe044eb9babc1f4734d34c144134d48134c23c20deb54a199a970d940c66a73c4534cd6cd7dd1477346c618838957a8eaeed88f2a2ca2f014fcc23e53ba5a3947a6c24ece3c63b127b31a8a7d5d0b888ff92657d1714a81048693a9e02968c5efc34358d536dfb37e544e75f7a5988c5dcf1f3ca5b3739e62a5b27e68f67ccf7a57289ee231405d790a96c2ce3e9d5047f7f488536fdc30d74d259deb75b19b6a0ace61d964e9d9abb3384f63db3c4a164f71a9eab7aab7457af76af214b4f2144c039ea78090a9788cf3142e288c7205e69eb18d768c6d312e9c4e8baa6059f404cb1060d90db0f9b551a3067479afc150caa75cf3efeaa87180207bcd0141103390e70f442174e101bd42d27fd89772c6284b0aca1994ae32044a5579cdb2d40bc3208535442ffdb1c309c447e4f29a07f6f67c8c397269287a52c63aa686f4f97a3b66bec2fc4aef0c65ce39278317cbc6460f906a8d77c1fa59797f5613e5a917c6abf7bd472efcf2deeb183bd9f36a5e8996ca52a5fa16582516764ba244d460a7d827f6ce52ab9ef15c352e2518c87de5f2c141a227359846bb5cec3a5ae5daa45cf3289775eaba9ee97a44dc68f52f8c0e6773221a87129c0bc3c3cc752aca6166e6aac735d954e3b6475f8f6bf29c73ce4e8f8bd66b83996b300dce698fcbc657f5dab09cfa9194681ca096e1eef15816fc6d76f6b82cfded71591b767e7345d10cbe754df4a18a1ed43858ba688d8e51c2c713079f0ab790e2454d6cb3113da224632eee4d16d9a393f36d6098bb944e69bd36acfb14e22b3de03197766db0f76a8dc66e3c56a377493d81f8f8a144bc4b8c64e6159afec38af1ef452efcf23e3b67717eb26af04b3c7b9c21ac7a999dcea997d969c376e6a7f926f63759fd4c7797efbdf323171623e7eed7869597e8245fe32d9c0ac32ce7ccbd8738ec4974876c12afab5d4058ec91f27897eedc3797bc75d1ebe8e71eac9a75520d7ea13ef1d2232b04f5592f5baa7ee2dde56bf0cbbba371aa312f2f5fe3d11dd2c6d55eadc1349cc5550ba0fcccd8f889b111cab4d4dc162c20c3969a8f914f0cbb015b42d982c86bf3ee969afea7484794559a621f23fcd212e512e51363b9b96b889eba6c081ffcf0c0de27ffc3729b5a6a3e453e50bc9ee0fdc47c8c4cea13d39fde156efa97829d37ec162c241fcaebaad4e6fa51f97bb7d47c8a40c6eeca27c6363bd51cb95225734fd367e66384690461207795cb2de198e07f8cf0a78885920fa7a3c8ce759be55c2d350c848446e3ad78493f461e37a3c252a7933cfc44272a79921f2390b560c148b0d705b1eecc2d61dd3f46f885f560544cecb6606127d8835161e59f7bd582857dff18f91869a9f918f9c438e7dcc70894e970070e4d896dad888d6942a4642e4877b7a4583721cd895684dd68d9074dea2c299d0d7d3373cb3722dca0b0f40a55510bc65a50d6a2e1ab7e41abbf80ad08d70b53310dcb4a4ad5b2fb75834236ec6e42e81b117ee9d3160309bb39c12f14762b629f7c475e763a524a2969b3aca45495d58f8b3f2460fc9eeb2a1c76834853dae4298a56a7e491681c2eecf388e1f85cf51546bd91c190dd957e72692a2b8c1755e5517bba3981848b0b03f0ca004d73a2a9bede5bcc7fae7baa0971a9375a967aa3653d7701b546439b13b4be8a369aaa8628a5cd09ead238ea5dede1f7f5d7c3c3af15e337da1e2a0b798c1a1d0f35ea5a9b13147c9fa890a6776f4e342720753dd7a24d4873a2154102033434cd891817ac051b8aa44845580d8c1d7971a839e78c87612169d239e37cc52225558f1b5663e36118a5a7f5d2a7fc5a4f992a4cb39b1ae26b511425bdbae635d1b326bc2279585a2ed5cbb35eaea810d24a7c8af2982496580dbb31cf45e80ee99a3ea72451674da4cbde505da18d842249f3d559137e915e312cec9d144a7589fa64ae6aeca45252a6f54985d8ccfad93c9e9224e93ccf9accaaa23ea5549f52aecb143f4abf2a4fa12a104f58d255a930cdcec753164ae52b179532290fe9a1592f4b496289eea045208407c262e726763e521ed23faf78948b9e5d2cefabc55d9eeb6829c6f9aa8ad48cf15ce3355d91e57856067ee13866ddb93ef353f54bddfae55b35242f352bcff91582533ff3f1d6b9ea60ac0cfc223d56a77e1989b52a4cb3cd568fa76f61b11eefb9365fb9a296b1a9d7aa30ed32121b1f2bea9095413ac5caf058cd753508a5cacac034628516e390614696fcf4c852fadb445c57b31bc2d9ddf3dd703633cf331a38afa327bb425414fbba78bfad0e36af83f58ca726a362bfce966e46cdd99f6f76eb8bd1ccb38673b2955fea10c676b01d6c07e74a85bf14fde42946082133c74a3d54ed212e2aecfaa1fe0eb9e164ac5dc0f99eb0ff0ea78b0a279c74ced99452d6d4b58006cc49a7b62c2b247d9e5facf33e3a0f4f299c91ce0963fdccbff345e32f6bf2484108e13c4529a5d3945264e19532c856d245e2511e459fd2d5f9e41697979f7a9923257df5c66e48965da49273ca8bca3a01f151f1ab2ab4f90f2b234b4a5e395bd35975b6be4999ea6576f3141d4cf2a594b2569bca85ac6f2eab8a31c64292624caae4552e4d9736af2aab629537555555959caecb5c532e37b587c8ebaed4cda557524a21d374792e55a7743a555d7a7501f184a4aea7ae30c618af54f57260d21d94928b371610963cbd245de5a29e7271de57cbdbd5e2ee62f9bb3ed7d192b2f40ecaf5994e1f59abf3dbb16a684aee5caf74a99a6a685ebe9a2a799e28d5d47955f99ab73af5335d3a5fd6a553d6376f7155ff5cd55b2e96cb6a195595ced948567557558aa2dee80eaa089d6294356e58470c92bb0adbb889d816598c999395a294c7caa93776434656329a28922425c94592a46b439224e9d22459ac5103657a43608e39e69863ef46c816a5344246584429e37b9452d24829e5e3a3ac714af4559846af8881a6178f380265a20c9ba00d196390b1eeee8e24c6de20846f6287bf304ca2d6a4935167b4a2d75425595126e886eed7dd9c3e84902d07368cad01f1c1e1bc8570ba5d21f78853216cf0ad1ba7dd71381c8e2772cee9ce3912a6a034aab2e00a54b1604a05a94aa17438d651ae8d55517492f50291ea8d96a14c1c26a1204618637c6c8af151b37d1147f1374651616077cb7052e6a5202d83fd469577bdd3217b4ca9ec28f546957aad4ebd9e4dbdafea05512fd84aa742a2bba6d41b3d7133b1dea8c6b25fef3c8a70d4a73c8a70f0f09547118e18677914e1e8e19f47110e1fee221ec67db8e27bb8e2635cf13c5cf58abfae5f512423d60bf30ea3bea837878bdaa25e974f6da917c4523df57ea55e8ff5a454947aad4ebdd15ab59b4ace4e09dfeb90744ea69de3917884695eb05983c4c2bf17fbb5185b6a3e453e3132c658b52716be25562663e1a38ac16bf6d48245f73f463e463e463e46a08b436b365e6bed2eedbd67a36dbcc7f95a8d1a356a3807b9c641c82074b0a6b9a69aa873ae8d35bde71ebb4a919d99301366c24c1a48c5dc179c834c888390098602081d6442a46610fe4e8ff0b15e79ea10d6479b77ab9a32e663779417a6b166674db026d8c4dc15efc5dbafc5185b742f3a2231c6afa8a49cbea74b0d6e22199b7aa34abd91f3cd19eb3c05e5624d389b7a3d96b146d5eea986261bd243e073ada10a9c7bccb1293e235e7beebdf7de83147e9ea2ea6d96d2f775a7d3daaf4eb3de2649523ae288238e30f25eecc7a618e3598ad8a23bb78d4f768c31461bebbdf4d2e3a71c9d4f47b9222663aad7c546ebd41431d0a6de66519d2b6237a27401f184456b9342adfef69055fa75eff860eb8ac240d1df124b52e292180593a9624c84a2c812c684302682b40caeec07815c5c72e4f0782c0be6c52394d678c46234261e8951b01b54d2b6dec2b4bb296b1aa5b02c8576288d31900754875e078b473a3485aac094abc4282cebee9af2dbcdbabbbbbb43d453defcd8e963a7518a2bd4945d312642c12ff432f280693cc25c6f8cc2bec8cdb25936cb66d92c9b65b3ec4bea5962cd70584a29a5e486b45da21cb6536a3c02f3505a183b638f31ec863c7d8482b118058b515879cb4a39ad217a9822adcda30807dbe4b82ea07b66fae742bea9694ccc8b5ae95db4a8d7834437bdfcd49ba3a55e17967a419eda2bacc2302fa5725c128f5c2cc3407c948bab60250cc4f1088b50586afdbd777a9e73dd48e646326cc518067a5505a3ac1e9ffe553cc29c12b6c432eaecd989524ca19495524a73d007708d4798467579dd0a32245a867b11c698c891c3c50504fa3d1ecb8a479c7332efb9189d8c7d47221ee1178e4772e47071a1a77f31261e8951c8542123138fc87c38461384a04016d2d9ccee0e41a9bbcfde94e095fee4d96bf5aaca576ffa957de71f83d26cd6086e08eb11121ad1f00824d88d86f09246445858e90a4d1165cc081436d123268b5a2e16a55e94374bf945b9ca2fbb4aa7d4e917456270ea43a9eb67fafb84d0889e94f2409950aecb965360ad2a7d955a53268a94e88e29e488e96a91c5bede39d7bc7551a7f0f11fcfcb976f9a13c6c647f0cb46fae64a952f3de7773ae74eb01ec134369fe2e608fae92d16756b3a825ff8882368a27684c5e18d0ea28b189f44ab17307e59cdebd97356553567c593635535218d481562893c9e599db14c1791e52e28ae582a6abe94ce863acbace69cf0734e22158b87157c55bdea55afa2aa0abeea55afaaae381fcba43e2c87afaea3fa9c31565f5971115d54cc45355f5c305c4c7488f957bdd752190b753580811e9cf0629f17bce7a2efebc5db0503bef7de93a25455b09a73ceeaf1f38754352b58d58f7ce3f0789c885405e3c515b58caafaab5ef5aae6043c7d13d29c58407ccd8988c6c358c83013672e19632dc6df64b3c2f08b05c3b1f10b85e1f7dafb4c6bad0da0c5b08f5fac5f1866e617398dc57879498a133ff6c82cbef7a299c779ed3133c7c72fcccc2f3ee608c3f1bdf7c7349c551d914d2c245276cbee0e12293b8f36acbb19494bfa1fb623aa9ba1a44ebb512836b9a9d3b784407c48c948a494524a5933c9269d5f4ac1ce34498aa56859fbba2b95aa40c9d85de97424934c386726d34a85819225335161248c29253bb2e786b95e9846498a99c05841499d35f9a6a9c99452844c508a40841911d8646516c1348dd2295dbdd96dcee6d6bb32d652042b82dd88b8096cd691e77346203eba089a6627b1a34f3753f8a1223c7fdef1c39eaeb7c7046f3777777777c366c61e73c21a6545b01b3435947113462183104247298410a6e3c57ef8fc42dbe2f7d9fe752f609c453ffc7d8e7eb8de6f66d217d74f0c96f75004c3b7b430c5efe12ced03eca91bfad0431144580cfa5afede79b8a6d78bf3ebdafc97751897ca5f5cd55d5c296f71a1fc73755aae59044b8dd6adf04ae495c85698b018eb9b7e7b4c374e3544993200b09515865c8178c2524941e177f89ccdc52c9ac1afae16cde07a43d141ee3145c84c8a98a626933101c015c570957958b02231192604461999a0d62636697a7b6317b936e90ec784546f73d7b52c101f3fac7426d80d14486fb51d0c0b51ff61a58823494ca4559335c12fed4c49d2e4489724d5b7f62bb124b51aaa4e5dfa6dce4517037be3d955ce31f1c988c45649d2dd5b8b6fedad35d738b1b50bc39254a5ebc2348a09860237e734adcc532b2cb9af34c9b556ad4d5da1ae7daa9703ac09aa822a9ef7c5f294cb3ae7da5c474b5307853baf21793b4df04b735dc9cfcff93a793e6a1c9244614d7438674d702acbc5b9e70aa7cca8f4ac59e88290211a010000000023160000300c0c854363c140523619a80f14001479a0505e549909c42849619842c618630c000020002202323335090218511a9a6190c0d005f976d64fa643f46448c78dc09721cb735bd15d70ba493802e9209f08266185c54fc783ad271caf1decc996d98606681472b73f4268293f5c25f0c944c8676bea75f29e6cc752db63cfad94a423e4b91180c354eb085efe322e0b165b5e55c42782529ad2b9c68146e711d4c046862517bf8f5967c6cf0a2b4c14bc33439079781fc143ab9d730c35bb555b82cd01befa111e7badede713724706f7cc468d2fd548179b3530c2b40ea815ca7b0b40427de0393feab8d5acb434b6062414b58e19fd1ba789f6e8066808649167af774ec710405407790db4fa615ef49a0219e89b9d95be4bf337af03210b1d4f4e65ff57fe748dc27948fc9a715b9860016739570f69f51d270e7c4b68c36ce3fbcfd7598f2a94e2d7c49c91a80ade82cfa5ea3d3ae72b45559dda173fbe9f46c9277bda2cf2ea3d1483118edf35f8ca9fea998f4ef6027e1aa2b01c9f9e31001a0221c99cf683ca8ae5fb7b16208253ae6785be94e092ac7e042e6ad55a0518a06e5b18fc755ccddf058ba393d128418a2ba55019d722e495a09cab94b94982a82334334592d906c0a6889e3d3fd41e79c3acb8594bc4e72ae212277ab28092f18804f8182a1314e70a437a1baa502820b0a4ba53c0a598f3073e6791cd9bdc983d78ec74a18c7bee5ee00ae01bb313ad51524e588e70fbe0c376d95f3dbeeefd979b33a711ac212c8a48793161a85f1667abb2f929594aa01a103260b361bbbcb0ff30e21bf35c018fff11bcda673c26518d2757403a04643c8d94ea83a11f7db1799ec1a2a3c4296c5f908fa83f695eb09083425d5f1c9f2cb92cf394220b914542420a27243d9687ed96a7d8cb131e8f67af26663b05eb890e4c71a2b36ac868bc7a74a9f214e018114455c042d14fe640cd1a48c43382f6d22a47815d83b54718122183ba2f3d11a423811c3abcaef442655117e748cd8f62d3a814dd103bacdf8e7c18f51976ba1fff99c07495a4c7a35fa2e7cc6772b110b7b82d7da964c219d68fa7d0280a72b5f9892602a6f19d1ae35cbc199ec79bc384ba9a1ae5ae48960367b84ddc9ee3fff7d1396d950f89c2855d1220b942f500ad83fdba281e64e2d2410a0f0f93b416a1710b94ac52be64c360f99a050ef1baf31840222853330d88a3ccd7fbc0728bc072375a0061398daf2dbc1704119530cee8eba4340649363ab660ecf279bc9230421f681cd0be7305789de474d40e9ec7585659b51aef947f57b3112297affaf94fd09b426da3827970d55c1e1bae7d32092ad9bd27d9aa7ee154ee4ad2375cf1f9fab378ecf686762b3a6039727a70dd00e1d630e55842b4633d9d3649fba21dfbe2b373786e35e747fe8c2bbb031581adc6a9a5c32dfde58cae84d218b2e5d3d5cc0043c97cf594935af2342f10df3a13f3bb9aef44e4c1373571b82c7ef92d4d9cd9d166793943430f009e918cf9a1e4e99a39f34b714418cb8810b8a26b268728a9622560a13bc4cc3221f4ca875cfbd10f25766cafdcbeb212b883d3433e12ccdab6852756964844caa229af9eb8b5b1d9e2a8001321142a91780372ae3d5802dae14b7573237715190ce3c4be26ec9d1ca25477dee384451d6ec00b56735105690f15f5c014e6378bc5fbcd349e374f403610b95e6689e574e590198ac6060502493421c78655759b9988a92b92ed3364d6db84212b28398ef817615f4d50c4945d3544ca25bdc80c305bde1f0d48451548a56d0a623a8b451e3aaecae5054d7b8c9842b0faee6f094d72d7cb5962f057ea42a451e7c14cf354333ba09262c03b638bcfe6b5b57745d6573e7b3b5315f6923849b4c390778bca71a57ac5a2de07f6866fc80fcee4f77eb8645871ede33087fd635b7eddeba982bcf1cb8f92178ffa894eee88623116cc967661d116a4100b2ebb01c59a21f50f5ed6891636af5fcc228047c01f77e9ac941cbfe8d744dcc74f41cd711440b58f0f97f55ccadee1d87d8472bb7259665873c053421b7203695f469ba9781104de570348bbabddf78667a862afd612731080bccea59807174f940b6d53b55ffdf640603821db1f18e967ab6a1682786201ac3b718513f50aab6bfb75b61d00004fd6c400a5578718e4d954cdb19e173ddb92928fa42c5afba2a1067d382825d0e825683bf4b1405c4ab51071497720f809f9aa2c569fafd61812a57111cf58e4a46e532c7d283be72018c3fbeab9be2f756fb8a138b58152b2403706d16cc99e52ebb2f114b28aabb7602f93dea23a1fc4334b61469cdcd6e8954e309a8fabf4bb3e95cc22439f592af2402f4a5f377666d1b35a6af9785257fc9e6308b6f97b9cd8652cc73d867fc99323ce6c597e7b6bd1178c879c44a5afa1d01fce4eb28ad03c172c0f5b6d309b23fc939bd3e5f628156a1dde8d2ca71e190d26ceef34bbeaae48cc3297cf0636f37839cf3d7b929b5423287c35f9d15e1d22531f154a28b8d4aa12be688e5127b0782cf9774603edb4af575b102a65da07cc26eac3d27cffd4bdb176ca79fc84fc1fc0d4381e1084445e52a4d9a7140dcac7b9a94c6b4db10255ba1226f01cc9a7a7ea15b8674d841e3b4ba242049212635284550356a40cc11302e669709d7a0417ef0e2c499ae4af6b1cbfbbd445b0f8c91668e02ff0f1f89cc0b031321f146ee6f74648b05607802fb83ee10b40c1f2ed997559503821567c7a8e267bba0242e15684281c3d0a37c081fe03c7a99a856de4310d2c62fefdda7c32110ccb30436beabf36f0a0e905115c66da477e3306d94af57a76166c470702958507dbfa971697572c72a3d3dada52c69b1b052a28b1d820a9c0cb9c1eee836d935a672a09cf5b7da367b6a708c5d0c8a38527b573c37940dc6cd05db9548c0b986c5898129e39a64b3e137a66a2b8b0c987145691c4c0e1c0161d3c7cbe290fd1531794fd3fea1c5b8d59db6ae9b4459f4af1ba97cd9c3dbe90d1201b3c0b17a0a778681e208769f3c383a729e280da5ea983e5436d23fba8b54e24bff617dfb93069e8f365a2756e445612a592b3045f6b6e816fc781449a5401440c7ca4ab2d29949952caf684b4c70721de7bd940d68697a79b1da5d7cc2ce8c5fc889ecda4663e17f3d4b62cd73fe39cb14c497a15e6eda303346a18d23f71a46278b843a8b66f842f4b9509c0e311f12553b8400ff26c234a97caacdd2d7781adb7045d98e9bc3616f1cf4545da9ffb8590c2ac9a892004aae73e7906a2fec470be6258c0b8998e37adaeeb4f993902d06f0621f09dcaecd0c4c8a18e4c9a2c701b27426cb779ccaee851547c74bcbf0941c2c0e9b7802771f26905c4073a13857e745e515565958a552ba6f223e062c4d7c21a05abe3394b08cf95e386c535ce86f37de079ee98132976bcddb50364b48618c20c3309f25570372e8f6c57e188af7640ff760c4c614d208ca7506cfcd38290eb9e630da5d5845b2ce471b57be424e42c36e733c56fedb6a2ab4e946bcc1e7dd0e334694f1ce693f8ba353e74bee3396e64ac2cf0f614483c7c5c76b9483e618c0b8a0d6dd8beb55d6e46e68c755ec0a944c8f7d91b4a311c8e0d6a29ec86a5017b887631a9d47bb04c9a3a41fba032782401575355429464affa480c17ca678d596d5666c4f3de1570acbcb7e0e0faa29b4e28ac616c8c564ac0df5e9dd098ae9ad42610980479d6bd82cc81c643020faddddece8a2886893d79997e23f6eac82f6ed8f33b91e5ba6d521ee276eb395da9371b0fe44857ba887103b22dd5a77cc904c2a94b5e0c0b9b7e17ab6a75cb15ae963aa2364d3d158c21f401b3ec4dd30a9dfe80d896590e13462bbee9c8ed947ca215482613129ae3c1749b0858bee3093c81d2172e4b1c2b01e9097a9b51024393a0676e30eed2b0670b735666ae656dfacbfc8b314dd9cf49c232c9b2b69515877ab4a6bf0338143b450d42eea0c3f334d99d72b926517fe45d3b0a9783b6b5187bbe7ef6c08c22cb906ec86acdb69205ff6a61788c0a7bc8cbbdd5f962ff32fa8b2d5dfff85449d4a88c7ddbc901b611127d13092aa0e6e9324bdb5673219883d69d866ec8c3a6f03453559f5e255cfc173a83dfc9296e3c33aa3243592d1ea4940e54237aa30bbc849aacb07d3c09cbc32b84aa8b4155ff4ab5aa96ea2e28a8564c50a2fe842d68306cfd7dfe1b1c64989807ae60377d6301574ab10211e85c8a2854002f27fba9103efe646db73cc8f86ffc6e2b607368c09c0acb2d64e102c0b5b6c3035665447962a4b8503ffa304ac10907e80521604da9611cea38d5b8a779ee0f3a44f42b81ef0fba45fc0d0b012480e95a7d00886dd291d401a80ae7f35b94896d02c7eb9e3d2239fa60a3e1845a518e022a495c0f1b81959b7a10c6360ca47dda742c1931c11dfe1e5d34588b1578a583db506cfdcb4c507a8af40f7ad1259a16a9ce145af9700d536c7cb99ef94507c3010180be540f6ec562b61cb16453fd6a4c6c610e7de3668e448f3839439f48bb565075c428943313de3a08b04797ac6882ceb3b61f72b7882c1f7416a7cd4baa29cfeac2d0382b2cb47c878acb2a374f3e578e7ec67ca828e436d2936a9cb4fdabae07c042c40813bc4ce99d06d3fe496c7f60a24b5281e0c196188a9bfc228e7682661a9239ec5cfccf6366c94c92f994598f49ba9b99ba46da2b8b95f5a1019a385c98e7a89d4957e56ec1e7b6945acab25a33972872409c816dbd7ff1b0764b0ef8f926c07bc03fab1c1756d36e466bba5229c9753b6e90fd8e546895b43dc942200c662788e168114aef7ba8ee9a48426bb82e874c0f862a3496e4040d8079ae7c25e4d24188a921e2d4b447071d8c4a3fd5ace6cde1885b61cc9290d0ab31ab33cb570e858a0e456b8760540692ebd09087af9618e4dbe06c1219418f40dc7727ec602fbae61eca793a119178a8e657ac73b7a4054559683abaf51c2370a3882435557ac54ea5016bf5081b509f9e8d4acd97f08a0cde7d988cfcc13ec4719984a79adbf1ab2c99902439abf5ac601ab85a6506961c2bff0109321da6bb550152614036403f36b4b201ef3a89ccf7e74fb9c8bdb85dc69c718ffd3819f595f47fb8e52a026643be284bec9f7b094f5500bcb44554235ea8a8660f189200bd26aa8dec22f54c140f185daa8bd4fcb746a75485be4538c789a42235472dcb44f27f90a38803b75ea6d2e823c3e1fc6ac12e502c12490d8a3e81178baca1af6a074618f211f9f666e7d3547e4a1f4ef28592f1a3e0438fc8010c0094dc262707702bd32b0850a09ede8c62fb12f3ef3f54a63bdd3ac71032907d8f0bfbac57a671b0ffc1449b28f79adfa2a4ca14f2831c84cb9c431d9e435872b87971300bdbcf3a766225ffc9281e937f236e3740c39ee985a933c572cf9a9db538c0ba187c34cf74b8ee40ad2252cf34f1e0d354254e286606885105d6d06f77ba52a446c5f6cff014cf14b18ab7e1cb2017d52023e87b011149674c51e7802ff2c353d57481ea848d249ee4783bf0bca1dc87442851697a23430e2d31f6c7f60f2dc3f184df8a9e05fedd344a48574cc16630632ec644831e09c03f76a3d2896004877080244dcd3905cf00aede1110f5817bb2879e660b990b4181c561afeb4cc873164300c89c8874967c947b6d4fa3d6c2d13b193704bc0deb52551d0b82f36049419fa2da62aa63cf6fe23fb00fc8324fd83e350aa867110cb6548455fba19a5353a0c7000b0234dd36c1a8682735b33600329e62e121fd8251a139a7ddc2f0a1ab017f5fb57de9155b76cbee405c96b8303672b8e52aeee5590fe6b2aca66b595ff829e7e909cdd19df4b70d47dedfca51519379a317fb5c13c69ee6b3198536d5c1d2b53f50224d38c9c50efef5d9c8c0e85d09e2507caecbd5865fc7bac70d2611a4355586213079cfe38dbcdfe59898db25f5154a4bd5fd42ff2bdaa896c794928e09501636638aadeb46ac7ceae64668e54aa5dc20aa68202e30520a5c58d382ff82aa4b1160370416537c483ae8a30a3c56d03fc34396d25dee22f1c5443515eeaa15f6a55089239d9d68402a0b264852d1525479484a2a2acca4310a83c622f037122b4dc52c10dcc14d5231cdec4905e394c0bee429340f045271938063703c41f70f9333bc920aed919fbfc521283f307f447ba38a6dc5ba32fc29216c48907cfe8fde0ef38b3b99221b2165c0e9b75a204b5d1d52de90db7cfab3ba2deffd10cc35de271aba1fed4b0a153f99bcf132654d5823153c27eb21abe7f52e2f316c4709891bf948a3c5cd29498a4f1d54e458eda18c1e0dc86163c8147628f154b50ed145f4499934d5bac1510f3f09810b8c42f7f433b7a4d453a7988167f554e8180ccaeb94545fa6b4611673d1669e0d7389bfb55436150c3b31d9fa1175aa757038dbf71c100c7ac97239a7fe36ac45e28b23646b16502c15864b56f54f3c8d784a7725058f6795944df2e9d37cb61627aefbe02d81fdd0302a52dc2563595bde74cc123d27483dbdf39920ebd5f6d7c3e7a766e315314071ea9e0d2103882e666feac3a080ba04c603c61d41b0607f539c9042363745bdfbb7b73d894b746a7310ab5446e51a6e4f29e391957f2dcfdea91d83e194fdbbe01293143f04e2a37d98db9adad7a18bb635b9e9897e091075f5740c39a33f74e38ec7f604e4449785a2532754c6419f4cbade9ecbdb29dd514417da6d75aacc8d6df0c9397505bda70f50035a8d9299e9d759efc1c89990b5ae230a7e29aa37111dd39394022fc55e22e4837c953c569902f8861650ae988d4d453ca4088ec35216f72694f2a85706d81e52be692876acdea7ff72966abeb7dcd6a803862cbd3845da2c2957403c1fc6b37ef828619c754ecf14a55477dd534aca89d4940bfca9895abb8870a18a085c34c240226c4891ffc068d6f5f84985a6a87e4c54986952ae5c18b411ada49542285b411c0780e4110b1871262cfe0324647ca51dc52c1c52c7b863aa72df21fab9f42a3089dfa5b0a4a79d35ca689ff93c03607d36226124882a8fd099f691a991fedf4c4dc3c759b7468669f84df734eb1b0384bdb71d485cb0d8928f8e3b96f55c6aca6755e6fd9dd4583f9aee55b8af8c05c6bc5e52686417383fc6cfe1144b08a9dc0a89ac0f9fd9f3e3684200e5328507314df32622568d94abfc3cebde601df82fc1771f802af3a496c9f2a5b7510df4eaf8b41ccb3f7eda2d0871e4f8c80d4dc137f7fc682e7197de0bb77f96a4ff29090bcd2b278b28f4c44a8dbc6adcfe466528a392f3ae6c07e1b51d1f3df6ed10bdb02e29c545def5577ccb221e89610a29d5575e41e53465f1e527b9377a2aeeef4c31a534b31490d347af3ddaf4230599b461d47c7b82959f6a8a6768fb2a76e2ccd75a3ecb4f38d8bfabb4736ffa52824f8bde209abf35a984555982b5ce7ebcc19bc7e34c04388b905a39becd499dc738aff4522b7e075e0e6d531aea9756b4c6d6f2b3d58f332686cdda9da3eea72e1615f35c6757bb7f034146ccdcae644a0937c64ae6bed9eff678cc9de948dfe75b26771cadd9410775ae0168ac7b603c4f5e12c55708e5cb6ceb4e563f1505ca8851007e16332c49a8570715943b4305e7b5b5a895f3983d1de1eaceefdab7facd635544bdac55131f1a62949ece54d59c8b6c4f6d945d80bd3cd9bf9c29c167abbe84754a65030f8b3c02115a70b70ee2c3ae2763c19751eb2b544ec7aa8263e56932c6dd5c502e815c173f26662c5af4a24b36dd04c9b8965dc0581909851e8d3fa0db16f6fc654bc49386565ee7a343af62fbc823b8c73f0ce838fb204928d2267dbe56dc83244385649cc26ffaa84c4d87f19933dfcc23f47f43fdaa7e38d307219c0d59b8daf7c3d79770584a4eb522c483cfd1e11c52e6b5e78dd6fcc3f3af630774083a91cf91ecd3731d696b57042342d759a6890fb95bf91c1c4c6fd7cf770e3a3e60c7596be0fb83199e469ae3f08802c3aa45c360f4bd4cf1ce5cdb28a0161d85b6162f73f669e67db9216f28b112cb2ad2f53584b5a2e0407db92abe8669b736724571a02f3f64e51ed5ba08ec041cf84bb4843cb1e368dfb729ac6fe8275e9b3a304da1444e26845bfa84411584ce2c97ba7328f32da048d451baec0f6066cd022176312751caee95044625c871524027e57c12c226818ad632560f727e93b4119e1c20210f6ac4a7202f477e6f41873e868409f0a70733ac35d92db66226663fb142972d522222b18e21375add85fa044d3f065eb900fa2f86003a8b70a23a2b8f0ebacbc88493351e3a0b755d706982fe98c0fc5b48766f283d44d62990eea8d0528e3d4311e63a2443c6858c079582accdb071269553a8842ef831085fbf3cf5d640e58f4129c335b36ac650e9ae944bf85021af5159d1c921ff184fd7d46547bf66a268e569454f00ce63c6848338d541554e09bf4d068a0f820f09eed024f81533466297a4560b850528b9f59b4ce9e269332011e2567937a500d7246525539819d0177fc1529620549c2f81e0befe095463dc16119be0095f7847f4ab19d199542538e062851054d956e4c938b65f4875cda6fea1bdaa26291912b561aa3f4cdaf21b1693c3bc574c36d3c394c131ce28d75034f69d4d533e5c9d0b2f171bbe94a4857f181f244c2393d8b093a8b4bedfb1f8543e38b370b63f245c354fa55849cc07a83eb406daa00c5b287adeac9feb24f509df5054bb0dec52032ed198a7dfbabe99b3d1dd10c4154aa18a5f89842297162d0b6204bc6ac6312e4083ad65e91d0aa121c64ecc2a84d04e834f7c2a94dd9cf196c9b869bb637bfce0df787f1bf63582002e3be917fc1ba13f4be59d3c0416ce22a553a44b5c89059c2ec272b35245b163278a48dc1fd8d4ac82b1668a449a493a70e85c05041c559295bb3700f4c7a193f4440848a8961c59a924d6d95d3b5825d3b91f98d48d36d712d698543d910008808a4880e5b6ae91622e2b81402759bf4ce28e13ddcdd255b408a9d04c5ec434dba54820d278c21d603d60f7d1f1e252d23346da826dff78ddc528ae093aa8c06b3f741814b1af9cee7ab8d542c4cc5c93cc1902cfe47d3212bfb61ebea639ae5b94496826753c7c63d3feef80bafc1ca606281efaff42f2f9ce2eabff74aba76aa5c82e6e47bf44fce96c564ec2e5b65babd17181aa0421507e01f5828f31e63874c275c5f99ad1dd284c9cff9af27f28289d056e18b7c06f3f5ea6645ebcc0d717d511ddcd23fa842b009082e6a8ee33419e6894717b2ac3e4a5c9b683d603bb02c51fc4029bbff7f37fa3056731466633ffcc5274518f51fda98e0ef72995b1dde6000054e144cdde2a85d4b456bf29e4a25f4e0257e2b084c262cbc45932bf7bd348c98c41fecf05b6bfdda7d8cb66067004a07486c46293a9ffe4d4a0cc4c7e3a7ee164f6892216567f7b10ae89387c8f9f70714394e796d9df7386c6d36cbb4ea7e2cb0cf447f03ad607bab5cb4445e7257f18b3a90c628333abf48dc789960d49f42461765102d9bcf1b90878b60c1bea8a0dd9cee97ebd39d5f53daf3929eb93c90697bdd038f6c2213b09c80f3fe24a911788868301312b928d5324409e8ddce1188893140f9b999ae83a1e78d903976796ff9a43056da72e4824c7bb256ab8e442ad341a89344731e3d6ddd386d55c62462fb439dd78fa27d50f4a25c9ba22fb69114c2dbbd0b24ef5d9108809d2794040a50654ca48123043d3c207dcaa991365ae1f176b3ff8faecd6f8c66be3227f23c7795c5b2faba518b1c915c9b950d7a678f15559c071f09936be71da62c4199677622b45cad87ca2df5a90e2eb4aa7748a15067432ea56182ca5152d6c6c4732c244d1724fdc33610f7e96161e059399a88732dc3f6766a7c3ee4e54abde105d0ce8c489ea31cefcd0e8494321098496a5a6abac5d743b57748fc71d393bc5db063ac79e9b63c5cae3e7059a25ebe487451a463b45c2859686a9857fdc189e224d32460f359e726fd4f178badc45030737632e359a630a61125b4d78b2ee671c78c2c0980e46499b7d6fdd1df2aa8c975f02c91eaf61d582921e441594fcf1a4c4b26f4876a10517202b332d7afcc1305d39af1af50a134e48a8ec05c80a89a19c5c2280c4c6595c088bec0f6b6d8a2b7773ffff5fa140fb12e9b6ba8c2ba3580528d86da0643d43082a58a379cfcae660ad4f485448290d6b2b8437bc51f824fc24ca064ad78db503d467faaf32ea35662f4aec10a0296bc344c9d59236e2e9330e9ee1c0b9d74709cedd0cbf5a743fd1c766fc9acb19cbec868f72aae868752e4b6c1eb0d8fc874b6fffe0e0426ad3c8858ce845395360ea98b4d0b0367afa289d1f55068037520015c59d90eb4763f766eb21fea8f61ffdbd8c31c69ef023c8ed11c33a715682f2edc551645d4ff44299e44cc2c0d90683c945affb330ef0f0e3996f847fe8b76814f41994b010fd028d1bae8175865e595fc9ae5f2654604e8dec12fa0345a566136c1566b331d942ad8ee005efd59317f3429ab5401d7267a042a82cdf6f47b72db8d2e21e0f9a094ab5a187f688771a6193208c0f254996466277669ee917713af2bc17b88475b73c16c5ab12009c2dd45f459436c3d3c924f63d607ec13310c0d02bf5a740ad2107f3c051585ffe231c4c628d79778137a7d43b98e863d9a39583521924f337b1fdad5d15246cfd1f39129070eb5c4a8e8f4be2e133d9d39dce2d898ee05c8a90c0e0cd6829c592dd3c06639e37813f05807bc82e984a8885706e9b2709af0b3512a031bcf1c360c2875f01be69465ad58e1b542aaae848dbf4c115ae910f805405ad90471ba1b1f0d7f51980af7d1a2333ba0b531226a2e746ab1789c7894f7246fcff9bdd10e0e9cc18cf600810cb91e90f7749a8c806d378f619f6419b5e656a51449821262ab75c7118d8a9e819ea9a78e17dd0e8c8a3e4c321da000448155b2637a04d36848af447508404b44af95069c586081d19ee10edd31e0ee4c4e37108e0b75ef34a503061d95f68f569d06d61596c0af833bd130f9c1b9c9640226e17a8c5966645b694b14ba0415c275448bcfd782ea5b6655ce2402564c6a760226e73e358a5d708c5a209dfa13015662890d3f8d4d0e238c395c97881ab813973f711883fa5ac33b81f968c855e90ba56dbb24655f86f0d6ca644d39e58f1897f7b0a7c529dccc056ec3f7a0e34a9d4b329eed791de6772506b77aeb8f86b16a15d6b60512bb285cf9c2d458e407b7251498b2ab89bc2125002c4a293ce810e6553ac697aae362a25a468d18d43338b63f070b174612c7a59c8876aa9f5f70a4cbb1235abdb33591a120aa31559fe83bdb9cebb2106a135c4d097aa4e356ea82d8ed56977c3c770b7388dd192d3f34fdcda673e710affb86309e29f1e47ced76c0dff9cef1d7af47ed07d6d25e81da59d64fc746e40a3930ed5486af262590e895a353f0c667be24d7b6c58832c7cde9aaf2fac2c90cf4b52841dc240873cd523dff8fa7c627c24bbdcaa8440c3235ccf52acca8966e383e2e12e372f740abc53dddd473fe4962e3169517090ca64fc65723f267cee197cfea497793990bf3d6dcf62a778b3e3b40164a7de8c8e8dabae04e2ea55c36f9e5d77da708d0e69ee7c4c64efd47c1a170e341a574d39211d9bd21188dae24b7d2524aac6df7932a0852766e187ca4c0401281316acedb230c87393b1d62d151783f0107af211f1713aeb145f7243b92d55849ea6a0c2d43c4d6b33ab68a8078a38c3d713b949116fe536c89a384095a5c717ed065265b1854aa17f8fa1b49db1e94f3c65b99be4329df60f783e5c9677bd64a2e1c2ef5600900b2b7faa02fac8cb3ee509c56c8f1d2d1d0ca6f6141781690ec1dcc63cf4a8de6655091d142a9b71aeb18426048535f68a1dfc1b4aadcfb19b5a069fabdded364f387eabad2eff0490e582c0e786496fbdd3ae9820eea2f799b05071d9f8b9dfd817d86e905bd6ca9187e081618c07dabe3122f73bdeebad1983c78aee5010bf4dd2214ea86c59e592874bac96c73a1ad3f61b2ff61be0df5b08c3021a1d46e729106cb6f3be8dac5a828a9c9bd20c597dbd6a5e376de33fa8c6aaff8250a297d91f9355519acdb024f0c06130449c8f3de38aaa0e235995968c282c588dce337c064e1cd0bd10747fb28270d2f736c899393962882e2f0e4709a9cca2d007039537f19c3c7518e6f65f798055821269d8fcb385b93f406dcd91a539c05e43514efb80d3bbbb4e3bb23370ea0ddb746c616f209b4f64e5d13dfa1d519806418bb13d39ade152900d0f9e724cac8253797dbb56655a69ce167239904fde385dcf12b9ddc20da969aef627a6299175a29223de4f8f63ab94eb209c3b1a87d0650a87a838c849a40ce71c904577a5ed97535cce8670cc28149656e7fe0d7ada0ae70d2994685d739747c9ae252eca9ca3747e6623317514f0c766176b15142d32689e18534719ea1550ac0315e9d12db3e2f2e1107224b86b83a5c1c66b9fbcb112ea7e21dbee403a421b0ff5020e25989dff4f89036d85568691ce16ccf7d81fea35e62419929427e01e61704dc2567198151aaceba97ffe523d5953322a281c16c422a6a8ae4aa113b19e0640919a4e5c8f840d750be11abcc73b75cde857e9d7b6429f49f06b11ec94152648ed7cba66dc91326bff046b3f717ac2cb3236d293b21d91e95537f3324f6c22c7c41fe19ae0b90c97aee4a224101a3b8fa85b5c4a2f4f857227fcd59484629625d3246bdbaa1724f6844c9d5f8b61d44a7cf7e9b826931938242b5c42669315512c3252459ba0d6331488fa9debe44c413fe6dc339db773e43002dc066c0281d321e16321fc903955b6857c8b2880deb7b008c8825905a28422f8c8a6f1dc8e848351a2dbf53c38e8fdee63f40ef1d6ffcc2c2956c56624c5e681f955fbc7f95b25da9b3e9e87517c274b88114f96a5f43661f598d62c732e8a8a8515912e45b9e672268ce4c84668ba6fea7a2c91a1639bc6a8dc32fa09daabde84671b2e62cd995022e4c9360194fc027f539b1f4f43133d90d95b343937bca095d0a1d8425c73f187575892bfbdaff52f4818ba6c55c875a758fa987fb2e0e2fce594e4311a02334f6ee3f132dff4d4878a8fcf3a153c71736db6e471a2d7b3eb8a8da0c26495ec92c12e3b031a7ff899d67cddd01856d4d294b0831a2155d4f894086b2c2452ce0172b56272b2be74ae8768448c36b75bea90f76388bc88bd8c7b49c889eba3b3647f53f8a7cdb285ae23a7c62c9b521b0a993a23aac58dd512dc8763805ae49af4b788a62ed96fdb7bc3245ef94f05b66afc5220d5007325f26f31bcd954b9d5727fc4b31d0326af32575e904d715843a550c8928af9b5d3fab8167191150abbc1b9946d46e149f76d5ce5504671601012769f851dd2cd0595d2a420bd3561a3234205bbbb29486f6330de5e1a3db3adfdc4e468d3da807e37de912cb4f22f02eb66fed67e4ab38a14318273539c4e2d0aa6915cdd6e0f4e3b04c760b817bd398ca66bb910f2624ad244fe0bf10dd368708f9f4e4ee4a08fcae279c93ba944b4910002ca472c2c13dda77ed6dbe3583c348f4dc192f919e7ff864e3087b0ac1bae6fe603247f1944153bccfb1fe052250017566a732bd5213531182f94b855e5cfd229480de1ee7373ac14fa6869589e604fd66b0895549fdc508a20c5906b188f6661605f205503e2fea483838b28844f778b226ab931f78c03c4e25e41bc06ba582c535fdf01fd6ef65dfc3064281b90706652000311994fe879d8ed6987e80504f2ff3f557ad521ba37f984b7ddcbeeabbcb12bb6cd80fc4df200aac85c41af952e5690af10b55f64befb343e9509e64410e5a6295d23c4cd4472a407381d1e96bbb9cee5d2cdf391003ab1c3ab7f959b2e13041dab2a1c846d7ebe405fafad0394bc459410379ee2076625bcb68b7c4a32facee6ebb5919820b41365944c6eea9be55c71db9ed74fe988141cfedb96095e7e94943e232d72c76f835cd0ad21a03862f9f8a5d5b9d467f5bcd93d39ebc3528a6272aee74d247acbedc52ed72aff1d4ef7ae4042a7753673d5d609f75c24cbf20e6b2a0a4edad9fa7658cc428705066b02c19a119dfce4e226595df7c546edac7161956110d515e157a556bb4d704709be5d0f20ab00409d57f2497fd6e85b5e12f90e0e287b54d78a5ce68af2ab4bcf23bfa1226856bc66755a38f1ef8bbf9e5c39afa9b26535de6ab412d6a4d88f7586931715d7201e8599afe49203d1aa9904926f1ddf068493b00748db58f19f5aa462b3f0da6534bfc06410e2bc45c9d5fcc2ddc018f1b08290ddc035eefc429f8517a8c2985fc2aa32e49d4bbad6b0d9addbd8aafb1241a6200ef21819896d7f3cb99cb247008ff37db1e353cca148f44cd524f700a3cff3be3820345631c6197808522a91455cc0297d092292fc7d510d16d10f9022ade1d77f29f6941a1e0e49275121212da94731cbfaa6ca657c53cb355ae3440cce71d7cade0e1e8e8e06313fe8afc567660ddc19a94b605bc969d2c1204222cceeb333a7b51eb32d49c05a501a81e7ec5ff684ad5fc6deed8c9ac5e0e5d42890653313eae6a16a6fcf635c318194e7cbdcd00fc657cd938f960735feb27994e7adc419103536d39f53734cc0d4b36c075712cd6e8dc52a52c3c66a1e10807da89e343865c115f313347b6497d17fcba2970b292e6375ee7951ea4a073aaf4fd76a7d1911e6b9cc2f09dbb49950229daa30e082c71e31d152fe953ecf03902f502f7fdec6e4e53f3e212ee31a5e2f5678541eefa7087b22086eb11fad9d805f178b2b98f828ca3856741c03289a01212ba0c741df7c8efa9d57bee12a738eff85f148bbebf47acc32a1f8e7dc9e841b4690b213c438374e76047776b1e03c977a711e98ce737f1913757bc3280798bc4d9f9fe7fc2357f3e44d5822fdac3069d04462f7d0b9aed0a19890e6653e44a4f2b8f999d36ed334a7c44621b04389a0fd82df9b51f140c3fb058f8cce355da744a38c4c04226eb02cb228d41e22f403afec12e958596134c35f231aded03b847a30fba96b310d59dc1adceab44256c8852c5a5533d3b9aa18b774443a9345df18352accdd27808fbcfb1ad1fd8c790a17c9bc32e9b59a59ce541916ceb0973e928da2526524bd7d027a8d1ad4cc643664f27927886a99f62126589219d88227b3573eaad358cc1ab1a622aaa528889b7eac81c1cce785a687a7fbecf163c23103ae76489fb4d75b25c9779b6d2e335243b102b6477494e95bcd87d460bb4db967a66b1697e15b379a694a1c1ae28f88af645a3bb53298ee3d39ebf303cd1a769e1088a33871a6d457e0e55c1b327844ac292240d5e4523db147775c665fb1ccaae3e42caceb9db3394740249787efa7ea80e08a47e9aacaf3e1a663f9f6313a8f398a9cb0d198f37c53b90a13d8dafeb0ccd6c414f4c0c6de4c90f2d1a3bcd9ea45a056d268f1807d97a8ec5313269c03284cda28475838e1cac2e44c0744ee46b7f6ce8b4649f53a27d9c32671393a4d695e438608622b3fecea6e31c22d578e4739e564f2755e8a988ad37412a9897cf14fc00c70b960847b4df075706b82f4f8d01f0ea8969a1948cfd8afd3b74318206de42ca1b228642558fbec668d22b68861dc48938efc39a954741feb783fefb48e0e2d14a66fbb2e6d9253179235da1c17b30f7c09bc6d8a4367c84b7d2c15e2daa9bd18151edd9db4b0fcdbb168cd9b1d7a7625bb20cc550bf2dd65b6800670705846d2f470d1b528371ca8a253d7486538277035b7b1668b9e93244e42316e1e4a35c0ed1c6e918a4c4d9236c95e5c647e46b9db47de5f6775d08c79b5f509e093fad57208ab71a18f5176ebf1d06aedd7acc0b746920adc04c9aaf7e8399e03ebe018c65a2a3ce6815986fc764169139e1944ea727bd000be3b5409ef9155aa6ca95878b4a505013192326367a81560d253f1b1847e5d96ff24eb5690ed9459d23b54e9a0f71e454f270b3a3fc7f82b8a4d1632260b85e2dc388d094606a80864df2dc5e331c0161f7c2a2da08765118c8a94562d6b3e519182cfaa5d04277185794059c3efe6eded4f03995c62b416832dbd3a547fd5eca31aa07e7b2e61630bcbd727393efa90d1bab6cbbbdc475fadfe9a44e7d7425ac1bf5893e781b2966b91fa6b5fda224cdc2020eac23455c354613dea54ea9317d7d129fbf3c9ea28cc93a01fdccd500e0363c3d7be7e1de7188e5ce1fe947e369ae05c9af08dce9e897c38412b306db97f9e9a4d06eddaa0eab77d743fed64f7ae8512489f3886d5470ce8ebd12577b1cb08be33740e599f765d3ab0a0dfa2f05a3f7ca69db9f5af3fc6aa288591d5554d5243b377c2277283d349820eb2bea7a5635e03b2ab86a1f0eb7eadc23b0276369eb092409eb9f63609e66992fc462d2e8d2e4e5e969260e672b70d10dcc83e864f666e6b7932ad3b1856b1f71f475a16b5cede7a24740d1fff06552bdc6518a6b44f1c9a0fea4865913250041e0a598b2ef80f39477fc27ec7494305ce0d0ee8cfcbd86b76ddee0cb76fb6cd40b3204e0fa7ae8222aa102448ce00153da7b2a60566dfb1ea930b661e89c0b0ba22de33e7ff30312960ede84fb1157afd55c87a43026b126f73f94ce991678c2f5c2d0e54e810c01ca41c2a72991003fe76b909a7bb85914d83ed7e07ca1f27bc9b5bf38570e02224fdb93f1efb1c11dda1195e0a18badffd7a40abf83ecd573067e4caa492c5afd0d932d1790d91cc70aa52b221726433c386ab67affb2bce09082e825643c1f9e904228318c1d9330bff6420b86630ed85d695758f9c5d4f1c08be8ee362fcd263e80341e67eb297c1496e34a0f4c2106adf0dcdacbd409032dedc0bbdec19fcc86f9f9b04270a280253a288be38382803ca21bcccc6b99bd0577045e2d4939ba9c9ab7c4f2b9c37d1195a2c82224e13f23f8e020951c80d5a751964b12ee4d66d0017b4700176f03f5e31f3857064f4ac51877354efd462d0eb0350615f9d326337a4f0a00f993e0392426a23f92f466d4f07b30531bfe883d5cc8f91fbc16a74427a22270b24ff8830698a02f22c6a2fc825ed720869bec328599f33f93eeee70c00be57e605d6eff003a31e0d6135b58d61c40534c4587e3b9165262e9fb706e196683b2c3cd1f00e53ce7f880eaa8255798e3ba719ca490bf124a009bf3134aedf6c383cc0cda3edc50eba91d649c603b729a9398fcb12fae2f9c938f9961714dd9943df520caaaf8c28b922075467dcdce22804fd587c1197232299b56be175ad9931297e8007a0c458aff5a7ceaa310b0f87ffa12e487985951c4870504464b3c4c1c2c4027de126d7e48d6d3720d46adc03b4aa40e7f8c6a0930b8b0855e70cd96ea56c115290f68bcd62d86f639aed98f0316426d47aee53710a050aa5ed04d2de5e52c7842013d321282e15cf421d4897bccc64283f7d008bf1a8e6365b039ccf301950d812b618eccf145649a68968e842005b5497efb504a969ebfcc428e6a534bc4aea38d9dc0e0917398384b55f31ec255f8b1a720f8728136d20a213c1bac934fd2c7e1f55456005ca370066aaa62aecde72fa843810f64b475b3d8d4340d7d00cec1cb211c53a82e5194ffe7c9884d09e79bcaf2406e821443d24eb3ed2c4994537e5c0ebf00fdd1dd582d00b1d6089995ddfcfc39bed692b21421a2d4398d6fede843baf9541f0e3e9a0cda0f7af98efd4c85b17b72ab56b5700b3694798c2317c30f38bcb63d52b8d9b41a4108290e52580524a242dd6a9588294ca0f02e1e952b3f3b6da8a136182ea294780a52c288ca74da9c7ea6b6bc19906b0efa74ce8be12d1b45dfec5900e4da2f07392ec26177c22ce4a14f17bb66757dc2130b052c03e9c86e442cb1fd8100fcafb1b09c177cae896b5981a552186c508345200cecb0b4a763a1eccfb6b47aaf3663cbff1fbdf7f396f2e9e962789ed2a516701b516c6c782b97ab42611b035dfd56fa664a5c654739e80351714e766296c51ebbb7ac4a3b449c6f51a5c0a559de16f16885ee81bde4f26f43b89841b2b72ae3b30ecc028395c7f3a9d893303ef854a4782241a55b8d16bce5d85cddd002a0485c85eaacb9c728100d001e30f7fac23757420ccac4e4969650ec32780f9bdbf5be43dec11399cbec60895889b2dd68df548dbdc30fa36f14a2d9c04ea1a3680235a3ed1cf0e9437acf941530b59d5e8483b0192876d598cec48e197e86048f23a3be2a36c08ce90aa30d7abeafb9347a49ee26261372457f4ebb999851a00e2e9ee833fdf97b0b52759a8f7b904c5f0f67ac54a85534f03741c8bceb272c941d39af1d95dab0406a406b771f0346fa7ab9dcc4b99262dea5a9981b669915aa140393da78c4b6cc20ca6f7e8f0b1acb55c3a8b3f814ee7853321a2fc2e112d2b30a878e16d741eda66c7344339ff9e04c6e5c023a0a1350f4a538fc27b3fe2308d72dacf59cdeeadb7fab63d38c12fdd11c6e8f0743759d5cc1452ca3f87f69bcf057f10a3cbdc5cdbf924446bdbd4cb7cbf548e4f52ba40bbb515333d74b8dd649a80209d4f618c03eb615101a5e48be1b40941859646dbc984b720026fa6e5b94303a0052f9c25c9c97ae9fae7f1f631b14710a73013bb73541675921b2875b002e5d498d08163e82a8978f6421f2193042179059a6f8cf9f3dc0a6c94ae674950b75fd733b8de98defb32dd307f9dd595bbe53f347c3df9ee350eb49f5676d1ee28fa17bcd73a543729331d6b9b2ca1128a324d2b257b33a0c44d183e00bd4df37883658395c1907a5a80542fdad5ea9f93bd1dc00ad0f38415054a18ee88a98f2b5256103d8af4c899cbc115289fe9bb4f05868896de610ad166828714ed12f0edce8ae84bb71b32df732b2917137936e6eb5c7b40ed0bbdb68400a4193bb5e0741661b429e63a228366070787fbd0c33cdac548a9c335e31c47cadc5a83c867d25ace75ffce4975ff9fa324bd9c7908405a0067eacd11a4e5acb9ecca70fbb544a24a9f368075e984737f6bb896c1c8969751815b254e23897536a8af2f890e156e914ea713a0b42b0f6ca40b82f2dc83461242700a44f6aac6a8701aa1ec783325d728cc062ab5f9d9e2394adae03759cfb6df5e01fdf32ad9ba3c1ba29694d6b1d8cc5f715922a0f4c5eb9b003f40b419293450b7e92eb064f51ee529f88f0ef22166d8afc76c62285d316979048cbdf68a881e8e57e87c7832ea0b0004ff8f1e1f9a568facc64af15ec08084a425695936d98b6edc8fd7f4c99cce997bc46ca5f59aa1f4a1162fee93838010bdd0bfdcc6ab2e25708cea2f0bc2e6d051f12063cd59474ace729c649112c8e626828185e2d8ae43b824935bc04d74f6f629b25ec667392bec135199e21a2183160ccef3d5545ca90fcc417442f8861d73e5cd2ec51ea8bd42b4addf8db68ea433385ee9f7a8255d2436f38501c30b838e42baa5c4bd892ead63ea69f453e17f1da5e3fdb1b4bc9779558773f6e9cc94caa8acc9ebef189227d48df91b2db96ef07b4070ebdfe76bdde7cbfbd8a2ae2808654c0c2f2c160ba6974f0693f6e7e4daf17cfabd8f2640740a4ca760064f28906b67074e7b07bfec361e3af49bb53ed9d15f40f27704bc5fc3805351e848522ec2fa942507ff92261f751318dc481c86b3db2b562b036ac8030990a9ebab2c7990a36ce435afc5d2c3b40506acac62647726d5293a07dbf4eb1d44dfdc6c8286cd333427806095398c1687d416eccfa2c7f9b04c33ff738205e55dd2aa5363e357075851bc158cda6f1cf2ccccea458e021b70c2ae5c2e0a04815e2130f32fb99e05b666e7ab53d2e6dfaaf36c3771ef4cacf3a135347f99fdfdbd555ab509e8f1b53c6cc24e19f00fdbdd8fcf8bdf537b3a940972a00db31ededad5f8082462ebeb02089c79e5b8dfdaa1b50c3f673fbb44ec12033357e6643fb24506402d29005057f3747fd64dee16c9c8dde565a5ccd310ad9a7efadbf86a8e4accd2139d94af6edb03c1081ee43d47a12ca3d3681e3b447c0ad3391ae947232e8c03ba2e4673c5a4b775adf247f03dbd7471d9041ef25dba3fb5987a29aa1dc75af71f4240a4590c69047d9fcbe7652754b92807f2f9f11dd6920170294d5d972aa31fa57659c621e4d1cde53bd8843e1b8d7b2217807c2eadcad9c504cf785247ede4df6720c487d6cb05599499fd154de7da7ef7cea4a956004f021d22723fa638cca959ca8b21ae02912982c8e8563112c8ab0ce25dccb95b4273568389373be0362ae1e00fd307dc3badfde659af377c5a70ae1f4837798ab369aad198af7a1e6a2312e8328ee21e61b8e2c2ecdfc47839fd91e6b71c9a9dd53de3e3d622a9748fc06cfc26ff671600ebbc79e81ffdb244a2e139d8dc26cbb1d59ca0bfcdb73c48717bb43386c3462dadce036646a870bd95a7e3a2759e6e2dbd0ddfa592f01d1fd5284495f3d4bb1c7c646e6ee590a22c5ad62212e9cba0311fd1fb150d4b13ed272fbd6e21e33165604c5a35c781609342fd2f336941e121dde1c0b925e3934de9d1601b8c23a2c728c557c47171e682f9794aa5d399d38ab5ad73b6ce8b5513658ff126040aa68ae8a37e2740a08fbedffc262378b63d246ef5d904c6d6c784c556cb80f6982001550fc7001cfb2d8991b49003ad37384bde7ba1aa1c281fdc5ab99b9c3a7f9649c3354df226f3e65f6c0c8fa167378b45489865fd703f8768ceee20930f98c4fe5a04c0de50c7fa5492d5545e913a5128c35ddaf037406f7888da3c41a4e2790f6704f3d6fb2392365e5936f98294e134fd434074d925c7a39837df65804c11ba437aeade4c9af1c3a24bc1e59f393d50e5f8481da17c38b5c033f63ac2128640323a3b68c49b1ed2b25a14cbf786e85317d4d7368693ce11361e0b69a150d500d125398b7a623c88fa946422cf092c887c9be2a65b398dd1b400672439d600e9b56d10e32073270257b8f63da75e34d9122902819a3d58bba886529acc8825b166158043b69be595baf2afa33adb4c555e5b06a544a9ba8e06e57d5283fb9ad84534374907cbaa8bfba8daa34f1046a7708cbeb120a132dd12a0670e915fcd4f7e8c71573b27f1d0bd8e4b164ceaeb5c0b1654e909d584e30f6112e8b391f710a0e9fc0da4231096d633338f2885312915db0a3bc47898e950431e921b4d248738dbca4291a3c0225155d84c45103d2c45a1435afae134808799c73354eca043524c77120898b73ec8cd9384f2ea61835c716e63e6d7d885062671fa4d28cd405105797a28c2706c09510122ccc149a6fdcf54aa54de92e509c834fd01a7f82f96eb251f3d3b30d6f6c337f7aef2b012471a0a1d38aee51fd9a96ce8bdf389053d1b8d1e929fd9529983c2d7ac86cbef6d2a832eaeb12a0541bb4c12a547657030b51f0ce4b56da06ebf0fe08d96d27fefe85a4a432d242d71c6779ed54fd3cf31047de2c66088820bd66549c634c7236f908f245dc42a066e360cff7b29af45eab3a947093c839c7ca3b64656beb10f879283de0924e826ded008a914e9422a087ef6216a22313d31c7d2d9f0e48d02e64127adafa843f01c07259541d31b755399eacff38912cf71de7c1e9582f0ca8d9cab14ff09b68fe00c970077dbcc318c87ebb2b1563607d19568eb7e2018cdb233f49c776d3da04d92324811818c933444a8195dfeadd75ac2c2026b9cb561527006cc959f8ac83b114fd52db2b8d34400b4b10bb67b7c13fde387aee6e2f90f0ccca20d9504535b0213daa49b4e11403b168c087ec4597ba8a491183b883f153a4d6de9714cdcb85e8c1dcc13c98362ec8d0549ca2efb4c94224163ce0405195db450c5ee088ca08ddde616fc9c0f63ad7742bc7afe9158be135e043b0217199ed72c913cb823884af44de3208377a8713ba1d9f6f44e93d30930d4c84eb4b45405693268cfd35539a3d541a7ac0adf72037c1a8b450e6bcd1ee330f082c8f9770e392ead87d31e493c53c6e4a1bd51a40e7b0b67ed7b045ba63eb2123c976d9c9386a8ef4872d8c84dec18abab58ca5c565d8ffd3f05f5a1efdc6d5c3491b1e72df4d4eca0482f42c331923371f95d7952e34ec13b99b0aef2340f6e0068e56362700a55469bf293678f16d39dda65c2ba8ab1becbeeb362a5fd222d5aa227f964901d17ac68683d295dc86ab2d0298d71529e6b241916d880567491d82467a7e5e9c0b2856b2b3a4468ddb9bff3f2bb69c614cc6cf3936f9c332c63b63222a70cc90c2babb45d4ace8c661fe1d4e6e4bf6d68d45998ef42cd0abeb4bcc02e8e86319008c7df10df8447a319467c6023e17ecedaff420523089b7d8d1553ba2731cc3a51a2391482f4bc3f85873279fe215c58ead04fbc9c35aa44810c9ef7ee246f70c68d4147a171be602a7365f3920215a3a09048c7fb8d5ee98ac635ddc3bc7847ea776df0353dc50d87fc6912b671ca5aae28a58bd719baf636bbe046f2f188387605556aaeae323ce17f300e56682e1941f3e305d7fb4aa670ef7c33d08cadb5c93d1164801f09d468e44ea6eb41883374c7d18bc6854ab9aee8b9df4836c6c42adb7587b0303e72587691eb9ef0f1a52c002845291070a21cbbdcfa1aeb32376c740efca2614ac9ce8c6220551a66b93932f0f50fa420320846338f479575244a0b07cd1e4ecdc3c0a379aa2db5741a9fb97edc6495e9f2fafa492c94d7af00188c8a09015d8912226b9517870892eb7d29c2495724b28991192b8d507222ada5547dbf42527fd5ae7428311f8124b66b04cfc5bae06707b48a560a9ccb7ea257a2d967d791f35f1a4a2057a35084e5eeb47ea4b77542884fd9e1661da11df80e1fac2aa9c833b1f280add502c2413bdf0289c4dd4fe7784286467aa49e8346f98b4bbff63b5793422eb538cf915dc40e04825c773fff66f296d6a31106076099a414f20e7afd81b14ba6eaf20cc6e4ad4dfbff048cffc219200da9873fa24830934ba8bbfbf83b7186fb13808d800089262457055c6985c7ba9fbde55fd3884453995fe0b2e6b82d9bd60a79d0cd64edebdcdbc6fd929921643cee2664e292cae1211124bc4a84a5e0f64a310f26566a2f52043225bd982562d3911fdda5d6d611032667ba02c5634d206a775a255a53a430a67b792ebf45d26703221275f9123af20abd4fc336996ed3480faf880245f8af185da71550249388d25567ca26bcd50137db2d4b30af51a06dd5c660d4e9fb6d9b492825b1460ddef4e8cf9bc017bb697c8cca2a2460ced55192a78e4bc1dc5da88633ba7f27d6d7b05e4f4aca0880ab27c19d6aee5870eaa5199d31254207892f1e7539b2c519f29503f50c4ece48a2b3a52852686dba76d4f7f1f02a3eeaffae62252aa584284efd5852698f195d5716fbcd20b19584ddfbdf6b61493602c36fa9a87c4ba388b692dea00a06279e01c5865d5075e0cbceffdccd0b1d62fc9c5602b32fec1205c27761c89453ad36321413d523c1ed9fd4425b5103e9c99613148b24643b33ca61cc830c454b874e8905c39c0b46edfbe47c62dd53452f36f0f565a8b66ddb5415d0ea06b37f8483c3a0ffcc1a70966a15f3c34bd5ddaf57242ec70b2c768987449c991f063f1292a16043f63e3dbcc4aa01e4bf4b857f9160243327e10ffc44e10e33ed3123a8cc1c334f409c8acbcda8490d83711619164a4d7219aad006aad41351642c325b0e52d5ef9bf9d202b1e4565ac33cf8a1bb351d7c486af8894b2c849f713d4581db3fd5c1186ffdbfc4532e168ae9cd4854fb53803a278e2efae9e2a4e5dbcafc8b73bb52e515bbf0aef4ae7de8365543137ed9cd508722f8834e59cd8ab5acf40dc7a83fad6b115997bfbbd152c64a7601db022eb3ccf179e7c88ae8033ce53ea12856ce255b871a836c73cd7d288e5d5c39108081aebb0092639c8557ae36845d53c2e96cc792b4c0106b94b9f8f8cbd915eb6fb3dbf82228f4a69d0b0880a5df4248077f0b425afe2de40ee8e997306e0a7041d659ad3b814b2fc3635230c93e221215cd711791e9241b5b544ed9fdd9f9f0fa28b61d84829662c310c1256c150ed652166416f12354652128bafbc8d8ff0c64d5d84154ce31f8d11c5ae5ae921ad6e57441a2e44b595c8d4162a6ffd515e2a7961258537a1be938287e5e6a1169d8e042b8ac02921953d1bc4c7368aef1293699295fddbcfb6771b568e574074ccdf5c6096e758d98e4eee175e1e15dd9135e5452e62e648090fdd498cdf342525719b07ee8d6dc6888cdd7c5053c05c79e9d40456b0bd1b7e9d45d8263d2e73cb11c79356522adfb62ce1d3d10aedcdf36cb17316bcf6ee29d4209b7a24ebb534b775edfc4585d37150cf03606b4211334e74413d01028ac5a214d5aa10b5a0ed7f5baa6bc270402848f930d10d004449f0242fae9734054ca14c22d40d57ed7e92a126050bac5f6bbc4a6d776e662af1e8856ce2760ae9e359da6534fd5132dd45d16c133a746d7569269db3162d37511e889d1497f1af7fca4218ebd173f1aea4ba3b7342ae3c95774c8154c03222250c5838c7651c18e27eb4520bad88139bc4051b0056f388ee66d5c206ffb53f86851eeae417754ce9b57b3cc74c18076632b1985b1b11368e0549dfb221a4ebd131d60bc82245cbcb75731b1c1592a46bce7f328d4de71abed21bd9115c11d633e3080296ad1c5a67308b173d89f9505511202eaa8a2147b742cadf1982824f98823800004aa36b6988b4628c667390f07b8fae18608d8c7dbda44b6969bf0a9aee90e809f1dd4c784ac16d5fd29ddb675bd850a82b6ae96933faaa3a8803bdac984bba1c1d140501757b8c12606b809b0ad45e0bfb6838954486a29e553b816a2c6a9136b7a7578dda8b6172961d348791eac5f8f376c63c155cb13e309f35cf281431507239f30d3cde665938ac48f5a1b4e18a35c88874a823a13c61d1c44024a4b60c04c3ff408d67a602f08df38412d7df634334c3fb42104be47e75a1bb941400c72bc2d8ac728c583c4915235a6d9fc9c574d91ac22585b942cf69bd83fee6c5476bac187b94fdf3fad472f23066356190c7b2e68143c6dcd2132c0851d9752efab0828b9081c8e2e173adf66ee96445b068a30e211e9cd01f104471b6a1751125ac4178cb614487d7a43604e2cd5ceb29fc824cec5251f568de8d17302cabc5cf47a5d0d0cab6bacdcf9d14887a9c9f922c3de357373e83c2178fe976a1a365b594c20ab1ab18f5f0c13b07d2c8be69945291f9220bef3754b55d340649959c3d911d4ef49f5c6f2f85a406aebe6565886c0b6b25e51b4745638553dc00d56b37803ee4113da61857ebd1ad1fc012f3f9fb0e1c676c49f7096dfabb8b1f53d03eae90a0592d881c8d3f8f058661fbb80d10d8669e9a10b30e86c9ae9949bb6aaad71e1fec235b810fe469b65a024e0d6b0b1ac84d11301d3026973e1c7bd173a9cbe7a73d8d669434df02d97894415b4302a323bbe7367d4825c1af1028487004de45b5b75ec77af3f5cfd5fb702a547d53814fabb8e57039c807f2d484bd2022edcb85e374d9eef2736f97e0343960b9f7da0a8de9f6db65d646f2143ec71f042905c702591899c8023c0543dc74164732c549ebab0d25b8da99d26a7bbebe146f2b7ec58df5ab0b4230b5383e4c412d802f37b0fd221dcd491ab8e9ff4a5a153be8c7b3d871c9585b6bb44ab2c3768d207b8886f36636afac441ea46a85715cb573f874e5d4fb47a90ec551323ae07b5265c9748d4e4e95f62bdfe34939d71f8d7597cca5752c7ebbca0ca85e5ff8caf0957db188795e56acb23b566c8fe8aad0bc60250cff5d675b1adb4d6b44332755d010a0bb21594b2fc51d497bd33e4539f859b8b60d1782e457b41270b31e78ce6f13e67e559a3952c93566a90493aacec5b12487c7586cdd0d159ee0ea3776ad50fa5f8cd27e59808872c5161b60487b32cf8bd251c0a0364db211b8ca76b8d8558e102eaaacb8a98d6bb4885f34636ef29ea2416920d969699a02aaed2699e3c3465f18ac2cc151d0ecf405189b6d781e87451bb76aff4102a9332ec022314968b0163e2b08a39a967743dd34579de202cc8b4ddeb442a73afa3935a2d20984c8077f3184f27768cd8cf6da2d67ce1ba91b10a6f14c4acfd8a0183e1ab06175bba9dc70a5d5b4942e6b4510f5107a4d7414e1a4a3b4214c2ba888615b30616d0310c10762b08690d588f3ad0ae92d9ba3a67464a6d8711c4698118514e9f41bb01b4cf94d430d3ff66e6956745e40aca11b4fdfb5474bb4ad2b087549b0ae54ae7f426b5a331f18ef00af89dcd6f5b0cc952c0c8d4b3afb60e2422a1593769220eb36549cec7e43367b51d3f7b36f8af1143253dee3695c69a52402df8bfa2e7f062a3c3871c15ae34edc92ca6cd4996e0a93533730232a953cbbf76a49a111af463425b87d9a057c89042d2a12e796e861bce2a3a64d44f8051484b17a1f84773b72124e368b3442f4bf3f83a5d5bc02ed574e3701a2673cb77d0d96c3de9b6c0106c5e26d7bfaa87c3761f59d7f318ab69110fc933b6104fa2cf127b22f0455622b30462a1a4d6137c347efde6e2a90f94481c275d23544745a75dd46ea84ac8a1460f055ab6dae395379ceb97791cdd09057ed676a07e11bb43f14748669c9a9e9df10b299a68c11286c552f9a9e230a19b5a82b4bf577e6df054f42ad9b52e34e64961efc94491e823bc35ab7c5011a3daa8daa9105554d11b396cffabdb0a64fb6c512481c51a03112dc5c262bb64dfdb9fab188ccfffebf7632f07dec99a15e69a679bc34f8b0ad4b43e46007854c9841df5c3777416583f002fd2138aede64484195195f2d87565ce933483df3ad94f5bfa8033da4f3041a591a09cd91bca84623f38a95b47e3d469bfd98ec70bd72169e65c9621c8db39db3e0c80af6a1de7b45c4c7e5f9501873f831998e2c89264b379ba9e1ae045a6282909d2757e5b75c7f087740db7188dce1ed8f6035372ff8067300692a3febcf001b24be9cf09ca4e70b3df0890deed4ee3ee34e9aa495d7f17812d5d12850764a2df6421f9ce741e968c909b417e05f074b07c16ff86a1a8e7993fecbeba9acf5b506e08089a14fa81144d0c8707a97fa310eeef266d112818820c011b9ea5367fb81bfe4acc5f028e211005895e44773399e0318116a66c2bfba9cdc341ac1b44cba5a44c315bf78b34bec292004e375bccbafa1b4d7d80ef78a3f0041070a6a2c5d7d3f9c3240ca3cd22d6a0f6216309f9a5e580d7333f83f3f3a37c825d6fb977b369e712c3e5a0e3986a92cf0f4f0aba7ce9043f641b042c828446c213e7574b618e3a24c0ad73cc751017264c036c1f2de71e8405bfe50283e9821476457584ef78028106f6cc953598f87090f19411f1bda68f373e373878f4edd96b6987101cc815fdfac6c1a5efce6c398fd853fb25d8ca1f8ac20367ab0c77b2452be15621ffe4ecf74e7953f4fc54ff2e6ef2b866a6d1fecfaf2ed69b6811addb8d5dfda298d64dd005da3bbab197951903c8b5500edea2110aa29b2bc5723f9259a9a98a6ba90f469769e4b8318bf50fa22a59ee107c0052115042cd68761336a96ae7202c046f99cb00f0c4ed8eb686a3a3672bc8deedae60d17462b69a4df2a14be64bc5824c2eaba082eff20eaacb818d826189a5f34b306be4cdb6fa6bd5866b846d6fa326a095e2d9b046f139168a5dab6ee017b8da051f8cc6883f79a1a1472eac7d778f8defaf5f53ca29b17275d7a45b7161f58a3542c96b75707333bac8f50699cc390f56d50dfa9d257e44037d3af148606ee20918860d485eb4220d7d331e235080a646e502f1115c99157c47cdd12ebf1403af57c3c27d4f29d2aac533f291cd1b9738de47005b132fbc343c053d1697ff5b225a4cfed01167e863ca834b1ddc9c8a168b089083edcf66c3ee3545fe0ed23f09f035fc8ac6240ac8eaa3c0465259f8ee8dbcadacfecd009af24f60234492615940e5ff5b2cdb576dc34f5c939e76e373977145bccc809a157eb7ff4f5a9cba62499111b8a759a91b86990a480476f6984ee95db5b0d429c1714f5d9111d1dc8bb54524e3c4f8e60be623065867b9b2d929504b91f8dbc889a887dd3d2bf3789ba524612f7ccbb33036cd5e01defd0a802d82d8348319db2292979ae30043f5e8463303a7566f425b120b160a78fe5e77c19aeeebaf2ad1bf1a7e19a9be460fbeaa2f6525a85e3f75e30eaf62c4dea73e96f268e9770c64db7fd17208c9d83e870f6285969a47e14fc5b53a30570793bb964cf10f3d3275a1206018f266e48e6567ec8d2862e538af0d7587b20c78075d78a17027fb6c12d3b4578ebc63f2bce684c5d88d024d6b4bf44841f4cf41fe0d90638139c3a8c61ffc9bd31cb891a37462381692ad665c012d93b733d6ca3544aa2cfd17d6f74a3679f5fb598732904f7657c313ace9f23dba5cfc0c06112b6b1c9d720c81403fccbb16902e8952c3fda93230428cb8cd4b5a60def33d02c34fa1f01420b58a12a58ed0f2c9f32fd719eaee7df5b395182c558c5dac152a9ed0d0554f19d88b2dbb5e2eea8d9e8af2d221305f105442e1ee4b34152d2345734ee8e700fb30f3cdbab2ada6400f907b428a09b7b86418d2d3cb31171fb395f5b848130cca798e8c46acab4848595ddbff2a0c54667be6c49cd8634b7881034d26e3e7995575033f6785dfb44c9f4e9c2898939800b716001a964aa956560a955148e4173def113bb25c04e4009c1f07b49c9b434b10a7b242c6a52245cc2033226bd911da2ae440236ddeed77cb188657e8cb464f66b5fe76eafe37ae894da4498f034d6dbd749056453a450faaccb80064704d7b1c389320a5ba584a6f24ab3e86716969dfd6707b67b6eabfe2a2ca77616e11e2fe05e35ef925c71f3d349d5dcd7aeee5cac0f1d45b77f72a4246c32884f6432d010d9b3d52333629ff49bc8e55f96a631e10e84815ea31a77b0442c5dcdd4ca47844099631735e757f5fb4991e680239217d732886357bc63d0480816d26989d6eb264c3d016c0714abf5160517448795fd56c1a4789f5e699360abe27d37174882faab4a62cce4fca2872d7fdbbcd7a7a29bf4c9ed5532f024bf1c0636e37517b715bc631f784d283c650c778801ba8b9d34a1b3ed89aed517a56b45166926e1412ea2b38d7861465f28a6e4fc32c402069dfc750c30de29b392c2fdf043e649c2b428bc162537d58aec9a4c0f3921890c01edeacf4f1e65b9f54d148bafc7cdd8338042daed3acd387701a9c824fb04a6ac587e30f846f0de1ba1c59aa2233acc7164536ee79913c2925af85b3a730d407214d81581545aba1025f73418b6492a5c2aea0c07755695e776efe1f46f6c6d0dcdbd0e4fc6cecade3395917b5b280551e39e62f6a8058f39aca927e347d7f9af7dd344d0f77e01118e43583dda1efc749c405ac0b4e4016df13f62883f8387962fd720f3afad76e6d43593d2c2bb1f547d2c98d4eba9e76933c9b90de774c5508de9389940a5dcb79a1b3890b39214087ee77ca3a9af2762c2f1de440ae057b4c036f967e6ea562c8cb9294f8dc9c8b1fa549c8fcca5714d2f46cbce23d012a235597d1055befb6528d8d096369611c11c39a16e698ad5c81b0264698bd838ddd299402370c9954b90a2c15c9cb4076a9a1715bbb9078199b9d2272398c8090cbcd9c6f0d574185670ed99402bcba31574b646bdb495c60a071f3497e5e7c98c013a6e371c5697663852c5caad40638679cb24dcd54e923169b5cb4e2b13b66b72b2baf0991b0fda97bacbce3010ca68e27b169933d46f8d9e10201d4b500c93e946c5671efe9699b2af929f85acde488f1e49dc575f4487d45cc2581c1b4a683b8f9d3a544eaefb73318175db0828ae8bdbcfc78fb8defa48533ecab681ad8a768769f43045ff83b942be006d8bbe95199d1d1b9d7ee14e2623a1e50b60960e94712c164e0a9bc85a3af430633a6722819720913ba6627f5a3fa3dbcea2f2bc0e7ffc9ac44561cf5a24acd7673295ee542ecd9f4c7b9fe5c65ae0d6bb5a8dff9f85d73c197597d7a80c94606c2af76ac991ae9d7c7ed363b80569d378d30b082ba881f18ac081a13743a02283b86eea640e3a03a14bc066dfccbf681d1f3b9cbeb16556c6717a08f892fbf1034db622ae1908cf4dec8574f88135fd9db6c971a6da8d256dacea7b0b6bb4568dbad839ac20ef3f7c463ed786eb54efcbde2fabaf8ca33b1594e474efaf0ffc55d3a79132430aa0edfdf8115a321389ee95a042a1d7c6ae9fe1cb36dd6e8a91ee243c0eedb3d03ea9af87164d77ecd450ef15a92478f76dae2bcb63f98848cd6c2c396df273b0c4431c3fc2d18cd3ce1a420d7417ea3ec13a7a017b87b89c8d310cde0682146271e55b402d7a31832d0cc22fb0f4c9fb0ddd7875734a40769d7ac128f1524021249040f890e35b11e71c0701f88cd818717482c1f4817f32169723376b17977f3be58188121939150421e06ec9a7062e0866be5a5d43e1e2c29bcf585941b5c729ab935a11f4d15e645aee9e13857e2241c9f11c067e7cf7cd2703a9de6ca884d3993bd4886249fcf94a1b03abc9a0113053bcc50e91d290dbf67b9462299f190bdf1c6ccf17b4ca0f7e9afbba143ac3e18ef44d5f1df68d9e2828c73b45554b837ff563364f8ac072e603a19f261fc8e6c08e40800d0a01b6ffa947673f7cf0936cb48692f429c9a241d322bcf94a1cd58af54d607a18e9a3547ac0d451d09d1415bdd5e92d63765a0196e7f2beec9883e9e2603cb83edad73a43cc283742e66bc0798d47c04fe9412d25c2a8c5b9b26f19e08a76bd3edf1264966b74dad7010818eec13919406c76d4ec09a625f516c8882cc81b63128b97e772e343d0b247a041aac8bb85c40981935076dfd727ab99e48c6251f1b4d46f92208e168c94019ca28ab84102988d4f1434ab3151c313221bb1c21055492e60a5adb3b0f28a3c7a7c580551202bfb2596bacb8c4302895d58ec5b6df5a6b2db4b4b449681329539201080b070c460b3b4332a53aa71fa9ce09b01554277b17bedb40d10b3f413c5a18b0d3ff953375c22376fa7b6b3e818fafcd27f061b852fee931f817fc07c1ef7fe14a993ab9705ddc13f8a4ef4e4f6fbdb4a7a7157faa2b40ac63c5d3ef5e81755c57a53a4464b3bb290ea57f5dd7a5514a5da8a30bd115e0498612715d1487e6501c4a04a5171e6b28114d290e2582ead02794886e8ae3c27571e54c6d2e1e5c71f2a9e1ab51356d16d6c8c015a7beae5e51776cdd476a310cff512896be764c97de210d613984556c67eecc9db94343c6c5f7ceacd55a14ea3fdbc92e709da91a0af0b0ea50227c5838cb0fb6896118864d6c661ff7181eadf7acef5efe1d6f77c7ef251e559ef5610d7b96eca6f5de8f19f6e6ac5847f7dff7d7de611d9695618d6157be2c1f64d0c4e6e7fd8d292f0cbb1ec3acebba2886cd39b19f13bbe89cd8959db735f7ddebbddbbf75f6d773b7ffcaee85bf8ff1c6baf72ef7dfedbeebcdee86fa598c639746cd2a9e7876dd45c9366cc5f46df494a66283dab0714bef7957e6cd5eec8e256a5da156e69057b181d5a652b7ef473f4a4dfbd2c546587bebc312a8621e337ff4c33a1c67de67f52fdc536a747457dc53b6ae72f537ac560c6b91b75ab2be6b84442ae5faa24b7aec7a8f893ec6ad8e87d1977e76343c5c3dacb57eccdc2ece5eecb7abfd8671dec57e87d1974497b46db77e7718feae91ad6a2f3b1cb418dfb63b96f2f625ef560fb7275dce535b090892ef33f02292ec651744e7fadf3522fa7a6948ef72a816ff4284ada7fa454d78ac2394c8dcf12199523c733aeefbb1b19b39a7d7ea87614f573cf6a251f4980b17c0cfbe4596b578d3bb30652edef42e9832d367efc2f42d4c0f5e29f1c8c362b162c56ba7d38a9187459fe613d691e193d38e4e21a6d3094f21a29a0ceba0d434854c0cebee98fe9ae434c162b09c2639394dda66c57727d3f652575c99473f56fb232a679fb31b4fcdcf7e7bc94ea6ab2fd3a97ef631b33e8eff5e4ed17bf62b6ee752f638441885da5e5e64ec1f857aad6f66add67adf643ffecbb5c1c35aabb528d4ff587372727272784e6ea282dd6a5141af4c2b3ed419bb9087d485b01609919ea5cb9e85e5b33f5d29531966b9a58bb1a7da578a756c9695ab0e91773a508dd35ea46118765117a25cddeacb4c4dcba5702a2e850be9b8144ec5a570211d97c2a578992e3d666afee4f05837d18642d9d93a33355ffbcd4a989e376caf89aeefbc6f51adaf75376c5fbfc773dfbd9cefbdd4be6217fe34ed658f2bdd44e56da250d662376c8f7dcdb2ac62ee5651045cc8a5b03e1af23ccff35efa10446f481f6ee0f06e78f2060eef7a466e5eb4bf5d377beecadc5d23f6ab65850ecd2a2ed4d9078cc343cf0bbaa1735bdc6f2f5f1b69ae78945fadcfae8547955f0debb0f07f864755d3bf71a36be0b08eecbbefaeb126bbd132e88d96512ba67d8cdf50ffbadccc62a611fbd873b773955cbd01bbd757eeaf3c25674ba81b9d77377472d6156a650e79952c947153f1f0fbbef0d857f6d848fee57d7dd215d5bfaa763faf71d0813dcd1bd6918d54870de3b126c31d03accb92a816b496422076a04a7904a2d434c69be79b0118dd7a4af3f494a692d35368f3f494a692d3539a27c6e987d96bf87a22d8f9b57e53e929419f7f425b73d26933a0d6afd8e94f21b5562153a7a351311ad8d4916f55aae3abfad49a42ae2be8aa780ad97eeb526075606fe5f9500cc92bb27402858d95395f552c6dfdca91ae14b59b65d53b1cbaeaa9faa28bf254fdeadb6bfad12dfd774f2fba2ca1e9256bcfd2239f64edab76577ce9827fbaa867b92c368bd36e7d7b57fce882ff5dd48b2e8b97dd0d1de9b608b25881aaae5def966ef594f62c17e529ed4da7bf217b6d7a46481fe3dd9feef72c5754b2e122e9495756d58c171b3cb4fe9eea0d5f42eabd5e76d785bb1ace3b12588ee3aa58cad1e76edcb01ce70367378eb3df5b6edb3c0f8f32e847d593464f227d292ce1917b121e559d8747f8c68d1b374a7d3931e79ca3e95d231642a0cea8b3a08e86fd65fdf439fa917b165cf55854e15a7bb008b008b008b008ac033fe79c9d3549575743a3759eb5df755e37124d37953ecebee6262ff9e724ecb23e3ae2bafecfd37e0a9942a69029040a77f78d5e94d2efb8cbeaeee6baf720dcfbd5a1711ec771bf65ed35f7319e8ddb97f230cfb6d92f7b1fc287f0217c88ce3121e69cb3b330cb12592f2a6157e98e36d7d0884a2251e9636649fbeeead84a2391e84ba5d2775a271a75255357d2b0cb7ace492311374da5e79c34d25e84fd65fdec91e8f338131e4b4ef2918bdcfae71dae1ee2f1a516a9456a11d993d180ce39a78e117bfa3f2cccdab72cce7aced6d235b3d7ec1d6daea1e12cc7d98f99561b5d1d9bed38eead1dd91f69cd8d18b03da7711f3747ff7dd8393c02c182e68b9ba4d1f6a3c8729df6f3b7db4a5a8696553c5a43dcdd45de595554431324e2beec45df8bb8d12644bdf43ce846ba3c926c9d64947d4fe9293d450beeeed4fb5a43b34995ecf9542dea9a2dede826eaeebd6e6969c9b62f20b2743a5522a79aa4d4273cd6e4d3772552aa479cea915ae4542a9dbe964aa78f99a7d3c9542afde9747a5b427d8c9b4e2816564ffe2e0b75faef2b5d174b372a145d6fa14e2ca6121e512429c2a3cae2fa1fae456a915aa416a945648f06e46b68268e3575587e9c3933c7d6d0605644b2a4b756644da21f4bdfd8ea478d488fc9a6d853162daba3cbf4d991c8623f854c2153c814328598ba1ed1416d857695166aa19e52ba4adfe8fad2e84bdef7fd4812623995462716db493c534fe9293da5a7f4949163506c0d4d27e926562b71fab197f492565243233b09a993643f7e23d18f358faeff4c3fda90bf1f8df0a8cadfd34f74c2168ba9446a25ada495b4128b39c1e27edb30ac86067b12e9411dec4947e8f891b12691bedbc2ea63d645d2eaf55b66758f996a6848df833aa48f99248cf4a6abc3235912097b13e94d98e963dc923ec6dffe58fa0f7b169975bd65fafefb302db3584c2512b6bdf51ed6711b1eb5ac5e1858c485c1225a86e53d9811be1a9a91f4a14a5fa3d175f9f0492aa3b63fcaa0a01b3726e94777b435349ff4e1935548a31169f4a4cbf33e19f4a3ea59de449ff42c7f7ad4951e9ef0f83d8b156193e595da7e1ebe71a3655c376edc3035a633bfaeebba2feb3a55d7d05de38c5c7f9cd1d2d232baefa6fdbeafb3578727fa3e6bbbafe4fdf58944789c913beaf42361d18f44dfb778b7a565742d2d2d35a2be80e08d4674348978a311f5e69091a88e467f8d3e668eee38bfaba31bd9d1e81bfd777df61b95ec8f246a62f97e8b3ab1f4554b789440cd48c2de1432854c2153c814426b448e3d21496e1064e9e4083b793ee82f33f6f27fdbfabaaeef3dc847ffea18b3b764f67df4c2fea2d8cb6e01d66b347363cdf59767ad7d0f627d888e4e64edf5def7ded5d67e8c8bfabbcec3238f697fe4314522ec51cfc3230fdb9146dad3efae0fc1591db6ace111977c889651f2b02d0ef3a159ed24590d4dd649ba0986619da4f6926adf62ef89de72189e5d873117618b8d80e05a4989c3b2506f3c48adf5c683d4ac560f526badb53e8f4ef4e366472edf5fa96bbb5ee451cb4d1fc287a0f4473b3fabf3b3ffec2b910c9359764939e59435c926e56759dd21528f90f231ec7fac47268661daff7569ffc25fd9cf6ceb798c914c231bca0e8fa2d247328d6c97b18c300c63c1be34c25e6431d1db7f6c546241b168f1efc207e01f00ff31fcbfb8ff30dc7f7cffef9532f5787cc105ec028f2f2d308847150bbc028f210a9ff0f82cd8844ba41126b29fd7593db9c4bde01d15dad50eeababb83bab6b275dd5f5750d7752dd4759d8575d68fee7d57b78ab579bdcd26a7dd4e522fdc534a5ce7d80fcd542a952aa333cbb22c0b3858cf9096613dd9bf621850fdb8772a0e0c2f4eb906e58c2728fb63dd0da69cf14061fbc78c87c542616330511e09e342cbbc39b169032f3295410816acd7882ae5133718419676d63a5a1845b0a3f49199991788fdceb27c22073ac8b3b12c9990024fbbbbbf9043d40877e13c2561789022100c60c0ce94fccb27d3dfa89458b0ee65f9ddede0e2cf14456dced103a0668a7e8c8f94602f1c5fd197b1315da06065244c67fa3bf0d02e15e8147d4d6b17d9a9713492725c71292063f490315a78489fa22e9f09834dbed8e4e4662a0456ce54aa7a20b0de3e01ecadeb670264aeabbaee2eb060bdb1023cf48721841dbb0132e59f841b26648f828e0d19c3815c0fd8bec16de3a10c55b48d6be1099b7c214a2412684b69f0811de5131759c35a14eac3f089dcc1c3b1eef8aaee9e3936144437d5344cf343d46cc878488390751e3051c335666a3e0d50b0955a52d8e48be5726fd657d7a9fc6abd4e978e45549fcbfdd8f3204414e9e25cee16509ff3d043fad708fb32459f4a2a25fd279428226754f6d8d05eb210f12004151eca5438d2e45a31ca432ac4ccc29271881f2764200a233af0849a22aeaca0063727200213461045d684ec5cf61953c39dc80f56e0408a2a8d7a77ccdd9faba18938d95470a54e9bccdd515f03298664428022fb4bff4a85d64025022a3b70420a2e20048abff4af338630b142124d90b284921a1c0635647fe95ffd5153babe63ee4db23bf516c47858c31f0797010544488282215670425480952024410a251cc10344447f1925bb37398194ec2cd9a992ecee58f5c73d3109a8a1005f8d9ffdbd053a88a20877275480f2294b1d4471816cb3d4411425c8232a8f28ccaf2768b063258a0e3690852a60c0bf5a2218c10a38508acc400949a612ae60831a08e1491349f84bff4a831386f4c002527652d033033984234ea640450533c0c204a87cc008422cb0842d7cc0ab0c3204aa00240a124c5901139888e3fb57d761842296a0b3030b746003a27b538941cd06a01841859f2638018324a2a4c1146ca258810a186840c8cde6990725650648b84205144b041125144f34d0840c8e38e1048b2532909a029020d1af9438b0b24448f40b2a11e709a2b0c00e15a0b0a181018a2b5441fcb17ac1169e80018af8e3540471628b4a01c5055c20fe08a30871c245040543b002c6cd7b3911a209544420fa955718810ac4ad727ee90a6a20ce4b7319e181186441fc31a3080115a274028c765e1936441c11ddfa40901516883f4225a0608214e7a54909c9a006e28fbe4142e49a9872a5e3e615c0d0077c3c6ede141443e011fd4a71fbb8797b054010018fe8170148889be5e685e161018e28094d8e0c897e5542e2369a4264904304f1c796030b20f183f4e35132c0c447f40b0314712b71f3d2502be0407102f14748832b3e2a2131032e4e4c9d003dda4116763084e8575a4104e2c6c2cd3b8b48c1c647f42b738044dc6ac52eebc4a29fb0840d8444bf310c117f7853276c72ba4c67a20851e088349a124cb0117fc020710429349814f147c905e2c4d209c0440c7e8e88dbe4e6955150208423ca1e08d98290d81dd8800a7a44bfaa24e27659179dded2bab27e230cc1e446f4db6203e20fef6ec17c7007d461210416d1236588af4620ac972d7377ad41c743ea3f68645a838e653d66fdc4b2655c4f6f0d39997e0d381e52ab06225a06c513d760ed4adcabc8bf550cd690b93e0e32575c93b15993698dd50d85943f2dfcc5ccc9c971e99edb327f0e8fa81e2dfba14a8c50023c351f0815e59f207b7272727272729e60fd25add712361cd429ae47ea114a2f6a51eb713ca93ed6932718fd51fa5c14ff703f5f2e1516d6cf365aeb86476b51ff148f2fb5482592e97707cd26b44d2d528b34c1fa4b5abb460982a1b261fdac56948b9d296feb9aa1eac6cccfead090452714a4e3608e83391edb7fab8e471e37e14ccda7d7093e84132c3a31b77cb0c917dbf4562de92599f519a697124a954cd50c4fcd2e0ad64b4a5a4914acbfa4d5e7084116b7753675a60e67c96d7bcba94c211e3485c8fc70db8da1af5df9a4ca145225c852b9bc333cc466507a518b5a957acbf0b7ee85c75665da564bcba0f8059ea516dbd2d232030ad65fd2aa6ac2265f58d943e8f2cbddb25eb3f07861cde31ee3217dfada55cd147dbf33d509d94a694fe90174b73be6986342567ae8d86551cb2d1a6cd9330c078eacde50cdf0c2b8dd14e18d296415cb7b7cd5dec302773c6c4b761e5f8c12b35808616bc7c3f916913c2340b3a3b20b4d065e048be801f44f1a2cc1225a46c7cc20e68f7c8d351613ac7fae168f5503ae6435aa83ceb0205895ca47e5a3f259a2ca2a5f6136c424a086d74afe45b8ea42a9031f6a2951e2097bfdf8e112db3233367ce868d74a7ac9a892990152924476ad6402c1f99086826dd32c0d7034fb010eac8eac25c37eb084d591b5645ccfe832dca2699a1318d5341c50ebc5894dd396d834aab5ccd85a549ad6d22233261b17bad86a61497bac4dc7ea21d85106e1080ab29058659afdaddb66566659aaefce7851f98a7e2fb2c7ca4c2dcb07f6fa51f58284c5f1430e2076f8eafbf93c7af8ead3722ccdca81e3872adaf74d2196e5759a14b28e5a3f4d3467fdd880fb8a6dda73cd5dae6f781e0f3caf096a492a3ca89ed7d9c056cffb1b2da3caa01b5502498fc3be8e16beb1550cd30c0aaa0fbba9d3edd8de2a6725c55640b11da06c0a6cafb4c7368b6edbfb6a7c41cdd62c0fc22ccbba964359995f5436f9a2c5f859725bbd84e0a435ec1242125647c5324fbb2e0f7bec6a1fe2eaacce92f472725d9e733db156bdb2aec392a857d6753e44cbc81ce7ca2e09c587a89ddf7417d6b096e1b14aa086fe6771ae99603e0eecc2f0ac340732639a85e1da978b6283b02b737703aba3be1544c557425da587acf8ea8a76ad979d86555c8bccdeeace8eedf2e8b183470f1f8ae58062583d02f3699b1ebeba81ed7eebb120becab69cad166919593d82655807e5d9403a482dd232dadaea0e8bb6d97afa499eeda3d10bfbb699b55a8b42fd872136315c8bb40cb7e17436f962c482f27cef6922bfc5a655ae32d119664b44b0a30b4d17ebc84ccdafd5855c8889d5a1bd9547eb48b68ef80a3bc2571b8fafba24beca96584af8aabb975db6b110d2305804f61f07f230c87f1cc8575c90a4161332530b2402c5857e3c88896d21afe2436ec5af3815d3062c02dcf1d5681dc9425ec5ad8045b010722117da7eb42cb0081655b0a0ed023cf966e984084182f0c3c9efe5b790878d1b53acbbc6927a93deb2df26e75de3da01c1d68a9a5da7c4e7ed5f84beea7e3e0cd58c173cd6642fbbad352bebc770ccb430774b1785f2954b6a754da865f5773db0d7771e650fca57d9cfef94b0edab7e4b7bcafdc4161e6b6ac4cc4f80af3acfee5e761aa60cb07210b38a2ab2b55d3953978f3cdcc29b6b50f8beeffb3ceffbbeeff36c70e3924118a5524a2a3fcb42f308d14ed242ae536bf59aa7795aa856a1eb9a3f956821da533e4f443b1cbaf7be5e0d97aee9b6e8966226aa74555923bde88ede74495fbaa316ebad73d6176b45d5ef9b1f33bf7f11c26821212121ceb72b6c488c6dd341b0bee91c5f555c7fd69f3a351948a5ddc7784e4e4ece2867341a8d724c2552d7c77a241a8d46a3910b0b0bc631c2230a8ff631131eeb09b360d2736fafcc9e9a3f33edb71719c3e17bd1dbdb5966da276d1a07d5f145bebe6dae9fd7f59788fbcbbee87e72acf34318d755cdf0b07aaadf92fcebc12567b49c41a545d532e352b5cca0542a66c9255a66b478dfcfaf7e9deb36392f7bed3fcdfb6e46cb6731e0b1a2509f09c59fcc1ed38fb3a74da7d9235d33610f7e8e35ec82c964d23493f62e5c543699344d33cd2cfad14da22ac2a6d39baa757afad764729a4c66cf6492d16ed2ef8ec9ec99432b98cc254acc254c7a86e612263d7389127309939e212673099315b76d662a14ab45fdcd0b8eadfb444197e984639b530ce7cdcdcdcd8aef9b1558ce94ec39e79cb387490f931e263d4c7a98b0b8a9083d2d3d2d3d2d3d2d5f774fcbf7bdecfafb66f7d6f774b2470ec1783fce68b12ea6b25a627c65759ff52d96d5f359aa9618abe5fb64c682e8dcdd50bfdfaa1f6645a13e138a3fe92c5538c6bef573fee83fb1aeff75af8d33f2e8fd7cfadbb8fdfcefcec896c558ab5887c48f692ddf626d2db3add73c778743f6d877f7fa0d933130e9ee8d3faeb7707dec6a35ab32464f4b4f4b4f4b4f4b4f8bd71c934dbea8d611f4c824e2012577b1039f3c493776b48e583abeca466b489ed6916c84843922613697ef5fe4fa998647ef31000162c4905f842a1765cfb27b77b99bfdd6d55fbb41746e1c0402be5f76de2d964401fb8ae17ab5a05dbc53f3356d34addb32c63cc2c36935b68ec85085d57c5eb039143b86399430ed2243a7fc3dcc3ecafe52862919c369f5d0c7b087073d02999a2f1b0579764aa6e68beacfd946f2943012e8dc3e2f110f1788e2a1cc3300b1f3df4553d84bc757a3fcb1217f6c4898cef31b206b36ca9f2c444b97b640a7e64f1407f2942d24cf1f4579ce4085bd76b2d0351460c3431f5764e92da5bb548bea3cd33fc8c0e69801886d39e424102dc18ed582e2ab118674e9b2e64afdc543577938ef106bf10c1bb07cc0e63c942f654abe548dd666d9c494dc58aaa44f0c28bd3488b5e88b0aca08d0dc4432c53c684dae992d059f2ce3a5fccd5fa66b50dd1f34ec80093ea20c969d2122322e126f9f7f613e3a432c5f62611d430a6c120be365474386971d8d193ec6fd67dbdee774ff20be2aaad842a6ef39f44f1e7f1cc90fd0617d8c18bf5977ec9fbcc3579e9b65080a421bed82e82a4477d5cd9490b344c421c64f6cad5faf925d5ac92ea9001f48f60e72cac95e7222eedfa49f6428d9a3646fa01f8bcb5ecadea24545c38faa2ce3c7972948dcbd5a1758ef96992e319e9a4f5f0057a6c21b800b801bc36d71150003bed7c6f5c1c3f92f5c1c2e5a5c203c9c0fde1d978787f3575cd46d9bdb40e69b6ee37838bf349f347f747b8887f345b7ed6d227d44f374925ee2a1121ece6ed24f1acafc96fb9e9a4fc355796abe4ac60d9e613cbd303ec6c5e00c303038c3d73cc30c327c15a897e5134a64215b3a1e0ef18ad8cfa3f338cf74a93cb9fa95b23efcfe1f7c15806f007c7f0f7197017cc7f0fd7dc4572fbebf8ff0150cdff8fb3b89afeefff7b712be7ae1fbbbc7572e7c7f37f1958befef27be6af1fd0dc557e0378befef28be5af1fdfd03e4abd3f7b37c9bbe4b557c459a2ede148ef0b1b1b1e9018ed8d26708231ab011fbddc657a3ef7720be127ddbeff720befadefb7ecff155f7fdaee32beefb7d88eff88a88af8e60dff2fd3da60b0ddf740a121bb15fc6cffe2dbc3207e0caacba526cb9529471a548c395620c57e617576618aeccf8ca7cafcc7f657ee1caecc295b9c595199479c595192573eacaece27a1e75b69e4f57e64ce64d66128be7ee76f6aea492bf7b6500dc9a07900a2f0e0ffb03707ff0b01f00b707703b88a7fa63b89dd330dc1ee261ef7848c4c3231e1ee161bf0bd7c56d711fbcad048bdb3d2b50b7a178d87fbaede3613fcbed1f0f8148b7a9886e57b96de5bb7d850ab77120d76f3c0ce2389e735de7e698a97e1a6e8f99ea575d1957ce94cff8e0e1e7b6c9b4b6080a9de04093dc6ec35c523b1e279e04218faf052b798491dbf3f8e2e2287b7c89af787c259de80094dcef497c4591c836c823975b763878762ca3832538c9dd7f7926124bb2636f04fb5d4d59f2c00643d9414a4d41367ed8e1ab99fb697e7263385354218f12c80ed9d8d8d88848a49327c0208f52688aa06e275480ba7198d963dc47007ea39006b87971da70a312e78fa46bb40e307fecb29430467cd370f072105d45b7838bfe21a59a91fad7cd716f2e900cbc8813cb99baa962c5363a1e6e762ce94129f52cc3b586d56c142c0f5f8d75071d424d17f0022f0cee30a7bbbf8fece74b118cc015011ad6cb540cc68e9e4c1f83e121787d0f715ef0c2323b664aac57150e553b5da6c5a18ef16cf2c5fc0b9c18dc617bec7bd0fec23eaed71ec33e90cc5cafbd0f5162d87505a143e77e1ea458bf7db6772753bf6b7b2703645e5fb10bcec73efb1eaeafd847fdeb33ecc34726c51f3ca4f643395398a85d2966578af54a11bb52bcea4cd11c188787ee34c9265f8cd60673174a7ff0f07a7ab759757048982312e6657ef3f4917cb90d7d8f881dfde9fd471974c200f7352206467fc220865958300d120c9b316e61a2e539a6cb88fb1196339563a6ae211c1e11eb0389f53e46cfbd0fd19fbe7f87d1733f33c220f62c169e1161700796a70f627886058f7e3e3853e7c95b81ada70b625895305d034b1ebd2c5fb68baa53971c9d5e8a1b9ee19e8645f452cc6e6d4ebb9eb38b65d9d10065462ff30ccbc7b8ecb6a7a1cffd866746cf8269e68fb2263a81f57388dc052b9691e22d81b521e3ef2baca3b139d71d76ad067277e00e9e37d33d58473fc3fd08d320a94f635930cdf7a3af094287cef5799022cb63d735903b19968f718d05ab1a28c33d0f52e4b00f24f57dd0e7be8711f6315ff4313efa19fa230c6a3f7a29cecc3ffde87b38fd08fb4032337a96f741fad3f7c0f2a637611fa36779296257d340de46178c40103a74c69e07299a3e0234b497313d28437a900729921eec21861d183cb111473ffacc949d9e05c4bef452245d10c3e00ea4377d0fa32f611fa51fbd09fb403253fad1fb303de9a5c8722a99462491e54ad174a558ba52245d298ebee299f9a2c72e9667886e8ea12b36f992a2e84a11c72d817dc12a0faf97a109db3faab89fa1cf61b0be1467e683d74bf17f0afbaaeb477bd52b03a13fdfda6409c30faee67211148b93af03cc972d3c757de807f0bffe5152891eb293710ccad46d7be9bf43c2f4d63aa3ed7690a28de2e6fa1ddf1209f392afefb143390eab66eaaa3153d7cffb326566ea7aede2b015b352d8510ea956d75fbfe37a967c3da5225fd7f5270963fdf52c12c6e2581ad8aeef6bbc9fe1de7beb3d4c83c4c233dd7f9886a6e6c3ff1dd305b4de7b295edf63ba80160677e0fefb1ebaf7b00fefbbffb00f2433de7718a41d95a2d8f4e3ae4b5ea8be2c3cdacbea9fe13e7b17b4dec7f60e36de613ef733f3b9b7de07120bcfcce7de07128a67367f1fdc4bbe5e3cbc2d7a29b0d64bc6c144f0f2ed74e1bcef0beee0ef3d48f18cbf076e7f41fafd18a811ed7717a4ef8f811a91a32ffa1a8ac11db8ef7e86fbeee977980609c533f62d06351a7f8efb1aee7e1e55a036dfc67401350ceee03df73d7cdf611fd807920dcff47fdffbf0f730a8bd14bda7dae7f9a87ca80f7765d39001e2df7f9d6028d377a18414e97727836150c67a7026861d18e8a00af13282bd8dc28ed2e79257b0df6c1436c7749199fea723615e727d4a5133458dd4975d0f2e566c672a9c29fad7b4aeb5556ccff8aa9f850764e7fb3734aba0bdf4dfbe877e0dfbe07c2099f121516f41f9dbd788f3734c1750627087c63448249ee9df300df7bd3d4dbf6390be14fd6bc46a5d39533f857d999adf63e1277420852c31289f4767d0fa99edfdafef0ccaf78184be0fffed7d20b9f08cf6fd3e365062d0c23bf86f3fe3bf61395314cff46bef43c3aae99dc08eaa7c0175eebc49f963b6422553fd624b17eba53866594ad94fe5ca9bcecebcf1478743eb90653a813cbfbb1bb69c43a6fd968d8cd14b4c9c9f4112c6f4ed99b29430fefd321286f4fdbd236146dfdf431246f4fdde2361ecb7258990e75f45f27caca990e7d7f95908f27c8dc680744b3fbaa4df4477f49bbda2dfbe6b7f33ddef37cb6fe94dd7b32eac66dac6d1375def3bcbebc7da8208de058b789705152ae7dd15da8fdec4bba810701ce9c4fd38a7789725773fd21898be66d3972ee9cac818fda3db3b433d1347c6e8f7ee0c92313a05b9a3909d89dc20c89d84dc6ff5531e19c326cbc146ad0bab59a53cd685556ce2045dd885ed0c79cfc4a1d665518b0ed1b913e4eeeed306b57b8cd7ae46368b042b5fbe7c8b04dbffc97eea02202020202020202020202020202020202020202020202020202020202020202020202020d9b9096c97a620458a142952a4489122458a142952a4489122458a142952a4489122458a142952a4489122458a1429f3e5a42620ed935a1756336de33aefb3a211a9646239a156b0005bb0b04105d6b209dab1a13c208b15a8138bbfc9850b2fb498383b964f108ba9441a895e78d07b76268ec87ede4ef6effeb2689c6ba8e3b66b28fb6b17af08d345ce94f5ae694ae625030d64a881ed97b9c60557a41072078388fdc633544e63e500be645087cedc77e6b00d2f1acef00ea8c88efb8adb503184ed91c7e5d7fb3fbda78c5de9031f6c21ffc8964d026816834c3fc6fba78fd498a918f1a7e8b1ee0e1e3d96b0a33ff1950e194f9efe3070ff3c7123309ec2781eee02e3e7ef8011c4396ea09e1d2a8e1c39c27363c387237a8efcdc4c97069a1d8dfef170feff98a8e8f11f1985f5271246cbf34538120626cfdfdcdd9f4c17c94490fb0059c982509e3754e4e94fdc7dc8af4c9bf625798ebdc457208e1f3c9c9fc40df2c84d7f32fd499edd8f83e7ae3f47b20bb97f648cf9344401846828e5e2678be4edc0eab8de3ee9fb4716f5c8197e72abd55a8a75481e5c7475c8b7b445a24fc7f5f2492fbb1d7858593ee94d4174d621df4adb9d65c626d2531a161c7dcd234a2f38925752d3ddbab4034b3dfb11d3bc973fb3dcb897ef12a6c31bbda3abf28025fd68f34b94af748c5efe88f4d6f7148f9fa9bce3936468fdd8224b961572655fa2e4cf2c3b1c4acf72e79faebfbdd3deeb49960c2d1e579c6e6529bd09df30b308a3640c3a7a7a47ffddd1972e900cbca865f5bd47128108f0b0b97b1ea88c3d8f9ab9df40f9328bfebae0f5f6651e5d507ecdf2b7efa505ca97a2ebded060109de94dc2a23cb4fec35606296cbf279fbbdbd5e467320626ff73cd09a5f4433f00b52aa5f4ca324a29cd9e524a2f3a31aa6958e52f32f4b1ab09aa1d43b6d2299b7c31ce9e4cb7967dd33a1749ff901d0f298deb1b8804d488f97e91139b638787de12f685bc7d75f9191ce109228e8001a12988f4af1b433f1be2ab1d2434f3b3b77eda4c970e4f9b99cd9b4c318d7f867d68a6e8d35857beafd0aff54a9527339563a6e8a3504256a8626fe2a10ce8cf00c43ea594d26fd13b819d5f2b751f297ad737f522a1e13efe33531e94e97b4acc2e46c6a0bf45f11ff7e9711f4c8a413ca4ef3f9029fa45089390e98faa4c9d0914c3ee236350995f0adb387de3219dc10576f650d7acd093fbca7e4fa85105c84d43ca45145676f35b60e5469fd20f67c428a09bc827d287fafc727fbfed2ff59c737e12667e4ff931ee220956ce107f8f953ffed743832c8f2f5901646a3e0c27b6a36881ed0db4f04e8c026cf0c0024b60c003219cc8a3f43932b139277595afc600e4f9930742ec6424f264d21e0009e33f3d0c62fb5d24c1f6dbe0b4c705b66d8879f1b2e31719e389f56f984ecdff014a3a219b7881a10a3d0c33251bf71587d3d3df5a5c9a7e8207b2f023b6a06927468af488342ebec5c31882cda145ae5f93fd89af5840f1d5e6e3517ce57ddf60a0e549b09287f5dd66ba740c7ab020d87b8e0ef6bee344b0f723d2c914a864ec9dc712b1b7b2eae3abb1a9c83fd8631886bd6827ec2874092a55ac60df54f80aac9c686747b423dac998a8d65a5ddc1f9d03851243a28b073fc65d5c242c3e5b9655bffbeddbb33a1adb6f1bb771d5fbeeca78a8fdf43c0dbccde2f6f0105b81693c8cc28e6e03c3434cc2dcd827120626d3d084752036bed21e7bbff11516622f5cc7874898eb31ebb36b5ded7198be8e40c82018beda30fdf9d8e8417858192ca12ef8bdcc1e98c3bbe982a5ef5cc26e3303893b2264d1d6e2ca1e4ca1c81531f455fd16d785114b1c11351adbb7b8f2670849f08861a783f5d2c33bf6c87566b03ecdf5b70b56991dc88fdb60492cd91ed4a1fe90ebcb2cbb0c886718f6d708eb37d86d3cc4665881adffc25715b78e8718d7a26f660aeb1bae7e17d5c243ec5f0bf64794dbc814f61d1e2b9661cc895cf168b1972f30ac43d4637291132cb2d024e0f999560c2db0a3fc59400c16b08d258f89c3c3f6c186874046301ed2ef03e44fd6217fe2f1080d3d7674409e78c69c2e158acc4c4905f8b5feba406c735e540e6188523134e7bc68f3d4f05563394a28756234a49449b0eb58b5714f601df712896730c28e337860c4c0c4dae87e95af5ae47e18be8a0107b63fb4622577c700f9d2075bc261c3eee0a28dce393c943f40e4cf0bf9d345e000a20710292891bbdba9c83d65949806512d6c681b3c3edf930d85f298d88b3e786135a60b0c1f191f72f0f0957462e5883ce7bcaaf8c42c8a9a6095996b31bf624ae9e34033bd8195d93b1c3a77d34031b1fef3caa00e7d7dbfcc9bb7377d3a453c586095a513290ce58bfebd4658195c638615d89287610d999a5feb689f50dc38d5071cb0e34b0d5f8dff07c8de3f2601be1a7958d9c8cd8bd9f7a7fc1bbfd0c0cadcff82ad97f9325fa64bebaca4cc961329bc4c218baec619fe335ef8cb4bf616beeabf80b0c917a34832b141962fb66035507ea65d0cd488db775fe3fdccf6decbf7b8ef304de634da6f18f41a914e51fb4c5a2bdc6f76c8c32b26b0f47d7c244c14dbdf3fd385fbcebe5f6a28eea266ea09ed81cd5eb4071d1be146d4142f5eb9ede6b8413eb20765b4d7308d8f1f9890b8fd14b57f861b4b1f1312a5b8611b4c48d430d8436c9b99fa296de139b37c22892764d93edd3353bd64a6c6f6ef287dd341faa77d3c1c3b1bc9be77322c67cace9d8ef10b64c7bdbd7da64b17d163636323c490afacdcdf5400e55e0224b75bc97dc56dfc26f7d32997f86aec21b9bfc1c6e91c0fbbedd074699f2a87f088b243b925ce3c00017a114e754b820d94995f23d2972fda861258d05f8af441ff1641194ab19ca916e7fc162796339ca99e4fff0556fe18c6cc97ee219ff66e1c1c6c600e123472f3e2f7463efc83fe8848fc6b72635087fed958e69a9a0ce670e5f932eb98d87b99bbcf56dea4b74bea8137d03c7f5e0cc8fc04132a88498013567e026ecd66588105b551f6d892edf944124de4896ae16163f8afc73dbbc7734a45fdecbdb33c22673a58cfd55ab96d7beeb31b5e18b57e9914dcdd9fc6ca0b10e953af7cd98baf460925fbfbf70df94a6583027c05da92953edddd313421613cfbcb4c171a39842124b040fcc1f9c00234b0315d684025258c047bcfa17bfbdfcdbe554efa445f3886303cf46f68a6fc459684a78dc2ca48982ffbcfc07c3ff3d9c032df500ed94f3087ecb1cfb20be630aba8a28a2a72cb2aaac8fd19023298c3f7d97f5f93bf21111eadc52a1919c345de73ff711e6e5fa50f8fce1a7751b745103ba2328caeb2ab99f655d3c01cea675f3fab18fc1743d34546a6fc6bf56f287b29cb277a88c8ed3823efa870a568595735535ee3ee2018cafe32a0b0926b0edfe0d9b3aaf04c2b1ce12f478e7b397694fe6971f4ac6e1c1fe5508ecee9e12b50e210c97ec47bdd737e1ead8f145152cc2ac679a32c9f48220a396268c28ed647c2d0f4111088600a3a22cd8fd44f91274496ff3143084f7a209edebf974c179a17569001f107f80491e684817002f8b3e01d4e00ffbe522b104094462418997c32c2242cad94f6b18e86e51961516fbf71dce8b98e86a879248c97fd3bc97745c2c0d4bb7de8d6a7093b2d040513ca3ea70df2d5d838d6c757d2895093ec6fa3f8db1f5f492755a092fd2d90bfa5e22b3a6974cfce8aac68869eafa361593707edb9ff3a06789f434e91f560561dccf00d9e33cf2e9530d44b9470172073a84af6ef26403a9f286c203bcd2e9d340189ecdf3fd209157eb27f0b4d9716fdb36af5f0d528adf0f01558391a5fd124b2ff35a262cd635918c481660b75c119d4b33cea597e87af7678e8288be5add15bf6ad4ff6383c5415b1997f86737848bb1cead7b7be266fd6f7bd6581396c39585fdfc2a07d1e56b65f936bf2f6351967a6fcdd5422d18e861ddd1e1efa8b2ecd0dee36090b6c2fc1cd235b60312853235a1b4a60591e946161017b68f1f42d9ede7b811d1be7bd4694338523ad8569b4006260af8f993113756b44eecafcf58d405f1e9dbddf2a87ba35a2ec7a20c450bebe0112e6075798880213511cc93b7c25819022cb778f8da05e8aa7efde3b1a1dea9e6e8d99f267b9fe53f4bc86350e5f71efff83afbcf7cfc1fdc675d6cbaef3bef33c3087eeadefdec2e0f51d066d641fbee66b847deb335d9aa75acce3939d94e51338f0406e3c5a26aac093c7e6c994b34301a996a8f4cc968541eb14a21901000000003315000038140c078482d17840921435f914800e88aa486a4c96c70325c8410a2983080106184280080014ccd0264101466764bb46cd93d687284a2ef37e74a1bc1a8186a50e8474f974d1092432de9a4f38ea8fc8b22972cd1f109d04601e2b4f4543c2aed26856696e9228eecbf618d0a8caede336ca6bdb2a8a5c4ddd449163593a110e2125f8e778973242add9ea6e207ba386da2bf8dbef8b0784bfec462df371ef1a9f3d85a555a91b9b938aae01c06cbf104aafad4065785fe17e5ae3c08ddde4d43a88b83bac528bcfc67787865186d3bc69e7976439881245cbde72115edb32422b359c06d8beefa6338ee61b17d89b83bc9e20d3dc4cf7a2e1c6183c023a16238eefb858ef1d2c22191664d5c4810cfcca8dc99d932e0280d7a883dae6e817fdf7b09fa6b5a4937988ecc2946c4da956a2a19cad076dd8a0e0233d7068d770eb55353ceb6cd518b10879c09b0fd3376c21a6430db62b4d6ab47340ea47e12a6787554189f7241080a4bd8961e6833d427742a33e23d38b9a76783a8ef34cfde2aa902da4d517e131b9f790580d294ebcf75173ca27c305f0883814d1af6ea0370668f108d62f8017be91f1818ddff5c62557147299fbe60b3c5b4bc9dfdf1862c1d920c812350383c202991b05cc2baf119a9da86cab7ee0e1d2c785e85c7232f66b97880752d9a728aa106ae3549bc791b6e54d0d708e6b5167e8d6e28b6057a377f32a9cb545ab91c8ec704a953118d70404506d172d088c325e5b4eacd090cf988e6fe1c91ad2b0e8c6289a1dc9b84a2b5bcee2b4fb6e92f205fcfddc5bb697c8e6ea490fdcb69f929e76d37965b17d41e3568c9fdd1823831a30c0d1b559dee25b5a33b898f20c7ce724ae4c9e20b8aa2ff93044b2e03898a7dfdff9dc30e943542b1636daa68f04e3476e4bc690d7375f756293eb364ff1a1fb783d89ccd5672e188ecec787ea7fe859896e76cb226e79296e1b6a4590b255b663c5fa17c047ad14279dacd52c9589649003fa0e89257a3c39b0d4a01435149f26d2469ae0a56398205a314011247b05c5d1be76ae42bc582e5ba6cc20e5655edc9030e98347bcbfe08ecab323aa19214475c6431855ca887dc8f0e3fb0670b51077770981044cc9ab67bfb205ff5964c9ce43427c9f6498ad9aaa1401a3251b2519202daa6bacd3b05e6ec21f2a5e575c5babedf2138ba6e03e518c5266bc52818d0f55d9324017ebc1f63e7f74a9223a55304d369546c527cc662310a136cc746b539c44cec78425e8c2cff9fcb8a66a01957636bf60f02853af911435d18823bd3edccb108b8f9c905bef76592b75b10b59f753e7dbf1fee26565d1ce9491a537fb3032d1d24e11ee30a512422e605e268dab88d3c891d192d0d99a29cc0e9d41577e330af7a79cf242d78e081ceb395f2027c02c41b7cdb07f18a1228c196074bc84bf856e928bc0130af2ab345d7f7dfa2836c92a070868cd4d836068e84cab7a6a3784555a7234051c6fe9f7320de149381df0f328557fbe2f075846b5c15dd76e3c1072dfda4675c11e1917b605b25fddda2b04a9457385e6d610574b15044b65888466e5a508e53c4ecf1a5622366cdb91a86a4655d25435624ad5ae8f31fd0cf23d306e78f47e3bd708a8b8899a4bcadb2ce41321101c0c3b4fca486d77327cbba0a913ac893f72b06fc2b86ed3c9c6bbe14b9ea4f92d61a69c782c8797da56fdffb9fa01deb513bbc5023b2016463f0d3f43d93c63af601bb7c93ca6dfa059bc7e5a92ad0d2e56105f8c13f18233afe3f1cfe74b1489fb21c10e9f00ba8a89b07625dfc292eae5a704c4948a315456f9c8145e933692d1624e458f6880d60f18762ac0a127aa9e812d9d4b7c4551c19185e451b9b9712254d3313afb21cfbe5662cd908fdaf9fab6f8ad4bf73a867c57d934ac955f22b1888c325f956c7dc9053431f6e56f2e93f294fa5c28c13b9101c74a47533c47660e2876c8c8e88fee0541482f93646ff2a5227ed53d554a1d2ae117f3481cb343a98683c86ba650c06cb926c55633c2fd8d7ebf38428084e4824b6cd5add16ea73dd35db66524a5c7f0876cd895ae999dd1f04e977b7e745b3abba3f142210df8e7f3b370041e2f074b99ddc870ac24e3e7538c9b1fce2e1f34974d14c190635842cc4ba966355f0178626adf6de43601c7b517e69f983aded515786edaaabd7b65fb38597ce569807b2ca8c3bfe2a3dc4d11dc746758441744d548937d0a22c4cd175efd23ead706d3d4d7a06709be658602d305a0a02e5e454d661aa30162edad7ad23075c4f3ef0e0e5f530eb68ba3ad705bcf925cd8668a1d7deefdf8615579c1f09e6af8bdebadd3ad7d5363f2456ffecd0053aa3237e814b704729edb63c49cced932ab07dd53996a0e0bf6bc44ea089593291b95c924c3dfe2172812ff096c61d971cc2f2a747cea1a318830170703de5442494edd1d87bbd3db809840442fd8c695d12eb008144ac71c840ca6090775a400a498b90e4a6343236c5b79397102293f2325b747a2aac7c1e823d994a81742c2e94128039d816880a344d0e09de4765e2ad430ff0cc2f7414ef688c0a86fc466e4622b0365d00251d666e221601ca19319b065d2c4dfe9c8c360986d956f18c0e7fcbac576db690a45730934cb961635053450578b84cc4b09e0dd669b7234ec5da1bc3db6fbf68231502d5cc2cc6d8fa34f67cb1d6e04f9b0a4458a21fd9c328f8d7c39bc4f2dde2ec10c8f57d253aa746a0de1be137981a181d18066f78ef3a882061067d8cc9968d746cf9f5c85e3a0a66972890f1f4de01cc40c3693dfbd2b583eb5f44458707a02818a100a4addc7a4ae10eada19f5203f891421bc70e746950b572ba1c8fd70ff21d2de941b96c792ea40b884a24359ee3d6bd3a85020a3fd44cb21f66d790499e9d32e6323410ddcfc9898b55ab0655e06e3042981ddab814ce3741b2cfae20f6c8d4f53d0dd8ef8d3bef73d8ad0c91c20ce75c9a429a286b04901d48fe77629311032aef3b5216f60275773ed0a76dfeb2ccca3a58c4faa2463d1518f9d444477504d6b5c93af832fa9532ec6060c7b5747b315331bfe6adb2e54ed0706c9911515937b524df66d01f6bd92d412fd6ff3b888c21efd3ad196acc3732a21b1dc24aef702460b9cd25f1d450e8a5d256b9fcee90d84d07f9b222f10f9bc32430144f0f97d826a5ada873cefa8a67b20b31b4236b915c17cf41336e4ecbcd6c8b94f70598e3076f7d7d30410b7def047e332bec210c32561ecc2d17fca6d0c09e02813379823f7eed085669154a5ee3dc7c4490a2570c5ee3304f1fa30e1146177797ff86ef605dd0366181911c09010e38ccc49a9e98a20936900cdc4045d241833581b7201acd6975a4c6565e14d5646cb037cf8e2934fb987d255b17a286f42f78ac7399d15b25ab85519feea823ffe98d36b43ee7c0550fd0db9fbb1cfd09849d349489bcb11acfe89c3880f5a25b7ca681fba6e1003286591b2fa2be9290df0d2989b5a730d85f44cbc3d1afdd5fd40bf4695d6a06fdca727026932986bbb6c19bfb6e97e8ff86eee1434cf4bd4bf711bc1ce532bd5c00c16bad40ea19d000d73524411f2d8e98a0a626ccdba7caa4973e3ce1e4998abe5a5fbe4cd650f13be17bae456c5f395dd28e82656a130b71cf5572e18685201958de21fabd3072f01dcd9c261cc774c09dcc2697e8c1a4dcb9fe46d6ce1585e726043c35d5de701dea48fbf2832ab6314656b16f3f2c4476eea5e606612c271f80fcb1b124b38e8a608a295b96e0f695a39152c98a0826d1b78779f5abb5bd41662a15dbd08b64ab55bdb9966a6db18b607b69d37b7e73951b22ad299986e2749b43ffc8d1ed7954147f7da1adf4af50f533e5fa9721ad92bb3329a7d5e519d7dc1e65db43cb95e80db3feeada3093bec6debfa88c50dffdd48ca8513fc97bd79391107698c2647837dfd8d0f78f19df4fed5f744bfc7aba6a1b31e19ee5b87ad33e70b9f1b1cc09a0e6e06d4815b6550f8d8577b5efa282ce12d7c5aebd7c468ea723bda1036eac8cb71e62284a3aaad93d45d8ca0313ed512bc734d264391047b6f2372eabc397f575594e4ff32efae17984af237eacdf1ea542f96d752ca2d696b34c82ff24b16c855243e7a0c707e4bec4f699b029f52e6433a4fa83d36f5ade7f323d24fa7d65792316f981f7141180f21d99888d15a016b3c56005c07e8a934b2117a22852fab15a30f9a89a4680b768b93d3c446c742ee477b9f11ff09f0b7c94874e3d7efacfd2c8d8fbdecc3413813a3ae1283548d31e88961aa819ef76f37bc08a82f19491288bf8af4dda12fd3720bcc1a46e92a36efa4e28c0dfc29ac56d0f2290b2bf0af1d80a2e6414df6e6cee8ca5572b18739e6764c869f4beabcd4169d6e5fdda8eca109472928959c133dac274924e5444f21f6f211a5fabae93851ebba1015b737e8d412a8aad23a6ea7a1c662cee6ed665e5722f715192bd1b64aa48a8e55911a52950c16e9619f694fab9a0460bdaf607b1c8f2971fbfec7aea97c1a778845c0e1d42b85a876955044a12834b830bfa806648a9ddcf152b9f871c662aba87f786a41eeb35db92ff6c480e904c536a6719f9d8af510f35dff6d690b5cca05edd30bd46fa775818f7e591065605f8343aade11b37845e81d974357ce3655b5e7d1bc99b3029603de393bb08c419a8ef508520166ef87e3961e385be6a0580d61def567aebc3b6c2e16a4278f5b2732e6d9e13ce4e3926046ca98cbf318d08b8d60ca09e17c0449a236b3774bd75d35c4b64ea7d39dd58da8898d51e61e3fbc55d2eaf1e4cc326f0ceffb603147fcfcf343a5fc92baad91bf5166625907af66012e394e8a4b44e5dcbee8b4ece1b1b5b97dab914fce2800345f0d28669651b34b4e7a4222b76e12a5401a407370459831799abbb0d0137ea975f2d32684ccb098cc113df655c2b2246a4e505a0abe8b92f0fcfbc77b55514323478497ae5fabf1ecc1b558efb28ed39056e10c1cc10dcd861792405c4523ec17f5de3951c09c8fe532bc9521ff179ea81ff0db94281188a3f355f4399a78a5beda7933253a580f735e5f756bd28e8a8b2bc79a0a8ad5d13b78943473d5813332a7914038f15edd1c156133e0c50d632cd57274c00dbfec804587f2f942ec69608840aee96582e1bd38eb14fec7a17cdd23201b177c11b2e05ebcaf51b1e553e6319b3602a4e1fa2a6fbf88ae6ba7ba0f696f1f9c843afa8042eff8ee41ae8e7a210d615c8de1146a1b0feab759ee1ff70f3738cca4ed69f86a4f1b9b1c1cae99f2c1f2d6ef4f0ee79be36fd6fa582af6ec6d98c68b6eafcfb0c5d87b8f600ed4d2dbd7f9835387f1f374361212616e0b18b5d2c8c5a5765095d65e7b3820e803bfbda9cb18923fa569ab127f921f74366b18dc4a486fdcde6e997d70e92b7143c8ca52bf4264d5d7582cf76f3981d2d9cb2b30800786235e68f15812c8a04b5c5e88950362160b613036a4fb1f9d559374ee113c9a405a3bb3fb862f4610cc1657d7603f7a626a777b0d3e083de29311ec164eb114bce1609d0298f66cef86127622fa095a2f3edefb2a6de4ae63ada8642cd1ff1c0428f53120af06c4078858bcbb257f78b006f117de364f3d9ecf591ee37bcbcc8e76471a683957e335e32f73c23fc4d7c9dc03c56450e987abc24447273e088aa7827ab02fab551987109045f16257796c01f0ec2ed087c911718bf17b1ec6c9f35b7b1ef68bb77835e5629fa580183c4c3cdbc4ca5875b50bc34664d97550ca5dfe8a61ed1993f54d2a801e8e1bfce78b34907c8cfbd00389999909584a701dd3e35012537a3eff6cb30e323a338159fa2501d0ba4ba7efafb937f50e7245f7235c99501b3d3320863b0e439df79826822c2168e21c84600950462f70f3e8ac25d8f10f2d1cf669529164dd5977dd65291fbbf3913271d485f529c8dc762babf33a476930f0c8e99634f5b6952b9ba74b1c284b6b27d720336e303dcbc94ae65e6b5700b63539b4b67ff74ac4853836ec5ab59c6ce0db0f2cc8d997600b6b7ce9705f00f3e39d95e449612391071fa0180e7fe082c66bde61c8531a7484a25b90c129fefc9bce817d7afaa6c3623877d78798aff8f718670057b5ba0af8e0829e803bd2a763d71260c3f626d0aed0045027e9e74a23d2adf8614865fe3a163b42274ce48d14361df75167db0d25a9946eef40521760a5ca468f26eb20d2cd132b4ab90328f29e6ca8d03d92aea5ace32329daf7bfba0a8e0f1afa61cfa389b47b279c117c86e256356d3d7781ea3fa504973a0630e9cbf76466934477b69e9ebb475e97dda90a8ef5c104f517b5720d10ce459e760026a4c54fd0529b84840c6baad742e51d3b2f50922595b0ab8fe7ace2798a878a8b03708bc18cf0f8e915793c6d0534f572a87b691d037db79aebb634da695166c4c612654dd7c7c2eb563d459700ba542e688387ef0941e1c18085347d0cabcf44856bf8338207f26441176da54363f163bf2c8865a8f2be95cee5132ba1762bd1715df06959014b72124ffcbfba8093302411795b0c19ece0118835908d934188baf9ece3d8f95d17c09d6cdf98b9e060b570282c8713bf22061491d2060a7599ac7948321f22be1a8bd04694d2becb590536f59ab52ed08b05842754ac3ae5991f60141997a2ab0674343e6860a3ca6dff401e5a4a705665004353f5e9e26ce9117458f6716a4196f2e0c09e64ab3bca824423c3fa6641d60ddd65abcf0678d66ca2eb4625336860cb0dd7dc1b3adcdf79e51018f20b2da0f9c8819f492441633646903a04b9a1dbc07e4591912ebe0aeeaddceb580ac78e95cf1d480edc85b3498f7a362e1e29057f7833703fefdd27af6c340e7c357f3a3010abc06652b15fc7a6ea70e12b2afc3a8af9327d863c363156c8dc7f0c90f419c0157d3057b7a14e874e0f4d1a1cc1a0ae639e43182d613ee64695ed6e2558990f579ad4af02b2145ead04595c8b453b9f25aaa314081cec5ff5dbf0dd1b5a232aceacef17374bd874bf3a275590bca4d5abfd22d89327af0ee16c5ea5a5c1aca7ba420328ec9d02a1096168367386c537f695ebf0917740672763a30c1ffe8f6d888e64bf2b6e4236a196c69d6eaef712e14c753d59ee214e9d7a575c9fecb4f7bc551900110c9e9d305241bf16592e0a0cf12a08b73e7311f72430b931cc0648674d4bc3fe714ca7ebbad81a30e53dcce271cec4a05da78666f271baeb7fbaf1449e8e334f12bc55a7040c573538b6cffbbf206e48425141bff8c05538c4e6bc6fb9c42d9189d530fd0ed612123a86738fe2a19c9c38daf00dd2298c164da64728b9924f9ae175c1ea2a507742b1995f8f8c9c743db20f3fc14a70216220442058f7e9acebdb845536f9ef0ac350b13b397b212461366c89f248b9a52ed105cc9c842b51d11d11fb9d92484fac27e026388e3bbf60d8bfc0c8c774b999813d760a652fbb294341820669a1e5cd16966bd47e290297279c57b5432d1637eb4df7dc6bd85c345800673562a6547c00985274d0e35a58cf5714bac07d3c45d7889dc814e0e4e2309c1a5c896f8b94a73c279dd17eabeb92ccae1517b0e4e6a4519242f06772ced47b6f77e49387a75588bb9084757a21f0c60aab872135903987e48cced77cbd5ae9128ff7a24fa55b7b70cf12186c0e5981b25350fd15f55982c79a4c32906e955a11bb19697080618d1cd7d73407b3738bfca5a88ac84fa28adf2ba635e63d81a5c3df46d6347362f603feaa6889e63330a2800fa12d9fde98377a3dde65d311c5a9e0c771a3b010d475871eff91155fe279f18b7cd0efd78e13efbe8abc79bed67a515ea194d799afb68ae425f57944f30c27833ce3d33c03bbfcc9036022e333ee4e88e208c993615fd5d51d5af5c9826be914debb340466cb63fa07b2abde5425e18708f5b9233f1da16b16d439c44c7128c26d38385bd9aa9d3e7da0a17a3d6f1c2d55024a11fa7e7a602717a06f6a334d4dc50d065aae319ebc50d6eb3ced57aafb59d69456ae6aa085436fe72acc1345e04c35b10911c2ea4fd8da7421709278d59e731958b1db003a08744ab8ce3cbddad3213793d095f09cc4ce2175520b422e3fa7c6094ac816ca540d3f2987ab03021bf7bd02595631245208310348ed2287c47479057d4c2812ca385c62450f21860e9b82842408e7c7e259cb6d9e2a3354312aff42852794f8f16011aca4e823c731ea18a28557e10928622eada728a042e70cd994d4288ff67645f297693152a4c4c57c47ad37b74a2e9c9333ec840d44d1cae978f817d04ee3c481efdc61335fde2b2f4ec1ecb21ffc68e50809c37820407268ae5c2562e5eeb0e12f7d51c29463f8778077863a412026c9b7c2c3c74d70444a2c8c14958295ab18db94e5e1517e40fbb39c29b96c204e1d1cf140fe4fd46144ba279150d54a7a4cb3f8a8e91301f367123faf58f0ef9b5a4feedf30c59187fc021dc60001a015695e0374491f2ed2fa76db7a80956a820a522260eda8d6ea889de5bab74fd56f55a6a17b4ce6aa4bc4867f7090741207d217347570cbd6c98e38ffd0d5f2ce5adc7ab403e7bf60312502a3d29c079fd0b9928ac4032c76785bf85b0293823edb200728940218d11fe9811b10176a6cde956525b9dd188304d2dba36a98a2d4d3e66efc561da5efd3ae8f81cceb9be4f60205ce0c6cad2eff745f732a16015aed1b41415d61edd6a176aab91efaea39a0c116b4c1cb4bf3754ebf3d37e32fe7db2ebc6d012487ce619a388d3bf184278ec31f0d9e16d2e7d850b56e6b9d9e52beaa64fb1ca6a0ae2d5e24e98973eae48b7b4c3c0ad97ed19e7f5ba971651c9edfee51604d77eb313a54d975faa7c519ec1835a28b50bb489f5896f53f3cc667ee456b7f8672b373016caf0575058b9205d368334578ed2b737fd29a118affbc89bc43211a1222ec44465704ff1ed1e9833f2c8ebfe2fb1bd0e40c6f620f77283d330c69fc6f77a2a56343090c25aa6a213852049042adcfac8221d98597cadc495f025a9328c453f53cd007d70265964cdea924f31357e9c48875584c659e01df606d6b2e9ea9be0cbea7a7935aaa08ce0bcb70892c81f6698ac61e2c932fd2b4a4909b9f5f480d66a998cac658589b53d9deae72d9cd80955a38a62f95ee6737973e59a88a49551809df7f96b2581b2061991629f4cfc9ae3fbafa11dac1a9eddda390c419ee7ca6a4db2048fc268a456ac6c1d952d697b048abd8c202ddb3c40216a29388bab851699615982964504917daa1cd586187aca3850faf9109a56ba42da499ab5c1274a62aad6145de2a9d2197a9a1bdb0dc6b555c32a6726e7123e43b345b974d18a161f503d4eb9145d0b01662f1490efd8ca9623519253b51be3168edbd085ee780c3da964719d8903ccb7f9a94d17b4242b5df9026b6aebf338b8122f7d61a3a060856f48ea2a9500e1a05c8a831013d20e3b488cc1debd7976c1802fc4f49edcadaedf3e66d71388a453a990bb5f87553d3fe113f4953ea3f09f10e8a46fbdfc58fd35446b72b44d04fd014788d18dd1ce05bd99c1423bafb56617f77074a28e93587e0aca63180e96426a74b054ae2e1fda44ba37b43718b39ae01f1fb3f25bd077f48d4b6321f602128e1dabe03c1a7fb107e9b8dc43558834974bb14856c7bc3f128630404a7d486ae010ba14edbe6143d6a0c8eb5fcd14bd8ba1b01e45ced6f4609636f2602561f77556dffd6956b0e3b3443618ad4bb311f7992f84fdacbe17b623be32fcf9195284741fb985e6e807173ca97db9ce54ee3b21880988830f2a1d9a385653f00dd51c21ce2fecb1c4ec79ac77e14591643edf49a83d20141a405633a3f480930b9105acee064cbb9dbf087699dc38cf6a366e5aa32edb0c794ca2ed9fbbdc82acc672a0f006c4c0f4f9dfb4aed66d7fead2fc1240712510cc441032172d1a90a42f1e8bb6e3b9ae41165128c07473fc4b34094a42dc549233320c9986ea8bef5e13dc98209bcd504502e47a0b1ecf4d710666fbedfb1ca1940a33c500682fe5598958c2f7ba9e2b67a2898a3ff902c995fe1a47e304800f50719ba5b4876421e6d4740f094e7fa19c4c7ef6456f51bfdbcc770521588edcf6378887e36897f5a92088495b6e2ecb77f878c9efb9383fa738b47fb2945031917576192276d9423b553523a238e413e025f6090bd6988d48787acf1c39680c4026a3207015295f9f025a8042c585eb9edb66786edd15d2e8f12d985bfdd6f2b395b619c6a0bb2806016cec909bd6543c58ea830ce057ebd1ed0a5233d7cd6713cc5b5d1a833dad7b97a395e3a93f812cacb28a57544c3d2ef9df0576d32624457aa70e4ffb951f6b3c9aa5ccb00f9f227c2b5467751d625cc189c49a92aabd31c5cfae567ee3ade5358de40a05e4783865b6d53652beb1f045196b2d63d771a9a2777a039fcc86ef54eb9e1367aa32fbc1f87897aeffee71a941b8e2359d52b2aa5ab39fd370be8f771f6ffe65d40a83e74385db1dafacd6671dfbdd9fe8a30fb02a1f995768f9b02ccccce00dae7c868d381888d5adc7ce7e1cab4ecf0497d73d77f024a3d46a40e66d28b7e3e2daa1cf5ec4ae66e3ee57c197c0b58ea40daa1364666b58c86005ed432f4b697b8cc906662217e274290b57bb66d8131e256e259781a25ae3194ca9655148f24e693531851b323c64af209a0fdb14e5315e5249a5684a913acb44b5d428bf5f4ee61dcb1b2a363bff284fc2d4591d97ddd68d5c48cdb4d5eec61f1515fda6b4023767cb724ac7986e19269b91e6580593db51f30f422d6925548ed0c1f5f9944e031ad63e132e0a7b733952e110e2dd2ae6672971e3520d9bb3bdacb74001fb44f2bb8be75bf2db80045df1e0300ef03c4930bccc66f00de73bb09d4663342eaeb9aefeaa2dd4f5ba1e2d0f64b58bccac8343bcb7ed36009a3ebc972cc390d8a63a141b41d0bd750dec51c64771274f965a1728fea1987c83c8535071132ddff52f0c68c41d9b623397e3b1349818809cff0513a3bcbf57e25979b85ff6e1ec62396de4a0114006b301c33be69d8cb91485a17a514911285fb527860df0b95d3c5c9d60615f131466cf30a00789a09c19b74de3c675294680afb18b543c3d1429dabd7324646c8bc4bc3cd29b6ee8090704a8cdc5a1d6da36605b3fb8143caf554747ea4c9d0bcdab91f9707048f795c700bc152731e1cbe91a9b2677839b089c23a1294ed761358616e3fb8be88fd8d50d0d7e05ae3c732e8c3a57e92a0337717292acb758127409e322998b59b61e07204e9191308d20147895a13f66a2bd0805a586d65e25bdd6a9478aab15712aa8f4ec0978fa52e038aed1d88ddde4bed8b53e2a619817f5230cc9cfaff7c0e9a6b92d903c028de80ec29529f88ec6bbf41944572d302bb0c5ab759c1bf70e16920f227552bbe041f975b6f3f70718be77744b2517f5c48f66b3bf439709396aba5c926c6b814457574aa69e6ba3d213ab3c5f3f26f300a33dac395de19dc3a503e7e13dfff66b99f8f392f2108578f066a724826872ab1016ae8c4dfdd6f90637770bad62ad5f2c813e526aed48f3934b92dcabbe929eefb2a64d9c5577dfcfbfebf596e2949abe6c0d89818af23e65dd8691818090545df910b351065708c2b0abdcd10c662f10b3e13da435fdd35e87accbfffcb678e1a2bde7b4cc569a67158280706d17e12d73c6c9c1e6ea085a3d5b883b759d52c9583c4a992634e9e2c4a1cbe1fa69bb1bff9c853bab0df4d00712aa10c3201d12788568e5d79d807bdd6f2238593b85a7bba88079682372a5045ea5771d4a7af0c6b8871511f02864c9788f925e4fae4a008c610ace3199d2b3a87df1930e2f584fc36771e3291783e63b00ae1706f8f5b02941a66c851c3ad627c4a7ff093b99b963732d003c10b90f61d616212cbf76d307d4b7680edb5770f46ef40cc4efc045759d3404158f1d3b8865b646950916d3cf30e583eb30c0bec8cbc85a6cf5b7ad068cccaee6883cf6b50d4ff6296ea1891f00603e8f5d5ee7509dff530dca53e1ba8f84a37c9cd980fbf899f7f887a37f53d496d40447a472f1f3b3ac29ec7c03c72ea068116a3e5cc12f3233f316eab3bd745d0853ca57d5572fe4369706f0917f476ab161c9bfbd0066927a2ff3833aca1a912185c7edc9856203db0c46e8d5db4290097e858f862bd6168e9e6befc3ce809b10002f053296a9932228719da0fdb7ae6bccdc47c7e1f4899b9358da9316c51b0eea76199ac73a3cc9fc28a35c9b277eb973e87c40226f4928ea52f15a8e708e567a492ab56acfcdb211e2f2894b10766a08e965b15cb51fc81cff37097f5b64e1edd463714cba6ba3fc461e4aa0f019ecdfe0d47888003f26afa671ad8f35e32213bb003b497a98d7ed64fdd1d88507ab2a006c99bac0833c3bdcfd74934bef86f45e8372c99c987eec995f25bcb5cb276326962672e5f2da63128aff497685e8224e8d6fe9bcc9a810377ead40314ee5461bc55fd7160c34904debd186e9da87fd36a7e0db55fa43d9a4309e685f89555bdb8ec25b64a8e23308e127c46dfd27c10b44e07cf27b1fddd97e83d8ab336620413fa77dcd35b066cc4df4e75520ce26ac1dfbe3ef90240b8b783b91477d82f9f6145d1252695260414460883c58f56612d8512b5e980994824f5981b34d3cc10a17fdb0b3d20987cb27f8f6790ff6ad6bec3579bab83877c6de83416b22972e7f0ef7c0fa4df9c36d28ccce516b30d347f59f30c733f15de89ae895064232324bfce797fb80ae84ca20888081312d7bd4d3ed8b2bd35bb5f6c19689b3c0033f5e48ec19b3d91de93bc1dbd9a15feb112ca7fc45b1b1916bdeb521eeed810d81d85d77a196df14bf42b034abe2b251a0efdb6a0a8c08d0a4d36b7f3f22b305c069400af9fbe8ec1989a721c9c4976b522efa6f4364b84e22a2c160f21ff0c2375ba276673adde031e083802b2b0947319023c6f0b629aff4f4a853fa30a1191a30f4cd0d1d958673c0f26b0d03ba2d37b2f232059b4b7e40892a3156463a52b494bd3a14c518b1b549de9af4a61085e54d483ba733d05dbd1191808ef6028fa48e570dbe52fa2d1b0736817be2b980a70b8735f37378b37fdc633a2333e0842c4867f92689847a2e31822d171e4c848a52439b66a55f8f11679f25f7fec595d11816effb64c3bfc25b5362039902a4518967dd140afc013a4e8f59d83aa0eeec8664fc92cb34c5a19012c6edabcaa83550fe2465826d7506a32c8b0065cd458b4aa43841d8630be8035ec5ec6863a32bbca24f46f8ef454f62c3af0e64a99a4321978dfb104d817c89dd394c9637ca6f2a906d4a4c74d99bc5cebf7d11193f42165f2fd6e260339e21518293d14ad4f813eac780065305ac2d08eb60dfa2e6eada4dae64e3ca41f6967b05a0a20496f0276ed4b55aec9739005021fe6936f13df8a6ab3f6358b352cb7864a3f4045c425e368786ce37aef3944bd4d84bcbb833af4a0072da8423d743d78061db4421d7ad083fe035fa10e2de8410f3a86f80f15f44105dd6ccf1bd4b8461b3d6df84683be4d8c94ec4f1ad04c296e2fbbc43d3776957902d1350d015e1a0279f7831ea8a0147a68833af47bf0071db4421d5ad083fe075ea11f2ae8830aba0cf10e25e8410fba6ccf37d6688d1a3ddbf04604fd4d14294512f53c141dfe1314ed9fb924e37672111d15a2578ce39583e9191759ff5c1b6af374500d18781eaa028c9e0eaa01038f437524683f73908700dd898a8e135af1b51f65349e7aa03cf3a82633a8853ab406ea38c224ec9cdfabd2a3952ca4749420da3feafeca7bad38416bb1eb72bb718d8f869c357ab4485e00b49687e81f2d77eed161b94199c9a89729a816f460a187ed9f5deee11b62d6523d9ada286bb28d9f92f33f57d09044a3b6f663e8fef07d0c2759434912aa06f38c68a11fdd9fa1d6fffb4a70d986e90c2ae78b620a94220ecae6ab4ceea7a23aaa010ed8a551b04df8b1e914a9331804cb210c2989add54838ecbf735d7ac39bcebd5ee7e28fb48476394734ad1b36b43c3ff1b0915a02cfb2ff16c0cef27a39b4b00695f683e0256f3d8e2b493771fe9bd6df97ca59c1957481a324d072d8dbd61c6e1f4e4248bd67811fbafc3ad98d6394044eec0ca1e8c3a93f30a1ab25227bace7b7e48cdf8e077e4ba9c2593fe3f544873654064b803b0f79a8d97a038334e714508ec99f760f43dff0d36a531ae5a1982c3bdab360c4713df5ef08b36a258e9ec51da1c0a57fec5109f00257700d4a30df0e19afe570a5a0dd41b1cede76b5c07515bd7888aa06181e41bb101bb7df2b5c8f976eac260183f8a1a1e35d70bafd3c2932d8fc524db2ac502407cdf1511dce2672df32ec0594ecbeb97eda70cd55235e5268a487a85e30a563ccb1c18ccdae759ea41410776d1c4e4f21f5e2363e4da2cbf6ceb4ae8f5d4c3439fe8f8a3f2125fafbadb261b85b2cdc192e1b18a21b0fb6de5872cb3d2efe3399df7d1f62a40abba159b9fafdbcc64e359f72f9da67084c1c42b53b43c51fa063d72a97499d3f9c1e0272e0471de2be375dc8c0d91c1013e99b9e05ca5d28f24d25107426b904cfa86343b46003c5281948ec4f26340a431cb44c65bcb6d532ed45870d29e728a06eb62a08327c914b332cd42e534650512969f4e59002b46bd4644149cacafc3d2ae2c10e0e6db7af6a52d754fb772fc01dd4d99d747f1308c2ab917328ae1fee5332a381ba73fdcd5be3e8490d497136f326f415c8b6bf27d8c62f7fd9711011fdc18c57152b8022be869c1a3d3b245a46c340db7bef7d756fd136e5245443f9d79e8025626920591aa6619de30f02271a2218908345864bb64a15b9935e2f423c3c7976920a8530a4c1a97c6c0359f2f19766cecea9db5cb4520aeff9a853184ee4a158a62b99ec12e3244d30160779662c2a2862666868c362b472996115b281e07d8603f390b78619b80d044d1a693aad5ceadb903f6e8c078ffdc473b4474a06a09b41f8f552c194f7f5cb802f06971eddc3520abe9d2f75e3988e3d03a6afcdfcfceea18c51664b37661603d1f615b2ecdafba0f987246158b694b024ff54a8258ce2136499606cbc06dc478fdf8bf6d1096c0aa56e3fe5b906d19a6335c769e9a047d968788e1973bb1185db4d235d581527918eab13ec3bdd530ca43ce6d7434daf9b36d34ef7f6397ae4c39374d63248e64f8b963037ec7f6d341361ab46bc861ac08dad45bc2cc6c38241573a47f745e039947153746f40fae46c0cb7fc2a83464548cf0675d899d7d284d296830046d1e2fa765d45e7409ec3a6ebcf471152198df6a7ddd7015be6e8aae27f0f532b3975cf27d44a8bff68ff710331ed480675d0abd1868dbfa3d3cb7294e63ddbb9f547dcf45f4ecb5ac608a542ab721acc6d87944421da0d57fcfab45293d23a0f63904a79e976602b5f00119204daf2286ff9cba161c6b12beb8bc0b8d18839b909eddb9cd1d29280f270e8cb21b1cbd04e061b573a432c1f89260aa0b1817fbb82e67c7eee8d2834dfaecf150297bef25c7ca3382028b2bf05367755e2882068d5cc98e9ce92be2cef2ac7f0cc5b7ae8cc3b1d8b74fb81a33e0aa01909525975ea76119f1f02e8b1b2b1515de81188cc6f175cf26d62a34afda8b5616f20df52ca1bf6730b6c91e7477b0f377cab4ddf40e20579ad37ebb024a0af17878c89d9f483dd3002977f45f2c4bb2dccc382c9641791e826c5d6b0ebcb5aa33aaa4437bd7fd7b329c9f3a42c5b97b7c1850d5196d289a096a4324862a05fec814324daa89453b984ae5adac3cbf432b0b812a5669fc48580be63edbcea83160b8058545efc959156f463da638daefb653d1a47a2216b4d326cac2f38de4c345ea76346f41c02662787f8713190f27694b66e00493c07a3ce508f8ca1283807c334f31b22bdbf3977097a30be947c98367270f19016881555b780d02fd6abb2e779e8c8bfdf06efc700bffdb0b9daea44319e5e6cb2ab1c882382033fe654bb90b3c5d38f739f4417e2942a6a2759e104de472b3dd9bd1ff0476d6dd2f0299eff6fd96702df7768d1702fbf254791be5d082d2b14d1c5901f927a6cbc3eb07efb68a82de1de7e09807cad3ee39fe9a89f36ccea290ab67dd1c568c35e2ed8a019de8723b8abac12a918781334eba5abe05536e4dfc1f9176b93d109a941c24c1fc6681482b1f7286e7258cfd139c9764e779f9cdfdaeb3daa45364bb4f7a4b6a90f5ee950b31141e01a47f9e0f8ed82144bbdd6de8edb83b41b2b2342baff7cf452c988d607c449a3d7d8ea3c7470d1a743f7f3617c3dfecf36dc82065652811e6b670de006c6a3d901fd73f00069509e4f468335faca9c98193f7108fb979c8dce97541400e8e723e4c4c34330ec6cc9f3f6dde371336eb598292c0deccf8331e25581d5b553e8c08116feba82a7a89206af918f624645da3a9bd07d122bb1f15f6f0a7e3a5e0488d45fee67eb6fa3c96a88a83a6d7e9f200f7b7c1d83a7f86bc126497598d076eddcded51a5b29cecff4571d24f99fb5515e14c68c78e18e14b00e28a4f1c903e1e19e89e454b19337e1dfdc1fa3b150fb240d4634437a7fa4cbe9ec87b60f3d258014ba4182b2a8f1dca351a5d0661fcb9fa31b208bc060816e4cf4f18d7854ad0ad4cddcb5bf404d2e36b1cd50ec3fd48b8f35fa29d6d7b34bd27f8d076195ca069505d16b7eada2cc36d25ed4f33ece9fa2e4f482ce2631e55d85495cb8744c41632efd9463a687f942138dd577683165e2a0c51dbfa3008951ecb127bdb138aa31cb17d7ed816ba0b8afe6e391b29355e5efd632c9ca4ad760b0b3181f3ec95d4dca46a00aac6c44fdfea341cbd120de2c96652310511a82601f771761489092e248511b33295a5beb41e8ee68a7cbf4a7a9d7fb5635a334e716ea1eb128720c4f512dfc7e4e3eac6202bcc675791b9387b229e36fd96ead5ae9d408f9b85e3e1e619265e8221b2f9952a51310994c92ce278057696dc36071e064856f62d2b15b381e97d26215293f0979ccbf4d55cd262d4fae12e5a32ff98be96b9261e66c9dd8e78f1b6eb1ddb2c32edf6c224e2c2c77f50f135cca7f7c750374d187151fce02abbac8544c467b3b4114bf7dd2934dbce70081263005161fe8f53a28db9d8e6e14eda7e5b41e76f984ad38d2803becf56e7451218a9ff5074566b31faabe2893f4b2b2e299ffd2d617b9c1a0efffaef828840e8728b52f0ab4fd33f4aee92d4e0a5ee426a836c8759fe123c918a5eff3ae217b60ec162cfd2ffe55af2d4614c7248a3f4e7e83519a9ba66b910d08ecf9082e0a4f7ddc30db133339887f2044d648d58e99ae53c36fa4ddb36203636f574162a2f78616edc10cf7bb32d4d3d71a8a9dacac4d01b278afa8ead2df387b6b5936d85781dbd73f9f32197c351ae7b6ec61c35aa53392de31c42c155b0e139184a99c42564c00b796c9dd70ae8eb79e376d08e5f67739e6adf3d5c0af3e0fb777d6c9a1b337096ad0b52190897f1ee10be252120edf1d821bcb13282d46473b81f9ec509be82ff243987fd17c93d038b5a17608fcd875eda8fb0fd471896b918fb1a6b8beeaade32d6dfc1cc5e08f95453746e05dc93238b51e73a785d0393ff2e8c78925016db2425424d8c1122755464b90baf976ab6459e0022747d5d134b29915a485786c9d5b929d61ab0cf283027c042f0057d8b75c97c81607e1d24e5212ecc451e62bbf556a2b3a540842a4b87b8011d5d57a7b2e97c8e0680200588bbd8faaf348d0e33dc71698ac463b7f0150c2048b3d28d8f5825fca182989336b78d0c9d89569d307e0a1fe080d0c4e65318563acf5991c665927f9662b75f4df453d8248da84c79f1dc3784862638cafb14dd282750ae23089974d30534f1a8677274648f37346e1d0c27687da331dcdb703f8ceb9c96687fb1b7a4003e54093f5281e560e26c43a6de9b2a62b89ae0a6b5266e04b187e57ada100515822c6729075d3010a9b52052835b964b0ae44c9d21d6c64abe51a52041c8d4487686a4303b317ecf7adb22543c3a3623912ec21455632701009c0fe618c19a917d37d197f31fb93f227aaa2f3e4aee978ba645e297c30b77a0c00c6f4735d2937c83caebc0df23f98251cb120b07d6544d2b477fe061d54dbc5e70bc0746ed900f383e1d5403c84bd712e1bed50d8a7b88f553d2843121598dcbf105f217e23925a6fd62a9f7e195303754812383294834efef7b7b76b8e7e0eb33d8041d73da6672dc163c420a15a11404020dfe0088075dfe3b838dab84d45c414c441bc7ee092d2c37dd265aaf4750512e3150fefa5e8ca4cbcc76f620ae462a199000aabcdf2c53956c34444ab9accb167d3b47e78173a2be9c15cf247c47b3fe843e176ca73be165166d192ffbdb9229ca43dbcb38b82ca987cbc255f746a517c165aa1199e996118a11ef059247000170cdd3d21d79a075b0dcead8f6b95559765ad05eeaef7d92bcb21bfd5a92e02f1ea3b1409728528e325cc9b7e11d2c00983b41a248af4f621c6f8cb866d7c5c68fdff9dca497be26f9d43a0187cf1962d3b7535b0201ac36a5450b6c4444c884db645066320a8712407660e0169df622056fc242b99816b66214214014fcb38535d76269c2e13d9a71d922ca2ecacf7ab1df6c08db5135f3cd5547b4235d43c92c3ac93b4296e9d22589d54006b416095d6f81273e2bb81a7b760b7bacb2111384ab5ad3eba044a87e1e53f6d4623e926f3f12de2702d113677f49fa9434f00c87017844a99758bcde74de899f25209499da903318f0d060a8a040a1f871b142d6a3de6a0d25f225e9cbc2c9ab9226af32ad4761fca23c76bb5304ffb740b21a1e4e5174eb3080f21a598909e4bf25a8cde03865e1afd258151cb61496e91d5ec4818e1107775cf804868c10c6d2a0f264d0636cc49b007c2dcad491caf4e268fabc2df0a87527cd412157e904a3f168887aba8a31cdfa0d40c8056a1cc5a5e8440891e12a7876c42bb6d89d2d62f43f6992dda3789f217f6c012b5ee95932a88d9610f195f5eb594443868cc696a00a768ee801f10ac419e203015600a5d9f9475097724649e2e3904e845a04708f31cb06a8c00d92625966c8f87282b4af788537312f20a13ba4d5c7294c23742bf87c9acce78f17e18f259a1b07779249f8f7c9b5080199771078a9f7c6656ce1d146234a5658c30d5e140e22f40adaf7f4590a20443b6dfdf516d9570df0ae39c903f6883a0b65bdc07c4102ea90fe218d98981861b0b1eaf9c2ef35b2693b0020b130ca9de08ab2498571010e93d992856fde107689aca698cbe3cbd616a7e5194aa73aa055d58e2a4e3b3cdb63341b567818b08cf4886cafdd0f5f8c8f68059ec3d32b7cb03c96b8b87c362af3cda0ba785701eec71ce8fe0752e33eaeb778f49e27e74febf2ef62cb18edd5a0e6c7cdd9ed15868906b4a026450c4c443327c6380fc7add56c083701ca80ed5214a1104a85903cd77c5f2967d89936d93b420de513297504410258629130d05387d7235aee7a38f9fe92a7fa656f2f4c5eb1fd3e9e599b84eff03e55a2f0026f02d64b72769d116912002768b79f87e78c9676730a5fd93426de7357ba8089d50c9741b2a59fb31f2ba8ad35fcfb920e0623c434ccc17212a65978c1ff6afd41f164e8891dda39f09aa03102c66f86bfbbc1cd6ce1fdd6acf20216ff459c04443ead3ecd31070f83c9b9f8294d62d10d5d340a0537c7367b03d0c53ccee9f4e38bace629354abd6b04e45208cd252f72d5cdcd8b0d97894ec3cf80dca7f0c1302b3b8463d4d79813f36cd5e5f2c1f9209b74b309b2a8bf326c76c33223cb98eb2319d6f309142f9ce757fc332ef8d0c616a2dba00839aa32ffa8192f4039335765d3f8a7cf7bf3f2d79265237060ae7ccc4a4fc13f7eb6558101a35985557cbacd2071a987c42ff286accc24c8ce39513da111361fb9c0e02a59ca73b2c489e62ede6a02ab14cb705099d133ee5b1c21827517adcb3a76724c238d66a9588f9c4e4956c517b194075411c76e5d396b1595a137b04695022303a213d5240d2ed8fc80e9d8306bea3f521344be5b71229d794349f0c7711cf8d5e0a53c3e88bfe15f30b71c8ab7d6bebba05a51dae9780ad7fb018b26aeb58939dd08e692d6bc2c818c2949c729f913803dee9697fa897dc71ce008f768efb87b1d98cde60fb03a75845d0d5dc614eb427f26c11b91558694d7c7199608e27dc2c824f3c9428f11e4d02172c22a82de07ef6925e09076a338111e195a6b40d5180db9ca760ef99f62da2c8eb8fdbc1987ff914bf4540dc66a70cb3d3c23b8696a8b0f7c44e19484c2c13e8bbbd8c88c5919404b5626ab288ec84d74c8e5d7f5377dba80a5eafe078d620097243f740f38a4df43a0e0d9ed3106acd4cb3c737af9775ac9daa50ae75b0e20814573e85926b9e250da7259e7aa10cb4430e62d14f442a057ca53c92d4f3011368ea5df078aa834bc414138b044c1acdd37445361b392e633f04ee69663b1bf1c935a6f0b2b8f8c4cb3f3b4e9e9fa802a9c72c2ebae54b60b8591cbf2072161e5a935fe44177e8c1bedb69c12de7ab258830d84e712988bb5f091b53ffa04eb32171d84e289bd007d197a089c5603c936311224d7c98ac21cacc57fc6638b319130cf9d2329f9917a360a46b872a7211ecc4d3cac77d95311ef3a653c8fbdf8ef187878c93684640e285aefabcf74cd61ed9893ec50215d8373fc7f311c3231fe2d78f4e11155e3382035205c1a50d5010d1881d38fd872f2c74cb30459c783fb439e3e56b99e3547ec60286829e4d7e8d5f86f8124d9182975aaa9bdacfb0a691ead298ac71b7958856386d080885aab164726643be69a759f8d496d60d4bfe9c0d40f5975066c9e3b682f241d717e48ad1379b0887f25de68ff310cc6f9ebe7badd92c3d5c6e4e49833be88186f5be8f214e410d661f66107b29f1dfea1d0e2d1a05c9465f1db5ebc996de9bb391558e165b3ab78c1a4008c5c63dee7bda2ef7c1ad3d4f39edcfe016b66a935c57f91aa05ab9920086ebab1c8e4548780dcef78ef797acab33d1603751394117e86fd8a2cdd369d193dea88152680cdef42fb5a2483714e4a112f47a1b8c4cab9e7c08c870527f0d402c7241d2c0d3ed9602350834f1e151d16d402b863e8bc60643e6aae001727610c22c1a6aee0110851ff4cc9875939c01a27be9d6dd95610c29607ce901918b52863c48bbc54cb74bd05da661babab4000fd0dce768ea7c062eb20624206508960620c1b3ecb035e87669a2a8b15cf06ece5d49bf88197ffecc1dadb129c823aca5d3aa92ee2d3857c5ad80ac568e38e0528d0ad361bea100b7d30113e115254b6d34ec1d74f09aba6146f7aeb7ae5cad210604e4eef493af4ca5bc1dd8cd422ecc9436212a61d19a687f912b6a700e2d78fbc0d58b30b78b5e21fb6a7382bf5165089f8dec33354e36db56c9c629439ff95c6db8e8e0fc48832ee6986fdf18305636202bbe4e0b645174020a72699895a15797b2c9b04f68198b0ec67755ce077107ccfa8537d1e132d9e863ab71fd75e7a7a38ed0db49f0284ba0548d535359d8976d166e55420f45c1e4ada586f4120dc1bf19c0aeeee526f4207e9137ffcdcd7763ccf5859da30150da2616f2c524c5b8b5d4b41d6857a504a5ee1b8a695cf302ccfd3ae8ebc0d2388546f1b404c5098f4b5c05383ecd5560d7bc7d428f4da2e8c1ef0882f8c6dc38d43da0fa5c78420fa55616a9ff9934e0f1a84a4a9689224363e1613a7f73acfea53c41cd025c537b50ef77a29f11f63d25c48c91c6ef1ecee571acef1de45e05e9991cbd5ebdc52d639b617c4640a990ebff0d4af225c902c690ac015bc0296800daa25323d2e5f851ba9f8500b7e82d5a195cac6f11ec746e38a04934bd745c76232e71abf37d04f77dc52164702e029fcf07b00c0823d156ed9284af6d9f00475af39901a14fbb823e5b407fa5c5df8fccda18dcce74f8f9e38ed31d9128f765aee0a0359b54cfbfb8e7684c47049b099369aec9b978bee0086a60f1d2a55a7a4a8b44c44c8b431387579b16692e925417a5aeb91d3d364c36caba1a40acd300a88afd4114da38044b9aa75a7e1b5505c2ea7d23ebdcbbbb6c42d25b9d582619637ddb4c4aba0c365dd62b7e5939a1278f9e350d8a2b4409e0a7fd506600e0e53f76bc7dabb9b416cd57b3290afa9cdde37baf4c5699847105085a17de9035216a6d0f5b4db7f9dc9785ad137379390cac6f508b885425b5297523d56bb78e8a79c1a2b7952dc56fc12ff019b42b88140e33c95cce44a1a9da13b8cb51e0a69e8a266502b35331f27a062281ba7c4a618386e14cdc91e3047885bb070463eb1b46e24a6705ce5d45f4a3dbfa2f013a7efafdd7eb1d948c6cbcdf8fe4fe368377e1a14fc35e586387824533ea453435febc5fcfdc4f6a21864e28e674a5f128f809d95efc4176c730105e04cdddb49da1844cbbad31d0a3183fc8ce79c0c8d036efda089411a5a5602076f5ec8018d9b2764e4df4c4bd04bba52adc743827eb40f1f7a840d447a1bff8a4c615b881a88c93b2f97ec6d79d446212dfea468b0b577c645135d9103660723bafc7001b7ab7977d3340adb4a638ab2731452205471f81b57812fed1ec4dd9f7d84af7a2a8073bc28176470e28a3e43dcae49dd758b7805848d618d01fd509ea28332f0d4a37d8bc5c18f5ed801248e994ea9d2950cdba75960a267f6229a41ca0d614fd881faa80b1348b58b465d3d15810b970682c43fbc37f4561c11cf5e8f761b66b463d23823be6daccada57cf62696ea2e2be6f20250c51fb075cea142150256e8c433144a2f72cd8ac9e4c628bffa7c91824260d12fbc91da1d138d961d729af31973e37297ea147803674c0eb90e87ecd231a3d31003004bffaea8a4ea88de418aadab1168c42b0f25b9357fe320a30736e138593fe629bc29ddeb985ff93a124b5fe7bb7e0fecc7afeb278aa685f12682c901267650790c88cc0ef6c677f278cf2c8881c2e4a93394ce38ffce146afa9d8e7f944476a9328a0b8bd780823080d4a421804dc06c19804c808c4b393388b0ca2fce96f9e9fc8206577ae6cd82e5ad64e0d195371d351617416a2aad6e4a49d3c2cb21d5d01718e3bb25072e485dab0cc20bc7aa554f3a30d1556bb5a05ebdf168ac1f705a605c9513a85a7dae028ee4900ac235c746af527222aaee381a90f024bd5a0d6d3e06a257ad6db6827432ba74c0b5cca1a66119e365befad94a6df23aaff5695ad54a53ef4bac55227f2f9a4a4bed9350da751e39895540e7ed16aca2bd452ae4b920948f36786f62f5f89529c1aa2b56e40b12af1971c3162f667380ff5f36591a13c08403d18bb56393c8d97e910ebf82110bd6b59fcae92c2a9256ed8769808f75cf792a75dc1f8b425dd950266ba06a9aeeda8f589edd84f88a2cd6a01c197b62ba5f9c6075093f2e175a51edf56dc3bc96ec23e501bb03a9f0db7d842a75b2a1a607ebd7cc9328ac1e1e1b71c735733388b12b27d277401689c4d641adbf1787428e60158b5ab71698e3cb386a61edf25d6c60af2fec3acaf004b06d2a00565943aa9fe256bb2047406760ab14632aace205c212c2292172f32b76bfd481be1c6cae84187f8b62d576a871fdab0859941b70d07cea4f9ec0331f4240ad91d7f6979c395b387ce8ebd948ca2ccd876d57a12d1976a68a8274749a9d0d6e8c44a0466b51f2c9dc8720fef7ca2dc9a3740ad013ad0fb1fb6bfad7b8caa0c973ebab88683436a70fad7357c9c7405e4532d3ba6805329f529f4cad5ea7c34cd4167168002121ea8afdebd7344817cd66106cc896ffc50de4d7ca125cff8c79adfd51660b14e996902587b31bd9beb90d898498ad7c7bcaa3eeb2d78240c1629d964f8ac2a8376e763964f39ee0baa4af460f52abc57854479329f65696663a49c25f63d49f4928258d042981e262a4f3a586d0e46bc899ca37d771732de2597f8e27b68af492ae5ed284d04acabf572732d08efaae154c03fbfa097a3e076ed865ec7021225968430e626464af86905680f9cd8de335ad24a66d10089c969213e0eccd56c64e3d620f7628fa6e3b066b6f7fc6550c3d25c038059f208a5bd5814699c09a6c3ace24035016d1e427b3fb5b0d09244ea30c4779a948c4ea47c1d988b66ae21f37ed6f37905706d871ee5ef46c14ae6774e15c6fd198bc4a1929866bbc15276a987fe4169cace3406d609be3655acd13ffe95bf256f7ee6b4516e9eb9ea914e7143941e9ef569f704441e4830a3e4154fcdba4c2c10e15a78028d310909f456a6e6492f91f2fd28549115c06843771a7dc9aaa4f1e416536f6e50c084fcdb51bf73eeda6f5107c8fcba3eac0c7416333ef04bc4966541a8c5cad61469e3a38e5938d15bce683b2ed594689b14c2d3e4d3249d79b6f7f6fc41628404b5918491e29ffab657d1ff80100b14997a56f1f24d3842e2c3c2c06e54700c86679682000f2c0ba15ac79be3d645084caae7408953e297793187989bcee6afa764daca3872e820e10bc78751addf376a54853d55236068ba77a4fcb6926dad08645ed3dd6413af1c2c96f4eb1ad5a7176733301bfebb1369be249c02721bd9247d0b2b1105fb1f9e4b966ef34a016a57a179dcc9c74b6ac32163fbd9bac0ea09f4ee03e711efdeb53232076b3496fbc400db90cf26a04c29017fd5c854dea7872a9ce3047722535c7d652892e375e512e25821988fa45222a5f140943d1c3216a44f6fcb9c445d29ec263b7ff37c44b3454756188cc1335e92c82c63835b837d4d6244c22408c30da5f2858af9886c20817daa100f15cbaa98370a1880acc29d9a57b9a1841eb97c0a5473ea9d31dd8dfa2a8d163749fa226db2085d7bd0ee35e366e01254724d51077225c642b48ede1f839b89919c3653da4b2711ba633a407815a451c2ddad8249f41a78755635403a3af4c48237a730f79ed007084f875a16b36e480c22d7594ad94b110c860ed236114915f0a71f5ab52eec78f53886d6954964229aeb93db8bbdb8d77cd6872e7d21890183a48db4b8313d25e1458e354ba8469cf2579357b5e6ed0a82eb0921bcc8425747b3fd89090eee22023b20086ede1ad0e07e665c1645593ff285b3eb70659db448d793fd56cd52cc8a19c90862d94d7b7c21432d74674c5a7c356c4d54b03e2e211be07a26de4cf0773c052d3b02f97a6fb6da4e002b6091204b8636afd7be726ff342c1a4c126ae6be54904e4332f7a3e34a80dfbebf6665701cab558f93067b1dd653a7a11f2935ca6eaca8e4ac0990c98a60c2c8270cde35cf54797a9983f94f830964d0addadda386be03577d9948bd6d95a7d63a0d16c161aee70b74fc0dd6b21ae7d6578b300b6ce7a97f6c17068db0dda88eb92dea01d5bd8ad7314d774f561ac9abc628d40d31b70fcc38c4da756217389035527fafa3e99600ee7c99481adaab28f1e2987815d08646315851625707f9250d8145435031a486694c85abb009cf863cff97e881c7f628b02f051e49b4ea89e5280c2fd87304515e5495e89633590318043664bd412babdb4ec5486eff84ae3971115f20f2b8de6763a85c873ddf3d38c405467722c341fa3a9fa9edd8d06304ce0ea245f318bfd2d4215f3ce18e350ce28cc3db7b70921c4e33ec12a311cae3f8c8baf360edb9d47242b688b66ec667ebba6a750e495ef04e677f5d8c5bc55acbf1ec47f48e0e9dbbf9eb35ff8e4e6397e19a2a1dc57a68c24fdc6a4a40fec5195ecd431f6a31591e82cb8b268b4234798e055b8361639eef1cc766415d6486f5a9213dd42d68092eebeaced0bc0b82154ce78cbf7626b3027c9bb233473e8769ce269e697b7ce85de93434ab0e00309dd835409554b8a2a5a8145b74e0513b01f7d19ec7573ba0fea14858b84387258ca72d4819bc9b57f8302aa7ac1712b9d0cb7918f3c0ecd4dd752e7dffaa20a990f00807a32e3a3a1c09cb5bd17101f753b0026657b9f05ec879c519b766aecdda8bde285770f5d285f4e8ad82cb932361fbcd3937eb862038796e1c4aa9d243070caacd110beb759a83bc6b7b8b0f639713c2132daf18471638a662f94a3e93aac0a4a71ba9c744d750d7be4eb663650c56e80a1a6080271cae81e46927af419e340a088c2820503592397302e80dd61c1fce888c886880c6d28bdadadfe86d228946dd78338d66f48f3f3b9f7fc991b6ac0089d89b30f5ad9517ece26a4f3d8e8385879d41211bd62394035dcb7336bb96ada44bca76176c52b485a17399a1a69dae17366158c5b81d46cc6deac01437b7497b8b2b18a057ea67743e53e2a4fd55974660507ca6f0a1905ad011bf715d0b1e3561b0a7eca41e5710814732f71b48e18c400d21fa93b0ff69963158068619b2edd3a4f67637b885d9d140249441659f0c308a7f150a6720a0f3abc85f6f62751d87603a3e13d5708689ad584fb080299da7da6cd1177eb024d15b92bf2a80851d6fa6faf334c298dee84e927fbb816f33b5ec2cb93753f793a4ac63cd2a4e58d5c276960add7e3a59ec85911c2757f40e642904a192e2ba9fca8581c4daa2e2df8ef5bcc307438fd6484a3c0823f6630c9ab0f609a36f3de8c13908d00695f113c12d9612fd8edd4c26ded72511d60d9958a19b863461264afcab594a54b24eb6de59ae64ad5ebcfc6e0d5531174beabfa292f98d001b4aefe7664c9e03f9f0ab270d888436558592d1d73cda4ea4d47e4cf485619d377bd458f464f405a8e416287534c868b046f64ba831e5d92eba6502a194145ddaa2d051ef3538c9243a05d585a187033c9d94f3fa2d2da1fdb67e99b2cf56e425ebec831dcd49abf7036258c88fda1ebbd432580771e69fe64470c2f0864776128a1e63c9be1ac2490fb2e37b3919284a2fce388c0d6a9b884c7338d036d08fdf8c7cd44689c38a67e664d2631dec3496e792137edd76b8038d173d5b2e91c9faae2719197d60d4f5bafec375fa7fb969a2ebb4e3165313c2cd3f40c1cddecabf44f0178244baa2657c3942fac38a0fa90eb35fbaad755c91e21765b3d02729b8ab4b58f619220b7466311b69602508ea6aa53a2e3f4ae5dcc7a76ccccbac514af40c3d8952665907e61b51c0a9c6acbf76d2b37e7c7c05c4bce2b70f460b317cffbfbe15e3328dc53013a3fe2707fe39636cf9f75daaf0a987cccc0d247c8ee1e870081a6deafb2d2bffb8f2480845672e05dc923d2c780232d8cdd6e2a95c0450a7286c73247a0cfb7fe4c2dee3da32b940f63c99a371f51337a216ce63e0a6a7dfc7f37a15ac608146749e3999640aab0e81e150fabccd14b26a1266cbeea0b7c01bd0e706b6981bc32ca6ae54023c158c5fa51b115a95958d9d643d88b0388f39af1df55e2dad0edaed1b33db24e60364650594b3f3abb249308ab3a6414b25f336d012ed8fe15af8819adc5063b3eac79e63f7aa508b53837fd2664837a4d31e3b746ad9c86e6fbeda1fa3a82ce0e453b91cec06f8eadacabeb9f1e7fffc68911881dbbf7e2996720f6d17eb8437a7e8201a64ffc9f2b150ba310aede19e206a3e4d958bb3d6c09fdfbbd3abab21262d761a6c95d5086f2f8d8c4d51135d28d96df6573ce887f171ffabf4a72b9eadef3ff15af839b60d51d067ba76464816edcbecacf2f136c9d1b3136574756071771355c530ad139d3857830c131329efb151513d8433c32f9ae3698e65d5d897532fa9ab25297169dd54fbcc2534cda7ccdd135433216ce1453607b361fa26aa21b489a0349feaeabef31e08d38afb4a52850a413d5bf0cd3c2ff1399cec28c537fb565bd6b812f35545039b66070cd7e28f36fa7d5a8439bfb403210258de6c8b07045c1b66b95ed7f503a598191108d9dcdba3089bf6b54fc3396a603c2a8494809da46d558cada91876f846c9e61c7df2288acdc6a4d25279ca84a4b3f7f9f8d867942d87fd8f10815114c12c2382389ed507cd87edf04cb9a0faae56e57b42d6d27f55fc1d271bc3357296d900e5a600949f348abd7bed28b886b933a637d5841aa9d8e7c8508432c8c80ee739c14538c011a487b4935c863c89a18b431b4fa2bfd727a6e8d4307e50d888f3b6d5e6d5920431f215e4279bd123210a2c8a445c1f0912428c18a2dd4430ca78413fa425d34e2ec26d353f8e526edb692e8730f380eb1ce74c2c0f3a429228e7ebcbf48df355efcd37e9458cc1657bdd30735db19044676b7f30fea6158fa11c68945275c2b964592668655f6bf0992e25c7ac55d83c1aff6dccc07ff78ee1fdb8dd6b02f318cad96eb2a0dd9a77f5c08bf24be06ed05b57c51f96855ed8f9a1b32937a0dd23fa9cd23f6e0035398414bef5c0c4282ddbd5fcb7aec0d956f2c4205de5afd9e8c1efbae50c72d542a6ed79c493604eeaef667c301d2e76ad514a65684a4d99c43c9c0711d95744a8f5d11a0f11f4cd675551cdca55fbb4a9b68a904c62bb5c66729390306a3243089a097f8ebfd29f2b7c6dcdff3252a5f540c4faa0fba7bccc56121b502506eb3d621224249dbc2642a88bf836c715bf9f601413c95f2e30cbc18415049f7848fde1ca557719f46cc596495f91f71a4483c76c470729ede72f524f70fdc4a7828feb9a572c77d9e02bebf1ddeab16a183c74faf3688734ff05cb6d3753028de2878c728fca642d84e1dd04192a570d6df6c5ae0718cf77ad82f0d071f49eb4fe7206909e5320d8ca0b6c0ec83bd0d473bc5cb555a3f6200db54a5cc280202a5972552e255678b31d3eeb9e13895b06726c10f36b217677015bd2b05333c5f2dbfd5a60677329ea92ae7aeaae2ae307cee3b377247c6722b1dd692b12dcf32dea678ed9cd14a03e90890cbd255db5900f023cccb23096852f97fcac8a089438cfd14bddc95f965e30d5e84da47ee77d8266d633d72e76e21a5a5592a767daf321b720f7e49b26b3205a958ee49900a1ef148cda890c8730353db45d75ba03f647ae183fe1d13934354bb28175c440a247ba6f9c35e526f3b6995b8164db3351da63bcee9c02c08e22066f4af43b62fe6533b400c96c4a3b62dfe901fca624f65479be2be809289c4624a6eabe524c0c820bf9711012fb2f99afe1bda30f7a888758484c750369ddf24754095101091f221d24f0e6429bf7d2332fa3f2fc992b6b901617b1927304a15871e84856122af7f52f4c3e1bdb1541a72351569719511753bd10d9a2942d071833fbee5f50d926752dec7a2dc424ac6fc402460d0df10283bc057bb7faf85713ef3deb45cc2ad31f4e8b333e0aa02ec1cff6d2baf2f1212d41c29f3c05d79d0159005f29913b5d4c89508088e20239a8e9b5413b7f5ba86af98507235c409086dcaa5bc247c9ca201f62d5df640d6641caa4fab06f11fb9b652105209fed575cf6451f16ad4911c582ff390609137af9ee74e87b056ba173c9103ba8700201ce7d993a2249d08df4b3007819144334e52efa398c5b20ec2ae599a1d024f81c429d6b4859c87d82ead095f193aea7eedf476993e1c1638465c9ef97212f2dda4dadb56c95058ea466670c174a69a1d6113d202fff8d31cc3b0226e081c8b8c1653a9dc96d15394bb9b727ddf0f530144c63e94f16e9bfcd82135244ba8836fa4d46426f1ee6a09852bd9013d969d9ab5d802c523e46b46a5198739ecccbb921be9962c0503970ac939d7bd0edef5f9088e2a835d4a6da5a090058a8db3d161fae3590ee57d6b566a349e5013a6b90733e15ba69adabe1336777ca2b3bb3ba321f09dcb5b83bdc60086e40419a6849b500d129e32d5a655ca195893244ccd664a32bb2e97734bc3846f1f8ca82b4c38cf29e37964a827d5c09774a726f5c49d1ab0e6e332e24b1ab593aae331eecb27e70e2fddfbf9451a59332597705e399cb0985aea0906759d93bce6c7c1080ebe11a708311a68661d05dc049145e4238650bd7a380ff02576dbb96b80b06f95cb905a15f1bee624ac1a96fffe8786af33d1613746d2dd366e36ddbaadf184ce75a24e8188b712a33da9635b98bd8dd46ff2fd7a358975372fb32b50768588481cd85202fdb4ddb76de8871e75eb21fa5cdb615e3300c6068226f8e921a276ccfcd3a63ed69cc0fdc1cc87571e1b8cd88b258b1cae322ca92e4b2c73208ab0b23b9b5b6d7c4480d873746daa462190e997e987ed3192d04d644bc376f483597cf5d6d4d547f2418d8546de3c752ce0357008e46f85df7ff16e16d31f25e159b9e29a35e3915cbfeb0132dd5232e129d2e9a5da1f8719575d07af584eb2eaa872618d4759d6694f291f5c5ac1c10d3b7d57389020b921879f7648bdcd424da99a7e8fc15b8f15bf462484cb846325dcf8686eef4ea13e2e6c2be02770ddc016e338254b510602232b0580e966d819e30c3ce2f2df09858713c0b9e16cb0c95e27c2fa21d0bf5b5e3fce0c4156d36f55d0bce5532dbdea37a4013b4d95938a79bb69fed82c7d8d9f17fd76725744779bb5484d8cea9e9e2761a0f57a5cb1c78079311d6f8bb1abdc54bacbffc4eb8471b96d0b8a6e042d0815dff1cf77cdae33ceb8f10512f70c51390a1e04f1362f6d9bea9705972d1a4f414273b2bc20d912f5aa28a1d7139a8acb859c997f6f70009b6e131bcdd5b54feb3ff5bd9026b9928eaab567063dcdb2b4ca603c091e37a59ba83a3ed758d56a08eab023a583118800e03cd579016423847079d6618d82dcb27f40544140ad77d185828b2c93cfdd5d3a10edba6325f07458e1b40ce1aa2fbb5d1655f916f3961be6b4035ae066bdeff8e85e41c30726c39e6d20e3988aa629913eaf62f68e940a89b2a22cc050175ecd4c71e2030d43fcfb4833f28eac6c9d92ccbcbaf4a04b485b1d293899b9c217ef3fc5a847d49fb877c50faaf378aca8fbe692606ea56db9d53eb2f679600d78e9be10663d80da06622a71269bc1cb525407c178f9c5d75b561c87f6d09734576a21b9772ef6ebc1cb5cd60c7648bf1b5d7b1c6faadb508b1eec8c9efdac68c0ada8f044f3135531cd94a6b480c765e324adefacb991ec818a2514457f4a9efbd7f1a48ad0d6bfc05174cdb25d0a4112750fc3fe469207e81f455e0b06d40c38f63982b8301fd85727a6254a02a49e6ef1538326b9921b59f5d4970e0817c857e2509cac3b5044b8a5ca209463461b1a48e6ecb2d159125a9be0739f52d0ac73d33db97174d6b49b12cac644b320b8004a97d0c286e49275b6e58d1f1c05bd22908f932874e66b0b4db79d2cde84987ba08d73307cc737cb8e94930ece33d10db3c0da72739f53e73d793ac29f6242bed49a6bd42b906c8a0a824d13f2468b1ff1240d35f26ef35e421538fc3947707ff65340989a9f2f0fe61740db8f24cba855982930613730edeb2833c6840f8f479fee185121959413f2722fa77808fdd08380bfd4a3bde3f7e90fda45f27a05ebb0d6c01aa67f6f62b397ff97241f4227da7eb9648e3a037e71d822002578523f1192e788225c804c4b8b340859e44fcfee741909fa89c0cce922615a202498743f7c3ea7360104b42875b10220f661c95258aa361e5061c795341c750a3abcc43dccd099a4f8e14279cc8432e04242265ca587fc10f3c9843d10e72608f34d2cd89f2194f3014ec48b4e3907160a73488bc05681f65311885a9dfff77d18c85ff70b9e9ea6ff8bf1ac9a61cf40186f0fe3b7924187669cb146698572b159afe453e63923f682bc5f94fee3f662d028f25ecde62c9fe4bb3d59cfeb99577fb84dfa641bfa5f619624a8da03929bda4670889e7337bc6806cf0182be1d6c3f0d9ae7c5b102842106d79e1a2a2c00dcf628b66d96f11607b19d5da1f6b95fb972983c45022612e0232d108d6697ee0f17f169ec7c17f07e5d1521f4413f967b42b96fe8ca83a45a94b54c8ccfdab77fec0825f348ccf579377c7d50352b5fb052be38ec8328178bf903b272080e4e00ac55349902443fb0c9e38c7be6378a034af6b22a23cabb4370cb20887f675c21dea3c123fdbb4d141426b5b7cb566b8e6b8bdeb9320a62a2e03dc1874db8388d71bdc85277e9dd220f20ac067d6afe137fd8effc7a31bf2ad3d5ad5c2d2b74df84ebfd9585c1fea13e4857671ca1466d0db62220aed73fe1857daa29980ec522b7fa860ae8c61fde742fa66e989adecd89f7a7e819180c55b260089cbac9eb072605ecb77e556e0c850c88b78c21f4284d0cd47d2a774d863888efe19fe1575d3cfbd6774fbf735b96d2d57654af2022925226e81f78fd2a13eca5efb673c5ab658455c6bed3f6d11796263ca19da1b6d01c57f6cb03f6af99b9be83fd0cfd5f3f347204dbbee04580f1d776421e8d04b8705138c6152101a9156de3631f3e05fd4d3e262f5d460b387a5a9e31a8d3ed9f1b8b4fd805a9efe6413a56664c9d3ac82d94bd3e2b996d3e3ce0173f112d22c7b38753155551773c5546030572f6f6cb6026bf0e662338289ddd6bc50e5b43a98e3999fcba522d6eb09b97f9d12ce40242b5d068fcefa792d8a83c2505c081aeaf122390c0306d1d640e28b53a98c88fbe577df43bb66fc6a7fd1ae3ff61c7a6cedfa808e69b5dc37608afec43102788be13289ff6bc35b745e8b06a6b99aeeb9bc6f35365fea6ed4e2a6aace7e6696a96b0d040d3acfd5be7525fd625e80f344db3957cd5c849e7ed770057de4df5b4ff36761ff78c0f7c0da5d774f6a8447058f8e093a2d198aabfd63ba10cc4476076f96723fb3e202d452dc3efd015d702010d21402c54970b5d55a2058b97af2d655bc57867bf8b9431b98652fbb67aeb639a35c133b2e118bb813ef2a24c1a3d90ddba16dd2f08a4fb519cd91d3013b53e8aabdc2ace5e900da2b604d50986de56e88c64eb6824ecd14cb3662b1193f27ef93860790151565e5425bda90089402d6aaac70692966aae80f77e71ba22395c70ce2ab5783eb87ec5b320834d57c71adaa2079176ade8bc27567e6447c3ba558d72b14f806dfc88981aac1c81b41aad9eba71c1e27106038fe4e13041e1133f82586cfe589af2358ad13421008af3e6f856e84d84d6d912059ae3c3c707114b1262ccc9d7afe6b3e088bca4d65647f5d566e62f0a7a08c38a357c2fe4eecd4822226f8daf157e97c9c223dde3e7850b37bf25497791213986669226df6ddb599bba9381645f8cc49ba2f8599de79e112b709829355e680572a7f4ae4e580eacecdc4a1b5eda6523247fb764a65eed1d2c976bdf79ad63953ef5c4cfc8960c18b74fd485f6a166a4d58362feaeabf753e83d33e4d9374cfac54967a15f3a98dacd2481c747500d6c6cb7b9b9a1090589a10aca54bf6376455d3c15cb933c28b0f12f78ee4814620689f80e078f369e444a9a662bc18d41069de2a1687caf133860a47aec10585af4179e7229287d044d4219236e4c2089a2a483f13022bc3c7230c700526f97859dcd60df1c786a8708a18686df824abcef17a827de670a459d4a964a9a5cdf3b09cad0b9072aa42af77fc7685e03edb9e89ff33bbf06d94b0c2336822a38bd910998844102c11cae683f832b48f0cfcd30f55e07095f140251904eb7ca0fc18d0e60771c1ef94f375003109f373a2eee6da84a736e7e01f38bd26d2a3a18c6a355dc2a8b2ec556643753587bf6345ab1c4d0f222161f3b5bc4fe890bf69ffb52ae17a92620ba9ce9e427b6f139717da5f7f1e4b28b114d9c197b6368fc11485d57efddc76a364b54106dd388f6b40d62c5cee289355a1e4d7c4d5427cbfd4ae8fa8ee06c357db5916cab962a3600dd77789c2839358154acb20d219d6dfca3d1428116044cdc29a8d570192472e948db87fe424da1e0631dda758b3339732fdfb335fcb0c613f3c436726ccf99bce9d2bc8bc6f9987411beebe4cc58d29c37ed2e5b18f638d9666fe5a63ff2c6556e11bbffcb322ee4f2152d24eef8f97a3cbf3824011d6f6f70bac718f9bff6e1bf16ae0d4fbf555517dec383beda7f8718b22867a17390e8ea02d8f9e56411d615557c33d1a52b8a4f14c933eb025112608803270921e56bdf5d5f15591113814daf10d65f9e61e739d338009042b6ebd517ce7a0529ed5260deb8db39618d796776eb186e703ac98c1d02258f985c107b1469b53b5b7931b70c361a3783ad5e9aca8f2a050be6c4d12196d7ea94c42038173ef4ad3ca61992f72830aaf18a1e1566910d3224a37340dda944331cab6fd0d529d184d66ebae41ffd8e8adf9cacbde1cdf4252a49e9ba3fed54ea70ec6214de06ef6c29ae51a6272f3057c78889a05c15631c6f547dedeb75785cd97aa011a385f3214177170ff57428702219d399223bb616ae86f21e196b8ce5aef0a153ec06c554eb82e9fac7e14ed5882bcca36c6fa315c63a40eafb44d8ed214a9e98706ba72650f24df8cc5f664cf0ef70c2b657d91fd46357e3c0f501aeba3f9bf18c016ef60b46c6d7e2ebfb13cdacc2db3d730dbc50eea2cd0ceb0a9daf8862f36d764293da5704057dc888bb9ce102a7ba85e7a827ad762f6a627c8153595e2c1472d46936454d87d4d33cd15f37e4268fc855a940da8dea9e2e060546b5cae25646de0952aea67479da85f77aba1c06b1d5aa79846d4dce3a80b9de95c170e309a7f69040bcf8a2590e383610acbd132459779862d0873a9087a327cd847daa60f27c82ed3b03a3c225760badfa630dcf4ece43cb5242ec3aabb8c9ff7a5bf2fe359bdb58b5317272f6c6121a52e4bb7f90b7c00e270a2de9c8c4338833bd58eb62b5a6e8c724385a18cfcd1dbc28a4f86cdba18798fd708f36b79a9e3766b35b030fcde4b84fa0f0b83e4d2a28460289fa2115c2547db3652ae0dfde90822d202112c6c00218959916e60e5bec90508aef11fb8edbf6712ca1f56996bb9ef533b1341ccf399d2e8ed669a10703d1082a9cfd22311a2080754cd4fee44fdf5bb86dba37a3980ce5b0ab75db2876f5eb060cb6d262d33998190d7dd494d38d5457299606d65231bec064c9303cad752925b30903a4e072dba2dd1c8143fdfb2cda47885c2ef475ff23e0bf105bc262687d568365ec12fe42f59be751dc2524fcd0feee3fb73cb5d9f85013bf37c03c5093883cb17afc39ac5ffd9078f81e5ccba0909697befbde5de32a52465e9081e09e4083f374dd3b45a674f05f00af329fdaeb79f41381f761326b86d13e468ab7ebdf55d4aab86b36852911754c2ce241c5dd8241a285d993483305dae4c9a81923b8ba4f00ef73f5d0522328c9c507cd309e9eae7cf04d39da12909cb3f4a32291004283015183497ec702507980b5b9826b02a2ce56826195161b159d3a87f822b165b40a6d3cbaf63358fd6f3a3794ebfa4b258d61a2de12f01d90b12605d0f57d03e133b7ffa2a1ab682a03ef543ecab4224aab79f52bd7d24a947d1501a615dcabad9775ead055bfef6250c05b63c6c05f11ef543beb72112fbdfa3c2ef91a0de7b09939f01b6f7d06a9a36414d02ac9b85a30ad708ab881672cdaaa136eb6fde09fcd1aefe5a348d8aa651510f0bcdc362dd6e5e120196ece1d3886524753847063baeeeca2e6dacf2420ca43347ffea6f14efb6d50a74b58d13a76a037d5a9bd7bb6a92cd639d755dfbd5ea554f64158a777b554844b68b0bc7bf5b38025ded53a16cd72b49d1cb4dec44b4c0a89fc5fa9c9e122a17960fad7a554a2935d943ca9b672db50f2ac9ed88d9cf85a99405078480a53ea881880273a10613b0545825fb9175030756f52b209eaea941d97c10ea6bc2564d02fbae55315338d6f0146ea1d64b35dfcdac55df7e12980a543dea85a81ef5b5864256ab0f42bdea93c0aab366bf26142df812278d47bd0d59ed12516195ca02a7a05e5e21a830f5346fdfe6ab08a979d4a3506f5f08eaad10d4db84413e614d68847a9b7ad40f493d8ae6edd364a0bc13041d34f2ba5df73456ec582fea0b8b492c369779cbacde876e8fbee4f644bd2ad63ca89063ed1a6a9e55c8432625aceaed07d97ccdab542ad5ab4217544f2344b27a1a6fc320fba8b75c5814c842bbfa576252547aa85dad85b17bc4a22aaca2030a9b7ad6cff8519261cd08e792763d4182c4260caa2264c6dba71122b1d686311bba80645a61fd8c70092b64220640ff8c22206684765a79827a541854659c827ad4a3421790cca59ff1369c38b8d8f94433c22afc354a40d484d3c7934906bc352fbf06d89a3056c5f4554cffe3fd8e98fd46d69513d6d5ef67a394fe09f2d24f721d1cbfc8e5a7a08f1e4e9de432a8813e6d0279cc171f58811c213bf2518fe925ee215015530db1e8812ee4bdd4ae7e15d864dad59f1a733d5857af8a0b3df662f4d0518771feedfd47f7b19b09fcdb6ffe2a819d5f6e370f8f611a7ac8e1c8646e8bcde3432cee25298ef54a197228e7c63bab2ee3314f7fc13da6bfc9f0e0919373e58d77c7bf632f792d1406982630094494d31d9fc6ed68f3e0f418de11c363bc6303efc82d2ec0725b15f31daae8a52d636470c72e33ba900ff9122762d51855ac5f25c43b6e823959fb4c68302ccecf00b28abbf109dacbb06580faf44fa817d8be821b1f85e19d2decef32b0bfacd002dfca2b84145c817f05ede5a5e0c89f85d93bbf668124976ba83d83a31bc0339e32f4b01498e649856ea57ba8bff04e26c5adc0bc1598776cb097a44e7b112c36ab84586cd5902ac6620e2155ec76ea53616b40bafa8358eaa19928fdf490d4691c67a6a45a0c8b8d02d95ab93da697f8073bf2d19201f83d4c52c4f597973f20fc1e3216167ba8879aa7977ac97ba98f545d928f6fc850c30a9a79777777777777e6b4bb9b524ae937edeefa2d5aeb8469da4f9816da76bdf6b57bc0577777d3eea6ddddfd51a712a79b7412a7061576be9c430dc69650e19dc9d30a75167867944997df08bbbbff903dfcd971b02d20d9ae24302993b46ff94f58386159f6139685b65d9fbdd6f3e2312a34cf1c922efe5af947f3d4da1ecea14c876e8afca82d1694cf969f82dfaef90ece2ad3def9414e44fcfcf7fab0123843eacc9752aec04a983be79c72ced93c23e7666661d557c833640ff964280aca2c5db9a58cd2d1955bcad8703532b75f7e95a2c7b0a19bfa2c937a277e76abbab038a6d899d43281af943d2890499d7e3006cb4a9cc30895f2e1a9f3ad639601393664c729d469b3c6b9e8aa542a950f53c0833367cc5caa74c72456420948a5cbe10a357f7af99950c35fe99c737a8e143bca2d63207165a625960322ea19a572af1881996078a725bd0825a860b97d74a7d74985775a7665b98d7952b96206c94c120c94dc4e4ec0ca2d6348b972cb0dbeb0e0ca2d65c8a0eca7bacdabf909d6bc4af53a606bfe8e4f02d301b6e6e77c125847c46233941617c1620bd1f139ef60101d9ff394b118f1152fc357988a9520ec388784a8dcbef96e7dd7f8b6c90183aa702844e7778441afd7f9a0105ec77735c016108fdd1a1d74f20352e33ff8aecbf11d38af8d0f0ed816fa8e1648726fe0781c8ff3e24bde713d082bd0cf9e82dccf1aef6688aebfcbcc10521965ee38633058bafd930c21ef997bfeec4b40f6b4b5519fdee2c2d696130e19211421f4f7c071fb9b2f808d0c01f3da781ff6707e071cd0f5cfbe03cc674aa9b7b6cfc2b1d5a24f64c7e73c91115e84ec391c5bdb8ecff9213bc29c70c808210b975f8490859bbdbc4097dfc7edffc7bc369e0b7d44b1344c808df770051bcf852bb8de8dbc2e0d8dbcae8d50b288e367381a795dd7df3c08e0e857fcfe0c5003882b74167184efc17b38e2849245f19d45f100376e7cf737c0d185db31380f30ba3816b9b49befcf37c21149e67520770798ef210d470fbe85aef1a3b3c839c21cefc1f3cd91231ce7edc2b1f300e4e7ee3d78feb07bfafd707742f8a62d76448db9fd2d03f8cf1084cfc216901a3fbfc6cf70e42ece239d917273ae443253e6ae80f31f7cc71f10eed6f80fde8725ce7ff001d839c835fe03a432c6dc0f1e07688a36decb91f38de386cbbb3173841b0e5728de0847d68b0cc24f7034f2ba1ffc3f7503d4f80ffe8370c47977902711227c7340117e0738c24fb0bf56909c17e187e4bc08e1901d3f4228db41be205ff9b58288f0393f44841084cf0941f82120fc8e100420385fe37d181c6768e47571c20ffee67dbab317bcf90747a07bf3f4fbf120fb7e3a78dd0bcc5b0388bf706b8453e9069c4993cbfcc2624f2a34425638c390110b6719950bec38a9cc89e3073bcea157d83f572f5734341c59a16c97874343d5b25834cd49cd49493f1cfae800254c67fcbbea57552b37d5281bce32edeabf39b2232719a95f4df2cee953e89b734e21b8b093b9b0122781c0e62cd9f9322895dcfe2bf86a57bbdcbe9a271c7b5e2cba0a9542995e5287fff56229f15f8d5e055a26ccebe3b7824c584fbb50b76ba794d24cf32618b6c0b5dbbc0956c06f0bf4765de7d1ebd7e3eb7dde044d9e111c6f9efa9eacb516e5a1be96b429af6542df2975bae9fd1870e6c92190bf3a14dbd5a9522815b7a95455cb5454e59dcacc4c77f7e9ee2ea59452b2bc7bce39a594337420b168a62389adf783f7b028df260acb1f143bdf260a3baf4d1456def95d5397d29daeba339596ea764f800c57b0a1d4a8cdba7394521a9ebcdbf3e4475dfe013a93f77dde35cdce9db3a1b439aaa6a43655a3bbbbd378f293b2060f763e6804e747be0d254ae3bd0d2577772977e48feeee3fbb199ebbbb1cdddd5daa9eb0518583a37825d778a2f3b1a97ba06471669a6f3c659b562cca2ff3c56d81afb32897ae77d8d37db92df0adb582b0712074dcddb4cc9b9ff77d3ff21d1c3fcfc1913a3866d73d29fd64bdc92995277967e5752c2f051c9a2b47b0a3d9cdda636a04e7f677acefa7bfbb47fff636a2e1519b7ac383d16d81af8d2ff2b9868d694c72d9132346a3bfcea3d120eb7a6057e4bf53dd9c661ad7993ccb49392ff73ecc6dbf6df57db8f6a66559c66559d69e83f3527713c83070c2eca94d1dc8b009f33ee93dbbaebb8b8da3e9eebaf6e2a95a5edc393f599b49d1838e707fd7759fc0bddbbb3ba9bb938ebaaebb8bee39fb04ddb26777775d7be9eebaf6e275475ee7e566872959b6cdf529454aa5531af2043ac8badfa9f3be6e54fefb344f8a6f7f403a8c173bbc302f95b3a5b78976f34b80e4e27a61f71b37a5e84f33907dc771a1dcbe1fbef36af4d2acbb8633495b9061dd099cb01438a5cb1f05faa3267033d4cdbe73d6a1defd810ceba6f78113660299bbf45312b86da7e4364998e4bad56c1c0db752a148d7792acf93b2478fd4ed3f7284c5569706a71429f701a1b7bffb50a69327650f7be53bebca23ed02412952101c708437b6371b410880b7ed49c94ad9e3f3ee9462545d0a44a4ecc1090162110a3b693c297b6cb54c37c393b28726cf5c3e22332079bee77016b58b77e68f3386cb2f8b5a6368d05b650f0accdbffa34314a48eaa4b9227aff3ce94227d39bb6b75cfeb75e3f195cdf3fd3785d52e1b5da6e915db865073125dfe113c7244a5c57e536c74b11d7852f6b069d160d9988c3aa67185e6831454b42f0de6035b16123c309b608d593bbacccc9799f9c60bdb572229e1c46519312f71997164f0917559092c1d5cb9e505646ebd12490924a8bc7ff559c44a120677cef98f93f3621e368ac5623bc0e6f7e470279114e89ccfc21577be146368eea0bd9c544660651651b73997a2dc39c5cc32770a752f9928cabd2343b993af60995f036ce43adf4826c932312b57e6985974e733953bbfeffb8cbecfc7f77d25c872bff7ef6b0cf7bbf2c4fdbeff2b55dcef4526f7fb571253dcef7b72b89f09bedc4f8926f77beffbf9fe4bd7751d0962b723c197db8d20cbed9efb7ebaf7d8b6badbcddddeb70d092a7743c28bbb8db9dbd7ef67fbd6adaf7d3f551341d1d53efb7eb49b9b397d69834bdfbf1f9a5dcfe2440ef0fab82d8f067047c965c71de5973e6a125a92314ea19d3b4e1b9ee809e5361129b8dd5fc7b85d0412b78bb0c1ed17ab94e0f6bfaa6871fb7b7c62b8fd3f8cc072fb818c18e3f613010111b74100c6ed67e1c810312b600c89e02285e005b7918e30e2f673d197eb4af79ba718b06c7da2ebd5b2a676e5338f6c30464398e4082860fe72196988125c26e286cb2f3fb0c5e517e1fb61560d1ec042cc1435a8220218bae0418c90098c30223a02c62ca408fb139793b8fc253033f38eef877dbca18205a63bcaa30e585181076af0c00e1e90c19428a840a18203a40f5871fd5fdf8f2b91852684550f295fa4f870f9757c3fcc8ae910e2072f427c71fd75ac4e19637c70fd73be1faf3a7a88122547d5e1c34d1023a85166b8fe38ac545222c1087e90d20412103b3884a0870e5441c31208607e7485737d3fee2284c05cf4e57210335c7e59b481cb2f7e3ffc310042053baeff8defc7ab95609094a86c7c3f6d4978e2f2572080e0800a2e3f08566a89423577703c00775c0d800341fcb0c5e5ff8045e444943ac3efa727bf3cc1e507bf1fae642041e172fdbdfe40c3f57fab63c4fc2760aefb5728315c7f0fbe1fb757eeb8f2e104599e2cb9fe1d7c3fee04c7871aae7f0d110a0a78ee782380d1c791e422bf3821e33a0f517828430402221b5cff574f0f22b8fe3e3f806ebe1fd7f164cc696243074d86b8feac7702c3f5179d1471fd5bdf8fbf7492b8d1a188ebee3a94b9feac1d94aebfcdf7e3af449214600cd1a0c5031dd0220a143cb1c20a182c60d2b5784d193b61f3c7d50d931aece440c4e567e9f0c2e5a791450b4ef660cdd5cd8c25645c7e16132bb8f8612d45354b7af8e10ebc602bb1584c871af79beb38d771ee98739dc7751f2f47c94586f94292a1e38e53689c36cc1d86b25c7ff7af1a00e2fa5b96064670fd3f86eb2f9ee07a0d33b8fe3d1b90e2fafbd830e5faffb80186eb0f44e4862cd7bf880fd79194d4e0fab3706409120e5ecc25abefc7894868020429d9433579482e434a68c872f953ac0e5e304318a8efc75960cc70e5bafd7e9cd5c494c105a7efc7e519289e7c7fc7558fcbfc1649861b2effc752228c947704c5e5ef9ae8c209bd70f937678289adb2daa41892b8fc9a0e0b78dc276ef51864b8fe19cbfb47f30ca1440a3eb1796cdb50b2a3f5c13b634f0f8fc3a6cb1c775c857199915e40bafc16061f2ebfb3a02083a6959062415c7eaed3d62e5140b9fe25f08ee9faff10e2465877b9236fdfb87c668ccbd3e74a4ba749a55e1522099120517d8aa268683e152241927ad54bd8fc7e5af487a45ef543562d1a06517dea87a8c2d4109af0434b432c8b1035c1748b489dfe1c494ed89e2d567e045428c2a2fc4903a43ad245ba53a954f86a57d38040768ced5f1af3f25fb4ac1c6258f9fdf3053248466430992d2fb8c2db92c5109213423bc8ec7994b5d62c913128ca10737bfa0cd704451939e8a1b9a466f45b223571e6d2dc3601a5f44fe04bfb0332c2e5f17bc81ea7cf9e650ffb59f62e7ba0defb917eafddec339405f964fabc0e943d585287fe58df5f05d61468427d8d82b366405ed61b43a5408e567e67b9b048507648f6e8715958923d2a922a4250ff3d12fbde8fd628887def8758d61054d8d3ae7e95fd940dabf073924d50d8153ecb0292e7dbc5924533d955f5deb466c69c41c392ffdfe363998d7c686a56aa148ac6e4237b48ca3a37e8a4949de4b6c249333040cc3c58eb69177777e59fd41f03544aa72f0f7dd832386f7367833a35d93567edee927e0d1e9c7653a636aaa093f6d8a8424a71c3a2856ecf2538d26efa9995524a5aa93b0f072f15e55833c7fe91bebb3b0547a6e018d2e6dadd7fccdcddbda98d2f35936d7778dbddfbc86855ab5acda877a95a46dd8956b59a51ef52b58cba9339994061fa7eea9b4e9956bf6a1975275acfd6967e82611d7179de194aa9e3aeb513ad6a35a3dea56a197527d433ad52cfba38a59e39a91a75275ad56a972e5573a255ad66d4bb542da3eee407e776aa711cd76d454444d5f3beaa64650ec5666c93728e9963a41792877be6c2698506973d0dd8a0232c265993c974ca64b76d9bd2e9fba97ffa4ea03deaf4e64efda5c9036636d8baf366b352ceadb52857b2a2c252112b6dbf1ffa564b35100fae3eeee492c38a959c974b9230b9cc10587b2b49403f68a6c445e939cd4a120b5d1a8e938ca4542aa5e29ee6f95e1a3dd8f3527d3ffeaaef04bebdf22a77ea8e7ff72fa95df956f24ca5764912a695190227bdd839c9b0c85fc1f9058b2c06bb7892305f2043e067a1cbaf4d32bc903afc745a3181de0ee718a9c35fa3899ddfaddc7d49651a5f9ceb408f5dfe2c1e40d775610b28ebdc97644bddfb30e7daf7f80065148ce77723b6eab1ab9fc503e0be37e9dacb7e159ad6dadddd739bb32d775dd823823e20485f3063937d49ad561a59a2269adc79a79726d5cb52a714823a05136e2a2571bb77de9160b678f1a24ad7e2e507119cc7794700977ec5e2044188701d0c0a3e903ea46082cb0e5cbeb8fd6296dbffeac2451432b86cbbb9e5c01ae97ce1125effda82105cbffe5afd401443d7fdb31a050a263aa068e2bad529ea582c1623824deecb9933b4cb2738f9d5f03ba54c2426162d5fb21c5d71fd7b7c7e3cd1c5f52327d75f05168aae1f55b9be8588eb4f6471fd092daeff242ada924405ca7589847483eb48494e5c7f5f028cb559b4e470f979069fb571f9e5175e0d3c42333c2cfba187d5973f250917a877fe055877be277970d8e5edf2f7704af65dabc09439b6b4245beaee1ea08ccae045a19048f8b96f900b91f4532295d36255897b1fce946259c6695cdb7a21543f7ba1edb5e74020229a935162a662143173ff3c00db75f63770fb8e3d065bfc9b3ff743fab7900b915411b27d3f12eefdc5281386397fbe207bd0dfde04470c51430e377b397db899d8aeec297843ea64bf655bd8023a795e0d2c7628e3c21650c771df620e6c71d80ac271db7b5bb83d12d9262e2cf7db7ff4bb49279ba6989ebf2113660a8392c0503fbd5ab4bd0f7733164bb2b548cc4ca6b0670381daa5820f917665cf812cb42bfb0f3c755b5816ca5eb4e166bc85ad20dbfbb7ea77ddf777614b7b5316b66a28647bff2df47efb21dd9b4224dc77bf71614bfb8f7bd30fd9fe0b917cbfbd29445245c8f7db233179d96bd99bb2f7b64cc26033e6d3c3222d6ae6b89881f59f53548dba70a5d1735cd7be827fd6ce711ce75f5fca706c8176fe59f69d7716a47b9f962dfaf5d2e6160de5ada0b4ef58486bfddd699ce5dfa1502105fb6660cb04d467cf2dfbfd345f03b682f4d37ccb8643fa695a43bee6190552609e51c0ea6784b25d353493257564eaa53da1b2a22ef31fbd28b6459fafbcf597740f4a58c7203b6cbbeef5b910097ffd8dbf3e927ead6a1ab76ddacf2f08c3b4df381f967569c992f802e2ba06c9d8512e1589802fcd0f4848687bed85b8afdf01759f8112463950857695019f5856689760fd3503e3651115b4fa30eee64f07aa7f648b2d2df976b1dae5ffea1f600f10cbc6c08e722902459662931d07ed3baf830d58be41b2ef1f42bf3258dba5699aa64932b34ac78024307f322e51f630ddf92632b2474fdd6103107a4edcf9d60576e37e7e4fff7822cdd3fd64895d583497c2881a8f593a89413a2d49296552f3582b75e6fb708e242b5b40dcb7866c6f93e38495608bdd62f34831645e3e7e0839c93077ced92c4f49bc337a272ebce35bee7c66e6ef7db8c57675f6f4e50724545f134a12f397df0224ccdf874f5c64d3e912e6609298d8ae79523a25b188238b953f9e926ef3264627715a40f4bb36083eb19379bed0491cfe1fdccb17c2bdfc7e19065569b971616b4b02ab1c3333739725c0dc0e8d96b88c76c7eaa36acfb8328f259848c1cdc0d8d695484c60f9818928da0b2cea4a242698183121745950c61dd384abb67a7603643a8ccdb4d0c26e57645ea6a85d6c2645455a620925d4e707332192626d84e062603f275a1138743dd87a25d21234d025ac7625d212b1323e25fc4041496933aab6b28c5e5ac6a9eaaa7ab655edaa7a2c82b10116189b33592c99418017a8d03841cd1117963db304139a1c2ce560cc0c7ba6ca92d50c546ace48d180ca4c12cd192729180eaeacce281942c55466ca38b1305c4999d9a2c909658512d3186b464b0e9f9593990ff8e01140a88cc90c0f39743080f9aa990dd8c0c5883c33376c630c7533a83158e1b458a2955161656cac29986449558b127a26a695c0063f53940dd1b031c0d0fafae1866a1966664c6d32345f3d4c0019886add5ecc5bca18baccfcbf85b512208658bbe4ad72a2f83d49d3b88c7abf5e3c7a7cd02b91b01455237106a7da017db92c76f5aeb8d9a609fbdad7d108ceed095f3c546053543427d6e6ca2d2e50d229d9eeca2d2e88216b815591d9c2b0fe8536c6725a6844d82c4c56c69a4e5045606992a858ecaa035c0aba201aa20b1a6a18f61b838362bf187431581a30f48a651955315655656bc29a8018c36e4e382a2c65c2010d6444b65eb9858b17b2261645a69ab1d9955bc4c0e08eb54eb1dc955bc42cd5c0bad1095888da256fb68138ecf22a8bfd4a2425a2a8f588eeeeb6ddf3c4d2344a29a56cb1db3f1c7777af9b764f2fdc66adac4de332f76e94524ae9757777378bed99f8d4deddedee3edddd7ab359af7df61e7606fa777b4b546b06ddb4777a384114ea4eee93bb89d24cdc50345ce5c54e9a699ecc64cdc2b187ba56bd4df670dc44b9ee8e005cf9a0e479fdcbe6bada799ad376dade97d1d7c96fbce912063b9f693b6db72846491edb2efe2ab2ad5fc354d2354a32659af9c77e0fbb93d47982f2b365263529dd59d75d3eab524a69d5a4952ef9eebe4329a5549b9488c9ed34d3eac675de673a59544ae5ad56df0f952fe5cbb72bad48045430c2420b4782666cf592e665cdcb192f5b2f7fca1a2f3b78e9c1cb7f09be94345efebce27af9d3887770bcfc79c43b395ee6bcfcc98577745eea7819c2cb1b2f5f2fff070a40bcc37a2982f022bcfcf12f8f117600f0025f8ae2df006fbe480820eb26023ac0154b051dd01ac9013d167280b5051ce20df1884bbc21de00e1e7db001d049c507e007a2cb24d589fe021451b9b1fe04b8a3c5008c19c1710088a394276409674d93c00409188fd7025451b1b15aed3f020b452b431d201e8b15003ac346c5ab8b17949c3e6480b9452b4a1e1d786463806b154df4f871d68612320453ae652a21ab0484a0c141a300529d2477d407c09242245fa368c141ee00fe9a23f02a804c4c1501101ec912efa3b40295da60fc81515c8e3fb80d89e97c703051498d3bd802c287239426ee36c221113f8db07de48917eedd76e22d081ab8ca5c2eda72b231be879d7f65ab8f548364360540d3a89c352ae2c9a4bccb55acb62fd8be2f8eab9de4fa32ea9fb71163517a954ac95715eb9754ce5984b67e92c9d594a98db3f4d2f640a4724647ce138c540b9ed85e39412660ab940861403451e5d8a85a670fba74869775814e55457ab9bc93c5ebb34a09b855c476bed6ac655b0ebe96171fadce64f12b2f0d27f51b067084b7f7c9dc0a3e7fb09a79c5e2a40c24c216b489f39033b852cf0a062bff7fef4f6ab0841bde94d6150955610d39f5e88897e902914627a1b0699425468fad1c224ecf4bd698857fbcc99ebd33c1e8eacad9f33bd7da1d3a366e5efa77e57ebbc403ac08186a3eb1f7680030d5aae7fd76559969d42efebf749603e5d5f1e9804f681a6ff5e88e9bfe7fe3bbd1706a1b8201bca369daa17b6b820d9402d2e6c05f9de7b21df7b4155b85088f7a630c8fba0fadf2781f9b4b3c830fe1e58069b5f029ce34e4bd5dceb806e7d0e1cedf47e03dfc8eb6a5e28790c694fdf5f5cf6465e7433fab81f7b7e645da0cbfdfceafdf438fa9502413ebc69f75ec10fb82eccf00bdb67ce7018cea11adef963367ff407dc2d09163193df7f76ccb3636fa670b3a0065be05bc1cc636099e538dbcf378233bdbbd514ee96e4ca9dbf65147097610a09b295862d054858125812d88d2eac2082491885d1506cd774ef691e6a3467232d95b9537e716766c5873bcaa322a416b460e82e8d6928ba73c6b0a378272cd64f3a493f4884620323180d5bf3fb0b1332b02009c60e3a2c81b9e03581d1974137586034acc22fdf853907f064cb174b482859db65c9498d2155710377c5aeae445a82618c2cc86459ace94aa42cb488624f572265510516b6bb12298b249f127ea0a0a4c5ea062759c690f9410b983143445accb0c50a25455330d942e5e868a904373c61f4aa82081b8eae54f164c9134b55dca0e4a80c156670d052860a316a4032a202063520c5a858aa618b11156086908aa8501aca52440513391c11bd78f4f8e820879a99d1a2868e072d62340596b3c10602ab819115613f2d341258d4149b17cb21716424518fac76845683942b2dd041062d9081e660cb5219472c85a165494c16463e25fc40414c9628ba6e73ffc9ddcdccccccddddccccccccedee1a8b99997a6566e6aedcddcccccc939999697898dcbeb1d3ac669ca639ed6ed6d3eefecc471aedc8bad18d73ae6bcb7a1f16bde33ceea6ded43dee9bddd49bfac799a4b78f121813d771261fa93367f040439d27cd28cf4ccb786a4126cc65d89342960199338745f97ebb93d3c780d39f6e659f4623f2a8c5435247febc347726b1d095610557f6c4d39a4c3ca589d2300803300f2bd2c924650d1e3e666f25a5ec971d2661bbbba39452ca9ae3b4f28c56932ea5a4610e09ec7c6943897677f728c3f4f0d497527677b79653b38d76dba842c9f6b7c44e1b4a9ec7392598ddf2546b18840198890658a4bf117974250db3eed6b8865b53ca227712dd5833bb698fe1a555865addb8ce0b47161198dbe194ec7d9866ba3f75cfc111bcfe5dd6e32948db5d7bedeedf3481a77ea734a7736ae38beda72c164fdf8f6ceb757f2c4a9d18ecfc946ab5a2a9a95247fec93426b92d212798f6dc1de79da28541188065a16da2890f4d7c68d24427d624a9939a2479497a2f491f48aab09a2727e79d6e725a1d31b96b9c8baedf2ea3f1eab6a2dbb671aaac79523079d35df95bf3f0c15772329d1eb249a43da99d8722f2bcef277bef4b8177bc04e6a95cbe68efe4f28f245cf93ed827009284b9438fa615d1b69048ce998cf98f34cf94b2c32461f62043e047a24c114fa40eff0ef563818c85c921f30c6c0be591db4280d4dba6e631c1240a2cf26fdf0f1639c97e2f51144b60d1c78355def11a588782c9e736c661f19b8783c9ef541bacc81281b1343a506cfff7d3451d4d11d15f71124d22a953c349f3d4c04e262975a66a0b5b9d65afb8e2a900071f617c10238c0c4c58dca0ca42878f938c3cb8a38745d1fd2a1662ac7aeee8ddae638115b723c38621428c7b575b5ded8aab7d15e36a2be8a282272c2f8474082306cbac14571c9960053e58005c11039d8115568c613b1f5eb437189af209199c7851831b50312207499a1c49a1c2490e464aacc0d899c59c74d9893397e5182358cd233a4b7418e3e8beb9fdde5fc1e070fb2d0b4c1229c072fb8558bd508074190535b8fcac67518b67c5133056d0e028e775c7d5172192be78516d66830ae5ca268223b62f4370526c26a56b42c5136d888a26433728a142063a86ddc8e898d8cc063dd88c8c0ed9076c1d534760b5166c536ccacb46c3139c98253224cb5d893485084c4004f7812984a85fa68092651bf5669f127ea030e38adfc27bf584a1641bd37161432db2c2a10007ed4abba4141b285a42820702087165ca0d0490429443ac091874086228064da2944162220b25415c79f1e8f131e38a4f755175426c9183a22d68c085b1f6ca2d5b6c41a5b0a72bb76cf1a504d6bb72cb1649516c3182ed882da66c506ccd955bb660a23db1ab2bb7685146ed99e2c9e519573c15d32511094955d7ad54cc9c4231db6e53777f5ec731f3c662e6aa3187dd3fda25954277cf989929337b873deececccccccc5c2b8bc5ccccccbc52a56870e0a09bdcab76aaa2592ad3509aadb47d361a9bb4d17026a73e4a5782e1baaff33ceaed7ddd67e2a8b7e9b49d6cb5280d95ca7e7c8f51843be2b89ccd951ff20e2c76569b5251d5caa9f78aa669a4924cea09a1bca8344c19ca8a54324422000000001316002020100e8844429128c9635dcdde1400116c94386c56301b4ab328495114838c31c60002002106100380310c1551078a10948e0f99444829cd742afad15fe80b2aa10f01ce4454299dbbdbe806469d6f0b0a81752d89c8dfde2dec4c564189e3c72a048dc0a5a89e4982b0608677d9a18178cd136965fafd769a82efbc0f6077e7a5d1420b656b596d5c84a7e490da23569e0a44e87ef47bce6fe1ebe3e8f44399a4cc9c165ef184acafeeeb1593af250d6a9191f580f4b2fd4ff72d8777b091e0cd1bc4f8656884512c3e2e587419b1e6fc2ed81b2a4feec1d83b490f7aa19bacd9d92c871b7727f53c1e9da3a300f7247574b4c5e21ee2bc6c4d13dd64bd914dac7b890e8c1e990a06b72ee42809780b820296f81c4d41b24a1767b58512c11fb1414322829a3c7caecd1ba07fda2acf6ad964c081ee7449b1f6797cdf9dc62adc73c3a17648ce2d47f238c28964ca0c0dca484999a911bab8135ed93de83d640edd51ec21f2fd81eab0116d5e7394500323daa4fabfefd11bf521a50c87ea3ef28343c12e58e026e7b1c9b5d314203aeb30ab47fd238c5f9efdd73e124f2db56a6bc11a96aa755b2a47245daa9828a19f59d3d6b1a18aa9dbb56608fdce5d4fdaea8eb71b0085f43c5f6931473f8d4b16498a43158be508cbb38161cbe7ad58f0f2403f9dd32c100e9e554d4f55da2ae052785e1b940d95f4b8a8db75e7c3721ad39fbffc6a986c07ac8790f726e817535221e7a19c6722b047fc91ea7328b11e32b936234e0f728e4be1634edc4aff75bb93df6ca4801a7e59db88624ff0053460e7ac53eeb7a239e04946d92f17f02787fee04d596ec642bff49193d8b3ecca8e2b586d3f61fef712db3cd5bc6fbc521f7c9d15843fe5a585faee97ac9e5c02d6a55317e2954706e1c7b5b6413de499cc8ca665786e5e48212f315fc824e5e0bd1ca9e7f1cc6caf84473873c7610b4b8d9cec45198ed011e05f5a13392ef3615b699de33ba7dffdbf8e236705b67022ad77be12ac27c608d21e80d09e41bba957d4bb39965038c202495a7124d42ce8bac451a74258a0327b13c9462470bd0bf75f7863260f396350a03d3e800993cc33f8952803df9660d87aabce274a6ac3034da1e05ddd366ab0fdb7111aee013df75ca43777f764678653ea342934f979339615d7bb5b3c4946627aaffbb5e64ea75abbf6f47460474d52fbe3f45af3cda78f4d9e8be85e3a0b299057c6334bd3d83b9cf1b4e09cedaf92feb60db795292cebe293850ad2e3a39328ca1ce1105905c6ddbd7815b3e27a08404befa98f95175499fbdefcaff7a70b26dce835fe55d6bb2352b3f55ff77e2d609462708b56451953afad8e60cbe205368acce28ad13153f2a6a05fff21b969433cfcc3e6202f4625ccdf9b57f275bf38d1ca425eabfb92ce228a87e709197d259d73fe80c4c0f7ca78c486e50c14f9fc1bd8a1ae2e6afbfd8009f2f6e16611f5d8a1998d4de4c7b9933d780575a64e36b51c712896e3da5793038cb96a82c43fef70a1e6d60363d282881bc157f789fbe76903b987dec8773dfcf2f7dfae12f0b6998f1688e20d786f34540d203c9e5e707d05a1f6bd5c40943970e6b10c75ac0476ed23a219a2dbcfa876f1411e505f706571a83751cd5bf2f9d9de4534fdda40cd30533aab1466034073fabe645c296d07c28bffcb10fff1acac391ec0f01e92ebd2f4e350f0d3ff88e504186ae00e19d66a0ae70772cb05cd543e01813a48262a2c10e8d577e718934e25c520b99237c2569d38d626e75e183e57bf0b0e102ae25b8430b6ba0ea7a43fbae827c37257fb5199644b3dcce83b4bd8102ea3b51282ff23152fdd53606a19d1e946ecafca4f608d0fda44d09828b9914badf764ba6786056201c52f2f2ca30317dd0262e09c646d934120e9418b51d07fc0182bb0949c55f40f45b46e2cfe0e5ab7fa355335ad340291d8dd5e35ea4324e421639e1ccc8d808a1c7fe78a77ed954e09b00df0d544bd6fad9171f08b2690ccf482e667e043ea87e97605b41d4246ae987960881bbc9ec38d4ba23d91241b0e1f24c2250fbca0096db6f5455b9161e0e9182dfc9609fb22f10758ea832761054d3174481087e94308d93397798a870c4cbbce58af2d75329398ae2e5968d4c46c1e9288800846ace2f1db199857803730a92b07b607996c4f7300e518b5ea70c3e4f9584af549ba431050fdf1365510814695d2e011748d28e8d785c4393b5c1424568c8200e474202963f395fb2c97d0b425079ad8ea5260e8ff515a468e1630242dd272fdc8f33ea0db2e2ece0632c063e1d16d46b86028d95f0895573483a81a5dd245294049d61d160952e7f120cbdea7d2ef32023b47a15b5297d5ed22eab258379e23748720ab1f4925d7ba7f3bba4b0ba8a535bcbf7b4e45701656491edf92af0df23216bb7b11ba27ca792d7467d8a48545b997677c017076a899a72fe32846a4c57fe463bc34da69423c8734bc9709d21cd6752d61306185ba24ad625a64f91edeae2605b4d31ffdab7dfbb7578c0d2028ee8f4ac3acf7e69d2e4427b612287bda1d25724b5c7d7b6f31573d17500ec3cbc89b3ca0732752bb1b50c96386a1c1b5c92a72e90a802733d19cb700b0ce9c55157adb29d0614eebfd9d100eb048a506553a05823a24c996d5e26f920431f49f4ecc4623f933d673d63b06ca7fd7f2f40bde05e0a13d13261fa0a5cef00b30281f37c6ffde2b7423b71f8e0a35fa6b7d191f7da2a1eb10b7d5949b6d8cd575e08890759cd7d3fd921ae5ee618373078f521e13ef9b3a4e18a4ebd04d0baf3bd25463227969476fcd3b1f569ebb9d6ef28b67721406eea3b9ead0083f7413ce3aac2abb5e1c4307ac7037aeddb1169c90f9406dc79acb17e9ad26da99f7009ec8bf0e39bb0ccf36d9ce7c0e0126d2037a7301d0d01fac6db10a3ab5e41bd89b4c282a199bc69dbb8c777707c85d7c152a68358ea103c83ce639189c10f63f103431fe3a1c9a68fe3b189c78f63b349c10ee3f109818f73e1c9a68fb1d4ccdf7f575384558b2e7cb72f2f57536e996b7dcd0f8701cc753290bf3c568e1d7d55a1505093f96423fbb2adbf7b784ab8227b1fbd5bb43794e894065bf48cf5568ad7e6d68ef7891e370412bb652deee5ab5b8fe082c3b8a5ca11576ddcce4a76cdca75896702ac5399a28ff760fa3bad7d4ef82bf40b943e042885d20d4dbd19e1d759c9c4ea42ba059756b9e2b23889e822b1d28d957d294fd5737ee6a93c149d90465de92c951ae8b214d12b8718b4caaf886b5aec2cf468f93becda1c32bbd10f25df36f2f96eaf68fd95c8185060df0061979d95be170f71e68ae5bc50b036d1455087ec2817c1d8a856e788ce06ce07d034ed2343185c7074265aac0b48212dbfa067b71f3892ce6c21230b734786a64dac93f4e6cf382078dc5efce7ca59eb12346b502b0b5b20334991b6f7a4def7ec1ec4e29a027e50dda8a99c70217d16d6442f1d7486fa003e9c48331a962c76bb8f50f5210a5907ecbfe72d6e8d9a0e1a80f0e30ea3590a23f8197197a3a60142cb88e3cec3c4bb60966f4e7ca5ef75d8f820e7c9b663cc3f35780e790ce50af06a16d210b2beded0c4a042b226488cc1410268ea804d0196282e3464beda208a42d3036630b5f960a952989c360ee8b76cb82b48af1507822873f50738e28ec301e6cca3f0b43606ff7b621ba4debef83158354b77bd767c99848cf079f835f9faa12409f56c0e7f8c1d29feb0de0cfaeb90a8c6b42814fa21b1536833d2e3f1484d4af80af703c09922e427dcf10c6472ef74e7aef7c17a349539b91bf86163fc5104a6f684d5038ff880a6ac3dacd398a8080dbc4d3191a31e5df57252bd4c69581a11e6a72dde3fd9557995be88f9c996801d6dd2a6d068eeb23757c46cf91f42dc62203bf8bd782a30bdad5e7f93a50b7d7b8b7df66887dcd02f81e8f3debffba76ab67f5e78f181bdf24e7c41df662a48c839e7be7c348754806ba4db59e0b122be1a8a5f74d0e4263c1f765d6af84f96d0df4002ba43a84210b973c612c2846460702f895d091b314e35de0240ebec12ca2ab2a8d212112c755e48ab1812af50ff9c9ca6649d229b30cf68249b694bea19483d048b82bb7ac310206c826aaeb09fac194801690b33a10bac8e56c298aa9a36c67f4bc022c2caac4e88f818ac663585bbe46dbd120b7c01f57241bd946aaa53298600fba73264c3682ebab9985fcfb566d3a6bdb260e230ccf7f656b770bc07cfb7f742abf8a86cd768b6c95f4618474f923524038fc7d6890b2f43dfba0a656cdcbfca0f9f05e5d711d8158423f118084e4a56ff47c19f10438e2972aa2b70dd479f336b33d6c8525da9ff283a8481f40020116647d1908bb4d067a0c5f404f9034e73c4dcc54bbaaba4020b4c147752ba45b4bcf206dabb5fa450aa0efd30fc359c0e1640d8de18d22b5e1d2c9d147b09a212e21ba40b5055a495725619b77bce0e6b434ea9c6406c19eb868c1cec9ca318ebd5167bdc174d7c304751a40dac30bd960890e88022b021d9d2b06c92b1005adf00f6b3b2f226e8478afc23780dc58e754c62b5c613ff6311f30cda76a7a90dece5b0ee25e5195b6fed37e3154fbe48efc228dd23a0003ceb542f6f904c782b0a0bc0882138733207af1da8313ba6ea84c95ddb555afd55b05fd5f9aa3cd50df4eff586f8b3bff3acad164c119280bcd5545aa43c4cf7ed2b3e586f14a6647c07d4357a5b3c278ef53782359e3864ec7a344b022930a6c9ed9b64e11ab33d53e02b18af8cd5c4740a27c52e0cd5bdd0eaa28e8f1bf1fc251afaa289f5ec9755b8469201ec9be6104e656f15910e241501e26fc3b2256ba046bc758b518111f0430bd785054104cdc042ade4d4be2e9ae5548846f2f8108075c720cf655d2bb9ac39122f6fb154295ccd71c134d2c400cda5880b1001bfbe3462ab79a45b915f41559e06454926d86379e00da4e58b66e322a69a000c5cf21bd8bbe5578d916e97dfb95576100bf05fa4cdb842326e84ef6d06c56918973e8e677ce56f642406350db6d8ec23170004aad47c9b851af59b13d6d814f0685a957404f210e052cd0d47d1df90a8806bc7a513de0cd773244b767e0887022ed970250dd39dd9dc999db24f790c356771f693a25d0dfac859ed5f3f3760bf3b6d8fad470fa00589b3df48dd151596c33dd85d69c8e97327805612885234da228cd261fc5b1c24597c23db23bc22c70932b52290da20240777d99db7a6cee49791c8434e0e8001d6b30afa191a889982e7a4822ae33ecabbbb7200625cbb95076cd8334bf68417a06a22fb4a58d89da8a7d5c3315ba1e522ff3da3282bb9e3759e8c8ed3191574950d2bf07db12d270783671ed7456bc0f1c06ff6fafb612e191140d7082a8f9089762f682bf450844522f86bad2fdd51b0c17a8a4211554a5e43ac5b3132678ea11c146cf367265e8beb3e2d083a3b1a48b4e04189839ce5b64f1bd9c88ee443a8b3589731396595095f2c452e997462d0b9a466d37c691ad10d76a670108b7d1c07327bef5c622823bbc1d1b73c1ba1bad6b572ec17c855344d22ad3a85a460e22b2b6deb21575f78104cca8803e56c902e5ebdd468ac78d98bc9a9ce81afec0fdd29cea0602ba929b51a71ab0eea0c64b581598a79869b71fe68ec52d755648abb41150429555e1d629a721956b92dc039babe5adbf4a017790187c33866c23993697206a0fa379dc74aec75527c064df90a1a47538edb7b7b30cd71a0f4d56a70031c3c4b18960c437caf37bc215eb4e29d50cc25f94cea819ece3029455e976b025e3a2a232bc0b1992cfa55da0c199bc0158244812f0a056ece5c68897e9d3f8c4ba9bb189d8b75253c391ad743394bd7b72cfbb192a7425e55084e8f1548b423763790320b2d5d97c9e6c49fb9ad6cc08838218e92f212a3551a6c6f82bce6e06c85c9f67419bceb4c9fb0a7d45c9a844d214dd0ced6aa46d79e666ec3e59fa9e101a76b130e6b7601a9012ff328569b4eb507257778afb8be71e047fe84e5b7b30f8d0ed339136721559e3d39e9fc3eea13b5d3885b1b32fdac5c34a4ef82e85dd781567ebe1d4881c42f0bdba0f689edae266a27ed4502ab45052d66228e949a88dd9a7be4f1a8fd37e214069fc3ee9cdcdec3270b09950b83b6322ca902fee9026adbbb438b06231b18706f15fdf381d62fa99adb7d343fc5d05d1e54e8f72dce4dd69dac34bbbba08357973cf8f7174f6ae115cc0f19aa71980f3f37f7a853710b734a604a5b95c46b5b566e23715e8cee4fa2a79aa885fe37cc49e10be1377b11ff14b262caa3f0cd4a00158e8f8dcc4075aa938b504adec8b1edd40c83dd235b3e25dbec0719b1e43de99cde45070f25df227b884c9556516229879b3186716825774f9aba286e512d85c9d71f436fa34c5613c1e234af4dd8981d2946fadb9691ac8d0f7230f84053f6f5c01ab428e53b0c4609ea5cae1f4db9d7f7bf8f2084de31cf0cfcb816c0c88c2a4a4dd7dfee110c6f0f2e9878ede6a72c026f59ea33ab3e097eebede272d12b7e0cdd0ee6e0e406ff75322a66ac028846b0442f4839ba0da68e8dc52d2057d38f254f5d6ff607645db5a2cdd8e65cb886c406ceb5b1b9074d08ab9516d8b355f69ba03b56d2b7e2516df626c6b9cf29dc5cdf800d728434ddb0a160898367e69ac004c9cfa3bb0e80b4e3226d149a94485c7e024217c29c7f1fd66342055388959721286adf5a5eabaf8a9a10d2dc938e9f6d929d54acd491d9b4b7f44a2fba04593ea07bc4dd073177601dac8da4b3a123a3f099c2ab150f8984c7fd3e14def293ec9f745079900cf201943515248612fad0f4e5d17e0c554ba23630ae261220eba6999fe8fdda4e1bfbfcceb86bfa4f700328a009ba51a0156663865627ca887490839fe833c0ecc2b6b2443100cd17efb7ffbefe6ef6e4a0db8af96a3e1988abff5fde69febbfe9a5e7f0194e2631ec8fbbcca5cac85b89ca974c0400c549f8649ce4ee1e18bf41421227f93058aa140ddcefe69d1aa58125877762a0ffbe3df8b10d63906d57312c9bc5049954007e056256769e8cd1df27a50ce44669d007a0fb3adb9ec7d24aace0ae73cd09af13076f8eb10206e79b4d5e8adc01d02019152151a8e775871992b5937175a6b3597a9e9ec38d8aa943f36fdc16149489bcfa8fc1695ad19b7430a0d91b36d0640f0081026f3cfeccd7c7be2858391c1b1aecccff80aedf080aa891c55dae8c409a37dc6ee84031d8b7b83e5eab37fa91b090566cdcf64dc9a637a99404ff2fddd260ad45c3ce1abb654dd872220f83aa2e0d9d5ae796d96ccba53c0dd237860dea9b001864a0944928260b2868703ecd7c3a19fe31bdaf32b2f979b4610a27c220b071c2b1a4a294b3315b851834e1b026e33a49d08bc06fd3a40175135687b08ff5315d7aa573283147f5d306b5c602a81db46fe4b13a8cdb1c8038b740b5554b0c6900bba9e0b73c0e794372e2399e244f7a939685310294f657a2e0bfdec032c13256ec147d644bc1d6d54da178955a5fd13eb3a1db179882765a5ccaa2bed860cc48e69c58e430fb1c38f7f1a56124be60f403c26d64d7b6c60ddf76b65cdf2c58021c1a9727f0243eeb2a5b3788c9bb4c1929214a6bc7055d087577602aa8d4ce05ddc78a0a64cfb372516a1f73dbefaa0889387b02320149729ee3936e13de4e5c54c76d3c571f61a402085537774213450796e4f5335dead38038cfe4227b2ba14b674444077a4027cc7b397d684c0843dff20c96aa268ba34a30749c8ea0c6326cdab3ef5f4ff5a2ef5be4124467ed7fe27dc9d6813ea0637f1f2ab81558554b70aa0fc974a7a60d7656e673894b551bf6964f1ff14beeb87f6a631af9cc84b90c59be1030b4d0fd7a859056ca2bfd818a24b773ed728a36245e997abd42dc0067440680b38e7d88376fbec5b389803f0e85f8ffadc7d19e51604ccac90a3046f4fb36e97ca61a27071d3c2a11591f1a1d20c7ae4f766f8b4f09827adfafdaee869291ddf769db1cc67e518c0f72fab3f0c6822d4436720a10cddb93299fe7948e905216aa08925f31f882ab759c3a9f84b64f07e5fd721d139f86bdf442e02f504e9bc5e2fec1a2aeae14931f1bcf5ccc86db4aff9057c7ebc990310fbcb45b51a94fbc3a6451471f405c214f1316b03d4ee372115704e473ac3a7b74b95e909388ccc2e0938bd32dd1be506e1c9e9fae391755a1632dd8d6ec31827ed10a8379b1257b4600d2d28602bfab3a66d0220ddbd60ff953f1d562641ae5146110b8d8722e442967513bca4ef8f7217910cbc333408f8769a8b79caa87111fd62ce34d2cec9842f4ca6a1eb41c80347344237d555aca73d6775d3dd7fb1b508c404d528f8e335ea6675580c4062451a6435eaab6ab66829e5e9882f76d2e503d02c0745a6dca87bfe203bf291b2efb93ba5eaca7054b56e2196ed91b5985e6b4a19a26d3393338e13915ee6b3b5b2df21d58291fa504aa1554b370dec6d9d2d2de931539852890f4764bc3264569afa9d8676a9c94d7b370e537a3e699807868c50f52c87245e5e4621125714989de6d97f702b342bfbb0c9eb2559d45f5fff04470a81780bd93cb6e5b609c1de50cbd04c3b51eadf8094d357b8b3ee16d4cbe53e19eb6b354a3df8395f251246023627a0e28f36467ec4b3d28cca2b90a1ba064d29c25b32d39edc28cdc7093831ad1654c8e55a1bee6b3725b07f4ae1dd74d484d4311d3f048dc6caa36df9c10a3c6344c9bac7f0e761b2fce90e18a2c04543810fc964936cb96384201dd82688e137a519df9fdaaa413cda4af51ec3f0c182df3881f424e20b2f6906f2e17e549290d464841e0097244e9ec3518b61504f96516c006b31ba3a79ee10aed9a4c18a940863ca3edc3843f778855077daa7dfab9b8d33b81eec41857265f996c07b43b5fc8357e660c55a04083a7aab9f94b98c674918a8ad4b41592f51ccbf0740add052e8ea66ed2b426c230b16c0365c8010bb1ade97edeb6bdcbfb6a58232d69865ee021db1d51804849606fbc58309e7c5aa6d04e19f3cb50085e92f954000623a5227a84bc2d9631fb2c44eefcdc4ea189b92b75350b3544f3c65be691a0738443b81ed17fe40af5ef69609731a4f3f3ed2627e9c10a4eb908b31973dcd5fc01615d37d7a9f610939c32b02ae044e9dc53404db23cbd13371fca616ef9d9f49bb3027cf4427626ad6ca6de586b69902361fc62a2c54727031e5be76b82d9ae86f905c7498a7b8eadd9ad30fbc2413c4c10cc62f79e967d2d246ca9e8cb6b1e1d9c596594e169abe884d1139d5f833c291e23252a804e59268cda3aa4dd998d1e57f1e031080d2473cc8c2cf50ccc917e574396c7f402849043d0dc1a9d4c3ca4d3209d1f33e2254408164c91f189516b71be5edc3cc64d39e39d35c5d989e9c17d8af7c7f9b406a9446cf4c349294e251a4ce55b166b283fa8784a91109dd24ad88beb40ff21312c653d8e2b114ca2d3f58e7b1e056c7fed90f64b5c35d5b935bbf37b4e9dfcc5f00c316631b36d93262a2342783007ebe6063e7d4e0c4c59782b5c1457266d04c35bf695ea675b32431e01343267a4d28002cc82755b9a19b2b0cd7461843c23e968ca0d70b6f18b35e217f1366e7e9991dcf03603f4c87f4199943f3711f5edf3e8e6850bad2825d6c9cd6e84c24432a08dfc0109bfa62c062b1c983c036d60bfe548c79df0227c982c158ced7562532dca672c859c417f2769543bdbac8fb06e8f891217d14424e24d4660c044e932a0268ed8f01e357038445af89e0c073cd48861f756ace4983653a5f48b1a2074572d298ed6e86f2b04a3181c9bf856c5c94fba9fce17cf1050b6b14577f75b400973622a150208a602c0254f052a5ee036ae0d0a07717450beab0f45c3d9715f6f4879beda465ddfdee5866f92f43ef51ad4638e1758dfe22149f99ab8ec3f9924ed4fa75f325d0b49925e2706b32ae224c908a1a55092e2a1b80e6c98529821272597624fa093492b469e4c8cc68e256731a38d269d12e929e423b8329a5279155716563b20eaae460af04ca89da0ffd2a40a8d3ee1952615287f838b5a26df996cb28da38c36ff104e06940b5b9b3811cb7a9f7b5362a7fce6a0f4a1326059778b24759de1c4c09de8751114caa174a7e5cb80297cbcd70985fec6c0a427a13b1953e49e482eea00d91c5e88a3a4649d56a5c8a83ba36fdec8b24d5cd07f5dba71a3f361169c76d16812fdbdc8ca66590da513115699671830f3ddc5618ea6eb7dc14d45e6a69ad24ddddc4dc9c43b5b1d8cecbd33088cb97df27863eeb3b484831c731099a3cd659e0a32586374704144bb84bee6e3643f2e5863dc69da54668b616698e3b11e181d2d4e702d91123bc5ba5aa98bf79f360a8ea95cc70d216719a37cd35741da68320ad40e03a5aa8d61647d4589062666c683d27d62988c6f77049f4e05d1a89b5356da22846b9be0042452052d25b301e48faee46794576faed8acba8a95c11b4873a451d4adba7526e8cdd5a4a5609deaacf83c6f379d53eaaeb8145f203458ff50a4404692ca85cb85eac49b028fa83a568014d15905fc1980f014f9e242d98449974fcf8e02b8572df55903af94fe9c0def31e1924b858ebb49292ced2a2c73f576c8e9a0f1481a3eaab4185e0251dd2254879a30a52bb373dc743ad0852ae93e573c3a264c4f794f073dadb13ed9d31175040e10bfd26997ddeaaf4411647bb49d2527050ddea00a4c1d4244fe36274e1972e289f10ba7c4aed8d7534d30cc2fce7c3532224894d4e3be91add9b89c5fa1b546bd41e066370fede5bf3737172f1e57ed059eea51d2d14d133637e3e9331510c8676d7ae66609c34121c8084ce820ca462032ce8680979bab5c35263c5ed1d9ee59039dd08d7b3c92f3341ac23006399f277ac65947abe6911b0009034742e2246613254cd14d1459f31f57ae80183c963e6ec2e6f522687f28146a0048fe383b52bdc40ff73011c7c8b6e3fc79b30b27a13396681d02bb813568f4362e26c3fe3ab5ae47c0a5f49d404be790f254bc67530ac2b01095f2d100f80ccf86b4b0739131e3c98a1d8c505272469ddd5abf0b85673bb6723dd255b4119e1f74dc5a18eb07cba9205ccee00a2c03d9845fbc081175166f140de594686cad2ab3755d386073f23ab6240db12801c0b22524eec7ba3a53780cdec306a775964377de465b025bc2a696a7ece334ca51d580aee78af9126a2424319330e4a7001e7b738d901725fd05852eb968e2dd34419cb2c762b02849f2a300791eac804544ae2776216ec1ca9466542147f2ce44bc210f0a937e6291968c0dc932b7f141113ae48b5b69c57fdb42ade8e47d8daa10943d21199ed7b272c9ed247a9234000e2a168047c2140b91c6ac6fa0bd7a5ec42b91481e0817e4f54c9a438c4f52a93a34b5491472acbe1ee056b91b940ad2ecbe7707aae3d33c31ebd749507132e47b0d19600b4407365be9a501fbb40989b3d1487407e92c5fd651cbcc8505277afba17d0911c3ad30e10037bbd1630761763245f55ea9f941f638f58be807bcbe075c2cbac2a56023a2beb680ef03306076e66b63539015a687df70b66d4325aa8a3accf944b39cf4508a61b14c715e4c915d4c34296742548449d87c039640ab5e7a6f2faedfe87bb5aa56b6da15f1df02eb76217ef27e88607aacaf5e52bf4b344ca15ff17d5f87d44117afbecf4510eee9938efd5246792ebb9c9d905aeef03006882342b3253b0665b47848b406d68e81be36dc7e84029e78402544a90424e6bc72c24f0f38d52f5f0a8a9762764e58e3fde0801bc5ded1c915b421c6798fd74fd07a83d6340b58fe1f6c82675890cf5fe30971ef98c070b7abfc7e9acef646d26c798b38cd222064a3725b134466f68f033a992640a7de9a4065d83aaf09b1f7b3a15381ebc010bf284919e08ea7c5a4106860815d87c793bbe0f341f8f03f8147f472bd3b60873209d810b7cf3ed816bc4c02d72881a7aede12a39b4b188a94bb3305a1d574ad693bb8d75be0c99cc10bd21d8b0724b6e49135a7c001bffa6eb60827ee885e99c496dcebed00f5e2b73d2ba8c6b643138d4cdf64531d6fba1fa18b58cdc4f3c99b5a461ae3e5aad88799b80a79747650043ceafdde16cfeec7c4797cf7bdd01126bec8915babddad27c2cda7687e1876e3269acd73090848aade257587574ed8e3e922cf598e7dbf84986032fe17759e4b698258636a0f6ce1333b7eb23e6851d88e9dce44347501bd4ce955dc1b7aa62242506eb6e39527f94b808beecab96fe8dc871730b0e029e24518d65b30deef313dfb4e727589aa207eaf2fea1407bcd223d7a5b4c65d2fdfd6c2757574b75985a05feaa9b2e1a678dbc07c535d98c538f71ccfa45b9f3f5b9281f8969d5b0b5c98d997875cddb321d7356bc38cdc39c6cb1d5067c3ade992c3ad362c510ddf62bb148e5bf36c8106141625fa2cfd7a58d46a9d710190f0da3531bbce6224f0ae81048afc660703d00db4d20eda79946014f7ad5d606d146b41b24dba8b82b5e6238158419f5a086249d1c0a54f41b070e4f6fd3e6f6e893c8521beaa350607eab0caabfdf63c0ae9833336a08d273f443e0c98435a88ff50eab51f89352165ef1ba5fd82e2bc31b3e1bc0075036ebbeb6816e9ae7783013b069a1efde5b72315239bc150ef22032db8c976f3fd4724efb88b7dafb2849f334e27fc3d606d96e76c9b507ccce63a0b0206a681b29ad484466433fe29bb066080cc146e4533528ec0ddcfb47121f22f4531b0abd437cc32642ab47c89fcfe4d5a5de5ef1899326f0fa88c550537f68285adcebf8494144153be88eed54b1adbc77bbf7fb5b8d23d5187cf2c8fc541ee3a8b9af539908396b013ba10565077679f1e290ede354005e7bbb53ef7ea581c1dd21ef2d02196af907069f66e9971e1c0b1a3abff73510bd516d4221fc48fdc795ac99f36aa60aa7a162d296c8597035ee52ed4c8d62ffffba89996855fdf5b7bac6957d7d4bd63e5054f1ab7050b22c1dde53e6454d92c5bf12af95dbb1e622598d345e98c5154a682d9470b4022d89f0c32b57ae16f24828f6eb02cfb8be0f24bd6780a6eca0ee3972c053a3e58de4895796a9d6e49db7b502b52bca8a3470b83d6ae33604742f3e5514852359a183a96a360d6c5a3765a0c03636cbdec769ed81728c5c4011f036fa55e3bb24d254112abb3d19cb0cd155e791983496a0980af5b30bd0e6ccf5ffc79eb8441a35c3c0598101c3e06f3bfa84ba408e31f3d8dc07c35dab31ea34b0070c1a68e8a4305c3dc111cc55f88b78d61c4b6b670fe40b69cdb5fce0b6c897b6c5cbcc3aeca41b0e9ce7080df0c5b7aa5fb8d705f558adf339eff21cdd067e90a7680420ca236535e89a2f086e5d383b6a124625f4c1312de1d0dbdb439519c1c7e406e0737bf492f2346f83a3fc0426d38cab7a8b19b78a7d21ff11867fb5bd62e50bc25df87dcc89552c14a296ca70f67e43581a85f799fcc209196c20590372f57f1389a7068c498ec74610780147278aadaabf3ced9f6bb918f0297983a17dfa1b9926db7e2e632c2535ffd3a9bc53e6ac2a0720d38e8e5246f4fc5d38daeb0a6c5a7dbd196915468791fcd0edbe6caa21f416415587f8766cab28a37d7ccc0e12f22547bc3d11853f28ae65b9c34a85044282ee3c86f8b6fa3ae74eca4c93e35d30839e9e31243ef1ecb1275ced33726d470e98c944045f33d35a372adc60d90f73253e48a9da5b0383058081cd5d3da01ff6627563e8faf92b40d8a16ee0cabd182a093203db9d74695925b7beb8f1735a79a4ec48734f6de27a057672287f8805965aea9d210e25bd1875218cef97ac1aa4fde28b6daa3e81fda4e4b8abc0c05605e23359b6ac768dce9850e53d3e03e1d8a6da4fb75dfb049aace427a33000d96516d94fcb1de021d18d6bc5ad2abf88edb7cdbb674ebf0b168e2dae314a60cb7f5cd24767057386ede3cf2338b15fa2041188f1cbecd2c2d87491918a2774b852b95f924669bef604793e036825ef36f025d06a269c038cf6b412a6e18ee395dd07e5bb549ed753de5817c5e0861d77811cf536328378c2a1ea986b9a4d0b7ac4837f67faaab4547a56a61eeb8a06d69db6e37dab9d2720fb37488b5e3873f46ae2a947b007a55b3bae5f4418d173f7134d7ebb77b9f219d5551739fdefd5354df8dd28a682e6dde5a0da4e21f76ea3125859c469da9c4c047b7d196df5685594d414ef1475b867c34f4edc495ace52a5774770d85da7c6bfa33a4750f232fd9e1a8ed880a48ec32c8adda2179835acc093f57fa2035d1f3e8b0742578d5bc39e8e9daf96fff8480835f94cb8e614a7e68b49cce628e0bc66a5be6fa92bf4ef1dbeae19e7091eb107491c9c0181d9cdf8389032e09b7424264b2f963c52d819d09d37b0c60da58fa3c85497707cd4d5daae8b1b9aceccd79081b5ceca401e61eea2d4da9a1dbc94478aee8d8eafaca5008c59d06ff20bab33a15ad8faac1950bb909d31d626007ff5ab59867a457960001cc5cb94ac40b9c338629c6d3530dfdef8a6aa44a6a1f70d5b398d000c4f4f8412b3ee536319d8d9bd82e16ada13e13d80ec5487e6388fc7105607f22f3eb654520dcb5d5200882138dc170cd0de1aaa4883f82bf230adb66f1409f8119b4fae128901ea315a788d0ab9ea876a6c98d230572903b1fba7be16bd5f12ce50cc0e26566885b5a745fedee0c4cda638ac20432a346ef76e410dc2794ba328f0f862e45c2f61e7cd215d0f5c15f1f1649746a64f770993bfbc50515a1ad063d2a142d83f08b5a1864c3308d21c17c121f98508705537ceef84460425c3a2b13a2304dda5518b959ebeaed924c3d9abe9cd8a7d52020f9bccbbd4f51576e2aff2637e94e50e256fb1a1bf2098b9023fbed6b35f624a3acfbb2b55436bc5da6cd3e7c4826c8e26d142a45dc59f19435c212d5d2cb139a96b55327e00995b55068b8ff0610e05bc907cbd354495254eef76db000758d059887ecf5ad854c389a915a295d7473843fcc73676c612f25f65b5f355339532375e2798ac41069dc847785f5041c71ebd2ee80ddc8d0d926959e1b8db9a0a45a7ad76a6bcf28c33c208b7cb4db20802f374894cfd8f217a62a44574e8bb45bf01f137fba5a7564b2363406ec88ae24191ca81df5b7798af14d50bc216a28902f98d37895026758db8a9e748cc99a154a1b980cb912fb45f3bd506cc0919be9c0aa72acf872b910eba82b457443d4d925abd1fcbed8a651123e0fc6a292e385e01bd93145cf2dc15eb2c5733072a3793b9daf99bc5b8bd91a29abdce2e87e3aaeedf18c760221b631746b1746a7be507376ff262e77b673b2f9932fa14d39deda0a112d8cc4f560d41b50024b0f778cfd234cc78110ee03506efb146698ba56881c20574cd05c2deca1f46b9415860115e012209badf183de2833f7b06a528366e0886d8a310876c56cfbcc53d088ec53cf24e89b5ecb7de3923c055fd3897189bfab354d8e16da4d0013288a6ef577f5d03f57f28d9eb0d969f722dc2ddc719569e9b1a61056629eb57163f2600c38b44ab1f5d89a6bdc4b895cae33cddedf1a1ff6d8bd44a6ec9e49b0fd9cf77c09ba509af6b69cfa955978aa91efeffc95e61cba3b1f36ca9edde872f85b22da1c1267a7183cb873ced78f1dccacab5ec6b087fa37c59138825364dc1edd804fc143ff4ffeef8f334c5839c73fff9a08f5f287fe5e02934a43f30ad99e593e4887781754894a757753378845221950702cbacf010f66fb291c1e7d39a9d1e3e9f6c976c4e3dcc473358f2f070ab9e48eb4eed98f70adf2820db9e456c3ca91ad9875f38c59cb9a1ef365a53ed02c35cd64f8da071e5214a193473ad578368710e1456fd3a4d7f82825672ca2d1d5e83779c4140d8c79e86767776de34d93b551f0932cb515fa8c399e522366e6426934c935a9613f6c99a1662cd5efb0c2174b433115258a1e387fdcdd9e60aa87d375b3346f1ec3c12b597efbe0e04b08975c45e06bd47a290c43dd789d9c40a6542d9e8fece63ad2c0fc9b4973e4c69acd2f1028cd9cf660bf44ed54e67d6fdead6b5390f47104e2d2f6b563ff2055722507574c70f34b1c8a460b1e9d116d2d7b300abb163ff8a2231327c8558affbcfa78a913c3cba6a1ba05dec251543ac86521d346d64b97da7cee1227239255eb83763ecdbad52dcdbda2f60f55fdb8573023765a22817b55a145a8a0aa9a1e5c17fd7dff6a5c961b70b3b97320ff363340d59cb317364da09d009c6f5ae74fb50cf787642d9cfd6b2fe08b09b776c73e184d976edcf00a400a0a3aa118db39010ceb9caf636977212ccb9f520a334c509248e9e5cb055ee08cbc6dd985ec949edfdd72892856921d77a288bce75b3a60daba062177794525ae315b193804c41e9f4880816caae4c07eca2d7cf552df4a906b18cd63243b2cd3a62c71540fecf229c285ddaa8074ba11e667241d4ddc3e6e21e93e148ac46a9cba06601a15c0d41a5ccc70bc0c64d3b4257285b6b29fe479bb7fe85a6a7473d08a7d1db8ffc22cdf58daf242d499677e2038d35d94432c5b7284c4acd0f140d972df1c0ea4ecbf1f27b1bb45520704749c9fb09e76d29d44dcc8bd3a2ef14e19ab46c634c764f7754fecc8d1b11e5e4a0629c715d2ab748f8c078056222736918e25809b2a1e573d0dd4069ec6b1c5195404283e42e3444bfded6a890e40ae55f8095bb6d43a24bdf41bebbb9bdac494f2f796ae17471d9904a6b63d522d6784ffe0711fc4a78e6a8620582879c20194d940bdd0b9e0ed33e5ca7a26d7df0425af50ded27409634be43ab80dcb70b392130423474841473de2286b05fbaaecf008dd8f1671f79b75a1a79067617aecae28b0dceb52a513fa21e0225405c660e91ac37c94f1da8b68b682bd76c603b0d99228796d66dd859e490ddafce681bce62a68c6ec7e8873a7e0f4c0ea28fd399122d17294b9bf39dcb9a9d39b2788f9f3d968b735d350848ca0ca7a528f2179874e09ebd4dc0d6933c78ab5dc79ff89739fc0c6c08854dc0db4b99b6be2d286b123d34555cf8bf5a96e3a46963fbd43f92440bb10e7e95aa64b89535a97f38d734210f88353026f8684b3ac2b57838a6bcd09a0198d24bb267f90a89c236d469d1587894a19bde81b87c13a9328f99f57c8933129e26139202aae7961a47ee80e585e44693f532bc9335362801e1015d698d1baed9f28e707ea8c452135ef7f286b59af3343439323ec5bfc1bfd4554549107b7c995a89eac42e318dae81c31946a866d1f0998e19c39394ce84efd0841f86770db4f08530414bc5753a95acb2004623064088ea106df117fbe3f6600635647662b49a7203d71a4fa45103fe3a8a5e21a0653dd09d1c924dd61fdf0421ee2ac6b7114f186a472d59414eb6eae29ed986baabf692a2b9ddf547c0b31b929563d8ede8485c187d8a879a48e8ea9768ef36cc554f174046de215e12b4e3e41b9ee0654e4129749b1d2e5c8d08b9d7b11499d873d813479727acf863fc7d7cf2ccec54a705fc507942d1a851c202fe4d86f1f2939ba70032a6359bd7ae37f2957df4ec8a5d889d2655df1d60cf17a756a5dc28c515cd7d5c0c44e2279b0b0134f9c6cc61d7ba13db98b8c096b9ab6f1ba0111f5f1d2cd35d0a28fcadcaa0a74c28c67521b90f355dd8bb05258876245a9ab24b7595885bcbdabbb60c862a9241ea2c1adf403ba59da6922ec77e133707ca749ecaaf6ff9a48b00b2dd8e6bd3f0094841d07fa7186b6b2dcefea73ca0a18c0a781370978e8d2d8664659a11cdd2cbc7507bd78a14d92694831eb0c62683e75981d34f73ef6c7ec43f226c346c2e9adc81f81246ca24fa91a046df40a0f25c1fa6020d542a7a8b250299d9d8c261d37bafcc214772c4bd55780cdda0f6ab453e3d43a2d98d1653d4b22d8922be02b5b980a271efb4e4c566c3089a04113c4f63a8cdc1327f7ed891c8ded9bc96d9f6d46db5767a8fcee972827ec1ab44305610d155bf6a7f1dc14c86c05dc776f62f1415638cf3392b54df32fb2ec922a646e4845d9259d526ab03d24b13d50853ddebc6c552c74d96a3ceeedf96b2852ee960d3cb52fc6e0aec9cbacb475075885098dbff45c25c1c86aa44dd1be2e9095528230f9e5c9ca1805e6d9260ec9579f1e4ebccb386b2b55fac6cabc78612f580f27245cab793c1952b77f22d2d06a511712ff2e3d6fb98d9752cea444c0f30b16f60e12eb2aad98441471ffa8a6aaeeed36736e334d2e20b292198bc3f89ea9f4e57c92f2915d28ed6b9658789cd53af1af7b90b429ea89d39655a306231afa13c4c4d0c0f479e746623513905c2315b3b38e0ec47c02156d77070f985219b7f99974673fde193d95c9bd9df3784b1d3e59423654968bfe175cbe5051b2aacb2ffdfd9d4aab133479376aa3170460743620b4e4a465b789d8225af452bb7a7a700954cb4b07c37a8125b0c342fc16faaa51bd0f2be941fd1a52637973f6c985d6f2ca044b570e8a48b83ec74e1eed33ad5779862b0fdca4fddd9cf9e7aadb8a4b83a87f2bf80f17e9de8270aebd09c14b2c35bd5549fa6341ee28f8988cb7457562b3aabc269d2654f112f0e3be84b28686116b34b0122f34e9103fcb5afec3b23cdbbb3984b4498911b0d16d896d72cefea232974208b008293341dd55cf24e0d5e1bee1de8e02a32a8645712149351d3209113fa8ee10c23fe50397835e08bbb1350446d82e12451700a26f1440c42ae91937c3b40b9de510015f9faa4912fd1a0778cf214198d3c24b3053645dc104192d3341c98085094bf2b81d20a7b94e7291f4768785723fb890ccd254d281d2d6fa47a6e39e555f08be0e19a404ed49c8953ccb1798ac125312af23760115fea118a77844a0884e0a83769054d248df1b24fe0ec5c5a1fba2cec68e3817d8cda44b04be83472e90e3844daaea9d2b5e2851f6f12cca67ab24070fd964003175a57ff80ea3e7183cef50d0ba338f206437ac828bccce32e26b1873e21fbcc93cc14ea7a1f32f8d74df66e415387ac58c0ce1b02ef389bbf242978e6ebb5e62362e980934fd9825eca7462232365bf292562e62a6ede277dfe5c0c6a911bc4074190aeac40b3122dd2770dd7a0fee30f5ddc8146431b54b24e770f003db4c1e9f5fe5b821d132902f09151d715f710e4fe8026cdfbdffbf3cfa0b31a3f6ea94a6dc9b6c2317e32278a3908ed7b5978c42d1f3606b9cba01b3eb8c99261fdc4aefbbcf380d082feb3411e942870a1316225b3faf3e52bbabc18047ecd4aeda6cf4a6ed684c44c6f58826d46512d89a6559cfc512314ab6a4f99a3335ae0cd103d566773e8d1a466aa363879e276967332e4138a5d8d9643486726fb2a90454794fc76b27ebf3d9e6b33aaef8a90db9af3cf059784e5e821a5604a669ac7756113efaa2f8759109b265277fc56aee2fe4f8e37fa3f6ab6ffbdd426970afe075adea516a288de9f0b954b0f61ed1263231525cb8c51ad492d6e2daa261b83bb1ecdba97996eec55f8a223f16fdce025f660261b300c54c95025e5762e9b7fefa04227fbeb20543b0e873b933d6a41eaa8a2c90086885866671105cb6a270c24ce2c294bf0870e0ad9f90d42b154039512809d1bc7c369a84df8c63d42798fd6218db5a5795ef79392419a209e9791269b1211f9e36b51a0ee7267dbe95d09b3d12b069abb71ff66cc6076cd4ec0fdd512309ab24e78ae50bc7c36583f89ac67990396056d351b11f3f288c81843308d2217e99c7468df3204e6ac7bfc51cbf5edf28d3815b82d7eaa7c7531a35a76865b63568da58571df210f392c7aad8ce073242c210dc61b19c892d1ca039755607526dd4bd217692119722507cd4d1cc06c9dc6c42e0daf3ceb2c1533f9ae15668273b19dfdad8d8ff9c7cc3d10558de76dbc3202ba216f53ced4e4ce77cb68012dd3dd3ce5d6d7f23e1514e141c1cfed18c9942fe80b5c3f89685b88e413e0550e6ee82d9720766dd8dcefb24ca34d836d0bbd83312059ade8de1b2dad440f4d92fa3348da2830eed3de7a0881ede975b3779eadde393e99b9df869eb7b50722db24032e7e5590c026fc67822d8807ecaad2d0c3e9d93c385adf938c7e4c226a40704f07b9f7270eb7aa856e20dde85b659a0de9392e4b0c10d3f05d0ccb81b6c4f0b5508b140a3511bbec32ff77397e560ad841c4b637e7c3e511e2fe0383fc81bc7c420bf8858ec016c15466a3eae32c56d17ff4a11f4be71e721e14523ec70f9ca173c4e15522ae0c6414885365d15c89cbd5396eaa6dc98b54323f5b164c58f2502604430cd51ec4aedea99bc5bd690bcea12c907c027e92838b35d2c158e9f6616751a164642d3098e2ee0e9f18131305351d75b748eafcee645cd16542c09115dec3f7fb34cf9aa95209a387f262257242a0e31ed46f322ba2814687eff9c2a0f5f5d239bbbdf32b5622636a723f01b434aea6ce1d6818634a68c8de5b415001014ae00a55766b8c84c832b6862319c1dacd160c8e41a8f7434a2db5617492e00552eb2bdb07c607fd82c3e05bf22d888a7358184fe26f4292e74938b24928c7e23d16b5dc99fdea5f01c32adf01ee78c077a70f3c1f8717143760367781037c8a8a8248c825434997af03a81291eb37778c93e32b57616d27d9421a1142b6aa60fd6bd15aed2bad3ded4ed5bbdf5aba25d09d5f93bb255a5e5a3423ba76f059c83f75dca12e3e534c182ec2d75cf5974423397a7d937b5ad0ef4f3c5a17a641f2a762a87fa384e70864e1e6cdc571bb038ad62ac31c1720f32766e25367a1aa64d9c080752e4e41936541d09e83dd9df6d3a860607f7d2294d4654a0250731d228ae7df6d84e93a987a40a4357cc351a65430c9478725c019217f53039489d8c1793312a6681486684975f8e717637e9a566c5cfcc6b787cfae0e5c7d10466589bb74d744ac11c19e9f6d0ea423ed68be3c68ea6c825428c9311bac9fe721a2af4224a7a5b12fcbf02d1fb8cf306b20927a576b49e522b675e5232090422d77cc93cbcc3b97f2a86976436ba85c80f2257f50ea8a3f1a6d4dfb8b46423925a36d493f676655192c20237fa686a82a1cadf2f7ed6177a8637c6055f6e5a26e7bd643593295a491b48e49fabce69ef46ed6b4d6ddad64028ab874fc0bf3bd4b4d54a3552d4e1699e03f45c51a5a1cffeb3f273970e34b6ba5d90d7632491fd7ff86ff11b6c04855f8301cc4b0d532c7e1df4ff06d2a3f564dad47e0b54411953e87e1dd1f08cfffa4a9b5dde7eab0cbcd31522650909a6d415d675adbcc0eb2253fb182ca261177220c34c02552cdc025d4884f1aba848ab03f1a9d994c1b1d0c64aef6ff6616e6398700c557b191772a736a45f1251913ab3ce5608035fb0473694e477193553d8e801e854084a4c6a2342ba882a4d0166096e92d3869b8588132d3b73e4749f5954044cfa188c3d3a7537403e597d8d5a8a4c9b1e36b2953641ab6205b206c8b3150da0045f2f1bb8b030ecd510248df0583922625c891ede367646583288ee0d202ceb35091ef31f8d8fa779c17eac0fc68f3f47402c212cac9cff64d241c9724f5704f28c44fb632cf23a446f7a93110645402f79a3d808000679f4b605b0b4ba518dc73234b0fdc7149b80dea91aa7a0fcfe9edc8c749a4c7a68dfdd6f884f2a327a2d8ca24b16613303ccc8e81fc4b6ed0f31cf92b252c8ff5de749d9ce66941a30487d1995b2ac044935e8df915cbff38995f4dfb45b015ed8decd9e854bdd76d54de12f21c1f95e5795c2b6cb69f0da4d32badd5ad4769305d86ec57ac97a6af585dc2f7bf569b39b78e7accd7657c020d83ecdcd91ea0a379493733eb4f635285e4b36694fe3c6bf589b5db4604f0b6bf41211ca49385929d8ed6edb864f1f69269f6422906ed9c1af51d791ae5a5b2bb8ec5b1775074a24d6948f4b9eaa9efe4fe33e2e513dae4e7d6c8b516a5382a27f72167568e4b54485398b331e2b011916ab56e2399376e92022e58927c9466705703cfe6679f567ff02da3a6219f393c7bc065b2d33f90b71318717ccec1aef7ae0d81c7f42604ccd3be9248a2d7f63d611c69cc37363bb79f65ddb60ed5b0ad824595b04957b97a9deefc7d5c9e06a91a2e4b7d7406054118f481b9ce83da1642a42c1a67d97799681ad30c700be84ae3f0bd7aeebc0072e7d918a7b969eee0ee18100bb16b8d628f460a92692b2c784a9315fbc685719476c43e4fa3556f8db74f866a92169101263d516a107cdfa3ea9194d0261d4c9b711765aeab849d2d00766db8b3eea23f737fa8b17c57ba10768d78ac8866c61369fe980fd3f5520b218975cfc4c967b11c2630d876b473b7a38d4aa75b633b592832d5accad046954c877dedb9ba51e561e41b3e46a75018e3184fd514de7a65549c2888831ac0c59e05262fada64185a7d154de3043354c034a3a35c534a9398bcec1d132d569191c6591de21bc43433e4331215439ba95e242433646a8d37c906d0eff35d43aea7462c8f1df622f69b57e0e6a7aab4d87e587825881618dc083c97c76e32f8231cc8e49c138c5fd31973de56067fb6402c5a7491a66fdff03ce02d4c00994a60e364faebd00a96ddf085b9228c38ee07d0fee8661ffa3bb4c00d75580bdbb4e7eea8786573291f3f616aadca3127792244e0f7422b77eb6d072877d4934eefd2eecc4948c814072d2ea05ddd2ef8d859fa04e4317bf1e23850d6999cc9cc77db7a6287f71c60258884620fb8f64e03d007068418566a453405161d372d81f405b9bc58af62793dceb08e02c8f4b34c8634e19db493cb89b6a4db239032cf517d1e9f83815691d70f778b370bd0260f37cfccf6d72fafa6f3239873e63ef9abb8648eb6199bc8fbe59f15fe214a6224b8627993ff8712e8fd21b2b5b2875e1fccd46daf33961a4458cc22a7a6662473c24258193189771d70916553ea343049eab617729bd569d525188b2e593d16a7426b7f97cc34eaeec2a486aa939b4f0b076f67a618e54ef7372603e8f32d5a7520951769b39147445391776f232253b0cec67b248139eb57c1b21902e0e57c2a97143ed8525854d02ddfb505cef905cd2eb3278b56d2d494df489127cbbb560e5415c5d3fd65213cdd99420eec0635a8ac568cf43cfaf8e7a7595fba1a768f1fa7c9d9ad9760c540cfe4048b6d09a745e346b73f997d2e051d92d950a0b7891cfd3545ca3159d7ea11be4df3988461f7f433d16631a4804a8a111eea96a5b2a64219d01bf239040ed4f0074d33bd4d7be7654caee31944d8ba93e9b88a85abf703589f6bcc8eaf4374f4a889e3c3e080f2e5383ac85a084c184fa2294368a0394a4699a3b5e5567c353ffd921a23370c97d8c2b0d3f26e54d6cf2c28b8527987731494061a4d600a714159e7010af3d88035bcf6898f8a9bcc1a0a20d20e29860c7fabe97a4837b59981610ef4001acd6fb30fa3d4550d590989a85efa05f9466b4b50aaab68227d1586420e1a2c1e03e4c90a2f97a25cea4dbaa5aa19cb34ab2fb1c305ae67521bda4fe7dbc07ac30a7e521bced1d0a0aa19bab03f0ac85a4c3a5fc230ae9bac2f5cb0cb50e8ab4f09da3203e5cb25db72945c241bdb843879019cbd402b0d7b376cfdb62633311df70fb676a3dc4db153ec5a0f8b2553cfae9758ea5865f91c0440f42c105f12b8966a993a89bf1904f7f862b3890b14d5c33b78d8805f20fb0216ebfafb301065d79cf7208ec1d0dccbce2df9470d7f889647aa36bb6340db4536e9f7f231a6cbaee1e35bad223dea274525e81fc799a1baf477250406d51651b7e4e738b74e7e71b5716a20a7fbab9f7272fc510eb140d8dab48ce9c38144536c66228231e885d19809b40937bcdc73f5b8ec02fcfbe55a48ee8843a64f18d41f77031189fb92f390156df1f77364a0d31e80d3196c7e8b059d95dc2b20f245892ae8bb5534286cec741f2227b9739846e122429096b8f2b8eeac4693d50d1f4f5ab2e50442d1ddd4d85d58b053ecc6708f7ea0877a346eb28b13c9b856fb50eb1871b793c0d0e937ccb496dccf3e2157a6a7bf092ed8160179e4d562ad20ff595728d400d088c14d3d67c256d6791d0eee16a88b5a53309eec8393f443d735bc5b5417b0482319a1ed1b45f7648473b150aced4a83e51ec6094fcbb8a800ec5789051418cf731243d8f4388f2a7205ed86008e257fd93fc6132fcc3c8b2e75b8a4d9fea74b19b05148ad7b3b44d6dffb5d34ed9bf62ad77db67063b5e066ba3f43e36d38b35c36d0503ec16a53840a5b4c92f9faafb7a36969a75146b0e72109db0ac39bc6d43eacfdcc603c28c16bd3a26b31d237e31a0088a439b7ba45aa1a3698753d10f0899b065f7dadbe9b663baf7ae1d410f3571f0084030ece21dbe951dd9ece2ef88254a431359cb8c46ed977bdeb6beede03e2c6bf998e1bce35fa77ac05caf784887deb518719c982447e7be3067334c449580d128d5239693ef7f0a499bbe194582168ca950b77e4d2cdcd2e88baaf1993a190443a5d2822f7f6927b0e85e03c755531ef881217d09ce575a1dcd6ffc18b67eee13b891b0738eadfb5404a1808f5d253a663f705e811e1cb6ac85bb3568128d86bddb21a857efe57b3485ac3ad37bca29a055605c862b81cdecd08dd3c7f689744c1ff26974d3602c714bdb2a02f2cbc141c511da248101031c8f44eea64e987164ebfddb31043a4eb8cdd8400ee478cf9d2f2ed80ffa9ab48bc5ed52fbcb0967fcdd1fb0db8c2ade05f65b2f72122852cfa07683b581dc715221ffeda77ba86c59b045b3f2f74010d065cefb149abc65bbf8420035892b6f451e3a3dd93a195913251d04d7cbc82080b80094d5c9a4c9234875c306b4f470c46776c1c094a2991af1b3f13ea09fb2f5d81b51be372240953e0c956e48eee7390aa4ef10c2c2dfdf946d0a7a98c6191009df2c1f024d0636caded4e0b72a6669194695759849afe9db4fe609106fe0b61ab39929f052af5ba8667939c33e53c58fcaef16ee4e520cb116ca711db6b76c753d302b62c282e73eb1d73f1260070f5d38e660698d70638eddbb09641b5a719035a69de266e3c79b73914ed06219a87053b185e61f4f4d2e393def0081331a9f9ecd07b3c360b58d7eb65fc52c3a0d7a67665db20c10ba283cf9782fb3498a0f05619f654c2a32fe81a94b538c5cb64aa5dc3111f6bd2fe776bd0b51df859c9b99e004eb4bbf6997fa525fe662579136df8ed437b8f38ab908242c7322df280be7997cd6b2d42af382ba241d21df5c4a7fdea4a49cf8f43a53746cfc0d0883c79f4cbd418813b9793cf07eac2058caf13c6ae002b286d3ea11e1c787b36a2f851fa751cbdeab6792331d73f1952ab3057f90f9a53ac072375100eb1d678ef8a590357d05e31a69dcb053b195923537ed5c29315558e423e42e93635682a4800d03619debe7d14f98719d02bbeab9c36abc08868f8a7304ceead0a8ad309505c00a522fafa54481d63a6e21b5633a76bbcb7e90c0a77d8e371f78aededc7e5ecd3751b2cc35d7321a848e998564db4c3e088bf53545c65c00247c16975c7dfee483b18a08214a7b7e718c643d0c7a544b4a9a4574246cdc690cf18e38252f002dc40cae4df8d143e846889f4d4436aa9734ebfe0f289ac9c3c2f2366ceb8a5e4fd0349acce7cc8495093d26158c1446518630a3817520cf9b967f581809b16471907fa0879532eb7483280a901564f1c300d79bcb7695c7cad0922593269016021b4787b5fe4ffd591ad025101ea3f1ed44c5168464ee48836a3f68cfb0e79c9ed8aa36748a82aba4c59c558b1889562bd2c710cb4adb284e274212076b27459b14a85c8c58266ad5fb104d5447364e9d1a982afa925dec463268261450e036e185526270f0ac11a9f903d8c0133e94f107cd29d9f426aa13ccde7e3f6321587227b9fc0868b245369c781068281e849aca88134602be08ea4fe1ce27f43f398570994ac88c067cf2dec4744aacd17097e909d54d570737d838308941ebb891f28159cbd4c0c82dfd4e1d07e672658c5e851c3c3afb6ad2079b81629fb8cec94385742b6316c2cd72cec9b2f27c067d63a1ad994b116cddf74e33036bf7da44ae3a68a4a0e508447955ef06de3bfb8c12376f35db2290a638422358a78eb796bc072708bbd54bcdf96b27492f1989720201c2f651421f1013e5b8096a498047762db89380c6e753bc7b5c002e957d245103f8617283340d3c881cc4a074a082517e73f45882c7b89a1f61b1f5f8f85af90b13050afedf89297831e4afc3216fcd6ca61c5d2cf7867d186e2291acf4de9cb788767d4d7ae3d62106b747175322cd6e48a5972427a58fa7e7fb8e9c37e268dcb4f2d50a6e94595154a14a4ecff3b65bec137f663b9329ae24b3c9aa6b3b288fe974bda7ceef38b0d228862ec59981c3c2bfdac0fd62ecf43937038d4b4aba51247068fa5dc8f41e80e799d06a3e33bcc11d111e6d329ed7e1c42804534d309f97408aeb0b1c49b41b518b4eb4f8043a02c79e44b6bae287693e982150a214003591d62dd6df5fc00182c0f73adc88c23b2de3bb1b855e349db1736aa1b5618c988bc940f14f96244d463af73fda80fa108d0e4e104524bf6d2f24364ba431666a4d712b63cce4d0e2a40e939dfa3968d5f3b08400bdbe627a3e374a92e6e0bf5af5c03e3d5797f3b62803a34ec8bcd8749862f6e62be2589ca43424f40c0b7b4c4f466f796768fd7bb0d094c1877f2e8efabc84cb45db7b710d8f400e3840d70147ee5e96896245c80e407c39b2e38bdce29ead73e6280e3d14566f4caef6ce25f0490f9303410937b94d6fd74337d4dff898d6eb2c17aff24c32519733e20a5afa1559064a530b498b843634414d256b51d8f56a99fbd9ea442454daf20be8fe7f78c230974011e0527528cd3a8b32c0b2d389d604c28be9b5e884c69a6e9aa121c7def48e9c388f6b905ccd7de84ea543f41f1b69d856593c7f534bf1a806bcd0ee2d276171c04c3838af43f4c45dbaeebeb0bc48df8c88ea407f7a427949bcba1460cf33d0f6f143ee0454dd95207913d83eee76a086fd5d3df345e8865a55e4e2aebe6e6fc5af04ce150b1cc3b0758d9e46814f11f7a001d930f792c20d6aa8a32902bbd5844658da2f39984f1759ed6c7943f90075aa4fb0fd4cd6f12edaf44030ef2f933a15fb89d12c782ec9f64a88851d0b27910e12d46069fac2693e088ef4e175fd32a6341dec3d9480fb0d559e52b58d0fd6f6b612f80f3f6bcd8f55fd11d02273e06827a3e4c06baf314bb18864c387ab42c403228d3c323483326144ebb5546d59a15d64a6c406e99bee6c7da6117d2f72df00e2741c04b0a43f26fcf7a788856f34a38bacd9bc1ee4ae080aa4e920110da9c6ff0b3a754e8eba07924eab280d2bc02bad23318c83264ad061543e81d03c0a667afcb2d420b39a6184e0bfecd0b8e092b6ad23f2e201aba1d9853a8e4d40c8fe8f9b9d48befe2636bad21c3c20c11f2db695aad06bec4f7cbd5be26eb5a95c728d21b0bac218c40136556dc392af6e60dc6bbbc689ff94aee350134d03ad3e161be99cdc466ded12c08223fbd236ca5210cf8e22cb4aaf9943dcb60a3f57b39923bec1d15b8608d8559ad65f81a0d7a49b5f6401aeaad8685f805595e1806f0bd9216aa3330d1ea7c0c5869fc3643271039f9cee5744e2a085b92df2656fc51e91f2a763bf2d335e860289f879852d2b801a01f572d38bea8eac6d8d9c89d39129cf627119527557f49305ea3830d64325c36e12332b9ac08c1f8dd1809d0b2628e211f1f34c24a676cc3e423244f8dadb212c02d30dc2e73397c651347ba0b993fbddb871526f9e031da6a2e72106a0608f10473b6929144a83ecc4dc219986598d8d1bc880994eefb8e68b1edbbc147cff50227b73d984710f6c8adfff16052113c4ef6d23820c9c4fd1b08d06d15bb5e0f37264de68c80cbf3dd89aafae57799361bfa830bf437048a8f7fb45a30d815271d7ee7a45f5a478caa3b769a5db755f94f13d918d9f5cdee5d442354dfb6a7f6ff9f833ffcfdbff3599b02787c280440f128ce52fd5ba999180c2ad90131175d72689e2ca2be21d0b7d5be5d6209914608217b6fb977ad0b520bee0a78040679c107638733eed178bfc6cd14a33e3b1fe6edbbf34103a41dce00cf02c60e51a71da2c02c244bbcf2c3cab515cd5ef44d7435efa29166ef5db4a25d9ca6d97b232d53918a06aabcaaa8884ec12cd25e2f7a48a485eef558940fd8bb05816c1344f998b79fb71b90ed99b73dfbddbc2b9acfb26f286026925f70e9a39b81c0a3166c6a09c354ea58746c59b065c1a294c75e7d58143bb4262bb207ad358940d247371dbcb15526907b7f74af93bc7347f13cef1998b77b9b7745df62c783ec78f03c50747b479fd65e667ab7b1bd993b26ba771bc74f09dbf9981c18e4056f60f6beccb458bb98e9f6f7c416ed049bd5a1086c6e56af96344c6cc243a6274e0d59b0264c6c1d232d73b3586da4717e3a07a8758262fa26080868dafccc9a5610eb4776fbf48d4fdbb0baa6a795f3a303b4233f593b3a466e58467a6e78a48d4e500e10cecf8d8f4d6e566e16c690d08cd0f9cc286270c4073e34c081028b0c1990200738603981851ffc200a168ca298620903f022053b602c410a2e48c07282203088f18530e8200a5140418ba2496a490c1645866957a5339351582b0cc10a3b50816475770d8a02e7cf0b7684e3155638410eac70044b6b1610412c4008118e577411d4a25e140995c06e610a5d58e19986e3155d4401e7971a18d4200a4a70b71630c0a26a8102032a44358c1083459161da55e9e49121400f06488266a405c4c462f852bdfa3bebce341bd88ae3155d64f16920ab0b2c5299076816c2ae4a594004b10021ba0b1fe0fce19a0328c4985e304d2ca610410d80bea0054f1758b0021d1c49228c9b2337302041080646424a08e178451738f88991ce24ece870ce177a3a315ae2b82c0837bfd880f064f082638c5c08be2db4186bd6355a3426e6e5250cffefeb1d7a583e3c5115df3551159f65af117b583e3273364e879d14158bf1a2d735bf5cca4e33f0f4ba61ca892c9bf3650b8d67461eda6adaa22ddae2993059abd5d362b5aed684c932fb87216dd116ce84c96a6a726a747670ffaa993058ab75d9b4ae9bd665a43561301e9e1f1ea07ee59930180e4e0f0ece84c1aed6b76375b09a93fd58a0ef0aca5a592b6b65ad2ccb329ce3e064389147037398693cb49aac26abc96a5aad568b8787870727b3381d2b563361b41aac06abc16ab41aad46abd16ab41aada65751e3f17cb063be6434eb46603f6996659719a533dbe1e9a93835bb8c89797909c3ef9065df97655996cdcca7f503d4945299655946474984b490a531143d8a8ab021cd1375a20eb4541cf17e030b2f440516da25483d2fc8fbbc37b0f038288ab0c554d658b84f43dc6d2974aeebe6e4e6398ee338d9cd5819eddcb890d6a5c266d2ad6ed22b4fa705b3642fb9edd361374b0acb653a5d6b986055603ad81418126ca7438c074bd2610fa604637588618de5e8000a4c04c519cbc9c172705f63f56c31656f4b72d2eebef2b5e56cb25df3e32cfdbc341ac0b68b460317d6625f74e688b783bbac58b6c1785ae8f12fdd23273bae3bd771de46e311d14537a51d61b90e3ba485407a81a956f55b602fb0648f883e45bfd1936a5518765efa00b274c83536852da6b20808898db98160b266d2a457f512fc7007559fa9ea571f1a96f85426b5d561571f2558f90eead57c2f2182e2dc4199ce8eea3361424b5ad567d26af2e304e8c9436384215688d561dfc38195e774268c88dbc1cd855813469465d67edf1f862f2f5974833bc4c27dee66c27440409c11201ca01ca009d3b158412cae86c5d9b0264ca7a3e3a3d3d2d19930ddcd54f5b31d9e1edce758dbcd84e1b81faec5f97c391607e7cd082b13b130114bc412ddcc9898979730fcbf2f8b6e44749e5220202020168bc5d2d1d1d1e96eba9beea6bbc1fd21ce01cdefe3808eab08c83ec124458784bd4e2f5538bc816d60ecd2124baace39a97763e5bbd460920e12593876286764d329b30e2f5995a001851a042d1b5995efe2a9c293061a00ed59e489943e3ba6d10c9c32f3ca4126931d58f9234c291c81f06460e5299d194062ded3e92737587f811362d015c1c2045814c162c698c9af3cc17221ee5afccb0e8ee3b46da39178e366a557866911e8a61c2b8d84ae5c42cb81e5b21b3d4b48231eb025d9538dc89b2d58969b8f93558b493353dfe3ea8e6626fb6be963f9d65d8945b7c670bb6601ac5d5ed48134ba6c9bf670db429a065e7ab3d99d6c30b6382f5a6bac97b44a0905235e09ac8c316659966559966595eb32e69c73a6269def10c4ae2a6f08a3cb8cc19fc082e8092fa09cd882f6440de909139c1a085b2ce1c2085d388207070b430461a8c20a181c41054fc480f444153409321c78820c8690042e244187081240f891c107b2600227b678420bef892d4448286d618302911d64e1440b142811069b1314410bac248e2843075d24f901fa585ee8428c2b64210a3308810ebea8e9809223c22003082870c2e64ee4e0bee2084d4c011b421264702426d81713e7051fce0e48da0e46385ec1c58e86238252e774bf800aa3304213052f608273c113d210b617844114bb60c2929a31961cd1f345fcc217a8700310e0400ca02cbc408b5292177cc044b5888922d33829444ea2f68398182a4a4740a941131ed066f4b800085f9842167aac2032832ad840490f7a10460d6e78b486f36444b32802d74413d9771c26d0a004f8a8108424216041ab0c1e0514e1044d6a82f063b3842662903561839092264ec0c96eac0525c07d8e84e3155c60e1154126663c502cb12581e205251a2c0945e1a4834c05c72bbae8c011a2f6839818222920c2132536458270051222d012b670e4270a4048e2880b96182571010c6a5023acca48c20c98c08229e458e1c38e912435c042055bf0000b5113be22b4600b5a0b825868a079385ed1e40976322704a49396d42706e66318569a188661d8b10de3b01f260642434ed88e9c62c71969e442df36d141d1153fce6659267a3c62731e29a1905e9562387afc3204e8e9f11fcb23277aca032010640239d86b4b41b0c74da034b22321a4911dc570641f47d6f352a20894d9820077435dc112a0876e061382e5b2d32540abea086ce90616599661599665590f9d8f53191dfb955a73a263fcc0e0b26e2cd258639c36731ac1f11447eb9f06eaa05993c99898979730fcbfcf5a6c076a43690fcba785e5e8ecf05cdd4c635de50fa531a358c52e0c03bb060251159b06142c10ddddfd45ecce00be24290535f6c4b26b5ef3026d8753ebacb1ee9e57adb4e71259af62f7638992963e68bcada47b3a9c734eda2c11e08b07f4999c73621798392c3beb26aa0c54e1c6ae6a23ae3e035313a61da6af1146bb9447c5e822a93c06e64feb1ef0000542c02a9cc29a4e51658cf790b039c33493b2524a834c91e433d24d329973ce39237d965936e5af0ccfe59153cbe608e67a66c134ee88305fce01d3a7a2966197d22458a6675f5a682ac15a5f679db5e79cd4524aa9e4f024829b7ab1cd6b5e5536146aacb17a29b097974caa8c466059af959992524a9b06947e578c1d1ec340db210d494d66f232763392528c088a2b48e714d78eb8c2d705e6ebbaaefb7518ff0169ef466073769d070e5f59965d57100e7fd7955d1a88f2713d0bc2e19a51f044f016349858f9f8fee930e63e9009c420e04c0408787ece6f7db386fb1de670087737c1b14f2ba6fd9bfbb94edc59a63a8c1aceaeef1aceae8ae9ad14b3988633ec5eb4629a584637336995b45fafebbafa327d492c33e9bc9e2b9765199df3d7cd1d05ee67ddcc3c0cb29c18e0c6bae6e99e66d52e426593a0298bf8b47e805af2f4b0a4ed82e002001c3f223c8b7009d806a52d6983b3bb1bcc73ce0cbb404a678d8d498d04496c34de50639c51a38de80280d0b30e3812216681e31270dc02c7cf99045f555479bafd071b80e7ec64f7e3b5f3f128291561a08c2ef594f6051279aeae486c9189cf920e594a7a92f05441c5890a2bbf834487ca981c2870983042e7121dc66f5bec26bd349060e5a9108a670f374814e950937382d6fba62aca1a238ac02e0cbc2ebd402b64024120f08021a6100b984174186fe262b646b3d9dd9d4d3a35307f174e8ece4ead5ad7b40d8ebfe26b1ba1b1e79c738259d3a4a669daa9a6b57e8082a6c6d3c3f2e9ef03bb1b476a9aacc1f1f75e9ab570fc4f3237023431bad81fb1398f011c190018624dd58dd8bb3c6ec867aae823401362459790121518ea89d828bcf77a7b5578519409a650c0b1bb115ed40d903e015327e04d763d324c23cba03e387635bcb40938765675fbc7bbaa4b84772113cf00f17ee3b1fbc10381d467f8c6653aacb737f310310d86551f32b10acc15a4c170f81be0908943b09d7498fad67136bceb668800c0856478c7575f5d1296dd0f948fd0611e3acc63bec98e8798ef808233f5ee8d1d6e967887e370730c02c62154a416e9904da8a6431c2ed39b6481cda11e27bda20ff5c41b62ce739e3355f47fec6274c141b2260c8fd3bb3c4e1802bce571e6380106c003149d763fbad7ee0798cfae470ec7e15b1c2273fc7a55f7ee0d9006c3378e6fbc3418f6c25037a3ba016689bb1e3a9c4696c183005ec39b6fb8ccdce14601dc3c04ec9f0e53d7e1d260d88219e61e4883e1fa1c628e03d84c3a4cdd23824d3d877230fd0d371b816df876f3f6f0366e96d8c60370733ff101e631a9cf8e0718303464e29afaea963000ee8e7b84e8421fca22ce6003f8a9c0d60072335f0b6c967886c70e6978ce360f8954c2e6c864f3381a6f503d84044c9f3b1c433e98621155f4b97a5bd81ceaa13e9149ec50f51baf9706d5cda5f06689c36f2fd7fec615e2c5ce8440bc976efdcbb5d93be9374021c28e07115681a19ee8426fefcb6b68c17244f8b0db4960fc8dcbf4d6f9d88edd63623b7a217ad53e1da664b7281399e1a27a342622a424bad0d368a006cff01994d8d46df4a06c2966aae8a947a9bdcb73f8400a3c4375fa0b49afc21fda8375802e60ce66bc05cc56063007384375bff0b284c186dfd12b1ba70f538fc1917aea16cc5fea1e98fabc71251e7dcb31e0cd2ff7e6f0970859e3661a37779219375b969b230feae6d85ab959d6a8dc2c71be9b254fcacdb275bab94d2a4098966e9e00d64837e620fd243ee9d5751ac4631bd5d0820de14cb1778abbab20d852bdead97904122a300011ec8141ec2551f5118758a80a461c52d221fda5c21641e00210a6cd049e3a1dd2504f87f452847a423da9774861280f9e78db1de60306663d51459f65a11e5ac16c433d2b3502e804e86c3195893cfbaa6d1dd6dd963a9acd796d9bfc9c735e122588db83018018dd0584ae100f80c0bca612f292b39b48b84b80db83012e104174d8548273878253a743198ebc45b7d7eb9697243c55ec20d149c253c5ce44024e9d8d76f737fb450476931cf783b76d9352ca21f0bcbac76e9e7bbd44f0bc422aee38ee66dfaee64447666a9776a8043711b48f63fa4f753f87965ecd3975a6aa3fa7ced48913bc46d2b3628ba99c0284e54f97e9eff528a1995047ba2d91acb5329d897a8b7134baa854839edc969834f1ec9cde679f46565687b2fa70613356af8ca4ccb84aceb0caca6bc8c2e6caaa711a19a9d5abae49afec8f13a05eddcbd72741bdda5e44df64e755d6b42140d6c46709a9f563ba99930ce849104b7a60c6eaa9846459e2c3a4c35693ec2763f520c36a69a9a7d7b8f9346cb0fc8c9b3f969b332758febab90575730b5eb94450ac72f3c60496ff4e5f89498bb524746ddc7bc82d0ce9c3a742fb89c096be1fa54f13ca870904fb0429a6271d05354f51f311934229e5930ee5b4893652dcbe36e94e24a6cdbc0181bdcc84993651253fb2de08c72c9294813b07192bf3c998b43a6cf2e364dbe9db4bc0607f7a4599b0a43aa94cb295b3a7cd54c9db4c988c5565a6854f077083ac9cb1b0fc290f9902a44203acd4216d9a1e3b1e38ecdc56fa388b61114b1a0de05229e2524d8757119425d8f8ee229919e91513bdaabfbc2ca7575892cacac1098bbdbb77efb846eb9b3953759de56349efcac2137b28bf5e32729de4c47a67d2ab2c6462945b5b915850a7c30b090a5877588ed81c84c3a573e7aec4a28c9660afeb7afb7478fdc6c0fe7ad5c9aa8e5775aa0ebe3c9c5e79bf5ea1e895fdf59a6302c33a21aef29079bd6e116fa0c17006e621f37af5893764200d8643e0925e5d95d5e13577264c6545d5f51d7c6961871d76d8e13b649939f3aa210b2bb178c55859132606bb8957bce215afc8c232def8d8b06a7a5a39d815af5f31c6eb25a0d17683e54844422171b72592c974996e1a894d328dc51863bc1c8dc473939ce763433f37ef9f76d22ba0eca6574b6446321e2c5f9974d5a948ea0ee912a557a6cbd79eaaa4b2ea92ead3abeeb936c1f2c7b2a399b9de43622cc699f84d765c8624cbc9a0c8a6c874b29dac8a8c274bd2614f873253d22a79b904736f2991da6a7ab3e950665564507498d3e1141dea20c9763aec0cc70a3bc1306738585e86233673db8d56b3c52da3742e51d24d29a594625d54d993d65ecd896599d64a58180bd67ddfa569217995acada16ee648ddba4749b3ea63ee9f7b4ae7f6a45a91e43fdfb192d6d9928a7492a8c29e70bc428c26e01c794236c82e2e963085cf6699b5359e7081052c99cd380b464eaba2ce0c183b34be275cdca0c250240c48ccb04f94a860050c3e2c4f7860029520d493263e3178569e20718294201d95309ce09cc2e0f9c248c20a4c4140296104a124c6290c1c908074630a03c7042863d894c2284112232052f602061949d8cc669cad99a2470d1214fb92797102ef25e665474c194ccc9401455b309e50021d2b96d80143073740603c6102303a8033e3859e2fb6b0c40c188c7cd104234f6e5430d3859b2fa86062860b3b5f48713343c6cd172b4862260c3f5f78e0043d36c002a2c213335f68015511c54c1835402a7862e6898f8e2fae8a4d23d785553afb65474c0e3a70b4c24908a2c08556d7d362a00077bfbff0c205672e39cd851bdccdd38225ce26108ef12d2b940f357b7dc4281fe8b1d3c730aee25951157db296fd89aa88817802e8f86ac1f406a977023fd644046b73a5b0b789aaf92cbbc06c373a42409122178dab0c3c820a628581475041c85fd7ad210b365b3c6d70ee1afbd5643986cd8401e2099da88a3b31b70d8f91277aa22a62201d54cf060c6cb6980a3ec0d900962fc51b68c0f23528b1f96535e30d8de5770449dcff2abebbbef40ff13419d081bdb40de617ccfac12a769d4d6577d3a96937bac4635a379dddf39a42944d7bf5bf5771d22aab3652c1573ebaf4ffffb4e514fd95059a13f6c2f10a2013b4803a70edc05a1cafd8421846c8b62b50a1c48c173757c831c115545082192bfc5ce1034af878c1052578bce0e2a60b2f94a860860b1f2f729082991f235e1869c14c17412f3b6272681c1e2b4605c216b8c03973c1d18a2bfc64ac2624cd2c7e02d4b516427072c9ab9b99320b210880b6168ce0d60217b8ff6de1481696e0be0e234e664086d5f1440c58a8020e2721c0f4d336810527303da5385811348a269c391e2c8ce00a59e02260afb7bb496096989481c05d9659fb7df9392482d35d0188b4e781281fde4fe7c103e989e06dce8e07efa5cbce87377a8740e04d869a1e47db06e6eef129f794c308b6444da37975b7be7473e8909ef49d6e9df7d82370045a30db2f7f1db78121f08b1d0f419aa5d49714763c1e469fcfec6337e3658947398be1d9a69ebf1b22119883bcdcb8366eeac670c19b87c09e2813c1a3d0bb4ce720b0779939ba127b1b58badbe8cecf1c327a28573b1f8d475ba6726de5333ee32c47d5b0df6eef7d13894a17bd4bf9057b236fe48dbc91e779df4657f4d23d7d93a713e863f4ef2390e5327dbab28788b9933e8ab847d75e9eeee85a992ea5fccb5ce6b0fd4ea5d14db7a543fb94fb5454894e25d1472d1d8a2e33edb5ef6e2ebd5f80db5fa62de89dfedebba3796b4f830c9b68c5155a58eec0d2873b7ae9662070e9b5fbc1fb28cbacfdbe7c5cb2cf212e956427026924a600101a892de8c39ef48845df9a1b5dd1b76b2de9ddcd9c6644b77703334a04168008b68f18881ce40557a006b660ca3bd3513e3b1fa6a35c76a79ca58cbed153463fdd340233105874eeda92112cca43fb2f4c99f6a7d0821908bc957ed95f5d0f12ffdbcda35ff0c76f41203f0a0884c6ed4b37c84ba492a844427d9fc874292c9d02a62d7cbacaaf9bce72db860a2b31e5a191e870d2b43c64e9d564a14975586f2c4290125c6f7d49c483f4be2cf7d1653e76a48b6ece300924827ec5d43728138a8585c5f4aa72539b2e8a544dd474691392746822a2d67a520581d06798f4a62450d6d38c53e9d4046622d81401fa4d745a6b251981ebe91e99f1add4348ed028b1743867e4bbb0d4d37f473de5f943f5f77a514deb5751278252fa150a02f99ee195eb3b6ac807ae804326be4014d8fce127b89bd26ff13b81cdb3064f1b8260b30d4c8ffa7514e9116721159b6eff953e4b4f297da5f4195f0a2caa07c5df7d6800e70ce00f8cd105854ddf48d76636dcc09248263023813e443705893ff19b3535a7cfcf22bd221df5c19876e0958f25a58f859e812d9df4ef32c1f9baa39949790f893f94caca7b488cba4cb70df77d9f299f97467a75fabcc49139281f289036dd74599e0a5b4e7707aa91b81fb07d03b64d0e19d8124802730b102ca9f076f7a5c31c42b36dbe960ed80bacf4fbe8f7e195b6f1b38465003973045b4d60730cb673d2d6c2662e268b3de3b444708c0c0c2637a5a0020b98f9c9811364d7ae85b2a99ac084c91ed240d4f753c17e888ead982e88e66104111ee1c523a7123506d798a9aa8ff4461471123281d3097c406631f5130c84de747a4fa79b4e7fbaf4d7a5f4b18b725641228117ebd775786173ec993ba73b7984e8b02e2008206a0f24148974fa05d4abc656dc5c3e4ceaaf26d7cf845179fdf5a4fe0abac6e855bce20b49703d56d32b89ebb122f5980de9745dd7751e1a5f32321d56167c95388c0273b1a6aafe0aab4c8735c7e42083728760cd672430dbd33d1f3b87b8a6aa9ec014d2af3b733aacdfe52750afaee797acded3a6e870a7661605cce14bfd4622819a4e87f52750cbe9b05e0625b41c5c7f2db97c2ed6119b2f16f72b9f7e7d3bcd9553de7e5df77433e9282512e9a35fa7d3e92730cbccd1a531029f4ec717cdc4a7232bdf36120a3c82022f9fa95ab9576baa6a04682e9fe8722de9b05eac0eebe9a5938ef2eb843222fd7451a352e927d209a57447449c4ea76f271008e919de4ea4a38c3e64e2119849200d864fdfc021139f4e0003f4eaf4fa1e80e855e9f541f40a65f47a217ab5bd76df2877baa66f9be4aeae479fc0dc4f4ca7dd95812930fd9683e94750607a141c4c5f6202d39f326024034b607ad2bdc1b4ebd1f802e315bacafd6a0eae4fe96e8eadd88408ea4d2020a49f6ee8a71bbae89a28681fb3ba4e8fdd7592ef4bbe49570213478c6dd7b65ddbe995406461205bb6f18a2f28c1dd49f4f202f390791a0c979ef2d8fdf0813d74583f64e212573abdb3fbd19df4ede226bddb5b96b039c4f5a257efdce8a6d3af6b3abda7974a27ddeba64b8361d36937733281595e6696480f8d4e2f3a290f99985ef4d1a5c13005b37d0fbde22cbd5dc4d84337bb6a965d2c5caf8a02b2f963e955c459dbc1f555e35931b2f5e0b07c4c79486cd7cea9391326620147a75755f42a2ee955bcc20b504cdaea02193837125cc4c371b283733bc1b995e0c96a9f6ee1f94aa3cd44a25792c312ec9c19b2b0b1c31d1df6ed1838a7733a9c6d63a7b5b15c186c7ccee80d09b698ca590b4e61165cea1eb19f09d3813d46abfa1c07b495b0a0104b0d6e1bdc6007c5cc88043ba8c33e473b88be87945792a01e9c6f4b1f4b87158a9a5371329caba9cfff1b7c051b3b94427af6181dd44f72d4d8f929053ccf52a9162c200a86af2429189830670d5087f3c93b6c2909a1b725db21865d1ff64cc5059b1b68d228e0f98944bc41e29965df57636c4a81260d1cb5587204a6375e6b8ba92c9373c2cca067803d3e0ac15c4604d3ce87794c3e60889c1dd12e4487127b83a80b3c127a4d69d74e55fd3d818de9b0d6cba0034b8f8110e85036ce18c0580f0c63af65e007597b81324461f34bbf3ea62a0824f2006d368241faa1c41e4002e72c9ce245c8c47285525a4121934ef92113cb970d6c3195576a30fd579e602b89447b72342c8574905c97e7813dea3d09b0602015943ba004c1607b8e60e11cca31d2abac02847182a88c3d459898c248cf116360161c7b8e68610fb470ecf1000bdb80638f0798c0a1291aa6c80c3c1d39146f59d6cd7014f41e3e76332a1c5c3dafd6476c6ba80a5f809e74483d3013c92cb85a0bc6baf180f2d1611ebb202c8f19de436298cb34e5a155d01d3a05d56175489768ad0e29939edb9292241dd26b55744871a8d78687c3a5911a8f8dabed8494784ad8102bd4a3eda838c12d2a4e30d580a66a49c867c214e9008f1f711e33f04edfe5f4ca86d37753f46ac7e93b9d5ee170da0280875abd92e1f4fbaca43d51d5f9b0f17a95a05e5510e5838e7a1da0901fad0274753eaee778ed6672809946eaf8f5aba391ad9a9a1aac03bcbccb52031bb1918e5faff70ef31caad1e1e5d0715139aeeb3a74783a88b8aeeb362e1b33d47b3a3e64621d609ee136c02113cf001ee1716d27cbacfdbe873b98da67ec06d37b44b05cd8eea657322cd12b1c8cf4ea62a2c351b9a1f4975743d5aba004ea90ca1f9b1b5e431656000f05f09ce15000cf16870278fe70f8d82a55ab540ff5cc9898979730fcbf6fb6e8f0fce1940ecf16a77478ceb00e4fd1f01b376878b6f8060dcf1fbef17abfcf5a19198cb7e3efb3f6e5e51e13209ad3218b4ed1ab9f9eeaf44ae5f41449af767a95e3f4b48a5e7da7a73cbd429dd24839bdf469399de194e5f4d249af5c4e2fe9e5935ee9385d393d8f2bc3c5e1963abc6ec3fd3abcbe6d5c8e6fdd0cea5b4733f3bd87c4392ed31bcb655ac76337b3a2a3e5d4725b6a9961866e6686cbb48d6ac37badb536be5ee765e39e776db2830163871798596e37e9b03e763d76f8f57a95dbad0eeb73dcfee9b0febbeda4c37ad46da00eeb0cb79475589fd272b90e6b3dcb75b92c1dd6c7e8b8a90eeb576ed83336c0dc4f700547a7bf2e0e56f7e4fea1e1ea70f3ec993f985e0037539b1e1600dcbc1569c12f376f36981edfbce5607a999bb71e4c7fe3e6ed07d3a76ee66c329783e9c39bb91e4c1f804bc48eab5dc3814252141d2455f46a87d0719c5ef2c89e2d0761c1f6389e4ba9c6f6a1cd99570a5b308ed4a5913804865417c7d5803aa413c061b69e05bd0960204228cea457d0bb2f2f540c208c711d62a2c3909111ce1407a1837eba6d0f9407e0a91b80dfb8116f371dd2ba4494692d480b32e2bdbed4ab1a3ec3ab91a9a2f51a50d680307da20575484f030d7fa90f5f5460eb4b88060d254858a19c5e6515273abdca2a41a11c95315482a650828356378fcc6d11ca71808d57744107d7c0f18a2edce087efe9c00f9601c72c923082b738848d5774a166db86c0048e57044181e90ed692504fafe2155d6882e9434a7a2531e591434b30e5b8193a1e6e0891d803730570bc1e87639c753fd40be02a4f7a2580d3ab00a93809d9846c629e7570790640cbb34b8e671b3a9e67c8f06caa11a6deb0c32b986d98b0b588cd0d377698fa0e17eb7ec05cf521f0f163c01c83ef02e617fc163087f839c07c7c1d60fef065a0b8eba1c3f1b76de364c783cc431e6edcbedef0deb54fdd4c71eaaa9b25560d91e9f0d26038cc5962ef3233a5ba0d979938dc900a4788890ee975088f7fc3fba6753d645eb10d6f37b932de3dfa1ab2b0a967c94a9da7e7f4d8a7a79e82939293927aa867c2c44455ea34a61e434c1d7c9ca9df54ea32a43e23751d37dff7a5fe3d7595d48f0a90caeb4d7d25f59ce195d45774be1c9b4a1d85e3f9c32c389e2d66c1f19c6196c77896876a7ae59d3eb48a9e0d1f3b1b8a98d80103d77fb6feab146ff5660ff3c032af219889e0f09ef76aaf3a9829b6b373046274f1c06cafba71698cc0de65dae2f0d24cec1de1f12da6d2660313994c4741b18172329d4c26d34d39ca1d813652c098ba9187ccc43a03cc380bd6f5d0279d7ed3387b4fc7ba03483ce3decda89fc01c9cb028b07b8340669c050412834a4681415af00c9004e6202d3886c78e01acf1fa06f3fd6b7cfbf61aa86fa81a2c47adb01c5583650585a24183c6b74b03d51d8542a1cea1380e6052f7d0bdaeeb48dd26b9d9f5a05d105374922a2a2995d148741a753328301781519799a84b53840f0c03e98e65df6487d28181c90766222d3908cc1262d4fba530856a168cfa52be920aeaad02a6dc54baa89414149851cef2d25325d1572e33576e3ccbbd8e7245ffee904a3b1fdd49df48b26340ea36bed9b86cccb8f72d555554522aa3914a0b26ddfb8c9b8934b68db78dc38248dd46dbe0b0f72d753f55b2fbf1a5ac3ce5a49b2797225da6595858584e04a350df572ecb45bdb4725552bea2024e25e40c29ff6eee164e79ca09a41d8e4a37c760cf070d04d25d661118a4057760e8d1620d6400cb205af010791b1c83623fe23c42d78b9099d9a5d7ae872e6d005c8f19988ba8745e2d99606becb6d96fde5db9bddd754fc9b8528bba7b9ef7bee5be72f38813711d67bb7b1e4af40ecca81fa373dddb07d73de222708fb8eee6ae039b0381a85c040249792764aa74ddb66dfdee5bb76dddb675dbb6795b77db45e9baaeeb8e6dddb6d91aab8542a15028140a85ba51a2f7752f141a755dd6dd9ce16ed475592664666bf1e48860532f3067d9f523d79bd04a45a2c3cb06773f32305f00085fc7ae0ab4ea4dbba0de9081423afd94556e7a49b47251ca7c1629379f45caaddc823948bde12ee23e02ed45a3afdcbc8d54462a236ee5de396efb681b157101a06da4f29591ca4720109577209094df20564504a63cfe2ba59c48a652c95a12e9a193481c47e2442828280f5d144bc6766ddb36cde3bc1c9cb0ddbd9b83d41b118db2fb516f303712e56a83bb7ba011596fbc9c0b00d59b7ac32d81de2c84c54d03f3755dd7754ddb408baf2b0aa95546489d528c61b37fd7b42ee52ae748de479b774d17914eb760a6391eb2824d28f9582412894cdeed2f33bd5c7a7cca51bceb3d7607f0461799aeee07d16d96590ba2502aae400d5cba45010b400497c03c04b68f78083cba57f2523eba7974d328e51e0824e5282010d23d21157bb715a780229023759b0ac769dab72ddb344df3469a48f469e0f75dbb9f1cc34a7b29a5fc107854038b6ad0a03163060b0b0ab5b2a2a2f27d2929a793c9542a91482828a391b59e2712751dc76d5b28a469598661d7552ba573769f9e920266195dac28ba4429bb0210f92bdec0ec43b4b010fa49138fa457a6121b5104cef142fa9212095fb0a150e99be9dbf70ff5949f54bec2dd3bf7d137ee923eba568574dbdd721cbde828a291e824d4c5715784e22e0247a7a41b81b95fdd0fa35b548772d1233622a33ad0078ac80823b03d7799c98950248bba0804827a070251b927a4310a8c1d7e1fa93c7ee5a4f299524e2793e9db4d9b356ddbe81eb78dee99b68cdbb611b759afb481a5d2b75b02b3c49be56e377b6dbbec66eca6795fb05dc8bbbce78542a15048147ae8dc48488b4e22bd7b776a45606e2c7a5604663908ce23b6cafba843b9646012091c224537e2504824a25e26a21d769769eef2a1687186dd5c8411387e52215d13f1c26e0ce52270c51a9c00ce7e15813330d72af10f1eb25f3fea2f4ac19ecd822dc8e6ecccad67233d94b2f2d34ddf5546f7b6d145b9bd5c773fbc8f28606fceb0dd4497fbe6237628baa3d17d742250f4f802acbcbb4c6f2ba36f20f5bc7f28f73ebab9083cfad5fd60ff6dbbdc37793d108890892920b1e8deb90b44c8c42290c6478feedbb98b1ebb1fdd36ba59b4ad1c05e47eac7c0381acbc03817cf72eba42e60a68bfc7ab98bc2fa5f47a0fcc279389e34aa51277ef9b57d2bc90570a7123ef9c4722bd921ebaa17ba79e97853ebb192f943d14643910c876eddd69e763e336ee02d936efe62c178151e75037b6aa3b77f3f551ecdcdd0faef326127117e5dbec7c8cbaf3e1719cb49d8fd0bbf5c0acc3eef21ba8598cddcc1581630d96691aac8c32ca08c242faeb2e8c4dd368671d98a57631cdfe10026bcf3afaefd3b04d7469ff10222504cef141520ff5ccc21323a739eddab99e6d72a10e8572ef28ef6f28f7f496259bcd52ca47a78f7e753fbca3fc30757783a813e8c30492da3ee56694a37c275d1cca7799b6defbf49485c0f657f7437f64519e8f2d0a8832dd0333ea07e928de47604699401fa41116028f6e517ebdd6ae0789bf7bf7bb05817c2781404ab7416ef6c02029fc81a5c7a33a5be256ba8ee338ce7a96b39e4ae8aa7862d84b6ced66af97c228f76e16027b23b06feaeed1a84d2c2c2696d2bd9bce628af7eef5d2cd4152d8be3bc9da4b7b0dccdcadbd9eec7ed8be281fddd9f9e8a380b1436b3b14bdc1548729ad67a6a4fc5469af045bec8de523628bbdd5d8626f372cc182261e53f954c05d022470730cf07064956009892915763ececf8328fa8a354cc138bb035b4ce5520dee77a4af998d8d9dde1cf50d9883a4c2b6f1802dd236a9173c44939e37a41e5b0705e7d6e91bc84eddb641c459a2c336d2e1e49955cc1d1d47a233854d919a31829e502374097a230f45877d90e248ca449316139f25f3b14340e4e9c00ead9a1a40da9aaafebc39fe103182a56ab272880b7533df552e2955ae7bef45cda8918244678a28db06f186d4fbad136f101dc15e6f726320abc8f04c323d2cd82983dc4035beb099b278f03525cfcc94a74fa15fa04c539f18ae182e902ee9b06300290be7b813bcd3f4c5696bc2cc29faa2926219314de30b9c49a24bb390508265c0f1084440c245bc6103f1060a48524edfa4953414ad67d28bfed0160c6c151389ad9b9936b3484d8fd141734e39e56396faec89e11dc34d31bc14c3514eab00dfafc10436d3560ba8d6b024a64d7e9cf4ea49d018bda2e0eca14d6828c1d21f90b63aac21092b3fc1c933773aaca2c39ed771354c5be8676c5ca6657879195d5c543870e4c09103878b4a9583ba7c86db387dcb9d4a5ea8c8b38787a9a4c327a7404059d22634d44987f4e79257e60cd7da84b6723c9c3d53c9646d2a49030d2f34bcbcfcfa0cf4c2412f0a87a43e98366116e9904e2c4462da8a2efde622baf4af0ba4970829e5710099cf300e97cb3b7f4d9006c32ec7010e99d8059c361df675dc0beba8a1d3719976f9a43402f3d771dcf8bcb37304e2751cd70d298f27963aee911abeb95c361cb101a43f53d5b8864b81a6aa2340437fa20b6d82437e3ec7556f79a62dd595e3f2a2ae392f7348d5ef9cf3870902c9f10c8732803e326c23c75517cc39401a0cdb78080e99d806d863a0c0023d91df1276be67f6f40a451f9bd09f5ea128485b11ff4839031bc3c1e7f9c3e2805853d57d290bf73d26f66a1c366ebc87c438be852a99ae94d52b1cefd325bdbaf13ef5e9958d778ef7fb49af540fdff8ba725cfaa3ba34bc14c7a54f2e0dba748c147851a9a9a4c39e2970f67498430636061004730bc67e6ffda54da8934b81e8930efb332e65b9748c5b576eb5a9371df6bf5b97e8b08613db38b74e0a485bd7963a9c3d26b004664b0251c0106d499f0e1be35a85bd31ec4604de74dd934b38774f7ec1b9812a6d112183bcac66afae8b5eb4ce473a1f6b7a55a469abc39e3d1386b6a2aa9f65b307f704b3a5ad52cd4baf763622b08d086cb381c7e6effbbeaf3f6e53d283babc99cf957bed66b26ba6e6acf6be341a305d1a895150b650c759e9a4d5e1920ea74f0d26b0f9c3614ccd0e39067e94411d4e15252795cb744aca6d66f6433dcb6347b382571e314551d447228902a56da8e4ab576b93982a5524020000004315002028140e874362b170340ff45cf60e14000e829e466e4e188ae324c76118669031c61802080000406040866ab801c0f5ca7f2e122ca5ad7fdfd32c7c813cd155c205f4177424226a9f2df5c4c303deaea5b21d67f4ab0614f86ac59d4b8f602307e455c59e16fc7a9f7a3a1e3ec1f19ce62dfbd434c900a800e21db66ba1c1869a2e655fc0001a0b4cff8a32c1ea02e562db2baa4d10fc5818e04f007fe75b62250b23a5757a733a119ca405c28b5ee7cb4b4539b771cb2f478343c55bda17ba29e320e8dacbfb1604d05b94cbb2b3237ab26cbed908b2d33922f3a74413144705750ef692a5536b06dc80a3f27159753bbd41751979ccadf7250fe12191914fde4b5195037248124afde92e654392d41f97e577c3f826646e24d216cad09b16175d9d61fe6d970574623fe28a06cac79db2c249696ad12e65d2c083e569fc16a38eb2004f7781bfdfa44236310678f400614a0b9c5e28ab5f0ed976f94edef951a222e07aa13999c1695fd191796204e6d7cddc057e71adf07caab029f10d59ee8f6715f4a67bb811af7d17cdef6cd6a12d3293ae43a3f1ff33962f4f6bbb6a7275bfcbf0349e3d42061a985f34b9264b90e96907ec20513091061618c89b334d2e829669278fb2175657ddaf8f9418201421104fefe25b55b1ea083fa899a7203528a2396c02e53851d8cad390375bda1b190c312a305e2e0d4c209531d6c3e0ba9c5def659ae755561c277a22a6ce711f369bbd50c03d677f5e4a681764146ed6e885ed922fe86512165b3766b548f1d65db01441ecae1d00094ef2702df2c11da873d8d45f45ef5d3d6ab6cb6997e71a1e57e367231cae28424f1dc02e28f29b9a4f46266657021c3fd64da34a98e2998e88974ccde34e5b83e62b4ffb2e3a79691769ab604c0cdb68526b4893adfb82e4eefbadac55c6569ffc5abdbbba5db475183abcdd685f3dfd4ee03706eacb52c170a943e3c1753373d620f7b28ec5707581edeb2e878205d6254e108d4dee2d3540b94bb73bb38a2bc5c6f7b5798447b20006647a7382b61006d0a59983ea17d8e2fe01e76f7be761945b8ea02871f8887467f179e27e18fac718d4bee627c4a2f2548e920c8421d63276ba4e0056175033daffb5d0df65ac3fd75e1797b79b1b643d6e20787a231ed99579e37dad4ef19ff157f5f1ac44918cb2666f0be911adbaeac7fb2ae82eda0e2e68bc133c19901a37f73851edf419f044f81af353f0cab54d4296c6d9f4a042c020103278c7cfd6d2b223aa4742339c2342f2f95f6bffe1cd91d52fd66e4371c897c520940f8c9bd37b9010c48a001d9f1045c0228c990143029dc05902a2532bd59241c28c598d3346c07085a6cc6471f0ecca01ee20d0407a395867c024731a969ca9c5e1c8c9e6d599912c942c09398f967881648a0fc70d91ac0b78a115f569b8ebcad3e021e336ddc7ca639b07403a07a0f4ba4e8d67e32d5ad818d543f878498b403d0d642ae3d8d30c7e04f54a7051418226aebc5ace063a52efae3e90f49e5fe6728c3368c6cf930046618e923a8b6e6b11f1367563e6a764f3bb748b7acf1b1620c229f442a01a65942eb783cde74a85c868980df634d087a2006aa3e7f31962f8d8f6d5aeec2a4184ea89f16327251042f80b63f7dbc690cff61c0bcfe528ab94a51d39420b3ce531a07e1034858e8126bc2563aa76f42b9a7577c26b454efcac195ce260d2013fd089ba349a83d64bcd2abaf398de7c57e5929ecdc9b868a66062e365e8183744de2394dbb9f4174f350f041535fb4b391386d5f382082257694a6025d01fd897a677c25ddbc0d0d2064e59ed8188a71ec34d32c68eb8a58c2c5b4fc5c17df13087382d846d2672c11dfbb1102e267313f2784b94ae00ccb14a859768fb3918e6f02bb775b1bdab47936d7aef604e2d001fcf4ca0b752bd98584374464ab6d74e221cd74e30930dcf9834d9426de174e88161be3b6c9cda57b077aa71a494aa7189986b44d35efb3f5a1e26b2ef1ba3a77db58f196b96ffc5caaaf6c62eb90638c446c898e48c9a15b74f70486035c1b4129407062092ce0f8328251280f38e801d0f5258fc7f87b366679db9a548d8db35a3b727756fceab3f2a46c2db78e4639f95a4487037be87905a48fb20ec8f6417c13de47720df8b603f92fd487720df07712fa2fdc8ec23b9877817c41d917646b213e91ef27b10ee45b61bc96ea4fb90ef81bc13d16e04f6113c61d1059dc20510945adf9d477ab8460a561824ce9e00bd6c24e217ec6a1ad130e0e7f585945bd061e099bb4c3b10037b05fbd39412ea8f1830afefa993891fc783a99c18c0e4e4eeae8bf8bb782cfc6a0436251934efa093d671157740625cca49fa165f126acc3d76300260f1a6037b87ecd0b4775d3f6e0d51ec6966f6aa9b9c91e2f9bb3d51712f6cf4189e8ad53631294985cfe0f0fb86278f3ad97bb75f15ca0843da4200d78b028cf35e7c020a834b8c1148b03c32824ac6769019989200506d307b1ecba1589b56caa3c1fe31831a31a2ccd5d19ecb0037f086b12185f6ca1d4dc46ce1ffcba7188634b36113bc3824a5c6444c7fd5b84230ff2e756a89389ec3d5e5360919528970f7f99433d2a6ad5c965061f4e08e27e2532ba25ec0b8d11f80f743f87a5a42e38d14aab48990980f0225ea9a256c1247ca192af38ccb201838546c096a9ca5c64b4530283fb35659782dfe05f9b8c513540479943c63689dbab4e8dbecf7b99eb74586d7ab79052b95d6f13a34259b110170714505e049beb37934a8d9a30af31c3166818c58a766a8555dbb384f8303b999f4fada60ffdb7e3ebf6bf862a2804cd070b15e64cef282b912020f5bb9fc8711ec627600012ee8006c34b04b0c96d730f0a2d4092d1aff07153004f9f8fbfe6e5fcabedf3c75117838ef51d0bdc70726760306b45c543e5ba909f04608915af24e16ad04a96d32b8894becb28ae4d93c3c36d22b470352a6587aee717409d6b7ad81a9a8f165541c043cc5f9bd0a7d7edc8aa6171964de998189253fed1cbcf358dada9dc77287a3d911f32d9b212657898df775c310882984cda136c391cd8865aeb59a9e0aaeab512811caf5699a7b4464ed569258d40131aa5f27a1dc29a7036848e27806b25e144b5b32acfe3a37e45e7a408e6d6ab9524610d226573543798b8aa0178913a054f1e00247f1d612b062f53c95ea9ff2dd09e44c6f700d75f3795f339f778a30e4028c34d36b852d991bde826c58de5c1e06955082841eaf02f74b58618da7e555ab6f67339681a5066c7b6db22917ffd934aa1d98c368eb8d9229ac52b85771314dccc04cbb4c8f1362336ae7e85eb1b09ceb656cc6acef46c02150ce25f7ac1c6096969168633f1a7865f7d46ca4d71e1752c75604878b82722b309dcf88f1ae59ef68c4cb4ee5ac47609ff34ccb3aef19bb28da30e38bb73d7f94b90ff3fc5d278d806ec8a2e8e947d9726a667ba52e611dd957aef47eb606d643f612778057c7ab5f82b29199ae0add0f301d32244c63128bdf39db7572aaf47cfa5ec1b963c028abd9a0f4d6282cc176cc21bf5adc243de7234ae2c27cc26add9e41b8e4872b6d2b80547bfea4af3bed414acf3bb079092d1dcb3c9cc540eb59d1ce649b3a2b0b8f0f0b76d65826ddbbc78e8ad88036b72910da362b1fa10b4588bcb0c01009270f8ae89c7f8789cebcc9a6d70d3dd90dc8fa5c416afcd469346f38e7f22f9e88bf92dac787b74347b6d16bfa1f4decca869f94cdcb1ee8646e414ec1768eb25ac69881e564b7c35dd8048cc711ecfa887d432a0c561f0bec9acd93aa2d32d48f45a05bb44b08c34507a7469ecd93d5dc1100c27dda389e768d4535ed904ec6349ad669478cddbcb3629bfe2f55735e7c8e7755de67c56aeb74ac76aa38b0971f6d903b76870c29d1aeb0ae4176979c5ccb1b1543bac86e5508f91df47c3ed8d8481d6ed473b32f0db2fed71cdfe2995c534925191676e2c9bd7ca242efd44bc75dbb557f09e69140d7eed60e7573d95de11131367c6a998ff163b2633eb4c2a47f12bf9b830a2833ea15f89a6296335b0a491afe9a508a45e9b12668ef69771ed24ed2e2d54f97ae473a213fc7ccec257bfb690c4c996c0d1f1040ed42bf323e0380666bb9e3fc27705fcddd313f6f87f344e30f5a2a4580e8552ecbb8a0d99c6e384f2e52977ddd1d34d27446906631d6967f85195a9635a54ddbd4e17514b5a77cdad350579eb44a14a72c11239b76beb06478728b627de0be93a35f5e5c99bfdee672dafcce3732a13e83eea043a779d2ae47a08dd269f01092df749ea8eecfa0dd64929c0ec7987a17b21f8d8015de90f6b40f82f43a05f8f752a36387cf92216070721494ee4894151aae2ed009a420793b88a25fc712e13cbbc9e1a7b26e87c09a31d7a5670bbda4b66def5fbc4075feace7c0968837d0ceaf37a522e9ee5b7c52008a1d49722b54d06faad06d630985f20f713816fee03f98662d3c10473419cc94ade3df299757448cc1d59a623bb08436edffe53cfab331d5c366efbcd081bcb05e0ea3d38c06917f497ab26e6a78b422a49afe68f947188b6404b3929223102cc976618a6a299925194155af0f5d19b9b066e3ebaa3d55b9e48cb85c38210f56c5c363f3a4a47b98bdb4203c3e9c30bd79c4cf8048ff67ee2eca53142c5f391e4c8244af47eb1984c856b9c58be34ba2229a8461a469f377d0a44dcd6dbc56afa6a98989ee66a34040dd73bc857871ec82f34f57266c3a23524b7781fe9f245afe4ddcba4ecd5dd9d4fb7aee84e08f12035868d6af743267f2a5f948599dd0e52c604a53cbc59d109789fcc369f35c8b22b53b3beab3a9c31bbbee082183434120f9d8ad44b1e440ba5597a5a8e73400e6e0e238ffe7bb1407aa08cfa4d8492ab4d76ba91c8967b6f73abb91b3fc7dcc0ef354eda792e5893ded667f8b6411ba8ef968757ba215ff01aa2a1e78e7b123a9753b631ed1fc135425ee4ffffc4992a17385f2023b167e8146910c8b6c7ffdc46e99e99316691bc40e041627f87e05a491c1b629a273bad5fe75a4c7a91415735a3035ab2c316eae2e2f03fe4c496936ed25d3aa188511b9e23a41c8c1ac81a3dc8c001e6aae57cbfcb36375f152e5df74e933f31a8ff86194e8d0649e78acc210c5962c6fa3c379f717ec4550901763a19322e6dbc6d58d8b37d8b7cf7a5d13f7fe16672041e8afaa72f376e9ccdce277f11551b9dfaabfb1b8381f36768e6eb2eb7c20dac143edbee44869c7488f1daab0fea9839a5ee5c265190a9c46f24139f7c246864633cafefc963d98873df41326febdc1037756ad83c5c56f57f494d0990c4db2ec535d401dc78106c468ad4232377138296359b7d5490b7a79049c0468acec984aa6c3d440422cc32e79a8068c631cb462d57901c6495e8e5923790541afc225b1774ec230b5dc533ae7667a9fd5eb02916f0ec978af4f9e861c01b4a8e2a147ceeb04f01ff638f9adcd8db789911afa08e0045081e9e15be708f90548c4a992d8ada3489db3c72ba51435ee2059faaa17f56e360451d499a0bd8ea448e90827b097813cd25f511f78936307cd46e85d44dbacb531e9302e6e12eb7b39428c394435501a37821e506a3e6ec3254a2414c8634a6a2df646238df0bd1ef4b2d2fbdc0e0c1dbb99a0294c2648971db68bbff1d0c63a7a3c2a13421cb1a910fd94cac37a0de4c1751678343f0368c026308c70fc953001ce1e39fcc95ddc34b2dba12d41713ace144289d3c73d821b86670766b93d71346ccfa49f1b35b9bb5a11adadac7413a693b69814c38c12b0948ade791056fabe7e3ab92e68ff4762bdffee4def4bde5ca3c7362b5958c18809eac2d90330ac27e02b5d19b62f2e551cae8d1fa06848d22dcead61773ed3b1eaac22c1baad088938dc12f83bb5390f2a88eddcecb63369e71f86a2a76846ab0ee328a6301db96d2bd716070c20fff56ded9f33911fd66c62ab07dbcd9a0654e6152fe621d97069dd05220320488871cff804ba8fc967f3f9d6608f4ffffdc17a7832b7563618206e11391972afadfad35b7d078461ddd454acd3e9f0b1e2b24b4ab53d98894fdb0911619140f5dc5d7f6a2b68b2d03a6e19dbf843e7d1eccacece6273008c3b00a32a3ef4308a1019a3965e6495ffa5622105e27ce519b8bb8c1d8ae244b5de23dd614ab00972e97416124342e8953ca447d0056844f87b36718265c93ecf85382cd835a67a5c876b2a1435b243e02bbe00e56c03cf065df8b7c08cdd518383125da3c1f78a978c55530e03636c6db4cf79cc067d8f5b21727afc2c14049fbedfbe4242ef64e282495377b874201f057ab0f46c96077c13e0c3b0e9b28943d8daa7fa37bb8fdae924edc5d3b30e12ba6ee970ad417a3a42280095fc865f4ae1f320f39e2050205b2a2c3e03fc50992557ebac488ef62912fe0925e2c86c6a6c3be7c189d2965d4d214069714942560e0df376f2b13b843f4a2fc61ca60802b0fe2cd2044a7ed05672d693a7141c1b38e74f5f33be0fc817760023ce2d0bc0dfff4fe8b729cf8688f2804a6c35e2aa89191b6e01151f031141f8e2364ef515b06d5699ad93ba5f288907c10b33fee0594b1e4d4684692d1b267260eeddd2e3dec7270540a3c459f2179368d2966d8a7426bea6bf5110d97b688ad4034ce99c982bd48eec33ddced5a8a1a3d14ba0316e272d60ef324f5c180aab0aef40c0ece295d6eed4e5f7db0843faaba6eabdbe0e2a5335374cc6391faf86ff86ba52194d3d63021f44fa88283492216b54ca8a6741d2cf24e17e1988b3220d13a2364af08d329b79071e50c178255e1b3241d8427956842e098ae47155a44765438ce653d35c3bb5dfa4978245554c2ebba913d6342038807757cceabf437970662bfd8a440ff7c52e4f4dc795014d30ff8e808a6f2bdbe6846b3b8ed6fcadeb8dc3cdf17092866de1a7686bc254a8f20b0f2bea877382dbeffb3ad895cdc3876bd85839f2dc3a4d629d8c6cce0fa3b153417167a9a1061272d6d3dd9e41be90bf05f9602ee89ac1f5c9d9021ce990431e38d0961673d9afa45dbcd2e189026ed3a82494e76ad6e2c6de1a6624b07bfb20138a4e5501e72ef8c501f819709d193250edd13b8bc2b0277b04c037e8d9bfefe8f24e549f6d194131d8931decb1123d9df2885b768fe041148b3ab640da86d855784c091b97eb09b098c36e08481f10b131ffb877bbd9d311dd3324352b47b24142e7d89863e5f2af87ee24e9f0fed019cb295ebed8c5f6abe437c675b22261a7b661131e1ed51d9b46f80ce6cfb42263bff4c3359c56842bc4b6d1806ccf744c0df6e2c56e93642f76841e4d473f8be23381a60a0942077d8b8d69723ce2307c54d170a7f853a60b9ff49d48c52491083cf65c5a8acd3820281bdda73ab895381bb347b0299eab7936b4217e272f059c0b6e67ee0e1d537ebe5057436a40298599f8f1ebd4113b9a44532c3d9c6de6a4bacdeb67cf0ddc825299f5da0ec73e560b401a8e6cc68aa85bfa067ea5978884495f72af03fe5c2d11347361e0bfb67ff3ca455f0de8fee99fa8ec664363b7f09ec17c237e5dcaa736a6fadfee8a5f18bb95cb00e5bfb6849fddbc324427bd5826c34c7aeef3ce6f00789ca123f87904abdbd456ef7e3731e1df7a59eb43be5af621ec25ae21a98414149e7cb2377e3d345965d59ebe9c34f80caa5d0aa8c64c274b51954ae11b3424623cc7e9e280493866bf5cf2aa6680f77b0e474b82b0e862062098335f0fe9dba13a938e909536c0e27c3d2d82cf7b560d6ea51e84fccc2b105906170a1799280ca198d146c480a5f2925c14943217846a520026128037920004b87154e2c9a9360188c1da478c4f68c7e9400a3b7f5892110b80800a22eba8cf5cc99b13f9516a5613004a9d00e07e02a0c869fc022431a995479676cc2e4bba01953e3a704c48b9f23cf92115a1c4239c920e451478de01719373bc30f5f196ca0007daeed3abb989274e16af293c6e5853cc753d44e3d61a001f8962e931a2b5d7c524ca743c5a38a8de622c73205babd838320205363d5f2b374cbd7a9a55899f45d52ff2264f385b45af2d8edbfef79e78b1b4c94aaa5126298118c600093af69c5b7eaff678ce1b8a7bc94a0c7be2073246e5693bfe9c404891114f6eda4ca8216c291a8c150b7de2e212beb9923c3962cf2c27d9513aeb7065934a88e45a4beb94baac1da449eabd11c6a13229c1a9f52f7b077e4a88fd627e0d9301fffdb087afc9346a19d5bc2419f0fc33fefeaf0ef072906c714d18bcbc81eef184227a8be9858b2f81d1f4f27dadf2f722017d08c0154f577312f129600eb9383027962d3e991088b996f92884e6fd2ffa9c058192ef244e7c7da34f42eb23cc769a87ae9e4e6275e5bdc4b259fef3d6c456e79b0faa26a54e4e8dca1c5e794932bf61aa408b1e08e81ed77310eb9a1f5144a4d62a588ce35506d034ea591b99b651cfa59ad34b52950c8f81bbd02e8c34258787f2d19662e2744d1cc38f50c4aca72deeb75f4dce28c2d94aac551a1333e637ac1773cb02cad20ba2202e5c1610874bc0850f5d79e0544c00440be5786b8473ebfa88ffbfb9a8ff4cbdef76989f509069131725649582e31bda0970cf962f4c956cf42b6c8ffbb2b4831e1040a3555ce15dc7cb648dc9d67c63085b50c3f59e9f978b5be0caaab0dd9663d10d5e157aa76a64ef2a2f6a3c7b5f0dccfa260fd9325bd10644c46e2bcc1c401b2cd3f7597ea0ca22f3d7e097de9fa282912e45891e57e946fe53176dbb0a9841eb5805a9a7c018970997bbdd93312b5314a32b1e7a4d459196ca2a98b3ccd88768e6ca92ea000b8c68d2b84761aaa57c0dbf24beed3a5451622849486520487438caa2db9c1cbf8a93487a7926ff9dc6899b993c935392716e6859ae41e98bf82703fde80fb14c9c8c2ddcbc637b88a19dce185b4bf09e3fa0a01139dcc4a88a0b2f9144d245627445a99492317bf05390e3a7cbf69663dec151b224a44fd5d9395ed114b9ef0697d07a31e17c1cd3777f49b5102bc1b2c5a8b7b57174c51dfc7749a4c2ab945e424bd257f5e094920cf8a72ee86497678db2c3bca32b23e1ab46e19848bff8455ff422cbceb03b7ebc8844d816415dd6655adc71aaefa1c2dd3b0ecb9f411e33936cced3c3f3a7fad0df6fa2f1340f882f029d668231a47154fcf5bc70b31385170c46a7e51720fff3909fb25a754a24e59d10e91804d33070b1daa33bdff39516ea6470acc07ceb7f9ea543b4fd33cd6652eb9ad67349a6e826d0c022de12122b926a3a64b61866b21bd2a04187b3a4a990b3bf9e624005ccb9d1872f614d608b8f0608782ff3bbeb82f1c1be5e28d432a5f38a437edeaf00e5eede4a6a967c22e2e6055d28ce8ac364b45000fe8e34a691777346b51d2b9e42b14c4be9e993a9e414b6681c28291d8be17ab110adf73c94310b67d2672ac02a252ed7540efaa470ec3935da539aabf2d8ada35083aa4932fa1a09174ad2363c3ca405acfc0991f9eac616e3bce00a3efb080ae5be0aba6191ccd7a432730f866fa4a6835cdb4527df4d20b58f149a1c58c814b0093d06104c730ee40b5cd73e404ff1e0156e6f6d76c67bea60dd3416686de30ae5424cfccd5648fc32f567030d4678489786f29a9c3d0b893a96459eca386db3a98898662aad51b07fc215ee0a4d775aca5d0304eb54afaf35235b0d2d67619e620abfff0951337c6daaa07abae0fc6248096441a41f03499426993901592e23b77c25d9dc53d6c329dc20c9f16e5ad07998f994145f41d0581d8044040a6d8cecd85b5799e47a81329dc424633f8bb822ee7e4d8d8885ef35d924e04b8dcfe2e6248717fbf15141cae6fffaa8438cf809bf1d7c15780407ea5339ccdcaa403999ff24fdff6fb01a90f194eeb0fc579ff99315be34d22c05aa09a087ea6763faa391821861558d5066eb597f09e4aacc7e50f0c869441c70d657ff5f5c8abb64d6f64303ea03f0366136243e490dff65dba954deb4dda4f08a015e340ce19839f654d8131db635204994bf698b80f122255a9872da2bf42ffc97e5f7c73d4315c8258db1d11913230135071470abc5daf574af83108f9319abbec6e23e9571d95895e3ab9effc694ef93c735d36d2ef265dede958bf9fefa079e7022d451f3635a6b1bab3c64d11c64d13d6f5cdb963187092f1694c785638540afe51f35b35e27246bedd008f838c0013c865ec548e5bdccc3dedf1a7c8945105d727349ee01cbb5accfc3a018047823a0d316705a29e07909785932133b0cf18c55ac9077bc6b0d6938f6bbd88b0e83aba3794b88fab30f529e5c32c619e62487985a34c81a061179bfe46e79794c5bb2ad882b2582ad55ff8cf71a2e9da75263dcc9ec38c7fd2564754101836ab17af96863dbfd355b7222648e393cb4cc321b6c67ceeb601f53b3ae332bd4fa693a8655e5147e0c1bdaded47b42e02de0514231e909769811b1dcc16bf6a421a2bd4b96ca09aa69b69aec69e83505ef2b26067badef57fd0c96998a4fb8dedefedabbc5b5e6ae7ad58247c381e215a576ddbe15f57f942142093da6a45fcd4d2dbca7848d87e23514ff631f4607d1071adda19b7a078b518508e477558b1a41d8d2288257989b45bdff86f20aff9dcf60f4389ee7c50717015237bd5cfa53fb20db4c7e6dc7c15550a6bdb0f375ac5d12fa37c200a6dc5acd8308a362f8ee90d3f49b39032fa565c07866920eeb37a2e305810d4f8ecb8eb5d9f88a79e7c6bbe04eba3ed526e6fad91aad062e5f98c20eaede60b148cc4eb8319cb1d743ec98f1f142eec9d878c21436b71cad05c65a0ea8b0af47a4109af7e37846ae6cfbb8ee1854279f5de3c9a64e7982995c4b2d524cf98cd367c9feca71d7f164778f20849daa1a263c1317e2a40ab9b9f233eeb68ef7624fb275e127454b19fde6d9236842cf1248b7a7df26c21145a05e9a8554f2099510c18f028d29dc3f72d71a6f94b741085fbc2662cad3a8278c179c050d70c7a3ba8b02884e4c8c0a7cdf43be2d62df57aff75bee538a1669bc31fc8fbbdde691e973ffbe0f1c10e53ac77842bc50021678b1b5f6ad8839ef2cfecb16a475388283c30e5accd3634bf4b6be5e4dd42f46ba9b4b20372a810dd193f0e23953a4f988341b74193fd1996d20583bfdfd024f89dca7d83264518437bfc063dafd9eb73b052f9bd7d0de65b82c9eed302be5698bd4137eabe6c425d961364d8195446d09f2093139c0c73cce8bb77639142de05ca66eb8344fca84e3580c4318988312433de913e5c90866d16fade4106b294c51a83210223ad112382f0bc4b6086b94d5df8f91184a74cd374fe2ba7340d46aa2a03de55d8402ba515a8ffaf9a71e04fa27ae5b7c81eb0c9c7d57fcbc6776b599d1d5777e1623774cee2cf5e7c492653396cce178809decbc3f5e65a0ddd295d700ba2ced0e3b31dcde6e9d49b680b1e9b4dc08d6ade8e4b682437527b7323b1e41a74c761cfe735a61ffba5f0df2b96e54fffb72cb37555153eed474bc2a80b351193051206d8d7fe489978b41f26e7478c6af8b72cce64eefdbbb10b68c1c9669d4ba475d65bda332ea04ffcd3b177ad38c5f5f9d14ad920957d977323587aabdc3aaec13391113e5c9068bfda7c44c450e8bc5edf765b1dccedac352fed1eef7864036bdfea94562cc7a4b48e83350989e0b3f152f033cacf3a5c1d6809c32cc252502b71bd980eb22d9e9f0d5c473d8e9fce10548191e007d54b0b3897a8cc806f51cb59c0d753df3d5ca7def704ad1104ec317e28c55a9ad80055ddd85e59ebe483e06e46e81b3ab2ed68ca1793140e8e9124d79d312b5d890d1df777d8e6b1b91899238a36fc964e3b501ce83b92ac4074d46d957f5d03ad24efb1022815078796d1199f10d5f866431fcd5e58de04632e9223ab54b9b4ce74d7f500d3fabe1660d8abb2b15bfb2bb52df5456941b2762f00b92a9ef8b72e4f83ac78d08c23da01f0111102fa050a743305999dbc76cd0f6b6c3c23cafa735139d6010f3616907920c13961eb038eb673e9284ca4a2bdb8b321b2967caaaafe9b06a1e68a2712298f2dfad3485b62ba5ca69ca792deba5e5aafc0344885586def460d9cc37a101af17fd9a7d0f1f70564a6c367bcfbd147217bf07ff4ade2c77c86c20c38a0ac17a5316669e85821eeaf9971355ed9d587ba7744d2edc8a72f46e4db2de00f2c81570c18640fdc55ae781fc2a7cd9322a795bb0cf51a5f0a7ac4e32f8d3983bb999ba1aa2ad851bc479aafea1b1930c61aab91065400e764be10af6e1a319cd389546ebe246afc03223bbfcc801acc0d3ca77eb1bc00ed74c4fd4a26d2f318cf6c312812bea6067b03200266d9e7fce29c1eb18c5f2bc64fb88de1bdee5a80d1d9e60e34bf0631dceb2ee651c55ee76057ab84715c2e780253d92186584d5190208c8a5003e3a07d749823c1a146561d57f27a1abfe2fb856d9be4efcc8b381d71e93220da9bcfce4edd8e4cd4655528e23987aa292070f65d241d8e80515ef582278df5c9c1a8580eb16330a22fac05bef41932172eafa627d8e8274fecad84b8560bf1939c87cd0bf3fd4fab817ca4bfa6e665d2ffab22d7147d826fb4927a492659f99a34e27cd8113c043d6d7719c34031b23ca43cb7681b2a26a8495289a59dd0d3e5063aad840e696ce42cafacd2810f50127f0f6de701bf265c50ae6601e436719e692ea102824c1c08fcb32731b88abce96d91c337bc0950075c7012050edf529e66cbb0d1c3a88eaaecf1b4cdddfd8667086f60041217465959b4a4598992d270755c83064ca40032ed73b52f0661a6d6f4b24affbbc16e08166f6e86e955d0190ee74daff8239785e33daa1d32bafdeaeb039f1a64ed4aa82c9debea0118ee9045b38599f69ac185e684fa97deea143f968d83dd5fd36bd153df507cfbe3b0b58739ff8f5b89bcf381ca18cd0aa86f2818ca3643ccf06dc15d0a84ffc41844b628e661258190a350046eb3ed79d4536c091946a8d87593a41a208a4a1bc9cab35156a2e5ff745960291b831847b005450d1dfe67dea4f3e92a748946f747a2d30270a01224b6a28c42d7afa4d84421092b2e99060077313615fd99f4b50953c07d81ce36af5a91320959c48407477834739fe44ef9d46ecc0b94cca53ef7a7ef9c532b9cf3f79ebd2d2f388bd85a1dc1d18848e91fd20ae443ef546f3666d4c8f5c63d64013e738fa8f44031f83ee11201b02cd27c69431a4b237061a1f69ea454a636810d4f668101deea7ba499623ff4a6fdd820757ff625c5a228eb432f234bda5d3a069171b0cff0774418edcd5eae4771fc7fb0003fe88e239cad8d67bb6e4f4097d2348a0519bd1ca39eaf5ef10e0a6fa98034ef1b5defc8260e3b1d4286f7572ff5464a90759aff408aac47ecee8437168b62ab4d6c17ebdfb534b373196209467f237348fe4075e73c52484372a7bb16f7ac83d9c535f196c5a01c1dc0f74d564e0f28cfcc0c12a665ccd13c4f0af2e1f40ce56d4c921c87f9041a6a0ddf12486fbdeec3f21e280bab3b9f8ae1e1920e7896485194954606456767d9d008a0db0931546b4bb54b9ff4854bebc91b860111eb029e9c93cde37036d359624a07ffd08133f0d4eabf9c6ccf31d04f9e833f945e90d1d198dd94000629af42deaf0117fa9e5efc85f423b3417ed6f30a5934f6ecf6070d35738378fa9d37d69596557f50709c48ab4ef3d4a9436160935ed1aab49fe27a941df138475f647385a1e76461e2c6a3cd40cbd40d7872c0a186c6a68d53b3da6f5cb1d66537f42e3a4a8701e8b10328b5fb1ed790115268778b7d1be1bb29628f8df22215b89ffbdcb5c1da913ea32279ba172c0dad8092a2147da9ab94d8c51036c5c89cc377c77c5da8f5d0a663504ce5327aa667b4b6ede5d997665ce8f203628b88dcd7ce54a3c6b5ad2771db818feff948044bd9d4166ec446b6d3ace318c13bdb4296eb1ee78a120b56f3412237b987dd3c37b8b906079c7088f2f8421808987c8e425a0c40096f1269724c7ad911b75a2b6f21f7e8192ec76764e975b9a90798f8efb7aa24a2d2cf6e8b389161609866c159d0cbf7769ec6e95570bf493522d2146e4f21b158ba70131a00dd2b64c54ba71f1dc939f00ea1ac28bf3bcba9f0617eea732fc1d4e5d7b2e1f18735270d2730a7bbaa4679ab82afb737e02853ff3a1e07315d088b2da40fb10bffde9d175ff39e429ac33811c702f86a2b17369a0e0646e6d027ca6b10b15dd3a38a0529e17c5f7aa503d342bc39ae860963cc53381c9df1b1b5d4245c1c3b2175141259e4c26c9713745c5ddb7e0adfadc197e1560acb848d60ffd1ea693aa1719a7d53ccf31ae811592777fe48e2fa0581d2cab480cc9015d56b7dd6d7202827558c00a3e472b5f25b75f7b9f473a8b3a549a75cbecbaa6a38cc8a10dbbdb841877de78c898fcbaa3b4dbb959e947184c9d4078bada53aa6e7ad283bf584d276c3bf5866c9e16375d0965035e5deb833208f98a086e0f6e57e2ae300eefb12b1c8aba1aa62271eab3b758e1f1e0fb61f8f354da8be277493cccd0e400acc68dbe2e59f09f308bcbf42dc5d1a9f381e96fa5ae67618c2111a0141f3224b25b33d93ee7f6d03abeef9582f70c274742e8870e6b3230a754356965facebb2716f4438096246bf100246e125ea950b823286bd76a5109ad6e70fbf774e7666f2e570318b3bc3ce662baa2dff33eefa833bff4f267e334a794b0ce7e8b032d9945dca8f822105c6cffeabfe472e3a94b44f8569af925bf9ec202b043e9d0a1cae6103d4a364524bcf38d167133abfbf5205514fb79c63efa1a87f36bbf8f51f94fad2d9f53a92c578d02fc9ffbdcf628df0d0361766886b042f5d92e63dc50c697b394bc2e269499ee64e540e2cccf059a2996d8509c73b3f072429eb1292e41320422db6e06e72b780e0dd7953bf5f4bbc3e9e18ca9e17e14fae000dd3fe1cacf030c2465e8101af4418be70daf26ea224f36bdf5a5251774445f1d25a32fd29f3b8caebeda8a50a2e1fad91ba6e908b777acf45a222b27165875753447485f2636c7129ae5090ee6547fbe362ec2f044e6ff2d93e5e92669dd46ba2b3c81cab9e696f4fd4e934b75707cac5d899a69e615c6c4c3485862ab9e1288950fb172513e34a3cb9e8428b0d985ec4b58a5be6e18a6c41d660ffa4385e2a0fccf8a2752fec5addca34e0374fb951c7d2380926b48535888ad21640ac8a33ac3d972a615c80c0f98f82244baf8f93d09ae0289302d5fb0a54c1ba60838083745250bc3efb98b5e4254b0fc417892a56d6b4fb2e99a2ce03d321edd59238cd2fb806bd84f6ad32de9a69925cfa01f483d4bdeb071134c236b8cd58c2bbf3e33653ac1ac78645de9ea29d6757063b578dd65d56cc191d20a82af95465fc100600a7ed3ef02008196d04f2e73abf9f4a0f2abf83cf6b14a3ae100feb683cbbd81e4f803eb3f5e5709a22002f1322cfc786c2e97a97d1b2be82fdf584e9bb2ca4a3c4d30c6dfbf2fbdaf1a06573b44aaf5961068fd22e556bbd933818cc6f7a478da9774450858923d6393bf250f06fa3745de5a8c2700b2b522acf97b8a4af734fe903de9d4032ae25e55a4082b7801506d1b9ba4e73cbaa91ad7b862587e61bc52afbda8e4d91795094fdf1541e45522e7fd2c4d6ff5884f75fbe31c60240a7c7f1aad09bcb32790f0441d266d4c58d8506bfd82b70460d3b4280b787fc6f9d9b5c3f12abfe358aa780d53c61a458a161863a2a9368144b70ba4f638d22729e8e71033e9ef9c10a195b0a0dcb2a5167fbccc1a53806c2bcd5f9965d02db164b51a7b1364c06508ecb3f4762d53505313bef74b506ed7233f97e3ddd70710e035f32a176c99f1312fbdc92d38eb59fa0e203aae94c55ab81ad94efcae87fc658e92b8d4c8a99fc72c30e1f66c0f78be5621285d85aa3ad8d282b2082c6057b334a799c92db44214cd03074c2943c3f0d59adc462c1b35ea34ff19e9840b8fd36ea47cc21da8ce3da075c802fe9aafb1273ca3f87aeebd4d411973584e26b177ff4aab46e7302629cfc60853a98c98e7cdcbfc15e68f481b0da2a4af40ccd81ec782906c5921929d8d1e2946d3da9205bc6be13786ed729d99c21e002b44b1b766fbc11ea92e9a2970a6754d01a1e392c7e899f6aab2d87c869161f29795bd175ef40e33cc838697bf94bf38aeb7f8c479ff5fd9662602d57820801427986e85ce886cea126d039a8102f40ef692842e51ceee04ff9278d3b027791358000391ef27dc92c87c5aab98febfd52a46895ac525065471947cd80306d29a8ea9687ffdcf6ddc6747376fc04f9fdc5562af83ba28c5e37e76d316b940e7dc6f68c0b61eac852f05fbae74b3c4885fb613aa01bf17ebac5d0c3ed832d322fc9cd0643cfc1ae1fbabd73b57180804c18b1d0078ac4eb3af47b88ebb7735b480d3732c03ed25194b8b56f8d60647628a975e2ebbcbea26105ae82b9f619230b9c31574a22b197fb42900112b6763f5c7df0e3e48c13638ba6e8cc209c8431c0b18863ffb0d05020680539780cfc8583f439474cfb0358fbec74403e73a7311139a62d1932eb46c388de46e5347c9db32bdde6a612b630942695b5434847a977e6ced2adffdd63815baa94862417c50998f6f4699f069088dfa8ea9b656352860c5bbc1517e5f2f0cc0759fdd2e65150a0baca73637863884903ad646a3186f28e14603c9d995e310a903ad6df7336cbb24f80d749ce52a310a732e0167d6640a4ca8b887db74510b4ebf5c8d236453985bc2c10dc84fcd87b6179a6381e2d24ee6e5d8d4f9e26e38297572bd210c8e76d0eb898043319f138b5a23076e50ecfa3f4925bef065e14164b68938be41b1b5cc124aae2bac9f3e9b24672ec25d2e24a7a1bb8ec0ea3bd2a0aba3a8c0ae24b364630ed3996dc9283766b49ca3853d811a5a4f682c11d1b02b8c468c1f4dd0268df91d3eab6027582570072473edbf08440784cca91f9c4c2cdab0a613bd41ddbeecc9a89d695f2f72864a7bcf6de7bd4c6d703864e74f228f90fb4da1a64f99026c4319887d9d8637217442c4346c80c01180044afd149ddaa1bbf2d5e8df08c1beb65386d0f6a4edcc095813c3e9eb2e1f00a731facf779290e44422e6d914a7b9cfb25683ee4dda9667f439cb3045c6a4918917f10986dab227c88647f9a7ec43453903fcac66255f77d6dfcd9a6731278a1c0b63181ea4ab42f0407843740820c9d543738b05e716b0c1843262a8b882df7fef750c6740f05ea6eea0222cda8317af14fef6400851d33d8ddc9c65f251745493d1367c84955c365f220ffd9f6420b9aa231a83e0ccf3f8e084fc55cde59e674a90e9a27362553842574660faf88ec8fe2e14b1ee314a6c280612c63030ef2d98017aec587c0e97ff5777bf3f28c476a33078e47565461b979b6c8c41512ae105480551f6759afade88a5d0f5b40d70aac06d1cb73bb0b1c305938f17664026c1c8ef0ab5d89c4715fbed9a067511cc22bd9f06df429be98e1afb3816bdc8f7eed540ad55987933164e3ec32bc088488d1df785217d1fe9bacbc50511103a1a2b6a78539f0e93050b03e2ae965b9a4fd6293f45ec34789cf826a90576e154ce288cf53c389729f190b3e4b94809c22baffe9e1a9f9745923fb72afaaa14e9769239cba587c4547152875d6f8f60e5cc80c9ceaf6cd28357b588209be16248240b83bab2f823cf882e264063e8632ba739d889f6bdfac1825a4283b5725e8a17d44e0e91ac6035ab411e09ca48897ca70b9f803d33bf160de668dc848c92944c4de6e0d85f45d1933b2bf24e8db821d2efc2556330b9675775f64c86afc9fea81366a74eb387245d96ee6b17ed210dd4b98bd9c17261fe4c3c1ea0920828a3bd12607024543ddf36289946d88b904396f9b49800f8d3f04dc7e7aa8a47873057c3df0561121dd954345089d433130226f6d629f1d7198e81f909adb944325f415fb403ed45812bab9760ddc4c157fe507bd1fdbcafc40813b41a6ebd08b1ba8ff2db9ddfbe055148aa7b0ba67cb6b8ae7af70a3da712a545a28aec420efd19a7cd83722e25dfad82c4038c72b411d5102633290f9cb82bdcf526f20ac28815a832deaf76d029b2da982236efd3c672abcb85c234c0e1d571a1200d7048755d525c033854dd9055a03d5bb8af2a021cf3ab33428df0abb5d497515cac46e1820685a3ad1bff2109eb1e2be81b37cbd47c5d4925c7228a376ea186c2aba4451990dc53264cddd6c447d8a1306e3da3d5c12a08baf5ddfb444ab16aed55a82f76c61a7e0dae44f782d0cd5d972ee7963d31cd69e1f771761d768f4b26cea35db3da2504b10979ddea1e3160ee8b391fe7abb07b5cf2713cf762621d16f93fc82c617eb9cee23df788d1ba5d5c6002d030afe447210fbdcb404c0de3845b8b23f5a94183cfedce3b3467284df4b580c597f4838ee6450fffe804407aa7a0f066dd9fb85c39704bf24c058ec445a97a340d88c4b077b5fa9eaa3c3c297b0b5989a4842e1640282aca569c3abc77ee92af3e2ee84ff1ba59cb31fbbd7727df83786dbb9095dda16d2ff18716d65aa352d709571ce2cdec12008488ba658bce60772fd926615c91ef9b86a1e18372a544ee540e3a308a0ad00c8fa89d9b75bde5b528a3e824cce996cb8bb9945fee6008a37db25726401f75360baf10f2fcc1c6eccdd8e571202ba9049790b6d5b216e868db192c5385fd7e3faf92dbdbb90ed390d1084660d1cbb6a4c957a5507219a59a9790d8387ef07330c5a21ddf8f2824320f72c8a8ea2ffb9b5f86aa38121a57cfa7dafc0eabcb27981f31416131dae218fb70382d7c5fbf2c6631abdf11ad0cca4f6bf64269c1b172a4240de389636c4d1f47e7c34b04ac445b174a485c38d9476ea1fbcc835676198a8ee11b0438e3f7731d42e13782a02e2214d061911f48178ac53cbfec55b86c6bc78ed170a2664037b109523f25812fdb0df09bccb8ea4631a77b2fdc37817da0242804fa8725758eafff248e9139b86026e1ba6dd37e9335139bce7c9c2c7daa35f15d6bc7b7844fa84436c3d6c1cd0b6960ab2898350f74924d63bc842e040f9589251b4ea0057419198e44c5428556029294e5f2ce468dedbb0724815cd4525f4baa4b6650e9bf06f3fb6168ad1411575f1d40d0218caee8a52e11794c58f75dc7f55a72193b7981baf5eed56f3120aaecc5dbf1a0d6c408a186b61d7769c0d269d2225d6d9d18a4a0d21566a4180c66d798acaeee93e7ac937cac3cc876e7325ccdfa7119c2c8e93317734992d7f2bf70ac4d4994f3c601572e61e5c6b49fa17c0c08b646d887daf83d19b485eee50a757baa338aedb805f1c0cebbc1e87ae9c1a86cbdc4a693ce36349933923436f556ac32fc03e03c7d6c10689d3e15a7fe2c7fe82556d963e295daf2f95f1f6ab43dab1b15a945c4d01a21fea7a67f22ccd39edab73d3406c1797ac12301e86f5ff59e5871d78586532d0cb7080f7b62e56e179063cb4e08efb4c865aa897916ed91a1a955b2cd61c1eff37c1576c72d3bc643a5acd2f9068d70f285250de8ddc031404599439607d89dcdfda88ef66760620e0a68825305fc180107570927bae9beb11ba2e605f25362103b2f3016ab168c874dd0126408b3bd7eab77ee106cf5afa08a2a8fcbfaadff685996fad2637f33981eb4500697e286926d2ed06b1b4c357ea0e3afc6e9303495848696de2ef67f9d200724498b9485b4609acf2db8e042990f92d4ce13bc988123cabbbe8a07b9a6eff665107bc6c5e586865d7b501feac95f9f53273fcba6e5c4d6c3c2668f2c9da55d38b7a15106374166b5b1e009995943d4242ce618cc40bafb2486fbc812ea4300452586cb8683f7a315b1c6e20afd263c7d0489ce331a15aa0326af6793ce8a1f1c9834a0d3c0a46ea4cf6998efc5e3167b88e97588ae84aac59690556c488406aac1e1560f45f83ea7511107fa483eea01750425fa94d50a6a61dad2aa3f75fc47e14004cda15a81ba82b13da4354a1f400f94f5b890ece720c14bcfb21c8106bcc0d00f7315fbe2b6b12dc54341ac52644d11144b64e0d4163094e5e6c0ab4901688bfadf5e700faecda2eb0dfe7236ab09625ee89b53bc824b71a84a33a59547b84bd4f21cfaa6ba56236f83dc562e54b8104e6f342aa92bbf5ce28a558530c8096ba058799aff7d7d1eff4b2a291224d39f077f1ed7065c9de85b9803a6db04be0896e6848b531c9bba904dd48fedc9a3f39c638be8f9b39053b117242c737f655b522215674bc60ffc9858957e0d3ff1d8e1605ad8cea2a8399e918e68cd1050bea665b403c9b061cf4350dc34ca0c143021ce32743e0688f97fb1ee3ade0f5121e9ad289da307b68459624d60b07a1bec329a7cc1604d263c0dbee0c4c5b9532b12bc82661a3cd0cbf76306329fe9b116b2f676b767ebbdd065dfa959140d30e1cfdafd596f6c31987c2d8a46b743877f1b4039797b28fe4e247b2b5a81522f8493e52b11d6c1849f0121aa779d625be005a00c758a4d43d282b372bad97c9bd9a026a88080443a9ba1af1db80ce60fbd2fc2fb37dac3f320c26c327a55f48e363ae9a9a297ca4d4d01acbfecfcc0a17f885cfa19fb944ac32986d0128d0a4406eb16dd1396cae2690ecc41b453f87685d861f0546846621bda513299f09a6267b3e1197defcd2d708967b9a37326f4144b42b9bf6801c6efcb275ac33d452916a465d63b5a843b0e301828b6e30eaf36a90eba8851086adc0f09c90c7ff85d040590a23f997255503df4f251e667f3f05e44ae1549511fd7d032c152795f7f8aeec95f5e6b45dced4bed22f92dbaedd09ffa06aba7db67ba928ef4a607480dc872291306ee684541db1c1092660f98e5d9f6c5e25c4fe14c05c710c7a9295dfe238a4523091712528a5110e29fc1ec3bc055bf455bcd7f86b5da1fb6d5fb728334e598c74fdc89036af07141829199ecac213c0e4131939d4dd714cb8f75d64e57e9b4b2678c73c4af838f7f0cad030e86273b23ee5afed676302781e84f5ccae40ddcbc4de25443885347fa874710fe96ecac6fc07dcbb548c3b079d9744c295a16c061aace2bb194ec0ca06e5e959ce01ff1c1d52513b2bb2093be3ad999ce6722957a9fa626f91d5b92d3f24315b115645790a58adf2a18a0188678506b1669419f28f64653844e22fcaa30c63da0b915ba982a4d413acea68bdb9b0a63343a7aecd33ebc3b1598f0916ff9da4767a0dea127971b2afae3231b1cca16512b145db4dfa8338909397fbb7119d70794a9ffd6190dee32fa2b4299fd2c3baf054d21ebfbec24e4417f5e6dfc596d406d28dce86420736032c677d2ce7e0c3302423b9b7fa14f17d90297eb4f1ea7bd1ae7c87369b441e83436a797411a8abe51953d599007c5febfbece57f5c865ddf9a551f8e7284f46421b56d3d0d206154da37d168ec4f0545d274de364f1b1e83a385c28e9710fb3221f33d80417f06528d318ac3474a750740c69696d6a8416e37deb2d42b7d8702ce2c41c1f8aa2bb281ab5f87e62cb77da5ee095a195bbe01b555566803b75c494cb0ab31964dfe934ed4085b308d0db9bd01f2ad81acd26e54c160ee9a45655cbb54af0ff2d9fd62266d4249d6611393f3a44e7926b1128192b945e4c9433da235dd50889c6443abd00d669ed55d21df7b8ef1dc308c73590a5af58ed3383d06daa7aaa74225eede32a6c8b55c6e124411077772bfc28b98235b0e170367641ab6e3d1058d69c29844260ca055ae183789709af52ce04a6b52ec81822a3aea2ac8a1d79afbfe1525eb03330f06ce20dff34194bbea70a0add65ef9f7e03dd40dd78fe1cb1de96578787126a29744edb496cc1bb6847f9de5bdc26cb63a9497610ca290da80d1394a70ada2d79ad0f4e405d0050b7f35f2564f1276d5eee94776ff908786d86d058e334b56018a742cb2e1694b9ac032b4448d27b8bb5becc05f6dd2208dc1ebd6dd13f55fb93fe761b31be5c8a1cd7939bb01931dbd36d65ebc851f58d077187aaf7909b07c556dc5e293db7fd6e32bcc4c73239a80517b878cbe9e78ae28a2e7a2bede1159f54893a5f437df560706549aae3697d6dd2b32724573f638dd9594146c422d71d12c80795d30a06d9a4711342bcd62359c94e6fd6e850430d889aa78b84d11f1e00441f286570688f1aad6eba33a6884a3fd15e8d2caf944a5ca8e36c4fa2511cab2f6c01fd867b94f776dc5c8a739ff1263f7991c8ba12ae7003fc20d3338472afe3537ac061965fc7cc19ff5b0bff4b6b138ec62676dc5fc1d9c27202a48addd2fde03f481628ce7d870648c716a821fbb0591fa930186e0caf56e98733190a12022462ffe84468134c6e5220c61b257e7faf78bb90f0a502908ee1595b6021961b2801576676d1215ba3e8cba39913ee531849fde9f7ab4db3883fa8aa5a4a34e863c717e8b7850c4f72025f6d7c8de826f5f0116ee1a89349002e2c3fdd3aa3dd8472415fecfb8999a75135a5a3cbd6973eab6e4559e8c00c09620284c26c765927da24a201089895c0d2b12c6d3396f2db2e4e756403c08855716296411c265c2fc6c65bb3799803c6c1849f0dc6d4e6d07a75ff3c3268c1c4994755919cbf30c14c597998c71abbf84ba1cf59ada58bffb55d22faacff7fdf9753dd3d8d9278ddaef803d5cbdae4e7a58380a3d8831c697a879de9e165a8a365854ad4861f9d75d516aeb865678db45e24045493c756d57254aef1d8f12048f123c6fe9ae0ba7ffe8e2fedd8d11dd738cd5da24d0340db7f84636d82637723cfb80770d840489e397e45d532ae9c7c1a852529bbdacc6d58f645f7a5655cbff5695eefb4829768ae53c1572163d2b9631e33f672785162193b6771ee46e2616380d690c1a59918176c9be9083e1c507aa163cb2ef2cba004d5cf18089ec9e5d449d2c0925b27ac542f9e5c54f1d09274370146ee9d9f5a7c59239246df7c03ad3ef1f866d051dacc5af20b512ac5f2d1ea310b0af8dcad988a16264c598a4bad474b1a0601300bc708ee9cdbda3cfa3d810dec63bda76d3aa8edddc50ef989e9b5c1eab58a69bf8fa897e19e71344e212701b9f996cf85534fc63a19e5fadaecc853ed19380e4f2f73024f73ba6aedf41bfca9aa96b71c2d413107addd7db19483a8f0d761fb3bcf9c21800c4e709009d84791787ae865ed045e414a4cf3d0a49b1b05bff2e60d552457d7c6959990e8bb9b6480193a05d9708119f80a588881b7d95669f49d1139cb33fa54bfc03c46b7f8c137dfab0447b286c751c69a8a0eddba11f8fdde77517a02a53f67c5b6f9d9d91381646a224b94636afc61fbbfaa579f172b7175e504e144356240ac1cc2dbd31993b92dc396def6adf726795e299bed822de95729672cb1849f56244bb84cfbb11dc394f030859c47d2e74c45e9abdd760f85cccb418a139a6452ff2f462ecd3a38756208d053afe6192e356a4be55df756a838293561fa27e4f44c07ddca8621395840f6583d3765ea755b434d40900df1b9f84a64f42f5bf4941ca013a9be6b994be32f989cad62266dd28efb8465709480829e455054f906779095751d1733354df7e1ac4640ec1324f3f69ece62da99bbd0f471d87167e43c832850cab7e54b182ef02663e55a3d24d2491fbfac944a01ea13bf0d7be025618d4102810dc95e7f0e2c112f58fd554f4aef40120facb8d821489bc951bf408e52868e8907137bae067ac81ddad7341c9e27d55a7691026bd4b80082422a42c15c48023b53c42df4b09d081ebdf02d0ccc663c74e8b565429f8d66b70387dad93145292c04e5628eb42271c377ebe9e87f03b9dcea458776e63f468b4ea491ff568f4c2e708c270f03079ad1d4487f2edfb406110222213f213a4bf815dde300f2fe32f504aae6c6b9b92695200cb68ed91664c36d07e12a16662a21213d0ac4ff3119471399719a2f48a278210737a0bf6154360c94de10d25b06d5486e27c732265a86a1f7a96e7201bd7b7faf9428b528a25042d8d090e1021a4c98874b0ab1f0df8a545264dd53616becdebb9ef5e9eedc8bb6de8720e388cac26f7e4cb09f0e5ef74e5bb00bd7fda283a3bd489c5729458086fe952c05065cc53778d3ebc96f25bda4acbd8e3398ff6ca919f8a91ff185d0e4368f7c57a8ee6267387f1eff887488bca8755a5261955325d2bf1c64b9dd8e272ccb2d1c9a23931c3baaacd4f14f530d8beba24ae2e5e47f645049c703bc2e4f735dae349c62fa526b1dbcf4fa7a646c770085527698d0a747cffe8dd6b2d00abcf63a69f4b4c2486cf4076a9c2e07885f0857b50f0515085af29c8035ea69063c6739dd2924b392ddb4b049e30e0a2516ee2e1a066bd5531f3e376b51531bbcc3887acefb331d3452aba6706f774ef1e486580667cddbbb98c702ae2d2c025449e66e1bc3267b0ddc8b49f9dba2384414d2a6bbcad9bbf6ad6238c5169ec6ef9a87048122d04ade13090bbc206d8d2cdb9d57d37a1a720ed0adde1a50fabc842a71e833c6edb6fa00688d9278c9297b9364ba9b6dae180e5104e0d300002345b90d5610a3701a277a30f9fb4fe76c9966ad32834fb16d2416b83efbf1b039e46cd26fe2642831869fe8188e13d4f36b69069ba2fc1bbf58c3c705f58ba60681900e7ad39d1a8a194589d4c47fa890ad39e4dfad9cfa4c97fe3f8d31131f53674a09eb8e81cee429eee002875a2ec7b0e6fcfff78df1c2b14b2e049b448574f90ec0c031a5822dd7e98f873488bfd1b155a8c1067dc27be19b0ee8d33e21fffd0d300319824d9e61ee702d847738349f980c9b51450d2d153e2116b0f9ca0e68b846e313e20cc1cf103c745ff98d3a48bb387282823c1d51ab155cdca83b022a9a90153d2424f625962893c4a1177f26bff46688aa1b7034cbe1680d6926aa126bc0d90455e82b3e388edfab5a6d74a2e0ad3edc41317fd4c3169dae32737eb3e70423180d101cd1a5fb30b4be4d12cf18a60ac82a07f1522919ad02b13d3d1673e88921851d63faf07e9ea60b4dd6b4398e47fdb2c13da24ee314e6ac9cc11c3607eb3d298f178370a3f3787e26ddd13aa01054814b66aca7a5318d3cb454685bf71bd4a19dfca6268a45a3bb8430c8ed0313a2509b26c3c67b376c76e0b540c761850206b7b2f509a4a1e00d998550f292c0a876c0c03d7b6beefe124843aadd26c9884eb6ee8e7fdefff722c7ad9982b23a86c252f1dfcfc1ac2cfaed0f846581bfaf03df0cc831da735d39514ad96a18d2699ae3c782efe484b72c5ceee2ce6d3331d095ff320a579df15390c1e6b707da8d3c3a46ac7870c48b4734ced92c7b070b911c25f3991ddebe32611b8545ec4b06128e6d067dad56c4a57826e2c2c9e0020c1965372efcf1a30e0b53c263288afc9b49470276ef6dd516cd5d790324a2e890a372736da2e94c8c5d58e4e5fdf56a6c76306cfa239fad43910e75811d9892900eeb8520d2c4b7d7013a2c3c3a249aa04d84eed6c2f6c6e4d7367dd060427d7970323bbabac88c846d9674006fd959cabf90b3ca974f4950e8052766240490304db471993a496f50865c1ae15eeb4a5bb1e8334650f6c70eaf391f85d56645710a7ea478035d809c9f190f386c4cc58a07d857deb704c5fd82208550179c3f63a99a879cf9ef3d7c3d564d7a1a548ff11ef1cec8c0b627744ba837b4e367e0dea567c83f87da8bb74219fb8feffcd8d35a1d0d781008af5163d768c82a5afb484f34fcac31571331f30b82dab193dc71560dfe2e35af54d99ddb078b4f15b9a2356a27da6fea4457694d183a1b4eff3eeed375e6235de683f37fda8cb3002d3bdb310ac4640790f08e2f4bb9677520d44a53cf8e2ce903c384d0d6a3bb435d026304b5b1898cfe612138364ebae973d32037f1832388e785798aac5bbfea5c6d2465ef73400a3e39dbc57230e0e029dca06e466018644c74cee198d63189763f6a024ba3031ef08ac2926274d6bb986077d44b1ed1c98f91a76b19792e2501b4aa6cf6d65b392d1f43cd4fc3647acd7381259688b0c8d7f433f038e6a54a1770ca15c6a633060f65eda07482ec1e6a16aa5fe362d7fc27bd25480397cd43280c5bb6a2467c58e83ea7e2683f03424b44f003dfdd08752ea9f57abf7e4ad693490e0fedbb01411d5d2e884f29bfc5fdd872291412180e09817cda3fe7986d6801defa176b871c6e62f5c5f23f34e09b9b2cd787c98099d46a3282769ad44cf2ae81587bd565dbb57e97f6745595de76bf4e6cd58bc01202e595f6e1bd73be494ace8c49d7faca99ece5126932ec5d52cec87a014551843abd9325d1e92363d27f44460a53647107288f7e7103b7cc297c2058402c87405c340557a65c5e3fb7b37d3070162e4e76518d83c7d67068c79a9ae8a2759b6e14ecda5af980bb1a6fa1b488f17593b70b6338ade329f95ec7cea1ceeee07ac833021a60a4d934d463db4e12a9e486d9c936f0f1f54179f9ba56c6d5689829d99cddb77df2ca2857cc304d2d0b6cb94c565973aeac0b468a4806fb71e89665edbd1e0e47466c4c78b7aef002de4c13c5e42fa5121c09313a2cb3e25498e3a8a7a9f6f85443aadd0c5db94b07c7c7b70e4aa2fe87304acdd636e52be44df0c27d346acc7bc54f531efbb97bd46bff22f33b1363c2c0d2dd05096fa08d83380299ff648ed7a2c43b5323240fa22ee12fd5eee41620ed8e80c9fb9c982f566f1e8ca9dec53a144d5b006bdfefe891afc52ccb51b92b4259c7aeffb2bb060dea31257ff2ded01396a1ee19cbf97dbfcb722cc567cafaf0cde34db1b50015a0ebcfd13f298e88bfb2daa10235aa9d31bdbc42b5a61737137348ecd6d6ebdf43135ecef16d9169bb7b916f0f57842c3981c0d31b7dd41d3921bf2f023b937397019cee808f77c1d8e83d97233a9adcfadf045353d352d92ea645b4723ef058f8983f228083d89d8ccfba79540bef3a714b62a9091d964e3a2eeccd3f5f7b96e7994cb8076b4a896c6fa2d0234cd2ecae4f9726bc5b1e3effb7c2f7dde6cd5fb2b41293e5235c20258a28e279568ca6b475939b9c76b4cd119f9c8bb4febf8be1ea9775ae1b59d3aaae69d7ada4a5bf7d24011850d8ebacf68fbc22fca58f4c2bb8f45d0196cee6a07da2953a544e21cb85ec88a6588fa107d5e70d7c73ac2df3f7caae8158d961d4be16eb847b121ad34a0f73e253b828d1cbe17580da765fb74294092e6ae6f610e57d5bbd5907a50196c66eb61ffcd1800f832a0fd35f1a11cb07b465c8acf4f1d7e35d12cd528b1ee77ad274873fe1e45fd259043b805b20fee025a30058899dda75382a4fc077c9f4056884378ba26a477c28a676550237128296d3d5b7cf1cc4a41b9e863bdb8598b4ce5b1807b34f05c478f5978c68fcd8c66c7e69508e7c6ed5ba41548bf65405e08bf154d28981fd0f8060a8f80ceed2b5b56859c2abcbec437b337189a87730882540a921ae6527e1bbe7d6d7e9b1da252951e248049cf41725a3c7af8e41c09c4c4a26d70f40272f665743fd56a4ed3ab2db507f785c441b38ec1db7d14f6664a715cfdb070bec4cd273a100a19bd71c04775291b0cb3d989e1b07a7f1f064e57769f4c1875d5f867b75928dbe425ef641cc5099246958ff155b9489e16e92cbf3bb2090309546fdaa3e8a6b599261235f04e0c712041ad01c0f9bbffa8854b5b86ae1d3b6610cec81c5cb904bf89eb1713c7ed1e2992da31cf32438ecf424625cd9d40945f26e832e58abd8d8e41e8c0b2392ee1d9302de22cd36bbf79a9c9cdac250d8e59b3d8da62088679b646168c7eb2d9a77300765ee173a89279d6719b2f90c605dc6f5d8c8749de043e5102a74120d9d9f87ce19a253c8e19004d7e319447019f096c2c6708a530eac7b7f3c0291567331af67df9f70b33845fdf09e17d09a73ddeed4317b6bae0b596b2c7a5bc852b966c1815971065053d0088b23eca25e95e2d92c28b4d284733f20222bbe8154a4ce288abad2d0dc151cbc53f75fe41342131afb8f34552c6849672232fa5c8b824d40f0a1a21c78b807f594802805dbe34dc40d2dc1f6363569eb4bbb3c7ce1be08100716571614567d8208a5fb2b718b2309e348aac9ff0158657398f2dad56e88ee00c28f339232f73df08365e00e8041fde1d1af8b397f3b51f03a1bc3a6178c7d090225c197ca961f32c4379309e6cba63259ea29523ee5192284d43b45d9d4dd29782f491f1b2ccf53a328661dfd9288f54e5acbd1b61b920abd7f8519a3f6d54273f158019500dba50b8e345857a22620f13b16b4c9e4a7d23544bf25ef4f6512a93ea9706294d1e87586a771774212c347bbc9a0f5c8278c7e29eb489b2db5f9d13b967c52c0ef36e1c8c364b7a85ba2bacbec593e79b8f4662e2929fdd8a6aa94c467c35f482e21bca9449c6682acb77f0d8a1a484a6b984b9c89d7853b36c2395698f831b30ca0b83569266c0e8fca8752c701cfe7eb10ee6c076ad5d82fcaabc7cf0a377b4c4df4681a9906cecb848f13bf7f94ca67b514655a1a0758e90cf254da72117960fc268e5a541069f5233a7da299acefa2b1702855b499d15a47315e6115d3060e4929ff4aa8e8de7df4e8965a84d5dc3ff5917b9a766a882961defc383e16aa092e1e16566ca2a2189e3ec04ee85c2a373905561d6a887e2aa1f077b116bb4d0ebac643f361accbcabc299a75deff2fba837cfbbad323edcacaea48f6aa84d58583be8a0794229ca0ac1c412662a04fb8bb7ed9813241cc9efb366386c337d068a8b8922cf50211f2e03c5e20e6a8b809858c6edd048b5e8591a7344d9490171d668f0cf6cc9517e2248296ee393348fb88ab8e44d6f872dcc7c7396365acf7fe3cf995d8d340441391e37242f06d92bae11f7bfa352d55202813ec399f5261e65bfa4b65b9669858ec8d4986c59f276540b615ba2a19b92c240b3ed212a7da962da85d6c3455509e08bb8b71d0af42a65b9959b90c1e4bf90368aa0fd97e995e329fc4a860ade464248fa7b61e64e59643ae59a8b7f0199961c82b51d890834084b2ae9608da45824076c8a678de19af6c42602fbbd98ff4905f17f62a38e2e3946cd589077739968c92b504b3561b2fdd98c414c13caf65fcc660ec83423e06079054214b6ad181407d669d4317feb703fa7b6e8bcff29c8d4e80ab5d61d8fbc878c650460788215ac86524e5182755c110c5fbd4af89aec013f2fcb86012571a9206a2cc067ed51a1b711ff1cf34b195ea85e045ee557e92c9802ef264c48376db9bb020b68730bf8911ab54b5884e1e413d9fcb3ab5167853eb92626ea0215888ed878e4662bec0d6e7119bbe3702b15d6a94295c731135d4b86a7a3edc04797484210aeefc85b3f8b4ad4dffbf1a087d760372e75afee45a870f9f33d6fc352a14b48be5970540eee5d9b0bcda085435607a470ed5b52c6f86fd7a8cc2596191644cd2b0e32e042969edf901bdadc68f540cf4700b7534e7344d66b25f922db8ee6c07f3f76bbab3fec710106ecc750969c4c03ffc47afa56f82692d7cfa15cbd98d27fb19a77564d897ba42538f6218b2c49426fbc95704940a0a0bd81d010266feda2d34704e2bf226038544804a6ff6139a4ea76c85c75f44cde949599ae924547ee31869ab0d99e162f6f4f4b6b17851268e2644cc330dbfec988a8ccee1352c95154ca852329e5a8eac03b3b943817d6231419fe40a91a5f3e8515466aae56903f440a9d4cb3c1b64c6f41ba026011eaaa267586e1812fdc18152e3a867502aae77a6246793676bff5ab4a0a688e5a651c7d758dbd88f071c488c2856e8918751c4169741fa21c5692f14ccc5706c931c2601fce671cfb899b2ce82d410fecd97394524fb9fc3fa1d7a7b3192bb297c242108294ed544786dc8aa80c193a47fa1b80cb46292ccecd05c0e0f67c22d62c5b5c24af3569a6acb01e39bb89f52c0fc0d95cbb9557dc2793fd5ae2e57b35f6d5060fac69f3ab340493e9f09082651d6d3c3c9556f87c2a1770b301ecba08a3456828aa1900af812c85c5fbf1a530b9df0a02dcbcf652a38ea57137ba052ec8a641e1044064e9b85c6cc798940abfa7f5449af5f009b96a9445a1895f552ca3cb94a54c14f9166ef8702f912fbe7cdb9aacea30a0c14db8a73fe5aca73d9d6cc4d1400b251c66bd1dd8a072097eae583387e1c63b1dff922cbf09c143e29647c86f51a705f9281832dc92f79f6b4590472e4dea193f6a0ae72e87ffaf3f1a28d5afc441cf5366642c85ce2a643d34b43c6f1391f950577c4548aa1f5d08c3391938de844601cd418cc90d6c7e98a9ad4744579776a540ee80096b8deaad5dd0c2b538dc443f4571ca23a6617553344f907472d83d401ee8398bf5e8b7811789d85d585b2a44835e02f2fca1d39265831f65af37068ca67deba448ce78fc03e64271422e25507fa8d280bb27040307d1703bfd91bd51bfbbffe7ece18b35d5eb68f62e9d799703e225a428370487478ee56f1e4b3feac41f6c067e9065b20ec572d490ee58b596e3c1cd23fb03802497bb9a8dd5984f377c2d5dc5bef7f537ce4f06f176933e760b4100f0550f00fe1bf73f1d03a0bb45f6df71327d60c3714daada11a861002ea4f8095bf39a00165369bfe79f22e7b50ba52194be158348ae7551475af0e2a85961250d65a68ce78d1070ec48f4b526306cd193b71e587e66ac17f1c9bc4bf5e6eb0184486193751817c6dfb131e32b992c99a5c35dbe9939a3346521c42d8ffc2f3792935c738cb1a2ca9b3d8c5df1196914c890dbdaf3c0f0464c53e7daba20cd36af94df9ff7dec457618bcf3a14b80ff95a609098d6717e115d4117e5f36f3df2a1dad58311e4b2aea2eaca251241fb01fcc43e2d0a6c1cca5ae7025e2a06935aa9141a290684086caa9d8e1ecadedcb78616f4a1463cb99433de91b3c6eafbea3e44b340b60e8d6de0af7238beb6a2184d90a33c980ff09a868bb14dcb1fe745ee7c077227883bf1df5d426ab086047fa689a0e06b46c4e0bde395c223b9565024da79176c31bc51b72cca9e9db49b0ca4743326f87990437ab3202a997a106a73c02d58249b7534c75a5a95e4b436b480a04e0c3b37af097d3d7beb02c31a74b45f381815e27b616902695a12acaadc6264fb5530299f3272bbd39ee0b3309f5de4bcd506f07c6760a6f084c3eb3045406d925ba5baadaa1b3436c2789594eab38388a82aaa374252500ee00d5d0094277f6cfd6d4b47da876f392b2d2e8fd48e285a85163ea15fb3d300d1a85ca8d13a22a35f88b41533174eb6b46c79259a65bf5ea91f87285b48fe5b3bd1ee1f7bbb977a8ee38a6a7d120799a08b5debd31ecc8dd9d870ebc4a0d8046293986a4fb168fc5d9e2094da6610032cf06ff9124ab21534c73d2da7cabeb785e1a056ddf282bd8d1e1a24e4098bd6f068da1a48419ca2a29378c9e6dd3752657fe120f0d7cc1eebbea8037c8560f3f7f05c78162b178afeac8a457c79662f56712994f7736488295d8d6aa04dd2b6484418a2b21d0985178254050698d8e447f3f28ee01d81e84480f87ae7f05763f4690ccfcdaefff156d6396e1fd9e124af9c6531cc0049c8ca7344aef8b59678db01310ca1e557da614fa0f18de42ca9802b6894b3a2c481cb71d315dacc474f102b1aff72d8b6d5b562e049eaa38667044e60b59c44132674e5ecda8c93c4e0ab5a0ec98c5f00f01bbf89a4eb27a07f0c4f7707abc115bc54a6c5d31c4d1641d7b91f25f121d5785e17c981154dd91eec55b8cc345f0e02545c49a8f53684021c0e7156a0d5649fe515a331677213943f7c9300205fd633b76ea942956898b49f91b73f1d4c5b61dc68bdc709a634ab3ff051d3511d535a2d1bda6c195b9c948826597ed196bfc89574404225b5596709ef05b44c43a27d870a75776d0efe27bd4ebac5d25f1ae85bce947fee41fe4337fd03983fe05aa0dc16e9bfcb998502ef7f50f37605f575569a18b04f7d4f139a7e51e904a0defc3680376e50df5622bde1f502be042edde97bded22d97ff4199d58634aa651938e189f72208d889f97ae8982a1503ec871936c81f89265ce31ba8ac32d14264a593863162dcea41a91966c438f4f175b337668a07c1d8a761e54efb58d499f68f63428f80b45ed128796271dd7bf24c3c607ea7edc2bcc73d0b49f0d8e262f00f5df12d7c2301dc8db4c972c1d1a5f6aa9f30af0285d5ab3d254f0d42ae5a9f9da2d967bf46e4b20a89ee98ca70689ca3ce5bddc0eb0b66c8dd465166dbc9477fdc09ff01a88810ee94514853b24b8deb3552e8708e1161ca77df1f81161c357eb91ed422fe63af988701159245d8c32498eb608ad692315f4ed03fa49d7a65920161e35633d413fa8453dd6a1d7f588bf21086c2ae5eab775f5f61e5b398a84c51eeb67e9f9e2ac5bd91be6a580090001816010286632f558404b3d993c70a1cf7498b76c1c52adc3fe6fa81b3521a1a5a54d429b4829a50ce609460a8c0aa6cbdc88e9c337f27dcbfc74112aca2230142482760c336d8f11c9c898443132a6d21613532a954a254dd38e650d5fbbbb09bf64d2a3b5bbc75c264b3913a3476be3934a9a74986c77377a27c6cc300638dcd5b54cd374b4ed6ed9d2d3a5b3afdf5027a438780226e45e7a2773d347fa27432232bae8def781a20ffc964711380231f8619d0f04313ea50775744420470a37c88d3412199d30dc5cb8653a9998b0d475611886210c0cccb30c839f0875b6371a7d6118ee0ffcbece0b75c2ad240b1ddad825a55ebed0426bb65f9a5ecba3cbcc2c1b759da4dab32fe8c9f2b2332ddb8258d475da6483f7ee42bc777a6783e0910d5ea39b288f76f635023dcdeb3ad135930ddb4517b25db4b3af0ce671ee231bd3fd6904cc17745cdfb26b99c6aa65e9d0e5007642cb321aa3dda6d2b156caf4612e65ad6e251bb687c7a132c754ee2192d8e1393d0e91c4e6c252095fdc90a9cd084744f04b9a88e874fb41024cc3d8baf4f0dbbbae2bc1c0c068da7798dc85971c908bbb81c6472f65fa8a1b42b56b2b65ed156780b9691ce75124d2210d1a1f692433b84c2fd23a348c4d63d33328a559761c2a2333ba687491cc48241269178944a25fd175025f37d9b0c3d38d865a9839987f1a044f22d130a88de8cf929e3c7ad245f7323d789c0ae6ed9f7598300c436e72446ac83dac303030a5124ce9fb604030775e498f3adba6dc431c9a841e6d6e9ff71d0a82da6403e9e085900eea9d3d1a1dd9a3c378301a01f385efa33cead81f3e69ebf26593b6e402094400e596d1c149ba46d2261b4ea2796cb2e9479994c7235b748d6ea31c7e67871a01f305f060f72f636db1ecedda86488286b195cc16a84909edfe561bebfe806f3dfda053faa1444f20028c61592683b7ee74c3a5cd719bce1181dcf72e8fe027fa4cdff6c3f6303efd7ef861d3cf74bc95f677fa6924a652699b4a22f86153510bdf657094d329954a9bfb38ae44459a18ca5dd16eb5198134b486fb7d0f5ad3fdde7fdb876cec625c2afda248b3b75f4d4b9c2cbb9619f97e1d06a604f327e1f552f624d472c7675f170dd9971eb14b7b2fcf3deedc1e54a4e26665b88461320e3454bca7d9b66b1b78d29f7c0fc1932eeb09bb91ef6178d28d940e136a927e52ba87b136d24737125e44bafb66fa49688de2be7a1cb2ad83facbd7b9a1f9e63a42c321c0db6692b98c8c16b86b5e0ecf65fabb855474ea0999636cf01cc7711c168114d3d050916a586b2334f45f4fde719e63944a18d713c55a3fa1a167604fe47ddc908d8f31c69886666321df699039c6f67410738c219b9a72c07b370901bf2db9eb3fa1f1199f1c688406e8e539c6c81be9f1c878647f1e8dcf00ff848696f524d2e3904d3f430fd9df17924831f446e6a60519195896994cd6f42c9b6a08eccd90b7f05b6df4ab8cd2c516f44823c32413532afda24843a3b3ac3a3794c3210867e34f68fcc62767ca61c66ddc088ddfc8356a68fd27cffadf9941e346a6711b79863723db388d7ce33ad7f8733ecde3cd38840a996370363ee34f6e9c868dcfb8911ba7f15fdf0800eec2b5068096f5f4d1bbe0c3a69c369972d0af71237fbe7e0dfde4cfbac8a636f28d2ceb89d323109e0d3454ecde5deee419aff127349e67bcc68dcc780d2debc9088d67fd7aea9e5fe3466edc46ae217443631b7a88e4a8369972a8717d23f97f0dadb5acdd9f647d7d534fdd77360ea59165ed3ea3bb7733e42628db31d1182ab4ba6557ad74937dd9ca11b9ba09a54660026b9acc683709639c791947046ff7cab20c671eeef4055a1d86614f46067b32187bdec8c3dfb187ef61cc84d77151042e5b72014697d2965c808183ad6599c9945dd665655c10cd035fdd665c4ec5c7638c31c618fb52d1d3df1262adb51ed997f85a6b3d224e113fb07132f8df77b750fceec5b854fabfeffb3ef0c3f89ed7799ef789dfbbe3efc8ee3e1ed9187f5804c51247f270d76ddbf62c6f5dd7755df7017ccf75a6aeeb3a93288aa2281e11693080591610bbfa65f120ebe966da075a4407b80e7c97b74d6fb73a501b3169197d0d831803b1f1863fd365bc6f7a347dda886963b2bd4db3fbfb066a24a524767b5cb76d1bde36bc6d1dc0db86819039f85d26771b0f32d9848dc8dca4653d6d9b8c163923f0ad36400061ed1438702f8661adcbdcb792c9e277dda6c1cda4e16fe30ec4ee6e71386cf73eed186b3a0704e2c35c122b533ab61b5b3b2ac184a4d1280cc330dc44222c8b3870efbdf7de7bc17b2fc69c05626827a99b15b01b71d4c56e06b2e40c1d5222cb382d461381ffbe6b79043feea28ffb34d13739037e9ce671ff3412eea28dcbb22c874a801789f437f369afdbfee54ece8c56ce8c9bc42193ad0b767d5656fdc52a0d744c985c97062e151bd4a31cd57ad8d7b561b34b7a6d7a2c651aa33dd8a86263a3b50ac740e999e822ee32e1bf87611e9d668f1bdd0bc187dec3531c668c690f5e88f1781bded8c3fe30e68e3d1b1b46536ee344a5986d2b954a25eff34a9fe7799ee7791ec640f8eff33ccff3e84d9e67f24ce0f5e9d1c6a6b4fb6863dbb0e125716ff579511fd5a5ae80a8e8f4728c449675dfde75deb61dc6bbc511d9de753aa7bba6bd63ed32591bbb6bddf6aef3b86fda76efd39af7eed33619ed69dcd66d9be76dd7b66df38eb77bdbb66ddbb661243c6fbb2949bd760ba2d16c6cecdac6ddae3bd2594e8f776f37f38e8847449a2f34b8e787761be9322f99b42f87a7998bb95b383a179ebb4544944771d350e77ce7be8c3fcae3f7910e7ea4f008f77ddfa74d22f08848e780dcbf234736f761528849c71a09e9a0461273ac236e7c4edca44ddc31a5980da6548281818181e9baee59ee7af0c5300cc3300cc38edc985f52292485208c1e2ff791e8d33a2286899778893aa2143a22fad0dc7bb3ecdefb99dddf7b2feeeb575ea7a1f9a1c146c83c233d8b19dd7498edd3ba92b45bf84e098d2d7a87c1e37b3dcfa35aa47f3081df1d9f7a0ff308e2d1457884f1a9a8c8f63a4f9b40fc0e8f458a80a37f79c417e9d13d8d647411cdc65a87668ff406538a219960602825919e9d4449daf5de913cdcc9c85099675946eb346c04be9cd76158d79dd212290465bc440a41ad61f488495aa43f3d8abb769791653d75dc69deeedd3ac6369af12ba679a41dd53a343445686852d80976629920a19fa2493fe71fb0b3e9b516f3f196b46f4dd3eca6c95ccb5da795c68fe2d6b40ac3306cfc2e6d96ddf3bc0acbb0ec38cbf2bd97391d5be632b7da2fed22ed21882f6b8c2e6991268d30ec15c783e932aec9a0329ee5d124d217f676ea66ca320cc30b913bfcce16e52b8f3eeceb3883e043d133f0f20ee69d7d61efbecb9cb64636f62c8fdbc6f4b8370e07fa7918ba29a151e0a74ae5665ca3714d5fb7f0fcdb7dee5c16e14c22ddbb566dd806514c4c4c4c0ce5c1a3721bd9dc63f46864c75cb381b98abb0efbb0bd219344e96bdc8dcba26bdae47262ce99ce1df370e3363eeab857541289b4c9a48ddcb88de3dbe8be4c3920df47380135d0d33c0efd2a8d73651376b9efff04198465cba693e2d860049bfede9636c022c6a632689b02059e800008a89a7bcc006501296150407661451528a89bcdd3082f88802557c1842f94506d364f1e31788ab0369b6b9690822e50dab5d95c638526a680d2ee66734d952c154069d8128640851e649bcd72c8053a7882b6d97c2f565cc102db6673938791c5044ca08888210b282c01655f739339c5145044842240592da98f116f9ba5129ca8a20baa66c9832ba0b6d89265d321f06c4a25ee6440228b0b50358759a0aa13a52d8f51f256d6487bedad4db49a85c0626c192f7bc45d94d45db5ec510665d9f33855db1c0496290a68b1ed96290a5cb1876000cb2db7e0f2c53c028f785735cf2d56a30c128748b145bbba8eeab86757353245819f5dbd065a43c517a0c66dd32368338abdb7aee350bbeca6b592527b59e520a6a59976e3b4acabc12ebdac2154a422fda1a3c70d4e0d54bcc1083d55cf0a3c96ea272dd11a5a2d1e4dbedad0dc20659845842165aa083164c04c206dd436957a9ed219e489aad261adb87333c5564b6e54ac97d993425a5b53baac10768f96d6a1d99516e26e1d2ab425152b0e344945ca733fca302e4b1e085a3356977388d68caf97f3a38e22bf818a219f5078a121868f618f8f251f1f651ce03c3edac8e13b3e8a928ad8c71ea48f37301f7fbcf05166a1f928b5c0f0517e4144722922bb841fe51729c64f1fe712cff17142f9ccc7d9840de7f1714271c3777c9c3fff3ece2a177d9c56708c33cb9638a48e8ff38b9ea3849cc738b96c2943be031ffc38c5d8f2f487d2004437b1a81ab413fa437dc813e0f298c7e57f001a2abec88ecbfb40c447bc9462c34b97325284bca429944b192e6f67b8fcfd71011c8786017c945ae07c94a9fd5162e1a9b1f9389ddc7c9c3d0190ddb98f538bede34c6d8965cbf3c84bae1e79475e72d164312fb972e499975c38f2922b061c7200801e331c5cd0230c0e3b78c880030d0e3b76f0d0e30c38c4e043071e3f80bea147925e72b303d9d023a6830f01fcd0e3d481c7817a647a24dd870e07f2711f7ac42eea90f1c881870ccff4928be9313b4c0ed80c7a24c5a0471fe791430c07e221831e6f1c268703657ab4712c07ec4019498f317c8603f9d0238fcb40f31d7ac91577f8e8e1e3403d00f01d2ee0007320928f0ba0c77ffc859c22ba861e2b9abfb083063de2c81169f4a89fe32fe454071aedd0e300725e80c1003b4e44671a0e743d5e03881b678701f413274034341c06031c0886c3a0c77bd100e10543a8975c1a0e74f558e92577007a0c0f835eb2bda04718be432fd95e38d08ed3a0c7fc8bc381ee2b1cee81aa173e8003c1a0c71da741eb71141e28871ec51c071261a081e14034a11e73d438508ee7d0a398333a5008c337cd71ae7f0272836e418f227baad1238e4f2093c6ad5e8245cf01c41ea885931e6b2e0239100e3d9e66f2926b4b2980781bb4c9e640317abcf492bba5146026007ac471a3c75900f140396c5e8003cd14e03bd0cd3573a3975c9b03dd9acfe8251bd0498f3317f592ed7420d1468f57ce4d07cab90e646bf478bacd8166f428bee63134f4d8825e726f0e74717c663ec7cc8172dce85144e340a28bf4f8890ef45d8f330fc081a61e73fce62d9ce621baa25e0338e3542fd93a3dd21a70a0f428e3e06b0df54028ee386aa03af2920bc781e685681cc40a32e33882e808320f8443e6414ac741f4137c20eb7ad43ef5926b4b0162e925578f1548e94095d3e30644f538e3532fd9e881a68c1e4b7a09de32072abda4c70a443f710254bfe9919e3bd00c3dce772fe9380ebde49a81d24bae8c035ded33f4920d48871eb7e3d04b361d07c271197a04f512bc671c083ca847ac97e02da506fd043c10be13238e02d2f4a8e3da81363de2f87619c7a91b67003408e0c70c3204e0c6a6c647d6f11e19c763c8f43cf23c0c59c769328ebf90e977e4f999ac63892d9f23e338659ac49617f39407409e7117b28cdfc832b7914baf91675c6719cf59e6cfa5d3c833b4a4620b598696543465192d1f934b50b6041f8254b69c92ed2e5b4860316484cde5ca4411a926ec2d5f35e8f9810ab6962d2f8ae8228c9d6559c3af20b780bcca526ad04d26aa86db2c25886e425135bc66294074938aaa413b91d407d523fdd9f204d0461c25a98fa9474ac59653531e22be1d14ad11042a9e165b8e5476f50ac59eafaacad249a6452fabba7de58854af96cdb3aa722e89544d244752d13ed3cbaa93cc4d77ac59895091a074d235399fc032c5c3656f2f85a169f657c681f4d2ad4c64e6eb356bc755600c3bae02db6ba7bf9eb7fc6553cef5f94b2399bfbf4eba12ba91c41cbbe6494e765b77b9a796a5f7ce93f734c96d44e47c9fdb7779db5d12b1bb6b79641204c976b93fdc6473dd0376f69c720b8374f72e3712d1e51eb9a9cd3c6aa3768f8252ce8cda25adf9f4a809c1846727a19be384d0adc9e420e60376f6a7b9e3c3607953ce77ed9f46d2a4c9f72e574bbb4e8f399de85f1e81d89ce838d413bdd3e3e4d9a24eb3da96b77ae9363791eb5b0586a1aeaed51d6ebabae44221db2792502b995bbb964df8a26b0fb3095f7b28448eced93eaf7ddce1d99a5642b7a673ae93ad6924f25cccc33c79f6e4d95c26bdf418fdee93b3617be994cb21bd44e4faa769dc4bf95e4b42b96ccad94efa76529614cf127bfebaa65ddbe4888cf6d4482e69370776bf32a7376f9bd7b58343af65efbafc523c4ef67cb79da4c7b949154744eb3429cb7d8d3b9ac68efdca92c87d77ede6792c4bdd9dc354e0793d9bdabf0f09f7ebdaf55d5ece4f8f54cbd77569da3f269e5632bf2c3797c78f7373a6ebe035d3452f9df3dde3ae819ef72e5f077377ec7dd7b2e9d3393037e17f076fc23ae73be85dbe74cfd41d1ffc954de14b99c4715a26c190465a1e3b9d43baa91bbb73978739be289bb88bde5d03f376980c9e9447a3c3e4eda41d29c40c750e7813774ee77c97f7c0cc71bf32be285fcff9cec9716e5dde2e391b60d8f349e6beae659dba2fad84ee790d4f81e7a5855110c4c543a22529685a45245b4de5a47a1352d16b0c2a7b7e57757a735e39671e4810fa7b9c7a8d7667572d2906c2f4f29e15b03da5af6e55746e8d3e678708ada817647ede7acd959573ea2b223c30266c16b4ecf9ead24e3b85989bd67aeb49b86d559984561a892967beaa74109756a2807167cfef6c4b3fa07a926a57fa01d5957cbbd20f984fc2ed4964de6aaa95cc3de9127e31d5d9d7bd18df707b76b17be453b81430dcf6d245dde85ea6dd078674f49d7ae14905d83e3a0e0d71e8611def340c290468e051676330dc4661a8916c1f69245db8f169b8bd4ed4712211c7711c2745f8ef1b799e17def08ee1a6f4babbfb18ee500759965997138c9f2e88bb95c8d8dff6751e77cfa3df2904687005d8d99be6ba1f19992ce3b8cb5d18ba8161024debb6980ffcba8bbeeedbb8afdb38dafdd348ba8bb8ce04941ae172e1c225cbaabb55acc9be19c603a7aff3905dd3b29e66a68b5011bb972e7269950205c7850b972cdb2e4edd72976d9b28dbb60c7260967526f03cb470192650cb8037d282960116a1a255848addad631887451de779187b5070e1c2850b172e20088220f84e4606ecc0c9030f9725bae67ddeab2222530e0116ef0a822b578866b805a532aa28b1e9317da98a0f5a8005087d1646f04ce182225c9003a19d9ed40a94b052821308696182951f68600b9eb0800640e80c10522ce0b1c73358b962a39452ea71406a7843ce90851634acf082ca07a8f8a0091f2a62aca0052ba882152fd8f6dd13ac20c1b6f61c07c45e6bc508aa426f6fc45a6bedc5af5620b1eb350e48c5d6082c2e50ca5460c5a6c730ada162d8e0a2342c68a0010df6f8635b394a2c5530018760db97ec6f826d2fa20009db9e268a6d7f334509db1e0705dba652302404832434ab0a2db3877a61e58a663920f5d2500595296c8a6f0c0161f6482d415f6417688b1b51b4eb1a155f6875abd95a29b56907d96408497da02847640749cd961179ec3ac8635ad6931eeb9554b77225da0802fe9e7fbedb06562ab0ebe50ed2b04b1b9f1c0ff79894179ea7919776ea110b0d31418b2c951a227fa85dea90d41099aa94ea64b2f4129769449c7a9abfd9969882ca96a92d535278d9527cb1a513b64c9d800953ee944159b6f4a19136dbe5af05257daa4d917a9a17aa157341ea662267e6bbdcfa20675e0421cdd6abd268aacd02aab0e776b91fe40c06ba74e1385b2b103a549cd80f3e77c3e00446b877dcf679cce5207f307c7ef0e932bbcc295c13b50156cbd6118093a02d65bbe476b05a50db0edb10dc37add52c002741db395cdab4bc388bf5a4d92a545eabd9b3cfef68134a9b8c64d7aee926560b0eb5d588f64c3731499128ad845da25e696eea8952bb65f2d8303c28d48792708077a5d7a61c2a4a7bb7e750b5a13ef2343f87e65077ee4ce86bdd2ca5c1b4831ec2847da29a4c14be77890ada62d37a6ac2c3240a3c87e66718537eb15e8a528a40117b9e1eb1e7ad127b9efa4cca31913318a016e3fb2a6318add829c54eb16f0cd393e76e2bdf9d652a2fa0c265526dbc4f265791523d99729828eddd37ea537db9526d995dea69f654b9a2aa6659966559b5560fb5cae950519a264d81855c1856ab4eb8edbcb909156765f20226571cf145078b62e2c417265fbe58f9c197100b4249e0d9d20a5b0a7579b283f501cab44376ec981e427b86c22e519333925dd34d4c52242ad392d341a26e3d4dab052551a68badb45ec47aba953437f5347f3068acca5689af60ab81ef96cc540acd947724a458553d54ac5ecd2b2584c4fca287d64c2fa458bd9a7a6aa182b4011617a0f458ca206dba7f9f5b6c60cf9e3c7deaa96eab279417a1e7c6000a58a8580951918a51e091fad01abb81274e8ad8d5e912520649835d9da4045c8f3f873e7feaa9bab8add777f9f5641dcb6e55d32a426fd8d953eb883bbbe56c88c172042b76f62354aceed6e6cfcf4fb591a8db55b9c105bee1013c4a214aefada7eaf4876ab49f0e4e9ccc7be78f3c5540e68e2b7092bb4df33b7ba6b4e9badcf313557d66a9466c5959aded8a3bf3562d6659b962f6549b79459eaae7c0c78b5d5544d8d57da0cc282939531d0333556750954d535460d9f2ca96292a2ab02507a448a79b4c940f374080c7999a58a6d0f7487dbadf6aa7d6e51b58e0cab45dd2ad3a955834d9058f4c4d3134a7a4a6f8b2f9a49ce8c0a49452aa43775311ef9e6e3251264cc57aaabe83c2415ff8506d3acd63b5a04c3b74f7eee9214c4789f554bdd326ee12d55da26ee50a4d3e1834160649ccd4901bb68c936ba062e5536d664a9eaa5713d3239e152d526da84f3d55afacb5160b43f6f566ea43c5ea57a64bd0ea266b7ea6bacc30aacbaa9a59765579d955555993ce1e5a53a57e96d8954cc2ae3e93c8b130f0c4b6348522c309a69fd5a760f4860ab4fa49a3501a14a5dac84cf9c162c527077ba45470291328618fb48a09b44851a19166d9f523b573ce392d078250f2e265eb895e622cb6a25ad8283445b1e460dbef8002b6af7c68cd482b28d546f254552e2ed5b9e5f6b40a0daaf63425246598a928b4eca02cdef692ea1317d81e8756f6d65aeb96da9ba4c0348aad5eaf57b6d6caca7c35eb7c9d75d659679d75d659679df5168bc90478be9ab3ce3aebacb3ce3aebacb3fe9ab3ce3aebdc8440e94f5cd028517ca82841b95b863f4880ab57fd830498f2d4caca2887367db5252868be6a828ab1a716a1ca67a62a9fca27a8fedc380993a87ce612160bad3101a61f6d141d93465a2aa8cd264ff45dba6cd942471ba5dedc84e168a3d8d8a38d6279aacdecd2656888f6c85b965a49b897431beff156d4d5a42970bd16c44a710405b2b0334b290aa1e10dac10bad1022c3af00545f31c7ae2e3044a4041d16c8a02752a44e019811628223c2d76a359c606503537994d0c31310414119814d546b30c420f503537a915684207544011314a01ea5a3a9490440a45c41dc2b5d12c81900554cd4de8145350d00545445804d4959755d96bed0f72669e0b1864d10545f38f24a076261194522542bc517156405a21ce02e35f71f67c230dbed7c3b82b95b85f8f347b14051ee797fa2ff532054514bb82bbeea02aa502553fb5c8a02e73280b4c0f4488209264d97550b13e882473bc1ec4b5b59f8a9992ba35fd5a8370a954c719347e72330a98e6b002631f67dde49cc1f2583cf2f2d4123d0f2a602db858106061440f76fdec526bc7b3e74c5131dfab671018f83be85e1bac1c4bf7f4d4fa2b2b21970d775756ff2067e86994cd094dfaec7929af6451654f9de49ffcd1634445014d7da50c4234798365f6b43eb7fcf86df9571b4fd7d0692cdb92521c65168b89714249c1900b3616dbcac1b6b80945ce58542e1646f54bc67e36515dc774fdf5faebd7b3bcaf5b435fa0c0234f72cbb0a1d02367e41ebf9fd0459ee4c6b42ccf26608033141bbe915ccff46c828ad68c426bae5b986db2b1213646c4c62e774c8f577659bfd6b1d3c0007397d93e50d1ca3d589f4d584b3431430e1250c817e4ce41220979ba321b875e5946935c96651bb07def8375fdc8957da0a2751a2af0d50315ad2c0b41b391e8641bd30dc8f6fdbd7acc7bac8e549b89b2eec35d82943228c19b9e5ede7241496a83456d05a6a9124cb176fd0da6dbb6dc0f2630762924458c2d854e30836d3921d84df6938b3db5d856abb699b70cf06cf91be01c4fe09166571a9a4d93019e1e3739a2e0b164bd3e4666567d3fe2d841cc8e67cb6a53aaa79ae18d692f576e233472a6deca30c899fa2af3903331c8174639835d67c032c072cb54133efb888ea103f8dbd62eae0e36b5f2079ba2e489ca3d5239eeec3b862684fcbdb431d264ba6f4e72f7942fcccb5bcb1edb16a556756f61d7fac4e16d887266c6e04bd733877878783480ead9538ff24bf7118bc071fb4efa5802411d1b041a5de6e3cd06411004c14b2118f072ce901dbcb665519662ea22c574195ff6bc8c6934b4a744855cf6bca8cb9edfbc685bf694f385ed5866e5cae66abd2c252505e6a52ceb6994a923b4ec7952ae0010f313226589c25aec792f8545723a7544d03ea2ca9ed778f67ce664cf6350f6fc6d62cf5f51f6bcf5b3e76dcf9ea90bf8ecd9f55c3b236a7dd1e49c61ce908212c64e3237044a3b49695bcf616a73b372cd1465e55ba9edec514264555f5155ab4c45e8775044a0344a5196be79a2345babad9da5af9c9928ab692582521ac59c17b0c24f151411b2081cd84114d4774aa7e7015a437a9ad72687432dc06577afae5d3d1ad9575bbce13cead8f4029fbbb3b0dba81bd3dc2b4b478749bbdc580df464d26eb796577087e0649f5b4d9102f6d8f4376c80471954f31a865ce2b7d4e9b1e9ab4ff71c05ea92d341a29c64c766b67559d6539528c9012952c2415bd620e2dba9a8b784266d8c98cfaed656d5cd0d438a683d5c9ac4a1a148d68a614ee6b6244d0f6badf54ae620a6767d8161d65655d67aa85893b0f6f6f8c88bd4e6bb04b3eca9555b50b5858a35e3f1040f0fcc9ffb9c536e2f54841285dafc509b4fcae0d30029b4915ca79fb7ae9980ad064821213b537655c9797deaebd29796eb8b06d06c24f6d52dbd655996752599bb01724b8f4db3c79d28bb01349b6a24f3486c906b9c1ac8bce6edd563f5c57339a15202185e3069104da1321f5e2cf414ea9b85de940704fc65de54058fb787c7123d780478bc7cacdf1f2a4201c3c0023c26b1fb3251c03fd74fdd92af2f54ac5ff6909aba7cae2f3050f1a93ebd5fa77abc5fecfb73a998e2051eefcf2e45c1b6bbd78377849bbaf355eebcab8b8e8d3df53504061e6fb6023c5e5a3696af2d25c0539b66769b0415ede57369d9f5f76ab9a57da5523762a5a2f404b63ede54690498c706f065e574023cded4beb474d1aecce92b888afb62d15e7d0badd1b858b982f45505062baaecfa4bf79d72a9a06215ba3fde0f700b5370123a34fbeaa9f4c543c51a64ee9b92a7fa54a9d2b78a2ca62bf078f16834448142059439a134416b2c17945e3e97cfcf12502e1f4f09d81e94610bfdc01713a5533e90d2e3504ae935e50351e81592c0aaaa2ad355c910c54b25ba526d46d5e6fba9aaaa32555736423a78ed5ef7d2004cb11e08325d7add1e5a73f55011caeda1353873ceebd2261becbe2ec4eeab497892bed9c8e89f6e22fa486312e0d18aa09876f80e1ed44384ffb4f5418d37a46c7d28a5d70c42b303424a582047adf49a426bc6faa50a187b68bc56e6d4287764b403495b1f6d79eaa97e942d947ac2a161be5a44f75e3cbbfe2405b6507c9eda33554f75fed42aa6f9b9a7e6634a077cae181288c7051e672a456ba490b5a5da885e6f0d551b89b2b4585dac2fbb529e2df76853f6a79eea45d94ea9a77a30db28a727f06853a97b31d6925559767b042d91258badb26b7db040a936403ed5e6fed45305858af543542e1f28d712b4c63ee1de7ba1d01a918787a788211e1e54bdfda13534888706f1f0ec2ccb6e0c4b2c71b7ec9a55168afbf3d1da02658ac9f2d9b5fede2de371efbd433ff766b9d7cb1dba5fb02dab7e68cd686da182d6d8a1aaaa2a8b038ca5c0b0178b15a2624de5236c4ad3a6928c940dd242f4d14ce1808f97cb47288a969f6a33aab751aa8d4fb5099fbab2c7fbc5976ad3e4d34dbe13815d0182115a5040d745afa16e02ea26e0c38fb4984f53e071a63250f5a953528b6d5333be90d2daaaaaaaaab2f60815ab0f582a9a1ed65af94a9626141a4198eef1619ea8c95db7a914adb9ae8b86613202da14f5510f7e07e5f9e0d1a6be6c32f25d74916e22faf71d944d8972930d0a201bf00045c46f30861628497d8c94cb09aad081104041e154304bd437848a15949cd545ea966a6353b56ea95ca88d44d5ea25dfc0565a0861cf1b2ac0e3c592ba42370bad1969123b55ef531f657e4951d17a9ee2a66eeaa66e6aa6687a703f98c09f5ee6480ad6526dec1579aacfc1f562572d9608bb5e4bb591a88bab4dc9999ab2360b1b5436555536556d7258a12f688db4616cc0caaea31563d7cd6ed935b5abb5959d5f684dbd37b553b4661689426b70e697700ab17bce54b529cd21f506f3e9680f5d68d2670b61e9142ad22954884ea936378268152de81525a8159aa226da45a3f6a7dad0b4000449b000892b590ca1e8b58ad91f1d846e9ba2353fd5c6852d768a159b85cba68fb2e947fbc5a6d514252cb0474b653bf186b070608f251f2f7bb458361547fbb32976a78d426bc6da6543416bac13e69cb376a936f45e8c4ba55f14bbe8405135554ff4180c2cf738a14c11481b8b0239a1541b2bc5b452cc9a2aa64d76fad1b260d35b608f1cb7c7096553a127fa9badd0295e1578ac5d36bd17ad1ec3b00b911bfb0e933d2b2cff60025ff1a9b235ba5db5feca3b7be624755bf555b5549498a5b56e57a8c2f6ec02df6bcb7481ad2dd305aeb64c17d86e992e70dd532894e902d33d3580e7ed1e3fcc851db890030c040c84aa7241484a8f09d6da49abaabaf6558ed5da55cdd56eb757da6b5b36d7add129691d87d02e64995688eba54dbae16a2babe6d8f53576fdac2a7de5cc744107dbdacaca63dd36c7b679acb16d3dddf512bb975571544c114bd286b392f2f0509e4979ee0ea4483f6e3593092705887aa277a1063c3876d8f5cf9464d5e70eac4f9e2a5a4329d5b5529975d6aa95bd9775a5ba52b7bd525da1db5ea9aeecea4ad572f228a980aca95ae70a9562654960d7cfaba9b5d65a9753cee85d358695a60d4ab3d6a9ad754d0d843ae1bd2c5bdd541bec3c343c725a40da501e146c3aa56c8ad5e0b26c55653ea7501e5a6b152a7da43c41b43d854a4410ba32e5a2a324d45a6b96556de3ba8edbb4cc8bcad67b59d5e5be5b747e487511eae1480d3835f5e3267553a5ecaeb4e472ecae2ef08eb098cfca999a89f5150797b3a6684e39060b7542ec5e569565b984b1dccbaa2c2762db4eec5e56e50556eb7647db85d4e852181e9fd168544776146ef1f45c8af982ed489bed9b9e4ecc0f6860d38fd3029b5e2cce29f807787e34dab2e66eb2e67a359233d4db7de51e8e24c087baaf7cc3909411ca1511ba99ee2bffb8e94163abeeea20e6187b017286fe071d0566a2e4eefa09c89aaa93509102b1c5d22969d6557a6e9133740b38faeaadeab367fa60787fdf00339132486dbb092fa647bc7d31f974237fb12d6317bab83159d4985cd3e3cde6bdb854e92d5300b0c7b9654ab58900a594522636a5f741b6ab66f2033c69c704d33a3bda2e6c81474ce40cfdc224a324272ee4a0d65a2bb6d55a6badb5d65a25a6f4f201cd9cb30bebf5f89862eb6a59a6498c450dcb19fa9eecbab29ea80b58f0bcacae8dcacb395f5fcdcbe46e69b3ad69b609706bb3d26d6653ce68a5d6d9764ea523bd4eb8251577c4705b8d56db52aa838a53dbba534d8bc4c74a2ba516b52c8bd62b8fd6a9fd45afeba2746b368f56b5276bc4e4e5680a3c4f4f3b8fa22d3349a7c545d3484078524a01d083e7ad911478debe3a0ebda4945655eb2b6df5b8e3c4a4955ad6754111033279021bb02b95a76c574ab2aaaa9f5665ad652b6b6d554929a5652b6b596e0712b0fd3c0ecda6d0269dd5d67da9cdb5c12e5fa27bea0fdc57033009e473c0ce539eeeaeae25bb1edb555ed36e550504152b8b43e794524a39a7ac5765a5cdbd00b080e5640b617755f58e2e3d96da597db41e582a5842063e4d780015832d4ea0a0828252547881829241179b2157b65c576655b6d2f4b8f94153575c10d4850e30b865ea8a943b0696b165ea0a121812b068cb54eacb90d4502a0ad196292728b0ed96a9d413f7da3bb74822705a66cf083e2d39e852a925b83b841a1f96c7ea141728c04014bbed2adc950c71db8a0628d8150da2b0ab2da8ec6a8b293441dc3184b2b7ce6007bb6ac1c40ca0ec5ab25a5073079bd797a089ed15a74d6dfb792f74e7172ff60614b0d8e8f1430659b6bdb54123d836e805581881c50eb278228b256cfb163820360650f064c8407f65008218e060d39b3011412842ca115e88604415720859460004822e8ef0831fd8266cb1014dca4cecc6608b4daf68c2a68fe180501cb4c52d8d326cd4587181952c5c91c5b6562ab0ed613820569c3d9409185020a394524aba5568e3be8067dbe3173cd9f6a517f860db1f06402e38c1b61f714002a3b3a8744cb7a4514e3245230000000000a315002030140c888462b14816e789a4cb0314800e819848764a188a93240862140421648c32881842089019919119022700b1e93befdce7735ac0c7858f9980a7ec852f010b693b71b19c7c8eb5bcdff64b59fc5a65d9850c0fac9c1dbcd142f73eac89db6747b11f920f66f35b0f22820bb40569b86f23a108f1a3b539885a9d90692c3448e70effbfced2695bf80127bd9c2a2cf50df5428977703634668d2635c33cec33a3a394b6cabdf30b8d4234614d1652e297550e85763a52d2726cb929b9b4531db8c729c67c52cd819c6377df6502140463e15a44974d289ee51d1aae6e79b6479baa79d5afcec7e6ebefe7dbb5fa144cb520d2360e2328fdd40fecf112baf7804ba25fca5e65f611ed3fbbf12745c129074a6b4b536d02e5b6e914be41f9f23656455d39c5830d7df1c550b29c6546b32128f255b1f9ca10bb4a7a9ad94f804b3df4428f06ec246bc57af48aed4e7fa3ff74647ab9188d589ddb52c600c715ec7aeb8677dfa8bd181a30ebab7895f4605ce87378a10659446aa0252bb5c6ae008e8b5fd4b1a53a8e28085dde3027da3ae70f76e1004bd40e66c1f9202f7d6a3e6882b79b87cec0325d2d032e36e587d3e953dea1ab54aaf7e9b92aa3d8d9546c949f1a8e427c3d2b0d5c6664ef578880bdab1386613d350202f07065d6f3824cb0b2d2f62d4c3d93838beba01efbfd8b8767f1f63c6f547c5833a04717e4bb5467ea05842d4d4657fa03164c223517e06858cad5da97bf7787ec8a26a8dfe401af9ed5ba28ae517f759c9313b09b7d22b844e4d2ee521c543cfb8c4188eb1289dba9458a63b3f2329f73266545cb6015bf34d7e95f0a4d900e2d4959c22312c1dfd89349fd3c52662d123ea4ec064cb40f129398bf207c252106ac9cfef2ce200dc92655d57da4be2a943c16159eaa803e322188c60a4bf4a4b4617249e22cfaba51f3a243e5996a3e568e77edf41c2111d0f965a1510052b569f2181804b2b9a12871f40aeacfcc0f0bc3be5b4cee1673c7081dd08f791f7f83a290948b27fe63593c716938ab3b972f7ee3b1f562a3d741e50387cfa4ed57f4fd71f3ef8f4266caddaec3e75f56d346dc61a7377bc0ed6cb550e3fcf77e3c319144b165c114d424e5ffa8ac153e4b98da865b45c4dd58f418a27408423108285f2708b1bb4cf9be5a47bec9ccaab25ba8e0f6f16b55099e6d04ec7bce249038606e514f50213abe16c5407625cf72713fb09812773df9aa98cb8d9f84603f734a877ac2b4ce4048ebd27f767fbb8f5ea6a0074c9dc9c65a89cb276b678cae6e1450c789312330dd94c9ddec4fc90f09ca0400d056f7c20f73573500e730f2b215d1e823361095c337a77a60476a310ff0cd2d10fce12d7f48b3e3ed614649bca0f45da765730958969227eb6663449e889b8f385a3ce2104205a8e038df43f9fb4ab8fbf32552e1f3f4881e23f28665e8208a03354f33571d2ec679cad4baa7f40e42dc59fb8c3bc1202043eee9441d303ef75148921776b90a52dab6d0084cc14875062968af24bc7e9c3c7bb3dce6ec327a7a33a81e85b0f3b7f53f8bef7df42bbd04556293f66ee71e30d1c29b1ee5ede96c765e51695531959e034a431b3b7686c9235330232d450f94b24d3789f3965626af1a14a2134a43087d1f4037181515a6ca40e71a5b87a7329c6fe7afec3b9d5eedd10455de12201c54fd2c8f3b3cc9f7edba38076748f2113938d31020d39d90636def74fc650f9e7c509b21710cd25978e7eae09a87967856cd06ba744080dc68ef37dd274b76a17ec216f8da35f1658c1c238ee0afcb06cca337b18f1e3a16a701b6194fc21cac87c72501b7682acb9c1e464b39369ff050e31df60ccee2b9746149dd7c75825fc97c8d1194bff2b69113e57b59d85cfe5500ef398622416f82421f70283098fd0af3d4d7efc032be96dd41d1135f6e88769284e8e5ca1135a03ab74f649286e564c7cfdf0ec3038c391ebb001234647d4a6ded3b3552b11933bee18ac695568d5c0007deaf31e8f221e2f54082b0f1195453b808515ae8065b705c5b91693a7c0b4b3cc94e381a3381ac88ad2aab0f87124a70de576d1addc97e607d24ab3337fc606a0d57de90fb4ec71d4250fb733bf1cd37e3dd041b55adbc59bc939956e6fe03dfc60c4afb4a27b215aacaf6b0c590c69520dc0614125c8cfa3a4d93836eab6d096727c3e01228fe82da65213c8182b78340b4bd88d3614e00b19a3a3d85e21da9a6774fafe308fe9a958a896b9535b2a308fde57b641b2968593c3894b20c4a4444a88253f4aae32f600d4f0e1387666e1056f9ee136d67496a2b609ac79a53c15c3aa4849d8d4746247090240c0b9b90534d819eef4dd1ef071ec2c1d533b52fb9f0db9df80d1f66e6b199125f9b7ac54152b8a7a4a75ce72bc45c91e1af1ac4ac6d52717895846636b6f2e96c3c47914a840d78af2fb0d4feb2f0f9354a72ccaca58da32400a0f34ee8714543a1351524337f6008834f8cf6cc5ba586f9245e4ddf3a505f2bf86d82683325d52ab09ab7fd878ecea98582c9caa5075d582488bb492e8cea5c1ea00d463e18c738aff79a1029f1e77e4b54962b10febe5fba360c16f83afde3c488977d9ff316bd389b199ab5574a8deefb192cbdd4ba80fc08aa30ec856bdb055d4e8f772d24a9bd2ba577128d5024d8a053a642a33f86b124355396e0bf9ded09ae1ab39b0f198fde0a87442af46f0c8045d2ca4cb2b23b033dc468ff5a7597170c48aa9e867bba8edf99995c3848a6ebec8d1c5afe9a3fb55dca4dec49becfbf531ed4cdce5646ffa04c45b434721d1c85eaad273effba7c9e98abb7aa613fc723904f5adb1a0fe51fbb508e13de8338d499bf56a244276b157dc9e20562621245b81b01089cc757bb934f1d0c94aa60148950c3b3db035d3c656ad0d2a5ae2e63b83fe421eb0fe5c39ed70ef34a2c688cf202befbd13353eca9aacb96f1fe1197f94ad16b8734c3a1a09e44a37164df4b1d4112b38728b71c041ef46afe63bfd297b8fe8d120770fa24bb0e2ca90538aefb6b8b744407f959b06cccf98946d04f4b78153a4eadbd7d41081287d9f5d5f6ff8cca14d2feb19f3c5558409a974654f9b195e2d24287ef3118947d3138edd55cc84caec0e15cc9937a49aa385b4b46568cc918f73cca55f1f71e651d370fe1d4e7360bbd9e85b6f8109a38ce18b36c3a6843d92c3f8835549ee61f081ee2e087a1fb568586c5d0773473e074b3d93d2a45bcf6f8116a4017240617ae4c4f3872a56b1ed41d85a3f80d68161b36500c6e5541965e15d3b0d13e44a347c5d6baeb282baf74a5ad144140b393b4209cbdd157c2a9ac30d83af581e842a54a06113cc689fa5a5d5e3d2161c4a055dbd12b37af967055df4d46025b455ea97988ebe524f6a18aa86b8dd6612aeb45fd2c190eb2eced57710c0ba22191de54d9f13127d46566678b60ee7d05f01df2e544e720a51cc0d312c545d8cd09355894feac135a307e16cd9cda754370f702405772eb22644a088461a7ceb0c6fc51597c52e476c8595223fa2cdc4407354aa599f2243fa4da42b506f74e94b401459b74dae8b72979f8a502a12d40b010ba85574ae57afa47b091ce0ad73ca14b854ba0cc087520d34aece2980494ee8e89b2dbc6c4c2361922c44cf244595b6bd0a2004dcac7245390fa470cfe6f825c646622bd66c68f984c1ebb18465471d3de939123109361a19518464f56149868f311f60cf51034130b0653ec15896200f403d929b764211f798b7ae48f2824ca0439ed01c472bff8e00ae0d71280327a8cc20045093d750802aa69f851ee194fe80ddfda6e99a13d13ab7fd3a4e5fb654d1981c49d9281789b4fe4f9c38b8b0db712d1ad191e682bb7e59ac6e304fb34e51bef5273dc8c37cb9209a960396f1f39cd0807f877fd7dbae5c738ad07adcda930eca9df58ef0b654045b08572a9901537e0001a5512280c195900a8f24aba845bba481ab1a71c59b9b7dc86348eb845b306a83fc7d114138dc0bfe3e9f9098cfa6e304fc663a88e8db8646f43ec1a15e38df68282fa4748b5d0bbac71a1bd7766365fded16fa576fd8d1d9c6c950849eeeb64a771010143c9a9b1ce8ae65041198ea6dbecb9ae37bfe380cfee1611c6fb0d38eb44b756809a3bd083a3506da2e449c5e99fff5b872e04fbba22ea20972d73d086f981e23023952405d8f224d5f19dba5ae13955f336286714790c007c8189c29f7496ccd49f0d16cb372c277dc4c7f948173555c89ba000f94d79d7191c1117dce9ebc5085e7ed8d61eea3beea46e81d10bbce7294c8254b8dfa905536ae3e2e33032cbba79a2cdb3fedcd2cd7d48b8cef5e6eb7bfdf81a0ab014e587b832520aa31f5502fd2008b323a0e89a132903323e9f054df7d833f8cc0e1b645af2025048325087a8762aa86603e68fec1027ba8457a07803c3968a7548c107082aeaa3c1c9eab255e404a54bf12423b4e8a38c5502111292bd7f53b2883448cfe8ac80cff9a9fbdda93d60c0877703e9905e7706fff5a353b24f745c243d1a517d6aed78c3197f758aac094a1db3cf63332d9cb477829e7b5f9cf28f9335f02326f212c645a7ab2e84f22c21c566afda5d63bc9d9940a12bb76b72f1bff0a83fdcf102c3d474e7afd79aa6b8ed7d99c26d16c8090a00e541488ca97c166bd31e3c5611447510e5754cfb70548cf0f85e302717e0092d789592b231d4a3a3158fd54100f55a46903dbf198bc204c4649ea195a42c29795f1e9224f5d01828b2e3404b7e2f0738a0e8a51048956aee5206d530c37c143f781ded15ab1c47fd57aa9a5e3e82025e46a889ae7629c217ada4838219d1fcd0999cbf2027253e97d3c0d1214842b32dafb4bc6d85a49f081ce698268036d546f752d6e7ea2fa10523d61429f03f4e22b9444fe465e6a3846d15f796728feafbc4a35dbecb07289077f5db9d57124b9d7145f606c833a55c8504b7b7a3bbf83a8bc21b6067573c1c874f53db1e2bf82689b23e8b2e6cdcecda6b7fdb5676af5a23065c987d0cda95d75ca56b54e15bed17fac93f2884fda25f3a3cea5df5ef1990174ade03f9d0e706ebc89e689eeb008edb9225177014b511635c4f494962afd6ddab2a8220f790154eb4b7cf9ec0e2985db334b543873a7c8789902dc258b23a4df0b8f275c2b8cce9e6157a59d42f14ef2a22dda1b5ea5c367d7799739e0a251f09aa983309dffab04e52a905ebaebd8a5647013957be1d0baa091d08a627632b54c053125267fba92b60cfd4543036767252f895a63707aea83e948b2528cdb83951734a45e5a8c84112461b35a2699c36f2b5d2455fb466e22d7f3a6a1ee4f8a60c96870b6376c461471cfc408e373ca8bf2d9ccf29fec13c17ce099792be28dc0400f6d973c031387ce1bcc6077e3ae0614ba09cc521c1c9a4c2cf028abb99cde300af93cfbb491d08c723ba28c713a132863d3b9b80da929c3756b3ea16d4c63b5adb9a924a7bea649f3cc812306a626c53d74583e048486a89887a19f8f7257d6b914b7bf23497310126621ab2d09ce43cd856838324c8487e758d4b0b670ea7f59afba5b9fde4a5352290b765b89171caac959f307676e6abfb052266614c4c1fbe24100cb4dbff2d1e5c9d4aa7824a8ce0fbda740e1de3981cf8a4dd7c56beacfe97459a730ca62639e2804370088cd04f10c71d383bef060ac56f5667b8253667dcdcb8ec81d2324742fa36264d55726a4124107bcc82146b0a547cf14a21ede0c0f51917658a50a6d4c4dd1119bc1c66519c288ee9359f3de69a0af283abc32340e70cc04d0ccf07fdb257389cb028f9609c9e601d80007f74e5c652ca60da29a4f8ec5a10e82c518a1372fa77994625f8490b650f14993724432231650f5ee52168e065784382843d2cf01d140a6488ef02ab8411f5154c8493da7d0072e0a63ea001040fc841487c700410a2831cac5efac1d51c42f4e1c04df5e2a0f9869654cf13061a579e32004c780c570bbc1b2a996b255a88151d4454f882ac721834ca734c0f12a49bdf2cc715c8a5832ae09ec305ac64ac963d36b4ab6787de1b8a27586a6e313ad84e8ccb8488f3227263cdbeddbe75cc4d229943b5112dc420a2ddcf18e7fdbfda15198567447ef8bbe53a16b3c5ab35a204753e83cb4e0ffe9819a6c9ac9599f35938c2cff76033d8ec230d39042992418327fca6f3dd383ebf0a1b0b399a1c3b4ffa80ea47e93dfd1535fb619c1d7d5f1d034c89f8a96ebd00a73d4bd0e1442d51907af368631a12c627e61b0b186c3f6ef38f8af1e22b96039eb983970c8c30e60f2424210672194ede810c7246fb7d84ef2152e06e1cd1dd1f15246110fabb1bcc3dbe26cd37e7b96efee321d2e14162bf574a4f51f8d389235c27ee7de7390e061ab4b8b580e2afe09cad1809ebc13f22428e7835e670f246eb429f448f053516e0a7c384754ed011f6ccbbe582d7093db88bd89309d98802c4d70fd6c9010e13bd6a8473007f2289d68d816eb8033c1124280903c8462b23c042c5f53d8525b049d89ec205f5d109593b950504dea5ef57314f519097a4f902336bf82431d358f1de5428bac44e9285d4032c2799c97332ba5c0039846466e4e6511dd6ea3ef768bcd3a5103f072e9ba2c974e72ada1a94b13a14e8fe76115ff822a7cac026a184bdb7700027802f099f79e0a879ef49c1f6fd8aaf767bb153c786df33196cf73eb62c2c8ac5eee13f98cf9cfb3c82a2ad28bbca754232204d10a185f5fbd8c0bc75f42ccf48ac69381a5014473a0b74b9fabf2e9f770461defb2c2392f50d89ec3db1665a0336d6fe2a27c8101c58604a61c3b758c00b6d26c992ad299928afc74b5476ed3edfc374d8d2a42a87e9ee14975494df27ff6f0ce853dcc7408b820986f63e0dd431332994c9c446a7afe563c104b142bf778dee89de7cd68dbc514b6ecee274570dfed845203474bede63c7ede08904b9ae37ad655eddf092d4075cd47172ddf654bc6f5dab0545ac07c28e7ddbef50c19f9f7aa2d0b4ad11b6ce75fe59c17867c35312aa1d1bc34ffd52ea4542ad61ec014732de7fa3ffca212dabd14312004f6c899d114aec3eedff640cddb934813597e71ffcbaa3505e3ad2dcb6de37a05e77421d06dcadfae5cd63c8e0974ff23a31acbe4fd25755c83470c8290d3dd0648962d8f47bcd28e6e16cf442f7cfd793b3366dadc93f1fb53378261a5d1bb4902caec9c5dd391904cd57b4030d1c53600d288a22b8461d4448c09395fd3294e1c470ace2a2581e8bb307d8cb9432a601e1db6e51256c75db8938e5e8eaff2f6a33d0240d150dc40541973fee91f8fe5cca444cf05e8f6173bea857c814e0d87306a943981db0ac77640cf2cb49f414e47c04787784fb2902d587fc415a88844ffab87e6d1875d6ae6a0c2cc0abd5af09f67a03d35cc444c59d10fd2e0061d1a8491035dc1c060f2f003f944958660c02fa3b4be3646be4894228cfe88a7811373a6b344ab8e98532ef8e66cd201c11de1b2b49b138fa4a48e12cdc01f3a1beaad1c408f2372a60670166298e1ee5a9029bf1a832cb8fb5344099b5246142bf2997c9e3ab5d024c549a29d8b8c3fe34ed31f098e5a6c3caceebe564d6ad6b9ced3a5b86381dabc06ae6a8086cbe389e43d2746cea956ebe374bf1e1087e2151137be3b6b42c1312e5addfc2563d33d3ec7335ce7c02133c299d7ba03aa7433b061e9c612a3170a5fcbd07cf75839352fbdceb77dce4d04f2faac3c4b58638d24f97ce8bee207dd2db5fabe81b54bf2ff8c0cf96ea867607e0515c193d4372374ea595dca723bd2bacc7ac642d54dd046dbd60cb44c54089a4fb9300b80f9f93ede815922f3dd3f21852db89ff4647c93e4ef8da44e122d346580e4788ac9c9bdd92c31639d7cc7e790b6886f81cdc1f898200bcdf2938f8831ef6bb3505bbba1f682afd60dd078c841ff9c3cb96d16d7d32e74611fcd35a75ba27e36d0f8f31fd7981882820129c7c540709be084406cc81a2aa279458521e68e409b520eeda833c236d4000ef5b32f5042805385b99dc1694cd973d0d40b461e6fe36db81e59c99b92e1303ee303faff15794803b0bafc0f5afa5ad89d6a01aa337f57b14450fa643ae51805e6fef452387a6dc8421d16be51a5c1e4cf3d60062ff60ed4f957f6253725d49bc019d5854fbab2e9ae9f400c5fe26e25e32725dc7dd70820477c3e0cf717cc466ea739e1cf642c2820de11ec469b182a6c118507efaf8dc8c928eec02f7d2141bb2639d2f40881f559e8dacf2f0f44b44b13839de83e59c40436b9969dfa29810091a3c74671ef76a59d12063b6ff14c6904c44e37cc2f5d3a5ca5124ac60a4cdf6f6791363d709ae05ab60bcb006deef336d47714912edf0ac49d8676ae750e46f1764a2aaec77263143d798f635be1d2dc1595cedbf2a3f08f1aa8571d34cfbe92a7284f8eeb76fc1aeb71f5ccd703d223de5da71b11ac0e5f57950580a122fd2fef8445805a49d4027ef790216a972220b6abc7af8ea8bc72248570e83033ce082a680c4906f01ca205c3f4071a117b0d5e84806c81e4f644aebfdd765b70d37ab4c768a78e1029f63780cbe47571d8dfa2d2562872c2162887c4202b3437594843473a76712493de5712349d28d8a3dda8befb00a22a8f0a1508bd80c0ee298b31575b10cdd19bbca815796b7820320ec578d1ddeed24f3d5443110fe1520a06e614e52faa645d2069a6970e65a38e68dacdaa85e1a3e52bfd4736ec085bae2007996e4195f430a6b827c2e6116ef2af7e9d4565da8ab407558bdc2b7524a5b95c5526fcd38c72850a117e80f1c47b5be8cba9aa46a3e19d07d4a8ab196fe1f917ee798fcb70c65247bf9eeac6070e9208c536463102ae64ec8a4adf9f7f63fa2ba0317091e86264cfed88f74a54216a4458d7e08edc1b65f5caccfe78bc23b51d42436daaf7cbe87e798a6bacbdceec827069cc7e15a509d530b2dfcb10667d11b255aa1122166985d179d60a02c43d615072e52590a8c4d60f080c91159d1c854a02709e36a499199b46f086ae194a3c62e516636ba5ab293a0b3342bd0d03fe4c5183fc5af52d94d6962d8a1ea307a5451d45971520dc01d13355281c42a0bba012f984a1616ee98a232022fb0bce98269d1dcf720061ac9005bc6ecd611b05e395c4d0c7a330a2eb9e3420a20c8de31bf0ad9b31652391a686e70b07f457856a1e416d82e9233aa7be8e31481ef07fec9ea219d367b7d222534eab995c450b49249eefa2ea39e8b2b5e5c6b124659cf1012774756bc7b2cd1b3e2272ba451b9adc2a31116e149b8000d2ebf1eaaab756f76256da48196d09c3796c01374db6e69ea8fb007da9f1255e2b1cb55a6a95e057e770ed5faa6408bc7ab45dde79cdab98f6b83a80a11cc7a92ec100f9b0a7ba0756a9eefcd1d9c9b079591a4e29447e943ef719274816ebec26ad05da374115a01792ca79f524273acde09e5c89e01443352f43a59e18300ba81a8311a465d99125fa99101a1d0c532e25f11cccf8a93151c1048b073a21868a53c25f74ba1d58fe9a324e5e415ea65b076398080be06d4ada2c873b5a92dbd33c0fff3ae0556a962e3ce5458152a3abb107c93352a0ccce9bd4038fa50096ebacc903af7136949814a1de449256cc9acea86ae5005fe93acc2a5d18022fcee71f4be00216f79477590451dfb0a224998825dc6294eb066f54e1272bbdcc358597a084fbb8facaf9f28e85ce09afcf624fbc154d6f9d81e61200c266b351e64048d17b3a57b966506abad6ae5b7c26c7699681332a798e68d390bb7b243346ba1422de20491e16950c3b5ea66f632df32c5dce272b28bab9802c4c94ec85a9640a62e0bf7b34165d725c5e7747950e22449aee0b716614046f633aeff930dbb1454d6a584547e5c7dab821e4d5233022ecdb8702d87fe810a68fb0b96a96e8610a737c725e0766b7182ea961e9457ed8c1a61a589039afa37ce4ac539beb34286f85271eb3f6501c0a7e2749f7cf1b6d27292d0b48280f5604edc4cd7ddd5669ab31ca2e1e4650353046491369545f4b5de64164120369db736ea0dd79616682572a2fcb4b3c74660c5888322a1578a777ad838b1c1fbaa4ae20201a0529e88370e5c6bf97d1a117426e7d383fa48024a720c3040563b0516d772942b6e32ded0fe3d6c107a6e929ff377e665c5413c8f004a27999a7fa50fe8342ab300f0f8838efe441b67e4b96ef0fdbc1fd9b4caed2ca6c930ca796460421d0f1321b8482fec8b60a27cd1549fdf0063f887da879da28416d5963ae92485ff25015d2c752fb9005b47a25daae8069a2837da92cd7578598dd5d491778ef0b6d2d269f082bf28252333022190f01cc482b8a2b8102f15268f109057377fbbb8c57257eb841da8ac5e24856da0c5acea340e517e4aca636e32b6b0daa5021f4a665e3f7b7c78cb78709a09271222f0a1f78ca32c7d48c89844ac6e842fee80baf1b1c341c0c45d00c6989ca5023508b7fb402c996bc36ff7d4981ffb335b79342b17ecb990df542f56f67ee6b8f0901317d9108c84cbb53451b29b2b33120a546f2a17ff5a12068e8fe21e9c1028c0a6b35c29317560b04ff97b3e4973d9540923dc41cbe89f81ef77e7ec73d83c0d87818bd2cd30617223e70f62b313b2323174532a4cd2ae2426d480cde46a89230b7e7121302d19082151ff07c64ac622c0a17a0c2a84128050d220776263d7a097c051a7f9780fa6cf3d2e4ae8427f0c92ca483974150df73b2d1af7620e2dd809340da2d221b18fe3b95cfff54dcc911e2e8b31e1ff5ffd5df8f5e771b5da12e1ccf8f466b7184f4f4b3ceeca4c07b4a644bb442de1089395097b5195affd74bf26e66470c74cf418144186fdf7c5a8c5683889f264a018e58c94941138f00dbc0d783011fc65d3e14ead1e6adf5338971167bf316d849773891fe4f7b76baa9212f44cdf3d4adeb31cd021460aa47b6391c232aec6467d03a1881a07226311d21f38a4a5fa98ba32f397c20e721f460721565864b0285d5979f860e7913a9eb3f8ee4757c6cd67af552a4f1540d9e382a0426b9dfffecb9ed4696ea8a30735775b0cfa2c34c94527dd7069e8a31c1dfc96b325b879b6908191740cea711376edd552ad4ca91cb857700def9e7997122180cd1b5bb1dc6be990182bfb9ae22a7a1ff595826059510ba3b01103faaa5c868ab5eef41deac81b9c6072fa6e8f5d1d8bb5a0e666b07960330594a7a50552dc35abf4b8f4ccef841c48721281098df1b9403266b77dafdab679b32fce61b7777149655592edaf34351fb43d8cf6e9d06faf4ccb745a48a982b9ea08c4af3a9fab2e0730331664a8caa170e53a47371b200048ee3db360cda551744ab7d3b89716534c9e6245a6e39ac6d392a99a370f0b874e46a928c1539e2bbf38ce2a44b27632413336f098f57a694526a304083efcbee8e4afd9d2e4af5737ebf368fa8f18029286e81a94407a3492825ca3ec0b6bfdd5b5ff03dd02b1a680ec269e29e35ceea4ac018bf26def385e2376447eeff477ee9021f48db8315892545a4d203ad6086551eeaaf0e01c2a8fb5cfb9e53259bb13250c5bc0f62c0cce58e37307ad581abeadc0a31dd70c106f0e58c2bd53d3cd29ab7e308c5886bc1a4e5a10afeec2de0204aa7dab9fa960e09b9da507436fff121de78622d267b43874be7b8541f79066e1ebd6cde6c223617d7fc03398b891cf80f46f700902ff0f6400b96fe2dd0cceaaed6bf2c95f3bb66010352b3d3e3c6b99fc97631aae9f11d671b2fed069e1a55f5c58117ab926238bccdc54d6cfe2597301c428ca67c4d07ffde35923ffb44a36c8d607a3cb2db3bf582a9b7dc8fd0bd899a6768b28ad801d3fac38d928e7fc604595a9c89afd43652d81b7a8ee23368a9d88dbb375db83f73bd675a220f96c64a980120aacbd23c81e78402c950b3cd5f379b38c5bd9aaf9e1fd43b4fd63788f438605b7af472774fe9d7f349bf9d7be6d363b718f8628f3aa2c78b6ef25b5536e0993243de5f0ceb62dc1ee60c257e3c40cff3a33005a51dc16c79213242248ce8c7e3565838e38f1183fe8ba47fbb8c61f1486ed6678d74f1a7846d2fa6308d8371fb209622e3a291af983f24b2b183e199872fb863e0c0e2686d4566d0620888a5c194f32b59d59b2b0f0ec795020d7ae4e4400f399e8a49f81959681ba44d99cadb6cb6087c1f6f2680ba5a5bd29e8aa867ee73017996dcc6a93b2c4b2a7e10b468232a20d79ef6728d3c76a0850f799f6baba86b2651e6d75e8febdad71f46f9bf6638fd7d9638617aeeb92dc7866bbb587636f8626f198d5d60c0fa1f88abb0e5ac186cd4c51354c980f1e8ac2c90730d30e585a4252d46f87da5be7bc74501f821144de6700c43e52201027993620f9b04f82bdec153ac1dd4ce126d2cf083415af33403025f6ec391a48c96c2e9251246622c88c80ddda6230ffdd4198d130b8fe4568858824a174571262880e6ce1ff2270bf82f443b1950b7f75d73bde42c10e2dd29889b5e2359250640be1285a041ded2f5ea092850c5988caf4d03bf6be85a7818831df5f18b88f5d088db3cc6d8f8ca48d36de28b960f64261d5e9d7e931f657ef88f29d3285ad63dc789a9dd3a387abc8a0045a38c1aede2f94c1b20c270892d33554ea4cf71ab8ed30096583c9985957042c75161b58f158daaa0b3e135d2f0f3e41c335246418508562509ac6f75ed30e9ccfccfcaa541d6a73308442ec4aac45ae38a89538a81f405be040e5218c683c2f2c72583cebabbc675a0fc37cfd921b5d885361e5f4d29c6b791b234e4007f367e4791d4518bd10e4dce1d2ec8589c78d2319a2c84f3a325ee95a18fbd3ccaf71652e83d2133f940da21addef78c1d6919b0d932c7eae20908fc89262bd48df34e685b3ce73bd71c146af6dd416dfabed0caa170a9ea6c6ce49d0846136f64dc4e5d8f71b4e4f6c42a0c2c75a8c586dd8eec1770189b8d16c1f4abd3c30218ffd66c317f4838ebd1d9112aa367722ba69f79ca1f9b644f1ea5e62984fd44ba8beaa84039f8996bfceb84f21aa8f7585209163002822005131e59c9e3b9e3779c8e2a842a294b20a74330b222ef27e853c1f5982f9ba9c97f653833e55371725875699f882b3fa233d062aa0a100907e1f8890e635e626fc384e63dfb2fdb477fad8dd7fc85fa5078723e899e51944ae0a37533c86e8712877f4185e675428b3d926d5afc78638c9224e0e7efea31fc58353a780d48ee0925f73c6b8e0b46e3d7295dcd4a90d81f480cd922ebc2aaa551ae9367504200dbcf540cea9e603464dad240eda57c823c519b9c04aea8a0ecc62699316ac0ebefe60a6615d9688c34b89e12bd073299030dc37bf99829623a90570f4744adeaf026e523f4b448b85b11f37f185c5aaecb643a5156990f90640249423a1e6818b173bcace20a4713c2da217f3ead1e1fbc834d929bd814b5c6031216d97235372e332c4204f4ff855743880c0f195247692daced14b4ecf2d08fb0d399e74843518e24d1daebd8e89c8d20234f3eb1c74bdf94dafd3d1452072425310ba7c705b6cccfb5643eeb816a401f4da310a1e5d3f95abe3280384fc6ec431a4b5087bb2d3275c9ae770ff83a489675ec672495399ea2899772bba29b48d7e3d5e59989087bbd7a4625d6079e36b5e237bc037b81cf64cd00d44dd07cd8b3103f160dd75ac3ce6a08dc8f9104851d3beee2f02fb012dac64ceab245ed88828b532ee558d2a40074b235534af4b01142a884f8bf376320ad21f31b8b4071e26dc5d9f278d612e3c2d74197be443c1191acaebe6a94dc5a0302c4d001fbe079c548337ca3cf85596f43d5543ab3332324e23aa9a1ac8afb5203d37ef1461a4f3e72df909d073cc8da23df3233fd8decf9867079838b53febf683f7935bddc75b90728361fce4e598348f53e073eb0f60df712637008a104ec368695236e9b0a60c5fd3f877381b8337df30ac046c81de265a04456e1f531376245959c74c8903c64dffb25debb407a5d11438c1042ccd0280c80140acbebb22398f5837962e0c52913ea14b2bc20f7f72c6b03ae82fe3e7e1b21310c0902e7eb6aa50478c99264c9144f8c1e62cadba04fb244f44e6014434aa8ca7a525fc9505d0680451a33b7e0cb556481b3f62f74ec06228dbc48b51381affa25b45582d284c0abf5a5795b9e35e701b5ae622c9daa3263751f29623178048f601f4312d399a9d2391100ad40e4a223c23cd8c48f7965950a8c07eab7534f394ee1131f7720f368c42ed9490a1b75fe22742039d7212fafa1a1f9470f92b0d58d1eeca07a8a3889ac798aa02564f901d6ec48b44cd85a241b9c6bf740261bd8e863fe9bb6d65a0dff1eb678f1ea75620942b3981071348c6347b5568765b410d591dd91f66c0b4fe99140f8134ad9c12e4200bf761b408a7edd5ea3e9647f163aa1e0d441709df5cc80c711244464a094c36da9613a59f011171ff7ca45106dbb73a1f5969aea4045e31b3b117874e28b480c9504bba622fc627fa3ee33438b029527636cd0d585f038533893421457110a32754054f2323ad548bbf092a7b5041771d83b11d765822cb0351aed0e4a171d00b5db14449c89b35596b7806038aa6db001e7910516bd3a0f1468de3c60e56bba6026a25de32fbbc4516da67eaa23fbbe4eddb9f6d94eb9fedc5ed9e2a58a7abbb752055129214bc34d842b725b31a10ff80253df240944dc2fe4a733e727991c589a2ef76c23f22dc9f072ea27b8d6ff4966a08345100b8967ca331a5e963ba194399226b548aecc9a471b8a9cd80069b2e1affb1df323db692953ee1b46822f2c152f359085916783800aa85cc7d09767055eb97106870f50c480c8b9816c5568da5bba37d1d3d6378c944262209c35ff965142b6a9bfae7ef22f680b02b94012528112ab2dbe44126cabf80d8098b111ee38f61d83eee1776524b11483a3a904a87daff01e4f74beb073ec5b9d9838dddf1cce8c23fb634300846fa678af7fa467ce11fd69d3d2d312ace3becbdeeed1a60bb3b25b20273e35bd0471720c36747ea96d18e0769de2b190cde1cf9afa49fb09323ea079ab8422ccdd5f48f510fc1553ec97ac42270afc9f94608e517907ff80910e981b2d5a546364b55bb653246e4caf220984bb9e871aac4485bc6896ff08ad76e6a319665882a8f5886f38d529bc42e67af430f039857fc6c146e66b336e31455d33a0742da43732d82689da4f1b7d66fee033b5966d389b2c1e30877fbc0b3d1c62343cfeede4bf31366ec09e39d484742cd8b0c34e64473384a129cadf9e24148514249333b445c080e1972308571250cb4bebe5a8dadabcd2c038de7d61feda0980e7a5b2d66683324a6d1f93a458c2322a0c2518adfa94e1769b2927abc7b2ba9307891aa8e5257b941d6942765403ca8f101212d0b650db3624a4c285ab4f0c81508143cda1ab87f37240204948425811513d707742781dfd07f8c83f272e3b3141055cff5b0bc2b9af419ee15a4d1c3aa506458aa3eb6d6cc355e33b1d93d34a0f72e81575268ef911dc4bd2be2c8298844d25f1ad7b357782d5429a42f2fa44cca4cd7f4f119a50dcfe361e006276f551cff6a2b10293242011cff32e92e9c344533c2994208f4c869549c96d8ca321cc25efdf00f39ed53eb8612c9768fb6b2f19baa148a5b3d0de40847b742ebf9971266dc64d1cf68fb4eb88a082f7998a48c32baf62a22ab2bdf89a185f5a57660e4c44d7906f75ccc8d21806fd271c8a787ef836ff3c47db033d30fcf46cb97ccd7a53b14e7774c13019d73aba6c2dae52d205f55ad19701112356d1971fb51ffd5da4ab7a53e0db279be7d8548a418b4fde69db21c4c8720a4c4405dc5427eae072110c2e4fe061c5c238ad7ed508006e75f3984b16b03d73c7826aa63d23db5df6635aa556d11217c83bdb22fddcc56f4239a9b77d8f62f51638e05ff0fc6be6e75d28a570aecdde77fd44ca22d22315af2df8281fa533e9fb2a142fa9c5cfc8389a772287af8462368904046ed1e2d0b0dea346a7ffd6bc6f56d09ca5f5b35bb8d7c48ecfe8164dc8b4683a30741ca57bc16586e810033d849a51cf6827396c5190b101351326d6e1a8ba546f17a9cd86c823224e56157fdcc075202c90b6d26e62be12b7318ba828052abf770c5355bbe542783cc39a45039c82179eecf5b0e5373415e9f5f6802c6968d632c2827af3caa110e8981bf8841ff2404ee601bcbfb1319596cd3cb28ce668b7e0be5c1d6225110377604f9efce338bd41398fd0d2e04997e2282dd3778122586b3ceac13ab58bd80ecbc9c6d52d0752233830664018f6220afb2b9a48ccffbaa783e41903d991f1619f43fec4a0f08df0ebd503aee696e60e075c0076e219b6f1f2c299931ec7f41803205bacd81ab6a395e3890ce5abc6feac6e7f2eeb4f2bb55de3478f970e902591b9f87a9d9511ec594ca3959131c1d45ffaa07c8cebc71903d5910c6008267f5bba491e18c9dcdcdd3c13923f7769494d445889cc7b0d6a4c48ff7606e7411bf618444c2f82ffc1ceff38b4d6543615787b0c8e53c6a57a6599ae1dd57aadaca5290e4893d5b87bb7614788497b09887c35e4b14a6ec84099d96b6f5e5793f3380dfe7bdba579b297d5c45e5eb5dc5e2712ec28bcad65c1fa941236ece4babbccd706e7765ef1622f8b5dfb938d9646ae3d949a09f46794634fac2d7d2b0c725801228241db2ef8f73e278af27706cad70640a3ad3d43defb9b0f189a323aa3de5a3b51cd4e63995c9a0cefe8216d6e81bc27f5f96dad6b63d0e25501527eab3c4614e6aee3619cf23c77ec62ffe749f36fe3d7dfa17f5d99c941c521f311f89e8e320aa04cd8d1cd0d76308a5d9a536bceef8343f34645abde4aa7097a4256dcfb0d45b1891f25f35fe592d8b6403926ca5fc72ca4da0d37df004260dc2b2c67aae102cbf61f1cb79d4b507c45f0303f5f84cbb449d1272a36ac36fa48d2526a6201df6be8f736ffdc1aa2a8992a1e5b1508cc337550508db4dc9c06d120731fcaed45d44df3d3c230e06a396d1ab492b3ff3ed012e74ab2df4f71199657b998628a484a2fefbabfd31ad4b7f440a258dbc232dea76568233d63fba01ffd7d130efe6a1b3232aa99b6ed27307eb438525faa8cb84c54644dcff6767e0d0f66e65d9b6a579a905e761e682e9510257b9c265670e54b9805ba3e60dd1453a977c83ef926db342f2b5ecc7de4fd399b8d92065991959b0c0d4a18d6405e180003d38a34595f75ebf18ffcd8c58b87b295797aa96462a14c0187ba326f6c96eee8b8688b54aeebe940622d639c127bc41b8cd70ac9e38da232840f34132d9794604ad852688c2036507260262a56646748226b2737eed5b1c17e7d7fc984ea019a0d889fdd0e84f9e384f233aa342632738724c88e0d7b214b43b1e43be38270b3ae19264d8c2dae4c796ae10ec1a71198afb6b2975fd4d1e4bd42a86c486353ece288bb252451ff4f455162c97f8be79c055116fdd816a65acb922f1a03f1507715d4488768fb2da9c580c10e34bb1fa77291d143433575f3487689c5f3c79bcf4601b2aa84397736341c2617a1cae9df3b8e6fa6f1427bbc7e5ad1e184e02a55fdf14239c77c1287bb331307facd5abccb2c0436654985e8cf98d6083de7643f617ad093241480b6595214766bc04a90aba3902b223477f738c7c3627a6ffcf71621635722cbd8bfc1c89ec45bfc8f66a71d8e031f24b2e8c948e0ad780e040290e99e70aab08dc3fd88745c7510fab53f80678db0889b2dcb4b3604b8b59e8251b328ee077017c1adfd610e27b96ef34deb20a5ed65752ad013986fcd0ce2287312b2003be2a014efbdfb637a85603d2dca46d2cf480e0370e5c8f805c6de6592e2e8cfbf986360e93235632dc1d03e8633c87ec1edb1e06249ad98cc016a7be5080cec72ff002ee0b9ef1981cc9c81d5e414761b218567df65ebe6374dff8c441d142ce4da60db2fda48e4178a3e64e808d194bebb30d16bb75600298e5640f062bd8508ed75eb0bf4601d0d99e08592bd0fe36c1e39f1114e5be04725d0dc9b3052aea8fb144863512b608295dc55a9e9e986bb1606c398c6b8ab129c6383e19d3659cc2848ffba90ea540882badb9641a42ae607f20fa81fe50d928f303854f4e5908b666f9d94579000a6e94d5d199ad28825f4760ecd44c5125ade787454b1bf900ee5403a5e70429a4dfdc4832d63b1058f4793a6fd908b275989806d6b3ecb7370600623ac94019dbea72e493fab941a84a354a75c985aa3fd65f7335e04c6b34d9968e5a2828328c30718705da3a671e89e27afc8a5dd6107c3c1c44e041bf8e223a1391431bbb4354cd3a4029b4362a7c9f6798dc8285ade3480875e6f9eea557bf4be954deebca3b5d81b8f4a25a2bd838c61a6c63ce6cd4b8ad204779e268a065eb6dc25d1075f8752301d3c194d9162d8d90c9e7c483efd974e6283250ce97c02ce22a959d8b092175f81a66cc591a4a2a112c8cd533185f4d54a4e2d0ff12d823906155f832a0cc89b2dce65af25530f6496c5a8bba36c0f69a323179a46dd6c1a5cf73414f1acfae0951512e5bed02148257a18f30e188ad45b68b3cc6115bc26b6326b18126a2e5b113b0514aaaf071b682a94af1fc63cf7605fc9993264a6099adb79e88470444ab2a659d0b635013a0e577326d7e55336cece5d8380437071a36a7bdd33c7469888cd3eee5d308084aee362f5d4f9f7f7571d06c8478d9a2297dd5391f04a969abd778187acbe466daba1a7d96614008420c67794ed6502a3a4aa016032dcc99e2270e0957e90d61f7f3d916e6a441bfe6120f405dfe859f0c0de66dac3a72b71487a482040f8552052a0e44403594b8be1b7460d25cba4727fa8055fc58f179c1193a1130c8a5df010ca201a80f756aefc1b46e6260ec64b178aa39f859408455e18796ba7ef187285df86959813c72c6a71cdbc4e64d7b2ae7b371daf156d9a36c4d26710f9ae0b3b1015c0c8bc870c3b800d9c01f204dcd5389256bb46ae71693a3ea53a91515bc5438a9602d0256170b95f102e9aac8b7f3f442457ada4938c50e7a9a88539ddd12d651a440b4839584d480bd63b7316e68e08f159fd7c38da6ac63b4f2e75ffbed674c3c105d8c770f503964ebc7881b478079a55e2027f41bab89923307a8c12cdd065f9027a952243ca66f1418922e214fc42b496e77d041d84e9696769aee8a6c445913f0a7bdc1146f13b566533ea6bc1894a8b3a78cf35042413b82447b263fe58005dc7cae6b8508276135c8baa7017ed24c3e4762f6cd5fd8f4d303d2d58a5f911b3dd63729dc757dea36d7c2454ee0ff8581aaefc8e39129ca79fdef00a9ac1a731a82dd3e09ee983b7bbfe6353c2951b1c84d546e91d6540eeb818824aa20a4e241969a1a0347e3d917fa1eec699080ad0782ea47acb3a6c23b7a932d086675795a0b78f77c82d72f5487f1669918f254ec49b98128c31b05fc1b974e6bae762298983dfef7ad1ee719ae301749b9bb1903bd1c23ce6ddd077a1728c15028c36bcd2e07ac39159f0053af811eeb81aa41ab89838e59f71bc14c099f60fc4e8823d1c13240816377a0ee988db1ed23c400e6b264ba4d57f3d090d2f6c27a25e261049633ace8a82d0bb53a3c00080547eb29746647e68f6fb6a9aa6d556bc6e6b34610204ca0bbd9d1f29a43f6c089a1a5c595361831c35bd2949d54c1ab1b9ee3f2833519244c0489bae24dbb293be62422d2bc6b4e62eed5c8f93b73397a49e631edbc525aad80f62cb9df656ead23394b3307909ccf71786595207f02405b740f9b55484a5f0ca522aa492c4c2b63909943347ff01c61786361388ee00932e30f3b461d915dceefc01db671e22665bbf423e4d7435b7fe17b2bbe59b05fbe7507bdf4457bfe6ac66d42f5485531b0b88a66d790f80454d1649bab70722ec0b1c6b520bef1ff757e52656f6620c01798335f4210981352a1073ad189035565937a2f3c169f26f00bf694d0ca1c93ecbd771b4714e4640f5f20353ee54dc4096526f057f6de1a1186afdbb85fc55837a8fb3cba6370d1a4d78dafc128c0b2600d3b831cc2fa6ab557ce903c8f22fca76ea26cddc850e05c5c7c3954a0cd0ce0c9c92c277b3c7ec0b4e6411c5e0e14e5588bc75360a9c10cb2a7a29937fddee89f086ed003c5b98c48f9e7b71dbe4f9ffeb8a1f05e7a14b1faf6f36897865ec81d6ae2f9f23cc0bd7be72d0a39e3acc1164028856df7540f9ad90d0df8a90e8ae40f715cc38886239ef994488633423e1a806be7e524cc5670872b5f1a51f9e8a35f71f00f967cf6b2404d01d28414b321418282754330988413cc352f4b7f746351137b88be237d6b69f2c12bd53fa794bbcccfd92b2817207fd300614e95e28c5dd71c8dbf98877abe5d202551a6fc4552a6f797361dadc46bac5f9fe4d7f883167f1e10e5f8852fe05eeddc2e864ce3adf2d434eddcf0b0248d1c205c4e0456573830f12b8b8fa764e88748a0a6481001517ab54c7c6306bbbb0e2e100f924abf219a85ceecffb00c441187935cffbec7ab6a1324f72d206578c691b43b60a552db408a4d8059cc48e81c591a438be7d1c1077d27e356b5a4ede95cf9200a8f56860cb279230c6aaa738992e4dc7f464057e62251ce1a6b09e6db8d8a77646e8b044aff828a765b7f7aba8d33ca61b6c4964901a98b179ede97524b9b2356db59dec6e32a5bdc7fd0d48631150c38699a75190515eb0b68434159d50552c2b8240e6b1b40e1f5e4eb74a279f8035f2ec2ad6417fe2973f1bf7675baee96e652c026e8ecf0fc6e533ac90446461343488e6e733a25786fa60efbef330103b6e5446011ea94134c4fc30b93594c37526df30923e68f071db6b85545b3120669dea1f0eee1f8ae8b54b1a526630b4ff962426628a2b76332ce46c715b4d7dd0a58a482012bff978791849d0a8ebd187ee648e6a72538d658db25c3c20b124f852126844070c38798416971ac3c82324881683dd00614fb268b8d6de1e68d8e7c36ac974ba9391c59c7ed87296b5737aabaaae1faa4aa462f9553b511280e1ef277228cf702831e87f83bfaafba647dedb9b8e9f986f6da25942b108fa82c3b72e0ac09c88c7a838ea4e927a37c5f54c2cc89a6a80c30c1e2abd06b2d87691926415975786d25b1466478d97773b4e9a057a529bb7b756eb9c05e7572cca1b18b5655a2dc55ebf840991a3c1a7cb109e03d084bf3bcd4806fc5e2249ace3d87dfb89b9dcc8f8486a1998ec6852a6ba9c0adf2dc707f0fb7922fd31beb57669e49ccb2600d6193d5883e18ee91681901cfa5329bae859a89f8a5cef03ad434c2cff2dd17afd07dea9751290cdda97301d9e08fa5bec4105b8747b97fe87792958117cf849901578441d3a6f2d02d007983a2855b20cf2c5425a5c79f146474b287c044928f02f97b3d715534dfdf04cf4c861542141ab75f006db962f717f8ec97552c7a99260214356458f74b00a5153b3b261f94e02feaeaa160ade1919dac03e000da5dbcd8bf7232beac63ff651f30da2be8cdfa8c1f2cba58c50ffec48f671f5e486811e4ce9c2e5b1c9a7ef47d20e4a9371e2e8a6d39f4171bdecb58f67fd3acbbfed7188ee1900321bde9661a149d7e5c9d0bdee79150c7e05ff405223469de1911585a7fac2f4a7a48d30fe7fd72febfee60177763b62062ae2a71c30941f82866d66a13240bb34055984fe8ea323f66ffeadc2cb38a980834647eb3575efb531820650f85ff6651dcd5970ddcac1c034ee44661e19263b3173a0e396558abfdc7a3e00db359c4b546f60e5cb0599c66b317f5611721ee163b250f45236339d72c38fc72cb5472ba970c972af30deccf020167ba311721355be605a8e3b05eb4ef72bf9a058fcddfa8563a8ec40fc948562470bc4abc5ea61b7506441b9d435a2c3bc188976f9ceae19078611ce1708ee8596138bc65d25d167127a304f834a172e757b256e76e583bc663f24efaebbe83ce63ff273138950d4aa09adc6ee74b782a92ac64e58097a72fa78b72278d95698967a959ffc13be92b4cc603950cdf5ff43d373d4af9ef7ff8d6c61735bdab3ba9dbdf0a5662426571fb5a7295d4f5c4b63b5dc31e767092b05fe2fc8decf3d1348aa623b11ece9536e86a952e9f3e6609b49f9a40073e22595de5d8369a63c5ded29044f703b2bf7fd1a4e90d0ee59f4941827d89a38ebeab8ca4e1a05a1099818d6fb8fbc2a108909563d11145708eef584f14b2802a92bddc213f590cdcaa3170545d461fbd4073a3de186e1cc33686f9b36c36471b3a1655301b9210fd78920fe303d96c37304711404bf2aa3d9371ff7a3fe880d80416842e66f8411d2d9a6a511ab66d08435489ac87232ffcdd3408cf16bafc8308ec34442eda12c53e681174883cb0259cc98e8abdc02f44eebf220027c98542dacaa867267d518e1ac5a851eb89822df7fc7cce8e2d5ccb11c71ae12939eda1dd3f9f95aa42667751d1b88d2ceb9b3f8d6213fddfe52317c8b4818f66789dea06d69eb76c65ccc0af5e2ca040cd054b39e352264ee8f271e04bb7b6baa8618cb003e163f86d04b6a28768180b75dea0dc12c662de833b30b5f206bd9ab932322bb5f788c9d2a8137ff1779530ff06edbb200a4b05504b51da1ddea099e98aa2658bee0fe8b075fb98bec18dc35d08eea7760784906a469e274989cea2b477404da46d2f16adb563bd5749c127cbe2a1f6cade697f439600b1115ccf825775121c7de1595f618b13ef61dbaaed08c8a7c1225423730ff64b1a8e10ee01f39ff2e18cd5a653efbf112840663eba7fa523108d8846080eb0e6cb0b89077916419910b3e06413b999350b15588a460a0f5f8d95d94578013cd98b35e31f509058d31d2bc55775df4518a98345d59cd4c2bdb46f59b35d9d7ea4c34d9876b1e78345c883e235d6ac39f7d89c99a47ec09a22c979ff4e7b34196677633bd489e6da4b4174b205562b75a467faed264cb023f8747e3669d5df308d620c6b00a98c813ee0578a63c3b47adc87a119576857acce77b0d3771ed63531573564e240d0d578e4b430a6a8612d05747d532ff4abe2e69c9304fc8e30137b16da4d6873fac10157584f6c91707832fd433036afd5ffe34e6beea8fae4dcfa2c372c37ceaf9c66505ce4050039dd1d32beb71df01a37761497d7a58f30884e4cea24e0e528245225e7b36dfe938603a89bceea80d4e26cebaba9e5f170b4a0c7b278b8b4d5dc22026c7b0293245a07229166c5249cecaf4df379e056c0845a90a3a9d3d29a4bae835b760323307bced0ea3a67181846806545a0988f255d9664c18cbd573d0d2200ef4acb352db665829176746a06012a528e41ace6f2fea02070e3b01a5e4b31587b5c70c190ef5fd8d58da6c7ad95fd9dbf4b18ff59a44db63ec458396522afb15fd5d362404072291575a6b8bb86ec6b3a723a95f848d05e95c0adc77510712e550783cc32195787b0edfa5fb04eaeebe838845debf1e1015826beb05388c99bea4d78c46f0add1a297a4b660bd07d221401986345d2b209d01a5aee0184327045c1f8460a5d1c1107003087e551c30c594783175b4733f4e74e6451177b3be047a9228366783b04299ec007fc40e5a4b3219d61f9ec06edac692f70ecb4235f313565aacb72b6bcde4869c9f7a9e29a2695ae85704f333590c5eb93e276020c6f518f9b7857d9181d4487cac1f42c2c4b0ea78c2aa27200d90cf1c76383e11c28dc637bb4088e4412413eb5d89af75f994eedf27b8c80bead0ec3d73ad0d6ad769174596cfce2eaac2fbbf111b8a30d4bf45b393e0ed942d81cf21aa88c3c924b03a115a9dae3dfccdec07fdd05f6de5d24c029e06b256ab1a15192bb7681b0a0d25a1ecedd95c87ccb5feb58f9a400edf05ac7fc1e6911d41f1d57c3f91604a809fca04542527db0a805d08a64cc4bc439824494df694e214e3f6081b4a1d20acaa7adfdeb163f106e5871a844303c6e54778ddcf202d019410b013d1369f211e8174b20e17d5c5849dcfc2a372123e2bd1d83bf628ddf5dde622149991785d031599027ff885b4f0878f600e1e072c7d5e0801d1c949a144f6c04fb23afdf6542618203dc2cb8a412bb3d231fcdd74ff31b8eaacc7603c640c361690e659c69ebe7618bf3104c7eaf2b4c79f38932721db2446ce9214a986ee8e28912f3c2989ad72db6cdf4145121a34e6da2a8aa4e7d31e427883f6391c623593d14edb968d48dd0a0367cd44d66f674caceea3106d3dcb4da63c4770b2201672d9370abcefc85f4ce98fc0ebacffabd30ed67d30fd2b219ceb389ca73ef10bdcbce2cc2a6c09bab486c1e2d1094dc88584bbf3ee655c8ade975a91b97457d9e33eb14c66533a9e2401a00a9d30218434fbbf1abbbd3028d127b0d1038e91e2cb4b985b94a7d0289c9911f3a1e3491db68f37796d60985ea390f3deabc4617f616b70029ef269949f725571b794fb5147a29b4649a8101aca74da9da279e5757b16c4dcf9c7308e05f8d3a798e72b08f94039b0bd803678a68cdaa4842f976e457d5d7f669c2d189e51d7804ad2162c908dc50add289b254924aa562dabaf5c725ed5d9eba736b3a7937d1ac2ec63580156bb17af81b18da30d97d960b141322e47799a767383fc0b23823a9b263187c1fb2ca9cdf4bceff64bf527261e37d60d94e0299e4604bb035a409099dead983d800305dc232b04284efef5f12e4b8625785e23d6304ab6c3d343813dc574b72c01010223e723fe7579501dbebd32324ceee7b451e98304fd46850e13685710046323be119fef7b03821af87fb39a423fb57dfbbd0d2097aa9a9af29b3c21714c8623ea0432352f883180d4b6e893818fc3f4ebcd3dceacde0a39c38eaca919d5aec1aa872cfd6fc0ef5d5c2b9cf84d69359a3918ba38ca914b9bb8076ec2959485c8efad0010e5e4a5a050e32b067aed4d592b051e55c43bbb198b17f476c61d41d8bd032244a457db651f731a1abf3ecaa19df554e2f4a744c835c3c29fc5ce93d20ea3e6e1ffd65c293a307f22159ed9333320884371d0001d90490c99dd625cfe500fb65d5aed53cd63d3f20110b7518362110e1138e2d8492abc9db033823b3db4ce142629838824733ee53f5d0214599facc8c738dab83784197a95ccd40e117c8f8a7118de1d95809113e3fb0c65eb606bec5c6f0f2e6c0355f47c86cf3965dd60e2d5acd1287cfc648b0ba879aaffa2718fc113f7ac4c734a97ab4c66c79af0fcf83f7433e2a263ba802b88daf0c863980ca3f57fe31e61d792c4d857fee4515c74ab21df4f7b0fd06482efafcaeca7d1c8c1a016f8d88d119e1c36ee003271b0da304b0382170e05fa7e2194e73ebd484f1afad100a75435bb50055f32c85e41c39911516d55aaf7018462f75b05e9aeb89427c968ae2ec0621a7ba83fa7ebe63d5edcf1d30437d0583bc730a571ac8e61841cfab5c3d86d566213195fafe288b3af51005b592888ee0ffa0a4c600c4fe446298548b4c1e2d837c5cacc1cd537f348ebd972a0909f4d3e771c5e4c86cbd65d1fcdfc9a17fed57beb38c531967942b79d4fa7d90705408b51422dc07507fde1cc529ebadb97544b86151eb1c6d428758974f06b069c0c6bfe674941a888d7b9100a6bb06a5064fe41b690fd97931d874797b4957eb683cb713a3f9691e679a17a617c8921abba63f151bd00d4ac90696865d048aa010020b2e0fdc77b74357aa81b6d040628374c1ccea44df11194a86e2ec50761762cd6bef8815158899a19fcc2ca53ce043e5b5f58d0a94863b9fc768e1ca2c1b62ccc9251fb4d321678839296d947bdaa482c18ebcaab293d7011d62b3611bfdfc27b699ca0ea8865e117a383bf9d7ef53dda861e75221ffa7ac41391cf9c027e1b6a07a527606fcda2b534fc17fef3bfcda25d8ac5c330601b1d9383e66e10b9f73ec0b70e4f939e2c2b2cc926bcf24896ed827f635721eea82aa7e4d9f8d649b1fe3bc5002ee235c3e7e609bccbcf3347298041b28d0340def87ebfac1251b476c9cd22696ffce80910d546c6fa6f73aee1e6411e4625400b89042d4dace2f18de7434839dde79fcbc38c778a4f3c1e100683b95613b73b4372b6962492f3d16a62ce0046b5816864401894903708275177024e24d18b80670af900227e47a6ac4600da54bb5ce180ea10549b06106118513b185d7c7e5cf8913a421460c570597e5f6c67e4a28f4642040c1d3099dc48d1837ca9f5ed0dbbf60c438d8c8d79b3aa5df6f717646379cf7a8ec0d944bab3a8b5673704ac741a788f4e35a895f3988d009f7a5897bd171b13a25678cf7d235f48967a9379b3214c7373c57a2951ade1639940fbe385ee5641f7b549c4b697878a67d045e12190effed225d3978f73083c93f7e17c87d8323cf4a1fb44cb8ee0d70c10483bb832f4f7bc7643ce373a869ece25dbf8f8d013f4120dd6f7afbd96a809940a02db4b63110c0ae2ca67bd470c33a83b86f13f5d9f6fa1e722b6a9233b934ec8211f8275c88be67afd2b59413b3b00b0af525ae77fe7af1dce9d5241fddec343f78e3c356b210b23f748d5828fae2256f8e9b21a335c0cc8d58ee9bcff0e947cdf9e411dd4dab9a34e13c2cb4bed1fb66d908a22abb990019e2173eea563c50abea0e436487fce137b711ab1fa44d00f57d2ec0fb27c6a29b78c50e775bbe8a2453bb897fe2daec2666102826cf9bad2be062270d8fa642e6f3eef4785dbb2076840f5c9e69d3a12b45a606a93691c3ce3a9509489d238d1f8c04d8e2af97b06c02bc4b780a2c9a35329bc45ec6d05274fd729111ecbf561b21769b6ede33a534c0de0948c00d33ff42a32d5529c817a71a84ba61d117597ef42c962c3b939e457727aaf7aeaf6899f7207831d5a0f7a41f96d44dcd34811bad040b4147b1099bec7fed9b3a18d9bd8801b59243136b81bd68dc31f534f44fe06f7b51b960f9f352cadcb4101bd4855b984f0886022fdc3b0b4f04a1974fb9c568fd3e8e7d90d3945bb12c1ec39501bf8e8fe203bbe8544031428fc416cd390ba2d33c25dde0716c0fd859c4203a53bba999da60e76dc1c0fe6209f28451322c7aa7bff167be868e0f582160e8267ba0b0bf2748d0adf2927b7f8e599bd66750089286d7d45ee1bb94c74e7c2a1eb33e4b49d82fee4cc9c48861db09f180f15a91bfb5b9bbf5a0f2e76b6e69865debf72afbda0119f9731b8256a5ccac66943ff4f6527b117ec9c98c721cbb93e0cba5a4909816f17fa1e4c6257f1fdd9f877a1b96649631107fb47405be1d51e814833b092fc76fc744f96fc81132d1815a7fa367c7490588c7bcd56a0c00b5d38ed994df109fa0282878bc244c07938aff822b93407133a3a0c46a02cb30c81398d11847a260963d85d58f36b13da9d5540457e0b2f22ccfb9cebc91edf97b06ad5262c2295888fd3f54d2f08458274095e25dbde888660b6cc1f5143085ae8c21c9a50319a4318c50933904ba5a0d817d2213b84383367d5ee01f33ab926b85c851178647a10cf9950b4a5e142cd5c1148dacd5d17e511e9d02b7e8c23f1e46bc480acbf276ed952ce7da774e008e2f77727e900f3964eea34abea8c2de9279cacf7c0b4c783f3ffec900fec8e013b8252b7f5bdae59bd1b7db006470f3a40275d431ca29bb39aa2f8c8de39666f916d068c80840dffaf81614d6386188f54c6142c52fe4af5e95c684278543f41740242332a482598f0957bcfa2dc1a07e1016e77ec0aa75dd1563c5554563500e3501ac5a28084342c2d0fa2063bdd7c0adffd4fb8078a70b092d8a7a6962cc2453176a17bac5296f1d764275c49be8f53fc52ac308f5494615c407ca317073534d4ad8559e7b1f14bcc1a8b59291ee886d01eb2143351e4ea76d39a78794a5da0cc6ff91476ce7b19e69f0af2b4fb980c0995fad24d476adae40493ad68381aeb66fa5d24e9d36c0ddba15eb27b73db328ad474a59f615d6be1285d71ce93c8eaf34b0c72276ac3732877eac80b5e16e0225ffabc5e42a4485c18bc10c233d24f84ae8bf31d42c33dc63e8d2b1882711af64492335adc7cc60d0944b72f03ac58ec960c35d8929fff13ee14d06763383d160b8ad18878e8b18da98654b546aacdbb7c60700fc92ce4dd32ba93262026468e2200b3613696b4f22afe282d90d9321374024e8262f6b543d2e512621f34a5c6d3cb28be23f60c4d28711bc12196e1854a84c1ff8ab16038f2ba05c9491676050336492cdea0e8225749e64404249e7c14be3e01a7240810d11dcc5337136b41fad0de1b90db92fa68e18d26c90db0d7932db44e49325c17f68204a9e7f2ae53fff31034284b5ab804b4e0bf3e11745da9954a7518603468dc34066200ded8048acae195818f56336239b232b5a8f5eb401b17e004489824ad81fca0c2d4778c711c05e0564869a5b4d75b33cfcd28c5a3bb85fe5b6d07d98558c4f36621cc1bb576c6481cc73e5b6dcd988ae06b9a22d371996da2fe2c305bea95982304bc5058208b9818de2518bed0cd41a4a22644068ffc8f66079287d1fa5c31ece2ee4cc41dcdd88343adc958738a196937388174d81289487f30b70d837eaa92dbc600bef3946531e92475947f64db586dc5a1fa62f0ec0315e20b13d67cac39eb5801fc2f37ac08700e91c1c31b30902cf2f2be41528873db322b5b83a4905f21c6f4a37e4ae1371c576571383e63179755c052afec5bdef64c87c9b1bfb2bc561bac93a9a4ea8aa86b123be1992f7fce6f953cbbfe8246d5a7a8b19fc3c1177f071a719fb546821ae3bf241d71dacabe6f444b5f7881fbb46d609dd2768cfa69f817f9ec750b819cdacc4f37787f4b0e6f7de671b1ffff1fbf942015a7223d7e9295ea01bb60de6b45dc3bf8c53ee1c2db30d6ce2fff9729de2b356e3a30ab2082121878c82803b0a7cc4f2cffb2fd380e39a84404cd543870af4e4afbc1cefba834d23e18fc79adbe6fbbb4696d83f3f05d714ecccd38cbc8452f9410ac6dfb6e916b8e1b0b60dbef7408e522724a231af251c0894106859d6acb7ad32c350f123f870d7d888319d6b48466b1cfa25d26602f10ba6d2975c8ae02599d3698e51e5a30049ee16a784a3ebb48b02678fdf7d201824dd5a4ca7b2fee7aa6c2874d0d27ade3e3749fc94c511319a95bce8ada2d98d08c1384ee0dba5672a2630d8dfc5ec774243436a415bd2a3fa4f1e5c0d78c6886ef4a6c4ce5a75fecfe87dd2a0b43609cf05667bd1f97e811562d6619c5f20183698e1d2633326b0e12c766463c8c58926d85aa7de649a9c080e092b7313d8946d85e6588505a6abaa39ff443e47438bf2b79ade8077fa0da4c72a03b9ca31d1ac0466c6883dbb5870fb11c1598b20f4c84a2c605ec6d214281756858d18beb86c22c6c041dbca5f4dd96a801734effb389da387ab0245eb5e991f7a8e9dcf292b89a77e9e0d3cc6c7502e594d746c0786f803d22ac529aa0d9f822892d27452247f7b93b021811db34e578e69f8c9887ba62d45d450df93cefcf1fba5d7d013d70e7f694ac47aad1293bf55664a064ba78bdfc1c8a3285d8c816022c0f11db33021465241a9fb7ed46dcc57beff82bed924739832245c62e8ce1435982317808c0c9e2118d73366fa01559b20806bed5a843268f72ad4cccbc9e224d1267358388b1de7c34412c86bb183e9a0af30665df3cb81d3847c7888e7a8871555fc817315510369582420643fe7263e3f0a3344e96a8fa6cb8044772e87c4027c2181e06f0a6500e70b4eb431f91a39a6563b02f6cb1feffe5d434c99a6fea1dbbfbec194e1933f9aa4bac648720afb061211428bfcbccd4b1a4de7e1acd03e1bddb8a23834b4f25a821859be4534508c88873e3b5df2012fd15b98e10023842791573e360ea3717c626eb58f7b503171d1560474dfaaf64fa8503d3b41b9ad6b11237ea36e81329f2f36d717a6c5fb3e94917cc6510b6bd2587f153f796c6e68cec27404794316cd2400db36231bb7151d55136653d4d130664ca495aa25277653d18dd0de94e59e387abb9ea864499d33f312d2257aec96fc0ad7f7a5400537bcc36f9fb2398b14aae52dbeef9322c3e577264607817ce1da5339cd862d9109409bd3ab4b7079234644c060c6560c6da9b22fb53ce330512568527579b3042c74eb726756594dc91b46cb7de1ddc520128e66bbfce6a155539bfcc1a64485ab06706348b52963cce047a83a072b9278f10d1c0104ea14f79d5920da554d0739bce3333337440f35e531849d712302340117173d6e413e8ee3e8956344653f9920159ced9aeb0148583f00da018a8adcf2c1014771999729ec8623e5dfb255cb5e3e6db25c6c1228a4b5bdf7de524a29659232b609df09b4093d72c83b07bded03f97ff1845a6d1994ef7bb9e2a58c4eeebd979bdd68fb15508aa8f7c0afebba4775dfafa03ed7b5b3f7de6bf15fb7a005668d1328fd00a314a2b5863518f58592b2eeae20de32052938fd09a87d0aa81fdfaba343d219bd4e7702e6b8ae7b4ca0d5396e9b7a95c282da4ea5134ec5843267ed705771ed7057b183280ea5629993db30c678e2896b2afbe4b68a718f540e6d543aa53f4d90a6001bcc0ca702a6fe04aeb80af5d3b3393d45a9fc277d85faef59409cefb757813845f2e3a0bee5074de75651c1ff03f52a8fc39bd3af843fbe3fa19efb1b5418de7c386f4e05f4fe04766f1a3d0aa85d9b827252c375d2ecc4652a99429b5228c3954aa6d49b7064d639ffe4882593e94da6ffc1f2a5b0f43962fef955e9673acf667546475f4ab570a7a095ef54c639234202e847e8c77586663fb31158a4fe15fab925d58fb2a8f4dcec546091d2b7944ca56f31994a2a9b1c3a666bf2305d5226cbf6c7da1f2023b22532a19f9892a0990d9ac994c866b43579663c7a3679f0cd215bf3073f52367f7e342dacc14fe3a9faaa6f791630553ff52cafbd66e3315ae86686016f116dfab3ce3dfb837a956f01714acfbd0ac4e1bef42c20f738269c2237dc971ea77bd3573095c3f4ddef283df733468b4ab2a010059ce2c97453c0ed57c0136c4ad9741d972204048bc9826643443f43b39fd989eb04c60703f339cc0763f91c7796f0c7ca4ccdeba0a4b08c6116762b2c2653a5709cb0ac9436930aaae707364e19103c3bbeea8c743118ac33d2b17cca8f32e8034929aa67f992ea378ee35ec5954e6f52d5990dc817dae2c00c6606b3c31e9904a574ba06f29851c3f48f46c2dd5dc37211cc877cb9b686198c9cc94f9f0030399c3b3b398735f6693c7f86f1186dc457b435ded81516daa8eaf7a7d75179d3732ad0e559c0964f8129bf02967e07efd2df0994837b97bfe1eeff4879ee6f78b37d4bf8a3f41bf72ce0f65c05e59ea2296553b22850ff07924a29d5573aa458c1148a6884270683cdaea758a16ab3eb5cdcc54575626149ad702a28dd6742a1508f7279965f71790e64e93a96efdea563f915708a8e53e426f5a8c751fdca7b204eea5134deb9587b59be73d1ec6b61cdfdd0c6c585e54790b3698f1ca6f0ef48f995dfc102a6ecb37c8ee802a66cf8d63ef72d9f23729ad5b4fc113d71042685769d5ab6f565983395a37b97bfe9de25fc51247f4bf84315a6eca7fc0af72ce18f5498c2618e945ff9940d6f527e2575537ad4971ef5dc8f1e057aff81fa554e53a4a0dc26f194735d2ba14b2922cac96fdf8df4d3095362fae992f9f4b87aa468fa51ce4a8ff2270e2cfd06a27c8ec8bd8e1c91e3bec4a1fca95442316ddcdcb8e91acd572a07f7dbdf6cdb8fc21f27bf6dff2365fac669ed72b9be194e97ee714d772f55b7e7e6ddc175c3e9922fd365fffe0d8bb8e12dee29e7b3f58fee29bddc37d44bd5d0867b1d52e47e878bc72567f0a64f8ac91999939e949a6d4ca18ecee8bb22a60d7f715ffa1bee4b29dcdbf0667b94f0c789f69af423efbb227c45b9ce1b9174e7f2e971f55c271d51b5898292e252a77075fb5a4f5feb49ad2ab55467ad951249c1144d6f6b7dee04ec7e03b912caa77c0ffc2412e95148298f02a77802a2fc06a6bcf7c02fc55a6ba6294a67adb5d65a6bad74d65a6badf5fb6a8ffb2398f5bdd64fa995877ca9d3b3fe2e450ccaed8152fc407902a5697a36199cbb447a1338450cce6de9055336282829cf4daf04a6d85a6badb5d65a6bd539d7bd196cafa33d102da2502ba4af8e5a462913894545d334d5a6d2364db5a9b4edf42dd3d39123aa5e478ea80a7fe488aa27fd8d05b09001ed20aac21f16c04206e412472d2c9f7ae9a5340db5c27195e3344eabb5d6ca6ddc73da08b5b26ddbf69b16d2785d7996d7417d8ad36460b495f6daa7696d0373a773c6816efc38c81c66e7b62ddcd19235178802898664b19530c57d0b2a4ce568f9954f71e18e9670e5558ffad40ed5a35e857a1aba8138484e0274e340f716e2b0002d7416f0e453a0fe1530e55120caab9474505e9348a3ffc4142845150fe5e4f529d421fdc83422e9139494d20e4f45c9d93c45094a63323034ef2edb99cdd60394525a85f650e606d0a61a0998914f79ca3d4ff5dc7b214e911bd57361aae2b878e6404df58ffea1adb92359f4b3a63b2c32db26a5faa79c8a09e53ba508e92934775a28c7cdae458aeee9e84096e7c0d4cf14ac7cf729403df70f4e919b5e0e29ea7caff252e4e6cc0766666b449143323befe89f6c830c02646bbe40d3f628b3b393bfe6af61cd7d1ab73d5238f5f655a00ba8b5d0a7ff5ec7f42a3f7d9e1424676436cd3b72266fdaf283a6fa675324e48cf6f4b51064eb03595062535964440b6d902fbe47399483e555cf2365e3e202b6a84022f47cb976f28fd365c351af867bc9f23aa4c812e214c18fb3f25d2ac4413d97cab1f2dddfd4cf43014f34123d3e3f40b0986cee68a09f9d1f994a0e761417fd85963ad024a03d7e1029c863f2c77c4542f285963fb7cb8ecb0e76d971d9d9d3e5886c7b6a06baef7c19a4399264376ea0493d18bc20a844874a5812440a725250386a5290cb8f7d42a63a40b488f240b2d8c31c432ce2aff38211511e8816bb979b7692b64c2699bf0611a09efedc19a218a82188884de641593473b373d3a3bcaaeb506cba0ee5bb3775e18f1c11e555e0143de5cfbd87a91c2a2aa7e7421c963fddb0fce97152d34b69cf23f52acf83253b38b56f3ba7720e6bb8c1c7d4a13ea050ece043738d1961704080ca987f5ffee4cb5725b59fca2815902ff42b688305268b7e27a701c6d11ee53cc028a7b071c84306d5c7ff38acb9f8f108e6b69fcaa18dc984d243871475561ef5292dcc917a95df910a557e074b78caa91caa4ffd8dea53e18f22daff60795578a3fa96f047ea55a1a4ac5c7de52c18cc7b05d48f02499f026efffd09f4de04768f0272fa47af437aef2dc87d498a28508a1f28c5295a50ee94ed4fb41465006ba02cfa9d2c0279c8c0f8ca94839d44034eca18356cd31e3d72d0db3f07d9953ad992a12d1a47983db4f5bdf6a7a7de93266ce4d3e3abd9d364d3b7e97ef6802adffda9880e29eaa4fcc97fa014635c28da320a5bc6be10c24e790d3cf90f9ca20a28450f2481349425b3bd0ef9a3c8cdc96f9ff20571a3711b1dd39f5e7a299b94d721c59450d2935053d609fc1c3a6e30e02cfa1c0d2734cb12dada10697f0fa5f46e689ca3e18496beb2817cb9c1ac51844f22ce1aed1ace1ef9423f06245afaca71b8dfe3be7d1a4ce8fab504b9047a1b91f2dbc9bf5e4c59d24a514089a66c524e9e7a291cda6cbfe304d43aa4a8a3a3f336291365c64a3a3aa3ef5ec77bee39ad859e40b347ce0cc9199914193699c2f9f241593b1ad4427a9cf4abcda31e2dd135ac1b0df59efe28b36f2465b5fca0f1ce4f1abdf7dd731f2e97b8a557e9e6a8bbd399e4fb72961fb8e5bb7f5fce4e29adeeeeeeeeaa21a49452924c4a241ee2a48c39b3a64f5cae775b03b5b4d67aadad54bec8d98b3b17e81ee44767644c0ce60fdb698843c670c0ac563432ee2e04df0f35f7dc35c40178e2ee003c912fbea3e972b9e48cdc3397d37dda1d298a3f6d6863ec4add08df53777bca647bce30bcf5396bfae83d9e84fbe6ef403c6c1ecbc2b5dd611c0bd876ceab17dc612e972b88e822e8d9de64be26103e87f8106df72eecd844e24a669090ed4e83cde2adecacf958aca00363bb11866c610925409cc0828f384a2700418c2e1021c80b3f4944395dab9810465880831514ed20322164051178828a7c208e1348023e5378c28e0f7c7082083e8916b00420f05c81052f82f083e8527081053128ea096276856ddb64eced65cfde7edb5e4be189bdfdc7b3b7ff2908eded37af665bd9806177af79359db31481351cec86afe6b6adb6d8423067c998b3e496ad8260663243ce925b88c859728f3236ebc1d65e7becd568ad9c05104062feebd5648d0514d8b56f85010b216aac8c7198949db71c760515326cfa33aba4a0267199d7c35129e39133326c9ab39cb19b3e6b6b529e48941cc17b2fbe175f29b3b99744d4843c6576bd4688a0041d2e84c098428e114590c113848a74823c21162961c9685e81c4a6bf9291f04d945de915378802149bfea7a74e8c09a6a9d284a24dffa4e3892ba0d8c1074ff0200a1e3e54c081155238c1051e44e7c2126c8022674cd3094b70b0e997b2a6bfa294de504a7d079bbe0ca594524a698a5743eba378357586922910209d0a6f8257818520226c7f1db464fb137ea0b0c3f6879131281861fbd358b1b3fd715891c511d9366374ad02b6e993667041064963017b9461bb1362b0dafed90947d8fe1f153c6cffa76208dbabd8c1656678a1a48a1bdb677ca1e4082cdbbf4b62885b6b13c0d02b29cb2fe3312e0529aaf8c2c3415bdff7ed29abe8b57ef66aeadcc1a6f43115b126e86c5fa248cb9880c4a65f7386fc20041766510ce102081d4174a1029d2540611603d1298e2fb69faea03f18993d614c80c10e377d3afd1b80c363eacba11ecc014c2144b30ef4143b98792e6ab5b5d65aedcf5a6bb53464dc6ed70a5e1ce8b637c4815a9bb3753c055aa70cb43d91beaaff4d6aad7532a10724e108b36b1d6ff8f6dc1687694389c39c16d75a6fc5f8a98871a5a0ddd1459ccb4073bee59472ca9f36b42ddf9ee8e80189afe4963ffffef56aae07819c2df3bc3f9fa31ef52c9ed3ba17a66ea8989f8a39fca18398ed890e9d20be92bbe4315fd853beccb7a0dd79bb846cc8963159903d723c7b3e8d2d6349665ccf9ed927d7ede5cbbafd3f199a652ce5203d934fa3932d0fb2912d0ba344b2cf0ca7cf8faf263dd1d1431833ffd0300615e8510363ecfa63f689c0efb0ab0e8b103a653f7747d01f22d9c7085ac6d75230a57d106de523246bfe0f721134137bbe077990077950f671ad37d9ec41cf16e4317223f298bae76f61788c8c4951b4e7cb711bda53c61e37d956b427e7dab31347eeb527f509f2b30304044c486c081911414346af6248c72e8199ecf9d3886d849c2132a711194a64d8e3f41162329b6c618f338c3d9becd175f6f77d2b8d482bca3eb23d9f89c760224c8489301126c2449828f34c9fe9337da68f8f5d4206874cccfb4efa6abc3ffb0259988fd5f118fb9af6655f2f9bf5675ff6a70a39e32f85b611291bb96fdc258e421e4468cab281b2a4d08ed17761caa67bef4761ca897c353f47f4a0fd23c31edaca3f9435df674ffd659f0ff21eda92a2cf0f506ccfec4359f3c1c83eee44c8194ab5948f1f423be36beb1072267f081ce61ea6a64bc72544ce6860086e90427b2372b9f616a6e62b8814da199442fb05c40e103b4b146ddfe119e2417a8ac83e16967d2c6c3a0d753bd713002470944a3a539eb1ad31239b72630e625ddba78cb13f5fda4de3ab496bf0180b1ea1fdb5165a86ed9a6e40b65692e55426f9b90318db7516b657610131c8d64b967c9964bb5cc276036cb741674fd890c374798c5d3261517841ceba9e9e1544c1648f53c7561984012fb66b41af562bf9a39ef1f018f935726bfb02b6fffd13394383dc3d4ee4cbfcf9f406ffc811e74dc6f139e2f41d2d5fe627d11994a91bed734edde4c738cc81d23087d79ceadb8be348d2e98b06a9d6c757d212394942e467cbd7485ecd11ed2149d774d23d6f7a769472c0ddfebe3d1c29b07fbc1a24b6dc60db7f050bbdfdf84a8e1b10d7755ded3aeaddece46934e6a41ca072b23d1c3bb0e7d3f88a060d794bf9e248642ec3d6de90912f32cf9cb3b423396b482c6560cecff61c2470d061e20052ecd1e0cf91bc9aec495f691bbe9bad1bdd369f9b548d4613ec198e62f245be2645ce5a4b4d59f275f460c4575224f182caa9aba44900c29e3032ea85adb3deb0e70fe91b4a7bde50da59d04b44c136e06d750bd2c4a0025db3beb536d1be85ffb44593e9a76c198b62073bf4811e651430d8f41b20b78ce1a067d3eced5fce131d3d0c994f5b34305ac31a546a5d6aaddfab7e07ea55429c9647bd0a857a9cff2f4cbd14f17c14f8d55abfd65b6b5551f98f964a2da80a7efde9daa9b5ea5a6bad456e1d89ad36e58f7a297e60cafffbabaa6febdf5aabcb7fbfc3e53fd5e3fcbbfc0ed5a3429c96573d0575941e54790f2cfdf46a46af320a7fe488a507c129d2946bd48f7ec7f75e88d3f2df8f5afe7b9c7f947d1e2e8f7a1eaaff5450b5525a6badb5d65a2badb5d64a714ab3f569adb5d2cff47358831f4ce547bdcbb780297b431b95979bc629d82365c31ca877f91d2e8ffa1daafffec1540e6f50ef127eaffa163075f3bdea3f8a2fbeb6d65a6badb5faa85596fd6409f3fb3c65653b954e5466909df97a2d99bd2eea378ee3384c009a02700aa85eeb0c7b8aea74d7ccc78fa21a680bc70c47ecc2e0ea03cf0ab8f982c1eb851a41cefe686bbe5e2890fb4fce8c367db92315ae84a9fb2b2fc51d92b270eaaefc0eee57429c2237df6f8fb3637bd4a3429c2b451aa7715bedce0f102cf69ab3949ceba4d8cca55b7a205afcf7e1f2d2a5f2aadfd1f22e218ecbb7bcaaa5e571c2570953538a5b0b98294bd3968f700bb10cfa0f7f94d93276aa20f82e2d1559e483b600e0a57ee55f3cd307e8870f5f15f9bd362d9ff21298f216d00765e1548e962ffd8e962f85384570c26ff91daa5709715c5ef51c373b00a47284aff23b5cbef43523f0df03c19fe1ab3c0f972f3d00c0294a2f75ff51ab95006c1a6e8a6b51ad3e5c4510299a3b73488fcf848181c1dfb72ff8f58226902faf1278822f9a3cf828c2618da76e68f361ea8261eabecb971e270c53f7ff5bc007431c973095e3bfe55337bcf930753fa5a97ccbbbbcea53372eaf7a973075c3dba326bf8dcba7b430b543e53fc429f22abfa3f4ffaf1a41ceae2c8f7a9dd47f8fabaf5a5e85f229f0e44d291ecaebd73979d268e4fdc956506e53277a23923e4149f901f2f1e5688e52d4a37eda5f6ed7c196311a20b1e71ef1cb673064bb3f7e01b1fd7110c7af30f6d8cd399f86b9e7a7fffbf15462a0a578958cf9458030f47895dca2eddfd244cf1f3150d0f6e77628cb1f465740b425c520590cc35e94e5318ca12790af469b861828069afbaec60d34f5f15507ea1631625789c7b0ec198e8cd039b9c6107aa43f9327bf64ece992029d72007efa384cbdaf64e0898130106de51706c240db9d98c1b6e1a899d0c22c08a43e33965f5dd094556397fe28d9637559253e63f4976421cde46b62c3c82fd7f69f47642bfb20b1679e5c43881ee509660f3e509053347f982b1a3933579bc278504dcb396f396fdab6e5bc79997b2a72d957f9955f2a3f5a8ee62c51e0601cd018b6c8123591a34d2d116d4912c9313065986cd26e09a2dd66018ceb721894a0e9cf97c7d0d7ac16e44c09b4259a366658022f64eb7bdd1eac17b6dfe02d3bab806cc948296c7f1ef246f7fe769601c9f22ee45eb3ad858c02baa7e10c2513dbc40b3d778ffaa22d5b4459fe6f89424bc41109a2b91fcec71ed1d257daf7c9dccbc35926d511cdb93c46c763b6f727a22deffd72210e33744fc5ee390e3fe7eea225a22d8ec81259a2971cb9d768da23f7e2421ce618291bef73441dc4951b37d05687a8035de47c4aeb75648417deefec8e058267080e3ab8f60db3afa625da0eca6d8938d8e6a331fcb57dc290482959a0af4d42bed89e3d79a4ac2d2cd07a3b0d21f4386113467518ee315938ec9b4cb60fd99061d690c2d09cc9144d50093d6313a6841e659091198be1087a94b14007240f2b64cff7bf80d4808cb1efb6883d7f9457d87e2d9671e7b66debb6addb9ed26ddbbca7a217fed041949435376bc4c27c351f839647beccbfa01de2ab1e5f25215f668cb2a65542d6981f646563cc76584705e740db278f2679bebf266740f9e2efbfcd56f899076d650ff26818ed2c5ffc5bbed013639ce5cc8886d19ee1ef407f40b6a41824b378c1155cb4a4174318c2822fb2a0841688c4f99d2763eccf7932bd19487b7a33b8dd377ce51e04f9e20f0a691a195f790c35d03694a161ca49e99c944e7aed90201e9e85382963dac9512929f5a474160f44291ada6cff4d6cfa367ee32bd7401f9488070c4de81166cf70d4ec903df1bdf8badfebdecd37b43b3cbe9a22702abb8e9390b3654a793aadd669b597fabc2670b47b82e0e630f589ab7fcea6937e9de52eb67b7eae48e81ace394b336b74e21d5c6dcc34acb4d9b1ad175b6cafb5d65e190190e539736d67ed5bebb386b5d65a6badb5d64ae9d65a6badb52f7d65adb556087afe57a4e7d357c9c05a6badbe8269ca501ab5d64aa9bbbb530ae32e43bec877ea3be78d6bddb5d6fa75594a29a59492548fe8f9f45333687bca1777afbe916c70ab6671d62cbe95894c05ab69160751af6683e83a4ff33c6f942fce9a0d025f7cb3662fce9a0dc256cdc6b41a55ab18638cb10d42be0ef4521494147a7110f7534a5e59f027e5aada505e1c84bd75b3b732b1514ac3295fb4d7a86bfbda1a84942c39e0aa755aedc5596a5e4d7eed729bc3d47891366e660d9fb9ff5519b44f4863991b1919a964fbe75b3f1972f861aeeb3a4ff33c6f94f368e4d5d41fe14d43c6d4d764e82880eeb70ee4365b6572984924afe63e495fbb7ad296ff656f86968ec81a1587d67ab3f669e3ca1ad656972bebfb56eea4932914971c7fb94eb9ba7fedcf19eac6f7afb53597bc1afb25d39cd15ec2e48a8c8d6fd6f00f80127a3ea791e0db39d4a75dece94940db359cf22567cfaf6d59830c6d79c0595e0159c3bfc9963317f878086192292336167491843de61eba288250173f4c7491031774c1d3c5cebd9aadd48b609b177ec05020d2887eb84f906521a127ec0859e103146242b21fa0d023147b554124047b59c12354041504c1484adc2010d1c4107c00848821421001848684881f362410e1c24886d0c21186a0429e81d6b68c0d1105ed086db78c0d1164032aa690f3f785d5636aa50df055cedf175297564a974cd0efcb19acb5ba0df9a3e740fbf69f207071d690ff5fa77c91eed65a1bd65aa753aa1a42cf7749c3484a2761319d565ba9bd9836c063ec9592b5e583531e99423e61cbdfefe197b5294e1bf3670d9f36acd033674d535fdb648dd2734f5bdb6b6cff4903739ad1b6b7529f764433e50ce3d943594da4fce48c6f3c7bf063cc7d7266b4f18fb4d0a41fe12931fe11387f280b3f09bfef1128c51ed006da43fc143f0c5ce81166c77a34f8d67c4e9f73dad03c1af0ab8ca0ef1ebf1b424e58a0c7d933aa6cfc786efcb3081df82f206de60464ccf6f8292063b8c71eee6cf7c999b9f1733054cb5de7759dd755eade8d9e8aa3f0870ee2e83b3c7b3c067760063590827627abc819faf8a50c1c8260c2b06b57bcce75f54332d050f88e158050a0f09d2b00d9ae20856dce5eb0e714a262cf3dfa102d614073487a00e0c2677b9ec74514b68785207cf7b97bceabe9b4e4328764731c13226c8e0b1f3677854d4f11bbc24e7e5c6f58e1898dffd62882125063db212aaa80832a7861c888d59c41912834ec08a55bb460d32da4a0c2135f85224fbc3a2cb4c5829eaf4b312ad460bbffcc2f4cf04e0b2053685185498b2672663efd12107dbb05d9b232488cbd85cea630f942df06d19665c2cab6d8d41ed9d406b9687b288b8e76dd82a696675ba24dbf6ea165ed428f96c866415a220bc6a677874daf0fa69d22707e32dfc6aea00e1e98c82349e0d9f26bd51246c6dc18fc474896c421f403c99236aa8d4de3155f30755fee6aff56fb16c6da2a6545382983932167e8cb9751005bfea35027da0ca9e597a13282e6eca496c7b8da737b5b05b0e773396b6d6386ac311f6f1bcaa013d49ebbe00dab7db7707cd9f5d63d3f77606a86a00d60e042c27ac071d2868d9f8ef7e5bd97864a4ceb534a6db5f6daf77d9fa360ad21e8f60dc71cb60d6fe8f6dc9cd76e941adb7bbb3cdadc5170bc95664ddbd257141ce58ff45de61f7cf3b54fc35ba55b198e36ac28e1ab912f69add47a0be34e4a4925b7034a299058b5f369b5b60289715ec438a21890189018e83bd3ec230389612038d8be2123f3b4d3b6ca00e32b7f986a6f119ac66a65af6542cbf8bfb8b27c38d3929e95d33a19434393b386737dbf5e20518e88900dac101302c266416f5bc6845690c5183fc88ca0495bc6b220e2aad07acb5816b32c8230deaead94a601382050fa228b1fd9edc1a7750622881591ccf01429704115a300ba322dc44036b34e9881679d3b9d048924492431a2bf2c3bc210538a22a420a26332222ec8b21d198fb3e41eb3cec2a7f445f705198d923dad9689a04fb68c09d1e24a01054c85eeb68c0991220ba1b92d63429200d32a5bc68410d9a384dd233a6f191352c41ef396448fb68c09112227d177cb98909dd21716e8e753f0737094479a98b97ff50982a004741b6182fadc96de04ef96decd0cdf1e7ffb3ba5d45271da39bd4e84a0ef73de49195a9e3b246750b6ff652267e8fb9b7efc2f17f26e61fb0d6317c997098664f9fb6b87f9851eef50133dcea09da0ebb8d6fab566f97287eed069e71aa10b337e66f84839fe4aa8292a3ce9f0f482998ea40a483669cfe7eca9d24b5f940b710d6dc895c02972267056a10b5604899309168488899c8b410517ed4ce054c2164d008927d0a14842e42c756ba7acd6ea919d41d8e20b44e209b42e1842d45a6819a79e138cb64c31ca7a4d1b73fb9f829c766e38fe787afdec0bd2b2eda6536cfbfc3a448f58e5837cf107c7d0299f4279b94fded4c48cc1e126d8b4d13d044c4e6c294362fb978e9036e692592252fa29f9c8993cafe001c9b5fd4732dac253f41f11d156e97df4f408440173378a9d5ea3980e47b1d3e9fd4750c8d629c43e94e5ef6902c856e9fd614631da2a85a62c4896bf45c0f670cc76143381b1dd74321199c21b64d19e8f7d3c73dc0340104cfc38e580fbf669b86f431f80f60db268e3709c5f82b9ede790f60571a0638cb1bbd00423eda99520607262fb9b9a982bff207b2c2151fa291129f92c99364a251ecc253e7b24d5aa5732caf2d12868fbeb70b1148e62a7516c8e64b3f4b3542a85a797a989a9c844743211956e89e7749af183e2a61f70b7d1156d75a1cbe639afbdd32567dcdd082e062be871ba968cfec3397b0ce6603735e2f522174e216739bd94524c2995d44a8c25c6aa2ee81cbc1a1fbef229a482140f7a9c2e57c1f6af31e30d32fc5d057246faa0ad295be5b09a487c253487e6cc5763ce13e3c933c4e51abd600c599a25a7d5deac6d1ba7b5d6baeb4e929c24399d462492d627282929a592c9743aa95c151515aba2728292b279d5badc75a85c773286a6a19c25596f97e29546273f9d462592499f7c1ffd3eff4c1fea745251f93e146a6525956a695151f93e146a652595626151a1b840bbb8fcffb7c2524299564e2996930a4be988140b0bcb8a2ad5c2e2f22d1d8baa737917176f06fa5ecd7b351e565f81326233e88c19337cc68c39230c012043c68c190108800004b05ab15830c010760000402763c69c2143860c6f06ba3329d7cc902f3460701804b062c110038d9701ac565f3a6286ac140b8ba56a61b1582e2b2c168ac56255ea1386d10c141c4a6d8df4ac31da483a3d6d74b386bfed3adb754ac0d9b64b6cd09e33b44e98d3f78910b445c2ed8faf7cdab047c81afe9607ddbdd7fac81731f468617bb43e3688b266942586b461dfdf8e21673a3b4459d3860dad13640d30648c9d42aefcddca8abad05661ad4cbef89ba6d0e3edb132d9ce7f85b0fd47026c1adbafcfed61e95e763fa66e4f079620effbdcec346db9c7dc17fdebc87b1ad7dec2e8f62a981ef5eeb93d3dea8fdebb1321e819da227c75ef0874f1f6e8db737bacd6dd8854bdd088d40d15f5535187d7f20ce929828afaa7f351179a74f7151a56e8e9d1909dd0ef56b876d6b9aaf15de528a5b55a2954613b11c9ce62181aed0c02ef0dc92cb63321ff289bb09db44719b3827a35f869ad565aa723aabdbfcc97554ee851c66812b0188e0acead85d6b19cb5febeffd54afa4c9f9cb5febfef2b1fda7271f3597df76738fefe9498fa74b432c63ea535d0d6f4e1344a22f21af2c5321cff7b4c1f9f3b7d2ea59752958f1e57abe7a69c958eee113fd8fed99ffa631ff9e2ffaac13d86032fc2d8343058d6b44c29f6f1919176b83fd7a7c94ad5053de58bffa8adb53130d1a3157ad19685c5b67f157a31ec901da2adfbba2f3b6487ae1dda7e84883b6dadb3d65aeb7d2aded5fdc14dc486866c68a26d18723dafd0748f7ae60328b335a2b45ca1ed1e75cb15daf7a85baed0738fbae50a7df7a83dcffbc0d090a6c286a1d8880a5a8a190698077a94b10f00a1b56c397ac3197a80f015cc0689a07fefd0268e6c4abd18b0d038e4cc071c205be3dc21d685edae85294647e871e503c46ab5e2baaedb9e8adbb60dd930e28293d265a0c73cdaa325b46fe9e55a71adf8de6a6b55811448454d78482be86aadc5b8e6f70a6818f018f93c483d72a6b5fd6ba02d191919f288218996330c90b1b1001928a64b9f7484ffa3f4a31fa5b5d6dc93840b09e7da8a64646464680b86981eedcc579a909cd9420b29862da2ee2e2343c346b43561329bcd68cbc26033d86c666116369bcd5c9a132929b5d65a6badf5556ba5aa257aacafd9d65ae8fa923377fb571f6a1094fb33dc10cb975a5f5bd0566c7f0985ed53c80e9d08d3a69133f7fd6d3ca6c763e8f64712f07a51fb51ca1b2f185123a2041bc1bd06beb2a8fd06ea009111b57f65911bb2a5b8559eea0a69517d515ab4fd9d525a5faf949f0d082793737e152ee8fae367e9517a29c5b022b4fcb4f8905e4d9cf4ca1c09e322122ea22d920f92e5ff029710920d482ff9e2610f1a7352cea7d7c8f61bdbfe14467a4d1ba3ed4f0ae231323943dab194e443a3057a24bd48af70d43a7c799b9b1e09e62bdf314978099d710c43f428613953da8203bded3133d95e222a0d9976ee2bc76f7be5c60df415da238b9c655c44598e5d7b2415e55a6f80033ddddb5149a3306ea049ae5111e9b5fd61b0821e15ed69ffefc518678c339e734e0fbff46c34d28bf46af9d147acccc6b44c83e48b16d21cd20fa29452db8d78f0a2adfc5e05d992a2644d4aef1c624bf9324e212ec3f6247c85648f94c9f6a74342dac22c10bda1754aa995b914283b28afedcf35795b6df8b48679e85f60adf52167e2313f726601dbabfdd18b647c4167a16b385f322b43cec31ee7101b1fa22c7f1a2f3d830fb90ff910ca8e0dc29454aa70b59b437c95556648d3c4d08fc6b7e10611b802bcf7e781811ac09ff3e7cfe71952840f11fb4359f3f10e3839d242e3cf2f836710070d207a9441068fd17a5a7badbdd6da2a23638970d72c4acd30daf4a5b74c5ffa31eb2c5fdc1466afdb9cc6b94ce90c66c45bbed0a31a4568ee4bbfc45ba526dc9842dab8e1757206096fd19478308f20f2337d82d01de4d479bdd3e97295c01a6633ee5146dcf49149967fd6e1283482914dc339e2384e739ce6ae0ec771a5a762c903ad901df29565e2ac69a5206bcc9f9fc59e3ffa06da997ca1c1c01299cc85e22321589805fa9fa6247a4e3a27f5e6d3199731861effdfc2681338528c6b72225b2c2d6746db577be2bf23223923a7d8beba02ffb8d272c66b758942a7ec514740faaa879c75b8f654d4c21f3a881a4f5dad48442a3f2a3eabd58a46ce38921ce87116a047d36ff942632c601e33e2d70e104e22e72cc606c21863fc4232aa4184a63f33b6696bf2bc785e2ffce25129b94194e59fb3fef00dca413ce62567e8f6abe4249333328aed9f81fc738615e558916c3b0e6938aef6dc415f25bef2f77098caa17ff437fa478f7f14fe2882c31bd27be10f9427fd8f93d79f23e6d7b495b29f62df4b097f70f851c013b0c60c3469148e9fa62c8a8b80811ab09383f0d06cd9ca3ec0605d87f1cc3c3a5ccc3b94e54908a550a814ed17453d0c15918c000000017315002020100c074442c1705050532dcc0314800d6e8c487854409687a324476214a59031c6204208310046446668481b00c44d6a21faa695bdd88540057e08bf563a00b69fe8a20c14bdbddc9ceeb3589dd79dfafba5c41e973c51df2dc4d53fc224df1bf07d7926923e54285d6c0547c6dbfd9a4542903c06f5f05d5047e13115a92a6c8455210014e30e80a09afbf7782daf200069ee9cec4fcb5d0181476a8e3ebf461da85e2033609708a2002c1b829bed5a5ba9c9d423f05891d6aa3c98c6f5a7e0eb2d9013c42ac890c5c10845cfc763ad9e817a5a446996faeb74ea1a18ed05072f92d369a43ef9dec73a3c06140b81a766b3758ea96722ecad727cea15fcb3a5c1b5ec7d88dc1f3dd10b4fc2da917cea29438527ebdaf9e451335fb5d5d80c3d79709459a4504c5eb8b03d27db6f3d40224122f435a248477324ef8f6b78da38f24a805529a87b5288ebd4bbef21c319b85349bf2602149bf0e3ae9d604341daf6e7f595c0ba2fe7763a6f4868109c4e1dc0d9de0b4eb814a73ebf89744117778e04d69bfd4d2d43819649c9aa9a3ecc9af748688642f6af1f5ed98d494210fefcf4ec07401c964d8c3970c830ff68af33625089a6d96825f084a32e0c0e0d41a4a3ae4c00b045b6bbdaf8f623f62d2c3c1a588545778d44b8058130064bdb28ec1e005220a2909b247ae8f0f838a6938e9178ff39925c75d231a78c2d4f65a7930619b24e007e5e629ab1e081fef1710ee230d9e7e0d3a171ae5a0b1f169785661454d536076dea39344e25ba51dace31c85116751b4a661d0e530743054858080825b9980494704eb9524ea7006c3ce1fc33741793202bb912b1055a6308ca5c0d67c0f942bd320b29f6b9b92bdbc2cbfc9d7d6219094e8e3ea5fc05aedf9eccc55808c35431710f1a93662803741e63a3ef343160a478dfc8f5a2c0af735868d4027b2cf1386dc56b890dade39c703dddd947d599262afe5e690cf82cf062e6a6ab9315c373011ca5d674194764a38e5759a7e1c7102471b02400440396c2a744fe608cf0a247766392b5a65a42326b6a7083d038e755b98433bd2bb7bf861dcbb0ea3afa0eb0e59f0824b2addb0b69b0c2dcb817478358d5817bd4bdb8483a40bf81233dcce288baaf4d15cd22959daec350055a47d6ecd5b8ef00ce2ddfac779e52af08a2d92e38c3793e7a9536068e956eccbc458e15a78719bb61d7f46215d508ea9e68953b46a8f158c21e660535ce0521151a74ab37f484184f29ca52c649aa3ee963bbebd14dc50b77a4c53a616dd029565090292d7afa6966cd41527ec8d8d3ddf37c7a7b777a83234095da3f2527f5b2258051c8af149a20fa69ef461ece0290440e6f761d517c0dad75e7af894483ba9df88e79b3d02e6e64930a3f3cdcd2a43a60fd4310bd786e08440c33cc732a880c5279f2a3d871b5e95007d4654b9c8ee43ecbe5327417bd727b168aa245e726761cac32a54ac13b17a126c8025edc7cd12a16d7e42851fc88633355052b64c44ee2a679aeffa9fef71d003217d74e29e556b1e848a12fd19dbce972158bb4a940f7cc7761a3bf8462736e66d0c83b55c1e890531c490a199c88837908b5a4f3e8cdd5da35e79b4b8aab57e40bba326dd606b5f36c2019a2e94315917b3fed8c577ac756ca1524460b73cf35e4da328b9edfdda202ab865f7e2f34f23d0845023df374a176fdc70c2b78415f80e4645861bc246764c627620a62608e11fbf281bcf6613dfe8a37c177a8c2ef0fae3549e74df93c19123e400905045a42bf484f7307407d98fee984dc3a27018d9456a0bf007ae765a705722ca879097784c655fea33418d6bb832380f217e0a773190ca56f7175d266f21baf965549d72a74cdb839c567293d7c6f816e3c8debf8d131c16f73478a898649be81f2647fde29bcce20eff1c7c4cadd59bc8bafcfc735397ceef392e389a4e415a0ef01d8974caeb6c9d192c4df38da6dfa344ff139b94766bed4a0e7cf31e729ebab41f43c427e2c551d2fc9d8bea8bb732a554d4d8ab89212316604f9245c7b63417383f51507c064160ce9e2eda7c6902f49bac72472f1c050820c5f1ed0b72961d05d6d6a4332c4cd194addd3777d437ec549dcddac1a8056264e595165a0d4e406a5c5773540e1d1cdb73bb66e98f8de843d51bf19a47c29fcdd650deaa2040d6190edb7f407746c3ead87a0bcaf3a49bd912ef72d332b87b190c9ee681cd24ce4e31da8428e790970c96430a682408f5716addea498045335b7ac26f7bea71f1c74297e007aafa4c8567012bcfdc99d430bc6ebfebfb11993facdc71757345296ced0f844f07d482f81a70c5e99386554ff4db606e39435fa25de2fae3370303e3848d73173da2b129119ae8e2ddd580911051a515409d1360057253e102569bd74c0652293e4127438a57ab9aa45c4fc83833ad5895926495bb92feae35bcd325600a9ca3a4cdf9f263f4e59cafe1a7c1db0f19526c48c4edbfb3e308d3f8557c1456c51aa1b892bd7a124f82548b0ceac39575eacb2fbb1a36579f524e6c8518c8e0b8ab79536e955d9bfcd401cf72968668bb6f793b0c09d062f8aebee44a7f85959ecb4caa78153898cea4ef7f9d6b22e567f49f2986892549322e108233c09c50fa881eee00a415a2cb6e5216f3e514cce8e9d1aea9c9b9d46bd1070ddb2507b6bed66dd67a5871a89afea3846a6ba13cee60f71a3e224b4ee480adcc456a4602d65632be926b8d9348100b7a1cd980116bbfd63878e52d0aa0428fbd38c5e6665e2a8678e11092c05c519f32765f5873cab7c9fcbd6637f64407b2d8675e8b9e8bb66ae2fb1eacc95bbe44da28a5f9d42b398a6624693a7a8bcf361901a66480379493b80da7d510d05950a0c6a3ce3bbe309eacccb77016480e9a16169e0fbd48194931e0c0f51a9d16531983e1e1e94c04dc813e2a80bd991b981117197f80b51c60b4e0910f16fe4bd563796408fd030b1f428f3effd26f44861bdcad33d2be9c8c8592c1a351500f018f46cd58a49292957714ccacc7a9f0bb951cb960ff95c1712da13a54ca7c438c05f62add5cfba78190b5b923a5ff645cad5826165b9ef1151e1187a2c2be1832a99955fcc4fcef179adbd729c288fdba40035d08e53644e017b3badb4691591b22b79ce87c9bfa609eade0c68cfd7ed7cc8a16ff48ea0a694d38a963e604a1f9df54f9ac4710573bc4131a56c72cd0a3e0057deb03731e6968478fd76bbeaf9c0b177d2901a45406e651c4251c4a964d3bf6e4506db3aeb0c581b103209c079bee08b352e800c247701db0708714b13e4d7269a67c94ba82001e3aa2ab3c39f529a69276d6d0c37edaf8841487246a23f875e4e74123d804fb6a37a9804a626abe693168b6bf6c247f0ad1ea614754695fb6ef1de240db16b61da29d5db4e82c76e6f5f88f07aa1d2d6bd4c0f1d2df31696f5b8be35526c58608c442c6b08cf12fcfdfc304a8fb665ac049b07da4ea8e3b231dfb6c0be719acf0539bfd3032f5b45d866a8f978fa356262aa9efa10517b3ad26f1e86d4287fbd14193038a2330f141326d412315520aa9f3bc229f088eb990c22d09d6762c56fd13ee8e033e4e1db7c3b3845ab7c64456dbe04bad27a520da5400124ae04fb99009edf79fe8f236c4f8f64ffa0c67c3fc562276c5e24d4fc8cebc5e4b11292ab8facb50dafc0f9a3dfdde6447c14b8775b1f63ed46836f769a172ef6b6eee066d328f68ffa78d802662c4870928e1d87643c721d29a4fcc9d27e76ea5bf930482c09d577b8caa485b83252f7e22efea150af8a1986849b6f406d2611e092e11663a03bf1502772457b39607865d3c8414b3614e82d4a03184d83051a4db4f6f0b977c5e7343ba79c08da83a86c259b549e2205a67c059dca4d738bc82766ab9c8d04a659c8b2fed25333ee0e3d61d2382379df7536c2e72ee9a79803f86dc559469259cb8833790efae2515ba3cae401f2da91e4e9911ef51929dc80b15d4e3c703b3e9ffb0b50934404bdb31fad37f87f1d0bb2dc7f7946c214b0c499c3fc4773a1f9174a5632e5fc4734ce83a809735f044732e22bfca99123fcc2a8dfbcfd6d6f5f32103ce930a50c100de58e937d217503e81f6b5972702d53861d1ec1416f9fd83abd11a228e461025615e1da622eb2c198065b6ce5688f28c48acc25b65d1999fda983a860b308c00cc42893fa8d3ae9adbbd132d5efbe74c52ea6caedf45a061422900cca04fca24919368469c537839008ba95375f3e2de2cb4617cb6728f9611cd6e13a65dcc4484972ba07642b271dfb047f296c983cccb22a3006a23c0f130461e3974071c60ddbbb75a8db078e10f282b406fd5a629a1fb423a5eb80b65a513ac6cbb60ba59dde161c3e12acf455955a29638ea7435806bf9dcdcb9073014c5ce425065882796d8fea7058cc70b1afd029cec0def041935d6eefa60df08897a16b297f18fc7324894f923b93fafe5a7019aed62d483823311230ecda0aef5ab46a2139fc138449644e788dfc0253b7d1ff313f2df92dc4b82ff4c08ff36d47379fd86044ddc05a82a0e6f11566bed764ac356252d15a5aff943e52fec0b8a98891f2b81a18195d109bd3664196e740497a0a508d9d9c59955eeb879bee712a4954d25c00bd04f98a25cfe8a0aee8bb4e1e849fa84d8d0361d644679c73945258f96cff91e4c44dca82deb1bf45a1d7eae7ca05b7ec1322263d36478a6b411d795fe0bafcd52b4fccdd137cd5809c71514d2bfea641244ab539f9773883a1cb10505bcf4371fb0089fc81f909f8f98f16007ccb17d419d4c26cbb44854b80932d0bdf1fecc25e5460906fe0372f8e2b66ab5e98d55b07ae183ec2d803425b7d8c62f11fd04e586937b1628ad25713d9b6b0cfee4f7eef155a48f7fde6ba8b906a4ef5e53c1c6a5aeb385700bc45a536fc5f8ae5d5eed6bd653e81f56161adffe442d779fc4b16d039d2880a01693afdcd161e6f06f7b2ce9f3e070b1a0bfd0a28f9e56d01a2f309811968c08dd19e609f9c139e10a306b677e85a55f952cb9bdf5031d12f64682887a83ac67585168c41960e506311e214868e7d3e2f198f7740e026a50a55cfb71b54e2904002684c2afeece5ff79f0b03a6fcc759773af082f33252dc7363aaf54bfd34d5a34c911e41c1c8f594f02fbee561f96f67aa23562fb8d1c99890712aa1a033c2b187c51f9342126b75ba0765793a0f07944f031240bfeef0e1e209f46ffbd9d70f70c33bcd70c2d106d57c0f4d2c097b460287bc37e4d27424052d477c4ff93bd06de3c9c36fe75aec3a88441c9f75741de6fc88a6af0a47a7f17a50521924ad9d2a93be853330a55770e53526b0432269607c1fad82af1d2c8468a7a3f231cf745129ccb269611296261b1f123a15296445733c3ca43a20601aa17a42a7fe4ec329e8dcdbdfbc03bb95ae092dd3493e651bd505f56721e90bfdbb96c9b14ebb248283dfd2a14e3b1db8596696134867e4661ac920c608e6ee6c0b8216c322bf00a1097e0a4eed035199804ec130c262e0d3d8853f5e851663856b9f9062eabcfb4f1db55af481a58dee90630aade4cf836e9e4d6a2b4f1b36de494e04688aab4ce079cdbcbe315c74546ce0163dcfe94cef1a655e86f0bcb6ab7c99bd412f42a60ba2975b5faa1094cbcea2badfd0978fcdc61b38e8439ed7a32f4f8e47d554231a2cd3fdea54d0a6cf1f2c1b00a0c03a53aa7117b6626f115def912eefd7ebe8fc91a0936e6f930c460aa403744f3ee6a1796ffc72484518da7943663853f43616721a22b77113a48f3862cd692bdb214ea5e9eb3eb49f99008f59db3ea705dadcc1c1f9113f1878fd9f46f6bd01cebd23b83972ffa43bdb285fa5ee75315d401f6eca1343d023f0fc888d745fc8258be1e7cf7bf053694ba7b6d5e23b5d182ea920c813a9d0a05c2407e545cde1014ac14509c88717af79f6b6ce3275b3a644b5031d721a2a6562f067bf11deaaf35c90330187ed48dc2d8de80833e1638ec706307d9975a9348e857247c1f8151eb36f1f8d65dc46d381619e80f3ff6ad253badccbb0bd335a60a499580b23d3634c1478a21015801cf1f0423a44523da2111ca6ce25d94bc08c1f3a8a829fb5212d54ad9412fac7083a9fe86049fd794bbf2a7ec57f968807a2188d9cdd3b23a003909dceed0c69dc741a8dccbfc246d9378593c82f7afe610a7a87218f5892db1d3ca1f3b459156731c3ea7cac2cf7158cffe378e3b47d1c595bffab89a2730df357049d290a714bd1cbf9c830735152c521c6fdee2ccfa5e80e896c81e235426e41e9ae527113f549e508d32b8efda4dd00b326e278c428ad0d9f2375d3d7700b3334ddd046ea196e6e881267cce702d2a94eb6a7f915328bce2adc81a2fa9085a3e98f06b09df7ae6f8d7495f4854d983740c6168f88978323aef124b4b91383ce7351037a3361b5f5438bd59e13022f3229a76a193f78efb3fe73e63895116df44153802fe186a261f05322e4ad3e9c741addd8f1973cc2304cb147229b41e25a39c438c790230280ce4351fdddde13415368681911afc3323bd75487b47f8b0e576237f5e3f42629d5c896663d402a5ccba8bc848f886b8b6eab58528992aa50ba8dc198c2ac502fcb4cd79590f931932e5dcd3be810ea01057d2212d6b77f2337790837827a450e3737e12c26783e87707414598acfd4a45b02e424c81f8c282082a7536976574664ebca556a1dff0bcc4226624a225fdbe018f49aec36265dd933bcea917f785d38176671ae844fa218a4d12edc158bf0a558c7e11002cbf19814c82786c2dde069ed52649adba15c2efa32d84a69ebb51f672d15c0fe2cb78f90c5e2efe7cf8f3472fbf629def3cda2f81d4fe1ce4c813f67818c028c2d7708d3bced0b04f311bb8ecf419d1b1cb8a611e4af172428358c22e79190c0b7db305c594cb94d8e2b5d731a6a42dfbddc764275641bb91a8cf5719987a0d0aa1ad86868a96b92c2f231bf1c17f2a379664fa43f6218e7ca4096ba74559fecc0f0ab721b728455ec2bc976e751a527f091004f9528769d449248e6e310504917d1e1c991685a4abc0cffc9f91aa53ef5e64f435cebe1194a730f5a0792012fece49225c2c897da9824e18f526a0d34e53c402714e49808bb0ab0febe0d4d665f44527a84f7d5c0c5972b488102a02080a7ed4735cc50d8edd1156320ba2163b8841df0ba79c33c0ad0aae5a3f0921fd136544eda24823e2f6d4f0edc0013f65c6e93234cc6408ddb91942e0cf332cb79bb3a024f1591a302865b82de7d96c070ab1189c5876138991b2595c706217b3ef495c7d5625b4dc88813d5d6d9b97dcdc4d61567299fb0bf5f5acefb8d4c095e5134368b7b32e7d966144d0a8e9ef133bae11f50c3f59fa936a90e4aa788b6944f74f9527f673c08815c586d7eb08424ea3a95ec1e4ab5cc1caf12c54df2856565420568009d3e36b2ea3d38374b6f22cc2fa859a20f1b08aaaf729008412b195d558f08fa6b3ac5e92f115fdac746e7365d08b3da9f9e6abce1f02b5fa10fe70a8243b33cd6653d96586d430e7a171ce1c8a2e6e080ffc1c09c4e4baf5405889ca78e125364ca968154f29228bd162b2ad2b701cd22ca987d362ee142900ff6c06cc53077f1a20ad4b7471cabf37a4fb9e8fbb726fc91a37c2cd063d7213f812d12b817d6055504f5c230127862c5951e91cb303192501c33fc973dc056dbee1581e107c8cee477cae73baf6b3a5ead10b7a6056b81e34580593c8fa6aca611f685942c433781e837210caed7a39eaf58c7984488ae146b163ab45c93531bb3bab3528aad3522b7d909b381c22c2dbd98a0f0e329791b38f0c532dd837829e88b232e825b7778e83702b4a3317f850568ac19ff2ac6e18e644e4b904836675a3054dfcc27f2ad56904832eccedc2288de8af10d8d111d6ff401b99016a34bd2b7c498c45e3777e0c1372c13772921134784899288a52d38b5a14dd89bffc346c5636bc21da37797e63a701cd5858439c4b6f2eba1722ac8becdc609da866494b5003a12527994ccd30526373eed42908dca2f27091ce50d71e57b15da1ce7f7c52784d292487ecc452d04d50ebf071c6f27cf458fb4f00888371be3e09d07361725d9a345ed02c5e62031dd0695b08e11e442882218a83e1e5c09ab13e6fd7750f312264a2c001ccf9f40991bb4c27399a182e0601fe15b6f39f01e8b050661703336bb9199e2288b0551f95009764d3f5481a634cf69200a0a0abf80699b567b98181b1af93927ce9de7729f5c9cc02ca60bec3cfef563985b7b150e07d611a20af24459b00acad5a7f8fecd0da01a0083c9eea06d2322d0fe350238856d1e711bf517f98c90f6f561faea8f155bc7881c7b9aab6ba96e2bac8157ac35135c634cb2ad4b7a4049c90e796a65f15dc2091a46d2d1f404e8a622d6fb92542ca48abc670c840a0584d2aa1ff0e7151f002539755c2c42c531b09b2eac82d1604764c682b5bfa7184a2b02ddec954580c8ff326264bf53cb7293187dabdc545fc4f0683500988d825a212b4a202d8435c2974fcaa47b28d2c4208a5e094e18722e961647d1725dd25b91cb83ea2fe823a2bc042e2b345d9b171e24f6a08cc5bac2674c04a54284c37063a782db543fbae3d8025400672cbd352f75b8df4e38b3ad38b2a7a24196e1699dcf896fd8519daf5abeffd2657808e34f577149891d3008ea8986abbaa6803b04efba7c391912cafd65b3366603c5e1308164784c0f2adc5898eb88cb1c90b04a07d755362101a90724082912212f645514ae574938bbdcc896d78fe829c8293d14fac4d3bca7632b626375842b431b14d7009f176ed4c0ddd305b0d62d6607cf39562db10ea3d4536cc276eb6dd18fb3eb0024a1bc36f619a84b3b18c26d0a0d8ca310196ab2d7369b297923ecad96e65cbb4cfce2d904386343138e959f95e634e8c566ed8d6d9d747bdfc707a2e009d3bb8df0a83e332ae1819fd8db20d9b41ef0ce00c0c4dd201e7d7a553d1ee73fae2841aaa59dc96d8a89772c06459415f4292efc7075fda0b2fff28803d43f40610997412ea2b1171b436cc9abea33c903a57ffc052074064ea6a74aca5399f64a88eb31144be2089ef1a294e978d4986e874929cda0f8006697acc83034e19cb7136d8432a34f19ab64534d3990f183f764d6f9c0aca3f121cde876a6b196e4584f143e7cacd14fddadbd173125a6f39013ad9daa5dbcb205a10371738e4a29099efb30fb514bf39ad3f89eb6d580929419e5f194c9c6839db30da9f1fb5b249b807b75bb5acaf03660cd71e1916158452dad6a0754ed57cd326b061f16179fb5076574ade8b3b470f752e3f06618170b363f3e5a13cf74378a70779a83b066a0f16c59a2e29fc218b88133ec69c2d949c5fc6ee1c5fbc4d6d29fbe8caf755f0a77f3e1e78e1be4990ec0efa3bc7dc71517b37021fa6ac86809dd1ba05e79fcdd1bccb1bd965c7d08a1be948b94e17f305db20bf2fbfe7101592a9652fd928f9a01e0d8c7c32befc2323757d1fa42ee320c1183880461561c55096f69e90b7c03439db621d8aef530193ea7e756c80feceaa7c1ba989097f021ed1e72a062e981f2d4c19e376b0bf9c5bed5b3c7a0be6f4ecb18390f9db0cf02cd1471e1c32d37e56d4554356d6400f33df18e5891f7f743977de3299f8c58473f822df026648146270ab7bd92fe5023e0851b5a32c12f7b68ea76a7876d68dac8ff33239c21b38ab4f1621b2f51ec19c9f38385617655a4ea9bf2bb1f1bb5b2c821f2a5e946715c0752faea09bda0b77c38d040bb16b0586a6851eefac5c5518cb9d2688d21a343cd0f5e557ba5b3ad8d27e68eef7b31d9c75647c53619391a93e2c595b9651d9bd2e5ed4c09afcffdf1086aca4c63c73142ab08c921d650379e479338d93632dba6d9d464a103a20ef8d023e26196ff0644b02b0dd3ea03aa505f87af1a0accf33a9b0cfd4db4814000ac38c971122a016ef5666b373ee75919d48fe5dc2415ea42fceab2b695f1651da0847b40baa0037c4cf715c336123022402ea28db064b0d6e165182de343035df7c844f62ca3a64fe9e5181160937329b0a3bfefc58511ecf70541f2d78053d13c77c8f9b06a9d1ce9dda69204414bf42a504e2d343028a14f3b03f7148c076109b55f78f0b2b811fbf13b8bca02388822b0c2303a73d1434eb8f26905e8da38f0a5922fde395f1506563b2c2d02e4fb3bbad76113a46bdd0d164dbc06a096b6c035026db4702327ba47de9104d6c18706216e6ce898abf75f42c32bddbd12271f091f17c377793cfa89a9056cf61374233e72913a67c2ac4e8e373e01c299ca450f8b4eebc871eb6962428e58280d7339348d4c9e1f6fd09b6cb6a0217f7bb3274a20bb855cc4cd874de91541b72d8828acb6460b9fd6c0c0c975756e88d47ae1bf3d82df99989c011c2a432a2d7cec01f9e248e620f98d2b28b895914d4f23de5da09a035e26209811677e1081f43865085a1e14e429569d83409745085925bee0808c032dc79da7a5b0d95560eae8745d1e9dd0fae732fdb1132cd004af2968154ec7841ce98b5be88cfbd0e3cd700ceb40795b9d0c19baf77cd7a331c9326ed77ada491aa57e8a63069c039603b7c0498aaa2b38e3758fdf6247e551e744903c97d095d4fa3d9545d467326861b63fb0a0729dca9fd6f88fc35f617919657cc12125920812a8a23707a8716613e60fdf59c46768e99afa9a6da58067f8469a3f8a1a6dfcaee2b14d0916bc219712903c75f4c6b965ba519d7168886582ccbf58835f75b1455f301aadcceff582e77ce8a2b82e90b38e4eab4e3d21691c5e032c59b3af0401627f5788b50e654959548109d48643de4f514b7c1e93ab44d1746fa4b5afb3e9c2ee41636f894aca81c94991b6f27445b41643ee6bb4ab9825f76783f21d45aa7314922126967f12ce2c9a829024b3a2c91aa3372e4fe0d8738cdc441ea77c1425fb0af658c0b8648388df481922fbd18df8308f0f2d89d2c1150e012926da783e5f37256e9090da64b71e909b395859f706d6b4a23ea97e28e3a8fb4d847823412c734391a1aaa217b3148226798ef1708f6ec9ca5f96431798f5db1c38cc0be4fe8add17f231fb37ca188bb62627285c8871bf199121a0374a6bbea912b0c3525a38ae72d1452e3bb3fd45efc13442cf220e43406f6e984a97636f567960137d288c1ee56476579842d6df4d00a02360a874bac5501cbb69d1e31cbc8d518c35c0eff8de2005d26afe64a81860d0812795a02a77504c04a2acbb34b65bc913746c8c75806bfc7339c893a3a1a5363bfa567e1ef621469ec044c2bcfd0137e49660c663263d2862cdc6f281dc3419d06c905c5b52dc130d6dcbc95f57029e5266f6dbcdf30db08fca995c504d4853ae43987d68571971841fe389c1fec94906420708d84449fb045f9a8e3d5a58fbf242395b2a319b2c928bacd990780a66f91060ada6e678fad2099dd65cb0a4e11f3bad420ee753ca0de6fc7556ec6acba30d40cc2ffa7c23d0b41d401fdde0515b8a1d1ae01d9bcc3544ae32785800df5f706bccb63a9040640d19660a70e4a3f27f41eebc727fddf1c2fcd5a3b5b957932c7f12f2d635d52b2f5d3f464067072edf6506807d580e42ebe23c276d00fd99147037282a5b4a064e1fa225b790cf5dd5e651a5b9f1a1ca66ebebff8f502da4f4883da1d6b004edfb0d04fc700a16678dec53b0751025280b77264de60f8404a4003c5a6c46abd5e7fdc992ec70014596769bbcc3f0e2017151d169951d506691472e9a00617940a9bfffd255a06f36401b54477cce40429d0cd75b0b2ea9429b4415e6aa6d89ef56626af902da3e9ebbbc9963d1f348543907df82ac307a698aeedab746cc292216a5741df8e283dade90b443a4371978105cbdf9d2292f6ad695083f9de6472bca4b07c300359579bc3adeb80370db3ba63060665258036aeab1996c2f9d1d43a56fd35bed1b97cb26cca48f150c69c9f26109de8654eeb84d7e91dfd58f983fa13a998135e150b19847b35545171a7f68db1351597868ecf4025a7a0aa5280b907e3cf07f07d01bc48123877e50c0fc4cfe847dce0735f0182e60ada9be06f46e9c6add7328734a5c51258914f2960a3c06a211815fed62e757389e76090cabc41fd17fd11709b05a9cc82ed53191bf21e428e02eb97b42ce2f1eee17e34ccc19a3d0175744ea41f6246b6d53bdec4aba52c8776c787b19a97e2304e8d939829c4ccb2b86fa8c9b30b9ca4517ddcfacfa11422adc975657764d1eae9f67c848b312a7da86242583fd1441fc8a48e7a95495a72e3de8f60b2c0108c1cfb40c6636328d21fa372ccdb53c730b93da1c6c56ea0fa586c77c07a5af50d03e192b6c9eb7b56dfad42e66065642a76e56eddfc2b4beecf964a1fc20897bd7d2815b7af682fcad08e5eaad68267d84b2b6efd9cc87c095a54220cb1acee42a920360a7a913ac5a59e8871699ee61cb1d1c815f1f312ac9840ffba2c3756c2b06150e54421c008ca5445fff8378495160644d82c85e00b704bde8f0b6aa11cfbc0ded259f2289684cb8a930818b2bc37ab0878f0574188c6526577161412705b6621dca599bebdf40cad6e3bc4956f3cde1f935ee5acdb5f80ff78666032e5846046200e7b66848617b9fe94c9c3bb7cb99b13d9df3a6dd33a1fc3f18b060f3964e4f64dab5e5b979810d851b15eb448e46c5b6088360316d3415bdc9d9a80687c8c53e0864c5e44c725d1162c52e2187cd0554d3b820b918c3642a548ccd22f1460f55afe791b1c4b2bd0615e56ed7d1eed834a36e1de8dadbd63e7d62110fd4f15d8f454ebc31b2bc1703e3f90bfda3af7f93f93ed71e9906a386838493669fa19271f665ea89883dc96835b9d4af59e67db7631c41b5ab55f55accedad0c97e1be92f2b3119e469d039dc98aa2b077ac1c3b910868c2448d48928b66e3a73078d9bf7cd407c4203627ac599c294eb2cdcdba6f29faa11e4406dde5932e69e323ba1fec3667fca7601336c41669be42ef1d04b32073c17ce4e379feb635d5e416a4f0f01cc934dc26f199506bdf5507d5f919eb6e6c9f1c4f679c9aeae7aff8c44862bdf9eab73637a54d8b474aa3db84692d29b3c596e73746e19752d53f4f90cfbc1458b5edbf0f1f398c22016692bf1263ea9f79f0e15ce7734d03468559ba798c391822a0cb0a77d09f440fa5056a7d0508fa170f4015afe32226cee9105c68e0f925bd27a444069118ce947b5a07aeb577f7ce3f0193fdb4a4390b9fdd098f285406018d9e011e60e065e460a37ed460e408ae8573847d8dce9e97d11c0a9375d1482cc1529fabb003bceb063697c07256c9432df4f638644e3136e80abbfdf42d8c6c96ed33f640a79438c18f17454f3f51fe908830287e9b90dec8137ed25d0b7ec098d4f6b33229455707a30a1191401ceed7d44edf7264f820b7c91e67adf6478c1b5ddba1cd27d88c17de1fd67ddf3ad086e60392927f6b9499413a59a6a4a95d0071e0beb6294fe7e7f7b3faf797e5961e2adc6aac24fa42855cdb4fb6cb6383469a1d9b5b0fb95fe1caf09dd6a3dbdede4c0143337bf5ec2c60ad8958dcf82544eeb5932ec238a01604047dbe649021e6dc7b34ced91e04300299a6ada995ec654cb3a933549eafd0f09cbf28f40db7118928c03e806845f32dcffcfd094032d44cd4e6b810dac905258594399c115d10ba401e8c179366b35e7db316f6625fbe7faec0fb15e7c653291e7c1a0afdb2fd62e6f1b70a35cece9307ab268cf00f46c473ea1cc94fb95f771a3fa75cf03ce2d56a90e1298feaea25bd7f41509887ebb449e6def95327d6db66beb3b74bc3e8606dfefcf6dd34f09dff7d8f3e72e52ee791bdf5e7cdff7cd804d5a70bd5f8b04f048067a9f05ac5688afb47f8890a3a16e3d79dffea05e6b0bb0c0cff1ae1fbffbb9906b839f77c1372848809d0d60ff0eb109cb04031b867ed81344399d72d071f47a585ce6752cf52400403af8713d2e8461029da043abf0779490eb545595c11008d25505e6a22140b31a3628deabd8c78f39fd0297152aa7e0a1609e28ba20f659c73b71e99d15a4a503be3f4e97e7e1681483861d811ce29158d67665299a2544e8b9e96191789afde0e38f6431c7c3f46d0487366cd24a7843c7f2a0a090412e4abe391e3b36a4770b66568a12624f0565adf24f085f26fb9f994822878a388c2cc5490f9d52aaaefd675bf314ff93b7e51fea85e2cdaccf0c531029d036722adb03d798aaf737615b75e1a9be9ca2c6327ffa403ff2992420d979ba79306a91a923dfd52a8c145da12c35204104c6e556451146fb93cd32ee9482b57b8d08b08dda237dfc064052a1844faf375890b60c09cfbb2e508c479c5c5cbede9519aa21156b167301d6836df56c4f8806ee80289b3daf6d8acf7d200bdde239a58b8cf256e9149783b7b8612db6b8b39d32234c2fd7d1fa84418627b6ae4cec8cfa70e183afb55b0f2be06543e3b7b1c5a41b0a560c11980e3ddb7acb7299e164c0b72b6f74a43ecdf7696c1bf816e6c1e1f545c4a1b11c6aa67ec6a0557fafd0e43ee150bffcab0aeb2df17e380735c25c344bff74c8806eb556c75282da7fae0c30b62babddcc51684352b1136ec8ef5ecd69e21260d159186b83da81dccccf42d96bf0f1a42dfe48c6632319449217a832fec973b4b368c42f5e4b6a7bc703203b0372df104f6fba38769ac0d736535f89765affbd3c5a3e6a5eec30f99bd89dbf10a305985e58f59725c00b88f8af108548fe7411eb58339fa2365ffaef703b5f47e8bd2c4b336c4c0601b8e790d4218b1079dcace49a395efd4c557704176a3f86b2cb8ff4029787a8f61c641d35b9d9e42073f3a24362969458f481f69f9fa22b9c884017850d6d49a5cc5cd817df18a2ca7aaf38e7e793901c9e265c4b5813fdfff973ecd3049b89c37535ad1848e74a2368934e7206b778c5688acfb73c35a9f3942044b4e3211eb2bcf2b376b719f69de60ca7868b478b7c56d3daaaa1b24294db5917be0413f6ef07609e2127d780cc095b90bb34bdc7eef6009405d41889f179454554836455a112b0e739fd660c79a9751feb57479f8323405485e6bc8ba34ad147d26cc44baa501af848ccba3ad6dc56ee2ab02aaa93df6a1588e61db6d5c2b628835deba255c1a9842af465f0863f7469cb3138caad750918a4ce96d5b68a9659540dcfc573e02500e8382bd444d09a292eb4754d8debc7912c348b66d652a268b313842e7b1049ea658143f003f08d4ae5ed0335c1afa1a13fae8f583914fcaebb2b97a431022967720c2e740bf003194ada123638c80883f4d7f4394d07e3367ad1b8add6b115351cdb44639c706dfa54d097c96e6200bde14cba3c15ae5c8207b65cf28d096c379f841762275808a8f5c2d42ac6e12e13dce197811d5dc208e34a6fc42cb80ed6f818e315a3caff03eff94dba9cc7b5721d968452df743355790ea95df3c4749c071ec3f1dd39cb0c72e7c29f2e9b7f7714fd605a024c57363796c83f36cd40bf392b4e64b4a084e6023a9b62de160fe9e21a779167762d6d4acd84e095684974a8e23eee655604bd1b8e36b73a304ec36feca210f233710c84e7950c0c97924b6623391e4825a7c786c0d0000fbece2f11e3ad05378b551e172b9771899096a8686fe0e28a423c3870612fd4feecb5e35e1e84faa9d6765c78a219c4ce3264dfb1c486856e627c544957e3a7b76adb8a2650e70ab10e631e102258a92d5b3b0149c3b34d0353b99df2b783f156cc9bfe62b5026c28255ca3a2e4504ea0d5afcbb62d6e6846247b51c61343a9996386df28a149867a2f67078c9c12312485f71e8f73d33ad81128193b41bb7568c045c713d2da3264ba43a436a29e27f359df1baa4cb327b3cdec952ebd97a3448ff482e63c738e9488551336e70300f7b4af198cfb78a20e03a8cff7ca79d0b6bb0f942edd76a63d33ccd9b0c9c99669c76e837073bc34289a45fd2da77d35fcf642630d61e51d4eb7d996f15aa95809108ce22c2da5e2733e45cfb48e1a6f340d6cbcacafcece001c29dad36f546e6deb99994870ba4c33331262a6aff1e53cbf6af263a6ea363edddf140cacdc0a4fb48ed16a8c0a189e51576e9dbf7c1ba39f9c0c2f95f5074ac27f3527439de162dfb7cc476cd78cd1fcca2a3fb86c047c4c1760e927a36a36964f7c495244b175f24c687589438b456a221e1a44d17b82691b640c4897010e8ad17af9499533a4b5027f0d4493a849d78a2c2a23a6498b6e32070313a74128dd7866080d42181edcfdfb2357a508c430e0ff4e8406c0b40db15cb1e9163e02a01597a1385dc157de44a2d0bea3e87e1248233b10320c5c9e50985bc95a9064a171fa526beb84700bf636586028de38bfdc6e7253e216250d12a3f29dc4b1ae9961bb5224597a5d83660ec997fdff5e696f8c954e0c55c430339d092ebd59b00a73eab89813ad12dba834d526069644a4fa9d1dc61bd160a9d23d884b37e702eda2ba5c916ce614d6a3dc5b88fb08cb6cb9dc4979e62e571ab84361376290d8a29cc41c558b294a152344948579e7b71685e35cb72f8d0797329414061fffc04c1f00b0d0d469a593da077107ff175b477ca5fa164d74b935f72aab2082616a1353a961055c7a68acfe3b117fd5c97dd397a1cef31b7c3a293a29939b0ed74eecefa16c59664f2c6b1ca14687161a1ac6b53fd020bad86016c499f391dfc40ffc4475d63be7013f196509ba3b8757f9fd844a3eba245e8de51c77a4b3c04ce4d241d771661b9155fb3882ab135901a712909b22161358c532ed14eef9e411907cced8e099f44e333d053d72323aecec0db39822b1e173ae9f940932dc2131a4092db728d6e1b8cb3b3a25aa3a8299e0e06a6a5d4716277ccf58df23d98d85e9a1baaf4d20a45355a9bfd7d8408127b75130fb096bd8ffe19d58a6a8cb83e5cd411ebec32dbdcd990c6ec53e8c3fbe10e41ce1a6671ec23a1fed705506a33c7f10ee40a43868ae1d20c0f2dc09de2371dc6368afeb05fa2b894e4dfc23b38bffbc7aefb3a34e9d99ff39f7612dd93e80b4da01c8f2e3cd5cb52018ee3b0621f3c319ec37738f4890a95e20fc755e4dd50e8beb60d2f9f86f034d9f5982c4a01587aad68cfaab1128581e2a761ad422ca082540fc27681bd7292ab01cee7da1e8f7053c9fcf039595f4258513522d7642593d90a38ed8bcc7464f0d4a7e16f01322e881c6590d5ad8b76fdda82e64ec283770f0707a75032d7a14c74dacb0dabbdde355c8213fbdce4007966277ec7bbf38c431219c1dcfffc92f09cdd486c0d8125ae4842f77a972287f3bbe4585819f097f8bc92700fc64c23ca615bcffce620ced129c223eb4510d5e43cf5d8f3d09fe968da43403b319b883c2ec8fe71810e47b4122a35353cad42c3c107f972491806c50dc3b405b949622af078dc7e598891764e07f6631e020577c41375c5c23fd01eb67ae7f83487692d0e31a9d411714ef99b839fe078f33e8c5659434c7a1f8d8b59132d63ec93d8a935edac8fc9088f957fdd901ca5ac30e1fc46484563c12f98ad6ea85ba2ce9e242ef4fdebe535e698f9fc3309f23701edb7e2df8868a324484dd9efeb42a27a118c41f79a745884199ff432b0f62f0b4d0eb1920066d663ca6e238c100e6953f3857b90a55acd02b0956de3162e649b240d01bee2280464cf8b7c5dcf4e356f7320e8786628cd73d863a23162dab36131c2c8c6419883d1f04a1a445d3026a183762423a6057459484168c113bbd27bb92814e050ffc55f2fbac00916da6474c647d8d075a02a679eb5a99c3d9e1b4b493bb363fae6183d016107565d6e7135e191b582894ef2f70e19df444d6bc4343c389da27ac72c80ec66f8cef0931b5c81e7119b03fa6ec692d126fc24c97a6ca97e0b95dabd42d41cc3922bc3dd7437fd55a2d6501034a65181319b78af61192c1611704ec1018915a23027bc018bf4b20bbd4c8f445c40aadd926fb5e85f46beb003bd51e9649623b3d54776ff5215a2ae974b66e6f1ab2af3068480bc075d6d0683742e1cc3aa0f5c4df5dd0adc9be78232f25bf5d56574145adb0d942e1c6ae52776f270797c33f65dbb5b7044e1b86585e5361991fbb27a409c5502be941f2d1306de3ea69cead92d6c026fc6cf9c9b194098db4a273c6e4f8973a7065e137e38244ea348da3f67195eb86b570541add84510259dd8fccb4635ff5b450ee64739bbcd057cb1a23c3619086477c38d5ab178c20004acd416a7d93b0f449619a96a3896e79f586716c37221c5012826dc6ce821a26aa096b45bc95aa5ad17942304ed323cef6ea71d254e9bbb378966eef23e1f9403ac99b835fea3e15b23be21c8c19b5cafc0a557db9b2c1efa9974120f34a995e7d0d62e41122b68521868d912dc8186849ae6a0a8173bf716f6fea4e22b567a501328442e078fcc64e99d21d56a6133898e9dc1e28534318c8046b7c6147f31aee2d4da7d45607a705240d4674052e54a4e0c610f2786ab86070605c7004ce4374a57904e3d31c115c165820947eeaacfc735b6dc4040e1a6e6de66311091683ab09078b50a96b0c290f1214cac1ff6d3aff0a4db233032e759b7857b7d99b04298291ee0499b47a4463740ee5767e7090935830d3b9702f1b710090bc0b6b7b017838c74a6aa2582c8aeae6808cbaf041c4e613592f938f1d34df5e212f1031171f1f6793993da248deac0f6f7b7841d01c44d9e2dfab611f78248af6e2872967772b56c7a7cdf1b247f26c9dc998e8d40bfce8f6aace2d45c14ebd8a0ecc887cb433764bea80c73722f45ad3089d688884e6a0f990e2d12149b79bf5c25c2483155a166d7becd884c39b0d3d7ba1dc40305e50d45e913a00153992e5d2c727c6ca95afa884178f732485694582ae3842c253784c102cdadf0d3cd258d80aa4d6bd653be10fac7223df09ed0282d440ff808f9fa4c32464bb610fe887a8d1e4fe662e3a277e1e61a0ad3417b5ac6f4e8d9333152801a06b46d0391ae072baefd8a0ed33f4e5ee4a8ca41426a61ebfaf26072d08c2c60dd8fe83a4725e996519014b8266ee1059383876c4a7ffd3468f509f3e82b1672cb392617f0be3fcd0b158fd443d9e3322b4737c8e48811b6fc37bce299b2154c0f0f0258efa89fbe79b1cbc0465bd5cd4d20baaa07307cae3234d281ac9b09d8c105a8465963861818d280b6a9d1e502e31eca8ae30c427627eb99bf7bde688dadb5e284552c6c96241406161b406022956887b4f6015a60333716fcb4a77d1685c7d32c08cb9b221f5a147a462d096eeaaddbbd08a2f083c357a112d93886e81b38966e769216200ae07add5661edc9ad29e709e1d6fbdd08f5b5d68d2f6e64a5a6f59121b5237c63284792114eeee781356f6d04736be565a8534bc5942dcc8d6a8515883944a950fb18953c74ee4dd0f31cfcf16845ab49ffa715d6960601779c689d2ee24f80060b8b4301eeb766fdba0d5ca22ca6afc811ce2f70fa6e0b337008ad13a1400f47d4aa1698bc0de4a99580dae2098441ac60082940c33a205ec94b6b80390162618aff5b7d553a7ec60b9437c092ba57852a18e45defd962f016726b1226ebca09a3c622f368707eac820438a73981a745f2049ce4c332edce2a44860235016477893def48defc56fa6f245417cc55b168b41d6d1eba62f9a79e889de4a9420c1aa8bbfb628c238bd3749bf26fa52348d70d7daa9f56a9a1bf21af9da5625693ae1e4554e8d72314de0c381b742d750ed03411d81382ea0bb02c073e1b0a4c130838b30e5b3d58efd12e318bccb8df8237c6401f3073b0967c790e9982bfea422966b03e09fe9fb22ec238e947fa8c2c4c5e065e51bf09107f9eaf53034faccae4681df94c5ff00249f40b63ee18fe1fd1d1420d82a2943853a2a6f79df284b49f1381ec534248885ce8b15f8e5e30393aba82f584d9f9250ec36a28b1792fe276c0d0ae483d7dbbcb99e01185d48963c02081a59547caa3c6cdb5d4f40e56288ca46435ece23f37ab9b46193c8d2db37b0276ddc68a0c9b8d2d4d7912f2453c7de775322a4f6a50388c3c650749a4c775b489612d00677261afad697dd1a809ec6f58df1610f1e654d90c99480896a014b720e1b9454da8a5ebf0ccb3c699902aef67094c3f598516cb4a6749969bc857ee6b2890a1a98faafcaa3d6fa5ce3b36fc3450f8b4c6dfe05e0857c34ed043fac807b42cd86ca2622fa44b665245da40350040ce721db6c9832de2d3767a290571a2b5d9138853d1d75056755cf8b35c33e4b91b9f19009d8715e78a183906141c07ea26f9e3aaa0153dbfa5a394d693cf5ccbef4761c6ee12b1c06352fdabc4253c34a2d5ecb2cb9c2182567945f791114af992e4f88ef4f3def31b4736b42e423dc9f99126cb391a00c9a033df3847f87f5d36680afe00aa7dc8ee9bad28987b02b07e89186346b9077b70c3d43d30a0382dd3e13e0df963a75b5dd291175b01dd25f374f5b51c83a071649d2b97485a81aa4bfea678801f2ea5afbd5a551b4f0f6a2e96f826581c85f3e9f418aa2670717b15617247caca81bf4f218488873d62d8188a5791b2485a2dad26902ebebd46e8d76ddb46f40ab5dbb39ba0b42ed116866f9729360a847730e8de31056ff3efaad458619088648dc2d23007b0bf80ec00495f214f1dd6291e1f00124b40ac71f8a42b6a4087ad9b734e0dd53202454b3fc242049e9ec17e162b7290e9cd668d6a0df6799600a15fb0da6367400bf2ddc9b7854246b4bbb11d85fdcfff118bf8021fdb090ad73c3bf4b17e5d3404e209ef4c8de68b22d3eab71ed59826e17ed9c6e080d4ffe9af05ed814ead42055e2835b11b9d2604fd9b5e22d9e9776595e58c2bc8929061c93ac89f25198503822438ba64e1bd676f5412d7e9042d152aa91ba1b16cd26232090234cdfc87f75903af8fe5785297048a0afe1d176c84f8d5a9fafe9f219c5446ceedea90411bf7c67b90508bf0ecc63b635e3ac8bce80463a30aaa9055bcc49149b59be574c385214be44a133924c6467a7b50011270af5e59a68548408e830bb22522c1ffc244e90a9942ea1132baf1dc2f81d69723d7bdcb0ae030d45a1ffae2e49b44523ed1366bc1adf2bfe8c1681e4177f990a4dcb6c1216e420dc6ae98b65f9cd9251f665658e38d732d0ddc6d66e4f79e5c8b0cfe6177805b9893f7d3956c6f736f24bcce66cdb1093bdb81bd059dd5a619a07dd0b1a6f0ee2c4d000b7610991887dc563f1a0c3e6a48936c461db5764c1fe15b5760459ff4bb97b40ec14a12c7f73a51a5643ead55ea80ed594bb420a98c2b3469c8b3b18e6f0e502bd30c819c5e9abfde7856bc549019022b008cee08e8fbbc30b56a08a6379a9c057a3fa39472b5ce793e28eaf0545d0184c0486ea2b9ff7cfb08e54ef7cb35a0dc24165579f8daf71058d867623b6e301e7cfae7792db952029e67749a4e57c570adf7993ef0430b92afa9f607fbedbe491d21378578d3a82c14a5aebc883bbdac0872395234a0c40d0b3017790adfca886180e24b3d8eeef64da18cc75ed4bff81193a51688e1db0040238141eb5425aa3a77e9b26a9b984dd7b4f59ca701843264a01fbd08a7197797c15572418574d7d956d6f27f0f5d4a55bcd8391088fbd888b5423b4cba81515684d00ec98e041f68028df9d8b5e24560f720211dba38c70624808ba8b4155d0d07836962ccb410ac5c36664f542a6409bd0220f681e72aba68fb4228a3ae8656c9b8276dc86040e00b3201d96b89741a6c424e18f919f408a66dc0230cbfcabbbb6da59c7fdb95cfad999413174e112e98d432a7fe8355df0e62c1be6f81c48e49b751c80a820245155073e540905c2eec03b887e8a1462cc33f5e242120ddd1368ef8d2640cfa186f3f4e8deaadac608c274a586088bc2931f681ada8b94772e08e00971858e72d86feb1218c6bb2c72979d0eefb13625b6922cb7dc3f35f227f361ea281fea09cb9a7fdfe160092858058c93defa94c95d8ce79c76a2c2286bdb7a56707c582ce40fb274e4485bc84a8088c091f0143f542e1c900f21edca4977e2bd5b4878d47ccee9cd5533427fd454d60a16877dc962f6281894455db796b2d89a8e9d0dd01021805ce05a16abd27a9ece9f7aa69d69980a8cbc1f95d628b36beb2d6cce5c6dc9cd320dd4e3ae5062c43b0e680e9b748e0067f4b2776173e829ef2e25f76a447e7c4dbd40e78f9b24d4f6d73d2e12cdb966cf94f42be2f15d9fdb23ae8324aa618891d26f0cb84545e7bb7209696f4d6d85a9d5d07b4b75d79b32d9e46f62cc711966ca935207ca1c4d17141faf3791811c2d1e6976656b94ec57e243693f7518cb94da552fd56436562a59457fad35c630541720482728e6bc804723b1fa2fe1222589ea16d3b459694010177fb58c89877f5308b5302f92218d3a1e6d3f1cbb91d8e74cc53360ad6320d2079ad499ee358da566e03c229ed95b8fd03b4ebe45d7938fe91d57da522c991668c6d9573a251524ae163f0eb7efba06ce39f17ea0747e39d164c1ea422a2c299ed145ec5bc3b4d3373df4059315ae9252348285eefaa0e9256f31d0fd2ccba7c5593604f6a1e54a1dbaa427d5b2888254c78de53205b305facf8134c9129dd8b731c7ceb1799d417c7ac2e54c77b2e557e50a1a5fa9ca53bc70c5cf915c9fb232ba6de51f6242ea9163687358dc9450dd1d232b121f4456d09d0044dc830a6d06592ad9985195f02fa6a3b627322647610732f51bade881f9e7bf08ffe7365384df76d7af0db836873d0bf1722e96ff958a5d0672b77991fac685092f33402424d253efc0432bb24db7f2905defeb7f50535d163a5167e2c44bdec69d89f0759d69410b4b4d34ddff78b2fd578fec2ebffbffdd2574737b17ba8a28517f700742a6bc7f29a98b436d315479111edad1ce27a8a6f3f7ab7f4191386af2abd1f0fb9b1a3b174e07657512782875d66f2e74f9702f615123dc2a2ab240ae824f73b1776d0bfa81e7761252a8309f303e8ddf70b3cb0fbf92b9a5998ae1877d4ccf8cf2323fc894479efe4cdcb57706ea57d8529143758685ea05e4f3ee80d81ac60f716f4f05039591aa3d5534cbc9b596095d2500f146586edf4dd8c46325e3ce9c6379caf15780359264e44ff1892a6cd94d1431fc38f3ac5d4d8d2892b8e34affa182620494e39d324ebccf04ca94977c8d1989570c58a1e90d16f287bb0755da980413f328a60092265ce8f703357862813355efae8225311e1e597321f9cb4c05aee6d33c2c1cbba70c1680459910b7f7a26a319da2b1270d87ac1b81061d50ac6ae45b8209b015372946af64e1c35afee469c523d261679e06d15a1b19541a69b9b11b848abac044a8ea3439e939370c525a2b425df1a80ce7e6cb6ea0bf3ee41b97225bea891f220ac4860b96da108a4649bae339b66b064b3cbf0292ee18ed2dde95c12889f2d8625bd6ec45966bd64e20bc5159634ec936d3cc1278841c22cde3c12c81411236e4a0478a61407ec4ca128410b0a4fa46ab90d7222e9c5ae991b625405fc6326e5b6d6bd8118bbe9c203eaa046c33c61220c2da434202f723b5d03422165b08533319b21727f60e80fe8aca322d38280477371e968bcea88983cbb38848968c2fd81a44774043bd852c84b34db90908975e0882a5c2521094e9179aa6e266fca01b7c90a99bc358dcf443744a6d5c54557a8b12545d70e485336cfd885251b5ada773bc3d0abc98397a25c72feb27f5c3b4eafaca7c10862b9c32085685003c6b5486e4cf95df7aa86d5f1343c2c1d4d54dd6b3ebb4e6a444804119278eb1520d093ae5c5515134b5df36ec392ded59db0c0d44547f748278d9160d521f9e3f0896426b1c7efb2920a0a60e23dcd83e84fb6e9efcb18450fd6f1eb4d63943a5e280a6aea954d1fc28fa4357b1ee8b18116867aa7347bbe0036fcd3f20ab78785fbe8052742253f4a7aa73ef1c10af1f6a1ba64fb612f068b244e1ce1bbc12cf0d868a10493aad47c20d3982eaa3c682d1031f29c2cfe2b4675e6efceab113c9fbfa016730585406dce641570a1ee511d04941fc98c833ca303d5eb93ed2b4c0b445125b459638cbb9fa2cf38273e3e24329ceb0767cc133ff22504890e00204bc8ff489096b4e783702f161a30595669b436b660f2c049f28b662189cfc57253f703e06d3901f7b3a4225a221cfca65ef09acc67744b0914025110d6b8a4644cf7a91c1e375927678be3b349df58c5bf8587062849cae471f0dfc5d247d7d58239ee2d17a3967321eaedc0e6d928733384c10f4e2b573d2443a0c2e24ac23f5de96908691efd77a92397fa0e4aca95f96e9f87d408b87c2ceb4262a281b9a430cd06c309c2ba5feb9b2151594498b0de5b28d8b091a58b0336b107c63e7cee058ecddedbde3a6a0bf99751004ef4364428a1f895b97b8fb40a47d23b3f8da6eb7f04093a0067756ae3c74056fc2714e6d2d0413c72dece251a64bf4b3ef103b67bc09c4ea564260f683f468954e41cbf95531cc4e46e5193c0818b821af77eca5b0a9b7c7c1d25b41a07fb8ee76811e3dd80212cebd8ab0a0059e31502108da5f55e2ed049cdc49d74b0dc1219fa85a269a9671750833441b35201203a06f2829e4771dc279b6859947e1298059188ef4057b061f6e2f7cc8fff29812f0f5ec49833286ddc44a9d9c48e86c1c53a0c3538db8d4c16689071d41aaebf2bfd23fb905c61cbbdba25d8377cdb58308d67ec48082be9ca30d47895f20bfb415891b52be294351199a365716323d5ca95000873f2b36fce18dc120d840799d1f4ea71ae20e35841a6d4923ffa305a14f1cacb4889036b7e706478334629cdf0d4eb31fbbf42521c822b9c85fc5201d5196bf8d9c5b06893c948f0f2a38479baf71fd41b928d6a3231928e89d448e5411b1a371b4cc872a90eb8074934253e7247843ad3681d0e91b8dc8d3ea8169c0b25b9575e5aceaf6f80f0a067f3b573aab9d9b696d5bfcdbb2013ec084b9a1c16b98224b5b887be9dd4525c802458a377810ff213df2116f84a51745b97061b8ba741262c45766645f453ae2ea62c323ced9704c65d14a6668468ddd2123737fede108000414cb193442d4285aad5366a46f5735cf14630b25a0d85699747c94f1f5bd8cde5512d6ec410bff5c49c40a8ff541d146d420742ebde8c2368f51b864b40791d3698014431aca6f8eefa59f2b869354ade572bfa3290ec6d2f6624fd27e89be1e57292f647de17f6c636e25bc38bbde23fb4eb183985be1102358fdd5a7303c4f7182b8a60c8883290af8273ef2c84c18339dafb2e3b455e422d70bf824b11f3538a4e929e85e347dd98aaf1741cc98d3a7493cdc8a05666085732c8a58ac84864606d92257ac962a384becc55fdb98792d2c6788b37d99c9abe574806089301f5a25fda8e60471afe61b50be757f532f858c53822da6b14f9ebd5640273bb3b0a541213c16c6c525f0c1cbd8c994fc14e5af2785b2831016f960c89f2f535bad650a5aaa0df7cc5b35380ac07242b003feb85a0b149eb7e1cc616071fde0e948259edc77c26e0afb30e129a52280a4260c3df5d806a03a28686d2ff843044cbd14003d512e53277f75ef29de7569548b37f425143f75d74546aac90080548f101fd7e5416d0a787d0c04dc441f0184485e48a65075f5071b74eb5fb9b0400991c0dd7d7c12f7a305ae54af83550919881211413303e3a84036e2cbaa7c979ed7c5116dc4897c9a090f2cab16afd11cd47ebda21e0f1cac3e7f50ccf5020eef302c96fb7ea4d42e0676838b03bb2bb87090e08393eeaf089dd952237cddabb898fdf2546c3fb5172244c55605ce92615905ceb7bc0c9c4468e6500bdbbf0366d581507096be15e7b72c5ef6b5fcd78b8f7f59fcf9ae7809f2c96e55e2ed4a5b2bd5869c19cabe2d2dad942621dd56707501739471fe86271c62e428ca79e0c98c8563c83e580f5b157d30a1571eeda7cadba089c80bfcb95ce2856d4813f848baec1941dd20290fbc8803936c290aaa2f56438462cff30d0d917dfa25e8a73c1d685f857ed3240b4aa29a975a55c4238b86ce0460c87f98cc88028c024c4626fb01c507f4440603d2e1b8385ad312c7b8fda3b2977e4d79e60fe8158104853737603f5400b8bb0e64fde59bf9a858a01328203f7971645f40358ddcc0152ee1ccd8fb2a7ddb8be5eb59c143dac089522e2a93974c9f0c69a35a1052b943a93cc0d336c2e2a1e86724f91835039c57696f9b4b58f3c88383cad8e9caf349bcd548a2cc574f3b48c787a0bea22547d9ecf5b8f83ce25ffaca94e57877dad729e01b6ded97d635a421f001c7ca7f0f72eb4bcb2b30f778c2bdd9b13203d4943909b0c18c6f0abcc6a1cd986e766d9ea4c8e57c3c7acdec90494ab79e662fa1ac15a73419ac57b3ae48daf8a04649f315db3b200f535d009ff1ad709e2932bcdf04ab925fbb514917e7d93c4d7fde22c62280ce0f21e10d9136087ea49c31877e169eb43c9eba8ce9a5176d2d528eb726fda602c4c30e07b0bb3e54e00f2f7e00fe7eea43042627b85c5a352d520acf3c320fef29159f7facdca47ea75bc82c1263fcde08553e3aeaa00aeb98fe4e81dedf80600ef0a23ef3eda924828f19c4c0c5877d58b4320b541a0b32c5b7cecc6138160c705b061e8d6af8f481aeac518eafc00091900310405f8b8a18fb46034c784b0ccf71dfe9292153336958556890f287d59cc22cacd71073147776815f8de615ea4b07126a47f94ba0399f7900fbc799ec7e709c5f355034861cb29dd29cfd36a6fa03039acb064e23c8445c64eb8070fcb0c5631658b685ded44605e80fadc594a4eadc15b198479733af5b9ff3b896f7fe4a731d9a090630df8f6940889e29f3534680d0ad9c101b90b2ecb6a7038c84c8ed8e26035f82c61aefa51d6b407229ef3b017dd1a72031cb176554f5100cd89e6cb69dec68fae5d651cea4de44ba2c3df673c22a6a4baa3861cbc9e2acee22c31f789a4a1c2492e5f3af498714016f9e6a946060aa02762619725cffeadd1f84cf0ea52be70c09315782df9d30065744cac6294d4e18c5cc7cdb09cac949d4637dba8f51aa90bba9a8a12c7f64a6b4f44a82d0ed60a2446135ba44030b7397e061d8588b6d45cce1be33730e3aa87918b55171c2609c22372868a2c05e460143bc00e45d58233de5aabd554a300577e7aff90b94b919c07fc91a2d937e7ffc539e168926a2fead1c693e34f6cc18d1d4df693f3e2eb2af7362b5c4f2da61c8a4238cd14e9be09da8b6c9b5a726e687017f3c96584abb79dd97fb1e7d3fcd1c32fe6418e4f211ec432608d2e38b3b8ec3f192ad02ec99995f81afc8c5f818730fb2e4c783b9109649de3e9f79bed41bec50895d4e56e1e2fbe717555b668a827c8637113c353bfe1c62ef791904f78dc710be83fd068774c4d5d5bf617c861d2a8759ca443f09820351eb7304598d1321ab3f18327cba7de5d9519dae4ce814a23d91d5b5a856ae70614ecc8d2fd73de127976be8f92be8fdf45acf1719402f5c4eb7811094ae196a5d786de09f00b23c6a72b5dd6c4027b866b246893bb208f6cc26d7be1928c36a199a4cfd3b157ddef32b4ad5bc2f9e26f44ae43f73edb79f556706e51016645b68eab7c800972e3394ce7377db9633258b82fdf336c8bd3a88d22044d8ad623e2ca6891e1a156fe8dfbaebe7158690f7a046411e8cda03cef95c4fed5e08ab5c2b74d9e4161343a8a2d8ebc26f184b70e5b68d532fbdef8cb83cbfd1f1e5d5cbace98c801323e4cd40e7e63c3de1d600ff02a1bc50cb9560e6d79669f1a8a58f6dd61498681ffed2f9bd9d87573a8d3397dc68df5296973674f5715cf7657e0c9bf5fe9b3e9843406d2d01d5d45afb7a9b2e779c862ae929444b4d35331089b608c17d3acf02b3bcecd81e1a3acc43b3f75b27115bbb2b1f4c6ee84c23e72f05870d7bb74bbfa53cf338c2699292bb33d574e3225489852e235489c52ed1d99ebbf8fb0b59c7645629e74f7a942b19b6ecf40ea20b2bd3303fe48a78ace9683a68d54d4807f6c59a2ae64b11a18ce6d7e9d54b5b01044fd2e32bdfd7e292d75ed8c3e70b97927d03ceb0aafe531f2c2b35655be2508ea60976fe9f137cf4592d3faae62f9a7c94257b7a3e786fb8b5704ca96ea34205f664db4cdab859f14647ce14219422a26ed3a287879234492b275fe629b72c895e38b081cad431a06105226b06e36b17e42d485baed431d2412c3616114c0e7d2a71515f6fb0f49b44ed89358557fd16f974cff249510476744bac49014b579eb57451a8aa181b7c25d597612629be1320879756139ef98ecc17ef896060a46f7ce19d322bcfceb78de3666615ed7c9d123c6ed47f188b4402c256f7c311a40417bf665bfc5fe82df2070678b2ac008c970dd2b112c10413d3a67456c76c24d6e18c5c390a53fea5872f137deb12c2eb303f93f0c3fe3de46c4228aa119ec9b82e498c5b2d6f30c3112dab301e2b8157c4f84712406f5eb5d14624ed006f3a9e940899930b7de24a0e9e419b7dadcd79fa677d3f64a0feb7e058521fe36d86164551307c045162727a5f6923df40246e70437a611aea7db6ad67024e3bea4583febb2dd0aac181ce845169144bbc0a775f72530fe5f34db86908acdd3fbd3b297c2fd79df7694e81164c432e3a8e953a80a423814aaf8925d25b6e106c21762c500afadadcf654d157c9c76c229859b6155e7e5892f16e51ee093b6f447515bc62d7f5bdfeb594113905d2fea6583ac361dd8528141e4d00f6dfa85ca9d35feac4baeabbae6b06a677e83f8f631b7426556f386ebd18328b6c3c14b1f903912cd41c16a966cfb30793ddb509e1c989156247814ed0d30095010e36129be243059cb2a7d82a0d2236f05a7fbd70ce15398e1b06a716cb31f1835480ed463fb1e422ba05329c73d7f01e532438776ee4af2b8396b214735a20bd380ade09c3febb515cfbe9565749e00ab66651edaeaa1ef3439ec7768289d0410c587a482b513fb073c60de1fc4a3716951a3d62f14f7b6506eb82d0d9d4e2ab9abfaa90091b77e03a3183d5cae89062ba57dd2d162bbf67908d267718240b87951d582a269e991b595ede9acd9e58b7bcfd2c5c36fb1b51eb1e5bd4824bac297bd794d23ba81bd9ab070c38554963e4aa185c80c762763d340c674be8999421d731a6eeb69d8b9e64eddd6abc1d2856e5f7f4946f9b70df49c1ec5add59ee0c9f115d9061b83a1117975e071f5f3f1e5a37be34ea6eedc434ee6e4572feb6cb2db4cda0fd45e355c81755383aa0c257403f53c1508c1938949989a6605e6293f840049f5c8f2106c57238801117960cf12af04e4c42ee7093b7a538d3d15705748f03ee7aef82f6165fdf414a2d9885c785e3736898165e150cc1ab60fdcec56a19667be2da60cb86a53b1561a5ef3087e6235524ee3863d0a252cf39c1ab1d1cd7d2a352d1bcd723c7211205dcedf228bd65e4c4bcb4537bc8d43827ba96eefeb0d273bd208f10d7883927c283b554a53770702e776f85ad812aadb0d6d40758e02a456cc4bb7fec9f640a39c8360117d6754a3333e01aea28cb8c135d85848310cb523e0c185354f974cc11cee73e614462267f92a33101cee19f5b558152c0a63d2735a7c024485a7af7ab556a34e3043736b45509a662d5a02e8ff9912b20826d299205e41db0541d3c74d1541f8464093bce9b0e7d44840724d44878b1841795835b6c50a865468d622db3140cd392be96656a887676854e94538f9695db79498fc36f9df4fc15a9333277dfee43e84a7e690b3b90639d6dc282464f9d1896a49819b5d30527d075489674dc7e1f64a7145caa6222a45d37eddaf94a7a4c3cee28d85f674e189468b2ab88a8ce33c96c46c9c7c14a562836260bbb8cb1ce8c8f931eea4c408a4add3a6b244c81be0f920d07edb53fd32feca32efbd55fab0d8e69f41b8204d0d09090746d126ab6e3ccc159ff0ac101d9eec3da912f893a89a71a2cf194da4851466fff016c956a72a9609c4d7bd0eb67078c0910d5913fe27001b315abe160d2942fda4cebbbf94929b8b7b033f71f7abdc9286013ae1b9f66a226d48b35c13b6550af609708285fbb45768b8c4dba2528e0e735133b502f59486980182750887229e935bbb49c58422512cc397efb59a747bc3288fe40d4b9436d8735fb3d72eb19af81b1dd908b132eb2ea0329a97b0294ca4a5e361342d6a45b44d41c7ee4d2e3f08bf477eac87a8173de91a4acc17693cc61b94ac5d5c0c1ce1395e4af8ea4677d4ef8b6228a9712c8e4d241c1cd21cfa3c77ad4b3ff4611b06633897d701c6d4010e7a2a3b7cb2ce45256f23ae21c53916c866aa3ebd49a563e684c65640a5abf3ef2e540c46032e2098cd8050dcaaf08a38846df8404ea617ada62b85f5c3bc46a30cf0f9c4aeb680399a9d71f3d764ede53a4565d4cf2ede1a96e4badc4dc71437a5114b272ac73ee17c90e3b8bf6a6b6d407d05dea355ac6db51483b1b8eaeaa84adf2bc0e77a49e2ef462248a04cd0f72e605ee2b54a21ebd8ca0fc5567f241db30def821fd8831f789fa636685fed87a9fa00833e8e60aa29281473348401edb61e92a11fabb25c7310f94a1f519e73cad636b7911324592db4b2967538595f6468943ae2d941278e8f91f00d705258b7d0ff7ef6fcea3860cd032fc842771d653a6e5f8e8150f8644ab36227fe27c3728868acd5f38f7ae2ed3239143313ad338c623402b5d7a9414a5bc9dc4742b0657af50107aa451b3eae9e194628448ced0a0c8d470950f4e1b4bcf73d3422ef90b313596405efda8bba54e43969877387f74f994345746199e4894594a3161e9095ff192c3769a4176cb880b89b677315691223f369171527e7bab444d39ef5b5d164648ccbaa2007cabdb991d98b7c93b350b0fdda6b31c211dfa0533300b80a32fd43e2d8c9b1af0d73d3fa516ab8bdd32ba4dc1edff8a6fc4bdaa9536302a53c3ffe18cf5a2ed0e029e0ed7e8de59899eb6c9b748e27dbbf3d455f59784d9892493b89b3f0aac5ca75f193eac6c2e2f8b3cfb29324b48abade8b3c8efd255aa344b6d2063c69ca29917829d7182ac061bd888c002ef7fe9e0ba9e99ca29584c1c874eb72480f2e33c8b12a12cd2c2c2dc07cd95e2a1289633a5a5262c87a783c4d984b6d2058e87a8355a9ae8ccfc47675466b41d75d037680413650633a1690a57c54c96169788b19a09b18805b2e1085dd74622ad4f7ea8ce38a50ac554bc89a229c2b23b609a53b9f692c33ed5837b6711deb3e89ba8464e1126d7fc1188b0dc9b197ddaa6e946db9a8fdccf4d9ba0343b93e5bf6f14a31783bc543c0efbe4200f4f50c0ba16542ae51c445222891dfe946a172c451af241829748b0fe3c5f9bd7514c45679188ed51b29564b66a5b1abbd657423e4cac52ed9854b896cbb4cbde1848951599bb1cc353f557c5bf0cf79cf54884c77d32165d4000766f5386891cd35135b6950ff0b9632e91152effed03d89a2750f76f04b750dd914a605a7fbcaf5699c8ee21c2abadbe9512b130857ff1918c0331642979bafc87cb6445c49ce509d9e58867f84466bf7337db673cd270807c916e798ad021d7e95352303b842c62dfdbab6f7177724b9ca3e1caee058457de9f25025bc959f52f2b63d9322467b8b623836c8c73cdf7d8c43d8ee8f371bd5539ea74e950e2c860a9c7e184604a8092c0274de42842d63317039d1db06ce082c216a7e0d0f8010e95e49871d052017568a6873137d127968cb13a640417b88baa0d8396d70f91b1687db88870cb451ec26b0475e7cbb25bf27b7e0a39d85669929c71e7511112beda1836eaccb91206cdc79a3a28d73cd251e593d50a3a8304a4e42805c458034a2a726f582018daa8036ecf0bd9fb8cf980f861287d841c329a31e87ede6dc58969a5aec522fecd75babc72382b3f473c084e7dc53cc67d1302721debdbaf0d4879d158f7202632d6e9c07bc9bcd19803df24d9cdddb69452ee94a40cd806de06e806a220f0b471d10aa60cf42d035d71c20827535822537fd66fdad06f09fcdef216ca5b1890257f41e68166d179a059946ee34a3ad5286f6ddb067a378140206f79ab25f66900ac5e5e774f57bc9aa6793c8c80c57962a28205e4de80d0be883c8bc54e41a04e1c0841910f48192a466e599a4f434cb880dc379c0f6aaaa2c230c438c421c63dc09d83683ca860310c5b46a43426d229b95fa4f40ab5923b040e8a4052a1c2298b4162a7ec379054a7ece0b74418ca4207c33ca5655ef4865a699939ef8d28f0ac3e8661aa7601b3f0caab780e97030788a4b2d62aabb5d5da9f58969abadba7b34e0a16e9b00761d561feb8a7d20b3e2c59da501404a5142103f2470eb2a4e5fe9048cbd09b0fe130771472e060ce3f8dab9eda01d1de3f2f27c9f6945d73af965e0963040aa37ca48314f5df8b51a8dc52b28329c822a590e1ca3a7859a47c1cfcc90cae38a389b5e0e989fffc5e3c2b2ff57fc895e659ab1029331f3598442232611356c39c1346a44a315313e59a76ef06a4a81fa7c815b0286731130e33415c954ef7ecdf7bafbbbbca66a6aa90d2cbfd6200b2fce40fcff421270d02c6b8c110a624fe032d4cdb1b21cb07801c754cf2b7ef008d8003cc600009d32f7a31325c1123628a71bb7b0dc65a50d3fc82d552f99c737e9c7beee722c50ac7c4b9d69982314f4a281527ae64aaa984a0847ab117132768132b4ae96465faaecdedc34e338bd8c206c7745d449fc612acfd8c8f2e12bf77a683919689e1880c545cc9f4736819d17ffa1ceb7bd70fc90b7898998749674e2047ef22fa21103142644a9a271049873bc8a034134987f479bc004f9777a70b2015689cb33d4ea4b30b840516f1b59dee39ebb07bce663d671c07d2b146c5643603a92c11f45da81891b0accd28505a45662c5f6354c9f427cde2a5b403b26db2d49ad601e1ec7334473a7bfe7e21b8df71d01270f8ef11fd3b5adebef3cf8884653104dedfa72fce34e001428c789e761098a0e8816fdfbff3337e65de3e9945fb1dd8437bb8ab03a4ad0e485d39c0218f25b0f637ca90dd0df7e7b78162df2802936d265fff39b73d470ff0acb57e4f5f06418874d17c1d3334909ae0f0ada051772ac4de44e1020172b8a9392b4f87994fc09e6779bea4ad0e29abc3a9b96beeabcc8dd77e0c5b60913bf9531977f29174e74ece9daa286f282b4f169416a7aa4a5c2d1d2de37f63b2bbe7fd48ed5e0d009f8772382174ef58a3ca62d66ab572c256d80a5b3e0ac3960c674b96ba4cff244ba5dc3fa3c8a450c93dbb22f77b46c041e4c3e04dc1a184e999c5d0498793a56292cbc0dedd305f0621b07f367267b944cb0f6c5c2d170e65d901b95db8b6f3db5760abc00cb9a81bc330cc47b325450dbb19c36c15b3d5289fadd992308dade0d86c49172cf7cfd45a25cc6ccd16e8e40131d2b44b29ed6ad03cb9678a866d061e5fb0eed6354d13028ba5d4aea61cd9b760a7c44e39a809eca184699114f51f9167e8542a4bef344a29a5945acd5329cb2ae28b916105d8ffff71f0897d603994d21c3ec0fe1b69cbfd347c4e2a80d2a194daf4af46fefadbfb276a354c506ad9be589034939232246dbcc813cc516550962e22cdafca94615aab3bf5ce27d649d1f7fa800ea7006e5c3412f27e012ce1b363ecd6dab746425903390d4435aa7d4d49312e9a3f83a623cbee65fa0766d9bd68a0900d942ed305df45f3cb14088dd3b29cee3e79b5562a733a94cd49517d8275acc350cecc47f657563aec3e79150a76ac614da2fef569c7195ac883a8a009a88009a14c92a506220172d429587223032aed0229f95464c719580c85344ae9e9d438dc195f60fc7abdbccabfbafbe52f4b5e1836cb5829c82f244ec912037ce4fd8c07f7e2ec22f77d6f09eef1048bf235513efaf0c8920b1ed0013acc1fddf75340da307f74200e119032da4b09c381541043555e21b70c430036d882af571465e4db5a886571be88c0f665693a0e22b0f5ec0064b8bb0a78ffa9c0e7416fce4a6d5e2f8cd379e2f399dd01a9d50b2f264bf226c7d0cd007fe8ebf31802f74b0f855c37a882165266b18e739a442aab63ad691955cbd496e1bebf2a6999cee77d54d61f3b55e9d0d5a1a6695acbc635e555e5a6432674181353064ecec499b2d05c328e38c4c09dc590124c433e12037b16434a70cd6268dc3659dabe1f007234a54c5728bbbc0c42c1e2fceea7f5af93a108fcb2fd0f79eb0105bc81d315c3328fae2a5d7062ca0b26030ad72ec8b805f6e1c97c1f3cdfbd0fde7fbeb3e7bd1fc1211ef8019f80458923b2028b73a4428526e4fe59d332aa2d44a9b1421324a0c8028ea97f7e319fefdb1df1d1079c352deaf798e038c1994d5cc93e6ba60adc1105961d526ef3e05082e7eceefc1c885c67cf9e9d6ddbb86de3b66ddbc61a1593d51ce738713afd49824fc7366e18587cedff031a48b58005c785e3c2c2f572699acbe57269d8f3c4621e4fbbba4fb7fab85c2e57f7c1ae8f0736c1dd58a362b2fa70375d7fa648d9b2524a8b80b5278580d03067484a60926b9a46b234a03854e69ac9b933774e1c64dc7bef0b3cbec00d6899dacdb7a22971a2d11885f90c56b5cf4696dd0d0d44a66fada53846e0edf33a4e55cb3411fb5b05ea872eba3fbdbff7493ef26e4c58c1f7dccb5d29a9d7c4f35ac65ee0397ab85b81fa9def803e74be1ef11135dd9f2bfaf73f2d5992b07c1fc9c5f73efd420973bf7e979a3ca110f83ebdafbdbff8f9fefd23b2c4c9eba2d771c110e014163747863e54e40b7c9fd36e4c8ca7c3b3bbf95df7f27c2f43bcf7bc07a4f1bc07ce1f4c9f1b4c33048e421651cce6d8322f38b8833b60d80c28a440993c9f8683274c3baec0abd56a357d7832df034e5407d2fc60f2803e5880e98b7305e47e36b7e6bddce0d9dfb36ba02be01f4c3f98c4f9ca343b4ded62375aaef77ba9200f1198ae0f8203f7179c29294129b95aebf50ec8f77203fdfb97fe0fa094220a709859eb6ad06ef03ce3fe6daf7d9d8d4e5687bdc99247c23c91a34e8afa63c8e2bde2ed4025ec3802737f3a9deea975d000cf1b1e42c0a2ccd2a9d745f46e9bc7ba66402a98a36d74b7fbf75e3074918e7fe17be50a5e3547e92edda711b2c8f35e207246b2a096042bb0e80843310011e279817570517f9652ce48a232b0180ac5d03a423f23f71f4096e6bda110d590688f84ce9270c5142492a6a938276013f613e6a37fd8c3fe4619f86a9e1b6560cfe2df7b9b4987edb9461f5d537f33e925a4cc344286fd7253d548984ea76e76d292c5bf510606c00e26abe72bcf2a138a16258b9e39573e9ae0c4b105fe1f9ce409bed0cf482ca8811e302681cf77ff525ff07b58254ba5dc2407812ff54f8dbadd4792c105b847132c4e18ecf630028b24188f2a5824b906abb109eb904404467292add5d65a6bed6030ae3f4ea47c79796a7ed3630aa63fc117a97d569f958fee67353fab7b35ed5a6b6f624e40a1503299cc7dce59a9e802f7fc285876f8dbb5f65a6bc79a7f89040c2e185adc9883471f61c741067e01071998722ae74659e2723f570380982cc92aeee8cfb9a8cc4ad83575efd59c5bc28ddc78069ee08d27e01baefacf77f0e57a5efefe36db5edbcb47dc78b9717b71636552532eeafba5723f0c0e6eaffb22633b62db8ed8562d236edbb6a936269b6a6be5de8e90252042b88d0913785b6d2a287853e57eaf6054594a96e8ac44c8d1765fac4b8cb82fd623726f2f26553aec6fd2f9fb6df751d7d4e19c6c2f6ebcdcb871ab7b735fdca88154d621c5a1f4bbd6da57ff0883ebf3046b3535adeff672db48a6ad73b4ca45fd2d4b4ad02c5f49e0204bd9fd61c5a0e5049a7999eba654025ac4de47099012be7c4c905473c8b0278ba4069d64090bfc72c3045fecff0fae96d17e8a9d20988fba413010ac41b0ee6edd74ab5bad9b966d9b3703b7057b1c028281602de3a0906eb55a2da70d085cb4600aa624750c25a4c09179e75ace08d814489892482b987004234c1e0d3f6416732c00754d9d0250a1a90369e810373025816109260f3c4023a05fbc91e57394d24ea7e322196017c9970daf9617dc719136a48cc501c8fd55d620fecddc714d993e6c9038b70a372f774dab8f830afc4deedbbf2ec39ee0dcdc37d056173181fb78e8e1e6e91f0f5eeee1d65aad6d071bd0e10cc8e30330803740d634eddf1ee1c60dcff372a687f25128e4853cbb43901ff2f63892c0a190a704357fa862f0523ec2dfa8989cdcef0143fe9853a60f2c1e1c8f6c8a270ccd5e214795e5a2c6f111b6b2dc968bdc568bdcf6656f8890fd9d2c6967b8665172571c91ac37166591f8a417d8a22c4a96e8f75b21e4c8e5454b44b64bde43428e5cc0daf279a0288b1e0a04d6295af75eef24304ddd73d58323894abdfa7392f32ba5945ebdcecff4106a02bdcc4d403fd3fe71b9882cbae47c2e382e585c6453e60f17305cc2904293bdbccf37f36ebc97979230fd95e523ef0a8f0829ead782c5054b3685749d1165c604ce1248e4f696dc74a06795d894457916b5052c7a1e2ab787f25037585c7b70a8d0126b81b5bce52d6f79cbefc5f87fc65b3e6a534c4c1886422ebdd3e974b3721d4f27cb1f25f806f408f451e4fe2a2a17853478d98ca624320b12ae1003534b37c19464da985a5e2479cb6d5aa26051c2544c8a68f968a6a9c5b1749802491a2097afe102fae0f2344059b71a2098b8cac70e3dcfcdd92d1779be69e3a38f5ee6db70958f5ee60b2fd8007d007d8d7f01e4f2794f7f8d8fc6f75598f5cc473e4a517ba08873c83373afd10196d9e541efdd9ae96ef9c85b2eeabf2d6fb9cb37e32de21c458f5959f66f7c924791a27e97d2f249d3ec2ce8b22cb49396d1ccc8fd5d44cbd0f9a34f63d7e4f9de01a1739b74823e4d2df30c61ad57244c955dc127db6a1928f6d532623d4d81d2320358417fbd224bf3bd903fec16d287cc4717b4add000b2585d9d5b65ad095a2b12a61fa404d32c12a6255558e8208b249a5441a6aa4c98f505d2e64499484110b20a646d819499a716892e59ac279f9425611a4a4b96e796f25c36523650322bb332992c870f30e83f39b260d897e5f001dee4277f94e0662f95a0f376d6f99395c912abf3b21e517065c9caf2817b0f585bb95db3582c0e68651dcaec8bffe7713c1fd087ce77ef75beebde078e6a9d07b4b2e9a0c5d261cb8404c17dc7ce6c96ce67652eea57872d05feecf7de64d7ba209e680558a4b38e45595467d2af5ad1eccbcaacccbeeccbcaeccbcab258199d592c6d65b68abdb12f2ba3381d6e93d2d9e974acccca6ca4ea0fb75b72a660066b752fc6a150ff3b1192bbb59f49422c2047930b12c957b955f89226eadacf44715246c1f3a3609f9a67e33c05042125777ed2b004d32ca5741185fca2bd1784e66e2ae9b0bb59c4ec342b4f28a801284121f1af07f048cd7d51e27c3e9f77d367ac51a15098d5b5c708572be7fa8b975201d8cc0e88a6edc8e2da132a4db3d5d62cb048dbc4a4439f242a450a3573a494dcfc9a78feb68c19da735f398e93538e40b0611e3adf03279b40bcd705962599a71cf154cd9eeffe83f0842a6c9044155490610ac2f35d8d1846e2a2938b46465ad40fd68802ee96b13f67e5a107cc699ac6cd776dbecf9b65870109d3bf7d19903efaefa70109d346be23e0233a74b8a306580c591094a1d066adf58c35321d521ed8fcc2c0e2cf39a7bbc7a8c86b6f9e37d74e0f06efdfe7310416b58e36a7a6cda975c02a48b218c129726508cc860e3b943107d0118662f060efc206274b2a3631141528c6dc03ac652415284e262a4fbc322747802515285659527101962c4e30b2a412e5ca111cfc39039f21ab9455fe4cd7d0e1045992f38776c22a94923ca76b73ce39a97dca1db1385a0c2c061613d3b90eda176bc7704d2c2d330b62b65a0975d900210fa10a9b31cbc090c5308b130623e5092577dbd0538331a1ff308c8989994e260c6706935d106965c254f8336cc2260c0786f3fa224f50c447244cff0c2a4c25ca153072ff9c7eafdfeb5d8d0a06a39102f7649a69334506285387c70becb5d7e6a0f3d1cc2b3cf84265eaf0d80119d726041f0b1663c8fd349de76cbeb25a2b37623941d9a17f87ce7fea82db43ee7ccb47e3f22def9fcbd734c8b9c131c9a004163fadfb1f1b599a21df2f4015fc345ae6c3caf7432f869c40c9f763f3b132ff63e5d3fab83abcbfe30b2c7e58f9fe074acb58f00587f9f73fac96b17395841731538f2958fc58c9f7c5edeffdb47c843b11873c1f2b2d6325ce37dfa7e1c1eefd78cbedbe56beeff9c4f9f63b9f11cf0b0eb97367993fb14ed322b8a752a9d44ce5d8215b5fb7358bef21ddd40d854231969b2db0020a382611e7309e6012433348c5621c77e7dc5e24e54ee6348fcc72c4880eb34dd204ea0245e2a7090365c1dd4120910106b85f861496e1094cbf471898bec300cfbfa04802cf1a28dedfc1e6acfdfb0097c0f3066e2fdb77401eb4970d34d2617b423132cc794ff7769867e4500cf0fd9b8bb4cc2cc2037234692045fd3a74dcb851a34637db4f1df2080226222475475df7c916d19013cca426943014d4d121b5af75375cf005d3f86f09ec9ed4378be8027f4ea5b69bf28e5a4badedc09a54ca6b249142c95ad6b29c7bc3f086375658d3646128c7e82c4d54b2ce3a7dce39a7bb7ba2d80002961dbadf98f252985f44a39ca669367860a0c8f6b0ab69b7d3d93e6ae3d889ab358e231ef188471f613ce2711c65a9ab51f9688b89aaa624359b896635cb5aab75357819636f168e2c362b8f2df3d24629ead6c67e5950c4230efe33d5e1fca904f56921122a1b64c9fe0d576091c5cb8a3714ebe4358eab6eb1c6daac19d63a8e2eee0aef3e464c29b5d62bb5efee374a4ecc4cdb74a8697d330ebd2706045e0c4abfb296853126596a59ea2ac8d11cbb26b95b368198a9266311173519b10ee4d032f55229c2026a026bae06030bb97fdb42ee770722f75f998469d9189ba377f7d61e1664afd7ebbeeeebbe7c74efebbe64a9e6d27ded7616ef84210622cc579e04b8027b40c0409a6c7776f3abf88e2c60f1abdcbcbed93d37d3dc378df71214e7d80169ee7b689e0cb9cf8134dcdfcebfd4f7fcfd97219ebfef012fcd7d0f7ca9e090ee3b3e3ca9efc3fdcefbd3ee4502f73b3fe4821355c1211c384f2f0e0a31f96bc28c8037b0c7d74bc21461335db3f5a222617a52c1056c3a41092f09d39fc3ffffbdaf576d0f0b37afb03ff47062be4714b8badcc949192973ffbe74773a9d9fdbedbc87d334ae394de33ca04dfdd42bdcbc3c9f4ed33a4dcbf9d4936786650c2d60f9784020b0584791c65c55555746c87a842c8d158565fee8403a465d2265281670ea1850dc64b7764a957b6f28bfe7b340ba2edadef36d1c27615cb898b3a5bb5b1baac105ee1c2e60018981224464c9e6cc201320a51419ea9e73d6a826eb8acfa74a8e9c9a26352d14037d4d8b6aadfbed30141243e67581adf5a0c02fa1cc71628b2aa6c005186401c7d41eb00711b0286faa844288ecc8026f2f860fd86180d08794a1aea9695a8873031aa3301e60e0f9378874cdd4d4dc3629dbc10416a51644d21113d0b0c4565befb66d5bbbbb6bfab052985978282fa7bbd33e9e4eb5540abb448eaa8cfb1e65a98544c7f31f0dbf25c22eb12a0c56d965a23b83525a2dbd9d2773dd93f6c7650a06819f05dd0e08c83dbc0ca1a6ee656ec2fdccde1744102c7893f72c8831750f7a5ba96abe3c2224025272867eedf346afe5226900d1a3419633889e0d72bfc7e52d619d31e3400fe59d2e66b4d24a2466c3c6bd37e7ded147f7de7be7cf3bde71ac94def185982c5d23a4bc4ae43b84139984b1600c912d16eccb451d6b96f6059ef78eb234f312399a41e2e62ec144ebda68ef1aa5f48ea30d1767869f5e965c4a5196b2bbc3755c4a29a594528274a454b31d6a9f7e3b24d961e6ed1e91b37b7fa6ddddb99f718d036fe815d3df5872d7e93059c188f8e90303d578efd5e1c610300f2ab028655794578cc8d22b071a44ca54d7126cbdeffefe3ebbb76ddb6c362bb5ce395bd3c21b29ea2863fc5e8c43a1ff30f45cd7755d9717c56c16bb321cde9ff373cd54d58bb3ad5057a194d629f5d5eab0ab4d6eafded016cc959bdebc7c64552eea3aa5ded059bdb135d689b5ab0e9bc25630180ce6b2570957cca462c8b0a91825098825092bdd1e5960fa443050ebdf3a89146182e98b1256ab65825d479e79d674785975d5e1dbafce808495931ee930d93a565587b49f888fe8bd188742d5094e689ebeaba010222e3a610b56565d3dc1fe625d7d41448afac590a13852d29c0ebb7abe713a9c75485fa43f4d1d50ba88aba00a3ba775b7d65b6bad9d2a2b22619a888f3ab665ee7f9145519e12468b20e2a2a6589014a7e5a55bc8fdd424bb5a6b056b544848d1fc284c2c916775816490e326c70b3b815978b59233dda3068bdbb8a9b6d147d5d4ff2dd1a7d5c94f787572fcd19e85c2a12d88938ff01252d4cf042a8a94950537d476d2401a2f7c93471d66604c0377032147b36bb490fbbde567dab3cb47fdc27bafc30c1c53e35ff809a5452dce295556f38727f7632738706409b37ca48307863a507c0b8a2198024c9fc3c1046f7d3aadfa94056f2bbcf21135f56cfae89fb2dbfa4c57ee17fe806208f6b1dc2f7cb7c667b1b4a83fe7da2c388890922f22f7d730b9c9b22b9f8d49182b16666f687cbd3a754d8b4e3e6a21a4d827500c654a9f7e4dfff3325ef948487f8408916f9a881eb793d602f60937aaa505a449626a6901f10ad760151ef10a8f37a8c0225ee155eed52ac70d6e17273829659d970fe8b0a5f63620f1d66a60e7f3728f2598fbcda7160a99e91e3bc0d2451774197640117f9e06bff71f90469a40ff79ae83bf970dfdafedf5554cd1224f500baa2cfedd3a1cd7e974544cfea5a7b72f306cce29a66bb6723ead8fa6693978c0a7108e9ae3744945ab63681d13b5c385c5d9d2642c8b21eeb51e7a44da801c695f8414f39107a4a847a0ac426e0d74504716650c0b0d1bcdf5a8c99aab160ca1a9e59032348607c7d9b86a5438d65121679248a49983079872d2b525cccfc7118e50b56ddb444fe882b497b9fbb2ab61e6fb127ca9f7e9cfccd91aeca5b756ed45d24c87f44592f350824552056bb059e3b419d2e7961af8354cdc54a1e1d202c21f0f898f68802ef7b660dc3351ce6084c0af43bd9d2791484d38a114d404a6ede93a0fc7719c13d60c49df78bae3440af7de23482477f72a046725066bd9409163cac88d0d3d7448ae4f5cd8c33f8b4fcfa2468d7b319628890a2b6ace9c7b31fea73315070cb06751bab48a8a8909c3af2d436025d9082e10c10c134d12508c1d986423802689dc626585e949e793844e98b9c0f4e4fe4b2cb8f05f51346e64494a41c445dd8282b97c4b915c95d454457538eb5f9a27433cef695c9ee6c910d07b208df7a0a7f19726bfdbcb0e0828ba7c342d204dcbbbd0b8fc4b4704148b48609aa48b4cd245be4392dcd95a66ee74c00fc944790d125932428ebce54af8eb895b0145742c664eb374d8f78914f537e1ca50e42956e44ea2a2b0404998eb44c260c11a584f331e34b5e6abaa8a9af933bd0ad6110ad8c1ded18519d570669e74430a166b45d5a82aaaa26ab03c0d72b1582c96dbc962dd1d536071b2b656e7bb3e127247d19943984fcc22e4fe6ddb1e7f7e83d2a18fb268d9678be5e06459111b66dddd59ace6dae3c4ebdedb3dbb59c51461cc50590c6b77ad6d9ab6f5bd634d1886d11b2a140a5509c95987302b4c68491fd956cbc63ad4b421600b8a01166d8bce3a9f82a894eeb50545c968060000800083140000200c0a8683e1906048301e47f7061480096a7e46785a389707644194e5380a628c31461a020800801802a6466c8840001ad35242247515dd25aea9bd1a815eb13b684f591a12e5e2936f07005b91e1280395374a407da2a5fa2210d60d5434e3000446e8254883c0e8082f937664ab41307f8e43c53b3df9214aed61572b1ce2ad97a1cc323d1aa8c59eeff5747a3451153be82b1c293b3ebd73aa827cc48be50b89aabc9622163eee20ef807562c16c2a6b314a8d6a60791b1cb3d50c5595fd6248ee8f2f4c5e7c4b6228d43f319abeed966c94ddf12a233bcfa2a6700f5a1246bce30668549ba375d34189d49b3f5623f8448a707c222d683e61e4096b3b9a3e81cdfecfec11b16b0a315b34081a101288b026c199e6756e1658c5c6585db0aa20ca9d206e649b236e3867254e42966fb596c4572163a29603a08a05622c1a8164bf70c57e235f4988c2c5ef111a1ba5ca98f2fb8dae5c2e2c21a904cd86b58d80d468474dd21d0a97dc76e5ed7ab4f29c8a51e17124bc73ed4189df74bc81424c1321c1f65c2e7dc5dd8610ffab3780f4c95e8602c2f9b550b0cea4380b464ce46cea4746dbab8c8a32c7121946c0512c6f945668868d821e52c31efd58e2b80e46eea42ead5a2a6fd3b02f505bc2e9c793b166242b2c2c2e5f3e8c46b642e630e67b7116ec0953b2577cf6080e775ebedb294a2e77a36220fb4c5af98c39c9b80435034a053300aa68666986d2f655117f68e687e033db859379ca671893624670f2d762e9ae0fd4c2f1dfd186fe3d35519fff39fe9bb4563e1cb4fad2d400d550c487eb1cd85a1f2d9d10f503a1a82bf522d75aceb7d711fd2cf3cca0527dee80786ca9ed53d1a0cef6faa349616d70a28525f2b8ec8bd0626316e61f358935b0e7ba97be1833d150a38d050f13fc02b189a98d7d995942aa17eb56b7ef4ce2f80b3c45455998a70b38a1d5ddc1186a73104db8f226875f21b415e98ee9097225b982cdd68e3586e00fee1b0ad841c54f92c9ee9ce8ace47de7d72c2784dceaf1c4d7c58d114319c7e6ceef8342546d0b4a291368f8c9a23258079f081bbc9b023e2891deab175090f42be2f395a13c7523581df68b39ed85855944c6f99c3602e0a83a59e9737b5455154280c1d7c9bbeb0eb9af43da1dee264bfff8e5749c6ae7d60e9fc6921a8dac6d892e3eb026998dab0511ac24bb1dfe4aebec3f1c68867366fe6cf3077758b7b933f57318f22a1a06d621eebcb29bf1a5b44d04f6a629177780b0b54dd21d95fe77dc91dbf37c8d7bed54d0a9ae744d930fd4a1acd2f370026b2bfbd28596dae134a79d8913896baa4bedb089ce1bae748c638aeeb49322bd1bf1b273adf9110235da41b4a47808cc2608dc583528901d7065d4eaca3d90cceb38d21d93ba6f486a6d020db715c1dd91abb4aad177271d976a75d24b2f0892bc943b46285f893fe5f07e6e41a017ef0b39b7543f17babb1f6567d95a93a5b4905b3c5f667376dc5880e2f2b34ea7a59f683f6021d88fe8ebe319828f28b89d74e3623d51e3d702636ef4df3e75eac947a6f796fcb29c16a111c9404a7f2954b9ba689c2247bd99208978cdbe6a7badbdcdf8d31912522e3dccf570ebcbb51ba0c2dd951a71a9d3cef7a34a664be2252f2e12f1f931798f96bbb90f4c6eb4e4c779e88f9c914a19a9e00d5da8aadf2ca002b29f80e4508163a704b12042f4149f0a34cc85691cf5b9fb16cd377472118fed2c882b486a29309d3bd6d51d468d8f5247624536875856f0f09aef960224e1e8ed563fad2a8b925f6e81a77767016f8a8e626fc30ad4de082f33b7e30d041bf9d83c0eec99436f1cefbc4b2a75f222172853d70de7611aa236e2af1d9c43dce85e7ab6481b7ac00a1cc1ce2522143bbe8db98909817722b5e381a4765353a229156077f6a738f398be0095b551b1fba45e3e80d4814f34e13fee56435bce74a1ab63e7d7f1d00621708d81b94890d20ef8ed27c1f899e31a0404828ef6887d8e5b605d8db427d65ade2f9ae584151ef682c401e708816962161a8470f1dbadc2085e92b591fcca6c66e4fb5cb4db43aaec92e9a3196d464d5e93c81834447948a26534fd87cfcef9d527cfa0c22f3f2ae7d1f4ef22e028ab4c4e7f7dc11bd83f50413b39b344e2f945b6f61c819bc5a983ba97b9e8c678d22ffadaad715ee6a160a9b3f8cee850103694aff8eda216e8ed416ca4b602589029255f97d7214d323a943681463e563d075fb0ea4816b696f10c66753c2abbe1266646e74943699faddcfe58501e7cae44cbeaae05853b2bc484ad84e28b471f261d6ca41a090c415fad0920b123ca408b6b0befb34f64d0fb121b77b25ee94f1f61282b2d2dc7ab4e73fac76a0291bcbf1bb212004d431014b13f26c6cdbbf91ab8528838dad23e9235dcdd369ab5c3bfd9c493747dd7eafe0a2e8ea05bee2679a502f96feee3b2930d56690b77f4ad965913ec1fc193163cc1ce896ba0c792ef35dc4307c4227d0a681bd7d8c03fb1317e128c645720b720187401873c6ca93782c65691b5143d90b0c0a22e788cd2a890026e9567ba97ee10a58e8f4be51f3f9f2f82383d2e87c7c331e719bb2d4cd201adb4538ff2719d0ebe9215e01a232c2108044fe00681f0289a15365b39cac829cb297e0037cc15406ab2863c7371e73fd7d20459379fe60d64bd92f3f5260cc6c0b690aa08f46ba1885084a0688832b09265489ccd9d457f98dea5931e462ea40698808c432a3c9d2863f0a09bbda496da4403326e91e5725ca2eb66d1b4e9b8a2cc250ef85c397335d127a85ac5da441cff651fa4ad5981b833c242c783a180512b063cdccf8d2314431606b7144e21f8e4730ed8ed1bb883b6382df6ef74dc7f06f27f08bcb1db10b369561bc884ea1894d4469054720f6c4a2a185444820873cf73a11bde7a776e6d0217920c057ee2bd4b3958991fcb2f50f400b3387e1f84c55198a8e87a4f460d539f28d260d943ddd5ae07a9eca4bf8eefa5145a8c0e3a78336a688f492e996ab8a56283122b68c858cbc5400f7301f785bee22d0dc53fb134ec25cba8187c5730da9a378549c328d5774995bcc165f9c04304afa308ed2e8901740314631ac3b13cac3f85ec79d267cc9442d775cc0d52bb97199192d4497b4b3cdebb927db7bd410d1cee745d3a7cdbd022b887d12b4faaf0690b9cad772a1871d83f2d0396564a5b380f86919887dbe5190baebdc0333f355bf7f494aa9fa10e4cdcd4cfbd2f6c973d61b5b854c0e700fa147846c517c56a37bbc6e4eda39b526d14fe64494ad792e95b6875718ee0d92cc816a5094d8434a58397ed4cbb5862a1574e06ad975f12db225beb7b58fa7d2f4bbbec8207996d74fb13fb2fa291cde27d131af34ad5506dc2ed56d9edca3514da72170971b34f7fae2fc02493e25672539c3e4d25208a7a57a0afa5e299c440ddd3898a787c922ab0fe5ec5ead20e97d02da5aea9c348148e98ab9ea6fc1e318051ec5818857129e3fe52b346fa3ee6346b45775082b7352135fb2892c7cb1731089d0387b19dd4691331333f2dbbe935642d9b53db333d8c5f8640c04d1ddadc5ad4b92b568ce5db9633a3387bfe14b3181576c57c98c0d68f2b45261b7b614748d092430890e1a509dc61f40686b5689eea15fd9e5d704d0607781105a440aa4561986745d9f8d392e6a4ef31e44ffd2ffe6c907e4d3774079029d31eadc2fcf4a64d65a917945e792d5640b4fbbb97bea9db8786ab457e5adb3a1723767df504cc413641f444001f718f6fff11e9f0ad28b1e8fc2db0dc53b6f9c7646ff3e24cf51ea2ec7896ea8fd8b633062068b8e72950eff65fa1a3518b43dba683174ee80ee8869195af357003ec3a23dda5407b5321814c049f600b73eed059fa8ab028b95f03e71581974b8f1f0e8f0ef83bb60cf33a695036740901601792ee57190abd932d05d7243250cac7fe450d03a1739a8c21689851202f1994b8bdf73e2e76cb09106c431f3aa0afafe1fa83691190dd18a7cc9d7cc3822f9cb434c7699c6aad130f78e5ad6c76e657396794a2da5151d74e12e1ee8f1a74f25e60298a9126456ac2e553db5fba0aef2755814cfa94e1f6c0ba8c3c77f4a3a95509e6e794dc8f7ee6fd78abdd2b045bc0a26ef6560410c63bed05a43d9858d79dedb47f9288cdc04843476102c82c6604147dde05de7c8b8c9f45731112eed495ccdb31db83bbb968ac61900946c6f154ae3163575a29f82e8b94566656ea014d72876c34c5da7a23015a85df05178a947b448ea59edae8c111922d1a220e9878b086de5419810cc52ad341d8e781f9bf849f72b1ca734177852e232e35e6f2c5881f2d84954676b4682445a15395a3d0f915d29803fde0c1d7955add5333aa09d3cccbbaf9f21c32135389ea4acd483bc7e7d5bb61e3d9b7ba8308615fcb5fefbab56cde38a817b9d9329b3aade619998470a15bbc4a88b71b2e1613a1df3c5ee0be45428a5034673a2cc740c237a1dcfdd07b56e03e6ff3aca19a4c665afbbc644185201f77e61226d193e394429897df1c1ce11932aa23cbd6a8c4d6200479465c5183d84f412db145aacc1996e3e8555ee3a302d26cbd2947807b14051e8925393c4a1d29a30c89f246cff02da9493f7a06fd998dd5c34e543a7d804eaa99f7bf3a25c664ec01615463d18705aaffd203035520caca3049c05fd2eb7aa33b488c94651523f1046324a8616124a14f1ff259ea200e42b422c5871a8d0e01eb8316c2671f9244285b30d02f3c242fc0188de2470a76eea3414303093f3ff1d1309a7ef508fc32207c17eb5654658fcb667e6aeb34b52b1a696ae500dd6e1b19d50229555f1546dc4ebda087dda36b0e53ab2ac61ef38ffa244f39a78ff6f933f7783184b1ce1269309531b4546f69b514a026377b1ff3c0d4c50b0d53c672045dc67900ad99322939796e6e226182663889720d49553ba44fc1665596e006d8348adc96371c2ec5d60a1ede52d6a8b3b3146e72d06af5ed31ee591e071b3fcd1be3f990c012ace2b3ef3cc694c19e25f7886311999bee4712a57d042d8f5c410bfa2767c5cee7058c5d742349a03ccdc4550f87b33007fbee823637b1c6cc47ce13e62da9ae463388ee6e62e4209f5a9f07de7af81c2a48f72cf418f397c14f0467bf0e47d8d54f364e47450827a8d83ec05e5c80df651c44a8800dd4a66bf0820753eaad2ef8b5d5667dac02a4a9101503541db6e89afd8c4a53626c39cbef710ff57df37040e18008bbd2d8163eb2bc2096308f0ebd3d2b8c59e7473ded2159e1ff7946211001b236dfb3330db0ab56e8ae5a20d9c2fe69f97fc8b6f3a7eeb19d74f987f86ce6beb81a7e8f78beba3028ee1b9784d949544c64ad4c88c687edeb4118a37c277084986ecd1f7006d2544eca2e301a11121208481165b039077c1a2115249bef9c99a790a42bbfde4d649b5d4ec77a690b00cd0737c26436a3d40e93361d7b34683833424c2b386ec486adb99c30c76a5fde823791a3832cce0bb883373343730694f11f98068dd35ec68afc1f8041637ca965ba98512402bee0f049689063c12110632a47ec1ae294430fd497f31017c15df28fc0cde13f192708d57aa2944786ee7b2d30ef3c3a06615903de899e8b9b6de8ea9cc6392f078eb071c02faba22b04b97df8f40216f808d9d6d3a0c90e9a793545df12e77493ac2fda696f315c5fa26d723a4a8bdac2718ce973647e19127642f2dea87b84642ce065c4bfaecf886c29cedbdf2974f7a4f2ac33a407ca21160af3f111c0d1b7e23d9cd2ab5ed8b288663b84de659137e4e6f3c74ab2af4320d25fd3de3a8c6777bdcb29dc4761dd6f7d8b54c83023111cc0e143b68c01abb4fd221d1c0ec2187a29593ccbc99dea46c7b3c84baa814e49e7a2e6a1214ee5624b52143cc0eecb2d676385fceab7946a52a91701afce00ddca67e2a651c26ef9b222c0dbd7a959b97a0da44765c68b2c32a91825c4ac130ae684167e418f6558556140f50a3e7f876b99e4928981b297fa7b140d9355c4cbe4078884e16df95c9cceedc1d67669da8d873cfed61fba9194e4e59e0d0f2ac17f117f70b16be7d294b743f17301230545af7f886f700fc58fefba5e769897897bf141e2266a418ae5fcafef04b5cbe128c563848113a67ae190e094ff2d7bf4adf58bd83bc0315af29bc4e42b6789e9f447e3b090c3b617191410feeac5258fe55e4cfba3db1f027527f5b0a0f545a238578df60b7d7160c7719415d53502822917951c3bc6e309c69be1a067b5b87e200fdfb161193f736826957c480b14205bb019e304d1c08e621a20d77a0133e010e5c1280d5f178217e4a0c4ba11120a80dd081f53099b32d8f8d186d7d20383aaf732bdb8e245012357c07efbba849ad02210debee21bd2995a90b87e120abede046b0f16dfcf5eee4cb9422c64551558089c34c6455fd927435e3b7046a1ec2fe75d161cbc9bd683f49d5a40bacb94c2f40e02c559930a66f84593184ede64c997d63f45c3aa7c917b8d30c76d02a03d27fc67e7891c593b018f54f68a51b1df174d5a966acb929b35a7d4770f1effd3fc36ff55fa2dde207600a7eca42c08c6d78decbf02e713c79df46baba7dcb673136dba46f2871d3f1bc76fff4faab721a390a93dee33583eceae2809b71c97b1f7b417a97b34e0cc89e53f77df8a8d355d63712d3ddbc2b5a48afee9b5a399a1b1a42f7f54042726d9b4c4389e101396c96267fcb253829c42b49a0f0e29df189ec643912a9edf47999177c71547c199a3a063ade3de8264e07b40beee1eacc23187588c4181112c176693e6a5dba1baafd1ece46af8a055cc4d315de8c64262ca93f8ea0bf2e3cfeace9545810328f729de075297880ba8426ffc2f62c3aaa59362fd1aa8c1b75e52a7232ac2c8736a8f301dcb5b9297ad4e62f96ff97a93e419fc0039a4b0e50407bc022a5e076ae8f56c7249b6aaa017ea2e883218faa9720f46b559c29b4566a570bc6d96ebd9d858b7b297a623f331dadaf75e2246d4955d3483105f7f569f2a3fcac29b149994d12df555b986051ea205dfc3b3df8a34f874f0b54b3b7a7304a39d479441705146215fec87802da013d594c1ad9d87ec7fa2326154261f62ba632ad0c84804a0f6dfcdae9a153e155f15933ae606a171b85c4aec5b4abdb19ccbdfc54cb14943be06c5dcde8542266b71b3c2f0d889671dc32a4c3bd9046eb370fb0eae014e9e227fba17c1fb8ec0ba0b0f114cb43c67751efce48d11e73fd1d202089de111638ceb719a48a78105cdd70d7f17a17b76eab72e1a1adcd71458dde8a3df1b0dfc96649647feb8e63f48a2839a51bed4547e4a9f95a826664e522b2018c59f07c4d45a2a8321fbba03b7fb514f4ea4c6c93bb21df88b60ebd75258b94c93b01749e9afd4340482fe2e1a6f04c817feb935aa96911225799e903f768884baf8b9fc1ef96015be73f9a8df535d5774eeb438301abd695b6f048a1b5ca2023886de887e4c97480e272859f0b9b38f0c8a7a0e3495aeb31b1d44e1e099b2532c3ceba74984e18b6e92bcf8d5159eb16ea7d85cea366a522818a077cb9f718228e5b7307069c85554b545ec4d59aeb587390ab86d8e1488532806b07f7cdef024f1e5fd28eac181cfb8a2dd250eb67127a762e112d7ff9cf3c769bcedfb5221d1cd63403b39568cefee9180a9021f0f8bf8606e8a1604e13a49608d635463ea0bcc3ba3fdfe880a5c7defcea98d852176cc3b3373e715ce30aa0ce0a0987df9dbddf022381bf9e7cfbaa159aaba58b07c7a34752eafe5e549a351850b0664801a0fc32d6647b38a0b49f88f80a402f2910baf0df0ccdfd8876b697ec5e9d54630bf04b8638c95339590eeeb01fd4d7de6c13a3fcb0aa2d5f81bba4a561cf997f144a1213870798cce37cca85a6721d84190d9f2610801536b2ed213f7b72c23cc05c62f3ced50149aceeb5a21b5b36703354a42ff4a157ce2006e7bfb9e50e40658c97ab0adbc4304e444117bf15fe3a2ca2a78de18d915dc87cdc3f474f07dc2dfdbdb7da584c32ce01460a1773e3744918e2308b8753b31e4edb1b136a4fd0966b7c842e9641d6b3afef2c182061be14279e196845c715029dc21252a181a5c1883252023e447c88fe1ad94b18b31ef2ccb07010178b61b7611e69250d0614880cff227710d150757c4837ee649e18dbd3843a923a2df34b0dbf82559acabf4590a04bf6812af248e5689e612e11a88fe5c24d3ce4a1754ec1f61674ef6a98468694347c9df6069fd2ee2eddeb2913980c4c62c2f1b97342603c3183b1364cb7dd756eb2ef8a464481a9da05d2b206c9eed196036fc478b58fc035999259c92965ccc26ca694d194661cd0234a2c4db85da661751618a7415c0fe64f14559713cb84853de1db24398ba8caeab81958c2658d1ba78ac23cfcbec96b2c0a24230236760d3da3926a900461a8a1a62d5b1341b08eacf71ab1d08dbfe50143bcfdb069d5c75bf495ff6f98f63c4a84844d7a65409efe07cc9c3f2c67c232556e7005143e5353b53d7a26abdefb0d325621a1dd0c587323349bd57fb1e082340a788073a3bb67c2d0763b50080887e786624022eb8b3c8be4495d20595378c971c079d704b28121332784f6766ea7bcd3f23f3e5286aab3ee94174131287962195cf5bd2138a567d9d47ce4c95168488ac9382cad4a25d1716a0056d43acc83d7b7dba4c5d42487f168a9c34d866209871cbfc3f5af3df77ba506e6dd582e21e867f74a26a31a263a1a126bd161d81570895ae3e48e7a6b210811331aca6b3b1f675681305f656aca37bd6af4dc32d51c2a158cd595d1e0a9dc771a50cc39db78c836ab5e6841ff4200bf5b061219ef4ffdf6d31f9a5bc1159d4407a9f607b31cb1f06f1cac9f9c5056b5055add2fd5999820819365b0b6481c2d944ba9bdc2eb1ddb6fcb2902e87fb5b2c848c3ae2714f1d77348001f5e6e0ebfc18dfc0413e68103275b2b1dd301f900788534b00f5b301906eb38981b338fcb368f1d04b7ba73f56a1c006591080ed1f87b7c0317c1cc6132452ffb1a933c740b0544df8bf45a0deda8b1656f8493970c416c2e50fb7130591168c2fe9ee176df6490555421497d7d1c944bf5e079fe890a89615f2b574b9263d2b8a59974612c5076bb9ae5fa953ad5909c0a73f708332671d94906c5181f04921dff2452f47a12602d5d3a25018771f4621b37ba35bc9e6aa93561f53ea001d670863b4a7556f6fce9834a6063e1e0bcb022904e8e2249c360d911f8e19fd7c7773cf0c70c3848b27c2933b75895c41db11a4ed509b70d90669416c05d1facb869b7406a11239b7cb78f1f6321b70a026748d0b6bf0a17f956afb50f20fd52628242cd4cf4910df09516801e232d35e5a09d60c9c84e96b52167753f6d001b06a6a91cab7437448d854a2bc488b48c1b510ed721e42af74594d81e06ce5f831a8fe17b6fae02a6a3a25b45bb78d042c3c4a72c7b56be30852490ee91d9d72fb509f4770b25fbcecba1eebba786c6cc3edf65289a3a7d521a80707c00f2252f3cb20d570184d163ae7be0f517e90cd2787186b4645eed11c721c4badc09a1f7992ecca001feda1d21f221c296cfd21d244b07db898f67f6396a27c9156d45bc221e26c3a45a1ee78e9db8e0c194343001f898ec3e39453a67fee72804bf9d853939c46092af30107ab074f1d3c0145fc4a67ced4314185a0e28a165c5d226b01baa30aeb2d839e7354f15024b9da181c1d7f7610a0840b99a5e1302ee4ff16dfd53a81185a9d7a508ff0bd3e2071b5dd28ce329d06e4b90d483ec0cb0c9d6dca1a95615a46eab3059ba8653d2accbc04170a2de77b35e7217ad9eedfa1d2dd3f5e37c06a95ed33c80f5d0b155c9d2a4e34e01f94346e37c7b0d06bceeb34fd501be60430a9f2548832740715f2f25ffb2a7387476dc51894fb89b1d9b19d0020c853af062c5d5ec0f08a6394c5fa1652bf45e452602570b90b411741a59041bcc8d120fc965b7ea663f636c06c0a4c6a2b99208db60f3cc22f3906e24d172507f823311675b0dbe39fce4d234de175d7b2734d4723d287673f1f36ba732e7508eedceed03ef889ea5f2d1f84e6e842c825682a987a6b4df7486e7d6e7fa1583bedd291eee7276789af29cc3ed61005c040c72c0285e8fcfc34813658b07a6084612e15ce429ab6851285284fcf101599573a2cf33c939599da526772ca7f1ba167f88b868eccbc15e65eb5cca9e18b855521bb0228156651247eee9b28b2b03c0641d137bfbd33585152680ba7a8a414562a0401704e8594f0306020e9f0685e3d7529901241ec5c0ab85978c7a83ef088ffdf341bca436910f1a0fce055b7cde39b8f6c2ca2091055707f606505fe0536bf79a0c628090a78533febacdaab21760297e567c1faf220000e10e2e99f30079c864dddc11aa7df2258ede8a7c4a6787e79799436f77fbdcfd69629294d574f3cff9034fa9edf3bda38615f570a5840f3598732c584d88e2bb2a465a412803edc11d29cea511fb217f0fb154a8e08dd6996a0c083dd0442550348b869206809fe5df92508a7474851ca81a92a46d559d040dd8680c008ac08aaf8dec735110b05264928c52c24fbfc91b2c665316f7e55fd0bc55053aee5dceda0f0e2b120aa0c8058e3ce02454d917063de20573cf9ba48884cd02e6f1794fd090eeaafec974c7a15e1fe9f412b78333acff701f8ec7c277cd8e244b6d4f100750f53608517fc92174e1a2f25e3a3b46c94643d90e8a687ac919a4fd293dfbcd558452fe720c82856627d187f87a2c5dedf56cc1510cdda7939060fe2c66155b63957fb510aa4f681b626c392441d5d4a1b482fcf4021c7dc68d800c4e1c24e9ad1b3b4de3d11390b54d2d5a187b233d9969ef323b3712e48387890033ace1acd43d72ece6212bc3fcad79a5f76d771b218124a65e863a238b385b3f4b5f925dadfc1b9c1867a31f623eb73c282b38c599af4fc9fb2d06cf4be8a27101b62cb24a46fdf6d7b9828890eda25228c1e6b5e01427655176c03e4bea1605bd05c351d79f270a50b794c6779ac7968f4a253656d08ac8d21d5ca401b336ddc6c5fbbcf1456d279ba069a2775293832cc455018a380f9057c12a93514d533fd23061067feb47d571b6eef982a20723c4fd626337cf4ced5fcbc620725085aa8965fb81bfe7a0589ccf93e31233bf39afa83f099fb4c1e79219d3219e2c696b5df3530924dae7f3068fc2a6b539b449ecfceae04c03d014eca253180e2f7f91dfe6f2c8144427955944d9c3fe94828917372d00dedfbf6173298e29632f2c3d6c6faff40cc81bb1f50c069fc77c91cb585cf59885719c290bf7cb2b75be049eb3efd396a95059ca5686eecbaa0064a7165f3c9f45401d32def76106f86085980fe11572d8dccd7473ba49f034347e6ab34d22f02ba4014382c54a145477b9cc1a9ea54d30a004289ad6665bc7e391659fcdf297b167718cc3a5af31928b0bfef88128362cf28edbb13a67913b5029b82ad349eeaefb176301e765bbd46475231c8b921a1aaa072068396da180b8caabfa9f947d6350fb8d3488c99f61030b8a0881d96fde491bf09a0c9737e7a31b2e20625da2166ff045a61b42cb39687fd2877d316b505df7f0807f11dc8118aacb56d6a1af6c90f44228c19c5e761d527aefad365ce8d50d8fa13c9f40457e6718086d3fa4cae121ceb4ac270214463558bcefbc7808d71056309be84e2659cede327d07aa7b59cfac95c438a801f0c921093011a0b4175812bc8d4d5874c8d3e5296de8f9c4deb4c79665d3ab1c952443af6b451950afa9293ed22b89edff93884bc938e72574f66337624b8c27cb46fc00c4f84202b526135c15169c4962dccddb6bbda55bb0b18a079e0c4bb381682c42ba27ab6f11cd999b62b5060db8b3354e5cf84521f2af8c05614fcc0cf91515e91537e587ebef3bb3a0c976b3436ed385a4026de29a16593744503b0a30fbb5add02d851594e6b3e5fc914983eca6a572f51c025361d3e3c20c4ffa26355af4326be333ffbdb0a5322af292887b90ed060e8b052ee70b60f6d826b4c75c89a94a3093fa46f91637dfa81fd7e07822c77f7e88c6885463bd01528a420857e0071e89b2ec1003e21fa931c9ee3f80367faef174c9d9887e1062c5ddd5c6497e2686b258480dbf1f4deb1acdcc02487a340843889111f82e1fa5a58b3ee47aa202f3a614c0771e88e5b503e6b40b913b6d3174b47932b737b349303ce07b62e7b3577386c99c4d2c4076edbe4c459728bd9fe57c21c6eff2aa4e3f76a0ebebe434e89a9e79ed98a6de80236e8134243f1b6e828b0d7a910c20fd50be70b98bffd94278c59fc6d3c5f40b61f2eb7e41c64bb03bd87f520633204d60346338cb11566b98431898ee08155d205ca1550f140e07492ccc8e6813ed10de83730d6891f2374e48227cae408b9db53c4aafed1fd77a54cef20c2777ad39c6c30e4cedafc49088a4a43ef267f8f30afaaae01386c4b7d754f0ad31dd909d34c3da32ce6c6bdfc2522bd72d43cc7f8606c29dbc161d0d0aa025be078585826009b284ff089c73533d9d9f5bc953664a5fcf9568aac7c3e54aa94addbe1e54ead5b6925956be0a77795afe6a7cda311c80569afe424c24b86f456cd29cdb08347551beacec138f824976537908197a959234cfbc09ce1841258d8324fa48c416f26fba42653e2cebf0f6e854721dcdca8b482a18542eb1fca0170cab1257f982702f79cf8ad849238baf659eedc36bb753560a07d11dd5609f0bf98706b7f0e3b13bdb63f0c59406e6dca73ab201a3c7c799790bac98f3dcef77bd53bebb0f2e715cef82988a2834ca1f75b87a10a902cb89d42a6b255943ab542b5c7b37dbb5b2a8795dd0ed662eed0fe0d2b5c09748ff378a9b7a95d5ec538778831ac416fca3c42641e381cc21a6056acd516c86afcbd332b7cdb0d7d22a4cb73ffc2a86df00c38c3be0c641f66f490e93cedc5a3f648e59fec32cfe2abee6cf12de5f9da13912667dd9e323d1c32f1c935d6f856444551fc621acc75eb70ede3bb38ef740e6f228fbdc01c7147966cc379ec436f59f38734a6bc38d316d0cc232aba8be940ebd0af2062ad5f13365ecf2d9ace2c485d9d550085603ee7a802fcb4227aef875b6931a78616210ed38b1ac60387bf7a62a793656302fe86012703e3b0104e91028dc443d144a8cfa4b3ea55bda0a102ac10dbb51395d5748a0c768280684763d0455fc3a8ad81ab3821aaadd4b0b51e75696b0185a7990d780b0a3952429040442e76bf74b583d733a0462b6e2f2302e9cc0f7f782291136147c5de04ae63d346c5d47c900bb97c4e7326d019ad298eff44d7f4d351e875c33889e4885cafaced34ff562787c3004a79a568825ece55113c8d2e742716ea2406167e0e1690a480dc16830a0bc80335b5a1d28cea029a96b253da941787987895a41d88edbaf4e71bf53b52492a52c9df1482b7715b793d5b54ee9752010f4abd71722aa57cbc793113b5e4b1ea4492e47336a18501385c386d70b2e46b425e7b71870f6f4435dca86ecc81e7283faf857b5fadee2a512717fc031d803f65b6dd7a6d9924fe6e3afde8957b156b6e712ebdfb1f9e505a0deffb832394f9580994142fea3e5716a1afb387dcbc802cb9028a2a5063c148374538082c6c92859e15b6ed846cb4dc8206835b0ed5b1c39fa91c2b8814128868452f20e17b4ad8635f796fe1d56efb27c223617838529ac4b3b153998c40b1a083b440dee87812e76eecaa031f40840a33bec2110413be7620882b16607c64cae0d78b8d10f32a18e62750b744152a5ebc768b0f83a2ac85af02e07456240f836a086b9e341f4b3346164f01f1be4858fb37d1463ee1063e9b02768570f0dca05696f839384f4fcb053300822af0795a71dc0912605b00824183b3be9d9e709e53851d8fcbcbbe541cad3d2f915a0fc62a185af17ae84cf9a80e3f36a6cea459cd45264232372bab7e03e5069d024b353a03b3897b483696a4f9ee52336632bd9cc3aa36ade094ba51767aeeab6ec70d5221f82d1a44d453d147d219e30a018a78cb6d569f6b6499b21401385711736adcfb01c6870587b2beae740f0e5f84dd1b986ab6543b073f5b8c338ec3f86a42f5e2ac17fe4c8ea6d1f6efce635dd24ab6e6778b00c08c21b2d6b792a9fabd979448fc8845a97e1cbcaeec40ed5e381aa5cb0706b4df8d1b930b6865e6160361378737cf94122d34212716211228832972a1372e88ad007bcb78f68cb14fc0eae8b3e8315fc32191037e43a45ea3d861f6d930e2ce2e22c6c4547081db0d86885beeed3d2dfca16af4f0efc75818a673af204bc024607c6f701ea2ac5c8b372a3da66c9153925914dfef8b3574f67bc250ab1d0a378d77ff5d36ee2248ac73c8f8260fd0c860ca48301300866ee73211bc65b97bd9e1252090cb6d82c74a318c7ada7d20993a151a0668f5ad3aad626c6db88c0b89a3355e0cf48dacd17402ce55d5e8e5c7e968b0464366e239a8413d33c632279b75df5b1e67e5460f2d313fec646fdb9f70edc056e7739e081fc30efcee0bf15e7e9731db9d6a01e1061e4c7b523eb2a7f3b2ccf662e83b837c45a4119c78feeaa47f4990def4cf5e329f60615be6df4fb4513b234174b0909688d3803003b5ccb7dc6448ce601631e0d01e171eed832f76b61e53826d61f6b4f62b940b8835aa423f3a077b5bee6a20a809291859d5880bc2b80c16917793720eb694d6696e713d5cc1212ed547ca8f361cb08bed9689533f2ec3c8896b4f354e94fb8220e9e837abbe7ca68b8835793b4c069704b222f95f6563fa9e6cce67b79f8c340bc3f7f008a1d359073c2b4123dc4819522b34d6935d1e1cf0959fd2f3799dd899b7a32eae6667fb3a4ce58538c1ad9b2356db6b9f0c862effa84772a9071055984b865c34e1ca7e83f92c0c0740dae0ac5ca47b1aeefb40e0f0abde6011a61739c71bac32f80c62feffdca9c9445ac0d770a7391ce1650562113dcaf71b2a6a232dc0e6f79be84c34ca2b7ffff7f2f9fc86dc51e60aa15a399a21a726db5189d7264cce2ace2839a9330368f4bd82ff22028fa66b4ca286db01f5c6aa9dac79c7ccbb329a4da304e244161bd3b3325c5be82fc41ccbe042c351cd03bcdc8a0a10e1143fa8cc038dc61975266480403ec905b6c0075518585d71e7aa4bf1df2c02d5a10b5dc2211c1ab10119ba1e7f61f0f0535d9ea7130cd2abf40504078d9a3825a42daca66853b08807fa4e41118195d4d7221c5e221618fd392301ee403a6cad55f9201b49c1a3bb2df64314986e0051ed50109af4f4f3e8b5624f0f0d672d0e8ab320cba18588e75286b7762e779c460c69f880ceb395de5d721a6bff4eb42a8eebc7616baa751164b1a54ccca8604cd0294cf1035250f96f44533e6393cc039afb57c2042515dbb85fe655feb0b3fce2b61b755e9e36d5deff76b93d04567f5e6b422887a09bbeaa72fe9ab96002bc7f6ebd00d552028ea772a3b720f6b1e5363d4d56a314cc29c2eca0ea7069367a5c02d15d0b4d46758c3c0b06cf9c297dbb69dbea6cc2e6c0a92af6ff96fbb64de32a0ee67b54dcf464b1bd56ef00f71d384d38c9d329fe128c9d64262eb73170725ca0effa96418e9ba4d157c7b1400212a415b5394a3e2efee419103f5d86bedf4cfb9f578a6ba712a16022a8c0bfd24ec15b9f77230f707a8f0f0c91f14fb7fc0b57dfad59970984de7a7ca629825a1c84981ef3b9d0d66496fff4e83d18772ededabaeb347802fc745ffc2c43ec1945f5d6b756c26db2c7ea1665560a588029581c094ed2daffdee302b2a01d6b255c3c2673d7fd54ac6dbad93263791cc4ddba43b485b358178877ba805f2b6025006b3539498825a34c57312b9861e573a7ce89ca073fd19e8f09f1a2e6ed6f27d5d86cd92cc7b253cebe2932960e8caaa686ce9cc42740257a247852b62f98075d10a08a4a4b58e47888d9690c043de3f6bde383ab07f7bd11006d3286a36c226acd11e1b1906c5f83efd4de1a28823e2b743eb8ab684d6f00449bfc0832efd217c9130fe8429b7a7f44818c43c050e948e575103d1b9f06026c6f0e2df9a8756606490856c8bde69cf726b7370a3378563175ac84a8c382f859f51ce670e3e1b759bc24795e49261e16212b869d733d98285f5bce2bfb89fe2c3a9e0269cb2b67da9d384b68a03628dd7fb27681fc51895d1799919a5cf8b05a6ec53a0bb510df5244ee8851bb6c57be96a600e9c03f5932dd9545ceae12746f2c2be5e70fe99a6537707e12643e889d0309e5a1fa019a79234c23f0d8158e30ca241b86bc37e074554ae880a328ffa8ac9772cd93ea2056effa72fcb8e6f803e858ac394cc1ac57f800169ae9a7d8ebd80f5a904049ec5bfe531fbfe3f90280f603838b05daec2eaae6af01873a4f7bb89a15a5f99e2f1c3e4a9bd826dc24bfd64aa511a95e230d8d543b7da799de659c4a06ac616461664e4e85af6144a9fdfdf822779030de49ed4a7e464916386827e73283ad77fe8d148a47b9dd8f24978c2d05bb7d560319d8b6e137273842500035e18e58f45500efa1f3a7abe8ad29180e08ea76369d53b3e10128f363146c18891fbec5c1ab30fd3d6a29145446c913c9891dbf8f7f0e635761eb9d7471a89c4e6c484ffe6e2764601c24bd9c15f73d70afa20c70cf28a8ecab03c4f052318176eba996180126770d0152485278a1def8a9ad62c7e4f5fcab62269359fc99c0622bf3161ed794efc92e1cf4022a7ea311f3aeff9b675507139e94c1e35d44f4e3da7754f8c4c4fb7a4d3ac3a875e47ea8d21557a7165c637f2b319b33751e97e16a70add02a300ee345a0b5ec4640d17e9a3c2489e599338170e746a3805053082e81d4398501ce5423b10b6ff885126feadbe44d81aafea4447a5fa774a202c150da3586d2bc10399aa1941ab1f17e8e33296433a1fc1b002eb98e659e2971dfd5ff4edbf9d9e879118a960e9d68aaa6ed65a0220f9d53bc855dc50d1bbef9daa4d1bbb09e81b15c5cedb6749fb396db6d5529f935f883c67fa0f4a1e0dca1dd21689ef8649c7d34bfba48816c719b00ce34788aec6583cca60d6f6ee7d562a38f773f5062f89985ba0b92279b248b1d36a94466d37f1b3d77e466e8bab9c0b62a84819ecfa5529c0f4c5dd3823cbfce02651990ec43b930c779d7b4c36e6786429f8c372533d12e5605ba077c95a47c0d56905344a8162899de18f30196434be8b1068f5f90afffff1d4b4eb31333aff4474b586b80c802acd15a43fbe0f9cac95568eac44dba55279aaee1c5a3a483b58f8dcd9372e3bb7915ae75bfbf92acf7f8aa92baff97e74795148d99dcbfa8e180b8aa2919694ec63e39c3b07ff98a45c5ce696a387a8b4e2284172f3f0a96a5ddb1572bf1406248716df27edc4068f3e6b468f7c7f11c2b63a693a7ed6f28436975a07b485634042f8db50c3d0a0f46a3f719ad7462260c9bf42ff182c38bb06962c4cf7cfe9f697919238d9977d8e01549865627e9ba600c3aff16161390de872e94d02d5ad5e92ad76be30ee9029d7ff177bedf855c959f7b3f51dbc568173b93e461fbb0cefe409c6a9a1a545262c462abc8add129cb92152b7d434064506438d42beb790b7bddd829db28d385853abcce1dde8eb3cb2279fdc910f019b65117735a878e8bd412ddeaa82cf9d42dec20242dc3cbaa64e6e812c6278d540f2b9d16b3b81e7f0277e3cc7a0cf95931821089518f56f4723e8ae8d29eb79855e0eeb484522f41e23efaab13cbe09621011062a7c7d5154b5ff03131a8c91621f478d3265a9082e0d07014267a0f02d9f9fc80555a1abb6752c0ec96865a40e05c0b489ef07697ac0dd9604c05c30e151b05d12bd2a0c1e1ac36aced0b286da1bcf4fbf5e5abc867619ba5c2d7a4293a529670802dff444f6822d6ff3de81dc576530a69354ef58510813e308ee02604ca470c228282ff2b2554af1b5da533c642f2ef76403488089c539602b07726bd45a6166b2a8500c0fd74ee26c1083723eb8bf4df4d5ab483e8f3435e75721b163c8146faf88afad07f2b348e334898fdea807a0919c1a4bd21d8583aa7e48be0e0c5523e931eb8c1d3e5842714c963181fa5658a2de73d7f0710240d031e07975ae4ea1165193cbaffb6210144fde14488f32c7e559412e2969e99651e29c7bbfcf262fc3997c4f479081c3730a0d135401e78c222b3f8bee160a0a0d0a011c2f0164b224ba43ab1908462745d39edfac97d2ec4036f6c0a0d2a8e952019f9d0ac7e9c9be695b2ea2df6bb7b36419afb1de8e5cceb2c98dbe27b85915b2cc5b40ec3d03c181832467a103cf32ad5a4a1db4ef0eaa584153fc276a6a992d1765e011aea880f700fc3948f10a20f320181cb6c103d79c34e93e65fa4920bc9be7032f99344b13a02aa8c1976377ee1b847bcf3a9524e137697dc78793b0aeed6410087b1f24d5e10572ccff7aedd0adb60e9b042baaab1ef8657865962f28958691a8ff422b6ba9a03791e1db10e4cda3d17eec4d62b56e02f56e1411a4614860c9c937e8d5555ac10698408041be2803a8592a7d38c56a1f7643f34e75d899d2a0a9817ccd81dd813a77e31b58e5755fc4332a201b0aa5ff2ef9289476018c331f165f2d4c0a9347e238048bc1e9ad37f21fb42c3eb351f3ab6d8392750b5237c7c0fbe218671d045acc6f23ab4a35e25f0f8bf8da5d0bd22855513a155ac29a8a0302d22e35ae2d64918ed878862c294109d1abf2638e255a1ed3b3deb10464a4eaedad250d3d130a9adde6f83bda0dc2a23b2a9c007ab7aa03f75983c7f6eb9c4d6ecc4162ebd058d442452ad8bc49016991ad4338056623a7ee5198d0e54bc5adc04557eaf5d5c730e162e37722fd385a7f4793bf159600ccdff9a29837b0356c5a542ca5560e37eb6fb4ece57d1b64729b3dc95088ee3068315bde7eb4724c7bca0c0f11c5bb74870ae635185045141728bf708a0b75ed7d33dfd46c1893fd68b613891f687388bdd544516c91f7c1a445664687b6bb3c8a44bccc7f8c33a1b87e4a26299bf3dd17bb5377d8ab27fad394b7d46c5e50050ab25d363a08ac0225bfb017c45cb1a0011c6bd55e43f7ae25dc9b6864cb9514e15d274ee5b592cfa0f890c772a3702ae09221513423cdcfdf1b9de8d1a44d3e87c8440674f8a61b41a6b308e89211a3424c406fa557c2a494874032c17cd88e4db74f6e28cecd722e498446474bbad9d27b7f54776e3761ab10dbc327bde78366baa0d1db5d39e872190f627d65ffb632dc9fad0e50adb99ac7f9998da0aecb59a7b046aff7a38bdd72fbb4bc7bced8666d4e86d4b6b8dbc40c0a8b2cce98b97a5ae9f41f4b3b6c3308674f890ff0ba8a7c99619b1af214ecb373bab204f551807b9c3c5fc3a528a6e3a77add5e5b3bea0611bab37f96a778bc5b24e848a6f367db1ffeb7fdcc36ea23ae000f86928788cccf82706c6dd920b129182562c8dbd46313d255c266bb537af4b1b768e0662cb93f483516a8429843767f73748873bbb86bc144ad30a81edb91eeb9b86beacfa1008cc25c0e1b13b0464164c2ea368561489c2421e51a80feb2506ef2f36fc390bd034c06d57c167ff421dc18d338de898ede2a7ea87590f755ffc5d93665f69d4579152f0c23f82539aec82501bed1e49871daf8f11d99eb22b73497f2633b78d3155e5bc151330520f15bdcea1e7f0944e77cf4281ac8d110cca29273b3dd0b436194e179eac5c60016008ffde897f1f27d91f08187ea6259ec160f0865d306b3e4b0617a35f4f5c787ecf3b9f50d47b2898d270579c99174442800630839503b87cc487ad5368d0642a1242eaedeea504bda4a45b35208174d3185f8d8dae1c2ebce4f8135c73fb37123a30bf9aaecca1b7e86747c1739b7d5ba28b37bb1907f7eb803b24e43fae983647be677ce18cbd5b693a4cd85217695f47f0fa70d0c27dcff03e4d681a250825c71aab1d88ee7e9b1205488d563406bd7c0c172397135cb5627176c0a26f78d091da779f90c88e782845b6526615ad3fbec982e5f18baa2d835a48984c22e3af8730afec8f461f57217c9e3518a21e038508f068e48b4cb7a5df5568fff052e4770e139e9eb60a19b62f5009e1f0c18a54f3b2fe3aaa274e767b0cbaee6e9800767d82de563d1a135649bd0a1fcbdc5ac89a4f72f44951a9bedca38ada187118b31957188948b32d8e0237d8a1a4a656bdb7fcebfb927d75b7bd68dd2bf371e4fa3147a381f2854138e8e4867080b542fb3d3dace18efb929a32c1d0bff1d5269363ba695a8345062bad65114359e45fc49ce5fa67fa2dc4903af573e076d8046dbf004704203cfc434ec99e434203daab8a49af1ce43f19adec01425c741913f5aeba5cc7d369a27b168cbecc329b38dc48b2de5bf8f6140fc1582efd0e19b0f3538dfad580156d458c175f31201380c55288b301dc313023baa585f8ca149e9537fd5b599ea9985807d291802f4eb48f6df92142cb8ed81df9849465fc65ea30a6b3d7593a537320a4152425ae80bafae36811bd793df55ced6ad58a8c43440058920fbd9bfc7297872637a0bf5de31a213d91cc68e41eecc5ffcb95d3a89012546c8f01880d4906d56310ad2b4720980735c2f1bc85d320cf0939177cb60d539f23517669c378a6280cf5dd9c25e983736d0fdc9482f68b9eb72e9c4cba3f5aa36000c7436a2a04bfe882c4a4c836c0223c384fe678c429875022cc05756b9de9e882ed255006ef1d4d24012bc716951fdafa3147cd31d9e9ca72949420e675da20a7da2ae0108e8ef21229b0371a1a4cfb2c4965f0ef574382eeb0c40536696a960a1588daa65daa9c034ad8eed7e1ebb37dec045c744c70549cbf95cb722968f9d164afc140573840f02435b35ed82e62d45ee7e9fec567ad7ad368f3d204ca1d60d8121622b8a35bc7fb5f619a0adda306b5d91e47a91be2957b194ed9fc99c23e3812d30ba99995f53d2044670fcfdd2cc31e614cee1e6ecb10ca59fd7f30489d37a5da80b42b39a4ee2dde91f428091d660d0bd7cf01a0dddb2ddaebb9bca842659c706bd3c9c9b012dc36e270e9f6eedb38402b9149ea129619c8e23343a0f28c814d880013d5396d87a242beacac0d1b6efde9973f431ccf52cd0b2ed3ad6eaceb2146bfcea1e2e601bb3e1aa29cac84660325193df0b0f27861d9d5f545fa899239d0b0f757297adee7b9d8f4e8d98377746481006421ccbbde87b46dd049024107ad613774097c9f6643e90c69b3fb942d24f214ed258a2a1702fda37cb9418b49918b58d80a9cdd18615625d1b5156bc4210da53e0aa511ea9130a0f4b881061c12ce61eb17b38361f82ee1641c58bf3d59b2710fddffc21026611b32528b196c686e4f6db72ef4f80e4306b4c7758246e878fa8aff39bf7f61013a680690e85e997f4eaee12240f48826b39e431933342b900a74d46390ffadc4782718d2ce519b20c817cd56c450e2391bdd2dc02ce972da75d67f79b0d7a318f8479c7175386f041ad5d724ff641fc7867f20bd2be993905074861fd3a98b4b7c2ad4b648cd9eba8aa4d3e853b6830ff5cf8cbd593905771e35adb63f5ab46816f633afdb0ad99e7913b4af4eee974e140c8e819de74ed7752687876680a37455e36ef74065328741a27383c82192cb36a0b657fc01a0bf6104bd737020ac8662edb42b6dc223faa50bfa5a2108f86b063e331ed91cb05ded6ea3805a945e59cdd103749dbe1a161ee0a55168cb631340722957d5b8bf30165a51fb5346ed3770efe354b73a61ef25b4fb7b93dae2c8dfb4b5cd34250f733a746e6bc5b6a70ca04097fa39d5fa02a37a6d6cf5345228ce5aa1b9fe794db1c93837df81b0700e8c235a9820bb1e6c870409cf54e4168ed70faf7f5c113374cffaccc1d645bb2db84726298942439d271fb6febae20db1d2d1828e038a5137b61463ed64468ab433c4cda10d544ed9c399b7c37aac2adad3c07d9004cf986a3842b7d430d3d2f4e3fb3d25b5212a946c96eeaa35780376bd3cf6c589eaf2d59fb65cbfd32f17a9652ca42162e337448ee51b5d6ca7b1929b4fb8df40835bdc5c9c1ab5a0be2b2e5f64a6154cc9d762c49140be2269d544c41427c252b9edf5f716f7dfeb9b484fb1ecd518d0223f0022745269cda60900e4beaed49990dd0e9ce195fa973891e221a0f52c27fa9c91a47766934bc68b8b2a10120c71650bb64d8c00bdc78fed143ccad3ad9457779fc5e102d3548fea9ceb990f520c45398ec3ee5ccd6193719b31a7e163cf5460d37c1826b042b48c80dafb80996bbf44ffeb21e23710a2daeb631cdfcd6a026d8b66bc8358b855c16b02daae2d8dac14ad448dc277a3ae25b5625785b5e6b23f10466f18a35e9bd610b76b1a0bc3db4d3f21d7d2bc18233cbf8a1158a68edeb9f764a85b736c45b491272a20b8353398a2299356f9b656b2cf0832c9f52a22e4f5938fea061e4122d07616031cf1708390685660fba08e718cfa7d53aa13a939108b320b83bea111df0a1b91d8f2682ad8c74bd464438cf781f89a0b9d677811d2edf9664039e3de59b7143a05e250485b6e27314bdeccb0b05f42b4ff87c6c6c5101cadf43c8c6e09d8dd8255e31c9173646a66684da1cc9b0a000400c0da6f6281b9886f9eb4a10cd4aedfece252aafe4e609d5eb16bd8caa102f46378a1655236658e9a20e8e11af8e3549d16ba9f06ac841752cc9524cf822ec316957c670e2afb7e37a23ed2a1baa7372780282be06c5d6aa3e101e46329bdbbe648df822f8564998173e0cd5e02bb8624a4ec340e539e052694928e84473bfa3d336a1f3fd1cfc21b38eecf7574c4f043e0d1f14ba5a6605e3f65d0fb84814d7f249fbf91ec0abbe643b96abb307df4b56774ab8d846fb2e31aa50be1e8d916bc099fa289bbb0d9132df9ffc1da2a98b1e61a2ad2d4fd458f6342661fad75901be21b073c48500a99f6a25662c267e4cf8916bcfd03622be6602a82f5043d8a7c857fe67a28c0d1884d65354307df2907b064c5073c377bb808cc4c3e2e2e615c2c18ec5ec330069c58adb2d56f0704dbbfd2c8ee30aa1f0c61db69589f7f1e3929baf5baea3d775f600ae84e2e07c70dfab87bb20f4bca9364e6bdd3ea3d363a8c15582207c8591c2c9424f1fa07c910030e5b4bf826b1d4027a4156b0245e413376c1c5083cb05384ccfe7d91cca8414fa651cded3aa4bc0dab07e7a6ffa944e1a0b25840679e4f6f85857b9cb1c9844427113d62b7a8a59c9f2cd8601612d6f11eb24af306749ad5394428930044c64e4e817eb7dd48d218532365e707e70af84f99681c00ddc043adea8c0d90193e0d0c921ec5608515e0eaa8d7426fcb53332c4401bfa6ba17d0a47eee3b8f0db9b7630786ac90a444d3b71fa6dbf451888d5503d7571cd1f0b937383b80128d724850b60ee46af4002f72aeddace5850859b20a79394ccb388ef06cbd7746c6edaccd5215743979b5888f2ff97cca11ad7048db5d949e826d8a02728e3c51ef6837c491cb0c8dc6d43006e0d1a6a5c38bceb7a69d60cb741d73f55d5b2ddd5339983575f51092b0d59653111ad48a603b885f6f2c0735ec2a4664bf4786c55617659d8a78059ce600f3d7e3d36e7791e667910ccdee62992fa8ab2d497aa259ab3bc9745da61266c7e82f1d9b24ff0cd172d5b811422eab668f23871f24073ac9a5e73802c39d293b13557c341066a0338d8ce4ccd57c4979b9a39cd12c6e57a2b1c551f30233eb407e8df9255c7d612854a8076d334d84434e2bf255902094999f64539ab8ad0a32eb35172d92c708a8f90b12d61b3a0aab9faaa267d09d9e2e15ed253060d7631050f3e1932ed83fc2b3c0c436ab7e3f6ea6984159d3d4f2017d6163e82a2108eae8870cbf208bb710737bab0097c4c817a33749d87abd758fa2a175ac724d816d68f5df2dd8e8187044e93ae16f36f1a5502aae9450263e01c648ce26b9840184dcaa569107abb4a665d50ffa3cea042ab365134750c04d42a0657f0f9c7e8ab8bb32bd21b6049f849e54f1e0145d02e999c893db3e5572f8acb46daef587422b87763bee2a5b25131b106fc68fb313b76107da86b0d767b9303d293c6de682afc7c3abc2d8838d33ca9254763dc7ead576134c194455db5f3295b716f580b7ff22e2185b24d3f7779c537f996f24b0011db8d8aedf334f19b25280e9fa71197288764777b98c613ed1add4423da43f344f3aa52e71a1545751677186ce3d0434c66d7deea5d668fc34053139f2e6f89a13eaad9c6b3fd0cda650cabea25f20d5a0f26ea6905ae30fd65f1e4a69755a500cca2d80f324cd441011e5a5351d82e057dcf4095dd21c6eb4c279950c6391b35f08fcbdc17ec4554a741460cf3ff932c443b09fbb270fe90c8e558c7ee72ac6fdd06fcea8495fddf78af9a3398618cea73fb9d2a0e4900e9d23d30ae3bb409949cefb25ccddc8416e29e597ed90fba85b45e1ed17d00fb01e188b543b071a08b1eaf232aef8f4f635e21e8a22921773256270bcf8de2d37df4e364ddf09349caeb7b69a41d02485478f45abe8952e463db17dd20721564e6b117bf0a5ba8f91787db1d4bbd499c7a987e243933488ea8116dd57162336511f5b56864fc1a6f82c705147533de16fabed8d8814eea4343b57225c51ce39934e260496428b09e60b0d6f7cf5516650178cfad05b24e811df095ca664711a57044acc38cdac2cd6cc26118c26006cd12e3a4eaf934c2677b18c9f94e72df34faf4990f4ce5c265cee6ed2b5e8056dcbdbd35016fc1484388ee95267d762430f762ce5c82f9ef9e09856f03c8f72a4138fd4616bbffe6ea733536882171ef7f85806c219e35aa14a40b7707c8fa2488b16470a4a07ae99d2b4a3d199e9c8673993e03553dac294759c6b8d6f42dc51e387450ef7ea9c83e0e46a9a000410dfd9e7464fb6236edf1d48c7997d16beb1bc3585aeb59a275b6e5ce51e1e974ffb4bbe241ffbceaf66e18763941c673cc1f42c41557d1ed2232b900af3175dccfbed3675fc51a0459d7703f64a6be74b3c56d092125fc2facf47992ea89fa0976aaf8ee359af3a6ea2b08403aed869b19bd7a053b2180b5ee0a9e8d0940a5601d453e7636a97a6e920d95b8996b69573a583ebe42110a8e0892871313d841faf6ccfc6bf2814c571fc8a57983870699d5b4124e759bec98ea775ae78e2ee856819c6f8b225648886c84903fd1875d8568579548cd064554041bb8345b02b90bab6357cefac92dc1cb9a2bd7a4ccc3e7cb062080247463cbd1a10d8e218cebcea6251fd4760805cbcf87d1aeb507b2d7e8174a55e07dc8fd669406935ba1443a4b55594363e9edad32a5102a5bf7d4b845d42b871d512f1fe051da009447488b31a06438c64538ba1c3aaccf843fd8d3b7921f23736a2a3285d4606da884c9413720fff7e8770eb2bde0e4d8d44095fa003a42242061ff897f0893692b51a26127945429b6f4bbb5688b82218ac48768055fb6059224baf7764fd4ab42926bd00e10fa1a9ae07e30533e0c106ef1cea046bb430c9910f0659ecc65379757bbf6d5fddd40d6bdc3965c450958d33fea3771712aaf9c8b81f8698e349d32b022116b8db661b151c4aa4641f5927cfae5b985d4c34c5b9345534eeeaccac8533edd82ebfb1c7923141507f8333ab8f69221bc0037899ecfe53419c5403f5b323ed9d6d83608b9f8f4ff45bd49133547509bade3a0e7816a32f95d0f243726fc8ad66f7295bad638355a28f64451281e8fe71d7165c5621685fc60998509bca9e53bbd6ea062facb9576374c9ab246b10dc599fc5836190236d649e7aecc23887d450c64fe23deb2181ca88fc7e08ff5b3f2f100d517c801c32b44382d393c03895a4001e9d44e72a1f089656a8f345619d74acf6805843c34222a215060fa12b8ac609493724ead35b01068d3e33d0acb8c69e2d956845960bc1480177d72300d4ea44130ee064594d6f4a9da8eee9cfbc9d73e724c60bba726b76612b80c579918437b56821ad8389d90e5136e4af71d7c413ceb15d8eebb768632a4e1a62e016a205c391aa2250df0c0ae2db96b223839f62c020d99b5f6fbf452f096b11e7dd3a1cb50c8e4b9521a1fb6224350a755ef71e8f1e625affbb52d86091d5af9a09432547b1090e8012d6f258ee222fb60a625c57b7853a4a5427e00fc3fb545e2a5d8ea8e77b171cdd83b0e9ebe1baf89bb06169dee8f683b6e49e3dfaaa3f34188617d987b39bfe57d826a2d06e31c9560811eb03a5fed353fb94198d2680221bf91ffbb9d659ba8fe57d65d37a0ab1face49eaddc8a605ba649d3d0fa784f3953c44f0cdd88d588caee355f29d3ca63d0532ca85d21229d22c3951c72835dac61c752a4bf9738b4de9b965d8a72f964dbd9af8b3ae15e702a7011e98f29c1ef2e30cc294f1866583265d6055e177ffbda00c05106d3c65862a0b8d5a06d242c9c9131df17301ef8a0c5ec52a6cb2c02aa34d530d86cca2c8191932e15093b0df9574ce59cdc63f8d02aa2630f17092cf16d7b675e52c23dc6efe873ea083bf84ac59b58fa0773b1a4dee684928d1309a510f9140975c6045f5b5ddcfc0d1de96d9e9fc391cecba9ebb55467a2128fe390475cb5f0abadf4660404f5d0a2252d7d82499511b3291e7a7836f6411dc894aa98d5836af994bd734cbfc7492e24b02f98ad29177347a3bc8ba7e9e37bc7f974af514193df45b0a8ff2e71c62a039b8798885efd2e3cbd5b065b3bb6465783c849e05ab8377f902a78c52f2fc855171462c4146b0bfe09d4e46f816511e0160f3883ff371c65ffc9648933774919a74beb7b16c107a3b44d24dc6206b16c539a37c269ae95f3c8726aa19d899e40eb6f6c180252786f8f2311cac8efb275405b3de4094d1e6dcaa4a649f08e0bbbe59ef0f6a15096284ba7d3d1b318dd0fd7674c09838ac68e230d0c47c7509a869148612a79f40063631b250a5130a19be762559cdeafd94a9f40a2484793347b6721f240f9db1e4c3a6c0da408f06f67d1e5bf301ac91d6aa16471c4967c86c0b44594a1bd52788a09197e6ebf729fcd37668cdfe60550c371cb59f0da53911cb05ae18cf6385a17e58c78e1bf3f6376072fb7f4ebe61bc65f798e61c40b6de5297fad0a5c3c84524021ce14030340308c1c049c8c39d125ae16ddaa55947370d7e9913c262664fe39d1fd99b2a37066128100c529dcfb7e1edaaad4b09f1b18c2448425209e596b25f72cf3db230ce9a77071c469ab476aa2af6b097ee00d6a2d7809e62376a7e5e229cbb286144595fcf832f6ca92cb2e00b45a00a6bffdf799f2cc65e3ab93472c63817bf9a0368fc2b0d42ba2e0e007f19ab0da2cd083eadc62e51b915699e4deba81c2e89fb52c94ec40ee50adeec7cd000b761cc935b2996b280e0c7a444b2128fa4cf868289263fcb8adb26daea5867c9b93ebfc8194fe2a59afcd06e69ae7c93e29231efd39f7a62b820e6eb8583aff3c7981b39e8fafcc0e74710860f2d28d47dc1327e81a2a71f939edd809a7e7f61e16a420a9c7f5602771d45af3027011f6a0e36501fbe9f5800c78539879e73930dcf391bb81c993f6f5ea31733fbc7a3f2fb8ffeedca7ddf3608fe117dc5037999154482c0e0857ff84e2ba86f89c4f2900e0a7dcadae595c5652a292cf85d4839eb720d65fd7f3433f9c6ccf5f08da8a380d40fb143234cb4ae8df9a47b9481ceb1422d62ac518f34385a2ba6ce9bc7e90cf082b13df6ce73d56833f20fe0a49ceed8b237b71b307861da1b39f8221f46dcc041146477c9dec57609da7592cbb9a8ef3405a35d5cff42eea30bb16bc0d28d79dbbd2f622e49eb54dc53bdb8e6f0777daf41f9b67bd7d7ce45794f98fe31c76983561594a58b5e3cc9681f523d3e4dcaf256eb89da8f27a425af420f3361a5a12cff422374385069e2f1ea3d61145c3446aa6203c248d1b975753ce8c94a950567de9389724730c3ddcb28dde2264afa166c8d05ba2f5414eb57e241045835b8ef1043c2582d6aef970d99037250c858aeb3df2a1cbb2b3293ebd970147e9cba6c10dfe6d6680a03285a35ad0ec2ae6c9e2d2a22293b821c5ae6780c898c9bf3e21b6d263f03b2044ad1411baed524ae539c2ffa46e179816baa912a4e2180398c1752002afe923341e2e5ed807db5f6279ccb7a15fae30441e670bd3f5e6977c2f63204881468fed91ecce2218ba08dac89e2205de38b5e76c4c502f8154e52c30966712a644ebe8a7db81f2aef9de84290576ad18042f48e728b13b3b71060813435746c16446cbd904f9347426902485fc21e93a1e1cb9dd79047ace9dd7904faf1fa1722da16b3c6305c0d6cf14ae7e2d36e102c6d86529fed6307e304b8136e3e926dfe7aba148882c2d3b93681b6f143b8eec6b9512ea674d6f30ac1bd3853ea1459a337fe7571bf1ee51b04427f12480b22b0af86a5151f1cc1695a9fe03362f0d8372770082b38380dd68afc0b8322ac4d3d00028c3303da0106695e4050b2710e2543b2d0bda5696e1c7b7fcdefde39187aafb4acc8dc3877039e51a3f78574bfd23ce76ac561da0e7804d88349ffe781da1b361fd0798146d8ede60a48702be40634e381333c776de2cb72173181f6d24d44844829a59452ca6e0677066006b29c735655b29220185725f6ae6f7d20d5f79a31f5459825500d73dfcdcd40271508a40282ef6fca54fad912b743a457ceca075a8154948708280fd04d0d27201008e402a9fe3bd51d45be14e591bffed7c30bcc96a6a81393cd7ad9ebbfb86ce9e5d5ebbfac6c89db2467f597964bec05654ba09385a932797cd4c5cacbc9a59c32fbb9aaa77a75090304ebf5c31317ad703b24a77e7f888bacd7f7ecd451ed1804a9402217f011478902a5d79781523110aa898a5ff835e212cae45e5326664b9dfe19059df64fdbb5909b3dc16d69c6fe82405c28eab6aeab236e06524d983a8254fbdbdcacd79711305c7d04911c1066c90bb997b51c47721274e25e2fee15f238d5519fbb808fb844d912c755d935740ed5ddc0a1b8d9898fb804a9b899aa571be21909a402a9dec532444b0bc678a792b6b4372637b984e34eb2cc2a7e71fabbcf25a080626b4a526bdf0b3e6292dc9b2469f40b43828f9824c91658be626f1c3be0e363d89783ebb88dd3388dd3b88edb42249da6e2ba11b47c3ee0355beebbbd3b0eb76c690b5b16635675824c825f5c2f1573ee6cce298f8aeaf6a58f3ce75c472c68ac15e86b346370e361acb50962ac62a870894b5ce272c75acd47245c9248a3d1c779a937c7bf09041cac9440b411e222b30265038a0fa212ee858c68708506a2128d734e5d29ebc125cb0b9f7089cb19f0e7f3362cff096d3e1f7a1bfea0e7b6cec3f9e68b3d1eb779b88ef37061088401e7304b96243fd0f2e1081c2564980a694bdb8709535f496b333942895ee21b1ce2992c17c125c7a536759851b4d6eaf1e012972b1c586d4fede7d6e6dc94eeff1b3ece98e92b9553dff45e4aefa564ec85d86ab55aad56ab952d9148a3d1f7516e4610ee8a445a913967fa03856191e88aae44347b73b129745647f5456535650a30636a49863e316340799848d01b4c56f838632f0c11f76b5fb35f0b6d94d8b711d1d79edab086b311d1df4f439a6e883853abe65a65ad77cdb59381172994fe705cc11f50f342a9031ef4bd3f9df79348a46aadd5f9b19db0fc655b6b298e2ef8fd4a6dced4d2640decfd7abd5eafd7ebf57abd4044706a711f473813631a3af0dbc7518912a930259b0a337960200333a6fe88a3477e98edfdfe12ba46a9a8a67baefbadc119b8972d6dfd7abdeaeb6eee4d908a1c17da681e9052035108be222ab13f0409f733d513ce5898eac3c2d0fcf082bbb48ebdeaa846a93022f8585ff555a3c02cbd4c38fe994a030a1feb4bc7ea8ee9c4ab895e2fe56143c38c41f1b1beeacb86d56bc09c7257f75e9cd76ba7b4fab1e223f3c1d138c2ab0ffee2af39f241f3e37cadf0494d99033c41453f827bd89005fb34e10c7d80393ea87df2824e4f9d5b00a6e1421f2b3e435088ede3351a519bb3cdd9c767b6dd5481693db22e7dcae0881abadd5b6bad1a8d70928eeb4018e7ce93c331c6dce6f6deba4efaafad200b92247768687ae094356330c61e4a5a1811cd1091dc5e449f8a229aed450e1039eea9c8fdb430a33d4ed96761663e1389a22892891f5800861826200b8346d3eebd2b6d07e317c0d7a13c4c003de0f7eb9390cc5882cfc4ac1353ecb71104af3fb73e5f4facfa9c75d799f5a5f4aae6f3f022fb4e95de9cf357a38a9cb3567d7ed87baa3ed50e89341afdbf2ab403bf3376ab5eb5555b657380140c72441b2100e0a289c8bd901282b881d87d0eed2ff143a70b9183083e6ed52cbf8e050fa2a4860bc2bdd702f7de911fc8cd7d90cf7b3e88f7dcdf9b03ef442d7c3edccf4f588e38b403a7e1b899c420a46f17b621b6368649e79c938e8fb3dd7beba76f29a5337bc6219b669252aaf5cc1e3bee9a59ddd547dbfad013f71e17dae01f482145104ea2e7eb166b8a1a913263eabea9230f7c2a7caf5475b4a7d8854ae80f4eab2c20d8629b8ed57666cedb96701113a63a7945d9ad1963855340c8a9d3cc654aafc8bd159f8561a3dfe6ac2286d9d23fec31ec31ecff7feb3c0cebb81089b7a96ebaff2dc655c139a5744785984ae5517d6253060177d2cc92e7ad09ddbe66799026fefc09b33477e0a7aa9f25fe36d62f48c3e98615befd38cb0f4855fa555bef9efbd951d516965aa5ca39e319da77c0e78b6a9ef280441158ac4ea2e7f3221b25359ff7bcc8859f914310465627d113da28195da081c44ad4800188b84421d60698e87909b474249e30c8ff27e416c6da0013f97f7ee425e021c9c2d437001197189266a60cb517637cb36ac25d3a96bb16abba7592a4df54b7d74ebb029d163cefacd67a98b882f16b49ced7d6244c19fd14a56b933a7abdb2a516cb32895df8256bbd5e2f9c33c618df9a730c2a2f6e9361c6542e9461b665a855065b5a408de2c6111ca7b417ca607338ed146ae4a5fa66216de26aa55a796bbdb5d65ad75d77ccc7ded6dabd6bb438c7c48f3fd2930f2070eaa9306526a594ea6d6f2aaa430454c70d183a5b8c1298533a10fc3100995380604b9330a153ccdab61f6b689b8744fb0d446d517b90887b41708a2022b8a5b403bde7b6d99c278c5b0f7a7d1b48e29c258c1b17bdfe96c544a1be9ddb4eafa5adb45b17dc89032b01a0d082e438706b6d2e0bc381a22edc5a22aa81222fdc5ca1887e17c67add64221ab650c5edb77083859bcef5826f30d4146e7fdcb6e02387faa8d0af00a500e854d4e98778e0a7cf324b1bccc24c1ef9a91752a8175326ccec2674aa03ff5661eab326877a129c2287b2a50db6c150a80accc001a063047bfd0d56b120c006cba193c10b6c118d4602ea02bbdf7003a9c8e16bb1f520d142d587e2507cd5e5e05331bef77a3e39bf60eba78fefe5b6eef5add74e3d776c1f0bb761cff3eef5ba26ae27b1ff4dc59a4c9b56ca7dcd87e3be0b6d8278de7b2e14599b204a8274cffde7f343c4ad2f95711864af1828e0e394793e08f79e17d9af11d9b085eeb977a10b5df0845e68c218df2b2b81d4110c5998baa7e1be0bb911bead77d5b6d6b66ddb9a705728a551f6de39e79c338d1cb8ae828ff4f4b53e87168ff3c294b154d340b1eff4027c7532bedaea93b5d66a71a7a8a6fb2122b72dccd54104af4f02d7755fc555529a730e830593226301ec46d1efacac14a7ee18294ec7357814a05726a51d6863057ca4373836072b4c4b041f29496feae8e6e488828f14a7dfa73722fb99843abad7c78cb97fefbdd746109cbeeed32853a6847edf842897041a6c69f4d7da6df58d2f46f7f7e6d7de4b737cdcbf7f9fbe66cca52cb28e48e8f7060fa860c1408683042540c90226720cb09fe92743087d83073eea0efaba31a397c2ee8773074e420ab413d3b134240517fdf8edb720a7dfd76a7dad4fbffd3e272b950d20b8fd69ad9d71c46a872a7c00a230d594994974eb5514acc409e2fde78328c11fa47bcfdbe7333e9bdd3e7370b052ea130a0e4c3a85d9129fcdba83cf668cfd1856f0d793cf711c473291485be74de3a00d3dff9a10e72c0f7a9b5028e27e8aa1bb59401eda7cc661efa93f7e8d1c9efb8c5105af3894d474ef11dd50061a2c8cb50f835deb791a85474575280cc78a5350ca734586e5d70e577338ee399c76bcf79e55262e351c052e43b09cea99cc24669138164bd56e73328bc4e1e494c942cc981ccc524e4261ea17915580c923f8ec5807c34ef8007f0c736196b5b85547ad2f6f3dc05e37472d696fea810e3d1085e82f6a40976841e1de7b6fad57036554983a02b1cc98fa9513e13cf0e813c6acbceed414c698086a1ac14793b534654a51157cdff77da9944ee2743aed4f1f778e274ea5df45f8874b607c02284971680d138c180e7dccd4d1bd57ebd3495b792eaff57ddfcf19709ab37d195470910274b7af754f06159ceb758eba0db7d60df8f8b5fa6bca1c993b30933aaaaa16cc9b8e5d0025e37064cadc1f751118d730472d20d2e9d714745a030eba087c9a31144371010afa688fe0605533e6ca27b3e2e3d6bad2efd6eaf5b7d694b13fa3e7f0b2ec69a4245ce1e3f7792eed82af06f78123307b0d1d4eb8168a36ac8b58f8c03afd0facd3701b475df6bad8d345b7ae8ab7ed0bd80eed426ead76813e6ce7f9f9ddcfaef3849c63249eafc13f3d8bc4f31d88daa2e7b90f38bfd0000f34a6830d5aa287ab5cd0a0a436283103d5dd49cd6086bb0f38bd88e2042b93290db874f701a910ad28b0ed44075e30d3a2a48a406d43c8410717d77dc07985092350a207bc8185c875575c29affb8073c6450a60a2c7e3f13a0ff7d097a9c19397e801c314885c8e1b6ee4bd1f268f11f570c8eaad7e1b3d2fedb225ef31a69e0d3d2e8f931953733cd2c3f1569487f78307c40d9dbeb54bbb2c8c28ff74b5a89e98258d3daf551f712cd672534bd948c552fae87931e9a30ccfcbc70a13a6fe579aae510b55ecc219ed02a2ebba2ed4ae281ed8ab8e3c76ceedd1ae7b3d074cb1cb3a679d73ce9d7669570c180fc1e7b33fb3ced84f67ce0238317d4c4ff887c3cf5aab62b23f860e38beb02672be59c5b40c40a05a6b256de9bf7e7d52866631636a896a419fe69c6d8d25dfc929836753664e51492a9bf1b1929ddc17a6020da0a24f461dd5ef614330d4a9ac92a48cca2a496595ac64256bceac24a5426754564701d65eead97d83a3f0973d5f5e70f8aca34de9d3a7af71dcd6b90796158b6ac6c5bd30f3c36ecfd92eb403e77e84b83eced8de5bd3ca59d7a68b6f9b89c4144290816823643231022044ef85dc88411044cf9756cad03d6d5e96b46575e86fda80fcdb7319a4303b85cfbeb7cf332d689197f77451dba6b79f9e8805fddaaba0c3b96d4336303fa5af3afa3cf00a8008e7c2914661a54c992a6629fb50d256495b256d95b6cc3761f091aa349a1932b7bb991f5a1f676c857bb2a5115b4e98aad9b2a5952505a7082805828e609660eed09b0ee1a95e9832b44f716eadd150964d5c4f6c694b96169c92531dce39ced1d081ef3e723d434be51181c9326562d08163be370d1d68e8c0372aa6f774065f1759d49beaa332a935a7e26c22b54835d2eb7b2b989327f1ddb0c1cb5301481dc150479cd5993138b5548d5498fa948a09539fcc2724120c0c08529d2903d5d4fbe00609f848592c160d287ca43aaf2994c7b6b3811a04a263507ca43a3a9c3369e5af2bd43ea90eed676ad65ae390391f1cd458d5b1b5d65a6bada9bd39ffcfb7a66aaaa66a6a87786833a18ca0074c442123184b0b5149f743c491d42be73f441c4d394ac0c79a0a8136a1e7a0cd94228c50a8c47e55d9127f96285004210491e790711bd293f59ef42aae3e5f55585d479faf2aa74e6ece7b1b56cdf57d1aea889e8af03bafe837a4a78fb6c34d43063b954a8113c63ef5a0c24ff83386a2beb146a7e15853d06b7e1ae8e9a9de9e8233e0ded137d53e4e578f27d4ce3be7fde9fa5e7998ab8e6b0af42c1f24f4fc83809ee53d58f00f7d1096077d76817f887fe86bf8879e86354dfa7ca29f094b83f389ccc8cb3e0559f69da2920b569c4ad610b881b473e0eca02dac2b9d7a6935625535a5537f58ab632fc638e6a9220c2135a79dd3ce49a54fe1235e6dbcc2ab5ef16a8557788557339ce89415169973cef9055b6b53a7e584594b985c4722599bf357b7e6b6ef746275554b62f30e67d2feab51bc8edbb4adbd309b4c1a879f0dc707a9b8412e583dc0441b1d0af13821ea1f226aa0688ab8c3198797f02b56af070d0d251965aa28d4bf504c7b5f93e6f5b8de02865c269cce60828f269329e3bac576acad3fbf8bfbf8d876734331029d2c441b210058ed207a2f0454821d44cf77e1c877387e4d6c2b74b2ae6b5b3a325414ea7b60a9433bf071eef40c7e07615e24db1a71cf711fa75fcb5e1f5f8b03f000b7adbac3654b54f43c977f8331d8cfdff7952d62a1fbfb2a747bfff6776f200c7534b230da7be00f2b534608b384ab9830f595282182883e4e9d26b5491fa70e9d3137756457d614d668efd14c0ba3e9b75e03acab8e2ab86d0b34c2ff878e932bdd8660a0fa685b2693c9e4dd848e4c0ff071c248dfbdf72b89d27ba986d1e82252f66ccbf491944e65c1474dce46cccab33ccbb34c2fa537eb7c7b36bbc117d46e2e2938d064491229c4ad69daa6ddd5f984329f48218599029878dc5801dfb183f2e09c6b245c52dce21f71cc85877ff9b8af3977a8ad1409bfa139656ace99fe83a6cc7dfada2c69ead1680f7b3434fb3d2d678bf1cac2cc186a32c5f4113060d6f26b7b6fadb5d65a6b7dafa6cda9699aa6cd39a7c6699aa669dad4eed5eefd32de9acea96d5b0762eddebdfa40d149c9138a0ef8f88da64f6532c5c0d8300a8542a150282479a6da3e62b23ef5ee9d76842738c5240aaf6450c137eb6570cba93075cba9a33a02149cfec6aaa3ba91755491887ae023466daaba63dc90742375c70824b33a12d48bf98bd403a0521b926905bf5f67f4cbd2e115a3306a736d14804094524a29dd296d89441a8dfebf8f732b2566b2a52a8af33561a21a4dfb21e20645357b2aa144dc3f44b44ae4d460266e702aa1449c9e02be2ca78cee32bda652b494524751d04c198ddab29e520380f191a6521c2d3fc0e97b5f6ef937d0041db8f7dc03f9781d8d40be0ea33a22de4719bd858ba8078a2847bd9acfd30ff8d9e8001fbfad7b403d75130df5783caa755a1f81366ce0e308878e9771aeb44cc2479aead4f5991e2e4827a2ef7df75bf7221ad67861b7bd880be9f75896525a2a331911593d951e2ba73aa2e5cd0ddfedcf54da6ae2a2f58da852e5accbd0799eb7736d49e2f5a4dc4fa3078cb1d6a30ee0fbf89a4c26d38f30beba81a0bb7de21887183172ce39e76abb194670dcc79d73ce4264d2434e1921b4a03c5a6636063427a125fce980dbe005c5560137a2d79f548644af9f434573cb963289b5880118529cc411d70f3ee04e94767397dd4275581ecce00b51884eb212390e4ca1a2509fc318a7d042791460ea78f116138c8fdf8fd91636b841bfe2fbbe6fd50446b38d58ad563ba64a4ddb13b5d0fdf65ccd6fa2a5e9f3f9cc08028fd147d3ecb60d1e5abc1efcebfeb371031f81e467c1841bdebf21901880cc29dd8612985372d4d148073e8e7ad8a7e5bd1914d99002b4937a0933a63e88846aa55aa956ab1e291ca9f666a5ca38965c9555668903252dcb159d5514a6c07eb0a7bea2426f56aad5a5a114e6793c1e4d53b9404570e0e29273ce39e79dbd39ffbeffd188442a6524d94826fb3813569cab22cbdbb811477a6201a9482b91f1aaee15e2a6e0b6e8ed55cbdc2e70a0a5fb030a53b106447083d0c3cd55a6ae0195478af2783c5a6b16da7584808fb9a47bd753ce34c098c9bace279bc17ada01cfa1033e525708b4b15c20b1da4214e2f12207715614eecfb0011f7349631b8d693436230a3e665b6e6e3f5ad21021127a22f3676a8d0d803456662b75473de9952db5c470719675d99086a5e7083ede9927c6690f85a16f09452db87cc8e543a29a976ff9976f79172b7cac293baba32efc1f2286c09ad0b73cfd969677096d5ebee56d46f8d00f11638022fedf0f1163fc1091b3a5cb49f42cef828c770983b8bc8c67098328a97179191f4484175d548d16586a84415e7ec6d3088328a979f9191f648467f9297e9e7b0147a397613ae1875c82b4c386d8a2571bfd644b592766616a9ec94e80c2c95cea932d89e07933befe65d9128bc7237a192c3fe367ea74f92b03b42f22136ee0dd734446006b70808bf02e8f5fb3aa0dac54e7d7af427efd43ba05c76bd32968480785b607aa439bf242d7ee4d776b583e860821cbdbb026c67fa18d4b18e367ec071c6f8a2c1f504731baf04709280e8eaf4f7587d6b3eb1d3bbc536e4c7b291c69a28b197367b9d46182f2caea880817e55cce1810055598dd0e6193acb2e8d56e3163ea97f7e472975cf2922ebf448c0f06970595e624788ee9f0909a32fbeb0e1e66892534c3a1b9cca52de198e95311add23875c77cdd18b9339d6387e863e7b17c58a28f3ae5034e1ff58d5e69525b5aceea8e6bc49d79d65a5285aa91b47c08446d31f42c20111b4c62e8515b6cd1457afd1630971c65d128d625b386c09dcd6c29e7f2ce72f9f22e3a1c3f5441404b2fa10efd3800969a720997c4084fd05249c01206a08f77160af500fa7867b9b43037470b7ea9eb22d1c474253177e41d98061405035dad3acff3426fc55059baac3c4378c5d144399371b4a3f6298c863534fb4926f0201da9a424ea0e67a09d737befd5b3fd99292cc14aa176601c0d2b78d65a3ff812025ea3cf9795221d2311810827cd7882a3501fc5b8d3c734b4526251e8c06c0f1c38e0143565a810f48659a2499c18a1021e3c42f005d4918f3aaa2f93c96430c86437639c71ce18670f6fa94e89280ab02d3ae7fa9cb6b93a4ff1ed2c48de4be3d9fd4cedc2146cb5b6a73063927dc718e3cd0724128944229148a424f8ac53e324520e311daf23cd182f2485241269ca54da2273ac9d110426ad3aaa648eeaa68e484ef8485d1e2566d73670e0f5a9124c4c990b7cdd3e055dfaf45600751a56581d21e9de76617dd18e34656cf727e88a4375d4af391586491db570288f2a441d62c6d413f44a8a8cf091bac64a7698caf5e41585e45caeaab2252a4e58102cbd93f29cdcbbf46d915075cb3969b5d59c0b2ebc85a9d4e572912eeaaa29cd84ba8ae458223e5eaf17a65a53ad55372e578d81849698f8020bbe350e4ddb5abbdbb66d4d3e170782e0235ed5c7387593b5da71efbd65a50d0e22b3c8478d984c37abbd81d02ccdd22c9a44b362668c661d8102cdd2ac24569aa5599aa55930e041b37696f04b6bb57b6b11c03ad758c2bb1a4b38ef359670dad558c2731f778d25fc6509c71ffa53d80801dfa68cbee8135694bac3be58698ba43019e84c4663df94699e0ae38f90c3091fa70c6603dcbe6c7b3d361b3b70edf5d0c1049f3115701c34e0b38e56d0defeac1e0bf6453100d99e7b110bb36f5cb86de116b2a0fdf6d55b41b3b08573be5e5626b3d6daec7d36c04dee53d2cee6c084af9852abd46fa76e2ab5d6aa75db6fdc26a2e962b5e38ca862b12d04ce77e6d43026a7cc2747123e5292921b11542d32a3a4ce988335f57dc4392a9a199bf1234952461dd930ca4749ec71dbc66ddcb6711b6cabda14d68a998cbf6cadba3739c70002d75b75b3c2c9ae2aa8adb1033ee99cd43ae134ac473e9f8fa562084d4891045d04718a531c6bf479ed7da2db3d4d26fa32eaa8fe0f12a6a0388ee0e3f7c9c8de0c273cefc0648a59d9379c70fcf7b3d6379df3a864ceb6f5b474664600002000a3140000200c088583c180503c289aaf733e14800b687240805c3a9687439124c9a1140531c810480c008000000c31ca501195019cabc1bb09c79cd789056f9203ad59a3ac8b70c46a30a447ce99d9dbb71d269c6835b89406e65005a0623fc0c2e1e4f4f60c5e14afe651c890900f851013c17077cea3f18700407172c28c512623ec379e550b45757435117cd6f87f85e95ea391047972fa52af434cbdfa663984ea82ac01292e8f8a3147c4cacd2187b539fe2e828c9b0ac697fa952d8153d626909f3c41b424084349813d64a6c13a1ee05cf719e5c0e2418c35c79a4855cf61d76b598191e68c2629e743a4b471a478670d44a3316d382e526ed1df7b1deedaf1520a04b98491c7e09cc33b6de7ae11f8a6e7ff12da86300cd53cdef308413483735b133227f3d478c7df22b1dc0aa6401221f35f5a3c5d3239e0e5f8e402a04c5c2e8acde18357858a737d576b314cf4dba599e86406da5119d61d4b42df78d6e00bfe0d246202e4a93e2f1db2a41a77691ed3605b64f3bad2ae7c405c141c5d2738e3efcc7d873a525daf90152be2205251af73d8d09ad8705d2d921ca8937290ab24716fe036e13c8db3114172adeef976bd0305f873a24eb526a3cc80459900f78abe8d480bbecd1628fc1f02134ebf1172155671d87d032fb6015a4d845cb160f59d995f8f1a33044f9e12a50e000012283bbcbd146d9f7862a0e1137c48a692a9ac3d445ccd45be773b49f766c70ea4f1c0f888d195c443094c2defb387c5487d1c670a64152935c9e75c03d7d11829416e675b13469631cd6feb19cc30096ef52d961940dffc39a1119ca53d9a48e5ab6b52dc6427830999d4aa43404e3585dd88f30b7dfc58248b31050a307893c322de1162e629b04963d6379a1d5cd90ff0c721e869a0b2f271628d8de514960783e68583308a858aeb718e767cd5c142a65be21687818e1f2e848895f870e4869cda7c209861b0361533c4003630a080942d7b6bc60f4a22780971d00c2e117e1400d15d82aa446136a1182a44a1b7ec8930e2807c8ff2d07ba864e553f945c32cd40e87a9260f32b0e3ce4c6b390534d0688669f9ea734bb6d813a04a6dbdfdad2cacdfcfaf01986585c01dc0c5a50c801cc221cb19023c003a8a04e6d1f12cf3655b6330dced1a83eacea00351da705d7f616c441e8554c1056b84ad16dede116cc87d3d20c3a83a869ea705f9471886f1d1fcbf407786ddc50d2f2d47c24c079b21d76c4b54cdbeeeb537bb0684d90736c5f8ce26b10c292b1e6c9f87f7e4844ff7094beeb675f35f75bb87f3ab92aa0e515e19531769b74152e811ef527d29f0411ddda10d427d5f15a16539569674f22312dedb42d5db87ea950144163f9e39a743cb3854fb4402901cc0ead83a42d01e52af228a2e4a8af08c11dd84f1b4fe08ade3052427a15c85cdad7430b6a6a98aef8faf594b76b2b5791eb2d0a1f5316e4b380b552f5d1b3e27e75726774f076052d7a280cba39d06652b43876674d3e9419036741b4eeafd6803108cf0a102951617dcaad6ba6642e75809eb03f9b5b112c93447f5e4d7ebdf2c137b9470978c1f3bb3eca32c4f645634d4381d5fc26f7539cbe60dcbbd4ba85056929b5c57b29ae8854010de77062cf61d984124a77e10bbc3b93f8364fee6e949c22740457d116d5bd9067568b328265c81b6ef01f3b613abd597ede790cdaac896e979d8872da0e662230cb415b917c2b5d64af5e1bc7b1ea450b8f726e2d24fdfbf0f9f5081c013ea43ba569c8da274d33d07461811bd0a0c3271bdb2c61743a765c97e9e2d76815973463683fa8bf7fc29e895ffaba13d7367a67a9977451fae6dc9974b4216a8cbce8c1adb2c248049f7471b9e9691053586aeb3f08babbb96b25a5ccead731c70245104b21aab62942ab445b9ba8d6b5ab050f33bcee9d9189c3ba7cdacb93e89f101713ddb1ac8a319d9954e245b946079fa6471c141d57cd2e3bdbb409b4fe2819ed3dddfc45308480a5733ee128eca0729262cab89bfb540d8e3df0338cecb9a049512d96fbcff258e2c40faa400337da9d95d12959f250df27d019113f3d232e41496372aa008b60385fce178c9d0d3f159a20c5dfaf21b09cd01002def8dd0c3d63d14e7191bd5a219223a0dfe299b2c0d442c4718718d27f18a1fbe8ff346809737d923669f081ad513fbf5e732ed6a9078a57c52921c9d501c8168e00d80ece63f4a428a27b0dc18a18f2ef14d84e6aa754c6b36db5c66680a3318190ea6550fed154f89b0855f246a41ca749aa85cca838fa81d8c051d7c847b2f0fac118a787969c8da08197ca13f60c416db386faa5a40919d0c5f9f5d4ca24d0464452c9a2ef2c6a604da7e6e57e09839831ae3287730cc4355c19b6dc449a26c873d83fa446defcc9c844919adab189598fe376bc49b31175c8a06d5000663c5f9bf903febbdfd56bda4054b388713c5bf2b7127a3c4ed9711c9182e892108857be8a2f0c68daba38976b807828240213fa197b783b366540df1dcff02129ec995eca16e85175144dd18c787c75577222793f0793de92a861d2203158a33bda1352df299533f8b7d6beeb7f1db1c789557c1e28008bad5f12a6a504dda7b63d58b5810bcb75c2546f2de9f370b055f3def3594b5d548a42fa13a9af06253255707001f1b556cf3fc69871d1edeeec342f5b85edd627268a8a8f0a6ac4afbfc1bb18751ec015432adf733df7f8506416191f5fc435f92c30a3e053062bf12f840353e767dfd925aeee1a8fd68223adba0286015d3f190d5965145bd6e746ba2c6b5a6f34648bf1d9f9dc3f63b63074c2ffbfc6e5554b566d247a4b01a3b279fed4ac00e3bfa7706ab4b2804b3581c5217e4e008939c943f9feff68faf1fae33ee5eb49b6598efde63f3dd12baac697115d3269ff6f926cc3cc8c993a9278e42c700a57866272f19a9478d016479af3cb8bd38523e93cea4a64ef0746330267b8e505103925cd0bbf7e8e0a5c5ecd8906061d4edf29932bb71cc38cb82eada8cdb6daca01b10f39e5e3a6fe52b59ceee150e78c6c75880e2b70713ce1b3bc58a2cfb26a7b1c2f34612c35abc87435a6e749bde532ace4f3b11d3f5bf0509aa12aa6e362f9faa49a9e7354e5cbcc3a804dd7487f66369b03526e20da141c9e1dbc11cbe0baa74d924464fc3c55a17dfd44e535695db25f0dbd6091c0a718211b11cdcebf6c8d68d989c834d36707a5188f636cc29b86cce7d55cb2fff1e7c6535dca80fbe1e4a2096c7d1f4925a2281a16eb26c033ea0ecb44d1c472765ef051f1062fc38092f031fe869d01b1e29b610f8a0d188a43b70901e6b40b59b9f67da257efb08457b619e9aeea84200c8d05b534c5b1e91b766f662163130f30ad13c4e2f15b60381338b25fed84f362cc7373e73f4e33d2062da0e14bc43bb48ebdb36bbe1808a53df952b513c41a891ada49faf3b5d6138d3ed3ebd00117e994e732e036a4540120acaf4ed1d81d3e67ed24aa7f7f167ae812354b995de540c5a901632a3b00c268d664b0684be3760c460928f96e1b063920362b4bdf6d71d250f7a1719a9c2988a66949e42a1a59c0ab988fb0d94297435990696f1b4ff0b6be0a079f42531c1dd099c5ed5109a9f9c5b6b5ba86b5c8f94a9d49c38d980735724714055a6a3b188828a8575406c17d51336393425d00926743e36523f7a05c384b406d8a950048aa5a7a121b5c0db35b484d47bbd21603ed392245f45c667fe3a1f464daeae03906547442a78043a8be15c55c681201c58aa7de99fcdb416aa2acdc40beed01f06bdb47b89fe1b23cc0369e4d1f81b66cbfb3b997fda77dda3737dc4ca90116dc4f575bd7725608a1e0b53dadd815b19db8e0ea7370b5ee583245496fd366bd6f30f25799fb8e187c53e3326ca45e2c5025679f4cedd1ec83f29712a320cb0d4b0a0e4adc281b4e7d13d5ed6bff39554656c829d22ede5f4ed4648276409cf49ef2b344a8c41616c379dce419f920522131e55acd0df1f674f00d64ae82410312648406d44666288a0208fd89bac499835416a791ea1d0dbd8f9d56dfafba09503e84143d677173ebea61eb01a974dacbee61d782c9a0b32ee93c036490b45968e7db4eef66acadcbbe452cee90bbe58d5263a046fa6a88a5f0180abcbc7498b14bcd50e22574489fc373aedd7a4d2e5c6f4d0abcbae25589a22f62115dfc51fc1fec654a281ff043f0d09b5d3c238eefa959edaecb168a0a24f257ea0da85f1cd2245ef3e27c670d075fc5138815c0ce0746ddc22c27fc149e204df29494df0c03551eab765cd65afb7a063e52eb83888814fa05f9a560d34af290dc72bb688835f0363b702c7d6924753315cdfca3c8605d00d6dba944fb32e146fa8c7d82fcb81a8614cc4bf108a56a7d0d76c8a5a8f5d7bdb4ee7a9382f1f8c836d2ba8864a43e31d565f119db7a8904963ea6847b5ca62d93791bda15946b0c011bf5eb704803c656d6f4648e1689e191783f246be8e88919759b4b6cce95b0c592ea6c3fe23920309287057635dff34e13f2d354f25119908aab2a29734c0334d724643efc2183b772101112b04e00607018a768fad5e361d68be1ad551280005beb36d6768be410b5f76da03bd55c317e60397819cbb4c03b8fa9cded6cd45f8ada7792695aea7f8ffc0a0436c3e8bee8460472a60cd379dfc34fbd96499246e54b22910d1fae19a7e3a6f6d41cc277d7b0a7e6ecfb78efb1d117095700341353b8f3c7c0d88b942cbb2d4d38dcf630d896e40691c7f4069c04df83f9c2d3f7588a085720e552f20b066530b57afcca8fb50074a488a9490f25af3446af06c665067ff4a0c16f2f0976f527a3ab7b058407ee53c0d7f9ee392400973f295f230677d0f15e32e1256542e21308a78f832d171eaa57a0b9e8a12c857f1b16980ab0f9baddd33679b7a27ac2b349c70ea6bcb9c5a2ca8db597c3c997ccb9ac532e49ca5583880a32f7b21d6a1c712ef78d9d42a0ea73d863e21ac4b2b16c830cef97d6b3477439ede5616638f37ec89752235421c9cb3b7abdecd8400ae33578d46996f8f8e003c306d4ac60fd399d11a2cd2093dc9d9485d64f710139f3846b5d6821159126cfd896b35f853eaadbc8151cce6ca78fc47c96c9a117824766cf56d5bee6e48d1a30aa26451150dfa03ad1130c3fe50d545892cc3628258b3d7c0e51852bf3c82eb0e005e61c823b93f01271993395e3f3a247e262e4b2a3973d16d1dc7f1deab17cfd70513c9e38156653d26835b61d0c8d11b03912de92fad584ea8c3322624a5c203a682b6c9da1c6e2ba095e1cd974c9df1d29eede112891f12221a8f3b413cec060404e8a0c8d444679f4b0110435b42133e42103235965b59e97dd1d3c051dfacc0eb5a9198f23d36bc2c3c5fbe9f0536d538e7ebda66b784a16b3a99a016a4e286a3441b6227e61ebe31c3f2e5243f22b121f4b061c5c486550b10f6cbba637aed3167dc4f3f97330690b7bdedebb4204e168c9631683f17ba6d30e74a01918a8052eeb3584dd5b488d45a424729a7850765f012a49c2d265d9110c0fb95da5f81e74c3242e32d021e401dfbd243c32f1398ef0954d417833bdb68ce33a6a9c006aab4039bcede821eb7649b0abac969dd07f5816e13ce86e55d5cc79ee147b0e00a58516b31855f93650fbf679d3943e27bc386e7c42d59fc3026bf919e7362be2bfe7124eda77d178603d56508c0ebe6b31201368a0ce29e71c5cf2dffc322267896c2631f2c826d586482185484b6d170088a07d7f8c0e150467ed35d5afc97dd8a2818d75f56b0a29c2c040299c1e22101f46ceca62b140e86932a791a9b09c09b85a94f0ce1e6ec4208483b3b8ae70ec6f91c482742a6800184e7624af489219558bb9233440dd35f06fb7c92b47d5895df75d47ef7df5c55f4894f62ad123bd6eb467c76a9bcdf9218942c49f5fedf127cd327ff92fe24d99889b2661ef1a9c2b7d34314c5d9d1fb47264a479fb7ba638a13007d284627d8ed5537a54ff3696aef45a01f81605202144af65f545229aa6acad51027077ee923ce68b1d10ab3c9842a51c050f4f7d6eeb27ce0ecf5c3da9604ea28721af69ec6ca705152b068e952ae6c082f9c2cad2ce5f06b60aa5ab807c343c55e2e1b0e7a62ca60b004fdadb28693e8a65711e9f9188fcde1bc51f28414113f1a206cc0151fd74fdf4824105ce48905204c54c5b9642ba3169c02c84c6ec3986f0a9dc6561d36b0b638e21412814c2627a2a68b2a550c28da1018f3844c0fbe0be0c0e2b0e3d6515b1fa447d4b9e23402dd9900047cc45f569df9614cf1ad5f583f4b15bc9036c531988039b9e52c87d35130a7a79313315dcab3400f6b67e7d2b64e12fdb19281fe763af3e7fe25a552837c30d4ae9bf6f008ad3b2bfab26285d22c666f090b1c4512ef9946db317080242b6a32dbd9c5a7586c1690a175f31bc048e2a785fd61ee3e7aa14f674bdb26b3f583115b46f103aec26d7e308929ad963f3f3676f7a29d99a202ff1b0f0302dadb400eb2bda468e5f153634003e27e04f7f75bd627b44c637558d6d94370f59c670f1b068afa4b76ba28755214d4e74f39e1c39616d4ccf0e792bc00542b5c7bdfc60c605b68f56f9d17ec37811acb953994eb4fd597aa100cb57d7d81884a2e800d9696d302400c0b02e5663bcab806257e873e47477ef5c750c787eeaced4e6fc5897389d0e3d85eefd33403e05a05cd0f1ac75ff98e5bf6ae1bc87c524256d8b95ae430ab90433a7c301a14381d3bf8b0c62ccfe1b7af6b8466d9ae4ea74bdab52d9399c74ced51a17fc72f9760dde54f954761af6ed818eabf025b08d2e632fbe5b6e8e51c03a1012e76434015969a3aab33d7e5514a4a40748965ee78dad9ba168f8fb8442e14968deffb345a4d1c20d523615d2c20d5b9580c3c6b340369fd7c6e46d7bf8edb11fc5a50056018f50539f44a4a9152e78859d45c5516905607bbe8a80f857d8794ca0deceaf6ce1d7ec4072fc3d2093375d2fbfda23a915dc81f51fa93b4dd6bf7a055cd4cf593b4c8370fd5979a3585e9e1cb18d2ce4acc7ea1bbea4cff87399e84f3c44c49a7646f102723663ec9f94cb1c13e240e20b83dcd22d0da7b2f7822da2396adadb180ec4351916f57f905e6f83d49a044a4ce3d1511c8248ab141db53f00f0be0284d84034ab1cdc38a0603e35735becd2f59566f62be5bd5a318899fa38e93022e78e15a030eab8b42bab46c245a883977004cfc55f81431d514248aba5ad10d4d86a1cc2422ec9a64e59976a341d1556145781f68bf8916a0abb7db1269751911e0748a9215ee37af5b9530588a6b53534f3604cbb2d97f250b25821de35fbd118e7d49198d2536dd292d7e4e58b032e3b332bb7524720e668e222df87c49c69da891ddbeba746daf55850b2cefdf9f790a03e1014caa6190c6cf33873bb50f251f31b4960e9ca449b85339130e540a029d58c173b52af15d9f39d92b0b47cb85f3d707cb7bc030ac1ad4375fe7226701e3a99079acc0c3d66adc4aaa2b5e46d5bfcc6cd0321c8e435a7ac2000f1514a657414ff50a07db0a299d8b33a9ed8df27df341c8bff26f163134f874d579f63f053c92c12d3db2ef8ee728004e504cca5bc7c9909fe4376f6a5750ca57291039026e527b365748ed5968f707c1a9565effe0e0d5641cbb50eb0c6c808f5f56d2488b5e9f107a9edfa088092ddd445ba7816d8453392842c893cf9e459f45f22fe17c92130f30fa5f0f28deb44c01896f623e800f4c339a2424caa6af7f03644db8053ee4e056d7a78819a93bbd808a2e602e8584ab363a5c31813706079cd128bae2da7c0841b0048f56142348df9068c41f458d6bd30e4a81eaf1e3669b888dca48442c9998db5f7a1a8a18492ca21b330fc8cf2ebcd40b272c596f62f8dcc3f667e4a954b3f803f931bbea96f3deb015db8b44c2ba3fc5aec780903fb245a4e53b0f6d5f10f6ce11f87ba852f17e24ac8dd94c949010dd83481d4d825ad3ca81c7800fd898a198749221fa1d92a692bbc88f57df2c685dc3941da53cb836e3925f9b1331863f1c31e88f4f6022c92c4df7080a85a01eccdc42b7260b42b86a474ac57aa01df20e4172fa66aa8f00c2cc5d81dded9b5a6100d94db19866ea16d94e846d5081d2e7085374c6808609f83900398738ca8b5cfdc8be7f85640c8caa9479f5eb420920095c3e456c573612d30c7982c285aaea99efcc92dff9a02a55e5de286aae3bb10458af58ac07faaea8571ca3228a48c43e1ed435708931d690185d39306492cf5a92f831024d8b98b95ed6778ad87d1a339d342e4dbf2b453fb68f394f612d365714459996faa1c8501b434bb4c05eba7f219ce3ee5476eca2fafd1f125b0133800d306fa0a5b321685d90aeb65eea0d43e6b578ea6cb6b089856cf9a40e0c140e934c7c16a17a72442a7ade5a26bf24eb575fe7f0b494f13343204dc3231e2636f3f32cf3f3ec016c5e8e67d0370cc90a508c1a12b04b84ef29df7341f2789915f80b14f482070ef32d186e79176690c55d22ebd6b1f38d618018039f0143904d00e58b6194914addaa591834585fad4f415b57c2522c906165ae4d130af07148123d1a2a66cf992de6a8139965f74338cc0717c68a6f95c7d64a39de1bb3ba6fa4459b55de5e681da223ec9bfa001e731c239680e72aaec60f59b23ea2de9a62008748bd691ef71f6b8b0f7dba190d7e4a0754a2db24cb5c44aa899c306bc7cf84abc06d9c8e3a27d636362e0f765077b87d43adc7d8c8f0cd867b6ee33146922f84db8ed187a858bcc7b8d33b87db2a8d7a5917ced41d7a1cdf45523b768b70ad2d65a3d4026ce00d8d0425ab3c45b5e520f20745b8919ae11613b43ed26b42c1d913c26db05291608f8720478414852b0bdbff9519b930d45c8ffeb0fb2aebb7b63ef5ad105618c28b81646bf9f79582a2e90a63142f3bdc90a1d1c4b50424a51d99881aad394a21e7512bb3fdcbcdbd68bc8b3220145e0dd5e6295dbe0c732b91a5280c025c07083eb3da768636f141404b6f7cf5425318c87fbac182809845931949b7bee05425fdd7af4fb99d27ed2b43b1dc4eb8122c19f76a828f1985cb9bd1f0cb025f9030065ac799d062293e8164ec4e621afa53129a0d58369a45a05c1e15a07d8f9cb334d700afbd242a622f903049d410a71a4d09161bdc7b9f778d466668bcdbee39e76263a9f1281023d7530ae726b0fc6b60db524459a62f7577599000bd7a19eee8ffa3516281f0e6ecae2f78475621c31593a3987641bb908ce29279ba63a0bbf48c37abaab2992cf9e75faf6e1d31b1b6a2145efadbe5b2afeddbd0b642136261724a66ec0737fb30002df8d62196344ac1bc47aa191e8f67be6c3aac322abced6c04b40b20a7e664f2eca39a312f40c8a98f57bb989b4addd4cc4d4f9668964e73d3bd3f64544f31a9a2414b03bb73d3027745a3c1aacc2eee0196982ffe91384f12cd3d20934311741a3bd8cbcd3236ee22866f4d1a0feba788410be4a604daa932be5e4de1db09907fb00ba658fd62c6543864477b19109f34b3636d337bcf866de0f558a4645b40e39e03ca0e546466821de711b848b05ef4188fa7a1718937ff8f7e1c66b2baebd59fed23f8f2a8f4ac91d8dd4ffe2472c8eef9cbbadad6239ba3a90f41c9947410d88ccc02ddfd78dba3e2ffd2c4d3261bcc40f219526b94abc75a7d03f03974f03a296b25abf56d7df5583afa4c375b7a7bc817c7e736f997a7d54c7283456291b6dd84ae1dcd129a34a5fab2962aab008ce02dd3441381f59dce84abd357b99b7ac81d5cfb1d7ac640d87ec553cc2f70562868c8c0ea68ab8ecfc6e839e8d5f27c6a4dc869ce08e500d2189e5ec11ab94f34a658449ee7aac3c518e246058235e31ed404aafbf2ec388b0c9692a155f7371f4e9e1f04b105c17a53980110de11f520b840a0ac8e1bb7abca6a09ab71483c00df58943dd8a8d19388aa07af10a89ac8b4194c62b8685174a6e75c7aa1015658437faf040530a7b01a372f84131250ec5d8cc025ebd341d1bb99fbe012e4a112646e5c2c9914093cc0e26aa3c4b834060146bae96887052d1e346fd2b582ad578f326f8fc0a46d5f12c964f3ad11b8229fea1dc50e6c27dd54c8fff4c71827b119c481e92c0ff4010dbeda115695dc12b0ba8f3ac4387e3bf93717b738761ba407f13f2f6faa8b9fed956f575051850935a2b43569ddd2ce56c89cf6d5e6f73b1019202415c87274a887b3f72c8bc0fba3364d5f2517627bc9d1bbbd8b5513033d331ef62510678137d1741ae742ae0ecb0f4d3f9a32fbc553c88bb24c06a94b1d65c1819177f36da497235c3519f78548c8017650c53bbb8c1cb69cd553ada0b130c065e0d4289fd8cb4bcf155a95ef04debbfa54f5b9ecd657722d821274ae3a9abf45854a5a67315eb37429e1463ec5a983bb690c0666a57c61bc3053001ceb48643f758c8272e7b647d7377b9a76881f07b47047a2bafce09f25865a4a953aa797d4eca2d3a485819759c59866c39a3fddbc60e45b936f7562fee6266852b8446e407b6fc13e4fc60bd94fad290bacab790884ffbae472c8443b08aff302d19ef11be196ed740e2bd9a4fcf2cbf9c0191464c2671b2b7cf943de2d07d6c2b2d1aef897e6e4d6ee1dfeef5a0397b7ec779bbf021ca32abc3bef9f09e14212b18abfa32282b167e47d9d0f9b34393d698fc3379435a0ccdd04ea3bc483074fbe754a16e1eb051992efc744ead7c51600cee60c85e3739d995bb5aab4f38b68052ed7de91ca2d8821451df98097330e6a357aaff8dd640646f08de61cb2b0d7eab089b2cb035b91ec7fdc2012b27958d1f281d3e4ede26cef5b03942937792a5f671778673f9c582c7dc6697554e203f181d0aa22896e488b5595623784a65755f13d05571fef35db71f4100b4bc95fd15a30e8a8f82d343d197a02738e0a6ba36d2c5437a112c3f21aadad6811d08641fe6d2609051290fabc58472bbca253ae651a4ccfed673308a8d5343798941e07d8b4985dbbd484fa47cb72ca4d7f938faa12b0a5b025eeccf27632fea2a2864a9c58969069a31247a23a6e7b64d10b3a75dd798e75eb4b2c597117d04b54a9be554614a662a2adea9d988bcd770a684fb8ac9072caa94e99c7e779fb26108bb36bb46c18833e075ff760c80d87e5bffe3fb302164f2cbfca2be87d9a90eec4acaa4441fb36a4819fdd0933ebfc06be6c3e95341b9af549f4ecc2d5e186bd9abeec36fc3ebc3bd49b189da0ba6f3d725cc57f559a4f78ae629abe4099d87195e502247df521f079cfd69305106092f45a4ee8e205257f5b58a3cc8d76c9a6e43a7a510a0b007c857652a2bd545064d41b9857e5e7d59920da89c03e6c8e53c90d23a02ee29a4d84e20f03ea94c275ad16c4bad85fd02e8cfd2bf3aeac466ea80a18975463a79b728ebc87ba0b4690cefb5acc8c5c58f0bd044f7f35eae1503662fe24e0c5644484604e893fc87f0ea2a046a5ded233698a6f5cbb5433558355a4cb4c0599b4461f7a8223d886eee66d65c8fc2be5aeea14c6153d921084f5d4d0b5cc93797266451f19545f0448b4d062d81fba286b1daef345dd079b5833506166c86edcc21804192555947322f1b436b6fce06d7e1036ee3fa72f1318f2636428b4516e3b274e7dbba6eb0c279113f86caca6568b45b71a16ef97ac789edbc7822646b6264b21a37a067608e7f1073af9303dd4a23080c90dd8b70d785618ed5a9dac5504ce23e900f355a6e08a00ef1a1d3df5dbc91e6f9fb8c7ef847cb41bccca50eabf973f69180c636f1a9408ab12baba7c8116d6632314fe33e59e36c5338b7f7633ce8971530c6f7d68a82d925c24c5cf8222a163d70842f1399ef431041fd9ebc8df7328a17574eaab139f3f9856422c8c7403bc736d8ea5c5b6b4ee12905bd15664a99e2d925fa1bf009d4fcfa3f20b1517ee1eaf429fd4c2f100995f37cc324acc89d71756a802a023a06592c98dafef2def1801033f24aeaa78353abe4ec30da0c368791e80359088c0e0e60e126f01513d4666c8fe4b5d17aa3eeeb5d857b7507cf24e16badc92a64d789fa26ca70b51a3408912ba687d396525f78e38b159de81df7b11d9de73698ea95db309355c26a779a29be4c8711cc78344741fb23b4cdd1917911c043560493a59326652106f401166ebdb9c39d7e384887b8bd76d0c3938f082f98e3450d04a4b6197ab351f3bca83f49545962d9215cfcdb2d390875f7253f5ede667fe10f8ae85f30fa185220e05c9d807496c4b1da39197268b537a3a4ab43a8503fd28c04e185587fd9548c98e5018e1aa589ed9e1399a9e0798304edde338318b299f18389ede6ad25952de4d6f404ede41df1d597b8b9ee947c558ed698bbeb424bb76ae715f4959e09d33acb11e9014380f8859b894b682299df6d9f2877cf93518dc7921ea150d2788ad26f640cb46aee51a911b6246905aa3a81dcd23ec01be54a0a036bd019118a24f76b6ce63f5c5c2104ee00cd40ea76855a7e980ba3007f78049e3a458d07b737691b9988ac6403fd2adca49a1a1c8457d14285f011cc6bf3e957aa77faa1c8e6826f9246805238b78d8f732a822c0a746190de1cc83beff9e34f01b20d8c840d944914709189981d07a9ff41d4b66d3f15754b3b7fc6cf3869f513bec81d1b67b254548bb0b2d3e4112544500c251828c6799da2c340771e02576af8b35c12daf72a828d4a94fd8823997453457efe048295bd616239eb285c258917e3d068f3a2a2755c621f7087f25b37954f075bd04df240e07c827569bea50debbc0b80279178d68de753668de547d20e950fb0e5f191e0728e7d748f4e9aadfc4ca32b1d873f98073c858d20492986e6bf83f9c8300f3868c4befd01e9efb7becfdfa09d7d3f768f83095d5d0386173f8e5b6868899d49b9f15668a3607c5464962a10fdedb6672e6af9573afd079fce05745a0302b3ea076356f84aa51314eb051bd8b780d0f895fad54183fd6e5e824e241848c5bc73e325212e8d8e80274919935d8bd97ff11089489f35bb20340083bab3e461c184f7c9ebe88dc92674240422ea85ece51c0030dbf78af41320a7a50f2cfc6c327caf68f75995eacf7a495be225fb7dd6acc6a3275d5ad385247703df595c1cd015e311fc1a9ebef6fb211a55ece646fc5fa8448d1ad7aaaadddfad2ce37bd13c6357c47dfc7a5c3357f74921ecb671ed5af3d6a86efe10f3ae44f07509c41a67c85d0a750941fd0abe1e7de49f4c42ae21e625f980290823f3a8912a3c4b1176a79ee2cdc41316908da32871cb3450667a8fcd99b2e7855d3bb7d1c9645dc68421760f71e9f74997c52530ca7ae3d5187b673b0ab725447837acd0a3195b51de626a27296b606ae8cafcfe0a57ab8a75d8fa96d4f2bf8fba074971e63d96dd359da2ed439dedf960ac02d1ee2a4a5088a73fb502d3617556a1a7ad11a85131480f4ca76cd3ea480225d1ef97625e370ae4df9d00956ea4bf1d5b32cb37a241d53949e22cfd9d1981e5799204f9c104614b561c9faa24cee688365b60b662df7821acc80e4824067f34129560c690e85e4b51a0ef2c4b30bcc8f949ee326ae84c38a2fc13184840317a8a5bd144db2d701b4c85dcd01e4b010b0e0e707bce76b2fbaaad1df162e870ee0bf5bd8ae55d316c996246aa8888462f1d335639210622e00ee163b2f85230409517006f17c71d577a91ade85f3e999ba48783a3fd329bd31d8f79cd572eab63f717ca97ae0d140228a3e732301d44e0f39f20b90cec76c378785d7682a1646f33959306d05e5ef42200d94750dd1d41e80ef1569c95352dd00a12b919bbafd43333d9a9f296a384f43675ad617eaeccdda12c3027e52138402a27dad9e6cf6e277d30de4723e951275f5219d2fde3ad67ff33fb5c0bae49a15c1e04599afa4b819c5d715731e7a2c7bc6f60e3d800f19dcf2e0b428350a0f6126dcbd874b328e58974489f22b8db688c32a334ba785841fbb01ca762120b8b03bc8418c7ed2a1d433a920a2a98b68fd991d72f01b6a4d7b4b2fa234ce798cdc1a1784db392529a01726d37e5c844337571ec09557d704a0aa3bda02a76bec69491056f0b915ace57d9d8959523b9606c3f1e0f3ed7c16e3cfe92e29c450dc528c41cc76fd6264c1743e825a2309c7e2616e8b8cc218cbeef538f7f774afcab01d61a61807a34cf600698737cb85299a8bb59230b221fd45dc3be6058d81252aac3b12a7905aa70ea439e84779cc8b2413746b2718aa4b0a65051bade844bb70b1d041eee6bd46ed7ccc4c528a808444a11884ae6242748585ad5c94a37fb640670cbf6a1419cbf2740f46cd6094cfe8bc3e47a1d86be1def0b70287796f494d6775be2a768711d31f60d75f6d9a5a060d2671c3c1d7bd31e486c3e25a2f12e254a01aa1f9a186379bb95db562f27d5a78e01b0ac8cfb137d8d559733c93276cafb93bd18a44670bf4026bdbdcc29c3951b2b4cea3b7202fd974214b6baa8a47b50fb2b8a1044e8b64fdb72e4a7d287eed020a503f424b5072d709bd8900bb14c5df53e5fff2f09fb6d191465a0f0159e8cf6c2de04a7bb4f91e22f39dfe78569160da1d08a8ee0a91e5a9bac312729e14e6abd410f9161f513020ff45b8062974000d86d5ebe49d9caa04902ef8059f6b7ae509ccd0cd14727a266f52a6b4dc207f40b25d06b561c4e6cddfdd9803ef170ab62685617ef968d4261eb95c760547d4dd08ac88a63d189c3306334ba9a0e052becb1d17c4f86a69035ee67ed8e788caa0696effeed7423ac3f11f51cf28ca4c3afd36ef84bced048df7452e96dc952d25655fbc4ba895d4531434647ab108f94e59d5a242cb81cd96ef84279803a431c10241897e70231a219b8a6cd4731bf72d50523e94d0a92403bba3d7e1917739b01d05aff550b6b089b8b849955c93cceb29b31f2983351f84653fe1b84d795732a08dc8f882250643379785b3c54f732452bd356cfabdc51d7de6f71b9b2ab3bc9e6846413cc48e347bc573025e8814638fad7ba6c76e9d7feb325b4f6adb47d84f3dedc539bfda7e96e822a1ebcac3294a5f29992949ea7f80e389dde233ee6eddb8e0f800594ede7f156ee3ef855bb27b5fd4fd9af05cb24b06e0f8d4cf468c33fa8bc64d46a829dcb195dac426ef3c76f41b4ded3203726587acd0e57e9f54fd2ac383cad20aa20ac3f39cbd21429f7adcc613031e7c843ef68e38f312f3c119919a53f298ce0774c7a8fb29f58a4f0139f310b2fe3c1db53560953a8c49ded0a702eae16eec3ad8806a440fa1a80a5e8a633c144e8c9c3367687f3a19c8088cff8b8bf64c60f41cc47810f1b3c800230219d3eff359b1af5cd67511d1da08949e9a8cd29c3776c9631e417b7e88ca0c01af10bed1e617b274416291d5d2f4fc24d8d0a177c988c5890adb1122b73301475001a74312a30d5e0ccf88da90d4b69f8be88728d0023adeca9065960946b2fe65ec6d911cac16c14434b47c63896d95b0fc78790688c6ff258bfc27786fac0d8b6ba2086bb674fe833ba196e74cac25060bfd8db4c4104ef2193de2a2248ab07f91bf69315c660bfed13e182d113b2a80fc5e487639ee5a19be9036d30b467b43ddeb388852f45893c768788a31f923bce036a7eda08293e054a14b8680575f181712a36123dff54c604c9c5a0ad7e4407358f7678f2d8fbfba0710c0ea45f83f69004caf3d386c28c2a0e39bc05452041f5ef4d698a5ab076ca8121bed01b943c7af1c844e8ed2448d5ed96f650dfdb1c70b1eb38527f0c41ac3e20caea0b97073a03d5b8e28537618d2bc69145c7a941a2b9f787222744f856439c3750f168f77790401dfb3701c7558da523af1bdaa13289075016be3bd8c12e1c3eca5068a3ca6feb7007a0060f8b83b750df32ac6891b05ab89163ea4dd8b0a3c4d5974fe791dbf6a4495808ce28c322ad0e9afdc4d44813c6ad48f18b50cc36363086aad6110d203581520b08bca7435b6e454c5db160760e260640f371af78acbf2ba3a95be60025a92f94dca9b86881b2452ae3777191c33b9c489905d029eb5e613fcdcc71fe1e651c45b5cb83cfb69f767a3f7b629646ebfde5dd46915f303b284dd70503a69bbb00b2eab9ffaa719b1ee30db2bedcc884e775c4b90e8c58f7ef8e3c0b492665d6bd191a95d54c7878e92de721e82bec3de5903d513ad51bea54e6457afad97c2ca829c281c97d37fe60739401b432cd97d285a0245c245c4743ca17b676575d955d61dd729ee3401c512bc4b686de02cd55facb38abcb4f317fe50dfb0705da71755cbdc3ba7c989dee24e1e44a9406322acc5aa97564e5652335f08270c17f68caaf01c0324ac892d224e3cdfba8301e49017e38249bb26cfcf5428b579d33653ab50a44cc5537f9495ca99dda75ac8ca5974dab538e92247b64e2d192957c211fda88bd871b95964b9d282d7d7727fd785bf5cdac6c4473732f3510fc33ac516615c6e1b0f328386f714aee5e5e539e4bd5df05a775260152ef6c595708f2442c6b72c9c6f1647bc60e51b906c17fb8849fb29bbf7878a2aa00a0d23256f03e532933a5e8e8304694f8ff4405b286694f3f835debe5d2fe3b2819d0a52fa924ce3f369181793aca51d15cc2d932c2c2adeb4c416dfb3089b8d244ab9740d4a8d2a5c5bc50a4bf71c1e14e120c5b30ad5d0fb341181b52953b67d9fc50ad7b2f1f199510d88e8a7517a41b6080b360840131d6bc9fffd4739090507cd4edead665ff275eb8ad11f2278c4dcfe4ef1fbe491074b146b681533717458a108058c15dbdcd413684c52bbb3138c7caad85d928965162f428eb59537d9954ecebe1e24611628305af907eba2a29f895138f64253c550aa2fcf41b88dbf556e0a54595f425feb597f31cb870ca46aa4c0b82d7c9cf4d33948b1b292875734e5469f4d4bb4a7d1744010e438ed57f5abc250711b945cc850bb591b63e1a8f1598ab832ca63fcf00acffa3020fc43ab785137feff22d98f31c0aaee495fa28de5d4876ef4d98970fb1870cba132b81753b74054bdb08c9fb85325efd7ff751d61d9b0e2536f01701e1322a4aa9e01c00821517e3e3ad0262c2d90f6bcddd7b9c23110ff93f5fed2ce345faf9b093c36b1e7419c9e241f4a6dd53066df76923326ca5f2a963cb91d885aa8030da7bc8497bfc1c2a0728608417aaceaf8410b5916aede8f6e16179af4729bf2c6a747d1f54ce04d3fb7f6a538acddd73478501a4b371c81b6d1a5f55f6dc477ca9dab56eee308ba9712d74839395761563fea2aeff298f1b34a57a1b0d32e065d7eca75631587a805b0df275b8b263e39a63c5f9a24cac358f813a81ba077b763beb4c577bb137c54b52c5042e26152427dd934389ba92657db6fff2fdf86533ccbc56a0d2ac712b034acf5b62b17043a787fbfe79fa5eadee7c5d23980411914326335aa615b84dbd41442093e104367305ce358d1801f9b8bc72e4eedfbf4693079ce6d99e52004cf00d25e79006a5bb61f0b0a2e0fbf038a134c4c26fe655dd4991da345c6a17f7532305665c11bd685716fa1f2640d607d801a258e7f59d13457617f80fa3e7f44b230b9491295786dc29dee4c4db00606f5585f2b960887408acdff2cb691a34878f4426144629518cc179ea35173405161f9bd556904870d6ca4a1e2a6450f654304b1f0f3239c52379b14af3c91743e0f3d0583513b9175a7d90545115a6f377a3b149fb7d31f4f8f380ee048a4148ad64981cb7e28dffe7cd5ad37e1f3ad69ccc27ef16baf1b5be48a39ad4892b7ae4747d63309bdc0937b591f0b1c95860f8b6e693afc4655bc57966e11888e5f6b3415b8571bdd0fff799ca1aecab9382abee907847a4d2f23951e8add10e5a25f526b959832520d66158cd41cf3dbb1fd8de2108c1a7a6e8b759236b9647beb240e39a803f267cd92ba0487e11ee9b5e94daa6002ce3ecccb8be6e3ecec6b1f96213d4b4e2e849add7fe0337e846ad8e2ce3515039dfe56a4abe6e45b1cda8c49d2f3fde0d1b22e80f934c5d3184baaf040760bb539320cf697b2b875a1e22cec4f14c3b4b367fb7cbe160d0f829dbd898eef4bc825f38c06c347411d03354aefe7d8b22e8c87eb0e5181542d04db09f7db538030839481a26ca10866654fa04a09706e580a24678132cba2172739fa8d4d2b089324b99ac282f786fa660aaa1346fe0972ebd510de75854ff6c446e6c42ba56d4fe3e059cbb0e33dc34a44c11b9d582b9fa7ebed792c035e5f68f88e9fe7af6db4db332555abaafe4453cd568d648f7dba508af82ae8b26c92552f80a07b0be8905a8a400d989e9861a0afab3932ead7997c5aa8e351adca49fb28f14d6f97b55e0bbe943716913dc250342c65f78818934ebf8b54cd60a7f499b2039f90a2b65060e2f1962636d86bca2bc63d8bc2218da40073e3db9e629ab6dd6cde2b93e27c2812a2a91e3c21c34d39987cc28fcc3b614612dbaa962a58664c3fbd156e44b5c8285f736c508e1b3e551e9827afa0d90d1a9b6a396336894be25a17d8df907809c662c75f23db7179b91e930d4f2830bb030842c9074d88bf8e1a5c724f2f732a187c151935040c852e1136958b52277766c20f61d3dd11734a936b0d56c445f315f8b764e2c1d4f9eedfa4005033ada7c7f84d1e7f22d66cb20f66bec89ddde30e34aa0048092e893fe072768b6aec9c3290e9af31e2a9890d4db6f5a680f8ffa35eb1b95058caefbefc3064399daedb6b391a858eeaca10a74f491817fe0a45489a9d96616bd1841eeae2b76020a3f120dec08e7db4a39127f314778804606d6890313f341d1a8c19323b1b40e32cd3d14a22ca229ca1004081737a5e08fa5fd2a3c5142e77dce13399e796b6836998e4ad6af82a86f2405e3c8b81afff9970b6638026f74af89799fcadf06175e398c57d91d6365464cdaaf9ecde8fa3ce4f2feaab3f2b31b1df1860c5a560f21d4df8ddf46d42ba573ce50542ad11eccdb5c280e888777f00e81c9a91f75ccc1fa75aee7badf8d29be00d36b467815222914a4e0d9924c34da880001184fa2b2fde4cc638ea62e81ad96de11942c9ca02adaf35be6aebdd16d8ab386d13a8e7b5cac25abe9407f451f9a0aca8c7c45b7dba74a6bb49dbe831d7c0174599c621c7aa15c68d4aca456baf33df589fd407cc8fca1c655a089dcd5ec0e94b6b3f1942ae2491b68272262eb02bdb6af120e0b1545284e06975fe49dc1c986490bc052e3043cb6dfdfd1daa5bdb1c6e375073e107838aa8b53c79ca75125c3b94ca35de68fb9e075c3243885d708a156fd96f434f952618aad78cbc7f8333f8737b51686987204cb03c1bc7cda3494669f3e50c8a210251d71dd7f8cc4c32b45b8a327173615e6575b13ad4db2e42e6ff42d46e0dff550eadaf9a86a02244e64b47a7471e5d3e672dba24662bdf5c56cd9b7bb0ed8bd04bd80be7c5878e2f48a35641c35658a5205f9a53f543b9748fcdb55d313a2429e6c29bbee2c942ea144985f60b595688a164dd80ffbb46e42928c10b9b42ee27bccc863a9428fd5a554594e69df39a9b18a1dcf8338afef91a1b10eeb3229bb0c6efce1787c76f712891fb2dc05619828b0705bcd8eb2ee2a60ad91bb980da500e4185dc7d241a6750856dc0be4cabf360a7edaa2add54b45b9c538273ba6a72aea7ea2283a968c20b8e0846260c8fb09ed81e76b494264b4f79a40aa1b6adf4f5aebfb5d4ceefa42c82cf7708f53c0a82442c2d1c739de6d258a2aab6b72fb21d5b3399c782b4432b5843c29df2162bd5855461eb679def39718a575cf8da672405216dcf9372d6b0cbf2d879951ac8206d20c7aa60f2e04b093b706ce346ce1ccafa3c2cb8ba01d7c04978bd955589ea0697d9f9c1864ab15eea0f5aea3ca59f1979568e01021fc5b06fc1bcfe260b7ebba5a343cef7976422d00b2b6d658f133fb687fef38e4e477f76dc2f850f921ef443f1a1b2598e3f7741deead531fb9618a45d59018c76a1a0d5d81275543b0b3d2f7e72761246104312be4f1176b4c173c064bac8afb9d2e97d1e72dcad36b61bedc9e356d890a23c5207216632b9c99860eb817e8e5a15b446e8c5ff62549b6126460086590f2ecf826e9de37f8c08d8e2d7a45ccd24f26956af44d3c7cb7f4b196b84aed952b905b25145dd0a65587056b1691e9065dc4cc70609db79f917e3573db2b0e203a0862acb69a6a4f429d2856a3b00f4ebe624d1378f8f031b5138a9fdc4993a273e4c3f0c68fcb52dc90d4869469ec9a028c7e007f146842155608497994d9eb2362813add94ec625015490539c259275a4389d71483ee688160682f14616ce17e51ad67cdfcda8dc2d09a551ff5a2eb8b0b2764689c9c817b0019209ab8b7c074f1f9de15da1b52d3ae441492e763a2eefa6e40da5f54bbe76843c20ed692cf6fcb6832e52547067d187409fbc058e540503b139f8c1ba1f0fbc8905d6ccbc0bfe08b5d71137211755f4b0868c8cd9e96ec777cd5555c5a994b89a4a92d5eb66e4053cd4c34a7b6ecb1f54daeb780f68ce0623e6381352837ef340c6c90204b77ab5ea3403ff6fa4c72eefb8359aa115010f4faefd6fc8207cb459f02d48cf21ead338d8d0e253597abc2650650b633b14d2895fc46ab4461919a4eee67b48bcae074ea10b3ff4eb8748473e5927df6700bdfdedd417df0cfb71aa5ecdc3825060fab3a2f940ed1918bcab7189849080870a33aa010d9d5d88b64700da39c61756abf43f440aee45e1b12d883336a59048c55d102a9d4897ff974f2d5c41ef3f7aaa24fffd872564c96d1e39ec279fa183c1c1fe6a4c29821b2c4debdf01b20c137f152f96c57012101ec5d6ca4980b59e626ff23b703e3e61291dae24befaccd92ba78725c4b6bb959fbaadffb51af5267e47748f502f9addf2896a752a56fa026d6041ebf8ee04c665be1269ad9085dfaa4f3c0a6573a20234f3024ff07e216cef358d86a4e7281863f0f7205e6c50a3efac602bca0ded81b7fdf2ec5f83c8b08236760ee993a29d61d0025bcd0b13f551e853ab651f6039b83e3b5873111f7fc58d773b46952ba011bc0ea08de2a76b8df8afeb7925371f32ad08b28bfd635c84034cb120e2339034c36278d4256c8b9a770be440919af0bbd3fb976a37d66474b75fb7826ff34d88030311aa88ed27bc996aa3d9fb4877f8884468cf58f0765a4f69ccf148ed6d0f0c6749a77c2814773d8460d48122c922a5815453a371d51b13aabab13c10326b8f00d0200aa70ab92f6e9992d6dfa80287fb07989b3550446caf5af40dfa2012c9bf3e6021811aea1dd308049da641472d750b17a0574039b9974cb5712b442530d0614339f22bca784c99bdcb81c822d71a86fc2729a50923a385459c58b8248238969c399ad595eabfe0a9d81919729493f453b57bfee056813cd6f472283a37fb3ea332129170ff86634d9d6d1283bf49ef410664d5b74ee3f90515b053ee095670be9a7977533d921a49635284414c8f54f813acd9f787eb888fc67bef318979ecb0da6173aae06c89663b87d7e1ee0e5658db21a73428b34867b6c32e99f1c8073679dcabd4f0b01fb26a15eba572a3e0cb1bd9f052fb0925822c47ee1f142b33669bbf71946165df504c0023cab0cba3b0179c216f10a32495fb6d47ed2515caf779ee5f368fca1766648f21439ae37e1897beb862fe5d7b396921ea7006d995cca3b3f4e32b3f9e72063afb74ceaf09c10d431a1710b8a457c9c4672a1deac86076dee177d6dfefbc50de6a39cbe63c01bdd4ad160e596c443a046d85875d20889cefbbf22e93693f77c1939af4c3145ad6413f759dbd0e12851d3c243be8cd06fdc531a178f5e7c49f81216a72cb8141457c02438f05cab66e9aaf602039c5d35d598214be544ed1a6b0f9e4409717a018ca89a0db262cca67b7edeb78fe3f61fa8ccd1e4fcbe8b06f60cbf6d24825802f5578299b1c59b7f85c6fc74b7b3285705aca33540d31be0b49a6be336d9a0bea357498afe630d9eed768524507ae8ef8e7f13a8c4e76e9f0322f58bd5bec62e42228166396ef2abb718bcf14ab510451c38dce7010694b6600c01ff3b8297c62c943fe599936dbf8b3dad21b38f5d23d5c32deefeaa4beeabdd96c25d3a0975782b10842708abc704a2e70cbdeabed664f60272e622e750411e405f2e9d9d536374111d6365e42e8c6e14e06260b9658dbfc4aa13f90a8e01d1a2491a2963fc9a57e1adf9baeadc9a7ada8fc87ef8486d6433c51d2dc756d8be1e81c7768d80504cdaa705f523d6b5b57e83a003c5f7ca27fa980d75c566e66286e1c875226556544a7de44e75a4604fe55662c6b52deedccfb95ad6d0393cf047b1b22055e32a817712b2d64176348b55af0ebc6cb57ec003a93822c35b935d8b26d6f9739559ac1c4e8828999fb65c328b8827e50b667ed1d91695ecbcb189d99ab750d597f4b3d130fd9b6a5aa31c1cbf0fd3d139de71b35a660671375f605878a9e1fcb1f67a792a56c5b3a107ce145edd0806d770ccdb60dea7a108c3c95b66d655d0e1dd8efd58d940cb1096bd627b94455cd0b45a4643026a392f0ed9dcf65b853ce11673ebc36ee3357940735c90f239711e6a008d26f5c511e1491afa68df6186ce53045c3a5d0c19287f14822461b897bda7175c72ac639cb7407afd76c9cb80e8c70ee960cca5117d152721495191e466076068bf2479be07c3bf464a3bd3bdf849807c13c5209e7d0aa600374810ea5bc5988d8377abdf72eb2748165aebbede94b49ecd0c1950ded6a6459d19968db5e21571a355935e864233456a785c6b7827f066cc2b87519efa4bc3bcc12f2e9ed4e51fbfb60d1f6b78f8761e0b23f0e2448c370043299f6ea840b2fd5793777a7193947b2d16ab19be4d168daf98227b67ea4a31e9059fe1ca70eb0480e23f9f01dc468522d0ef37109e73f0ae4ba2e2ac747af6cd5c07d76e3c7f3cb8e0b5eb688964ec66d661bff4ab3e90a98a458e6998fc171cf87a6bd0a1172ca0a6b0ae8d77ba9327d7a57021c5cf8c5d4b55d362f6f8d59804cdd88be79525eb9e0318e3e6e4880471025d71d706f2f9c42ee9fa6321479ca5f7fa5db945949c44425a10b6d1d7d095f04d1baa00bcc0573ff1125e625e04d654126561ce5711f70bd48a68b0631a875e201835294a4500a3de0ee79579849e9b2b0b079e631aab380b31b23a3063ebae07a6d4751d7817c972e85337c410df313417df67d95de425b9bd9c702e03a8325682726d1e3006975f5ec46e8d2ae2d1a2c0f973bde4e42974d9249d283d538c9cb0ecceb911177ae3ee92fadb0451754e6fde6f47c0a84e0f3bd0906a4021777b8ea40d38034a754399752790ca0d61d2dc4a1ca967db990c1083df1f230f7c70121b1c3cb826ab5cc728aff81a56a027aa7b6a8c2fbf9aa9629a44a295a3ddd208de0a14e7661de8ad3589e0ced207c04877e9fbe92f8b3a6b7a1422c68e7538fca57ba7d35144d97225ce0d7cd1a0be055e002c7a5e99cf796e468b16de8cd84e36967142071615e8d4f792b39f9bea0c88990235d177322f7661597512b86949a1eb940c3fc0003646546cde88185c9035cbd328c8d3e2a90d18f9f15a9fe1381788592d631cc8a2cbfb0ad26f41b988e0f8a3b88bc9cbd1179d0781eb46e57ad4b1301efe66d3c797ea0eee2ecab5b5728e1081aa3fc06db8e2fe0f22ff24bcd0cbbba872c38f445d8910b634d4856a6ed95f9e36442706fb07e137ada1ac11528ca0d7244cba017e88f82e51a22f86a702a2802ecf5143bd9ff2a2ffc66ed80bfbef47b532ce7592c169ea0d49e55fafd6bd7a53fa2e25dfb0481f38d5014d035cfc0da872707ef3cf1bf991c9f2e8be4851d769d0ff7118c146e2847a508d09ed9fa41ef73c4adb0b6dc44237d7aad5b6e2a8a2aa7e8accdb6e448044dd0b06baea1eab5a78c03bcca522f24ae0f1c4ad2203ce8fb475bfe40bf6cb4415f92b6b6a21b4a62fafdad28618757747950e12c42c76017010c74e5dca3c21dde1a8694942e60ff85a259dcca7fb7ce2cd4540f5537b3002b9ec21952247c804984ea99a00ddf544b835e5f039997e7bff9795b5f9a4aa437321c6a4aefa0b9d061a0274955ba19221dde23c3387304d82dbd407df41d13fcc8353fb410fa156b2e58c3b259b151460a3e24f4a52fcb566c4a3c678704b05d2e2f1b57d05a327b1990ef00f2de65c59ae8ea5ebc1ae5df6e2a3a4d6ea8b90a31e439952cdd0d2f7ad1ed5d292ec56016af588e2426b3109876ef6e4de4b6ade01652856591107df824a5b68828bf39e2ef9ad544910b1ddcc8f33e502a991eb988e4517f21774b7816a763beb52f344be13f0dfe042cf148ead3683e96c476850f6cd4c5fc896a6f17d313e427167038949c968b279141cbb738d4bfce09e506a0a34423dd172bd649fc8552cc8281c915de1152ebd6695f2349384fa6ac94467c8deb96ed96bc3454c1c2871dc236728e90811f5d74b1f552c516c6dcd0e143b486cb6910360ecf99b391c66c62379929c215c4e34818e74a26764526ae3123505ffc86b4aba945dacde021cb41499c9e8a944af71a432f0eaa7086942e3e20486ee940575840ede582e8adaf6d8a26be5a152327922a95476b15ee95d3117721b684dfddabfb8d48e4fd259020e1156737d279bebe303ea0b120c7b15e91deae47c8a433a55e8975368dbe4eca06e4412de107c8ae91ceabf5158262c864b301b5b7ad3a4c7e8d38ad088e64db8c0ef4ee3c43547f3aae4bdd6cc68a43881033dc70208cf41e3c07f514e0d3613620a5b63450d868b92e896bea811d52f5401950fefe6f5a059c1a64e047cc2cf00939684e71de72db555b1944d4118cef35e8e308e945cd3306a63f6f240aaedfab1e45e2ee220ad7aab1ad6b337638d9f7105a6e89ce1e69e43c28b4842747df4c68c644ac42a05e6a0d2935a0ed65141f48a193497714ca7656b4a1df89b0a0a2ba4a27bc22e7e1842640c9b32e00b5c2cbf7f1afd4340e613ba4ac59548319c8a1bfba0beb546d9097d93b885434d8dc492c1d6ac0e6d7be8edfc2e4dc576628bd74db10f35056d5ee4ef62c1d7d9793d1e4bce966fb6fbf4d501ede69281fd0e892388b1fcefb935875e815c0830e1614f7888f95cc3abe3e8fd563720b69953a9e32ee113efd72bfa61155bee210cd4dddecb998eb2687a86c15ec47b6fda154ea88c7e2d5340c9776af147595f77d94a20fed7c9c0101d434c2b9a178bb7f1b2858d874823a15ff3809e32c3a52ec7944fd6a2e276286f4a1bf29e172dbea12c84e82193b264072dba696804dadada59d1e4986b22f05c95bd88110990dc6ec5ea09b1c7cd5db0bdc725869a4efa42d020553683a2bbc415c57669057fd391c74b9f0a92513c01bd4b40ddab8b9564139bb348db3afa59adc89f90c35a960706bfa0b75b407363993de2df042545c42221c2f0683178416ad4f86ed22316cb2c00b835468c0e36329770839ba100bf05f323d631cabbcfb65250e6af067f538960e9f114b0f5b5db41adc1c4ee7a665699f6d7aab5f207a0f840160ad4713dbfc90bada129a8d31985ebba6d6d222d946f34a6de02a8f1a4032106bf1e06142d458efbcb52842b6ad622e230ad1799763e2b9bb2586f00244e8dbb0cc17201430955b482f3f47489917f2389e711d8459befc52bdaf5db4e74014e60a3d89906dfa30e8c153d4ffb145c5f7d4e3c60603839a08e3d303355f0e2f24b032f41ca99c3debafe805271808bdd512a078e4060d3f681641ef04a61bbe92d723ee098292d9be479ade0957a10430087d056276826ba2eaaff6e7695488cbabd07d1108159d05a531523144139c71ba159ab7b0965e2e80efdd83dc6e6a6700ea83526c8e64986baa44f6d16b056e598d0351fd53e584e9a47553f5eb0857612b4c043eaa35774cae5740eedaf338ee688fd0717610b7ed8757c3c351dea861e313c400ba5a589a4378f58b25f01b34b1f140d86a03d1fb2f501ed6d9ab4eead03aba71fcb1f5d0f913bba776edf8e29511a5f1285227786802de4005b29aed83d671f5d786f3690906f03efd283b6544830c703b0fa79427289f8d46a117c14a0885c02cbf5442637d09ccdab447ad07107a0b00b11a1be30f7ba15a43b9d6f5db88611dd71de6f79cf95dd17c13e36ca0e790cebdcaf5b44bf5369a2b04e63cad4d2c5526027aa8c24ecf8629ec616bcd56befde8b31095de91a05b7798f8c2651690697c23e2c9b3733f06d702d5505ddfb1af5f859a208c22e926e6b30ba69dc4dd9831fabdfca1ff37e1ab6ccee6c06ac1f7c1f463c0d22fff9c86ee9a4030210e825c4b829a1bfcc60e8fb43b58ec10fd9158d7546c5e62b19dfc798d2cc54f089cb94f6db80d1ac360a53b41046b0c93cc635068db6fc1b7236e952fd638df64d008c9f7745afb2ee7634e4d614e4d2f494c89726a410a0793fb42fbe3e4b5b694d5720a271859753300aebc17488875ac11e18301777e758b89915043dde24ce2e5682f68731dbdb49db0b90fb49a5972fd75c726a23c8932a47da0f518faa88327e8cd9f602c4967ff4e24a4b3826b82702113d730048c112eb87e4e69bf04402eba68d5d67929aba2ea9788246883ad32df2015b7d351a9ed6be3007ab2fc7e89f58a9088ded987191c6f0de2e81537afe1fc9f415e08d1dfdf065a6083bf44906922e8daf360ed8ea72edcbf55d4e66fcbd9e69774d600946ae7717f8a8a1e22317ea5ef04ce094050a188318056045ca1d655c27a9a782e094f317fc655edf87a6e8ac0261ad194d7a56573b5b4a4ffc513df64ac6243e2ddbe699fd5568d87d6761919f66f02f1f809942e706aa6d27e1d83f791e2ab4401979d6810bcbfd297f635d042375ff1c4fc0cc39bfbce12ca8c5599c3710e9803ca1d0731a4b41c31e3d150323ba023872a2e13d9e0efc7a3cce501291686de39f138cf7be2ed71790d6b2a7db9b0603069f9efed5d815241b2c065b94ea720083424593b3bae9d06fb5b91e9adff23b59700be4cdbffdc2ce6b76455e892943fa7f69a3f0d9069c84c66e6a4a21aa04e7941a754cf6339fffbe4f2d4626d7e09e05c4046f32ff05ec8d963cbe2efde1a0bb740650b3b6d4362533dd22c92d5a7c1eac1127f2805ee9d574bd277e022e1a6708ac64a67cb028ac1d0e2f45bb2d18793e77612eeb07c1f07cf50fc66735ccfc1f31b6e5fc3bfcdd437e416fea917b2990b6edfb5e291b38d14bb58060a7dc4ab3088bd48b688f548e4e049b41f2be10f7fc40449689c5592b1c38cb33ed0f01b60b66a94af276010c48c5634201fe3d5f44f02871de745a07baa320031f869fc7c97cbc62399d446d537094e198efb05e2724281d4229df5d595e1b03f80fb13c478ca3ef4d6175286f3be3d59d17dd370c49d9ab87fc68a0cf503ead086935b20503ae21c0a33bb97958e0ff2a4b27582ae57fcac616b7a60469b78dff062ada3db28aab6a8dae204222cb12015a000044b81004aabdc959bb2d99ce70ae7cbd287f4756335ae8bbd7c5f7e6d408df675e57bb97665f7962949192c096e09be085f6454aa4442b164940c52789151a8b23e41ad0000f53852a80f9e8aa80fb08f8f4f9e419549ba3c632a53a64c9e9929ab2c45da4c9236e3206b2cc7cc939f7a102506493d989ac1f1549cc171fac142443ebd6ae6f4aad3a9ac516826c90aa53ccd9cc8a7e2899c8952a443c6a752250e9989529443c66e738ab4194556a01f4360fca89f79f02406997970269542d5ae904136bd4a846c7a95380455bbe28b6c2a31cd28b22ef1c084512815152d8cc8a557b572e955e21015152da8e492cd33a4cd27928536fd3b207f0c814ba0490c821fc4255c9aa919424c12592c952a7148cd106290c862129a4d486e882551fc241dbe4a1c6293058c12e4b03cd2a6db8c499b4da49136fd6d90a218a4f460292c85187c5508be0a9c00cde2ce0a0c415392ce2a5055c46691143f0c1f0c49a4528ea21ca4e4ef55a4ef55df5702a122f9c3e6237de253f113756841658b7cbf12c998efabc4213ab4a0a245be3687a4cd20d9fa41bae153f18603e8214b18d9bb259254f606d0431630b2e7953c68fe72d478d7f34022954a1c82803265cad84c226dfec8b14d7f1af2c710d8fe77edb524aa05152d8cc8ddabc2dc6941450b2ab94b8560f725e95c42f5f1f1f121d293bb328967ee55e2105aa64c993299bba4cd9507f964006ce011c0715647a0f53acd59c12c520729840aee4af0defbfe031f0c3f145f2c7dc9f426fcf8f4a7b396a84795503ff5a9f24c7dfd9e8cfa9e3cf33df87bb2e97b72e97bb2f83d39fc9e0c7e4f267dcff764ef7bf2775c5229f73fe1e4364d64490cc126d983fcbd27ba2362d56f7ef35717a3ac7e77bfd5b253e176a3e1bcaee3dee36ac29aae86f50e63b158b1988a0642b784689a78f005ae5ffdab14325e5c506532994c269389a132158a1c2972c449534a458edcdcc9fcd56a99e62d82824709620b336027859dd488964255bc30d4bda56b6deb4eb5769d27334eb4d3967659276c0a4eb4931934a77eb74c147f5561a7d357d8a9d64ee2e944f357cb6427da8976a29db6b495969d68a75a9b2528b00727b21e59598bdf61b3ab09bf4e7360949639abc35a79fe196398b2bdfb07047766fef43ba8175f25561a9ae7317dcdcf98489e2a3ba2f83ca63ffda57925a7524915252a2562a924f5a8a7f95a9a30f3a747616687e6c5e7517d0d0aa8174b9ed4d3a8fc3c924fee918c33640d4fb2fb20c8e84ddbdaa88ffa46ba8fb394e81a085abe28f795dcdff509bcb2c9b4d9d5566bb3fefcfcfcfc7441dd600fd863193988720ca9a6a884544194d5d7fb4a3f252ffdaa519ad52520934fbb56dc0523260aeacf926de6cf1297b344e4a4d5c42cfd90f1122dab8228abdfaa825441d50798fb555070d85442fe32bd0ccb737c55d0ab825cec6eaaa0eea60a520585a251ed218a316735aaabb92833c52191c814696117a2d947863432dcd266adcda39bbfba925305f90bab825447dd195eba20dec45b77530575b7ee28954aa54a5590aa890a48f5a30a52fddc58013e5541aaa0dcaa2055500ea2992ed93003da4c2693c964b256cb3431a6d5ece188872a2786d4ba8b6458aae1d7528b0bc5b75f1d67226c8e81bbbb049f55169379f5d2ae15cdb15f87a5cc1bc16e0a4e2b0026ebc9d126bb8ef7f54abd4407153fbde4087b2f26eb51155365f446899bcb82b4f4955b0859f0d9b17685df32d27f5d0d4551dce896f662c2a55a4ba552ada552496f4a9c10e93bc2ca904c8612e30b5c7393d09b0e50567f5d8206c705f11582f857279f032a608157dcd3f70374dcab04fa74f65c2984ded0e0804f7af39b0a1c12e3388ee3381b67e3074f70f5d1514173684e052201e6a139de01235b4d006670dc78c3f6c5ec596b3da802753319102d163b629665c6bd10e3a1b503f5d1e2688e2de15322d76f551f3caab77e88d026bd397de43e798419a441a842d185ccccc1d4c867bdd523ce4601bab7bb76adb58ad0ad560d9f75564ba84978c9b6d6c546da366d4fe6c89e8c2d0966f287eefeb67d25d7d90d15e0b3ce663353173114c3dfc161f862c9139ec2c7f8bba413fe2e297cb1f426d39742532914c5c0a24e2896a1f7ddef923c04ad05ad0581c219c4605c8467a7b7d6e258acd2a016b50bb9eb19b96b98d10b5ab96811ffac4a9cf504f50a9e5981f1ec887e1d59cd9c158c95fa04969a654b77a9a4596c964f259ee1af3cab90a9ec2c9058964660e971a177c309ec660ed2237c39722d4f7724a6f590d4236aacca84c02a1c6993ce14e00021432803a8bb3bea1d86ba9d1acbc2d0f8a88e4a267b54f357a9b447b5aed6ce1ed19c52ae5d1da8f8fd9e8ce6749f43fa7ecf0634c7a85df50911a31199c90da88745f594a0af5b03d08561cc2806fea2cab8b827c057a8572406ccab64fab3f36206a63fbb306a34e772a13939f09ed01c2b04a55f1eebc6d0973deab8a8a3958e32ea806ba0d683e17e81c5a836c8fdf6c85f15e8e8681c6bf698bfaa2c16eb237f551aedc86947652c08e4af7b812ed005fa482499bf4c992933651ecdd5be3da2d41d0798e3ba3fed51d791351fa94caa8c44aaa417f3a307253c194d0649da714d3c668f4adc4fd78eb81cd431943a0cd46ba8e5e99db55d67f1117f79cc63376e74ad878086866ab29aece8e8c81e5531a167037ad32f9253f09b4c5f32fdcf85e1d7c1bb81afc0889108fc24766b18ba300c8d19ca5a484f466f3c2c2acb5382b2fac518bfc8ed71512b1aafe0131c75603a23172a3da8f3a0720512e8fd065098a95da47710247d2417037ad33d682a35ec9220ed6aa92d4dc165a97b13e9b0eb2fae0bcaaa6fc64846185098fee2525059fee27ea0acfa5b8c5738c70226c62ab94ac9f56b351a9f37c1ccda1a34bcef0e70ffbf6a6bb5ffb4d9bd248ee370a0a0356bbf8b73e27c97f460188258daaca140fa815e771b7a9ea5d6526bfdab8305e8af3f12c791ace538ceafd7713623a4d875f9c537dc51da0dc58bb517d35910ee9de4826d693e8786cb3ce6b7dbed76bbddb2dc6eb7dbadde6e5f687a9df68008d7dc61b94fd4eeb6440041b044f31dcab5964a2408964a6009044b206845131aa6ba0f964aa552a9e4485219fcfbb795c127816f6b8900826089e4400bf974aac105f6c7a0bb1884045cc224ce0f20428814d9998091365168d3853e68041aca270eade278c1dfa612486a271dd42eef1f556f94d59f2acf8b2a716e38b3276e57caa0e5b3de54333333332ad53b4cc50d6153490c8f905969f309222cb79bcb505d438a98f66eeaa3973855748ef36c1d17db2e0f7fff0c7317be78ff4924cddf7be73ef3978579ef3412a9ef733da4de3f9249df91e798eb02baffbad346eede59f54f645b6957f7deb7f79e0522f5dcfe11dc10a2c0c11039b8f0420d5a10848b0d9a98d5b47831668c1fd21839fc0f37c612a029d35998322b5bc16bcbfea12fcdf1ca025c6b12d5396ef6841da10eb141a88cc1e4761a9c77475c0428a6a37d373790a921288cad0a0e14b0e9f002181e8480ca8091c10466041fc8c00315361f6663b6b8d560a6050b846e18411a6088c014fde062f85246f7654c080605a118060f5f6c75810f9c227c491826a0210756b49800003784e912c41819a858e20b111e951b8e89031142c404bb7858388ee3b896b3a8b5d6ba7b128ef31a61e0fad75ef29d45df23a95b6b6d795a2644448810f1ae73794f7fa75d1f480a2f0ea59c0e1021edfa4468d9161501a7cd9619387ccae29e4452ca3acdcc3de58ae06c7f1c899c3e45d2a2e6f998f6ffdd39bee842c755ebb5062048698ea5f45e7b693efdb992d66cbbdaea2b6802e47cb439d9da2497c1ca95baa4f4c63a977b6cad4adc2f407057c6f89d41c108680a70708cba285090c617194a548cbc3cf922831518b0dce04f680813c40f656420324157031b6e094e1156bc8a47abc029228a2f3b7c1dfb4a557c28d1fdc10a14a441060e88e04107d839669c2b26e0220a2f8ab818324107c50e0e069c22c6fad58ae314d1312e0a31aa78465c0fa735f67f1137d3591820ae0aecfbdb3249674a9b3a75eaee3bdc881021d22e2241ec1321d2aefaf48bf40bf4befb1e87ea0011428282b9df3285442233d0ac364203ec5f3f7be945e3c5788aa9338e18bf91b76da77c3e5830b097e92c8c0ee338030d9d83898d304b98e460528e69e4ae3efda976d91c4cec770c97510a239fb6a802d21c16d31b0fdba45d378ed45a6e013709c7d9f6527d683ad4ab91829ba3cbfab7bd681581693ec7eb759c1d6d3e73e4c6a158c2c827a54699de6e57728391fb7f2644eed799e59eddb4e4fedb3ee35733d3d9ad873c93e9ec26c3c885add5b9222e32575ab2946da53acdd91950968f048662199ae6ff385631de0d3f929172045b2d92697eff771c89c0def48acf1442301d33809d2d9c1218a3e64a2f4bb792d18e11e05f552b67a42183d52572704557f1440c9f2523237f1521cbc0253063705519a98cfc555219a98c4a462a236d1e71561ad88bf8ab64e4acfe92110a25a336bb488a283949e70ee09c04e7faef053e62c428f7ef8801d72f024385fd54a94fc9b820c5110cb016198023d792d61ef64b3810a586ba4942281bcdd1919b83e2a43ee7a45df6392123703eb9a01a943b2888c88bc8b0d56280bbffe7869e1ba239ca3cf7231444e977b64e4e8846fadcdd3defa315e9ac80d44a0eb0c4964c21e34662037c42b8c13e18fe02054d2c985a8594e20e4fcd108cf45f493180e96c0c992250c688c9fd44fa8bb1f3aa69050b6433cf1342cac10dbe4f4c0748606b726210cfa779b3741c59612472b5f3f15429594c1982f17ca5d742280290bc4102180f49469b00fe9f27eba00a969d5c6d363b41c8093acef2824faee6d19c5b8537cb523b9fa614b87abf8237cb3bb257eab4494ef9de268165edf2f181f57bb1761dd107f879d8577a593ab20b2c44a7cd1d3b64f168de8c12e91011dd98c2753ea1f00a15e0f36b6c80ed90ebe3936b75f788aa16d40c6a0624f379adb5d6e20097f9bc3da2a0401439431ab12d00bfa5b431de52a3b529fe5969268cf1e313c6278c319ec133a66e899197d213f14d2273e8844d367ad36f13e4af5369ba265b68830b5e8dd0652f4f4ef24c3e4db6b6d904511780e045ee07c146733ac0b125f7e3b8623374c388e60c7540441295371c44386638b2b46b8524378bdf8f83d62e0f0796324cbe92cf0eaee4be41039b13e47e9b1ee8eb944365f5fb20c4690812904076c2c1460cf069b2e50e7aa0ae9493ee2088e6809fe2819afd67075072eac96933a339dfdb08a239a4b70922d190fbad8ce670dfd9da55f2622492537e2eec248a24a2d203c0097cfa876ff2de392fb9e712a81752c8903d2f91908886502fa488217b7f4f5db8ad76d1c96ca716602f2d68b2996ce1835e78226db3daf348445dd013900f36b1769d27209b281f42e9039c826c844e40e129e5a45da69f3679a02e530dd4ec6f130ea927a69418f904d443f6def463aa810bfef269b281a477a3c884838d1e6c41f4a68c4d904d50f79537da7f859afb46fe4a2bf39c6b42a2e1e45ec025370cb50c0c979c8fb3b82649e415ea0b32bde14a2bbb31c3a74d50b509ca6d1364136412957a060d1410ea87d29617198ff8e8236da48d3c03c0a14b1a309e1606908213c0c26f811a91710556a58ea3d38cc058239a6365f5e7c7e8c7e8c7c8e8279c895fe2380e3c724da8b0f02f797325e2bc2781e0832f921d9619c27df72470df77a4e748523794c50afaea6c308a19c58c624631a3119ad09b36726b6d1806b9911ba180442c543e47b55aade6af71ac8db5b156ab8d5f3e47319f23b5d15c973673705cb8dae85dd09bfe2edcd6aed3bbc8fdeec56d160d3f7277f72e6e38d29cd2d94337bad4bad4aa8f9450abfbf1ef460d9f5cad5d14e48cba25adf287b3d58e70cd6778e083593d647f8aecc0c168338ef466269ff6a7b333d017c7a5f6c3d56a6d04bde9aa09f40592280c72d32e7ebc546f123e8904492118ae06724041f84fabb632017cd29f0096a4f2841f0373b55a773def7a9ee77924aec6d5543ea81c665dd7654121f6240af4c61f4441003ad4cabd83a9b51a75ee2507a08ca159bbba775b9bb9b3d22e8e2bbbe43e42d63dd12eeab536bbfd766b9343a1fa681f2a5554714b8051d04991fb4724eec5789c9dcae0b38b5d2221bfed781eefb776519b6bfc18029f7e4bc27d4bbb66ef59da455a55afb9cdc1b48b8e1d676d32b5628a1264ae93b5d91d5145f779b626532b562094391edf3a7ec79f2e63fde94440a3100eba9efa0f1ef51f3c8e273f657a54e967c43f858fc13791bef4bd783f7c90f45f3dca3783aaaf06c7d5d431f4e0863d1a914ceeb71dc7d5ca55ebd584a732d8967e6443abcdcd4dc9d302cc6f9ded7b60a00e9245ea2857f56f5825eb75fc0eeb757c7d1d3afea6e4d151763256d90363911446c52045ba580d14d8b9c028cc69331d248505c0087cfa2d6685dfa81f75e69f75f6c19f9586e3cf5a236badb73663fe627dfddad5d8af6ff2b0f9faa35d35beafabaf4fa4daf80a7ebdf1f533700449bb3af8fa2a2461a15d1e7c7d9ed29276fd4f937699be964e309476e5108a02802366acb40b84a1ca72b33c3bd907258e922ccf6e660442ea713cf9208d18847c904c91a90f4c3248112cd267547bf8fb93d020c1307e63302adbb2ff05999080b4b83fd5070907da83069a43d281def83b500866e0f303cafe9fcd5ff7623c8ef5b3657ffffea26691bfd057ed4259b6eaa36af9964187415ddcd360f4d2b67c62027fc7d1ea96366b8d6c9828f4c9a08d46fffa27ce9e422d5338cca920f645223dd96b7daf61bb2c13600860e07efb379ee0ae7f8307dc7fd625670f19c1da28ebb44eb773a9b7131fb4272670a5d19c2dedbab524a29452fa558b4802f7ab213605abd34db3421ee4418f717243a94ec8d20d6a1a51f5d1596a8f7eefa1d4ae4b534ca692fb69217f71b28f09651c24d4259ff688dc312b94a5fab0425822f2d959727f075870fdd30ac1a848a447241b4645126ce6002dd37164d8e60c0d905aa7b43bd5ddddab12b8dc4f57c85d6d0d50792613d129703ecaeaa753dc7b517ea2207dd92fdff5272a5b9cff1c6c2aa5ee8d1e70bdfd4345ddfab7db82f1f90c072468822447c66d26c089ed6b8980f383c8095883f20297c61333a06030d3d91330a0134f649083275b5c2a3097e9ec8996d9adeb3ece7aeb001142a42fa6c974268418d9661180673dfd3fb0de367380c0e7e546a8b4ab535baae514fb0eb3e5aa04faf56b7976a6a5b582ed0a3623a9b4ec3c6081c5ed2e024b3de0fab67a7d166191d11b6b33ed8a28a5f62bf704005ab470eed5ba5b263817707db75e2d13b87ebbdb2ab34c709472d6bbd28e0a4873544b3baff3bc2c5d9656b5defdbaee8ad9c72531c42a81c1aebb62067249743decb78762d75d3113bb9691b806cab7be675abe19f44226b0ff0dafbfb8ae23cd27715ade8923ecc8f67e24cec60886b4fab0365bad091c8c2b8dc882385006438721246147aea511247010afb4ec5e33a560747d19a917a82faed471d53fa918210cd587d7fded2b010d94d9988dfddb180d4dc9534e212383b5e04c24d1c40ed9988dd99825b23299ec88d43d087a1e27725fbff63e7ceb854a6a3ccf6a8766fcd5fb9b346f435f76867bc32a92e09567a5c97878a8a08981d578507634aea8d880a621573b347fe36f943c379ee606e906b9eae046b922916abe060744a7078740d96763fe02819ce57f2f908d81402010080402d918492c039fa0ec03037f436f65a9f79a57a964fefa88401928eb14ea23fa883ea2f323ca5e43da7c8a44aa212d2c45aa211d4622f5cf083b32a9f4686a901118620b169847d23c7392c56c50f6b719a77ce418924868480a1b493a92ab223523d923596160a57987d1943c2dc068ded2d4d0d08cefb0b1864626a37d3da856c588848484848484849c95e5b7c0f6753c7c9d6efbe1cf586997f7357fefc5781cb9b26c599e994f796984fef053686f40c3505fc3a3aa51d5d4d46a7e7c95ea6d54634d1916a437b4b6dbd27d4f484848484848280c49081fcf000bd11c6ca44a91ab9d544a8552a9bea6f4da0a7cd5d73c8a5ced8c8ffa1155aec0af01518f5209c94c0fc6dab542929b4fef0f0af90bf57d27527c14e8e303920dfab8d392d35209f44191347f024ba0859d4014e930106cb0e344d5e333a4499d600f2f4c752792da2a154aa5fa19d4cc0c6a464888c8d4a30d409ae3ac21642db516f471965b1f7f35cc6dbee66b4a9e9ab729794a40348102d682cfd0c40eb02af67b60270894fd5b2d1baf5ee2dee75e8cc7f11ff4b1b6497fb00b4db44cfa12e883f10cb0cd8ce6d015647fd087e69cb2631ca32c7ffc949e9ed2d338fe679a3b35abaff91e980db9dab1f9f1c792677c9bef81812bd26ba4dbc01aaec8d58e8d5fbd8dd26b2bef576fe34772b573e3c7bf51bacd59fe63b9f2de46697d9ce56f7f9ce55e6942c3c61fcb55e947a02dc32fe88381dad52b24b9197c7fd0c75fe3c3b8237ff9bd7eba2aa7fea33deafbcd552a8c6bbc69242d0c243d70b3473acc23c5b7b1fea965ffd13f376c910335ab7e0bdcafe31eb9814acb670bb29c02e94bdfe4596919fcb305592eedc8de9f3fb20d49612a92e6147982b9a2c8156c3ec37c82a41bb142ad61425269d9be484e193f24572081da3ce8e18f7f630cf6421b92c2ba1aab05956c411bb2611fe981249eef129f6cd3a10d69611f796bde6135254f0bb09ad7691bd261d4f3c6afac212dec2b6bc8865d9b77984dc9d3026c2c1d06faf8b4cbbb8650aa69a600ab124b28b1020b408ae9749a799f29ff95f4180116bfe52cf1c517d2ae8b31922319fc9344a6d0fdf7dd831d0982a4f2d371f169eec4b2a1b4496ad3fb9f80bf4b25fdf8bd545245094d0090c0dffbe77f82f73bd8e4eafbfa77fcfbbfef92a7cad77e3b772f4ddd5393e969cad5f718f7b7dae9a7214ddf247e2751ddafe4bb08a505d17a9d56bfbf922e8da8406d5e5267894fbf076d78abc77c4bf61c8c57eb25751ef323b8aeeb3ab11cf2955d596489e971f820899fc24cfea63fa11f974af0f79bca7e2534ef3f6bd3fb12d9596e7dd4ad3ad39f80fa4795279894e037950fab1d0db9eabaaedc493daae4c19f7a1e1a0aabdc9b3e02f653afd34e2a5bfe4a954adc9fa65c75dffd7845b3dac16f22fd31d96f225124751efc4a6a5e49aaac3ecef21efff834e5a73e88b1ac403e4f6a4a5c7aaab29554d9c16f7a254a705989fcac4241d0781ecae4910da54def4d640f39cbfbee2b895ae1e3e3e3e38215cade5332d9b39d6148640330f98aa1e53387e7c14eeb93bdafa4eef4287d24e463e5070ad02c68c8a3b46b556d6d7a69311ed7a46fa72fa9219fde8486be656f867c3a94ec793ea32cefef387a5dd73d0936773df3973784df7bd0c611de770d7cefdbd65eda4456186d17e83d8849b08a45681eace22a02a6c7a8efca1dd3e392a7c24ca8c7a4e96fcef2bc46dab2e73367792f420c9b1e3fcdab1e857a9adf413d8df75d598d9ce53daaec1f67799f2a4f9f19b907f3fc4dbfe36f2a573bfdb85c75a6dfc16f323d2e77508f5165eb6272f53dea7b602672f5e1df313dfeef71c953e52b77f0a3bac7cf63fa1ed8e9f37e158aef64da75f6ed23536d86df6a8bf7dea55d367f2d11b3c81e18effde662bcf723ebe3af0af33e14bfa6a2862cd9fbfebeefbb25f82692c270bbc22c4efb9ed4d33de94cd21d79f6e42e05f069eec852fc8e3c710b99f4a7a5a4f20403fe320095288208220ca3255e78132b248081133508511b0373772e87a226d9dddddd2d17b4052f7801d5229f3c8470011745432841440d447051148509292d2b95473e534232e4c6a248888b104604e951871660ba30a9f2023266e86162cb1442d8ae3831e30b8703172cc27a2a402d081c647f3c060144f6372bc5e92472d384c8a6dcdddd3469609a436df5bf188819003194fdaf18c4c7336ae035c18296f2030fb9ff1679b9c5a00c229c4842043698f1c30db2386a414c09583fe5807e48422ccdb10e258bec5060f0e48cbf6d858ccd8230729f727777e3581c3a13f2c4bf1ad1200cf269c3071e6e7cc0e2430bcc34405312b4562055680d2474b8d806294420b30108292c30b2a1490e7ee39adca0870e3285906d62d3a1d682cc6cb8c9143d0461c10f6d0c192d1840a63332b60851062dfc604cdf10c6bd95c9ec894bafbd5cf4e0e1a3499108c0b861071bb4742bdb1c6a7db3f3148a52da6754dae2a83b7e1d8b4d2233652f11111125f230345de0fa1d2e5328ea56c5cad47ab5d6ed69adb596e338ae6ba71cd7d1b28201a8a26556f7724c65fb1cb6d65a6b2dad443905fab4e46814998a2f689142924a2149dd1bc36c069862fb463ab184ff2e1718604ab10ad6da54b648b2fd94739dbd626de765c98bf3834c5f455f98fb4e94e202506aa3cfa22f1d1d7a9305c65dc782402a7b1e47a7d0a109a02ff0c1b2c6681528a8319a8344ee0bd4578d552af55583685de20aa554508a05ad415f707daf134b20b3579a43a23a35e6e30b7075afe4b06842c3462e5437b27d09948073f735c6597f557618a5020b2eae7c912d103a4d710548fab5288676c9c05ea6453454c9e78d626f160e8d99775b4f00d98b6f88c70475ad2b6be26486c89c170b01011943930108080819a3188e8280a001a425065768c1ddaceb82c34c67529eb84e48598226a50a0899ceaa88a20cef76ebeed6dddd6d8e3581663d380277efd9adab70a44da33a037de17cf988bfc6ebfed4b648c25dd98ee4ec77b633d2007dddf7770fa3017ae38fe3af5b9e95e5fe9d9f71d2298ebc15d8fa9f3b96f8b9d75a6b74a4ab4246968a5044c394dc37e4fe2b3ec164a657c218e5d3e68a83e3c457a10e055db2bff5287586235ad96da9faa9ecb48bdc41f62219d0c8fe4394fde78cbfd7fde949b3d0d10a2c5cb8ec2f64a75f16e6eeee14763305bd360c67b98e8507c5841d0b98e3bae7bed29705288b7bcec671dc8337bcdd57accffd68937b8efb191a3ee951500f412e080a1a476ed5559104ef57ddd72972c5923b285d15e2e8d107e8eb7bee59a039fddd9f98ba90b96ff203f4867b21fefa4a10d4fa42e6decbae3ca90b58a037dc631560afbcd4e4b8f7efba7b31eeba7751acf7d61fefe5388eab32db601324213029d3a2186af0518167322d8aa10bd704fe322d8a210b8f09c69916c5208517e3e3323ed4e02101060931488cf7e92df342bef753edeabfff34a7cbb73c91e430f1fdf386b7c44a7869160573c5348b2a41a659b4ff96039012048911039024645c00015962a6650619ad8a188054b1b5d220aae13a012283d22c9acf8b4938688621c668513184171b99ce8628734d400aa165b676b0ccec62031ce65f4910b2b5254f08605e3617d75adbb296479b66ff0f7a43bfd5f686b0025c9fdaca711ced4afbdcbd18734ebf922704303105b5dd729d77ef77edad1f09ec606ddafbdd7beff540230e46dff416ce8f4b5f46c42070f570dc70bb7bafcdc6dd7bef6d97ff2dbbd288b3b0e0fa46dab446aa8edb8cd86cb60bb30dd2607477e79c72eeeeee9d53af2b4b5aea68e0bc7b45606dc7536d57bb1304d9be735de7e58fa16fe97bcec201045293337120bb474dee1bec40f6daedf2a80976c0a9c93df53ed0aec46b575b826c8bb8186c8bf80b5bdb4118b86953dad4bd833070cd20c725b9997bfb780630fb289868b8e52cfbb50ceb2df2f543f8824d37d3519bab13c0d26cf9ab05abd682ab2129ac86dc5c6ee064218b257cf99629ac86a450ff2b7fdc52a74dfb344f6087ded80749234d00fb1c04e88dfd07d01eb6489bf6bb750481fd17e05f4b1c93e9af222d8ba44d6b3a32dd7634814fd32d5bda7577dd5c37d7d692213866b4668c7e6dd22fb359d5745285bca7c1cdf1a2349c175c1f1c5dd235218a209ff5a64a192fcc606082c30860fd1a6b781e9541be25c89ec7c48aecc5d0c4a341039fe6add86180cf9b6b98bb54ee9e86c9dddbae088659ee9640c9dd783367b770418ea36c8b5cb822dba217a4644b026bda8e656fe85e9a189f308420308f221f191a8d22236badad3dcad4b20f2bf869819414d8400614d8d9cad6bee9af1a60c698228b1dd5600798add91762ad0dd95e914512d986d95a6b6d8943f8824fd3acb332520690bb2f5e01115ed555a594e2115fcae351224584507a34c3639b674fbe658f25d423b5a8e7512f883c126d89803394fb7f504ae90a63dc5406b625b53f0a163f21d6893496e4137cdff77161aa4dea79a510b2452271c2558d26e19b4aef11f9783e8431f089851469f37b30c1dedf9ff1f8b0d0a6589e3d99fe19e610482934e1b459fab075a44d138bd1482344ec3ed448d620213327fca017e630fca73ea82bfc4e0c120ae9193c73c2b585858a4144a8a909e1cbac45e2b4a9fa7e16bcc334249076d17cea5134aa31639a2aab1314256a53062c1c75b3b21b427afaab9d1a5fc5fb1adf035b91ab22366f53f25498cdf7c0c4b72159a83eceea2791f5a736a941154a9b9dc5b23a216926913be02225f511b832a444355d4304d21c5d582bede23336d18c4c1730d4df86cfd0e892bdbb9bbae877a6648a94c1e85d6494bb3cb1181f1e44b89f0714f8c42bd84c7d6aed6e5e3d842ff8c446c823a82ff8cdcaa35d94479b3928ae7f6fa53b6dd623a591366bef68029f61bdf90bb8a06cbf921df7fddd9fcd9529f45bec858e777b18527b91b451ee9e025a8a23c3a976d970ac9d6ad76d93fe6943897bb971bcb7b4e5890400b4f1b3a8adeca5bbfb58dfebfe035ed779b7abf92d0c30674b7707000dbb3b67b91c9e839adfbdb3ebbcf2b4d97bb7ded18ec4d65a29ee7d106bddfdcbdc7f524a3ec4b0c12406940f8730a21438a4e1c3859f2c409c22ae8f3c3b830638d3d9195cf297e9ec0c1a1018f61391dab2964c604a733fa263a5bd1ff9ab6dceaa7f2faea4579abfbc095c7d7033f7f1d77da782deb8fb64e7aaa821fb73b3eae3fed41e1c1534e7d2406f1a8a7c92b82a3e9bcd5fdf171aa435a874edb645350bd5d0000000000003150020200c0c07042291502c1e69a2327e14800d7288487862369508a324c7611c858c2186204200010022023443b4158000ff69ec42ea38e690d2e044c09771405c4011f467ed583ea67afc5fa7aa6a316d5e9a1dd56cd1aaf1b3b609c825222205545447574be311a73f31bf4e05ee1657d85b386e41d3b00a23636a44e6fa2fa039e24c2673125ed7ad7d328de929c17bf295c09db757a2c79890e41428a6cd0e52ad43fa216b9a7394e7fbc2141f24640485ae806dcfb7fce7dcbe09a6e9aa59dbae52cc931de88dccb269f13d01ef387ca403d31fb643977960438bb517b61feb53340b00f295eb3270fa18d33f8f11c64bc4116767d750087301e98db1ac6d4633382414a2e84a7ee348e089908e6df2383bcbb9c33a6bc4070fefaf2948a7a1672139446e54003ddc2a5340b367ea09c2438a2ca20864c9209b7dbc8bb85e27cfa99e25c8d0abc6fab5b55a0743941122444c6a1bdf349a0e938a5765af780914ce2c5db8025232b71ffd1dd0ac775632fade2dd55129549b129d026ab5c6041005acfd090d752c24c4d56da5cadc13810436fd834c8ab93b6f2e8fb14283b98b729023410deeb901c023b03e3cf9a4d846dbf9c55a4af984d842893a492bada78daa4c294c9b67788d3f9dce807d24c5c6fedcdc8bd1e21e39ee404dff10742c2ff1d630ed625d27da535565cc0001a4b0245f44677d574163e562e150e7be59943cb302e07fa2a4efc54b8db53cf6986a850df30e37335b3b657c2835b3967f2060ce1e25106aa521541cc3814b188050b986806363df1fc5ea20c51005a52a31596b730f98c0cc1e4aed5bf2e5b19bdd87016826f32e56186320193535609acce401d97f64ad000b3d25ddb2c82d22d70d43be22e183e2069e2011ffb7b220b94ecffddffd19bfa5f8a595cf2b128dffd313b9ff8f9fda799f172012ce14c7ba6d8a33a9a53dc88c8cd69631ebe23c0e289667bb6070e32307e2a9f6165a6751101d7de9676ad68bfcc572445e5cab4bb37dadd63dec892e3f114406ca6b2a9f4b423fbdd351a0ce42edbfbc3ce3a9adf66800270fed0445f8babe092448b4f7a46b00ceffed2de727ddaf9732bed5c63783366e6a6a5494c8519ffffdc87f4a8449193576bafc52a71d86d5abe30bf100c92117248556d63145b8d5606f17164d1b51f00073c414f4ea151d206cf3deb5fc57af2febb8d4a6caf2a2d224b3413b3bdcb27fc7eb62c8eb14fb547925a9a305b0a70ca1a4c004949a5fb2b93e9114b0f5fe9230420d0da71822cddb48a10c7bd5490fdcf66197f1e71a92d95ca267b8d7cf209351a1bb251b13ef1772192e602e8859f2d594776d2ada51aa784aa86454f6d688c2cdf80858cb1429d03f947dee07d4b5f59c59fe29e56f1467dbee3ab96c25e23ced021b0de1fda24ee8ef1b2de94bfc0fbbd6461903d36538236cdc14000c2f26a98ba5c5af2859717a020ef114d0d276f682053e452d11e68f8890db8ee607b28e8f94c1acab0f75f60373db21c1f36e191403025709c3106d4895a84cad8104af3a155f9d706d422c4ae91ff3fe2c37826878154d8b4fd5d9302f2668b5567bfac5ca3d5c88e0e4109622df3e4d3c9a0a527e995df45fe1725ce2776c77061b08ed11865d1c0680ba9fe36aabbed77db764b8e5fa8d7315e309d8fb99ad48a646cf68f7822087d8bb2919f1f2c707c983fd950a3db7575083fe0485b6ecd21dd4187a2ae394495c4119eb16264c4d86c1b4dfecbfe986b90b895666ff65538547438c6717f49f2a4c0a4c2ac2dec675edd4e592905a79177534d32d35b72dd221944152a7f80558b11c81ea1cdac0a7ae513d3f74d684a1ce39dd8cb698f31e100944dd5064a6eba3a0073bef4c5bd733b535d0728b0767516119182573500615c1a1edb611f9fe0b9f77b7d1b058cd86065adc7d904e2306cd7f29e69298d9c17fb0919cd3ddee4c10d8cc452f35a3c2fdcc93b02806f1fb0e5702b1448d5357b7f18d661be55ec4d9e098d10f10c5abfa02558e33f1dbdd0b8b4243bb285b1b8cd0da321b830056c96d86acda626f4b1b5e6cf417d834ddf84eeb07a7ce8f3bc7d4ca8a521174b7f469853886bc0269ad1e9e6b7a9205501e2a970299570d37c8c19f382dd533e0893315a9bc122b577af75c500518a95cc0e10611ee09ba2ee657e62b91fbcc5bdffea348643f56e2e00585416c3fc14b24e2ec4056e16d594b64fd36477a739570b9af84188cdad176cf5954d99df8ce22655517811f19870af839cac8680086e78e14b42b4d3c0d1347882a8f1fd3cb1dda5638a0c6b7c10a2b7b1db9af30954bb1e703ae0393c866819f6ed5d1f073013e24cd6ef68cea42235bba7cb4d2869d143495deeffda556f56e58a2d8a9f96d377ea575e90d9e582b3ac035e900d63c0e6c5aa538923641f330e476e26eda339b25b7861d3fe9e15d5d550cadc8625c7d60f60fdad6f962a5a1bb139777fe9e17ad9f64a11d9a1295f10f96474363a6b417633594346085b1e56fc4e8c50f84af55cd9b7d78d5848381f8c1861d31c22558d34434c666a505bdf6eba7ba1daaa781b138de498846def6954ed57575da3594d2131bff6ff25407d2f2c75d02467646ba67d0f622bf9d8c13ede1d387aadcc225b8200a7c8b80c77ae784a030721c232a56cab4f3796dfb5e7be40b344cfcf77ff735a493bf9610a4f02b2e72b882a3bccf3cb8fd1deea961fa2783ad4c4033647447136359ab8506b76c31b86b41de7def0ca5bc15a40750a646af86bf82425c51b8de8614bec8d28f4bee9a9b9ed306449808080a3d22209054ab0123a4a344a9da581045c4c84b6130f87d8dba953dc3c7ba09697fb57d1ee37d9072b317be1ef93e7c085363c9f90dad90eed6ad46a7177a90d98d7b83ff02cb90ca00120bb51add79068df553e1f0315a4562ea346ca6b5532f14363d973e0a67819cd0076c825c6b38766a22f300f1245bb937c8fcc3debf446a8e02483b65238f46b081db06d6840dd2e282a1c16d64b83f9840e76a46889c3611b1c02c8548da19626004028ac71bcb6e43e12fc7ef9906eccd893db8e1b8b9d12fc436ecfc510ae210197c999aa0af378fa1108e19c00d6c8fe75110266df619eb5644ac6526a1b82a65d3348108814c2d6c1392779910bd893d9ec69e4d5a5a2ecb782f03f5ce61e26c3304577712087c04c55dc96274f559ae44225a8c29b883cba51ad89d772f1b4112d3cbe9e105b631091bc348b8e8ebb73af926d0ec9594742ddca5ff880c99a233363787fc47edc6a2166c8b2e42cd8794937587b244835ce74f1e0a0be443a993ef5e141162bc940a9df9f26e0d464f06bf7dac2ed6e0dc05867808851a76ef34156e6698b57f7b2fadfc530e3656edbeea5dcb23a6508b22b2c1f2b080462266d97d34b8db4c4888b4685f53349a61ef52a6532dcdaeff5a11fc9f944dd0c9cf6a7d32765268f5db28ec8260c50c83e22aa99a166c1c3a1b7bc328aa743292514b6e59764ad34059ce1ede5e48b96f7c4d5d1923508adf26b0cc644a8f65be79b19fe066eb3c885f0c096aa701045f0ebd61df89f6821f2fb702843a050ca340270559fd68f36eb43fc44baa710e50418138a54d425668d920bccfe21e39536b3714a91c4ef305c4ee4c7926549bd1f428fd0a81617a86b732108b5734f9cc28fa7c9e2032ef9df117440d0974f851d041e6e9a22a16488abbf539f35bbcd8dbfbcfb20da08a1929938aaf7621addbb4a96ea0c538db69f87d0ccf4e12cf82f70666bb0566ed5c07726881ad405526622300131f35844e3e0d71cac580fac08779f7918cbdd6da2a1bca94c9220850b664e0e5f4bb48a124c073e2e91e00a6a3f8e100713950068741aa19651729943cf6adc62880303175a34a913738902f9557370695395294b2b90f628ecfc0002015a58caacf15b9b2ce6f50ede942a31e851d134026e5a883c4aff869d348520345a8904a5b22aea98153bba8a0a2f2c964f38c534d784d17620467fde8e362913104424eb3cc5bc7db85639470812af75edf2201f6290370860a2a80699606147c3baa4842a21e39f9417bc4445836037bffc586f5153f252e677a57843a2004effff05083ee0dfb49550d225d8f029899408ae646760386a603ca19cbbc99872337d001b9c58f4f5708badd91531e90c84506eccf1ee110b8b4642e91d1e9707cc8793c6e310195c02905350827527a785f36bc06c55a9f3e8f6334302b5275aad8825f4499ae0537ab0a8934eea96d4cf8fdd7ea79d0fb4186dbb55d1ef97d9272a346a747b94f8edcacedf3e866a25659b48b9716403b65c2b93659bff9d83677dd02d0ac0615ad994d37b0f42dc752c7b0e20be92e6db1e047f9e10f56537421c10b1e3e98b809b47e440656fc24d18aa6a40d17738521c87fb02a960c6bc0acf591797177d5104e84efb772b8cc5cda0f356ef36d4a02fbe9ebbba7239fbb0916a2f9a783c672f4aacd1fa0058b55d470d5e040d48487f4ec1a156ded5005aaab19b052bd9a46c89a8ddfce283c35ac420339e55befa94eaba9676f1d897f590f0e5fbb67e1cdb602845576591caed6a0147a0aee9c77c199414e73e12b79abd43d568aefbd604d163285a1b41988ba5dc7d36e1e2346c053498df515bc4e425a3a0ffb776c42ca917f7565c44ba1aed8440eeae84bab8a68285144f809572bf58aac2484c141c5484221b32581dad414dac0a9d371f6cb4dc61df8a0400497c1f35e48ad725b8c69a9859834e7866140b93d9c84cdf5c4aca2a2d2d831919947f3ec53dca6380da4857e290eb8b6dcd1c53fdba060995507fd92aaa8a8452a1effcdfc31e0e62ca562de7036882c8c24af3692360a0dee869be8a2f8389cda3db718491b8bc31e94a25f56db738cd0be367dde84a0c6c54fb8e6fb2f9ed81f270855d468660fa97a2990d780f568a3acb3fb1b91338e6db515e082892414c818e9a089cd5efb84c94b837ea78174d23fe59a1b749182a2f4ff2753db2a1a56bb25815b3534918ffb9a0c272db77f7db8e94bca4f4105f55952257daa90414d3fc7547cc35aef2fc9e0f42215e539049627f1a32cb3aa073ccc8638c47617c76123b5d18aabf010bc03081bf546d83b429fe0c9f2abf09e6725d90c02d0706c7842e74900b100bc6b09b7944f64e7160aa01ddbf2b505853c292f7d3ca5a739a6582a68faa69e04726cca9a4a46bf4dccc04036b9415177aa10cb67df8a08da8e083a933bfafeb240bccf61083b0da8c640f78be3dd44beb15d0538d2be6377f633fcf94d255c512324b4cae838c11a033b707fd200ce0e6c8eeef0bcdf115bac30ff4eb8e0b3fa17742eb7ac9f655bef69bb0eb446e5447a522ec4cbd818e2dfefc1e46208e4e9dc41cc0acea27f0a9fe474cd6d86cff5b788b9b77cc58e9c8797567354ddcdbbb3a521d09f82df8c00912ef5209c0cea89a6624dd408aa8bb6b3325c53d4dc61d09d295273ada84382544c4ba26ff95059de70a57083b775b32c7e276b29678b892965c980922b35dcc4551c424e6ccc62e0ff8f4564b86d2600062236125e4dc307db8e846ccccde4a9e01dc8e37385c98004b8807def412949f46cad51b981550234b122b248a144f1a4c541314adcfddd66bdbcd8c6e4855785b7881ea56152c9e235b575e98445a95140780bda19d718b8af9eba8747963520bf645f616033d639ccc851c25360214181670e9779a08c1b98bb7755f855cba63a837aa71714752f57ea6167a02e3e058ce3ef01346ecc331f8b89d8180e14c3e00814198f16ae547e80b18e91cbe33e3aefd929522a32084de036359c22bfd14a05078e353d3dce53eae69dd5ee5e67c12b6d910b6be651ee39680b5bb1705b9ab061e46adf20e7189484804a3fe56021f5edb856e07b7d87233d8d68fbc8123c55072caeca12e92b31b1361381048275c5c425eeda0817271e01fba5a2e4b2756c34b148f211b3040dc73dff14b45c4818315edd23dc4251dba8da7e3ce05a99216f0682675182f808a1b0c244ca01b2c1185189868c2ad22d6d1eae74212bbbd01d95362c5a52b7bb65bd981e553f640eaf54ad114d35be7803bf0c42b7492f3f751356f668a23b2d48bd88edf76273aff7aa0c8490097f3d3be4be9a4dc574c06e3b79d251db544211931aba328910e1328c1a836b357c55407e836543f324d7d9fea249da4b699a48c488a48c2bc5f2e141224b9e56eee64e49a43d1f4e54492c5f02be85ac00a988a23db4a3da7bbace5ac72961ccef75a235be42f62227097663c8b8f72b9eb987801db80ba6be4131429d79fcc2046510e818ed7baa8e8b8df28ee687e5aa258d6ae79f8f1fd2836ebaf26b340a62649b7eaf30ab49166825650402026631916d042cdcf2f39233fb4e66626fc20209d55db13661127eaf4edb9ec7419721db35d764bf02cc86621627de70da998f91ac05f6935ba49e9442858102470c866e62988c1a95e0db30ef687e1faf559a80a5d8303e08c622c4f339638901c102ab5b5ae3b0471c905e4c9665ee9d3de3cfff7a3333e77aa52d7cc91b62270eae2fba513235f74a13a7ce642abab46166713cb84dd6e1bc31a0220766cb542fb795f8596c6d4fad77d0c0213a3ad40bb5a3855dfee4dc27670c6c87346380678905e8314dba51f512887221973f9f50cd08e9f3b5617926fb4dab023c50d669c4ccd94ad5a409a2fa269e9f5cac00b498c5f1eb038d16a447bb547007388f1361d9d6aaf0800d9ef7c09e3758127fd4f0580f40ea6afb532fdd11c6e4fd689985c41b33950d291f8c0c7095d88ce2f6d296309b15608670dd5b950e40331f92775123506da2ffebfab2f5dec45fc6da9cfb87ab3cb02587dd1accf8a95db6683750457e6fa82a0cfe6de59e0aa8f3ef231ab1c1952158d1738e81d6ea96ca04a3c52522b50201de1f1628d7ad79ebf0b316d6b6d0ec7f3a69356ffd03775654069c14b6388a16f10b6a293b95563f871b87424be1bfb88ea564af503762aaf3511fd22ab78f4dc2f56db9aaa646f497b81fc111740c5d3c1c1963a06c48a21fb995df6372b1eff394655f96ac89a58c067f7d93b9c4413ce698fa0ef0d7b240f5c19e925e874c9b88d428f2240291fb088822c3fc479fffaab5732e880905e1d92e509d642f28d370fd34b9b1a9552c8aeb2d2b41363056c0a8bf298502e381960a53a2e99f71b12fb49b5361e9da7c633e6315daa984cab75bdf00e24c32a1fcfa7cb5fa0d4c0ec611bc537e25cb3135f43031e74c35a2909168e09ab88d5453273a5f9f920ae8ce20b38d5c72c19dec16e6733d4077ab4bf52805f3a71aaa69d1141a02eccae1e0241f3c39a93ffac3aa9d44f2a858147c6a478842b13a9ae0f581f3b9678361013449ffb5660ebb10489d8a4ec22516c1972e7497ee300d3716d1d7bb6f6a0425ee4d69c7df94a8de004bdb5cc7b2cd09684ebcb9a7b90b052cd09c24a264b9783821bf91b68490f9dcea914790e8246959e3aec5bc63ee80c09a70bb89c875a8e5d5bcecfb47605bff94af76a33b9cc33d90fa53f9ecd8a0f617263e93597861ce61710dcc1538284a79e2e65705f6872023f4cbf3c605bba166ca28cd4578d20d080c733dc0425e2243052b505eed8c1f084e3b8563e293cfcf30c4e59474290ae3895039a68641968cd346683360c1c3ba646af62eacd5f186174811e547f153845997bb980542bf000b2defad9c2fc56077cf19742deff1128cb95c41f38e091b038087b1ff9583e14f19b7d8a079f7c784a03f592bc56a69c65a06da2df5fcefd540a04d7302c5787bfab7c35401a5d7f2cee1d0a7566ada64372994f2b56ccef4c828a7487c970bf4319d4fa243f0d535ec06359f2a3ec1e07bcc4f04915d4296bf0d34cb663ff16e76ef4148be3be5ebdec95d90e28728b325e937b53f569a09ebeb66be79b48360da7bc72755dae75613d8f550b8a18fcb08bc8d25537482d35cbfacc13a104db5c181229dced1b8ee0d7a2a3fe789484a3b89aca7fe828ac87edce007d466d17ad8b14fa33ee7808a9d15a8fe0dccf75b9fed46e120d3be129d19c073c75f3c259de4002e93b22374663b103052aca21178b8097dfad11015dc1e59e2322e449facf09cd98c6b6663bbe59fbf686f03c90ec88a97945de579a7bd6c2e70a747d1e1b5b307c8bbbc6fe6b093c3d5d4dccda8dedf4ba2eed64f0ba702083957f2c7105719d41d2e96bca2a60e11485441ae72847dccf6de4f3a4acb766c50dd0b61dfc943d5c46730d8f17500451d5649ee33d8245e7552e44793c94d95e6c8e27248c78fb21cc49a9bf589b1adadbac1839077b466756d6eced7805818b8af23890f96719efc7a3d0016720c79ece8a2db3310cbf0945c2ca37aab613a48ffd68c4a894af14c112272f8f13ced2969a68bd5e4fca2d3419a123aa511c384adf1a574f4bab147937500e8f8d48fd27dc7ae850677bb649b0d1abb961b1a45261cdc883b6408ed5b3cca2440733beda0ca7f20f622e8d8fcf4522592e2c705c9d2de47e5cfcacfaed53c1c44fea6bc2f5f0686cbc2eaccef864ef663e35e56225342c854979ba2f86e5e3148a8ce26dfb4636ddd4a4d68a21a01cc47795a72ae45e3b8a6274e6e1932066105bab4f57ae19f2d2ac33c03b024e833666b4a4be24318443863c5183597946d2ac119cc819fa8c457e9c187396785d7f30e49077d50493cd94aaa66d06e80896a680dfcb9fdc190731bcda6ea354ed012fe1780947ceb223de3dbfc684652b2a92605d3a08a6e5e4a52c584f87f64c5733900111389844bb39fb94a81b7c276a7e36f48d6d1d10d45360f313f8f6a622f4bf905876effadeb985297700ca452a791a84087c9d1d6418feb20c93e4b4d35d1facf9efe8a5aea8f1f28435d51e6fc57bdf86e8012b994574ad9af5a8a1da2f1482df6843e2373c9a9379d360730ea31073e405d445cd48ac3f2d17fe4767ab9f940a00a4262ac0b8be536fa54cdea0940dea45e1cf00c0920813382d3d152b2cb3ec9a786eebf891a054b970b5cec86eb216b6e8d68bb0b1a047c8028ea34a40eb77221e057008bd6b26c832e69d3b3e6994ccdf8a1e045c46d58df461518a4de30931d30dd359b5b4043090d9293b611df01e587c1199f3c75f889743f359932152eec416b54fd29af905a08cfd1b80c8e2b4194b9baa435859a9ec167be3bc7c277a78ccc1599f6de7bbe23cd4513aeff5628fb2fb7a053103d9cb8b1836c3a46f578ebddca2509f5ac1a0d0f55f96d364fcaf18964d08f65546f47d93993cf7cae52ec54aa9b0e31e7ee14c1bd3a4af72889753f6cbbe7c2b2570fd09603e440a378ad13582bcad526f6b684a18104570674a3f043553cd7d38602d0d39f5dc59685761bc5ae8d146549276947677b5a3322e1af9ce4e923d8d72c3672685a69a6ed218c713898860290ffb19ba1a6fe04829c64976b1a27148f2473b6152f232683b6c5fae593a9cc091344e05d2dc701601d9e943db93e1118f7dd0cbb5ce31e56e545b58687b0c0b4467f33b85031b86d52250d8018160e10f778350d0cd39e379597fdddbcef7efad49a0a524e8c3248c70b6a8a97e32bc6724f6906c0479fb26abd8d947afc379ca412d4d2f89ec1bc2082a4f0b628137b10143d0a7200bdd58d6259d9412243efef0b5cfe6daf26515f6985c0499db9d5a8d10e8bec2ed6d331f4694a77d39aa51797d84363bbd7967c25b415202e209338ef122354fbceaa3af9039339db194f8502c0729b4dddc4aea9764658b0b3d60cf97e5fb2544ad507fb93f217a5ef4de34e2abf5340c29e4050e0912e57d3401e65b714adc347df949aa9e5db130484d89e569ea644d1387743dec88d72dcf200cd8e089379e9b841d15ee0d9fc0ff208d4b39894a7f144fb2f99c31039a6fc52233cbd7f00bbc8424c97fa4a3ed2bc864e566860bc58d4fa62a7277c539f733d59736e223a9cfa1d29b0c868abc9a647efb473504ee9510936e6446f9311f5c609cfae7bad89480524534bb8e8ba169fddd769fb2132fdbee1332f283938f5fce6f12df8d1367d8601f5ab93713b2dcd77afd5e8d67033d1c3d4af07525c7f894c21abc1ec9f7663c4c290a2b1f38fdff21013f19bda29f07197f2462869f0718c197694e434f938f6aac53baada38bf404b013e1d31ea8449748e923a0558ee9a8c39c67557f4228b876488340f88752f39ac0cb8395b296b1e35c62375f69126be03fb011413e90403deafd71adc691c17402573a2009377f594a6ab976c65f7f3a6d45c53b68603dacf539bccc433c6e6511e5a78823d718466034125ebd1894b8c29f94886c38c930f81588cc9e39c31058088611baa736625407a4ada6e0bef66221b3ddfd429f135000a2f5287970d7da86a7c5eb91f5a8a38bcf20a460f98645985cd85832342f7551ce6487a9b80b558a489f08874b608f95a9c26afbbf8228b45417f7ae962c6234fe9f290a473d58c67cccd1fc23a617a842aa853a1be670d29f4707fb55a9514276f073587e0a880ee6296a53137aeb157a03ba2c6954809a9c5c2a9a4a34f2899131f53190cc951da588db1e117f5156369feb543363e1fcb3dbf2d53f00a6f726489243d878436c575b150051b4c60c9eebde8b4384c4fa92bcb858b6206a862a28e725523984cb424cb450f46eaeee2c4b27205b67f0c060d4d37ef56d5dd7f639a43b5b21649bee95ecc3235403665ae3ff1a3a25bd002eddac98b0b17eb9e0799c227c5632043ecce47b84b4dfa9713f894643f59fb2301ed5a39ad5ead16ee72e8dc70973dc0e6f40c90ece6573de920d7ad36e6420662b017519104d6055708bb5bf2bf301531a14021b690e328e9c03dd754d4e24b6745cb5584ac769ccd35c40b312a3767c03d0fbbca7ffd7bbd72672dd49c71913f63379067dfb90e0f8763c39ddefb83ab2cf63a578ab88e4bfe51f588b7428f4b44101ea183749de8151d5bf45041a44b953f78a4843c51c9ba6546d1a55856b91fdab53f74b19289e14f66280b514c42251120f21892b51a0c873cea82bb00de9dcbf14891a3a13a0f796c44ca133fee5a162c411629d5274f07c9220b79d0d725fe9dedce15913ab243a98b2eade234121833e798d1fad2ecefab5c96fa82de940a8a609c1cfad6d47a5829145cfbd403a76bd5caa1034e0d3f9ef118abdf6d4668bf52a8f665d0ad98a840348377bfb1ad305d906a63df6f8c840f43ee9970f0a254f433d2c860b7995bc2427fe34c649916ed580f2ccc2f64b4ed0a2930ef5288a6552937c0c781dafa4b95f96779dedb904940cdc94309fb08149cd46b5c64c9e80877cb068972ce84600b6294afa88c2b71d045e8a96d9636354b200c7e3b972c69219679346f97e4fab8269aa3de27d319a6f60fb367e64101d21ecfaaf398707efde4e1232d937fd0641f2607bf9f67d9c387c1629eedfbb9363566be39955a2b84f8b3acc2da600c30d06cab5150df0a19e127ba351dde879a8d7d4b0a71aff695d206ad92130fe9fcc0e93e326e52b96e0990e49de25a135d449c81e240712a443af544edc43583147d20774cc784be2859868022ffdae3e4dee15f4f9a2985b292a6f2a4394fb6e151ba995dcd2365f12b2342b4ba09675bc4dbe6baf7b27a22095ad20924fd6d479b26d7579865ac8ab5b6256e4b8bda8f15bf361ced4064035a96915a1f10ff653a7d2545ef36a10a8dedcb6351f2d32c28897bc04fab6a990f31e0436be04b917d949695a322a33671adb3113ee0872fa15cf9584a6293f622ed413c71652738e5a8af0d49747600c81ad61f4ea93c88c704d1269609e5b4d2f61b8d938513e74bb8df4c3e9a3ee3b7e7008054fb374b08542d8ee194a3174b23c4ec4d28d68a77da7621bafaec875b4ab52716d3d885d8f1a3a5aab6e00728aa23a046cc6d22e8015f85c1f7f0bb490cc214c170800d45b0748c62347691b7b3c56c1dae377ff6931668f3a432c62b43db2234556c721a658b41049abcb3b50f324f766b1905f8ea5e1c90c945ca71514ef3caa7f4929e4ba5d93ed9dcf0f29b3d2a9f5354fa953b4eacc05940353bc88a837b06a9fb5c055848ec86f1d4a5992bf93564e85ec568c8d06d9db3670709a5bace3bf3972a5c026e265e559a8b79858b26c3fb5ab85ff73d87bad6915e12471cd65e9ccfd311f7f731decd5a80732cd48fbd358f5ebceb79f042978ea54c94ab755fa94c89ef902974adb95155b11718527cf82d301fc7a8e267bbfe21504385ab1f5d452d39454d7e75761a17898c67020cc0fc46343d9c36daaae61b110aa081450150b407a66fda274c5c14ae0baa2ebb10e959d97ea2424bc4a49dba5f0385f65c34bc0b81d77dcbf877c0b585867d1758f674ce4ec6608c28ba904e9c2f717aedef3849115d9c3ae206760ca5315af688fffc6c6ab112131c6acf04a6a383bc3d9bc17dee50a6c781167e07393424e832b40203c54950cac608eed24f365e10855e36084f62e6bdbca032b6be67e00a017296eb360ea363eedcc6cd4e27986c29836e719c31bbb43dd91890e5f796a599bab6cd4633952168e7ab1d682e36f50256696e5c83817b76ddab433ab1aa597fe7a7f06180d3cdcea85e76b303b179b7b5f03fe5df611438096d2025ecc324b8cdd824166bcfabdab685b837aa3f01c4eb4a39ca87ab43f4fced39e658b9b9f453bc7fd4aa2f0d0f9b6fce634cb82592da2311ef11d54bcd44e563cb3a836e53e941f977d9ec9c655cfbeb637651671bb99914196e11502ac6d3f5defe914d522fae4fbbf9468ba2e99e1219d632c2ee4db8bf20ec4fae2b07a4a214214a174894ea24c6c5c8f80675367770e6d1b16437328b5b5b030674403ec3d0bedb02886e7ce084732cf34f14be91c7ea6d329cf7e4b02efb3ead68aaffda4659444581a00e7be1dd4aa5dd98e78e0e1b4bdc680049e90d48e7cfe66bd03db09f097d724de936364086614d77ae580a5c86725bdeef4ee0b0639d1056ad7a01e0a1198868edbf61b55483aa7dda2b2f77ef4f2ec43590db0f09f63631e0eee509cb8176516f90819b276a44dd54c3bb28f1871d85c2a2709379d4a8748fba4e0ecdf0d3f91f607ad166221420e25cbc8023bcf6c2bcfb54fc40859c5315205a0bd3ba15bb30d8dfcc8c03828296f5521ec55978ce17a3b4db8fe94d808c34a743205c70356d35b391c23b008e037826b569bdff08bb5f4dbc32ec4eaafb59c9237b3fb5fe67ed30882f6980fe158566d665dc4e33e06933757301b5566d9ae0dd025981a283191fed6a2ff0157f8b44d2f6f4af4a1b268ad388ee327bd7270b60a3e73cb54a2bc4d652f271c2f8d8facc3aee4096f0662ca01346fd14d9ef137220690501763ee5fd40fda7254f1cce0ff7297329c5115b46fc68e434fdfbf0d8d4a250000698f85b9cd9485ab9506767448a2d9252dab27a6bde51d986141eb2400610295124e5ca59559757e908e711f14ec5ab806198123944b8445b30e7150b7b0085d6ab6eb88e6888165db7c54b57cfd75e42a26b99387ce61da530303e7b1678b422692c656dc3f5e2f0858ccf7aa981f9c08442556a97939a1fafd6684be578c86bb1da014e377b80e873c4d6439cb7d5cf5be0d84cd46b976ffb21631ab9c73a44c89bb9499d9d1b3465e5129a8e3ed680bfc33e0ac6ce348fea495277e425271f9b92221e875e219b7ec246015de64c04a65150e1ef01df49512dc70c4326a0a43c468b2d397ddcc29440752f5f9f8949db513bdbbc5a11cfa36af383876ef3a16ea16412fa14d0265c33738b3b30540c203667e6818893ef373b058cd8a5aeccf5b48f5cd6b581da9e32228fe6083913e90f39a3fd2a8b9030d75d0ea1121b5f332cb21c196b759a13fbfb83bf95d406e89b7a4050eda8a6200edc365ffb84d423e416b4541517a144074b9391c068ac8acdb41a7a0840e6e967858010985d5991ee89c29a8912b2ad5e3427fcfe08e27cee41fe52d88f625f178ae84b91fa3c875436527e007b6634fd50603992b37a0a53b58dbbc2c17457c38f30c642046804e8a20d013845944fd4e6d2d14efd0f844bc83fbfc4fea2de3ebdaebd719e3870908a1e4dc11f41d37337c4d936b73582f5ac8e620bc4012801a34884cc8980e264201339c3466c6d769d90f1abdc38298e810ead5269411a42467a15cee81ff90858f638c6a6c681fe95a0dbd000a5de2508c322fb9a7aaebf5a6826d031c67f6944bd2382acd17aaa7b72cb18a1d2eaccd88e79609298895c4b91ac1522fe8e7f0cc86e4619c9efc0e046dd758f0a82a4acc3ae11fdb8a70b24347bc0268b556281476c84575d8f141678f4b4df2318d0acc0a53f8f98ad60527bdeecd097ce8df8677e61c584414e287d925804130b0d5a81b285d1421eb4784bc00257634d6db545e72f12d78c8d01544a5c399b003b2c0dc7037247a53d03f7fd6587d650233b6e74df9d1be68fce431659d914711e419e82b1fed9c86fd3973878d54ed5312dd4405c385efad0146585aa89bfff611f597580383af4f521a9e268fa99a49cdaa18985fbdfd2e38eaa812f8f45828d340094e518cc210d44a7b5b5904286c5ad9a5087db41b84cec37936040f35ba893e9183043fc284cc3a6bdb26f5629c887cad708c994b5c13affa02451d75c9b5c21beeab3b058b257651c05d9716face1283320712fb055c6f8f4dd64fc560e9c5259d82210f904e3324f9fb44f6a5238cccc9d08e01a0fd88bdb498e0933fb02e47ec119017901b9d91b9fd097637ff3a4f1ca2c02d36accd72534b32308f4009039b79ff592629347ca4188179b148ec704bc4ac140ad5170c108547ff861bc0a0640990c91620440832b81ca189648ae8880db02c43caf0c1fde9fee80c5c002a8d2a6d54f646ff63278caea3a66a35358009b3bf8e905a9630c661d6570c47806c4bc347178e8b2d000da8cd372f0dfbf25898e7e8d2bd033a407592779649707f82d61cd269c5b42107e30c58982603b10e3464b3e23087031408119693fbce9118eaa0657609718a8cf8a052203f59ea3c3c10c8a0c5ac87faac6bab9bd8d52ec0d36ce6a2a0c85185343e8220e3cf166ca89e2be79d967b46deb21bee8bf21e4c31f662ee480767d0687d267cb7d99d246438641218028ea9a010d312712aecf571603a231c53a71082d5abfc3e10c56cc175fed776cce8dfdef4d3bf0d9d59c48039e4fe7dcdf78a1bbc3dcc5e789c4f4902196323437084d93c4970fc598040ba251a447216ed3e81fac1cbcd9ee0efa147f762bb6f436af8335bae1267bb12aa1d20c2e84ea701f9b671851b98d6b55012b8361719c3f1c32c42e0a5ef486baf50d4ff788256009768b4f70a5cc18a5162568c575ced919ce71285ff99b3102c38855063c4c1dafc70be8888c2952a5e658aebb2608c7c232da4e876fb07f3a1d467687457a89ad845a54c5165f2ca2b12c27ff47280e4edc4c4843df368249840e4145ae6ea88467dd0dbd909b3c0293397138865376bebec4b48a0471a1cfb18e1310195c4d659f8f249c05063fbb08108e4ea157bffd7251763cf3fd597ee19b28a00ae8a7626a673a8d83b519366f05d32d1afa0de0b5c222cb5633c35943219aa55ec21593f29548cd4c41cabdf33fe42b6f758a22c43f7364bc1d01a531d91aafa65ed26021d47ceb47ab4fc5f32aef5aee00b9d12522af3696042d9692cbceb5c7fbb623fc075abf4132185534b482ac5d2020741f86b3ac3b1bbaaaceb887a0a67e06d65d6517afa3ac9c4fb9a0c81e01624b5d7cd559474d41e137fd66c28c9feb893c59297421723052e1c9b74c220a5ba95fb4a2f493851caf35e057cfc3bf5e71e7548f84cc143caa52c1e99596baabd0ff518d2c9f3778373761b8c07e0da23b48418795dbf5bab05c40599dad48e663cff009a3e5e259e17bbad6bc05b94311893f794b9acc1e89dc99638479db97a3afe3c108de8ad75018062bd58fa432a8e4be998256e8406097962a49d977cd03760fe57e34cf7aa0ea4da8a54f5f87205f6bdf9cce259f305544c15b5ec6dbca079e7d32c3458e275c62030959680c26524548801fee18cca43090dfb212cc08c95752807b37874134ad8a6cf17ba865f5d7cc25ef3308c2b6410eb76c3d54cdfc2090218742aba2ba19347b340cfa6b114efda5aa5801ba8e69ee074ee93a1cb3fa79a3640d81091814fa8611a523b7a054ca68ca39acb28732ec20d3095d7ae22e14202661bc969cd86cacdf550481b07103c6e7461840fa11a9dc2c6ddc06c3d0c4930cba4f24b0494a6f5bd9e47abe09e24acce59c23d717d19aa51517bc40619433bee82e8d362ea50b4289067a3bab37f272e1fbbdc7c062a46c11b26b0257b14d9801ed560413d38683cfb0bbf3a7813e6b1ed8dd7796db16ec77eb457f8324a41cace7f84a5f00e07612990dfe698c809cee2bdccc685e3182937ac020ce8230897459da0520dd2e167a0fd31de55f3f69f95ad22d5691f211ccd061bae4370d51845591d4357482cef2d30f1872996beb80c984f48b8b61872828827255efe0762d9da931d700ceee38cc090144ff4b93fbe42e8542ef28ea9ccd7ecfa28f5656223dd843c8d5fb7c5fdff386e81bff51822b812bd1a5ac2e3d5b07b7e676bea6bbdd705967773862b71a7b5e1704d2b4d1ee967d6c07f5b69501409400016f98909666bcac8df7d5d9d199c651d2411a681f3d94d2ad94f316d5c1c00bcecf3a4bac3d938d15e7d33d3536cafef595e1b2868e14fd4372f41752551b7a24b319a912f41b693ae692fdfc98ea5d12ef3d679bdde5f6fd9e846c720014e9d5d3285f11f440eee6a418f123f4e269c945fa3eca6d8ea1105fd2747d8c0d01f34f74d06f9bae7f3bd0eebe3746d928faa70865e0c68b23ae830b4838280a3940c20bbf0efb1391a4ba2cc53426d04a0f3502349a2e1fcc899d2cbc0ba707375449f4e824c2cb1954d843e1bd1969ba23e410777d2775a3a9d8ac951632ed6f1b531a56a0510fb9e243ffbc3d2f625880e8d51c84eef167c054feb2a31253b00473ec3d1afb32c3cbdd8d8e69267c84c6643040413e35ecfe9eaa97f0c1297f5ce91592aeb242f78e93717bcc0c0ce83f3bf4df822a79b69976b8c7f65728d8a41b51a2acd79b02508185f577842c42d24aafb994cd1ece8bd0130bd6cbe03d55b4ada3b89ebcae9ca4012e705a0466d1f392b87b9b9f51aa502c7ec411a6cba71d5b0df24438117cb6c0dc22a2e28670787a0d099e6a3b5917edc2f34227482693791e22f549f7128ed22e10285f1be46703e92877abd0c846abe95617b0165340321c046307592f6fa9907a9ac280dfd05dc232f6dd0e59ffebd7d22f1b959e0652116644cb11ceb258e0e962a2cbd16f551a9926adae5fea650176126292c8c3e1a28b3c032d2d629f84f0e9cc9b7174216dbd3dd6525fe510fcee8b3c12dc463d716db9572285f6b5d0244bc1c08aad5a18167c881068dfadd495f3bdb04d58d6084df2bcf7409eff573adf4bf07bd74182e2d36b4cad69a4f769a5a9da231ac4778a7c6b1b0bc401d34ff165b641e07ab33f3b9f54e27061dbd9101ccba8df5870360863430553c4fba7e897460958c6ec936de50c5b5faad1520a02c7cb48b32ac16e06edb0fb584611985db2f820b039e5167706198ae835192a8d7104a81f6ce9b504b7b8d907ad33029bb5f144614aa98db14b73b7dda41ffc82650ae797962ee8b7d460ba3194aa76c2192be8297314f713b72277c1d8bdb1fda159f409bdab8ad59212415c39bd9fbc0fcff9079fb357dbb03cc97b97498daf45b7176a8da5365f2dee14a749dc1647fc818f9a7022e257610ca17e02c477977b85e41f53ad1fc10fb6a9e9bdf4d302254aa2831d9b47481be54cd83340365b795ce482de59fe818fa2606c4edf8a0aa0b0886a284f615f341b7b4bba4d55936beccccad9b7c6941686f6c5e7662aa8e9c75b436ef9ce3d4d10cf96af03290015583f7bd67c36ff5c4bd92d38dfc9ca2fc79b1b5c75660841f562032b14f6a0263467118f5a03406d674ee4181b67e560835fb43ee8845f34a0852bc32fb1a38d19ad1db71bf6024e35d32a714b59f00bfc731d6f7b06204d8ae0f226986d4f57f3b773cf4c4591ecb97beb09a0349a509f53ee2945108401b5048d310ae64c903627cc18822ee414f7dd7f166110b5b39e0a2fbc89b1728e26593db47231f8a79bbc95eb3c59f6e60c8b2093d14925a38612d7dc3851070525c315a89bf84b347781ba154cc2d522420c9d5727498df0fcf81bbfaa3ed38f254e45ed3aca631b0e88acaf2b287c56db43fdb0f51e926204caa772c9983f770dd620f6415aaa37b3d1ce5d1d313115bc875c5edecd63391b9ac98ade7ae16479e7ae117a387897411be5a119efc50a19f0d94d50bb09e29d3d0ac7433cfbb7c060a92f440f777203586a2162c0f70eb3a59634173dc1d81a9c102f4b1beda21e543977df7384cd4296d6f47e419f2615712f34e9fbeb943f61171401368c71b7293afd5aa80523e8a387652086e3f1b5cc4c02fa08e67ed8701423ebc4dbd370cd1a4bcfbcdd56b98c63bca39e1525039c318f79a4844d73edb1f75f5193690f26a9836a9446a9074652f56043d93ad6b58eb3e30488eb645c68e145059f5e506e4ba91652c8efe310beaddf905def263aebaff73cace39058c5801bad598b58478ecfabd8884d8128115ca2d3408787cf4f450596029940e4b380f6f926e10ad61045fbaf648c4e62a1165653c50e28a2dfd62cfe37f6226cdc088140eec5a1006b31e29e10be9d5093b116fa92e6ef0b16b49a7e2e6b98632a16126e7adcbdf9ac92023b6e226cea45a5e30a3f5dcc92072fd43a1c63976632c321e710dbbcc4a05cef15e40f34e353d7f21ac9ecddd65b0c32da25dfb83c5a573c157e7788a065bf5df82949319b2ba7f07beb5f801c74c8ca0f5aaf7a58dbda5b09b4e24ec2bab8b5ab2acb3256e055f5e688e07e3c926bb0fabe586c9de51a64e5ea1cbd046bedf95e331a8c81a6a937732c6d370642c00e409db0b6b4f490e9538b86b544d545c163489ce527b3e46cd5d3e9d97f922f59846a7b347a4d9ec2f84989cbb48e574d17bb6a6d3f6a5aa6ff715c1ba770e45e86ed38f8db7248e39334c08ef04db67edae9106a38a08287296842141b786a6d45a9afeaf54d46e5bc072af7c22857976675abe292041e54463c038b0d688dc0d6942cb3120db6f0c43eb47847231a6a38c96e07710475341b24a66a3283797c7a572d56b05174e23ee19ead53d68ead2357cd4604e27cf9a56d917a6e881ec7555b533bd3c16cb761c2e3f13c13235609149f6cc23543dbb8dd297ecce481f5e47afd23d2e469c82b57f5f47be913c2c75b487831243c1df6652e062bf5dd1383ac4906cf9a5c8e20d229b9da750a10c87972fa0e2e58cbbdfa4af1fa075b80e9b73581b446dd71d16c6813343e6b8c1af79012b755334083d7a3710c682ec4503f346da158d301cee757d96aa6251c0435a97defef7cf39028390314fc0a3867375853f57cca7d5d105332911d28d0e835b34d167362c8c61e8cec6adda3eee89570670259cf7cfcc3668590c4cf1b2d9efd3b643e97e00aa0ed24303d0624e43b189e88802f1e25fc74cdf327e7101ea365cca17c8f2eb63475bada05686e0728f2905ebad04da11d04ca0ea3ab9e09846b8e5d764a73bf0c950aced89b3a4b75f7170582d6ea4ca8dfe1e3f81d19536fe0a0dff2927558572e61478c40b018b5d2b4732eb99ad484a530ab92ca0bca03dd2abae1c36c8a65023e64a0e0cb1897d19513b09e2ae95872cc8c864550c848c3dab5df8686c0e7026be4b481571f402d569bdc4cbba21208a0d8897552f9e67e0062964560e5f58c71244e75dad07dd14d2db7cc1837317af745908a1e22a551c0b3e07150a491f1ce157dcc205c24d8b1ec88cf1596ec233c8089324e5798d8230019e85fecd9653bbe4832dfcc5b475e0a1d319b23416901ec77c519b9d47cb30efc75800604814e778843c75ec8ca2a87e5c27f57c3499cbbe5c69723ba5938b0a7c099e5a0b419a4d9fdd82f3c060eb30e153ccd945003c77679345d718ed30b6bb90a9a1786090948047fe5a80e5a186a24328c4b034cc8d31c60483a16daeb3ad0dc7f5f651be9c41543e4de9c5dff28fa01714156101fb5a830e41e676e15c5222d90f41c34c3528ce9b04d22250e8ca3d028d4c2a5a2e6c442ef9dcb68a0a214a3d815036d56c8425d7d0bca6f9fa2c4861ebcbbe672ab4d6e0f9f9579adfbe4040b5337728050caf810c9a0d5c7ee7de56471af3a8f465bb2c5985fff6e9750b393c4153fba1768776b04ebc5ea356645723798ade5428d483ac6125e3abf0b13a58337379f6fa4b08287e4539b5ecaef12900294fce193c02d0471ed247c4abf390987fd1aaf27c273880d8de43e90fc8a85606e84b5e72f84205d546f1aae6d372f494d9940d8cf23fbf06e5f4941b0c4470d62bfd448d918530e53aae7b390a9a920fb894b0317fa74c83ed6307f682e52673875b1840c0982318cbd1da2135008593aca61ceb068cac339eb54225299e05a48487bf32be7d051483b82a4c3ef2e419ec123a92906c878eaecb73712ba45a85c4904478171ef790f3de2412a39df6797b1e51ad4865acc8530613bc654f361d24c20e5d1a262778c66b965a40eb60747c2bbea246a220accd8f88404d7474a5695d25313c92c5a1ca0b01d92e49a4086221383a53ef2a0436bf338ac959fd3dde95f3f503eda305d86452945596c256e09306c33133baad4efa9cf23e9e59ec2512c2a11c594fc54260a313cd2d51a5329baea2dbea5626371ad475009469ab64b6965566a603da696e259a033b858f93ee34fc8edb416da1d1b83d996b29db623c43fb3c1c50aa50adab1ded8cb0ccdfe44cc12006604da8328bac2c5761a75f6c8f6c20321110fd5c11d16eabbf1d9f6b64ab9281889f8647ba70a76e8ee49ef56c048645f3c403c9c7b08201996efc4f8ac9527f99f359bbf48765e0befa668b8a101a149f8a1df0d5e9a2215226c8cca44c6ce4f4acdd27aa545f1a0a5804bacc483faca833c5f94c9e4edf4dda44410d88e3b5dbd54906cd104422aa9be870a02613e09b97a052cbb3e2f8496982adccdd7c349eba366fb0d7202f5ae1346e5681860fabdc5b5acf5513a4d35b22c7e1ee217f0900b56093b1a93288ab083067f9553bf2abd0e06653546dfddb6297fbaef1ae81769bd485d3b17fe0348a4afa5f4efbb32ae031cc0fbc38fc52dab288b604e08a7254df2e9650b95c9e76a32eade377d254eb034f1ce6be147d4fe07807dbe42fe8dcf2c4a8df1b665ede89f88f1158d2ff5a0cf69f11f8c0f1ef4ab4eff30b39fc064dfaafab8d54f065dcd561dd6dc0d48950c171b52c318c422854c43327058bac23369a6f382bdb1fda2509f34812cf32b9eaa7b60a7db50341edc699f67bdb226f5504ea4951dba4a23f128a7b642c901b56cb61e067f529458f33c9bbaf692358087243f98e9ec37e4281f651ca5d5188eecf877bb99e4a30413cfb149a5a9bbe92380ffe1036346a5ec22b7401c7e11cdc8d06500588aa06fe423428449d6021032b969a640f236b20768ce5c36a8e5a99dc7ae121fb702112f93054269786be7a24cc3a4c83b1fad40119313e3569c1e128ffc5a6b9dccbf8a818849d2caa8ebea233175f5b22ddebe0d09226454c1cc15e153716ffee221016c2b94ddce91e24502e47f48c0b00da8f8258641283dd742e06b79e27e4d19c4b0ca2850ab6088e1531d7b9bff086c7baf0a120462a794ed88f280694d6644d74524b841059ce667922672602aef63c41e735c7ec0b4030a0160089ccee93cd59d8a8a6c505fded193b9971e0836318da6e4e99da1ee3d56689601d4ac10c32470e803105cd5b0ab150537d16c3adabc4f0641b70b9e3451f02a3584b2304f6ea4d4659bd100f8b26e17606530516d576aeba9a8e0a2a24d6f8a4cdf80e4fb2df58a688b8934a6a25f01363d4f35ba4e758f36a9a81d33aaca48207eea106668dfd960fb8023685795a37c2d42005800a4eda599db79737f0a4ecdb8da8150a7c74e83fda25c1a56bad4aee515900b8fc1a058bf60faa38460148d1c46ae6f513b56933820c1381b0cf1f4a632315aa5eff8786b34a9f6eb237ad162e76379f1a9476c925edbf0c7e264c1ab5909c8d2e8f9c7222f3263bd2af19e244dfa80f7bfba275e8977dc782af638a7047195189e6d335498d28d7ceaed84924b40a1787954ec90afa97819355ccdcbd01363a716a0a3e547a760da63f1e446a6f09993311188a29024f5e740941a6c5175dddc6422f13c3901d60a3d250b16461925e822be80e93a765bb711d33c774fdf1367cb3af6cc1a2d32d3212910c634aa8d640213e0b64114fd8c56d07f97985834e3312fe50d6b134348c8124fcdc712ae8c5694a58d3fc542e43d2cd2ba7da3f8ef19c59b5e7cae2ab274f178a861c2a68117fbde1ca71b3e28301e54995a7761b935b3d3afd56eb35e5902bea62e68bba11accc13e43b3b25eda5d700650648326618c4faa089334d7f9002a873cff85114c6c3b9a9950b1ccf49daa5b9617c177545c18e2c8b8941952c566611d1b02b4c54805393c1e10c6b10104b62a4255433dfab8f06ba5ae0a6515baa2cac466743026178a2a8ccb3cc8208ec9a896eff4d2aa1560d9203a9cbc82a6bd60322ddfb72699f5b0263519945f8ac8e36ae4b3f5dfc81697c8a0bece7ba975012e188daa371312af32e6b3f700d44ad5436abfa601463b548c764c876a1c9887e920e1a89bede3026e8b2d9f002dae5f54db419c083e7aa5b3efe5a80562fca0e27fd0e15a9139f986ab2e9b61ed36e82a6e791ed3ef440b64d71273ec7acf2dcf1bd6d6e3570a1a27e49b5520033536b0e723546eb503ef2f1c682834f5af9150c121f5db72a1321990b9426695f93bacae993b0713c2717e17860c7515085cbafba1d6ccf9b3e1aa6de8518a1f94e637727dd48b6e17f409a543d29dbfeb74c161cd5d26138ffb71cc163ff59da3bd6eaa80ca062d7a95428fb3cbd1c3425824ae4de219738f48613835018bd5d24f9fe41db9a40535c819b0870bc63bf0fe7f80d96f8527192e59e3e42389f03e97db639458ee882382361a414e1267e3889ba99126ef49aed496a3a3ce60a64aa10a60c179c9f7e4a3f121105a12b4ef2123d9fb77c758da1018da9ffa06b37f54394020f4234945cad3beb3a664c0f3c966385e8b7614a65a760d065e93cec692a81e262c8d23f8e544541ede09ad438d5211d396b116d876cc4ac2ce9029a516441744fdf7dcdedb6dc52fedf6520a2661b4a8662b4ce6ab4056e578445fa713ad0db80e66f222584a02d27e0389a3211034b4b52ae0b4d2f91a9dcd7b5817b45d99e2fe3807ee3119aae896dd02c9e4489d81957fcab3f6e2d02ed903102cf64e3645541c90eaa871bb3abbb2068769aa95c7f95cccfa1905094940ead729c9bf9582a20330030d9029cf0bf523cd71daa2050f616cab4d590ecc48f10b745cc0322dacaa6970bea78306a7a12aa1ba7647a661ead8a30dc2bf5182b3336858738934497a78083541dafc67d1cee72ca24eddec4888aeab7cd7231d6f73de9b993cf9757d15496dcc156f78c6fd21903071712de61f63a5ec981e2c1951711222eb7f8bf2ad080794eb311b96f1d3e98c1335c80ca176d2941fb93c18a829ff0678c339d571af874b33f03520c534fffd46a94fbb5bddb12654f9b64c7621eab3224d39986a42d5b33da1d05491c6126a510303243b78a237952ed8cb45943069d26afcd032be856ab46f320294e45a7a2cc5ef9cc0f0b245e54c862311b65175ef96f151a39f8570b792ba13e92c941a7ef2d7d8f4d07d6265be0dccdb3bee7c245b9d962793b23999f6056fbb985a72a99ceee78ca2d6488b5eb7ca061261e814ca253c10690a6bf3f8f5ef34548ee38de748f8fda9e124bf2ce760e24c6a5d2bbf411a1bda3a67c5b45b573741cfe6b417b72fdc55ea2795dac4403fd6bc1408cabf0df9d4083125330c0916fdaf95d5d5bed9370f13dc9a9073f9aa9716aceeb40a5ae64c7db032b4cc92438bf911d5bcb81e97c03a2a16829802de9975cfcf3255a8ab138a0fa513f6bfbb263fcf32a29b111b179e0d3776d04331aaf925608cde44bee2b41880d544fe1c84d769e1e94427f298b50d52516e549ebab5ccbe2aff6097ea2e18681f625f57364bae5446289952dc964e76d173987a83b8846d4f718cea386215b615144cea451d03ada622bdcf5d70cef89db4020044dd6ede6363731557b3bd3ae4b329e545545a9adedc34dda42c38973f4b9b9e3bcf3125589345e469b97cb981144a6c2abf0306917d6a537e22df410de49e6837f996f1cbb122f172114b39ead3779bb1e2d2ae97ae806f5392e01872e5829602038ef53e27ba1e38a097e148871a78568f6d3bc813e4bb32fa7f1dc5d1f53de0d94d3c66c290c314449fb9e185aee324dcf8031d9470798c4bf7b1fe1f8ff785adeb3644cdd0ff5a67445c481b2f31e17609f696d6a2cdd64c0df182792817a2a42d5cb080a28452419b4084a3496c79b7540dc208c082c50bac70c7bd38ebf4a2aca2341e682f17fe3f65b3ef12f7fc23195a8cfa546fee77b7c634c1cc5ae525980950a39d564959e79874a5bc5d675459fe2de8cb92506605726cb49eaf809dd149a6da46160dfedfab83f328a6ce878ebaab980c4f7a69c6563045b61c8ad4d6c001e10c1945e912cf26861708b51d902d7026e0f543317a5ff9def006b272a32820ce0ba4849d280a21f76ff67103a02a45aab4fb82909c0789d459e41ea4245163ab5d9646c1c033c29a3442d85aa5cb6e7f4baaec8a0b09eba7e2686e535e060ce76336e43ccfc38e8e572b4ab4106c14eeae7d089d4691e5c76ffc3b0d406a6fb175c0d12c5ef9ff4ae677bcffc6fe1d3114a9ad523760db203158d5d0f96d94478c6282117035f3307a75e680b434478631d5a16c7ffa94d7d4e691b3d536c51d114413d691c506eb24f5adecf79d31e85451115f598c3eca8e4982a2a4cb99381499a4575872eb8f7a3955ffa0bca07e69849fe78f1bc4703997cc77b1fb185fc55b3f34490bf0e7bd8946861d6525cb3e60ee285f6c31c8b901d78f9af3adf256396234ec3fa0ca9b0a61d2fdcc90da7de72fd18ec7897557cbe97b4b4e8584b9bf81f3ed12958ce26aacca62121a4f4bd3512d52cb3bd6ea0fe44a23802af18c4ae87f69277d7cf5cc94a8b3b7bfb979f501d72d8913caea7787b9975b227f1c7cc98f71377020b2cf3b78fb21c5d9962aed360f040da1cb5781cfa8af6e44597cb0b1e8eac429808def6113829a823d7cacba16d1f5985ffc03b7b490e7e8c69a30b9d0ea0045f1160713f38a28c6a8b926378c17ed654c7539f488d1cfdaf9ffe9ef6ba4b5721a7ed5cd888271c28e03f3235e67deca9023f6e5b7ecaab8d29dcefab20e637bc21f304411b769950ff4a7f5c4c0f060f3f1c08e8183a5d5c9729949509f503d73a18ba012194da4799ea3a0aa88026ece0334215a26792da859de2ff695206daa449317810828fb9a6253dbda08d9f5791b1f1474239fac0387f7cdbe85e28c95b8fe4655f4e35d1960f09f8536549855d10f33a7c79b4e3ee7372073393cf5cee4d1148a7ecbe448538fb494586ba5c7f94fc23903d8b5b2aa60dcac5647f4669be5252bb33115b9fac5377fdeac8ddc9c298b38cfa0b55f5f6a3cdd5d606446bde4272e5e5bf184e851eb9b52346ef678f8d761597245615a96f5fc7478f82e96a144e0ebf1c2ac10e2e6994f07b14c259a234b634555a8bd514de26db8c8e14a35865a89c830518e3cf76700f9752037758402bba5a83552fe368fef0afcb148c45ac4beabb60c40d50e6730daabb6e8702c27c8c0be7124e325174086ae73fc16b20d38dee419c6dcf5395c771733bee79bef093748a2613998ffbf05162ba739f4dc76149ee9c959d839214d48dffe5d76627c982d42c87caf0aac2305f36278f6f0e11614585446b5ecef24c300bf0dfce4ca244e3875b9b0bbd6a8f9338da6d644951fa9639a1145201e78ef7c47ef342ad49962e3c0b04a1ad408b26109f6465bd54556a23b60a340d66ecb04aa517fb2045a3a4bae231bbb9033eab9421548fb98663c39eb776128c441a33995d815d886342bbf2cb96db5f86f5bbd9bdf7591f7664bb884ed46da350aa5a62b40bc607f156f27fdc2af15ffd33b296f9308e708a69e381ee7aca5ec823886762f2838218205e3e3d405c60ebe8a200aae9c3a596698be213827a5136996b2147a4d2ec2578267cda20527f384b6e3982a09854a361e4dc206cd57ea97e07384954476b2f427e03fab78a9cfd3f720d27c9c9f80c8d6ab6a6c9d362a1b3a264bbcfd5d3bd0ce94ab5c4335c50317d0e5050b743bf3c5e005b9443bce0c7f11640e6824f355a6b2b1333401d73875e077432d5a188474222d5a27e98aa1089a407fe753f53fb9a11c2fb5bd68e910d8a1d0eb95ee20566241055dbeb0c1664bbd688d19a96d2f55a458f1a2556f919842b423e65e00386c203315e7a557b8ad979c55d60ec3f15ab41a775bb420451670145cb40eac69f4dedf84e3f416bc689ddc7b53ecea7b58fef6f1fe61d9f87bbe1e8809baaa823bb528c379a8a31cdb664cdfc19029125440d568cd736ccc0ce650e1f9c1290b7d4a8187d0d8206fb19348ad70763cb75aec03c8dcd0c74e33f6ef817b20f667356d0145898607983070c84182bb3f90aff033c18ff1b509f88da496dfffa06bb9999d3c00a2aad25b6ea8ec9d89b08151bd725065a99ab4647d14e16ca19a3d4d61cbd476ab895d485bd01025046a3cca72861d39f3db1bf26c4b691425b07dc0f48403cdaf39dd906421859ee03da27da2978ceb1251fbbb9698ff158d4920cc38bd553d8be8ea058681da60a14690ab5f7490df1c579e07c9e9b025ed9e57e20fce7d1bcea97e30fd9ad8d349f7bf918f3060110cff2aa8421637f8f898f35b9246bdbe2e5845635d669fdd142ec9ad1b78e2b432c07b6e99c510ffdf004f71c66750dc1949fa99a2f6ce0cfae3b61b7b95ea1ced46a92c9b4f226759f46a2c4c5a9ea96f4051e077fc422ef18fb45a115117588a15390376f6307e8799aa152c23a02f08b76363a804fdc7a5da83ca0692be3d1993e5039e33c7901551d112df5afea1cf748bbbae9069bf79ef5c21b0d0e186442cf76aebb52a59d5cfa3d677a1416b0026cd8095063425b156ec4d564bda151aca9626b393db11570b3a4dd9966d7189f0c767b09895e6a33ce49340c4ed8a43f2d077495360adad8b735908f463e0b3c8c82376411bf53f63f31ccd52be2c070d6b0ede24c11bb3456fe6a00b25385a5763ae21ecacd306b285229b1aa324b45279208d6c32b9c5216e842af3de3b75338f141162678e9ea4ecfc876b675080b37a3b094a2f39520a3406c558ecfcd59e996af0b35253cc6370e6589ecd4771278f236cde9c662271cb28c2a10e38c1560ee73cdc11f5afd0d9b85289b0420755746afb1c7e422a2dc1186a63e91b034c5f1886c1f88d18cf7cc389f709fa8d4c2b7c02711c63ae9391a52112baa21d653cc7406b65e7132009479ff1f20c0394733ad5eb33be3e5fa7817f60e68d1c129779842233e8cca7621a5419859b96a544010944984feb8382bcce3caeec60b3df6be811d328bcfe1fe816abff3075f05697611f655394dcfdc6bc58dfe724e9cec805e2dfe513be5ba11663a4427b523d80c1efcae28ae60748576a622c252eef19219a647b91d3855c05a60a1dcc9909c40ef2418178e10b2a3d5ecf98e4478eb6215db237e79f1c8808ce9a38d53cdc0321bae5d089521432306363556e274156da805114c16aac1eebc0509d5773384f804848c38099e32ca9d50ff528b49a21442e1b7c8f5badeb9d75b1dc8c12180c5452ce740d717423babd7f45680bf4fe0f7819f06280f7e74cd3deb0a2f3b33ba9d09001ab93b1866042a2fdc938c9e9508369f8a4113454807250a9e4401030b1122b4c6bc2ae6cae48c5e806c965c251cc13755c13a0546a76767580055432e6823f2178c64b3c01408d4f2ada312344f3913e87d95f37c376dc324de56122774297161c2da2ebee42bbfceba8c92aa1637bfa4e9b0edc1809b0eae9aa37c9376ed9754337001df7d0b27858f21782b9dc81846972665f44f7bb0c26262cb8772e50bc9bc193eec40ab788d4258ee3fb0491b074cb52ea5f89e9a3730a8ff5ba7f064f6337f3cf0103c54d1ab7cb6a6e8c3dba046354225ac63cd2a26d681a7c8c06ddbdc5c5aa8be2eb440cddb940644ad8eac219ce40f40ce6731984044c87c75454d478a518753b4a9a72514903c02b1bc6ed2eea6f9e40a003ca54730ea2ea9c9750bdc72d11894ef20698054539ca2400f9d67d7cb9648a1cf3c7a09ae8cb65089b6973a0f90de3300ee9ad2bbe74569c2b826bab3c664b6b5e698c1c50e768740a95c107bdcd528ea28c91856ff297695dd8f77299021adb8a269791a83777a699d2c2e5183100a6b2eb0fd5e4325a44650faab201aea4d68dbe834443f481bf37a27d97c5d0e2c609794558255f488ce8d973f5a0ea91c3b311cfc1c4baa902c30017b377ff6d325d66cd6252c0236925377f7ac5c10b97c49b76e0927863cfaa58ccec3661c28c9a96a5b5c09b402d4245fb8c873a82311983812af0f07c80a01953570fe8db2fd57ac086500aa2b7f13a8a21d7dc3ea72814471c1472743e61e7d7c7d9418e67301baf221af265051caea32db56eb8abd1bb66c68c3e087e037d3cec1d07b09d9fee66fa8c443c731adf86963048abd2d8f920ca17ac3802f4c2ad0507f0b00fa298cf7262ce4b68c9efa5562888a86c4b72079d7adbd82ad942f8278ce8079999772b12b79820c02dc6a665025d223a05e689c9fb287e5be78ec6c22847da2cc3af1274d9c07cfc2d3ca52c960a0af635bfe738db05989a2eaa13e508fc7e317f086bbb82e5a621b701d113ab26f1f1860e24c1d778ed32284b0391ff2afd9f36aeaa8eceade8b20aa58f1eb8aa4c8bc4d45c2bb576b209f420b16d54affe788f675281366cfb04ae1bd936cffcf2c5545cc77a481fa44a385610fa7ee931c1c9a2e53326ab68ae935ebc43c61de0babdbc1c211a1da1d3f608b9841adf41426e5c129b67f7a007ebcb9f86bf95f70712037c11fe99d2cacbd2a719b6dd096c9a14716118b78b013e37d0620f7c2f8695b7ed0c60ab33ccf4ca011831c32749ad5921361c34479d925e454c91d08e9494d0fa4d100d58efaddf3674f7a81bf74081eeda5d4bc3dc9742d7453e77729acc2b3a5f5b12670b54acd7cd78bf1023d8ebf7251404874a465eb8b0c600b174865052ceb6e715473e36c13c85c29678de0681d596085835403dff2fbc5a2b215f4e105ab289fbb5fa794bb1bed93718b01a746a668809e7577d596d5c5b2e887308c83bd60db01cb9679f8d3abea9ff04e4c15b19469c7f7bb38a48ee8c612ab7daf82b892d33438ba3fa9c41ea876563343b7da15e342987e344add2d07a20125d0c54cae96179262036c37762b4c22f7a48544c7020323c85950a9210a3f9d586031ae0375f39aafec6ef13d44c3ea67b1a42751f8a631359b64215544eddf722d34f43e81844921a59b924069f39b5b3826e97bc1aad33142ef63b4306b2f7b150306b16f103c55bce35aab662c1c18b6f66f344cead86644706deed609acd6f6c700a0099e646b73f4436df108ffa004dfb0cdd8a7431e385d4d01b71a4c5f8deaacaab11ae13f3fb1aed5d89cd47b12ad9cf2cfb6589ce13b73d90e449c4e9b5c1ed757244557ee4e752e9cf9b2b172093358259c38052246825a8a45ac076aa516f59c3fa7a6ba59f01d734b2a27c03edc3717068471994f1ba41cdc3f29761dbe4bdcbf6f16445d91dc7e06e2a1a40457b6cd38a6f366c95c2568f06bd56b1f0bc96f63c5672b97bd1bc9483b30ff2adb2c9e28d8ad258ce55743526cd9148abceacc863eac4bf5fa7754d084570f47c8e7c05c667b83afb0d3f11a577daf1bfd1da2aa1a080b71718f3e89f6a03128bffe84315b3ced16b63c597c70cb2fb1b978bfce5d58ee351100018f68d0d445aca040ec85c10daa4e5aac5ca8cc8028047f5b3339a78f9ff51eb6e61348216b334fa526f25c392c6a6dc566c73342a0187d69d4b17108b85537980aa9c85610875bc2767d6241a183b1ce3e0835c737fab001f18658b4649061f1aac7ebb2e6ee24124e5642a94430f1b30cc1afe3acbe949a93b0e5cf28c8123bf8bd5d839c0829d756079680464c00d3685627f30e7bc114017e06027b10b4900873690c2b5d315ac55307bd7a043d3feda9a358e7c925f9b424b4e65f310dcdfd27c5b71ef3c8558a2ce5e2f9365ceb8a1578d5018e83843494a0a2e7386e1f273a630caba8f319b9be9c8a99258d5ea87bc7e16dbdaf505526138bbdfd4534ecebef9a76782bebbacff88107060560fabff97c77b797c941880764393c29684259bd0b604a15a02801cfbece9378404ece1c4347d63447853bc62b30c364167c1876ddc7f5f9d067ca7930aa13bbe9d2bcf99646e46838e28e5a980cf19a41dfe6a4d51d369b335062a8e82fe0856b931c167d5ef876c3ee321c0fcd07c72e361aa67cbb25bac9318d491eabee4fd0590c6593a6892708bc6e4fc1314a4202820bc381f1802529f70df65805dd23a0ea8115986429127e94c02a367fbecb631d3191392e5177492a020c6eed33419dca7253cb789612e9564c42d6fded62021b9f0625e5240443118779c0be7438645633164a4c2b3467aa9402e5c8aa1317c83068b45134765255d8504b2c7c9e7940c2e9747d0d89ed835ee857bc9a6ab9a10788cc13a22625e94fe6778f9163da8ff85f5bb5462ac96854d57fd5d3847e8f197d2df15e9efe24ecfe11584d4d44c5d8a3b778dcbf425768fd1cb386a769fe88e2a63cc4ba5e7b003fd4a8a769f817dc8d3947a79654ea06578f4ebaa86b552e9783d99937b0b7a5dc5cd80c69d2e3941348736d562cdeb4a30403447dc01059f1a2e7b7c039c06944dfb36f5270ba73e93a550961e8df08572c4bd336f4b0007261273a384c98b3093f09eebd285b0520d0849e23c1896f4c5f2d5b2ceb7447eac36eecad28328d3e8014886954f9e50a60a95df72e29c490296829f6794b9d95949711a08f9395b275025e2bb3d603e9143644e61bf0a2911a930f29e15c9d7be9c071580a9575c4ebeaf9a2ed89f1a4dfd5cf0db56a57d92dfe735f164b08738223acd1f1d465aa46bb64cc3afcf74531d8552ddd15fd0a1286c06aa0026b8500c1dd6701b7090983a3cea995b4fc74489c7b46ec645c483d3658bca7d9b9cd4f2e1791777ef84f8999cf28269b5e940645529b0bb0897b89eb8735c5eca33e4480623e44896f56d6c219656cba0905e2efe8658b4a015e586f361f29b87b4a738b63340dfeb44d67e0015919b4a7c4fce9590cfaa50497eb18716ea31c329d558a37d61d0a32045c0214349d8701b3f6cf4ba639be60e6a85bbda4f5b65dbd8a010fe8ad7ac029a4db5f0413e3139e252f1524090d1325c187a205ccaa42ffea6585c7e0e594985f4652d4569242addd42830ff288c5fa750d48ec981bed3b08a691c4fb517bee4985b8de12638ed7df237b327ad9d497dd11759eb4b2546bcf11810c9a8b4c8b674694b5a69760227d03c727e2fa95bedf7873d9a88772b76974981e647709afec10cef7252bf543feb8d339b3d6aa9c76be997a3e438ed23534e9160441a064cad0df3b8d0d24939f5a864f3bb20f902c41d40f0b3d8d6803b3afd15fa1d3cbc0ea707c33096d208e0723b195637d6fc6364996d85b4c6eb7e702e9df7c7bcaf1250e060fd4d6802a0cedd077f58452530a4abab5264f4b580281fd1246ecd7117ae052495b1931dd700219b901e84db6f6088e542caa18f72d37901fefc79689b2807b401201af26c8313c317cc9e5d23dfe7c4b3843be76c312d2bb93ac17296b967bf319fe0d547a2b83af666b42a3e0d6244936596003c047c1455243cb61a7d172b955a41028b8ecd2deeb7b32870f091c90105d79960923cc0e6c5e9e858351e7cca7ad8a1200038f0c967be4fd4c067ddd0c69cbea00bdb01d25d308154c31a2f3945cbd1035176a72d93bb531baa20133e396df6cbf088c04dd9e07dfa8804ccd235d47cdda2dae4e2f5d974a3fd4f5c5e45bc983d9b10c9d99b8dd17e743dbd481403846d023abda15f5a404a69351d003f8272edab34503972a60a616d76ad5e233dbbc40715295556ebf686b941d6c1fd61466051148d974f7840cb3e02c53b50e97a4ff363d4a53077fa707ea968f90461df3e97fa18651645ecde6d6defbda54c52069205a005d6053154840ba434826ff34f94c8d3d764bfe4335da6b0f4859b6c2d0cd30bb4e99c02b77f08393a3ff9accb899c822fba8dffddf427a6a11cda77f80cff6d92139d2439ec964832fd4b92d8efc88187e559d889ac680e7c86436c39cff516af7845d4798e33878df173c83ab285d21aa9d96ee57993556b5231157e930a106ec4dc6a53f9cb47c94fb15f07b85f81173a02dea4ebfc48e839f4f7ffbf45859d510f57fac2505b84df7ca71a69cbb46801b62623e1d6caf7a65b2b530f565a75cf39124e6f930c5b7218675bd807d701a831f620058a13433ed3a2d5a18dafd346a5f49fc04929bab26e565796bd8f3b0035f67b319a02559d46dc74ef9a62288b1d801a6ffbf745fc77e30fd111c25d84df6e1445383db13dd8e9df5996cb9b2c194eca803df25fd195c283215c9798787eeced93fa2dd6e2c7c41781c6c4094712457fb1569ee89dc3a40b57f36044fb9f2871bb411c2927de1de69641e9f0c731ddeb6b345159897290c1c8513cd1cb630726572badf8e297b60df574ef1a84e84aeb5caa713e13c957679902e60ec167e2d36ce8bc81f8392572fe9297109f84bdc49f893eb3e9c2c009596b7cf45af4fa51141f952a21be288aa3489221ee683fdf67e22ff9288a3f8a4f0113edafdbe8fb248e2f6251fc514c41d0bad18170a2782f056b963985b25086933c86ad002ae0b61bd19ce480accf8cc222fa5a13455503ca69518839843703934a640bcc5947084d512dd627508e517878c2bdf73ed0694a7e490a84c8a3afae46b6e4bd17c7a4f4fd8cb3b539678b2db6388b18638c7328ea10873e628c49ac628cb13531c67875a4383cd2b7e42eee10fbc6e8ab0edbaf718b1bbbdb0fafd8c24bfebde35f58698aa2288aa228c6d9e18d10d2100d48ed2cfce00031dd210436bea98ba228a2f73caf949e19c270b617e71c9e4038dcf80c01279dc136e1065ea2c9c2759c61fb8e8e347e7e9c66f043dbdf062a29d168b399c5d8ea25233bcb6f9391f9c40edfceb2f5608736994c1473cea2b596dcff82c1de6efb3074c88f48d0bc0fcad20c61f9038b41799a5afc601cca69780261a098697e6031f00d4bd9e09c73ccfcc062505e5d8c74247b1e008fa25b0f73ce9e439c91b299b168862e5e10fc339add5dbc6593eb388f9e43511465f6b31d493293f8f57218ec9626f976c425e390e48e365e11566e9504d2418209e58f77066fc0d4f8cc166abcd43c955c6ee4ef099643888f91cdee9563060e318d8dbc75dd6062f3748343030d8f466ac9c034259a054a697cf8c8debecc586f576430adf1dd9ac86042f304a359a09441083819a6a27404dbf2ac846c95a4ddb18241faa873c332538525ac5295647ca18bb6e55545953b3f559a685b5e0f18625b9e151c5fafc4eeee6163111b47608fb089ad3d4fd0f789029ba6d19ce6b4ad60883b5aa53191610598609f4b1cc447bb96cff05fae223de4cb3e990c3bd01ee127497c7a90729e264e788042021f8cd9b73ce8409f51c0448be2bdf802e56059a7b446aadeedec38dcf35ba375b788f172760f8bc2508eed290a77c4094ee39a843c2b385bbd50b720395de8b7c991548d0befbdd70313cdc7dde6239bef7a9fee3a82cfee0839ebd9aa71fb3af97eef8a8ff25f47354ee7709b54492d028d56354ee31c49bd6b902e9d7324d5e6bf4f299a5a3cdd75e6eeeeee79f597de9d7ffe922fcb2f233fe3d18579ae84b6ca81c99aed46dbbdc98bed2f6bc2c2f68fe064f237c324078793c981669692dd4c0970c9952749251b6608c1c9e4e1dd43d6b002d41a3098608cee4a922472367ea29309bfbbbbfb1615b6ebedeeee9ee164f23c81c54ac243c2b5f10f75757023a5ad8ab020032d476a1cd1b2a918dbffa7d26cff199229db9fa6b4848167e7e2d67585515a23e7e07e4504cff6cf9e0bb33de701a9284568ec5e91ad1d4f3d2369b6bfd0c9e466a280824010f9b2f1eb9309cbaaca7011d2a66a4812224fdb3f16bb950cde7e5e703cd97989e2e30507ee691ce113f3c22ecc13db385e6939bfc871f305888543c455b9a8dae1831c75f8175453c38e6ecc3edbd39c60092448c140645c1821406b982e5d4c847c41ae8c686c8ef0c850e73fa99d150b9e6f8cddac5e90a9fa33c4ca05aeeb77fe742c6c3d6c38ba05f379b067b5c2d7eb62a3c7cbea0a92b2550baf576466fcaec0f8aec4b059f7dd70efbd5f001e6cf6558d783cb68832f62837032f4a6b14667ca3d0d8324604963c60b240c196ab066f46be2fee48ada8655404965a19ee96e4f8d44a310b1733346ae5c8c40b9f5a499a71643ca9d92d5954606a6193919a5a98eacc46679885dddfb9d1f1a2965123353577af533a06b6c9eebaac6d6a2a622cdc89d4afa642003dc0bca8e5c76893f5d554582eb59c4ee5bb4bfc4308554dc5cc049d8ea69b829dae9a8a198e5707c53d3d6e74dc6a4d269c1a5c3515b8aa76ff92963cb3e505aa1a464b2f3547ef56e14ed77d85e52093893ea4f588c37bc72c8e1f3e467398ef95d27b53de30b41c6432d187b41ecdbcad4d82fdab6ee4476e36a1d68eb023ec88fc229a4332c9c88f4e266c6d6833766bed0bdbdb3d6d0394315445b1ad7e6b5d2ba3f86e7b5e3218638c02e312f885f10bbffe7c200b74a2260cffebf57a2d71809342c0e6638c4dfc7a8c456c6ccbf2bebc2ccbdfc0ee322d61a8fa1bf6248aaab07fa1e28f4e9b445d6b8fe2120e330ebbdc762eb5119d73892ca6aa053b7f92ea40517c8dc68e7e9eb464efbe8d22e4bd9493803eff9073f024d21eddcfda02a50cb423bd2a62677bc5170e0d607798aae64892e779fe06769f290782ec3344d517d11355c387954edb2cc7878dff4255f7edd1f4990d730ac21d73646b2d699568d5eeee2f1580038c4fa6579af7936418865f5e993dba3f7e0639f406f9d10c7ee4dff8bca718aac1779f06b0afd7eb64826518f9426ffb332e6169110776c37e03bb61efb45fe8455514fbeafb7a1f455475ad9dafe9b38b9f94f6add1ef4e712b3b2e974cd537786eb4bd0a140d93be8defcdf2d9fd25a7c2494092c7f3f9b27e5b4d6f90938ce5339cb37c86b3cf8ea0ba6702f2cea903b78a575c0b5312878ff04fab1edcd891ba489f15eba5f5520036b405515aa3250e50e024f3b1b55663251aae2467304dc34a8e8a90376e21a2f709ca6718e38b23772d440afac48e037c847928fc08bf4972b024897da28b6d4a123348401991a4d632d9ff6c66adfd3f51f8cc43a4a09f445bec7638c541c2ec17a8efdb8c4568c11f861de0cf8fd322ecb4fd64baa1187e51986a30eefbf7efb6e02ad186b8d3687857da2f2d6a81bbadf97eb4a6e0a6d5bcb7bd4a349fe57dfffa28ef0beddbf6a86cf9ea4b616d513b089fa71b756f17676f968d315a78beb323700e1fd91bb480155c18d27887ad256bd5d9c598e4599fc5abdbd809e6e3a0a0192de84b5b5a339f1a04a5eaefa0c7f0504f6b538aa03e43437f8131df5176412e7f8702dab647c5665fc78db19c73ce262d168b415d6006a1954f1757d0674c772be7abcbe5ec6b4cbb66bb8ca1c927fa9cf3cbccb76ebe7533278bbd058ee9fda537cb66157d86542855b5ec8b5208487243aa14f41adeee215495a1f80245201291df0c7a7b76f0eb6a81eeb6a786f402774092c45d386623455717e6b044ab178871b9e548d6c476a74b39030f3aedc1895667b25ff2526ddaa6cd419f3fc5f5fddfcb548a1798b384b25294a208345abd595bbd58dbddff062fd66de233718a0dcabfac953fef9cdeac2c5ad0e7183ad35023aca07fd92c2c0f4460c92e96eccc291ef6af1f316048519cc873d3b95224732ef644a94a664887d25495edd8634873507a814279c88d021e6f4204330d18ec96a08d7c5696d9080201b8d0f981be8c4089fecb551424f4425f14f4426951d0e71d94de1c6c8b4a46e656ce3608bd4021f406af0f07a5463ed3c0770876446ea36ff002b7b0dff3b423507e0dc6ad1c4c2fd0681b85af51f536d9fe01b510e9dd619889d983489b2e20c2d5559ba5970a1f95e975275df4acdffd91e35b2e9ff9d5f2ddb30177f64bf865e2e843a44db64a4ded28bd3b7c04e26769d3f55571215803915e2a2d470a53cfddfb6f4958a4fcf78a20d04b7577f8c8b99c8fe7ec1afdaabeb7e83451b4ab7d787b9a20c01daa5fcd8387f7bf541f3ea01e509907b4113a4369525254095d5241df473ba025644424404fa028c4e8acacfbf31f9f8d530e68111a020afa6e08809696a1f7e9daf091ff871bf79601bdb8304036b47a7f9e66fb772db90450fc754ffb0fa118d0144bcb9c82fc65f4a06041b9b2da73b3562b94d51edbb5e8be1beac99154bf637b31ba1cc9b7b6d11195b6e1c98496c11f6c68fbb135da62c1a4f467204e32ff94c08d2e128bc4748e0c0b951093ed691b9d437294a53b2ef6eeacdb90dbd71fdd1945b73d9946df69f159fe3952bef2d97d114db49a7d57192bbb5f89f8a1d53c4526cb37ffbccb3c28ed5a7c76dffffed99ee6cc6687935cdd659ef3b4e80ca4e08856f3943cc5918a6816d0d2055832bcbc9d5335dc39e3edf955e7ddfd4eb4ea360cdbf6a06e3b7ffebd9e71ffdcd6f766454f935bcddbfadce4e1e9bcadcf8d9dec3395965c1c510e4af8bde1a49cc61edd8743bab0efe70fdc7092ad91fafe2880ca38139081e1483671f7287a4605dcdae59dfe230a827ffed2f198c70df08fa9ea8f81bbc70f476066e3b31bda0cc3ddb35f1c3b3b8a84535b3aea54fb1231d57bcd7b2a20d7cc34dffbe98f67332ffff24fd47578bd7d1d845e120dd1342d2859bbe470dca123c7312453d28e184754d5fb83f0bef03bd54a12cda9ea3b7cb1c804f9452648f2853ad596f02bbf48a2b6166e704f99c597cc6c7498929bc4e9fdd38afd0ec1755d6368c837b6ff05bb755da320584d1fdbdf0253b7ae2b0c9b93ed3f00dccbcaf617c04b47f2b67f0082ebbabaa00a7ddb9ff65bd795c6ee2566fb5740aeebbaf2e8117d20f863b1fd47fcd6753d2178dad8fe227cebbae288e5b63f05bb755d6940652bdb3f046e5dd71936fc65fb1fd9d675bd812bb5b6ff6cd4852fd8d9fe2082ebba463939596cff0fbf755d73f8cc1bdbdf836f5dd7a8df9964fb1b89bb755d4df02cdb7f0251b7aeebcdd7730fd6f64f45dcbaae26b80cbd676bfba321aedcd367fb3f8ea15fccb6fd3be85e44b6bf04bb755d83ec205061fb47f03bcf6c7f0e19471424f904b77f11fe69e0f367fb43807debba42ad619f19db7f03de6978d2b1fd65389bd4f6d7a0e1361261c7895bb67f86e0baae3622ddcb6afb0ffdd675b571a18c67b63f06dfbaaeb6ddab4f70fb0bdd34f8f4bafd836810fdc8dbf6d7d7b7aeab0ea01091ed1fb3ad57f7e407ccf63f67d8463f68b6bf695bd7b5060e989f39dbbf5c897c907efe6c7f58705dd719e38cedff32410ab663fb93795dd7550713a1a9ed3fd2e0915db6bf3843836f1cb3fd431bd1efc566fbe7185b996bfb631a1aba82e4953f7fb6bfdb64eb6bc6f6bfeeb24d9b627a92a961bbb8f2d8b98495d54589628be2e50e1f24bcfbf1673585708e73f1a40cbae0d95e4436dcd619a0383c2edcebd563a1ccd51c2e1d49e27e5a3c80706758705185c13a6362bb2d315cbb17de8dab1a2abdaa0972f5882a08d6e9f29be3e23722e88b0304f2c9b1113cc5a048c52546a587ab4f0436db5410db94173c50a76f3582643532068f0d8b2b9d0ec4e830c1832bb9e08c7421b9fe7eb9df6f0c1e2727d04ed4d5c10a5e9de0333be87e30b6a8f0fc62ba2b2b9d1e3cc192c44ec4f5d25c61b158cf10178ab901e9c46491a1857c3a38a7edaa05aebc527441382c4e1f1b16ecfc6243c12fc0386be6e4d1bd6c9775851e9f15bab410c3adc083b142558b1d04b1a0af0d96569edfa90117b40ae3e3a2cb132475941d5fa420f94285f5254a903c5de66e0c2e471e3d6b68ead86099f344be569e8f383f563db65128d8e3d1d13245cf6dc410cc412c41204f70e268abf3428feee5bed176c685d7ef0c103d50827666bcfc5838b27bc17eab1c3de7afcd961e9f0e5a833728df0d14f4fcb20eceed891738595ee0e0f400853ae0d617567e557e02b21ccc8b9f912a9d602d4e519a50d9f510d3e5061c0a20a162430ad58a3441462c615080b97291c38b983a5ca4baf22c6047c20a5caed8c951c9912cf08ad71c0f2e1158af023b65ca64d932c50b551ba9b185fd43c7d3947032921c4c485eed8fce9f13e0dca25a50a148e9c38b885e9a381141218c6f2a09162a5052211f7c8bc06f05307fb666bc8002e31af3811f7dc0bc0066cb8aab22641c8c070645835483ef6405a9f4e8e1b952c2ad058cce0c0c3e42a06d7b7ba0acca34f9486ed4c282d46b489fdc1b2e920b1df10d0ba90e537aa854056bf0dee499a560664a1f1f58807ce131b4040415a9292c2b27b4d8d1e7015645aa604921cad7a64899524b1136e1141cc1b6555051b6b96d151499a8110fd612c105172a177ab489b3239f308f9205f7b16427868d0f1b2c0f007325125c059d234707e5094c982c52b00f641e3e2c3070e2953df8e5b92fe49edd6d4bce61b5cacca811d58b73cb2235069fb1f1bafcb8d2064d1f3ab07ca3c808057fc9894eae0bd80a99d4b805639158baa010626141875892656cdb1e9e2b3c74c41eac8def1963785529230709d1a78e9d29467038b9e5461e1653bec953454432d8c9ab8a4c6a94825788fdc8f3a6ca9230695ae4c22f17461da0902d52eefc294346ea87332932eec0e4b48c21d18ecedbf6ac80f418c5307b60c58ac79d40ce0b161c2540bad418c096be4985528c9c94c8c1f3c54b8cad1645388891658c1321515990cc2031d2bbcb881280282fe0ac7af4acd8725242c5ca4c941c983025d89d25fd657e0294adda9d06aa29283c3551be4ca9a94a349c84819b1c55baa2e61c6951f327b73dbdc605177841c2c3042c624c207306098b0c78ce97ba2c53ea8f4c48923d1024e1e9a9abf38896a3728b921840a6ae0aa8489eed9ffd0b98218c6643292b757eceb28d10940d4570a45d68feb89119d905e96cc49074652891608921010384e91801124192ac096c45478c6c302283c80906194646f46c0812c7c415c0c1a205334b9e80607441b6e8a8b637492243562a292aa53c9016096334554603121c19f016244788b64c0585b016293de74b36864c1962db2a1f4b76d0b6553e7c604027a4ca00853d2e5401a9b3b5a20697609a24ab9b35566bfc497f194eb2e6b620b1c6fdc936af3b2a7475e123248b19193b7c590e94a75e0b0fbc30c5cd6b4f09c465e785e094d612274e270cb1b1a70e579e3b4fb89861b63fa14366a84e8d3ab8d812a01d093a72741d124cf79c85ce10d30996f935521aadd51e0ddbf6f6e8d8af6d7b7b9468184d88fc54e1e974bce989f9f59122c60b90296b6cd8352cda904893e3277e409bdb56ddec6c55764687db56dde66cd5fa5e6434b96dd5ad8d7bd0c373c64ab06dd5adcb2eb7adba4d91fdeae01e58a70e126ed26471b375c9175cf022a4deb6f7429a3d6edb7bc10c0d8b4326585034b6588152e6e92a5c72813f575a5c5b4c52a053d300151a5175b0c4c8d3c462d991c2102a7fb6fc9c99d1686b42254e40a87a5aaea480230c919ad51ac265851b50c008c920059b9733b916e60041009d1e2538b953029029293d51ca0aa20e4d4c0c4cca1051ca1151ca92520a95224a563c12565f643d2eba5db46dcf6a6a8bdbf6ac888829877adabe1d2fc0f892e06200581028615fc9da1724d61ebbdaf5034461ad394a6b52733977a4046b8e5a2d68baf4c801c4c3c29fac5a199a18bd2fde20c9d54ad1c468d21230bc5a399a186dba59b0fc3152535176b12fa0386278b89aa31ca2d454f8bd554afba8081ab7edbfb6ed7189da15d0b6c7a5c5deb6b7058f0c3db46d6f4b9acd80d2faabb4462a6d83083b621ff519a1b4462ac66d1046e9bf041c209079207a0f69918921b3451c5e6de83dbc87b4c9c3046887088a3668780c6991090cffe13fa44d191ec33bd5ec976288e2bff835c02ff42ff4ee3234ef99ff85da1df3e90bc447472e84867fcfd797e3c994ff9ef788d62febfe3ca7f31906391abfaf5a3e33cb9c69da3d41b3cc1dd1f5d9d1c42f1f0794651a84a11c4975fce4487e579fff9c8d07bdcbf9e015b7c0b68c2f2f8ae5f1c1dafe8ee54819cabdf7965cced495d3dbe5bf2c7c1f861675007b0c7ba77d659a0098c343dde733f7278ff2a80a14e577da3955efc51803e59c56d081bb2f900353fa41a41758d400103ffb59dae4540391de5feda278976e3ace918a3ad03a2dea403bedfc4e4e3b6887fb938310941675901d87af5bbc73d087f7f0eaa5327db6e36315b0d0aa3f81787fdf89cb73805290282053ce7b09fbd3532e776f2e0bc46c06e2ef6922c4cfd2a68c82949651a894edf546b175686c00004000131600002810080884429120092345d27307140009599840745a30164804712486510c04311c840000c22008024000220e31a318d50ed7e74b2d240f98433a9bae8e5b869ce533a1cb90629b003021a9620d97d049c5f27ec0a26f469174ad554442bd56dc7fe55ea1bddde60a581dcbdec3d7503759d108bee1e274c9be15f71f235d4614ab132a9c5bc6be099c5bcbc0559185be9f8078ff524537e67d56630c77a95f6afab4a22880a996dded1620ad3fdf301d5a14cc044306ca4ed531c4c3d283629d2450139172be36139cb8ea06c56c32d133bafaafeaf524d9329f017a7428ac5f4584b61b6d98aaf71e56fa8c2872120f358c0cdfb5e9fc55f7152265b025d9f59fb872f767a6cb9ca2fdff49d6bb9ab10052f4085d716eb48f0710a45ca15c4dbecefca9e858a1c8d36476e2c6254174e2280a5ac58f5ea382e4c943ed0aa83f3bedba0e508a3ee927a3a02702546053bf97296b5c6783520c1161123fb6a3a6159c9a4403ab1664f5f64539e845be9604dfc42b7f2087827a0c1d7428d826a55ed131b6a237e61591fd8cd0a3a9c8999cb241cde80a58846cf0a1d7906e6245233835a2e992ad00368c68595f665d66454ec82f8293b12f4e04e7c157f52bdc61c148a59a7f226b0e5cc1877ea382fa3d44e01789e4d2283e063e4adb1daccbd2ec126c3c5cae2b0e258d7638f98aa3392be35813f34b15efcdb6ec95c6eeab2555e688eaffd71b702228f6c04f78cb604022da6ef2601c1dba299889acc6ffabc5ed6e3e9d8301809bbe0945d9260aee9227cbe8ed126baeb84771ba1e793b7f4f55519977341d9a14d1ae50427448ba308b56dcf9cc7433a2689b50cd4e19ea45cc4e570e94dc32b99b0f80e9e6b18945b24bb99149751534bf0b1238be72c549b77c83232ca7407b1d81b41a44218a941c6cfdb623c2f62e75cd28a8dc26ba023e2fa1eae835d54d4ed1ad36393c02f5cd5cedbb82c92b37891555ddc395e61d455599978e9ee51444adbae4e403c559b1c507423ca9e82da348d4146c7d1c31e256c15046b070a56e0fa858b5f606e71c2021cedc1f3b5d06146b27a81940431d251940cd01a6e09ecc3a805fcca530e9ec9d090c273282b180c2225c1191618ed2ba1cef22660453bd4e4e8932664235872bc9317b2e6490207d06141821aa7c67867a59b0a7c7afea8d821f78c8324c5a2f7f655886abae281881b7eaddae2e01939422bfe9e92c51c42495d5fdc4f66fd3fddcebeaed14b8afa2fcd804ba902b1a054d20dc1f26facc2992bb0a266e6cb133bcd28b892dc623e06a5f7a9978b8f317e04fc7bf28c78e7c9e01bd8929e68dadc2805caafe65fd0c743245d1b924ed0a60c17332ebcbaecb9822fa154a702fab17e28a46806595727fab8ce96554b897d32481f4c3c8ae78f4a3b29ef9f1ee12d6795df8d3b86cbe22e8948b5263e529d8b2dc48315bc189ec69b23883e2c190d7dfff63221d56cd34456ed90caf9de818e260da0b40275deaf0b8e4b254f188f03fa3353dabb28f5352c2f3d98e218d0a9d1f641360b0fd5e392af677609b00bcf78018fad3af858b068cc9a215e43263e0b55520b736ba6f1f42cb62f99296d26dc271c5a855bcec010bf33cfa8bfece32345a6777966d02fd1df6f948253829f3f15f8ce0b2578449db122da9b118454332d9f47c978412651121944debddf272979752cb05eafcba66be6316fbeacda4a6c9c1ee81bcb5c85e7cef37fe4efafa10aaa432ac544e8a78ed25ebc46fc155157b3e8c6bf6ef2d8d498c09334fc0568fbd379a14b72daae7a3e27f298dc243e4bce9a3700fa323a698571c935a9f3d8a4027422abb2f13ccb42d49b0de1c0be9178b267afde156aa3d455d1da018c3226da687649b1179b6bd5ee33ebb9422efaa9877cd7a98f5d4a5adf613122ea397b66199c32e4be593722022da1dea26e511d5a15dbf9be7cedce665a78ac0cd85dfee54d171c4ccb43dd4131a0444a063759c9c347056e97782c82fa5900c256161728376b8faa25e9f10ffd8530ad29ed383159fd4c4b5a7a35811e468d7252d6598e1086b1d62da46914e7a4f292fabe2b068b42871482c9b00d73db9756e4d628fa2e9f562adb87297c6871a87a3820464a9456341c5717a925348bfdc9b90991a0489f2d705afaf1887b9df1d7031225cba5e9b441a2be0a370b805e4487630c721b03aa9073fcf0f71817e7f3ff74404881e08eb06d761f94d73fba73c122ddf1aedffee5cbd50d81678aee7e164d7ac2dbcd843b8a80d8726ed3139e36e7e6350f5e025d9c94f0b3a8320ee8d9f271bda8e6bf08c8770b31c5248169529f6ba804e7a05ea911c5d45a6bca6eeb3dfadfd56e36491dd8c0ffd8a13b008a1c31ce0376d066dee3ae166637fbc2f19af7eb85413c72d924dde4a4871d3312683a2f91172091df9bb4919f996bbe05852e7703ccda08e4b644355192a0c81125f8499527aa1cd081ee29d4a1a90641b5b2630fe6d068af09024f0ecb910674eabf8a4961d4db21424fc55c95c93ec1da34a8c7179b7d134a0c7b2c73cafe4122d861cd2738cadc9a2e2050013b8d92041b6eeffe6c7590f681bde85cabb229167a46c24e8d03f7465538cd473e1d442fc656e54373cc793b9a1049ae73be630a03ce2f3000487bf0e54eaec3aa8a29f9a7fc01fe6f0b320306fdc85e3b2dd65341b2978a07a1c86c9ce7a9cb60459b734ac82bb250644f6249eb57150a77b63d3feb93a9a7e48560fe7420f4e0bb8e40935ad9abdb18d67602794ac8d132b9170d215631ab47a4299ab6e661e23ff0c2f92bb46639e36773c5c4dd4dcf16401ac2a30bd9fdd6c27d082cdd525c5a0a6b9e95716f8468867524c0e88e08ee9bd1d499f7c5915df0b3fe01ed28b67543e0729b50022277644739909a270b74783fd49bfc79acfc4558dafaf461e3d14ad7d3b404432f27bd3716c2eef6e1abd3568195ac573d402888fd4d1b1e7ee4d6e181cebffe7041cbf01b2fae29480798b3b80239294087d0d0ce046cabdde7bc9a17a197f4b975db2b49333e6866993dfdbaec826394669d824be03758235cad558bb623609c6ce9b564546fb11bd763f36efb2433fe85b3cf48247ef5e225a9d46eab213239fd409e41e999bc41287e10715364b066798249840afc49e4668656bacb0ef9ce6a8a8f9d1c1b42fb163db8da573aadf24b15ca8bdd8866f213c5b278a03e8043bae436d4661d8abff4a8abdb98036790be0d57d819b2968507f90faa5635c991331cad7f0b9ee6bac2ae29bb307a0a9037fe1fe3fe4fdfec0cb9251007df6ab14a05f4ac767d605c404b2b82812878d57debea002ab49e5c541a1b79f1a644481aab6300035a83ac5d72bffe2b7beffc6fe883645224b27dac1a9adbc663b01d99225a49253ae7a7fc5c420e6d2be2e8ad5d764476ece5624d89b6bb29f77679150c1c20530fffae3e4ad7fbbd0c354b692d0c35959f9b93433086699debf3e84d5967f81c57897d6c05be7e3171df02b3ac54f37eb7ac593e16b1e3a4502421d278ee242dc2665f2f3a796f16e416287f4ce2380abc7720fa3b460fa1a8ed91cc0735ea80ae095ab05b2b6047e657e3209521b505aa6a388208730a2ca140a251896b5b86a55799e5c875e62de81d677ff459f82b58328140dc19e2c0d65eef53d2b8bb86fe8025301811f1fdb2c86b660a4db4486802263c9ab974e57adc92964eb54bce039efb2c2fb5f4ee1714b5301d6cd6080532328ee499b08c1f4af902238b64be1b37d4a9aebfa18fd143429fd0818a06e2a85df841c1042756a2ed9684daec8a0b1d83892c069a7dbc659e7f271135ee73ffa1ae289107764a20538c60febedb3c33b1b34589dd95bf273d92ab99e584cd3aa124df80c3a90489b3ee6e3a8f51e83c19cc88ae3d6ed2e366f488461ef6e2e8b4d2af02c0fd24af17191f732bc141558c1b0692b926c1298dca271d1601a83e5628a36c59fe4f7f4ed26647b7f7171550465a4269fb0e739d26e3cd0c18d8a5adb3a31ccc7913a9d3525409f9c842a569570d7f9b49ddb958cbdcfb6ef50b45199b6eb33df7d5bfc8f64e0e0e1dabfc809423e94a54f7e2185c8c999d401183f2e6984f236cfc25214ddc7ff2ca690ce6d46b11e84a6e30948f5cc7f20cd6275ba4ea6487fb803a4e227461c784fbaf92a146748653af06eadc67a4131d0c67a137394d55c2e3cb3e6cb4c95093da6bc2d83f85cacb2d1d86b9b17c46896819aa8cae0237b21ad1223be80f6528cd6c83ec80b325667934b35bec094bd92d4a33397c693602091d03cee8e8e357ec2d67f3e949509f4501c7ce1815e60a5127556e9f06500b53c8dfaf13892e7856248ccbd0f891e066cd41448d7792d2e3da3ff7644876f4fc85ee4a642868c425a3c7622ff4b7eefcecc0a3c2028a88ee6ba7e0cf3fd0852de44e82a52846998446e2d67e4038371f6627e3982434730293a85322d1b5f9f16511658da0ce6f6a806979a274d0e6032ce0ae63ad2da943c0d466650a6c8fab5a134180ff77e9daa552ec3d599119797b19936844bef032edf871ce45111dd145116931cd7b09bdc2e5a5b4eb54dddcf9937b0eeb8b8cc411980c316624282807e3bc09990687570320a38c6e793bef633fa8c2c2de63120fd079405af234341e1bea0940e1ed2d31e8eb57a35b1828f2b9792d9d51a1d9394917fb3eccec4e3ecec2ef4c56ca7793804f6080f612eebe2ee39370aea60e9c1b80dcd61684e8600a8ce1d1ab819468558fdd9198aeb1a26b000f9a290baa29c894e43f17713146278b5e6e597f63d4f06a2f54732bdf4bc67b854633fc0a48310036b1ea709fa6f9f04099220f6825fc1f69581ab3bb1513137fb7903b60e4b37bc859fd6e36b42c63b1db93caa796716f33a209a96e65b38f69a3bc300aa87952e8a1fd6499c24dde75ba565d53f1c60be1edb5b9ffea45d2e0619aa1c47436ab9b9022543fd0ac93108d2897da0d34094c71b53b038409855bf36393001e29e0d7534177c729e74620f22c686ddc394ab45fa51c31cce2172b10d07a9e45ea28f284dac38e809f0ffead1ffda2a748485d38c9229a636cbedfa0fbfef08d3924ee8580969b12d994e322403e9c813e6f4586d67e4a3a5e578d76aa451086182299e1a29f0d8f422ebff9cf4b2dd62107fc3ed0a9b0b5768901d92aae91eb03ff53089c29684391751bdf68fc4058ae2ce110a8963769e795ceaff82ec9ca6eac5eed0f0861c9380a0f1f474f05ed500a25c23aaad2baa93e87a297fd2c5a1d30ad01b810539c67fe800a2e85f614f21ff76034e6655eb23be4249fc277a510afbf8781e984e22f5986035e690e6744529c8f6e0bd825713ac660c09451e2c1e294289aaaf2d1c7d355f2d06f52fb2bb74842b82e4ac8458f24c256d7ce4650e233632e06af4f5024333969ae3a49e8cffab1e8f42b9a1d8de784b538044df1d90dbdc5709bf37a2c2843d5b99bf81994bad0f85dbff61b55fe5ef3a466a36d1c8a3ad8ea36eb897efb0b8aff9e63417593c598af1adff2fd3bdc6c3708335a1c0fe6bdfb53c76209d9ee94c7c838e4099af7fdbf89b29cf8cdd4c23a5154f28dfe18c26977f7306ee8acb9db3fac924f11f69fb7035d605c0534e97365733207382e493414b8b59e241bd860841fe3e4329351d3fd44dd4b0698463418464911a83cd6aa298abbdd00860456443cc892d0d30952f90b605bfa607c4932e8885832be2402e84c00d484d922615dd6da84e4cf6337dff208e9f7b1930c417b1c6dde4bd2b213f3bf2252f1b468f415c37c599cec961232584b1126b446d5881bffbacb3bda4f244f73bd51f6d5759251262363952275a124cdd92210b74f072f8b08006622098582cf653f3717fdd18a9c1f0528cd4b76c1d4a0e211882f9082983bfc8b6662b67cb17924886c9d6b84f18f59cf03c23f8787b8fa61a8c4c9e946f9a3a8388677dd6a4b2c8ab3aed1ee18228f239994e90d37a3a8e26f1005757babc26798924848be3c25aebd503f4d2198e74d282ab06620cdb234977852de0c4dcaa071879b1832e5155493a3dcb7e8108c7fd1fabc173e4c3b6dc360b8c1bf0cef1d3600ee279e73d009dfa1211e1382866053c5e3ee643f1ec4b8a66dd797ca82859cd0e21ca1012b6e006597c5d9edaf377f9330dde3e8e5b7f46d7d9fe1ee23d440a5ebe408211f8d50d21723b1f3d11253ef99cc558760cb026820b30400f896d0c11a77f9f1eb4151208063a0f4171211b2e5cb14469f3b19dbf778b13203b3c441b743faa5aa69ab729dfe636ee4759081534a17cad1cb75b89b1fc8e7e45ec0560cceaa4384a7c9c289b7e81e07cd2d949862d9c46aac447d9030de21c10f4be854aa090dd29eba0c48f8aa8a14af04de0e095f405d339e7fb5d76a5f9188e6e55527c00b94f20c32240288878468a5cb31c8dc7be384cd40b5c321a09986d60c540f64e2789eb378bcf200d86544ac2702f10c028174dbd19ee8599b8e27b90d651e2d4a2417390851d1ff7b886b3bdc737c62a382774bb792ee7636650fd93e1b21cafd3a79628a9c4ac6702f6c4a8029049e704ca19d84bf506abfe0b21f50bd6fb478421b1ecde21f062c70a37a2c3f297f6f65c64435cc041e26360d95a7cb285a0b21537dfefb334c4502c7ed3210c590a4bcdd7104e0569d56b14aba1ef57f644361067365839869cb0c72756b89d6f962ce7d40e530c3fbebc126f48aceba1b21b0ee716fc347e1732ac134f874f085a174eb7d7d8c0663385dd1d3468c0850ffedc7eb11cf6a1056210a6dddefb3430a221e31c1c067e1857036b86759321c32b1f872ec65fd848e506a1876ce1dce3404b42af626c0acbccc6a65ba8b636daf822f88662f7674e43f4c29a42c54ea4c36549e106808606547f2d75d570d7bec0a2d570d73ac062d55039b71deca03de86f868173eda0419b3f8a06a5bb511cb103853d7ade62da5d1d3d882dd9033038d26ae4a84aa132ed072f997c404a2bf7aaa116c220c334df5f6639864b0943a3b2056486592cb22ce787341824404eb29753222b146577a01c345765d21efc3cc706b730f3bbf14f1ac83e60b1e47af75391030d6ca7e60c71239aa76670a345699defc2887d8c81ed60bea6d04ba92251d61a89c5e7a599d9ea64ea71affbc089c694f1b40ae11609de81aa1b32ebb7206cffd23b88de016660e3d06e8a211eedd36586d1320b76b3d1653619bab202f0d537141ba810eaac028971a48ee7d883149b8036866c5e0675c908ff7455468f7cdfb46cab82696232701e0d3f870bd5c4475bdf2815d943666f7bc433924f1adca0faff60577eed0267d8c76a9c498630c28e19590f40d5aeaf5592f6fa49a3fd83528f0079f6442655afe6e8eeb6c7f25e824bf5b78d8b4e62b3e2ff6122ce5831d42451f940ff0f03a15663b427ed322400fe39b90204b5a68018c9e4488e3d74ae93829601a891e7dd75ef3ebd1c97a17aab034954cbfd0ac017f564348364d0c34a69e6c8a66dc80c2ba0365c08baff90fc334ce69f736a9b9cc5d417f2213cb50b5c1582233daa072c7bdb7a947e08923b4f4d62b81f0b1127838e1649f83069e1c8bd78524623b3fd91b7eaed9d4fbcea22cffa78c40cc20b18055174aa6595eed61912ed9f33201cad52ea43d9c6d9d92cfd3f90210009ce1762b99c18224afec8012d91e63c8699230b7daa1bafc117c136cf6ac9f0b1fda5b0b44dc6da3a3f64c61daa3bb79735b4a72c847b1943f831406a77306ac23e99eddbcf38f8ea540cf547c086df4f1e6ec7bbdf0e903a81f7cb452d79c756fd20003ff13cc822ad7c1cf281821f5cfbab4b9cf5639440844c39570c0cf61281415aec1d092014591164f6e849c1de05f8c9f3980c74115635700b0e2f26d31f6372ef2dfae039b215f6186cf0fef8d677bf7e8a64ee718cd69df36f640678b78ff567ae2b9f7f1f5639ff03268094c99ec7643133cce771875a6b4022e6e8d63a7f843d8a0e64a67554cf1130d9c8dafed5cf129994567d5983478e9ef8ee779e483672071eb507636636ea2c317eb50780d379540f16398c356254abf5c8860fd2f3b7855c72b684a5aedf5c417fa3ab66f821afda56e4133e5b8df9c1e266d1f19f9cb630d5818b5c694d943034b6ad2fd278e8bfb844b37483e4e41c21defcf60cc8787b343a811f0ebc79b83f8f7def41a96cbfd2b992c8b1861e444c1a405d4aa3706851fc68f4d288596e75fbbd2b07b530d3513d1317b16dfd6c871f52fede032f1480f63c4d5803a42038acc482ba822c604fa3b837cc938f28c0f760d397f0f5322a0004546c2308d09bd27cb07f05fd1843320cdfee822a1be0ea0a12cc0bc76360ebaff92027f06372b5da09a967b80eac2da55e7c43d2a47f9417e0add5fd4651eb48ea9de85f9f595c494ee4cb3f7b4479b4afa5cbd2f08e4cc46112b9e5c1865c2260f21c3a12c1de4ae21c03726e44bfbd483431698d010cda5463a3f0ec1e7af14629a4cfd5670436a8cf75931fbc38b53cdf1b2a9f241f8c0e0fe1ffd2cdd57b71239b45bbf1a519b253c5d58184ed9654c496c40cb6fcf90c7e626d40393b7eaccef77a65a0f2db2e3d192e626fe4fc9772090f8d39cbb8ed41b2ba9249765bdd04d259d60ebab1887e7ca45827a04f34b8965d249c142b2a38889cecf2bb31ec14ff19524ec2782fa050f85ef59beb499837e3c79a53648a663394069c8364b87dab4e755f0b6ee981da91ec1b10f42ded265ad5cd9a1246462a04890a1e213752ec0ad122c29ded61cbbd9797da69651cea9a3f9ce1d2662d31f88c16f7116e81b3df9bd6d73802a2a89ac7036c89d66e9905e8e270585772741698abc592967bf8deff9073774c6a0969e5e5c46a0906df3823e6a8caf643ffa5d1a6bd4062310ca4d55c85914f014f81e9334aaea0b0546158de22d7ccc12a8ca3893910ab30ace5999323d84ca15fe75195d223be901890fc1d8ae69fbaa2c80c1c40e034eafb815b51bf076bc0174cde9e3bdb71fe796b57be1adcb0fc21423cccf09a4223071d73b110990bc78eac132ec6a3050af7ad2ac09499f9b7555461d66bb2c882d879ae98188a5c717018c2f80cf8a011c30ee2d2c563fc1622899a89928bc377fca40e12970f408ec77eb76a86e45f2e329ad9410a915e4ec2054c8fa3d15daf6330d5f319bba6e3be6356ed8ca5aa0a8e8ce3d909e83e1befb826cba82fb06609eb4ca0b95277cc864abe15f5bc4a87217797002e7883ff280c945e17aa9bc7cc3b4fba9cd10186ebdc7c2e5f95cf9da15e26b30348be9a099b91c7f44b498117bef803f51ea01164b233758c0624c6c4ed913b9bfa362f6d2f828bbb950cbc7002168b21af8f4432d681fbf4367bbf982a99459c3e56b45f0dced78ccb2e5bc385060ac8dc28b0ee2f83a73d830184256af963d4a63f79fadc0ea45c02f5deb60f2cf3feb0f0b0d10f832ec6bdf29bcb8b26eeb7e2695793a172d21e3cf8fd1a35ecdff78516a5ed0d24fa4e5792c15df7758495310d132c25d95c0e0b005a97280b6d51013ea004469ddefdc80f55a41fe05d0217ea1404b8a8b206cb1efb049430a6cba0445a246bb857219bb56058146a7ad4e67141b9f861126a6363bdbcbca08e2c024490aa2966daf00208d50faaf07e5888f3bdcabe7cdb1bcf38bd172ba0abb76dcd3acf6301f22e1de960667ec082001a3522b5e187a8eaffb0569a3d0a57b4bbb45358a1f2dbc560ae0d1ee11ca3429d97a0db71be29c079126d1bc6fc259a3c7b183b1adba9bce5b498810f449baefc2a1dd828428af0747e44849b51dec214e95e702fcad4e347bed2f66cf05a66d0432fd74e478bdfe8071f635b3cce5ed3e9610eab301de821ae8227142bb5a00917e80494cb0c07e79829cc372c12afb5de71bcc30219d1fc0e7cf37158df2b413c830ef03d663cf26ec86184d89224d73ccc8259adaa4b7e8d7786fb6f4f5ea02b8071157bffedb16a20c9dfa6582486ed1ecd15909470b7343d655359b8734f114f74d7b3d12b2e4214d8b8f606a84dced369f96cd44988f375b6f88f77d9284a3a37a12fce92bf75416597f5a5e4f97d1d408cea3e7608261bcf06b88d4ae200d65a7f089cd2b98f16f7b3929bcbe0202592629970e51bc9c025431732ab9f83e1689e2134854fe5e394f4016a442696e2f588dc790680f23418f162fd38b118675beb5f833ac822ff4cbe65497e4afa9313ff3c0c4a82847d4e2a9e11096b2389f583a2e24a3ff3eaf294ebfc2ef0c344e3404c60c813ae6ba0d8f6d34948f0a016523dc66f933541a43429c42eaf2564a6f2953f085bcec8334114ae15a3cce7ce3f13d8e69fe43722758023a853c817b147ef60a670d7e3ab88217de7ba61ec8d0221d97e4e19382676b8ddc8a280c95e4ced38e08a05caf048c0524dbac39c86ad7ee3673afd57b15a1e4358f2d03913093878ab2d5b227115899cc2d5d77f4bb6625e5219c4a314c49a7a5d5ee557cb87ca84137412068e8d40cef97e7a004640adb58e5d50a609020163e43499a225bfbff6dda7a5dac7bd6ba6495afa0a588e23056127dbd3695b6ae608972b8c996a7eac641c331eaaefe8bb75d741d0913b87a89b074e3c0ed333dc97fa1b43eb56238e7dbf6e838bd645b51365c2520417636670204861e2bc4740a5ca9d86e9e33911dffc5607ea806a08cbb80c58f6fdec0bff48066adcd9ff0d50294b9241e877df5fa11110d0d7b2ef3df32faff17c080097959e7be401ac7a52929ce260b26c5d900463594200c2329a640da9c86f703fbc3d21797878a8c1b38c0ab4c1cec38633ddc01eed92972d904397afca582a0f6a37f80b2420c3eba91fedea1b4a320c6fe28e144049ef62d5cbbb36a624c5e1cb999fb184277fe2992de5ccc3687e1414aa98eaf49eefa15b3590efbe68da3bd0a16e1e8b3427cf5469c553e80148d02358788979508a08a327f89f5ca0dd84708f5426bcfb5b6f5f6c770356ed745242010f1ec11b2b114e86e8953db7b737b68ee54376c31c510866d83ef62af1a21e76c2cc301e01fc16741dcea9ad5bbb589e1073d1c7f2a53c2f1344f84a9babfce840bdd752f08f60421d558a947c88b3616e5bdd16178e7e376f2a3b034ffda56e7df8061aff107bf90d9773dc2d36c79a641e58cba4a027e892eab5b9c106262b9fe47ee1f651953ad5ffccdb395a89df92fca9aa719e3481f4c3c2ae683843a831330f182fde56a60a111e5b355e0285dd6505ec85db7f77189e098507572fbdd98d8b94b927767c7ba886cdadb63bb7bf4602d4d05f8122a8b116f605d2acbf8f62ceac374db5b59e3c12aa8a3ce6837a7aa871950ce4a7fa48d41a2550061a552f7b1d4558e24456dcc5c784c29af1875b98d45ba81a0613ae3e7aacb541f9d482e29b58507e5adb013ff9c26e8188803f88c5c63d719e4459cb1f449a08d3dc8304b9d33f0ec62a898cda29d301c54aced91839cbd238b36ab99055a86f22ffc766e69ca601939a8e019b7a42f7b2237aa4f90b09b61cdb31c938183af0a1f50521246da779640975584bcc3f2ee3009a920e08315e1f6222314dd42160a3011d40e02aff95e6b7e62e7706b89932ef9a0d2888adb612ea6ff343edf2acac3d9fc0147bf42722bb26eb753f0bc39f7d123052731b52f3fa1fc27f7faf317b581a2c97693a850033dc5c08b084897bbc4d5a09e62af2518a7ce8f978203516812940e0cccb886d9462cf8af132706224636f9bc4bd683cf70c10bf0fb2a58ceba04c178a6853ed6246ba47de0298616346f0d9dc3fdc9fb1eaf5f2881e55940d0d0acc3cd657da01cae79377c87a692011ee365243c40d6eb93af6ac3b7dd4743b0208cde29f5c22368e5f03a8b2f3524b1c597aca583d430edd81d9c60d99344a5f3e9ca2aa2133d63d65da562a155fccb7126106aa06c625d016327bf3111d3a76daae2fcd4e9d22e67429e44afbb9bf1dc537848e79dfcee41f6a339468b31bba049c4c2168d60ff25f4676f4b885b006275d3e22ac8e88639a13590b8c7c4271448a35e06f3bd9a342d9957ae85f3b5120f59ee03b0bc18d69182ac3b0a8097cc3f333816a6f2544a2f842c2c77b4f442615e1e901977f989581998dab39c42239a223e72665e3df3701b83c7daa2282faa6c3623777d075d69805f00819a542940304c8634c45ae1d41e925d105db5376d86110111e6b0af1c2816204c00a8e644b1935cbc4f4dc866b0007e03193e588ec5d2befa63564369b36b92a65a15a60fb03ef5b0dfef4081a4fc2398d206957e698a64a4d86b2ec18da7e6d9b86e214278a85636605348eb16022b2acc3536b49af63dd91c0b7d708ab886cb81ea76f9371bb178d7074704835be5c87feda7d90421b81abcdbbeae0903b832ee056207cea68c808d5a4e457386b8ae90d2e8a6f935bdeee875cd25ebd36154298e66c07ab7fdc46f91fc153f787db75fd0387ece79b534ccc18345eada8aa4db87ba80b355dac32bd90c0dd00aa7732fab03392af8ac8187e050da43baa6095fa013aa4fa6649bc6e7b9efa0ed4136bfbd8a6de97e0c3adf5045b15dd401c796e339a4ed406131b5d5055ec832cb28355e34528387d6336cfe0e9c25072a4146698d7cef9d2fad445f6b0da06bfa045bae870da69ac17b3462727e047ffb428e2b0b4b234ed2d89962ed1032abc4e5309960112366cfffa6cee9461fb5c99361677b886b6ab0a17622cda853a4bffb441211d02b78cfc01b428566d4325e32467978a1006d76179dbdc115295f12b78f6a7d774c9f167ac956487c333537246008374056ac80d5f4fc93cf6d57a2b3b786cd6744c6a7a20d5b1c6604ff667d74b1c75b308af25d69f576c6c91ff0fad881b7437502f1b7df9a12ce529a7ccadba9cbf0ada8de5c501d961c90e4e505d6189725bbafe8425e45bd102a1921b5dd990129c994edda09c73b632cccf6bf3f7b5f4e7632acc615fc943670bcc1bea2bf8c4928518a89864127ddb10458f76d5e64a8ad45923b6c478ff7d83bd59fec00c0493e5e868bbb112fce8832071efa4e446b3f9283ddaf15e65292348f664dc123c965336e02b54dd8a9413a6d21830db502dcdd35021e0d63986fb6cb551b50a9cd7545f29310cac705e546e3537c2f1bec306256a15f683211a0e1c4be52611dcf98955e9e83a1d0418b30b4d678bdbf74520d96cb430e6dcfabcbcd97ce7edfe591b6c176f90d0a642f3f285f983778cbc3a1786242d339414d553da4a62e7e8e403d99d76152c97d4f536a882c237f1eaa474d9b7c236250436bdb191a5d5fb9108b4d208da05fc91f455fb91d49abbc595419ea41e943ab20127d2eb164d810577d825af6b180e7a71b59121aa8ad90575c77048505fb8e9f29266f96863b0d868d666c7cf51110415e45410f894a103bfb6d158c2a02e3afc6d8ffb73723f51eff0d6cda5edc6b7cc1aa5ac40ac99631e732052f44cd0db45cbe41089a00248a69f87441cc9cc0f83877a5ed2f17b80bdda935bccad3b898c2d5f4a11102ec69d19d6693f4c36bca031f93468d4444caae08b21ef26aefa7e197cae4cace00c8ceb5dcbfc374451a661d0de39c18b4f35511c570b6cdcf306345d87a1974137430bb74589ae9cc781a81b5939a73dfb29adc427454b08cea15a2f85b4c493b800acc80a18427de4de42932fef99334e367bb58f3545c7b392cdf4ffe0c342742e68d9ca8dba81955c01fd92b5ca21c0aa4e1bf311791f61bea7ebac2c03642ab48689e9d19218fe779280b7102d41e7f1e287b6cfb8faf88e8e15dbe7d8a4c26ca42e4e59d5130dd512a90e8dbc72a909a8edfe54cae073a12029ffa3e1377ba59cac67a30071b51e9cf1e92948ec8c2a66e69e9c0676ca14240e8be7c577357d135f50d851cd7d0f5c9b7416a9813cba66dd03ca250d97a2288f6516f8068e519f1095aeaa59c5f2aa26041ba21458fd9dadfaa11003575418e09728c585d5eea7061ec8b8bd5be9482ed7ba37f3c5047bd8f15efe32b01664e404d27ab7741861e8b23a5ae48530369a91a8b79bb42c390218dd34d9d7cc43f267fad1f714f8e385f0f042a1c87930d00b803eec341b09cbcf0ff58343f3cc02df7300709b96050621dbc422d5b72511f3517f1518e51f530f5c55ec6a057fcac224bd5a5e88f99c40fe9b0c45e88497402fa173454d878b377421b9bc3ea23aad74c7c07d80dedff8d45b80e151f6b4f4e261af367696c34d2e613c76e9287361a54ed541bec37b457c09e0475e6feaf9fbd3e21101c833bd6bf7a90747db099cb9d12302afca3c7ffe76b90502cc2751b5a3e7c819a91c26e5cbc4812cdde45345fd8c633417a25cea1700a619ca452f2ffa0f047e106a2c9239a3088ada15e220e9bdf2f03e4fa40f3d516abadcc6a67f5e75ee85ffa332c3d55290de64f77ef3a7f4862278be0d0efde7b640ae96bd1a9e1a0d52693bf450f75465a1ab249c53cc6800bce6b01875c285357b67f36dd457c3cc99d797bd1a17126105da419c63d89d52d9a436ba0a70acf068eaf7bc56eef84b57f877963bb19db88ac3a33e097160fae1b59e532d40a555f928ccd7b349c9e1ea7316dcf52d9751c5f6b916d8f6ebef106f7e19ce5798aab13574fa17e23dfdbeb732fe47ebe672c65dd01acbf15fec8b36887242ae2f041a99ead05795b1a34c8a37718473e1352a152052ecb70be1ceade2b24189f69f7c595ac06b445e275ed35d346d89eb0ab120efee9689253c9ed6395c801b391d357441978d21494fc2d7c88efa50fb3ef18a34c77fb142e8e9b7ab2351a2032299cf1bf339fe070625bf3b0a5145b78b40915a4d565d4f198ead9f911e6024fa17290d19ca0d37ccebf363588874d03f962090e5687d111a7d989cfb994d0cd867c2d32d31284cf06383d3d3c45d38f0da5972d0a8564d753350123a571931b3403d40ade54dc39800e9cd868384b5f6c22c9eb4a95ab227c0854b8f5a57a212190c8fa6e48e8ed070c83999ec2574c7b50bb7c80b3a3ae750f5671213354706953b0b36abeb96b60320181b3384e252dbf296b912f3c93b4265d07561497153b67101105cbb1719c9ea1d0981f663e7882b9aa9e5699b274a321ee7d786897c5853528fa6702a2386a36f88fde0174819142ed60bf85df3ef8f04831fe9a8ec88c3512dd765c889541dee62829e99055db3ddb966b69c8cd4dd80e794b0d7a63feecfeebb0bef3c7c1dd47fdb5edd9897fdb8d6c30f242722e1be9f2637b99101589eb5fa86434825888eb277d3b8881ca4dfc710f06b465b443cb1d1c21b15ba2bdd8c46ad9afedc06f837cb07413ca99a05a3ec3849d5a47ea8bd8f0119ef252364ac50f326df040b31570d004be1b70650aaab981244487b44fdfb2c99d638e5ba750a8bfd35d62aca728f59717188e45ab8a7b6da8a5981e573bc10487836b20bdf32ea565ddaea0f4c0d40db941ce97174b9b8f52b46491f45613d4c9f90697465443b29b2843153c241cb6a0d302b84cbda7e02728bfb09641edb71e1627c2cfffa2dbf08e3369a9758acd4f3ab7f5eb0aa37f74d00aa5b2dd27d8338b9a7764cf789dc35ff941197c8cbb13f7d19fd6785ba639c6cc00ee58dbb138b28aac859c79aaf288d7e586c911d802745101550064d29f0352fd6a434f28ae7f5826d3a6057dfef131042292904f41bfbbe43adb59697907b70f7a924b39b92405fc2590d50df217c85cb76df8077b66bc470ef948d5a0d6d9f8aa209e5b1dc3ebe179ecd282294d3f95edb69faafdeac682caade12e546f9c7fd112f0721060c03f07a4334c2226b54846fd71ed42174f168d34fea114c4934d1cf2ab244434e1c72c6c8494b3956b1161c30cf5d3ddf97dc51748c7fc8d73a7bd3377c6f025cf6d27a64bdb23a40f103cf841dfe57204d495eebcc573e006b2dcc546603b0f93d4b48f74182b91cf859d72cdfbd369ca95d40c48d8aa1bbf122a37924261ba14a01b9887ce9dbedbd77191ff381546776d5a63e69243cd74c93a00178ab22e40f2fcc8fe6595d2e034d82b2b72a063f904cd2de1434e41a8720350646df47553520cd64fa49718e6a75e34ce696d2d825473bbbf6a4ca1af38f42c1a17689a5a5687da69e8f34a2ff59a9e7f02c1a6ac2dd44cda08cca362f9d99350e4a0d1521f2be154fbde064cd10d5c074199953c3615fd59e009654704b9987bf4f77785c345f97ccf8ba8a85d751520b1b1f6f3dbe1e8dd25a303602060ef8e93833538d6676329dafdb77a2ff2ae8907be05896cc7cc3b6b4315f7eb5f01537920f817621a56e6f2e160e87100d5a19f798f419a7a21215e811c52d7dc1c70448288e77a941cba19528ab324040b903c135596fb66cfe23f67282dfeef970941838d57ecf241b08ed239e5bea715d2ddbba4c1318471ecd80d8f298f8ad9c01095b736ccf63c736db62cd03d0f3a4228cace74a579e0bac42b3874d33b9b7d7f629527ccf35366c86337eed9261b1ebeb71bcd2609b6ca7dfc8fa289a50beacb8378d059de2e8d5d83dd5fa58600a015fd3a368e373c83f0157f9e5dc15a7367295a1f54960a15aff1c6dbe6f33b82a374c365f66dc5e80a1be42eeb0fe3609bc3e5a435d3f1db9373364581cf79ea3cb75a237551de8f9140617458ddd4ce629aec49fa2fccc1c5fbef0da1d21c1c7529838738ed3df0ac9e5fa1473e6d8b57064c5cf2eb46e4e9f797b87d26113b2eeab2a10f12e3e09ad9c65ab19a6359ed0f9d461ef03c05e92a643eeeec1a765d5ea492a1af780b46f58a72a1673ae25393695962f95b77b8b0356fc911c3a5017e89f6d0f48e565c5e4927bcc395c5ca7920667390362bd0ef9951bdc92bc174c2e06dfdd57630dec7c4e71424df0104845e554144a1b6f94c8d8ed5b0b7c2a0f7710c277829b218bcea729ba916366d7549236dcb354a2f4d142708d6b8d56a9900718eead5d8f2e415b9b0b867181b23871ff3998e4875fed9b9c1b8cb26be4b390da72b67cc1ba6c1a827f6a41339e2ebb9a2ef98258979d4e050c2de2a5b0bb1acda1a833ec50dfd847a392cd5600b1f966105c26a186e62ac01fe232bdb5b8a6b151310b4f4adc6d5976b77111f6201e51e625ce9001fe4a24dfd22ac97be93734a131b7d8c401c3ef7a616b68bed3b65f6ee887a54e71273e1306a224a19d487be910cc44f8eda12d897e76ee168ae72f205b0df20c91d8a19b85bc246ff7e7499b3bc7219165a3e02d0f5baaa118d8d779b50f726f7e7000a4ec58e4f676c2aef4280791678595269e5844428f5e204e952ad00f45c6fec6ee7d7a0fe3fe5505b70a33b6a68cfebf1e003485d16c5e9f5461b3a3c1ab924e48823ea317ca8e1f7c4fcef3e52e132be355ffee330a358900c97812e699bfbc1e1d489f4010ac39a0c9f41b84e0f95159809cf790f436534d38d809d37046a9e3332360149395da11c67fd4334353189888891926d9b0da98e4d715018bd023d416661c5c24ede02dab4098d4e7c01124e9e0b1088559187d2e159599abb4b53dcceded091c55ec0a3c68cccb46326b7a529f6f9f3e230a51595c71c19681a78ad230a87247826c33fe1b4b51042c039d048508583634e98a1281cf5e6646abee92947d1df51d47b011345a7b599ad6a377c25299f8146b674610d10a5b40e61330560e345d9e6b7092e4cbb6344c84b5d718f86ef939f68f282c6425c01118c54e78feb734001e847a2d5c18e58868d32009d09fb93bb9d2585b6022da0dee4194a6bced0bc74295b9415214b0ea9a6b42389cc5f35f687834bd772e7cd809063f64f5be16b487ff7d6cfb8a0614250d7dc9c64c1fbb23923a5d882af013024a580bfcfb2f307ea3aa3dc763ea5bc1e86382dd6ef2393f6347fd3500f458130bf1c627ec875ea1ba1cf3a065bd2b778c3b1e5968087dd021b56d6842ed3743325a662f620b69ef35e3ba4d62cbea685078e8998298e82a878865ecd472b1171d0eac192a83c579edd7df7781f6a9150f3be060ae5ddc78ce47a8f4e1f925eba57103701320491f429d43ffff50fde0d701ea73d302835fb73e103a95e3a0f005bfe4c95d9299f42dc1ee47943d4160ec924842e49316e43888dba506661c27a23b7d532bf34ef00f49fb18042fece45f0b84bcd0d48116627f3bb10d08d6833a8f01b2bcc3803a388a3b3037cdf9d585e1da581518bbabae5cce6319ff38263f736424bd2f31226ed2236b28b477ebdd434c92ad79de6c16abe9fab0d296baf358f8b6f3e5afd2e2ad1e8dcf600ba6588746331369f6693f68c9d6eda034f2f5bd54e340f25a367a565db349d57a549cebac48d2bde268520448fa19774d3378cbc82ba8206ebfc34837c7c234ef5176995ddbece9b7c533aab3da80d63585323ef0bf8701bcc25a4e7a3816d0d03f4f85840a27686fac92a2384e048f169eb8aa9453c9c91ca32740eacb7be2430d4f62190381f01d337a22be2f6e89592a20746fe68ffc80c083837ad82ea331cac5a234753883b1d1841b5b0aa4239f223ff7f9d2f3d4d1a9cfd6fe0da627b4fd42d32589f93933c9ffb809e152f2f5c46ab68dce011b81cfe6f48c3d13d973163ceabe2aa7e784a2a113dd53ef94fb177eca41f49cf0a64bb9da8b10f6c2f0ad757d535518e043127c558932774a3e94dceedc490de5ae64c3265d26b9a764870a89d558436d0578b546ee399b00c9ad4704613cef102b6ea2261a598790756b0a222c6a509c02145d2c9cc98d9bfb7727f7afa822a9b0103db4f333bf53429644da627de08e687f7a6fd7c497bc3f9c7e3fcf75a1e3b23839467f9ed63344948263460cd4bc61810938dc34423ffa0bc4ecf2876015d51dc9cc667b0df3bad497cf82a5d4738cfc90322581154bdde09b44e636d1c6c78b359f3338f7cb5d2edbd32b95d8f26bedb899d20be6bcdf4836bea19a305ef174e77cdb69cb3a447fe6bc5a21784b2e4127401283e871387d0dc4a5fe457937b662b512314898d785e6698313eea588d9c10fc66b36bec5ac4481bd340216aca5859585cfd09127ca8fbe82356ef09510dec4887699c587633bc1620c4a3d1fda69fc65db7286dc8ae492d017e31f6111f62bdec6745ee6ffadabdcd531fd0f4c60dfac0761e3afd40819a11404f0afe6ce7e9ec2e68c7528e8402d914b0dee8eba0729cc25a8a8ed9606970ccd0ec4831fb5a6a00434d2c00d79d8fe343e415cf87290b8c2259efb9a313356375dd1a0cae09c3d47ce0eeef683ee5c0fbef746f3c95ab241ed3fb21b7eb22edc295876ad7608cfe045a39ae2abd72e22577b5f26a23aecfa138be4ac1136f557215cd8d588e5e72cd26f7b7fe3225c832cb79d543055abe410fca33807b4b3030a9f1a885b308e424e073e567aed2248d05d56237a8d08903b1823e00d2581f168f4017791a7d59e3cf01ecd05986bbc643580468efc10a0a30e83989ae8790c34c12bbf061578de17a4bbd7b4511eb93a7e814fb424d484c8bde5de52ca24532108500880083a38d0bd93fbd4972f5518958ed4fa14fb25238c936fed0d27d73f7ef7c5fc9252caedbb872ce58734f7d34da5c545a53e859358304a2bc46230159e62aa6c2abfe1e4468fb22f2e092979debd1bde6484128d74b847e44fadf7ca9410c7aef9147729dc487163f7d4709bce69d78d3ff63ba7fbf026f76d7d503fe4b2cdfdd30950e4c1500fc60f3a0c147778c5b847f7801d0c85d33deaa37ec41d8ec2b54727278ebd022d0698291094748ffd8a3c987dd457f430fb403fe20747e20e7fdc75966cb91408b6934ebbaeebae9b9dca0235d34fd23dfeb5492c2212c61832c5491c8c4eba8a78c3c98d798b3164776185ec39348f4b19b2bf63a0239882ac7b6ea2b8f6a353ec774a488c65691f89e469343e72ca2cb3d8d3c6181bc8142dce50b97d4a4f1940832c719cfd70a9bf94b9cf94529cc7c1c4f8b2a8c8c176d5146d48846938c912589c49914712875116694b55774facc2864c906c1f8293abe990dd20dd63d2a7828b003c5d3237f7aaa87bf8783715cd2425fd1824288a86632c89834142780a00179f82bbe120c923bf242ed0ab95e2af7ab03d01a48680d2aa3bfab70b01b3bfdb006ed1d224f79626d2640d726f49224c7eca9f7b8b9659b612a5bd0f1dee4b2b5134eabb42f0dfde7ffbfaf37b43d12fe6989a4fb7ed2745406707e1eb54083cd9ff73d107f37c0dbb2816028230d8ded9a205494b946c236ad33eeee3f7dd78b9fdaef820b8e283797b2d622160debebea5dff69bf6d1a5fdf6d4a53d9867eec7e1fc5c202089c56229e49b272efdfeb0ba42f04cdf021208b5a738ac5f71091a76694f7fc32e0d0b01337d30530c3ad85776b1736aefa04ba9e1de72e64bdee99ef6bc5ab1c3d03e71ca2007fbfd29585f3fa77b9ebf3b8d120448d0a34e084e864056b9103481182b314e969b52d020db2904ea2ceeafa59e05392d9151b9994c50447d8112507777bfc0f7a69452768e2f130a3b1b63031b9b642f452cfe8352ca12a9f8cfed8f56805a098a51ee8ee506ff8b3161727794491fd5941662b91fc9452188fb20c1c2d20b3858335a5e682f1cb01282262ea5944b34f990b012e7873b0bfe82329ce90566dc9d4ee93e521891fd5b289752ca0ef294a28769b8867b28b829f982b552caf872cbda74ff3e706f8cb8f1dda59c92ca8c7ee1cc7236e79c73ce39e7b49e573f534a298d4579d2a4dc4fe7944f9f52cfc08c22e5f93745dcf8d30845cafd71ce396717c3acdc36e7e49834cf1cd3ac584ef9d346d6e6d4a636351c0ed87e90996a13c758a454c45423f78211e5baafd1d0b7b28617e74b26d373f7b5eed2a54170b869cc3d744168b67e36b2a669da951e8a6215af92aa14b95442b12689b498166be23fb6892d0a53b4a4bd46b929d669ec41bf935a892b9da58e14a8256e6042ccafd70080b46fe7adfc62eebe40c1acdb3d7807cad47dd164f64893c91f7ba4c98e8e8e8e8e5654303551b926c433b24ef6f00ed463cfa99278464d8af3f8009cb8a13da2e23f968a3d428db147ad2c31ca412d57daa3eef13f3aea1e9d2f1287c3fc4e6a60e407790ed97e53b0524a29a594dddd2de5f4da3c758b0db687554a29e9e790e2c6eeeea6b81f8339a7ca861be9519e734e106ccca6dc9c10546a9cb5cfc9b79de53a1b9dd8d88c71c63aa7ad486ab4d65a28248e55cbb6e3bc561b39c662b19846439745db3a14eabbed5116d55914d79f6b91ec6a28702d5073f50ca05028d4131b8e5a46d976ea9fc43568ce311d750fd954e9152aba24c9e33e5dc7a541ff2e49fe74b3cec9412eedd365d1a0bf276dd2ca14102122ce001a910af10c587d162a8cebca38b94f87268bf6e9ca882d348f93dec0bf43e33e31e66019ed135568b0e36207617c1269f0c03f16bdada19d5827d9df2d0ed9df35230b45fed822798be44fc3668db98f8d3938dda733179df44f0bfdf353bc4d5351fc69b9e6f072e3bfaed8c854c6dec66c2c081aa18dc5f00ac9c6dcc662bbcac66cccc66e8c2381edcfc9cd14850c3772337d79f22f39be955f8a9832ac72337d999231c8cdf4a568d29b9b698626d7c8cd340ba379b9996659663c6c4e747233cd62c80f46f61144ad50ec9075723341d184ab7141be11e68b32dc66f2d225a7723379c19269e466f2a2438e1f93170fa05cb9999e4093e3178b2e8ddc4c4fa020c729b3b77b58c084ed8c33d0e410498e2fdbf2d40ec6fec1a4fe50f7f5fb2b4e52a531f749ea3371c1f64ed8fd5c90aeabcf6125dc57eeabc5dd374cdb628c542976ec962f785c3c2e1e178f8bc7058790bd5f5b213ab9fde54a979cfbcb95a5dc5fae2465cffde50a11b9cbfde58a0f393eaa1760e5f74ed8c96e7eb8ddcd45cf9e33ced83975f3c39d3ff1043200262ed8dd23a1740f9bb9f841ec1e37d99f1a758f76027950c91efac8ce546318a3b7c480e505d44a321f99fb84338afbd024b2978042915f27d3b4d85696de5f8184b8f2dd486b3e4d01a159f2ec2672a6f3fd152900f20efe7278e5cc32e7d7ba6dbdd536d2ca8d42d5cfa98c90d756924baf18431db95bfba2561a98b62d284fed5faf577cc1f64e18631a7daf4cff08491ebfb7fdf260715b68f67abd5e454250ab91dccab3465cdbfb7985b8d3482bd39e4d6ff3b943c5d541774e6e5293af1cb3ebdbbb77fd3afbcc55a2720c75e4d8b4ca769c2a48e67a3e7864d7b1d33c8ebd9d1c7adc87ad3c67ff6cbf933bdccd61afd8375cc37d76b2cf3bb8d34ed3670a64833a74b8efe898e18231ca95dff21abee33c6e75af39bcd3676df92d9f726e613d46ef183be764f5fe393f774da3dbac92f32eceeeb8b6aa99aa3980dd53e7ecffba5d2e5a15697ae8a89820c061441966c600f38297192ad10823b06180c8968711b6175c210133319429c11815a830c410d3c8227dad514f3a758c53e761e02bbbf7f65effbceebbfc7f7fa53075273776e7e1e04e8e31de98c325b91d0b6bbb7d7aba4bb0672fa8c1411b355c3aa358dbee137239de441e5f7670ce39512d259538937a761cad46895aca19669832be9c728b2fe39c6106319a9db3c1f9ad4d8d6edb91d47711ff597d7719f7c1d16fd07e770f0d762c4f7ff0a36a7367f5dde6b1b6b35db5b6b3f52307fe0085748fffdca65677c6545c7768d85240b4df5206e8eebec15ae7bf6eada9ef07ea5a8ba4ea565b13c1c121e7f10fa177f88fc92e2448e51ae5ad9d1c24489c9d5b236a3f8d61c54886cd08fd458e5f638c4b35f29cb347991164597ffcc6270982486b5b197148c4c60beefcabd113688ed808e88ec32a6eb7e39203182a4e61c820c64691134838c91205164b5409252faa382b065ba0608b24bc5a95c400c5191e5062054ce860797861cb152284b0228bdbf74809893cc7dcf0f3162ef864ffae148bcedd7e3cbee87283e6712d05e484ede531b51203f05754a1e91e404dd23eeaf5f28f413c9cc7dff10b8bebf3ebd3ef0414aea51cd7494e4a89c19deef1d73e00f40ee701f4fad54c13c1b797fd43f9839abd146f5070ffb9b72688e0e07c950eb773fded6790ee996fc38c5b234f073b83196e5833e8a07f5060fcf91af48ef6f108824d7d760f9bfe41f58e5eb50f7d9d1963d4689b71698e292042441d6e79532ea12a73ef4ce9348dd591c50d513a9adcf0beb461c69531041bd9714561596d5e6ece5b6ef4e40b63764a29ddde45377cc290f73997629702703cce0ff95e072bd1f9ef71b0922a433ad853928393d486e1541ceffbedb7bff2e7bfb3f90d874640eb9f43f19791d17f2e211c44f2788f5ddb7bdf30ecdab0ab013a9ff34370dec34aaae060d73624e7693c0dac440757e9b04b48e71252f3f46b9e7e0e762900e7bd776d7808ce7bae213a9ff33a3898c03e4c83c6fd242ed4bbecbb6860d7ea5ddbd3b878083f8ec78193b850f884a1ff1b4116bb1480e35d3f04c7bbb0922a43aeff5ec90d9c0448f2c87fdc34e4fbffc74a5c98c887b16b854f18723d7ef9183c8d1f82c1d3a0819380436cbceb5d58090d4c021358d84bf84320d7378c46d7f4dfb01b1f82dfb0c7555c42e86fbfe1126a9e7ee7b0cb6416fe6efc7f351ec787c17f9f8d8f299710ce0fd9799d0d0b794f640757a19ff33a007826301c1c9c9d07004e82f33bb84af73a5f922a433a8f833f1caf040723f95ee777de070e3b5807bf2b8c45381e7f03be77619710c61f61ff1f61bff34c60dee7dab04bc8f35cbf839378efc2556ae8e7a29ff325a93294f31ef6de0606afc4c3118a8dcf79d7fbc02107fbb8e1de781a8fc1dbc0e06f7c036c3c0dec12ba71e323ac468d8f3911a6b37d110bfd625212a58e2900e61252bf4abd7c7f825aadeebdf7de55e8e5d56ab55acd94664b1e0ececc4efcde4dbd7f2954eaed27e5af56dc0a07ee6cbedfbebec5e0c6ef34fd8a579202602ea17b0b3209997cadcfb5baf96e1ee7876e7070922a2b3cd45abdf70d6b7d12d70abb1490f3383f44e73d8c83697c0e1358953f396fdf7d566f7fc77d5457fe786f697cce1371e188e63b75c290eb69e024b1cc021a163def23ccfb86d1f830a2a181abb884a04a583d0ae7c35886fe7c07fbbb38dd7b6804447d2eefec4d1c949acbe457e952ca8da374d39e52eda946a30d33eecce14c85a0fd86ab839a4fec4e354c58ae83b4bd95288fdb5b9adb8619d73555e572f36cbf7deea06ddb5e25e692b94482ba47e7ed5745db07913ffed569dba47c0fda2a0eea9eede6ed3f21c9b3bdfc88b4fce787f36cbfbdf2066a1f4735cd4e6d7635e6fa7282b97efd5e46b7fe9612e2ca2fdc0e6e5feb6b0be2e0f65b06340811d19f2ba7ed8b4b7eea55bffa5c0a48bdea5d12bb24eaed6321d5db6f980f22b2c8f7dd709790ab1fb2bffaee57ab4f927afb4c607665edab7e88bd778595dcb7b8e50943aabf38494b1eee1be612b2ef5a40afde3524c2561f612bdc30d5875785abb884d47630fcce0db358d24d87831694238034345115e02419292971d0c1d6010737543850cad2847444054a08b2f468a0430002f01b2d621c70d081009a30003eec2e59caa62ca594f2c9f5619ba1f161a3d1f9b0d364f0618c791f46229e8f4db27c19a1d87c18a364c97a29ffc36805f5614cb21f462cddc72e597e7dfd8e0f2ff801f8d0cb353cf8f03bf810e4f1e12b73f01bbcc4a191d894a59472a50c721effb8c1eb43edf8c0af1580cffb6a7c227c38bea18fc8f75f91efc647428defc8870292245f8c795f248a4546506214998352e291ca4ad217b138a8f4c52c0efa77d13e0fbe771e7f1edfcb79fc3bf8da7938f8da794220da237b5e003a7ab04bc7d7ac4387069f0d544e3985830ff29b30321995c926079f81c753e43e32070070e0ef31c8b9612366711fd7471a1f3f7689b30826da382696895fb4cc4434ee93c67da813ad3ebe3771a39adf3e3e09f2c7838f8f44fe74f03e3ce2db0cbe21222338181f0cc0179f832f36719ef839a044914939a28294f3452b495894b22cc52e71d614c338e814c794f9a2f5c5279b4935d5eab26a3cf848903cf13bf890489ef83e5f4b1e1e31d6d9e76b98075fc3787c0debe06b588eafb30dcf385fcc3731dbf855755fc74cbf1e93e7a765f9d5ccc1d7302b3b924989eeee425ba69449e1ba4cd6aacdb09db3158123ee238974601a6305d3982653ba1265b2817f5978943b927e6defbe5e418288c8294292c7232c07971b3526d1231aaf7be69c73ca22e9ede81eff1d1146e34b1e6fcb8e86edd8d13dfa153d2a51bd6bba5a6e263142283189f961678529022766871eae38838b3b9cd8b803052e68453b343aa552d08f2022b818195260c60606ab7be6ac45dc2b17653dd59e77e57a57aee78397a4e3bd2410f4d1835d80c354df6e69f864f93187feb93d049165bc92e5531d1db8f1b58f1f38ac99d5b2c1a19759ac8f29202c292e731fd607b1bc224feec37a7924070907e906a985648354837491a454bd4c01a961fdfd6ddbb6d0336bdb3ec8afcdfd5c6373258bc5fa22ac977f84c57acf2907e33cdd3cb1bef5c47a9b27d6d73cb1b0eaef53cd62a6b70a16ddf05bd7f379f9739dc75fbe6b888be33a67e6d7d47cb7be2deb6b7d4a6b5d92595f5bb45573536badb5d61b14abcecea8473deb67b579940d6e7dfdc23f8202922433d71a246d4c4092595f83dc842db35ed69aef54ad6155160bc59a125bf22121118355fa141b9139647d0598d44f000b537c1df49a9b9b9b9b5a93ead7bb699b36578ffaeabc2c6a436b28adb53e576d6a58b5dae0e0d4e0fe61b1587f63f3310542fd16ced7af6ddedab46c5a15e76f5a3838363895561a83162d20a20a2e6a1856ac68a2c51058cc9081a9eac449a56a77d3b2a961d1ba62a1501595690d65d5505cc2eaedcf1408abb741ab5a6b7d9ceea66553539f55f1eae9f6aa2f82b15f6864a35de538ae260dc9ba7d5e8d470de1a5309f8594e5f7e7b9d54e79dbb62379db5a6f6f53f7f820872e254ba94aba31cfb735d8e6b72ffc2328204932eb56f375b6f94c40926fcdf783bcc6842ddf9f9f6b88f75374feb3db7ddfeefbbd1f5be2bdf7d6fb6c4c0d793856659b3fb11199ef08f76bd9d6d79243ce23bff509391825c871158fe2a01bb991ac3572b8210a34fb7050f2e895eaedb77d9cf9701ff9ab2ab7e7e1c341f9af26777b201f46f2a75e961bc93133300eca8d08b4e27a918cd5bc7c1fe33e377ff3f2adedd4d6dbb77ee270fb9ab7f920c9fa6ea44d8dcd76f32d9b9bb73735377896dab6165c07258da51bba9153711f494612155378f0c18525c4c0361558b9a2090d3790e00b2e60a90df571168dea0dbd97af6c34371c1ae9d94c01b9f3adb5d6dac9da5898023dcb1397a0faee670a04d577dfa9ad673f5c48deb4b0b4c13ddb9e85c397eae74cfbd417ba94308201e58f1096281efe1361f2cbb892ca7dc28ca51cba51962f3484f3dbe6d5f8b672fd430f29378e2593898de328711bc852450e58be94c9e20b9315c0c052855316573851a14b1a6e051c2b30d1650c8e0924a66c3f1c3f71349978820a8e9701c443e58e5039ec31ee454ccdcd444696dc31b1a0082bc83892e286306e2a3713193284c04c1a6e298c29cc381d516fca8b2e62e88890a1bc30c113ce8a91f5e2896ae5a9f3422986cd082a4658e16e112f684328552f46408790499eea4514198a58f2a289111168bc883529e2a88b26f105f4e20144c44cf274eda2298a114e669a5e3c807c7c0b4a55a2626e2b37139318459069ba282ab4242eea892992c8615d820908ce072699970e38c1445481dacc10994b14b6b5a6eeee7ea5c45eeef92ddde9a4de6d37957bd7011b5d6a4026a5da46615b95dac3bcaece711daa6b77d776b35bdd54fd46d8d0dd638cee2ee79c94d2f8797b7b03bd248fbbcb962db7f879dea2478f9eafe4e997ae9131e79c336eae7577cf996396438a1c33a7af38e7cc21c59c735e22daa7cb9f94d2eea6ddada1b6ca7576ebee4aa5942d4b29a572dacad52dc82c492997a4944b332c492997a4944b50ccb024a55c220305d78292c6551466ea4835424299ba45a088536fa9a68e0e09cdec741f118238c83d2abf6a6424958b4f3aae52e122941f3515e43ebef3aa952c3b1428dff5a6d43aab731bf0e6bd95c5fa59a75c19f5835145184a78666a9e80f07218a930f1813886284d8e4bb8512c16e3c06c053d38187f32b38a32e601d9244b598e2f8f2415568c4c666439dac8a61c5f3ac931f28b1ce553a3f1d1c40bce787186480c0ba207883e8082a62d4e1cb10516c6a45806a80320404a42aa41082b786608c2093324b862690c2cf4236f92465942914b72966d6cbe77c83636367fc77022db7c4d0a888d1763b15cf3ac14909ac94222b3fea680b0769c608918ecde5fa580dc5b23af5610435ebd2a056455555f3b9502a2ba29314690538f4a0149a13ea258513851b9dc7dec9f1e366c39528ac13a96971cd6c89c96cc717fbb64eec9062615b0f0379690e5ba854145de5e4b01d91a8a186259a3df60789129ad1b510c0c199a6c13afd38c31fefcf833fe8d7902d1f24c89c024a4399e51ebec6003428cb1458c1061a43982e6072bba48628b0e4f2d3cb1840bab1e1cee84c54c26fb4f5a99ac004412134f889278c2836bb1610c1345b327623e9822683aa3868cfb4513def3c41753f2cca2847d7971a656e9265ae7faf46f51f768a64c5f470db752ede9d3d701c3ad5f44d2f7ba875144924f299aa34c29a5342ab956df59f5a8a8d43df4555adc8b591f956ecb41bae3044bf8534ffe44a5e6a14fb19745895cb1bcd1e46e4ddcaf133d23c61cae0e2c1715a3c7182b875df563ae381645295d52d618638c31c618638c2fabc718a3bb7b8d317efd2e9aebe27e622dd238a3b7525432010739dcf0d5c4158afffd4528b148f2107d6d1b22b88c3ee84713077d621cfdbf21eee730ca5c69c3b2279018935b49a9956690fca9d1b9b7bc900595cb7451f48f1e65deeed35b8e54c8ce23fb77d1dcb0d656c6c29ee0b1dcdfbd6502ab3fc4d5fa1b4e22bf26a9cf71cf64eb9eebafb3679e2b377c6525dccbeffe1544fe44d8b7f6f55ded1aa2b2c3aec643dad7af0db312f5aa40f2677bf94338ee25275f09ea6129cfc1eee5f792a77b30ee17913fafe6e99e85c37a7158ad4c75d2d58f42ccaed40f6dcfbdea63ae9fab5f491594922a2a3c34bfbe92edb9979fabb12b8587b8dfb0abff66180a0fd59faf646257f74092a7fb86d55761b92efbdb37cc65b71fc2fd565fc97cee875425f26bf8ca5d874b88b1285a145d68a91cb8aadc4c5d247581d44551b76961668609c549d3220c1459801a9ca81652b8e04ef3554b30437c01bd7800d950648391f3740eebd5e2850240310aa18bd88b07900f67e2a2c9ce300bd222b589220aceccbd2141b744133f98e076b9999a90c24213b210b898e1e4665a0a83898b224a2995ef78767b9d53a33495dbb420242f1272fe56efbe07d6d96d924124eb8bb09aef6b9ef939738304648d4a8baa936ada5bb7399a877e4b00f4fa1c60a3328baa391a602bed1e4a27b5aab717d5748976a133ad8cf685ffe47cbf664643133f7041f750fdcdb7d6a18694fbb532dac736e8bf6a9be4f44ddeec2ee57e2dc9bfabd793b54674d32099c9fdf40bffe9b2c8dd75a163ca50a7ebb69f82f11fab2479505fcc5649fe5029ce43c5a6a88c0985a3ed54b8bbc766d4c7e9203d92b2a7d668018d81159bf311b1f0309ccfc9399fd61ea0beb340ee3edab712d3ee417dc35019c8ddc7c7f9627b807a5487fadca8aefbee49fe58db3dd9eec976360c681efbadef8a6cdf1a59236be4df646e885330f121d7e7d13db661ed41b6426abe9de6b1cffa2c9585ddac9be5b622e4fa91ca507fb16c85c3abc261cd3b2380b6fb9ed926e1be59f1dc706f50821bdf1d49fe502aced3257e4073e81d5996b0286511745db25399171995514a3d4f3bd36183ab8a1eac78200726ba50c14b0725b6b0e20b134d4e8841868ad1c5479f98620917829891c8e9d38c1245e0061bbe20a2881f7430ba00194178e940161e7c80f274457d22738376712fae0a80903331926ac4d8e2cafeb404a7a4ddc04969c92cc450a18a01c3942a2c5144457b222245b988e1843221c613b901ae8a90c6071c227712e270682a48b3430e7b960408aa0d977b52af6892ed935b7333a521f252c60baac752948bd62ae4b0d144a208a5c39814635344c34404b22c8ad0c2820e52b840258b165c999d49f20116c6a3ec1129c63096894f536c7203618ee02226538a4e61c49cc8e1cb89c3610b039aa082055ca6884208a3d88216379841c511175b8ce8448046bbe169082964451c2105091e6ea002083090b8dc40061874054c4c2891a632010227de27d0c20c5416296437452ac40d7ba9676514f909ca78a10c18b27f7542e56672419a23a4dc9f54c3ef3238a44fe96b956a5a8d54a47c52a3c42671de9e4040794e1c6a9a9cb9b539254827add5f3a4e3ea4cb425d1390ed8963f6ee54914295424981ae68ef1be783361a7e1b85706aa1ba594524aa9369f0a0a0985c325b32560ac46a7c66974528dce8f60d688e449378dce89238c09b7c45db3217647799227aa67404d5e91084cf366bc2fee983060bc25a50a74a3ece608f4f42a139572777c5a6214a944a42533af23952560b2b41b10d0eb0582ff4a4d2c0e9798e9be20de9575974cad282929292d255122597e8c4e9da39cde5b52c5df99b88652af7a550abb866c15ee6de8e5d85cc07aa31ae540872be35eaac45cff20f2a7b3400ad3a5883288d83206163f486e00c30da35134721fe772ecde360ad40146a389644346b4752077266229b5c08b15309900b999d074c9f1a312535c9a92cc557deaa510910afbf7c3052228fb1d914136118486462812012982d00649378c46d4e80995f056aff588ed8b455f247230d614cb42fd94a3947224a913397eb4d23d3a17b9cbef8e1f8d96bc5801a68b113258c39c5458d8de09a92ccb9f645c32d7a9cc03ed080d0e39ac76039a984b7333a1b1c14b0d6dea992ec2c4ce1ce14298d9193145561c9d9922862bd09c61ba22e90c132c5c114b01b5846c89871cb612154b34dcb0d402134eb8208514b895b56f7bdd318c7e828eee1248d720a3774b29bd728063c519664a29a594b24894944ac9c21299195d4a94e7fe58ddea9f6d7b11119fda7a21020fcc004408b040220cc194278638aaa1052c3868e1090ace00a20b3136d0c4951d9ce4a0458b0b460073bf7ea596ecb315b2ff0f77f7e8c50ed31916bc3a59378acb534a2aa594b27bff20a746504e700d532387d52add3fbcc9347e3735a4e41e3561f4b340cd73e2896bf7c40ec3343051768bc4289efdfb696b27575208fceaa8886c41966264c9c50cb7be9350c441a92273519ffa224e4e4072ffbc0cea1ef34749e986933ba34ba99a62df53431c66bffba28c84ee2932ca1388fd64d982170bb4b8c88f2041db0fd68783fddb536ddb288e76a3db06e460fbd02007a5b669da576d52ba6d35d28ab5df34cd37ac69af69400ef68fbf7657feb5a6d1ed67d74d73dc92a797b85717ec266595a0a50d6a5aa5786a409b5993f1b92fc2ec8733c8f43940c00366ec1eca692bcdbb1b3448db0321597e7c7dcd43bf9ffbe8dc2001593a963f12164b68250dc9c91567de8e7b8ddf0912f4038acbe5663a7304d3b5b999ce0031353a7d263444648433b29ccacd74c603397c398160039f1c63c78e03c85187ecfe1112dc473efd58eff5bc078db8fc99648f0f829e279fc62ca38c3246ea30b90009a3f8e55f12b73aa84194bb9a7de9ee6e29a594ddaedc34bc9c8c93db06f5614dea43964dcdb33e4cdd0f55ab0f5759f5e1b5c18f627d1fee1effd587a37b00d03dfea82f47f7711fcf9741f7f86f9f06ddd33d9f00768040fee517d59d496c290a12d460c54bec03b01838808505506c61461064d823a6f8c18a253198a88075961aaa90a14b129414b4a0d16b227b162f6160c0a1a18f3c6171762be4e92c60ca527eac5cce64ffe95f4c0da5a1e8779cc532566f7e90e49195a41817749f9b6f3b0f2b20febd83b443237e293febf81ded63fc904796467a96a3747f726760bc6ddbb66d6bd9bbdd223a3f85060d1dbc44e775deeae844c0f39aeebd37f7400ad344cb182b8cd902968220ac08430b2e538821034bdd269f7b737efb7a732a12368e83c59db50d8bb5dddf6cbe5320449b8bab8335373aadd68daaf5418e48f79b75713eac99157144aadf5db36d3f5340b66865db3614620602d43dfeaf577deb6fbea686a5aab997c562b1582c16ebb654ad0ff2db7ab96d97f5f170d0b7564b67de0cdddc74531011743e148e605142b2622509294b061da94c10a411869e3820dcb6dd3c4b4a330e8ec0aab9deb5b94897f543ac6f049601888c10ab9e0d520d928c49a227332c2947eed322ca79c9fac2cfac9a5b736b588ff31787ac6f7d4c81c0fa9b9c677d9de70deb3b75c3ca799c9b9c9c560e1212eb5a61255dec481e4342b282d405098bfff418425c21c3095478228a3130af4286226008428b23601e46caf271be865583657d13a200a874ad3765b174886a060000001315002020100c07040261402c188d144afc14800d759c3c745e341709a320c6619441c6204308008010200044666868c6012a9973402ef8003624399e6e793a3a96eb8f018f6db63e43ba85ad79abb41aee0d27a280265c31b8695aa0ff4ef4572ec0b07f6c2698a16d812663327f6ff805202b0425804d189912f08b2759cc9c17b19b1ff19c96e413b94d8961bddadf550c0bc50099d4120859454964c3296f470f43733b372f69a11d1b4729f3189c925ba87a88e50e0793d9afa07a5943c89622b309ed2f6fcaa26bd8bbb5972b9661e49f07b6a1143422c1a65ab6dbf5c3800b3636512e67bbfa6f3775ad8f313765e442abbdb61837f024f27759c4af6ce318e63be0ee7342e2420fa06649e332246ec4ad961bdb953353d2c067341d7ba9972af0bae62c0b5a3a7b0588c42c0c8acfd2a3249c3a1dfbe09b40ea6703bc75c8b6fe0182a32fc62c7bffbcc721e43f2a00d985ae804befafacd6d3e0ecc33cae3999f0d36cd757636cb43ada3d392bf1dac50f4a61999802e13a78e08b858911719c809edfcc4b12eddc08c3252e9f24c707397c2f048861ca6a13e5e498658d5bf2172482c45c308da16b103dcaaa4631ed95a2c5eb3cb68bdeb4b7abf7e0b1c8c0ae95d216560afd9926dbeab2eabcee22ad27faf54e25d692cc6bfb252df44ba171d5abb9207b01269ab72a045d26339a8275a8722a7ef346fcc72d408341c0d60864acf886007ec0472add5c98f0df8737328cb6ea42051867d69651339b6c3a54cd02402a6ce31af55c108cc339bc2c9ffe7d1740749467d4912839a8ac3b3089828a9b8e627fc91538e92c8ede801cb8f472c75a7f93f7d3a88160c31f7e015c567b44615e37b29b01a966aa4363d7abd8e4bb52f46c0b9975070f98e75d80f67542e59723d5db3083748ae01b4974d2d9dd36204dae6bb861b6562a3c4d72f90ae2d20be459fe81e605c01f10a5c5f2f63e2a2fd0189a5ac59e54e3c87965e609a8d25693523a8274e6c749c02513de88ace66d6db6712ae4dc8ceaaa5d6e4022dd0f19bb005616db2f235f378f5c564e7b5eed114025f501b9a8edb37a6fd67b9dd539652a68ff8f775dffe9db4d4f3e3e90abe247ca56128b02424333105d797a9625c1312ae943d1563326a566b6a9809d6435258115ed7afd7e0560e178266c94edcb9eb250e8c580bff8fb133e2573e0b41452896aa016d67cdb989908578f18415f14eca787ae2e19070db89a415cbc87b16c98f48aad38cd743890889f8f8f819bf4a05fbaf850c9cdf736602a312e72bffc935c7e339e6b01beabe13d08b85f5cf0a45bfa1f27ed5110e4c4a6ea608e8fbe44f647a88b1efafc5f7f34c879ea8804a0feecbf05578c612ea7ae612ebfb97f21586b482d304a2045abf65d6260b03cd0e6abe810107232db811b1b9dbf218d57be48724af21746074881365efee65d972ad6e0fd1e11e21fb2151b3e5d8de87da899b2962a3021fd52e10d52b11c0aafba7d299c867262da47132b07ef611c7bdd056492e3b68c9c71181a215496fbaa38520a7aa80ca244084d490c26f4f20fcbed0348c6b474d7d88f511b2de24c28e3294223802342ecafff7673957a60f2dd552054fa80097cbb3c9b137411d6cb71d4d46a1b0111edbaab9760c1b5ab3704ee6995109878dd5abf188a0e04d977cc3c120466ac1347d4a9a11c685e584c9f3a5c17b493d3967415869d6f79d02267075194dc9b5cc582a4823bd06128f1d451032859bd5a9119e063082b09c7b2c4d8f82840e6a34b0c08de7446efbba9e4e3b504dd072aa4195120aa5f03854ad68530dc03492aef0293344b016b4c3cc4403cc1cfb034e2b87382d4beaa9c0fac21e9f151ff036573a7d93d050293277436e795a925cacb76a90998cb852451f40e9397904620ff43c5032db1b5cccb86e9ac09342df68397631368b9aa09e07bb464fb9cc4ecaaa4077018d35591b6142061232d21a899aaaacc915c2244538152a0078cf539d5f345c6962aac5205066d900b81403b113dffbc08860f529841ddd779fde756002cac5853c22799c66f6a221efc61bb7351f4a2939320a8932c1de87b24f470681231613bc656526685ffe9c065be26db5193181deeb3944d77321d5efe17a5a3a6cb902eb79c044fcba0255e1de14bb3004e440975902353d66ee96923390785e75b05c80d4d7960dbc96012aa3005f048b0431d8b856610b9e044c089a485345a66372a4391e050aa5cb12eb8631618cdcd66a1c69d0d4a9ba1b1ed47868a102e81ba6790056ad8edb9aab5d4629de44f4e60b3d94d203577982dc18fa311d9c914d6b6fd5ae69e7d1e14ab7ef4d1e4dd9eb4f5b67a4c5c61771d77002f32e3ac450da2cd6f6eb428bbf84b668d99bdcce4183843b8e611bae67c15ffc32ab25016a03eadcbf4385b1ad071a55f17678528ca64aa9a2aa4422d9be672e7e5b0151b648dfa74a0f83f07fb69fe63d31c8e77d3f8736735858949f3fd34ce36817b476ccc7cf57456ec8735975a78413d489414a6464ddaffb2dbbb0c1133621de936fad22637c42e1f64e381a2e9c1c8443c29f1c2b213b95a722b72b38cf46c087d59701fdad23c3e6b2087c625f62cffe62f3f7354a50d347d919cf506c5329e2e19559840baacb654d8fa831765dae33879819f6cb4aaa8df5742e3c6925034d2fdb5047282a9676accde57146fe4d0c4ca6e780129e05577638ff32934a35237b2f75e1081c1d6e10af63ae31fb3d63841b7c632002337e282bf9d73e62aca7d253692447eca13b8a0c0c634475ce6fa0be150fe5e414113bb6fd9d930f99241db724dae9380bc14535af67b250a003058c507b9ae6548691e98d79cb86a3014e1ad9069b335739458511e6337a7e671c514754ef5ece8bbc22d2b82047aa1578ec08657a0ad1ecfd64870e2cb77d09deaaa45e729a91497b9b71f41c834315c9e88acd6123f3447331825c2c3929d0d497caa74f72605be6d68f933ac6037e520591bc945cd1733bc23cb229be0ab997156c32c4d165b1b72513207d9bb1969050d5050291b52ee83a54bacb32595746dec0c389ba14f32ff2531f8a93bb892a1e9f9837c689b753040212420d1bd379969a0d9a2b2d0efc84d1ae80d9648d88b726014ebff35eb8cf6c4636ac13f39cb1dc0c5619fb3441da032a00d969939005d00f0fa0fca779999f4c32ea5a612dcc40586b0ffd383e7a685becad59198ab60f6817e0c9fb5f7d08265cd2071bac7bba70ec3cf13c1b0c47e7a72514f017154835824aaaec0c21ba19ca1f215f4083624835af7ea0e1266bbadfbd807d9a307f1d288a3566c3370db0144e2aaefec090c81ebbe0ee973f29134db58f18846e5b8da919ea629e9fb1ad8a417cec460c28321bbee00b08120a8f4b67de7f5ee4f0b482e06ed45727a3342330351f9fd58758d2a47ff8f323f2f1e68c1a218768e109d15dc9ac354735a4cb73b205453c097059d945c00742ad1e4fa556614bb852a486c5919b404137302033af8a593bace23aecb8d71531583cd8b06aefa0270b441d32506de30b587bf0ce1ba315bed903941fe0b13fa36d7a24d07022f902e3122eb25f2d82a667989d495133bc546997b983c27bc2ae51334b1eeb53e86b3ff974ff2c7c1cde502066a1acf39792084e3eb0fd73a9b6b8e17a3b0833331fb5a17255cd435a1dde6abcb8a40207c2e72134b27b38c660a58d565573a4fc46d30defd3cb3d5a6262d0311f672c266403487f867d116db863ed72c24ec2308d19e5c02abd7468a599611c240c4a68ca5e5baedf2da3e4c047ffe323afcf472519a886cf453f1ef83e873c89ba3dbb1547b98e43fbc3a33d6bcfa811c06f22c29d75020bdaa53f05d9ec2baaae97795cfdda6d9f88b6a9da0d6cff2e360251cd943a60309c18ab57e0a34c71e705005621f69c91169df5fe4ab79f3644fff85720e9f086c9df9b06490fd50bedc5ef8834c69dbd087b1c26d831fce4ac28bcd463e57035a8ac0dca0c8a6c558f940d1384982f51aaf206a2358c1e1c6bab3620b0ae5b8310f118fd58d60b83561be7d858fd784e7b070b4106e543a7885d75da4871466f84bdfcdeaace0c939fc2ee45a8a903bc3b04892293b61fba6f3ad2960b16c8e819c5aab957903046cf335da09d5c998e79b4a9967fe0e00940452f587fd127041afa38eb92e86cecc1127e1bd9d81c8075a5c1edc349b7d1e7d0d0abefc6876ec93902ee822cae30e85998404d1d4e1c53d5f47f7ea08055be279d1e2d02275597b97a96cf8440d243ae8fd6028533b18ec70b9be2d5cd631e266d0824982b900b26e2a62ed8f8583033dd1ad2b35fe86730fd33c1c05acc3299d401b13c01be7ef042608ddca4fdd8828777488af673e21262cdbb12a7d2a0d14de108ca1fa738919bbec42622cb7033e73cb95bfbaf5dec2abd35088761675f04b7844d50aad7339bb1dd8de126b3123ab7365dc4467d80563a1c3092452091da5ee55a45338d5e095198b01b8b7ace6f9857fe9f3d2a437d680497b15e2c31af087f061b4a5d08846c452ed6f5cefb1d31ecb9f3e74b81f6a701c37e1245074ec6108009bdab63417ad1e857d979a6bd164b52d9942d16b104eb6d34566380bc34245b661742f22a07a09de6373b2a19988b1e870f57f37e15750c0bc34ec14114782236e4c5eeb4fac0f4a0553af72e00a839bd14e2d0b7f294071ef5f493261014484791571a443ce14a815e91f006e0cacee1b6e1968b03510479baf120b73c5a75312023d672628af14cb09bcf1bc70d04ea09e3b7ab22482c7f479915c434d616898ca04e62fdefa3051bee426155d4626ae680aed24c401f0930c88028075239aa2e9b058b9955a2ed6f8cfcb0dbeaeeb01ffb164db3dc4532887e6a74d2984535718594f305c916c65d9acc33b95953d1ded52c4fc485b96711e575559ef43d124de4b03b7ce9ffb637ebb5fbcfbeee715a2f14e8e887728ec4db95225b0d29dc59881eccb78b8eb82861114eedd478d8d9d5f2a989420aa48ea2ca46f73b6476928fdcec2a63c350dfd3e31750754f0ddaae5b164b1ff29b5e1d15f09cd58197959724d85c176967d98fa8a14ba37de052f7f095b8342c7eef18cc85bd5c42a6258f226811ce6177b5a7d6705a8f2fe18088e441f4cae4f810b8960d1abd0dacc9e36d75e2e2c3c26564f79137f38911cd388c9e4010e5064c683fa374bbc79d2b24b213155a32b33ba047fb4f395d89c5d79073db28cb9b5a424c15552facc98d493cc070093a3993e784db5633e19354deeac3ed5f68e3eca23caa221915cddceb0880e12f4a08521e95422e9f3e2fd760e4868618bedfcd94e6853c595ed0d8875299282a7427c714f1c6668654b16548380fa6aa7c52335dd7cf3b9ab58d70afddf1ad0f48ea8c801bfe268e6b2c4131704b90543f283adb59134f40ee3e07d87929045473422b51389b8c63a81e2a074d967ce4315b35bd8c495c85180837e9c6ad0148cc51c95683b1f44af1912998dfb4f7fde5c7e2cda9c86e2462bc029933404ca83fb885be5e3b1c37ed46197d945120835b2e78b6ac3256e4a43fd67d901c3e6173e1f142479c0cca74ace2abdff4ca791b96e73c14fd25c63b3bcad7672b43405fc308d1e94caacd3712d9272015c478f5fa576043131ee9a6f9c5b0a65e7c8e1cf625ee32da2ab623a5d49591900958e952762beab54dd0c19464b48b4a90581c8a158c7dd65460389fac268a47617ee6a1f974daab7d73ff51d5aee8ac8767206923bed934f410724d643dc80e3635cf7aaa3832c4be66b9f3c5a5d77411d0b98466e2dd71ec099552a0e7ddb85dfbd99e40273e272fac63d793803439d0284067cecfaa324c991a45063a1c96521abced68934ac360155a7b8740848a6ec2b7158d3d7fc031e133d226c38844dfff42f615718ddf329d38e51b23659ec5a97d19177cbcafd1a681a96633064aa5c350a581c41ecbf55317e4c825552c6142157b2e2c2912569cf5735975d656b4c149ab39d1e580e2b8a3eb478d46fc6be527084726fba2c5edf2575b57ad00aba7eeacde5fda77a81184e907c6796755b6c9243efe90cf9e5db71c7505d97aec829840867d6625db1b314c260e7a19da807c81efc640c3b417512935c47a962915aebfa31342331b064eb706c5d58c3a1a1a98e2bdaea456d19c869e013fc68a209572bac42465c138b327a94cd221baa886a0455d90df121875ddb444b4caffc5e651bca6be2f983d7beda53fc46d719bf4883393b73751150174782e9d4cbaf00eacfe61052e5b38c7426ca4c5c513b59e5e2a6d0e66d1e9bebef42df92696b3ef8fc79e8771987a2c1d496708b3c5bdd01bd83d9886f274e10e8434d0ee796868d676d5cdea715c64594043fcd4b50a49a1c8c3e860bcb887d325857200b53ffc2d5a80bbe9205a6b83890e62a0ac540e4a5a8b35f7c74b8cdbaec851407861d3711cad4427f5328ac356aa5f8b806ffad440b92df5e3c505985c256dc2034039bc840cba83b78f5076afc7478593195a471920c0d238e6d5c3d3412ba1846e7cea1b0eb3506ceba468944ca866966550691e2fb4869bda3d7df7ea4546a6a0f028da13ebddd1615392c685d21088d6a752dab31f2b99e63d8123dfa01bd57978246d229a971f703289543bd4551a8cc326a6fd9d46f409641e76e658c1b063aeb0522038d9a3fb64ce89cc9cef1dfc3a81fd8b04e200ebf4924dc0357a6fa6cf66ff0d1860a7bd7987c1edc775d8aba9c9af02830a7a03f3908d7faa85c4eca3ec434f4dc661b02f04941b1133885aea2d70828fc64e7069a96cc4ba5d85ade11aa5aab94044171c9ad06904cc5f0a5ca1fb7347164d6e93cda91c0bfd0216b5627bc4a6854ce402476e59d25a64062e17f296dc7bf6833a0572d2c10b77adaf54d467f56c943f9f3b8b7b59d5aa6f4aecaffd1d005e2401b66d34fa6127b5d99dd9510cb08fdb9287db0cdfd460736f884276030a20028961168319cd2073bf89216409981ec38f205050b0e9d03414bb801e5d9b927f95d8f9628b7696410a731742d49f65b762e650b103fb279e298631e80b2dce1d6ce1240915104e6932f9ea5f3224ae503807984b8e87ae05ef90062227a83a687b69b14bacc9a9445d7744c2c41b803e0b9d259f78eba1017b217bdf422810b949a0b977988893445d6039a5b3d7a2f38cf5fbdc792f10db545356d54c84a6d4db816a2c8fee631e1769745e385202c25230285227ed69dcdedf0ad8e12a59880cf933d80c0c5f710bf8297424fe582198b946145dcabc81daab943de811103e168309cd1d1dd459e910b74d1530623f5e5fb441e34ea21ac519b5c97353f93ebe6ce685983bc7a0bc345b5d02e7d84ec72e82d883e1f75086928f8b54a521b502288fd7e8ddb3fc4969a5c6a8f300524a52d34973ac06668e08f7ad1c4f1d196f63d3a24e4b83b1cd8c4d01783fe7b3022b43c9a68a5e4c850868511437e2823c7f78642a991b70b8abd97d709547a63efef8d153538c4dc24d0318c9dde9f332d42698df8540bfbd12e151b2f4424df74723dd62ce2e04590e72842d264517a03645efae523114f294e05b81a50fac76ba093e8e8b961db7bec03ded2937d3a4747ee36e4bb8e50efc6b4d7794c33503a969e05eb0845aa17f541f747cf3a9887fc5cb43f38213ba85bfbd2b189bc45736de0ec9fef66fc3b0bd8b21da02f919807c223c8955ea80e90796d2515020789661cc3f760e46da50b014fb6ef1532939c85d1de75e722587ffebb6d869ce57b52dffd16ce338677179f41ec80053744dcc91872bf39481b0d7b7ad9d9f61c61ac645423a51b9106ec2b48a7edf1cda8c5b81df8d4e5ff16914a3f8c45f2b0bab9960469a8b3bb5e0f566adf7e6c14c629d03c12540abdee7a9a2d1fe4dae95af84e34ae56ed57d7e08dc1cc1713e677a5b5ff38d6d3c9e49908768b85169ed72e9911447bfd7691e3fac129337c067dced96aba03660dba16a35caaee87958d41599052d6031520746bbb7022491465037282ac51d361ddeed55f6450e49af19e557bfaaecede3ded1505a68564e365c5279ee705eac930a6ceb275d9bb99f416b052bc809733913b6ba6e750cbbd4d71715f7e910d78e2ac7a3d0bfe78d0b7e184a0ec3a9dd43adc77fb6546cc6a4f7bc958c298add5de1c374253023ec5387cacc60a2ab2316d0e12e305d1f68710662d59ca03ec82a5df08308b1e73f49795b60ea433da425749218a55ad8a5e8836000dfb533e5e4bdad5cc86d5f01687fcc656a8ade3cd2e92e2f3112877716479a2d8bde22e5e2dd9676ddd8039f850958072147a3787272ca405d052bf7b8943fded7f6b2309dcc36f0e2494184384e49103e536c8e76c9b96c7cedf6f81d08eb589f6a71d2ca14823f9bb51044aebe507019932037cde204f63df5c50229b0dcef72b36e3f54d279e0c5044b7b830ba275d246fb788f51e321ac2f6910f7f9c2486482b2ee1d009ade9b78b7ef7057d991b7baca47c423d6c0bca70ddc8a26cd18e25d961c6def51a073d11c060c0989321136bffe9ae1b5b1bebf09b9c771d1f6145f872d088287cf47ee78b77c86b59a69ca1984dd7ccc7df855a1fff33d4660525419929d3a59fef52bfc9c173f574e07219afd4603cbc29d7958658dbb4d282823b2aa76e960053da1a8eb2d678c3883e8b60ec18ef7c51b9f630c6b8d3795a98b0d623a1d0acaa4803b3d80cd2c586741c6dfb035b10381aec17bb79597d5fa3895d542c2e5a47099a944e5535d8305a317e8fceb0b8b32df8326d2d9495c365ae480e54388ff5bd5af52a6531b8b52d71062f081afafcf3b2c7d5e40bd497d4a4f05634df232beba30616e8deb843054f88676afb7c62b71e8c27b7c71ef649bb34eaa3cb5a21ffa96811146f25e1ea5b0a3107457ba883aaf6eea07e65dd09bd8cb81d5fb2c743dc01c066c6af22921dcddce93bd4e51045c382e961044b2efbaaf6b4680e3efd758d41740c874520a998199ba0744cfcdda2651eb8315f672ce092437bc2767e2f1596ffb12c9a4788865f14c6290ea068db9195da8ebc8b16ab3ad4dcbc0d9a38b31b1fa268b1cd622302c9ef35ac62e703197efee49f4836ce12664e80bea28517a654d2310c4aed59a44a3fb13989cb834448fcaf6400f04dc367455b76b82e7d1734896e57ff5668e2afb0affdd633443457c24f171fe1b37d9768c52bda8e17919f25180206b159f5bfedd0f7b026bdd15e86a880d3328d3fe0b53c29ee1aee38bf2f4292a180267cef8fb26ffebe2b378423d216e659af003c150d2646f352f3bfa40880aa4f912d4d7774349d4b34a1c4f6b9ff4879c2b77ffe8ea814b045d71ad78392bb22921dec14a20a2445e2db0097e0c31284ca8ad9a4a53296ba0a175c96f86118f6ea2b75efcb1021324422990d6f60d13324be97c5226ba61ff839c2a14f1180107d54afdc0534394c79f9298c012e140840dccf87d5a09b9c58fcab8eebb1c8812338333878c3cbe4cb3c7fe324d67309c8fde32c6d4dec33e1a1a7e90da24e131bf1383d05a5ac2d4c5cf8551a039e487180efbfe55cc2439f703863da81f5f6d172b4631dc4c568ae1f4d542a2f6cf5cfd305764d64d03918c488e31c3c4499d695be73541369138d5a7f8b1740a47a8a38b9f7bc3d43e01013db043524223b0deb75174d92d41e506944e0b4cd5d079383d9542c0cdf865fec0b37efa75bb30acf8186316e4beeef78adada2dc5c984f226f21f3efb7a5e4fecbde44be6793ffb9ffd85d6fef142cf4ddef4d1229ae4b22107ddbe52167bb06b1cff4076e3fb8b34180c74fda4f08f428c430a57c8522d3eb99e2ef8d20f1e30d05bc23cee31645e5c12a38ad5fa65f80dd2420003b68d97195cdb8761678716499213ddc73cdecf66e7ff48cf492c4f6c622a4ce16c31e2cef827acf8342b0732cecf55855fccb448661984e82f2a46ef5b8f0c3a9ece98ddfe87f32f788651c20ba3c7ebbdaa5d0d3af278ff9b6fda789e0f5bce721abcfa04a9c98c20ec30d4d1f690d4dfda99724ea3ee2a5d17e1a757b02fbc4e597fa509ef6dede3bfe6120a72b608f91d37a40b46733605d907e16fc2b8b918012c5a98cd68cab8fb25121f016caaae69544dfcbc0aecf175d9b6e5dc85d9302d5d67e4e513608c3606c0abf481daea76a80156060f84354d56da00b1a0430199f25b9bbb1c8090aaf00e5ba0441503c0a772b735cf4d83b5b06c7df38dc3b835b85b8f44fc2e386191bfe9cd71e52790af90c50048b9dba9ce9986a1de110a6540d8215abc0c1c3b9e152d43b8b6ae6216ef66090da7d21fecee386ff728bec2828cb26ad6cac053403f53a218fa3c057c5162416458484062f27ab1814d753d377c3ab39018957fc5768c6cb160145186f696dc1d94eb5637be75d860820ac6da40d2b3073ffb800489bf4dd009f13e04e47b2f9bb618e6ea5b351efc4cc3ea0dc58624a4444c5caa5d33f79cb4c88a83a0836a8e8a17bef1698af49f39b88f324ecc8405160df729738964c708f055a8342cd7bab8365e281db18f9668c16c10f9d0c5e8eba22bddc537246a9384d297753aa60edb58488240497faa23295957cbd210212f710ef159ec819c1394c0b325fcebca13b01252bb52c803db7d670426c2197c5498ec377ccd732c95b6363788b25ac93ec7a3e47d09ac893f95cec1e5d949dd57190680f52b0f5eb0c2fb678e5cc848fdbd2e87187d81aa90d74394ebae76f24a9fe446d437f96ba6cba187a6ce3f3f0e28849ac872ba34832b8038d341d7a573e4989075f73d04171ea7e8add0fed1cc1644594b2d83c188d8e448d8eb4954a834b75cb6005fa4773c4a8d9b700702deaae3175296462b5cc0f58ede96cd670c687930c2bc43d56ba5df0c359f576e03fb84a78a489f0894afe62397f39dfd995825ccd0688055ca14bbe817409dabb83aff1ffb63925332e4847f248e2a67851c565fdbe6aad82fa3421bb6b92386a8cb018a06fd1043542d50105376e715f32fb2196c221180366ca1073e8313cacf243b46ed8e5e74527a1e50f332463ba1d82dc67c223a684e1033b218d790082dfd97e24252b8b106dcc3cfdb3bbdf3271e50eaf7f2bc5bd3bd40831607f729eafd37cace9e70deeebc2e4d812f42403f3084ad874619de09c814fffaff6e5047d2c15432252b8acdfc6d1c5236839c864d0643230d3085d23821d069096ba22ca82ee5dac05bb7065b2049a0f8f05075669f3d50877b0450f56506836874e9f7703ba889a1071706307288ad923f2c7130b0c57b53270447ef98517d8062fac89f2629651b21ebbb4f087f04c76e8aa59cb1610e94984e96b2766af1b4b4eee94239ada163dbcd146b550cc57907900967150b86da9ea8b7d04808a49e70a788c3ab94c5c60ad8883ac2e5607d40f28a4470da07b63951440b0625ee3a9ce156001855670bcbc5caccdbc70dc53df1ac10c629de708a61705b0505a41381a80dac458ac6166ab35fec612ca4ed5a90c024058fe0f9b76f9849ff1b6a6d1de21dde03900f6a196d9771b9ffe11a1f49370576d60863ab97b1bdd8e3cc9771fd0f550726ddfc5704df19d503b616710b72e2c8b8be68f5302e563a506b6543c1b27043be44f196557feb6be5e40553052399b45ff17b54d5bf80c30e4b5378af700538300a4f471f8a1ff1a61a4e3341fc848bb4f7e35ff835a30643ec10e8a10f9608563a07f054a4c7d0a0b00bf69e18d6f8bf55619449747fe71934ee834f33cf86467ba6ec8f1fa114325f358077185809e959d13334981cf61e64fa70225ebe6f8fc0c6e4b9530be2ac388abccda910db5fcef959399f17b4d14dc6f98fa36567b9024be4f5c002c7784102d1abd50d43e38a6f415497a4cefc63502ecbc2158c11298b39b1caf7caa98cbc100ac691a44810289da75e65a477879dbb4e3cb7f172ddbe808cb5aea8df91d7deb8b68f6adf345808a2ee6f5c62f8a043344ced800de4d4c9dbfd70f303fa181494c356a1b60a8665abee4ec6696fd31c3ff41fe76f0fa30c028cb0d10d5ebbcbbfdbf9ca67b8347e6a050dcf1790f65a5ce05f060fc7ece38adcbd1ebe05bd8cf91875ae49b1cfe20ff809afbf1b5b8321effa1c20dbbabba1a5b7b7b262215da735c733a801f9720d3c79405c584049ace2dd77d4d8939228c1e5fe54b42cc4bb6a428f29100077b3168be083fbe31de23548152920b738a5de776d40d00eaf63fd8bfb8694bf6a3c9ba226deeeef88c9ea590bf8e31398903378d03012091cc3546932475dfc871371e08008924047afc3d4666421f1f9d3df4752d7dee90f7dec604c82686fafa8b532df9175f1ee96c95362121eb99691bb804ef498311ec70e7ab7de2a6b62139412ef8877cb110e6f1b1212e4f382f8ab7c896a9c38b51e5370d9bcd9e57f2a7367cc3d40012827cc237cdf62402060489d39ce7cd210c8d76b7f5b4b077bace44c332b481ffa35ec6675f98961fb82abfc34fedf614ec35f202cc635e30e54181a6bf1cb40924f6cd042e038accfb738867b365fc3498af489a85be7e452a678c18b54e92b9c17c83af72fd87b5c2835dccef3769d84c4b40cd2f62584fce49bb60de389e823f6174eba65fdf1579e2d9b81aee9606d5d65e2e16e4397448a2f4315534a1d51fc3ad003841b5525c6e92bf541152949ac41a2e532f170f5f9e1baa361eefe93c129a55106c504c40e184c38336c8c3851add3c316bdd1ba52c4ddcb8e35de75bb05bf796c54d4acb5400974f28d4da425f4d8cc70462ee8b5d1d81f8c362442fcc53cfda82c205a8321f339578694e2c4621217c6bf44639fe4cc5942862becff9449b630e92e7f223b52111bbc2d04a3e53e11a04c8ddef947d3c80d5c09b00be685bec0629f14969bf588e4648e4ccf3e483e110f5ed7d32cf494656b9de2af6a9d3ff4d494b2acbe17a536ba2e2872e089dc50c29e44e5e63130ef92007a3a20a0299e286b712b893e2302079b8311861bd41e41661020ea3f0180c3d971d79be98cb33d2f39de89fb7fdb277d0820c94178232bb57caebbe828653166e920c9689a14ab519f81344a4730182992e128778d15a86edd4d4bf506a7a911057c366839eddede9f02fb6a84a9f16aecb7ccb632b6a5b3eb95e389f2ea05a7b64f022bc5eb81e740566c583f01649b58508378a1185869a6f3aa1fc5ba7c7ec3d0edb6c9e65e03eddc3b89ae29e32e02f773f9c2c044d859c30559ff78670dc180ab3da10d8404f9295087c08ffb506b5646b50292e508f03cc9df06a31d8593eb283879d9307306c4be0ba216b0f56b686374aa8ee534fd693fb147a0d41adce51240f7322341ce019e4f2ed791aa7779fbed640f8d01da7b59f36364e5980d41eb8333e76dc6c0f81281554bc6d388562da116524ad5c6d9d6dd8e48b9845472da76ad5ba799716086fa054d5a3883c91a1bfc2cf8c88cd4d1de121cd625bc83dce1439e512339b33bc9c06464e37fb705c268796538a5e2e4ca0cce95ef8c32cbd647b58b9f7c5c85c2283d1e8962d21e9db37a92d9c3a8503f91c89f2d183d8c085af3926863b46d6608a8e1ca181a3c60c42ef5eb726a6ef89e4e3b5c8b57c6d3237753e9f794f16c2e0234a943be85c3a57509ac83202ea4d21ac82d2f4c95a4fc83f86564809d4dbdc45471dc199c7e5bfe286830c966cb7a700c5e08bfaf4fe9ad391b08633e8e1082eda31c89f54d09b3c207c2445eb12699ce97f58c65a6d9e8ecdbbfccdfd3758992eb51514f575e39043d652d61eeca8080e762b13290d42fa285339b9212715ebdd2ced70e0801ea0433aee66cbde2f405b1f0ddb7148118f84be453b165b7446029a1364796c7b868389afa7a6f1efa6c42a878d6bbe39559c73ef0508ff27662d3f42a5afd05a0f09303d206808a404b71a18767452f0265cf6501e27d467cb3736651d9a9c76d4f65ea62503c55b8c501528500ce145389de09865ec5c0e437cb164339ec31e5e89c275076c4f483a254275c15568784ab981ef066fa9225832c760119a62239f709c74d349456abb0f869b9814ca3b13f332654c5be8d5918497efebfef656e1401969d8705a5e382f86e0921190ff4d6fba4e84fa81c0b7ec570602f0635237012bd901c4de7b0de4655a2c58929fecd17c284d89700ad43685264d5107f36dc96d7cf1596321018325a1d6d79f6de0baaee48a0c1bb5462d4e2499834ca5984400f2399c7e999ab792276298a46391dbb26063ef069c2da021f024bdcf12051e1c6bfd02104293da79f2b212c6e0759ec8df330bd7ce08245ce3bb7e557e3a6526c894c994d9da903ece76e433685bcf89758f35af860f14e0b4592185b73e98026240677c49d7767924fd745fd7d1cfa416eda5b9b81a309ada71df18da18023fa676fc36fcd96d4838e61039e9d62faa85028b207c781872705c73e505cdf3494f9f2cecf7898d809683c3ded66b8ef8987ad533829a66105ab49603505f144a6a05e168049fd2fba2baa93c672c69cfda4ef3d84c9309b78630cb71cc2bce636a34f012844336eb2e96759fe1f60d81d57c035c09bab77d8c86835efeb2ac3379d3e5a4c65f22bae4549dfe4b05e9268788ebeda9a6536d4f9d8d29c88d64c874534125780ee12f29b891bf39e62cb3cccfcb901f465726c11c02d9cb2ddb285efd3ecf1e84eae0cc4461e542ec297b14f1bae4c8582bf1bc6fe78c4f47ce36b6515507372bb5ac91a558d140cc42f94fb0da52a72780df9ed7b3a2d40017ab62498a3618478a2454d5639cbd0b3dc7ac490c70df4e1bc3085c901c56a0b31f59d7c738e9a084f52663e8fa0ea35ea114fb7cff55ef7bb7e490f9a58d84212a605f46d54b06752d3323e3647b2b6c6582f4ffb187f8c84c8552ad295de7c938dd3b333d3a359f3acfd901c8bd64a745722c2b2c4f4345c7188fc7ef79df7a635cf09bd5d7f5fc9f176f17b054ec4b3bf88d05b86a4443b4bb0a3f2f331002f7b02dc031b84df8b24a29114801d32fde5a3d38d54a0f03bcfbf5085c59796f355b66ebc1ee2ccd142f08c6752cdcaddc80171bb14d32b41f4ef05a9c2a35d26942c431626bf596e011642d7d2d6debfc654da62fbed2db53f8ed3f130d08b694406e69811c529aa143de40c2ec0ac702a386107904acb7bb2342815f62404f93a82a343b40a64923d4d5048a4b623c421de2fee0070df46b117663bf0f343b826ab2bd9b676e3ab5538b02dbf67cc080b257b0ff490b6a85015d8b79fc8cb6870edef3d76fb8b8161b31f5451c6581f290b2b4e992f740ff3495eccb222032195ec8a4d7c60c747c5a302638494774d15222c93eea0089d470c81f1ad0a6e300a18870c817d211bd1a444511d23e97d7d4f4413e4836d73d72bf1c802a7f4ddb42d540e6d134a6bff6110f67cb80bb13b6793e1a4f1a864a1157ac7d73c0f437b5292220c55368ac5cddcd781fc307b0078e5c8efa4cdea8a71c22f08aae5264ebd76354805bb1f273f3e2d8e57dd0084606ff0ba168a24c8d84afd0d138c2a66266258199aabc03b07d010c48007567493e29c0f2c042f63329ccb197b690dec1c6361ecc2779b0b96e166ac1dadbccbe51ea4aad298df093aaea3df2ef84756368bd179873db908fbc56e4efff1ab47564e0ca6b5b2d91542f97547f57e2e3c169232f091926bdcedacc011ac3a941479bb73289e40749c5eb17e8098df2c73e4bfd8074166eead3dc7ec011aebf2845224b7232d534681f20c2411a32c36d722edc463a6385791d880d056d176ad470338270b7e9235d7414d22e9d36062403a467e08c0274f44bcfc120820f7b48ddcd44e1f27dc29bd9300f48c3c40d0c5481520bd52b7ad4d848736111f8f7b97fde6bc03df283606deae1bc9d39de4c5402c7ef939c3abc52538c7ed32a686c156117a27512335f9252641a1c9b33932f71ce6cf40c0f5e66709eb332109e318585408fce1bd66560e2e6b11aa4699969c0dabefe1d1749a1e47917ab06595c7cf1e0922962a8fc92ba30661e7845c172d1b3d5c37d58115909dc2172ca04ab9c7ee56159f7c6bd100b0b1f17836dc572942e58d759fd4a82cbc056c3e2fef1b6a8d97bd957a868c36f5faec7d2568550540b5c7a0b5933fe270e40039744f62d835406ca8cd8256b31922e48748011b36e280332706e3be3e78d195d74a346f2ed127e1e3e59662d4bd0e8574917ce846d51cb1d22765553327b52362bd75e46a2329b40e779f973dc43e5e73e717101cfe1d16136fa2a84ab145509060ff54d0f6834e209048c54493381dbd4575f33ab2a8925bda59d953ca3ba6115f8f9c90de1c12fc58f702a4b88bcd936980a8bc9a003ea2cddc9f25bd1ac1f7ebdc71b9752f996d5f2989fcfc5b13523419447d1ca0412f80246df048cf112dd5b56dbff13efae8e62209e8fe2934cdb93d3457cf395fc56380ff0dfc99241cd917db6de2b9d63289679912d330185582e1203b31c1c73d8e5eed909c207d0a74c914805fab0d2d41ac39fd00b6f2323ab026322cfa8441956d9644723a7425f00864a3062ea74aa6e39e48240b046a7a38c8eb546188b00ff8a1e32ac154f07185aed167c6054d0a4553eac5febc33aae5a3890dc64d4efbcec31ec68e8bace0401cf5f3d58fd0ce244ba99991cc999d5652390c1034c41cd02bb3ecc631fd8aa7f5923deaf884a8e695982963d58636b2dc198032e937fc99bf53da8f103b9e8df0c46d2f3d7a2e6362fdbed64a2f3a83b5a9612de91f41b658d6d8b7d3ccd23dd098cb3a4c8d84bc4e03a5461e9d79633fc5b2d3548f3be8d5d7ca04cfc5e386e6c11dbd067292f23b4210647104a866fa53ef328f9efb1f8ea302c75130019595b9879f6cb51c34095ada5328b8183d41c8bad20cbabf107f3f6e1783a8269f519606c695d2b46dbf2fcf3227ce13a09b7dd97d281ec831e33e631660c1550ef62caa8f9eec64a758d311c942746df061520a3cd395d6b2b83c32252013e6314a5600b8a3f7642c4d54767630ae69d4db61b90beec2505d8a64fd821930facdbf16fa877bc73ffa38ad284db075647d8fcb88182fecd32df66febdabe28116f43e72464d3ec5e1247d80ac601fa4f9dd47d8df4aa86894af8a3ed18884200b14db22bce8e1da00718c75f1dd23fa730701e285bcda46a972960600a68ed16621d5016703f32a71f65939ae1b08aa5f527d4397a8db7e97e91c6b7a250e8016ef45a08e3eda048480bac1933be1f85ddb7566d9e0cae478ed5d07f1da20ffda65f8f4198ebf4ee39662b150bc03fdd0817411762e802faf8f46e2c8995e142acf93abd2ca067e0d7b4d41bf65c2c1b99a2df4e420ecef4983e021e2e304aa4fca0b4bd04ccee587ecf1a1a2278ccfaee6f6f0e6a418a64905d4927506df3c5719d6d1f498a612a70cf89bc73d19fddbf072b65a69ce78c6ead7a93997436e7768dc204948b3469840bc20e24e72d08e647e1ab9af92522a6e417e15f16ee2a5e9138a7a5ae2064b0b64329cda446414d2d731363b0e840a1df0a39553d19df8e0358b7530851add4c447bf01026b9c603973e8d1e0eae594a69cb090b3146e0ad436b104d55aae3ba9a2dfb389f2d2349e640d296effbc6199100fdee2fd2405ae0f78f28aacf772570de600f7e4d473a604f24dd4e4cab5bf7b54b76904c8bde70130a578b479801fab7d80fdf14d6e24e99cf07fd7da4a23ba869f6d80d3c14cac92c2b602c753da87abe9d4bf3f121161fdeaa87b6cdc747e6c826861c14076a5fd159dfffb5b36e45d9c11dcc39a27a24317d0b64e9c342b956a2cb5753cc3d18d9cecefd8d19b5a73c96c3c37656dcf5c85cad59bb3076226fe1f01c6d45e205d4c415a637f94f22f3aaadf4eccbc14427ade84da446781b66fc8b4c48755cd0e565b502f2e6233daf541580fe46d8135ed540e3dbc4f26d9effb4e03e4dfc3d08abc1de42c1cafb0c68880b94b51614426a04131d671300170fa04e905b60befbd66dfce0a8611b697f665b80ac204a20c29c01d6cfacd3f11ac73c7104eddc2a03d2ab0d5959b7b54d91999b01f1f0187dd56901d95b65b1090050e32d00a328dd889f50d8afda0ad2ea05f952e73a3b14ede232ff35a1c7fe9c1ec57d6135ae9888b713428452fb44a32ce931b95268388a753654860d54e75fb3158d6499a0175da09be6e549018ff92019c7c4d5a7bfa30271148c0544a218f396f27b4fb652b92b4eeac07cdce1ee43d771f1accae37ccecb4ce094e7ae1b8ddc146f88f7ef06f6607638705c1fbd9877e48ec04eb1615dcc0f54e7c2b201daf9fe1d69c79d004698a2526d03e0cff03a01de8f7f4573b428735a4ab9f43e97b6c222beadad9ab56fb1b03766fe6a5e0872f223c4cbbdc9978e4d5dcfd3b82c508104fccccf9b137053c7c424524da279e80a460c0d3d262da458d2bbd443cdaff8d9b43d15f157de6c212200427e09daf0da00fa6b84d05babb9c5b361c894321f25aa5ff9bb6c7001849be6857460f36bd05ff1a543255585ed69d41287dd90d0cc72f0eacd702d2a1f239f09e184135ccb2fc1c1e53acff2b5150e441abca48f8f9c8fce2ecfe9933ca7a2436228a25dd85ac6c4492fa81b52c9328a76cffae907234639b948e812a0e6199cd83559ecbc5420e73ee5f72cbde6fd2a016a6c78266b2be62063d21a13be99d8fad7cdf501a0d2a752a8e6532a99d807ccc1ce3209a964d3fa1a5c556d7aaba806e4269b50e1e5af97a0d46d9934e83e64a59958136d9dc7ef84cc8dad8a21a8fb178b18b4dc786d540c27bde468ad646790658b922b08854ea2c0b793c180a2e4212af68b44e0fba1bf3de6ce06f1b8a30f2d75f2e8ae124bee9f6c984a61cfe176a3b419adf89d39e338831faef5bf31e14e89041295a5a61b4ac98880ab0dd21ae0da42ccedb891fbdce9f1beea2d1c7c5d68e43f03a581276155f298eba893c6e076882aa597b8acb8a02c555c76c9b3befda966620d1a2a99cc7caaf0b62b4a9a77cddebc17f70e1732ab947b7478548f12cb94bbf7e0dfa761e6c0a010f1bf092eb08ee9dbbd12adf7318ef5f3c37822152f7083bcd3bc5ade28d45e32245fe395886544bee46e57f380d1630766aca58c737484d5b25750ef77cb4e574a5726a97d9af14853aaaac596de58a52964f4c9bf0d084979b3f36430b560148d2458a3e8f74e80a307e02b704e32fe1d3dbba71528218033744f0f48b92fcbdf19a05bd112c0d3a25f576b91031859191f261710a859debb5d916ba70e2936815b57425b8f3135cacadb7a145044751f535a3609a6e12c44b292416a2a392d6759889982ca1624ee7c699e24cb37b6d0efb3428fbeac018b218db6fcea0b201b5c197077c3c655c8a540331c5640a7fc4bbb97524edb23dc504640b61f36bd1ce51b912991c2fb93593921e106c4a96bc30becf2298b2eef1f119b17043c3184810aec263fea7de7fb340f1b02fb22772a8a7844a60e287ad837f1a6c6be658a1ef2908f0ff1dbcba018a2474aeb40dab9b89faf2ae3be474f38f43774f5ffd05660f9817a9a257b1d606e7f28dff979f49aa65d4ebf15aaa9f67d2e3824c61f8bd8a5551cb130b2327a4af0a7c971971ff08cfb81846eac3dd6c52f7340a8e49faaf4acf9d59551827bce014ce3ff8540883d29e5c564eddd363b95989c9852df111470e90ebd24a20a643758253a89f180f2f1774ffe5bc7add46c4070f4912db64a3f3da2e14858dc8fafdc9486d67bd72adf31533108acbe3d3d94af29fa9ef288b1598541d415e2baf87bb60d0fba0fe29e2bd94547402f1a84835fe73e2f4445ccbcbb5fa79ffc0c26369c422ea70ba3a72eed23ea116dc24927c5aa25200ff03e6502c0e9564d309924ce31ca8d13ce61330e3bd2344767becc18d182d06453e9b389e8ab9675eb5323ea531d11649517e38b35b7a08de9f09915fb3e729aaef4768ab93384f07c9987822c75af1170e2ebf362d9d6b48b8a763b9725f70979c08c119459efd8f49133330fd5997480730e25f9e3bc63d60159c09b84d6af45c6ee880717c5e3e337b2e054d33fc99d3f4dcf99f0ebd8e9a8fb2f1d8467cee5af5aa21dc42ccc662baa65979df8dc421a6ccf22dde2710e8c9954e27ac5809704f974c19e750ff5b9e998346da8f9483bd2cf52892c6fcd5cc9438171e89121185cfc2bc82165fd4334e5aec0bce590d2660e697b4a3106ab351ca4521aa31cd9119b132cedb7513fe7345f58a96a8c5a021f17b35116a0cfeff9470fa97bed9484f57b9cd91b0055899e24e5ece9201d071424693d567dce4e737433be979ed74f89646444c0a22325519d960fcac6c295e76306cfce8ace62b489de22a5dfb7d860c5a6dd8e5fdb203f69133463e507af339d72e392834ae897662c9977d1a24d4071a2354dfdb5cf321311d0e090bf5411e36876c39cb1db43d20f37c8843c14d8b84112f211710f552480d00de17fba214a4c1be27b3684ce88e2ad23f10d74a43f1fefb239243704f42437e8f30b4a4a896e9871dda007154a4811b1e5b41fb3c38225010738379b86e2eef09c9739046dbea40b4cd2ed0cf4d678923bc64b0bf9f818bde626622a7802de9da6e5263c00f5aca4e327bb9c518985abf75cdc66906db003f8560747dbf6600d37915f420fd1779fcd2014e929b98996ef7a420cc58f8661c540e697aeac5f3535116d29a6266edd969af0689848d58d76f0449bf05c05077a1d72a00e9f03478786ae1c732fd2098dd780027adef62b4e9ba00afda42e117df7d2d0048939034589902d6e074d7f4f9dd326daa95c44aee4fe8132580350e15ab628334ea2537c8bcc36dfb9f82d92fadd19fc003c8373a119da84f7b140b45c095dd44e42bb4e7f00ba6122e22f723ac76c4495d949806f26ba30cba9d2813abebd14168c1dc61b1fb46b8fe7cdbfa75e7c0a59b56e142b5e45ba54c2551f3665e2f85a53103bc4f49efc7f5a2d2b819458df1adee453b8efe747ba4c60b60f0d9ca827a19d5b6cc9c633549ba3f1a71c5e63c15444ab87542a23f74ecf69c7e9c04329c583c4da11ec4f7e7292b94f90a51bd9fa8bc9cc0f03b8cda126b6fd5bffb1d4721d6951efc85af4d140dd2c13a26891d8ec024bccab8e3d32da6b428926c9f0509d2f75fd21fc77ef481722647dad24ba85c043f22bd9a304c4992955c9f683d2527ddae3d1f341492c98fdc83442e1999e9f4f4d93472899a6eb9f67382a116ef3d9e9a6b744852f9aa85c339307943ec78fc4fd33aa6d84aca6999cd272fab382298156172151a7cf905f1d89351ae4efbe3f9ccfdaa6bbfa94857a07f04f1180eb7e468136daf2834628819312d59356b1bfd437ed989c6e4a4bd0073280e357ca3ea80834255dcd811f21cfccb594458caf0084232848719030d80c011b8818f980d42c387665a23cdf0f50ea2b985f578462088c3136c954be243e1632b80e4c7e705d797a9304c50f127b731c1dad22b8b9e222385a712924c933ffa9a11403d00ecc99a01850f70f6feba747f39a113a6bf29d43b1624921170bb2b4cb501243139d57a67afa35a4154864db2778b8aa2cc1360075689339648e4c3f4f546adeae5129d413b02ec32eb849a3b38bee894a6922f8f32534fe26fe22a1ad22dcd562d0586b4174aff8262fb0d77cf2f601664f3ac5f4f10e5f07cac64e4170743e18c97be025650c1b20a35fc188ca98cd69e0e8ac5bbb47cc0c5069326686d4ce2f4de07ee38ff9a34b046f4e5fef42269a6baab657d0c03367509304dccf6c992accbb916a3710559a8ac4e3b25e4eaa5888116a654fe7e4a547593658536a1e153e6546cc431e690f73338a972d114845bf7ac264465976d37a0f08e78e388ac4d2b97448fb63eb7872d975876e613bc74bec42c055143dcb6480accf982650c5e729073034418ee91aaa1a7f68741bc68777d718708bf0489362733f4da8b8d6893f3cf7a949f37b419621fe13587c74ebd9b9ca3c5dc3a8186658ddc2a0a2d2bf442aaa0e108bea52a3921ebdd80244eba3cab89ef5c70b55aa158b271a4b5322e8af3c25ee3113ccaa0c62d381289a5a656fddf2abb6463162e2ce21952dd9d88ff327f8439bebbef16de26567efef12c0351b126590b7d2038440c9cfb7a9a9a0671cdace3597a8ceccb0ec70a7521d04d02e6204ab5525f56a59aaddf603157bda1661b0d40751c800c9568c81992310e00c465151889b4e80008c14255dd6ecc4927858444e73dc4aa7773c8e9808f5109f92dcd95fa7a10af18e2239fe81ba3fc4365d9be2d647d55053494d9b12491c4a24481b7dac617c4ad2e762798429133bf66ce1b460a6111bb447221ceb7ca7e6cf9123919a6b1e426672e65a07bc26488fd55497701c4c4effe41bf67927ae8c882aa51eab0cf5ef5b8d59949c149110ddb5cccfbe0cb27ffa09f6e1f95a39868c0a65516ca16a1d0229c9487216fb7548c0043e4936f4fb7f4692295c9e5a16ceba9dc092a6aa2b1abbc0c0a10cfacf0f19f494cfc721e35e2270205ad65f806387a5569d42b23424e2283a67c85d7ec08fb3d1a49d4bc5dc0e5839d6216d50554c7d44b3da5122787a033253b32b1c829e56d245646fc1631dbeffbe99a26d83e15cf9570f4f3a1f25ec8ecde29921bf254af06792a7238cd18e076f2bb26a750e7e725dd6fb161e3d88e5462e69fd6453a22ebb020dbaabe5401ae087cac66ca529ed03446dcb0be2a9e1520106920900a515642efd4db643b7f8a445ed0643d0b8fb1f86a42de7ba47207b11a87e56e86ea6e5ccb42749c38559120265532bd80d17f9b2207b1c13c9ed2dda1fdad5d28f6fe738c48b3ce407ee53a0ae3dc3c73d6f58520254975c4381b600016b44f727e07bab87ed93d0ebd312e650a590c01de22ed38cdb4e5c46bf471153fd6ea6452869251ae7c2049c5674c7f57d1a46f6cca4944c7a62d8488d5d1809a91ed4639e755c4d68879ac36fef29862c1cfbbfc370177ce3c49518ad23413d5992ba907d8e62b089c959cd79ff48371142f117f091d41ab4596686d05914c4f556885de716e3f240834e0003f0847af41be9cfe568df455d04c775cf3a13e39848915e515b8d81348b6ff2b6ea3262d3c7b3873f167cde0665ed6cd5b7a0b6ab2fbe048004b387ca5d8b1b74644a40d0c73705127dc4d846d5ad813c71e55bba9ed0f144c501f2b6fd307102e84d212a1068fc1e36e3574fc2174f4119c6368fa7d9e79589a35da1360b49876ecef094bf49a4e5152830c9e55fc1d614b9f9491ad2b52d56d2560d965904b03276af9547bf3097fcbb99f0018248a814ae7864dbb2b3fd715fe06477e49130165e8277493b7257d92bc3560b3ff2b0b8b4b08bbca3968c51f6ac464bc63f70f82cc57d96413583b749d361a87e0d57dde09ad7990324866c64fc614c08b6a47695576a996b37e493673994f8bd5b395557b5cf379149b0cc905f0a423259c4d9baa9c75cbf6be1565f2006c63dc72892b865b4a52a0c39330ec202723698357cbd5d09c8d9e429df63b5e7cd4012dd43923858c71856529a8b9d203f56b6faf7992e569bcf22a5eca3f874ada4860ca7596919ec32ef76c2b1a1e08c66941844f29478fde5c6d60ea702e83bd1f8db54b40bc4521cbf573d917596ee8533384a189f7cc75fd6393f6a1755d2bdeeda7dfc155543d3a2e49901d3a7e4564c4b83466b71b1b6742b0cd29d2be69277996564014780a96a1aeaf8e566d8500c8ce893497aa379f16904525a74916b1c7c88a9f56cc57fbb17726822b0c6af902313fc9e149f9bfcf2598ebe1816e230d74704c15d3a71c3bec10b183aa2a874452f770ad5069f8df22067245c9c3e11ef900603124234bd1b4fba3a34b01edd691c2d39f4e3dff13f25690134e3f1ac1a131672185f918981da4ac25fa180a1516626a7788ff9161e68a719f00a2bc49528e956c8b296a2fe3271b20d4bfa7f6ad852f4c0bebdcea87d486735b476739ab5317719575633b46913081dc6007b7e02c8faaf9f73bd8c002d5a1db1a44623485a7dc2b3fe600b050b0dc4d002a57cb2763ec8d3d63342d9763eb863f3590d8fc0aef9069f833ea0958b29fe1b69cfe0fc45e2c8ecb34d51b2b1c5e3175790e8918e4a41979c2803bac587ff513c902a9772717238a4dfc0c3f99d9fbe23f7ec1968722933882787bcf352316f8e20c28dff2c22c38be9d95ed4f2661ff84539df351fe3129020f634c7e85403c613ea7c48f9df0e416679c478930656ae330ebf1297e443ed33de93a51c640e6999e4171bae597d44141496a235a2d0299a6782c7e1e4466a9803db320e89219c37f9196e41b81d778542f596fce605eaaece8a7ea3781be64e6b9282af6ae688e514bf3b68c914581c8dd114374d8060ad313552a039ca493867c7404c78e08b79fe4a2350e076edfb0c95cf0e531ae4fb7d35d3d627e9d789a9a97c49a62963dca03c5324454633564ff651e4c1d12d97594ab3e04fc79361161e3cdc4866ed9392c5a4b06b166b233bfa98f2405252d9c8e0b6998201b9333ec832b6e64e041ba146a4121395b4aab2778e965f01f6480e30e4be370c85dce2951d22afd1f8d69ca3fda1dfae26b833b5fef488c096766de23685c81b4efbe29c68e5b74c1619617b9dd485eca7633d49435a1c6af1de6f39de7b404d3307e0636f6f222e52ef98641f74e8589f72f6abaaa3cd50a7893ce0736fc8648f669b8ea692f0fdff2b7e0674c9cf009c56ba68ca34db85c1ef4a8bec49ce23d97398f71c4bcac23b309146fb02c60f9fe7c47c92ae533bf63aed317c46b56a4c1d4dca53d30c89a1c4155a11b6a25beeb9699cea90cfd5b8101a347c070089ee208b0096125757c19ebb9f9081c08f80664bb878a8a8715e62c1e4b06fac48be327f202a412a318b5abbc82755c0ad3757cc1a5accb7e6f8285fa8388891e13e9e382fc21e9356898ea19c57527b68bed66b24047135d0c4265bf01c899867b3cf33ec603b254ee8826830c1ba24fdc4dc214256feac5e15f8ef17149728d756eccf67989de43087e80d537caa99aedc2cfe7b0b5cce309bc2f769b46870de19bc4f2001a92da4fbdf31ee6b1812d435f854564422f6f972c260104c0f63366b654cf46a2c756236ec0d03966fba1af4896a0c1cd6fea753a1fa8a38c9acb07375281d8b64c7fbec0858db3fa79a43ea4e9345143df9b8c7e1c771ff01a7203de9f50a363d992cea950472444efe94c538bc0bcd9ed847f260f8404c51551d42a84d59905f2092eea93951a99bafba989bfc5c2ba2531448c063dd236913f8b7527e19a247a6628fcc3580ad87da2e4fe1b03e214d699aad02451650c46be6bce3c23e3197c2758a00b6085e1c6c546fbeb8e703068bb63efdec9473ecd644dd44ad4c6587d06d871867719887b7e172648b23e5b4ac40d6811869b964e76d5ac959027550f18943d5e6dbbfa52b9fcaeab49cc348c634c1d43533b26130bce84c36ac788e547ef8852c17e7e9b2b3c09a0fbf629ac7c8ff3071b65d54f515dd85edc9bf6e387dc4627829f8938037403eedef619f1cd0185f081d8dab065227ba1823d3eb3a1fe115704be610c9692ebb0c5ebb51b3d147f9ea65bb151fbd0ca7d252e4668bd1ae6be59b470f98ba916d3f1212576ab60563c67b21d9f13997430364825181cb1fca2600184a21fa0425572b0f1615b4102ed2c6fa387a1bc97ec40317d4f1ec9f1fac1a408face3aff2a0cd60f2c48fbf4b71ddb1e2d294a770e28268feb18822b5152386062f450b10d556c18cfe859cac572ede82629710a20815e3800e30fe29b1a04ace1d9471f8df994b400fe97f86e033b76683d920b4ebaf5ddb4e21a03a02a5d06742c31c97b2d2541b889384368ca5826b450d26abb983495acad2fe07ab9bb1d813071801ae0007bbd3397ab560ea7904e049464292a24abdae552fa3c68c882e4e4a0366b7cda73cc2922798834e1683c0e6d9074deae807044f31eb2be018d65624bac4836f4b9e0ad4d0e823526c5845d73218c83829aca70c383fc2349e92b697d41b250084d712c88f598b2adce4a784407a0f9d16ab8affdf71d886b1f1286ac0eb46bdd2b3de52c788973f26717dd8d6dc1d403b501b58606e9b628b96382e47d92e6033ed199c92bb9b9e0443fe0b18d2490fcfcdf93f3de15b24a73dfd1152e79d19272220a4581c35770aec84394db06bf0fd3a91e6bfbd79850c2618840aac0a374c01a788c8e407323635404def5d02751fac01807af36aeeeaa3e181eb4023d01a5c2213b6aedc414374aa295e57f6903d3c4f11269f93650e8b756785c85aa23491a0a10e33a76d32875a6fc7f7e0d1e891b5ba32d4002443abac6471cf0c212f8e9742b6096227ce9d5d22da97c0bdd1e88da09d14c8ebaee574a41649882b72ebbca693b465f45dcd7c52620f1d1508cdc24873cbe0c2c9e200d224089e828eb65cc14aa27a3f21d15c6124ca28cc6e5222a58b987f0d9ba4a3bc2e3c710717d1e9caa49469b4900944ec43c8879a73143dc812fd6ffd247b6f27fb1e0b3626d6ef6365540c081c3d0f133bda9f04254b6d8f3a02fe2282675416c37d0bb923b6edc842694d35711e4c41032981c44381f51f216048466e1d08ca30d35533b061b109f88945001ff468f499fb5b77f261960a688d97c8a0caf054b0af3ce90dfb477c06ba96ba901223db0fe8001ef316134fb21499e6db92e45863d0f3f4fd64ee4c966c2058ce531fd4228c3242766574cbd78130d8dd66a010ef7bfca02c3b5ca3296f2883e30b833e71515be97e82c74021e3a78bd7ed330f80726a284fd737fbc0161ca11b82aeb8027a0b016bad637eed8164a5155578978140554d4ccc650d6c632522b6258683b304a607c13b4bfc8094dc2c12f84a9418ba48ca37016c7eb17da6ba88bf69fd892bab99c223810954970de12261f616f1cd085598e5d9a65ea0dfb2da3064fd13585c233288bacdeb23f507645860360700a4b1d86a12089006fc7a51817f260cd1e4f6a502c3f0692e1941ed8cfb4004f879dd2ed54d2c68ff706da4fc91b6c6f3a906b657f55639902cf1027eeeebf046e2a735978a235084382a1482544b184c3b216f3aa326ea82f94f5a0373c07ee3933b122f735abb5a5d6103f91f95e5ce4ae43e5e1a316dd1fe6a9f6b3305b52a081abb97a37566de8856c9f1828bd7460e4e609f5051d221abc33a320332d75ee821d0b4b2899314aab4e8dbb8c6d3b6d439d0e47ee92423b9f72011f065c026bf47a90ffd9368182ae1f778bfa3ee26975c4f457256f19f4bc69b3b68f6d12d268ed9e6f12f94762d5e0668117851b922fc55ee8641ccec424fe360d59fb84ff1e191e2c2575d1045ac24641a5e5bd6746b5829c814a3c36d6bd33bab42b894a3e7690943368154da716d372c46afe72affd3edae35eb6595545a444cf309b383806900a49236011410c14ae259f2ce7f3a89b97c4f629c83236b3d6ecdd7bd7d70089580209949d24f313dc91978b438d8a89d1370b711d9270d331fa3f34d8e50f1d8d99bc6c429b2b48ae5f688be0e61345b643259cc413cca12e74771c6efb1bc8858efd46e1d560942691e47d1d5334882a3311da409d666db4c422c3582c131b17d2e0f853cf33e516a3c14bd6f2d14ddb52fdbea60c12db23cd021f741bdcc6f4d2a7886eddc0d1a1a6d6b4500f5a78ac256a32b689844c39eea588dba1bdb81bbcf3eb5f8008e0880c103b0f24526bb59e4117bdf22271e511684075656ebad45bd4208eb16c6c9e0e2031286b91086d5c257bf1fd2e81341637d02552b2353fdb7ccd61af16948ba32b1489fed94557bea9bc0031329d844f1ee5b337f80c1da5357df1ad13b496e73cda09fe3a4cfb90afa39de2aaa7dcee5831242f5895400ce39a4efa6395bb9d1f1f582f216833caa426f0bdb8c145eab46b78097a9fd7acc7ec004066307be7ca31ccb0eccc4b1dfdb5c71bf7ddf103e58035f9962dffac9fcc4a53869acd44814e7bb7006f123b810be32492a884f70620f3da0f360c98a078736e3db32eca37994b3edf8b594ce546c3228c52d7de85a9f2ef4a595b896bd8a687bda944f1a4b93f469e305046059d99454d3e8bc9ad51c01c85a71c34c82835e87fa3db3e8a4897ecab39a0d4c84f618ba0aca5b8894073a30cdcf49bc6e427233ac324b5bac4758addce6a08540a6413c6ad9b3668839122407a75b8f12715b07303ab22833bea1abb10dee94682c2173c5cfaf41129ea2008d9977f7868470e0e54c94368b19c5caa08b26c55f6952dc264172ae3e07a294a620021bf44e7f51cb6667b130f53a0d1b877c3946bd6a3d6d6b2789937973364536491275dc530573d6af0606088b1e998a20cd595a9b4c558b2ab1d82ec94f32ab0667070ae803f128899c6cb835584dcef17bd65414a5a7fd1c83bb0abfbf3ca0894571ec8a806d5a612a14b1503576e362a70ae02400c7ebdc3ca900530622bf4242dcbeee49eec01d6c72c1df9076b84e9c8b67e5f8465ad3792b0be4d025e6f8314054595b4201f9b59c01bc0b6972821fda612133f2571547c6da56a2c854406e0bef9c7418be83eb7bc697ab56600c63adb1f27618056876b4da9a950693c76a0cd6b131a361f138bd35bbde6d3826b9a43d8760ec2add22cc7666d20cfbccf43e857291be0a4345fce193a63fd0b8e9c7cd12e323a6adea734e21d310f08618286d30d6925322dba370f0e3706544973a310ced7076d60fdb8d4d1106b53c32568ba0bd55759ec86d369860ca67aa54a86bb7fb3a399c3cbdbd6bb14f5e728ec5ae4a9e29f88b672c181d0e499eb4f806686dbca10b0814ef8f72a25dd1114868e4385b6a8e3e50f3a307650181d355c090839a7cffafbf899b7c28e3f7e777263db68633f620029016cf863dfa7fe87c00f6b04ec4057517324f5e1604fd4ad8c927311c64529459c72692cae0f3ff1144fe8ea5cdcce0157a1a89f53c064b42708ff8753b04f511a15f9fb50b5adf339b51eb1215b4ab23ba8985e75e20218646809522c4fa5c88b9927d8fd8caa94a1c7f7d249068008688ea874583094f495270b5b18aeda2c662c56d9fe1afc612f6f39a44f04574a3073c28f5807bedad0ff73aed6c281aa1ac4e9566bc4d91d85a5e4fb0dff3506689d140197624544af9a840125df3f2306b5e7f5677dd02a200547f9be70833c186f1310cad2480acb21ab7104b5682bbf521abe7da4543294cf50070927370ebb63a2f94a9cc12b0c6b24ded69cbb5b6e75bf517119f879868e801068ebf7beb21db0f59e84219aa7fc68eca6f55f63bec86251ada339f74071bd9f65d6839057bb506e691b05a3d5efac7a2919ec3b8a404d800688eba66de6865a3aab61d4d702f5ea11405206e549d9fe574b102d5fea4a0acfd926966d7fac903869f34c6b641aa2a36c35c7beb91d1103bac45574f424b33c63a5ef0f4817887fca0b010d145508fbde5f6d4221a3a7ac76d494c09b06a8b0f516fdbb219dc3ba1d0c7722232cabd0747922b2b288055f5b8b3806331b0415579122a53792ac827f8f4308969e950c902faa4d689e4e7c48f9b908f84f247fd272e034200d3f4fd65af374cc98fd7b4fad4332036767a501eae3e322a05f967e18f75ac81e4409278e0685a67e03fe29b9681de0d32291459f7ac4184c30509875c58817ee6328d26f776e0cf69543be5c69a3e0e279a5ba2cca7e19bcb2055cf15d823c54ac976554f73ebb321fdf6bae8663ea934fc477c92c0506eda12fd22aefe6a0c6aeec8a8c4c0f8fe966f28e5448d941aa7caf2c50a3e5a2cac91d48e4c73199e5194c909ac6b835ebf50fecbbe1eb58432b870d85d0c55478e5eccd66f66736385140f80527213a0a31775b0c76b20581ef92c9174ab328525f7ac8457c8fe0c4f6c152cce82b0258707c656c401db8f4305dd47964f4df762996bd3c8ff16a954e336c2be388e44dd9b51ec3f362e3a304f08625341ee2da051e748da8a616182bc35076164826c9dca4e522e45724d2c031f4f724b33e0d5988559f7f54cad5184edcbb73a99497b532172104638886452cfad9f0dfc3d794c87a066baf3c7723a9f32eb3c06290fa76ab41a1afce34247e2845bb1fc332b65ac800308034bc44bb62e43769bfbcaba2cffe473266f8cbfb7b1eef1d56eb83d7321467394deec257c752e08ca7e14656caa0613b0154bc966184f51a4aca7c19f247c5e5afa1b159481d553b58f62f511e4532dd72b8d7eec5120313d432cc174b3e255ed159095adc0a82acfbf74047e5df5756c941fbfe84d276fb402abeb65398025fd98416d73d5f4040ce0ba9ed87eef0dac29616ee81ada76a55e1558bbca71b9da101536b5c20a56bd465145506ac0dc8eab330a1ab09175a557835ee94d02040303ea08c66a59304ada4a07ca94563ba85f41b4d278c99dc0f1d9ac5767b6ed689d69f5af0b387a812ae2dd18684005c6748bfc1880eb014401812000e24c29a6b439d2dabb08523cfdde2d3889fbd08110479a89652d219b10b9a59429251930067106e906734e2927571fc6e3e8d6d7a8e4fad6174eceb9cfe5f90d725f977af2eb6bb6810f79a70593ca7c61c486f53a6b807bcea317fe8d4e698dd297a051be1a74999cf4c339279d4fa953a79f7d71d639ea74e9e71ea07352ca5646a5e4e8d00ed672df1cc77134d2196f73b6a6a8945c12a5d4b68db67fc7794feeeb567deeab747eaf06e3acaf0de873cf7da1d0a59ffc7a4424fe68e6faa0dd1ff4fa171b74ba2e18bab864634ff5e9caef0511dcb1bb4ff7f9385a29f6c4e432f674bdaf44a41a43babb3bd26ee4b89d1833d483e5176c2c7f3cf2a63edda37bbf74269dff94d66bfdb3fbe527bb186d92d660774f76f7492b95eeadca7dd2fdf3ae67a8c04c7df1d2650b162b531a948212a504eac80f79bda9871521041078ecd0d141831a3428fd820689a00c5919b262d9b362bcdd79c3bf99694c225de45b1b2c345762a8a6a494524a29a594524a294379a5f49f1ce52847ed0f48932447df3fca4f4180d81f2434f51021041078ec685076a7b331725e07baf7f46e7ba5fc29b33fd1c70fee6610ef8d530bbf5c4ecad596ab7bc17c7e2c502c4868c5d01051d117c3e882b6d1e76acbf5f179801082083dda06f4f19bda16fb1f6d135af1f183b46de8e3f353db883e3e0f699bec238b8f2e3ebef8f8303efe47efe3b354db661f9fa78058b674f1f245828f323e3e53b58d468b9f41abf14934bc8fe08c97e1157d081278b22211903c21598f23cf0a3545e0bd66907069f5f5a3e6c91940c0191f65cc90417b7e235a8c2f7e455f0d238b6051d12d1261e609c97a789e2d9a7dd1ac6806a36856347b51342b9ab9289a15cd5aac508285d714c12c2899d743c88e12224f84a09b540713a0b9353c0b626d7e9207e6cefff1962238df07b681bd165c2025682017ba7e6e01b558c49aec67997ce940573ea05d561001eed28fb2ca9f6dc05d7fae84b8e7d28f2392bcfd3fecd4ee345b56496a5303e401800928b0a164abe1d19e06c2357afe24251b0536003041b30c22933a26a5abeff55bb556ffd6ac25f09d5fe5fcc2a3cbf5ab2ad890dfb9e9e8ce11fcf9a8f561d3d19def5cc4baf5bba35929d6a55f9dacdcfa0e38baf41ba1d5fa70c7a57f745b4ad66bdc44ecacf56b55fa351d4d49d90405590b0404148bf9f8c82f080ffd40e7734a8eb6f4d8c3de61dfb57cdc7aa1530edbddb2a0c90fe701f93c07f4f90f4cc307ebfa3c0eb7301b83c9a72fb9fedadddcd7da957bfe9ee68465a1b6855688363ff96d935ffc69aaa68b34fa7a3aebecbce38664f15fe199b2f0147b2ff93f2bd8c824f43f1fd62321fe0843807525f5edc762b1eefe0c621842f5dab3a0099017f411c6412d2fecdbb5b2419f9fd8a0cfe368210c5efff3f1e3f0f7f164450d361dddee73c0d1757d23cce77eb27f6d7552fdf64a20c09e3d7aeed10d7778097cfb73c0d1e56f848a41fd92923c60f9e7d3da16d26c341ddddae0837dd436fac9a49715d1a46d2e858e56ce6b4ab572dc50d5026b8414dab8f106e84110e32635d8822dcdfb4144d2c134fa250fafc5cf8a6a50decac90f7ee01bfc8569801078b5fe0d648a6888886463361164305de32f7c83a718ce19d6224444ea0ff987db1f6fe8b1fc1dff0d3d16838814ce0f798a2f87b1214f319c5801d30f89601ac4cb405c090755c734777e48e411201ecd776f01f168f270992c623d2dd171cf588341c0ce1e3e7808cb8c350983c0f58fa09c9dcca2d3d17766667667eea77b910a96a347082baad8f8b1bbdb6f04db63b3f491dcee7102d1c58f3420310f908e317afeb53774c5c60f1baa6df1a31fcc3c60a6ee3f687d330f98a9c10e8815af6df1225e836ab02914bad199ead7525ee3b73184cde2d82454954441b5947fe467fe15556c7fd874428fba9c85c782196ccc6a97aa4374312a712ff35a9d958bb5725eebe9dd1fe5299dc6e792c870a683823bef4c478d90efbcbab01ab703c0fccd6f7e373dd7ed6247761f7aa537d341bfc6e5cb5ec8f3761ce78d9738ee0b2284897eb14e37a5b33f7bfca4586bad1f950e846b5859fe38e79c13cdbca22222c587b183e50fa9971b7fda6970c6396beb43fa533acdba91f29cb37e94ce47298d0dcef9352acdb7d16bb54e4ae9332d72a97429669d62289d540dc67751c6f20d4af1308d39db3199dcbf2042bc8d52e076392957bb208f32d519e4714c5dcbd54d97ab6bd5ee255b2d57e55e30af1dd6e7fb94404b2c22925154aad512b9128e9f5cac2072609a5b49926e46d9b3dd65db9805793b92b9fd49f1935c8982ac0b7480d0af48c54296f3d82aa6b8a41bbe8c5ce0aab9fe15ca2e3939779494eac288054649d0181aedfad1751b4237b420061114aebfacccf5ff6845248aeb5645c4442b2334ac00e74513474ec6558873dd7d8517ae3fcbac8dd93044132c35486498e34dce4c5dcdb0850a23c430b9cbdcd55cafba2eafbbbb0fb5111a9281242b159854cb19a97298b861cd13366160d842811eb89871220a3b58a8a250a11189648c3c81228825392c01b5a409244a4045d14413719454714aae6c58744abf40830ec0163756ede9b2a7861b2638214e7f3107fdb0cfa01ff64e37e4d860ebd5bd3ca7db7173aa271be4d8e01e1d2c206181273c54b920c5898897354b70d0b2660930696612467420061d862c11e58a0c0a187385062c70947499ba891045ca9233718218a24224b304c914726c28e365081825503e4860eda172e326e3c62a3d7bf46475004e126cc87cc1f3821411091782d0f3860916ab21261a4e72fb7f072e6ea2c6cc81c2654c9cc6d281cd963554f2485de1c01909c0d1b38207a67093813a39a48042981330d0c3130f767298624d1a20a29a70e3e508be9244892b55ec61c2c40c0d189e9871c24b9d1cd4e88083935372b516800a2368f0418c932b380065090155878815ec541d995795a353ca9c6cc0c1863d5e907ca0ea095368799669a1061922375ce9d2e58601a3eb8367e6d0c5a5cfd3e6c67632d7bf285610a59452c6b6648e3073fb9dca500d43553ce098a2d79e4c800e2865465df9cd5044c765e5860c74fd9bf93b5df719752b8862e50720cbcaa917d7bf16554402e2733397aa0f35b09a3c5bb9f0961545d87e968a32d3452e7f7b5fc0f2739e0b443b5fc407bee13c924dafebf444487861e687fc3afdbbf76ebf0c03367c294cfdeeeceedde979f723127f122ad8fe10ec7efbc3b46b5d0393997f0c8d61eeeeee39274d6e99547a4ddf75230ce637c2e40c61fbfdfee80be78bedf7f1c3a474ce3929e539bb7e8d4aadafd3c53d694be7a5a6633a29f9081ff2c0bc6db3ad63becfa51fab69777f2170e64faf2beac0535cbe5d99bfda56be35b6df5d7eff7494cb649243a72998faa53785f5281c4654935017596388ed9f534e496f13edd6065f4b5e1cd1aae14cbe776310379c71f4f9b66e389bcf97e8deb82dcfc9e5afd10397e9128fe641ca6849afe70f01cd77ef67ea20a24138b6dfea8e95fbe343e22fb333f7d5dbcfec1c3b4f8d22ac8d51ba0c14ac946ddbfd41afb4a00a8d2759ad2f18121bd63be7ec1fb257e85ccc59331116d69fb2d60ade56abf55e63be754e4a69e7deb1ecba97dd179b8dbbbb2c224977ebeeee37acaf6be7d706ed0d9deeab41779fb35629b95d7a741ff938babedc0b629801da69d7bf26f0d545f9cd5b91cc68c19ddc9740d62143d112454cd81891fa89a4b4838844e97394520e4783944e999c9fad310c950f6e63ed67e9609d4d1fdf0fc96a6477db8854bf93927b2993d51a265139679d2d59e78bfba8bf9c3a1aac9c94f5d65b0290fb6ab0e5bdd7eaed39651c83950789c80673fa10ba3fe695f5948884252333b3bcaf677e82abbbcff75783b4db05cacc9619f64d6eca555aeb944524fe99848529e5942848191b8eed0f6b977f080eb7fbe01bce7c269faf578b481af862fb59ba0fd7fd31ed9bc9fcccb345062ce52a37e9945f8e22393ca77713d681d39df90db9cb52c8ddf283c4c1877a68d1849535c89f909cd7c7ebcaaf09b418b49d974f60be1dbf243fbf6063fb65ec6f06923fef87c5f6f30c03bff2671ba47019851be3f4a5b747448a4f0401f1a32fa51ca594a3b6894bb1fd73fa74293bf2972ff9fd47dfa6d7651bb876b7773bbf7f330f9a897eec84cbb78d981f02777757db1d043fc728fbe5927e6d0447b918634cba3ffad2f05397afd7ca752d6504c1a4a4a5258e23c0d4e3f088632a5c7af9492b9353b6eae75d2725add231101293612dd8b0caac943f64105d47fb29ed29e5b4c558677e1c1af3021b9cf3b32f1ad86028e7d7f472d7d2c5251ba5b87ced94dfd57facc4ffd658ffa4db515e8b8c274ad13b586696ccfcf99857367777b770c2caea3daffc7cc82bdfe946588e1f7edbb76459202bcb79cee7c4f6cfef6adb22fb38b12c9d5bb2d71cfe91df935f41143b65d490172c2c30a92347e6a884d4608888362f6317ab757f19665c1f86b3d56a81d7e57245be94528ee3a4ac5e8bafd7cf1d718ae935e59d2925c7399d2f67b73ed3b95596b130cfa9a8a76348f45aad46a62c6c5f03afd5ff205976f4ff7c902cafaf0fdbf1f3b02f08e8b5da5f90d71744e6b5fa3e5f0ddd8b2c045fe1d2addfacf5f568b03e095eabb5ba97da6228eaaad54b83554635589fbc56ffe5d59fe1829ded787dffeba9da36fb20320d6128afd59fcd74c0fe83c804fb1200fb2ab08fa5bc56bfbe8be11b7cebcba8b679f1291b566c64926a528873c51499988b192d2ad8b1f2f4c41499e695d7aa7ba150d589396b121137380e3f437684e25499c2c56eb46ab2a652999161aa4009c5095343ab1b4be50e1a8b6dd98d564d84b841375a35d152c6460795166bb495b88ff12348bfe3105cada8e30d5602624d82cec4954ee2f43e88e0017e92b87299a2953c028709624d7eab822bad1057fe70fd19887e3522929cc05a2be74f777709e3e0e74e1a6bf45ddea493ced70738904f3d6205554a6e8aa5ba22365ee160274b75ad1b358a4e569414bdd1aa890d564e2ab4f9a1092758c26c00050c6e77a65c5551530394397594669cee464d1365d5440a77a35593175a4a645f545b707201393a4c3dedf0860c07dad0f1a2840a940b13865b0c20da54015506cd1ea80a9464d141cb9e2e4f545cbe4c384fae5c619eac71791e283ac5a6e54ceb2c98c4cd75a35513224160e12a9dd27b8aeb5a375a45e9a9024616ebfaf83a76f000e127ca5fda5cdf6c03d7cf777d4e57d6d74bb69eafcb13b2b67274ba38e897eff2fa4acf0619ecbd31f6ff26c978f5e362ce4c17ef4f6c70466ff4dac4407efe31a0eba253dbdd3ddf5dcaee6ee6cf76d3f9d99fe9b97b83eeb1bbbbbbbbbb6bb77bffecc05436c74de90c26fdcb38a54fe9dc735e392ec92ec95cde7bf6abf7df73fc956b45ffffffd6ec97194ae7337dd9b77db9bb33cb33b65f1ecaf739677b0f1d63a41c7333466f15ca505151515151f9f7cb6436262cc982255905936de3aa1e9630e181a362c544cb103676a3159318506042019716262d5059a2a73bc1fadc68b524abce195cad22a0e8095735a9f610c58505a90bf664254972d3f3428ea7292b244ebc90e30428174c0fcf28678c70c560fdc6aa33542e246c7763d5191f6a37569d512393b748c410af3297a9122853dc3016e667314c217395b42974a70577ce2548eeacc011535684923dd7bde63075a513c7f40699e51a93ba7c4218218c507367ea46c4e98518a42c0165a4033edc6192f34d193bdc101a0acbe1fc2ae286421440c28412d99697150c5dac28f074e54b580e69afd2c8ee6e67336868570ee04af916cacb952f3372a3d50b6eaea4e2caa9223e324692343726a57475eb774ff3d6a7cda1d1699bb4d3b6f959ae448b402d1d55c086f1d6f5c7162d4eac5518592c77238c2c56c2c862dd95ac78ad7eeceb932d121a417d32aff1ad62bd56ffc8880d7a8add62b7d88d888b0d95c4dcfa61a4cd695b5512d3b69fafaf64c595948ad450c98a95d62b1571a5d8d78ff1e5b6caac52915bc1d852922bd925ac51115a7d0058891544246e7d1a1d1a1e5712fa0a6514652475eb7f68f474eb1fe160632ff4a12d039fdb7db45b44aaefc3840dfa4cc8f2f326047dec577c26ac78a1e70ffad8ef08fa66afd847a69f6fc78a6ff6fa40a624afd517fa66dd9700ee639f095976c49ebe0941cf7d02e80bbdd06742ece947261c1d811e6bbb293db5adbf566b43dbedd68f7938bc563f21661e701ffb1ddcc7bee83be8fb0d8a8d3ddd786337afd5d853f78291c5521859ecbc31ca86b19b7d877dd04cb49b074194b51fd26efe2d6fe61f6d4e44aa2f83023f1fd26edf1076bef51c20848096ebdba0e5df0ce8e3f5f9101aac1fdd813c1d3ede8e2bd8ab73b568731aacb706ebd36eae5606fe331e0dd68f1784d8cd9568b758abef43bb85d5765f28737db1dbad456eeb0b2ded76691461fb95ae8072e87884a68f9f0384ae8f9992394c5df738c63d08299a1b6aea05668a0e4151d0b8a2bc199216f8acf88091c102302d45375a1df1001457a0b043375a1d9123bb82d29a49b47005c78932354819c207248cec8025cf1c36420c61c17043411b304d58add04399233c3552b2a0dcba0c21654305826cb0d262b609c09a2a7c70a3e776054f950bfccc80a7fad390162ca431020d0b56724ec09345050b49707083849b27920049214d36b41ca0a0323587035d88683263860631575d6480a24a13af2a7758cc3511b899c2cb9e2c501cf1668c153932e46101c64a9d203730d80b4c152288980a288e6891adb8d3f26a5f5647667041565b5f16ca93d72aa7c7c26eb43a52811b264971c4c8912328dc3089ce602b155b5ab33a53f38eccea8905244cb0f6b4c0a4c81d234da6e8206952848d0b4bbcb48c8c0b51535154515636ab73e3b658b51005a5c1ba6eb46aa10e77c6aeb8d1aa0534b33a31c618a574eff923449fed5ead5799bb7062f9a5f46777311d8e399b3efd6aa562659c93f22fe1086cf936870edb612c12bcd62ec6a7f80b7be9b2054b18d1c436f116e7443b312b4a11f1c43cfcc443b8081fe9d006ed7a91bfac92d18771aaf6fe85313c9995c1c69035e824780824fc0012c49dac80c063878e0e34b8e0e270fae2e445881d2159333e142285903c4e4f4e439c8a381d71825af1a19392a10f9da294647ce804c569ca75f09f59c8ed7a4f11024588d475a3af3d7fa1902b9085dc84cc111275fdeb226c83cfb34d9c75637c322a5955a0223bbbbb7bdfee7d84e5f8beeb22b33b4fc9ffbc12f84ae955011f47c300c2c6efaa3c51e5613ccdb69e4b48a6623a6debdb56a6ac6a18327443ce0a394f3fb177fe3ed7997d8b77691bb76d263dd87598cfbf880e80a3815e7aaecbecc55e7aadcbb4678ff6d2ab97d973f1d2e32eb32763f6845e7af3327b967f5eca59118cb7f816f62d5bf83bc9c199cfc3ae0fd1bbf7ba43ef5ee79eeb461807eeb5ae74f17e80d9cbde3d7a859c039e7a7aba3f0fe3a2c7d1ef4181ade4d242a69a9a63a724689bcff7fb94b6c99ec5f7fb111ff2424cdb6048d136d717fdec5b7c1b3d8df7be90afbcf72fb6986e346d23fafebebfdbc478ce4e56dbb05c49c6f7f754db84bebfc1b46dc5f7d08f559a2181babfbfb4cdc52d826fda1772d411d58749525c8c9ae7d19815b9bad4175e6c908a85ccc7a373fd259851b1a190dbf53e8c5448de9117810781e7c28b79b141a0195eec9afd191292e17d5e6c90c88b0d829e7fa110a8eb7da1fc26cebaeecfc273299fe2e3b915cfb1349db6d3592d4591d778665ee769e1f953e7f99097e745fc48830ee5b5ee4e6a342da66b4dd560bc9eea2feda5bb0061e9295d93f21a8ad74aba06c5623cbeea270f4d1e29de8e6ccee536d48bf7456472e1f52df2f8ce3cbe2d3cbe2cbcbe32af2fd18a182f50649ae145a6be308fefcb8b736ee7d1ebf2e66d79d58b1d70b2c4d3e5bc4806ea1a799f7441b78c0cfad2cfbb417e558c3ec272187d4c15d6cad42fe32cf6a9988aa9fc2a462361d9cb5ebd9d7c51d960ec2ad7cd71e498f90ab7cb595fc4b1fc5d5fceb56ab7a6ab7bc17c7e2c10500c284868c510918c458b59918b1730def3fabbef15fbfb62d08c64cc006b1044f08a5e38efd12b7a9d04485c0c3618003d81d790499306f78ab018257077772477773f6a45e082a09bf13282d17cbe1fcf3ed0a413468c4ef717412e848a56cc865810c964442c865aac98091505b988bd80f11ecc7bc162a8dfab85958d97e3ebeb62b468d5889341674c50d61c828e808f5eeeee1e913eef7f90908a7842e5dd4260ce8e741401b743c041647fdc0e4612ec1166ba9bbb23337fb4189ff7305eb8e020b228926067b746126c0b1632a2a11542413120fbe37355a673b52a47a7f4ee6fe796cf42cf8ccbc636a0ab6d9315880c97de4db0178e76a70ee92802086e4d823e468cd165498bf1790fe3850b59646757da162c6444432b84826240f6c787c808a2ced5ba95a3f34aef29e50f9204471140500367c830a2c54042fa90bc4782f1c245d1ac050b1912d1d00aa1a0189045424272b52878210d5851b921499822766e4f70f43cc1c1e202e70b97068e0cf40638465c4bdedc89f2e68d9c3742b470b0b41babde44717d7973048c957163951b265a49dcfcc03d61bd1babdcc430ddd8193756b5b963449b3434ab0d0f68da486d6963c455874d16f7c5823756b1a16a99619365036ca0e8149645075a606c0b37ae276c511015c99a2c6fd6249961acd08d556aaaf8809a263a3716e9c62a353e6cb9a1caa0a609173bbbb10a4b8f182bbbb10a8b0931d6c58d555855f78695e586550b561445c206dd5885a5428b083bf4840a69e0d02f16e8c6aa3464ba232c8b1babd2eca0c416dd5895a64947c4c2b8b12a092a26b8b12a8e98ea03dba5fa04ab4380386108879765a41bbeb0a6e8995283b5d6c907275e80210a17272a1011a9eedc70268a17a69ea881c93f3acff95285bbbbf44ea25f4d0018b77b862cb76948e176d390e4360d19980189d113475652da4cf08274973c018e26bd7b06a6a65adecf7f5f2225c97763d51154a4c8bbd481cbdc57cc9d0be08c8dfbe3514c44736bb8f3119ee20485249c60399173fdff8905ae3ff8248aeb9f048588ebbf8463c9f5d780caf5d7b1c3f5e71102d6f5ef118313d79f042ed71f8812d79f9f8a4461024a061ea25eb8fe2c255587270db10bb0bc30d1e6c8153b57325870c2849b254eccd061f229678a8c4fac32e6e838c51e21ba2461a108180d3085f6c69b0e4dacd1f2440b95104c530921bc00c56bad9c2ed2569cf9a099da4bf57478cdbf7e90d9df9a7590597f330fe6d7df313fc87bcd3f881393135374f9014524973151ea1ff0df3c4c65aaaed42f0babfd75de80fe83c804f4e101fa2a40dfcc9ab75bbf35f078b413d3eb3bf6c29e0a9c32a814eeb724ad84684c101008200073150020180c0a0604029148301e07ab2a1f14800d637c42766234134b23418ea3208882188661180631c418628c210c292891556adb5debc62be24c8b22c941c640f71d4f5ff11289222290843657cbdbb4db4273aa100b5eedf0b70ba4f582f4990b1ccf90b1360a48ac37cd72a036030d738a0530abcad1a5be8e8a8dba8797d3ed12a17062ffd99496f3981cde2a8f782107a7e52217d76fe16432743eaf44ae77749a1bf135225b6d8e9c7defad6aff08ae3a52df0d9f7a806c93f062014a369c7d16d00d1f9ac195f2cba09c0fab9abac3459b635cae705443937e446a5fb862d8343da7cf2f1dd1444a580599fff029a29f69178066f2379a038933ad67bd4868e3d5af00f8dc7942104d5c219a6187688af9cdb49f36330bd5cc854c689a9bae5202c22834bf2c9e29f3d799d213f327a732973238d37fda664a77355351c832cd6f3333c5bac30e51d26536f3bc7cc37e7b45c11d4fa7f5dbfccbe4956b0a3015fab738ec8be84e9353f01a226e858f490e12ef57e6a3d94c381805b6a3f97b6b1aab52e7f36ebbcd9947749516157222684bab84a151364d587d8d98de0ad21a7166f29bbbbb8ac45a79c8d1a4e45eb3c6e9c034627d74c0c64d3c571693e9ceba98c6da01fbd4f2ddf0ab02bd71c856c3641760eea08702bb3508a0da6c6faa3a2cb005cb5988b86aeacd7b8da7989a99af484cb17ec96c6114b10641ccf1e9982776e6e0aa7a478a5efedd35833c48201565fa80fac66635595b8de80596fd0de48a536e27dec880ada6fdfce344df6e682055ee9b467e820eb038c99194b19eee6dac131d39ae5dd1c25e984ab03563c3e5541321905c28853392d9639de21f6ec9e7c3214a925543c1b8d10acd93c43ddeb71a511ece8cd8cb2244f44867a1acc52c0c26229b4204e4ec097b425cad3795262199b5ef2ccccff89ad3c44614dd0809fdfb80a53a4ee1a823f29e0d552a232795e0cd05f1806370a6bb8a41a40cdac02c15895d1c560d12474f35bc66cfc4a10119995517ce103e47826c7f0828006d8f728b2b7eb9a99026e24a909c0b9d5b6e6e3c46d3edb388583aca4a2080164f9faadd5ed4222b4c5509f6ddb75101a6cf33b88b441cc6b03e378935503db6a50f92bfbc6865d09f0bbd4d8a69733a088e0bad8cb47005a00c095410e799a5eaceb97c71075bb5f64f5ed0571652c55a84fe0470f808b3651845b7cdb61d133c606450d37101548e6b05cf353754715c12a6d193358d7a29e0122f339a55b3a4829288b041b2cbad5684f103ad1429290c136d824897d9f859ae3e29c489475e93231c2acb508bb877ce67e2cc433045432311f0e5ed45d2420924fd41862d2c49da2635555aad4130af413d5ad17c3b54ced0703f1a9a93887dc9fc20f117254c670898ee38f6d7421e4f7df026b686fa41417d5729925e7a23ab1f6d8ca2a72cfab97d3ab4832e6f23173b9843f86dd2bcd41c7cbfb1e53c4e76698cc51a858577092aa9783fe140fdc45385dbc30c3348bd5d9eb99d2ac9d0f65c3a5ee1ed309b41658ef1306f084bd253ee35110adb764bcba5ed919f3fb4a3ab467995849f2950b9e1768281455c37a32b2b621085cd6e207be0bd40ed00153616b2a84831610aada568e7986dd632591c7651d063743e66bfd66d7be8be392ccdfaa7847aa486b00fc92060b6fd0b224418f44dc8cca9f78a707e7cba29c9db5038777674f8ac339acd1cd8dbd054966de7f1acd01f684314a16a23001412e2247eab7ee19f27817fc9b628ebfb54a4c951f4e13ecf995c74b2af2f7031fa5d053f4ffee3f514f5731fee30a915e126bf58c5da76292fc3d149502225a03e0f4d4a00f8809362e073a30ff844eb324e4c526eb06304a1324a4f677a02c7a4c573c1033b7fc536aa3eebc5cb440cc9e498c99ffd3a9894985a0d65006e2cc8c9464107b8ab3a30315a23ce44ff0519bf08d77a0ccb87dfbf41e8947f738104c4161404fd1205a37f6a8dd1055a994c72f75ab21024f40e4c69b4ecb7bdd7a2626f9649ac3f0716600ef870774883cd1decb81e5fd3a6b5d798686fbaf9b34a46b065c1f50cc12fb0521e802f05789c2356b688d97e4e4d4a39c64d800fac5acea72acf441e287a64b64ec7ad4955940c797bb50d8a5a3d097cea6bd8c9dd0933c4ef4132cdeae6748f65725a84a22b37e1c9bb770129e926c8b9d2c5ae9ce409dd7bbb7d094eb5beaa995e64f0282fd5c551598efa7be3adea2891185ba1a06cf96ba635d5ca1b2c5d2dafe01d3d1f69687de9e5bc8af8ca0a3e8b6ce1f314c2d8e2969c34afb6132733bc179d748f817686b1b782b5cd122bf9eecf810f3a1966dfa81c266a01cfaf250b1155b336cd12fc1c2e54d004347d912a25027d23d97cae3cd1aae8d463ffa58e997c18d31193858407266e849bbd07f2daca711a05b551fab0798fbd1a08c7cf5d28451128d196445a01bd58c4cab18efa11ddc8fb7205641b3b50e47cc1494330d4be17134e5ac181291ad7202d8e73fd392bbe34a3726a3f8423f928e7550bce91b52fa0c589a1607ccf9562cb3928eda83c5cddc09024288ad38d319dde1736b9f78c0f50129c0c3c26ffa4da0de5bc1e1b599484ba378c633b4874572ec5d69077aace6bbf3651c20a28e847cae7c94987f11db164f983ce86d3140c0de027b547fe6615c41ea0cb8d289df731a2977253aec672acc38e6e6545591bd7b11cc3d17af89bb220c6eb8a7943181fc53fc48c638cf1939d7027e3593999f3496aefe0e94709642cb5c95b2643bd37d6b91d64642b0fea6adc8e75a8c8a5dc54bba11cd663238b2bbf1fb22a670e371827c52e53a7ffdba92c4445b3f570b64aced6929d45ac6e6a0e599aee13a3d1ae5e04c235364e0c9af822db2935e6708f08d4d080d5d8602240b24cbafca46254e9525d0e2360fa8ddda071318ad685b6852a8aef5470199cee0cddeec59e32cc5730ecde88bc713040b6348d18a5026d4872c753eb2d7aed6f8081944dab5782042c01eab807e2963d0282cd7fa49277b4640c25bb3bddfab9cf4e5dceb5b5907942d365606e7e6225f2c57ae023e5f556fc14161f4467c902930dd2443749836a260dfacdc2f1e68440b1905b3df3d3092d57a2ab0efd4123074a1b91f044f43cdf61d484fc14d3c6b7504442441309114520c43491122823d27216ee7eebc93e99a227e41499509364424fc82495935d74546e9dd566cbe74e0f8b8b1fcffcf889b46be343fc86c911350a72fc1c907a72403ba564c172f46dd3890e81b0db4f10d5039f950f48cc1c361cb33bbfef6f8b2f596516f0489695696334b967c2949127007aa4d48aa237904d458f3342663f310475a33fad51c7281b0e4609cedfa02dfcaaf60d0c0c7255cc81d991672ba352dc624d5b4b623fc18926a4cf3731a6dc5c50252d693eb68f17f4699dbb08fccf3ba1a2fe7a99bb1ae221decc37e45c6a7248597e8e0ae443816b2cbae2c468d84a245deef4b2a4b4f1bb1376c01e0d048216493f6a5ec169e945b4b3db581d5f31febf4b1eccd70cf4f3a076feef09446426d07c7ec47b716c95e294f2fede3fbd33a29e02e9a01ab41663b549b12929a23353c66a5cd3b18d48c15384e6946b695b7aba5ca519e3803335d67fac67f06e38643dc00fe48a111e719955c77a1c23f77361412cab58622d3f3170f6d774437b8c80851141266dfb6756f8ae338ba7e76679cf37d844cfaf7e6299ee0c85fc1fa7130214441f8f900f84c37353afea3eb10cc60984d355a42ce1a4d4f849d200ac4053584d56366360ffb537cc66ed18376aac082d0bbc8eb9b4321e1a51270eb0a3b1230f32fdaa8f93777ae74332294d3d00fa3f3264b498ae53160ee822075457f02d1c21ff8919e82b058cf290cad19ec248e5e1e64e29e56dcd25d773cdfd6f68fc6972585a5f34c194478dc1a6660860279ca207e59950c7685e0b16836fc5895f9c7265e5b53a869aa102798aeedfaaaf1c5601d1f4749b047a46ede23f20803c6b2cb7a8d26ca74de60c616b733e341c579772c3ba9317a8b7bb806f3f2a242873a24f355dbb10fca2f2436f205b06df865aa0837f7fbe8c86038e1273ab14de9778a1649e8411e558dbd60bac50a6f744ad8c3937b1627a3cba9a3c34d546058bb743f1fab4dd90169a4d527d860c1e9f240e8322055ad8627a16506d541ce33081f0e4aeca87592aedf46f717a5f6a914ed1212672a56501a98080ed19077fdc96335c3f197860045a7ec429b1fe34e8ca3794ba2cd322c827f8fe83551213a2c346bd81cd53e58c21d5a5f1507df49a9235064144476a6ca92919c832f5eb70549485271c64962166359117eb4f71db54acde7a07cd9adf612e0e4643829dd7bb267ed5d8992c50a77e03012a4b8aeaea6b948eeba400134089f93f97836ea5242e97e89356c303c15995416913db2e7ee07138b6172d9de4565ba656c926bbfa89998364be49fefbc44f127a507dbbb99b08feef92cc9d952704678975e020cc70294cf747deec21436629532c7ff21891f12b9ec90525fcb448a4724a2045d965219101701c472421ea4ff8cd222923de06229ff9fc9abd8df232fe9a031c64da654268d6a8d463f1cf51d761249d100926d8b2d2ad390cb68874b9fcfbc35fea689d180268082184b51be58441cb297b54f3ab83135a3864604c4785f959263ab9e7ee584ae8356aa351564fb2289b904caa2a54e81bd51af6c6afcaa69f34838effc6686d61acf8ea304b483814e30a76b3ffc05d7f4f7b94e3b718c00b175d7098d91540c65fa56b45160d9bc95df16acae02476c65983d2d477c48f503c803f5ccc9f0d7d573867ff45bd6a38c93a4b8da4ee03897f750c3fbe7d6cdec6f0f6c90eae915fb21b85dfadd1d0e74fc2ff09034b6fd2e39b91928ec51407cd1bff318a86988d01caca55d7c2a3b07c837f934ec22fdb1e7da5fe435a654956d928a47089bc59f610b5d254fcd645689c11e9b5f62c7e6a48b8d4447ec9b2ce016c2eeba572fb770e9f904330fd2e2f0211fb826ecc4a4be3493f4cc6740e653a2753fdb3370d63b1eaea8e81d6217799ac4e2362674a7c2cb4534eeec01af6f955ebf736acdd7b92395a7bd475137490fd21d110350e7858afab4de5b0bcc3684e09e7b6c1571890f842020955b3307086b0c82a55af8dd4a168a6e8655c70ab89898d0b9c04dbf7b4839f5070f6104fb9bfeeee2ea86fc1d22164b9b8c18bc2bb04dab64628e2d96fd7de26dde08aaddf80ce61213256275e3549a046f31062fb42a6149d09bddf77a7ccaecd89b84c21d55c32990a421d58fb8d53ac2432220af11543afac65e970a6bb8116288fe262ea514165366c73d72d6b44934e903133a20b5044052a2392d7d32c597aae5061a8bd3553ef38aef39107e104ed8bc23e1a2964c5ae0a78b92f04d4762714355a393535dc24be13e313dcf4fa2cdd85c2cae754b55a62b76b089bbb80c21e6988b627690db76cd6d2911d118701e9ec9121c2e46b32e84328d6e496a02793a0986f5469a584d954af851765924c66ac4bb6009b46239aeb415cbdec0ec0e991b085d0a31804a48403d09592e50f6160e494c37ade417da4aaf9102fa75371ed6f050beac56ac466d7c92454ff415f4e75885511f7c450d85ffa1f8183d8f1502cceb6d3e8087ea75566e79382a07e6f60d0f38b8cf03bf164a00cf8a842398fabb183588ec42299d10f4d4c2014f7959157643708cb6e0ac95f91314b9d97290a3e132bae06a434177996080a02be18e0ee9cb176ba317452463edaa40642c534d312ae77b7810aacfe427364fc7148d2085f5bbbda20bd56de65efb60d8219ac24298df7bcc8703a66dd7c54f40efa425b93d07df9b68e2979ba908a9676517f690b802ff2586d33d1b2d9b149a284953da8ca3f486375abe99ffaaa85998efa3603b2d7d325a5f5328536626d09051b2ee1e0100b213bbf20a6ac7638d3f8c597cf8e3eaebc7d650b540e8d2a9ccbcdbe03dced458294ef35a84982ae15cd9b5b83ce80ece0afe0d5aec3b9b7dbe9ad271a820fbafac43405a8d755e3573b9492042d35a247525d80fea5c54618293789f110dee15faee79834191584df5695e27a2548bc44ce998e214496296ca0b66f203a6a1b5189f38ae9f3242157ab616376dd67b7fb510115e8669fc20049f1b09cca53056fbfb61fa8b101ace99442fc4063ed7fa0593a1902604af9192120eaa62e0e45000b257d289cb3043882a11a9b725ce7f5096be25cbd1f9e685a2634d43449fd784bd281d550e9aa0d4e8a964414c0c7508b980a45146cf016512821542d505424dc0d858997705e584277cf6095c68c2810588b11c5cfd01997cc643f16dd7559e7ac433f3afef2ce9df7f41a7f89bf2f1f26a8599d821572016b0b91443c23f970a722a5c5d2b357aa97880541aa67d2c13cf970a017adea5b80b81f4206237c1071c0da2b9080093ec458d4097c48ff2a107d9ffc3e6221dd62a793c8d30b19cb0222c195755eb6d4e0f29655cc5bda192b9af8c37d50d6c1ffca742a9958cd68aebdcf7e8d6e9075ba9815e8ce2b3e9217ff4280ede3057f8b6996ed77afb52756de080c820b68cac103febb93a01b2875bfb80c04a023d39ca930b473c7ae5a1e19a35b32204679906442c60c3ccde9fbcacbf87c96a9d0616fd11adf7f453618956b52ed3d387fc040505d6255a6c7794cf8042ed77140a2741e97202e5774a84463b69f71d45ef3d9766c719d7f1f8bec4b40174623ecea164d9cabbc6a24a7807ff37086a9640ccd590a72516fa7db04faadda72b755f26ae8d34f8b1afbccfb4f074899d86b4945b257cdd36faf11d1f0a07d348c2105274d45a907ea3334fbf827a831f0af71d51efa4aed1a0789b9eb49dc8529bf99da5c4843640f0ae3ca16e716fc567dde93b8671e4197bb1a99dd3f01629ed8da81010ec21d018a8e97699b20a249d7fce70c24875a965ef2c6453953422d74e948f0b7616e2d33ba24c8eb374bdf4c67e7f16923baa276ce19bbaa9f640bab42e1ee20fb75ce897b944800b3db14bc232f628289a97bdb2799cbb484f7496b87503e70610b56870fdf4cdd65bdbf83bb358cd5139c9f4a71b89a6e022a23a8b8c29ccfd07ac9515a22f244b11b114a5aa24915ce57904d25950dee3d0d5e9425ab9242b9a2534a02646376dd11e6635d5efa1608b00df4b2885b5bd46004797527e151c128399e93b0886f0d5a6725f1ebca0fbc6b15e5264f6b77cd9dde4157be80dabc67636c9250f091a9da67d687bb9f89d2e97a6558f6aa25f25a2452ef26fde69839853f0aa42c281cfd4b48cb962a871ca50bf307b911322097ed1c721d79af1dde4be8b90c46805c467e776c63390cad732fb78a3c054704ba71e82d3a29b66ff04d21cd09c960f29c74d5f8ad0eea129708332548b2be3f41b8e106b47085cc383068fa5002d20814d706f6880f78810e7ae4243023830249303bceb18f4cf31ac2182b45d2ec6a592a982dd32f70b8723c23953589db1b97a08d0159e96d576722c118e517830ff91bbd311e7a747e828942116abd33a6668139700a639064ff06d16d97527217fabccbf0997a205bfcb22550f02162068bf8c5d6d057f642bd83a2898d5089fe89a288495dfe8423c2dc4b449fe4931f01691f340a69e30b5ce67e3be422be959db137f6dc09140f7dd92425274f8ce02dcd57027f7f8d1c11414842dc16bb8f25db12fa57aa2e78cdb14ce3c87ec2badeb084670dfa0b76de64833892973e5949de787a74acc1fa17b75da3c5483422a1f21e455fa460b4ae18a89a16f1d428700e4b0c09076b6968eeedad5d4ba26511e029314ca8c510878f76d3dfd62003d652a128e99196534dc80f6c176d1134e1e5e5b34cdb0111823b15dfff1729659513d650f9b4b4ed9937f23b9039f98451a8a7252a360d4ac9c9a6bc0a8d1530c0ca664f51448e6ed85b274122d50fb1a05792109e226120340cfe82b05b5696d63156010134981d21115f00378522a4c14817628608d6bf6d6e993ad24fd334a589ce1b1f0f2ebeae8d5c9922813100eb2d66715899caa2e6599e4122333c5256aaa0c519e9c227bb62d915fa04873afce45b8799e7791ed93a682ef132782c9c5dbc2d4c32ec7cfc2dab044a4ca0a1bcb69238a1dee661afb64e7e5d5aa5017d6d217176f2e3f6fd0ac50d0947b2091732dc1cb9762d7ab28f189bc37ae49ad6bc778cb0a9ccc16b174d5c18eaed6e16f1e8917d38070ccfc0af711a8a55852cdbdd4a6379912ca0df795009d81decacb4d01e9358b5dd412d57c329df0bc77418f89f7b257d52ad247099c89fd09d64bd2eb76802750b1ad710059cf19dfbde54db30131543a3a25ab57062863e85b33339dfbca1d47cd6eff1e3321f945c310ed412917ff7aa72a4dfff3a831552e9a4a99c56dd0d1054ed74c214759043be8192f3b7cac0cab410017264020db2b468e08092a99fa87cb1f843b1bc5bd736a1cf0bee8e55d8f92a68cb93b947f02dc62523d63a28a116476de9c22703dac51dccfe79258dc8c983ee112af373a9b5c83c1ead6f1aa727fd0c0005fa79f606a71bc9a1bd2ba15f35a33496471dbb2b31c5f8d9edab9288a5e61aa4c94c048b1cd6b217d1e44466ba58753b1e6279cfa2006c0467229b2d8a755bd4c55603c9afa208460e0f381637dbc0dad188f36b0f4e08fa82a3ffd36dbf014cf89dfe1964319660fd96e4e277b0ae3174694ee4b983350c992eb47aee363fcf1d80797e6e6f8a025795a2228d19fd2b7b56fc3e878cd888de5f9c2a834297f0459e1545e8601ae5be4a2cd942bb97fa41d8553eaac3e21e4b5903bc162123567c38ba0405d1b5631cfbb30b9d829f923c2c4bb1681eb25a3caeca1664ede3149256007a73f14352b8d20863b25b8b45bbc9777200095a533ef2ace720922e056a6f2204bbddba45162c4f3a2fc7e54283aeb43f7781d9ea93cb6b8e27dd9ca3865428c2fff1b02950665bdb78c85b0065322cbf4c5f9ad1326c0acaf6f842e48891cb29f1c86aba54db9fc8e56cccecc819d74fbddf428d0614dd02612aacc2c631c880efcced88040f857ef515e0150da0f6d86113f4c67965477b0ef8aa3d7451a41c931cba40a737e2ef2cd82d407ced10fe82ee3c8494da0af352b9e63432195294f0240bec14b90a754c4504345ed34ed50942e6fb0d8a8b8401956a5f72910ca223384ad87ba011f485c7ca7d6d92e43f1c5eba3e196a692a3b5ca0796ae4f2f2a16dd94a5049008b75e7e22d2424d9d937de22b285dfeabd4da8f13741331e2c0b763e5926ac480888756bb3e0170e24fc3a56bd7ae933d2fddb0353897d17574c47113ccb036cb39d7f9e022aaeb383f27e2e39a346e07b246ee20c69abfcefe9c4d79e1132c7331e0a5fca2e29dacc898436588ef684e3af6259cc7082940c5f3cb999a7d19ac92b44db7f68a8ff394bc4f1d1f0f58890ce6f0c1032da8f915b570d3e61e9264ae57a4a26f517b15206940d8a8f69ce832d3c7078590de8e2bd00a0482b1fa6bf317985f4d31cd1ead4ed74c19c93db21ae36ffa6b3b44b14b02ff1da582472f5b508ca2c7c6f584e117aad2bb4ede136842a48a5e8bb7873d0b7db413ec43a9b5b5545c6e7fe987bf2c4d438d3aa02d5bb8ebad141f34e6e6f33bff7c307088c9de4ca2431d70efe3635b3f51d32018c9cb0c1b32daf8ab465a3e3a225d1acd5989836d5a8feb75c7efbc157ab44d5c38a75e1e10cf4a40b12af1a07830d4438a593aca557013c0edb07cce8978b09cadd0aaa70490d59db3475f558e7c42a2e496d6c2ad0f109fdd4d27c30dc56153965fac17414ca7d41961acc2b09dde8aca95e9b682bf87783ad36cda2353729692364a2f7db996e908a1baecbb1025c96b37182ab14d4d2f7ca61eaa39611b82fc57c60bfc55b7d97d413257a4767c77119e93c8636c578558b177cb9eb0d49bdfc7e675f4475fa593ca0f7cab334870a5b89c0ee87c088dff1b8b9e271d0d6ef5dd74af3381c8af635fc4fa550dbbebb45d9e5a7216a86f3a33652c3ae295129acfd691a3492e7df8cf3051baa1639ee8448b52d14dee56209a0d6a4be9e9d673def76702b27f06486a7c4efabdd77007d876d94f91a21a9f4893c9c835c9f22b65356191e5dd461853f52383c1608e56f490af2677a0d339f06dd88227199e80ac84fdf51e82b1aa09d483c134a3316bc7b9b4d10e64b473679a6344e41acec55a4e94e1baffea5663de9f1d0bf8031735294d6d1f72b517720da7248ad11f0d8cfc1ae4cd2043953efec2768bebc60f129c83a73c5f6af60302b97f47e413f8e9ed0eb950f087a72077cc6cf27a7d6b0682f99a9ba8f51bf68f3353e849b74f922428520770140f58d9879b0fdecc5aacc36f0a140f223807e5f323f1f91b75a913cf2c23258fca230af1bcbeface82c88fc29d3629504c5e639dbde4ac3883d9cd52a847ffdb5f59e15806825872f2a998c51367f8033af2733b6f53e022ad37f57a3781468f2dfbfa6a90480e6ef734d5741002dfef752d04f5003b0e5ea31a15ba19a358f52f715d38b2d57dd379f2bdcd68d4b31f651ed571f0cd764925982bd7417435c7aec98f6834c9d2cd486b8baebcf90ad0a6a96a253ee76eb8442cf0c4d02f834576d177f2dfa1c962fb26be29ae5e0f4878b4db490182a50d6e00aed8b2589634ff4a832040d439a0874cab47c8e078fa4781033f565c7513024c6485c98843378ff194fb1d4d07f47525887479a234241ef2975f20bd1b6007d720b3b9aa858d620a642046035800cf7461faae720e1904526e9c9e8a20bb54ed0d66a53f2859e3d2797fdc7fc2e672fb75358e54a5819b3315681d98c9c93a0de7a7821846dde0df524cd088b3714e6e5f9182573601df9ce79c2fd5cd0549df8478e4f58819636fbba03d98264f2425faeb78ec4fcee8d17d4af8ad5b08bf818d1e2818b01f911798f1a5cfe7d486c4b5cd3bd8758ef3bb46e4e30fd275ef093c289ce7d044b49bf154fe0a015d43447a75c9844777dcbc7a33233da6093ebc2ac2d06ad5573b5d904a29a1912c2b2526e5f0cb0cfd81a70d4383f63e278b0a0de78c79958f082ec58ea41a3e1782cd90683bbb960c2b067bf70e879dc760bf904188a206b1aeb1b885e4aed0aac3b8f4d51f0e3d2163bbed2fa285d546c2a2afc1ac1c7988ed81eb20c25cc6be5ac1a4f4bba10d4bbe86fbca268de52cf968dfba9b6aa5dace23ed3eed60cade533251de12fb33348e8c82b50697b4540baf6b6521d9f3c4f7e1bd68b3e9700e2be49589f597a2486800c8ba86d51fc72429f652f221fb68fbcd49975e85172b1d4558014b60fe32e32247c9f5a1da5824aa2bc7b00a54270dbff28b6925ca497e5aae4aef173d4585eeee7ba8c9204ef4394e9c3fda724eebfbff02df1bb5f090fb98246f8191659732ea9e00c103152b8ed7d568937e2e80489481e7884b6efbc085a80620b4d0e5d47a8e50730c2fd9ab6a30eba4b5fa54b9828e1c82c2d7b947b022c79f87706fea9f351af58a19ccfcb51318723cf5fa0c4a209e25e8ba81c26365dab0e5e2c31002d2566007ed18f5f43b5232f23a7cf28345843defee56509ac4348e92aee8040d9d5160bf3b5d84ce6884147cb25b86c6b3f9643cb3d986a7f6872edf912258f1f72535f90cb5e876cbfe963f095424fde4622515b3a1b4bda62d0ade043ee76a97edb44c802f284ad86b98f286010d030cdd6c2cf37024e7a1821dec037e2cf33a9b423f8ae1e73700fe9f024e267a25d468600e5159b90a08c4df9847899ba208d9347ee29f4f5869733c24acdc0da25155df253a86cd39e8781e4e914f0cebb4d71a72b768a84f23870df3595a3a24bb650d1bdf0fb2e0e502ec9b1e80645886f3e8e3d16478bca866fcf1d2ed25a472f3a3f351e211014042450adb6f70269762b3f8812c5d5bd88180cd1a7d2c0584afaa0e666ee17ba1120aa4876262b78bda2d9c7c473f1809129506f406da1b3d1fbffddb1df9353844a4f67f35be96d74899d0f130dbb74cae761f9f794efa388b31afab185d12343c5b3a8b2c298700af25aa449aae5bc0cecdd0a5b889072e81fd59bbd4de37569b43dc62573ece4d97d756fc8b155fe5b477bf6f0e8975c9682798141b68af5412cc509b73dae4ee3d07541dfb8864e5b50eac3f437505e51547a58eb0beec11029ee565cf0000a624ada5f6122d3c3347c07da9ca1e708e881aa2b1f7a9be77a45181d6d5141f03e6e780ac3ea8b0bf1fc32bc5f5d7b3be6c1a66c402bc690082168067b422bdaf9b8040ca205dd65a5a387d351aa88675a05912245cb51a0e204ab280b853bbace330d2fc967099be0fce8e15162feb3cb61057c0703244431f9e4ffbce0ab63cb4919fcf614b227f81017999cf90ee660b2b85ca6c05099c2d35ceac26a3067e0e2d7fa601c801fde6653979902a53b01f7618206f9b72d035176755d575b811ae6f461af08a2cc13bed3aa1ae280a253b707da9decbac24041c95503f3681fd1bcb8a808053a0ad483b6f9ecf50a39e59ecf6120afdee58cdb880aa3468acac968227051a8ae6a867b0a3f05b41f1f343d57d01dd00f17b973cab4104e1257b3dce857a7a61352558b1d723554904607f6f111fc84b7c1215a1d59f42c56914d3576443908d7db98f4fac25ae3c32160f4de4dcd87c17908fe1b22c692b3bab8128d4f6f32b63ff630646151f402f4f5e837a77de939bae6f55b511e1e2e919cd308eb96c0bae738ea9ff9dec5f3cf759367287ea2e03f79befe62a9c8fde9c8a8ae0c81176a35f5ff036a46beff6201deabe345d5031b162af52efc4f0ff91c309a336e0b3c302a9808233e68212f9acb140a48b2c2f73a7571788595f32a94f3fb570c6f7eddf9381647fdbb79da362c831e55dbaac3b1b1fb111847067a2686a282ac66dad1ee5159ca2697b01df76618b8f25fccf758e0e3aa03503dac3ceafaa20451942b3b81b86e72bc1092ab856628ba6aa85e8cb453329a4eea2360c4bdfcd4f9f4c72ecfaefabfe3f6b598f8694f876316fa5da704c604566f80d8f9d89997c65c4319df205a9ac5567f1f4e92a6b8c44c7d4f41c53f4bfb7214148b0a2ae449f630b9872da677880e8bbde9591f67c2b1cec404f2c4a7926e90bcad181409fd16d539da1e5fa4c2d94299f5aae18c559cc5872c557a5470d54f56f556e397a4570c9f6af2edad9563eab27cf19583b6fd1333f3e76b011514bfa87c456b52dadda28e185fe0d792a4c7ee871bafd23f9d24db3c998b27f6e10b83aa8dbb8a3bf945edfbf49f9d0f49d6f75a131f7ef891194c3abb56d9bb0c6c3d8ce72340b341a69a0f76cb111453f7b0115aece97073a785aee3f93127b4e8a404ba6c8dc4e393c72ec908556e046e2c8629a9cb3a0f95cf23abebdd1cc37043c5b873b7b7703ba22164a1641a94ce75d754f5a0f16f6191a85397244468f795674f8b152dbed2b5ebe857de571e2969d194b527f9208c545319025ae348c0096e4fa92e3679dbd2f98dc3c288c4fbbff49cc9aa5c9d5647c49b245ef3158784341c0c479425128ca359684d64d10b86c01c8cc10d1b852271f5dd190996f9d1215c437aad1f28b1b65f244b969a50b8f1e23c088d64eac2bf0b05db80e7cd2d442a560fdd1d0297a267d0e5e6bd71f4b5470f41abfc3716fbaa5a7adb410efdee85d01f3c4e673bd40b8930f0f3032cf40d32e07613e9114c669bbe5b6d726a92c9b219e4d68f65cd727ce8966f2bcb486dd0eef43e4e6ba617ddfcad1c4d759858c97005733fb9a087392ef557fbe07e6e210000402c24a3fd933f26db61d6ce54b7491c848dc0988c98839607b60326e6fc6a4ced0502224aeffe478c1f58ebd4c08b4b731c8b7eeab1011bdaf5004f893d715bfc008c9bac029eb2831f2f73a4f85cdacf32286d552331a07990444b6424819a017a9de6b50fc10841289fd935020d621781da124a45649256d48a3655e26d4af2c9192b638320e1453a8480d176902897318947483eb539d236a07e14fe3c567068c03071c0cc341ffbc718f40a760625ff5a1df628aec7f87694a0cb679e5def3e6d28869cb7e1eec9c32f60e3f3809ce02e74d3c838085b961aaceca8606ee556ea5a84e999fe29db213dbf4db4d6f91b94dfd155ea15400d7954b5ffe402bfbc997428c0f4c31231b55ff2adeab30060a99ea87bcfac4fbc9cdc3b0c96d2f0376606eb4baebde5bd35e49d64aecfd990a78646546f4e19d72a87b2c2187a4ccfd76526cd55a1a354bc2d4a3cc5ba8598078ff2e0354efbce1d2085c135df55b054f57ed067171bad0adfe166bced2de9e08b87c79e027c612063a4a781cf1cd7d7925c4fdc6f77ef71fadf110389078466e9ddeec9022044cfc84eb54647a2f0295ae3e5707136177d01a8177e6d22629cf59e23ad67155347c4ee6aecdbd8fb3dbb2e5b5e0ada986b4e4fc490263d42c43f219200c5900555c90525d7f21a401d9aff366c47fb2a55d51d3307f218506b00a3eb7db46e92396132be948498b4e09fea66fc129bd92433ee099666c04140eb3d3a40db0b9752da62f357cc12be51b93cb1bb4c093fa2337dea1f08908d8592c13459c35cee005e0115ee369505ab5cf15b5327b73a0aabe275e798fdef7f106be94e9178c317c1837470668e364f4cb1f860b88c9e6405b122b6d1363da6c2010fbf2ff4b0245729d1691e8b87e1b2a6150017b99c837fceebbd4f861faf9c537629232da5e354ec0a35cd975ee35f198d3b164c9b2de83a3d881665b90348a81bde33cdcdd26d911abde330822ac53e368a11c3773dfcb94c207b592b9f7c4e3d586b1373f62736c694d3fd2099910e3b8a940890600c1a48733dd9d4ed80cc1c1d9426e8eb91cf0cf20807cc3adc4dd8df264b544afeb1c6553858042659d405401a556b6738c34e5b79b01706d01f1be98c660370dbfeed5cd0fb5cfcd8666c74a9b60f40cbdf98d2de98b0d6de9b80966e45042b7e42a4de250ffb0d1800585a4cbbff76aec782779569977863ddc3f49e2627255ab20484f239b6df61f1db472a63bec75dd1c900c48950106c2d0bac55476e924a09125af4f6c7d90b67dbcd973c2400e0d457b337296e187d07befc3bbe9a29a1c92612a9365e3a47ad14966920615cdded8b9ad903fd74c0c763a58170ac0ce003287c70ddb6e7d28a819c29ed8d1e13ec45821a4d66e1d4fac2829bc3d7459f2979c2562736f32e17c8370554b172f44e3c42f1814d34443b47febcd8140e4f637c3c9fd504f899009732f8c60603122ae2116061ea1a55418c0414bfb4f0cd5f0207d1ba509136faec3985eb442235221984c46f97f2326a762ac78710701112555c8473d56a5992f4ccd36c5605f6f12ebcc72d9957acf14b4ce726e1c13382d5e9f44b6d49f6f43c5113697d2c2b867aee311bae67e6878fdca2c9f0da3a6af54fd81b570e2c3574ff17f3a48fae26831cff9570f00db95563ef3a9300d702dc651f94b2a5911f52335ca44d3e1ba5752865be2411c961b12791419c2709e7c1abf87f6a2bc70ec1836aebc8f8900584e8ac4746db68eafe5726a2c5775edadeb6ec3c7898866d2612720f45e03114183c253391d9781ed5b86dee41dbdadbf227229f1a5928b37ab9eacda66a8a0046a42238e158edd614ef00687b586eec44b87deecd0eb67d96bed51688668b88ad2bfe4bd7f44a939b2a004d79c1abc6c9609bcf20cbf740979d1fe179bdf0e791eb5ce40aff1f711832c9b4ebc55da4f4446d1e12d20eb1ab555952106c80714ff97973919128778c5e23452b1155ec34f76c1d23567ff5062e4f43eb9eb8f92076ca0e48d46885f82ab04cc385f3fd1e8034359a2f452117b011217bc3f5358b1d9afd60d70cc5bbd5e92d7b8282a1067e433c8d34d6a36664c9c4cfa199a5c9b07c2157323ea09e44b88b8231b9d7167dd0e9109f3b4b6e96a3070f0807c25e455e3c6fb89fad64cbf05267e2bb05fa67492a3a2bb811eeb18373bfcf663c8d26239458a0c438a4733facdf9fbb950440338797471dff353dcc6a1fbc4a01e8cf23f08e79c948f3562551f8d01e595a26c369b7d0c8837100eb0177ffe36b1c250e4934774d8d9d05d6210b0421c2df8619e7b6dd03b516aa43d0f0f2be0af3f54fc95a5792065778e5b3060b975150c4644d181d44640efa9d80cfbd106629f1b65a2322623084c60955691a2901ad531f9f3e221401569b518763841f0c2548cd2e225a5b00222da9611d8d2d88b7314a172b07a02d95ac0d8a4e14a35524a8ab79578a6f03d19c86e13cdba7be2ae2000f212dd0ef10f19898d124363fad30fccc5fedc03b16a17f8f9946b513ee30a1707e9abceca8bbcad511819829e79a30e7a30c4264d93aa0de914fdd2e5a0f337d7b44b238aae01d20cd8369774972b9f2d6c13ce266c93a9ba91f2e519c025f18bc47fde670e5a86936ad90d79a5d416568c093a8b89b9db1d8e15df185b46edac6c4430efe9ccf2a499bcd6ddac1c3b3475710a501795534aea9fbf81db8c81a6cbfb990f1d6492e9b9aa8415b47a3cb9693281a38acbbb65f0e3162f5e3a0687512d433bc4cbdb5c90068df7215c1cba215c84d01230669b85716d08e9444328175659512406b25d2084998f322e5f7ea02576ec90381fb74064c9a8a7d1286fc31376596d90fa46c4cc89dab555a69380b06f8168c46d923bf95f7d220fc093d683fa2688b151013aaf187a4eba4f3d4604b9b6febc7e593071a7924a721ad181caabc4e97429069a942e54809aa69b47e028828281b4f897b7992890e2955c2133a8cb4e4d2482c5d8cb58bdd058133924061716374713bdab3ad663ff3e0ef2628452fae5b0b426c4e807a019ad121e94a3e7b1d6b819a9a4326175bc0d35700d1b4fe3aca6ccdf2d509d5a69b08f1d5c8d40cdfbf5067b7b3d886e67b5bc9de6813b560a4a163b35af5257a5c1f9141378f4d64238adc1b3c7fe02df50aae9abc604aa561128096b011f7774bb231674662b6ccf04617027f2f6f75446e09095853f97260ce07665846200093f5cc6dea1b36bfd04525391e25a96308637ba28c8c4f29ecc44d8306a80dea2f3350ca85cd852b2d75149841b7ac491426c79d211791d86142e74e093f37958705db04fa2e1f47add0b1813805b2a215c4919eada6f2e828b3d9f03a88c3e0703a98de5f1dc69ac6094ef4af9f1ba67cda71922e8a8e583535acc3221a66cb1b418d36ad8764066af244f9a9a8b0add4c05ffeaa2cb884f241c9ffdedb278ed09c23eb93d629a16cdfdf83459480ac9203839ce9583361e028b0b40f78a7cb98cf1e8056af46fc3c68163ef8a77dda07905b298abe793100d0587ba02812db83c7019d1eb2460e4f7e532f7a260e923c8e5325046972229e3f291058ee965264e4ad2021c1b8bbf4596509f5c5b73bce23b58b5284be77b23735095031b4955c7f6720967ce885b857fbefdb1c1bca5b9088340bb663f9c4406ffbe3c0c34d0c406789d85a5e5c9bdc375fbc7c02cd1075b4a4c17b0b4ec85a3e737b2483b7e25dbfce8b15053928a14e9447fda796cd42ca4c2a1a38d5b99c3281a1900de02dc1d0b42c01b974f101d8fd7283f6e1338e4a0b04a3083c67278089528fa6bec91d2141f1ba5e000c40a9f1e97defaaa10091c0b998ba01ef8294243631d064d00e24b5fa921997414679ae1f87bfcbc6536275bf4995992fa4de542dbd459f2aa946d9f28c34ab6491de03388989caeac6ad0aa19453e72dfbd2caec5dcd90d77d19809684cba2038ae29cc236afd97596d79b13c8eec5477b71f922486c9b9911936abb9b2d6a831c4ca2eec168865fa1b37780054458d70c74a6dc008dfacbc013a677388204a7a1456c0e6d233cd6459a927def86db6ee24b1b3add78903a82a68d0695bb713c81a81faa34de2e462e23be3650076d990553b987cc7d99887a8e00a5d0db7dfeb397342dc5cbf56d105c6b84290efc6334dce00a1e5400dd236e40c9ae488d3a0865c0962e55931a54abf6759cbb436748e2b5c766137d0dac43b6e69144e236f001f47f1731e581b34ce9f849eefdfb79b0d0e9850673eb3bf4fd0eb080333d8559128798484e03b4df55d666c51da65b21161003c90a2ad52102c93b8efba14966a9a43a74f51787bd9f43bb16aa1396637584db90392a946ae42c2fe866ec5a0a918f09bd4122e36c0e3ac4d2af866ba9c022bd7b9d0292d92d459c53e9ad0b191a6cc8b0ea5d40238c0a2a6b1ff6ea067052e3011c1f3f2be098ae43d141248a209aa168b366ce5a58f41af04c86c45007ae4d0ba732c6b45ddd02d0896f276d72fb171e58393382b292af86585ab1f1155c32c32d74e7f1cfad631d8e8ec82553a5d615334ab97b64f35e2f9b5deab3b6cb1554571339adc8ad110059b43911247fc1023e6449620cff73d3a5c0c8c0bcac2616f872c54d981db99d70a25c66e0763cb9dd264d7da70897675d1b56c8948ce8ed420ef9a223371c01758d6ad2a74ce035d8a5e2bac3254973bf5ef12c5cdd5b0f30b39183eaf56b45fdea02a50ccffff585464b43083dde855473eb17bfc5380261ff4dd126d6618f132c4326a3a72353246c7ba89145bb2f2c8088031c32440df1136873eda8a5f563356573399d2a463042da4f48145e3b79c60dc8e09d1119dcc66dc6a361f602d61d61eeadc5aa3d9f4748610c5801f12c2c3924532efaa1de4f9043ccb89ef2003dddb062d14710fb89cf8f85062752b995feecb8f4fa55848d5b58bb7b96f28c0ff63927bfde78c7b6f7a19701fca479572fb3a81e63b2ced063f63ecb2dcfe013cbed45a09201d3a39dc1adb4f4597a169a2b251600a78fddf28c894b52517c09beb3a09231ce2b2b17630a829b36f5d2bda8e34f5bea21a23b4896a1a1b1235efd3be7f648a51b574b7e268d6cd3aee10d559688f6f6cb61d579597ba57f23816cf2aa52abc64c1dc425a8315e452c70438bc7a4534d98b5fa41a050231b5d87f549c543c8df28b00ff47bc3587c173044c41f5a010f3c43677919d72988f9745814ee5343177050c3f7a55f159f2000c41a7e26c9604f895fcd84643342255eee71855766c2a46317614c94c25a42dfa8051515b35a4cfd37010d5aa168ef02b5a3ac8c6cf8c4e5b5743e8bb9951243c100e74aa819c213acf9eeeebc3317408bf1cecdd5ff12af4bd2bcc51efe149fb531671f4cc3f415ca15f591554703cb5797ff0140424fc2b7b318192122966f2c606aba06f88f050c4a87af0d49e2d55e97206b89aa0dcbbd610971316b9979b35dcde14a0d73d0904eb02707f654804cf9e9d689709d40046b7795e8e90fac78c1757165749706a94c36088901173ed7f5adc8570d8edd89135f8d19229e7b281961ea61538e41fc981f3843b4f27ecb04a1fa1cb0408001c6605a5809974e913da1573900421b90505375de709d0c943a2e37cf7b7638a0b7f3d6de602e8e405bed211f9947a4dbad6d22ca5053e27a1e91931aabc8f84809a80b15408a5fe1515806fcc403334535869d97f37b9739deed4c399464dfb3151162bb1de8e15ce2098d1f348240361c031cfc46564606358eea5fcba21c5887eb22cfc422930b875f86b7433c4b125ceed79cf476d9cdbb16b2c362f595573bde2e70bc9b05ec447dc536fb1dc8e9e9d5b28122efd12c72c1bd397020f155dae9570cc92fa01d3ea0c6b9843740c4fee2ab4cafbbf18fd5df0636fac1944d48d75a03bd400841ec82b990512d1a636895b01a02665710aa16bfdb8fd4f5891c8806491894fda286d9c3c7a5d7956cb1d37761650fdb9762079a54f69d03497f5a76f4b57d374cc5b068f89f56b387880428cca3039998867342df589d50957c7bf0498a168ab06cd8d407b8fea5001fb648dc72da9c19117bbd37f54df0036e6e473ff262aac2ddc687fe309f1ae1f3d0b83314440f491f47fc40db7339db31b2b14e3509a5a00753866813118d05210dc14d7c19f65b1711621e83ab1f447525f686f37a15db77d9c2e93eb55eecb653b24edca375cdf7edbec90a67f7779d6456ccbbc0bdaa099c080fe538694a99dbd4a3ce5658d63b7e6f08c094f254c1d09586dee9f721e8c62ca6bd101c622563dcdc4775ce4e7b96b2e1a0e1b2ecc6870f030591a2a332fa34e324b5532b78ff3e3684c533bec8e22dc23015c0e7dc9eb1092f3c4da403925536acd08a8124dfc44cce70cf8517a3cadd4e43cdd5aa5237b361fe9ea9d796ac9264926f6b2d93fb41ac52582849ba009b7006e2baf8e08b55edee3178c44ab01a64c62f99ce07b237f6de75b98a5bfecd6b851449b22f2b130a8ddaec456df12f3ce21ce42770a3dd2218d81455dad7175c31529d8408e09290483800edd80ba99fd10485b232ab25b8abd3b82f56e5db5b6faa15e4d3e8fb665259d0b90b3e02eb08a566b2c541f4839957974e359239045a4922983fbbe5674e70795b5505002c55aefacc09ba5564307744ac3b4dbbd643089ebc0eee208d60a2c8eab6a98408674bd21279a622eea31a539632a4e6a204790860e2476bccce6cdf00269da13d97a5c03d5d003dce7672b287a5a190198ce3b197d40d5ac940c4d486aac9176ba8634e6658450b7b67eb43d27541cf94d8fd009c05d8be528e3dc4d20a23ae573255b196c30374ccac8ec096f7a0cc671daf11284b76afe148f0561656c7ac5fde4f699bd4d34059b064ea560f0987a1d575ea9cba7e95ead1a2ecb04adf137d81ef7b8225c1c303b53818a638606b25d5102f2c48988312361a6c97a74f466f1d22bc12b51070f5ce5a8c58f2838bcaf26ecc32527d1c3178b317605d289ef3aba08b5667112df9eb945f47041f6fa6e2d385abfcbe0a906355dcd59745416caa7e1c0625a72366fd2d52cbcc38b2146769196e401f98be439cb2fde793adbf0afcb5a7a999dbd72b3e54945e801d84b9fe73577e526f0a50557a036c4410ed84d935ed4290ffbb72cd069ee5da5347a2f4cd8e15f0dbbd81f18e9b4b937c83f9223baefd81836bfcc80cc0baea80e25d3399e9e403be01a354ae1fae41d0948c56068cf683b49bc77008f04ad56107bc6b0fcba0ab857317bc808fdfb8f53f6c05ea40c303cfdfa733f63b7cfa3688fb24c433f687017f30f147bcba766705a5d738431d50d3a16e5e6ab4e86e057648ebe4f083e5a01821874604dd452e669fdab6dfcde74338267b5a3f83fe4c33552d70f985c87d6d6e172aad01d8f61328a1456f3844b4623883fd5dfb7289173ecc3b7db2caff0dad5521092785e560080e005f6fd06e06bec103bda095508db417ac6ddb02b6d5d861a12a1129cda50feef7c62ccd2c68f817d5207daa9e1ee7ae33a0baccbe36aacd19902f454aaf3230691639e23b94aa430f047b14aa1796a1a0905d4fdc285107fef97eac5455dd700ef86a345361a30a8dd37ea8a9ff07e8371f542dc1ff613625cd1e66036f977d5dd74ba4d577de4d362d41ddae2fb611addf16557e999db2045bc99e45be1dab745796524ac9d58c0280e961014ca1b61af51ca97e7a5fa23774a4b5542e1b378be4389995e0640a5bae5e0c98bb4be0ea0421e34f486e7d335150e9c88d69fb65333b10c88d30f357cf66ec577077bed9731627df0e16e18731d70cdf28cf048a188ceac964ac21d101086ed720e6c770d73134a45bb56aa136e42d9e33a228f63837031f8b415431753c25c31353380e6212b04d285ffe72d3d9857794c8282bd90ffb448eac55be1f2934d0df015631890a8d80d6472f4e8ed78bd2fbdad09fd7c36f038be115b94298ed1128f9d8a005caf20e8dfca36ab62a4ca09ff178d3415a003cd753abf8a3f553894dae2cc4f418c520f33f4992c836170022b04fe21d12f9df02db8ab81dc1184411068dc6210d99311c1ba9fd75c9878b8cb4a653cb81f1f987806d6b57b423cbed19fa8c015e898571c5e12e78f1fb1a4b3e817f02b92240f8c4487247aacd3efb4a24de6014fa13f264bf461475ac3e47d2fdb5f9891dafd532803f350db67c395dabba2af1e8f36789b1ee1ecc315ff06c9e1b7f624b079715e96961b48d1d57f85fa9c6b17ee6b5dc5cbd245f14d72383ec7b5a03cc8a980243871599dcb8213802ef2a8d1be5a95a2d06586bedc3000eb59cc7d853d3b7d4304691903b520251b65a6101ad83ce51deec4e000c85277ffb4a5cb02de2a2179c3f0d061c24919f9e0964f946cdb7cb73281feeaf80e5617835491dccd24d1c51b40406c6e6ec14681f09b87853675d2c5aa9222da3b54d6ffac1317a84d9b8a0e89bb667ac77bb3cd1fde0e3826aff3a7599e63914d3623ecd044d87435a0a4ddf77be06a51a369bdd06766da9418060a8d44c0e5fd8e5a25ef1cbeddf19b79656e87151fd8f234af3b027c4e3145394b01c92bd0fa20cd2218ba0733a431119dd99d7bd3b70286eb6bc8735c8e1d371cae62a711f1e20e6c1501a2e25aeb2bbb258df5514e0e297ef4acffa5e1ff727d344448360c4440947b5196d9da9edb05490d848937aa98d8e37509be9b005727c29a55e3ebd992c32427da50808a3494268629c8e72097c065274ffadfd08b5f76e9123b7839b8e70b17d6f98f6e1771e112a552345147555435628fe7a16718015bc11cc7dd4ac8afe2c1d37f622d9ace927157ab781203559db4b7ce3cf89c94a9d11da74ae235592462a010459a17ec4c49f8aa84dffcd83517768849e133589f7d753a4d945124d3566321b856848bab3d566c5cc4ecc1d5728d8d8480da0510bf61a617c465d541f4624d1659ea44006e3da9c5e4c0d0abca140db44037e4df4ca0588c0712536376be23eacb6494dfa8b1e6ef1446290d964fa6464fc0c7636cf406145aefd18c9cff5e56839c88752bf34f92e8f26a78ae37d0e8e8e5e7aa3d5295a22e2d18b02e03226cb8fbc6f841f32becd80a2515e00202c5ddae57a215abdee549f269f186900c71382972a22d67d773f6b0af91b2e0ee098ce7dc4e07fe5311e60d8e57c28cdf50b79f2d51463a9548272bf8958bb03ad94820f8915eba02b5a49f731c3c328cfa3a0a29e29b1e75c9300cf11c4354a40d87572c3940c6c4fd1fedc47a1bd5850f10f0820e01a53035a12dc3c0cdb7e82edca8cf9f525fb87f4b3271a0c7d244ad2efbf375ae175a91401eb0b8f0d5e9125dd14d41fb4a99f4bfefee397c634fd2e664c92e9404be41a2d953c8c1520b651242579ce5703480d282831a64c03ac8a108e0f117761abb5957fb9f0d453d510924a2fc898acefa1065c74d62dd220acc4da693e81590b03f84f53d87483da5c95a28d335700358af3d1b4b2d2bbe670c6f36a3fdb03b530a3ab4124dc62705e5822bfbac53d0ff512623327193f1d584aff5625fc1293a27a3c2f7c9661aee55681ef62af0b8f1e115073d610d4cefddb9b412e631b12c6c51638238782a4aa6b415bf230b6fabe84472eadefa4ad3e5f5f37dfbdb5360357c3031d359a3d17a614cc292bba7339c1c2333622b42a2ac2eb0434233fa97ff58e434051c54eeb599c94c2ca933846e0a883e78f11e00aa52e8dc72b317f698c6efc8303c32a7230dcf6eda8cdf46a0bf82e7757bf0b9980d20796ea593e9252defef2f3ee8bb1196a67b87de9b04b3fc0ff114e699b78af64e644a169187694a476914b970a5be49bf01764962df8ede7ab60f5ae390140d157c9a844c13f170e841f362995cf8e35afe5595f9d890561d99266bffcd3c29e1656834b1675ad45a3703621fd24e5d4ab0c167df577ea98128d1d66b36bdb742918218fa93cb855ef7e8ef5e244e0cb9c6d8f7da37e44dcaf1d37def80fa5576eab971633782e42f364bbb9f9c0cc5f6c3710ab37763eeae98a78107a2df1505ae6f16cea294c508502304cb54dfddab2c94c22d251351f9915eee4af9e24f712fbf45010a59dc2c69154ad0fcf962f48f17a01971f81221b832d12fb61a3fd72435c3bdeac5a64694dccc7906be85058d6d9e6a0ed746aced691325c8c4bfa1b5dd4b757dc04613dd38121538ada05c4b4a064d0b06e3c67f00e14adefb556256a926e21c2f0de4b99670101ef226b25540c9d3cc41f17b8c435ff63e821c11e533a372375ec3e53f9a3898802f2ebde11449c568d16a5cadfb00951359bc711f37343374fdb6ae89abcb6f6f11a2ca1cf8dcb3f090c88b5813d63af18feddada2b27b7c9ec8fa5fa25e3488c5b6b148e39cbb576eecf4913bdc562892af487f42fe2c4df9a875b3e251007d29d8f41ee57be5117a0d3a2f1da1c1413b013cd6ea1eed81b3a960ef886b64be8f190663e77d0c32e7c6844f63a0dac750f76bde94d050b0f963940afaa915b173687035a32d3761e18acd31401ad39403ff7f20fe1f3c857f5f74b74928534ec00f70447d9f393f2811dd89e9d2d2838739146849289592f5a0259add664b0e8d23aee7c53ca8b202fcb2fe29d90da28812c44cccb73ff16af6599267079bd604a169facf3338c768508323fa68a11e6ceffdd843ec90afc6462ce8dcb86d8d18c8141a24964a4cf8b33a695d68dbfce1855de3385ff3b4c6376c58b692f65b7e038904d684bd44eba10e429f02aa24c5c50e5ed34a374dc20ba984dbc23307059a15f8da91c6d7016df9ea803527e9dc832116968e746ff5e0dad14b88c1f4748bb86213703f46da44bbd160b216550ed64527767da4e290513dd30aadacac36404de88f6313fcd228911fdddd04ace86937535708606be26ff7015b63a5ad77ddfd76b7a261ca5b353f0a19ed5b6934ff95a24dd15e51d493ff72346808c2d24eebf62449629e13182c148344482684764ed0256223ffaec2f3f79d6f0e06779f122a711bc5fc3aa59f11ac9f98d499f180b23e4ce2c076a0125f91e029d15329969e94f8fd8f7d83ca1715497a62c25cced053d07348edceb2fb5cdc21cd4e80a1c7423ba9ec08fd2f57ad39638a823c1e2145ed2ee3845f8836abb1b34f3a58ad720347382758918a7d1ef4b20aca11ab7b97b600ba853ea4e699012a78906870ad2042fe9af796aa8bf9c2776642ff1b6b2dd3d569e7f10c42ff8f0f784602ac8a00ea701016402051aa033013208e1899529d5051e9e044a4842352ca0982d21879a7685f809456a694371caa965584d0fffff6fe965246cdb26ddbb66d5bcbb6b5ffcb68f2074c015d0130019ad47a8b611e2f994a62c8c8b48d2b3bef43c1c7a7659ba66956317c50c10182040a2cd8b07023d336aeecbc0f051f9f96746b6d6eadb7de3abd53fdb27e78c90af8695b565e1c7697cc9e668f50c44fdef1fcc0ee9614d426da79dfc658a66d5c79c1e44c8769f18f73bce46331f4f08e0ce652729b9661e8e775a8065317c34cded16921e238284c38244688080162e3a0e9e3d3825fa2b1bcc440f98e86fcaab95f1d4df84e7aa2e72981984e0687d59575edbdb7d595756d5bbfd2da5e56e5d6f357d695ddfa6555efd62fab924834f61d8deffd799986811c063d7fcf751e96691bce0efaa66598d771fff9e775dca6655815b93704c5f16299b66daef3be1014c78b65dab6b9cefbc602ce545fab0fd6e7c6fb799986f988416ebc607847f1e33a0fcbb4cdc5f186a06f5a86791df785e01dc5cfeb38dfb40c7334f7ab23710a38e3f8126a97e2311aff4c285065e3baca3a2f2cd3aee582c94094c9f31f234478481365fdb22aecb69c50a0489aa8cbaaa8af9a49fa8926fa470351265489acf3cd37671d82d677f850594a1cef2445d0a46aa81b2a070a2e2b7aefbd43d0fa1120240a1c409040c102060d1c100f09d2e183891f806c26b8ac58528ace79eb9c75d679925af5c9a7322d927baaf13e7642ce6d816165a91253a63b8a15b7a4df8b09144927d7790f8a65da465e301d321d52044d74451cef44c165055ae4e375dc993f003804ad872269baa348d9ca89f19d29f9e0b25292ef54077534d5578d7d490c0df568a408aa9c9449a5b29d4c0d490cc90cc988c48624c7050c19aa861a62c186851c17306400d110b281034807100f5ea661dc874f83db4cb9ce43b14cdbca0b26673a4c8b7f9ce3251f8ba1877764309772d3320cf53a2edda00e11e9b84dcb30546b8deb6bffecff9edf7d739df76daef3becf73cf9fdddfddf7767777bd35196eadbb751759e79cb37e93c4658aeabdf77e32454d73bce9a35f59f6392b26a6b2d4349132d8ddc3fcb27eb8ceb32e2cd336124db7e5f2680fefc888e3c5256543b0c25c2c294a6e5a865d96d771f93343b07a0a1449771427ca7e5ec7919b96619765e2d2919827a00d6d0d417a24038a3d49d19ea4e87d923e3f3a8af493a5c931a8d8fb72bf2ebfdc5d8c63add5abbed3ae7b57777dddddddddab57afeeb56e77afb57a75afeeb57aadb556afee5e6bf5eaeeee5ebdd6ea9fe1eaafe1ea8fe1ea7f71f50771f5e770bd70f5b770adb56a7777ed9e5f3f8fceeeefee7b53f6b22acab1a5262972fffce8cabab0ce1e491365fdb2aa8edb2aefa1913451b6424bd24c53e0c718e32fa9498a20ca64ebcf39e7929aa408a24cf47e8cf363fcf831652faba24ad44587fd40ca236ae1e10ebefcc7e6c4f5518596c2d879ec4c24f28ef80c807de31b890902974fc00928c25ce759f822906a08d9b09519dcb1f988208ac60438c74b4a93c71365c5b0faf0e8c858cad442c18b1856d91999e1a0329c18305eb868b14204850900e908071501e283870e1c36846870305cb87cb5e1a42315440d516100c0bc88172d340e09cce525062c2f31909e034e88a09bcd66b389303232b2d984301d0c8bbd29eb7558767d9b5551f603a9c9436fe3bacabab04c0b7da2c0cf6a19765955c76d9ec8de94f53a2cbbbecdaa28fb81d42479e86d5c5759179669a14b13057e56cbb0cbaa3a6ef3a489023feb75dca665d865551f26e9836299b6915ce79d1f0597158bde7b6f110f4a9e30b2ce37df9c358010b4beb29438de498aa049faa058a66d24d779e747c165c5a2f7dedb01840813285400ad872269baa348d92a822a34f6b69e53f41f7d2d178cc9e827cd1ea68b634976643017cb7d14ac9c6db5d67a5bbded761b8810a864877c3b1082a201931c042000c010c00ce5d0681400061530bc448c5431180a46a330200c060b828140200c0a0280814000445110c5a12009f24c510db312aa6e3f8902d5808b9f95de29f4901079301cf2b1bb9057b766b866edd9dd5c2867a0f8c70074d3dadfa04cd38915d3a0ccd3a78c43d495bb3438cce59a76dbb3fac782dbe066bc46b7ab1a94c9eaf20a33d85f7b71a9d0d50d16b89601a7fc13b228a0aa012a6ef739d1a3c52ad7d6e3ca690c647f40388d86e46fdad6bb1aa09bfc1d6e67d60c7ee3ba01ad5222f6d3ac01c37f0f280c723428d7e67c186f2278de811f458f9862c9589d1a28936d9d9c0e518a7ae4cf3fb3a7b8013a5c56b7c35d72aec73ed944071691d1a98eafd99035e05ebe4119064a40d480034d8302240d8a503428fb8c6495c56c6421ea236bed28dbd700d02ebbc93e3a820d565c6419dc54fdb1e08042139d628382b9d3d6b553654b3b8c65d1ad9c6948b3c038de22d56079f8a7f29972a4458c4c0d7a3d908eb30236402126bf05f2f78b9ae1f777b4c370e6e0e628a407d1827f238957bb21479d716b50ae3962239ad628b2e6efd2888cb1f67c0d24aff9f9733edf5fbc5ece35381b2cbe7c98982320b241515ee494406df309246a711577bb67230b5759906c806e5708e282dbe134a206b22e4c345717a63b9f8abb1cff2eea607e01f057310301c32866084ae6c0b4b0d98effb48e365daf545de351ae6862130f06408790684f1ce7e8566c970927ca911f1fecb82dc36960bf0d0939db1eea29d9609860b44d8b9c8fd05170e5f0f3b1df42ecfa73a04e23bf74a50c0511af1df865afce80c53c3059a172f294ca2f249b5d55259a8ff623fb68a901d172f80f68f2bbae65ba15a024f78588ba640f681160090431386fefbe0544e52864f02e8195f9dba813fada4514cf7e092bae72acb085681ebaca543eff6461422592a9a7793982657fbf8d6df76f351e1e31b5f4a48f7c136e0961a6fc47cb956a143fb5f6d6e493fd4cdda3c2409253047b4f6f91b5b02705f745d271b13a203304386a3949d621a88a74d98f9660cba4646e753eba45645bc44676081e623c88bf9e5943964f5ac63a43a8547b861f031d181d1068130c570dc9c84b50647079c8f52864a514648002906bd8c7eab1c7403c3caecbd6b18e192d645d0447b83f0a52a04015180ac88e0c3c85a7c0e92b21724ee71a429e17601cf2aec9f6230bb6b120851728c4903dc73e1c72270d8789227215e1906d0e8d436eba6dc8149bd2416a508998d33f255c30dd77387e9b374ef4bb5f5a05ea04da6ae390601c770909d927478f5383022a58f91ab3135bb6b309b4d05b438a3e057dbb0a56365f3040c8e16ed6de2f89bdd5363b5cafcd937ab34d48e0a6bc9f11adb268b16ceb3a6ba96b3149399a442d2ff80ce760ace267057aa0119e182b5f1c24c752f79fcd08f78841d16c8282b67e38f2898eb5576ccdf6cce0cb6b8d5ec157ae8cf3c69af740ceb6fd00405eab73d2d456f08110ce2cdb5a9bb3b1f9a1ec3c45371ef646bb1a0e582d78372177a591cb62e98d6dc154df36eb27d0e6ad25fb353acc1b46f1ba592f8df68a296c5e415c496233fb6d317537f672166b9cb48472b9afbff9c06b29c77d60332a68951163483a4d4a68947eb6e518dc983823d9aa897445470a30488db6e1f9dd11159304b9a5d61cb5fdfc06eab41163e8359b6a3d0ab22aa02c4033388aa1fac94784d39ba13d9c47d3bd276af1614d091825d394ca0408bead4748886302da86185cfffc46cc0e917faf9a98d66fc9599d19b48dafaf7ba1c0e16250a626f7bf352705829637b133dca16a669d1cc8865f0408995a6f71c62cc08bbf4a998c29a0244867f41a478e8e3a20fc47b1b837a46f006ae557ddaa0c2742deab4d5e4d37203fa4c811b0b3077b9b3683f4f19403586c704ff3c1e6e682947a427fc835eea2c9e4734b7472edf8f07a85fbe3eacfae1daaf725617ac76d6d145803bdb88b004ec62874080632efad5f10fdf111a4106e9e48daa3d3d41af79fc94f18f2cb85c9c97cd32f89bcb27f06aa8ddc95bd6fd984a86e453097c94494e0f5e8733677aa0c1842900f0c91b8c794550cdb8d5cd2015a1e655f4bfb08370b61435f12671c14eea9d8ed858e19524095ac746f57755f6bddfbc7bf671fa631cb760bb245e98f01c99bf06ba34ea04b2ebd920bcd062c68b5071e46c65254711b952383bcdb399f5494c54e8b7a57078b2cec7d271d1982ef47813e1c50592f6a5d7221564cd6f5513218685d729548ab80ceef5478954dbc4ed6152c9962dae4c4826bcab16e8d93b3857f12b5b99712faf30d5003db14d1f6790e668420828edca1d73b88aef228ca02e39094c9f381c889430eb10bc2b63b42263f95c9063befe769f22e597cbb65f5dcbf5bdc8e2b591f7a49cbcda60ac8fa8b62c94abb5db288c421d39f999cea0747255d32e621fc0a4e9a9dff845f15ce1e9662324520cde48ca8e209d4b8e1a8a960345916120022c3c4885e580820f8db85a2fc26681b068a890844551a2fd189315df306ed13dd674f1e4b014f43eed9261601cb447c3276ccf924da2f19c197ab0e9c407260b448e0e85e1ab677cb8a5dadbc5a07708b3a11799b32cd6ade0606365c0edd46f2392136c3bf2c0d16b1b8fb4e74b008de2ffed53cc4c86a1c1f2f786118b73f8db7d3541503b4362509372d4e971b66318f3701ec9456f13e13eb2add627204257869268a10a4758ab97ce48cb3c5e9934d2713cced831876434178bda9d1e0adfef6addf02b5ed4fff6b9107e7433cde54b86d2285d81e84264447edf5fece9e45306f1044eb78f778289f609d5c8ebe1ff8befee402b28abe7200121f9f9b24e5faf44ae26f703887fd06a6008984a82eb943f96ef4c9a24ca54e567a91d897d0413affc7d4282238dc9c6da59d37190265e22694349a03e09b65ee8e18b760c26fd6d66ecfef747fa1f109af1437c38f9b6c993b4ffc019a718b86b8667f449070a3296e89dea56c96093726c56dfbdbe5ca75a4b1071df63c4464ff0b3704dc513478848c243e8c9b1036b9705ede3019cd563df38559f5654cc40d3e0aedd11012424dc2e48787674c7e7d8e356f0388116b4b37e572a3dcc645a07db596c8e80b0f88552a825f9c4c7188430711cae6f08b903a373c9f20a5c2091647e61290a46875bbd96227ebee79713b89c20a67dc22c6de0ed07afd20ea3e5b4baf501fcdb2db66344d42fb7db748053abfcb044de269b2095c340c347d97280f07f9b13fe192cc4789ed89115ac634893fee455752bd1e129a977ecaab475962649057910be11afb475a20de5c0382891719b2301de98ab996fc93117d460294bf48c0f8f1023cb4607624e086c0f789677173e1b3c97f467fcce2f267d613eb36062a1ec24d644cb02dd1b451acd1cfeab74bffde1054dea2d96e42856a2b8161793d098b986dc4525bf13edb85ee6143b0eab678adeec7658f96d01567dce8fea1f3f61d41a054537d8f800062c621091873054802314bb700d377fe23a81f4a546863752303a71bc162b6ea037ce0731db89716f01099d928e1f6770cb4bb0753b814463e80d5b6f2721d8102dbc9a37b72d3cf82d8216521c3f03a72db8a4100e41d2d4cffd0b854e66fd9e29f0277748488a0a660f8c3c533506d2b2f1de45de26f14e061d06b7c46967a48a5d47d6d4893affb90f3bb7d81f8c3e55f99be629eda0669feb4ddabaf1b498eb6893e6d6517c4dfc12a83561becffb57df5afb6984c69b800af36ac26f455c1255e4d9d6cc600b4190439c69a90bb4ffc3840e80be7c606ef98b858962f89120c202ec05131b385707cd8016248305c1958a2a2737535cd0d4cebeef99692c2de74f21612925df8ada72ea856056c379df33526394b069edb332b657286cbc65dfbbdd181d424d10651db20f2b2839f251a5e33ad2a3312ddcfbc8e0f21e6e6337f8ec7d6358e64586d458413800b8710d2d9d681b2ac8e4ecf27bb40dfca89a497631a645fd7f745d9eabfc9a45fdcabdb54d135a19785d99429772b19a9c5c8bfe2bb06da45ce067d39ae03bff0dc069ad67d9831e56ae86aa1e4b2efd33063681959a6735b54dbdf4b88573419abb401dbc96f514ace83ce7066b7a9a3387ca0f353fc6c6b4a73741b1c1fcd9e963ae806c8bd3f45452634f4b3a84f0eecfcd26c16bfde4850cbc8e13174bde15999fe88f71bdc3d016ad3302c55801655e722f3139c5f5f03860cee15f4aada13acd33a67c5f180a0efc470ea57c3fb835e5532ea8abf658236e8c6adf586cc498b43f3eb0b0c5cf7dfe1bfe33a068b4d885442d71c06aef5a85b5c5a5ab2a5945206c902b402b502674834fabd47a2d0d9497cdfe09c5342624acf9068442a8da0aaaaaa2524a6f40c8946a4d208aaa6699aa22321d1b99f84b4494ab6a9a33cf548a59133241a796937ed12923d1285ce911289a3bb84649bd0911269240a9d3b5fb59dcdda59dba66a4de747ee8432395153a954aa8569a0c6096572a2a6545555914c911265b249d4b4c54be57aca53572794097f1254eea4a893cf8f66e56f5b5b6b0a55557748749248a57d8644a3912874ee126997482351e8dca7ea549daa7acea8aabaf7de1b8742d56eb5daad76bbf1fe3b980a7e58ebca255b652f43cd910aa5a7699afe0ea6821f994c265b57b216476d4685c2ea9ca7bd76abd56eb5dbeda673c3e5f0d8e47674bb1e1fce3bef9c75d3b9e172786c3d36b91dddcec756abb9e9dc70393c36b91dddaec7a736d39e50b59a76c1b75bad5683a9408693c9522ac914294fa8d8b374b05ac7b40b8b2b84c72cb7949420fc8da89349f59fea1b0bed6028ae48a511f3d733246a4d5237b57fbfa537afdffa6ea1329d50a0121296159556cabae2ac190f9a8cce8e8e67d7937d7290fc93af88ddc872363e6e35369a76e69cedd011f3817d507e6e763a9589feead66fb7dbed26bb61d9ed865373eb8a533dd57d9e7b9ffb3c55144ad5f1e468359c8f9b09ea5affe327bfb6d4542a95d2d52841644cc74c6c38646a386670b434ab9a5255f584fa586ca53aa152f88bcad582a4e4047532e95465a198a8a8fa0bd37a4ff5d7eaf556dd0fc0dac2e54261a529243a3bba1f9c89b6d5ac2f3af9dbaf85f3faa24356dbb65ab7ba6dd7b5e3b66d5bad5bddb636556ed952b376366b676d9ba6743c3a2f05d3b00caee11b0a471b601680ac65aaa78fffaa9552a9542a5d0d096e88a09cb072b43968d8f55f396c3e3e3ead94942a356b67b376d6b6a9544a7bd3da7cfca8edd4e072723a58579bcdb437adcdc78f1a5c4e4e6707eb663c68b905853299b5333d9bb95697cb444742a2938432e901a6898e797c85585e735ef3bf744cc7308d46d3311d9b9d728907aadabb0a55a1298aa6b91269240a9d2b2bfba82854aa76dc8b8c1c1fa12dd6eac2f2af5ff9d7ea72a152c12fb90491a3188222acd58565052545a5a5a6694abdbb345553356df194b0ceb906ee8ec70d12db31a3c9d4722eb76c7c42e1dc8b0c96157e42ad7805dad6efae15d2a269676e3c0033d264720dd75e77a772773dd965c53fbb4595e5163014599867c4b4c92e2b6768f426cf82e2dd9debee52ee4edd2abf52994e287ed22a21d9292e14961556ba665cbec939b21d331e3499da4c4b63c33fb7d80d092ad0b616c73599e50367935939b91f3a3acc93d71deec141f04cb36aba5551fd0ae6ee6060724ebbc0e45ccee51ccb8972acb1d1e0f6eadc9dabbb3ce61912a146a4d2887762d2552a95aabc91e306be1430830603bb101196b9aa2b144fa5eebdf1c65aefbdf1c65af37fc95a6b6d2a474a67c8d4e863feb178f4987b576f16873cc620acb56d879c73ce39e79cf3cd5ece419c6fd6ea2a41e4e889ab16eb08efe90786ac11a9142292629e2111aaa5b2c212825fda5725a693e8b95856545a292cbebfc727c8cae2b142f150ae06bfd5eaedd56ae5839df501181f924de2a35957e4b5a71dd551bda72524227ce7472e3750c35b4d56225ec8b7ae5834edc2f22ef546e566e56614c32f45d9aa138a04ff0454869088e8f0c8323b5005d51404d31e9c9ef6de7bcad795aaa59e4620f3e5729162a5aaaaaa3ce7952acf7c0a2ec55ad3ded5b4f7b4a77d3da15c3eb1580a0a0f112957aa353457484a4e1e7532a558366beda9a75a99a4a8ad97b25852bfa2d24a59a9faca1feb5e726c7c9cfc68db56eb56b76ddbb6add6ad6e5b5b0b0512b4c4a48a46a4d2489aa629095a62524523526924358d94f6de7be768b519b5b5d5a8aaaaa66e5bf548486aa9cd4db372dbda52b9c5a3aa21d149dabf3727f5af412f8459c0620fe31ea027c7f07bc89e80b247d9cd11e400cc04e5356bf03f1b22b84dbb87e16b6f1741c68461f83bec36bc0fd9adbd1739049ae5dfbfbc0e0327d0630e6972b8c13904c9410707071df68586f8861f36dfb063f39f74a9575804fa94d7d6e9bd0b9ef7bdff177a3137795b87bde5eebb08f6963ce6be049a35e07618c8dad80a741bfa43f6776e899140b3f7f5fdfd3bc6b6a17fff3de5b564001913fcafbf8dfda7bc6634afff3df8fd6decbf617e71732c46c2400540d78773f109e6c5c14207bb5f7c09342b9a30c4dfafc7382accb0fbc30fc3ef4311fc8e59f17119e3b5bb092500ec305f201695808b0074efa6696213e37421b86006847f00d677abb96514df05f4a07dc1be2bf921fe519730b628767555d777691faa6e86227eec00040f3650e1c30a51d832a021c109201c7c40664c0019826c421f81fa40c4133d7648d800420f202c8074004203ba7d708142282630c25e9c144688e1fb91c2ecf321050df6e7ed9c933183060364c95e9cd60a5467c40b6f8ba0ef511899c5fd86e8d7cd9bd7ec85f90529403b1237d1c3cb0d71479039628e16860e7cb4a0c40f1b00a941091d3246f048220b118cb8953a18a183b74488715a1e5893bd38ad0c419462587515e4555fde8ccd4fde3b3ef997d47fef385e0dfe2541321cc71e8ee3a8c7518fe1f609510c760fa68ddb87e633d27a720ba6691ef60562c78fe596b183fd958345a150202e2177354d0f9ddea1697a46b7daa66b50a006c7b1ef38c5f2daa130f18f34195c1aa045a1bf4f4367a6cc06e3eee31bae29e10b1247fe517316c02e9b248000e8fe3df96b1a3f64c7dfdc349222be75f6ced6e5b64d5e376eefdbaed9b64db3db3db36b3a485e35ed67a88cddb19bbc661d327dd32c8dd33959efc8acfc3fc21d1e5c4fcd8ce66750999de6f18519b36608f7d812249e84e803397c146533d67145182bc6786756c332bb3f30311e8710c17135fe7d2fc6de55c1007460e2cfa9c96d17afb595f3828ecd3f3a62c5c17a9fe77dfc0a00a30007fbe98781cfaf0a7ecf0591c6f642441a5b7cf01bc82e2fba6c01037085d4b8710520ffcec82e1701004030f723f0a3089ea0f22c1f5496e55972d35996270882231979dde58b1979dd2ffeba3defb7b7bded79def63c10b8c12f79cc66c7f1bd171f2496e5389631e3383e3882decbc82d21bf7f068d1d228688e54fa0d9b2bfbc0e962d3c305f34ca458ed9918779cd62df79755d748709f60934fb791cfdc0dc21181404b9800d08346b8aa141501c4131bcf741703f49bef6e63a6798052cb8410ed32cd8637c322447bc31f8fbbfbf9f07de7d4720f73e0604c1d7a0cefb3fbe3b2cff3681bc7fbb09c67df05fdcbe6f89efddb87c21befe0b43c459b0b8c47fc30c6231fe6fbfc85b201fcc97c95ed63dbdde25040886666e11bbf8e493222618e28f1fb359fc3121fb75130cb17b1f9359f1c51e03f7fd217f4b9801a34814128542239833984110148542a2d09fa407821ee8795efe46a4d17df145fe7240bcaf2d7edff7f5d7f8a0171f6601fbb1e2c7619afd46bc07e9c93b967a944e7965490ec04e25a8a3e1cbfb4d1e8059f27f05d36b82f576acf924794bd0e52528f4a217118542a233f4217b9a2152448a4851c8fcd34445423848fcf285f8e4c761ad757d6176fc8f9f4e79c530f5aa7c7f717e72d88fc3ee5519fffb1274125f66c8923d66b32347c1f37bf13df21b3f6e1a45f1bfeffb185114c50fc910ecd9b501999fccfcc56563eeddf96120bb3f87c631a4f731c4f73eb39825bdbdefbed7031feb7f85e10772d387c15dc493f8c31ff8a1d61368165c60bf47621284c7f740d0f3c28d6ba8b4eab7a7920d0100026318000083300c436110888124ccd3b90714000b4d746c5c50521e8cc522712814478118c4300c043108c44008c34088c290630a526703c3054de9bab1597530f89fefd3a2ac14867a370f3e88b3ca825664022450a2fc2f279a5b6a461ca33ca9243bc088e29492a50265356ed550052e71425ac23cff828777eeef7ee8a55a4dd9cedeb5c96a26cd01d34393dc78a5b8732f7553b257713fe1884f8ec5ca862b0b71eb16acc1847c1789e696fcabeed5b5f7496c5bc9a6cdb5b4f994abe4c99930db9accab106a8fac4d7af1ffd72cf09d30f155fe2568544c68a3dd1e15935fba29edc4c26edb46c999b07d601e0560d3debc713089521f485673aa27958f1568c2420bad1e314025e93638db19992e9f00a56d1285a39c9046825c18c68a8b42a56256c93eba0a5a300595c98b4260bc0a1694edd466f67b7f43c69f89698000a93bf5eb60b3716228935401f24bb0a5b9dec89b70cce29dfa1d56dd3c13b89c77b256cda4f0055b70d84dbff1a0f78cb81d84e07e8bfe6fd19ee64d4d3a1d4e57fa5fbf9860029a01350ef5223ae5270d8ca71d3862af1f37bf61d6961f83d81d7a79371fccdd3901cf6431c5a50035007f0158130237d4c88117809b8558d4eb828f1202c64224b2971868c0ce1dbd885e9b39587b82db93e8217560603493d9cc008298805d8fc37e6e09a6001f2e0765c59eb7d82967119fc32dceec982e4e13d4418715932be67471bc6a71117871c59d320644513ddd8fa618dd18b0a09ea295b40c0808f48cb75dcb517626266a94dc0055ceb7e83716e93c85d5abc426a7c5b165d532ee76ad4c501a3cab3bbf9d2b79bbc5697d28ce770b702ceeb2e11428a3de4e25d842a57b70cfe5b1fe25b67fe99c0b6b47f75622bff92ad486aa3b7d93d0e3c83ffc321a9088a5f96fd238d22f5a38b7852a5d26826cb9d55b80f514033fceb9989afa05fea68e49a6d471e1f4162b877a73097257dd28b19675d44b72670c6ae0b540ef833151698a9512b011d69127552fd973202c8cfd20ca38879eaca4e5be22976f9f4b2e277eae6146734c8bd4b5ca167ee8940584a2855f9768fac676d1c2f5bf603bc8d62746f3589d463a57b2ad5bae880f69c3f3514e6f5a98b634c982eb5bd0fc699cbfefba4728626f8eaf169290ce75a43f1a77a916128be555223fb5ed9a2a95126dafa63112c375d8a8d24bc25bbba4e5391c59d989278be30f908651a3c1ad6359a2705ac30d6bbc4167360d331e20b185b186568c2750a5f6b823bc1818585091667294947893c42880a1485da16392cec3d40c382abe4151d4fb60fae597ba4a2d39d2b0accfed2bfe0f8938e2e928447b745f3a8c87b6abdb699d82bf14f5d9ad3377fd72f87a5ff9385e32db442a38a9941cecc4c7af02defff8a45d780cf33755e03f6c80438a7f858f35ec48beea7a775a060b51a5574e7aef4bae059549f57b4164e6b158355f855f9bf5c2c25ca19809836d2ffd7c723823333e8952b147221f872c8c5e8d9340a64bf5706fba674b33ffd51b65ab5e42d2ad596c151301621846c1c49791b3546a5c25ec149e754a490501b33299ab634cbe29cf8daa23ad9c14b4e5547f2fed8f029a8709408fe8b3e7e8e44a0d63c94e4c42342df42161a5a44cec190273c35951c2674e4a74847a0f7b73a21ebf37459ae10b54960d081d6d8d77b10743089b60bd7fb0efd6b4f04aeb6d107327922fde8f45f74462ff4e621c5ab496a8fcc970b8fc2181bdd3057d7f1476023ed503baf08fb6a85e977623fc65f9bd91036061d98dfd0a482a2da641253dd1e8f4c8116c440db7217f2536e2a912ed45710e0bf18728d4fb126306d6a0a3004ab61d1f1139c837eed0a276b74744a18fa69876a8c327fe23d13e4531f6a21cfe5478977ab13893f7b9521f484002d5dd3755b38b1f688530ab5edd4ffa2c2d11045880119b8a1c2a35877f0709a0f64e02e4861190db1b9a8eca377ac7f2112899ed8259246ad8fabc8175c789d1a6f09d0e25945485604991cd6e46c361c1bfc6464403f2deff2eca606e297d7412ca083a97b4b776eb3f8501bc952606db46f9ec29b5e91041485a77103c99d1b669ea34ac75656d22e275402da1e9d0dd7721c0f15dd235f41ef1cb2c84cd8a0deb01ca034fe99e32c6ba5a0e76d6b0587a260910d1e5118b2d39a5f78b75a3acd60b009343f2a6d6487e33717ee8bc73b9dfc38cc24a7889b815b11c1cf35f95f798945da20ba5af58d1dd49d9414e6763797cc78e36d1ade6a484521aaef9ad680d9f5f25e2e77d58464a315161ed3ad19c12bf31e31c75ad999c5004c759e182d55d145333cf264e8c5c8c24d9c2cb69d887bae45ad81e54a2e3f0ca80c99dbd1a4472652da3ddd71f58c2ca14fa4149df079f751a78cdd141bc4ecd0a6dd14615d8f8188a66d5c1f33e691dac2aae0beaf513179606d43db0377cbaac182b681fca1e344ee10a5d87665ba1b8b15161cc7ac2fe05b4c547fb69438b40efa404a9461f08a3a1c8286e29aad96ffd26e43c904aa16c036a347784a255838add233e0d9b267c31f3c0ba24db8333ca524b463f93efac48038fc3a2bc3678bc6f805a7bd042b8e0518dba7fe9e3b358532402e3cfce231c9454d834c53a8b12e28934268f8859d78d040a9c81471279819f308a65f0243a37784d4b5223f33e0ac324d44786c5a9fe55ca4e19aa3c17978c2af140d78fd13347591c3e5563a72e25b0be7a855d224744b2390ebd2a466b87e9515441e4784ec4f2de4698019e01cb580db4ed78c21ca25ac1940523d0c22ebfdf9c1088554756a0e7f3e481b4f457eb31829253afabb8402cf2ac0fb69cfc59782a61d81ac52e5deb9b83a6206ce3601dd3ac26e0447c40b80be9ee153c69372210b4c76476fe656b59caf2ccf9ec9d746eccee84e720aa5018a50e323417a8f695541b6324b64883485a00f1195b10093ae2163f024fbb1eb6c5580ec6b12be957dae11c4359aa50dc2d8ae92800a9a36a14e4d092f24e3a09401f58d1582ca1858232edf0f9df4c86e587d246669083e647765d4925598a2c23816a20223eeb5522c22143f44d9a2d3636710ede447e0809d3a8915a9a06afe1b1fea57a0dc9d3e31e23b88a489ef1db18f0de264c9d3a47c0aa28c1a57e9d0509c15e34ea84c88af4ab76a2ba6002b68aa641246327d700f9f6ef3e92604c84052767c28996df3949f81513391e0bd42960acd0831553a85d4a35a2d888a0b8e568e2fed1a418d57b25554828260927e00b6645d93ef581151d893a29543e9093428ac732ba9e4904d85eb57c77fce5cb8e787c9d00f12225639c80fe9345f9aa2c012a6c5947a025ec3835f9cbe3420447ae214c22d39a72a2a754cae7edcec0050d3f2d6d3dfcaa335f58674e15cbbaa86a31d62b01a892d80536af7386120125427c80cacdf9d107d0e41c707d9072da660f562c698b4b31cc8d0815a09177ba15d61102d8b017837604b6dacf58c7980c2ad5d696b4449056882834dc4f57d34cd131b7d35f3c1d81e3bde8210671babc1ea7027a082051eaeab51c9ceab2dd768546297dd552761f110851940c4b8bf318453423a6c76a31f2d9e901d473d1585229c5586acd7f6cfcc5153211c11916a707adcb3e0da2401962051767ce1eff374aeca03fcc2f139f9e43c5c03c8ceb4f706a39b2fc493f935b8dc35370c9c2f8840a9bd3c3203907e0e81d89c14b8b66c5a0a14556c8705d8c9f530d1a1eebe4de620b7495c6aab737f3a51393da28986b34dacf0a00df30a6c5f9ddb39d159a216f1ad0f7f3cf0a02e344a7ac49594c945a88203f49c759e827ab8ef34ebbb30577b801583366b28c9d5c61701b949ab731ea68fa488600d8c45106d8a422980e73b4103cc83be385d675628fd14a9f785dbc2641613faef44d0f6032681f0d056556eb0bc7579d03d91c267a5f8b0a2594a5ab8bb7957c5aaa43a48d9861657a3481264363bdce925b9242a02be2eff2287818ecae9c6e4249803addfd07050ad82bc50e9659108a230af8d6d7191cef575e585d8ea56873aae324dd7fcd575f20f39878a54530948c8eaf5009840f928db1111660f33ebd1bf22e74d2c758eb0a88114374e1d55cc33e4bfbfd093580e779168305852a1fd1722f482adeb07107522203a9438bfe6e44ff08851a4fc5962441e86940be76f906905434daa22f2d27ca533a7f9ed4d1380473a3c8861ad07181ecd358287213ae54135cb6b21b016f9870a92fe1d2170aeb70874b46069786cf6b8661dfc717ae9984c1258c461292ff1e8ba5da18fbcc3470802bb4ede77be371663df02cbb88fa6c83ab5727b33a8018b72717991d84c25d35a165b8f41a7c28351bdff5ad3a1a1112b76291dfc2234dbd23e5d9e72441cdb7308c50ba09cb075982ef263b46b827e87be210eec108faf120172fcf270e22afff66a9d044d40055882816296a8ef591722f2ee406d61d2032090d842333844d8cb0100967796dd75963fc03b765208ff7303f916c7dbe509a925484ec1dba4592fbefa6ca2fac50acdc0753bf77f22ef26019d06bb7ea03b9da7bb9a0f6c2022e3cec41f8ecf7bc0114011b14ca08a881f6aefca450a6b96f26f970b5cfaf03c428197c3b3cbc7674ffd4ea5b0bb11bba85db5fcb22c6fccc5ff350e658c03a84b844fec308467b96a3ecc6f300d7dce204ac327f0e094e10e272bfebd1852afa086037382016fa7af4c0bd98101943616363f359d0912844bd9335079945141624987f002b901a4c538758ce3b24aa5a95adad8863327226e36d9c93da991fa61d915775ebbe68a54dc2825e92f52d75c9d6ecb16d5909b997d0be4933cfbddb4260a5807dfbd4827f1995ba77c51f3934ba6ed000ea57d8fdde85d0c3857c51bf59d5f8432b45bbbdba8bf496a29f938c67998a256fb752647b62a7cf2a111680a8c87bad6d8414b33f7c22e989294f4f9943c3739c80768a3abec0ea9cd146ecff2026eda608b3aaeb4fd0fef73aedae8705e349041bc59f557a5ab3d62a88a32a2ae5cce84b550af0a6107b39954151644d1b772e6942fb5b78cbac5710336490da4aed00c3c5b5b141ecd394451307d7c1b050d164cd797f0d9ab05f1ef4c94643dd8c62f747c76d1d608459b40314aa423c51beacee04e24564d1a8f0576c5cf2138b8dc9210f669ec6c6880071b0fad0b159676656a257ef04e5318fa6f0648f8d6f04c0e2bc883983908d4395ac93a8c2291e2d9c496260e3659e5c0b46720def3e18cb4ca03dd36f98251b63c8b27f4349c395b6a6e36c72af85ce743e448a8b40400df5fcb86d1864cfc92de4d613180d1716407f5cf4a6c7b830643d3f0c6d5fd8d95c04958f3459c1f404e417b6c5e53b7a40c8fd0d73ab80e8af7b10c560af052f3796394010b589cfa981cfd96dc748ae018754f5957a04333ace81efa16ce8786f403fa2c0ae670a40f834cb5576ec5f261b2ca9a212d7c0e77d06d5f939111b536f1c85142894c36bb5d0c573e4a85b7b163df5a72117908c261def8bf90f5d2b62730579107e3d6437315d23e16ae3b4cdc6786b0a80c69cc02c8a3989e622622385c5ac1b1663c37522f62801b10e03f42cfd525279d1312956c75369023a9ec1df02bfb473d1b1622dec89646782d883dab127b0f088b526c8c87718d595d50ec96a77a181cf7bc6a1bc9783185c9f8eb5de6a2aadd5b2b2d64edf54abeaab35bcad564357adfc4db52a3e17be60e69a072c83a0956e39555506062b9d9509cd8101c586adc7d15c5990151ab9317777c5c2396868a0e292bf6a5ae60a74f1da73eec08b76ecc436dcb304d1ea0d594cb82c0e73152b269ac8fec1f764b2311a00e70bd2e47b62246771858c2f50df65c6895b2d0bf3b342f8d1e5c087c235d972d92432c3e30dd2689cd0f39bf1108c433aa11dc0bb87aa930c5e563feac19b7a2ca79c8617639ed31824f309529e1ca601bb5d0a6b04122612946d93b3a45a8e5c4b9c0d5e2e4161aaf252a59d62f3df186ca5c2a5c21b3b105e3316bcec0e4a1d6e92d20b47ca65a8a47d7a43c9a50aaed1cedeb797d2d95481a0c0b78f38f0a66b1c921513e0c8da001c25a0d555516b742900387420155b21aba135995ea609985e6ed774f017fae0322f03955ebf7a28bd67c7cbd3654878250775a9a640a437c22546815b8db85495cf972d9332d526c97351953581d75213fb6ccb465a7ccfde46dd939aa39814182915334495033fe45023bf991a574f061e4cca3104beb26c52fd4ab8fdbb0047b011c5ec5d828e15cea3f09c9e0c11ef549def2a9e4a2bb266b1d572249c4993ccfecbbd587c93aacb4cb21c29d1331d9480359b9857330bb83b38675258809e6afd72fb469150e2f8f8033841471f96f2b2f7be7ecd9bb9ffc41935a58a162c33093148834214b40842554e4d30e66100b44fedf1011a79caf41dfab7a71fa0da0996dacd98f39f1d68d2a02af5403556404f8e79403104244a0fea021b0de901aa59e1c4e2cdc1a9f13eba816adce431a8f0106480838048e47087e6eb5a98b65a405595a671b479c2576e40cce05645cc1e12f25c300c03a5e785cd25c3bcd0d0351c2ac32ddc6da8000db3a8e533bbed952a5e56b32f8019cce3c24b86911b64a38d32914a9391ba543e82b3b40c3c4913e8f255ea08ba246cf3c8f319134eb08fc9ea87d91ccc418fd255296d90d27c84bde9424c2b52da2f24e8327ee699765e30757c36cf5de005157af00bea3bc1de97e990ec4a636552b06e1fd96a91f94aab725dad00f6a6634997feb08b652b5f9a320f291e8deed1e5348972b698d8ec12a12235b1678b292307aaf7bcf0fea017bca03df389db32a6225d9ed0f79ac43751d2dc172d2a38c29ea1eab50be3da51cc7bce5d6a9c018468477048c859e6353752f287089f6b773a8a3764dbd788bf874869675186af27384d247f2696250affb450622cb2381259f30355ffe9b865f907c5771611737bf739ffafa2beebe8aafc7a093a1b8b69e4d6cdd5abd028922be1e4e489c3e7623eefbd98e02457936e5382a5a8429d1e99d6fe43d4e29bd045b6bda53e06c49e84f2a2d33cd791ef10c67ef68708ebfef48508770041f0af97b7213b46836d53516e78865bfc98621336e00d93a8703cc37307f164144d19508a43f2aec08416c65e6432d4169511b315ac9b366ea3ad2c58165588d2d5ec8de8c920fa31838219e14d41f14bae90362aa322a5d22468f6dc331b6431de62d9651a4ca20822f41081125eb30d84eb9a20b30db21aea674c173793128494605812d8fec519609b383d2dce9222b6337a99779d40f6eec0dfa78b21f0c69403850e44993178dd960ce1e2667941c8146466edfa9ee08d85c4523ca0ce50acc570881eaa1631ed57c3b45ac27427eef4611fe61421c6aa78bf3873027dab01e9543261a191a45f640022a24da85ddc8906807c27dab7a60ce1d030850b05413c4edd10f38de4fb4cb542b3a190315b7af0773ee13ff92a1ed317c5ed10d314fc40f509e4491ce54ecbecc9180d9251abaa05091ddb12f49721f8b11a5f617b36b4dbfafb98f8a46a4898df272ec90bb25d8d9603b6f9293ca2e0d6503b7ae0c90ec53379f489755edf0992f37188a7150888c702353f35418b4c8d9c1692665975f5155695b7db87e0a9770422dcc07ac66d0f65b59801aa43619ba5a79bcf570722a9781c83c1026a7a6edb7382e5e7c87f3dfd134429d22bb73c0b0c22083fab503d040ddef640101a85eb56db73f8ef733aee4ab1ea635193fb53603624bfd72046e0b11d88ace9952e998108fdeef61cc5760eb34cbfdaf18fa2d83ad843e4fdcec8bcdd93f75344de1a330302c325f37ceff61cb2d7c8b96ca1af357b869f5a32e3766f8f2a914f0bd1aea14dae5c885c74aa1d7543acd6e27f3abcaabfb0e6729b8d29428f313f84a27d9ce85b8f16a226a3d821a21dbea78541f7b435af3c8d460cd17681ca8f0879ebe134c203f7b62bb6404cb40ec4315f80753890b1481f9070ecc6048e1bc07ae5a085ef771e440919f1ed9147a01e0648be086e5fbd6eab643aad9228df6c6a3dee8478ed67621376809c81a6c508f432de18204ee95f34fc9959f8dc51bf1fbd8fafc84484ebf5a152b00334e49118a128f009e65ed25a677bdaa54023103f639e61c912c3da1907745f28d2c9f61b9e9d1a1036a1e5eeb3025abe97406371af6bc1c8564b4ab38b8a15577c25f1ca50303d32cd6849dfd6bf2703fa36f8543f9237dc6a0677c6d3c42148bf6e9b4fe7d3eea346725c0d253cd239f6a29f6431d7c1881e7fd279fde43506d1ccd48068060e810569016ab6d1c60dc280a3b41b222f8dcfd4a6c0200118133970e3c618d49bd6087c22785cb109962a1855a95fc87b77579be6f4686804cb4a6491879eb8008d25b8836cf23d74bf637788a559462d432121a3b03401ded62abea381bd94c780e0c79f0d8a5a7698e0284ff2b1b021a0245b56f37727991c341636bd671b95cf44c69db66b937b8602424830118285095bc79807a0ce3d5194c17c51121ee79436f90675a5da748579f25e7281374a5949a4ecd23b63e1a970f54ffd142b553c3ef969071429c38844ba0efec0d05c5a9ad445ac078007a665ec1496cc8cd9d15a0af74851d2b11e887c87906195556ff948505827d66ffd91a1d0c1bfe98b28b551523ae6d67da2595de36c0e5198add3d89246eb21964d60d03c7462239305e6facb35e078209c450755d9b7804ca746d3ce64ff1857d02a0c515ba90ca2805e09c7a414d8b12c32f0e5bcbe71bb53143be4ff1b247d0c3864ebfdf70e7105ca238a5d681065bf8fc1dd20ca2b98da7f1da1a8055797aa8d5cc184d8e94f33cc02a5e771932d4050b26e9f2ec1507ba82a1a7c6dadf6517bb4904ccf89c9b8ed4a6f4e3399cebbd0a674788d439851f6dffca5c5c56d06c536197b209dc1f91d9b9bf659449cea0a2517261a5148168ce91f27dcc1bc4f7a058bbf2b0ece9f126519c3810c0ded1601fec02cf8f4ecd08d6035af5b56b2c6839f7cf6443af27e201db829c1ab8f1bf7513da54893d065c0e98f486974145067faf465e05e3166448235027c6a4618c4d88153d339c19bebe060545a9ca6000756dfe6ecc9d68b59a3f34e2368405dcc45d4068462c272fd985d99aab20b42a81ceb3f55b55f01d54656ff9c5bd1c0e49649b1046917e342672e01cc1a2f19fe5aecab3af4fc58470f3d0665c716a08a896a7b8c2d7a4c211a8f1fb93b5e7ec80adb4d2024ca6f2b62ab7c6307ff57555978681c2298881c212536d4c770c4db51a16caaade245dfd8cc83e3e8367df17d9765d177776d9aa386c88497394aab6245ec0e792e9addfdc8bc84cc6725f66acc92f3028e94866349dd271b30b9ad0a2245e4efb2f87f3cbbad84443ac286ad4523d454b7c26419c6b6f0d9a7ff601a9f36c4e0d60aaafcbc5681e6838baf57284ce03e738ac70de6a578507da6619daa3cb9e4ae81d8d21a75eacf9a23ce7f0dd9233cf7a80feccc2596ce7a3d1296758c5f59120dc3b6b8d1c0d905ebe4ca3c69cb9a0c54016fb660218f8dbae929bb13adb94e6a3d511aa584a681cd7009bd23e427b2eed3189524a6ef8081595ee55d355d72ba17140dc56af2eaad1f7aab4234ea63947a470466808dc505909dbb0a42f41298faaf1e2ef78ec29cb1db9bc6f5f5825902acbe81302399ad088efe2f7c4433cac503120ee4d163f9367c12734d596e7a865bfd98a20906d2a34988f98831f49d52ee4f81246022155c5a80b6b2790d1c8bc7d802d406389a9d9810cdc002eee5072b109c06174894c013092b81c6586fe819e05d670da02cd933a62e035d488e34eacfdd1af0c8540f406cd1cc607c6e19a415ca000b8b0cce140071caec8b8140330ca2d1c1c0f80e1830d0039ec0229cc2fe0c9a69a94e56b705879f02bc251e160f86f904d56e2c80907c6becf3ccd8c5461633e298f8427d6778e14b6237035f6130b942669f452a39863bac7a35529224e804734071e6adb6389f378fd7acd421c9140dc8f848f0287b14f37936bc24fa350661766a406c024d023727f11214309e607ae306ab4d81010ac880e4c00c36e766dbae358117891e5334c12a2a5691793b41f396265d62454168c748c4d58432d4232a285c0dafa0996245034fa4a328f1953873085c68e0f1d1132e0909642b7063bdcaa41538ec3027585efccf85eda61200f637dac53499beb8163360aef4ad8cd33c009b2fad63b3d791d9733d0dde5adc5d8305e78be2f640dcdbc69617c9720d93820403a476049a5047b258200c5a351da6ed41f13dcd273a8d38287c409df3182f9b932859ae0aff740623d58ac17b1339f0b0f0b501810bec32af41e89965307cbe2e2a690d83b46f4a8dfe0bf4be5d075bf66a27959d8b976e64efbde5de52ca2465a50a780a5e0a634a701ac7284933a4c782f9887dd25d1396202f4001b2ebda4aa224883d4b9cb0e9c28585038b4db1bae8e9c265e5fe3892424ebb426e5e7aa4459fbf4fd9be67fe1ce5761956123efeeffb445114cf10c2d0e20fc9a66d41fc19df6c8188b4e834f2dafe340f91b436447e62e69c50f32162310384105c4de86b90b9d25a9e4bbb6e590837f126dec4dbd708a2868e02574654a7116efb1b4d191985d98ee3fedb37caf2a45bea9cb50e2995279d46b7299c91955118a3ab206b020808e8456fb4a4d679b4a406474b6a99385a52368e9694c1464bca62a325652fca464bc6f468c918d068c99889d192b158ecc5a1bcc513228ad968492ca6182d894515a3dd22d188bf6ffcec16f1f891be456ffcbc12937f2ba06002bb8248450a9fa1383124542900bea2d8f74637ab54d46574cbe60645fb2b5dd0a3fa455df573515751d7f821796a328834410291e7ddf63cc7a2ada238453b24fa34d2156d79d269e28de258ddce93ec5b75f9aa7d6a1c777fb1d6d2adbe324a46a66e60ffab1bea69532c28db3637c55aa1cd36d219adf1a4074110042dcd0bc51fc7703cf1435fe3443954c1abbb2f1ae9ac92d1cdd2dc2beb0e8d6e54daa8142d4c03f1611d34ed9c29510350fc89af81a2bc209cf8a1f751c3c7897266694efd45f64aa31bbd5eeb536ade8701450ffd893fcd5badf60e1f228d8ed0a35a1add92e8efe9a6fb34baed1a21f480b2dad52fab9a2d2a57b76c519e9bed7653b8aedbd314ee4a0994d1cd4d3f12a3a3ebcdd2cefa54c3e8e649f6882b3182b2fe46519e74ff6c7f232a94a96e6a6daeb5eaac528d6b9586deffd69c8d74f63930d219e9ac92895d35d255a0af7d9ad802fa1a2ae7d09d899d89389e4474fd4d6c9170b190519489b2d920d09d9b26be5aa0313ae2a63f96dcbe6d37baddc80ca8fcdc6a466b4ee0a08d76463afb2676bb9da5f9cf1940137d36ed8c6e16bcd1a7d1cde8e64958728019e9dc74333bddf6af61954cec4cecac899d899d919709a7a1a95531d8a006d0426e499b6201d96d27a74db1827c51b0299610ae5daf3c09167b20af7d22c3b00959e371bcd23b54c77a82f431fb1f2e6afe432411082786fe84974ee52feea720e9762daa54afd094a14af576df48057dbaae87a5dd17ffe6e742b1822e37c50ab2668b36e3ff1e6e9e58c5d7638bc60ffe49bfc0f7e1e61d7f04fa60342600cb6a6b1abfc2bcf1c77a8bc168d4fa0496c105c9722f78bb22fa222fcf7a8c31fef2a4ec01d0cea5ef42154ae8b34e59c9a62a551d227d0cbdff09d2cb2ad9bb1563b83c91e86820a82b93eda0204abb1f94c771bcb2b4fba3d6f729bd2ff422b540a018be75ba7fc1315e71baf4dc2127dcbefbdada7d31049f3ce9742aa8b32ae9b22f3622834040210dbbc39761778862055dc1a6584e579bc23e70a2a3fd4ffc59af6253fa745dfdaaf7c4df5c7ee5d583e503bda5e17792da3a75a7ae77610f440a91a7dda7e33a507710797a11670ea42d7b18154f12130524e738d763ed871d77d65b7d72338bc9c27145ecd3c36cd3f33c0f973c7ce300faf0efd7db114fd27f5f0443f2cc1fe6f7c88fa4a78fdd8382d5e6e6051a817c4702953bdcbc40a56ba23cfaac502265a21b477ef0ef8ffdd108df0b3175f32371e0ef14eb88698ce17bf8c11ede7bf8bb347ad01033b0c5a7d5461221b940bab29e47e47d3487b3d96c406a97ebeefbdee78e75da17e6b43d204af659e45ffbdada696ffb7efef0d5bd544fdd3ae996e33c49f617e7215a8281bdea559e7dd63844ecb372ed8b679f458ebb5e0f1fd586ef1e2265b416bb71dcbcef91bee5e67df15e9806b10f46436f59597530b25ec5c89a95459faef335dac377f6e9776c76ae7d2fc6186795ead595be67b49e6c0c8395ae735357f9ec18f4e8fe102980235a5be4931c2c02fe44205822fc43441140f1f651943ea812272471a4f6c3a99e7829a136f4b6f4f10326c5842d25d8345d4ad850fc0f9a07cf0a5b6a28bea9466111b04f1401159fe255943e846ca8fd98c04b6de853943e66a58fa236b51ff50a1035146595f73f5cd486caaaef63f80992d6b25b91dec573441c10e9fd951db8791d77a5831b728be3f0a8c3ae1b224f90b4b6c393c492a8c6a6006945bfeb28edfe0571198255f2bf20048d99f2aaf9c7f013405e405e80301828d1d55aa9fc3226f9edbd3e66231bf4394bf21767f6ce3e84d9159a95e5cc5e8045e7fbeed696d7912ab029608dd716d60d25c0ba210e0537a9623bdbadb52fe36e3bcbb3590d9d445a84e4479f79bb7093bef54a928063c7dd4433ec9ff5f1d7a51d779ffed44b186e962e9ae8336f5a472d54ea47a29452aa1369d1e9390cec8b4bdbea518f6fffaeb8e9736989831abe6d079e44310c34f78aa29fd14bf5dfc670d3c2b83ffb21132aec8f44afe21da601113db21a7684f6a4ef7ef79bb637ed4afe258554bc9344e528a4a2cc6e529807353f8eb13d5c24c5ac44519e28874a132079366d50035c12d1c88f5f46bef7f3ae50d3ae42fed7650f92e36752e8ea3fbabe90930913330c4cd0e62ce8f4afb1a75bf615df1a2397dcb4249aa8a55a63b869ebd2166da10daf2c6dafd67b2fbe24dd600c186eda7269c9bba6559ae066a5094ad3d2acd28a726669f6ad910cdabedbd9f55c0a479125ce089f04221a557c221a1407913f25029feea2d3a3b69395b492ab88c910dc0ce98fa406197093ceeabe329b69b7615d3dea0ac1b57e30d1298cb4226a9946d2b18f524af397ebedc0f7f40c3f87f7b0bf1f6c86dd59c42199e3fb3c7a5e2c869f88c6f612477efca218c6c25818cb0fa357bb7b393beb86c9cab369cbae6ffab1f39d3c75bd9e3b6037fdd8d31996e6f81ef6570c39a01bfcfe7e2f52d1c719a5ff2dd194e2c881dffbca11f0dfd733a3288d64978caa88688c40ffd63b9b76d17f42cab522c99e88b4c8e8c96cc53b898368067ef7f2471cd89d1472535fa044eba4254ffa4ae215305caeb0f429cd947a492b2d71d02d2b84488b2e51ec619f3709f9b1e7d51f013f0e98f8a07f1883ff790e593bc5a658374411c57efc321b919544f412c54af1a9c539931f8070a5e60c5a6cedd3955a6fc55ccc2bb0fa16c94d67e1a65326278b5c2b8aa2add59a06fa2421fc1d378495d44d12872d77dc1dfe8e13f6e1e3c80f0b4b1c4433ecefb07c5b8e487ef4696edf1ebd63082b73ae3524ad0dc5a7306bc91dbec5b2fe505be40ff383e449c2ceff915e499d051a339b21033a097f4708ba99b37b99639a4f60f5adfaa0fd5b497bdfeab09f57c1a7b00a56fb38db33c7b8eb5faf346ff9469e52fa97ca5c42aa01d687cd8aed455b7ab0a281cb095c80b02187eda6b51978999364af8cb2d9b57191c588ed3500c1aaa18d9618b6386581dafefe05469722c90030706d66a09a210f961cb0ec683861fb2fd140e6c8141be878d9b16343131724a090b982c40d2784d14388d963adb56556c10587baa84ab33d062dc36dfbcf5e0626dbdf94a16bfb23e1be6c7f0acac194293fbdf7efbdf7de5b81caf303b1ba732e79d81d7b209283d8c7801ea8bc6ec64aa2ef71ae14039bf617b11d49180c060404f432fc81be6967f2f4b1f3d60f445237635963505b8c1f232d8c04c14d0d983a207ef8621986e5e9afe3dbf9c1ef8576e66189be72460e63619044df70b1eeb7d10cc0587251c6e1c09540705ae5c069f5a90e842e164845316e9000449409faa4216416db29578d1b24dcbbef8b2b92e87336fbf0f77954ce53a5c2513d58543decb3dab0a8acf6598f18edb33e41797805112de4344f0ac10394624189942a553c46f424efeba54be8df7befbd67a5d44b806d940947cb5ade0928a000dbea98211c434f4c2822de17efd5a64fcb4a5958ebe1166905ad877788488beee761ab6125ad4254eb751731cdcca40c34559a252dcdac776133c2edf108f70529cdfb0cf6f7eea4772ffe42fb47cc9e44e451337276dfd3147ba3c027f6d0fe488fd26cac03221e2f29b483b6892de6f192405b04c74bea2d86e325655b1c631bb64598181365e325c32d028d1fb845a0d1c428340a8d43e389f192768b28c6d948a44245ec96981ed9b722f0c84a5fa820ba7e978894821428c5699200a800006a595aa074f29ef01857fc3482b6576138d1443482f67da14d4ba21942db7ba12d3a11f5241cf8712983ee5b5e9aff8f784a80d4bcb07d3d803308822ff7c0a4a5e6fd42e09604dbbfba162722b54a35d393486a7a1efefad7535cf2021b539af796f480b6c7818d4519c6b526fbc37a354224dae0ed1629a5b452bb455abd92be4b6aa3d4fbf5b18d32795fdfb757aa0564d7a93e517aa0ee3c71c5d9f57d8d2bb9cddaf0d7129c2103b17d5377a06ea047823bc15ee334ff3837aeeddf9dedfff5d1ed13db3618dab8fc74556cdb92c0521b3b9b76bd14388e325560dd6d5e09d893e278180b7901b2a9ea0383c3be332898ead92e2de1f7bcec4bbf78a5d6ab77df2d29c80b90946988e2f2d60a25524f329ac0b65f7159a9892bbdb8d63953a2b5fe157bac5689b43e912d6998268bfc6295027b91cc8b77848182f62f3259209d303628257b9094b2ba9dcb8bdfcaf84b83087c5c8e0f2bed1558662adbf9635617a7cbeab11d81a282eb72b566eb8e9327914e55b027f2b5aa7b6134bcbfe5597e63853140e88369dd011974832fa3eebb2f6d718a0ea341822dbebd7a5879fa16775eb5edefc50f46e38a3052fc91ac301a368c9b2bca0c9b89d1f8af3c497cfb1e8cc62d6d98f2e451f749e68b01b2efab8e1a963aaabdb25af4c8be782badf4e2d8bd6fd7b849827dad8e1ed178d0a7cd85a1f829c320d2a273b46dfbd1f68f21d00a65018ba7faabeaf1a4154fbd3b2a4ec4090a450d64421683f9e8b1c393720a4a7fd30fe80756a9866095ea96599e59000130b25e92ffb399a6c04ef916533c6e67ea3a17c05b9b9d02c09f3fd36fabe0cf7cea09fe9c9d3e05c60177301ae924e4e80990779666ad5216802e67a372ab686b5bf06669535609c401a03c7f5696e74cebb7e50aca336b0aca534f50da29b03cdf823b300e8c0144ef119123a0786db3d936dd0348a2cf106a43d140f12b4a702bccc959e1bd8a2dedad20a5686f06a14ff06bb7ed57da835f7d3c89a80c9d8436ab442bc3284bb31bf8f6ca102a097d82ba2ef08e2b0de0ede7a61ad2dfa71f38b5f80bbea946bb7f77946968dfc7539409b763780cbf800ca1c229925a1a6edb7731a5690de9ab40a07d8b6f5122953e5a94b412ea690da9c5d3a430f80bfe9c6d1b3a21972ccdfe0524928f2a105a3cadf451512032c6566b513e1e1d4685507ab4fffdd3fc40aea84b9fe1257388219427d50d556b2da56e5f1b8014edc58fac2f448a5ea21ef1cb4dfb2a489100a410128e3eefac92b86569f6bb76621c7127ae1175a498255eb9695f468a61c49cd845b4128f884fa2123745284bb38f9b4d6dfb5d51dbfe111946855ce156e8c44dfb2cc8d00272b7c32e4bb31f4e89b7176db3af6ddfeb76f5334d6b4724add17d82f43d44fa16227d0755bc63b091a4679e28d766b1cb5d387037d35d3fd3b86d2da0e06ddb07c1328472d39257f4835f562984dae0d7b62154084523073000f23acdab22d10c1c59478fd6b812edad8d32ddb777865dffbc3350d9bf69e4755afd8ba34775b405c027486183eb664582c908b78c705b184db403ac57abb52fc3df3660c9d2ea2f59ece1ef0a6d980c9711caf8b6c33c98031ce62f5ebff7de5b698e394c067ec7e57651b641a547f5eb074e4386644ba395a447f5497a94c10d46eb95de0b93619e409b82301967d3fe6032b49635e0dbf71da06928ca3400ef8ba3d31ccb3019a518d21ababdd8fb7218c32fc32b896684e5f9816d7713ae60995dae6155a11c00811b04f2c5ded75babe7c5f02b3aec8a47e85be434fb44f4a84fbd85ec958881ccec58ed274c871ed19f0d1166d3ea4ef18836c6ae4f8f9cb43fae7eeff6ebee0540a79d4bd456a9a4e07bf9f37befbdf75eb02c49f8fcde7befbdd75a6badb5f6964328a59d3db2a5d52134d456a9088ada901fd65aeb79de1359aa793fc407fe99a5d5214d35af1ce2bd2def7b49adf8e2c5e98316805e7036ed937e1896945e50db219188c91f7437b5c490355c6cfc54d57cd81ff2e328073a56bee4d192b2abdd77f727b254f3f24793949b2025b8d2020c40d2d49c9c9c6e4b35ff9b59f377bacd6afeb75cf3777222926b42645f36dbac26e4eb63b3d5ee3b11316b42ac1f5bed3e91a5da2d87d8b22a2c89c49286b9c30f4bb3d4fe23fb0806e3cb87524a29a54540859b6a157164532daf3e9b3e6c532d2f22bcb876fdd1adb5d4ab7d1bc36dad41acb42534ad481ac058b35b1367b7866bcbabba5b7b2fb6f762ec795fbe395baff962cffbbe9c416ffc7206c13014c73b8ed6eb0886a1288e232c766331eb358654c57184c16231d918a342b5e9cba6367dadb36cfa403813d78409ebd5047d3d5f7afa7ce97127b77985b90a937515668d2ea7a4425525d54985aa4a2a95fae45cb68b72e9f10a6169d30dc3055275b7f65eec5dcfb35ebdead6de8bb1f7ddef62cffbbe9cc1f086a1f51a7e1904c3501447d885c1ac5758288e230c168bc9f4d5da7ad5b0984ca635109089a01b1464bd06990812121a1a3a7102c5ecce66d6eb0ca892413768aca490908ca1b192276032ead6282e0a14d62b8a1c239a01d6a11c239a91777d0fec164e436aa88bb448f40abc9040f0248ad525cebeef62df9ddbfa457776ff7689bae5b9e35e4c8dc2e8d3ace5f541c42fe2ba45b1d64a3defabfe32ee3b254f1f9be6a730dab429b9c3772e31a82dbeafa4a5b53cafdb292d2e5de2a65a5ce2ecba3465eeeca604b07257536e5076bdb80988f4bf36d5b241cfae4934d5b2816b532d1be06c2ae36e4a8f683982488bce0fb7ed8b32d26b32b2d6282c8fb99b66e17e79d2e9b94d52e9f37e6ddb8de1298cc359b6bda21d16009ba6fbbc5f39cfd9fce13ffd0be6c14a5a9e6699b3d6b3d9f9fb34372c7c180c0683c160397bbfae2d4f7dbfbe0f8a488b2e4022460bfad42ca6e8136534b6b2ef565f4b315a58b2d6ada5960ab96b0c9258a044639c38bba2cfa5bde466f5c7b818f68215f4a9ffab9027d17d93bcae5802df823837b31c3d39316e2f7db72ed18d7116e370cefbb2a23d623dc952ea2f5e6a0bcb53e72bdba37d850afa9cbd5ba73a24f6497718972098adcd76860dd5b6a5587313911685f9a3fdca736e8112fddd57814109fafb73c5f67267532c1ce2b8d8140b87a99dad12b54b396b3d9bfd67f0bfc72fe37b7cfad86ec3a7300f4b0c6a8bfcf65e37ef8a7c811e7d5ed1432acf1c4b2fe88ddf929e277d35c7923ffe73e9235db8e9f9eb86c8b32b1afc2a86b58421453bd49775284bf3c741ddb6dbba4c79d26795c4f7bfb70b659564d7769fb6dfa8ede7cd62b5fddd8a76d91db837cbf6bfa675d1818c7c4ab31f234b297d622f1a16e90318a929cdde4a31fe70b78dae7f7e19caa1b29797c2964e0956468383909d15285373e8c0d86cc94152848619be04555ef5aa9c3b18b4a035d9c1892e2e4c2d34b3ee62e33b07c9f220c7e6a3c70e4b4ba3fdef96b6ee5fa6bf403ae1c7fca24fd35c3259f8e0244b0f5b3217721827e482b64369be445bb0f81ae522d3f63f2dd86706d37563b0f2d4db0626ca936a54571caf3b7ab6d32e30fbac5d76a8924991ea1174a9f2d97e855c9d6e64bb05db5bd83e857c6adeb346d1a6ff9b963a7cd4dab44bdf3b8cc1591217240edd427261adb545d6d6c75fb53fb0928dcda7998f3e9f7c610c1caf888b30f6d66a2d78d25aeb5c5582010bda5f5c90868ea614ceadf65941df84d9fe77fcc0ebe5fa5aa8d6ea3d486623de0740a729ad3beeb6f885d0d94d25fa2ef1e6a9de8b228854a96dce9b6a16363a3d7c501ed66cff3bce468db454e4a69b5bdb1e36369a85105dff498de854953f0fc6170f511ee7de3f5d777a56ad1ea36e9e393c0e09a8ecfad6beec89f6b727fdea416fbed6e36c5cae98812a2db1f025a1e7419fb4eb460ebb6497966e5d0283655befcbbfb6efb2dccf5a21379dbe16a2945e2f7fe18fc2499f59b6a52935f11ef36a375edb5f68859e55a0439abb84e4a6cbf67701a35e77473241cf70126d5668d91ccd01a5f91507f5919066e55507ba067a7583a8b1a3409b1bad11c296ae608c3ee9a534fbe39697698bb6578641d9646dff3b5ed15ff89f78a3fd2bb8d2bbbcf4a896958ce854f5bd38d36c8e6c7fa1151b8fb6c7b6de44a57dacd9956adb9741b101d06916db9f45add6e6b054bb82304ef21a27db7f14728b6badf8bf9a55eca02b00a3cf8ca4450d0bef3dd2ecd244f6e9c6e50f607af8dc1113489a2b67e1a6b3702faad58e9a526a7568000546edf4a9cd20faf4703a0fe7e13c9cefd9fee74cdcb9b8b5c5aeeda2d7f653fcda0e4279d2e9e140a8ed44a751b2fd85560ca9c95f94c9fe1eaa64a32ccd6dcea6b0fd6d91ed57aeb339db95b34af9f47e37d3690f97dbeee1b68da2548d7672d36f6e7aeedaeec16f0f84c289ba2d7d7a51509ec7ea457954c6112b6f737b51d6da6dff5188e692f4be32b43bbd0bada89cbd2da2684a692735b350184f3aadee9d8aead6f6fbb552c047dfc75f591bcbb06b58cc28ccdc591d2d29352ba5d152689f3e76dd72bf4e625c9938091d6efb17b1f02a7b896a29e53dade1c7a4b62d5cd0e7e76ccbb7494427fc356aca5fe0fc85ff055afacad154fab6cf1a35353585a60f9567899232e2ac11d1a9ea3e2d0241851b5d75bbd217fe41757489d3185cd1e76cf601d5e124d054f1029cfe00e7013d92419f338ac6031a1cb8a900a7b9180ad0425b2aa079aaba4fe46bf96951addff77ddf57dff34a9a49e8e12fdcb4699b2d223a11f99af7df7ff90b9bbff0a7608d3eb500fc68abf274ed5fa716f23ed6a906dfa2477e6595bc77ec5757b73c5da7a3e5e9bb9d7ff997f64a8c0bdac25bd8ab887fa65edbff12f91afefbfec25ff8c358d0beeddbed49569871aad5d62124f499bbf6399bbd1042db2f212b6b5b5bbdf90b7f1744f0f0a2ebfaa0cf6a73779bd74a29a5b4565ad625846c5d4e47b65722319ab4d5a6ab6d684a9fd5ebcc550f95586bb448007df459fbecfae4496c2f83ab1f459f799f7aff589fee3006b7e97bf5282c2c3d585854b2fb2caac770f385bf2984beda5c5e4ea3713c4947fdb32908e4002154620c2bd6d19d7812d5127264d3ed0020519e44b582786dea00a79d27d14d8398df73999fee6badb5d6deb47d1a6475b456b4d88a32196592c96231ca148bc1609409061b47ca348ef4c87b91323189621852a6300441ca0482f4c863caf9fb28d3f7791e65f23c8c2913c6f4c8fb7be991f79632c1debe53a6d85b6bdf52264b8f484b8f9c1e59af94a98664a547941e79efb00dea9fd88de55c9ef73d80237cfc3842f0fe99c15ceab89f1fccf673f8e183a11d2d7e99589eb8b40f92b0c1177dccd75efca078411104022a6979dad023294c830b66d16e40470fe4d701dad89f36db5207f8a707f4197cefb3b5405ea923564a1501fa213f8634d5f4032dd580fe0750a9b5d63fcb1fc34d26babe7ea7a69b0d080808085b0161279bad06f44c6c35a00c66ca0f959ad6fa892cd57439a408ca1bad5bd59114d4d4aabc12072ce8c80f62b65ad0218830b54ac5e98c9394ac3e40e86a2b785173c20f5c782852ab1ffe08b3b00d4412878319ccefe519f9cc3bae573fd7a7b53a8c01d4969ee7bdf54a221adedbf7caafccf16dafe46137bed6bacc510c118b7048790fc3118bfdbdde057ab18e4123f859f77d1f56864761f9812fa3f4e8962778e6181f249b701b0cc7d781c392478a1dde1979ebc0ef011ce07b8f03fc3be27104ff47b0d4119af8fa3888f4bca75bdb36a54740e5e995f7c73bbe26cf26dc98c7dcb4f1ab80e182f67e3cbd1f5f1c7f6cf0fa70a27f72909347093e4bf8f953137a21b576fb6a439a6a4225b5b4aba39e32e81ebf31c334c0777c7ac90df08b2f8a0f7b0f34edfbe007be8ef0cf1ebbe49162cbfebce02d7584420f0a85ff3df8f7de2b242ba56873f49983ea760ce4806e4a599ce2c3fe6cc26dd04471d8279a9177f839becdc3ee26184e74f8f5cf3ec117450fbe3f7ffeecd9df3b6c06f85f7986e599a584de6152b7af09953fa8ae564594f7ddb93236ed647b0e4bc9761fe1dbdf8cd8fecaeffec723c7b73f18959849d8b9c471ffd381ffecb1bd92470a100441102c75782a5214d13aeace650794c5994b1d3cec961241fd0ff0e79d3fef5a0a69b2e5a29e6e4e4180d86a94d2ceafd4e19552f5716971102957adce0a45ac6a5fbac65cd57cd0218ecc70abfd18626393b83272abfda855045189dd978dd2227f9aad12d196e9e2c2d8f75ea4a5cfa336d49a2bd5853100197dea22572afacfb98a0c636c1f5becd75a6bdf3aaed6af93e8f571c5f8def1fb30c6f8defa38ae2daf275dede685c9b818635c31feeefd3e6cf50ca68187312dba9e9b364ef211698d92df4af13c5b9ea258bfa803f5f1fbffceb03f2bd818c5c6b27dd2a45806306a0beff17f54696673cebfbd8731963172b65433987d65ced2e2b5556c9aa585cd3ef1e7937ae59c3dcf933cef49b04a263e3f8c9ac1126de1bdb7b43d18db032a67b099acf44698379621b8e93d2e6f9090bf88f67049ad9873ce38b29b8eb1873d7f1cd8f33cafe2ecde67f7444f2cb59bdee71894c96e8a1d48ced92a91404484ae4f7ba3bd8ff1de93e049e07ba107e6fc951ee8311cd415d4ae1f5e4deddf418f31ae5f2953f899c647821d7ead3cf2d661fdd876fe5ada07c91d55d49e7406bd3f0e7af7a0770f821e87c7e8e3c3837940eea0ffc0e6ac35ae603b4d0a7a8ffd19a3d45a5c9d5ce126fe72468ff0b760a3bff17778d2073d424099bec7430ffba0c743250d730ffd50c9c2cdfb428f85c820597d1340babecc2d1079ceb6ec357956b0656507ea8ecd368691592cabd59ee4b5e2a8d7dfb197da4d7ce68df1cfeea5b6521bab552351e9f0cf3cc6f04a860e8236e7ec353e7013bf887403c5ef650d37b1073757ea31179dac15b463d621dbeeb05dbfd0db190429386207028275a447f5696ff4ac66503d7f8fbf86279d77f3a8d4bd129738aa0de8ced5ed7ebe97ccd56c6391b10f43a434e95efbd6ffca00b7ef4029a598564a29a546889466e05e6b0bbfd7dddddd7d8e789d1ed1f25e7ae46177f7aaaf303302bfe7b987e9a547b4faf05ababe7d7777a7348d48692ae6e89cf3a8a7c051051cad628e3e73385301478fa30a379afe08dbc0827bb58609a9d8d27af62ae06831cb8a64e695b1626b2020139452ea79d4a31e3d4d8a834c0401e9a0a0a0a0a01431fafe358787dbd60412b11e11eb0c584850c5f5815c988b814a8e1d20d0a40a1e9a665565d94db20dda54eb01645cc6cf2b4f1fdbfb3b8efbfbbef3fef7b9b6ef746910195ded57e0d673d738d37d661ab27db3fe5cec9368c67dfc18e9c4f6ef5dc221f821081fe8bb1ff898e82b3f084b0f2cdfd2ec506aadb5d4d24befcc5a6badd6e9159e64690e7397604a28350dce5eeb88d8febd94eef09a44f530ce997edf77bf4b1d630f7b9ef51ec334f0ae77b505e7cfc33b7cbbf1c7f0db87318d4fdf2f192b69f600ffbd185f8c6d4d3ac23f92951ee11749bff4083f487a1bbb946bf15f6db37defc31f6c862daf676148d1f7bd92be78f1bdb71cef119556a51fbaaa3efdfadfdb8a4bea41c518cfa8e508cbf85b6037a78a8ac0dc5b34c2528e251b4658aaf6491e78e3b03fba993dc9ef83611cf48047ddb5fc4a1e787b258efbb82ca911b1967934dd39c2dc463aa3bdf7a72384b98d4b1cb71c77368166e5acf56cf63fa30aa8549c6aebb790b696be510ced17da184794c9c77126ce66e6399be1f48863aeba250d2c0176fda45ddf1bcb1301bb3ea66376fdf0e8881ed5a8319ac6b19432f9aed56b8afec619ce7339c1ae46bb8e96029e54df26eed179d1f684e4e96287afc55a6bad59ba6e51145decb13997a7d633af3cdf34cb1369c9d26ed0a33d9805d96cc592ae2a90bee8baab577ab5296c8373369b8d4ee7521236375a9ec8cce9baf2860f102f8d0744ea4489e109932668700a1e08b244955315952f249e161c2553b2a27cc5a0848da6275c514bf441b3441165745bf6381db9e108ae4fd58da91b77cb900d4890c059d961b3c4438382094c4cf82a4111556cc084147186cb1d1c0b5dce20004814377a9cb890a5444b04ae254aaac8c025858c15335c5b4a30258c540f706e4481c189902f4bc80044d6047d94c4a962f594a5c5860b3c4dbc9c315244d6ae0c0f3246f6408d6972831811c6682561029544975d1c2f13a7c9132229d451c187126ecc000398342a7cf1eac1082c165de2ec00449e1dbca246d84325ca1e3e759ee4d9814b0a3e683d11e20a1c11727c210933b582116d7cf0aee024e9f34687216ba69c70032b77cb010b0523e49071c14f9624a07499418a08b12a2a505852e6ece9618cd11b2770a2d8a0d15382084e271bf1c3551f3332fc39418c102549b6ac94d17d65a0013c4e6faaa0f0406113066b882f6286f05206852186a8d3470aca11728c8069c282e785305a7d9ef6f490fbb243e4cd0e5fd674795f540862491f34527e9410b9f126cbcd095f72e0fcb9e9f0250fd51f1d96bce9b2e606510c2854b59003922266f07220d052c70bc80b5d43d019c109c9172d45dcb0c515040c373a31bc41028e9d1c3810b505d742102b46ac11a1cb098d10373d7039c20bd533e5c60b1254bab4ecec20a60b9e2d23b6ece0e15001334254e173c32e8b9c2a51585e90520218ba94d0e50f1421654cf012f364ca0dea67c7c3942c5d92c0e3e486bdc3116eda74519287ca86cba006cfee8b9d2525d87936fcd0e4aa4892322db480e4c6053b3545bc41c2e40b09973b60b29adc4a504217121b502234b1d1946407ab2ba9ab23528a58992dacc992c3126682dcf13978e44479b9c1891156b64c115265080e2b30f1937503881b1cac3036c03922464bd82608175ae8a2a6cc141b097061cb1139ae2542b05003794547eb872d50a68c10a187953f75aaa6b850889245889a394be8d49142831036f62881c706222c7c716282474313273a3369b6f8c0a97203cb9c125ab8124545c3110fcb1129455030a14a9a9dd48d1108218286ac2f3524c9b994262466bc58e0e36449d61c1b61961f2580d0e3278d91193060458bc89e365660a6444883a58b9ba923de9cd006b71444949d1f3b76704dc00ce1821e343cec50e48a0c43cce047862837396c70b871439939428849828c17295e0ce1540c526ffa08c1474b0a6ca0ca9480c6852d395439e5dcb84085490a67be6e6c202565a4c9982a4e463c2a0f7819f146cd183048ae8880d19cb0478c9ca72b344fee54153650c5cb1015b47265b44210e50299345cf8c040e40a0d49a488c0f2c2458d9597620407264a4831a9539facdce1e3b4030d3c7c19e24310281eb8c0cce0c53e8d5bbe865cc12192850d0d3044e9c3431ba81deac0001565092959659ca8497263823a3ed04152a4842272569ab22c6122e44894962f4f6c386027678f911f946cad99ea2145ca9623391031048808396278ca0151420b982552b48e0c3924b68ae8e08216154e8b8c1715d450f179618225e8d8c963240529146c046581f3a50d152b564554311264899e228a68012286a62150c8584569b3644f0f1e952f16a418d12609115bde0440cb142264f862e58725bc29499400f142c24a171b66a80ce12a82872062d05d2314cf1337666c00028395126e5420cc1162bcb0389da99292fb92f3414b0c941239ef43a48f121f6e5a40b4c0270a015288a3e5e6eae94e1d1b50d2440159a1862248d04405c9b1e112c542104fe6dc30cfe4a278c86201c90b1e1473ce9594921bbcd68c1101851b286e80f205852a3925599e0b4d39e860b36d61d9b1334401a44851192142a58b9f9ac29e2240c87953c50d57960d10ea4c4509d245880d7c9e88208d4e8f17a7257ffa8890270533564cae5290ba726201266cdeb411828812c408e087af3d3258993343550b5240bcc001d2268a0e58e8d51e27384b6e678ef08105334870f8631b82851e76371ed0650e1239405902c39e264260a452c839c1210635224c5581a20414a7218c580285a3ebcf9cad2b41d6dc5e5862c8161fbecc294187156a12344f526fa68cd0298990c1891049f070e181c99f9b1a3657308cd929a92ac2c709ae0a1f1f9618652139828f0b4ae6fc60c3c80b393471e34394355b4aa88e883f2b68e113e44917dd25797183831c3a5a5f639274708301891256a204313253e26ac81e169e54d1a9a0a30e9a266beed8e6d8e9006a4f1519323cd4a14ae104dc8e889a2a66ac68b0d1028b86155428733353e689027ae69cc94a7226cd9908b0394188177448a1cd170a1848c1814d90175ae092478415f854e132a30392ac2cde094d44984c59a2464e122a4c81a1a9881b2d39cc10853361887523849a2f2b4fa25e60f2a5469fdeb8381340acd0c64a1d343e7c21a193f373858b4bd7d5121baec3c0882e5a4720a13544093417602265668c1518de105900159e5d0978a688d1f283053453ba1eb8004122a6032c44edd19db0020b54bc232144e979c29a41a7e7c93342819b204cb09c9123074b7d5a3f57375a52777e40d3854a18a7b2819f32d8f569ae3f1be399b05f106208354ab8e401539fe60a0061a7ab873a3bc0f095ad08e29c7d9afb03bb64956ecd2b92222a869c0f79be708152a33ed450e5073450a818e1a5567f56eb53769b03a9b95cce79e95c98ece564fb8f42f9e65d2e7bd5ad9c57cee6b63fdd9bf8d52ea2fab976dd2893e5c0fad035d54eb77d64fd4c54f183f03dfefa187fc63e3e6d696e4b0cd5e47bafc4f8894b9fd6eab62d7d6bd565ad6c973c5b178cb556947e0543cb7b3bad952dadd595419ff8b66f47a23c26482ef2142959ed563a67a1388841849eedaf80ac6aa3aa764cf481f70ea0b508782fe5950f0207ee5d3b341a869b01c8d227cdc550c0120c1b75e37c11d1e5be963eddebcc71b6769ec341632f5cc47dbc6569eee55eb466715c24deb9d980eb91112f4d8d46e145e35d762fdc85b78870c2534255b7866be6d1c4b366fbb37041de4042e79c5f0be9fc9db893469fd52be7bcee1a7dd6ae3bdffb9b7af044d50ad360c5e7f55e9bfeac68b60297c564a1ef746d7fa115970592e9a696d225a90652a3d0696ea7d56491ab726677a8b6ff8aa2a7c04d0562f499895caba79bbb56378ca7d30088ece5bcabdb3e3deaf428ab1dd5028fbe9ece4ea1b89db952e4cb0f76c2646d0d11c7eb833521dca0a3b519af3ca375f46cff3b2ae933ba1e51c789d61042df1f8098d1470ce11262bf8b992e32a3a2a83895b7f54ff479af6e97ed7f7a5917e74fb8edf74e3dedf35edd2b4ba399b9a3b775cb5d4eaa14c6e4ff30e5cb84599e0981dbfe2b8aaaf983e8828e7d385a68531d8cc8b3cfd9ce39095f2178eda8c1658ba1800e3e284b60c3528ca5209cb63f0c1aa6fabd9605d29889a6e8cb6597e9e8ea67adb5d4d68c9762f0a02f65fa144024459f481bc9050f4ec7c9671003e2779096d8e81890ed8fc485ab0344164d0f16104e9c4aae560f7b377b37536bed753f52a19e94680c90e8ece7872c56a8da8b8288becdc9a2faebdbb91a735b5c61b2bcc22c59b78cd8beb1833e1daa2ae5709a43dd3087d027ddf911a75d3587a365e9eb44ee16157f98bd78d4f729bee4cc9ca3cfd9cc2901d0e873e619d9e18b9712a5b42267e12ef00ba253a612a8b4ed94453d4c2a45910000800010005314000020100a878442c158301e104390f90114800c81904878561a89c324c69118c5943106104000000000068866a07100c0293ed899b5fa46d87b8be6087853ce6a1f438fc03712828be9653b91dd2df5507a819561d2a7b5c9fe79f09d55c2cc6199e6ab98401ba5ebb91b10689dfc7270701ced8452f556c5a98fa2cfbaea1ede5b1c2ce7c1336044916e47a798fa65b2296f08f496eda95803ee0c7f926fc5b017c3933a6946616042644f60c1d08db8faf29912bfe2cec4677299470af4a8887247b3d13b29cea80f13b0f7de16e4c64699a48d5fa2b76e3e886c2a20194e78d0c33b8e7c9ef561bdcf4bb2b7c482819059f2cc507d12d51a22263c0815792920761d0e36c90c306170ce6430a3a5a34bd1e88e1fd61dae52edd8d5c0552b50abd6f55aa1bd5c83031a9d15bcdae09a4901caea5482977b39692ad5dd4cc41a282f80922c5e8074587ae25483e8b20b1f89ea6ed9c1d0598134b7bfbe4c821296be75a063cee7d2d4fae83a3bfeb619c09a96d5154aab50e281d25a73e5ba5f60182fac47419c8f1238b412c43d9842b8eb91e780b1bc3b7523f2a9b5e2159cc3397a2c1972cebee567e8ad70a6688228dcd48e65d3430944ac733b68831008aac203710d0b11a26de4c8a5242049403e080029d7db09517ade40c8570d35739e9728afa891cf0e2fbb09e4c81aa1d49c6afdd91678be009cff51cbbac391279a2ccf3b70b3a5bc541e8b0e8f041d9199310cdbcec88936c7647a1675bebcd75a5cc378b03049543a3acac87c4b7759d78a4562c5dab86a04fdb5002935726cefcddfd8fe2cd979b797e80f97c1ed6bf9a7b49d3943e97cfe14d3a4e3d3c3f4ae5f3053ce11262ad6662f0cc4def792faa4f2496b2fab289d7d5342c1c3775439d049b847b3ddd66deba3e551a400b9acedf9a4b1872a80dbf8f16a9b1e6b1e33dcdbe1ae46712428337801a6fdb7ad2423663ffcea4fd71f9c5c4e1a668b73060c33b76e90620be048d285490e69b02583c6bcb52e355a5b868b122504e1e1e448a819b729c215220481e7a39e58cce30ee476bce450855e93fbb838755bed47ed982dd1df0b8dd40e6367fad067297e45fc51941564d862cbbd1e53086e397284a5f277599d9ecb098dec668b8662de6337c957377611c01d480f877dd7a17cf460773aac5db006c2251353e843d7a0a5defc08ca014ec139b73e01f41449d072ace8c505fd918f22761cd9b2f2b825a82603472e1eed9bf4739e1f5d122d303e471d1a5e73bef5b8ee72a15edbf3fbefc8fabb257e2c22e0b041c651d46240617190005c1ec685abad281986dfed7f18adbc9b6de153055d34095ebd17fb81a372ec34c5c5e0de0f9415c8c1d5997315e8f328e144c5ce29e85be535cc61446ae308ce942de1837c1a8b764e2f948d7f9b15ccd2f521b999154e498d166c86251b9b7f5861bb5eea0f1ab28b00dff4a8ec18cbfe8fc5b7b3488b8f4e3f79a91b0bb144691f34805d9aa0caf3b7f3748541f433fcf64f1a3194b5fe93ff16080ad44d65d708c1662ca27a02e9dd3062e3692710fc912f89c60747550521dfd1f2358c5790e39bc15c5fe37bb29e5bbc92371b8fa121f790f928f71fe6069c1e71b8ff4999423935abe9571a9f3592cba2d2bd587fe5129311fcd16ae4aed361be5e708e81312011410cbf4c5c35b87a81ce37eae262cf39a23edb1ee5e34371d26b2d05fe46a56a4dee1cc2309e7134920809b8349944c74bbc80c77f62ab7b5d0471f4317fa0b6899f4b8e304e0786526e7d497b7890519efe597b0e485040ec58b4be76eb8729f721b1cc3a74fca2e6704b7669aa2bd69d8a916f7d2cf0f169361923c50c356fd7238eb63ed42a2bc1983c4bb5a30b6a4100ce5f8c1b53e807ad9298434c32078e902702b211b6ac14fb27e1aef83cdf37effda3d08153fc11b4ff2ab50ce627a83298e930a2e503bf0e91ae24a29a00ca772474dc88d0877babb3ad3dc0bb6688615c927cc8d402fbb24da0e44e9d652feed80b31fc44175054631398b87093c6dd6fc9ebeabe8c99f5fc2ac0d6e78506412c2dad1987009f9c7900eb5a05107d9123504374fbf084ec9514cdcfd2bf6d3bb88a0b7fca5e6c8129683af19d6d0c6ae5d8fa44646d03b5ee0e510ce91790386d2f3937c28febedfe4e6a280a186b291142fc921ab2cd633e42c8200cf0a8fd517fac789e733c3b11ebe9b91be3308976aa577c3c11c0b372a37c27de04a2aeeeffedb7f3f98a50bde27ae5f72189ef4422c9b68bad814705f27816f132dca9d0be24bee68da7278b8a7623e249c6a75a59e00afda335a266fcb061a5369fc1a726ae343a4d52741463126d3fe5e235e6f65a857a4684beb6c800d2cc69bed3a7aa96337d74e4d1fb7a06a1ac98d9c92eedfe1d564a159223b47c2661227c6129514d89b4a459b9fd2fb24be9cab02cd7219856c212893b24322a7b75be9f20af560e2a961b5666a8a6ff83fa01207c8b5fb6850220f868dc7f0ef7185ba946ca45617bf8149c72137499431c9dd710448d8285178ca56c9787be4cd94c950cd66800a2611ba57d74cd112160a7912b74583c60e7e44869a0eacba32b70ba8f511a6c57c15e880fc3feb328a65dd7e8d2347efa488be18edd84ede7a7f99ea923f899a052af0d1cd7748803a69f72d46ccbc1a6f80123ded0b564b4e8018b1bac10040061541083088e08e57fda6dd6d90d51190e68ae99d9da7feb17a50af13a95c85c908be0ad9c4aeb4c33a2e44ca320ca47f2e7cd2a7da78cb83eee634b111cf41e8f2c01e66ec52d6c08922aa9a739b7ee21542d0981d589fc6e63580f571950a4907c30576beff09ecd1f4a12c2b6885dda9562d64c797e4f7355e60340bb204c1ccd40b180096197afb9854dc45934f7afdd976b1026c8236cecf1887c8a00c087f5937ee0410f53817a1eeaa64781312596e3390f00842f96703564b8b714c1a7c502c606fea0100718ef6dee5e14a64dbcc2054c71f1a42124cce7d2fff40330189eb48a1515671d6fef23ab5acebbbdd969e0ae8f172df39dc11b88ea4d6e1feccc1c9d6f19f4a0ba152aa4c1db8c8733893aae2af1a45a6ec8559d1048cedf47fab28cf2ce796bbaf14d1710608a9fad7246aab5beb1847fb6123d1464e4690d043ed7004944c8e8e4a22301ac8a12824fd4a8b7185961138138a72292e1f3d41a9aa25196f4c9966d6334aaba813626ab49869fdfca98619657c53a43e515d209a08f7df49c3ea4cdedd0732103bf460be340238b4c610e2d0ea76aa8d6a1521de3a9f21c72d1594ee201cd58011cabddd2a5c7fa6f914ecc927db430e49a004315189467f82732fa1bb6b12156693cb7a92e05c3d34e0a41906d656201a8ee4bc37e4a62ee8d3fb52917ac04657472330dbde4cc0d0f016afc46e71af5b4e08964461aea26d1a9c320882c840331257f5610ce0a3c53cbec712f8138d939bbcb6c3c2a754a710dab3811492ed2ae00c50bcf683b8991acfeb483cf595a65fd430d3eab0c2da24cf91153024b5d6afe9dfc4405b536bc49ee464f5e72ee89140ff260e9f0bddeb0ef2d9175ab023a667162e86ac0545569c0190f6add9a526b8a378c3da9e409d2ac4a09965a2a2205e89cf40abb3556852bd0c2b073e73f7887f2f0773a6ef9ac5820b292ba6bb476e15ab5308e93c90cac281154cab29d3ea8650fc524d5ac0ea857c69a76edef1cf9938d1e523f0594ad8a79a37fdb482795747da6f08b35ad495792aa6453c1e191d5aaef99f45a4f17d7a8af5b6c6bac4e57a0f032d02420b940e5703fc2adda063700afeb36f5a369019508b01a85e080342e73da4369405da872d78f3d5819fc35cd87c750a0651296d3b705e6b6796d5e3e18a73272abd1f51996345bcdc0e1b5fab565c7126c6202a708b15323a7282b2a76476cc1c8148f24ba7eb4535b92a8e85a245c6ed3b0ec0de565e543a420534e664735ad9b69ad23191853ad3472525f64e90d6353f5701c1dbea705cec126d5b774a8edf557350350d931b1860069945c7dd5321115339dd3aa67a890b529023eb536d160194e0cd93e4ab095ddd53e977bc6b57e9edadb0623176a56578ccfdb2c45b9e4cc6d6a5f8781e4fa863700163577aaf287bf3aa36cf96cf1090e06754e7e8c1f183f81193252ff9371cfac1df7792d8046095691f4b45802d2a41a170f02549854b1cca9dcad41866fea480f25c3dc1d83d8487f1090cdb9fbb2fe4dc03295c4c52639fb39e07c80fdeffa4ac8bd02d362dfffc549a8bc56fce91d3f9fce66b60a882cc6948265179d5c9ba5ffab9a8691bde607d5236229cf7e64eac7df383b11423056ebce828fd3d1afd77272c3f264e39366715d71d3293b30add29cf954bec84cfc41c5b0cc2fbba74314070cc3dbeae2893a8328a94e645f90fea3c5e8970bae2f009ab85c1f9963184e735454e80460283f4b2999cee601aae8953fc5c3389286171b18787a27a35dac2fee11d0f1c06ec66ad8f9cf4cbd1a240005bc3e35d35ed4aa5dc23b44904333865bc550a01e64de01c7d95c678b9177227e2927b1a14e59de167011e68cd89017bbd2353c7f134624dc8f68c94610dcbd1869b0075155f0e115e5d653756023629474a5da783e48151ce18ce3468a9c74df0f2e75c5bd42490222ad0acc64fdba82c1d889045494045fcbcb5fd931045f59e3ca77383081556c63df68507387a79223256796b4e5463932772d9f5dba372f4dc6ca3d1b5532a799cd8d064328a471534fc9cd216e39be9920a737abad397fa4855750ac33ae7b3cb3424cc2f3824913f5b8c55d4a2845a9c31dca6dda3105e2eed63e553302e279e4f6876b4efab6bc2c2c3003e82dab162121f6cac5a1515ea7c92fed38f70759f9d41de5176605734195f7711cc73dc307b608d2375dbd594fbd190f7fe41e728e53f131b2a3b9e70c36493444b03c3df3880c0ed2c93182b7cb1cd3af0e337e18a821bfc03684735e8011994f2263ba46921c6586385a1512b30d15d3eae3e1594a463c68a82898c0989dd643fb97967dbe15058f10f741bdf258ed802c145e845c51f968bb7e67f3f615d60171972ecbce54f2350a54a3cab7db3056cc16fa34085d602ec20e677032791e01b8bfc87e4d0c31ccddd2d8485a316e10d1adfe27de7b3cb561fec6b7034fc6feae1028086b53efad1b2e0fd72c78e94645e658209b4192957f1064d4a96a5c15ed1504f4e8661fd8794dbefcd7da1189f1a5530224acb6ef322ac62b0f10bd82258fdbaba321a4e8788b92d18a1b9517fef67b36dab4cb1842eab177034944bf909cbf398a8ab54197a9690d41a092a58edc5e2008f9b185f57873d40217caf86f022238517fc370e8f531726cf77fedb8e8c56ce37d791be061bc6d70d431895687f869a8cdf5571a556ac6cf7b3f3e7a901014ae8891b352d75bf88e6f3217552d045524790458bf034b86278016bc2f9b6d214e776d9b9d054ee93bcb7c0729f0c055f4917feb3fa71ad7ba52dbccf89bb4122218b8843c1e1d2adc2ae0e14c91e2b119c93acc19c1f2758b36aab1e35a9ee584cca7468b466fc14eddae336c4387adf6d0feec1a93c7083843fe8ddbc107c177a3d8786bcf2627ef96247aad11608d2a3ebca874fbc0a4da1e6f027e227a6147594e2d5766cb6ccd344eb950edd955863364653ea8fb740c5c813b23cd043e1c1da62753a302334a58ea7342aeef36bf715d3fddab0c52be8c820a6508d4f7075f39edb987c3b3f96199ef5e909adff3f5e6a46cd6bfe94c3a28cc663d636433134f7fbe29b0948625c59abb58ec2e638f95e7efcdb4b35e73804fcdf55f256dabc5e65563ca449056830eea09c77e89b593f326d62a53a39fc64f2e3be334064036a50270ead0a328fdbcef80a9a657f7cf7ec5c040fd55762bb797539a8af7cb3c736e8909b16f583f7fb20507ce89b04a1d26b8a1b75e02e9f21173414ed90ceac077af06d547cadfa2a2ef8dd82a3366505a2e46b2a465f3dc4afbccecaaa9505e8a51080637f927c1a532810317c4b5c41cc125be50e0d7dd5dcac002b0bdd5d7abffeb8f60a7c3672863cea9374ae840a817cbbbf1cf4cd34ec879e5dc10bcf2adfafd2610bc8b5772ae8bd4292b231243f7663126f7f36a373142dd6c828ab222a220515e44df020b13dbf1946fcc54468f66e1991df09c9b26a4cd7dd3f536e7f3118400d3c68d4a9e577559936942c6f8df8d1770ccef18d3cfbfb10a8dd4e192d2778716894e7ab900da978596dc9ab8756387a280ba2496d9e0958dd49f79736949cc00de151cf5062cf0a96d37a851471e0d6b87e315921961baf0d71fb19e21f1d798acbc8cf0cb69dc68d8ead7a8ccaa4b6126a340cdb72d696ccfa76cb36d2fe90f016c30de5bec85b416be3a8c6438d3bfdc6e045024f0d1e7ce5ff2af7df634f367b0cc005220c1c7c80164f03db5ed11d956b4d03d6f57a642fec75248398995d6a6b704300a12346dfbf2b497e867480b304777da8bf908b848279fdf22df967c2e5224f0329b58c91132fa91f1875ae973906221a14221cc9fe32316620c4ef13d3ac78487296f33d47ad2a95e17360eed38b632f5c1bb5931021bedc4b9f53f7682ab690ba487f6fce72811080ca5d3fa7f96914ea14ec2638dc00cc90a7ca0bc15a95f6e8940161a9d1ed36a097cd876eb01378cf4223462cce1d37982294602996cb60c4bcdb56fa228cce7823782308e1a733d1a9b3b5db85332cd9f29c9d8cdf1de1ab8e8ba06defd1c4d3b4c34dbfce45a94e1630186e3467a2bf0eee477acd33d04b9e7b85f4560d9a2dde124475bc3da5831b81c44009cf8b266572d85d1447262b289e08b555e1a30e66e4a63bbcafa224f9ffa39d47d2eb87b38e64cc7d24019f622a46ae4dfcb5692ebf43c332cf2d13a0189bae1ac5ea0e8c51e8466d57d36448600d1212125798c7ab76cb7ee5281315109ce8ec5f781acd5f389570417467d87ab5e9d7a194e234fa69588259a07a2a57b72d1969ba3df5ab6a5cd7ba871088bbed5cf8a1b113f2f1ff0450adca4c32d49d937e43c43c219c460e4c138ac302fbb38fd15000fb257185f06c6052a1d847210978602b07dc9aaeba57e49a9cf8812bdd960693ab923d9a466a66af1a01e5730915eb2240dd46e78cee481ce7d4991e3f2e82ca47b30e76f167e6f28c2e3439f6b04c0abef516dc39aee1d156385353995b08b584fab4b09e9426127231d07c7ebab9e38244c7ff579c02ab9964a769b49417f95a9395b3326f29a3ace2dae2a7436cb68eb3c1af14a51714aba71b1cf32f8f86d83874143c29d03ee93fc06c17133f8e5c865a7384712517c0e68003b379ba349953f2b9ef38f2ff5e6f84e5b50be7867326a9bef68b504c7a18101972939b4427e6012d7a21a79e2b6f8559ee3410dba5001174ab18e05af7b992175131a92d768631dfdb477ea8abbd0854fe26e5078f5a943bee104f87f99da0a53adfb98f1cb698e0ab7fe4f1f8c129d5cd87afbabeb2fefdef7a1b1c4b28a8d81c0309aa1c5b27601912501e6f99ccf0cda59b51b325e178afc802c29c7b2a2331af7a2e66617c43e91bdd7a12b022679f966fd71cfdc6f2e0d50600affbeb9d9b57186feb1694ff16b4c8602221fbdc9472de885986074907e813060cf1a7a86646eafdc9ecfa1295f99d374689d2376f81a2389da2fc28f660a74e96830e8b85ea2b97d3d748f1e1d455cc27a683a81ef492e49fb468b5647e7ab8caaa5d884825ed8c97e018617036318ab49e7fdcb611937e1bd2bfc9167c6b250126987bec8c01eb19ed2bc9137f9bd7bfbcb78e72eb7687cab48f72df5748885124d1a231ad281be19167d1c6798849e32ff58fbd1daab050915fc2da7e80b0b74b4dd71c0ecc604fe4e092d673624649239b31d97304f29c5f1608520f09b8a27add56e1a4cdb825d053c0d85ca6b9f6f63b476af061ed22e29496bbab605486acf102522090d2b7fb7990cd2e23f40163bff5f9cb040bc779b85acfab09399fc8aaff494ae1dfa57a7356f98e7f929f17a7b0cba37c83d4f6d8c9f6cbc4a688d7fc5c98912c8ceb16d20ff0562e5fc6db66c7c4266db30c7dab25ed3c36a62427d6dc80b35e88ef2c44ef32664c5c8319432d7f90c8eaa6ed7ddb3e5a6eefeb60558bb0e2f3c23c4b2bcbb1099d12bb421d76d6d9299cd63210834632e8bebcab210b2523091b56b68e673510a86518e87555e953e273c1fba0da6d09248d61dd6ee6277cf181d2a44c82702215bc215c4b12a6e498936421d5c0a94c28fe9a43829bc2632c91571f00dd1198726c272a0b2578c2d922a1d65f3f01805c113447d81152ec3015ba9e1d0fbed6a9e0769536f81f9ff940d2a3eaf80158b23e87dba34a1c96617015bee56cdd86ffe042c3441e49740e9ff704efcd9e07ef1c882cb4f510cd20720b6da1bcea32c2bb1660e49137d62eaa1e82ed4cc68866de405c12f6326b15bf058a97176665d6904684c149e024842d08838795b1f2c2b9ed0c870d84b23daa7d107220f8b2baf98a3cf7e5a4bf05c9c375dae76cb86e689a760585ee17982f72294d45e8b910524c82b5ac1870868159e58b468576135eaef9112aaba7bdd138c4a282c24c4d6461aab9aadbe2af7be416cbb1604c3ebaebdf9e9561981a31082907be7927007e56f4ea0c1ed0b48ec2537abb90291056f2219e9d482da35527d69e4bdfebab8a695b292f0e0b74bed81764dbdaa097491d9e0a789e9b9d098b77b2ca684b095a4884d0e640bfa9b85a93262736917045753305384cbd7c2dc96ed39603f4a8019d9894856e28d46156390f3976d1f8850d4b58159be2712bf8dd77415ddd61df4ffc3d21f478e790812b02b4e7dda43c0e60e8433f5c89e6c48600f64fcde808cc17f3eb97edf1d53f633ff5e260122f7b146727ec4730459908d84a5e69144a7657055f4c09f8b3673b5422173689e6e1747f72683250c4478074a6585272e59e5f8e3d06528e4f1b742bbf8e64edb68d33964442a92f63d4adb6004a68a711f9d6a16496aa04d08c5c911285b785ee371dce0825f4152f9c6c4c515608093ff5490b5fd5b128438ae5d4d09c39e4f025ae9c69433b8cc292ef831e122e4b7d48389fee4caf5b31fff04d18241479a9aec54dfa28b3d81efb960ffa2718dbcf42ed696faa08513ac1d8261b51331a0be62109f81f4bf6ead296aafdd24bf0ee9ad7ba21f52a9b18bd5974669684ceb7a2b8f6bbf58b9c1af0062febae782284d7963f8a509a854b520b931f8505f7ccefab7c83281e84239b6d0052e78ab2a92c171e7754f9f5a4c88b69e89a24a77abe9242831d94b5fc90a49ac0d4dee2106398a6782a38734f828b32188c03db7c4ec14a65b3ad8f9eac97623a2aa8bcdcafa82edc32aca002835fa2d377185093a6976ac7026fc5ce7899358594afe1ed5cd281afa534f72c46940314632916908d12588f5fe46ad917da775e32f1c862c8ad45463600a464c257c20ffb3326665093f3add3d018210344ce57c1764dc3490ed4eac6924d6521fcfbe09abde9cebe58a3bd133c1dc1b787363f5927b39f93ad9fdebea52524c7df95ce93cec5d1cbe5df269af779b18838474efc3d7dd86f73300833232912ded525acf39839e6d15f64f06a0b2ee196fef7c3e8e56ab899944f50b40841db255b76c418f758fbb036d8ecb1faa5874f4936a3a76f21579681c29be718238934ef0a3e019814a174c42e899c7c34fae5c5b681713e8ec24313724f880c0c65fa3584668f6ce3e8480d8fa02adc449f31762cc1c43506c5cab71e5fb0280f48629d35093b480ea2961030a76163e00c264053098628be9f86ab32fcfc6d87245d1c641abb18d9a3f2c8b239caa013aa07ac244134eba0347262d307a0022bc8d6652411699c1a00690e08a7498e3eb5f77b6f52b8ed0b6649b49e71abb5dafb5c7e7dc2d403fc5cb8d7feaf5e5f2fe564f832a3e04175e41df9b9bbdca8d4696e5287e6de33b86ee92d5938c6e160eb56f684cad57a2e7860cc1d33b554cb5df92197f45ba240896de3e53362acc0bb9deb662a1cf73b8653a4d4a73b8b9823d5c6ac2acc275d6d0fc5c56228719fbf1b972f2f040fa793a78d532cc73e9889bd97014fd488a801483da7fa081a691dda23892e787cc5ea7077d933f5d120acefd10e97405c3777ab30f62f5b5aba3afd1f91444e3b0f6ee2040e5761ac2f6f8693700a16f589c75fcb02db6b787ee3a9d96cd4c2732d6987d9a3913f712dd185095f20436d49291474a22187b1669830d1c61c2c1d15f538f2fee4fa0fafde6d07740e150e795ec55819f7adacec7995742fe397e3b7f5f506059a00722c86db5a9147a3d79d6cf421802cc3f475c7cc33f977d6856b70acbcd9f6dd44dc09ccb807115f44b5386cba9fddb81b3184e644e2f98db0c7a3234f80efee3502c3427be286134f1e168ac8908e9480a98613ca6177896d5becefe406da87dd7ceee004bfdbcb3b31f22b1f08d954f517b14d40f16e847ca2404d83b58ef708514d83377414006c3b31ed2c4863540661eb61e9fbd17fd44f887d918068ef24f282e4f00860b69f27f94bc086718c74af26e29474e145bb1bfd34062edac4a6f98cb6d14809141b4caa842e55a96e3d3cf531ee382dc0fc248f92ac7add55c558cd0454358ae2b8b14c7fa288a73b604149f07da660cbfcffd374aae9887fa45eb24ffe13a8555aa427bc6c9ce24074c9ebabf640b780a4ed4a41dd524ec96d7b9ae7d594dee788744c58b9c38186a402c1edf7eb31177f46c1bc72b3314a8382172fc70f71ba9241b73203b3843e96e6d1cc56af5dd0f9d0e67ed50566b43a44d1a83452d805fba5c712d104ed10c64cdfcc0342ae9c2a68f49402aa3ef89b00b17f026fc45f784edcdc104a52e8b90943a2840eb067d96471b4d9c49da532c6171a9706a9ef2f94441b6debb794ad98d415788c093ec658c5570640324d361403103b1ee9868da36fb575ef26cba00316b058c0b806854484a2a89fc89bb3407d434001fa169b3baef861867287a44333d30008dbee85ae4283fb975e03ca4e0d0096187e68522d23041255aeae14d9bf577901d28dfc95cc78c91bf8861e2e2630d545ec598bbadc88ed5424c28a3571e5faaac383ee736ccc2cf301b115317181655804ecd730e4374797abe8dbf1fdd5aa4b9fcefb2936f86eee65d12ccb7a99facbfdb6370b9007cbd9f21be60ee367d33950719e6ca321f9682a0b934e85d0af489f0981837ca4274990bf142b1f6415e1a8c2e573c0513cdf5918ab1247e56c1325e670b85613781dc446028d0a541ffea9efad700268e88b309baea1fe60ed018288fb9a46e9740c9fc6113de585b19b575cb98f6e1f256fe18818d4b3f2e26e84dc7b5f8a8f271c54a460f4937a9fe04136a70cd2e21db5edb88ccdd7b91f88a6d213aa360490d333f79755948b6b4cbad40c19f502c2dc851157cbc33e6d8c0108a4702d60f3fcae3cf8108340dbe0770fc09f10b022ded9a0c646cea8427903ec38540fa5160ac99a4052124b0d0adc090f61e4e1a42dfd850f8914d926d3f3462e854820b2e7fdea9d30325a4dbe80578176f946880c65abe118b3447c1c9a580c8533d2af7ddb3d091c582ba86452d887c25d8cc10a6ff15ca7a9b2aee1df554ba657e81b53ccc07f404f396178eac18da74e3474f95141a1b1da5ec812f34548682b239dbe085f98b725444c65826c86bcf522301dfac9596293648a97d3f4e97321d162b14d01f97aac08859ce4d6c5cdef09fa4f35dad7a45a49e16d270e1aed18fe4711680d6ba7d1622b4d4adbc3323a68416c6ff6a000cf9d0d350378f6ab37017a0fc3139fd4709a39431348ef454fe579791161990bc55bf260866acd3e76069f6dc2f4a4dad70b1577799f294ebd12f4340012e1bdd61f46ccfab4a0006c11789747cc56ded7afce0f1f84a9c1cf0545d5cad8a5ba209749f22562c8b9f38c9f7f61c8fc2ff46c8bfd9d9ea9ea3b2dfd0a0b833f4c6396f950daf85407014c63656448089dedcf0864da99071ab681ea6ec5299b6f451b35a3356c8a0375e28bb58bd15f60f5d37568afbba048811f2ad52d2eb362386c535ca510c20a1f81373b744b29f80864be2ff16ad4205b60f0134f79343659fda65fd95a2d68560e0bd2dd4af9679eb2da8cd724cb6c456c332be37f2e8dfd0125f4ba2ca669891bca848227255df1cc2c04e6b89ad707c5e1b2ee62aef5d35e09933561d087273d8f2d68d96ee3159d3573938a38ee3ad42895b77c140e387de559aaf4181eb26c67dfa1cabaffe2a4c922d4814c0729e83ebecd7e37c3e0c32dda1d4c6d4dac510c5a3cb94a65b4d5eaf3ab48298e443e34511767783456d6bebc32c0b397b38a600b07c4a8f578d4983fd7d3533c569e2e0c85b11cc08b98eacc0a0c89af423173a5ee44407fc5bb68ef30e9084b361f6a8553e505bdd0c8cb079936b3945e48f686f2428101142be63548b9c3729543485c7a10098efbc059cdda9052609b07c513cc93ddabf8782465734685a6d319092f4fe80b107336bd3ac521cb5731b3708ced5ded4272822663e7d28a520c7a417869e3fcb9ffd9f763fae715e6118f999bc8c650d5e42f85c2b92748828481b809442f741aca1334293f29308a15cdb0a1d089c3c457677a1c15f9082519fce785898efcb14ea85b0b483c8a315404cc8631e3d5a26cc8319b7385bda59b9258abbd25bc621f66d06c357191d6afa87ae22fe2d938b5442ae72f40d4d501dc989a97eb8057be41d5b0c794aadec173078fe48461c1ed08f46796267e551484df366497316864db6405320286de1d25e678e5aca70efc198a07ec0bfa5fe8529f36a44489dbca491db919626f32e8197d03499bf41ede1571d032239e020dd15de620e36697543158863a3e5ccc57331473de64278d852dbf0bbbfe6a4e9f03ea3658d6625552edac869b80c5f47fe136969d2a99c890904b8c441fd889c6e405e44b99882d58b4675fdf1371efd3dbcb445cfb6b1b7c1519f4d462e92c1289437282c262999f22c562376310c473ac76beb94be7de467f0a088c003aacbca25e314421e50714dc8a13d5ffe55ef300252ef14de2c337cc3b19b650a4e87dbb4d2b28d5776754308aeed5a0174913b3500e98785025aa8b38860318b0f0e522f7d6efe54e33314b63db66f631eecef47d8cf9944f3a14d988793a0c764cfd3a920441e37d2523df11bd48b5ad9ca9e6a75c059875821a6586d797096aa1e8b51005236a3d8a92a6402a8cefcfa5515989b6c9aaa37c1e1457bff963407ddaa3fe0e93a466956e7aae3f4e1abc0b62560ff1fab4773388523e57d4bc54ac86bd8a6aad0c3e730b1ba140f34b1eaa1d5200b76522724b50120f30840ed346aac4147f52748f287e4f361d0a6cd651f43be418f6d09c8931fb97b4a3c268d668c13db1cd37866ce4f63026254be4c972b8d545d66e9b165979f901d9c800c995facc899b1e0a8ffa7eb119af58b0d9f0291a5dfd9797c4a0e855285e8fd1f3362d1e2a59a6b55d35007be1b9d19c585f4fbc0e3126e4ffb5faee8a955c38bb1fb1e0f5eff0f2868ad95ac40758310abe054be014e6ff95451e0b1a5282b3c244447e911e91b017b6b3e63a274d41a3797024b03aee6f6d1552626ed6178bf7539ff9ee6b6d16cca12283ff9268c93a269cfe6dfe0fe42b0f13c55b8e65004a3091c13c8a997ea47c5b28a2e591ab484770dd25dd7daf7f10dfdc5e1b94047ba097e8830aec009a467d07d45fc7e9bae25224994b502e50a2942d8ea26c2e23b3d77823b58788750eace71e8623f202c3bca507b922626767e0ed54735e13a5a9d06636bfdc544e41baea23f78a836ac38d8fc68c2e68df951a8e6b220f740c502206f15239ef07e8e2a334988148f5fca2e31bcae505d4ef968d3ff32230d9e460236c006283581a254bdab603ffeef5b604f0de8802551b857e7dd4f7cbeaf4625bf8160f8293a4afad2c7fde4b2bfdf9805e9afb7e74192c4ac9c62dbb5c41c0c42a7733b0789f1f295d5e51e4bf72e3f0a16b4b468fc5499d1405c4324d83c8767466bdc426a0bfa2c3f04d5f1a165124d957e6a4a957f3fceedd46b06d17aa2ee9dcf21c3c570da5eb06bd1dfb818a8824f547c3aa07a7370be44d960c78b027295e4faa86467474c6ad3d3da1968b3bb386c3d940c51890869f827f22bc494732a3c9ac28a0aa4b4a1059923f58d7d652ca69854465442058c8d6d313adab4f8012b5886d362f4b20af051fc4f148408168817e5fc652a59cf06e305503f69275ec4514e63b67581b62738c4e7210be9599ce10517396453ce9c9c645edc4a7b7671464f0f445db667340642ed152649035398165986b06bb0e0de92fa4980511891a88cce8ba3023206b520637c0220a28aec10603ca49f44229108cc310281f4c794d8c230f67a8a26af4a97c48eaeae2bbccd208aaad47d17ee3fc3fc6820f930b2d08b24ff8f85313261b9387febec8df40e8581bfaa1a4b37ba011a6ff5c7dad37356ba7d54e30e6a12d05c8748c30911cf181d466e68d08eb6552cdc3cbea3d41da6207111553b552836ed0895244f7a8e59873c51c94fd64ec05dbb2d832b36cea55218bbfbd6e9d6c5cb60b1bb53ee7b31da9399bb726a7a4539022aec7e6ef702161aa1a8d353272b6e5e2c53e7b5848fc10c0332e0998748b8f96de056e0a8343455ae08495445a9ea3497a6947c1c44b8b55c59f181ca32bbf5ac977a4e3f8859bba9e8ee848f98b429f1e87f9bf5d4c633d13b5d66805264983806d198bad46408499d6039e32890a03d1525dae4d2b41005a9478d652f8887c8c8876c28f5430527747c9b60e367a67ef173321035e5fb5ac0fd7f3f1fe550a12db7cc11586895dfea22addc09eac023948733a18daea1b12210260d82a5e050b8adf19e1952b035a726255660cf1c2e537f10f31947c5c3b8ca425135d8c9a7d069d5e6113b95bb0e4b8696d189725cd37ad1a7fefe0453fc961c43c4c3152c3bf121e93aaa27368af0c9e0f6626ee522fe29c9c31be7b24e055efb904c06de4d902788d22fd597ec1c6f0f799ed3d3c306dc851cf6a820eae3cf9cdaf7a340a5b829a073b9e667961917a274ccc64c29814eb2fb797a4a78cdca731968e8bb74d737b1a0ae757585113f401cf481d14327c9a47bbd009514a1f54e489fc1c06832c95274986ce4318a85727bd2d2c801cef0e3d9821d9692c9571c7c93ed8bd5b25b66f37e5676207d2ec25beddee3e00ca03186a2329b0b36a2a1822cc4aa2cbc20dd6dd02beccd88c64964d7c087b9755b8ef7b46d143401fa452b2e48c93a4b3024ba23c547f83e9760ecc9b32fc9dce84c0749aae7e311f8439899b66689adfdfe6b90916035beada0719357a783083b594ef2f106742a4c9b7066630293ef3ee429fac61fa1e56b80f5de1b8d87c608f73e39956d33746f635f0b06fae20779982c22cfbc658792e6218632a86b88d742bcdd943283db872de510b2524d41d41eddb30e8c554af26fc325fc328d7eaf3582fddfd8699afdc492d67eb5388cde028af3f11b266bc2f16b376b825dea1b12504df79f355d73ddf95b2b6faa791daeaeea72e96741d9545ad722cfc7735f3ff43bfb00981b85d95fe422314fc4ee9c0d1436c638c7092759c7414ff3f621278d761fd26900db0b0c1c81c31f825b30c05717a00e453b90f46d27ad9611b425804e35b8cc6a5aa945e523097cedde937014d0639e067ca39b49fa6db5405b0114e19b57eb41f00fa530a576a27646f14956e131930b0746d3eb30d7991e6c00d4eea35721f9d487156dd72b46ef938efcf671f23af525064d7db14f0c7b0eb55d6291c8a7af25f048b28b280ec182d3306beda17e27140f3d97990306247fd9708e2c784af4d23ab07ae50e3f6171ce89f9c112a707c18f0d0031f2bd183190f8ad15d5b8aff87397b458230f7716ba765cd59355fd48ed9a83e4223cdf5593536f36a22801e5ac66976e0bc2201b4307c283eda31de2a4e766ea41a0ec24182b65b00d85bb70fb6fde2fe18b66c0a6107c584e069a5d2299bd4762f26c69bd46eb508e7e048a314089dff082ef12c5b665c9320a4f646bea134b40bd62de82ee7b7c6cb6f0a3105f805c406dc4ee6dc1d69a574ea079d58044f7317d68eb596e791f88d21e4be647334d95f2e3f6f0782d3402f0dd28b066e35d5eb796a064eef403253534930bd081669651f336d02317fcae3b9274899d810eea0fa10827b6ee49f30b5dca556c30de0d86980d1578d1d7bdd3583d5ec69a115913f904a925598df195add396d62381fc222198d7d1b25af5fdb6ae1641285129de9549c5850083304b91d4d83ace21c07e25db7455d4e5ef650c212ce6f3e27bcbbbad0d08eae288ed29c1b176d76b340712a825f0d0bb012e1861468d4ecece9114209cdb28ac4f21ec2ef09fca3e64ea060fa3043e764611e7fdbf6f40984485ef10c2e05257e7c33f624c310bd77ba058f108878e1c7638d1e2ff1deb6ce50d5c771f3168729fa3bb2a90cc33c7a1560340cd0a5bdc69cf6723aa5726fc7727386748f94c71f21764eadce8449524ed849975c27db335c8f412f6039bd74b2d5ac33cd2b91ade5182c1e66b84cac155113ea784dc54a48d1d9c77a4d5f3eac5eccab494cd6a5986c67775764e563d8723fc1d8c6a030161b4e410827337ae47bfc448e33ffc35a8c578351bd027cbc7f172c4a91d79db4ee63335a0123fd4348140459a8df6ac1ee728aa7af9cf33b1deb9204a5726e07bc4dc6761f4a3a62b7a4403f2c918e70383a10b1fe2c6b0f95a817f5c7c6029736c3ef35ab2aaa70adbd5ffd2884e7702fc1382a4fb1bad5973b3a33db5f8a8848f8b3b8e8370e6dee0f3e36b476be9a366293bcb1f38e5752831fb5b35b0dd70c9a67a8e55c6b31bc80abba06a2b52cd27ba8841e239ab665161624b8f2ad613e6160acddcbfea09057023fc87125efede61d3f17ff55fd4f20e86b80ab0abe9549ffe8850fa97eb56066a5d0c87ce55db4e97624d04c80ef0007d7127c66a4199b0f0c9d502e2a1e02664f82fcbe5d7ed4f1b61428b2fc943bdb838d5681779ec318a41f90427ca2535534e40c913e5f88dc801777cf49d8a6cff4a7ad7d64bc0108d20949e0e8b38fa1a9e8f525f54a25e8cf7144b7fe1e9373dc7833c3fa36d75ba6bf9f98faeccb47a6f898137563b70be390084139a88647faddd3b1ae983f450f69963a09b11a9ab24ebee9ea7626fe9a1403378cbe66e8ed8fd89a8973942fa3facccf640ba707c62a74547b2fa3219d1d719c35c447735c1b17ee32af54f84ada0f43b1fc75db790bd376208167e7c6b4fae36f7d35c0e8ef1bc865feef9f1c6c5c0845ac25604523e6120c0a0a0ace96a37e16182767b2de7731398ce8b9ea3e71d0d6ef448fb76dd033d57c32438e968159f6c7a4ae1bcf391f0905618c2dc0ca616f29f27bf12ecc9b9c34db97905d2300cb1f41c797095cc04ea876213914edc1c181b495263bd79111f88a692af0be3cbbdc27d87fd13d50a8aa10096d018c730d2792bcb4950d83065d4e8ac71416e0bedca6df1619fe92fa6703b54fcea409e13190eeb8793f6b7e51d18364ee65a3f1abc2ac200988fbcac149368d9cada14021d9323b5b4146d4d2911ff541a214d3a9ec8a47f9318289e62c4f50dd03909d630a83f5c972615de01796da0c6e891748e46fb1ba7b21aaab99762131d35be9ecc6cfe4ea2567de01b5059ebef108980afcfa6b9886a9c0cd5f58c35c76aa15a84d2d1ee7e3cb89640784e221c718e7727a8ddfd7634495705eb2163483866c835f1da7dbb26be43c78dac81b522a33dae274182363beb1ddf8f34254723e38d5c2714dc03baf7dc39ea6d5e712d72b2ccd688f0c17340a2d884acd0eec85995074923a94269424c21ac960de7209ba92e66d22e7a1903906753fcc72a5651b7f305e8251ff1cc5d26cf28c7904600e19db9a0770fa1847bc8b46c284778f92876572414e3bf4b8238f26d4a3989613a37324746eaef9490ac5a79acd3b07bfe1b54e595905e8e8d6210794301fd1029c1a2117521cf24960d06004033123f8340e379f1efd1a6128ca7490718794c8b843bd0ad2493769d61eaf500b4e4595f3399f175bfcd4074370ba1b66aa7ae06a01f5a93929024e5013b13c51be99bc0dfcae6905ce5d26e4693182bb919913c708db2b50c78922b357e79d17e8f511995661d3944f860040e839d79947b01cfb02fb61685c96bfc98e1c1179155a460279963bd80797c0b3020fc2c8949218a9f84cc5b6745e177efaa3b0dd7373d81d6e86c93501a24a9381b7a7af122303a633284635f9004fb6966857d197473561541d52df0e11440b1b2c98d36fb3ea57cbb60223c5ec994e96fc1e1f62bdab25ab18506e8051494e8690cc55212b453869806eb49fdbd3c10f53676bf19789a7d16176dad5064be4998ace2bcf4086beea38e37acc4103eeb5dfdc977d93020c8635f8b2ecc2f5331e328d925b424e3aa7c421263ebda75ee65d68133c8ecad4b4ca7c8bd679404e676b43054020bbe98c897d040816d8640c235e1bd0a850615318a01ec1010f60e7a5f4916c0c68aed2eb622b1d3e80e97599e334cac4fb4c60f68fb30e7e8234e33cf9bdc3e3d8a0adef30821eacbe9b6585bb8e6da085aa2439a3ce9c23a048d9d2040bf0d56c7ac01461fd7decd5d122b9ffdc247d8c895c1dd3aee9b1d7f9dc2457688e816806a732a9a89835027fb85c2a439888d357f500fc900ae4b025636fccdb164b775ad9df4634b1d80ccc98e8467334c7b6437c8031ccc81afefb5e4c72acd4970b72a7493e65cc482fb10cb646ec33fcfbeb3c3154a4dff79b8ee65163f10a0a49cbf963389986cf2a20e9c88ac35bd1c561dc606b60941560045b9b636075622e3e62d716ad39249c46bba14553ba2cd3636668fd7ff6023dbcf92555b90c35dac7eea35b798513aab8a545cdd11211d0c963e724c9a215108504d95d74a5da1ece3201250b67c97d8451cb6c16856fe33cd7e169635dab03c8aaa4c38cd25d4a32efdd480ce27a3c23e3d301f848735f802ae1d1bcb961ba29cf318853e9bd988f85ddeadddb8404a8b4eb62c22cb0cafd38a394abef4e935098acd1f7d7ad5ab60c3ae3c334346bf4b1f42e4b2c5e6d7dab0f04640f988411f22e40815f6d9c779f0a6fee2fb20f4ddf5fd04bbfb104ca249c2c0da58f8f72aaff7c10ffcf0ec567e8a183db584c15599ee02d520b38a91faa48378f78ff94752b9da107b2036b9455416a21420ad2dc79fb86a1d8af8aa4580d05954f28782c1244fbae0eca6f51fcab4bd3a4737e00492fbc7a97548372bb4ceca2d973419dda5b19456dd2ee0e43d58e464f7cf4f07aafdc752fc63d434bb3f84d6821d73b2328c3864e9a709445698d6adb7afe4b5f9de1ebc8cbe39ae1090488ee1c1ff49de8587f495fbe8af114e8288aece73c691f730846a3beb68b8c120113e741bb09f3f01f29e9cf43b11cf43592e8d250075eb65bce3b3d8ea7b0f259deb52a352bef86a6bc2cc842ec58b5b6a24c7247d868e1de48d3270054f7b219537f901ba354f63f99d7fe0a8f0a7e004233f56369b253744db0e7f578e1469f01ffbc06789e987c20a9fd27f2d162e007d8b75debe8a462248e454f4e30d2bf01ee1d45cd02975a278d283aa64c3a22024ca63493f9925105aafcaf58a024dac84d2ddc8d03635a81f9e408ea3c44a1a07645a5f4d434e3714aec05395aec3504b8a6ba9a700ce749b11034058cf6105348605906c64c05b92d5244447616f8f92e634699954b5ff89a7f3db25f8748bfc0080581302a5bfedff3cf94bd3f8fedfa9c0693606e7b3cf054057059292fccd343c6a1e1fd3dbfd44976700249408f77486d53408a3d94400815ece9dd28da9f5761b90365a885f23425111688a34e86694c5450e85cf164f873a0cc8f0e6a477a1fd4f1c3a6c163e894aa33666aab85e4ae228b38a6bcd8d246e3b5373b21f49a0785d988e4f88f19a57b03b6ba491aaf0c2a9a82ba2dd781187ae2a5423443387c74afdf8430b993216b350f2daf9fbc02b75b0f4abc5b75f20a4e1c70b9218be10795ca591ab6c555c48e4db23fd72d042de8b8d7391a1dbd799dbffde01ee482db89f3b21104a181224a2183fe6bba824d19e5e56d71ed4146994123056522ba43fa06603053130d54c510f693dfc9df3bde8582eb624becefa9f90671d734193841195fc6763ba4531541ba670b2d7fb8b377049dd63b8a2f4da6a4df825c4ee395f567b305289e766e061e1592254f5b8e64a32e5fa74d1e587494735ea38e4b50d3ead22fb9141b6de6278e7edbd2c983d394954a15b7e7513b96ae4d8c654803c9ec509f21948a2699726366fb38ccfd5aec05c8908079800cfeddacd2f16a21d4debda295e9e7310c53e85463fa21f947612e2973de14301dda8a156611fe45a45bdea8146771dc81268104476e4e26386b02bdbcb5cbffe94a198780e342c9adec3a888de0c2d95e68864b270f60347113b45a08e5bd64869cd7fce95206b6c24d20517543805364a1b3ace9ccf0e134605b784e567320831266c2b154983954dfff7c5aed42a2da1e440619fd043ccbef5078e56e2560e0b08bbf0618f455d72a3661738f09cdba8968b7254ef9825749b54cbc6c577e951f651ea00867fd5b51f0b1b7c2c5c1298497b5cfe1bece5a25e061eafc3c21fb44ac486e51f2a07c28c86cb19310d69fb203beba7b5d67369281745a0ce63d3a1e69aa0e19aae30c3431e52753980f932585670eb196a1dd32a2ec1357cda02d15e0bc4ee9046b8ef64f8e4fa9ae933e99a504d62f87cb1f064869f8454d409908910be18c92467b0b85bc4cb88ce171b648b60e920265ea0d9503cf28a21643fe7f01b466607200c41b20b78208fd872725b2f290a4394365201ffce04ce51457d8f597fc65b32afa0f863cdcfcc44bab796a2f8a50cd99c78d0bd6e56685179aef2d08adaf9a0f14f37bc4978e5d5eae0c716429878998d1a0f4d05eb77365756362062e1c3b399e2c8f70a1fc15b7149684be39a7f91ac25e10d02cdc7d9ca62f4dfd27bd1a4a08d1c7140d14501e48bce06573186008a73549b70d6d76a4b97845c322065c4471cd397ca8599665824c1aee1fa4c6a991a0ea8bf10927e87b06c58488df63b5c908fcfb548c9fab41cbe2ca8d8dff7dd774f701e43ea235ccee5c3b46594dcbfab627c1d1a4f3794e5dc0fea3fa015e56828d4620c55eb7008e441318aaa7b08369f38a729627caa3b29e127c3fe60ff42b25a255236ada0ee793da3f85b373deed7a40849294991383a71d6ea7ad4bbc60dd4b3a2550a076b6574ed5c92463cde6603a24fa0ef801b96de591d67f17c64810bdde867d568624cc8996cfb0f789d9cfa00e36361782fb03f41f8655c52f27c250371c6296cf253fc38aa6482a5421302e1a61d33c274e75fa3465b3d2c82fc73d5e13a29d0bf41e81113307f0805a2114500cd3a1322cbabdf926ff782f3c1f3fdc8776e554a67688ea266f875ca5cd67117a6925b87a42ca268820f17b723806e39d84a7d3bee1039fc7411a4d68fa12c28504627bcbd972face4321a0d0a16a10c8f106611b0bcabc8b827f87d4cdb1c5186cdcca5eaa9e4d88992806d183c83a609ca0cb808dc97c045b9e3650eb3f97db11d765870f2f7aafd8965120be1a527c412759873cbcc18e422832dcd251df298aeb532cdd6937b9df7015f59ede79b4c6be721014bce3907cb8a213ed6c11f5a20d6d1c9c01e70c1250b637a43e316861e89e784519816bbb591409604b4f2a6c05c41b8c7d00ba9e7498aa2e81a97e1a3e61fd0e664a8a9882b1393d52f2d5cd74c6ebc536f3cc88ff6c9c26ca1cdf373b26e1ba808d55fb9032d9bcfc3265590b76450975a7545a4f6a9910880f489f19c5e22d514c6309daf4fd5bb99fc1ee435e6d4825227938d0d44c8ed84181cdcd64fc619b8078e7efa1b41f867565243e7a4df6b848c45c7463bc9005a555e36b6758b989b0de2215ddd522711feff2ecd1b9d1976b1136beff1acc972832bb32fd3a251a7c664c53bea97977c603702f52a97e76e709ef7a907cb6c3df473871c5f2198816b8c49113d8625dd2b29d2aeddd3098feae5e123b6b0427d5182061f20a419408c5a270d9c20f348fcbfe757fd7e1cd649f00b952ddb1b689b02ca0529d9201b45595bd5bc74096642b98511887dbdea458fbcb4f17a0331d05210fb0db8fda2665cf8fe9d3b025c9ef5cba92c1ee1c2f1bbf658466ff92fb94a10e573606dca60fb9a9d48e19079bc4973af61bcefad26e1f0af450b5a20f1bbef02dcc2243a384f9c2b9a0b8a16d8eee11d16a75d90b071a1ef36e87b24ce2b176f039f141fd5fc6ec3bd8c5d5fc58080b7ce5f4abeff2e64302f533589998b70318d1a1757c81a96cc4f15001a9db05c7969847173e41528652ec5bdb8aa53f846c1eb1a12620b7919f531dec9d6ade6827cb385ed50dd0f4b86b5441a835d1baed5c427991a17e4d55c74b35a8e1a33e804ef1c244f9d3cf3f7bf8800d792c7a01c53c0717ab76fac58de4d88ae027d222e6ed9896c36b3813dc52f78f011ece44f2eda347cfe021d5f81da141073e21910383f1dda86ef1f670a6d6fb9ba22ac7cc2c3c574356e28ca030e3b47771f8278cf6839420bbe1b36563313d400a68ee2b1977ad9ff6a3c462a53bc5c23372b9faa7fa51778dc557b6fd3b5f7a9f3fbefe91bdd8142edbbbda36c3904a44a71ffb9c85e06f99fd5847411e02a0875b4ca20cc73b0c402b7466e99aa05cb36f42102753802ed41fee5d20850bcbcdd2a1e38b1b179c06ff560bf6a04286c1cb1fb9e659abe27de4d7b2d77df57f6941601647e61a9cb45623dc81ae6d8acf1b32c331074acf43d4f00f8d557c32348e5af99d12aaf931411fc674dfceeb72e9ef41bfa14f0986b436a3ae3c9b7a76988f8106e19d545e1c14c9f3e210b8c2a6609f4fb1edc7dedd0884e39d6f7c695cc1a1b297c5525243515ef9827503a7a964f859561613d85d1d671598aa8ec0d0b52bc2619cba984cb29b04a6769e9384d6544f6c0e2344e9394e95640727ac12634bb397b5b0be4d7984d44adcad1930ec2376718a47f4aaa3e50c2e80a17eefe557a9a488ffbfae4a5db4c0e20b3f95744f306e27c030bf1937eeb88e31e4005100a672e98e10be4ec695b8922f6a1b9ca45fe265e7481694a0473bedec1446d227f12e45a2c1c6202113c0c2d6d020d55b0b00bfd042c30d085acf9d97a72211fa086896c4749c628658f6227e77ee2883af0d9998b7f74b8d28afb20f40ff81ec88cc0a836086301aa723fed2b6b12191e234002953e1f29077c7d5c4133a1a714408fe0b3208b9b4acb131c23e169eae43e26c4abf84b6bb5ec2697728ff7342df37d16b7db7f71254855da5cece9b7ee7e664580289d114bdf51dcb35a4f2321bd6096c4b6ead2e8489022cdebf1569caa87ccfb678f2e51e6f361896527aa72c7aad9725f0186e27bc6698de3a10c2bd2794cdd799395aeffb155e7ee1e24af8959f49f0998d19ccfa929036273a9b99567b60b74f63159a531b180818461d12d83749e4bab2bc2ccd70892885700c96561897aa157c6e3a8cb287cfe3185c8ca58c7b26b56f65fc649c46577d0ec613c1536504a2c8ee98d96ce1d14a2f74040faa88f4d927b9290ecbe5fa3865f07a516c60d1adccc83ea904f84436950a62252ffaf025eab46defeeab7e1c69dff6829226e19ba21f2c22abd6a1e4990767a3141a09c457bca2f01adbea4d369a69a90e14731a5eec4c0c8c202b115ea2d42a18284805971bcbd0d68ed64a677d1755f57219e8e05237416655304278c8bb62e355bd786185320e810eb1c9da9d77ce0e6a0c0c346201fdb0b4cec9755c30889ea586f9bd2955d12bb6947936711f0359820338e159de5a5b456c07f9760ce551cb69dee023b203360ba4ba046fe533552d751aff4262dcd52dbc1144596339b0084ac985e9ac74ee13711cd442a5288665a0336fe00779b5f672b0363625d03457639ddd1d5f99c008e97549d534a00b062c967bc903a2afdb693eac0971aa2b9d4c6b137122bc1bd204cb94f2eb8c83241cbc9b01cb2c11ad90a859a2501e7109fe3898f25e1dc8785b568aa6b52530b60fb65fa5e2bf26288bf04226407784c3e423a2829a8dd42753592092a412274e55ea3fc61aae98280711a98c5b44630f9904430cdfba850710825fe633d976c52ed68e30c4cad09ce9c6f86609dcc42a0600d1ed32e1019f4968f0c9e9037d4cb14a026c43ebdcc01418955244a3acfc884bf57f9bc9775efd042d2fb2a9f2140e04dfd6f49aaf8fe12a66617cb83b5ecd5ad43975fed3ebb98c05213e38814ae175af441348a6a5d2fa1c2d267ae585a5cbe405c1fca11ba836d2c7b859607628d4bb36bd35f9d099d42bf1d7a0576d4ce5249bb3307f6e02fb083cafc45f2948ead1f3ba62c855599e67cad77b2003952f3c4faf664230f3035ec4e17de64486ecf538f07f874bc5e4c8e6ef750c9da8825a72aad5576dd696b18ea3043bdabdf3f075165e47c74966756958002c9da691d4c8ce7a4e0031ae9409203fec682910ba309ff3454c8ee516de77c402364e825a0866d698f86f3f997d1256e6ec315282209448f03faadfa6259a5dda06e51fa5d2edb752aa5c15d279262737a855a4125752557be32ea86598f57d30227f9e03f0e088fb3be07aac2e631b6c7b0eea3102601120cafd58a4e7c6c35c955e3dc84e0dc802e0a14fc9158c2e302446251b0f565db17508935ae8c99dd6e9176f28e67fb2e70b992b6cddd6472e93a36ac61a1c8f836ae78ac52344c9a1402cfd5e75077dcafd9432c5a45706ef69dbfa6a1805c5c93dbb205f20741ed11ce9f9598eb5fdaa627c55f6a71b54ba6acd65b7efc2b195b4639e757cf718b2c56a47469dcc2c316f5efbca744f3fb6318213eeb04dec87927a4e478923c8bf8edec87ccab90b8ec0af9f4d8c9db6bd7b48f09892a4d93538b3e6fc24e69e1baa3564848536f7bfebf6a5e6f02b481c77421a39ba0df2bab9c236aaf792455d93c8da050459e5cdc826cf50429267905579a3e54da2404c1f3564f3357c6772e89c22cd3ef6eb3b58b00fef7f644102e41afabff41a4f6cec36945dab512cbb8867c1e4f8bb4f65bb91ddc04e68432dff344d15a05596271a8c8a7893db67cf62fc31f69b27bf7b5fa7ee7261f6a2fcaf8d4c254b20171244aca7129f4b1cc25586454e3dfa250ec58c5e24b0e96eb730864533884b9f22704cba2d35634e5477c28567ae4544ed797756557f6909eef10eeeffd3c2c01fd860142d20b0944565b9e5281cad5e461adad9db7569b10cbe56ffbcb39081132caba43b6202a856373e15660240aad20ae16477561cb074e2ccf2840e643c20efeb64df6a48be26d331a64aa93af117746d96a6c204a9803bb1308e49a6a71e8feead80263fb92a51796c4bf1c95db0db79c0e31d31a03ee4e3f7b7c23f7902d1a7b2c5910b0f790ae23a0b6e64544bcbd5dacc89d8def95b63437651fed0e53e34b8f724be8ec92f8873273eaaa41c15a8c7129bbf800ab16934bd164febf2274d1216e23e9ee58ff1872e72b702a0cd50ee76cc7ff583d2f762c6643cf6c4a6d1f256952c403da0f31df38dc699ff1a7a538425b6f55b1a5b565fdecad9fc7f9f0acc4ee989cfe3a03ff71e72e9850453f462aba8abf7e042139792674d8f2d8fd1c7f49dabae1e0240c3b8ec4c8cab9961b00f96f1ec0de1413db21fcebb4834d8fae40943115c03126b55cea2d4831b5055aef2461f4f1450e211c7e6578861eedcd61b7bc14002663f4ee31d00460fd25d0df1dcd1ca15e2c817b87ec610326014aff7c176b54e7f9e2796281c3b6a97bdbde420e370a89e6c67cb406d6b93fa3213d5b6a2d66847fee34f78557d255265de528bfa143a543cee7fb541121a9733d8ee455b60e8598a84524d4cebdf44528c1fa37515ff6eac32649abe3f814b350f33aab19686647545bb5bc8056e2994a5537511e64c04c506b68d53fabdc46df0cfd2ce039ca4f1b5fad658f0c831df79cdf4d62b4dbddb4ee24b716de65d881d9a71cd4b2e0c19ee5edfeb1ec85c9dafd338de32a0a9d069c36479902494fbca4ec551eb2a1b172ac3c061eda39c1d0d81956b6c5c2baeae7acc63597114fa88c2d1f71f0cf9a2d9ef8f231298854520eb69e3276045965086a78b6e2e78826b1bbded04704710bf3e812ba0a383885e2d52bd078d40b7946d06d4feee5e61074f42fe800ff6441d7455b8c5224430a3246a941b0125537c667bf952e2a7ba975036f5e37f24397255320fb5200286f5c5751393d3129d55163a721d0a2ff9c2583614f122bd79308355b2fedd7994ff376a1e35a4b6196fea86435c0e808e0878373ffa16d62f5c3304403a14ff1f5c7c953f32ab8f9360c92d7feda643309aac9a9d3a0fb3878c52de13181e5be039f243293311c2011abd18214b420df4d02d921ad153280df2e2923d1ce51895d175aeb3bea47d5f4733c73c1577fc83b8eeea9513ae1b7384537cd83dc65345799b5b0f81bef4fc8a55b5a7774d7e8201c6f9614082305fb21af58f61f303d784e76f2f985d6e145de576d3bf1366f41bc25b3c57a8fe1a9345bcbae979d8057ad0bf2344fc91df2aeebd21b9ccfb3c7ba0e54a7c24162e1667b6f16d9343a288708c14462609569902bc68a1a8114b1d423b549f2e90a191f5cfd0de749d0f4788a33201a10c8ed47d4960f09febd2e2eca5697ab4a87f156787d8147d73a43a22360b63b257aaef351a95af635fdb33a56ec1c50878174336d0e2c22d8f5989b7cc100fd23a33e65dccc79070cdf3d37a77ed028dfc55e2f2835668ea5a3fc647b4d981d887ae1d47c94ef48c7afd21eacfb403ce1270dedccb4c899052a571d97d8753e4bcc9622b191303da79c19d5cc3b40ff1189718460d9f45f31d8087c08eb7918c1ea9c1d0ae799af4c833c3dc8d8c95318d59644d63b6cc2c00729969a4b3a447cb0e67d94f2570f98df8e2f74f80ba070766b94dec5613ecde1d8a2d085119c24e4cb23be948add915613f86266710186e93c6977f35662628dc7537a327f0a780b48fc830940bef44c1fafe8988e24724880cc393128d59f8940d716390fd5e1f162db2fac22941b0b4e686e85071dc7af9b991df95a237f3bd4d7eaa0cd01f82214e92d8c60d4f6895e733f709a16e2a535498bf2ca495ddf5aabf9be8824be4c34a2a20e5e4068374c5b8ee27106758d171729ae8aecca105de4601c5a00450d9b37f0041f38c15d5de20a41f31d1843a744455c57e58b4f0da1244cea84853e2e99e89e1a3bfb6290844925c2e485dbde80f6f5566bddaeaf5d39f5c52ee19dae828e58296703981648d13659b06d359fc254ac3d8d8387f036155f3104bdbac55625f3bd84edae025f2b30979388694881c91b335f182eafd16c2f5b506ba111d36f90c918bd674e13680d957ae235d2ed5819655c2339e6c81a729515022f187841baadd1d46164e9770787bce33ac60f62cafb0a5a39760bb0ac527408fb48fb9be63bd357223afea70e6ae343f5afccd9198282e2785df59534443d7ec147fa8c375b02488fb8216d40c48000a10d018114a160820e53db3cdb2c11cc052746bae89167eb5c333ad8f53edde1d7330cd4e445c93b5ad1396c2e494666642bd0d7921f3187d96551609dcac4aa44a7d54de1b21b324e3505fa46f681b37b30de4ac066972854380ecc7dc8e4da539a0844e8d91c5a46a51fe597888c2a5a980a790bd1845fa25df22e9df003dd6eaa7fb2669ebeec0b78ac79bb92b0e31eb8b93a72267a0dc4c68d58447d89459dcee2f5c044ea15a7d91a66a4fd10b034f61992704f53a89479e8def3a7e44058e45ce5c96be1d0ee90bb2bd12b86ef8201e25b986d4264ebe463879d44268dece70b1eb08ab96fdcfb85ceeda653495c06ab1eabe220309cebd167196b8578ac95861803fc3be626b5356e54810ba0469e8616bcd6e696e806d03ef4ecb003fb567f38ad7e4cef29b22654699a6105befd79c5e9c0bb9b7b8f1e1b206a150b54b73684b5298e6da8fdde8c787d2bae4a2cf4b0dabd33f376d12a94d965674d938bc470915f2848d36eb7d4f48ff6dff225ede0d5ca970bed7a600f98d3fc71f18638f0e21f6043e4ff06c991a1c1ee08908998ec237e5c8b5edecbdeb186530ef9d0c20e89cbc0b75dcbce02f085d042b4f536b74ee6c9e55a769ff588f7f2b354df17bfaae565648243a51809cd6c141cabc5f201fa805ff28d1be413505eaef82060b026db8344713a18f3ba26fb226d76d97a09e222aff7bdeeab49e2a429272ed9816bdea1f715fe2113a089b5776e808d2eeefd3027c494e2fe4baf528e3294d750f6ef8cf121970c788e6738d6504bc377e96f7f3b654947962bd2947ff15d576a7b945a4f00c7ca277c65bbf8ecb74cca9f9e3c1e42bda94e935c36eda84f478be212a8444d47ee2c6d700267e0991e103b6735cb8d93236631977b3b77dcfc74aed31cd949f775287e7a0027cf56994662dd2674229d8a302013b500a9359b6264047e1108503298d2ce38c2c37fdd0644c00750705998d416ee389179666ddfca1048cac544fcf93d205ac48e0812855344cbbf0d375633b1885b308493de10b8b6018e0ced0c03d9f668ec7f77748619bd959cf5ad73a97e7c3e332ecd73569d8cb5d1c7955c586c4e7256edd418c9697947e3f0b6dcba23fa5fee5076271d8b73e2812bb0574ee998060a5c44e46779058208084618a8e094d077ee6fff5109fe85857c01273499a356b27653a15cc7c275da85bd62238c680a76c373b1573be54ba2d8af93583658c7219fce13967a5171748d2626de39ad0e8f38873028403a3c4798985c8d865f39a588ca070a1da26ebf6044af350203c2d87866f16defad122f99e76dcef049ea28079c5c121957985db521036c94deec026cfa8bf857f32074ef03f51bd47127504f607d09747aaca14d81ebee868ab0a658ae961d8d581157f7051f4ef321c0beb0d01ea5f940f0b3326effe9adf62ea0bb08135c0224c55ab32963203d0998f3c22e92d087c257ae303a59aab2f5f9c1317446d5b9f9a26597eb11790579ddffe2ce8d7a7f8facc1ac25195ffda0237e1e908c281ffe03452e87f91cd5b53abf052d8437a2f0e17815daf6480dfc857024ed6c392554b4358b9481486a7a1c4e4517a3a38473af8a9b3ce52ecf7b6e286931b3e00b8c352ebde538fec08e5319d0b9318cdaab837901ad46d777ad0ab926e7cbd383b03a6813c67c0fdc29cea41a7dac2b972f84a84411ac01f9abcee2a518d4c0425d93bbf6000c1849343863921e1828dc7f7ebd69922e31b972eced19926d620292d08402fc82b540c82ebaf3287978a3077b982502abd65bfa88933565f02d93fa7eaa03707d04948a692dd794f9d2c8503a1f28b113e1568572706b0ea3a2eb40697ad1a8d3fd82ffa03307a4185cdad85b4bf3a496e59ff81894613c79d171ef918e30c440513f334ced64592621994071a71c06b0672d665cba03458aed4b4156631702898c56be328177c24d25f689cc3d450f0d56ad43a31aa276e1bab529bba0738622e0c493d5259909af05d289f2053b59a3d21ae15436a4e972ce88e2cf2997f0fe1531cfb68672ca6cd2c3e5b7b2568d59326fcdf7aa7f54a76b125a02295262cf3ae153d2758b834d6b3999ed1909a684b7134971034365b2e3ab6e2b9d72061d75bcb5b1af78dc6ce30a7ef8da53c24a0d3626f90c62712b3fb44c9d19562a8fed0cd2708d68433740fb26ad2418b9a18cec35b76e459ee3fed30799121a1bbd4d4e9802e6508c6efd4ec43cf874c3757acc7859ab1f38ec79f3eeba20f738c76230ebf7812916e5edc7befdb282323c6066232c79959bc0cdd7174e0aee21b7e034e722665e05843924b856a53daf7019a1a2aafa318637d08a97f1eb26a4cc80ea1aacb29f31138e5be01ce76cb01a5460daea212dfa217c2f2730806843667629142f7f66ba626ea270ff4a5209210ca8f27736567986f4d1e82d5e8420ecfa8441f3d23ab6c3973f842093dabbb8a2c9ac0c3b52f2ef88123d6f4f587cae92c4d66526bc10e92cc7e452adb5e784b058ed1ec501a27ef60cc31fe671d3516ddef7bcd7fe476e4a7670f6004b56b2cbb21240ea4fbbc4caddf3428aadbb7b44bea7ecf733ec10ea04a099f302e91909962bbb6c13dfb3811eebaf675c8caffd7175144d68ad3465d0fabc3ddefd431e019bd333fc3732caf28cc3faae51101165d2acb12017237db95610cb06ea2d5d06219f776d19282bd8b26e31c1b942d8a322bbb59e7d42c01e15ebaa5e30e5950066fa692783ece7a5c68ab0ab31f3d024d2f1e0bfc37aa18dbaa3632da5e6314ebfac376b0e4bb5930b3dc9695a128b38af303e230cdafc7ecbedfc86d64be0426feb74e143b8de0a2e84d0bb7e4db6af63b31c6395cbc7e5273c0936e349b8e0f55465b42b93c3da41f4dd7003c2394d1008fd8f1c0d8ed8efe8165099baa46214b314ac4b9421fc517045ac4f1a057fa64f89931c38887974c80c9cc4c67548a081712bb628c130fc1a1533517b757deb5c1b6e014bdf14f52f1cdc2625c2569ce6c4b2d8d6464d87c836f28effa16a744620eba48accf06c13e8cca16560a0896293be99db391d694a45ccc61596b4a546a9b80905d9f576f904488bfb55fdcc1d798421b5588b13ca566b380bf08d69505f0d9e10873d61abe16d035ce85c16c632422e1b820e7ba4b735507238158cea84535d020806c85e3b523c7744dac9eaf818d25eaf8154df08cb9c47e5c98f65edd51cec9c1339b8224925b45a43bd14e49b2c400f00b0d855a2edbe758eaeca385ea7cc30269f49668907338056babdacca8fd6cd7d7b292d62c2a626c5cf72dd11c521fdfa06961dbffca2c460be040cf343e8a1804750c5fe884a09a86c32548e2a0473b5b66ef03e85aacd27e153e845a5554d5d00dddbeef24e1b0060069946321ccecb0f53e9fc5af69283ee4661e8034653118c321694aec67e9b083d1eaabae22c610417e8eb8a1b683cf593e20da3d47bbb18c787c0ce11b63861ee7ee7bc2621f2f48016c09f23e82dee32baf53ea2c7ac4b534a0874fb51233abb3199095e6224c9d9d17c1354c11b55486834a9db4ccc62b81dc8a2ce3a1ca7c6aa0936c0a8a52569ab3a51710d0eb5fa7b8a2a5c91b8dee255f049369e38b2689b24e015f9d5fe88a646341651de3964040f430fe4a1c684d6787cb7256521a225f10fed61680b34f668e1e01e32cb06d5c982893c74ceb7448200157bdb826d1ea4233230b9ed330d4ea8e9787d079e41394e945a29383a0e731c1f2b99cb8ac6ef24113583105e0daf0866c7322e79932803968757dcbe0498ea986ae83591e554b4e66cb7ef98256f379077df0eaed91b17b80fad47629f916fee40c769190396f93822a7c422820255eb0bb71bbfcc0a4ccb03db10e858c5486e85719a713987d35e646415248b3b246ca984b387fc6237fc6dc79d8f90356fc435bf4ba1644b4b2f5ab370c1971b496927d10b9fefd89f6858675c20b9670e918ed64a3612ee914e9a072a6b00f2be0f8b69937d80549710c72921068b8f33ce620d46bdc3edf28978fed4b4099a2d26178cb83ad4c6db955a419f610835b0388c87d9c30823d6b8dc61d0b32fc679d1aba0d9512f351e556d55c6f8185079728d5fbeea1eada78aec31873562c99ecf9354f1b072f6ea6855e91b2c501bbfaae52f2d36ce61b5c82e1ee1591ea83b497995e6b42a04f3f648a1f3ed19d1c9e51c1d63ac1b74990891c83eb3410c69a99c2c9fb136004d54c435ae879ed2f515762cc255dfe3d3a84eca9f47bd412fb13e053e3b9a1f3eae7a3ad7896565f54d1fa3e82d42fe092824668eed594a8b880094c4ffcb0553b0f865149cc9fdc8e85bf532756b76131a302986c666d0699974c7182ee7d3910533df6f73bf861c4b501317eb0a3dfbc38d51486962962d4a343b444b0b232c3c7df60e3427c5766de53f0aab2116e301ca09e7ace0236132a5332bd4ea140082ed8df515a98d14f454eb74d12826525ddacc2537f24d065261aa850fc3690f090a5fef620485d3ce5061a321d6999fd5b1330ff306cc680b816565baaf97c3e4cc0f526d1beeac7bca1c8fec2881aa5b6f95f9443411d38aa8c4ccc20b8e1cdebe9948b6b84093de0c575e66aef08ae7f9cbade94446df7420e7056d7aa74096590d305250a39d778bbc3f067034021a0927fd1d6bc793c63c75e03c34e3db359a698702505bba4ea7c61b073b0375a45c53a7a159ffc0ce859a7b8b3e4bd35caba645847495b9d6aabad1031e014b0b3932f90bf347893697687eef526a5feab7359ac53d886bc924625efeaff82dc19cebc6888f50799606328a241a952ec5dc7e7373e43004fad8a7d200a652348d9c71a820538cafb2c05c9cbcc73612946c6743f6a58ee154a3d59d63a863b4820ff12f6cb5cb26a36c5bea453272ac46ad15c83d256b5c88eb8e285c123ed2d5ef39c551b378e2ae53bea59aa51469129589067629afa8b005fa28bfc7abe831572804d158ec5094d081c37c8e3e918447986d649d3a0ed3c42ba996abd80fe3d0353dbd26f1ddc47e8a28ead2be17c3cafe04f274209aa6c4926f6c8535002c55f017c1c4d35fc1dc5431b50801e4e5a377de84829a97dd0e36fd23dbcc8f8e88de1aea9b6b2e9c319408e332922a2a0114ded2d6218e2a0390f9603d1e93a26ed98061a2c84095b381759e4a2dcba26ea05f62f12a52586610837ec29b8d3af9ce3eeebae6608fe7c7392d7d9a94126bafb3a07d1daa2250f26bff8a2ed203d9216a9c1ce1580f431644ac429c7f6d7c93664b8283c222be6fd1dc58ff9ecff431d0b3fac4a0d109856636635b19e5149474efb7f8f8d6693752c8ec11955f8ea5d45b548e33ef767ca573dce91c1082630089feb35dbc9108ebc288492fbbe4668fc31d83e63845ce7dd50724c97de13887b57c8a56b1d5621a064c7713f9374fbca9d66c97b3e81a3758a7164e6025df2a560c2e341631a0a99bb3e9d83bae2e401ba0d0537cea699629c8791ceaa532dc93c726587c4edc3db1e083b9e67716d6a5322120632952a9b4201e4fdf174f97d69acce4c553a1172b178b86d274c412035999ca6c252725ca0bd3dfbc9eae0b831de808bff70c40975b9e9c32967236f371512e4079747cc161a44a150297d8e46be23e19d8f937f6608bced0e5ebe6a8205e808a501051eb348abf6946fefb62ab64946a16f22856b1fcbe2b6231407057c485bb89f3d065084ea92a8f1cd1a690699270664bcd1622b42341875feec4ae7d9d917b8f5eaf5e12d4803eca3d1dce85e371081039021d0c3fd6fbc0102215964c52f316af1ed6fef709c6ad665dd5c906a9da19a4d3a53157885066f7dfc3e9b46137ab65244ad8d3e2a4ab17038197fe5d3db4fa3fc75422351ea9c5cac1a954708a8924a6c239bfb75f7e7d05956a7efe26643793a917cd907c6fc835e2427223f41a3011be31a22eda1c890649e6308c3016c26505eaf966ce548027d996adc5be228d66d38d64d759f49f585b36b8737581f494b35ba20fb1b6631d7900bff4777ad3f17eea805cf7c4d349647eff60487f8851aaf6fa809051339f68a01ee6fcd600e465e2ab9725eb3b9668ae2338d686c7e9e3c05c62fd183fe75c9410ec4afb7c2913adc108b75bd25008a55c83b2a66fde79ff53681be41a94854af253b906950783188012e22eb2068d7d2bd83068d5a06170bf869de97d8841ec6518890974a5c8611b0442a71494f1c3919d72e90e631664c486d1a155c6c5f50f63c313a2c26c14aed24825ec841295c912b7f2b14a77aabda00a40df57f2a120d5986056b3786c08235193b9ebbf062871796652c7284c95146726fcf5e7d04c618ca4b45732c828524aabae9432c15f12a1b4cc85c00fc17a1f03a6a78a98e87d70571e4bf328e6e20be393e8cd14ee656378a414a16c05080624868706cb61f68811b29a7d1505f15c1f745abf114037fc7b41254697f524259b2616209a1da24290b21393b2f28eb44c7517718baeb633c3c32f7055d484b36534756c3d795b18126d4d1e335b817ecc850a35cf182d349b596006f2ccaa11a4d07356f6cfffc83854d4c7106b41380aa26e86744b6425dc3ffd9f92294f5e24338046f51bfc884a237730d64f5aa6fa840524d1542540b4f929807e8591ddb3785617c38ff21abd62fac2136b3146b2bac4c81cadbf1d1f5f6f2bc4c4bfb3dbb050181bab86a14b9a0d0c10181955094370b28248f7fc75080cf1fca46c3b9144d904d0e7fbcc816aad27d37858ec77039fb0679b61ecdec13b6abb204220526a1cd3feb2071023b21afd561b0cca3f30f434b8ec4ef7fa7378324f805def5562470c86b68ff299d90ecc846698141a3a61822fa23e938623e8e786785d365109ba0da30db7c5e3aaac20816e4a543f7d116f45810dea0bda822f349a68ae4b50a41bfb0bf23dafa8e697be1c400937276ce5fe794ba27ff10d70fa62d2da03666ebf0df815d3992e1c8ae75190ee1e24e37a542ef970ce8d229d81803a985e59e234ea1404fa4a9df4d1c1f998c5d46861487a53ed7d0b978f232e3b1491d851831f719368781099b2534f2c6d02761c127ae2d71f09b625fb6a073e3d4f0ebb6694502042636f82b56b45b5eba0d74449febe80b57b70176816e9232b331cb47f783f86fad13ea9af4f7fda6f94e632d8d32ed06f85b7a55bc8f222cfc053a54f95ec500c32501553c5604702e79eea31d96ab44a72e04815dba9b8a8c2163332f73a02e11ffbcd9b9a3ec0497d905c333bd52b48dcae68e52bb2c900aa2d7dd9161fbb63f46d80d9d807abf6f7372a2e071c2712ddc20f8cb3af6cf5e6cb84b17b6e006fb4ed663d88f3050b9a1ce756e0481b3a85174b0495835da89075e912f0488dbc9dc62cef24399227549402fda27c44e1284d0f93ce3b82e60eece08caa6da0b90d2fde829a2777e6a630a20a713e4bbf4a1264967cfba90dfeb55098fae5842fda2f540754208c543f7258f7be54ff435da1bd851f8afb2e8602d176e36116488fc4607beaf3d16011c21f13d3d1bf1c37a8f5d972437791bd463b2ad71b7310ac20bf906fd3741b0d191fb4bddd82bb79f32b634028b79b7b57a6948ab2a30e0a6c7fd32db279749b605b1790a21cee93cd6589526af0705f6caeacb352d35b092695623084d48e75bda2904cb4fd037a904a7d57409a55a5034d78dc1830bae02df36e6a4b6ddc09616e769cb45a286c02a99899b46615f8eedd4c9016b03d2eb003f4bbc1a1054c78921800b44c7ae7d670ff553357112d95d0966d042a1b229290bdf7de524a29534a32b107e307ea07cfb2cee39d1b07543242c9a15b5642a7c6caf3cedbd181ce09baf5111fb18ff41176255889f6d131d29b5f8896d11c1584cdb934adc1fed1fe6e5c3ec0e51867ee76e3f2cf5cb63635def3c59e45db469ca01bf33037ca6a9d05668358473aa69d70b9e803cfbad1b833fbed857ec32e46a2bfad8c7e3b95fcd67231988bcd60769b2fffaa8d1a55ba32ffd57034aecdb348449cab1b073d77de4be44e40397e888ec16cc6cf3faa354712285fec6d4ce0691ad6acd2cad2b15ef4d9b77d1ae26f8da79a4ce3580d9ace310128de6542b607145b90d03380c5a00324e4f88001246c4ee8431609fa18a0f9cca62e1a2f107df69beba1160a7e910dc251fd3456409d0f3e641ffa9e5a8b7019d78da467c3ef8cedbb8ccc3fd3033db753d7e6dd4b5db8526facd4779995879b7661efd023ae07a7047a27cb57ef7cfeb55299a502195d6f3a49319dd81a7d4faef7e1f1e13c30b8fd2dc590354c71fd3a24ac2104270e096a48d2464e52ae27dc1120b79ba7e826dcfea9aeeb7a1b7e2fd3f5a5773ef4b710176f7a06a87cc962a0f42aa557313d0654de8575f1ce0794eaa2feeca0d86d9ed814bb95aa52d54259a02cbff29b3b724dd12345759794f257dd52f7fa94ba5d7ff29dd5b81ee569fcba495d77a436507feba0d04037e69961055af21bf3d430f992a7f1edfa12eb5abb56ae95ef91dd26b51b5561b12bf51d75fd0d9731857cfc2a59cca6ee65a35569e95accce67473d289dd1e082fd94e4df55071235fa421b76fc8439891099c27a7eb62a49644edbef8ec304844dcf95bffd757fb2e2cec2b5dde04c71e1bf261289401a4bd07fcfb7f0749e05ff7d94527e89acaa29124d2c8a4aecd5d0faebfad931956d215550eb2badb3ec43a1d068347a1bee8e1ee76275fb9be35ed8953dad38f7aa3ce015d9e602bf644fe399880a6afdd6424aa516f243fef8212b6d8d0dd4b2ef67159f8e958259cdbf1cc2abbed6e4b4d5bc1c167aecaf7a41ec3d6797c886f5a2c7b959dddecd36bea11755d75ae63837ba8d4086556fd56767b42ec4cf09f753032ff7734527b9f1b37f2a52a0f15dce9831fcd7315efbb1ff843610b3301bd55b76836f045febe5d7f8f04168317e1bb8663b23db0d8e90cbafba8d7fbfc3d69ec4f1e19d85ab6d1ef6734e4aafd6a968557d0f6a278e0fd9875c4045a5aa9a9565b1f59515aaaa29aa3efce35ac3fa3e9bf7fe9c6f3735e27a308086649a3ce985307992ed0105fc1e26760a61d243c3f1e17dc80594b5b81dda026d1e2580fcd63a188002be7ce7321828f9d1cbe7cca400d29bbc1015b442945846e2a8fed1fc1b100ab481b44e6b57eb581b6bf19ea8ad11ccc9cf61f533c4c8dfe63b08655f081db3a1b51ed09efefd37cee73efb9ed3e66ff2a38cf259be7c185acb20e63d2f590380d63d3fe73ef28b7fa93f109d9bce49e9fe61bc0f19e23a67b7f8b2fa4de9ba960eba977656ce39ca6860e7ef3233334366e68706f79c73e7cee173f0c57f2e9dbbc1ae9300aa1091f15d97c8dddff3e7cfdf797b68b3b9fdd31a3bd7eddab56be7a063e7de3976ceb1732557a2a48c445b7be7d8d1119774b77b7b7bbb63373ac3d5a4ea5c6bde7b10c628e713359610ebe29a65d399abe85d9dd235c268e6ce5ea2aea4a4bbbb9949272728288cd2dfb7492829272929292493929494114d494911a584b2949414cb0ad72572fcda55a640b7f9446b8ee709d4bd9506ee6ef7f6f64dbb63b8d8eab1c25401a2ac83cbb977238d851680be9b9082a77758a7bb7374b7b7773be79ce36ee79c73dcddcdddce39e7b8bb9bbb9d73ce717737773be79ce3eee6ee6eee76ce39c7487a58a7bb73743737777737777777777777777777777777b773ceb1e3ee6e6fdf2a7059072f5c1792eb3a0c7d248bd61c10fbc3e87b8fdf0505ea3f5d0254a0e048fe589266655d5816ca6cc4db261c4884618ab0e76fd29393939393d1a8bba973aff243a9406916b4bf4d534668c261eeb6da4badbc03a5609e8e953e515227b9fd2a52a0cd14873b4f4e506a806668c038233559382c48254ab211575656e0caf3e6158785b61e13a56ed36aa99a73715fca6a68a90954a559a5a7a3ae92ab2e4899a94d2676d045087355057375a32c2ccec2d22c3c554ed7b995172f64c2f1ee868469a5a5868554f382c280f13f9322c9085f8c18de5c85369c95151616be308ec280f1436bee3faba9955a52776fda65d13333333390cb3a4892dcfcdc6839f116ade1682ddbc9f116ad89dc9f64c870193264c86876a1e9a82860513079418271f2f504a5aa18f95aab0bab12c37492d13258063465355664b83761c078f1a41b64dc4051ddddddddef2070fb9d10b0e53a843e63c68c9e318367b85168bb000e386828d48c1939e40053d392d948b9b8e8a0c3cb8bd36460a86b06cf782a46280a95d5d0e28caca65fc582b6b0eb59e35c5a3cd529862e598d0efebabdfbbd5f2aa776e021ab0100856119da70ae0e3abc8a11f9b2c2f2f2f202c6cbcbcb9b5e5e545e5cbcbcd028e1226194f41ebfd9da162ed7974b02bbd9ddb9f7daf61597c85961e522b53f182513b0372713de13359510e7e2deba30fab2e976ae734a5cf72120ef421d111f29b99149a6d1745712d6b5a6eb9029fae4844f60c884235efebe6cb7065cd133219d6435282728282824931214941145414111a184321414141c42ce981397ff45999fd790d7bd6738e275763a97f74e6e4e0ac2f8e27cd8ce7d2f31e9022ffa0a9b1866b92babeacbf5605da18c5a34945d96e8cabebf723d5cdfaf32b31a4c947175cec5aa9739ab015f3a3ff8203bf487570b7af9f5006b8555e7a89e952fe75c2abbf1c3fbeac6efb19f79bce7cff5308f29824ad4fd73be957bbaaf545d0fe16ff2ebca75555895d53cf84aadc57e79f3fcc1d7ed8db563713dab4b2b0768ada624ac9cab68fc70a3350f62450b5a1750eb75c1fd4071b999aa63faba8751cf20fcf83e5af15fc7c468a99bff62155f86233e7c181f6607c0112d8fbe2f28089bfe3344098a266e4a34ae070b1e710b220505919b548fe7a373bc0d9480148022c95340026238031bc470063660745412123d5734b3b4a4a3f354c46c71c5d38a6c8f0d7850900f506cfc407050d0b5b9b1443cdb1bfe2034147a9128c4cc6cf9056b99862492783d3795d2518244ca588848a5baa82ea5a2d493d010528af45beac23c4d33901f8e729906d2991532e44d0ba47fb8ccdcf9f1e3c78fcb6236645b8b9292d6c2c47ddcc77ddc6762d37dda8811233ba4ca7787145af016cc82d646dfa314ccf3eac262884614844c1b69203d3333f107c88f05f2e3a595f91df39ee8db88d69acfcca4529ab601f17154bb0f76b9ff70ffe148ece371826efec3914cccabcf665841fd43224fe2e268011bc0c23a921d3a3f64fd87cb381247e23f5270a3ce2c3dcac907c15a6b5220d9f1ae0d72f28f75fc9568400486c6c51ae5885cdd45b82505f3ecd3691cf5be049db3801c90fbcb7507d7a021447319a74927654e0e20dbe3ee103e6889384d8c3f872701d4ce0509dd38a72bb67ab43f9d34abf90c2d3c96d5a3040b092522f28c816ed68ff5e37a58893c3b5842883ca9cbd6d4e35c4e6aa0db0da69e7b43cecab57e228ff563ea69e1e9ee8ee95311a6ebee8e699df744414133e0b98f7f32dbcc24c776c36c8420110ea808e59b3df6332e63a1a465219735c256c8133c3c847d86740f11f9d6b788f925ff11a16a5a1acfb0d3c524d632a5d732a2c7301e07386a4ecbb516446b6c27110ee8013da08cad035ce6089fa2b59e12d06de6468c75c3dfa2fad50147544e8be9b4c83490d25a5a525677126c492449361ef166ff5ccf1585b22c140a5923e215d98de6da5c99ff765dffbeaaa1d414e8e67282a091f98ffff88f8bacddae7fd65f30777317f3f08b2346b1841539173e76c52896a822e8c2f9a24e0c26ed18cb2a312131b3cce50f801b0001dc4b3fb34563524bea95d5c9d7b25c66baa8c7da0cc01373cc2fb79c67d9c3b81ee667ef3214a8e428ada61cc559c52346165c1f5f0e4db93f918a02dd1c360a7e3cb261a79621358affda5a5c468f37c4800c40373edff81340dc10033258e1c61af0dfbb77a3b579395b6839695acb4be2c76da054aafa99399a0f9f545d8ed06736d7db54cf422e4c59b1a56cc0543587057401952e88f1757df5a23a31cca298257a6b4e3729666dd7a4f1506537672405c39cfdf300aecfdeb10c4d96fdb8343d443823d7665699868fd5eb5f5633ab8a71725c8677723c6756be2ed3e14141f7b24146ef320f0af26164df51a51950be253f7ad29bbcf3c1838248cee13360f42696c651f05d16848f123bbaaa07055dded981bcc33bbc0383680d028582cca4606066acee16a8e1e73a24a4018bbb4d24a4618a6b44c768445f9bea6555d569659126b9d557564e8b452a83cb9d36cdb9e8b9f37e0f3791a7a282f2b5ac66e9f856958b9ebb8d3260c74089b07a2e9ca924748b3db7ad9e1ed743eaf65b3d36582efffc6d9401583777d9a6f0c86002dd588058805237f63817abe7c2df62cf0ebe5b8ab6b306d6b92f9bd7b4666aceeab1bf30be552a4b5d29cbf23f0cc3300cd3b9ec56b26c65dfbb50da0c480705feb9f2a4744ea081139eb8f0f926e59c400327d46056d7a84e7d2014c2aa29afd0d3bf68285465979ca2ca12655996615675c9c937ab1b27a1d4a2938a68886614a392524ae9b46829f4598aca129d724eabb2aa29fbc1c0a9d3dda4dd4e1de332ea5ccce8b7232c60f950bef3fb5b08703984c3e5afe2b6cf7673baf1e1cf1867f68ca344668938dd0d67c89dd646be0dfcf8ae46a3d5396d6bb92c5f51ddb2bb519a439699dcac6ff69773275d0c7ff6ce0da0855f1a6b71c17259d8776b6166f89299194266d872e5e96e10384959bdc56e4e2bab0dca151bbae7ceee71763b4149d15cfc96e21f5a2687e9b799a95417b554532a4a3da9ce5124d13381f215fd131541b21b3529c9e99cd44c0ecaf4e7facc3292d2cf97d696ac436176e3241fca421ed26e28347343ff43cb509a8ee10f857aaecb64f6e96b77fbf9a2cfa1180be8cc9536e759a7736ece8f496351c889a556e21fe88681aefca71304ca5776abc4df7aa775d805a1ef0744225b89e7a327ec2773f8fde899bd1b82d0ad777ac7d57088646ac0f5b07bbeb5dc6ddaad4534b749a9e8b3bfb2ea86e0220ca3486c71b1a769a9832fb54460d666be6735a291cb6ff42596867e9b7714fa913551fab2742293f2925d8ff3f999a42beb8e1defbd96c99eca878eef840fa56c54b74ca051fdd39ad8e6d85a6e978450ebaf9749f9f926bf9d563612ca6fd8dd1c9214fb50acf5818da2a4526b13ee8cfa12c2ce7d0fc3405262a03fa4ec6ff37b40f9be3fb5d6377c640df6ee2dd7eb0fadf5cf79c22e842bdbe9b6cc8d2268e9b1a035ee61202931d0159fe666c906d72383fdf557ea5e33f7a241e35ed64c4afb129dae514644896375e700a6259582e916c0874fa0f0ab9793875f1cbe44155ce8b9f06fdccff525aae082102eacf3f2cb4a8d80b35d705d3977ca941cd5dd01137308693be7029ff51450eb652d15c1e3881e6e3749afa024ac06c9feaaf6d4287ed1ddddbed3b5aa8dffa3d957d5bfd905c8beaaa94e8dea974dfb851b8a4829a5c45a4bc0eb819c2111654e79125297b552a21aaaf2bdb44eb2bb67ef9e61b4e44e530ed25882421cf4dc763110c94681d03fbb15717988cbd62d819af871ae0907bf105c53261e96a0d6bb9731100740241dd35803a1d66f970e0ced8d52f67bd98d2e017dae552b28a5897431f2a7cb885eca189df3c7b9ce9de2855a50203b3f7858823adda954c8ed7780cb50ba843361c1edef1f72fb2f1d77e4590a0408f59f27e6a109ca365e5c51cf0b28ff76e9d09e2a0d5356816eff0e5dca1939be03e27dfc686da0bd6d82cb9dfcaad2f0c416fd252d88dcfe95130eb92171e386811b7fc36086c36f15eb8673e36f3afccef82f4380fce8291e507e77e1cdb8ea8636f22d0efbde752e59e4dcf757fcaef1bd7e9344403b1ca62bf503ca73dff7ce7d0fdba0731fe6598d1d3c74c83b9d8b113ae4c59c0b23a01100ed86639246bceb9c8ba9dfdbf673024ccec5aee1c186b2995f669609c75736884908515090b4f8b3aa83b9a594ddb7bbfb0885a21ffe7b4dff95f837dae306603be2c109e1738c2e282828685e77f2ab6391cb0980f2f37bf7e6353feb88c5f9361e8009c77b50d0b5ac8b35fba9430605056d71e3c7c7ac0e8f7c713cc957f69dd579ed02d5e14141f07de3d1b77a1e7c2beb1bdb261237f24b845ff5b8cf4d18617cfd821e8d5d78efdd83ef1551821a8a28e1e3f37395c1bdc8f021c1a670a1210a181409824f1545b2e073832247f00152e40a4ef80c2943912740c1070945b011f85c498cb0831214c9715812ce8786591ec0a33f67c3c5eddedfcf5844416dd4d0ee732e1e057d990debf604b943fe351c07eabe1ed73997b6a61af33ae7f29ecec5cc1b70ce75bb76bd02cdf57673baefdb39e79e73ae85812ee79cdbb4fb80cb0d71b9ead773efb5753eea0b981c4826cfcfffc38d03740ccb8a49c5452905e5a4644445a10cbbde6a71d1e125b5030f0098f1ac5d6de61ae16187d48b0e2e2d3918b941460e311efe15a3c2538cfaee0da7cfe114a3facde114a3f695718a51f9ca38c5a8eeca389d6c8caf30feaf7f87f1f6fd83f12f03c200301ec60a162e4a29282724939211dd62064ac3c148158e3861a40a476410c291d1d513203f330d64c0a7222690c1caa988099a7044441e28c21578c4529c584bc7d02ffd7672f1db8aca6f2419d3978c7ef46cb7f7a68a354af42a2e445faaa28f750b9d3cd758b7bfd9f66e9651e1f25f6cc5e5b7a8b85c05e0f24f97cb32321c8a1ffef529299ff229768b4f42b1cfb9c42ff993bae1dc1393a7f1933afab8c1cb25262526551882e4e08e9edf8d3b7247ff3220aa3004c1c12d19d9dad29a4864238d67fe57b5eaa6dda4a07fc71590da9b75b4a43091c40634463f883011031695fafcacf2fc33e7e41fca4a87a1635ebce7cc8e29fdbb48f9ffefff93d6728af1f1fbc873dd3ac9090cbbe15c18cfd5543737846b92f19b0070f86d07ed3714eab73ae3371739fc366af9ad9271f98df957dea5b243f1b7d41cea8c8aaa1a3f0e955f466596a77118f5c5b5c2b2c282430424040ddc17cf59dcf816cb8bb75ebccb6a5ed89d534db5163fc6db4a8309aaf2f0996d5ca97c612ddd507dc8f80ebc0f322c14b53273cbc954377a4df3c56f98cac3b0b06ef442154c056b0d3e347dfd37d5a4fc9552532affc36ad57febedcaca43080306cbbf781876835f7a93e92f99d5bcc05e9858aa6bcdf42a4fe32bd5c56735a5bf4a2a2515176faa4ec5c55b2ede65352eec2cd57753eabbf1679c71c619bf8a55ac228c6e26be49c93b8f3ffa29452b5af1adf85744893f3762f1319e92109ae9947ed0f821cb7f42aafe2675833fd0676b24333d9b03e29c1da0ceb9dc404f7460664034d0131c9839317a44050f789a2d656b3ea5ff649e743a79ab5775e8e077ce25fe73acaa1b3b974ac7e9692d25f6f7dadd3b0cf1b93b9f19a8c10d83d0d23aaad4a138757bd3be5459d30e68ecc8cc4fd4cf053031150047600050ab6bedc81129d650c51129d69004ca176b343fdc207dc98f8b139494928b520aca09a97e6ac6fd605c884205ee07e342144b881afa607eb9a5b5744c28543d3384909f7e267a1a1fd550f65726ca441c8aff44a17f21be26f51d85d99b999b7f57a4ac1b7c401bfa730f7f01fae52019841e3ea76066628522518a090945f4186ba42afaec45d55d18c242d9bf50f62fa3e9923aaad7aa2af497342727a73b0342141414046130735a1942862c7013ef5abf6135d5abe07bd86d8e02869bf44b2b1d337ac1c2625271514a7994e7e77144c7909ebf470efb60ce742c9919cf393c3f3fd1322dcf2e53b48c0e445ae6a58a9649c9ecf0fc9c056ba16500f038682fe36f78a67199eafd0350a7a3dec30080871db078ff52dfeb50dfbbbc27eefb1caa6bd48cea1a857af3f41b5fb8bd8bfa6dd6877fd50a6dadef9eeca36cad7e51b6d6be316cad7c63d85add8d61edd71a2f7cf173c26079ae79f1d70b182f60b0fcc360f9c7f217632feabc2e63b1aeb595eaaa6bcdc5fbd2fb94f72827ef496f8bfbbea43e2a621ff77d287b8f3dade250dfdf509dcba8f19f855f55f8fc3d264a90c46d4602c463a2043fe6082746576de898d7901fe69a185102162ee33ce618c01ef7afaa87f03d0670c77aa8e332220b8540a873e19018b818f803a7f543211de39c067f70af1752ed063f5ab6aaf1fd9b83300421409e200c2108f3a90063913b3d7728dade6566bb892af655f57bb9181a4c502cd45d5fd59961ef3ec4cfbf80f0dcdd397a7c1e3dbed773af8985e64f97c97eceb9e350d5876af51caa7aac4ed19defaa9b2b2bb88118a50cc95065853ec650cce60c7d66448b4259c55a2bb5f6a7955f798c4481fc9c551155258de04558d7439aa779e43f87f285f0971f44c7bc9736e674135940f7b6cd3eeacf8c3450fe9e2b3e56ff5c0f3c1c0e890f1fff16ae84471e3c72db676baec4ceb4a673f15dca23e6a4b4a4c55f5877bcbae3bdb703e5647a346ac50dc169d1984bb252c29a8348ad81f2f337e6aa4a14a2212c14aa7e34f2a1147bce6f55f91ebf1ad111360a8d46231a9aef755e22cf6a2cfad49a80804fdf067cfa38170bd5adc70d5d91866538375479f4cd2c0ffc723d26dfaad5d3385683b2dc395dcb2bbb5997c708e35695d4b04ace895552ab60dcf97a3cc8838a6e3c7adcf7cf6e8ea91081cabf95f5733ed632d7cf79596c9f0854ce761d87c431416b664cdd8d524a697c1e3dba477ffe1e1394671c8502ca90611234870aaa4120f8a64c30ad9de0967a7465850acabf95da08bf3dbb71d70dbb3dfb954cd05277495ca0d3cd5001dddeb34d3fbf08639b80681bfef621bd8761948280542ac157afaa9678e03e7dce3953ce65fa7476af0f0ab7aa22cce60e7f1d47dcccda58c0afdb0dda678e5f41e7c1100b5dcf14e0f7ae7b43bb41861f24f4d75529a02388c8a61cd532936a69ad35eb37edf98bfc9535c2777005a355c548dd75c53533d331559532d5e81cb74a550fd35abfcbbeffa239c00fad758e6973b4d617fb703db89b894d0e01f3044240a23e1ce6feb07a31bf0bad093ecee5521be8f6eeb4b05ab09a765a8397053983fff1871f9c0b3f4d7f6b5aa0fe57a01102f1fe7a1befafc7b9b36eafc78517c4b9b0f2c02ff167ddde8d8fa6a5352d50b71914918713d04d7bf5a580be9be4e3ac60ad69adb5d657be179f9fe0d114381864b8c7dd7adcf9534a8961d8cbcb8208781f5fd669b7e72ea6e9371fbcf32902a6fcd9f3f5a07a8b67564ea01beffc38808cbc95726e1025202023467e7e7c7c36e7a5d7b43999079b70cc873fad8db4a9d783c4dd47957d26616258a9e41806198887cca082809effd3681cf54cfea3a72faa26b70c18d9ec4d0c087df621cb468ecca44c525a06641f323180fe45ab49fe287bfa42644f5f3ea53fb23de8f7c0f16188eb4fd319fd0b7db4c6c3438264ef32e7209aeda024e946fda3575c41c81eebb7167f0784105a1b7eef6c75c8536b2418798a2fedf6acd65ab33dc1748cf55bcbedffcae5ab60e50e1fef70f7afbc92b22a49c13b2af97e9316e3ef1869846bae14a1cdb305abb47a77ab8f75a3d566c221dfddaa7af995f52c18ab0ba78bdb94261cf1e7c79fefa309877c7f1d740c7d66ca7fe3522cd506ba51fad70dfefe1cce301d2347fec3352d93aa8d185a5262d62cadfc24a105235421043707293bc321af05218edc90b8f07902180bfcf21e63815ff823641a7faff4e0e38e799eeb622d54ac85d65e7c8e2f538d0ba663fa8a3e9335b54267aaa802b318e67fe2efcf31d31a9f3ae6cdebadea996f55696bd2c6bfd2ae7ca935d212ae639add79e854c2990c3469ddeb4f5daff1a0c22aacba647d58f5f362d57b23fc62d608bf568bb49325cbb7a3b23ae4ad5ec711d7b2363cbc5692a9d6f21e4dcbd17bf93c635d7c49c9cf8f7fde07ad8a5d7f71cff84d50f86e7d8680ea8df06bd9e01b77605687bcf2751c712f6bc3c39fb49bc5f3e49aa0d0bebbbd4be2b0f75a544399dda42bc2bbdcade815478f55e5885c4cce39e99cefb4eaabaa92f2b99a391e6b48449b53f4257584e5b82220389f6d563cdf65d887ca6f34f724535a311dfc7873bafcae8729ab4d65537df61cc3f379b80d0fbe44389f8bab27534328bfe3775cf9bb6b445f9bf856e439df65d9ce4d5889385d9bf7300b973bf93f1ff99b7adc7019184108d13fc4cd8bdbcfa3ff088ed9669e0f4eeffd458fc15307a0df743e37dc5aef9ce580f031f66e252002fcc26f59c72ff0bdf27b0de38800bff85b7fb12f754cc7641fb29bd71c7e55586a0835d5e88b3d5f7e5229c8e8b3d15fa32a7aacca873daa34622f4bc0bc96ffc7acc67aafefafcaf70a23f4dc1caebb820a29ee7476ec3c041ffb93d60233d328fe5849ee2faa3030ad71df1b251d50cc03f459538dbe4e545b1ac5836fcd7bbe4840e77396314ee9b4f9b460ccf5b03b8f77042baae8b9ce8a2a78aca8624ac7f021acce7d6ab43721e73ed2a42d7be65f97d56cb429ed08f9cdb46686d48f139079d0492ecc5c6bf2993dbe25bc0bff9d74656ffd1088237272724870fd9b88d2751cc83c85cb27f3e05dcf3aab817df557dddeed31dfaaaccb1a4d40f83b15704e1e8450668e3f74a6b5c6dc7baecb6ab652e9ba8dd1c61853a999c831c2c78310ca5f446bbd430b28c422f505aa95a8bbbb1513b32aa9c31dc06f19fbcb46ece5baa3efc639fe1b0bc999cc33daed53733ae0fdf6ae0146f57fe0b6095470fb213371f9e50b94b6c639110bd331e1fd5c97d56cf4b9abfc0088c70540dd879ae6e3dc19658cf48f54ae2a7155e1f896f1fbdd6887f011b4f4c84da3fcfd8770191a1fd77fdbc17f43ddad5eff52e908a56ee47cf4ac235fe07ee3152308c3755774a0e796aebba203402ef6ae88b17b7c9f0da69da8c4ce3654b9644502000004a3140020180c08864422c1703ca289aa8c7d14000f7e924a7c5a1b0ba42087514a21830c22861010010000919999a601a1d84f23123ae064ae4213926fc04df9ca1a7d842d32fedfbc34464351b9d2a4bf571c3ea4db504781b7b6d1e66b194a14dec4f50ab308a4508a51afe7c564fe2793d46f349001dfcfb7df93806185219d9e5b52f1c5d248003ff86f878d4cc315ea4b2e23e741195f96afa6a249d2d1679180a1c55ed04cc998f87f56e86a38590487854d84d08a9ecb574dd4afaf266712f2f457a6ace1f4b60496946f400bf55da77f8467a50167b56ca1939a10dbc804f1c295bc41b0f08ba725410994b77062b1e05b0494cdcc5491dc65f3c7b7c0e22e3f7133bcc59b10102c6131ef22079104eec18da499a1f9368f060aa6fb584ed6b9e60d52aaeb87f7f478e074fde22dc1b5c60488dcc73df2875be2d859c63e932d676aa0759c8d13088ae5e9c3211712ddc770b234e16d51b259c156ad00d6788fd59396be6b9717fce3eb8b287b84f205a1bfc4764342d0b6e58806a5d5f86275fcade366ead82f10c6af0c235b545427406386b515808a13e253d4d81c72570355ea8988c6236ab47e88a0895dc2250e20e8f418053610b06545c4fe1424103cb17435cf5c22642f6987334c05c815b0440dbd5cf0d091431ffa9625910707318024b4a794169d9db055f536724b591fdd8dfd36c61ee7990a505bbb8d3a06f0f3fbd1293b618442144ced6348f06e8f1dbc98ecd6d8c30a35b6e4588e8d94ddffcb0a75c62665c6f1e906e378e4192b31c64e3ac30db9a58e1477128069c6051d1defd11876c437277bc18238ba1218339b2b0e9cb05c01a4438558d9d3470e4cfaf610e8bf06c17dab914e5598da32ba95826e0dbdf591b560cae7b0ea71981cd010548b7798ea12b0bc788c11e331d6db81a4f0aff5b8c762c3c800363ddc44c294a78a9b56bca67730e3371af65584b3ecb92a766b1625004e5a6d6caa90303b8f3dd59420dedb334c4929d250d202d0e880f10796871a458bde1f967110d5e90971154b9a6c5c1d8d3dad384d54aef3c42b1bb115076f37b37ce20915c68e5dfa8f6626464aaefb524b2aeccf88b6a911850bb0fa52150560a34182be4b665f6af43038dd861b00ce1cfcb6f6c90180e35489772f0bfc7a99252075df86edc4175345bb6aa18fed3e2ede3e175dba48e476cbc49b7243a9022e06074085d6ea333502b9614a310b9589256fc7e534e580d8a5497b781354912af1d92868b2a7d04115b61867663298090d4e5c2088c0f1d01c4cc72136c5f90db8b242f62abaf5ef64e1aeabca144cb98407f031e7ebaf83e159cf7c3729fdc171ba307d583a2a3b0eeb9d04052f12b0cb63fcd77ec2fb17dbff9ef691cde2d901bc0e1fcbbfc7359a737d62be268ee2c99b5ff4b9b22c2f3b7e970f8ab0935dc0354d9b94e48885262057c41e89ffb8494804a9f2c4119273c1579587a57bc903009960b67a53587305743aee3a4898158639ab7b3918441d13669bed41171d11c25cf536a0e5744561e8aa184c2422093efd2210c28cb7e57476d485485f1b921e35b60f35f0e10cc2a4e19815683eb7292f78278c456a45039e2f30f59f0dc013ffaaade8e7abaaa432bd2ac7c162318631b6837129608c82381006a87e16fd72060f2662657250c59fc1068ea74a2b495f51dc58f2209c97ea6ec8ec6ff59c5407e771a33d7afcccbe66aa5e297a7c187ad5ffc949e1171fd3c26a6b0f31a3936b0c035d9d9c0a032647482daa4043d44d55f0529e0f62e668bf5698bcd5e241ca3d4d005e13738764d912309d69e32e1b15c0c72cf3fc8e2ce61c8a4f99f0d63eee14cfa18e99b1f085226d95392f3a53a6ae9916b168721a43575b44b48cd43547150cbe35daaf32bb9fdf66f741395faa2652054c733101b16ad718a1ce2b230117e43a022f5bda1c9e0086d5b4e34987c4c7dde42c4a4ac5738bf14879b9acadcc630132fc9b8658577eecaf804cf028589c65c21615bf951fc9d0003b5acaa4a30d4f88abcbef5513d25c03d59495d8e0ea1611c8345195c54da13c44bfffe7bc57a859632b4480b14e4edd6b9549b7e3b68476d8f26528cbf1489b6fb50e7614c2eec54b5769b8324263ccbaa5c5a63e35f305fa8f4201e7bdf381467adcd46188be6bd8b197a8b6a1c61477bf0064ca20b2eb898e44e805188dd4254958e6de652235577d510acb8f0007a5af69171ad8237d55bbe3d78af815e0aebecdd632687b65e40deeca8485d857633dde5282fae62ed3f79042871b45b3a8dd956b2d278d56b8e0eb3d5ef8b5738d7d0027773397566afa93557c8018df0a18e624b672f80131fd1ac7dc11068c57b49b181832df201816f3ef4c1a225f110b2c46d92c9e9739fff385c847ef7a54463cbdd9a46ed87765a043ce77e1fb2cfef5bf033fe5966106e756ff2e4f68fc348c05fd0caf7a2609bb9681de4c1e8b91fd578eefe77f59bc91ef8d2468412cb3b8399ec5d703dbcf674ac48d95292465ab7a99677b8afea3677581f5fc539fe80cc85e94b828c2af4d274e583955492e645c80c4dd192efd004af710d17d472028b5762eb20c94ab02458d138c0563cdb5ad561a4a154ae78ecd8cb92225d4c0d593de0ad5d7d6a45a37db003f27cc2bc788980532f58e4aa44fdcdf2b308169c9a44cccd791458f6d864855152178295f92e2b6c4bedd2e25e0336a078c84d2149e02e4905f83bcd29f68ed81a731c1d809bfee8855983f985ba5bbc995865398ae9b1b847b178b924501f0079eccbec4e0080531362c3a0f88007bc1110a7f7bdea100ad3374c2f19d0c42b1eda68c84c34ccbfc7794ddcdfd7e5480e3d585dd637e48757fa527abd269c9c27eabb2e67eec3686948731124c69899be399a8d82565a278ba1185650a97a87e277a63dd10874d6b1d8fc967e93c6b36cb7bcd280a963a63228ae5e2230b8fdc3e88ece8fe603b50edb30636331e4b3b8fa20d09d761115400189fc8703ed2c7f38d6b4a16bce9c7b5cc4c2b2f5d39c1fcb16e62ea648f31ea533580bf7be30d4fcfc271096e676e96c5dba75301c1fceab2a8f6417c1b6782f5a021f85bbf5d9d38f40f9b8f0188614af35b057efd6bf9b8cc3ee39d637a7bdba388692da33b3c4da15f36d07a593a86040dc7fe134251c54c9f1d3fe4d721c8f39663d41e8aadc20add2abd7f56fc17270a11ffff034b903aaa183e862f4e101414f8a0b8fa6e7b04c3bbeca1eb18115716c8760a0eb834ba9ae6db0747f8830867606aae5f986ab0a8c64d1cf7cedd162dc6e50d271ed3679f59b5f76dea5b7ac09c593a5b6e9d4b20db1f7ae725fc1f7c2e9cd1d2f2700e667fb951e3efd4364182eb0d697f7d662a1cbfa4516a6695b2f2afee411e9e6e4d70f5b3ab80e63c67304e20e9a2a22710a74bcc5f50c5567f61b9701ba3e84e4e2fa269d86f27774c799f969f28a835eca76169c9e22808f1fe332f7a54bc53501054b07da755a55d2c20e6e50c6cb66546bf206e29cdd7172b50d010b38c755a7c9985241163f4a04a48302ca967dfa705bbc7517aa3811ad429235996078edd7e9cae031ccdf96bbaf16931657430be37a656e41332d7e453f8e8334aa8e4998106b501d9fc6034681d4422c8f2bd1518d72cb62b2e76ec15d7d8502cab0f61592f42a5bc5941e84c11a1f9c813d080b866a1e924d5cc93f4276925b16bd8e579c41000e53267679d53afbd09122b1a3e79deb485fd4632cd2084e50bbe114bc0a45a2087656f82b25d43358688f6ad36c45d0cd14674b8b15155f42ac41c5517aa088a39cfaf8c71e29eac65bb3cd7b19770a8432e4b0bbe4025eb66d8009dda5073204d3bf18c67518810b242fc4c35d53a051ad767182a237c234c476e4015a14877058e3e64d45c228e272dbf6f7e7d984fa3e2daba683bfa802491d79daa7c26333e1284921e02c5ccc577362d46bce8e98f98ac9ffb5422eeeb14be38ef4148ad959161e863ff54a6e730fd901d3529c4af25da7725b065068b090584e22604987ff41577679d61fd463bc0bbaa9e96f986c629970a0b9f7b11aca083fb53f9251762f29804d6da2f4fb66dc814a5b46bcb5b6a785243b38db267946491014b96d23fcd76b9c628e1bbf5f49b896a5a156cffd82281719974d451a1e06e68d300c0198bec5ad8f60a303ee34324c2edd8f0be56fecd5d2b2e7fc62e1a4517b8881d3b1aa01b8da207bcf44df051d9613ca6820f353972680945cd9dfc89a016e30642d25dbcd0a160f8031f9f168a2c3b9d5192731f2657b63243b210b0c5cbf79354ebf3ca9cfccbf52425d3181d77885d6525ff93854833e495d3c8c3e2f525b8100de30dde68b32ecb51af4fc7a02c04144a7c5e6fb7e7140391e9d1c2d9cb01c3d881a4ca586c6a28f734bcbc730bf47804d721295d3d2783dba2c420272a663529324a188f5378f849caff9f3896ac5a810a5ccab951cd9073837f9e9c13ee67aaaaacb26ded3e67e5cc9c190138c97807fd16d1d397991adeed5bb9fd6eea3e96eeb6bf6585ba8696aaf793160d3b924e51c54a9d4f064862471582dcff6bb3b0400fa3004a362926ace06fba176a2e3c0b2cd5519c4ec7adcdddfbe1f8963555f8e897e11c43fd731b33a1ccf7a5b22568e810adcc82334947afa989e596b2fcd3590f63e61b7afa804fba257033851be1119262a06bc183fed6e39e30ebea7ad2e5240493c3eed4038858ee073512614341586933877502d91b147971562be582b59259d8ac2626dbbc3cc1c2170e5eafd851dd61d6cfb02f9c2d69f3c20e46f2ac7016807c742d1a0829ac88386c09b09aa61cb316e3b2a0d3368de9fbc7e729c3627ec769e3cfe020c1827e27c6337cc3f75f8ef8d44be7f341afa47d234a1fba05944660e89634de735603b625404a3331033bb6c57b4975de0ba0812a6d0812bfa69126c96288d7ef39ff8dfbf83b20c13403b8a819bc4c33681c467b115df101dc2f4c60901ccd20dd2989c7ee18dac4184080d93cf778a36d075c626919dc5aac8d57a85cdfcc402ebd648e7a964215c893fbbbc31db832fec8621f38c2c1023f16adcb92cbb2e591eeddca717863abf06f212833def8f4e7ac24252206621fd3fea32b2ce370e43f657e0e76725e9e32a23b379d32f09a966e37e4e426f3451d5af41513934bed5c884bc48d32cdd3f1e7878bea866bdc0d4e4a1f5932da1dd49246e12d435fa80b855ed3c8d2051708c9f7ab5c3bc4df2d7a0335324f98ca22e787ea4e88cda49a449cc2c0d7d01fb50485fdc3d86681f977560d515622dde6f7c376dcc11b47febfc5df1ef29cae1d8cc13528c5f085aeb020dfc85ae013156c9817e650d2a999cfa4dcc6464e55bc513efcadf01aa93e4a5a4aeea9197014c27c873a5c9861e5a15f6d6125f160a87719ac0b1b3f226a51cab66e1744806092532d747990717056999da9f2f03005ad691aaff129e6c308c60d6838fa6854383a6e2e698a0ca81dd1d87549a3396228ab30873883daa2f415ce9cf5afe6ed926044ff3a4f99d9b8cf6aa2071c30276cc59dbbc09c925f48fa1d92e3ad34badd113031685d498dacf23db23ef380ad9a15e1ea6f863310b796ccab09bd36fb322f74010b199911cf1e5364cf7ba077eb3baa33d56b1c994940261b3a1cd9a495fa38231c246e18761a590ccdcdee81a6ae21751e59494662bfee39b3d7d188a78eb3568da15dd37c9815fa57cf41e485938f7a8bbc41b73928036cff66805daa998b730da2014f332592b749e9efeda51c2f8bd1fd8f0beb910ed9e92723f04910cd69d75d119c482200f5e4ea5cae9137de023e7f658fb1134eb42ab74be8b4d35ae2c779e90327bcf5be7536582a65be5d27815d0d3e668b34b4db10d4151a57ebed9924f020eb43cedf761c9f7f6148f315f6970dbfd4f35f5c68e208d3b4572d238d04554e1118a272ccb7d19229838f1de234c0a3a82a99eb7d7053cd802bf73df2213075dd3a193289801d4ca1b57f2e593916677c878ab106f400aae134c349016b9e1e24254c9694f103a76e93561109606c0ba74092cfd5396cd136022dcc38afa11584101a907a884ff8c9beb1b2ae667cb664f3bcda6a83f365892b482901ccec7bf659b8130c153b0d1413d71a90283e31ee105a18780c3eb242e02309242283f16f3d62f23019121e9cdec8be7b3ac20a87e2f5a6498d965a45fb21837edb7bb5dd4ba4f8b3de83097e70da346745a493e64fed831a1967524046e9f2dcb319693526f3f22b58e0f23e9b4aff3dd251aff6dc726525432d2a39ada450566799c6a3209549451abf15037c881e5e551ebc3f01d168b795e73745e0db68bccadf9da57667ea6262136cf48ba81ffe59094dd443e70ba28665440948f0b0e3954ad691102c90c72095df6fc35ac1f510401d4f6e20e3f77e5c77cd2e53980d1edc25cb9e0acd69327b6974bbe2b53653078f1982b8acba6263873d092c897f4e411ef1c79a808b96e6b0872809ffff6d4ded46ef172fe5e840126520e4f5493cf1d9c70bff49313ee5adfc2581415ad3ea1bab66f988949efaa62568e2b3f0ec4074d152b32f10459b2eb3ba75190daefe5f1b1c78bb115ade0c06071e31db1b54ca43a1d17df0dc502499437a474f09f8b23e586e0865ad05c8751c7abb7466db4024972ae3037ba0e303560c05b13b270be48baab0ff79d327398e585b527c6425253090de9042295915fb16b8d29c71fd18b9e0efacb4898b34c4d413c46f81df939ec403751a4e35f7836440f902915986104b22277ade8ec4d5eecc66535d96bb7d5d616c14d94d2e8d3b6fa48370a71b85591b8ec8a980c646d0980f8a6c076ae993ef2cdcf14bda9941e5d9e9c7bbc5d15da1d806ee7e4c6173b3539b69b7e7c60eb3cd74a1ea4a95dab67e8299007aee3a2a671eb43b0fffbeeecf041b606a8814edf57453071a5bce9efe960ec13e21024f6da7e6b61489ca864d77baf04f953612ff4e2eeed55286201c5fedb2bf24a9b3d7ef9e7f7fd5156135be31f8aaa404b6427f1feef41c4c729a03155a14fda6332baf53b4c2664d85887580a1741ff9facceef74650aa3747f5f6cda97b4abc4335c9a11ff36d5f02fb7d4cf0064bc5b0b06013defab01942df33de9e37b5eb35456532e0060855bbce9a4d009200e676d568482a00ae4a8cf70fd3baa31e3fd5616c618e141d100bbb8b2d86d2e34df08ac941becae190a5a472c32a19f70f53eb92e856d3adaa55e12dc309d2a4749bfef06b758548cf9dd57b25b289ccc9e6c01c4a959ebb7ad673f64f388990b53cef297a4cb244d961a1f6ac1d6f28dc091c58bfdbf9523960d8aa3fdb109bc38c18276e36d8c10aac6d38d4c0ed2947e93d90c8f7b35070c9fcb225be59f03b0b21aa5b17ef5b8ba20e5d3a882b845c340b5c3e0f18f35bb7b05b573c2d1417067eabfcb0328854cc4f62ba7ed19e85eae9efe12f6dfebccd5a0cebe799102ca8d142381c5415d1fbb281afcc80e2dff3a3806b1d463c69e1ed4ff5c82e1df859ededfc42649e8e01cf96ea776a3c90c75ce9193a17cf53e400e1471d476756dc5a920ae4bb2944ae16aae34d0d6109ee48ffde18351ee9c0bb438637fdee7a4e4e62f4e059810d31a04596645f2c4e032d0e17fec5c083205578818dedb074114c3dfc69374a5d3281448b9870a33f23e9a78db498423438a96aecf57808b76217d3b59af21e087c7e3f86ac2b9db57e3a285ec62a63774a81a56576f6a1b5f7b2ad85faf5049411977f0e012093cafdcf881013bcb8eb53ecceeb3c05eede49a73fa3f7f7f82e254f92ca3c4187f3da0bba5680e33656d0c1982088dc937f4ba2e9564403383997796779cab2ae66b1d484b3cee61a947be4e25579ca3dd6c0857a8b07871281f120a49a9942f0e45dfd4f2299c9657d06e0d8e0e6e943d5f847468f2411d64221d529e4a5f2a4304c8644f0035146920910e291dbb085fa05f43718ade45809d9af56b2e1a850dc2801461e568c7cd821c0b19574020eebe96fb370a742800bbf364300a0e1a8036d169ce7adf00315eb9112094822b3c6023c8cd3bdb2c783194fde9776828ab227642553e9d2a3ade1593987c6e7d80ad0a2b2a89b754fe97b6e339a1ed5136c805145f25ed79c745a0713162d84ef78f28e29e5dd458992c520721cf0527fe6908cfe1d70376f1e6c41471debd0b6036b0ed89fc2003d7660d67b74ac30e6fa04950b9a76c357692a798192043e4b7d35553f5029cbf239afa8370ebd068a286e613bf46edca57af628756403de15dbee8d5d6d37372aea6b79cb3c2039e0d4f14b813a44530accaaea9dfc6ac82c3b7523c6cf2c0fd347ba97411e15dff2a5cfe33bce8654d0a3db123d89eb97edd46b882444e5671fa4ae7d4d2da27bf167b07286472d80cb63c4c659ca3e6d8772e9e165c5da83b6d324502b6b58e94de6135aa8d4702872d57fa41d2febead44963810760914d0d80138b930b088996001dff0277f4823368d37427e28a737162176e1fb24917854b76fe2033362d185452a3abfeb4cf7849a218b895e41e3e982de36e12992ca1b0e100581c25ceb3aee64c01be8fcdbc435406f082422540e1f5dd0eb0fae31ffb3b0a8911b84ddc8a68926aaed4b990e08aa9c835452961ca9cec4102ada9fb24fc2fd924f17daceeec75901ca18e7bfebf0acc7566d2629fd1a1a25aa93879c4025a47465ea8d2f0741b29243d0ff7456bc1d92a26ea9ae80675e924f71e803d8d06a6891a9c70c8c4c6f0abc1c2957e844a06fb0ca7c28366bcfbd0c1f7dae623b10940a2da82539452a51dc9c5d4e2204539dbe914b300c66757d97909fbe5c632d906df200c0a9004189f2e23e7cc1024439f777cbbbf2342690a9d0cf8bc83dae8fb1fb0dea7e14e60d233ca41616c7105fb6cb794096245b5542560e3c5f9e3995bffdeb490c1be5222360e5ac4c515c54bfd736249aee91d59af0651c04e1c81b2b72276ffe3e26047230db751e6d030205504076156dc4d16caa415f6d881cfc7e53203726e4d03e1e55998ef01e0ec2304f1196ba31365214558ce1db7f5f7f44c376ab8308fa74ee23e0d1a2f3571c773d7524b1f7d2ef01cfe2b8a45e8f2015de0675e83c6bc33ecd9c726637f2f2c05b0bb988397976da7a82a85d0ade16695d410be572e538279e9151f84250172353d2c8ad23f4a63fde5203f7a5c34395c1ea24160680c10710d26b75990656ec70a655eddd377aa667a27fd1313774a66e3bc4f0b42a688ec174b911bcbcc959a01bac57815c5c6a2bb35146f39ccfcf7541cf64bc6fdae21d80e6651666c4676a618a8f007a5981e73490760e8a488fde5a239b3c193c56edc809ce024a9955dab67a6abe2d28ef8c0f468b85bc16565ba3636b82ffc24e3cc6431ca7b4dddd82dcf70d80af4e24757d6ada7eef5321fa08160c3661a0b4a0ddc829d7c386180c134a1a7e0e3ba0df138658291b8792eaaa27b3f33556471aa34ef5baa312e05052e42b3041fda6c2ac5187905d8b8a4c99d98f61a9731cc42c765a6ffe0117505079e7020c2ca125a8c5eea328196f4adda5bbc3a685efad5a6e572303883b3c71177aa512ff1d268db295f4815b9a8ca28d09f5bfde563ccca517d117bb397b7e93d7cf517a6cae43a349917c3fe2c0b56f91d152c0bb650ef57bcd83634a5a570c737d61040e57655de7d7f7830ec032a5171d2856d044226d836ce241999b4c3912025a37aea34fd10d08bf5ee8e23cbca1d95d7f9a1cc79a2bd515210de54e01e0b9e172a22dcf146108e7c1cf9e747e1e98c8052dd0779b7145dedd59036f0f7c80994030ec2170de743826158e0bd53fce29bc0114093ff2105f394d030b44b65d804bdeb332a845b9cba8ba486702b58f1c3f086970847e038351974c194a1da84cf42a9875b2b752001849ca1cbc19d92e0247bb00f9e7f8c7d2b172c70c2cf9dd1350fec7db2c14b6ad057fab93093ddfba1af80c5661c0c5e30bbc6b30ba54d781d5b88720e46c1f068a29508d819d5f7d6c8d419d5de2b4256a99aa0f849ecd9e3263edef36bcfdfd2ba69dc5f33cef9b5728c15c3e2d68b83e9568118e90fb39b78c90d9d8a29235fe7d891af2c5f927d9f5c8ebabbce27e201e001b0624318fb38bbfca87a64512bfedd0d62b17e0fab742cad56416625220b833b9007776f773903a63e8be18c7b1ca4dd5cb30ec664e507e69ddee648c7ae9729341ad30a9d414cc480bc168ee69c3c06ca8831ad83340f966beed55f58cabb79cf26e11b1426def051ceec9e5ff4520816aee40b8a3f39f678435a37ab45db1bb60bd8e6f6cd14c5b0ed17e47d98e879175d3e99d53ab8ccfa2458dccc7833753ee5fd001f1c69145e77f55956325bad01e11abe03502cc00c0edb0effdfe6dcf8e9f9c77988ee8eb94842211ff2382cdf43f1615e7f1e932fea8498dc6f3579f69823f51da2eb320fe5698d0ba5dfb762cd45278577cc926113c404aedb660d2508d4f4cb45cf648c63d7761c7a548c1c6e09ca2a6543dadc4c0315cd5ec8af2be4385219fc8130ad9e29d85cc703624b7edcbaa5c7f8e50ddb9ea567c47167710e2ccf30f2dc0e44738ef7456b1de91bd1337dd4f3c0f47bb99a9232de7406d050055ba0bda93c6db48fa778d7ba20ca0524926fd5bac86ac07118801a15dfd2780703ca043fcbf53f475ddc10f94aa1820a0219746307e894245d3e99339dc1385378f9fdc428e8278e850f09cacd52e3624e5c1d11db5bd0ef0d97dafe851435d47a1d161a06a223f796ee5662c77e2aa3a22a873231b0963655214fbe35415b76094a516e1081961465509e50c775ad634cc747b5931eb191f52abc91a18d6d400dd4bf48280e4bbdd9021d7720f42e63128bbc181a0d6a54b39368399cc0540a8996ddd1a9a4e55f5afac2f13fb3349562c303113c61d67b05aaf30ba67874875be8e08ebecd15aeb471a332d7942abbdb1c8e791c4804cbb858b184e893a24e9bd8e1cb04f656347c1cd05c9452cbc73f29a393644263ff990d6e1d97dddab07267ad3db098dd5a37ed1fe39bc2152fa130d33206388cb5b265b1ea74d8e6f8410639efb02065a8dbc8ac58c60d0e7cdf5e15ff56b1295277fe811b30683d50f962085471a452397826e13b76d8cf60973ceb147df1a09f2099e4a429c606ca063b1fd064cb61a2378bba66c76be306f2c45ad08d1261e0b14621feb5c75102ac9a711311ea63a99502d51a66c3a293dbf6d8a149c913d8502658eedd64f34db8502496a9ffc50003543414088ee85ddd528b201d1f11f52788cb7544e3b0cb8edc83c2dfbc1f442de1ca4da036adb8d494136786eea602771b378b5ae01137fef28d04098aefccb7ee7bd12fdf96d33624ec268d5343ab90e92de29591d88ca98174fb0d04476b4eb0b4a9e65ee2372365f2ae3d4b416efad29517a78d562e142f0bc7ff6d61fe1ce9d392dc8782186a38a03632d0593782345c6e279341067640b63f01460607e6f45deedc8c96d9ca0c94ee8387d77c390e01f3e4be7aa4eaca766a2b279ee3ac62a7bb1c0ac394c122533bfdb9413aa4d595e0cfccec94884b7ee61df60e0c5376ffe45b30b73f403bc2b1ec5775fe00335f7bc877cc1bc3be8a53f9cb2e80292d945a86edf36fac00a9e7e386c58f78b642e52324ee0c88a0ccf8eec24f5928ec437dc684be5745487e3ebbd320bb23058ee499595da0513de4a1c82dffe09811d41409cfa6181b4502846f82525c767d89d40ede04aeb90fd8e5ef5e4410074062b6a9888201d5a31debb518f719e73ee6521393d00005b43f943f85c7b2d29e426c437b2e12becf8bc33ed151f17d4b842c623f833fb2bff51d874c2d4673db42f15a869cde7eb592fadc82b53d8aa683181d6396a787b17b96bd9d665e7db2b86f0541439cc0913fb196adcd063c727255b5c6c1f4be7be0fc21ceb6b63e03854890a4a12d423a31f1585087630e1bdf0a1c5b43bd073bad71352d69ae9875f90bac503078dcc1ac01506b260e21b6e689443ec6ec6b3fa8d536d36b6116731c389c8e7fad79a391c308491164512d05739ca091c413c52c92c2dde48823bce04815787be8c1bef84fa8cadb872a2da41d83192603a4d060d5bd07c5511231ad8c955c736efe4b24f9142deec74fb8dfa8624b540680cb8eabf3e0211b496bd6056940d5b9b4ae69025c5a1fb8d435bfb0c53cc98a8147b853d79640d4c7cf7bb6ebd7f2795e8f58214762ee1056f804c6e4e5ee2a0919822d8dc566698571ea6511afa1cba44a837b363017575a4e8c714a67ea481d117246ea91054c810b7a0e5c4fd006115653feb007bf3d3297c16d1e9e0313556288f03a4008781674cf45e150921e1fb06435a283b732917b984519459f64024081e11559abe3e4edb2c12ce4fc1427248f97075ae11e4c6ba9bcc87c95a2feac5c3a8efa748798317b0b0e599a312ceb732384c48490bc64a1ceda6c0e3d989b74fd0b3cfb6bbe6a75a30556a3fd4a2881809a0dbf09db880838a678d49a2498d0b1bb1a3111276222275474a5e8bae4a0f7c86c51523369cd4b236385f78f065b212847abe10e8d3df1c2b83e762f5cd0c834575313cb1a4b19444380569c8eb2077619c9ca1d5d472f139d501338f12638b12672a24dd0444de0c49ae8891bbd26e2c48e6e263a4145a7893db1892c749a9843cfc44eace8688263a204092f9ac2dc98e64aedc1ec9d0bb287463fe9f7541efec1a2f26da3e4953166a3c50e119301b046edaef3bce20d5d6130e34e9826ec33719425f9e80fc7b745f413aa03194739f022b4f9090b674e4a4ce61f400ebf5cd5d3bb7b0d978ceb316f9a56546c0e51efdfc6466403e7629d18a8e13f25a542824fdc7023d8bb17305d7dc6a65777d40c39755e828c66d022a44a80f379600a377198cd34d2f2aed2dd4cb70ab7970521beba399550970af70c782518c3a320007256268abdebcc8e93cb7f0a4a651ffd73a6bf2b1ac1898055a810a0cb20b8c4c3c4f908315d31d5583e134a8e224d2c11ed484789747e2f40c906d7d73e2b200c86b0111861d7f5d75be5e1eace18bc7a758a4da79d29764245464a08bfeb6261bc0951a389ff71abad9c4e63f7310e031d156b1f793436e545edc3fc5bca60c6726e3d6a14a8671a69c8abe6f2a1990603b7722ab3563af66ff3761654c4c5dc4dc0a9f229d9cd4c15376ee37ba2e6756996314470c0020aa466fe8fdb3a1ff24447e69e40c77ffccef3b5fcc79179f8505e7b17e30c1463579ee4a8274c103ab8c84f4e261540285abb7c9deb94b38d63767443a8d5cef34a09671c0450dbfb3f09cbc75a3fd78ecb7027f0864aaf7f12ac64c7f91144314184ec6773152fa6b3ce20436d5edb3914c78d0ec67b1e0421151ea8dd92e7eaa2ab52bff20a195c39268d6e0b09125a2c3b1fa9ff61c81b1a6651cfdb18c54fa83741dc20d873629dc4f6fd935e09851c0f7ae7125939cfbf75fbc4616e57a1d1dd2e3e9a9c9de21933246f0aaf731c0d7c18e6d4b8508aa9efa89841062079e752a9200c6b944de084d948933ee31a9a18e09664bb51c570c6f65c1e82cea3dfc4fc99b12213adbb9d21fb8637b35accbc1c0dc34204038a41828112ec3f86c075efa03d597a1858e95b730a10b3d9b501abea2009bd922835f38077f510829a2d76a2763571f44561c9fcf54fd4a43570ceb0bf9546e7d819871a9d7cf13430ea3e87a4e25c6caabd7ca2edf086b028de62772e05a0192942f33b2457c3d09511611bfa40bee02502a49d123cb353363e76c61efc939cbee8cf1ba48159431af323a14dca9f3a17d47c56c838c6ff5f84b93b0da6d947adc0b7da5330c2e0328ced3da66487f869c3c8dd483032e1f4412d237bcfd707a745e75887efa9ae98b42382f249fcc42d5c60f5b2715196b6555f6ff318cfadf9c7be1b998032d86e61b779592c52a009f7b3096d8f40e3e6f437f9509b85f722de6b2aa630a02cfb0d1c1a0f520c0e3e10d41dd6020d47f6a0b28a42dc5c3df01ebd65761a1881d2f0fbb08d63974e3d530e3e2143677caa749859a55299bd6d2cdb75f2649386eedbeaad075c55a3b50628ffad84d1bd2bd62374f12f20b28b07c8c50b8092d0810a1b07586f53c373e8b540717b19a59e4e384007a2c7e911e29fc43c00b1da7edcd739337e462ad79befccbeeecbdd8701a7cf28eb221303d02b306054fc416ead9172ace594a3db5139a2de3aa68c3170626bb393aa603b29fd64bf06c2da9a43db5362405f5cffc852eec412b6e52306e9098a67e9728e78334b733bbddbc1625c83753b94d2ae9f2fa3300783f20b81a387d2abe8eb6410c25e0d044120cbb0e198b9b5b9d8071931a07cac4029cfa37190ca3e10935f828a5686d6ff1757de7528a5e977d84d07e38db6a33d4a2bed5f787316ef18379c212fb6b50ba92d0d8ba9fc46cd50a35f9e930315da091fb4ffdae99893d5d917096bc6cafabc31485cc12ffd86b295d25d81f4a2155844866500b82f495d2dd47bed95c7c51ed6ac882c4e7d5bf0f3fca7f7513a95b639252cfd042c70f934559a6c63996096408ffbaac5c71135f1381480e8aed2b6b035d96ed8432efd5cd184333061aece1314adc2eb04638e3aa0f3005b4858e174092b1ed102f712564228b0e1d67e6f8d578203a054956e46a7606873f1265fa5216baefee955f44caa1f155e25768bb13239857dc939ad0829aa4c85a5f11a997a24c7e49b5fd0c51ae9800562a6a07e459a485a5e6ab5aef00129f181df64ad1f2587d49f9baf81851a03c87f9434ce1b2bf84aea84857d80474987af5ca70e4f32b1268b8b5867e14b06a8d5930809ab9b8fdd11cb9e8f250a6759dfdb789bae008d3d17e7b411c53574a7cd51c4681a6944c27fce79a41405c25387b8ae8840d0cf77f83424162babf08dbd662ef7b663b49288e7e35c4969643b0c09107f6c1f3393edd458736e209d222e61150ebf1056b12cc788f25444727de4f7281755746a16f5b8d9d97309187c16885216e9030743acfb180aad849387ca6668d1cae0731d36a114db0f42bde7ac06ba7983ccc984b7375eea518bae2f90a554e9830c97d688893462c59ce3d470a0258297021162986c232caf0cf8e5d635f7516609964a06394d7dfb45dd2b50b48cd29b086e7881ae792c2a5799b1ef41327ccd43fbafffcfa430b40055f4865aa90fc7c4eedae43537034c6bea405a9d24c83eba6a22b65fa435e07940faa5173cccbdf44b747811c46c2de96525f60e86026bf293666f275c98687e210432ad3a420f990f8baf283ad2f1172399870aecd00f0d39b13167a1592bc156ef857ed6f3f196d41bf08c8f58dc400274ec21cf6bac26d4a8b5270b2efbcb11099bb15eb8db54d7cd663adedcde33b29c66d3ce5fada331f6cb682f41b47382253a87aea0dbf402b301d2c941c855036dd11fc1f31bf3e91759111d98145b15e2ee545474e80dbd3962a47f2c23cb6a7d46cc13f810a0503ce3e52c288710d73a923816c525a478aa339de49949326f4dae8b6d7ff0a515a79158f4b2ebbc7b22f245d102da061cfba6e911ef6f1698ddc7bee1dd9fc384aa4aa107c109000637f26ecd00e4953294b1d0e70df2148f6761929dcbd8ad32cffb608caa7102dedb645c9a9102e8065ad59ca4e88299884119aa578e0bc413ed29b3583451a8e1a71958d871867f869c651363fa2a6c0939ef63a5eff070b239a30fe714b981433c347d547ccaac3e35f76809c6c5cb9d70349016f9730b43d50e8f8665403a7192e97996309c26d2bb706e3ab8fffa74ba6552cdd9ae50ba5f40f672991cc86c85e42058483e453a2d002cbd8a2fafc55c8413de380a37c2c31c4c99a81a608e9450f221498907d61e5ca4b2e9accd81a74cb09900fc5045413fe44913b5bbaf13a74080d7bfefa4ad9a200492b6e0b73253021253e4759e04b446f7e009ff84a86ce2831ab3a9aa5e95ac4ae6983cd6e1cdbdc1e4b1fc84b82588600030fce5e2eb72f620aeae2c33211c5f3210c39378060fdab7012ad21c38a066c20ef992b6fd426f62f09069bdc5110b9f450bfe1decd6c732d6d209e778a68c7ff91c444677090f7cb815b5756e856b91e65c3bb7452544fbe8188c38ec8584863e334adf99290975213881a620cbd1db4224b5017d08ef41a2ac279d759f4eee8c30ead128ecf619a0cc664a9ba1f902dbd5a01241994a6693cd316c25bf070eb7beb21e6a9e5ab4426065897480a0528304959266f11ee16302771446fad85f8a0ce129091e54096bf22fe536c1fb7a954d9dd33c3c5dc6c7d29a92748529a52eb1a8d41a2d508fb85faf6610b1c5ad58e0a5a5f16bb7ce263ec703385cf713f7170c94935141f7dececabd027909474348890386792a0c0c8bae3cd630aedce8038a60a54011741e9523b5b46a1e26ca9aab8a1f9e926474d1fc827b4cd3b63b24ec2d14046aff51b73f8f84c551a42693f58c9045c5673ce557ff6a279e83686dd6026b152533e594de03ca3d471da918490b63f3a78346689a4ab62198d0d37dd6512382c662c451b168bc05c446cde246595baee46173953c441255f2680ff1c7ec9014e6e5a148c1456e4f2a2d23864617491343e210f9044737612167b51c70923eaf592b957223f0594a7f315116912363dd9f4731ec83ba02316a170de86333d2801746e5034929e23c83ab70878633d768200cff94a39e2569263ae15e9d44aa237a0cceec1e3fd2d5164db4801442392d7298b03a1ffaf9c7dbe11b18afbe244c8ec490c48850dcf0d178d0037325683bb9beaa40d6a5a0d91fea23355187ff3563a3861f2b23c98f11b53c04791420959250da6a82db31c0fb4ba046f9c7bcfe2d2eb8716c6a65bdf731f5a42745d50e26d275dc41744142f44042ec0db4ce54d9c8c9c942d4e43bd0c1a0a5efd427d28c09f144ec20a80e4829b5b01116df245447e24bd06701bffdf5da60cc553846e86651f879013a94244fafcd966156f01aba33db28630f261261e80c704b1a5b549b86aac4a14944e27102494a3d81dfd6ebdc7bd465df8e59823558d28da55629321a081c0df095ad7d0a24622ad0101e037665ec5b02e4eb49855aa46b8691f9343e98a13f86642334929aa383dc4249fff7fccfa3f3f9785aea2b50926b6c2732f1e127b2a90acce3ab71988d72f0d7b6679430ab0b98c2a8be1a95133abb9531ce3f26bcb5d57a51b1a22751142bfffd1c5da3845f070d203c7f5949e5510ad82148fa9d3f2694e6b2dcd5b47340c0953833857602dc37f1a12594e181cdf665efa1451f9bcec6a2766eecf19ebbd56db992d21488f82f812e4a399661864622b944bbdeb331511c19847dd3486f0f0644cc776e0fe623e2bd547f4522c76c8b0d1df99bda2d0728d7676562986bcd2628317d609ebae444ba4bd934920ed5760272121db4590ace02e51e62d6b7beb9a5211b56905a67915a3f29b6939ee797895a0e4eb33abeb5c2add2d0a327ab2f178772854cff583d8a52d5fa47524e571ab3480b91ddc117b6cc81acf7296a5703994ebdbf7ffd1d8e227773d883df3dbb093f87c9107285a0e0e1ebc72e2e81382008473026661e2996ff221834a2220bb75a4ba9a2336ed4e9a9804c509de574053be22ced90dc1f0131501414a342620eff949c1f5690cb759aa1cc07901b8fb4b6d54470419c5b2d7857980b8216798bcbd4943a30d2157ac15f4603cc2af3891ed24cdefb96d75597bec1d1f531a56d0d81a44a430594aab61cefda852bd6747612a5b7285bff0866cc62efd1c82e6d67e2cfa5630a8bb3d2d0796ffc354a715eade37b5c6a6e9b1381f7fe490395512bcf7660b8cf37ce9aa1631e5d3db7f67da0f35717842320a06e045421de4cece0841ec316f17e627b366814b2fead0eaf0a7156d0b8e3812ece9404d5e02d182b68b9ef0149484587fab80a3bd35424769396659f7d4c81b9cb7df441781510bc0eada802ae0386341d3425e1ec90159a98269542d4231a349df263148f9c30aa0e1105265980e34f8b91fe09658477ac7ebfe8693e4e8e9b4cd6c10226b59b834d988b85820e7dea5da523c98b464fe5d00ca9e6539a9768d5f67f49aa323759d06f48332eae322f3b1003d6f27e329452a82898d95fe225441cec8514a5046827f2526bdc46b416f50a127f9eb421c34e39dd2b887a0c3ca3198ea9f3000be2ee54e87427ca386e3d48f5e681e36ecc3b22cef6982626e428930f82e6a390feabc7a951168079f67b39256bd05cbd6c22a537368721205fb90bcd4beeca12aae60574d779fc7fa74bd075c99355dbead6bba6826eaa3246f3344738435bc45921ea07d8c1cde984dbdc650aae6e7dd8f59166f0517b79b053b90ca0b44a99a4b9cfce15faa3a28247e78e79d28bdca6b9b69ef239aa0cc6fe5957bbc8ad4a3d7c9a452f4bf86ba9f8e65f44d464a18207846a1caa0b2d91b1bba8e0fc3d2c10af62b1a1d7a190499d3039b0e4d09b8851b5c022e712f65e5772b351577c14f08f9dacaa954711b216bab8f05b28220e5a6fbaefefaf7485c188d5cb14547596b82819a4abdb7f6ff49981ca4f6ae06d548f03b14ff88945611951e127fc83e5018205ea3e2c0965342cdc927f51f4fd0c785252a791f80590812c209245d989a983fcdef12f68a8a20b59b6111c4b814035d15749252e607bcf597636b8cbb5e0b35976e7ea567a6b3a4bd80d8a610e78b087064f40cd433d6f0ff4b401c15f54a8c5ba8a859dec5a8cde69e38fd168d14d36907b13f35afca4d1014524318a10bb4b74facbc35fbf0585024599bf47a2f2866740596f5cf5ad7d36ab28dc5599306f0bf2376faf2025783703594d696048775592896c8b87654cf2b17ff6e42023b53cae5b8f540eee857d7ce676b572accaa1ee41207ffd70558baba50e08ca140cc99f686475c472749218bca7df0ac7d2778a5fcf887c13f4f89c16b130e4764b21edd5b3dfbbe6769e6d0dc1dead10387b2d4b4f43c7b9546e29fc0aedcacf6ee6cd5e31a9feb2a336fc717dd02160b65a1343eb39b313ae406e7be3e152fab97a4fa2c660967940be10c2c7c48c349ba0b40f8d9fa0d426f50a03c304061a9009e547580ebe2822e000d8f9f5d7806b48eaaf410da06a40768ff0202d6b4031e1079567c14ee0e5b301b4c09b74a1dd6e588b05ca0102c9e4d6615e3983ca7d938160ac6143b944c532580785b7db12396bf22ec651fd7b30151cf6c2282de14a82d718c81a53becbefc44fa243aebcbb6e8e45e6d98cca3d9b187eeb8c57367dd9d04608e1b7b9541ce762b6c4df7429a317feeb8359aad60ac80d89e3e107b9a09d1536dadc9fb5423c3377e1ade04397aba0d8018ecd2371ae4b06b99245e040fe188a4eaf8f76e89c5b3fd44acf72aa7ab3f0160802602d93c6631b30f30cf05c84ae9a22737fcb583cc6c65af5d2f73315d70cc94a11b1cd1c7d6223589bd90fd2f6ca9cf4d3561b72e7c9bc7befa15ac7b4e10d40408a58fc4d47c9458128ae9924b031543b0306ca5b0af54b091fd0d15c410607da60d85c62ac31c6e31de3b6c95a8fa6aa4bff00e800c8689537c742a53a6ef225005eb08e5cd6e5a2b2d581641524c10b40b73929f758651a54de80db3ae756ce26dd6687c4bb6197a1ec3ff1e29c36edf7033487daf96a8fe97eb39dec1fc674f57d82d96b5d5805e854603eaff8cb94b51a9858e57533a1c36f803e9b8db733d4f76e36460e04c7fbaa786a4f1b6660472130dfd2070c3081cb27629e641165cab5b4cdb2e627ee779200123e4fc39e761d2c556be65514dd447c628ffc357152f6a2b75ce92d92f091cf005883d4b18e5664580274f77637bb78afdb484c1c2b0a365d3082a742c4dc05dc28a64c8d24c50d2e1d0fc280a025f28f2ef5ee5fc1c81fc9206ff76be1b745e495a69162695c5677a917c0c67ea56e088dca3cdf54ca05fee43b5287b9e21e421686b40d798096effc4a0862147089932bfab9438dc3236f617a6aabdc5341e6d2215a38dfaff8d5670312b249c3fe417b2d05f5431699044ff435a7a92b9a04e157da3b3fba5bc78e997edc0a5f4aadfa94fa955a0044905e1c74df96c85e2f243166da577089770b50862dc8fc24262ce9c064bb7971c94158876cafccfe54246eafb7443d2025022a690596c45b55a338d390882e4b1d41df48e829961dab7f9236496fa6b939baefdb652f63b823b07eeaff4cce445a74239fbe53645e2abbeb62ce54cf2eea174f27b914cd4830f88488a64af46879886e68528875086cea00e486cb7268928dc4a883bc6423447b8a4d442857518cd457249e203550c9e6ebd46d270b2b37a6fe27aecd34ddd4059a40e4639b7677ce5d467684b66b7088c5a19642d43a7241ea4ad35b8398cb88a9c844b1d506c307528611270805382b1f0581cd4b22d406ff65a0c0b71cab73a38ce2572a93b41cbd44db6a434998980f5016cb44be8c1659d17bdcded9ecb34b0c6cc2918115df7837d924a262d86f5507001c844b0fc9b95439e00493f8f3e4221e3f513434248f5840e278768a9df7f20af6015236ee093cdc8ca69e2c601965217b59a1caffb15abb76d1d2c2c6bf84cf5b5a9cb1831e939b609d609ea62750ad51f7e7e985a851f6fc7ed1096700ca1bbe385578513e057f87d22dd4e5f0286893553f341176d95995f11a08aff0e97d739d04e99bbee06d634360039b3546a7405dae8c9351c9dfc84a0592eca7f588a375bc9ea197602ee83e19c488453de741c16eb710476310f99169c58bd98d8c0b3ccab42571b1f065d57e34961ea06f03bea2b23298112176ee68c11e461fb9b4a9746e8c90e74d9b2623eab7832ef8c92219f77255e8635df59afbc3cdba83583d62f84c20593bb12eb1ff58e6e7d69a4ae41ae762d3de708cd418714a4949a84d33fe75ffb383d7cfe7fd5730c1fa321f53848f56858a88b69476d0eb64199428e0097148fe1d0913ae1c3e462e2c83583b618c462e7f4ae5baf797e1323aa0c86ccea5284a0efc6db277180242e54a7d1c5fab663c6b597edf1809eef906897a9679f2b73e70233690b9e5162802b90e712320df24f12a57c27f94965058dd7cb2f065bf8e18d1b69a2e67a59a4fe0cac0c8b0887d634704e220cba54ffbf18987ed384b4b290f6f8e4e26bc069bdb45a6c0abbaa934b5d9aafb34eeb63b94d084d3e679080fa80c782fb05f6c255c73130c8d8d42690c72aa91f97d22e8fe389587bf50b64bd059ebb86ee904686bd26829b2f65902ae254b9786a26f9727d8845bcd475a84db20edc9004eb3735eb0c844e4b8580bd37f0e83ea0acb33a833105e70ed72cb019f642dde84a36dc19bdc0468114a52e5749a7d6878ac80c444d949fd506c62d7634068029301ca621b15406332365b045d1b439296d522beb16faf03289aec4f47899650be2419da7820d91fd9db8075ce722f3e40d494df3828202c811ec5727a22a98b35b1e3cebe27a18b8def35c92e7ab6ce0e01922ba9ed8540fd00936b0c20afb371aeb3f676c56bf0a1145175640b61789caa5fe42fe31ff8d3cc6d5260ccd2f869cecd45827252d72a76ace3b9834f169f9607c30cbf70b0b607e7f5d2bd45ddb84e8da57f1338a2aec63691b1b537733432b3022cbfd55888551ee5531f1e3f566096c67ffd5865bf455a87b084eaa305acf291ea2bd008466aae41a6f1654c3ca64a58f6b016d27aa006543eb869985f82d652bf074dfdf2991086e7aad0f65a57f88792b58995db74f7cadd50e632b1266e97a72baa7ddab453f8599504798279025b5c5797d639049ecf5b174a526b91c28d17ff08ae4a5b426e2176027581b3098e8938d911e69f22adfd1a47bc515633232f4c5f8ea8b4c995133d500029a8bafdda62d03ff75937cf856bd60f3bcc35ceba38f34a934d41f28db30b185049a2b8754958d9b360cb8c303c3f461141cc6d27aac58b8f62a42666d071b2acce0024484f21014ebf4ed2f13ed12db5da4f11080fdb217318bd425db4b5baeff8b48a409849e0fa92364e716b706a2a5fd86fcf605602f3808e8eeaa2dd437be3631c2cbc822a4578e8664ff7d74269d14ec682c3bc31ad761aa3483b3a9f1b6b880e8f52cd3835fa6db463925442d46c347c4d94e82b9e620e4f41dfbbea8be19b2570e3a40f66966e42927544f033a51d7ea82845735d3f4b33f5c7954ce6d1272a4d0070117e164119a39feaaf6b4b0563b5101b098298139f5c6f7fd03f9547e320dbf9930d8f3bef469cdf68ff303560a3a8d7beefdbee87ff2152e62807cac116bf22a944b6e05f815c53ad9e43fe695e37a38e1cc83771e53c00b484dc668fb80390f1fa2a30030d521bd8e0850ecfbefeea3baf459bf0d2426ba2e1d1c3ded8a8815346526cb9962b646792a6a629ccf4647e52926d1241184cada5af79bea864068ae5e482719fe6452ea6361385cc926d941e547324731cf27cd605aa7e94cfe6c1a9504a6ca0e7f320bec8d39adb6dbcd294872957840d793f611170e36e095307b51396e514593fb51f54224fafb2d6e0328b271113dddbd01f6d7869edbbbcdd72185fb49d5d65247c9788ea1d474a3a58f8904ee0ab428f9fa07f9d5b9f6d5e989fa6e48b725f3ccc06694e44655b1a8903d1e31ddf25cdab6226d6ef061058180ef3dc8d38e059802170a7c85b813bd646b230e3ef46d725938c11e48d76d33813ce89460414794f7cf7eb991bed7db9decaa87ef370dc6d070fa62a5e82e061719a7d194d8139729ef1a3d661517d8b168fabf9a1d46e57bcf178f6ecf6c01837e9f0d2cf3ec4380cfd756edc480e6e16f71f790211741eae5175afc033718bc8daf01804a1c148f5f893c4f170e715e088bf4050fb91ed6157de90ba720f7b7a3da386eb388b2994117ca4d70e2a7d2154e9ea43abbc9154903d31193f843a98f7f5666df28364540061600fd184a0e5bb74f02febb225c77a9fc2deefb98b64b4fe12e37d737b9bf275ec91217a7ab26cc8210b2cb9c6012425468d286111acd726594a5634bd4d333acd1549b3f0de2bf485923c4db4376ccdda8bae535c32707885f9e0f9b6f25701cfe9e9b0b148b7e34e95a4f845cc7c9b50911db6448347c86aad61aac8d57a776a79eb2a444cd2b42b741677cb14c24cb32da218e6af3979c050ba3d34ac5a72c7e6e4d4ade17751fe089baee70670343e5a3446f5c3ca7419dc3ecc069581208c8bd42bbac6111e173364343c510f42164647b3e300a9c69727388c2adac7339d1308a66df31b233e567c4aea25c308e82fe69b145d6f8015182358fb7b0cdbcf09910ddfc42b273e649f3723a1105fb8e481c2388a400e041e83485bbefdf109c48135e3b84a31c24ebd090ba89a2790d2b9f12870dfac0f59e4e45b95a44d38b5239f8d8185955290d21dfe96d5df36db6179664f87815aac3d668005adb957e7fb66e3aba10552d106f820bd83ce34ac19b34ffda0b19bea54711c0b5f578968f512c317628a10359c3b3fda9616b5421debf951be9c9ff3e28d1a451a21aae1f7c68494df7b4f04fcac9845d6c4b957794cb9b415d076d682ae448511e24827dcd18a9c7a2f48a5494491bb684972dd5d5ec02c2eac11a85e93b9df36f9c5e6c16a66a5625bc937465c8ae93c732c0a5170802377f983c0bdf7beeba636147f1a805467c1f92d7d14eb0cfc4a2b4d2cf893ca6803a6ffbb76dabb82b3b7f646a88cc984393e560afbe6bd65f59bd25ca332268c0022428d3bdb7065c19c149c05294d2e239bd45bba1246e3aec4144d995896c74b43fd4eacec29007d5547427274ecb56b1f186cf9871c8ceac3023036d4d72842fbee243d382b40743ea2cff57832af7ee730b5f63e35cf3cf537e781366e11e0a3baacf3d8b06762b918d5cdb1a48efa5271ebbec8825a39afc7f4a034c1da2208f38fc62696599a9f7bdab06dc531182394e7051ea04d8f77b3c753e3a0fc6165ddc6be685b9264e8e02a1132ce3d976466ca6980074e8cc3c814b9cf1fac00c80e7f71abf06ea7d63dd318d28089112393dbd10a1d7ea64e40050782575fac85a7333549c43a2db9c29c71393ecd662eafa2e47f71c8d7ceb89e7c18714eb816be45935280ae301e543895e19e62fc03994647720e35fa9ecebaeece016642b72b43fbeb38f0a8b2628b71533f5dbc7c70bcb6d8a1c1d806f2dca42a85652c175ff568246fcfaf99dd04ced17ece2158094f055a773e1033a899069c8616fa72fbb8acc9214e97c0cde0b06467f693570c47f84274f0d0be92864ab5d30630be7c43987c62e1a17cbf0f6cec766c25d430e3e016f9c7ec492742c7be79a8ac99ac6eaf189e6e2f9a39b9f62984f5b0a67d104de2dadb643cf4942af37dd2e6f629a5ea09b47f02f8601db9c87b97ed9659bfafa3087f0725ca25decaa94640dbd4c5085741a74508f7f84c6d15e5845724c28541034a4c5f657be16ed87515b132ebe281d9225850e6af947e870dc5e8d4c5e567a4fc9f0d4c4a3ca6f2c96f94d88512bc1628e170df2d19a07c3e30445dc1d284e34e6b53ba5bcf38232d7210270cc8f21aed202ef5d68cfd41806ff1f9b41a131da4ca3570247ae073aef6836e47a00f7ef0c50389a9a9b7042d9397973a739db2fa5359128c7ad24eef1d3fd1fec3dbd9c34ccff287fcc5bae13142c3d0f58372a01a356611480381a11b2710075ad193d20c96edbb17041cb3c196b6efa10565a81bf64a120e3443071c6e6334ec6bc1c84ba6f5c6baa9fe9f4665aa6312d04c4aa6371a90cef87c3561383f87bf434505256ead36d6f972121d97b8c2b27b1d25e457a0bfed709b13d58123944b2db22636d6c8718d1a1bc24be3b5f2eae7797cb11cb1cf2ae591dac1f21f38c3ce0d418dafafb471bc71a15dac231fe782ebc1c09a7efb4032f87066a8810fe619d1756a8c1e97a319db1f6c83353930fcb018c70598d4848d3d427f23927395db000a7c1f3364bf29379939e261b2d414df4b060378bc4a4a34521352b6af6170dd1b2d6dec74015d8e58454ec35f8e915566ed256436fc6c7e9d90479b80498d21e8153b946da59f7bba6061bc9306206690c59ec39460645aa30964eb8aa951910b13d3d892de0063d5d6fb5ce09d9fe5c03df68937983fa79cc631441ffcfb62204c3282eaa1b034bdc56ec06352d073ef984bc2bb5d17e8cd4356a3271b637f3647e5bdcdde0a674e9d15f10c87282ae8148da6206f462a56f6ef27e6dd7051f6240f3c794f63b5408a63559e72f23a4594e917fff3280d419284002da9f9354be3f35dfb16e3b2f9afc5174e415b0ef479ee1dd264d394b88ed9f983c20e72ae94e3ac3bca7c2384e2eb503b66cb0710f58eca44d6d86c1d1505c9a4f3deb94cb1ade582f062ba79708fadef8911ccaabedb8738d51d6cc08f5aff227c58775b498f80f50dbc635e25800245b0c4ca0be47a31cd93e910595a6e5bf46222ad2aa41ff549b3c45b3d3ae9e713c68398505c331aa4ce6fda3f88be4151671b3ebd5937dcf2afa253a24e3840c65f89ab315b4ac66943ac17b86858f2501b649724afd902e68bc5f6395e6ca61d98b303f20bf61995ed61ae339eb3ad8a6dc9f8feddd10a04b7ded1306a7d4152addedf8c561be94203d1e985d908e4331eb6ce94754a25cdc3a9cd606fa82a44e38aa591e7303dbeea20560af051ae222d4318697e31697d56577a680849dcbc16d9948a85359b7c949bb9f6cd9f3045547f0b5651b9a2c79822d131bc99454058da8ed5d1d34c49a88159f273a28529ecc351ca19a82a8ffa3a023426f3b60520db70340191d4261c7af26bbbeea62c29443ac0784749a282eb776a349153c24079326d2dd3aafbeb46c21c35b39a6ae6abe6d7c029f75b4ba641634a02518433747c46a89db4abf46391481769118d9ef50e51a8602b7d17da2b2396cdd6ccd2ece4ebfbc7bd50cd20e6079b9b5749d91d25145aa7f7cf3a456785eecf608b09bf4990355115fe020cd5245934bf2dbeb90a0ff32154c9e0548a4d206097ddbb6137700c15496c23b896c6e0ecde9392e2f4d5bd10ab857a7b98fd15e17d6039c62a160691d3ceb84fdc71f88382502f025cd69dd5ee2b23676af6c86d3691f597bd40f56562502b2477c4d2e07ea332d1a23be58f543a0192f529fb8a1112efc20378023be904aea2447f2c351b14c334a2c8c5601ff392bb0fcfe220412793c3e9cd66a5f714ac32dac4f24a7edfb9100424eda96a9840959baeac8615fded3b92d66d75115b713df83847d4f3c1ec87cd400e3ec6800d62855a5c2e1e630e782ec825b8309168baeae3a6ff1c99e002414c49b2ebde44b0f3c08409636f620736586a59cce22406031f906599d83743a2ac6e16e59ea5ff69d666b93df4be8cc068b7d7456aa6d43c8723a0a6b2180035392cc9b54c3988f060ffd00c2239ee33e84f6d1eb416b14c71600432610928653db01477c04bdee575cc23296aa465ee34bf1d10a300a74b93dbdc18631161e0260746cde0c336a2be4d38426fb9b1e06ac2cb1053bb15f8f3e2b16bc16fc55de0d5afeb8d52bcfbdd96c4ca4908b4bb03054ea4cd032e56be865a07f3e9dbd5caffe44b9d05cebd354bfaa1b3c26d5e8506be10a703d733bc27e0e08f2c1dea97dfda0d52f116757ee255fb51f7dcd3b0e65cf034b809ab4b938716d4db7feba561852bd98504f64e3b4464605e8a2079052692b585db917fc89c16eedc418b5b7897c78aebd0c2b44aed9a339d77697bebae574fa1398a9b94315e844a1d2ea8dc8027a9bd5860386959bb29564472fffa9def661c190c128d08c6fe475d2f4b4eb8a8dfe0cb8146c727a186a21225069d972666363368c5efda675d780fee39dbaddc9a029b0581fd5e7623bef23b8a27a4870f8cc1836819c3091a8f4635bea9c1571bd58b2883a9014785bb24142459a6e1d5b55741d5b57e3d2f361a1ca4a91679081ef797bfe0be769bd07f887c23a42603fda01a3784676f89f31b425c9958203539ea07ffd161b71192bc2f09557ee10f2c2f722c8ba7577bf21998e91e3bd17a440805dfa0a8f2de2a16c2ad875af2953de1700ce4e16d58b0e5cf5553ed1011a6f578d9c6366d2c7805bb0d61d26ff196cf4a44e400439c21a8bf8adda4c7b3d18f6773baca23d1d97e04449f58daddbbc79b569383b4eec175fd87ad2b438ad4930f687c4394c218f92011aeb590e9c8912904b764674b75471bfcacb974672aa0ad11cd75860b8f6ed010b03f992d593a87100640a2c6b5fa172a033008e54c0a012d1b3aa0bf9c855e9134b74cc546f11de413c0cc8e6d8d9b13ece8a71acab9a11d8dbd100e01fdf2e39a7dd4048884f8921be88a92a314d8778289ce9b6c46ddc226138084f5c21a20c48ebad3c31e1379b3462d6c8230f03e9aee17029c63fea17e1e509a1c606ddb0b843b183d7e9604b796f748d0171071d616fb20badf059fb903f594fed7307778ec63a4b5c601d19b115c30252e16b1c08e3b8fa605a12f3108de9a26c85260e5b0144bef642a3431105ea5d18dd20d3a3be08b21e4bca5eeb4e2f908249e4d24e9309c220a9244da728ab081ff68e44d44863cce4226cadff159c9e54d6b338a4bd0d1831d66ec292314bb96e233d7834fa3efcf1f4cbd0c8a0a43405e9c0cae27f57f210692f0f3e87e0bb1bd3996808cc931031a5be6633b96ebb51b8102df0b2da28e1c0baa6828222f4c484c0219783f0c6922a126c5cc455321737b42b67eb5eaaa733036d31efb2d1931285bc0adafa7edd0e06052424f33052a1a812a4d5206c648b0e58fdfa6c31f9cdac4306d80bd9a7f4a9b8aa73f14850479d2c2e52c9f43840491956211a56866013c1961fa5898713ed76abc767a2715fc5ec5ba4f6a2f395afd0cf5f4b04ccc7eed332043210cf58e23eee73b981fe72b35d6f97f99e7c9001b59c28140164365d5718e685d23e83970d10741ad9d0e11e056545e896155f5c4bf7f7a7d0b68dea2bddc86afe3504f9965f816b0029806abb02070e69877b2b2cbf4353cf79a52e2f7c89af6861badb940aea8ac5f926ad8b574f84b6289cc353655b94f31970fd886ab333f7505053293f2dce8be25e86e31013bd8fca40486db42a2ea28d49a963f44faf7e3f5d042789709d00048dfb300a02bcd109d782df369e5c23def633c8f32af03d21e39dd40bfa0f8a7bec52d1ff7346f0c3f0a8ac60100e49978c362db52e2017a05feac0dcb169e1565582f106394e321effd87ad7956803b4dc3e09348b099dfa4b3e7f7ac0bc5a4650b764bcc5e9bb0cd57eaa1fa632846365d50e2bb6c7a7a4d95653b3d09549479c12ee3a75abd4bcabab74ee83b87641b747087c18d25276e08fc4deee9069d916431f39e661374e66064ee0beaafcefab35db6c38082421e3aa781f6639b6ee823e77a983006212b47f8dbfc66b8d83686a3372bb73dcc3fc1f34b66520a397ac97d5c13e0fb756b1fd8771484a62958e119a66fb471fd7dde2514353065a2826bcbf33a93193db1230cd860a1cb47f80d110c7438afe25c563865e9bfadb9f7f3d5ff2c1b128f4fe8e86056dba2302a4c7856f651a1cc79fc65f8604b69f4f12331c78f310748daa520804a711995d3faeb0606c6c1e12e3b9de424e095b18829359cc5215c57af356ffc888649926a6bb66d51c03a9c85a1883bb6a85e35338d4149476051d912309a4bb172fc06bc2e5d21e2113d7a65add1fa54e02d9a7186686ef956df917190585cdc81af11170558483c1a17c62f7995cc149e1a9abde09276119d2902fceff2ba9db11e024d3e04a306835fbcd9cdb35f40cbc79602668f1c51086bfd7f76cd3c0890a027081fc7f4f625d55abf8dd56652d628f15dbebb3c0637823117996896db7d51cdb252d547522f6d3dc9cfa2063a318593dcaacbee3a640487f2b5bd74d1cd7ea822b444f41357d997d6ea5c49a4433b4b6f5ba3e01fa4f79a94d5d5871dc922daf65003d3706b2ac2661d18058a2734029bf1db5d8384415be439f7d4f81089a36c65b2102893eeb3047aa5c4459b363a6720bbafc68204a0557c1928c67462ef5c093e32a8731bed3fa007b8f9a45608d2c01ccc383f229f49992c4121f8ac30a5816770636f3c9f31c4d882657cea21b35b749219635c9708a075cb4e42909116e1722c15dec7e83608e1437cc41928e98bc9ee8540a3f3a1e07e500ac680d38983a898ac0e86ecd109302a10d998707263252572f5ff2280be10604e6eccd363ab0d3e2b941a1d6fd0ee2c0c34cb272780308c9194d950502f6cb2aae7fe8f770f61413c20eadf86f11d22c7aa66d18d50b82810e33f050f41c1aecdf5e45d4d17f50628af36a687a21ea86fdd168089596d1f4d5ca0b3fee3117c120b231071317c32b49a0b287bc88f60607d28c8363255cef64feb1738ee7967296150d574009406f6ac79743bab4387dc3a8054bcc0635cfc30ed486282563ce68389b32ebc36940f4ee68bde3c77e1e4abc2e5b928aa0535d17a78c57483ed6d6d7a9887306074aceb0348ef8fda39b3500aa1726fd78462c7797aac8912ea14c017d68391b58f4491ca0c6a5c99d268f9ecf2960042af4a32ad76b3d372188fb57f9708744600f799efceab5cb0fd17ba93c20042489c33c8365f543789523baf1343e2c21ee870099da0701a221eb245a40f9020e4fcec5a89c1cb108a80f9a78b0d4964fd65c96bf1dbb10a92c70cf08b300df19ef969a9c74116bfb65218504d35c649daae28e3b4c1bbfda0b00202c87d789cabd9aa5493ce54a25e60e9c5443d09223a82e355f722137a84970efb6011b69b90d8cbc01c28ec42244bcf8d3d453b98ec102d82217faea1c86d32cfe72fdc5fc4c232445f443f3ba79b6dac3af8c304bab5d63de46c34c114cec0df7d77304a9aecf9637e0993448cf5efa93f6ff008304407b38498cf81cee6a5a37e061f7fc0b1a5fbc10a207aef8b98d60c757dd75e0b3b3f93f3786c2a4d6099459d096e7122d1c5da7f1875c455d54de103611c738d6c82cd572ae62d8aa673d32f017969ff401b91b00b6b9f1be25ed18a61cd00624a656096874328189ca3c62d9c5a8212032fd6898d1555a8e7c33b25acbad3e368c8cf674a25fe48b9a2b50a4d45488fb50a4bf3a76c02b94399fd043936aacef95573896d36cc8595d0a3fb8174de87a4b299014c683d20eeedf3991b6386ea15f27cead6d4453d801e8e1407cbafee0ca560303cc2fc0ef41e3a5c8cd585fb8b7b8487c3a89c476ffeff9b3f7dbd15d7990780b7d6867df9be91ac25149ebec9d911ef0a8086faabab8391705bf2953939e81a04e895d37d945721ecd29b7b11363f4ea7aac9f36ca8af2b5f1051739c35d5ccfa5efac6c615b54bbe937b53d879b4551ddaf312e40c909ac0aa9d9c968cf88f392be34cdefc320bab3afe060560d5cf3f3378171cd909bda105e52be127ecdcea5b5ae0230cbba97704467890503b703fffe3eff356456c2738aeac5b753d7e63a4e001ad9779b81c313fe234dba68f94134beccd0a9b78c8bd5600e0e7626dcf371e77c77c93a5514004bf4c08b1c762f5e5866705e18e9ce108558b84cd96e0da85f9481f0ccb279f13f8e0b02de4c600fb8f99b66892566f21c682905a42c9d130486362687a9179be05a8343c37eef84044db1107eeb5aaf62a3f2c58df1f0421b57af5426711169863ca77e05e9582fa7c202a5177c177d4f409fec316946b7ef106ca255ff3342dac740741383edb7eaf75a59b6f9318d4e22e020ad0f7f4b94161651e5d7102511412b4f647fd60c4c8e97958c55664b9df7cf86cfd59a3002a79ec9e6fa97e27f048714fdc3e658688201848b328b4ac2b10bde733b48b7f7a395259cf6aa1cfc408dad0268ca7fa0c5023443f83dcd305b50e551d6a737ae458821b53f173999b238eee54baa2cc6b9cb91f1a2c0a35ea2268870c8d62f721051cb2a837924b97a29e0ee6abcfd140ba0d95cf8f7deeb8085014234b75328763944343fe989b02405670d560846cc85fc852086df7106eb2f43f9cd279212a66fa6cc805890bc412c0e573445fd7c75034de9cae828b2e1a94682b7ae8022732bdf6f73a69366d3d007a41a0e2e60602fb4f649c1fcadf6f11a471daa1baf6799cf7fd78c5b51397d9d64bd74c904bbf47c1516c0fb3a3e05a9039aeea810da03156fcf32ecc43f572b5d9406458efae44501b64847e40a025b50dcc3314ba234d0564cab01cf30c751babaa3b438f33112f6be240ae631e231c4b5bc799ee8b09af91ae8e9b08d3b4d7f892bddc7f3ab4db10e9e963c97a18dcb9a6b18125123b48457a13b77d1e80e6f16c5f6d5e32a7c78e4b69c75926eed7ad314d5643ef7ec385d0604adeaab007c438d7dff1d0e1bd660c6debad995e6d630c0c890055cdd5c0d847fe33cecbb0dae3b14838bcf20fccfd431db33929e80fe0217ed066780dc9d2e48974c1fc0dfbd0f74a4a52346e38a3ecfc6c9113e2800bc03c310cdba61c908690a22fd0b251933e413f8e1982dd014312780f62b37859e440eff65924f0eb5e3c1db078a96f9ebf51937cd29c34621957f165f17bfb1f3e59fadeffc1af58e2187d1fb3af04d99c74a9533ad7e8df67a5b06622667cc264b547efca84e1b9b1be2c9a3a76ca6acd0e93818155c5f167e2b6dfd06afc8692897f7609002c32ed26b58f30f6b418312c07f65a82f5cbcaa9fae8583182d16b9aabd61ee326de906abdd132c270fea86349eba9375335d3238031fc4605bd53fd7df2a14b4e84e34c6e3bd56aa7c18041dcb8f712abab866cd5727b9021f88d6a7aabcaa0ca8f4c16ddeb2a9f37567fb12871ba245aa04548a3831623d01b2d3b0ce68fba97b49e7233556b1a5a18d1de85cd96fe7a2a8db6072e101be11fc7b8cac8320b77503edd987e2c8365c64526dc0c7ea3263e3acb94228341a7fa85b38df7fb500a6b5644c64ecdad3e9685b2aa399b82f9604073d8b5d000b2d3b055759ed658beb4d9b6fb0d31d83b875d091b609f21067be7a869e103ec37c460af1c352d6c80fd861aef9d83ae8506a0cf50a3bd73d8794be50808416dde07caa1ef95eaab030fc2fbf31bd5ce67f68760214919122c35f9e63d9bf107b2d427557e5d60a451e63d6ced70329a5e6e3404179c7799b7e8a0df952170bcdc45c85ac037e587e64bcca2b6546957cc63fb308dd26b81dae1d2fc72b1fd955828adc059b927b35e9da67b423f065f55b3e477f70430372189752cb28759814ce71588d397202d4e8e9b13c18ea5e5988cf36099e62ca60f3d78ddc44a0c8919931eaebd66bc616fae4f032a882319be496a7481c869b3e13bac5a63c390da373154a5d758734d88a41604d0d9f6dfb01b77649eeae55971a4a84368bf3e1ee393e4c25d44df71fbbc296320f42668326cd385d683510df477eaee9ae82ebe0f909cdcd660188ce0aa9f2c12c8138e3f0132f0bc8b4b38b02354aa48138cc1e380c49ec8ed834394d239d506a748821f36df6fbfbc312e9df25ce45c99bd02a9e358d8a306c2150cd11bb53136f24f92ce07a4694816c812c0dd09d41b3af2c226f0f129ed9646cc8f080b45a322d824a3892e9498d19049b80910ce96780fd4b068d15c08aa14aa9cfd9c117599836adf6c61fb4fcec0e6df3a912d513547941e2dd85508c21b2d250e0f922586f78649804fd84c2ebbb96d217378d751aee156132be01616341828e4389998261050fb83dc6ce5690acafd72922ead594d87001828694da02c8ce0f96c6a048197896e5bcdcbca536324a412c3ed609d46d5e60d1825d123b7bd3487a95f33e50b8592d42bf8f2c77a9896486e445c9a842ac5f708204a88e485526fac1c1552ccc96c397170fb3afb2f2546fd5d380d6fbc6f6483c92ac7014c02c5eae4aedfe71cc76bc8f93da25bcfe7c6e6f3209873bd8a008021908d2d7b58e143907164c85d89eadc80db7eb68778c32ef195ead796575976aa829ff818f3c581d1bbeca8a5c8df41a46ccbf09480bdf3cbea84e0bdfdf71e81b16072a38fb09ecaa8902937d65cda81bfdee8846e587cff9d8d35359ad3401852c105b18344b9936e0c870517fec632a03ebe28c39c7d9f36349b0ac21d9ec48f7bc8a593f86a08abf30f94a486fe54dfffafc25415233d5a23acc7b5568a3a6614d8bf3e002072c9120acb7707203aff80b6252872daaa2f828b327c81149d494c4a03fe8e6e26364c81e47fe766a654088353fa5d45bef63cb45b5d4fa487fcd9f4021fc5a35faff92254ceed26f546da1f1928548110c8185d78f4519e07506fd48c3277111e7809c50de28ba0740ac6078919fd17e8353dd2ebd55f5d1276466df0a9eed075c0ddbb2a4522975a3e095468c0aa4fa12a5f2dbeade073595a957137cb7dc1df68562aba2fbdbbc7a6909184f2772e646e36e41cc2fe2393c08d1d7b175ddf487502f127dda3306725df791ed36f30a3fcfcee4a39f7280e38c666864766be8349d65e3cec1041655987df1fd105873664621e62619c95a14454f52af04bd0c67b1ec475cebbaf748aa0df8e32ddea0af7ca42c6664861276b5248a1604fa7ea8004f75b2729ba63c8b285ec9f9b6fcb91a34b115297e9dc48e7305b010cc4e9497f8d7ae8900da6a65e963876eb25dde6c139077e6a5670819bab61e21b20af02b83c19863349c1579c7a7ea7e296904afff57b04dd6e0209c5b7cc478bece93372df1920093d107b463fd9050deb6fd426f977ad3bb05e4afedf9a4f64f0f48e98abdc84e27bb4026dea06bd4b0792147dfd25013f6f7412ad26d94f48d9be966dd6a0c6f3a7eaec751296b509f48aefbea1f6c14e8e609948f58972a7a12835eb40288047a486e1889350273ecb3c49598ff4b79707b3cda42404ef5d93ad5e16974684dadae5bcd62227162e2503a55b5cc90c8b105c18ce92698dd0118a4da3dfb456e484a8af376b91fbfe2ed49c4befbdc75d8c8412780b48f01f9d04bd18092ad81ec1f696172e70d5cf6a4e8c1f2bd0c23905d3a9248e2b98c27f71f1150242f9e5420fac2cacf627b424f47435faf4008843731446fede05af5f802f9e0b6413c2670cfe74e122cdc62211f89cdfb65473b36546ac07405aac3a2b4e0519ead5676decc2be7a773397af192e9ecc79daa8c41b9079db9bee25d35ae40f62837298bcab4ab3f59378535c01c66828a732dc27c10c7a0416a1b54d1989e006ca14135e90238823100fdf7ca3867356af0ce0e9331e2b4ccc546bfbb26797f4ba47962af288747ecb61c30ade3b19aef3c6aadf0f846fb41619f7279e15c5e2acdf953d3e789f455b3e553c2b80d48b35cbce6295e2a8b76f97f8c1254b70600c323a5ab58b65e1d526470034f58b3dde198cfe02e38e37f9e60b1499bd516af857492a6eada92062aee55fd506f4dde4c660b9ccef4571cd03432e0151abf115bf3123c8ea4e5ed53ffb1ec9696fa309d08bff2ca1423334af5ed1f76a2442cddb2b4af277a1776569ef45f020c1bdf46ffca6882b07176d2a5d2b15d1157e4383279f8372ef3da4a4bfc3127cd176a0d54149e17672789fe5153a0530ce837b642dea1b6ea49a54794c630ecc6c4395e68b2386924907ac5bc2cc3c9db16bead0e931be03d7bd610e4c0e78c250f4e82a7aa8c1a695d3a3a6cd03261342ad9d69d74f1b060590f22b9cae6dec4a4e6c5496017475039a9716895d308e3a5207a5f1ab841e416006137f03789f611899ab8804af4174e7e6bdc0ab755f6ac44f83bc747138744065ff1f94735707875005d077f4b59175a70093ba25ac848651b12b5c47716ff8023f5c04199b2c217b37917b4b29a54c0132083308ac08269c08de74c925fe0007e8fb656ffc86de3bef45b542f84a960ba8ae7313e4a686e949abbd9d874da6707e7355ab288aa2484f4128cd375d72937386cd9a1fee8a496993375d322567b0d7dd195be9ecef54eba4b3adf5ca635fd5f2f4a03af356144a14734a4646a55aad6666683a1a1a9a4b43935332a511ea55e6ba924c53006542d56986cecc9c54a8959869d0a83468501a2b1aac99191a1a1a3458ac9a1a1b9b1a35686868d060b16a6a6c6c5aad9b5651a66439c77c9338382e5775b95caeae238b688046a3a635e79dac1a9b56cb63b069b55a3735705cd4355d35705cd3e5c2c171b97c247374aa8e8e0ed5d1993ae348e6e4e8e8d8b071e3060e1caf170c16926498a3337572723ea8afa3a3c33d06005461dd841eeb83d7db30af623b1e04efeca9e4dda1a2e3db412f0ca92cc7b7b7aae432c43406b395ded9dd9018bfe257fc8a5ff12b425d67517b0cde74c9e515fad7cd976c72b06b4f6a4130c463d357ad18f40524d1182ac7915d25d118cab1db718e393c007e600784123df68fcb63ffb6d01776cf9b9e75fa962c17f01f8ebeb0dbb169746c1a1dc285cea437bc306b31b6a6a79c7a1e5b93ac723d7be7d3e428a29e7da3cecfa80354613d6e39dfe3d62308348514bcf192addfe4721ea25465d7b10f59aab2ceb12300010838d23393938f757ee806366ae377408f9dead81ebb8d9a8d1f7a1a426997d4b163e45592985cae92562b2505740ab89ec92140686a1c925461583cd2b145a66e4374c1db10d117be8124973ab95c9519f50c74ec3a53f4cce4d8758e7472594967f68c3ef621484392aa6cd2f0506695fe4ffc3c763bda7821672d1a09dc909f9ef9631fc08d13b9fc8690c03deeb9692297384b9f4346ce18d1c5b18a53f44bac423c729a4234ea1decd52929fef40ef69351efdc74c9a05d2a6d886ea8e30af52147f30755012fc0d6b31a577cd952d4305384dda95e4b1b22f1a6897c6b2b2c3aa693a32fecd857473deb98ce13fdc25eae90be5c2d79ec2ba09e59b72192c1e2b1cb1cf54cf411e5e2f8b94e8eeef0d8b1eb3cd1b16e3c05ced292da6328b078eca50dd16317c7041cf50e15c5ef86e46337e53cc65e7e468ffd1bc238677c128f401b5228dd349167ad77a6e72c81533cf6326787c79e13d433d1b1fbf065dde1b19723198f7d0ccac125ced263af0b00ea1decb35b2e57495fe3451263478d41d8c12fcc5ae40804f5ccf5b8bf243df6214755461d93270499ff91de3bd883e87890ac925d862d1e82344ec94392c821471768c9d4cbfe72f4d85b0793247416b51618d4e7e3cc39292533a082fbadf8049396beaed31a463d7ba1881e48485fd7c8de246b93501793d88e0401885fb0429539c6999c749222908b4d9838c13eca865c8a40a20d9ca368138172982fda2819d217c674d2f27e594267a1bf42b132f480c64420d1f6d629c94112f9c716b91481deba084463b489edc98dc62819a49f8a184463d8ad8b5668cc73fb038d753ea405f736483ff552dcf22291c845ccbda564d8792982c95ecf091d9e3842a625c0cfc190be3c0f9cb576ad90540026bbb1049e49bda6708d98a8dd026c50a46397458a3c0928500bd4b64ff137187a94cb2602e1a6eacc1ed5074c4bce4095f9b0326c6bd9470f1ae3b95122fbf0014295d9ca833ba02dc0d6c198867e2df53bc286c89d972ec42f760c7499ba754b5f578ad9b7bba6dd42eaad8d5fd9f3d2877b40ebc9764e080e43074f24261bf6d58e5e92ac1eb56480fdfd8d9e7f26b22502759307a1b5f6097ba391c72199c1e4f179fd2c58670826c71ec444620c5ec8932c5f3fbb8ec4e0857cc912ffa4a74a62f042a66469c159659894360a17a4a338558ef2d5d8afd498e9493c799f4e33f47a0abd86b5d2ae14e8dc93d03ebdebf144ed0aed4d5f18bcf0222d7dd9fe1bb9ac4307cf0a7abc7d7ae327a44cf32a83f9a3ffdedbc3c7932ae4d2a914f07390f423256cc2f809b0a58022b30b21dae72647617e035ad3690cd18ad018a2816ef2f94d5fb7c847e85fdf453e2e7c914feebbc807f73bdf452c4cf13c3d9b3c43b60f28e144045fc488e03deec6452c78e95cae4a913379b34a501e40ea5eef4caf5d2b256f13fb705281890e7e56da797a3891400bc403132699b1b575ac514a635db44253da5c72755a21e69c135799a579f644e2d2436d42994f3a05b68e4d01d58576dd08ad56af649116126d898c9427d922626d0aa1129a7523b43ab688d4225d8bd4d88166494b37986e04052248628906563abbda2fbc286144ab630b89e6935216da34621313b576bf3ccfb2fec3a09739042d8db540bdc3dffc51c17c2306f29f801d85fc13dfd057e7df67e70f0ce43f613a0af94dd9278f4a7f923c2f7a1a93abdb49e226b3a01ec69e908ee7bb5cc56422ade5b17905438206251a424dab0042714989b81835f98401682e97a3399acb5122ca85e6a80c749353231aebdc64327d4a97cbb34007eb29841a445f56b4f2d6dfc68a9c7beb1e98b7eedd68cbcbe583b4134f63d43b54bc7d89819e3a14b9923456c0739c596cb5a79ece04cfbb77b0d7530f366271d3ae5be56cbb77f0cf39bdf6a9da1c94a8a2a767ab98a71084f800a9304b3616609360fd9abcb49eed48ebe0bd9dc98ea5053cefef96b40cadc5268ca5058cfcf4ef739c41d03b9084b02582e7a083240926f7becf1d1cc3d1e4e0e87d267c2ff6ae081dd912e15eb203efeaa705af870513b97cbd3068b29febe49e5f999f862c1c66ec5def5e5180e749a8ab1554993ce8e4411d8327b4886ff2983c9ec84c1eb356a193c7931a758b7986f29c643e7d40fdce39e79c737618daebb66bea7625742d100282045f9d005e7d84071c7df56f27b06cf9fad52b50659556bdb33d7a74c9c2c8092192582c5fa1f8eaa710ea1963defa88b7a12b3fa37443394b72290e894355d6d4de59444d45a402a33a5285417a9bb2bdf55225c667de908ce0cb10546a81e84b95110c62be54215995d25bd5d2db6ba3b132e76e34568a43e250ced158797f524f68ec12f1b633b28135bc753abdfc92de3abe1cb0de2574c9f3af4a899b26721721c97d89bfc6ffd4728052465e92af8acfbbfc8ca05f8e7e60fef3bef366ccbdf7563b1d08bd1789bee84dba475a72e7e53d3a9a493456fd7a4e7d15d4330c9e7a37de2d370c955d2bb77b74bbb87bbb546e50eee62e98d64f20348713b96461d64ccdba80703e7cb086753c3d68a99d6dabf550d68150f1c11aeab892cbebd7281b3d75fb894ae292a8446362686c89c66c334fa80c74ca0374b58ca1b24b65555e5b292adda127f4122911d198b56fad678b45a52a6b09011d248b4c9ae7b56d55262a358c92a2d21156de924a6280bc2a5ab19262502eb3a8f434bb803c2b60eb983aec1d59e4851b60728f2c6262d548ca657e3c36ad8e257c385a6ea4a7b7008df475529352bf0f0f9e3cad3e7ca99385892f79b250f9b203a23aa5082bad6000f97c17011d7dd177115093a74133d3b3ce59ab9e794e8308f3d57b48cb571205fa17607d99815aadf628b0c4ef6f133c78c209d6bf9dea78acf631599216a83fbf45a0762c3188e97c4ac27cfb24ccb7f4656fe7f5ce5720d4be6013fb62f0e9d80774c59c5ba1efc8896f0fd2b2ef480399005a966b6c40b5e687d532b156539e7fd828993c66dece69e79c330cad652db54ae969f74ec9527a1a7a9f867495ec55500ece3b7ea00ae3a9d77a0aa29aa409f38ce95532a94aaa3014e452758f74ecda809c864e9ddc246f0dd03253ee83b44cc6a9c3dac4c19bc294d31f9f0fe8d431693a5aa2b19a1aca8c8ed5b0d09127b2e6a7a64aaa31c255594d925195d9d42a0c69feb041327950ff54388c036b6a34f6fdd4b0d0b1af03353fdf518d5195a170a8dc67f4262f7347f31f108d5ddb1743c7ae932b45cc5da5a7b99a9f2a331d5518ad32d39292f9e3f3993ca89b7e6a7ec4dab5d1186a89beae938ea1cca849793167ca55994ae8a8ca54b80a5baa32955185f9cc1f2aa41ce8655619999654489fcf53f7bace73154e25549354652269a3a46159257455b8a76da3e486b689f294d26c6a3648e60f9498c9833acaa6f636485a442a4df4b1ca8a642d8dacaf03cd12f3df0f8db1c26049b9d5a32f57b8a75e7e473fa557551a465d665cf9d030ea285c95ad826818f52250b8554db5a41293abb2d5924eaab2d58f98f9431546f35849795ac353c7b915904a8ca7dea1c43c755095a452ea99854195847b6762da54bd4aa977a88b639114682814a94a4a419e5eaa924c9867d86bed9c21ad0180d27b3e718fbff6f873d46ea6c85e77a32f1715faf2dc757305e1d025b4b3b4a3889c72194fb98c9736401ad47a86ddcbb82a73dd6efce4a6af797bc9a45c37cf5d543c1bdb7f364ffefb5c41bce7b3652ea18679fef9879b3fa807fe4bfd37e3f39f77cffcfcf7fdb842d032972bcbe7c289a2288a28d4e9f42df1d3ef5095a97055f67d443fbd447d8d7b26cef2a967ced47e66c415d6f96acc3874e1538fabbb6fa6c856354e1af835b9f7b3cd8c3b8c563b8ede03426f4890cb0fe803fa9a785b34970f4967899f60bed421fa99434699b3f4b32497f84912fd0cbb58e2a7bb703d3b952e22229b211bdc4f9fa9bde7e58cd0cf992097d0ccede7e471bd9c51f2d3bb9c41f2d33f5ccfbe9f3e53eb99eae7cccf2c7b3973e4e70e29def396edc835ccf32933a7ef78a2f10e2ade7301d953cfcc51730955d98e5c85dd68cc0688bebcd2757bcfc11db92a5b2955984763363ff4e5b987ed97363fa28aec0aeb384802e86328d03d17910b04bde3b9cce8faa131d795d9f3870b88c9c3f3d226c907de8ba44ba8773ce7e0886c03d4b3d57b6e63eb197dcf71703da379cf772455990d508581e9d817437be9d807445f5ecf7295c9d8d8def37287d27ba2973b96f017830dd075cf3f20afdad852e367b27be7fa25cb12ae832e72f19e11ee4f30619ef155528e02fe2a327e3345c605a00f923f9896ea92a983f5a64bb6a720351e7bf7f77ddf77dbdaef76b03b4d07bd3035ba7ea0300fa42e21fa72fd405f9ed22c49de7144639eefa882c6ca12aad04f303400a9dc0e231a9beef94ac99b1f12dde2030a0366f92ede73301cc15a2b10e8a1354d13597be7a767433d03ddf3ef48c75cb7efa767d33def8eb9a6f4cbf3f2fadc9fb2aa807f7a66726fa5f4defd99eebab9a650f754000a3550e13d2f3fa0f730f0f29e973640efb969c420d73b27cc95d28e5cef784ec1d92d9bfc84f9a54be83d770981a415ea5a9bef95ad41ad773ca33cbddc917bcf01a05403864fc4094fa86f1a036db574dab61eea5f37389031cf57ef3aefbebb555019e89da3967ac6e3bbce793af7d101958514d6f9aaf3f2c6e9cb3a985cf274ced383ca72f79ee7b9b2aaccbae736d7b31aeff90da231211ad3f19ef779df75622e06f0f53d718e1df2f47a001007f7d627ad2bfab23776fe2da9671ab40bb94955c68395accff9e4ad3fa1b1d2081d858c405fb352d4245bd4fb2f1d41087d619cb39539e7bdf3ce2b0a79a318445f748e2084be7004e5b27b8e23081941088d5d9f75ce52147aebed54fa4e18aad04fd96c49beeea4102b6456cb58146f199f82e4efde116f62d0db55ab4ac69dca00adb74ee9b8af5e6bf5aadf076fbae45e32e3b3a582288d9037764e47cfc5a57bc40c2c47ba3bbbe5ea25f65beb1d4a236473d43bf5472dcf18f5acbd7e365a83f850cf3cafee583a268a535acbbb927a661d67a867264f0581a611bb78a34bc4294dbe7a35198197a4825c5cecf01507cb572fc5259ca1dea9ada1a0afaea4dea93ebb65939734425fb1f7cda6a8fbb6a585be6a0bd71aba404b9e5eb6867001b6967df8a604fe087d8338fac251a5e60035427099e5b6823c78c2c6c183c18d9a1a0c3230e13e201b46884aaca49a0c34d0792063082068d02307031e1e0e7cf8e8b141d8ed2f58cf2af9eda911241a1b069008cc3d759b538f101007c358e30e63bd5e34368885263c7efb1c539d9b8563b2d0abece418e7cc62f1789088be4cc0c2a0cc0e616f5726c282415c85e15b31cb47e8e3d463c2d8e320b4b95bcf8c7ad6f9f42fa86717e767cebff6ef876fc834c5375173e1c56f120dc9128320ed23c12d234874eaf148106794cbd5982f3e4f566368ac5c996146cffa53ca444f9d46673a99304f509a8b39f481120d073530ce19c481381c88fb320b51443debc1a2810271b89ef5065b6a596b7d7db73a15c2aa305b2b586b7737699b9c41c946fe84fbd34b68d94c5fed9472a084cbf9ada6af26b1d07fb89fe4246d0ad348a5a16dd21b5f5e5f2ecb5e215ca446a918637babbda545d540bf7cc16e4ff0e1a667adb5160a6b1dec3c6b2d1e3b8c6782b8429316a9512acde1c460d8a76e9da40f0736596a20f7ed752c3ba1af9ef3443ef5b47bdb91ac3a545265c5374db5569d31300e1ebc5a3b9a2c79961dca866c496c6f78c9934e1c546aad1447509e9e438c634aee5a41d003d65a6bedbdf7de6bedbdf7deeb996468dc8460cf6a9d06f9d1ccd8d09033ad1c7bc5e8543238aa1c991c6cc38ededfa85e6dd4da75b3e70d1c7676213d05a9f1dd0cb5aa4e3dd65a5babadd65afbb25cb87c51e9c06aea860c2c85e3de7befcd616fcf256fe0f284d9538fcd21e6783991adbde00545bde0468c4b6d48edc97452d9b06c705c39372e0ccc31eaf8dc857353a36523dc099374e45654268412a132ec94f4463291ee1526264f2fbdeb5a3635a7572edf7b7ae576f08e9907f238f5541e9d47fb7543e8be5e1e0ceebd56e87aad648ec6ad9d7aacd39c7abab1765720d4c64bad198455663215d19c5e4fcc9c7aea0e0deacc4a4695c2c0b698c83d424b633775eab1e48d23f2f47c7ae550a79e2b9e7a841b84f5f40293af9f7aeee995cbd5e7fdd9e03381a86b732c1c40940d7336834c4adb31fe39e71873d2a1fb33439e3532acc001e84b1c030e3f5d2eb3944c2d641a127852a42461b29251df455286c09b94295d185290605b5ea9a043c1155be499efa21b139e086e436cb909815e6e35dc6e47700d5966052092ac42e2210a982f4ac6df4551b878914fdf45518ae85ac832df45517a288ab283b744367d1745b98910c506c4031008a0843d3070c6f9a3e1f1415fb3077d21b5a0aae136639444c686551724a9186caa2c31e418946498f89044b421857998e4800485330ef312a1cca4862427274022939f25a1932354194ac0a413e65902c687330eb30c47322485590b24a62638d008cf40f4f1e0a2173c1e1f3c3d7cd02064c2986708251dce38cc361ce6e1c1432d28dd6cb41a94d066b8152d71e187d644c98c0e30d1c2640b85f5973817012911d19ee4448092c4d3c3470745508a1883f0c8c8f9bb088a0fa61f28b620b28b0cd309b27f17dd308549868cf35d7443960f885cf35d74030e265b4e7d17ddb0c4cb0d4890641adf454fc4f81277423c410aca47223c317a0264bf8b9e2451015881f7ba2c3e095d51b0df9fd71b0e1ef2acb53604c2e021092753c45062861058e0a28915a32c80183a62643a82458f8e0432744548587c590188142c818493fb7385a61281e4d61d814407dbe1174f6a366072260ec6e4123ffdee10daa74fb235bf82752ce9cfa064ca8152a60eceda14554a60fe38839227074a799a70df7552234010d4d1a2e648419bd6ea224b8c8c83871c0407070f793a9db55ea07a04b64eb9ba3d757b7add27dd8dca538c276e7add5fd7de9f53082c1effddb0c8d3cbcff6a4c5442e6f909b9fa405aab2cf566134e8e9edc7f5df4d4b9e5e0a3f7be73aed92957c797f5ac9d3ebf78a36d4124f1d4544c3a8e1a98761a82c753bd942a226b82443271fa1314f834231a91c28de9ed63a52a2304341d79256e8a66e5546c1d02a52142945c3a4a898141d93ea2a49739d37bd9b2ef9fabd15fff07c7627a0809ff68e75af5a4bbb77aced3edb07ae370df5ce7596d1f55b13c3f87b3bbfa9dcdfeb140c9589b75aeb59f5eb3427128dc189190a73af37bd97fc6c2096309e1ec9e11f223143411d691aa2c1abf1e2adcabc2a288c3a525018213143d44724faf92ce919d51e5b2d9dd557b7151691c5059d4d5f744957e6d890ef779113277062b2f85de4040743f6be8b9c602972f20199efa2168078f0bbc8c9942e30c662804cb8edf431828719d4dcb141054ffdb4836403113ede7a78c3c7861540115c9041280a1866280289991b922f5f449005122aa03908d3bb2958f0d969a8a00207b65146152b1b0b6fa7786babe2ade71a86debae9d463593ba0cc2f431a56785bc55b1aac78eb38d360c45b23deba87775099a185b7d8fe588204a8eea9d3892fc32219806862450fcd574a7013c3edad756c3d0bf1b6090d6f7d0445317420364363d6bb8d8eb012d230c9848ba754ac02b6f7833a94c8d48dd03eaf2d4a0af181c17a049d3cf41e88803db40ef0f9c98bf440bd0825897c1e92453e906cdae71f394de043173ad8c042056a351a4f85d50ea260e07beb258f08d731305f47973c21e03ab8e430102881c684e0a38740bd22787e29d03dc203664d84268b08592980fedb8b143fdf5ea258faf2d6beddfbf61245187ffaf61205d24f9d3207e8ad9d21534f684826cd538fccca7b093063bec4375f862f9a5e0c126d1566ebe9e6289739b5b79e3da796e393f31346f4923c7a7108e8adaf7e56495236158ec66a8a0a95e55c85d9aa2cf55361d65335304b5c94b62469797bf43665cb292b9fb5c84674ec63d234947a46dd3a0b47fef42c83b79f0c5bde9634c2789bc422aab2942d65cbbdc5a9b6b4aae432654bd9682c25a3725be3a7cac8a355ea473534aa7e185542b62a53015598751bbebd75d1a60ab2a96ae2a8f251fd8caa24f4656d5fa692de7a290ea58e5248b92a4b05559875967fb9dcdb9451eead934755967a4261d66d8aca5b9fe4d1db948dbeec9016fab2738a38fab262fd94ec00877c6b4f2fe933b78abbe0e711c8e5e882b7731e5df0d6875c5621a1fa03a5175cf0d48523b6704008283c8a05f6ba6b75e6775190cfb7be8b82a4fce9bb28a8c9982a4693a667d7a7cf641aa33e2bc6f38ccf542589cedea15f6def4cefc6db4d6bc9b292735a3a59d001e9769ec55882b00c59dae1eb9e7dafbb39e43a450bb404d1eaa801141a38835ac0828596023e1a1a75a0a520520ec8f0820a2d85e9c5184b569cb2a0a5b00a2ab2c110421cd152a04534a1020da8326829e0b0c20439a4538b20c638a2d511070c1ad885769c2750d2248956c71a2da0811e46d11e9c2831a2d511e787464311194865d41e14bb9690941ba16523b4f69bcf2c6a1d954aa1c8222debaf0aeb8aa05ca6088b742334f75161afefdc9227109171d187342d9345647c942c1a2f7d5c5ad364bcc4f6fa0815d65df284214d13d2798a3442eb200aad755b44509e1d45b684acbc45e4e4aa49cb640f14a8127a95ec4d6b00ca534e44f419bf3e83a2a15c7423347a1212de749d4f2bddfce1bb69fb6e52f92ee83b3aaf108d9595a806755ead74f7debb227d569e7220b468e9cb775eb9d098ab75c1d3584b9d4623299f9e7dc641d5585b79e82d22a1af3c24ab2df4490b89642e58801e85fad667c81eaea7c6169193a7fc44d69602e6c9ad1319f4d9659f7105ac3c456e20e52b9f2137d0039194af667c032bcf64cb9b366b13a0afce67c60a5458e728c83e6b0debbcfbc8129b3a96d7cd54376bf4d5898e92711a17e2e393f295fb649f711a19199f2e4343169921e7d08aecaa1a8dd0c03cb6a6cbb8115a6acc329da77c7ace2e4316e99ac92229b26baab32e925d9bbc5ece5a474f4480ed28e01771d4e78801fc267867b4a0f61495d4330efe02a9b2cfaf0ba93293df19a51cfac93dc76e225b950441b2480ff5f3ea28b2c8083ed207c1b1d58e6ab5a3dc082d47c441d051e4abc2ae1701bdc2ee748addab9fc2b1c14904371e3d52c4517a71005b87a7867177639f4ef35052727f2bf5f35b37f4cf2f89c922a09bdc08ad9e7a84f8f87c1efa981c24817c234febfa685d92c8e726bf6e32394816f15c619e17f9c8ae4d6b1a8d2172ee9deba9deb94e61d73b1d6fd2da05ee80034fc8f8b204e50a122338fc5083210b28c014919145c997c586a239645921cb4f4775a838201e80400013c5c1880d2bc082852bbe50523b2387173a1d7288c1468cbf5f784bc0d6e9bada8cce1fa8da47add0da5fe1982bacdeebcdead183be74d0570f19b2ed415fb5e4e0abef00230301bd7a75c7d5787c700902e82badd2ca4cc79bbf71c7ace32eb4fdd9eb706825bb0c6f19e26fed4f4140b2d5972c33ce0cd9b6d294bcf257efd49092db7996ba6ba90720104087109fada022ac1ec05e77edd210952472560c201ea5d4abd75d4b737c17fd003464848123b61811c18128c210238127bcfc90822286c2004323eaf25860afbb16f65d14822d1ef55d448587dc176037a57dad1540adb5d68aaf002e14b07b760abbc1afdb8429a59452ea75b73ba42fcf4f299ddddd6dc2a1c3ef8aa2382b89423de8f5a0501e36fd244fa7a7deb3bedd330b8661c7c23ea11a25a250e0ac2d0c60f70eae75fcea4f42b5d55a6b67dbb646605d75b7b5b3d25abb6d77405d3743c2cca5d5f3e6ccb244db2bab1256f3d25aafa5b5c785dbfeb76671b794f125ab289bc2309520d72fbe2c53b818b305cd447342888cf0001fe58b67ca172a58df455384f8f0bb68ca079e0a53ed8829054686e032c6a7043f4861748445e2e5882f48ce606294041066d2d16052610a064994b044f101244661a838a304616411650c2f68e824980c2561250b11415b6a1960e440c60d3a7c41228a151c79c13b325df8e988b8558440e6048a2a2a58a2036e694c4d0a2f4da8d8e06488d5025c98239ac00186256a07188003135466b84911ea00077246b430c101a9800c9c9511f048eaa288304d7c60071c8e18eae10a31c669ba204492498830acad52b2a8e8f094ca93aff92ea2c2c28421d7efa2294ac01b32fd2e9a92e401b85947f0a58f05ec1085043804b14208b868339ef82071023184983c112204478c9c0823258856f25499d281a7464522f08012188eb8a2684a0b5ff21c1de98ebe6cdc29070e30c9c2248b920cb4d2079525619c584116221052d2a488a771ee638616fca90f2ac315344ea680d1c3952c5e4431c2c2d26d05267ebe3431c309b0e8a863ea2c2daed4a248dae213a50820a48a048985188e6081982b4030c50652e4aee8a1488a1649f3d559610514368ca0084b142f901cf03386172fece0840b2d75052b740c8e0a6f8b96202fcc0871f8258bc67751182eff7d178101c5871ac697afef4249174face0831030e8b0c109943892a28c123061c509a2781394c0c80519bae0828b5a15508ce0c41061ae9871e40a92d3a0a7de18a9ab620563b878822b020a1217a81d9164c88bed0920704705c61507c403100820c4027b0658ef16423818e1705423c372b91542fdebf56fa95359ac9b8d202a1347ae83c6fa7e750f6c15c9addaa8f5dedba542639788af2b1a9b5e6fba9bc303f946a9d66fe7cd710682dc3e495338c7ef0afd24c1708e393ca1c239ce276750727981c4708ea5756b29bd264b5533041fcce11c4d3ffbf5f6c13a0341fee055c215fa3b49930cf993ec991077b5b2abef5bc01104569a1a02d17e8166eacaaaee4dc63b7bbb45db7fb66fb7b5a6bc8c6badd536c6debdd9507d7fdad64e201056ea0da2af767b4393edae77f1bdb5d6ea16fcfe50a690f505ceb8bd140b870fbb2fbf2a1f4dbd83ad8e16b5d65abdc2aaecc3d8b3f767f6f49ac3c4daa1ce7e417f6f3f9d46e84fe4e9dd3442a0513df5952c25f03bbe98b4dadb79d8f481e10925e6948c6a354343835563d3baa981e3f291ccd1608cc0088123cfd1b1918110d1fda13d208ebe6a5818471b366e6030badfb88183c75d2e1cf7c52367bbcfe47d26ef762e1c9c174cb4f5ccb6db7c4a389701382318d580e5d8b9c911de74c917e8b8c1b1c30596c8d6337cabb2d305444bbc9cb8c13a422b54fbc9f1930471f465415cabb513ee9c7a3adf51b14fd8b5426356e8071af3c0cf047e26700b48445f9604712c80384b63bd23b4b89e01f50cbf7073c81a91039e59a814aed455410a95b21901000000c314002028100e09c5429138301e69a2d67d14800b73983c744e409787a320876114034188186388210411408c21c614d1504500c9816fe54a02a389bac8457452bc6ee419a7d1204e2e2766a9221c9314b6e8d1208020ed8c088dd4e22536ddb352d0ba562d2ee5fc6c3f4ce9889c1d9f6891f9c18c7570640548eb320182007393e344ae008dc6b8a7156ec5a248485b752d54198c1eed500f03f9e6b94f9db47417fb340d3e155a0cdf212314f2160ea848572c28374963698101009297e5a51177cf6888efcb9ded4262374bef59e2f000e8e5687932774d5a3a4bc10e2923ecb45af16bdaa405ebb41bfa2d8c56cb0f2416988171a896d3b65a99491b74a92606b8a3510b6f62654c8548ba01fd9f10928f003ae56ddb0eceb2009514a0570f3da5c856bdea54be2ca13a4e53fdc4deb39d0323a887a9675146b5ad762cdb41221b0b15be6479e88ad208f7337a18d195a76f7404964d4aa17bd8bfc6d0953bf1a5262a4f27944cb96ad3b909426a8357d2dd93f9681258361a90d296919cbca1d2c5c0e05fff755f2b338efcc8ad0404311a5456f4795f17800786219dfc5b631e2f791e8637f39cc3a7a4b6a6aa6a8e751d36624b8af6916a12a29c83804bf3ebab86f9645f9310a240fbe081e13633ab1074f4664683f266551e57385ae7369ed6431f8c5cca063ea6458943686d9a3317d7c3241bfa5c147937c44a40b70435d055078a5be91e953c9f4f8654c6d16fe5e8a41c34880f2def9f56782a0ac4a0d2bcae721afe88f039b3cf05573860221d076931b76de6775d97114af0b02db6552515e63f864fec5f72820c314584eea8c761589cb322e78b013e260cc6967803285eb985f264afd45c0b0a472a66d0e68a2a84b33323c6d3aefc1a50941764a382cf81fca60ec97489f84bc9afc3c68bc1a5fc1a2e1c7e474d3f95569a56ce7b8a2f8e3072ae869987c2e8993181ca377a7ecec84c5fdf74fd20cf13a08e74fe3cd50ce5dea0693e01b4402c0a62133e54127c8d258ceeac355f08226ec9ebd0a105e27d47c30f2d30c29c04283c41b0f907b3cf4a440b410500ad1468a19b13e2351ac76133061a7c03fb6a1eb50a913ec2c96b55edca4f84e32c79e2b784b169777f0a8f894d433384a0074f406293611c24eeb6a4abc1e703f4eea45821f99d470c89d4b1150b2aa591d2b796ecef853788c44ecca70737d42737f80881b1e4f101a929a31bdd03163ae4cea4f3c934ff0782fb58d61ee171c680a18c3c5f228bb7730b15b0554899503b8ae295f8f010532914d29f76da4cf81caeb112d7a699a6961c8cbaafa0b498d651dbe48b907f1a5446661219cd31b58b63c2c7042928a71cf7d77e7da41de7c65b220b493bf925a6489936c394c8f4143f0d860a265615f50deba550758e470bb841963cb857fe790ee863900ac21f38e6038bd0eb6adf957a29fef4991421d71ce793a68dbbc76ddd66137d9fedb6139532eae47307509a127080f33f6322e024ba1a31c0cf97c6631ad04e47bbca458cf8901faa582cad4cf0d6360dbeb581222d1815cea4830ad1fb5b33415bc1961ad9168fb0f3077fcd459317eda5aacea3a29de17e37e5149af178a2ece33d44d91ab3f5e83702fa18b491e96278d3a83558cd92cf91e64fdf9eb1ffdd5fe4d6717c66a8920aebeadd626d7c52e87b7e9f55063f1b3cb7f681b03fefb3b9b4a4f7484b6d7cd30dcf96514e981e3a15c2e88bcb86e2bc6de28fb1ae484da8806e72ee6a7e8eaeb236d02d8505bc0e0eb5f9a5e902d35a565af25686ffacbf78897acc03341348270b6e4f718570e298796680d95aaba5a8d0365bad0eb260ea41c7822e708518823643c1bb922d157c0518eb437013537f3928be5f6d11d7080ae9321fed8b3c7455b8a1147999bcbcd6f6a033294b24516b46b5b6d392c7c0dd6cb3ca304cac118b5f071ceb94aa52889f4a8efc46c008498827a9bf2208581ae3568e64ded763173b3a021b217cf0ede4c76c0d0664083f78f3f201cde1280fea2ffc2dfacd0baa19ab1eb0ab13ae6abd00741509738fcd89ab706af6569734e7a9b971f49947434cd3c5431ac463f7ead2cd2dec4e0aa58272aaa8ecad9fa6335f0d9ef4f787f058b98c795124f6849f4c1ef2383d7e7e42326845d55c1d407f19ba8e8fca3c1083645cd074561b28996790c7b327a85abd8618634c8e03fdc500a55b284ca442929e743b0d63b861da6e2682898481401360704c4b6fc8aaf71de39a148e757b166c001002150036247cd4e3598c2262603825af0272257a82348e50f860c55b7edca77e5b69f6b117fd0d6c27e1685d1cdc49adea3828408dcd88a3402ff71d7365464d718418bb22d5b0c4e3069bc4100dd3296886a16e626a276419520ca580814cbd39fabdb29d355ab366f20b03bea34d243f4655b9559627b4ecbe7951783e82e9ccfa806a5d34de3a409409b68b788b29105ba888f7c24ddfe6be0013b954a2e45dde2a12390524d88bfdff834af15259bb6a5349c2a1e36d7ca606cc58865b0e5f0aa4f72c3df01b651d0cb4c1311484d963596a7866e806a974275ba2e61d9f4a0faad179f6be897dbd62216a0edf22d51fddbeff79b8d78bb875b80720bff0182fbdb94abdf6df8430c33fe7a00b2eecd9989f522ac71ed13b966de526528467d13e98f1237e78986137cf09f30212c1f44790ece2368ccf303b3cc43a607f6338a03430461a43f2842469f28f09f26c6fdd89f89702faa5b754516ede657661529c8ff6bf928103c9faac64a938245f297264c91238a73e63da82f448a495874439de9e79c61f9486bf4f69b246ecd8b9ba63bcb3c6a4b8ea265fd718cdb398caf1c6f7e9d0da01c9b9309e11699c4998df2c0a2e7d1b4e351fc964e8247911ec713870d91ca33879bee81ce99f1d99d4ff73b8bf83c8ebf1d2bcb7dcc6be656506d51453436a5605216885fd1d96f6badc78be8302292ecb7fce9c129c19f632aa50e5d5198952fd543adba1c7098912e8c3f729872eb41b4ee0485370942db997729f0a6202cc6a170c2d7f6faa021e7cf903d1ecd493e73bc4a38b0c38638d25f97c32034414f08354991f3a73e025eaa878aeb43ac8fb1c58d91d959a7f288dc78c3f27e052a1f814d9d6e4571466beb777c2b25da3ff76add53f16b05455050eb47286314227370f6e1731b0cb95502adafa31cf6483992fab82166ecad2f7d992f3df4cb7591e073f558928eccaa401ba451d9d2448d98d43941eaffc0ffc2a2278259063587b741066fe3653b931b1108b8d826af9281e2c1f83ba46945b3439ea175fd8ab1f34ab189e13fdd08c68f0678b5e09018bc982060b540821bbc33d1b4549aedecfdb4ead6bb18e50f92882cb10cecf9067c37aa6c5f85594f29a70e68e824577a1e0c822f58ad24e70edb82b9fa4f257dd6f43752c36e8686fd4a43292c6fd30924e8c99621ac12543f1a96a95639f5da5420c9a38075b92a1bd386a0ef56a05587170c503543a2ccd0b6b36989141a35a3b63b0def12bc7de77d6bc215c9729014a13a1d781d6fe5506d5c7962e477fdbd9d945c05a20d396efbd6ffc8a9a6717a900db258f7c3819dc54e933f21106c996d3cfef258af07fccc0280ecbfd3bdcc5a7bf0da4f8a2a3319829e75ffea245525f505da8aa42116cc65c2f8fc2be84b2f7b05ce8ce8f2eaf15968edb0c1543847c22358e23df3d9ac5603df774fad84d56158b9ee55269a241e736f5cc5a4c490c54ad5cd806cc8e3b8feebd0a47e9172b2fae53ec7211ee09fde8412e4a37babc4c928e5895cb1af11cec396534eaacd215c0944b1d4d1a1a49687a0177ee4a5142f58a5ca4b409c138846951562dc13fa3d6782ef7961c053a0379e53622ad7230f2a1d0b9ea7dc8dbee48097ce9ed8ad03f8615519a0ef91f272ff1f6a5fbd3732a076a34372f5591bfb052b7703fc385db40ead4851739d556b41955123f7d5511ea6db04a51ea2e1de75fdbbb285a7f274ac264273455272fba9e2e70d7dc4e99bc9d4c3c1d8da68a505bfb19447366514a4836dca580c62c515433a6fc75116066ce526ee1167ac47a4a0e43b41da048f235d944c4bfe13f45e58d5b5e0e8701ea412b46ae382e826d960982e9bc1b98ac50d755f4359ce3c93544d18b6a48c6b55d53425c32e598ccde471b44524901c8635f54174e721be5bd58b298c114473c4f6aa7fee05524fc3b6c6c3a8988b02f15e634f3b86c3e3ac65dd1c20cf556965d5a36c3b4d1c1ce02b67fe7438c0ce256c0e450beaff653d4211aea5015dadbd8e86f33f46c60458f0dbed9c70bb02d8b799b020a769f4ccfd036cfee535368c6fd77ccd2002741044fbe876bc10c2f7cb9a86310045f5fa2ee6a91d67dae311984c8a9b21a7f655086ffc0afeeb439070ed1d2fcebb4f0ee78020196c96596f3793e0fd63f4bc91980f5293d163af79e5c08e2cb0594fcf5285930813981ed828810457bc897efc52e89190b39a9150125d5fb6c115f03a75fbb030e76f8d2b513f36514621ebf1744302fa1fd196b1b1ad12670f1b28707d5e2d70738d8c67cb9f7603125bb58c4e78c1d0af67052be3791ccddd9a7cd8822912775426b4c268868a0484eef0049a915ad446ac8e34b37f8d858fb0218efd7aebc727c1add3e919427d34afaa4bc163bcaef2f27479c33e47352c13a1f98c0b4923f5a4e7c09a893e50561bfd01079204c5d41de0580be017b893da8877c52efffbb7c6e1887e7f7341e5873049ae179737349c30143a63b9aa742b3d061d53cd6461507c785e80d14ae189a38752da53650d83c2c7c3a38d710e48f5f0c8208e120bf912b9af88df36bb21246da146a224408bd3caebc291a86e0d31e69f169259f5573e69f3c05e177c61a5fa63116485358c1eaf9f34cbcc2d413e745db718d46881d85196c4df6d7d0097451131399965d5d311eb3cf3a9963dfe4895bad52a335e546490834493524716cc2857051ffd6b30d09b78558377ff2019019d6fac8c67c328915e6699c9f0dd143acc58d763cbea170db51c3bb104a1c0264ec12f2f4bc90d7d22b9db50c958362dc5a8b4b873c9e3bb48d125b88be19d66307571c846eaf0df1851239a4f3084f66c7cd82236bfee9a1b0d2f79261d7650d534711875832727330da2f598f8f40587585551032042c3c82c27bd711df9cd153b260fedaca8d3231ee95a9f49d76d4a7a27259b55179210dd3809ddaf00aebcde6f66228ddadcd3a26337f123959fad0b4ce866bac015e61363c9780621e8524d81ecc4ebd362442cde4c0709966ca5fcdf9aa839dc673133acda5d5612462136c7ae174ab09896ec6284cb3438c092d3ea50d0e169a2a24949b819059e0d7d2a28d23a85b93cd0933eb7f92cc4fa1a37c322a1dd3fbe2a1ec2d5f2cf94a41d81f2d61099326ef9aed5d151d0495dad226c7cd546cae5d357777f5877245e3b97645877dd79f3a4d7944d3d4ff30145914c320649b98df6c10032336fb6e1f730e01aa0288ec7f5d8609d76285ab4a32c2127a456abbe4eda3f2942098cf3a814367fd23f0595acf663beb06f97607916a29ff748703fb285758f099bfcaef144ffdd8278fe137dbb0d18e095e9bd04a423583e1b3ba571919cf63d52f0047fb3d31e12812bb7a969f5c96f060270e19aeb0f8a44a21843f10c3be09722fef6b05123a286aee51d9bb4f556924b705b793d161975c69119cef9b98432f6c3447eb5f19cccf84a6d70e5667432099a462ee863c28c30c303d92e2cd67702d941ad067db167ca257b52d7800739681eeba4ed90270a82616bb5b93293e13d10f1c3942f2ec9b9e517040446e0b6d2900914575bd2429cc23261a8ef73e8795ee6f093a837fcdf9c4d7cccd2ced972600c8ec6c5fa9eb64ad0d476e5d28799e4edc1ef9c1c1b426cf4a7c0f7ddc639a9abff8dd98cc4a83d438a1c5adb15e71698bd0adac5511f17030aa524a55d7dc38e299561e2861eae95dba3760d95011c14ca1d721a5d916363e0df39abfde32e30baac8d87b6f0cde75ecda14f57610dcc742d6414eda98d926b3ae2c18b082b1dde1954958382c32464b2490386ccd9151c02e2c3c6bbef83ea4b39a6aa5e4288d6a8f9f89dacc0373bc0ce8c41aa6a57d1a10e9f230e6a161d652273fe280bd2b7634d1025867b01ad0335f83118fac7cf1eec97100db70b2a5684cac2953e5efa3eda19215c62e100020c029750bc4788b8a3be531f1388ddbb8410b077490f83fda99fe3e587b6a67caff6882a0b1ae29cecee439b2c13e57d36a3bc19e2898e10a5d08527dc0702d5972dbf51856eacca4bfe648ef8561a5f631e51e42a8db508b178667ce2d4105d88d20a27e9e3bffdad5a1da53920cdc5964336f62089aef20f0516bb5b9f11450380c781c78969df02daccc44c42366d87784e5dc3dcf42600a7c81c9cdca02cb85748473f0eb57c8d358a88f0cd949904265b0c45906c5dfa7540e2a9d2d52e4f2a19c3a38be1f2f57a7d3ede9757a76ba1d8f8eaed3bbdbedf674745c5e9da4dbe17ae67a88b770b1ccf98d45fb3651f6025c11d014ddd0e0051cacbfbcccdfaaff8cc9f47f5b9fe9a277d3adf9beb1870a132eba8cd158c92de61ba77d5d2d91fff3762e3658c9eb6a8af9d7fbb9c50a4aadfc3a0dd1a3428b6c849a39eb3b5eb8a1f257cdf831bbc0bfd7adb5f86b1c27e7c056ac896a39296c9ecf875d471778b0b09b44e6889ce43224f41bc46914001ee3ba515c3fb9d5842493eb93180c3fde8b3eb0f32b610ee2a4f96f833f9a5b1176ba3b1412c074c31569b99def6a194dee98d7d3adccac9988f1fb9ce728829829d6b19c5e792a10e69b13b3b33467ff45dfeb7c0662db9a07f9aebf8013a154370210d8bd21e909b471525f5e1ae22cfc7c24436bbc33e2d331b2ac800a6ef0694f9e542651c621f5a055541495d0da8ae04f55d2d76983b02da64210647b91585af6bbe6ae696dca8aae1bf930c9da9b679fb1c7d83f68b86d2eebc82a2e242959267ae51da4b6363a059c933c50581a9ed78beb4ea32b12e2400b3ac2f6f12673d063aa8d320f6973c12f5ce43a5ee7e4c430e017ed5e2410ab1060fe3c2eb897ec89e624de4c8a76da432aec705c7b1aa21180f4329fbbd51cd1577eff9ff2447aff6e50691f07cb7e0726934dd892e4d714db6b4ca3b615cf2ca632020a08f24274f23956aaa80d07acb7e94ee852f3190a46ba498302d8fc580db55b7bae44cbdae6cbecfed7716bafe5db4535792cb6802104503157084c463a0bc6d178a6dbb23bb7370ca424e1f214afd4211ea400574e9a7ac420cdd03d2abb92c6d4995668e5b93ae7998bb534fa1ae7221eef377493ec238e39f2589cb3b591317f6071aa8486e55072e08607536c3b506fb1a2c83aab4a91ca3d6d49725815799aa5171118415fd51f8866d202b873fd70468db388e71b9cd846442839ef168a522768590606c97ac4120ba0d435ed1ee0b87e747fdb2f91ddf23d03eabf82d2653de9c740e9a63d928e4ad3d9264daa0eb1d495e0f827e855898497ace9633a85d69079e42a44b23933b072b7f812014ca933d7d85a8d98386c43563f0c9632c590de13fd5524aa706f20d23d95ef83c7a34b13b164f59e9cdc8f20022e2078e5dc5f696c75023ebf3068168551fa94a3e28ffb1c60653d9a6839cd919ebaf8933c4c451e371c2868dba6d57c386c3299fb10a762716006a0531918cdc26bc9d4d6c26cf9b141927ebeedd5f791d89fcc24ed5a1a8bf3d74651d969c6896a00a602a33c3a3843088544857a5d179f5e359092532eb0e6c631fe3572eea0be8282f0c00d3d413b1a3b31d60bec166d293c9d0fc13c248ed7c6c56a6904b6922f16ecf48f8653a3005a168ae0937c4461761807446229e334a30e31778c7dbf7a20dd0246fbd7d1a32f0e355e9c41151f55ad8e20a49eff4215eb3584e876ed44d8d49c63b17f6aeafc5da22074c416664b7b5d83c9b10aac8301155c46742bc44c4747c952227516fb3a6b1566d399ad1894f11dea45fe14a205af81dc8ec522359ab82bbce85fbb351653d982f30fbab79151d60220e655d691dad884f2640110d08d3103bfe08ad3bce7747d275c62246b6dbb031ee8a560542cbc9c7c55ef88192f62d80bd6190ef1bcb8c4a91490f46ba1e45298e2bcb3729f05f12c65ab15e83d6cf72650b54c804bf5ecff1f43e1d293a5faa3d33bc6536f1bd5a828f0782e050b9e9774605b71ed2190fe0da6f4396b415183351d77e881049d2bc95d856200fc52faac2623913f1b32463aaa23d1493463413f121d094843e1e4bb4cc89ec6ff75ce7d7941787b4de8b5ada2efcbcb98d833474957303c09ac9e6669f4cacca400881e392cc9475a0c7a583f989c1bb6c5e17fa8403f878ecfd4ac3b876a08319a2ed56a4b5f427c102f8f4c0fc9537a637807a68e7f3b15f93287fc74ddbc67eaf7dfd39b650c4f80c1b3974fc92afa91b2ccb83725f80aa6555d9d94fd260fab237dde1bd271a2743c6b7eb4c06048dbf43e16c5441436786066e8a66e650fda9d16948742c40e965fea3746cda4f218796e4622f59b4238e21182ae1d0e2979617aacfc97e55d441b0434c237df2b37d58c8906e2537dbd7fc3e701a3bb4047ea620b2669fdeb79d40ffd39669044081bdaac9777c5cb525d7afa3d9316bef810f34bbdc84da75d5c52b4524cc6d3c784da427381aa5a965a7faabc1b979d4f682d4477ea5d4830cebf8e1bfc30eb6411fc4a7c1f81dd4f16dfb6f216ebd900c0410c95e1629bdf67be21473e452d4ed7193b71cf4c9b90e0bc4b97c1815be27ed1b8800658fc212c71aae323df3c7f3e0c294d67e0c9330a679e4f3cc87502b4176f7e294bef5560e870eea87d9d004d04fa3c286ccfa9f261cf7e88ca63f6aae0599710d05e7ac162461b05b453e02ba140bdd80762c950281ec3111edd56b5edb6ab111363675fb2df39c10449c88ee2d95883fec214ec65af90817335a2b85de55da48d696a9dab1683956c7beb440b1ef652ccb64b0fd5da737c5644c52549321825bf448a00c3814e03f04c585abb95e73a751f5f13874c948d51d51e47f6c0ed33c9cd2cc1bd6bc0adbd2e9abd7b82600c3c5aedef34d416429651e32c9eed01aba84bf9fbe168c2c32ec38c3d0bbb8eb3c00d5ecbf06f8a8b3bc92f705c7ce71ccf5c74cb967e4abdd7850ce50052a374ac984823f135be3db53364a5dc727dde92485837e2cb2c17f6bfbd98c10bcb115b52367a306f7946d63152c9d4fd886850e73c54d53246ab818e41fb9d046a2ad9e94228b8c5169add6052f32c552cd5a19b5332300afd0d26951423085f99da262b78235c4a46ae09a44a639a8ea77f2e0a41c99c88c93bc2ad16f935e0c41db9b6088833152673cacb423aa65835beb2648405c725fbcaf3e01e6cedf4a09cd541079803c6f84ddf4ed43ac1156b5a8910958c357bada287c96d7f8caae629becb80712a9d1595a775cc22951d71ebd705c64c81ac6168f0a7c9948ad4040b5eb2af1bec614e08d2da6f89ba6c9a268d2896a6075a0164253802d08abf80849bbd7a98463ccc27f61738a0a1de2b8efdccc939b65552ef343798c1496d19711d80ef5dc33485a898e854c9db82d05e2621f7332d81198c15964d31b82b4b2be7e010231308efc9606fe128c3156ac85d6cd31bf2ebb0ad71ccb27d2675eb1123aa10863d793f9fd1addbf72862ac1adf6ea4c187c4e50c66ea8a960e78130759b449baae3064c98a80b7e8d5d72520dcc6f85cb27af4108b243d21eb2ccf308d745d971615ab510e6db90b7f3e1d3c3a246da7c8534ca979582ff3c1732226d2913b2078f3d7d17a298027121583140d08c722f132451d8f8f103810dab7c4cb6b0abf534f062b24756eb5c8feb14ffde0d571c430ca6bc89d7ccb2ec9d4a035c36400448592d3a170bda0f008ee6d62b129d798964c846666444501419db18179d6f36d46f207f001f19fc6e006215e64270e0992b42cc94578cd05e70d874b36fb7f062a4476ffd15d1e4989b1d120232c2b919e1f9eb4b3e84be7393eb06f8e3b1d753f6f900f8af5218ca8437463d9c8b12fcb88abd5d6d65fd0c183591d23159bef4aa4822aa12700ffe4167142148ebcd8f7338462d8d6ea4ee19efa3232330583402b324897837baa2a1abfad2fc4dc98822b8fc7c7724715d6234a0c905881410d8e9d7d21e6b1a26c6e4e04929f4acc06e997d08749cd05be190fbe629b5b777b2c9fea051a64241242266ce83faf40a0a1ef7ec7dfc51b139529001a56d139d9f64dde6a745f0e400f304f65e4487343d9a437dd89c2ec65c03b38072d08a2e309d0567809aa85dd2ad30398b9b5518f1ec5c455484344567c808517585a8608353746c45fc448cda07e8604af121dc2bd73ce7fc660e8a8e61981af72a084ab96430c74a3c7257c6af078f8e1725ba905ed3a84f1706c56e93e683a58e0248f3935a99fc62f29649b1d2ed09bf3d6e53fd191c1a2d0849a868f39c260f2d8d45ae36b4a4a1b92096affddfccb2d077e3f27ab2b596e5ae0eab43374e4b9103d1fe14b0a125736338d80d38776682b5226ab3683d51631427a9b3bcafa703eaf9c2eda2f4ad80dcd0b43519234ebed7b08684e7c5426c658805d0577c30de9342c3fbc85f10519405dbb5c885fac3a5142f24a26ab105348b5c9650401fdaf9a97286b6209c9696558f6f2050ea08f6e17bb0dd4ee4a1f8a99b9fefd21c5175565e284979f5c4b0cecff2ae9456d962a77c0bbe6b0d2a868dc0514543214be23f3eb3f597dad0339d6daa2f256ae33962838bcc18cc2ad1aa778a4c833642f55d5eca21fcf10de53501d2382644334586de28bae1999c99a3b53299f956b0f32fb263458b1d698b64cb74401f4414f6f85cf5033e953f2e67d20c0fcbf43d73a8df351a3d17c65a7b2273e0fed4928f97b0b9b95b7fcb1dd947a3c7e099ca9ff48a58d8e665527a45d982aac9fd14074b58c75d8f54938ec11118a9cf3641af41a9027a86fc4826bcb59427a00b8caa58faba199e5fc813f7cf30d62f99493d84358db63d901c0bd8d3a53aff0bde3012d4a1f92ed116e96e22632afd90373452623102c580d280e6d2157063122fa5f6d471414e220f24ce87dcec6992c11f12d22a3f58b9aa58469d4438a56ae104872a835b4aad2e1e8f30b2d5a70abd35625ed4563724d03851de2a94461bf6d7ca2258494be090239b4c94f8f44619939e9e518da215c236c39da7e596dda52aa240f1fc7f783a02040ed56deab7d9ccf53779b2bb0dd65d06a4e9ab2c552e02d2f3da1811014d32d3936d9f6409b5a6d21c02254e762b83e9cfa9034ba31daf8e35edcd909d0cdc295cb693a783c1fc986b2a7593ce4f893c770bea4a92adf201ad764f09384206e7bbd600afbf024f64bbb8eb1543de951de764210e962570ee9e34f22cd25957c56c11f36610c9f4b464c67a6fbd93a1a6097838ba637934159d6251f26eb739d2aad1985d3e65103325d321bf82385c4b0694616304009794f07b764c6b593fbc02eb272a711ca79bbfb7be2679a12d8ff48b7d468899ee32c21c88b56850b2b2b62f4ba7426b6881c932f86fe0658c4f8ed31f342896d0663e30870f2c719221734b42aa2361ec13892b360dfb451fa02b5941ca95090e46230506333c182db8a0396e1b43684b8053ea9c429233449d086379aef2d840dbcdb65d6a3201bdef6893ce965a720746c2692aad28b3fd409c183d784747f024b2bec7c239c839c64620f836e19310b0154a0f332e5db8b0050aca494846e3dd472b5091f6eac65512b27648e3a836165c4db4de7297845b4c406181ac8543c127b5ffd69c9c8c5089bc3e6c06df0624b85c4d925f4714903dbdc811f78d7380bc1abbd61151c6b016d8652954758b3894f68751964ee9fce20c102baf08dccd0d584e66bebad4a8d452ce7f95e7987827698303192aab29e7f7f6ebaa2f51275a141d0ae3529bb048517ff64e56481fb982ac8d0f63006d9550b3d14cc9e24aafc8813b1e9e7530b838fbf1f5b6cf4faace9d08545dd92ed812890c57a6ce6001c76eb862a2be2f3d0fdb4d8ce9492d5a2204536ada10c552bce33ca9f3479248a747908d36c46466792951dd4d1d9e2d00b31be2f15e2ba2853cd634d56d9e9b34d0e46f726941b4b2fe0e2a2350bdd8d59d5569780cc776b02515253e1ed000b2919b757c6e1f326b07180895570583467f0077e52def6c844d18ed3688f3a8bee705e13e9ecc5d55796c31ef424a5b153e7fc16c4250b340aaef1d207e27b2b0c7e29a1dba9ba5ae960db93e0b7659279b93ef09549bd84a6f06481de1e5abc009659cff32a0a140cc1751713f7420126ba9cbaba84be9850372d2233a64aeb5600c35ef658b7429f9c0d817b432266836e6189987907896cc13512254e1f84e3eee281d13fbebf8437b800368355f8ee469193554a97bc15cb1d0886ebeaf574a97609ce966d79a91281d3e830a8b8e72ea30c84ad028c66935aa832e40aba3ba6afb262ab0011cf00e3d4daaa7621572049009dfdfa4d35a2cbd4a82b68db0d0291e55e70ceea87c800ddb33744d71a40297e80b12506cc43f72f6cf6cac3e36fa3af16f43ce1e54dbae371c4870028ea17c295d2d85c8bd9f78a8d7bb721b9f70392d89f64cf5353779c67fdf6a382a566c8254c31995bad2426f557c6c270f98abc786f93526a4f9aca9abf6ad6c00d8831a93451b3a64b5b321be5e88f140c3f515b6a80f15b8cb3cc1592c699a2d6ff7a6e91eed843cf7f4422c12ebaa7c0e55876b0237c9db22531159feaecc3e7f4ecf8523e568a0cc4494c33cac812a5137dc98d5e1d3f1c042e22806f4e6bfb1a2eb0f72ab8556c95049a638b152603a2dd87d62399773d756e384ea9b685bb29ce04338b5dea4d445d780dc74f85bda220ea8ad63df1f9f45eaec921811dd8447240397790fca4b4ec5979b73a76ecff485b09edbce769b280223cb1540963af4e9fa0af46c00245e15eb4642de63f602fb75e131f086bdd55d691b21a88f9d165442b9ed0bc26355c708c8d0415f6944111f491dd48f4dab2f3b3db79725123029b78f94fc1d98a44dd6513c3314eed42c855c7391b082c6af88b5d60e3a71a16ee00ed8df8201db0053294c3f5a8762220545354c7edd46bb486577b0d08047a8db15da2475941c8c2da03aa2bb83da8060b8208062528a9f823fa157e87e9c0ccd0fc7118a2be30a96150b9fe5aac3c87f170a1451677bf01a2ff86aac804d776db5ec6b8d296833ef4edbd56507fe05c97455c710266c527df93d147e0a99c3861f5692fd36ccf904786654edc5271f1e552bc9f7971495859afbfa7b67135300419f51eaa266e0e464b44036f04bb1edf07606c329f7680440491c5db000c73ba3641f3e3e52f0e593b50a7905f84f995909301b8222eebc8a8e90b2adf994e71c2b9064004e4a2af4371f2adf81a6d700a3521af7e4c7a84fac3dc96036350fd67edbaa0f4e0ad4f287ec1bd13c101c228bf0ede0cb2484f15891a635d32e8cd6194aa74bebf2243b477c930b03e133a10a73c04beca29e25860ca02071c7a0335ee43d1d56f42f10ebda2714479c7b0702de6d13bee0567090cac8a2bbd69b94ff9ef36b9bd545097f4da771902b0411dba852efb15ed8fd4ae4db14355a00ef61c3739d991091e401f8b3d54248db45c20863a60d4022aab6d3b825f61a39105a26f2f30cb11fbf42bb238e57036b544881df3dae11adfd564a256cbb8e3448ee02f7e0c462df72514df0e6324b7bb8e71e057034d295a14524c460b791ed2f9e572a34bf9cf1059d466526f9fe48cab4f8c040345d2120c9e14169884216337e577c91bcb2d885a871428368a72b034a55a9b81874457d951b588bd9b7a2d98e18af17df9dd60da01187e52e0274576ac8ed790cb7e169852cd5dd18150ea08e10a15159da31ebfbdfa39ed7dd4efcd226b6b6891763858e320f4ed6aa56b96ce6e4db87c0b74110d0739a59edb5f43a60015ba63d9b85dcdc9c227dd9e830567b80de0ef042480e9ba615a988a6ebfa05b06a63648c5a88be3bc79218fddbd0466faff8d903bdd4e083b9c6c2800b43db7f6dd26ef5d54904c20214984d2670e06a76d5c7f2f7e8ce90e6522e3f4ebd273a6c07433f9117fae0355b3c85fa5fc300cb1a7f00622c3a522f356e11db2d9b06c5b019553e51b9d5964a20128aa2a87cb0ac733cb1faeec8012983f623426b75b94e29ccde30585fe193f98436a2512bd0be3ce79f8d78d1cc06abb233e7de1ea8ae1c059d9013ddc80f9a9c01b9b7de222a1f84c901d74ef1d60901362f5c52906ea94982d8691a29efbc4b167e6dbceba5d7f399c6cb65ad675fe2396ae78b808af3d09af840d374ba2bb5733e0191365143d5b9b11e47b1f1f73e98937be641dc43d88d844fb72bfab55e26195a04c4668c1606225c166a87ff2d7e4b435cc585a13adae99fced7192339790f3107c6813b699e9c6649c7fe951e73509af3284ef21ecb835fcf318a849ebcc6be0eab179f4e324cbfe6277c6efb2d73db7f35852bb260e13cb9603bc07d8575dc067148238fa79495de698c1817fda33a993c7fcecca4a8d517ff1d12e2921418c386f5510c49f70881f43bf02b3e248eb012d1ee27f2e759f43262ac8acd19d6871264481d01fa55056a42d3e95251491a9a573b3766f7d234664c9f2173c6cbe0ac7ee8cdedba40ead1b29bde4cadd809a3a1b83207e8e7b20fe0568c834a18363cd3738ba94b48f9cb53eadc62e111eb476843ed83dbfb68e37f6227306c750031f15b6481fe0c09a17754c0bd5d9669c6c7d3729f7e68559148a88ddc5054e9afcad4565d1e02a3fb259a644c1933d8ddb2509c59c4aa2c1979fda70edcadbff79f1f666ef310c760bcaf7447bc3b13c982176c1c15552439d01f0f3688495a7b87482c4c57224a54f2d2cf55f28e682265b678057943d5d2d9f2361508079977c1d2ff34aef44a4041737dcf0c7314779ed3bc086036c3c21613f718782fbe7fc40c36b17bf93f90686dd08f930c5ae5309f24a19f1955550d48e2496db80af7f414a0f1d013d08f802c82430a6126523b2cf437238609b4762782ea1153cc37b7022680fd18f96dfc1645f8c7b1c22b8ddd769ce7b56111bda24be2902b264c9da1fbe04a30beb8db4cb65691c1fe6c514838f01fcdc3e31a0f1ffe6e1acf045b9e97363f55b8eb1906b9ce8d065443ad83eea908303ae980bc7af2157430874994cffa18f4d8e8263bf18d07326948a3cff250f37c2f6e49dee9171f2a045019438b38c2446895696ac404f34a909fdc4b3682ba32c04a7528072f896c1836320b0b1b487a7a94eda430edadad3a015d31549f7037dcf816ad42be4b2d35dc1ffbb529d9bc03f0bb8679c8630c4ed78f32097c7857d9e3cf7d03d8309a18e3d24f7ffd0fb10d4ba3a569f938723d49bbe373f25ea8ce22edbd9aec5bdc737d2af6dca96a03d048f54000ac930ff09907f70e8a80253fadb6c1098ba2c95ed479409d015e3b7fd2d0a42789f78659d8cf27c8f3f9cf1e7f223132d7c3c2437bf5cd62504fb00ca8467271667b64273ab3ad1319ea948089b7b5ead9ec11cef63e1d690a0740b16ef7302aaffc8116b106d5ee46a5e59b7acc0dd5cefeecf3fbf018cb7f19c819e98713bf400d356e92f697a404d2331d74ac20ce8583acb813da9f480ae9a83bef2d6632d24d98bfdb1907d4334640b0c019fef216b2f2cc1437d01320f38e89a44b995a50d77a0936f340cec80490392ef3a9ae353a940f0d5853b1538bc1b8a0576abc1c50d607c0aad7b22697700dd88aabfde5a1f8877cef45fa00f8ece16fa084a750f1e240ff17045ce8a87d91d50ef328c413fad3300dced91db778a0cdd8a6fdab9fcc45c19be21bd70fdb9b7c3b3290789da3fe08a6b88afac6c0d06628f0e374f71da53e74a6e6895a1d085685b5f896c3f1a75c49605b270f9d5114e399491eb09bd415280f71dfe23aa91b3c09ddf1b6f7542149625de22e68ef8c2bd0c5ea7df1348131bb9f06f39a64313be3ac6b132bcc588771b92484838a7e79c277a37f1349ed9165ccf0f4908656710df7261f932b45ae1ab033e10a0c7d0f931f57876475f2bbb06b57d9f538e57ba75831d1fa2efe45bcc84d09fb0302ea45760da2afb9869c24065f0205dfea9095e5d64658d9fd1c347ce123067ea1a444901ebf52c2f90e9218ded968884b48a423b753fadc1ef70aba937820ed2f5aac1020fef4cfbfa6fce70c7a5dc42c4e5924cbbf1f880d71440708a6e7cd8840c0d6e2ff8eaa5266ef83acac024f73e946df49a6dd289561213e77bd470e08b9e1e6477e57bf2c5a65805702889e8924c0357f25bf79b8ae310be629d4b9a81c083a3fd2740aa7a4277423f80c3733456d1133c92f2d35d11274cea5bda7fb5a95990cc45db2d83d67f1025bef4ddde8e2b0124ccb1967036ac89e9d7fc85131af2a748c4aef62b6859afe10429c3b74857f36af746476b3540748b2ff76bf9ea330e85fbbf1d606086a1bc7e188595b0e704dfcef8a2a49ce1981410dea718d06c74c214074283bcfd21c1f5c2276c552bf1d0acba2efd51103cb82ce19462d50fed6d5021bb811a3ad87dd956919552869c3e1bd55f20e814c801970b4f569ca91d41b4b23c1118c4ceb1801021b6bb7317fd4633d279a6f4dbef98c52cfb1ba259c32ceff5afa6c74b01690207550f0057e544dadff05eebdce89853e0fc01f633ca02071913741cc8623bdec59d37db0ee4fe46ba84be18d500f2cf623aecd57a4a64c2a1f943d0713832f7c307308b0c12648f15b35c5260fbc81b60b86342e377318f97b1436c344a816599e34d05f214fd9842e1a4033e46f182d42c0e68503e3cd5c2b5230046f0ce1a7d8a0f2f21609c3a5552c056420b0d809fb850d7108466b7685ef6e27a0e59ca79701c3bf0691c8565ece5e1d0718bee6a612201206c70718f65760c3130ff676200457d3ee9bbc47432c22da5a02fa24713857a63260d0a3c3a5c02de08a48f269dcc917e888698f2db1d9c3645b795199069296064e8e76122032fcad193abfb9f49f833d277746889911d15b7b6a25838dcc524b716b9bca17d79428fe4b2b40a1efbf5f9944d5fd161e2df0107bc75aa75ac2c935152b963fd2f0eb520bd0bfdc62641d066053d7dd63a7a465e92c5e7decb50c4b1dafd775497c8908e5532469c712da82f962a6a5793e473602b4950065b32564e7220e8ea8ec4d13f4e17041a787feafd2d364d27596b364e2c3926c914eabcc1315ca10e17c65231aad71201d1aa20ba2bb3476fc4b21fc5adcd566fcf7d66171627752ef3f2b5488933276a775b448faf4c6b13037c7c47030197da231177d0e785f8381f06df824a89d53a74cdd86c315f71b4b6726ba43b53840f7806d572960ed0eac17358f32daf574a838fdb77a8972206d56917226e2ce1e2d95f76783d160b0c7513e528fc00334c2483f23a070dbcb2cf47a1e2fec9542fbfd3d6f2b4701a19ae421ceb606fc77004f610105f074d2d9acea1c296a20381d626fc1e087223a0de5ecc57c56002faf3422d01b340263880e65f88526676385b3e42119da4e6c8717707ae7eb6764b05d15c429d24a07a02d8f3870b0fbce71a69488fecbdf468d95adc00436d0f9e08584aa66b2fc8a0f52343063dc57ed4f00274ecb2f1ae2bc651b3572e62330ebb1758801569c6d65b0b56107a74393a8c7f09a2e4561dc21781ea220eeca049e57c18e7bb1a803437960570605c066fb38c2b111881fb84e9865c7f8fddd54ff5c9fc72cc10737202b23f912c81e43f31526171a550d6cd1fbea3685256f92a5365fa71788e2d25e5d40f15df147b79fccf047cbeaa5ae4bb9a23f9b0ccbe2552ebd51520439f09807638fac5ba4c2ca3e2a2399b07442a21f64a9bf3387c9b7cecfd3472b302d0242e4164c6bfc68025bd0cd689fb7c0608c25c853966b8b8bd275f65037a11805e44b2c8d19e5e37fef84d6f55430cb6a63a56847019031665a98e0e95209e65a3b740e871f1f1ff83ab6aaa3d52fade934d4a8a0f348f50a90af3b6824ba8068fba4d38fe0637f69c63b1b0b5233826dadb23e419128a97900027cf044395b5ca1162da9b5b2a520188183f886aecaf70266c296283a9912776bcf3dbac2c53baa7f5f1300247d7fdebaaf78aa70aa7dddb443ae3558db1939d4431703d6279b485d718b598193cfd1e44f4e1d78eac3abd28c12f246632e7f58645fb7c81fbde98c5a65c43088a0680607e01dba485200d197b1c28b369d4b9af3948721d69cccf6cf9cdf5abf2e4374791eb4ef6352804c4b2b70c68c41d1a36fe8125675c11351995c2b39a462e0987eade52aa150653b208bbeda6c16a5d3ba87a88d45589c452598b80cd54816f3644a9afb5dd3621ea7b52b619427d8613beaaf2ed45c30965d5a70ad9b5782965550cc3de25b484058b3d0693f6e40f8dab0a7b650a59001acc03bd7d77cf0551cadcb4cd397b87656ee3812c62d8d615ba60bcf65071ec6933fab42ae012d1d9d0dcd35a65bfc04ebb3105c82ec75bbd1d58d5a3fcd1651a57702705d4b9f11dbe1d9fa50da3e5c460450a1306ba1a4e7c049687941fe9682bf5d1244df28119d02259341d82efd406d843e3b3c05039d8cc95f5ee54b43cd0200baab138b2c391695da1a27023258a089d83a9809e59cc2483d56b234bf8ec2a0af3118ccf32ed1853cc29234943fa45821e3b0269f88a8f9bba5de11e2e43af4e4aa85865bb57b7333e6562254a92b3108fa30192c78b83eb9c56f2bf8d794001a678a835d74f8f2960b79be7da4eeb162819a4773572efe2a3fc72f2a89b8aacec2f783568706ca71541294e0bb27ab18448d48fa9902757b5fc3078b65fe7d940e1392388520325baeb8c870dfcba36d3b38c4625553b859f54a4b231c55295ba603a96c1ad68805a252e3999c1e43dd5618a8719f8c1c270038d11c8834c09c8a7bfc262218125e40122caecea42f1f20d95e7218f28a7ebf2814b193903e4e5054c8758b49d0c603aa69b4b79533444901b841b4784b60b79e8a73b520aa4b91c842484f39e9558aef12888f1d52d58413b84046a86593ec73ca4ed71f887c7d4a738840d39535da02a68f6259b7fb17ef154e250505fe2f5980468482c27e6489d98cb26fa19b3763f1a69c4949ebc20e077e4b2b9f8956e1bf478f091ecaa51200c8b07e87e0e8451f7ec033960072bbc9c625db2d495334b4a2fed68e8489bffa73852887c517603cd9005f9a79236ae1622672eab6deec489ed88dd317cced4fd16778cbe0dedff2cc1bb94c6b33e7819dd20c16e0c0c4c74358877cda1906104e93164ec52ddb23b14e42a23d073d949120410badf6b9c1651254a5ae121df17af12ea1af662d91881232508b3d309c0da7e448b8e49eb815a3a98f953a3e53788c8c2b4ea4e1fadc425907f7e714f0e586f7d9c9621fd5265f29a2a74a74194ff15ca3e0ad4695a4f7816788a172f8d6487089359b89edb164059d90d6556021abea0a5aa609d0888b882562ea163ff8935110365d389146be2032c5ef07a5c3512a229c35282bed36939b13755d4a22b52518ab0687572a58a8e0a8a56dfb287b7474f63b274109c11c3988d83972aa214038693f5ca2df5daef8648ca6d6a848d6b1428b080cc81339f2a08283a718e8b7d166c860d96eda39ff78532c1fa0002038e437a3bd35e647b6cc28fe70013c7ecd617a8eefb8a359cde80634a2efa9ce391d17d14b6972a0c8783a7fcb1c5c65eaecb4ff8b959398fa7bb297518dbb61a494039e08e9276f2107c78843560ca99021723c2e882b6d8d264302dbd58ca7b8543c0b1fa49f43df35096d5cf0a95e4f256c98387508e5f6daf864e985c717e5f25aeba1d4f8e722764a8463b3c489160f0c09f27a7a3fafaa841a233db445478ee23bb5aac87cb27c7746ce9030a8a7a49ecc911434022d02082c738b1cbacb50ef5b9568c40847b50c62018ab2ea3b46bcf59945631c48069d1825e43c854cc60eee4940cfdb8a9a660e65bcb3aed65c0e67986b087c5a235933fb10eef8fd1b5de8e1dad96ce2f132827f5cefe49fcce0e79aa85526e2145c5af9297d8b9d11cf9f64f06a0eb83cb7b5ad88631c17aa00a5549325ceb970fa6d02b847414daba9686825d57a9a86d20399dc2be25fdc1addfe30b968b7c021531ddebe64e14c7acc3242f855230c8b77590eb2fdbe7c8c00be043f96f1ca608f929a2adbfd044faf3ea3bae707085694ed975fb67eacb327366f979f300de01b1ec8a894584695d997a1d076a99dcf744d3d60c0891f5ae76b23c222a044e0e6bc5f7c14c24028775e4d12472ec2825d569c9aadd44d19363e5c1dfd1f49b33be2970ef43992d223fbb141d72dd7e0840a3281d09e46ed7751a8ad417a8348a6dbb72aaefb8c11240ac2021be18e367976effd85b3abfd9240065eed22927b4890d375c83be5db2b7106a0dfcdc47b4441d958cd9f32c8e495440815dec6934f4cc84b20dbe29d8be624acfb8039edac12619fe33a25d7d9f9819d69c9244377869fea116294bf042f7d4ab41d948b5d7d040c5e5f2a4db4252126b960ac82899a3469e6543b7ac5c101835dea5d9f6d8c98312da3d018e8b1657f1f8dcce85085b1d31de92ade7bff9bb236c4c2d13328a3ccaa386aa84888dc89bf065439d348f61e21f3a151ceeb4984b933b4faa3f2dbff398634944ba702d8a751c16a9efed01150ff25b426fce10472d3ffb575adfd21e8eb3c72fc90d56fd26c2db7bc5a32501b9e235c9f7350b1ad4993d6f7ee9078747cc5499fd5a7245627f5c999ed27279fa59f1c5040c0c6c2a560d303a10f19ddc7c2a939f71646b5a15a2bdd9b232479497ff32ac4884cdc444b8150b46f802dbd50ae00479acb0998f0bb85b5c849bce75adf608bf5b8fb9487ed10923a002e83c868cd05c61a8e3b6305bba9e5518e2e65e58e0ef3c81b726586f28e4fc3a41145a2b94892125c0b26a82cca409d208497264ee1be3b5c76567d84910369879ef9c440d0b0f6ac581a341bfa5814fd229d6c4cdfae933b85b8d2b9d9b6dd92d37890c80f303073e29d2fe31681a85e30aa0193895c299f9c00e87245720c8a94cd2105c4ffd8f3ae22e49fb45fd7f5177a3ed203fca1e8c5bbc7c51ac5123a72432acfc28a30cc50fe92b6f4d28186f6e731e2e9e5f347b1d39eb00006032949d51d642c0b4bd5139a59c49c7a000b98a5f37018c5ad74fc4444592b07040390dd78c9d1894117e614850778b6a86c32d4305010e6bca6f9a31dd36af830cc258bd20cb169ee06a2a78f23e124544e8807d96f02bca1fd26e0bfb518f547e9ea5b4358ab594568b1fe7f7eaa25fc5e372793cd07b61d3858cb443f3f6f9406721610804c7428fc699ac9918d9b73e1f7e3160ea75404d5b069f4a8a3788e54264e7a497071c05c5c473b40e7925ac888e933df961b12a370cbc7606d3410d7bc5539e4e577c5bf0b878d545e82a743efdbe283e2e4d799af3e9779f2716580540551ae08222278e6f43467ecdf74ddc892d6e32efc5a6fc349cbb4282744fc724548c5c8624cbe3ffd555a4a3084fea3f72e7fb8e5942f65084e0a0aa93b1f2b2430ca1cd5d6ef1e1cf869fe0310bb6b32809f21c3f482c22909ce23e51542e755ca789bcba2c7a5a50e41c6cfe5ad42c9347cfc4ad4fe2e0c2b17629325d9e3a994096d751f9978df0bcf397630f4c57547935d88d4613a07896e9091d3582d0dc50db5166e72f111110d0c973122b827059ffd7559e4314be226a212f1dc60085ca0e010bfe75a27a2673be913be3ea78e83db81a6f338d86f57f1feb70af7e4dbeafc081590a0329d52343fadf740dfacb2648af2ff2dea08e2a44a4466dadf524b651835848ead35c65220e9eea7a09fa8ac148bade6f785bb862b7cc8b04d0a3d8605531d186889ce4dc15fd68e3dc6cd72a06402b3c663821349c3d1b3e415e326774572a27452f890a0aa4e373fbd86eab20ba87a3bf1739f693de235c2ad189d185df4f2a0c1c31f9187f9fbd224f87a69451a1ea87d31e596bf02ea4bff672a4711e41965b33745d4ae16d93346cc83831d39391f146d3e4f9c93686fa828232d0105a14ab1bcea1811309cbdaa2b9ac75a92ffab73d3ed8db91827b9d093a472b1afa6229c0fc18e154347103678ed10b786cf430d98b06e9facf8b6157c63e2327913ec4842f7e899b2d59ee75371151697fc357a74fb2249298d1fa38bbf45d2b336c4a606896e54d854c074282471c042484148386ecdb801fef9da4cdb138a00729991a6c874139383cb1113201b84b0a65d2baa6d13d5cb743f48b873f61d0d2e526b778891b92d957db4303bf49bb649d88ee1b2ccc99dbcc9fec3fa00240b12b66124d2c1ff5dab205f49cd4db3c2d471be1e02163436fea193d18a58117f7e447847e848b6ffa34b791b770094a9c1ab4178aabcc6b5b34b96d7a19b9977e44ab8648150ff818404bafaa7e85f4df903df3f050e069e9e863d49427d67291c855821d4b6d8cce8de411d4d3caf3fc51e901f706d04ab045bcb92ce20737efa6dcf16803ea77898633b5f812292208d0a16964db6fc8ac903240c0a3ded660e7e3651e2f5b33b14af9e351b8e1d2032e25cb6d7cdb41bfe1f0c6bfc31a3b14de2b3f335f789855439cff453f24934c1173d3b9f67809f4d3406ed4ce9415828eb3a7338d9193eaa87557298c688deb132e0c10abbc19f29ff9448ea8b9a9d3f1b96d1c0df50861a4259c267e1f3852a2b4f30305d52f7fe4f0f4ee9e85e2133d5a1025f16ac6937712b3c89af4ebec37b7d43105431b503ebd4b1c6929fbc060ac4aa24b98d713abdf27b2bacb518d1af966b2dbb30295aa9dd9e72055429f8c83c4434ce71bb4be62266db8625cc7e834a77ebc22155a7cd94cd464cdbfe833c3e19439cff289cef74f3c2c07025760cbe6718bab1b13fe5b301c184261ef4648bd83298a07553e58691d0527921b534f31b8313178066a252f104b3e190530c9d3c2eb599ad769d536f86370e222e7f2875986640a87ac93e96988ec23834c3578db33af3ae5002edc5ef2dae2c2cee062a4aec1c6b2e69ba68925161dd094a0432252889c8edb2178ca9b6f31112ea41c0aaa91b4a6fd20d8a72303c3bac4cb609f858b71e067f01a6d030207a892aa09860d1ad0b594131ae4489ccd1ce0837460efb9ece020af26b05f1ed772912e0086b1d2c85bcf3a7e0b928bf451519880267f0fd58c482089af741f0629612139696d36da67e3065916eee476411f132dbc5140a2b56c814618fbed8071707ebf19de39701dc09b4103bc4bf6ed966f7988bf04e08ca5f70798f1638e47d4c49c2a6031d495059455af63052da0f8fb33dc4848c5f86e973911074e9ac2ecc9acaece3819abfbe7747fe376d968a6b93b48e121c41efa232788bc5a78d1751fa600ac8fbd49caf0d11c4ecf399c335a307a05eb42ea8925b34895d95eae0942d80008af1d4c2ab280860c376ae3889bd5f06e1d1c989e8bc20369c9f005254eabe8d883b23329947475b796406a6c92e4f2a7d35e365c8fd70b0a3262bc0a17c6754fa2e36f3566cb987f411b0ef68e49510a51bb59259df69cf524ef5f815541c4aae99399b0b7f8adaece0ab8234bd5dce5dc552fec7ef850d3ba1c7607b6cf0262f1cf5b0afd3dee0304aae4f14fb16b614d5fd34b732894c5068ca22c1d63ba6d47c13b0fbf0b9aae6b690d1f6a37b753b17839af4eaedae885cfc5a8e0ca7dcf475431070c51fe1a515382fa31bf928b8d1f6e479e2c7a78c608f1c467c1fb203a14a89ec464c803d129a845371a02e5b66e47979af6f04c69f37e59638c9d8e5595c74e37eff86bc43fea19082e3649ee692e2f66e48e111b47b03d8f97aa3b1be2bef4c0abf268ae90cdd224f2202e77e14f4f74fdd8b27c2a29fa00816d7c6c94f67e6498adcd7d306dc8cd1d534cfd5c57c5203ec323dbdaa1d005eb33738c583184ebe0a5e17ff800ddec491e071c0d6995866b329d1247d4d3fca5a901ec606991949f70bb63daf0ba4e2c6fa227f4cd8d0f1c42b7cc3983327fd184c4547f995018e30e78330ff6cfcb89183cde28c07d0f58e5992ca7b1f47caf44056ab7af0055e2c33d8187be96cb36431cc7fc858efcc5ea40ec586ce420c87ed1022c1347e7d9194c985225422572e5d241c13515566269a342de394ed6cd669a6e9ab859766251194bdb60b2019f68b6c0c5124975513083c6193434382e73eabb23ed72a48e2ba77fd30f7cf6ecaf013d69f101bc1a6c05cafbbc2a1fa17d5555bd0f8b5b2f70e8320e4b66420fb9916d11f00d42f9228c38426ca24ad6685fafac8b28cda21d676053835512c32bb66acb706ce77b4be01b01c9c5e4ca284cdc69b721e1c111a76f9d2558d9b750662d0801a35d1564a31b274ae6698087313361fc08b858cb56fb413dc07567e20cdea4f6b809ab5670c0c4d8139c2f85872d009ebc38f69c564b53eea1d562bfb982986b4996593183f7022f0b5f928ce8786fdcaae49f5f593ac9d3313855f3c01c462f483d55f42d2541d17bb36ab5f8831a6d186813f003a50303e5620365ad82c2268eb3e717029f714202865d889ccb5039b3959048827c44fdbda923cfcf91124a927531572f573e57cf2bd8a87776101d92b26385a784c650590349d93c17260431fd5670e61c6fcb32d3ffa8298b6f1818cef6d6a326e009b3b612dfbe6d570407e455c6e082a1c3877683591d2df5d2381d16d23f268a64abd21191457e245d33cb0f21372dbad324ae328c85dd3a3543c880f57cc46d13baaafe9fa8038cc6cc8783c5dacb846ca1ae11c65082632aac1f7dc7e31c2f3c87b592d4d8e6ce631092ed8248758c546c2d0808a1eea6c09a1216a332d75f1185efb26d5c2613e31b06fb3a4de56871ba7cb2ae9f48439dc1b6b772b4ba3b8f7c5d21cd92942d1c8e0d07e5142880883d237b4281dfa90e41f8a50bcf7922acfd02ef059513859621e2e7f0ac2ca5da1936264340413897c8d48043e821d613d1d9ad9e61e04a2a5f710ec56ae17d4f3db17a17f026ca70ee59e6d58fbc39ff9835eaf01c56401290715263599f686c71140c8e0da536d038e047421eed75dc7c67bf7f8955be4d582d97f7d7a717939001ab097b79c39ce975675d71c47bd67a76fc4ea907f96ef6ceac33c45da5b19c53dc102cf0ae2d7e9e42cbd3c8d9d51ebbb36bc30a3d710ccaee4a7443201cd348c2c6cfd29000f2350e9da5433b6cee557466fb44f7e9a2a363bb1c5f3dc0ee5e535a01a06ea2a6c3b3d3c480a3a1c6c1a5d9c505a23f7856f26a1959b849e9be7c3fad11a30fb0c9212c3f759ddf30c954529d78d9626c195017834e2ca2573b2d0a718a644ecb00cf36f2210699aac08d112ccbf81a6ce407e33dd8a92953a06d0e08eab328fbd28b823b045b2f4bc0499b024a753573815a3b28b487b61d86fc2e3e8b23d204b75e1f3535d1900490365e13767cc43baa87b89be045f08aa382c4e89c08d3d9a67ba3c36e84dd6203c09de9d174ddfee067794bdbe0731894f554bba42353933e08cec6f74ab8972fc6ef530d59ef317172f4787abab5056b3daf23bfa4f875989a6fdf4e6196bd5fafbf1f3722695e7d8189fad26da6fd4f1c47df206944598e48c05ebbcd6ddaee098d978f78a42b70e063f235fc655e5d153c1da6c7f66df961b49ce59ad3dd2a0e0f40355298c4790947614cff8eb1c5bad64fad2b9e454b30ada5b060cb95141950a525b0faab5346173bdeafd5a3530b86756a6cab547af1daad39ee634afe7faeb6fcd7a9c42b3cd26533d615982e7510431ac4147c67641995ae639a0be2e8b8549589d03d1f6e11d0d0c0e6f01d2b2a59474d170c2ce9064f740c32e14a4f6ad463568b746469cf5d59810cd8c3d246b8742b0147def463da94272ea2be0cf489ef447a095ab91f90937b67ca9b266f68a246ac2c7dde591d6c1991416a5169318a22fe0f193f1ad27e3f7a4d478a7cdf13b3401c7371c769983d49730b3a55889f752096da8dc330ca7135f1bf76a48b91f198fb90ff686d9b18d2e76ecbaf1e46c392dcf5edcf1e07c0f84e2ad24b89a5f686736fc4d39026de90960ca30999b5576805f31694db77e7551bac5d081e93091955f9ed18fc7a8bb21ca4febd58dc9718191e219d963304a41329540779550c0601523ea6c659c744ff1fc6a3ed1588e4e7038681a4c6fa8202e2021e5e58a678c23922d1930e8a4e31cd12d963c7ee73e3118cea8bbc7594a09ba1372bc2812207d789161becb08cdc328842ba3370b636b073918d16c8251db7edab60e9ef80ebd5c511561399ca991e5af27b94be20ce33f4408e90580ef440920c53657f1e614d360e1f02ac54f59e211ed2670d88975365af607eed40e781ada3a35ebc8fbb4f2aa8969c2cdf505509c98225eb3ae4743b245399c0dadfbf4c9dc6e89e31a129be3e10f27341d7fa9ff1a6850f9db0d0dafd5f7540d1548a413abe85c07a8b992eeff3eea733f4b6ba5e21a46951fa6bceb51936a36b1e89a36d4f040b407c4d8355f9cd0379dbb58b5cb8e424d730be62051d973a4914e71a178fd9a42488507788930f3a3e74f1e43f7bbca8d3077cea81a974658baf61e532cc03bd62b7a32d415b81fe4c0c610566cb08e55cadf0cefb14a2b07b9e3e7ba9f31712e9d5e16f6a7f7c2a86a3c0962905c78fbe1ce25f873ee39eb7d87bddccb0376ac9b79362378f61d2ba0cc53f10238a659a545aea55f5912ed196d8280c268d01c673dc9a675551048a8dc25f946987fbca24b7fecefe50e065c03428768a23340c918cbb72fe2c7663c57eb106dd12c4a07d4f6d7067a9e1c16a28525e270d57d00a1d96f4513afd078824aa4a5c811eb9469d06df8c3b5b8899d4b8f27d4312338c3826af09b3eb544ba32cd40f757c951e354cddba468e11df892fd9f8848b623e158be8292f11c8c1176e12fa27ad3267db02785c205c1e021da9e3d2cc1ffd6687b289ddfa39289fc26765939aeb0680b135e858ef23e553ca30e381e55702a4223a40d17dbd6289c42d4071b3a802a4fedb1ce745698884cd62feb58c7ffd88df84d713aca2280b85dadc590043671814f405b1d684794bbbe9e16b4a4c5122aed0cafc9ef3d8b36095cafab9335f2afeb7a890bcc881f5208d1fc47027241eac377433d02db7cf4f63ebe028d12d3fc4afd065d3606b95695b132d44181f56f14a8c976a325b9918fb0cd2d582bbbd90ebbb2ea68e46f8b603766086146759a01fd1b52acf4239a6773e69e0facca6f9beddfb45366d94e7c940aae37d9faf3ce6031df64a4407b09ba0f05d31ef36d37aaee063c226d05533eeac03059c6fc86dca3e05e820d1f823af2e3f743a42e74d0242784db49b9eef105a686a45ca48ad548e032e25ba628118f68ff3ff492ff6020104a1036a93d6855b3050ba9a0e6512d7f34fbee968193b47a7de584f25272fcab5a0d6d5bd1d8db5b5bb9dbf007312adcdc55594f1f52baa57c2b0820b08e4124c3bd13b33a91ae21f0df24fc13fc3463fc8b5eabdb022f9571be94df6de52ca94640a2308440886089e2765b77be3b0a176afbabb9bbbe7243323c781649d6dfbc89b4f9fdc46e7e4365ac3dc72e0e9520564be9c12874ee736b9c949afcb20495541fafae3cc0f3def3e7232a7dbe901c28b3de8ffc123c72d4803b8b90c921c732228628a9c94db867022c20927e593227018e2288ce75dc76d40a8e76c10f7285bb74759ce5f6e6f50e353fadec7c92f44210179cfd920f05b0584facd06a1be9b6abffcb0e57d1c431991f4b9142cd7a08c7992fb3c195bca6ea52bc51b7281aa8d44feea088251f59ee6538f7a2a40aaefbeeb3ef540dda76c1015fe200bd43dca0671e77df7443cebe22742635d9c7a22dea7ec122a40aa4ffd129af79e48ea57bf7a8ea168defb1368622eee04558fdd2962e9f227e41b92c44cddd653053c88e1a1828db6d1c01cb144115960d0c10d564c2ba629aad478203921c3131d78c151c41849e0ef6febe2fe46965346203c98a2931c60aa3fa33a6eab717dae8b7e24c1e5ef3e312e3fc7f49b5f3d8175f839c75dc041c50994e8c22486c73d2edf39ec619e9f1de6d979e6797f318f2a4961d424161e564c1b95de2ba616d34c06d0a4fa5377f759bb96739c0de5e7491e3c0cbab3bbccb0778d10ca3bc6073c54168e286aab0a51c31d2e465414f183e3b8bc04965918965e1b1ca3868cf4e342577acf17f51fb7f974e92a1e5554fe2ac8d41eb63b3c902a7f65b14fbfbb33eaeb3ee99b5be94835f8467327dc4340ab2ba623c8ac989e682f3d7ae038efb8ee4b711b9d72092462b80e061495ffa71d041e0834b0a83d7687870a2a57ae3b75092d4597777880a961aa0694f7ec91d0493aa394d4ff7408a98ff39715d260076378961db5f55c74e7ee6a624321a594746e939b93eb8e93296fcceeee5488fa312827f711c1458c189d9de3267fbab36a4d92e2756d713bb5ee80d03b67aaf4cb4112ee92bfe73227edececc814705c8e6ed87a024467c31053646d08d00910f8cb4a433cb9db65a5215c602ab3d1daa8487e7777b7d7498e67d1bc61fe307b983aa4e086497a417834a3a41ac74920d7f33b9cc4cdf37f3657ba4bf7f849b9f1e93edd6f966c4856eda666e37f3abe94d67b7148832d02abe692f5e58b64d5fcc67bec86cbb29270fea725eb0b0b994bce73f3fd735ef1440a315100937a9292ee09456ebe06f090c9147a376425cd324947a830b59d245451c84a421da1689b0d5146f78955adc1c6b16a82099b6ac4da52e37ab4864da6068360d8bca20c3dba5d840a133de8dfd901c1ff224771616ef396af7a8ef3451c1b3f2938eccd52ad8955149735c4dac21a620db18c583356118b8895c41786a196fa66e9464c1f60e86338df7f53631f1ba8296ad8fc8d8c7de6b35644ac304bce73e3fb594ccef3857dacbad0a8596d9b8defc63f6425452938f666c95ffda8296ad8f026cc452d35ece68b8d0d6f98587123631d960d6d3cdaed777d2c26d692bfc234d8aca65597e883f5c5ce5811d5d8d086a86d6863235b195d56d26ac64a5a15dd6635b1f272fb3d5692f3b4be9d95c4c558b59b56cbb2925849374c378a50c51837ccbd585526cc1887440d5147b3a31e6646b3d96c368dbe19ea08858a1d3bc6168a6d1fc7389781c6a3c9aac518232ba9417f0f8a785748717564a686a8d9d19dc910b76fffefbbb9a9891fbe2e8eebb5ca84199b43fe5a1dcd9510bc3a629ff9ad2fb7b9dcee8eb8fdfcd371b37387207e8f9850b42a412277379c330e55a5c1c8527a514d88b1e70f45ebc17928fbf47fdfcdcd6c168db69ec5f828215047a82f282ea8236efca616c5b48181a2ddf893a801d18bdadfccce1165a403f374fce2afa9b9b9e10fbfcbc50d5f973f7cdde63b6794da59c4458d1ff9a6c686dd8dded3dd368a66c5e31c4fca54cadd3d544bd4f7b33d123d983fff06fb449b8475e6f760a91f6f60d81f711e0ef5f3a3933894faf991a861aa9ffef363110aec13697e7e0598c6d6596b5db55a51dac0bf0845f545a2f8243a8932e739d2d9b0c7bc81c18d13a148004f553a5d019051e5fbcfb88e76658ccf31472666b20200a3865d547400a20a7611c7d5a62e621f9a58b9568d4cfe92b28b2abf8b542fbf08e83c7d027e31cf11254023fe4acb87d64dad71c2a9554ff5758c9364d4e94305ee72ecc359018c51fdbbc6451ffe9129fa70eb4dd103f9f2630cd887b3ae05eb789f807968bc6c2957b60f57f695d5876dc495adc495527a93f3d07899f250369f9cf94b3eeb9334ac37f94bbeb45c8dadb5b5b252aaac2c12000daa7f7f18694d57f2472210fc6fb538293b8f0c8f7386702770df7593b9d6fade5f5106ad21fc8a1fad6c59550338e6a4b3ec4e5d78dc53a7016ad71fefbbdbb61934ab5a43b7774a39c97deed3e746e3f48fbbaa8fddfb501f0b9d10d71e460bb03c0d5184c39b072b49494c8c85c4424a2c66ddaceb665aaa965ab580a10186060d303d6c3d6c5b0f33fadb6f1cd356a51f7bfc155fb6941e00a755414add9097bca5f78f90da10202e37524ba53fbae6e4a056c4c80ad1c9931bb9169f5099f96025fa8043bc8931c69fd2e387d0b019373e90bb3b90aff7bb5ca1dd108c334ae58b08be88808692dd6b80db2f7e355763f15905c62f865a39afc70ee8812aad1417d0fc10bc41f497b4309f5a17916d09fdcd52e1a81a322df7e0de520710c6e470b585f3299662d410ecbe72abf3c8761e98625073c01ebe835359f50335e4a51c9852d074e5f5569c4a4c65b542255392893e6a931a725294dc375598acede9c7ca3d5f5a5b9d0af36e3605fadc141b5c48e684a6a85a6076fdbd9734cb386e0f4e97958c9cee002e2b1961b9376c6e2d410d513526a81afbc4f7483f443159d558c73f9c4c5632d641f9d8505935f691cffd385767d77fb821aab6fa72fdb72f497426d7c3590b5fc63e01de23631b352c5ccd29d3c884158d468b6d3d1c6eb8e9883a421d39cf84825ffe55f8cce2fae42272e48e2664e1ce0cd8247145bb9161f1d49a375c67fd8083cba4701631617469ae24f1a6e317959671936c96b5a2dfaa4a83fe92a6a9d2b21043d344d3b4a215c919d47045e4b3e5444473a815dd0a2e386846349858c78d9c87a60cbffcbba0d18871747dc5e4fa37d1e050c3154dd375d415d6f19fae399d674e9a8769182b9561f21c6e98721a26325b9cccd00d67971a3e65441d352c5cd1901a16a2ae606918cde23a991ba63c7efd57d8a79b35a38ca4241acdca1337e430a89a9716b47094a38aa51b3219ec5d8750d1c08a0ae583175730a5aea2151d8f7378005ccad462f4856242a28f78e937bf5593ba626aea5162e8c72749141ad6354320a0a461ac9444d18d4c46129319e91d73dcf8f1c6efb18384c50d53636ef8e373441f6e5f8afa3704c1f85c0423926c8eb631dd2dc64823b673185e6226769a52a5631ead056626d29513ac685794ae3ca111c588866482133150d49036591aa6c16d8931626e2c525969ccd1ed17410479b732573648e30e611dffa90ae25da663725c301b536b4a1a530bd3e494e444a5490d6e3c31d6c573dfd2c36908a606ccd85634631c3c51435a6b42399d798f55415097552238eabd38825fd4bb6a84ee46b94698d77fd22e1f4ddab622e7e198b66ddba06cdbb66ddb469fb6c89286881a16d23177a34f4fa0dcedfb511ff7744b7f263811b33dedd2305a5b1ad3b0eeb7a751a851c3386a53f0a71d398ff7db53a486753642f1b6975f94d1595083dbfff456c4c1520de92c16cdeef6217dbadbd3a78635190df3cd492c52b24283dbfb17d460f418db4636a628b6f3197e8a5094ac1014657128426115109c26aaabc631eb70dd37bd685ca0f4779fcf18e73c4bbc4f3d8ddd64fedab8671590f729cbb4d6e0f6de479ffc85e250a86ece80a0a13a8ed268137da235eea3ef7df329ad614dd40c7dda64b4067402041ae6bf7d11148e348c6edbcfafe5afedb79beb35d7bfb24fbc6e7b785f4d0f6a9bd5e0f6db47578cb2ede38cef36efa6b425cc46c50d535b0d883136206a378c58eea6da6e1893ee168bed5b51c3c29451c3b61edb155bd1ddb629778bc10db72a77ebd130face337ffb6d7b56c33626d9392a40424c114345e070b3492e6ede73cac9429285ee6ed235d190d12101050caa05952ee6f4ec68e1a2f51cce65252d4d6849e2685aa6b0162c1d5b38e208094b940d090b1145444ce93c7669d0dffe50632da995a4a4e393eb50a8d73526b4c07359c908a7db7d38471a9cc14542d83aa2a441ff0f68a8f18cfda2c6a7e8d4a07750821a3f8c66bca96193d672c271a6a63e2754e6a77e8523a9ef6fd7759d9279b7d088dc6c28eff628a4db7d7f6194353d313935755f5bfaf03dcf8642b4db7dd8d3366481e976dfffe3fdd1df6677aa80d0b64ae69d5b6adacad15ca9f169aad4d842d4505e97754c62fce9d10dc51365dba4d69b5dee7caeeecc990da167444aa2863db29e1f53c6d8deb7ea2784a8fad9668c14656634338a520b7b7a8c8c66b32833a3591423b061b5d503758337df6379c32337857fe6121ab4407c6e4cb7a7fcdbf723a4c12ebeb794524a2408f0cf023deef6fef5b8326e51fec7cffbed0b81ae911e17f55205c443bd8732d2e3364c0a69b0fb7ebe101af46efcc0a186dd951fcbb8250c497d4d52affa23370524d1e92a89570a0752a9dfb6944dbdfca4ecc6121a9cf684e82a20f1b7a8da6c2462525371369fabe012332c72e9ab18f037ca38c555f1bd148312889152534a29a5941d55f259e5c10c363be917fd8c4550e26cfe94a1a45f2c226a7012c56dfbdb91a86133ce8fb2228f426c8ab99c74c3e8e4cad87c21254c1a0b0ccffb240e1a54666729a3bbd330eb44291ee7782ba9221d715cadadd6a3dc3d46971f51326ea84f7d38be305e1cdbf6010dd50be3d5f139787c4d787c8f1f725f37aae0570a07783cf7f88284623c78581b72377640769d478ee7f1b99023478e2f569956878d474f1aec48e4ea52bbdf9155ead9d0482a954a4d4f01f59bc731c618a35fb761d051838da5c1a69163c61c83c43e5b1ae6763e81ffe2e003f0fd8e1cffd2e6594e1ea9119ddd904eb9ed44a3d0a7469cf5277fe978cf3f6af4d15991dbc1eb78a902a2c3292c766091846e8c3d46feea1e3c7e033d74e8e0f1aec30ee9619f1829c6c3ca223ae5b211542ee3a041f5f762ece82ee78dd67558af0651e37b1d7cfc6c74605dc852280d4627f6893e931a7c59d086ad00580e6c5877d8cf86dcdbb02feb73d8b0af8dfcc97a9b682712eb748e1d2635c744caf14da779e6ff43ffe9d4a0530a0ef674a2b3868574ca94863de5f8e934cf34ecfb1cd626e16efcffd5ff47e3a9ff549e4cfd17f6d16da90a42bf70614c165f9880cc17454851e6fe57f3f1fb3b5541a8920c5a5871fda34a04111440bf70610c99ebf6082c88ae8e8f8f7862e6c69fd6f1a170e591c482e43c3c3a6cddd68dd68d0fb9dbbaf1ad1c1b7200b0d6de7cd84739fc699ef97ca8f3c94e270fa3865d54e4fa76fddb90d3615d1fad3f85b98df3dc3838ef5f089f907ee5b03f38c250def8c29c7be32d7faaf125894e49e28c2fccb9e144f2253feaa37ebafdaa3e8a4429d4179fdc7e2e34c2351a8b71c84b1df65cf6624576fbb68c75b3185ddc0e239490976e942c64dc304e5132624a4f4f4f4f9be124246cb8618bd25efd31754454efb252162f9baca22e2b65d9d242962bd2cb5684141f90639268114cc0e05834cd312ef4182759841243110b237f315704163245fe620e849e1fde0b71d2e0b6c0b8bf614485854672388f4b13a5ed8a2645a0a0891037e49668f2c30d9a4ca97159c9033d70351a781ec74d4abbbbdd36e5660a2eb7fc07a78c1d3d32f3ff7fc3fe5f9220ce6ee7c278db3233cf1b043f43b6108494d0b0f92740603e5d4ac63c132b75e292ef2f147349eb6a40bf3f50bfdb202af2831c08f5fd285bb932ca0ac5e2cbe69967fa459fc59a4e3ebc54d039ae7fd8875efa4268d3a5ef3cfe94babf021043adfea2743af98bbad3a14bfda5b8a5327f514a29a59c8be3706442808a187172a9d3a594525aa9ac6161ce3c43dfe9727143eae4d29c86f5d36f390fe5ba3003ca09c47dbac748632839ceb9568373cecf81a5522faa82208965a05e7e42dd573dd4577323e7bf759603f2a968b906a795f6bff52d9c6f7f0e866a6b83bdd35df1bccf8165c852971f9a56f3e58704c87c8e93614d5b56ba0287cb4a3188a2140318d86e6558aeb3d4a71e506d84250ce578638517fd6563eeee66ee19bb9bebdfedb465ee39ee74b39219b63a9b6401fd409c0bc29beb966a5df4a38bbedcbe21ea90f8d325b5614d71c8f11703578b1d15d40e5644e1640512655851a5c585921539d440b3020450889698bc289a315d218210644862aa8d200422598b8b32350097953cd0c20d1909457ba1cc86c50b60a8bcb0850b24665749dfafddc9f6e8ef5177204338a791e2401d0b5ad2dd60449ba51a7fce3967ff4f6f94a31da55b4f4739e9c6c939e7e4e426a99453ba803ae6df31b7414b626dd95f2c746f61f4b0276d5648c76ca0e0b8e9c80d916888da2350d428847584b89cd2850811f2ecc39b1231723b1edc69b57429a50f492b1edbb98d76bb945c0eecee83dce84643e0524af9453a36119d98fdc6cb1b84c795f65950e33333b781fe5ce9d71be5a2d334c384a405494b181bb5ebee34d6727d29d2884e701ed47b34baeeb5b65aff2078435efa711eeb6a40c736c9453dc186890d020d7a91136850c348a33917ddaea006d58f8042ce299554315693500912631a600242c5508030f8c0f5266020a68a20984060e34888fac49404adf3a823e6764b2266e05c0e15475752115541644f22f681d230ae614713cb449a5bd867fbf8138b2a754a941b7fe2d03c7f8011dcf85306f6f1237ae082b9c474e2c649c58d9686fae24d75dd1719ecbe5523c8ebd9b003f33def3a5942bffa8e090326e0003f5eea0fd2ff50ffee94949ee10e005c086579011143b4600c2d2a98c2c9183d7071664b193a70c142f3821d4f8e58925dc020bd890b64702f382980d00e2d28638316803082e249cb8b215a70620b2dc884e18211b7c5055aca4940a7f4d6944d943a70876bec344424e607f9382889b1c289d1e1769795c414a59ea0c2488b96f829e61f37547fd9aa20dd755705e1542274acb35de70655416a10bffe1ef58e9452a654a268fc7f3a480e1e6adf29628668636c368a6aae9b43254d4183779cd21421f0008cfb108424c00c8d8b420a1b6c1b4afe8420a48496115e709c0403c6086a60cc8020015b38ed80d0f3a365040593aaac9e1d10a899178e6ec8b58cd0c163256a6cb538ee5fc74b29a58c524a295b4a29999999dda50d3b04a83e523fd454833b3b3b671ad630ca71ade7c70e08608fcf69e1842cfe72a50fb7550c4aaee9ecd7a9bed3009d7272d167cfc1a02ec5218f915da073439ceb51d0aebb4761e6ba143f5c1ea118d72da892979e649c0076e80f6e985a1262e906d504666ebf6ca11eaa209a2951650a92140d012f9298fd70e603664410eb967406fd2174778d2622144720449a4c268361082f5d77d8e3767bb9216f690ed3c4076e7f3fd744edf6d7561351dcfe0fb3c3ed07c37071fb77acdcfe1e27aab8fd42a8dcfe210d63a5279e2e2fdd2660c4b01218302e2b813173f959041a17755341e24512cb78210e4931837ecee960891a2a99b3bf78bdfe695ddcd703d74d027f0f08413c11e356630293317c638c9cebc64baf54dd7fa23a9606a9233548fd8875a89cf98bd2b0a2ca99f3748ca22e4d22efc64c38358c8c1e6a2845b565605e7067a4c01c5dd5a579eed952474894ca2025831bb2ccdc1027c6185b382740a008051a368f18699036f54b0bf6012336451f4ae2059887f5343604771efc1ad69bb7f9307e51031b1f76934bc386e1d2ef115c1adf36dc59c1796e6ca4b56cf8362cfb2b456d6a6c08d2b0a1ce657dd14cbfe8479a6d32127dc43216609fb805ebd0a7ad43a8536dd8385cfa2b1b825465c37ed26a6a43ce9bb18975b61a135a082399db50380dd2ff69b97df2721fd7a8e16293f3b49cfb4c60128661f237f9a15fce4a06b767700bba1c67bf41da6a9089181b4afad14c83d446a7cec626795df4364533cde334994c56821855825fcc04bff892f0050a26e10b0c18e41bdab8dbe6ba396aa8b12fa5b2b62a81c7396145bafc2eefe3f57e341869d498d0c29306f9368965c4a8f4cfcb5f564082eb75eacb0a60b8ac4212bfa8a08cdb359ad4300e19f5300fad9ad8e700973fcef8397e1157103589ec6088164e6d757054f9f9bac0dba341fe7a005d355420d5f30b29816f8c32a4612aeb4a7dbc291b67277c414422c6dc38336a90bffb6294297116639c31cff8c5cf1d6d5839876d0a95267260789c53c33fb7d43fe2aff933e54aa56aecfc10321f684e10ee9c4bb154ecb0d631773eab617cc59d9f33bbbb57c07952fbe3bf2bf53de5863de5aa9e0ee1e552d5e4407ecd9fa86e6e934e228a82aed5baa3dc5163eab818c6be7dfb3fb24fdb8089a523868ec08146e7430ba49610d4c09331239f029c96f3fc6c91c344c2871570ddeab29297315ec45ce1a50bd292136a8930463421e5e82266c988198ed2982cb22c53ec0bccd096a60c355cbea8a165bc26f933f2d7629dd8de2cb490ec58c663e671fa7147ca2f43b798628a2b59c088114317ba050352a065042d20b1021c6a90818aa0a30b3d28c8a2b9cb4a5c9846d07a9c249c54a1a149110f35a8c2636860058b32b21966a075b13d512d281659b8d8c0458a0f28818403c81445a1e28726c260a28c46c2044c686819275a5d74cc840b588481c34c89329c5ec8b285062292a2d8528218cc2c50b4a946a5e0707842d229bd84d6c6c5082e555a5dd18cf1a79c5a393b3f682e6ba23a2951831b824a20dd10841b332c61e6054eb8400397253ac8820b148cf1928451162cf3081b67b03411c1161f12c892e50b185441a6c6041216e400035217677ce0018b0c282c2550d12f6670a5858a214a29a59452f24b29a594b239166129a59c62c5491047390011821810bf1757b297a51aaee42f9878310a41142c00810762524a296b7c01e60832375892c238c3c5c8260b96198e3245649951d352c4cae208285653906a600db162e6170254849999617c250f2dccdc6e8e4aedd836682166d24d725b86b618491e93028f2750b9f6c419a78671cd092cd7b9f644d2f5a766e899ebbfcd244b4b44f5fdaaef8ff1696c5090d09121457e9898b86683a84869816c6abccd0bc54276fad573acbf7e9a27127f6597ac3eae6cfc252e196e328ed5fc8ebffc3f88cad350a936b6ae6c10cdd3588e85dfcfb1d54b9a27529fc6dafc929aaf4fc466498db7798e853b0f583dcdcb9acf25adcdc7fafad19811692dd98d0d8a3322da4c66e3a34dfe7a9245a2d6ece65d2f14bbf11ca3f161a4798dcdd7bccd07b90d1b1499bec6065101aaf91a3628d6fce53636282822d56a906a6a3562fd303addfe5cf355df1ffb5dd312517d7c1791ee7fdac534c5943360cc683c15a0199ffa54ea550f947a55aa67a9a761833a657b46d4b2d4ccc6bb6cc60c16130d1b36c8c6ab7e860da20234e35536c8866d272b63b22fc4188d7672dda035cf4c35d635c376aaf11cab3183c6872eabf14154805a9f1a4240ccc68d0dba7995ca06ddd8ae510bea967d21c66c58da64f361d36cac6bbe500ca95ad7b4439c08c5e81315209b57bdca065121bafe2a1a90ea6bd82027f297cd8cf5d126ff195fcdf7e30455f5a113a9ec122a40f555b325d5ba805634afb22e22aba7f9950da28274fd89d0a71ab63081099264b29823f9cb3f68a863f559984ff32bff1f3a548ed56a488e05062b5ebc8c91c996b0606529eb9a76042993327ff9bb5c43ea4ba9d699520f82918c552dac08330343a34d4452b6b22ea0d4af3ef52bebda9e63a124baeeaa776d76086df2973f7572a7ebff62d4a749ce439b6813a549d78da0e2aa3ea447d725ad3ca248d7e527679fa449544cce3696d0ec56141ee7c4bcf861bdf3bb6f0b6a750532848bf3338242e2d8c6316e58ac490c78cf492d33a06df817f5294ccb75a36ccb561e7a768bd5f2eb9cf58324aaf431dca5d30d0a2959b8f014c5165562e030ceec30e4c529872a64b458ea7898d13f48199262036d8627b62d335041bb087b42b0a6050f44435900b14319156cb48b323ff01622326a52443943c6918ecb4a65606822a3ca1435a6aba46f0e313cfeb9e29cb915aef3c7ee6ee79c27f2abd340d791e7f5561be7f6064d66905842c20994362e6761e6329dde61b707f90dfb312074e3cbf9575ef95288c62682f666af7357ba46e064b72fcb6e7c067037f67c6eef8bdf7cf948b64b73285193448b64e3b7dc955605effa7ba194510a6612a3649d792309e9c909c4e79c4aa612ef3e236d5030a7eceb72aa80c8a92a41a8bbd3ba4610baad1aa15dfefcfcb24efc27db6a4daebbbb6503e13dc3a4bbe49c1b2fab3ce7fe747422b9f873ba9c72cad864b8f66ca254124466ce83d0acae9c739b91759a3e4c72a5656732b7cab5b6eda75b33de1c101ac656e27c0ff71f3faacb193c66668e53b04e24418d2a6a7cc9decd44c4f96da73b50832d19d4f82d316a7c3335fa0e1ef5c83ad2a5949267e3a4f3746ff79eb39d9321c71725b252da2a53d929e539ffa7552508dd905ee61c9f71e3f5d8443633b38f0737ac53ea8c1b7297797a2693c96688b90ffc621f24205d610ff8928024855f7c79c6fda2c7968612ee2e5dbafb94d291f8299f3aff5f67f7e7568be3a6fcfede305da8fee1fff5778e79a494538251762af497dfc489ec6e29653fa5d2298d1c0cf1caf76f5939fe66638cf1abf2a59472d2ef183d88fc3a6ac32ad31a575a25f3a9305d7ed944953f656824b6e2cb6b2432b5149e73df6da10afe9c12b74fa5fe949ac49debfe6f6755f0245189ffb42a70977d92748f440afdf9ace3bdea1bfa5893d5d37ca4a15f381fe5de1dc6f93e91045dea1a81feca8a990b76b132e64a8944cad0c7906040a87b1b2a77b142c6e52e41205df939200fc72467a5a8de55403a2b85e6575fcb5f72a6ba0f090684ae149a577d4d68b66715cdb77d9f03fa4bceb9cdcfb5cd39a7cfeea9b771db46376e9bcfaa05743605fadc9d2320d9a60ac9763110df7fc68fc2e37cfb64abc571ef3978de0e78a5949272b5e5eeee5050882af9ae0ad2b7633f735038b282f378511687765c8e6ec31e71fbd5c7314e54b53848523d40f59e65c10ea562aa1f9231279107d07dea8bb15601c998cabed06415f3be631dfd38669907c0d9d8bd962c5a620bf19e9e9d1d10f4d675ce88bb8dc047a06c27c4fd3868e80d061ed730e6daed1bb5d24a18bc1b59ec4c39a5bb0dbbc3c87e7e7e42681e14ffa03e0a1431a226d1fa38f0b2512fccc4e4b15d4eba711dca4ba96856b566060d968d1402dcf80f0071283e6998eb1907478e671dcf37cf1c4c019f5fcf1d3cf378ee9193d4857d7408b0c37edcf1e08367017c1f833b3ee7c3f145e0da1c1d8bc33a3b704ca0c7d76270c7b30a080e0578ecc8d981c2b59565e4f5a5ea11f0e3524a02f0490677e47077c70a1cecc8d991633f3e00ecf737e68d1b371feb060a77d639e7bcb9716327013e56bf5c635af28301ec781082d4e91a1f90ee7c7ae5d2e604f54dbf7b8e49da3d11ffaefb25a877cb4ea45bd2dfd9ca44b8253e871a9ca82f003154cfba80baefefbe3f045daff92ed710d49bc03122525036cefc35e7ff883e86c09dc99cbffc35dfbf4d6e1bc5419b0fb485a8372a23d19c33752792f7a4c2e39c90ce2efde2e51ac6bafc1fdc20a5949cfc204aa08da8842df645412b15d140000000f314002028100a0744e270382c18d38555ee0114800b7aa43e7c521a8a83519002290c32c6184300210000008821646686a86aea94d47dd2fae5a8e099cf943e81ad240ecc4163d2344f0f046306fef3d1608dd823cb868a8290fc286eaa502260f09406f3ab1abd058440d9a7069da3a0584dbc7e686abea4f113a496410fddb61fa3782a58b03e2f7da43fdf3b57dca6068a9707d6837c1f6cdce8155a94643c0ca7cd03290436c7979301e5ce36afa5f52c6419e3d3c46268aa4ef76458b3543eb6b228929aee9ae0b74203bb249bbe34b6ed8f97375899121340e7ccd22bd19f7aa8cb1ebbb2601fedf8edd77862518004f0e2c7f445c610300a4d048ed39f57a688995aa95ab8fc4497bab8f8e41ef54dffb48a5a72fd443775bbf4c91df59bfa943587c4839c08f48f7888fdcd2547259ab61e647c10f07551423eccf206c8dd08ff7bd6970829baa670eb69193bb1e8034d1f5a388e5930691096880448c84a3b3e621ca4dc59d91b638a648bfee6d10e0223afabf301c81153740a65acc6008ecf8f1af1437ad66c85998448146ae69c82401467e8159ab7806c3d245643d14ace1910f55fd59e5e04aae920531b18bb1fb3ecce3476af67d89c0f8696223e544c72f6c8517caa669942e72dc46433821e6109050c00c21188f7ac128868d4237fc777dd8a1305a8ac64d8b0507a86ac03a0744e5a369445090ea0c5ebc14da40459733dec8330ddc17a534d8be74eff814c1d1caaa01ae565f4f0a7f5b1fad680edcbcff7e94d543e5d7c4a86b8ddf9868a813ff4d1722f944dfb3cf93d2369ac96936bc970121bb39f75a30e7f7f7c7496d550b10987c360ddd3561815cef0d6aa077947a0bb885d630d2e8a540c584ed79093d7a49ecf30b79cdc99a4bc09b2717e12caf9197796e6fd1bb2972724203d21a3810ad397ab9fa5a852f366ef0c751697022050a1a86af4a7c5f1769679089ee9b9e52bb4ad0814c798108af197116ccb9cc31b116b02828a49ce4a8fcd5badc5c5778b85c8f5a766d12901b7dd870fc4e5b2d5a97c943ee1f7848ea24b51d9a5cd54eb60419805162fc7efcb699708e4702ecafcdc37d801561028fb2e4d150f12f912a176edf853670a8a9d3ae46066ea493a8e97495af4680dd6cc15b0d50fe005bc691b6ce8797dbc493674b6ace87cb2609d0377656207d09e8af431b39d3f6522cfe241bc4a1494ded3a6bfcdd7f9e96fb221173b12e89d24397547b31c8a354fbf82eb657b4679ba9f5be9151928f5b9f6cc54a1d223f42c8dc968a8ece06fcd6222cf65ccaee3c50c79e427d642d4aa8b27ceac544d692020dbadcea71750ba5f2a1c56e131cf65d3483d15621346c04cdcf842526315c16c97e234a626c20ee58bae1444678d9eef05823b7ede794ff2b881c40c405e0335fb2644ea75e1e05558971d292addd997238fff2c7d93e8c3c3499ef09cbe97e81b85f199779a9ecd84ef48471019747420dfc33fba2a7a7a156eb7551f21645779d5b43b7760c2b0b0a7b69f57f478c020dd20d676fdd587acb8eb042554e75f7a2a301ed15e5037c71242f5d8a751bceabb809671905c096c01bdf4d414ddd46bce4bf7c1b31308c483b027d0bd14a1e0e7aa114ea44248564064e7c13443fe8919bd784934f2f46bb65024bd9731e390468d6c3e15c7b63212db9b77a9a682122ad297abb7f627613a0cd8952aa4edb0c3fe63e5298895502d33a1c2c9d927b66dfa0302640773d9129087938140351b01149f96346b16b8a6be26ab2d04c925512e60a14af220759505fde31cb602de28bb89113a3c305e19b2ebf9389cf678951a168072db716dc9e7e123d5d00cb96e36e92a1d5c4de61fa785dde20879eb4dec50f077edae3a549e1d18807a0ac1a918b4cf4e6fa463002f5dbee7bcd54e5361c5953c3861026d2651c8bf930619a91e75a2e27ae8be85f0309952632684ffed1b9433b3910e44b100d7539ac1c9fbe62cd0e3feef363ea94a856b31b37928de39f82aa73cae8a4e64e0c001e4ee13003bb8714e8ec63ae3ccb50ba2d1b0dbc0c9750a2a1e7d038a3c1be1ed2515ef923afd64c41e38b58491adf14bb011c2c902ce70ed4811b84fd0ccf1d4f59f910ef4dae2e905861026be460722160c069c57df130af97142738d5ac2e4eede3b27076227e075cd9c61011de89acf46bb35f175b7d0a1a1d388abf1f6e9e23d83baf8d9f93b88281213b74878651c261323e94ba04092a43cda5a69228d9ecc15245614b4a1f0bb0520b1fe291a56602e9dc105f9012ca3330607375fc4b87e389de43e82aaff2204b414781e3b18f9f37839afc6be38a9b40bdce9ddbce5c744bfa8f58e436ffc79c5d44eedfc4c4eb3e1794f43b498feef1978f05f641b3c55ef23b027708f44e16805dbe91421b678560d6af7e8105d94b473366ca88438d88308014691fcc0640eb5e2b535201b8002a874a5f35dc19fd8533449248381daaafb79768c3887277cdd5b48b80c815bd99390628a7a32e8fddd757013e20b41f90955a202ceb4377ff0a4ebad4b944ffef54bc9463981164387abec2eaecf13d2600d42a94d7ab996aae6beb320535115524f79cca6e67f15f4a31d9f58ad6b19db02bef1cef22d1dca3f8ed344eb5fcb0e5d116f5bde1efd71f2be2eb0260f2aabd3d336ff6a71f463db8674d66db9166dbed38593064516917ffa88f08aa92b13c57c7dd26326930fdce46ad88de073d16ed0c892e1ee3559c56a7ac857118fcbac0f732be80d6e83e8ae58dd9ed805b6791b5350cb16da2208e81cde836057988979013f05dfd5722ce9864255dd2a5a39dca8699cfd53c78a7f7a5845657b056aa758cba007856e4c10773fc87826b69aca46517c445e73bb29dd990ac280242552da38838564465899516142ddc2fe75ab4e1528222cdc26c89803c993ec47355ccaf549cc5e8194a1f34f47f5b5fb31fa7a1780d393e0eb60b0caed8209583a36bbd16303f00866838fa293706ce0bee804d8668a048f522190a656e875687b54846fd7a9018412dedb4263c4a744bbccc82d76fe64f0d5ddc3faf1c1fc64fd7010e0fc824afbd9f2cc12e1acd21d9a57ea6d213c4437fd1df94459192474d1c838402f7961590ea4e4522a17ddee2e32d4e57f1c3c3bf091dfd2137fcda3045d184513318242df6b86fa15fec3a7178d40affa0df67172aebe6f212745f80392dc2dd561f42463720bc41e762ad4356397fd325e28697ccde7be8c3f3b7bdba4cd1161edea1e54a007ed4803f1a529cc68c5e15bde83af5a0cb937381a80899d258d57d10b0765c51a8d2883b34bdb4b46791fe156eae50d4ec91a35eb288961a97d403174099b540631084301d985fed8272d78b1e62cb15591deafb421a04a79613754fcfc2ad891c04053222ba0a2a7b37bfe43612de9661ecc4146a6cdc8b1ffe3d5f7c74f15662a84ab199171b3394db1cbe43fe50e041576762f7374d4e7d32b517c5fdec3660956dbc02858303e46e93993ea643d640976f233ca883a28ecd693853c8399833b29beac8b0a8dbb941941e160fb965dba9abe889367a89a8db2a737fddcd4f27d7f29fb61c3ca65e2b112d9706129bb6e955cfeae1e09691585b5bb806ffc49541fe68c551c3f7075eed9d9769a1cf55ed1026e3e7366513eb4af38a52699b308063c6b12a625f39bb19fb54abfcb978a35b18341bb03de6e3e5cb2dbdca964e30def322204e93150c56d12c8589c80cce3dee1897305625f6804e1d08a09e2d59ab2870acd30aadc347b6dbd528b1a3ba5aaa7bb10cd6c9b577d8d51bade6e4d6f965b936fe2650a3e936ad46b5da377f39f1fe51e2b5cc467e2bb0b38fab15125611c100e27cb243c85cfc11186b99843fa0e348d6837360eaa1b50501f3f8986559f0b93e1fdbcb3a86fb6a5db4890a3bc012641d40ee6b5f35486583286ac5e935d009923d72fe8172d926c93588bc52c464befbb692864567b8e2f2257aa4424c438366170d8f97ad31be0eeb22f70e744b69e092bf895b1605db392cd467cbcc1726f9be57a84f41adc924a6881d678338da92c2234814799de58a6ac5dfb9d565a46f7b12d502538ef1d3f0a429214f2cb54b1afa7caa2c8fec7b528689d05a5337ca3260b4796082361f9718cf84ff08f39fc707a2ae0611b1ebbf4f95353f33c83318533178997cb13993d847e0ad03d2923b0a3c50cecd675d670f3d48524a5afa283bed121fd7e4215c224223923a57db48ca24f71331f4207301460ec8f6ded41648995c963f0777e4f3f8885a8e3c879cc2425c4f9b39608e690689c9e49b73d138fa3cc94a3f39f0f87d804b3559f54bb7538548f7793b7914bb71f1515f3aaf226ef1389abae77c51c8b5039d08034476b1d81eca3dbc4dbd428eec89526cf5c7c807618f0434b4ef33b6d61e71b5a50be58bbcd5a4290ab7482e853f71f46bb88cfa2aa8537e7ac573b1198b414f3fa5b6cc06386aaaf33f1759e441138bb751f2d449e66039fa20ee413dd88311917edd8f1881b9f81135494013fa1809626f4839ac4dd139ad1aef2be6b58c6e99dada7f28806880363f07126c69f806226fcc46e2d1e1c28afab304b238c7ae91264baabec92f1349da7628f7971de73f42403a24558744f24928a13887b434181f61132a108bae013eb94ec0293bca7e51740f3c74708d06daa8253b6de9dba1a73680a37800c1ac216ffc7a7658c4a51477940df17afdc02d44e9bdb7f0eab722c52e399955ad437a55cabcdb9c23379031ca5d7d76099c235d0f8904ba33ed833263861bd156c006614351f4b7efd23780ed91c6bfc2350d3b3ac712cb96867088dd4873ca14f94bcc258477f4bf91438e9c5dfcf50fe9d682471193fcf5375653b103864bc66a9d63d4ae32b1a9a2973e0b2821bcd90f1949e1c6c8c9aea734d5fe97e8ff3edafaf079d1308d4315e726c4f180093fb52d03c77dbaf29456e4e88efe601b03f9784dd8fdebf61fb03e2fa22843f3afbb89c534f01118286b8d0dfaf1406bfdc799a80151fce7c2f326c8b992555e0bb1e3acf73334e4f3cc979b92c3c5ba4b320ccb7830120dce811de956df279d96742781e1914ee7834a679eae77fa8760b96032907fd0df52f58aa9ca60bf21f35032c7282c84fadedc5cee3c60eb2146adf115f83fc5311360d43909ea39805c2faa1be68f7968b0149e194ff85930e61770a836686520bd74490e0a188b94801c395852a9a2059864e83b8fe7c24efaa9d89c16b01eb561cc9172347d20eebe5196ee208af783fd562801190e2088b680124e301ff0f044a847c5363957e2cc3225e90a1fe60edd9ff64411cffca52777fa241f5881d66101014d7fec3c962e032b7b51392624f8519785c2ea05259525c82867b997eb79735f7d81591b2666f81d057cee468b917ad18a6211f2f95b8a83546d4f2ff6428eb00f55988cf38d5156eb17c262f41f251cffec49497f5c0cd5e00e761d4af0b0fa210ad45019aabdddd6f7a705690ac473ee3983808c25d8a04f06d201b1727f7df89d9e19e1bf8d5793ac1f4876dddcc9d7c1f24d8d0d7f31aaad98dc375cf92ceec1b0f068d67f0f7f4bd98cd869fa1e0462bfaf63d95e87ee8d8e4c4e457e14407ff2855bcfec525a0d471fc8a1ed70a7ea08b31495dabf639ce301ce00317d4d90b28de7a11d29b26cdf344a7f168587c26645adc87d880a744db7e6a607cc1113911ed3e149364537c8207cd7730a1e16f609376d351e22527d725d5d0dd3c0996d7ad3ca6e65e2cca56aa6303d5ad6a80ba2a87c09d90f59e2230e16a6a5824f3f6f6c667728141004408c1696a6151f281cd57fe0de23d3edefb8abc0a5a3db4ef063afaafb2e8c8d79237713aad7a384779df890744ba74a31d6ee29dc16f7d1a5009eaa26ce472f1d9b61e5a28543dea3b8b6559570175cea124750b2c4db8a58810c904f5694ced386672e7fbe6b88e3bbf9119626f23a4374d855549e3e2ffab2034b7c3c7aa99ecb4a0714441d344533427ba2257a147d58f4ba52f6e18173d0eaf747975b00efeac29201dc2294b1b3a6fdd89f6e1f0a6021b2908f952dfd9f1268a404c6a3b868653e513aaa2398a0742691a6de68baafd1e4020f21da61fe1c517c5007d122af5a4caa4f080bf757a043874cad96f265a868147f09ea99aa154cefa0570aa44f58f5049d026aeeed2f1c8965d8be8546b2a60f94f0e384a7499cfb3264207a280c00b7685a73cc2fa27318217b556c873562754ee373157358f58706d2efd5b4d559eb0fd9a9e51f7cf334832c5297a2f8720f14f5c049f9d2fb839bc201678a4238e1ab89bafbf3316a12439d1a53ace70d108cfbe58aa375f75934bef183520c20ba41962647c029d84038de992a456534081aa0aff3f4b1b3aee59ae2c66c8344d35d3f0515239828b375444b9913630c87b958134bb01d9ec46691413876458daba8f2a9d16374583f4a7aa35dd175c3819d0b6ee80081244a0e1bbbe4ff59f8e044490511e6788740b34266fa644bbe13cc88a2d3ee906af4785aa408f2cac2d3a53d3a82e3d8888704187b0a02cc8e4a65be66e870c77aa1c2f3e558a417c78bbd72692d757a12a444eb4de02996813212baada11f70f23d9249893146b92b551079a529a1d6441b4f4a9da036006b9cacef9aaddfc835c4b976130c831f4d077e540b0799b56fe9784d89ecc458a3fad1070d5e14d68ba68bc2a3f806ff97ef8955d3f8e62cce36cec9bd9279755aa4fd0c8e322cb0e0d1ddb3a27180f6109ca4388ae93e8ad0f80f8ba48b5ed3e2cd4220bc15289b7d99bd709d3a56d1080ca87518001a0bb2160e90f97d528989462251fb3b1aef4cc5470258b3154256054d8265f540f20dcd7e73248eed4d7499b19357c1e1c42c86170488cdc0e3c185e0b235d489e0ade7ed1317c75f084d75a2906a77eab811851d7db4231603a0f1e5e772a03c81b3ccb8e164d63061034ef7d180b4f3f3509c61c17f54a4b264d8e57fd247922c9c36ce467d857636d31636552497eec03cdc5475ae726fa7f50de060452f78056e7143442027e7fc8d16cf90a6b07779ba69b09dd37c5378b232d64ebe5aeb3592b4a7734b1b5c8b8b96f55d1d1bb98337ed3283a45dc4c5a42607ab3597282cc6d592ed8a4910577451d5d735bf1050d050f01391c561de96f0649af623e7ad7422ec35a66d86fbff8d93100446db440511a67d2d2ac83a77625f9389619a5b5fd58972f026596f8e361f12ed6aac713ef03a25ea9cafbf884831b1bcc05ece55bd119474a447dcdf49d4439acd4de383713d19f97027b2037bc525cf57a01009a19f124737e4c40f608bf88bf05c8ae3aa16f02641e803df6f3ddf42b91327f5d1608248a71ac68a845efe44146cb8103954f56afa1233b532c91cf4fa6393080cc48a927a6991296b254f4e0c72386a6216edb70fe0d685cb2b1df0e59301231724a957e04423e902c604994c76f8ea9bf67a1b45088d08c41d2e5b061ef96a186c73b2509c139ae65ae42352b460b7682e22575d61c1abe2b47192c3922822ac61e5190137c72634f5202b0798c46e8a7e2388149896d8bd0d6e5bc92af0fd431118659e764d01f7b2d9caa56f3409163c8c638db03baaabccf916f8e64fb51ec68f3b33121e92f2eb92125ef602589906d4a6d7aa6d983842b150aa628c56ab31de0e45f07291215f529f93656d931dac0b264f4e176eba6cec7a9de7c62cdc5f607f699b08b5318ea97511e6ab08f3d186bbff7c93f49ea8702bef8d5c70bb4514700929eb0a2fd04af851ec253943a7148d70b7a95f3af845c9cb9b47f66edfe5d8c3fb11078c990e5f6da020d06e6368871b967614a65f17ffc9ecd539530be73190493f7855e1e4c5237b06ef7f864e5a11e9ec17c466365f55e2f90181b7f8f90f4fcede66d92a7777ed2881220e7b18e4f7c90c0380936e75a639e8b4148738178d2ab03c9dc45cd0a7cb0a0f00def92c0747bba81dd8eaef40f99c368aed49388e6b0de2eddb32b0d719902c2858c9a4d495799faa21cf6a98fd206826e5529dc20bf2735175ba76ca2872f3862eb4076d461fa75b35da3db82c119eed9c22225b1e708fea5015514b42fdc33394ff7323fabc11a0610669391a6e551a06cbba3b630d5009115ccf11555c34a243dca918095c31a2a1d7604a0828ac4625c401f0a9257eb1b9c182887929b55101d410e713578e5f04360bc94f2d74a9cc9b006881b8aaf27f32af9f37b6994381b8dcc65022e704ea32ddc3b5e935bb89c71759bda014c4fe18d83455ae24f2652ea4369db98fa04eeba90a977014c378743fc841388d9363e3e939adb5da8bd5acceea7dfceee511ebd99d83370f2112fa62195210fe6db117a7dc74b0251fde37d076dc9065847c14f750f17802e09e01d56ae3d0d25295526ed2fd635a57b9acf796b87c7ec381ac80382e48ecb83be2945393159410d25715c1194fd261672a472de8ec42321051dfbd0b1e6a8a351b973deef6e657f161a3d65abd4934cc712d21936e6f9ce4c15519377c1458e54cacc0e8aec57a45ead51971ddce39f4c02114d04dafcca41fe564007a94bfb50389185a44312a311d3c1597b2a86e1ffd1075e0e02350dc2cde6ebeb3a33b0b9695e8fa09681f3f6fcbd4a32adf33e066af02c9ecbbadfaf2690a4c55eb26e69632d5031feb26508bd2a8c3add6220e98c42d7d0ef65dcfe53949ad2f4df0f6d02afeda89b4609f5601091b034bbfe2c4ee8ce91ef1b1c791a1da62cdcf37ddfc59c136a052410f8a7fce1893edc13906e5552e997b8676e7ab8dc892700a524d4bebc02a567df803a4a649d425fc3d4fbcdfe078844a40799e14b003d898c3007acc12b50eaf077afe849ba4a56cd63d52e6903c0c39f253ee3bdf9be50426d86e29f7102d7d19a16ac8dab417bc38638b33665a13e9228aac333a456b04a5cfdd6cd7be5c55976b998bc5e428d545eb8ed6fb1844032673b14fc8f73cee5663c4ea1179c6d4803c71ea4536536c9f6ff6e5391022cee2a7a8a26e5f387fa3fe827d1d3746d7780983b45261e892544eca3375e74d21de1c97bde9516bc8fe469aff653ed71ececdc592a8e93d99b72489cc4c70c377d1b48ae8a17547497750a2664e8e557e18036e044640c981a5db251f08c8f5466a6bb5da0784f25a038e3bff2c709fc5e8558f5e390a0b21317762294f1be92ebadc641835121cec3270627dc093823ba4bff7093f6c80fd2d6e9158af6ae431293c7263dff462ae912eb96b1e8357946e3d61e2d9e6c1754b1a668e1bdbcb0a6543bfe0ff96a1e83b31b364b060007b5761cb94d1edbec1f640529e477564203f04829c10f10e3c011222dc36a71438567c2a80db08689c8352ad3352676438106d98799c8a731285c3cc4d93ffe963691b047aa692cad3fd3e15065f712f6527440939f42865cb71c2e23b46e7ff40b87e94c6a470326cb9723e62b78ec0d70cb67113bd2bddf80649378e005075a6ea3f95b578b08f6ae082d83672ecedd08ea848502ab31ae062e9b838ced42b3ec1e6111d964919dbe421b88586bcbe7e81885d8e9bab67d905b043f3ec6fa3fd762370a19df7a719aa72b444d31762931820a41df4b7d9f13c8ab8f30d1dd143b7737184a38f963a8b8caa2857756501a3a754944d76014e552a7e81dae2d6e0da48cbe28d8dff782bd35f7d128d2b88d412a4616273cdb487d778cc2af0c4cf1f042621a3ee96dce25a6574392b3205eedb79f3ed67c383d04d4aea079a9c1cbbd8e5133617279a909b327b84cd35785ac014c4e7ec09c3f6192387516828cc641f6a9090b9299335350241553b69e048b903af4e237c54ccccb17cb5234146462910047d48115e9d09cb480fb4088198a5b09aff4c83491fef6f4b19146590f2c2e7c46dee44ac269eda1cf67698dde7d87a240326dc173cccac1fcd14ecd1d20ec261100746e5431016fedc295b01170332f33ff75bf6b2f8bfc97339b39c6a7905de393f68aed4163927e1335ce3704639cf28162c29c03f923e3e76e8f059e2b1360e694be7c8a42178f7a9728f8ea345b1010f60d23b3805200678041660a6167aaba9980b37f811bd9e557293072d52a05eac5cddeeb33a73e556573c13d0aa1141855e3c0dcc3323c58ed3a297b5e825be0c7bd7960302313bb49a77a343b5c7a9b8ef006cf6e970c7f9e2d6f018f341df2df5f83ca7bb675e29fb5408964de566aba67884a80036f3c5849cfb66b82b3ae4cf6caba3ab80d530d4658badfc321061a550800fe46614a52b624c391466387274fdf2b93606575ec274092830a05a2c7670d4274b4a2666629a1fc7d7214dd2d4656250ce51201777f74a79571aaf0766bd98b29e1431f89429c0aaaa6194a78fd7812a4b3a4a12efc5a881d9f8284693636098cfcfee3213fec48332e03e6268c3737557a1c88ac1a921db06751b4bc13cc8d66ce16d164a4fbddf999b9676dcc6cfdabb513a5a1c91845af90436684e145f42fa66a557a01accae11b51abedb6554fe8c42601010de0aac36e1eff757fe3c982186fc6681abaf1a9e4bbe9a1a2b71bf98d6e4338a08280cd1914bfa65bb834c9c727dcd89f1d94800296766c4b22d2f9ab2a92759a4e76ab21ef895039aa29634cf70719f6db2e299684dad46b474b251773e0f2166d8902af7814582dcca9b396b6cee825e826c852c9051a2171727af956e55fad047707f262c2247967b33fe580d5b3a83fa376c56158c33a09382b7d7c5cb226b5b25a61b6c9989313094bb35956809afde13e4bc3d1cbf70eab1ed241b6199db50e3317708b912e32a6ed0c12509eb5d144f7fbb8cbdb5f74e13ebb94c49f971083cbb86c3013fd7c5e3d2cd33eb854cd1d9d5b10a7b764205f6f23b918ae24a6994efba53745b0e98bb2ed0067f260da39986b5bc2bc258a857b1c8b08e0e3c7805ab3bbad90c6e66c3ab4ae73707dbdb169434d8c977c2a7db55fabfa9b3c9f52df6a24cd907d63475bb5d5b68c1e90dee67ee22b40ef31262448cc3cd86d44f9b9107aa23de84ed8b44a641721f4c02338b557c98e382c162ff496b0da5381aedbdae30b7eed73f37f8148f6101dd0db1c2ec94b655eaf32938494b8c20795b87605b40f77771062faa07e27e1aacab378ea484c2bf7724625ae1417c6a1cd9f58059f019f544de79fa7fb34b14e56164522cbb8d6914b12c150eb3b56b2e3e878ba531bd43bcd4c712def31500cc4baaba323b42a25407b516279049c4206521d6973643aaa2bea9c1b42c26aa53d04a95ea5ddd46419da1ea9ce145800b4e34435c538b325f38e1373cf74d55f62ebcafce950db8cc77b4d7c2557e5e1ecc75c100813133dca67859c9aa3baf147dbe6e3473e02ffeaf5762f07ff009158f8ff39ed930faa48d5ef02d15f32291716ec0b7ebc55913a7648411a8cebfe32f33944306558a75284d72c5e2e91b089f6cc6f06d17c07696a3c058b666bf82cc8ad4718895021af5ad0400b7e04503f23556d6ad01768462796a743b246992bc82ee1cbaa0213610c95f1717d176547fa1e9917dce15385138f5ed50c1cd2aeac699e6503015eea3e43dd102e4ab9f825061ad8863024e81b352fcd59b2bba345fce50eda201a48936c245ec7d4b8b7e3b9508a33747146ecb780a9b777205830d48c4de859240f869e427d1613c23b5d08b7d0a5f26f61707c25f91d2537d247de848933b5fdba4a1b15561ef9fdca43aea7aa1190324c42462c4a4e40186c753db74b3e77d9a333cab29760102d9cfda420167eb8d8ee31baa23d607fb9f63cc018199a3332bedeafb5e9101ae75cc3a3c51bf53ef44ab83e050141c2e34ed72ebbf8d945aacea7e35372297b0b27a736ee2d6b7c5aff2e0f7cca34597d6ab5adf21d973bdabee624bc6d6b8c491dc1afddda9e04efa87e670dc27f946e15394100d4b3da557a0771c985e18e95d87c88bb5707e79adad539a46857e3f315850269e1c04787f2abe7fa4f2d3b6e06d3d242154181717c370c73e1c691722735d87cf39059ab33e54d601d95bcf09090fbee7b82c34dc3208a0fc90b3df0af9f83334e6802023fe47c4e102bb526ec74b5390f158e47e8ac34928998e84be853177ba4a2d981e54a6c89546fa9dc219d953ebbcf44a4f3c11278e83bf8a6a14c712a29e433de4ce0895aab8a18d3c9b269b3378379f8df6fe156cad15c92b490dd28d72623a88ce69a1a321847df4c9b41f86650a9ad241654cdf367336f198b1a13782e3add4d25ef13ae9c40c8cdd20815483367815aabfdc2953177f66ba5929c9923e95d98845736459afe9927d18fe1c09474a67e18e19c06ffdd299c44672d497855d42c649cc46f1b18bcae75116cc19be0dcb93515d7b1351a341fa862938e571eb34fe2d487a9fb38a8bd8d76095427624f9db1411e68d8044dc2b062072d44c903336fb608abe2e2f5a09a4b46f10d28815c7a85fcebde45a338f56993ee29289b2e7de1603c2877bf5c4c18fd37c64856f2a96c85053c68170f8c1dc9acfdaf89bc03c6531e5a42111a34999b2cf37ba9337596f17f0d809da36c6c4a8b76636966b19b855d5bba5d2404879a12c60b8eb79c65cdd530f29593d281288b1c5f7240f6b6635079ce662b62ce78d2379160574ea8e0a1c97bb4634bc4d0f207702777f5be6a7e0d065444e017422b08377f9a9f2cc5b1f5afc558b8cc5cf8c3b6f8b6d9fcc7babbb30f1b14072f2673d4a03334863ce61fb0415cfc0ecef85a6c4471d50946e8feacd4f7b4fc183182f9550f6c2db2b7228ee58658bb75de9ff9e8683a216639787b3d0837c6b2009e2ecd979037786b4534a13158f703df37dae8c55548813aedc793fbc6697d5650715ab995ce4072f1d232a4cb666d31936040f8c75ea43c432fd6584172ab8d18bcf65ebd544d555a2d43293b4a51490a5bd1a864dc984c352d1ed1535af31531a17b32c5150f1fef2a39140200e3accf74494762cb7554f2db117cf01b3af0b447c272718d9e250a831363e047be1b0e40093a047e19c1b4b19ff578d18bd2b7220ba80015b611f374dd878fe9e9782108f9308307b6120cdd5d32844ac9e9745335d521ab810308f3d0a724fdac104e82eee0b95a26ce14da64e105060a0eb9a2e3aaf94017e5c654783d932d67dc4920b41e046344077b60ea37822c524297fded04ed38e55477d3ce142e0e5a354f66fad9decd6b2051ae862d79671bf0c6012028cb53360b7fa9c8d0436667c077cb36ffa2113ada8657c7ada76cb93fa5a4e43c1108e622243c982ef80bb2d01c129318f639ddd95f6a172de4854bee38bb960379436d4bc2d8cd19f71d5c0afd6a2e32ebe5a4a7abf540b9d9841de064e233535314c52334d878e6b62d671cbd203e76c4de154b04f07746b9401a873434cafe8783afe036aea253e2b2566c2ba42ac4c192ec7e4c7b0ce3d42add17f6d4ec3688f5c8d4034054d4ab65bf23ee806c2fa5c3fcf3572409a54eac8d3e402c0f9c8c701257733c784142e4f0b4a8cca80a0656f813cd7bb839d6c2de483829140cb82cbd564f2e37448a00d5212a62b66b07ede928982f1c8979c8419ebafeb675e2f578289f2c6d37bf105336a66302a90f4777ecfa6b202d7b14129f7205216c7e42509eda5a87f3d4add07a259f9b35b85f88d5adb9f0d3dc8be4c586a6daf1026a327bdcea07430a495d7e743557f98fd686e47dcd6d63e32c94ca738db704f0116fdfebe4fc521ffa28589815c769766ea01a40f8c31fcba380b20a2a99b0647ec874f40fb5102225707cb44831ac97527ea29a67d76c9fe690c2b5b0d5fbb56918d8b7a271b3dceb78808906997a91f8b8744248bb743b6dcd8af150fede201cd9378b685f75679c9adc1afa96729dc80e61ce197bb3a1e172f17cf526da73c65edaef6c2f6cbc0e7914a1dd1acbf0772de8f286e0551bd23054ee1a288413042348ea378d055007d886e40798914704b29ddf5a61f29882c41436b20ce3527c1bef98c2f5296d83e8c4cd93ab2f42f10af66f27cfec2f0c11ab75146e5cc524487f2dfd0cf0ea04a9b570d34e4ade2608c142b73a1bd2e81e584003173ff0895f6aa99443e5fd1bb2570a3fb5b34162bd752dd3214263c99aeb5ad02bb94ac6cbf3f5dd1346cbc3b75af8ffd7e223ade405c0ca962b2d6d09fe7535a0b64e07b59e669143fbab9a7d01c22dbda90620ea51a346d0d83035e84ddf2ad3d3a258db952a56e59204e21005272e3d58ae9414695804a885e1c87018b72aef5cb0db4da19d1c8c8ee262879008a87b4b43080f46bbd5b554c92b6696583140ad2268812e07147630ab0003cc5badb2c4434b537c85a8473cd457b62fc47ab4a64dd1be42e81107e5af22fb02aa740542d3207ecc470091d31b143da7cabeeddd214a7bca8a0861682cd52f56f54c198c81f15f9b4dcfaadbcc115024ccd6dfe2b6b95f7982ed4cc2550337a2bef741b20ef8ab0669ae2e488b1a0fb15a184c16a182cf510f869e9c238f6e76e4224fa5cfdc32831ff698159c0afb22c761315fd8015f05b4adec2fc643df7f79e0ade06f287ab71baae5d09598294b0be9a46f8697a9aa5653922f29691388c34cc43ed044040c83dfecdadb16bb3724808858e9dd0b26543bde0249cea128ce399c9488a00419c2843e1aa028a2c6af65d8cbbc070d8a403b9292753ee71824a6d4ce485db7b2214ee2119a87985eb938987f68a53ea00be0af0cb1bd8b3e4a1616e8eae63f9d3457668fe7a9a595bff4ad2011db8df45bed866bdeb972566f0b8a8a7afc2ccdf3325645266b55117fd0f837422511a56a945b417f55c059f55a1aa28c62211721a87a40e30c7ad953e348a136685930f4c503039b6419445656c5917a96f24e71ecb80f96304ee1601627b9892ce9bd647bfe7cc1132eb7cb28b800d6fefa8e95d5062d0c45c3379025bb715a5674563e345fe71de98822e326a013d5d41050709c00c1e8cbe2386401d878dd30513e1fbb414fd27a529779331a63c0aaa4cb80ddd8584132143c081b57d670c53366aa6ef368ffbb8174372d85c661b09ed613819922d87f7758ea9ea5dc41a6fc9f94415a29a0b4da1d4cae611cc0b7b87a5e8506cf9aa533d221f08010bb9555e85ac52878879ddc1204d9fe4d40e19bc0e2d0bad67b5c20b2001bfe7173e99950ee447ac7bffb89580fba506d8863b0a25e293aa522494bb6bf81cbf419c19625a3cf3c85a3c32ae42086a10f915c01d27d19da532bdb53d52f8406fdefcee89a630fb64b2a415526401ecda590e33c4a82304cfa976958d6a2c93f26e067dbe025bffaecde441dae429a6d3e1da1a94686aa5e35c1ea060b37857e9c2a87369671f66d8d31606000571d57538da62812a3f8cfe6b2cb68369739ddce703a9540ed79bd982b9b604670ca92fafc7e0a8df5ad51ce4d38266c22b2a14fe3cc9b6f654313f6265698e350e6d6a4769e099d7b3e0e7e54d66bbe2f7907cb9e5e14ac6421e12404c1bac0a4b8712dfc9497059dd674e039a23bed1a8502665feb37270bb44a408ae87525bab4a96c9709a5efa1d97815e087d5b42ba080eac6e82b28ecbe2cfdcb361497caeba242cfab40507c46bf445c13061a16fc74753f332e216c235ea7ac4ca014ebf830d524a53f5ce0e69e18e3396f60b2742e9b83514b249c8d7e624168c70b8d9f3bce9e8a72ad032f6876461e3db05cd3b74689a418b49889acb536cb68159e1ea77f3eb334c886f27803a13d5bae3bc024e0c942dce2b5de5ab0e4e58367fd12d8f508c66805528fb44aee6e6fa16ef2a2cd291eecb8ef4f629431f5f02c64ce3ae191cb2b84e7f8098265d55d237b5bbb0aaa0794681ee5cd98e40ad7dd4eab02a51d64a55a1e5355a8a40841cf16674fdf54bf06d42e9744e5744d50842735f53883a7b4ed8b3303d16587e413ee721a3c92e46c223dff1ea64d107ca45cd574c32b773b9ca3619e4a76faf64c6f1a30bb74effff693c453df991be7c2c56975c6a4c8d5a1cad54aec24b2bc1949b3292c2d42ec738c3860854caf306b1e5dc36566836a1bef87d0e58e274e0386b008469dc5e9093ac6d5152c2af5f756e36bd990a083f395adf13737fd00b8107c6cf1ad6f06a9bbd029cdca351770b4db51d30119c56809f4cca6326587107d365e064e7331eff77d9adde1fb6a76ac3c7c68395179e47ae53b7e65f93cb7096b5ff22eb8f32a60000ce7d8f48ef81004ba97380f219c73d9ab22ba6aacfa81aae6bb0f79742d45209bd6e42971e0384471f97408eb2159dbe8c6acd695a404e64175f815fc2cc2890c878266f1e954138318bd86858552625eed8d24c4484552c97501d6ba431dad8e9124498d126fb3a81190b4d839eb82bb3761658f0a3dc00f7f0457ddc069432e8b53d9c2ee86380697c698cbb4a0c916d1812913abada106bd74090bdce99f13b7b89114fd84e0a66c8d92d55684e1b97be40ce1345363281404e921e6796b56358caddb4e0d209de24bc627f5a11e3589c75f9c2f4d06619975c8cacdf2308353959920ef056e8102a36ddbba8779e711582b80ce306e7bf233d4da16cee5fe03651d9da519c2cb47c03e9c9fb30138e7c4d123d40529f4e834995f1bde21bdb68edf99039ba8eaa52618b054d678a6a75356964d7b9f82cc56463d4b80a97868a782fb33acafbe0823ca17f5456ca3f064005ad582d655eb97515c65c19ec5276557fa191eec20ec4d838b2e2487688167f9ae6ccd08e5827e4235f500d6212caa2ff4652d1a2bd2febd63c751b2a324a5206e96b86c7a20eca9a6595fa8ff9a9a114fd6b72b2e8c2e9ec6e51c6e397d5a4e201b64393771b30210b30ffca4c73b2ead1d1b8abb6d4351838ac3409909ad2b9a78c80e821e84429fe9b46c8cdbc8674a461957960f8a48dfe484495f783834ec951abc297fe1f50c13ae5ac53719a7c20e50d0073400747ff431a945909973aa6d974306aceca894b1e61c901ed5dae1c5081b49069d4a437e4d76ed334bc3a2ad1e90919d6e5e80f57e80d5edae85ba40fb81d758d47d4e5dcb9b17c3610083cecfcaa968bcd2fb8de2d802cf7432ed7d4949b970c22fe272a044841297422a8abfd13e3741e962dd773a044a32840a86a40788c9059feea250f6292eec0ce6b8988206a87f8d35c8d41cdf81326c2ddfe91a00ac400ff74a73b9614d2cce586a8384bd65bb797c937beca9b83a4ee50f44131fbfb8d30d6aff047fc3677a86979fe136cc52e214a450468835dc4e5b04a39cff84620951cfca1787ad8b7d12eadf0808e2cf0de82344bf478bd8695b6c44f98d402f4429d510e5cc6725dd6692b87ba1885e56abbf21aaeb2d823dcae71d9448375f9f51aec05c2929a2e6681e972d21758b4dc8b194eed9f026b5f7acf678424e69a7f7231697b149d22d1f46e1516fd32d6a3fad6a5b23ff268b13812ebcd36f6b5b174fa514ef97b651a7547d0aa8dee314b9896713f9081412d139dc115c8237bec79e9b9f622b31691e50534a7b062d6cff7d824300b5b1a07b7133ca77fab5f22b9bed4fb2f22edcc6984e283bfd03fe448566158649dcaba7372902d4b53bcbcb01435b646fa1a638825edbc0f1c978900f9ddfdb66b1e840b5e6bb6b6731ec798682ec1ce6895a6244c2eaa67da5fed23bae20762d961d3a88e42683a80b0461d9fbb2ac82c8388e7690b304503110e44e60b7cea02e1430e16ec574ad4e3d3be2f3e00ec840ca1ceb9a5bc08875dc91bf9d2b75c7fde9fbf58bbf2afe949f340d85b64b69dbfee362f8a76c4e5ce8da70215f7d4ef13f12fa6bf13826e228d5484f581414804711d64542810e159961e4f8fd0972d467cb031098b5f71e7ba1cf16068db1c0ccd51aa83872a89f4f9b40f8f86ff22cc14d5fc43022cc27c4747431c400fc57fa0bcbc2587774c9bc24805a5199a7040cc3a8654ce1c78258d0067bf76ea5810a37a9295156c623a945f338ef4ebdf750f746aae4bb10e5e643c1c62960fad960a121765cc2d01512ce4e84020cd09acd47f408b6ee2397d1f8e0694423f409959577fe267633b1f096a586848fffc0ca243f14560a0891912fe037f4af1a3c4d2750575beeac819e2942870579ec1965e239204d8310a1ec27c5a3ca125b3e92710d5360d91e5ca54e88bc65423961b82b01f9d86cb5aab765f3b7d76f2811c52338bfc21156a3b3b8bdcaa067f451c235a86cf511afa1e615740e1e717e0d20f95db3fa03fa86aa68ac4236d465590c1508969b4f775308239a5cccbcf978ebd1305c5ce5f86086f90cbc730645c2d8dabb056c7af0b4b4418a22e82f9f68ef359aeec8525105b1b8e51e53c6867e2595f82bdaa88b0dc2653400b46730a66656f6127d3a99904993f4c39eea5a043c266df81d594007650d745c8d43a18d5decdb7be312432b47c72c878d5275df5cd9942802c5ca045bc061183eb18b68418ee047633fbc7313d2dd52c40ca47b959090b337270dd24bf8a41cf3b49d666d1ea45cb5ba58ad2accbe165e455b533b7cfc87675c12c7cd1abfb753aa4c5790c5e9580ad912c122de74b34705cbd8c0d075e4d382c904b8ce912ef3760be6b84d4573a726b3f1e75c543de16b2bcee524807ec53750775fb4434d719cba7f297874781ed56bb8c4d163ae60b4fa837585c33a56938cfaa3db93f8eca055de4a595e1c5a18b0682c3ac615c0097196e40fbbbd31fa09cb0d31214565e305caeb5da74c685ba8891d7b5a84e4f0fc0fe61223c829b1520492ed2db4d2fda4639e780d743076660e7382879f238b289fdc89e46fc2f88517d2823d311358513ee0d33d9b5a36719b558d17680f214563f48b66b674b5f1e64d33eefb66aa96463c5f05c57a39902433d01017993f9bac37545ebda87b82d641f4948b7b3763db0908f93627ee5598e79d4ad3c8d88742e92266e35e031f0fb6a9e3065b9a9b81b62fbdef0440b76c846d012f836bdfa1d347c7f8145872c64a8bc3ef1474d2102f2d96523241a5a2080a59dee1380fbe81e6a856de326c4dd75720a816469c28f2e168a54b86b3d0485895ea10af0506b4e24868a406d62874d059300ea69baccf056170836088d62491078f95da4dc0fc26c14e8f0b8200720ae6721d66c7b21f3f33a42753d4e26311a31496da152bfb1372b504a82c9e3352b1e4f73d863acda4c5704e542675e98c6851349bfd8dc2650869c22595563e0cac07f142d0b2eed4129ebd8b9df0e84dd6498c59f48cba170747e0900095db18373539225b1576c10fa099ec639c61545d2398ac37ad641f2b4d856b8b76492c3141fb6cb5851dabf13f90580c79af3af21da19f238c38523a9fb232e6dcb03e5978e7041528542a1e7c4008d45f69df3605e273f1a1de74e3a906e6ad9916a95e388641fbac8206d4a892acced5aa62501c604853414f809ca1ec0b2cdd251c0c042d632460f539dad1e716986562e5bbfe3b02ab38ce2cd40abd52e8f84b1a0de4dbba28dd771263358a996450ad26e5f1b2da33acda258947046c9102a4036b59161e043eaf6ec14648a109fbc82100cc184ce8727d6efc13a7a26a8451f0972a1e7fb08cf083f14247ebe7ec2cd620a55ce7e4aca5d02da8972afbdbea270032c58a85b9541ea35ecd96920b4de547c85b79954872421b50659ef4827a9b8894ac8dd647aa485f2869f9ca1d8b5ce71f4888a360ce62833533c17e5bce9b2ba65afa092484f37b17c1f9733b9b280b8c806e7befdd56f8a0fa324f2320dec8f1fd80ee70e4a98ae126eacefed4a6692f47785ab143e9c191bd964bbf95cac6b791507b5a92e9a8609e8c39c3b0a76038e1be0f3776106ad527545e2e69cca0053ea5e4383f9220d02a4d9a19deafd8196b95506324027b6bca48c07930a09115b89ea1736b814018752fc11a34e7b29ca78b86e96f42fd258f1c6309fd9baadaba9fe82c0589b3eba957f42ddf89218cbcf5f046460aa5f4ca6fd28505e066fb0bf2ddd360459455ce10c824ee2104e02670e19912b0eea9959463c04db2855c606fb3c72f7539e4437ae870e0d4b49dc81b078efe34470e7f6f5f01509980ec309ff2208638e77235635248884b6136069ecfe221df77752aa94803d24ca2ec30c12537c5134267bbedf4461053322a27df149eff71fca96fbe140460f14fa73931995d8e37bf46c64e9b6e3a4c9858e3ea145f877a9619fb4c9ba344da54489b02ae89c3bdc468d5b5ec2d1454b5d03a45ffa2fc5f5f5b5bb432d1f3d86ca137c73e964bf6bee316d3e81a939b76c4ee1bdc72593daa9a69f7dccac7a5fe4677b03a1399747b5f2ecd4b278909046107691b95040fc6aca15335a011b2743a3b74cac73acd2de163c47f30389b174eb19127d002f289b5c25043e628e5b5f503c23d89ecdbb009c8db82ef0983ce0e7cff96234bb78e2d1a9a95452dc19328ae3268e9dda6ba2a48587a8a1e397d955d94b786d100e98784e44411590389688ffb26b6c13de7a69615387b19471cc66da60dc181b88ff614a34a82f8dab34a6cf45672d5c33de3618d08b5151eaba1685f382a9f033a5374b4bebc465ab529f68ca35162df4f2249ec591d1a7f724b589f56279916b6decffb3915747ce1f0efe9e3b7c5ee2217d2230a752e25cc778ec428cd079e83ad953b2fdbfafef8952ee4b7f69d06900a3ad79b107ec3af029a491f22df8c1e80a45bf63f4d19f4690c49d2457dfad0ade56486cbb2665fd4db7534ba4c078ce841917269bc5270f17984194a81f792c0b78de9dd05797970db57fe154dce080facda9daa77d0cf3c2f3071641a1935826891d31865546ddc0c21b0dc607b1e893653e85285ecd11617c2f6a53e4b2462a2524cf761b94bb4e4982cb4576e73f306e45d23406ab855e97703032fa1fb24214eb8887e2cd601a20fcc105ad5718732a939d17456285765be587206fabdcc46cc4cc33b94d4572ad7d0fd2c49290ed87b70e1baf6f23c6fb2c82bcf45aedcbd484b378d09351719c984379379585fff5e94f6424fa9c848758fa37cf3d44a6cf03a527766a6445d5f4d23e5e6d08a8632541f19f693c8b7272a8fbd4b06965cd19dd7999b7bcdede8b31a8ba0e580f440932ce67143c01465e4cf4edfde96f519ccce0f8b0d44df5e3e6c29231712ee2e1adbf41292348a007c90b462f3e4144222f446c6e14839feed21b0c986b85989f49547151e90675dd9a50b1a9a0792e9357daba48fa819a088510a76f50c2cc04585c24ba4b8fc02a8d6960a3c7c5824e96c26369fc488645b7bf68f55360d96d75a70c314972a25ec02edf244672d150bd6683e854026fcc193723e99382788ba4936900698b9313838ee8b32eddab43061b5f34270458fdabc7bd5735199d81459cad5a6b81ff8a61c342711351e7af5e417f936bbaae0b7a0b90b4739d8351b7660f182a88703cc27cc03f70b17273a7e306b36f54bf7bf993a80459b3010bb88f43f03591671f09711216a703fa7b3dc0da47d08230024830d073f332b21a41460cacc0989c092bad4878025844ca7c98756ffd195e44f983cdae0ace1030fc641a22549754c943b434b56e298bba45bdf242aa39dd50cd62d3c182d3603a944e3753749546855d276be35821dcde468458dc7531134f6cd32758e358b40443056f72aa4b486ab40733b522f3a69887e19c41389b624572b4b54305626d461c7ad3944f60b034cea38796b837bea76a164d5b31448f986634492986a881027119704991b1fa38f541e609691c5ea4436f4caf5b2f8da5481b078b54cfbc876b3b6a4e32e6f23ea64ceaf755f616b81387a063a2302836c951de8c71915a95567fb31bcec6f56582e9100a4e1172e6da6a19dd8cdcf4129c63593f77a063c305b988da4ccbf94e6caeae0cec8dfe2215dc9da1cd539e727fc6afe973c5514acd9350b839a9dc9834a5afba5842765bc643d42c78eaa561f0cb3d0eae0ce3a19ab22f05d39bc8d28c271dadb74d4fb75ca09e37192788147dea18346e627072aa98c1ee12653ad01171970f49b0c8c4b9e830037c629df6081165e6b954949e5a345c817d1c472561f82acbbb02e598e5c171a6d064d4b7896b5ac611170a8907e5fad243172aca22a6fb2ac995396d56848daed0365c27fc0754351d0113ad046a135a590567851dc1b22997d26d8f2fd831f5d57e294e11689067a01d0b2783ba96848a14df71d5ca2e5d0e075ee8279f2ee3651c9057aa561ed2862d6fd4c0ab11f276a2d470e36d6fe4222b06c2c08d4a56a24efc253cf40d5664f9f4f752db72f30f792ae577e03d0bdd155d9926c8764d3712e22e97c1be4ea6945f84a0c59d06efc1d957c3b9bc4e7a5f35390ce65919de8795b9b8fdab1baa97715199dc017978f535d87720d1e6aa2ae50a09e6e0c9282a1fca2509d5c23d697db13b105f070add2fcc6a71b8f3702393ae57be8c0074e13610d589e7a8164af6e7eeec85e586dda784873742cac4692b34645b1b3e08131db004dd3f3330cc156ffef26322192a478cae6741d35fe108decb0c78f61dd98075cc2a63ba6e2458dd4b5250cbda703fcc76edaee4bb85709e1b5c76b97b616ddce6f629c2c4eb78ec6f2bdf62e77138280fc6df33cacbed716bcc0e31ab9ec8025bb8b188314f41dc424b8826816b71870eb1c89419547f931ab94d00636f952acfc37e4506a4da92294bf012228db848fc5a2050710c163b527769e03c5ffd3cd0c1b05883ec73b48faa4351cb7666f8d2ba4eba250b093718d100d5db694f8a318796ccc691bad06f9b1646dfac7eab742cf6451880f397310744b157a7cc87a7f70b010338e00dd621513065b1f10896945103e62e651d12c54fcf349924c92a5986180be4d9a6fdcb08e187492895c63942d25fa518424d7f8877e02126c2625463db1e5fd16bd76022ab90648e8375aed018b1dd12ac9d0a826418093250e5501dc284a2eb64fafa96000ae8b426f99f30b31e5115ab6960d401ce6ffd4fd0784a03593a5c31177a05d627ea165f018912ced5e6f487f791638b396ab5b4bb29f10479c008a6c24bb97fe0ddaa72457da42fd761482aa394c1e9c8e9c16fe4cdfd956f2989ba0c37d7865701a886619ffee989a181a1210ae57ccb12c29e3ae24c62c8103a0827852916b01db3f225c2dcf6d423115833ec8b102ac02173fb4dde82e642e502fad549f575086dc2ea5011f332bf85ed860dd5558b7974e89a7b5ee525425e4c40ce86a814368fe874a12eb5361879957150b7fb0deb67a347302bd27ce094d32a67655168b52bf94392fded104396cd015c60267df80c30c0bb5b181a157e2cf97ae4ec382d98d2155055908295e38fed7967b4a5dddc11bd0b7c02af738670d3afd011637a9e799734a367ed730ba1c8476fa0469b5f201f35188e44b0d1d1c87dfbc1b2616320eab8460f1845da9ef5ed0d25e4e7649cc65cb5f659e3a6b2f02a1cb11e63840bfb0176b323636a486564d33d3b95e14dfa5b8ea98eb23a097848195f28b05334914366b9c43b1ca3c0e31324e1d12eb659171d9a87200e225f1c57d070f0d8012372db73dce99781e32b1ea4219f7410464436934725ac2b2e2c586fd44347676129b06a91692092eff51cae383eba9c19728116c36c663023021cbeec88be4f176705570f73be9cfe485a804b319c60c3a2633227f4a10db17e1207a8346ade2c4aa9283dbcb8cdd345257c0b4b4a72344bc0e74a8bff264482856e1841459dd9d0a6136fb2d50681d29365aa3723cb86cdd6124514dd8d0866912ca251bd4eb57b7b7db6d8f864b61cff08d5aa82bc8c09a3b0b4b0ce71e3f8e220ad9b20e26be7b6e3488cec1fbf9d3dc2d3fe9a9cbbfaae65d608cdae3edeeb0c277288792388bcce210ea6bca3e4d31c2b19dd1f55b2d2526b6b15ae697768cb740d48927f580561a001f87550fb6a51a4e04dcaccd8cd69115a98ce82f5886507bdcbb5d35764a567c45caff7bc34f46530fd907484f7853be03872f55b278ed7019126ed457cfc86b8213f54c6ed6c84141cf573c73457b2ea1ccc6c19ca98a2845e4458b2532129ed68c962c94f6f555931507907e5759f3b207735b71976700c942b57a6e0eda6f66f10f8b73abcfef2cbe1a6e1cc8a6a55503761996ef39d0e962d58b4fa55352b10a60dcfdb0312fa28ae9c2b298580501c9b4787f8d1d2f37af36886b7117af4664606d582105fbadb8c5c5d500a4e822325019ed349127c17e88182444fa38018b6baa71dcbed7c76e570340b76e9d0f37fab2bce95b60606ed8a66df3701e2d7edc6c170e3e1529a4dc8290f712f71d9201f4906cf58e4f6fadd7a533c4356c8c9c4c8546da7f8b69185216a92c45cc01c3d4123c5bf626ee96b95827504e19f55a8ee327dcb1341835ce068b37ddb3fbe5524b796bbe881b3140a27e135d77bcc03c28d10c746fc7289fb9020d266afac9ae98a4e12b1d7a588220cbadc525adbeb2df625a1666ab71e05bbec3675a389e521795f3a1a32d804c1e63d0a3099d5d7a30093bde1221c349f356fa470d5811a81b117823cb17c72cc971ceb6be84626972f199d32b9aadbd9cc8f31528dc46114b451469390dc621ff978b02cbcf39ac7de0bee2d3e0cefa95080bedcd6c211810e64d395f32ec79e70724f37edd5555864b37a0016c8a51385199f8157108d3505c02efb2c452dc8cdea5e37df38eaf820022b645b403b92d66b21dd2e4aa0086f5d0f5961398ae78df05a96c7e877e0765210f0130309a54c9c3c5359a13052084d71bc39900c760775f0744034e2e91e7deaaf17e9cdce92e5dd338a270fb034273bc4903e9f6d20c44f0681ca0317b69813359786c6ae8a1bbafbd2431e266b05cffc1e210120d8d04d7cb31638861a55524047221eadebfd2333f119a64939904bdd00f484bfb8a2860e4863c8d060eb3bc82ce87340727d214be64d59e614fa34d5ec7c8667bc3cc2b1cebe07637288946406cb243a294fd6dd6155037f5e8da1e25ad7908110401ef011ae1c51098ca73887ce3cd7316ca023b06bd7cc9fd87af5e41bc390bb20cedc06eb554382487528f5c68f2d369e8553b2afbb9b9b2e284a271c7d6b3517d73f9ecaaf35bd1ae9ef486f168729551c54047124fbe08a51daace0e9161c2807a1244e41d8bc82724040d2c025ed6e69dd811db0ee6e7ad523e2c5e2cfd901a3c14ca44d3f9b1cbcf54625097f734e3213153b0b3962f788dc4491876dcc79b9810b9224058ea3d1e2ad948fdea5e0e24a9c251c0bf899c4d22269061ad4b506f60a363128c880d44da81f1df08fd2ac19a7dfd9f70230e60560f62caf7442c21bc5bbafd91cdbe95e5dd48f6f3e646ec1db82b128a36b83af958e1d3e5f91b745dcce5d48010e125c48d7820c6c5809b29c462f40949f9c59de2f67a28d48ed7bf6c0ed4d598fc286be09dc94319648c83bc5802e1c618fa0820be5cc94b78f771e01e4f88ed48f65fd7dc3cf7ee225aa6564986cbb4d901357a099101b1be5552728cfccbad4809b1c11522f442288e53ad36067ab337bfa8d08db6b018880020b87337a55c8bc19e30e32b444d22ee5718256beb143d962ca92f16a37438c64c74fc7de0ae6c0298bed8f36680f760a7b805396f71ce55b8bfc06439ed0eaf79a22b65b7d45c0f56aa87506b4a5b53d6b6352bb5c949e03ef4cdb7b1588f4606c494fd596acd373ac24095bbc99c648b7433776f2782a0a4df3e221543638112b4dc51f802a8506d0c8a3a2609e60e8ef5e508dbbf69b83e8ea1a534aea99da5d89bd065e7df0ca1163fed6a54e662b17b2de40cb85c292878e620601cc80eeae4a6202220bff842685e90708913fa8f6d364a4c1aa4fb6d8961fc444a3ed35b0d1ae54ba29e597d44130d881806d8a03eeb56de1c7eb00a5ba14a5cf1b9a9f5cfa94fb4c1016e5c084480526a726ccbd1a7f8dbe11e1a7668212cb9853cb9bcaad2d96b2ff25fcb6c3ff892032d47f8a17aa970a9f354213c1d9c5fbbe2da0cc20ec70280735fbf35fbcb935fddcbd4e5f74824e610f9e3a3741d2e7800863ac7d7971640af76eb9be9e567bbf7c1371dc513dc78afe717425c4f9a2d403b1e06bc16a7813ef74cb48432431cf40bd797c2113a42d58e1fd948358ca443a52048e124d615e207883fa0afd0854b300869479468021d03b9a29536f81360ace7848d2bca47aade0cfde053c987349cd13ce1433c749c2dfc5a474267e4e921d3132a2d6600bef8b0d45a22bb3d723132002b941c06369a743e04ac0aa5b612b31d9ac2e74abda39472bc9f046f951010c7dfe12ff77fb99a248a7e7e13b1dc18a791e2f010569293043a8b85d4c8ed38c23b9e9e0b5059a04de2a2ebe07cfd4e05e5b7e3b9a59537c714b49964621f5f71a133683538789af51cd8e1a960303f16d49cb50a96478564bae0746acb8e5e6e94f83dcde962fa9882820146f8d68642b7ccf46f9d148257137516197a04a822db6867fa55a225f5f7f932fd3e433a65650bbdaa7c4db337308880bc05ca9e5101b053dc68212cf3ae7aa64a58703a1c0f60736a2c79b86ee7a1a3779676ec83a0bff706ccce9e3103373c66f2db12923681f506cf23d8ec0fce3047cdea31e826ce21edf41a9eef8b6e26bc4cd4a7a6b07e69a2a9f413b1b308288f62a0951e5afe80efe428ef76df9664e83aadd4d1d50b0c2ee62bbad3746a0a194d5be1f830898d81401ee37c8405312a4fcb1992ff8149cb351075d69cad2f4ddd6c6dcde124123aff15a7566b650ab296052bc3fdba49355af98767a07fcf0b29c24a60c52c0e2bbb934bc46565d169d19b43934a8f19d0f3f6cebc08d5be987b9505bcc9758b1ccc441e231c000802e180c870d6ba968ffbef50538e4d3bc2f0d79d11f2051aa9268dd6e1f07c518ba52de606ae9273277c9bf0d92d4d37519b306a8e1b3b20d9e9641a01314c0026b61f1946c811464441306280114b2d8c7ccda160c3c0c25a9b9d4ed4c764a9d3cfaffbf9407ece240eb0e8ecc418c54a1317c4cf06694a7b64ebefcb140556c4376a6c302ba73c4fa977a02396b10ebe44ff411c80f558ba59fa410131c704d842e80c7bae8754002daa243c6977d668274fdaead1ebbfa86525ddb845ca90b3bfb4337f0e5792d8b0f140c7cad252030e3e4b4a25a0d9ea573e89d7e81c202c9eab9dee5594913c9ff5c2cf5e2d619657e35150c9c5ea0b0a2910bf4250f9b91767671344f8d9e2fb60b2f909d7b8789a46336f441d0e2577ab209ed3848dcdc540311becfbfbf8ca1f354c17505d5673f7183490f0b6385292acf3facd446782b3de9d452342bc8855f9e94b5e21fba4207490d0f4ebe0fa8a4fa4bc256f4feee6f13c4a9766300f5546987bac3093aaf64d0b8d649439e04f6729e82c15e43ff867fccbc62b65f6626508ffbbfe0b031c541ba2c22603a159e6012ca75a30f506a863c8a1a57ffa390e68ae1236d0039ce7c12c97865dc75592d03e6d784a042e937438a2f1c2c1376e93d1391d97a38055bb13c147cb9ab5568b91959f7be54c061f8c87e7f47f59f7317a4754786f5510488e36c6eaa3b647b3dd9ee8f5873df2803a3398657559570da6ef0f926b995ae72961b8266ad97f39b8fa45f908b86987eca02d28acadf776fad336a5b1d660323943e29af0d98f7e4ecbe6b499776a143eae671925ca11d371ff13c8bfb2e86d365c85d39d3befdb4efff76f903f591285b8896a09b3e9c9bb225091331ce5191ed519badd9ecaabedda411c48c6f0ebc2853f7b9cfa996574529dffa1a5189baa0472718d2da754405ff591db49198f5ff021649fcb782cab8ec368be19c156d77870e5628d5e94352fa6fe63124994c55e93be06919881bb036473cafe12be099fcfd84f737d1fbe69fe1ad54d2e69fad83241d6f7314dafd508cdc2acee182ff6f6fe266f59d32a884d767eef3baedd5c53c2ffd81ec2376d613202e27e475f71ff79313f4e8c3fb402ef505c191364675c1bacf2f6aadf9415fc0a23eab3088c54388e09843c4825cbda9b429795e204ccbe5954475812ad8faaecc11bf1dd2919b65fd6cd8ceb55d518ff817a57f5f63726ff4367de434c4a88eb9a033c2529caf079eb7e72f768457ad1f5ad361e5fe56f29bd883feac832afad0a6a1fc4ced01a5a8fa6f30ab772f735617d9131e13e0cc7025a4e97da15932c6d914cb89476f3847432bbbcdce330d86e4573c2468f96d4c804b11b8d0caae0271974995aa2b3b80b386aaf12e1e10acdafc1bfe145632cd34d7f6851e47743e3e2e1546d26ba14a89f3be2059867daad0682ec76369c7332a2aef204fcd776b51e4ea0ff22ec6f8f0372ac5301fe63ea107be7fce0d48bfce3466abeba2d22afa6ca09bbcaabd60b671eec6b0a7ad43b7081b0680569557e4cc6482a3ba1bcd0cc3f85199e5699d79c79546f34f6fbc0dfca596111fd5511e6a391eed12665fe48b9383493a90c22e89c7074ba92e3de935ff90edd10b41a9191b95f194e5866dd2c9f5460a1d88f81ab7163b99c3714056c8b309821fd77135df8a4557ce97fc42e044ad7ad36f57021a2fbfc17b4da59fb33f58e81e8ea88a064ce00c98c43179307d60a4fc04e8101ce115fda669e12f9ee499777033bca7f756b45f2b56b387ee4536085dc3ba6fa3383fe67a84aaef7c833d39d3788fbaf774139538d62f831a4f7e535d3ce3f08fdd3bba198a94635fc81a14c0e8cb8bbc4bc03432ea5708d659f7d18b6c39f1bab977e5c2bf7c4a07e7725ed4effedf0c29694340c053e52411e7d32541224453a3d4f6010b2409d7986925c9beaee28d373c2903039619e8e0278ca172810008c7e4171d5a41c53417a984e2f528409591db7026af62dd3c10c62a3f20afa56ff7149739f688b875b86869f9edcd731d38e026cc9aa67a953a7099e6701b675d2c6a6022db4d30d610708ae674b7044f7eed55ff9954cab2ad55cc297e6c1939744f363f729ebf4e5b63799d45eabc892baf376e32f359d31fd1f4909b4e3de8bdc9afd507480ee93495bb14656d5408102017aab8c88c34c346b390d57ace73ffc7919a6619a4a895167fe1d642d38f4229a2e33cb5315816df226d83196bdc3a98c06b114ac9894a0bb2ab571bd263d7b0915d5287eccd1f710b4392cf0f4f3c98c6ab0ee923c2a142883075e7b30165f5323f88fce3afe0a8538c56c7062fb4d035d06767fb13d6361cb33a252f34185ed2f3d4eed5aa95f7718875d027b99aba3fe6efcf28c6537472879ab89c3dd159b1d2ce7e6898338cc64155ba15594a9ab9b058059b1ce6d892f035ab168913de158386873e340a2b473dc2858e055ab229961f37d76b19509d31e0314a871b9958f7aca994353a8d70937a28d1d55e8b1ee3105e1204e11e342db3b874ad2b44875be264d0633ed15ca283206f9f38bfa0e98db59aec0d744d04a2c34c4eef7ab803c8800741be902c62e6b2e02a38d36644f712260ce2f097f6f0542282e85ea0eab1320e65f5ea3437e79f0379812f4c2d935b3ed7ab0c92b68ddcb518c71351128eacde0642f039b471c7ce68858d44f7411adaae0db0911a82b3afacad28a01565caa4753305023d7f04e8f10d038dcf571b463c2d5a07953669c2143a172c4e1f12e649f8cd29586022f789a239e61bafef015ae5931d9df14cd8a94450fcbd7beeda5b2cd26d70e6b415f09a3bb60fb421cdacccf94869884a00be10a890db10a9bf015e4addd19e34260765dc00e2c1ae76366f59ce7ed6889fbc59de93b8b7a743b1d34ba4296b97a94e275dd4ff3e6c529a1978106f489045aaa3bbe551048cb6a3a84a3f14889ab8ba2ed22a4dfacb67845d981c730dcf277c44ee8a9311762280ca5d42cab117c17af9809cc849c9897acd00d6a5a1b229a1bf8ccac9d37b2ad7081c23ade0ecbdabddbdade7bcb94920cdd0b110ca50b2e2e3d0a7141f55bba681f8f2284d0b774d16c6c643d8e2284f0293b8a1082e53bd4a3f8d4a36a8e1e9b013b1fe451210380fc8baca057430becdf0a2c8865050579fb56149a65605084040b92829afa72bb38453e987ceaa87ecb5711925a8ffd7a3b614150f0717f945a64aaeac9c910be3dab7a523dddfe959321882e9a0711a7b7ac38bdf5e2f496eb8b9035eabca58bc63d7d26f541766c2c057b19c552b0a3db2edaaddea23765ed84fd84bd53fd5be2141539e9a2cd563fca45f3aee314b9708a6e7f41878b36c33882c28a4e330007feb9e3323805795ea754149b1bfd5076c2921cf1e50aecba8e6b4e82b32f7288611a7bcccb752639e6b8e5aa0c39c6f8ca37d6d22eae07d7ff4b514aa99db02ff50798b029b6c0e296ba7141791e95d22970993253c8c8e0539838c932323217cbc934e7d3e7a6701cb57cf91f610249ec4f3dfd2cd7d8e80b18b0000b2e3b88830cd8a4230b2e708cd142c9eba76007a09fbe337126a2778f2851b934398fbc26ad6ca922168bbd71030eac8005d5b0f10227ea188ac5829c6896e42022a3e90444aa0fc5dca79e76cf7dea83b84f75ff812108865fcfb951493deae77fa1078ade6f62504a1ca974415ce73175eabb4fa5beefc4dabd88127bbcd5f4a9704fc57bee4b759ee81d51e9c24fc57b8e0a8aa2bea3ddc5f179e7714f592c741df79d38a6fefb4e7dcda19ebd399df45e7092a262108a592c74cf7d50f75cf7540cea1e250a75e2b7e731b5e77def81ec79a2508fb73ef51ef75d4ae4a818c43d4a14f244f15b0ea29efb20d473a250170ee2be1385503fffeb4ec19ec52cd210e43dea0f30612ba1ea0deb3cb10b73937627c7e7b56bce21bee9246c7adcb36ffde366bb3275ee59a4302b5e8536afc1bdd7a1a8cf0d56bd6e9663a2ba78bd80e88636247ca048c82e6a8b43892d0e2ddb9d65c46184f7831746163aba2e8c23c66c5e47e607649c3a5693eeceef3a324c1bd7510fa9888c2b6494569cd8c1d2184f4858907490c6186330ade030dab88e7a70b0e0ce32e0404add9a3b06cdc98b184b63ccc460a748bef9ac267ee7fb9d22cdfc99b749144760acbcf881cdd18c556380c22204109313ca3881115c6ce8811348f8d0c653930e9a7a5005921757cc7801c68b7bea628e1ca2bac5c08b5a07c68b29381ee0a8f283d110618451c50eb44401061c6ebc6185185fece001718f381ea0805b41b8a9c06b7e82c253c1135b1b47402730516196a9a1be7062f5393ad0a157cf9d113978a38d9d1c287b32a851b0c7a67c3daf4a379d738e1bdbe09141a181840ef009065d19153ed59d65e4e00c3378ce31ceddaebe737e12159c043d993f6bcc1ffe1e373f7bd5d3ff3db852f9366fb825d754252f316d4a4a3c30d9456c83b962a21a71cb1e01c331bfe41edcb6e1cbdec1c949b7114bafded29d56976b27d149c9492f62b6fa97f09ea84ec56d2742a3f297eee4aad6551f51a1c70dc7d5167a9fe139723fc3966f68b7396ea80e802208b66f8594c873bc1b4a9c9387e33cefb9d6755c0bc29ec81b08360fc364e1e6253e1270d8d9e8ca37bad1eec177645cbdd87f7f6c51cb1fede21ff367de6ed56ccdcde96c6de055c9503d4646a579e2eb39b16811850fbc21f1cdf9434363bba7bb7bf16db08fc18db33c5b54039556712bdeecc4c66d1bab898b69038b0d977bbf7c3f8e5266e2a3eded77b3f36513d5c5ebb5d401e105b5850b252f8ab660e3861570a8820d22a00843054a5f9ce81718d43812a855150355040f211120b419be0516629a0083871d5ae09283ccd21a51b0e04a0a8abe2d46e0e418001a3784692329054c70f15f8c41c70ae0706248065ddcc0c17481031a2e0e25666c81830a1ab85186072cc8e6e8628d33ba0481032ec6745cb4f1c304dc463d9603f6bcd8d9c9c9411149615ce48004471e2a0bef7833d579df02ea53a8f7505d9d99de87df42ea67442adfa7a8809b45fd0b30cc26284efee8f4ea994c2f409901888913da3dea41d0033beb59f17617d470dd6a06454521263a52ab34fc2e41601784fa5014f2c4d15b1ccb4a0ff51c0a35f3bdbeefc10fc597b7e87bac4af854988462972f5f4a809f98e488540ba9ef514f25d5028aca27d6d45b014a7d10aafffb096b9185fa94c8ea3e8512591dca0acc0a6cc2780ccaf2ac9b98a947ee857c51a13889fae6b888aec5b2765dd7df75ece4ed61c13d6777ee7472b23ca722cbf37c04f2f7d664ff163a086d0a2491c52df8f7b77f9fe2ec0cc7e25c7c716210088a3dde9af341db635b9367fba1439d1bf7d9717b94b76d9bc7bc11b9872474a9e578c77695df43a45d25919e232f6ee2a69d0677da757be92341c6e2e7de0a6cb32ca61fc462107d4e149a40ce56d5637d5db7fe44da3569dfbc1e125f66f2fd0b097509f2ef17ea12847a7fcffb8fbef72951a84b4a6c178550e2745ad694e889d35baccdb23691c582bff72df4a7442a292a5f87e4e4c405285228a59476dc06442995d4f7bba5f23deabdf767c067bb04a5c4ea284bc5137d3bc1d7fdcef59e4a3f8f9ffc4fdf0a6ca6a31fe44f45a12edd0bd119b1f46ef89d18a4f2578d35353348451cc771dc4f8e732389537243061cc7715f33d3719f93032c97ebbaae73428214355ce1bb28d4c7819dfdee0c531e8628d403f9ecb124c24df5dbb6ce865dc731e011402f77869428eeb058e8e7f1411dc4bf23bcd38943e36335994f9fefd3e1f5b0b7a53bec438eca79943ef9fc9ee7edd19b73e92fe144d6d35139aec9f6ac14bc18baf7ae7da23ef9c26cf93bd3912732d106a3facfbb8d7167193850ba7ccbc9e47f823f9376cd32dc0073fd5ff087c143664e92c2ec919f49e7c2d02ebfce9af4f6cc980cbeb9f32599f3c8eb0ceafbcf77e9ab6696e6cf0c73e9f6ba34e7cef7ff6f6c175f1f72549c15fc89f4c8db2ff1df4496950bd4344534454ed6d9a25f698a2ea5515aa351a854ea3df5549c3fd4a73e10f529f0fb362e352cfeed276cbec7bdf72d74bf8954b6efdeeb44168c0aea3991c5a213efb9667b446599c8b1d273bab18f746466e62d3ccf5b347c70d5a2e325427b2eed717242f116fd17bc45cb39f6f82bf5943ec728c33ea09e308a7aea33c909a3286ec5711cc7596662bae4333720dcf0cedfc2034cd812be41f573a8f9e9ad0df42147adb9f3bfee88cb46842c783279d89b3d535a5182fa68c86261fb1d1f74f33cfebbb12c27dbb30e30611bab850f429b022906bbf18ebeea5d9c9ec86241f53d9e45c51654aaefd1e36f7e7b560b37bffd8dc8a2f909bbf94d64d1d8d58d7dd56af560786718304997f6f4d8fd665fdeeabe87ed01c5af13b968c29cb0ba9fb0ee3daffb42079772620edcd7cf61fb9a1248504bd672bbff78d817bcd52539a2de7c8f6f755f1a59623a754a51b4713b23277d46e4adaef39ab7ba779ab7ba4ef59dcd77cfabefde8d9c04e1bbf7a39538614e866ebec703358b05eeeb3fd978ece0a219208a9cdcf11d9b609213d6cd1da98e879db01a30c5460c8695584c118fe12f8cc469f01233dd2ee5b47bf7d2fdd7e10e18a57449fb518fddf7b037764c75232feab11ba244f5ce4664a57ec74f58a712592991c542addbd73c15eeebb7504365ebba671a8fefd8e87625cceaec31d5893b3dd2221eb6bae52d2e2729a243f58e1e54aa5fc1df9b4d8190a4b81eae90c34fbfcc7591294b7fdbf5beeceef4b62694099989e4a16a1697fb3890861c09b3c5fd16a230733db167f2705fd346e56a5c9193394c9289b78638242ec7097139ce87cb3d7d83ab714f4cd243959c092ef74c9ce49843e23a663a9d64791f7a419e18047e280ad1884233e20848d407b9ff423bb9873203442db99a9333cfd52ef733a80eed844dff3c5c3223438f5c8fdccf3051fbd2f7a6ef396ee501cefb3cef53224a2c579ee775ef794a5b7f6c7ccc71dcb672eaeeeeeeee796dd46ac197fe12beb4b472ddbbc3bc5113218420f367fb9df9c3f530dd30c45c16a374735c297d594d7eb05882e0d24c9ea699118129e7bf79b6b9134b1763f0f7b27b16bbca620cfef4c6e0af52d9dcd81e93a757459c6a4943337ff87dc8516deef414cc4b7244cbc950277262872f206ab9fa6e32c9096b6d93d207c3e9f2a740cd4cbc06930f3e2eb55cadbe9c76b5ab1cbfdcb9e3f5294ee49f13f5d8dbb6b1faf97b64eef77e37c28d21304860d208c109ad0c1ef4808c2bca0813421264d4f8a1cc1b4d6421c5f6c50a94bfa00c3030146ff1bb605fa5fa6ae79da93333343334b5d6fa4bfa561474dec51998a86533b5ca3e7f073269d7bc51e8ad81753760e5ea7c2013279be94938a69673e98949e7e7d0941ec3ceaff333b41e861f8a52faced0cc88392058bfa4379c0fe43336c74611ba3a3a51fceacc199df75089ce5b5b3d12d4f275f9a3099f454353fbf2d19524dad3e5207866b684979bb444ec969c85696a18fe0d7f49dff05fc8f9a12e5d7eb1d41159363fafeac1e76fa6768122cbe6e9b579fe21b258cf97f5fced46bb582c714a8f311f5688f52cf03363e98a14ea67be9ad1822dfc7fbe7b34b599bc152af11d17f893ea8439d50c356cb821f82ccb4a017c7ff05d64a5c07a7f1763e09ef5f37e340cce50c94c5d81350c439aaf6114bece45979f9999a7f4c821f8844d13e94ab3359f5fd52e9648653dce6f5ad5a53b5fc8ee00c2aa470f4f42532cc7befc4c0866cb751e9663b6c60a753123d28865f550a4495ec31ad62a965e3a0afebd7bf8a175a71ea77f9fc8fda58f1a4c8f483d26f5c87fafc8dd29d74651bf999c9c325bfc1e9377b5309a4fb689c4b1a2e1d941193148b8fe5c111ceab8c1758f070544c2b8c0e920e2bad37174bd0e1faefbaa5677e7a2e9ba3f93207707ebd11c2bf0c850828e2b3f48020932ce8095f5c270822c9e00e24a0cac0003f39f9c0fb8a04355aa7e506b95f10314ccc1c51bea66118afadc377341c746868e5bf6d06e6db1dabfb371771ddfdc29671847b783fccf573df528dac47892e8bcd4cfeb6db39bfa0f678d9a527533b8a92fc894411068d07688a595bb0385f1e214ad514b170d096d0773eca771aaaa6fc97c68f387e63d4663793c8d53db40b383d4f7a7bedc89a5be6cc9544f5beca69e47a55fb6787e94522a1e1e43c1c7558973f294b4e8a6fc6e62b9d9e0b5fc80d32dbb97619bdd1d3f43253bb6fcf820249061031a3aea8e1fdfbd8bd6aed4ffb02e2dddea77d160e08921c6759b584a543d9d515573d8c46c86be1d768bf829f13f959d41f8dac83670a6ea73a0cafa75a3c77662fe1c33ead73ba020054f75c03e9c09c2e38bcc8acc402c341f5a1fffd1cd7b07cde561875cd6eff820b688ac757172bec8ece6fb554f37aaa79a1d3e688ccc8ccc8c6009224208eefe851620331842c9c9204fe4aafe6b30a6c3123335a6b089a99e7a14f2442d5b32d5930821b88c5ce112bf3bbe886cfef0f846635e107e4e1710108acc788ce64fea8b307d3c2c12dae4e90782468f1edfaf8353f4aa2714746e4afca1d5d2088d074f4a4cfd4e4c84d8687f18fd08627f20f90f25fb0308131026275d34af56204c2ea875de1b9d1e3620d872b3e1962d59697305c40155b66f9122b322587aecd4abfe13e7509d375544cbb545b2cc11b400759d412d5d344e9a020b9a8bd6ae949676ad6aed02e1038cc463b0181c06836130f80b36011fb117dc0573c15f780bd682b360232e0163c157b017b6e2fb492727990a9e82bb7014fc046f61127013bc042bc148f0115c0413c14270100c048b807de01e78070e01e7c038b00d56a880606c0955e118dfc043ac03cb9807bec23f7cff0f8f80b1f0104cc44670164e8269cc046b6127b8c6503097ef47527392a5f87e24454e72d1f723317292abf87e244748904e689a3f3dbe771e2709131535bb3334473f9b04a2e4ad2f954acddc6ff3ad799b5b37a37a46b253fe03d603cf63d80e3c8b616b780ec372e0198c95f20c86ddc0f31796866713d828cf477686672fac069ebbb01978e6c262e0f98b95e1790b1bc3b316b67ccec25ee0d9c842792e81b5c03316b602cf57580a3c7bb1303c5b6127f05c8515e1990a2b81e7223be4790a1b8167292c91e72ef685672e96e7390a0b8167286c91e727ec039e6b7684e72dd601cf4ed89e671258179eb558129e9bb00d7866c296f0bc8465c033cd1a7956c22ee039091be41909fbe4398b3df27c8455c0b311d684e7226c0bcf44d6f54c844dc0f310967c16c2063d63b1afe7202c029e47607d9e81b007789ed99f671158169e7fb0489e7db04e9eafd824cf3dd8159e79b00078dec11ae0596603f01c025b80671dac009e73b02a3c0f59a0671c6c0acf37d8fb6c8325c073ccc2de8a6df255ec009e8a65f242f68407815df22dabe4478bc2a72ccded618718d1d2631ba919a1f56884c648d603cf63d829cf62d80e3c87616b78066339f00c8695f2fc85ddc0b3092c0dcf4736cab3177686e72eac069eb9b01978fe6231f0bc8595e1590b1bc37316b67c36b217782e8185f28c85b5c0f315b602cf5e2c053e042bc2b3151686e72aec90672aec049e8b2c91e729ac049ea5b03ccf5d6c049eb9d822cf51d8179ea1b0233c3f6121f05cb33dcf5bec039e9db0243c93c03ae0598b2de1b909ebc23313d6c8f312b601cf341be45909cb80e724ec916724ec029eb358139e8fb04f9e8db0aee722ac029e892cf94c846de17908fb7a16c226e0198bf5790ec2063d8fc0fe3c036111f03cb3489e45600ff0fc834df2ec8365e1f98a05c0730fd6c9330f3600cf3bd8159e655600cf21b00678d6c1023de7600bf03c64ef330e5685e71b2cecd9069bc273cc0ee0ad58027c157bc253b14d5ec82a79105826dfb228fc68973c08d6e657f6e65356f53d6ceb0219d363036912813019a1dd7e214cd4d2452b8130dd7e2334275bdf6fa4e6e4f8fd468a8c18b5641fb045801c7d3f10a59a078f6ed70051ba1d53d9224c37b6c8181b5ba40987561661bafd2e9a931ef87e57cdc929dfef2a72b203dfef3272b286ef771d39c981ef77213929e5fb5d4a4e6ee0fb5d4b4ed2f0fd2e2627a37cbfabc9c919bedfe5e4a406bedff5e46406be9f8c398981ef27654ecaf0fde4ccc918be9f2472b2fc7e92e6e405be9fac3909e5fbc922272df0fda4919315f87ef2c8490a7c3f89e4240cdf4f2a393981ef27979c94c0f7934c4e46e0fbc926275ff87ed2c949087c3ff9e4e403beff1573d201dfff9239e9c2f7bf664e36e0fb5f444e32e0fb5f342717f0fdaf9a934fbeff55e4a402beff65e4640bdfff3a723201dfff427232e8fb5f4a4e22e0fb5f4b4e1ee0fb5f4c4eb2f0fdaf26279d7cffcbc9c915befff5e4a401bedf27e66401bedf47e6a40adfef33733285eff721729200dfef4373b2c9f7fbd49c64f2fd3e454e2ef97e1f232751f87e9f2327957cbf0f9293277cbf8f929303f87e9f252761dfefc3e4e4fd7e9f262781bedfc7c949017cbfcf939301f8fe9f989300f8fe1f999349beff67e62492efff2172f2e7fb7f684efa7cff4fcdc9d7f7ff1439497eff8f9193aeefff3972d284efff4172f2889341c421d5ebe0c1847cffcf929346beff87c9c912beffa7c94912beffc7c9c99eefff79727284ef471243227392e7fb91cc901021a1e1f4e368f19b7aaff5a9f153a954eaa78d299460f6adc0de36b940f5290f5817cd557315b98c5c472e2497926bc9c5e46a7239b99ec818292367241149236b641169441e9148a412b94432914da413f9f48abd64afd98be8457bd55e452fa3d7d10be9a5f45a7a31bd9a5e4eaf279f988fcc67e643e443f3a9f914f918f91cf920f928f92cf930f934f938f93cfdc47e643fb31fa21fda4feda7e8c7e8e7e807e967e987e9a7e9c7e9e709490c890cc90c09111295dd12e4855899070b22fe28794b057ac0e2144db138461db0384735581c240e581c252916676903168789068bd314c5e238cd60719e346059b10c58960c03963593c1b28862b02c5a6959b50b58561114cb32b280651d55c0b2902860594a3058d6d2042c8b490296d51401cb727ac1b29e206073620fb0393207d89c990b3687a8013687c6009b535b80cd297a62738c1460738e5ab0394809b0394a41366709013687e90036a789059be3e4c4e63cad60756206b03ab202589d990a56872805ab432380d5a935b13a454cac8ed112ab738482d541526275944eb03a4b03b03a4c30ab73ad0e90d51180f51100eb0300d64712eb0389f5f1637df8581f2feb83b43e5cd60792b7fa4db03e94ac8f256ff51bb13e4ab03e9cac8f276ff5f7d81f23d81f45ec0f1efb838888fdf143043b14e471a8128389f04276bc0e3db03949e0f13ed89826b44af4c134797a27e6439b2474ebf8d0e68feafb7ffa7d7850cb96cc12914d1e9e96c589591c99c599e110f5a87a6ac9809a872d7dd4c0c70c6e7f4d11170d491292253036182176fb41a84d1e1b6a9119679f0bf241ac6505d90ff2416c9020d606f9256cb0b16db533a85852aaf64b1aa75493b5486ade4a358d228460534829a4105248292436ea2c3273d245f356ff6ae5e2d2aeefc522b3db1ff425a5bbb7b3bad111788bdb85cceaacd579c4f6c43777aed3b94ddefcb7ffdc3b54f7ddc7c04bb8cce5b66ddbba7b9465f55356b3bd018e6f67d2f1c114fa9d7ba26721a4e6cccf1daa6fe66dc41ae8dd21c630f3e003f587c2cc83ffcdafc1192e017fe6a9092184f03737aceeaf6d6c942ad7e6c6e62fcb46e16bc33831e8587dfbb35ddf7f9f28fb7ec8e6678f37ffd9d839c325b3471b71f6f83d50dfcc1ebf8eb52c663fd632d90d958b84c05155fe3732a825d73e226c6e3e2fadf34364d9f7eb581e5d504b96e9bc3f5f69d204988e0917a83bc6b24382882151f108fe35407e27c8bbf8fe2c66fef0ff9c3e7ece1cee717afceac5a25a72ade634ef2b1e6f45ea43d4ef6da038c9f27205c5c909f36a932df9da176df9bdf8aa9aef5bf340c4b2af6879a9477f6b398d17cbd5ddf13f441f6259ef8ed7f1ee8e67891d0ed7566239f610cbf196af6bf31c2ab91159cce4f12b3c63598fe08dfdac6723ce99077295a5815e1e76872d4db825974cc7f5af76c6a63e0429f47ea2375ffc7ad566407a4713dfdcd928a5be811f9de1a29eb394bd3d90c3251cd74fad5c2e67b852e8a5288c7c8ff4b8a5e089447adc7adc52f0c49e1eb7ef442fc9e4d936282f0c09a26e5f4ea69ea20cd349063feb0e14f73af4c0baf7de5f070f86faf1696ad5fd4e52e0bb89aa1e2965b1871c51cb99a442bb5641a8a8e5ea5294b52674235cfa1ccfed77cb6a1168f2d0afb9fdc409bfe7b4cb27134dc137773e28eedcb7813fc2d4548ae60ee6bafb7f28cfdddddb38f5fe8c42f13649ef75f0607548f682d5d7c1830d0de9f0b0f075e829c3cbaf336d2608d3b54d57f76c2fca76cd72e19299cb62cb68505b355fc3cc25ea5362eae342ed13b58bba56aba5ca0345504b15971efd6bffd4b1263163c64f4ae38913d31112510f1628d3dd7f0e326eb8bba3dc1de55d9b3cfe9d6da32f3d1e35987ea2a9b8fe335d826ba40c6fd935b17dfc1edd7f8817b55ce5ccc21a0d112dea2472469d3fff031a42449d5fbe6650b7ef3ce7e80c2af7de972fa09793a81791e86c7e31e161177b860451e7cf60d0ed3d71986ad3f9fd0853bd3b516ddc999444d1e5db453349891897f672b7ffa8ff58a3d6b86caa1eb7ef5aed21223ed4fed2aea306d32ea6b551bb90bad6454e64564bbea81f616af7234ce57e84a93fc2548a227b87d9dabe6528b1c75bd5a8aa56602271880e6aa9badbb3e61502442d552e66f26c3fa4071c5fb12aa83d137b8817db77ac879e6b3d6e9cc5c16ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66ddb7edbb6dfb66ddb3ab1ac3fc254fe11a676932ab7f48037d371e9baa86b3d6e3fc4895aae6a77db9eceee04f2a1d160cf6f9993dc85a96759bad62e2fed9a49483cd159123edcb2b15c6f5a1231efa236ea2fd77b72874a72982a0b0706955bf6e4fcd49335792f71927375265ce4cf46fe2cfbbede7577777777975514e3e0a5afd0e9ea96b59773d9cb965db634f78a6541ed59cbdea8a577bf4c1e672b455dd50ea296de0cef0c5198cfedf45d697fa15d76f54fb5acaa206ab542cc830f6a46b09aff6638abadf9cfa3b931a3df7312d4a313af9b6ce7edba8988a9e54c427ae2531b4c7d4ffc69c2a7791a27b68787dae192a027b6e41b3eea6798824a042dedb169a8244abd21588566993265ea106ba0371463e83e752993767d28b0acb7c5eea66cd93da7badc3b993c8f9a0ac00967fd5f4c85f2428f4388a8e53c5201e884243d7aed3a436f555f164a8fde2cc220be40048d5aaaee64720a3bdca12983d4bbdbbbdddbdddd79a6d8e076ba719d87a294db50a9cfb90f7430e46ef34ab70e04393092aedcda71bb37d8dedded35d7de9b534a5dddcd6d1cfd4da4de5c37d8dd2111125f6d5758a301ef6ebecd917eb7510fe4388e7a4db6db8feac0d65bbb5073a4a16fa8b0098de711cdeacd9d494870b9a5b7e1a086772621a1840e90b8d2b55179dc9994a5074f557567529619009dc04485383c9b1ec05cd9a14693aa07122851a354e3d1430f4654339e76f0708325685020abe1a10c25665240ab3c141d11aa6144c3031610054d333bc891c49782a77007359648a92103b340a1f1f4793d3b2091c5f3aa0756a7541d4a744ac2d05031c8c22951c1ccfbe2c4a6e40ca52e499644124b3f68a2c674841233944ca08492349242c04508256818f51c7925f9614616511091455d9541d56a60022894b61ad47a6719501cf1000a29d6a8ab3bcb80620837a088795e5d75d172af783ddc74e9eab5eab942bd5ac647c8cf1a448aa8a30549462c1d01c4115bdcfe3f228edb3f6651bafd333d2c00897c5004096e7f182ae9daddb4bbbb6f70bbc7edee6eda60a8a4d97ff49f3eb8bfbb192af0e1faeababbfbe6a95089f38634c604d3f081c62d67d24c430cce2de7182c26100209219810e206b77feca1e2f6bf86e0e2f6030171fb991031c5ed7772c3ed7f424b226252d24d687c2e08a2045e0d62ccedef56757c392378a2c8edf7ea085670fb574170b9fddc7321829ca65b8820a97276881c0122073140b071fbebeac71efae2495a438d1f7a2046046acc5645725041a96241920f31f8c1e8f6f7aad65893164a28a9e00a1d341cda1060bcf18317517801ebeeee396e6b21e636eb7677b777d33874e8b9a58a480f4a23f450e6ca1457b4e8b95eede1435208b29ce0a90c30497abec79551f9fdbbfb1a8bc52e7b89f1508f6d57afd78b480b6aa9badeb4295ceceefd9d5755dd7b364a95eb7905e8ef58fcf37694d2efacea6b50475108965a8260bb362b24884ac57871cc7c7a34d0e106b7fbdbe3623af14ece0f92094335af0ec171fb5742dcfeb76226e58083db493a5cf15aaaaa241cc8dceee979164997de8f59a0c451a4415994f6524f1226415eae8b7182028e5b82344c5b5a9004451ab79c493a97f2eadbc5e00f5e05758e183f35538f4c3d0ee4fa136917cf234a93e60f93c9c3bfd3dc3dd123267931aab7da146ff678cd693d5bf851bfbd777da2dcb538adc79fec28f445fd37d5976741d99e2935a1287a509ec537d1b5f4a05028d1695ea339cdc91ea739ed3a753f0a99beb953766deaa0f4d8cc3444b59c4d53bcef97418a48c51545892a346b7beee765b23d7def7696b57df7f3a2d077fb6f9a602e142f5cc414c5094a57e030e6ce3274a8dd227726e1e0a5c9b83e3d8957e7cf51fc2393642ab868f238e5d8941e85c8514bef841e81dcf39e8e8e9ea228d99ea328e19e6605d54b8f46fca5c7a30681b8a84777263dfa33cd42b3b48b4ef931462d7bae5756cfab15e4228ec5297f71985e37c5dfb9e889d2ea605eb1571e3cec7ce1ba58722f65bbf4fbfbfa0335cd0f6ac9462b9adf3ab0fbbe147d71d58354fdb92f2b7f0cdcab762c919e5792139838097a420487ba024dc8fa68be55832b9aef075b5cd174e066691a04699e9be699e63d5442238e9d05e28998284c12991b76cf653fd83fc37db773dc7b98c20f4e1cc3ea24c318882effd2ee277a22222ea2f8da335f9270d072e9a39e2a5d7eee598bf3c72feabde9f247a150ff31ea67e8a447d4033595c1b95cd4731fae40df7b6a9bc8f64c0a2dea91b9f45893555a3b6a174aa9c1b40ba95d9ec6929876d9a4cb607a3c83b2ae54fef6d257da5576acb9b4ab94c2485f2e7fd3daa5a56bac999f377cfe2e62d4b327c9aeb8531fa1680de5a150aa1e696da3355abbdc472cd4d39465a17ede9493f3d38491e0115f06d364e5e724274ed2187489c19a8cf8f9a85d335b4210e3f68a6a3426422273d98dcb7c144583393aeaa315b758d63ea24a33637c2b232a0bf5b3ebe9e9a1a7bba7e336eadd0266fe1af4a17a3db61039aa3ffd7e838a20654ac68a51fbc9694b2ddb8d4bbfdd20a2fa0aaac79807493760390112932b294686c648920d433b35df8f076afe980b088245cc44b8a828c9432de7d3e3886957c7d42e5437b931f350388d7671ab29423ae41332a982597db9acc5a6dad8e4d8e4d45a6bc5b24433e95dfd4c8f0f2d6bf5f382f06e6f66a038396586ef2f65bbe1cf7cced358d68ee7bbe3ab65ed709b63a720d5f0fded16203fc326e1c7e04038d62efe1282a8e57caa329ae79fb1cd647b0c8d7562f90ab56bde5a451598f4c8217fe81531b1148661183e50e72051cbb9145f40e0f259ca68e2072cae5cfe0a6cdbb66ddbb6bd6a73c1c6b19a88ac14e883d103e4eeeedc3747e74f0dde9d3f35d43b7f6a586d4f275bdac4bb3578b7b78ed5e4ef168377fb995f4a94ee4eb106efb2d8af234051a41ce9d19d29333333534a5de9fa768233be64b93d5dcea4fb7f5b8bdb67e4f0d350037d29ac267fbb9fb71363106bf0ae471fc86990e27d0cfae5b8ef5e3d4601737b02f5584394cee7f6bf8ef4d8650fe76c59deb1a54b97aee03233b37f69e5fabb5883df242c926ed33792aa9883864bf9530b7edb281d0dfd33ca06acdcfe707235db87bf644669514a14be35cc28dd0541b1640ff54d5949147eba9f38bf0647d928dd75b1a4dd7397592c6bf0285d99261da36595d8f5ff2e757235f49f4be552b99fcfd1d0b11ba56bd2b1db82cf6b7288723fc270e032566ec9df94eaf05c334ff1a1ee4cddc9bd953bb9e6067cb5c9a1a12e653fbf0e3d30fadb4fd1eaf0b02ed4bb9bfde1548519023fc17066fe78574a94eefa6fc0ca9512a5bb566e99ebf9f86893133697b8e07c7367b32594edbd67de6cc95068e818cff6de770c51b8cc854243c76e2746f144ef3db1f608ae3c863979ca9aeb0f65fbdec4283c608feef57bbf7df7cdb565716de9125dc19d61d7dddd9d75255945dd995405d1e5ff38a98ad9e5073ba98a5812154f97df9b8c0325a1a8f67b2a954aa5288792958ce5963928cff352a9542a954a656957698584a25a36adcce4f127610a2f73fdddaff8cccbcc9f9902df89b27ec5673db2cb98c8b15cbfe23367227e35be19a44ca95caabbbb9bca50dcb6799ee7791e0d3b919d52507d0c99a71e6d6ab59c4f4e93098cc9bf755eadabd5e4be39d113a914aea158a2c43d9367d9cd4f59dc78ebd19632c0e09bb3a57c3376b9634c5c34477984dbc5956166513a9ddc31fdf213f192744b50698aa65baea6f872cb9dcb47a8f77ca0ee36f2160f362a7fd94660b3eae674cc639b0dfc3f1d1b788f9a1eea3dcff3746c505fc70695d3b141dd363a79fc6f72a8a53f4d11e2d87be46c910616a793c9f344634e7eef4f97da2546367f369fe2a896058d0aa227664bc88ab8ee3120aed3a4922a3ded10a500a8e79e85aa2a55c1a7ab1cba2ae793ff0ec7a6f4e83f84873a613c65f2f8f793931e9845533c515b825604d1757f771bae3b939133f918effddd0d77f22fed469fc62a09cc75386a399fae3b214df93e1626c9d30908a08f2c6ac7f8e9b3637b5eadab15b8838f86f9fdb13bedbcb13b2d6b7edf79f92604958f18c90c4e761c7be9d64cfa82491ab8c59f4aedd88183238a42849470cbe954c22d995ff768061e81b9a848522d7b9054e07690c3981aa4c173cc7db2a3b73ad6adf93ceb995e8be5f7ac0664bc210a66c8a340173269c850e9975e49eff633490a30777328dfec4fa9ae92906a5a86624c62be18116121e3a494c4a5261b1ac374f4250bd1139934947a88b8c14a495c6a544687c6301d7df12c4ef444260da5222e7da5654d63c01cddf934ce32c7d39dbf74a467f24c9e71562b6a79e4cef726ad4befe4adca9d3dd218f1cd496934cb1c4f4b697829f2995f71a309098c96a6756c0e314b465e78e624039371e77da82cf264dfbaf8667f1f3f6771e0beebe65ec80a8c7bb6a515eedbd2e72cc8a0d84d9fbdbbbbb9bb4a5bd9dac17cab1fd472e5d4ab32b5b9bf1d338b2ca79b2d895cfe1d8e6845ddb8fc1db8caf9bbd14ba9c8854aa84841e65fcd51cb9516b5a497d30041b8a1f6fb5f6a8f8cf3270aaf723e907da3cd93ab32032c783e0b7c1f3f018597c9d5803d529578f331155550b180d8821460680856547e700a2ccf4554cb4a3dcea53b65f8788aa2ca5c8223463e3ee2d5e54eca572a7f0f754ed737df4aa943b7ccc1d9409d49528cb9e52a87476065a0f4bd3dc739b73d2211a424212a90795ead93cebc9565fecc190957d4243e03bfd9711cd7bd731dfd2f4797522f9756ce7233549dc8b118b5eb4bbbfc68fec89ca4316fd1f756b298ecc67aa48d458e5aba2c76a55d8e65fe5c317f7ad61ecbf3708ebe2c3598ced2b5f6d2467dd4ded62d89e9baae9b39a533293dc407757ecf668bbee7cde813f5bbac47fa4dc91c264aa48516d12f3446c1cc688d7aa146f488225dfa9d3795b5aba44b344687280df48a2ea34bed9a495bc4b44bc8a55fba0c2887885a4e24262a38097ae2c2d387a15d32b42bf5f4679032a55d1fc33e10653b8fefe720f89b05bfb3dfcac71ef1c3a70995709c8802f733df4d4291a65f4aa750723fe3851fc79a5704727ebfe01772dc94eb1cf840ed16c8fd8bd25df013c71e69093c427ff7f37220673d2c3de72d21885abaaceb3aaed3323f0e14125925f008dd538e1ce9b1bb28ddddc49c1ebb6fec69179389d4b38ec90c0be538a21ea6e5cc6aedbb42739172de9dc8443d524ee42c62cfc231b5ec19d1a5b276cd1c8b13f5ac5db369d35937650279e7082e4e673829d576b60fc34fd9796b580bd34c5012eaf15876a471f9b9ec71a15d5c1d47ba079c79b003c399f203a09d37ec2e87e10cf8813db2fb10a2eef8f9359ffa1eaf87417feedbc640c52445d4a756eab7adef333889f3362ad1094704c1f356a2d2f72831654bbea9fadfa3c0ed7bd8efb3dc7796e53f6b2a13b564239aa41ee79d4e527a9cc285f3da63101b6a5957392bb19b850598bfd3e3f7c84a33cc16bf938b9dfd3617d3dcb2f8000c6b9973d471c42563d9714b26a271fc41fcdb47410f53e07e86208e7b4e2cc31b2e7d2b33f4c836d4c0b7859bbf11abdcfccd0360e8e87ed4f64d0100434717004347d7c52d31000c1dc1aadc885dfabbd0e71bdba529f58196428f0dcadc168ff4d853469d3f513507b061fe914d57a727c6982133630e136a4b4b4e02d8420b330be32500368c29ba5b0a1c5780eaee3ae698638e53cfa13cefe6ba9b4688fa48b3b5bde771dbc68965eded4114f72c14ea3b6a25314d66eb34eeb6b51b77dbfae96e3dc7dd3c76b747751cd775bfa46fc7897dd4637f8e15b5f4d9ddb6772ced1259dd73cf6ad2cffd6f449ea55d76360b598185dffd2ca6fe9b61932d3362952eb5a86e1f5ab9522cd0dfe296d0fbd076e15256a8942d5299a18a9472cae54aa1cbfd6cce2fcac610650af75f839cf5aef79d8dd25dafb9eeee58bba6ccc999dfbe89266cdbc424596ac7eebcdb57ebcd56f7f68fe4e4f446ba5bb70541aa5ee4753886e3ba8d7af71c79251972671945495cfb0bbceac5f11161e3066d0cf970061764cc7cb1c6510e64a4e00b0e383ac0bc804711386af9979c302297939cf08113b38614618608d304920faa1768314616242cdae8a291c070018f0d0e3592482046a53ff90a982d37f7eb53500b0e5f6809e3f6371751f06035d1e44d2fb5d4c4525251d28bf273ef5d6f35a29a678bbec71efda6da338aa09674ec51ca0c4c8b82d29ef908beb9b345fc21a6cea7f30b15f2469db3dff99b3771a7e79cf367e9cdf9f3a8afd8e87b8fea0ff50c54c20db5f4bcef9d1c7b5ecc1022dce3d84f38c2a262cf7dcd2a132021b35a528a060d96fae1122b97134b2b979362e5d239a43667f86644b047e619f1871cb19a11a833896674670bee4c5242cc5522e96e4fe48bea3d1135aac72068a58c2e90f24dcb3326ea9183a83cbb350ffeb7e36b97eadec315aa30955b7ae04eeacdcc07ea999c5f39512d11d0f61c59854ae6b3f89a3ccc53c4197ae41fe245e52d65cb9e5a69ca29b7f4a76e65f5999b997b35d3df122c9f1eb4a14525a4fa9ab712c60026c4439cf1014c88c75711e105b09ab752b3c566137f88a92ebc203e79a36e4f435b4a711bd1856ed1076abafd185399b282543388c7dbbc8d28e4bd15584b06157cfa4280a8fd655de588a0f6ff8841dd848cc880073360558c84b1c40c6035cfc30a8545c0aaf0b801ac46ece25f1fa83f17c0df9e636128ee3c28bef064a67183549841adf91d230f1e2af44879bcea9fa05036dbd3e9e27ee31eb421d8bfd9ef39cba4470ad41b0fbb8122cb28134af3334b29f0431a2c331f6ea185c1be40639f883aa8fefd43347ceaf70591053ebda0f8a447ea82acfe139447279d61b41073b71db6a68716f6e0f329fe10534767299ea4472eb925535e8adf397950cb890424be9aa82aef039073996ee1befff302a647a8e4acb486552d99a2110000100041017314002028100c888422b1502820935551f614000b8aac4c70541909a32447619842861063003000040440006666b64d948d99c2ee6b9058bfb721277fdb76a8aa23ca06bd1ca28ae4383c658aa2c382f9d5731134e16eb9b2a16be881c8851291f32ae795747ea773889f33628c4124a05ce9f913a620634d3fb9d8efa89bc55ed8a79d7d49ae9ac05373a588585057542d58fe080c59502b2263c382d3e11956190cbe7af3993f57186786a623872ee3eae928703edb0705da3821166d318771e07fd0381a4133a3cfa14a876c1c364de0bb1c6b5258a7528ff74e1eaf16443ad87d7b3e7ab0169352f892a546e4d1dba496948705811da32c83459927e600a4316836e56e858055070854a457a7e68b4898769ecdcd757421395f2ceb52d9c7ce715a68454a02987d37cee16a835666cae6c731a3bbb4fa4ac7a58f34d2e3ca650fe57d990e32602ab8b0ac0a6154f92c45749ca9df1039f3f7a6b3f68c0407a79742f4e286401ea8318aaed356bb2b8d702956c7e71c6da77a50f3a33ca9270e9e9c9ee718eca48b3cf7485cff54791fef1e0017a677d59e2324901b84a9c2dad0fa2e2c70bc0ab3eef6698d37fedfaf54493e3e10545e6008faa5c9af2bf944d9ca6471361d7b18c9b02bf39d08affe88f258f50a29903c3b2dbda4322e594493ace5780f6f8c232c3a033031a0fb32c063ac04e309e630eb6cf24c070574c78b597fca948d33a219c3c132e65a3cb190dcca8318803717f0d9b81a40f0f5ba509983bffc00a4d2e215fcc8d5345f774764866f3ce0ddd280f1e20d06cf49a59cae5923fd3c43d585711e6c07cb22a50eaa76c6683bf27e40e17fa08b06851640930f894dcd4a52d2e4aee3a491825fb0d36bb0bfc8c247687523843004f7f7eead97d41c6a2183380b62bf464a6735bb0ec460e799a5bbddfe8dc563886b1b0531abfcd01e7e946ecc5d3b34a69ed0eed4d3524733d1483cdf6a9e20ef60c0ab89e53df5b0765dc462edade1e78c66e4635bc28a3d27d5ddcddb9558e8f93fe3ccd390821fbcd4950cbc30db4b80f3a02d7b0462e6c28b2dc1e0800f13a9b5939981936872c00b8b0d23f4be4d674500038990a1e9424984a4c59b68cf6c6ed75d2b4a2e3aab610000a710b5b72140ae4b716e2c543e16bd2ebaf067c665121e5665c9791af2154e886b68b21bfb3a3096e42b75dd53b5f84c476a8836bda30364e2388181918e907073e9ee6fa70b41664030bc26e269ff303f356ae37e70b3925cf3b965fd789c2de95ac118d2b356abaa965fe3e8a2371c6d038738a7aae3c1991f668db81739de981cab52c2e16aaae7eac0d4ca056f0a1be5a8a338aed4c6b7c04752d5e9502f783e9b75d24578ece435f71bd2f862e183b633c8617b7c7826b8c4315077c3fdd652a66710192e2b6c1bc38135ff493c8040395bac1151ef0d0131c33ad09bd314cd1642b3ad8d96d74ee7baa8aa9779bdc4bf8d1f996f9b3f602158398d999d8c194d51bbee7e09cc7f86c22f52ef835f8ab6c0fdf9765e6e8162438aff9b5ddd7a9f6fdb07c3b3713c4e328cc17795b8309f52ca7da0ab9b8f9e5dc7d6af66720e10dcbf8b86983cecf215708f7962d60da6bddf98e64ed4943a08c9667fb5d2e285c7a1e05a84cd70f419fca9b635e9a5acec7892f324c9cf78dca223e8081c400b8c7a3c47f0212c644c1ef18ce9083157b0e519241997712b1dd0ce016f06942fa2ecc976c66c1b5bf3916d35051251402c204c641f629a569421ea276dd79fa51181b063c2eef8937851be4475dfcf5c711034f58fd8fc8f0cf7c7cb82e0d7079209175a9a98b975138b701a953feec8e235cac54e7958010d8a7be3af757d68533d3a6f6ac56221951bdde9cb3c54d9b98a36bd8c36ddfa5adac2996adf829ba6fa4322bd90fc764ea72a455a49c71cd53b5fa70f4855eff6d3dd22731f3037f030859030c7bd12e4199579aa23bcd3063489f83f80fc254593ac73f44c9f43e3f874d290373c4dc3bcf9d2ba47708a7974404d6efa54d97850cb7e691d27561b6e0f4539978c86df43f1f0a8f162278ce4cecaf8a28156dacaf168ea8c9bafe1a52494422491c04cacc616931f4f383d5b1b6580b4662f933d59591005e7b84e789baebd67b7740eb983f1f9fd45d0733a040d067000349d7fbaeb0df024045a3d6fcaf4a1522b292a007798f871a44d4f4807066c6939b0da54b53488af0a8230762f204306b901a63624760e587c66e18a1350a1605a0f3b4f0217faafdddc1d89c3b1c72f17e54e5084fad7c76505e79df0eec1172a44da9b059765148ec80503156fe494ce78a414ce2df401611e042a3e201688559219ad924133e827d3fffb0e91e26f8e4f4117c9dd23156501152900f73c385ba700c7ce7f1eb4d51da9ab441e22ed618ed8dc141ef757ac147fc28fa65cc67cb89d86b9ecd2c0e534ab1f4708f111e795efd0902af1b2d1668f3f02a29a5c86a491ce8000124e92f51dbe6c0f6025db21cdd7ac950cba77a85249dfe2a6c077d559aa5bb044aaab2d9c6eb733a6ea16691851e3890d2bbf9f9c0f0d710c406b88a5375822dadec50b43aedd5d70468584e651b79a3d5b0894fb1ad9f4cac050f4d7cef22935bf4611780e251565be1e2e48bc879126a6c5b25abadf2893fec42c7021d2b6d1a61f95015f987954f55251a3d2d5546c9f638d94573d6880a91e65e3e2c20f73f6094ca74cb0b423aff59481c3c9559a3e33cb9e0c1eee42591ddf9a1a7d56cf233ee7ee3923420d1f2ab64554dd26a425a32e0ef52a42caf8ad5fdc88b065cb3edd34b8ccd78789dea6d05d880720dfa9114d382f6aeb7e09ce49ff140c40708cfe66ec74addab0b6193ada306bcc170e460dd022474d9e854d9fb52ab893dea9168e94f366dd76356e8b769b4747d5dd33bb3778a9b508305f378c1e700e2170eecb04de735db76f6a5ca05663fbf206d9b3b6087492b7e16e7e0a5bc1a46b94c9e292a6509ecc140b63a710efac14844d5cc3820ba9f9eaf2996d05582b669a35a390dcd2ead4928f035cde7722cd1388aa40cc329825fdaf122883bf1ef8ea630112045fd72ac455e4a65382c2f5f5eed77b4dda3d6b8921f0315710d6f8e9e225421153dc546f650f5d690acda7f1e9aa7683625b2e9bb70875626f09a402e924fa337879c33c960660e9549e429346ea142a068ac9fc104b187fa17fffe3705a61843bee01cd7942e031198cb9cfc15170d3bcb01fd8fb5b6af68d361484679ec4c0d86661510720f539297f2de2207cc38c8605f230a5e62cded7b7d2911ca355671e4feb6412de2d62bc2536ddf0e819bd423265b13639e18d570b36b27d81257d45e807ae1f286bd4625ac5b1fd2fcc409f80461aee224cfcbdbc7a5e4a7f2b9b95832821071f86d1e1d3fa4b0431009b07ed03570cfcdc7b5caf7886174cea044771ba87304ae9ef1896becdd14229d44fa11cd7d128f18f6ea928347d9d4d358ffb0e844b090f3cf51e38e4cc2ab03bdae0fffeab3e562f394f64b1104006fcac6af72b9ef63451ee0e5b283722752d7ccdb51e9895406a07b327b264dc1af7e6cc62566b2aa7b4b3294d2bde26363f74a9e6d0bb532b7f880fde66696068ec36d7cf6c7bbf9a3d6de38faef131eebe1a8fcfc7cdf738fb343ebe1f97cfe3ecdb38f83c6ebe8fdb77e3e1fbb8f93c7e1f8d83dff5b9981b7cc6f75d1dcb8393eac7c031403063d0ce0892b13241a73ccf14d0cfd2efec9a0fc711ff7dadc077dd67e38a9fbe5ee0bbeeabf1f17d9ce6cf6b0e5c97b88fd4302e717bae011d5069757649b51935d9537251a9b3fa76aebfc524854d57336c68b86a32676545e1192b3743ea17b9157afa40641a514d5bea8852a48a72b8ff3cc32ff6abdffebaad95d004ca0f76cdc7bdc9824b9d44f93ada15798f45e9b2d48832817a34f6ff4704529d50aa92a7729cc17472a94c9eea08f0f6fc6a9738d525e23ba9687ae3f3d14c10cee7d216efd83c0f64f3fc38be113ae21307c121726a679538640f58969e238b2022b099e35dfde7520b8a2435bc12bae29375eb38d80415537f55d0978c31b5e0bb369e2899ae8c6d0d5b5ac10ada047cf93d689d7eb24759f92ff075baa469c475034dae04748f360a2b826ccbb26e23e49a7453ef155fe0bc2aab7e6c94e27d26475dc53f6d579b543ba6cc1316a714b46bb3e6e8959f381969ea0dc46fe9a53a7412cd70edf80525c512eb2449d6d22feb98b1e3a1f9ceebf6aab876019a8602617449fa4bac05a53b945f3c9f1c0b99436050825ba9fda93b196e0139b5501f674e1928ec62d1bd5bf10c19278bc0fdfeb067c3ece8ea9bbeddf172cb7ab77fe30413a5dd156eaee89672e322bd361b8cd4d97987f43744072a545d06e5b324f303a4ad6b23ad9bed323088a63caf637140d5cb094b6c35ba2753fa93c9a4c3b21ccc32b818b2c3493d80fe16e48913af070e69def1a3c54ab1a5df643f8e105a5007795d582105205625264c9aa53deb6ac46c65462b8ec2d445ac2bb880df6a9fa83cf49b677b90ccc721a0565ce08c5c698973d31abf76ad4a01d89ddf7d5137d3173bfd386744e27adf78a9e8660b68c0638f89864ea9c6538fb70d1b7b840cd991523807754146dd09101dd21fad2bfffa100d5305ae676279a36c0538849c3d81d3e467c02ea2f803052634b06bdf60c247a0fc696decfb373805095a5bb4dd8098d654ca5a7bdd2b191a5764a72d0267cb91083a960a87afb8d06f8b4b089480b21a15d4441dcdcd90f3951a7ad2c0b8b3b26d60a41270bba4164d26287438cda0ba1bd83d1deda30ee3e474c70813ef4b8944db7b53f88410674e41aec8fd7a06c3b9fb8eb4c5fe970c73ad694a837ff7173a3a8a7ecec1662e614b0653cc97efafa486f25ade2aced3c2ab1e2fbf04e69bdc2229f03af60208ad80c8a136ae5578f082beeee21cd6a0e82d7f87b790e206d94784f74a7072f421b5c43a880f0727ef13a580c04c72702f3ba26ccc1150d15b891ecc10a92c409c5453d236a7a2978e9780f894d0cedb528ed58e6f4193c1112f7d012798c205bbfbb065cde929b367324dc491009cb3b2edb9dea31f6ebd9f7099bf8fc9a780eea243943e3ed47e2562ea355801222d297a9897ea8b74913ba009052b120c09581243bceb6e15783527669cef9b308a09a3d32e2f4c944de92f01a175641d3e355ab09d76054ebbe7fd0cee79be2949c40602ccd08318bbacedb81ba6b33d65c9a22df735f2dec86c4cf96b808615288a62fc130796beb7a84e6272c3816fe7fc632d8aa29e051337332400c5cf453a206734151f83f9ff2c09c6d3b908f74205d4928a35b777b4960993f4688d2567678c6b4d5860d3cbbde6d01a8c749a2a2351fca5559cc250165cc29fcdbc65779583cb9557fe560a4e044127b5906345f34dedd865a6a89f6360f4fe364c5e2135c5e2315a374ea55863fd21a82e0a2363fd990a2f3d9aaa44ab6f325d25e96700ae41f0ffcacd382ad1c6043fa19153a286647c02d4c892fa8c5636b2acdc9f94ca7d86ff160b28b112248bc95292861679348c4392f5ba76afa2bdcb25506d705ae1eb94b6585aca22fc75488233fc06f525cf0306dd201593da93ec42d416ca7762d5a23c1a6b55f83a2a8e369124e3bf491a80ba4c2f4e09a68f6c9d691f1f6ea8a9cad3aa118dbbea5ebbdaa77d0429314c60bf3d2cc766f9b6db3ea2579c2ff5dc1d4ff96b73a6dbdaad7b9f43dd3319dad855dde51f8585822696afcdc1022b821d584aff527ded7f878e6efa5c58f08d02a811b8e4ed964263ab5a8c73cb85b4fdb006546bfd2a7bfac87c3761ad52679935fef6313f61223fccfa8d21f27aa575a6a9062f51aef575753bcca8277a920d0abd3a8cf895bcea0279e083352dea5be116dccabd29e9f9dd47dff6b43050c0858e6f198358ebfe7b6f738a9edec4edefc4636fdaa6b68d35c1dc2a06b5b2234bde35eba58186c7748df5b8d3534112dee157dc9063d0d0902317139285c9027edb4d9e45912a3dc9be6ea6843914849c49c37c85219e1a61c39de05bf8b0b530bd6fe888b45fa4bda7010eeceb8597688c22ca4f9e7b6c5c3396b83c386368a9423a14acc604dd1c425df08f63e840c2f3401cb9a0f10b4560621cc58455d85a3eb21b0a6e6054ce9e15f8847a762447465c2e8dcdd0430d0e8b291a4938bfb60090646ae71fd67c8dcffccea40f49d236331616194990d32d22a96f422922543f1f90edad5f7fabaedd5f4382ffc59061304281e0f5441462fc5ea0b75951f35ef99440cbbb09a4a00d3b9833c441a1bd9f53f8b15c3fd8bf9bac179eea1243cc5d02d97618a46739c8c14390bae5b492751605af761dc3a0968eb1d15cda83899926a09fc771fa7541712304c8c61c82b68a59758855a9ca3eeecdba167f67ba3f07bfdfdf667f5103b8d33307cf955457d34a02c3c306f872e235702ca1f04bf093134861289f28217c013a656a13f8755024f262b5b707eb24be39d870a277c71b271b9475d6c9398c74085f96ec160c173c5533cedf27465e59f9fad8a16efaf3209feb32eb822f6e3eb012813f076a1d0f59b2b6f9feb995b039c856d824f3b1e90ed5cb71143493722b02b60e69aaa8e73d618dc3f6708ce70dfa4e658be1ed8370c13743262f7abf68096e681a083eebf9430eeff5a7ec0f24fdc39866c2302350158340da4e5e16e3a65a9bbeb04cd2f77273b0caae511cfac8af52d9bd818d70e17acc82dae204b44a78a523ac5bbe34cd6675fc67e111fc23b44bcc320c19f05ecfa0c18995c42d1e895a38da644bbb7e0e4a96fde84b57f84e4637ffc006afcf55ef45dde6cdafce1af866a8bdd668b782e2704051129045c22c7019b0fa4030847dc7f56583f294e141c0c16f12d69e538e0e4660618fccef68f2729e5876e2d02790c6c0e0b801e0a181583b4c2e7736d6d74b872ad80a426bd839475b1b049cfdbfe13a8655192494f2c5bd1c025bd5da89902d49f000374cca5501982165432526ae8ce4792ae5dabf65b02ddb2d6506543b9ce2fb0c108c1008951b43c9ebde834067d093543c4aec981f3e88e53134288824786dbfcbe0444560714a4a877a74604311be8fb9afc3a2e6dbd153d3e94b774974e9d59313ce3c16d9971b6d097d3b062476f70f8b9ad1940879c97323a1933828650de83b06d75ce57fd7c5784ccc7328aee744731d7bc380dd8bd2ac20234f71c70ca2c8dccd74fa9a875e42befab1ce132dedb4386489cbec315a551ff211da9eb356be94349baa72c038371bf50ff7b264c0971e44a83ff5485c096245a8417fa5959a9b141b454a334b787d8229e6905788a37d26ac0e862f4b97b422087b6bcac65a9fa8802abde88229af2e612b04942fdaa802eebf472757ec86bc8acab76974dcc3ca1f6553d49af81367653eba28298763250ffa0b586a44387c3ec10ad48da3634b8ef29472b20a5ffeaeefd2895e41ed97cbf0704506da4c7739240f6d87512d6611779980dd07e74374b46e4993d4f33abad0bcbef36feecb804fbe20ba32c0020c2188c25d939a8ee4232bd13a3e3719b82d72a56a5bec35aebbab88676320501a399f4409ada8c8ce4ec37967c2f5bdab49b0e57b4e3a2887b74045ec73f72144a1ce3125bb570ab8cfd71a605dbf77240d08705677c4940820502751f752fa5aaf0b2241b0f0fbb0b8acae5bf4320fde3a93457fc5217aca0db90f5139e547d3d610712cbd6ce47eb207bdeba614fe8a9c1f212c8a1c2bdbd8d4b835fb12a80b41753ad724461aaabc0790896397e56ff36cc94a3e607abb695e516aae75604b34ac498774da1dbd559f9a1bd91054238e64c729c409f5fdd49c029bb9c01fb76dfc1d8b6307d2a277390e493fdd90211bc141897d534b6122a4efe3fc243361269a4090f98ba74832665832c0941560bf904e3aaac6dfd2acb6935e07cd42ac55e3e9923c30680bf42d15c2b15fb161a7a5301ec3b0bc1fa6141c392a6da260496d0e8ad8de6d5c8211c6e0bdfe6b85622e03602c26228be45bdaddd23995a4df361e8170ff33cf749d884e38866947c076ee7498a86bc4a55fad1fc339a11725788e3823c0ef524cfdf12502fa501c851ec8fd97a8e768134ee30886cec1d14aa3aff70da50edcd4b32893a994990b1d449e15b6bb8c8f9dca8657a624c30d678f946799e4bb3983200cea73dc5cf19d11a969103b0b24cae67aac398e40cca5acabed5853162726c3aa7e34986a4486df3d5a67d85fbf3849bc7750ffaab625ca22f27b73960a329534cd60dd3a0cba2620c2fa3f09967af8075ef5e4c0f257978f0a2f5963dc185a5ea91d4b31addc99baca637b41ae864edb6377295f3aa0d07b46722d38e8db3beec33507542c2ae3ccc4024626d55cded18e0a96733c903a2c084876309e3f00fc307723f4731d187c4569dfee2c5a0f0fdbb871690758443f5ebc363203f5427c45e5448f779321888ad8687a907df97082551aef75bc436d6e9bfc53293219c025f5671fd1af0455f004e68d7701ac7873de392a9c1aa8ad089533d831ad66312e1259bae1e368d3d1703c764d285b9310c40c88f1274456bf641fb3d996ad65919d77c1b90be006e6e18bee4dcb1ae2ef31c39980ec9e25c6b3bec4460be86b9bcc1bc0a476f67ead41deac318515c561c3b1b7f812fa5548a3fbbe8878625a21df4f26a759a2dbf5f2962adc7b9074c28e796fef72f5e069886d9bfdeab2da21a7e084fb043a77d526e6485c6e2f56c75d2fefbd925803b8b37dfbb08a3088800b5b245b9ecb320f775df47356c466f9ef2989c7ed03b626e3f35b1f2c01117892aced75e87e8e965527cd0a9507e33a2f388060f78fa47a929e7441b8eb1fa4fecb63bcadb881d08019ea9f69ccb5073d93976f1f2fb39c1f3e64ef5bc2e5235657f9ad6714b99ee19afd4ede4dbbae769ea84b4da014e78c677d8d6fd98d45250aad594564a6bd83173318d81a8c82374deb7b86d87e05f638d2c3b18f574f4f9093fdabe5d3042f7e439055f7441a6a8096f2ba6cd8279d8960eb7c3461f7e190a4e6adb3392c5a438f565ece17a78e71d247bf17bb5e46e64db1044b24f70d89cbb4b2a4104f1689d957b7379df4d3252a94363418712e7593a1b604ccbc1b9b4f9475a56402b92df41652c6280d3714f63fe6c84a63e13e859447cf1f7a83efc16e20438f19ae14ff580ec736aed6c9f9b784ef3d9c79e2c9ec501f91e70f87f262158f7b703594b3b1a4df0539c0bebeecd0caa13b3a1ca791eeca163a45327579c1ee534cd06869bdd10442d11c82d6c311af7968e1dbbdc2926840bad24e9362c5c74755eb74c1efaa6d84bb1a90805d0c5aa6b63c6ffbeca8e17757a075d9725e36976756a60931f852f8b2328909dd9c740a0cd3c1305a0c42627319610ac1535a4685ee0c7825e9087218dde845b10016fa5b025bbb0217a68bb5e10342bc246e988bafb872b144d962c91ced19e7672b1ec424813e8b09c85025f0ad5115519607530ed757dfc5089f33f419d8f5d63514df6cfc51335bdabb604ed1eb29ebbf5d5de54f3975984efd820b1a1b818a4e5385c7bceafc4ab0fd1ab703cbe71d85a577c7fcf711310bfe4bfa997ceb5d20080ffb3ef312d71620c4b3a274b23a22c0f105db0a32fa0070fee82826f731505d8ef7d39434130fb961d12dac19f5fd340833e6fec8963b051da5e77ca6b128e4e905443f34547d1a407bea6997e253ab33fda64a263e23b23f01e6a9904eedc0eedfcdeff73cfd8b64ec21b72f69e31c9ec6e0549cfdaa0e99b6644c51d45954a9666ffea4cd3ec67b3baffb9198c6676592691408bf311a9072ebc4b05e5ff0e3c7aae3489b036e9ab25bee8842f16273342d04a3b4858605d637de58573caf981dbf5dde16cb767018ee493a12359a367f0fa5bb15bb556621492aeb5fe1c0ccd5fe4d30ff2f9e3350f82d93d454197a6c01735bb8b0ce83a73628a521a1588bfcc3cf495ec610858ebb9cb54f9f0b6a067a079859b506da6402d30e9689346a2890fcf8913f4acee76ff7ea862950f9a031583870b435030657e2bc0ef13292ef27b86c8a98418feb1451b7ba35ac788f6d1298789b6881a5b175541d1598833dfa98b41345759993978d478e9f9c3fe39aa84e3386877f79b5006e5eb8e9a2407fc45ddddc8e261508066c4b1fdaa44820f1f9540fcc4a66c1ec328a252863b039350465684680d0055493a3fcd104e1404558dcc944003bb1130d79e1bfb0a8f65e1308339b0f539520e9a05dd86b8525c0afb586709589ae10caf6a68a73de07ad284358b2022510c1b6cddd14980d53d7eb71b4593d2d09201c92bf56e15f9bceb9cbb847891c2f4d9945a5e7483eb583b70047cea2933db3beb2365149e1fe14acb61b43f3510b14c012c2630d18823bfe9345a45290dcb901cf9c0ba2143d3beb7c929710a3a02202f304d40c26cd4bb619c519e43534f2032e6d16bd9d821c5e0a275b1c4315b93faeb80ef5bf08499fdc0dc4a41fdbe704357f2c371f7fdc05928c8de11e31c50c6af2859f488b88d5f9286839224e7e930ac439a40afb21e323df0c07a003fdfd4d74963149a19cdf92d31561d1c46482c0cbfc8588a886119c60bfb1cc94862f70166a03d3ad3288e9fd04cb1b22ecb065e6fa8c1303834272334985076e2dc1af3d1a31b25f330f3c6838a11920113f3ec20cf5af0aa63ebc63c9a6d21259d333b3a150fa0a6ee39fc5181895612388d0bfb7a01bf0e24da0b0393b08c61a13911db27c2fe4d17c78ee26f4bc007a86e94a1886edc902ee07f4cbf017e3bc5927cff4e4316673a46794e992adfe00fa1743cb90f38a409d82c7c062e25249c26c8c5ab32670c5b79e89666ccaa123346cf62f208687686566434d7779a28674039560ef5ae79b09aadc4250bcea899adc1af1846c7088a4dc94ca3a3e0490c0d8275971cb64710c72eebe266a7b779069fcd078708e6ffd1a12ce691fb981d2e75129027da921ce92ea0bbe5b146ebf1f3f11312e54c2df1de625a2b1bdddb1f0b6729956a185b3ed72287bafa9b96e6e5cd3dcb1bfebbe9a321bf8006d3a86d1daa4514756d6626afb0b3df9a5bd85225bba8b0579f53c5d73367deff12238ab4e445164e37891d0af83690c67f78d90d44ca7ce1ccde0cad0d08fcfefb7b013df089ce43c006451b955c2259ddaedf6809492b5c99cd5893e6b220428cbecd74e89b6273ee74eadf682fd546992f97f49a0f031196890b5513c72521850277e7c091d708423ed18c528b0aa1c6941d151160516e7a69fcafff5ee58059cf668749891804388114751afd95d61423f62e19045c78d6467ac036babc9aed214d9be9ed565475d0562a581db0f6d9d18cebf9cd0d845f598023280cd96a81545df7852f85c24a8354d2b90ba707e2c524030ba8d0ffdc370eece9caf793a20689a1e77adf7b81a6ca66c902a18f1e9577df1e28a588ace6ba46738ad97f83d0da1b11c190a53414dfc3403b9fd2b37c12337fc11fbd35d729a959eb65bfaf1009f3d4191eef08a65c440a1d990bc8aeacbdaac7a2b409fb572da886a22176f125a8fdf010ee59732c6f74734fa6c11563c7deb1b72aff3903c5f8db613de4705990478a5c70881c5b91e030f30abede23e70fcb0eef078ad3b8d91786a826afccc04c4056ffaa81c5bef0d165722b1e73852306681953ec568c82d395f6c56486d244cb38c758cd92a9a9125153eb8859fcf2672db1122b900cb81b13235e59e48e7f9d4c54bb648ad54256504bcb3571379b024332dcc85a9a336aea106ef67ad5e83970f0c94024d49d47256e17c59df2bba9bc9207d54999cf467fac67e8baea6855fedd933b8a45c8ce6580ead5c0d15f305c591286f4f8bb0ce6d6dac0b3d1f1042ffe2b76ef8deb491e9e975b604175a3f408025bc3c1377689fc1ebbf2f1a84547cb551b522c068f16d8f58246d15cd28bac7e7f0ab6d81f8c9ac0af2937027a3bd591a0145acca04eb799cb50c9037935a43b535f94c4568d1990d8f553bccb80c25f75a3ddd7e05dee4af0c53c412d8e4d5a6f559d2fd45264a6222fb6fa117311bcd468e3d0fe8e061b10b81301487f3ba9efe5f02125d1f06d2aa478b090752d38a29c7126388fcf82ac3d3fc2cca85014e371d7995d700ca650f5560ed7b500ef38d714691d5055dcb71665e8c9583a28d11b97c8ca4c3e88d9237cfa9e10c9b693a8f32c66d107cd4315809ab3d783c9f1b369bd2dcd10e70534f58aeb28deb85d4eb6b5f23d3a09b3e262e445235a1406df4ac09aeedade28856ea6ebfd110aebb3de325bce59b90de3517cb62a216ac1359485d9af6a09523bc4c2353a0f053f091cf75d240f3f43ab17600828e1f1234c82964168b33387d93a237cafa1d5c200fc4d3c8067df1e0f67eec208a0a7708e3edb37a48dc7b3424d084d58805659313944d920f26087eddcd95f2e93e072fbe82dcea4117d2bb6f49f0a0d8bf641113011c08f590c2c8ad079c603050f54f73d906531b2875a2164fb1df039a5400f8f5e2d9320dbc39e1e10d9386ca487a2e1f106c17112cbb2c1aceaa707e3f026c872fd84bf1c55d4a817754fb0c0ca357a2d42eb7447f8ccf783f447d8104469541b76557087e84e99dfeff4f22d279cac23139d8618422a60a1b12118b19dc6cfe65e0b47379ddde2fa88a6d0a66e2b2ec26eb26e162df66c0670073f4262b95fafe612c01fd974abdc5848722539e79dd1cd8c7e354f2bac64a4182b926efd6b094bb3bcd08612ec6d3f79429b69b65cf04aa5c53e75c086b1e56a16c104fc1d15cc0291b92a158269019e8b92ffe2ae35cc590022fd5d3107d427c2ff341d653e7371c0453faad52d2a35b88838695aac1243cf2e01daeedc3e321c850fa3ab9b5f18440fae7cbbb2c63864dd2138673c25fb6c7c3218662a16c932245104e76a589fc9024d4ce40199bf53284b5c3ce1b9a2d21f5ae1a265ba772f4e3094b200497b42a2c2709c19074741638a11580e156f2e7a12ef9e12479d6c1317bf5728589be7f7374360620d534fbb0bb34620abe60752acb6cc0a03554a5c702b1959dd2fb19194213ff80f2bb3cd89189afb8d53bc7ea4533d1558ab62d0c5ce070ab72429d19f8890ae6bcc5c58a5b6eb519aef08f107c14bfd05d2221cb1bf3b4008fe661dc95e9c5c831b6497d76860d4b67fc789da11e6e6f46f5fa593c04d445475b3e7e397c4c06150133f139a6e4d6dd95ed164bd6aec5da31ffe8756856714a3b200215bc7c21b2343dc7ba5357fdb508afca45c79e47b37f07c3ffd478ce4c73b217f8de49c67f9ce97ac97f2c86b12df00c41f258640fcea69d4135406c02c4c3de8b05e91d079c73357111c20f72b60c908b384dab724a611ae3d9935304f66c1bd00a59e83a682c741d24cbb993681c9cbd2c9e1c2e31096428befbb7a0dcbf913dddac027637ae2da81e4fd394fd6abc5ecb5dbeb832bdaff5b52dc4c0814782515c5680f60c410581cc0869b5b45b8bac4a4fdf8827143daf777d6a4ea6530c9a699bce3dcac5d24c19e405556e04100d28247a1a15640ebe0b8cc7585da89c666406a9ffbe3ed340e630eeb52980d7810758041117ae137095bd0d1906f45a00c05c2ad7b24cdc767b625349217333fd53cfef851808ed3c5b9e4187370ce8657201750634f5240421f9022531a072d819721414ab625e430ce814005dd15e31f9babbcb6a7b5302a66c06b461241d7176b89885ecb4248d65b6d981c82b16cd01c50bb4abad4907abc9333700524a9db219d0b2ac02bd86d15a592aa06bb41cc48348fd4ba8e588b64019d3db01c0d6269dbe43844a651f933d21da9980761dee901118ac23f797727f6d5dc2bee0bfa8306295c1991e97cf42266883060aee706fbbede3e75c8b8161ae6e251ce146501e8aedd7dcdcc4ab5a7bf6f9ad178ea9526c2fa3b755332155abe523fa6f445f6b79d4b74a2dd9ecb68a3ee0b1198127262bbf889a71f98e630e33f970512486f31d9f4815957c2855b6be09db6fc11cd74a320f37494c081dd601b67b4a4a2b876da0af150a3e473194f5f5ae63a0a06d41bc2b7cf3c601b83fc97d2494c8a08f7df56f0512f7f649c1a6e6773e7a49aa000ff2b8a0aa937233593bfe0054894dd34fa3000c3bf3ac97fce66054e4ab4b7cd5048fc38f9e769cf68a87e7ba1f285e7573232f765942831c34f1b84003f2943e8487b66f11d6795cbf0c6ed307233bef4708087438eb6143ae4acc5c698d2fb96dc08dda7737ae7208aa699795368a7d50eaf4b63710b5dab95719d2940c3a3e4e3b43cf1690c0946ce196907f637ad6c42804086b0b1aa946f23e79a66b434495741c26b1a1dae85020bae8b6eed9098ce31fd6be90ce5b2f3b98724c23fcb867ffec83d7bdfce2439a2cf85849f4d92330f038867d8b78bc1363e45603eb0be23afc3a97404e76d67744b89aa00a1bdb03f9dd9a7f6bc49366c1fbcb29e993f9d6db0cad1475d6d35d206f9df4b7ce71587ea996e9fbb883c0f063861dba6e7274c9dcc16d706459b180c17eb88c89208321c0e56b50885baec68cdc8304b9d847c4404cddd202b35420cfb358f211050ccf4fdb2600df0c5f2117f1c2707eef55731b1ad1421bb5cd20541b0ed16a68daa615c013e5fa7dad91060f8eb688622ccdb7906b7b11d191d4aaedab082803043d3135ca1a7a35b49498ed9d527ce1a05a75661e4c7bee9d24fab255fa71cb9fd2528084311232b61bffadd5b04f5a4c0b025b437da6ef1f7407ba3790b986d991b31f3e1a1db9959479d6a8d700cb9257e62b5b8e98dbf989219e421f9a40798ed88e67585a0127eb72280df844aa9607ff44991d3649fa731704de8dd05f6a4e060e1d321c82e20e88904b63424729a66b1a54df98c10514ef3b5036af547beab3e372268890d01555f385879bef1c58f00b5cf8f9865062cf0ae2fd5ca10df992fa4a98e704ac4e5776ed4203658d89aca2f446594b52bdbf923e88e6c9017d7e83ec7fb757c58e4fe9512b5017c075fae4a30d95ef1f336e13f1b961b9213bc92b20f04a91178db6b6a8d558e4f55b3aa7aac671e9cd46b947e4a4c63f20027e89b6a4a3e2d028c6af5ddbba823af35a180a05e291f7703734947f824730ceb53c288a540fda1c16349747e64786bec371106b2d6ccc5b0dbc8245f6e8dd4b75d42be8631ed1b75670ba532d39ff3be09e3f43e96d70c197d947e1b26a25e691e1c2b3086853c574113aacd096d66e11c6c446f9f833a66f7fc59952d5c119f4500a42d366f6527c5cbeb04fe7d4b0cc7374d10179cfd708a9176d31e1335f1e5bc8f5a2d72b3fb7173159650cb005642a5fc756d2a8e37c5937b383957fdc4efe7827157f9c1f238504e9a5c2f61d9b558840eaaaa7618798ed73be174776200f4b5210f1a48fc60b325944f2656b1d8c6f81eef4d63070a425fe6a4721f8d775674fb14d0f0a117e0a9006c2bddb25b63e44dbf9cf045b582dc5a9f67250820d2b32442c6c0c71c25f7d68ae25774aa11909f582d4e2448b5e0eedb89c1f9013f6d1a1b84123fe0378df2b0a889a1344d667faadd623ee98be2fcfc7d61987bdfcdfcc46377ca761d255bc092b892aa51252a4a1e4a0a076a7cdf19a4389c25d3b8e291fa5951bed1ca838cad64eda7044d3505c5d6be27841a3bc72fb3e340b6585734d1ca034ca552e9a734cd1283ab73ac87ecd8a048a65fb9ec243deab8d7f62c02347f82d69e1fb6adf51fc4529acdd89db35772abf20a46bb773b3f695f2574d89b5743d303e21e82ac3b0be578dc47380835b6dbf4c069dd088adc6853abfac96133462d738a1e62f268bd8afcfd4fc62c65ac1a759eeb73fc29a2c729ffd01d62c994da5a9133f16b827eed58bffa1eb0f383104e24399204c597e2d250b5395d720cefe60e70ef5bdb0e238b40084fe1293ce10d44d3673b8f8fe9f472402de0729f6c5324321484d5a98fe364d5b8b7a64f270b471545602c172d952206634c09060fe1dcf8cc80e6e70961e210598fd80bcccd0049c305dc37c30550eccd5b2193a02f5839df62400ce364bae899662fa4655deaaf1e5548da00ec7f1df68dba8430afbe1cd5b91960812d07a167560ad7ed969e2637925f21c607642866b6fe9485b4b0129b38aaa3ee2e618e8185ddfff2e0898fda04292e58cf978897c16442630ba13b168faf2c6dbaf315e410044ba0101acb7ab00de91bba4a7a6aa1076513252a2a90740144e720694252699c36befe376a23516f31ab2780469a806653fbfa831acd224c5427fc8907004ffd9525afe90649ad1a7654c6c9513cb17df93d1776f28da19a98e0016b09306c4dc0368bbd3b72153a9c4668bbf6c5f403d91d0843cbae696e130c52265875bbf5698060097a613c97440197c68ff60855ff0d0d723de086fdcbac4c673635343ef05ef28d77fdc29c0de59c1509f92edf2457b22269e3ce2bd3c584ef0da176fd006500bbbed11d7cea45d5a884072ab310af211361ad7c6756d049155aac8a54ceec573c3858d0401cf9dae33062d4891689b8dd72cdd00210468dd0f3dd1e3f7772e8032f5c7339c9926bcd577397255366391315bf087ebd47f1bc14cea91fb004572d11f7f1fc96d44ba5b1d76ad2b0ad7dd43c2d3807e1943a331d9c46db5aba8f8920c77a52a487ba15cff5b19db71a8173c0dc56e99f22224d0a57e4333a187684e61ac177ff4c9f3d3ad97eb7c20ce328835a420cf6e3f824a3b55b73066848b696c535f4be21844237d39de43c6c5dc02369506cad7da6a78e68f6c8a351115e20a2c5e26158d684c3380671d092ee7a5fc47591a59e5059f28c4d0793661f92d513b167fb88e70e3800466bfa12fe88a91ea93cc5f1fafe0680348643eaec33e25d236e92b13e157b8ad7f1c56f471cedd6dc626e46abae15207db43bde42a4807dd0899dd38ce772c4e1629538202b5f38ffe8b4e9cd8afe42898eb15aedfec9bdac65d40928cd00f7b37fc184bf5b857218807ad5434dc21b48c980490df7c056257004dc18221891b03604080f96561b2df0078e382edd7c2c8d647eb4d33e496ba6f5b8b7456c786e4ca959266b69d3eee3c6090966ee5f9150b5cf56c221705bf57f90e3482c6ccc93c21076cb123f46c7c7765285b072787b32fdaf354520e90d136a66bb41f89c60d91eb111ff9ca39e4a48bbdb6f17935c7cb506be31b5628f6a044953c4527b41f44df453d6d1a18a450d0b4321ef3d627f9c983755741fba1aacb7a7e40d308d3ab7b605d5de2d3f6ce8e66d1445e2500da549ea4743a2682a3a5fde56c1485e19c1cbcec3c7fb5d95936dc2805cb3d6c38570827b3889e7ef62e322f8a622234c2b083fa264b680f6a488a5e1e7c483db12cbb6736e65046b7c93fa94c3f2b88ee909497a51999d43e54732e7fe9359e0b41f1cf1200b9ce0e2913e9c63d6a6d78103954c23b8400310980dbf6c9272a87304799a0129a8da23622f170271bdc832f210ce4f289b905c6fa8bd26eeaa3c5e202185d78fd9da6debc5ce45bd3c7c3683b70f3682a82a909b2eb279209667cffdca4b1d4dbc41312b93031db52b9e352d9444a43205c9e1704344a031eeb4113790466d522a3d6c8283d7fc25ecb7887aed4df6c7e9c1a56225c134db2d04655195f65629a169a0d00f637ecf9d834e6f47af2d51b0fa0f46a1e2622e6f4f08a9cc47fd44dad10003d267370a64ba47cf186577b43c908aac18e3d11d2ba931b4f68aab77d8d2925640bb034a0d915acc87fec9d8d1bced73129eb6a91514a75e10334cca42b23f54e5d9a7f3118de94710caf5dd1c335226ab2b45bef6dd88b7555841bbe902f259814fb7d798c08e6e38fb14a76eb44881c9c03a1003e8f3d8f48af3620e8f0faa73885fbdf02ba76f28390869d9dca13ff9e2c9f6ff0b52b6523381a43c1129348757fedd3b197ade5910e5cc956c8acd75891fdc4b2c59deef4999cf8138974fe88a0ada849923a53fd89c114aabf50c64fc38a9d205083a0b117bf176608e59837b7acee57a9edd0c0c816119959354b26fff0adf7207c781414cd6a75b372b3141ec75c7c7b1fef0f12bac7c0a75482b66adc9af8cabc880ff3724c91f8f48e912e05bd7d1743271398005d739e3378e8c13f22e2da4715ffb1059926c7cc454623092821892be6b31a75bbd5ff5d84e353b02506440675a8eb22ffd2d2bc70f1b8ca0bb8e2623e1560365eb46d9ce19ba4076819424af6fae303e49eeb65440eb6a81cb6319626703ff119f368b703e34ea6a1a49baabdc7dccea78bb66e2df7e28c06413954f4a0d7a54fe7b8f92dcc6c963e2bdb7bcb079e6c3f4af38397320d94fbe4152ca835ea3c97bce2cc6ae494651ac1a9d6708f21a7b827770d1671f1e90cd9c128cec297f93923139f3918adaa4e84b901e7b419f8a62a47da46122996180a8ecff9524f0bb375402169ca7e56d6c37c692e55d07270a56fd8bf785489b940e0712a0d0c142dfb48e926a4b36d813e654101443ac8d895831bbca3f86732e066edce750cc0b70ff25b69b307339acd9227ef3de9185b184b25708f4653d334513f0b773677521db9845f05d76be48be4f3476f6a64c1e67f6633e47ae7d85966512fa25c43a6ea7a22b3484db6badbcba4c725804e962a2cabe8e392e4ef8b4f021d8395cd2f036e0a96721885dd7982d995666a89c411d837b35568a1c465a84d47cd8189a67dcc49c8685c8294001939160b33f021e4cc6a13d104bca760f9eafaaa4f8e5bf52fcf85a605832c0fa7d73e46bb2c659ef48762757f466216ebc2665cb8a55f27edd36a162748dc9904269564e11067f25dbb059eeb229e678d6b559aa3fdfa5517d187e32a5cae219826e3a0defb67c92f5de2ad04f5bb64c0faca1bc17dd23a1a6de8c0fb77822f1ee643adf0f5b99c7ba4c88d83af672f4700e9d4c3cfc697c9064e41f9afe8e045c189138f11a78681abb2b23b43f7d30065a13c2d8ccb02b9c60d32a6bfdc6835409a7ac2197006accc45ce8bae7b574d0fce7e6bb9c94f26fea09b4023e5cb3611607ef35d38ef1a9e161aa4848541b434d64730cd704699771b934b3062b482c1fe2ec705ef58ab8c8918469fe43752562a8ff8033a8d369eee925ca62c25428ad92ee59058c2951651ef615676a741a1059d1027001b2f928344048232c2217bead31b0fa3ee282a0bb16e94d0016c59533ccb3639156dc023cec9ff5da16081276093fb504436342b0f82e1202688f0de74f38314abd14db9b297258b55a2901e31d5ae174c9fd712f86d76482858db61ae81ea9328ab5885e793f338c8f49f0fc35d3388be548583c71c14d2f15ef781266404866bdeb082d1e534c95cbd2cbaace1152e137146d015e2169d3d372f73c657c5149fd3b445df5d9007e2bfd0b362f8823875a78ce9498d3c656710184edb6cc1ef6699951f905667ebac61f52e77c79b508237254c9de5c395ef7e0653b401dbb07720629ddefccd13034bb87772d58416d82d0fa14c3a6fab4968a077a034577d22c63169c71072c1c51eb4eb6d2d6dc1a6339ee50faa97ea7690126f1d976474008a68e3202bd20940989e2b12abcbb905e8c7d053960eb9f58e3ebb5cdfa2b1d8b9adb8c419b5c9f215f24722593c692c5934758586456283e030a1bc97829f9e20abefca06a95c8e6c00ed01c30e16599f61e344b01736d489a394a5f1894c4e5f67b679e474271c0474ad8e234e364e18ed11a4ffe855494791e4d6572e6d52a2ecf39a99642060a34caec85102a3dfba16db7a322b3f9be051e97b4c6f1022afefba7fe6eb673d480e1f7792ac5500ff16c9e0b165700773c2d785c61f392bf7efe30649d1e0f445e9f6e960c24cf2f038f99359f04f2ac9fe5cd86467a4d4d597045ff243194438fd2883a139164aca677e2204367d82dc7e780722c2a78c17b47b8d77d4076513861af0883f1a6c0921197a47602f9847db8872885d9273fd4177bf1626094053e76da531a893535a32ab99f62c556c129dbc66e0da34edb3a46c6368ef726a780538c9bd8ea555a581aa698c3ad695a30efa4d63709e27c4ce5a9ef00ec734954e21d4ba95696689de3e854c5fe509a47c2d75e8d89183a090ee62031c6b2edeb36f98cd4384c3ca1921842dc4a15b33e122be86072ef8c080153b03cb8cae51dec2db229db6b7244d3792688a78925fe8fbf78ab8db1a381e62ffc9c4ac183924e6e030e56b263da4e07c94a183070862f344ba01965cebd1ffa392a1419567625c628852241d3d013ae8638daec50f9f46d216d7381990ea04a18a25a41fa42d8ce0941155ec00e267e404baa41860c38d6ea7448a2023d11131f148b8c0b3baeb73aa58e0ac0929879064b4b05b55035830520bfffa8f9b7452507d4ce70ff77398b2205432e8f42a60f363dcc0ce85a2e2ad583ba108d1a38bd4705d189bee39c49786359a98c006753fd0a05cd9832939968990fae4fb73b2effaba867dd675e492fa5342f1cf12730cb116d50e2d166746fe68c96d42fe2d2138de69cdc5d70f3c76ebeee2c3f39f776111c1aaa8d57bc6a51fc68d24109edee97f6ae2c4b422b8b10581ab416180dafba0a62673a39e9677f322c7e933f2b5e644fb2cffe49f601ade1a4b8dcc0fb0513844e91b8eb57467211f0536737c007911e0fb6b36d1079fbdfc8ffeaff0de2c0d440dc44fa0717e284ae09daa87a879b871b52f175b1eb7827a2679eea030a7fa679662f84bbcf64ac680452f9409300efc666198b88615255a6cc4b0ac52e2a30da7250828b8d60f377c590ba6f443dec4596e877cffda0bf2adfdf14ed2e5af064d5e4f6fc6810918c536e9694490a947434c57d1aae4b4b25b5d0f0ae811fe665700ce248f6cdc0551ee59d03c07756b0956c0316ebfcf1fa2c1461515f281d47f27968763c57b521ae840cdf9e73b9ad9039273c0ac55f0244cb5c8d09de8508659ef979eca2b5b6f3bee8487f6a07dbd10e0f66c6d40df60f1069c6d0e6f4f0feffa810920debc589838f18ac87e87c25180d58212c0db16b22d6d641417abee3222d952b47d831f19623b23a5012b5502e5c8f7b4c60de4801446352043343b5a433d239ce456b8e7d6e81b939226902d726d1279bd5ef4cfef0335080dc8c83b8996eaa29392680751cdbb9dc3324296e9bd122bc88aade2c2261c272a448637aa0a59584bb0adbbe49c4f6fb1571a5e6519852ee0d35ae49f12c1bc436a42fde4b893c3676168f43f8f64057021e74b40ebee1b70e3650825f8ce97c59bdace6c0fffe80c34dc4ab59b532816292685c421a25b1ced998150da9e63c510cc9a0f44cee6af24a3df406f688c8dd816e7582e728b2e9caa3f6e0d246151ddb38f92ca0a7c349eeda9061efca96a4e0786347bcc85a0cc9dec291a0bdc0b9ea031193acf5a12a9016793ce5e28b237c325c118785b7a26eb2c0da7ed6d88e34e4bb75112407a46bcefe32ec8244f0f4a0ce3e51ee810861e94ba5e817838f8ae4e10a7cef480453c839be9648b7add61315824a64471d594ccca6a90b2baacc0c05a935ed9506c86e6f7fd5e2b9785740f41b325212e70d59b51bf44a1af78fb7be9a715a53c46c502306a0390aa18eb698fa03e1e15752c0c792c4df5b7eb8d843ac227ebda1b08ae141505be3a5c92ba9c72c96b1abeab90a4f327b7ef2065bae77e99c67a04642d50f4b8c7a9187a800585a8be22233ec86974eb1dc5396dc283e4e7b265442b5354d7b73a153471daf67cef98e6791acfb2026ea1ed9dd3509aa45b7a95d911f270d5dc047c72f6b11c857d73fd1c0334acd4f63ebec6f490740ce9a9cff76deddbf5e08029076926d0308da47ef51e981a86c8c9bbec87fa1d993efc842be6ddedfa2ef1dd12bf63be8a5368f173a6682c9cdabbc862a19c73617beb8fb52cd8b8e82c024a84313d9d02a046e88b667b5dde0d9835b94025955d4021d060c68e70ec7092d18df5925cd144e103dc3107b62b166a480025a7a085058e7da2fe49e88dd366a8cc515514c9061111093d64b7aade403f253f19e6e72d29d02b53af5bf923c1fc3e1ca12250dc3bdbf705c30e5ab726002035978b4e891db09688c962dff7da7874f8ec6c4e57477432a48c92c10eb611583a557f694ad009dc62308977f78cb1d04a236a0dd2bbd50968493bda533ce2391f1be53331060dd05060ecbfbcae31897eb49ffcad3dbf10f76df5bd9372528259ebc71bda0970e0987cf3a6b5c56bd227c121a470ea30263ebe82619dfbd4c36e4e0dd1eb2f35fb8ed27917201d34bde723550d2cfd8991d361a83179a52bd26d26891575189b28d8c642a403faefce1acc9ed0e1df259a9d84238b1de755c10fef1fa9e94673035df28b6089410d9c33f164946e8a1b6a4e32008b4b56a33e2e201234b10cd4f7df74c7181da60d256aac659175d0b03b0980ce4b0325056f6b26d6197f78a2d0e67fbf39f49f0f412f43708b548dab4f7d4bf80b6e6723f413790f0eb1aec688f4626aa9ca7852572432e34dae209601cb312b923db6532f4fa56228b1d95b2a9062597a1e06ec789cf4f66c5fbe873aab463f11664dba50568e7c56e3f87afd94f09a6632451780daf0b42ea68db3ef794577017c631c8b273b0ae8dc6a57e0da3e6ac92a40536db3b8e0d89f74f0aa92a6e982c91bcb9974f9eb4c1b75158ae8ac5b17bfc571aed361ac612755cffd9912eb3451a0246ba6b6c6dfbbb903783cda2c99366627778a77860d17979e1fa5dd1f6e033f251df15f2b2303ade8b124b679fc35cc229313c6be8dd99de025fcf0a023f5d397c22359a298b222950aae470884c04deb8f0224ac2aae08b78faaec8cfad1ec42016c9a053402af9594ccc313e744accad40f39a4dadbaee3d3db87dcc17edf79cd34506b313ae9595ec5117c75102dd40f888adc214ead654cd869466618a18e1a39dc25d45152e190ec6bba892e5ca272c009891026b3369162e6c3487c93ed20ee386656eddc09d704835ea3c6a08004aac389a59912f5b5ec04c20afeb76cc8530c18a6c19712d29c1ca377051bf917cb242b481440d7f3bccf20503c555301237180bb32357bea4d18b21bc2d941a25b1af46a4d0237361cb40cf7d99093cdacef0a152e1cd3232bcece930dc5220a6ec304040140ab6f9b3f4df1464c146aa5b24439fac1300966bf651e229a0a39d8749e1f782d4d011f927360637fd0e178e09629bdb9e2f86d9e0b9c03eebdaf4a3b97a03761767edd0df55e9403d542127b891e74cc9b4b049d2c3e813fa330f71ce1d1f5b89a24d82327329f0a4ad3a853c7aab0435658ff68cb802dd98578e17e0021828731ec97d7c200f6c965a2cca2c6d262d4581ff697addcde505f41a50298f104e5c4039119209cb0dcc8231d9c99f4093c54b60d81279b4bf55d70234efe58082d5cc5e8ca297666ecc03baea4fcc30312f93fc64ccb303f3aed6de0b81883d43d71e4ca6f8f42d667907b5e4cca36cf0b267fa04fd58f5223ca7e484ffa975808fe075a4751c3e4994866391b409977eddc558c9e894cbc82e966fd64f71ba7c02441feec9e2b942966c4a06942d5994b246f44c8f0093a1863b42b0a5f7c9a067f5f15691ed323a7da81651d04c6ab100dc330703f6762f03cb21ff44eaa20bbb376009037a66a0a34d1beca9e7325f5f4e3347928e58b66016587f3b27dd4594f9a94ce3314807c0734449999665077e4a0915dabb9186a5a5ace9f4d8ea67c8b066542230bd0f0dd6750d33302e90c9773066d6f461acd704766101e66447919ce5806d92b23d094e192c9a08564a41719ee840cc23c46f49f6338cb1864be18012a864b1283f68891f661b8c730085118d1230c2707832c82117860b83460d0e62f52f885fbf50521f9229abd705e2fc83a2f02c10b17dc05edd945cabae04153a7ace0a22cd2259673598f5c1c3e2e511b2ecbbcc519dc12e168cbd20f51bde95a0aa116544e8b20a185e7e882f673919e5cb8332e080317d1bf85b3dd822c6d11005bb81cd55ad8cee40b01a2f0856490820fe71cbd577f62a876c5e80c787e228123c1c4b2e02a6783fe83684530c5e40b06531a80d1df67f7fc29934c55a2ef0d93f149a7c3c79cf691618171ed9d838b2c8d1a6ac83100e970e26d1aa479522fce2709934857eb2279c1d365ed67db4566acf90d5734f430aedb288d6a926b1a1d756529da4ab1358cdc2cf8df9733ec919f97cdac261370a1dee097254dfaa0de7c80794467427c81c99f370f8435e9192c68a7a3a21601806e3d238cdd2efe631cfb882a37630da659f30115740c398bca95a729c1338fff825a5799167f56a2bd17dd988736262efd728ae6abac716e847bcf77cec4594f12fe9635f0db21c1bc20cb3cf567510f78139e9e09b01ba6fcb76b00f70293f9689641bae7a61efa26203719619f4e79ec5542ee1b7d929879c45ca017f5d289da8fa9bdf520c3e8efedfce9cab319a033eff34d7f4f652c1d3c963f7d7b9f5174eb41cee8efedccd3956773a033ef339bfe9eca2d1d3c9679faf63e5774eb4186d1dfdbf9d39567334067deb2724d47b337266882f621594c518e63944d44e8fe9b7b30b515fa6f767019bca40a6e23f102d44bcac67f168dff01c1d5fc094611402f9d0e341ba05ff45e5002c5764434a0ac6718e6ea73916a45c7d2cf73bdd4bb55aa4191a0a02f68b6887e8d71795a6006819040e1b38ed6cf5dec8b83856cf3ecd3ae6896629fcb18fb27c9ca6b1c8d7dd6d30e3a909df5d451a88d7c7ee8f08586f45bf9f468a910e66df5c06d2cb8a01cecba0e7216691a16c054e6a593b850942f7520b114dc945ee163619d1602053e3f3850cb6d7d71e98dfbec82e188685db6dd438126fc79d39d30920bbdfe10f1202e05b38c96455ed108cb81fd6b8dda957bfdf3a29cf5286fa40cb6aaa8ce72953e2ee0e419e2fad3d4fa17e95c9d52e1d2b9aae66f4e7702eae9ad31bb7ea5f5937d97d02745836b96e8f879754e8f47032c5e619123da3c322c99b475e6b39ef77e3c497a0f596c74f2ad551238fe785b4f632db4ebd9daeee583e8cebd20ffac8afb4690c7f6e37937253eed350b8c185b74478bf2672184674c866790e631c980704d851de7ab457093f4858573118b4754dc724c19328de522cf8724af4143c451b08fc819af97279776638eb1030da4bdfeaa4a70078416f65733299334cc9ec3e819865da9c8734b1f91dc09875180e401644d089f32aa5634e88916fc807ad880cdf164152f7581a5eae11fedd399ff7935a95e09161cb4c01357d0ca4ec47c13e9e8310848a484bbcb1d72a46d2544dbc3c739e47248c389a3b20072449f0d98431c4050958ed628e601b7aa7cdb69ab4925be1f17fb343c205cdc23bd8fde9c72d8d3acfd8474cc0aef94f7a7bd45a87631c18b932ac2444ae079b57b1757cfb1e45bf0c2e9883be1ee35b3703b65fc07eb34f66b1a48c883d282bd722ea1e7927b99c8b12a589f089d5979991fa3e3c3880c349dc5f1249ac40317832c3f17adabb505276bc43f1d441210230315add864ebe0becb685207fe9e8f219155a2a3a548831c547a948ff939eccf06601be9b054bb03f4d14e3de084f5a574dea656bd31a5b9603e1d86e748338ed598ef4ed7ec0c9cd4b2de797e25a38fa9da1bb4d1ae08e3169a2a320000a815b58ed2a1cfa4d4fe3e98dd4ef29181c3704babe38833b572585f72d492aea0cfa3fae4ba5a920f4332e7830b805ed69e13b7c015ea3a339adba44838e5dcde10d420f34d079c0dbf92edaba5697a00f3372b84455cff7d20093aaff501c551dcced18415302713451c81475af5d25de84d895a50f71ff94ab0c7f953950fc360acbeec5bba8c97b86a03d549c0b8b14d6758dbec6e2fe33a9015bb839c5f56e2cd50228463fb563c09ae8538fb0b1fc93595f0dacd36534330f29e15d336711bf9f135495201cb8b6f1d89b202c473ba43419f076569ad4b763d7e71e635be1b44498d891db3b1097c5e840e510520574fa7730259f041f746e5e224de6d9b758b2d97aec20a02b4677af5473ad2a65c99da5e47c00b8d37f14a86f49f89fa63830045022f81cbe399ccd166b4a40f20428338df5a91d00bd25f9dd9a1f8beac9591f8df9623d02395eefcea66e908d54c5c749dd1ce0f805038fb1563578cdacd4a5aabce1cfed14baa8a4ffaa8bda445e64eafb4e0ef620ebea8ab567ed3e937ea12290cea8f8e8b9aa6fcfed1e4fea7144749d851130dee51cf18e2bc6f2cd38fa5bd3de260cacabe94eab99fd566619839d2b2092df01f3e5824ba7267770d537b90c021398f011ea7b25f4dc6132fd5bff91e23600726ae41eeadfd9fae6b59959438d63cbaf2408cbcfa68a1f2e099786d3a24a0ab40a5e283d50972ba85abd84e68811d94aff7c4510cf0fba2b5fc47aea815dcc6cdca7da3cc5582c86cacb4c377f2759f6becf0ef2529265e3d6e8f08f42c120f8fb528d20b1302eae490222a0d5ef8b9077d36cc3a6d2b060fd35723fd7495fcd53d1798bc6a1dba8bf6a90de08a82ce2ed1fb7e02e7342bd0300f1383164dadb5972c1c332071e705cca87a3f469f5705561f54143386f66a2bdc57d875743d5889b814c373bd21bc1d6759aa6915adf73bebff0ea062245f10e0859f6c0972ea69a4233fa5b0b4365b32e9d76cedd379987db25b8c0bb1a369ff011dc4077dc3e03d6c108a73034fc037bc992c5f088f9e26abf43158e3ce07bd8f46bd01d183d653e337b67c157933c1c7ce3c0054652a3628407ba903fa7041f972fe39528b08b1e21e9ad828b4a9da7861ef73412ec3c0d387c191e61daf9b2d46205a7bd59806f5eb7aeeffcf1d2f54cca983129f8974db845f4493652d2604e8e2842d8916c2b91e6ea5564a0f5be0506298c8ea66d6ca82bfa64ad6da8375e87fd73bf4404121c689c3ffcf5e11fbff52a1f7a982d35805d74cab09555ef8302c7aaaa02febefcbf868e994bbc4f7ae17bb4e32df1bb88b2bc7b33aff558d54a40a8016a38a589559aed80cf858af0ea9075a7590bdfb75b7490a9ffa959360bf3465960b8f417694c2865714804eeeb66ceb3ebe34f8ff9a04e617022967995f214ad55d1204225f5da58577d565d7e5eeb7078ff4ca55549a0a59e3706835ee9c48801e7a4a375b2e58c3890904eef5ad0b5887b8a6de4df709408cb86746f7eb3d7291de6f40ec9fcb0ca1665c2caa8c3313098c2c945e228644e75a0e2531b300524c9c5f2560be700173cfe134b7437726819ae919cc4d2e34a035990ff62d3aa0280f03cb42ddb9dc1b7813a007e0ee7a31eeff79ff47dfa3d22f96fbe6db7d0d1d4f73d0ebf7691f57c673f0a1fb4695f46e236771e46c6231b574c5ac3656976713c18774abfba3dd629f4bd5fdc43572b8cdbb37cfa4b2b9f14d7959df2d38232a979a66436f449c6e9a768ee288f4f7bafef6b297765ab29746eea61b07d5f437ed9a6e92817ab59361ff5a5213edc8af6710eeb83b88a8159dc0e0558705aec434264cc925dd898ea6e20cf02259d6cdaaa7d2ab1fdf9c8931cddcc1280543eb8e6ffda31bebeef904db411356d9e34884e6cb3f1cea8ff77ddfd442af79c078030d14e4f5706a2bb6d4546fd796dc15748a91b0d282ba93a1293458d61a32f9d98a3d35e88dba72dff00e4ecb6f1dae41b9b880afb54c9ffbaba74673ba34976c9d89208c28e06b3a65cdedefeb2581d4f7119558a81442cc47bdbcaabfbfe02b14fda48a4a47c8015428ddef2d37f30a194015773e878fbbbff0efcffafacfa5a10578468dba62c7deaace027b99fa38477e4d4754870db8a8fc4c41d323e6dd253d1b42e7284bc6e781b9e23339af52dc0c9949df5f671d4daefaa3e3e1b3a1a1fb50f48de9e60c8cd3d23b34139773204155d1933eb8f2df3b197a667fb84ce515742d3990f63e37b88667135f8afde2d580fb9524d67e818aa7f3540410fc40385ec0575b8141c02ef311df8021d4ac09da51303a2c82d93964247df195704bd42fb81d72a1f7399b4d6a3c3ad4534a98507d04132d5060428a9958f0b6825885e6070250fc28e5e0a3cfe2663fd4b4f0d537150d62457f1b58f1a7dcb038fb904ab9581d8e707c6cef380bea221881a2df03cec63c3bc662facd5ca80db0cda16536744ee63cfb120f5a663152180252af4825641c1722ef98103728b6bad2e3a107ce52e9317d4f70aaac0181178de6c731f3977102f708c0ddcf5d57cdfd8b80c1077eae2b3b5ea6dc6db099db2fbfd15d3828360ef74be1a0f111928ee1acd41d05df7ca440b555d23a2051e7926c9a81298248d27b19b824844242ce1916253fce3e3cec555599b5e19a3d0ffd149f2348dab8c35e549852721c16d89af19a1a93568ce2811206db5cf8f22f950e552eb5de8b562940484e362d44f543eaee6c9dd00478952776f2fef1bd4529c66eb8ca9a5f6d878f0d0466dc71c6e6cced9d93980d82eea3d9c5016894c4e2e23d65c11b2d25d69bf8dbfc9d999c042b70512494491669e2d76245c1e5355601b8ddb4e267fabcc4394aabd69087d96d00ef5acfc10e4c48b36abb97971c67116029d25bae248c96f2b34538b4289e3f5901fcb4e2cb08cefbc6f9a9d53d284bab6d9f3bd698b9af26caf6707c054f5bbc6abe4b57e863f752716442a61d733f7b6c105b095d095178922cdb327fb8f5448b189a16518e18c6a624f434925db05fdad0ef82ba17a8664cc4b070b9af45fd2b161d6cce6c0a7206e807dda14641d374208801044c65876601022f3f159269485961cb3a0fc8850ebcd1c5a0ace7f4ddd2c6c51257f08f65ae1111989f89539bcbf45c3c21e7d5f98ac94d96101fb43b6e6d025885ce9b08d5837fbc1fbe6d6d9e14e8178031c23c0bf0dbb670a36ad93efadff7a3a0e3340dc410ad83d816c6de2da1447c41698d9cc0c2136d5db0aa9131771319fcb8f8e06c37cfdd6878905e5ad3b81344762fb828eb75ee70d2a20238fa43436092301b24886423ea034cc9330cd73318f4394d7c3de8003b018475705110066a8338c076d37033278f90ebf1826b4274a03fef5b1c330702f26899043ec987975b05ca3b344926e9938d96c194e1791ba1e426fe5530cf9c1e025cf0d31458c99576284c6ae040085934cd78aedab345e0c44560e87958d36c84c377048aeb8eb89586e0ca23d474ee6311c7268e2d832930192387907dec785a83bb9623f42f8545ffc2cade194b1ba681dae5dd98e07b821e32a7dfb894e68ac68ecfdc9c2561b1b9774a28ced37149194b0a7ec7e8b589494a55458e1a0a4328655ad11a73f41c13cd1dbfe01cd35aa673640562e5c155f8d8b837b0e44910ee6c2b6514345916b224d6616ac2c893baf14abf554c22b78e7992b4831fcbac458fd7964aca102cc40de2d108fbb1c4fec509b121a799c15cc23dec0f0e762f2cf946ead518ea25b55c711a7fe6ae7dd98d4770b1607b580fac04b77bd6e571f90c901d2bbb36f6988cc8dc94ea9a634c568c72a267499e439f118075fe8f4c66ccbc4fc48d8480009f707005236e85349da75f405ccc50c7ccb12bd6f9e96e8b1639ceed47e3c676378fab6360ce6e79a170ba2c0f0ce4fb03d17e423d7dc3857b6e4b1a8f16690891e2f5a5924ae13d0b791c4274d17dbccd3139aebbb67953b35eb17647e6f49bf35de287d66d04a40d370c31a6bb54a1362ced6026b3a47e2363c460029aa391c74e4e44e997ebf0cc384b1e8741e75c64b001720fa16e5fa712692a94837307df9f2111d8b27dbdd3c509c30f483c037a0c7cd5eeafd152f0bca8ccef6cde6071696ce6ee6c76a83ccc9269e5ee6c94bef1ae8a3dfe765ea08ba5cb9ddf50348ac88803ee9856ea83c5f7db60323acc11a4014a267f73b0a3072c2d7cdc34ef458402004cc0253b2a53bdc5655a929d931f6625368e98737fc3435b6c230ae6283a692f23183678c46be1b2531777c2613edebc720dd2563acf80f05805addbfeae117de6b98ee906e44ed731e3ef3e3bb72f84044ebfeb5411d98b5508b82065afdd896c44c71dc48489a75f999e2243ca8e26c81127394ee0bf35183d664af68710d187838d960947acac11ea4a1e77f0791dac53c4e2ff801aff0827d3b3254f9ae1c2c55d977791ccd12161be394556beb7ac7172c9641f0e7c4b168e32c337b29249d8e48991c0e2b65a8b4c382b0bca91cf0373d7532d82b9540e0b2a04d56fdda081af8c54d284c01e120287e328643eb66927ef3940c13b584c007eb6f8808132249f801493c12d634c4b624435144c2e29e0620773bb859fda98468cb5d7cc06dfc1c2a4d1a707ee020178e84ebfbbba0b3014601d15b60e5e9c4aa99a8eca9cdd7777e233ca90901bab011c92d331fc9225b14e2e23edd42a051a161cccc50c1163afd0d9a5d295ce09682689b83050a153af8e0465c664b4018b003128f25e4cccba06396645b9bb44c2780724cd217bcc25824a6abb3eab4bf7cb04a1b9273045e1cd74970567cdae59426ac2e929753ca9c267bd7ea05411f3eea960f34108f4dc24e067d012038df991e8cff107d494d6a99845267a83b3026f30a863d605e36d14066115a82fdb3fd81109f4d59b01c3bb58b9947104e0c79a158962b6a939b01beadcdfb1ae105594d44b9c8360fa69f57d1f92ffada161b59344a62655cd981ace47011a0c59833839b37153759844533e3a2512bf6432eb886d124af5d6ce729788960944cb4b12f2531cd0d2bc63f2e0b1da5da281f1fda951f9fcb35d23a53a11ca127553cc6d328157d52c065069fccf1bdd3a9d25e0c094b39753ae2b1749eccd739b03e974bacfb96802e092f1884dc2459f3278f35b199d157791f510881e2e0ebd29402cb62aa71baa1046986b65dff0a29f22484f6a2f5fbcd21232397a2cd3a747c5bab6cc00ea71d684a5212359e90e0d16e0d75169d1c21b210a0fc718f170630f5e9528edb1e48b338f1771b66e651a08e5f4572258300929b9288928d9c8baa2f701a9c3906c74250b8dc8ca4f4e391e5ddefe4edcba626a200be076b0b387a1f14d2dd6103cdfe0c1a88d226f683461e68f1946c57aaf6eb06526d3f2d790d837ca6f64272a1e46e6b36368439e367574e2047a3a5a765e9e8d9eea2644fc8029312c7ff9f86f6c6f1d2fd53cf442e0d2dbca4442392cff538eafdfe71738c89bc4dc12b7706e6aa4d1f92872277554c4b9eb7f459dc74fbaaed78fa0dc6ce3a2f1c9ec3bc4e7f12345b0cc32e29ab9f55833babe7789f1f111df6b0c8200abe1e30ca4de3d7db1ae8e8a1037d8fd626f261da9f2ce3f47e9bbc4a3b6ae3a1b3fcf4137bdbe20ac7ba835ddbf0b5f9f42fc1065c6defbeccf28acead8166c4c201ef8cca8ed08fc82cae1e205764bd638af235060523697e72b1b6948d93020fd01077e81360cc77d545484b3344d60abff7099e72688ecf644d8fa88a31f0194823e8848e72c14b7180f98a53cd7ff1570661b19f81c25e2dabc42698e7775d565a7c098c3c99d85c6722cd2bf74d0b07b2ce184a22a50fac55da482a8573bf9d49846e0e179c68e776f2aacf343f593207f3da89333f35eb13a1489783683e6b9727092c9f49bc8cc062a1123de210e7d9d6f964a713c0c0b8ee4b33b43006c397c6d80bf493c3675e62bd792717c16c98806ea7051095c406240a2b2bca49889bc61a74b852a86e62a42a3f8280d2567ff82861a47034cc909bd124320d66409ca8bad2b2a6a01605ed813f631d9542d7f49deeb0f0519a0599904ce803b6473fbc1a6ef2c707621d66d12afc58059e2f34e3534c9d66452a27d1464667a1407d2c8f0e3e88dd2179b60eafd65abbee086e6671ed5fc008009a70bb0027c88eebc1b6210e956a315d323a5bb0841aa0ac337a085a597136620304e098292c1cbc2c14f286152a988d806dff4b301e624037ea91bc4fa44e512d6f6fbab9ad5ee19cef40da12bc5a0d80cc3f0c777f40c9fbb555a69092fef46783489062fc15894b0410a285ce60178be49e6e838d7aa40abce9ae69c7cac117ad0657c8bfe562f4f159acbafb6ddd8ad7ba367ea82fd0cb13ad1124d39651e97658c0e11f01e9f87fbeb0e7f2b15b1f8944b3d81dc020eece15934b2a0da1c80c701b0cbb901d40201c41fc070abcd3d504d10aa080099021c6ef2b22e6cd641f8d6bfd0ae427355b516b4e872e2d6496abb1834927b047022b9e903a1041d5570e3c03acec8dea6d806c1a80381af836ac900c67dc106f7cf005fe7f3f65af7d4a09a1228d546d1ac92fa6820458b87731c1c067905ccdd0e44297848821c2f6162f3725a83a9ecbe7f8a3d892349bb2aac68aa42c21490e1f48bba6cd5726ac6790f1e8f2bce8ecb580d413352951fdfd8548644317a186db54e623e1fa8b0e6d4727dc7cdb8e314ecb0fb6e4729786cbef4aee853fcf1ca5e84b99be389069cfc80ef41cfc058c0ab18f96a410d1893fc86fca58938a5a649445db571e6075afbfe4892f2a6027db6c1d821c329a2344f2f2dc21924cd5bdd04e3b676b6fb7060bc262c3cdb89cd1ec3967d9c194c0f32113ef5456982b1e272c3838faa3d6eff04ab8ed8a0c4c7837dac12ce137f743dc8c7cdc9120ccb6e8e2b3ef928eb6d2b0a11d82e1755f0d2d270c38e7b7128de95ad156d4e560f8e9e73fc1c652b05b16459cefccb18e51d1fa04a57d861713298ce6f9d1f72b7fe04e23cc8cf7c1795f327474b2ee00b0a717ae03ac832081c994e228a77cd8fb235fcce599271f16009e2e51b208a5456f98ca40589a13082454e0c11ba78f76191a921435bb21db067747b7388cf4bd1667e0ec944fcecdc7576472506096f4a275e9b90c7f5e0397792083507a2e6043b29c4636cd6e71b59664eab523d615302866733849d095f0f09fb074225f53f095d68390377b2b9e024f27da2e1f933ebe2abc493ab2bfcecc037309fcff3910d980a809582cf28679269199fa5a9a719380d0eb12be0623b0395286c15a1bc5c72b22122c10f6e9545596ae8e5d2b625c0b3d5f78b2e64f797e81f2912d6afd05fe8569bf749a3863469d33e22580b0b2ab0bc5731b558e3bb8c1d2693b10b5b0424b343eb7d0deb4c720a7dfdaa610d3d4fd7a8dea5cc5f8022ca6f30e4da1c6e6e4618094b14279a6213324a3867d8b8c60349db48861bc7366682e0d152cbf1799a134fdf3a033fcf26e08150f03688dae3b32f0ed2e879673c86afa81f2943f4a901aa5936506171972f2746a2c4896045e79e68d1c4f441bf29135dc83994319f61c390d687d5bc76e808c686b416082cb900d31db1ac6174e97157c0400892141cc9564271a3a882d1f3ebbe12dbb6bbfc7dafa117ff046b42e8194d851aecf92be798924814864d10ccc3e8bad39b63d4c6874f7275be6a1458d21fe0991846833a2e82bd9c94aa585f7e69e3384d3c8421a8dad7a99c975ec44918aa93b4c1f04470d08359c2571eef7435390bc5977a4e0b43caaabe1553e2143f6e728692b8dd7e3920933fa07c66b1488fd3b9e7614ad6e7a929dd4bb091e2d3ad69f989b1465f099be0ceb003ac09cb8022ba2d04daa5b5acd56e2c31c9f816acbcf484f9badb466ca31bcae4fba8673a2179cfab68d87dc66bb8e1df6fdd33ab7837c1bb1acaff34adcb2389a96945b8aaddaabd3a9043ba74501a757efcb74b98a07ecf01718031e8bb3c572eab67257c38a8b09538fca85b888564cec98bfc4285c27ca9a134532020bdee0deb55c6ab58e34cbe9797c38d1ef8242d79330b9e1b8a6a8e4520a715ae0c763ff383156fb2a6e38307eec316c42aa43fe44627497dc5cd9c91b42b556a4d3c8bbf51b15f3ed85f322fb357e81a99d7aa6cf847955fb899e72d5c009a046eef06ff2a1a72d4cfff0019cab3ff76c45311d5a9fdea734dbe4f9cbdc47314f8e4eb45eeded2dc4a4701a13cbb9ba509328bb92b7df2dcc8dc8cd32dea0f061a81da28bd4cba0ae9070ff222deff24f584d61c854572f52d51c99950288d0d18c65e374de92385fbeb886bab36af92f0b1a1efccc3a5f15fb685ab57b18415225895780d4d788e1e02e4ad75ea494c0dc62c8ce3809bc8ae80c546dff17deb1bd28f1afc8645810963cfaf3ff974367ee2e2290d51a9c25c8c2956ff70c48b795132b91a80b85dca90bbfbb3d26b29ae8bc72ebea091b9ce7ccd0a13a2067a5cb29318c561b89c2d8f4c3b0c033f31f39d2d06235654f7c46d9dbb713c9123af36ce4106147c7933d490abe28eebdbd477c2bb21735377eca9fcd0be49d9d8f8b2f5379270e21e71e88a600f799dceaf141ae5d0ab1f4b668041a6152c51c23345dde65934b9a67340c11f79e6dd1706fd83ef906eb166cc28b1054f1e02c0a6b5f0ccb0fc533fdd121331911853cb3e085bedbdb82bdb581274cb766a14388416c8dfb699bf9226c9b3ce026f34c8b04d653f15dc0665a0325e4b039c808628d5d6d5dd2b3b77eb9a71e5e4d3445fd57feed5b684135e8d8c777374dfd2c5245fdeae0c575d903e575b9e9772ba028015d76c60c87b418b8cdd0403966167a3db28286aa2ffb77dba5af27f47d4971af0c62a6a557e9f658370e64d1661658ddd1904b379e56d88cbc4d879abb0f0479f4ceeb19c7a30692567368002cbfb46edb29253fe128191742b39d3b72c766dd10f2a3889a7ca28140c341b0d12b85301dc6cc2221aec5240e8ce9c593f055478bc5fd4e09afed9606016d8220092447668e56a88436272bba4b9e8de2c2205eecbc89a1c19260fdc67d9bfbfa1eea6e7ade90164bb7bc2a36c081509b4b608da0843f41c8a60bae84d5099c06e4ef4a3ac96e5118cb4cb489e1a0326cb9a4b18ecccceb740d0b0e6491e5ee7957c4901b559d4b09c13557eacea7bdf360e82acef55080fc65b5b6ce31236f58a77263bf7e9f5247c4fc41704a2411d5585a336f92893c316f249ca282d3c21838a28f8029303ddf4a1707825fba1449edc034b360726a9a0de5216fb3d08b6fbf768af802f2f52abbfa8c4fd3f33d2d0b884a866bcc5765e0788c9ec22f49e0635f0c672d851e48ffbbde80c108879cd2e9fddfe6ddf6ab29220a621198824ba05bac7f6bb4f8b2b3780d325ae5cdd9b56084c36ec5d936f13dc3825cfe0d08143273ac02288e7fe52809c2e02ca7af94b5a3098594d077295699f6fdf369bd2167b1095e58a68c10b5394f74fb22269b114efa7cd663bfd9dc07a16754054a5bffbe031dbe3f801749773a2e2c6ac743adf7b9503329e61d9a26ce2150e76031621d1daeeb6b79432c99452c504ba047804954f37b0ecc32b54a87dba81d6edbab3bab1fce05a817d184fb87245f5d30d3175bb6c6db43a4f37c4fa65a066ede5d06a5643d78c676cdd383fe590f51b6263871aba6e7642a1240a3551dc9c33a2e6e6d3047bce39599b734e146a57a81c34892d3591e2b5362698966db73b57450824908d18d45ebb03c5577bdbe925859fd6f63db4366629b78d526edbd60ca7b580818179818972bac4e0b498979717162f2d1f83050b162e2c605c466091c212a5328b11969f5394135291cafc9998485313688a998e2a7bfdfa879e6c70b7751c779f0956797b975334bdcb0907edb74e4824822ea7036c0b4612557d1230c1f664c323e0ef72d2b152aa77a624ed3594815053ea8e4f88708044fd6292da1156bbb7e686496a7f03a1ca80555351df6c145392bef922506db8ed911823959fdb1b7fe6acf5c114a9303e95f9f9bf989f9bf8ecc2447d01ab1cc5484dfd546639d9601a4e53ad76e001a416b48cb2b8c069593659502f709a8a9900050f5ead4c050388d6e6719aaa4fb0e407a0d6b6e2345583a0ca1100384d1500256450d48281cb543238643c00b5b22edb6ab893dc6454c99f72b270b1e132550fad6d3bc51e5a9b492893aa4864f93b06518e448d0a1744c986d6a68fd4d68eb88b43dad110bb9c6c784f659567bde210f44f1fe955ffd6dd001d1ffa6d94da434610c524f275e2105cf5eb18a1f2bf3854a3aa856a01d8b8034b2801d4ca543253b4603494b4e52e43b1d40d36372a5b9bee14d017774b39f540d04652c50a45365826826e931ab8361883ab8e71b197d84c8ca626a342dd4aaa48f4a9db33a56310475b8ac56231cd8ae988c9c75444b332934a498eb5c73580a05ffc893f0e2115684c5b82ab7e93ab5bf360da12c47124b8eae7ff25d7965c5b6a97d2ed569c8a4fb9f1157652fa463a52ade143dd6538c6877e5f915850a09f6b4b3108a9763cc2a43212cfeaf8103de1a520584be9312206a900b992d5e91a7f72ac6a2c6aaf3fa3591df632564e0a16a369ef532f2cac4d73716950ed8cfefdb5a1017dd2e4ff6351cdd3a466e8cbcffec055d374aa068deea5e6c6d3c097c24a92ba52584102435d29aeb0c42f09f06515c756486b00419fd9612f47d48b6b5b9aefa454fb83aa57871254a1402691212cf55e8530b8ea8f41587f7b0f6bc8a4bdac7a076135a01f23c1a038a1de4c65bc0b087705e17a904fcc62ffeaec7b521aa2055295bc38a724b892d1b9a1a07f4a9227a5d312cac54642f5732359370a228678a28d804e49308655edb5cc849a9082e81ad5958f2a42fd9c92be53d2d00b0554654ccb906908e2b4f4cc96216f19e2962198a2971f97a496a1961ee9dd1a5577589977e012ca46f4e35e43482f3f2f402d207879b51c79196a41c1cbcf0b514b0fc43975dcd253e57313f47349aaf25d94608b56c12e4bb245a9f44abab845afb02e49a797cfa207e268005eb972a5876b712f9f26419c5307534493b80e0abab059370a228c54982249534555c2205509d37d304a55ce585295df4fd0afdb240645aa06c434f0cbd74eb03af3e55431146be3cf2a36e219fe31df8cb9d129898daa9c3188a36531355640f9e35e2d4355fe07e58c712f66827edaeb25a9f69adaabca6f81848ab2c100e880feaf8a50c8ccce47f0db901103c68c4ccc8b948b16302f2c5c5a280beac46d262d9b323a6c86bfd3dd1101060f6ff09ec10f2455776dbcfe01af58a9fef007bc72e5f9a1ac4057ff1fb07ac73d247a355edf70d79dd0fccca5930dfe0c082184703b21f93bcd515c95ebc6d234954ac576401c971f1e0ef0d184084bede21d1c3ccdccea9bf8fda91cd455e35357ec64e2eac42ebab6960368604a7b5bfd83287555577dc5206410827ef45301e96c37cb3e1cdae3caaec9ae1d9e29a50c4250b94735acecd7f49f07690f48fb68afbf852ba87ff64258082a2091c2526b88132182052dd3f7c907124d0028341c0024faad124fe3143922fe0fdf327547c8ef67f5c76003fa4920a7caf7778da8c2ae17764b68f707c129325b3f7ceb88ece7e3b043ea2249c99d0f151014118ff6fabb1f7504f501fb7fa2d83adb36166f4df5add24ef3e7ff4b9d27a86d7dbac1bdfc3ed9d07c7be50fa76a33bd54edc7556aeabe97ea35c477f91c3bcbc90677ba316b37a59bd209c1a45eb468e1c2452af5e2454c8c8c4c8c182e5ca4522f5ec4c4c8c8ccccc0e89ab03635df3561b522800db6fea5711173b95c4468ecd8e053799f28a91f47348422d42fa7b2f3cb4daa532a64668e31461796a0def90d2b37a20a7fa6ee3f334bd741dd325cac0d7cb837daefcfbd31dddca07e9f25c533206f3ded419faa9a41a2dff6fa1cb621d98622901487f58dd7d05d3db821690f76f736b4bdbe1fa5f2224fe244eda160bd207604eb2e2fe23deed31e54c1ae624450e1c31f9e011f3a1215ba101f55a30abf83ba7ad740db6b7b41876d445c4b2aecee427e0821ec95c9016bf7f1a84f361860a800a847b05ed84eafdabdcb217eb770e5ddc2550e70b38cd29d57e59c84581e0c30ec2ccc53f002bbd02dec42082184ce42eca46a7e26438b6182b1cd7032a718d40b961475d1d262f783ac03532164f1f2c202c6a5458b0b9a6279818a39c970331b0c530c4d46f65325bbc882d3c016da057e81dbf550b05f8ef0349542e15e75018a06d0173c175af818e567a153bd8c18306664625ea45cb4a8fb1b0b6efb06e625cb5e58b47071d192a22f58625032dcf6cd69fb0606b77db36ddf98f6fd33d5ec240b34de42bfb0e2b66fbc95879a33be36feeeafa676e4e8ddddddddddddd1b9bf59cccca71c4292a63dda708a96acbd9a0a7f933b1007c260dd2645f0a9900a12887235290213d8640846400aa18c3e5f2c7777ca9dd07c9a87c279bb0f5faaeec6ccd57d94ab5f66fec684acd48db9a089ea376466f7ccda93df300c41534cc37cf9318c0397b72affebaa6451e57f00d8199f6a4fbe7c1892a012763235b0d4f97b83a5ce8ef5816d7f55bc33e4d7a96df3b718b518b56cd37e99becc48d5d378df84f23fbfffd238956d5f4e8e147593a42432ea3a0102c99109a60aa07c40892a9288708419ec122bac30831fc09c00842853082246a8984294281051c4496b8b48f0041127f041131330697db4a78b80527a58e295c48a2abc008a0f45d821a4e08a269c5812a50a53a0a18ba8fd9bf5a78892b6a892c99c4802b6d45a1d587125a9b5310d807a36c8794768ffe64898c21043b188253f10492eaab6090e0015b55cd5c5a0b54d2eaa784a1525306ab92a86e004ad2dd372647588254b2cb55cd55128e12bb494bfbb0b218c1a34c10d42f7cda4755f2a53c157651f5537e4672a183f7ba9fa0cbe77705d88712fd89c8cc055a350d2f236790537e18a2fa0b0c5400c14e4de0327c4ad5552b6942cb9bbbb635d8ff9a48387fa61a04647edda7857a1f9d5854c519a543c3b41b0d37afc9eb6c30c66a9d4476dedb443ed4cd33a6e6df8d7630e486d3799aca4bcf5fae9efe0b061bdfe7c54aefa6ff743887331081144f718638c9d674368d64c2bde3e4ab62a5fa3c3ea642c560ecf8e1e5258464f8e90927480c0deb009d683bf3ba57eaced58de31d0dab82862d1c08a6808dddd214cedc01e3388d07626d13dc61863fcae8a0e1e6a54c51a87d4a8eaea0d79e31330eb62581b8ea1366b6dba66aadf15c35a85a6396fc16619cc12ce81244c2104aff2e2391781923c3a424a8272245477e0fa7975bdb5f17ea01fc7624fa4927f008920ee8a0d0aed313ac7bd40188b11ed37d8dd65ad8effef5479c75a1b566d1fb59bbb3a717729a38c1e77d59acfe54e018c10caff16c6b323fb1bc25132773bc86e1eaad77e5e6f42280524bc7a7a7e807c7a9ac480d1af5dae223fbf5eff0f21f5878fca942b62eba4e84a3da5935245d0af915a0a744108bb2184707d10b74ae910babb7315ca68dacb4eb466dc139c90c03f669d659c7177776bbf6d4ee597b39bbb79c8d4248ee9c5d4ed34ac399b0d33afa6b38cbd59a3c91fda5e77c7f85e77773b747cafa6bb6364f728a46941c05031e7d4b8a5e5b3009229a468d6f3862a84f065bc813ce4eebe54f7453f7ecdb96e655b1a1c3f8314f9094242a2d47304885f53de7c2c2e720092e981f8680ffe8908f5e731a794cfc37dcae7211f7e0f8f73763ca4a9477b2e38a15028b5473c6ce8f13934a415a99497eaa173ae03815b9d4ed2a11e59230e4a6217fbf00f03f1cb238e6f8f6c308a31c6f8fbcb03eeecf0f08028a0df1ad507c440847e9b04a46fbe5542ed1b1f0f589ddde1d9e1d9e1e1611e6e7538a2b9a4062edd5d4a29a58470bd79811cb1bac32831e02ea3ec7690d085c4938d29a5ec1a66006e378ad786bb6b96b9944213399003f5ab6fbe1e0a027228acbaacf65880a05ffff011ae89fc67ce39736a34cef6a6df5f9611ea271ddb0c34d4e0e119746ac80650001d9e33c38dda0f71d47ee899ea6b74a8fd907aec9a96d337eb191299a669aa5dcab90bfa2659ac1c9e1d3da4183d39428a4b7df3c19e6a0576772369162c4e4fb12bf8471c51bb673083daada4bb1a2ba08d2476fda4e60af46ba556ea1bef689a4adf3492d5e9ca413f8aee31c618214b8611d01d3940f950b2da9b59426d58ee599d75779f43788cce10421c3b70252308476aba5e2fb7a03e1ff5870fce091cc42a5518b5ec8b6d07f232738c9c1520113abac71863ecf534cfabe1692f6e4b34466e862ec421f710dfb6764965c5abaeded11a68ed0f52fb997246e67477e756a6da5d35d7439b6594a652cf62a101cd529b7913b6a8031a63e788901bc6287c597577673ee5d0dc63d4ae1b4b748f10c618636c82fa7f9e338e841181b98c7075a0159f631c13cc07984b890b04adf82f8c4361455c2e2b2e215af1679886aec65fbe7197abc618ee9e14dde322d1189f6d7a48bbfa2bc0defd15e81aa3f42105a343e8eeee7daa81eb54f1f40aee0841fd4f39bcebf524066a0751babbbbf3ecb44dbbd49e400b8b1709c04480ae4d37059c4eda37eb32092a9c11d1639c713af773e3f54cef991e66a637c1d73c8781d5f0a9ddd4710ae9526a9a0d1c6419cca66719776ce8b12fdfd8ca2fe42f0429ecb829e073da7820ed448852edf85d758ed6c96e9bb5b1eadf8eca3eb68d933a6877f7138559af5facd730d6eb8f877dce694073c423481198cfd12aad15578f9c0efff378dcdd63d6edc015fcce451170050835a7470e08581df69c1a1dd7d32ce033ee0c9e953fa7fd8b3578c8a99153635f7303cdceb0eb325c888295f31f1a56454bcdf44da6ad945e2450fb9d7393698a981715c18c9414ed159647857e4ce41cdb01c440afa120202476b5f74207e8c73f3f4776f4cdba5857b0acf8e19a00eb7d8dd0ec7cd0e1d2637477f7e6660e4e814d39a74318238919b1c43f1cc4432e777838c4a494d279f4681c97df9cdf0522c544160a4f33082cce0e1db26c3d4de388fa84d41f4204b05e7c577d3766e0530edfdcd4d1d021801abf4545436b7ebdf82c2a7a522d8daf4795d283310fe204595a5ada946822cb284da5fe59e0eeeb2107bba4f207912b5776d57f8bb3537595bf721c74adb7a52268cd139cd04903324829a38ceeee1b3be749788dd58975f69c71c6eeeece5034726aacce898586dc629cf35bc6d9b1d626861beaef2a3e4a15bdbe813b4e2acaa572a82a53d4b46e21d34f55c7d32bc9bd68f76fdd107a429807ee6e8fec26538dd589b33526201c8210c29e523f162b0a0a9170f00a15333d48d1c86e01d1eb178da10650024979dda332d7c29700a9628049a5a9dc47e5e6e901d7993ea79cd27b6c6c3021e8c9cf5290be71f50859ca590fae5a80807eabe403c8cf0f9b155e299a5f1d19dbc1c3013e844024495496d8c53dac1c201aac676554ac6258826a710cb02bf8b2dbb136f06d4c403fafb2a057133bd8f35747fa2b422152a204997429e3eaec92aa2343dd07c04a334aa9ea66194d753172276919d154020eda15b5cfe6fb43ff2d4a539f74c0a8fe1bcc321eaaa68ad5a76a48d5545de3c9c6d4b4ccab1132753c55745029e79c6c323995589bb9d78bdbedbd64842c8369c29903465e36f4f4fc00f9f41c01619103fab1cb55c486d7d267e180383b9c27b548b239393c1cb05372a05488b449abc44beeee395c0471d895431020a09f9f9ea52525a52fa7763c8e36e99136678786886a7f3945aefa63a73252ddee470e3fd35e4f19bc68ac4ef7f4fc00f9f4b8bbd3581d7ff2c48913279ca83b80879e2780c551c0aee07bab23e8c72ecf355384c64b8a1b3091a2f13c560d1c3840f1604d624748523caf45129e137d80725e82adeddeddddbedee45234067077e9d28b31468f6580d5d17eff009b05811a8109753df5c43231430ceb59f6382084aa8f01aa1add630f5996d4c312b53fea90a49addafcdc70055e51cda33196075ba66567adcddb38cd2548ae5068e045d9d6e293b5f9b2cf39ac94e48eb8c504d6bf9c46374f7fae346b0419391c9c814eb9bcf74646a623232050167a4d0cf0433c14c4ca00969c666d12c32aa4a66d1944b755bb0807e936812cd24330683e81e79ba7fcf0792d5bec11f7e9c337a648f9123bf33d72ce394205ffce2a124ec3de1571113a3988cceaaf1bcede3217ec115fc16019597a8348d3935317466440000a080001316000020100884c2a190503c286a9ae81d14800f689a3e7c521ac923498e0329088220866110020821c418030c50481a34730de576912d14b9cbd9a0176b156079e2156016c8430a33d63a2712126694cca597d8a44ea56f62cc15b801b30514a5964b8ab439db0a06fa6949745a547b116f36c20da6373a07a338fcb2e94c2874e8abd9edf1a65ca74c49430eaf716bbcada5770f12b79b0382ee3a7555d3d0a10ef9b95817c19dc421fe99d2ca76dfdac0143ed856244c59f61a95422d64a1913b09c4d7d22219ed4c8f9764f94870a65f08ba9c1f420977551174538938511cf43d800bfc5f36afc05b85edd54504b8814f95f8550673b084af6a991a1cf7565c69a6beb12144a9241caa9f368d436fa783adc4e155aa55525ace3446cc2aa514276cbcf99b31fd0517561db2d5f09cd9bc893bfd19818118de24a2c3c9d69292205bcd7af35df359705869f7d4561539d9d830b3417f6c7df70063298729759f7c9056d342c7edcebe988b7d17adcd9f5fdc03a1f13a04f56c4de0ab49cc2a212b7c0dc06f05c4603e1b1530de29302778d23e93e6aae0a928b2117b670fc34c18dc1563ec6a75c8902ea54247adaadfa593397c56c0032b61e71314959d8477dfc00cd06b1c47e1449615bc9871ede547ac1967d46d951bf258da8b0d8a73d07ce1ecb63c1dd267d2154a31daec48b42f05d31d279dc8acef2d4439b3c6eee726971a74396ec355097964f1c1a7ba1d7e28ace3e4bfa323c9a7b4786df178a46d65844a0764d661b06dcf9b43d2d9c810a7245feda091cd787bb0690dbbdf717136c11da54c2f1a42bc18fc887ec251559101ab071da2ab680ff30f6488b0dbf1f52e2477f323870a45b80ebeb85426af224b6054f05ccf80ef908c28decf8ef7dd0274a64856dd05c01ed3a991c20002b1249e05c3416b90dbe281fa65b20b16d0cd8cb9333e1bd0f6a3989e6dee8db7c13775df0c7d4e29ae946b8101870824c3f02ea0485b2d9857cae7096c972a12a43297ab8a695a791aacbbccc1698e742c2905753fb83d0d699032ba90fccde94a00c95a93e6b1fb84dc4b3da6677d622f903a2885909e30a2478212f09e995b92157fe44b5dc7cf087c0ac30483a94235da60aee560488f189a37c5337580f6906325bd386ab16f1de470355541c09f90689083317c788cbeb865582cecb7c360f6f726272e9529254f4b0b967b5d324328452385fb012af65a6c15435dc2ccfa4080280d82ea9e24a101c1487ae24b393d1997eb7a17e444ed6e8356b50a8adde8fcb382e2e202348d4ed6a7ffd21ae4d45c2c842f673f4f952f71b78202b1ffb88491fbc2a7ce5f3b15184c6ad5bf50307d6fa25b97fd114b44508f01cf0af8c3018fb8316fb69f9c0bc36801ba1e38b70baab77acf0c8da85d3d7d024fab6fce7f00e10deae01ede4e0178e3326d5b818717c0ec024620b0f1b9bd48ba8ce995174766cd29bd674dda95090b805b4b352d5900bf1b37be6f01422d046ed7d381b8d5b876fc43b83519242454fd35aa20c2a6980ee67483e3c18edcaf594f3cc21b2a91a66c138f3e887356a4e0e6303830912f2a06fb0eea5e13efa610466e060d940d30a9385f00327fb2e04a38943b61635f2090795f066bc17d96f08b60d9f5de119b744cee337f5a537a94a3374899a42e7a9f6ee4c4821ab5bc8328bc371b47993112bf652e02cb8dd8d7b58b5a8824d45fa59d79862c5d861e416d9026b740aeefe6163d1da08ad6521641d8c9569c51bb0d9b538d8d24a6d74d6f4421780675ce06943ac73c086caedbb54d78d4d5ab81001666613fc48a3ecc56c9c44ec489b6290893c30ce5c3283d503c5bef6a30101341661d50a9c11a8369ce82a8693d92aa3e7a24556ce245a665b8098305f7f8ed816c607601e7c843482d7233fbcbb015984e87fa7382dd52689426fa95f6f06b33582b00e6fb2b58ad32377e8043b57e58655251d1925a266007ff8da507d30297325b66876463a3b144b5fab8b6ec7b56b56e2826faed65d1016efc67804864c4dde4213098df4585ca4156f5bcf0d828e88eede03636bf449a9f4d5ec8df2c08ba26fea6aac261864b21c3f066682d96cc64cdeeccbcbd2c0c26c41574aae8890db0db2ae20a986e72769ed7b359b91596ca3ee6ada873f44341d18a3fe50c2263bc28a579cbdb05225a2fd31e93128f10f4e5e6d100855703af73d684784c7548eae4356fec905d6987de5462989e14c391dc11b11b78dc29770a2d2d3dd62efc777e29369cb14cf1a5d9261b92021202ceb3f72da48602e0f2445ddd4e4c938ca01eeef07c32d0400caab7792aeea8991704e4be963faee2cc53e9cd22903daaead83d44c5a5ed4d914d102496f1853d260214e57baf0d532c1066b338637b48fc310caeb2799cdc92aa095028eaf4d92e780c0f5ef338e816600aa7e07d94931a6deeefdba89861967de1e75b12dcfd9ff2d7af1fe72ffedd3cd20b76d4367a5aca8a9be30ce17485f5a56641896495273c9a21a052e38cdedd25953ac9e161ca4f4e15f62113860791e84d000bdbb8e099fb6a6b2b370573ea4045bae78ec4cf7abc10e6fa39712e5bebf8689203e580808d2e853db1b52a31a5763490f4c0cab99e814952b2e2679f637e7773df84cfd3b5dbeb47cf025101586c1b327806abb554225810b71b1cd0afc0ad571906a25af5ab0b6e00c44b4b3ddf1445e8863f6e186618a073d9b1042ffbaf9cb9664fc1b62d083bba9e684299e2cfe94f70dace3bccfda10213562171914e4398ffac514b32f616d1b576cf1083314a0c5f5550e90133a220b3011c45de959a3fc966bfc1e4e1be7762eab1fb2c6b110d243d14232602865871451fadc8933508e46620393528ff6ae49c52b05db91b3ea9921a7f8e72b8a6fb2ab80e30726be52834b836a0800152092e5d661b95816c60651d0f624242ed387759cc867096cc7673cd0de2ed7bd56e54ba6ff78591de8f28a06cb54f6602d2005185f14c01b602d91bcb8a950b68f87303e8f91b55f2229421f9f67d188faa081299f0b68cca0bb9be562d448ac9778d496e221af705979362ead23d5c7ee341a24f9718fb6c69a4fadc930ed85c99a08e0ceb6e0836b5c40f7da969b0a0f418782fb5f32f776e4c28f02f1f5663284861f710d48e279c4ba739142c7e990e119ddaf22da5f6a6dd9adeb81e1c9a00ca146714d9ea46ec204bd3fa5a7fc8c0fd653d2ce3127cc7f035bdd2101afa370f53571a8ac23cdf6417faa5f011b7e84fd33aecae4c70f3435813d69a63874eb7b47eb7365ee65db7b4e5d2672616d078d4a3af037bc70ee82ad092e9dd6bfe2b7e0ce76392146d809f99ddd68cecf51f80cbf8f35d4ad27579bebe7071653a925cd2f4eebef48770a97aea1f33357711c90f39d900c176404042237b9d1c9f6f148205f40752cfdd7299d24629f33ac3eb1360b64b78e0bdb300ea93d591c57e2bc4aead45e77fdcc2ced6133607b9b7cce13346fc62780c94553efa99f58d63987b2008c8c7c41187a36b4cdd840a3a445bf37755e88b7d08f20039e8e07182e76dc037dd9bb9e95e0e4c64bba9941108cfcdc350fb22c0684f636b27802a0ad4831bee1408b6ae8cae87dc9f8eccc8476a6a8e8dfb66bfd138581aee98f1fc96a16b49e1c0986c841dcfbf14e8cc09c89eef873ee6569f348316841b000e9317cf33b8f80f9527ab26f7a0c5fb7eb3f7ae3f054faafbcf8959ae62809934808fda03d36edcac489664fe589be0163a1157cac5691323d195b1e35e1018c08c4de75e26bf1d54c62bc482890341054831011927c14194386763897ec414b805e4d7951e115b43cf581bc5d2fb32360eadadf080748866d7a249888e05ea0002945ed6f243fce26975bf16611b6774a254888afe426267818277a975bcc0dccc73b934b915fcc91685cad605c7c8b2f10a0321cc29202369a92276ca54c6583fb3c52b6bd2f9bbc60ad85b82a9709a24e181338e4d3c684ab27f884bb4c1a0384a6143aced76d7984a6f27115328e7b903a19509f3b876d26a1e97072e32b035d74b72779fcb2022f90c05c3700e1ba7cb257cf70992a02a6d1e93d4c75b3396c0f8616683d742cae9712ad05438c91b59ab6b3a2b3a6ce268ec139e01066c2ace2fc8d041309a83e7e53e17722b4c849f70246993d3743d2ca3f005593e24cbe4542a3cb46986a5220a8412d114aa34a42f7073ba0cd27916d40137e37cf68f9dd4c5d747910cfc71068ee573c4ce007c24b36a58cdd1f0422b56b34535a6aac019efa4eee2ac972da90946f505eaa98eb6ca4f16f0fe2c7b50640a9b3cdbaef60e3fefd21bfd08b0e3a7e8879263346ab633deca92da62d7b130fdb4b7b9f7db868d4bea47b589e61e0368b890b5e1225e167be3ed5ff26a11291702fb604d8050e57b59dd4e450ccd4207c51940ce9de6f54b25bd1bdaad359e42aefebef4aae9f797a90dcc5101859098a27164a622a0e98f17eb91b0f5580c5c0f44e1170d8cac7c7eddcb97bbb10f977f29d8fb888fcc99a2f5cb60e6ceafc252dc7bed8538bb890ccbf3585471daec88df0c8c38dce2439e55e552340e7ff35936a3c68183b5e4309dcc9d8364e0bc555fec90529c368c5bedf9b954c11533a34c6f4ff399e8af475624d5b1cd1f662a4be3fbd0096bbc6cd122e0d688e62b90c72955689dc2f123fb86321e7dc141fc2e28cb12c8d3f970d0494b3cf19044dc86c514403a1c51d1eb82c411db08ebd2953e26352ff56f7a014db6e835dda09e72d485d333f410436a818eaabb61fd62bc0c1fb3e9ef4c716e879c43c6c32880dabc0a12254878f3a4558e8b48739516a41879e284b9aed12695d12750f505196ac1439bbfa4124c72bbd9a540f4af61a8b52993b7fb5a4d5800a40ce9798697712aa0865f67faefc93a59c905da8c51b2af95f8c3a98639eb457729754854ab1fc50367cd1247c58284544084781aab42112a41f8302791d900f3ee27cc56e2b7127ee4e989079c0006cdbe9b6aabe3b258829d736825a183ffa6c4016273bcde64b49d6ad05f2137bb3cf71af59e1866261b27e7120ad811852e7e52fe88beb4176b4f7eb5c24d0ffa4a8c280a788f7aebcb85561e76cf89a6eb8aab200f3881be06934dd767e2bc174c45c3f3c6128c8c83c2aed00c5a18e48d39d03f9ff30cf704f4519d6a1aff40c4510d37a417ba360d10aef101335d349245f19a69a2f8259341f8036f527501033924c20fde5ffbb82d6d13315e387bc9bf80adf62a4d9e00e8919ab09ea7cb8988790db1a4f643e92ceb86997b9b4216f50f6601b07fff628e1e56c955a67fff2f41319f050e2cb72b5cf9ed97fc3a9da657db173b84fa69a3caf92a976a3cfbb594df000f7e5034a046176a52e5dde87962f25e97a0c8ca0cf395e4f352e2f1710b2e65435948fed96355c88a918aedf03efa9dcd81c1e286b2a4be4f027594d824cf860d93c1cef4069285f0fcf9dad5f2e1d1fad1f6224bd468871c8ceb0f1610ada36c395cae6fcb00693b85b3afa11863e893638fe81d5cda331014ab24590ab99fc7f1a8a7c9748ae57255725e31f834211e9fc10062950e7484d3f5a4db8c547f34ba7824f2ba60b413185c0967151713db3e3eebd59bb5c660a82cba7c0f85816130d46a2d89ab00111e407582a1e1cfa5400f860acdad44e22ac4922667175c3ea5930b861a82dddeeaf6cb05534e1d491cb9817741616af551b84df6002bc592c26e89cf5ade96ade7880bc992ded82ee393b169de773a083ee5f0f693ff08be446cd31dad158259a1313e8ad08f618585427bfcbb8fd728a9b809dce70bd006fcd1acb8fd7fa9996ff0d7f1654c2e97242d015f68dcf72fc04e373ba97f5821bad13de197531c287005150de7dba8757c04d8e5666c0419f793e331cb936cc1d7a63203b1e754131c1cb703376c3149ab198d89278f63a1011aae94ef15aaeb647e645d3354f7c1c94c35f255c270ecaee3d0dea0ab73f5527dbf4881ca7f0a1512cd9524b3479db0d44185cc76ca5683ba7c6f8f94576f18f5a22efb70142989baffa0e8a7fda62ac465d9e9f63e052723e0d3cf0af68976bb2fb3966e6de283d8dc74624a80b3a9663267fe6c25621580ff03d40684f65cc03934aa8eab4f6d854ee633f0320a0abad5f1deb278c913739e8dfa6e1a680a3c627ae9e2db12d16c29a53fef2726254d3e3cead627ca1bf9ba236a219811317f53893d85d6171e45774b5a70d4d2347b1a76c3ffe28fc8f5fadbb81edcdeded7216453246bb747657c6d6d77578deb971a51e66c80f3409ce8f93081975ec405528f13ad8d9d164de35f664822552d2bad414acc39f236dd7c5eac84e372dcdb95847e5d21cd3d309063df05da6dcd7f6debc0f8e00c0e52d69476f50a70f21dae80d55a940d58cabeef4100f898f94ec8b02ec3618e2b51f3f0810d1b437b0c9a66e1ae0dc66cfe966cd4defd568f81d82f268b905d88729847bc374d680781d7427197e86e28889d845189a6218575d8b51280f9f75fca3b3c78fd3a5a3aecf337cd958d2b10cf9936f1d14073e7d8f8397ff730e9e8537dadd0bd0022107f136e99e9de431cc7f7f52f2ce794c86ddf15944b0afba2309ba144c858a4ee1941740f93397ebcb5df676c60cdae6e0c7a98f8a40554dbe265084ea74d1a8cc5c5cf25fdefbaf66ae31cfc4b9322aeee17cbf156a19129cfa2ae6834bf4272540364b967eb51c96a1ccc8b97ddc624b2a3bb892912f98fd28657f4849df585030ceea978c0e3541d2754b9342a8cb364ed81f31cba02f39b3949ecd14b413a8782b9982b53117f3e82511bdc25803c6ef950f2d7fc8e77035877666f7e54bcb002dc9d6d4d1311712cfd45d4ce54a568a6178799c45f1d7f908c28bf65ea309adbe24179303e2625e87ac8d4b5785ac8158223e0b24d1db2e7c0a7e7acf138674842e3a4eed4d036633452453be86c307160672e87082d53ca1b03b3d0071ee14d14bb17abc42684d37c9182c1f59639e15afe0dec570878e65f75dc35d74d758c2f1f79785aed626567e0809e5cc90b00ec03116a1418b32a9ac64649e9c413fb0b20bbd7086a71939a113e9b8d7d873f06bc8bf7f606364a1a94acb7f1649c35051fe703748426ffa6130580b777cec48519215807c55d7261cfe41a73f75c7cc0b043a7a2440beba7d1697b9567c2b7852095a440e61f184985dd521db9bd0eb0ec6401b2b8f6e10eacc9fe353f0a4c6a5c5519030db8377f6e02764a45450a41992b03a25a86903469613b26f6f0609a1d7511ff3cfa482b63e2a6e972bab18af929022fe190820e2d16ae514c1e9b8877db5523fa0d525eb331290ee564085d3058b1fd4e9f5bff697218946b7d83b5e622413f05a06d7cc3d3ad5835e9c3c78659371c858c152336888f6d896134deb54e887c24b7c082c848d32dc15b2e524c8c647a12f6512dfa3bbbf0b910dbbf250068a57c231a71a2d64f628d3f7a4cb4a51eef028eaa3d914c93349b45460ba1c38ecf713d52151d73ed7d12b820d65af68cdbb683879fa853ecb8b75bc8c172e9428bd84d58917789de1bd7782ae0d312dec4fab79304b8b3239d8086e2ce7f8737ef928faae737253bd33b0cf578002741e6e35908d6e1d2129d7d9976b6e3b89c1123d641e252806be581cf8354dee3546c85d7fc002742b53984dbe0453035d38863e480a0b5960b5f3d5227ac48f616250441ba8aec32b705ceaedc21192c2d8255ac951f6bcbb8af159cb573355d95bd7077bdd85cda6700249653c470f09e73a8da210321929d22a281b991713667f7ec5331e471184b8073806e79537a3556cbae5832ca1fb5737cde3023a00ccce44131a8086c53c7e984f85c981875a16281b600e8d6152b913c574fd917682681892c6b7b5c00757cb4106451e058a6de3342428f8bee1b05646d0006771f17a5811d270ea98f402bd67cacff782243ed9c43dab80a434c113ce1351506c94e0e313aa6ec7973b4b3ae87fee1851b4b7452bc35602b59437835b3e1d136d6a40e8429b6b621c8b7707656afd13a62e0cd88ec2a22ca511e901e989995ce2aab4f38e8e5d86ae754a93d0f4f2c5a98cc58521b67e6140cde46f31d2720b1c940ef3d5648823ed1b53c080eb6e5dc63275774dde2aeb0c78b1e0dbf9a4d72c5f29ed5f77433ce68908355387c745b2829c42a4e8022fb1f232a7822dd066987e649e16e481b28a66f5939a5ad0fff2f59ca6786bb3aa15f8dcd9fb25852d73ef51973caf5311b2a489051d8af68e653939c6f2daa4a418a7197ecdcdf831d2ba4add435285a67e6db49423dba0c3dc4ccb5836f83c7d9705ec75b4f019bfb1dabe622b7a0a73242b6ff39e408bc5f946937d18760fc4a9a082184c2c5183d3f9a6e81f071a96242d6b0cad214b1aae4a3a06cc0f0cf4d287b5d50feea27153de3bd1c3b2ece08790ee74de111376d51bdf641acb0ecbc75bc735d1427d8d3be1f9350185849d781537e22673fbfdd6eece35a9a22cdb9bcc98a903d53f3abd2eab55e68ff8c8d5fbcd28f4e212582ba17547a6835734decbb1bdf54122509ee113e481e48a1ec1874257c7331559b2945db1e09c58f50d0dcaa80fb82748b79d5a18de4bb71174d3dfa1f5c18a3877f543374a95a74c56de223a795354146dd9814322b6b6b520c6e630de588fabfcb6b51a58fef58b37e5aaa486dc683cc6e7fe707b173da9b723b84bfa8683671cac0e22fa79fa076f02d0ef3757aa4820d66c4d49a0b227b585c6ef29e1e551c186294ff4a46d5e36a32081536aedae4b8177bb0806424a1795ba948d019da57ead7985825a588b171aee1231bf46612f34906dbb97f2c0636204d1b9cccda743e7837e9eef6a4ec04c4765792061fc33226ca3b9ce275add9f746e5e04a4ce8a9992760bd19516b64a9b6abf7d5838ce160e68fb3be6c84d02203e7752c41eb827a021bb3f44a64c34c6157f9086429c46f7e1a2da7e8d67ff0b1815db9b50fc0134202aba0af5bef94abfa2dacadc6adad97d9032e4567d1fdf2a6db5a96bcab6825ad03633b53a073ad332ea121268881bc162245acef4f500ca069199a61c1b235cc8c322fe1ee03726e9d8dabbe35212850014f0067356c84c54cf92b8298064a5dd034375a1bbaa4058b7f8c2da95b6b8604313e08a05cfded9b549239ca71a50b1126f3c700eb49d4884fca590519a8328e94f803cbf6cf54e4a209db8877c308eb94e2b596cd037bd0ceaa63773859658ede708eba86977d2ec706f26047a54ceb397328b31204e82029830a3edc1e8d05919d6594ad6794bc940ddcd930034b6df1ff9e8020223680c15d2e216d289961aa4ec4e7e7eb7204405ae2628d57ed8a680eaae32f199b655179d016ccd8143d7581140523e6b3a8f8948b7db2a88a31c28c859c0ca375a76b5d653e84ec9ef10be6dcfe1da4be9f26cb1690d96ea8bf60d851fd435d66ef8bb2c0a12d26d1f3527dc9b03fc8043f832ec93fee9fbb56ebe4bf9f04c6c6f49d92bba5f52bad469fed90ad05754707f292064bb1b590a05847973baa27f3189eef28ac9f24358e6db979dc1a8c2106e9c966c85a2c61d421b717244b6017a43869ecf8221d9459504db861dab023f36c97e197b1dfb2ade30656af57527907fce8c4e8e452194d1c206cc6387fc48f8b8dd6039aebe032b8786172db00e6025187f0127a7e80d3d25113a98303148ea3948a98e5d9dac38f6e04d9a2dc1e2873f6fbfc1d43a7b264d5567a0bcb76727a0012b88529ad1c9510891998255e2c25b8ceb8bff09782b873d605b3ba3af40ee9d1a7cd01b87122de29187cc3e865d3258c63ce52bcb6136e1499d8bd2bfab50202d62542a3bddff6b2746784355d592d9217013e2198523278979e4621c1a8cf4c1be27e46be2008d6d69609e21be645048abb642d9ff9ac8b031b5196e0ed61d6966443ffe6788d621c724d082b137f9403f9867bfdb792825b6f3f17b6a44ab655cd8c1c2d09af92561ee9f5788db9ae76e354444c7f1e9375d05e681708ee52b959cd624e76ff8bff431d05133738ef600d7eaaf162893f34c979480434e2eeb41506939030d9093fb6104b6f79519997f92140b600cf6537f9546da687024a0135497199207e64dc904a0489a3d8948638ddb0d8f261b2ff226f1c14b00b526ac77733076d019e718926e9169c1a80a0962af29d492493c2813d78f0c1dc16000a4117ff07c814500d604441fc13097315cf83507645a62a921efa810e9a25c0332d7a9e1365689a3aed987202a613dabd44b9d6392123bb8774f5993ddf17075c28bde4ff7760a07309d567df9b741cf1450d802e53f754445a373f014faee1589e0fe2e71c3da7d5a51c7b65f801266e446a3ee39cbc05d26cb899adde6a91376bf6223284036ca15aaa928dc110a6980558e10413d4f483eb29fd3ec58b0549ea1af52a3f127d1f0d824248b88cc80db20489494545de0ec78d2e6af7a25bd187af8c81531ea895362c3d026f02da324f2dd17fa6b788444416e06945ad926ccc1011817426eb51915119666a63f22fb8881565f0dc976df4e107ecb691308bb5318a81d9e966a17485ff12df76840e6b1121871e67915777f12162091b41a7bae51b0869812dd4227eb409a6fae3235b064956b715cae8dd45d179905c8763abf797406fd67baa03554d7b1cf1992d1bf91ea2e218ba61debe7da0bfc293c5d93202984f46990e0c6348b3b10fc72e8498df31f0ebf8704c5319503a7da75e95190632ca9bcee9c065c89743d6d4692122afce00001bdb2b9c45e0803810dbc4c824af9d24c86afea17298d6018cc54144106a152d6ed0d68823c077d77fc13afc1e540896f89480e8c0124503feb387d1aadb93fb10c1a271eada07a3ab6a9b2dd83ab6851175c08e5f1df3580de7612f17823152cfdd6d9f7b85b36a98a543d2bd2e9694dce1f5135863e208be3ea6177a8329f6274e72eefa2918b9015bf810d14e5673ad4f48d806609a0938e402afc39d6e7876127337bd955ef6ede26feed4d207e2346c0b32ebe1eb6c558f7fab062c02284de633eb3bb6790815a07a9db645ed407132ffef37b1a50c47858a893c372b8203f9d072c87e758e08f13b232ebc1fafc5eef9053a954e3c1280c071008f0425fc0100c5ad207aa6742180952ae532f16c50dc124f4a95c3ab82b84a2bde308a7b3b4c7787a2c15f4e1734006e3bbe9e9d781c4ee6e9173c80949c02e295233d85992e607384f254228487733c0080421bc050692ca084f1f85a9e42168c18f6f8e4023d041efaa7b295092199fd21c9a5c74c854502df06ff805bb5b6bdae341dc60c7da07ec26c9699d0e7d4b63b422441c7da9b5d6c29ec449cdebf4792615d687c027aee099e7bfeb917c0bdba601f1d4e6ced630103d314348868ebf90e4b046eb5163040cae23ba045a2f1119b18951b22cbc09c70b36e558bccf74f3c5f6c066aa62e78cb18ef9ff9c9629852920290ad633b3352e75656194fabfef8ea9aec608c9bc7956e305f6746b9b4eb7703fa8e54d6df80588ee425c7c1cad46f0a395b2b04101f30f97a060bfe93331bfd3dee205589179e41360dc1b3121f2453d9eaa297b9e4f081681a1f5a19f82cc357f3df81fef9e3ab138a2529c66f6e2815270964513046d4c2dea2b0e42d9b111f994425fd610741ca395019dd06f5b5b8054b7ad3de72ddc5e2dfaa4f64466d5b48270be8819f05b58e23c5d58be07fec2527d8342a2a3df91e1626eda816b0a649317698456b408b5eddc7992fe4f3a2f3f8f0aa5a37f0e316a3e5ff84a83299c2793657535c808eb405ba79d7a3a767b0300fb81210f1c9da48311b436439cc6dab7a49ffd94137e0d5c2d3f58de12ebc92663234f75694358a68c0355cbf27b75dff2b4c37732e53c0e6c22202c82b08fa20d2e8ea743d3b1dee60e61da1357113f1fa18f9ded24c56319f0037bd4c496141932af1847ad9418c00ddeaf7fb34ffe59b83b38f83130c30f9ef8a29e12fdbe5591a64dd5be0021a4b5b0eb4280ef579cf49952b3e19f73cdeab80c756adfe5c945fd759f60fdd4ea838cf7b3611bd7b79ff9a5d160b9cec387debfb87f68f36a4aa150b3c864e7b0d57dcd33488e7b2df9a42d7d46da66c4c5d6bd25f0bae62e25ba43136fc72ef7d1334841b2e958f8c0667501e61677a1ef4384aa9bc7ca21493ce9e53d161134131e8111417551a6b73cac21559401b80e82f92a746972d4d5c5437375ea6e741eb3bbdaf440695c304424c9c98634c781d03b38b0f9668fc9ea2d8b5d9852a95957b07686a1eb0a0950a2a48ed32be04786cdf4a4633cfcf0bfa3d14e4f5fd2171e60ceee7e507d8a462a540c592599fe8480329b6e35a699ca5a8101853937dd24a952701f14e2b5c3b2bc47cc26bb5092969ecf387c59d113558faa029ee22c4c11de06e0d596045f9dc84028eac861140137c3d7818f347249fdd2eb44254ceb54968f79a682ebada217da25d39f9d70448885339ec650d4056d1705dcb904f1af0f122ca3870208f324daf1cfb33c3ce5ff5ef224556f92d9ae757346505c639ef78698b5a02a327e7279025c330d43b96a5dcd94de2fb4c0fef0441e784859ddc34f57493492d85ffb9c50784a6bbdcb10934ed576cdcef794d3bc8815386fe34b65347fa45340c5c4a209aa235922191fa149d977cf51e1e67996979cde23e843095d0247ebf6e5a83bcdfa44fd1705fc376d66816111ac9591859f255dbeaed94a031ac64ba81f1f3b64c4d021f033ce3acdae77ce49d2fb04d8dc9c7f86fabe7d421024957704380b5e046d3089c16e7ebab9fb3a01ff0fb2461bee8c4cd13b354a3416fb9485a3a0fc801f7b484c6bf8b2089146073eaa7808403e078174b313a236aaa525c4479598055ab10088c044e3391c54127dccb735fd1d5389abe1379d86eb43bd9a11ac6983b99faa98def0b9d8db78939e74155cf1f16ac6bd0a4ea61b2de1a0e9ca75fad2e93ea7d15c488096d151eb64c3e8f17f28e262fc69fb757c5eef6696aebbb4bc4449d8cda19b7b81ad96738a83f9c8395f895e7c1c1f10739e350f91c84e26316517c6cdab9839676060cca43ad06c85f88b34b34cd4df4d6be06b5061bb569eab837d2e8513a7452fdd25635c3ac734664109a77f7e6466226d3b9736770768d6fe70b80c7883c9ad9c9d41945a0064ac336450dcb3bed913992dbcd492d105acfd165234eebb77dff6dfe56fb5bdddfb4f67fad40fbf918e1a4a5558126eaad24d546a89ae8a393ee7611aa12184c2cc8d6cf06edcbe5dc8969899b95960e2c749f226b43aef66fb5bfe9fa6deb6f9bdfeafe56fb37addf76fdb6f95bed6f75d9affdef3ac4e0365c78c9a1011b0fdcda64f640626cd60f03a7ee6d1695bb23884aec1ea4b1872c619c63e66428f54168e839893d861883dc5215012da67825054b3eb441bb019b2df030eed6fb2a73ea343e93b96550a22ef744f5101860396ee4abe69b552ab2d695103870431c0436c8813f8203f145ecc21a094e4e129c94e5eca7a83a1a42134c2dc4402c7457c000175a6f0921c838840bae5d03cec59cc542b734d630878d4724f48663635261d16ebd57c238b8cf4a28565d77b18de1a0c4c88f311ad8cd910cad511f8b09d82783ad66cebedad83e3c09ab0661c330882df31a99c4dcaf5603db1e9071db2100b61d0013e7e00b9cb2d69300bd31d45c41f47cbd2841f8670de3333ec2aba2c82192e000a4886722d7906c7f3bd1837326f101a38111a16a05e9c9fcecdf894de83a3ba01a0eb6ea94950e946a69350c344b02c0936d8dec7c858e10eb628571f5e3ef3e7947ff867fd5779800a1498cce95a8d75c54d3575426c4787bb52aa1b443da57292f886e49e74cdda32326aaf6aa7d8a5173ccbb8be839aa395b651c1187a8870753740348ac0ef01a2d826f74105e8628aac88eadb829afd47bacc3d1b8e23d0880019d024032e8409f185381f064b062c5ee18e81b11bc9d219f6e1f5c1c14165cfdf787498143471085a0e134f8baa4be14e895ef4952274935d7d7c35a7cfb9cd5b46ebcb3b007d1496afc722e69d07518f79f44b021d98944532dc6639c82a0594e0e927f7553376a5c5aa46fac6c06ce5c4db42d1b7376fa441a19049c6bcd3270853f2f5f759e1dafa78bb04a5019ff5a561824203f1abbd7a3e553f7346df33fba9186158cb8c720169aaa230484c71b4e83bc7f9883f629acc910f6ec58156ad8495841d3023108102ba5c85f7ab3dedc86d1753e184d30a1571e0d3e9090fc88036f9cfa001ae4256fa1c45c4d31aae3ec942d2807843e44d446de916f3c7fb08c1b25df1b539a8a3651af31d1ff2fc9ce7352f260e56e6033ec751ade8d014dff02edf9f4f3fe22d7889cd8724ed3ef3539111c4a932a706a1c54af9af0ae9e5b25d3d77f92092a8764a2bf86a4a162455da5b4ca36667df4f99f8e2e4139481511415bb1dba9036374759beb7f52752e478f83857ae99f34b86e1581457bb9481d0a11aa3a648a30bd34300eae48e09b6811731aa0bfd23700830a76eb86fb09126a9823f816655209da3a14e1b784ea7a16f011865c17e56e83d4a8bc1aea95ccec18b7d6e3ee70b2600e4f0add0b886281c6a7f0b0add3ee3147db007e70dba154c26e3b7375badae531a2e3748fef955e6bd34d35784869a468a1298ac4671dcb3bf1ac33c4c47262423c37da69d3c1df7a14f1ab5bb160d507dcada05f87cec75ae55919f8888352f69eb1e4d87c4e5a9ecc44e60e080b97c77bf88a97171e8b83c97bcbdff31fb2cc7b76a59f3c40688bce92fd2ab0bf610b1667009bf2a7f4932e3729d3685725c59fe710ae4bae0416ce57c562e04913552b1b4b4e9ba203b80c2f90111559de35a259f180de14ffdad408c4e72101322287a30f847703024e4f406041480282dfa4c61e16dc886f2a23b6afde0d35113176532dae8fdd91a494b0795e3f4804947e66a45ba1a2168040c1d946e92e3a76f4bc97f89ad0c181f9d951c1885639b22d0f602369625c9bc06c295c502f88d750fe22d15e3ce3d32a98fa6902fc651540e69bf69128f6a8a0e3946834d08eaadf4d4f6599e07843a29c2864376aaa69a5bfe309fce54dda128f02add17a0ca2656476fb052395595a1a2e8555b8bcb6eed8ef56a540210c1e57070ad3d8008e531da2e4a95221c9dfa7d9a1b3a6cb309d98e934e25f6caf79cb4b6da88c39a79fc17dac53cd94dbc447686acf001d52ca7102274365c9f80057e3d18ee92797053d2abb0a2adc9ccaf9295842125631673a1ba001db8d89311512daa1011bd4955b5407cd27043c46c6f3333a6df6c4c2e3b22cb496656198d1f9a1e7891610e2175a8909d8f73093a95b4d9acb46e73c2cc229f8d5b569a3f37f3015d2d9d99d62bc45027cf042f4297d6d3dbbbed54345f52673212f24feaf44fe0ebb1e44276199dc07c9f08e7c5db085d6a9fe1392e135ac0b16d9ed5cbf5d1bb0ae3d0856ef34b3692712bf5e8612f804d9d3d65e54e6b27c6aec766962c6a52d9096b0e8e279511aafbc647a5ad70866aeef2f035710405cdd51117a086f03ed14b92845f91b41b7f4cf7e4cf4e808804d86dd0ad4810aad07cbdfa4d6790b5b9e838762d7007612a01f183b8a8fed105eae48d10629cf072e2ce9bae5bc4178e3869d01fc6ccb8ef0600379150c46fc68201e0cd4eef1eb069f864bbc7b1dafc289ffaec5e9d1405aa48e500d2b85922246a8b5d74eb1b40a2b58bb981f5910e47d98fefb59518215ac4abc4a70a0eae9d2a72607512d4cbf2eef435dc7fd57f9072b3ff0634a73f69a342d30cde4407d0e03b6263d7fb050cdfc393c965a313eb847b3f09786377c454655d9d0233f150fa71b6ea0c397b4baec897d6dfd5092c591f2cd60c52c00ba9108a8279712a8a7eb5fec9cd299999e241abe8023422175609514a6ac749595f03fa48838ecfdcd4680dbb81f78044e9b1d69018ab88d24905d101ab0b570f6f72aec5c3fd9a225f281c44c36a9d3000048522f5cab3d48532b1fe41ebc1f24ea81a84cb11cad6cf73918a6dbcc185f2f2ff53202b89bb9847678514a24a6fd4439b0bb6d9ca9265e928705124f9b6fd4cb55c4eecb8b2a26dc82e12abfb77161ed6fb11b8f08c3260ee094a60b7afc4fe11c99eef528d6bab0fcd7ee0dc0a1caf48c3bf07a27c16bf72a4983e57732a0649caf644b9905ed48ec267c5ec93b2c7e85aaee92ff5f78fbade401b27744e9d83a6d10ad3418807c81b9b22c2ae91134328eafe0fc0cb8c2b260d83ce6ec51a5580edb8983d0060710945b988b6045176df65258831cd87a20ea510521b62547cf15251d525e38876485cb713bd437b3a61da0ede2a32aa46ebbb0570fc6f4b8abc0847cad1abb809276b8cadddb9339df24100e9fbfaa2a31664d7855b52f464fc861a97fe5b704eb340a4be06ddb3c6ffcac2bb42054b74e417c83658a5a43d12cf138ed58d8aba5e248ae85317d4d1bcc943520fd824a0f10aa157c01ec0aca9da05041b82902c1600f1ac10242b095a2ed8af0dd8594b50e39200fb150e2401d2406b02e0303d12137a8b11eb211c832b06c53871820129a50a147f74ea4c3cae9c62871bec082fd8906bbfedfd5996a4a1d36294f28d941433ff1e2d163c62e16d0cc5df34d0c358002bc9a2c0e3dd871a8b5bdc5c03045d5dcbf733fecb84a0ac1c18cbef9d787a21d7217a84c328de6bf57fe141aabfa091a6966e9b11cdc5bfa7e7d7a5d3b04edacc417716680212e69e85f94ccf4ea5f656961ae396760a9da1a1c45de742915aa05b8bb8010477435ba60093392f43d4adc635c0497c4ebde960aa47a4ca384227af57d43008d7461fb4fa163156e841661a5c71d52320d2bba3013d2b9d41d582dec70ed44626bdf845a87631e70225269947a3363c6b0a52329d808de7706942f2b348d0901250f901de4feb156ccfc87039d40c53e379daafddb206fb0f17637aa95dbd5b7e1e0794fbe997adb35450d7bef1f76a4cf0934e3610f031df15b3e6f7aac85cbb212f035b35c15643fea561c8daef4a293bc6277df95340415498b6634833617bffbf137c109bad7778030a314227efab112dd5905302893f294292a4d7e5ff0233b0e46704c02eac838a489496ce9c4bb2f11289c40016542334237764423d0d969e82cfc7a87f2e4c61ade339d3d6a0a2c0b0e9afc74a0bed3fdaf2203f395ba8d53b0f9030227dbbc00e8550ef38f6c2c3d512ba1b674a0d75c6911815af01b8227d1002d3ac32c49528596c7d2537782b87c6efb7d46aa0d3fcd24392f38f356154195c56a47331209e6f058402249b1df8e81ce734a703d99fe814e4420dd6c3854cb7dbe6a57eab169c863e9bb6dfaeff245e3b67621d700f42cfea21c1440d81bfeca1f008fa1b6bdcb65a49cf8ec8f3f97eae7c44cb4d86d16200368e5269a070048ed0fec403a215599648e5a9738fc7c6718452615ef4114345ca3e777601429f6f7157a967bf32f6c25d2286c9de2290b2f678759a0df12fcec002b0cc18e37f7078aec6420150f68f1861ed8c5983f035aab50163106726ac71ec46f1ff12859feb151cfddf9e3163c533c3d087190061bc6fe0dd49e5f59c35cf282f4b889e6d1350a1bbd0250b403e2b9b10b31ddd7e93fafc56b157b54a632550308df98f14051549aa779f64c784880d2145049a2c160ac4fc08791df6c655240ca1443df18c8ded7fdba81c34c00f7ce722ad5164dd8e3cd6cb0c2bfcc30510baa00c9b444589aadcfcfef5d00bb36fa5ccb732bf5b85f6ef3f536555b19c386e817737a580159137976783618e3ad119ef92718e6510a591674364bd7cb7109899f9bad86fd71d1804dd6aae54dc49d98dc9b62eefcbf8493fd31ae274c1ff4c08521baf19d6cb7664f4932e42ea9d698118122433b3feba0895de2d2a82c7b109f8fb3283ad680346590c2feff5e690eca4df0be81be3af3c55918cf699a6a37268e7e0858bc59683a40cccc50cfcfd0c08ede2301bc8e9634fa699678c143bc73d4a0dfec4d67f445945b45c050019562e2a366bc5de65d530e2d19dd4b1d2fb516dacab4abb305456c4c7daae29a4b3b455784be64c1fb840c24fd4aea1e1b11ba4d8531a44a8f9b334b07b68c262a95788c0fa3c04cc39d1193c9f8a484abc8c04cd6bcd9a0ef5c9ee932f1b1483769732461a5f702d725ac76464e41172e914a29e8a43eac2c3ffc98ab100c72bd74b8c1ba2f8c4c2899bf5821cd9c4d106032371024047c24040a2317a05b3054b24a0799e92cb0b8a3897b817ddd2944e71fda97e6d155eca77fc2f5426ab874ed93cebc20428697c756a1f307aff1f3e3e48c0f00c268cc0974138e5185aa16a835c060287f972b630349f5ff7f49c8de7b6fb9a54c324919c209e209f509dba54dcc920e917c38fa7a8dc0a74ffdf422d333df260b3ebcfa515d731fd5354a1de8332ff5d5c35e5c1ce340028525f90f138281e70bd56347a4abd78b614d86b08b7209d16b67f8cf88eaa537eac527d860cdc7153d466dd48151276c83511ff121bca198c31bcc3f89bd401d986f329658aed64fe503f5f25217bc19621b77d020a00eccb10bc01b98c79c09666146f006731f76310b73ead1261d0cb34589a40d765d969d061a80c4afee033e751f7066813cf031a740409e766c5a790ca8e9b13a8da87b15d592457a8c62cfb9e456258d329d0fd4f3b5f006f3118cb0f23ffbd5f94775f81cb330ce2575a80bda605e2be77accf55c11d6b4727dfb3682119675a833763727dfdb10bcd9362ddb138d11e451e1dbe50dc2b2cbf9349dc2b04e4adf6e83ed098ba0ef6d8b6fff38a1ef2478d35158e9dfe6f46daf6f23024ad436046fda695097d49955a890d4a1713ad92a49ffd1984e52677baa4e2fd016bd9b93b431618afda8d06f4edf4e13fda342dfbe0d718e5ccd18d05443a74dac2344da50a707c4f73c30ac0fe1eeea87fc18c0ac9f1f77f44f8721eec87c4e2d36490d411ed8c44f9f3ec30d5247c6278e8c3ec01d41a28ee9133ae11041f1c34fce0a759acd398e522efec65969c2710a32b3f8e893067df4f9f41342f17332c95479caf4d12918da01309758580cf3a0bcbc5c19a4cd9c9e02917d711897e133ae848ccbf021322e4366888ccfdc1f973673a6a69d2f9e72222f7785f98b03b5524e0426f5e22ff707e5d6815a36758f6e127b51d7a346481fbda34b1f5d6261e95d518735b8b0d9a54f510735fae8980ccc6a77aa9c55b141ff60809867fe41c16e50769128115d8922468c387b74c095e1b2277360d93f9739b0b30617961d4a965d1f69834364e9e64a0cd13cf3ec6eae75639e39117a57337322f5aee69d3eef10ec873ae6402d2cbbf526c1e86df994c5a94948083e262424242424c4595e3a3bbd746eea999920a08dec3be32ac104f643cd30410d71ce37c3e4a510e7b48b732a6a09e77cddd14bef192671072a48428e8fb5e65de212aa4221b061647bc52b229857c7bcde4884bae6d4b5bb02c27e2f472111cde9c541091c6aa58e5d185711c332c7885c1cb26baf531b29b08e83c362d93082b1fbc177f583959a5879a097154269d34ebd1e0fe8e1037e8437cc0d21134dd2f22cc49f1da3843c50eaa0e59de08d44b9325410d046b2807abe28d793fd665c8f72cd081177a084a28e99269cf37593f39e2cca157734e11cc8514e8712670ee6a43ee3f4797f80aa03b522e6d587605eb10834826dba9c4b9c33a9709e184ecf7ec311158e844c4fec079d8ea0be76b1d3674b7a577978c52f5179b8c42ce8b40633399cd80f45e36db1abcaacf89ccfb8a40eca056da4d73ae362940be57a19a713c97c3a0e945bdc21bd641c5a3a11ac5e289bbeaeca4369635d2f77801d916ecdfb83a435dd87e7b54d98258750ae12b058e81fca5584dab4ac52ff761e4b629be8543efa165d8b9e458fd1b1fbb1e853d411bbfc37b37cf40f0b9a4cb3695a6156ec60c0829e4160e37f95bb5892222c0a1584053d7b09586cf40f1584924a12fef625d1e474440f57968800e2ca900e436009d242084b5a585e41ae10255d091281d51e264d51c414b63e4c9a4203419a9e643c4cb20118df0f939a8a5210219c73ce597b0a1bbde79c937960c1135a1369d0e5500b49e9249b24927cb54a4569236d1ff8d6410e48c6805978a0f60b5e7a64a24a9b7c2b5fd246be7a78f54bea4c26eea2e736fa792474d4aea31e62923af3f56a7a39cd2069c35e91ecd2917ca9aaf45223a954d6cafa1f922326957627537c21613a3a3a3a7a0e0a7a513a5fb24e97336806cd201feacda452592be9fd64f8795946a6415e54968dd5f550072eba78297dbbc1228397d2b55a3b47fa10cfbe0233334711458de481eb250f74f0495c8c600ba767598b3d11841516204501c5113f5c8e8cb0424b131e88d2c23ee0849386753d64effcc759b912c51631891346dfbd6c9f5d8f8e36ff71505839da41109ebdad952f18821b610754683ed0031655827ee0c3095a43a49452042fb597524ad95226ede0045c6d4a0335b8d88e396260a39412494a49d4dd4cc826f6986563e2f3360108cb8a4dcc7ac1d7c05205f0867d9346f0e6ca224964549fd9ebaa3d3acf58b5d7c0ffb158ca2e2dc42cb9d404121f75921c382624b1b149d66a6dd3b3cfa03a46fc2dd54484d128be5443d62594b5cb50e1920fd481d0a10e0b879923e51a5cacecf172bffe17daed304ebdbb3b22c9c425991b9180e8199771497dc6619c7a289799f13a9799f13897f1fc379799f1accbcc7829979991811b8c973985f1aa530ae34da7301e3bed86f134a7dd31b75f607ce48b37c35baf86f8327ee065cce165161f6ce2f36810049657d00f4fc0c00932c1234ae795ce411cc441481189858298254d00c27ed1296a894e318904362e45a412beb03126c64cb1d98d59d846ba94fe05e0e3e59935b8583621a98986f8f14ae761b19f2c92452ebc7c77ecda31a397de9e90d8246da4b7012b8db02b8b4a80818dfe49c8037558b20052361bb1a44ebc52002e2f1d081ca55b0bb3643ff56da7a6976e4212fbc9a222593483343021fcad35e7aad5d8c253c7b0235686a742349fb57acca026a710d537ea6d3f40adcdbf2a6413c2cd49c4bac432df380f87cd7f3a6f4564c3a1c3a1f3cd6337a4cb309f98f5aab4a98e09e1ee0f508b336221dbfd8942380cc38648973a5b02ac6ff7a7737bab0dd89094a3a417944a80f4cd6d13db90f370e01c1527d68fc50c49ca3b8747d8874d199ece30bde898075fab9f55a61c94ba4ab0a32e0edd0bdd5d0de1ee8ac8e6d2b73a6473eefe6c1885ade9d920b9257a0096235619f66fed18977d0c785a318cbba11e793ac353c738af34c848039b9e616f99d8afbf2b867dd4cb30179866bea21e9d76be55af08e6dfe6d5336f05042b4294794536c73c763f36c76ee77dfd5874cee3a8718f2c9cb2307a76ceee97d523ec5f8cae459216aec7f130290ba48f9d0b9c63f7db9cbbcdacee7e36c8ce0576eaecf487035c908eb955621b7691ceb52a8db06ff57e4d6bd6353bf2d486a75e69f899409529ad4cd901ecda2d529dded50f7a574030978eb9ec56c75abc1efa86fdc0bcde0f32ab529fcd25519bbb6347666666016b4e4ae9a47462dd4d29362785526b55d59a016dc596ed0cb8a3aa204f7b8d3b688056eac4e045151f2a9bbb6364d657652c33f33da2e2c1d2bbc28582e9e0e0b072bca258fbc9c848e963630338355689c990353f24736240d42c519f186358ac21c222d710d51471ce578394440d1124923044af79110c91b439aace5e74f61aa26aad8a61340fb2d837efc60d10159ebd8648ea70354491086e2a681f10bd5034676f5e8dab59e2c11416dcb0c3185dfe8bc01863746129a564e588dcb30bb69c0367cf49930234badf6a8cb0fd66c3b3dfbcfadbc57e2381fe56337fbbb1e190bf01807fb301008b859217f360ab67cbc9746936953cbbbb79ca49e7a4146339f9c2cb30a0419689259b5282f042931284185915ef0454849085890d2a2086069e5c2085131642489a22440f42a030106441449024c612cfdef5e56192184588c101d80d5dc239e19c734e6fd8ed4566dbda031a2f330bd8ae8f79ed84c3453e104328045dc0a8a24b0fc4e892c552172b8ce8b2d405e989071e9ebef042881594bcbeb862081f8abea022b48290d117525c2bf85065cbccc2ca20e1c4600546501981b685ca6b7e91fd808a10555505931fb66d76cf49eba4f4768d64a60e21e6957358ccaa396e0d4be6ce61960cb1a83d45963de9eca658ed9a499adf3c729c93d230d4716993c2f7bc4fea7de00d1336eaf858eacc2aecfacc0879bab52dfab48f0ff5e9ae6c5f64e1f8131ee8eb6fb3a3d87a57f4510ff41b9d4d81628c4f6c9bf7ad7264fd04fc56dc673046b16d53ca8e31c6eb6ee359b38699650e29a564296bec1338b3aa07dd90639e2d3fa0879b9414edb84d7b999bb669dbb6715ad723db5eb8c65a6a4b5e2133864520d3b616d49a6990d58eaae12585acd620f0d2e3cceea4df1378197bcc29e551e782c8b227c56aa66d5c874ad18edbb4947d9935dbb2ba6d9cf6f3d230315c0aafc1b064e5601d1c66085beb7a4c67815f1545fbc754e69134d626ac3736bbc21f73625d8f49bb1ffd10a3282d2ce8d4b2cb11323d9bbe1a1d679be860c8a0d321f393c6cf9a9f6050f929839fd3ed067ebaea033fc3d8c20a400e2e62541c3f2cd9d85044e7121493482a683c6141002e1be0028497ce0e45d29312c824a59c82e5a56c3802eb830dd6d0a9b2ccea806d55ec0e507d9bc0d0cfee79abf7845531b1d03feb4ca96cb61cf2c067a19a208f0a743accc97c4eead912c2b09f3b94704ee980fddc7d6a9ae6d06934ffa6939f997ff3023f7d0691f91ce21c17e4e17cdecfc86c7ad9014b299c2e6f369943413c3b5131c9ee85d9ea1c68e8679cae9f3eecdd6cfaf9c517666b7a6d0ac0d35b996539e9d57a4f58d6751398d8e8ed4d29f0863d7534831fbe28026704767b98f4c510350876c6102a062c7dd24060b31f08c105c5c2561d604b6cada20261332878605147740626984a36f530490820d87460510f93848001ac055b86f0013bf330698b46650bd3962a3e2bb41024694b111d420a8b92eb098b141faa6061810f465848b064088b114ac240c2d2810a2c61b9c00f06100213961e80304017d7952e4a0cc085e9ca5312037ce174c549c8004faf2b3c5062003086ae5ca1c40061046d21c2308dce96383829ecf48890847ac0720f93aad0210676c6c3242aae0461611e265151057681242a64d0c5f6c3242a9c58a2a281212bc2c3242a92e8818a0c0c61e9c3242a5cbcb38587573d4cba81d21217db1777e00d0efde850caeeee962d25c7ee141e28c6c8029962d970420aa59492f2e4e68b6296b265c76d5a562565863c6d9c604e6696dcccd6a6a4aa62159352b664e68a554cca96b14614cc12325bd4641aa7c8b227c56aa66d5c874aa1521645dbad7de9e60b0c0713b3c5c868323359aa9ba93264602f0255a9381877cc87cc9ad133389fd1f5983e83a6a9c32ba8304ceaf1d9f11d75409e962ae992258554696363091d998e3bb21c35758337ecd8fde8b3c3501abb5ad1601a7147c755af82533d4bef4b0dc8962bd72abfd648f26ad4c1fec235ee801f77d887bed917abf14a0cfa5b6afe9692bfa52004fa2d7af3e7c38f2373e42b5db6648c316e2a2d35a75b623b9863d441674b8691259cb3461433071b1d87590ebbb32f521be378c95005a7dc52b0c692451161b12a6f034b8f178759d0bf0ad9a3b4819c740707078771e08d6a0a9c38293029562bf62d719019d5e6d69cccb4df3aaeeb7a64dea5b41c35b6663938145cb09302b37ad88651c0712902d870a080342fccd676718a6cf4a31fb638e5c762a96cc0d2ea77ff34867dc71d10ea60245c6936b5de24e748a9fdc6715d0fcd39474598e3332dbe40cb6a30aab1946cec126c2e94b6a405752a3bf4152e75614eb2c469ca8e059efdeb599af0e8c811504b74c5261c9d3cc717d10a9e9378ce9268633cfb17ad3c17c15e8f20127c622d86b0428a2bba90e20346ad2f36a580092496b0208a962cac16197cb7f39d6f5d8f0e05859e7bface65a4c046ef50d16df44e5dcfbb2040dfdd1afab9edb298b055685a1052f09ae60593d7bcd8e23555fcccb1ae47d6b57af5c157200c7d75daf5a8164ad0633ebb1ed8009e3a5151adfc54fac153d5690d7d2a6a62d4a52f8eb2582dab1d7eb00ab308bea1a00283efeeee76e278830bb1f87234d105932e9a7ea0e4075cf840e919e7043e70f25dc1681bdd34bea9f8f66eaf4a7cbb2a4bd2777b0edd83107c77ed6ea426a9eeeeb64958b0a83dd860cd0a739ac1ba85375186e830ee80ce8e5119664b8e348883501dc3b0886db2d65a6b7568848d58acd56b55c9ae8e79b156afd565adb55257add5656dacc6af9487af0ee114e3ebdcf2f59b3ef88ad5aed43faf1a55ac6215ab58d5eaa6611a8669b23acb0a44734dabb5d6eab556add6aa55da8459d5e90b0c3b50c92e7531abba0945367a7558536b18f3e8a50ec736d13f9f45acf65a4737b882204f0dd335cfaa67577a5b6ce69c65712ecd25a0c8046f249603e499be596fb6a697d28e82908424d32bcb9cf3c0b0d4677cfc6dd61bb0a0b88326071c275693333d84920a491be92ed590bf58442f8b5e62387f73b01f5c2ac00a08f5c9d45a247a3d61563b0a49f68b2f94f78c56fbf4569a4f5ffd98d5a7033df5563f2af57a2bea7d987ad483a40ee4828221252a9050e24116ad4eba52f44237b4d7d7ebdb9948cc92492f99d8463a0df227956cca667cf2d12c499fba417695aa289ff155035b3da5aa1d4ad35ef3609697db6b34e51bc6594fb6a6877abb858d4e7147f66d04796013d55a95ca5f62d82fbe56a8cde123c527c803b9d51e5f55b314f60df0a68168b5c3b803f3e928af7674bab5e78d2f1b5ff0a6ddebf245a7f67ac59c4bdccc41e424756ec831036bfde5834f4cbcc6d1265ec5a7af9f79abe8e48561a9ac3ebd2a9f26c713f68b4eff2475a891b7533020cf8fa1cfeeaafab05633ed46271332c04dfe9cbc11f8543dfa342734b1d335af52e7c533cd85ce3589f25aeb5dad5e8071eb4460dcc25c28adf3107355d5377f71a0efbc7a9de655df66f8cc27eab2c00ea7b7b916bff3cc33a2face51dd8d419b7703bc41eac4d70d52a7b61abe3c0b1b9fa213b34ce8c162fe456ae46d1b573deeb5cd677780e89a03fd56bb0374502cff100e9236ed246d3ac6617cc66160ec1de232b72d0e8cdbce5f88c4dc5577572f58dbf94f8c5befee1098178771a0168c951e233d09cca546d2463ae7120b8bf2ae0617296f437950acc32e01f60629515d89d58fea28af8e4239ea16d13c7a55f27aa797de3151a3eac5cf3c8ebb1e42340a06bce1a0047d76a91666c9d5bf98275ac4ac1eaf5b391e04eadfb8b48859373cdae3c9162d8237cfc3658bc7fd41d2eab99448daf08eb7bae13c6edc1a88ecf01e273200e7e13dced3737f2a8ffb0337a62cf0867d26c939e6c9f57ce3ce202d5919bf41f3ba342614d919ef71c37d3805ffaa00fcb33fe33000fea97e4604ff6cfc8c6f59200f0acebe31710e8eb36f56588700cebe31c1393a9c6f9c7d33823c2798e09faa04ffec7736fed5ef3a967ffc29877047741275b0a75c05afa7083b29112c80431e2fc0fd5139ecacfbb00ed6077c1cf701ff060a8ec35570ef86ab80c3d3e127b80a5e7c1c1e8f24471f83d49953a04dbbd2b7a770bf6a836f170063f1ed01e069e5db457014da09e04d26d8a61dc79b46cc6ad7e1cd26cc6a3fc19b2e78d37ee34166e5b8df1c9a4ed8a6e38e1904d4d133cab79b703f5512df5ec2936fb7b917f876961300dcafc65fb9e4e13e0c7978d76b78ec5ca0e1f7c28e056fe515b91eff8687c37da4f4dc6b3cf824783d40ef4977dff156ed3dbee33cdeaa6f0d29a4200b1cb47e761c09842207265a4a48e7e13c6e5df6a3197aa719e29ccfc6e8798759343c6083700e0dafe1ec2d08b1e1c11fc16bdf564e98c52be95ed7c55bc9db3da1829eb034afd5a519ea9858ea1fcd10911dcec363476400dee3b11bc2e33cee0fd38089168fc76ec88ef7dc1f288516242ed0da711f49e3c19fe101bdcafbe812366478df9432e37d9b0fbee98af1687298525294e8dba43ccd946f0e695fcc530d911deb9e763c1e6fab418bde955fafe99eba27a943334433d43dc9eea97b7a569a82e5a319faad06ad1d1707b6b6c8f124400062e8a04a6b0097b5e3c1562fd98f65b301e5b0f712c19342de1673a30b2bd3b9854e3dcf11f2c0788f8064657c4675bb99da671c421dc7c9628cd228120955573cf2a472b298ad55ca8871cd93f1fe627cfd079d8efe834622d38c6f3371e6473f2184112fe37c8d44a69771ee80f413423c7919d8c95c9c18184f27c87e3bec3640a7cf061f214374208f0ff86d03b3e2123454359e6c3b0a05f6c55fda3b3fa224d290b90ffe2cf31166b0558c4e2391e9534c1f639c71fbbabd0f0bfa7e7a72726a6a3a8248d15938f743f28af1e7e8db37a734fc7ceae8bb140df253a9bbe21ca23c1b8e78f13d0b587fcd09b584ad0e80306cbd324d6ce6f9161db5aa691984c2c55a2c3d27894c5a34fdc775152dab9811cf972333928d1e6c89e0c1966b86555684a46850ec10fa61ab80162e680737d841070bf880054858250b204981c11229868432f03245084a8d274b12892de41414833460228ba02b48d8e04a152e2830c10503611491012743b0a24ea9401631f206594bb1b2a3c9c98911cfee23831298269864c4b4ac7b36c69557d8a22861f29fea099b3d5452c2c3ab96949404f150ebd5303a9ba60542625800fbb0e599ef43251fa6f0c1091553120d22ac746700e7c0dc18bdea9bf758bd30e3314e24e5314e044a9b940720898d71194742ea7ae8c53b47f926616060601ce8a57cf1603cebb0ebfcc5dece40d4d179cafb78e93bf79128af8759bc734472d77ec6f7ab313831dfce0b46763e3aad416c38f21e142ba4059f17a6466c10ea64d5da54b794379bcccd6e668a51d93dbb677743299823a639cece11929d96b9e96c86ccde53b60d724efb566595b2bdcaee6ee64a5972d532b65de79c933a63fe4daf4de90636c9cc118b0380ac56edb4f4a802640dc0477b6d0ae4a547860ae8497b4ad9eddd824db6943c008f5505c8ba016da463372f1d02796ef6972d218437517a41a7b0f4a1920f3f64562ceaa19290185bbc50b2c549488b20b61c255d0972bac202ffd51f7030860a70a005c7c3a42b3a00450b0db10955b145a12254d87905e95480e9092c414a9b16a3e9c4c9d0a8c9f1ec29f4304c8259a0169dff60971c50f03ce5390c251da6e080d20e5b3ea094848b120f4728f100c643a51ea478f6234a150841d0e3606b8735406080463c1ee0077bbbc0de3d7e60ced769f454455640b848bc2b20d78342a17f38bff30afa79ad84d60ab9612decaca873ae79e6ade825c2b9b6225233dfaab7a29cb7a2b5d6ad4a18390fb62a116e6d31b4f046767ecea879ab786dcbb6ec9a61d56347845bf5cee875156520fc0ac5aabab0d3891290406362b1874a3910614e763e54ca01043e2bb410a4c6166d658ac145931504890105112ba8a0480c15acb0822a2d11866974b6c4c14961a7a73209c256e08a85d9a21d59981d502d53c8a08a295b5481024d08551c51e361d20db6f0c006a5dbb8966147c8b329ecac035b4f2abbab43168ecbd9950697b506610f2233334b29657310c2c99021c31d29a54ab28cde07f45b9ced129852b6477985cc0b5f25a594b7331d365ae31c02c7128410564e4a1c4b73e258a2fe350e84104e31e79c13c36200216c0821c56aa675378510f68410e2a0d2d3238450353d72f006faacd365774fefcb308a83ca477fca4fa51581848484d418682424242424a49e1d45636022216db15589a52ad6458c2c7b52ac468e424d5cae9a69f48759413168934711e93987339aa1c906a78deb05c45085013230715d8442baae87e65de7837fa21867e7599ed276c42891833c9305c5a50dc72326866294b823aa00ea60c72e0be04d8c327fde6894819e9d9d157c52aa9c6864c450a092f568a89e506557da29d66086520e4b3e7acfc7181610678c3162f9fbd3bbeb31db2a052969a01965154260ea605d628c19cc18c88cf104473dc1881ee3c677dd818d3b43d8408e21c8c0d63ac56561edea61cc1144a6af47f0c2469e9d8359fcd19b63a4b2c4c62a71a98427962212af9f6eb7b031c87dc219fcf41c1ab0edd14b98c2c6e0d35b903aec4e3fa57d0d35575863be8874ce7eddddeeb7f98996527af56e2965adcc51727767157677bbc752e9abe6556a2e359fda959aa669f7cb6e95d21b366cd99285d058c39a397661e6366acd738665ded9ed6c6634cb304867eb748b65d740d5f1f3af11de0a55cf1ee5bc12d66a2d945cd44d326fb57af1dba35fe318bfa2cedfce2becd2e0837ff4cffb2179e9f4e36781f1b62afb30afdee718a6c52cd6fba9bcbb1e31898d0132306b019cc3ddb105690399636016775f9bda9136d09bce1a43367ac39e01de40666646c24092ab13d4e1bc75fa4729d5c1fafc1c010616875910c3e0ad4185be93a980b3bd95cf6f1a9f0946b3ba69389035a13623945e557242e950dea6425ea08f1e3e3c923a357a5c923a5aacc279fc62d3471859d2bd78249b524aa7c32336a8ab089a9c8237dcc236e7c421050e296cec49b19a691bd7a152f665c274313233324450cda059d1a831820df72e09353770cca49ab5363fbbf135afd5ccd5decc576f8723733e00f36da67fbb217fc301002e8b0060a1fa36bca1002dc1a6930473c8e9642fb9ca392c00fc2671fcd6377e9b9cf46a3ef36807036c552624fc86dddfaaf75be6bf69367edbb81abf75347e43ad7e4bd1fc6667fcf6a2fa0d4684df626438b9c3917a21b4917126cafc2623e63711607e53bdfc36c3fe46b342fd16329e70c5a9e01b1c6161843d3cc2a2e8e11116af874e1f1e6141c4cb787884c50e17f244e12ca8280521c5c80b0c3924f18a5bbe1c41086110420f21840c2194114208236408238431c6086552942c28adca593162ad0a79620703d6ca2e0a3fb01f7cbae1877376526056749b8fffe1c4200e427b750a29f5b18115e442b145310b97c5a6ecd6ca654e1e85201b8026f6834d31c6196be6510be7749edd9e226c7492f18ef14f367d4b2718db32373ac5c426eee5364981114ab2f7c62b629391fd2012abfdc87e314a9417d82f1211a9c07ed1891325ec175d2e22fbc524314909ec079f9e56603f98250b0cec07999892b01fa4422505b65d4dd8afa7c705f6c3c161c27e353548d8951396faf41509ac4e6c92362e7a8bbe2fbf6e606b1c1282e5e2cb09db0ebfa53c546262e5fb5f2b20152a59c0cacb6f8e926559f6da386f052456090afa4d763e864c603f7edd786311eeaee8bc2b7a81c42af103162a59a0cb532ef3da09b350b79bf41073178a6dcac2ac8e4e4bf40d6ce61148f5ec4220fe379d731fae1ebd58fd7e34723e3dcb327eceb3cc6996c18fb7488d2e66f9a871c885ea597c41a57ed22e29b3eaeaa157515337e19cd84f388e731ffd5c11e7ac8064ce39778b54cf6e642792727b7f7018a2092e2d6bbd9f55920e783144eb47e603ea86a45c89eab556df64bb9ed49bf9f43887f194c89c7ab46321f38a705e3b17d833a7dd0faefaa6753110d9601c760978f118ffb18e849b5c5081168c731e04e2c77febfdac7240c1095c2d998bc41231a505e331de0f9745eba787d952a273241d8216cc55823d35a3d8f6ce13e2e34aa53c7ec236ed5b45d9d48d454deeac32d3b9ac8798d5ed2a8a4d63d8a6a2f60f3655263292e3b3eaf19324f825a304f66bd72b36d91cecd72ecedbc539fc84f36213b3da338fe6041d587ed2c4328aec079f6ec8814209e09317e74c67400cb20521cc6af700f4602bbf64c3dab036ac5052ed1aea1638677a7b10213b6c13bd6f77ed1e02deb46b5e6300ea68c7bc6e026fba87a42be3b2ae07b6d5ba39dc38af59562b92e7bc6e1cc7a5547fb9b915c7f14beaa4e08ac239479cc43950c9b5f431956fee3f36622934dd5c0e16826faff5aee2d726a9d34dd8a64fd881fd3836497ead4c60637b2c8a51a2d176abf7f9578ee3bcb2505da35acd1c76358b35c2b9f49004138e3608b196b29bcaae614aaa522483242bb4b0e228c90aa3d84e1bacf91ace3aeb8ca194c69692e5c0e96eb8f5b6fd36b90bc56eb0ce3aabab89d4a1a1f1d80a6f94a35d8f7ec22c4c6676cb7018d70379264da5d1b43aebac2804d998ad3d6656c98113e370f6401e76e870864a0487243809b7a570bc6dd447370bcb3940b222b6c23afd92e123380f22780d95bbf330c36d38ec68dc731e564ec3c75b4df7fcba0d6f55bdc69d041fe24e825727c186d7dc1f12dc86ff5c77076ad1f0562f787e7d35ef10cfef6a080d27c16938095ec35bcd11bcd5ace1d36bd4f09afbb39a0ed4aae13f357ec36180ad2456da607efdc6fdf9b9e1376e953618506b04121cb648f04ff5180917b6ee85d2f38064bc3d1ba8e7018d005b343ca016ece46d226661de37866029e63197adf4eb31df3c66923102bbd4472d857334df1c7b71ed63aa538f9e8c4befbb5faf741f29433d96919d18ef3372bf1dc661dc87bb73c17a9491f1188f763d625a803fdd07fce9403fbd1e22e88c8cd7992b85883a64985ec67da48cc7798c87f217efcbf1304ebd8f7b98fbe23e72939c93ab496331de6f76b710a42184137be8995bcc535e74f2988a911cbf79c6b2ebb161ae79d06b295d1fa9c0f292d4e923698361be6d5f0c400fb6bd8f1e630861748a4f478dd41e8e200b9949ead8aab2ae62390b7b499d5aad55a9fcf2eb316f6f2ff5ddf990ce43ccc2bc3decdb0afbe13c8655973a5c3b86798dbef2228d07e50ccf8352040f4a9b0fa72d508a5d07d027852e215c61ca39af845162f70709d6c22e840e2d60a3c71ca8c4a4cbb3d755a382c4c08025cc8402272520ac7c03e1a469cacab143954aeed0156c90634f996fe7b9846f9bef1c37ec7d383bd2dbebee96b023eba061b70761cb164698b2f35d7b0002ac022811d4e0828b098ce0a28b232c558722bad080164f787801096812295e30a29d236a0d6df2df0e0e5114305069001ea93295d330620993951faac8c194244045b8e055051155a014910413581227524e78e84074450822b8ec9005d2c08a2d5376e0820a97a7244c9264000706a56685168268d912235687049686d18959d1ebec9edeb3482f61a1c7a48dd26ed8ce7476ffe8bf124a0c92f8309de02203b34b2771b073841735063b8411a7173b4754a952292d11c1f650698907ba701a0f9596ec204216be62f550e987235924f350e90727d0b3c13ec042a51f96fcc7d13c54520204ae1bf0900359930f32e362997cb01bf4106b78a86287ec68059f168228b101d73c545252829f0f959424f10456181b2dec8eff7cca752c91ea9c7394c7f9a6238a85cc424929573ea4f4945389e27e14e61dca53bee958c24affe28b7a3ea60f7a2110656ffef1a6529e622e76f356d9168459ec4393870fff8b148759dc3b4236d41632ab17605cc61300e33237012f1e736160fcc55bbd20e330320eb38af117df64bc15e6320e5b31de0a8b812de3c156cc4c37b76a8771d87af1567d5f1cb6ec12e7b6aa2cb7c492369cc3785c8c175f3cd8b21cc6c52732c638774438e7bc55778f592acc6229cc627b64916c956bed927cb13a212fdef1c9141b89e2cb535e421796fa175f3d1db046761e753fa0e82f2c6e6ab2af1166c93003b398015dec078f988e78f60504692afb4884a20aa78f5f7c75cb653dbe5e2fc803a91431d21e61ebc501f23aa53846ca066b3e9ba31d1e87bef94bef06125dde6df319de07f4337c86d3958d1c1db01f0dcd108d13ce198186b3c7aec708fecda697524aafe13e2c2502e2d738ec6cb80f3bec7a90e03ebc6db15b01c0a1d4e1e1edd72bb272f87236c9e170e3e1cd168df7c5a7f12fa23c0dbfbe315753f382dc7438ec7adc9c60c3fbe2db78417630d0160f1578789448daccaf7ef6f3cfb6cac5ebdbf550bec57bb7946f193763498f192e1ff16708c19bb67e82751c14acabe0996f3351d81b9b6fb3e994a8cd28e55847e39b53613fcec52d256a8690f6e25fccd30bb38572491bf6bbfa11fd86dfb89f0e8f0ef45ff7d474c3e36f4d37ce37776bd2e16d56ec1696668886099a231a2990e786b3b398208f0acf325c85fbf967321c85fba93e93e138f784fbd5cf704ef02ff59c439839843a1d93c78e85d4a380c20a05f7f196f3788fefb80e1d3c3cf079f4782b142e941e2f80bf774c3897e94fb85feabfaee511c07bbc96bfd7e3373c9a2078c34e3304e3a987f12f05f31e01bce5cd78427debce60eaf16634cd7092364c33c4f4ddd317f3840ae29c1a6eb80df573bce709f0f7c7e7d2bc08707f56437838019c00f7a7009766e8ae84ac5888373c4e69e2d58a5058616a01b576dc8acdf174bcc7e356cc93d4a1a9d5c6dcb8e1c5d7f16ab041e5b40450073bcd109c99e9e9f1a50ba8148d36754d0b95aa1100000000f314002020100c07444271482c9cc6d93af614000d7e9e3e76589a8bb42c87511432c6186308208000020044648088883300f1233dc602bffea6afbcb3320d16aeb9037f28b514d779cd1f01c7ed209066960d9df822958ab7367c56a8210083aa9cbac2a55611cec40f04ca1acb7a4110d05ebddd8c872622f1badd872a8a5d1556a5b739f03d52c3ffc9a90ba3b6bf474dee5964a481808a40d99420fdf3842ba5146ac92c3ca1a50dd2f0522cc53c5ead06b44e2c9b53dd311ab5ddb36763dab46ede6e95d40b0ee012ee490f216a7bb33524e4f2e8ab998ec0e8d1b4166a6aa7ce67ca456a5d089655b40558c40611857d4f92519e87dc6c0268de1fc894af0dc2faa999b9baaa25922d3d2614389cb9cb861ceba8386cfac25d9e31278cb5fff9c096a056132559b0fe4d51dd891fc1209af7a43d500147c2dfe0e3e6d18093708b97b991b3f65fa3087ae85fc8d8e431aeeda06d776823eecd3de4c1c7311965a45fb925d5734de015882e85c24eec3f11d821dad253d5c47e28f27e5489c216dda7981d2b67e13b8622caaa9c0f7ea18fdd9ff53d08b38667217cd12e64777d8bd38e223957b786858b7b956a341e43d6ceb2efe9e6b2d7f441f156c4bb0dd82159115a06bf2f6e9e55825921741485b021b4d66e6bb27e71ac50de534f33cd4da0d6e2342d9eac01f79a89597d4622e8eff0b5272d3828721e1a6c2897c27521403bf53c07380a732e9d5d78963beedcca3c496263e836523fae9052fa8d4ac72fa612a2cd2743cc297ff4d7e33949c2aeeb830d58bfb1016161ae2df3932ae6e1724c970407be8b44f4cdf05c65b0eceb82d72d2236c8b54669b22a49f265912039cb61af84b1d751809f6e8f19b0938d40bf70db067bbfb8e0533456ecbd1b5909996b783ef5060eac5fe1351632003ed1d1678e89b0f69a22d802671077e25ed7edec1820f54f65f7223963c84445ae08e072d50aee9c5770ba4b7bdedc4a531349ac5cffa2017eea38aa1f0273a73dba2f7e6ab3769e1c6d236075d5e12b99a00b0fe5d215ad8aab96cb88552de91b7fe0564961c8b5c58eecc0601940a4409de6bb300a4b6b433dbcd2791942695d23c00c06dd73b49bfa99feef38d45cc4f3bfee13c7813d5f46915c16878ecbe3f6a4568b591ac4e300abf023827e327193d91e0c8b60f07d8bcfb1fdf6248eb60e2a08649ec30a82bb0de83a583231c8dfcc35700a814752f6f0b05ada14b99cc50b860cce2177d354769005415035a131a41996f25f185fb1f031b4d147ab22bbde70a1a9055611e2dd1616a2cd4a9a9f32d9237bcddfa0c0c801f5ff0a8bd9b9fcbf7df3f1db8179b24a0c56a884838d22dbb4c88400c1902ca2d5dac409eb10ba25824a4ec9fe419d71d3033a42a4239effa292301f82435e536344ea0b55d648493835b15913be42da96180a20a5a9a0ff8129329e30bdc1d55c7649c3d3e7389b951aa8113577c41877337e73f65b9aa26c9e651b40eccf46e858979d437f8829d743bb616356a2ba6ae2bdeca4ba2cd0c78f08ee71a9b0daaf5ccfac78eda7fae6272f1155c162b5fe191e76285111f1bb5fae6daf0acd5d62fef24a7f9f3d3ca525076d0e1db9c1afbc06797efb519b44d19daf34018fd59b7b2fc1c7376b4fad350d9ef4024da7ffbe56980e13cf5a90349f9517cf12386a07af780ba2454e46544f67d9bd000be464b07800f7673698acfa55a90777b25e7c7424c24a20cef768ee90031a18624143c34e326a5128b1d01bd855a410b2b20facd1b5e8c5e18abe5be0676df6749aa8a78905196a048e8533c35f7d8e2a646c32a03f9709a4c16ac1751cab3601882943a74c7dd4268001e69efe173eae6080f4baa60227bc4e7098b3d48412e535a1ae4f21b01fe01f0c2cef405a14932c57eb4c2b9be36942443b34153a043ef2ac4968523de3dd92443046a1eea4c8649598ea5668d0f0df3bbecc2ccc4baa34c905c37f785f3dfa039298b61073c16ff06e5c91eda13664a8ef8e7335d6f3801e4a0b35c8d8312f185192e821fcca1998827f3cfb1070a6233602fb7c398f3c2d1bc888f3c1c8e0e30cbd5e8e9a2d5f4df755459dc87a079706257041a4f89354652db6e4387da43c53cf95df13831c66c67f868eb42b7825179233ba957e8e544c1e172748a8e48222b969436c7a1b6124700da441831beb6baf46b7259ed996b66c39c523aab20cf1a1317b337e404ca86d49989af46632b96ffa0ce40a96ea0e5b374c5664863ce0bd08b382b0bd14156a39c6090b129471becdbd58122714ce6363ac26888f3a533013ee4060aa060de8d70a4b62488c6fce645a859358c4410fd4248366a79ff0639ae895aa9a9daa3607f1418c5a84fd6034c173026088014da939fd73718214fc9cf4cc70375dd28abe7811b8c5a2596105ab9c911c059e77235cc9089c8e9c5dd41780535571ee80727a11f26a419ad50564aac8071697e3e2d05c55822e63aef4cdf5faa2613e5cde316d32a174d040b143171e8f785143631c01365f7a66baae9e95b920cfb5d45325586c76b3e0900cd5dcbc287df4d948ad7dfc9e96ee994a1198d9ae7ac6f3425e0c90cd3228a32e1acbc3bdda82fbfe1028438b8f174bfd0614322a6f1b8cc1823719c3094993fda72ee2b17636991e1898636469e65230a770c5fe724a4acc8274847f501aab0f9bd185f705d1a961a98f2a10b8c61975c4c9ec0cf97f3daf08cfe23091d480583514046af01bfdea0494b392b950c76de5924d62c8440de7a77d2bafeaf26868883fc87c8ae3388132760370a31a7f2aea3ebfd131ca0aa017f24f606d16fb54a8d4d778963f4fac4eee846d625c5b1d6e0dee8b8391e6d478cf85c59abd20a7324a92c433097c00f713bd7205ae6ce8eb7500453ca2e199986724d00fa4e71fe6c70e54748429b0e46e3519b5481727ffbfcf7188dbafb263d2930612695f178a24515796cbf7fccd332bed37dc47af3e4f749d903bb3a216ed68d33e041f7101e92c76eea6b1fd07f162990c070c0d8a07388a664a6c472e0dfb9bdf6288b96ecb431b306ec0d90d4f9b8dd7276cb9d70098850cd41912ba2335cbf49a71bd862d08d03ea1bdf48f35b06045a1f1c3dca7cc3c5b03051b4d8ed81ddc08fce7189909bc6a3839c7a4c0a9af7542fa3eeb0073d2aa5cbb8ae203628eadd68e0a841a21f6bf4e951143cb6925f8c5437fdcc8040de74da2c743a65c6ee3354e4f7f287d22fe01d707d32c72f922c5f3410e626c80463a8df7990148e4cd7979e2f9dcbf059d8c132a7fae9e8f5513303b81f1a927727238cad09661eca44c78dbba0cd4c020a4d1e67c24d4d336e531326bd9038c6f21982b660bdf12f84362c0d9bd484cb82e12b84e47a81f4fbde472f229ed0d4e5139dce6343474a8d4e581dd1b4abdf011a029bfacc404c839119c8c1e915014b6684c8425d259b72b8a037b0abf7b7a870eb0ea893d3d4a44a86374bb9846c16e607b129312bcc0c3f0fceae3cc482779cef9ce0e5e532cc1a86f6c280cf30c6015fecf19a95d1edd83e98cef41909be0064e01d73313814d57fc7b46af8afab8c9a7728153fb839e5ed39f8b956185826b0c686d572476af426b59a6105a650e2bae421f0447b7fbca77a4e3daf77ad007e926d03f5631210c9de5e69e2a146cecfd6c3e5aabbe18f3d26070b14711ad534c9cc84ea60835e0ddef09daaa709b4ead715fe7009c025833096b970d4898a300a9ec90641612d92c8d7daa06a17d5d45772ad73551973f157460802c1fa24a29ff931469fa97ad1a628455ea4daba5098655ecefeb2eb1fd8ca018b765e17a9d7bc3111cc09da467d3cb7e6507901d94f34bbec29c5c32c35b285159ce7de2c0ef765108eb634586396930bfcba43aeb381aa8df6547737e2d1bd8a75758b95693dafc3ad5c7f64c1fe80db20e570f4c649d03fe64c850f8d162d9fe14a1c2cf4abd18976556a740b09360ab7810ceceb472f7d4cf6e2a039ec67b1958a8351353653407c1a4afc5e10771ca298fef4a48467c2af401abbc4d9bd93d7a820108376786958fccf055599925989578f5ac77ddb49f8fad4d048d9527d1e27327fcf1aadfb51c34fb450e60cbbc568cbaffae41bd8bbc48b3491004f284f8c559e9da57a1e7b92345781d59aaf8eee77aed5b69a6c0cc50a1251dda38a30ca583a3332db4cd0e0f74dac646f4692e3d32ebb1e171aede5e0f7ca3d7c2c864ac9e8c4127cdc13a10f65a52705b5ed387e05323e3877a299332777134fffc20526e0ce0ec9d37606b3a5a8e1c63788b1baf9580bc92373f53fb8f1f385ed527fa121c5fc3e039b07d1a16de4670ca57145b0475b7e55a9371cb58f8d41f2c505ce10b3bb102cf3140416cb2c6c4004d05884324567156fe2116478881bf2f97d31b583cd7d844726263b95a7877940b0ba0ae4765075cacaa1ab5218362fcf2899a2f7b2bb73760ec8a5689083c56ce82995b410437b30ea4a7800a89cda4f8aa5fc3d6f91669b5659e415e6d1191c807cb9ce4dbc76d86d52ce45834dfc2a83e089d81cf3d046814cfd915ce0dedf03d6b6acc3087d4d19dfab3ce67d5077b7a3dc0c66dd2aa127e8dfb0c77f4b54d2aa17d0c8d0cbe13960fc6087d63212a3dbe7304b534a5175c4be0a5e283ed11c4c7920b137953f38ee5e7ca3661ad8ef656dcd181eaa74abac8b942d38bbc0d332630b9f3e15f0d28b6ec35be49c373b7982edecbffb2f0a1d3b2fb0be55cb38509ebeb6d618077a2cab3fe54785f03377438d19c8a3981e03f0dc1a6f4b8e4d15dd841298bf0ecf2915960f9ef9dad41e877033e99db31be9ea956136aaa67089e45055b85a67aa1fc7f888cfc3fcbc77b3a2b85903420db46482feb9a25b41f5561003826ad9efa420a6ad7e3c44d4cf03dbc2b4a1a998e13cadae832f2777ebb522ba04e5d1917f685316b46eab6416eb4f479e629779dbc2acfe4293a79edf22d8c0999f36c9a45ded75a008db89735eb539407fcca5722158552823285a53dabbc935a2e2f08039985ae8daa2bc750d93308a5db20cf5e44ee980846e49cc9e0fe18637cbb51390c9dee3c9497b497e11f8434b3e99123650d8cb232a5db30a7c089b376048609ea00acffb2f670d99b3fa044e89787050180667a3a461165a54e969e9a87514b1cb785a87845badd942749cb06bef7886bab391f4d269882600c8fbb759b73b22f8a0f2216d00090f0affb0b1fd9eac580fcf31d650212ceef3874818cf0a0df4b52aa07a24287315553c7aa0dc35ce780593d8c420c7b241adc862799bc87945dd9464a7f596310d8d8424369309cb096a7eb884e092b39254801e7c975749a78d26cd2eb0055c262f95c4e9c2d6127af29b7339aafebc7b0c3c0ffbc5282d82538c3bba328bdbc319d0e9b4af5940a472a174a93eb69c75e3c5f7aa61aef33718f8fd780047797b5b2fa938baac04aec719188ac0b3666015cc5fd03db1723e9a26f390e2935053594c99cc6cffcc1491e697de8bebe524a9e9106640906de84813aef7acd293b03ad3a5c6bcc3575cee4b02c16dd0775fa3d25ea2afa206fd078b2251c093bd900445cde52862c7d5150c0a23a4a2b81a00d6e10edc636210b5d49ff585a045a8d8ef8be0992327a4757f74115772b1b3e5014e05566014bf5acacd6d68411a4205a6f0fc5fbeb544b8aa4bc896e2c074dcca8d0918d6b046dc2690ac09b72b823ee5f113691fc8c1d82073b1e7c5df50561c5ad9ad1cd809563ca83a6774d744ea32753ca6e68db541323c64b08dc19a289cd977fbcccba9a582dcc69b2b922bc8cd860d0740610f10207da135092eef5178f794c864fab4e0fc5669ba9bf3626646bb3638aacf0d8362494617a827b0d8da8be181d65fe5adeb439674c2b4382f22f07f7b2ed0691cdcee46444ba7fa320e77917bdce4498dcf90fee5e430dd705c99a7f68ed8af326f011756286344e0f06acf43cc867b11edf132b0fbe85278f7aedb209b2be2fb9e1870f6c6cf5b09f87e22f11801bdcd65db5705b9bc17c8a153c7f7970a45ed8545e3998f894019eded0c25416930156618f4afe820fc19a6534dac7a5d5ffb20487d5052269e351dc2c49f94da81916aab2053b33a942840cd3dfc0de97c6727207d8a1e3ac60e621b2d2478f7ec6875ea0edc1bffec59944970426ee0f07471db4289f4396332e18369ff6a4af8ff6ac8861135a1027512873a2a1e8bdc252cdaabda9adc25279dcd9097f9ff02421283d079198f3650361ab648381a7170c179b29f58a03cbfc9b0bc0cf92aeaf71a03a1cade447c972018107fc6f2f74790fd75b9d5a19953ee24e21901d16c0c38d9a62712ba2cdf34709c8cd8da319b12c270aef4eb56a6af8365678f05ed3c15d48093118e10cbf803bf01bcce98401d5139d9fa4322c19d0b48002567c104f581077c4d172cae875a4f1ad19623169e8c9544c34a29c77a28cbc745237704036eaffa06890dac4f87113cd9441cfcc22fe5ec2f283b0baf428c2d9c6c45810b81bbe4023e0056c26b63adc5696dd997515c00f43e60e9eb70fb702f9c75e28fba331fb4129ce57fc158a40c22cc49a7af063125aa26f2719b29a34d925e1897eb35020caa4371abb09063128b91a9921ffd0f8e156af03dc2aa6939c90f398fecc629d678d5c303fa265590e1c0beb7cfe60849b2b928bca08badcf15679e2c9437f968886c3e1bd88433d1c86e3948749e91cd7e399238374efd91de638e4a40a929e6a64d372890128e813e24b5f3703d7b755b8519ff7813cf3b1074ed28441aefacd7d8bc31fe677982c4457e0328c218a90e432d14eb80d3a940f4147ee7e0efe9c2a67de07f263d21ecc29697352d5bc29f9d8e4c3b79c1636e75b2a5e7fbc1e773fd1204bd1cc7e5c3da2676f41da8472c09e52b27db383cfdd4fdf14cc47521446e0cd7d4a0cf101a4e5de90a5c2842602c16030664c7aa699a1bf5fd806f7cd49b154a7d4caf111d836018e9094e9f5503b2e5e71c6dab242bb8d84b5cfa0597a9326c49c06d4a36d203e1eca9f4373c031cc3f4ed04d7443f3bfd18bd056b4aa05b88e833cf99323aaa1b116816569770ee3913ca4bd0811097a14c3698b70a5ee70c86c3d53bf1da80e1e7cf096cc369d49f66034ea5433a508c4fcd33b1e48b2b490c23f81a5f7c9aa4b6b113f0c9e221f2e0f2be2ae1c2e28a2fd383c67bf6ca44949c496cde90cdf4a44eabd150324da89696b8923a9eefa1d76958490472e4ba772233ccc4b5b89c729fa0e0e789abd948cba2ec06ff764af76c7c193881ba9a25b6e79e6b2d147a7d3cc48d6014ba6a93b3dd98844408ed68626c9eb0de83192047a860b70c62ec6654e09f387b6892ecf12587b7118af5de5aced0bb143c7a20ac3777ceb08ada9aebcb0cb8d6641d2c687c0c33ddf3e1582aa1aa8acaec8d2475b8c8f5af48edd875699370678ac8d0ffb5b70e6ddaa12590f20580c4dac684ca033764bea87e80c6c67cd1806a0426d7e83d4a23dcac361e761ebc84e427761333423d1085f3b2d501838ca9603df0c1e904c10a823532e5b7ef592ba1f3b28fa74c0c818be139386c13bd747bf4fa1d1ba573362ed94d97d5fdd064c42dc054a7c7a9dcb0614a60e69f55322bcbb8b1ba229cb85399efff7d39e8934c9c7575adbf35a15abcda6377d979a9024034c44cc488ee7248a65b32f5613e869a93321773c0c243640a40d481dfea88004761b3aec4d7dd9ed4997d98774e240a6ca32343f1dce53a1ae9044beb2594fb1696798c0e99854a4a8248b8f8983b1ea2b3ea68c5cae0ed5bbbf73f4a4371663860e6059a04b396867ac9b7179f1bfd01c8d9a0698165491ecbeed69c39625863a6132740f1b7f79827fd9628572e126acd53ad030138b37390fe75bd8b928df46751f2ee218c1438846c04d2129da973d6ffa7486121ce72244143c96544b4104f41bbae82fbebc225565dd4708316decdba70b72ee7605e6171cb9d1a0a382786f13152ee94b0f247e66446c85cdcb776a345c67d428e91815004d1e700d904a9678c07e3d4b39fe8159ff481d7866a5c2caac3c71f33e7ccb0033018e1eda10cf3453f8bf9f67c9a1320ad17359cd494520251569c68b52582f0009c698c8347f7aac746204477f43e09871fc90216d2d3ef84d4e6343c9f2281fe35bf1734e483e5f63545ff52205e9e66a6a822f6f31c0f99539e44b876970028fbe2194a0c97e0950569e8c28139dd75ca38c0334cebd55c162574d2c58a9de39eb903e7931d8f8e881ef517549b4cba7d55bc381cb9a75285d9688baf3ffd65ad5ce90be4c41604d97cc79f276aefb59c083c974f6004ad43d7979e97fc892ecd6f4755e6a28016122837616197a8930f4d7b67a6bdf11cfb9dcfedd46077fae1c5ecd5b28b6c3aa258aa8151716dc3e49eb1f2e18c197c28b38efd344643f8d57641265bef504830f098b6f0e17fa6e0650aa1dd7d1a3ef5eb96dc64431cbf05b3ba13d6817a1fcc40256222744d9ff560092dcf60f5a75771e088dbfdc305421409380e0a956e49981c67b5e6334e4f2bd2437e7da8129108228e6541701bdcaa2856a3c1677aaefa36f2b8eec2354db53f72fa457b9a9476e7f5ab5726c3fe578ac9b606ba0f5718b1cefd0b898c5b41587dfd698ed352473881b7994225be23fbbd61fa6a03a965c7fb10545b6c735c8b537454b4d1d7083399b294521b8f9acbfa22596e0c4a6177a04ff2c9693b7df3b81221dc037a1b33ba8e7e19413a476628b26d0811bb72f0ebd882e4e10de949bb63b0b49a09d1cced0ca40a1e0a2506e92f4d8231dfab966e88f169acc0470c6e3d21f04cb19d965aaf47e4890ed755a1a5bed89e29b126340ec2ed2d4cf9ca0b3669051c834eb905db64e461938282001739c5d254c62483f7d3022faeed098848e604878d72fcaff134883b222b54e57aab0a789db2ee7a7c9b6fc79321ee6e3007b225ad13b2730f385aab6fabef4eb317331ec2fd155038b0564f6e0a9676d5064bbda75990428de9af2836528bf1bea6a499c0d52346540115b49daacbe1c32a4223207dd17f4fcb8fe58f812313bef4811d45ef6085bbc76cdcbc76c4dc01e56501d40dc843b6d3f60921cca801d4aed2b247400d8c020b755b7c5f547ce26acd61c9cdc5dc6028889ba8fd6687d804e10deeee09e58ae7c579ee35db572044441ee8bb7ef83e96a8bf915ab75b4840c1d440ae11c1a7d1c46938d125d7de83a188e9bafcd58bb4f7d575659fe44009bd00a77a6e6fe0e557bbab5b35abb656592c34cc64f6941eac98eb4fcf7d7458c4345ee2bcb1fa798c96f5307cfbb21d0267170d54fe2ef357667f1fb2ad7d25d06cf29530f3065c65627c6cfa42829c8d16862a0b7486202a83c7fadefe8ff6dccc04b9c2923d53b954aa3b50780e81f01a6182b6bc245c327b468c3d71da5f4548093b57a76e83e5be03b86e1f0c383fa5bb0e97499d23fda007e5ad3929dc17118143fcf66fa247f7f10038f5d9474c9f7e9b9b33b7227fdbf7571870b1ee3381520dd7fa6a3a006c07e50bbc82331e61dc97b66e3d378db68bc5bb7c52c59af367007f95518ccaabf9a731026053d71c5f2882a46c43bd2c5f961bab8cd60651d5b0b2819e8abac72dc9a2b22af463248d613aa570f8917740c071994e6609f10148e9fe8751d00b90b4692c59be2561151e98a55d2c3c15a1f410275c58d2afcab2fdbd2f8b6b1ea6d707faffa2d6aa1f3aba6eb2423fe05283fd0c9ebb2002c46fa8371c4b3e50b3a5b620fdf08ddd9714c01aaf1ca45ccbf21fbd633215a5312ecbcbac925e739bb3a3ce54e8de02eb6dea4af85b34871ff166ad5bdef4ccc4b5b5c6b361b2e5bf7f6a80d5a827365d93e08cc826d4fff0e1b68d7b1d19a04a0338b888ad98444666e53980d417b79b80c477df94c9e3e3f91ff693f55e23ae34258aebaf81aa442a50ea3dd5c789895e98e03caa83813a35a05e97205f1f31ffef612c2b8cb55ebc3a44435ceef181aa946f2b68de245787e42255fe92c89d30c0b4af86fb0391119bd5d97a6384a27bae50dd7861f992fa59b474f15bce1b783ade9e0b38d1b09c58530b969a66ea4008f8a51ee1243c8c800fe6bd316cf7d5415c8e21c3eadc0edddeb36ef11621565cf236d75f88c49ee8052b68017bac3f3f6ad090c2012153b0e3425194ef5d02a5a49ba18d4e65a30ed8c0882724487efd35408d62c9a236d8831d741fb20b2c3c39c0fc5d6cb809ad9b6f05605cae5cebd4ac413c0dde2627750b7929efe53cafa4e500252c71cc91301f3fc8f8c5bf5c420cb5a98855845a9ff2eb937313ca06bb607520fe3a421eafe4a4a0745f423d767d8c5b52d29cd954278a804fcf01478be289d7f00593067a530a73b907ee35f913d4d441ffd4ff685f3f1a8443fad9427a940c24155966f7dc2226b7a683108f061391adbfc94f1e1c8a68dc34a43d1e4407949554171865f6fa06ea0c3ee481f9eec788e3026f2650fa956112f007a8c291e39b583c085d97a7bf5acd484b2ff202e3eec7bf1120f4b48acafa3a46d6da6d1484118ba74ef8285c1bb3fa66c7e055f7c98dfe8e83218b9aee18d866e156c8640950e6ff8a045d48189fcebb4231ad01dff0aae4bbd62a355a7cc0524fa79a7b5724956a4d687e90d5bb92f0e6be6b93e5f0fc8f63be20a189458c9cd4118dbf1f1fa08803d62924575d0a2b4b53029f729dc88dbe727c32da13fd6385aa1fd1d17a58461bb732ba1d7e4853572ba614e9f4792133f99c18f5861c9bd931794990c04ca22d634b3b5f8e26e0ab1156a34a1b91c37275e48e8c9a45c6126386aabb2190aad79acad100509dc18d46ad805160bd2920f09c44709c37eb9ad7552d2c728ad2550933ef7c37791f11c5315666b528ef8e055973b6af9065c203bed64da36892734ebe186149c2039a857653a1ebd25d473958ec2ca133825b83cb71e9fca85b4ed182d60ed2b6ab1e6855ed8832927b44e90558e24eb72f11a04f2f5f10dd5fc031474bd056dc4525fea9053cbdfc33d0cbdade1c258d7ceeb97d8b638b243f1203b85ce1ba8c3d2edcd1141c23559a024352082b325ebb0df05e5287f1713eb3e7b3bdfb6159844227625c95f07157879b6fe19f26e69a95add7700e010fb554b177295364a7d932b0415f5ed75beb09a182e8363f4c3533a227f1f5901b2dea8a167b21d9444445416626bf29bfbcafbf685f3d20c3db26a0ba5d0a066862c0ef2a4125347f585571d4d7ec043c3719d0886b88070c449dd94d7638d48358c714b72adbc200f315da539bb5a430e0f0976aba403f67f851071e4be0bc16100b61fdcdbbc2781d3000ff1cae3f9079337da7f090e4ce1e99b50870e6715e22d0aca5c9f31cbbdc5ea630237827266f0c2672ee6cca6420c691cab60052562d8137600b101e81bbc84970a86aafea2081bf394e32b42845cdcf9793d6019e1c3df39330bb3261af85278fe099a12b79997508389a788fb35dac1a40ea5484232f4a36c6e5f721d8d5e92f70dc4e8ee7bd04ec3d9404ae6a3fe2cf0238100710815e2229cb487530adb6c09e2bd627a0462bb2ea1cad244f77d1861f3f6eb6d950d62b92be4f00eccfb1ec486ba7f98c059411ec938b84294c010d694c6a0156de2350bc8608fa01a10c87232ee502de81e6140e694680a2522275802a5484f6c1c49eb56a3ac7d4d0d48c493be6698c414339a80cfbd49976079a56b7aaacc3d9ec542daf6912125a8d5b288ad10ce9e0199561b5e6f14ad83dd176a276ebe4960928300628e99813ee3577ca4d6f8d29b0fe3598d5c212f2429b2c5041e2dfea43c043517519137aa90a5b757b78d91bc0af96befa059890495686da6483c8e21c817810f2c1ad7990fac43196ca8cb49a47ea56f19e9a86a021985a1c47f4463fef055d61ca80ba586e596998724d32f932d063af1eda00e70485d941c7ea253d0cb46a2d5ba6b6203b95cea66fbbcbbd0e2f35d250b51ed15414fb8df398bd52d3109ae637e20a0de8177007fb9d0e0b40679a0ced73f2ec64ed1e74afe394a3ebc5495bf0f464acdedbb76552373e217e79eface0440e89f6ed1a05d7a67ebcde5031ec78bbefa0267e0f1369cceb1167ad39f1b7faa36f323e2913ca7e827feabfe472f4890440daf2fc9b9323ce24915bec144daabd6cefc2eb38c4d082d5b780e0d7b4b3e9ac77560b0c931e7c65fbc3569fd45bd91bf128884ce54afbe5c738b5b6c6629df48b21d19d08f9cf4cdccf12e2d90a1c775855c15add606ba24a8f229ed3b58381bdeee7ed32eb74e6f63501fd67f6f9efa055428569d9244411b86a31ded970d29317abfe811413d0e93c0d1edfc532e1dd883088e113325d90b53f20d44897105caec244fdf9fe2cf3e8ff48228af9bdcf6fe68a87f8f806d6f3470d4f4d00280b86cff01e00fc6265c9bd12b504264d14339e086045bc04991f1cb9d750442be5ce053deeaff38cafe3ba8d342ff1d73e25464c0184f1fbf895083a9686aa4a81042dc13227f83c8f91d43b1e98f914404144fa83a98d373eb9a7b9311fc9ec783f2a0de416184cf7102fc69555250b050028d3b76454ea070a92b29fb885ccac6905f3f02087e60a549af57350ca49ab80aa6267e3f72bab3cb909a1c016db09b237b832bf05842229b0ce57bb7d1db092ef7c793241c29499a78befdd7749be325e6075504f1583b061cb71d952875e9395e7dd0a64595bf64dc4aa4eb92491dcbe61b15a1d3c81e92c29c9657b421602bf3ae384574dcb53df6e0a7771332e20f93493efbc590ebc71f74bdf31bf5fef65fe6576afbc71d5d38d0406ba9e7917f07a09d8642860cbbf4a82165d5357374eddc41adb835e875e92ea86155e1994f27747ec0003271f2562cf21d82c263dae597e49557cb66922d96e84f3e95afc468268a0b3ba9b3a14e91b7b8ccea3691b2720e5dc764408c164c92da704e25108c70c35be53496a454acb06844d150b6ad7c1807fff298278c2e8c5a5cc2a3adfc1c098f96956280c0a419f4fe714b968d36868bbe83b4e087482d38c47c0adf11533fcea298e77ac06535065ab645fad8c9078a3991db3118cce6bbd62c08bf04e645cd513a2db65ddf65d11eb57a4849fe9c529ef419920308549e27d0de69c4c562c6862ac964154b316f3af0e1c27888f0736109287b4ecbe3c374610842520d3e4a260cc043d562814ae49374976087ed2daf43177ebbc0c73e0d72a8391d5b652aa9102ec046fd9c8abb2bd4bb3e31a56492d894fe1c027291b5e24d7f9c8c5d1760025331b495fa2975bbe56007b3b5de028bbf5af0af40633211080824d9582c07efd5bc173d5b6b32f73fef7477e3a2c5406a29ff9420751db9b3c0ca817db53b79b4a90a594bc74a7ab03d3e966f7e7ecd58a930af6286e3a7dd8ab113d48d3984bccef2c4bec4ef1ea30a36dd65303a0ae7070b0b15c87adb90ab0486769a7adc5baaa7afbe4bbfbbcabf0b971b12458f896831739a4d053bce11bdc4406ee8122d2181f2bdf56353c09bf20de9cf329224ca1e7dc195f2e02c954936b02df5c296672a881c7cbb7314ab699708bc803ff8dab2bd27ab4cf48d1607b4accbc3465a186d5144b4a2ea6f09aa3c8312295adc9203750d710abcf3eec3161414d116458137b4e802cf8acf48c02cc79fca8de2c347d0f4930b1a874501d213c4f6915e08858ff6f404040b1cd60bcd495b44c1a9356705f6a895b2b7f4eaf4914738630400d86e68115bd9003963d7e73bda321f5f9568f6f01c15f23e8778c3e9d7cc291d1a654efaa51c82c78050ec5af6290ab263829dcd99e5cff8920120adccfb7581e051d0a8bcdd5938ee0746a8fca6e96fcf0c024d17ce1c9b045c636ade1935da46121d082a59644114978eb8d06f6d8ea410518b3a5d0fbc8766be7cf19e45f01c783c4b178bed13dde3b16789c98c780afdb125044505d00438ab5d532b9102e5de635cdf6586d1cf16643c09b71ba388158a73ed88610a8bb8e00c123cb009ea79831e8c6c9e27ab1a970d1d9e13511b4c8472d334c0563529f63e7b047a926c63296adcf028e0cd0bb6281aa4a6f0e3ac6f518e2b0dcf9cadeca01c50a1bd0ef175a0e5b56ae71c3928498596d19d0ec62c41470ffaf03df9cc195f1aad1b27e7836ac7bf1c054a441957af04174c662cd9b26e928ba5e2910ae4a5d0e0508bbfbb161ef1069444fc2e50c2a4b81624624f293ca142ccadb70fea72c39b9840468c163537c287890154f8a018c94afc04eda4c0492d90e113241090f24515c254850e8baf40eca04822a3c42292742864dcf1e9695f007c09a1987f7e15f048b8a1e7ee35beb8211a04dbb008e41e55c60a334746029494456d0f3c288993f29f1edd93cb6f0c7ea0ee4cc2216f47cb1348c7b42db525362785d10d88b6b6335385f1341719fc95d45decbbc4a8dd1bb565ac500354040c4bf9ea8244655bb70efe4ed4e7857f81fba1e7cb3563d930353f1dadc5498cd2ede28cb1abe06decab45c252c8448f401690469fc0a2335c91a1fafef283019a0bdeb336366ef97cedd9ba65065eebcc3080a6e39158d076b4045e61088e94ed3f584b631348d09fd1bdcba3c8619784e242039603e02c9a8bf5efc70aa8fc7400b5c957e5202936f73e565f4530415f94038c2187363d7be32121f6efc08a13c61e2178b3bf30eb1d2594bb07a167ded256910a506c410559c8be1f7fbb4ab3b79c69bc8463e39ed6da280b1b5477be9afb2e9c94ac604bbdc7b2cb3116daadfb784d1a21533ba4f4623935afe8337267959379c298588ae49d1c81352f65bb0815655cb85bb28455adf81b430d6eff22c80dc17ca5ad3a6ec57e699187725498022181f2ed861a7a737010ace939176e3a8e0a1871c6b913368c565d9cc0488396b5134e0b34d88601185fbeb605131d9919326c693fdae15cd6ec16002f688a4b59bb707716aa019b3a5259b43bb01487de7f688cd715541386eac208c9b48af459bc15145a7992ae90b77f00f16c5ef85cf10093e7a8e7dfe0bee2d5185431a4124b6ce1367956db4d22ad7d6d4b25e04aa471738365c4bfaac394ef3cacaac53a8e600784831beb76f3cafbdffa466440363603556eea4384b0f03e90577342514e1517106d04ea679e0a6469a8f7ea179edee9063f5abb10a0810920908d9a1a5a143ae61834085149733f0ac5d832d2dc47f0403e494a1588e89ca96ada9faa0b1ce8ab5cb5b3a613b6ab8e29edbb57c0068954cee3a84a085413b9018b61bed51db64de0503efb5fc4559ead42b53ebd11fe33aa98583b728951e71356bb64006d5dc442fb4e740afcbaa54013c6d15b97fe2688c0b7e95d6d55ad2c5fd576d37ecb229f3f315c4287af3333760c487b07dbc3304d1081f541ee125d8ab3b5b2b0099fd0f31ab1992fd618f1a118a3ff5bee9884dd901d12647aeecaccb0b5928f72726d20c0b1428a02095ee115a98495745d53efcae364e53d4db2ceae68203a1653213b3c4272b41fe679dffc33990a738d24a4f59cca1a579f1dbf40da659e4ac165a4aa95a44223d1057510235149a290f49cf61d6f10f634ccb7368fc500e39e4a5a1cb03e5ff1c05f731f9794b98476848ce94d6dfef475e75eb66d8a9629cde3f10d681c351969c64b129d2fd7f34650176767273e984145756b08b1f5b7189cbd1843c8e2f08d91b001b28c90204ba67fac1fc3e57c8309cf86aae391d1cb4546ff20d02ca7a72917f1d859cce75fa9f3587c709927b891fcd10552aaef40875249efd1ea9b5ee107b47070ec666ff843e9e4f768f54deff0c3d2e4efe8ead106f0623ad0ba78a8137a94bffefbb6fbc8ea73491f0c93c225297fa8c0be6cee1dc84ae1b24a129b6ab1b4836ec73b46fcf453baa786c5832dda23f3c9fad2cb47fb58b4651640136caa6a5863d121fd9367fbc079a78368878b6857cc83ebabad9f74a7363fb5ab74eb8b522ece28adaf5c39493c72c63d45f18fae4d664fcbaba092e9859d9faa494398c02a2652039d070ec315d2c1e4e0304789f45256f106d9ab7aea974420b4888f12d1f80cdc5d6ad24ad2d3cd031e624f3cfb44f671c48e9ee3cb488b538fd96796f3a83b4f5036326c96d9418a5ec8c98f9667e75f738da0da99ef2cbe07809403e66101def5987dabfef43595731ad6bb83282a9d306b62b5e2864e85a32bb108d2a549469b99f6d3414d63dda0c0f46afc3c90843b8afa74cad7ccdd26d06f5532558e0865e5c5a67264600e14a7294c413a75d780aa7a51e8c8718eb62febfef8f641499fd1a5db9682a7acba304fc2c74f21a3a4daa96827bd47b20fe6c846439ab7c881784fa5c8dde746d2c73829b4268fa6e4e5a8cc4a595571336d7cc659b03f36d45202acd85199181c3eb48d8edfaca2b2cb41082ed48fca7c4eb43f6688d619f6cd680fda1c182bb5554b42783f9be21b5d1a1ad07de3c3c2d38925f4ba4556458b2e3537c7a6dd0b94b579f71b1e8503c594c81e2dde500a14c561bc6147aee087be1cfcf905292744476c35d36624477087c2c112960dd21fba7f20b9b46820c34d0c83ae208a91bc0a96ba4340d73f35ad3e488abac47ef282d1021329172e9d22c405a8b71e62858bb18ac7ce17629a8d3a83567d8b6dbd45f0cfaa0fd81cf1f6434ed60da4f0a7130b46a9bd4c99fa6859265a64c1cd968761c3a80babb97df683a86147404ef1122c15f3437f138ede5d0f602ebac991e8785acb9a279059399f6e6050a363aedde20a83a361faa5ec47706d5f4917a775f6d525ec1dee9a04951db64f2acc8623a5e4e3e15a41d85175433746a2142270783af70e423cb9132bd6449cb3e5061f0cb40c58173db1366aa9434103d8a11e5ae4769223f2be2432fa1a83d55ac8c1f21dd2538a6f47eb6219f037a3289af1dd7c874bcc78d7c2754835309d3f5acf1eeee5aa5916cb6d9db48472af070d884f5e08f76ad22e94666bba7383a7ed7b136031ab1638739a958f9f0d06ab3b6513bc8d7218e0bf8ef4cef7ed32cc6b5eb74ddf8a6c2dbc8772ad8cf1ab61bc1361d6db28364a6845550f2085150437f11f010774d5191dee371b857a955ef5c00be1772186a0b27f16383f87e1f9edc3750ceda79c9135264f7e045f4d28f7a046b272ba12b887d03b771f3709e8e1c3a63756ec4028757e2371116090bc487e490e1b031c4c374315c2fc48478780218c23df9ad7a5042af967b69f8d8f211d29bdf45259d4065a9026d21e7012601e6ad92b0fa088b0b775eb2b065588bdb4b5896c0c9aacfe62c25d2176496f6519a4f55ffad94c20c4c477dd4f6959ed11c3be23972470908be52f9c0bdef44aff436f63fe8b44d8e7cf72941e406e9317318632ba48a37f78c04cd553c96fb71bc05e95b9de4d793086a6d1899d5169a0c82b918d01e4568db7b267d750566570240b4aa0d3c86e508f5548b74d26d50c2a4fdca4d3b93240cb1ea35e13b04040234b99bbbc32c88a3421d07ddf20b914cabb8373ae7ad109872ef36cf98fdef81e9461a6238ae470ae67a7214d719b52aa346b2b9e2533c7b02071dbfa1609bd05409b21c6bebd29039fa60b1d0b0365689461dd5a44dbae2e07a8c0992381de25fa7c29130118f0cb6c2c9fdb415e76fd687d0408fcececb3efc02a15a5861a9c5fcca397b50f2dd9654d1a06b8de5ad46faff15473b7323ab0a13b5b45edd16014346d8e5343ac5de9d781797e3a0a184f1fdc4c302845922927153524c31178f8a31c4da9938f36a311a66246cdf42294cafd338019d7b27bc332ea701907bbe8b140b07809e251a85c6a10d85caedfbc509162b56a72d2cab452754f346b15d2884f28add106c955a109398c3a04e510e23c25a6c704ab1d67970737cc2162afb339013815d4933854c8d63dab24a2db281314e093ab9ef19ad3428a9ebb035471d228b2c066c08741cb14eeef5f08a3f71e3c669b2cd3b9f21c81c0243bad9a0666fd502ccc9fe15dbe0057e31ea8ed68b94b92a6bf611ed8d5bb675c9929c56d13a63628f80c0c6023473590db25d315782a8a48f65423f597a63c759b0955910cbbba2315b32c85819f781c2eb747727b7d19cebcb34295b8083039acda3d27b60d30c467b3a2986531a82b549d285adc3e4379ed54b0fa598f1d983c73cc266720916c5cc63ff3dd05357e21b424347eb5a543e209fd88e022b3f2d8703212a28e7eb374e0553f1bc6f3ac74d5a0f8b4ef5b09c64a067648e9d41c3c941d4f9984ed8678ba335ca53fe0abba70c604a11b9a9cacda1decf57c4391968f4d25f9e60a0767f9f451850b85a84b07bb3f0105413092b29e2fdfb88e67197d4396542069e5fdd436c8c5b247aeaf91553fd27c652ab62c7d0f5169e0dc716e9df6fcbe60bb9d397f45f66f999cd5acfc59133b13c9a6cb412b3cfdbc05e80fe5efcc333416f249f912c0a6cc47e1f3901dcfd12c5778448a6016f5f723ae328f09d7f05da583e747c077b36f692d32f0082f0eb6cb50535783d51e116522ce395b145d98e31e2d59335d4e01fe9406f1076c40fe948cf19ec90c7d0a1fbd73409b61a8b693c420ebe0020ebb158e3872a99249f41b194a878531f7c2fbb6d27b7fc8c5a4e94368bca0aad10aa6ae2cf7227b5bc3e21f4676878ffa43eb2fc02511345cf0e8166391384da0bc3250d74d6865650ef76c243c83a2c2ddd3353c63b42ac0e2e2b60e9e367242dc404c6ee72ec8a12a62f5fb4298444b59cbcf89c2443fabc54ae244df255407ee7d348accb76b14374a632220727368559ae095489238f1d4c43a2c4750aabea39dc0ec39f5c815b27e4536f287b36633c712d44fb84a9ffda8464493501fba9250c7bad6387f31dc208bb210f76eb6a8eb263ab934805318f92c81ec5559f0f3716086bf03f48bef6566c4d02fe08b64a2e19c2027b793f2ee46ea2c8b26fe8f5324e70c05d5534ad8bca0e16fb3398ab2e7afa36cfa74d119e037b8b2b6ea3d83ef8883f62d7166d0b9ccde282ded084fbafa1bd6fe2f016e202fe7014b0a00fc79e2cedd494161b1b390d518d8595c4fb203efcb82ae2f418b45b921143cae471dec82de5b824c6c084f1aa9a507586d7631d4554dd8d1be54435926410aee4d241f5ae3aeb632592f8b33861c218a522e973014116a920ec023f05b05064eeb84fa284625f2b117b109494cdc94cb8c4dad37ed077963accaf3b628e4f71dc29ff6fc7af2d83852f9f6325017abfbc60ce8c50c9d8b90232add95015a5e52ad087c108a344c40344e9ec3fba0f6b3d13278316d4e84118bc0f26bf6bea296daf6fe279fc373a996bbd5b2ea50e2dbe5f747dac7d1e04be22068adda31636d8d054c586b9c98432f182126a78efe5a3dac46d3702410c6be5af56f5420bf6741e816335ac8d14049affe3dc2cc901d14740faa232eaa23d9a1f5b5abfa3c27a3ad5845607dcb17dd93deac0a8a8e4e72b9050a53e24a2968061015a35e6f33dad24b0f644b9a8c9e5e549dc647700b1b13476dacb8950b97257a41ea977d06afdf74839ba1469293368dd6753ecd15cf1ef637e03890df6d81ff01ba0c2872a22e427f0bd51f76df101d0fb8a978ed3df0866d0682c5ecb00b9c6931992b7aced9e4381b3003a98b8f69a91dcdf695ad7295e37dac463c3918a35c24ba2a044288e3742250c76d2e982c366546f9aecb8755717de309456fa4f7d47ed3e1d46d1ad4bab5b3c50ac0456b971c7366b01c5d9c6a9f09679aca185deb59586a154d9bb534a0b6c721ad686f314de6ba3606fbaaf59782ee2242ec3ab73a3aea4689b1de73c4b2fe7b5be10ca1474852cd552fa0aa5c356e24e3ed86ecd4725e081ec90862fa9203f3062eb99cbdbe8830e7848e8335fe6df1038973d870cf8803f5acc77baaa0b79485ccc73fae17887e3ec4fd3303685f90c8a0cf61cdee4de9d97a0350de0b3582459ec09abd9052d9e21640299d6d008f7e6e02e90bf90cca997c26b9eaed3fad57907313a3cfb97ee6b1dab6f856616b61f327c88ab6ed5bddac0b435629449bd7e103bbbf68f7fe9ab1d1c1fdcafe06713ef5c4a0c00e27e2612efa3f419bd5b714c5d0a79471b53ea1a6582105962c1b7296820ce7821226d558ab13a7b94c9c09e2662b3d0eee975a9d380e7ccd7de33096ad9a75d09d23b89a03b8c3e3f575299c6f180e16ed89bcc62714c00d17ce08b3b0f2c2b2296d2978bddd9a61491cd128c6bc91d8585636f2af6fa284d21d1eca4f71ad20c0a35900a5d6dd557ac079727d644ac4824ca1db64247bbbc67e328a2f45c876b86765ff1a15fff4c9c10c16bc23b62b0d81cba3e3e11df5aac1717fa8ef551297d6a2488458dd911c197bda3e7bf73afb943c493059dc2870b3506e8ca4da1873309357e84510a4950e456eb3bc46bc865359b9af47d24a03375c8dc0ebd01e576c08ae1f8b9ce5c6b0c7a3b9c83492ca173b54c5e958e7b1061d4cf2dc855818ecdb515c5041bafe811de76ff8153fdeacc4bd05158e111fa2500be4f3af74381fa0e13b9783120c1c58f68522fd38cc626a187b37e30c30222444aa810cc39d6dedec7cb805c900c41bbf164f999204765c2cc623775534177bd2d65decae9f886c36fd3695dfc8cdb9f89ddc9f6974d500980f645578800dbde7e2270619268fd3a64c9c6de2925e29351f780f7d46a4c71394ca7268d3602c24a1acc664afcc8af8e78c7818896bc1a25a5f1ea29acee1a1c1d557b9ba880c3745bffe9f50bd83e4a873518ed1e5366406b7a3cce4a86f99729b36ca5419d7bc73662dae25ddee613fea62ca65cfcceaec47f919df2ac0d4100b4a958d64860d8b2f2d018d72e52691ca4c7891ae44b37fcc6f2536bf3582f8c0b4b56dad97a1026d5b4320086fcd6487cbef14ba61a9192a99a5189987b81d9997ba6e742178a438048e15b6f755bc390b1c8ff7a86ed58e813c59f9ead4887438dc5031ab68b7f16b6444ceeaad22ca5bf58aa0ee60deafd3770ef8527fece159caea8fb59a9e2c26668b40d6e6a1c390b0400693835ec85ab6624a8e54e5eecffb124d74806f48e009ef804e2cc77b3c2ab07795cab75ddc284b05566cd37bd159ca6fd24d7009a1386ee1e0e72d17a9bda593f3421a83dcd0e82c4fb758aacbfed7a09e75073142f91f00d42974f2ca9a71c8a45d73abd70ce2f5c951358b788ef95b23fda906ad106b4294df44b5083801f8e884800f904e7b28f31e788e92c8ca3267280d7d397fa0743646c4267e10066e9d907085a0469ef3036ec3e78d7dde5ab6d325f1203aed490a4c3402233176065be3b7a780e9d09c36b5d7af9d1d78cbea6396fca53f1d6e0bc2895baf2994f9d0c36eb7781e2e08bf7b866d91f0c64be1cefca89bebbbde969bee35f6d5ddadf4f0e520d280bbd87e4581435d516d6c862a61d44611d4161e884ed4461701c8adbab5cd134aad71622bdeb3dbc9ad4d92f7478440a9b0bfdd7c43ac4c0b15bca88471a488f1590edf8883b446609173fd9086dbb7104b6f1a334ea1f92df256bb41144519e8cef4352780dae1f46d74e6972cc1df09e1cc948f730a9906ae0bd206ac398d946199c391d528de553281ee34a53b05d5be8fd4fb0ee14e516d09664d47819130d7832f159d7daba3151a7d7e11a09f78d2ba46dd8c461ddc480566a0ac5403c85f5502dd5b1a9342d20ae9fd056766404145b0463fd959468f10e542315b383340b8bd65bd8096a83dbf0dc7a98cac12b35ac921b3ca197621915be8b7ff768dffeb79d0411217a81dbb7d26c055c6a7cacde22a128eb516f22826a5fe5823800dde5317d55df2842c8c03b0b5e13b3280a37bc6f24e140d16c40cc7a2b04ecfb9b253f6adf3d884aca5f02a247e26b084c9e3d457520f0158d6939aa8c305d4426bcb73d266d4b385daebb42df2d48c29016ba3fa390d1285101293a0500630118e5e0a1d04576ba9af65265d53ebb841394fb5e76f292eeca6ea9cc1566d7410ed4c40c82dbdbc4076f5f8148818f969660df7d2c2d7332abbf98d35290a054d74cf243eafc5ca519e00f82a09bc786d01b9c4882fd24ba335b847dff3ef3ad7422be7ff909215b0b420cf84a779e13107c223d36120895b35416368a1bab5d452fa35a42180d75c6a47d50ae3626109a45d8ff8e412c4c49a31669f95134ae2b47f7afd5365f76f1c0efba30bdf001872ca41bca55f8f8ffbc778afc7e080a8c13bde0d8fd64fba23c63e36d8b2585c668151d8358703081293c07605488956c02b6b43cd0851d22f0b6c50c2ac1437e121c5758ec383f522b8469faf1eb93b32c9140e49abe90f362ab35317ee571df07e95dcf7f2d53771530a4d3d88782cbf4a2829cee498ee5634b4d8d36c4eee76528c79ad99086cc8eac369b60506a1aa26f0a588f709657d4eea10d08b06213bcd55d9089876c78c2182241e819bfba1555450a86abec05f26fcfc712bba3f4669824fc82ad4a0a30ed6925e4c908b7d4d103ed2a532034d563085a3c0a37b31f78deef7629d430cd6c5d383a2e87240136e20906f7cd5baba35314156c402dad8f19cc7844e0652b05e575d7e97752a90a2ec00cbeae36139261250627a9d99a140e6149534c6ad68e7b06f70e442d9997aaadf28ccfc9e42405979e1b6801357b3d160b8088ea1bac351ea8d0e2efc17bb6406573370e0017ddb233424148f5b65e790b83abe90ef101b5d56876fe3c1a8b8decc330baaeacececd2d65758023d9958e9d966eb456d707e40382aafefe8ca71dcecf4ea2ee088f9fe606dc96399ceb8cfbd7a701d703021611371a8eda79fafa8d4419c062667ee6578b4bc98f1143f5b97e53196ef4d389a23884bce6cc331f0feb32ff9150f94adb5a3a2bd0831e811c492963cfe41de6b1f080e2235a0b87b448acc93d46f4a98620333ac6edac2906a9e6a078c6cde340e4f15127cf53497d0154f388772f2491bd0a7a0587a7ab17d48ac2c10ef1130ba2107e046e8be30c2d7df219d254b9896867d3c76774787e26572a6050d6dd8ebfe8dc4c37e5b7ce2b31f01c317e784936f7097c85863e50d0562e71d4ad9671f9510cb2dee75852961c98f2fb3d9d8561f1d85e9b7adbcde8b30c967210b66dedb69b0bcb9e15c92aba76b85b64383c64f399a2bb3a5253cf871b53ebfbbccc597aeeec0c0c1144b2054b41245141cb243ca73d029b213b15d4a569a766462310f7fd78528e79995802902ceeae968b38a5f00440864d17ba3810cf365de00674a5e620b073596038adc27eb96901cda40c7314f63fd77587fa447249b1da6347c43a4a2b1e289037565c4a6c64fc6d0996701630423e65558395f210d79582227c16ee47b0a25a4bd3a7db3a272dd8a429132cc1c5bc4eec5c2bfbf01e06b744824c01db2041652c94b6b6f57353ee4ec01cde38b8b5db90e00b54269a70efabf482b73d365466bbb911a76cbfc0f50538087249f75009e6e194d4e439029c65f80820cca65bfdb0d3c543a62fa0d044e275001a206b6077c146e7e1c0f18944cc4addb1ee1146012094b2f3900cfb7693dc13a13176694c63310d7a035fd0433e266a12fb2ab3290178652b32b3f0ee4e032eff50b08ad1d59b1330f1cb744915f92edeb6cc39c335e77a66b21ebe4531145b31b46e1361147468fc3b44193bc570af9ce4ac09e320ee92b25da1285f935327c0c68e47615a08a5ae62db99ffe590b499ea0af9710c2f67c651c4a49b4934ed8c1fb9fb3a42da34d85bac2c09dac894e50d7c2b5d2646a6a54a5dca55926e0da11b6899dde9fa2658706316ada5ce3e4b85c61359d02165c78e334429a23f2de91ce3b4626009023205ceb1d50ae13eb055a2b182fede06252e605baa17f8cc0a26ca187189c017e0577e48cf509c1cf1a3924302070811bdf674ee67305f8f248024dd61a026a14c1249d533298220b9be36900780328e756d8c68a542b1f6290b78887c6b35064de967dbc126d7b690aceff137af2c59a411ae822c4a94a1a0181b29b629da52737128651c7e8ca528c83f0a04274505337b5e28b32cddddcb1a3634f8f6774464b7305d4dda73d50a1c035c70d8ff82b408624b9e8e1352432815eb206dc35962fe946b1128fb69622aca0231ab4e801516f64825a4d1f27a3a6ffa56750f589c24ba1834a4588b47f0261489c0918e7e32d68f990fc62b9db92706e195b5c6d9e10b6d61797c66ede1cee8c938a44f018b3cd031689f15914c83063fca831086fbc414e9d07bad8e2e83e8247075c7f5df0e090c44a0f8e54f06626275382d22d91f9e9088bea8761bb3971137e3bd0e5595f0f97dc01383bda30a7174a6c6011dcc36750178da7fa78e911f9556218a653acb99c1286ab1b060baebef0a1d108e8c022ccaee5c131af6cbf7f12c53c7e4f1fb4d84dcfb2f1f0b8f3478adee60c95899b8246ff66b1d106685e1f6f06fc1ca8b22afe280004dd20ca3284f9c88bff325a6334a0f8850732953896008683b41615c3fb32857feec858c69eafed4944d458608d5e469b55d431b3d7095c50447f5ea14823b2ea66afeb828dbc6a78d2e260eb8ce652a87ec5158334f13769affc29168ae4a55426a812c4819ae0e80499d399115b5c06bac525c6525eee3770df2b59ea647d2940bd88d3b098c7f299e7ad931099c3f8d14f730d1f43bcf0fe7f9d38df46e877651d57437702046c8354b0fb7b46537c0d30f1f77b913c69b03e2aa89b54165cacd8d9e45e1ef55f656339cf0b019088fc3ba73a0c8af31db85c46d25ee1ab245623a976a1cd3ae0a9d01fab040eb5d8e5995e8aa99d1887fd47a9e88ae6ba9af7dd796144111be04650389c89006f82fce479bea73e6e6f9fba115eb1e8f402f2ac1b51cd2faa47abf0205130e228777969a4fc0f01741bddc27f728d9af3b858ed8458838f0c64ecfbc5140f248529b5a3f1c0a052f2e328c585750361da0b8134ea5607a991ec88bda9770c46f8d6ae440e5c89a88e78e05b89e1756a109cde1ac59150ae150c52b5571a8ed759f1e6387fcac6bf92563667a885ed4085d8fa43d50fd524960ec1eea49b7a1c11a0f06c5b50c360f1daee1f078a74431f64c1ff3b444be21c4aca92d88315d936f3b06dc37ca89526cde13194445717b02998ce76fd62c3478dc8b0b38208614adcfef4509a43a12e52006889aaa5ee009328f2867a9ff459a1b4408eec256757f0de1878e1f62bcf546948186c2dfabfe1e001c9253503a2f62336112d6207a0de1c578e7f53586e42fb6c85a2320bb79db51fe4fc31e2282ecf5affba4fd8b6cb48d9ef8c7f4b72c87be045c4446b4ff3e718844a1abdef9bcac8a16cfe826952fe5aa6885040d2745688417203d0c8912b9ff762e8dea1e44c1729fc9294eb29f96fa9cd677c8a35b830f6c878efd80638196e1955440b4be7bac4154ec875e7c53f4d6b8b67599cdbb95056c59e8ccafe90244552c2ff6d6e65bc54f019c30b161ded69694e7a0fa22181d57a561474ed66d664d2db219f33536928323694e0f6a61c366d301699c3f6da6fe51cf6ad47eb54b71d41fd1c0a1d3997006e6aee54a3238e5bdb71b5659c7d0ee19fa08fe6ab554b2f57211ecdc78ac62ea7312153dfecb615af96843e6d3a0aa1165c19cf81c84fb4cb9ad78ba52c40be28c714199871232b31e4df42f3b532a207e36b234a46b169ae7da855f0cf71a766214cd0b83a669b354d021b3266a40d665166733d90a4205ca8e208b410d6de31a1d1388823f40a2c177f0c854f4914e3e430e2bd1e722336186a2162e41a15ce92f4185430e8d4181bd4267588b740ee47f6bd300795e206f5ac6cf0be58bc082d54913e300d1d19aaf5d3504c9d103d8d021d15379753a40e8fbdba5fecb8ee9310d21ddc55c5037ac15b3b3d913863086944695edc5f6256f50d3acd8f0060a4cf541ad2dfed806a75c45e4a2680d23b3dc55e0f9eab9f5753ec096736122c3b291a28aedd358544e0caa0b71132de2ef1402d7c0c47c4488805f309ac4b8d83bb28282ec75b1201bf091c0af5461a5028889004d0c03fa3bc5074c73c459ba8e7c7ff13bf9d4257cba0c5898676bd86991e156298df5239983e0e11b8ab85a2f50d6b3bd71744232654380d35bbb92e5d8cc6a479656f75e008457007d64ee1ee04d297f65a4cefd2f073e0e31fe22023769947d0e4e86735eac822f5447305624f292363e015d8cc46e852fc5568055abbb785bd1a0174ffee39da21dee2499abd4ab1f2ab7174136ecf209fc4ab6d0b06fdb4c8dee428ebcb52eb8f8466a830aee80a139c540b2ed01acb6309c78209483bbeea703e65afbe2159605b81ae63e815a95e6b3ef91e9a79990687b1016e382e188164307a698ef11869bcdca6378153a65a65792979cc959873d6b3de72296f8a63bb9a7e3b5aca34e072d257f63f00e72017523198634017283379629d0a296ff8fcb964edd00f32b69bdb00c6e80e3b874d91f2702c0e40b5766aae8f08c399a00295006b0ccca82961c0a7176b19088fcefe4d232cd33f288dadd9d6608901d0c04271c48ceae3f7c1e9993a62e1f8e8b4a7bb8427bacb5ec3c1f2bd7d19b42cdaab0bd4e47fa1ff907f8319d0086449d28facc9a1026028698ebaa94aff30e95fdc3d4c45c79e712b17ab3f2e954fdb8d09d2958d84eabc470e88452c8befed1d2083adc1fb086cf568ed9ad1bcfe253143f47cc1a59128040cb9bc6882e7493e563dd51326c1a064b244e56b81cab6171c55ae1e05fcdde460acb830de6104c7c855063a9cf43ee5835ba419455a8a1ee982d1dbfa0268ad956a49a4158e08d4685032e81de8c4559961610a44cf10eed1ca7f3687662c51481a3cb3b409036d9aca56092fe92aca74f8bc55131d35967c65ebc96c7a392cb0ac23256f0ab728c8552deca3c96e083ae0397360b84e11f9c41d81eeb4e43ecf689e133f890d2181c20d599f169c2e8f2e09f3631aa2106c9669c361162c8a81510c38b2986b69e5e2e9d1704d96316dac7c28b2aafb7ee685d89e8d7e176693acb3a57593c6c36d862c2a2a30f8c8ef17d09dadd06acf70191c6501999f444ea3b80bbad10e7ef3c26646142151779e955593cb8bc645b108bd4c93ddcf1e1f5ebf1dfe4f199a0ebac7e51291a7d6717644325816c802f57726bae2e461ce56ce830c7316d957fbf553433870c0fb14430cec2424364c411fcceefeaba45fdd4cdfc8fca710137e6ae4661e3e247c0f972ff7e8fc4762fadd8184bb95b313024ebd9de63ae2b3a0e2c1dd6fcba3080de64512d664c5cfcc93fa1c120ab70455eb276915978d954243933b0810af9f37493211a2449005771aba1f0b11050fe9b2d6624a144cbcc868e59fb8938afed914fb5a28e3d85b57b9e7199d32e63a6d57808c9762e651a8672fe4788a959669c0ad40af0419ceb4290941ba027fbd2043f99be8c581d17c4f7d6f65aa89b059fcbcfb978f315ed1c4146131cffabbcae41d10506cb4f56f4d2366999d690b36891866418e13a5d7151eb7c1d7bd4413e51edcdd180886bc6939aa37bde79ac8a53479de99a762754efe6ec680f9a6c9ee91a3054661c4a727e6386ee477f99e26c160f8d1cc0ff4183db7040a187d9ed0e77f3939a92dc4c7c78c9ed1bd12d8adf8eb9cc9d567806d4cf3f51f1ca742017850bca1de7bd2ccee060860c2c770cf0c0f50122e99c698e45c04c41fa264b12dd0b22e02f25f05ee978c175414dc5422785af196f7185e67fc897a45b64c12761f16ceb6e3909807ff171b8c3d91941ba1b608bd334033f83245a3ed408ba9de6f739f669120febf9503dc0b8edc5700547e292b4c04eaaaea4059325af13b2153712c8e60ca7defbab3950035aff022b095c550cffd08c503f501557f72630036b75112cf07d40c3d819986103057281e00f756672098f5c0a804a3c891d97257dbd8398bcabf9d82bb05dab00a5cc7fbe95fd5b48d5752955935ee9c32edb2966328b0a5444f9d80b6f94892de544374b8863ceb8ef3143b17eedccccda82d531e1fc6b41aa530713b8fe5ff826ee41367b468b0a21c4009f873be25f755367c958167fdc0a4a2347150377ea3b73c69a16b24cd4c8ef6302979e55e55c3a35cc0026121d805c155a68e6aa17fe5671a0750428e416b686ee7490793fe9a1ad74d8cb613893156543f942025ce448bd8866cee44155763191c4b24a25ae9879629b32d735bfe5f7f292346cb09238e11132d4488d145903e6d021c3b226b601fd944d12310e4318f70e0ba731e7752f0bb8239881382bd074664bee02009dfa7b3c49848ce071539693f71b256b49228b912bee6d9f6264a258ad89daded00863084c30de4d393bcb49d284954434da48fe8e9c02935b391f70b424caa4d002c7defce593d5b4e5b0ea1f44579c8a95c2acdf031e43c9f657a6e9e77e6e9e4b0d9568aa8262e076acf5ccd76e0f6f33cabb792807df3800743ee545be550a16ac2345be5deb92288632506f0e9feca75d98478fa0a2073265fb0b4900290cb991947f46edc158712751effd1a48721c8573b849dc6e3bfe6d8e6979c9046c3c3689eaa81ebf101c41c36f945f8719e3b737c006232f8d8c8e52d9a8f1d8f6066d81685cff8461ca6fcf4da700af61928f3a578013a0e05e6ae112ff243bf19adc734262b9ac5493efdc400cb1aa2ba53a2ff488b618066ffcded4311b1ca111ce0d23be27ee83c79c08896d62f81e2760d0cbad8652d00b47af7fedcfa0d64f09ec3f26adaf5f9243c474ff78bf78043f3e4f200c705d61809eebcae4dfb90ce6ab5439df02b5494d6925bc083c6cff0ee154566676bcc1439b9dacf630a1b618bbfeb93126928a035d8bc483dedf91d59afa90bd49a934880e8d529caffff6930a40925a82b0e7e7a3d70ccd2757685e4cfade8ab2c48f22dae327a1eb7ed683491f8d99b60720f38980e9518f0a6df6fc2b645910f1e0ae9f0eb4261e3da68a0dc5d65e9b1a2ae8de4c15fafd9ed221866728a2ee9cc4bcd9ef8a689306aad0339004727c8c09f245208611e6138b8ffab3abe60699fc6259cf4c48f83ee2699ff2f858602a72dada4ede293a55a497eb9ec9b25c1c322b46d5dfc34aa4f0b57a861e859d031b533c2d4cd921884aba562ef3a38b10543d8c88dca1d0898975d52bd1a56577739cb7daaf41a3c0f3289e12732f1d23e029f62ed4718ced2dbc4b6b7828e818a0c6944737c094d8f900a113eb9898ecca085298128bcd8b282ba8862eba6df9ec8cab1ebfb5a2028ae056c5cc102408d5496a7bf0e80bf70f9e0040b10642f51b30a75327a4ec897e48727f96e356d62f41fa58111810286ccf4e919583f8d3b7cef621bd6ebd74cadc8b000b01bb286021164a9bb6686af684031f9468ac3f8c661397ef327ad54a5c53e775d0036ca75b50c7da2d9f30fc00597993d81552cf03b34941664f48dedf350351d78c0383e3d645073b07e07c6dfb69ce645b66c67c06fa2c94d89fdf53d10ab304074c897c92348a9ab829d0a64be5d7cb2aabbdec4e64950dbb4f3014631339eb539edba4b4aea354e2424d7458eb1a962ca9140d5c404e38ba882e90ebfb56887bbc120fdbf4fa11a0c8f59c0311cb9a6743c3bb7c634007dd85fde58a15bbf7621a25472b49ae506bcb5b13b9f2287388af4e66ea6479885c04cbd429252f37dab80ae8941961872d62bb342615be6216b35b0f8bfbe9a26374b5c6c9843a88624d8cd1072a11092cd23dbcbc4732fa6b62bc8a29c0b998547a513c39ca282a7d92881ebe572318fd36ca53393d10013f4f718bafad0811fd1d55076c1a80e8584da1d492b0ef7ac4d47111b865a2d9139e0db78352ed5c58af8cd1973c74bb8bc2b3e4cc342bbcd07329e1b15ff1e3f7a3679c4ea1b5600eb1262cdee396c1881d93ed61a13a9b5d8461fe5c6919960d486d04e68e2a2243854b58a23e051d7c97c9bd42349825c136142620a64bdacd7532b1177e936dd7ca7cd01ac9ecbed38f38a6c0099d9bf6ef9658185e363828a021c11f8ecbd035054c25105206cd3523f7e85e1ed10365a0656485a68c81fa5571ae0ebfb1d5a1a2e06079f616470f4739486a0d810ed72034f38eb28879402e66cebac618225dbf1397775f48548f2e5c6a1bd7daf5c7c38c3915b01d3f1c333b052cfbe129557dab1fc555a0796c6fd3d4505e5aa2399e2cdb96a89755ae68d26ff94f7863a225a24686f88268b5628e976e7b3e5bd6535f99ab975196dca881382cabf245a98c210291839f04efbbf9fd514c3d52333e0a20536d160916f6096cb89d8d8f88ccd9dfd469f1e9c54d5f8a26127dc9441355605050bc062036ff358795a69d963630a456ccc8154409f89f7069f54a070256a984d60370d1e655e300745edc34f26bb5201e87ec5e13eeeaddebb88d606b6a51d9033a860599085c6d0a19ff01c442ee2492646dc5b093d98919de4fe59e4dbb47bd89ed30d39268b12b39eda6b87a3580ae57aadc70c46451f816ef0f297bd374f002300e637dc95ad13c781d407070413bc73b6ad97179f6ded2a19173e21c6caa283640302036fff5090a344beed50f5195e93b64d0ecae631dddbcd2f1e322c03e074c9667d30dd8383a1daf03502ca58be80abab6ec56c64b3fa788916d7160badabd94e80e882a3078050a4d4a02c7d2a3151baed265b35a0b1e16ecd24c837510a9f50ced50cb88948e651c5940f00d5d5e53e0e5f541133ef0f672747941364838012eaf7c31d4613e552fbbe5457d01fa2e04560874b8818704d42cf9825dbd829debc776979534fb7170bd949f975d0d535731fc0008fcd10082abb93523f62eec44cdde4d99bfce445275d5c06687d4279f1b1bc2c1159f8923d11428994f53249f20606f2848c3d91a6ce9373347e8ae558c21b45f726d4993cc01690c0aa04e008dcf3ad4e2c2163389d407da0569861488ac5f27ca268f38730e27b1552c30d48e78af983853b73f231ccec9b20f15af1e3ff58d3af78680793e20447a6e45790bfcbe9cab599e4db5f9a1d142825485528641402f260983a86c3eb207ff48c025d81a419f7d3b00635d104ac709d79c75822b006742dcda914a02db077124a83709e6514b36e6f13a5f6478c4381f28a15c3454acfedb6e30932e560bae883278055d45c19bd6474088816ece1c9bd7ae305f50f0e4746be6b125dcbe177a7d5b609107fe8798daab5595d448e6066c74b168d6c0dcc39dd8cfcc860cd8cf12e52015aa327f8048a2ce50ef41c95df6c133189d01b9d7776a382eddccf5e078fe24e407149f870ca257769365519e22fefa49e8a1d32246865095428e28fd028660158dd7280ea100fd89261c4db7dcf818a812e3a24bb4f6d6c094a69a52944684a87ad91b016d9264a763ff833106abb9831e94e9eafa1b45178cc73fa4b09261c3abb417aff5e6370e4e74dd683f8790eb0fa8f787a076281b41cfd4ca8c03079a748bab1ead7ec9db5023e461e223d08d71b1a5d665269b3cb2b104a51a42e9fe88898db846ae5174e721086eebcc8c80e380910115d543535d756dfc2b8b499041984af4c446f136f20334d005b3aa896223cf04b314ed7ed0a84574b711601bc8a2f665046c744d1bedb7db59a6290293eb4b2728a81f092a3abeaa51548db3a140e0d666af93bf37a3c06937ae19e40321aa51d0937246a3b44a7c97dd916ad4982a20817b845ab4e93a0cbdaad19aba2903a6b7a10084204971fe5cad1700c5caf88e30ac9a31278392ef6834b5c82009a398b1bbb553c62024616fc62c20468988b8c1e2755a17732b16a54e2169bc1bcd820dd4dc135123e4114050c617dd416b83081a11208f7f0cc8a99deabbe7e460aa96c188976194a29b783d0dfeb47444e7d541c4ebadf9fc6d295edb274d09d9483f20b76e7cc13ca51a50a7a7720b38393165c79c1021292a360cef0a3fe3265d89b8008751452a0006758fa61ece35f8c468701b75444300bf4221780d3da787fbfb6278ad6fd09d8a3122c03d99bf5105d350c9be32fcdb693ad6ece0ee8cb43a2428777a50f7f144c11a2d9901533119ac7de81b3e77bd5974fd0e480537e35a0e22535faebaad439eb71b2b8b9d6588cb448fb7a516d37aa3bac1459429aa22e509152c8bc2328064bad70a98ece2f85c76ec5d2d409546d193805f112a6621c72a0dfc2548212378764a24edde3a1a0fe354ed7f3b6756e5579f181206cffaf61e0994885528f794543396adb125e11eb30db50de6385e1cb6000c76ed5c395bcd76eb8f6f957fd1af83879a5084e6942d55d53244945249daf37d91ffa6f798e3b6d8233360cf35e36603117f040ac66ea2f346b6c161ec07f2eaa7cab24e49aa1fe870cdff3f826da1c5dcb088eddb266afc2582538a704496b62d90bfccd0955527158134cd86d603768fa6950ff328a88850712ab8685c350d1eef9b6ad4ac2bd183d7ecbc05bb6944a46b6384430736a65b279c1519031f47adebdbf439d7d87803a2c406b2ef744272ac46d42fe444f5fd9f95f28aa359385f782450c20e955df989f2d8c0f53de3c8dc33e88ecc4befd55bd99b4f1c944be1014aaf1786a54d2d0937569bf9101af6b2fe848367073ba1441be5242533343b7adfd87538105da0e28228905f9284237de07318eba50aefc557121fc49643595045e4528833f9d5dce091cb6959718ec4c3b350aac1a272a6f5f7ea5b6db0b53ebedfc41e4dcb4d2aca841e28e4211bd617cdf74736bc3454b6b47798e80c98df1eb1aca2af5d553962bc8905cfa5c576edf9e5fa42dbd5639867b7bd08dfbadbee3c2c4826bea23920fa81d50989ce9238104eed0c048c6a095bac48387a8a88ce1235f2b74988a0781da5d8b086427be3fe0f8a826c2f3b9e3ed1c6893a3335bab5b0f2a3dfc3520b38988ce324c8c4e42600972f18d26c4a6a12f45a0518a439826e60a09114d2404722acaba86f3fc9fccde76c8eaa09ace4f50418cee21b4f8d4ace7deac9ee7a397c9336e701ea54004c18705c7ea1cca774b95be5d1acd5676207f10c8a219d98b03861e5091a83881cae00ea4037bcf1db741c764c011a2067479c8dbdefb62a38a4887a98628ba1da33f4fbad61bbf49b1c73dd66e3b78678a3f51a3431747a5e38b6db8dbeb2dfbb9f7429fa7ec2e9ca5da87c12cebe6847a37e07fd7aea404383a7177f85e4247ccae35cb4e8dbbfe7c17daf18e4c6455ddfbdf6bcbd22dc05ba0d3dc00326d0ffd72c7c3a00903a24748abd632a648a529304b3cbbd3adc34eea54cd0e81e39d863f69c6053146b7fb64256bb31b6cb11da60eda2a2206c9b1d3ee62643daa654d743d41c4445f652631b037472022aa001555f40103a520749337a2ff74c9eb17c5fd538234b5d7f94b0aeab24a8194f51eb0b914295d6a0661834b85a30fb5282dac12026a46c81ce0077c299cb62cca54decfbc115a33efa5a89c860da1f697b9e45a2f69b07e511d94df6de52a694640a2c05fe043705d3a548fdffff7006334c30816a41959672ce7108342d42087265195c8910028d09593896fbf7301668ab9d8be511b3425705bdd0ff0fed38c1554e432bb5f5411fe4ac2ef46edb966d97c1c2732fe590cc02c73949509dda133a87b33ea8ebfa9182ba1c246efca01dd07e2f8abf64fca117fa3f09a9adffa0eb6e7f4bd7b4fee81e750db530d7836055d023008604a798850058145671dc78811cf76d0e677d4f685d5c0682c1aedbefe9b8b1a7a784c28dcb4cd3a30c0664a0059b057eabecbceff597c3cf4f952a8cb2e99c65c50602dff4c018a4e3851b56d85812c314214364c780af98e224091f3220aa62053b4f86a080e8c847902a349c7904edc8fc232fcc716bf031b44344e767884d2b05432263004ba2fa61f244143ad0600620a0244549b4c072b0e1c36b494d1180978e12ae08c1a8b2654e0413dae658e52073285ac827a90cd163d32dba60e3a20b369f3bf1893d94144c1f2170a88f0e3823707033d4812efe91d8cd4322a7062f12d00caeaf881bc8a4acc7e622ab1ccdc0975e0283adf402baf88110b358055d9437f88090cca6450904119b8f0592a31268b5296c3e174f8ac0812182899e17516043309a184216e3082747b030e6c3188eb1026ba266f3c9907142e665c8b08c8c1334b8a049cce68bc931c5e898d884237f6e301e9b4f0674b1451939b28d49069b8fbf19d2ca9478d07106cf90eed26ae1ce1703a92048cce633812e4a23a288c166d3a20c6283cd37430632e0f10074513a81a246037491067144cfc79fd835bc00064734c655139b17e58f1c7864f624458711f088281d45aaf0c85ce35703e492b42cd8b428b380b6f95e74c1668a2ed8c860f3221fe192c1cb864b9ced2ec749ce85d895626bf2dc5389071df9348a96c61896c06f1cc771bed768c92b9797296220b369d154021b17ba856f1817d8808418d12bede7a05f069ae104ffdd820e9c89fd7f4e4a39aeeb3cef0a89c4f37d2058faff77e9b8ffff7ffb55d03e51a7adf36dc88205c7712dfe398e724fffe9ff3f0ccc8b1730beed93649c2c15f97eb64d9cd384ea145857785b61e6093d671782b5eba8d7cdd925145ca4401bdd1e3d4a173c396128d3952bd6e7b5b54d9f63e67c744a01dd6839497a2aed9452be9ddd3b9ffdbc446fa860074a51fa90ae5b8591b9aaac02e00baaa95333b3da629902c250f6773bdf17b303496d71da8ff6a0e30a7f7fec412df7b747bb8f2bfced41d93aa5f2d9b6387b4cfb9c5de1dd4e3af670abc2b4d44af719504a291dd2dddd34d8092484e8dacb40428e70b1b3eb17915bd5b988dc2aea593ab640fbb6649f64bfee1320a78c0a9e3dc14b25913db0fba6fd1f7972d3722329013d56f8eb8d322a4caee3c49ae4fb627620a92d3bf61819013ef698d6ed1c7bf4f011ecd46a862a4729e4e38f4116ed0b10555a3ae79c7352cbb99d28b0e52cb52fb29da25bb72d8e45ce999dc1f620cccccf2477676667d18aef5f7e3584093bf5bf82a7563faa5b27b9d862e196c6cf798ed3aed0d76d277222d8bdcdf7f031aa822c655326206f1ec9b668749be443f7b32d6fb7c5020ca480bcdd6f6cb5f0baa4b1c7db1ed472ad1fa9e93a08829641b16bd15d5c25a9a06dd502c08c61a7ea25594bf2c6aeb3f91cb8fb689fa8ee7588b7ee62c7e3e7a4942bf1e0ba0e7c0f7c91db2d898769fc7bf14b3c3caca5ce1d76fd4e9c97177f77b79f14755aeaeeef3ee79cd4dffddd7ddabed3dffddddfdd8693649feb93b21be4e1de318d8fe0d675573f29e5e6a44fbb0761f76d3761606a3834278fc9145a9be738f739702caa933b35168d45b5d5dd07ed58645d9c008ddd19449f6406c00dee2f2c615247d61c088c9d72bc8953974ff0e6b64c93631e16f76aeebd3c9977d32c50c7ab51596d04615e0da67acc7b81b5182a4c85e95477e5efa1529d368a63d4aab6738b8f20f48673d861f306e3d76d392cbcf376e2a17e15137853f4a86b9e8775388b0a0911111515a53c968a5197c7fe69cc7178ddb62c9aee777fdc9677db5f77c85fde8b039ee542f9c3e49475308bf2aa3b57558f4f39b583dc964972d679e894568137156cbba559de4dabda0a09111115155199f3c8788ede8b36a1317726cda253a80c46563d99c77628b3bbbbbfe9beed2167f7d1aab6418afcc6c8e8b656dc5a05b96c63e4e8ae15f3c3b4aadf7dd6790cc9c7a816cb4cfff4f2c80c4565f14dd7533766c41c9da2717d5c75aaede98a7555e8aa192bee819626095351564a79830e999e2a65dcce59e1c949953606078333752f505606067c1aa24a7b12e2d4a44a7b0aa2049e4a50a53d91a04acba75895f634822aed4904a453083ad39d999d9854694f40c4004f20a8123cfd50e589a74a7b3af950a53d2da93366988e440712b343893f8e9d2aedbb8b6978ce69278c7f7b825569df7e8ceb930867a594524a29a594524af99cff4ff13f34d3c0b8df75524a2b6827f818d5026b0e84756069562864895245dcbde5b6dcc7f5b1baedba5ac3b0959a45b72d8e7b8ebb9552ca59d42c0ab375cdb43c463d3e2e95d12074466f688de604398ef8e2d22067791e0f0fcc59df9656b5f5c1132476d39323bb3acdc4e7f81df154695b1e8f6409e37ad7ba2606156a5509596acbf3783c26cdf21cc7a9a276ad6bc27730a6836ffff79e6982ddddf97fa6099e2b8963674ee7e114a9c65dfa0cce69a79d76064bcfb792bb3fb295737c87e1b9cee3ba1678f2c6b9a4bc55c0eb3620b9e9799e970a9708e5c81fc3ac87fa29013b81002c7bca752c1b9a22c8d6292afe0f56dd10e42c9fbd6b0aae79186641ff836aa1366016c9457a915c540724d71592abbbd9e1b6fd5793c0ee3367713ff8067ac5ed1bdeba5a1c90fb409c458f66a547b473750dbd75ea3bfb37f0ff6056aa5b5925ec8ff4c63492bb9d08a4b3e146da4adcfca6593e731ba433c4c4d6e4e67334e55def7216bdb9aa2dbd7d4675470e2a3966cef2a6b4aaad152c41424344b6a29bd111bdbd4b86030437c9ed426ff4c63a4830128fec9cdaca715f1c750d0cea01e91a0a05a77ae6aaa6419d6a2dac6a4b12fd0ac6d196ce3ca86bfcd6140aae711b70aa9dce5c0bf7e3320703b3e810bddda61c0db7ad9778d0a14eb5a5efe36320811bc25774372ec96d5bebe880eb5f52c1fa91935a5060de4819a00ffaad6b9a5b1db8f3fd0a667db6ad6b61562a0c39173af6fd69e5fd44209d0d69ec3e48b35ce640bc87754c3bbba9e504b90e13b735f1db8beb376771edb7dbae113ad576257396273423aa157946b7adb78573f9e8d5967053925ca4253e4681dfbfb74c91fa5f3339b9ea1a0ac8a1dba3b20dcbaa564f8c07d6cd6f8170061586235fae1281194808ee4d735bcccceccccceeeeee37d89d4170ff80aa386666073244aa7b75cccc0e64081bdd8b449259af505469e7cc7c0dd5409d666a3594a9531ce8d3dddd5d426a7ff25017ddfb8620a44a16df29eb1ed2804638030f98dd458b1930964db725c3fc029c31b7e5eeee73860919cc3142f7903946864c8c3bccac35653155aabc31556a8f8b49396ef2d0f08085a55199ffe58aeeeefe2f978a033233bf8852a5fd92bb7f2770520aa23e926703ec263755eedc29ae9269dc1d8493900ffa03366aa4c2b80c54e4ca6d851c14f5c565a02244dc16cb389d5aba0c54e45b02ba4650c244052428a2e449141d44143a3050b103262c883d15b513d8fbf95cd71d5152848d072d4e847c04e004162388f8c08b49139b2943cce0460054a4c80744e4a8080dd72f03150112d6c0fbddfd03feffeeefef333566e6999a64d7e7de726f7e99596ea4a8a44658515999422a21ca52b16047a583a954eaf30098a6ed000660c36c369bcdc250cec974b28efc117674ca1d65ed8d54cae74b5a08ca3b994e3a3d8927bbe69cfe9ccf4c9db9d9dddd3d6666a64382823a45e47bff4fc16ac75c9fd3ff1f94ff11b8ffa4fa3333f37b833876fce9e49ea02f38e64eba0ce6628689c9f1b4e0ba8fc402fcc09fb0cb5f4b964f3db8944a3cd8babb3f8f0bc5b9a71eaab4b3e4ee140c71ecb068e102e60507c1c0b1e313c78ebfff0c13a71354699f99993d4646cb788c34b1e02798cbb89f9038e184b4d5f07ddc124bf763979225d317579cd759f024555af7efb6925d46858fc0ed96ed62fbdfeeccdd4fcafd7f77b7e4cc952bcc5c6b8c18210768133464769eb9c2b6fd9464e68a7c66a6cfccccedcc5c7960409917304c251e0f83c3072ea4b5414a394aa9c3ee0c1931301cd6b00eb90ce8eea285f3d8d08662eb41165bf37a0b16b4bbdc6df5b84fe79c5f8018f7c7a0cbf25a1c3e5489c3076646fd77aa06337f5f1751351e46cf80364a3c3886bb5b8bc38769a5e83e7de6ca094b9533573a1040b8718326090d8d291544dad067ca5523b4aaed8e98549154cc7577af6296e5e727fc6933d9c6483bf1a3e4c7090b2544d044254458415332e77cda046135189a92171ea42949e2e727c8121a8d46a3d13e4aed7eba2a684baca04999732e9956d09ec88fcf8f15342b6856d0989052fed4a494424a29965002c8ca4f15dacc0454a494b424a620718414991145b8f7db8086fcf780068d94cc5d756aa8bae9825dea46b34ca14cd73534fed988619dd2714fc7e3f160ffddbafd7fbaff9cf461b43b8f7e5ec9a5e4c2a2850b98173068705dd766620f755007ddb61f0d295bb6d33daefae91e6bc5b09b74ac63e3090693e6691ed48ba2d93a1faa86a753a3460d0fe6cd3c2bde0cc499e1ba69a341e3f4ab3d06e3becc8c54694571044f39f5046241d968e1c18e6eaefab97970eb1ad8672424444434c354da69d729c5a2c8663399646aa8b869231a1a9ab619a3527d000208373e90522828282605c2b43de7c1da26adb44dcee6f4695bdbdad6b6b6dd9e2db1c4c828c99a077b81d5c01a0d4d890788a553cdeceeee6050a71aac85f002bb01d63a2c9d504724a748d9b485b7eb5152bf1bd346c3859247c2ba9ecec63a7c9af5a2d3aab642424444454546602d9ce30bac27466947c6240d1802c876dac01a0dbe9b08e06a7a3307875987f4212f0e100093276fe028005c07d69cd58dd3d65ddb023ad5f601a6d24e0324d3cc1600009e72ea08d336b90eac75a5182a014e7881bdd45e749af519c1c01a0cacc1c01a0cacc1c01aecbbc914b50dac8135104bb33c1d0f76000f96c208239478ec70932361729d4f37a5b3326dd3366db6ae67c9b4b9ebb46abaef88f94820a1c483ba33333309b3663e751bfe84d3e62aa34eb5a5e23cead496120a34ee1ce7cda74a19a59d54aa86d530b5b27248b0cb51027b3a1e6cda6651b3402c60edc11a97505d03b0a15d210897e5eda8e872ed3ab903212128b676562410926258e4e835a6e923ef71993bb92d196cbdf655b689dd16e7f28e9090629d024f7ededea4ca64db75b2653299ccbddafbb0f058763bec2b2c16bb75cd4fa366793e68cc6134e6301a73188d398cc61c46630ef35c34e6c5e61135ffffff3376db7a55b89e3f5a125161222a4127b8c1e2b28ccb56dad52d25a94f3881a5892b4cd4965002c8ca4f15daac8a09a82431058923a4c88c2882089f2854a03ce9713284104d82b0d6723c50aef3487408275f7ccf839052cad907aa54931b4216a49a295563004eddd4a4a0805aed5a2b8514b5da74590370cd89536d39556518b68a01cd9e06bceb27057cbf3f1d8774a238fdf0e42cb18742a345a145a1d128f7f3f37393cd59141a117fff2834ff28341f9969b44829ff7b58a5dc062925e55ac0b839fd273d22a3c9683f3f3f61952ab39f306c420394175854283a9eec3a124fa7643428495481a23384d28732c3cfcfcf10284368341a8d46aba1c2d5099dfdb1de4afcb9e94fabd070b8215ed0bb8ca6027f7f3a87bccb683ee04b75297d24d2101acf4ad9cdb8b2febcfcb894b2803f5307194d4693d14810fb1b94523a9fd2d51d810842c0040810fcc0b383f201b504a583ea01a504c58394f2070543254121411d41e5a08ca08aa088a0705043503ba07440dd482969a81c50425038a05ea81b5036a06a40055935177b71dd07ec398b94253624258bcb2b26737989d2e6932419da3c09e19678e86e25587793401fa8522e6ad219e67147c577b79779b797b57551bb21cd1866f6721c87ad9c653d972d69e6ac1448331e2908d516498824d4352d1214b7ad0e9efd51d7d51a8636bca49b020a66a4a17fa1c695b107541554b981133749630de5bcea759c294475aae52ce5c92efa1825fa7fd05c0205f74562d186ab548a8a29932fefe602d774ca76c86e0e992391d6674b29a7bb3580745d1f9fc72d1f8d8ba2cce26adc72e8345ca552d7c71c4aaaace5f031aad5e2b6ba1e5cae23b1b82de5c9ab050fa59b6c012bddf8e78c2b5bdc94cc30adaab3de654b1942182f6ee72c1fc330b401545b3b4cb8ad832510753ab92dd59c71d286e18ccd2121aab455f6922d7cc638f6e20949caf77291a47c2f8f8145e556ff226ba894359de56ebab86c6142166cc20c2245e84e24f19074a4e42409365f9435e78bda9c0ee726883461e290830d2fa669fb721bbc3077f28587927ee02ce9a8eae4c0e60e462edb49844d98375284e671d6c41927ce8bcf8b4fd7b45ea2bc44f1483fdc261dddf6665367c266cebc91264c22d3c8dc61e24c9c17d9ffe0c2e5f67b80bc93db922eb57983e315c19e8f14a14b543c69b9d46e976e321a4939ea59b9edcdbc293ed2044f0a124cd3b6e51571fbc513990bd70de0fd1567679615539e364bec87da8897d524bde68bf4f2644ce3b2c37c59cd66b3d56c367bad56ab5468d4ac06b2e5b6cb8656c2ef1792a1dbb66bac837e93db82a1e1d86e7b4336846acbabdd2b3e726ecb1bea9a96b7c45dc20507da6d6fc8e5e50575aaa987c5ab79435e6d88a8591e91373434b4828f51adc9ac63257b266a9652eff9ee0e93929ad457ab550d278fcfd86c327ba6cf944d188eafb18ecfb2747d5e961993de1089eb2c479a719934f2c8755c9533e62a9f29eb548b73868208d7c7d94354db96ec2cd2f29aa0b6ac458142f57f2585cc2065a8d2d2dc90ac43ca4f8a7ca5942820555ad48f2a2da362a8d2a23e80f28182a14a8bba280fa05e40b9aed8c1c98a2aed962aed4907555ace418c531555da18a7a32aed494b9516e684832aede90655da935195f66483530daab4272aaab4a7db890655dad30c664e32a8d276ab535195f6344595f614832aed090655da93edf4822aedc90555da530b70ac70acb82316e080e6f044880c95e1c80f1406f52f031d5982834aba0c74c4487704871962908a8ec40083fa721928470b079413831a6064158c82e44305af783e6abd0c947384e753635c06ca01a2ab22078937430e0ddc4e05afe8a4a8df6520234631232f9012c888100da49cdaa24ac7a4bab80c64c4c99c551997818c209937a8dd6520233b501f8c78e0a6966ee025514b0198dca8140d9aaca964330300010563160000180c08064442a15818459a9a730714801061823c6a563a1d08035112032908a22088610801c6004094310621858ce606000017c8c4065e76bfb25c3669bdc369148d411174584935211f9e0b17d910d4ab6bf9d078d10a61fa1b236ad794246a688a93890b6d3c505daf422192ff410deb3c2b370667e6708975744ff4fac4985edf5ae516212844d229c5b1f264926c40a11235d8d089ca45cac51040a626d22edf056fb1e588afb7d534fdb52baa1f5c8271d8233031bb5351be1e80b11f1a1a8a2dfaccc18fe01c9b24c4d5dcf52e6f84d9bcd632a405a9e4c360b00fc9541e21f77a9930c8ccec0f28ca137b0578dd7a123a15f433c3a849e1207517e3299443ac412f0da7bfa430f6c2af6ac6d4c64174b1eb24cbe6133c42c4adf2f429fd8cac74ca9e3ed247fa1a987304f58578af811b9e92c6df755fa6a4a7b1789c40d84a9096657987b477c45fe77f8a192f602524df59098a814ee63bde996946134850821fca4ce20125e6252de2015f040f9149597bbcccdf631849fa7c71a29e1aa308c868ae77bb23f56ea854c2e2a106532fb6efab781279a2cb49658b2b3c74d2c5f0ae70127ffa1b3af5acb026993826ce6d67b125d6ebdb93a4730a486459c8839bf8b233d54ccc306ad2ca08e2e08a89fba22434649d2bf54625bcab15960d304515ca1bd2125f8a4a06922a93ae01b5be8e39a3a34dea3ec9295f99f9e08ab548afe71139a9c40df75a28f10446285c1e8540801bd02f9b27b1387d3f07a99f41e8bf7c60b6026713bd130835e29348be757457c67f0bad08d9c5e2b1f9fc96f1bae61f31847dee2e67ab44be24dd8ada343c2017e199a97a322958a430919877f7e23a375638905025d5ba82518f5abb0312ba7cef86ad91af5984a1fe2b2eb10943136956fdd8816549c5923ba0e16c25d4fbc12ac22ed5bb2ea53096ae09817414865d49a810c9d7af8411a0b944e28bcf1a17eb2fc0b7defc34237a36c328cd276c29882af902820d68a4f08da5998953d06bdb4729f8f7984c5770bd845ce173a6f1d2b9f5c4ec9d053430514e73579e6fb621ccfdeb751870104db35a24897ae90be786cf4392bb0cb758d4720f87c94543d4d7daf75b9c5c8bf0e29910dcd489b7c03587037a1aad8321fe87e00ed6e05826ef550870e33f08b8ad4340287ef59335112a52a6d61fec3c60946e6f27c4f2070b8f355b83ec802dbad37b47099a5478f38fa7a0411b83da07bbc616048c4be200649db55cc5167ff1561060ee84345abb1734760e536580e8ad3303bfc893204eb9aefaee97468f19c2d99a026a153bd4225ae6edfae28040054e0a53fb46bed9a1ddf447842d22582d981abcd7e4617c3016f55a42e405140fdfcd6cf08c38b8b70c45201d4f6e912b3d41ec25773514aed5687643697a91e09308821be997e51ce0f8cead99f8fa73876be945ae4a2fa2df5bb2872433eda6ac480f77bed2bfcfe95c2aea551075fd9909ee4eec2e727527178c2e2721423008c69c94071b5012968e8a70ae91dcc5cf808a859dc881133f66d62559891904a88b76a10067e989cd9131365e873de257a92bbf1875802670c1aba0ef30ccec53df674633b2e77e658a0cb6cf58e4b41f8031d6aa73f5b9750f18ef2870a42fc6b99e5874a2f756d72404bd9b5f9a0a08dbfe9640f0305e6a0dd52042e7e83293bdaf94387eb3dc429ee6a9685b800fa40aa0743c037fc1807a2221f093b408838c7e8474ebee063eca1393d09b44ef41338e4cd5d77eb7e3d747990ad17387e22b1b639581d7ce3f298acccddb4ed1d017c40958132f590b62fe24edea512d5c0229e0ff511d61d98973b93b5ce5de3d349e913e2c9b7991c778e02cf90dada22177600740430567132cba3acf058d1ec9ae1e891697a98a52b198cf5d9e7484f300351c11a94307affa179d4dc53aa7bacdc0635b3259516f18c8a2af6a9759a258f052f333a51b8484276d366a3a8bc6b11118cbd83c8b18c1457f1135488e9d8081dc2dc7041561351fff13f33db70f48cb06ac94ce336bc16c69f43b068b8cf2695569d3c406d38b266a12b9adeba2038b8377a9515cd5d172440f45c702f48ec7597ee053997f7ee9067c8f97c138ecc27880406e99e09d0fd13746b80b8196fe3467e4a66d2891c29b0c98bf86d4e468d00056f46e3c2aea56e5ae03fd725adf8383d97c25af85e367527cffbc43f13bbecff80c45ab06e901576e3023d15664851f91e7eab39a922e179813739681a63828172786c50a6b68d72fd2f6e893842e33c5bdd741d375c56569a964d5b2e79fe4e8b498c3c3360ee762a6d83411926514bb56a043141156ea835087260bce2fe32e6185ae7903158666ea60be19b5e467015c2df89fcaecf22d8e2c31748175a5c592d89e1467f17e7d0bc97e29445491e38c470f8b2b2a5da1098a0d86af5095c4d4842aeeddc10069dd0bd100a7303f4a753c121fe730323348eba71a36df1fca91dc2a382f18da920ab58783f19ebb94124aabb0d01c9c14666c8cc6cd64103e16a1172971f97ffc5039455d8c24d0e41e509bf210d98312b9ecaed49bcdac2c96ec7c045635ec94e48962ea77e6452d58ec94130a8b262cc561112e40840d59a7b8c217a2ae703eef5ed5b095d1d864f3e6ced840a9dc0e706a43a68219d280101a3b13cdcf513879e21759f7fd638e0a9e40ea8a0067c5fdb1bc12d5d80b0aba32f2889b46dcde876030057c2f74140c99f4f44407433b656e90a8f41ef503d618cce2e719d521ef18f19c6a630fca391ed5613e200c7eebe9308013995211900934793282447845d722ddc3d43d904be7348351ae82fa7607e6a1dbb0a8f8f8788a6065715c2073fa5b88d0a74bb64a408abd5666480c37b2d569048fc0e9d317cf901987a11a225868536414f545b25ad3a0483721a38972565ff3cb949b50a3abc0729a3e5a0f8553ac890baa5341b7abe5561c5e971c3b52e24c14824a429378963f9596e728f50edac409e4c13733776b69e454b8f6e6d63af7a54c72d177f4a5f6a658e6ed53599a3c0bb825bbd0bb334f76824c98c708205e3a27a4d16e0078605e4d84d7cca8fa387ac3ee7e70e9b372dd83f37805109d5b0e205fe8eca0dca262f27b385a55a7b56bfa2db2503128c92a5409be581e2ec2617819482ae287789acf904b45a86500456a593d160cc7cd78bfc007580f2365c511b5f9cc03cb5e4e0fd20ddb63eb4ca870dd85e36dd518d1ae04a82c6b1884e140502b9d7faaaafeeae975e7d2a5599e8404ee50394bc3353a63d54e90168f1825b0bf73024be813b8acf82ae16734b47f467996b8ccd668149fb4ef6a89d4ecda43a0b0c80dba6ae6a3a3b8a5e9a7ad7c9f16ce27d2462297b32e997a7df02e623974c49c0394785b353857d9f52d0b7dacce4938bdd9fa72675d562de15dc2849800402844b9033105b5d7156d297642554e8b62ce010c16354950fce03b9c092d8936758be08259cc66a943649e29f381627d51494dd9aa6b00e9727a6344a309b89bfd5f806daa366e6eab1dd7883105b26a09208b3f75358aba21e06b2fd11753f86cc88154c2889d090038ba7da78261924181283f5cc190041971b9de32119d9750e11a7bf861cedc77421c98d641948dadc2fee774b6f6ce040a167ca5b842927a91137ef28cb847ec8f5b2e9882ebf87e5f05e04e80b0c20ac087447c3cfe67c5da2b48fba987d29e19051ab7b6b4379bb0713a11cafb5433ebd24bcbb730ae4c2aed09caeae15206fc90853c3820825e1a2eff2de859a11aea95ecc3bbc461d3d11f375ce262e5f8407c6fe1c0985d746d5d55bc7eb7dd0f2e1702beef0da07d6165d149e30572509e6385f5c6a86b4939ba37f64c2d8e09766b0b61f77e757f600896afe4ca53016d34dc418ac46795250a0819a09200cb8bc7d56663ed2d1fac0d351ff9258d6baecee52571442d2254cadae8e9e8a94c4a431a4fbb684ed5148535299a51877d5aba8a15d296c8d63990313c65235cfe1b8340d7ca551db90a42d956e914bb767df20882c42056c69261008f2bf5e586d0c766ee81427780823ca34e2f2d06b66552512240083e2190c08d8a58fd6fda6fe0ead5141a5b6e15e99710f430960a4fb0b5c0c16f2d1458173565d50a0639f3b0098d70b95436487e0dcb9b29a8224498aa2f5b740b0d2343663cf21b8a86ac8f24668fd88f1dba73613771af2d1ae1291602bd2b084bb19035cd8e0c2c5b688635c7871ba1c3a8ee50333a1eabb5a5e1d6c570bce5b61870e1ccd705ab314cad464928cc76c11cf334f89800239b63fa1f44f72cb7968503ebf3e38b45ffb7b38868e2c2bded7fd20f4bcb401d62c73d189292767c0c93f6296caf08d166486035fea98b54af7c9229d1f03be4165c33b23e2e021be9354c9bd1a2c2d7392524d02120fa4dfea24a6e1d6a87f2ec0d3fbf26b31ee40cea15a0f1a53b986bec78c5977e1c73881b9aea24a239dc5952fd605fcb850ff7130b57b813ff98140835455ebc0a586b33470a03e6deaa98304043ac4c93a5ed55ed7efa24d6a7fb43199ef8ae2555d966a7f33e6c915b0394c07916ee042c542d712283ca4ccb6374bde0aaff924e8d26b25ddf4fa5be9affe9ad4dd31b8989e4e831986669ac2fe78aa0e0e350877f270d5b5cdaa06b20edca9a7fe838b2109e7913035969a1bf0805ca8108ac288b6025b004555f9319506858df50d99d20e68f2d5412f98e21032a97a7bde9f65998a52922e1ea29e43f0d93d0fc290b143d061eb7e91d94f6e718d84ed143ce7cf4a4aaaa6c7b0efb8465cc49acef252997d60ee71409b853c14b5599074e1dff7580b42693b55a250246dc4b655e6d298907a2dea43dbdaef96d7b5a96a0628a8270ec05f93c2b92bb078d9ae8504dfaa0b5f10ea9f80fcb1977d1edfd7aafae57f813ad0dc6c522dd91a86858fded3167fca20ce2149d71509b92f783812752d9a3f4885a00461fec228480c00eed6574e68be2ce5e78dfbefdd39be7c3017ae767623bc2812d0c31fe9d3642f87a3ce995ac42dc8e71f346480e9b07b6064ec5a496d5dc7fa5cbf610b52aea90463a3e61bce091da162c4d337c9faf9d4148ae0a3f3fee7e8c7c4c37cba0f8b167a227258c40ba18b07302505fa3234e4afc7f23c58fd0073f1ed47308ed6ef60276255541e58f04070f1b189d2cf7f1f64f1e07a0c9f9ffada3df5111e1115066fb94aca842cb6a541b9b1e8b123c9063668ec4c3698457eae1d908f7240d9ed6ae055d27ff2db88ade33a3570683ab9b43e6fa3c75389ae2b4c5e2bc2335c06059cc622ce7a25cab732c25c7d87394ce6f7b470bf8403f832ab29b8d26a5f3cac74910ba139eb85420787f9071d9c0c192634ac00bf42cfc370c46344d703a8a50ba627fac6f630bd01e0656dfe12a539d629cff1289d94676d64d205a4c23f1258c917b5fad6b82c590574202e5a34e7e7b20e9add1affcca9ac2933ae01b41583f272f1d60cc536d7fa7387e0927a01459234b132d4f57bf768b554d0c84fcea0a42de0e59a8ca60a80ca67a60aa8378aafda6baf5636d3ba0eae190d8052bdf7175e9f0860617f0d5bd19314b2d8bfff58186612fad68d877810ce3b0cb3d6821130e06bf854cfa580eece7e35fd1786fe4fb806e74247b528f60b782624cca8abd816913d9a8edec2e94f7f04e2b20ced64616ea4476898864b08bec6ca0332894aa60e82fc5ae5ca2544db1721d7800df13d158d5a36c37b9c9430b43a2715b2ba1ddfba9d5d96f1ce8f14f108dc99aa16efa70c3d3e942ca57c985a11919178d0e8b77cd546701bbfeef5fc97a0870a6c9038d3054ca03cb8c4f120258d2d23874978d4c72b23adb700cc4d83c23c4a71f30254e99fc5b6099f26b2b81f7d8e445fcdad8497be052e31febb4f619ca5bfc439c240056177478e943e178163da703052b739d3b0f462bd2731e036c254000446bb472735e70849490e49ce500a3bfa7a6f42d4b0f5d9c73e5cf2d0630afb1cbe4a16f29b9413244557d7d32681e844d6c2412c8da46892211847f3ccd8e95455d572826cbd03058db724b01bf5efbb538ca54df4d0c772a96ff60873781bab0e5497d5fc9b94a4c5b83c5af232121fa244025318d0660f044d544d6b34b62dcd8f99dd9c00e373fe7194b1b9ab630b64c4c93ba98307135070bc3dfb3855009752a890f0074301370ee28b14df94badb5356ac68081615c2bc62c82ad3cb327b372e081d9ee649e51565c3e3755e44acd6ed21a3ca262c3f6e7e7c8d89d88b7c09b060d485f0f67db551388ff0eb05e400306f1982d935731207aa69d30a3ab41603398685f6503f6703b69858a6c280ea12686a6e1e76fbe0c20b1254724b5a2273e04e2c6ff286ab5f54c91717c334cd46e70caf09f07da6730b2e9af6ced3503d791a5800d1dc5244a148d10dce761efe6a3e74356026a288b39df6dd24062623d0ec1aaba113f926cc565c676d036e64fe78170f4a7e2719cd499d0301f8570620036d2fadde038c3db288b39ba6639cb5508d99c54fb8eb07847cb288d5bf961c74db4d00dd6b2555154591f1cb833bdc5473104a062bc519d418dc993b5adea51a619482d6eb1c6d73b63fa13a5a3e8a71b4c73194812d9e8ec00ea33e439b806e85821446dec4949bc918b68a1ca18eecfc0cc23519ec40c62ae12dcbb43b7c877359d93f4c426c28fa20bdfeccc239d04e2d35eb12e1f8bd9829d6b5548b1c8cd96293e9bc7cbc021cb8c14b375c5b116c3c55d87c45209a34b28d868461d0943b75a54373d883c6883b6dc7a7a191ec7323eb2df74eb2dbe42f7cb15e681f770aa49a99779b72b735703d9a9a7a169b9f75972e4dedca7cde43a335acfb03ff4e1827a14d2b2055a5b19ca5b3ae82a7734971cc061690c2433cacbcfd399b4da88598ac8d0cb6242917d5e0a08192960ee680e758c6858f6cce1a1a49931c6c25d4c134454cf81354c11fcce791ff796b0fa894b4ed8b37af957161a87f3f033ad139f357ccecbd900b44f122d467cc9f0b023c0108957f53b020c47ec29c9a2ea820d010f3fe49b406a6a05f4b21d6fa6148d509b9c0419f31a31b745cc8f3d7e6ac2d7eb72dd2c2a7891835a153750cdca9ec9862c0a0aaabdef017d44460302c2aad21a97e02d6e7c797954163143c7af60f52ad8a4fd53e5092524fa8901403056d3014baa8d3e2ea4e5c79b21d7c6913287fe6b865f2690de1c9fbcec7b8c4abf812986ef79c2303fd2246120770098dd8f0fedea5a1af9c313749e6ea5dfdf050d314af5458f445b552c3d0082f06de2d6ff424e5062795f29dd0b022f095e74e6a3deacf743f77a2441936ecaef49d5c47b0075cff4401e4ee9a8847367d2151528dcf2824327c445dd9edd9e84f7e664aa260800ffd3f9fa014e1759d504702321c980ee5c36b3d9260e0594684f0e1fbfa04588f66d4529d1d9df170a3044d91ff5141001b67cdc6931b9de5a5a889d51e4ae094d17efbaf32f4f4910b86e0536fea8f430a75d6922d7a260134b69cc30f88b2564b99a8c532c835f6d32b2ca6ac8009161fa3623690d44e185f40a98e719191905136aad91ca296acca2ca08bf1ae414e14751f2b5861d6b2ac5eed27848f8c8e095cba2795cf67cc635e02e8c05e3a4e965c1b54e3917558a4fef5347de57888860d345fdf90e856918b3860df44fcae3fa093e7f0bee6bf1bf2ac757d6b58eefde46162b815d54b09a81a9e82e84e0507a5409434f4c2013868b2ef044c24fbc084fe197a40779deb54b3a8d536df5de6c5d93db88e68547890c3f90555987329d176a5fe0d9410c9341a5a6e0f1e7f79c7ee0fa23f6c229bb10289b3804d124328f7f75b0084b7dba2ce7668c61cb4d21e38bc69c64e0ea869749f8202db8824fd20238f6c507830335c9910d7d9a1cd65c6b92dcf717987f30b430c9b86dfd35ed56b2e92fde4b200a18e1bd4766d30a9d029af66da6f061253b0f770706299e0c5a1eb94fc87cad3d41b7d0ebefd9afc5fcf424e6a933314e044b1641e2e22e2a8902f808af2c2a87203e8d41ca218b8c81401852532817a80c4655c40c3e129e40d4e2d977dba52597f4e2b49d3a9ca7d7b163e0869fb8cd18099d55f2f73f76af04a6f529b7d6d95a1a442d52c8e7c0d650db6681433eb21c28e8afe3cc49dc7ebe8388cf21a33c89ff2f6bec265480f10e8a422bd619efac3f49ea4cf1d329526d6f177a0e1f273bfefd0540b329160567ca8c2d8c4928d3b9f0cda96ed77994834ece298e2f303280b7703cef1cfe87c639eef237c8b634ded8adebabac42a5b9a237a3b30e502c4362a1df61863a2c8eab973118a951f45b3ac880580cacb60dae4aaef66e723f85222b0446703be8a8b208832fad7c5a9a932e0b531bac0f66b97e564f24ab1e08b318267c2c739fee04938a2ef15ca6477e367aaa764df7856b6d7753648a6bc5641304596193eae07d25d9a4b1a80f8762a6bfa5a431aa835eaddeb6d48589b645a54ce92f969d85e86135a4179841127017f46f7f1d16667afabaddd7db0e79140f36e61013a31dc77e8184a114a1b171d88a1f0883458dd9ab06c28ffde4b0503bf59c321df095bdf216fe31328adabd7e958580ae129125a7162fc2991b8480a9fc9c7f10637b093bbf0800aefb597d22b94d5563bea9b04f33db8c724e6429d5824d5b437947bad43766249ba61f7e47af53cc786cd6291bf2533f0dde01cc5837d6b4c7de6277d471ded92fd9e040b9633b0090e143b767dc3376d797aaa0e656171a89f57fed2c1df667b3501b7f8e9df9e933ac837fd3aa3d9505734076ab943a45b6c4f00bcc446b01bd15f77881fee893cd442282cef605f7845989ea25b0277181f1ca8f392a0c9c229e30744ff79caf2ebebaae197a2c5f3be58741f2ac6a298c14d7c5e844d07aef46d80b792c9f3375fa60eef8f045dc5f223e1c3e406e95c48a50502f5691d8be1cece50d4c63846220a88aef413179445c3d0a2b783a985f416d40eaf744f43c4b79495fdccd59a733ae3d2a189c54070b3fd4a0bbda396c1fce5a6ede4e0eb0a94f3bbbe37330a7c1c4f79c6d769933b2f2b164fe43edb4984aa985923bd2d541f4598c29f9eb3ef670afaeced996ac0e06e26ec7fd6221f7bbb363f4bd8c4b4345857f467c3a62dda7b31f19658d5e17390027744fd032e9055309c02c30f115a71287618a383b04701d9a6451b08f4d62877e7a88c84cd22bb305288d45956f0812b113420540ce1e8082840c66f29eec32ac1ec0e3953e5b2047a5db5e0f3a270611754d3ad782ff131bcf3fd46c6428352b6abd9c35f6c1558cc0887bb4182ecc05b8ae0b344dea44a3ab641d259b759b890474809a962112cf56ee4980f3694bf47d5c3056834c40c40f896c1f687577d45a43cb565113be6c691b518d6763e806a53df22a63d194331fdd061fde41fc3f6d431170692e857aa0aa6c41e1c815b6f8b66ac642e9d576c53c1e97c856c522b7d4c7efef718be179f80853a0661e2cfdc8e72646d4c9c3c231de05c21350400fdffff0bb33e6f51e51d890f80f446bce5c2b1cb23b600f44de3b8086782cb1a2dcf19861059e846faf9fa964fdbce1130c3d4a5e4706c695e32bbc02a3bffeb569c09855b83dc77f9a4d0c0a13bc23dece30dd8dd7d09097d612d152ad735f8951b2fd0d81af878d540302237e91c1fac738e17e3f348dfaf680500c0c91bd5b3ab795d1129b9f90664651e679c5290eafbca86fcc600f94652da211cb590a07e4a05989806cb86bf25080ae10b0fbc376b6ba818d27fb1dd9cc8415948f78c98e170a166002d9bb9da663182aecd6d83fb3f78547b5fa02a68371f7068d69ce6b996d9331fc0724f579b072bff7d7f4ce43fdd4784350205f3f82a7972e4448cd2d4e01f9e02d8d36129d945c86799d1ae11b1479cb4609a6dbf97794be1c190c3d847f417684cdac040f0e785dad8dcfc61f43974797d2c65626e8c3b3e2b3cdfb189858fc9b5456793ff0befe46292be6b1419f08dfc03408efe725b78f28527aaf027051f4685b05e88209ce4615f736f31cc8aaf8de92ef2ea8b95e513cb3eb39c2bebd6ec50da6c9090d53b4657e6dd9d70d25d18dc76d029bb34a5fdcf719bb7beaaaeea55f104613cce403ec8d85601ec4fe9db8f4b6504691144819012cf64c9899025e0cd02122854e5107ba8210fa5f651e854a9e011d9437cf8011b16e6e36724513ee5246dbea0a9d11ee06ed222361378d48da3503c75d9058e165a809d4c056a65799cc52e329ce353565c4223796c578c7d124d1d95cf08c9520b3eea2864ae1387a428f556a3a5f1f452aaa124d463d55b5ad2637576c2691594860efc6f3e41154582461b5bbff1f619b13596b60ed4b7caf584639093fb3d8245e933b7e31ae73a3180b8d495fb32c0fb930b7f0e5a46525f290883112beb93cd1236d02245ade41f1a7497974536252ee2d2f32b2ec5c63507ed80c535683a8cb2d7c2af2520690f57cf895ae739d4f37602c8015ddae31c4a33b2276321ba02e5ca3d005fd73155f1c4dc242f3292f0e72f89f3809e043bb5409a76aab34823f64fc768778a4424a826d80646e0c12178e7140d2e1596021c7e116e1f0698403a711eef488daf5eb1441aee1492015d57530b7b133b6a2f5b04cc1b27a75f49e434a63414e2bd8e3158d428abca208a32877c403a4a9278baa219da406774b56689139afe6140b16596fe10ed858f356e746ccfb5704cde0d1e8e075c37ea0478008300c8fc3e3783fad334b0c5d72f1301329397bad89ca63e5c043c11491291efa49ae88f6780ffca6a250cfbf7df7c39cecb7936a16f80b00588eb0d700494f4aeb2a7c3e2c09380d3e220401076235385663c421a36a4f71261ecc89a0a049e1f8251bcaca1a6b2fbe43680de90567997e3e7f2839d38f340f17344a1234b0120fc983babc39f8a4db9f714f44864a19c8e319faaa67a3462622fb053deb44205c6708a1a5d8c4470aa1810e4245242fee29535e9360202cb46692f5033049f12243101275bd79b71158ba53f3a98678619ce286b243c348280503b0bf879dd3524973a3d94513cd2fe9a78c96878c3aeb236052b2b77f4f28247e041e786f947c692b1dfdd83a38666c2ffe201bd0df9ee16547e7c445e5081ec4cfd6185e25bfe76e268197314c95d9c3b14f4ccd01b1c853010a74bc782c461476ca80c63eae96d1e2bcce65c0866c22f54c4d1dab0f281ad780fca12370c9a5959c54a3fbf0a632250eaf75a8758b1a79a97aba3d7ddb840b75dad81475f420af28ff5e46c20aa1b3df6cb820c9a17f98f0925f850cee0a5894ff1640213ff2d217cea27c4584253393c72d7b7bd05302ddb03a382e260d8e5da8471cd6f0c694a67a36404781fc3a187def450588cd61c3db45121c18c725f2243a417504b51955cb5e3646ab2f1f9331d93d582e9b510f7a1e63d9645fb1cb620f1dd150e66f4624431ad1fc9bd40b1712dc53e63f5002f30861f24cc7d2466d550941896b0ff31487b8b878f737efc865c33b4b611c156ee0a90213090a1794717564bbed6dc2a5ec31c3dbd27bd987b7099a92e3fe0c139193e7abc6ee702ab8f8dba436395dd53aa760cff764aaae0d422c448500ef49fb162280cfed3d98b3afd91dd179f27198b01434ec817b5fa1f1002ce3502cc5a6b15e740972b73bee78534f9b18ca25dd1226c83852a4b9a6b211553a3450abde69f2c1a1a7cca3b13d6d7a65322ac3be9ec86ac408c7581634125f5418e9bf15de246f912b80c4a3c3da206f588a8a9bf1c787efdcb2973b33aceab343aa7f2608461b2a5389ffd20680e87df965799036823015fff3126fc85c08eb42823c97168cf47f702e831640ef1bbc46183b322de5d2e001615eb886379e78163296f1959b4583de94c191bc2772411677d159668e1c5fcd973b0f4338ebe1be37ebe5be1d0a3aa05bee2d3025520cf74a79e06803a6b2e70d171433eeb5f053539443fc115462101b8dc517e27eb15525af886640933b039d6bf599f69303e9d49158ed18538b1c6e099f1b81ebd24d99beef03b3e56e11a8b29afd35cbdaec0ee96e0a230094293b98dc27ba42f021b97381bf0eb38c26e5ac01100082b6cee587245085db1c3a004c0c1a6c90aaf458345970a6c786a785021dd22a890d6c649b625e125c241131623b71154f34462bfcffad508118421f43930a6d60078fe18ee6a04c905f3fe462fc8680ce74f5c414abc9d81d56801001ed1129d2c34a9c140bf1f56ac5b31491dc09d255f0f3f5ebc7bfc2747d1f16e19810c1dcb880376a91031fd5bba5521102e04665332e885f18521f426892743321cc8a88f0a8e859a937d4db30f33c0e5b93dc82c9b7d56aab0f888d6d1f525a13491ed2c4d4377d128409900a228bb2447a7fce35ee69395095cf1945382531e07c6ac0038263ffe8d9b27102cf899b69548d3161579ea7f4ef328e9eef3bd5ddc41aaf5611c6241d7a804904bc7cb85641170d375520460e40560b2913d1151d6302a146efdac21bb48a47ccca7f3852c4a1511e4ccde0772f899243f6f12486e6f38c517771f4e64bc6c4afe835e980552ab6d9a7ccf068381ff34bb11d66d802c36308f255852c6d1089708adc48cb21d1739ce9b86feb15cd2ae088e4a5e66ca8c0aaf152fa8dc5ba2be23e968e2178c41c93b229bd88c212a190167fe7d03e035a4f592615ec15ee616e7140900a3cc0260735c86cc74fb102d554e61f3bad75d3fdeb61a6aba5588ef4be975fd25ffd9ae8f6ab0828dd23c84f510977fd1d8452239a3af7a35041313c81247573059426bcfd51c3760c6775a36b35f9c2692d37c415fd6639b943a4fffa927b33bb20a72edd6b314285ded9d4ec6557a4c7e5a06978e25183b41859a779f78ed141b03521513ef6d78bbcd5ef660c635d21c40171431cbb567c87de48272f626103532cd1608a0b205d93c001f06a4f912bf95f7c453473131372c8ed31f1b84e49c41ff6ec943667a0da46b7a07401d4f87a22fa8a7adef507cbbdbc15f2092e77aca23a7617aaea51e7970beea8314a63827d1321ed895a39eaff4cb1594b759b84f4cf6eac8c1ccd01ab365848508cd2bba84f7dc4a12e0f92763e3839cb893c18c0ca69ce073f02ecab5617e2bd1a44ff58e04c0c02cf8445e85fd197084e72f26333a2ceb04ec17e5b75ff98221c1e4d68508fc28c5342b6061a1f0a33fb9ecdeaed70e183609bc945ad245a4bbd842541c5b4996b76bb71964ee8c2d2c498aaec0811581487bcc1f4ee8fcea0259081dfa7400ee41c714df622d8950101231f04a51651cdc7cd2152f63e0a6f35d67b57855f2b9c7e5f9c7a5110a1fbc0a09f20f8437aa357d802a42126b2fe6973a827b892047ebcd95dc2d5dadb4678f16059e81c59e4bda8d99c8412e5db9fda6e393e6f31e3c77ea06515cec4198cec6ffce25d1b42b11c83293523670938cb356c0c991ec8299989028bfa1726d36d7d7908ea92c92d07f8ce2c7937a5fe627b3b80d65ac7a32b9187ebcfa06baf4e66f3df097fa4210b70fc3c2d79352da279897864cf6236fb32ee5beb92bf465e010465404f4f0834f2321c7b33897dee0c04d281708046ebb6fd466ac4222951d11b4b863e20953a5d609da7a87872b61710b60ffa58ec41dfe7a6e9529468e71d434079493173c28afae6d1025216ba1ba83433d1ca3aec451b83bd2575534fdfb4ade905d9807e6427542473363d26838d09a5f94629efcdb2842e2465cf6f1c3b6f2e53ae436d911d251d6fe8d329a4ac65812d13446c87571f839b265cff92fc19dc21c50246c017228000cfb11962fc8feda94b094181a0b7296600ed222a22a3905bd7334ae2c472bafb84bc8e65e05a0ad2b8a4334fd192318ce6e375f663b8100a5ece21a39fd03e1723c856d828862b47d301d5516ac6726e6ff7956b28b36669edac153ef43897fe0878a0e8f7bde98a9036ac8f440f0e08d142154acd347b0b5cf789a4a8e9970381bd0c95b1cef1b190f07f4a0513249e29bf2f9d26bb1ecc9e9a58ff9245f3cfd97fbc7b6b9eb9316aea1a4072a5b89d5b05f8d1f1fdb130e3be036f0bc9a457653b7e21da4b3efec4f0d8320ad73c66f88299155ee2534b3e88429665154cc0827e752a2d1f49b8bb6390f7add1a36275cc22ee190e99b6b988b908156593f35dd57a9ea3d91c212bc08ac15fee19683e652218a09d459e1d92e126f4254ddaa70247837f17265e1cca2630e54cdc952eca2dc888852fe973cf6c2b84ac8dd30a4c8b71554c210290b333725900698410d57415465ba1e89b74b7fe127a3a559257b9cbf0e9e01c2a5084325427cecd7ec5048d923cb699887090679886a8f9d8d72b38492e35a1d311ca530fa93ccb4f24ae895c31dda14870769c6d4f2560397e6030928f8c76e22e3aacbab6bb20439d271b33a27b8bc3e3724229668784c593f612d8fead6e960d08eb1538b2d624d904dbcd54461d32fde0e11f8f2aa24b516b0c7d6eafc562801b33ea52fb5660d5a51430cdac723fad9579cc35c0feefdbd9159c1ec0076eb81d11c63b7649552ca5a664298de8d2505e0ecfddddad0ba7ed6f184d78ece2063d38e7e9f1cc0c6fdbe285226f6ca782da34f8270f8ad7277c7dfa13187c4e546b585d9f675f43828b6c1b6adfd31cd8b5c776567b5d03160a08211477e906ca3c682fdd629f077a920ba4748227f6362202bb6621685af65c63273de63662977f6e3889a5f08177776c19baa361b03af2ca916ac6cb900041a473c83fb4fee3e54418fafced79eca298090034892aad01090a05f20c370b15ac335a4f8a9bf15b212b9b1cf7318d89051ea2af420be1e8e092ad4c2e492c20a374fcf54e797e7ba6b4f164014769cceb58b2cc16322defbe3fc35fd1c35aad6777e81d75cb459d01e829cf48807199655fd600d8124d9c9c18183698621f80334937b8cf087cc1869d34b8b6cf1241cda4f9010a25746577561988d12d189fd4004524e2568c50e60f7c83f4a95d0532730c819d46a20893237ea936df69a51362eb0e0219be6f039149fb937b22f1122cdc137e62569fc624496b22954b764411931e2dcf04d301887080bb3dcee827ae00624c5ca4242ddd534ad45eb9da464255abfde67803e460467cdcc4d06d524af71e2b33f3768315e8498afc32063a3531305850a288605b752f3632a6294d3890bdb61921e758157ab8f05645fbffdf97ac781144b6671e7efbebf262672e3402b119df6c5c942595a3f2b7ee968c5fa3d2813aade7b89c12ca2a710da7d81894ae7a229e02a97f712ccdf7321d364808f1d70714d2620c62f5de29d53060093cd6fddc41a99206c41dbeb01be41af7cce53a931b7379307156523e887e9e6035c9c296ff331a04c1c43da8e0d670e8efa967e6bd34667405d76fd8711a4f66444f736c145fa0e68bc1889eef4e839c0687ded7964f1cf483bd1d867b4b10cfd4313d06b0c9092e230b8029121102046f3c97fb1423ba9273fa5183d88cda96a3080ed4ccc7c6811a42ab5f51961c0054793c2b1a2a8ba8c83b8d679d2881295cad00336d0995140c5160af7b1b276946bafcdb8b158cc1fa7347fad0b5866f23280b3c0acc1b595517c2e559f6442cbd113256503d871fdca6a4698d20d2bc000306ca8d9013e72ffaa8473a53003d3c6bb874e49b6e8fb4b2829e77dda4b484b8894205cdd9704611721126878fcf1c92132cfcd73a60ba2da4da5a97eadead48df9802405df48802e7fbcd1f51ba1d49a2a7cac8310b8500cd34e3c8db04178b33195640389a2309ba189cb263e21d1122439353cd5f412c52c36ab71256307f7c623fb2c40548d9e6c5811a2bf09121ff364ce9c4431488b59f7295547e18d372a026a0360f3a275484970122932c1e9c51fc1a6e4bc8a1eefb5b8cc44bbb5f76e2f5f7342edb364a62af98c022072a84de20af74e83401038cd5927856b5f20e78519563a5fffc3920ffc2e9417cf8ffd8bad17ef9fa74d7337adc7aa3ff58c878aa6993b2c7c86f1dcb55e0f7640bd384ba9b508235bb1798dadf7cc5d3065a5d49868c324984d16dc7051e66abca35ca7ed67a674fce2b93c339e753cb5de828d7aa41c20ca90a670d280eaae01118522e78fe9703a294b516ed0b0d0ce3f772b27ccfd34bfc8e77088ce69640fa77830d7617fdd668df44c55ea59bfbddc11931c22a24bfd1eba50b89e721421d0af9f5c2756147bbe1776f801725295ed814f516226412d1e047a9408ef45420935d8106624450e1184a388061c63a418800eb9f21cc97b87c68b2caab70574f762e2873809132871610dee58eb34342902e8f99fe483bff0029c187cea7262765dc69cd93047b1d8fd0565aaff880e63a0a89569054e6b31c786bd04d3c0635fa79562ac208e1004fa5fb3da5cc398fb04d61f049232861b45fbf61330d0d1dadffb73002e8ea9c907748c8838d9c4c24837f79c47cb9f83e5f89ff1f277443649c458bba527be9423c2ce8777ced5b5f38bad2ccb29a134edc379022d2e39d62e0801d65b96d8c5b93234d1a8b9304d05dbec8f9aa1d519c41bd836beee01e9b67acb3cfcead7b57398dd6041c472e8cf55fda2f0d4971081d3a710145fd7df109fe04bcecb3c80d7bfa3fb83b80bd76bcb332a4ce804000bff360ac5069399a15e1cb463db8467d1ddc1039d525a43df75c5649c18094344cb6087046a5bab4ec48a00ecdbca13ef2ce7cf31bc41b0b76f8d1c35a145445352c6a66af5ead69af0f1f81495e8b4d99f208dc03b7aa05c1631c1f85d1b3ff218e5f4d313062cdfd1623902465c48586106acb183a2d83453d09c94a2ce61ffd65287ab69d284091caa5cc1129758072eba8071e5306e932834032062d5f3e4773b7c1fe1923adaa052947301f1986d895500432f0b682f6787969f92a2932e4709af2063c07813d817dd4ae98dc7cf505f49e551aa9ccb0bcc7414e85e43e88889f3e861a204f7dd8275bebfd15f8b706b4f33c3f4745c72b2d381d8547d330cac4f052beca757f4c3f8b492d6ba1b8cffd0d8b8674a79d03f4353930c47cb9686e049a416139d0603ea7638d772caaf180d2235a0200305c727cd56e031fff193afa5fbee3d1b900da5b80ba0ff4b34ac0342f9e1ec0f12fbf9b1874679a8c6a203b174f12c0d198fd6a36cb9abb0477989df66ed319dd7f453e08b8152790d8cb39d95489b6884f7fad4cdca3e87f27c7e95ec10141d8bba1afcf48cfdf6b8953c0ca459c531a7c13cfeb327abca4d2e0581f9d7be2c73ac7a6a2503acdb4ed40ba4db59e983e55aba8b4943d16104ee98544d1f1d91babeac4b7fab69db8f5c7ffae84f97a00cb4e50d02a086d5e091b48c48ab0929f569659d8c3f7f5a7ebf8dd5511e469cb4f877b804fc7ebc504aeecfbd3efee2c8b7a7ac1c4ce8cd7ce401b19773287a649112aef25ac95c2322057d2f26e4a03991e669aeb623e6b0e0de84a48a34f25e9d781ee221a626a0683e32a948385e1c37d9d8650246e80688a3432700e959ee748147b2595d4c37067dac072790e9402eced78c6cea0462f7e9c0b372badb850b9159b24b38d99548fa76ab93ba0b6f7fef27fd789b1465501926e0d3448af91f6d43cbaa26546b51163a49d63137abb60af6f678b87b8b13c1a4c825a4474bfb5ae006ac5c334b0a2e7fa92f1c48b74a146296561ce9c7da085ed99c9ec810f0fe52ec7e10ad3738900272efa68efa3cfc65957500f0d8aaa2d01457f8b02faa1f6eac59b2341a20728b26f52dedf3aa7525131e86df5ab449390ea945eeda3367a5c6832ab05a722243c5db6eb3e006778597b72425416510b6e0cdccacc4c9c0d511ff0be6d936debb899e441e5084974fa198161cf5e1b5ad8a490a44a387e6c341472b6ce54c4e12c174f224fb6c3593b92d15351b96c8df27c68bdff4d602b70b8f2550c1df6651758f2387fbaf5564f533334ce77c1bfa594367d6538e17a228032e1b2302cae0d570876de13bce74da3c1e467b19036c0b8c4ef1d6cccef17e34c3993a656a42f854ba1acf3315d62f6dd73268cff797217e86beff6fe055998b5f00deaa9720fc98447ac5a3a0e0cc1e85b56a01b3933d1bf950e5f50d28f702a481f72a911d984b682c4b39a4967584eb8166ac82a09026a900d8f9d94543634b91b483edfbb3ab47919abb9bdf400d2f1f94807f852a8a7bf7a8139dd869b1f3d814a16d0a8db4c5bc8161c66bfd592756a7761fc06e87d1c4b36c502c9f8f6bee861fd7fcaf9af825564fa3914013654857fa2abb94b96c20aa50219b06b78964587e69ec20c310d8c8056624e860bea016fc2b214ccb400b8ef2bf262895e15fc4cee7c9f5a5c46781392bc16424aad3b7190808e0733b72234670271de1717d029e7c23ba98a0107663cbe8960ea49ac5e97b03a165cdc176ecd471249fec85ac1caf9df74750e291e70420393b2b62b76e647a5ccd51e0262ab3169165dcc2466d740f2daed7ae97b091bc4705d1a831a434a264378ecc50e5fabb7303fc52336e32b8d569c541b2578978b91374c1a40166808230d6c397a22a85db3367315b30ff5a23e749ce6142054b35c5ad59a0d0847afb6d019a9d8c6380d31f1478bb90c62ea0c62eae23c09ee30792d767a6f56d30327cac70c5bdc1be8355d229117e7218f298049917810e145e8c9c0c46ca91b6f618bdb66712824e80252f733871c6a46378d11482008aa3b3f615514cc235ebb9b6e5bb3e22a68e292b3b017298103b9011fcfbe1c2010ed591f58c440c265014fe6bc93c4c288417185b3b992fa794231b0d1cb61b702c41ff5f84c7466c0641693eb2a10639098454031ec773fa394c1950a7afb05bd4393c7d56328338e503584fe2481b96c8e39bc29ed117231573ffd914ad8fb0807fe6b00665ae2fd4b2f8e4c7e7f9ac0203f9d5114443b6acf7e82959289b7d617aa1d78a7948ef905a2fd07b7f875b87b0631eee7c78651a2210e5d00410cb5c657b6999349d26d744f00179c71cfb48408c92c6ac03b20efe0bb84813710a785a88599b1ec8e9bf967f37f172efb09ca06bace1bddf5add55f6ad2e0716b4f5da1c16da6b2dd6236d7bffdf858027629086fc9d04f13fc202e23bdb53240b97bcd36894f10dbb7192dab209865591368edf3838e3ed0d917bf2372254c371d78b069985a2cebf2c7c8f7b77521dbc502d31c3585a6db7573ea49aded431759e7c6d8addcce819bc16b60ca50050c34f19c270b1cf127367b3ea4a584ed7d8c45b65b760d26780caf36deb5ec7d90ae1924548d3f1d755d0b58128395eead58bae640a33602a8478c501dc2e552ccb51e94a4dd7e8b4f1dd990143107f3c45e017af867b9f71c91dd2704ae663bfb2729419f03e17f1122e54e08f7d94f60ebcf7376521100c5f2a9ea41a7356316b922faa150e0e3f9353c0229455a9038946633c50327c92c72893270e47ec812647fbb1491e1a6f1b9f1c5ee903a491d4baed0b1dd5b782de56ad4503a8727f78b454dd6b7c27d829ffc5454f094e17c0e39e64233ff4e2de57f4ada7442b32ab421263d9eac72a0f51b912f867e11b694fdb2b2ea1ef6cf2938132726ab718339959c22be16edad268ccc8997ef014b5aee28058921c1daf452915f5fb9846786ebd7097d32f19fa5ff77c19a296dec8c8919f2b84e951e9739ec2afd25ca7a3dd4a3b0109826f48a8a8b7ea12286253e1ba94f3cac07a26a5535208344a2692aa871a850f753e08122b11c8254f5a5b44665154645c5a0fec51519385a849eb230d7e065e01c92a8199e903b72f7840c163d31c88f9acfd2926f5f87f72f964a1e3cbf84d6e4d4d3289b0f356b793658e2c9ea3b0bc79cebcf765d38ce301980327ba5f8a1b507543c1587520446fa32860b09eee552015a4aced03f7ee98d24d8cf78ff895a355a8cbeeed01fee29cc35d2a1b24f0204d7842420266720aa3093aabc9c6d83dea93ec9137f1e2e2b4ff6c593d9acbada32f22f57f43e80a368e2cf73bf41fdad935dc133aa38134c69e796ffc7507fd23bcba21a17e2c83c8fcaf6e10c8ff3117f9e12d8176f35efb76ddfd3dda399314447e79b1b0c6a0c8b50eeaf5daaf0911f408fc8ae2ca22bb725e36bea76647bcdef646524c85b24a223dfd75e1b30ccb189554d32029cb119feb62d652375f76c60545b7258d049e435546deac526f285799e4884ebc16e9b842cd34c659723582a0950e8de7e3a4e008c2116d749af68b83a9c0f55c9a98d7f93a229aee6d03927595fd0e091355b2a1af7c9bc2d391bd256f5310b950d5a6086f45cd2391ae9c5e26957b7ecc3ea4bb64a4d109059ab2b3141a9959dbefc76880d18e78110e0936a2d2b58689de6744b970de4eef9fbe878cfa29919ff8b8a57f8265c20d0b512c32a288b51380c6e529a0c9ddb9158d49c26f6877ddedadbc2cda1ada0d099934b7925e4b4df7eeca51710ab4b17a7ef4a6e6aaffa7ad4c8f70223a978765ef28117917342d73f303954f702caf87968604010b2a20ea53a02ad4d86ac8827d663d67a610aa469b876d11705d1142d635726fd12ca91ba22b2f65ee88bc3eead9aece1e79c4cb123300fccdf5c4683ae1f5cd753fb8143151fc40f1367d156344860869a8a288decbdf7de7b4b29a54c01be06ab0609062ae54e2997744a2ae56efb8c25ed5ad3b121624972483828375b9398125419a3d168f279327042a5dcae2d640e976f216fdcdd915bd4b2ff51cbf46b9335a335d488871c6cd06f0250def7bdd329e7af5acad9f659232816a3634939eb429212c3651d7b12b3f92b568b295162d767cd688d5892d810ac5957c668349ae4f39494fa61874a392b9464f558f2185957bd53a2200a026485a09269cbd64555a30beb4b9ae787bf92fce5c2e259c183f60e16be338557b18d643c10e560c3ae9fa4da678c568bd174cc665d279e1ccfca0a6943c672216dc8588fb7a865ac0fb5cca4652ffac7fbb8f83b85d600b5a035f5c3a04b85e78429fc59f77993ac4ef8c3baaa4eb8c45df5439a75d16a4e68abe6109e132a51a33af34d0a6f2e4e493232d0cf45f318f11ce159d13c45feaa0f6a416bf0f0e03529f0e8b0836a0e4f0e6e561e23bb3e086ab173d23be10fabb33385bb9638d2a6599d9d18b84b891d28bb7edd71819be55913c404f22ba87850d875078b5ddff463dfa17dc668e1ad54eee44e49326cfeaaa68da54f228aae461921d92b4f49ac92666d3b6a99cbc3e818cdc69ae8984dc76ed6555d1e458250b3bf32568bb1205b1788b33a2a3066a563644ad22b4d5b355d5ebce81b14a4652e3fba942e74b62e1717258bb5cab77361b156ab1569c37c629ab69797163a9719f0f5b978faba68f719f50d0ada2f5a8696bdbcd8b2c8678c2663c768d51cb23a1f089db11844bbc668bbbe68169614eeb0f7383ff64e49272d50a48d52ee9454ca5d7c4b39b3c32b2b2a2a2a279b0df349354f492ce43346b3edfa315bcd394f49bbd294d49c1b6b724aba1e80964a7d55721f1742e45aea015a10ddba5b2fcf1e5ba89ab4f43799ea5b9f7a2d406bbd2289da0a4423538835dc0a37b0f6c457d302851a9e917dce38c1872152182aca9d80033e4d9086a080480c52923022851a3f76ad827ea00882a0d7596c9a998ab6ffdd825e7727dd0ca74248bbf7deeb55c9c5d8886b0405c2ad4ef5e9321522ec2144c1125c0eda819fbdb6f33c12d4c0c43edd1684ad86230dbc74ecb841fe82003b904523600b35e010a5862110d4c001a11a7c844d327905120d4834acc085165622a3927828e4d10478ca08f6e9b6246880e1abd1b0011966d8c28a2c56b14fb785413268035b0e5b8e1d361d3bf0cd66038db0e5c8b1c3a66307b8c4115b0e1b0750ab3f9247189c6006187828f082ace0410104dc802d8a2d872db4c187016f06bc021b08a5a3d93e266cb61c3a7690803be1707474747474c48409931f98fcc08489576b5bb843ad1cfc64e2c3a87522c07657ae54b2f061d45438d5759f8a00b3416e39fe49fe09fe19fe29fed9bd38d29152b2547a52d7ed254af7a8e916c1717c702ca9287e2876def7bde7b4c7da12cc4d6dd0d25aeb84db6cf55a8cf1fdbcfab0c3430b4b0831840a470491c08214178678008a253a28c18dc2d6e3c920c2ed20899c1622387283911c30f18128462240e1e88885871996b833182145eb8917300060ba9c40427a1d44d156530b8a4c1f9743320982b9b1636ca2d59ec076de87546f37e61315896e2948d535c5d2327e3d08a97ac82e54564ed45e1c63db120bf9cb3f37415f56acfab8eb3ad1dafb27feae8379f33ced77317fef16f578cb212098bb7eb6ac5766c432adb14529b6d6ddddad75b76e63c46839e13507860af00ba77a4821c9b4d2982ed7581343c6cccceb05a5196d6a60f6451eba193f9a4292a90763c5194b410290a3334367868c18391a632c56434687b1b5d65a8cadc5168b3546ec6edd7e309682086074db76182a68d1a068678707f3f44c00a77ac03d3d328a544beb8877b4a6987eadb5d65a6badb5aa8af1b5d65a6bedf781608a29ac25836a77c67c15be560583550b05d529415365339b5298d9ea986636a530cd19330b73fc8ebe3b83f3a6699afeaa262ae719538a79dd34cd2e08ad0d79cc7a4c6a1ebb5e111df66b7d075b0dcdf61fa9caeac02c2c8a4c69b52fb7225b1172cdc578a6a6769dade962ac544b5438301accf5d59c1a7c034b4a898b7ff0fc7daf5205c1350103961414dc799e0ab5ae096e226b416931d15ac25edc79b49250c9eb5425217fd592504908862ff7e5721488182e73e85e233e6e434b4091393a3a3a3ac2339c6f3e9a21b947f728e7fc04a51477b72bf1077b24e80c7fd51933404029b5d70e913932e7312773ac4d92390f4839927254441145a8a41c91e076b317fb6015ce0787335283548c689153360af27d1e160a874839326272ab0de1213c84878c82d068349ad1162296859828045b3298e5868e8e8e8e8e8e8e8af822bec75ff53f803b7c6f4c86bc211c08e23ce64f6e56ca910aacbd9dbfa8942312802329869fe70dd199bfeaac88b108318c92e99f013cd017f7b77d9b94525a29a595e2c0c9910f44d888820062ab95521c88107aa1efeeee9edaee64491387d97810edbfd4f2f3f97a68751a12b65a1a1d66acfdcfe5031af24981cdc33ef862fd3e120420198300961aa86eefa98ebbbb243657396142c5c66d2c746bce22ac34f5cc10d96ea06edbdbe16ce640263d56daf5511f6c8609b3d6856cbf8ab0b2ed77268e5bffc325c0ea98a52a49bee2c73e4df0c80db513c16eb1c5165bd87a2d0902dd55d3ce7636308e2744f4e209215c6be381bfa8add5c65ff4c55bdab84937eee877bead8640dd98e213e87e0961d712effd0561dc61dce1bfd83fec73a5c8160771977d1b700edb621eec90bbe87f5fceaaee52ecbdf5f1302d08b13e16c234ab036e0639dde6d9fef5ef9766a823541df7a90f42d1712c7c101285cc06206e90f3b16f774fcdfef571589d17b0d3d9baaceba1f86ce983be09772b63dea45558a71616138ba18c5a113243f001b0fdf3eafd5f780d100aada9244c89c562b158a8db8d7563b14a130a8b45f3187d5e99c98886543f56d002d4edc6badd6ea5a865a549abb4b09c58321f2cbd2a1f9459bd46adac6aac8f11b5f6a161bcf816285a95937793a4c3c285f1a265fa9852df582d2c206a45b42a274f8785abc342bec99c5a6466468caec8d104d1a4e0e2429522697edc0cdd5a2c466679c5e88a1c4d0a34412e462e8c54462923d2e86544f3e366e866689f5a5037f1a4a24d1f53b6d46aa79695cc8d095c0d4345445efeac71f9d37c55d6b3174ac25b8b85895b5ec1d53044a4e805e782738153e180e4c7cd767d99174a5e28d9f54f2da81b289e54b4e963ca965a0d865ce922f8432d7b51e54f4443643aec008ad664b28e7c2c244646ac161622d910d4ca0e3a80a235a99c5847be23fbd4726a11b54ce6432d6b3da8652ceda3488bf8f657322e542952c88516b7164b5e71a14a912a999aed89bfeabbd082d688adc06b622d70a1c546ddc432e64d5ae5d47243bab5b0422d937951cb5aa05eb56e4619d5925961b995316fd22aac162323b185e5c4021fb5926f0fc3caa85f3d0f4441be7683e2afd80bbca67e1330d82bff2178d23298558afcf00642a1354a5368cde9a1ecfa2b91d5c29242ad90a035a908edf3a3c5944ca13562442c3995a6e4dbed6f59b3368c367d0c08f32b160b66a563ca91257624cc08f34af3564d168c91a5dd0726d432d68bac150c2bd43298076160605837160c0c69c384d204eec692e106a509dc6d25c38df50259acb19ab7db0dcaed06e5966f2614d3cdebb08a4b4580153242b27fbd94de40146584b8aa095deb1d6996110223071839c020d2a22d191999566be6611031cd9d2442e46e14770a156ede5c35ab4e0cadeaac7458e407f3fe7888e7641d1f2f780eca1fdbe035ee4320df80654458fc14424f2479d04dc2756e0feeaa7fdedaf6bf495c07cfacab3e1e02c9f6bf3e784ee955de4fef3e8a88afc2fb14097d14195f85f771f1fee08b4e69ad36642ed8b4e12fd5b1984f3e99cbaf681e232aa698f2c42c071b82a8369015543a1730ac232e7d312b7bd4b298c79ae7e62f9e293c483c381e28d5ac4d6a349bcc87cbabf0f9c030b1ebf3b8c06b6a8cea5f05988f8b8761a2248911397fd5770153f37c5c6828c9a78e905d693ad311da558baca2f20fa37dc0c8bc0a309ff2f58d105a439fde10dd8c37453734b8c1dde46ea8b0d944bd9b24a47d9afb8cd164748c08f7a07ea9634bdca6bed6b1241e805a3571d65561ca18cd6d681871ba117273c30dd17823e4a6e8860637b81b2a6e72f6a6e8a6e8f779537453e4dd14dd9ba29ba46c5d2a4c6d918acae955f422f29abda47821bd925e59b4b0a44031a4097a117915bd662f295e48af2c5e492daf19cb6b967acdc0d74c7ccdc2d78c26485c41a146fbe2056d6565e56dafc4df740c5a4813e91bdca4affab3a6e54ff3555917f92bf1b6bfe918b490be4113a9fed6f23796bfa5fef6b715fea68b50a914ec4638a258b44bc4a2b184582fb084764dbd5cd0b45de822f8452d638dda840b94b7e93f96101611eb86f09454bac25da59c65155516911af355d7b4b18e54d6ac9a44b066bb7e8c565342336209a1357892b06e40c222aa393c3e0cb1eb8742760dca9d924a57b088b08ac653923d2591a7a47c4a3a259d9286f05712af5152cd188d8611f97409126209b904a5522ca40def4d281921f9ded21eb5ec5fd432fda196691f29785b6cb1854f111380f21edf7e4a28e5426a28d50277d1a7295b0a978ae2267dca425314a4220d690a69535aead22de564c8678c463d1790ec9450cde1f1a1a4798290fcc58342aa0549299bd5d9992215654a0a5773769e98d5ec3335942a4f739f297028c81090d4504e21edfa5fb3ae61cb14cb0741f63fdfdf490278d92a5f42284d1b4e3e7ced3d42f4a774fcde35f8a23ec3107f085ef0cedcf2f7336ace57eaeef1d9638324e8794928c594522156d81866456e9fb06b2b16c2e442fe7d9ed0e3f3bed33c6ad78120f895e5596dbd9756773020023527a53b1212b0809a133e502a45420252e58e6cc2dd67ada55237bacffa84fc9236bd8b4eda608940eae5bbeebb477df7be51e5897a95f76aaa543395bfc4415641955ecd54798e5be50cb757f374d29468483d7dd5c4f1297b42e5d353166ace596b28d4021c4001fa34a8e6e44de9d31f54289f50b59a43cfea116dfaf549cd816d8a42a1503b584a902a47e84ad9ea29fd55e9f67fe5f429eca2547a9732012e34095a48559edf4a797e79a3ca14062a59acc82a4f42cda17fa252507f4abd982a5f44287dea6f943ef53d76a7a2cfbc551ea54fba519fd25ecd0ee53df64a4965406b4e47b603f6e9a4c1cf3acc207c8f2dddf713e8e52b7790a091d8e12efa377ccb587f7e2beea59698b8ffe7bc7c9f7fde7a27dee489244931b000d7caa37bef45b13e0f20bbbbfffdd99e3ae384f506d0fdd7bf4fdd6c55b362dc9dd1a75888b4415324bf098909893b088a3f6a0edd670958687f47366bdfda897f6c2f891434d9e6f6248228c1066dfea21f165972001e655ffc347210b4bdafa40d4adaa0919487fc45df7bfb9dd0fd57da1bac5df3570958c86a19f82526b22e98289ccdac0e26fabeed74b6692dcf8c894cb40f06b81f4008f59d00f5fd63f890fd3d06103182c8feeee58b6aa5735095a7a46dabc8dd1bbef57bc330b4ff85f6ba75fbf7be8ed879f5de1b3efefeea30bc37bce1f789e54f75511d23dc9e7bc30afac084859a2933eeeeb2d2f7d452e9f4c75b9e1410b125bf3ba1c726bfbbe508bbffe5e0f11282ffbee557cd3229d3af3e432c4f9848c72328b5c1ba92ac209217f257fce2d70a82fd1bdeaae6ffa0d429a54efdd63e1002e287f533f95573fcac45519ff2bb2efd29eb5bcdb1ccdf634ba8f1c3d38761b8f2a757d1e7dd2a2a2adaf4272dea5bcdf0b32e8d9f75f8a692fef4688665a971f937fdf0f780a298886882c1fe77034316c5b0bcfea2efa28d0efebc4f1f87cd16f9d62efcfb11c08625ca86864c3f7cefeff6deea17ef7d7fab3be3abafa6ab54fa7e63ce05c9d5a438eee458a26aaddac8ee5ddf5b8123dbaf6bf2c3998795fbaae6d0a7ff81967cd8ee3e2c75e1e35dbae4fdeecbd3ff2c01b6c9c79824316e971eeb1307b64db05d2acf1c1b973cfc79dc27ff84ed8efcd3cbdbfdfd51ebc8314235a9c6a1ebfcef1d99d0ce0443fe3ed767a641f961341b6e7b6e7b8ff5f403221082b74f0b6c7c428ffd40179624e3b73cec5ffbf575ec0a9d11d10404df5b5617ede6e14f9f3e9015b176d79d53edbd7fdfdfb86fa9f7d8bb074b5847abc6c14aa44db57d41b4995ce83adc75dee1af6f3bd7d8f3f7eef7b0f6929a964f34e87ea0e4076abd74972ecea826fd1630e083884d9fa506f9846d6ac2855593ea1722c83760c2020a9b3eca636ec4a66f4ac9272c17a9bf29e6e1b7275b7ffbde43a6db79b8f6baf2467daf3cfd5afcaeff2b61d6757ebeaf856920c34af78a9342a4ef2dea4edd9d3a0c6fbff75f42144db45ad01492365e2891fd45d32c3cb29dd2d938de71b4751c4767299d3d36fd991bdff6dac49ed556a95b8c3fb841f78cce236fd0fdd1effb1c979cd6786279967069add47330f4ba10dfd08661a5a1b34aa704368c2db36b798eb069f9b9c0a4c30b59b6b0d615f9d62fefdd41b665f98237dd3d768f5d03c73366f9847dad3957a4f5039c19e707bb7402c40a24102b92192801dbfe6f75baf7209a6cff5426adb687d9f7097d36e8505985589c107217fd8f529adbd4a935627b82641b12626492c1bd5e777789e20e83e97425b5dd1fb627399cd00b3426d4bd627777775b8776c52c4bf23b922cb69341ee14b6833c03c7073d3f23e4d0517b6ce80cdf9fe35ad0aaffd5347f466dc15c2892ebeff78af7adbabad581c196d8b02b45c16d7dfcd67eadd65efd807bcd9248b64952b0ed13b6daea0405571c19220765adb546830a1b4175a9413ebff53fe4903425065adda4cf82c5ce39f6cb971c24b80408897bfa0f850bcd5b238bed5f082656102df166013a63673575d7146f4dbf9de92e59ddb968ea2f5a0370fe56cd8c56bb6b487575a58b2876023cdbe07718d38c58cbca076fc9b2dac5c6f7b3ae981818d7906a5610c4ae221791cd4c859ceec1ce068f38a3d52b1bbf781999bf3fdee7c5cf8f0b6efe08559a229aa29f21ebaa43e20b8db30407091facf7a1c2e753c4041380b2cb03a9f67d59bd0aa6093f4356e767487521e1b1d3d7c739c2632a4e3cb6e231d4d7c731c263a9af8f5384c7589878acc563aaaf8fb3c4632ebe3ece91c740223c869578ccdeaf8f2384c75c622fabaf5400276a9f38b4983f59368eadeec47c7df16748cdd9c1ed73075769add56b57ea7ea55d44da65832b04da75836b887689c0858376c9fc555fa55d45ae1c5c23d02e222e1d5c4554b48b04ae1dfc553f06b54f1711cb76b1749117da2573d70eeeaadfd29e8f9337baddb988acce0e92abc8d677ede0333f66847e82acceccd00cd14c908c241955c8a8c96822c326e3898cdb8f0b333e7e846ace0c0a34455647061419538c76952165d7771191a58cdc1299c60d87864bedd6853d4023891d5ccda9bf836477a4ec5c713a7790b096b5feb6ca9f20eba2a9d118d1e0cc708c90ac0e4d11ce6b1c221c213843da839c075554d383240fb4c8020707c707ce0b99051b548b6c6636336b6354cd6a63a364574c82a0da218d2838b41d8e5f1fa709ce101ec36127f11a3f43280eedc747d581f9fa3f4130dae648b5f19536b3961538b49af37d7d1c25383558924d91c7c8af0fd3a2eae0f8b099d114d9cc688a6c66344536339a229b194d910d4d569121d4107bff9145a39021414030c4fbc0d4164b1c1a52e4d366f6e19021d40d7ea73d30a44444bb3e481feb4f0ffd04d11aee352706b2e9535712643568f5b7adf67617a325536a7c5b586ad84a4d29d48acaa9b42ab194cc5eaa029a181f3646413bd0fb28052fbd94d27b29ed3a5adabf17bcf77be9f9c1b8553d140c5001374005fc23c280ade3fe6a841caa5a75c6f1bbf5f558e220d7f38ed5aca5cb088d835aec4fb07dfc5dd781401ffc1bf7c112fc1e1bd4677dfc62edf179230e7a1d97b2f0c372842719071294d64c6bd64c5bab79556cba4df67adf83fffda7dfa4a19ea1b5d6daffacadd6ba635391fa1ef8511918987192e9fb5f0bf2a8dd574500ffb416c86e0fe3ec75228b4f7baa48ea1e3edf88811e3eba878f9337e8069f0263f9f2816e5f3ed743f1795db54044eab466ca8a52fa9363a43d47b83badfaa6f40bf507ea513b81c172865d00b65da4f688ce5d6c0d5517e2e8ca9e9ff03faff32ef4bc1e15adb1a22abba235563fd5aca58dffa335f64713b1c55f19963f2f3d2f74dd124f02c14fefcd2e2f469007e0ffe202b9e7fb520a32dd1a03f93d4908360de1f49191a54f111e2594e04964400741fb960db0506cf002bd5f09fadff0ef0dc3f0e6eb2f5a9e40d6a25efc2c794384effd6ff87fa5dd62795230fc4f87e0f74190fc8f1de3a8e64f8f36d2a38252690dc2874dabb882e8adeef3fbdfa7747fd0f35373c2cfa1c3526a677c945ad4a6efe995c7e214a21d81eebe2f2188392abac4412e89f737e9d52c5d512cb1c8f5c727c9f2a47b14e9586291e98fff5e73c6f14fef7b2c4fbac9c75fe9abace8fb8d237db2145fe55fb03d8ddaa2e8a40dd3d3cfa69c5f14ed98f5e9809def03e8a66f0261d4be3d0d80136c27b3f818e8ee8efc91d440779f2a7ff5e93bcd23bf4a3efdfd938a4a59cadaf73de91239eab3c71e45919a4a3a6bd335e91efb62f229f9257d3a60833feaf045907e106a8fd99d76274ac4b0f10c24ec97d65a7badf802ecbdb72a8e1f2cf2e934fa4eb3ee53be14a0fa807f521d0fea9e1cda477ef7aba28e3b12bda54a92857230afa480f75ff97d0f963d7c72ad2ffa77b873d86301fbaaca7aa5dae90c17d15a2d35479841b6f4ed57935293522f65f796d4863c46d951ec8bb50ca839753b212201e846673ad8e7b84faf8d42417eecd37143b97dfa94ea97097d6a5a076ca04bdef0289b46d9b4cc3a8ad8d23fab83e3cb385e901f47add62a9214b6ad600656d6bc8944d5fd96ee25acf6fd19b2ebacea99f70973ec30ed7be85e9a13fb8a44dd13bf1577b04d33d0a56e56d3bf9a4e9a2088a985fcbb5695fb0b20f2e934557ebf7f029d4036e3e771bfd3f75dc7f821d38f8124d3f70d64c910aef711d8f529016c99e9c656f3b8efdf0d4055aa6c566520df5ba2be27ab7a6ed868e34f287cfb609c1da884edd7353965340300000073150000181008068442916830cef254543e14000b7588506452389646235990e4300aa220438c21c410428021861084a8886a000a94ec377a98db164184fd2ca107bc803092e20d8afb601d4a1831d5f538326ac5ff87e9b58b9af2dd0282cab05aba9dc72a5ba24d06ec72cdf5820c1ece49e3510a2a430d3dd286cfb5a2c8a5a73ecaa7958dc1e3b8a5a71915079d91f2435a0ccacb949d9cacd2e9b82a295e49c69c5dbffee974187aa3e20db41a2667a323a01c281e98f5b054c2e4bbb818f797a5e01e05b1d8d437c6f1c5671114119245d029372eb8035cecac51a41986fc22a2f3c6b27fb1c71c8b930eefcdcb45c45f1c3eb1cd9680a56a998938e5d9542aa73c6dbe10dbb27c4f0afad2e77d2634aa408238d5a751e9acd25ffa07b29ab4c0aa8bc2a14fe11cc52bd4a901f6fe5ee1e4c6cab29142478e4c83de2889de935dd6d3c7135d0e45f06567e33fad05c5e792bdebf0ca19cd6205182c417986a3286ed6b8af8d40cce5dae75615f9932c10c1701868de00b21fb7aba3f4e45c36830741d1502a2b97be6f36457cf96fbea3407cf354d7d96574a9a0679700a230d53799049f9d6f70a9bc2a457d49edb60905bd38892ee88a2afc3fa0dd39cb576033cf47f2d9ea70ca4e9e4f6ab2e1f15b5101b53d91ba3dd9251cb4eed1098d9d445e55efe08af264369cd9935af54e647035f32b549177afaf3961fc934b2ac0b77596b5de70c56db73650915937f012c7bebb11390f21099fa53e745b03b49c9b2ee91394573a0379c4e0c2d3a5c52f81e3160c66d70e0dadcca2151dae51da25aaa0154b38b80134bd06ee8b834cdfea52567f65b2f235ed5c15bd91b32313c1a05d51ccd18818bc9c41119e7c18f82764aaac8c145fb0f508d48a6293aa7a3557eb9113ba7f27ea2ad017b074c4deda824ee165a8f00e3c24eb160a7c7b74ce2dc4e6455fbce00125151ac35064262a36bd5bd93661b2a2d35cd717c8439f22efb953ff6b75bfaf5da1d51e1eeef6bf81809061ab369982fdf98a97d0254c8c0501aa0aba290884aca49528a684f14b17f77f2c25455a6b2dd4ff681e08d13837a0bfed743c370b9243276164ca137d171dc5102d22b6a93f1627214993c4393ef079164ccbf58a640771c3be274c908cdd266a1909acce27e091700171e53ccb985ecef4187997c290c7f5ba1a51846d5d0100c0a63804695c51dcbece06e5a0814e17005737a06d4275055a0b088d482a6cc637dc6c58fda193845657584da07478d52bb3ec623431ac18d487633ae6a611e90be9d107bab8dc363d618df2994c7408f7e1d9c1121e51f15aebf3166f2088530d85c550ae2d5c85cd834c0ae6c5b711c4e4e22d74e7dcbf48d4eb3443eaa1e12d2cb920fc4ec22941579f082be1f4af184dd556dfc423830fcda70dcd5ea8dbfebd505254a5d914b007eecb4051f747613171626f3a8ada7d0e201032726dae64694a57177b757f512407b021fb8d0946747bda9b08566cfb569307279fe2236d0c4d3a61397aaf1cf1267c7836f3f3cbf60c78d08ce7a454952125f63f9f15cbd0fd27aa4a58d2256ffddb05aa6d4feb506a20ffe9f846b1d7900c85a306a8751ed40966b9924fb6a04665e35d5329667ab5c7c32d1511303b4381e60104abf920ea7bfc972f70be16297e32cab58367ff8e2a955aef1bf5cef353e72606fb210ad4d723d8c406edb95b3d1ef87af4b2936a84323c2e63f77a0543371f4649541dad05d1636a5139c4420f871d3d36654bd87c6e1722aaedb69ac192eec761f75fce573e8a22cd01852580c92719f0f215f477523deb7f5c8213ac41b8cf55c1709055d3b6a6fb8c1232fd0d45ccbcb1f36843c091229233fced0c9cd6de12200ce41448001f803c41e73099f72d50b250d6631a6c97f61e049233b27c8c5522f169c5a8218b8c48c1b9a9841b02325f2b6cd1d80fefd6f668fa9fbdf587414e75771387e0645a57efccbc64667e22d272776a44e89141cc1a6a878b45ae74023703afc1396018330b77a05261c26d3baa84342859c142c242f52f53d6944581dd2d9ee9de82412ab4690fe61bae6003c5302c4e0b401b3302bccbd65843a087748cd707c8f9219be551dee5623d4b00cc7a978d59cd4136135404f39a35cb8c53dafc827df415ea6fb5d3ac3d1f7d03bc5862c5075e05967557c6522ea35797e964d41cc7515a45f83ae3773c83aa1a3988c7dc10d6c60119b68096ea7a42072731eba73814b238b7decf8d7f9ad8e506d0b25f553c4819ea108a6ae57972d417025e79aa5ebd547b29553b22e090c17e1cf66e48dc6faf3f0a24c37a54482cd3f331fd1be850abd32bfb9a764407fcd144e9731cab47495eddd91301bf3af0242e857beb8d2e35c09af5333386cd813e98b84fee3d9c0beeceb7f341971ba41986075a159ef0d3d7e162fb6358642deeabb3212e2ed6ca903da27f273afee07721e7313a441e0502d5303e268bed0e0d8931c17992e6423f597a1e661ab8f71ceb6c8d0fc51feb20d885192c409bd7b0c756e11ac3eaf82d8d8017cec21b52e35b6496687547e777e300cb0c4389cb6c24325e0920aae4002687f1015e24ff25476f3c4727608c24e0b88a2e7b4a70df1ffcc66c840d4d6aefae3103a928e60c6260a08a29cbc136680f61e69eb48b83c30a0284c87748fc9d97ea4db5c6c78e91182d66baedf978f5cc08af5ebe6c5d9650cdbd21fc4f508169ba65229cd999fd52251b126bddbf416620a8cc3bb631ce880ab3c2f4164e95e1cb6e00a35e0a815a81d1fef6a587b403e41347e56907495c59df1c043717fc6fa8e24cb4913088d20ec5b6ed28de2acc95ac65d8ef8cebcc2800e890a1f186eec7196ea04f478b6317a27e38d214619afaa8b9d54b5a253af62230e27636edccea7422290a37548eae1feae60e2036cce62311c29c24a84c4b6d99053a4d7c903afa9b2e2760050c6b905b2c36a46e9e026fe372341b7602a82314f6e8cc28a41bcd4ff9c09520760ea94e638060c31d7a25072348a232de7e6c63d126c0b353b7830364a57a0d90569a7045058ff046dbabc1b9293103017a369a44aa8529642049cded9d46a00e50fffa08fd4d533b736704c012f69ad6bfef1817726f6b21fa6bb6ba619d9c08105c57b13bfe1a513c883e108a1106ba73cea5a1892b80d07ac4659f994154a88b8ef01028f01e05306170996bd3e52eb1ca3447693c5584e89f20b36453d11526f89aa707d5da65621ae36a28da0dbaab04085af70023fe769760d661f5003c074470f7c69508f56f9af92cf751b4fdf128b76ad7f71fce169e67d9fcaffdba3ddaf880e64aeaf24a111c0c87e8aaaedd51d7b27b4502e33b4afeccc7e6496ea5952adb80a5ae5a178f1b6cdabbe93f32d15ff978134f7196301d401463d6616630a8bb4d18c3bb66671081176a38925051c4f5f5c075da68b1eb4e7046f51f3a14eed8257714e0a8c4de9544cf8942a40ced808d08233001a8751b248186abf30a55185559522a403eaaca26c68ce53c06862867cd590f94799a019e6dacfdad319574cda18e52d4d3b3834a1fe6cfb9282d3805582b466e8c05c20c97c9645dd8e45d78fd1e005803205688c51b9f145aec3372e66fa53de2688b0e9ca1d4ecb03c1aae679ea07b640e21d57546c23b5ab8bfde38e4b3775ea9308150a9cba3e2ef9479758ece2aa9b834462bed857f96e4f60b367c6d8cafdbebc0b33f48780e9aeee929d1d44dc86316e5a5ce2f22f5fb161c6863dca2d3610007d0d80b02fa1e5a012fb7d7bc07a6ccd81f155b72945a77883f4cf52bdb4f0c5cf82e0c65494e2ae5ece4d809319b8406a549b0a91a54e6a950097405a1115e4d68e7cf3f879425f04f47b3d466d752710c0f4d68c80286d7a8a46f62a0cf16acb1e9e6c6e1199663806d3cd903d8fc728fd106cc027b992b68a9c3592cc19f0ffa2e0d4f75c3a78292a183dd3bd51236d8a1f6e25302a10ca30e368e19cafd0d677c1c8f682faaf933d17ed823680aac9ee01270887d10b7b79a818e986bb2bb7dd10a0ac86494c941dcd05dbb28181f6d09d90fd655ec2d2f81139ba5b80af877fd2e0be8e6ed62a09c5e08d7bca5a5338fe727841ef32732cccfa7e7eee31c8b6027f4c7fe5bfd0e6631504ef0e117d147528c31cbb4325d81c10285ec0380162346ca6984301c326820228f80442414342dda2f32d482500273401c9afb3095f172f834123880c1444a7248539b132307008e353b33a80f74d118406dd2078b593b6f744361f0799cc566adba3b70b7bb389d59b84294bd3bc17de327a076087b3c2d89b0de0eb2313d326fe86d14f5bb2f582e1c8563ab3f64b6ec8ffaca352c82d5c3eaa398c0652b09a5fdd92e90dbcf2fc79173b56e0814e89e965c466544eb36af0b1e4bc01a213a042d563b2b55540ad7c6928146575ce1b817ea151c8cfb7ce0ccbcc52b344ec2b4df43122a1a806d77fe86ad165b403c9eb3ce7f3dd123a37b1a7b120944afb9e66f1e573c72413117e52aea858a1f13cf10ad9ff85689fdf2f2e1af939d99f71ed8e46e264228ebcc61c1043c9189f8fd583534bcee9bc003443bcd054aaa22db0c8c840b3a50109a49d1c2f79f2f5be1e4d0aaccb1d3284bdd41cc54ae9e4adf876e35a19547695952be2199e7dd8104eb9549c355e164d1af5c67b250f67c8d8fe8d9aaa0674eca7b4d7d573c6280e5218c01bef7700abc165d96d7f6ff8e0611d6cdc76c222ff855019a05f7bfe475334db83d6d9ad56f085fe80cd75407b096560684ac5e19b9fae89cfb548132001d521fdefe2be2e2ff0a5f4ebc7b5e49115ce805eb67004b121a5bdab8b70e45c475d3ee60bf1e33b01f031b76f8a89c6aff35c2b35a89190a171783ab7137d90ede2c07b10905dc317d07b1118834aa4b40f3f576a00da5e3dc3ad647400a27c0e3e95f960908740f246808e716885d6edc4928d575005e00b42380e663d3d3823cb7af743decb57c63a0b88cbb4c4a5462bd5f1b4cc0915111ab7ef0ba70a703a184f31808acd80e7c3fc10cc1a540001caa38d4272a977f3eac5487ea1d4e093fedf7dd82ad7b4de6879dcf6524c5362ca3756196b83ed8e3a431aecc77fdc25a64847f75785b8fc85e3317d554785719d5c877dde3616b15b93b2702b74192d7278cb7929851abe3168b73f31fb31e4275db54438f531acf45a0dab100a79eb4fe11050e8519a128d91c9c30873cc91d9ee81b21e4756363eec3b7e10b2feecc4c285f64061984e35e5e06be415d3428e3576b6db261737720a2c6d78dbb94778153640c7a21eef50bb2ae67ba1bfb1d07660fd597cae1288098134e1568ba611acf2755a964b3cb117155b491764c9e2c62d1138e4ffe820753e69a4f9cb05ff920e46bf185e16e8ba220a37b8124da3669b1a386a0619c07e3f3a03f84a568d1373b2f63be859c964bccb9aa17541611c5dfefce4eb5d33bdc0281ded3caa065c1a2e83323a3c4c7887b07a8e6f3564399b4a8e5f6e4a554b029a166a7b8c88e987ebb704b7f3f4522c92af1860285fe84179096b732efa713068bf4aaf9fd9fd16b9f79dd253ca3c4ad9372ea87b92fcc8985086be8a94efc9ca87ce2535e72a133d7c5c94cfa9930073dd90d76879f94918145fb3a680b0d434491c857e19698fa0dbc7f3b2486b0168255a68553326751032b12742c8ed6d9311c54dd3fea4683cb2d6548a77d323f51bfa212f271eec42992174335c1a3764e572c25be136d5c97d528b23fb868df00f7b0e7a1359a066a1a569b8625bd42ce7c49093ab119f142a984d07d0acb5c34784c4e82b8bb0f360057cefcb93812895a3ff61f8746f89e576c354d547563cd976fd102cb02b617a6f103aa597a9568221802c1d5de32f7eaa212a7987e5d98e8369fc0b94382901cabe7bda11c1e0f3a26786f010f2bb9a19c8e6d16ec5adcf7283114324251b6fa2b23e7ba5c3a7c8078c2d27c0ed2f69e76a4a65d32c19811912b6bc52b5971dfad11bea9b0d582a28e2839105c4455bb5e18227a17c150151a877c02cac9544df7f4af592d951e1562603e5ba8f33ffe9a06307914158ddf0adb1e8424b5863163b30bf95e94545f3e429256342f086be13bcac0694e3c58a287a01eb6afd0d56fb01c06443afa1f54ae70b1bb102aa2ce18043a0ef3af5c3b7b2e64a9f6921195cc1d41b31281f5c022fc97cc53a963024963394bd10b5ce19a9f30ed9de99e7fecbda8065d103190d3c3e2f669f56742cd1f4094832aeb76bae55964249bbd14bc96458a36eb9df492e1f82ad495317bfbed7fcbfbc3ac742873fc4d58ce28bc39e6cf8e6ff036b6c0119e44a303bf37b311fa97026e90e7bc676c9c31a579801e116388ff6fc581c7b673a460041e5f67192a8dfb59593e3c7a1e946003f87834a4a424d53ac31487f664a6e23ba313006f6e02b8b85c4e71e85ef5b1c3cb10888223b2a2daa841f1f207aa8be749517211b237daed9daf58c6b556f7730bbc7b41b30617875f91de932dc80374e576836a2790131db99033370f29948ae16730a90a2ecf02d40980be967de3b6a855cea56e09d7d09f8c948ce3677e1f28a9ed399f54174e360e15ff451a580fe1c928057d60bb00f6dbdc4f40b194c0299c83af540dfc96666179065ab7a950061b423b570a53462e1b1b34f722ecb0913d0e3538a8b390b144eb0375ce75ae1c6c8edd09d6393b79737035ddb732b0f4152fc0687c70b621c77077ab1754573cb00d5c50ca529063242e97c70ecba8752092dea05999ce42793ece5c1d09ca62acac7e712c515b11b008246aa9647fac6a1e4cd14a57905e334f8e6b33c0bc7d19db4e630b8f82082b60941f2003f8cd99a7ecceff90e967e4a1c6b331acb38f79b1656c2df205cb62f76a78cd987b510f5070a4b51e048cd4375b4048929fd25fb9175b96855058c415cad425e0e8a15baf94d141f848689139700137a0edb6d4f9511f5b6f79bba8f7cf5b0c19f6419562bb4d95824e4f43953b17109077377d0bbfcfbb70ab88579693c5079b50fa8c336b84a11121bab47ff03d15221efc6091ddb4768ddc40f3f589a82619dd18ffe889800f0d9e2ba446761473cc5ac5d467eb40847ca4a507cf6956f85a72c39723d47c2735d97e58e41fc02336bf2d0518910916a2606dbddb7eaead09a3d60b0d98046e3953a3c88c66bc032015514c734f0580d2491b3d7005c274340b6b56ea38ad1770d062ca8871c13318c84ccfc58c3f06e6cf918681f90d5c009fe82565fd1381aeff8c720111d748e4182a7eddda1909439b59f3e43a1345413f9a6ec1dcceb41a818e5ca247cd5b6e8705b2cd0bd7a7c555968ce144b7c2ae647b6f34e8ec64fc886e850ed2b84507510d85c18691f74c0f4adbddf0c802b750c087e447d587eb512af4ff50191d96dc466ff4cf92ad3cbbb29b958d835b12fbbf21903e6e7f345b7d83bb8f98b426d27d6d1565845eeedde10f67a911943124c0ff6a7a7da47b52028c00cdf3ba7f0063b30ee682f73208cc2e0f53689fbf6672300ff83b7182a6300095bfeeaf33ce6d9146180da48190d7c30674c107c69bf0a2afc170ed44247470285c30836502ae924e8d1aa19d19c78f2e634a0fc3664ddd3439f29d7fce49178a1768613370425d023c2bf6b2ce36167c86f989b6cdb51c4f7836e719f5b911905a67b7e7f3abb8af55378d05ab7e839455a33266bd5b99ec9c2a8d755b53259584aabc400879ab10f19d95cc93d823133e729ff8c9defc1b12609277457654d7f40f60ff87bbeaf651265a9dc02f05e460cbfc73d5a80486d54664106dd4e97e29dcf46bd29ad47fe9b65ebe74e5d0a4f79079295563e154f266ba86ca38aee6b94d9a25493cf7568344668259f5a9b816987114dad41107a24935458043a405ac0a7789760b21aaaa3b27c62c9d4ee72dc67e8fd4bc0d43da362f5387fda26a996ef9993573ca61b3c0d09b46b06f3a7656d3e5181d376956463885a039b67e41b6cbcf7e2ae0e0cf7a55cc224a04b8b04e9964fcfdfbf06828ee47f92b8a42f8375aec202423a5f2c4fe85344f144a5274e8d64b8359c878a82eb11e77936990cb158fb0731626cbdaa97b18cb15b6b1940a9dccd7e044c26773716c40a9585415e39d6a87f7afd78947faf36983a5e20dae98769172ad1d0c35e1529db503ebdaeb264e60370a8318999f6561a067a2bc9495f7914a6e015d350be4d40d1d4f328029574bd2d8f22d3eb00235fb7ba91f12472323ddc80d386511660f509bfcac2bbd7d16e5a42b5c671ddeeff40b24ce97285b02a63057dc5e635b6924ef009753a728a9a5e8a6accd9aa6be00ae8e236d94dc9505c2ea5aa9cb70c2249f42d9323b032db001193f6c849a3811778d1a44fd2af62cc9335c267f26f9c5f747d5473c6f568fd8323f16af7b0a00cf327df20c255de8927d09322a696300b24101abaadab873f6704f8185ea79ea60673ea540655b0732001b6c14201f4ee46a8eec62996d064326e81cd08b5d105a3ff7fac1962520b96d95ccf0ed00350f79bb2d5c7b301a4601b8be3156fe66574f96f64043b0ebbb550fa41c092fd2cd99be3aa808f9dc94261421a91904c0990801d83cc2f4d1e413d67a654cc32a18411d2e505abcba267c78f2da5b720315ed2582c0af33472e4cd4b1af5b1c5d2b9425f6588cb36d12081ec1b30d502dd1f74642eafeb5c61dbb13fd0f99fd7cb0a5eaf4636e8b6822df31c2d0fab0823e09e7e068961eb65b5efec8422a03a89ad8903876ae290074f4d6e647c35ae866744073e985df6379b188408f596efcb734af6d090f8e521c4680eb418f1ab9278158a179c41e1f5e079322eee61241eff277a499da34161424e088f018599d26e0cb8f15a99c20aa6d91b5a8ab133d609ddf8bf6978d9b669f69b65bc8c71e24b93dfee4698536d007fd6ff4305d55c8e9749794c005c2255b554f5fd50b443014586eab7a364d38c5bc7d58e949409b6a9a06d71b0845ef35772fc34ed5f4a825b132d5bfb2566675bc23080d0bc1edcb7f71fb80f3cc88fa048f2062f1d6f2c91a233f9bc7b7b3643595c555bb4685f9ebef289e4ed5be3e4a0ea2df843a8fd1a4d804c2fd7e54857bb10323954483ef05e37fd7df8fe6a0db48e5ec89066da212d41f082b7f22ee98f40caec97ca9b900ac210108f8654de1e03473b1ceb88ba394ca5771fd166b5c5874326a90ae9bbc286f535fc9a9144ebcd7e86e18a10bb3276182e0514c25247e62969a760782c18b519cfe179da7c9c1407e12c803b9ae2f8f8a7341d45431d438bcba416b500af57c52a4024db43e264f901cc58971c1ba64a504f5a59168507520d5c5956840b141b65b02c3d4caf957622abdaaa5010679a23365138d8e068b20feb4025597cd42e48ea653eb2d42bff43a13957cea87e672f31a6b960d4cd12f9bb8aaa229582322ffe794a52f204a342b6d4717ef1f6b6ec80eeb2968a9be008be681fccb3b6d3464b873a20be208c937c9f3aa2f20a6b53689f1cc79855bb20cd0c66b1599f396d3e8680fdbc829de18b1778e3ab96f300a5133b49ab75b17db682bc2c204b07d8baa99b2256281c36f8a512ca134b16cfce89b863fa4b0a446f45848083dc7f2d866b283f468083d90495032e9f9b8991f61d651af8cbf141219cfcc666cc77abc88fd9a359cb169fc8cca507bcafc398c1b0c2eacebb42a6cd623e4397b39b73883871eaa5130550073ec3305f3a1a57a3013990e9cd786e1b3b92609fe893264cd877f000e2f10452dbb80696874e06f2b2098b880497421ae1881f4df72f3ab459cffd935b898c9ced35dde2357031510b7044f4c810ad0257ed97b13f9f276786f2a7a1508f4d9c5949e53ba3fc5e91f05ee43ea12d2a6a5da90582623f8c4f945222eb0514be85266771c794d1601795b1ef0ee94304432eb181e8159a93062cdde594658ab96cde40fd7f2e5387cbbe398e28f5ef2c92a1a03f168ed37074cecb3e01ca5d3d279137bd77849c70913b8c821c29616a154ee8b412033ba2c8fd601678291be8e0f1e800dadb78d97e8caf19a1bff07bf0d18c0bfd65471b816e5fb720403c1d6832c4e671b5550ad4875c5f92a5c900039b9964cb21c45bf1dd2d23942d6d8979a596fb9d1f083b8527c4f91c1ae100b971698c26a39f684374f92b0361e56aa9ef255591739b2e95c8e9721e43e42c42e171766c6809ffed8378494037d2a3c8d5d26d427b6d88198e3f1c77afed139d46ec31be10c537af69b4ea6005f008f605ebd50b750ed78618072ea6427cf9850705d8e15220573a6da15cc662537472ef78f15cf1cca0a6226299dfc1e3a9c6087d1f40c15d28dafca318ba3ce7c90f09a7ea62dd4b932784255e477553e2b11d1e7101a8526696cc7b68a341e0e3b6cc51e4c71c8c3e0f8860141df00217b784da5848185103e58bb1436f54236477f52e23dd8d218fd50884a51e2849e4d2b373fab497f8f1104cc79337e9839ef7f1be87712c41baacdac501c7f04e5ed3dee9a9811d6947bc6b48b8cd01419118eff1e3c080e1e776eb9af92648e59711e52da58248725328b18752a12d6f24b67c96a958c904396eb6c130a086c15d038796b7c0cfb00ca4ad8aa54857f8c70aaa41903bfe5cd2bf4d5ddd4dd308d5b860f51277fa6db2e80341d80e1596d5d7707b2dfbe7eb86559073f1bc04b547034f1bb424d5eed660a55b39171533c8eb855dbb2d9e1c4953c10bcd7935f0cb7dcc91afcba2ed9f2d7b8c40254c5da56d255ba812372305c5695a2f89b1069f22b8c07c301117c88376b99d9d9d88bc25b303cfd2de1b33620558eadafbbde0ce60adf8e1d9ac40ddf423631cb59b27ff8e2a5d399239278a96b80405ad3c693a81d5a62e855991b79f593e21f5eef32ad90b3884ea7eb3d985b629b4b1e02caf17cb9bdfc144710f33a9fab8b9243079263725624d1f95a22aa2e34804589f40889f32ab038e2b8d0a878029e29b767fc34726da2b04e40f869b49591a82656c021968d222c45d3972ab0fc0f471a9483cbed38ef4f382880d817aba8ad801530457c15dbecb0f599d21939faadba36a26f6e2e986280bf1e6833ee9869ae3cf2e5b9d2c3f6441dca014d86ab154a13e1293a4498b84e307ce4cdf124918d6b9b5ecd2fe84200872f74f5546e74afb9532381f82fd04b9fbab513e1e04c3030ed7363b88ff9b3b30036c107db1e297db2e8cb723cca2fc8656a35751a6db3128bd3ca7ea862f3d01e05f66fc79c7312c159974d695b5c12e26f10986b47c89f37929e7a945508b71b4d765d8667c02b0cab3fc8ba6d2869983dec07ee7fc49c343f36b3d8105f8283ae3fb980d317fa7dba838acfa37f5d7b3e7c78efe359faec2a2de3f78a403d96e0d8a725419ca020a10d955a18f585af487ad99dba191114fde62919b90da5ba7d5cd8ab1612b05b1ad78e365c1a4d71d2d7a5ba9cd9951f0f4a774a5400951d485d36577cac0a92e4f40ce0203afb80d45888ad2f2e9f08e526eba9303fd407bc393ea9f3d362ee935a588c61daab27a0ac4f81100e68663c849e0f89e85039def13970094d8f9828acfa45492d50a62f3397db0d06e4834df916d6463efa911faf87aa58ae8dbf7a108e57cb470238b870f109a43202a21f085e12fb3961b7ed7b857da9f481eb140a1109cfad61edaacc9779fc3df9a70e87e67834a1e8d6544d8466b463aea4ad680b3b0942c22dfa02f487544076908bde082711b005132d5eac7d5a2d07a16515dc9057cfd98b02e4fa071b8c9f7c3b789ccaa96f0307c4eb727caa7a88e5f38a58e0949cc9894076b63a4df18515dc2b06f4c789d0c8592ef1163d1b6cc5f95e0be0c0f5673aeea38c6fdb0010b77863d48b466700e25975960f6838c1717d6f03b309ac80e29d6819f45e2486cd709193f04e1ad1283d39e9371ebdac5c72dcf54778d48865a099781eb9c043763ce5f58f02eed11f42151083e8baeca35b3d08b5c02973e368bbaedac239b61d63432babfedd4259c2ad3154b499076739e006f54d41a4fb82b3f02afe9610e567912c6d3f18008bcd19bc02690074c3c9cc1d73b107d7a40abfcace018ab8e1914dcd076823f712a345f870546bb624ba71629cd2fc908c2ad109c1c43125507efd732bffd97bc7e2b782722769d682123342061795f0f11f4cf13d18272fa45db93009369889ee210ff2c091724d2389ab3aa1bce74c22b50996997d20446d922d497c2fe38bac679ce1644111114c127aab1a3a3593b15a16aff36e9f12de425a800442e5c7c220f3099f3e0609bc3a5363114bea908dfbefdb2789e71641f7299e32153371631aff063a7a3081ae44de06131218286929e7e11c95e7ec8ce86b1c4361e044e07b517e6d224c85f2c68dbbbab0bcd230bd5038842ec31ad0e28c36f6e1c802e013b98ccd471af9eb14912eddc99839f814b4c9e58d59545c20c4f36232fac07b5c0db3b8393aa28da48c581197a94d7e22cb1827e2d33368257da6d9da04ceeb699a8b0a4a050dc2b0d4d619defb9f472c273889c2f44873e5aba68f372365883deb7641dd576807ca6778eed070dff6e1dc16b915f6715f931282e8aa6b1105432f5a9c1a03b3e05133b41c5e067733947b699257b4f97d59bca95fc113409763bb5eb2f6f70d94827cd9e0fc71dde2ed4a270d998c944390536dc943da8920df314681845a4fa8f1d1e300f0166fe8bff529f61dc57e241c74ff17dcf19864d5e99238f4db39307240090e4552aaa8573096ea7eeb275c0e878cf5914b1886ecd5a2d2b53cf674d46376c12427db66d5e540f350e94df5080737d2c52140bd2930b1e9edbb46f88113c133503da547110dedc47e019f792e37d7e67cfc0e142dc4a84fd8dde008fc365367ab23a60181c17f32ebb0fca60c70870d1a6fc5450b5c9bdac2c355bc31c147bb3c8e16896e1d10e38f1157df3f2ba01fd26d9aa590a3077ca3fc1dd330189a1830105293466c9184f2b03f5f2af9084fa54c9cc19649021099e4b5381b36a026ac49e5ae3eb891432c355c6afaed37d326f21209ed0d23b537d11971103ddf60ce8084e594ef54e88c7709929aaba0baf47e058dddd99a384a5b257d4548dea40e4911c4d2dc6cd48bb91762dd92a35d390a8ac698924f0a5cf0357175ee2fd0e19175393bce3581a4ea88ace681eba6d821d89270a827e9dee8f40cb4f4e8bddf82fc50e18ffac78d28177213b282074c3d88390b58f07c96ecee28e259a3a6cab4100a1e01168f062b39cb4851fb441237ae47a99cac6dc220094b7fa7ee246ac285f838ad409fcad21cb1a6989f8f83529e4f9cf3de7a46543284c0b19c91d83509635126057c8bbe45d6225e6e3c85b03b7c55dc0798c37d61576d30a849ce2883b36ca650d40860433575e71e5fc0ada8a96d319ca493ce52d0a40f8bc22c924997781f65ed0624bfa4526f99874bb53a9bd4b33d99312a94d577e807dfb712930fdd244147817c4fdd8c0e97789a8c605148c44502d77b6eaba9b7715455da111d04656ed058e6dfb6f41ad6f8b439a11b249bf667743b64a2addd233dceebcc7b479d9d2cbc31095ece4996acd20005bffe921d4b6cf2bce3df4bfef40dfa715cdb94c8768c41289d55996e3006267800453fddd62437957f6871c172190d665bf6839e61a1cd433e031afaa8267541bae1dc0bea1c919ac346919d21f02268355334b00541592444932551606f06426ab51a8de684c37d86cb66f82ac78eada9fe0b5d38d36d29956a723c5f2aeab2e74c24134742cf32e10e217a35eb25dfe8e5f831c678b806a025a02d61a789160a0e90eb7ae4185cb81268a00a39e99f73b5806c0ba71a41ca1984de7a47ba22dbc090eb03141c8d6dc439799f13ea685145cc04b48886b3178a49576897e83b4f6546f31bb42bfd88ea49a5419e091b1b3d68b53feefc5eebb277a102f61fce2596de4295136b457e3638dcadde4472e255451f288874a43b532138759e3c0213358f0030e96de06b0993592b9f857a20ad0c2e36e61b2e2641e6e9f790a0700f8c481206e5ffaf08a04380422a695346ce87f5bd5e3959e4c59256b471c89aee1b13228c8bbb60383c5259816edadbbad3a74e261934b7cc8c553dfd2450a86e41bcb98265d8bd8c7d17f8243fe97a6839ad53b0b0e148077940b8110a252867ddae5acb9d706ceba685f4f89ae650810ac69e232bc4dbd5c4cbf4d5c239b8085c22a5161f0ca2d50f280b45424d0c95320232c4968e0e61d4749fbe10574edd820f573e52762c717b29167906ee74e38e75a4731a2a3b573d3b0f49a0803445e83e18dc7f024135e738d04f639b63d5f6f76423e3a52e7faedc2583af751b96a19d9686adbec4929f35d3b06df83627f0dfa59e7fa9059a249c269cddc88089dc23f49281b9982819aa931540f1c872a2177f470eeaaa9e3b6cb583d03fd38ad8f981508f5bef3373440e37fad407e5c35bfae55f4e0fdfd3be488c1d6bb8eb091f17f26b81d6071b7c73ffdf3e84f4f42984d8a3b541b298163768223d5fbc6637f964f1608926c2536164084f404745ba0763cc94f70b3adf88fccfaf91387f83f97d78bf6f70015dec83da8049eb25a512105e2ff245159d8755d9f1b03ba1bb6299cb3c711e93c46ab3fdacdb78edc65ef4cfa18dd380ea3253ab7cc0481ac80859fb0ed05dd2c9e0962c0de70ca5c8738c37321d044907962b222d520776db5d93fc1eedeefca3c32db6833ded4fce0cf1d641f1edeb46dd95b2a2572df077ff0574691cc7bcee098eeef583cd151698221caaf7b10f56fbc1e2ea7d163471242efe253154f0105683526703ac0a49b206bf57833dc22ab45428bb455fb2a7f916f5c8c87a4a242eb4dde3c870178b0ef9f0b4a9dc56baefe159d7273783007c83399779c5d54a162244aeacb3945406c7c2621247969b501b65082460d00acde7755bc1af4ff466649e675c3d04621cc3977feddb125752370d24341357bf2a1a029d6c66294ae8dd3dfc00adc52478f46506234d03bfbfd8b04d99c664d80956cd2e2819305a9d77111c5addccd8926c0e1f42b7699458a153c5c240505511d917249c8883d896ade446c84023c1ee026725a6fe5cbd262a361be7a962e24204418b15c4fb2972f402694df1dc2b743ac94260664f6480b4d20f017d2e5ef6d58057eefbc51abf818071a922b11f9aa10210c505e323f42ac486d2451effda11b2220165a77e92aec734e21f0ead41fda44d8e213ed10f28d21dc078fbca40fdbc95bbc2983af95542f63ca4fd5240c166fb79a709694e5a585d48efe129956973adbf2f7c79814f365119973cfdadf952a2d4ec5c436dac56fdb0460a20521f220def94752d3229fdce4cbfa9f39e66fa9827102dfa6145e412d6ee6f25db229157f7d01cebc86651c7c54ae26ac92893664ecc3beb1ef9f936ffa5f659475b5f2e70fe66a9bcc968b025f2e9a0919f5ec215c5862224c89a80f59c55ad7a82872602dab3c5c7994829e414d74c544948a245b3557eba52bd74fe6f41f6707053e2bc2f79702efa9cd3ade7a7f71549f6de5ea08bb6afcf940cd538cd70809a0806c2f380047f94081adbd6d263df7a3c5571dceabf1029f66fc60845ab1a01bb0949d8c93e281f8649db25c6b9bf88aa9d783b5aacc46c9349f8c78b8c2c8b1269a2f8b06e6abf7ce49b7b1aa56fa069c59f5b43412a0aae13a26b6f69d0c5d63ba6e4042950964a85055ac7b60359587fe9c4f3b1929437bab9066f55d7597145afdca2f987c75286122aa650e8d05566494c1a2beade28177d6a2871cf0eee2c746aa9e774c298a5eb08ad6eae3d5566e07c1e98f8c9d1acace6764261d0cdb950a28958a73d72e4264766d9f3bbcb6608ed97684e79a982d36bf2d1e8bf0b7fa659423f698d93e05f180c2607449c0e344fb047d4387aeec58d965ee86c3178f25855d21be557a4f489ece4c8510816638ee1081c007079bc70b2c4b39273f54ea4272df003e4bb18dd4d135e0600e823296faa3647a9c133f01c245d37cf21887ca3f2062bb15370562ca597a725d9c70757b62f024a31fbd63d01ef85220ce9aa129e8e689b14159a5f65a1a822b0621bb545c398c2ab4d549535eb539d78613edf94a73b723695c8f53534d5137a55b606ea1aef64d5f73535912397e0f815a776acf6e58a3bd72725c064983b3aba18efdb131584623489d80afde0ba4d3d9869727702fd623e4e175052a972796a7cd6fd76223dd97c68249392f1d47c3570b9e1004222dd0dac69bd5b086cb83c2f0280f14e65afcb21d97e088c17738360c69484cc7c9f4d4670d365862cc4a34c1323eb0f7fbc34b4ff21ae8a6fbab55789561c63dd58993f62f6f869b3a098dc7f86ee56253e5e808c87d22e0de9785eff4cae4c6230a87f51305773aef1bdcc795798a0e13b59022ac2bfd0d57c06e791dc9d3d21614834bac04c329860ebd70ed13cc5a38a8d4c5b8ddb73d93e172dceebbca9dbc46c69cef58039f8dca9d3886d498e6b3c0580193cd55591a519ccc3e04ccf8199615dc66669a4195297dd316e2cd90e732014b7b77ffe3b35f570d5a4cf4307516cb99aaf4db081c04c4915a7a6b07090538fc47d048023c6a2ac5a9ba4140b7c1a622fb68ab41a0ce7c0be4105d0a9668cf9b7a76bc15ba92167f82004d02bab453b0c3f80b1436052107b521134748f70310a9afdd5cea95c10ab8ac5083d2f8501f3197cd261272905979b74fe927ea78ab11b938450f3038a446b231b5c1f43e92a247e0f3ba76e4241ed982d850ff4746afb0ba271691fc6c7d8630ced105b5d0347e35a8b4c4ae4db8a63cffce4059a2fad7464f803875118f4e9e163538c3b7c3a502df9ec0121207abdbb87afe7caaac53d6c209a7e7dcc2be9dd6e1651d8aa6e82af27684cf496a48c043a0f8e2506cba568922493dfa434559e54240ff776b09d19b8598e69d8a0315d080bd2a19b2bda21afa83ead68d1594561544945868ae97078bd8c1e885d29724e1f7c020c73030d8da0fcb18d7a8f30d4cb4c5e3526ad62de88a75a010502b2d90d040eb7d1959186c55d3acdc95998d0b9887f493f9534c31571acbbba7bc4048790b3e4010d8ac51f5d9339729d49897a376e167cdc116fe3f206131e8eb30e97d5df87f8d1cd560a477c653d068c1f9416ab61abb971a1865af4ec5b09218841caf5625c88879322f7af100ba6b8247919b2d88fe800f1ff37846b24c6190003f2112acdbbd1178c3d193e6f7558a128ab73620f4336b0c80b430a2b68d582ad1a8880a3b8197441501c612eb08ca05103ad39f1a97895aab068485e9e36343cdeafc18441907f5297c347f1f8de6094cb60767ef469e57098f89408038d12e4ce9ec5ef992234021260177ecb2e534758865a0e4d9fad31a43e6a0690e4623c33653b7beb402806862c509acdd65b1fca143f28afae5acab4750fe76e59f064b87b6409b06241788229b823825bd033bed6c53f6b1d1e1b9b29eba30837771ba05ad52ba026fdb30cc674b33ce6af96d894dcc7d5809a0a36d02ab6303f01a340632fd516b806546f1028ddd6966ac3b76b6543c89a74e6438bffa52871c7a9a2d7f7fbab7f156396ced9ea7086ed3fa96ad683970d6fc3faa9ff8530d5f9ea7387b1cf8dd2cceca0acac0c7621f2e01491861605ef5e85e4ac8689e8f73d9083d3a620410579650ef317232320375e5b7ce6031274bc09427b8beb17b1901254d340baf0f8324eda1b36047a5a91e6fe923357044638c47c0d87959c9f4a22d11d1b1ad95b9fea7e65a6f6bd46da69d29b2c231418764520c64f4a34b32eb9bdf3b4c2097c21245c5b1b2200bd43bc53551be40bc64f774c486c9859768792947b865088c90afe182c7cd360854108339cc05f44db32a59eeaa49a971fe2994600991f418970f63de3e84723007200ee893ff2f00547bf22f8f263ff1c7a227d688b7e9e30b77d5928b1e9200bb1f1f8910dc89972097cec87f7d525d18ed31ccc70a768a4440f1deaea7fb1a13b25f1bf6dbee89fa93fbe0e20dc604d4726c19ab94a131783963b530feddce355f81d72535bbb841510357d6e9918510d565af3779c6c53ef8c91f53fb9dc6d0005e3e84168ce8ccd9957022d5ad9b781ca654c360fd160499113aa46eb8073621765ed841d1132810ef4f9b6f8b174aed7e3c94fafb05a724cad8d2449f214a248ecaee69394d9b29f27408db284c5cd88c50bca6678c5dae7e8c5d92be2b0832e713414ba9403f54184523ca6b602791c129dfdd092774251f2141b0dc8dfb9b4cce65c69017c0ff419fdde97f6a981f5c00f4416565bb28cf8aead4b3a1b1d43aecb94e35a6b75e94f98b59589fea14b5fd2d1d9eb72f36a2dc85a85718760e48d199251f456068fd984ecf3d533bf6bbf0f1e64d2a5d6a39be3d41155f673f03a170b59265ffd2fcc5159a8e5252dd3617665ac9c1f2b58341ac65a066a0d1b796a8d59102c53dd45b11c0e80e7d7471e3a4b51c9c2e3210e1212ded8a0a125f438c600c5a031052218bc6b17d83cd7562fed537725d58e69400eb413a7154bc22028c7a6218f01a5d98a0680787d1ec1f2f48e96d311a52c497eda3652fac13a93d9df4bb26bfc2ddfc02751e9ec418a30e32219768414af2420543bc99602df574b4eb8977e7354408e8cbf4b8345c8d8d32869aeefbb57a47bca929f9ae6310aa2d4843ad83e67c802656af922dc7bd546fdf4cd632edac8c298e4424aa938a4d374b1f80e4c9a41aab2539694335ab41032d650cdb1571be7939571763c4338a8af20b9411bf70b17abd7beaca3041ec84f284dba32d2315e76d61a5222d303cd1fec0b686d960d5d2a7562b4e6a00c45bffe4aee6c0f8d8fcd4032502a0f6125df3444d1253bdf3c3ae1669050b43cb7a7e7e9ef74cac5d5bab311d77a0a3c7a42404fe0b43b32fe5dc59146ae565414b3a92ce41bcd8645896c41cb67fc0db55b647029f3f78a52d1de312112325cf6d40062bc7b287e6b85e377a9153ad72328a84cab86dff10875b80e7964952cfd8e0ce9a493626a969a2fb7d06cbe165db2011c2de2b050e5a974253baec2d567c138dfe8af01c22d2a2c6d811bb8ba219ee985e37b74a6c20073497974747ec59c36700f0d4ad77026d283106de1d021ca879160e66ebb211e5ff088c7edfdbedb93e0d4ffe9f1b94f196227fae8e80ab5aa51da44f8c11e9e9dce45721f8d116ab4647ce13da1c55ff43b13be34dc78f818660f1bc73574026520d865794e81ae0ac5de9009d4ee09a897bec3fb55270fbdbde52f5796f05e84ddaa0fdabdf3ffd720d1c7409c3a6b41fba240cadd070aaa057f3210f2b5e627cd14ace510cfddee9fd6b97dbc6dad6d9270c9d6b259350023495a5b4b877f04b69e36d9f7fc5fc535fb7ba078fd032627447045afea3056097391f60739106e9d37b98d4fa326264f78fe1e3a57eebda44cbd9b6c7644986e1b00fce1e771d2c47e3de63d21e7df36521756e1c234063ef8b03cf78575a103befe90f5e4783d56206be9271b527b865b0470c0bf54d8d5430035ff605401d8ba6fc1ba925fa4f2d88b4f0fb2dfeeeac085436d58bc4699d95bcadbd02792134640b95fc9cf779d27f141557f9b3873a5ea5511a9803ebe2a678f05dcb9ea2ca3fe622f2850422f78785c7164d5cb6cd67fa9f94237bf7e533b409412287723b0a93580d1abbc52976648b360c99d5a522c7768818c9655674a8bc957588ca11b538d991fd158b4e653713f1691d862cb91ff803047e7a43d659adaa7eb7b663331257d65247ae2ccf292492ad2da0c6dba5b20e15e08d83cd3a7d1d1a4666ba2dbb51cc56e88c6a882322916e3c46017237256a0459da06cf082bf6f45e57fe68536a5e7fec558285e4a0ba9327d551deac6b06bfbd3dcfa75cfe26cbba403c243989fe7ba5d1f237bb7644767e5d3df8ec9722e93a6c7be47d80cd88f4100ad8eb9d721e5c7b64a598c84a540a60d6c2b3b3c8eff53135a57784627259cede46aa3a4b07502e95e4ee9a76a2e0f7f6676981ce91560b8c4b64e47104a7c96d781b5cb319b33cc4fb6087122cd08382a08093619728073654edc19fb4cccf7655e9989504abc77fcbac9095af27a17fb0ed563e78e7ce81c1e7f4d4e5eb8fccba72f1bca241706d5173e2b9db9faf92a43bba9309ef0c9ea0475bebc04952b52c47f770b24914f55c0bf9f0216cef75566e50a65e7223228b1c413b9af22fa2fd7a09ef755ecb158c1f0c7af225945e9cc07fc2a76ea55a04a821e10b702d41971c7ba6ec4f954f45873a0c8585568c229ac200790302ea21208246d07e94cd32ad45ab3ff597b7b7df7ef9ec85a480e9fae196b6bcd531e793d4ee7e8813c627855e75d67bed75a651d819b219b6add6271b43176935d2a047c8abe4765cf891944bdf0fc91a00d26da3fb10f67de6a37f8bcc3374f7e47ae4eec2684b8ad55833bc8f0dee9a131259c4a5b276cd10b9893119492a047ba3c5ed3ace569c37734e341eb82b1349d9510096d5c07b735bd9ec54ab4df63c726030e29d479df0e1e2da1b26665065ddc782ad053157937f4ad0774bdddcf035a505fe9c62e64f32e38aa1d47587b89b89b362d754b67f6d3844a607f94aba8c75189f661b05422c40f0c9f843ce2f86327573f279f7720815be8a2d2eff4e7d14481f206c25dbc8fda1309f30bfc13a8db84910c2fe6a7ca22af0f009964624fc337c8a091eab88132f4b3321ea273411f239004912bfff746d2ec2b956489c843466dee9224eaf1929899e2da0ab937c608dcc07eabfae6fb85ec913c34ffdfae2876edc7db46ca6d74cbbe36d087db90fec8e31a7441f7370165e550ece46d65ef203d0ec4b341d71fdc5ae940cbbe11aae62f90697ec72795218ab0b8ad4af9c8c3ae3d8b4088ecd9442b136850f46c3de7d74e36ff4f5b6a10c2e619d89a5e8a4a0f77d699ae5e2815428c2f6f563425b60248085af28c4faf51ef8ae456c5292b50a0f87b54b80b9e304aaebf6697be2b66f58a9a768f8ddda3d17f521110da5c28e5241ac651352184faf6de8e93521062bc31d6ba7f56070f8b1f9b70ebac82312cf15c48072ad08a7d9887d3a810c78c86dd63b1f551e1776af65de0bcc4bdb031bc8f6e668839c3c955d830c56f8877a4f068d1367d27cf54c6f17bb927176d0d9a21be63db5da68d8721e0a104fb6b3d3ce73ca4d80224fb4b6d0b2728736f985b234e70d4480ab8ecd9ed2d994ef2e9da60aa30d25cab1af88ee8a467c966a71e8d87b81ae29ffa3ce5739358dfb7c2e60cb6fa97d9e0e4a7bead943b2b5458797c557e593e133b0c06b32fe713846527b81e1b5d9541eb0aeb0c69bd1057b56db1deb5931747b79da001f6b11983a6773660c43bb001e89f16b588ae6ce2a2e04729da87bd7747a0aa8bbe8f3f28f658b8845fc09c6bbd6c845c08af35d0515d66f07aca39aaab8d6ba88a2d92e484a2c4ebfa01437ad5315ea50cbd7be3770ecf4afa8134d6decddff2660e7413d24b09f40fd0c001a01bc00df0ee7d3fd04c44972744d66bc309553e2bc9618efbb51925d89f8be04ff0d8424c3bd51395ee24306a87e903233412f11beb78c60755862be26a3660e44125a6cefaeb4735f2ab30980bbb0767574f178a0b31efc783631ca8a4164052204b0a74178ce11a50912e581638eb18c8c0c15095c2915302bc0cbc9671c0aeedc728bd01648cee0f03a1e3d0b3fd21de2ece83616d3d86f92bb971f79a490c9a12dbc003c1e02b52185257062d8bf7550913f86cd3cfb592ee282c2ed71f141494c73b4fb49c3b0b63d18ff46a0636225481398b872d6c8c250e90913c91226344b94e11379e3530370616b54e1b8aa502f0a12fba7533c9e7fa854a63830e840b43241030559f412fdd2f9afd309aec859aa845f3640c0b810a38fca92b852a4682f6a4b07f0fbfcaa52e2e1e3dc66f81acd406d59e9c45fe2e961ede3ba82035fa439ebae810e57ec23533862cdae1cd03a8f3f106bd4ffcb24e1ce1d69ab1a68ef177f50bc59ff62f9b1d26b940030c9dbb1483e8056abe89640621f55cad3591e0190a8eac0080e5f1518e1a18f60c9b2504658ddc9e5ecf5dc6c6d6340c828326e16984103f6a00450b7ed289b58bf4c6b240aee2b8d887db4f415bd298efd19102062bc6c807e0f74b211f1910ab795e0602195f4d462162dc90297e07fc3b39f2c506a9639f083746820d143e6ba0cc7a5cfd9604dde35e7b4d48cf751066663f96d2533769510fb7de444b42ed1d4eca9317c2312682498f76a0c7b4ad8833ef8785ce249352a2d9b4ee585566c902ace00975219f332e671b4c205c2f5b47a4fff707612e1834e068f010d4f102692b815d7f64464c62de47385f7be8a7b6dbb1156449381b4728e8e8d7bd49061aab1402ea1b0418721968e62f314e66f2df2481bf7ea042d765df79f80e3fb264dfbc401407199c1d203c08e5fa7c5cd06bb76232eb3ca6db357850739fd82cc53806848021a4bc70be04083969502f2fe636b28a79e37de672b64020fdc28e0043547c4bc33de3dc019aaeafbec08da0d8196c6d83385493d6b0b3ecdfc2eb11b829923e75e8ab06c207d5b71e960e9f9917adbc1ae265eba1e535051117aa439c41952068244bef40f129030501c132c0452dbca3dd9093a7effa9891fb3a6775f5cc1f2fff03c7f9cd4473bf69937b7023f80efc9daca5b822b208cb0be24d026d9b51dc283e5fbf12ecfa6e25305e7b2a2b4710f968c38359540d2e3b8881da805d5eeff5d631e16e06f0b7aae652aaa975de9e391e66ee28c459e61ae428dced98c42d4470780b1850cfa1eda72740613474f7f6c8dbbb39d9e1a89c2fc0d242dd01eb961d227795bd36628a005e183884322da459cd7e228910dbd0e6ff565be0f6da2a51d457d69be1b33930cc7bc3b90e82eba909ca6d2eec31afa9a41a57fd84bed391be814a22c7928955f9995640e2658f5de554ae26d87dc43f7b3e8379cd674313c61d9c00b259b6a2a3a1161c26b895086f9470374620bc2ed13ccb083e0203226497f8f82b48ad69e83e730b315d6355a05e7e3fde5833c994a0dbd1002f1f9f20dd6bbd3e53e1bc052ab027d0c0aea7b96d0b61e72a46f091139a429daa81e88377de936ce7d9680047d9552c6711a3896218ae1f45c00e8a3113debe203f19bdef0bfedc77eb3de9cd73ebd02efd7631bbde675d5abc779a55160bb1d6bc71bd0607226f3f55bb98928ca8e3c0d02f6f32fdb6c67512a925365813c280fadd0af7671fb476aea4a7d00b51bdf6be20ddce124f25f0c907322ba1fe5a3efe6df310a334f2b83575315452f0c28aae9b4a4fdb78086536f8ea489250bd0e97ac45243d9d3eb34b3986455b4a681ef9c267e20976981c5a7e9696ea0e771df16feeb3c37335fc6be3938e34100c1f5a7969fbda85dc79ae381819bd238fe3f0ad10e95abf1ca9a2fdb5021c5773a857587171fb5d06c74db6883c6c4abdcddbcc0df12b667ee127fbc94de140546299a262154f688fdd217e91ebad781dea3858967091cfb45052fc96e79b9ecfb4907596a513ac32f3b5bbaddb650f70600e0f1dc9abfcf446d503d71662a5ef90bb9ca02ad82b1ec1074cfa05d6fc99be12b5490b56a66a63f8e1f82c17801990657f96b96293bd767d8297edb5717078619fc9345c717333c70ea9d72bf080455f013a1f41bb27eea2f64254e44debbbcdd2a6249487952c1fba805c37f23bbcd8c77cd01fc96ec8ed8e7c875e7f8b96426dadb38ebb6fd7aebe57fdee1cc2d5735e60afc6e44f64c7396fe5b5a8ea97a4088b62174e2dd6eb3b8c0ce46d8444d198a9a1b7ed3679b1ac0716be6452f1cfb9b798c66ec0f60704ee5de0f4021372e3fc19e215e38da5583bf077c1e14d99e41163fd582513ee41fa682665638df1b2e7fa89db9eed057253fd797ae3f88ab26ff7a61717dfd4de4dedc2226407df3333570bd1d1b02cc8f4d7a24c35f357d935cae15ca6adc39cfd4af70fa00dbae18aa2c8994629626c4591309a5ae462f286f2455c78f5bcb4d1e0c9e80f3ce6242386bd4d6fff922166341d51b1332a657af519fedb8413c362c5ffd7abddfdb97c35f78fa58627c86ee9402fb9cc704fae816f70b243fa33d5c4f52a7059fc6c3ed73708493536a9b604e5dfb86c1421bd233984486aa347561130894ee73e4f94e5b4afd8577cd0d80a9d40c99f615d969b3d394ceb10cce29610e3dfa7d7a5a9dcbb4b64ba27443e840b015506ddc36428b674490a971a14707263ab8675e1dd19ff3d3ed15e889ed4612f36895a0ab049d7ca934b6f242acd20b03f9234f26a430e7772cbf560cf7db14de9d7037c38334d084e794983cd0cf3c743cb7f75cb103939b2a3fa255843e6be1ed13069c7255d14c481f78971416c2d48a437fa84ce0fac949339bb1aee4398021f6046d929e4dab95fa6418117f56e53afb07b92b8698dae995ee1607ea802702fe1070a826883e1bb9070567e1d06deb4529f2cd81cd35e2e52cc2d298eb9821b22465211ccc1dbd3b69ec89b08023387f8d75962b11148234791f01199d943d86c25a5d86cec8eca2fa93a0130a831f473fab960600ce58cf95c1fcc5a5d9429a8c24ef988a96858dfca9b548b1057147a4cfcefcb79e1923ee423ff37169d0a13c7cadf30c980fc12256ec759beee4b4a986b9c27b72a44a8ea867512212734da3d1c8963582fa2dd3a7dc6b59115fd18e035b149c04a54db03daaff68e5a20ccc29a0e75fbf31e1222d880cd51dc9fa684dc10d4a1ede1375973c25dd702748e775300b6da6604fbc4868ae1a7661ddf0238f0dbe4e3af96af43c7a4e085eb78e70aff1fa551d86b4e0c0ceefa65db7b77db724b29939432d408bc08cb08322c1165043332515d11db7dabb952c5098733b4a4ec40ad9d168e6a84eb7d6d46d8720e0a74adb5d65a6badb5d65a6badb5d65a6ba5456acc504da922320bc644d3244af38e2a7a3a19213342d6615809a4a4aed5314353d377efe18f9cb3e8eea71395a5ee73ce9f7f481da9cc48fcd538bdd136a1b428932a40c5585f5dad7758ab633d0d23409562ac7fd2153e8d89a4111a13bfd30ebb1f8e3f26835f14296e209f055ff4fda2bba7db7b2abb7f83bc91167523bd9f7198404f272ab32d8eae57acc761f77f8c7e30cf28a72c654ca0518ff510b9898a6c971923d12118f708dd51dd2fa432127dade89ebe34b1e1b834e51e6c379b6da8032009b0cd76f9baabd966b00788aac2351d68f02ba40e70059596613606c90527295a649b518f4f10075ff3b4204349df2afc4293a95de66dd3ac3d9d9a8021d3644b1158ec5b8d3d9d908e7ef67dad6cbdc3d75eebeeb57aadd7663ac519a8e7cdd0ebbadbe19cafe7d9d5cafbecfc3cb076b8c666d69d9eaee79c9ead5e105f3927adb5babbd53d2f87cdb7512cf55aa5b85dd0ea96e639e7a43196e3a657b4936776295f7b7eed6e16eede842f09f085c2dd9bdcbd095f12e09b67ee7630746d8f3bbdaeeb7c07be1ce8963dd7de2f4ff75c9996cdd9466b371282870d6ebac494a29b2959dcd0946ed874690bbb4411b923e2cbae5db60ffa35577fc4a75fcdb03c4f6a9bcaa1008923a618c3c51a21623ca099aaa20433e84046acac4a15aa466dcac2d92c22ac6c16139bc57a5d44d166bd18fe60b5c40fc31f623884173b7c30fc11662c4e4344995c667bf55ef86365c9eda9f1328d2399ed7e4e6982975ca14286268b619b288408226b2cfe794c10306cd7ee026e5ba34dc4a220954613e504181e10501460d7bf75669f58f2596badb5567300c40c9507cdeb98d991f318db5a98534d4b4f135f7e307ffa13dac130ee96ea275a7b3cd080634944e52731dc9895a92ef79c41b2596750df6eb80b247e45d31d53af98592f47340a4bd416edb483963163b054d8dc65d60a9082d4752b7cadf7f4f80471e0f4434e4c8ad2e09e4e348c609799890460ac214194027e98aa5937216287d1f27d140323bd85a69fe6cb5bc1dd755dd500b83169c21d4b8cb7bbc5e18ff91f06edd881762cbdedb58e2f2eb457ad0768577197efb51d78ab5cf8dbfdfdf2c501e7e0e04d1e987d8b6d0687f0dca6eeb762d002d12a5a8524bffd4965b46ac6726ea05579f274dfbd7b204fbf9ecb59a8520f69441f92087f48b2690efd493b4a2b67d5a6b2dd6de93a9d9ddd69d95df7b95b72b2bb2532bb6392b23b26355d0f10558ae1bfe10fec43927d65b6460f5218069a92125cd6463c529c2693c994dc701c4f1c6cc7b279ec726ee1816afb5bff0ce5caf6d72d2862b6ff2b316dff5714d9f6ef89f2b4fd8372d8fe43eed40311dbfd2794951d401ab3b63af17084d4ae8fb56729d35d9d9b8f2ab395cd5403132cd9f5eb0da7262e20a1008b2066f0c0243d393303539918dcbd1b020b1277f70f4c8daa3e5284916b820a3282202256eaed56342d0dd18509a06c42c47437254b92ed3cb4984faaec1aea505f57870717f7feff4efdc1d47364917f0407ca99dd1f62420ab4a875b4ef94eab6d1beb37ddfcb5dd074d281ccbecf41fd9928d8f771dcffbcebbaae52327f23d49fb208fd6a1802bef7872af879b9ca6c77f4deb00cda41f7ef972f16241b5fa35d4ea84d13c2a98f596bf51db2a62e94aa766bda25dd12e29c7290da2575cac1b64bead4ccad9bfe077553fafbbb209ee1365380cd9fa96952fd0102d69132a148f595545f7e5f58e826cff13bbe7ce9b87df93b5e07991d0749de20a991d31945f22792e566fba7e0404431a2587d3afa76cf41a26fd4877f0afeeeeeeeeeeeee1e000a34298cf5394e6b3cb6e8262009a8e5ec8273b3d17abaa5fee496ab076dbffecfc0b979fd2e1c4f6f3c7d6aab40441ce64f6fb4e9a3142aa4748a4ea1365a64a99fd025a01a24254b2dd5a27a333d39246d7258de39de67dc6432c7d8dd27e23f83b4714d06edae67772f7ae79f77bf225d238d95d4b62f937d9f26d12774c971dcbaecfbfe408e827aee97af5dd25b49a3a85a525a8cb7ee1d67ecc648ab1c76492a429dd5a839603eeb23d38ffbf492509bc688a80a5e89322583a5d645d3ff28add531b602bbb54af7def007eb3f8c71d7513d79329829a6947a5ef8035c853f30d62d100443d0022974799536fd501445960e7f68cdd262a86d6cb4d65ae36b43a4fb0b47babf6fa4fbd323dd9f08b2c2133e2b516b7691f9748b6108ad1bde54a72a207fbe238d9f66bee6fb92f953ab666a64f32b55057259911b55a087aa62c01ff1e7d72947dad9debbf0b11c3f5fa702dd9bc3ea0dfcdf0776e007ae40d64877063dd63831fe8169e4d0fde110ad8f2d03c8dc00d713bb55f8e55cebe38c2f4a91cf9a8a7cb66c0faf3a3162c829d8bb4b517cd8f433c963810ecb8685c3860e6dc74c87b66386937bd4bc118a057f47283a51bbecd1a3c74e8f1eb61eb5273958c0638987d2998b65b5295454817ac4889192a2a232c34389c6eec89eaa1b331ab22c9bcd764983064823fc817f65b78e1aad63dc51a24d9e4a951b5739f80c1c7b4ce911e5e1db0131f1b9f9dca42a95cfcde7e645bb84f9dcfc6897b08e049ab172f66a56ee6b843fc0aff15fce495074fc50c5b5a85c41928fe10da85df2806ac2f150e251b3b56cb5251f259f221f239fa3fa9a55d9d5878a0f1330ec01d5c3e63d6a3d6e3d9a7a6ce9d1e5ebf26541f6ada44212247d04c7efc61da4c953ff8f8ca2a4a6a8eaab7e8eb1dc49dad5899922352d9ad2d294d59c3d3efbf8252199a952b323db296272d4a4e4b0fa3a6ab62a58be0dbac81554304ecfebec48710549bae280e9187790680e9b4d9e6a54248312132535555f3b46a38fe5007796cc0ca8ccc068b027b0a51cdd2a83dc0e2d0333301a8c06a3c168305a267d642b1e4b1548c7cd3c38ad5fe56b016841f8e3beeb09eda3dd3fee20ed30d9994d5199a9aaaf0cd4ec1cf96ca7e806a3390ca604abc1a6c0aa806406b90dbac8f598528174d41f9cd3e57cf47dc9df31fadc1c563f87cf962ebb861eecd899f469c2429717e7435e9cd3bf4dd9f6c5793ed67d472801760a55408ebf43470e1cae1b365a357268e0dcccb0d12c3104bf95973b7cadd728727b7ad0e95450c512041590a38b65087e870e2b964674e4c0e1ba71c5f208164b153af1ebb228c69e25d012687df7c76056ded3f748234c74f51ee9ca46bc278ab9f2834f147357fe5e48f87de2836f248c85368b21c53910359a64d9a2463de3f43905c4ff81384962f2d2a439e7caeba0befcad514e944c265b610a6d3baeca766a44697489d6a88dde2854fd292796ed1437d4410ed79514171ce9fbf8cbdc813ba577ce69a9b5f30677a388da4ec34eaadd65547c53c7e581b881ecd7d6f182ae3b7cef4700ffa3ab11c86b53ef57ae0578ffbd10efbfaf47560f7e381e61aaa490fae047ba2a1924ac1688c3ec83631087d9ffc6b1064d95c857b6b909dbbfa4105ba780a062a9082590f82217ab4fc3ba1a33ad95d6b74d34176402241499dad6d26a2d68ade4696b1d1a9b47f88638908b3ea52bf0bf37e2c5bc5f3d510cf446d0751cc51bb4f7aba02904810d265004a617db6ae1c41ca3c40893c7be4f7dadbc9e111ca8551b62022dda627b7c7a7c6867a36d6b23436d2887d99b93da36876d7f6565d7ccadba6a565553a8be45bab76f26c554b32d6e25c627dbd668db10042b584921e2830f9246c0173154fd1163c18b6f858ab2f6ef0d577f2e547d49317f2e15f365df92d6896dff62908a1c94bd25c85463b7fab49245eac67a539c4d2d48eb78b32906691d5db8cb601dc14d3db08e333675ddfb38bbfbef03ebb8da1404e904bf708ae2353379ecdf30e68efd3a869bded156724e1e7b73e0cff059ef00f141f208f8e2b3c8234c42c01749d711f143d25567ece21c8ef6e67298a4e078c551bff66b0a5d2f268f7dfbb00974bfa049b6c52c6cfbe506778c6dbfecb16d4ea7be6ce80261d74a7e7dd99bbbb7fecd39d08d5b651f273d99d9163fd996c9b648988695b6fd5be9973adbdad7f9689a8b6fea945f2df47627fed45b1cb191c3ee06b39d6dec0b06c0c99c1477b80bc0135d62d96aee8a547f6ad250fd1267cf79bf04418d43492c1b81beefb86804c912e3ec12cbb0ec8218f47d2cc3e37c3a41d5fe30d8dda9f1528d37773f1c6fd50d1f6274896566be5c01b1efaf6a53a8fe5dddc7325c547fbc3faa3f4653087f17e7301a16f368dfa82fc92ebf59f9218553fcf085881f862f92656689cf228962949cba54e2d3cf13cc159b4695b476a3b6da145aa5fee4a7bf81d10631501ff407dd60e6818e7da7ea4feeaad917cbee54bd58cc9f5b664e7c1f0c184bdb3d3ef996993f73de169d2ffab703e9a89dec2f8374c4316a617f1e58b5bf1548c71b32fbfb403cbaf0cf271154d9f3490465361d5d983cc1cb906d7f0df6cffec02aa6dce809a49caa38da943cc1c8e640ce2e224a0166d99f06e9386388fdd9807874753f9f90e4b0bba7a3ab7b9c9dff9b01d271d5647713678a62fd70e3713e2151b2bb114af839c4469804495c545f57d4414f272ffe063568a88a491aa73585f00a93e7fe7d1b1308b34061f7af0d1bbb922596c186c230596225fbb6f6954d9efb6292d6f575c570c4b8c3ff652ccb58e64038b011134cab3f77dfc74b514cd9b7c453f6c5b27d31d2beb8aa5de227fbde2f716d5f6cdbf773876df5e7de6b05dc495c3cd20775fda97f1fa7fef87fb59b22eebcd7b2a27052704e507451eaeab5930bd47623b63b41419b4fdf01f52979847ea55f8fd4ff48d7cf1838664d8203b93c7f220ee4f24807801f7ebffa7045babc23ab074997179b636b7ced3fa7be7135f6883968ba6b57b392afd26a67684bfbeeee42e66da87ab6d2faf3ebdf5a69add59b3c146356f96dff6f86770b01a1eddbd083d7be7f6bfbc35e2f9d188b2e10f0c65f3726cb07ec4b7e9b061d463a01b65620dbda3df4b858f55ae55a16a8ac4e167ac82b3d81bbb5f9e9de9b1d46438b33dc597c5d475ea2ab42adae83e7eceeee4a78de3649116b9ca4d9220c1458ca0091f40210b32e4db1b2675b7b655b25b675a2b22d8f2d676653aa88920554cc3a31c132c66808a332c088596badadff5daf82249c0755238907358574f8b02decba7d0fd5228cf674d28245efe9a4e5c976da82dbd3694b6db7f674da826471c531d24cc9154849d702f4b35e887e16a9492336e47ca298fd4250b4218f301d211d607364066943466da24d6f689d2c2794991d8ad0ad59b3664df7de5876d9c748bf6b8dbea4ff161c1e628fbbe8ca4063e5aa458d57b594d12255df9d7a40d76cfa816f5d5f3860c8aff91e04089dcdd6f619459dbebbcc917c53a7874cfbcf1b63e81e138276aeaf7f1ddd33a5cb1935c40224d41f202370d0cd903eae63fc5d62e8ad63336d485b5f4d3b7b00becd7fa3cdb772ced7c0f95abf35d61873461ae3579174ad38630dab26cbd7fec2fa0efb6cc6cf8e45aecddb904f95282de44197bf71393b91a86d5b55dbd6a91b9c204d501f34469cf1666cda43480832c22740a702d178ff9d0a94f337dde78caff11d8643de90de6daaf56948da3cd1b61fb2f40fd53a126d5be46e16799ba01a84b6bec2909cdf371db4ff91b304371d729c5d7e39b1e8ba6a9ac9601081981898e492c8b7261ef20064482267450b0eb92f625dd7755ded3adc7d0d24ba6cb5c0a89034550605597820438d952c57ac22e0a4c995284aacc031117377f79e3274f9da3db854a22a5364c43039b303d20b9d65da5dd7755dd775b5ebae773edd7d8b290eb2761d7eb7dfe1aec3ddd8ca49a34b3def9206a460c9cc87334d922c9144134dd068001425502dec00831360bcdc20061326b8b202cd072498a0d59851454a1326baa8a108db1134b6d8a205aa32686a10a2ca9a2b49c0fc1005134f43100105092f4c7261a48ca6e88e5822089d19a868410baa24537039e2dfeb1527ed911df9aa313c63fe2d28364a5a9862885b9634606243bad97546ec4e092bbbfb0eea8fb83b1dba94a144aa488c24625dd775dd50d791b6c3b8eb7037963387871290c3852e7502a6d0cd422482335c64f9a1880526b6a0724341ce0c2ba2aed40003134a2871040736cc9ca850aa628b1d6e0d892438e84aed0733b42fb8f8709bb62a22083370932101e505882ec478323a798823a2b036700a96ec4065ea40ddee1a4c109fbdb703d588c5f8fead459470dfeeb9cbebf686f56b73dd1d768140b3d74b62aa41922eeb2e81fedc942cefbd25dcefb0bbe32f0163f283b92f69cb29e46848d03ae8fe4e66df28d029a400ba665b12a34037c598fa98f69ddec903d6a7fbf6f0b42482916d3f8770e9e75977a5f4f1ee403812c2fda7a5a21a7e40b4edc6395440477d85af8296ce0d2b1dee74f0d8417dd113e096844d2801a9c0f0311dad9e24f14ee0e4857c8224b3eecc0c984ac98d3ab25d8e7c08488b8911d4d6e32abb4b12ebbaaeeb3a2ad4759d98daeeac0e6ec1e26bbdeaec00f10df674a2c20b313d6c1b2adf7420cf6221450b1e4a9053051022c84256772802e389db6cba22e90b1c104f525aa05292c9625f680a8a25d1a67f69ac92ae42ad2ee549d39f3fe80fc779b552f47cdf96fa5069f8f2a2e9d310b2ed800edcaeba3e4ba902b16a2c9b055074a943564d0c3d59b45db26a33ccce5125d3f35935cbaad59fe9b44e179d64d558b54d9f65db4375a8eac0e1ecfbdb79eb0112df1fc814c2ef2f8ee1088ee3ebe43067507fe4f4e9c4ee8a588d16ac7ec276f7808c499dc9d3756fdf4a607fabd54d60dfb75d8749eb7374d23e511c0a76cea74395aee03d3ed5d65adf9fda6abdc77c6ddbe3b55a716d0e74bb3565edaff7c08a817d611800baa755cfdfdc403ba5ff690d3ed081563f7318fddc7d2b31b401bff12ed1a1077877e49de58f06a883630728c33175d8a864b7e580e095797b829b46e801de99cc636b0af1e0d9d919e4b8403653dad456d361c1c4edf80a3cf7d7e140079dbf46f8a3cb38bccf39c7b176203d661a5ec31db415805726f4fb085de61c16e0b8a0a56fd8684101400639b51d1a351e1e336c4a51966abfa32cd96a4b516cb525a570cc2b0d6c7a740f166d41c1c1b169611c1d00512b21394972d5b4e724498996e4448976932ee97d02082f6dc945f2beac3433e996205911ff50657267ee03662170003f409ead3f368342f93fd077c21f9d0674f25070f431effdb29557b7ee2f304297fed8bbef3bee59933d376f0e6e10e08bac88a9fbef439ff3de2e16f1bfd0af1f8ef6bbabe4b029b77db180e3955da32374796d3e433c85e7892eef942bdbd9dd7895c20f5edb0260e3f1ca6e5185d53ba5c39684ed02540380b6025d99d17641d11f061a5c60814e053c7876320000ec85414fae3e513e5a1b62d5af33beeaaf13d7ae4e7d4e12423f8bd1b5d65a83e8d10ffd1ca46ff476f354b7a67d165966912cf5b5d4d2d64863e0d0fcf93386501a0dea79cd2fa98caa35df8656396c929546b1eaf745cba9cb6c97383e2d1cfa4f57a8550e8ce5eaf6c404af26502a0fe81d3a72e070ddb0d1aa914303e766868d668921f8adbcdce16bbdd20ee8b985698a134db198ee71f64f58cf0b88aadcd48c429a19c9a0704db7fbe09e385dd38daae044b552db37f7fb13551ad349c3c5a4511fe540396bed9476a37d5425298902ddd4b17893dfeacb1f00686e733a6e0b0fe273cee9412a509edf33e44034d67d140c3f7c4a29a55aa4264ca8570f142d8a24931d3a6999b24510443102343ee785d4f896485252088dcff91cd208ced398b81d86afa54b484508c96cc3ba7996288affc1dce213ed702c7f0fb9194bba6f5e1cc31bd6dfb0dec6d6179ee2876339a13870123e1d4b22a5d18409f50db937769fc732c7fc75fd197b5ef375dca6662a3d497499b7ceeb494ad4ae0f5a27bec67a5230f3f444b5e7d393988ddbf3e9e9b6e7e33d9f9ea8ecd59e4f4f5236bd8013154ca891deb2e78bd9932c525f333c3dd9b0e9d313934d9fa400ae8c2a3635787be2ca90614f5c194b7bfe991dee8923036ad3999e4f4f454fb8aa3d9f7053b8a83d9f70381c13db66cf27dc944dc959814fdcf3098ad9a64f50c876999fb88cb1e9e76ecf272e60088511f3e5c916dffca933058dfe0728e8fadf1370aa36390e4dd8305032e090840de3c427c4c54b843ce09b8488d4615b0e530be3831786a6c3ccc2187d60aa3278c314217dd61478828c030c98bba713982ef435182c606cfbeb412bdd40830d18a41b5e98916fcce0786568cca042e3080c0b3644a9015503abd136564b6e581715451647921638a0a8a0a26c5038bb25a98a4d3fb4926da84287beb2c3f6504a50b45b1214921e2d944e7a81cc5046339d2f55d9822f53f9824fcc05a0e705b462c61318809e17fae013e017237217da17fa61cc7cb9a17594b3a7d317a3eca5eaa3a299060088f13d9d9c98b1509f01d8f41981f774f23265c340ff4997f45e8e567b3a7991e5a6944ed0d3690aa829703bc3a0c3291e395168407ba6b86dea9afa2e28c1534dc9812a90c4a504392b676460c47adc92a86021440f2e10119b3f7fb6ebe7392f9893d1a97ffd0711ffa360e8e0fb832e8cbd4e264fece92485b6a188645028d2f4dc753bcc488e248c502c8e9850c5c0ff6cf81503347a3a8cce1ce672f1dd4921e2b3482354242b4d365dc441eb0fff48f8abaf4242177609117ff54734e9c2a490f059226984a974808e2bb4180b0428bac554985c6631f13530a53614913c32e459641087d50f7f7299c5582f3e8b2489388c831a9c4e39acfe8f8a830a2f2f3123dfaf1e1c8da84097ce8c0087d84732595045ab4097c88cd1456c85a5deeaab3e8fa52d65bde57033fcc85ae342973557cd9415875d99fc6bcd816acec95ab5d59c124a0e840349ab2c9e314a2b0e69ce81c010fc5644681cc936fdd86a9cb14f93d165cdd9d8a69001f6f7f5ddc87fe540e0bb7e22d60b119ff59e470a61bd268dac8c804434259757bf5689afdf01acb761bd66912ecf9bb14b6df4e6b699dd43d285bd858f746192e22814e9f2d5ea3fd2e5ffad1e7c7005feeab7d0bf6bcd4d587def6235f5555763ad7258ad329256d557c57964abe2aa1a5b737ee44533669928d7d781fe47ebb6407d5112e8a465760b0ca93f3e4b220b90505ff6abb8bbd27a5b91512498c0529a4204a0f446161d9ee8fcd2a7bee807d517adf93f3b290a364759fb8d41f5e528d059c5211dcc280b90507fc217ff3559126ded7dce5a97ad9dc9d2fbf06dc677d88af543ae47f1bd1a869ef8ab2c8a9f37387e506c742a107d1b7288052a8c3e8e3752a13f4acb7060085d74f97a01993cf58980a0657e710bba4146e8241fbbee45b76c2e9723921b920b02a3cb9c1c7c94233f220e34348488030de5a0724153e832ef1ca61ad01faa7826aaf96664a1607c4af5a7c4008329e46f2b92a4292d967479a79ebceeebde5eafd72b686a031f578bd77d1de9f24ec9a6ee575b9df9a64e79b5cdcdaef3ce235d06fd941cec6a6974061243af219de78d133117ea5249dddc8c0b11382e45c8268f12a5a5a827b54a8911d3a5cb162cf466866af3c587f6123d5dcf0dbac44d402a7dfa5886c568cbee82b4c5d06c0a1f48b344db777cadb7aaccb022ea93a23ea6a690bd5483f29620850fa4c991d091f04497b8694e357546e41adcebba1170130426154fec3a71403a2036e812378145a0d114b25356a64453aa95442e31df1a5d521995c9681aed5fce1c119a52ae92a1cb193524bb56a3cb890b626dc85c9bcd661bd2b9e5822eb14d6963255cc3369bcd26457fb8fa63df66b3d966d46673208a4467363ab3d19a8dce56d065b6edfaf9498eaac105e9390cff2a17aafa9d7a24daa373ce40e4db7e1245ab54d52c96c974b6e490cd181aa9fe787df7926e0fc1078a216bd970e99ca7f45f9ad22e3fa9a6a0a6666566572a8be29b3a9ff893259247c4078f7c9a3cc262fdea8fd8acc66f7451f185b05e7cfaa2f820e9a246c067912eca7a21dfb39e86a411d6afde88cd7f1fd2983c19045fe713183063e26bd28891a9dd0893109b120aeb4919eb59a40a47865ebf0df859d701e4b2c7fdc19128f636a3f87a643d1d43f04dff03472a3332c461fe209982c3fc5160c08c9dc080190ba9ccc6e6cb99b3cf7a5d69a125673424da4c7c7064fd0c45f0fea3a34776809e50e90d0523e28b24ad39cc193063455464b186fceb8b218dcd90923826b476d7b9ecd39fe1255d960c811c9289ba9ab1d0ea17ecbc4e7f22a8f3baf9726786cd973e3b68f2b85b9b71ce57ad20784854436d0530686882690a2b62b3c2565fc7902c6233d25d8d6cfd3e9a00ee1950707e060ea95361f66d46ba6ff843874834786d9eaff06758041cad0d53d8aba72f8e73bec2f710244b237b455a9c51677cfd11dadaefc126b096fcfbb7de647177af2248fd9941863a00f23d41bbce39a188100197bc47522615568188e4ea6cb73411affed449ebd576a1d3e20e8258bfeed7cbd8ee57aa20d9eeeeeeeefe938b2b479ca9626326045d1ef21d7dc61e7f4a963ab90a1918321912e854201bdbb1024a6a8c2deee92445d5eae17377afaf5c5f157477b7d6adb5d6ea3ac5c5f75a6bedc5d6e2fb81711fd4ee6efdb670c02779befce7ef40ed10babe5b4a9f4ed14e7f70f79d22747dfad9babbaad1e4a14f378ded4db568d327da55b4d0163050068dad0a63c989354be4aeb460656e7172f2c416c51563aa545d3442a460090f3b0cd103166b88b00208a918a280f1421755f58c0e2bb22bc46ce982c68814be60a1a20a2b5fe0e0c4991c8603c0c82181a1891460314410275344a191aaa20cda19d1cc0c25413ce103172839dc5af89246891564bc08d3c5c148a20c346b72a86176c44252103944d1e4650b2aac2f66134740a1e2a68512499a2080cc1819b850b1c36c8be7050296a449e26987269a8c70218c29906842022f929af872442e48d96104512b8431430c2ba489020832a238220a49d8c5ce0f49b620394ac26136c9c882ca0f567e18c3c90e980b2750a08397309c38a1b261833267ca9c4982448d0c9a6e8b28a6386121e98b2a3150d0b0b4c5d4122d0731b2548154385dba60418b318a6861d45052021bb4d0a2872d569c01d2000c2e3488a9e149154f5e664fa71c90d0b214050f5c9634a0854793e18a292da4712244acfc20a2206e4cc4049193c50015a3dfd3b4f39e216d42f39a5fb24c2d16483345ca48ca940f68ba2cc25853860b293e3c71a4040353524f60400208a8a82450f46736c04d0a2427a81c92a4c059985ec89a907841c612150ce4f9f3025d0aba28a5618a818c2a2fa22863d39f51a4e182062758c8c10b08a634e942b5a4ca122fb2f8a03f734d22c8816cccc73743c9099d41ab2d1bae9c6db8e8f27e47f5a7c421f3ff6a1d0576c613f7c688297c63b31d98b3d63d2f0702e2b0fa2ffffb0e93a2aa3f75aafe7856e3cd2832f3ab5cfdb1d75b3d3db6007a70d487933da8002ad0d7181dcbff1b29babcb8d514adb0f2267d6249b42fd237cba9c2c1e5f7f05b4c5ed9142a217cba57e0686b9367f5e10bd5b05a09d9a0c1ff849274f89e087ede20f857368570f6eaafd114aa7bf5dedbda14cad92b121c73f687696df2dccfe183f6cbc27df07960c1c622640fbcc7ae10f2e3cf227424c629536942306983dc99c3ac06b48aaf56b2bcb76bbbdd17826d0ecb0ebbd9138426525bb67daf3f63f48402ec39792c5955a095ca8278ab2e77d923bb37cab11599b11d4b9b7d838c82629e0553c8df8d33e67527a4b02d0d45e8f60658738e2774db9225b8eef07fa0a35dc657f4e05a387448a0a71316b75db6b0a8ed32b770bc8e2b48feb9855459ec5b79abb1c885a0587ea31cfb3e72f579f4880860cf308499823d492fff073c8f9cfb32cd5b82add665490f6297340aaa3dfeb27fc73d8d5ab1fbdfddd348c7ba3d1d8f463976dfef57bff2d347ad94feb843c9ef84056d97190b249ca7414fa731b8ed344636a669572ba0767d9c6a43215516f3429c3bf7b7f545f30f790adb625c4291ba2dd901dff36ff60c43c0604f12ca07ac4c4665b258fea11ae69bf3c75aafbb7e39e79c28d05da47e39e958a4ceb796bed349913cf57867635d7e1f33595d0bf03eff085eeeec58d4bd2eca3f1d8667ec678fffd2862080ee7af7f8ffabadb5deb0fbc953ffa7d074e19b3a4cf78e1364eafe8e28806065b2f9ba21b81d7847148450992cf6fdfdef1be98479207831f01b29e86d8c6deee85a40f8e08ff023cce7c086128af8f67e4e6da21851acfef78d451f3816813f1db6e280a64f9855bb886ffc3336678f8e5a0f88c071c6cad6aed88e370c41003bd739a787a2477e34638cfdab1963ff72be5b90bee9404cf63d26fcf6827077154227ec5e9b3dff137091d7037f1d3b4ae4952a9436841b3ae7ac57a41db59d659a239de19b3a4cfe391e99e8fb88c23bad36682b8462928e60c47442d02ea1c8268ae1ff4ea0b408bf0b4c2704ed22dea3be0b4d1f63772cedb65fc3106e6ea8005fb366bb02f2f5e9b5567bc5a2caa8244ef555310915b28100000000c314003020100c07c542b148240d24395b3e14000c76924676569f4ac44992e328ca18630c228000420c00426066686856010ab84147f5bf29e40739a288bf58f6f3e206c2ec85a9cdc49152f2c85623a5307f5d698740a5a34f7238bae1fd53d60c1e3814eb6c756c84e35ee683700bcec65c7c19b5c071b2b9634d4a3643e8bbcb19375c399aa46f71aaae7f9d973a9ef3b3e2ec0a706201bdee2ccf801127628a477555f3a9b99ddce46a179a67f424207abd4555e34ee4d3f6ad50db97dec9e8245f25a595d11dc3de5e06b24141e451815eb28c7ad279bc8cd8dccbc6e160cd8a33758ebe6a2f7ac06f6027935ea6c1c669463ab32caa7269762cdfa34138ca8ecbc9ee36e9e6141c0f430508ba157e6437c3eafe00e095e714c5b35c54ffa9588ac0473efd332c7b36270d76f1bfdac5ffbab13dcea52054f1e40cf317d04584d6883be108f2136433c6437e8c274a75f47fbf21072912fee6668cd24318171fbb6db89725cd2153e85e2837851bc54cfbef9a6f3c9e257b2887dbb6914ae020e2245398be0b8bcbdf6732317b7a2f89ef92b5e7c53244adafe37b477735d1ee8893dcf681e2bb252a2e0878177ee41bb7ec9c742065cf6b593831bdda386f178bd9cbc280e7c3f3dd98ff70de591ec6e040481365db0645efdc741f43a4c957169ede04f1d15048d6e4674ef4d340a4127a98068492e842fa436a63a205af0ca745bb9845b6af47d613c9561ee8b3e8e1893e1b3d60f696e0a0b1f1c09ff343b4895e55c606766d70591160a4b705e224a71284c63d92053edbef334d605985affc50092cfe68a7a0cec3bdbb494a6d94f614aae4a70ea0341c25633f1bac6f0db80fe135154a97da1801c220334b8b267554bb433281b2d3e96541ad5f47dcf291b7fc7a11dfa95b3e4d2d8f909992f46f413b65c05cb69aa5479c7210ad709473aa061f248243f61d2a41a4ccd2801cae1efde9181b679d8ca011f9e78f4e826bb07f5be094574b1dd50d6a9dcea3080182c4d87143e476e7ded81e3d91498152692a5e37d445ac93b647414a6486c472daf23cef58c7926c5a99f9f7a192436aa93eba1024e45d13f518e89e98c2e95c2b65efd0436f471a8505b9c32bdd318ca218f360ba8bf25c313469493d1a543b2450f31013f08eb1d61329a4cfd39e24c15e3fbafefb08692a4e6aafbf37ad42b77a30ca561bc55eede9e183dceb4346bcff97f3d50614d46bbb9e367a61963582c210ee327ac78ddded6ad9ab03f53f3c07dbad9f4078057eb7d4ad705c5025b427fd8a3aadf58cb90874f97befbfb66bd40c38a58c6499187c589e1605fd0f0a51b4f3f45c5167107b6ecf5d5168c4b42051e3f4bc2ebbebd3c364341c14bd14ae277123fbb7b125fce0124c1cf1f589cbd5f939935363051526d4bd7d99fbe007eb32a81d7f11027a3fd8f1446ee0043eabb9cd393762daadad132ba383c87afd70bb83bd84102b5c48ad5e5a742253b4a6dd237d9afb19366a855f9cd1de74f0a341140b377ee616a0f78dc3fbf9dfb6d526ccfe5538955262fa214efe1e140dc10cd490ebb554341d6fbd62097b373a56dd5e9d66ec760070d875955df97f17c88a263c6a809f431a9120d96845b419db162b7e97fddd9c116419a8172159922709d5e88ba8b0a9f25652a7e79452154744d783a01ef777bcc562f7225400ee7e6224a316e20b2ca729fe4b9b3322e5460e5269f130df9d5ec5807006cfe0efc0feeeee4f65a387ce9681ce82d6a074339cc0946890407e3f60ebbaaafb87c07f98e3b9d382ae4f51729e67f0c0e1b90c13692aa6bbab37f73bbdcac63b5b31e03eba4705a1ce5a01ee1d442b7f0010159c092a0b5b4b22ff6a47dc5517d26e2986a8e9ae8196c5a0bc172050ecaa2e9b3023b78d4c45658c4ad404f2343b9b9d81646e3ee4693613ebb7e9586bc9db123ffad0b73a7a3f0039c6279428a0472499d8a54539c244b9c9aac7f7aa5a70e5d7e6a06ce931c7936ab115fe88b25e23edbc190bc0d3c8b32938980d4b181cc08c2b4de62cdfb4aad1dd3faaf7c70374da807e0ef4de05e16a88916403a93318bb783440a8dc3de5f4145ac52050efb4a5e2c72c35e91e22abb4effcfe5c3f469d2d7d56e5e806f330d075b22e817ac60d620aa9792a6e3debae4c496d0c6e4ce3d22d5c3bf4ea087fc4846484fec7a23d49e6d03258595200d46eb3269e8ea149f6c5d336a0897d72fe62dfc27678d80fc174a06823979cc857522214020c9abccadbd7274a9fd850bc8a52589ef3ffa7f486f2b7ccc8cb93314f912107dd31a4d56810c9bc7a59ec3d1c80c1c799bc2fe865e3db2bf55fe155f1a9edc352563438cfdede8dbcc2161dd255199b44b9032971e15065f21f6685faacbf89b4470a7a6ff47505bb2ba56ddea3d3ad758dd52d4c7c7ad25a44ba37bea2df63408be28710012d5f6d4b0f1bc067f2c481d133f41c6da768d0b84eadf7bfc4129ff022b2b54af3f4f1dfa1f248e2023153aaf8964942ba5ba253357d7b8e353a5d7d6809f4d4f0cb9b96455e18b591027b7ddd50bc4f502357a7a6239786749045755d1ce1e09b33299a2bee3a3e4cb723caa325888a69519e8ffc04f55d9054f3d0acec8dd3a2e62e4112666367a98dcd94159eae1ddd9712e04dbe465a0bb164a187f5388d81e7acc38ca38832a35ca572dfbd80746aa310b3d4d4fc607dea1f0443c166d540466599237ff0e67e4d06b3a28a69f747974bdcda227674f01d78c3ca9e2bcc5255842cbdfba62a0e5c8df4d3a7a78d4a76a0766ff777a879e3b6e16d3b9e3a316fe0d424b138b9ea46d2aef073c10bc1848be3426dfacabb2bfec533155c3fb31f146b099436631aa2811a2e9b86048cfb7b82a654db1c8db3d913cda8cc2ec62c7cf7c49d0742fb9b27667a03c3d79dccf976d0d5d3b83f7f4ee809d989b21ae4b6973ed3cbc23e19ec730146ed2ec3a7af39cb1567a04ea05adc8261724876dd9be7d9bd000e9f43def0d98f6916acc1f65ca95b21852529da9464f8317aee9ffcafd0d35c23516c91040aafb51eaf0f7c25340c393fd29c5b9cd9673a699775908d9a351a71d9d30e217fc667b0664703f2cf13238d0a0cd3f6f8d362f819d0db14e7c64a1a8f1dad0e09167931eea9fea7eb4fccfc6c43dd3525e88d7d4c9a2249bb6887dc313c4ac40a9bdd45077d95855802653c86d5717dbd9c3fc67a543ca216d62d6b6f2cf6990811ce15c892ec15bf35561ae33d9510ae5bb2d87b5e09848b4299e0b70835a6c636dbc6d626a798524d4adb0db0c8d444ff0cb1ad08071fe63aaf4c74709cb1fd3e79145e7eab8dfefc02298feda7b31b2a39bfc09ba767e55b2461f58578d7245bc375f3d9c7bd9fa2add836177590f4d41fd8befebf4935aebb4386d2668707328b609fd42f55d2d0d9ee71625f59704bdcd167f63f498894989d19b12cb14fa7f1163ea405d3c14770c0e474badf272e345485511b5f9e947b03a145878c861e39255fdd44c37664b03eb46b81a7cfecf205247580ba49115001e7529ab324b3e127ee402283d3834abf37d87c5758a77f4ef71c0113e41d622fcbf292e9be95b08a8f0c03e70e97e6e39dc9e9f012e7c99f1f47d2862df2ff614913e81c1bc4b6651920e5f88b0f967bad06f0fc1974b0c4f81a4b8ea913aac8836adc3b55bc425d50be0b328701ce594f590af249faeb7459d23e501beaa570fe1d97472df5fa778ae9d5e3fea9843fe76bc40e4761229a05a2eb0bc8e6e722e75476bb98a1bdb56661ce53d1366722abb79db14f4184ad1d34f81e51583bf6e44685ff46436b913193e420628343d2d9ca8404fb734d4eb6bf7f08d296d5e89c0a3d253b6f699f655a54088318340f846442f71e1471ea839ad44ed24787010631e465c027f6d27f4399e4b45bf1f1e0936809befcfcd193952612be140ae9dcce9d473d7d43503467dfcc7332ae81003967404b394f252696d963ad9d7762d7ad9d35efd6a21cdf7a4a6139ec7b833ff80409beeef104125b93dccbf4d9142f2f6f7f1b91d89b3e363abc8c7e767b13d10fdbb8f3db89cb2e9243377f42f463e5b66211fd44cbed4e843e6ee29ea459143bb9bf09d8e78d3ba6c845b9197873e5900a1a50212446ba412b08d72676ec02fa33ad1503f5ac7f7079984574545a6e836e2ddcbe8a6e94bb41b71677978a620f0fb91d74bb941bbe76797572f327e01f36f86995dfc959227591b457f80d9198e5c31a8ae847edb97d0d4c94925b9dfd7ef8165bcd38a2ad9ed70cc982686453155f414c7d41bc63d267d8df46dde75378e35ba58a89ce5f91288ee7b6351a339aafe333d1bb4e782bfec51bce886e580d67b937ba2eb0923edb42baf2f1f22eefd5e4ed331384db5c81e75e350cd14e834e1a852490c1d534b20e13a255399dab9b242809266abeefe08ff6ebef58f904b6fa7f2a1498cf703cad64e71ea855511460bf0061d9b29c430b35130edc60e509453ba81eee4c8874aa2db52c3e04127caa743fbac4ab0d536566dc3bdc499a6aed71cf6451122a912f857ba46dd49e17bb6f65fced1dab1c4db24db8874eb10251a1b706aba2a3dab77b13218c04475b09e9d123bfd45d1a2a32529fa1c5cdcf5caf3a4fb6feaa682d6bf84ed07f98a40408d95e7689008afe284449a89424c1d7de156fba4c3da2c4bc867392a1ea63067e66fefff57c5d81f88911f0d410775be2f0c6ac873fd06de47bb16c4a1367fbe9ebe986de20857f793478599c50244fe1574a5729d6a90c03d39b8abf6c6b0c39b5b16783f92c130dc8cc0e727cfec07d63c6b0793d9b6b2ccfc53600a3985e6e969270475fb80124b638df5c5795fd28bfc07c30fd4f2107ad3712d81e40c725b775170b73346db09001470d3d34b858cca2109084cc712d2000717e4d8b7cd1d997b8e4007ebf10d2f7886c2ff3abe2fe6eb8c94aab49b440fba83e1dc9825f309a6acfc3b408c8ecd80724182a1d4fd12533fe572c2cb5f029b9f2cef0c68a4b2ab01debba674b604dcbda2c0611bc22d6844cf77d7f32dcb0d9d5b358849879cafa2e2dd12fa3981e88644a45af65ce6a32724b0930c915732d8433c132abe7aa72d2b778f819b3ed5e1227240158dd120dc06f83afd5e6c77028890068da2ad5e30e8a810a054b247d761ff7a988d3a94b94e215c8866e477804f0464464741601c44e22281d1b730cd60d8881d925634481176b8da1779e7a8706443314b3679484045c1ee96d2eed477494bc0712bb7c6ab98849824b579d6ab2d75b9fbf9c0143641da1106352454ac5ccbdfd82b97953c1d79485203ef846923b9252817d3372ebd98802c9604374aa50a378d33116375af05aef69a4e365727a7477a3f04d7e4fa082ccfe540c209426a0c0a1793cda4e52467cf86fa838f9c11afa7806e2fbc01836dac9af11920c2fa855b2cfca53fad2994f596c79edbbded7319cf10224d4f55518a13ea1f6800817dbf0ac6f341b2ce4b7f8e178c0041801a14e33d342e233296302415ba722d110416aff8b0200f4c95a199e8e920bfe253205008f25c1d3a760b03216250e3bc8b0c5f22200fad89084b4c0a35da28eddaf9d938d60e6bf2a1efe52a2eddcf621c2eccd9d984a725b17656cef4252c8b4fb50505a4bac3afafb4c2e389e082b0e0704989f318859377d0ae7fa8ac62fd3e89c1cdbc628a2386db7a28a308108d0a92cbce365d7341172d330f3795ccd8d0b65cc6d36a4595cf4d69061ba0c7d7a8ff3a7103d175dec4b59eec4a0e8c78cbb76cc9ae9047439e048735c3b263b8b4f483947d94acb53f99dc1346352691b1cf1ed5116427851690fbe0b35378f55f2d025e0f6bbecc83e65c86547beccd1be6b7066b0e65423789f2eb7004708474f0c22bf5fe13a268e94a139183db4abaca2e5ba0db54ace155491fd458c278c3d6a2a684182a0372529d246b655cc74a675d3e1db9aa74e34fa46ef3c6756f8e9f74fa6c626dc133546b7d002b9015286e2d8972f92dbeb7b9a575f5c65203f1456cf28685f7d719bbff2388000ff4b05778d847de5a6c28bdd94c560918e86fb14ce1085f3c9e131c498d38da06b6167bd1dd2b15a9057541012475a5d5f22f46acf05e9f310c50d11b799736f658f78cf3f5cf3db6c090a8a898e5148a9d4f82f7fbb00f3f45b445f5a4ad00f138e6e67edcd9be5c04156c405db19dadb0b0ea3dea86cced164a2008d0a33c3708010931183c41e855804bfaf0d4a4e85b0ad9add58888b90c62cb6f8ccd61a71e1998b3c25b9326cfa80628f20e87cdce008a92593d60f30abcfbefad76598a792aeb1f3ade22dec589b6e893817a8e1ae02a25a355698bf893724faabb9e6d4b28ab13588c64c433426d0179631c5e45da18829535e8dfe1183e2b4ac44eb47cc91a7dfb92d47bf61012ca9d8b93c056b72a9b7bc56022d768c0859ac88265fd594eee8a82219643e4ca8433cf7a68e0c292286a6d5ded8048ceebe28b0d317dbffe2bb665242eb0f1a69655b54dff0567df225dba1c9053cf8769c39bc426173e20db3813a1bb2f5a427dc37dc00d4804f0b7a0b8f061cd517ce07ffe3003cc4976d1c7eb2e280c1372ebf7ee79fcb990f44776f3e5e37e642a1c92940b4f61d3667fdcc09f0aa13379165a5baaa867db58b25556cf922ed838fc0dd202e6b000ff916a4d48ff5c23207d38ba262cab4cff6baae29128990706480aceed12c95b10024977f31121db56535db6ce6f34dd78eea24e2c03ae90dc1625cb2082eb6b01312d2fad88a8eb3d935963899ff7149b9fddbc067ab507be4cf772b8b04677937469485662c1d0abcc8a983d55c8fbc420d388706dfd8921aa232b0b8feef723af07218e0b124c235ba4bd826e6c8bbf4fcea21097e11874b99d55b2d3b5fabf11dbaf7e18e94c38301937ef67fbb6dbf105885894cdb0e07adb7e9e85e1a459950e063e906b086f3554836f3fd4ef81f0acc02caed1ef99f268cf50e3b9210a4ec8ee358f363e09f8322f9984a551ee1b9afd5ce1b07156f1009f0d549d7c7d351a38d3673b64ccceaf983016f1d018fc33c1a462df98303fba51f68e05c2245d13815418f29ecdfc00fb74343aa57882ac872681c45e74bf2c070e43a14571beaa82510242e520a8903ab0406f85ca521b3c85d4a27e561ef440ef098957c866391e0c53af7dfd45ac26828e9c2a34ede99290e0075ea5fd1229456fac5e21edba2b16b98c84642892ee92d77ba49fdd2ed82d2ded75d0febeb144dcab70069cbc0b7451353992d42d38d99db4b250750149a492534d16cfe49fea1af79a1b02d25da4da09a103815b07a997ef7b271bcefb39c11291f41ee595eb3e93f173d2e9de4e675f040dcac73472cc0b65e121592b9ffdfe2c3a4b77675564cb48e10a365495d0a94796cc717e0946ba761308e10c3f6f11cafb396752c66d2a575c651449c2050244a34241fffc5b2dd9c71ac163e86b1816ec537e90e0be7e09411e78792c69c6fffd35abaf0d7aa2f9dfd4500718de5081a8bcc368717081660140f58577dc1d077701abe99be1338e564ff47c05f249b85d92de58cef003a16eeeaae4ebddf736e8f70d5f600fcc99bb21401961c0e56f9b772412585df53cc026729fe20f5b18b0c223ceaa97a1b5d2508a9ce5631ea384a7e7e85148ba5e470174526aaaf671ca9aee9def91ade0f5c8e7195067e7594b7d12e955d1c930d3c23f633568ac979e297800e8f9e384e056285b74504ebbb52fc6b49d65ab2104c2bc2ec73f8e1906b571ceba643159072fd19abfa5b65c5dcc3bda60913b1fdc3605a9398f8ffac5f9aa246474ec00ffca32002b167d4c75c148c95e457580fffa48cfa400de4be945ffb31340ce134f1ddb9ad4d6becf315077ebb7997dd2d8d27798f3b7213a0c1f155cbc34d64c37f29889870275b039bfe62f1d2dc0f66b510c10116f4af8c880273bdd3c66bd255fec249904bbeafa21426a22d0fc6df7d37f9d9862247749d5fd32e863b5994fccc86ae84ffc56de75b986e600074a5e0c617fa16bf2bfee30b6e0f6f9984dd04ea88a2a5d9fbfbb81bccae66cc15e3d5f603721a0a054801711031d0c59c881069497aa15fb765c07bb60f0f3f3e76240ea8ad7c04465d9a21972da52895e3ba4d2eae4835f55bbe6d725c8b29a5eef86e92932ba94cd5e5db46b925c80384f3890f9f1219e0110843329911a803a5fac562588d5f121a3df8efbb2630e881510ceb5781ef1fe55f43b34360c49e444b944917869b97260ae2494aa2b48275d4aedc3aa4924cf6d994baa5f0f22005d71038a83eddab4c1cda3985aca60302135ac0f259c3a18226ed3ff2edf579b75507f129a63a487033d292c8ef206670373cfb591a8aee2b251b0124425c3884a16559af16137115be15c054a078c1acf95121f41fc7ff031ce70ff8147b1d4d2e2806294711a6b7935d67b949224242e13d07952105b7c3fd145a816248a4900235649dab0a1700c00a97725523b35fe89ef07aaf3adf93adfa339770a2001039db61c6ba8f64b0c2520fd9fa8452d8d4395147c759574b7f99a8a81b628efe4e05b3f005f712357556cd95e00e0c0d5cd87023ff4c28fc3ae8eac6922e3a69d804c1abc4dc94925a8eff5776c1ada2f1120edd3f0e4f36ebb6bd1c8b32a6ad571511ee26ed94130903bac9d45669981fce989acf9f913fea418005510c8c85ac7f0dcb8181fb478548f52761500626ebadc67ece56c204b038f8474c7b1cb3995d7307bdf448e2076ba37600cc0335e1eae02cf7609bb040503524ee1e13bbda4d417aa356997ff29a315b0bf8a454fc4c68f5abcbb51c03b291f43593843b200aba8f689ffbb84bc574b4a54faf27b85b554724a4a31104a7e00840e8b84dd6b384d497b3c5bba0246e2a9d55326cb6411f4365d762f8004f299e5d93bc8dd4815121cc633b96db5e0d5b01629772dec5116b6b979dc8ea23583d0def9776f801371b5b5167b3d668632c8a2970db180b19295cd933c2333be39375b74f0c0e44fd3c7b12c4c25dcc62dc2cc44e805854733155360fdf484329256be245e64a99efb32f4f9b1b534e588117851d27ebb41fdf77c5b9d5817dd979fec7cb5012000a8069f21b8d590195f079a93dd77db5474a45e675df493028ea0fc1e47b4a3b406e38a93e9b2d4c7b371bad4046ab04a22184d4f8ad7750c536b824ae4c84b0de1185c63f737a39b84aa5dd51215d2c4e669b7a1689b7b63604a98909f5676b86c2f2802d786a887858776d18408f00795578ac7fc42c3343b206146cbfe5997f150a676e8ca84c0cad2ba2686c30079fe10609e0d80c58890fe1e1562a6974e4efcf0f586f807b99b06a44c1b4568ff78bd15c84c4eacb42ca3cd03f0163c86c135537a4fbe61445c457953ca531e736985d0a3729ce60125502887c70a74392b54a90e37b65a0f046546ca2d4752c7361e2cef83329de4abc3b30cb9394e04d0c1036dc974555d7825ef57a57720903c39452a4ecd3dcdbf130b8082dbf66759cca5fb77865927c0ba8e8a245b177240c8dde4dd0526192c25210fe16adcd4053f49edc4dabf2f4c06358c4a9d50d7549b12334b9fcf0c74a6411db55cd87876f4427bbf16f0511931182e5c62828be30e6dba566fb1018e4e6416f291dca89953fb54d9d14071c375a060fb7daf14142a4c7cb54d03ec23a48d8087b9c887ffdb80e922318b23b2bbee5b058c51d0dc990b7b321d1f87afb3d55a9af2acf44aae48b00a2d5fe4db2efb71aee7236959bd96f95d0fb353651fd045af3e9a724805123728d80d11a9cc992676a7ba871b8a134dbd838ee349c3436cd4531a933ed1b4e71a234c68089a8b7dbdc3751bc7f09e6fb4c594c80fa4d7ecf7ef0bc380df826fd7854506194a13f8e21c863b1609874dd8973bd2a78252c67c0eecc3b4b333fbc2215dc3db45e8c4b8c15a640cd9a5d441ad0d240d7e6e688777d957b5e1ae57bbe07369adfc8cd411d63a104af8a654d2258ec70bb8f4f7c38e43546eb936a9096810f87146de51c456ad6c022ba7ac89cc68fcd1d836f91a658c74321ba7c13598e3480602f8ee7625b6adeefbd5d78ca58e5cd1dc8a18174d334e5220dcf165d5c14a057f022eef752ca51197f6bc3a2c9a37794b989a25e33f95f8275c9c04b4d9610a4abf664878cd45044d51d882fc60efa3e280eee026e6b286b2ed534cbcc2c6c1856ab964a2825b6c655ddabb8686ae01ba9acbb14929499058cbe915c669653f54564a527e9107a2f722144277c53749033352016f58ee298f27de274ede7a92f2a146ac9148ac52e28a6a997900a18fa9e10a67093edbcea758e02642504a60b47da7e3e43f5feca433c964d1b367f43efdc69ea37dfb2c4aaca42c86050167f0eab5c84cf6a86d2f058827d187dccbb069b7e5ff24e1b8299e3cde69325623915a679a1024e33b4e021d18f452a0af7bac907d67b84ca9df0100ba692a122aa1783e721d984d0272e1971c460430b71873dede9b0002bd27e89fd24c613aa5fe8954b36b1f5fa8dde50c0c25aa6ad4f657ea44ee0c4525cf862e3ea30c06e14a09b77ba492531c548dd75dbaaad5d1fa1597ce4607ad0e24f6756c5bf0eac45b92a2c66df536f67a6c3cb29b3b40fb008aa509caffbcf6cd98d9db79bab94aebd1433ad982cd2e55384b4c6a23f9815a46b185af4067c2254c16cf556c68eb5f89f7f75b44e71f3da138fa316f70bed44f797ad887e5128851d8384e16c63bcd1297f9aa1d2b621977639d2eb1f7685273bbd288ecc409512020df625f393a1cd0442a807441c4f098173c6bba7d4c335fb915689ff9a601be3a73c32da664b42fbcba72e6fa36d926e38e984be6c4d43b0f12409db9ab7bebcaa073fa3dfea946b0bfbf4394a2e600369c62762fa74039fa0aea21ec9efff04ecae9b710a24c3104e2d9d79398b6f12cdb34b01c27d4bef0d2ab0731fbc9219ad23e8e34c267d0163402f997e9aed14a973286d9470db7c9ff2d7f9a014e552504cf45e00f34d4d1695ea85223e509df8c51bbc8b6c30626bfd1db20ad1fa967f9f127e702456d321008acf5b0373cbf1f00fdc25a8ecda23c7db8d14ab90d02fdbde092e88fe746a7f03efcf9deb2dd48ba13521a19017e0dd9272669f2c90bf090f3cb7426a6b78e4e8401af2e2cdb843dfcfdd77aa07c0718da89da5ed092a65907a8be91ef814f35ff94e2ec064c1c8c20bdcf0bc2b44145ee07b0b720b15f04ad42a42f07ecb79980b8e0c181235416e07b53db12f973ef81e118c27c618b647aefb2ee881b0817cedc8ea06b75a71d1b23b5606ddb5527cce3f4cc46d86fc2ed8525db0b54341a1c229c1b2f90a41306453672e9299d8008632b69b90613e22e8cc5a50d0ac2e878d84a29523055422d67cf1e6b09f53a397bc107f8f84b5476ccf4b01dfa0fd54bb58531b9ae74d70abadf1c27427d64ea87bb388e13d30bef302f49c3ce82000b1b6fa469af5025c5cb5c252c294230d8d45f301ad6e1c8537d52f8ab0556119333badbc5d7a05af6cc17ac18eec3a294e393a572cb1f4c2a55e0687f3c26c0d0c10a91a6c69110bfba6e5056cd87c6f4e8f63b66878abc293fdfe0ac2505eb0e152b4c1aa5cfa26780a4903e258ca0b8249937e60eb994f3e9412c717d080832df6faa13ef0142221aac58228e1f923d6cb70a102b346d6cec3f1110fe04d8976f4ba5e61174b5c9d42645fb26ab793f965c02e8b6920344dee42b7a0fb6cc14557301c9bfd3231453b8685b89650b1d6acbe14c680b6215b2a73e342103969d842d86d9c5efca69f61bfe786ea412369ad815dd258771aec920675d1d8ae34ae3b0d764d63dd6868771ad74d23bba5a16e34d8258d75c1743792f92d0d0d7e6c3309a2be926ecc8cb671cbb30eeafa82d1c194bba754e166ca79df45d46e2f04b78040c2ba0a2f87b89704f416e22e1dec9d0167261598842695fdd149a50700a5f211343ddcf94ab3b8e0ba1c3357083dbb3b2709fb9ac426e8b3733a1274ab98fed17d289af84b228dd0cd26f10b6bd3315740c772790bfccdde1aeb6d707ada299c06d5d7f3fa934e52a92b85973a8e4c945ea5e2ce3288604b9b36c8e00548d381cb0ffffbddf160ae3da3a2b78c4ca23e1a652c06d6cddf08f61e58108a43e4c0d2444985910428c4b9e20e70dc68023f2437f9fbc89dec4d0125c6742d1b132bfbfef09de8a6d16b942151e57a6a426764ace7efc97d7ab81ac8757d9981ba9b5d1d4470fdc98e0ec4869e4baca222173d588ee058356f4f541851d67b7e783e88468167cb81be0ba1a33161314e7ea4a2c38e9f6acff78646ccb8fa62d3716410689e726aae7e16be7889d38a843b3c3f0ea016bc2e0a493136734fbfb55a7ecb3e88c7543279938301440aaeca0b23c7ea9b39adec84431e4e114011c333aabd459e60e3f632dd874bd50c8321af274235aaa473fc7c47d08738074f1f58285309a092ef264af732fe2631fd227603a998e877480426b98cc1ef233fd7eabad2065f70683d954250715d735651878f6b4e6cc2111003d4914d357d35919b0716970f879b56d2f4d456b550cc193ccca4adc17e027bf98adf491e96f1fd238d1d27ac08246511eb2bef83921b5186ba140178d254e3f0f936709b140bec607939ff0df9e9c65faa0e42e01da8b9c9c1f7b2e4f062e322868b2396bf808eebd890d2d1dc557c2d1d1d9501b9e516d735b9f0a5cbff7dc9629c8a856a5037fca6def9d0c1ea9ec80b062e2dbb50b5de8e779ae74830b984f55ad1a1cd2a6e79e3c56986abaf9fa225cad405561b671aab8de651fbe29c3e305204b2d7a092cb2c884f69672bf005ee5de745ad43f5717387d6f103ea8eaedd34fbe1908ecb22e971ef20cda87fd0a3a944dc7ef0af84414b5fc67a7de711911da53c30f07461e63f61ca7097268e71e610d1b5c3c2408b7012378a9433414fbc318c6f7ff6d8d52154243535c1e175869ca12d031a3e39322b92400757a730fd0a73b8deb82791fc52a5375fdafaee29ec7ef78c2d2996b43b453d45fbc1ced874dd5db4c724ba3b91d5bdee140a4cfdf93b8d70834b645a4564e79d06e2d729f3a4ba422f096dd798228643e38a466ed21ce1ff41723408e4f747c724647d2205096b7c9d632366eaa0ea506eee1be45f642af3c35e8dada01892efb26d7e7722f87e641f71f892fac0e52e73ac200eb9a13c2cd491ea324de50da514a8d9b98d56b3478909ff9eb04bebd38807a9fd92e3b8bd2a718d70b29bc8bdeb6c505e69a1c2773d1948fc2a0f05c77bfce4a428cab734d32b329d7162a01a9b7ff39508af4906fb0d571f8f1ae7bd4ae3b2cccd2af33f02278892c3b68199607a611434ed4934a278678a16f5964b8f11f787dfb60097c33795200e9875e90c452c898c7d688782686563d346c858148155822fe04d628e3521e737015e387d375ddf3dc558b39e16a0a2662ee1546c72c2379a349fac7c4940c21e7322639351807af57be27bee1ca7ef11c28274d20a816a604c01f236eccecf3729d115145628cf33224cf56e88d0183a735afacafd6fea0d64e417c2b207d4323969aaf266371181ffe1ae2eed5a918c29bdcdd4e6ca720816db489d5db7d83bdc54df03b33b4138be201c3ed63f2130cc362cf3d8abc3b83aa9881d9ce036ddf22bacf2af62f65ca8d2aad8c11683ed0649765f2917cc27aad703d6499bd744744a2a5f7bb1eb1d992610ed550f4a55dbd95a987423939752525c94225e516d6c9854a79026d355f9d9cb4c9440e578fe350774d78c6a5ea696be1ca9e37ae1aa0c3dbc9372d1234881130650132a8baabd4e3efa31d24a50e1c25e83f1d495b32a4bffb3e21a85053c2569e15c136ba72025a9040d9d61cbb784179b409e1ae251fa1849764d14971c0b56562d554cab3dd3189ebc9613efa990fa76c84c05f79a772a36dd21ab217905adcb913c920bab6d94fd62d6c3fadc1003b786ecd2e393ef0d482fc072ee8f97ce3bb2c7b3e5c2586edde2ce0197ffc0b0193093bbb593420991c6ed6432b552e9ab83585924667a128032b7641cf2c7e0028054abd8c6c5a6ea918cc1dfb2c703660405ad8b5fec13778ea61a264fec90022a53194d642c37c97f800ff9dd46e79a23d446d31e69b8691c51b4872236599c15243db4a777875502584261b3425cc88611c7a99c6a03f29b6f6ab0088f881f5b740925ae075dba1b78fb06ab9c79f57d8cd104bf12c51f68e13190eb6ab0ee2253045bd38a4b55e541213afbfca7162e8a4ff03fdd2ac8793809508d4d8ed5f441ccc9107d37e6097b36ed946b9bf225fb947fe9ff9ad9b9d993b3716937dcc03764c2191dece267cbeef66708f348aeccfe4f59a0ded882653e78752532de22ed966f9e022de03cf4a1dcb345ce2cc063ef97aed9b8504f3714d5c42797bcfd6023d2729bb8d53dabb629ac4d7d83f9b45b4c47fb1256b82378965dd3509b62a9597244320fed618336889c2ecd06529f4860c2108102768d5c508e229cb0884731ccfa72bc6e4424ad82af9975e48ceac86c06eaa47417956ade4a52da3de6eeed31c6d440661bcf6761bcd21db6e602b77917c3c775471c49e2a0dfdedea2ef8a4673011dd748ad9241eec300b87c64702f72e142b4b6ea3a6843e19ea39ce3cb0cd4a0c99cce961429ba3f9f634e1184d9ee24d5b23b78d3212755d90a68ecfaa41163b14e7a2a4f925d2e4296513e3929017297352996336c0909052e053b84b5abbad38c9eaaa42619b2486b7df73c7222d34cb4246cb59fdc012577120ba28aabb291c5a06b68a1065648a2977cf990efdb5b3cd952cf40783894ab4ed90314c554e4bc84eae9662b3a8a5609f67af8ea21af02d5499909158420a097e73650892d158a9189556d24ac4e55b57630ac6e2009f6ce105c9edb9d35e7e6a409fd5ee1315c16ea5b7fe224786b87e8929857588ea433159c40b989b640d98efbd87b8d8a5c0d6a8d04d38d8c34f93d1acf7e8d1458b25b1bd47b4af604cb2759630e60425104eb389314137d09c26358a53844a26a4e11ac452cec08a3bf4f510a4718b61a35ee572785291bd165ece53d5d2141ecae3562e55ca954834268df9df45dab338753a19cfe5a8b41e3dbab92c642b285f435057ec644975384f883d4e79d224192fc24f2945172f1c4394b1c85c3425f83a56539327442dfef819724c1738c873dc818dd208c4d59418731e3103876e101a82371887a42fcc9ac421bbf344d482f35b074698e4fa6893c8080b8d12788acea47574ad6f824b15f02700c42870cdff4f1056c4a5b1fab6c2fcda632f6981ee9a01b84bb3ec738776a190631618c2f05a312fe399aa9bf2ed1cc1c4ada2d4ab3f001ea8d0fe78bceaf8c08a7b226cb3e1319b3766dd606b2f9921eed163ec8bf1d05aef8435279ff34227cd66b2ba27e9bec7b1441800874709f22f36a1657dc719692c3c83b754461c05bc25c9a056505dbc12fc72a42b48579410be09add0a6acd669f24ff682744c5b1cebc41a4572536146d2fa4b7e3bca24aee987c79506c86e7ef72b05ef78b0abfd4c0dd905526f89ba0b5811d8a710c0a70e79871ac1210ac2cafdeed01a3f1427e5048c686463c741c9c84fc19f1e852fa2a593eea6c9f03306dee60662f8074cdaa087f8710dbba37df033de915a81e310c27722cfdb884e530ac30b73e03b8216481883fffcb9dffad59e8fcfd309b6bda7088038ebdaac3fd358a97e364a4421f9271aa19ece877ef4a1e538b1ffb075118231610b9bceae81570583fad155062c8a5ba7b969b270ee256840fd9f89b85a0c455f6b8e55c72c666b5b79b131d737d900114412aebc8215cdafbdd48838b15434b2fce64550519670a1fd81416d1aa514e143f96d92e71423cae69973a9699497c67c37e6ec823ac2c1e00fa7ca54a3c71638b1537b678b162c6165b5cacf1e28b1733b67871628d175f5c9c71c58a1333a67831583c6d40095f54bac6b87ff68a6f577fd9ad9740864c6f7c74e8ca720dd12420e593724d4ea21815053a89bacbe6b7c24022570d3f9991f8d2153621f2f6ef83e48c0f6c721585ac2922c438a1895aefe35c630cab1633b9d02e8fb5c4ea6d489276919579a6c8301d855e549e16f5264e2df2c2b098a530605df86ccdcc7483ff0d12a0a0eef0738d5f7881b3826c92a3c353b1f54156a4c3ca61dd0690d929d11ce9d6ea0fcf960108596dcb292081c612318d4e40be30d2a92f53d02774e4c8be60c5318fb5a7ba25469c06df33a91fd2a9cfdb29394df8c32a7e1ba0806daea094d592f43a3023e6d563b52a8e5b8070315e7eb421547599154c10d3ea2a28a2f9855699dcf5641a298c5e50021b2b22f223b914ec1e46347e8268c7475f677296d16b5a8ec7e6e13116b9a056afe145201416d95a4181b47162021db688150edae880d5d19da310e383a857f37a4c3b232c9455d26535e00b454411ff2f7f42a4354d8f60adfb651d05a08b1a0f01aa96489f0483ed7672a640a02d085705731bd6c80144311577a282bd09c38f4f21090102ed6ce1f7f1332e91c5e37dcdfa7d386d693e5a3f7d92e7f9017cf9a3e75101acce9f4b61b9237e09b5547d2bfcfa39b42e40b3569fe7491c7d2cb961b100a24abdcb71bebfd45d26c85f1eca6c802d7b9fa9305d64f817ed3841a958c18e7f48fa20a448e4170a8ef278f85d2277f4100ea61c03d4bb87bbd67b62c02b2492d1ec67f121f7f033ab0de4826cb23c03bb0c45ce8fdba3176619171d680e92d1308abf3d0ba3cba11ee0573c268867f5625505d441f7df12029612d6e2e157c9f4185ce96abdfc4cf5498261d5e291d1893b4a5572532846277ef47bb19baf5e10992d3752e918c5d507b59a5df39680936478c30041610ef6bdf109b78bbbcedf2afa6e3b7432741ec8108a64ab0146dc148f3df36bda5549bb209fa74d1217321f46dcd474afe36057c0366d1c0338b09dec0a848a1ad2015a256a7582a483b69b6ed0191beec2d84da74d67e8b066da44ac005b1f91505b0c6c1a68581a6d720f833b8e335f800d4d7a9056a69a908a582cbd84b6f42b8ca7615deb5cb19700ee9d3add5e21e10bcbf664e43579afc0d5e9670863e38dd1ebcc3468f85e00f985783055c6b9af5464a1a3ea823410b17512ba24ab120aecbfeea4c6fdc397db16e8cc52c86248223fb70bdcf27815f3770a5459d397d477b057d2c02ff12a2ab763083bbc0a59be40a993757885d03eeb699e105393576e29e284613f5a83d33a1b1213a3b8f1a51e5a0805dad38c9bb1dfda00e0092b78b7486df01aa76b4091337c436f70001787c8d323a166e0e32537b0643335634b56ef47182bfc2afd146cc13960140d5601cde17a76c41988c612186be6e414f40a7af450aa1580703f54d6815d0eca1431b7a83ff507bcb36881594e2759fd3d029a463a20da9507b6322a7f51f3b76c8c1af1950f8c51a62b9827d063e340d3f1ba3f3dba70501d8918a527babeeb269bfc8723ea19958c2f0c95bec967ebc5d96625a869e2a81232b1cd60545421e4a08abafeb8baefde3cd1f7366654843c6b4e955186253a5441f8a67cc0c9cca51397c7a8b11f28f41f86c021a3b08d73c99e3507deec2bc1a311351885eb9b0cca8ec3399755d4910057c7fc572c25536cd9f7da2e25c5320b52e449936e653d43837b27b8dee49528328b46fdd8dcadae99d2896c3e4d79ca4b55d4f964a9309290c4c14a213e01eca3d0d762338fbf9b4e3dc7d91cb97fbb409c9e6ba2c87f4b12657ab0a26e6ba4d860c614073b30ef361b90f7e77215e5eb872a405b216e2fad261b1fda2fba432953a3506a664c31cb27da63f4c4121d28c1783976e8fe1127cddf908ea293e594698065b627597610962438f78286c246002c4fd2ed2329f0c7a86d83c0a1e89cedb3b4570dce2a1ddf489b7b3de761497a0baae725e400b18e82f35778782374566c4a92da3d8e7a68bf6413862247866e67c16a21cef152a23901a0b853d3805fa2c45d352547d12e68fe74581836513fdc7db4b5e059ed08b9086bd44c9f29eae8ba0ba7e043a268b6bf88a7891b989ab1299af266a462396a26d7646f43f1c14fccfa543a61a5a1c6a08e38c66656eb788ac9f072d478faae983709cb976b096e043c171fef32d1415c54f863756ed5d6d9f1afd19bce06c4e9c134cd1b21e1fe34fcfbd33588eb02365cbf2b9be2e780e064aebf9e4b3b74931b7d4fa40393c801c0ae05e81ae2ba12c686f86ffb3de3f95b56610fbe9d80ab8be6a6ba8ec47122794ae5746cc26cbf069739f195fb67995cc77326fc339075b1e83860b3a1c14b04a3c6dc30fd4f1ccadcb0e12986f4bce714c1dcefdb481a4714cd0271638b052d44e933808388876b5ed559f38563b28db46167796b048714313ecf247b70a45b1811bf4716a55a4a01f395eeb9be660bd7a0e049675b1830e8f8fa10cacd25160304230808241d5cfc2705a57ee949c3c4a69a10b7d1ca033a58b39e3a380d3a293774d27f8efee653d19205df26d2ef5a4e621e0bcb8a6c4c61a4cd1248d876901ef996b8232fbac948ff4548149ede3ae8a063cb009290f904c60bc91fab4cfc3f0f5161ba179d84f97269ca13611bf6f0fec64d1f520f658d4dc7aa093ac2d19de24431095255d8e379d4df005685531e2bf9447183194770f26b0777bfb221e0c5abf64e4975ad90fa7f7f1d781db906dad5962a565771d96f19d0b3ff92c37561ad83d5b6d6fe343c3696abae364d4d2bd3c7ac32df1a62f6940c9b8c34a5087855f823019c7a2013aa2fa4c717ea4b79a015f6e616fce196c21ba80c4f7863435b45311afdaf04985345dbe2f807a4e93a1ad701b98d2945f3cb7086ce81a70331a10342435306a3604d402ad8e6df83373ec0b96249c3f297ca42b739ceee736e3167d7cb33fd8863d3156c514dae171c2373641535735cd79a8abfa5571d42c8bc0bc3b0d761aa54fb73bc0b1278ce8a27a74735cc3df1e380cf22ba602cef672d2745b6eb48437a2541cd67ed5ac14b577d6ce81c1e0c0b49fe393a8c4d084de540902104defb529196097d0efef626b60d195c3cabb591ebecea6bef04169697d967794084300cd063b2c445c89110fb981bca8b76d89c552fbac292c4f42b7a04034a3ed98004bb9a63fbc8456b5a6010757ddbcdf4e1efb21cabbfc908dbfdb8f1b33a6a05a2fdbfee5a3231c4f157cde55e0b162549e983cdca5a11d7f3bd71000174fb84afecc6f65337f620022a2b6798e3a89a4f13d5ed205d11634b7a183a1cde8786431c8f9cee0c2db58dbf7e161d5a826b45dd803163bca6f40de11097860d87c45ccc5111f30a71565058b998a82dd2ae9e51ddeacea81a422be5fa73e93001246fe815f12f5a0417b7ce82465442b3b957def2c8ebbbe1db567556c04bb4acc616706694309216babffd87504ee9bcf79330c29d0b7f9b87b7cb2d83ee6c3d6c7dd5a7262b654f618ccfc31007fc2271f4c0126a8d4e13b9a365947ded5425e9ea7ca3da9164eef8a18af4980004bd9052df3f69717a70f7543171cba232bfc648db6cd43762b40f419d33fb6c64ddeaff619fc65f38e0a065bfde363a7c5d0c7782ed77e2075a6ef7200d8f51989bda2d03a67036ab06b4adfcb07ef38a5672bb59ae607d181acc24cb16f1c8d7bd62c5f5c978a98b9d7f768a9cb2b326dab66b6993b8045ed7dea7c9a1fc0fab73593330ccd277394ecc9a2956f3d5d71c42bec07808d98a5c7045721fc3b7424cf43c22f2ef1859786b327c05d6500b8459cef6ecab228c23916c7a9a3b0f5ed1aa96e86a52bd9d7c608a763a9ae3db0947efb7df65201c1ce4ab71a06bf6545f11dce02d55a68c7abc6f23ba90ff59e32de7ec9df45deaa032062909eca0a7b9d0c6cc482ff4648bb5779446cc9af0eec998b831cecbd6406b17f90ffb899eaf311ba17d6170892419327384a9c2898788614ef74ba33f2b70618bc172037ec68a535df4a2640b49bc9b20dbcf6b712df8105556b44b5026b6015e218c7bf04bb1cd6c48d8ee7227696312118a98189dbbbe23686b08c4b9973e1beb11e98f9ca49ea41becad662c46a3f98c37a828c902393b4e6d159e2e002376febfda8866433a097f1eebcbe7704bcecb475615fb74064b63f1086e5adcb04f7f67c0e0d42061b73021097670057878166b1caf06b0407ffea9ce9122a0963bfeee95f04c7b88c615a620305da3d546bb5741768f5c7a06bccfb730bc4270973293af9f35116e1b8e0b2e60449d7c20f6923c9611e1605b2c0845944f427e1d2a79b88943d739b1ef3e622fa05ee189d05301fb080b6e8bc8a942bbc35a6fd93819aa62d4c9da5c186085c928588ae35e68151813cdff9f15357b43151619c41b1a8dc0b53eb2dc09776e2f0e5416310624307bf43a29b00cb758ddcfce337359f37faacf02c3d80c5c46c6b805180ecbc3a266ce39acfbdeca437efa01625f6e092aa0ee93c07f0cf984dc3cfa0fd89e7b0eb0a551e803233aa96fc21660a6082b312a950d263fb53d618a7d2e32734a5c75e2196056ee10a5c0ed126decc61810981e1f0d7f39894723cd41218ca78c51be3226d10f243e88dfa8e73b2622797f4c72c77c3a5dddf24488eb62e49e096362ec4ba1d7321dd6fe40cd5ea75293bffdd0ef8c97054e0ca0e6b96767d4ad94a402d402f557a52b13527d930af807149dc495649bd6a989eadf14f386e1c5c2ae29bc60b185618d9177f1b86175f53dd8dfc29527fc7323e9729c120aa45fefaa09e1047aaa7538f7848af113d12b255931f12d56cb1d416f49706bd7ef8ea466a7c3a1b7f0efd6c23866f783c5a71f5342662429d994e86096289c12dabaa1d5a5c9f3a4955e8cb3b2a53c3fb1db8f167ca933d4b08699c254f3f8d0bfd1a835beb5548e3257a3bd8a38ffe90ed41f0a4936bdaa02ec7c476835f3203c193b08559ab6bb63df410cb2750670cc910b0b789af7f31c7ac1c95e0735e3a8ae6db68923e1ba6ae558393fae03668add06f0bda30b5f41d0548760391681cb0c5e3455e03320d73e8470f3fd5d81bb6cf68144ff6cd6f623d23c7dc43dc77b7415538ed6a103f748ad6f2e7745158f7240040a9c7726219a10d227c3029e7f1f85330370812663a0f7bebf17d0fb0862f6a83c4a518015e4eab418b9830420db56a34ec4f4b5a3a67c1277aac42d87a2094eb4461518f9044e074039cae4213a4b9b1e58511f5a72a45b4d3091ef41371f992304ac3535275e3dc7cf11d076daef7cfe4001a884ee2253092c7ce2785ef01a0a498ae256b6f6d2c2052f5ff16b23346281b3954346b5b541973b47809aaf97ec32ef2b3b03645c58305750e8838305e5c4c550bf41c93cba14f52bd0816f5728b63becd50f6618eb97c455e8052ea8e712cb80e1b1148db18b944918c32a813c0196b84d2ff747da0dc1fc07ca67e65114a962c05c35d30eb41f70b392ff814025c2f15b5d609ded73b718afea36f20836367e21a6784bb39fe6535d7f8a4618a349a11076e83ccf0ccdbf81332ec518668ba82c7fee0509f3da07ba6d8fdd6286749703b60f440c340b70f4842c88df337db566f394d80efa10900210a4623e0d03e8f386461fec2cbae5ccbfe67874f53340f8e484922d5b40704912355d2b48f2976d027fd471ea05180da5b8bd3f037838bd42bc2bfee61b8443d0101b004e5bbf935e486ee35172681c47363ae0297553c2f30922baa105c08102eb0152c62ea556ce82b827745d1f4cb7f28132bd0776bf48157b2d7b5789bdafe45859dda175033f85f7cc437a52be675930060b9649ac8cb2cf8dc677b8ccc9461ee0ace6fa8a37761ab851621d790b7d2ffb1ad55d1b9bb9d25000174248eae4b47cb3cf7bc3342f705153ef9c03baf41a4ed48670a43dc43255252645c0afb6da5024d324de75e8407ba6cb38139d3131d884b6ba29888d975bdb6251d9a352599799c83eb9bded27d61a74b6b0468c314b270e3186feec7224fcde8c502d778cce602372f4cd5f72e862616568be9d8b93f26475743af5524f4d612715741111835d0e841fefe5beeb316e22081a05e5f9509fb79575d92d5421f03d186b6488c4fa61e713d66f0d097d7f5b90f835a3d40827319619ad6803b326b641b183c6b08034787451dc221003774a2a5aa1cc039ef8386bc0168c228940df3e2527247db022ec8d09f35542abb26230e5af439e054d33311364c235cb0e9f92a4efa0d28dd93f36b021bd27cbeb38ef69df8d4221ab333535c42180abdc7e9d6c7da4991f8bb1bdf642c75dcc37783d7773f3eccd7565948daa7aa501c74115a63b63375c00bce93948549004eb8353e18518b5d50bf6b2a5bc9b03f0781ad1064510e8bdb1f1cd6f31f9720185167d9c42323d17fef5cd90e91d513460553204255f33a03b4ac04869a2d7ac54899a17b59b6dab912951153425dfd178fb80377159dc8f427904646e249625ab2599ee5ef7039db1c427f065947b2c4f843e5f1707c1d5666aaf9faa5e0c1a7be67ed392258e770fb70da221f3c95cf318b7cbea886d40b070ddc60cec1128d174fc911bff2ad086c067b96559ba39b09958399f4051d7a36129588a40b290346690afe9f9b2a7ae1fc5452067cc6504f1375119f84f55f70f08a1653abbdb163446281f476dc65ed1135277e57fedafb23e62d5b791eee76120cdde7b3f07da1e5e2194b4bea18b0c77c74825614d3b07826af2f9735c1693898331a88598b864c931b4e129f8be88c81ac505fc5c17b6a67c584dd67af761567187cba9846665d0956a12fbabad0671fb81db896b3bcc003228892ed7380e330130d1e291858e3ac0f1452244eb7c19b48629b3fa78e63551bbf23f511dafd4f8d1b7919a643f43d1bd12604fa0047d7385d3f277f9d57a80ac2c252774b479b25b57f3fbeea5b08ff181fbbcfab20267729b60e991004096f2985c938dd2350a9710941b395ac359547902270aae9812acfc34bfbc50b63d8aa9c310a9fe285d14f2bfd16a7246d5e462aa82f85c8753d178668b0745bb74a3d5cc36708d01d14b2e5e307daa67b24d57b3716276475dbd56c9db5558f5b5a53048a8d7016b5f138df35a93482563d2a66cb475710420d4c87d9891967886e257b3e33cd83bb75b338241b4852c858051c166301e5a279dc9b99c874baf59cecdbab8ec9661c72205a05b72d9837e5e13a995cf0505472f3928f9803335cafadba31ed4adf6f454f08dba5e8c0abb5b90fb0fc5d76d14fea831fc7c15e3a680bf2eb8e26a8c7179f9cd5d80f7309dff634409cfb76460e465fd1583b901b447ede8892a341a65c38e8549957f282041697971f459cd87d4a3434b7f45ce40a31c36a462d3e809f1514d09ad0efa0c3dce01748936ef2404119a9b73a62a9c03f12245307e6bd55287ca8faa7a1e86b6078342334925f21edfcec35da94ac4ac1eee8b15a922ae04eae5b0dcc4661acdae211b9d1500ab17d191da1aca8225d580b5bf82d5c150ba5dbcd5999e2e3814232e195de9b4994e2d6fb2fe3f6b43b29d7956d2119573b0cfa34a1ed8133a57459422e3c4246c5dc60144cb293d4da98dc824a2e4cb1bf4ddee5679c99fd5cdc4b9503e0275963e13440be59303d4f4bdffe607d07d5f6c52aff76e4f1a5a8e9b42bba97835b4955f0468918674b04ba95591d98b420294f11d6236aa4648a4ca46245c9762b111f9c566d364818bce9ef120ab45d4ac598ead45986c4424e8247a28aad29e9da3c27ecca70bb37c166480435bd2028c6c5cd797888a6b8393689a296da0866b1024f792b6c875948687aaa40309a89b5f903cc2d2d63a4a1d3991c28e5f2d7f446dcb2e679691207299efc15ee87572a10ddfb335e6da67dd400b9b9b6256971209905189e6ab36884ae30d1949f5ce03c81a7d54d3e7d1f0079ab57d8158137b33d5342987d98dea08b4c127dd952ff40956d168a346685049f961ab6635c1627f386c36bde4c4064bd00acf7ffbaf413678faf70e08cc54103dfc6a6548bef5ccafa60c5f365503ce0e70bc581b414fb0c501880914d21e20d48c9f3e2799c3abd16b6b5a9401baa07715534955f87cfb1bb627a795b610d804019dba52416fe408ae2b44166595af440a700b5f43f086f34e6e6fce4afe82e0eb3126aba3aa32cb8bf9628828a5addc522562474375e72d4d6c790aa739e3a38aacda7a7b2c440109413084bbfae1f2f7c5bfd408d53b8b9bf755bda213cab3b9f3415f1f894b30b3dcc7a6046e7675f5b3a28310bb74eea35ed67c83b97a47d8ac920a2c782771984297ca9234b6f2bce007c35d6d6627baa2bfb3e5487a77f84528ec9d50c2e7b7a152569fc033cd9558d6bb026a08096ade932c627d33374690180e24095b4c8d0e7bbc4831cc4ce63583c7911b7e9679c2b91a3d8c064ea461f2735d1f15d99cba5576fcc87c32ac093a0422b14f808cab14f32fbbcad8826e391ea1e808e88866441cace1a483a270c74fae90904ba7fc11b9be8d405af0ea73f7184e6528196dacc5ee25e518e734c4f5cd3f9dbbad1546928bdead044612bc0d32068b7f301f9227a1ae0d35cf0b61377d1ac1b57798eccb17b6cee58f14890b28c58399f3e67f4229c90ab951fc9506b8899fcd6e95a49abe20fdd6999f26d3d026f92eeeeb27c6d83a8cc329714c8adedd8f8be95b323818af50dc2166cb0e7a295543b0c2499701b31dbdb57331cdba7276422b4c1e2608016d8442d3a370f4fa22f9e62ef35dcbeb27053f284ef57872f6d4b6213324f0cf654fe68dc88f97ce9161d51b28c6cf16662a0358a38541a985a8cc34ef20cfc706ff106a7a7211ea3f76f634c1886bd18320b8ef62307add33a82e79812d3ade33ba34dadd96cbc9be9cee55d4264fdafc7129598367ee9213f9c2ad63da7554e1ca7785b2f1ad0bac235c78f70d790b38c8f61a35a02e9c6e11daa8188e37be39d6b40560e24a8825dd7655b15d88ef41d79e30ff0e7a32041325f6b46f3dd4398b229c71619f8b50ea0a27cde209418d26b83a907589ebf708a84ec41cc30b124973dea5a37f2909d9d5bf2e496908abae571ce359fa4b437c86aeee02c08b246065a7760dff3c1cfa817c31c59f620cd32a746ee952b25b15e1200fe329b3bae1c686ac7429f0d48b0598b9d16f9760224fea9753703af5d12476fdd999d6918c2d77b2238827d6f518fadab2e6f7b3c98960ac5f2609cc8afddae54e3ac61833215132758ce405bbc0a70eeeb4314c5f37ae2e700ba73c19f170ee0524928db512abf67111c1890a6c2fe4c9a4b7f3eaf0f664382dc193dde435fbd0ca84cb1ac323ec2ac3f2996055a918d83f173cb9f27ec041173cf9cdbbb1e4742585fb45c982f3d71eedd28905336bb531b9a7279d52c413ed64ed02d9866a145e96c6e079e8784224639be5b264167f8de8104fba23c06ebd3246d0d787c87b5aa5bf1ebabe96868d3de3cd8b537c2af1c7fa889419a6c2695b909e04ce98b392aac68fdf84a4379f327da6aafbb8d3c5036506da0b7d30e8b5d151e223d0c13f2ca85f769f8c68f2c8b1a899fee278fb6d5c457a86357f3501c4469c0529931c5276d7f5f19d7ee25c5bf8f037a68bee0f3854fe83a5fb9aeaa96736f99b8bdfd7624b68ad9a8c7566321256631aa6e65a364c747d95f320c434d3f25ec2a8512dd1d04969ecee0de828e6cd640d7cf7e03ae114006ef0b50bcad3b878364e32a5b6ba0718a7e931e780c30ed043fda17f5f94d2752e7592ab4f855aae610111b735f6c724d24d3feabda48b575769dc18e22ba5d353165b8ed11b0823707f948cf601b6bae78f602e83ff6544dad28e6df16201a94fb608651f749882b14583c1289e71de9fc3a44a80f9c4afc50296fc1c1f6b24e1bfc4dc029589f742496ee228af185892891944eaf254a4c59aad4400541a1a1fd6f4f9afbdee8115e5d52007baba076e98b8cf4ecf6a6fb65311cac83939213f9c21cca0a7e156262e94b20544122ef7a958bc74cea9babafd90044db0f5d27acaefbc6d5b79691d4f9e2780825d3c7257fd1d848998b181e0cb17e4a3b18941c1eaf5b0e4d3fd8d2355f110d2fd96b1cf729b773378538e0a7d9f06f06ec5159bc73e9c5918ee88603cfc92a9da8e3c590507e71c265490d57c7e288c24e1be863b37d4c610b6a3f8009ddd810342ab732cdb7a6c6fefa1ee81a894d18f0a7a82daa61aa1f6094281551ede8e0dda645d886a8fb143ca2f88982efe046c1c9e0485717ccb3742b42709699bcc901389c43c287a22a6e0975ba5c417ea9a74ce4b827e05d0ccfe7e1cbc57d2b3f08feab6316383e79b359f46044a8971a434b8f1d4f2236d0f47960caa0b104502219ccb59742abbfd9f2ebfe70a15835ca071fe9d2fd1043f7d76b85cf11e72059e2d75f829cbfd20b0bf613f64c6bcf3eed0ac7f6b3a893ef2bb23fa7942bee9987124a8f8574fcc067d3bfe6264ddc47b73a2118575045a6d9b1d4ea6da1653b1b6d7318a941dd6fa4122949195437693d9082d22ea87e2b2f3b442ea40afede338f5d1b28f70c546367b0157433dc98812ace281107dd4152ffef22c5622b700049e48e453ab447670a7baf217f3b9483cca50605954fa48050feafae6c1796b54ee60e9ba8cbe326bc68981787c77ff7635dddb1aa0bb67b9ccc4c2ba0b1ed395fa2232e43a7e693794c321668085a794e762aba4afcd18f308adac9b057f2540acf40c648a8a4dab45b052725ea7a09fda677ae0ed3af346b9a93e69c81cfd28c00e584a55a37f812a9bdd854f3c0ab7781c931d232eb1c2b851a9babc4c74d240042138a776b05464252150e11bceaed4fbb0bc686e82a5d109d56c5805c49b5bbbb270f49440e654893116741a280feb56f114870080829707a10cfc1dcad865925e73908b3a83b720b39ecbcb52673ff4c34e45a8ec2068288951999c8cf3a48ba53476d6930e486c3d8de941788fd23034e4a0bedfbd7da502283fcbf91f6c4518b28ae2e6073c055aa25ebcf9d08fdbe30abefa1251f712f829905e3ed3b0c81cb78015003706355c889d75216dbb505d00c7c40367609d9b85f372a91cb1d65020c4d9c82d783a255c7d547b208084e2663199ba9f0e579dc841b20bb3cd92b2244614959c39d61b20e7f1ccd6e0a43fb25e22c650bde901ff21651bbc795030ad25548d1373e85d05fe4300b64b229d3273f60d90212bfef5178a98be82dbe7e561544febd003b29ed679fe3e421f9d90615194c1658c7c0969697951fdddb41050b1411b1c77ac5c7e8f25d68a92a514d0078b9014d2c800089e31f838b0333d8d6c46a7f94046096c0679d07fc242902f03edcb7624bac8eae969c6fb4708639672494a4f67365de1c6c40d00d56cbf2209e8e0eeac77fefc73a0e37a0e6ae344611fc25fdec90e778f48a20eda50a35e48256ad1595f6949dd1c7496b3a0ac517533cbcc9ad42e700cd815ac405215e82ef098b6b7ac9915377f5b94c7d23b1e451ee06a79384849596576bf183d8c09d4cbd2283ef3cb8777e5d772a6f9395db3037a67e52b35f693718d1c5e505c890260b051d63d84edbfebd560686c9921676b36cfc55c20ca35ae0eb8e11a177a0e634ef32e2253746a782015aea5a8ccba41199772830c160708a419067633b9d30bba933807eef33e30f406973113ed4cacecd1750b6190fff58b38daf22ab06ddb1f3bbd8fba2db0d4a25f969ffdac06ae3e086e8a80fa35dad9042023586c1c830ff7ca299eba01fc93f5f4956a2ba882fec6353a113299bd84c5916bd095493e584f6b180f81a63d5876f89b3d9d9413ad61fb50df11fe2fe24fa7b188e98d8e509daa844a3609ff1e16d1e76f8ea46dea77d83a8da4b8b8e5399bbd0774f4bca985dff8d098140cb8b72cd249c61fb29c4b710d05129665e4e016830ea1de913b46dd8bb95c2db5395125455efb85e17855aef5bbc6c1cfa31145a932d00721f9c33879bd15064f78a5ddb242432cf0ff0b5a0a989e336c7312d04829aecdf36169e89fa4d025cf08c63cc8e750977b2539f110887e395d631633e2fb4715e36587a0bde726c64c72fe0ea988ad9a0a08923e5a3bca925d466191d367fe7899cedfc3a647421b4a7cc2b72fd744dd78f3b9a791c8bd5c27b7a02058f9ec91e89999a5a8d1e86368baf6f8240822a4c7f7ed5be7d9504cde1c840612a0c1bf4c33c4141494b4928af983da6d36dc8475965827140d9c872c0dd1ba16a62b1c2d3a94fcc24c179cfebce3cf108355d1c09eacfa2355a51afaa8d92470707def06fe80f01109bc037aebb9f45f3e7ab738a1d7f23410175ef227bf8d835ca8e6f4842e231342a0fba57dcf6f54a308668480600cb9f1e063ddaf1e9a0189bb35373d5aca6402456f1b94300f976596f8ae37500bbb1cffb0d4052c066d148af94875720d8451a6273c99cce596b50a220bf8b3916e8921c8d1759f3d6058b2c0753a546bf566b1900fe7779f68f20d913127b24e9ca2f59cdbf57d71096918550496405f32896858561098065e98602d37ce9c580a14d4ef95db564ffbf615dadc30844a84c9d4f42b98b3a7ad2efa3afdc5297c3ad22bc8f344207df2d7a89d3c93a1ff3094e8cc9bed49727f6f07254371deed2d7d6e3244973bb33ee7252df97afd4984092602f83a4d470a9ca4e290ecdf57e5a3ef3fe27bd0a0892a56e271836e4605d812c28eec041fbb5f83ede7791b449d9550a3cb3b8907be5179631f0485ab0266991b348e48a077d630982254415027cb09e942586ebc9eba1beda2371f85b98f50ee28853a4c105e98e92c3abe90a7b015e85af54f4e49b30ba8eaa281184c3bbd466fa096526a0e4d680f3f71df0e5a29f544c8551b134233d564e9234484833c53796ffaf81807906377ac31a8cae12d313e0012b25c8ecc1605bdd5c305320ee64b4a22afa64b9d0964f3b6550e3c6f26375a1c6a2981876b588f3e9b6f026f2ddbde7648e686af1f01179a46109bf2fad16a3b69044923b49addf9816164347449f27671dadecee680886ed7b623ae0c5549a95d2a00a58f9f09585166515c8658d34c9e25b92d4ec959cb3d1fd0a68fc7f50d22b826bcb55721acc6b226fa226a2cf5644d379840ac7ef90c0c3924a333b181d7381ef3519d9551c82a810f952acc5032555022d70d64a9c4c0694cb7876d0ee1278e207b34bcc18b91a48ccdbb1ce24049c0bcfc2af17d00b461053ba5407210c6838ea5e113fe544553627c1b6aed512217192088b9d86b02bdb00af465afed0069340212c933d3f677e1806a6a78755c80570cd60ff3838f1770f18d84d0b5375135cffbf4143e9404c682756475f4bd0cbc129c2b45912fcc6c3cf22dc92b94f280497a847c523676ddf6dda7fbb9bb0535f4c9b366e467761219536fb2cb310048e2f4721ff8bd3fd12f5e0120b1f25abee2c318aa903fb36217240a84638511112de160703851000aeb5b29dcc1e649eaa42744e71855919a0d360700d2506326531d6a964bc97d5969a7261068e194a8d366885ae64440d9eaa830600e423700673c4f1957b111e315eaf2a5f12da874d994307c3ecc4b7e08d2704f3bbbeaef9968fc25c3fed325a878581b01dc9a1118c62744b5e61c2a0a52030b714367ff9474a603febf52e6094fc29c257d10c565550c22d14b7081e21ddc2d2ef1ebc600cf751dc19af67ac86070d80528538e27807ea8aed04aec8353d8fa1c9de9eca1cd48d8ab4234945255c6683bc8960e6fe199056b294ff2fbd7ebaa33b325e8d5b05740517f42277fbc55434f670d34b0ab576faab32981398dca24b8ecc7c8e31908cb2e6406e446bac7c63539a0ee3086f882cd3acc128eb08ef34dcec5f90e6d8aed52e793bb0dc1bedeea7521dd3a481db3f9d550cf392655b297bdb44659cac040fc12be2cc69c16f875cd26e197f00354864965cfd829846cdf0af58d78aaa2e0cb66c48d6c3e636bbe6df292e62dba9e21b8925b64bdfea254afcc63a1a0c7a93f46574e37b6db4fc5636014c0aca8ab0fb041e0ae27f6e0e3e67127ad7e42c68afd4dec935482cd1d97d8d40c8673b8472be98b7e72fe174462c6cdfc0d4b0cd41c756d73ffbc936325f2315f349924a356c62869763cc94b67a257596f051a5df24db7add8c0bdfd6083f5911599d325bae0a297d59f6023186a921cacd9e20634ea31809b7c30c61d9b1eb914ff3d94345513660a5c4078fe5c289082f573ee39473713183e1fe88e1f1cdef6dc1efd122c5f9fe8352b570212c0bd7609c437430feb2713dc329548c361ecd13b7ce1f2bf3aed689b69a5e7ad2922336ca2de918e3905856354e94b78ffc29ac4bfabd074c5c5329c68ccd0e9a4db86f8acefa15c5fde2e46b1dcdae93d5977061fdeaded28e9bbcca92e23ffa3332913d0d99ab648d9cfdcd6eec610e181abc18895c3ced82d564ca7be8ef9b45bbd2909307aa262c0391a0c21cd28c8bae16eddc5b712e1195805c495af0309841c8bd835180a7f822d2b423fa2be7ccb0c1bae1cf4dae1ed2eb6b174dd5d67c0339c70b69503e0c7e4158b682f5e6c5ef0fe625d8ab983cfad431602e3eb2321fc47583712b61c221eb30340f58337d2f8b6e38d8490b4c7290a2dde38ce898d8193294722a343f9e43ae6cc7374e5c10f3f223e6f4e4686a7a99aebc56ca4206a441fe2815325888896b2cbcc8c3a55801c22d3d393fd620eb972d8ae15b76d81e6cf645b61929ca4ff2d73d421a83eeefe012d01425bf66e8e6b94721083d084c1eca9f5604b98df779c072ae795a22672c96f00fbe1e3def533bb65170aeaa8f9e1b3a6428794d3a900d1eb91a0e24b5803d981f728ef343c5dfba59fdc7c1bc31ee8241b91d2179f7c005c9b85023f6b481729fbc74c85b85b6a18660c8564857bb494d24714b23a4d2ee0851f50d2c70f0ee222d96fa4f5619bdd08f5245855fe18c1e5706a3e17f9c0d6ca362b44551106fac2af47343e4f01fa7103e263faa84e81a8215667caec395b8e5dac707ab3fb53aa88ee5b45a984a324865112b208d34df25610a994f0f1bbe12344b0cbf41515d848cf750454e34c8c7b13478cfc42c9e309720bbcec80312a29844e4191b48137aed2f395b6a52ef594eb8a683541f59ed5998b845baec1f24e47b0ce16680b641553eb148c77516e894c41e219be26d9cb2926d18c5ba737fc81a1a0087d61115216090d889f9f19f0d85098793e4edfad9fb0b720c3f3acaf17b87bb90ebfaaf3bc72cafd3d5c0ae7e0dd462dc39f88f2f4953cbc9aff8611ad936f4682f1938e2a6ffe8ddf5646979030f9c9cbcb0baca3480b3df9c42bb0063bd8773ea7855ec6e0ce6dcfe0f07b6e6322e4273032a4115773e14a17cbd2a2446311769b12b9885fba251d79a555f089d337035442de8716221f6399799e39cad3638559a7b0d75af7e64090b1c1a76921a3eee7666a3fb2e8a3a21ffba77db29b5b8141927d68fdc9464b2cd384e2657135a44c65603830a5618d9eec19dd3e47d0eac61e378eb475cbda24cc27d23362cf1acf0e12a807564b15a75e710b6cfc010a33753f60508eb26d0e6b351e9ec86702a736ff899e10b52c90f0c8fa099e3260389ed0d209a4e3798141b94b86a44be276a80de12082fc173cfc5a00d0bb85b7d1fca72cb028bb135caaeeaf30eeaaffcd96d9c4d40002f46627f1bb6bb6b79432492903e603dc03c103d6bede30c899192819167538165d3c638e69256c1e1c9b8deb59e16ed9e6e9689dd14cee70acc3446378c3ae8d5213a5a4182d17d3748b055a3870a175360cd8f87d6d8f16c2f73b56bb5e74822565155f1ecdb3d9b8b67e7a298ed13d810123fe9c67dc8f6692d096decb8247550613dad6a1a5e642c602c45a6b9f5a6badb5d65a6b2b0cdf3d320a002f41887e3bd653adb2e3575f63ec5d9252b275bb604b7486e5a592918b058c8872a6e26b8ff7de910c0c2ee09d8e306032458beb5e2653cc3d05674a1253a2702864a0ffff96ac96c579a70c654d1dc348de1792ea27070b2aa2d678f17e3c80413be49ca9c8a499128944fa170ed4dac472dea6c8832b423aeb11e88ecd2237c457fc36680104a42443a3163fc4a450c9d9a2a2c0fed7cfa75aa37ed6c003a17b3e85f9148ab8bcae10f19d62e546603f3dde4a4ca9c4f1a03b6e9552addbc2567cd01db74c7de35d1247b2343d261e42f70d3ee8058b7168bbbc18690c632108733e2ab81826269fb21763b7c6f2e95f3e3ad8229e3b7358973b028332903920722cebca54f9944ff96473e52ec7906748e5533e1dd13d668cc1728ea19167a07b8c3e5d5b95ad72276c39699f138861b59e973d3105ef616674f66e704f74e8411b6089e1057580d278fc79812586f94396ad0b9483a5d845627b73e566614c3b6f5da86434c6599e287caa74e7799e3a74e8c0202074a7670f81db1d836198057d00e3900e971732c0309644777a76e7cdabcc86b302440811202a8744891a8290729e6942690674a7a3492507026709a1cc0d0e4b0811204244c699171068935028c4449b5a775c9d5c789c900afa8105ba63f38465c2f14d1363139b246c62a020ba6313336fd6999233c69d0cd905bae7ce64f5dc61149098ac13a782501da3a4121c0f93f7352aa0d6c021864b9b8259bb14ccceec521d0aff2283eeb903adc9cb8a5c3843c2edbe516bfceeb0ea5f2a127bd3755dd7755dd7755d07ea9e3b24d6f443f8909c3241de73a7b2a69e895d8d0db72f18709f771ed809cf461740b49c6baab6505fa8271d3ae88c262a14a1c5e13ccff3739e2123f0448622ca64193a838b5266241606dfd897f33c2d8b87e0fd11823844e1f3029535f56bb3412c14af912ccd560e97824b06248356c3bae1a020bd0e0804ad9649e6c6e72c09dd738ee7ad367a922a72759e55a5e3f383ee993c5be48a8524c94c922449dec86499c93c927142f74c8667eee8494f7a5a11f29e4b6bea1ac85510254be176e3110e4b3a74187b44300bba672f0bada963d98d35f5bd312b5aaa345b673ee514aa1c42661b8b613ce89e3d56288ebcee5e22792d79ddff51a27bf6b6e889a5d93a31c6e14d200a22e30d7ce31303ddb3277a58db32c7c4d71e56cabf7feddb6d2f232c0dba5fdaa839b126d29aba3d45a1820294c6ef0bb4a78711314a84ffad279df1fb026b8d3baa31a022fcfe2b46f0373f25d0fd02b9d6b2a6dfbffb05f21f5d2a28bcee2a5657785678ddfdfaf07672c199a0e65c9a31c0084d483bef50773a9a25038cb0f38e42773a921023521c6e656e8021aa79c642880a5cad562b8c3f1c4782f056784567489183affd0cc91ee35889d95be5d5157a5f92247194709024c955a13b2549906d6a8dcfab1b330ba034eecec02cccc46ccc486ef1571cb5c6570ae59385ee9414f3ea570f43fff3eaf32aaff2cae6921b428736268ea5f005198db163841964e199b0824b1213b24d33d184153213b291915aa372faa24ec7d66e8116ded978e31777db18fb6ebb7dba0a8326b05fc12ae44cf094c25532f702ecea01c34036375023a020419b08419718a14976d092a851f07a650a2da0a8539ca82950c2513ef8169d99083c21cac8f4106a276bea5a8bdbad9ddceeedd43ab7d3967a186db38c97876ba186641c11194ae36a1f24afa1f806fab694b9819d929d929d92295e3bbd21499254752c3a48c9a80c520973b640d5b4901411bbdcb928cb3297b92c73994b100c24496aee862d308e5be99e4b1b991b64ebec38b2ac5560d80707ecc491e312a15056604a15250e1c2023bae7923cb7b0a60db6ee2d0298a8e13ef04869413174b5c3b865058358a89d3575edabd1424dac5f05b54f03b5b0d610374de4116157087244e00f8b822488874f0d1e583fac65856ba27b6ed55853a7a70e6bfafda981e7b3c2524cd350c6a160d482b4360e04505c7c44a0413470dc129f8f2afc7fd007a7d5d2b292d23db77616f388a3f9a034eee5000b00b45a2d9c5b5f255b0f53c1af55bfd6985b9f148472300e5b4a8cc04a05eb87eb3745f7dcd27c58abd558cdc66a438ad08a701d686387c75b1a35e4504f5b438266ddd6733fdc1b31319f2962ba1a7d5718e3d26980116440da787740777c92a059378f37f8c432e058bae373db642b6bea24e8500cbe422d4ee1c1934e29a5f4ba59c6137cd319ffff9852fadfc27fd7dbc269a682534a29f5800a5d37bd17a33b385625f3408a6affe26caffd2d4f29859175346d497457194e2a6eb957df7b6534b9ffffdff25f6dc79bf2fb5bc6debde39a41ebe28b45199928e1bdf7de7bf1a62449f85756733c4c3692a961299324ac35cbaa1332363c9e8bf386d1d242c72cd3b28baf6c09bef6e38bca50b1330a6557f4fc13702349784732b556259bf9f7627c45715dd7755dd7b58917cb8d8b6b5f8ff385fddf60ff5f820edd9efa1e4f8631c6348ded8ad52c0b3d4ce77e47d3f0de30a6176358cdb2e839cf3e5f37b6d27b5972ddfe5f64e92c51b5979a4fc307d2f07d20087e56efbdd90c1de9c8b54063f33b1bdbbf714e09d8fbfe25dfdf33b5461e71f7ff4159e8bbf3c62a6c3343c919bd6a4274c209100c6700c119401004c1b045d74083ca9bf2abf178aa029ef1cea3170ca0417934288fd66bab60ad89f23ca2417934a89535f57ab2bf69502b9ed1a03e4ba2fcff6950452c89721ad4d568509ff52c8ce634a8219c06b5e276a341ad284783a2410df9110dea5ab12ebe30c6b8096df1755d178631be17637c31be70cdf5e27bb1063defc361e5e1b05a799ef75589b200ca6f159cff6fc0f3e06f2d8f8cd2ed7757c1f2abd17ab295d2968bdfd2a7d5565a29a574db8da37674354e0204c3acba37df9c55b546fefd1708ff4dedff2542f7acfa70eb3b6e9918988119187a4aa0526113a3d86c991734e6acfa8c7437d5dd1afbf4cd1fc803fabbd9ace126078920715b6daa4d051ab5284030ec3eb0fb3e10044392c425909104328e2449e232f3b2efedf7f7ef5f2cbb248d85344807ddb199d5bccdeade22fc8ab06ab4fbed6f6adf665ee6b5b0b935218e220c61288a221eff3442d36f9da7c64e8d5d394f9bf3b439cff33cf369c912294e97dcd4dcad395d7283e255582e1e095dc5c66a39df1de2f10112fa8e76d796147a41b7843476b1694d3c3a4e99b43ccd29939607f8a96e0eb8b7e5d2c75aac386552a5080f71ca240b1af0ec2bc6a35e9ccd2452b8203e4cdc2a3e33a04c500bd4b4b9cc2b44a03b36b3212c5ee236b4332fe312665eb14329f3d13d8cb5093f3c78564574c722076583af317e42f7fdfbf7efdfbfb98897608e06ba63514a694dbf3170453cc4bed4fd188f76ac600256fd8ba755e452696c338e75ac631d47521db92b18a67528b43789f45d89999a64634a1286fd0fdd43dc86b82b544e8664cec9694db4f4ae9336ada99332155c478ce58ceb18448726dcaa6a976bd7d201dd313e7638ded80ad7a9888d8d5249605dd07de3e414ceea4f210e26decf123678ef7ded546aa7b6b7bfdca5766a7b39f52f9c14d7a531ac05dd774ead52a99410389f844731af4bb56bb0a14b5d290de8be5337ac9ee170dbb3108b8f89d684634d79e358ebc46216efbb0831ec60cb8112a2285e376e90c4a7ff2283182926959cf1f5b820e695d7e28288e01df4f5926911dda9585ed70dba845226ba53b1947293008c5d28e1a8c040ef182596624d3d84520c52081bdba17ff97d6591f658fd507d9ac49af01879b70bbc407bb21ef830f6024f7a7581f6f401ad914af0ba99284d603e69f1ae9ddc08af5b0824b869fa50cdba85ee9524496b3b1b839dc19eec091403ddab782a2f7bbac40e7240a9a2bc222f3486d9a03b164d8009d1f9b4c0f38992817e68a0203650c702bac1c587069a1b0213b9213e1f0e5c221f3ff01e83c44b94ee6d3fa059c8c11c0dca8725518e457b3bbc8a70d7022262838f83cf1400808e843816b87c82e800e4c30b24608c01e0420180bc012cd95c12a29bfc320281079f2362808410600a199c0f1fc440a2f2f26952004e091072cea01e301c829673fe1899f180138060d8fac0d6f78120185e01c130a4431ca8038cd21ef8b3a6de12daa11d0a59619aad20a519a42c4dd3bc4292e547921f499224598ae258238a35a2288ae2686e66cda6ccefbf31a5920674bf52243ae3f7957a01e17261874258e87ea5b6d29a3a9649de4b29cdc260fb4bd8663337f3eaaed4d662595913db2fb4ab6d16bab1ec13366deff8bd8d4e60d976b4c36e9611cfaeb6b5118c18518988ee3038de18e7bcb17b6d695770317651addf7d5ddabeec66c38355962d01bab7ba7bd0bd69598707bf2a5c9e8dec0aaa089e1eeaee887aa01babfbd26cb53cc254741ce3f7c3f2c62e90ed38a23bdd82ffa64cb4f85e72c8603ceab475ed7b3dc639be7ed674f78f5aa3dac0fc12fdfed2c3b02f8fd9c8f130bf53d0b1ff72278a9c1c044e9954a172817a801c34cbe79da56cb7afa68dae25613ae38a5448e1d49d863294c609d694eddf38ac49443f8ef7d572b0ec4cba441fb040dbedc5b6e5781850eb743a6540859335551f5a9cb4a665845239e534e19d9e3941f05e5103f88de50a51263929fe1b863581c07f9ff0b55efb8a5e5ca319548fef817177b4c3d355b099086f36b2eb4504b604503d740f17a315487e6d1e3c8b2e2d8b36962d6379f07bf936b23faa089ead897af5b8a788c733a296d4c1b68cc576dd2ec6356bba59a3d5cb4129a594562f877df94e4b4a3fcd726796512a60506bbf5e8c73be2e0c6bd2e9641e0fce326ddb2aaeb5561616120bfab78baf21ad31c6dce70513fa471d2301816aad2050cb66a3830f444d9ba0402ec20bda135ad27397908b8b0b0038d8c0c5a505e4e2e2f271e1b48b8b4ba73ac0c36fe7f15944e258d42bdd1ae0e1d707fce606e33cb2264e8362d9ca628522ce632c5a12e534a8d19228b734281cecc6c2d03064b1404ba234a8d09228ff9c3ca267b4425be1113da247f4889e511447194f4451c6135114457104c170efbd474074df1b0441d206497b8747642f7f40e4555e652f7bf923c9d28691b4611c49922caf8060f803047f80200882e69123430c41529156385ffc9b546be4df7f7ff702680c43a23b89ffde2a15a9442a91481d2c8c885248010a119253dd11fa889ccaa99cca9d5903254f9e80201816f181457c1f0882a1e77de0ca03572bcff3be2b2459b24692358e244996579070e20434cd560ca61983699aa6d922c9f20692bc81244992069514a8644d9b2b1d640320800000b3160000180c08078562912c8d23b1c7f814000e4f6a3c5e5240368ec762e1502847511444311883000c02010c0200a48c430e297c004a88ce44bf8ab106874144fd3b54967914ba45556ca8e1d83862b39986bda97098b486de66dba4e9c0c434be265ae3d784687c5c5cd3b789687c5c98a6af09d1b04fd101e6e646a7e3e1cd9f2477ff83c62855cd91d70bca9e1a8fcedfb5986e0854be060d0b384c48f788f82344737ed2f90ecd9c5b2516d7399658832748a89e458873e34762e2604cea91ab4b409b815f2384c4a29da67b7e59167bf8df016e60f38625a434b83cdfbc33014d5f13adf175219a3e26aef1ed229a3e26dcf8bab8e96322353e2e40d3c7c48d8f9ffa0b2bfa68d2b9b551ca0145475b350c958a37cae6b746cdf269de9cb0f135319a1f276e7c98b8f9718234be26a479becd25819152ac1c46306d764d22320d8072d7c78525ef5b8391550db325ec05ad7a5194c302565488db5692fc5628691846025520d0cceb9e829325ad24052e0c7e92b8fa7e20584b0d6b5f93d0adb1250d4d8e0501444ad4a10009ab8446c7ed807918cb9a39518b66fe8bd3803d2120c2b86a8344575a1c0c46e0917f2127576dfa202fd3aa12796c03a0329a7fe36082cfdf48e939385dab2761358ca1de56f6f47b9878a9d409e29861e94727ba63b26f2d7b9e74a9d009833163e9452fbb3e76806af4fbf1a7bf961ee35d6051290304f68fe86f21b85dda231074375729024369ef2bb00bd0184ce1797242fc6833cccf8ed1f0a732da80ccfa7b36a7c4a2de56830f180a3063a8975ae379ea0126ed20965db86b319637f2222df1d827fd500ea18b0accb6a7533402f6ab00c30f4e12387ecdc0af83422d12fa88fde6c518d49455b375d3a9520fbf33f2586cab77c76671f7e4c2b4dd33d53e60b0e7027007f5d2f62645d888ba89f88300f08e464781329d13c2430150bcd5130ff94f7fd86c0af989ea01239b577847e6f276ded9132e79cd1ad9d341ea3bb4b23d260332ade21a31be1901bf5c00823c9488cfd9b3e4d37b30b2ada007a772b5a1be91b995724bc6882011238848fed057a5576fc9a3bcefaf5e39d1e8598c86e8492a79cef7ae5163a571a18e953d370589e9ca098c2ddfdaf92556b611df1e6b40bcea95073f04072347059f3632c33160d6261f2cc192a152aac15697c366db5d88e123ffe06ff5ae17d33acd65becbd357e72b5cd8db582e06693a1f504cd22aac9ba52d272fad68fdd4c00988e0cfbf163f24c7e5d1672d39d1c4155b8b40548dca1951716fd64dea7a7ebedd33ea6d104b214ced1ac4f76d7bd583607ad25584241ec9758a36aa18d26cad19c1af63bdd1a2f4260159dc0cdc67d2ed49002b1f3e32c9b4436e42962bf7c8c58428dc5ec0865181d76ccf165dc030bd5a3402c68bbc27146367435415a1ec211b1c4f4fc26db54706c34782ca2816fd8314e2433d9057063292b3079d1f05cd5b9a845cedae96441fe0f17b900aa770ee7ed5100cd8727a3e1a413d13c03b9cd0ad0da0b040257796240422fd270a3bf2fd4ac4d11ceb50cb1a84905edd1128799c89263016f4f762943c69e4a63f558a01038c8c4bcde3ec777bfdb27e1afdc4bbf89c65e563fc0eba1c9e55318e2b0b100ffa4fc9c09cf96485bdef7da31113e520156ba49951bc8e6908a67717c43293c61f5a8d3ee8a390c0e9ce3f8a93877c7b6727102d38f7281c7e18ce28607d849413e753bbf35b2cdfeef8ec8c09f5c3513cbb91bd1b294a8316cae7ec7b708099b0dd2420642e28adef32f9890bee5b0cd6e3bc6da52f88924a15b7c456f979676687463da9b28cc6e3c6aef0982d7b1bc34596860813afeb6d87a56cd0668ae0e1ea9410431995cb02b6b2e3c6219c4d4bc25aaaf52dae73ba62108d9b241159f62f10120b46352003e0e79bd68345b2b875b9b258c459908c5ff2995dd27b6cf46f1be6739741e2a5bb621694a910f89cd2ea5dc1b4737f364fce3eb8469aa413c15f1cce289d5720f1c9b5d9ba25607e39e9245bc3168d6ba2b5f3ef0fbe01f4ceb1d111df4083c4c9e632bb0cabdd3998ba6020149ee8e232578bbce050bded2e493249879d2fe484c464fd89c9014cd84958d15d589fb727f9ec85f34ec85f32977b967ab7b0305984252cc22f2966d7454f94478b90985c9348325bfea736a5fff7b38057ea033d5568fe05f698f2c2a4a467d45095564d44dd4a975d2a892f9544c627ee7f3b7c56ba4ca2d7cf4511f3db8d99c6b0cbc953d45082852c087440e9d65a88deb6d75053fd5f01457e779be118c495de20ebecd6d7c5bd16f473a7871184271772baf86c0ae6c79343c10d60208a681881967a163d99e1d34627240ff87278e06b198bbe0413593bc734e5de72c1b3bc1a672f350b5764ed8db3fac4c60eb5e49f6b8a651b263cf9deb7626ef8ad042d7de0cb46f06daa82e9d45cc8c44ed5bead4de0b94572b5bef2b084f7756eddda2aac61484968d63093051edc8cd811d05cf8d0e864b20cef93dbe5e0c2f85990f33abace889a3cb652b57b33d1d749738f3ca1721b34384d9d219c2fb0f02070ac6d0ac6911d45b6a300531bcdecd88a3bf244008d002a0a0ae88fd6bd3e25e3312fede1dc0d0be4d5d0800a0a286a919ae7460468f0521a133c8a26883f14093180fe96e3c2b7f56541b3e2f878902b29c20a5c91c31a58d4559fc45d9f5df4006a85cf4911e44bfaca1d9c9fb76bcea957ec7215672acd333d140fbd9ca44b2f25e7e7047a1a1c58b9a5991a0e75c72ba6c9323dea27fb00e0e883e94fdc78581a501fa4e80801cdc70cf3d3d05271140c92d5c7003b79c292cc850f3d2a10f212dcebc458cf5692ae2671c3816d6780fadd16dd25994c883ecf469a08bfee1d3ee56aadc5d4ef46585304b0166d686cf1a4faddca71c2a801d98c469ec72aa67ab77bc7e5dc55c23215257d59097d7630c6ea76cb247fe3ee838d75b692747fe36182bfaf41e93b7576ab5e92d5b1fd51b793d33af11d843ec720fe39bbdd5cfb6b5513e2a3403135a2f4006b27d252c18d638ed5a240783c9954dd4c14863dbb62e9f9d40a21a5329ed1434d055f59a35397fa3b016d3a87107f3aa74243175e005787c97c3524f4687dfa2ba6669217c6cfd78a263f6776ee8f672c9e006ce73512fe6ef6c4ddc51cb443b99e5602df19a31a31b560f55ef05a5229468c7bf30b1957ee8035c0643f289a993ecea334038a65bf575a95b54b11ce7a2ed6d685cf43c215f2d2dc5b7f83af112ecaab05abf7c3b7c5976ac42aec3c6f21a1f71b0326f87a0dcacf81dbad9e63951d29badf1830a1cf6b507e0eb95da817c08eed8dbca594d67f4c88f0ef372abdce4e97ea0b4c3cb607a5b468b7cd22a518c11c775e6f93d17cf19c842ec720f639baddeab3d2b6b1272543ebc53909393e43c4bc6bbb25349278597935275494790568735c9f6c89bd6256335d39f74398c8230563633f01e73f0e4cf0f71a949e03a75bbdc0386b921a5973d760fb81f0d15f041d51a0c403dc867c5ad63460c2827d4258fccd8852ebfca977c54eab7b04f29df2d7691db4a21e8861832b37ed7c63a7edd81163b25cd86156b8333096820624fac63ffc22f04a1ae36dc09ec9297509e6d2b85ae0a54c2cc461f897cb70330c8ddc0b198ad32af8623653af437216d5cdd778c8ef8083b3e47ba885bead9ff472a51b37a8e92e069fc07406b032ce4e34ded27d61f16221462d30179d71fe30bfccc58c674e915a4b0206906d2f2d030444a54b6404c86b7c8f5e4e7dd35626451a2eabf783c7924aed2449f70326a13bc63b085c8791d87372bbebe4ccd73558bf27edf98bab60d77e6561f6d041ebf5fce3323da30a1ec6b9351369b3d97addabfaa9d61c022d4b1be454ac5f2908e58b3b8617b4b0a85a511ce51a8853182339a2e2e748cf14e18c44b2d8781998e4559fbf0b257d2588d6783b99ac8b925434840d86181123fc3bfc40db3a88b84089e4e20c5768e2c9b738cb20fa2157dfa3ebda769ac1c8185984b51927ff847b28bc13f0686f720168cfb32658f999f4c153c38f889411b164c8fc9a50f0adbdea7e56b5258a613d490d5dcdc00ceb89dc44df08def28c9d912a0fb621ccfba0d259c26c7c2f65cc5ad48e38cf2cd3d5a603286563646279da1bbe176559f123531954468b307f3a3458b08c1c3c5a1a21a246fa150c2ffef54dcfb8a724e68c62d791f8c980202b65d270137ef1ca7057800639e5ffb46c839f7cf44973a10418b98d1b0acaf9d3e77b08695ab728b5e18612ba9ddfac1ca0df5ac6095170e85afec27e291348487b9459d334d20ca9a343f785494e0550db26d8d6aea19732c552f9af6b609c21c85dc8b5c86f164012cd20371a49208797f1c5f55e6c1c032a01f0b65ea587848bf726e192dc507b9acb6eb15c7f3506435dd583336c8e573b6681d7df651f07fed217c1a535ea61aaee713d3a50cf53d06f1c2b98af21d22d867162016b70cbe4184ebbef10d29a9c9b79861f77cd92618be4126a94ce858e16dd8c8dbe534dfd689eb763653ef68178c5e903632645f020a0cdf10815381f8b530396d2aa222010be03ee5b6efc158d7d0d24b5aabc81218883ac387a01bad765a9340cd375c58b4d0730565d3fc0da67497e9140c47f9062c0f995aa50f7819f57efbd5705d4e429beb1a1c643fbe61e92de965eadc35a7f86e50a1bb4832f843572e2248e148093ccabe59a0c94861541138640b9165b660503d34868b7eeab712925328572b5bb0a56159c2405ca6d7259821f281fb6ed761f7d34dae4b150575dcad84172555f44a003c2c91b58f3c89e80f4eaedf34d14e3f59c2e7b937f31f91117268c46c7146623afa289111965aa0ffb6e104b56b061720ec54acb572e3f49ff078df9243cad95119d5e2d9fd25efcc600e4122f288bc541ab5ee7cf83cd70f092654d4273f63d6f65cf679cb50737dac314acfc4563b2ab5c2b7c3a084702d72b4672ec4cb93616c934a9962f06127cf64d30ea4e672b5c92810de1070edb3dfa994d2a4489f654f2ba1ed32e83de39fa723e7cb8ec48bfd3ef3190bd4c260e9d09e151b1a72e87e47bc3aeab4ef15ba1ecafd29b65526dd3f626eb0d27ca8684e59e422bcaa492d17cc70611fc7ea3d2cfc9edaae359cb8e5451be400c82a5b451fd6910ddb1dbf92b175c64cdb124aa1f41ebdcdb6ff0e10ad78adeb30c0f6defe027b5f97c604e88c752eda0c7066d3f06f1941174e27ab4c193200fe2bfab7040338bb6450400f6508327f5bab860ff9c22ef3b47007811dc4b68f1ab7f7d59bcd8a70a4266134ca067ead3ee23327969251cdbb6e38cf20ad55b71c723a3e42a3c8210edc1514387611cdefd913d0b4553727974ec518e1e01918638312d560b5277c9c961a9667723be82129bc510db2d07024b4c8d96843c679de274bdecae69c031af019f2c005689eff5ec7497bc362f4fc52b31049e494733334ccc993e87550513fcbe06e5cfd1e956b78d85ad01f0d028a3ac828470033afed879e90aa9373c7c14d68194287d4c6c0d6d5bf7b0316bbf454e8d2c3f2e2a3a08bc1e83f87374bad50d2bb58d249fff6f6f7cabd5b38d68e1f10f590a85a64b8de79d67757fe567174d83731708e3134bcd5c6f0fc86e49bf8e484d5af1f5bf177029b1925de93b7bcc575057193617060d36a2f2dfa058b89fdcf3b33357e1ca6c903e88ae4753465578070c4810694dc9597459fddf835e3217dc2232d8724e41aab88a636a8c870c5fe5d1735d2603aafdce79b5a3ca250a27be1242fc57c4aff54233f5d42f754416a644ce5547829cce17fdd939f63f0a4c866c0f0719cfedbcf2439a37f1d41453cb33869a787538994703ddd3574d18842b74c05a9b3621ad75e4fa238493d6b8b4063a684a1f9c077f021d4d9fc4b42cc142376dfc31fa9ecbf37f31b6777a7bf212e0c8cd7a3b87e089ae11b3a603f257115dac82d69c5e7d34f76b0f09c98d58aa6981e3800b38a0041bfb8e60386e6086af95af6df609b517d7d6c21c9a796cd5ac7686e208b761997559f81a19be9961420a9a138af35b2df77c12266327274597db3c6e5dc6ca1d6367e78d91650d8d821dec9f889b390ce478ba3eb74bb5fe7d0c5c1efe6020accaf9f1f2eacbfc6be46965f6937c8a333ec5e6948183d41280b20a3fc3b86bb5921ef4a62efdd8e31f1f8cde9fbb2c0a85a45ea29692784a93351af5c9b1dd0e71c68f5c63fbee0458361348a6597df6c93fae015ace9f2064de32075bc1255ed5033418e838bc1962bd4a76d1d5bcb7eadc8225b20d4d3eec1343f345f74828f85cd04d886ed6bde249bb5e9a2406877c22fc34afa12a262e70447e3296ab0368a8194f6175af82b6241f1b6a4face92e70483d29e53928ff137ef5c0f5a17a9d6eb76860ac50730a8490f36073ef03a874101884878094711120d0a4108c1853b24f0e7ea6b4cb09b8a2cf91123caa6f998d29b79fe69a2903a5b3488d35613eb7a007abe1b0f92e45224ea33b8f9d75ae4d3426ee1007385eb8f01174445f204a8f18d5aa7abd86d84d417463b50d8cd070f2f7fe82021a4023fbb982b4cea10e30e547410cceaea76285023fd2956640d3c516ba31baf4900a1a1e2b716d590b2156172a6c824305c41c8fd0e43a2e2ec480c32b3715f8d45489e6279409451020a1f16d7b01ac2918921c22ea2f10d24542842818275b264ee98671cc39922b827c5df3a12c581f7cecd4c670834fe87c86b84640222f9a6376d707463dbf9d3cd3dcee394d70b3d11765450b8d134c394b81c02e84277812ca00aaa083b36ae08e0f8048ff7310f08030b43f2124f807ed69115620419f70420eb79f336c812052113c8d31cd0e88197abb051133d09bee55fc88ab83448833453b2b40a07c6a17c58a20218830a4e34ca1b6f51297c1af21e006ebcb5406ee16e8d51beaaca2631e82a3d006cf389f01ec2bd8d318d5f1587f7dea087b78acb399f783842c24b37a0e2eb7c759d1926ddeea887d944ad845de0d570355761c2c459bc41a1ea6b672f6d6845c6411c53b48b8737d6dbda1b4a245caf51fceb75ea78a640f53eff387c59eea987392d90c4848f7557f69c77cf0c57042f51b52869404fa97a5aea6f95c7e3bb5aef88a58c4cfddc35c337fba00982765c38bd404cd2d1efb3a998642155963389f944c50235af0f30ca5cd5239423fe1b6c6e5eac300545557118dcab4217178cc1528f47600a041c3c7473d9c66c50f51fb233ab498dd3ea12afd754d38415553cc6b85627588fdf59cd0f6fdc832040f4482d96d690bf469623ae9c92db552105ccaf03d1005d9417757f74928a98230bd24c631d1af294b1fd49a973ca3cd5de3be16961b00e04cd31e03802fb8aea0ffb431ce42a99d1e92975b3ec68fd0d86f41336044b67305d76b020bc2bd73b079c34b98822ac3f0fa210e851ddd59e1f22be65442d29d0df67c2644760ac4b14f12bab386eb69a4747cdbd8002c01a0f41f8f442c2c6ab0ed77fedb812539d4bf8f1e5c49bf1eb18c7d01661089e7be9cd37b74f5df80cc10e2888b69f011c7fefc02d748d18846bb5c3328d26905cecf3b1ecf54fa49e5539c094ae8ae715a941d348f73ec5f3103b8abcf2e0f702567b00a379617d431ab514d956791d6147404dd173f786fa4ef045a3a559d66c0696af0a5a8e99a58f4308cc0253109d01afaefbfdc0cc3db5c9d3544bb814c413ea5e13427c8482c4fc6ecc537d347bd785dbd11a61bf8ed82988a664a03f9beda875eef7f745c5331b2929a0b9717474db91cb5c41f796c27ac32304e1bd1e2eb9835cf77547ca09133a43013b2a72844ba23cddb209e41db0e2b81e16eb2205f0b6f7e634dccf10e237dfef1e978a41b824368ef6991c2df2d43d49274a9877f7a1cb771cec48660e77a05cb79aed1a19fd27c61605342c176cf5b5f25db3e4d6d2b882dbcea6c5718be2b33ff8722716f8a14c983e16a16470bee6317eb6f7f820b01caa1362f2b7cd40535ba0640577eae736655e0ce83e2145ba8d811c100e22037bec6c4244f727f929a8db005048871192e994c9ae7c0fe7630b9bff92f5615546eac2bae681073218b2b4a12ccd013c680fb18b2acf207192d321a36b3ec12c4b27cf6d1ec1a0ee124a48bffdebf4d2a07b6476b1bdf35c42f75a65a65e1bc1e2c99261ea30374aebec58e29d07d640414eb542ba8759e9234b97f15ebb0677e0645093dcc0e6b7d037d9b7f6f5141cb14e363bb856c5cda8e0cb423f7d8d24a71fe463ae7b3c982ad489fa244d31cb1cb5f4d32eed7d090649465b9c1a468112c074aaa25a351ec2fe287704189598e25f7c423ed85c145fc8c1a70881e156a0deb5a5089f552caec664fab3550bd325770b22ca7e82dd0fefe298bc2c54f7752504030a8b12dca03df603e886de3e0bc4176471e4d182278e35c87ad32724bdc890671613deadea39272ab6d01202eca6aecd8838cc0a520372acbe0b2ccd951d927121033441979aaa241decb0f3d2ed07bacf946a537d672bc2c298acb073409c23a0eea0bfa7407179f28ac2bbb2fb861e6ffe3640b7e45c63ad3456263076f9961c296d0335be6950ece3a4853f62c0601ae0c3d387ef1113a34889ca39d6a1c519547c74bf289c40737dfd151d52663183312fa06233e7ffb8fb1444056486490b840f3831781c1644297526267f4c15400fa351336aa1506755f347e210e6128b3f8817c20bcda94851dc88aee800a833d6c8f461668c7830ce655746a5c35270c9801cc0a68e36f1c044ac279f1f9a36e32d7ef0d5d09a6e49f07f5873d75f9bcd3d4586a2be24d8dd176b48706b14ab0658d60636487551d8e8a8007a7a63cdd7be7018a056ab8994a006043acca1ff194171aaaccbfdfe491e8d0d84a3ef09b09aaa0caae9585dfaca7f03f1654e6b63674de76f90df1bcf910b53f94690d034392e27b0e322669003638ca80e3e19646afcb3085cac86e12fa3ed57d8509f6217cff30445c23999f48008084c215bb23ee98b1980e71d5c9287c63a1b757c25bbdf8d77fb9dd434e11fe9472216dc6c45bbd901833c3d27e904a14fe708c3bbd0db6091e8851b94f7b760e93e6b1f6b708440ed085506c97c3a4f055436d80d6db754b982631f6271143163092bb6914a977827ca6399a27e905951c68212ab060181b70e5d6135859eaf4f586b18fe80eccf3bdbadbe066d6775a31e243bbd8dd2ac4feaee3a1d7ff05fa89f18c49144c98e5f313e40f605d47d0d54d4c56ff460ba4be182d8d3e3654a2da5d3cd1f23666414a739ad789d9af06a00d6dbc2700d3f6a7537c94e4958de23566d0b197e25316d16632040116419cb2b430930b587e015016f2f5ad50b050b571ca3b801087c81a40c3121a3155afaeee94ba726fea395c4ff3bb34c2090deedc54f80a6bb80dbb1d78c1a8a206b0222b5cb93a83f18ecca18b36fe4eb2496a4373697959d1d1f0f348b4ac34ae026216afdc3c6824f8e88a6a28341ec059229601b48ff9e2e0727d886be2b90498b9d15e269b5def42654e1a66b60d5bd194ceb847ebd0921d23a28a7423c0410fff25e3c201a10f1e2f279a439f4bc7fcd8c02f280cf4b5e5134a1463f2cd62e67ed89c4ce4fc89a5cb7a0020d04fe5c4f13704d16ecbe51bebed16a2b637ca3bc74c1c2d553e7ef65164ed9d3a64c680f202e95c276888352b52779b2f4ab8ca06268a0010851480d8a42851db70f5ca3908d4a6ff4c2bf26fda0c67adec67bdc25c62d1bdcd396164ef8005ac9262214d867c36d6ccabbeeea078331f3015379bf57a5c70e6301ad33e98cbd120227a8522b9441522729addffd274aea4b7419c6581881a03f81ced22dc130dcb311203fd02bd84a0616b8913819d400a705f7e4317a12d69e99b8c27d5e3085ecb1f200a02b02872c6ea382c06c1148d3178bb31b614f08c37e5e864d1c9396b51702c7dbbdb800656d674c18945c4462d184eb3150a7d6942f970a103e2b61c3125773cef6287a2e8a52bba444db3bcc4b80c2a69b732ce44c4ee41761ea98e022433051bd551b4df76c538e553961e7838f321bf75121caf49f2bc0704512c54191a40a4d9c07e17c42f8444e573ed6f068d55e03aae5e080415c58263fb9a632e4c909fb443ee14cb0a542df7402349d12684f4ef8a56fc75e2289439e30e9a4760fbad73a6ef72606642d079ec2bf46a5e675eb5e5b6d024cd02120b16622d08accff5189aa2553c59c7947e82549f3f5dfeec1ef6120bbcaefebe7ee586415a7077f667ee20e7075a7d2a8942464cb3c6a4fb215948959666c363998e1dcb685c81fa0c22b3574f0552114c46f4e9ec8787a1f387e5094a06d61d99891c4775bc647cfb1fd2704d995d408a3f786d82c40beb72b97503d4b42193fbd0b2768bf352758bf51a421cb248cd14eb61a00e2b3a34edec661c8fe9143b97986406a77a1e60ce40c00dbb6c89a9c961c810467d88f7164e055e9ea14103ec4241f4a55761bfbad12e2761706ddaefad0e57ea8e73098ff1ef739e36fcc44ab705f8bea06f8a62a4269c05490808d08d4a8aeb02d46dd2ecb49ca979a93823e105cabf85ba85a82d71a46904149241efccaa3450121c67ee930c2b64a41006e2c49406ffddd5c2682e6d772bd572f97bc7885ad9295c21898047ce3b76b54b6fe4754190b6bb97c85f50e54153c4a6787ee743b12ce116580a5aeea936c43e1a0c989086b250768d691204d12680bee5e39f69fef0fcbce3f703a48ebf349786c9b0f8c1bef5bafdf7b320c16d8453b39db4ed7295ce0144fda07b486567023896ab63c8fabf3cef2203cc99412cc133034fe8ee55d36228ca28ef4846036ba01961886f75ba767f66033eec5279ac47656ebb84ab0c218b77b031b13c841b61c0f954ca864d91fc4c7b8848048300c93208a44a594a4c95eea9392d0401e745b7cb1a90171bc1b5decfb450d90b1942ead35ef8fba2e02a8c26c9f796a2699b51711151490b0468b3c63ba4bc913566fdafebe46da53cb0f7042bd24eeffd0cb4d22a89efe50f8890933e6f90c1747bc932189a00d6a33af76d862d9bd5e2ad769c0a6c10189cdc13eca89bc68d93dc541958c8b165f60c9bd6093d3fb8794dce11eb3041ba7b683ee8394c1701af600cdf0a4964bae4a1c32489c1641744b1820de30d7d088d058fe519b2a77fc99453b8b6132926804fe3e19f29a50a11291f8686753c08a11d1f4bb91950bf395a5271ede02d9bdc1f563f96e998553ec3a23d414e8e82eae50ee7a352cd8042eb4b559233bc08e6e65baee2d032acb9da4578b0b791dd7481edbce7b39a3d8de8ea072ec23e88101cfa4eb8283a97bb1cfa9503b72426866c6517edb05632734c665289433d72b560bfa135afc58325df1f71f1495896c86c7e8c88b46cec05d70816b24af82998802a4147e231461498f0cf57127a0efad5ce8f1d035da0f52e131441730a087288ca37745b4a23d0eef4ebae3427ab37200549f0f31dcc59c63a642797773b7c9d60342be2d4e191e0ffd584496ca663aae987bc7f604a122a9052c863a9e4e90e47eb94e8e04e56f3a2e48825b68a2932df60af71e726904bafa86de233b305390ac82edd9ea496a446692df532400118e988065f2d9c64a82200470522ed6e011a629f4894d5866ce4e01634d2b0031cc019c06c66358afad872757fc3511554ba060f5aa838ce8a634b81fc3536e019bcd51d261d667dc3a49093929b41cad2435c504dfb0483925548353ec2c8bdee43ba1c7640641e6741ca4c440b239e2b0b085452a6733a7990a466f09f0f8564ad46490bf315b17d59ab640d56414a6bc233cfe7bb62e3382c6466b536ce974af2dd46e51a0e22181720a9dff29b93a8358c744fd1a9feeaa804141878e1080c3ac23e753df53f14b6d0002cba9cb28bda760625c7ffe0a8bc98aa180dbd602cc584720c60985b5a970dec5372a5e03cf4784c9958e8ffca05c11ea9bd7362bfe1c87914c6b175e2a13ea6f59aa87070e46b03c24af8d480040897b1db6bf56fec692457b634499147c075a189ab3517a01b2aead07fe7cc96d8170cbb1ed6e8546d61ed8330333355b124e5db9d2d886e3ff315bbf228f2eacdf69609037ce5659e7ce581d2212a21b4f7214a6821cac30a9bd17a6e053b6e0607bb362796bde218616d70e55e6d8a2613c466ae0b9837acec080fa67b0bb36ea527971ec22cb23c2e3d95582293641fee0ed586688e9169e2030c865b96ee4ed4c459cea47298892cf53ac4612b8273291c8491507870cce0aa157da3bbc823d4dcff13e8103ac182c291b3fecf50c9820620e1c2815de8a39f645c4fa8b3cf1e5ec519656d3c544d4456bc9501ca6e104b4fdcfee22b5f75573b65b07ce23446258593b78859594c5594edc894594a087db85b8e8181a17956186756639c34544658459aff021579139ca4ff6ff6a566f1197b6ccd7b7e19e384d7319d6b57dec41b1ff3c3e34eb3c2f37edd498c557cc9db73e0c01fd0b8a673c5a90aaf9818ef5fd4cf15f7f9d2799dfc1496ff4933300b27eeb5debadff8839d77eeb513181285c8631c9c956d2149fe023a6031efe5f4494b9c1ef4da31f02d91fa84f215ecaaca015211b936c3194081447fcdfa7a8ac13a4b5dacdac0517b67032d480fd4030fab18f28900635b62474ca6f89ef2e327b889b5ce124b73d39ca4e4f91eeea26f583ef86b201c18cfb669d44c92b07687e77c76cede7ab083f7abdaada4366152a840c9ff9178517f29373cd64c936495095146438172ec201bdf48256676907666b11876924feeaaa91144cb20c4f8ef0ceb8c31b6803c41f5a3cb1705c8b157f99a1b44c0c4d3bcd848975cea1adcbb69d780cd23f82ab6256a2401b6a3382767f06acf8a1753061450dffaeee5d0284056cca2f047d247ef9374f6871c3c3bca9d415b06ee21bfb72882a9262434c4cc79fde3ead131f4440b40bed948f4df194d25933f780878a4bc9aaa5e2128ea4baef5c56db30d55367dece5d42e2d4c912fe7de0dac04bf5c1f00c563ae78387645b082211e793d43659737c876dc2a8eee8f21a295fd8ca0c5a20aae5dccf6bd234a6c1c0cebe23790b9d86fa69978debef3fcdc2f66b4f32ef0c7be1795ade3b669b0e561a1fd1faf884344db4b1eda33b16d2ca64208c5e40dda41967c7d50f669b0de6c1ffe4419c6f22753dd0e9ef7c8bf3c974e390d0751061fefae6c484214bfac0c9e8ba56123c0ed88770773a63ab6600870481562203f8ba3dbb8d20e088467c8b44174f3f37ee2484f31f5e874953f6347e9f548736ee676b747dd0cf30c4a330045d7f374d401eb8f99e16319466b324e9723486d5267aeb16c87de794c42a9fa67380de4d0cba9b00c8442380c8d818c1830224bc599bf6aaf4653a18a53bb03a6cfba35a1a90491a24d11ea40079b737dd234a04b036e5b6db92c59be1b4cfb18674d2d6b2b13d2df90bdefaf268ce2836ca45ed2bf6ae8536603a235d8f051457a1c40cd007cd8af38d5ee3a2b59789ffe683e43ffe9b832ba3196cc8118afddd4df2ee7d4b630e8aad5f438d84df63d6808602d3401d8620420513502aacca0b8c5dd6ef05449ed07f6ceaec6019243fc39eaee4cfaef559b88d79ecaad43994ea87baeac110733ce4447a96e7ec76d99c12c26595247ce0ee24422ab9ec1e8a61174d73604b34aecd7fffcae01747141a1a56a142e8a9e13ac241e6209828b0837430c06ee1c9304a2fc78bf8336efb59c8c39b0c1571f4a477972d37de16fd67afa22b716d22874d5b0b3c9f18ec0af850a553d991810246f8fa2d52c6903967b250ab4398a42d228799cc9ee3a20a32843e8ef1218b38698f1695231b9b24b19ddfd737ad65dab8cbf4da3987b2351b6a5965447f0d6682fab31d0076d6936d3d14d5ea456d8f08c3e84aceb0b03379e7a9f8227e6fc40988154f9ef88e5aa6a2271b7403e56486979642ba46c99f6b2db91a52b0b36651e4a973de02c312a249a6f52fdabbf2e549ab8512a7d02800332752d9c53732e24703287ac5256ff3035d2d261cbcfb725b1a77184657b62f21e44b40f6ba49f194fc29d457ec5ab0833ed07ceb0879eaa2d937b869688370197a9fea25300b3bb25e1af9f86edf558f6af3310eba8d861cb17918825bec6de01dc1bca1989846f9de6b905f38cd6b8e64dcc9a684363448b407b9a0d0eb7d0bcc252382a74636f3648486f577a975470837a9c06d54ffbb4e7e2052f8a1ce913b713b144cf135362c6706a010d1de31ac985638408e9a92dedcccb76ded5a0cc8a9415a0da5f3bfc30474ee3ed15e1e0b599f2a4cf62a8f306125413d449035d2eb485c2415305c60cb15db190ef2b57dab4835a43f268b06741c49cf43e20d0a92002d0cd40ea813c350ed5544f7775678d6ced8890dc4ec0eed2de4644eaf05dc62bf0e129a501957b4e14ee082a87ec4c7811aa6fe3b632dbcecd5f6debcad79670871da3c6d7158aa5cfa285d790087b77484d130b9ce3374539a10240c22bcff429e4adfb7168118c6fb62ac93a57238d8a733c1e4bce36663c38c53606ca2eb6ba847a6714c3f65200fa7730670821225b5235b7bf4c7c7342ba90aaa50fcda5fec2f99f0d06cbb6de39b291f9e15a3ffd58a3e3c3a597fc88486056cc476f175509f8ca7ae99d45a2664b4296e07070e9ebc954c58cb89a278b416be305a540ca04cc04a2b88604a3404730596bc628e9fa6282d71efc9ffadb2a9e6a0ebc82af23d2206c58bd391a2593630708d51904a389a802fd0e54b60cdf50f782d0c0209b9c3559ef16af2f522907273828320b4a1c165e6f15042a6138eb2a6b14a63380fa98e016f786081cab365039538bde27dc2637f69ad127388212ae55e51fcd9e6c02af89f09f256a17a1824634f02080dc2635ad8642182530fd1aff09467c07b03aaf5d1c846651e7de75061de69b145755db254a9beb16f0571ae2ae4992905222f5a3f2cdcf8a6de20b769b226361062609188f53ffdbc0fe9fbb9e19f42d67252ae2dec9f92bfb3fa07125bdba0a8d41bf3d0399b398f24b3f65a9f6dd1c8aeec2d5d44cdf23889d8cd1665a011ad725b118b508e610b0fd0f6ffd2fcfbca9f374c7c0642d3d74307f1b7244c88ee376bbcd86ffbd0f1f74d81e06c0595b22f4029d3a3e98a9b6044c25ef3dce2754f5f14e5b4fac5d64954e1d186a49268db54d6515ce9504239db087c4fcb7636c3f33c03173e4b4cc5fe82acf30a94cb68595605a465c392b50a62f647d7bbac077629f507d8ce7f6338cbf457651ea9a770f9ad4915c1e03e60b61a0e11cb704d6a61600c4f99cbbd1ba40be51662bfeae86f10db47d7bbf265b71552643e0c933afa7a52820f3fc56c0b1b4e04a1ecf1c00f13eb2f0e207c68c0c09845b9bb425ce8bc7a455f6fed2860e73ccc140a16cd9350de518d77a318b3f69790aabb7640bc23639813c3969b771361f72d5acf9fc9a36d6b0d4261fb86e7687325c1ebf5f62ea435be3f115a83cb2cbb5a49686c6017347c6b8d95293cfa2f65c630e5242338825a91ac057acad5471b7963ad5ea0a947973e10abd1889d78b0f21d0e225454b377ac8015792c47f6d77d64817cec6477647de2e8ce858a9eaac3199159141011b0e7c6bb99e2440f2937c489b38a1e223e06d707254ae226ed3dfcbb7f2bf95bae34a42d40a3a150b3b128529b4917d3d9f1723e94e0c693c9a137b6ec64d1f543ab230251ec6bb3c682e664d9379625d87de56051a3145514a2bc1a8e8c8ae61f8a317074c7d0a4a9428ad0b80d8878bb4ca4796fc1c603dfc5b1e930e31e015de5b3e50a695188e88085542a5c86364f10ac644502592a48ff11a59a495e19609c74b254c80fbc0cfb42fea5137d81630c9ae6239b156d838c6ed10bc7e8af750c9ef2074cbdf3d1b71323779db7cebc56cf7b81d165f9765710c7d290cec5e5068f572b23e8473e6752effade88d2ba4de2086b74e2af9352eaabc91fbf924bca6d06c19d9573fe4e79acb142fad39040de0d7cc7c62a3ae8e95094285b764b6aa09edc3b29065dffb9d9a3078d4fa05864a4701513f5a11301c6c7071e5638b06d05143539aed4cecb3d0ceda6df04c2437db1051190c315b22c7e2c19a709789bc448ed81481ff203d5648074a1681e083fd5a459ca6a8d16631acc86ef23593a400ecdc0fcd58881495dc842fff452c076db1b2b1728e5487a61c7b2321403f4383f5966a411184b7a874824adbd8e8142ac3f9c011f2c9113621d81f4ae209473ffe519d1c209a8b5f980d5a5e36d0477e42940f64056b971341f0992983149707d5b27af62410892059adacd5e16cd0443f58339387d1325aa9d1982d400dc00490e677db498b250107e0fed1e2840ec1f3655105ca01acd5255dc9cbc9ce988e00f5856e1a0d7abb66aa7cfd8843a222c6973065f9e2657ee762759ebacd5e442f05257c7ad97f52cc0b1b720d84b4d0a3e12ceb0bc53df8dc321d4b987fe45b3813584231546a692d35c65e461f12e59d22031825f9a73afcd728f4c27a7328fb454e82d79cf773fc0943d41ebf12ac91d85aa213f65b15049f37d8fe2d79cac40e0530ef87a5fe168b360c8030dc5ecad2685f42ae6a14743bb6a45fffdf1905ccf31021665d36b4d27373d0ef556676fca5b55fb61876868e59b198e99c8c34ca726799891fcf364fa4720a14a57297a15a1d9bd9330e091aaced2dc86380242aa3335e2412575e7fd558cdb92421d0d6af4652a9cc3d813bcfa881ee62806e16a3535e31344d3126034b54693a80c00f62017272f1756e199afc9e47251c8113ac1babe4eabde0a297d4bddfdf180daecdc0894a870bdce2c13f622c191fdfd07281642eef6b6f129eb709faaabab7eba17d9b8ecab5001691a7172308c0e71b3dc946e3e07c92255e4c2cdc620f4b746ce360371ba209f6c691077a3e92ce1ac4ee8131ba8fc8c8992647beab6b5dcb09b3bcd52c76994ff2177da16b66681641adf0a698121e53829258299414aadce767b62d05e731661e8fc5c125807b9b1dc3ffb100d030a1128e78ba347ace2820b7adb1deece982d53257e00bc8522d5bd221941b2808b8c7f4376d0ad11cd03388068407030a0fea685c30903a4e07aa31e9ae52fccad045332e99a4cc8de263f87da403fe1803e3897fec1d88fa587b5eb289ddd13a66b131ed0530300cc380bc8620c7916c774690f808f0746a8316b6c1244bf7429dd4e1a309416defb626a59432a524036e067a06600634c88bc2acc19317c5764a8a94142914d87c0a0594e8c6cf64ff700ab26b189b103f7e2f0173bceb2a9658e9410e13587f962e78135f761f24fa39590fe6a9f8d54a52d73476e3075bd8fecca8bd28a5bb7cfa405c76d1a8bd78647360632b4254d58c602a7e8dd18812318c104a29a58450c6d8f9094416fe8e2f2ceca0083a090bff535dfe0e858485cf3d25833fc2188b8257a5b2760c0b632283353327b0f03311b05898d397e9f377440d9975087d12438d06ca00500b9617fdfab0855240697ee961eb48773f73327887626175fb57b7ff3b2283ec502cb4ec28d05ab0bc6ef7ea3a07b50401dd19562805681828550ea580b911c99e76a82194f47476444aa59f1d6ac89c5ad73f9e6281fd4ef73d75f2d48f613fe8447564cb1054ebbace3406c426748139dcf51578d332de61b9ec910b668813213d823763c0547fe5e1e2b692dbcfa66b7760bf18a331c7a1419e6a1a1414330085c51c878853e801e6c800e2b41430f5ea202e95542a124808210414ea7e34f6c1fd1ae8a3b3734061fd3f1a14c1d80f16452f9b066c246a2fd65ef308c27253acbd50a8033eb79f3be6a9f6eed740b15a690c28d6314f7db15fed890115c5b6b05c7dea930ca2604bcfa552778c31c6d87da4f1c2ce8effafe0756f23478197eb2260b15f1bc19c460dc95e1ba27df6d9ebb4645ea715841a92fd96bd4e4ba7c52fc335100beca7ba3341d6c6e6a08425ac0851eca7ba03d8096af9fd7e5c7ed8cae2973815e0fd01849517d5517cd1e276517b03d809baeda5891a4c908e19c58c54465607f61da7c88d19abfb76a9f43dc3cac00f236ce949df5d2a75240ededcc8e82a56afa0bf48d1e7f257ad5a1289d25a6bed4a690ded31bbca7895c143c618444cff422a9650b9700b42bb3260f78b3c6508412e62a2dc2ff27cf18a81f28a501a07b698b85fe4b99f47d7dde955bc42e5f2472711cae5f7235609ac9123f6ba0cb68026f09b11e5bacc80026754200599b32fa5b4371578fb0184257dad9b0adec7a4cf2209c29bfa24d6086ced68f775d7d09e7f6f2af477c72e57afdc9231c2189bbd1dc352fa2e984fb90874d195a1390cf4c85c278237fc239c61f9fdc771b40f8add7ed94f6a206f695d0379aaeba9769f0315e9a2cf381f47da1353743f78c47359134374f99170e42932525f027c499d1103e8b8241748ddd7ad73fb935d912eba116a375600a6f8955c661cf8c9e5af8e03996b882f126eb09f0e8f4d7bf0cdb030fa646d83325813e17743d890fbf8a563c87e9a12fb591af8d03b147cbe901b01bcee820cd67c0dbbfc157ed695532005804bd283680c268ec41851a217940ea281f9c82502111635c57e7c04a4bb677777b7cb7c4f2eba5f929edb5c74bb9361be092670d1e5d2cfd151ec0e0067c27bd72e97085ff321fc07c1dce6fe8361a0d18ed7f1ab1ca8ff78e736cf8dffd8c9e93f7ed9f88fa1d4f88f7b40f88fa3cc7c2ca5398b89b7dcbea2d44cd41f8cf1ccee547777fbf84fd5e33f56fc52a5f9a8bd76116159ccb304f230faf3f81338159772ff11388ea63d77777777777777777777777777777777f71e1cecd46680beb3ab257c97f026705f112ebadea16e1e5eff123ad4cdd77bf37cd4ae1b1beec5902079c42b8211b66aa9676f8495085ff32174cf5f64865efdf31fe9d58ee7afa1573a9e1f49af723cd33c3feff40af5389e9f9df4eac6f3094aaf6cd400e17986a5306c87246e70b5d883e7e727ecf373965ec93c9bb6c07aa5fd2a2b912a007e2580f7f1fc407abc07a007075b3e38d812818317de8e8397dbc1c1ab83833707d77d491cccee071cbc331cbc2070f0d6b81bc7d70400aede1597e2ec89a3e184b4c7bfe286748a5f048e01355c089c033a2e06aec8733b7470357048d8b5c3dde0d809c72f8ea130081c47f169ef0806e22c42bc856318c757708cbd30110b807b4ff1fbe0549ee20f00d783839ed258b79bc77168fa1585f9a8a9f44f03b5b7cbe8480c1f31d721238cb8fc1cc67fd1b579781ca7d6e6699e587d5c2e97129acbefa3e8327d35616bc4650727602e6218710dfe11bd1b7642734e74f887949edf9ff9618704890e64c8b209760169aff46f12c2f2a3005354b07458b11f8b056456cba25dc7624d21202492bd02f8618701f80c9f31c05fea5024d8fa86092132190a81a95430150ce6442c4644042c46040c168bc58866cff4f161bee66bf6cc9ee94382588c04b1582c16232a8a3f1c1dfd7074747474e4841342423020828480080a12121282d1c8c21e64d147380d2bb18ae499a71b1d9185bcb4c7441c8b5dfe6c0719acf91a08e6c048aa0d43f358819ef759e589c26ab97ca2e2fe7085fc0383e3308fa798878747c7fd2011acc2057893c30aeb8d05ccf1a72ebfc9e0150c7dc6e5da8571f8c753fc5fbc68302ef30f7c860c1fbe0b30f48aa580f9791ec7e92e9ce2fff2f2e23283d1478c040f3716f6840b5d1a97a0782e95de028cc383533e4930e5a5f85872727a70dc8d1bb38b5d2d793958ff0a7370a4627c203de3ece84f44487b106601c681bc00ff0ac09cfafe4288c01cfa3a7a4e3dd81c6cb80e71701d76711d6e61a4be9442aab8fc5dbe7841afb7839552ce0018b9bbfb8cd0af0c2929ba1e638c100a711462aee53f1e975f72ace521165aae138137aec4921e5271f9610e2bdc9df4a598821bbf95c1ed0032c40ec528da0de98f620652ee11bf2c7425c742377a6e3a23eb8fb7c714bb1561bfd33de9d0713a7556ca4a9991d8f5181bd7304f716371998ccb5d8825066f82b01fbf7e18fa53e995771a076b0e2d2a3f00922abc710a13c269a114cce9bf2de4b57af7412d2bc01b1e2978e35f04c2bc63310d6b210fd88fc57091a7382fa2439c82905318bf1c47b3027320dc54f02c29b81fa96156ac0b7910094aacf69f074df9fc6c14c60e1456eaf882f6dbebdce66ad848903be297d4d12136611a7d3a3f973e10af2c4fad006f1e007398993f1d5e701897e378109ce237e2f27fe0e54bf45c061ae232ec1c0878c36cc445edf1736f69af612dd41e0bc1b296d992f90f29e9b9255c38e4e2223e81f1697461dfe1e80406cb2fb75f5a8138de2381608e9fa8b05a7aa2e02f6e7ff7491e48ba39acb09fe4e1a3eee3239b22acf652bea41230a7077eb204e3c42b724a913422881e3d7272b0608142425cb4026ffadbc60656ebbac753ddc7471f1f31fdf8a8a7b41498c3b77f0acc911fc59525ea9135e2587fc00a2bb18039ec400ee49e05c4e198a7bac01c3278256d60af9fbf00c3d600c65a0c28c6fd244f7c6d6113c6fa7ff1d5536e7f2b71fbddcaed871dc5ed97026ffa59c7e9f5f21ec7e1d3c95a20efb9fdfe5eab00eae2405dc447314648828b0b510eb86cb1437784602284103a10ccf1dbef2a74ec301ce84b9017420e06cc5d70db085f89451fb5113f0c1a92cc2104c9978c22a5c015b85e924acbe83dbdfadac8e705a5e32b1ee1ee6e0473fc7ae740bd82432e286ef3f09c58fede88e8b4ac10e8edfea987ffc12022ce0981c13ea24aa8d2730b25c4f4a817608b5bda772a4fc5a0d3d2def4a6105876d5105f3c35521eedb3f8f254bb1598ead771bfa8e3be5e2f2a4e5a6e33904701c446311bf76b1bf7e3587c398eecc2a9fe2faf973c0a81fc7120c791120798ea8f010101ddbee109ec2416eee33d3d502416fd9c141b7bda7b491ef85f7cddfe1d57348d1b5cd7da23e753596b8139fc47b55adb2ae0f682f94b972aa2f40d7c62ab8053fd55b8badcee2f5f17f940c5e5aeb570522c0f30ecc7e3760e2b2cb30ffbf81cb90073e0490b84285c3efa4eb8fc2a98139f2f1fd56a2d842f70b5cb49f7f4115e7ca8dc6ea227eea76d3f63aab85fbf88c2dcafa3dc9612217339620c13375a611fe77161adfd2978038d4ab287d807d224d293866c5f8a2a8d93ad1257a96537a158f0db7d44ee89eb56737c4b08b2b0055bf2493f4b2f896410cc31823935ee7c1cfea6879b02a0972212d2679a6d6fd69f25ceb600e6d43103db60eea44d29938c4a1065c22dccf3296fe9d57cc19d3f2b732fec78d11a87f4f3331247d3a9796a6fce2a6cc72984eac17e304848af3e29f4f3e7afd0027d219803ef9c3fbf005b9011ac98324c189803e76b5b6c6772eb1a7ad3021b0b3301f1d2799a949efae773d78ecea799d2285381d7b4f6cde0b0fd32acc7838aed875cce287a6666afa78cbf7a9573c9b22a299d916671c628e3fb09c2e95d9452ca8e177a476eca3d92795e2d65f7757b0d10a77e4bf91e83a7e0e954ebec38f392cdfa31384ee9e1d7005ddc035780156663b012b154a78737b8fd5eaf50844d28027af2bd153cf740165c8416cf4aa0277f85a7cfa48ceb31c91807404f16a1af64ccfb34d50e0678239f4a22ae0b81299922f2b90e04a6220cb260815c7f8f05cbb5be1149074434ba740f0117642041c4151be0608a211d4a80840f539668e2ca0caaa1633411a3c89c46e02c2480b8bb3bbdbe0212410500e2579e20b5149a25463a4389db0f8408049a9238a34b075280d819438861e7855ccee8c94070060e672c69d2a73fd10829c3fd8510810d113261ee66e4e0fac7f61a2691bad7b0ccd082644691193033b204f634a32568d68053f82afdaf54fa4fcbfe33691e6c9b698b5cb7ba99eab699344dd3364dd3b62ccb322dcb32ad542ac5772f6532524ab517b00f150b0a696992fd49fb19afda93a72568cea04e2aa564a65272e44a2b2d554a4b31c618238d4292085653ad3d9dbebfda138d4a08468411d21fa91a4766aeb4c4b596b23967adb4c639e79ca54a6b9c71964ab546a694d219e9cfca5752e1e3b24edacf7834aa1a968db5efa982b4501840229b156c8fc572d6c7fa1a161572fdb827ed673c9a1ab6898c63594639e57c2999657d5959da6a4f42efa9583746596bf745494b924a4a4b52b694f2e57c39492492c32be47a92f17f304bb5f6747aeb59b68c633dc535fa67ae7136b93effe418a1b7cbb78e53abb5a7d33f847d96c5fa71e7d7973ee41448b5966aac92ca5afa5a6b8d55525a4ba54aead81921fd917ad908e9e3fb76806ccc98fb2171b96eec48921b7aac513ed78a253305213dc618a3bf2e8c4117c64ec6a8e349bafb8bacf6eac6c9460d9cf9528230f3810734ac8c69d3b212a9d229a33733fb66448c2e79da83efda0b3208536828256cd90d3b85be31c61863945d8c313a0d2e846c027c18142f8a7dcf657f04924f1c07b8a23ced79691cfaea18d5c1c9a5403f62a484e1437b82a27822e57e5bcff6d28874d8a05c9e5e6d4f209018293d9d29e807fa721c0d4ca7fac3f484715bcced6de7f6e6a4f4d2884a2fee2452fe6b0a65b55738a84ced158eeb36668d9b51cd39102ee5244cc5b8b150effc8c568da439e7c1a571b34cd33493464d9a73dbcd6434e7b49bdd8c86f681463fd0663410341bf16627ed8646730385c3e45d8437f167e731c8384915d401a08d3f12af8dd7b99f07df1d8a851a335fe375aaa058d8baa97d243d8c98617e2c7d2cbdec20e7daf14b7ebe5819861258549902045a6046ad1d5c735af5588aa5d873ebdc82c8cecea0a6ec1d463114699c9dba448ccb7fa406c7d9f16068a704979f77802e0186988471997ba6e4ff1d2f85ec7cb7a3b3698f3d1c6c4b6e6326a8cd22d42bbac5b4712c1fedb5200448018076bc4e4bfeeff821ff3b3a2455fc91ecf8efea1024ff9c0d1b9f75bb7a2381fdba45840347108e201c413fa7d37f95bb8ba20f9c0211d785f0df09078eef381ccf71389eb405e9dbae5ee968278d53f3dc7f15a7fb1b5fc375085cbb8c3a857a4671fe05e9502bcc8efb66b8d252c961e087c607e2cf3d3fea4bff5c733a85606d8eed9f6397a7681e88dba0e1501c0ece48bcc17d0de5f29fb8af8fe88e7d523b6016c3cf001b10b6d03fdd6769842cc3186a941e840e45fa7a49cf5fa4571f3c1f997766fb0f3ad45744753db8f669742899af57c6dde572b97c58ccfd184ac3a12c98b032a61f4a50362d83818b0eb76380c1012af8e1b20424d2d13946907da5edb79fe995f6f0d2876dea96981e894ecbf459c79f5a4703e9bfccf4f5a3c9f44c745aa68e89fd2feb6820fd9ce17249c8bc95e970306ddbb66ddbb66d2693c96432994c9fc4b46ddbb66ddb165f6e2693c9643299b66ddba22cf50c8369734b96d435467267dabb7183e5ff54dccdb407add7633f55dd82b094359769664894d247f9cff727912825a9be8646a3fca8f8fef1274f29e59491d9c6534db1fc38d89af698344353733bf3925966394b2f29a594524a49b5d494524a29a5a49e73ce39e7ece94d29a594525a996c07b091524aa594df84d592cf0489ff69cef94d58add92561a2d3921d13f9b49b734ec99259f24b4e026352cc60720598169c51a585a40abfe524b54b5eb2f82eb98f24a409b7b2cf90dcef48bc3294b447a2d3d260a9a3a1fe079f027e93b080f6f48174b7044992312ded99e8b4b48ec9f659d7d1505ffeb433dc9984f61b97845d413e2f9e1d1b94b496c0312d9e1b2dadc341cbb22ccbb22ccb344dd3344dfb26ac96f649b42ecbb2ec4f3ed84cd3344dd3b42ccbb27e773ffd1c22838ec3443b74016a411e8268c187354e3ec81ae3a203242e3850633d508932a6ab104363215183e285434b74200905cd1d9c8b1ca21439987121260b87a12539dcef54e3fa85434b9a482d58043f4ef8c31a8e45cf8889c2f44e969793db43368471bf9aebc1fdba5b5f477ff5ea83625c52f8c7092de3f6f395d7c7475032ee57d3b026dd28c4f8b9fdbc83a45728db5edb184cf5438e712c167322129470cc06c6798195f01610e704268239fe1ec7bc9c9c1e1c77e386ec5ce854ccb156f3c09c293007d60a3dd50b42b9c58b08deb48a042596babffc386f1cc21f32c8cb35a716404829a5ec5841a01c796aaa24cfd095f26724e501c853f0caaf4f7aaa34aa69c1cbd48cfd832b709cc032bf9d61752a1f3b08c32506366a293589a2d6c04628e60eb692601e218229b65ec8254c12f7abaf30428009f3c3fdaae34404bc8a623cf1b8904b982c5e512e0c56a3b5b6c6292a6d02053243af0c5c7e66e6e18a8b02a545314a1e90d861c8f295226c50a4412976a872dd8bebdf168c134250a10110508061d4aa528b1a6b904f9dc2ca46796460270514c0b41b573088abc0891841bab27ccda0b21400c09eb838b1c5317e9a0e5e6eb71325ae8d3a57a8b0811533e892832e6010e30d30308a220c1035c8402b4ae1a65295c05ef20596a55cbe3ca403161cb76f2d4d11344f4f038124620004151bbc5060850b3620c490155fb8b0f8a2e5b3055a75d12eb6a059ba28a2932705d4bb708887089e1faac820cc8931c648ca4a5b0cd0a8d63a54bd162650bd5a6b1de386a8031dae3fc31630b37eb450bfafb859ea16d8021d10509200ae3c808cc9470b1b37a3790d03ae97b422d79f3d19914458edca4303be0cc1e08a2cb400c10c7490b8c2c91721c8a18b218eb832b334d1458a8e914ef4b049b14419590ad985433c4e4a57c84a659c31ce0fc8297aa031c6219e1b6e8c3fd3ab223593499c205c38740318376c518266661abc2f6c752a39ef0b5b3bcf53fc3870a79d40b9dc512ef343619f1dd80d9e78395173f56373e517c048caa0af66677b3539ba9faac909eef7e34a39e1195742085f8578e146e3051ba8b407650d2b563eac91cec318310a1fc154dc5dc89b5d06590d86357c2c3f842fc09657fa3a9c849b6340742af56049170e35698289a5170e3511418cda85434d3a703f15515402dea0ca17452f1db89268c2004ab09ca0035b7cc8125445b3e20135c3889985045224ab9a1471fb6313236e7f6d92814be3c2212662a4bcadedf5c7244add5744bbf32b259de4ac4f9a098825f95fed24f4fc6b4fd919217995df53caaf11def805ae7c08bfe8041b989da5d43498c327d81057809d73101bf0a6ff0425f6f36edb606863099f52c6f625648ef4016fa6ab1c764ab6775143d5b6b2bb6b47d9d1cc5865fd4f4a39254b035afdfaf43ff8f0366569c07a51ca0f04cda6a90e64083dc4602c3b1f484b2965f7b953a72592fc3ec6e891c8959f04932edfb6ccec92898c5af4c844ab9fcee5cb1f716845d2d56cd466da8b524a496ad91af58ff25457b53dabc5b6b56a362341afcebab5a01324b260247645bc2ce6ac7476d9e4aa26e7d3f93e5f06ef2cf5195bf071630ede530f58d91dedaae4b4aebf229e663d87ac5a2d4e49e5894e2b63ec8a68d79f708eaba0c11bf827ec58af32de2235d75f90d5ee29bbfbaf75b6cfaf7677ad1ee3fcfc639c9dbbb78ced710c464edfbb4f4a2244728518bd7d68f76ef70ac0ae87e0823f4de650b1833fb714d434c898dd653c85615965e3b998ac8bb0b13ed378c10630dccc094938c7b73dd35e4b97fc69f9bee57286b08c829175fe0884c3ee88bcf2ad3f8d14d29eed15853bdcbfbff0a344c922aa0bdb8b31b248dd9c13beec424acf53313aa4420521346251f2f9721748732ed860ce39679c73ce7903c990a103b0b045682ef3176f119a8b033a6577445e8eed822cbabb17eb2ee4ee63084991b9bbbbbbbbbbb7cbdd05df239412860f9897179e8ef43359760f0dc3478c1417ec28fc4fce272ad89a38b4026ed1c2059714f8bb43f992ab79e803762a3873a3e4663a0561f70fe1373441c9f50373a2cb792e2c037aae04aef80842082184902b4f7551585502bd7e08a1bb8cb2f8713e579afe7e14d42a141bbf0aff4b72658cf0398c16a5fc60b9cf0c901f34fe81d31434b0ef2ede0d5436d41ec2878dca9dd8d4784735118c5d0242c8f2a1b2f16a9ee63463356f08cbef3e48950fa15ac40fb63eaa0fa54cdba8c94429d5d204918dea673c1a558d2744f48477d27ec6a379980f93966a966959a645aeb50a1951cab4cd2443a5ad965a6aa9b532323232323232d46432994c26d3b66ddbb66d54d3342d324552c5fa0113e24264634fa77fef6b5844148a59cab4cdc432325646c646ae3255c65619196b32994c3226934946c66ddb64a4940ac9b0d5004b55c3b2511915c91dd2ef34b1a5df69f2ad253e664cab4af64d54ad2a3446d6b8568d526d524a6b8d7552aa6559ac91d2f91e638d7466b1465aa2344e4a4b94c64967294e4ae79c73d239e7a4744e4ad2015653ad3d9dfe44a38241a1a9d69e66c4e0f73c958d161a065673bd93f6331e8d559d5e457590f6e4094d23231fa77fcf5379364274074436aa1ad67b2c22258268aa0d9ccba8640dd934357568460000080043150000200c0c074402a140249205d2aefb14000f758e44725e3c9586a21c866110c518638c21c010600031c01043886ac60601222888c38903ee1cfbf2aec6c22181a7c6b8895c8ec5a2bd5218a73d02b5d7039c78ed5ea5dbccd46d8c12730d1ef01dde522cb6cfb7b4d3c7668e474735d1a1245940384448f97410f9f74ec2efdeb8d9dbb88a6ef659c8aff1862c2c945fb98b9a479d7ff19d1b87f29ad7eedb88405bf59d4a0ff4e8a5c6fd9757e6983e648509f1154020f3e0a311c24dd7e899c8013830e0b34e80039a2f3b78d0635726c2183abbe21288a1e540d7f0afc8569ca6a32a48a772cd9151a1e53106b90ba989ee9ca7939958b467ea7f45ee3f78ad0511a23e424edf0e29182a70d0ae20fe306112e344286cf322b2780d0270db4857871b0f348b26cd2eb41b93488c70d6e180150ad7d5d72697516691286d83d6103135e36453cf3ac68c329aac23e90c0788ccb21ed02c8be6502b23a17f4888c6645c9b30f80c104ac4b4844de31845182e5bddbd6c891237bcccc372b137b52da161dc3559cb2092f43a4e2f0345821b0598701d81597046a8ada3ef622141dd47a875c267e5368e68350ce999ba48e16181ebd3edf72052fe0a92cac67d48ae62440e45a9ccf110bcd00a3433a8a291aaf4f9564fd437e4576dc058e3fa07ce2099b861e21cc2f09593a09a2ef15ee3cbd48d060c1c448214b11dff54ddf64d4b18e6e77aeb66b28b3625619b2bb5a002500ec795ec0c77ed19bbdfa212acbdc273315ba2b6db2e727d93d6fb5bcba081e0b72a3b8061320a526a0959d90fa461c24e0cc840233ee6a3657afb1c8b38bfed19656401ac7fbe0d27230a35a6db100f7b3153769928bc5f6dfaa304bfdb8dc4691fa82d6ec127e33650dbf245d7ab38fe9d16b024613c02b5790ff9b6ffebd234e550e18031d8793741c8102259b5606f07f817f19ebd9409ed971f0b75435b3c3d9f3dc29e0c214be44d87fb8d3819e326a765085805e2c6295ff5bc1811128e025f079345f5110d2d30d399e3222680dc31d7d428a04a324b5f6b835b6a69f9a41fa67309ac0de9474ad1b2a92facb8ca9e6cd8cb0766bf5dc91386e260b3692cf9e2d6e80d652ad4024f95965315cbe760bf2f043c30b4d8001fd19a2008815c051c3a345f62a390fce77e165bb13d00465487ae955aa427afcf87b543776ea0a52e88c5913a6090d41dcd785c2ba9abf599cd7896da001de1ec23caf9239414492c0e3e1decd80191a2e22ef3d6bb51a641a3346849a243ac79c0b1cc72ef4092e86a0e28d77ea33563e2d7a392daa3d4e14ea11e8534c8fb3ccc25ba12ae95da887c300ca0e0076d471bd78660c0f8604aef3e7cedcd735f3b285e912363714e2b4f643b215d09086e0d66f8497bb8b5b93e3a4c406a0c4cc7ba301db53bc6106686309dba555895e623d5fb68ed9f3797829061b63ac46940d31dc4e8f46d484601724b39099a1e5cdd447a7373bb27a8a6033f84f58600c3472a768683b73a09490aa14fd9e0cef50eb57f85b372adae93abab6166bf6e58f4f1b388f81d93acfd3e8cf51711b4ef32052812c9ec8774762e2d2fc107cf4ef22e9468d7123b74f27e18928512a9987dc07d4b3b0e7d98a50d73a54c8284ea5a7b775433495ca451d66a23d75a44b0a27694311cfdc869ad4f529c1f8979031797164e4924007dc3ad024a14e831c3375801ad6628cc94c54024a27a3f64510110ac4b5bbac474cf3c8962e58ec262572fe923c52206834c5c7424d1dcbfb18657bd076d8c252dbdf255f89f6ce6e48858c85c902adeeb5dd74c7d9f93f5d2671c1242aff45dae54ef3057a3de009256ff044aa52de747013f5b83863091c9033838c7428ab1f8d63733d85df56072dd769615972ca604820f0128ff30597857673e6011102b3fab6d7936f6b5d8a54d119611e98c1320ea8a46c47d641a1348957d2462e8708181df58078e8a71d623ee5ed25a7c0868f81ff042c8e4bd93d164d3ff881bec1cc08b43e695c3e548b5eadf34443cf8618a1374740e6251a9a92c3ab58740e3aa035382f3a5ac97280d0e0a23a813a59d1b1933d0f3cd36589404ae2993bb2cbbde7d1f85ae453d8e427dfcd5939f079a12a6e48f32b57b5d87865a53a9b22b483822d99ab1112c8a614af630f0b47a8f0a7d27fe4840e91517bbf50b806212665ae73d871319b4148549c551d652c45261cb9559bb91e864fa6f68e91fdc13f2a6762a640de58a2a259d49954ef2284754aff6e07aaabd1282d764a9e4971124e202389531b1a9e336b731b840e0cc60394d5dd412e5a553a67e51ebd96266b428c2432a640a2ce91456203c3648bb97f862080eff3ae409e0f187801175bdc8773b21344175a68e18cb4bac38ffe8b5584b661e7c8c3da5a39c6d2159fdd18bf7e2d42c64a6e0cf1d669ba088e12c05bea4ca6bd9a63a66c4ae08b1ea3f1ea53e9973eef0673733514863f755e5b29b97579d5d0ef6f331515a813e8ab64d0f6533c83aecd58e5c24727ad923a690e0777e5220f13f19fdb3b7499a5f3b49d0d9928d232c18172aa7695736a0a1b347a7c9063aeed18bc176c454eecc22c237b91a10d147e6def467475a5a338b1c3c515033ec355de605e6edc9063b65fc2b2dfa8383c9b482095fb7eca4af9d1c8be43ee3b1182526bd1f6e3573bff996bb7eb1fc1850cd0b9294f1a439ab4f132da6917c5cd20cffeea489c1e33bf84d6f7bb2fb979ff90152d1d5444797d3048825cba5c723ec0e6ad12038fff499da757a6786a8ee34ff454e4806b87cec47862f6b2c924c731160f3829324d59d1400a54f3d1e6b0a05900840480fbbec28c13135b054d95bf0c261bac8b7589c4b54ea0a39630950455035641bd3b521601ab23de41c7eb78fc0d808a9a47f788d90b280822291c1c2712408e09e02166086050691486b04f0811a20881d397e64729098342d9306aa000b15f6cdf34cf02225a1a10194886836e060beccb8273648ee30011cda6a89b04fcd4b11e97d25beeecebe62540d3b581895a5c30929189608c9b262bf53712821cc60f622f9d388cdd06d8556ad88ecadbeab51b404e4198ede650bdf93b5f28c7d5315fa158dc432561296fd811eac3726f4aff8ab2ad548bbfe73563cec60996aed43aa0890fe0a671383f1b20d96930e841f10514c229d8831e6930f664b56aab3af9159d0bdeaf4efefd3f639291010a7bad8c59a208cd2686336b28faaa1ccc870af6b40741ae2723b8a0a719e146964e725808f4849f71a690032c1bc17db55d804714538a52bbf2fde3f98bb9c42c34a872ba253a77b5c666d0ba12ed54b33154efbca5755ccf0acf74568576d103c437740ad60f3132a12a2d927004dd8314342ca4d79263dda5c5db416fce27f63a28db1e9fb3f6c80184d1f0a8c32acdb2937c3c286b5e096ed0ffbc0d39aa984839f4749be3fafc10e3f64ba01c046a4943711d4b18c2aecc72384435b98a9145d45fc34bbeeb059d095cc5eb9cb2d40f4212775d1aa3b802249f88609bf0cec4e4ad5dec16385bc0316fa734ddf49d5e25cd259046bc44b6fb58f97e0cf478f3afeaff58a8886beedf96300bdb78b6cf30f42b176dbcd8ea12c13c1c2dacdb96a9f728b46a78d0286edc274ba2f46200e7c917922d5cd8214b96e03c4e0035c5e009504fb96442ade137389330f703120684be7181bd9580be44c35aa2100489e85c41460307943a83f577c433748a30d103c514b07e1a53fb4dbeec0fee3988022938ba075d97a4630ba30b7b7a09160dd365cf615734fa8f188ace8876a2e4a6407eba12b46f3ef7a028a6c65950832e83b88836cfbd416f27f525f65149abee0c1449ea516ebd24ede1d18550b14078e67e2dd3e4a14d41d35b7d7e795cf77879fb65d0cee896dd00eef27b6db3d313a8f247b5576db3d26850b00bca4c3f6e290dd6eea31af169a689c71d97e7afc113b682e901768540c2bd6640509d105726636e732cce19da63bb8100077426a303330526b1199a34a006ee0fc163595e72a7866fca5ae4e9b182e48e6795a9c291c278ca526f8782f65122c74d675b0826c0a84b4c89714bed3e99800ee90de515b1b06fbd07ddcbaf80883cf26914a89dbe2dbba15ea928b6c136b8c91a75e6adad3d533528b14051176ef9d7fd10af0bcfb0c14e6aaa975138e05c8e6390309ebb101a314db74cc76445842e280acceb964b336a71ebd80ce03d9940391f66bb6a37a2328cf116e909f52b8013b5d0c9200b523de46b5cf64e66d1437cbd8dcaf5c23296318f6870b09d05e730c7b6eabb1a692c5741d456510d695c4c42fcbb7d26315ac7610b0aa21e002f002a9007e9379759e919ef47a80dc27b6bfd3bdf30f1a50f5a128e696675da034460302bf281dc2fbb488fdc6da482dfa02941ac6535561200bff540167488035a5f5d4eeebe7a5ffdda12803e709e68bab95109f8d96a237533844eb84e34974c127e1c387bddaae4177370646f29d0a25de696a8c94d7b5892fd118b34b411c57da3137db07e4136a0120159917968ed7e113115e35eb47dd2ff90a977bff5299f72354245ce5c1084a75479ce9a2b23ef8ebc283e004ce39fdf50bc284f95e60a6f23a2679d278cfcb0c7fbc006a7d7bbcea5ebf245df9b3d7cf34562d9ab7b24ca1a5606c0fcab03d3180c46016a5d1015ea1be0573d26d4cc2fe7e9e8504042d26f55d48dc463db4d186f8e8e7323d26a439a5ddae62a6a9da381af0a7c8f3061a35d53ee285d0821c4e7eee9eb81cd225412f9a202a284ef834d00bf168bb6bffbda5ef57204b64b8ee07fca4cd8c39f2958371f454c46485e945d9f4169d2b1d8e71a12e959b87d51d1c8d1c6c21a9ad41d9f91c580a9b999a1569dcd271feb2f306623d3fe42a0dc28d0e5012d73e3b1cdc93154a18f18fdf6735d0a6337fec2ec3ea40284d77ec40e64fc29e04c2b3ef7a7ad6656e90ddcc97329ba64a328e191247faa7c121522e9bb08d850c869d51a299f465b002ff42cc1580db3a7fe094e55afe8a8e2a9a1b29cde5a4b24ce914f807e6a2390f5d3c204a4d02f0282d4d282df4dc41f5efc4c6421432fd2a342ae11c433ccf772e5c8af91f0786a8ab3c93466818ed614f241616730a9a1ea58c1db353a80b7ec0f0d1c4042a43b302a882f50bc5b70249a946fdeca14cb38d6951a9f12e31801e99e2d3cfc8606474f625fdd756814a23c9ff78b474095fa1595e3a8e636683f447b1f17ebdff08602d1c465c0fb2a2beffd77de144ad4e21feb131a880dd523add2d7ded90b6340606f564bd9ec25031e6796e17f895ee2bd2fab52a3bd1accfa898a8bbe3d56188138c7cfe45750c1e6614d936bcbd89f8988e695c0c0e55a690a83569f591dacf81fa636286be2ca54b4adf10a0dad4d8e3e243b2d1d8c603fab87acb859091cab49a8248e1fdbaac0f8ac50f52c5d706f41459a1c9444528270125121d02310060cfe13c701eb44d307bc7d0bfcab99cbff24a22f17dcd10ec3e9b4731d37e36bbff900c50e06090ea05bcc3f3a0c092319dd5c3fc9b4310ca6298ad1ede20833d6391dcf29346167d4e9e7c7ab731a68a1934b2429f3a8f1db29a030c7fcf570c9042851b57f287d9e1db51cc0b54cd84534b60955066ea3c7ea616a6aa706af533fca053124dd356fb77ead4685302b36d787adc33ba62bd31ef2eb5a218be167223e3e7a3fa8b0b70fb3aac5276b833d7544c33e9d59c778962d46e83cca61773de4545b38df994bed7358c2ef866141dce97224c84a31863c07ad63307197c000cb6d82619dc31345ed4b376af5762f1817b8bb524927848d0d93252449c6a889416baa7f659dafcb162543e656b555f48b5454f30558206543aa1c9c55fbaa0f9d90d6c5dab10e9470570e356cd2e3f9ef4818b99bfe71cc9137dc5cf43a997d9081d3c8d38b6ecf4b6a5a48d10e8403654c33b67d0ee1686133b511f395212dbb873249459d73c67a646c3b7444a77134ab34111b7028717949a49a9750f24c686591ee87f7774ef0471ec43ba662d2d31fdd2a4ab154c625f43541ebc282606bd9dc2afe51f61573441bd49ddd9641da30bf40f38014f671874de837c45d62ecc39bcab05874ade4a176d16fb5ea196030d9653ccf121af3a5fe55aa5db8fb4bab6602b51c9cd8081071a78403c9c603d8eb3b95ddb603adc6e6cea20104d1d9e0b7f2d985bb25f6d4765893a8d7e45b4f6d24e8413413b9c8dbec2bce8364d6a646d56824062db0a7febf8266a7a3d84b0b147454fb6ba43a549059e0a63268012db06a126df51b28ac07b63848f3b80498b22aec2ea874cb36a63a6e1df3ed3b6650dbf6ef8589e456b9541fec6b9899c31622970302f25457159d170df57fdc0424322d3a4a2068bc246bd6957be05a96cd3943cdbb64dc165b413445630826edadc583b62690c040969e882dee792f8653b11afa0cd989aa70979b5b049be7eb3c5b53cba23bb5de4605ad4e16bc0934c66ba9025dcf310d02bd6f2a5ceee40badc2afcdef04c71700463a3b4447eed09005d3ad27ba1a70f9fb8f734a3f794ef1e826dc96185d2913f244847172b5c6928df9132c64f7bbb8fe42f021c48b04af9605a9f3eef4a210a5ce52583be62a58d4fab9f934de0144614445fb48901e6358da5257a937adac42e541ab7cb41c198606380767f10dc08fc4768f6a1b81163f1a83baafff661def6d5e85ab48d13d9d629b9781d297f69824067bf09f46bc5d542deebc89433147531eefd0d9e6eb3ca376b136f9df9dd732d98078a0fff7ac4e48e1e49bd9c2b649bd545dbbf72d03f95b33d7bf3c3d36ca1fa826e116adcb45e81ee9624513e88946853239b9c0fbd52bafbd3de130e1ac10ff527e501580cb1492c89fd660bf0d33cc9d295158fea75790dd3f89d849a402a7d238c018ac1064a7c901d4e50613f5dd1da15753d28f8147edb13bc1457c50ee296a063c064ca5541107e22b61f04b25dc418192a3e0467c9c61be9abdcb9b9ffd502f22e7bdb79fa947830ddb21d194e643737e9bb5f4226a3ac855ca17bf802912573ba51c6e2a63de74bc924965dc0b5a37bf6093f35ad94bb4e947bcfcc22424526608b047e7f112bc66b0fe64a6f6df6fe1a4f7fe0c3be1fca4de0d16665891652a40b6b92d16b5f0e88511d4b4c9cc898c45ad7e842d9dd21495fb931ff084069c05e046d91dbd6f9f59d558ca3ad9979a25b574b40d6296adcea94652d36a09b440b7f6c93ce3282baad3af3c4ce9dd03484070b4f13c46d1d56163e559a5b46417c21d6def9ba17c5cb36ac5f8855e0d8d6c3510b94865831564eb8814ba1ec46f7e3b02676dae4fd913d2c5ee10e17a1ea1f75cdacbcd7f08543e8733a077e20805127f6e266b3eaaa1ddb4d14054f4beae5445062fe69e1b260c0eb43b42b821d247d406835a5488d14a4d60659ee00dd0ba468322a13d664262792162bc5a169658a50896e1b86ea804d6daf54a7d2a14a51e7b308f4efd74bd5914891b446e3944230a821dfe55cc307366f8780c49d8e12a12633baa561a8101b10728ae10dac4d17f586a67be9b984854d83a5866aa6b8ab95b5bf9f35db8a183b1a7be53f10d0bdd9de1cbaa55c89733db6fa5aa5b007e50b1ba6812e457be23c5ead7f51cbfaf702f20bbcf6b9701740c764c5e7b9a9feb5143e2b4683fe6137243d33c2bec8a078a53949d1aac97447b5119806b40bbb37eb16ad943ef230a3022c6469501efad94264b6efdddfd1e07a6913f403479b2ceed91fae61e55fcf46e018f248af36de4cf268997b1dc61bf0815d04cd06dd6e7afe42b69843f4db546f7f021faca60461755b7acf0510a1cd920f634d10e4f763abf5ecb8fd1d41945079b0e865806e1ab0ab18b66af39499e3243b844e31e912aadb4a8a589c2d5cee76812553bd5e96392c7422db72f96092063ece5b00563fa43ac3c3a18f35a95b5dd507a35cac9d670a6947fa84c37481d301729bc74a58a97d7b208b09dabf279606c270c1a70090b195eb0692c08a7f5d53c4a2a369be7914751eeed04cab897ed9755d0547e1765d95184ac5297438820f4ce92a4719797e058de1be0b4443255796e389fe0150154920a3e3707954689343fa032072def4f36b8946b06ad1db10910fb125c192307f0d97db7de4ac5a6cf181ca5b960ec0d3553dc731273f9d5634adbe043eea986485906a196f1a35c0e2b45e209fe817d3217408e9fbd15085811f5ec2191c134b99aadaaf7a6fff388d891f12b5050b943afa2ba1616ec81c24f6610df855cb7aef574eb0276c9848ed44788e7d5ea2ee81dcf170ea64b2b79ef6ab8918a7de6ce00ddd2262ae3b4a91038dae677570f1b077029967512b656202f7c45701ec34f5c56086d9bc289a0f66375317561a4607606e2402fed36a6095747a8d2c67ad19f2d7665db90a15bcf9b4bf239691abd254665ac539f7333ce552c767001537816be7095d1b9fa5d673c1f38981f39fcb8af9b4506b9a0406bc093560fcb0f237782fc316880e8771c43d3de71fbf7490149b59ba7057480f4f88c4d6c1e33e2d060aedf9b6a766e4e452051b0bbdb55b49dee94e3149fa65599e0967f4ba4ec021e8ff2d545715d414d45297f0fdbc155054d76a968bdcc3633c9eaecc00d4fbd41f70ce7ae1bc5bea4c29a3206c07875bf26079e3bb8585da2f57698ad7a568757d6120f656be6adfbe3df54d88fa118483e308058fb8966f1c2931f6c65da2925075b8921806920d984e123618c12f0e1d6a58d3c3ffd30de863eff8cebe1880efc26c90887c14e307b602beb5fdb15db5cdf2bd823536533b488270953c0bb4aeae543a41f1a72388934485ec0ed8c9227f05160ad1509f1cee4b6000110b5b2153b08378cf2386d05c78e499a46e6e210933b340c2c50139c25b0acbd6a21d4a8a0bb308282f0747664c7e61fe49e21e8f5527bc804e1c4304f9b1c3d4a278834b88e0ffc3476d4add4f968ca1dddbc99ad90acbeadc38c90f21d94aa276a7058136b0c544a2196f7ac201141ff6250b15665d677d0891aeedbc0b0b95c1a95aefcc2bbfe70b776f5851f08eccc155b9766e2f5b55c5240e733f5fafc2346757055a589523b0084e0da0ef31a8bb0b26854dd5140c48e405954f4861a9d03eb69a81206c509e9aabb9efab418535a54dfa497c640b4ddce5e60184af7f8c7cc517c71bbf4585157faf81336aaee6bacd927e6d9566749ab95555e566150b8604ff54a4fe855062a06c5ac1e66d764db4578330ad3220e1961896adcb97232dd5902f8a49f55b07131884981f19a0b60964cf58cf9700bb962590c6d28632b0b6ec1e94a9eef7918663948d360c8f6b3fdb4265db8741b62c43fa3a6d965d9e191a543f399f2e2131a55ab5241089d9d9d08f391641db21ae2510cdd51308b3e9328362e988e442c9dda6c9b5c727ceb15b5e089617d5e607f11f5df56d82d53733472c1903b309451d56bdd8c878d1ae2035dc4503a5e505e594094cc2a8441fa7a0e6d247ec1974bb027d6000f8214c5c735df400f8695930a014e34364aa15a644f26822fc49f7284ab991f8f6c10fad2f03edf32f34f44f1a80aec3431834f97de1664f7002dcfe2a6ba8f340402977970a5c95ff6247c8536aea08604fc44e4083ca02a37a93caa12092c0f58120a681e5a110d381c581129581e540131781f550129340fa90c0c4e406913bf318e97b02458a7ca1469c3ffd73cfb4f8da78f34a36a1ba2f42e485588794906354081b1c445883db2b516bc47de25f9e31dd991d2bc33ba96cfd170bfc577fa71b942ef713ef33835b7efe28284e783e11e0c7032a794db322cc1fd54cf247ca3facd8f5fa491f3558bf83e8a80472968ed0559e073874b0d767990cc3dba1818d445fdf77e90c3f2fc02448f818dd7c6c854262640ec4189007ac9be21baf0e561fed65e031350e8ae739d0dc741e5630efaa199df9c77c4dd7c5c2a2500bcb995f90aa71d68f9a470eeee04caf49d009bb512dc9b178219b72b5ab44ef5af8df73a52dcfd2ad0d92fef3a11d7054aaeccd5d739f682a045aa284abe391bf715ce7f374d47241b243a4ed80fb7940bb22a1c9b6302cc599464aba0c52af09c7fb5b46ee43f3b52e730b16848082c1133b5cd85bdcb8812623fdd76ecc716c429cf4d71b0b9b468b59921a5a8050bf985163a4fa5ca8c64372912df4aac627fb43b6350703682d661d887acd98f9f90c06bf775ef3d4eed2030fb0f075d3a77b41560f14c86c829c9d39541c79299de3b6d304c8ce62f68dd0b3362bad5bbafcd60dfd5a75df177ea0dcf3f05c170712e5925db701e0b799a6e7edcf97e089f45e2ff30823c7b01fd6a5253d18ea02b0fec91a8e99d6d2d9ca8c003e9356da2672f629209655a189e7d66f83969b85cece700cd94cda337f56045beab29fcd35a7316cabf4c59339eb6543dbfb2d8c990103e1a4d4ac66e8dc7df3224425da0d1d61c2a143580e1db324264271b8615ff1729f1e9b72bea3f4b30c8c6e1dd482e2fbbe1a54cf3fbbd39ae64d821ba35c083445685b8adfdbbc20a16de3b58299bef521932ea988b02da1cc4ed60f789eac7a0576ee500129ffaf88d09de5a64b7f39721948f0a4e0f5323b653962a700180da0f61a410acc833000c901d627af760282000292fdcfca87c362ef907e3f46f1ee4c9423c3ffafad77eeb4ca2313d9c6b6be44c83beac19ae82e6c9a42f03b5f0ec80b21f9081c48ecc1cd87120301b0a442f2227f40fffafaf75b8afc94e020a4ffd66dd95eb635bb3528964176cecc0dee300262ae3df7ac164355bbdf12b6e49bb8cd83b85f2ac5925a71cb8015bbb2eba39d6feb4ab3efdea4f079d23f3fe6b7813d18a0232a04af436b3053004308c43135ae521494cab61f6071552e9767b93490ec84a854ae36ece74ef6d00e17400fa2be229a971c12d75725b20f4e74c179e263f9cca8fe19280e2ba096ec71c4aa812a11a8febbe1bbc4239dad578f1800d2c041b58277b95944912b347cf40724503d1fb4952c132528892283786aae865be1c4e0ddab26230711b7fa90eb42a0c1b26b9629b93c856e1cbfea9f8f2cd3ee23a8fdad53a6d8d3689668d8ef5375434531eeb244186fc26c1eb00bf9427ea3e200b08bffa1a34095069946be50226e6e3940e368746d49fc93263fff91fd9ae32f00068215f6237f7b5aace2c03a1bf58e012e1146dba93dcec64595d7a15ce281609de11f34f4e51e00d9d016ce7b2a417c6754ba3188e07b51af5fe38b6d460afdc3975c897494073a00f2e6ad6c02f8b507bc5ef3f2d3e8e89cb2fbceb350eb3a7f9b4369323a1ce41c124fa9c070023dad662e54afdbbde2aeb8e66ac79aea9e4efb5e5b076dec7aaaa4be064330453fc285e24d39dfc48d1edd4d301e32c8a316187d762a1b60f2cc7285ed35907d35c6579c744f0e4e9149706d45e13c96746c404372cb6e2edbad39f144496ff5818da6ae6c8d2b09b8b49f1291eff72cd7c84d0a38073dd3b905e1f4df1952750234488a02c28ccb487236312fbff8132761e565b393bbd2d5fd8bb6c3f2de960a5b9930f97141981cfb80411fad3434244a82ee731038a2174b124f9f971adb82838dba949c913deaf854d7afdee138572a448b2546bb7f3ef81ae017111dc63d025b9a7bbdf2c0010bcf7c0f4faa98dc7ec3669aef60b03e53a28de2534587e36e3c432b8e2c7a1cc92cf014a1af794d25b998500a16ec283188a3f2f21ee74e3b7b02627abcee1686afb3953b4689c804e322984c15a14ca09915066db4f4a5d926144505846b3de06fa144dcda2607847ccf34f91ca472a9440dfca9d304bfc32137e0dea16d820593a8d414fafa696a32949517915a046f65b295b7b1bd90458e5fea0781d02cbbc83af7bcf5d73230ba44215156a751d665f66a060e80ea6b8bcc74d0aeff7b40147d7c58f9dff33da88778011609e9dff30635e1beb0b0e960aa82d6c3c3add728eff69d134911133bc8e8f074f3616edf2c257d4374f48d4fdba845afe4ef74f028b4447e2b85719f95a4c2bc7b8945f36a98642c62e1a746ecbb60c67749262f537a76898288883d770f8961eb48b061870e60d1db396ec86f7d055d72be492260d541074eb27e842c87e339eb84ec5ae715266845fd1967242ff04122b8b31bc12f3b4bb9c8158c63e1fb677a7326646a396b7b3f0225c810c7386ee902a52f9ad6b2b664636eb63a4bd29a189480b0626cb0d0c8165d78002c2c0a889a11f8b762f8c3660180b0c71a623053405ecc6c47f9c158b054e0d454e44e6c71a3616f19be785970a5c078d196103140170d22dcdb7cae7e9ff081c3aeedcf9540d31c8cf21002048ec64beb8ff86457bbe3227c980a587bb36ddc7bce824238e2268c4d2755d24cf64f77640ae7fe99cb1a412cef54f5e56f00177ac97d0fc57760c00a045ba7bf06e975117f436ce1e455d021ff88898700a6af71e48670f5c70140df7a4bc1a3423c810137d4807305ea49be8956b4adb20d49ec6d86edfb908477c1743ab1d23b51d19efb7fd141b9b588cfcd643a7e1042633f1783dc306b48c1e61179087f479930313e92e8fbcaf2d827ec5d2277afe8ec6b10aa32aac318d2949f95807b36b2fdd5fc9b502076874dfe6db4e3a28b098e64b92351617296ad36c4b3a0f250e13a5c2afdcd1b6d1da631dbc95800c990b39de62dc497e3163ebc57ce06f4c031991d65d03b5fc709666b7e17f1c0b6ad2c9cc0ec61119510d3f6d4a85ff0a5cafafca108fa99794b49188e5b4a69cfd8ce3ad80bdcabbaeffc51a42f28373da41d5cd9327bd84558cc0b51fb8e23ee3218c82e52dac80e83f182b09c95ef0bf0a97b62144899b19a13a5b82799f96480588790c80ca39948529f9d8f3cb602983dec327ed7d2f4d9de24354cb8830b34a1900c8fdd4c829723b0035ab4d94289f81050cb16689305a4d97354f9ecaf0a0e276271e121b8fa43950187a05a7a45ae4b4b8f9f42aad8776304b20a11e2f13584f56df7e33b943596f5f7932d3f660df60ebe4f8e67f1b1f7d63ce4311e0079baf5c0b45ef265bd33ad0107e344f1010e5014bba7d8cb2c46cd484ccab3f45d2d1e57de91c36b25f39cebc7fdc82d9f640e9089d7cc03bba642f9fa635e01fd046d6907fe80bd92f1bfcaacd1082be325c8b9236cd9ac2d2ff9d6cc44fe2a599850f5ed9d67803eb072e6c73b74835080ee8bd982950ff74beec8abab7264271f329c4a31992839b946264e0b459d71d0740cbc87aafc4b05e78816494aeb98eef8e73e38731a8253ffd1cecef700e53f601625821fc56cbb8005c1b9b7b1cf0005e853f256c83231baaf8a74167af5c2a05fe1ee00b6215ff04a65f16990806a60a300e839c50914bbbc62f2d91d0abb6434b911ad10fa6f55f0064b4fbff5f3deba4049252bc11bacd4789259ee31d793037941562851560588713dc932d1fa4ed78e5edd8e16eb45d5f7fb7e9d6282beec6fa7d4ef9a04f86182a39f4f5fd51f2156523b2c8e089d0611a8166d3df646c850b4dc311f41d1dcb9ee1bf18430b152df2a5c4bb7aea1be94120857c1a9e2bdcfad584cb3937c110eb856497103e5ab038944200c30b6891ecc684658e15b2dd4e449e0cfb2b26c513c1610e8bded7e31abd0fec194f5eddc0e06fcdaccb31ccced08577e25d26797b65711a589fdbd37f9b1f5caef71122a65c07bd78f0805c0615d14ca10a285be1f0ff4ed10b49e532763753afb709092218cd0df0a289e72df82ce77ad771097422cb89fa0c0bbb6c098546884850169313fa741c8cb1207b313ccb65a6540c5bfc9afd3c113ede53fdc4977cf920f1813d030e39ea523727bb1610cb46e056d4ed359feb8fe99a441a55b8a91255e943981f433a059af8ff105cdac143beddd60ca60619b1c553d3eb4ffc63e06e783e69fdaff2abb47b805207a8cba1193986b42679274917b7f49b26f3119807771adafd64946a4dca45744ef1a0d682260103d57ecc59a306f266ae4360a235225cd42bf393cc1b0a63702dde2c6abedd5e505291728648242a614222b6c9da941b906cce99a2291491f052aeffb50ac5912a20e2656349f2a4822d7437390552110219d7b58209167ae1d6803e9996e8a82cf4b238ff80d000d81966892854ee0ac62e07df3b89f6e3e29f8ee919f4651210de8c65012dbf31621ea49767ee4a541456e91f930449aa77625cd8b696dc3d3ea923875f51eb6312f75dd99ba58ce42242cd3d0e2133044f00522acc920e96a660079a51363e6bbbfe559b11df2c2aa97d5d44102162f802d30fcb6330e8e56fabddc616fc63fadc3bf05110997a0a62e54936a217bc9891ca800047ef49e830021608ece819cd1154f9ea37d63041dc83de2f53214291eb9c459a5777e2467f46f1a221732d1275a1330f4cd38d6b258af4c55c9400b47b9fe3de1b466c19b6618f8f50e82177c847748dc600317d20b4d51abbdf2065be0e4f1f45330161256ee4a3a739da560ab95878ff75e3288f320b32bd8cd26035aaf826a1f93b5a2688b4f39556a29e06422642a88148ad3beff85434aa4c615b056cded2e92bac84e95419238692ffacd94e5c30d64df0d712251c2833ebcc06a4c406d2ad51dc30ae984a05c0a9db0a58408c799b61cacb0c4daffc8fb5cfdf95cc3a2a599e866ac370c34333da97d0f2415817818834659cc78a8c39398554679556b7014067e4b57edf0bdf8c81a94a5c2867e7fb058cbed460ef3d2fb868a4c1a8387fff81e7474d5ce86539b908b4c07e0ecda41a0fe6e59572253cb961539d6ce0bd81fac9d46a93c80aeb7edb30678783d4985bca4caa055f5dbf9a4fbe611391247f14a2285659e22cffd367c0a93cccb00d404c21cd28baa4ca96912ada7cdf5083a3db2ee3395e8a49945248ba13827bfb23beb64f1d3821ba4e3d7ebbce2c74ddd8fe513fbb48efdaa871b21e6b96d947eda6260e5409b4f324277da6a54b626e316d3caa0ccfad271503a6eb8512f712c3ea0e943abbac30c46b6ab1ae588c715ecea5805ee03167693fde5acc0ef22321627d282cd1ceb57790c68fccb57d0a8dbea006f2ba472a0ab5d9ebe3eff21195d6ab7444e623c6a96cd9da5db976db569b21e7516edb2a5d2a0ad54e8755ba105ab75b492acf7680f69211c4c3fdc2b5ab691b77c08fb867996f3f29db23c21474788f20d33f40a35d0fcc693b955a2aabeccf0853f63c680864faa5d513c3e17c3e7a9d2ca9068c11590af3694d2c2afa2a1b2bd8da9e7258ef787bba084eee4f2b1e506356a6cf0776a11519c1a9fbd214d981dff8bfa3951bb9d63f14b972992c4a4c9baa33f43b2a3092cdc35cb65dfd697b5f605f1958c1fa001204bd41216ea28b72f8589d54ce6154e70a6481b0a93076aae29741b0b7d8396a3d82a4451ef34e2f35f49ad9caf5f546d2cf9cc5a8d0af5594ba8dd405edef4456abb7f534367153e8827a4094bd5530043bd5eccae51be53565f9d0d3ab19afa6dc46e32179fb5b89c1b7bb7c0b9abf559dc16468cf4cd066c69e72190d6570b368cc65d515f79a7d26af4af3c736de24344efc0ed650d0b32287af3a65a1b456525802c1cf9dbf27f7202c4fe03848d087a2f15e176fda6c02c2d4b1bde2c4ecd9829f31364db839e812ee35581fab446ee07883916806994045eda24771c5ebfa43568512fcef0e39b3cd57d535b0b161181702b1703a14ccdcd36d1caa5ebcb6f15f05a74c694ee58788a2ea2c2aafebe6b1e5e64cb8e5587493b27eae3c645ef5a220f837e5fdd19698a596f4f22eaecb9b44eab2993ff5417742df522907c6a29d5e7dcc58930760ba6620a0f2b10bbaf8cb882757d5f39e4b6d4208997ddcf9f2dd0fafb02ed325579f025d662d62d5582639edd45777c0c5b2e5633a899740b50e56296972f6653e20d7365c89490c6be223691de575cdd506cc8973500fa67a89a43b91760370583a9eae5c560ddac8122bf4214bd6ca2d33525aee4dae8a27f5ae5624d30979611e80732b8c46245a8cad8427c1b365952de9324074901f27c597e2c2406042b317c1a1bc26007116402710572e103321888dd2a2a664479a8168690f50cf8494aa23a7fcac39b4ec4affb48f0cd3103a7e3d2f995b75e5a3a11b240e466eeb90b8ba8a800de2a279d0d59b8785cbf5639e7bc300fb8ac37bb7632472e3d457eb1d135840fae6a60800fbcf67c393578f6a8b10b3c4af2a959ebb449eaccb5aeb5370da777293c19f3fedf9c4cc5570e29e7048d49b01e862dffae96c9d994ced52bb5381581dbe3e7f3ac35435e1b2c49a8cae17975dc69969d601765d0dc0823af0d51b5db0acea54866a4392dbfdfbf80f7628e8659a4a63f6f5c47a23b8f276cf17f5c4e9b81626aaf37e306aca94de7abc4e8a3da85848e5dbfb6194367048642aeb43baa2836ed947d205a091117b81f3e476398deab16833deb4b6f3df84417fd3119a08dcb88470b0ddc8286f7ce17f5b0051fe2fb306e315e48c5af6f74474fa68e2e7668a79a9578ec4c67a611c8615488370b3d23c2702458dd4ff005a65b4fc37c4277c95ec0c878305cf9b2c4b0dd13cd493f3aeaffb70aba44e6054047f15165fd67176fc14409f4fae8b8ec3e4dd1ff224e9e1821b463a50c519e3c07588c224fbf705eabd63ca509a1c80b7144579a2fce4117bb7f95b444bb667ca5c593e7081e3c437045644de8ab6cb3144df8cf3f005d082786e5773712b8b15f8f8eaeb060033474c0a585cee921481d21ec63cd047bf9cc05714d54eb4f73c2726a7157b385f95122f1ebdbde3d668acfd8b5762d4e1afa738a85e61e4eeff9cccb44b7d0b2f09c033c5bb0ffabf4a0e806c830aa0bd8be17b9cc053dd86ca7975842e985f3b6c41282bcbc84c38ee6102882c0752980f16b93a5cde54822a8bdb1beb10a96beef437542918a0e9088662eebae50e4845882ce19e4e00cb89c5a8861eb7a957ed45a0b140565ab91cdf04f453459f75bb7fdd71b26374f2252e34f5dcb501002a7da1376afc1ec8c838a4b741fc212bb4085f4037050c8d0402ddd7b8c46cac2e56f53414a9bf5a59232793bcee8107c462c6ded846e3eaabddc1adce65959852c559e3d21c4419712ba55a02c369caafc53168b3cd0b331cdf3652b14304e14607b5ec4c3f9bbf7f98732a47509fd4dbd1d67df4f3fbdc4ece0206f0234e31eefaf4dc8e60ad398d0c1c8468562469775341dc4038e5dd895eb2cc8e2a0d6837234ae498207306e07d2e135a627c16622116ce2986654fc9a52ff05de427eb63a9c8d1f2b271102d028f552eb2374f9f1aecb6c86615f493c986dbead489f6056c48539a13c2b142465387f78b3aaa26891948f22001a831fe15b2833ae7283faac1393d417235ff2e95e862dc8d84207fb3e8a7945275ec5849f10c3fea139dd1690e1bb5cabf133f5166be34536c7d64937334f29980aaea7d007c3db95ac3ab41da854a276f63d6eea50d7872c99c5e43ab659764f45692e7624a704dc4a6d8b086eb9490cc5d45d90bc73f22e9b2116d9b1066cf2b9f7ec0da7b49a10865bf4130e88afa0ef2a2055c41301350ff6a7490c6a868d8840b38a62f5a062ca6b0491c35e4799c4e39822adcdb07678b1c34e4d8a941e1d3d59a270644e461a5b53dc9dcc1870b57b30ed92b74428b6bb1425f2be37e45835adc507cc13d5e1ab1006a282b9d5220d22b2d5481aa7a65cf30a69b924004782aea5ef4c535ff5e2a938b0d7e6ae605e887e64a3c5b1f45660c0bc7123a4dc61cb349f1403d935e2f988ecfb55b4914aa6b72415a1a0bd2bfbf8c70943209ac741b0160e47f2ba90219363cbe597b5dc8646b54edd8175ed3972c454205827f7bed59e9bc30c2dddbb5f317222764658baf92845afc94f37035be396f666ae296a258f52c373f19272a4a5153341078c27deb402a4aee08075e350914c9224c0fa083d1167267a6731d7ef14bd9a3df0c6e87fdc89218cbe74aa1fd370787ddfb9cece07107b87ee4eae49ffca5919535c5571b9e973f48b70a915e395cbdcf7392a173e2474ea28a33e5b97d16946c972a939e8aaea6e96761170fb19b3d0de877db194fc4354200b1d92eae5758e29b78d89f9e5e7a7c9d8afe54007345a1bd24af4496765e0d0b37d91a2888b702363d161099b32d9e7068aa8c749ff603b05e91ad3237440ce46b3243034a9cb7b3f77e9438495757ca3647710bdd36bfcad11473f3e7f58be0719b1b9053f7582954d9a8a4f8611b19e92feaccb702a53598b8f31bb2cf7963a1cc4df8375f5b80e8cdd7afa8d44ed27fd61438b2516dc48eb9c65635f5246f59e88b1569caee67cee362ba39273276d85da021e8a0dd99fbb03eabe4865f13cfd880a79d5dacf0d21cbe0d0ada22fb9bc4518139cc6520ad2f2491232e1a37db80b1f10ffc7b22d7b0dd4ec8909ff47ad884ee9d0fbb45021b3612b85473032cadad230b0284ed19d3db0be753fdd71b56011c87eff547d63244d7cc36231f4e48c3b76dbec9e092ede4f5817175b1d76f259f70947daf66a77ddf444c3ccc8152d2c295b098809ceff6b700d8cd85e0c139bbf68f02163642ddb08ff4c2a57bab819fd15792562767d66350584c0ab47c53004f54f91e3cb0e7b40936c2fbb211f729aebf9caeb04ab37aaf0e6fbf019514877e94261b929c341345669587fac30bb4255aad9dfe0f01773545a15392c536c5370d08ff729e6d3693b028ac0e343dfdb95c61099e0db90e3eac07e089ebbb795ddb3768797ddf768020c74fdf524effcd13ddcb53cd3d4f04701a35fa40f92975a4604147504af4232760b34298881e7958f09dad8bf13911fb77a541d325e006e67b261c96f3a47996388b9705e5eafcbb3ce34745863d49d0cb2c061ba9bd0b0403fef3c5317ec6750df25757bd1c3c763c0630303af4b98d80e6ca92c841737478ad832b050279b9c3fe977e2717c2b9803efa862f8604cfc3f48cc76f5e4303577ef04b25ec041285300baa7829ea3fb4c9c0a627cad08d2585b82ebc982cb0dccac25554c40f87a964f4fcf4bd465156ddcb51fc6a0b8b00050fd85bd4ed2b58870172f034a2a26905a8e0d88c30cde653a992768de414c05e1fbe232689717d888c8b7b4d04a7a63864a934fdf0ecfd606c2d2d50ae67d7be8ad484c810523c810db76db506fb27b71a16e44746ff775d87aeef926d38fa37e00a428ec81078bb83804c4f0678048eae5224b55e0068fb3ef85772b3d7aad0fb73fd69c685f8904fa2fee016e5e7c87c1b6d4bef89308532d18999415064565e9e8fc2d8be2af5a029e62526520192b036577fabc18f07ce82e6d29bb7c802dba5d2d9f4d32cc6f3566135aea079d24072c42fecbe2eb4eeb4ea4a5001670a628147f6f872195c55237fb679ce76edd2797de5dd0bda5942a7829209ce2f9c6de69bb5144bb93349de5ab2cdcb8592a2d5463f059d01f00278169ca7d48698cedb91b250796b87f5a5e652bbf1d896b710de9b50a6066c28ec0800f1d63d83d11e5f036c702e78220263bb800229061e76eb253a4946162a27860aacffa6bf92792be532f6a9d68822c4e038465a5a1e8d9a858e6db88d40cdbedfbdaed40e877f74674dfaf6c89a7afd474a8a606a91fd39277bd8c6ef583baf041dd5f1aa8c97c25ce339190c41915a009c9a16ba783d62f971737e48ef7d6b111c82bb92fbc00581a31141ddced818202fd12e3a930516545f4aa2abf753dca314b1d02527daf0d1acf568ed3f1248dda1b5a8c62fb24e79e8b82c8fb24c6b4890982ed3af8589495867f2bf1a706a1f13c8552b1a750b83929c36af67f84f0d1c0affd7c0e595968d6df8d2320922bda8c1a9857543df386d6606b044915c2cd9fbdd17a57819c87da9c9ce278602be7afa0d56ad32456d6d6c2263d621c9c139ea8dd606bb7a5a3168b09a8e97d54af5a10d31b4835c95fb0b020987c0dfda7d2a6512c757f8ac8a0d5e43021fa6182534b2ae14e4f5209bea2a11ddbcdab04ffd1be5956980e7e4a560545684790df4e1f9074de605a23f7667d6fbf1d50bc5adb0877a4e7b9c0d0029af5dde03a28ae0c82f6fa8a9385e9ae3827d67c099ca3746f7411b03411caf222fb4b803fd25d3c87db39865309dd35bdebbc00a1f05fd71a5d18d142e738c0c5578743c0c32d02d6bcc1a7c754f602964e45bf4a59848173e4b947d8a59042744ad12d777c2b5cd6a0ad5eedb65da09042ce16b458075b6193c8fe365a3f5c4af76a58fca075b7fcb30d87c90f185e4d20f96579631fb1b2d38ba634f7cacdf3b3eaf67f701aa47b3582cb2f80c1ce7e00179113db311efecc575c8c12378bc13776d0f736512c39aa9783d373b3de83144372f79a482b1f04e1e63d67e0f38d3faff7e0ba047b498b8683a8dda91ac450bdf2fd5d5a0fe374f6e90ebd02ecae1743135d0ba353ba6f2488fdec95f7cc8c37e9643acc1dae07b4b17db027ad725dc9edb1615df4f563a8e5db6aae5e0ece61913d3685ea1684c8ce971598f185408aa13f9d1dfe3be931dc79be17127d40867268ad607aed970b4bbfa39371dd034293528e6593b7e8872e21d43cee3f19d13d1bd8b38046f19e1ed9e6fe261e60af251321d2d98e2a268509ecfeac835dab8ca83c5d989a68a4f1117f25298dbae649a18f368fb70d5258c38059429180320e369b6cec9941c2ce9ff40b9a28946ab8dbd4fc1805681afd157947451a9a8937fddbac8653be707db4bf19fa8c144c9a15138f9bcf6f1fc8ca66fd09a74c8bb513a40316bb072f148963701ab169d6c9957ec85692c4aea1000dd31e70133540c6f1efd082b6ea251e48c3d2399c9261b10e262f348d6564e755c0aeaa53e9074ac2ffa13bac0649028b03f3f39a8625a343dc4040f1959cb9016eb6d33bc40b7727cbf8694ce0f7adf170e6ef5967c9ce41ec75479ba187931d3831800310a3022d64bb7584e57b7b117830fe6ca03610aa5a98fadc59cf700433b8c3887ac4c6274f52cdda6da1382897cd6351c0b8d8a260f25ed5fb1bff3e75cb42c4485c2f5e379b8da6d2d6e834e4e9c10dfca06983dfddbfbfcc8ecd477951579815cdca9b03d1b148c2bdea816ca405862fc76d9cf1b47e3f54f7f12ed6623633148ad33b238d65fd5d1ffe48b519d2de0ad16f18feff58ef3644c78c9f025472fec6fb578447769187e22520f1fed809b1b54697982ad3d91773c85d7e95992b64d5acbe27ba12c234c162ea391804c6ffe14c2de510fae65bc0128eb0b690f240af786ca2726b8628a7c4a3cc18504bcf95a0b61a5df8b4dd52465309366e9b7ff8a54c5a7870e522eb9ea3a98db69563222068def994e9d0161178003f718c022b94532e86dda6a2189d798554d35ca1fb8cb591c56829e6a181f22806471b74152e30c70ee9eb9f64a4d6f6b2aac3fd25412d2927c0e05a10d65ad917d7e621cd99bae3a75b9716b92e17c446963dfc10e433a2cda6b727b64acac2663137b00d59d8a86526120117bd7de91231e53d9dab956367dce1d890a69f3a58f133a8161e2cc761c7e49cd3b0ce6f70ad33346529102be4c5a75b0f53dbe66ae287508c1ff5fd3bb8348fa6b2cd93ec013b36452138d466467b6212345b3aefe3dd634e3dcf8b88cbdc2d88cb58c3a9937bf985f32290496915d5547e510e65b5a37e3d9f7f15ea602dfa3889544b8bf376ae61c2f07053484eed422cd3ae884e5ffc47e905eb78b997b2ab955ea33fa93ce0afb5373bc821ce19e25ea99ff1daab0e11df3f477c37f252d22d9c5815c402943be387000e4c409b5427c8789131c87cc0ec902f415726618f781c4b238ccff26feeb40c38b8cac2e253cd83596b5e22ad63a05e861ff7580dea74849926eef5747ee1da8ad41e709380069f53d3100381bfc213343e43db760167e44989f468771a44a08e3aa3e8ecf2f033d4b043e3a536babd86d5980a4f70de6ff13fee1d5c41db21fc053f3d76a42bc429111fdb82d7bc43c7c75a70af9a44f4be62849898936a37618a441da7803ddc4338544f4d56117a60a3905d909c45cb7246373e4e0a11dd3a65c867701a0203e919af9664a9e749ad70f57cca2419ea4a807a3e33083836cc935d7f288b09a53ed9705429589d1dfb9fc80fdcf0a5619d7bdf961703d8cec7ec0533cc2cf22c3a30386b9e322ce2ba7a1a8bd5f1f49dab4c86cd44433ffcc26b0d00ce7f46f5d1e5e4260d6e06c3438d7153b8876e102ddfd2aae99b8eb67813b0df27271489e2ad4addb0a9cfbca5ffd572204ab3baa20c1747e042c53d03a383c070ba958f1e4fd2cdcec75341710efc41dbc3ffa14caac836b03fb04c66d606afa64279cea5d583cf22921e51f8356d11dfd27668fc6673beaa1cb24b73ea0dc27de3c1112deb5f9489c98207674cb0cd88b47d99ae28d28d15ebd6da43dbf84ce1ca1145dfca82b1f347174c345a310573f075820cd80a8a4a0eb4e7fbacabd12a16dcdfad4e2cfce6a4338159bd24eef96a0bf31cb2d7eaf5ba63c063ef1793839569d522c6049cfb756bc6cdf82778f84748b46aeb5f5174b8459d01ff091d4592d2579bb3d52eb3aa236419bddc1ad19916b96ae415ed48e0631b885128d73ad7e2dc0fba369373da84525af6c0a324cee285452a6f77b77e8aa777466c485aaad60e675d81768f5bcca93bd53f9da789248b9dc1a091440c06654cc3b6e9363da6130d10c4bd18f2b540bb962dc39610344535bd6427a5303118029a096577fa60ec57f26b395247f0bbadcb1f07453cf4f13e2280547e9cfd3bb9908d5991f87222ea42dd822da4b95e26f5b84821309460a6662f82851167fb3d920ee01d546d83cf3b7491091fb18cb948fea8a279c6c6ba55c107bdd41c69483d466a7e0d72d4ea88095d6e2ccf46a18f73a160d7b88ca4446afd5e88aec5b7edc5400ead898d1c024d4525ddc13324cc4ad40024f42d832fa377e4a58c0db256b5591c43bc625cc8485a5f77fe862a091c790408941f60fd7509c705bb289042fc8380d8145eb6a185e33f74509142e5dd6f01db4163dbef823dcb2b6e080a51a0dc36538ff17877ad2bbb77bf1f81c303e97c15a014c9da576eb918ac09dae3cc34725e881e0f14c95d0a4c610ad5ba03e59da3eee07fe6f1039a81de879c979454acb1e427cad1a012497b9562167c76986c8c84b14089aa66f192613d2a3ade0596c52230034236fa054fe7c2704dc45b5cf041c2d1359124f8b2c1617b46a526f37b6148c4e4b102f0dd25525b6324032e963b3a73d00be07b1ff8250c21fbf25a8efaaa161399d437b27f2ce12c67988f42d51e9486637d5a2d7a1aa714fcea1cfb32c210e83c3a9a9235d95264a58a64be9219777905b199dff4864a5d2b48392384eace41b29230d99bc571b3b62c29af02d1c16679e364f0c8d21e62e53a554f4188824578f87c20c92a63e9c6a77199fcc3b2a6dffc1a17c5653827c7e86be99eb96cbf32cc7a89b49efe29e26b58b53e0c12cff6220ece447cbaac920eefe93b3204f519ac636664084605f6a6cb1c3a0ea1bf3b8dc78b6a02ce4bfe7e0ca95f96a104d199304f66a8299ab24b905bc4d580a256ef191eba25ec5ccc4bcc9279712dbbb3f76d2178c9d231d069136ea641296afd069ecee03683221bdd85bbf3199794fb5f6be54cb9834d40e58c4355a936e08c87295e8db09b413d6944f1724ffc2183116c06ecc6c2a10da996247d9a78cc8266320e1fe4d8e13774615be2a3e6993cd44d9a0e99236d4bc2918f53509f88ec550ecd78a534c97aa069d3fad9e37986f4451f697e0bb0a8dc00c05387311cc6221adcb698b8ad1285a96f7e46e7377ee1e3f4d43fd0664eb77925f2d12b10ff42748ac53bda7da26d56d52d800ed4d4f449644da917d6a7b5ea87d9b033682c040f930773718570a98d4fe6c1693c31ca6cc58d2176913525a96bfcc0ba8be2393f1ddf22367723eb39b6f5bb2df9bd41170cff99c493138ce9a6d8f982c559e6a47f7de8eeb3d0f9655616c35c3d5371962a090df57cd0fa4aa761d6903cb91419c4f42512d670213dab563c451fe32835741f8ac5f71a0624ac62748d5ed41bb548e77a18f0710d695bc47450a1b874c46679bd075dbe5c57b13f30ebe2d0a68a3576b6e51c1a3926eb6057704af20ac0a6a1be33baa6806756e6ff88b853c58f405c404f0bf36bb05ad9a2847f29b97dd3267a80e9ae813aec42c9ac1fff6d468470295b506ba5cc5ae7bd70a52b73104bd782e14a430e15480e3220cc496305cef79c980ecb25cd2a27e4b24088ac63dc0f5eea02c7abdafff3cca10204f209b6577c2b7f643a3e90be5c891a37f116285d5728002ea8029689d87318be5b68402ec80845b5eef691379b6a9b24a10c6c978967e054adb6f4b265fa93f3e9b95f4399f83f4fe5954cfc460d0f63fdb35969b9599dbb2becec7448710682a7fd7f1688fde05057f9f474edd1e6a45aec16ed1943d3b45a2ec03504af1cf12ec4264400198ae7a256d3dac098d5b44cd38f0add65cac3336fac4d1b9e381badecc19dec652f177fa300d4e3fe1454528f1d78330d4d5f5503e80b0776063e1f50729461b83e63904620824f7bd6b5fb88bcc91a1d2fcdc3366852c864665897faf86aaa2fc31a99ecf7b7345d33f3891f560e19ef426dff389d1f97772ab409809c1c83a8c909ccb4d1cf546e70447a01a4ee37fa6189b94989164472462bf5c7c8885c52dd4384e5028d9a82cc901c89aa770095e15b4ec3527c533911d4fb145134bd962ea0318afbb5b026cf0f8739b1427691da997acc8fc15c834bd27a9ecf65b1b68b502ac1c2231f17b015790225936d90814f32a6dd7688876e992ebf656274752faf44832fcb5dc1cf4c05b3f41440f7acb68305063b32edb28c736726045cbb6325bd2e9c25076b1765a9f49c8912a75ca97f7d8285ef2c6df4d0542044968ac4c2da0d9552192af91d83b127e8060a6c987493b1f4680fe9341c4037b6b32ea33b636ecd8dd3503f071dfb516ed2055b5e592955c0a4841bb647dc61c1676be08c8f75900dbe1b53c993f3063b9d7919f21a56e7d2b977b0384e99b6cd946e03c2f08d5632b74dc6ae62566e2673b4bca0245d51f5e601682896ca954c834239e54443cae9971c493a94c4196d2346ee3a0bd040f668e5e49e943e924854c7b9d1dbd2208ca252badc4c394c84c276b28c3970cd99ddc9f0084502b15e1bbaff56707659c10db613f0391b65e33ba13c26c85ae3ae8f03e11b8185f126ae053e723671c5ee4ee8aa0f90a1cae00f01fd15839b6f95ce2e5cf9b627fe42041fbb976c7f04ab5f82e4eb0c378f559c5d57ff4e80e206b503fd88e04ac22824c686ec0dba2e933b191fa14020de6b43f7df2a8e2e2b7183b2d5b2b1fd6f668aed87695869471b57fa8334569ac38d15f6b8b92bc94a265460dbb573f415e4ed032af3ce965a20181ea5033d9acde470a13059648294f547a6e8ae580f6d8cd2cb48845e92b3385094645a8948ccb06fe0d102a9acb732d90397473a1930848510a016c9a10d324fdff46c658d5c140c3a8823961049c8de7b6f29a5943225198c0921090109398eee676844e5287fb088e786979be40f3e069a64f99f975b42dc5f90b867738c26e08006dd35a5efcc175c2e43232a3ef9b3ad0cb118c20e41364600d26c88b9fd3785036e0b1d496cb9011015a024888087ecdf120192cdcd52128951823bda1f7ea96fe6bba18347f6a2ec53b6647fd510d9ffa111126040a310c820fbdb2411348567b20028c7d782b2bd4289a81631370ec6bd8885c9f1498d1ecc1b5e5cf85144815b9072e14b16d09d712fda50c1c540930c9f0bd2d18c35313beec5ef4ecb86dc8bdd123fd94facc986202b7ef69363673b5298bab812673e992a891412f79b637a72fc6cc75d33ee15616222869423769423260624aa2184dbff65431f06e6b16cc8e62b7d3425528ad7681fb78f3fb94c2ff3cbdd7c8a4bf127cee3acf85bd762382bbed6f5f01b2d5ed362a2f09a0e93c56bfa8bb36ada8bb3aa784d73f1a258838314c3a5189761bcc6f4f1e517afb91f7ff29c3ebe048a35a88f3f7ddc35c56b643ea63ebe1c8a35323ebe0c8a35333efe277d34e953147f4637c486c88a9f8a2fd3f516671175385d0f4556ec5a8ab3e29bba9ee2acf8a5aeab382b3ed7f515ff0ed62f572a927b114cfee618950ed7c6145745e57eac9963222bbe9d63628e19dc99a111152436a0ad89f58d8a0d221b88384eacb9f488fbc1239c58f3f4c66957465819e1faf76feeb27805656b520c93c04c57b5ca3db7780342a55a41a90a324283e2064c88f112822d5510010210c42001842e437c49e28819f48483810a2a86a04188a1392acd913f9bcf6b1dd1041921c4e0a8aa7e2608323008725299608fe8a28702210a45d92106587811e28a992300b18322f430040852d0022e062ad80ef8db2e43a323a4c8598646476451691e5bca2863f4e812045a92a4947206910a7f5785a143d8337c9ca6551df67cc88517797eca06105cffdee9b62de892b0ddfa3560f86b58355e35c5abfeece16be21c4d47c3abc638db6b0f5f4d362deb1ad369f25acd5aed63af43356fe8d176aa4adde31747b9be5663c86754ab4701e6a80f13005fa8df823b60ae5f1a0377402295177707508b0140e8825c1fc26e46d54213d7561eeeaabd4a00fdf939f4275ec99cf92bb97d0ef7db4a3e075f8e734c2bf91cce7dd3c357939706bb5664d5377536f5b7fadd1157e216a8a83f74b11a4377cbb03322bdd31ec3ab20d9633f43f618869de35bd259c39ffc1b3a63a69c754922d69932ec9a49a403d00e8fed9a4aa403d24e969d272ffbb76b4c443b20ed70d991f2b2af6a57f3ec0065fb332d03a6d132f8d7af7087fc5aeb9b8ea8d0e894d27697a14f8f98a3605903c8f03b58f33fe3bfe441fb68eeee54cf5cb3e6ee32b9bbfb4bd261e4ef9497c87e4967e09463db9dbf556757458f51da1c713ca9683e991d1b0d15cd386584de320c9741be54a188a932028451060863945ef731aa50c4fc2fc618e34c01843046995467d22a65a553c21db336cc41730812b3a451f6c3ee49e5a4b7093a618e1cc4f309598a2811ed51ce496985453ac85e531b0673543b6924a40f5b06fb10e6982f3dc7873433fa9c9f69dd32ccc722d113a932b435443a07dc616b0da14b89b40c18cce125fb57eec754818898e36b1adca22ac6f7a21767384137aa3d518360c4511423ac28aa1fb928795168d053654c142b7ec8aa146d518682c0a81c6951847aec0882b2284a00518192b0285084a023b0519c00319500a344d2852246082022283e0842044750b47802e403258b1e2a3d50acf0814ad0b49fd20124d2b4b82730b25ced0b0f2ed5010f182e581260b6f48029b2110212be89975f441732b48c8c50c109cca645fe5234c8df4c1140c89326cff9f3439e3f5ff2d32844d90631c6bf5be4f8aa229a2022042f776411128eae0e7a4b49d1c116430c653722224a56b940026721ca163990b202c7a2411aad0613f7f6091e37189fbe72568d715e37ded2deee886b4311ebff41a4031019e25e6c98d449163e843ea49cee6a896589002855401c307f160150a4802c8965eb47d020cdf7af17dca0cb5fcafb448334df492857ae0613b70613b766c8c4c532acc1c4b53598b826ee29e763f32029b70bdc016f90ebfb4fad379ed0bc1b36aa5ccffe867b9f75369edcd975762120abd660e2cad460e2cea8c1c43da1061357cb7006ec6fb8c7cde05c11b7a5b8eb739e9e5cbf725fdf9fb8abf4f59da7cb8ebbb4d5fcd33779ad26fef92814ea7564fdd216d44b4897be7c9311b5bf7edfafdf49b1267eadef4240e91a872c1fb8c3f43b76c000430b2d609c420a1f2a6779fe97e5cf7d7ec46cc231b70fc4511fc8bdfaa8ce7d22ab7e2928d7dfbafad77f72fdcf725bfb11f3094a5cec391f91551f5371bf1e62ed70b5779fa8d5774e48f3b8571ffbc6f04a95e119f7eaab9ec3381a7ac7bdfaaba771af7e2de1cf72f8b31bfeae863f55e68361ac7fbf06249155ff947d915873fafacee5a0fe621d2cd8ebc847610ce7dc3f619df81717892cfbf56ffc9095ccac6466ac7329cdfd88d962b7f56fbcdb72ebdb5abf0530d76bfc8a8d9c84b81eb0f6686c3bac125239a78c31c6585dca6e6cad4a25a58430bb3e6859967387c54ef0e3d3e0f6cbf8b4d66ae5bf94766298ccb2a8699b675c3b845e4b5e196794f1ffb3ef3fcb4ada13dad70ebd87ccab200d719452cee979dac83499e24cbbebb256b7eebe23209105bdc7d75ff78dcf3dff7e3a5f03744e8a69a463430d808cbd256f689de9373f6a73f6f0fa59c62aad5362d99cda5c21db00c0ec708e5df7c11f31c35f05f10c71bf20432a72e715b8d81eb22b8dcdb5d8142d2d84508b0bc6282595534a08b718218c31c6f835e0a6c211b60610422db048859fdb1f1bc20dd6e0a87adf8d96ad58490c972293d49152c7c405915d141515cda028c6a2188b6691945372994151174545453328e284f01d13964548e85ad44a08514a4e43c9501d1cc6344f89eb9d9d7ed23dddf3b37156dae74a0705c9a4e61224476c9031a3a3a431620874399eb0f500926fe0566e880c3901aafac6b76c8888303fead237aef2c1ddb83bc3a9542d2b8fc16d0737ddb15a7a826b6aa2b5e56a55a8954b43e0734d191a4109c2055078786209da977bcad0488820685f5c9ba191103ca852ae96a191103890428823218a84000a632f512ba2705313db1f5200c48c218e543f280145c6077a4ef8018a10299f31337ee0e189cc932019b675041f94bd76bb4324a55a2dfb05caa965d3d26113658bb360a651b8380b6699eb830d82b84f76503e5cf1c444c4d0c987277c4a3bd707267ee0a004997ca0f2648302a6e4c310406849dc1844643f106d627ec08010d27a0002101688a3ac871a48a93e45986dc9a0c7de4b840f6581102d1b9b0f10d12582b0f70ad185f610e5a765632325888e3962ce19534a8ae7040271c8b78dd93a63c428a5ffd1f8f2e50bb49ed24a1b23ad31c6183b463b2d9c73ce15a4b8fd3cbdcd1a4d5ad1a88cb26a34c82cb14c398b56b111c620cbef39e77c207007f6934529a52ad036ee69b80ececfba15a0275bc8f05703b6b142960ea00fa05b6818f6daa02945cca41ac4f323871044ad8031440403088c20bf56b8903284232b4551c0e86245e827a98b16410063899a0cb100c38a1783c411e120bb05ea2027b2fbf021b44739a90eb8c3b940b805422d1066456eb5d28b2cefb20cc9f5a2b5f74ee8cd23d09b17898d1bf4a4c541fe50f1cae2558eb59f7536b2e47f164b7cadf10c0d2fdf91a63f3343e9fc1c3be331ccb733331ad6d6fa4724b0acdb3c069c4c9f7b72bfcdb2b2b7c26cd9b225ed8eb4727cac5ffd722e6eee5a05e95ee560186565af762bd8c8b6d65ab3ac7a0c14431f510e4183345f3ff62fd9f50bf5db6b7000b76ed106b59d5e436ae9389d4ea8d7704e9d357c81566e6c04f5a687dc0fd49b5e43c5c6a8d4cb4ee6b51beda7e7fddf6badf636eeb53e7067e0816f4a3f45c3b0875c8e0c5ee5c88fac89539fc23a2955644d14842a20292cdadb129058b3392b6eaf75b6bbfbf15375953cb59f280db59d50a7d30bd55ddacaa7bf9887bb4e7f429d7e8542694c489a737e0f41232b405328cf28f29c4579a2b4930dee9aaa233309cce974fa19643ec9ae7f3c86f93819f5d9f79f7e7e675bd73feecdc8f93875571fd51d69cd1f77ad82b4767672f632bb6a27671c0a7f4d32eabbfb5adc93fbf5cf76faf9da8995bd53b7c28d9f3a1b79eb5276e644a383f1f0012c2e7831e93c4005062ff9cef938c5a0e1cfa23aec1bbe28f7a35ea095336ca4fea9bfa27e43fa3ca3741a558031c66842c6be7bfb1b4761f802adfce1e40d1bd11ef5e164143ed2caf6d47d2d6bef55a9fef479f934df0484eb8ffa29f37de8a6875e7217f6cdf94061adfeed3e9bb73795ba4f95b7e7363c5f3bd2ca98fdd47bb126856d64c9e0cb5ae2b62ad6c497ef4d4a2e830570879225496af6ef0c1d7f3a8a800172c45df6e3db40c409a4ec39be72e03cdc2575bcdb4e89ec3c5f7674351fbef0e47e57466bef6555b91d45d0c4509ec9d0a889295933c1ef32703f9ba5c7c0115be218e7c3243537fe66f7dcced0a8892fb91fde15dccf1a35d1f9ae84402313e2254d78c90e8d9ad8e25646256849aa96a436f1244ae0422326a6dcc61205962bd93f2f89524a2e48bf941c0dfdd363b7ac75ee4773eed1bc85f661e9748aa7533cbde9b1d39b3ecbbe3386598b751f663130404d8231d976966cadb5dd25dbf692ad7d196be4cb47b7f6dbe9b1eea4c2432e890f1affa3f3cacb529e96fa1e776118fbecbd87e37cf84e7b0942d294c680ac07b286e1a0f15a76e365bc4c966553fb58f815344dab618090b28d5dc55d3a3a5b1eaafd82680452a6645b6d0a5d671d5d4f71cf6671af0a18482ec57fbc8a07f9152fd9be0b65b1ef43b10662f6c875af05e56216abdcb35feeb764e699b75f07cdbc5dbd7d1a2a7c36ff5fabfbcfc3ffbd8ae6bf3b24b2eca3f033dc0174fccc4f71d78c0b2d3ce492f8a0f13fb69faf09e0b5efeebb21e6d6f76f39feca55b224e6171e86d75ebe2000fcc120ff05254b6236b2fdfc2435b7f097036f2e6025416cc63e88cdf26547e3bb7bf72c06683c86695ce860c6817d707fe3bfc4b54eb2e5c9dc6b4fe3b3c734ec6f3cea531f00ac3be16f74aaf71df7a1a56e74331eeb240eb2ef685d67e771572a1580163ae7f11df72cf63caf45d14343eed97cc56671cf3e8438dcb3d0767dc53d0b71d8875c907bf6b14e3ef62cbcf7abff3ac86b165e76ab962c484faef0903b00868db0f036afc0c2416ca6c142e661cfc32ce02435d3f815fec64fd8afda8bbb3edfc1ba947b5850ac815e08d96fa12cf63b8a18dece6cbfb7d855b6d0880444d9facb7e4bccbeb52bbc7de7715767fbfec45d76876cbfb91966dedcb3bf02be21661a4362cd7516f636f06769f07701803f15f6187f8f7d873feff1d7c2be06ee92edd3e854e8ac7bd8cf749b7bd84bcec7ead4a5d0c11c43173b25d23e0af655dd0af52bec67bccd33fe84197815c466ee5742a411d4db1c1ff532f00af5728bbcfad13985bf19604e5233f637c48ce12f3eeaa5972cb9c8327885c2496ae65e76c9528b1c391f9874af84a17bf071dc355fa5ba1746fbac2a37fef733c4bf21660ecb94bbb0b7afda1e72da2ff11c64fb811320c8d8bb7342b61f386144c6320c3b0e6fc1031aa499f10fb353b79a39328fc23aa997f9d9e5c83f611d192f3f85a4dec1d19386afcc69184426cd7e48ac815f7c8ee0e02e996915a14ce996e98a529f4c1fcb66cbe6a70f99d239e774cda7df0388a6699fe12ceb9bd8dc8fd2cfc8d5105f06ce300cc364f02ae7f4a8476d1c0dda6718723f4aa94ec69f3a999fd1a59eb3363ac67d66448528dbc7b82f616f039f0c4b18c39ae37ec44da63b7daa936132994cda09cb07b5641cf7280d2fd23dd363bf3d8ebbba4fd2a03d864ddc8f12e42aae21be8c97d1a564b8b71d8d7b942b59eb91a3a1542a955ac3b2191d7c9d34f9a2ef99328ecb389389e3b8bf711af7b6bb44fe4d6d290ee2efc896b16fec4b9f659ebbd4256192b3cff2ed9274cebe39cef4d5d441d9a15e86e3380e857ad489e338bce21ee6205d26d3a15e76a72f52c46bfd70b3ff601722ee515ac30cf9278f81be44e1558e8ccc118ecc30769a1c0da60deb8a10e98630718d5460fe673871118fcee7de54eab8d738ae542a954a1808d7ddb847bf64fa2ceb4a2f653af83275df129fd1e5202387d4c7d7b8ce732f637fea749abc4e9f65f82bbdc984bf259eb12f7de50e90612b3dff423df796a3817bd4e9b3bf8959f619b672fa538785e3b0e71efd1b2f7543dc853d7d22eeca20577ac871251db27d5fe242dc664ab85f2bd3d7e12e687fa3dab66134eed12c3bf87a5a837c190fb99cd4c75f31e1ba98fcd75df5293744fe09af7264a05ec60cbcca49a5bec9abc38265d90fd192873c0d2f5eb63da7fdf6da10770d71d72a486b997eaa63825dc1050f5d5e5834ec6272f6a86e95831ae231d0477d9397c9877ef6a76e95a32333c45d329dce56050b1636783179a331b4bca0c7f0f9979c7d34325d972009431cf1f3ba89a80ebeec6bbda12cb6a70602c0914a755f9b72ce988424202123255e903fc8c5084a04adf20793a01833ca88002a479005bb5824c882190154c04016cc9f0a08c41104b2fcb5c8fe01eecd57e169ccd73480c45d5cd350fa7eba0407b8831352fabe617bed6fa20add4c6785e6571d740f854e8954752774df36a3fb545946a7a4a6ba259d65700ff7e6e34cd4295e7c136b68f0941d949f557dd6da4c916a380b4910ca34fa8624d6c878babdf630662fa36b185fa6fba1b6270a2d999ee409f5a697df48b4568c6489d65dbdf8477ffd1a1b09bd91455fe5c58791691232fd8e90beb5f766cd4dddfd5396244d6e51d2a639b25b6aa628d2cf6056bffe9c3750f79eee9ff00d9d51f8ebafd8c895528699b3fa29c6d3431df8bb53507868481b82435483b17d7c6ca0f1084d5e25e9dc7f83ccf5939872c55fc43dfa1c0df52fbea173e3259d2b9e807bd4e7a90dee51bac3fdbc53d751d07f95eac621f7e8b36c70efd79f2fbf46e2c9949e30ad987e876931ee925fc140775d2c1b4955e5fa944ce97f3ab01f7f92297d0d066db78b3ab45a21c783e77e12c90931f2d7434344994a16640a8d9c1043db6eb165a6f629fdd83526d2534687b4eb37851cc77d6322eed1b75b3d951e1a9d004cfe7a88529844e9d752327d8a24b2a8d528353d4d29696a37aaa98a609d84d48538a71103edfb0c33ec81c01d9eb53fa27d1058a37a085559785a644dd31e5a4d4cd6c4c8da8dc3ee4ed025c330191a0985c95a732d3f995b561d33b64f38edcb6c31d4ea296524d4257f3659fbc9c35d9af640b44d4b6933db75427b07e29ef6d0ddb601dc45b17c20eeb256d31ed268d848efd02aa8c45ed37c6bee00b4bf1e23db3132f69506fab633653a83294b9cc494fbfb89e81fe1bc2a77ffe813cb8738ba376be48698ad68807e0a5946865c4eefecc864c8e540a2172a47911d43c7d03358d033fd5dc447605176fc9d85f360815888c98655843d652bffc62376413ee51bf70821e72fbbe8317601022ab4392bfea41de3d3b88b00f323ac3523eac26e2da5ec606b5a51c16aafb47fe37425a446f66f6cc461fe1caf827cb6ffd9e2cfa6e5366fffb34f71abeb74f8db3762b10d6eddb8d1d261e32e8aa34dcbbd9863a6d6d2da7257679aca6ee15108608cdbb641f11ae33bf6a19d619c1525ec31714c4fd7afee6d46305f8f99ac7e9b1b8be12e982b52140262857b2040b8879048bb34352dd5a7cab06aa705c4c311d05191de891049a5ba77bedb80deb40f5979be3320f7bbc43410c79cef4031d2e8fd8e437ff0e5b50e4877e3c4fdecd66ab9c38d072dc397ec9d95c7d0595f367094a3d0845107c2d82ee83901195df1e16c0f279f287c8c96c8020805464b68e17c087ee7fea80a93446db55f6bad56aeab60e5ce19e28c0edc6ffa14f5c7eaeeee5ea103cd1fb8a33f6260c2cc2e5e0471c4b873f4c2bc82f542c742a13ba1809d9e382b4e1fbf02b2628f7b518e7197755177d5d9e32e2ef24886915fa4971cffcee0eec82eaf550d580e2bec79705f1ec51a2f82acf8d6caa31ca3b8227b8c562e37a304b7c88b44e0400ee4400ee44091d5e39e946c2726cd1b71d8714a29a510d2a949f1e9ee0ea7ff207d716f060fb7815a06a4f8c56ba214b82552e91aa741864a01b700831f459f0894fdc85df18abbe01781f214f7da7b9c75c583dc2743ffc910c877b4745296dc499b11341892b8f061f576f88682608be0c22c8cf093b1976f273d8a44d1bdfc45a28f18aeff2bb83ca8dcf692edd78e2ff36ca4c8f2ef8b046323493c5f6b307047e7249d9421c4f1b517d9bfbfd080bd440203774021d84bac93dd0d9d6d131acb0517d3200d0ab96fff4c865b9453fefcae9b7b040002b3c4aa9edbcfffaecbd9e5208b2574c9420962d89eec592c8127fbe7402fac8c436b8bebd342c01d504a4ff3e4ce6f64fac9f9324ffc31691e1c6bef55a9e2518e221b500519197e4b070e28030ecce1352dcc6ef83b6293210ce242a8f1f8c0fdba07764fb79af807969d81c0dc2477cf344013bce27c405f490c5b94f2c854c76c8f13ceef9e070f17be4b4fb6842e6969152443fc31c91d810bb340c24ede91611647d072a3ff5b792b888548f79c0794ebabfe6ed5ef65fa72d558889729f6b2ec96482532fb432b3df695edd063bcb2c737a21fb98c2df75bd5c7425ebed779acfe8d0b53a6e5989ec33a58b4d7e1de84350de3f4906c9f9df6b1567bf950c31f14a33d9452d3b477cda45deda4697fba54669c2538e8b84b7bf913f09afbf273bce654837af9a8cec29b5873f1859035a591ec29d59e7ead75dbb69f01e60dc70f49fefca97dec6fbc6b92b32e07a18c59fa3751526cc5f4d7a3941213615ea3180915e5ec3f834947287ca70fcbfd0d6b6c220bfbd6deb577ef6e758b7bfa5decf4d9d8c01d9eb127e22e98b1b7c15df1b11f00f658c4b088610f21866112cbba1bee6198863df7b0fecfc6266296c3d8853172337cb7f6dd5996411a6c06416b0f5ccfd0c804423410ba4378adad74ca083dec21570386e11cb0dfb0edbd6fc8d5e0afad8132ee6bc0b0ed31bc8a58c4197284fdf65f13190159fd22282ac10972ffd078e2fe4fac59c5fef69bc8eaff82a87f2a6e2a1dfffcf060bfd13441dbba9563ddcadd71cef626ac63faed7532cd94659dbfb4de4cee3960c9293df6397038fbecb72e8752f62b8973dc0a1c380982091f42e0e5b5cad91efb0daf245692035f483c25246561841d2821042a5ed863f0cb8bc33a364456ff865959c3a3177c61ffc1312f0d2aa05fd8ab5a155930671d7cf1d02f2c38d84324ebd7055aa7c5fd92183e6374d611f7bb2af8407878f817962873c45f13dcc3e14f77c37e221b433708a1ec502b753aed440ba03cf162d256600045d0cbf45ca7d3420f2f2633827899deca9ddcfceb836b8aff9e0fcdfe3c4ea882fdf62b9bb7b791a82747557c75f7d67518f7fcb1ae93bc22adfda60530976268d3473a79899b70cfbfee8d9b32dc3e3eeda3fdca7ee78fa9eb9ecdc411734ae21b3992ca115f2095bbb111fc580fa40cbd7c6cc45c21cdb5a239a7c322222f33c9c8043a9890be706254822f2a4c87d562363a2c80799efdaf2a15fc6e26c1f5fee23a1c7fde8d268246416332342a410f2bff53060271f4ecb93d3f7a8c31c618638c31c224249d43c8852d3647ee8adf5bdc253ffeca0497477b2af6636388b4b8d7337aaeefecb40c91488bd72c71567f7fdd81ae81a159fd598642a59002c62db4d082a16df2d7caadced15fefb40f34c18d9dd778ce6ad9d01cf980522d56fbb3e994bfc291a4b26b0d2d2831e39e50af79d997d0dc1fa57bb7fb54e242bc5a59711d7ff7c6d075c105175c70c105175c70c105177680e1445df0770e9732c7af128725eee938ab65480273f83f710c7c46622dfc205e85c23d21811880403a8aed1db2c32564d6e2ff167a6e8c3516b352eb3a638e12533b1ddb4208450c71b30c8d8880b052887a8876b8900116dda738caeef0ba47055164418b80298a301e3343a329ac5822295811a63062882b2343a32978b41e5c99a1d1143b0890628c4c86462c6041964510437a509112114abba3bbeb71971389114adaf285a8884b17243061c6788f509628babbbbdb470b8d488c50d2962f44455cba2081093366c85d1fed11ca12c50cc2c1832875c3de99f75a3a6c6eb048a465121111f970219fe6c675bb59a47eb8c3610371b9021494a587888ab674c9b09ff669293da57df2431c0dd4551aa8813a49cb110e0e8e9d3407e7286748ce5115347087dbf4b474f0f81bd5ae84941e664c6a1c9ef45e9c61a27b1fc36ed70348dff8945c23c16b99a60db9a9192bffef8d1eefe9b0b90162009c213745e3debdf7d27834ff566871e7f3a428149fc42a7127fec4a078250e6598189e94e34edc893c7127eec49db8138f2c1531c6687160641d41204c8916dce1407e6e7a18c0dd55feeeb522cb06e2c8c139c201c2a0f0b137445ad64282834d4f4b070f57fdbdb10698c00d909c21456c38820407bbf570cf6d0f203d886e7cb02737ee82f006ead868a04da9debbd1b29130051a8dbbdc2249699f23cd53c4061cfa49ffb8be0ec2c921028b6e580b9172ea86bd33efb574d8dc1499409122dbddac15da022471302dc3340dbbe961007777c7699e1cf7e051ce909c239c2584e00e1a70024d2eeefaa60fa669f38a960904873acf707dd225c308411c7002e58d06da94eabd1b2d1b2c5691ee4d202069254caaa23526678f7d5c69af69453a7064d30308ce10225a86794fd36a0cc3b0f8d0dd55f68d609ffd87fd96e154cb5bd84fc05d9147ce0e924d0f2038438814b1018b61628c313a67a3b5b2e9702fdbc7f72d4a1d31dae888b86575d0822cd97efd5a315b31ac3a123e60305b81b4bad65a57cd43892ce923864cc00624eeea19d0a80557b27fefb8ab27d674a5328c534a69e62d9aa76fa4befdaf06a91a942705e85005e2e88757dc83996a3f678b6a3cfc4bc3586a7d5b63adf8dd53c11db0d62139ee398e7b9ec53d98290be298c91d51d09286d84dd84563e4ce1536709891b8f127eef82b21463e23af9278c6563146bb8cf1657c8ae194585ed15c9a561a9a4a051df26136d1038d134c821ee20d90a229d9dddddd83706b86464543e40f12dd54372a92344e246664c515333234b2624ac632342a7292a12062915ae207405488c00318d0a03502275fc4a841115584c183e843f68759a693317ac42842d2109e24c105da261f30c2d110493c71842614e1070df80289158401c1159f2f70c08424ae502103268c008a11ed9202072de8c207598ca9220a0a7e2840051228b8628c183b2f8063f020063180220b9e1d2bb0caead67da60663c638e119421364a0c3eb536d2c38028c116334c18a15bcfce1154fb2d3ecb688eceeb6d229a3ca480aa1a61fe04288cc03114a0c51832e8e0045092329aee4cfd0688b30b600c2cce265ff6c89c82e032a54d005142f08038957932b4de820cc0e55bcc862042f7f95cd90f35627194521d4d8cae6e0dcf01a1e47ea11263b10429e0e1b286090fd6f80b86b4676a328bc64ff21b1c65ffe5662aa9b6a657f2cf7c9887cec3f29318823fbaa59f93008863df7dcd2ee2f4ba043b0e2043d2f880427c62892e145182e5ef199c8d0c2ca0e2f39f3c58b492a88978ec458fa23c6e207d060acd1915266b0a5e8a5c304ce000b1308bde4eb3061e10736f879c5d761a24aeac9e2259f8956448517af5883e8a5c344c6102f89b1f83391217a458ca51f3e136f1cad87fa4ea494928993262fa5940f696aadf5850f6281236badb5d65a6bad9562085fa8554a29a594924a29a59452d25a6badb5d6596badb5d62a6badb5d65a9f498db5d65a6bad9276d7651761acd188d0fa5d7e779d020a3e5cf8f761730a80afe9a73fe1a4f00ea9d769f24abd46b91f497a8c4c3f498f41b95f957eb57d7c137ef756252cc4cba52f61faf439bcda80b847538d934aa5b013993f6518a69d95fb3217f7907a274c9c34f9d3e97452cd4f412c4ea9542a954aa552a9542a8545e22db93f8555545cff88757a38e1e58449ea743a9d4ea7d349063be1c015521401050fbe4042e825f3a877c2c44906c0b8e04a184854418223bc4ecf24859ddc542a954aa552cf83cd2b859d9cdefd653a94ffa9eb2ef92d8106f10d31001a8c3552b6ffd0a4bbfaa5bb7c157110ffc68066c497c4ec7815a41ddf10b311f84dba5b3768703febd2bcc94e8edf648abc9aafe426c7863856130b218091f8107690c6d3c8fe340a204bd95fc92d65ff528dfc35c93ebb1e237bf731d121fb4e6ef8f1261a81ab88851020fb2b814419e2551002647f028c91bda575b2a23fa9bb326471b9fdf63f1bb31a35b2c5496a64fb5a7673924eca36492365db3274cbe03a681f4767694ad3060fd0619fc90ce3381aa6ed0a90212764fee7798602648b93142076e6519630e0f9069a6d8542b4e88c9427cd774c685e4ba2e9b0686fd25e6a1accfd7133bd508ecc0226cb2019341fc9fc25538c3c352459f6524af944ca1a658f7c9204d658697aad93575c0ac9229b4dd806d7b9b837df8f7e86c01db0a6214bfb8941314e3c1a1277997efe12994f827f3e127f0b937075c813ffd73de429a73665fec64d5f391f49847cd6381fd8772731fca3bf6edc7d9d3513a6714f7b2d6e5bfb164b10e2d0beb56e0c9225eecd67e1e0664ffff3a33886d330b4a7eddb61c7923d5df778f294f12183c74431dcf533fde8684e3f32e1d27750a94d12ab818452ce47f75a9b249605c9a1a8ba32c8069954585cecfb769bbe6dee9737ce838a69622f391fdde3b40cd9cf5700dc217ffe02a0abd2fc67b38c9b65a82a64f5cbf8da4588d5c8fd364ec8fd349de7cf98fcb4cf4c32d502c00600ba935967247b259db16f5bdb643299f00da67f23a64ffd8d678fe127dde3554e903d3d793e003a27726fbe8dceb5386bbe56ed140ce8e7274806b90b7f0dcdcf9744b2481eb92b8bd422b7cc28f2c46da70c823bae4821d3df20737f12536e3cf332dff886cea64f05cd9ff13aee92cd83240988bb4cf8cbdef4f37b689811e81ebec243871b6be2188ca5509e4191359f05c55dbd0a4fe3e3fbcbee71978fbb68fcfcfed1992d25979ee787e979e870b111d32fe92cf346e4949f5813c7641b6c99e72f19c3c3bd99e1ceb3b8379fa6f3a1c89aaafffca8793a8ea13f6b8691b837fbff53f8d523598283bbfee7eb409b51b8283c8428749dbc3291cc2415f7580605a2c0a8142d3765412b15a2111100000002e314002020100a0744229148341ee89132fb14000b839840725a9bcac32447511c841031c81040082186100344466a884800031aacf0d5d739f9fcbd14924a56f631046ea8a5e3fa9c22763b5c90a02edd4e786d3efdf95fc828c0af6a32ff02223cbe7ebd216eb8f23083ad3cb6bc4956b1326309f510fc9b5adc2e40e6426f7185fa9c1c7679add2fda25affb855f283e1ba02d0d4f95b44a06ce4188c6887b1c37956bdab508d5bcf75b7d152370839da51e3ac95db48566dbc3918d735032b92053175b47f52004659d8c23c2df1859f3dd15876c9a87a51789d866c0c60068a208aa4815054f182afd58b35de20399d5f9443b5a152b00f896f46e73833169b6e12c1ac48fc84b79a16a485c9d2a6155a1d120baf45ac96087f2b33735f178dc59ad86df98fb871c537a2ba6e36e33b6c7224a7b416a2a01b1125f00e8909a3849c9865c10c5941454f05c640a52680ad7f39af52191e940fb38b8d812bf0088579360082b07cdeec3377aac05808fc6e4c39f55d4bcf9343497ae636043d4490a4b32309098296ba4a748cb6d72e3c450976fda83e1cec50377735856ab64a0dc6a7be1ef3e5cc2062961ff6f0b06bbcfa32bc45c0281de0066b29ca1d7a17be7a178b1602a77db4a48bb75ad79aa495c8f0beab2d381d9293c889512da56f85d2c205731a0d68937f9384f2e0feceef5261d379096603375f9f4b515b84e0ed82e84bd9421c441bc5e1441cfc5c33141f958b714e1631b14199c6387bd96bd006800fcaf2f3e184f00e3dec38b9d9c2552f7f740b75093690bff5bdae5a2818789f5c80425c58b6e5034bf08306671df7c1f8c7581ee90696ffbb6655308f619a1e679299fc33afea08628d4e82052e256662829ed6febf2b6f6c52f4b8526d14d88cadcca8b62cb977ca75a478e0158f294769262995f6fee3179da1009b34eb51df62468efc9713da65bee239fd12cb105e1cfd4a7dbb8d98177dad4378be147b04f42c6f7434831e573c33afe331f7ff7a3056fc98467e184ebc254166925bf175c9a8b55b72b82e2d4ffd07695f9786cdb0b77de2a61c3434770b7ee1f1455688ce3c784b12355b04191d5de47fa13e0c00c2c4733040f1a6ee8b1e96bebba3bcc1bc48bbbf26cca946e9744e0575e0c50ffac3e049d4928cb7a37aa018b16e1d8f67698101694a85dd91b816466c658af64ff8dab9325fa12fb510c8c25655376eb968f3e9b31f961954e80b04121ea6873f914925bc1b33c243664e50184611f90e3dc217615f07dde084b1c04ed72ba845efeb631e3a34588e1eca217f18bcb2b43aa654ef6e8387debfed9006096e3d7976c3b1c00c419c2706c19c37e4958254c6686005e2dc9d0e1629839f8e863f876e09be2c38712740c32638b6a669e83d1f0457aa00d535bb754bee89dadd07a782c7199b528060bce485fb66fde52b4044913dc85473eda78814f7f19db6fe204043734d7969cec61220df17fba9c3994e968031443f478018c7a9f630e05a47f47ad10aec5ba278eb45c1df56c48461556fcadc9f0e80ae19d0b7cbfbfd286e6266af80a6718b68dfc83ad78c3254a74bc26cd7f8ec900e06514fd7a4c4b1d3ab17e3e356c8b44b3d8783ad27d4dbd518136794354faf79e006a66301e192d2a09279300ad3fcb540b15b345562a55bd1a0c5eb62f4864eca05799df1e5cbde52416c40a0156cff90be3ebaf4d3279e4a6f033aad4bd583316e27ab4686d336f2a58822e600d53817e61d2fb8aed9c11576f5b79c88bd71fddc688da7862cbf6886d6c7db8ffb2bb48c8da6e0d8005ba0430a6b21853a9b0bd9fd41137ae6bef2d93297b8a309e573c4733d2d467bd1a0cf04199fb855c60628a90c1e0e0aaa8cce001abeea86a2797055772bc8c597e6a9df4e125aad253e07ac8f814795de6772674ccf1b3d234ba9f1865fec358cc0a5b362d47652ee674ed5a01193425618d348479231a20b69a212f3731e7ada34595ad4e2f61414bf34d3ef23cc7d27f64f017d312c73532418ee7fdca39d6e3bec73337d127c760f141755e2a93d8b9d6393e9b066b53d8d77e6cb483a1cb72e7934dfc3144954140e9a31a1c8cadc40af4508dab3888da66ffbb6ab3781a9880618600bd0c727cf0c95ec5af34e8e2b6ba0a4878589738176cb33caefcc29a0a59e7354023584a594311b30e8d41f42f6a6a7c30d3c400f3226107fcdcb53603e97c538017518e54805fcedcf8fa051a189929c6631c79049f25824c7b0a2fa1ed6d61ccdf8b81539ce48f21e6138eabbb4172dabab51f6df013c5a168db0687f0fca33918463966871469727ae837286dcfcc87c04c67225d6959601b9e3bf01bcb53b71f0aa9f90fd24423ddd123c11c9be557ac4a90b7f1de2486441184b1de9857e04cc7db562f1cf53792402026c9b1986d15bda1a217b5a31bb92889517abd2c4fccd0fade9bf7b8dc0b813f94750fb25cf0c1d9a83067a68fdfc53cfd8f3d8bb8269647adfa822bc1ff1232e8c5efd4572f16b53466d7a0a09aed125f085b18ac29e463da9f3df77f807dd6c5fb207b6a549244576b2a2b209e6c717f6f4c345fac3c7fc82383ec826d73835275b403a21e28edc93ede92afe5e8f202b16539075c9c420806a3aeeac7250174cae26aa05cab640b24b6db10ef7287fed50d2478095929697eba1a9a116109d4bec9cd0c9d4c2db3c38f17c13033564d11e13111862eff1d035268d582de491f68625f8a17c760a3e96340d25442b3c182d7367f335c1c162ba67b550c2b45e7f3e14f2c8b5cbeb50a1d3f11923afe0c0fac7b596c39a52f64671eaa53ad17aa3addf58805f4a91c2227adc0b23375c117d7c340141a468ee049200326d2fadc8bf0cfc873b748a29d9d23763c5bab22aca3d53245f45fd350087d63cef5efe8512e7152ffc5c6771b7092e9a165b4ce47270e69ed4fa5426b6a2d16d4e029eb338667dbac52c344295f66f955c2051698234f3302e5d1545d515254b2b9529402876a6c09afd703089acb3d4bcb091d9ae3d8231826fb277b88d9a78b1667e3257e17691e0ca67e4b71a29c813f750479b083f7730eb54908b19b156e69c01444864105e186458363edf187a6caeea4d10c456fe3c6318869b47381681df190fba287aa0110c997a49908c71610a011120563e2e6ff8c508a8ea37d181adca052ba1a93f497453555cf4eec6563035963429d1658b99c888188ac9ec1e81381f2b085f52e419c0ea617003234e9f06abaaf1d39dbd57031f227692e093031ec5d1889a5f5b38c4f1f51ac1f366288b2e5cbcbf2ab42737909e4872838af1d490dbd5aacfdfae6ee012dceea0910cb2203a3d7fcb4f5f645e85c83627fba56c270a335b34974c08f660e55cdc4db07b57a23a2d63c17f79c0c262b79cc3a14d26411cb775a2d54187de7a048f94cfa30644f7ed829b29c66f8cc4201edc50d82445bb9a918a00b09f0b5168e500a9042e4d8ae62d1bd34cfb6e2c5a1910f615032406e5b8ad1747260dcca0ce436b185be4041e280d4f6d1200a4d06c526b54b3aadb42862850704b5a87552d913e9ad7a2fb209d02d52828f0eb0bc036f338c706a1caaec10fba37c6f9699fdcee47163331b39d586cfdcdbf2fca20ad74ec4d86b28df7e5d17cf9084f6abc15de52c831a10238250b74c0d4f583549af93d1df88f92265247c1e5c2a40cf5a4aa49b359af7fc453044a2afb2b8786d11bb937572c6fe46fc1afc4790f68595b76ae9eb1a71eecc60ec0a834114895191a9e8ef9cc24dc714210e2490a58c157872c8c308b6a5715c8ed8820bbff8d6350176dcb04d3f1247442a0fd41620487cbf2a9fb3033dc0e14a4215c6f4516ed118c4a65f1ab06ef9a7e6148031a41c5c26a490213bebbcd43555249d7b6d76e0f2798d67c3e455f801d961c196f0a958aaefcea6db962151cb2b96733d4265b67ed1e74385838ddb069da6813cef186ebb449fd04fd71851d114c19ba2253ad9a612003a65d772e9fbe9f23d176bfda3c370e21c77d9960b8c6e413451525b0ba9e4a2465023624ca95ca7165c1ac7a8ed0cb2b79da620ce60c91b961fbf1024d0eb2d6b3ffc96bc3d8edcaf1ae1a5a5954d07c5d02d5cff74673df6fc21c5e0ad2e52c66e3c8f7d81eeaab95ce560e40be196bedb3fca33761f8fcbc851b00bbb15c6db8febb128b5a42936198801aa8faa0e9e866754e5b88afe5ff443ab0be2bf376e405b112b3d941ab6d3e7603101623c04f9f5c6f9dccf1e399c3891008a0106f2a0ffc97ae71414a072aa96c26fda4043b86ba6b38798d5516114cfcd168fb414483795ba2896c823ef89fc78b429cdf79144af284939079352b2e2863f4d820af778358d710dc9ddca2c3520c546cf42aef31d4a0eb0818865860476fd1341b0ecbcf4235d256bfb2292b0617256eb5de696b54a18226b4996237c2d57a938374a209c9b191ccca099de7438052cebce821c131385460ba27421c6d9605bae20a45e00cba2776c674cf6b76b1cfafd7191cf9bb07bd7b29783cf38d72433810b47b27cda0bd54e3f11117bd8718a1e0f3ee0130818ee4d78fc4a0339f676ab8c3be0af5e83fbe8b28660ae5f9cee000db6e070d32b285bfcf7b402ebe5f38dd08147d2b3ebe837e26c1d3edbea5320a5e3a51d083d70ea314b50ad9a82041f35684053b8feba20cfa335bfcd98b3283be7adb64025da9cbc160cbef202778f3c3bb79212ecd847deaf8bcf1f8899236a082e3ddd7b82779de5a94da0df12dc0e7bc1f307bb281460af8b21bae04c8627893aefba522af6790b3feb50b4d4f155ff30bcbecbeb2fcff2abf698cb09b22f19d918b058dfdaab8a823cc7fe6936fe2d4a04c6039b99add6c4d94fd6a246af4df30038e1da00aa081783446bcfef69be72a2020f55ca6837b442ae75ebf3a59b1bbdae9711166dba81ef6e45b7b48a094e56bfcbad80e84a16397051f29f21ef1591253ffdb1eba1ad17557f414fc98357c256e6175d8ea568c8c763e9ea7700aec9e5a718ddff4228b8091b9ce6791d6a2af0c1fc18fd7f82b43062d9327073a9a81a0c5d27d7a76a206de6f7530cc6e6ecf022ba405b73f40a4e145003cd9e4ed32563b13790cd28d4381b4acc82b3e90b32d5d3ee05a6112b7ce6e5e4c727bb22aaba41b0c72e7616109c20f6d2dd04833faa83d14b5af71c7a7ff435ffa9f7ad8cf85297f193be949cf589e4f5dc0e6a6f36418854a9ea5fc0a41310ec36e5083e1de5e18ecb8b10aae6e3243b0a5441d1e5391645d217a4c15306e4ae5496ac090a9b30293a013041ea49f099c430fcee57812f80508c9bd47f6f46c01e5042f7b9c7b4c7d1639871340831b429be30cf7fce2614f7bbabb731925f81688ad1264971f79a316381e4688747252a75620eb68663a20386d8ab601388048c2825d9c1fabcbccea6307ce2b19b2dde034362f20f9f7bb8055cd62f4023836e9367d2572787b8fc44437b3e1986b48b06f4287324a7bf4313843abbb060d96c3b121ea0855b4572466876f64a5797b4d3f3cf31b42c2bc03b938b4ed35b59bd5a8a23b7612094e4f1d666441d8d11f46a9c8d303d92d551a04e79efbfee7a58baa53b00586b79375f7f09bbdd4c5bb14e899ace2e3d902b30cca123e1290624463595234cf3c362c5841ac741989361cb693a2b6c3d898934cd36706e9d3ccd9b3ce73404984b4a831bee0a5f095e4e56c0224b5d2c3c98b5a5acb12bb2cbacbc11c4a0241c7dddba53a09e732c1758ce2a35dea4134b1856696f3aaa52e7acbd7cb404a2382c31f82fb2d19b8e5945b781228902672aaa5129228cc6d3a445232860fd80ffcfdf895db1c0946ca8e89c2501016793735638790c13517ec9be4aad436447298056c4e1642cba91c1e2fdea13e87835527c1aeb614ede34aa4fb2e8c6ead4fef91f947d9e5393ab6c1eca8914c12308c486a8be203d088954872811373b9cd8472fbc2c3cd27e234746218db7983f91c1232a01182eca3bd9fbd0b10f0494fa677cc0a4c1948dc59683c6c87cbd5fa3b3249d444af3d7c0584896e90a848cad628e2ddf5361b133c7a3eba1e1eaa1104987fb2c84bef1201c9dbe349d9c583f217ceae136c62fe8da72bea1fd96e5281a4ef97fc58f8ef99258c2c0dd5421872aadf7da92e344e441b31b9dc38e626558540cf770377c0dae6b8d1ce47b0c93e2c66e2c42cabaff80c9fe19f123912edb17c6a5f865988689dff9d02b7611d2f53523a8eb9a64c5d38e5f3c2578951f1113b989de2853a945ed6cdb188fd9fa58104785d80ec81703bc491aeefd4da159ced79110ebeae729b93cb7f8b804c01fe58062fd9eda9918901aff4e2cd0707a810dfa07efc45475647be267e633cbb8642463e55689ce3c7c897da07dcc0f4a178afd129c4587d57a734551138d37f2d7a407591e1319961dc06c8ce9dd673a101df790600d254b23c4b81361ce00be42793cddbee0aa38185d63bee874ec16731294dac338e07969ff053a6260212ee3a565ba1727ba6f388323d0205dc297da33671a831728a1e1c419256952812b81345c603a5f179145749a86bdfd138aaa99132697f7bab48b4b19b1d003622712866f81cec4c71fd3772e6c0eab033b1e3af6b8c09812167a1e5f51044eefe6e27350c63408baf2a9c86aaa6a4d61f0128c6030140a63dc3e889de7e430319c352eb7452b3212d9239753bc9589211d461418b61881fd70283d9cf6ff62f327aa11488ddd20ce18210fdc7beee8e7e8ff65149f7a327a084dfccc49f682d57d2bebf3a0211ee402a1528300596c62d40b6552f430cce454c3d90b3c8608718ea7fd63200db7192797c3a5183ce4fb163b012e1950e89057457ae2aad81e786a6f32d1a6697bd6f4d9755b7997e3e45b86491e6fdce60a5ccb7d1e1cdc7d52b6b21f0421ce0021b7a545fcac833e4b217796a26370559e7f11bb70467b84c8b624da78db51e2565817d214f6e009d47f30c31e8bf72a623f2fe755935f9cba7620da556e96de9fbb9278906a8154a692af9534b3a499fc8a2a9429ab531acd546bca2294f0c47c3f1c4c61c9a500b130bc642874c429e9ab14f9bbc286034300407d00f0388315bb905a73988a2155d253382c7e779f0b540954a1f1626cf286e45dde60147de499d00a41042eb20f583e4cbbe1bb05c07eefc685575692e781ec204115a2da2268bc1bd7934280815090c0a6ef0db73a44bb238b8fe9b0b3260de0c1afbb671d8e7cacd6a8cdbc0940f5a30fc71c0a9cd837473e8e1c54ba040eb8eb863aee3d591101dda76987b6533e95e00e27655986e082b6773ae25c8581d60dd1e3042f6242adcc453433ef5d930600164764a41eec607706fff292f69a09270fbf57fd7c07a313e9619ecdc0bb21e95496cd275be2e8a84876575222db98e628a1ae0c5ec8056ab2a7c6476bfa6b1f99fb9486a8780efb262c1851f30eb23e8272ea47ca0d4681038cc651304582075a225b3913462a04632bc51f6b03a03f034d1a5413693f878344549006977d39d381f325b4e04afc3d8dab0e509aa75bfafb91d79b5d1481b222e285dd00fc0da438b987916c53ab693ef0796656a14c117d231f2b2ac32b3577906c7a2abca466dd08f2b195268f5b0a583c0378a99530573761f14de85b282748c8fb29283ab8bdf082ef83231f73978e3c5dff144726689f26ec171e0df3b28b84da7d5effb2f635839122a706e36ee1b30d8aba8e8212b1e9adb689d3f41ef1be947d78b0fbfce6fa3dde122e5a0a5eef00ea1dc591848f0655623d67cd5e5e99aaa03253fcf4f3c74de621cc2f849f7fe5da5d7aeb5dcc1fcc931bec0ff9006e91ff8eaa134905e2504f66ee9f97ce84b9c6b1376e29ffedf626949c9bf79401a38fb68abb370fb23958a313813131101745ca4bdf344ab7df7a638bc41423d5d7b86dcd8ea71b7b1ba1a9834ff91f2648c8e94f73edb866edd46bc4c7ab80c7ad95a1fc222f8ed6a199412a5959fe6de7968d2221e2b2a496f2ed01169153467f48edfca0a04fde9c12e792cdda0a8538e8178168f505bbd4c6c7fd93651901a6facf610ea956dcb6e373d099c9064cc89027f62a40e15e150c013357d224f240fc717f19491b641f56a1cc34d77d4343692b50d6c609aac031be8d9e6395b9b9f0457f7ad2d61d61c0b896f03f667dbd2f4b98c59baf03272171a8f4346e1bed193873b2a1d5a5272680cfa66857f2a934a607a2ee4c3a0dc790a4b5d7e73b01430d62bc75a6fc0e1c0c1ff09efa48d3529b4b089072875e88ee1d8cdeea96d7ad99a4d0d4b36e74be7fae3ee493b3a1097400b36679332ca1bd8742c3700acc945cb0962de4fae334feff7858702e1bbac7ba608396dc946ee61333a8deba1693cdd4a270201e0036c2d45da7269ea4216847bebedd9f60fdb2592160e6ec5f24961783596e0beb22f7010df381e34b03d5dfc39900d23073a2b509a7f99e492bf2c783e52204c9ca557fb8cc0e98f4a9c385f294629654e4569ece12189a678083a5dd3d24b3c5c3bdeff2ffa55ced1ed25cd954dae11bc37a01fa251641031c9b5b1bb21241979aef87bbfac239003f390d9997c14d8ba470911a1d99f06cfb60bdb632c729f3d79a839c84113017d197ad69154ac2184c2f93612c698f3564e031db719a6e5fea7c4ab4d238dab7235515a550a3b048dc3f996bce6e78f59277ded9a6ef0af715db0d5bccccbf67250b1975b4ab861a2d3bbe85aff87d135bc46f6d03ae7e4b313bd7ccfcabfb8ad1ceaa1a1c1be062e16c5b9293e2322b812cfc8e261f2eab294015254b384be12ef05e6545902cd7243e8a20b6325afe2cf1d578e71d62a9c691d3f62f655c5ad09dacd944c135f3cd27eccacaa9247db3d78835d0635b495a1e8359bfc5427e73222ea465c6d223f88bdedfcf2ebe2637af9898e9a1056251c65110e41a8ab8a6a818b4bffcd34abc655352e9331aa60e243b04ea0255a19db044d0e3ec74871bfbfef6afe40748428d7f94898b788f6ec82d9743820f86d38a38d084ef201e46c74e849811c876de555613075a51185914c4337b02ce81deabf44093a3790af912807057d377d6500f437868aa55d9130da23b8bab0e8624bc800b8eefae41d8f32821e07d0930367a3a70d218dcce55210282da01029e3ec15bf24c67cc6dc938de9fea6a21fd1c8cf47b916453b8bfaf0784f0348c4f2643fd6fdd586a4329354949e4516369d0c035629f302d987fc0365edb40bd64ac41d5d438a09ecf01f7497924edd12671e4c5b7077e40b97f89ab2275136645cab0a03b53e94328dca30d09e9a6753cf3d6bc39a3419894144a095a75620f8b53f8c80206ea6f09a999240c17dcb401e3fefbb76c73b6050810c87aa94d0f90fe5797e6dee5ba0904d659021532ddf5a3eb54f612e329c8becc62086bc8b350e87b075dc0a12bffbd1116cb30dc2f780d13a5f3a3a41444f9c46654e2790ae9126453858b602984a007e7efb30ab10c714a80a31c29b309cb018211294b69e0c7a16ead8c1b4e56612693a6cd7b6be98ae41dd409eb45e813f5f3d1440aaf1d80635ade2b0fa5026fda5f43947cd707526cf9f9efb08049936a610085d4e39b210b80e65cadd83a4a4cd6f0496fda19fc28dacb6abe3f3262204b338d32e27a51c42d9836e126c8c03e747881db53d351941cffc4df27e9c39cb67f56ebe91884100114b64e171ec2425de48cb30233e74d7564384921a7d64018ef7336810d44bedd7e159b49f4e568bd5f70d66fe4a3a951fa471a30f2cd3aecf715e6d228c729f2c3a0335bd3d68770a52b5ae954723e64e9deaadfdb0ed527afaba516894967a21e7c0f23ec095a215e4704057d383e897aac69210f8b586bb8a9431efc7773eeb8091431b12270629b5779722334d75f95a22fbe9f958b1c1b530aaaf491604d89ca00bebc722bd1733edf3712f51ba01b97197a2b48e8c62d3b6895852ed5e6edd1d0ed9d9a88995446cc06f0de9de03bcd5df3406a45807e2065605c3cf22a14b1457845ccb055de269a61852c07bc8bb267846a78cde558b5191992e0f0f6b9a471a26ff6e73108a174d3c3c5e020f001e888109b8cc2db315f4462ce388c85efb8cfbc6954c2c0b8f8f48628d77a2284615c263db3cb3bb656fe01171406c8246012193ca8b32082a6aaef0aec87adaa1d2f43bdf3ea43a4c24d5258fea705224702567756ddfe2b9afdd4f13362a3741fbe0a83d0a86586eea76fe9cdefbba0373c3e360affc2be04b72c004aab2ef075ad0875d2b5283292c5495880c1bb13b13611a66ad2b02deef77d5c8007620069249902b2814edab4c130a53105b295bf79b6e23a41c5ac617e90456dc69190ac113c67962e89b518a97c5b05bcc5eefc6130cfe65af303aaf30f74c74b327a270418cc699b3a3bcba68fa9fb7604f6f80e1c435dfa1527df580359039422d2bd8c3e613d7042cd4478601159977e8d0e943d563129a9fb5c86614d2a72cbe8c72aa3bba5ea5798ebb8dddc2e368b5a062e810dd571529f6fd6331aaecf6f23b8fb6e03e4b8d0ee99383e41a249801530d4c28efc5a35ed8e7d0292d467441706cef7c7a365554d5180e44016245155ef7226891c67375a32fc97c3d27df14d7e38e8aaaac7aacdcefdf434c9e01987b99f352dd8d44749b01ec4777f9e1f7f2d0b6326dbd73493a0e81941401ab423b71affee63d933f8d9e55295dfd526eb463821586ce5a0f55a47951644812fb5c7f25c201030d546aa4501b2ef632aca3902dc782d1838d6e19f1229c47dbedfdba512e34233d8642da4b3892e193d6056e03c8e19c395dd37a6c1d924d7bc69f088de661e1aeec25ea705344b89d5afc7c3f8650d8c2d28db43ced6c92fc83c07128096cf986a5768687ada8c76ead378564bd5387ed3dc2ccbb0f9d15373600968e892b903320da3a2a2b5139ed35a788edccdce0e6f33e2e77de6c921f4645a29cf60566050d6a775bfdf4f5e47dc785f1cac5e72158fe1c0c499d6a1a97834ddddb7fdb822487fd2dab252dd138d8b0994812d9886727de3d4b32c197304e597b7a243decc5ac759b9752afb42c9d7efeec5c25eff2a034636be4aafae896babb4fead4da5226ca40a29ae0d84beafcfbb336108a82f95b9aca1ff1efba00a17c52b361f09922ecc388c222aedcd41d03584845f7a014b394068646ed0690f17dce2cb4afed57527b6ba066a99760dc8ddad8da73c3e422be3560f297dc58c01bd217ae101795a53bbee5f29cb532bf1670eb87f02bf2aff8422c6869ebc6d845167814018c704387340cc97803570d03b5c7558b4c300c4163995bded53ce33afca44dcae6acd4dff1900e7d03cfb65a7c5b39cdd086adf3a8ec996ac376b399f78fc6c435b1754ffac16d4bc15da3ad57a4c12cf95eca996bfc6ce045a9083edf7b86cdbe040380e2b2ea548fa1703babf3ffa55ecdb3e778b55fbd70b763fe6c6ea310e9b4e806d43874efc20c11da9bd1fa01fdccf5980d051321f2ce1ee3b0df5f2a2ec27cc70958cd1625b70b694ac4f1d5a37b67053e0f30974eee66174028ebf43cb1045bbedb486f64d264dd1ebfc950c31e47c55ddfb3d44c87f34c2b669a99643f169fbf65ca079d55c8faae32e37e75dcc8e1b0c517a9765a45574f5acb854bb0e15cd845f606d580a275a7d5966410590100abe4bf974b1348c47a8a1b90842a2728a546da8e23d4637f75d08fa76c83fa59b6820baaf8bb00eb5d7451160fcee6b76721cd9f518c0f6370c7125358a9d8490fb453733ec26225dc5f0a389314150a5f536ae150557cd1a072a2857c80d9d79629a66d70f8c858a24924da042bfba3f6af8835eecb9f0d36f6e84e1a6d02cb0b34a4d8c4da29a0f2ba00e07d589f82f505e1506252b968dc279edcadc51387c93082674dffc2824d6402cfec21579c0d2e83adb1cdf61ef5b1c133772fdc9735b2c2b6609d4ec1037b393c073b26b741cde7c7f622c33ffe43c99d2df69c397f39592b7edefdb762c916778276a5aca98a9fdbcae94d9c11ef9c8b7ae0dc6644e589104328e2c4c96b65f85a6d5ce758dc0c7097a35ac842e81ab9be07f1eabe1f81348165ce861a92dbaf9fae248c8a62212f7f57e3a5bf54e3e7c1cc3c99317092b7e0f190710dcd2c580686f250ad61659eda436ad016254bca2ec15910691777470acb49ef75e46e0255a171af45b97c16b3aea6c8149dcdd04c31ed46118da98e6a38798f72393420231e084e4c924e25494fbacf9d1a327d526d53775334f9e5c17a035e84f3e38cd48557e700808018cdffea5ed4b515335813ccc73937da6daf888d1cb3b73c7d137bebae064e3cbe4d0c7fbf7d4e7f933bbe2e99c64b39f3bf3cfbae4ef24c76fed2bb02e60c270102a883e20ceaa54496b0941452aa283457e96b5458f117966759a206cba605bd90648f3e2420d86ec29a7201e081c0146931c03c310a3b8b84d6321b0b839d958f6c1c8051db7aa144133040a7c9782b146bfedd0bd01f31376f4c1a0474016f2151925cf38a89ad2478c6200ea9526e9f4ed26d1b5a7bf2a242a08b240ce91b8197cda2ee3c0a0ae23a2176ff0800877ff70384c5cf8822f8959736d40f6ce323ae3419b911aae896c15b143437eb2cdb4c1c1d4148c836634e7012b986675b080dd80618cdf61cf88eaa353854ba36d0379f906dcd298aac9f8f9ce1587c624499d72b5e7fcb996624e4dc548869d055a4f56546042cec04299e8d4298d909d7df438e3235d16ccb6150a3771cdce36b03ceadff59d8f80c708426102d0ca23cae81090429605513c446d4e623b522d8afe788f613cfab61c21743b9d02560697803ea1437a585ef22c580f047ce195d1fd543765bae341b72468b0a6adcd95e0e048a553bbd6c6b7380a5f6cf0297ca6bef1c0f12bd5413c5327aef9481e530feedcc1f946517a315ed17456fa2dd39a6b37a0053c88b621865afb41d47452d8fa0425c6092c08a83ace0a48d4947f628b961b8740fd9ebd31799ca40d2396ca0f86ba8e1a06190fb9e8469aa8f2e2b69282299c6480ba124df9d8652af17625ac1dfb7e544e79fdefc57eb0edb7d84a5d73ce0d0053c19e79c6adbe900d744d118a2c11f7bd419fb828b16810a8f6d8e1dca65a155c2b434f5e939cb7587503b562b8404eb695fe2a1e233770677933a400c6ef6586c60997e545b058d6028a06a3e17eba648b10a40ff55809ef3f938819156bf7d4bcfec0b27f45c437fa23b563b5b9a2cc011c653abe2017414deaeed6ac8ed03488e25543a93a7c7e7ffbd75169f7e73929ce58e8bc648c7792375144290478e9b0d0965bb8df146fcd20d807ab06a9239c992a4d5600af24490b1617a01b8b030008f460189013d35070d952e20efeaae58de3a6f4c7f9e45042714457400e600454f76b4b9106cdbbd2c1aeeb73e758e8faccf2f99383eef006341672e8d2ee46a93183f6c15ce494821b127bc89063292b0dc32b3c78f3379067cfd2a4dfd64e728f96b244c1576f4168aecdc7201c1cdfc2c363d7961df64bc9a1279b2de49dbc3602730e3e5ab3c9c171af92bbdd44af50cb817376ac7a8a8ab659b589ff4be915e35dc24193fcb7691a1adcd4f4225273819364590f660ab5f954bd80eaa1d9f61c007fe0d111078b00c47c68b41b0b50a5fabce08a6bb7e57d626fdd0c278220ca0810134ceb03ad0181eb6290e0732e396e0f1d67446cff7d49fef72c43b057fc3137cc8986b05f9ac1f3bb961a97504791be5f27eb6438a0a5474e5da4ef960b42606cf56d21f8fd5eded28c8411b5ecbbd4c3800132d86fa5add89267f851baff65dc29424e687ae736c9856b7f8b78d4770bc8816e042d33385ea2d0d516b75d1d93f4224f43233204995088f436c5b1a371a5b4f09a42111c850df2c2131f504537af3212e81beb49ef438a5fd77096d1c21250b64380bbe5e26234d609fe7041f160aafebc8ecd38d2996fa2730a07bf1139c70d39f1505dd0a9174274c425a62f654e9b82ad202050a91f49aeaab65ab9538c37409c7d3e8a0daf44a65be862d58e3d59bc1ac380a10db12a7a82ff469544457c92ab69007329b06f196ca68021f721617cde69e47c48c0a55d851a5d02e9bad0c10c47a3ef6fdf34ea3eb32a686138294597656f5ab029e4c3a04c9ce0fdbaa7bd97b265346cc6bb7b1bcf5b96e4f50eba191be03120770e04a7ca00c647ae64941791185f34cae3e502dada49fb52b9e68f08dec4166375c0c0abe3c5096590e38ebd1910c003e7201311303478521e5e37d473266e7514b1ccaa068719edfcc75d32c22f8256b8fa68c54c705c2a93909eead5e999dbc116613149ee5f9a00833448a3e1371a44fe853f85c559c6f58579c1f14d35e1d249bbd64bc6075bef6445b242b955be94e476d26ce77eeefca37a243a3f9ef0808e009da7c46f57303eea010ca4700e3c89c63d57ae8647e0103a8d9298a2b07710c53736b07454ffec31be17fe99926693f30712541f7a4233032cd2799befa2ab15d15f3af0d17a13e0150f3222fe4b55d7e507a3a28fe7519d8fe6e39bc9dc3c70e6d13100a431767982918e0bff0021de3a58b33056c1bedb280b093169ffaa030cee03fe152fef2ddfec857e297195de120afc73a8f3d13f04749655e4c2ed9df1b57c2e32c1cc553ae40629f1bb3688d4de1b01005632b24cb1335d8453d25110aa7d2c87559db5d6aa2db8256fc1928c0f7dff7670e685101999481a53215a443381741556c70e99b78be9e704c85d0df119c9dd413b88537bf06dff8ea7abbbee5d6a9d5fa77a336ec00023dbd2721b3e46702c8c0d69a6b77904d3e25bc1de923ce62b1320f3a0d470d8db97e2054fdf675d8e4e6e2be2a22cd494f6f3672ab0e31e302909fdae2a6246894585abb25d12e61949007e6c0a4ebb62d86c1c2cd7411f8f42aa90269c0ec60de50c6bfcb623863950418651126908ead51393df4593720507e2c044b7ddfab982a8f94cefa985cbbfb6d1020d35fa831bfc1d02ab942b37e7695969a28bc5e8ad2caae4b3f3d190e28330dc26fca6836dd25b227094c7be77964976c9a727ff595c73f9aa6eaa4942f904d4ad2bf27204bd384d22bd1b2d943e57996f3ebd242e968429f6f82d0d8dbfc92c0a9b1b7c077a0ec16c5892979f8319262658a2f2d41ac2f953bc64cf1a33142da1b8a3967a0ada0eeb5d3054e4267c6f6ad6a445304b6711d38516122bb36763db3d066b8d5ea68b46fb0800d9293b2ca51b58e9682cb656a8f951e3858d046967d68f40f5620d6e893104761431dbaf71ab09176657386144634d8fe1704074397b1e330cec3c66bb88f9acf61420834e78380366fe185d470eebae6a17b5c70c81b6a9800a7637f2b3180bab1382bcc8347ffd998272910af2b2c0da1de320914e49c93885f3cbdf5d5ad125983541225498812b1829ac7362325ac35e3f5a9b78f9c1b8e5f1f615cde33a61433eaeb65d09b49a86f1d8c78ceb7477a60a4eb0e76517020f938806990576440af4a574fbfad52dd826034c7a1d28b800ca9552fdb39185299df1e5bca918a82b55d094581ae6c605b6c3d244c385e89eaa6d7ff87d62ca3b8e36de8b10727fd231a678f8caa682ed383a04c6aac7f957022110b4459366580f206e420e93373e8605b6a04e37d790743e7243325985979b43a9c95e032935038615325b434736bfba38601872f7ee91466403788354b9c1001df73dbc8911fa5f49c6d4dd428e962b0204fa0c4f8a0799a72a61f64fdbdfd2099e4a99b648cc8bf9e9901ba216f1639e79d79abf5123a31aaf3e7384199a037eb3485312d3e6f9e00e151c6cbef41088dea41124c389f26ae1a646a2aa5a58fdd91343205b3994d0e4c354485a4ab1698a15778fe7decb78e53c5734123579fa4840f800894182572d2a7baa28677a65cda6ae8bfe0fb28e32e95edf536161d1a2296b49445b908d0511a1328a1b100a7d945e829a5a9d7b6322ebb4dc07bb4605c4e3ca84901dd4803ba5881e9dedb018ba2504123f41d54bb77264baea473f6470ad1096561eef4e02723fbc321ed43e5f46f3c68cbf90b90ef4c7e28314411e53bda5fc2485da9ca4be4bb094815f7081f091572edb35ad32a935ab23630d643afd3bf671d942eb1cc280307bd9025a069879d657ac161a409d243b6d3c2de5c1bbad6b72561d8f0d9ad0400c2027106a429828738abf543d39028e9fb16aede11f3048811e79d08b71f295f1a2c16b6e4306326fcdfb09f0837cba0083be8b4c93198178eff15fda11bf59a7b2939de1ae3942d20488f156fbbf193d85c7e9e0158d410e4baf6f11d340c5d77f4b27412aef2274a3de9ff08934c5c732ab2b7031592b2e667d19c5a812e54e250a4f7327d7098f697985cd05ebafccec3f995f26fa385c7136a86b22194169543bc3be1fe1394a8f897c95daf8a434032841d4c86277cc5e919b88bfb00c9756dcd1a0424185cc3dad6098ab84b1d05d435a441fe70d41782a56f2bd5b67682838843e1c11ac0cc397cc8baefeeeab692d9cfa2329a132ad730c107f8336e4991400069f793662e65de67cdacad5445f299faf6d11bc903276dfb982ad3167a3405eb92b622b6ee0d2f93d6a8af57487cc0a114401a4346828adaa1d8531a462a9e84a143a3d871768f60905b0d8d08877e3082f63a595b1bda5e11daacaffb7f4498eb07b00cf5b6c64a17e8da6d4d70745bdd81d9e16016afd33f9a7d78e1b6a60cca6d9dcbde9881795b3797e426013e872bf5f0d2db6587879102884a76f2e76d66e1b5ddd426a3b47607a84aa2a51568bab4fb141c02525ee5e1dcdc0c423fbb1743c80a1bb9eb873c1c6c8103666f394bd88962e487bfcd4f45afd09d5f90fe6f60071bbd02e16d85456ee0489d9565b84138add7aa51a4437ede145aeb8f26792126b036630a1687cde9b492ac5800dc0b4c60cbe79467a7d6d67e63741a630bd274df396e915356e32943e38dc9e7d4a1d3ea540a4db76e04d263798c42afa070dc437cc4649321e894d2fde5dae700b1d0b2ac0135cd314826892f4ac68842e048e7cf2a2d79069587c17671b416d953ec0773724b0ec11a8ce7b42c7e420df9a000f85407133aab987cba19a98dd92ed393112267801c8d58501ae9d5f0d81b4a0a5d5427081dead6f5b00b77b17ddb8dd623d7407b88d30b875283c298b03e9ef610ce309cac70204b8346fba11cf84b67941d03889a3ce0c9b3d49521a71a036389ceac62521cb0d607ebd4d4681523eb12988d960cfe95f469c4eb89ebe704164f7a7c8f250749c1a02e95e2a37220988b4df8028d2d24f08a827a7254b5845afb955bce17f8030cd17ba503cb4d5b52918fad9594f85f3608e0c65974e715702f5eae4ca0cfbf8efe58a31c9eebcf3361a12fbea7d0aabc307c93899a7b24a8ff4f3e58956e7b170396405938b7311b478b74edf74bd2ea21cc6a9b3dc0a750afdb73e14cab110920a0f69e2959f8ce7373c27edb63379703f06c5bf1449363f58b1e2b32726606c249d3bf19e60d669e738e0c448049777ef836d3c557d6dd0e51f3fe6fdbe3e8a60206c0e5d1b5bbe33a057745421600036bf11f9d5f6f53f63f1f01eb20d792eba3944123b3f8bf5e60339c8ea39211b35932629072d98e4a12942af1d3465d281db3a05eb1d26732b88b1bb0dca412c4cbe87d53efb724081791f2e9fa155c875446c57cec345aaf021b3a017afe3761434a5116a272e388edfc7f35cfc8282d4d1e93588207ab1510507f869474908fe9bae67b105ddb1acd3168672ac71ccfa9a5b4856294e0fce56781760c60134f0f9cc7788da28957106af42fa0c387e95983896814590036c0948b7fd5c69a6ed21456a348dfa97b4e96526dea1722861377789ec25cfb1c68e693c7a24bd8ddcac9e19d13a07dedbe77f54bb0ad7cb2a70646c1afbe1690810b6c147c50e6ede54a3f8625dafbdd5483827a2183bf2a655d9e76fec0dae49bfc8406dce6117804f8985dcfa7bc518e127c28d4b1636593f71969ab8dc7909d7ae9d152ff7b7077039730353728e6bd617a7f0ff2caf53821a144a83b13b89ff418bf0967609c144af819c30244079a7255543e7c8ace0dd06435c93e6ab55810dd63e0e57fe36bd79a7aba9d829001dba926b17379090dcaa80fc9162ccc188ff50d101cdeb6e6e5e639f0dfd6e33c45a22b4a50e7275988a553d730bf4c652c7dd4ab0ecf80072505c3dbd231471d5d6debe8d798ff6d3418b61810249d07f91c54e7131ea2d23a2f1499852c1ee02e9f49c67cb61edadf5bc8d8da036403401a9b6bdeb6e0733702bd000e7192662e59161c4bca2938d67f5d772b2d95e0c3114987f22773fa606c092c11a9c958158b2f43fff52f531f0f0fcdf25539eee75d1d78b56af6b4bc2f471c2379eb3f83daa4c88544f8cc5ce82e057c8e75f6d1615c39844901aa6112a8e2e1d67d24128a17feedd69a48080245e2d19a02b0a597828ac5e0041d108e450bd38efd781a6f46521a3ce51ef2668ab6403ff65e0f81db220ae282799105750930468d57daa10240f397a474480e89742ff9b8754a460e344d513a071942468aa951520db133fdc95f5cdcfe5fa7435689a864770c9428076a57b7aec5b01963ef583559503dc677c4195575ff6d6f0f5e9511698a9aba00d49c08069762859d5602611ff82dd2b8f8ff92373809ffc36049d7776e439ac1d5e03b3c8e424aa3012a257da6c4c8831f2038280e58431cc386e77c4a06f108b9915a7a0948f51a1c1fa7bb90b7b6de945241282abba364bd27a8717101b9724e81d4c3d4eeac948542b1099a624ca15569e4e304d959ccb4a310741539fd4dce51bfa6ddb9fc497446132d4f60c4d9bc1c92c65818180818d74e2959ae5c823e6c225e63f9fb428eadd71f83543981cf85e9bd599676409ac370b7396ac3c1ce6601ede26bca5b5104a431600c8816cde9fbf43e2266458ebe9d1b9d061fb6d0a06209acea77beaa533e7c4f5b07455b5f6775f5ec9476ed292e576dd84651a9aea61bdf6dff3a66bd68b674746fc20417a789c7b50e4d3da1f53c5e4eeb31e3b09e02882ece16947a2fc661b39affefdc3592528179093e10a9eedc5a086e42e4afa883736edd0326dae183d5658afa216d7fa06e9a88535edda315df216aade1ba283f891c1177c1a8231f25ae0fff6cfb58d3e126379f8cfd06305894698bf53a6e44fe7732fe9199ba8d5525a6d327ad8db33fa2e8e476b7ed7d1068436c443b0ce4e7033de9c6781f14a6c594ec6b85699978cbbaa991a3a9e8515b41d7abf1d3f5d462147aec9a3c7df8d0301c6cdfba2161a24d2516c74a1fc465f383f01c4868c626a11c15c81727fe86dc23535a0e661d05f4c9dc52c6b2387a0b82790b84a56932902fef4a03b61867456bf8a138265808df82660a387e2ed34f5e161e1ec8b408ff150737e400f94e6fdde14e53871b79c91491f71877710ca804047ef6243a47504cd02607c8aa944f43710a318a5f80b5c7a33ff62ecae4fd7562aaa38cf7df872857df97ec79f5c392a61097d5da097d677f5628662b9800769dccc5d0caa2511995317b5c95b41574e86b97af9c0bfe6710152b135281729bd5225d90e65d512dc111163e7d24401f8b3f64fc66236fe7f45efcfc55ed2eeb49d1e1c801f08901f378bd69f8be916fab7a00504261aeac3ce61a247ae868bec530dda8bb0ea3b8f0a8f44ed0bd8b81b1053a2faa3bd4796cd605ad06c3e594346a78a1a6f79683a5b26413c2bc7472e9769644959adf0736affa08c4bdd9c7fa3fa72dc64eff888e6fd905c458f325e3261d3422032caa06ef85e450fe355dc4c027897ea4ce9714aa2e40b40f6244f992db51545921e757d3268ac770c395eab29141d3c6a58730e8bd428e4a11a22eb45f5284d7c49700a8092a0b6ca6fd765c8eb60394ed0f444cef0c2d031179376b998bdfe39c5224f59ba4fc7de184f953e5440fe57f70cb5fb5446a4bc83465eceb1e2bea923ebb08e9becdd08c369872f7b490dfcba695581811f91fd4b5d50683df5e4efbc2a7b6a329b7485c12496f154fe962ff541f153ffb369ea4c2f9a819b3a9612009e500d57c366d01ef21b00cf1256416bf0aa64df84a8f8d30bcd6cf3de7ec06c40df56a93e9bcb0a9a0d746735f8ab3d07dddcdb36b8d492fc3da0a6160b9ef4bfb8cd046b8c5cc2983347d861ccca2ed6c7b44cd3bc1c15ada01b773d4c06c86cd770dfbbcfa7d884e87aa8b3a00b04ce2c91a4e61a41d2055bfd16345ba3d56b457eafa1955d76c6e0624bbaa2dc4cb88db06d6162e24aaafaba73045ab3e93f529e6610ee4163a2ccc72b105fab6addcfe687c6a501a92cbb00bfb239dcf0cba81ca82b66419ce0816bc92e5085b6c373d8455a888c9066e9cb5e84295df3bd9540c583da1dc138aeef6003b22ca32fea90308dd6fe762ab2c4d3ccef85884d699cb24943fa1f04490478e95de1236cc9b024ce1785a4590932b57bc40d04df12c0369326e29b41053b4b30dd4b7716e5b7c91178752451e224d6c856ed206a345f29ec439be3541e0c773babdbb85bebe3683cf923ea474914c0c22b949001146a09c2bf77f0d8fcef1c8a1c74bd5d589b6689e8aaabe505ef3dd1e7c233fa3146fb5d8b5e4fc5ec37eaf44ee9893207dd389054535529e15459921a0894e40358800089c5600643022c28eb971778c30eb032feaab460a3f2cf19ad2f04b2754fcf12d1af70d04244efb0d50aa1fac02f85ea5c51c2d553dd4f12d254addcf9745bd26230bf0fc4924ce91bd8b482b54f1885b5adf0594e7eae90bfd75b18f6bb0b107122950169c8bc716f5abdd4f3b3ec1a95ea0feea82bd2415d7f30bfc7ebf70c542bfe83250086133d754264cae015d641968e27fd40a83921bbf107181c106892962296bf84c6d53b7bf3d2dbc0ab928185e054971f1eb25b0d68de02c0da5e928e00d43390ad11cf775a20ca66202631962f650f7c5d402fff520be952d3630cd16305095e007571057cdf93754e91abdd15f2cb8d73648d214b23a4ce57adbcf8b7590678daf8a9a98ddba91e8e4664f7a4f27beb3e43cd3584c522756100a94c5596903f287991b8d41c012638e216229f9f9e9188a0846b0563f5c34a0c47a2d86ee7e9afff33af7da4879abe57ba2c5a8a046883d58b1c06c004b6860ae1a40c302f740d11e9fac72d59fc5f665cb335826eb93b3f259d99c9afb406afd7fe37b377a8ca4a3a72404465d28b066624cabec6b4f472f98e43d96682704bb96a9307401ff1b41dfbb393c0a05a9956a40d7ce12758a2024b99a474ea603daf3f5cb52d3bd9c328d126735f696c0e76b5a7bf060918e53f247ebb456bb8b1909a0cf6a2841f457114abdbfc2e22bbacc7cd9b025e6765519f59efca39af7e80f539274c8c0d598a8f24fd7a4addd3861cfbc568cbfa0de87fba0e95be132e79f446a1c385a76248cf66c816b37588bf188fa6a43e92ee6a222b8af297da087342e1fa717c2b62162df5621779ae4fcd076da873d90ddf42c6101c695c788cfc579672e537d06058457aa63a1aa19521bb81024fe6861394e665b2e7a33ec0b6f384ce0b9bcdd794edbd7b05284f2324c7a2123df064dd0a84b5c59c22446329f66878d7e165168ab73ec271c651e9b43292e8858874eb2b5765c02a9b34408897827aa1912a695b6bc7fe635b95dbd4d73c87a6f4a90eab915101cabf5bd1e56c8639a346030703316f5db63f39a8a64b9fc490620f7000073469f68f7b5121e1a65af4f612cdca6b22c1febfd4a1a39ac4560b1181799bb2e22b2e57731bdbb4f6def27e7a6110c673150e8f906e724d8ee4aef2e7618350ef308ae5812e388e4ac852d33144bef5072b1aad47f5435ddcd132fd0da41a3c758a16dbb7c2df5a4155d0a191596d98144d36e7a0a537e07f16b92ee6b28d81b6ab3f3867a448f03edd91bd0918147605c190b8f2c99fb97687d6b550e086aeb17a040ab9f36055557bf32b59f7f6a70454fdf437ad49d164c7aa4cfe6aa89049f4110619458b390fad1cf9a9dfb0e2636e8ed61d25188379986183894544cc9595b4a761fca035b95853da9f1930b3da110d75bec2b616729200502cb56e3be43f3feffdb0c86c5c7e3f011efb009779a932fa6dde2c2fbe453441aa2c43edb050a147b8594494620c4ff0097350863b096a3b6a647b646bd0253e8f36fd13d1adf7f1476857e90812268c518c4c0b4660b592bd158971edcd5a9ca8273ccc68791b2c5e50059a1c359376e0d98e69be74d9cd0c0f93a27392ce7dff2b78d2222c846c4340e1edf389b5872794963c07bc06425b907617d177621ae185ca551474a92754dc3129ab8b5f7c24782b6477cbbc2be80655ed0a1a175c2a9e93dfa732f5a4b5662a508ec6db6edc1214b15c0208bfda5519d40e44672b5109b2711884a1f7a1ef8fe05aa7df62baedb92973a601b610ef5c3cb842d1b67d7f0ae347f109250f350698e31295a4f35eaed8a5378d5b8e86f7c7332c43a255daabf05eac0dea6afa76257061744e6c07f7701c90e48c0dfe40585d475a3ff074fc4781c90aada519757a13e16b2d22a94e1b8f92cf30c98b1578ee82e1ff299f4a8bb11543a985044ad95510bceada89198663c5edb18e2ea0af58638fbbb71900325d70dccb084fc37325b6a3234a57c401feb107d55cc3a0b4c97f1c2fab8e277a34d07599d733038403fdd1cb05bacbf7ebbd8a88bb1c8d7b895d242354af496e3b81b3e0cf1a1d65b9a5bc5a4e78b77601f19d7cab8bd5af065c582c247d77376bc6f0ec791e6eff165fce852413c5d649e4c002ff5479a2271eb09dfbf8f731485af3961ebf6c68e52d61f9ac2d8669e8fc4c4ef025427f8d4ba920c1cdae0da4b73ac03c0db74a383bc20466793278184f08f81551800dd256c9e473742495fa4e08a2d23599b60f6892378d4fda902c07d5e386faf2335742218acf8b52f527924710b0837586f3a09cbd8b8f6c1e27bcc87e6a9f2bce042f2c84ba1ac2dcb2191b6985f0217e446d38abb91117cdb6a75db583ab74ed3df8b676fd9f5a909301ae981c815d0573b6f45282713d5fa2a49e4570ba6ab1d603af091f1ab79d852e94478b708f4847193250e0b3b3c9be3a97bc8f542ad69d5202fe74adb7d08f1227875e5415826df331d3b8cb53d8128935cbdf08708b553f98e9c9c42aba2415408882a2ed0664695fbf6380d87ad60bb39a5528b9ad535fddfc62f5cd6bd9928101e5a547c6f7def1417b81cfa81f266db7f23760e7d72beae533b01ce04f330e59985c5c0d154aee9d6aa4fb7f7d42c72aa9bb5a2bc86af9731cc640d40d75414fbe8cf62b11d2c67581eaef72a96ddf3b4a1108d626265bcf9ade1201c21d6c08e6b0ca846c72a3939db751aef892d5cf856b02286ec36a6f07fb523d3cf719f685c996ce3fd6dfa375b0fb8275a9b3830d7f344b724ea3838dbffa325b8f3550e0e573466fdbd59bdb943bafe92bb49f532132d142a10fe087f400d049cd6f1ec68612a9bc6a92741e52a376265cd62c1f5d8959337cc2805e49fbc2e6e42156533291632a8cee44a64db9d103277c6b0a5ab2c8993e326d694793e52eedb9f27905db60de128a4182cb48305eaab2c35506963cbc95cb990915235c78b02b322411b00bb8b70d7cf666b374c4578bccf2b5ab7f7f713a4da8d7298c18862b0ee9cda16705d11febeb972a87b526c5d5c43c37e8d09855c76760b0d3db2ecf9593bae8d22a90d04c11e872fe31173f536481cebe34395d8b898ae2de9ab094b713a04ee2d5292d475c67b4374c9a669fa524feb8a7c60b1329c846fa993668cb2684b4a80fc1094cba507b157c7dd7b024d62fe77a1b83c14ac1d67a7d4b794e352f271b5b05c6a223830211d7b6f86acaa8967d69d515f0791050c0d77c6df1d414fea1a2e35b8c1ac39967151cf426bf367592d39d5c2590c0bc68c8b15839471ebd0658c187c671a4c97d8953c42d738d5477df101a1eba6cffac020b05fe807fa1ab7976c778739176a5585978dd6704432cce835fcba104e3f25558e62d03562c20951742df3977c129bce8e47d3f76ec5f3b3f0d6337d978b9d1ad7cd10dd1de92f6335585f2455000c2a19653154e7303f874712109b35b581c5f9314643e1bbb22058faf42924852cb0490dc08cf95f0e8e5114b45032e1c3d0c63959b4203e8a4044009021a725b034a7168c43addeea5159b34397e68d7970187c63b630ea6c5a1ea374ea8332874308f6f771eb998cf1063d32b16c9092af6732f277d1bef3f66a7a77ec1fab91fca506dff29d222868ae361f43e3a60a9169cf82235034e61261bc961959592f5c426e7ca4401e8532a4e6b89afb47451b5a20e34cedaaa4808b526c10bcaf5a229a55b0817fa790af3215a7023609e105649f71d94785880fae7c76042a42ac99a5ce3db8a433a1de5f702bd5367118b436ea048d4e9ce0338d07d0bd039c8044b69a4c7d2a74ed4f0144484f4065f47c50b284ec2cdd41072962b0013d7b5435a31b87a4d2acb150a3e613a3aa147dbfebe279066d74f639af7a21a48c509cf6a638d138d5dc86b081744b02b085cc70687cb05a20f6c09e6bb2bc023243d77002c2c38a445dbb9f30d262edb70544bcbfbc8a8abd6860806d5f4f527d74661e0c13a26ad0ad959d4947b4ccd17d6b19e0654f087e0aa4b51912cea40481439d7e638fd29a671505341770741c575e5ea495a8d73f9a31566c8c17952c1ae789830a602a0a0a72e6343f5fea6778379ef5c97a3fbb7819078228f3e5f8931851cff050a899ba3d0a86a25d3b875f23d5f023bdc5668b5be3bdd416e022244af975154dcd957e1acb119a24698905e23c426b936239b63c6e8896b82dc247b3ceeaf2a423a9eb043178b923a0dbe033e2d5a823047f1fbd01e2fbad3aa9e76f84ab6ce0156336f179412b8153acc0e0450099c3ade4801bc9030bd972c68c3e94d0332a4281336a292425b9213b0581db7a8a3863214bbc0408e62c1ca997509888a425c5a249ed06b8f86de6ea1ed1e7149b4000533ca8f3622fabb6560ae85a44ce33bb74cadcfed829bca604f97e3c9f152bf09f1607ee00e3cc8f08ba5f2be10dd362be2495cf9c57ad4ad0cbb04bd29b33d48b89498cafc345050ebbbdf0d363f97f2dd6b51e9f1b0fa550d7bfb65bed016f07e69652c95ab47811165d7d8ec6fe713d2b9fc87f02bc8c8c00b89065031fc7ffc0c724041fd1d03e0970813e8d5ca695dba0be80bd742cbdd017c6e023a1885b0a644acd8473be917744078c84211d01f266192c7a6472baefc13f7282669f59ffcf13b94cbc0b2e9b239ee237e1cc82456daa16722de219f12d874767ffe4bb2cd790bf6234526a0fc183544a979ff8178a1f57ffed956d875aa1614179756817285be69274ded3db2135b0004f04141e4a3180ae2001e1af88d36c3a818298ddc6003e17b537365cf238504829465cd4fef6d64cb6f7256fe1c109a725debe5091a63b917b1db2c0e202a42a020bcb01d2d534088faf9d538e84b8ee264b7642785befa736d7783a8d82658f09bfc4eccdb4c70cefcf15e9be2f65a2795f2d1ac8406e44cd846c9885a4d294386255debd915fa18fc81c043997333b196fec91640826af2bd1f9f200df4bbb4d65765464885b4c0b81c51ba45d51c073ef65b062dbc05240ee9d22d574543a7fd37ab349bce9b15ee63aa0f37b31c00d3e6db0b04ad8701bd4a4bd790871319367a08d98541fc4ef8b38a803ae8c7d048ee061cad9c7a513e55eea5547d57268c3edcb912b637904f1a87df6de1df5beff5e64d7807b1d15b804a0136545b092f0d5b3adf53ae6f5bed9890e21a5d384d34f6c3016f4cd9e389a31b03a65bbc1e0a4b60ee4c9a0a8f29baeca79df10017b65cb9483153ebbaafe8c38ec540f54cefa4111482ab0d6e6185e647ec0e2678ed243c1ad927fdc172cafe41b087a8f6b10074e2149cf6ee8f3fca4f9932d8a6f0309e58b83d9537501fc76e43333143ab290ec6466bfe674d627e5495851d7a5969cd0c06c230afd160d691e929f14c76ccb3109b68ddc212d8e6f91919be4c56994133c49bf1dab66be816a5e0e1be92b23b31d2b6d85199e95292af83b2e46987e27d12503e9aab9d0dd0f2508733bbdecb76c1a582a3fcadaefcc7e9da6a07c0c8fb090473af0f564fe8384360d0f399d7cca2044db2614b0cb67e736dd7b20a18ba8bdaef1bfb1a49887cc82e0bb3049fef78a4c0ed31b78a23b286024636ef17c0f9bc45b1d74009980b01db65cb1f32ef0d0005a32470cb8047c9c5d31f0ea41c92b795202877594e0447ea0abbfe7f56579cbdb0b54fdf313f4bc3fd294b512d4fccff33968216d47e6ef3f1fb004bdcf109dc455eb6c5869cb8713f70082db7ea1df5e282e91a8643f0d12eabe63e02d31afa911f77af62e19e9d4846aa67b029a4fbb5b7e234ec6d0093f27a0eced70d88b6033b243a4e082843dd471a640e091064a459ec83f26ceb2453c83fb80791ef715560f0e6042fdb7dadd9a4c9eb75653fdda951240c412938ed14314a749fb377fce22ad9ddb7152d5d970b76109e7e98de96190d019ea9bdc89c1865cf4e600d37049b5a14e23fd2c15ce4f105ba86c21c2e14a0633475081266e1c78149c3968c4a1aec7b91913b42fdb7f246198b76c1ad964d7646f9cfc6027f0904895329f8865219960ae8868054bb2773e5eaba205b813afc152f2f689ddf3385f7180821c6b41431c248f0b86b53934cb6d4d2a8742ff909561e20270146c75749cdee5797da0ae59d069a388cc6dd4eaa5523914066134d01fc2196bf1440ff0340f7a38427070b9dc326efd5ad9a00e60ce93651275f5a45c7af3fbbe45bbf65320a02b9a449160451d618d8acc4e21513db31494c771f6d1931fdb58cc53a618a14e181531fabdd4fc3880d29875b240135e4034f67610223c1e611ab5f30bb30beace537e79fadc5b82ad569807283cbf3a8cca8fd6488c9ed52ac162d36757908364a9df7b6228dbd948c5e3599410aed018119282c61af6626ad2558f2322f006943fe923817add1b42dc62be8f3fe9593c889d54952392968be441b2b088c352c3db624ee308de425a12fa85ca6e8df6fb763063c588487a056c41d9ea86a756dd8c05e0c7ecff202c3ba04751a126cbaa1d2bfddba369f47c1373566823696b85bf4aae56559a4d2c30bc01301a027791144fd7940f9a193690a2ede09be857b21ea51ccf81ef593d0fe8d6221396fc052b6372ce73d6d9093ee9440c39e1907aae8e9878763b19cf6e6b007493a27e0ef976c7b4e34da5ac7b6630189b5663f2c38922bd171af152782c5eda6fb52a946bfdc92d0334de7eb2cad55de0269c2ee1720e3c88086944923a984d5583f432b36ead5b30aae581e0fef623f6069e5ff5aab906a26c32079c2d1fd7e1b840dc7479864c6a8a74496ee531f9987a08966c598fef37615ea4871f38ddc1f70312573180d01204ea54c40d00641e7065bdcc1eff97289d1ab609fcad7df273109073a2b5b596832c51a897e5fcae95661668af77243beaebb53c7fb5a2f902d5fb4ed9c6ac46f3b223dd83ea747c062becaf6ae04fb0eb3cc05c71281e017fc82cc90372cd46e8892a4d33194ef39265fa343a141b1373f17f24fbc4411a64007d1cf4e8c39bdc5b7f97e8634e275f1f7c8a57c0f5beb2eae2f3f34fd6fbb16d59947f5c21b85646f7682eb8eeaaf4f0eed2fdac8bec4a852487fb72a9ec0f49e5851ab771faa445f798830e073f75dfc6a6397667b86489e5cc3cf523cfea07894f84271458b86a9a5f8f7a8c2b5ed58f05824e1012d2909fb7dd98904414e1ec86144886415fd19bd77c2c443d1704730ea4eeca354028ef207f4420e30bdc50e79cf743dda8e0ad13a71ab4635075d319d53ddc04f7041d878aa71d60498c38d1c9194a9e63b88280346e525ca9e2e32d7c3bb93d67eb3b86f98ef210308e73f3ce6fc1cce5db96184cc4353a4cb69f51455e889744d50370ef238fa918043ee369fac8a5ff72491804f702179d25b4d11b63a5a40a63e685adb823f7a6ea827664c828c18393fb8c8efaf7ef4b6343450b1c52d1527caea6b7687019ba9f004f7e59380dc2ee94f228ff3298650b4661cfc8568b6dbfc4beb498d3ee56937f659fe04d1f8feadeda501b39c8b6ddf2516882db2379a1767d4d54ac4e6c5ca20643f13ea58227ede28fa8753452fea33dad5f482649b7637278030983fae6428e463753222796fefa88f00508430d00e788ee010e2d4a8d54c5412ffe08164eb1e3047c3d7ef0422f88ce878265c8afb812506c25c2eabfe09b9bffba90a073b1b52f877a22107896f316608034de08ab0f3157a601bbe0621dbbe8dccb139887af73f25fc8df0aaa1d3a20efb04f165cb02dcea9175c435c80ebc8e01699be33893a9b9a3212337b8293410f5e4c2baa7c573c6c9d1bb2d6c45e3278071a510c86a9d07cca10ff971e20ed73ce710ee3e8165ef5030d444efe0484c533273d5d10d7a0b143c516b28b165d3bf174e28e202d13d876edba8c09e2d7fcc0ad406919b9e60c22ba2c3dddc5374dffd1368323d50491457a114e87d449ec6b171ca9f875da8993ed8cb93c881983f20465cf141ac23471d04828d3d24c38e27a0746b3e55911f557c4a87dc8f86c5eaab57d5140ec9b7f3d6c438658b9f275e217a79f0e9cba9633dc4bbd7116eef70fca65f7a8a65632ba560e86a2570f85858d3a5e12ac698670a8a056757a20b25120d86225216928b1548148c587970a0688a1cc0e2cd565744b82d72e150aff899e0b30a1c412c69704018da03821d3e9f3a4c82e34cdf55ad87b4d225b8807e950c1f0235df33daec773ae4566cf1a0ba0d53993c8b5f21d4511f5409b50d80931e589e474dd8801e3336cde54c1d7787223b9a7fd0dade6d90fb8adc659bb2cf241c1296168712ab2ba13201fb6422f0b28a43dd40be377b5114dfcf78d68289316eeef425edb2c5428e795664431958c74da7715038650c64efb5021f629c8e3091e6c6ece832b26aea96b04d8e78b9ed3e88d6fc1d27fbf12789985af71ee202434d57405acbab65d6b6e9aeb9d6af69784662888908263ea1fbf1400aeaeaa3d2cb5eecdaec275fd18441711fee04d29abbe68fd0e9e7b12711521c620fb8f242e2da633235f8538cacb16abbab5a9bd521128025e0ae995404bfda91e6fa58d15beabf09471abb64c92357ac6744e9ccbb79b95db5a5b5444892dec56ade38f32de626ccdd840206410beeb8d24d95d63bdbc1ffadfda1d32df2cd84d94e76b45f4e1a86c4b133c4f7fdfa8a97c192c616feb0fcc32d1b0ca8f8213f0a96e60e0293361045b4ef84b9a251497cf7c7a0337d07916b417218509a97bf8acbb865f24a5731606df32f4c349ece4a7981efc45f2d1d0ddb3536a8450678e31af0dbb03a4b656714f21554668fcef1a6e4ba086da2b9b0a9b7ff59d31872807ba86f538582458cd2b249760814c3b7ecafe9fc53f321faf1b78884f0845486fa09d3d33e9cf9e7dd1010256be375bc542479a360322e44a6270f893445271eeba74e382591479a1f51e17e4656cf45fb6af29c5dadc20c95baea75ff9e36e5c832ad3b6724fb5ed250181b8379365706f2a162fb10fc081e7483ffa372d2d6a790db074cfa156899491a8a2a7cd97b01f8029b1676e8841837b249bbd416cee26956f01bed5210d2057dd9ec955e82500f16298523ea5f6efbb8ee678882b661e6f7c21a307b9520b54a7fc960062c93e5724d4481330e32bc5587f048d0a62b7392fc975dc3923af8741ff3a1a1ded2b97c05fef7a98151ff0c2a4cb8a36fb971a398ce04a5d06645e2a4a640283b3459f526f49fe862899c4686e151d6d85e0c24d4c4fd868cf7683033dd01140d858089b4f843317c276fff5b7a720eeae37443a60e994de1cd27a01df90a159408df370b1dff74a5225d7c2c9426d7c37162021b95ea2a3a02e5de239f60dee077746d1bda200a4f9a1f91bfae52768060b666851080eba66ef1700949aebf1a0f5f9588f289f914b4a5a98c8f19e2aa867d52183f04a09d62e55211f4e503395f656797b643b8accee5b5af33aa10e747f3fa7ac50700ced32b9c8e21d89a8fbb0dfe8cf841d9e93d91fba7be2dce745b59fc8dcd96af8c139c75a03e5f76b5dc07980a7a4237512dfb7def9387a6d26a9b81ccbcb30adc4ee91bf88ad95f9594c3135a66340ffa72addc4d8e6553f507bc0e0526df36eedaf952940d7ca86f4246dd10cbefc97d9226a2746a068ebf41ed7dcfe7f189c58ccbdc5d4ecf57e84733c772609a049127653a05b0e5e98e573153267a572b518ab8590eefb448934c0cc0a016e014013bb9a42130c3e0cb93c62c1483740489361db1bcaff7fc342521b28eeac229a0bba86197f6a901093572e2cb7443fe12d693483215cef596ed530f17a7802caa62c8ed6c88f571889c9d30a2ef19c40b0bf5985b1a8b67cc2ec997467d99aad17a396f7caa5a5800d73e1cbefcf3ad44d1cec2a1b19a5bec21df4507541fd5b9ed86e328a67a560db1b43a9ef78e3b269b4c6e23f1ade4777d0e45e0191121e8811a35afa3becabc27358dedd197c82a4d0077986d18b1d5c1d96e8314a88471c3211a26902ee86e52cd7f7ab92cc448c3d303061b6d6bac45892e459aebe2e7635c8494eaa66f355610ac82f428a5108158fcd741643126696e7acbcb537cd9de236d603276a2c1622119cf89c2af9c5a16f04711f68ca57ac07246ae80dac85a2ba50cdc5fe0ab25d90c4a0cff7234edc5a9f030d2cca9ec263cf04658b6cba79755a645b9705af0c3ad3aeda675ea5a6b81d2db36730f826f07912959f784acbae186e011c21279b4d97d8a726f1026a7fb62a72035f0c8e7f705533e679cb516abb31f4ea5b3a59835188f565c2dba4ccaccfa4ea117c3501d7e5d1430c3454bb9648ed3a2f0f66e369c1fa3e82208d7c56c34946b6503b6b471bd3e3706a30c6d0f097788beb5ec769b179fc9ff799bf06b2b350cad5806152f874a5a9804dd124fe0c5d586c53ce58b41f8b73e07dffbd7ab7b1a6b4e1fab5179c8bd13e5fcb7b066050d0f2fd25e35bfc6a73dad66f393aee650d242d084a284b7715c83c7b527ea137b7a2a23f538816dd473008c6747a148b713c3e6d26de3c1297331f3dce3744b7c66426169383125f22a4dd27e625862fa297f40299082a3862d0c1a4f9670921d0676dc2512c14a6f14568aed5c586fc28f6365fe5dabaacc4bc70a935568c6e094e9d2e277735f9c5b801f4a4250c172bb16b5a624df8c826ff0e559bac3962d1384d825109d25d5dc2deea44381a402e713479fef46c7446365ede51e6dc3a960c5028386b0b72141595f56aae1f5ad65c41ed61e7a244a55e9af5bb96dc2c35d04d07505f9e0f2af69a75b8dbcb9aefa5820ad4b513e4009578786b1a567e82229c114fe8e7a35164561c621315dec9aaa4fa052169aea2f6b8737b54548bdc7e1342132bbab7bb6a1bf56a25dfef109a5a742f77d1328ab596ed57a134b522f6b873ed5153ad72fb27046af605bad002ac977b30e6f629087f83f2357478707f73e81f41901f485e85feb7ee374df61904f206cd57689e08c10a06fe9e12cd613ca2b41fc8d56cda4b27b294e81de1012420024a7a394b5f509419271d93e39343ee3952de4e72c088d36b79272185b51e23c43502cce1281a21e1b04710ac8d00e914c75c8120d23539c01608031182014ce8ef037d1df16c7ea66f033a845ffc3fcbd9b8977eeabef5c04317adad6d239becbda5dc017c0475048c043e3d5d0ff1c0e3a2caa7a0dd9bc538f66a6ce06b7a3c13f16a4ef589baed76e9b3f2d0ae4a85b98c3ae32ef5e5b602e02b75b2946a116b537d97ebb8546a1116be42b577398ebd8bead602c9e1ae232c3f525a41c657b88ecba6faf63aae9dbb58b0a9b6a919b8dbe370b72ee7de168785db5bebc39e0515155b8485cb90711d970ae6aae3c05cf5be6ac665581f323ee33e5487b98ecb06e03a2e9797dbe3bcdcbe6f01f000581ff600b80f97bf5cc7c5718fe1be73170b67a9ec84152a0fc197f81900a830db4b5da9f372958578f600f3344f8c67a9efaed4c93f2b6cb7ee405b5866611946df3a208dea321ebf03d2d8cb50b9dccaa8a9f9c397781d97aaa6a68c78957d50f5e7b22a8b0347c17564d25a9ab7f5adb84b7d715beb9fc9749a77fec5505b5f5f8c1715860a35baa81c832ff19ba8f29221ae829dd0b0e6e2536aff5053357e548bbc784bddb9cbc55115480e77b9b00feedcf5c2b201dac88d8f403c2e3ece7baaaf54b7fb5ca8cd356dfbbc98f81da86659168608ed46e9b468161668d103142b4e4067dddd1d219b84501ab72d52160df43093457022082736d0323637ddbfa9397d9ce660c7186bcecd93c59c9e8934d248238d393738b2bec981dd7d8393d3af95e4d11c8cb1edb1a3b9a80395ddf4cc1b6ac0d08d8c5219956930b879f5d62d1289b61999349184219af0811336e8ee6e668e0c993972e4d867abd9ea318182c38db0490b24303ed0deb66d8b318a628ca2d16824aaef36ec36db9a968342fb514411450d2a5310c1e20828989939c618f9bd5a36319bd60b76cbe6c5e25e362b1b2b284fc82868fce6ee777777373f61e309a8c5be9a1319b8d112a104d8ed5ecc888cb3588e9ed15143acbbb9359b4528535471bb179b2be019345ecc7f71bde1db680eb2ede263bd69aedf373dd3afb7be79d908e2d70dbf7a8667b815ed8a0b7adde0f09999b907eeded880b063f15fef059e0914236e0c1a42d4a00c4d4bb65ef3f134c4dbbccedc7ff50c6f87902143b8d557cb6625448c1b5f22d8b279c5695997b196103a215c08ff007ee1710b7d781b10b636e8841322a89069424c9a50c244892976104510b1fa002604d400bd7a4fa6fffaefc6de4eaa1d8b0e02379e636a68d18ad7023c0fd4ed8e86950b0f573c0e5ed4dc907611d0aaacdd0e8a0309e18c8767a84108a1256993e3571c1932b3954f76031f993811828a21128804a62601be8fa3f639a3fc8c9a167780382011833c425c40d10198020d137473ce77abdb34ccc339e5b55595791f00c7e5af5a83125aee3dd06eb5623de92738bc5aad50f731494287d56a9684637d321cd609f48df686f91b5ae8d48b692db63caee58cb1e5c5b416512fa6357909731989d4b10794dfdccc0c7fc0fd5e0a2cee8befde1ef7295a7e35f1a6fb5264ed94db87d13ebed3edf80aafc67109a48e4b2b97d4f2d64b92b0593e798a1deb5c528c514ac9a8e65e44426373ef3581a48210aa0082beef51143f184008e106482a6e7908212cc128a36469a6516acfd22dde80be73bc017d31420ba120b5d4485a0986187768739b736e22d13677d844737b13d6108d78057104e1c604c6070e7ff196bf384b3dd236250393d9f6b6a98df41c96b1ace34a0582ba8bab5420a9b35c850a44e52b2521a88a7aaa1e119da5e670970a5fa939dc95ba4addb94b859d0e02544450ccf4922c76811c49790e77a93cdab33c9e1edf417a66c5234df266b8a707d2b0cf505579d743807e7c7c6255f8f0b8f8771c25855909b46bd9657196c1262c200feda6d45eb1f2532db262539c528bb85cc552577e43f515fbe3c80f18575920474ebfc172d555f6c7ca59fe5c326c8a77ee72b129b6a9195457f90dd5555cfea3c555bfe1f2a3e52e7f29b688cbebca9fcbdac3f88dfa15fb63e5f530ec8f23382bafff01e3f6cf15a3a6fa2bfcb9fecbf88d185fc1fe68798ccbb03f8ee0b43cc67fb4f8ff5caaaaa2face5d2e7fef202cb5613586dd5ee7555597da3c3ef4fc00d9b7a2be4bebbc293196b60d7432591e62994708d2c090419a19a469795ca91406aac55bded1bb5253fd158c9a6a7b038eca57b815ebe32a2c953f57cbbb958a3db2fdb95af48ff07b401a95168f67d8dd4e6a116a0e4a90e6a92b2ad416d22ff12f6af7b0d4075395c88d4fa1fadc781728dbb0050c71e36392375bea73b9a8cfc5529f6bc525aaefb6d86c03bd5a32159d94664103d253c1d0e3c5c4dee1e2931b6747db3cf4c25ecde9f6bbe6b9f1cd13a483700fa4319dc4a5e6696acea973a2d771994ca6d3e97dd09b4aa58aa2a777ba69339dce97aee31acdd271e8bc0f0e87bef492f541fb3aaeeee613c70882763c84fba6678c60c55c007af9a92d77f4f778c51d5df46878f4d733f3a36faf8686fe6b89fe36196511a43171ddf4f68a9ac2ad4ccff41bd4f4d37f9436d34ff68de60f6952239b9a81fef41bf4a7d3bc71fa9162299b4a6dbbedcec83086c5219066a3287a1477ae5bc51840a3db75de68aa01a1290190df0baf8c788955a0961ffea8d2d070341f0389877647a36fa28faae80d83c8be6c8f2aef2bfe26be473c8d31c6d8b01713bfbd213d2d7e48529387b147507372c9d476faa6b3cce074538f17234faa54d0ae615bc380749057cf7412d0b9a26fa38fce3c50528a42fd23397ab7baa377ac2ef57835a72b8168cb506988a48b488fa14f12d94efb0458e76e17d5ed5aabb98ebb72de1d2f464a48b3b9e45f3b5ecdbcf29b9472c555bc18f9d34db2135dbb8fe6e63bed26cb3ccdf1101622ba4683f6926521cc564a962c79547948659ee63412e9424dfe7622cb30db0dab3d9a933cbd83ec1dae94ef1df885be7c0f5488d4ae699afc2635c93cdd73b240ca53314b260aed568dba5c73244be10bfce9a91ba5937da49b4ebac95a54e905d48a073adf5195d39ddddd7fc12fd899aef1b885f7d137dba83bb905a690c664e1514e50cd6e2f04f4bdef53c1ad6515a4a7a2c9a1a3b978687134571f8bbc783c2f79e1628bef9efea1675256b41ee311f598c2e3df9b3180900a5d809678928bfa5ca8fa5c2c6300518d044baf5773baf1367ac60734755d6e0f630c9cd081109aa00312865cf1303fb4e358cfd047eb6223ac2c184823016a2e7eda6e7116679901ed3806833477c53c9e8362e991e571e5f10dc49be12133186319909dbb629ec35d5c0c393732081b36cdb9c191e1dce4dce0e4bcded00e1e3d7cc058ac85030fa4a722b95001dabac1116d5e38336d657aa571336a7aa9647ae94941f9241aafa6fb0a8007daad6e94797ccfa09047130c7f3191656a02edbe83478fbf1ad336699d2618e845a2ee157f26d6b92b2cec00b730dff3a3dae1b8f32d486b8a95692ece54bf5b694f33699606bc4702353dced86d676d2bd956732f12c9da3417e7bbee28a3ec8e3d8a31b6460cf9157b7cb59261d18891638c71c793f1c4a52745d886627743f8c316fa18813e902184273537ecd59ca2298653af56308250d1ed1a7600193a5078d22c35ecd574c74d0505fae0cb0d103ade21c59ef9fc00cda8853bf025a2209049443adc81a70eee90b652c3204d3f512a71945310e8832fd0be5114c80e6cc8b6513abfd9596fe03b2a954c3442407d200dfd369f681a6a1b08be44fba2b827db4ab8315a6804088c20b4658d1d11dcd9e29cf3c194128c24506ef873e8d34035b40b2df479319148734168f7c0dc80c69be2e1ce8537f1a870e139be187852a90423088d979f6653dabb9aad119b5019615040fb51b68207d20b1f1f1f0839087d7cb81713fff27c5ecde94410608becd3a91f91c8e2904836258af7417ac9a64456c7157f29a8663988633036a0af39d21950b22406ecb06fdec9714771289934a2499415311a414141edd33dfd43ff20a7d641f315f46a4edd03e3a3bcb677dd53ba6946ba192d694e760f7c912fd5f6a9f29507d9300e0aea9eeee99977032892b8d2a767e20fcd499808dad456f2e9d96ad0d3ef1424fd18597a29e8b4bd036b0ec60634be6b580fd08bfc0d1c4fddf6f562e4678b15c45a11db8ce0840849c4600750805df99652f619887f18c90f4766feecce5848d69c3c4c11949f6019cb30bf7d8396a3f00ca6067493d4875e4ad2696a3ea774d1734c1fa520a9a620d79493ce6cba019272d25b4d7f43749a523ad91f249b9a760668714436554ae18c4ca72050f82277e08be427cdc96ebba4284232c93f2f3ba01dcf64fcb3e40931b8320acba03c9b0e20837a21ba1508296750cac7d8fdb8d3ae22f7d557c0463a909e2a6a419663453417cff5e045446df662e2ec2f269ee2551308ec761308ec6a8c3a456dd7e2f6de6c868a36beaef206be70d68857b37f31f131d2866932906729e5679685cef25c0f68c74297675086b83cfa768c9760217ab9ad0d1c85d88b91871184b65e8c3cc74697afc614c3e9b66521215052c7423575e3ad841a342183b9463675e3e3084ee9a3a7703eb23e8e744576d8353a0c253dae91fdf1120c253df2344a50b2872ff2c19791e52124d7a3bd816ff76255de678a816d6aab11efb4adbeb509d52c0b0605435a05b60d082169a209264e8000b6e120670c4cc304546ea2f868d3c98b442279d1697845511445d1166394716ed3c6b25aaf18638cf13146196514da64898e4855e38f469af6ae941bcbda691f8d46da471f7d1b89e8888ea83c9f24aa2fed2dc99bcd6bb3ace6e4a59452e6a0f2524a25985c4d762f269d3074e35988e63c1192c97ae5f3a3fee9999ef7a6b9a8d2b200d7acefb2b48c5c1c3f6300d1ed764fe8b6a8297e8ba7f0969a9aa1c553788aed8d164f2175a3254e84544c6880074f7e0842d334a83961016b094b1c217f70a0c3114d0845a1890a9a24217382c414b2084a5060fecc9f2431ee63f26489100e4964387012646588206442c8a85c8e528bc1603024ae272329bdff61a7a2e776df81d00e4108f590a40510f32789123260c2c4921844a552fdcd5cd5c4081fd309501c400209231c7181d6e6fba5e138846d5d40bbedb6621d9359e0f6d08d1a40c2481048f03c264f7230cd8e78e288274990c1c80165e205ab950cfe40891ac4981022c42a88257850c42c89c8872492b8c12f2c2183264d700282274a86624d782067aad5b66ddbe5b66ddb36259c724a4d4a09653225138928495c60fe285102190cb61da129a89831610326c09a8ce089105b0f4b14210651c205b21e7e264e9424b1446c453c7f83c160b098eb3dd6d7adfe34e63f2642513490262660c18418640920926cf0bdc984582c23f02c42164b888650cb064208211218605101bb1d4b090dcc9230917d40fef0d08c09e77cc79ae74f39e7d662d160c1b318da55e458dd8392034ab39830018b8ad8ed5843311e0d6a50831242f8048824b21f55cf44c9a518d9543d93a25d80b46d52a55fe4dfca092a8fe3cd1483ca8db119ad0d3e2084f1c41393491cbcd54af5e2bdf0df2673b9512c5cf96e008d72e1bd2043c511617b1c7739e8502c6889ace3717bbb36f840d19c783e8365a35ad188052d91bda89d60e399401841abf56ccba66948df268d7e912c0ad08e5f4905c428215c49183b2bb759575d8fb992e9d67e88908c4012638da0ecb17246739ba816592107b4e35ee41605480911340e24b9f23048d3cc18d0abe99ee11905c9cc88d00f57ca88402143ac23282142e340ccc4d39c7c14234241b1a1a16e75632b9918a4310931110932fd2033191142321b224a6cc8c4d3ad6e6c25c362b56ce4e478453cd98312e54501233723b651c18384f141f6df525e762026913ec0c09570a785a494524a29a594b203579a3860e2a9252027f80ce11808a6a6c94b29a594524a29676976022adf8d7856bc186671b983c0ed4780327dc1c93cf1334414a5d88788e27633aebc1c1342e87634b8654849e1993c909b08ee9b25c92f5e0d0c534e08e59c734e082543295b4ac92d802a08655acdb58d666d5efcf87ddbde7befd5b289c1d64bc208212be2e8991c3a6a8010c2586b067b43c16ed8ddb07340028629010220840d2184a9fe68f4eda9b63736bbc3c4da4da9194cdf9ed2ec0dd3b7d40dd245dab33e4c36a57da469da7d94ba9cdb0f561c9aed76dcfe83a4543fb5dd52370c06bf6d70d61af3eebc18789e1c48b3cd0b7f187b41505a22b23722f2060784c7c96900a35433150ad26c2e544000385018a55f94575b2e7ce0bdf75a372aaf06aa1c0974bb7c353407dfcb2636b2cf42660819f21f4386d0a6f58a36af1d72d378c7460b056325f9bc66f8ea1450160d665142b777181238e051951ed0a62a92f94514440e8014b45f1f4c0958042ff6f9ef32421bef7ddaf7de7befbdc74f0a12a908e93fa0fd68d38d8343478e19db1c3a74e098e9f87f1d3872cc72e0d08123878e9c570b088f8c6726640811de61d8ecc50c078ca783c10e6970ccfed967b3fecc00d70b50954ceb6513db80468497d2b4f73aedeff1ddc0f7ee05bd436ba3b97ecc0c6fbadbdebcdeeba6b7375f369e665bab7eaf1b9c0df59a851345c7d60d1b769010d253a55e8d0d2aa517035f71a028982213e033ecf22bbd219c679bb637350747162d4e4e9429a8b44d983439e910cd000000410093150000200c0a858202a160341c877b5e7b1480117a96426248944ac3a12447631c454280006200000000000080cc4c89885d01a288a83f0c36aabca9d738a6452a7926df2a073442e3c1abacb4c80569a3ec7aa5ab3fd71ea553cb7b6740039901d696ebaf867c2a6ebc79970fecfd5f444ffa7892f4871a23af129176600ecc977ebd212fe76478472333cd0e57d0610af97f55b3a8d3c2dc3772939170f3afdca116ae2c152d09f347dea7e53c444d5a29352f496a02e55fbe0be2bc59d6195e523b653b685b9ccc5caa24930d37bfac2323d7208cfd3cd904dcc919d0bb4d706ed50a980840117ea2af79e3f7a8331f1b41a3ecb468cc8a59c63dc01fd79db778474c1599abf624323c0128e02ddf936ee829efe6a623130461006df38d8ec19d961337fb1289cc3ed06e4fe2709610c56819c225f1c43c6c674016c72c92d55537a762d4f6af3b5316fce7d0389d0599d542927bcf64cd70e73fa7ead95f969e47ac9ece46c428f49e0e6d40781ef72761c7a3d0c7b22b62834793440d56b44abca6e91f18f98713c5f01f45202f85b43da167e2925b1e7d00bfb79fb73e62058eb715a96bbfeac77b7b6247c720a9f8e105776fc95c1f2034d240aa999bd3fd10d30347c0b9beb4f593af0b2000abe9e104b833f1af3257c22607776427a3fa7d86035909ffa40c9174aa80753c08f086821e6c337be3ec1c9087bfd36c74f71b640aec3d43d3b1dedeb8c7cb2f384693acbb57f82dbdf971b509edeb09497b70495a616e54068e6e757d887b6c88d3e69f3422bef93fcc4d3bba8ef3b7d00404386e8f4944e84ca406601c577c158e04f7f01057793a0af76cd4ec0c1c9ea09f58f173dd17adb91c1d5cd08598e58449572caa8094fc268d253885cb7fbb591ae192f0c4675dda433baffae171bad5818b3e382c8ed72c6d67bd0ef527a28587a692780fb73f859e463918ee255a1fe43c761cc5035e0e666198e4df531a8f4dd0643f21da2d3ea675695e5736c68214153d0316db1b9f7f63697182d03e9259e9c45ddd29510bef44be984aecf4cce5b776b34c33f1b14f39b417ee269adcaa1aff59daf8bec4fb6e54aaf59f4da8508beed5ce226fb04fca930439cc5c2a66ee4ec019f75246816dca6e966b4c997ffc26005d196d36651b10dd42acc3c09f90b72086bb3a1d59dee36f43a5a9abbbc2ba2ef95b861f41ad37dbf15af4dda792a4a6ebf5890ca16e19814bfe6c82da11a8bcfc1b22fb93dc88f8e54ae2ecde1796cc860854efff70b666285c5b0c1f95538bf48e1a7165eb64d3dcc7de19e16bb6c1ad0b7fa03adc6117d31e3f63d1e342cbc55def9e895e97f48d7aa565b8d650939e634675a0df12c53555fc8d9344813925c524d59ae2542c136b6ba8cab54da337afebffcaa3acd08793888ea9fb6a0757020dc62c20353b92b85da758c3058a96ac863f02945025791f776f36ea31e73be1dad041491b7d103e5832664512b0941b1b79dcc1b35a1d7a69b44187445a3a525c1b47d4fb6c9355b83be861345549e51f69987adc887a868a317e13e96f1884a937b4fb1713dd5c1d874c33d6a33c63791b58e761a823d1db4cc56476f13218022d182579e7dc0b2c2a75a91817c5254a82f575c74f863178af04e040de2b8c0fce7a2d0280354dc39455b05941ae9228bb60e3db1c7383bbd19cefb7d962d8b74c9d3457e9a11864b4c893638cd63259bc7d18714d895ec7edee3f873955597e498cd179fbfd844b0c7fefa044a1b71d39b23e4e4c3d820b34203c0a22fa065c0d759409f3ebc78e93bb5c1e56d62dcab6746ca5d4c43bec341ae9b1fe6e08015775e963f20cd867e2058e3a2570faee3ac5a29a3bf6abcfd7cfde3e0f83fb74d23f4119fd7785709f3a3a03acfeb539651f52ebc7a791d5b85c4b2543444db1e0412a8ae772ba0b2383ea5bacce377b9f607c45ef68533d138ea4c7977354f44edf83fe4885a2bf7e0a0a0d928f9c0f2216a65f72d307b9c519876985c71c3e930fda2d35e99644f68e1171c56a12a3da5e72287227c516b5300c457209516addda4e1df7951cc0b0da2a4099b610084b913398e32be2e613329a4e05423b0eeba8e6e72cdef37319e7642c19fa5fc8f6d923db89a7c7ce9077ce07ac9f482380c0de32f72df4ca64c0a877143e76d44cd4259bd9f866e1e1459e69c0c81b992a8e9ca6871f45b6e269526dcea8f309d94ae3d13be1e5c9ffa5ac0936fd126ffddf0a76e5ab62b8371e49e245f08af874113714c966520e7cad3a9b97339a58cbab47c080ca59a8c69945100bb28a2c4053ce1d22899ee2b4f0a806125bfe2419afa41b8176596efa489a0103ab0a167c85860ae4d15b4c7607042c38141fdbcacdea6cd05a96194372d28ac1f251d5a7213c39929ea6d62f3c968f23a281601d89d12e456461eeb707f14a5afa51bef6bf2dbc15d3046f7e2ddf83c8f1a16ae443cadc6a797dbff72c870fb0a90b9e621da4235e6703e91c1d70e67598d3cf2e738e758497468079dd3189ee4aeb845db4e3d9604de3fd6ea503e8c9be96c88029d61135596015e0b9732f4029401364caa7c4661babce2ee88a31178470249a35d9ea06ff4b5aa481f44042f9e7f081290b88a0aa8a8ccc9e9aec39a8f508e461a4ef49d77bc38a6e044e26530e32843578c6d7a0e19307dfc81e521d10617df72e9e186cb79459d587170520c522568407f88343650afaada0e39cfeaf68928b91fea9d5038581c61641f5729d458361c0f54ea408f6db20e1477509a23922d63dd222226a8bbe9aacd278bfa5cc1c8d32e0c455751f5adcadb4be20ccc48072d54a6b63cd7935ffb258e847ddf502c840256272abcfe49929a562c4c39829aa8fdc90df163ce7bb2f4c49fc1114cf6a37d42c6925f0a8d6d7aee78ba9af9b05eec95621682e88fb4327bd40f1a9d7ca69ad8afa6b3b03d00d63f957f2412e2e39090473de701fcc6158edc683172a2bd7f10d9e81159d62b3550f6accc32e1f586a17ca8a87d6b75f8fd8b7ae23d5e35dddabf99802eed5dac876c388beb2ca8f0daa8d81aa192b7215ee59b5b2af5a68a8aad054f19ee5dac515f1e54e0504847725b70853ed2b3ff908b90748b91b5321e0ed4df97b26e62c7c9504590aa71c7301ffbaa1e32cf9363a4edf1e7ea0e8fc2131dd579c21237b4b69119459b7474c3af1863ed7937e3966fa986fabe19b5aeb7bbd87148763de70f9d64f6811d3e8e0ce0e05c5e62af198b889864c76972403f2e77d698cbfbdb0d35c1e4ebb1ccbf3c6f5c20c5eb1767daa1d7c081ec10d102661e4676fb8e378c05709c6158da63889c77b1e47af445655307f6fef165007cf388616d7816d59947c5c3fd8ad888c91e7f72e3dccafd06a1d5f3e6bccd98893142191b7ab3644756b9c2378a853e5a47736c8a21ec5e0e201af14290623f1a1ecd4f3e7302a3541b8fc896b0ebe713d04d03a85152d824f2a479d96fce279b7a386fd2d1ce1896e116240e3f1663602d1cfb0a0ade69a593998c8274c4ed4b3c4af5783b1d2da9fa958a5a0c8ccbb924e83dc861396027e4cc3bc6f741c0839e153203e9a11593357e0655d962a123d3693129127445099e0bdb2663073336708f9e57bf527d79659938f212184b6987819c3ddcb3d00be3e4845a2512e55088e10945ca03fcafc607d8742d0d893ff32a10ec9efbc9e0cb5915d7a50a6ed08fbf32625c800ff87bf92d385e0a9617eb0a0cde429c49fcd2e3b02358bdf74d711d8b9215bf4fc3a82e99329e6b085ca47633b52ee086447f480af4b370592fe27308f453fba42a569f3899805166ba898d3892938ffdccb2a5dcf38ab80d6a1b00d7849a671042729812f6380403f7626a2e24a09ee2800d09c3e56603ebb0fcd2cf129f0355e1043b209a652df88865a5e1e46e2f8611cdd60694cf6714009f758291f33bb5b32a3116fcde2fae46a7767b6f0d9200b2e8567d2f8fc57ed0dff36194fdbae1744dd8d7f2e5e2a81950fd99546109caec420e4c49d2f79f7f5ada965ed3b1843d5b5086150855a0a2b26931d21a66cd3845f90ccee0391a41e507ac44065b2eaeb495c9dde79c3ab55b575f174d370a1f005c1b9ba13022427d21add438d15f0d3309d8e5fa32b22dc1f57df860602fd25d5390dda830b09e6620f8081081e5a93aa229d080642a36e663e96971570c0498c88a7a07cd070ce43baaab48cb65d6f625b883ec2df39abf828c8c947c32b7274c6cef04e53b2419bea843f56d04167f68c9284cf308855d7953f87fd3aaa8ef2f2e46427333e070515101eb0ac5c71c2a8d242adddfe96de255a9e7de2b0f533bec8e641c530e502414c02e52202add7fb04db8446f743d3a1daafa9d8e30768a300cb1962577d912720050e08dd3489ff0cbc3e415d741b188d0dfb6febad095c46821e4dec2d05653f85fc9193e26b9dff1e4f17f90ce1a1bc98739bbb847dad131ee7e0a2b8410d31b5236dd49768eb1a434b3f1680afa4c1546c2e6a0a807a322b2cb39a0a7a118810cd493f03aa24cc372d4c58178d8efdcec3d8a177ea33eeb5b2be109fb796a3cf310239d32830661212a8a271b6163ee6007a594c23a288a383e3bd376e6cdf1b17a591473d083d32d1680b4a349a2acf7425e59d6636bb31316896314a92b96d76bb0efb6126a02939d4bf45d789c83f75aaad519ede398355a2ae7d138d2a900dbf93612c550688a1ad02b0d065bffa65f8c0d582eeda80f42b9dd7036d159b7ce47bbfa23854019bf34d7f1ce7251b3512b0d79f4093ecc41b58e4270757e4b3b86ee164fe593bb9dd80da9900e9dc855899c046e3de1b6915be5850bd35f26ea7d0a83e90d71227faf3bc50e2570f5d96c9fa432e41e52bd95323c5d91d2d5067e885210be6c85bd8ba0084b1fb5e550a72805661ebc3b4c691f652c25cf55c62049708f892aa539565026285e108e37142a8b6beb53a199f1b8b89e7a66c5214c7e8faa5ce12d514d0d0684c7def4d79e42b4a6d45024f0170099427d2847268e602ddc7385d270c81faca903bb497d8eae101947d60dfd7970b62f3b2012bce1321de810fdb3fe92dff4e4ee29883e8fd6f193b4d6296f6cded598b92457ea94f32a7ef1cd92dc1f18900e8b3e33f68a7b23a1e31080c70760716ac97a2eb6464236b5e283dd9354fb9b7a6f715176f2e4636548f38d1c76d1057b81006251a932bc9fb5a39f01ccf8e233903bfd38b14f9e6faf61c144bd3becf7c9fa6d68d0f43e6f4546bbe9021a46f1d9775f9a66187d37b9fc2e98c9cbee187db2d41341fd11f9275dc037790090f4c643d99ea8dbabc0c92dbaf97667b5e16f8eaa81539b2ff43095644baa520637f51114f9ab42fc7c7a36e9cdcdcf6aff7e8d7cde54eb53cdc7f8aa830e62200c9eaca301a8b44699c228ad114592333458092460c13086568b7ecd2bd016882e9383c2af0280e3e1f12dbce2ad70e835b2bbe2bea830487ef430c977ca078e3bc52646706489d775b02bd4b5994c8d1edce4091e39cc4116bd5a57787cf6cc03d5a736ebe634bf3663d948fbad13a2d540d3bc245295b2b848066239df94c7b4caa63076bf8ed621c5c548826d81d9f6c3732a00c2287749d00bf5a6a5c7c60a676eda8efb3a009b8a12645721c71555ef00786276bc0cfda14cc8742d262e8a2d538f6bdf1aa0e1179dc2ff2855357205fd4815193ee1bac98ffaaec181726d5c09881fbca2fc918ac995936c4aca0a7b8a1b6e34b7ab3780265faaad2df88ecc47d17a6f228e2ff3dd4648306f949b5a6840a131f111bf2f02c5cf6ee7375e9b341ba50909914948b996ec39d99114e34eb51ef476e6c7814c30932e175c2042c10285f824ccedcd3899b604d67f1cc7a142ddef082b2677e63355af19c139abb85dc68b5c12bbc7e327064442d2806aeac32cb137fcf5f0d6d301c56bd3a82f95c1893ecd3c1339c2378b8e3bac03a88f463a147bde21524ea3bf111d82df5f815f349682c51040d46c483783a83d29420a84ead44b05ee4a166cfba36b12704b78a79dd7e8d4c00c830c86061d74e1987415e2eb24e3da66d44bcd7a9abbb923d87181ef732b82004f6397d9ac1ba6a06d9350348d11b032aecc7d39b47fe499506ba37061a2eda9a8089597783024c6a90737d16835ac73e262902695f0fb09ba13d4bfe3bf489ca492f603ff32cffe4c71c4b71d60105f78ba23ba299c36e4d52df8269a548c1eca94bf54386c6b7b2337df45d7f4109ee80302119237053653485021675d2d483f130d4655d10c249bf271cfa2bdb0f971038bec95d9c201235ba44377082e5862090e329d3e27eaf585916b4be8fac23c6ff63c21224ada8cdfdd22f8c05cb983ef1e234565c2a4eb7da5a029f32cc82cc704ae987c3d86132a8ce98463bea3810e03c709705189be36ca038de66adad24aefedf964cd7544cf3ab346f9bd881de68d507fcdefd270788cce2bc1349f6389d66e9cd70524db14fb86324b3de40162f795d3da35936e844ea670950ec1cfa1b58dc8e9fa935742d8e0c27fc92aceb5fdfe6831037f09ac367a6511a4b5455014ac3acc960e33f67b7f669bf168aa0d98e01e75e0e9a174d4b41a41d952f7390f9ab93c1d2c115c08f4f439ca3e30ec167abeaec18551b97539332535a9aeda5710ec8dc42bbf4a73f8cb12a90814b122d39f0497b106eb802e5c23aaf179cc53de2d6de682f7250286d41126ec7c098cfedc7e227ebf822ee061f22fac44f19c9107db0e90bea52ee62e66d3a9e681bf20cca2747e95f5f80262c10a9a367685d3a0cd42ae3ea267314a5ee276e74a87f14fc5eec8b03347a073ffc4c9b4db49e4a74392893fc3cb46028e263fd817860e035a33f30d0d9a6d771285c5346f38601f457a756e25682dee0b45971cf0274dde01a43b634807e1e84cb5c6a06f7aefaa2955f5071fb1d045ef1a2576353e8df561f55bf22e344125c0afefc6592d03caf626955551f598250ede74ae0ca8d9f4113c3f1c9486bba8f58c1449d9d1075626cc9afb4e637b6bc1f6402aaf1f322a6b88ce08eb033691e7e0ec5b7e0708d88e8a67b27210bfd48b7c1222340cbfdb639f362deca0c4f41811324f3f482119016bfaf454b1b57208ad81686dd136dd69b5bb85c583176290ecde3b0cd09674bfcc21498bb22c179693b8907e9527c85c79c604419b34ff52ccca51ef8e5145d4a6bcbeaf83e4e227d9ed23e00f07d870e0ac740ad3c40cc35474fb5a1663af1689fc45b2ec22dab8211ff7ce66b28466c0a6fb2c6a3a7850d60f5015cfeae7518484438b313457d6741606e37f7cd4f2ff91e909f847a768ae18e68ca513cdb2dc664ee892df0b30c13b13d8ab4c5a1bfbdcf62ea43b2101ba1c8d0241d44b1156f438979ec4f509e6bb943e96b178e684bb61c12da4e7a43a0b39113ee7c4e34cdfdae766a2433f932c978d2c35d47c6208701f00037d80e017b57b6e33a98e866480bf531fc92dd82f914920af0dd96c2c233c502fb6981ee27d07431cf106325de81f48a925c4c7d39cf95f50bc74d770db35866c7b79a943266b4a6cdd345527e8270b648be17de6e60641a635661811aa641cc21900fd2cb5c04b693c51df3b9b2be758a6eb0bd2a07d5df8efce70959653a604cb2f9d6d0e467a73e7341aa20633685c202e841f6bb3f7bb7f1b9326c62fecdfd7745dd955721ba2fb48c13e85e6cb7d46d09ed9f7c278d7163f381d41477c4576cfd86a117610384e9cac79b1e6b0aea972c549ebc612642002016c700b1f610b8c9ced1d89f34820dda58a3852572caa2907dc96c90af7e353db508754c1db1725481a29a1e496b4bfef3b22b23cc108717c07cceb2c21ba0a41cc0ab1586b89789676d49cabe2b7100a3d738dce6e40cc31c2a9665f17e69676f4f82f6f65778111132521a9209e68b5310ef0ea05bf98379469d81705bf327456b504f54247f6991e8a384ac11066b61f62d4ae77ce6a2b310101a71349fb8e5ab442f04e4bb632fd39fa0684ebf61a475229d5c5080711bd91687440474108be39ce9a78ec50945dcc883d67e523e12f06bb44edc222f9790906e91a9db0d929ad40d101688a497973b45c98c8f5b2cb18b6cac6c06768d147dd9241ac46a5fdb759dbe134a0ab06eedd0f62ec093b0b8ee21f72f9291065f7a9104e008294c98b97d655d0550dab347041da13ea97890f3db9458e5647925cf488f0832daa5bddffb75c35771f174131df5528388e488f87206dd9309e0980627582e947abed8dd22093cda2d01a3e8d4c41dbd271097cecc79692f7886ab53511a85066d07378f0e97e0c011012fcf5614ba9c4f12f1c8d4f39594ecab59d5d9f5aa87062ac24444deed4000b0fd0188b9a93dedb5ed6d0ce9d74ebdde13af8140836092a08e3aa34cb69a46e8078c6b850650627611044747da20f0a8055438c7307d19114ddeaf1e2af29e28120f66cd50cc7f6899e23bb11311915d4210cd03d5f14a2a6cf5bbb3798cd9fba4a5d6d9208fd0767e255c5e1daf7df71f85cc8af04e15e2ef694c1c3c387ce5084f98ced71a769699de2534844606c1c7025c8dc1b762f0b72bff25f13089f1b611c0290e39e503cc906801bf981bb06970902145e745ac6f6e5290db1f3bb7d5fac6a84b02f593086dceba5bcaead6da902c030bdc9255079f3406e53cf6c2c7ae183dc28978d7ee75af71fbdc2b3c8a7fa3daafca02492a5507948c80faa15659e5882bad2bb2113352afbea574d520e5495733a40fea05b70791a26a39fcc6fe380775f2029411209a06d8539528edb024d8686755e0deb02973f700986e625cff752ae3df0b4704273b062ce21a609c857cabf4360b0921d921b964dc1b89465b652d8d3118d4e632476931f096db46627aa6fe54047168357630c19f1ef186a4f347999c607cdb35091d884440998fad1bc63e8c1ee0f402d981fb0a03167ffd7037c501d5caea1f8cbf0a79eb3eb1c75156a5da3694443760f6e06efced69ae90382578e6da6019af3534e680ef284d00626b0a956e72087b4f721bbe95aa3ba5889a3d42da6b9e4f27b50c43c873cecab6b82f96dde4672b3c33c2bfa7344879eb537463042371b1ffd972cf67e0fffadaf9ac3c7eea212f69aa98e335e21e7f0ebe056c1d257e29d6a392c51720cb32e29a50f4df392e3d809f4bfb86da09bad738884b73958782cba3b58b0e6f7a3f5be1cd2e74592b80bd635b8b19ffca331c60911f37e990fc271f47859583b8c7fa70b900cfa0a5823c24be84603935a6e034b249aae66d0de690756656cfa870807e90dcf4d899bc9f2d8cafe2ea0ba943639a21c87fa30bff5d7d34392588b0ccfb1977763d0c8181ba18b9132912371d39c3b3a4446b158ca8e7b6696981c09140c76846f7afecdd404402fea9d9b91a73c99d2d974f7af5fccf0bc7464fc938e758fa7f2dbc54813dfff6fc3a8ea2942be6fb2c16c39336363b6473f762657ae1ef495d0cfa16fda3b09ce8443dbfe114a7c65513d6105a2558b31772f21e3ac129f7fee831a7ff93731459b27a5108e08c5f9e9aeefc9e7ce17c88118c09a47edc4f54e459b7fb5585d36ff484e3b6c7ab53e626b87bd9d5114a140c08d3f09d32d5c490de8152f5c5f86b3f8c5601712df41ce232163940bb610795f17a87c72151eb08f5c07b9a71a620a046768b79b7db5e73bfcd7c47a1e01f1b326d8cebba7e94b814cf37ccf454809a768485e0e3b333d0da6e03fe1bcb59fb5fd7d106267d55426f86fe542fdd4112bd79237c0eb7f7b3623533c30b5d062dc60840d6f193e2f46489fc45f231238785417b1721cd0abcf9a1cafd1d7013ad5a0362e40eb0671d01aaa1e8cb53a6f538ab89731ffcf97d03883ceed9fca16edfe2ae2d256430ced7c9fa730c967620b2c59c3ebd0475e69a2c48d471bc7e1ac7b5c8350493017d8a30dd6283c7c499f371ab8dfce55c7afb49be1979cf1b8efd52ce231888c8c5df3cc3e9faf8b897ca2f6ce53b05f6f61423ffad826fced124a340fc83912fa06d263469455fcee4fb06482df194743655b2fe43f937b0801f71e5ec6d8f86ff3c05cd2ba97a2bb7cee3b77575cda2eeb1fe9e1eba644dc308682a984dc72f95adf5e01b93abdc4e8839dcb8516624a9cba5b6d1080f04cd31d94ca9633d999b25ef85f1bba66436de41a45f59ffaed7d966a9d3b8ddc0a9026e6005efce7ed0533960abfba1f7654ace2dbfb1d23ec3271447d76c01d9867e11092b718775fd581629c97009b8a50799753cbf5c6606f9fe9de1fb43d33980ebf517a5a754cc39ccbccbd45c3d9ebb3bd38713fe9530fb8cf9891cd18ffdf565469b81ae9924bd02883c318b11070a96a0abb351483674e619ebf1a82cdff6f2b3795cd0a0de80e19cefafd7d7a2b7740c5ed2a3cc1c9f058db1c5e9889fb84cc40cdfbd8afb1f1518a9507c185cd2a2955c80641f1f8d815ebf382c5377e2b38111804ace428093c3a9cf32282f570b7916f9fd1c56c260e67dabfcbfe39d3c1b4d83ae4ca770d949303e2d22dab76fb0d723dcc4bf633b5d2fbedd3aa58e3b7e15da1bc3109a2df29d3b26a0a04ba16c96eaf2e5b7b7ee382c444b0f75b6eb8a3b963d06986d99f98a012f2f0cf78921ad61418b245af64b401ae26e2df4d868c9a7fb987578b8b355fe21d24bd21bd95e157615f294c99ea5a401e02979d3f604f0c9068aea33c89e86f8cd3736812d9fbe8a8ddd609199239999fc58aac2cc0012cc1692e4e6b4bb1f9926901284a381bc2e247d74670d76b9b410a61240e72008acbfdc7463a677e33c2704d1425092cc0a379f647a93cad8ee7e101b9a639f26b4967a04b352715e37abde3141afa21cfd6051301d86be5bebe22c6c1ca5662249735b4b801d9bc65856cc3ebb777ea716fc7c9a7b1a780dbcc751805e3cb638784558ac3c4655313bad71e8ca472d2c61d003f7f113e666f69c06300bcf78f544353d2c13e528b44e4649ab152643a185c8252096f92933e0d45a4eedf9f2b6345c12c1e2c525bbfacd85c63c1301e645a541d7a91c12662a287def63ad345e9c2ff378af8d164eec09fc8ca32349a5551f553111314f1932a547b5ac889ae3a671f72a3d0580c0f18c4562abb5d4ee2b90e0d2155d3b03399b453f71de33075f4c523d4aa907dea43204c573cc16186304d7c86c627830424647468c188b437f0086910f734baadf6ff3335ed89f941630d789c07bcc3e360e1222a27018368e8f96048d5f2299239e97977d7a3fc0932644849829fa63425ca6ab0d3f62ffc483f91737dfd358e98116f480d2bc7866a3903472e7d2fb50d490c51106a04a3ec262d7e3a708b184c95300fba72549ac56d222911d8b8e254b82dd6754e1e98f35abfd3e207c5783d19fb46109832e7d558a1b3330298eefb548716805fd478c68e865d259aeba546bc9a01397ae444b745cd6033e2214cbb6e4c802abf0957862bd0240cb94d0a6e4bebeb1b2d56908513887095e74df8ba30209fa88333a3984d00c0080be8e55e945bca45b822d30a0c4e130646ed0535ee4ea4abbfe4156f89394932b8e0cf12180b8dae500878178c0d5574d7f775e5b9b0f2ef86ad0b3e1b0721bba3dc9cf645cd67874f28bb13db883795988ccacec4409241770c4ed6e3d80d8c777aca29afb046bb7a2d3f13ea1c6ccc6fd7c09f9f7d24c36e4e1825ae102ad1742bd27983ada2c913022690bfbe510c2370fd1febe8726ecdd30fc69abaff5c627456d62e434e4afb4413c4bd1f27974db5bca2ab0039eeccc210bc1b4298947713dbbb3259f300b183a4c3cb62ff47a721669f0a27d0e69d387b084713fe80d629b5683a2390e470c27c748c7a31d068393e3eb1bf3ff1bbb71b23ae7c3137ab9e02ff77bf9fc5b5046911ea98efb7198dc349fa3db3280e715ba5b43f36f1f3f44f2902a13fe3a9a77783813020174674f741a6ba2cd4141f35d754228922dafcf4b7d76a10b54048e3f307305a696ab5e2184e1b530c18740aa6907abd55cd8cf11cca66894a15178c0fad8ccf61807896f5e90cc7b05cd0d063fa4f3aac6f14c1399a325fe988cfc59f28c223a8d9b5264b0a557cfeb685530a5338b1ff62d9fbfabda97c7818bf5dc8bef1d27053435bb291ea7e5ed049f15d30eb408afab35735fcc7e93926190c77712aef9c4b194374c58dbfc630afaba0d88d858239e2d92e4873159cfa4f636bfca74199fb6ca761c3f66993f24e78664cc78ca71dab5c5fd27c951cd1f9e39ae5c83d6e3f1994ac4109ee9eaf546e7c03b4d64e96b8c20febd5f916dedfc06ffa656a733588d0b5e5f6ffa2bafc569285445220fdfde37295eac81ed0aa55d3ca3929766056005b24de6544a198df5896d15a9a3eefaf52b27dd05b581993fbb11b2404fe1f8335f3f0234414e113e6ce61f474db179775040bda69bace1db587b9745d2a54677a6d5ca63d0ef9a1fcc82924761e5b98b342bc609ee3b236f5df4b074a986642ae54676f775f4f8e642265d5682519ea0628cb9b5cf640a98ff2405d05d48d090b1415e1cfc2e97326b9f8260244dbf1ca6be8b29bc1711cbe516cda7500cccf34bebdce1c1d6d7b912aa7197658af6cb2881fbec1677b49d236b7c495b5b009cb327684d9aab13e5e217830661c1a63a3f2b43f005d00af8f918d2ad5638048f9389982c892b047241ceb4565ec75bf91f5bfc7d2ca74637cb0bbc135eaa994891eb6097370550bc250b7e31cc7c4fcb4aa82bda256e98297c8b76a4a205c09b74cec6ec48be9804851d2c94a9f01ed984a3a47862b4df1870212e1b35059bce22d5583a604ff398914875256a4b688ba42e1737f6c8a966454c3281d82be3bbb9e7696d90269d3eb176e7099fc48f29c26cfd7eb8db1eebef8ac25e1aa341a83fd740a42f794ce6a22bb68c47b77c11bc4d6deffbd47eca7397f5cce79c1f7c2f81bb53a6598a62ce4f39370f3e2b92816eb86483460ca65dc7f8209ddf86905eb876dc613750413aca38d97443d74940f6226aae366e935d9180fea521423c0afc32d66715cd5a06bb708f70d1d85bf791a96d8ef56a588c1a3d9efa7a8f898a407158795f062a4be71ee2265aaaa041aaadc56ac71737c1a8adf38e7b25bca09575694249394da1836e45bd0bac64b8141c175de9b525b1922bcdd1ac747b6b0a8766274049565ad201d1a9be07111e41688cf73c1584f74ee621d6f9b548c4aab76727af241031b5a49674320fa2b9c0e629d82bb7d71f7bfe68c54b3e1feb61f9b89965e49a070a108244580ea13207450080a7c71e85605e11ee4521cdbd80b73550f2aa06422fe84d58aa687b5cfae8b8fdfca9237e0a69219ba8a18d5c129e3a5b8caa64e949947dff40c4657d64f4107a3312c5352bdc204c301fa442347e059f39d8da31d8eef3c42856f3183a4d999283e408c422db9c912677e68763b91ad4ca0543f732cd79642a4b5a487a0c173ff7835aad2c92d6bfeee6f0da6ee46cd537bd7174c4339716f301110a4c999334a56e489df8e2ff914112a56a1e60b69184d0b4f9b76403aa71ff7f778ab7ae53e9d09f2ecb431675df372ca5d9ae58ce8901ebf4bb74daa81b2772802c8bb40aa8a6725048dd6029e706018d36fa7832bb1bb83fb9c128f802c1625f23a555fbe9813bfcaf841edc92ff318998fec2f10b5843412e6074dd499bda6cc396cb1ee180493e10cdf67c205908d4b17795140ca3b2b908bae1fe8357ab0fa27187338e3d482a1a62c1c7c7a242a077270d17cec50807f7b5bb2be80a82a521881e3652006671a37a6b596bf718e2f9a634857536d92a218d2e31eeb96b9a33ba4a2011567f66536d610f37c44ab12ab8cae0cc30d11bf6ace12ba207f1c8554a767d7a470df195de2ad3636bba2adbb635fa8c7c58ccaaf78ab4fd17301da87affcffe80d0acc687534b2dec9f892db370a5a3b2673c5909d69a1c88ff6c12bf1a2dd1ec5d3d6bc7071271e3a89130a0b19e17e3aacc499c32d504abfea0eb00c9566d9a2217a0bc854fbe03399de87d01779969fd0ff4793657cc2f239076c8c87783d0c797387dded6d728ffb4930a6a0281c55833dfef3a37411aee80468e7869fc06852e3ebf978b6d933d7a0f33306e2b37ab7190de6074ee88eb85bd0226560beaf96399e040440de8d1bf5667df01e5b52037524d340a10a489b3d879b5924e80650f9637d31062ebf1d2d282581295f64a23115ded90cabdcb8d16488ec7cdb8bf8923701565cad18da95c6478180788543244c440c2b89a484c6e90278de75953ee494fb5f4eedeb5304ec86aea1aeb746aa57e979a968c00326209283f23c385d5b6d545867486e7020a653f553383371525afa195ae43ffa795dbaeefa98956f0be8e20a7d33dadd5f407dd70f1cdd5485ee6d2f5b49479b2a757462a6f41285213f41f74b4a597413ea84eab86d7652b2177b3515cdbbc4083ba7c4fd3c94fb5a5014116263b592dca3ca7f3272099f416858cef73c9a756c047a98e79ed1d76f98f6e60131b027685def3be6262f33a25556097fd4eb6f17246722b3fc8c169b09ae8b783fbf6327caa73191268d04e8d383e17427d5f04f68e5119136729e1aac2902c788a10d2f6de3e4f95b6f0aea5aaa5041856aeec53256613fb9adbedfc1a1b0236bb66974e5155d7c1046be4c3e7d8c6268e186291b15e3038fd6da0989f8f78d47efbcc9d387669b98c3c77c111b03c8105544314c08fa7acf9c3cfa1ecf57f5314ef39c379a122b0cd220a0894a347f32418d2d22ae093f8c1be03286002045a5e41e51d2d92cc21c0bc199c277f618151b9da0b6e818c2ba28248cf313cbb988fb2c59a53c7ca2646873561b8d4d876f5108f0aa9e9eb48df288b3470189d246986572be4ff4c1ce00f35885d39765ae6ea7d3e60fcf7a1c3879064f6ea7bca5478711624886c10e47d3948b466401b730015e13a27e2e1ce73bf110987d61112543034cd9d8ece31f796574971824d642e80e95f47dd7f832269229be42a8b9a349e1d280507372f4f426427306bb600af20815d383ae094118efd52b112ec471423a8afc84d843d7ac391312c445ea423f6cf5692b051c7996f3f5f501bae14dd1b6e5909493c73f068664ab298e0126e501091d437b22b3d8f31360fed5b3741d11f988f8191270e229ed5a89b45b63e167129f5933dfc5578f782ee27b0e37239f9881e052a9e21c6bd90ffd99e2100c3912903ec6053a7255f9df28270fed81934a349d1c7b6ab512ee3eb58f89aa4b34e487b97a1f6770ac2772dcdabc7828009f434b2a4aecac21305a832cb471b81fd87c8f019740dfaf37061ef9001ecafcaa6abf0001612b77fff5d54bda079d71164e05f11a40a97ad971d708ef64bbfeaea5023e046c282bf9ad5538c6cc91d12061a93d590ba6aba6c828dded09b3933a21f89d26f8046fb82ae37d3ace04ece50c34b2073bbe9500fd9996d9cff26083efbf4e0a54bb3f893b20ddea7b47268b62b054c7c6fb36866744030d0228fe3bf767836f8d7c391de5108807ac16218e3170086c671eb150963d5d3cebddba41f9c36b436ae6b620f228e16c6160c10266250f29153ecd4a10f14740857062a261f525b9273180f80d431ac2672f8a72ddd401f1c92516afd4f923bf8553230959d16412578b040a5cd52cfb187ec097230dd8c6bd162f3fc97a611d4c6779f0d5ba63d6ccdfc2b4d863a11b56811d2acf888e9a26983958b5c671c345a813e6a0699797dc0d1426d962c302b9b2662d4d18e7ad434a82180d6581cd30291209f29bd97b8e18c7792fb150233c1a385ef1bca14949923fd1d4a410bf7be851b33199e337403e0cd7dc43fa6c5fc9940049844c1b27e39a9e74a1f2f9b5ab8f1f357666fd197fd3cb087c55767e732e2a3faa4eca135856670ce7d2a0b6330c7e5cdbd0a69d82454f530529aae8d26eef322f7a0498c1bb96562b3732577f82c2164f07bba6c7d368d28626ba53e0edc5e991748b1efb99fc6057a861c8359519a12ab9ba0f85eac437d95ee4a83e7da8b4f5960db0e634d380bc7baf4cb6608c240e262450e40042b427a9944d049f2707ae392811cc00bac1e76a96071a9f6a681da60c941a63055c210002e9d3b998513d8daa3e0fa95550164135c7a8ea8145790cd0026b8d6affbb03766bf4e061998932e3ab632044976d1862329c6365eac33fb9dc8cbf9b471366a76436b1d4fad514b2b5f35e4092eaffc825354a90765c9f5ed6380f9627052c48cdb54e76e5190c17e6ea7dcbbb79aaa496d51f8f479bcfec9dc5a5d89b2098ee2e995004151aa45ced007ff5854ed73f5ad46543111c7c1a2c051e722df30aab27107a4100040431c2d68cd9afe60d86780663f219bd54e3084e39a9608289a05b9152b58faa4213323c334ae5772d777d0a90f627e42aa377af4a066284ccbcd70d82a5a89e940b044405f3de207bd2db056d68df3df841e98812b864f3e7c443317b3b4dddc9d7260c018f37e5890aae32e780227e95af55e3610ef37d53863da4e348d811a9108063b54ecd6988026223bb9e772aa53224e5c8046297a52c0d83ca3a02906360561c82573f579c015e433058d1be3cfe272ab3bc4edee09eb6e7ae42c9b9c9b979384bd5cabd95f6a814820e3924ab51a146a202fb50430cfdf354ad2859e34154ea8792771da62cc8413c33893ca5e0f3dfabaa900c59ce84ae148a4aca18206d33c316b48120936dc84e7826fbd54059d75e7775452628c1fafe2bc022fafb31e2294938e7ad77411667c29d5137a15b5ea6ae158977560414ddb096880973ad39996b65207c3ca589953b76f5b2303de1231bd4e67c51d7369f7c9a362aeb232167950308a6f2b6e16e2383b0b57d48281b787e6c3b10068b9a08b01900773ef9b9a42de2987081a28a45102b2a794cb24296a7ad87c24e9d22762b63b4349b3470f3fd78eb85434eb0366a6781230b76935fa5b7140e5e97e49935e5f12fda2a442193eb5979623abc3a29bd41a29ea81a16ba0fd7f5d7e944bde5766df5e28ee49a1e160101a7a8a44fbf3f86b3a52626cca8af53254f746471e4ec88ead99ac3e95bd52a38f8909c251d86baebb35bc355cefb2e1ce646dda81a0eef7350063a2ae95f523b09ae4d63eaebd37b61364b24e0e537a00cbcd78392c30ac34ea98e62cf2bcc9199e548a10085fa1a52f3a077f263ab50e462288b629b1df00212cd3518c263c57f42e836498e5f5f762c547915b8932d9c13e89e20a06b71de73b7242b828dd6022b26b4a4167808289b3922bfb5cdf735e62a35f13976102af65d8e5bcccfa451c43581785aa5b1296415112868ba078c5d440691b480853dce5b78de947c56ffb67071f4cc9c498f9151624d58b1837735502ca267530c2bcf46e69ed4468598387a8cd3b1702135d10e1a66de9bb024c7b1c573acdacf33c84c1032fcdcdeaa8caa5f7a620f079cac02b6d88aa9e30cfa9ed23bede05065f08035853cb5df485614c1850a7524026b37b8803b5e6b14240bb75604c06785db135ebe7c1eef5041d3d0cb86e5ad2425a9fb25ea2dda1b450830a1df9a064651c7d44be3929f72fcf241256192090a5e7ba88964ef0c85757f1e873ff810e46546ad962d85292d283e8e4236ce982c547fdbcbf6c04ffa65042e51914c595f3edb19d31e4be5c980cffc61828eb9773a73d7f0363cd822c3197a0fb989e057c0d04eaa1ce8359301856a503398aa0449502d81d8c11bb8749ba20ffa05aec8342ecb5596bba7afd477bb3294410d81fc6a7ba8e6b7d06340cd1078568a048ceb12ec5e0428560216c6c185b8d823ce8f2d03bde2e3f2164932da59432a594020a08ff07ec0775eb9fcb9d6b8627494507510fb1201dca116a39828d25818bdc93274ffa35110f51d97edd361879cc4f964cd71debc64faacf5dc9a579b237278ca6baafbfff908f5197ba8184c33a4da45bc469fc67917b42cfc36ca029d2579fad4d19e15b7611eebf8f3487d6b6f73a2f03d25abf7f14486bfd39b4d64466f0e8e4e0bb638952fb47ea622f9f246525a9135f2fd10e26a5d77c55ea9c1b3d43674fa6a38fb2c183b9bfa15c30319df82c4ff66686ea4f97cca8b337a5df2f6230b1aca5ec54df573ba8d8968aeabfb1907696542a5929bdff909d22bca605941e83a5f8820c663f77c5d6542a911eeb52cc0f66bf9f25fcd661e9217cd29b1e50a245b06ca7b592ac18895e1487d55aa43070b6a6c51526ff24b16c42909361be7254c6d11197d136ccccd97e511d6c1f1fcd676f4e3629eef7a1ebe577566a9ff639623ef2e123f619729f1a3b8d4dd77346aa6bda21bd7fdaa735afd183b9adc10abb664ae6a38fd27c181302a74cd79fb337a7952461396d43a43bf081adf1154fd330922e7212433e9623e377164448d3f863a730b097d8cbc7017b893d0ef207f6248cce187f9a1a2085b9ed955ae405119281b59961357fc74fbe08c758ce103f9baecfa1527a7f66027b7f0682bdfae9dd07a82be614320ac982f91bc3003e8eb6f16bd5c2c7fc956dce0dbbe2253e433e43ec63e4e563de08ccb7f03e2d3ccc1b89f99737d2c2c33cff8f978ff91f30dfc2af29c5f486978f791c5e3e86c7c90fa781f91668c4d079d1ed48952d2eeff29807208ad9d8fc2bd3817f3ec6d8bb66d6c2c3f497e92f975fd345533e58fe7a1e2c7fb95cd7e5d24237863393cff259326ada9d19c4c868b535fe9ccdb031f86f8d3f4cf692a98c988f6a9bea83669344e02e8585380a7314f8ccc39d7ba854a6b56d7f70829eeab8289b81bf5ca157c4af62cdbd7e42b99a293102e67cf4be5d5321c1dc7a682b159c7e59d4a69b91dafb45c5b8f4fa086b1f9fc1c2d4c1fa0876fba8c030d9c04a5af3ffd19ae70cb9bbbb7ffb0405755050f577312bf5511ff55f6937edd8a3eabb3009ac8f3aca3d45dc47faa0a590620aa1edddeddeeddeeeedeeeeeeee9717b0144951c3e8860e1c160b3243862c66e9c0713a709a9d9748070dbbbbcbdcccdc3c05b69201ae17b6404004545a9bc1e1bf41a89282e13bc80c1932dccd54385ae367150e966b766666fe21046c679c736fc8d6b8eee74710fe5ba5622ab63ccb4f6ac4c98f9667792353b5370ba17f4bc63ed990bd39ed28712ec9a9cae47a7a96f7ef66edc0b5f4acbd31a1a42963652757a24110b03b1443756b85569d7bce39a0f7c4730d5fee2abaf79e8c5062ae989bac42364a75ffe0ebc70fa60ad9284f4587a7c421be8a3a3da21c5505d49dd2832ceab6443a6d438477d8c0d6f86aa769fc688718911fdf08e9af3792ba417e7c1ce447cae30407d25fb1a330020b7a388d3747a9f037211b45c329c164ca243ecb5743503c848d27ae230a9c9919250525e59314e153b967561804ac3f13351f75e539af02fe3a4737f88f9620a493965a933af53dfc182c228cf051215a752d8550f5bfc158cc07341fc8579302659acef214f2fd1906ecf5c6686fd60745e0629357d08099a8fecb1cc5bbeb4460576cc412510632e2c4c8cbcf67eac298469cfc98fff2465adee58dcc7f79f93f5e7e4a7a83cbb73c0e2eb485cec7e185f6e0796338b922494fab85a2b8054eb4e869c177026c0e6ec1c6e02ff905d57f2b40cb6fd345feccde945efe4be69209d1aa84f0e147151493ab8eadec1f802826bb127c20e6bf4e9207f6f2d9e40363791fa697cfc3f492ce6c0dcb1f49f55849750c319f856e42b47aa21b3f07f888c36798056d6b7a87f4fb457f8f984cca40d259be72adede1ab6d99182833623eaaaac1046d0a99daf40526596064e62a0d2aa6d3665c1deae79e21ef676f96cae99b93acdce58782698d73b6a69f851a7122df88c9488b164d54d21f2d74b64ed9b6e8145332b36a3ccd8c253b994a376c2b6ada4b291f89b93c66de2469ce45e2217fa51056dd1e75ad6dc6b3b92a8beaee36b9b2d63e29fe0664f6b42866fc477578bfafc3fb99d61af01e62efab1cf972984c47517388e588d4a4294ed5350c2598dd42a5dc1fa98ebe98aed7f2a7985f5fa17eb64069645c44ed7fa11b0751fb5db26dad85a2b2759a4937ee791563130b6a6a285446c4743a6b4c41a39d097c60ae5c29e28f05808cc957b0c7d99bd2fbd75d3d94385be37fca8098d608a343ea12adaaef16aaea6b85869a42ee83b56a6f56a4efeeee26637ad5e9df58285f6d8434088b8562c9752cf83bf6d29450323333ec2befa8350ffc545494ce843ca31afd31256db3614992bca3b629e35d11f28c4c37c016a9c55ed46dcaadb9b2eeda4db59837bcd7c2b09eb6619af2115fc833aafcd93b729ad75a938f4851cfa792b2d7928f623ed009c63a8c5c861a0032e6fb55fdaeb6f5bcd85df5783204f9a29291af520f4221c862f2376a7fd44f60ccd29bbec998ef2b03edcdf5fe6cc4ae7a784b0db235ee92720f5f61c068bda8be44d5b3114c47716cd860c621e8de8b8e424919639cb2b58d30ce0c66d9e8c174cf5f627202e25ee48f35c6da1bf9fe08d81505ae174e30b9625e62edcda9facf30e61601b255ac734f12d26fec231fc5a252b5268153cbbf6fa13c4ede894e7aa4f568e901719a9da7b992f9de81f86a9d5e9491d41ef982165347d21c1b399891fa6a3e03b5cf8c253b65920269bd252049c1137a5a913ecae4f4285f0129d250fe32244c47a76cadc619b38704461554aa1b90b688412d34818b219c73cea954aab8854a85c3624928657cf1c507df7b44443a4960b19c900ccbd288706820dadd6536c29d93ae72ef82d16439672f95e874293d7b215d948a7ff220184dc53fe5a46e383df6a948713899be64c61ad5eb01f277f02aa90650750b5285a04c99bf5051ce5d088a88e9a8d65898999971ba856a1ba73ac8bfde8504a9f29763203d4e0b4155ffa983a986eb5d4a130f29976390976aae4975041b434c219213ac64812040dcc62a42a15828162bcba1357e1d140ad53f54301d9d366ee03e1c44751563596ea11cd492b11323981b07d9ecb4b470cbb7d06da72bd3213bdd6b8649b584f9fee76cad7fdb1932c4848382ba494d6a09f302f3450e04e7e01886c9765f481e84c9e5fb87ecf42a6645e305e6afa791f5c8709c068636ddf620568796373de625976c9f4da7c7d8fd796c757aa472b63d4f6da9fb26969f476a4b6604a8b20301d35ab3561809a66e7bc4411021e2b6a67f09f3514a847fc78c21d486122d4d12c2766663a1ed8edf6aa761f9f6f9818f93b0509d08577354d3eaf53abe9aa827aa477a5a3abeda5691516ff790504d50bedad6eb384dff125d194b08f6c38183ef1dd6a427f562cfc31fa3d7468a72cf677627b1bbbbbbbbbbbb3d6eccf0e01eedc636e39cd98cd6f68f38fb107677bf9d98c25cf6b1b252fad1fd3ef2a426042ba59178b60f998061bb37f039ef5de726cbeeb6e6da19c130d13d93d9503255d21b37981beafddfbdeabfd3b9087d6cea88ea802a1381a93baf3a3d526d48e16a20af778fd1dddd2f974c60d8ba27dbf3d1665a5b199665795dd9d7dd39e79c4c6bfb6ee22153b98c1b4d4c860fca84315b468cc950bc603d2a0e0034b3586203fcf8beebe07b6ff7ad0a07eb5d319841d960c1dcaf802be2dfdd5d56edef0a678bcc98fb4c38a1a1306028418d29766912747b1641e510d4fda2a67c740db235fc4663b85684b9bbbbbbbbecc218bc3b8223b7ceed0b15f3d1540d5eddd6c00a1c3038aa73efce9d83cfb97577eeee7c092f5c3167eafeeeeeeeee5ad12a151898134f9a245104904f104838491133841c01a9f263b7b51552e198d15032a5246ce4c8ccbbccccfb7177e3f2261104b31793a6f2d3f062a21a6ea99782345b0382c96114d1d7fef5f02390df8b3e99cde43f947fdbccda2b775f8430a8335456ec460feca3a23c231b3ff81035b250c5a868071e472a13f83fde7b349078bb4c83c9ac41c6dc18949c1b633239428d283030bc5a260316735733ad7e9fd9b7bb13a7dd938dd0df935ebedcd5f592a2beb55d99182c3618267892079d8ca898c940d4a3645a6b94cc37db58626e73c1da6f0d7f0e6606b09d119a3586989bb36b002bd37f9f77777737e6c85474852d614c238b29e9be8d26e64ec1ae049b3ae75ea63577a33a4d43b50d7c674305b206ced14081f3540efc19eaab29567d33541b3008032b552272c0b60db084b571830ffc33f38b008a5b06c0ae50316b03f36ce66f8d8d1dccfdfe554548513a013863c6f8bd42b4ea358498db4ff69d7735124c7e9c5e77ba19a9bb355fd41a56cc652f98983375f79979eef2645517c1c1e782708155594e3304168bc562b9271cb19ce52c67398b15646b5230fd5907334ec78ecbccd281c3d281a33d8b85a3727de3068bc562b1de17af57a4528521511a9aa6dfd5f9fc6f4429a57c7ef0e13cd65834f46000cbdd5d4a09afb07be618638c91d9d9d9dd9d3d875e5e5e66762f689a0d820f2f851338b3fb83fc9c1f3ff8de735591bb226362f23b1c2c952ac618a373cece9dbb3bab543f3f103dd18325ecdadb60010f8fe3330e1ab83d76ec38711a87861e34f4f06e219cee6eefa3103ce71b3a222bc6e8ce721d38cdc45b91c2fdf7dddddd9dc3810d5230e3b226e64ce5dfb76166e699224851c619ad83f7de3f32bb3f7ece8f1f7cefb9164bd870c2f91d92d29412c89425294b539626f7b4d6ecd345482965510b7991c3542a2993fff9732ec618a373cece9dbb3b17a10a025b609bb5a5ec6e4f1020bc05b6dc9ec9cd4aa8a6737f9b13c0766673e9a9fe4646975193066279f727b08a51d1d64040ef75b02fa5e0294bc554c461be3ccb1b7179d9bac785a6e26fcbe5e7ebbcd054a44c60583ebe7c781975516b31bba1f4a787190ea46f895d05265b79ca8c38f9c1f2a596cc080b4d3d9a82f44789a620069f09b38d8a6a5431fbb72e223d5b995917b9d1915191509191509151dbf4bf4f693784847cd5455ed442b0a881dc7bb8f3139732a4c4e8031587a552b9604762633d0c5517aacb4f561cc434fedb7caad812db412c57f6068784627950dd39d73f145171eda7ca195727188612cc9e39b40dc38defa61d444c20e45176ca142ce4040712ba42dd588a5051dd984a75262ad24112cf3133b3a41b2a5a601231c590156419d369ce3937e426d11095ee86ce3987d58d9fc020c55ca7c1a188d3b89f5be3de2cf22a40963ee0cb7f116397a6dea33574edca6a9b652c4a299d9da1bb3b1dadb9bbc8d281c31a8b45a483088706e7eedcdd3944624807eca07622a89fb44d50db38012184bd6457bdc405e90e9c851e39bec2520cdf6746caaeacc6871e14e4e3e31314743deda08ba670789acae1b9aeeba24648543ef6a4dfe40b4854731a9722a4494a846baf941e4e0a2b5ec17ddbc4f59730e67bc77f23c70d6c863165d89bedaaaabdb9b67d792089b0fbc5ee187b5a7e1cff9df7373807cdc6e0bf37b42c93c17cce060d58865c1798b39473a250ff9aca65f5d7cff5632c1811d1d35239d18a0f3bc180f439482e9aeaa7a92ebdcb8a1ada2efcd1ae52bdcd20eaa2ea4157687ed0417dc58b9c46fef095ee69c2072aaeb4a268fdf4505369f9d99bb5a2faf5397b03ab2451fe691b4b4cf85b934a7cc557dbda4a0a8f6e17b852df63dbcdcddccdcd1de4b40d71521fd556735dd757a91c6eed4be652c55065e2f2ef4acf85201f24e167e2617a5734a58a264d000034858a1e18a65401f4de7bd1023c271ffe222f959f9130fb44f8de7b8fc7d71d8a54d343530304504d10bb68ced670c6a4e5b3114c1365e242937cab85e6d0345f2f20ade5a0d31a7f15a2eafecab22bfbbabbc952e9ddb4a3e4a4bce8eae547caaf0299bee01d7852103e77777777770772ce392e02cc9441cdfc8dfa70d4c772833e02b6336d33518fa46e3306d0c1641f7f6229ec586a9aeeafd2ddcdbd0cdbb9eeee06c2465bd50e3cb56de0e6b66bf6af816b3fcb000421a7a2b5cb2d8719b7b87d05337bd352a2507b93d9101fd0b54b6d23b1b8651ce1334f6477db07b64739ba18e376c4851e0c58266907f90a0e0ac5baf67168e8d136f16a930d2cd55dd27dd395964cf7aefdb97fdefeef3987cf753be79c8350dcb9b6d94ac4c580852bf9df9b5ec95f275422bc65750d8084b9c9394f3ea819950e190d074b288a94284b1e4591ff9052873380c91b2200050ed868623707ccc519dd503a0835a6e886ddeddabb9b9f737ee420e71e81ad5c1dccfd1e5c303733ff00fb674ac1d4360a73793695cd602e45cd1e9512b065ba357ac0337b6086ec1431627acc8893d21735c129e2a6efbd9144ff45ad9689a6724a18f6bbbbbbbbbb53840748a9a8f452ce8942fd6b9aeacfe88946a809337b3543cb67a8fdd3363aa4e43bd7a1c6bd694ac3d6786bdc825ded581a5f4d2a5a6831a9484002a49c1385d2321bcc142ce17fef9c93f2fdcb48efb1953bf0b0ed38d263cfb483f4188914638c54077e57657691b2f79bc1c7f882ef247cf735c26c6b37167fc2ec6edc1d1ec01f290a664e8e57103afd50f6f9207c867e7eb4d61e84b38b17e5fc98a9259389e6b4f62468c8a727c8e7ba7c2e9fcba727c61e98181a34b2dd980ce63a3d4eefb4cde9fb8bf0b4cdcb632b1a2d7cc7bcfc16321ad93b4d0c098624747a1696fd93634c5d7e7b976ce33fb51ca99b1901da7975a25a3b9d284c6bdf5ae9bff4a52795de6344dc4cdd76e84d5ffde802356572e4d02506dcefe796242d3d2d488ab605c9839047916a36f1d2ffc0be2465b7f4d4fe969efe16242d49da06be942d3d06a90448947f28acaaade94cc98cb48615737ffb2a5b7c9e13ccedbf776da3398ce65ad3948e21264ce55f38e7d6f8bf164c3411be443c43ea4e0b10a67c285fc8734c66fc48ddcae882291f5ebb10eefa83bed0a13f775f2a3eeedede03471392743a8d7b3f09e67e5ceadf9a3637544569d01f0b6cc843b93c29e662661eea45ba6a8c0b4998f01fe971dae5a22e990e566bd257aee574c70eb35e18867563cc7c750da7d29f1ecbb65931161af35be8e66acb63da0dff03700eae979309a407035c5c32977fc9b623f58564c391daeff2301ff3476aef7485a107e0185c1ec75f321ced4271bc2563f999b1d0781c67a1f13e4cfe96f4548a3035e2ecf45eabf58074ca27b8ee7969cc08fea25dc741fcc43966e71cd31622f2a1d6daa9f89013b5cd90536108bdc9d6bc16daa690a3358cb298bed91621c176a694f34327fef5fd38be3272fad2ebec75f2ee3d3a55995bb775a2a91cd3a77e947e1f85914818866154879d57e5f3624f7a99318d30870c27b61685aa71064b11a92115dcc5a97d49c2e4fba7f8698a9f7449ff6dbe36c3a4092a219e5f1f1f93725f663aac73bb0fdfbc01161dee8e936e378392c61691f62a423a553eccc7e33ded57c2743ec6dcf7206cdf5df32338ba4e6b4ee38ab9f10f13d24312d57e878445e8e460346da060364e4be8957fdbf197b407035a731a70bc853fc559b4cdc65dddc911101207c482eacf49405152fd541d0948aa738444d205d51f4666989fb9f1cfcfb51e95fca07e22659f244fcc248c04c9a9080950eacb96a83aa5a1357f940f13d37cc53fbb722dfef92a13d55a0f17190bf969d0dc542a370931a9c8b034fe08e0c1ba9f60aaea0c94ae72e932ba3b2f0e964ae538bc8c1d77767700d49dc245142a3b0f05014a1d128294b6f2f8c77c349523e5a77078920e01223fbe931ff05fea86f778247cc17c34d5294771349d9c211f035b5fbd250941551dfa5110921a535dc12cd8b7bca9647a6c4d3c8e54cee05fdc3f49d44bcfd9cb64b6c95aa2d7fbe88a7de4276539b51f23bdd3940fec99547a57fa57fa96528976b605a95b7c21a80abf25733f3323291fcfa4779974ba09b9bee55f361f66a4c7b2eb796861e69fd747966cc697d929934e13699b6a70efbcab5408a9b461abd48e4f3004ba85bd5b96eae1809099c975d11b5738ac21fb41fad2f5580fa42f416682615405bbfb516c6304f372530ec0c3abfb3e0094616f52efbd6ecd39008b8757477b6c0d6f590aa6d3d954e2606e0f0cdb1fd294fb232d28910c711ae71ea7f1c7d1646e8bf42ca7f17f1e273c4e7e5c0f9fc7c98ff7a4f7280fca699c47befc6dbd16a43c4e7ec097ff5e9394e76485104ece58e2889e9e16fc6d459a72746331ad51c6e46e0f02c68e999999999919720b7a7208b2234454798b2aaf95caccaff7bdf736e72b323233730fd89483a2084a28395128997f191d8c0465653acda11e3513843d5282064b9061b16ef72dbb65d8c64e61ccede12bed0a821df5d050d47f294acad6b410e61ced7675a30a07932b84912ea57c1efef282cc84f4d35724fafc358098fb2a8f82edbbe3b1153e4b15162396222196293485a6d014e22457eae2ec8de3a8dd4b847aa43291fd17b337379b8de3342df4c0f0a79343518dbff19d6947dc1ee62ef3ee83fc961f3ff8de0ba50248b91608b2e9a813c70cd68f1ac04c01d1b0ae1fc2e722906320e74c2c32d03090bc7a7ad68aad0fe440b257b294529e4abb372fcb2dc880c8a9ae01ea5266e75c77fb200261a84c89a47ae7d5dedc77fbf3c1ef32b012d979b533de79dd5447f52ba0d6b9f7778bc3ddb7702c958e1ecd7265a830f963bce15e724f4008213c8560655adbe7c79c0b8d8b947f5669aaf6aabb6275d26db1e41c8490e53087c5923c9976b8f76c83cee483e574d5699ba0d40ea32461a5d3c9b403b2904824928358bc349d4b5ef2a25b91fa7e1d0ff6283663fa27b19c5a3009dd2b99aee843c2e4625fb1c74c19d77855796158c9c425d2258342c909a10b742f5e246c003e30e460f25b4dbe7c86bc9a24c90f540825ecf196402b9c7b2e2ece6bc0112abdb8a909e8ed304a520933954ea575af84e3d5bd9cc3b2dd8b2e4f29ba479af9bf7c965eebc139dcb42a1c2c1d3834a8349c07f2f5511209bba40cca997c74752ca51a49a6fb969294e11cfe5be59358b21e3803f8c1b9e8a2bc1c8ccf7970261f0d5f1532ed80afa36d204eec815382596aad75b40df429f54909946a8ea032f714254c53a870e229506a8e1a1f9a764469f34c3ba07beebd7c3fb510438b25a81f2c6656b75275a1bac96c16759bf9badd80a2fa4f7f141645a8fecf82ea53b210e30a30aabb6ad5ad6945573e9ac215772e8705500841142e4cb0648cd686aa0e8812643185085664418812fc8080074660c2c41196b472488162a88c2952008194164a2221836b8da40580b46685b9693df4153aa804a8ec57acff4e916284cadfcf5d6052be77eeaf1677a499a180556c4d36643a6c0c1bc29d57bbfb5980edcc4643a8ca777fbf5b72bffd33ed60d92bb5486bf2b70a2faa70418b8787090252105860a95fec4aa70a1b9fb063541ec41934142822cc2dacc09ccb27bef2971f9bf80ac6a12a65eca2d628995b51db1c61736c95d18b2a5b8821b590d1c8de31e95ef86172d7df1bd38d5ebd2e9a463e2a4d237ff7e601616be46f03c8f1b6d818ae526f3475635095d9aea21130026d8dfc93189306529e67a50623f8e96945cacf3f5800f5b4b67f9b25ccb4e3f4588d24a6ba3dc9b4835b3159ea079c818816a4297f1aff829c46c638bda169d0174379d60a44082d69f1f060f2c109091cb49c5c7fa485954a0a50c28b29b47878c07c683999cf037422a5e5e49a8188d686aa12894cd2362f4b23df09bf4528aafcb844951f6750e5c72a339af148318a8c54ba00c2bafcf68c504fbf3d30aa945f83ca7328efc15ce9943994d6e43f20134c98dffc89bbd04d524a77e4a45c1e90d3c8081463243f12b5cdcbcb8f54daa6e5e5472376f586e4d51f9de86846af743c76d11d4495524995124995b18c8e56aa8c55249418256651e5639ebd275bf3a0581af9f249f1a2ca5ff92ef4866e41e9a4f0867eaaacf2df10bb62bd9f19b096a8d2a59cc2989ec46b1081253b7e74130ef0235095f94a2ffc30b11a49e616811e50facd7dbb7c948f23db81877c1f25ba438d0f33e6e51d898947cb7b4f8691aa0b794bf692c9270ee5bdc7572dbdc77bbcc77b5a8acc22268b454e23ff94c52eb2d8527c15819c46be9473a250ff9a1681e47f13a60fb526bf85ba94d66ab8606e6fc8573ab3ba64449cfc223abe72294e235f9e4ad95697521a72a208d49a7c97225fca3987acd047d49afc127d43ad61ee09ccb4a314a97362bee761a32e96d9a82c5f45a0a5912fff052be61bc2a2ca7f54dac6f4f25f179ca35f9ede943d2aadc9d31b3a55137d435b231fa76b92cccda554293f0249a02adfa54897f280ad52af67d38e8beee092ea4043282302c6ec1c7c8733ddc377cfd5bdf0c3ac41d2f8a4c7a0c9015c750c31e1c5d926771e468b60a7376e30d939e7a02f71cf45298304103f3550c2064710e2052df8e2ca1146e4600a1547b410c32545ee1cc698052b3c49c2103968c20c2758a10415380081122730a182bb422663c04408254e784117538042869210a060c915a04c3134858914362049f1440112062c24a41799a22c5118b51f070a4525222881edae1eb7cafddddd0bc005fea94eb235fb5c63aa9576360ec5746109937f06bbfdbaa39b6d33759ff323e8ce618c477e888084268e008612869c58820c31a650c1159c9c6186142980520c71723d745c2cb870428c02cad5e46aa21af20114e9440b21be0cb65eab8579c945e7dac78c2d2cc6d36e8d6b316fc183254894a081cf1320545284d822046264e10390348208084f28c20984b872020885951e643144122b90c0e804a8d5018bd52476f166ae68c111a0f0842ac838430a2b6070c1842d4ed053e404162c4c0924c50f6f098e85c417ce42820ee1b19088b34a54a2bada3eec151f3180a001135378e2071f8e30e2c910097ab0d282d61457c4e841144dccbc808584640013b56b37ea7de0c39204ea0599438663d0817f7bd99334e523bec3e91d380b3b70162a4e3baf2fd47e1b5b801dc06a312c0df7afa80b2e00f730bea3a9afd03d184ef5b7a66d9ef3bf59c9d713dd3fbdea265c4607e037364326f55b2b7161bf8fd89b97774f5a606f48bf1f4408cb6fe9779f93d89bf9bb2ebfcf437b03f38bfa05c0af007cf5c2ef7317bdea245ca585dfd8ca169ba38f88f98d8150f75b06bbe22327fdc4935ef1d090d03e3bd91c6cc58a12759f83e815070571115c82cdc159700cfb437ac53e572eb03938098ea13f0925d47d06f46ac8f6a0eed7cdc14a3601d4fd006c0e21dd849bb80c00dda40c7581c90b4cb24f61311f74325ea613a984b1b4b8ccff35ed0000cc4b130f580180ea247d440b34682789a17d04cc0d3366b034501cbda090e0d2326425ca9095899582b208ca82a583b24ec2278912dd1a4a12a42887b092a3235672755027d15a3bd1423847cf6063e89774eb28b53fd2ada1d4fe1a6c4d3b7ac38cb9ca28634df8ac9b384dc7df2ae1e3f4be7792a5f24fa6291fb3c29f15d217b24ea29b8460320c0c3e981b4c1d9269171af0cf0c899393e94def5eb53707a832c850d7b4e38218c9e537adbe977f2c34751da0ca5063762d1575e73dd735d9006ba665a8348e9734b9748846000000b3140000180c08060322a160301c10a55df70114800c7d9642825e984ac42889819852c810430c1100000000001161da0437a6b0e522f92cd1dea1f89e37971ad5d5ae6a19f08d1fda4017a2b26520cb6aa0ed8c4096de7a7c85e6fcf3fc69c0e1280c41dbb03ce1dbf8164a0ada0d539c14afea76ae4c05ebbee972f67c993ea3ac948e8202c3f4fc2926d0e056b3a5dd8c5f4e19e78cf55573264b50c74c72e178684dff59667d6b287d758ab6c6b84147fcacf6a41592972c166e59afc5377e3e1147ab9e622219a8118325cee7107ba4ef3e8ee2dcfd4d4b0956c934a8b32cee0b8ea6e1470a4a8a4867d0e040498af32353c25cc695a03fd8bb5d9833210360401d1bda1f68a8f2abade700d26ed461111c2a3fcec7172ed24bda9be33a2b4f4bcd1c37859ac64a7e32d4193173ed1221198a1642a0e84990695df40bbb8528b01e40299b5a7a282d3079a3899448abd99005d84a349db69a06748defc6d69d613ab6c191cce9872ea292a7b994c13aec11c00b1d459a6ccb7e5fd0393c8a3b1ddc41bc3108046a34cee57d58e4a49eb76173a8aa42c064dbabe18cfd566842b1d2afc61e2c4d6bb250050ac8c0d185b0e1c87070280136c1ee2ccc5def23e24ced8f71b9ab83afea5634cf6b76c1fce88e58213a065985221ec66ff4fac3ec96a850b5bebf57c567d1941a4434848bef5d4b0091f9327a3bd742eaeb26e487b5bb09cdf252ac5ed35395170539f9ce0f4a5654b95b264c943fc941fe1f74b9894ead8263769b0317eb466029b8d220459e3839c28ea990c7d3f4370633dfd331bb285d06acbe1b15ce55f4922409f3fe1375b456242cc52aa2a67666a6b94d178881c0a8f1a265362f63d748a7939809de85ab9590cfc0bb9c954f78449a4485e9e5667e89a4a5669a973568b732ebee259e57f34baf616c957623a29b7dedaab895ff888f41375c9701b6887c28e9c80d2d7c1e680818e368cba5915faf72dad86865638e9a3ca6584615786e18f927e06d17924563d4471c6fd5fe829081fdf011d33bd55be5d69084183c481668173b227b7b5bced22a42aa97d7ebc3836381e206d5d0bdbc87120913324513483a85696d75365fcebf717f041ee7efa6ce4a602a10889e5502fd53c0ea0cc95f32c5d09eacd083db3def055333d5ad4f65a023b582a704cba1d96a4e80575765b5f33b07a9f2c37af8a1efcdcec3f3488194ab170633e432a600cd5d64d6a6a684cc5b9e468cd019dfe805dac20167afd0b31005f190ebaab8ac58da12bea89604fb44d5f8bc5bd9e938cb9a72fb4807b061b9d3e11c7d0d0720801ce5f38a0919aad0d418aed7a138b95906c8a70f31d5f2856f88961f0683cfc036d9af8905ceb8b28a0bf9823f518eaed2e527bdfdc401664eab89ac9868579fc69079984a802c196546a4c34e57e35371e39712f5ce9afa8383764e0bffa4b8b47f0043368ac780e1e5b94ae037453de232812902c616971fdb0a8d9c00414c4c9498b542cee512ef9618fda4aba7d9e685f6ba3e3bc2d4c6c89e7558bf29351644542f5573e53044604c50e1274cabdc5db0a2e0c5b9d297b631cde910a1c73838bdb93fa2567455b40de3cc09e8ceaa256d8deecf131d1b10a290bf4f60ae71d69686d720e20738298acf90e446044250b39da5af7d6c7adbfc574f8fdb71ccb2d08c7c8f178028c78b5dbb1f705ec4371e03535c2c5635533d33bacc1fdf70c460d1b64f46dc11dd812c67c6cb1fcdec2dcbdf97dc69a7c77d8a1335967d90cbea9e2d24e6126e9fcb2ac13a09c385cafaa9347048b3757403c71ad60b3ad0be104cf7b4011a79ad4e18a9fa2b9252446416de918d40d8838f375fcccca3d40c1a59033d030fff5a34bfe9ea5ab8a6a52c80d94333837551bb82b90f9f8a520b1ee8584205f216a27e4be7c432bbf922239a4ee63d363e48db00ba1ae842936c500d13e6091caae4a831574e97721285bc7053534c9462fc9fcc681ad695ea818883c0be88cd9164d77e7aec733c320f2f150b743f75e1d81735d02e086fe3e45cf99ed5d601375bddb55c1517d2f3adf779c06b34f4366ab022f945844b6c48e75c948755d8aa5161b63f8e6421067e633986f30d3ba20fd1b1f7c1eab219259830668996ba3f60738827bdf99925ec8e381f92765bc5029d60197c94f2ef0be469ee53d1adee894e96f03abd70751ff87b5f08da0a80c57627d1ac10e2fef7f0e3614771621d55688ee881ba39fc359bc7a8f3db25e264e8db84c65a35b5741c928a4cba3eabe4fc0027d44b26d811b0fd584a10ba43ff18e8fd26ac3df562de1c86b466d161d8bf206331bb325f02d6724a18ed9bc4943e0183fae027f6ebb1f3f735a1c7e51417615c05b9f121a6dd55f02069da2a0464491ff376ebc91c726b58e255b9cff9767b1821355883fc6007e79767eefc56f29f1a0548e87462190757dd8d13cb861a3bf7a9b1254810d7e72d73e98e5fded4b29cb34c8cee367e791c9b84e6d5289724532e1cd63eb1302fbd00816a9f3b9e22af4cd0b92a340911a13e5edeb820f62d3568a09b8f6dc972c8742859c879ae757b7e9e49ecbd1bd87adeb42042fa91e0b55317d40d030f4521d95b76b06e3957c9e68083e75c45911f79f312a80840a48371f1e518494b6cc00f9b2265bbe9f6d71ae25e3a161bdab4bc9c7119c4f407562e13915eb4cd6d97cc97447aa3de3c0b6db5a2705e3842fcca8885363b86d9dadff2f08d262111baa1c4f7cf5fee4654001ace2aced542a9f48f1e00558138da355864e9a45a48cf147db037a77ff6069fc1f1011c1a191ff7f239732bd9eecb9c2ebb5a6a621e21c6b7e799a3de30499f63385682116298a99822f1e05b626e6ff5a465fdc8a35796d657e82513404e1efebf246ccdc228798811dfa1c5e1a2e5003e9aec41d9cf159be304a3dc48de91ca874f4cc8f7398f643b14cfcfffb2567b1a38cb9bccf422f4a11dd75a26f2ce4d3b27bec7c807cf4e2a32b3f5a5d9038d2c1a174d9d98054577bee976f00519eaf6fd4b5561c69f6860bb98052cd1c20fd0287ad0c0497810541dc22f7d76854ef36bd718397ecbe8c0187852b0e193a0d69d58ae2bdabf31486d6e95c8066f538a6b60e6e663ea7bd8f4fd7e740846137c2bdde6ba8e6993482ea4fb4dd898159619d684dd5df2a0069e6c6faa28270a8118a9027212e0f214b874c110fbf94d2c1d6cd8a03501a97e14e07b6ea6834b899bebfd6c0a3eb22f14f8c033d9b7fbbc4c7a4a494da664232d583fbfdf8349425fb3d2ae473f6c7e475f7b282a0c6eb74c8a76aa0009eeaa7485ef6448a892655084d9fb6785c79a71f9382bfd123e92ddc0fd88bd0225f0dbb6145c1c18f2b8805d05b1aee7c1c431e3c1230e96c11c545866c61d1a64c25f4a2f38d5004ee03250c9e9ad20c5d29116644dab3e0efee04961fea8a04f25aa9a0872d825d20aafa1f95b9ce8625b97d906ff39ff458297e20462a7e6f213d4bca78e01f980d552ee0b8ce95ada802d7552a233c3449f9165641387f1e2a52631a4e934d9c612d0bfd2dad6fab2d35502af48da2b51a4e8b3f1e25f14d72b96d842b90bf48845ff47151d2496f02889f89c96f0b220afb300f8d68111a1d5bb689e07d067d22b647e7a9e4c2d0d2c1b8b278faeef76a7e86368d5d1f8874b043815ad0b381934cc4387f2cf8766484e2312248706a90fbf4ddf681b9ae17da265c56a0db447013a7f3d47c0f958f9a302fc61f7ae351b84c9a4311dbce5c4b3956170e62e0bf86cb54216bfd71e845f353d28038429f97febafc4d49b4950472601993580b20afc5efc308cd518a7682e9a69c4f4b0c926bab50d4dcd03ea03c3cbd514dea36ab9595a472bd3175c736621a9fc8deee642f5a5c4e65d2af890383d823d498f26e37d8a88eef40aface055d49b044a1595ec14893624282556cfaab802dc134d0cae99dba1d3309594d33c8999095c5a9bd3619512d1ae85d673e80e2582cc95c8c46d3040959fe7c1f4d2e2992493677caf04c2c2fc05db515c9dcf02ea0e1c8608615b72f412202b3b84191d3abd6afcad98ec896c54b4b2133d9fecc235abd75b8db7de64ca129e0d23b2a578cec8b3d51b84b81d59f3c90a8e83b57f9d9880d0cad8ffc56686fab428f0b53ddb41215c17312954aaa9e4ed2204fa5e1c0ca6197a71f861456eda9958ca223266ef1cd45b794048e1d70e20c8ff6283e8e64027818f4e2394abe32e0c492f0b650d014935d7ada36abd6b794e22b6b6ed7b4bdc0b45a852a252e88a896da978f976258e73e1710957c9a67ce9a8dbeff41f187845e86f2b88b9144eff427aa44d8c1f95e8f910ce78104552dd1c295fe7a47a0014435b7613b5cfa8377b7731544dd137aa4ca6ad1054fb5c029c8fe260469b5630975d49d754b57ef06690728922f96ea53232346ae27c181186c632961d412ad5d28e404292573e8696a49e7c9d31e1d4de43a6f464213fa844b8937b2849d5cc86fb0efa101709e4ef94d502fb8050016de7b6525090b2aeb0a50279cd6934310ce4b8420fab73ddbf06c4e28ee6ac38cdcbe580c6b8302f3ee70a70853c0699d5d2c2b294c30f6aa6b2e69f37c6254458f7fe42420c9040ef993ec7e408502444865f21355a0401b2b88d8314e1bc11a6c65bba14124abb8ada805f9b84f1f271db02cbe351c292d52ce12656d0def703c6eed2af423803616acce8821637ba2bed4db1bfb48a64a4ada9b23d3780cbb9c46550c3f3f661bdb0a8410d3dec0ce54d5275f50e0784051acffb72536568ea374cd9c9324a5f9e81ba8fc15357bb4c94057ea709b3df870ddcc7a6fa9d4a68000238dbd23ff4d051123b67590c4e2fc0b5c5e1642c17052aa558bb17adbd32ddd30053ef4c3f09fa4b278a491299d7afca7a6260f60d7589df7ecdfb060f1e80e5a20eb5ffec13750963380f02c530893fed0116b10327e0309ef6483b764e184b70c4a0035e01f95b0c5e634816925441d19b0a58041d7b220ea2241015a5f088f00eb8770f41937629b3f21198e18724df0696d4351765cb121623e33d5c0693abcdf0d97db1da4b4043b2f987022f4447374a97c14cfbdd72ea40fe0addf7f262d71ecf0f5d94981f11e3930d99635f62007beb11741c4b1001f90f87d43c38353e8438f382201779327c24199711ff8e8933f11d5186340fe87fe66c48853075bdefde6d5bda5ba2485c5ec687e7908f973eee0ca78f5eca8236cc7a7f21576a50aedf7b811008a0d5abfadb6a1cd7e86f2a9fa23493edf886afb2862c0ee51814d0f820ac9903ee7b622766e8f9c358b445f7994830668006e9619a4c4a23ddd47e8f4df0aa5deffd990bbeaf34c24b897fc3c3cc4ff70803ff4cb55b810983ea99c205ea9e128182ae65cd3231a8a3efffce7f7d8a72cd934a90f7e7c6c0eb7c229657c3f44579ad0f77e96ae5c869ba29402c891dfd42e2b3f4b10febd750c690fe2f0ed26fece5e7b7c2cde893ceb77b1d267560eb57b5b391b7ee684d1ebffa0cf2483f469fd3f0f9c524c99430d660c21d35d5313636d32581f998fffe21815a7fd1a3fa087bacebbeabde37997a913bd0cd6bfe3159e42c6c9e85443dd68635559c296b53a48f9bf1ea8550779f48891a27594c42bbc99b8fe6ee57c177fe42e7fe56032c4ce73907b4e5fa5cb2eee0dffcd4074c19aae70d0ed6d33b0c299fcf0eb217858e200a33d313625a32df5713591f0a443fc05472008acf36e5ab71ae7e7bc8d57b89e6698a26f775cba95603716b8a35d9384c2073fc7a12b6e9bca91cec855d39a3b914599396a95c1499058fcb7e48f2ab54bbfca0d6936b0422cfd6f2efe788d332091729ff42269729cc190303b89134fe5868ad77d8ed34ba9fb7f51dfe22678beac53b8bc8c64222943305ea2b173764d6eb4dbfc50c22ee72f2c7fac9bd0639f93e5f357a2a22228a8d1158558b109c9127b565211112808aaf084d2a1ac147967821a691eb23e1662e47b6807b04d28f55dfef06dab40975bd671155295217eb554dcfa3db8c860e55854e8936e4f474c457ad17834bbd5d0a01c4098f2964124baa8105dc53f11adebcc69bfe98488aa9cf1d82f186b6d89d468ad922860725f27d83a9a4b7319336c510d518f54d7e385411dfc886bf6a2bb8c1a97acf43b1913d38f2124537b6c15d5c0a6eec27f06f28cf5a3541ce201c01698dbb7eb7d77789f939a41e470a23fbcfb2640785cc334a12255aca1ea88d20bfc319cf293f12067792a1271385d5ef0cd4786ff98faaa86d9029a48f154032bfb8e06992bc93f5a6358ece5c98deb80931b0a686b36312d4808cfba99c3f4b408eecf19170a908eabc62178cfb14cac67141e9ce32f0fdee7ec0950425f1f4070b0e5667fe6dc9a292dadf196dfc42c4ae5e8f04482e1752d01e6e6cc67cecce3775b97d9d9ed749f593c8b207cb60cecf3e3c787c59a41d98037647461aae97e653bc1434089094772c7cbf7bd626e94c446a234932766f620ffeb25b96fdbb2a1048785517106e8d76a3f328a756b7c134165a40603feb6cdbddbb798e70654e7e1a4ce87c3efab2524fb9ce265bc8dcdc32ad0c0836cf0974a015027254af520dc48f9d61f2f011e1ec523689ab9a1124cc76a0e8059ac5aafcf2a49a33b28335778f8a6f29fdc81d68b3e14ab97e26ce1f0e28b80f45a99970cc615243d9e6caa6e3eb9857a37d1f7a4c89061b1db4b29150ce61c290b5606fec0a5a1e249589dd65982069a861f6c39346187f8a0bb1a7aeb31d5b766c2aeb88f6104c8b9ce72eb8701585b7bb736402f925b404a8ef7cbc7ebf2cc2250bf08e3a0168b9abe070bec1579d7ada40e033d43077e3ad636d1f1d511d9935e0c2c5c916ed9456b5aba20d2c9e06713a2b2a18a6a066fc88e1fa0cac0ecece833f9d94a489c9e88bc781cd949c36c9956f64df1c9becf7642e3a8f0ea9df5c28236a318590a243c21290636195df81eac5957e911b1a2a56a5adcfd09e03b037429df89e6e3c2f3a7596c98e992fd583b1f6a5730e1442499bd1a0148c8d6708ec1502714633da40ea5dfda27083099c63a9ee95269672607cb63db9ba78e9a1d203b7a36a70d3fd6a6b220a11559687a22dfe7a03901ba69a05f9730b59229a2f6ec9f1ae82b6a72367505e4121a3c34793b3c174a3020be757a979386516b1cc14b411e7738c2fc8b8588e597b13c76fe64ad61efb052d096a2f23a4e1c32fabd8bb6ca479248ce243846106f544f121c6455496e96f4e5b80cb120c471b9b9c5ad14eaf0703f3afe0bc6d138ff7b5162201ffe63634e0c80fea68312e4780d99cbc49651f59f3fa1e6a7e36cec658d83ae0be9a481785746be9c259037e87d1e6bc2e68ea42d2e1df34f17670764f6f3fb584112114985abe277b58f3292c13ba94bd380d2d47d0d75749d42c1b520bbfb12faa21a465fcd1e66af1fc8414212ac21f9e2c789944b973ec5f49bbde62284074222d28bc180219db376c7926fa5f2ec0bf422eb7f5e6ab13f34853790fce584a578fb2be09f5ae194c406049b9f9b99742d9e59052316e38d8c309847b42d029566238d36c3de4fe7a8b3dc99d8a4ba633dad850b07219dc37dc18d529a7daef5b611042224fdacba3a0af8d3a5d6dacf36c3563db07b8e98dd0850621d6501da885b7d8cf5f99fd783976d320d79c28f263626a701301e7f14a40f4c5d8115c9ced7b8612816733bb6472182386365e122d1b8b892072b549901649ad8028eff9a74e3d74ca0a269dec98edc55fa2c59d8bc48bc2b641ddaa6b002de2fd3730f47cbd7b237abc6e6f462c1866e100dd20c797cc1e57521286ddb3afe87cf8ce3ec36e3fede6235cf45c4eb821fd7dd1146d6d8daef728df8e4b242e400e852b7fd6e5e2845df51ba4a460d7e1c2726634ebbe97bad0d103b24e397027a00d319ece199b8a85098d60a654ae268aade236ea2d12f5f2cd28804cb86f4fef09b4ae868ad2e0f3678fab4830190ed0903fd833f01db83fb12aeedc11e02ed1470d5cfc4b70924f3ef1e3b334f50eeda4815690e84336782572e302242c89a50d2189f0c897d5fa28fb74a228ae30b813d1286d50f9e061be052bc2326192bc4a1436a4883d02718e1e25efe8a80c6cc775fcecd1f62b9410b4cff5dd13ace15a37eb10cd2b22779f34d3560b7c30b1d155dc135e3b6697c6aa698b4d0212916f9c46fc4917ae01246b3809eb2e501ab30d29384820a15c4a0c506a1a72bb4e24ce53259a672916caf7b40bfa0c4839223c27f46787760f0e4582f66aba9406501e04f15dad249001f890b47c6d69a11088fcc25081532269fe1bc5ebfcaeed1abae16e410eb7b9edea4b908b9328ff7f34bc8a979873718f6e70eb40f57df555081708a63ca89e73df2295ad03599d88b53bc2c8e9e19e8a5d44263ba48c045c8f376d59228e0cf549f6779613ccd03599b5704ebdd678f436707f250965af1571553224aaf80c0d112451713a9fa2f2838f5a433fe79b81f3060c980f2850e5cab8ade9514b49363221639a420d0e2acd4f1db509220a394f0e6239601ea5bd9350fa834bbd9ba1bebe2b11c52843a61186841d3d22b5e091bea95d987ccc4058d369968cfe462eace3ac6955911f0a11976250e9fdbc3e6faa6af57f3ffd368144ef358f73848fd6f20efb4917d28f8bb551e7e61c20814c9a41245f523f36cf4a34f2f515f1b30a2b531b99bc4378836c33e45346e2525b027c56f5362251a3faf55ccab9244164716d4ccc8b4716e59fa5f9788230dc8da00fdb6c236449f83e51cd13218ea7c27effe38cc67793b9acbc4638ae4628544b9de8ebf291198933bcd4037398c507adcd6612de6d8dc2a8cce52205202fda76f21851767b2427126df9726ebd0434b9438602f1a3ade89914aebe119485b48a80471237b09a1962c0b213c3c0321350bde3420649403906511c24bca715ba30c85f02c030621e0232ae620408e7366622ed961327acf24784f5152af3426b39b5e4f19d35c086200e2ccf07e6802672cd14dd747662b8f6f50b0ff3021b1649af962dd5cb4d33c3c2600068ddd0a9b905f8b04683440fddded5345cecaf6ed3798f95a1a46dc318e3ab5711595c7fd74254073fe4a6b12e9dd07e9b7d283af5ac4d2144be16ae5183f50507b37c59da04720131f2ff5122af2232426b7c30defa2d65510fa8f47a6d5f14dddad6ebb1a3d88caaaff9d9e20cef776449e883187acb3d79ecdd38ae93ef8abf257bae15eb97ab3f8712427ae1ab0dd95d7eb579156f8ce01b1d3eef83ba0ca91aa4b01fc0e8045c8d2e9eeb5eb70f3f91c2890ccc64a7bba8b8919940499d2e3db1b62059a1c1dc081f2e5b3d240e805d6bd83818fd812299577370c43341e9cd49f711f1761aa50d9159c5d10102954afb820ad3977597c206c5ce15093744c2beb582cb7a3c1b41954a705e762790c78e60f7fbc6af2b18e93f7dc37ee36388627ac04120cef317ad0b1a8052f30a9cc9c827caaeff8892f15c97830bf8acf09d26a3536f795b1cc2e9882048b0f822d5fd141fb9ce9ba1c3dc12a37412a231dc0d43b9c6c4f2f461d5da5046527c7cc73138376e9b20cb006218912e6acb3e2b7ca22ac4ce4083bf5ad022085446696fee410c4bae905694dd8aca8b1441c5f3e1123e7c16d51ade484ecc50cfbdeff24f50e3f5a8d6368ca4cd30f8bc16f59a879206f3d1f06a83964e0e1762632001e3c7b47fca105ae41479405c2d8af40a4cb4c91af709f04ac9b303dc31c3867705edad89b30c7c11d4a43d762fdb94176241a9f0ed57231f3d0efa16c2487574ca9d130287eb67e6d3e6f8565bdb0c0d44df43e1c0bb518a8ef9341a5fafba1a41779bac1cd0654ff1be3277c3aa228af50b4711c44080ecda033f23a857bd0707bf4d87a860c8916ac866a15dc3f97f8eb0c66c002ed056f06cb9cb84e1fe8d917598b4800ac033a5533e4230c5d854be1f7156c06623c6068ca30c16064b259c1bbf6f1bedc291d7b35ff83936c9103375c736215c23b06019653198b8578b6075f99945dbe8c9f5ba7e7d9c3ea1fcd2e47847fe3baa53fe300e7487ce7f23d4c3f589a2e8cc36ac6dcb10a90595cde30efaa117bdf8b5442525b9e5b7407dfc7aec4cd2c6093423c79ebeb6eb9b9d1044d149d3951387c9894c998620fcf868fc3441c1ebb224e6acbbd1ce82010a3212e5335a2da0fb85b4c6beea4210e81f2762bb5485df0d08588cb556a0ea3a7c3a9d211eb63e8c6a4ca8d364c557c791581c3edc60593926a09d56cf73c1ae1fee92a2d252d2316ee06cb3a5e4161ef295dab91512ff1c48d2d161a83827ecdc4ddf6960137f8c6f5bbbb1a6f41ce82b8f94716ee03267f14034ecf1d316f4a7bb1461e6eaacff2111cbf10059fd748821cdbbceac659a73c6c0876e008b0a001836fd860c4cc5fe9ef7e7c614fede49f6b1985d70bdf98bd104d99a51cf3cf01374ee48cb80a11eeeb698110b5c2a0a0c87a4b73388e7a556eed13648e98bcd23072bb27dd7694b3344577f07ee9290b2e4ec62d1322ca4cf0091ee2167c10fd2c0bde344fe91206793e4180467b1b86e0d32a5805bc254a6df8a1cc3b7b0af76951650627b24af71cb0df512a47b87b7a960da0d0195b61fcf10cb462754b98f93d0c1368c89d4b9aae382a5e3a110ebb0b133c1ca95e0b9b72a9414a5ff93d0e3d45d75c49eda0403eb2582e47191cf83d73d79a037d1f16612454214e1c67202c3ebe92a9b046261523bf5380cf18b279731e7510d0e215f8c459ecb224ba3575e0dbbe4f3214e34af0216a453daac63d100a9c4a0fac5987ce78c6788de51a7170cc7b4b915688fa88c2c4a88fbe041b832315a63ff1ba0a4c7f1c0eabd2f6c003390f4e3ce76f9f2de1137f801efb6f43f3a3e67cd4337b30e5be8f6085aaeda67c098081f3866ae9448238ffb79deb79e10ddc99bb591cad9552461010fa4f9e5b0ae60cb50d78d26191da6b31ae98f4a2051714a194735a74a53125c6b408c2a8cc0a5e52e51b0fc4e9108636c213fa7af17135011750a318eba568c834df544016a8f77dd5e4907a78a4430236a55c9009667b8935a0326287dab23ce65cca5185bbbe5a6d93622c41452546336e4a4712a3fd0866d0eafaad3f728b3a05dfcf6633cae62b494cf9c3daddb4fedb5a009c98ea6cf979129f5e2d9063cae6af300f0a8443fd25b414e4f5404a9a019d636f365866a0bd716794c933cf7d7293eb5b09694d7245e23ee1d314223d86abab9a2cb294310eb41137c836f60228e5d24930827106c16f82fdf1c69716e4ba0b3d692de91ed517ca5b28a6c74b74a8e0c823de0db44010e62eaed6b0fd756bedac58cdc0cce2b20fa05b2c519db6867c4574a42a41a8a9d903b3534eb62301f258c22d7128a5319d5c73cfc0b420834ea219bec8f1a6a68a0ec93a63a8e21c16c766037c208948beb268c83b9133b9af9c432c755cac7845ca47ba5425d583ed7d8ca9bffe6e04febcc5b374f02533ea6bb2d15ecf4da2f10d8c01ad4a88a7bad6daecd5a66ba4e0f13c38c61afaf3100a48af17026be56922f5362e717a893816a6d6cbd504c7fbd2c068b5a3aa5d2f2bb2bc1d1840b0d81a420399ddec97170378d18b60a0323bc60579e881932580639ce3bfb6e4cb8631716fe41803326b0e65e235cbcd513dddca8182a9046a8b5e74fef09875750eaf38bf991cb3800f3e3e611e5a3933155514d2b41ab50138ec0be376befe13a5669026edbeb216e189db794969b92e0af37e125eb51319332ab9cee6029465395ef5c2a26fb912d8f0ae4ac9b31cb265de939449ef54e4c565fbf3d7ed769f80174394688fe6a06f3c977ac5e3bdba9f222c0e67116b2b59156217901a27643d93ab3aaab02e42d1a6de7148585fc464028d58f726b21344d196aba5f18ecc1731e99f4a470494d99ecfc4566fe9df8e2f6b7d1ae10a254db2f259c27ac973b29dd51a917faeb0656d90913b0fe5d7a3c5b3002c39d0937c1f0168415f24bcd9c361ceed1785400598519bb249886f80d0c4025a8fa5800ee55cbff79c9df684c5ba46c0faae04896a8a4297dc71b2e30fe8fdc89a5a744e1caac32b915630a11f5fa857823b7f9592b2ad01a3fa8afa79c6bbd12786cad632a206797fdef9865c1d934f8fb3087099f8e9d78748654747cfdab18195056f8dd02fce06b6958d8a1c6914699797638e0dbc1bdd8371f04a55a99187ec54bc016424df0a91f4bf3b448939a5f06ca8931974d904de36be11e24b374e5b4951c311d87cfda86a52a9e1e77b3b574d27217194f9e14a0ae3c8c6cd09e56b049e6fe6400dee422af6d65d8fd055dc1237d0cc3997b0395a90b8af31a8bd48bab28f787835d64c0dd099cd9bea5d0fe1b9e4c021aa6a2cea4c1f7517781fed540809d3a468211c30aa12dd495308fc9044979c087669b4c537bf4300b970469481cf33b8322cce8280b1b621d354cdd4184d2320bb8f1f9a47d6ac7d05300ca9c7d39645018f9406afad6d370c70b5acc726b51152d2b1578ebac3028892c8347ea8a5c3d2072373b2b21167051867491ff86ff48de7a2d7ea7303d66224a497c8efde42a55242f285342e11a3b10fd9c51790c392ee5ae2ee38592564fb86f9c0e10e13b368cecdf5cb27abdba24d1c41d071c256900d7a2a7e14f35b985c385b387485c2a0ede9171436a3c6f1095b27412b9428a394372c2fcdb1672c61ad70ab11e62c8b736f3a30587e1914a6a6eaea740c45aed230b428d1d23dfd853e1a290f540988cdbc6f455b66444a28914c747d9a963041487c0187c1c1b9e54afef856430cfcc6cd09ec0296c1fd63883690a43d2c6c634d9623fa482336c86e767fc563a56e1a08348a4d4433141a744fff8b5dfb2a222e1cbead10374aa6b5c09c621c95d3d60cfc466ddb326fbefe5bd447b98cc95c15ba0f1b7f2b8f01bd45968112d9b806165adbdd9296215d6898240e71db522c05a8e45a749ad2fe33d0579363bee81cde83638e7d8393860cda0ac915e25da03fcf51280bfe12f38785e4a8f266f35fd387498185f8aac2e762f30a23f4c46904f89b38a84c2f865c4365bc6392e349514d3696c6445d300a363741df73c8d6bdb85c01da7d4aa3cd87e9c128c1da963f644bd041fa69caacb0128a4dc3f11d074a19661c104ad66614d4d9b909fe65964d14aa3a819d83b1d830266ef6d38f72ec17b48730c500ff8156baa89d3d1d9c1c2242a430dc7afb802b53c86707fa0349c76925851e9e5f0ee94a1a90d0533dd8d8b6194baa4c045e8b0b18081a2ab3974a1aaf6ed4aa60759d7d0eb1c0728abbed2ef0c1d02a49017a1079250bb69486b416e786544d1e47990bcf71af5bdf3c1e8174c6fdb813b63b285403a641aae0b07d8da190b8c9ee397cee5fd007614851d3d2b7ae9c85b459c6113ee2debc0ef7a2817210587af26f1dd61b6eef953247830c76ba0462068a9b1a61cf141b77c7bf6f759b92f2455a653eb70a327e4d53dfd5ebe63ec0e03346e42ce882a0b6095ffcb58e9d2554478265534ad461e37dfa77a819d10e96fd82a2d6e06cfce7ec9e2742f790def169297d2b802149e75a08ea9d1651882ff55bb00a4386be7784d89aeb2fe36ae12995b13939998cb8b2e74a69ee433b450c914eecb5139afcf1a4480e6105cd21973baf81d28e75a42ebf464ace214a7949fd893b526d681c2a8d6c22c2c5b34068fbd2a1a7d228c719db052de518405b313c78dbbe1614e3d7ac24939d24ce4094a7fe3a4cbad2e19fc701d7c63289a2598a934def10ffafd247eb039aa7c4658b8bdd6478527316149b5da8b2af41a5492923a5dbe1748fbd69a7a9101f06ee62c081c3aff8eb2ad87dd919853b939e5a4b58c8d4a08c8ea6caf9aff75d4f2827c88b14c3c7c56575770d8ed2660f7c62813454828b96878b43c46a5b2a27bad018abebe9fa7c77fb7893e4c9c71aa1d95a7f5ba9b9c14a5dcb785a18a41f6e801fdd2425aba8099091892af4b5beb7dff39d8ab85d89db87871e09836f78d02679bb8af70333d3230c09881777f4be66b633a55137b4d1a1cff05ed846871e0e1a27781a5eaf01d22e841e881539ef3aba434efef091d3ffb4d499ef07f57c3b6436566e27bd52df34c1c90caa1586010b939051c6a2c26bd462050f4b1f45606085082fff7bbf1480b19911af71801eafd49919b38119a581c5672e3da361e11330c23be2b506bdef6bbf265391501cd63044a3c2992ada7001d4a4efaf80f5d9df65d02b874f3f622cd888700dbdafe214fd414ece95304e58bda70d860ddef26d5fc02b1e3b1ad26538940a65a09e1e5e306904106035053d83d92981e7c10436d066d12814a324734993797932dbec9348d8a3c1d47efd08deef0f2feea8c46b5a603ae84b91c7811ebadca3703c55d973a91e81d7cb3f212d1424e0b471880a4403bb018c4a5bc3d26a4646b91948e32b0d0c176572a9dda8f2b46b4fe61aba58545bd8c85ae4c5279bd4578db77a21ae77bdcf845be816f8be65ec5534376941421b4ff45311cac7a0417fa5247086a4e334d4d5bf2ac4f9effaba24918e4e44492d77676ec21c9785e5f860cf0b2f276e767120032badedfae1284b857fabf681f4ffbb3622bcd66667bf8f71c4ea7794bbe4bdf4e6489d9066ce2a79b9268aea46e80c496cef2938e6290c4cc1b1b893e1383e01dd5b351868d9e6f8257e8903b0f4592aade8b13fe23d842ed52c3c557f1ca1bbb9c4675dd81a8925fa0dbcb10e1a823e6ad4bfbafe7b59d6886f7e44bf15a9b30ed5370fe6c2ac6cce310f07e759140d79e28dd9a1fc73030509abf274e5eda474b5f8392e9e82bc36e251913e0f5c486f6a92fcc03668e09455d78a8149d12b9b5354862a43a3de804562f953c38bca13780dc49be93c70e4c09c22ae1f9cb83afd88eb34b43a9de4d4c7998d878b8313f5433090f13539927ab982370b5e6bad30aa1f99037d650555d401a6e869da500b5acc5a3d41203ab974d94ec06c129875c2407490cba3e8c6e45654ed2f2c4bdb55a2f6059d20501c16d9751573293bd2dc067a97435feab151880643c3c588b781ed58a63666be562c1a9551dc09243db5f419222d17c086f02e1e49c79b10446080a5282b1be99a5fff411c59657d923c778add45cca198301647fdc85bfe483233b8944489ea6d238c3506309c57372c22c1f7513338238b70e138455a109c412ce9b68a85b7663b9b2681e10875717d2d24aaf2deffb7cbf04b2b629aaae7354925c595e627ecf06ff880679c4a55103f549f80102fb7df80a6fe5b964f54f9a9ef2a46a35126014edc53a21e0de85c492dac630628c8298cd5d848e369f1db4b1bf4a684ffa6dfd77ae4e97ed96a8542aa9c3e467886fa81d13be30674e51a1b3ef90c822daa1382e0b8dc3f78a7ced51320683b4dd41afe50cd611d870574ad7206b05fb7885f5e53b58737e50d33142f6cc42481e0e8a663b2e5623ab926f0ffdeb6d26f7a6cb32d53cd41780d7bfb42cc5b2e737121fde26048984b03bd1e7cc2c1c980dbc9254358bfdadddad3cf9ea908fb4e8dbe610228ef7945c0823834bb33046fe11ecbf96f0890e72ba9337b2183c32abc4f6d8fd37c4455a6fd53a926ef3b5de169b52e8054a09825eb00db573d28cf5153405886ec37588fd815492de1a119f49de8c9c8e1fa92647af5085b26e033a555c76ddd2fca82da19e2097c6202c1fa03d0bae54498809f8e34239aeccc544fc58d662c34d09770a6b13421555a711b7c09255c60f02483e150c5c32405c3af12773816800d04f17c0d61611f29da5d2a06b6c2060aa8332994c399c3907a57b7632a42fb7babfec50b3fa6f958de6a53842799e1412a52f7d2368fb2e13cc335aeef8c25906dce6bf50cbf967035906623eb24cbc913731d2a1c6bf17242a1960a8f982bb47a4b07924eba5eae212ee8311ac144d5f37c9fca0a485565e18ba1b088a10bfe691021fe624d2888992c0a1bd3faf8a4f3ec85a9a06a2ca4282881acf4d3f96c2e77538f4db8a9b61ab9d1b8001b917f540dba8b2f1d51e0f8614c7785698e8d3484004ebfbdc763921b720780124e876fba631a35ddbd2e9e8d980e24addb94374f5fa5252b018ebb6787840ae6f1a2ff6d4ea118ed902924acd82251087f5a52ce9e63954b0f7963e99f169c95a82d4901644f025fc1425673c02f648caa7de8942641ea80b92a2d45d88b0c7f2ce993f037fa58921caa5d052517f120994dee841d8e90f01a4276eaa613d98a64a68128719715e05e5546bc60c047913186f9ae15a4fe0040fa6943d015495ec08b17b2eb20c18ce3b279d06a0976f14ee2ff91f1bb36447c54553c384e5d0c82b36cb4d44fd00a1f6d19a7d852638a5a919562bd74634ed8de4d1a054835b363ad503a08f5ca1c8e660f7882edee207feb0021ba045f8f1ef542a7ff3cf744c5770ecd16544be029191376bdc2c44b5303a6acbe6c16d57c867532b4d669a9eda34085b73f6afe660502926d784997651184f153eab5cd8047b6f8b9171db0342bda4e2d7bc96bb440b5e66acf246241872a5b058a52ee5676c9c8a88fe0b201baa609e2729baf49602dfc5f69f389d6802bae66688660056944e4b3571167455490a94e6be19afa3310c3ff0f7b06145b951c5904c6f66bf491d13a6c73ec680039fb847c2ec5542b80fa4b20b49b104a0738585dc1e82d6ab3c538c192caea5f294694708a52e7a64cbdbd05631b9ccb8c916f46b725d864d1c942bc638d775e1eb5eb1603dbc85b7cb069153c4fce39115bd7041d1d45eeecc7e5df631b1e01f0b0d67b3140c09f9da91c320d13ba946ddbe1d46e13238dc47d7513a38f0350021c95dade483e69a8b950e1701c7c665af92b36fbbaad8b1cd8114880443b03a69666da42361425d951a1e604cab4c488949a8dc4162b390c3da39418b19fd839d6620047d22582462e0f67d8c53a241155828f2828248eb2b73fb8d6a329f361bef0714ae685d5d7cbd67ae4d08c1e0115828f24aef2c745252c4eb2b76b7243235ad98c15fca4c98465122d91b02980c25240f52c464cda14416978ae90a00fa8e15c55ec18e7779df81a14565ab81ec712beb1e0516461c4cfe13f57c8bd2f552ea3753f6af4294b9d8f98b236151f3a9ced565c95b12af16cb0d61142c774a441793d3d44448537653d444372b9aa01429a33169ca4bad9f0db99c3a80a856d7c08d8065e9a42554943ecc9061c1dc636657abb2dc009b0038a4070774ca45f2cbddcd1ea6b8b156fbf1171784cf6fd46fd4f934929cfacb90f1a7a0403ac011ee78e88383b4f92c561500cad97baf59bb32735c218ef2baa09295c87cf9c002825b8e95ef60799a6a947b045d32410fb75138c70e7558513d9a7b94ac13f0aa63b4fbfa05484fbe7102dddaa4bd72416cc69641217b413b46c6597d3eb72e2401ffe80f98c562d7da751fd9ab84b8b0ca6c3c53ee09ad1cf0b5a734c14a2056c901206e97376331214bbe5bdf91e5955e7974963379482a83b6b4c63c02586775c06384be876980a8233db75b3a146c8fb97f8ee1c3b657f0769f8aedd0d1e28029562fc4f1b20770c4fd028c4bfd132715a2aa8d68006b135ad7cafba801a53800718f4e7804462b125d80930f141159512f276622cf55fd592c06551c4e85a31d51b059fc0879730a70bbcf362d430f3fc20dde6be140a2c711a75b2602dbef84d25260489db5b3e6002f34743f4367f6541ed994009d660be00238ba4a22076ba994c7c1806eb383fea291bd491f31e19422998b3d4276d302801a023a203c8ce91920161bf6b0d4930b1d408075cc0750564b22a7dc1d5f9860ad12a398ae43df821e01116a7d92a421da008473b23305e1819bd03654e61041f37086018896ca59f9edeb4330ec16c95bc72cb3eecd1ccfdb6dd7e1f43d0741aaf264210dc98ea80da625ba62dba112766d5188b8949ee2890588c83c5e3e36c594491a9826e5ae7d3a350230a45a0dcca52a415e905608d03d78e5f38e06e4d7027687b921b730ad3909295a764897ad2c884f1b4a61b931ed03806388a11e2637734a3d409c8c273047af48049ab39120d4f4b943be1c342032f47df8fdce1a00fe223189958e9aaa3e406c91279bd9f727beb4ba64d757c1038f1fde43cac27174fa0ef8ba0c1351c3eda8a870ef291b6da7e6918a37f9172855640953295f6a5e1c1200a4ddfded5f06e333bc02e94f2df9016084275549429da16f08bf686b632f800073441dba5b25218a95a64044457b5669af684f7c61c5e9ad1918f2ebb758f677e95f5274faa4eb4a5589205df935ad4eb30bca5353448f46522bb4cd6b73b7c1922e986a397e281c7fcbf23ed7566c13eefc0e293b741cb724fc50afd7ca092541d29602c51e289de3611f0001413300592b845147ce0c064032a19c8a4d2a6c9930454f01f3d40cd97a4c5f03657f70f42d60223aca318881a1e396ddc79eb36a89937dc111688a6cfc83d9d9f4ab0c244b35787593bbfaf4efa1169f5a0f268744618cd054afe27b02e337a7e91fc0eb0c44d0954ea5962d2128234e2e5c705ad25bb665a8f5efd734d2742e9495dc7efc3779b899226d7099a73fb1f62af4d4316ffd697dc6b32d973686825ac7d7b9187c6352253581f3891d57bde4b4354a64f71838c7390f5acb7a911c7db23988c7f09fd4d87496e15ee74e69cb46d808b417abeeec7f75ca1c2323e73c361c082a55d38d35cf8dde8e0707b0a02c07122c19e4434f190eaaae180825d027680f1eaec4fa891582fc9f68519ef7c6e7b50acbb077f456a00a1b92603712abcf6590cf7a5ec80cfc15e172f2ba4452e13b2c3b61eb3405dafa64d71a467cbcc814b78af114024d96557ea8373f077e3e04509fa69473f086ee89d1720353fa441478a6ebacc21c86d4c11db6ea914bd235377e96caae9733cc43445a3a9eadff5f57e748f531142fc649bfeb5df867884e47be2e34af9aed43df99b00812fbae3ef04d83788720bbd7fc6f80d4ce57dfc97273c30ea4011ff600a1faa203292bcbc7f59365a8a95cce5e9f42ea41933a1ca72d8db8960f204173f51982a85bb10061b4304497b56c84c548de60688ebd651d8625860c5077cf26cf2c24ecda4e97875cec8cea592ec24076cd56d76faf382c827b6c687b7873939fbdcc036cb3f97341314740c27dd272a8a43ca6cf52abddc423749a3b10385423c7acdee2afd7a4f1a21dfa591239929a3e0a8c7d45678d40a167064946913579884b2e62f42f2fcff68fffd2cf05fcac7c41cbe85a0178c1ae667035ed4f095d14f9411230329b487b5b349c367d4cdb30f38a67277c778d4d67f42b8a4ec9b34435e86a313df3f5f30d0273bd8e4aafc5943b17010f363879ee3c94d3fc39f72c27ff53580de411c5b686a16aaaf0d3eacf61552a5380c3d2e97c1de9c0edc0fd02ff6dc2e212ba5c20cf78a4a001a39c589f1e25bc0bb506e999989b6e19c44b16f98d86179da0b9d9f91f5e36f589c8e272618b20ce4361178f5cbe15b14a8a9e8cf260194701a2658016b96160ce9858aa1313d0b1425611a94d2089a55611b4b576033ce44fa6c8767fced849076fa1d7adc48b4fc062b99d3900ed9510521abf3e9f53cbd43059249446c3c2d589130b9ebe79c423d688b94f91b59cc8c1c4faadb1a5cbbb77fbf8fac209bcdb850fee7faf44df5dd197af44a8cae568749e749b3fc32312d9ddff3f01635423f677b8ca291ee28bd4a8741249fbb9d30c5709b17297c4ae18efc0fad0f00db08392100cce618f47c2356166ba18149ad989c7d043189c18f553a89ebe312f0211f2685408fcf36cb08b03d4943328cffd7aa3aa5054240089ac038e304f4ea99388f5074472e99582611866834a951ec5c8dd683f5f0424532f0bbec7690de72f28d9b9888626ed1eb038ac79e4bf7e1b976c6b28d1934ec0f2dbda7616c0c0f12086fcc5619ed9ab59b241474a0244ea76ecd83c0cba45cba45693e6c418ba3878bafec30d0c07b00bd0db7207a12c80623d08839f60f4a390e020a93c1d9b43c3e493bb0a76142cad280ad64ac0dfc1b169878fb7a221862728e897b8bcf5327d77f70006270acba3cd14674541fcd1bb64d655ac373c4af7ae7a324b53e1fe100c51d82986e954f26959687c9d2e67f2295f788673419be50b4fda3748fbfea585dfc2643c74833a5e441847e9ed36a3e79c20fd2bf6f8e38017c503ee7407c079fed559c7c4a940af76e8f8c087cd8f8d97ce2d0fb3ff8f9be6261c3f2f86359a3626d84dcadb16a05b55f1a892285611aa3e91082898f75d873cf16831914bf35a92fd1685cd0acde5de7f1f289cc13e3d346ad212b9343060719932b8ece56014855bd20e32533299ac63ff253d4e74626b1457f2a5990236544c41f370d055a30906ab6c74cf09da6762d96057c600d199e5d9f8c37b5db67c12c3775b1688c6e48d170c4254a0b013e0ab6d8a86fe23282eac0cd1e43859763f83f045e2e8f0ff51c39dcc4ecac6f945f8afbbefc06a43158999f4935466c8ed89c2587a0157e2d26c4473ede6292322e6e11810d44cad0fb63234c6424086739ebcdaac4ceec56b4a25408a15b132b27cb03666960c0877470a136d0d0540354debadaba2156f8ce8ab2711230d85b20bc133374c361be8c6b1092805c7c19dd2089410e7e86727bad81b97bba4fb6da6439b287ade143be60c0bc1d92205afa6d3e7b7a34f87d742eb182f60b9db5ee283eb31afb1b8354dd723f5b9a7f5c2242e8c705d5b32b696e5f2812b5f9509474f48ebe5bd95a76bf4538b4351e736428e9025d0101ddf28d57496ea12e4cf1d603643a6ae3d11048acd2f56f52464bf1fb49a313396fde52fdb8abea8510663a4a2c7a1e72a3298235f41e23ccb66bfaf925f979080c73320bccfb8bfb5435b647b2cbe7fb61facb9cae926a48cc9de11cfe4fed76cbb1bd3632f4893fa480158b556facb408f77b6893188de464046ac00c9566d8ae958a6b8fa0c1b3a66453e13854c85321f389040cd367035a48855aea86695cbca1fc67ee9dab8c80c359472546d884f0a6b8a9309ce13bdb59e639d4e8efb78d3bd9cc27cf746d6eda11aa9f2e0b68782e0faa27dd31db38eb49c238de771b457c38892ed42fc6c1b4d163619f60e671ce65f7cc323b3072f8d68db1a9e5ba61e42a3133c8e41aaf4d8d714e2d74b1dc8f901bf367d4b3b74a9f0042bcd42658c3a596e8a900f61ee10294a625ca1728a94273ac3faf8f261bcf65d3ef7dadded34b246624b82a4579d41fcd6f898388de0e51ee075143e2eb00519811718306cff5d57565c78909c58819360fa8b4611ea61c1171e1558206c1e4e77731a143cf0bc1302c9d1f2beadb1e2b66ea2a45e812a8aef13c9ed5a55a24521249786ef0ee1e074c91dc037a25fec35d6c92451e5aaebb2fc3341062899666ca31968e7c6c9094d72ba9829e0cabbab17324ee416cd852c5405909abfec57375f8c36c5eff2d511ee222a7b4438ed249f348a68b5560339d1ebd4eed9088010b9120b2e9bf8ff2534d166daac503e95828e45c1d5a7c57026ddd122693d749d7224a9adbc0eda0c321574a5c20b17ef5bad6a7c292314b4f413323bcfcd15d4c3f014989bad19d368020dd9b02eb83f5975c68a17612d9f9af8fcc07d6ac1b555e32ab1bd450b02fddfde09c94d26f6de82030cbf2c2c0b5825dca086b48daf0f234e5c4a465fdb071b99808127426142cc1c11a1b5a565945a389855d07e98b1e553acbd8d1c29b346835c440731024d4c6c64990ab4402814beb87d59e75102086217088f1d23cde8d549cb480998436acc40968255722f2c5dc535be8d113ae621ff1bb1a8b13576d09314845ec528d98f0557c5f814898d6f2ecb238b3009f7181ed9cae1d5e15178ab2c78f04c17b88a9ff7ef794558c63370ca7865b91a93b8a60ab259cf2792211ba4f6acce88e73dbc820eed992da8d365d004f35f2e8660701821be38c107c5493b16fca105a15ac217959b39b83793578046e30ee6514d0971ee35d832fc38e96aebef7e1967f9b3553df71e230dbe259160f51cdc9a0c55c60f63b4ca39391d6fa9901b535d20837c6de112023b8c7121b5635c8d8b5ec556292bc7a2b85c8e69a1192bc4c3f705862b416530b8149c63ffed98fc8dff876fa21526450901bafe1b588f2e80d5022a7b99b31b4b0fead3fa32a70b601cb2eaf3b205a9fcc48cf6adcb370a20f88cc016741338aaa98e9d001142ac0ee31f568b8f9268699f64313b22cb4f7c7794f9f55595d863399710c8f68400dc4c4f4d18ae0505ae9524608e1893370e4eb2a5c74cff7729faf308c1e06d1180de3d48b982a38d43b8bfdc17ecd4c7349a329b7e64e7b29853f48551ebbe43c869274804bb2c6529d6966f106a2e1606a5c303eee0a47bfa3aaaccdc1cbd0f3af68b14b19f4189946ded51cc35f191a546d82cd09fd1b7236d8598088a5d663efc226166e76688fe2e60a1de7c108f07332c1ef84ea3e0406c2f3ff8399c9dc6ed681856a48c15459a4732e8221011e5fbdc608ce420fb3c4ea8f87779c2e8dbe4bfb31f72f6e1ec75aa410746e01c1065c13b6545c540bd87cc3527f1726ae10ccab059ee9829c7bb90da6ec3d6b2ca8a2a52915d2af6c932f5243d93d6867740b289e480641120d7a0df971017d5a6e74695c6e2dc515c70d1e0db18d4bbe1958e5b627a6592f5f88bdd124f35628b3825d26ad30c058746748d870a6a9442fdfb7b29520a2626bf89a4257f83df77b9a5dac980ff0b81f450a3340aa7f484a87a949e5ee6c14321e0f1f533bfd759e4ed3f7962a653a811439460ba243e353caa77262979d0a3019e105ec7ed053112351917246045521c8e408bb766310240afe616cc6769c9b7180ee194b0f8cd5046a6895faded2c84187f8fcbefa9e53d88594bef08173593a8b24d62715f54621b56e9c5de54c4a87ca48cda10c13154a9682d40702c104f55890ab034d14ffbdd6017ddc4c2bea3d4a03803930cc1c157e9a959b3dd9898c77efb09f4f20661013ef1b1c7f4a033a004ea2043078e0218cd9e60623f7584a8302eb546653a3ae78e1bf6c8917878d66ce75033caf18bf0891d5f4fecc1a69d6e55fe1641132989dc91e6c095ce93d9a527f34cccab22b82716bebc09740e51ed84ccaef1c122e9f970eae1a94f769f46b342befa5e7424307605205919ea90e1f7a0dbf3a4d28e65f1d3ca04c957129c64e3b6420463c8d4dc6800c830d3bcb963ed535a30610c7a71f7c51687c0f092576598d8931004b44f01063aa77c0c1f33f75ebb6307ea59b1727877e8c104ea33a29cf164b4b5a0205d56907bf6db7f5ee45fcdd79d2f15eeceee2db3e458841ab122a049578e6c31c69b35a350db543d39d75af890427e30914f3d5c540da34dfb6393218fd76203f2c614491764932ec90a18c138b2670258320479bb0d1cd606e345532564ae953ecef89e7796706ba1a4549be570cce8f6c281a0805e570c74df88cc77146790cc9f709767b52329c7dfc27ddc31bf52f1524c3dd8db6f99dc02f08e6300403c7ae1d2246ed46d35f4e60bbe8887531772f4e4c3f953add4f1a6c0d8a3295a300912a36b00ad048981b0f4b6e2248d445d5e2fdffc0006f490f078eff3103d1b22dae8fd4d787369404c812bdc1ae8bb4f12159d87825cf56dbd22b5848559e1412804c2c1f84d5eec48d57cddf3ea3daae01538810f86995be2b22cfa180274e97d3910d05845c8ed00b2e2646d24538c94d6b57ac1142927f25ee54ddd823d2bbf2a0c1e35b4b485c85fe4bf0e6ab2438084cbe38cf3e5892ab8f4b42e0d83ca9996de012fc136086710949bd892e3ff20f4b2fec76c1b26fdbf3e8529ff53fa6f584133fe492df2a685058b2f12302d6690cbe3c23857c4e407d85e47f9e66c2ac231be138298b39710847e9ca338f5c57ced675f4d7a0891842bbb8c2a7b3d534b01093ab88562d684a6242417f3b17db7b0d9dfbc3559a514aaddf78455d741900249d7738a7b12a5395f3055e01c0cf7c1f9218747092f0ff753b03cad2f6a2358a7dde355948485d20f011f93ca34154a93a6e2f1e79a7a4f976d1ad1b84318f49ff4b4ee383342456ff302cb26e58e70a14acd3913f30b6d3124903565e9577133140f09893e952374c3ad13c5b49e15ea1edd8a58ce7fa4ba4d0acb9a5d2f5784bd1bcafddb4a311ffd6cc322ea93eb86f7e19c4432bea86161598280cda6c8e61965b5d53b7789525142a447570aa874a20ac0a04b67f9c222cde5418716484871559d00b0bc1296d25159f20cd1216fe0558c8a5cbc2ca91944431c4fc11e364479fb1480d6a3c4d5d5338832355283efd365433ebef58a3414931815219ffbb4107c8817b8c553648a31facb61863c3233df7d8b83546d6cdfcbc167b30114ba4436ec645606a0266c556225f2d07dccbf2398045a6bf1c45f79410ad0cb4ce7c5841962aa00bb797c60acca554687b80799153bfdb51f781f0df387fc0bc7cdb31e507d40aea310ffc08de8673cf0c0664b2d20c8dc1a1e715d0ff4f052d0d7e0be4a3aa2218f982bb1bfd4510a74a4f95dd744230d2ca617382a0a24627099f39c08d30f68c5d8309c8ba2804ff2240ad35eab9316b104953ef3963ac8c7bc9988cabd4e4b21040a181f1506706f26b9ccaaa4d41e63461849c494283d1e3396ade895aef2abba39dd3f154376fa613064361c94dc0435ad0b374395db17635bd4af95c110189f4425ec67658d59ed9fc4362cf8000f8759679689d6093514a606a812ab55a67300153ba6d37ec51c282072f1fc07c88398878c8ab3ee1e1653ef34893df1a419291362714e09951e83dd9e989f321e002e0efe044d587a665dd031e9cfe219a4efc051df94c1acb29022708c6504d89395e8cb5e14e40166e5a8e09298bee70664605e34fbee761b78e68bd939e2338955fa92085fd53de16cc22b3d8a1be709b38775108b183aa4963b0f0888600e1575f0575c42a835eed62227ce8d6db6a5f876f5a8d7c4c5ff96529a3ecd0c666613c61bdb0b6e230a716c712fc48038d99af22e3f31c110af2a84345058715d92222a5a5feae614196cc0d106de9e5c3902b3e6df150681022d8a6e827bc0cf76138376f79d358fc78a25563163239b24524aad1e595e3466534caedb154a7c8595f650eb4f355ec95d541e7383a68da882265497711c078226d8fec9a30fdd921946b8d2c60927ebf208fc63902f0ca85e72f8415ffbbaa96d4e6fb56988114a42b5a03173c963f8e34f4574830172efc258315e7d7ba92287e32d79250466471434aa322018a31e4c4f157159a34cba1e8d2ad19e82bc3b011161d95a8959da3daa9f46f2efae1129da985560644c77b5c04de98a730cef611b66ea4f8ba222722a00191b0790368a942cf73a21d4cf26f837451c013734555a84b72deaae38744a2b22405026a2d0b677aeacfef2e407a1ea07282a6f6c3e4ebe6abaf342eba52a442e342d38ecbf405cca44ce7cedd0f3af307749cb7e95dbff4c9261457e0d9c686c5f46f5fe8fe384b2d8c17a09991892b86b7494cdadcb72e78afe44e1b862d0bb8dcaa4534eab540da915518566da86e0caec121d056a0f1db1bf3acaddc9041aa768446f97043d01598d26df356c10063e30a9bf6a8a21589408ddef932809bc2cf8e8cf03db5e1c8118223bf02ac0125e0204f4f5a7cb185c8eb8f39637652265ed8be8c4007f78503f5e8ead8b9e9e0e7db02c35ca378086ee076af0492c708db609924a80e59580000a368cf888cdd38d895c0106de2c24daea3f5229936c404d3e30726f18b1caf69836146b1106b6fe428700aff6a4a06276098aeeff7e5416e8fc62b621769b47682fd37d352e72008ad95d13d6826e135d468cef1deb67fc6277909e2da0ba8f0a509a01f8857596facb6c1984ce38abc812e43c710d6d187594cf677ce40368af523822f2742c61c18c62d4819727aca2f5f1295ee4de7052fe7458db5e7a5994c9a41cf3578bd65dd63d4a280495d8221acb89e3ae0a8b315d90360631b3a759007186ea47ab328e592a70e50908553b343543de48d8ba881e0c1c7a681129a51772e6a8ed995a9bf7d60936b3fadfc8872b23dcbca4b57ad260aad8e5563a222e89b22463238f82f65ff317d420b362184b01cb5eef28a6e675145929b819c046bc7172a1a2658270774d7e8d06aa1a2b189f8a0683f9e4ca99a41ad94237b9bc7850ab29d5d64d89c9085ccd889294ec3b1d451b505a21b556ed946e4929d39cdbbe895ea2bd8dd38aae85b3c3fef9cea2ad2a2a8b729547168127c2b3b42a0c895044271607d110f4fa4e7ff52b79b206db45289e2bd21cb9204faedaa0e207175d165cf4a2441011a081180b78d1f2decc997e70144a602eff2c1bd0b6ad0135a30d55ada2b08e286800ac096c9d9838083fa2b41c0361a4ac0e069a9e46e4594a4bfae27acf7291afb32e40089fea1eadd748f817b40ad6dda229c3ec78d0696f780fda7dfb81ad7c9b1ce4683660a7db797eea7a00a1874aeef1a47c4e3f6bb74814aeb95c144815fb1edb767c5d484e4b50f3af23715868c2cb3bed0317cc27ca7143c3065e428d06933b4409fe8394f7344314f5c566aa8b55dc6823a4812d03bca708537b8bc9392ee0861cacfb8585fed01a595f619c70c080381c30034f327f75141c597ad7bd2410e2b760fa582ae820b1336ceebaf7958a57151718d57922d5df4b9264db174aaba4af2d6716449e83023e08730a66bb2e60faa7bd3c185b0ac66edd19494ae508ce3add8a944431bee1d6804f12e431b27f1725b45498fe7d834a5b47ce295e3053a1e798385799b6646ad7be246c433024b13722b7b0ffe2d1819ac408734d8e4bc780f6b0931298693363c99e68cfdad196cb56e21c4d90e0207f19701b24422d9a35742086c71d83f76c55ac016d14983a0d66c0f38b55f7e0eb3146b36c0e244396dae4bbc426b42e4052dd625d10691c1df3d645a01347330cea9da0ae47cd3d2ec79b6ef016ac1250665c0bc024f6270015a16ddb317b0ce4efabdac72332fa20c5ee1cbdb234834ff63e7f291cd75b0047b0b3d21849f1508e823e1cc36a5a771beff6f41a048affa0b1292b13308c98686b105f33ef4467a547e6b40c5e91f1e19650e9c4095373b6075118e5187494ad8cb0aa8b652578618a2cd15172314183d39c7ea3693accf65d60cd485750bbe2390b2a41bd7935f219781bb1ae312e332dc5a9551770ef3da9cc18fea40262e75c615c81478091d3d230fa78c60878c65edf35be60e32097724629e75b63fcc586a9a31c3e1d7ae733e6af452c668d1984a647add56ce19f8ceafea522f38d182dadd41c2418a82198dd36bb39c955e1df1edd34c128e6dd64482d42fedc86f092677c9a496268f6bf083f2606d04f95fabc2a9fadd5f8d1373fb75f0b5c853f23187254439c1af7f65826853a1f708a2cde091595545b5c0ecd29366ccaf67122c706ed6815d6f7204b3499c5a7fae7f12a208d46ccffeed6b839ada70682f419b7041890326122772018325dfe81ad88327eef05415d8d9aee5c79d6f072341335f192407e8aa28b2f07f7df247a6ea55578e682059b21dfc10ff9a32a656666da68660841d078563b0991af95a1ac43315d7058e306a2a30b5b453d13aeb96501356cef3b6961daf6480ccda6086abc98fa214efb568a6fa9908d7d100cd8549552194665d19688794a020069e9d1a1af2773dc8b616d7f6dcb23d3e9e16398e629d8e44408efed9ae18b72806458a61a4ca1a79b517cc676353d67faaa99f8e13a2697ea8bc4873f555eff101f48292b15611207b55ed6cf9882001fb8f45b0fc4da901e9a50241f860ca87047f8c41296b7a6ab7be206841c60a8a8d704985ec23be7bb812c33d65a894f650252498aa6b349d1d6fefeab661631ca5ff6819a32e50ab4ff81e08c63ff59876096c40423fe676dd7ea018c42658f055b0e7e131ee4a99aa05d02737505e9ac5583c7be063693744cb41bcb903ed4c9a5202a9761481b0101d0d5731e2c4131a6686105241057b44d8207ba1617c1ed41fa1ec5b4c357e9d7aa6767e0300b63466277fce2aa299aab550b32cb3892056717c000b3397eef1b2f4d7a655a7dfa1267a34b4f5c67386ddd613872c4632119aa4b0e0bfe4a949b58ce38d7b7d5f8f9574ab07175da1df8287b72286178dae578145304c210167bcbd64aafa6d9d4f01d6580feaa77dd66721d408bed1f770b04b560614ead1ec5accec125c78e1dbc1cc264e6bbc4f2b2cd05d9ab92ae8df35b53f565018f05bff7cd8bfd44e3e564b37f5dbe4b9d880ff2a2af8fdc8e2ab9e31fdd24a7b91672fd251a7d615340114dbcd5d213462147d425ba1d45bed0baa531570fd63ccb1526a84dd93ef37b28a76bdc0a55a1b40837337708dd7aa67101b38009623685a2ea30ebe65638415024537b8f6411354cb774a6ddc152d4f9bf08760772d6f82436594ffde49ddd020b381d441deaae7b340512f62cc14f17273e78cd756cc6deec2a956bfd10685d2bcb7b867e6684018a2abf82841a1eb052fcc4572df46dd474b5af72020ec6ed003a251f42eea7ed09cbda5f6e03de3847d108630fbfcd523dbe254cf60024c20bdd621f9ef752309c0c2937bd8471fbeb31cc128fdefc9a5e123158e2932dde031a7d8b3269836ea02d4f09d0cabbc752af5260ca8317d453d8c9bb3789db69a34e1f070ac4263ee9a53553c44696bd91fb595296197c2c80e8f63c1ad24b34374adccae4f79bee962e46099b14606d7ea98db23fc0f6d19b0ecd6d0a622936cf9ebbdc434b2f66ace6c8737a1af15a10ddd9dbca46904bac021a6106706cd0416e3f1203c1fa40e3195fa4da198c345f23d56d031807defb50ecc96b255cd25c0a8ff1086211eb33f1cf90c2c3ba6bfbd90e855c10d0242ecfd509cb49f971bcb8a00c28d2c7bd1f988e2ce9ebb74b4b355360b5008131a6cbbe12ff019ee30b4d5a5ee0427041e596555ea7704b86199819a627414ef0507f92454424853f5afab87042d735ac15c276feb9d38514e3d736128004ada955ca067a2eced1aa8b990e952f01ee88f03305f0826cbd1cfc40cc8d6d25dcb2bf4f980fd2a05b7132c7e46c9209911d8fa4a183e32c4e944a7146a29b7cc8241d4a8c13f326451210bb28bdb83304ada288ef0db846365e3dc2bf1739c4e993234fe6b21eab5290f0da895fa7350b1d327bbb67be456c025afefb3dffac3d135ffab55feab09b46710c5c9348468cd3b93224815de79cfff281ef062f045540bb2a3c381525da0fdf48303318357ff16cb8106945027421f90f293d4066a05f9ba1c9625f1a928c052dceea3a440f4299fe26018a1d8143e35da868e572b1000548bb72c2b148a5e60c9a34724041350e9be2c146bfa15eb0508e9c6b3e940c7a858333eaaf4ad0088d1fb148abe36055f0fd89a2b2005f938a3d2b70d775a35057db6462bf74d491e46bf1de7ce33a9790572af0f148caeeb841c48678a1590d36fe0ea40d129c6003e5f345e2cfd0fbb927b54ebe4c0fa70b846c760ac90faf87f0c3f2620c9484863595e3e4c434c54fa49ea6f0fabb62995223943b9f66acffc092e430c07be273f10503024c6299c409a4511cc07861441938a69e02f11dcd08aca566e9e351ad2d1e0f4694b63a8b8dfd0006762ce141853900ddb1aee2975f2d400074fedb4625f023dd756442ac088a1d2895e004d12082a6dc6789690435887ebceb74da86b198cf0a6f019b7389cca9616cb8883662e4564561384f362c1f3792835c3c38b3aa35a9f7ab5eed510e2f06e561d3b937dcea4b5a85c2b0373289d742676aa32a421301a1c80643e68865c02c2bb0013b6b19d914c1a17a2c91a1a916ffdf7ece1096531e7fc83ed4bf20e34b109b3458239b173063edcc7d61119208093f5ed8125d1bb8642a12f0ba1914b783a8b25012d434171b03eacd1c527421b01d9599438654961d119d211873c93293f04683e53224198a045cf287203b4eb18de4690ff8faef85d22ea54b49553749182023e4f3f7d30490a0ab19f1b003233996d243d5808a4672c3de8d3d5f35c877635ada3d23f5c2d4428ede9c9b8111f2366d4608c125e4baa7958bec4de8d7cf206a4f27651b23eecf9b9e62d8b3624cd74a2246c9527f12da85fadb2b08cf44aa78e15ba2524563258bfede329d3eb26dbc8564e9625b99306e3203eb583ef53792a45db09d6bdff660c2beb03338d7118aa063c03fa046d99fdb686ab3b21f01a2dfee3aab8245a3f9cb9336466ff176ed2b60f26d4b8ec6ea8285a0665be0f20a4db33305edb9fc22d6a78b56b0151deb719e42524dd819f7b332a74382ad63bc6cab69d260125cd9b8a426383768ecd88d180fb9210a921e17dfc93908e0db90d395fd04d870ed932a297c3e3b7a4188bc43e0419558e41f85797fbda6bb77e39b45097fd0fc16968ef8ae522e1a7264dac444a2bd0f4d4ec22db106d828344d37d1565a1fad795a36087267b390d3b7753465666e7eb91ec7afaf048682e17fd89d25c712d518f8d43e84cef34cf632629fa2cef7bf9a43c8fcce4bb631607b67215ed2f59c6e9ab305dd8cf58a980bc23d4b8a0022f1025a086cb8d9222332139f24b40a79a0df278d70e38720a1c5fd8c0e2fe204b6ba7b9c88c34b4d163763b439ed7a404ca1d09565e3fbde52010e8852d11f0eb7d47812d8c9f80c884aa240103eea78213a849edb1a4bed4d3194b929e9ea7011c4791776d7bad1b4a16375e1f4360e0cd5243d60d6a0d2a7c5abb38aaea0a4919d18135733a3ceea1dba37349d09d706f548929d34012f322d56fdf078d4029769dd71448c1dc43564051dfb94f52a843247bd320bded956caf554dfd85fdc991989d12a53bc1eb81468da0e5a07e089b8b09c8cf212415ec292898f7275acc2ccbb219047178557048d890ba85362df2aeee83e74de3b9b2f54ae8bef732365743fd4f5e150e5729446501d5c84538dac530d60c34fbf36b03f8465a2bfc1609c251465fead4ae71a0628c4bf401805efca8c32262c9b0c851b3a0d39bd197610857403b4bd7b6af10e43d586716e70487cabddee304ea3e06ec5f15dc544ce978d55202c563aff6027ad16ac1660856dea9a91b079e18b3c1def01556243b89f38e9aa61e67d39375463e6d23858e7c31e73ef2678272c6d8d029d4522fd1197916058cd41e73668236b3e0abe2300f19d2cc7489056b57c45894e6aa2432be00690100eeb24b9f047db98191e67138f83b635f8bedc2c5b2794c571cc65d6c4d20c46064dfd29595804aa7bb2041469aacc34baa4b1e240b203123d11b41d4fdcf726fff5e88f807b67e88939f11104cd507bb3dbd0e335ea1fb6c206173afc214c03c88f5db525295f8558742501ea252ff895fc5d12db02809d0322f47a868fdedb39449537bdcd6089a835bf48e384c388564c7b325e6b0c20329b3d361340e18aa12c56bbeadd3c5f1c243c9cb3df5778a22a6c9ab55189860a535c085790c6ce5a9c9a7af05b5f802bd04e71c016250c3c350c3fd0078fe3157c03495cbbc65cfdc62b6bac8b35deb2f0cad67d2a4d604a4d55b67e2f0dedfd7fb30980ae7b185067d1fd04ca80ad38fe066745e09151f97a31b418d1c377c4682e6e4926b4902d9c74c0d4d4c98a3f47a89ef86c89f8cac2e259e79431333023568220ad3645cbf8b470a6af254a00125669d66e718811c7ac785e1d8ad8a9928fa43bcd4719095ef7cc26c860d88f0b034793e8d776b775c930f1c84a4f279cc1d7263c0c94ec4c909098c546e131df07e36870dfbe73dfe5b3518a57568c0a2991cd56cc742dd213ade0bf4caf2b12419cf71abb861bb5c4a9fa915b0cc0e7a25cb340396bb4d2cd60d1dc1f3d54ee46cdd13cccc18a03db72c33311ca03cf9769169bfe27537355fc8db6c379be26d1522d439f796669996262a508bf601be8e20e7a74f129a603f5545189b0164e4563f5bcf97831f93122db697506dba799107d69b21719a2a3460110c468fce7930839294b3d7268fe63de89128243e35faa6e78c73d4c17f0a29c1c137f2f39cd5eeb0cb76498532d2787f746112d55f7019bd06c66602b18403534b32bab2a7600881d25026e1cf711eeda544c78f962e39cc3e86c1366fc6c2ce5d887105e1c92eca6d5ecc42296dc97c332fb0ec7145463a2183dd8654e875ca4ffbca284e0953c0870e9d3072c3e7a897403e8792646be44f1534aa77ade41eb96e9d5d8dac97628255b869335223dc46ae507021a141ac1e323fda5234f3a9a9313b501fda6e607990846aea34e69e5202481b88420833aeb35fd58f3f43bfc9564190d59c05389d90e4530960b6c83ec0ba54df1cc62b784c6424e946672e3c12d7f5dc6673ebcbc90da85d8d74c9076e4dcf0fd7e1c5906b15405c799c4e00702a6541ada2414ce1dfca6ac0a682c7209744ad60744d5d3270fabe412051310a9ff89f9a133b004d9d116404a195b2d06cc541a23a4bfbd8aa93e2c593cc28c66d959d4b94380bd09899f944d16ece81ef5a708dcd290e6c4db7148141e507684aabf4f12f792dd639d1571c1f4eab50b6b277a2892779df1a9e30fbedbce0bdfd2f13694d0751a452d8146d43fa6be177ecb7ea7f0efb338b8789fde96bf050983ecce49976e42264b3710336ff85833549c253fa58e1e82110dad4664b9ce5f2fe0fbcba28fe7b6128757d298154c81174d19b29a2c1edc4468e5e25b56f8a6340ec41840e2bc2491387cfb18d889beadb9c40cd0c3a0e8f29e580e7e7086b57052fe639519053f2017c5c3ff4c51677d9ed8f209a639511b11dc7ba4ce75ccabb0f0a33e30371947704418898d88469bb1d462a898466513a41592b84d66244ca425162bf475dc06fe5a64de6074674bd62256b82709aea1080b82f2b85901d1762c9d2eb0fdbbecbe43a6048643b1f0ed65dc511e33d2b7d67562bf32dd7c991530a44b8a03f85993ebd4ac1a32c98e64fe30784dcf078332df842cb558bd677619250dd76efcc012ed493ac303056e655d027aa40f00718ea0183e64c1001493518f31cce6a7c76b90113ca62f5187197219fa0748ebd125644410666ce70d0ae0221af3db1110d217e3a9bec92a998bb511641d4a3299bdacdc9751184a7f67ff014739171ecd315f0db80100d92ab08b6224e0efd08d2b508e385621b859f0ee0cbf44ae2630dbbecd260829b8165ada8a120d0b5a7503cd26d29e3930e00cf2264a1f24bb7038535c001abee20c87b2b49a5b201eb8a2a5a6f297161b8555de6516297b4c3d66da473f544662289cf1551152268f912c1c9c5c2ee419e15f83eac8e526cdf3f3b8cf12b0a2d1a99d1091e33ee5b7928bdcb35d7db1c4a3714c222202eec32494a2151b96ebad066a0e18bd1137bcfb157d5a42b108ec3bd88cbe0bac4772a4163bc633fa35146ea996d2a107ca3c56febb4d1fc8b326b7ce081716cb0125fad15b5eddd8ee09bc17bc5b1e8d4acbab5501984ccd6c2e29f5ffce5e15c5b5f927728ff9927a04dd6d38f1382bf2f4a2dd9093389b5fbae5501cd12bb4a8d2caf18a625a19ab872091052651eec5fdcf2c1ef675cdd29ecbcefc4232e1dbc77719d826c391d2e4e238bcc2d15d6fd8fde830ade9c82ecfc242042c3948d844f467cc25f26b35ef27bf098c716514fd2e9f9a3ce46743065a11ae3c66fab00d4b452a8850f204f8d9f654eca932822a66b35a23d676a70d9b78685f56d11f6ae62911c217a1b39bb03835d030c2dc675cebb43b9d82f7c8542102ea05e9935acfa19065ea00f2a35a3096f0771c647a20cb14b92d7475bcd670cb4fa5f7cfddfedb25c9d4ad2537376cca37cc6506d2aa28b15c8b79a06e6bf6c6021c0f2793749618184c68b2abc63f467518a956e5116ea452873373dc2c53141bbd4c044e2b0403584b2a7aa2330401262ef25c20637dc9ba32d2c66b840d392a8a55b7c34719bb2a52d13ebf8ffb9362253734e148250b518865aa76f5526c2f635f52c5df8f922140cec21bc75ec9a1a638b10ea109a98319d388472cdea01e9e09a0ef30a7681ff9e76174d5eee00947a034c859eacaf2611ca57f7d1715ef02a5d79f8546b8895e3e2537446ed832ac999ac240f2d7b002c4d91741d0b72d8628eec47c8dc8116a22d76b0a9aa9fa9e61a0a4d850d7921885dabe0e0d71a84cfa9b8030e280a404fa3ad84cc1e045ee1b558b27f6ebff87025885b44703b77e740f568f4934ef884ce59c3ef3801715b1ee243b3a30f800951dd1b30582c91cf22ee59d7f849575f3f8128f49980fe5c6ca03f177cbd0faf2d1e6172cf93504321b93b7f5c50aa3c157bd8119d1b64fa6a25115e4dc9400e38346b218aaf7df05be2898b5d1b01c42487a1c0a84020d14f5dd71df006ba046b2a07f2515f0286bdba40ca6563751e4d32b458c6ba3af6320014a01376a1cd078a224ca8f8e6a51fc256379a2ae89a4d902a169076f5b728ee11d3ff3eac96e606737e003af8376d3a1348384c24591f456859ad235f869e285b032edcac0a93036ce8b0e17f23863591d52767b59d31a91daa205d605922147fddbf01ae711609aa1058369914ee66efbb8867a23500c36713bdaf0ae8482a5c2c779f75406b98f6d257cb93796df3b17576dcbe8c48111730c00f75529a22a2c4427f39c478f501a52b1a8e0c7cb664dfd080202d3e86172a178f351b92612c9b1dade46eb16555a807bec8e07466d004c254b6044dfd4aeb888cc00242e068af9dbae20dc6ea015cb3031a0c4faa5420ca703a1d0f066d54822641171946b47507ad029e8549a1779794812c50852ca99acb720ead9779568bf57d1969db560102c1ba6c6778afbb9377a3a6b94796bfdbd16f534c23af7d662ca631369bec2da59432d10b950a260a67e268f579abd13667238f94324a82ded4ecc081c6250b180554a07d9912c7ce9a160de07213c3c40c15356dc8e4c061d482511bcbe5a36d84a96266d1239ca1bf336f80dc5961b492864593734c1824670e23725ec8b933e60b961d179035287e5c619b93858c8e0be3436d8d991b993232753d277b2e1f4489d462c7eef48d0f81cc193a4ba69cd181c366dc29264c0ba02653f4c46d41e943e74cd52fd9884031e5a8e34e8d3a6d3a0c983c20c8f384edcf9d307aaeec0de646482d851d2963415274b903060c99ded740a608731664859b1c3960d28476b977e74967858d3f7ce00411e285035ef0b429a2a3870a2970a51130b32b695174d47003223423248b8fb0e3169af30b98261bb820b3e7c899b3326f6c987480094294f315a73e72e8a009a991c46b878189a156a00fdd16252fb0e6dc947149f1e2c5103a491a6f7780fa9f1542d8e8ec1892e64a9913230f2f5668b0b945d2e0de8c20e264c699990933616b88c4f479d325c5598d17d98b172408a0fd109312034e720616377e946cb5f08af1d6a30b1c16f4ce0c2371a80439b224cc4f804f1c254fae80fdb972751cb824683ffa68c12386c4051ca413a61ae017da952326b214d98c7181ebe12b6185860f2b57c6885129b16091e265a2cfdb9c3aa71b1c0a23117169d0bcd85263cccec50257050f5814b02957ce2d5f9003d00a019136b917466a941f941d19a10f9614639c531bbfb829594471c2a58a9f2e2562ac8cd7b831be53250f1f0f215c68c489ed68936cfb42a65742851cb12a78ea548ce0f38dd94fe5d5d8110f0d167d6c40394fb62f6b74c00811c10b6c479b3c3605cd12442b1b8373e44e193579ad0950928a392776a8a0e2048d39e7c879f3171e4f3152746dd941064b912cade54b0b366408b2b68ad869c36546af38c00834bac6ca68e99344ed8b8920ab3178c6e008b91167c71d93b6ea9dca73fe482983c1a298e6069767dec010c9216913a6088e224729695cd8d1c60f3f615b84acb870da98c83509b22699cc02828d588f1368289aecd1f101b7b6ba275933fef031731343058d2a7dc878dd13cb1b96a122712edad2bcf0c0b5c84031690b82b6bedb6dec1b89c58f65aab7254651bb3c965d514edcb053745b63002d6865c0a29c719171654b99982e30b51e492278bc2c515387434a1d3c7e8e6f47be889b58113131b5262bce9c801b4a6a44c92ac2f0d2870b08f0a0f1f2c787193569e69508c01102074e599b0c3c416251cce4bc65ed5961a508122216173b3a4c10f9020357e54d849e05548ce122244e93b433baf266b1371526501a13695b4e64d1a205803a798e7ac04cf8c8a272d3fc89c3626dcf0d185f8e5c294e92106961e7c8b14c9a973b441f5eecf831c2973e2c44c045bcd009456d736f0ddcc94a63e30c189b277beb7dc48b170ec2dc58e375e5448b1545f6856bd3426b09111866c6e4285e8c4745ce529881e51102a3d7a2ef5e1ac111a7656e04639f2c53bc8cf9ba077ae6a4e1a14336f10196b66affddc965ae1730a0173ae4f068b105ec461c1b7148f14b0996231e3374a4906950d303ce8f1b376436a68c5cce8e56a2dcf98ae225c70e8325696d578824cdc2f6ce6135d6b8a801e3ed09dd69f4c2c5c34093b4197bb0b8e499c2be365964689d9961a37342f1b26217813d7accbc49c216e48d9a9e7befedf3c571114f649da9ed506bab01b7697831718659c2b676878e4e97277b977befbd77f9e7ce4f505ecc520706ac5029feb0012589579e33016b46306df6801dcf7a6418730c630676747b73f7f4482142972e5374903d71cb20059b1e4474d0b0c18664136b74e9a1c6426b499732275dea5089bdbd986326e4459cd007073245d4e2d0a859b1e54473737eb071078e971b347386e023565b8ada582e1cc35cc4c9012519e79e7539f34a82e78b2e0cd2471d76efbd371b11a58b4c15a4e8e27173b163abc7091b74c6ce265dc2ca0b770647943a4edc8cd129cbb20cc012a310375d61c60c29cbb32ccb3213e965a9d4cbf2875aaac40f3334803cd13337025b96393f9af7a7bebcfb7dee76bbfd278a1d368b74c132a7b059c46c4a571694ae1f778371618394abdc989b526ceb3aa326888c4f1d8d21658aa4d5ad38999c5951c442c48893166390a83f3af54ee9342ba420eb9898d97364ec9ac105f9a48800c6e46c4e51ac82595a762dcce5b23b95bb1f39f3b8652746069f3051b4ba80bd403291f77091325486658db9a121c7c9123ff74f133b695486d040c1b664b780a3e38b9626264608a2828b8e2224cba58f16b03d47b8214a6a98178d961c687ce4b161460a05f2ef8d02197929b871222ceecc9326713a49df7bcf98d68f3b7de8c8b68890b637ed67e7cee1eb564742df7befbdcf32c80cda05019d69bad37def9a382e54d83921054ddc59e6363774f3f4908b92f6c28e9c1686aff483aa9b90b0ce683a373140267611a5ce45dc1958925387b96d6991824d0f9935b43626ecde7bef175e0bf93fb379dfa9b2cc453bb272dd824ce1f28a1a1c2cf6d829cad8a3857708dc6ca46249312d59929405413285b5fca53127038f092878e430b91acbadabb420cc1e2763fcc88d90dc4776e8f4093715bdfdc3a85dee3628cec28870a933f346242285c79126469a90c9d248f6f088347c8c790286438617d60f4b3ae493623a239d6ed959388f4026286a9b498c3ff88cfd481e79501dd9f2762ec1e146e52d86153d7419cb85b32b4cd8c0655959711ad96265c84452c56d091b154392e585a3da72c6bbf28489645a477a883973c3069b4b452644c40d11b464c57901466b579022bd7d17cff24949bae744991d1e67926367111f40c4194aced4d48001e5cb87dabd59b6e828d58c372501e6c69b23689a80993112e5cf1e2349196ee67211225b5ca40a10b31397e624cdcf0a06c4291a91a0583b636288a2f84f6954238eb9c1114607c647580eb23a387ccca1393202d7102d1e56cc7abccdc833262cc91f227dd0ce8ca17811823bb645ebd440999832675b38dedab07befbd4b354c3cee958e356b51e478701161939d42904e69c0c0a814b1ce98f8f6ffe0d236bde5e724518bcc092f3073538c3cd15c580b4ec5723aa0681de19c357992e59e23ec36408b8122c81c96156a2bd2364dadb2d672ba081ad69c28c8b83a3b6667102c421dd84326075918dc93b3320b76801d371d76a2bc5133970139383dc25c8c5d99bb9a4e73693bbb000e3745e4b4c4b0e0215366ca8e2f3ad09cd4b166c6e263c3d69c386246c588442c89d96f9bac217bee88e9c2a449bedb579a9d655eb898e167ed4d7206db3e890121a370274c8d0a3811d6c8951b6f8d45b854b472c0a913e38b6c4d3693410b911d31a47889f145ccd053ef1bc91a9b9fddfee1bdb7d5f0748d5359e3281feffd1fe4aed78cfc0072c8a3e48a63de01a4d05a8fd070bab161c3cd860d1b366c805df916678cec184b9d468f3f6365a9a4c7fa99ee14d0e31f5618c09d0a7afc06f870a7861e7f0738c58fcbee944f7d2c410ad3619ce452e52cbce24ae136dc16a3e3b66ca3e3c739301eebf8710c2ca3e30701630c60391d3f081c47009ca53f610a1d7f04adf049375d2a03e00bf9712b3c4fe059fca47bef05629577a759ee7a18985d824a97b3da5cccc51659c51b79458c361b6dea1aaead2e32b5948bc7f356d78a21aeadae15614834a8343f8c7803746ee5ae6c004c9557986b9bc37ace397afe166a450b37d2bc557493e6e2250be6d4caf91f040484fbeecbfde679feed101474aebb5ceabd77690671207e0acdcfa529ccbd2c4d0984ca72af42daaccc1288079b98a016597a7b26613b0b4292a2fdaeb19189d9bd196474faf4995ba627a6ae012a0f8db8e87919f20183e706278405860cf42dd9c362e245d0162ede9d16405ce0d038679b59909ae2ae0b101719a697aff3e97c32a37cd2f004a5c1cdeb7c1a0bc13ed393977669339f9e4e1cfa49c31394863732dd74cfb52b2c627029ee81b1e5e886cbcb11016c624f501aaa80a83e50a443fdae31496231a13bc244ce2df5f5bbc644ed4912a9dfb533227df7bbf664cc0c8aea5b966559966559965a6bad4bd3346f5c59963bb3cadc69d06f9625d4debebd77ce39e74bb7f7a6c0bd77e79c31c6186badf7d69af79b7326d1b38989d430ce7a97e6ee040a2a3a4f11412854dd75809455950ac5d4b3b9a61e863afec212b1334d2ba3b1fc0a2710cfa5f10bc559efd2347b1df46e87b3ee14bec2aa9cb14dfd27750057ca4a3765a9c7e606144093e21f966fa785797116daad774e2f61742de25e2457ce725fafef6fabfdc8225aae177d0b52ac7747bba2ddce34cda223bd43f18e9718689ed9db3d8f97fba8c8687fe760a7da342a3ab2d23b1e90fc2790e2b3f9d992630ae426f345e4a199969e8fdf3c32459cbc2dff3cfaf63cdb2bb7418e3a57f369833c347fc745700a5c115a0ab3bd0244a0972fe2cddfc0347b1d76df1b80067968f20f7928228b02394ae8cd1f91a37a6fe2ac1cb545dc26f3cf27a37535875ad548afedd1df237e1ebd79b49e79e85bdbb34d37277acd831e7be875e03d51fbe5974fc48305487f7ffa7b0ff1f634779c0bb90fe5cf94875a63dcf19fe7699a3b4799fbaec1fd668a532bbb9cb3fabb4ca1df3539586ad4639533fb01da99e58ea0dfb5386569ea24e8772d4e988eb2598b03e642bf6b7280aca5f103d7cf8e0e01f50ca86d47a3199958c65c1a04b583fd214bab6a475899355cb0e8b4b0e6aaa54197247133749461c2c696ab08dd94db2bd7adf2f9a7b7433fbdbdc63ebdbd707c7a7b917bfaa9a79f79fa9994d1d6454d859f2d42583576f6e04003e54e991d368ec71cdb9020476aa8b17b4d3b4353a48bc88f22db903842deaa3ce1aa1b637f9c8833a3216646094be64647eb840d316dac71cb8cec8a9d2bc6196c8b8f279e3b64fa2063f0513bf182cd70b2d0f14873737285cdaf069173d6c6a3881e36b775c123c348450b071356277fc8c952f7c2ab8ccd64726c99c0026ba302069b1f042d9501186f66e27e20c183c62e8df183c41ad2c54c8b251e7182a40b868bae1f746c7e6cd452c41827d818a9f1c4c79f38160ee3a0ca7d7a9e52e3ca5892af2343cab0398e0089a1e518448f1036bf3aa726cb4d73e1f08b9e91a848be7861e7eb0deb013e7892e4786b01a7caed57871a9564d5910c2ef334bf56e4e4cc344f73bc820c56dbe6699e55687199b199ff1b5bc11536e68ea779b682034671c3165692cd5c9886c574d8e6dad8cc2f7bef60992184cd3ca9067bc7d85121c6663e418dbdca689c0ca1c39640e38c6087d59ff9d50435b684111cabd3aef9ee079f2872e78f2316326ce64f652c3ac4cb3c821cace657984c0710176c896c228d4904aec5912df5d80982c5a25b67dcbb4547422dd164f1e30e199bf9a532842d91b1c6f1263208e27ffd75bd0ae2ff553f91551202dfb7aa6ff47ba3a527b2fc89ec8427527822dbf444768227b2169ec852f0ba09bfef282928e18fd62088a3477aa4f50ae98f9ec8fa9ec8fe13d9f5892cd31359099ec80e9fc83a3d91ad709f02aea2a5162af027ac7402270a7ad2263e8021ff609652e01a48c0452449db0bb7816ec2cfc43b5092b697aee39fc0415839c64292b637898b98ab2c5de2d88be3af2529061f69bb93acefdbb363313c9687f8f1ff12591a36ee648ecaf9f7f7cd07abf765d736c3f210631cdc747988df68393da7e97cbff4207cbfb45e0dd17fe5f3fdd22fad57dbb7f9875faf30eb7b34efa469eea4699ae6d09039640e9926119199979e0f7d7e3ef43ff44b3b99a3ccbf9b4f96e2bf2ea1ffe8afbcc4e27c27cd2f2df5424b42e65791697eafd72bcbb2dcc9bd4b336807ed24163235ddf9a995f6f1cb9aed96ebf885b807bbb73b596e9d3ce572ed36eb1acee9449ac972d479a7e3cf5eb94c6bac732ef79a98316a4bd228717cd44df893386ed34df87f3ad2161f3b9c6ec21fe458ce061d468a90b698ae2775137e249eb14ea43f9e8b3c1f8949db5ce457be4f9e3e3e04a8fc5dc97bbc1eaf27ecb9f5f57cf24a0e047dd0ef64e905c4e5e6b6658e8e4b31336abd2dc73ad665cfa8e47de666ce652fe79c332f5f674d02f7cc270ff16bdc9325d697d0b73b598e7909097d29b6935a2a48aec4caca52591ac9c64ec75f6ee5282e2d95d533b6e9ea3847978e79c75f86e5a8bbc626ade32fc9b41466f1a75c0159707978fb105780891c5f516d3eed749cb970f378f944b99906b49ac7d534e621fea09fb4d5721aa97532479d751dbfe673a7f0d56ff4b9cd72ce3d332db593a7695626cd76d244f63ba18490589499c4b0b080885acdd5734e0e3b5ec09d2a1f6badf1961ca4b1e0a6fc6e58617afe333d6b3a1a418ff90c311e62ac3263e164f9b9e7afa0a0c76e584a01384c983367dab4e92dc6ca41fdec05ad1e74dfd2f1972511ef51203d1e84e6f3355616cb5ff9aba7ea267cd105dc29ddcb070eb728a96a9a96c9b5bd6ebdfc04dc29dea3497d9e69aa5555a99bdf2a25bf8256ad93ab462681bed548bce54e057dfe5b7e98f34cdaa6977f953a50f93901772a27e05a28d72009dc5e379e6caf5b6f73d283be60c26cac15787766c0c1eaab5b45091d0fb6440f481a7be60cfd39fc798bc396286ba861776930b2fc614ba4706ed8dd7a73865643c7e74a64776b30c4ee817e770643049dbb075a4f7ee60ca5ee7530bf670022de6569b90af310e70caddaf1a99baea6c65401c29628ffb0bb27b24972f97a02e95d6382d6f39f6b725e3dffc6ff548e41a76e578530619a4bd52ba1af3046164489c261cb35dd73e9de3b831c1038b6e4281c36d7484bda407ad7ca94e1bbf7de5cbbbc2557bf79f8e7cdf2efbe3783fd1c5cbdde37f7bdf8eefe815331e36b060e9ab7b5383c6cb840c98154e3b243043823e81329670c172d7884e0a862864c1f0178c9a166842772c8eaf2ee496673cec6a04c414752471c1850cac4dd3863aabc9569c32a42e3b805c96fce9dda3de8ae19a3640a6f2c6d0e907302458b24264e1c39e18840d416a1087133262dc61a0619507ff9b4654c35a68650d4ce4624b3214c8e679aaca1311903e39c5263e349b20bce5816d4538521139edcb38ed2564113156cb0f0e031f146974d88469c6409d3446c8b5b59e6dbcb55b877ce755af253066ba8d84972fd2172c2016de0640109a3960279cf376ec6b0b8e04b8a1a2d2f3fd07091f1c6c78dd915214bb67e70f2d39e3849fe207bc0e072b689bbd3419c570b293bdc1457d061b3913557f443b7a28caf5926ce8ad19a33d300a14646640b8e2a648a9c6cca6ad40d897b92a7c59d176ae27a1805a9a9990186e58c8a2363f69c7471284e0a18e1e81882440b1523194bee800d4711ca0b2349b2d856bde79a0b27a64ebae409968c316bd6c4e49c0570c3479f3c5fd6583039d588315374c9c0d8d1444ff2861f1854415af9bc38d2b2d4f96f8931d6eaeaf9f19a98b05e6abcfbbb23767d579dcfdf712b6d06fd59a2df0fe71a2440d0df0ffa7bb61b74902f2fafed799e98b723d047879ec785fef2201e2440f977e814babf6f1b02eea6f91ceccf7f3ff316e3ef5dfcbc6cf2f66c392040fe5288f3dee427ee5da0dea9796b3b62bd1d3640c4bdec75305ff732c8ff3b03fc39efdf25361128efdea069be367bef1d021b3077e2decc42f086c6038a4cee7cd13366079c338db9437b6de02c8b4a914b45961e1762e061d13564ae2c8e2e0303830f275e4efcc0c1448e91aa16509e88d17186850d093e3bbe72b0a013c735278112b03845a40439d307ec66e1e28285160d28715960d46921829799923068da9248a0cb8b97a21a8ca48c6b278d17133ce2b00901da991f4884eab6c088b94991278d59124a8c1a2d62c4f93acd9849f831cd96334b7888d1413136f70486989b3353736a6cd5a1d193e3c7290742903f6c8688cdc1e86196350982c70a181230278c1f8e5c5d5b6f5ea868d360b299cc838aecfcd3a3850d185702999f1ac4a8fdaed1b56143f1850d7ade7bff9eb83437188a629cdefb4355dd99fccfcffa8d7b4acef929e3ac772f4d9db1794b21541337ce5450f4485f7df96f6df7b7f9efee9bb76bdf8feef2dae2fc0745936aa4493552fc17e39c73ce39e3ab33d3b5703fcfb9cfbd33cb19fbdc99e58c1933d4d0dd0e33dda99dde993e265d018ae9dc0953dfd4042b13cedad4773864ba533bfd56e0faee67c7f72b64a9920c0b0b4b08cb86024b0f8845cf7fa63df5092980ba69b74b5cd777aeeef239f61667e1adae1c5de3b0318dd1a07a8bd3d470178deb2af416f3d17fdbbcd575eeca39c2aa7a9bc7caba46cb6d56cb5dd64075fd675a87a3eb57ebe074fd4fa5eb1f9a79b253a7ebd770c7ca47d73f800f78e074fd1a8848aae8fa3708d3f5770042320d569e24c53c7db8ae787dd175fd588c4ccf1db33d598c388e6d7cd8e0e444e8fa315d9f6416961fb262b1ebb53f6a5e2274cd86b547cc0417323318471c97ed441db89e8d141ffb8d92460c031551c9806e6693dc789b03c506971d36adcbc3e5065a98b6387fd89cb3be9f8156f4dc26a5e79c73ce64d2f63bc6bf3fe32a12baaed3a3eb3ac5ae95e8d26702d273cf9f5ae91819e64b73e19caa689a26d5383ddea9ab86bf74938b34ffeb4f8002b01408ba49ff19263da3350d0f70d75bbbe9d73a2369740f30feeab797b75c0b1a2731320f9de87c955e74d2fbf80d80914f5079981fc38c9487e5eea48f8bf92cbb740f76f7601793ead216cb0551a4ed5708eec75b7988fb5e719123d11a17711159c2b8dbdc1be313e889d22122554dd7a155e81cf6d2270f70d720e4a146d28196b3b98ef11b8cd44d5617d381a54db7497f05e74ec2937e17d3f8a89bf4abc6d48cec3cd3f47fa89f7ae9234b8d57de8c38e6a852dfb8ae776b595ae5a83bbc163406a183a35a8eda5f96aff2d2b22ccdbf24708fba1ed20ffab5d61d4c80276d791d1f3b38f5b16b5e8dbb6f137edee9130ebf86b49cd70442a373834e570a330429afdf353ac5b6353a594c5a5b679c55348f9ce956c524a30694d7aec25ad05062850ec99823ad3e4cda74a4a1c205472a240854e472565e2f9d47e606a62ae7d499533727cd1c376c042db55ead74d37ec28ff1df0781ffe207817fb75eddf557fc5d3ffe10f9affefc72345001a0f746e57b31407d8fead33ccfdd6edbe003b1be351c7b6bc8346b48b354438e6a55abfd55fb6f8eda7d69304dd3c406c84d7b00bcf6e9dea79d9fd6611e9a18c3be32ebde7bcfbdf7de3adf7befd4bd3a8bd56c7a9eaff29295004b664bd62c852e5c0bf92ff4fc17eed47dc24d500f784cad69fc4dc167793536f5be21d2cfbd10413ad979edf943736d2b9c6bcbd47bbdb64261cfc2b60a5d2ba0427f7b2434c83f54db3430b974537ebae0d07aa5e156ab8ce25e08dee35e88fb81f31ee50d3d8fc896436bee81187a74bd427f48d3b51aae67a1f5695575931832c9e3c1f28bc78320ae8e3d3fdf46dd9477105016eb2d3e6aa98da5dbd4f4d517ebf9d8731b9c1c9d46ea646b7eed5af3abc45f24f09f20014efce74aec569907d1c9affb9575f2de0aa6719785ea785866bf3fec85487b214edd94f5b3a56c53b67a9000e567623fef4df6525760d2da87e65716f0b479b1254e25493858cdafb29c1563bab0257c756c4e01176764330fb2692c34a8d0b1994341c926e6b6b0d8cc97b458d4e469ce6b50f921c7669e44854577c6770724d412fb52db962c586ce64a48d812196336f432b519b0baf9e88d0063ac817231b68cb8ca8db5993147b5651b900a53cc444bd15b336d69c97cb3880f9a75162a4ae6e152d652b12f652d6d29f1e94ac93c3497b03a061b396bcd95ba72ce5c5869ab6ba7628ed2af1fbf52310f4da7ae1ca595b2bab9a58485f1ef2f3a1ee5a323df13d2c9ec282f1d8b8e405a278e5a0aaf46c53c343ff73ae8d288ab9bdf413fba7910c43e810f5caf526196347b4832124b3a159d8ad7428b04d7cd477d41dfea21779f53514b51f0e5c4e594c349cca9accbcc09cda9e8f4e534e644d6cdde3ef5d6a9e864ece6fb764b6d6d51122e47b56a72492d4799c86e02652db54b71dd6c95b0ba07c1106a084a66bac97cf52f5bf26008094a46dd64be84bf2c8564badfa9e8c4d5cd8fe04ada3a15f79745272e2db557b26ebe9258371fa5c292b64e45acfe01ee4b6d71d82a757573e9782d98af04256d8dc4baf94a5e4ac7e007d7ab720d86088600fce05f16a8d476b5d76088fdc217ae2194bf896c08e0aa145c95ccf6b197281cefabefa28890e54a8424064100812bd113d9cd8bb8749359f2a262d198d64562abafa8acc80c2d0f8382f467ee2b22f3f122315e3456446686a24345c9a1a2e4d05137999f665e84a493a323426aa92088b2884e13217593f944d6f43d2f3a6aa28c75c6380b91c0186858671445725a2af8e617d16929e19b5f84cc51bf37bfe898a37cbd9ed7ca276d97c8ba79f419c5b84c0e7312bb5a7375ed5e07a7adb234cdddee3c8180dcca3a5c300b2243511eafc7eb39e570fa72f2ba1684b6847a1d9cb89cba2e845d10bb5fd782c9bb90a3c385afa11e06bb5ff0ba16cc47531e057329d1a73d0f1374a7af099013d41111f53a38e5c843f381655227af3c349dbe6e5151af831357516fb743ba90861af190f805b40b461f0f895f30a65d40cb43f3f7c3bf0b667968fa7c47473f9ed3561e9affeb7570e2ca43938290531044f0cbe01359210f82100a9fc8eef56a3f707ddd0404aeab9fb4752a1a61191575d387a2a46e325f294dda4239717127af8c71be4442ed073abe2c945c8e22f61fdfbd905f3608259787e62f7969295368c96bc9ab9b8f7ff8f7c34e6d6234bef0cf87a1927c7214d00928392d05758492833a421df3d07c1f1f760a6ad2b6c82dcbacb750c76e3e945c8e6aa1926639aa35c5928c39cad85ba72254f24ee16ebecfe89d8a770ae3be7b33084a6ff1d1c353c74b6641c6aed7fce6efb8f6f1615f42cb51476b3b822c47e5377fa96cc9a835913ef2bdd1588e6a9d8e8e24e0cae2ca91a32e1b8de2578ee2cd1033223bf269a7628e6a97968e392ae821a9a3c56bc134df692b470199e0ca51bc2eaf1c0594c3c97ca7b01c55c2d150e5012ee21e6c22a762373fe51eec212e0425d7cdef151dbbf93cde12d175f351de16158bbcba6955e455968bd2ee54b1484c4b9dacf944745aeab25f68455c456470bd35c580d69628d9d1230fab9bbd554f5ee5232dc57a5b9434c58a8c398ae8a8a52edb0627a76291a9c56eaa5c5e4466dd244aebe603ef51ac9bbf5bf7e315f8796d4f20bfb70c92c0bd94bb16cc28a064978e497fcc97f0ea5bb54bc76e7ed956c269a9a16e328b92bba2a496928065be11d696919751d8576b34d6cd6c74eb2d0f869aa2241b0cb3de1ae500f6d6e8cb48cc88ac9bbfc3d91423cb51ad06b31c65aec1ea2dcffcdb9a68ddd490dfcce64be0aa9901ab284bf1404f34680522240110909053141305c674f72657c9557a6929a13fbffc2ac3c4721450b11762ffd0df5e88f285b28604a1df2bbdf2f07c949739f2f07ca1f3d10ce75be534697bb625573fff3ccbf2f0fc3020642a1c9e0f7556f55317e9adaf9f6796b3b0133d4f7c9665255a4f5fc3f3fc2ba5bf8cba09bfaa7ac0bdc24b61b03ed00b39d0ab684392f5f5af0a3915c6be3e5ffa25a6677aa19010be225a60019a2db0c0020bae8842a6b7c04978c0bdc25bc0f49be90affa2bf5fd533fdf374bfafbe8af43e777a95f7283cee59087af87dd34bf0b79f39ca47f5556f95518c2f78e24f4dad20aafe82f56ac357fd1587a7aae2c1a9b7facb52f1e0d405177c88aab75a43d8f0557fc186affa10389455fcea26a695c703f282bef5cc431ffa2daffb7e6fbe054ea24385b760f520f5c247794bd4f903794bd497bee42470172e2d39dd2bfc35f7f89f40bcc55df897af370f2908ffdf2a93c05dc3da5678a99780e32ee4b7a3403c2025b4547da2aa3160c050c1d37058c15be50c19a0fef63a0060b783fa75e5f178bcffdbebb03e9a7b2470e79dbf55be5f01cf90e195d0d2e097bdfbeacdf0197c1b783c20d08752bd552676e0dfdf4ffdbd55be44f43304833fdfbde80a846a0d0a9feaabfeabef37716f9ac4654113d76f16346869bf59d08cf5aea19e87f5d1ff7174ae4bb1ae98d6d7bf2bb2fa654b61ec25ef0a854261a99126cf443970aadfc0ab5e8affbe02c79d73dcb5f1f1ebaf57d5346dcf60f0834ff51c5e03aa95c36b50b5de0e3af0373c7a7b4f5d7dde87cb81af7220efc25f4ec16ba43069eb5e06eb077fbffeef71312821ffe35e06ebdf35488009fef7b7a3affa567cef45efefff35e7b773e044f6c76fdfc089ec04bc25f6f5c3847ac03528fcdd0fd16030f8c160505541e8c1f6ecbe7f20e7cf7fbfbf3dce7fbfdd3bf3d0f748370f7d9d03dfa36b1b5c3fc89df7be15c88635285c0bb0fbe157ad59432a787aaa356b081400d62001763ffcdd0f1f25e2754de997ad88d5716f5d31f23916ae48dbec85e90ab0fba88f5a39587ff7b7e36001861cac3ffc0bf50ff5b88216e82b78fce8cacb476f0fb8b6571dc8e3ed55e73d9a3d90c07f56fe5355f4d50ffad46f89fa5551f5a2ebe9417dfe17f842de3f9003fd8fdf3efc9593d07f386faf3ad40b797bd52b7814f3d6e1f09f7f90fbeeefe39efa2786bf193ec3f378c7f02a27ea19be25ea1832fc9901c30361f8dbf39061c5f000d869390cbc02aeebb84e6a3e4ed2361f858f12f19ea7aaea9a8faafa3ce003d77677de2ba1a52d0f484c7b1d783cf5d5351f851d80e472d45d79441cf845bcdd7259c3c1c16900455454c4814fa491d4208a7e9bf6b0d2ffb7e7caf3f1d7e1fe1ebd1aa98fda48246d35b2ab5fc6f34c4a8ae07104aff2b0ee9d027e7bbb745555abbafa50578ac3abaf26fd069e94f4543ce9813c495555fdd5795c23f310bfae7b5287e9e7a46e62353247a13ff4f8359f8d7597962614274cf87ba570f15120f468f38638f08538f087f8edb8a851f47dbc2576df8fb7bbff90823f8e7e10453512090909e95121bf3ffd05fcdf07794becc147354f187c1e6f55551edf00eeb7a7f4c46e955515aaff1e05e2b8e35ef0510c81aefe13bbca5b1e011e575f0217fe08de852b067c2143ffbd95fe71f483dcb7eeb93cc45b833b857f6dcf756dd3aef4f7f6fe3e5f5bb52b7d4ea647463c0f83823e0dfaa3a0a637dfe8adf44e7996902f9c07b8b71ae9c14e6a892e50c04754e8f9d1a1a5bf40b1be3b2e2e495b5ccc49b9befed5fcaf96e2dd7be5b0962b8db73cf65bb6f55bc251556de070c1b7c36f9f7ae7f75af1a0bf80073d870d559ceafc3a7f88f5b7ca39a993582d4731017d0702aab798a3a4745203a4b51dfed6f6d107aeadaaae17aee3388cf116db8a64aff014d85814bb04af6e20fe13b89e62bd0eeaca2b7b1db49c6b01137b7ad256cbe9f81fff9ecb51490f7cfc5a0edb0a3afc1a99a32edb0ab98e5fd7f1c1e0fe0ff73a28f1b7caeadaeeaefeef712f83dc1f49ed3081733dd71fe955a5d5c35dd5ff21712df741ee13564de73441d30f87aababbcfe12ffbbb6b3001545ff521363c8735040ebfe1abd6108680e0f01b3e84aaa7facb4af09a4ed351d0729a4ecbe9394d7746f013d4af7aaa0f51b54a1082c3ba61fd0fbdfdd0473cf508fef2dbeb10c17ae6e1840914dec2dfde01287c85b7ca2acf148643eef4162a3439fded7598806735de14c14f58dbdd2754e8dd3ccc4a8f7e95ba061340f5550f82eaabd6ab212a1508aaaffaaaf52a53ad445655d51f6f71ff21fd552faf5fa5375fa9a5f0b77700f525c81a8957347775bd7938e1253c7f2b3d810f9f02cfc53cc42f41047cd8c4335a36cb43cce29b39b8afbbaaaa2a07f7cd8c6668c6b6446f6b538f6d47b536956f0077f5b3d1f8fb6c9687c64733f20de8aeae2470ceca6d6d79f8ed07f9d822bdbdcdc7ac639b59360a85a46df6f2ca81c3941e2b8965b2ac1c9524b2787d0a7f35ff082620fd93a67afc439a1001855582758d606dd33e61e52b6f55d776f8f454bcfd20f709effb24de127bd2a33dded9c3e05ff87071dfaa5d029cd2df15c3e5a1d2a3bad701f3f9d09132568efaad2deebfc71bbe3d39fcd55254df0ebbfaafa5aa5efd0b386c507faff4e88f67ac3c4c7aa5bfdf5be52a2adee1c76f47e2239ebafa57e933ab84415eb15c904eda623a8ccc51b81df1d4919e0ee9f1a3b727e17faf72a4b5add0d5fff15603880ef7fccbcbaebefabfbfbc7de217f7d72abffd4ae0ed0f09ddf198f884c7bda447276015c85bdc25fc8f23f11677a447558ec1edeaa39a378123fded21e07675053e4f551f83dbd515b8b6f77ddff3705fe9257060f9d5819ca8c43f30fb84f5fc812ff83e89f330cfe713ea2f15e9db54c2e5ad6b7ba27f35d7c6de4b58dbf3445adb14f7fed5f5ebefdaa6bfb555d5f314aeadda85c2ef0d9f84df6a10f2e03fd7bd0ec05f327aa517f296d8859f8484f4390ab8e254c20f871a793568e4da7e6076e0071f05f2e76a8d14f85659237f8fd3ce3b3befa7d1df4b585b95f7561a890b5fe549ffe3ed91d2aaa4c46f4fe2edf7a3a0fac0e0a3416090dfbf3de0bdc0f503b3ab57e51cc0fdae238cdee883dde8b7b6c4fe7b95b7afaaaafa565a45faa3bf48effb21adedee482fe155de626357b154954ece781ed336f5db0bf7ed0f9fd6f64357bfd50004aafce891f8ed123889ab6ef4aacfe8ad3212bffdc749e86e74e4fba02c0008fefcf9f3a78f78ea467fe661fa5639c36929de6d4a1fa75fb4b667fa446b9ba66b3ba2421f7af385de4a6ba10cd7f1f7f86b64c76a204654b87dcb6d3a8ce147da9e7923a7328681ccf94e95a7861e4fe8c9f410b7690f77aa803b4cdf46e871f636ede761d96e44bac630d5f795f284a8efbe020c7d236097db0a32003af925ea5b03445d6fbddb3d2e1fab1d65ba4dbbbfbf81149fe5fd1de2f4369978b7db958fcb15c8b9f68abc92b7b799777050b694d72f9b165795f4ec974dab68d4c28af352be2ecfdd79ee765fe9ee972d0baeadec972d4b0cca3542498ffa5d4bd3a5b767993454c4a465bf6b698c19647d99e6a7019e4ea8044d35463d0a2ac6d5080000003315003028100c078462c1582c8b5245567b14800c6f9640765230940763518ec338868118c61843803106006208414c19a3ea00b67659f1135a3f45bb41bad2b8d5fd8c33c9e01ab613befabc027eb857c0fd863488540de45ddcc94f31f93877c89690f638aafc6c196c195a4206df681d70c0a426f1c7241584c5a05183412c5310dfa43881add307db713a9aadbd5f26cf4e542033e0a9ff544c9bd38f1a1672baaf5a3dce3a4a02da4ef56b91cc436ab2ecc7d4fc662a072da512d204dcee46fdc5231826fcc658d7c89af47291441e64accf7c43f0194dab7eb313f98add55c9a8207f981c62c7a038a903ff5b05b89118c34825ae39a2b2e25fa6c31040eb5f512ce2b6e5721f78f58bb2b5d4550c26cb0867223613183034ae30d42f334f97c503d136a4424216bd7eae6d61f6923a2fb9306c421a4032ba9e7866111c092c50e169ea7bedeac223ab29f2c9e2aefd727e33e90087582e562ab5a3bf608b16b790c8110fdf647aba0a5d5f35c378b8a28c95303319e58d694067ef71536302ab034a4201f87d3896a64f149439505d5090a699c690dfe405d618ae1decc70308024909451370165c2122b9c3b018020e3a66d0050b665a2090845c1469046e17c3abe465d1684ab5d43a82095a584e7072eb1fc6b38091416a28c51a40c7085f20a2197c9fc48cfcb2854ef6c2a3d05edc9c29c22b5ce89d3ceded19536a5f2d4b84c6eede442b840d10da9c4bcbb1c5da6bcf6dfb8f80d5e95c63c8a375cde32e435fdb4e3d1899245ce7541f849c9adc2b99900e6eb8a228953151ea111cc12dfe6381b83e48b167a56762c87265cdc8265764edbd0703b40c27e430822f74d5745f7afd6d8f55e6b3c88cf628d037b81788247f60ef718d20be4be67dcd86154a55e8882fff19efcffac494a9144b792ae5abc4b2ecdf4e12fb9b5c3d34df5033463330f446cd0600b8583224dc91c2b1a88863b4d487ba9f028d39fe8cb7741d64edc907186410e101ca3b39ce6dd81a87874de7674fdc073c357c9b969834ce60243e110c99a9d6f23088729bbed4505aa0570cedd7dff9ef563a305b90b8b399d2018e10c05719ffb34c3e1ea3c10afb94f27b821550b3d6f5031cdf3860720684bb95457b6fcc365ce1e0dbead491994d2eb9f9b165388e2f56310380411ca4d341f42d2aef50419a89c12a5cbf09d64224da6f99df015f5e1a6e746c420095fdf74500bbcaa3e1a26f1e40b2d355ddb7d52f9031fff0a12585477b4661db903bd8b2f5cab3438afe9e578cdf63c4d1a406e75d25fccc976257df13ba7b306111ad5232b5fb431fd75224a3353f6307b30ae5868f7c4f3de595f4e7d9b706c960990813a1d7b34a65825519db587be4152e10810d04c7d12b29451cda25f6285c5577c1c0dc2a58fdc508c844ec3022b4ecd930267243f5c97e4d33dbec7327e936f06d7c38e630d21892742dfc34f720950be9b2f2f6093ba4a2e812d99a4af9ed450c2e2937c57844f3777fc29a57646122627d20dc4b7086cb77e0a1bd7fb155ded046e3d9027a14453e1fc2d79d570e5e27271259d84080cc319faa59dd6b75018aab0529f4d36d839b1ca1ddce4d440de9c38ee5c5f504201c04714b12423b3837df9b29ac4cf9e9b00578c7c6528898c13dc37d2f7542323577c88935044433a6e496cd461668941eaed91ce39242cb239ec18b1989f30c8e1aa6e5a2f5b467cca2adfe638bebd351fb63c565505e0e6b0eca2b6fecd0154e7d4701daf70866a3f410dcbe90bca91c07090e1d271222a89da4a0ae6e844975b3ca45b22505f06c44f4161fa63f178df13901c85ff2809c46884952c4fe7517c0c2cf46a202f18523294f2520512d73b8e471540c55d1b780300f898ae8c2a872fda11f42875b3d80be5193808815bf3428c8a47cd1a969b7085dbf4adf6508e930a69c17af7e1b2cf0128565f8c0b8b3e3e0168947efe84ad3462d891e62651174d862dfeddc08f94abf00f64c39c2843525636739ca7e44c61a2c5ccdbe7f051399742946c0e02f37ef18e9edd70097add5244f469275534a56d69d791737038e044f56f4885de4f1d7153a68c07d2faf0aa1e8c6f2c1343e3f7c58243c3698ceea74a04e7b969850ca0c4e7fe2e0581894b69ed3662831996f9b16e81fa66c256355e52015d063115c235877346fe298d6c620cdb0c8b73827d065ce22dca0d463db6df06d11ae64894d01946a9fbc85e9d9b43439ad9a28f833c7beadedd5be39dbdc0c1e7a4be73ca01965da20d5bb5b81f243f32d0b3b0d580b4c2a5c26fb825bf33db860612442a07d4cbe967415c000951b3ba8dac5113a15b7a8c07fec3ffc5a475964d01ac28c6fd77e3cc79a394f0759803cb4627dd6112a5bcd01d90f6b0c3fd036a497cf1b1770001b6beb4b14a4b55160374880e720b4cfec776a74d0560b1b9fff537012486881875590d1a828913488281b485bf7c9c8cc20159d2a479e392e596f7556af0f4ee7a756dbde2a26317e4e52271a42f7ad732d7e962b86e6d9dcfd9121ec1d3a214644f586c681a6622bd79f920a9a40ce26a1dd807cd749d02209a4b1ec8ac932415f2e3fba3546594f33a75347e64545774ae309c6a1fc514c34b8be3b7823b026564547f8378fa7363d40b47e1cc2a8e4120027d66cfef17d83e3082f3188b4351d082402537e7b360e707bc32e7cfa3c20afa3221f0f820a4b47eb383174f22586e33956aad1ecbc26b0ac37ee74f01e2cc7ba173d130762e4ce99594b1eb07aae29cb989ad103c9649f32a6af7237e79dae587553878b5a244ce64594f308c7cfa14d26268caeef72f2ceaf757674695a8f04c6e8b2d096e08d4ef278b3068bf6b4dd57cf516842fc98170cafce1accd7c40e510716cbcdd7857e50f16fa4fcf4196ec26be5272468da00a4d50744fe9b5c33e62c27cb7525cb93b287c297e852f12eee50bd81ca12119c255be343f9b229d9e1301f85e1995c03ad781952a5da9186c809ef1c905a3fba2eae54d084807374fcb1ee0ffc7cf1bacda35320d7905c77b3498153acf6544f7e12e77e9b31cfb5dbd5db7ac6c7de2867b5002cee598080c979c0b184c2570f231731a2e994cfe5ef06f99c2943fa1d7965223c268f7173f6ca531131ef2423cc1734075d143b337b45f3e69738a386def3d58b073e6cc62f3bd64697511b8814c8a118e840ac980aad1780a439c94d176a88c9d7d68a88ef240b77eeb655c54d381c36a5eaf17c63d741e79d95e31f655d012df80f543f66cf4ff0b2b6be7244284281f0345046ee784da26f8d9d065ae6a93413ae34099df820aa3a2c8ee42208872cc90bf450ac596b2d3752579c0176a5a2c23374303035939335ded550afeb2531a11233cabb85b9dc501afbdcd4101fa553c279c13f1c2b3fcdc452868d418a332badc9a6a5d6f3d4ab7e44af544059df93d2b011fbcd28477b448d1dce163094e05d7390f8ca7a8a3ddb333ad6e156ba7b0d716732178933fd507835f0b1e0189e503fc1b25a78262f574b91c084c4bd82183b5ed102efd19c0f6ef6ae8a97085931236693838acf320c4e59cb77cc0092d5a55db516f2e86df06234015c4f9a356d1d159048fe27c1f304055e5e9dd102ea4d52eb88957cff1aa907c04cabf2f90e0752deb28c228b42b35c2b7bf4e208a11fd89f5691b4ddc84f9c41cde09cf3ddb0be830e382389ee70e5ee54062da1bdff664b42edb1349e671c1f1b38a8410129245bb3f205339551006acd5d88ce5b27e715702b4a480c6780aeda3c981b2078d67b84a09df88c8c328105c332298192d18a86418769bbde714538eab4c4d3fb13079515ffe3e57239e2e99f529d54cd40da6f495d8ad9c50ebcbe3a6f0e7dd1ae2797513a5ead0e8aad8d2d6960b02f06e38557a184daa6ed0c3f773cdeafba390de0f88a885d7a74ad762624b687709fa35703d7f1aa92990d9e8602f5afac01936eaa5b92e2cc07bbf8e188e7a5287582605bb6410e648722a49b32e6449bdecca57e85581305706e8c95f2462d16a2989ca7f1524de513fdb281fa6db9edb85d8ae1a4d2358eeb72df4887f80aa09633dfa60038970479bf7e3da6ba4db54764d17333d5098a22e5cbd026e31449ae87774ce85beb1300a0eb3f39ad1b618df33d0101e4907796d6924ddb98c99a567651e5cda11911ac30033f729b1da413d4b5b29aeaf309ea3301915d248f0322bab1109c624edc666ab45ebe77c6a7daecb4ac19158997614d32815c4824e224b61ab0c86e233507ae1531016c0dc32bac084768d3f780e0e5a49366cecd2016645cf72b3582e1151159b1f18ca96974f086724586bc6f619e57fda3e7d2ae8459e2308968ab3f8bdd353b9da7a3ee029e684f7684796bdd5f06acacc5223f9bc6bd169f912664ca661438c7c92cbe4d33ac438c623816a241a74244db1e8e6455e591cafb8c1606ace5def66ae4813e81d589c975b4f82feaaac4409a9a585d36299290f360f8088b7daaaf313e10a4122d65579bf97ec4619a2d47c4bb34b864d5bddd722c0e3b3717d857a47e7c8f7d251e0182c98edbe0d70ddbb3b58c44475d37ca30c046b7974b347980e5b516d545b241c84e9ec16480db1ac84dad7cbee454be9d7309b90dbb582f5048eda534047c8b6f6a23e4f11d5503ae8a9c90958b4007d39003eedad5017a2e934ef45f4494b43c3017ed765c9e282b082d7dd98f6d8f4924cd09e07e3e3af93990f96a3858442d3f69b336be43f8b534b52518a37ae69cf800f76991f52385a9bfea33d61744fca0d84487c800ca4bd0fa276dbd6023eab148da1107950b03cfb01833a4778fffb7d0d04a12dba0143d554c4274ba1df85873a1d9fa92c6a5309875bd7b0ca5e6824e1046034d3e3143b5b99b5ba8b6e34db689eb6d39c4fc52eb4705531e75e0278e3c57c55c47e5c66b604b6da7a5ad28d1caf5a0cbea16d8b0e98bd8ab87c1ae3ad0018008a3aeeacbef46405ef3d1c623c07567b746cf49a95cd7ff516e2d56d6c7b889b052a1ff8548491f18e543880f75cff4c147fdf7857c01cc12cc48c25181d713e5ba44de7e4eb3cead7c0acd39d642c99ffa0e07e345819ff5925484fcc662ae48ad2593cb02307cbb03b877d35c6fa35a3bb2131ffe011a35ae814bf3b6343453f30df928840ac1f069e16793151f20b01dad35f2579a7d37634b2212d8cf2705db83d74c1bfa8ace2ed6a50f808014a2a3b4afbde58e285970bc8a5f39da408ee532603df14acf8db568ebec843f52465ccdf37a7710ba55e39e7f21da3b7eb8cc53e3ae289704a51852d7d11e66ebdff4f366843772ccc2a4ea00021313d3291a14f5f93fffcdf6fe71d51a9e2a70f51581f2006a3bc6c46938b7148278efe557980d04e68d88eb6254288fc0f389a8e0f4b96eff3d2ec54a78fb5c1f54cc1e9b81b2a21a3b361e993f789c7b4ec3f058f876cf7ff84a2e634c4ad28a62e84d429a153d658c2c8da25fdf935a41d8da4e43fdf7c686f42663706ea7e765595b345ea788d4a0d33506ade385ce7c6a88ed51072b81c9d0a7cc6e81c51869038fdd109392a37e206b84137d747a5529da5ae76950e866a47db6793307febf010784cc75d34a68480b17aec0dc0f82b96ac01bf0a317a2e09cc69c287e22cc00bf705f9b015b4630eaab9db69d166c1a077e2cb30ca5a4c31aabb614cf26b3225d07d0303aaae80b4a2a23ba7ddc31a7e1048456f2a6ad503f3a4207c584bb13b64dd355ed8ab4a18d874de53166729e76cb502e1b504fd5834192f4e804b97543b822c24c0206842b95c7402c20011e4b83892b726fddd33db9882f1342fc6ef32b5a464ff1ff40d712e791c3155874f68894772a35a2c9e76766dd7c08f68a7527d2a32a8e2582066cdd0180066d5a246bd9f596910798ae77b5c1f1667689b31805dc3fb9507ad33939ad29544d1c83345e3e2ff87fd34086f3edc344f3e271c36108b364e1a9e3a6c720aca625ee3e43b7f6480180e4b1e42572342ad0425c232f4d81cce00d3e4a47833d72a2879f1d4e4b8e5d309b32589946c39e7c08faa1e7d93ec8715724b75dd28eea4b927e826e4ffe6d44711cfc6986e42802719b125a4c63bd3b9b4fcf855b0b01c05704d3207ae5d296954dcfcbfaed449fda7f7082cf1c138968ebd7c515716f4eb2731f3b9193276dbbf2411fe7d8263676f10c9bb507c0f3184ba761f37071adc5bc0f5505ad2bd31db72a3ba5bd58be055f6511e01b9db3d6f80ed32ace09ef825fc3740cd9119d8e5458b9732afa6646e48c4ec003c779d4b7724ce4bbb1867519578b569d6c55d8f13a8e90844b9a167aaf6a3e4474f82c6dd74d4265ad1084caea0e81b282f468eb44320b0ccf7ba810ab665222b1f590546181a6f2d3ae83bc5f8090fc5bd094057d39687cc646e0532637c9bfce6cf1fe1452c574257696c9b74ec7d97d719b179c10338be4da3d85a92c55c1724a8b3ef2b05dc5df459ab5c88c035ea193b69eb949760937c3db8a46e18e8180301649f2cbadda64012c402248f50e1aeccb0c382d3a7f832c654eb6c5451d9e42d47a60365bc3d323d4f0fce7530de9d3d2981d537772e7c81fa988d2a3874e04f71bc4dc97b3087fc7c2950df300b8159a4251598966df15de001f160314164e0ac4a43743181390885b768b905e6d63ad1e14b98bd9694169c2bfcd8b22cdea3f87f0174011c016f6a868881364542a020646305b0db0db027d0cc5ab6aae5d07b2ea0cf9dc121647d9caae74f58215997795a6494c1660e4d60ab570578ed90d325d40831c6fa002ac15167a15276073d0f70c297cb67e101ac0d77da2c3efc56448e2dec945b95d16476f4fd12ed42774050aed173275fd9e0ca1b661f3bfb7eaba382c54f8fe4a7de40b8947b6efc6f7bb775d5e3330b5d1224109d265d55d156b0a114a0dc3fdf0389699f2c6bc6e060f2e8b5d4ce6a222ae5dda224454d08af899d1d2d403b5ce4423ec4a3ec2086276ff70ee9215da4f4ef518d7cda2e40d38b5ff97f690da862953a7e4c49b5a02b4c374c3bb5e0150fdc7c705fab8236118981bb2e0d719a86b16b66dcbe012d87055627631bba118edb2a06faaa7a28229c2411fa9d57541aa2d785695b00d65ed2f7c68aa03999f3acf977e6d36f57ac2fd85ad184d420653f0d7adf3f0abd24a17041bc68c4eb38b14a1757d4f781721f84b4166773d320a1e6c773dd20b52d1a122fc41f94a707f78f5547bd83fe61715c91a809783b59a1ffc3c41533ce01bca8153a040c6e4cd046dfd163c68ec0ff4f7ba048e4449d9e66eaca8598172ddff946564daee7103275a37cda11c0339f3aaaab9f65e9c55098a6e052c033b320a3930435c92321791f352837f4955189e65e9c4b0de4efa13fa12e2da20be72fe7585fcb4d6c0205c53475435b68d20c4045e88391b89a8f1862ba35a80f56c146fa6dd3a475ffdbb1b5b29d4d7466966084439c5c76d6e2ecc0778a76c7f38a4aee1bb0aa5e93d0bc06423c17694358a4c1ef2888386c87d4130388a8177c82fadfdee2452719c30ddacaccfcd69fd5b4644215adc3e9e136a07174153a89f99bad29bf1d9da113f03ff51e3eee53c2d647b62ef42e2d7441d33b21e5ea03854ba10cb7e7abd23b816e86756f0a5b2344820cef6afb44f0e4b25f01a5bf555e1a81c8cd52c8910c458cfa8ed1f5f885e59fa18f5af768320f14138acebf4e86188bb69a82397f5557cbbef32b31c8d10e925c018343fde051a5a4570a36d00ad7726f9d6fc782eab613af88fb71138043d4e4764cbb4f52af29f701b01c6fc67ed41dcd82e5d89f28805f0a0f4e932aab74d5d837dd4b31c6d7e19a5e5dcfc85e2b0c4c3256b47527e7d3a9729d80172e4389771739a01abc90e02efccabbd8cf1bed38c9d7db0b01212086e65ff2cdfaccaed2abd3333eaf84ea89d9f500d04304263690b8e43185596a3ed21e7288b4115092987346fc83d91825d0d119ae3a28d0af4ed22a3941903998355ae2e21ea41aee1d7104f5992e800d2b16351261fe7e81d3ec923b15b397a399306427a8e1d5b930e7cce108ba66a9c4840c863d30e265b25353118df0a6f12b79cc720e8987568b776f30c08727b79f110d5e796fa3360335f1bc60af09b8c3fa107fa636cf43690a11e05a4ddac6d725a0f7112394eed3bad53e0c6228afea810dbab25dc517d1dcb5b03c1cea2da329a6b60a72d78dfa82cab03837ef1cc759ed2cf037e2166c16a7af083932b63abe5413cf19459ce7afb9f9101d2921d0400639953dee1ef7ff880d7012803c798cdc1faa57f20be8aaa87b21fc801f2663fdb913df31c697a953d9942c2311b7d9428ee72e1f2a669093021e5c944c8530cef16f0df94711a012e6d878e3715851547af58ad2b5d18f4470b065794431e5b073de3b8ccd547983b8cd4b020c4ce58d93f12e022e93108d199cf0da6d9909cae355d7496f9745a1b8a010c20e572be790bd42242e10242dfdc064f42c8d9b6a85a24b5aed801d8e9d0b6de0a15536fd13fcd505ae093ac951cd35b8871d9d7ef024f291497ab13c8737d5a701a8753ecd36ea44db0e2e8c18fb6002c10469493ff2f2fd60353d2fc38214c745a7bb0fb5150e79909409c6161d77b071e4e5c4d1058e356de6eaccc7a91b9322b845b8f2201541b83798552d978c5901f5f6d0c59aa96519f6fd6147d4dc5df644bd9822c6ade50f51477476f31479aef5a91cf2bd36102916038f98a50ade1061ef980ba5b874c8a290630f0dc188e1d7a53f8b29e6a91673f7bfc07fd66e68f7b788adba51bb0fe5f2e94efce43008723313a80d6d32ac52327695bad2f0809487e0d6dbbcdf9dbf9e495e256bba4ea2ae3190028b5dd613ccd4caaccef6839b232ea92cd1da50931a9014786f2387101831a0ab81c697762dbd822678a222bcd2721bf2b875d619bcd396bfc8dc6650eb10c9f69ac701273e7f2a5d1f32297729d2d9c0f3be1893e448767faa8ce187c9e2be90aaf0f736f00bc42c206aac7f5528adddad0c40a2733e60e4adb03a62ca7c6d3304f085291a5b47dd469d7cd753f60e24815d71450340d0b73f93bc10c0073a5fc18107123847d6d69cccf3dccbed62db1d6c5c5a1a345333dd2e804d3db817d5eb10348af657de738fac7764b96c9397b4d7aac37fdf3c905f9c026b3aa5970c9d0424882781cf271b9b6acd0d65019a6a4182b16289c0a33fc64060715866003ab1f34cc16f3d8b92789046dc29ed208ebd0a58adc4869d340ac5c4013c3291154ba7a90d5783e3c1ec56bc787d4298dc1267fe9ac70a66815081205fa4fabfc8526f5b8373fc380c1e02aa6031828c92796c28eb60068b2809403af78af24dc5c903e67db74a3a22c2a1a5a8946dd6141f1babcaf2df6743e8c2ef79c471de8a29bde2a78df7fd4e05e7b3d5f8648d0ad31f2004c0e27f96cce651f57ec9c85853483b45477581c627cd4c1c5851ae13a5b3050cee3e9d263452db69c5c643017f67bb0184f9fd141cd856f5d281f7834374fbbed77f2363c4d2690d923502b7b2ca21cb3297437576652c0e109f55c402cb31f50a34a0b38aff0b336acf2c8547d2bb944f1871056f985711eedd8589657de578362feda46432335d0b66e442b1f54fa715d20e7cf6406bfdbe09d6106c0257868168fb30e930576506223768661a7de2135c4fdd6aeaf58e70449dde3a3a88cba420a636d1d615e47f5cb070d09d0a84aa2e08cff65ee05ccace999bceb9090ff7c9772f9c6f6e1340b05258b6dda87eb761f9fa92b153c44828ace90cfe8aad527cafccec344af98d2cd616c9cddcac13ae2c5e7cb709b447beed54482c6fc1c5c375e6f6c2a32ed32c5c7a541f48baec4f547069b66a9040d8759ebe4f84e89ed3432c8f6938b30dea144168c46e2a87363870d90a176663f92726c9b2e4ec69d490cbfa7706f8b4c4feff5baf1b8630d71a46ed5f1392deda8aa15277344889de2877b0b316d05f5d7ffd933a1daba3a08ca7db00e0e2245eba4465a5ff650b19daeac5d8d0199c70941d7e93fd8401ac41aed42b972047dada2bc6126d18fc4aa6e6f58b766b087289f0e3d951d784c376a7b89c2d1b8b094d5a431e04874dda4514b61f403201551ac40c6f020338ae473350347874582801af1eb0b036cc4939dbf90e87521111ebfbbdf08e7de989ef347adc7e578b1e992b8062f942c36cfec741c6ff6a33c6571752cc4b3999838947ee7422f0421089e2b91e94c532f3140e2d9489e909e0d39622d59344c8f95705646a9169ec3c88c48f982a40053423e7e96b640783505f7e7d50d8e9ab8af2a4d0582271f7d4493c245e6ce7063cf99cb2982ec0e4bba711dba495db731692b490d2b406401d09b000bf0d80c3cd8e4c223f52ddee6a1c9b82424b33955212b9b73ec216e8a58776e22da0cabb24acdda98315e2a2da88f7314f151b8e4f47912daabd5a6a002db294b9e2095aad3bec0e3d61c274b2da2b8ddd8edc8d82849d8b4bb39918af8a392b9d9e808b69bd0a419c7d9e323a5cf10de66447deddc22a10b67f827f8402ac9e1566ea3636f946de1f0f36a5c675de5725a4fe116cc04860fa1b8e8280e7add130a58152722f5e1fdb36127479d7a393622de81cbde593f3b0ef9b44275588ed96ebbb7737909a3f8199806bc10fe15f172ceeebb9720476ef38037941744d408ae76b0afc9b256293a58ab132ffe5d8f737a601b18f6001a676f446a15365c623187efc1a4fd46705fb5e47d8a30c34d4c23f4f2f98adf208ce6d1d52030099d890a4ec7f2f8e46923add566e574b94a4e532dcc202c4f07bf45bb2cd3904740ab1fb504465c05ea64b8974c87a3346ca138900d7c4e940fa719f1f2ba13ea5b4128f33e68bc2c507763e6f4aab29c062ec8db1c8b2b1b18fb04c591955a1ac8f3a829ce33d4e2bf66b71b4a3a787b89413acc1aefe240847f92d69f1b694f0d950b4a5aa887eb863048032683d65566b6f7cb4aa54c8e0d436ba1332ed8105d0bd5232e42c52f9d56743aba41ff8266c0be8d39295bcb563a5eabc27343b511b288f056165fdc56335797198e61f0217e1365453afd971c3e8f4ce0c656557c64d840c8ef3cb2fec4cecf8c968d254131919f56ec1111489af8b4df6fe4b1452c908b0a38d98ddb85d590d60ef1a232f7ed30c001e3240eea61a45bf4a808d9677dae29d7274c3d8e5e4effe962ba69ad07c7833cd6cb319bb2ac60b0ffe94caef6c4f67ee6b244866a9c0cc44634ff2b1db1dc13e299e200f04128e5b6eca20b12ac4a10819f800f25c16978169f62d69ad2fd20476f68a85db0893116082931048ea1edcc124667ec058feb9ec5e0768a745ec94d873239a33905e4cb936ed040d8ddf695702236f1353d41493498cc34a3b9c077e160740d4898c0b72410779d9ebfa931a2eebf4c7ed2c3ed121c04becd70f60aa2d89808de44658add1ea2cd71285b003937a045b68461fca2c9ffa7e34fe6b4fd1b8e094551463e9c9b6927c6aa26119f65fc70d8a774aa54f25d34cb338e8f145745fdb260d7ecc6a787f0e5f0347c2f389bf464b2c62633ec5c297bb21116b2bb62632380c1b9636e70e2c830d4702e7f6b8a1be3acf4eb37d2453df0533ad083838a2f489c8f8a8b77072ec34ab20c37b23a3730675fd10f672b1ec5a8f7913692ae92153e296e8e5335e2aaf4f928ff72b0c869be5b7b49e15cba0208894b3bf5a5ed815b6ccd05418b113a5ee2767d34f435c508aa56108e73a4cd8e1c017e11b70abddeea8167fa1ec628ea45c0ce586a6d3bffc01c5f137abad42e48203caefd91be2fa423a7d3958a2f0b6da2124a50e316959fe28e2c8a67b4dbabf2992a1336d38558e13942818e793a67b6caa8cdf8e50e31837280382eec9cb97eabcdf294c3bd1b44d72e87f564fd27c36037feed8cd86407b4b9b9cb6795ecb00f9241049f953d0c6dd51336ae8290abeec4fd643438df8096dc96c1022eb0172f06ac2aeb146e336e0276e9698bfa5e5f3c24e27c0e0c942e693421e3662ec73311bed66773a97b5ba73e2fec4ac63c7f311c5be35ea392889d5cb9329970cffd63dd45b715f04994057d33864a3663b7ac44124db8a807c95202866dcc310c4bf01c78c38a23db945cafe3de182c5a52cfa9949b92c096510d3a349a9e91d58eef7fd0fce5a3f9eab628b9fac1e4e2f1caa75148d0dbd81af1bb2cfb36aa90d7e210b6fd5ebc48e9b3cdd6fcb8b43eac3f426c2b431da18e820fc2f9cc4aa156125945bb6da94cdbb907a57ff0aa05f4a4d39c2825c2b662690f3e6970c20da477d6e126831cea533396e1a994d40634ea78acd7f41c6bd25ae59ceba5131bf28d5f12df564cfae3844f800aa7698342bcef57a7970cfcc66593fca42ad3461e506fc6ed1b347623fc02e86f42c071a9adb5daa6138f50ecd9b8bf36f2a9cc000fc07a278f5c041bb7a9d564003479d85eca42a4670e6f661316f1d1c737e88f6ac1cb27ae8ad61ef14aa0e2e4ee81dd8a0e713d61801bbe42595dfc24b6d4463e9e120353ebe10ee000f96cf472574578f5cce16db6849cbed900dadd4d28d6f8a72d87016e6ffe5da40c21527cc3142c4f2027928c4c6f6ee586812cb6fae88374c8a194a923f286a098fb9e1e2c988be0a40ea0bbf96477f64c33409a55552788b97869500e4ca356773ed743ef243aac0a6745bbae2a5d4381da15cb025c52b9e80c197f29de0942dc40f1b367f0aa82568973a636baa24bd71897687fa5107ff5711f53e3c93f5e2df8726aa4004b417621d21fe7443f8ed40f335f125599a86b1c34064d8b38643819a0f54496ac2b5a2ec2f866b17a5cf38c2b5619dc42b77b4851da535ad971794709874dd24d3bc7aca5966645c6396fabcbcadc84f90978c5aafd966af79c0615373c580bee80795a045f3a2f538b619f66e7118c1cc23716ec9f0b70566065edfdce0944fb47bf508fc43d9c829923fdbc8e7e5eee8b5db670eda48e83fc9657409e806f09d5ea26e6e80a1397c599bcedd92fb3d8fdd7602e851bec09cee61b1206629d9239ead70ea24f4d6e97977188f0b320d2b30ebf47a2516fbb16a5765d6dc5d96661211717b5994b27e353b8abe90fdedf764697d5747052316a42f6992af9b220ddbe9896c9044dd8441b7902e47b4f557abc48d59fdca59fbf1a266f638e0855263a1a1f7acccc173a01b9efa239819027f550ca1ec22b2d3bc7ac772fca89a05262e964e16d08cbde6ebe9a00e201414d24d874435f95d6fd7cf84e1965c9f4c4d2a3aca0ed8550e8527ca69932133a25ffc7e794a0625e534292c2b8a932f44f117a252a0a998acb16b02878ade41157638e88c4eccc74bb6793dbe2614271ad6ab852d6dd8fc3559a5f7ef59967e41d7bdaa4a87f3f3a7dc0c369b464edfef7eb04ad55a4396d2c4a49a638b0a7c612c7df8afa913467340887d6dcd39cc8617cec2d80cffec71ca549efe105a6b65ed66b5bde53b26b60e28d5190c383a9d0eb0ac944d6a28d86b7f5f911d150f4937bcd216ad63cc3f0e18dbb3652f7f4dafbc7b51c5fd981bcbbb7e966226133350acf6063c867b1ca996e95a348a86e361cea49740b2340fa253a0312cc67011125c1e40574b6eae657869aa93217c320840a37b30981a3282b1706df90fd6cef2da4395bba987585ae1e904a3310a2acd910624ce8e9b65750368779ec7ce29dd853bcd376c0347360aaa1b3d840e140014bbe4c5c91aa2c95d4b5cb8274402b7867880bd3ae2ef6052ee3f610395c479ed8d4bef4020b5447d2db908b6b7c9b8e3071ebcc946818186dd5425c33fcb1e37ffb1cd008db91bd5b9094a61727e00a79325cb4010713dc2e26eb7e7d23d59a64c9864a2c6aa767af1c1dd518abcbb937e34a3ee043e6243812208793d23dfc0b68fc6e955448b01695ba7b620a801697eb69b291257cc1dcbea6571362e508fa51e0ce6df0f69b55fbf162de098fffcca85e8933be85cea3dafbd66d989178600fece72fdc3468999fad43fa9f472bbfad25b370fff05194243cc926c2845a09ee24c9666c6cf03786748d0c1d3728e6307eb9399d3006f7c728ac5bcde0e02ff6a96010cac1d3f6a6f4dbfed005d97cc963cf6139b1f42c74e2d68fda1e40dcb604ef6d2651ce22ad06e72976219ba94a6f895de1b9916b75431dd5e7330e69568b3b48d46d4966b75f5d1a769c0ffeda6c01968654eb0e8ebeea379b086b5ee3fcfefce9705a0e1c040cfbbd477a10efee9f393ef02c7fde14b4d0697587cefb35d783773874751b1ffab59211f3ac46d18586318b12608f4abf3eabe50d527b4b44f038c5f81a3521b081a3333a524c78660b3b933d0c8c6affa450f213101a4297dc6ce82533c8576c7935810d7523a625e5555750bc49e73012b2a462a407ac31a286170aad500808257137ba95e928cc6ed8b17086960489065ce9b3d6deff4c7411516ceb7fdd8a52c61bfe876356a137ea5b0475b9292f82cf958d7ea4dec8cca0e31d3780d814a4d1b9afd30ea9aa0702bd18f2db8839d2c3d560cec63ee0d3e1c6c62ff44aeb6a6b32ca23f3dfb3164711ae1308a7afd3c8700860ea888a122365d3eaf14aa098db7203ecfdfdb6b52891c1131f6ba341e6da6c3c1fbf812181ec0a5da512c294e7fd1c2ce3ea6e1c7d1c17630773f23980f3fb856f7dc7b5d1caf0a67439bb8517420c47752a9f05f70459743031eea7616c8f172a3d3fa854af78504bcc5c0d930895c6fcf5408462d05fd518bd0b70ca1049492d43be221dfe7883e5be4965f77aa885c1b08fb5c25a02a122877f2d9b73828152aa73c90ca4f4e22f3d1f33bce0f9919b0925bf9460312250aa595de858ef034f0bb1d2662c7775338c0d88c5d0c4689b3374643cd00f1ff1bfcf5dca0242d492ee99cefc7acb21825b41754dd006de0b0eb5db5d2cd3889455c2209ce955d453d0a1871f7721dde5f12f7ed3e2dfe22f3760de3cf2f06f1e6a6bda0d2321d4d9d441bc335a721ebaeb167b4d7f70b3821b9825d0797e1851fc3d43176c980176be909b123b6b1eebcbe9aaba00fab04aa4d02b4783ce2a1e702328fcf7a720897bb582e5b2f378efcc567c3c5005938c45510dc586e82a32d328ca6d96f8ff1bc5718b8b352633a442e8d31250704457e7022d6eb40e855d9f39686414c806491a38c2b87d21ae42ce6dc0da17f26b0ac7fa9314ba796197ad2b9879b9595e61dae6775f5443aa3097568a6a2088b0e0dd4a31b631f2a9e5a0feefa4f9b52f883014d9d863b3c627950dc78744a1c00f9f615fcab68035d37baea167505f98d42af5d08f8b7961f17c6a0f2335bcf6058a0ecc8da98141b58698355854e03bceb9edf1fd1bb96d00b584d60731e09908777c2b7d0b1ba9301cbd9282313480e059722464f42283881473fd916c993b0546a1592fee2d73edd2f09c374b1e0e3a6ed212a9c3cf5f8473031080b61ab42aec3ead87b07e218df5667b64261fad172af53d0d0cd8f9cb55a65e0dab82c4f0f85f4a29cdf84c5c2ee88ce44b8795bb001c7b7b5927009f198abe57cef643420b711f308a216f2ffaf36308d82a10c072ca1fbd799943048c3fce63c340703924d7bdeddcc7294f9efa9443086ea4585063165cebcc40487c21bd0b4c8b632ca2426cb3723148b7c4fc9fd7341e78eea5318ce9ba5e37102359a80afbf515f177452ae28da8b67169ef4abf393618037e2f022075724c7a7a989922f523f30020d215b2b188d6a790841044e814205b7548e199606537565683be5942f693970319b8d83304f7e279027f4c462429375203518f5f7c118a320b01c2cd621e7824258720a1559c685ee43667484bf94fd67bb1c07f321894f996d8d27fcd0d440d29366a34ba5d9ebfcb4aeb89d942161a57fe9432745c05e4d85e5d02bd22e1d6be361a7b5b8f23f22e1e07042398cf1e81b2f4d06624e65c9f247d989c4df74f5ed077337589d42795fbe59910ca24f0c42bfab0301222794f7b2e8d6540ff47b88489d007966e9010cf2a32ada8ab221e45b7752e2799061371bece8bb253da2e5c2984faa2501a146215766ee27cb3a38843e9f0a31f03eeb900aa55c21ae4a536379b9fee0cdab6fe834c010a38acc3c7effc7b72758639436fa8d33fdec4501e87ba7f0358003113042dd0bffd1b46a03f858349158913954b93f729e2b5305570471ccb2e24aff82c8bea6dc12f6fdd070751595843481845a26dc0fc4672321e58b75db1d21837ccfd8f07650538c5963821d07125626ad0dd4d66a98aba259731f63e9f44b018297f6ee9e4e9448f84d436aed7ad9ed2587869d707c12929cb6b3ff5ccce4945c5fc8db8d41570b6475b3089ba2396f2024b8457655a78a5ec74b80032ac1090a6141b75ddb0d072bd68a7c93195fd161d47bd4b2f901c3fba5a7ebba352ee9e459dce552575ed250e3ec430344cf676396c4625c4513a5df1c0fb9d272223e294ba2a8c5de22bb7cf0955b97872d7bab31bf700090062498b090083d75df8db9f41a11da308a4b04fbac3b3c1309b6df883bdc2bc3ad13867056c51331e666fab129f9407d775025b58f072d7682091cc30355858c3afc7d3ee7a1c3b029b6350550843c174e9afc994574f71bd8e7fb05d07380e7011d8ccc474c3546b22708f7925c5714e46a72cf10db463da51908eb89efcd694c01a2f3f1e10f493e99e469d98ecf2f804b70b164dd50e9d07b8d716da3bdb631a7bd15a1d73187853384dff3d4c8a54de14081b999fc8f250f4d674a8999a6d58340b427d7e02cc540e8baf6166fee897eb19ae06234d804c0ac392836fa79f0d7286b927875a1f4f9a7bbbf17211252ad0ec85afa2a94aac38f67f29d55c70acf684d6b211b4ca8d101c15c022e7c183f1aa4e8cc5bcae3006d6c348f605dcc7efc5fe576dd3cdbf3b7f1c4f63b0ae22c8a7c88348273c9172f918dd49295113b3647a728c4915de1f5273967e4ba6693c65dc0fcdbed6dcffc3818e801d7088225ec4e347feaebc04d1ae33abef50c5489214bd14a2e355c47053141012bc0ef0256b00bcb1fd0a49721e3bc9f9098696094a7d2c43fe9a7f8a641edf296627a20706c0a5f74c040fd8b1059226c9f3125c5ea1cd9ea953fd48f3ecd2f4ef1b820a3bc1558963458ff6f885f7d98d1f6c450aa8232ac78a818a73d24cffd8b01822310f94bd070ab00fbb1a4f252a41b9e61254645c4bb35d4b467201d33423304dd0bc04af0d962920e93c82e7d84e6d24ba0fdb9d4d561e5a36674e5cc66958a1a438c01b1291a1edf561fe85615b919200cbc39959ac8b937f8798a19225fafbda4ea20cc8dbeac897fbe6b061a3629289b03622cf05a37fa85fa6a02843773586f94b75316da99bb6db10aa2ee03287f999fbbb50f4047144b2f9b5615fed2d165155bcf15376d753fef35b8eadea303c44b663f5e537d5d2a901d4f0834d97533deea56c289e6276fc7e5bd4431f6f6e8bc3eac34430b284fc14ff69a0ed097ae08af24c33e7d9a943b55ad6d68008c0b1cbe4235ecbf2a94050bbb7d4e6dc239d26582a1eb58e8c38000c8072d8bb13db9d730b06726add42e8cf94a241897d3beae8c3afba0c3465c1d4f061ef05dda706740ef22fd0db5e1cf9d14fc56bbf6b2065c708049abd88e6c3bbe9e47a41a527a8b87755ca21c3fb03b59396c77da85dbe9bdd876bdb181af2545c4fd1a4925e6927a9d13500e20aa99b0a8ac8ed0ecf2b43ca08626e052ca0d18d131dcf076c0a8f5464daa62f37a68f713b4ed9fdcb81268182098404cf71ff4d405148fda95c59a4c16189da52c1179db7a226fb61738a4395ef57f0744313f01a710a9ef01c14087f220c26056445d53d5d1e687eae2cde57a84c61e9fb3eb7c648c3fd52780c7087e5bf65a81475a797dc1fd88b7f4e527f01d6987d86013a9d4e93e3000224bb924ae2891e1011d27a99516741ed123bf685a881b991e9f7226c7c12285bc8c61db86f7601452265b54ee68893dd04802c4d86b4550895e260cd202cb422c83a360601479c329dadd74554fa632439c441fc874dcec59eb56c0aa008d906725126a930558e7a5ab9c6671b5e5bf37336bbabd5087515ad970e9e5d86c40931d219fb478e91f9b43eb3a322030bce84e608bf6ff9e2d68da4ce00f10310dd8828559e751c2d0f96e4c9a5758eeae60e968c68f64244612eaf5eddd970553c3712db89f5935465814e9f974e5e64f73c7a13c96acc90e35e5a0308858f7701163a52efc41cb7af9613c6eaf0df8ecf950e3ace48af4273eb8dbb63544675c578ea24e7390bb490ffff0d3ef4928cc74c121d54e592cad248b20e9ae2f88eeb6eb85bae19b95026c6495fc11a90cf3dbd8efdf9d9f0a370d0486d71a7db383cfdb924d0f256537360bba346ba3cdd736b519a11e3fd93125bf098ed44918560058e8bd54753a863d8b200ed7356bd24fc60bb89fbdc0d5f10d0b5a860fd1f0890a4edd65c65590830e2de4ec130a38cbcd4d2df316549b7eb4917935c0e78893a32497663a70ad0a1b7ef23b90cd87210c63d633d148a28df54b72899b7b174fd940b9e54fc8d3d406257d4581d8d7ffcd3af448b532c5204896dbedd8d24b1fd09a4ab3e89dbabfe79c3743c84a102283ab523441f44d49908a255b1582cad7b644b5b456b6dd828794b12a6efaa7604b138783f8491c4e53d2dc91443f1bd70755e296afab4aa84fbe3562276f9f5e93d2660a1065a96ee55c15b12b765046d5f0145e7007ea0c2aefd1339e83f6566af611629335cd4be69a71f20023a73fe871a50606c8ab98876b6b5b5c005761e99a98e2a7a6e11882c95e5c210bdc4618e546e5291021694270c50f171fd8540af41cb0d02d94667adaa02dc84b812560e014d4d870f0b425d63f54f42d23d5900379401ddc307d925e29cdb6cf6a097b0497f224587acde3a898dce8696ddb85827cd17040014ceeadf6cc9b66cf35c53b6d236835c63ede93f2ce496b56f9ae1b9d9af0f666a69e903e7ea4716f463e154b22bf9e933e1d3f603ea49213724925560993431173970c6c9b2417cace14f38e083e42ea59594b9f7088b23cd5fd3cf0548740ea007672307e5170384f50fa034bb577ef518914db8d70377081965513d2a87c999c0c3616ec5bd6538a572fe0d7a6fe944fceddc8bca9a7ae3e325373176fb1d048992ad6fa29f2114351131b3ff5f7868377cc53bef41c6de8ff173c54bc5ef1391a0a319a056629ed4011658813f399cd7ec64a838e8d7bc05d7ce18b1c68132ba432778691ef43ab0474f42548a5c7c631bfeccd6e9d3dc40940b02c7b58f7d3ef310c3ce1ed74824747d01bda61bb7f9b91644da9761ee9706982faf4092f99550d43ef378a5a667411d3546c4ef4af2c6df1b3b67f48c6cc8f86b5df394b1bf922825ce8137b5874a3e5ff3aa69db4a2761271a97d6743b0ba9f9989483a138fc06e60903fbefd205f20c0d2ba8ab18cd707d8f545053eda7582edd37afe84625491c75de97242ac87bd7e54b63530c494b6c235fdacf319d0f9c16d7c2e5047989be5aa85549ee24e02ced025bcce5c5067443b924a9af69477ad77ac52590347dd1d41dc578fe6acde89c944d2e9e9c66749d9ce85580969a4616f370a9cbeaa5cfe95bdb7502f5d2b475c5ba80d2a2b4fe5c411ea607246954b6735aab1c29eb6ea9f5341534c0e96d332eeee522d251eb4d4b969681756b67b8bc000fd20ba6a6ca6efd992db8fe4a5df853abd27eced2be471a6f56252e8b1f3c5b10275e85ed56f3b420b2e9b800508a490104baf42c8cfc008a79b7ec2529a377027602815ec57737ad7c01ca99b6625eb8a049ba93c82ecb575299a469fb03b8254e861d0db02eaf64872fd0254c323744e59332cce6a402f0e40aa1494ff699b293081b1aaf0ecbd39f3baa982729494bbc3a4516b38bb50ad92f41add9f5844eab4ab49c88ac27d13baba2c3086674d485294de3936ac39c9e72cde129830e9fff0ff3e4108a3a874a0983fa1ae9a56551d189d8945e11a073a0a6e92329f6af61459408ba224af8c2cb49d102fdf366c99659e5288deaa97eaa8307cfb9ea3deb1f31e8b2b1bcefc925339eb8fc48b7338457e236e3eb4914167065a47153bc64369ae10661dfe1272ae9afa411ea1b7de787a370929829c9bf7ed7348e5c31ef19a1f82c750364c91e41fda0318377814320a0fc5274695bec98b6c318846c2f6091218886fc5559222046848749bb4f63348d87d0e28d6e205505b67837eed442f564d690899c5cccabd71180cee6026e804c717894e0978861b54a692b2c7dfed574c25561051f0880b91c2c5ce44a7e10704191f1d793e4e5b121b95aaf7b6d88fca61170460d1cf9ac106410c78da688bd9fdfb11d3b7543d91dc640b0254da0f84920e0610060f6f2c900b9ff56966bac9d5ebc7e295306151d3a4928858b5f423f9b888a98001721de556c59f924bdccd8429d8ed95693d64eab5bc61d71a43f9098ab6d3ad0967f4eb1d507d85b70b699dac44d4f69ebb3cafec6da74a80233a74057a56d26b496aea60901eec40c5c6d5038d2abf622bd428104d816c166b54702127b002c13d7f180bee4a7b8412ff36ad3c9fac87612e2e1b95457e09b9557ce8b1474ee36980e282b2698e29ee2df0966c575076869e98fe08ffc5503533aa50e0bd52c3c81f460fbb327d899e228462cb3142d29f68e70d92ec5c43114b863749a1e87f36944a5144e23f589bd046631f4c1b1e3de3d4d5028776d76a685f2d83e95918faa4b04a75638937b91b7fed3ff3e865971724815dff1318c6f63c532e040e995068f77c7f1262f4503dad6e1808a1abee2074a5b57206f10891c02b6e8ee4c4fa9f6cd58e92a769a4ddafae17f69007d2e4a3e872ba54405ed75e213e821162fcbd38c6e23c4ff350ca23e9c90610f4a75e2d7d8d5d42fa191f4b2571186fb594fd5329e918bbb1a682a766dd79bd54b7228e320c7354f6abb0259836449aea68335992a821d3c0a647e8aa38c85ed983e1a2bc703fc3bec4ad84d0db4b4e68d099aa953513f969c3111e027b149df938518d998eb262be9fd15edc227f001678173081cdcacc9e82503e5700636b33e04a287c3abe20ebd07eb7d68f15dccccac2456df7393860aae8d097f61a788229ddb60b3791b8dff0b5c251be621e95b84ac455338326500153b7d1505704994371ca5955b0641844f6cafdcc4aec5f6b8d27067db24dd2924f112fc111c0aac0b16492b2c959f985d8846f4d13c8d8e51984ae4fbee65dfa1d7a4cd28b366d258a2d6f51c1b36a4b7e7e543d56c5b534e7e68d6248d5086761b533d6409b8f906c78dd6001cee515720559e6ec5bcdadc55eb99b5497b5dfb929f7e5aabf49d152943ace17c12c569a53f43c29cd5f57facdf4ca6f177a4128df236aaa825e0b4a0665a8b78f832e9669c6c5369906eb6d3819eb5e8232a1759ae60ca101bed6e05adebbbbe0c18f5b4c59d98b7a194a4be018f8e05f60854e4e3e9161bc06e06266874176891f1a3f41de21d2b0626495a7c3c16899062a367002629cbf8e408d25c76f7274badfd088d3d96c6f05a683d172c3919017137de46b65d1ec95904052716dedfccd32d1a4c50db6f52a0cf42dc31680105e948eb7997379cfb6e14b2a3ea0d853e823f839b8903625432cd7acfa06430c175c2e6636e631aef4c260f883fb0244a31c2418ce21905702d2baa354568e957d34ccc802d8c042517487055af30f04cd01f5d2cc653f7a732eb34dbb3f2bce2eb64ee9526b77e0db4980fe23e407a8a51ce36a7e791f6bbf55bf331abda96697a450666d0a372bad1718d202363039b2e0b699668293e5140ca81e81938c218d640b8f420863dee6a1991902b4873fa90f2b969e4188dfdbba61bf722387cb99833916c662ff13481e51f5599cafd31b369712dc8e109e7b244649b4c3f5036451a030dec16fd9a4dbb9cd51893141c6b06429ac375b382ab4d0b5394a05e730a90c031c2d4cafd17f43abbbf86947da3029a0d8e3ea3327026b205562c5511761389d33d539bd8117bc7909e7041dd6522fafd09c01ae53e0dc53c7ec9abf2398d9091fdc49a52ec12f2e56350dbff40a97445d08dfe7bbc01fbed24e10335bc61234ef0f10a1873eb2cf22673e67f351014f48183a0470e7de27252fc4613e80466ad1ea1ddd8b41dea52d582e75d88c29ac1f753acf2be29cdee452b15c2da709e5c0adcf1770210a2dbeb0be7fa4c01754ec84fb61de21bd34567b3c68388678ea553b5a23a99bb4b49399f87fab4733a057102572878821cbecaf5a2192d05a1a7bb0ad0716a85e41199332458a3eba82ab5ba94a22672253ece07232994ad1fd0689d37c7bbcaa2b32353b52141db412a1ace8887e0b1b9d931f1807284130a2d8264c9fc2a94c47ccae0e205762a92fc0ef4abed72f4be4696b0fc46375a0a51f6cbac15771e6b13fac02cd8c3facea81e303f5a0b4359414c1360e2e1cea8874b84b8d32f40181a1896544241b681f42e1a4b37e03f367dac85a6b6e9d2af383cd3789bd221f808d85cac9ee9f22f16b7b64dd4f685c300ccd306ed09b8f23a93a6699ed8a88019bb6aa313dd7d9bfa7a49c75c1935d123494071f6ba68f67104d9479be9f241096e5cb4495a9e9172a90bc120d04b21af022f8c8fcb0860ca647875e09c2ebe5c1b69970172870991f93b5874abdff06ca5128fa93911f2d943feec0483ec021e0dd988c7c5a971ddead8225ae938bb9ebc4ac18b43a5342b5e39a223b30df162dd066525831da8125003458c81660166b5c54858620a7e27585594c48edd7e8206f125ffac308f5e02f31e3bdd7380e119f3d2f81fadf21d946d40a377e9629eb9f1bd7227004788e3d3124a07e1ce43832eef9cb8fe113d01545402dec0317603e45966d9d68585280a4bcf93c0239c65bff168812d227377ed85a0ea5a6af6afb4e84c30f59012d213b63d3b2e0c74556f2e5f03bdf6c66aa3302f47898245f92dadd5a9e1c84a768ca02ebdbe83bf6ee7d6399fbe6bf9f753347f9abab077c0ac7cd9a0aa0ab1ece316bed289259c500d3e0ce58a1043334457aa58008c1305272e2f8c185bc75077f5209e13c841a7c3bfb5be90af1053cf382b7167894979150932df731c07619207c066fb8336285c6302f2e9184968767d4d4164276fd274cb4ce28ca268521357796cf5b2fd7a8d6055da38db9c7774baff9091989a28141a3d5063b65421fb307eaa9a1496683a14841f9af46a22dfeeebd4a0485407f1ae1b643debf66c28dcd9491357a95ab46749880107b4ec779c16799ecd127c78dd9a39edceea8a5a31de25bd277bb8f51d3394b603b26329fbd897fe5cd6c48ef8c7ce4a172b8c099607766820bfecd64a5cc3f4d36a9173ec2ee5d6f57ba9e2103f0e581cd62995834589d5ac5345486bc9c902167e1f4d66d236f37474013da25d09b8d1b95303979c58d81dfc2d02c5e6ae31cfbb41b96ec28faa70d508b400468679b932799141ade97351a5dfc0fd25a9d97fd262eb0d02f9ac0075f22bce782d10f4164077c6d1b1fb8a947d6d30766ede165be5ba0b308a10f4b5e362d609f4aab4c2969e5b942310b4a33ac8938ab02592dfe858661f1c19509df4f06e47b21d29b84300c16b72d3bd1d00bb3d38dd37985b559bccebaef28b29a15da12e9adc3b2d9353f7cd0c6f65956e98ab6f615ab238f01a51a1904861e6f5b60a14deec07fef3d104909c185a1468c498475eef0f1bb02ac6ec5b75ddb4decc88698b4d11dbccc1e367a21cc53b5b683cd48796b3080a1e6deca281ff68956c819469f0ad0dcbb779ded3b7d18c8618044d8071137cbe862be7b36ca972a542c05a2a30d83f113eef050a056fd078f8599626a321d4421d85c79f5c16774c429a3026213058ba2b45188a6611e3e08073697146bf1aa8123c6ac65b3d38081f90f10abc76e8a7f4e9abd9a814e2eb2998f03153673680af8d49f943137a9ea61f16619e16e22d63743b0be7d1ca4889dcf949861bebc02652d00497113cc07c48ee209e437077bb0a8e2c8ebe536b9578843b4fae1f16dddc9513a5d4294cba8b9819a2056cf6b31d50c0cced818ab2812159f37017d8d55937eb605fd8d134ecf20aa4dd1f547125fa406e11a86bc14d089592afbe5f8048464b19d087734ae2c4c88ee494fd56847b537169c2b5fbdc8816eb0ab5f8705ccc5db323d71d1414470a0261650c3306cdf8084d9d5157595b56c07773c732fe7b1a3229b9665d13b3a8f53367d75be912224744d14a8ebbcab006a81466bd85bd0dbff24c35055077e50e621d70cf90b28b9cebd400cef29a1b536ef9361638face3fd742a490bb45adbbad4a6bc005fab7844e6e0eea8a3e0916210105d3d36a66efe518572cc18ad154e0822bee6a2efeaca85922eb0d05bf56c70cb70e08b06b23a0dac0bfa28cd428cc8ada00e26c7682f5a26db409232433adc4e29f5330688682de3b2609e5d03f09caa749170a6581e020772a7950004a8c77a1ff69d42109bf0511922f6693927a65a0012a1687d8459eb68b341282360da20fa795b4c461d367a0c0bd1bb71dd46e94607f6fe55b496ece0ea7be44a529217aa47a09200eaea0e5a8d89064b75d6434640cafaadff0e9e92fc8d2f1e2861636815a59f028dc2725523a4851863e4fb1cdcfb628bd16b16bf2664bc32c4b2e681f26595c204d57d06ea15a89b1cc1d817f568550247b27a3cad850a038f808d8d88a05fcbde64e04d3adbd7763692850247210452c30d3e6674c4c7a7d4b5410121d3f3c845cc6758f9bc2b2e8c519a5efc79850ed0ed736b1d5f66d1fe5722e4d90697e0e67610b86a6ce0b6e163d800fdcb085c122fb197c70fddc7a9ca4cc0bdc9f73142966e473ef48245330df44c39634ddc0784dc4ea1b03c7076091cc8d35d3c5c0582361081dc21a7e95628ef8afb0372a061fb776f2e85e85205c517bbecdc2d353378b8964203537e5190c3c47180be2987ba262eb3d59446761171f69df24ec1e3c6f4f122470bd88b2b682a7b9eb14e37356f268457c68ac5c3c951a3ecf90495327b6137f283a1a831fd3abaf3656ec42afbc2330eb07a74b326cf68403a4fabb21040680fa63c45a208a98ed53a5211b4d0ed88c28fdd38f5758e53ea118cca426462aa6178389d18bed9ff13a13f6827b45df8a246e2120f7abb59ba1476c25860295ee7f12dec1a850cc0d3067354ef7ecbd7bd43a359b0226d0dba72c3fb9452483186012d45264775d7d46e66600e1856ae6028a035e1a4fe0b256e4a88f457dd8399e50c7a398954729d38d17e06d87cd6493e240f4dd1d745c4a06dbafc4e191338ebc9ce880dfa91c9b72f772a2752cd0002aa7068b26db449669ec8c9025201a19cb0922e1d5443bd4953c074836f3a4234ec45b3e676a13972bfaa742ae482c39d67c072e7b6e84d8156309433b7e527db34c4fa2271e2665aeba266301885055bf3c10ea0fceaecbaa858903b716914ba59d83dd3026263c79dcf13ee4dc4f883c0c6a02a12f392cf5267ea86beec99c899042702ca4d4386893b14c40d2482ccc0925d174b1e1fc7990786b0200fbcc501260e648194c3b3ffb87ee8c69b00c95245c5f4c35dca94ff468582691b40725c1fd898074ce4a4201296bc2b39d3f58889db25be33df67ebd3ab895d2797c97c4558c41749a507797c41e57a155fc1749e46c8266b9ecce9e6312ec47db26e8d248116cab45212a651f5d64534de8229e723c72b89da429036050e9e6e17f1b6763c09202408687dc2a47cba95994997541079fe8b0a2dc915072e64601a6ede530bf93f09b1f73dfc0b44fb5c0108a50f27aed46d36faf2911b6ee3bb6ccffb6dd44e887e2625e602a4f2cfde0904b77ae7933e4e68ca6a86850b0dacaebd9f1e2f4d54b506ad926abf4340a4d7ff808df6fd9b40121ba78e0ba200b286903a929d81471e31381e07dff25e31aa3107763c0b8a26d87cc6bad38fc01af16472e8a532f7906460d8f2d3230667cc76c6d80fd6e37b79709c3ad31aa0b79a6f9bdda008670e1bc651441766a23c8a05e1b02b1897d7b8a68bc6580d136365a85b7ffdccfb35b00b8a9179775c52fb8b7b0d46a9d983aa5fac07cf89756ccb1f078539f54a967166f14f7781f70fe209fe69d0c61d9c66c67e3f558daa0ced6de20f87e38784d287ab7e2f1f939e25de792548fc13cb88c51b77c423cd81b8498f7aa976f4270101c363ea590db1faa9280e41b146a60eb40b6913983a565cfe4b4bf6d4b9c7f3aee0db224d000de2bffba87087d432db11f71211f3e5df9da93d3e2a12d5c633ba97e5361a897c5ab4cb3c26905cefa4c09b31e2cdabef94d6eb7640b9acfe9306565cfe46c2e545104ef6c7e8045e7c1357c21325ec0d1da3451f2bd9d4e41eaedcbe1955b7c41367ee87a134388577dbce66b7fe91c4136375f6bb31e87a5ad77682207b15efb721f3638f8924600124f05288c9b3a21f78032152f6a6b48b395c0b3556e23ffdbe70333bd9d301c843b1d0a44df02d23bd7b03da298f3d107b09921f0e07f3b92425330bf69faafe3422dcc43afb1a12b2e090aeb13e4f5dd90c63a7668eba74ea5792f8831757c00998b5194148ca6a62c8e45b543c13e6d2b44c9f7d0f98248fdc82961026d9603fc25679e30154987ba6d50fedd5188045d9cc0d610343ea7931b347f765a12b05fb9934045033f1807749105d8db240872f4db5e2c06ebad2ffbcc2e26126ec42252d8fc129484a16f97a431553642834509733af9aad4b54586c898e4cd91ed9cf0673cc398950efe78623e0c20bccac83f71eeef0f8a763da94b08acd4f7a6502e643f40c777902ed33cc8a3f6b919af4aba0ef2b68fe5ee70f15ab743d67023e5ca13d659bd0f17a7358db4e5ae80455d5c017f6ed3f3971876bd9be56bf2a85c15926ce1e145fa11114756cf2ae51bf1f2e63444d6b204cbb8b03b89e393b88f5f958ba8af4b586cbc4c9be238e6aad69c4193742d6ce9291ce9ad0e261015b2a88c1c4e59304e75735aadbb48612f64ca373ca8411b10e37ddfa5c23fc5238e1c3b19970a2b5fa265cda612002c7c945ac466648062a0c1bf772d2becdf2259ed5a2f1de5bc4ae33eae24aff48e155e96c5ab4b113d825138cb70606501beffa4a15e5d74073a2db77bca2efed6e8a068be79d29e9353463c15c8559bbb0a8b2351d3c3b81c6a1908ecfae7173ebc18113f979168f7e9fb1550e56177949d618c58d3f126d68018a7c4c36c165afa010458e1a19148fcd4fcb87e2da44704fa4f3bab5c30145a4d07998ce5c354b49dcbe3d29a5a269d22d7d66deb35b42eb7a77eb363b0228c7b210452e64f9cfec0873e047127731cf34677bd0e9c838baa691761bd61abde0f109832dfaae5f27c34d6a06051486b7b77db726f29539232540ace0a630a2b5b63f498b596bb775badf09f6084db46f34663fa1385d4714c7b9036f469764c874e38e2faf78ff42729a963edd33f2d6b2022229a51ed0869ca6690d0d04c1ec520011b666cfacc9f9c1c1c3b7ae41871479be9487f327d7b57ef30398b89384476e8bb91dca1ef3387d11d218a349b03429e320803a40ec5530769d3df30397ea9ec35b20d153c16934191d98ee5f028663ce2548cc35d0efbf0ea520fdf0ecfa8e0e2d8f0cc3a12abe4f602244cee5cb185344278e4ce21211a38dc117834fc941c0c7c6bef5dad5c7e39db1652c0e619b82f9220f10c934bbf77ea7b2514ddd982f725fb43bc2ffd90ee2dcb47e9bd2fbd873bfb1d2e6169b175d7c491b46e2af3b6f7e6cb7f61de9878ca80489dc6330869b33d87236fb07870df7d67cff2e9f752c41d25510fefa7bc418197443f65c79d34776ddbbfe0b012163fd33d59bb6ddb865be81d0fcf99216e7dcae11390a87182dbd34ac3a68ff7dbbc62edbda00eee3bdaf6d3675ec9db0361c9db759fb217a7cfddb66ddbe44e28c40d62c211576cb105b087f7f73a6ccaa46b7b6b370e6fef9542ebae59caf2715670fb913622b44b49ccb6e6ba7d2802b891404a92225f0c7cb626410c5b802007d91757146104992c70b0c2050cbce80112b4a0527ce0a2a1b03d77e87aee6005122c5cb22c42f2446ec9b208899455a783f77ccedab2e240df52aed65ab17feeee95d659c35f407fe28f72fb277569730d5bfe5a6c23fcbd696d2bcf64cfbf2e94c91efdba9095bdfa75614cf6b6af0b61648ffbba70953dfb75214c5665aff475e14bf6bcaf0b532ed903bf2e6cc99ee9ebc29bbdd3d785a7ecddaf0b4dd96bf9ba10cc9ecbd7855ff6505f177ad94b7d2f5f1776d9537d5d68b307f3752197bdd5d7855bf6607c5d58b317e3b1be2ef4ecc97c5dd8d99bf9ba7066af35d3a2a99fca14b3363c830ed40a34df37d4f9787e94d24a316d91d2ed6b9dd551405531b8fef2638070fd37775fb5986a517edd2826929b099d9712d132ab2f85f36869aee1fdec479d486e1aa3b925c5a548485b9fe2e64ee0e2f6cfaffbfb32e8df9765bf6aeaa57c55ff99ecca385a08fad57bfee8fe94d66dabdb4b1cdd656fca292735d52a324b9e3dfba86b77d3aedd9614b79fc6a06df7a3905c393cde39aa90e7afb2ecb467c938fa42a96d72ceef580d6340923124a42c3d9182dada90690cabd5bdde1fa4c683d85793722621b9d73ae79c73942185c32bbd92c74f4c32652df64b09aefc71be7455e92fadb532a6a4c689156d146fd09e34d440739318ebc7dca93f772af6f976ea5f77c967f9cfcc3a65c72391dcc402155b77fdecc2f3bafb4a29e7fc666936c54879ba1b7b373fb8fd5248daf4cd143753dcf8a0c6dff88000518a6c000829638599fd63fe857e18ef591c13f332312ff3ac897fc4bc0ceb078c673d8c67d51a0d5cfb758993055c5974a48b3c73c8b2c8c816790723a8ec6084fb2678f148245f16c8026740ec83a6f53438c884b530ab9bc1ac6e06b3ba21f2a8339964fc8c909a6f59158408bf84ba81394aef2940e61242d60cc3d6d3fc8f99c77808fe1971c8bb207730164264a78559d347f8ad9fdffa9fc1425aff2f64e6c3dac5e3cca6973b3558bacb11d07d13166b3ecffc1f319ee669b010229ed7df04758078a4197c8f559a0959a52f95b08fd6d36021257ceb85c8f89927b22341560be18f7f56a9f5343f64063f919d1f345ff3357848eb6930ab8469f041f3180bc97157adbf43eee0e81270e963e027f3e58e0c5cf212eed1e27f2f77c050e6d61de28e32e97b1b4010cb16c1fb63e65b3f837fb486d0601cee32e116963b263cca1a11108fa5ff7e89f92505b4585b40560bfe320fc45f06b380f4b3f0cc0cf84258a56fbddc99c1acf9342f7740ec03ffcccf6021ac1266b5107eeb7fccfcff90d6fff80f3fc443667e0763d6c44f83870c011fe3fb3f688680d703c3074d263c7636fdcbe33044a1b08f184f0386a56781d8470c1fe16356e94788f1833f4f6f7a80ea1486d8745281e12964b15ac04ff33ff0fff889ba1e1e3ba33ec5327d8ca7f91bb258ab373dcccf67b1540ffecb97fe3e0e592b13fe1103d36016f83160304b855b88f134cf02f18f18341f3e7ed60bfe113efe103f0ebf61cba31ee0f2a59619646e7109679879e251ce9699670c2dca84a17d56685f82134be0c9bdff99f048249b7e965e3af8f369c0a720eed1a2e9f4a5f0fb1b8e1eaea11be439bd770a4b35f8ec7ece84ac39e79cb384470ce4d2cf70b4add0f43398059a300b047f060b7902621f334f8385e07017f8332f04dc99e1875f903b5e49e2202d5656963bca208db2fd69ebc784720746287770f8341149b52a09504e94a25028144ad51406068f44f244499a7d1caee2662ae5a1be460317f5f463b4b8281898944af5425e1ee5d4b75e857dd0d0a8706a4e0a5a87b57ebef45a1fce2009f2a872c495b58ffa71458353293c06c93327d31818f518e3b80bf5385a4a7fad295377869a28262da7934ca70cc6a3ecafc2710a8d32c89a0e59a80c65593465c9941b321330c7f631ef12c6bc3743160a8f4482568fe3309a8781791c0e83799a10e743e92e18cf92f9182f775a3f7e9e333298d5c23ffe1fad8f8187d07ceb311ef2c407cdb77e48f82f669c8c7188c35da96f8538ee4a3d4d88e32e98ff3035e66418d54f14ea67d8a3451814cce368160c663d0e6785f655e178736a944839f5a9d4cf9048f65621057950dec340a94279bfe55b212bf5e394ad302b8573d830c135fdcdf956bfe6d351ce685a3f811cf64f514f5533e8f412b42da6b0a79e26f5ad30f51fa61e26a44067d54b1c0de6001fe65121ccabc2514856a5469a51ef2002b815b62da2ba57955cde419eaac2a3ac65555086f954ea2dea671516ea5b307a14099fe65928cc6ae1bff53efe5b2166a14eff78855b58080d66a1708f22533683dc45ffa5bb508fc2234e9e6f23e8c678fc25bc90273e5a1fbe90273efe5b2fe4890f19ff4f83853cf141f32560212f7c88853cf1113e0d161243885b8f85c818a7ec4749f4a33c7ab94383553f4aa316c67197eae5cee37797ea670c8c8910c932d5a345d5c3843248a33c55ff1272d816e97025cb221d92e47116e950840785472119158e34a77e94443f7172eb5b44763e9c3b611886447668c2e9a3821097e65bdfa3080d6e61e9ae897178f7a8d093b925bce1388dc85428d3378560387adf79b8c3d257a83fbfce8914d32b1c3cefdedca3b294dc512209e997046ae8570d1b6ab458e5e4da478ddfdebd46769ceceefd9bb51656e4c936f390ca53915b67873d64a8912d7ee1e78eb216a4014d80eeebc7506590b5fc5776655c28b270b8638e01fa35d6d079cb51a1fe0eef041f5c58f7f2fbbaad84679176d5f7701a69f1c75db5fe093db8e3ca48bbead7b7b57e2de18beb0fc41179a33ffdc075e17166d77b9e9c19f47a4a2adb57c115c0d583df87f3311f53f3aaef8b898989514da479e4a556dff77d48dff77d156cc5e08ed3c7915a31b8d3c767b5f23e26f4bec529b36a71e2f083432c8b38002165f1c5a7e5180e70c007e49a08e535d30bf8f2792f251105f167163f0cc598989897a04a15d3a06d31d5443a623e7ccff33c55ea1be4f126e8799ee5c01c312f23a59c3eaa4d05e68879eebfd0c62402f2787dcee3e85438fe2ae78e01a34ce206d9ff2493a0616ef209a1ec93ce49f3c9d4a6eec658b264cf4103a2f68706945b3e4f56b11657e6ba7932262efd96a134cbb46ba6009298a66902bb3fc21205a99bf334815c9137a6dcfe0ca4e6b01e2cedf2286a8deba53241d91fe469a9954b41765d3e85216e4dad86d62e9f2e26ae9c123392251517c8324f4e094fdcfe31c7014b5a74160b4c80b2bfb300d4afce0ec423894717fe2c1461c1c79f851f169224657f1664fd7ac9fe2c2ce9975ca209a0eccf42109d0e009205f56b94542c7100130738e957a5c17480cc014bfa35ca28b35933a586e825558bd5f470c1cfb34cc48514fd2f0fa4f766b8e5140adb58f51cf15043aba119716b68996be1ea73554ab9a3d820fb4b589ec98c8484f51c4db9c1e18eb2762f0f2e782089220e4ea908f5f028a5f03122c5cf298914b2ec522cc9ee0501794c3c275230c91e508b3e7b36f6642d7aebe0f2fc19569f1fe9ea39029a228dd41075ed40eef7a01ea017d7fb51c2d0d97043f6dd90b5d8dfcfc0e50f869f161d06232d7ac3e0d3a27758661898f40b06188ab488420e77f46cc234ccaf148e528a22520a9f7c6f142320b57ef595232c4090b2004902d2050cb1262f2f7393d4fbc350a45f271f23fdfa812149bf4e5887cc321896c000d42f8a9ba0de1f0626fd6235f698b4e8ef2d9161e97679b270886b0987fa157343091c140d85593f548f7ad48b91c3583e500ff3303f5243509f7ab9837a223b13f5e3057bd89bb96ba65a5a5c6a475e2416f3a91dcd192486574d96efa1bef95e92c3503f7ff2fc8ce449e96724499ecdbc17eccd584979fe0b1e3da21ea429927af41cb98b8a965778d4ae9576485ea50da4d6af514e410272c5290fa47eb950c48a447da9971790e7ce15c873bf6e3568144b6963fa5a8396fd4f9b09f720b5e87fc31e2c3d4732556e6d56579f97cffa0bcc475f2607777b14a53f6eb1148ad60d66f5c1f8649eb8b53f266dfabd188be226b0a8926513588c009065135898e40aa3fbf44d373ae7349dde9b734e9134ca29bac8b216cbde7354a3f983d10303e4e9c1d2a22a8b59fe0d2a7794531c4d8114c3f241478e99b419abd00c93f5c97e8962167375c105d44b77398f0a4a74d4dfe836b76ddba48c69ac404ee9a03a6c8bb3e22b23333333e5fc36c9d6d773d2404515945cfa598a8e26a8a70e43e1991918ab150c8c419edbf198617e912930837ff1c5045550724bdf7d3fadbfe1e89e29983ccab0ca52b630cf40992f6cf11496c2164c7642ee150f2467c5e0cef729dd4d8d61345f4fd9e2fcafb586d8e22f648c2fcfbe12660f2a0cf54d07ee28a3a80589c9578d1352bc42be6a683d480e4b7dcd277bedc70999f74b4ab1f15891e47ef73c3459134721cb1b1e6b50a62df3627cdf12a933a54dbf8cafeb41cb15758b3c2514257c35337ec60c9087a3a20940ac485d2d326a1545117a8ebea3206e4d30c1041a0da001eaa03988b4f1afd18184bd601ea93b458d153c90909830f3645b5099695bb9d22fd30c8b294a1747c4b2632058808cf288224090801c01a901b9d2a2df28baa3acd520201a353e2a6f72b8433e947dac31eb7ec199a851518c5a33b83580b23fea533f7a403580bc469037ae01d42f54ceb31b4b5c290025ee588334936d91b2bf8d0dc85353e5e6e78e9e2c4b295f70cf91bbfc53d6de97973c8a20612fb82775a77c911d53c101a78606f2d45469d17b8e78b823cca706f381197199bbfcadd0f564d953c84013b99fd25278d47faaa7201965bfa2240348ad3c710364d2c6e702f5cb26fb7b4c1a16858a939c42f14072180a6729edf22949f966071e729661aa1fe51447b518c220ee6ad4a7de5937b8a9b067863da8b027c95ddeb8e7a8c59824d4c61a5af6a7dfa30cab5cbfc54c946729195cf125bcfda8efaf31b23981bb39926752274524753aabf209df09379fa4b4be8ae88e328a5acf11ad1d865927889ea31ea406c8a48e8a0749ea9cde3f4b99efff49293da8c116d92591114e8b27dce196eb46c3f66e3e0fe793fdb238287c37462e0f520c0a28803c5c0c00be09ea287de3f186121701decf921494d05c73ba9936d37b9e9ddeb314222afdaa6fd4affbfed94abf2ace44a7305369f1e22ce54433ad457f5398c1301bb528df5e8765234f85999689dc95672dee70bbef7b52007370991691c759b3ff8534ccecf9c8508570ba96fd3b907e0d5914e708a99cde079a4e7d5b9cbaa052f54505b38211c3929969d17cb87138868c126aec0cd1654247a386cd093738b23fca2c7b9e39ecc64f26aa926b194b92298ea264c92ea350c95ee48125799451ae64cfa38c72943d0a52f62849d97fabb4e7a8074bbf3a23d9a910ba3d9bccc0933262720a4a563545bf16536072ed8bfb9c927165defbc57ce306d7f3c95dc3cc9f0c63fb93a7b724cfc769247f2edb7ec9642985a4ce5057933aaa2cc75496b04fba52f4351b4b1d2f668d625315122a0de1ae26bb3d549a499d23a9a3a24627244fca18712bcba71f874bb2cd7f3e30fb0896aaa90651f2785a22cba61a647f1ac224491b2799069a4f4bb27f0d51442dfaeb7871fb4714914cf674bab8238a28053405799c31a2427926266d5c4776fbc719235af69f319aa9611fa9a3ca8e22ca3e93718d084fca10401277ac31d616d77facb1ecf6c33a644e7daeef620d1c0eeb902a649fd87fbb31e5caec9fcafeb6fef4073c19e4ca221faae4fe79c5bb0fa83fe748411e200f0be4ea74fa03e6f70af3c869fbdbaf2c3dcb593bede4388ee36aa972dcdb0770b766eebd7636734ff303b8fad4e2b9235760adc0e24163b15c3f572c2d53457fe92e6677aeaefc65fcdf57b5d64a25a5a1adb91f4767dfc29adfbd56fa8dc5f356fddabc66979f2292532228f241ca7dc0f68d2f8ffe5a9423cd75f2e061813aa9142b1ea9ac0fa0fe54bceab552f1675684840ba0a21e5650a9fb6084bbcc30a11ed02c151f5891c74fe80737e411c707b5eead521f1421512437a5947a08ae406319c89ec70b64fa8ea5e4415fe207f8016456520b191919191999ee362fb4d26567dbfad372d972145cc1e6effc56aefd6aa052a4d6062a843a0a5c6635e5a4ee33db03125638a14e4800b6176029cba21e4a800324900051274aa438913b370a50361063c1076a55be903d48410f412f18a20a124c8e6e52a7031250e050ad7454b8954e07d5ca8deebdc84b6c9765510f3e5809000b964082021b3401e58824274042104514a5204b111fa0229841877082225105169523f26e309d204977829f1b3924b02e23afecc5e1460f3aa4b8c8540b8f0b4fca18eb4fdede731fcfc1083746f3367b623a9b37db6f8de911d2667bfad3bf3dfda12fa03f5b6a436d2e5b0ba6474f65ba901bb99043e1422ee4426ffaef4b75868871956707354dea70bf6ddf4dc817bd428adb6f2d98ceee8ff6e2b14607a54e78b4a6edbae015eeb04f8ece1ca647d266fb138ea0b3a4a48eb54fbf3c233f9da45f326f20bf8200ee2dcdad1cdcd2fb95120fee2dae34b8a033d68f212ed2a826755ac22137b43cea85a030eb87cb10d4bbbcdc11d2f2a8166cddf564db36af49d7f676dbb66ddbb66ddbb62d07c78e1e2f04c931e26e6f57bfbd88336fa442223bf488ce286da3b35c7fbc3eed2047765dd7755de7799e47c548894b92f592581981bbc23bc27647918a2cab7e9c7efbfdd504a62e281355fc7c5ed8eff7215312da3dcff33ccfc3f032e141eea7f68b1bb9dffe9c9ee8adbcf74ad86209833bb3ccb308d9620e8f385b75ec92fbaa80f04e40e276de4fead5efceafe10c18f8199c043946dc91ce2811a551235aa3475ef39a1f399227d118f5f19807491d7ffa1ef312c817ad98fe04b320df6a131226e9aad6996b8d2261a174f9e7b4b86e5081dc455f46a61b664956fde7febd0872589df96ae2f1d6990cc5dbac45040742daf8534a931c460329c6c4654eb2bb7b0dca5edb8170992ff1228f9fd75aab0321759aa707f51a6bd1a1dc20d2c6dfae9a7bb73fb664b73fbe64b73fc264f720ba854dbab75c57abd459a5b5e844b31a8259aa461bfc29ed78589ef04c5a4873c395eeeaa7f14c1a0d072eab76f7f347177a2f926c70d8a4c9499b575adcf0685d68d2f0acd9e043429336fbab4c239a4c9ab8895d49a27731f79855a6118dc60bee7c1c706f59f6c52d64594c838ffafe34b82077de5ddcd3c0fd0c2ec89ded1d0b11b2c2c99cd7973b746723b25343223b3638acf5c3fd8c19e124cd4912ee04b42749702f99e4712ee1384e28735fb21cedc992ccbd02fad5f190b91d3267adb539fd92b413042173af42bf4ac8dcd7d0067771f3c75d2eee71340088ee2893f2fc06c00dae0d3e4930fafedc0ec7f5f72842df7fceea90d759e4ec9928ee688566a4b8a31ddb869d63851c6685ae5006e26ad9ab9594508ed49943b3ec442b5af6e7ace82f0009eb2da4cbbf3d86a404956b983ff37cda595a74f74ea888992866a4b8fe6fe53656c19332c6d36d4741dad41f698b08b794475aeadc5f763b1c26258e0d5352fd9c9f8e6db4583b7feffda459d2d5f4edfffb0e3bf6bec34f29d8a3c8e7dd704bb8c3fd230e8eb4a94fc381add6df30abfbd2734f6bad5f6badd5ce9a0c3b1a164fca18b75aadda98475276ff1060fbb19303f0f99ded4750051f9c9d986dcb63c764370522ae0e36d926db803627dbd026c568a3b2c936a1cd886db6116db49a0e8cac18e9a0419e1e74c906b4c9a05cff7193e56e1bb96f3fa5430ea3814467548cfa35fba8a9155aa347743652bfc68ef55163e917854177a48fdcdd6352e7a957c326dc6fa18fddd9de86453890b3b3bd8fdde1a8e4915239ca43211da244e1151a826bbbeea933611eb661a3f438e8564a582cddc5613a94c73edab0f4fef1843cf6111d72571120dcf6c11d6b910611b7e66df192021177b491b7f7ba7923c41db758cf6dbb34aed22e157733da68f346957ebdc8b6df921c46dfdebb7a2e96b7bf3f7f7aefc3dff3def1f5f13d8885f87ff86e3469d31ac20d3723afd68e3723776db4ba516e11e1cef7c2edbb9574df277813c41d3b48878c3c3770592bb06c8b5fb6f3bd886d31876db4d5b644121fc8253cded047d8402d6e9badb1bc7d953ca67ad01a87d4996f646bf7688594379a94b7ae92695de4edb3846d4d48d7f6566eb58d266db6bf51d222c255b5887063f4f097e00c43f24824b37270555adc7e7e91c30ddccd68a3b5b83d8d19dcde5ebe20d1a0a54528cf9fe1c19db972678d8a3bf68fd4a9797e27b94652c787c877d77c2f74e7da364e4e0987466b5aa5d1ba76a9f41f1ef2c4c7101924865aad7f1cc6f2c9f2ee7d78dfbd7f87853cf1ee4b9ef74476bec7e27effd8e0ae1933f1e0f2c420cbef3e4bffb84be6ae190a71a7cf71fed46eff8c0f6effcc0fae909090909cf3a74520617267fe48e9f24bf174f796596b51b608d976a93253e58eb37b5a7279fadf65bacda7d2c336fc5aa48f82cf1de52c16933aa5223e4b648a7a0aba7debb0475d19f777f2307d7d22d9dddae738e724912cef7b38f6957cdf86a304f2bd61f7a7b0f4a650b6e88d53966ab1f350f0b952babb944f5d22492ceff49d558bc56219483281a6932428990e511a3485aa4b1bfa2595d0810a8541a6524a49658884a481e46e5586ab16e96f61096ea56f4212aefc0bd425e179275ca12101e0792928b9325a4c81c995d27573e4e6c8cd919b2337476668d97f66abd5a75dfedeeeeef333447fa4fed23b16b81c491b370d19310d350c92b4f1d590c320ad8850442b9a4c92d451998666689e8527658c357e327d6fce39533018a92c0303d7d28ca7e79c73fa0ac68b0a66868fc36060d4cc5df7aeb0f49a83b499d567decc97336833aab869c834d42f234cb3af32493368fd72f9f933aacc90396c45e4337c6618e957e9e7cf983f23899c8d38e26986cf29cb90c9583273980c1c64f060650091276a264303529caf0a637c21c5f92f610c24f8cc1b3192e64d0e318e90e7cf4f85318e626069713e2a8c8114c6c8d2e21c710c13612a440ec3abd1fe154d785ce18c2fe218cefc52888b601fecb9b7f29741c7461cfbe4898bb438dd318eb598d4458bdd333199a416a74c529ef3659264ba98f36b5015aa43ee4f6952677b23d9dd578fa363c2eb2e7f18a19c3f968ce4f9311d0bf2c4395a7973b982a6c03494296ad62efaf4561f50c74cde46972b321d51b34cb91f61b60883a40d5d11c924b5485fc78b5b6b84a40d7d9d2eae4cd27c5a23547f9c816399fe0c1fa993caf467fcd018923aaa3048ea704f659232a54233b986e3aac64faa5b66605ba4c2795e66b6fdeaf1bb593ecc8f2bd8eac7ebf22345a55ee6b015ee2077f9e3eb47481befd9bcf1afb578a5c5ee80b27fcb77401d932ee8258dd640af3995d12514a899f48bce7270c4a7b2539685bda4456f1ca4cbdf9b87ec0e446b408a5f48d1df259c4890a27fcfe68d99346f8e903a476809e7d1c4226ba21d2cb4e8f7c3ee787c91c335fab5d8ad0fcc9ed3536e222a2dfad39a2806923b36918c8888ca3aa154773a7894fa69e6f57bb4c58903ea28e5d651cad505dfe6f47ef76f2127877add2a4779c61c50472953504729ebe0b8d56a03754c0b76bf479b524ae994d6d4a08efa5d71632b6d4e39668f221bd8c0d4114a9bf9a1b4b19d8ece412da863b664d9813a2807eab8b7051d5c09742197401d3353efc4337679fed8d1f7befebeeffba6b4260aeab0b82467a99dd68db32553fd0d57d60aa57102d931913abb291e7d8f86373edcf932266d64cf9063263b6e9c8388cf15a8a0efb4ab894e3f71208f69036f0d92bb7ded544fa7c779f673b7a72f4ec0703c6119aa50be7f4f9745b76af2fcbbf9b932546ecd28773cd6a0596794823ca50f12eb49ea419a1cc853fa7b3f0525dde0373dd1d34c0ae2d3afed67eec1d22fd993a5a78b7ec92045fa35710fd266da7a927aba08126bd1bf7fef6f2843157a567fa52d8622709c37a7f75ef705f1f1ac37bda9006e00e2d102b40c56a1b0fbae95524a29cd46609e1db9a5ed65a84235b41aa3f0da59baf100ebc70babffe577502921ac1fa857bd0a0f61356eb27ad4ab1ef5722795e7a70a334cb80af3ac03f0bd57c1ef842b20988346e6817480598bbe84947cc29591726d92ac15668cc563c672e3185af49fb9732a0c12c56d6bef5dbd28daafbf07c11de595a4ec2d1ced4c6d92408e3a00fe3048d95f1545920bbd03a4e62e7fd592eba51e853311f803f5f4ca4bbf9272ad274bc382201539aa463211d842dd79c12d0ddcbafd3c0dee8b50f6643d4750b24bc952da4563c604977aaf72a3427bed4c2eb1683ec16d7a93a4070b0fa4da9e109eec7493e47a42b0908407120f242a496a161e493cba88b16084455948b2840a132a41549c5011a20225fbd3aac3c5279b4e271e591af340e2e19ede54e3045843bb325c667582b65afd83d763623a6d8f235223d463b264a314476f3287a1308c2cfb732d15467643895bbf960021a9d3ef9f80a12d1395debb17e499c9f2bd1494dcd34bd0f4a93007adfb20e0bbd71ae72092dd939702935646210477945194649e2f0ede118ead76a80e4079228f72e65181b2c4132851a6dc529645505c60e55ea1a11c402d66560ba22b544407849024c444072e868482c8610b9f2d7eaed091235d1633af8824f06091804a4564f1c37161d415d183239b10862c8e7dc248c5c9c1b19163e42e69718aa8c10fb5d77e778b2177495bc40b8c607154440a7cb0002a020809164445f8704408b1e6b8d256a9e3e0d8c85161c61dc982491659f0e015c9220359c46ec062081cc0620b19606145c7040b28bcc0a2099c2c99d02207b4227b5770451114225c66965c9ff60b054b7df0a428fb0a82a0e2f6f522ccacddc244bbbb9bae54b044767b83b692c7546e192129723fed4ec192dc29487a1b2c454247c048c136c51b9d813cff7dfaf49942a6a2bbbcfb4192a11d4a7d468362eb46d0483c1a13d098e0963a2582fb49970609ee482912d856a9ff0863c3d55a8ab1cdb141022eb8adc47a9184a04b33458de2ae6cd0c57d5141698b9b020157c5f5b22c42810f1e1614e0b07d71c123949070bf2cba27b7a3a21be27656381b5c5bc5aa608922ee9665d1131b7826b85c96454f664f86b6adabd45bce701860070b56ecb684bd56b4b7f4413ce1438b4a104d10444766a6d45a41c84007992664334134f98105c5904c10497c888942c652420410c0a8421403f258253b50b2a2920443090d400073839f9592263255133518253f40bcdca0a6baa249ca09da4b076e976591ac96479c291e70594228d5020fb4d080504420b9472e40ec70324095580b0ff6bbbbbba1f07197b5321f1cc02866272e3cf04d1132e15820ac30e2e1e4e0d8c8c98191831688288e949e307aa2f601c184914e07419ea5c562b10d5819646bfbadb56f6d78ed8ff6b3f81611b54bd2acd1adb54b161dbde884338014409d10483603d0902c8e05e2c80e152707c7468e911323289cc5c9a284da6bbfab84358012f6b77be5c70d4044542d0e0e7a704236ebc1c8e807284672f0433bc4b191a342d73835218e88c8a3b54637264b268e74b046e06eb8a620944e705da028f9705335d8a0b897059d066e15f238c0e488eb65c984154a2ced7e593261c511ab81db65c98415459a30514517435c54964c5471c50c7e50e2a20a1c507155593251851145b85b964c54d1028e04d70439dc52964c5401b4a1e0d22c99a862899d794593251357dc608952ab9135591bf7d9affb6628a548221b3900d903bf19cac81e4864ce39c5f34ceefddb56375ca7fbb7c232fbaebc4bb673574da19a423585947de2dc7b73afeeabb7978aeeeefe77e53873dadb2b7777c7a3bfd8d3dddda59527d3cc0e663bad9b69e32cc8c28e10dcf99b3d759f77258fd6dd2dfee072a5d2aa7a5fd7ed7097a4d113f7a33c422785bc6307c5e3e7e8bcc307f7e2add26eabb424679b1cefb092522b613b24ad524b4da7d3d6e4cef770c7775b2815716e4ecbc9c52dceec7339a1daae5e94d451224e2e853b2e4dbd48f93b5aa43bfaefc9b4432696bd66d9ec0a3f7bf59bdba55c01a44d5873099995637af69ba14c6afc6860e598998eac16e68ec86a61eee0e82a0d36d750f3953f7f622b3d726aa4ec8cd900ce6e771b2e7a77535a719204dc25dff4cd90b5c2fc949ddf0c6df6ae6cd972ce79e7cc3dace7f2bd6497d06617ea02b224ee51b1cc3ee46c87621794b747450dd5f30ad66e63d912b8d076c7ea21524ae9a525ef132a80b411c2d24628d7d0f9fb64bf6452239316bfe290e51ac416e58f5b3876d9c3b104d67a2f7d9956aad35a4d3d726aa46c920bca5f56234fbda8589036f26d4c2a4a1bf9326ba5fd9a5422f56baa66aabaed6aaa783ebdfd47eaeeeed4eb2866a9fa46560f9b5b684d95d1ce55bb3491a917fdd1dde9479d526c5194baf8b7c2d24229a594522aa594525eea5e4f9b9b2848f3c30f3f34e09b18892c697ca0f1008d07683c70e7734f93e4ceb74f93c413e58c7e291929e5cb9f927ae9a39452ea811fa594522a3ff497e001bc866a29e5b85126d91a34afdbd738b16d55523979f56be9a4d49b6609a5d4be648d1332a688565ab797af5f5d75aedb5ce6c4558a95ea0045e9d672c39e70dbe6c453dad4a695d62d884ab71f4c99f6b0d1dac3466b0fb5a6c0156af64a371e74d8a80e558723b43140f35e105e82eea48955b5d1fac3b6fd50e9f6c3121baddb0fdb0f95feb004adb46e956e3fd43aa5582f909d565ab7252add7ed8b696aefa2ea14bae3f2190fd279dd2a66e7ee9122e271badb46e9ced4ab6049a6ecb0b4cca49bb9a06042e94c75cc17069a15e89ab265030be29612e2f5b5ab24bd8127e908ff9649e52c75ffa64b15832a7ebf232c7256c097be72ab9f3c7297ba1fd924ee74cab01fdf23f62a5234dc8f2b18475cc7672052387728a9422cbff41965f0009eba08eb9c7fa551d84b19191d3ec906ad17caff35519f938707678173660e22046ee0c65e0700c1b968090169be6e7259c3ebe83c23386e486bd3323d092e63d2a5ede4b70a7c49d65312145aa0257a0d9a3a101794c4fd3c5b03628c0fb7cbcd0c2289dc8f27b489d0e46bbc9f26de48d49933af6af8c3c85a89c4ea79309acf295b0aaab9c11cde8c50a51e1846aaa8751931d1ec6200ff7b8cae02460c3aa906964f92e79630ad5c0594da32af3a67fe47207b564a1611dd44156a851956e4655aae47e979630a7098e0c1932b6547b597e276f4ca3d9afee67373ba87f5a4ab094d29a1a90877ecd0c9dede57f527458cbe33cc74da379d3ff20dd3a66748de8ec02e6a8d9710779e0b66c79f9de0de54e8b4bd812813cef0de74ec5d973d1895b6e58744d50a5502e2f34539b1084dab41249c68a9ef090e7a8429e2f2860ce62b19891f976d6272a4db5916bad555af1b94145b9feaa044c72fd174b8044ae8f638225b97e4ebf649109b6c8f57128916b2d3ac116b916394122d75aca336c3245f66f6bab0ca8bdab3b6f8e104e2e68128294c5e20919227e0023411099fe8a045a64faa8878194d274a136882822b7c542842cd3a77f2d92124c7c9260c244a67fd2e21bc11399ee20d3215490e99bee16320699529b452a258222dc11e0c04357f28b243e80f2c654144041c8fd5704508090e4fed2ca065fbc7426d0426045082c110a4cdc0938b8020440f0f41232a0596a755aa2280201073ee024f7d3dba334eaeedfe1a3bbbb291414534a7fb491c54cdf5e1764faab9f4c7b4025d3a2252ac8b4e80346321582874c738e108277670471945be58427658c2eb56cfd732921119dcda27428b06e2e02e9b23e8b99373a037ee4b07eb77fa9f099d491525c2cfd403c7a2ddb9fd1c1e5fe6604b7be149793b42cf38cd11d7d56f32b3387bdbc45d11c26771c0b90bd77da0d3b90a97667963bf66f86b815bbacc597d089bc95e43e9336f65b46b848b28811f741eac8fe49010be4ab7b4bdf7ee3a07330a3982eeb17f7b6b990cd83099b3227ecaf74700d20210721db97b30a284f64fb2d0309b3c15d2a98376692d948a412358062955c1158fb0dd268e2cef71a4440fd1270b8f6ad5d4206347fd4ffe867f5403dcb05b98362fdf812808a208aedbcd8d4e3dba2b58f7279d6c53f52a977c12cd68f9747fd0b66b5a030ebe2e82b8496506cd1fe4b88e32efba9507cd3566f88a345fba87087bbecbb84d667d265df84478badb53f651b946d9bb2ed4bdb531beadb9f49ec4b0903acd6f3389354e9b99b64f882c33a236a5fba0d2795b3dda5566a141153468bf476776ff9137de9371fb8fe5ffaeee97b5d284b383eb384c91d2a373c5e49bf93aff9953e8ebbe873a1745778e5ae6e08ee10def4b95286b27c23b2446589c70b64296d743c29634cb588703fd3b488702bccd6a7c02ad3ea4cea98f2cbc7ed3bad138955e58ed75aed97700bb9cb5f08f75d0f65afb84a69d13da607a804ad9556454c199a1100000000b314002030140e08c582d16030cb1349f13d14000c7da6487a561a8bd3244751983286184200210010000020325043b4018c0f58006069b7f5681341a15507ed5c4750d5fdc46fda86218da9d6d378c9a6ccd1530461e85b913471aa077bb1becb7a0f02d3a4a2425c0dec7311fde01ab3cc8756bf877d3f069020048d8fd86e581e47d6927c29ce8a491d400fe3e21d540213116c00b940e9628904b4256b74159b79d59a5c1445d044110bb4dc02d8af8801285f6162cf65c8d71e09dabc420c44389a4aaf313815ac8789d626128ef3126112c07892d0e13ed17123260508ddac8ff740c99918b0017a590ede49683688405c23942d06bd0006a8002a2c30f01515093fd6815bd97c0435ff4dc6c46dfcce71c529a12b5d4bc9e84280066ef719a34e097404bcaa8a94408b3586bb5953634f137de1494f7bd93af997655f34e3ee69c0e36dd9333a75e990e841ab09b1f109bbc42d040f4603ae998616d10977f8a2e38d6502b6143f842c50a5563515ee9a4f6933dd70c550927864719fc0010a4d519e556f691c250b4cabbefb31108fc54d0d43e6b17804e54a54cf81b361dc79a2cc8e0bc302e568e68aeeb49ee9803e72df69d48c93f96959f6ecf9656cba64a3542f4776398a61a5c615095620a5bfccf21dd3649b1a12d9c07f6506852a5e46781d0235b13b84be4e9437ab7f98e948944da2401824a263b7236022c274a35e6111517f100e3a2f72ff07882e87b02abd73483a30cb2528e62e1247ba340d822993a422457cabd863958bc1e91246ed9283c69fd75add5c0a5502052aadb6cacf286ea0b8a221473fdc6e5fef1903811091dd1810764e857adfd177c6a1aa9cb62de1d67306b544978816f98bb33616487c22b662cb0111b348aa686ec50fbb500b68436a90bcd04080ae3b1807211720ffa7a061575eb7bb70b0527eb96766d75ea102287ba8518e3eb3242f5176b84e55db20a58aeed861405ba9af5d2148144cef8a9bf1b32739192a05c002b0485527072fd4e7293eeadc26ca903b51d4c9e7f1496f5ae6457551ea945cfa58c848175818bace80c8dae4ddee487418d3e7111345587603b824354cf71652e3c513f6046770fe3776c7ad1a59f6f001ae2c76a3d4d7c3f9a0c0486de681500921c0e09ce456102ac2c3f9b080926a217808373e87fb762f60225b61bca67056e3558c7ecae79cc64ac13ca1cb103b3cde58e3d8184ec8927528491027f336e6a6f75bd3f2d64a0cb80ac0a54a18892b0a6a778903500681ec58c5002d5f00f6155140cb1088bd451c50191562771903a44c71a4908c9d82b08aafb450ffdac089189ba81cf9d624cca60b0f5f6b0abb6985c3cfdacadd641cf8ac5f583747ee455102161d0bc6697903b2574b33b322034a2234b3a200a2fcce46f69b4e54b3e51ae745b3a902fadaede5f7de08f804e7101b0a2b4bf503770fdf30b46b8b27df2f02cd4614509905087bf6e893569348de8ab3c8974be506f9c0f274904cd8e2c9771079067b2b1cbcac219a36af0882a9af72579a0b9b38a3ec0f4e078c94bdb9bda14498e473ae1d67b0a7bc8f4b5fe28b4b9b4e69c3bd346f3ec0dd3371191773c5c43504ef71363a6c56ae7bdffa2414ce6bc44884a209f848f395b3ddebe91068624cbb190276aac08d7884e2e74ae2388bc9c68135365ee680d4ddfcfff6be749004353717814a23f9e0d5ce0b80250e28e42e16db6b641ec5d18b0e62bf0f37d4da6e00d2d1586e8a51900cea04468e53e277edfe311ca240d8de062bcd89c04cf6c55a3672554fef75b7c1fc008eb8f6a358e354e1e829430967c22f4dbe058353c1884353ce24a15b4efa531385700f821b0cf091e653150a94a353fca16fdec6a9af31288dc84374d278397de82174410cf612a3e750b96f6f649836274c474e5a333108dd0d75b773746ddd002f067903e84a206fba65f9d8fef5858f3c714b3b5ceddbc9c445194d359cef9c859d1fda047da2371afa89bbbb653f6aff9a17296e01f45d960dd73fef30136a13ee49ef4310c0bbd93f934d5d7385d82ac03bfdfb3ffc888b2ac8270bb0bcab89502f47a145c7a914a2b1f92643ed8d7edb032bdaa997168128ab232a30ca182c5ba370110e9c14bf857bfaf7b4a895526f82e9567a9320a9b60b739635ca24a64540c02839b6f50b85254d4bfc7d8ed2d2afcbe991f527cd8d7d79fc42525bf60f2921bda49d3faa175a7853d748db82af76c9451ba5fecb20a7f952e8df1c7794bc6cace4d36e6df326579e228e5d702abd32ec93f5c7527fe21193d8aa25282c215fa54773bedd48f201904800efe9f951b263a25767671cca6c746762c6b3dfa3bf34a67e9a0d786fbf8fce28083f3783be7c2e1bf01ebd39ba410832dddcc47bd2d03ae467a92ab1f59dbd0faa2ad71ec27f76e70afc0936ed2dd389db80e06efd80a73f7383069e234eca4bce08ffde8ad254fd09855d36f8048aef1f57e3119d70d472aa6f96be7e286cc5dcb60911d6c2598a0ae3289eb77e32eefa9f8fae899251669be66a6601932cdacaf9702902706303a7468b8864624d63a4ac4d6e046b0041e08a5c8380a3faed02926ccccc9c0851dc7cdbc491635681c3f9776b7cd10f701a0c98f63e2b9797a9ccb80f7b7eacb8152ad2d77554afed3005034be47c35a7947c6c94cdc72dd0eb7824b9b7a4103756816bf1cbf33a90180e615f4b000b18368eb1bd323a1f0ea16cb88fb17577d4ffeda2701211c97b81c15ffe0503434c7b9c4ed60d66d518351a06e3da22ee37c1cc219dd67706fd29a5ad127454a883c77d36c75f44eed50c20cae63db1376603b9a416b54d18545a9f8e8627f7de53a61cf89642873c2069793cb93f44d0169ce530b1b722f87f28d8354b1b510bb35fa8ab0de074cfffa8e7e5e30afdb125f22a685ec36acee7bdc1718f49bb96efa2c856627d6819d754fd10acc0e0e86ceefe6251056dc271b5de4f49deba376e228c8c69456fc1bb28668e691a51f985e54b94d6281af5c80e17adb30632ddf035a4ba6db02900152b33713cd5874cc5c920c8ae734fbfbeb2ba7ec290b894d3999e1b3447250822f81dbc0a1ff8e471591f234a3b80e7e2725233077aef9a5129ace43ca62fd2a364ecea94640ea5c0bf84bb851fdbd032b53b3d343206ab100288c031ebf937a57d37855d0ea6953416573f84182e45c851ad0c3d71449cd14d72fa988f17738d2d828ae9a4ed973cd9c9635066818b213938407d56fedfb61ed023f0f22e69eaaf09b62f7e0b677cbe54d8d5693538c59daab93dcd705617e8319ad18a13b98f90f64e9042634a632bc2a86e1176dc5d0d9a43d9f371fe3b5efce1ab0374e4cdffabce56949952acceae19e2d863e2263ce19ddab93f04147256bfeec1e87d4374aa8f83a2ca74d6c9832252532a681c07f9cdf736a35fa225163a6d8fd1849fbe4afd156fd9988dde35c829c90ff9128b0fe4c593d059254e940705e53728995478b17d95c85c70647bee82c71b6612d40a6bdd9b52615d91e1be2978c21a126e293f710cbec5bcda0a248d66e2117fcf421b9109097f78ad7eaf44b3a81edb228a321cc72ac1597fc32976940fc19aab9920384f8db32280f3b0f2a27a62176658c74027f090bc922cf3d9b39c0aa31b3a3d8050043297ea0168dd95d46edb4cd815f3999b7e12bb4849b654b9df8da4bb7ad5bd03bd765fc167c425cff115169dced3700158c5dc5da7e120eb55ae9b1beb93adfdb908c98b133d581cf8037cfc913445fd7144fc2ebdd7cb0037c7e9b356854f6577aa352fda1960986218a7a746aa042cc7ca261cd9897f361cb047c331b0e213e78ddc55a2099ee48fedab931d8c789714c38877505499645f696e9132db7327822e1f763ab1bc51f3cb6076ad0c410d4d6b450e403133ee2a6df338ded6edd6d332aeaa02cd9df02e1f637eb67517d5bbd6026ce85affeb0f8dd8a39eb353a45af6551eef5d9b9117e0be6f840c7b840ff9bab7e72cf3d2956aa26c853cd8854dbf11c0d7643e95b118077ccff4700c4dde15cbbc40ef266e021f288295107af41e03d2df6a555017ceb1dcaca93de1eb93da7eb336450aa8d14c19bd3d0b1ca3b7cc51292ec58ebb0ab0969e763188b2d50cfa31199303549d45b1162c6abe58186db8778b7279b02031bb6d1c6785f1e61285d8b754f4db709204b6bf6e88e84ad15e13147ac707149d197010901882689ab4c11fa3f065e25ec8c3eb00b08656a98b49112570c8c5843cfccc63d0f7b61979dcdc8f1be2c14fe71bfa8e577b0154f87920cc7251d0d53f62834bfaf9e7ee3491e90e145a950ae4d63c1401916098f0eb8fc3c4e92f69fda6e630041c06bacf495596df6ce03c7e7c36cd18c7fe69f4c6882382638b6a705286ddfab1bb74f961d131896e0229a65bf41204733792a67e24a912cb1719b1e58c8d5185214b4da6644b4f31cf7e2606feb667c66bbdd518b48175d3d2c3d1aef8229bd91d8a0f00f17b08d712a6a2c5f996d6862310cc612444523fdd623311f07970284888d9a61cc697b87d72d8ca66c3162d5d023ba160252fbb571a108248dfa647d26f6c86fdcac1524e009539b6143c40f138aba2492a1f338790d869773ab175429ec243b8c28e4d1a71a103f9721ef0891737407cce1de33e62890793f8cf66bdfccddf132791bca21a1745a4caf4f30b67441fa8cd16c78e6e6c7ca8f088f5a4b73d8a39ac6164a9542b16142059a394ef8338915465a8bc19ee720a9f2fd0768939b076d073a161b7cf50679b4a3990c3e393a7d1f7c60d714f354e4fdf0eecb2964018d0eb490af9d28b65ae558e2895262168175cebc5116b7b3157f3399229dd239dfa15f78182f17e5e7b65e4d218e9bcdcc1f6e949229603a397010c8dbcde9835e62c90063d041491243d168e04e0636003fbca694f1ed7a6b0ba23410f604fb68194d54331570f0280a5205e472c96e86b81fadb030098c815d49512a952631eeb0eadb378b5f3aab21ce16bc62ffb05dafedddce6aa5aa003bb824a927427d7bdf9a119a73bd57a6551c721f3b6420d0ab8925e744909c5d204f3ed258e208be43c96675d0196cc9d326c33a2412866ba9b003edbf9625b11c0241503eee7180a076223ca36358dd184df8f8c93e52bbb55518b7fb02f95a96556588a89e02ee1469d54f40fb694e324b7f405f945eeb2143e3c040b4b1acb4119179ac415ce33993d9f9e5bda0408f7afd44d63abe145da0124172e792e2ab89fc614f69fe698546bea13b4634e9b02c28f3e1b40aee9614483794d55aaffe76ab9f9a131767c20ee4bfed08f54d742ffcf5e0fcf33ba51496ff992e4acd0400ab83ae5ada101b1cf75fe54c3c83fa7a0002602a2fa53401159f34b2aca8d74453bf6054350216402924a2c187bea80e5860e5bc5da1127a7190f78cbf2ad6a083e8e903a873fbb5a85bad2d2d09398a40b34a929a95643d69c01904dfad1daf45b05711fd6a321a92edd8ef0cb1d391ec155ef363ae7ae929dae22682bb9b8885c8dbc3a550176753add7af7817860534f50824b35a6b7bae74cdf3a7a0bddc2e21722c709fb1d860595c7ec874103fb6975632d6b92451c52359fc14e2cf5008cfba4de4b9a81b9762564c896f71380ec7f328b00b133af8b694b1158025e12cac7866086eb3ea78cc21dc2bddbc11f84e76c029768feb27947664d0b0e8f6e717449e5b4b9b0ca11590e30d2923cc9a3e6dce7d5bd5f5fa9ab4d7ef8cab576685df8be9912be5b7048026b1fa132125197e988ba60dfb26a5efc4741ef4c84121fac8184dd335c3e72af182118d915f2e583ec4181c8e79ce70635f6cbdc9c150331041564f4bd2152d8fb1bf52fb06a5b0109dae8bdc28b49656aab8ffac714503e4563a25d72f153958a969361362f56d8c0b20ecfd88f825e093132d6019b851c23ec0087bd31fac7d58311ba041be71550c76488b67991c0f7d2cd02920a1a2ea43b25a9a7c33b2a67984480088bbbfb661a1a22be40ea8278bac04ad70c3ce6777e840eff83dbbcb9bd4268892bb4f403fb6f43283e19190ff911eb31086d388d6b9d5f7a0e36e906e891f9eb7c46b9d19a26d8d8b04541cc0adc0f85714432b64118c36074f51429f7709c25973f65f7b6dde65abe33d331b2dbfab471132c07280c6b395803afefe95b79c8acb2069295219bd16056a978a1b4056aa6ecfebee43fb99a8724daca8a34730e51b3ae40acc05d13d62b3e564f31779cd8114ff67ef880f90265a4037196cb4ea458c3c4dedaac4505bbc20007fa2c0d58526390032f21a8ef647ab2cee8e053cf1ed4a08ea313c93804c415799e8777e874deddb8feadd14fe8e2642ca0a558bb81b70dc9c53355381459d046e16886079671dc06b22113ed92329103308401e60415c0463b13e09479aa947de329c7a0482c26147b4b44c7b1fd535308269a505706e5e7c34001ba2ec7cc5a76012021868e53cfc53555fc54ae6bd12e3ccada661531fce74e22d6730f72960dfe6a2549c9b99852a3a3e0bc12055a7986016591bded7153c43df16fb48fb74162be2f43a052514e74e88a4ca788bd501d35f7075f3ad8742d7495db69404cb7ae45b75485b4e6f2a6223ef270ff703d2267929835b388ffb07a628e1fdcad5dc3bf536cbbd21407e4bf38c467f730609c29a2325b6bfedc715b6e479224870e3e0cc9f7a6667217b0ca3b350eb49f3ec7c2e0442f9f4b90248b1a2e99638e4f3d964456b8cc2439793d94740111c96dda5eefaed101b7f6a2b1a6b214915da55e5d66e2622545f5832740fa70621704e0ee9c3c570683d02666c2ab2f7f720582a509c0e47755033f2195506a240101c1a0b6e4de9ade24b80e46bb2bb4f15630595c22d73c89905cbc6c26bc3246550112fb95877492627b330be1517b8e9d50e6bbe92968b5811f3268f65f513108382550978b448cf603554b0877c0d44f93469ef043e84c422c1228043ec1cb1a8584e395f438c522576c8898e15c08f139c5e0a294a888b84e7ce3ac5999869f650637b1472b90461ad7a974b9d6edae1ab4b91c9a65e6278ad397fb3e39743cc4777d5531bd9355505cec77121028f9a5cf9359e550d6ae7ae608f0548bc2d2b1868e16c55299d440cef6dde2374bbf0ba46e7d04ee29becf7b723090bbd29d50c85bb8c7652a4af0525ee750bcf5803bd65f393e75d4b47d80e3d3e6f2346f4ab4a517504e7db3a5db3f513bfdbd1871852667d075e49228b374023d4b4f13c0164eb8053dcb180aefa42b6c727a9c0367b4afe049a1b3062ae984ae226734e88b301bb6213ea7b0587094bf835be56f14d5be81e7491edc12b8c72ac99319ff151dca3c321c4b41de748e244dc7dacf35450d0c1b41116c6466ad44a85680592bd9ee5a4f654f6e4c445a400844b8c2803a7f7c609a7d8ae9dbd2a3db50a71b647eb35992443315f2951b1885bae769ed2819e660aa22218f16b5cc89b62e629d6d9d7fb2afd68d1068e8ecfa7c8388b1903e2a50c5207e8d3dbcb0f9bd073f189ea888fb2b8de2a41460e5c2338220700e45a4dfbb602723673c0133aeb65cc8c48d07d84a8a1f2bf21396a146c4c8c0a3063cd24d8ec12363d0202f1e4b0f0da3f69c2a1d46ed8e035752e87559986e00f63c377e15ef24d2df6920f96238df4363f6ae6b86bdccaecae783791f787d9e08d36dd3dd182fcc8e1641779f2b6e4ad7fe89b60c01afb24ce95a9470111b87d5e8ee18c106b2024bc489bbb028645c6fb23665d044b092610bfcfbb354dc08c09100fa238ad3d9640facea5e51fcedb5ffb0f0f1ff882ebbd9c8c117de1a68a96c663e1ed5a18db5d152b2eddc70a8e1eb1669cbadf436250b364e0736db8fe9d1df80a2c7bc04322c6da978dd4d7ae55daf49e38dedf3f842936121fa32f8eec3b30a998da3ee3973db00aa607ea669504218e19b94f012ea7b690cd87398204bb93b42d3e7b0d6261ba9b0630ba920fdebf71d6f5636ccd397c614836c7c66a894a872dbf22002cc99bf184a5d0add5beced8ac15eab7676916acd84f597f3941c7dc0b89ec2bece60d9de91c5c279f015673df0be264ca6a4f775ddf3e5c65fa5e232dd74edd791ad7b4ae531d9f3986f4942bd5c5724994c7cca2e583b89598f4e947144fc7fcce9447ab29abd2d8ded946238dfa1a4bf336201d0f3df3464086364b4fa0686e3585a10881f55fcb84b5840cba413b16c006c69b1fa44403c1d6854ebbc0570b81a0b424504229c5073c6e6df81b5aad49815d4417ee91994784e3a78b044f97cccc0d8aad2d265a63161abaac2092861fc0d366fabff3d9337d3b589ae3c6f36471d6736127a61acc4d40268bf8bcbe150ccbcb6c55d433ff4a29c562a635675f353dec999dcada8acfc9f0fb4de2070e8bf28aba3bbf8ecec04864fe487fe7c65f820f595bd9e27bc3a4407eb45fd04fe6e521447dbfc29babf084575005c753bda17af366f3619fd975bd0b4cb478aed973c0ac7f55fbd39b9fa870d4924a0943fb97aeb41b0bfdf7399c9fbbaaf1a011c0f90b8efca97f01230e7da5c8076b7f561c7f0eefa3b1fb96da2786be56a028cfa9ab8398ff1f40d2e947cc32402a5f479e775351ef0b450e6d48321e61945893ce090c60807586c9eed9d824289f2843fc97255cbba04e4e6eaac52eb26efa432c45ed96218b83a1e04fde382beee80862482b2a633fe50a0aec9733c0dc0fdf038a743f1a1531ed13c080fe49738d169ab8d4c484e8e68c53854e8976358eefdd8dcc1d9c270d24a9203a003958b55bd474734c8eabcc553c78331160501539f4915c4e75d80bd45a2fd4911b3041b1182bb86a42d8507055773a7d7f87a9b3e48038fb875c7b5cfdc76120c9ce522ec2abec27630b7cfd2f55ffd0eb57270e3933ce958566edb1d35ae41708a16c1420b30451e44373a1af7c1dad9004545f474781dbefc6d214a72fe058d80588b6b97ac7c2165f31802a0a98fcd6328592b728551b080414cead34325b68fe7e0205f2a11a404121c3f6b76b988d83b49fd0b1b3658ed5e4f68699d8764cd43ec2184ff61a7aa770b165bb38ac439f01cd58716415e4e74a7a3bc983b71b493ee2229c6eb8f99897881eef693b7333647b4ab918a42660e5c67eb622b81e332275896039a7cd8c0b6a4cb6dc7f26c1239d9e8f2f0ed3425acddca7d812bfb11504c34e92a3c3d9b5662048a348721b023a71f3341db92b7982a8fca00ff96bce32f282693524c7292898a4a0e3384aee1396512eede26784cd3b6fc241d55033cc5829e0b6422c06db4b3c92ccbe706a074443a587c9daf6ad06e5fa9c8bb6ef8f8e59d3a5e2c9e3f6ecaaddf09f284d5a21ba2cb7bad612b7c3681cc9249fbe086c8c5c3aa3db7691043e2f5a2ca0be2520f09dcd941f82bda99fbc6bc4dec8185da93c4d2989af2ffcf60122269e59ec05ddb2ad4b3b7e69ebe4513fb3961ed199861ee3372ee17972fa257b4267b68bf27aa90d5727799c81d16c94c45ce6f72bb62db9e3433788921a49dff5095f7b4486847987a6ae67dab03bc356f230aa0d39dcf3a51433a701a324ed7a72c9cdf39c8f6bb54cbb2d82a33379bc0546d12b65d8a917eaf674d4d96f4934d69a96e77c59a7d8aa0f18d43a3975318e79174f57d2cc7fecc51de5a182852f09d46087c279875bf2c60da33f43e3ce6a47f0b68be8f38071711f9445cd37af5092dea466191f0e83e43e66905b7f0a04930e02cbe26a8ab04ab388050933275c1c53c4d1e12d0c52b842e9c4baea7e4fb7f718da01217136c9784f0695980537820d5b29c07f207173c8f3a17230a3b21708a4e08f166eef94b2770f01e686abc8a2662065869a0fb7bc077ff52be60aa38cb78380040d4e25ec7261316564c8a30f85076c50ff8630bdb9e07b9df694a268ce1cbb6d5d4ff423ba3ca57179f002716a7a3ba3b4d0f9afa3259ab40958118c99db4b87bc99145bd56594be54d7a6e2b14a831f26377e125614f0724ee42284dbca4ba891b3c680333323a311d0e7210714820321839e58a90daf00ad60937d8325741c44eb96827f3adc5ddcb806976aee1919946909f3ec005aa86d59b133918b6261eb1a4efa8fd03ce1283b61967eda2bea5be072e8a137a472b54e9f68f8881b1f57fde67488a6a45aad44e375fb9a90b5dc58a6ca02e7b3768b0b9ad819411152b6de189451e76528676fd2b152b70bce756b8270d313011159fd12f907bdef1cef2212c3a114c3421f659c2b0e27b8b1dc1306508cf6d4d5bd43ff74f8b6a2cba95d0ad1f3650ac059538f4d078c7a7776ada0f75347354660831c600fdd89d0261784754b7dc472af8fb1280227247b0c58f030f5c452cebab26f0b31dc7a09f3326538a645a22f466644643f08c1a6c6d9439e2bf0e496480270707fd9817548e7f15625ffe2ffd4370488ad6665d1cd3b55f0ea08175608f2a4c3118ec1cdb7980e4fc85315d49b783d25437f911df8b948724054d377a0a549d68bb6a3d28a42cc3b30bb1ad2c77294e020272275107c197b5e3fce910283afa3097de480b8450d744b24206882c7ed3b774d9063c174becb286f7a9cb567ebc236161efd8985235d34ae25949c88b7a6540eff1acc3061b6426793c2d2d9fe42a0d2116257039e325462e5a12508bd7ecdefc0ff6e32a450ea45adf96347dd610609acc9d09f61a4ac21db027ddb48ecc44ba4b4b306e6e03a4074682352ffe93d12735aadc3b014b7ab7a345cc529061c5d326a97f55318631202b064821e20faca88467b1a0b9b54bc1fb435b6d2d31b3ab54dd4cf7854948434fbeea2374bdcd37d8552e61f3ff8888b7c53996eb5e84513a8cabd9b34024edfc79c8f99e52a402a1d8912de38318b9ad1eb1552c8c95ba9689064ec1c714a1953d954ab0f18c31b18d416ac616b8682b5e0c98cf14ba64b297d087adf74236ff29bef87ce0b1d0432a3c81c4e685e2df7e4c46639954723abb3fb488c2aac78c581fbe48f2c32ebd6783e72fac1470bf0bc3cce41bb0b133925ca5175ea9baf8d6b64ca1e419838d475a246b4d0acc07c1fc51326a206e26090ea51b8a04b31f18809b2b46888d5a6b3b6066d32ad837b88f248df179a6c3808a230cf28822a911c11b6d2ddc9134ddd551ee08fb3a43181cc1470dd5582c7070e52bd8cd8425fe7480fc901ba76ef6730448fe0cd824ca0a74efad0f8d4420c9101289cdb48a246ee88f9d9df14a3e030a4330463bca90f1b6336f17135dd50cfeb6e9cd65a4dd608b6de7d7859abff6c5c63487fd3151744acf50ab1b72eba2ac65f22872548476c3b6c128269824c6032162d0ae46eabf087ba5290b383a7bb72037b54d347130634aa625dd6648870cc0585a25dd8a0167e89234c95923220c5e9411135e24c2e39a9991eb6e0165d914290af9210781f8bc133320c4d9abe26fa491f9243beb5b04b29b3f81ac1a7142a982628c8b570a1858a9f80978c3a52946c37b4d9be800d7fcd185e3ce59dafa5ff027b3d9f6428123f390b8ec77f9b1f0bae1c0419b1a8f0c032f250624dbba61408db092381630e4a043b4a47aed704fce3a59c74c1a33822a3c890a41b8a6c2cdf6a9663c545a2473344d90f5025af71b5cd801cd9726276a3e5da7909ae77695f8d18412180a40c98b0e3098766b7a582a577494c33d3734e156acccd9f06950755f06468b36136b1e13925f1d074b545149aac9b25ef125d2f70cf47f81e733c4fdcde218d08054aeecb264dde3f1657be2d6bab3ceb3376cfc9b98269dd3e01404bc1e907011f8890990f8eb5ca1386b79097aec48fe370525d2c6aa6b4bf3beea90ff2cdc54f10cdd566c082e93a2c7c19cc5534a4fdcde47382f6255c84ecb138a3234083a5d7830c8cf5c54248e3fc729fc3e84f0f3244df494e2f40adea8e2da5cebe670ec98561a07c93175a4fe924d360f4edd5b711582ca447403db33b7f089c9e7e177c47680e262a6de1f8aa26c804d3821ff44110878894173dbde7517056cf58711547b19cbffad612d85eab1c5e0a98b68189b6f1f8704a1c76cb0220a7bfba17120b98f517108381b40aa592ecd436d5233b653c8a03de51a00f7cb164dab42009a3c4ac35030376d4bb35027be95637f9f66b135f49029695966c6c3243fbefb819fb4d5ac8903e76c26ce19e89525774790615216f33f38fb56cc6b81f1c8f882c8332b48996d78d02ea5d4d5c4a3487ea7e711845a50aef0c86c410d932b985fde669f918bdccca572aaf6bfbcfadd6d29814e071a450eff99684d2bd3a6d4d756a94792c3bf4f3b4f41c5caf4f50aa151f26bf344cb14ba69b29ded698f9f4410dacc6ac7e25775d835c0b0d2e07c7528ce199bf224af630407c76857288ecaf714cac37197aea74e9a52d891c489d148cc39d0750b020af9b515f291dede25d356870b3484c7e5b2076a25614949784cb64f27f0766280ad76bce4da0816225642125859eaad811e563009a50e009276900b689dd450fdd5fa9171a080e2864894ead60ba9be13f4586182bc29485cc23fd13dcf0bd8f4e5d5541b6b479c1ef4c34cf32ae6f2cb10e97bd7d1b1154fe2827cc95540356fe7c57e1d88e4f632ba49ce4aa5bc29276f6eb638c0c04d07cf113717b1445dc8917f7471f1b0e4e41a7c97e26a3e2784a2ded6bd9bc705510afbdfc36cc60e74f8b3ea5271b4fdf34c9dfd6c62130feebec238a8a3b43c858a87353d2d1d9e58d271db0666f492530a72afbf64c82046182e39c7e8f3a2954227a1f5398ef79d46e23e721c6c1d85b73e72cbdd5386b37b1c0be0e71b942d91701c8b1bc2bcf2bc24b17ebd8eb883fea163e5d4c8baa4a45ebf3cf1b767b17816ef4b11e147b42173d9bf96a28ca9026d48f756df1f1b3296cc4291fd4fbc9738fa47214dc1a1fc2d887401f0031fc700491196c03f3242c21e108dabef433c2840113fda35a25ab347790345ddc996f481278c1ce700c7c28551e6bbba415cdcc23411a93a46e7c02f7ebe5eca29d623529b0cc0196a769f4f4c91299815dc47a6f13b62122cc9c45893a1e8f664f7af0291b5446ce2c72c92bf097272eba39ea976f06bc7ac1a2e9d927bd898c2208264f2aae4b458b98e547e86d6e45d7420716c87db722685d6c9e974cab0b38f983dcbd4765652d57ac85f51335c6469c284fc48322d793052be8fe580d093a07352a164128f291be0e677ef212cd3f847cedf2834f619665fed38d3098d5d522c88691cce5a495742db41378b5b229a0d79eb8fee76488cee2d47bb0ffb9c672c5b18dd9394c7b6a40bb82eec61cbc0b01fdb4a21c99ff8b1a8d278e05b6ffccd2d49cb31092c88daf85e7877701b27668211a72e1f0c2218e366bf76fb77ce2205c6ad410707dab2edc604d76cf7cf988c37ad2e268dff4ca0af24ef3ff934db86932d7b81de1402803cac05a19427d3fbad7309bb195deceb36f3852f5578f06901434773e3710b26cf10e3554dc2d1fed28d5b35da988171364c6b9ae03ff3346342b58daaa02fca85075233d1ece0a33832585343421029a7dafa7cd09914116852d6bc3b3a3b6a7258445859e0b3955ec5c245309ebd0a5a05e7fc56383132e3cec162c4331e411b81df0d4c616f94e90c8cd946c8c40edf1ab67f8b4095911407c5973e16254dc24d3f80e774570ed30f9266c2494f1c37ff0c0bd05c05e0929412c3e063060b08c719b3c78a0a8a87b4a4a33edc023753a091d286b8651fdc0d9f65de7f00c37b92e1f18a3d55e0a63a624d859d0a9298956f3dca8104d789cdbd0c67fce256b4193795c848c552b5228aa61ca44ead4afccd2005252ae28d2da8981618b48a495cdc4e57f1d8d822e97e2cf7333bf673bbc697b2bf7996dde830aac5300f343dde4bc2634ae12aa1285cdd5ca5cbc7adcb3a6e3a950476eae0acd053ed405b5b19aee99810d55e769e0e538185d4bf10582bab7b6715ed32cb4a3985bb4e21c0e1b15e3ffe57cfd4785d3eada310061efc06fc0b212ce26ef931bb81eccf08b3d2e92aee9ecf8d174ed15c3dc7fbcd11609f0ca8d0a0a82c301f612fb22f5894ac20688a4ed1cc00bd3e93319d3bbe4b4dd0f018533a1dba06c1306d4e16d463d7a0757cd1bff2614641f1ece28a0ce2ec9b8f3f88cb5feba607e519dcdfc84e0fe38ecd9d585c7c19f2bf3b1de79fc4363102884f6169994eca8d671eb8359e0a206449f0c21467287d2b6855f466d96a25991f095008f99ecf3b79fbf682cd360677e76bfb6a193088eac40b55ec7ed10d418bcdf74eb41e15198f0bc4a6adaad0ccf06ad9bcb5c8e4625daf3d2e5f5dcf51c020da324cb0c6c9fc13be0d14844f38da99408660875118a996cc2e14de330c41bf5e9cc7310f9e13e5d0d40690e79edf7e4b0ba223619bfcd7beeb505255ba33a6fef3c80098d82c109f2aaf97f5c55d1a4378e6453311c0918114829d545d464be3b419ee3611462e6997793102337784663cfe948740cf4aad28c2b408a8956ae64a1e992450ed931c6e7963ef32d22ea906d326e70c07912f617b454f66944538a3e251817278c44961910430109a0dac2ebc1a806e151d3d157016aa490bdc76cd5bf383fe74fcefd15941bb0d30b29c5a715a338a230dcc4714ceb8973399e871eaeb69b05e97a2f95e64b9a313b65b3873793130bc58548e80ca8e31300afdbfce4483fddd6e45e4c6c670ba11981fd9a2215ae01436fbecd4b6367e4da26a820c33006171ee4d86600eca1516ee3e14ee9b5e6f15a802aaedcd7ae6523dedaf6224e6234d9095e3ea914dd93910e672994de12d279149ca83ffb67df67502ac723d9544d68e72d50f69bf7911b4d5633738e2f697f980f02472ac32a37814910c628cbb294ffbe1d64fdd7da7ace79f69753c8709934aa2809cf0f2d20442ff0a3f3e2483f47f4d65ec5d054bd03dfa2e8b40c8b867f645e6df586344f5ddeea81d287ee54ee7d8f6ce51a315598ba53c7cd667594faea585c548bd8e2e7bb009577ee5ae81dd03d03eacba373b9c1cd529100760f601db3d70c16e64f3e5e6e928f839e487b3089723fea3e65881e312346b120259c47394b179f74449d53239c33ecd454518bbaac4b0495640584d77545c5f478c3609db8837340248b6cd95aa238e1e27399f56c0ce0755793aaf019d943c876413233d017858e544bfbaa9e913f8bb2232d1c8fbc01903c282aacedc4ea787d82ab016266a11409432351a1d3820166325e2085fba8f5e06eef8125590648ad138fd06b0fa0df58a2a636ee62071a39d4591c452cf5483c423649a4ca80ccafb3998409da56efb9f9e6c89774f4d41e742f4344729f304ad4a3ee9cf11dc655b543d56715628a37bdebe325b6d857521a913701c1b00a56fde2d0c477eb280d3bcf7b940e718eebacf32de8b637b5f97d9320d47186d9cf49ce91c574442ba86d273d07ee60b4a2d8b682d351f721269565392035b878bc91f4eb058dfb577ed1c09986d5fbd136f255b74e7f5b4bd78ae475b6a735a4a75fa7ab654393156562630f9f5df05ced26ee208f5f02a2b3e31bbee73c99a1a93f1fe93047a8974e08453736873c5c61ba879334a2de32f7a1cbbee14f9826e4920bb797ac7d67962df72938cb25960e232f3293d96c14fd0e123d158da6bceac37d104697f41932f81396226f191f919f6f6997d7fd73f60ad5ae60498cc6d48e840046de04fe5173885c1e1132b5b40145a1aa523ae1431768315c4e754b5146537b1f031957a0ab409293118d4fdd424ee3107b6897fbcda0ea1554059834848cef503ea23df8fd422ab062f10f14b4d004c6843bdfc98dc0bbfb92398ace29a3101cc1deac54770ba89a95ce32f1abf973950c4d3b6dbafe699cc9b008218517f8980bf482fc19b7199089ed89678969a9f532e50ab3460cd7ff8c945b8f960bac6098ca3b4e44e0c7d94b3571c869189522806f430e0c31bc558e6eb0bf1924ac6e4be75b8dead6849fa7d87377cfda02686f930f67cb400a2d92a19fc3d1d74a7987d79afec004dce023256601dcf389a31639a727cbeb43d47f2471f40c415d27af27077ec47cf6f0097133eb8db9ab2895ea51bc3d5292fb66b828eba38968262a18acdba2ded3f0a29a38daa603f4ae4158f19c5854a69c030e0f8d4f4313f81498dfa30e545d815ddb7f5b50af96e339c8ce0425da997a8cd6b7e5bb5caa3df8947c582f2c3d2b753f037bd48784a5465057b1e3bfcac3b3f2e264e3cada2f82f9b31c5020482e4bdf5f96ea23cc3012b15297bb37fce57db873e540d172b6d2427e24f30dbcbba8dffa6bd500bb5e8f681fff226e9ad43eb9612fb6b71ddb2abca31286d9aacda5f4f337f57c9704566da969b9ab1d4308788b1065528e6926465b364a69476dd861caf517080317b61010f4d07d96e748c1d17bb174ef4f660725743e8f913a64ab1921ba980d5f65e630ee985a6c71aae344b12bf8498d93131df11cbc0d0f4fb3adf9841d92a1df52b67c9b27349f3fab3ddae85988507781543bb653c99506090585d647a1cd9374050e1262e9536cdc8247b79551e21a30bad46af83904fa11b3db065abecaaeb45064a586e576bd000f0a3bd8095d92676634a7dc3790bc8d57160a96ebf64831795e12c7655c62edffa4506e4c121e5e7787bf4460f7e7e3d56173f699ede9886265cafa7afb3c41b6a2b78ed1bef983780aee87e6b95295eab58c9cb113aa86a507a234faba7405397971cd912964539cfac31c19519de2f3e704c3ee652c24f51f1e1a9c6146c2b115cefac5a8156ac20c34380562b84369a34990e5ebcdc2394d82dbdd139bf4cb3146fac34faf0ce2befa7b6a50417af8763e0eb6b223154acb23c02a018d7182a5b2448f062c2114fe9b69311f57aa78b2485fd8f8067bb3a4d5de4254a3d2145c77b5b0d67dc58d22d6d7560c6c44120f14b355a951805b22927f20c3ef69b7e9660f4de9bba747e3a79eb3839e2c0252cda83edeac07d99bfb2b587ecb27ca663378736aace4afbc90edde5f90c0a29b530ecb3a353cdba472b2c42d5412f90edf7fbe31edc0d45dfb41e91a563ee7ef60284d8159386d7014c9239c6bddc7daea2c8290a16f809880780348e9bf495ce1fabd1e505762984029a4cc1a567905ecaaac00e1a1fe15cf19c82ad1cf43ef58114150b3e6dde4b2ca8f341cc968ff1b0ae998d03451ba5a2d7f480563c18edd134458488e4ac888b82d784e18de8a8729d2574492d887404ebcfbc7274935092687771ac279e15db64ea09e8bf4bbf3106ea2117b9549f2753b885b42a061cf3aed4b030ae5091dbc99ddb4a4019cedb6cf875ce4ba72fd45742c5aab41270be858eb6864fd2c1b452f6e31c654607b428d0e43d4087839735649d82d2b428eb93471965acddd1d403aa631dfcfb90895d7d6943f393b0282e5a41246d011a4f1b938a1a12fa945d88493d0a3fd4ef7cbbd63a9ed05e54889d056df592b9d8fec2f73bac5f96a26f1bf3a28413260244dd7832d04c94d6b54266edf21c8961102965e31c19a09cd236d7f023d8a6da2612687ce4135613c3611d60612289ddd4a9361676781201a9dcf86173f5ca0f37a081b6ff292269b319359fc02aa4b2b9dd276ac4a32f95a254d226cdadbbf1ef7750f49803f186946108c90215b431131bb29c34d2abdd5bd06bf12d97152a295d00dbcfcd07d0f881958b15787200701574605850ffd505b6d5b09b1e68fcb8d25c3eb1b14431546b58190079dc0f41002cdfc75cf0f3f9ab30535515efc93bf47b20a50cf7fabb19b3031e7b377fbc15b056ba293b302a427a5620ed91f971dcfe1b537549131103813c4f8d83a45cd504e8d27538bc29d938ff9712f7cf38a5431e4a2cbbf1bd3427b686355ddb5bbd2044857632fcf50ff2d4b4649ab0f1037cd01b396beb786fb7af8ed3e9ed458eb96f5a4257d842a6ea54d7156e77c9f418e1e4fd9f43d48d2d70fe288a0e010f4d8bca9615302fac56c4926085ea5a460105b5b4bcefcbda9787f8127022d053c240ac657d03c424dde90c1fec2e7fbaf682f7f21597ba18357363fe3b25e9a460321980a6c980bb7670c332e2bbc49366251ff64bfbf15c80c220a1a1d9e239092ead4067eaba84497c4fe1eb7c20d66e803a271afd25afdb86bcfe3bfd5dee7f0cb7133816c5ed19aa6398472f496d450996021a6bfd814f0c4933a9dc30f3c835360557f4f815ed2f7ed03354918d6a89d44b3bdf958870dfa94ca920e2d4b547d52460a8583392961b3579b04f72a5da13d6b7a9605b78d7e53cf1830e21dcf9883eb4a41f7a09ee0120c9bb8bd43e2d0d6e0be2d567fcb35c484f2ff0c360d1baba6d2d28e664145f7a308ba9e516ef54dc76d47296745968f48fc1d7ac932da43058b1aca518092412ab84892648db03b0d1f1d36de362cffafbdebffa79056b6466ac00c1e0e0dbe3cde4a6515f39c969792142204d0a703af2b05442e07b88966ba023abfdc2fd0d64ba7ea943dcf37f8471b9ab0fed0816efbd2f77b321e4e4f4ea43193ef9d7264880869bef1786f58f52d17d80e3737ef1bda9a19f9c0e204589a7efb796ee0d0b8e6b0d1cf6dcbffaac4fe49c8f80337e82236760e580d0aa88c577fa589e50d521135edfdcf60627b372c4b573a5075aaf8ddf5c1bc82d5c4a31321be90a07a3b0a55eba71a77cdfdb5fa80e52544172138bf2ee1e5a2e7e79552780eb965f683347558d8882aca3ae15cb255c04495787cef7c23f0a8f7b43c1ba1d8ede6ba5df21d2efb3cf42772c7222b0a74522521d2eba805e6aaf5ac6bed2972cb7dadbe8dd05a57664d58866f2ca513d5ca5a328874cb5636ddd7980b5f21e3ea7fe395d1a3a1cba00802dee74ef70709c6deb2073d76cf76f3200837947831a644396c2234f46869586699c17c87a98a45d62e9a59bda417b82e67764e34af509cb8a9cd221acb8777a3dc13098c564ef33a3026711e819fb4b3493a0a134ad615513da2e966463b1df4e2bbe6a1cd3955b32be88a58347d87689609a6aad00d6e4368bb7f59fd4c610ca8047e41fb686b9810ad5e4efd0a24b48f63a0089d7b3389cdf0f3b9b2c3041d8704ca46fab2edee6fde20263d668683d0927bd1205626c27ae43c55626206d2d44277a7d55f5b48ab5c4271ae9479dd8e9ca5fdb2d215f66253ba8d9582bded8c4c6dbf222c9ec1a7ecf50472c12e89c22d8ac23ecad805f552178997fcfd671630cb2034a903b4e972a777255dbb6a9101912596180e1aab64dc07809f9952af5062986212dced7877364dad1cfb29a67f94756cfb2ac898cd072c67771311a974304b8d8cad5765d50d74d3090b005bd16c4a0028832b8885cec1c28f2eddc946eb7b69e4027bfbe0b821081ae325bb571bb083488f6016250de3be2904a9e8cb0a606c27ffa366ad93df3c329bb60fb95ed0162bdbc3368aa469e058600318727900b0121bb9d3d5a8ccf6838f0d102bff16b4cd0c1fb49423b13732e9db6b51e3c42eb39b4eeb19ec35765646f8acd252d703202c08bc02e3590fae9af79fbc029375a8ddb73e262794e6ec197ab0ccf24281f687f170133d68799472def59751d958b2661daeaf4003cd485062ba9646aad2bbe4b58fd294526f4be95fe8466dc6b8f0af9d6e88834fb7842093bdebe9c4cf47f01096132602ebeb82b49d2259f10c42b2fae62f1b5e96aa6b56fae5b2e8e5c91cc2c6c304240e1f4337091a4522018347555713148bc17194f7824e597984279af901eea8bd0154e20f0058f5f112049d29ff8daa305ae92b0298e6cbab6120dc0bff790d30f197cb383356dc3d02c38873a809d17bcac46b5acd5dde402e6a0889cf3ff78030f0711c1fbd78d0576a01ec1d3bcdca6611eac3031542b7eb5ff6829fca85e4325ff9aad19270fc1f9582fc0bbb713f7bc6b3bb6ce7394158f550a245680b37330d6be4766bc1632d0f272ceaea227b021108085acee18eb49a911ff5261acd4814255e5e688d7e51e470996eaa85f146c60d2ab4d0abb9ecfb58e79e8bcf1d9c9b3e05c10b93b632e9c6b53740b0426c3a44de6002060b54de65d86940294c33b96910dba089fadac1b55002d7fc980907246f76353ee2e74e3f69dea6756addda0a807495c57d46da15bce2d23cb82c02dfd24fab0dcc59ac8769b7edc1d3a9a6ef2e8b007631e1853523061626e0daa78cecf0438988fc35ab06e1ec0ee97ea2fad185dabd6302ae0795a862fcd44f01380fe18964cb6093faf16745267db806ffe234ed82edf4094ad551eeda10c8bf1bdee80076223f4175428b9a4250546b2c2591679bcc25ecb2504539c9811b33ccd0cb0140ec9c3c7fa72060304b0e7d606fa49be9bcc39021167452e3f22cb5c2d0f368d4bea976083400812e4fc0e3a2277a27167a2ea885edd107a5c2a50f1852542d68183d91aa23291421f5a5620399df757b8cd4c0af98a7b459b9d9d593f884d9fc0cb55179ba9282992cac43e6e3821fa4a82d718518a94979ec5a627db6a6870da25da330901c1b88af7578879ea57426e71091b9614b55ce2c8df5ce126de5503ff7ec64c76bf2828a8c340a6c465daa863c719a0f6df289b7a807d0c767c99b84f85818c5913ee23f13da00834f8958b904e77254dd45936529f8890681813c8b0252e2392750acd07aabec90205eb6a46b0aa83c90023083066271c2c512e8fe04a8941002a8a6c2c0c05fd2c35d20650308d2f4e3ab3e8a3162cf4e810ea77aa2c256e075c1e65f0ad37e7c8445428104ba1fc0dd6fcef5c240a3ef2b1795bb70d9a407baa8539767fcbb95253c0ad6e95a30d73ccd752ace0413dcf5e25ffe6789c8f0d8037315f1f8aff25299ded5b4804d097d21378ac88543fa20e6f209cab9db2d3b2eb46163d65c4a366b81fc58b24b40eefc59825a351ea546e2b11aa3af35698ea05cc1134cdadd118db7de148ef88d21d87a659efcf2c8231af51f04463ed34595895420d034bcae25deeddce40ea7b2ec478c35b8dcedd5c2ad99857506a3a19437c84f6a1c4cd604e99c83de14bbaa31f56f4b9cc5b7b2a712520aff06d72388ae38a57ae0cccf6644604b0fc476afc30f159fca169580af7a1bc702288b23693588fd7a04c676bbc14e9a082d941d9edefc81940ce28c9c4f24389c7a348bb9d5a2ecb5c838d2492dc19c87d11928b9261ed35009611b3258c8ddcd1cbeaa4c52e41f3543e5a3758c91de8ecb3598d2f0fe5a9357d6e0a89ae0fd7699cc42d274fca3051154ae516f845c58b130543e883a1e79d11a2b24a228c2e461ce4a7a59c8f0e14e9b33ffec6b787793f3b58552a5cdc6e3f73e43734df340fa94e5fb244d109767bead51887e3531380f464f24a134b8e9c8e9959a4acbd1ede384c3ac773190759b4b689c23967b4aec329ae498af8f1a1dfafac8b1e9c8ce84d02e163025a6f42cd152a5eea37e56814c7536175448072f36508dccf4bcafb84e7ad91e4156d36b92972efb6dfe65ae3a8c8241b425734e21b987130c4b130f01e0763de38f5aa4ab465b002f4f1eb3445397df7e7eba40f5633c0ffab75e56c40e4501037d066260e62b880789cb52aaaa029556790e16a89c1b37eaa70e02de77fd7079cb03fb9477dc1a1623785895777b93966037208c39d8c20808f042dc8de218e2d0655a64fba71b44082ed7d5822152d459ce148eac8522dbdce1938da9381c17ada130edd7ef4bee10584c89f1aec3df9e373aa4b81c44736f6132a995b2e8ec1d4f9846bf2cda4c2ac4726774ba50ee4f546ad152d97f104ebbe439f5519649b609b2769576715c0f93436aee6b5a4dea6a1861073ef5eb1715788749ae8a6102e5c97c97373ecf76b94138accbfe3c2f4f8077ccb3ad6c528ab7a2816b7a6bf86e0e934061b8fb2ec73e10f15bcd62c05159aaca17e097c05d3e29cc3a35bbe30b10daeac28ab9df28a39d464f07d9beb7550597a251042bbe8d082cc711231cc20107a731b57dc321c412dedbe9069e79536eb5bc031b0e28dc78facd97fa8174b31510ce47484d2353afa24e230abccb0f7e4b36d55fc316e452c71a45276e861002900340caadb69519c83280de88034853bd8bbb893d1766520dc605bbe020ab1199deba51ec997d8fce5e7052be09b5459c04bf884db2d6f029b1cfd479d36146753bf9d4ba681b957509a9f062ccf47c63e56f3214f360e6b3ca16e141ece512deb80500582787b3d221aa53df50fddf359858b65a834f7e6d4af650b1fa1c89e247ad867bb9b07d1044d09c1a092389f1de579b42090439e0b1cab51b4dc1d8fdc5bf89ba396fdfb93c8812b9a29cfb5e4ebf2c397a3e317c2fa01c6c552924f09103c834eb19d1e5256c9c3fabc61f094065622793498a04428a2e69982e8a8cd791e7770e2a4b23b698001f862377869bc1c42f4b01478d1c15e7991d295e2d0b30b6ef8bc405aa29b7ac4818e0eebf8597aa060d7a42200c732712c75f880000c512ad138ea9a8f72e20d0ca2e75b79a1ac5d9a53ac292fa970dba48521f3f9637915f8c880a099938f86b193b84e86a9bd5ce177c8092d62e3165fcd4bc52900517c007d352f98d1b71249c69621cf86dd765b2998f98fe67e16c08e5581a8207b314cb563d7f60a62d7dc70e7adea17691f54790cdfa8066444444578e2a98ca4616538a7be67c9008e640ce4aa18b9fda9512b7bebb96c69102320dd0a464a32dfa4524e4dc968edcd78a1ca1678bac09b5c7385c6553f7e310a3def7e921945a56049b40e35e2555570b1992d4ac5d1e85593b2b50dc66bb2e7ce360d825518e557e84a13d13162206a87c75f4fafeb1151369c58f4c306f61f5470d3991aa2361d8c0358735d0a49030d465c5ccf8d978edec6bb198d286b8bbb384ecf8477b78eae9544ed71499e3813c67daee0ede79b10db8bd06c1f2bda7e423f3b8b335f5885dff27a777a89827e85bc1098ce6b1bb9c3ee35e31dc328f68a9dc9501098b355b2a274105391f41b5e97e6f6e92790a384b17964cddccb5e5f907a97787d62d55c09e7bf8c37f1904a1160b653c8dcaa539930aed8e2ee20d1971bfb3d359cc40a4a332df16dc09963d6611e2e9c03e307e74c725ceb272a222825de6d0777c0266844f13b0652c736b5e404143cdea75c047ba2fdfd5cb7d3804b3f16e4717aee5e17a20d5c224ba38d9e0975857f3eb5702df56933ccf63062bc335a72cd3e1e821c0d0c319bcb154272af4188cfefd761e949f5ce4c9f71b5da2570f2694a132cab7d439306db0d2c1d983e9257329be9d91a5c2d048d6044fb2a4621e37e7ffbbe37e63122a6e90151981b4a2aca6f82d7cf1d208a7b64b5f8a2870954664df34d36052890f50dd6d62e66dc242314d2ff94a395f32063b58a673d34187f7dfa47422bf263168cacf89eb860fbec232ab48c40168c0e6a0f8791ba029e32e32d31b90bb9f49d84a4fa55fe64984d1b7049e19dedb7b6bc6b93644f7aebf404b13463f9949a6b7603fa61c154c421e1015e3c1e13f0bc2c9e53d4147a7c3eaaca6884cd7efb08c43146958b4f12a029d71a82c9a5c119ae9dd2dd3294b69a16152bb65f5a21df3b1f7f52e8ede11fa242e44bd68ec17c7b0c625cd3b271180364c54c298798a48975f06c35ae708113e1dfbf225b4579e9a62f376dc218c034bea55332c9c2107527e7c86dbe235d57ad5ea28041c69d0c611c728efe1305b563165e78420fabc56ed96c22f51762149518a696642c83235d4ecb009ae37d8cc9321fa7f0bd4c9c860de6817e869822f5e7aadbce64d4117a99160d7ced1a98275d5512f6d49dccde0d806c91a651966beb9ed02401533a9f0a55578a1895a212e7ca7f16c582a8ba3a64f13b5f756b04f9e0388ab16b0a636192a50e132163fbd332ac0c8a57082d0c7e359295cdb0235f2b341db19843c3045603d4ccda05ad847fe3a7f3e4cbada8c33fac08ec6eacbebc731a42d7cbeadcce026818ae7851892d63c64ca69d2082618d17ae42bea52b7117c1e810f0f0f277eeb7153812bceaa605570485d282b82afecf80be7f7ce23b11fc82e62b591e7949c485470f767544c55bd5ad4de8792115037a05f3498afe6fa10f7b11229bdb12fcbaca9b1f07a16c474ef13c883e843f7a0b011c526d00f589ae2ec87bc323767663276df2b7a1ed0791e7cef33a9c2e45275244d708cbc8541d2eac4d518296d1a58ea94ffd8a72a2102039e2ce59d28b94eed21ba5fb40f27ed8d1db3e2fdcc24e271938f46dd52b20c7ae6663d629e6101121ff177c5b8eb1c5a5d4dd39426333a74f162adfffb2af524811b73a05b1547e3514dff15148e5059eef75aaa18e0218f00bc6d2a5a910e5106a05cf6466ee6499b318134edc4b9e10ce677b13bb4ab8114c9bc8b72984e94984d4432a9bea8532a657685371341bf4e43095916db038031c0aa723650baa8816e6db9f5fcc8971b3030a05bad9b52acb139513c9bdca0df230f81364f9baf91f4ef599bc3763461d2d9a39aa60b893c4b69081e0044a8b3bec73d7ae594bf078f35b6db775196a37b3aa63504caac07a63d7fa8db2873c541f273854493a17b374602c4109d097cae77d3680002bd158eb365a51c6674e8bb54a6f1d93d0409facb494bf8c3f60f55fd779563d0103c1143048c619236f96b7bffad1f9a1942b4264c09c05f584417fcda8f035c50a7438fe0ca786dd649bc34003a0bba2bd788735590a272108b070a105b41f0411955952299d1065d3d836bb8f91811a39255f0e6e86da0f7694d72b78a9ee138afc9aef1c0a4f469af6283bd4c0f44d0d280fcae81dec26af7e23797295850cd437384541781b79e25792d2635cbd37622328e7cf3b19aeff8f730deab92ef3dd88359e1c7a4831ec147e9884a715aaa892a2f6aec359af7213bfbc8d3ba628d3b6c5536f1aab28b61cf7ca39e02e947ddd59b638432e72d3b5598d01ed815c3c78886a39bdf2e98b397764612dda1c7bd00f88887a6747c48ba0a9f40e91b2d8d2c5fe2c0df0d62013d06dcbec20b7b4da36c26695a96eced3daf276baf48e0959afb650ec2768271fe25770134089b0276ef42ca1427339d20cb3c1755195232f5bcf816137b6d41ef702b0224a4af76383a99e8e6641463f2e50ec5cfd3b1dc416be5038751d564b83c7ea0dec74fa290fa67310f32f641a995781a36a844f244ec2861d00af69b657fabf1517894866ca76460bd8c997c7eaf80a77491d37faa944e963c1c6aad4114dc624ef90a28f3e7298a82f1e1a11efd01ef4650c82f8e03335bbaefe3af99c1d561e345e2beb10d5bd5d807937caeb12e4e898ff64f6ccdd0a74be8a71e4309e823d7a3430fd1d65e0c97ca7d2fd6f34efb3a7877c9a3db947d6970dfff83d34b7a6e8bf97914514908f359a37e0cabd1811749455ada7023c8585d41c89391896255b064a1d806f261988f730ba42c0420e54aebce711f97c5179b7b08071da6e5653467cf6e53982f6b79a30b1546182182f950a72ab17222d31cf52006996f1f8b305db07ad31cf3970c16e22f3499adf6d37c969e8460b13974d0356c5ebdf0edd7b6289f8fd9049579de0386d69deb79b32d862594b7b77bbca21856aa427c9eba060ad414c80ed0b906262ace17144cf817a4444af4fc924af7a9df51ed925c21df86a4c4de168c048c1bd154a22c4c52c22c8a5616e9718c61cdc8d5df28b7778ec6101c18166cda46072b834c594cf231d93a4314ace5abb7fb2079f7a8a7e8e98262e9f7c4969aae7dbbfa6bcef28e916a77f86ee8cfb099f366bfb9625de77eedf68790d7b054eb7452b7582b4abd7369e1958981af589789bf7780b82c921e30a70d0f1c053f5962510e28b658d43d3a9e8a197d73b50b0188cc3170c2007f40404d168ba71c818831c4876862160d6c6caadf2511cebbc22c53ec98f58ee1e2fcb16b4b6541b3b9cd788e27862e038ca14fcb75c2e58423eb3390b6e225138177f63b20df5393a4117fe416184b574610a1ce780d4840ae57431475de325ef94d774081cef7dae04ca4aa0cbea79b1ed14db4ec52189c129c573473cc85c4fa45f800761517bc43630d6edd2f17e74866f1d21bf3c44ac734d5e367491be23c7a31247e88241cc29a0880d5f58cffb8692ed9f2c8ef768db35b9d148c4b0c784e380715a0243e873da7cc74ac325b873606a8eb82938a0347cf95e94e6e1c3ccb0d5f9d86216ddd7f050898de600ca358c690e5db0d7a0e1ae9ab5d4c69a342bb003b548ab63dd9bcd53210a35092c42462f9698376432d23955548bc5700535f5c788d02d8383551afcf32dbcd9d12cffc0e64331801913b01923ff5d2b2612d6bccbf419a23c2004463185ff3185c8cba703c73b29853c0a9b608108b1a71d3986da223d8d7ac13c3f4ebb85e064ba9974d7508569cca22828dc1a0e4c05998c3cb3cff24ba6f3d0e27d70406fade30180e16011d3f4e9d207dd5034919e564bdfefc3e83182637646f2de6a572070e8a051d413c72e0a1b72efb68a03df9a6a015f1d51553e4230c29146ea883859a8e76270b52b2d043b43cabf7930f9a6263b4619190caec406b0a981236d1b24dcc1df61e7b3fb24797c8c2bbc0c58a5ca0006c8134239b3cb780d0ae073eadc2d9e71db85843b3bd1735800f9fdd3bce5fe26a4c64ef0c575532fa41bafdf93ec78c01971faa00d1c12e030dee75b7de47770a02a14e57525fb1a41a76ad90b9662154ca4fdb121ddb8782f5b1b24a6026475217827fca025b1d51431c841dee534c211c8a6900925a8cefc170f5af5cf15d4b51678f6574c10ad3833ae39c2eb54908c4fa6a8805d2a70376a0cd1a6ec6b5a48252ec7b624d3b2ac51876608b80f016b0860533c12040bde77948e8e5c37db7fc34a927e03f3115ee585d47049a4074ef324a56ccd38d3f0eff4b59faca213e27a71c5336ac4b953ae4222db3218a4ca726380e8833cc8b7d95acdf00b84627350e413b9ca13219753a914344bdbbb653080bac60e53aa6f22859bfb20dfdd8b1d29a141697fc13c0a44b7e196c679272908c19edce8fce4941761d78d05dda4ef064470ee275aafab92355a42692fd307a2a2ed01390657adf00690c9afad6330029f70667f650f8cad8a36540142a00e1f776b96a38f3419f0e49fe387db91fd03ef77c5bb27c2dff1e12412e9a20f458de5dc68ca553e2ab9fc951c353a846d6391e46c8edd3a1b92729f335de032818e9d62f142c09339982a36b39561d944608a319add897e68889b72914cc165b18cb9fdf9e741aec27cee4d118875225133d2c3e76cfe603670fa4b14dcf41d4cb0b14874e4e64590e39c50c29172c87f0b91869c3c4b2d7c04b214c940598a1b6fca73754de31a6ca0611eeb97b75a7600b6a3cf58a39d94d8efc38c897eba9cc25e54c5f8c7d6c9b5bbe2d672885849d851b706dd690bd5c36791a04f4c551753e1b786273ba4ecf3830e435fc6bdc3adbabba77d1e415a3b9bd13d5fd3b110ef0cf567e35665cea1313752a276f40dfc307dc1c73c0631405540a5a1a66cc540f82edad7cea37869896bfae0c879d4a1e2dab26f6cae8e133878c23ae397c2827ac617ace9795746029e7cb25086aee560eb60a049e954bb8d34890deffc0686534c31d32c48a97d98df53886f7d9a75a2f05cb5f002c48c5f7231d6fb1b8a08a3f7ceba2dfbb83819967547eb2f4ae4f215994e99a70fadc66d6d60cd8d9f4e74d47b9fcc39105d8908791e5ed01e5f401e5665619b07c0e7f7ed733bdffcda82882d25d50c904c1a5af78c8de0834ce128a4bf2656c00b07205146d8b03ca27ce4bec4408679b9c1b67c5ecb8f221b91c12bb39f975e6a78e0405339146cbe424ef87380286a5e057efbca8553d1f156418542308943af506b01bc7309e654964304f864b60519504de4f96807e552466b6b29f79a2df5010c9e84612c46f08fea4a865053a17ab94d6cf15b24db344e41a4d29c0a3d235edbaba926894aaa8e66ccfa5626a644afd986864a0b9fde1492fa26ef8e128a814f28237a2562ab72c97203500dbbf59d04276723a15780708c88a697b28131a9556acf0727dca42f5c23cb8208b335041149e21cd0f72454ceecced393267bd73f33b36b721ce5c21ce186bd8b5cecf4845474a1c50ee9cf2be2e79729783fd322749989f25810d4db614e47352848c13efdbb6a4196018ced538718856219f5f2c94122780f8bdf7055824565c5ffa58869b927f3bb8c92c48fdf3d4c11d5b78a283764760aa9b4980f7900bebb90ceeb82c41ecabf4e9ed80840892b94a72b45a42edc9afb405800a1a22e63cc910227f3564e9924e0af19bc3aff0e6621b5665a0a45d3e8d4eacb60cab062ba378628147c6c727addb04c1b555ce7df1112c012a58f783ebd2d5df5ef29a80f86d06b518776f6b30837c43d3791de6ffc7457ea72467b25f41d6bfa9553a298c90e5c98858d692b01d2501836ff90fe18de9a249015e01db8a3889fb0084977364e58fa48b7c957cdf411eadb7e970616cfc8889f8d8cc3eba55c77fe3082343b4efa43bee7f89b47f1c9f974641ca57e33e2b1f3ee27af17efa583102963df4db9ed213e20afeffd47837a86651cc28c2743f7628000eab626de463a2af9536eac3fadf84ad14094661a3e6989a89a6cc91a0e6df272a59fdd4939b9565dddac7e4daa4d625d4fcd3386729680f8489730af36faaff03eb48444cfb5b184eed58c804b8a3d475382431b07ca1578b5a1d612edaadbde3f9c457d897415a31313a3cff89f1dcbe94986eb492047d9c395bc690bef00b9908e5f41ca5eb11fbc6b4395b22242150ec14e5c51b59df1c6977755b0f4a024328c30f8c52a8e8b38dc73568c99003f034a6dbbc995e704df83da1027c93a1f7ae3e4eabf3081d5e01c4b78a52615f4f2ce49cf18561dcc19f93018eea9bab97c278d79b9f35478ed53e7088d167251a635baf16aaf89889b8937dc3136f57912cd9a90c4ca9b22e8d64858f74412b5f516116aa8350b0775220943e899c84034d4ae8588dc0d8413d141df6dbfaa447ce384f2a74eb274f6639c73ca81813e6e7602c92f60fd9e89c45aa7c29ff3b25d31b3aa9de664abc2329dc85382499c60598b10630105c5779b34428ef7381f26a589f510adf684d31156bf58e111099bc66672feccf962fee7419ee2595dd9097db08580b9b66984040578855e8561f1214828d2fe184827cc3cce54d459ed38cbfb162def06cc602c002708283808898f513faf5e02499a73189fc49bb5b5b61eddba0ace5b117cd5b9e085d0210408b41d5343306cc72f92211c945c9b7582b77397c213001ad4cbba7e28184220e886e913d1e112750e2515a27f20896249d8e1c97f94d324395fb111a15481e533e5062e661616c3b9dbfacd16f95a91958aabb4029340f571b3d484139835f41861758f001254e14c90c99ea261395b69281a1b40b342384f900509e3034a840623904f74b83a44709308a19d10ecfd627960043200970e4d7d67ac98c5a16540e7fc1dc8149defab2a456c31bc124028eefe68548f6304a20e0b8eab247abbe4805b3279b2647c03830ea7daab8a627dd05ab8baa386f9e5df4452e11a221e10d97a04a307137f8b00e53c0716370b5fb854fdf95f72c8f6e0031a3027bf92e2554ba06c4a8a2410c734459ac3da3fcb76aae7b16c9889652f30241ba8e7b610b0fa6ac9874cdb9de21aac07d9d4e935e3ce87b4f42f527ef6952177d874fd0e7f123636d7ad23f9cf913c8e3a86b29261c4fd78250d3adc1be518c4988d1f45bd1be89e25ae3274e6f63ec91a8a658dd8ccaa3fe96de449cd1aad11ce9fa4a5041d0bde9c851a6a4459f8cc22e53ed6716bd4642b98ebfe532c70efe42a048ef4316ed0bb4032bb6fdebdcdc0998717894f47131fa007f0d6b50fe33009318fedef22a3759e8cfc2790927f2a1b53f7615593174b600646f6895a2c1fae5d0c36f8015f63c3f132317267940855fe8da43a67288ae1765208e41a7e17c917b64d6c9edc905589f6c98c9a09d07ef42cf305d9ab1f54aa91c60b20f0b785dd1d020f737f47d18c1483f82a542088841e71fbfbe528112a535bea0c213341b79e644dc70876a644cf81993fd6557bcd8044c716e0a8e90e56010ee315ff8ed471d17c36a24034c94a3e479fd17853c4f5f56f1293c5f384cc8a0e703a1ffdbd62fe01e99f7e2f6f5c1ab41792f384ee06d2a9052bd5296175a9ffee40e4c2e5f04c4c88de4128ba5afcfcbd635d001bdc5407747907e3deb94513770b5555c0a57b05c95a597ceac5aa7fc758ca2d30cbd1f3f3f5cad764b40c531acf1dd6e456081aaf899cb5aab9f5a9ab85692bca71d970ff83a3019d1f5bf1099fa3d1e55ef9011fb75b274ab532626aceca6bffb54bd36fac5efb52a1608cb6ebfca2414b0bfcdace371c865dd69397521c62e946cf477178c0a0a0570f767ac78254bb906d5060fd6d0c5efa6aeb8824d2bc67fb2815faba4b6c9ced052b7da7d58ec19e5506c8e3196b73bef62d51e05cfdec8ab6a144e410680d6214f6d657589d389b6e74daaa04a87ea8bada76a05771eaf99be2cceace522d0d636f07f9a54025776258bc0453798d83401a0bf33835cd0543011339384ef8db65260c5d4a7ed7db4026735bb91b14223c092997c05b4f9eedbee6568ae453327d01358b253b0b4c3f5a1d794a0b2c12ec3bfa919a4b42f053d801fc80ba3a27d6a7d78fee703a25a335f69c045ddbcca2a264d98ed34f48513fb209ace249a7f2a034b232689734d5b879a6d0e005c9b01be6deeb628f627f8dfb00d6d1cf017aa676d371f0edef4f360c6083b1e33061b6ee8b0b843758f4c15ad1599fc879184101748392d9e79cd0d6818740082474a379764849941b15b2b2f582ab4fbde12802a6cf021f5cfdae058ae93db89978d8849efb7f39a91841b4a2b4626b662dcd8d08774cfbabf7c87b113f39e4abb010ddd943ca835b9bcadf09048d15599c3d3434cdcd4e3ce1b5c241ff1d24e5f87f5891f5081e94da9f72941fe47129a3d11576a2bdfc0a5431b2d11b9f131c30536325dc6fd1985d364ae88b212d5a18234879bd1fea0c8649e0a617bcec08c047d64d1e99a8b38824678d4fb92e75cca479594f201f93594aef5032c2d09907c9aee2ab1fd00a7334afbd6064e02d49cfde1c0795210f7bd8157e3a29bd67c3394e5a26c42a5da1b6c9688284f0f0c186a760566f06df4736935eef0aeaa78e992746367dbf1b1cf14c21b65aadd7ea47840cb09e92ee0b231eb7a9eff018fb8e530a1c44148de3fb5f2a24e154484257729586caa34dad7d093e2cc4017e4f96e18b63b007db2449a736c00cd22118fda189cf1b7d83e945a0288a2913724f4417add4012770549b00f84055bca0bcf2fa9f44f7aff1a65b62d86cce137847466246e0d38c9d35ed0f73684871ebf9899721a2ccbe337e3c3e95acd37b9a58008ef2da0871634c394ad3c3183e8d5ed00f4628f4f968804ffaac6119701d0b256f741e25934738dc058e96a487ee2963ca61408fc9afd40a2fa58586e1cc69df0c653da9189a574bc67fce46da4439e9acb1396b2f1a4eb841a86f23cd74c529b05339d2eec8811e4b8934f72971a93a79ff0036241c2f3c74fce874dfa9131c049a508fb0683f46cfce9893f92193aba9a7410d592a1beaf41277a75cb54e028e1a8a4b1d73c76e1104ac53a2bed09587916d9cfdd0e8df78cadb5ab48391d472eeae48179f3511a692719f41b1c91bcb6e68b60bc04546b409df5f1c974da0dbf2d86fdf01e46a1d1d85d2bb1f7b89abf05d5a371ad9ca82b84152c4e0f50a6ba854a06a4648c699038b1548d4b708ed2a143329004a00546005e6a37fa9dfd3738ddf2408c3d131857174d219ebd1c9d1112e48d15466d9739162ccaacb4a4acf057b1ed1a785552eef19df539124f694c61442ea7620dcb9dea3398cef1fabfa404f61624b4d77519c7efa21ec9c0bc3f4265a967fd4eb9d6d86aebbd058ce2cad4b957d2d011d0b1bd3ca6559bda5037923e9aac99f5816c0f012e4d2398aa01a8d7904e91745d455b3d747365ddcc54bfad2720f9dff443ef5665f38303f1169defabab2c3813e63a9622621994e47782f1c7d732a4ed3f7a54ce38ee0aa8a1ba4d2a3e292e8bcea86f76ab824e427a77bf01579ef68476a1168f1e6fa6ab2677a500dc160124f591458b981d87f013686a7cfcbd21d21d6f2ca39b4044bf85529c98b82db541dee3ea91dadd14ed0a6b0d81143f8013746044d18da4495ddc16ea07df8e823742d675835dfc8146d9d265974b7ea92bbf330269731b81307c5b89c3897ae5da5c93f14e7e5a77ee1aeae17c23232d41df7c497fa9f0ce1aa53495a43fda0cd57fb53731dad3fe2a06ffe7006a337636aa98595eaebba26fc38538e451fa6d0f19a7d1681acf80e31fa5e8b763274e254a814ffe1530c06392ffb111d2dba6e84907a35fd6bf4d63b3b917eb4fef98d2b5d6edb91bcbc29ae45d91cb2e5c0b6e6aa6b5edd52baaac7f80eab8fc31bc43ff3d8d88bfa2ee57b4d65c3809f4e6c076bae8db66393fb674d56a5e066cb6a4d2cc16d1246ce24309530b2128e302c7cd33ece3f1186c19c8d5b4024cf7aef2aef1a1b7ddaf4ce2be3c3e7a46addeacd90f8476d18087d25b8fb2a59d0588eb64b8381e648da570abd835f39dabfac3ec5ca173b5589012ba00a55a5e6e7de0d54a368ddb067cec16e773105d2dd95ceefcc4881311008d6ecf342d07105a2fd31a4f6ff5c81107cb90a73f6a16e8056543abae33842a45f449f8adaa2c7ad0c351f7f4fca170312256857f9631b6f83794be8f76b0f307040852505f80d8ffe854376b1f7a3ddff0a741ed7503bfcd36baf69b8c958f425526632f1c2ae0a76b7aedb64a65a55e97d6e827db105b612eee91667b48b831fc596d0aef2e3305c8210f2b346db964752549239c3020685f96518b91f041e68f90b0459dd40190c009773e7e438b2cb6dd0a34aaae0db2e597efa8bb8845ae2374ea161b821193dbb377f17c36fa49c98f98d5f131fb2ddec88b1f3427f951268e226c5cf3c8dc5dbba08ea4cb3b13d23f81d8c3dda40c061e00c4bd31e16c374562682aab6caa650443c83eddb3a95f1e5e46f069b6c8c4ca558f33965101d9413f41aa522d2da1c4217e39184b0ee2d38ea78136a5323865902d3b3aeb0d9c81bd69aaa94ab6c8bd058c39463906a9bfd4fb5d2d7da95bb83b8305c595f063635fe2c72d7d1fae7a27bda68006845069e03186dc534a000ab9bd3249e8f3b51fd78124cabf0b45a674358397e1bc067f5495d499708e87b856ee32a0bfef95290176de9beac28888568fa4351719e8a46c6e6b793bbe6ea373531373b4393f302cd684575e9187e80766576e9e69d4de7bdb41fe602f8851102227a5700406f59b261a39f305a63d2e8f563235a1636987a6b730dc2ee8677c9b1730b2d4081e996024edabc83028ccd9c2e136a3235ae7c186c2ae3da3e23eaa2cf053ad54fcc524f1105209f261a3a2f1220e56d231c067631ac73cf59203d8899a99ce677c669bbcf37284e12e104f5cb70a30ecb5401de3f0332a83c90365cfff3b68ff835aae2e63362fe2c381946bf5f3a39350030b91528b70a41c1210d1a7614857cf5c201fc79a0afc8956c717c908c4d0d1fb628a2c45868a788a389affdf2705ca103b31a6b929665596feaf21f013d2d1585bdc8b2e6b290a51f8c3154d5f7f8adc1392a810b25f910ec7dda37831932c72dd3a85f001e33ac3de1e300c92661bb394c5c22a7e29890a6c4bcf732cc7de0a1d94e1cd611d282d809fa23f9b2c327fec4094c2f13ef7ef8e1bb3d500069b50371e1025eeeea5cfe9f3d625e58260f0ee0cc559b4ad615fb6f468ba4feea539f889bff602b04d004ec5c045a868bb154205e153707b9a9a976b26dc0de0f505bf22f677e12bf66ba393accda75396376cc077b27b6b5f374d6cf1ba6022c5e44eb83206cbadc8708a371ec267369392cd277b9017f8d419ef6dfd66c77f7cade52a62465270b5a0b820af38505a7575a2c961383c57672088611268c161484849bf9b2f2a1f79a5e6938389e04c70627f41e7a87c184d178783c0639f48e64be98807aa5b5662c9f71d2aa176798309a12256990cd171526bdb2b319cfac67f6036b9508c584d16aaaa0335f5cbc7a6563b19b184ea8c587be7b30616c50d00f72e8db355f4a1fb238388d04a76b424e8709637978b278325f483ebdb2ad96ccb68042295e98305689922a72e8696cbea07ce8e9925ed5d98ceacc98cc787228646ba8cb1272e8a992f932fad0d356af6a8cba5e39147e100d72e8a90c264c0d1ad2ab8a337bf5040749e661430ebd07264ce5a1a10fd2aafaa107325f3c59e813fa1ebdaaa1d73161aa12ea627feacc66a197d12b9ff16855f7a1d093adfabef29043a15828147a70f41c63c2d49a50e86bab268c075117fba10ffd0b104728270300f6f331300cb6c15af04fb6df3b88f0d6da94805b0bdc5aa1adb529c9f655c6d0cfb69ad00c9c8133fb220b082316148a8562602c28db77b1f29d341c1e50c309e168383cd97e0b93f7426b694a40ad056aad90d6d29464fb25954e9c6935a119389b6935d93ec905d72216148a8562602c28db4f295db7383c6e71423838f651485b8a6d2909b16d29c9f64728da5667b66636b335d97e38b2618dd5a01a8bd5580dcaf6456d086b2e379c8a537978445eadb8a441a3c9969252c4162b0969d676f659adc121ae35d9fb4220b0bf26ad3dc57c75654114c67322c6b28d05e59317d4a4fd2bf395377394130ae35d315dec5b99289365eb8639c37392ed7f1ed09c61dfe58ab0fef7b93e9798e3abf0cbc9b6334eabca30675bd22acbc3db3fb56a7b6bbbd166c7f11d0af3b9a88b7deb0139ca7e18e264dff180bcd55ac203479abfd1e736496f7eae154fc9dae7caf63f3088cfad7d9f00f294620eb3fa13a4b8560484fd34db0d35d2e81490fd7bc3d0064d582ec1c3dfbf5fc3eb9afaec6cc89f1b298f7d19403c8ce28f2d6493f63f960764bd7a40d9fa7f3bb883f4c09bd91365dad2d2e4c4319bce4a7bee20794292bba162e18d1feaa75ae57df7f5ef57f2d62f69e46a23d7f7ba7182f69fd46a8be5b86f7036c971a191d9a449a3b3a5deccb40a49d37890215eb946924bafc9de36ae6a535fed836b6fed7bf7de7befb655ee5ecff33ccfebf628adb7c3d5d5a4cfb8ca9ee1c64ed22e9f71244dd2e06e353900997fcf349256d1b48ad69badc93f11badb391d0c429041106e62704a021639f49a66610db2e91406f0d3730d9ed7d224fd80379a2b8f36d681cab3c9f99ed7894b32cf509d334224842d74e2168d76c85f860e540d997414c9b4a953118625d9ffc35c69109fbd5ecd2220f4a7f93b089476bb3f09bc39d2f95eb77bad96be678acb2293bafb12fe844e9e52c8b3c913acecb95011d29f266fa6f89910f6c7b2e323aa8b17a84f6e4c7da8cfc6528237bf092ea01a714a67baf7ee7d7bef4948df7ff3cd37df7cdbb8fb694f3713f8fede9bc0deb86dbbdb568ad947b276f6b927656cbe7d0585f0efdebbf7ce1defc0640feb36665ab61f2c6e9e84b3c9da39ee0d43519be068da504168df450e614966cd147afaf74d21bc83fcde7bd257f75eac914db637b7514b935c49923252f3e5134281f27b5ff620443716ae4c65fae97fe3cc9308cd1f9661f2be7f9a3cfa14cf1e63bf8d9970b280861257004971852c4f29ae88e5f95c9e525cc1244f29ae58924b724a714512a4b8e225d2d041b7d48aad103815d736fb9a28924da664589cdab00c5246ca29ca6ee4b885f07bf8dfbbed44f1846c495243417fab4ce28885db9b56f10d5aebe7ddf0958adff01505c5f7953851fe58c4c92de6aff46bed6c7dd0e2795f775f9e25a9d6af51dfcc5aa62c6008bdef2a188487a11d0cc28aac08ef6fdfdff72c5708b5bfe00e1fd615ee20a249fa751d6cccacc0639c30e9d81c832bf8c1097096248141cc14b8277936614213b97e3dca09e3d1046726dba74c5ae5d9c67c5d874d3b6aee3e889abbf97232cd3a49f3a5c5cfb760105f76172c84b3491a4dd64fb5e44abf9248cc171ac58f60aea81528cabea561985191edd442b6b039c3be0c1484dae338027e37d155366bc396f48e4eb64f9df46a5218908d2c49778dd8476850b665d8b126eb5b1c582b2c43d82a878736b2a5e123a36cc9560937497865afbf3a5fc23ca963249ab4df92cb39ab1167a1290cbd930fdd91d1a0564d2bb84007e749b65f0b996de8a94a7a4371ecd32596e2e4f0559146d93f729184d606891b25b26d91edb398212ce96b4f61968479ca51284c087f1da1ef4a9acb1fdcfb7b3fbc0f62e6f0bd0986df8d6179c308cafb3844cbdf7be38fd17f193e0a9e4258b2c8228bfca37bef7dfc11e2215af65ee47963c97df7c37bee272884877f74f84737ceeccdafc9a64e32fd8b04ae766774066fb6949b2bb76fda7bedd55a0b0ac1c13d1034db6f9ffe6995f7f35bd62a7a39b1ec9df14ef283f59732bed35cf58da3c63ac9cffbf92191bba70f7e1fee3a5ceb13cbc261eb13cb6ab27bd8824ddea9b399907e596fbabba508ba7dd4ea70200802153b5e8001164153f0c1c523061eec24c17505114037746e831d843a36484871c27603288298cc1c79a20a52004dc1862564564441064b204181820a278857d802c0832e28e141122cacc023680b6128c318aa8081cd0e783a9d1d58d1a22ae10227408315343f8082149630b284119830062098810b3d4b9002138ef040095e8409c8010b3fb059011390e4800a2870420b8c20861c48a1c4153355070859705e038e2e21093de5d964092c31247b42817c2e908c94afb839377ec8c1e3365102154ac8610203bd57188c20872ec8400b41541421073a504204275e00434cff9c2237143f557034d74b020e6a1294c8fd77873c9b2881437e916713253d1b8dd72f8488ffe4c9730a09401ee2ef1f84900ade6cd9b6de36179d39c39f858910b481030adad1cb46cc17dac2c05c51921c47159511c7610ab3df12c2fe98f94224cc96c2d8d5f7fe0a982fdabb7723c76b4e0ee78d33c6085f51ead32dff3967987a7446788c4c9d3eed183b12b93175ccde7b8c878b9820a6c3178b27f9207c4fc4711de5bec3dd734dc106cc986dbcedd6bf6e646122f43c8f1872f3fdd93d872dcd114342cfa5e44c3dfc8dc49820c6f33a76cc1d89dcbcd9ead6431bd6622c5ec2e4c363ea7b4cc5454c10e3783a6a3341b73bf1daf3812424697f6f188a622aa56197c182b024dd3c7be420a4bfd12d541af141440f915267e2cd9612c595b9f766afea7b8d26b994a3b8e75e636122dc7a686dc6597e6404e32e8b7b5292d03af7ce7dcfbd2369154dabee73ef35adea9e7be73eb46d8ff2a16ddb6e4884a3b80f8d282311dc880b39118e652e89fa299f843fe9a967bf0b6e279828eee7869d356770a14f22e5eb27414af9fa3e52be62d316c23e48effd2d9ea33a99c3454a587414e78413df97b013dd9722dd6992fbefb7d1e6067750264d7e9bcca35f098a21f8b48faf9c3551dcdfeb93396d9c63bde3ddb6df6e77051084108956d9e7befb2fcf14af3fd2e82e47719f32be32e7377306f7dde86567f90a0947717f6f188ae23f49bee60cee3d7b479ae47ca649ee3becac39cbdc3b2b7328ae0f0d5ab33031b91e264afb1a597b1a5bb8f7bfdfee5faef3ee36a6c4c9e1d49ca1556cba8f4d172fd19fef7f59bbdadfe85b23b5041112cd6b4f5aa7bbadd552ed0db47675bfb5da161db4b3ebac3d2b13d79dce5a37f7a9b9db396b7577f7e9eedbc537aab5d6e25abfd6eaeef334ebd3ead6dddd45315a6bb6da4b36c04a95bbac3dcbee32b7552df738bbbb695c68061b19eaf5bccd66eb8544d556ebb55a6b3532588d0c3d4cb2fa90eb8eec63fdcf7ad6dace5a8d0c5623039d24d75181b657ab6ddc56bd7a7dfb3ed08a3ecfb5480d3f1087f6a3ef46df14c5e54da568d4005355fbf9696110cf7d04e2d050b6af1e869a63d14fe56a35a11273456f7c7c598190ab133509437c59978c71982b1aebf1653d41ae2da83210e2cbdadaa698ab66f1f8d2bf909d0cae061d5f7ad08a11e6aa5f39be7426647fc2a3c0f1a5effcaa777ef8d26b905d090742fed29b5039c35cf5ecc6977d45ee38f8090ef0a5cf9ca0982b77d9f8b2b1907b8afe8201beec9fd2b7c36ab4119a093e7cd939221de6ca815a05b91b065d831ebeec24a3554d7d49c3e0f2253d03bd22cf204dca43b140a190f1257d42eea74988f125bda142e0e14bda03f24b8a03306ca12a987480f1e57c43eef7408b7286e1054bbf0eeefb3e406e1f72cfc8dd2ab93bc6f5e18006dc0c71d343c65f081ae3b163012c1dac1c4100f1c2f1fa4101313b3bf786a278809ed980797119c0756f288a3ec0681480003d3850ca6500ad19ad7bc3501459c83014c51855c68277c0657861e0f2b2e0f2bec0a616382e70c922a2e0524c0989b4196d8625971d8ee7f2a2e0e17837deabc36111434dc365b5c9e34a47d315a17b75aeae35b25ed6b1a063cd8046eff9b1f3cad8d953c901436c078673e25a4bb87971ae959ad3d85965ecec9580b81870331c6b06245e98197c623b2acda47369ecec8dae2b0a3837af1769766f7069eecc65958e64da793476f6c01d36c87c623ee1ab0a39383723ba241c3b831b6be3c146d3ca34860590cc079c419976b6374d603938dc971d4ec7842eb68414ae285add13d95dde10f69732e8673b76aeb8dacc15d5400630d05f5d29231a7f7f65cd158d5de0cb3a63812f2b4d05fabf6914d13bf4cc55b328f0a5ff4ce04b7722815ef9ae4144c3c099ab7e45e04bcf81c0970e7b409fbe6df86896eff79ab9ea1d077ce9491af0a5db0cd12adf377af40ba0b9ea99cc973d63c097ce12a2c5ef1f78b489c95cb96bc797cdb3802fbb474797be73e4f8b2aba0335f5abce6ca61417cd937407cd938389af4cde38776f1fd7d64ae1c48015f3692982fbb26f7e8bbc78d46a9ad047c496508f892021da0c36f1f367ac95cd51c982fa9cecb979489011afc2682ba788d2fe912e64be8fb696baeaa6cf52575a9bea42f1ffa7b1a5dc490b9b2348ef2efe1cbf984000548f57b60c21821fafe20ade2be1fc87ce95cbe9cb2017c397d66cc9595f1a58e09b301eae28ffa72ea04e04b1b02903157d6c779b4ea7e7f8ef9e27d3fc9436e1ebe7c00f45b0eb4cc178d4e1820d4c5bfb7ef17411c7de32bcfa5019c017a83811485a14d1891f20be0f2ba0570057029fad3a030f455048dec1d0d055813604900d7a030d486881a999345e00581d703b00d0a435d3e6cecf88acb71c0cece10b81469921eb8ec9ef98abb32b8bc0cc042e059f6ff81c2d01675f1e7a193fd02ed702dc0a5c33f0785a1353970d9f9c282c0e5050286c39f0785a1343fe0b2afcb51fe0ac0310e94fd7b50188ae4062e3b6f334725a08580d601b00f0a4367a88bbf8d9ae3ab6de7087591f96a7b154161288bbaf8c3e4e41820a7062eef0a975785cbd0072ca3819d65048529a2c51c456190a02efe05a021004d0f38e5824b312cc31958062edbfaf84abbe1008519425dfcb5192014268800b04f00ac0f0a972289cbe601972200700c8ccbf63c97f5f15c2e97f569621cc7711cc71e4da00c1ec796c7df827149f388c771a4791cc7f7b73ee3388ee397560ca618df12a305979549a6afd70effffff3f8ee38fe3bf0065c6ff1d7efc1dc6ffffffdf61a4f9ffffffdfd4f263cb88cbfada61ec8c3b1a9b8ea6a3e9686c60acacacacacacacfcffffafa88032bfb232fefff82b2bbfb2b2b29257565656565656564ce3fff8b8ac47642e994ce662399d4ea7d3e9b4b2b2f22b2b27119459399dfe57fe574ea73f9d4e279691e6d3e9743a9d4e27d3ffcaafe0d27d58c6ce30b89c245c0e97c3e52479a1a2a2a2a2a2a2723a9dfe74527101ca9c545456fef42b271595575151517931d2aca2a2a2a2a2a2a2625af9d3ca0997be846bb5b816d7e25a2d93288aa2288a2a2a2aafa222b600655444f1f42a7f52c125cd221645d134d22c8aa2288aa2683abdca490597decab4f38b0b5473812ed005aa69512a954aa552491445b1540265c45249e5c557114ba52f954aa552a9542a954a2593ca8b2a222e3b28c36860301a172412894422914aa5d2974a241228532291c42fbd58c22412c9c548338944229148249249fc9258c265efb8183bb7b82e24d7755dd7850465341a8d46a31189447a12699402ca9046a3d293be441a8d7e341a8d50469a47a3d168341a8d4ca5279548b8ec266633b3d96c26250cc3300cc3d168340a5140995118927ef4a41ce2300c439ac3300cc3300c4da41f9146b8ec9994b133cace919d9d9d100882200882611886e04f991004471ffe28c4200882a19166100441100441d3e8c351884bfa935fbebdb6d7f6a22cd1f77ddff77d20083e087e2128037e5ff8e087f8fb3ed148f3f77ddff77d9f297c300471e7d0d8d9db584536d6c6da584f3a8ee3388ee3beeffbefe344a0ccc771e07f0f7eb8a41cc7711cc7711cc799c0ffc00f9734490c89582c36f3eebdf7decb71dc73dc0d8132dcbddf73ffdd7b69bef7de7bef357dcf7d5ceec6ce9e7633e4e6e62648abb5d65aebbdf7561094b9b55e5cd25a6badb5d65aab89fbcb61203acff8aac3f12014c6738961a8cd6477f566adb5d6da5aabfd40996aedfdfad7da0ec73a8e35d58a81e89c9a333a9b3e9c5387f3793ed852351ac673f56ade565ddadd63a0b9bf41991fdd3e9d13b2bf4b50d8dfe1b87c273b6cf341765a1396643e7d2219bab27b2e4759bbdd1a618e08eb0e270653da4c1d66266092f43d97af1430513b4e64a7749cd9daa809cb8af36180d22468b881822b87b9dfbf26ff5329519c3d76440df366cb9761adde52d0c9f4e2eb36086910f57e74cbb487bad4a72e3da109889be76f99e2fba3312967319659b487c28cd3a57eadb4e7668bdf0528f4a7ac7bdb8ae45fcd2769b793fd2abab0b6a652ddeeb3b5c19b2de5b793697bb5da76b9cefbc090a8e6e49abd114a0aa9d4c285a8f2c2745a81c1d2ffd91bbfeaad7940b87b30d4aaf0e77fdf7b2bd4b798e4d9e271c77194f59df99ee3f050982f47c653bd70fc5a4f66417386fd6ef480e68c8ff586e9f2e57c34587c391b6c83b50a7cfb1b935e0520c6961d67835117fb381b4c347eadafc69b79414f7a8321c9f68fb8ac90edbb541196d703e256e4dbf7c80ff5a1401da8ecfd48dabe4e1c49e1e73af568ce007138675c21bbb2ebe402b5aabf1a21c9e5abb006d1197cd4f8d57c344d7e2ea0fe5c2d916887178900208af1220f0683c1442d2ff26432117e91f77abd5e229617793a3aa2f145deec991991e85fe4d9d88860bca893c9643291e945de11d1ca8b3a180c06139d5ed4fdfc885ebca87bbd5e2f918b17753a3a22951775332291a89b11892fea6c6c442d5ec4c9643299a8f4a2ee884824e260301107137130d1f722eee74794f222eef57abd44282fe274a88bd511d11771332291889b116d2f127136a20b83c1603fa2fb23f2177d90568d5ef440e64bf87abd442f47591dd1d51189ee8ce867cf885e46afee0c8f56712ffa1cf3a5bec846f43291e8bb6b23fadbab4de628fba2afad9a30f70875b12f123d0ac421da61eb005063885a6032d80e30982cdb277188c57be9782f16efe5bd74b27d1e46f0bd19cfc69b61f166bc19cf26db07008ccf24f38ec84c32997724db8fb1e29d603f30130cf693edb7bce85c742f9deee5a27b75dd4b27dbdf418513bb99ce46ec66ba6ec626dbc72d6e89937547443299ccfe48da72b9e17030ee470c31ec27dbff140d857b713a1ff77a712f4e27db67a16e082d37c3d988dc0c3723e26c3616b733e786c37e603087897e608422112e69644be3be74eeebbeeecbaf0eb752676ceecc7667ee8c5f9b6c3f0c9d4e9bec1ed9649bac6eb27bc4d45b678d7b4b8624ea138091fed4cfab4daf3618ac099892185fd62539f435f4558909b3fdb47c597990435f93cc971d3ef495d5ab6dab335ba509e1384c984d899239cce6cbd8d32b6d36fb99399905c55af5534c98ad66abe9024fe2f8250913bf6cd8884b327ff8bd939020e26ba0ccb7e37bffdebf951596b1777862304677d9348a6ea0cefea0dc3f9fe4fe236816ebe8dfcacda711877f8fcd647c31364f93dbab6c2f8edd33b64f93dbb718fba73436696c274d36509341e1d8a3b3661c89d3788db79ac4db7fa3bf1a46e9914622805ac1904b9a6b8769adb7de1009e1f6b4bb76d326ea2763bb2d9da1a169b91ad60d9b2ff65547da441413860a61ba5c61bed0ed2975010a4b1a4dfabd1e6d02c72f55f27298b8535d8242fa12c29c3142de2813f2f6d67e2e3ada6694541087a98238aa146cb60dc461ba17c471b18cc664935b53da04bda146982fdd6fef8d3d9aa44fc1c6531b891de4f6b6bdc7090992ffe22973dfeeb8b85a87d1a446af42476d7fb76dabd1e48d7fe9260c58ed84995de8bd10e779de8f34df50a8fbeb99aee3b0566deee4dbbc276fce93376792b7f716f92f8a61b8fdf4421863fccdbceec31da8a3c3443cf0a3d6c6beb4258ddaac92b64b341e9d0e972d9649de6aab3c2235732f449439efd65b6fa5613dcff37c3e16de1a4e73fb0c31fa0c7d863e439f8186f53e5026e479a20fbdc8f33c513b216b1cc976b77bc330fcbe90e3e805653c0edcbabfd1e1bd6178b5effd561bd6e84f400db4e00eefde7beffd3b6bbd9e4884e228a00e54ae364dd6d1b9ae7bad1f7cff06bfbbaeebe7fcef2deff7fdfeea3d0882cf7d1329d4e400e4d9e40959a6c9de4c0f5f1d43af8df5bde98dda7b4e914fe90541efdadede4ed3437bf2f6e25cd19e89dab8efeebf136666cf4750a6dd9d47939c880b7d73df1806a8238489d02cc2a5e8c1f75f0165fc394cc4071f401da86c6d9adcdec58ab0973499d3e446bfbb57ab45c7d6c19cb17d8f8d83e9b2bd1ddba677405db6e7c65662ce4852abd72ca00e8a7ba7eb6aadee5e6badeeddd8759dbbbb7b07c343a3fd6e89869322756cb0c26e0383b8b8ec1ec40ebe13a9d95f88c89b4d595fbfe7f33156afbac7dc2ab99b861e9469dce07fdd9a13b6c79942ce15603b34a6699aa6699aa6699aa66942a8e630c7cda4ecbe776ec87f510cc3d68db7d7301097225cd27e6ffb42a37d8f35e953be1b8550229748e8c3994099ee3bd62aaec7e705b8a3fb7eff31fa21ddba61280a814f1a7d0e73c6f629a3c761ba6c8f328ec67bc3feae031be6e2fbf66e756bce70cf7eb557dd72d4f6aeef8593b79c144c16b50ef40a0a345f3a200a4481b6efaef3ee09940171c544babfa00eefbb1fb7c9ed271884877f7c0dabb56dba3a6f283e592baceed4582d004d92b7ef9aa4c96d4e5c009f791c5c0efea4b2e8585b354975adea9146d5542479bbd3ddfd3f779abcfdf635dcb3fffd663869f8bbdfef534019bfeefd7dcfdd03effb97da3bf885a00e0d13a1d9c3d7876fcbdc38b34b16e1f6de6a95e3112873bf5b3beec5d93c6f746143389b33360a33f356b7b79bb7fab6dcfd0443debe9491a7861323cf39e7dbb9d270266afbf99a13e6ca67d53586af42dcc215a640a7531feb30639388f9f22a2aa3d1fd39a389d4dc2f4494fbbb9fa18bef8c34b3d5ea4a35d95ad6b49c25383a3fa99ebc7d4932d9bed6da73bb36add5feb5b5b60d56d8df93bb2febec6d6cbe68797bdbd3abd91aab554fc420eb246f77fb596a47f2866d2cac11b524799b4c36ad8912fc8eeb405cf6eff075a3e64493693f4dfa349933676ccf8dda139a14a88b4e93dbdb518335b9390662d2a8edb5d96585331bcdd6d278f2f6230844673acce6cb96b7b7af56f56f5f9fcc17cfe2157da5e1543a61ea8cba6cff7df7446af65e88287b366cfc2c53d94693f4fe04ad2bb623c2eea77afb5b815ac495b054a5c8db9760be6fe7aa4a3165b4eafbaff3450365381a7aee4378668e070dfd1d2bcffc20d149f893b7b2f6e4edeb6cc6fd27ea16a883c344680e61f05f73c6f6e0589b68727b0dc757f5c651330a5385305db6df60a2b80d61ced8beecedc787db05e600d2dddddddddd16c4d163b764bc6f4c84e60e7c2fc688a3ff8e1a8eb624c751dbbf2886f45df3c9db77eb037558ba83b664ced8be8e1a4e1d42ed9f7a103fba07bf5d497a6cb592541b577d912d7f1249612a07b603802f060844e31f20ee9d26b76f264d6edf35ec8e65cd6dbaffe123aa3f61b283ccf72f6ef2eeb4ca674dfa7749e149fced7de6ab54a3fa6be4ce0961e2ce6feff9f079e36873370aa1f9de1b86a258fea6e1e4dce192dc9eaba08e8bb7fb35dfcd35040cfefd069edadd3da157aba7b5cd5cf9c865167a57bb87ce58deaa150890ef29687bbe941d865c3f040ae1ce715846936d49df9dab8e4d54fdd93eb117367c40bfec98ab5fdef41fcf11eb1096f547ff109ab95ae336ad2a6b4eb6f548ab7ed0caaa3315c97c01bf7e85c17c097d0d85fe1b4b51083b2804fd92be03813ae873df3de3460ff25612a0961cc2d25bf357e1d7f7e6e8e1e83a8dea9873eddfb3bca8425bdc77a66010be44f8fda4555fab6c886353a443f86308cdd57faa0d148465a511b310ce2650c8c93fdfabaccf912741f5bdedeb0175a0b21f69b21e51a94e932dfed364f59c39a3aa9c202c2b2b0fa1ad5cdf93b4aaba5a65335f80305fbeafdff7762c41fce14a9347dc579a26eb8f401df4c7ea0287b0741acfa93447d45c768cacaf32519faf2dff6995ac913051f59d70a790eb5f21d7df424fcc8f7cd9313168f6cfd7ae56d9d0c97dcd1cad25c96b2a858d5487e6f48dd76751a9356bb5e7c907894efa27d799db6041e83aad2ab5afef4fcc17eeab7f0b11650b0a418d08a71441b3dcef8ea7c9bfac3d9adc6bc1bad2596d6bcea8475ad561194dd2faf78a55ebaf9ae790a42683341936598fa8f966b166caa2210ad1ef983e0f1efdc3a4c3dfbeffcc3c3016a186cba9d3fdd4f5f5d7882d97846b93bd0224b42fa2fd6bdfe2854808a715eb287cfadcff0fe35cf57094f63203f490b512953558902176ac130351520be6f81bdefd0d0f32a487d5acb68db57e87bbd6266bed6a576badb5667bb711a9c9ee67932778723977441fdf8db57b4debbacf1ac7dd9f2087494d6a7fa39bd41e01617d8a63c341b44b83960a0631f3f65fe9e5592910f40335f20ffa8e4bdab8cc91fbe958d6d0c2fb76a2e8773f27cacf398a31e78abe88461ee7d53ebfd1dea38ca3b10c4563682c79c81d3e828238c49fc32291a222b427982f47da6a3d96a9ac1181d3c387f526776fb5dbbd37ade2d1a4df1b86a22dc91293b034714fbf4d1c994bcacad1f2ad182b9996c0b9a22c47d5fbcd17203532fd704cc52024894d5a3b522573345d0c44a5375489833c8ce4bf6337daf041c8bd89728d77e01c4dda6f795b5ed27f0f5fb5c0a57b49f8a331bb6b93da1cfd3d9ff9665a471f0a8843d3344dd3b4da54738d7ecd137fb5f5ebe3d8b0687f82731bf5cf26394ebec74edfdafd4e73423d5262abb5d65a6b6badd65a6badb5d5dab7d6566b2d17426bdf5a6bad2543cf10c1caf58bf091e6cfede3cc74cc6992c393eb57b2be8cda5e97294aa62f32e9a8fa9492755423d799e33245587fbe8c56d1da63beccd9d4ab8f61273b2853eb04a235defc58d91fff38feffafacfce9f42a2a2f8a5f2a3d89f4a3d187e183e07fdf73dcdffbb5beb54f5bdc492acaa1c167e7c3c9a84baf3c1727a3301d0d75f117c530a4e1649e13deab9b818b3a673783ec2e5a6952ae8e86c2cc8e866eb9f4ea098aec5d8ea6f2f6daf2ce2830c96587d3aad273792e1b6f12059decdf2d89293b1dcf15ac6f23b3c93295aa5183c2a1cb306fdce4ba67dded04c8e3e604716ccfed98cf4d6c7f6c41f87e6fe87d93d01d34a4668bafc5d569a2a091b7fa7d5f9e322d5b3295914a8de048fa6aa641c31ad19ac2c12bc9bcd1ae0597e3b8fdfc0f90f9c77cfad6bf4d508dd8a659d8dee724732a7f0d5326ed5bbc9118d268d252de07e9535efb94946f818bb8f8942fd2c21b8d26edc5371283329a346c1a02e54bef03e54baf7d09177142c33e462fe2222e7ef4455a3cca1b89d9b0f82a5f8aef2b17afbdca288ea5713a0a74149054ce71a3156e3fdc6817a3496b319a347c848f16efa2f42e709122d3df484c10ff19238af8635cbc7f0b6ceae1e24d22dc8019e3024f47d198166f82008d69f134a6054e39caefcdd6bfb417c77def6a57dbec6b18486a4789892998c82494981badba392c5e22c4a9ce08d817053e270a0a61798f387104cd42bc0eac6ce7f788e778f1c602c24d3bc2ec3f962660e52137fbcfb1aca511d9ebe658d9b7d0ca04c8b389143fb90a4f76b201f2acc2139feccd1a61e86986b5aab17fd95fb9ed3e103473b6f31e089a5f36a73d2336807be7cef8eac662a9588d982bb6f9f8eadedc9bcde7de6c3ef766f3b93775c7573716faa41ce57f71c29c14ecde3014c57f924ca52850a571b1025d9f85ac99eca22c741f7a7333bb01f2771a5fd10beb9d0b5472a0f40dd4aaee9f2749b26b343c4fc865f7640f595ab0412e5bc66393cb9e39cb0a4972e93e93bdd4a13134cefc35292a117e393a4dfeb48fbb90ee6932140a8d424261e9e9d83c63c7402a1ac3b1bcb9848fa8791b85a1d1247ad297de0b471310348726380a47d308572a72f542f68484638de5098eb50b3a445f7ad2687aaf4752a7d0bf39e56f4e5b9df903e54bd18e10ff187df8255c7e2f7a2f34c110e9897826e1d2fb144cc4b387d23ff2680c127ad38ef047e87510a3534c72639bb0481ae55f7393d8d0490f5fd5da44a3fc6baaa66c8ae7f6fcec28b94b8074b27f45e24e7ac757cea451fe366292b0a61e119f8441373cb727fbeffc64ffe6e9f1e91f77248d6a9dd7129792266cc424618d1f119fdcb80e15420bb339bd44586e3e3e2954e4f1a601c2ba22bd51f8379a74f1222e5359bc69f128a75f791fa75f59f913ca89e54fe510944789d5bea95ef53cee2b77d32a25bd0a592c16abd2d880c2509f13a5f47f3c8d736a200eda4af91b4d815a559f06f5ea49774db7beefbe1fe9cc0f944f79af9553fec5d8208e17efd1d27ba5d20731730997febe911c24a37c97c6927eca7bfdf5687a12ca98c44da6436c36e16a03ea92f237fcc54895349992829738ad8cd351a113065340157c03821fc4574638aab6f8f2e2f296c42b523c5d1c47efe26fb411be225be0f266118cbfd125cd28df98e5c7510a2e6b4679d2f758d21c56a6a91a3367c2e6ce8ccd80643edd7d7a1c9bb099c3f223ee11641ce744fd384ba552a94b7f7d75fa122e6fe47e4c29e94946f1c9a36fd1a3c8b810cf28584802b2c8228bd188321afb2788d296892998f0e9e1a8faa71ab912b14c4cc1041872e31fa20fdfa3dfcde1580eb14bc038fdca09cf1e34e626ccdf58fe7844ad47caae41cd42521beb6d14fddb52c54699418bc9ab5fccb3c91550902a1faca1598af3bb07a2c35b7981a4de6bbf02dde8904c91c8b448a63f51525e03974694e452b7f7a0271aa90d7df59dde895dbe3787a5983b3084a92b280c8012a11129b851f00def1e08f7e1b06d8f8fe57d3bcef9621a62f4a19f40f8a29f74340a471f7a15842ffabed13444ca871e02a517bde7df379a4c43a47ce87d945ef4331bc90efe0704cd20ca87de478b17a17ce855d0e2456f249b8640f99487000a36818f62fa20d0029b409487c0e85170124ef840f9f09348f9d14320fc16df022781f2e1cf9812367dd834c4e849ef63f4a40ffb08513efc145cc4091f298ff2459c289740f906cc98244c8059288f824d9044f8a49fedc3f6298ff2fd36789082a7a35070cc084f2fdf519e67047dedbb2c53357239a466eecbd48f244368f6bf3fb4b7bf8d94654499fadaae817ea17b665a4042ee1e2b769cb615eeeeee54eb21b9bbbb3b6d2bb0bbbb3bed9ed475d1dddde9bbd3ee717777a7ed85ea4ebb2998e0426e1f2bdcdd9dbeb715d7dddddd69f7b8bb7b5be1ee4edfdd69fd42dbd7ba6a7508adeeeeeeb4ab204577dac4dddd9d768fbbbb3bed1ea7ee9436a5eeeeeeb4fbebb883f6fb17a80ff5c9f4bd40772a1c68e005ba6369aa608623997e4fcb9cbcc008b29e2ea7cbd182df741194d020d3efb1319ad1f9810d2aae6b37d4b52304eda6a792fc89b0e41018c1824cc91c98c87a80e0a659078c18b040075321c3850f828a20a376cc93a209ad19abf10405329f10f94401055e8859210b42ac47c63959e208b29eeb3db58ab714a8fa40062da040b21b1ba440d6736a896206400072828592e0b401d6739d0a20345262591803101a54ecf80a90e060a7876a3a42168bd585ed6e5400f9a660628a217c5588019882092e885aa851b1f395010939983bf68642e18928d89b1e20a40a933228216ba0d84441831a40521548eaea784b8525d1af5de866751562ab86d49953ab983a22164c410c53a78754001c303103898a214ebd8a0e6a9f2656d0413d48b058ac13e4eaa914844f6a202dd9df3beb38e15cf193e9d711488b6907c54368f692664a05a5dd943e118f8a26a5ee7fc4d7aa966e80375b4aeaca1ad56834a9857386f63593af91c20dcc19da1b5187f0b9a734fe3f7a827460c214415de6cb7ded4331bcf145a4c44eb1226b37b45f394ef3384ebbf6692eafeb8afa5edfdd2ff76e6f9aa41d6b92a272e8cb318f42a3b7363abb5d3845ed5b294c8418723fa7b770cb70df88c249a36e4411d4e4f479ab4e75cd19acb0ac403fb81f427d1a7c5a7acb9f5c55fdf169552773325fa61ae68be6bde6e192d61d0e1462763ba00efffa1cf71e37d658e5f1993f69d48e1c5cfe2bb0baaa4da3bcb5d51d9e56555cb9f9b23dfdda83ee595e5471aee277f07d49abba1c10b7bcf5a3ce9aa4414dba7021f49fb9ac409952a0994d775daeb324534fd224d569f2884a6d9aa45e759aa4d53567d01fdb0fa1331f8e558f5415cc97aef372e29266a02669e867aea57f05d2e1cf0535497fc3d5058bb024ddd4eaaa40ad32b9b7825ae5ae990eacba7cd56e9828fa3d879b027f81e834c894be2769b94ea6ee2d57e24d509f9f0fc4e17573fa7d439f1b298cead06da42d201691b6209c656af2af268e7bcedf959369385f66bef7de7b695cbf5faf11739b2eba8cdfebdd741fcb8985cf2c326541a65404134531c9ba6d6a8398327deedd4b9a7d4ee77e7beeb79f4dded8e67393db30299c33e66fa055261d37db07b265229e4b9a6d114670806691dbbec7c9a0b3a733da6a929a3abc83ccdd77982669f204f38542d1ac6ff4d1e49c307d435de86b4f5b3af3a5fb7bc390f416a5d45bd4fbef8649fd108744e670777fa3c13105e0173112037e1d4b23144688e9c23d6dc2c3c84d2e81cc9dfc8388e7254800feccde03e4c60d83a38f650958d9ff8673e3f613ecbf63f919ec862293ac4f2b44012bc2ba91af5bbe70a7f57eafc7b2fbe65aa7f55ea07cc23aa9cbfd8a29ab7af77661586b2d31094beded6fb824bddaa6d5e2796395f8f7febd57eff4f3fdbae95fbcca8ba7ec4f9231fa50f421f0cb09c3dc7b676303bdbabddae84637fad3f454e5e9935cb4f8d2d36f2165a45a350a5ff4a1a77fa355e0d3ff21078feed12afbf47d10d12bfc1fd540194ac2f89587b1c2a3c92356de5f7d2f4b1d6168a3f5c6fbe0e87d68045f341ac3474179d298f2a591f42dc6d2bb185746212660e57e1f85f4ebde7063f917a3e955c6172f8e2a7f1a69c6e3112760fc0a3e06ff0b944f489f36d1ade25ea797f45572fa9206fd97f40d7983396afb166101a87ca8144c180b8be029a21900000441006315000030101007442291682c0e34518e0f14000d829a4e6e629d099428c6614619621032600000000000008090c4910040d5325db31821256c9bc0c212f6d734bc1426cb686ec3c398e0e67b070b87956890058686b9cbac8a33c2d6d703116d41e57f5f5da201f25eec1276888067266476edb21f5a870ccb6e3168c79b3a361317ed071453a31151c6b4eebc6bb8fa7f8148edaf6c9a76ed8f01c691845986c3653d21f95c780bee5b2c39f2f8fb58505e59b1f9e2e560e8ad23f1d17c17888b8375e193ebdcf7de3607fac9d47cb573344cb6c039840b4f93fbf71ae3dede87af40f410b732444f0fccc183014c8bc8cf15da442753082e7a0837d5a3b19311e13c019bbaccc5a823d27e5a154ff7b3065b05cabba6190135c7b1c6f5c1fd200a347528092dce211ac5a932db5fdbec3694b7c9745230e7239ebffded8d6bf7d24fda0808b6db6855a117c44aefa98da10f7327b87f622090298656079f0b777edf9a274b883ea8821a65344eb06ce5f1870f627756855686d3adbc6a5ecb6ece22580765597cd6d4a18b1a2c4a111785f766635124bd735b2936428eddbade1056da6e6cb4d7943d63f4cac94b5850ae36921174f27df2edf574d47af5936ab074ab689de91f640ef991edffd35975e298a9adfa103a2bcea98f48c136888a681db396115db70097832f106b4bf706f5fc3c85829950e20f107ec0a2e50536d1c34ace53895b46ee9d30013d8a33df4a784d5614f5353ef4de7e503c44bd9bf726553b332ba8b3223080fd9bda9ac5eb9d038f39a9cd3cf3a1811b9b10f67607f012c43eb1edc6c055515fb56ebf26086aaee62d0bd90f3530583a458b233798c276ad65f3e53cfd741e1579ad82ecaba11b5d9a7abe71ecfb95f45bc782451808015e458089b952bf6a07ccbd2c7b6abe8e7b33fa57287a730fa50d0f58549668c1782a35ae53ba8f2407db6372f41e771a9c46cfcfc1d4c8a77403b1718d35ada1c058918a335501f13ea03cf121e9762577e8a6bf9c8546a26b3bf765288f6c26f9cbde9a07e149c4c494aabf52f29007500c70fe72b4922a9e55edb853bd5cd73b4da6b3100f719810356f1cd7f0f43782b5317db005cb6f0bdf7d907b35f6fa4ddc2ca79596b180513a691595bae44f3c2099cec9bc05d83904506d526fcf5dcbb3ae1e8c21db09e16515df2808589eb6dadd282a0c76863956ac81cff89c078693be9e3cfab53425548339ade7c8e7d6df95a385e1059d690c62fa1d5ff54ac5d41de00feec181818bd49fc626929240c42bc685d835fc5846a03283302fda2cb9828bc16c51c436dc76e82e2d32ce7873bcfceadd34a476d58772e45315c428704131802f7988a7ba07ca6e4aacfd9e6f80c3528a0c619d9cd2ac15b840813960bc17c7a103f6af642542283c793cf340bc32dd37f402c6950e9bc468f2b9b818defb58c5cdc04ab4b58e455218ea9f82e1b2601029bc08c1d1b45ac07fb75c00f69fc3c4550d49140560651be187de9d97aa778414d8ee3ce1edf0462a95872ecd910a60834815ae4f742f89fd5669803343e33a918098ce086047c1e0d83ee201365c589fe8fa8c0716733afb32ae4baedde2736e635c205ba96a4dd4486e99b2050b8513b49c2415f70fc44fd209b94a94d4608ede00f2eafe88864d6cc37ee22e7343772d828762b979a40e12f79b7406a077a14cc11421d52f2c0130a1e9d88219d9e066455eae88dcb4d3c4e196e7781b39ae6ee1ad145b38ea828836bde6226a13c275169c5942ddbd4ff765d156743a36ed6b45f6ff287eae9f59fb29bd482d9631785ff49cd8629d23fb4f5f0f69a7225047ade4a5dc3a85dd91eb125ca581f87ec02e82729dee7d6c8f79c37cd8fde2300a29cc204a59f5e916d0354f9cd1c8cd133cb2ff3ba76c748b34afb8a0a3333810425f1c6df7f5779298d3d1ff7891563e4f2cf6a43574ac68fe454a36bd5603494422f7868c98e17b99e3f438bc45489258e5f65726f637cb5581cca644bc4508e324de0cf19140a431c209a2056e90e9f750c3b15682e54ac83c308e45922b6759746f094801da41c0fd136ee239b824c50caa248fe0e1828766014bdc084c1475090feeab881d7ba30232cf8ff8a9453c8b3304a3781e7194f424b3e0f5466965a1ed2b674cabc5a03843f8dd081a96b3c3183202802b01ed1de0fc48a1b061b81b6c132c2521a265608e2ede19b73cc2ca5dc2f61e37602d695ff844a0e229ba86b447397e883b2f1997b7464705227b0f2cda5ff957d965c68a5df69b65679aabb18db9ff9c89b4cc1a986336503aeedb41da4965377e1116ada8a4d0c3e7d6fbe9c0f507d7da80a8594efa5b67aa20d5454884c1b0a841ec71ccb5fc8d333c164438ffbef2a4326122e899405c20411c1658b1dfc0e69a31901067aca816fab1bdfadd4b4b68cd5bfb4d2c0e6eb52bd1a23c1da656270671a6488eecb095820ef19243d460355034cb1e2dae27ab875c71b6220817f092da4e6600da93bce3b14cf421ac0c6f1e51f7b500b52c51c14e1e3402d7bad5f918750928da3e4b965770d55f3729a55334772c299ceaa3d3c123482b921ee7d9b300d1dd418c98a7e9b88cad8fe73de51b6238edd9f2b29d9361e41fbf21187b7422174f8320536d66b4fd368f33617f2ad79b105283fc8b4f500f2cfe5a3b2cfd94e101d05df25829cd7379935cbb3b1ac55f0567500008f03e8cc792f955eaa709086fdc8e6ec0bf1ed43a49c07e617393d0dd00f5808ccf05ceb1cee0feb493b9bb79b0bad123f486fbd5645ea9b78e2dd8e462cafb56a09501e58e2edf67461da01cd37fa868918d5215c35d24f82a6b92abc289f740b99f4661ccb6ea1b2450fe23c6f25567b91d25c1030064605e398d54281a6c9591b51a47ed3c6901d8e3d82c7e13b4bfa9249ca39a7d764fe16b5a848404abc99422fa93f6807a86400afebf2f28a4baf49ab0b87da1429794e64c2867d81f51e5f51e07ec1fc4421161cfa71041dfa00a7e27e99144493dd9f474afd15844c362cdf5ff5e30f43665db438f1132c507311dd378c816c47138e482cd01182a8daa74c16ed2179b35239033b66cb6749fbcb719ba175f0ebfe52f1849754590d4ac034d119760625a806598a3e5dce4f42938e87c3775320c37802f8f3612ba708a92db8243fd8c1406b84ce92e7399a433d47af99f3e103b35e083ef35519e40a4268853a5838572a6506588dd0f90b17ef319c52eb7ec70087dcb8a1177166a1a4631518bb1d9539fe8e7c2f678efe1867a9d048ab2ba2f4c9554b6a0d835a3dd1c7cfb794148ae56da02e9b6af33e798d0fea86eb625782845e01ff665bb99c30d7c9d6a0b54939a1cbb5fbdb39126d6c3350f930026a08327342f7162081de721dc2211c380f77c11f5f50ef2861d812dd7e0f4905cde6bb4efc0fda77894cf06d2a7ecfa032ca419f24744b0bb336805d909a5169287868522670ce83b63a675c73893f18e8e496aa5fd1b7c62ccf7332849ca0d4f044cff75f1e6e12905c6a1f209c6ab2f994bd991a66b04e9f2eca83d27f8110d96c5a840d385baddbfb08b440f4bb059fc7d1a273c0844c1117cc57d494cb3716f63f9d8f5cc0ccc62c6e84c370b39e820d669292e8b7bfd306f42ca362ae7381b8b356635f48e66abe676a51b6bb467ce26fc6957074d66ed1d7d0f14e26ede2b28dc7ec05569ba6fb4bdcf1d79d3337c7d4798c8996a97496ee05122e5ccf2afca062bcf40b7a381eef7ea4842a79a5fd96a274f1c595e86eec6711e033e73556118334f96ec0cb7e54fcce5262f03dbebaac9a590cfd65ec23cce649cb9461a2394ac84af57b9b6a709f67074e86bf8e727e4fd45105684297ebc525c2dae7605b286f6cd7e3f5cfd6193d5a73929b1d7e029755175e885fc9e78113907de191ab74c68b176bb4c1f3e340ed71ea7933592f76d7863946669fa4b70872062a76fe44444de7fd9038a15ff90fa07853ac009bd1716574a48782c03ee6665867cf9c99b1b3f845ba5d6984eace854b9f04caf3308b52122d543402efecd3f8d78e26ccb70a8120ce9e5c6523925e168597c077dcae38976aa0e4dd3e0620a8b67154690e4f85a57d978540466211343cb8654b5d4d7029be8e85713bccbba4c5a7f240cbeffaedba2a4647dc526bf00371a246c6df273a65433c098accfdd5ee145e97a93bd3a3fc27347e0825efd5e41ad6924ebe2db3dd15688528d198a336313067f6c300f6550624fa74cd421780d2084322c436886a54fb2a423cd16472e46f3479370825624d0f84251874eeb454594cb9eb98f463ee97292a5828bd918d66c6b52fd4b3d7a545248c2070fe13aa10ac8c057fc6c1698ab5dbea554a75730b18dac1ca6e1155d76e3cedf5b9548eebee830d23abd7dba32d9c59091e06bf59e553fae8ec0abd415fc0a22a6b8b4c1f25d954b2278f4b61bc9fb2d803a84eb822a920159575162b849a6d02a8acdc4aef12bb0b64764db0a7ae5056e8d01c34a2d4e5661d31aca0d390483f51687e56fd137078ba2d4c43d7f957aff8497cc311dc4e642fcb5a8e7ee406c7355c6f3f26aa9fa5d6d5f0a8c2532f8be6a80dcf8e35c470c5d7b64ed9cbdbc86c58e1e5e8cc6b35f223dd5c45b8e2e5b2c81a499bf31d4caef52048032187f3af3f2280ff050927a27f854855b438c25ae510a7d03b630fd265470116b015f80ae31a62bfa4d1bb600d5a72b3831e49b32dee6c537793c871777850cc5be9a60cd3b85b6c8ad2c8a8f9c48755442a89c44dd83d162144b7685dc8515939df7511ea62d87495a3ac9f400932b060c2aa524f6c84a3099d70b39308ccb49e7b9e42cb64e1a2e40ab64528d4db5ea046bfeb80669e2d332fddff4e9c1f5afdd593e94003da45761bd8a8a42e8e5be590002e65b3a89e5df9b7320bdbe03738920970ad8e4853202ea2ea27a22765f425486e544a605ddb9431adbe0644c11e1991e525e419ef096d24691db52ce7acf405e83c490dc3c64da2a76bb6f6e2d59a0b30ee17a054c04815e328d4de67a5af8c292c17a71c39e6912b90bae8395ceb1a97456f494959863d94e109297d1f1ee3497b9a599d0937df98659a0a78a2c960137f5c4eeb769e26a17a31437241c51da19bdb8f235f314299136b664ba416896dea1a913f33374fb4de61f181056ea3862b35c3142b9641afb3d75c17919fde4e66ae9441a6cc94bea55dc9bf60a7e917f7b252a2d3195d8757926be2b4c6c715c9d6080552f763812537e53914ccbf252ef65c1cebfafdc6b7ab1a6f0eb66ed05fba4992c8c7b039891984acb628eeca65c9a9856a67740d22466a92f50795ade3d6ac462e7ea066bffb2c4e279dccf7d8ba017a523f7e618dcea3a4f5f4a0d56588d8036910067213dbe194ae45d51d2d4082606ad182455e8fb0bbd892044ad9aa0c0507a193676d9182c01b42592d7d8a6a337c1a4c45863f585a69c16a2cfd0bfc9b1441916cba920f811e477cb5c2bedfced0834b9658b0d4ccee33acdf6ab0392c28395df20f711f7f3343725d6f16005fc3ff0e4b6e948f69974db0136339f575d649405d583a0e31e4789e9d13b814eefe837af6943ff1f5d2f9d1aa398455b2d28ce6e3501786fac5fcdf47e387ac62514df53b7f7d5b0ad853491b7eb287cac1d8aac80046df432af71988eee24ca0cbd70db8e7e64a765211551b29adfa7c845a752078768344ed777a430f5d51df11038e3f3a77d6b9b8f283e11b3f4761126a5b1b230bcb912d35b234426651f98735cbbd856f481e91ad6642ddd75a13dc0838c2f8e95ca114793d50f31ae3900be64ecea067ec88afc82f06a7589e5d2b7e99757e740705d06731f35cb86d1930a3b8ad689174e91233a64ed9371dbd814aa33f1d3ecd1224aeacf438b8d63ba795139ac1ca005063f919913c448922689437803963c78c8e02f4cca0ab9d3c28348af455bd5ef453c140699b5d1c59aa32efa6d283d10831cdcc54ce222674511610c5fbea724bb1d7cf8cf36120feae9c46fb42a4e0a7f84013ad3a676802a0120603f74b7e18c6e17c18da068054c8131a8c55b78cc169c591a5744b261b04c0a37168e91bad05003a5dec9a230dd8c3f036c5c5e91bf76e396ecfc2742a9371b159d947b7cf185cd6464d9dbb7dcc3d159ca4ae89e32d07eb4653a3f3f809fe1869f5712b52808510cd062fc52a75ba908cbbbb273c4fba4454c8947de657777ddb08d0bed4702eaf64940984ba63c4610e0684fe213e7008dc9a0d122fc70975fc4d18e12b4c8d567b235176169ed5423affce25912a8f654d72f7d6a11e8ca5c4ab30abf614088e021b964f30dacab97e9dac0f740bd10476166d509d4b273189c20d5f0011213f2ee8055350a7a0bdd73718449729afe1aabaa35edf4f948a215a7b7df743d1c71dbd34af7432e9d1179a22fe33a93032f0f673e6fbebace5d3262e335748e0905568c19b8bbaab62f20d61288780995278cf08920abba3e0b8f704f57e24908d86c7a1f05aa21f4a9acaa85f4d9454ac5d3ef58d80007a1291ae1e2b5c95712edab46cfcd682c0618fd900a264d90bf4006640800fef7f693e0e21655e96a235a0464957e1f2158029e3f242332f205c437ba002ecb9233b611689d4b1572728bc9b2a54a7ae524021b964ed9054bc0676c47b260abecbd1a495cfcee554bc034b13eef69576be2d98ac01f5467e02fa4d45293d0f14a09f84add47740630a6b05e8184c3e90ffad07cdc7af357ffabbe5441c03a8e8258613fec18a85810d0408fcc905e01343e87ff0cc282c67c857135f72179280bac2f1a634bc420e0436af101641dd7e4d4dce63a06cd4e717c19faebf72110300c763e7d9c5ecdbceffb84059de8b84925adb57cb937488817a19a7504d3bb82804d4e08640a1e5162b01d8a7da90c4d1dd18c6de83167d15bacfdf6da17f8568afd3b94105966b5d36b8263ea673640dc9e67d261546a087d446ce3fa019b8bdaca8bbfda71f27f5c8d6725d929594ee29baf8942f137ba517d416474b352ff76ddd7e7957a03a428642df331142b33accf3a4619c66089c806d848ad1389459b58fed954232cbabda45221f906f8105e4cf420e11de26fcee9c597615b3fecf3c641a1a48e97c95312387f8b4e0dd0156a51b3443e72755a1297257c7721d5a96927b297e11c96a5309eeaf273440dcc8b4b969cf56be7d6914181b107bdaa10f81b6317a79064c7555544c5044696751342d08c1b6424d210516041466dd6e3af47e4173be60b17e85c27e31da9f7328016f7c63e25c0735d0b08cc872926ade70ff02b5092709a3d08d5286d45dfabb0951902a20e400103edc1c18f51045f5a3e370583bcb120bc2ac27b99ea0f923ad39bfb78d6d2dce3f3c50c21cd1d8c053a137e870ef1ae142f9f68bd567b9eb0c162e1e00c50518351a3629005159ad466738955e2339a528c676b0b71b93f6c7ab40410e7e0c7b4442c507e7c7e9a1e3517ea3aade02dfb0ed943a8e5227e4f9ce252fb373c0f9e80f986112aa9c008a09079e1a45e506ad953a2069392543437e12352b51af941bb6daf5b26e10c4c50a9beab62b0bc5dc0ade6b944f7af61b73f4fa76b4308d1bb6e8699d20d510295f1d06d23af4113d588f62207d7bd150a754edcfbe4c0e28c540e59084e2daef7c307b01dc9549272df3b4605e183b2cb4f54331162156eaeb5ac2a2960a535454d153fa49af94194c921f140732282c0cfcfbb038b85096a19763a3f52b606ffc44bcc04f0e1084a627f4013283802f9875896c33400e81fa9950d001914e959e0f1307a216e33144894c014ee640e995ddaeeb195cb83cd3864be569ef3cede0869b06b00a7ee26ab36e90c708ba48f9ece423f9a66163f8216da57faab78fe65c23f5bcd851f1865438598ef927160009ed0558e054d30c8b185b3c8ac273a01b9a64ffe853e5531ed299fa0905788db763fe2c8ed9868d78623afd1b6600ee5d544d2be4aeb99b07f906bada903568bb214b3385f26387fd953c4e227faee6aa35fa4e8a4b610c8a2ec53fb368b9e4918db8892bb2f6e3a4a7fb06a4291cd58a26fb6d91128ce2fb8390aade555c83bb2c29a03d041e33aa65e291567fb2717275ca8213f0f7c56e2087e24ac6740069bb2e02f37882648ebc0833a6948156bb511776c67c02b5621b1df7de7c7ed966e7ade592c2b1c3616718c406357fe4a91d714712ab460f537b722f334af49524712f2d78c11dd7ffbb1cba75c0ad0e3935ea9bf42364c4118075b0268faaf5b61254514c0a6e693e89934c892b82be6b4ce3ae771a802d688fc16b1429155df7f95a33550c3a8cedfd3e6be56112b5087b66a7465982aac6c53078b3decad970633f7cfe5355ad78453f3b791d88d5116ba0b5ca7480eec68aba7642eb3466772adf514607bba38ea4fdaa36b107982602e476d769aef3757d85a455ae88ce12f105e70394dcbae6d8c6b6f79116f1433dc1fc0a37c9ed1ae62d1903ef6fabdd69c10ee354970f35ce3e9acdc2bd389d73256ffd773c55845ed82d9570da64aea83a53217bbfeb5b43458d0826e26cde0797986214c0139dd076be78377c81b7266f398b455f7560ce29fb6aca3bcb801e86ced0c24ebe47ecf41f91b404f37364064c112975f5e33ae505bec1bb2be6421a7ae2cfbd3ad5f4d28b89a7f925a8646f8edf56a0182a38f16394a4920bf426031ce30f5d7e3aeef3a13e2c411ae7cf089d36c53ed7a57cacea805d1941ca8961c0b3965bba6dbd3e71bdc1f7f91e651a9f0c8acb5f66fb8106b80daf43dc10230449e9b56eb7d4a00b838d5edf151b396ce4fdb8f9b4c3f728f379a05ea623e9877119f29c91fd13722086b1db7c7e9d63a6ca800e0590854e51b7d2319fc2695f5232d65a0200091b47a811255001c2dda1917c8a73f0f7a29772f74963b90e39a8e60b8d4018a79604c37f54ce2162b6553b2dbdc35898f288c22c25fa1730748cd9fd731d1672a683e2e816675df9dc1041c4234bc42d9fc0445f38ed6c0a8757b6bf64b32008f8c045b071d564c85fd3c8415a61348f9750fba14b829d5177e7c0e29cdfa6bd5d589fc1d14eaa526d095e12500ee30313541bab14d8ad2a1f24149eb7537883b9c37c521aaa86bae83f8e192436224b9026a0e2f6a89aa4640818766cbf8c5cb717f1ec97b056da6aa016df844a4e26be9ab62ec7607fd73c5ec994c3e1e61a40d0055924043612a950009a37febe918336e3879e90b59d703263d1eff31cb41be528afd1b523f520c49081d1fc796977714a10cdf317ff6c36a2930566b5d0ae7de20cf638d96070622e302553b05a617ef704a5444b763984f552fca11441d6a3c6bc0a39588577a6aa4fc8029a773576ff27ee990594ccd2bdd6f9d31a81f3a8e0366ac01d111f68a50313b9c9cb85193d2f95212d1481f8e41e3acc25883b7f1705c70336ab64d0631fbb85485040c200cfc8c410128c743c198a375e950768215fb3c6e99145a7027329241a10f02f72514bf4942ffb6353356c08abcbb7e156b10940e85d6265c1dd214897c06682be46647639e1861026c9acad9b63b5035d840b1023cb934e1a7377431b86959b8087ab57b08e676f8bd095e8baea2dbd0a7b520dab63db279043da3b8d5c184ef0635f554eded86dbb494f98c4f7b5b915b124e26df2d0c64dedc5456d4d35c4012cc887a0a8ecb84a8cb591d09c6407a9e90aae76db6d05850c4a7505bac74ad53a830aca5ba1f2c32d41cc5ddaae041bee068c565d766aaab05a8f6a2981381e3005d8e790e47e43baa07c7b720c8b8aae25be25a6838bf58540914d9424e49525a5ae4c198e3b149cdf4862c88c210b774f37466d63dcefdb841010c71e8823428e86015d6d346b1455fc50c51df200932283e609616dcd93d615704697b04fab3408f8360c2efcc9e0dfeeb04c2e543d40a341029c0f3e8218736bc0aa079a578e2f409990cb459824b2da708a312bedafcae64d5e202b4b2fb4f0a00b326e6326651e19207dc530e1e2d09be55e463f013ea18509ae2c014121e68d4cb194d00f86668df8ceb068f93b15acf7dddc30ce95701949508ee8d0e55c2fb49fcd1475ffe074b3cf5abdfa5b7aee7ad381d42ed45d632d98ac3ffad491285e9ae2608ecf46cf5fcd0443938ffc1a5a1c4d49f83ac95bd138ad5cdb4eaea7037345557ae1c0d255528059c10a8fa067d1049b287fcce22ebd7015f7f445ba974091fdd1da9e6f691668970467f444187e710cccc5f77f224094b48dac848b49f65506a079e98da82580606d366ce3555a5e6b90293b675271914854e4dacfb2ea2de58fea5730e216ed500f18c17f3e642f2df8e88c8ea1aad576d081c58b866beb7c7a5872f635a4ce156558a98eb8512c75ebd3612b8e377fc9dafba22c6e5c2a88035fad10eb60e8872a283c1f80c200126b01f995f4618b56b924c9139e01c6a7753c37bec63b948615444429980e505c422b4f658104e03a683eb80055898805007d79a608f7b2c15935d19b008d663289d46038b541e0396297df638afd580528dbf85e3dfda15f10c70705fb0b3f2694184109e2d5d25c9c767e247c22e5d2513a45ec02cdd21b2ebca18209aa648cdbd02b20a113f5a295f772eb30a1902245fff79142166352231994521496fd8ab1ec6a8c00421c3223290a0c562b5549a04b18e0040e472de7ecfb792ac3a1d813e9db44d39fccf4ec464425c056c99fe0dfe9d625ec81e3291039cf5323fb11b155825c6fdf561f28ebde728012fbb01ca44e6dcc81b38d66bb4342aa543ca8356eccb2b40d8461d04a600e27be1530575704a8b4e0c02635c96d13c51b10026212690d1814aa065a883d7e534265bc27ffeac1658c2634ab5f4a854ce36247158dcb8438ef84bcc70a262c77f0a3d854b1085181e098d693ee07c2802ea2f10ecdbed6ec4204a071b12c3fb10cd62d28537b5349654b04a2de67cd446527eef8456881c6f02d69f8ea3f0b180ef91e07fe1d304fc6f7e91eac1ae76e92311abe834311e596fbd08f830185aa2ba001e03431a95680531e67e880bd4d3af5144df9ded5f7a4732d23b0d24480138c47ec913d0b21bc35fda48ae107f0b1e21fb1bf02c0c788840dc64cbb2ba8850324c954bb59c2520ad25d74047d5f45772c16efec36a569c489bb058c0ac54089822b8e653eb0bedee244a5d4333d97b0f401b23d51ac03f950cdc0255994c2c6728b6402c0f03944859c5d364714333d4fb406e93dce3dee7b40feadcc10636f2add1a64c5df50c56b1d3446957d60cb2de27d9e1b8c9f921fe1f4a44402dadf969e6f2d03e434725424a10d596f2f0ab21d589d624e64be67ad6213e08aa9f23b4970920c70358ce31f3367f92a0f0dbac4534615308fb338cc977e8428d0ff7164a4b05298d6710edb4a97c4eba56d031fcdc6a0b2e91e82ede9de3ca048182795501610cf32e973ab524781a15531b04abc0a01c099a8f258bac6035857e4b384d9fdc899f6e2addb483b3fa7960d39a41ba7f0a0a809cacfcb6e88f42351cef124187bd9a279ce6633d1f33c492e5790357bdca2f4ad624aea9661add86fbb037437e9dc61bc76190c6df23f4a6e834d254824dff961eaf962085219eb5babcbeb77522a55a9af84a5f8d7ceaa05ea58b3f9f59bed095b67de356d6cb6d1fea54daf6d98109514688ba6780de41d7ed6fd04880d3d04f357e5a8d28450975c8f987d86dcbcee8c8077323ef2519d63a421461b3f6d75f5175d3438314e35951e3f6e9f1d8053ca9faf458df846480a761e2fb93bc1fe86fd1635e09954e3721e913ae1886d8d94f97f46508de682acf28e6d864040a2d6311de087c9511bdb4e3706a3eec9a909b6432495593da19019721fd98a22a2f725391e9749ca491d1b64ee46bd4dee46bdd775d602d95d490de2a809c9bce27f1f3feb754bcce8546fd08f78027e646557ef01c41ea1dee1af1392c1a0361e716357ce0cb7b696be6dd79173b60ab124f1e61386630bcd30ec2553552689dfeb12ba9d411867f5c7c4c6a445183c3f6526604ac56afb061a881b14b4a7b7dd952a1c905f175410c149bc0ab9671117a4c237b2a344ec77905823138778bdfa25eafc097e7ea618f0b6d3cd2629a020a0de4b59a604802ea744c07bfaaf1288f083b50debd4f62d66092cf88baf8677c0ada54e81c6de19dd578f0de37dfc8dfa7d1f3c851f978b5f4b42bd8c990619fbeba4e6f3498ac946b1732c72255946000dce8742730b3728837066602a7242cfab7dfd11472f369b3df46dd3d13ad19987615102180a822887f97e32912f299e9c11bbc8791fb8c4b6acacb073a2b159826589bd3dc5e8cf0eb53d844ba78130a5ece095b1f0fd94c3654dd501dbb6941280a010d776320913aa789e89db66d721ad3416239faefc11e872360c3bad5d16c3d9491fe7924dadb6a1c6f44e66f4f836ba76f897debbfd48381eb20502eb19e48fad6abacdd95747a513e8fd3cd6274c2f20cc05d2594d0396397c8c16fbae7a7123e4be67ed6aa24abeba1e1e43edafd0753f0080a4a11c469bf712f575faaddc07929d04c399f38528be6a3f4306df394fce2b6531b7f8f1c0359f7b968884fabd24d4dac5b9ee48edf24d07e4a1e921797c31952bed34065810512b3b9485ad3955af15f1e67f595d0a08525003cbf5fdf7e5d5c527137a39d92d8db140800b93aa1b20f07b4c5aa3d3df61890b1cb7e6dec166b0df6bdb0a63d5f6682a5390bc9c5af895e869b493f865eaa18e829058feacb97ac05781e439af54c41ba506c4433d6341fb24754f1a224762578c027d4997e7240b08081a5be4107e0e7592cd0500d636f6255a7b40fcc88981091a8520203443f32f46826b29e11e3246c7a82205c62235af7867b91605ed7865b2aa16d62bf38cb05a689e39d01129cb3fb92f23fcdff7283b68d80581c7b3142d0c1d56b2b29ad5093d00a5cf746e016a5be797dbfd8a01527c82f7906287fec8d91f578146063c94eb637ead774f2c8e79e024eddc5f7a5ae8c80739ac7390dd98bbcc7936290087b71a1385d13cadd2ee622993e9776b8db6e5521a7ab892b632e141359cd868b1071db241fc2dcd49259e0ef27e481bf9be00bebfde8c97353077bfa69d12e3e435cd143194adb8e1896b7edd69a68ccfba43f1e38d96b406efe85606549dbb5cb694146e9f52f1bca0e3ca3fe19d751f4baaa08292c0c76c0a17b21e6fdd04a0db12d5ae5a75478775ee4aac8cf72ed9539700d50ee1986eae2409cd7c3da12cfbf57920934b752542d8ce6f5831d9d1707d12a2d427192ace7ce0b37b2462c69b9d5633d42e3c2d54ae7b5da689b14f8e84d01b5d588b370f8a4e04f115ee51efaa69227549777ce54ed075bc3d51f139182b469764b9185f5c8883e11dda65964d01e54d8631bb3d4944abb4b16b212ba2f6455e500495dc5113d28148b46be3f5e2833992fc0bb416e441e9d28607b72d89d5a526ad2f91521384106bb1e43ff3a4a71935c139d46e94e71d7ee572c3a0508a7aeadd323d876ccd9384c1e8e102d7ca6d97da5e8e57c509094a52b3c234839047cc76c06b20861e4aefe4450a5d87311583b76bf849634c651c0b158c3cee05f6c641aaea5410d924f4bee0e3d0aff83b2e55420e2abb5ce26460f0cb261fa0530320c9cf7004ca212f4ee04486a74e493ad33a7d8cf1dff278a82c243da38a123241865fab718f4d5cdf43ec07762481d6da09c0f0c6c69464849bc2c826f190cdae0a177e80fc764312aaed274e636c1b23b67227fc6e9536a31a0f634fe196fff6029ef89dd4694886bd0965b6878b37910aedb185792ec5ce28468e95807d11c0c39adb98f87297403cf8664db0cbd55e3e00690b5a444603bd01b55a04f2149556b481f58d116f068e50da138c18f8006373b965eba8260583c22f2baa953eb86bcfc7b2d6119d91a9e0d86b94795f01d17b8c419fa50708601c0ce651cc75e1d5ea9c6340d2395f643baea0ec81f173fccebcf57c187a14ffe8774de590962334b3a9f149e320a886843c650e4ef7ca0bfcf9aa1f9acdc989acd73bbab1fe0607953b42605d9dd3b1a4ca79d10cfcda726600f7531db4442c34d2968d7003d6d98f4327deb0e4f9acd448bd83e97c8038ba7d81aad7ec13756fab171f83778ad3a38965a87c12d80d53f36f7c5e6441e52da15b79a9d397d8eb1a8a95c0565b25bdc6aa6526ca2a168bfcd34432dd20091ea9839e1419b11a1b5245e85d08af241c7e1f6d8226d79470570fc436f7d116e3b5882f7263e83d12d7bb69faef5bbd68319368bce8357302a8630a803d1fe89c4013c5125e15155f7373aefd3a788e87e1cadbaf034d7e187afa8decd62635d00928bc783c6076b309871c0bcc28896b42af533d8993034f30383abe7b8d673b2a81a02a4422975662cbcd26e7aa06a495a43ad254582bd802401259a95bc24d7684f1df7de8ffa7cb443e04e1f720d12cf384597fd2ac759376d78a2ac3bc7554de70a1ce349e620bee7a240488a720ca2719d0cd52db4888fbb38d0997e60bf11fbbcbd6976a6d796bd63b38ecfb868f3f6feeb437b7bdd8b71c9d1031a1929680ee299bccabcadc4a55a883490df389a8a0d6d6762649bcab0ba7d2d049d821dae6331a3b28d5187c5afed7c21ce99d5b01f4a2153dfaeb68823f0c147c7efb7e9dc4caa1726b5e5dfe4cacb3f9f567e64a2684f2cd2a309cfa346d96cf2d0fa0a12321217ef181313e23a6b4637980c3c56c96c865da1af3d148789e18906f30a6fd233702d4381c8fbdc312cb1b411b2d5b7f71891a943608620090a22833f232f9f5deda8710cbdf2efeaada4d113060b2a18ad26c0e7ad42ef2de048c739c5580069bd35dbc8c7234d48a4b7b66852ef3bfe9a938ea4431626219ac89f5ee0093786c957e4eed0c8599e482f297abe329156d2482fd54eeba85b7b32ec1d616ad236511056ade5079bd9edaa3585a737882ce4d839370209a01e682aacce33fc8130bb3d384c496b5342b06b86ccc4d0ce3a9d4ed13be31d1ca88b9e05e8db6e04068cbcf4328f59ed1e181865e1199d1c749ad144ef8f62135fdd994202e7dd434c464d0d3169d4ffb8a3b58d2850a79042144f39b9bf8bd8f3c3202dc43d8fd0cb9b6a1c0352bab00eb8bbf4247f63e79a382812a60276a8ec378bfd0feda9b0aa55142cfaed07cfcec1841eda73c058039c879848eb5fbeb953314e320bd1efb4e5db79833826cfc37be4942af1ba9066083838fd6403035acabb308b075bf43acd12eb0b8968d15516450e3cef4e4a276ceced81e2fff0187052431d3accfe062ec45e4676a935d7bb16b2343e92223490a68e208ac04f292e413860dbed642f4f595f93275c7199066689f37cbdea570150b6f93a44c981d2d92b918592cdb762a1bbc51f039a21bd527bb8d3dcf3d2d4637d484eeea26e491f254ce82b82f429694fdee245306df5f3399bbc2992c9c0254fd87fb754e4848590476dc70cedb32dae66fff3dd20ca57048070c8dbc14a22360bc5442d3e14cf430adaabdf0aedf75fb50ec2f02db5bb2f9d4ef3188412f4ee97150d44cc37c18f0bd5f57ee8a376cb8e768612d829a03c473dad6a9651cae9fb4bea2c4b2baf377d88d2b1496c24c25227b48ae983d3d39f6614f6e5f165445f9c892b8f602307a4044a61fac9d144c736c0ffab5a40be92ab4832efad4713b9fef024ff174e23e6cf886638652e4c09ee1d099a39f16b50af6cc8aa80c56edeb675d50563016b5d5a6c3f0d790acb48a839958216fb980c4048f4b96648f370f3ccc4bb2237d9f602c80beee95908b0715db92ad2b85c1989704bd4b41bcfe6bbb208990c73fc039c74092c3197f1434e83a92f54af67a23cb3f4d82b9aed6b33cc92f9a3a7054d42068026cfd790e3f7a0b924de1de639daa7284c6731f51e048ff65655d37532647617159a31d2939052f11d04c8ae6a5b968e655fce5178801cffe9b26ca5dc63264c3ed5cb6dd39a12ac0d88163f662f4b145c879bb90f07f787f3777ad03c37731948bcfa5bc3778884d417ba7b8b57b90940defc0b0d76d6a775807d7a3eab14c53dced5a35e0b0ec19076cf4c1a7fbe6738dc905498a54a57c9fca57ce3bea6a709d81225b2e052e9a962ee6f18ad96de70231f6bd6f0f804d9ca5b9dc7323bfcfb5752769362db2ed9747ec823a329c08baa2d3536a996b2384986ef9da0a1690bd8ca782c8a59aaa48e3b3c02985b50b780cf6208484cea56e3f29a7152427159c802046ab15ad675ae6e2b44bb9d20f5eef1013e3355363dd3a1cfa9284dd2d5990b8806c32fafe2ae2dda9c93ea8f9e85a80900d2a1e916f2ccf053f62a197a7752f4fc067b2c556c8596874147d40f3333fcbc6332c11e0c06d8eb069c2114104243be40643b934ae34aa7b1c30deeb718ae21bf254e9d2cd2c2f8bab79a5cbb0c0454de09ea03029e6b369b27290d5212301db28179f08b73a6ad5c7b3cc8494671f2bd02c2a666cf9b32ec34429c9a378d960e9188bcff999c1070c9ef8332c64e2d17f4b9b0c09dde168c4c1cfd1f73bc6e6b16c74a1a9e0ee340e2ebc2adc551e7e914c2f33c816d2100aa17cf46647f688feedb53ef5043cc1c6f8f481083d404099908e763a33205a358fa7f30bf8b105ffbf6ff249bcf1325a6fcc9846ce0e92eccddefbfc2f0db402d3db28daa4117b09cc220b429bc993ad5ace8a90b5835d9f1863785379844299165934e08486d29b78f56c3d30e0605f26bcb12a071b03df86891cee2025a09a66248d9ff2bc9e8ca8e0c20259fff63fe4aea4e2b164a696b1f1573e692d499346986e94329cffa8ed0c8d05a5f57e1de2f4e6a30c9262a4ac52fd8b97c8e9df6d3d0dc96d4418cf53a0114987409a870399e968081151510d6c4142abfbaacd1a3281d377109d3be2ec848afd7324cd0c139f6da990be2d9cd424e9ff976968b30b21eb67238aa552becb1bfe239a7b6f9a3756f3f6ce12b9f8849945488bad577bea9caa83ab531729c7d72477d799bf2b70ab7945fd75a5358c18a2a6bb80d94e38a77005e581704133f621e424fb18b9ac042042acb8da9f31d9e088088601208b715328fba07ac7241db8c2293c3a1779b3fcd5017e4a61ba374e5d72887e2a16c0f809ea013688e02a7e17af8d1cec34550f74b931d9242c5c6b68b595f1ce457abb13f66f9e2f3909b21cb230b6acd344812b3574afb5f92fa264ad5edafe37c8663f0cb6cc2e8acf4f3bc27297f14b71dc03531a35975f4e15c84c12ae9fa894c5141e97825145b2efb5fa4103c463d65034737dba107fb124bc0401ec658c762f968e6acf88a1cc2026be847cb6df4a33044758712ab71d91b87f7dc955985616e57bac91bbbf309cbd5549b429f448a6dc87dde1968c261f40e210634c134f578f344b43c3524eb9001104c2cbd8a668986ef6b3ac98334a85b13ea13135cddd01ed144811ef4516912e1e9028a8dd8843f84842be9220180cb0429b03a421aeb6228da9dc9d8d0b35d8cfd46fde9f0b1f2903684e01eddecad2843cedc2c91dafc0a69e9f573e288771407d28bf04b063ef31082fc38da84bb17c3bc6dce9400a9828fdfd937d31a4c02f612f1887463c0ff51fc53ac4bc4f75f946829bc865bf24173bc21565442fc521e22cf20904e1ea0e6423d031b049348adf609cdd761da2039a12290dfaa47d45fe2c763c96c80ddea415dea1666ef64ac2e2e8c95ddaa22fea770d36efe727cad17e61d921c960977c14cdf19262c6862dfcf09a49a766c7511d49131fae1e064fa81cd0ab2e25e3a3eaa46b260d4cb8e46ad71161879491da146a542e9b52fdfb1252c87242918e90a3671882a8fd42e6d70dfb293fd490cb8845f2821a791c438b83054f5c94dba76d46b61f65b3afa6aa5698ed4b45ff5fc10d69ab5feeab16f3e857c390cfb4351f401ac4ae4fa06a73ab3575db4a877f5cbe6c17d6990bc2569c2337665397edf2bb34e16894cf233384efb7f599220d5fe247712d013abdebb79a63baee42aa6d8eb8161b0cfb8dbade108c29dca5ff1decd7b12eb0452b6d638c59f2b10dccc95c81da4ef61fe89417a259e13e4327da78d7a255befcca91e135f5d8e3b529165cecb4635763a0c77d7a5fc7242f05d7a4f18337f756e40d1f5c725a15bd1e85f2a8e80d55403cea1d121eb41a9805fba441e789a55d26fa5989f6e29c87db7f84f88144cb014c0a7d10979c685f5d31c25ab2c45ff74a343aebd857d42aa40a8a5803c8d9bc81e1789dfc73376a6dead9dfc5c0a46e18ba1ff649a75ceb50b3ee7cd5134a2ad9f79a65c786df9e7181b52361ff3077eba7fb68b8514fa9aa98fe59924e2d0a7b3e78356915e0f5a790eff0a5acf60acb42975caafe24e28f99d0e0a967b37c493165af1305e09b3c4888fc4025f8ed10e69efbc8ebac6c21713927377ca2a4a1d1d698e4c1d64a421a8799c736209a1d1dd46bcd8a994e02781df00941b7392d91168266d1e073e430881f24e74b3142e3ad56cab59f5e4d359193f79b09b23d370214d559edcee9a00440c5d5ef18c7148f10cd0a845b864a9bad4308eda6640f99061ce856ba9c6fe55906f0ad5dc8789337c531c7b2f2d2e9349f2453df7392d98f06b2072145568fafb9e8ebc1f19ffe69ac653857370036ac7fb11f373263b64957c8e9e8552ca9d966455990d75cef59ed60e33a3b3ff55ba2546a73b8916a614f9e531f8695239bc5534fb1b5247de79a8579795f1ece42fb826abf7ec54ddc81bf7ec2c2a7c8ccacef723e3df3c7aeba1b016eb6cb6c87353adfb8a2afc8e8acf9035ceec162a1e6b76dc2c224cab1b293476e45cda02e8f1f2d35f1f6fb752ab06f1e34d786a536c2906711fcfd29de7f1449ef05c96d2a73d8d2dbbdd2deee5d0ccee2e7b2f2fd1ab95834b98669eaf039145a0d6174fe85a5c245694e9b4877b904656c6aafb8b0bca9e8bd43962dabc885680e7dfda6eef6b8cc25e2cdb17d060838645303e057dc573f15a41eefa9d0539c2ae27686c22bb986c42800fe617313f678be82981e8e0a11294503ceffe5db8dee5c17624241601ea9700c5858287ded5be24f05ae5412ce027ab24e22b5e888543849f34e42a09cce0746160159186ba5da51afdd706e625766834a628c2d2addccce983076f3f64b17fb3467f0eef18a1d58cf29e792016f71e383fcb2df61936ece69a6afcb7b3d8ecc84bfd214d79000a9725fce0ba5c6679caa3d021d7ffb50d80847dc31daf91341e1878b78caa56e207f4928413c481f38db8a1f44beda91fa632d3e7ae62fc2b36e878c5cb618b0d14af23055dba6d0a60fa40b6b1aed7bdfa6fe16d36c856b900c4350ba4c94b94a764c92b894a7df093242dd54faf731f7667c5bdda4be4005a34aa4b62cc443599d5d014e3c6fd9111a96c621acfc8ad6e308ea766d11f1773039b1dc46e981bc790bb9bb9952eebfac58a5448b6788208a86db7d3106ab2efa24735fc5cf84b6c13d31b4687dbe4da89efe6632fea2e1a341bfce6deab7f1725115f82013ae2753770fd26a8c9768b95abb889f1b2600650e12634da4d524edc699e21c39690b61452eac0bdd8508bc08c171611354ac65d17ec2fc5b6e494c7c94034e0815c4e8141ad9a61c38b06ce54d3f02d3126450d22fd5e332b1777bc276f8dda7999f6658f04f98f9939fbf343732644317fcd990842372e04d952d50d9f7cd278034f336be9398fe6f522df18e3a1a14c9414dafb01ec47c4575bfbe5deba37492c58df0dc815d88aad37353a9ee0d083e9097947d5b5cb7f2301b2674a728e1e5f10a5694ad6fac04e07fecc38e9d4a49e80df558c2ac46e91a92b8170474a2e672bb38978f72328b1c8534b552ec70a698b7744cdcab546e3c7a22dfd44f704e3512801ac4aa6e8ea2a69e53d8ce7a5e7b0cc984cbbd72d45328317e086ea09f8318a73036f58439e0ae00d930d4a04296cfe93966240e2c071b6d6e59483ed1d3429275023d5c44ed411f37a187cbefdc3c796c1f00e93d11b49fef424a89c188d057c0da2225c22c1e37fb5f41412f95b86289d4df54f28f06c1092126236821dd2817584345309a90b663832621bcd10ca1d958c266b11b12c167c041ff00efdc7280b24b809618cdc2c85fa25e3d70453d2e8703a0156cfc9fcc6d16a42b0958367c902c9d2fa3574b02b1db7bc62fac81b0d4bae744105aa6a6d651e1b91e07fdc158c3cdb94bc770187d9e5c00f9afb16246d97c02d0b15c065492557794cb53fde80ce93f3019869bab06e9eb7a48f652a90527426b71c96089bb1da2b313a308dd07615a6d285a7365b8031155b3a47aee47d417fa26676d3a8c6131f5db3ff72319af179d9dcfea3dd0be35c552dccb35e3eba6785d357f7f063a4f084ffa62ef7f093805ab2974bf74723d4df3b7c570af3e59dda1ba2bdec118398682ce292c251636dc3cd0187dfe671bbad8bfc4dd7bbbdf4f1eb5bea951306296efc03e74b673905a200462f6a555874f720136a3b15bea913ed439b80cc3fcfec2c64a8f432943f395a17e1b52b49ff1573052f12b8eeba42d7a8efc08940cf46929110123caacea6731149ef05d5a1b88e7a67bef0839b8fd260c284292fd6c197639c8befa6741d5e99bd50efcca53e32527d191f8c9e21a1291c5d3c244c60dad9f064b64175214374ca6baee9dc92967bfbfb87d89fd4b7930ed3063ed31ff74b1fc4cb05a82b382aa7ce2464aa6ee0a1a1023986ec4f8ee24d963b9cf0f830296d1dd34c33123c123b81dcdc88f077406203f09101d5a88e2e38134c708d2c7032dd41bb2c2b38e5ab28745d49967bfffd4f5c4faa2b91096b073eea77f7a30fe4d545c4299daa53e5a4302b67e0b071809d86f04f46e135207a4f49657effc6436547165b0e3827b0fb817df29572b1787d446bc5d4e8840fcc943430d40d4d65c01f3acbaacdf13a517a2f49e5bfffce4f1d4f16dd1b0ece06858170139fea8ffba60feaf502c4941cb55325328a919c628d748ddc0437afaf27811dac5d15a970f76bc00f8574e9cb147ce4d0a61fc36e9074a45f831f0e0769aaef48be184f4269578cbe384244e4f556e3644ebaa6ce5435c0dd72e1c39c7ac5ab3d17d132df78fb65c913bdd563e2bac3f7f7ebbee18b7475917865466d2a92f41fcd10306a50b0ef90f989286c15e4ee0b49b9efbef7b7ebc865a5824102c9cfc18986e022c4465f1fb958cabeaa86431b5f673009a8e732d6b45393be692063d8d2d7bc3ff3f25755e345cd020bc4574faec0f2a22bc3d1cbe6e7e02682f185d19f35dc9aa85e5882f1992622857d7f2a96998416528793756ec9584c3b88ee38cf40dfc9eb2bf79c48ca78efcd236547a8b045189faff8ca6586e9de3461dbfebd0ffb0857c1a61e69bb3002a60932c84559dc60191a06bb8882e8ea1f0cd84ee06b44f538c8ca7bf3bd4f189de8b43c862b1a740ac222ea817fb33fef1b3e28560b125372d44e9548cd545cc4636320d0bc287f4e496e7d93afc8d3c0b272f18dac8cb7de784468a476038b63301f29fe1c406c402c72b034d4d10be4040f9c914544a6bbbbf1b4ff299d636466ea1cbda8c84a0919234aacc194a9c6d79c4065bc2199507c1a6d4d09c29aff0f121d0689004a0d75e845314b1028b682111637c1cf378b42e2452ad15aa2c04f4d6727add9fd18f043214d9a925e1490acca2fcbb04a9af82c69869704ea715ef48b222fb90f33d334e97da9e5202f14da85215d723c6dffba94bca07ee543456a982ce644a15bb8b8d1e649da899b627ef08561467b5f06aaa1a8d37473d1f369e299d5d378dc480f28e53b6c3b40d9d0befb6d19153c0e3801944421609177ad99cea7c2f88e9a4ec546f9f8ab8a73a85bb178a3aa8af6ce3b90665e78d1651c66c56a16c5e79438c1d5f4c0d03393bffd607266af2bb696d78701d6888b584e390789e1a1ff1edd6d6267ad85747b1302471ce97627b5d4eb49fd7cbc49830179192fdeffc394265516e536a7c9cc20dde2ce4586ca0858bad118b19f5c24dd400341b231cc2b6906630cc2c38a18a0c1d03a4339440c01d5c0a11bd6f10d8139a320071e96e3e344ebe82cd9d1af3d95d5dddc951b7d7515bff66d5a0838600d1d725f3d81ce1f4c2c272e6f7c4846703e53dcd26d9f982a4aa15443176b1c3bdb3a9769b7dbe3905cb28da1316b4c124908ee2892d99b5ee69fdbd43ca2784ac0e32dfb1e2fb720fc9518952a5c3e8242bd5ce4805e1b594c1782ccda1a4db3f42a308fba4a0d33aa9a0a17659d840266d388331880f12a0edbe3559267a5f3e78453a35df8a10986ddfafc9b56847ebc6b47b91e1d8f9c81eab80ac698c9bc6dd8fea13a9e0c5a36d68e13daeae3fd4b60b48a655dee21fd3485a0c0a122cc328dcbf2e5d636ed7a4383260b2f79f10fc6da42c56b30b870b9e0f16b8db8fb3424a7ba9cd8f252316418b45ea1ebeaa32af6604a511cbb803be9149eee1f72e88e082aa312d7d2ec4b06d0d2375113244aac2a6b24e11a3622740d1926a842e6aaaf995a5c721c0779f8f86625ca8f42ecbe7faf610af5984bbd564432f007b1cb2081884a38d6fd724c8a803db26aa84db94aff22bb41b2cc83bf5d2de5756926bbc94c425db0deaa7b276dd2bfffb693d07d1d9c651317914cb293b0c27d6d4a4c0a9e8723b92d49a8a1a469c14719c4b2d7551e524c2b886dd86591b6b91c43a6dc1e923aabd02c341094a54079a33549ac449a7aabd3278286bbed4621e11b8ff797cc283e6daad8b517ea272571ea72b8a03d71957689036748cff24e64d8626f1e3e5451233cc413c1125392b4e62825d4b559287d69c77bd610b7eed1d6b3497f88f745e25d73f3376af7ec36327b262fca0a603b5afc07f7207df6e515e34d52440af5f0d00ff961ef8de191a16e27dc91e99214e6641771470442aa41b9c65c46ed66cc1aca6099043e0626245587ad411ace2420c7c787ce46ad3acd8653f964b9b8b16dea4738daae6d30330a514bd8180dc0081f461fcd4e59461c8bef180c2823691c7a0d40a301e8360a8d86a1d90874da400d4275165fa3550b5428ad11e834064d03a1db08f41b424336f287ce86d54850c2d7d2634b04ba5f24867a908e3998d52b08bb78e5c383dfd3e474a93673a951843b739e446119090189769b16a92543511b2766a137fff72bfd028e36c05022b3b6900c3d96f29d2b16f432e1a814a439b8af2f7a9aea3b6191fd8185ce32052eb038c346b2c00e6400d2725a4463608d7365ce6c90b7ff4de888d39aa119a2956536d2704a0e6e452413a75a744c8004dc9e7810a833f1db9807b737210445d71d87d14339c692a45bb40f02a502b3fef7dd0c38fcdcc528eb6a3ea311b66549246674fa95b1e0e1d0ed8328731be6ea8f6cd65cc2e5a9cae1db65aada2956a32e35e62200c33f8312d34f8e36d4102265b2d28c091fd371e0a09a28df4701c486bd949f721e71ddd49269ee98b999448dc9f91f6154d69fb69ad728358e861c4f84f7f54299366dd246b3be16318e514bd53ec47405f7f9ec47a7ae99fcec37f80b8a036d61949cefb997b89e953dd3bd1ef7e15916bba8a8e306aa94ea84c301d0e7dc90db4ca4b7d6d5e5aab3fd91ea1a5c54b1c6478701453f41137b60ef09a5f814824cec297881c8d792c5ae076deb3cdf51fc91a6e945144669b83c317171b6d13cf74eb4a868a587725472910bd5b8dc07b2699f623a52fd68313ff3a1addd91d47e7d03230e07bdb5ef0650801093f15f79f17f54eb5788c9afb094fcaf9cfc9f5a99f55f7cfb8f93f1bf9bf4ef7118fa2f2ee14ffeb8ffebacfb6a53d64ee1bf4892d2d29eb61fecd9efdc5dfb2fb5fcbba085ff2f22f967d3fbf746f72f5d98fe8bff3c5c6a1c84fbdf7f056dffac9ebf4ee83f89b3c4ffcacbff0bc7c8ffe2127f9e9e7f6bf4ffc280c9ffe28bff332b461ee5b70477f67e06d532c62afdff950fff351e2bff8b0bfc3ba9fea135ff2f03642b4726867769ceffb555ff637801028f2149fe8b94ffc94b77ff756cffb319ff8f927e1d3873ffcb96988bf6e4bb2e751daa96055ff437e2485172b570b8390667923c11a470f0a43138b3aa0cb6780ced84fecba669a7d5b5abba67adfecb6bc201cf35807584d9733efd695f38db7f69efea23a5ffabf143a7a20041786f138cff2a77bf0d1338e13f34ffffe612795e15d532ba3179ff45cea1da83f6538ffd656e2fdc070fbd1f3efcd7447a345a11bc7ec1c18de8886360026c16b9e8ddac8d636a8b5ac2f5198eb64102a9f73fd1d612830d738afebcb5b8b139a1b2b5763fc4850a6d3ef297f39727a25003fe10021c2640ea88fc6f8454cabb38c6a1336542f2479ce890e054982b518ae8fb0f26ad64ba94bf1bc7b9062d889e8c48fdc1d6cb4cd1ace5189aeb1f8f611548d6dd315f07b3adacce0cede3cbc7f56f51686169c8c8345ced23933772bbabc6aa8722e82567ab8e075c0cb230c1676fdbc3241a7077392b756727a04d5164f7f622a00bc5a5fbbfa3b190c2a66bab8e011a2ec6c024023cf9b2d186d8efc9b4f561b66a40b9443b3137637962959a904244a7d2da1c3e38df8d071e6f4252e7276e8ad7d38d37998461cf494fb3ecbd9d13d5d2dbd8486b6f095d430760fb6937c7e5608abcc1b3f94fdbd93f83671bf8b14c5e88d6171fa6e2693013dbe64e3ace79a7db541f8222b1c770112adfbc0669355d5f685b42415352abbacfab5cf97454d7a2b7148a981b3e866385087b15751448a5bcc2cfb2429068627825a83d62954aae6c3bd53d8ce6033baff2d9283f01ca1f45f47cf2a7edeaea2632e3178ff67fe3f8a3bdb15a77fcf745d503e50d9d0e3f89aa3089086324fad7f15173b342b6e315488d4751896202bd57f41b6980a37b808d275d3de5486217b5831f1c63c14af840c41784b5112a45ca3f7d20305154541030aa6f618f23ce28fa168185762f2e41c4df0f14523351aac2ef3ebb3de6038f0077ea60b897c420ec1f98417aac5a55b48431a3ea078d82b1109d616637e5cfc522d477bb794a95f1082f958a60c48c4175f2a95adb4c215ac1fa1981a858f1cfc3d2c8b8666da2e3b6dd5671e662ec03c7418bc6c4212722c9d1a72b7638007ce5983ce5677055f051b6a5c1735c3117377886d5ce2ad20ef98da1bba3634d638278961ec2cc45cd8a1cccb0c0ddc3fc3a32c40d9a4f676a28690412df34af2465e3468e873d0229b7773c4e7040f80dd05bdb0b0e53ce94221ed226089df6b87db5fc2170d4d822375affed37a6436d9d8a6b9046d85670f148579040b58ed67d7e5ea39b3061031298237e28a041b36abaad5e241619e82ef8a74081f6aba88b2c4460289b0abd75a09bb6d2187ef81c04b5adcd9d1b91f7917422ff18835e1e363f338fdb16146644a138c386f0aece3b082241220a35e07f0b0668ddd689c7879943a4b6d5db8598ba312a5e30d257129a22f902ad5991ebe95563be466326157a91c2d53253f3fc1976431f88a06cf8715a528d2e027d3627e576d79ed97a3f1903dd69c60efa7c528453d9518d120df88cf306fe415b5dd5c05d204378a61a9a7729042826693c5c4021002968bdd3d3e0a890cc0982486064444bf305e77770da3f3f91c10e55d958769916bea0be5b78184601f530ff2e70f022100394ba7d11c9dee6066274fb98be4f896170aeda2befd84d60ffe9399cd264d217328c574a3561396c9b04dcc2ee2586a1382828b8524548f2dea58201733217695aba9d6c64fbf0a46796172a5406659c7d91a9229d963d6240a81e4b195cd45f09d5366af3eb4f402e67edeb5fc3a6881336ee9d2a6e2a5664526a5ac804bb662c3ae41fba8369ff5918a71ec2909ada78438fca6ef91b65fc5751da6ff449142e8e6bd3101fe6585ccbb93bac737532c1569c435a675396a4f264bc3c5bf186ebc6d511e47684455550ab8f344b19a2b016181cadd35f84d7507bd0e395d158681207ecc9f03529a1ba66d94405fef13966af3583167326dc8dbfa7de8c7dc6d1840a4a900a35691cf83cc5c9dbeb01331e5393f0cd37cf0553a7c75f81505024a1f466e038fb73006e792ef439a5e35c9d43556362b44ce3035bf9e942cb8fdc9818556bb4bf6410ceb9e91e1a2b1a28d815408c45d7d8b389a96d1cd2ff3a519725a267775f2946656c684c4ab24da62064a273f561d4654e54c4c4303bb9aea87aea3e2d7953cdc719fedde514d0e3635feef7a37151ef404831055139e995d84bde32e18dd1bb4ec69a800153aad0d25ee44273a9eecc28e015b2ae539c14076522f8145f14e21a2df408d8c417bae7bf5d94ee51e2ebe10421d8351ffcb0b35694ab2b5f564bbabfa7df47ef63aa100eb72b81e6704e2090328e5547e94079dacf38a65887350944d56661ba1282a916a6225d1d555fcac2713597121dc2e8451c97c23aa49934125608267bb78fd339bc6b436e259c23d7753f511cc4d1805360bbe12bdada2b08cafb878025cc5c72365b15673ad326a0305e675557523d09812af4b54b352dc39cff278762cc33c2c200309cf3f73421274100259a1c6fbfca5707bf27ac4f1acdad89e7780c1be40703bd80224afba6c03a57fd2ad61d6a1fec9ccbfc57622fe10f189831c94c8bdcacd62b27cab7b3925ef5738a85b6b7e340b367cf9c62132b0989837b13aee8d8fa0f548eb3adc519921eca814e1bbca41952b5bde79c7b518db9c4fde2a69b64b92fe45185f34620eef22885b4a143fee4ece91da5cac444bd517d5833ba1115283b3680267ddbd659e89e02817bd5750e28cea29a4a65f32c66b01b9ac46054b3ed41c9dbd007b149269d58be88581349485832bb2b0b111ee302d110d3b1fd528070f52d822a76abf10375e19f1f2e1f59d2ff2f31c850bafa32f27242d70bea093ab7745110f397b754e267c7d2fe5f7288c5f517d46316b759d11cd902394faac175da31651ec85f96e7799134fc81c3142cdc0214e51b4a47529cccd2d4e2203da294f8d6bfca8ea497a25aa4e027f4335597109406304c2480e43a609f00ad8ec681ed3d736028402db8123b30cd79376cb79506b1852059380d48db2decc6086bb4d6345cc8d10bee2d78af20bd05f512482f41dd4b211b2407d46a7305d5094ea2bbe921e2454c0f385f2726f9e31334ac2a9a09b285bda8e4a77fd1b642417baef274b342a7686fa42718967ae3731d9d52b44b769e700c23ad9797590d43174a83bba25a29a56373ac3cc6e017f975b7897e17868b70ab8778171446bf2ad0c7502c3358f9bb3612b9074e0524f4b4a81d241415b69f7c417a9cba34f01c4896a65e2538cb4916ffcc63a9a4ab9f180efd662571999cdfba1e2fbb3a784424cf7511f9b61607bc274a88d7b02158f7d1bf058d18f251eba03fb2c7cc1934dbc61a0cdf5cb2236dbeb4f3a88c285e1d937e3e10f086db02b7e4fec9a1cea1d15df3845efb80ff64080632e31a29a27bc8d78370d82327f58b8aa04a8a9355a5c73e5544845b45e98a0bae910bc1fd25403b7418ebb0e2510e5318f7e3a8e72515f6838e93a468d3866c512c347fd765ed519a3ef123249540202096706d9225f61d43e164757d4de18ba39209bf47b1127302b72234f226a6cfab4e6d06a3ff3e2d9d4e17cc15508a836094ca90ffa3582cd2257d0358041408a1f0aeae7f520064449aea8e4e1c235ad430f98eaaea53089d878ee9b844452b7faa6d169856e911bb87028a21426a4ac7f4bf37f40d5dc62c287b0b091b77077762af0ac3810f11d9a5b290e8163ac824afef3c6ce5b13e5bd35620c28cb0ada8e9fb8d01f8e397e9a510ba53c3767c0b9bd3fd61840947d11565befebe8ee8846dfb44c5d6178ccbae4f5d2063e81958dcd42f0dfa88c985bcece7ffd9ae1b99a6fb5395a7227ed323e7af75de773c0a76848aba7523657b84d605055c43d2a8c3bcfde7d7efe8618c00e51544aa2f023f8ca90544637d2d880a699340116210f4a89b168ea387eb6405b9def464e215230c927a108b0f63ee6cf872c609b2ffa6a4230fb468fc2494817afcf27d44ebe84c4aaffa63dcba2024b86c7ef5f1435330a01a0eaf7b0c4af2f08d0068d8a157dc868dc3b1558712d16fd27d547216cb21be1a95c638252de0194b08abc1cec9e30e958489e4543d0489e599358749d936b5cd9421d5b2c54ffa7203f7fb168265be6cfe73ff03221994b9683cb4ab2615463933266ac9af614e9c320c9b464b2e7e5ecd114014c655c455dbf28f9722988d5aa12cdf2f62262667129dac08696a9f06f12d622e0f173fd2454d4ff384df033b2db8afa1b6b039bf1ce91aa835866e259a22f51546c9feba8f72d489c112e84d85f37609dccd187c279c76bcff5ec25bfb2e1b59677702503c76262c127a2d6d9699cf8c02e294ee0286556e77b42a9eef585368bf7432fabb7205b5b2bcee6ddc9adea628d3fa21ba1a3dd3b4d3223b9ff39f30cb5288c80d540fa5e110fccf08aea1041f6531e5e13a816a26c9a1bf2ba18a233fdf4a4571742d6c226fcf36a34b33da7ffa44fd4323436f65c8bcab8aa4d5e690a369b46ac791dcf1ed0b67aea91ac7666f07e164dc812a17f043894f0d45f0d9897180a53bd57772e1f3a3f9b834a60dded1051a29595e3a6b9ceb2c77dfd2c86e816ba2b4e88fa1c90a597297aec79df4e8d1fb8fe93d41b40945b0e37b33618a0714889525789298bee8b3705a67e3b065a7af8abe61c273d98bfb51f19dea9284056c1c9c534263826f7478b1461d50f13d57077d9cc4c361097cbe10e649e319a25b18d7bcde92e4290a483d074b6d985f106c2f953b1ff7d3be445587850a64ce898cebef46fe70d5e78fc96db29545ca02d93c2378446ed31811a0c29a05f8ba908f81a957427f4f2ebb7348fe155155d2e1985be0144a7c8830cc0101218079184ea7bd2489a8d687c05b45952a711c81b624f500d4a697088484e40ad28ee783b3b9b21230807c99a20ff4b8f89d8220bb25428d81d89410d4096332f072555675d9aec3ef6afc119cfc41aa312bd19c827ed44a4d26721155b8d1d97ea5eb087a2138c5201152f54ef099e4f1f562c10059c5e895e084c42941764bd58243cf708f296422687fa1eee479e02943cb7b5ab4e9e2ec47371e95da452f0244341988a3d8ffcd364d6dba8cf139818e658cad0d69e7a67326f89f197ba6801705b26e3230c3ab7f9f59bba1ac7bd56e8e6ee47ae5e48292e06d0e8a17261687a08ea99c5def24e0fe19edf7494ff01125d41b7c95df2e8305d217137845d71f3508b1ef55ef5131140cfa0a12bb9d27b1a18581fc6823b33d5d811023bd34176f6882ba28a090b1e9d52d680a35d188742ff61f5617e83eed574518a7410607dd0c35676e1f777dce5fb85c9056f5345e2718cec63fee79b1dad5a213eba8be1e2812bc74ec3ad008192587f66d636a9be1a2a7657f6eabded6fd62d7587929253c9c40b718d9f6210395b35d4aadaee2161e60a52fa9a43632e5dcd8dbbd2027532419d397145e5126ccb07f79488495b7d863338760a723fd2511ec96e803be24b74334b60ceebfbe8a7880c41602e2a2d09bc87120210a5677140f240cfa6ba085c8f55cd51cda1caf79d560c4071585107caae29939bf165e1e6caa480dba8a0bdcd84501ec9102de7f4707b78a0a04a2040ad89b6ef0433706d85408e440297b2dc33c1b463489757dc84440e92e6fd71e4e3eeff92eb0363b038a57b58cf7bfd6fb80cdd5fee1ab128ff5815295ec632b20aa849ca859123f294a604da45825f7d35e8d458768e02e07ced61cdaa14f8923c43f4b203d3b53d31f3c41c20a7954630122b0f1b7db020f7d69c32b541925d90a986b2db539ead6f48eb1ab76143a23efd1ca5e202c106a67eef7cdeb374de4ab7514e3fcbf80bed755eed0ed470404140d2bf4ef22a7a8d796ad73f90f85f8669001e473aa51a6e2fc2b9b83c9efcaeb6e80402377b2f3f0341da3836c6b81643e60634f9839c90471ee4770b839a3a97906b1098e07c466547febac1227750a0e6108042702baa2efd35a6f45a3b8d5f31f37b206be43da1bfb58f68cb78ef148a2abd7a94d0fb784cccc30e4320fafd68af20259e89ac5d63121a09586aeca020563afa6920fe980afe81dbe7cdce64efd5a26da613783d21c64de3a5c5a85c33c320b4b371996e90f2167edcbbf91aafbae8123c3c206899210e7f9895ff0f01212274fd3ffeb88547609391646685352fb9938aa31073da2ab9e8179e9f9015d30004078ead143d5acf680238ef1aff4aada5576464d0a2a6103d5f0cefadfc5737832561c1273c25c37335915a3229a7eb22a706d2ab96016a4b00c387de2bab98f2647016e27e9851e6ecaa5d7668e69c01f701b4124e301faab7300906a7bc8ab41cd35f65ebb2af014d99ee9577657e5deae4d241b56c224beca5304e45cfd49f7cda075b4969d59bfabfb74728bc1b66d686512acec72f6df102bde91c43e823f6917928dfe51591d54fad949ad74b517c957d51b950b8a8c2d81d666381501ed04034ca54b27f5aed46207c4a37bf33abce4a6ef4be59583ea5fde507b3122e00a548541b59c06195abbe13313d42ab8534c8abd54edaae2a22bdf105fc6c011a9541e3bf070d0c434a7795c7ddf5cd4a59ac2836cdc022c9ae38ff4137ede955aaa6fcf0de1540a111365a945062e02782abd83522aa397fab8e13f37bb7e40bd02fda7d8efa8e0a918c0bdd00db1dc2a2a670f194d59118aac0f0d389cfc56384767d843344d51e07c098eaed011578a5ad3d8d3e3f63d729c0fb3334a805e924c51406f46c3db17c20f97ee1f5c7c950c3b9da87677c83213889c4dd4223f3ea8e55626d06093824116d93a77f06995bc05c031c0e861708d43c381339c4a4292e12201c75e8f07c30a8e714cef8aa4f3afb14968157ff6d58ca8a6da5afe694f927885018b28c458cb01464cfc3388f665be63562e775cc7180a5125f16db9f110dd19fd1bfa213e333d12c091dd70b541b330907b72b4d4470d36b7de9b4c2f3cd48a34cf51249515635d539530e94758827bd069c2a017444b8d05063e9ff107577e7656f82e2065a3dcfe3340c1b612f239d2a39550849e90ef8f9fe6431331d65929fb1a7c88b4ed660f92ca00d309035fcad9167f895a9bfec940453dc43cc02f7fa0ea05beba7a2aef6fcebb9e3aa1f682202a10e5f4c7f7ae485040b69c7481e06a378cc34b70a031520ef6ae4043a70b1a78b11b03a99e1566c87c170e9dac2c6ba4e116c3bb2f5593fad87f5097a0365a53b20aa650da7c222a5379280e740faa6630e17d9c534d10a7a136f6ba2a674ac43ef11bf14e0f31e94b65600d6649371107e969d7ae9e3bd8c4d36fb49a1b0fc480fc0a1ef5a8115e1512aead377ff7214a0e39f324c613d05784881186ce1f2c18b95ba01cd847133595fd9230187ae79a163e722d31fe1ea51bee6929078b36582ba94e26b2e81dacf5d7f81274c62f6c47aa2194588cfa632255a7e19af219fb43bdd64661f57698d451967c12fb474e47b03f1254507bc8955a57ae18e32b1021c8ed824edf047ae5c9dc70d6e2db92240221f82069857b93d053f7620e3897441797a78e5e0713ad791e6ccdb468d50b01c3ca8625089a650bccd85c961687011822b22886fe02d6a8002e43b618daa4fd81692acb5027c673f637d2c6fab279864c823b4669e99a2e9239cf52c2e8de61214cf9cad6eddd65458a0a367c00af2abea716e82e34081dfbcf4b84aaa9804d2b5fbf42cca2e7268bc4f50ee9d2c8077c1bc84d966fe726075d70aa74c1fca613b5f04bfc5ed0fe0907febafd244542e12090e981f5beb3fb7f6e20e08a62a96c12d69c7c409e50ed8292c69d7959ae5094c3a79ea6a50a296ac3927585ee3a002b60bce09296acfd563c9d231193090e070adb6af62fca4de46a2bc5d37f7c93680fc4ec184a3593ebcc616a5afe1a4a81f643d77742ca44b44ad3433ec2c7dd82c014c19ba193c5415286ede77cb557d873bd765b71f6f06eb9e5a6d71714c137ed1c995065236a508ca25b12f99c894fa63462ff4cd9c31b5459a5de069fe9e64866296336b9446db6912aa23b4cc3957de74aa6139956d3dbc0fbfc4fc4e2581d3af5f653bd6222ec3095abf3ba38e9cc83a638b1148b20bd2d140cc729fa7aa544c478cdfb49c97c978ce845b64b6b88fa13f3d6a261a88cd10034a197c735b6d2a658181a391b8b804aec2d9e938552e70fe0ca28a583cc35776d99e07e8b221d9e85cd85fa5b996be241771e4d2cc66e7ce0efb3b8b89c462e65ca376c169b466a23f18503c6b7f55c550745b693da3d11767f67f984c93d5b58d90d87960485cf7e32d9e90697873b7cbbd0369cbcb40d515aa95a7ccd61575947c27599f1863649215be2641f30f367dda0ff14caaeff7b45033ab2d92f1b9be5f62325d5479b7c2313b38c67ca9c6af2c03b91fad138077bf7e0fe7d052311d6a37586b4f830d01d7648e20a6ec20abdbb59a66f4efec40ef047b23bd4e5be9d42b959eb7e4c7307f8a504298a4726953a49e1a3c82b1ea02a0ed407ae63d9583d0c625fe5ded12d11e623bb102fb5d0d62e2d4241a2ce30b44d4a74c155f040437796446318e4eb359fd2eb959b64559a947ae35accda2f3515e9885d83150404b146f51952ad644099592daf847e8a4aae2af8e4cb5b13b33b1817ec8a9ede5870f18f0584f23a6036c456a3cdf41622b56e6131c37db8552421167d1c49bf6b1484e08e54cdaa62dd960f1cbe84fdd2049afbbbcebf4d3f2070bf610eaeeac96f82798979aa8b85d5bc80e63282020299462ebd3da6091f08931876902c5749197e509157da47950130b28bf4228e1f4950f8c09b6befcee281b87bdf0a2f2c513eac8d39a681b81357125208fa10c04c8c90165f1fc657aa0c9245dad2032f1cd528a810137d9e9e8be90438bf86dea29482d6ad9f30af2ee122a33cd017f9e80ca918c03caf202f567569bc66a5d90461a0e8ceb13a8f0a6775d9e97115ba2b80104ca74dbfa06122c08606efa8ad56bf86b59d944f1401f8f2dc5cb89dfa3fcf628f464377bc3ab0d60f7e579f3ab6c30f14ecf7042f9802e62246be8a3bf83dfc9d635081408c8455b668cc03f8e83ac4746faff3aff9bf1ab59fbf2694c8475db4e75a983d0afb4b5dd255417858186d1d1033c06bc41fad1188920109cae256212a693368c038e52a7b881473719ddc030dac6f8f891a2d00f1440da4b84512ca0c28706197c11a3e065490376826c0a2a66d19f33a06a5928dbcda9f4d74c488a6f44daa43f536cc65a11f049b8c4292f2b1091ff12b84312037fb2987959ca47f9a5444bbb1fca29ebf3813e7004af2d59dd01f0c45bc8027c3da320c402943465acdda630936df4e62ab018030832d00f3b6f7df8ec0a249ac4b272e4a005560028d2e245c7f5c60957ffb88b5c5b78250e0e0f48a2051b8d106e7ed7534e72f91d9b7ee880a2f008acbdaad82a11770956ea8a6f7ae85148c8a77466ea0e19b1f7bd011e451133334cd9a24d42cad6468acccbbd4baa1dd7c51ce8d6ee9b4eaa438e4e1fbd084c304c55ee3478f579cf5d9148c06871b9c6acbb60f76e54ceff11e07f93233e6926093c322ddf88046a3f5a6526ede3a70401fc66d3e4ecff1c09a2a904dd70f345990584dbc6d6958c2bb7a06321e63879c4312e9fd41c903e7e6d885d5c686ef25f970762542b745972fbe09e10cf612a9e6b56d171557381d2725f800c4fe76f5560f9ff9a8bd3af3d934e65da33d2f294df80fc71f9c8bd7d537b2363ff73236e279788c872af1ee8ce2d2819573720b995ca3873e1f8249b22e88fa179a1561aefa9057f693a4fb4f22353778f5387dfcd9d5321ff2a58344dd06abec0c25ebcbe23083c75de0338554c756db432f7fedadfc7d133b96388169e23a7d1d47d7cd7b49bbc13d2b3f65fa6ac2368fb95cdc2394f934dc10708b8e3435e5438f0d618f882ee9c2aa8ffc7ebd1ce7472c93ccc007b955f0e3c0a8837021c66ac97091bb1062f8ccc574b6b78d9a939e8e8c3bc6b2288c98e131863dbff724bbca94d46378324165d87eb5d3a0862d37d5c4b35dc32c7750f922a503df8010e08d53d186d5da7ee6092b0373a06d95b2d31c297b037d0a3999ab3ac8e5d2e6181bb09e446c8abd219f7bda45f140ebe4d69706d275dc7cf83ac042b35f9218b21ea5d9557b2ae8b4b88de69b03924452fd6008cc74036a818e805c30d61da7036e2b2a8e388b3729257a04d5568413120d836f7fe6e6122a48445a24a49d2751048aaafd841265d853e57c3d7a6538c3795a10e2e521f2821550590557405ea2758c95ffb517eaa48efec57f696defb626a59452269902af072508c2073b76ecc861661089535ae50a35bd424e0e13a794524b2337abf4975eecfc69942e193fbe949d247bec72a4fbf4f86e939b33012c8346684f0f570f263d7a7a7ca087ab874b688248e86c325db387e9337dc03825d76a679445a3238892084f9df464adb7d64b2bf596c998a3567faac54d1a80013187779ed13b1afcf35aeb37e7317ecf2836a5bf6fb5ebbd0cd0b3d0184720a1fedd6def2579f66507ee506ba58ff4d94e70bddb6fb507ef97bcf8f4da7bbdab79162b5d5436c13f70a29d9e76fd767baa3df5404ae94669f4ae7f718aed52778944fbadd731c77dda4559b69d27a5d3dafab69b5d90cef3a9243995525f765546dda9532adb3f69b3ad7549a694d6ae3ea5524af94e65ad2f65935aa98c5426c589094e626cffeee4c7ae35dfe8cb26fef511bb24cf7eb943ccd12e6572879823beb70726914dba1c79c4e6ce9d9c54c88410839021342e933b3071c7416684a669cdc13429adb576cbf12dd6812c493891040f7890af0726c9f7a5732fd84cfa982547e638a9039700b9be496b800845ccd1c9b71dcc3f1aa18839e8d74f12b3f8473d66d1e4463bffc855ef88cd4e3fce1d52ac3eb13b83e28280e383465059ceccce334bc62f0c3e5705065fda6002617b463b3f850406bffcdda8b27aadbdf8e184ca17a6b779d373a04dfbcecd3f5531bb140f3889bc117224753880632b897f36588063f8e42d79ed2b39ebece9243442e5e3bd8081c1df9c0158c4c4bc8c8c8c0b172e5cfc4e8681992e60525d8b984f753b19b7887916319f2365bae91fcca35ec838150a8ac5e9d7efc60199912981e6d2bb3be7a4b4567b372bbdbde74f6e6de31a735776b777d4677a634eca52f8846cefbc6b550e70f4403ac53b1407a45fa65f86058bc8a25128ef58c4af29e55053459c56867ea0f0b46458a804f934d9328b5989f2d3c39345165271e273b4a5e686a68536891fdbbb312b517e664f165948453a39da7225068be24cbcc7288b50189d7039ba5243058eded750811bc86c1c5facbbf11078ca29a734d5c4dad22d3511a1d56f9a2e667fada60a5c6bad35cf5a35ede705533d3df002b94e981a2a708cd7b11cc28c3d3a8c0726c93ab9be0bf2e6d2c41c2f9ee4b4adc334d14701a610b824632f028dc3887ff2bfd293b8e979fe18e0f9ed369a57ea2810c16b84460503cef671b65e10ea79aeea039aba12e89bcd8baeed9aee12f2f45aaebc1b3a3791670f60e8d57cf92ec81b09f46afebc21cf77429e17c613302ac7da38e6771ac0f8aa10624aa3ec90d01c7fe84e412635893e6cb83d348e0da9cc43e38ca0a32bfa28fdfcb843e3683f83348ee64d20eda3e4c5ce517fb290a791c689795edbb7bf6abd5bbfd2ce73d3cceb9589a6e1f86207387ae094b9e6e8ddc6b1a395c19a79aa96ce7ae7f6cafd1685ba77bb3f3333ddafb515d8b7d7624b9726b048eebf292b741ce1f81bd7820a598b59729f380e88fbebb0026baa2a387a272ec84c6efff0cb09e6babf50b38cdb8b26b04c6eff3e2d0626e605c79cae8d898989a9d48529fe8c8ca8d925d19fbf7dccf3e60976a46dc328edb9f91af7820e186a481c904a7a4ddb6a1773c7bc6dcec15093e7bff88182982be95ffc408125cdb75907583be6b8b3bdeb4d6fcb52fb5e2caf5f7f7184630f6884264740deb878fa35c8246ef3d2e2e947fa2dba1fb8cb41c1c890c5c37c4c4c4cea77728b0efcdce27107d290f1a33a161d184932df1c867916dd8e8c276d1073c03caa03bd28c37c8e8ce9a67fd86bffb0e7a26b16fe93093f877ffbf69be92b0ce4c5bb3732f6eaa933755c07a6b2a6f1c0ddb67928a7af8931eed74edbb6699af63bd93bf0411ac0984daf75dc37b7932577fa1cb9798db1941f3d0a1c49928b20f968f2c023366b9e8af49e8af4f1643df04fa50c6a0fc45f3b82c43f66fcb23b3dd6809c4c5c90ed8fd86cbaa523d60699e5fd2336df0d48e9a1e0fb24396f0d7d41cc03b1ac426e1d84c0276bcd0531bd7fed4c7f3bdf4aa4fcf2a08bc62f1d07e4e5b5582a953e48e7d2efe4dbd50ea421d79fb2beec7672f529620efc39f2a59bfe693d4fdc774a06eeaf7f9b0d476ce3b826ef88cda5a7a00d32973c30e6bb1d895d3af707922297e3d70ebcf314670b342779d107fd6e5eb98a36efdfbc9dedd577e5cda9578d23feaa6d647f31462ffaf0cf6d3c4ff0cb327a2d43fde843f5c283d4eb98a3630efbd68b39e8fb178bd4808066db50a899997f1a1a2a47f9660ffc6c63c64f422277066bc01b2f3fd3320625f37d945f3c8bd7e2869fe3384e2b956660c4e88f0183e370b76c926f60bccf7c2906251323060c6f7bad3b163f13bf4906597c8b162d542a954af53bb95fbce86791ea77f1a273998b0e09cd294fee1073b0f81cd9a2ebfc6e13f32ef311bc37dbcb74b7677ff6b31e3893edc378207ef1f0a9036fc440541743bdf07bc4df8b712ae536502474c6dde904668ccc44a9b971e3fb6a6aa847287eecf8f1a37ac0ce0d35437323c71d3f426fa116f21474687ab0e647761b2a926a77a87b0e3768beefc68dea110a29a79c31bfc332c53cca24a3837a9922a637bd4906f92761fe499774b5a40c8b169145d7bd92e9686ccbc7a3782516b98dc9c358bce25f51c4d2954a1ca7c9d3e9f4413a9fbcd39b4c27ee05c604f39223711779fcabd9c1e3478ea1f430cb9732d6e24ba89794c4c2a31e8f1a49438ed46a6a626c4b9635f827e57b1f5fba94ee21f692143c2e4efdb763874d38c230fe1c897175694da37591e5f3629c4a3dd7040cf610adf6efb776a5e6edb0def43b2cd2f55407d0def443b437793a524aafe395bc21da9bdee4e968de0e8bb4752d3decabf81bea5e52adb5deca19a0ef95ae4caf7b40f43c4b03a29d825bfe898a41c50dd5c5a126c460ac980e45042bc7743adb1058318f92f9984e67039415f34b66102cd4cb743a1294302fbf93f1efe4d3ef64d3ef64ee77f2b643fa9d7c7f27dbdfc9f57732fd9d3c7f272fc13e20c142fd64027717d56dce427c98970e77584aa41b99f8d04e7ad7a78c4c8d297ce043357065a7edf402138392993232d2a38c0c95b5a059a896ec9fa2462e5ed0996aaa54d2a38acaaa4f6d529d549feaaa50eaabbaea5015aa56ea500deb951aab613daa5b2a977a546796c7b6eaccfe5828368afdb12f3bc5c2eccb16d92b168b2db2319bc5cafc731b83b1b99c34bebca16d48fdc564ab71aef84d0acb67712dbe257aaf5158100d8768ac0816140ecd8e5ad575a73895c96454ae10b692bae258b83c3d9ca293561b8661185e97cbe57291a88ccaa84c86b6719cc9743ae197f9f2223dbea0326dd11eeaf2a12ffa738bb02c65f4b3efa8862e0a73b95c2e5750384463b468e34ca7300cc310bb5c2e97eb616250281919162c5aa4662a253da662666c16c98c6647afd7ebf57ad559485b61188621eda12eea435ff42706f57abd5e2f19166118d6b0865dcac58b172ad5cc0c8c1833460ce931064c9e3f1316f4cda1d9d16cbd5eafd7eb5567335667b35b64343b72f1e2f57abd5eaa3aabb33aab330f460c1932febbce4b61a69082f498c24beb68f64c1f1b7bcdd7f7fda75218cf9f099b41339c433164bc5eafd7abf35248818666c60c1a35668d1ad2638d2d2c6a49594b1ad9d8ecfbfe53298c3da77ade755b9e8d5d5ba98d4d7f1a9adb73792e93db735bb7c975dd5396aedb92a18b933159d42d797361d76bddd66d65ff1917e6a69c236fa44baefcafcb4543baa4d4b8249374350e7f18d2255dd2e56a9cbe5b04a3a0028f3b5ad380b2255d470a70942d1e8df417acc92d8bae70e6afd8a4d55e7bad2b86337f4d5a6db5d515c319add41dac015b36e9a4d135dfdddd678d16e0f8eeee5e53a305eeee8ed3d7895643081cfbfb6b0881e3f7c7d86005b2c7e6abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb1b0808080808080808080808080808080808080808a81b08080808a81b080808a81b0808a81b0808681a4143e50675220ab42827ad765a2b3d5a49abbda44922498fa4bad54b2a69dbdc36e9712b6da58d339df0c4587ac426fc0213839a2894f4880a8784ac0c855762e1ec680b97a319e5a1ad594c4b868749f667d1d3a249f64fb95c4c172ea44717ada02954826042214c1ac92c528b349232b945cea4cce5d3c4898f0bca6b8642feb99c4372ce7925fbd319abb356e9b1ce5007140cce998c0946a9542a915af9e53f0ef3a05918866118c26030180cd68ab55aad56cb8b5ce6463ef3230b83c1603058abd56ab55a3164bcbcbcbccc40cdee91b7bcc7150ec58a6461188661e872b95c2e9794c1fc0583c16030ff719807797865188661e872b95c2e180c0683c1de633aa0e01931611c8ab158248b46611886a1cbe572b95c523693c96447def21e77b98f75b95c2e974bcaa44cca3ad50c0c183162c890f1ddec3ae9b183715d1ffc4afd3c2c7ff11acd8ec2300cc3d015ba5c2e972b0ec56291cc280cc3300c5d2e97cb25a74b868cef3acf4b21059a1973c60ce971c60b0d4773369b61747ddf7ff489aff813613128fc19a6e0a540433363060d1a35bef97dd2e3876b2e0f1ca686e6ecfbfe53a9d935c2b187be5e7366677346e76cce999c331a9a2dd257ec68704162ec6afcd4d8624781f97141e216bb15ccc6b461437ab4714daa1748397e94a75876faaa91ee5d446337f531a7313ac3949bab1145a3c179cc6d646aeb014af0a16a41e61d8da37b60ad221428734a2822c0f35f5082c8f2391f0c7e710681217cb032c5103e589182653d9510fb3bac18ca8f3df2668725df7649b464f93a6e83c3f89237cd92f1eb11c4a04c0971e0447e0d79204f221c08c91752d278e5233dc91207b90ba28888bc49d27d5424a3f7611d2fec38f69b9b61b2ac872e9031c39e7b86af8c198eb967589a726b521660464dd268e1183778c9a494f23f1aae2dca311ac98894ba21fef9023a1ce0a741663d6a4411d125bf3999b8db95339c514160f7686e1d1c81c4911766964bf2f4542dd42c5f7aee9eb8dee5451891a5076252111aed91ee5e8f0ff460d2a307d638317af400692de58e0cce9933a79c72ca504a49a994524aa9e55ca11dcd6a52980ed124a8100d6912356ed334a44374688bbc194412ffebacffef36ee411934e2412fb4641089ab5e2a12a87c5e18a948d02f8c542fd58b1423d5590691481bb34b582b76c82e811bc7ed908dd9d84bbb118a288bb0e936ed020de6c3870f1f3e7cf838d19ec6a93e7c48192bcfbd3be69d3b56a8e9157ac7e5766ea44ed01afc3cfa31beb5525a79a5955e670f3107fd29e76c927dbaa8947252f78eba28952ea564f5d722687d53d2b6e7dce7bc5e2913c9bd50a97b4fe79cf5b5faded5779f3ee78cfe5d8d10054b962c59484e72bf101ef266b2bafffafb8d006eb0820914284c00718065e3891ee8d0822b74d8d2040bfc620b68f02a6242103f5889a2ad74808916132b41a072f403fd8112563c1982478a12b0c0043989285c25b062022a5010c49322f4f840020e04c10a0978809f900f3e8e48c112a01ce123840e44705283032cb04203a10c8040e43345c7119c2c9101275e270865144e98810c7c3074031a4811d42be594d5da2357f810851c6c1142122fd8f1e13284132f2c4988c209a411dcd40b92288a519260424b12b194d08e7fe7bc59228ddd9d78d2e833d4082ac767424ea63f677f911a6ab2f48cd4ac90bb037574c7296b9612531632d82f9b7aa0e7f9b3cada42ecaf903d6b75eb4eca287b641eb2b3d958c8b303e54f9f43e07bafa7b38465bdb9a56d269594c6396fae94524aec84daf5fddb994bdc5d035af7cba65fefa473ce26b4f6fee836fba337493c8106e309f885eae409ee584904be9fb4f2a260f0c793474d07c2277e83eb81bbc30ef6a6a06031e064891d58b00408a68022959ba84a145598689aed088c55e84069877fb186087c63e7c859ea8acc2fe215914fff37afc87d6d76f26bd7c160bf345193e45d9a29bdee46879acb2ab4a6895afca36f4bb86fdfccb7d696ba684489b8d13831eb481962fa8d7bfcf23af85baeaca763fa93d7de8c7f18c3bc7c25a69f0f83779f3c25f8e5c3fc09e6f4e2a9ece96778f9d3e99570ff72fa194e9b9017afe65bae72e40fe1098008e1c19e8ea927b24c3f430696983c2535fcc9a341ae4e2d3bb55a3dfb017b2afb4ab8c79ee95bce60fad39f4c26d39bbc254ae2ebf4d83b799fb3101fbfe9c1e8327911265794fe8f1fe6cbe7c161bee971908fe79b7ec87c9337443ef654766b16d7a9ac2784b334e22ece70c702ec3e2eff287d1588c0f4a3ccf454c690eb36dbf993be88d5ba5247ba32f85b2fbeb4a0499f762da594b2a5941bc6dbdde89540c4e036f2d5382d90deb5d760e03e6eb708f751dee8481982875a1683df4456dfc428932b8781e9fcd5f90bc9dac723f99d5e7e1b89473e78fe9678e49f3b4c17a65218c4de7c79b9eb425d914905762b7ea549108ff97785f0e961de99bc1c16e7cc6772e53379237be4cabfa76726a5d05048c541f9922ff92361728aecb9f247be64cfcb474a28fe79284f1ecceb9c1ec3783a30ffe2a9eecbbfe48f7f530811b93a75a77ff919601e7b4af0c3fc8ba744ca10fc30af0463fcd85b12f3f85f4e9e8e942127ec29a979f99387598f846943081cffc5f31e67c171cc9f1ee6c1cfa9f9c2a0984eca7dad3352f3fe4916ccf4be1a2570fc96fe9237fde33c28eeddfa38f1d68c473390c0adc609d2b9ea4002831e128129058274762d8dc31f06d2d7f7d7ac944a0fce5ccaf15267ea8a90bebeb66d9d0b5181a3151948de9598514638201110c2307f7ace033d742c514635fb0a39c944bafceb99cd2cc7d90e6b9ab5cfbde6e9481902f3a7c73ffbe6f42f0ff3d65aadf370080aa65fed16b916863819b88f5e49db36c9c9509f1691ddf61389e7cb79409c8080f9ad030283b2c78d3c8b96c669bfc19e7cc178add64bde38c6d77f8a8e23b007c15e2fb7013dcca10fc99b66b5a29013ec431efae72a74006bb247ba667ed4755b8751a9196d8b1c0cd75309f150aedc7650ac0d28987a0de34aa404e9ec8f831229d4038180f91962126050a505932baf41aefc5b4b605e493c7dccbf3cd8b398172f86300ce34999de7dfc606c99007b118c2e2c5732a3582c929ccbd8800cca4f8e580a1c8b704fe3e0e4fab52b88a7942f49cc2ec262907fa61a601999f4a5219ad0410f4de0605405ab7ea94b2257f5e7cfa03dc9f366282991dfdc019ac5739ff4395e2a59efca15a94bc955353d81a3e7de2757f5733c660798be7ffd30575b6d558107833c72027a557f08611172ad4bb0d6aaeac721b7b9e2362d8b5a222cd76a25d75aeb51ae6f6757c9c14aae1fa7542251a8d301c7a118fa57ebdbe81262d99d428b41a3b902d3afafd54e355f9b3a52a637e4bee6e9707f3d25a4afaf73df7a4a94949efe0e4b6b4e7ab6e33c25dc5b255286706f5f89e9ef9b9e6093a78aaffd7dce531d408bde10edef6fdbdb597adb913c062993fe7d92a792de10fa5735a4da2984e75a1792654d0f1981e36ad5dffffe1f6b3ea123d0eafcfa028d9c01689619dce1a4e77987c6b883ac663035ebb5f7dad21030cd4d54e5cabd81a80a0f3c26640891051055c18126233751959fbce526aae2443699d0541d3dcf317b6e1aad71447f8263ee6f9cce436453b81083902129274541326b89305fd6dc8d01336adef52918bd79b3f540cff64b9a07c6af3f3fc62e5dba5cd142bc5e115b372945867f65510ca12e0809721b1bb8ec018329fba939b59fee46bedcbd111eb97e24fd9cb18fc710377fdb1cfc52c9052d027992ae68ee051e197421cf23d1ba67e4c3800f56a67822cf9f008f3c6541d327df998cc802687040cdec01e36094c3ff1341fae44b939a61023b8bc6297d300730f87df76e180a063f17f29f8e92b20929a59452ca4749773f7591f42dce95f458a703cf6a5f8146b2ffb4a42bae67c3ccd5562f94402a1c6457c221ec681c199f4e15f0e89e5703f843df41b8f51c020637d29764c906ffade70a3254010671e9b1db809f7be08c9df651924aefb1449a2d1a65972e5dba746e61beb45b8dd432730091053481c1ce42f3dd70c0dc4e5d30984a19ddbbe10c603095ba17a73cba47f72863f021ea6e4f6024d22d03a28fce310281efdb4f813fb592067ad61e9c597bb0861cbf3e4d12c6f995005dba70c9f723b893afccd7b38166e9d9e0397a45e64b3a8920422905d2d1769d4d9b521ab3382d81133f720a802212c4cc0f3598a69637d7d4d590c12996751d7fdb866b951cc77d107faeb4936dd759eb90cc5caa371ae59b23678d644c6b1a1c59d76d2896dd2757eecf83db82c12e1a722106213a84c02f8fff7e091bede04a6ea2d8952c80dc443111e4779bfbee5ad4e284e1fee9835c920ec933e74528620eed495ae9dbbf17dc5d2fca025e8302bfe191227f03afcafd7388ec76e4db351110a2644ab2d666d28d1a1d47c20aa468b1b6a7592253850a6b7b29ef4fcfeef3fe27c57e8c510ead204b4f0838870777f743dbc0fc712481c83374c03fdc467a35784e60b0674ff3dd78b066478e3f83077c7f860f26bdf69a56e22293c484a49aa84b987d08690599d40591a49923b429b9898a82b2d7e036e02711b26566d2931bc902b7c8334307ac45ee769274affceba9b010795343cb591527f9c6597c218de3a4289a04d1c8b7eb50ca4fe6eecb4e4a17720cd9da97b10552645ba5c81b192e211121fe996018ec590dd1756a822b7b4260b026dbcd9f48e3a85aa839fb2f8086c621bdbf034e3eeedfed1b9fed9e60d9adea28c5956bd0b9c255c3338b9a74ac7b4b1f4d193f556ac6bf1d35312563c7544fe39fff69769ac91b21a7d969963df6354e6317e9045ad3802e62b97fc60db08d5f7dff943c5c5128323a92531a81b1887c54d4c2c76d543db5a800742e2f6fe6fb4166be3a9880417bd4e336f7ef6de236d7e3e2369de577eb48026602660323968ddf6195522d57f8a94baefa3bda3add6bc4bd290ec5999dead882c1f8207d714f5f764a8e0fb6b0475df0f4acccb345fef5abd004df076d91db5c4c7b729b3a0a4c190566b412875252a2abe29892ae22eae35fbf00a6e850c38a28be99f3dd3d78ff412c2385ef96b62800fe82280a4c9cdaa222b751b910837577f74c3f8867da47b8210332d64c0758abdf61697e3d00ade61294bb284aee6ff0f2e4fef6eb2437bdb0dc6f6d40cf92fb5b25c43fd2938eddebee1e8b689c98fb6b38011391ab7e5309b0ca3fe777580a20c00fe08708e07304f0ee0d21c00fe007e0e910e077582629f0003ee7c17864d46f8f1a47e67e1661e3b412391ec957fec97daf0722a9f9badfeb974449d716dd6b8b6c51eee9236f5adc8b712af5d5b4f0401edde24ae368d5101b3a4b5c871e7c802565c6df781bbfc35a75aa21abc7f1383c1d157ef53b2c1d55e098fb2f4ea95ce078fa00f8d4e7f8167f53f3fdb7fffec81b1ddf7f61778a5f2a6e83fafe1bca9b007cff1d729b98efbf56fc66c7f7df2b6e03f3fdb748deb0f0fd5776b3f88dcdf75f2d77266f6ebeff1e5d2e7ec3e3fb493ca41e79d3e3fb492eb7e1be9fd4c46f70be9fe484f492373ebe9ff4f3e3fb4953dc86f434be1a2d687c1f8d1761cc4114b6d8f8d5a0d1c2a7bf85131a3468d048e233ba194fe36978e00c4ff502fd193fc303532d7e27b3f89d2cf33b19f53b39e677328ced231a0fbc3705cff3624e6709d0d9017402e8e2d17b2096e175c3f0403ce381299507fe0b0ffcf27d171e5893531d9d427ffceb6fd1d1b0a356fceb97e9685147b3f8d71fd3d1997ffd301de5d2d59eae36a92ffffa4d5d8dd2d5a02a5463158b7ffd3fba7ac557fd3eba3a2457fd385da5e2abfe1e5d85c9553f8fae42f155ff4d577de4aadfa6ab4c7cd5cf42575b72d5bfa3a35b7cd51f808e1ac955ff0a1dc5e2ab7e1d1d8dc9557f4d47857cd59fa3a300e868145ff5e3e8e84baefab5962ff455f646711bd07da83d923739662d9e2760c9fd97894bdef09c6250e8f1719be90407af0bc56d9ae80942b9fffeb88dccfd1766493486316cf1b8937e7f3914b7995472bfff7814b7a1ad163ef7b6f069e1939b524a41fa5a75cdda3ad086b95feb401bcbfdde81f146a7c3e28a076247ac25a8276c61d978153a9d6d88a5b301ca9262c393a2f26feb13c354146c8f2c177b64a37bc52254b1273251dd2e91be3fbedce667d5352b4ee982883ded2e72eb000743e4d8b247968b7ffd9d12f8ed51affafbce4e651d20f1cad103717fd3b457c4456ce50078a2af26f481508713d85f662d4af1fa41148cc2de37b0e20430d6d669472c4d8713980b422395b2d0b5cca8bb8c35b80d187992f038925944ee82b894c6182391221e313291277438a14308b87206a89c0cb1870c2e2942b62fc411c8103ffaccae715d33818420c8585ba7f28165bb22f16dfd9137ed040c46c5897cf0cace034b76eb9155b59ee5dc203d01db4a0afcbbd710113255a56682483da9fe2080074b4de368da6bded3744f7a7ee28feca13f3fd933b900c738c33bd0ee6e7953ba2175d7803d727fe7b953449e1f8dc8f7ce0b3620cfa775d29ac43610a49f77567a64092a4b5a29e560b0b96ebef2bf4cc0b32ee1a8d680061e9082082025a620814b091958028f14a02a551c310512a80e3c14dc97dc444448b04dd0486303c02ecbfffb5d9edf65faf17311fad33f235d9e125391918a483c0891482f57c2023d2ba832031f0f18c1072a5917261419010b028a9f120f2f80219d40ca9c87bf32d845e057b342152596102208212e432cb08724a0041c8640e289236cf00316d8312c3e88a0c8a2e52622da41136e8e0b0570d2c56108d212851610b119e400068f112478f2042980e8010f1f28f1e0834d7095c849ec80871e7c9a40a5080b5112587c78b281101461072738403485e827f53359f016d31146b861cbc0e6030811501384e8e18708433420063ff881080dcc6240c50e3fb0423b088154021e2bb8311039c93172132d71c5125b5247885b94316b73c6196b673d03c4e9dffc5a63a44392873aa40123a225b2e41cb98974309435ba4db7e93a80fbf2697dea9c0cd43b9224470f881e18acebd1a76f9f7b81c210a5b7614f029703062f9d53ce19141483291d8318144427925971b8b62361b701413a27fd9e1ef6cf41235bb6e0259148d6de4b64461a3178b9c611b3378e8d82358346369b83c682062c68d0dded050147f92f1cff3a8d0c3405e5e3d8f582807bce5a2914f259bc20e0862bfe75eb8861ff6e97eed1a8ba5adc90d4ac12725f47a674cea9b5a424d2c592075af7c04eba6f57f2b66077d2d39949248d448aa59fa52ee62091669c33a04ca8948df635350991a201000000200073150000201008860383a15028498359731f14800b7e9c3e6a5218ca234996a3280c8320849431c618600c1802403332432600417652408e9e5a57a18edca51e9bd4aa002a841284718d62085db891d1b4932b0d65d852b70e39fc1e61821426a5d4e52f18ecf670760c2f5e32e1e0b36f5e401530c04a3591cc82adceefa52a458e031759c681ac48b32b858f4658be4e9c56b72cee175f0fb20b4cdd458e9511461953c26b107826ee598afffdb091c1396aee88da6761bafb00c920394ebe589855e803b79a6e0389958154367bad5c7d37e3b8d2d66277d5a1f6558e764e501f2f4e8f33b7315c6e17a714e4d94054683ea6e085b8c12cfec5035e662615759c38cc6267958419faa4835fd68f9f3e8f7410bad72c8e70dfe2e6d6a3e1cf87ab83b93df800ed87bda88893430f7b7325d95f3dba511e3138aa9f03426ae700681db01bd89f6ce279f3e704ade989daf5edc51e67f931340a1ac14b3c5a745431ed30839f56c2b4a7e8c5299ec4e4663188a381f5ad03991ab6f018368cc8cf903bcf3282273ca77b9ac62d9f28cb2a5ddba7f4c08365ba97a530d7d40d6dd87cec500234777c129b326fe595c712376b1b7243aae828aac8a78a066bd261575e2b354fb083acd8949008c0106732e6315eaade8c94e1122f2c2a212d73637e5b9e4d30ee730e604b2eaea7d887903743001b5348b1a80de5a58f4332306402277ff62f0ad25a498e7b011acd2510298392a4faf167e1fb5b972eaa42bc289e07c180aeedd2784ad945c9722384d604a5f8a9a22f3533d83066f28e00af8bacc466bd3fe17eec70709f5b51e334ff947634049d1d8dec56c23f78e9d2786f175dba0a4275200e7c4c8a3bb9a9318de6493b24bddb12c386f41e97a7bce667244bc3d8dfe53a54bd4c909c6a56d8b598bc17ec63591ec9940fada5538d5f44cb83fa332338b4f63995b1cea5c0a5e52f0bccfc99e14064dc8e7918e48967353bfa1458361d401006dfa9b029a12b5ea0b616d6102c975d9187a5fcde717ea7ec96f0a815a8a7a7a6359fa7a6ed71be7470037979876a76929365c1fd618352c22a7a8f1a9dd92f2215a701f2a64991db8fa261e106ddfb8773c6610468c4755db60ccbd065268e325e1560dece671846a4a3138ec4f6f28ff3b04abfef0e438714e38a27157cb7f7e4f13edb9cf3ba768f6042072bb7a23e9c1e116be3df40f258da34c0d1f0228e637a5e728f32442150b7a9ed67b6533ecc2375bbe864da7fd27f725d47ad527710c097a6a4147ba3f92dbf2c7782d9a3de53827c9ff43b50018c056db56aec94abd8a9eb558d1af09f4fb2faccb6d2b40466154d4d1871c3e5c18d9e076a988a86a89a2602738a9072c71a3a6c05c5ad2d8f43131c2c207b8eef5f170cdfdeca27052e6342afe0738d0e858ef55590f53afa3dd8f969e005ef6e3f270d00d0e4d2a282b7664cbb7c5e57c2da651a46f822a4839636f1d381b5f970827f2564eb59cabd129042c56f4971e0dc6f64232e4b4b67f17429af83eaf36174e551d0f203471695e799f7bd420001681733d2762d9c1026c1b61a8ee5a785c05835f33b844594dee556a1822587672255607fb4c319921d3d934f144206b2d029212a4cc4fb5fc4d1c506b428c06dd4413e880255d523302ef379a156aed529fc270aa02b895192ad35980a1526485344956224f92a584b5a444ff08007b1e2ff16a7f667108cb19be2ad0f655f4185062176388604b3df9270cbccf7e75a03823654cc0b21d54b30370d11d448f6733eee6324df21f60db8b135022556beca9d0b1c685570f98bb7b5021f44de2229a1597831819249e398589b7452534d47c9696cce23b958fd3eae888810d007305ff2f2800662c0c0158a6e5ba634bbaf81a35c8c0c0c61b3e4f97e601d0dda4069f1f45a2690e29288dba3b4ba3b632dca5bb9178c390c71f8db63c846dc6b8c6b61d7c755c5d5db6120cbd19a48ff7ffab909671a941fc1c38c460395c5b0effb1b74e4e3f8d989491126ba40ab6c8c210ee723b1599fa280622248999e46cb9b43171238649648cff91fa6b6f348ac4571a8ad94f7d944975686759ae1a783435f88183c621d0d1b7d68001a470551f1b4f09fdca6f16fbafa9a02c9e28015c16b00b39212d1e917659034db367b7add3eca9489603b84837a0275a4ab5b54610b5d8e87017ee608ad68c43c09aeacbfc51943a36ce928ab1962a1ed5f2e1162624f478d1663c70d7fc405520b47057b38e47ebd5d77b9a5d39707c03a8c941f75781419b0db62d4bbbd0c37e9cfa7dfaa9d9d347ee19c39f93fc052fada4ef816a48c70141b26edd559f4e81abbba5193cca7c5bce510a9f7ba3f331954c11b58f7e10aa0ec8be79f7fe0f4acad2b7a493047bdcac0f4db7f8bca1b24c40638978b114a5fa0273c364d17d068700641ee598a2bd7a9859efb48082a712d195ae3b1d0e87b0a9b969d870ff7e2f71d8f652b74d8cddfa818bc207970227ab04ff3e00dc753bd6849e6b64bc4b4f2802903e9ac8b7a613a2f0c8a22e026cb31ec631c0ae91a99697dfae59d35be2e3f24a93760a214d261f049b3cb923436cce4094743b660990672a00002f56f1246a1b3139c817373cc50224477ff33f38bf7d56dbffad9bbac8961afc955e1ffacd59bdb85059002b908345277ed93aa150fe0867220e3c5aa5fe6f23d999ad3ed3082ffa2c89acbc0120a91d56160da4147eef7b32779773fe44323d0e0cb9318e0211d6cb5888b5c22e1a6772879b9d6beaab12179c3e11e3b380671d094424587ffa44abb5639b1d3f35cbb09692bea10de82257a622c5af8b4584865df06135600cec10980ce131ba739519ee954400a080d3f75511a4064874db82a8d1ff049f8c1243779a81121333a38e1fd508d3d84818f718d9d7145ea6c82961302360957417bdeb316d5f8a4df78fa613d9ad4fc610fc69a9543f83cb8f33c4343259b7697630d647702696e0361bb4463ce09c993a43a6a3d6586e91bf1b6f2f811d1d00aa021014a59f2a604e98ac2c9520bdcda668ea071c0185b772ef742a0d12108d5d31f5db050f499dd1691c964f8b4f0fdfd87d630ff20dd2d79381994bc554e5a0a6675c28aa3029a82026c90dc36e2e6afa9cff17157c4abd223c32d0fa851e00b3810fd41086f996f70f4210f3e866537cf5740ea5d25d1c5e046005d2ab70a59a164e51cf8734d69225017ae31dec7e22e7f89c73d4142ada1592aa9fc0af9084c278e455add419347114e9091e0cc0e78272748f0768b32314823508c5f96fa6d9ecf91eeee7f0a26fe7fa70a63d36c0e17b211c0fac8e7d99d008a11986fe8a14e71579b320c0cb69a9c18e0ce21b84b05e6602eb14947be3b8fb72dc6cb1c118c2747c9f0e1c51d74849421efa8f7847960db87ac3785627c42318dcf801052f6530e1d9f400b6d7edf0cf3ab4a79e34a88eeffb47bfcdfbfed288f33bee34e10e2ef5f3c7a1b30e13348c45a03bb57cb870746623679be7a5da388e2794798c070657c237e99c9f62baa112ad10f94452f7647efa29b2af9751319ead32d3547e547701944ea6e501972e582e028bce328cb95d44f5da06bcb03f6b01b9db305d28c8005640a44f0814ae412568facbf0f760159a8c0ddcc216b61ae80943108f214f8450ad619395229aced19e408869395626c65742846e4e01a12f1caf615903c2d5f43e447942291ece59eadd5266953ead1c56e2178c84458d33e1ec21256f6efb2b401bc34621377978287cb68adc49db328baca2b8816e3acf4151fec28862392511b3890d5924fb8ccfb80e0ffff162acc55cad68c292a3d870a8c6bdcd527e2925fec414764e50a360563df8b0e66a12110aa221602ad88158301751d2c27e7ac06b6160550040d89890108d2746bc2041ffb69e5a9e1576433a9582a44687c99d0aa6d239bc4a269f84284242435fa50ffa8470e56ac9e073470e36a1192b65c1455936cc800da6a6353461ce610a6fee36ee21a15cd65f7cb9e28b273e9a88b1e4fa16dd2b5f352744e111d00e9d73333daff45d4ec58509ab44b73fc10963dd2edf6bda237be6b41b580822d404bd04d8d82287957dc7732ac3483b9fd5ed914ff84bb1b8fd004e10095df4472dbba1ae82d6518e64e5f829a9561baebf3f20dbfb1fabc85bbcf9ad7102fe78ef6fdd6f4e1256293cbbaf77438cd1c95f34e24a9a1b5216d3b693ce83a832f056bb717c1f616f041f15721dda784d0126bacfd38b72d0a0b2285540355d5d289d97fbee0ae7fe87cf73f3fe19dffd774b206402244359847309ad893dcadc137557c157032abf2ffbd2d49ba447d7ed9d1c95921b355ddc0ef29f60269c3cf4f61cc7a42857dff44720df6630e65e965e848e8c05069d92a16e0af00efeab49a3c80c5a595980a0ec9a1cb671ffcea44fcf1cf5c155b5c3c0145c4e7b732f3b3558e05f73237061cc6fa9d9e4a11fdb9b2945e2cb254b0628b22f63d82fe098958ca8cd0e71ad6d205bfe5b48229e00b2610208f2b5064bbaee5b05dfc7f5d9788499f851fe2cf06bf539afdda5253fb956f20e7cccf752566812f368f93d38ea9ebfdcf51c1421b6ac1b3655d6ffbc0c4bfe2988d626a284eac4e54648c07ef266ed3847b4b2ad6d08c402857e52b3e73de5b60460eb95a1f18ea0aacee1d0a80ad4812c996c2a344502c3006bd7063a78a25a5b2ffa24105ceb5edc7896d0cdb3eec2c07f37b873ff1b8bc6e3cefdeb2fdf81bceed01d3bcf3cb1cd47f880a1b785060c0199fa2b92229e412c334dc875617408566cc16e3fe4fd55064d2e48d3277df0d1b93042bf344146cdfe4c87bb8ec1c8a1c347f49a804bd94b741a1b033066e439580523cf6959b88719086911546ba828ef3fe0404486fe4a5bfbb0c7e7c1893b3e3c965ad9adbb44d3d4807f6b95ae12014ce075b80521c69a01959804b47ebe70ced2f03560aed83a964ace2c7110a2a05eabd3396bff6e633636dba6de21c4c594f1c7fc0c2ca8fd2fa35dceb129f28a09cf1efdea1f8842c316a771db24c83cc97231f56482a8993ea0f8c78f168eccf2d7ec403a83b5192f95aeb93ce58096fad8478d2b551f989ca791ec7bb4dfc0b8575afd6543e6df2be80f8e5cf58c305ede52fded057223a441ae7b43e57d9db7231de7b199f562429b13bfc6cb5b9641f54d97e388d4ee6eccaa2cebced341b5492d4a5bb6998d73193e3a5c43779514f5387ec6855abb08d4d284bf37beaa7ef958f391021f8d9d0feae0ec28e6c4023d6536c4a0722fd9e1565b917bf753a428aa7ccff1d7bf18e475c17bdc5f3178872a1d01306b8c9ec7cd9e817d66e6e00fbdffbc830d8e3fb68e164638a0ec23543ce9907d42f3721f49b1f43c907388340e2ad9973a10532104fc2ecb43416ed217081e6b03e3c1fac61219e60e1594175f5221ea948db66af4100b380fccb285dba2937e7a3025a54f5b93ab3435c7385205a28ba42c430b42ed1a9c9cbc73f6d705863c414af195e90b02f7fc0a2bb865006b14a9418171c6ecaac746afe14f12a6f488eb69cc809faa3ca2cbee776e86a14b58dda1d98da73365508d3c6f94692a75e120a8b4307cb9f814bd49bdb085a1c558c2fbaa7759fddff0eb5051a70ad4e69d36abc8d804390fbdc62f92c2d4d038e3f01ba7444fad387a48def6821d6d3198caa20ee46e0a293deb85b052b4a7d369bb86f48f3ca6578050f5d1d4cc142fcbad40a11a3b28b77903ea792870e83495c7211c3d971d2a78061f21944039dd517954edb4ec30a28798c1a7d33268c2d0c678083b7e97a2ef4d7f96ad01603e878a0fe333e312eaaa28899a35667844caa0fb0e163549996c7bfc5645ed7da1edbdf2636264968e2aa6cbb097159fd75d2f80213793f2933efa008859ca07c2b8d6d167991042ecde6c16e701763f5a6f7e5ab089f2a821d621685057dd6851832cf0d97452f362b409dfe6abed123d766bffa70f4eee4aff7ad203082fa2855d90244378c7af25988aa70fa6f64a91d2d7e634aaa98720c7633201a23877bf39360a1c6233b4082ea5e8a481d6375f4b6b29a4b43684b6702dcd9e7070be0e75cf1ac0c520250d84875564121064103f0502a5e2cf4da077a1bc3fc94adf14ba8f7e4fd4caac1e9a3d4e14b42a33a98e8434337470d6d2f532482ee890dfc6fc93e8b52d27a0f600399988530f2802a66bc7b5fee6532e650b319fec4893abcc0faec5f808f694d338a2303c51cb0fa1a227405d2c4a86c81c7e53d5af93fa28c9d276179155697ca65c95422ff9667dd12a1989bfc88bb20beee03826333aee03c8854f4b46376e4c85d62b13867df088d0168cf949251bca4ddc38bffb1683b3231b7b7d00a3a71818d0ce900c9c125765e3c0ed4eb6a03b15f7d6dd28c5a6313b8c4556f62046d285c77ec1ce2c98e258172ed7f3fa45b20b3e2cbf3336cce48bfcc78122a4840630dcbd335d19ee4b9e7b5c1e8012d067190b2b719c3b690abf7b65d8411a710af49eb593c6050ef2ed397ef675c9fa294aca748ff0706862b6c1eb0ac452f60ea00eb8b5a68e7aed6e44188b232f20f14686bdba80e55b6c0689ac953400225665484d9cd87a2ebf9004926f55568a8bbededa8f2d6b7845b61196a90ef23a97bc5c80b2de4ebc179a64c7655ee3ab677aa0017286de4175b71fb1009993f58f684fcbd81b9a475fb7b7bf71d39c134ab817c9f04cc1f6d2809ad02822dfac941bed6e6df42b9fbc0c0ae5dff0b2daa86e5e6431f786b8931dad4e2c6f361766a81421d575916bb5103137afa08846175bf23ea87d2f622691e57357af0b351019dde272f0a0b3044e161f7a31d4a83491f74ddcc86493bee377ebc4380ace1a3576fb680671bd2e9c2317132a4eb0de610a01fcecff12c280a47370c351dd02cab5bbc2ff6e17b9eed5f9fe616e54bdeb177c24acc080c66140b25460a9874b3c6b38cf619358e1a3fa4f1190296d12363f09080cc38cb0dfe7b74c5b0df52336ea2b617838b6d2e02d2ad39e5547d8cb099a965117ca80824bdcbcdb3b1f9ccae390d137b871bd0426da833a139ae4a082536b7a08e2497374f3e20d0a51fcb6a1c2cbb81cfda3b903d3175293751e4b10dea549175dc146ddb30042758416c531e0cf73cfbb23eb402cccf691bea3b87a81ad990a63c1fb0e3e6862cf28411c63162aeed81d7948fa622120a34a00edcfef49178752b729a13c429af3c0bb267eaa8a0768f32adac8f24d6bb9f1282da266736409e6023176ba8bf208268fe56af81dbb2f21939f1b72d34dcf5a02ce13b6261eba66b63e8843f1d33820064a4d9393d1de4c876a7f4184b3b3204d49922b2de90149c0a860f6cb53aeaa0e0d289d84aaf30e5a8806f8acac3965e2a901229f51ed145565b0924d50163f8c8be041c6bec508c644a38c85c4a612ac016caa23bb128b11f14439f9e2cc5039f6734355459b451e71859b8242c719c45aeef229a95f91d5ce631fe2f912661f7417228140c20c2417bc37b1dc7bafe633a17b2438364c85b08f8f7502e58b06689106fea5da1571665f7b7140551030f65c9f594f42468b5e12cc38274304bdf67215c79a7d1a74b82eabc39c82765ad928a40b1946b01d55fa09a662f7a86ee857ddedb4d8a5d9ceb97366c1cd6c8a7f933e7e3b6951dbb40079457a259fade86729f2c79494dc423ddb4097cc0ad088fa3809217c2cb5f65234ed95854ae1bed397c2e1c2a13a4f47c6887f2848d8b2e4ff9b5ebc5c32a37960e3cbe38e555032584b10e2aeb53759cb5ff8fbc6befc7e281c2d8dbeb33f54fa4437956e689f4f2270c2cd0c07a175c6dbc03e532df39fab4a4a0be1dbe5e39f033e832f0b1397391419d0d9dd537d5634a6b5762263f27b355d30e87c706aafbe4485a188339d7cdd8c0071fb5dcd4e3a18a14b88dc6a3902d28af5fc8132f97dea8b888d64a2aa2b470c7b1fb8530d434edf58c5efbd8f2ae88b7e955638d599674ef29e1e04c1eb54ad482ca57d4c4547b96ff778c78ad2854a2c30f86f3dfea158ab6e87b95ebb90b7d081ddb53aa4c7140906eba4c0324374505d22dd852d04c0a61308a50a0f3b51ebdf03d67569ab199aeb328169769b2bcda2aebdf028ef42c7a3168c5597f75db7e9ee6b1f857104fc8a473e4b62ee43b70b948a0d58cbbef236a30ddeedad6a270a13a6edb5c2d80c95d91d371fd4c2409ddb7d046f9d5e0dedea3e9e2a518092ebfe3077007d8baa5f1e23574f3a0d95b3e84bad7be02ca1135a6b7d4badc45c068a9356b76a2be43edb8b4fc36346584dd2c87a8727303263dae75ddc980ca4e81f86e6c6f193061dd506f4ae41354b6c01141e2aa3a2590fdc5e87178b401f04a8625f2d53c3f5f8c7fa14932a4cc2a2bfad70b08f61e907f2ad8e12b57dcb0c7e37ccbcf832192ba82f9014e23c013cb4f57f590c5a6f73746c9bcd8142acb66a9f26f630be9550856bb5bd420b8a0dbdbf5198680b51021dd578057fd70a1161c702952d2732122d64f625e80114b071c2690431dcd09f0e1768c0b5ec123a0ede1aeda5ebb7c3d2f59251afa0940f6d0549e5d994379db58710af7c9d7bf69ca908429cf9255c25595d818b482ea28e89407398c541ad652b67bad433b7c7d9ce4826ea1e895789b785cc469158cbf608952e1953ea387df27e1bf1760107d3e547b72b2e6f3e9eb1db4888fde82f48f53eedf45752038948a6f11c7057bc27ee12358aaac7c0b3f9a43a10000a5a671d4698c82f16d619c3ca6e53ae102c25ddcea6086f87a00abd5b950547b68b6c808b426094c9e1a413741aa6bb187b103ed9c6d09b6d68c82aa2af63ac5c81c8e928356017b7ee1a6bbb788e09a8e2ee3f85b9270f4552fd07b78f2fc784b17345846934a9c469e3231ff89ecedd24f5e7dc04d0432dad2c9f816610db840aff8970e9b5b9eec701cfed59ef22c0be37cc4bcdae5760be3ed211ce0bee1843a713684fe2cc91a4421922b15ea88c7c9402f222dc03289869bdd88e19d948b13c8269713a17100047939e6ec4124749e1bee471eb7775bce0d57944cabc3b0cdaf5fe8b88172e6051813acb9a537b651bf0a3c1cb1105629ba99be314d1ff1f9c1eedcba6ff974a1c031095ac12bd9b85611a494eb357f123c64a237b902baa3fcec855072b6e8d5c20e4718a869ca188ea257d476412d00a246625168d559c68d4caf1d91103f8f3ab8a1c0c449f6a1c781986510d29d2f987fe0bf41a8c9f9f8b9d76af59047dfbad734471382d783b6cc43ff777d5165f824042eb47f86207827c74adcf6b02b832140535d778f030cfaf3a8439abbd54ab5ef34fbb675ee751cbcdd78f0cabcee7f608be0ea96af9f66de81fa46f654028a103b317e90f52174e4d0350daf2cc84be1c2e3e63c0caf96adafc05982c872e5aea860d715a6460c915140f82dc50f6f7a7dfdf34c28e48c0311bc4ca2505f3270d8e8f18655bb5665af7e9327cce5e07bfa6962fbd3e770c2d6973e8ba3959441f61614eaab45b57fa8ef10d5d72d8adc7b11a08199091556252dc4b682e3d487279613e2930cfa34c017327409c9ca1dcc17d667a8718901faa04cf48c04eff2c218b82f975e1f19f5f96a24a3b7147107a1e70e5c0383d550793936abfeaceac4eb1c6f4c8b88caad3c748068d424b39b48b6ff065a21d92ea9972846f17fa003d4ab57bcf99f962e181f88a9e007ad599906b1df13ca4ed2cf07b6ec88dcc8dcb2968b90848aea471a30f21d46745852c7545a14c33f8c4f7b277c3d0a0b41438fba80e863f4f2ed9a46528c7b2b342b6f0bafefb4dec72851e80c182caa1892d81121d8d30bcef2defca0ee2515c428280c796fb417d7b5163672e4ef734de35fbb6a6a92a27b1dde54ac691c5f9a3fbbd67a0abb74067fd3d48ade85f2dddc142d74faa40aa82524358b3d1730fb048a35b894aa07de948bb55ef0a3d01f1b5331c71a0493e4a689fed2ed775701f6cfb459bcc6974f6211edd5375147e37e36305b34498c30b28d099a91ff27582b90531a552c20d05cec952c9860f1d7f76810d4f09953a74615939919eef38b1ad8a395acd1c3a598ca07cfed4b3c4b7410f1a08b9be11be02d7a4a1bcbe8128b06374249162c5d1edcce16b984ca6b485b95ed76038057d7cb8c9d5656931d22f7978a65a141a7e22093dc78855cdddf6f09c796d71391c6250298e15519a7d1e7c8ff5e9e0abbd9e466642112ae17577df71561a1fb415e5850c13d027ce74e0a2ee1fbe5a1a8543c251ed56d1d11fb240630447a31003f60ffd62ba44fe67d074eab3bb01d9509231d83091a71909e7dd337e569b45b7f26b7224caa9382847f44deebd31b6860462ef59a8326d3e44ef973277d818f39592ed50fea00ee47eb90b5ea23a894e55cea8809e04c688b58b4355b7da7745f4ed6fd7a9001fa474c62bcba000da1f662c9a26ae86ea14a216eac16e28ddba90843e43e2e2f0bebdd19bb2461d7982c726c53b87c13223cc42d23a3f14d9e7d7c94d50a8d4b84d6dfb30139ef8ece0c8b3581dae3c612da6f1e94bc6f1745f8477caec8954ad9a81f29d16e7b9d36b1f28e42721f9d04e3375ac7ad490af27d02a59605896c433ef560c80fb0cb780e6ce7c99a19c7c979eb0b9ed052283c946e03799a7328e444433a6919225adf938ce9f4644b21f9ea94583efa0dc4f06723145f9665b9255e0f64d39f226f8c3b5132f58c286acdfd4062f7c2a9ac09a8d420a4f475e989d5a44b3f486c1d5cb978c33ad48fb1f58d6eea85a219748920c971c4e458ebc4c981c59cdb57812aa3ea5042926a1b956d905d3e654ca8d283a1de2266b70847bd551a4d6a4c3c732de7a7cf8bd53f315db87c54ff5eaed500fd82e21807cbdef89580c5c5a8b2c40e08d5f5f3d24f4a2bfcc8fdb19b5d3c195d715b92819242f525f89d0683d359fec4be40a08fcb26b7f919ed7c0cda24f6a2d978dd282bc054dba9e2399d109c024d8caee457a08b6a4f870fe842f8a433a9cce30bf5440b6232c4b4d7781b1f431a797612c58a106bc706ae46fe777424eb0c6d8c5867c61fbf2673c2831ba4e7e139ffffe12d8295e7ff717acde83395ca901feefc7f63cc26b0058df29d0d9ed7c05004436a86866d66a921043284317aa40eac444200b25cdea026ba07c647cef7f39d90c862ebf7d6d38a6b0e32fbb5097c699321ea5d2e878885cac71edcb876afa4ae3826ec053f38ead826ca75e46236742bdbed488a361cb4ea87465d4523f552ebe86e62ad4d877e21e8d33b7ba9680c7ecc981d9696af9e2b94c6de2d7df3a3190b64540666b0a2ef7a2137a5cc52ab9b2a560e1a34b66247a2f9ce4f232d948fca030ac36d7ed63180b09dbbef36d522219543fb4fb4ce23980edd810a4d5863009635c56618a3a14dd2dfb66c31a904dd9eaabab33e1d40ec225d0b574794d2d81d4517999eb94924910886ca8dd67000cb01c0d0acf0dc0651cda2c67f2ab3831b55dfaec26b53e16985d41d82b43b2d82c0e7999c441d9f59a677536fc9fc7dafbb228a48b768458facc335cb4c305e4586a6e50da71097ae7d6e8182a5ed6926749c4c0483863d132b27347c325a66943008247370dc228f304b5a3f0161b61e5d8d1955f8440902c4a414b5c75a477bc82e201c2d72e7b20be4dfe8f516e055b3965e8cec9adec1981917af33a0b66c6936c0916093ecfcb2cd3917c3baae3081724e25bf83767fc04e476ec32ae02df04603238fd014b64caaba44af5025ebe05ea2500d8065b50e243355d04724d399b4c140b700dea7c0df965c1aaccbd0bef2da4da47ab47350916b36419eec611ba1a4c74afc1b71b10b5e46de7f0f392a87d984f4b78d4d4a076ddb910fd2657cac043e2767e3d7af4b7350691eb61567ac4829747428c07839e56cadd9a173e6a515935ef44f79043384d6aafcd66e15443bb2c5623ee4504f120602735c33ee9fc34a30ff9430240781952a01124ef1a8f614fbd5ad62d614a07a3647bf40c521217c9dcc9e5d0a6efe0e0eb28005043bfd8201fdf50384d3204953c842bf0041f1fe422d4b81fa4317d2fbd24b2f0bdfd09951ef2f1cd8a49f878078b565e468e665ff8128f12fe489348046187f213dbbab3a826d8f0b695d05316afbf99ee32fdc358dabca0d5610373bd21349ce087b237e2f5f179cc108abcbd7410aad54479a69bc67386e7d943c534885421559b405dc4312595180f99169ca83e99f820b1668afd2424eef628da83ec361b47684ba600163be100ce9f11e4f8e17728fe70c38e37cd37deba2520772065575183116e23972bd99fe66afde1b3f370180721a2733d04f7108e967a0d68b15b51748e384068baca0f3c2ec38e54e3a686b0dac46e73070636a2946cec1e4021a0ece6831db88ae8a3f277a734c8ac4b1d120c029527ea4381065bb04067f8502aba6050de23278024e89ccb5a785233556af211d6152d117056b5256a8cbf99ba8b9a0360f35bbbad36936ec41a0149e25cf1e7e766bda6813d47ded9f36e5e1bcc64f9527c46a6811c32e73496ba74d734e13ba3651a65a12119fb669385ebc9db549b65bc0cd7f29cc617b49ecff94e2522a169e27e45b8f16e29770aa04ef805f39b0319b388d8fb5ee7098483041f8e21eb1d15a708d031a8c8a448a859f9b61303574a786aa4fca4b48be9596494f7e96928f2918b7da891a08210c2527885cd564bd2ef709d8b498aa7280c7afbaaa5b8fd6e31e3d812c043ae24674d2a85a54843b7e0fc7b5efdc7c472910c0e65e7a376f526c3779cd42a80545610cfbe0fb782900c57fdc007e7ea2dfd527d317e4d84fe38d9db59d5ce745e5e64b6a4ab1a8f2644f7232df46b8011510e24f1111db6e9ebc0a20d9d951b3aa53c969880d1ee5225f0ac8ae4085f75e0cb741e3af93619f3eeb4b50eb1bbde20e7c4b6178fe8b3e965dcf4662d62a1ec40f2ad8ba9fd88ecfdb5c332459da95f6eb990b543aef74b93870bad6fb80963f7817dde0b023157195c59a2f2ff7a0b73340fdca4113d1524998f17c82a8dea8f0ee556b5843611b02079c1b78f9230e2cdc48f152271c01e32286d3e7cac435948c05f18eb75e3d69c15dd093a6d1cacca2ab386bf6f8bdf77e1445f157709a337198b378a76e93d003d69405de3e0701998a35413477208559d72ffabbec879f4d570a580d40e9e93f923c22ce3538d0696407e8d30a3b897198059e4b349b80ef4270e35a9595441406c14f32c9fec1350352afdb0a66522398053d97a4d83fc1115c7bdcbd2304324f6e4eee2aabc04b84b53f71d98c573bf5a3f4042a8e600d7c679af1034892ca0df56cfd5f42f6b5f0b0c85ec0de33e25e32b0ffc2af3706963d1ee65f465002049aab6bb937e1b162c3e8e588057dfffa6f03ca9107130b2f096afd4bbefe386cf6bc98775910a0b69ad062f8160aec10bc1ba00acdb0f7938358cbc8aadd504d5f8750cf4baa8686fd3718b67fb7728cbea0e8638afb3bd67e48da91f627ebcfa491257e73350af7f949f81230cdba8d064dfaf9c6cc8d22f2d94b3ac5b2f122ba50d8e743e11e33e4eac7193c1a5de78d4d3aba13f7add0ab3a2ee84edffa88c4faaf266b12429f623ff81f49bb12df59e9410a08c37cfdc76b0042d1eb5463780dde3554f1e9ac0f70f3a15043d31198c3e860f99f8c662189f7881239528ca592c70c63485db3650be900175bf01e69b611fc519267be933c79046059226d28849046f071092150c05866fbbd48873fc2798864e2d7c6dec69d59227aa51fb91d30970d1fbf4ba4eeafbc52bea7f9b40424371fd4fed9f92fbd5b66cd92e52f397b7e16b70417e78b60ed9b08ff1b1cd9a95082d71746fb97f832dcb003af3c5162c3f1cb3869c1c5bcfb6d671a3c1944047a5135c402136812f26d4d6adcbec84a2378999dc3140fa1393a8d9a53d2e1abb15acbab4f55516c877ed571c55acd8fc1e973e88b4fe9febe76f47c6d8d8f0f68f7d75f20787b767bcdd63e90983d0c6c1810ec79a03598a1014d371ddd6093d155766a96690d1fae88c3d89f28b535bce5ac2cb3f4b2f7d979a0486d379dff476df75a89b691f74372548a1bc24cbbb66b00971b5b87f52324ddff9a29111e65f563a5558581334f7e252791d4cd7a99b96971241060c15f7f83e2ad19ecfdd9deb1cfc49cdddc79846139f65b8ae880fb20aa25592d79d99298b050aed95491b93f68e708f80931b69835e17dac3f8ddfa70a31e76b8e3814b409940ac47bb306f6b677553eb717f8bb9cbcc3a69c87f05a3b0bee070a97bbe00b8a1ec84860cdf9fa936a3dbfffe05a9820f354bed4958c75971b4a1d06f61cdb5c4c32da096eae0e73bc65bf88de8dd0241c0a05f42e678686e6639da3b249659e2ba548a49259127c534cea98c1abc9775f2a8afa9f32ffd771af90f0a9caa8860cfc677c784d1701f6c777201b2170f5f48235c254e227c52dde5eda28664676df324192bb5cba23b3dc30b6f5a84b52a9ccb1bce4802a984a171b3a33ee1766cb7bcea519a6d35033b804ad8056001aa4aa3f24cf1bad4ddc586ce118d7d1f3542bc1300e9537a4a6da8951ea14156f5ffe28b7a5d75fa680d74c905ef1cbff966c2916cb5eba2c3f46659b33251618a7884316bde3e194d1210dbcd993ea330afcf8cc9a6bdf5b8b3c837648d78f2a48e61080181696db2d3f0396c5196f5b951f62c65b8a8b645dab511bc2a8540978950e8d2070a5b3f7a24221f5ee7530ecb4e7ce492da4c60c09dca38b166421e567783f2dc02e26f9755865684065813564150ae41c6462d41e0ca65d10c6682d16eb89501b1882840a0fea27408b06db04547a419a0143983612c6a586dfc6111a42dcef1dd22cf364f98c2308b2455fd962c7e13227b8c231fa51b007f0c63c9edb571ff4ef5009ee05dc014fd68bc10e1598eddaac8f03681cfd649b8b91d2637d3c83aeb341472088256b60546e1816b585dd46d41a238b6d47698b18d406ff77a4a46ae9e1a5e622d511e208f66e1de77b1c4f3716df1e6f9428df798b75d22d7f0ee7d6df8d54bbb3757bf17a61d9fe1f34b4943e7e31a7fcb816975f015cc1d0a8976bb8debd57cd43388682faa6229d414f82d7e1e9470f37b9528bd596ac1b51f5f61756ad42a2f7fc416eb4f1757b926e19ceb3dd188040cdb2db7b109ddd20038ff35a58904147b7a0f12412db11f801716113aac8ac6bc78e0b31ab436227e4f6d6401da8c7da3d3d6a619df2c5c19f7f21bae3693cc7ffb7ce048ddec75f5256349a5132e618e29f076be92aec1522a135e430eb29eec26da64f30bfa5ae01327df0006740ac7ba62c59e113b084be0e34b9873f93b72aadc3bb24066a22e5d4d9b84232b5be09af7cdb852ae9cc7280d526627b4713f5b8f0a3e2f10be02e659b50b49f58580ef0139185ef173bb550e2d516bb8fa65773949867f5d3b6dd28c2a623d687da37e1cd2ca57e8ea149531d04822697b2d391b303185237a353cfb88b2789732306cd741d8e608c64c554ea9cf047a601923dbfd643a388e5ca8412aac314673e45d9a013c65aa5c96ea5d18e7e9427b99d063b7a556a2b251b276f8e060bfd566a955a0d167aad142fb51aade856a9a5d434dad18d525ba969c4a35ba5b652b3d14637945aa576a3856e294d00bdaae967b4a2e8ab6845e92b6845e83dfdbe53a9a89bd5aa68b7f5aa28375a735f956e055d846e1d5d84ee0f7d947ae7fd754951b7d595a2ee9415a334ef3d596e6b2b45dcd32b56e9c67a56d145e9753ea6e1f43b8bde41719f9c923f77b7caf2a028e606d203e52e461b30a7bdf260702e3c3a03cf1b5807751767d5fdc1423ee918fd72ab34a6245ad5af2c49523a5c15a34005682f98d760124c38860682506f6bfc74022adffde499d732729a8290f0555f88f85f323fd3e5dd5b8a6207b259cedd997abf961dd51512fe17ebce7b0ec1b5df65404eee2ecb55342bf1f6286905c74ffd21e56badfc1c17b8be09edaeb03427736472ef265d05b303ee1da702e9abbbd14eec7907b0ad3b8e7910edaae9c4f59c384e2b270470ca48789ddecf244bd9b96431858c34af2321a2d895dbf8ba637d2c4d624742b7b9aeff39378124f63fc0db4fbccc6041b8afab53d631087be594c5bd9a6dd28c30fd4bfca12a4f61894e5b04ec7c3ac8481c567390f9000573cd0437f1e175d9063fcc55620bffd27a522b96c98719e1ab1d3419cb6df8316fa31923b70b37a23c0a9ed3784a233950893ae6861cfb35d777fdeafb3c7b89a99a470da656020a002b8a0d57616f17b384558e0bba7b1d2d5608e0232e75df7af0da030c1e09aa075685edc60074de273658982967cce5131b829e427a72c543053c8abab656b0d857ea58ad5f7959f7eeed6ef0e716ef5b8e7785b5e5975b2ecb27ac4f93ed5df2bc4338ef644648d753be4d7cad2c63986218cbb3b24da5983ce1764d4a558105da2e545ef180f4d5b139d3a574aab0bee71f107e21309b5ed95e9bc30a22f3a1bf3e7fc4ab84063ee3968c31df8c3be10ef5e332f796f0a968bee484f0a368b6377546d6a75bf57e7037fb4b6ee27ea911ef108c08b124584838f3f8ba1ab8ba9896e99fd1d0e51b9d9dee1a4b4b2fb99c974f2eaa4bec1e9108c0ba9cf6f1a6c9c26e40310ce33a1981e031fefe40b901f8f7e3c4b53739ccb9aba5554dbc9f68a24114e28af98ab1a4558f6bd3545431ef5caee8f7df57575053e87246a8d04145b1d29d57f256940f9b3153cc43188eb5b7bbc09b5ba46f39437e30a2267db5c269717c928b992a5c546c4ea861d060a541cf3aff57ddd6bedcb72fab6ae208c11c64c169e902fe6c2ae190e6b427f2d8eb40036438b7ffa2b28ac6066a64f1e0872a12ffd4a1c7a5c43659859d7d33eb86d4188906ccdc2cfba3e700bd392bcc818a7ae9223ccec5bec551d7b35e9e9776b8b5aff1c681351a1310c1ac6d71b87ee47f5905fab7fba7f3b38984e1abb2ef32faf0a058086ce8973fdd190ce149124034bdf99c2afa2db61815126790322cf2fc3dd6df89f71cce6fdfeec957cd7af55fef163061916b2d192199ee2bcacd0ec950120db1d580fcb56835ab2fd303d27e52f4bba5dc1e53ae5e3389f23297a735ee7e6bd07dd3d8e511e65418ab871d95fda8e578d8c855e33a497ead05cde2a7d7d9608c22065960f352aa5fa33fb29d099022163216f26079593501b76e198ebcfa4d6f834f0c47ef599f9fa8248bb59e15b25e4762a4e0ba3262e1a55a8fd9a39caf0a19490f5e0635508a4d597217e263e38811f4fcbb5f027b5954240a3c4e6696d209ddff3c6be25a3229e44da0b6a30844138edbaa4456a1cf17844e4bcf7094c7f7069cce177a77f0d0d1be36021de0fcb71429b6421ce8ae356fcd2d135301f008210c1ff02aa5886ae1ec6ca2a98b298a90ba30f93e3d2694755888601f9f8c4673a67dda32a34e0431708ecc36137ecd576ba30012e4da5d95aa2561b124ee86bfb8d81f843792b1d841f902c5a858a9e5b24fc50d23c9e49961f21a5997c1bb07f5b0c20d4bd4bf102a1b0cbb146cc3d350430629f020f10f73584aeffc93822f248784abfd13ff7472e49de8a24f8a8ca30593529121dd7ed35a9c6588b3544b70bdaaf1fa04df8b638aecf39dabe21daf76ade96fcbf9dff6ac4982d6ee790cb94b6ceae5c78849e6a34adfb0fc008c77384c9aeebb9a6e269a275ac7464ebeb5cd242ba4cd9608ecc3785d5c431fa29e999e89c550b76839ae54e2940f2815b9853760da071ad82db071a3e006b6b9abde04e107c7229a42872082e5d4f0d96cd1e26f031b46d14d30974599afe2f7e3d0f224b02ecaf1e60a9159a04ec6557bebd8523990674e59895f367d4582b852d353bd3c8919b62443b9acedd308fc89a2a934c33219c02a1a998a0d46306d5357836b1ab020e2ad89c0d23120c913cc44133ba3f186fce4ec36e6296a1bba68e72735e6d5dab9b1121454cd9c5b7b55e9d28d424188633d6db025a5a19053ace4a7b1f3ef1fc1177909d5eb57e3e2f13e11f575a57dcc90b22b73453ccb0284360899863a374c04debd9ed09116d2ed458d74b5d070a810b6befeb3238c8a90d67850afcb30d41309c1454956bd0c29be4b92436899da192d4e38e72446a0f4280dcaa543e7ec70548ba8c684734613bba300a80b2b86dab4718132ecada7e95ef30b2711a4651111b2a2b402f79d115052cda7415898545100c8f0a319738277d6ba09ddfdb61e173c7da0cb2ba95db0603175e542afe458206d469cdf2c52c6d4a54fa1a8e792cad402714b511bbfdcefc7c2c95620b4550c20797ad109eb34dd343d90a65b7469c0633d5ffa5c8396c0a429d8888a00abbe4d9fed17e27263434d318dc263dc4c0a81b22360efe2c1d60f0bfb4c16c9bcb8c1851dda2a7671a3844c8421b62edc9c3501c21ccfeca4c152797d2a229a0be699679e7535ac7361cc2a4d81d746a9b2748c6673939d347890f330e02cefcac1e6d2b8dab5392897743078a0454b9a2b2ba069468c25ba9097a40cbc20e40f1d8184671a15ad4cc8320610c830efe4c3dc4e8742d3591887d278f90d9432044e5b2d81bf4a487ca4919cac3087c19ddc9293982ef62c0502a3fb5149dac781b8dd09d5f8f092b24d21ba101aac5c7a2c59da119c15351c820069ef1d1d98e8f0a5dad2871652a06dea496aef95691ec2c306adbba00f72d913453a3beada27e5934b14d0e59ba6f6b532e5dcf70fb9c833b88c54cc788b14c563ba3f6ef6ac78ad1cc7990a5a44acee76c0f729c387d7ed79a26b949d8f4cbdb5f2aafe39ec2cbfff1de747447b38abf04dcb6a926d7be6d73dd6f26972608595dc23146e8d42eab5248341216ecb37c828f13d30edf8832d328f951709d362d0a3e152e6ade20788e0817e574b7b5726391fc3192704c4ca28878686b90fc227572078f88d534c32f177ed3a1679b7bae9aa9417173efc5c7168404c94003d112bdcc535efc2dee2cc639b59d299e2ac65e8e564ae43b5bc5b2c678f3cfcf19e1d5c6526ef631871c280988ce790689cfb48f8e41658f94b13dd204481c7d4dfb5eb18bc22075a788d41515c0e472ece71909eeb08b7a38e8c1b99683e0f4f9c10142ba00c6d1a7a1f848f2b10de780a0f26feae5d876c531fa234ed83042489e5889974cf893f1543eec737b4974a428835eb71c2f88101560d1683488c8877cba7af7d123eb986a0d0db1c1680e638acfb805e40a81686ccb6302a3110829085a40ce2a11d1f3e5849994c88c528c63f26e1fee6e32faf2941cdcb4fd002bf8da92057efe0bc17aed3550bb6ea734a77478c842fccb94dfc5e52afe530ff9b2326daff2207bfc27cd8eddaaf1d983a68d1b09fb132ab4e6c7e2324d807124c8aa5fbd2f54c3476fda77f05c5c13ac7ca14b24e2f594d094bfc36b6b10c49bbb1d3febb9bb8d3b6c59bfd7821b79cb5250b72e9f74592d70f000d056c796c43b0a900ba125042ef6ebcdcb7a56d073936100fa820d16cf885d9cc1e9f21decf769effa482510680fd2b2605040ec989a05b4e695225a4ba9d3f582d7891dfc0e01ed81f4e3b9a45376ff737711f7bd90dcb3e8fa4ac55bb53e191338e668f506b9eaa1aa2bb9607f10705220b2680c083fd7eb9a29f42e87bf75a1b4d6714e971560da93ddca1da6b69e7cb364c31482679ec697593cad270d29eba0c9d54575b72373c97cca4b13c646ff33e16f9fd54673505a4d0acc5ef433d1fc25ba406fe35b7c3236a7ab7ee49935c347b3226d7ca84894c2fdae58def3b3693cd4c407ac5744682aff96e90f2564739d5d8409f2db4875f5498a2063a77ce04535d089315465c3ba39d6d6b29a32b37b8000b6dc9a50713775072268c7dedbedf88db0f9a959c302ecc56d9ee781979bbe540c4b9ad237ac13e425c1b383ed7de5733453458440ca8d6152f854efe2ed7c60e1002f8a02c6a06ee0017fc944b05a2249e6b44bfd933c9ef3988d9e2edfdfd2d3baa95f2b0ac0a03c15deb392e97a3b55e49260a79904a0ec0f5edadbd5af3d339538132735da11d99de40d27f7ac5e9dce0710aec50094efa3d7938a421ffaa1435e23880bbdead983f08b99e3c783b2c7f38954b593966db5fd5201f2c14d258044c70f82f0a46ef21ad045bcf8283eca554103a92691debd8cd8ed3d4b7b11fbbe0e0a5258e9948c976fa46056451b22b990db4152eded2a198a9020912a3fcc252a156052e9a1b4ce09225d8b0e43641257fdf68b0b67c1fe2ba0b29f01c19843bf70c524c94343fda5295db317522a309ac24c6815208c8098fcd58e2af90b5a06480a7604215c0cd0095a8b298cd4bcabc90ec948cecaf4f61f65ef0d6eec444af0888d3509662735bd515f9b248b949894daf6c4cab9bc81b130bccf3fdb16508bc09eaf9984b9fe830e11e48aa47aef5a2a2769e805f5e1e5ec1f2aeab5efd79a208ca2969f1bb9460e5579b0c3ffe9197243fe2acd38e664e0861ccb59f8e09e330104e2ca4dd7b88a356ce0d14c1ef39f0a9c390b2850cc74f306c327cd2be80d1c7b158bc18fc3d830669f096b54101ba9f8c11c7f12041306f5e15863476626091c8d52c26ecac6c2ffb9b296f814fec4cc9cff6014bbc6601871e432371e5d4b8e4c074c81ada445694e76949c4592ad5d10c4bfc78c67e8c365254a7f93c170b027221f00bce8e2d2f05c476800ae99a9082052933608c501ae946eb084fb023a4001af736bfd9253838df9b708e6f5711fcd84da2702bd15e4e2945e18d4c45d192a1c3acbb5f8f1c61e20e5ae173419696d04bd68c43acf7224a79137f0d3e4a736edec8738b384af38be5a4d258575ccb3f4f8129c876a30177359a60d13911442b6fe2afe137aa8744791b33b8dd66853e28b3cd76e5301991843110a90f82b7e054e803770f24ed75cc18ad240f1dda3bcb1df0909c13a2a3a178807c295324436d499bb7cea689d16804c2a546576ba2892270e571a5c01fe5ce42c7fd0518b32e0e5f2d2af71b0d221c4a46d5ce799391c5806b5076d44de48fd01d061078347fa4b518e5c24e3fa4e6d35d0b110f99a9f362b5368c00bda9dea5bd160fec1c6850fd14deae68d6ede7f3a3570de7e1f7040b28cc8f510c7d351aac70fa247eab4693aa0480c8d01e0e901db89532cc9bb7dab6224c23e7ab8378277bb30a04d883e667b9977248be95eefd5c3aab21d39f188518932b1d69d59f495f64017bf064231731893b3dd17596d78010b10bba3d3720dbb2b4e63ab03d32eb4fc7d3ff83e1eaab27f50de0edfea0c68532ecbab30cc70d42e88789d3f6fac4c1955333f4c619957ca2bf24bb89a408703042ef5f5820a5f970c99ccd4650a71c0e01384ed87cf791a6d7b01b7516b6c653cb3e93d6df4b2010d701ab6e7ba519442cb9f95c14fd0038c01a2c8f5a6eba63cc3962608a22a3193245e3b44c8cfce0eb74ef934f362ed382f4176894ad05397689e55d99b1a550f09f2879b6f779ffadb2273b260d22231e763995d3c3b47055fe8b9c447f8426337a8e2b09fee9a31bb09a7fd2c08343b0e6cc158e73f3f50af10e09b2cc0dbe43c09ce7cb0f6f266503a9146f8768457654ca5136d7a407977b52cb3b03c54b6c49753585455577040c3f01c47049f1b92be6858971bac97aa5179b2cf84f7219df76b819a782d321fbdc52cd2057a37ef1db3f596af4583c164d60de4381c2c9ee5f659210187ca1b9a04f116d0fbb87e884cb8ec1eb191c699970ebdc2e8016feeb51bdc3f4ca8b0199700feaccedad5cab19d657b02e1734372e3352c8b17e55770f590136d113be1426186d3b22537bdfc8b98271ba428ac49f4cf5bfc7e99c007e7aaf7bbefe7944b84f30df23835b2a6e901510143c970d68193bcfa2ce7ea9602f01ec846e80c8d93871db47d0a220e1761e001bd794c6a0e62bf9be0901b498cfc6782daae8fcc7910810d6951ff83aa219defd00f8779ae3040f7629e6be4b03710c7bae9a591dc3034298019fd105929f0dc81413d9dc05e85cc2633683d5dc95fc32593717ea4b328dce2e5f9beeaaa2e494fd1106b2f6bdbac7fc64bead3c6d1d98fc2ebdde9c8854a607b776010d42ba0fabf95e4dd8219aaba04bd85dba43001b0d50f761280788d17e9bcca6a02d7544e82a1000cc195ceb136e8f7a4229db2c1e311c616d08590890de056293f9118d21b505c87a41d5496756dad1acdf532893edfa3f9b5f2b0aeae408fff75159f0ec1c82d037e2fd4c853884316b992c127b0d091b82ed41834c5c4d49dc3ae80aaebdb7578b2e590e247f0964f86a4439667a353e04644e9988dda060d8836af62f3a44046830766002ef6bba03270b1181fb9eb6e234710592b67009ed62e89c9059369e69705df44cd5534179b9cd0faa71bb386c035c7982ccb9e10b8bb556faf93d0f1e06ae61247e7f07b2d785a86dcc80ce29582f3c385aa8034851c213bf9bc44305e8c2ce9d0426c0f52d47f898668646b514ab2e2241c1abd261b5f13baf80b1e12ad1db3ac609c9caa553f018f1c82aca475f1f16d73445b4ffc6e0af03ddbbd178f21b2ea40e8e004e68284e2e6bfdf40e06d67b2914ba1b37d7b590a626927e2284417b888da9411983677c8b9a3cdc905c1b0f3487b99b6efdf77200b432c4608c9a60e95fb78291aaeab4a716d78e4a90908b9af2bb5d41c91b1c8504dbfb375f602e0c7e6245dd736daa3842ad3728e903c9ff1639ecae40d84da513b59d4245105ce3f733ca2ecd0c2a5f5c399bfb40f26f34a5b0b0c0fc0c093fd450f1e3653cf6715ea8bbe574b90e589a4af9179555d90d84cfaa6c248e33b3861d9b0a845827b397aceaac4df3025020e3828abfc25392b1098b14a20d64996b7a540ae7140673a68a729aff5c71c43171ae7a044e987044b0e7612fec022a7365fad1d29089925a959ba125572e1aa947323cafd7e833a1240fb8c60c54bbee6cbabbf473679f808c83e5553660f47250f6b2c1d1ce08a21f350eff6a1fcde5b2f01b7b2617c1f02d9b880d795b81974970432feab7d93d76361d7d0629ed8c54868937cddccbe22850e893e16f4c141598eb6b3df9551745f932daebeafe446431c9c9b78f4934e3ff3106935bab1015176dec37e90ac3a9090e545a2c0e3eb17fbb623cdaa3128c6ca5c44c667ac244253b84cc94608b574847cc1c50b30a577d9acca6b5206965ee8d98975638255583331a1df0a69457806ac5e0c0444292309a29f7cc8598ac2a179a2cab74ce337572cec9f2dd8168794da8c1e413c58186504c28e4457d4221d47ae2f733eb66443d0f3d36a1ead85acea7582a623a8b694ebfdd8ea301bd5e218c83f7af6fccf70a93b9fd889e29a0dedf31a252af4136ea4b37daf433412f49066ac9acea577d76cec9d1f29ad450d1c09973a12efa1a9a951ae7e6cce2425d0f9f3c3101bcbe6896d7a829c81ef60dbd60fea0591ede6e2294de91b93d84d6f402995e416057b53ffddefdfad4510247a03f21c926d5560b68ed9ad467f38a93088a6b1f5da6432fa0f72c648a6a20981bfd15af212a147595d7e8d29780f6d0bf024166fa99f4c623dfa0c3db91250e9075c973d8cd3e324a8a3a68ef41981fa8ccf9ee84d02eea008c875cc9a54a8f64131f540b6b72ac2933b24ce2520bd37550f12d856023b4986fb2c8805e48727f6c2c50df9b4ebc415c97fc44aab90d2f27e2620c5005bad74a66c87cbbdf7e17a21b803adc688d05f25dd4c26832d100b630a8e3e7fd02742fc1c5448532deea520435112acafca9dc09908cb8d6e8601406902ed6714f227effea5408f1782744bc52213777e662c85cd023d21e50e4c4a00701b40877da0bad4087cd10c52681cbeb1753e7c101f07f5b2a214c693b9ff2a3dbcfd851db058a0437f05beae019f92d75f08dfc3635780c7e9f127c06bf4f0d3e43bfa7089e31fd0f0d11246d4a740b274c696f5ba905bd1acec8acffb452a4f9d5559b056b123a71160c24f3153101fe56a2c00d86999f07ce270a532e36ec57fac13096605f9ba6483d98267e4b17dc47003e233fa70dce6e57985e4b70cc253f69418488afec5f72150123b460699119e0580e8bba7af006da0855efe7eadd469015b8abc574c66a57ebd47054e920b19ba2f65fe917e4c04d4f82aa24987883437271272978b1210ad7c65cc6953d789201e3cacdfa759cd2546bb9115338fab8e3da5b008c9c5e182f753e1c119cc93286a6dc4879701acef12c0749ba33a8504181044339437279419559ac172089b1202b0b9a72b3255a3996ca5e294b3c4ed4809ebbd8b09934a4fd2bcd358378cd7bb62ba3653b70108bc256b9d77594c966ebe21aa06845d858dc500f78cd9e866f4decd8bdd2e971cc54109f68b37c050c28cbb42ea724b62ed0667715f4ee218b256ea18c4986c2fd41d36df3cf5834ae99602efa38c7ffbf7ea1319cc10535d8a23f6060bd671534b8cced698394164c6e8368a830e909f333d6c34c3855b19868db2d6f02602748867c8b908389e97b073fcb06570592bda3366e54d0009f7d9ae4a43387b880d8c90ebfb18c04ccf3e48f994674527c123fe5e6b71ec913a05f13691045c2c8f1e1a232e93bd890192216d8a7556a4629f1fa7740c957cb5032ad3d0cba3a6547d001e1011ce5059729dcf9566d2f69686f75601428ac9d2bdc090f4f454dd6424df78ce70bbf096f9d09d1e4c15a252ce778355e84cf79a4f38c807eb1feb735f410474d247af54fdbd11f3c4fd10b5e96c642c4deff93ec3a5c697cec747fe9e9325f196b8ee1c44c45890d36d024a54e7d7ec02ec49f1ae197b0c184243aa0f86c294d713198fea818dec4137cc230acb0834eecfbd557c9d01fb07f3707b1ad5b39a4f8e0ae01f872c5b6f53eecc4d058d4e2e39a5f460bfd38b3b6d0c60baad8ff8bc69e683ce2f6af3a2e4a376ee34713484f5f5aae445be9ebe88b8086fb9b7cdf56bf9ea0dac87d77c12a5d869296f125a44c880f84b1be8a09b037cb87b9397a310a31ad137d0501c18a60c094f6592f00783d3066e5c8fc8239cf0906bdd5bbae1a67fcb643c148033f5c50941acbff1955b89fa905d99f139a16de1da54b3cf4b1b7e3fcfa6135b8c6aa2c6fa123a5ff40c954057c6a81e39f834b7bf5d56bcb6fc23ff835f95c82ae9ec8859b4da1f0287939543618f5a78e7f9fc4cf044b1dbfb74bc67843006e3e0235662e2fb72d673187c1c61778fb56cffe7cf266825b6e5ad8e7fe87aba4594dce01123bcb66c6e8ca912fee0cc1d31702785323cc933919d1b7f7ef8b3da7d66add592e63e88b72b92a9177bd07c062337207e94924ebb8395df808cfb851092e96b80388a425742022f72ea09cc7de632dabcdfa8383340677a695adab58573568f6ae2a0e03a74390e8f9b992540566c46da30dad9d82c2f8839af8ec2eb0bedc2357b3720fbeed398abbadbf4d7d8787c4cae231fa0d8571fe8319942fa095aa4c23b1418bf05ca3396e70414572ef9ebae5c15feadad4e714e6d92a3096bad7888ae0e6f8c5bcdb4641579f5933f4d64329ae4d9f5582f0720657a2ad3a73f187f312cc4e70cd0eccdc18b3df4e7b067c0baf35260155f87f382eeb828725b2b69ed2968154ddeb3d23e2d3fba160f0756907a06919dd2b15dcc419a0c50dbefca6a4cb95adf26bd171e2035f05b5bcc7813d8623b63153b925ac8271fb748df44479adb98a765949f09c69001e0e0db7cea22419ee87058d34f8be4eea7270ae64d8adf13813123b077df887fbe521ac862800df1c7770f39aa9aa4c22450e7d5c9547517cbe79ddb3151e729b097bcbdb4bdbe2a9dbd8ec32f70100508d84036437501c586401463bf4c9d9b05effaab06e841dff3e4d5f116341dffb97a039bd5045048a7592875857eb901bc5209390be4d8cdab7e8d8496589fd3340f65aab528df322c722474120e0bc91bbc29c3738bf43aa8cdf8d9aa9b97357edd70c20606de3ad4657ae2ec85890c724fea20cf29171e4264adb4bad353d2d98967dbff66b2e1abc408467a945d6ebb009945e39f043a54f044205c9aa114c76ea1dea5d20e60fc638fa35df17a0e70bd37cbfb7834dab6f32ab14ff5c9e44c9b89a5c9309c21b9872e75cbf8664c2c310eea14ae1677dc549505a40185f599ef0fcc5ad7d219c45f6bdef9664086bd07931e6e751890e640f1718c42980744fad5d49ccf9dc0f3aca92ce0b3c336e64015071cb6267de3093f920d4c51a605feeb9bf6a31aa4ca475c8ed26452ac3e74e14496853b4e0f820f2572b355d23543dd0229286789c27348b3f35a92c664a963958481c49c0098bffcddfc09dbd1fef99274c895db2bfedcf61efcd4a849028a2ea1654d730344a1250e70462c9b78e9c6bcca7fa85af0dcb42ebe224a520d824f4c1a6ee271215102d9de754046faddddf65442c6b3c7107b7fc25b6de21c74119fb5bde8e721e218a786204f4a1c89ed7e2f88fa178b793834b641fc707288647c8dc891b85ba831600a1e7c84eb35281d99434abec69fd2b311b06af6458969cc002a3ff3d3a109d904395cfbc5053a37df5a9b280623d44fdf07d448e127f53fee46ecd0fff21d7b92a35120ce1ace0877826105bf893d4a4b913f5dd92a40c84af0654e2e85617c99cd39b13184f4a78926e6025333a1d61ac6fa8857f167fad90d527131c4f2da4be8c697bd9b6cda8d6b8201f1216a0058937426645b83f1462cdcd5e8ab71f6dabdb92cf2dbe5fd9dcaa456751506180db1ccc9c568011ce0b8e1373df908e161734e3b7da10c5a007a3fec510bfec0a380675b44855bb17e4654ae594934077eb0b8ed3469e39e75ef11e79cec6a6e6481324f4a7d9c27fab24c41adaeb0628da42c44bd97d0463f329bd6a53fc8e2067267aeddd6ad9e2acb3a1552c635cad2cc3bc4bd02bf2e1f42b64902592a0ebf4cbe0133ec2da8b178a9296c8879fb04e6604ccf26371ff05999b178fe8d31e5741b3a8b55186e78c099191428c21fbcfeedb85a6198be6fc830fad987a60f47f43313eb72a49aa5f51233cd49e6ab7a8700698cd48d4c13687918d11bf59ac95e3aae679a3043d2ca8d99b4222bbea6ba416b0c492ea4366a2a228f385615c06ec739619bb035ed3225b894813b0812ef202b716ee052e06514237769e61789d95a26b1b0e58970952b1b24e1f3209acf8cde8cc917f650367b4a7503376281e51f4a1a3f7a87249354a630671a230c13cbe9d57cbc632e3b722371e61862b403c9b1048637bf9aefdbf8c6529db7102cac362832b652baae02324d2b9d0401d750df8527429be5e89f5208055410c316ccab8c700ae4a06754d5fbed5b6e9b6b4c8245fb59019c550ea648319c9bd47272b7b03e01472a018384251e1cdb58f040d9c5241f28149b70c2b07ed48049ae55131ff1e306881c068a2c198906c82839d8d4d303f215d0f501de5093d7a04f495ca82d54b05141bece06d016a1a2b239565f36ac266df1461559b2d3c34c68629f62c60f481f9f325a2a6203f881143813d34e3acb5a3e9fbbdd8a4738aef0fce663db40a218522b298dfbaa8a1d102cb6cc12b64f77acfb196e38c485d627093dc889ca124033e24d1a33eec2bdb634e60b5e0395f75d8aab193b4d290769a48d94e740ab82c94adcf8604a40805bbdc52f8fbc2a49e65a789c82ebbf55ce5c68296a653cfed5be44c5016e52a20e324013153cdbe009772c9ba351b34db65c9a99edcad14eb443fa1910c4a2d802b08552741711b8dccc9324448b9fd2de9244a9a59ea7594ab516a97fa7a67480a6f52c662e3205adc9cae4d12c9eebd296224ca5d91a07aeb651192f5d794b88a45bc8bde218d94ba92748e2549e2f846fbc31c582b7bca270629a3c2e958ef9bacc5ef34ec1813689b16e59c45464b5353a6683b44597b1a8845869a04a42c37e3ae964845b0c06637067a697f4fad40ae6a4f1398030a8d048f1ac82c71fdfd08d38f4f7fa891f64396ef8e261bf51e4ee4456020ec94c15256bbf3d706ee02f0a01d5c1ef296b865fa9dda39eebed2e0d394fcd9c3458280c6e730d843c49d0fd65bdd0dfdba480afa99fd3e06e29903649b9c1a2e2413d2f781550659cd74e3713ff92037f26ed499b1c2513777b002341906c7d88859a40b739ac1de102ea56a0f17029ebdfd645ff609b272413d8ba369bb97d5765d61c4111476b4171ca8c3675907df9e4148fffe574c1b34582f138418035a2ee2745a300cda4a07cbc0ff1d299d60cdda49244798f1b69358a11b08a96cd17027c854a084fc4a6f62cd0f787207e03e73fd68570cd3f14b01e1f8b917ff2d0478bea9081e76d32bb9928bb82d052184eef8fdc9de9b6cb9a594324919060724070707b928f42f440a3398f5a4cf83b4277f1e8cf120e8629607b18828f220569790071d162281ffb917227d0e839579580b2f4f79049fdf983004992c22752d1e0f71c2e37bdd0d12992ef9ead4765effc3a94bf92de82a280889153f541882640443d6b400e5ca51e5e1bb0b99ef13dfdddd34420a9b05786bdd7479eb4688b70e8648d663a4a1a0cf4f0dc650105158042183104a8428bf3d886b856fff6e745aac81f103140f3fdcd982b245070834df413447ceac302fa8b0d65a22cff6a0068431f44d748027a755950f4768956153e5db6d586cb47cfbadcd913642f800435f893082304458202342396403238820536641c162e44836c515ae8410cbf86024009dacbbbbb91011ebeeee4a67bc2501102470e1ad09d2f440c3f3f0edf6c5112275d27dd6647d63f540e507962f22fc50c4d797198448a1c508293144409921cd51fbf4e9f4dddd7dc3f6c882488ae21a313c48560b87c301f902e6d2afb57f1e0601be3df300e49b071ebe9b072476e0e17dea483df5cf12cd7eebd9fbbdeca32e29c75039f2018200b69c1cc51c84900e9a231ea7fe412df2c16288231f2060dc51cc419071c4335ef99cfa17a35d099d07fedb389224eb0fdcb7b7fba0543fff731d3546ef7f6970142b2b69da1a2158c1ac3144299834da7bbc5d606443558bc025ab6d07e42d4713cd441f0950f1e787688d0a2605d46b1075cfd00648f3ffdba4f1f3d42d68a229c68e0c538c1d19dea816f951a560caa7b30cc1eb2985aa514ffddaee7a0bbc2aef5e5d6b68833e8cb6d94420c41c1dd0f1818f26ea320fd5ad7e60d6e800df59e79fa0a27c7cc0a30333064dea0e680d1d933ac96892a77ef3808dfb75a456660cea624ecc89b9167362ee69cbe841940a7bc51dea2a5efce98e46aa6dfd513346cd5136622ec5cb1aa83d83a28fecc76bb687f141332bc9b7ff7483edd33f5d6b183df435eea016b990229a49232c7d775b3b2b6ef5f1e999f58ddb7cb346c29e9e7647d3a75823771eed12bda3cdfd48bf72164599ccdd667baa407666b1542d6a81a9ac9aa86ca23540a09ee6a8ada7d80cce7a5a3b4735118995a4a89a8884390c97ade4a8cce5a954dbe814a62ac526ea4a568fc5623c3cde3c6ed44d33c063a526c25252392a477577dba71ad544569a88c769057be9eb8dfaa916d1ab326789b2aaea7595eb255aa39d704dfbc9e22a10ad61976e57a7a7d76da661a9760af6b8e39258eaa170b5c84655206aa5d013668c965192e0f52496c25cbac8e6b450c730cc1ad6696ad2a06aa2a7ec978693c291c246a7a7220c76cd7491575620ea596bb4b6b8babcae1cefeb29f59ba348fb949f6819aa6df8a902516cc57a3836f58e86a9c63b622b166797688d5c2d1247fb54819c688d3ae2244a41d2d3259ee7f9ee91cc71bdbb6b77e81d861f9e9a3144a78e93f0532d12472ed80aadd123866152409dba9619833a8981d4118f6d44eb524d9404535a6aa21da0f3c8384777f0109fc7634e5341d6d8080d139914500f693887976838096ec24e6da358c993c04a4f1d86ab45580a86c3230cd723296229d8134fcb8af830beded531c61763d701c2ad158ed8639eb44e4b2908b43bc9a4a1e2e7c5b7eb5a6ba551d7e7743b5629a94aeb6dafac5a556f371159d75cd5724d13cda6b5da52759d9eac66de5a4a956bd5a294525aa56ad107746ac6b02ec308b1675413512f7a7555965924a5a232dc88d455fc1d653386553181ac5bb147b262d5b1674dcb59a7f32d094d6f49daf5166c4b2dbed58ab33c1f4c80b573cddbaf0f6a11a59ad3da5a6bad36dbaa451b176ee1a0ca53085219ccb4a995dc97ec6705593533cb2056be67ed15fc9c344c8099aaa9aaa2fec455cee9c662a5719a5cb3a02115823c5a5655a05912e5ecdd50f1fde1aa7aecf993b340be9e6a393dd9565ee50837d029fa88e396887b2b0315888aa1aa9a3472066a117daa4034b745bd39b942067447192793b513eeeb40196dc48e610251e7b1a1e2f3d854330675a31e7d10c976ea2d644eb271ad346974d58c41b9868660c0a865ae9001dd116c6caa0a445dc60f2218321d9869b47ce2e1a93d0d08814c5556eee98f5b620e974b8813d75769fd7ec7efa0ef854ba9e4e28a7a5cce399793463b75aac32ca2546ac23ca54a54429e7a98234f9d3ed5a2cfe9533bc9f36da681a86ad137760d5db8ad5e2386a78d483615552da23aa8a1544bc5f7484b3662fe590a59f399fae73384803df14d558b606316656e6bdcf8834836ee0b9dfa592691794b36ae71b354b2f2d49b4b13f553a7b86eab174444a4491d531898d4f59356e26d98c1f48fce19a6ce097e2a607af6aa94a9aff95906517a1a6e307d7a8fb9bb3d7b306350cfb3dc54b588ae40e6e92c715c32a5d56b8a6b2272cebc4693284d05cf1e4c1a3dce6c60db8ce1156822183428b6da4d152b22ceb2d3254fd225eff98c3a3d1dc1c6b9a2a70121fc3822c56faab6518aab454dd554383ad6a67aeaf474449750345c3e5dd7598c33c6b9c3d76275187b8e3fcf2f2dd7a2cf2fcd83ffbc03ff8da4e7197bd8f3e87b9e7b38478964bedeb977ded98862de7906459f986787c1603c3c3c7ea369248df4f73e30f468f3431a8ffbe058992805d93f1ad95bb0dae4652bc54ef08a78bd873ecff31bd4bdd17a5dee46a1a1a18e83bf9184f164e7414d542790656a618cfd463b1e3b1a693bbfdd0428059956289ff5599b3ef1f54c6b50bf794a0cb23d103fba727d56c75883ce617ea373d868495afef19607fd3c624a41a5d7d6d9b89fb5ef7cc2f82ea869165a833e1691cc6fc920d1f6d90b82a0df6807fdc7631a69c1ef601df8de48b3500aee5faab74fc38eea6bd6773b6b40c9891d8fa407df6e542dcdd22e32be64cc913147069c1332152850a4482193a950b162c56c26a34e8b162952c8642a54ac58319bfdfcb070e1c25db6e2675e4a014dc55c8162f6d334b70dc597a28bce8e9122f83c0b33b09f678c9f1bf427d480be5dc182d622bcf1034e9a8bf046ffbc2dc09fd0860b9f36e642f47111daa86fdb86e421122d44ba127bac2f5ebce8172fe88b7144aad55e0460084900e18d0eb2d9808006308049fef83c634c1f4100d4e88b596324086f6bb510e9bb2d56bf560e1d6760c4ce2f5e843760b88c3a31e468aad9d3ccc9ac6905d7ccc9ece9de4923f5b49f36a459fadcf7ade06aa2f977f6d4368b69f8d164ae8a10c9a62157179b869cbfaac175d06f5c0747d07f7cf61cf6d17e7ccf9c500ab2f58e8647db3717058e48dd8249e33651a6301fb5c45976daee6e447fb2b5b4b4b46a0633beb29f26eaadb6792d6a2e2f4f42f4d6f26c7ddad651dd488ad78ea4185471d7ea8a0f3c26f175348faea8f97c642f389df27d7312b5db39aa19d588d46db7c5b7bbbbf1ad4e6dad55566b9d330022cc490130634ccfdd3fb3986fd63e5a411f3a2f9edd7b69a55d5bc759ad467d9b4e6b63f5daf7f24d74f77aedcc34d3222fcb68d1e7d3468b4037aa401fcd4834aa40a207804983faacce49c3ce6e23f7ec559e51a781fd3a1ad149031b19e10e4ff8a8ad183ba573c6c0180498368e12abedc027315d1e27d01dc05fa1bf0b2801ae182700a31c2891bd2be606404c3846895fdd95af0d0fcdf9de3aadb578bce3bc47767647d53a9287248832d995d5a29087274693b14148eb0155d028f0d5e7ed49ed91950147ac3ea62022d52f9bb174559d3b08a6839d2767d51c3a47f8408788cf747c28d2761a682635d07c1b45961a680a21f6fcc4a293c3bd23c67e62d1a9a243e5195d8041021a8b4e0e9bc1a1889697214f5345702978ed90bba2c3043784111b4b4f694aa0fa92247765890d25a528466c10c161a921a1898d2155433cb121442aab7462a304ad0de828617d55cd1388267e6289d1e1c9fc02969831794964f1134b0c0b188b38fb892506c96725f2fcc41293f44535470e8c9f58614830c72b97475cc7d93f1e3ad5449d53fd9cde753d775a3d0df285cd534aa7a2ac48f1f0c03c77eedc918a727a828a59d0a3643505ca294793d0a630c7ad04941826da4473494a4be09089ac1cda2c3d4dfac00ec57d411fe4f092038391628a1c16e4e4f0608bcbcb615341268817b83c7040d69a5cfba12ac10f6b1c8041d9f1b8185830adf04f4d0c13d4d0dffee61cb2019c59e1cc2da8668361b1cf8c26648280f1b476bdab160177eedcf1b13807786685332bdc8fe28b6f4ec08fbf238f3a3b6bbf693f3aea33c2376b3588b479ae931e893231e726a2b44a29a594524a672c889383eb5f06c12e022d9822d2afafea5757659995a5a55565b81171188a759bc52ce69c6f36671773eebc3ef7130e87633a9a8eb7ba28a88128eeca2975575dd56fade9ac7c7b0725a7ebdbcb8ecbb777546cbaa92672d1357549dfdee1c84075913710ed6aa596f54a09574565bfacd795ba5197eb6ae127dc84b38e48cce5dbf114b6535add252d33366ad6ad4a85b5c85c75c156df2d70144e22f5dd7ebf9ae8e77a7dfb5d33b1e6a4f0dd2ba82e549bef9be4dbaf5213c92eae8beb082a299cb46bca264271d56ea5e07cdb269bb3f3b5e5bb76355109af59dfd54ae99bac53432ca9b5df6af876d28259fe560766f826a79ca822bee79df7bc9d92813949cdd02c2cdf5b49bdd43938409a8a4b775b7579e5f3649b69af96d3ed93ac4a2f7c3f5997eaac9a57f30b3749ca45f6d37777fbac4ddfeede224472ebc25984482eeccc340b87fb16fe1322b5b0220b332c707816487879162c9028f32c8e38e259f82c4462e19ed4fff88a10e9c70ee0674750fdcc558448b3152e0b9156a8b8f32a3c4588a4e226f1752493398a104956fb14377c0a3f1122a5f8ca148153853b4281c27d422414e2f827e434fd093711229df0f13c8b42241fd184115ede84f784482672091743a412b6d64541413d697ab07a7a5e7c4f8f8b4584f03d2e2b22eb7bdc8da0f23d3ed77c8ff384483d4344118329513c238a0710c5a1177d2a79b182174517e1e4f0a2cb4e78318e9d17dde615c38b1e5482a5173d16228937f1793c0c9178ac8d1131c3c71c0c916201a153da8e9897701e1a69c279fc133dbb0e0ffc78d1e79c347a88ef39f5ea4da239dfa87ab4ec301a0973f0619ed72367f77acc32f0a7383ccf79e8e8de67ec91df1b2d68db90d1056dcb3af2c8233b1d41ec94c7c9a01d3ceac7fcc33011d36622f6bc2c3cea831ea3e188b9e8e3d867a8009f1187e719f699f0461d1d48c3811d741b6ee0efe344ce578ffa9c317c9cc72c037f98cf878d382c685bf5fcf9cb307f133be68c41471ef9b38efa264c38ecd6361d17cc18d5c8440f1ddd57306354d7719b31866030cf43278366d8619e09186d6682c232cccbc069308ff90c15001b7164cf1f236fcc2fd133f6c831d1fdc5b147ed5133c84f9d8746693bb07f238ffa7787e73aa89c08dda3c9fc044dc98f4690790a2a2948ea9f83b058fddc9fe97b2349a98fcf309a8e9a4176ea39a45191b993adbf954dec7cf5a126a216ecd04145c98f94fcc8738fca092a285c46b31955d0a3e9f0c08fa782c24fd04640e1279c869fac6df5e7a3050dddba1ecfb37f1e6de6c5f01ed5cf10e6d166302d1ecd9bf2b2fc0ca91b75e73b72ce9e471cd8f3df19752fcbcf6ce4f760786f8af6a8241e7574b3b7638ffa748610b13da87a89169fd0127dd569a80841c2979a375344e61c39539472f08a3215441234ddc25587e98a84480d304980d85e413624c704576a30019c1d56a081cd521821a40411a10b92322abc176eb82424510394262648e29405c995365a7408a3c31926745cc85c51826891320526cc2cc2083744ca2d59547040f266e6b5606f188b45b116cddc754c90f8aef349cbd8ad753b55e78b0d288cc9a1ab841f3354719284a393041ba8a3b269c8779e54dd61e6a87dd70979891d15bb2ba54d0d56253cd1ba537524d6a207dca0640b570397d299a36cc97c97e32614479c6f71e4ac9a17c8423b7c9b89e77834c2e002eba39d47be5607bd560fb2631dbbce25c9ec08652e84c7f571df1963a44e09375a623ce0c081db93b7bf3e8bd82922c48899cec372c71cb7d76264e9817e9646aa7cfe591a39a1fc668c113d21166e0dde2ee8a27c349d832e8994dff3b35c5a23fbb208098f1dc4eed86d411ded07dfd147212c5994fd2c89104144abaa6f9873ab8e48cef2eb5707698d165d106d3e6eb76c453edd01dd7a4bfb992d76511055130b619ec05046ce51f59f269212c9af851541d3d7fd5489d4ed639f61033ad0b69fafb36d20ad41bfaaf966ad6dfee336918d48fbc88e648ee6fa3aca5c10e79332b10219f54f0ddd43be598baa5f9f209bbee315ead341a0944677d55d753974a5d75f39f75eddaa6bc3bd82e2eed862cf4f392aa790a1f2534e2147e5282ac5c5d5468bd20a942ac855a05441915a414e4b08ca5a6badb5d6363535d55c6dd26aaee6ea207aab8368aee6aa545555555db854aa2eb5aaea5e4ac7faf4244485ea537daaa195eead9deb5c17b9b79794ba48e73ad7820c11ee37a77f73cc9da5a2ca3235b350613335723596d4c8d55852a3a926d444946571d26650d2a5a253744e2f23a32a9bdbb24df7da243667973c4d7a3d986be604a8072318c277e8d62a343444c3acf282c14ab8b59fb793d97962e29773f6d0bf9c4bd0643edb66e43d6e5461220f8d1caa0a8d4226111cc976cc0346f37ae8efd08d33b7de86214f8e812e446be42b4429b830999362f7e08d1c9a4143f7cead871d11bd7148c80a75a390100adfac2d71d94203f5413f6d3823e7cf9879ff698399acff3a7036118e91d845bf9de70d5a3b923c6ce865c7a106b0b7236d13deb0fef967bbcaaef7d0660532aa31ea61ee2229d7e7ea337b3bd5ca397f341c22b5504e848d5cdf47494b8a19dfaebfcf23ede89451dfeb51dff410883282882b3e1027992b499ecc95a44f00063e6deb29238838de983f9dfa6c3a11a9d7376bd57f1cd9fa659c7327f3bf30826eb9c393fe8c016e471f57b0c3461fd6dbf1e800eadee8a33af50ae49b35f24ae192620ec48f62e3950fd754892e359117133fa7ef734e1db46957fd3c74fa81340ba6889f8763677367eda7836ef039cc3fd8e7f341bf2112f881df375a3a52adb64db25a7596671e9a95aa964bdbaa7f14b4483c32a6945a1df471d88d96aa6dd5bb9e8ea4b5b2b25556aa02592e966a0791a45a5c526012eadfd7cb3363f43c422479baaed3c0fae737ac7f947a97ddab3f7e34aa1d6d875a7fe38eefb32e68918f243847245db80b5b7ea947319fad559b3a91e6315a484724bb37ba00836eab32dd59d61556e09114c1d563da8aae155c33275632c64db5a26bf684674fd746c13e180c36929e5f918a488a1c7cf6b39f1139d4a117f3783ccf796261ad2192f7d9d0adb5a10dddba98dffa17d26e3b48233fbf9de7d6fa3c56264c715edf27d02b087a6c2461220c2207356752bc437fbffccd180db434d0ba966dc23becd7ba28764771e826c2f81ba3d5300cc31f0f822208bad725a6b81b2638b3078ea4f8dda8e2279d1e3c29936d552615dcbb8e93582d19e38c4712e78931c6184b055d96ab2ca5c555a099b15c5d6ca526d12fa0a192430984c0512335074c1839658088214dd3085263605e993159c418638c31c6f331c618bb80ab4033638cb1956c7d8a6deed4174ebc34fb9d2125de2ba781260730d869204feaccdfbff7de8bedbd3e6150fa7befbd335a2881da160ee3768107bdb5a0f1b50e5a4a29b5d6eba351a893cf067ad6d01c98d1e48792346ca67409a3c58ae9071d7e4cc9a1882c31805193e54e95333ad05862822a2fe454804a530587c7b85400149c3425308571526786139e9f65952e5865edc9dae7a8fdc5b45a0c8ca874a540c7da2a5bde5a6bdd5a5badb5555e786bdd67c613db54ecbc8b9f65952455828ce0fa2c03d8539ebf23697d96c14cf3414f623cc349c4d8eddf71d610c9f2a0df5daabdd65e5ba7841dcfeb045a71b5d7da6badb5d65a3b9271300a493e003fcb146e48c10a05203aac0c6547cacd2670b8c589c0d5161276f84213250a064d26a8d13ad3c52486890b53d58b1c6e71728ec141336f15130a20d34773321154411307c893375d7006b093c4cc102d38227c3952027be408131e0e44863092a5240d982d2dca3421c20419b0e8d839ea8e647d77848d48c6037f96506d32545843f016275719610055862a89b3e9b0077f3647ead3a16c58aa7995505cbeeb6a5e36d37e96504fa094b29d3d69125a1c54dac1bcae9c856210edcf126a04ef4ef6b219d6e16bc994279c00ff2c4ff852e1a35144ea3d5e7b4eafd46b90a61178223357506a816f36d1bcf7de3bbbaffbbaaffbe677bf1147bbf56ac3fad4f63a7bb976edcf5a58adddb45a1f9d670ffcf8aaa3fe1d715487517c04881268786e71728b9fe5094c667293179fda84c544beb6368b9fe59423a6fc9026e6840213ad1eea0451e1cc950b60c6883083d451d704654c3e8331ae615c4e31f218638c31c6425ba68430831132883e3ed4a08317295e9e9c55425562accae66b6b4b997adbcf72ca95294f4f0645c1526dc55de76fe660b132048822ae54c10202a4cd10261411e7868cafad52b04891fa9642a58ba78e007e9652708fc99840734384899718de9410028d324c99aa375f5868ea94b0e3cd8132bbaeabd382d2094fe67c29296dad09e149112a4854c170c4d4c533ebdce2e4173fcba7328f7f964f644cc839f7add5d66aadcd6d445861d42c11dbac619521db6aa565d769a35eeb5427525b2304e7fa8553b335384f5c66882092414143375875af858c10b3cf0f41cfa0d37003d0b397fd0369a1675a06a0872389a3739883383a076938a87ff373e76ed438aa7bb9a38f4dd07ab234e1eb7faedf70031c5566c3eb1fce007b3792382ec6816938b05f9f21aed5418a8da8d5e4585be3c02dc7ad080edc6a8ffb38aa8360e739bbe75f9e25103b9fdf7ef3cfdc833e1d6d8ffb78c461fd8e43bc2a245544683549a208a7339e181c0b66d2dc995a1204093250e3045f4808020e141166b8ea2ae17051422748112460a0081d303ee4aeb0a074316900852e2430e9503546870be4664ca162650810282e27a047e808f9c1871d5048b345e668fac480cdd4af5fea382e38d2a6948a24504a13040c93285da264518a0235bd37b71b93120f139398953a021532e9a163b255a2b130190166a2c6d2a46b6b4741929de69bdbed4b9690490a22932232392b961061c39d38260071b3a509072170544dce68a9a14d93252e884bbc1020cc12269e25527a6c08a26acc92281898bc1113c70816921e9aa03113a5e44d4c091d15605a00d36469088e0cedd2039210b4a8b06507255b605602b0bac5c14aacc49434f9ae5302f55dfeee0b1c99122026aa6e71f2f7b38ca26462879b179ab35259426f725e3a610b105e6fde6c89e10813c25439428622565c5e0d5e95074f727237b450cac91ded70f244a72cc2c40914906fb7659420df9ea1d849a36525e6276709068a9c92830ed83c19a45579986f6eb7ae282357554ab0852987b0045348d0d08406ac21921c8125795283865b9c24251c6901bcc5393243ecc898b062b9c5c9267e96507290a334fb594279e184ef6a5e2306425f44f0670985c993321a449e9f2514a59741c165153f4b27216857ad6615a4e6450309352febd4021317b4ab565b2148cdcb3a355921d4ebaac96aef49925ae65d87976a5e5d76d029a9e6653d143fcb2770644872d7be1dee3aecc64b52171c72261852e6044d475278b3031036776ef80e490b5e979b11beb6f613366acaaeebbacee797ef9060692b69aac070f07e964fac722e6690f9fc2c9f484d71070a9ad99be611e8eb8b49649dc5db136fc3b77e6791e77962983752ea7402d119fda6b507e8eb96189678790bf441288cd9debeb22591cc426c7a86900bfef50ee664d7751decde194268776735f2feedbc1bede30bbecd3f2e0fee62a5dedde149e66e847919c4ddd449d9f58f0cc1db4c2b52bdf7e924bad3c6aadb78ef12f169c938a06557eef26824c445ec7e964e5a4fce52a88b130f4fca6cc941072efe2c9dc63c19a455d958a9e9dfb76f2edf42f4c07407cd1c252c6c8ea69692254360f84188d59da3765950772ff94aa39e0090d5643f7e2449ee49e84dd775b4d63d0a0d81812dc5cff249094eec38e59c4f1932c392395496b8b152df3edbf38f2915baea248979418d972d4f86226c90d0300488244c477309961ea43081612a923b121a22480a53998c57f7b694284c7dfd4aebd3eca497b3e78ed6a3eee0615d47f75dd7e3019c57a8048ed5553909111909000090006315000018100a89042391389406a2b0b90714800a749a426a4c30130743418ec32888612008a218038c310620640c320a19950d02043759ce9c6b0904bcf5f909cbd65c7481c2654205b7cc431d905cf7a0b1ac251e5f4c3ba8e83c6eb8c8e30831950f372c0ac03bfc9392f890f6a39d1546cb6ea70a26f746c1ce99a1a830b658290af32dc76053f5b5e173fbb0109395ca4b160026d2de9ea58d9b32e9d65c28f7f0cc2f6d20ac224b8642085c140908a60f39b8b0e97b61af82ed0f8696c6d98210e2e9d4fa865c9a159d58e6415c969cae8fb5ac8a0e7a52e1bcdc290180471e28472e33c3f82f04e57ba58a0d3ec5018801479ab7a847562863c2c2c3b188025231826125a9516128ed36181611843238f8b3b59a80d5aacffde539b05d2104127ff964a8be111385fcfe4e34065683b6f51cbd7f4f1b48100aa4e581a80021185654809e170918192093965276ac45f0dc93004792bf39d8fbfbf506d47712608edbe95243aeb20ff80cd57e49b929a848ac62d28272e50ae7b0ad1d3b39b1bd8b7cc7cc48b4a7d6f7fd3a05e453c235b4c341bf4cab861dd157bb66538d99d642305b00b899d6b39c6955c7ae76933b92b418b6be169322c74484cef2d4a3311c38d2f77ecc75d2b47e578f171a11598b5700a47d8f867b9801d0c6d5c0b54c4c89d41de34ccdd3fd25f75d09a6f1dfa95d7c4f24f7ef5c7bbcb036e29e2683b478d9ce96fe2457c7a189f55723afe846e473607a4d6ea524999fb75d75c89d4aa02e22c8328bfd7923f99bef705981fcc22d2b42725a1cae42c25cad0b83ef047666a8fa7b0bab3eeb178f246b2d684db38355e6f6726eb691628308c0a72a08e83d1c56555f78792ffff171952e7e85180ead0af2c022a80b079e3851646f2ca51fde564d26db8599be577df87cbda5bccda0a6855b6cd0c540161e1d3ecc00cd7d7cc855c4dee61e3f04ce30b9737b702d7523aad30c700d65776288e4e4eb6b6c172ce43ae534952cd9a63232ad2b65cb260c2b5fdb7ca9a5bfd7f97dfb7138ca17b6a44c6cfa6553d7cfd78ee5f38c756f1b1af0a356423615a197961c425f647afcb00649d493a87a4cc8bf683df756bc5e59dd153801af5bd9f102f1578b5cd38919f45e3bdf723132962a3f544824f1df6c68a2ae620cb821e96e004025e5484f072d52ee597f59a7f06259b30baac796fd7841d5727c4e420af9b1c2adc6d9a6497e770d762496fe6dedcf58029ee0810fdebedb47a03fcb648245cee0b863d2ea8d664be90dd624069cc701cbc883e13f728b454031085629874adb5765ca39e8b2473eccdb76fdf7fe2c1b7c9ef090dcc8714e6910b339679873198313f6692e470a2b6591d4173c1813465969d40f32a2f6303ba7a0d470f4199021103bbca1db19e9554aa71335ebe8d8f7e2c08576e9e7da245271a62efe396a6f843a642e74e8699634f72a1ff2ee5d882a6524e7554ab3755d2411add001489d7625fa78d15fd22bbf94b1e7855b80387eb2f82c529f7b43dbdc7b31c7ef8569b492bb33880135eba6df0713d60a796a0088bcdb45c38a82efe85471abe4f283f0217be852cf32ad5f62003ee16677d549602a0c294791dbd76cd6082c17241d22ed8b064521165a80e3648024a46f4dc7a4ac27889b26b3d6e50684677bb3b747d8619ce40041f0b90403998066ed893052251fdd28b8e151acd7ba16eed5b970512a363fc4ca293068cde17df822a8b024699c14364a84da38c07dd1d32efd77214c231ac22c6956d589fefa88c49304fb5b69623ba22afd4c4cab51e8c14ba39b7ba87e43941bbd445a717670ecb96ca1b27e480571cd3288abc113716a167e168a3ba28e1f86bbf1862dd873b7aab4ea423ca4697edc1dc1dfecb79b263498edbeaa0b5eee480f8cbe6bbd0649a84de084c00c47f6683306c773aa85473e80a964024c30a04c800081e83190ff402552ae8b363bd364077c1da3221b0ca23d2569ef963d3db4a380bb249640e0fce3c776f40701581db2ae8a9d8a03940b1dc43bcec7e8cbbe50a82d05ba20f09259262436c8308fbe5db0ae5cb44b94756be3d2a4a2dfd3b106e5edfc57de022b0e29c0f643588941bbafa793ade209611e5974df089fd4e8d5c165dee0d1d01038088c6e84d6f172e4886cdc605e549f0d9fb40feae386dbc7d3ad9e696663e50b5803cfdf0ff5f4d41658bed053865ba31627fd22ad11371cc44bcc8d115d1e55d79245ff584c9ffaa98a48c2a8451d198b26cdaf60cf72461dead2f2e9f9965d06bc0228f69b82763ca3b594de80204456de8d671aa9c5fc7e581f940a7fa9d03fbd30d0bc38272ff15f0d280798973429149358fc8ec78acd126a744055c34eda8da0a5940a4e70bac84e1adbff3b8a76bc2ca4a3c432b30e29a87939cace2689006dcaf9c366dacfd1fc465e55e7a5d83a8de753caeb01c198c0e9d4aabcb1aa5b4156dca9153f6275fe8410a328b98ab1dac2672a09e0f9e416a88523955f78b31862bd06aa81f641e919a5e522bc11d42af53baeba4d8a2edd90c48fcb4e2cf74e8ce4674178b89e3880bbd87012d7daa33eb5d4cef5ec396a410baaf811b1bbacebaea9053680d9c3e111ec6981a26d0f87694b23971f49212afe0859753765f69723ff60c00f4aafb4f8379d9a882f2530abf7707b09b828411c02a194bc3894cc6a14d1bf6ab3e9ded9438b58b0e703906e141e11fb98b8016aacfe0406789bf060c72b5125310c11d18b9a9b83cd8c81f4634d7e621e5609a80f3bb2e00a35bd223b04f4165fc590682ececdd57e9e5063ed4bd958fab95e9c846fe63f11a5d27290c17c4730d6736dad0a6a7dc4a1a8ca1e60f8e94467bdbce4ce9970423c1300be3d8b27a069e8b521087e3ad1c78bc25d728bb71a82949e6182e540f75025c4c46d366d9272b89c3fe06d922c484f1ec195ace85b874f30c447935b891d07bea6aee4955887df38edd77be8c4d80e44094a16de801d3177583a4d52ac29de965dadfbb35512f9b956d93938203ddc200b6a14066d4a6b07cfad3e0c7d9ed5d8a14d08f849998720c6549b027e46100549707f7bebdcddeb86f37172a7bccd9cf626f760889ed0077d0513b49fc0b21eb5ab28c6c58cfee6ce78cb638a584a2734201cdb4061dc06cd3e82c3f624e58ed53c089267441558a3e387b2f528dbaf66cd263a9a12938859034fd29066072372e49f80fc3b21e6af4ea5e063cec0658a866dcb047d9d05f615d6c10a0a8f437807b74ecd70842aabe06051960f91cdf2fdc86824cb10318af1ca034015f00a20feea6c74470c41830d4993eaa0e5cf95779cc1e1ca1a1da5664b5f62c479580ea432b468a8ba4d6f133ced7bb3f116712144335f60d35dc6a0901d04b35ff34824d34c21ca8db918ce57cd403b540edb5ebf2f24c9f40e4c5fd4785318577d418b6b7067d322f381aa54d749e01a2298e53d079f0a38499e9816a3f09d1a1a27d273fabee72c621b87e907c8d09312ebc86b7ef74df1db48ea535d032c02182b8836b187d8996fe82573c6b778036e137ce82330363110726452dff445148173f181f0387c4e8d7b39fbf5746c3ee45de773d2ceac3133776f6fcf26b0faa0430f0ce7b59d83a3fe18a4a682e4e37ae7e11891428f8c5033646c78c1a027da5b6ae9110ba2a44fade25660675add24bdcb0fa8e6652d2edd22fe5dfd9d8270ca877fe9088a5e0b46e020c3ebe01c7db0b920d0a68144f342745d36b5cfdf25d7bde689cc055e99020c22419a5431a467624beec4a6176cbbcd36083137dbbd2fb7a400c38d983262eb330d75ce34e8b3efead9f52f5ac324d138a61432b5e4163ba1704f4e487242f72354a4c78183cc463f8378a2b9dac09bb7f3393f933ed97158921be9fd59918e1f31f1acd582463e9095e0d39dbb265e81cf77e65f480af3f58bd0202f020998c52326aa2f11282a86527287582bd5bb443e4474491dd2d1232a11db9392447213d2295c201b458b739719b01f303f42b6bc968b49dd96f988418d403e497c40c5485eda287887eebfeee89881cfe44168556e76f0481fec4cb59afda220bae47ea7a7e0f1ab0bbb687a1de5f3006cf279478d19e64a8a7543a9df9ae08ee21b956e85fa203b657728802d862283e24b1d7e42f408ce47f5e832283c026dc826506352d231e357184fc7e2218ed53616481fa5d2d7017e6801c406bc26c2b025c70b2c2a6455dbc94fd693831fb99c10c5e489cd15b18cfdba45cb98bbbe38403a00569db12ff8cd65dd771c63b6775b2ac4204e8ea49ef6b1391dd7c5373299cec29c1c7e8151ff8c83871db75c865d2fe0b356afe6f218b9b0041e43f3bb0fe2118dd607a4012aaec9e357ec0bf33f00e3ca001e8ef1ffcad037e3c0cc11d7cc9d37555afe582efa51033f0ec06e0f3e9d878e34ded4a69b44d50bb4a9a2ee771cb97ea0204f9e1a72da7fd0cd6d6868cca6960353066fec53caa32e0d40af76dd5f649f623db8cea0079f34d5ec8e90b6b926d962eb91ae3befb680c6e4bba3cb416b0208c84eee5ff062e64bd3f16ada1c480c56eeb7161e4c11ff156f568e6c4876af6bc94c05e5f6589ef421b64bc2e9a0f1e0db4692a6bf1b86d1f4397df5a66dadda272babe77725deb8238701453114fd4b8da57ab027e1ccfd8f6b225f109aa90863ddbb599ac22b9105ef735369afea56ca108b7f3abc9b0772e621458a18c1f8fe7e236e29483a46888f6d0c5ef64c95fcef2247746332d862e37737004998ca914436ca3c3f06e33a1c45a8bb2e76603b38ef66a66bda3c46d530ba37c07b214ab5af90172484f4bb99f7a4ceea2243e91df91b40108cfc3a8ebcf7efd277f6dcb2773dd3c340587ce0a62137d2b079663c1129d25bb117ee701f7aa20a03c817305a9e657b5fd4a2fab7843e4f8080ffdad3dea21bd9b614075b021ecd7515c31a3f07ba4a18ec455bb9854e99c5dae071ef3bd24b7e133db7cf07ba5379d23d1436b53193974e34233dd1c60954a9660fd1eab2896bd283d20c1949b394d16daa1927008f54e7528a1e309883e0094a7b3356da35b9029faf00322c05917d3925a4ef31e8ea408fb71f558e05a6f7eb10238415f23eec31f3b159afe8bba9597026f395e9fa8532b9121df7c5d2692a319f9b21d387e22be86bcf4026eb94481e8107c56fcc050fe1c84d2c201ca6194b1dd20d60b5ed784f8f73f2f6459c35c6d82bdb8db87670aef751fce78737ceec7b5704c9a40ea544bb5ec0dbdc12593efbb3165ff129d02970855ca9377eae0592e54728b2d84292a5e6248b98e0af93a1824ab871f6b3229b4340174f7169a061b1c3f0d09426c8883d8895b1ad2917ae417748b0441cbeae3c90904d44fcdd10833633c063dd451f88e64bdc240c621b2eae22219e5381be9d26124945641c1238dcb7fca1ceb87c4e37992c3623e08f2b4c248dd33f606f0b4618331308021540ad4b8b46eb46c359b4465959f254adbeb9f72eb1f85986ad26ea75aed544fb6fafadc28e22d6e694c5d59e9cc80d74687f56ab259c7e557b5588d3cb46ca973525b794b8fd9f75550ff59a84c60915d5bc5e163832b447308a88a69a32e818dd292b8eef84d7c00f6ed2312a9240f9826dc379f157fa078a115ab2b53968559e9edd9fcdc7d44261b6035c741f908d0fb25098f8416138b15125be48156a04c095d04505740308f2110731c1fcb71e03b7ddd5ed58c676e710fd7ae82eac64b4021db6e72003bbb34e897133de8083e38db27aab76fbce78f56a855964ec8d839bd3b2f8011795d1339f79e65da31dbf3e26cb20902567610b56538b53c356bcd708a50ee32ade65cde08e436a5117cc3d119d2c8fe2b4b5b81a2b607764d34932ce5aa79e8372467bec1fb9d9541d384c1d46dedb6a00a8297b6473265b88e2046118064f440ce72d7abd33ec2a166c5671993207f812ba45af56226b26a0dae37d26567b5a9de2a149e9c1280323a9bd80ad23d73a863321134e1738249e335048b61ec33343ef95cd0589330a7452830f95d316c7367969392b84851cc622ee227d3c339348c5e315f3c60c8613d3a77207d67e6208bed3a3df2a2202e005489a455f12a5d61d25ec1267905da54193f4cf69c3910c9fdb439a768057d94b627071d38e20f089923c792e14f577e3f4f8315dd8949c1eed18e8e7437ffd34004e143c602927c7114d75c7f3fd8f3bc7a39a433eded13b051bc8eb2e2943684889b12da0d2bea00110588366733e3ff573c107e7aa5f00a96d6a222bee9648199cac2a83433500bfe899e056d5232ad3b37010908ba0af66a4b2a475a6ed198d481c1e446426b7def172888a8a43cb1a6283a199595677f97d4b0846a01550f788769e0f8c62d1b2d4bd6b8156c4cc5eb39c6f7927f84da062c536bac1c9cffdf712a8b1e65cbffac220353f8c062bed3bd6739b2a3088d1d08cf698543d6ff2daacd60e6cf53b75ef045a619f9fec2f32649faafc026b958f40087579cc6d20891cb4a663266cebfeac1f2816d6bc4a06eb8e8d28a870c9a88489ddefc255331dd6e989c556dce807345f1a872fced4b8006e2ea8f4b9257bc15b7fb2c5cdc03a82ffee35c01564ae8997a3dc27d691fb53d08f34d0fc6723d7313659f5a6d7f51cb9c6eb4a5a52ac6697bc8cdf0b7ae306bbfcef08f4d901b295dddfa9364802e3ae3a5ac146d8e043b911e03d58eb5afd161ecbbdbacfa34e0fa275af0fa7ae12619149292c2581bad58e751d8ab692e33d17e6715efca88ef939934bb806b1d48ab9c33236bfa7774d81fbe0952be568158b6f9652016cfbecd5c15405503c766dae1ab543e67e5dbf225fdfaee03876266b44d7ab89cec90f7353ded571ac76fc77a1384fa49a8300982ec5cb790deafe23c7e17bc9c13d48a933359ffe2901bd6ef28285bec2232a0d675247192667e5476abbfd13dcd0e2409cbee6c15480ce901734ac1ee2d3968d8c8cf5d334ac6f2c4d70aed8fc36a811ce7861668e4eb9fcd2be75f6b18a26de2faed46b6f5d4461c5f91b551fe253c9e5d1edb435460ed9b08610ed326fe56647776aa1c676ea9606e5faf1825e7999cc2b16fed4edd8c5800aef329c46ed5770dabd9f9b31ba24645ca1e476edede9ca034fabf7e5c3065572030c9ada125a868bfc4331cebdef0e502f06eea56e7aabe3496fbbe2afaab1dda3c5bdbd06d4f6a62e9c4a10913eb45d1da0b9e9b8e10b92405b826869ebfaf109700d6f28a07ab9f0a0e37dd806d80b26cf0923c0f5d282941212a1eff85e2e6b60285d4791188f2f1a2c4dce6a346b7879404ae8b3b9c2f31a4f3291ec09cf1b492171b795bf4687682f5625dce53dd3a4f5c3612f0ad6aa359832ae726a55552aedfdd951d52fa603403bf3cb24c37c3af7e3893e5d82c5114a54e826c96d17a1b5362e33e1c23b7900747141d3520b428538a2dd97f6480021576642e9b731a2240b0ddbe4fdd6a352fa77c80c605e750aacfc4436617a6ff3988e91b7fc87d21354de7bac0be01707451dc9233042a7e81d19b478d7e5666bdbc92967152fc30d1c74d374ed630328c0d1dbb91c23b4fe6dc3a5e496d0f5d8547c0ba07960aea2ffce7801c3ec0a811d61fa1d4697f5f0e3c79912dea37ef5bf6c26fb24d14fa8cd1d515acf8e8577c61db31df700beea112be36f2b61ada437f50949f59a1028744022a0b9a1a292181a279f62a7c481605f9cdb027860587b6388ede2d8bae854a6833832f808351e02adf5e5c7be9fa765582e535082c55d72c44ec7b2dceebaebd34a9337a5320802f9f1280e65f0d2e28c6563c99960d7400b822b2851986003443f133ccb89006767c3ffbe00a556e403afef2bdb0bc28fde91fc163123961c0aa217671089a904505cb0751a9a794719c18d101cc1ecc3ef792a37e99697b60ac816ed3ca622f9b558d4306a1511887c5c0118f751ace5fbef539f01413aadfb440d14758692ad1eda75f757fa58fc5d39097525f0eb19d2565e649423b5ded0798fd27139748c072629790968b478470667fe24b8d47444228fac13e50d54c51bda49c0dc396133acc7781425200abaf3fb61caf44bfa5894ea06d3409363c2743980d87bbe73f8e9ec6a2c73de23569274f1e367231aff91092e402968152d35ba1ba714e7a2c12408ddeeceec8d2910087450d425ea14b091c35ed245ede166af81e2b6c7c74b804109c6e564c1ab6a0f0230d142717e661cfb83226d604d79cbebad25d3695f3fa84c711bad70d69ac3df4fe7d7abff24299cb934fc5eacb2e079adf667175f524990eb50378126e4c3b3a7601b75307f632c2c7fe26cb72b42881b6f4951bddf7c82c976543a3ed70c4c9326dcf58779789c1f310ffaf06afacce9f492b7555d6885a697f3d374327018bb9065c1a9b16280adf3593a82750e5f415f9f3d61004780a96a5f28b55789d50b90449d73ad559fa0fd1d86ba80910abb774a794487061585df91e0adf3f370e8b092886aa7d88ef7b76e5ebf73913ae7291642d3a165816590aff3aac19f9b3078bf7f43ae7c25d5bcba98f0da998ac357c0c495edde25c7b2487d3842734e016ce90026cfb90d6c77ada2e51c2c8142ea1234589c054b387003e0d046565f2ac427c1aa75fc291012205d2487835c598df6eb3cb4c406852cc393c47f38d95662cea562fb45f957c382860e184fe7199e9cbec48b5e91b618efad74fe92df0c319d9efe03a9e97ba20989e4ea537511503072e6db1e8b7cbafc28f7272004295799347472fed0ea7647d3904bd2fa943248934a4d5520389a10b0e1eca0eb691f333815642c8e00f55fd4113adebe1ca12ac478ae23a511b3f5cd456bdd7c462994eecbbfbf33a8c0488d162b3f59d56a3b97c52a506eceee6688890702d421258875871f04d1c820a1c6753008fbea4d421a0dfa9ddff8d2363afe163ec536c4bef9b2ecd45cff2250c0036bb1eab847190fe09d0c332fad03bc62236b7b2c84257f544c37f532cd7b9cd6f1b5c4abd7c6fce6262099041bafb0664671a2837c9a918bfad7f14a2f5237d2e7d696a9356ef9674072a50931c1202628f8302560c3d141e7e93e6e708ab10f3fa33874481b8843b4f0c615fcfa4b738cbd51b75a55bc4949a2b3a147f6709399328e51a867d21daf9163c58fe480267b97c81e1f4b064d4256eb2870cb6f5366bf6d2a5ae864c681a9d812ac03fa943bd04945c0e88aec9b1ff4cf425e5c17365db048841026fa17e18addf22a099debdd5bd463172cd9bd3b8f672b25ab7d05aa30eae86322827f2b8b27d55bf6eaca9be58e12f9fd0ab05d45f6fc47fd5922933a3f16b25b8a05e0f0a7644821ac25c5ff8bed461c2420f4130ae85837eeb5d37d068f01f7be4c0f2baa0d5834b4314be260be0dadd8640198c9a6d1da3d1f7723a2756ff28eada35488c2879e2b9b9a60249c07f6260ac40d38fc93b257610b3aa598bff44b4a3e0a93cb6d17af8b4e2b6e7c6a1e3682b525e1e37a191af3fb7ebe5d7915c64d83b4638e00f47c25604194905e4b5fcd79f3d7c5ca01566ce1bb15ea1773b0abf4d236cfcc63fd6d3feca55e21e872e262af6d4e5276b4ea41359c54ed52f6f107b59c60359da60ba1a479afb66cd67231d8271b60b0af9a749d6ced0e896b28f08f856098e1fc8a7bb6983604bd246bfed0d6938ab65af790790c1e508b1269e4da2f9490c9356ba2e9fbfff3b4e7435e04daef74585104aaba42c36af765589db165bec2a9e61c236733bd83ded33e66702ac843b50134f97037d8a6a064a37ddd263667e94d8dcc52bbf003392d82aa26df142caa9eaf129addb45bc1392b3179982a850d0a9da1327800c9b4d285e5bec0fda36ea7c147914fc05ca1cd8e1a592e7bfe56cd74a3c286dc83be3f442831172833cde089bc164cd08e8f2228c55fbc1aaf6eca3f29f28b1f1290f4948bfe7d236979e223e36fdcb2cd4eb7508a77ddcba333d29ffcd352c1753174be53dc4f6cc2ee5b806ca9a4d9f3ab3a6c050450f99b322edcdadfcf7be1a7ef72d3e3de8ebcb15f6491b803a8945aae411cd0282f67244b4408bfd95d0d6101faa78c985f924a819e4a1eb064503aa1e3297af2e8dd69bed43e2ab149ffc94db89bf4b46e4a8ad26984c8b2293d811f62c25a7aea93726bb9de4b231034255f4b8de6b021551c164448c9a7a71506983e8cc5b88b5b1341009b103f600aa30c5ab9038189957733ff6c29bc0a743802721d4c4920334dd1f9ac8169f6fc73d806a67ef13782d307e5d5a0e06142ef5274e881a95926dfd50453bcf909a6b2ee80c28ab2571f21d249a87fb6f19b9c57f444c50aae9a47741af88d9125943c38df904e43d73622131ca7a75841c261a40274ca871a2af05ca30a722042dcaad5f2d2943c0cb74f312b96ff01df629240b4279f653957b9c5ae611537ba34c1a9f343b3e8cd1f6b4637b79579b88940bb75c56573c813673f61251abc3c1d66671255d085a7688fac38e7f0b97284fc9d23c2a357b7da65acacb252bc2ee94edb3f2826ce574b7ff85541b216d855f98596449e2256820cb250bf6b35c91914a1d6d816e0fe0ed1ea6436a8ad66169ce9687f79028d0a8118846085c6ffb5bd120d60cd5aaa13c010d099590651e59f3b11e77bfbaac4b842c7f20755c72d958c63ac9aea73f6c9ddd19cc9d0fc2fe0238b157cf707620277c0e3150f348b8ec5b86378268d39f25044619d4f032935640b82bfa431e30ca2d12767a144862818a92c1382234bb4528dffbdf9861a643e5a4ce9405d00376a699ab2a74df7bae6c6f44c64b6387da659d4f041028e1d7923cb786f94a8c603a2342c948154a38da56bed04fc0834f4fd7a2d869d0034ad5bb646e6f4141b8928c3561199f8f430949e86154997fb68b965276e5604e23a4560432dc340ba7d3057425d10d7364039bf65ad4a3a3c1871701592d627ea4182891f681f709db36dd07398d3a5419990a8dc2e2f3d02c6fdaf31a79e0939f76ba614e4470095ff0b93375a5e2f1108157de56d995f1a0a2ea346e5492b6350f644d087cd906dcf93675d287e1737269f84212fb51f67238774cb3f188508a26d9550f53fdd8a84c6bc91c53fda2d5179b71dc17f20ce4501929edca6082dc7487d850945ece3eb33902212741910ddd4efca96d943a931658e59b1273f10ce92d4442a0e64c54e841007dcf4fca029505a3b7ca51fc425e16fbd5d509d07d69850360f56070e717b3c0197ab4fc27c202c009650e6757bcab32c1a0d5ffcac162b05377a2a04594512ae8552ab1c9e59cae30787b22443b8f6c07e8497430b8bccaee901f0be41a0366da8646f4ecdb2536e393bd60507613bb4d4cb4c2b9454f64c61ac98139d42eb5d637281ad3448b8d210f4b6524d1b9533300f5f8e2161ffa2355c0d41853839ee3f13cd6c091278240f19ebea40f4ff59cf0af444a40ae7922ad7271becddfb8590455d6ecb8b13623b7d22954421b6a96f4784fe5a943174f1ef7b6a5db0669cd2ff45b68c6553e8403d53455f4e9468c1c645c1ada9ffd25de6f8785e0004826d4da8728c9987538964e4992ac3d7df50f095f0635a50b5089a778a356efddd8c1644e91582a259b109cf46ecb4f5ac189ad052edf57d0dab23558b8fe879381cee77ac7b1d57e0c23cd540cadd89c461e61aff870762c3e4de608cbe342fad0556174096b0ca30d48e2f095695b7cc95ccf386a469fe88f6774607e4c53e03131ba8f771d86015d1b9733f21e459d89569b1328605b725fc28a979bc72c7100ab81690f7cfc54fe039a195c66bf11ce736f061e313e718c1721f2d2c604ef55e6c25858d5ab9b67d1f71432f61d0f1d902a52ee7b1c07e46e07a500191aeaaa430de1ab3608a0c91561106aaad7d5df597a063d430dc9f9581a0020990d55fb35ac222ae6d0c84b3e826e110d6e8e0b5ea675d487a9f547f12d1022a5c31c19481944f1e81509be6bcd29d05677c04354c5fdf9056799e89948d8d52e030660eaf0cac281647d599e91ae04d2db15d98d71fdc437b0f650eed3eeec764a1992abad409fbb55949b39b1bfe458304deaf7154b0f13603993d9f4abac4e925fdde6d07e3f98082284582fb9931a1a5b55f36ce92d2a562ae1cb4ee65ce45dc68268479752b02045f2ddc4b380c6a149414f807d6dfb13e41f78ccebbdbbb9554bcd64be663193e630f4232605c230975e7d947731ae4b389b61fcd25ad1042f804aa63994b31c1cf25fb3224c1fabf4297d9ae4b9f5e6418b9d55255646bec6ce11ab5a50b31ce7b418086df1268e481b953fb4bea455202ce2574c991977f42d9aa3564c339308cadfa532886216d385ee017c3795445f10151f1addb4df42b0f3f36076f6164963aea5bafc18304064683346201bc1a6bead9297059915b39b2b85d4cfdb15927409bdf8e023c762526b46e2ba440085a700a90eada594095b7e521af9e7508fab63ed98f6335e4c887feb188bc7a7388a62acf1afa1114aba1d9d69e561872395ced1484fb67cdaf1c1c9bd673304d7fa8373f7412a536d17ae588a937a5efe4e05336c5da92163cae9e7b60528198490363428169b7201a28a5d37c10e66e3580feb5a612fa15396436c164e15dd3c304cadc6e4f11a5eac2ab419bb1ff088d176e74b274088faecab1bcdd24e5c7735863624d711c501013b0cdc2e306bbb529ea0e1ecf95baf320d4c9a5312f0d0cdbdb99a5235e6f9f8399b55e196fe5405d6e63f37cd1ae20dae6f0de8f62200f731f86c990221e27dd0db5c79a863946d765a96293046a83e0118f3c46e0722f15b8575be0a101cb431930657437942ebce52e2be21c664013da5d49a5e0bac222f10c72ae6fa78cd413e3707266ed7aa205f916c77d0b52b5782a4664b5918916be5baad76d6e8f4bdd9e0fa5c5ec0f9a79589b6a881550ce237525f51edfbdae6b6a249362f1a696fc18e0ab9ac8ff0a02dbcf755dfcf8488b4b70d4bcaecbe63ec226027982c23878d0735af1000ed9d8441d408e90b111a39c28b92f4875dfc3811806713d835ba18b14ba8bd567f646fab48f9b756a2eb92f9a1ff315f35bd765d902d2ac07507bdd848ce9b15a31f147511f4e666e68d4ee633219fe602611f2d7f310f2b8664ca622fd61c1a87db9aaddbd8f8a68e55e2dbb110e25d8ec192b594334c87d12ed3eb17df55b0332c2c438b67d440512ef90c0b47ed8c06143a0579f8c503f248629982890ff826b7ce9d3bdee5a1f1c5dcb7a7803bba87d09f275501717086a133d913b7ff948936a2016b6d04c6f16e460dbd6965e509f519a91bf46cbda32b82657acf5a529479a846bcdadfb00562e806daa1d24c0ab99269f2548769c6ea8809debe3366771af87b392d7cb7a6025202da8713c03ff3a6973b43419918fb2c717c91125d0c66d66a5bdd8e030dc46017567490f6bad46ff5a6743bb2ac638a3323e696baba58ee14c9476d4728002f215ee8ed2da6aaed2fe787efde22c49cf60754cefd111b6cc68f884e516b1dfd8343d31f93501c28d4fab9094f0d8a2f3d0178161801891eeb57bb4a44db051fb4cc16021d100928d0c85d2713be6a2be0fd1458d6b25fe72a30ddac7782d87abdeae4e2b33868c026b669a55af0fb8c9a34a97b278954f44167fe14ab33f6d9d7d50b60fbe0ade21ea1978e8421160bdf898f08d4fdec2289b05ac7d1cd31f7ba9a2b283bacb1f54566d97fb018b1707a480f19cc1a0c916ef8a94752ea460261877a3035da3d894c7665d2dbb7311e925ae8cd5bbed198b81c020b13ad0191a3411e0bc10342035a30ea172e78f16955579206e4e4b0189e9903bdadcbff1309059b35b6e6785aad8b1f957d6a6225d58a31ed8bdfd2716a7e9702cfec1acd91f0a594c88d7e9eec057a4c94c8469d1bbd0b8f9f435f00aa0259fe1076719d638bf9301e4d7c9b494915c10a08d1ae5b9773585f6e8426668d6ac09ee935ceb4eb9b1df29e893295f755b45c84480e38c87f6acc8b593657ec3e0f51081bb8537cedf4e16bfce18c7bfd7ae089e3c764584993b9900be61684d22959975fd7003f1161c5a3cde139771239cda9ef83419501d5065146b460259c758b29a2cf27181de23ad2903d0bde14ecafd99a4b03379756a496be0523ad417780309a5349de66d55ad23bd9d2e0fe766be8be27424a97823398bcefdaa1d09ed3008364a8a5fd5544f9698f848a3084f4f4590d00e8360a35cf1d362e56aeaa2d71696fc47c7208e7ed476a11ed0cfb5ae20b7836275845fd00c3fe30a66e6c79992542d1c44643516b3160acefe12aaeb10d214fe730de30dff46aaf1959d6f6fe48dcd3a0be50dfd0ee209dacb1f8515fb79bd38f2a6fe8aadb4a658c94b99ae904f882bedf035b30cf8e2b664465644383f99ba25ed957c7ab3a0386f4563ded78189ebd3d1a371de50df988b13e17db1c2b93c9fa0f50d637f4ed50b9af5fad54186115cddeeaad9c4c23d1153f4ddc910aba8dcdc63500f8a30b0819d7827d48a00388362661f131de545ace8f11c5a04af2942ab7866ce216b0954a1233470709c0e05e37568a10266a644fd3161de09451d831c3e7468b05c78a6551ad3cbf056b25c266d8c4c2e5a06379cfb1cfc2ed3ada60dc17e24f94b3b517a7b3474602c6d93dca86aeafcbdc5dccef235a5b3cf095d44fb3818becc68764c0e79baaed7032f8c86479413da0ccf32a1869d47c94f267decad843de4a2bc1cf17e1e51016c93cac69997e14a26cda8deab7d852b9f3ca4f7199a2411871b6335d697e3e89261360061a7d9ab7b6fe8fd395819432ae2e4c2d577c3c0b8b97326aac0568d1503aeb395a60c7979b0e3888ad01c554bb66e505b5fc563d6377fa9700ce2a39bdef9d7b09fe506da41e170fbbcc8cea4601f3ebd177ffbde73c10ebba1423a91877743ac7ec059b009b2e9f6415dd4d44ba6bc543732fbb378f311bd1b3027e34be323e0c601e31d0907bd9a12b11c5171a11af7852a4f94ac3fd2f3255308128abcdf611f5c3644b1b7281e9b41caa654865410fa376ac3beebfd7ce61de80d56a81da98d8610c53de5cebfb610d7d547b84c9ec37eba1ec413a506a5506d8e4dd35bd5abc03d4c9fceb1e94652a0f810744aae0a29f9cf831b8ebdc59ae8160485fe1778498522edd6d9a18ea54f2630bd076e885b69407527f3f10f0b494b29d25dd7e930329ae2834520f338dfaa47d3f2001887dd3d12a3acb633e2d7558d103bb4370839af49ddda6c12cd977c6aa2e2355b8b1fdf0c124ffba103415549662ac24b32a083d452b762d4ae2416c439eb92a787f7b7521046916077c5933f90ed1c0d33bf98cec56801442fcdbe65b55d2eb2310bcc59aa71721795302ef011a8a7520932566bba572175030b5bc716b370d9a198a8399880c59fcb6b55aef4d773524f36a28d7f11dd3862abf058f245d3b508452712f7d72f1cf2ca5f6c29c386995305bebc8782079fc709f1061e4b4fee4c4a9f713fcddb9db71716dc431db63b76096f354a0d56803802f8ff29905a58110297e167cd88dd8f4b0409487f9bc26510fc48829817e8e1c4890e4186a2bda35664c88e7ba12f3ce0b171b5083484c19c3ab530fa184e17dc802120950310c6bcdef24928d138069ab608a640222d7c8526011a781a1a4e718d831ff269647a1e904766dd98498b3f9d469a984ed9e725a2b92d41e2db1429b3b845d2bbc8a37a8d2032952a1f2cc4067581af6d2bcdc44f905732ccbd264dc11732a402d29d187f494a4dec60929e38dcd84f7faf8d3b64055094987def5a5fc230477eb1ad0572cc0f718985ddc8490b1e3f1bd1538f1f72a003308ac8c1df58d2a67fafc31dcd522d490f4bda9484683e4564c0e626730938427fc25461a285cd530b7f27be6baa08f223f3a5f99e1d7ec8cb8cd56e40b843b209f9e241ff1628a2f908ac3e45e41fa289c3f464c0d5f93075b0feda419ea4d36f5a59353c7a453e554aa811464ddc4b2712b06250a29506d39abb84227ce4b921655297bcffb62841d55876785527d2301bbd63883a9a501e68a2f8a601e42a8d9983d1beb00857c3b5bebe187042efab6d91b5ddb670382fcf469793759edcb40e85f6f56866815aa0d0fcba11fc55f0e363665ff546b3cf6708821c1590048dc6bd8c38089a047567079ef15e4ae1ed89450df0ddd0e6f199caf295f0306cdc2444497b2ce3c8149c39bd2c0593b492928fa2919136400e51cb8c8daac6105b0460e9c49a02b7ea3f539cca2a98ba3f0d482dc2c552ead599cf22e176cd1ce2afadcce43bc7ed4d39b9dfd257798b560a834e6bf41dd061b1319bc33e92d15398584b0615bf59c4361c1be29f892aa2413bef4145c9a62a4603ed33d06de83568bdbd4e60e15286227f1a3df7830e9b57aa0449b2d365e90a3ce8e34bd137bc0fe3385e285a144b8c83ccb312d48dcd074e2941eaea5b6ef5479f448dbb4e6f12a6cb207c8aeef1368028e6ab837ae18af4a1c71539320350ee388f1782d3c6b4beeb9e7910b56b65414ed9a8af1560387fbf494330fa6600bcaa93e2fa8800dc8030da21527a9d3ce85d67d789df4085431881d4e4bda613c7d954c4df91f3053eb4186f4a1bf7c66252a6dddfa8c34639f1a742cdea0c05ecea5d0e598c16dca108c98cade11a1be792578bec2308fea40fbbc01888c81b4110299ca7c00fb050924c2fad926eda6eda4070e1201843675ef7c279cb5b4023996fec61c0909a0f50492dc924bf0e517075337cdf4c39c719276a72776f4fd0a1b3f14a15e3d8d97be1e4d1f246f54ca4634f25220d8f9d4c2f3124a3ed55510444dbad33f32f6a6d79b5bb20e12d44ea1ca03e7c97ea510c67641ba0d0648d3efac8da609e514efcc1a89fead14f21d74bbf47b313e812799eaae41b6d37a7af18c4a99d7ce7db9893234b488e84fefb966fbbf14bdc1a4a2ec58bc5b464f716e2dd7be2b47e247b3b1c7d7295a6efc6adbd2cf87487947e8e7beb9b6dae1e1c989c850df38439fdfb19aa3c736b7c65e6772b3241b524240074ee488568485677bdbf73351e77e221e44dc57bfe265775d85bce223f6457c31c004810d1b2de45c6e24b526a3494942c0585f4b284c43db5f3bb6351297f7ed30d2948fa9798285385074a43d07b5cb32f0dace61a0208abeea856522744069899100eeb5d9be56bdff6f59030f09a8f92ae52917c5903abfd2b6d26f7c58725d0da5f69f3be3e59e067d4b3e37d2f89d6474d810ff32d1c0e7107aafd5502ce9787580d41166b9d9149ea736dd167b32d03872c81d67e95c4f3e521be86e0137f605a2d81f78998037ffd2a61e7e3a386cf25f0737dc67a211f96aea0d6438f6402cc831550958b902bd91ea7e731d71dd04fb83a120314fc0c49a0d590bfe0f45da54bc458cb940e9653d21520ff85688878c2fdab712f3fbb6fa483640d94f9c72c3991412788d0f6bfd3402d7237c2cf7d4643f1b2ed8e0c3a9a182a4a9b6a2c78e8fd5cbd1b8f401dbecbaf5872da8794e5408ec709c4c253412bc639d699b091fbb432e8041b37b1d06d11c57e4ab1ee9c780ae3ecb9449968b339fa08125e81b4f52149881db64a406010d8d29287c08f06f29e091c093d7c274822610e11a9e5b1613c0e674d6fc09074e5c9d2d0c80f1bd883cce105d8342a855eecb9d71717cd0e05e5e222b3f82c129096305de0608e46cbc6d67748cbd0cf4654fc817860700e18cb5269b1fe6559a5e6322b232585a88b222f5d61ada73e1cb3429840ff7499f615df7e95da382e1e0e4ec554df4284042b79407f480911b18b6084afcc3c8740909b73a12ecca6fe242e27a2497010c485a64f35aaf7874e26ea032da937add48655b46fae39392223f63c868377711c4ed4dcddf98cd9fb593cb9bccc72973067859a240d33a7e025ba400349038ab936066b1c58f256747a98fa42b5a2972f0ef65663055f25d48789514d0ec6cb2d1847189e836adeb2490a2fb3a3dc19ab6760095f5d425ac34a567dfaeff340123828a0152c143d7a05974478a51d8ffbbdf8d3958a6ef28370ad6bc7f3a04809a78443e9261cdb620d378c248efd98c8a8da98d8d047180679773f786a072a9813308d8b00413a56b7052578389c25c0d4a362ad4034bda4ca423f770116a230a14cd47215ec66ac383098f9e13f4d848399db9c84258533a8b7ab326cd0a41a0ead1404ddc8d7f9964d5695ce5e99f907f0ce2aa28e717f5158d0047f3b86ebcdd02ebbe5879dd8cd27e60691ad8302e913ce7cce2af28dd464cef730947a6833235981e9aa12caec70303c4e52f8c027e934fb92a8a3a55fa0488979ed9947eefb6c39ddf681d64fcc333de4fb6d247bd0c3f9fc6a9c6a4e20d988b417aa4a61cdbd62f79358fb8adfc5633f70cc5d0b05cdb756a2ca866d185d22518579501555a0e47d58660c412d98d249d46e7d270ec7ffa230e93485f88f92c189afd1a8c331a9b1ed08d30de8d7ada17c1742b9db6b488221110f9941367eb8f47105301edf43d830bf1ce932ac4a06c58b8d305850ed48f2419c74f47ba98bb2ce1ec66d5220b8273d4ff2d15dddf3fc36b809e017092b3dbf1700d95d7bb29580272bce9a1642049bc3043db088e7994a90ee6c1064562a2e957349182a96fa0006841063abfccdcaf7ac48955a31828204a193c768bc20884dbdb8334528d8dc3f5fa45206eda2250c5b446600c152c033fab6a617e387c87846ae6e39f91047c9150c86058453a4417f0e6d67aa5442f48d90c93166e94ca0cec8b324fe441c4f7410d8a8dc2848b8c63d75e97ac258a0aefa8a70468f34ae2925e0b8c30575e7ecc0c78707016d712d665fd0bc63ced1b1a7c2a8f97516dea822fb76dc448a5edf2b55ea92f5f3041706f70771a9190c69f2f66c306c1b019bb0d4fd4a78e6a9d73657bb113fc633e28fd77fea7c24c388d046efcc242f98a9a21de0dbaa88d868e2d18ce114c53ba073358495701125780f8b58d312692e601a1d97ffd6c9b9fe4d4bc19fb8b60604054538c3e6adc2d64cce23a5ab10d82f2d594d3ab0e09841b0b112efb19c2a6e3914876a7df40cb65c6e0846d2869ce542e1f25f9d06ffd882efa13e5f482d640c43c5a41e2a12945b3dcd1bac9cf64193d48aec2b63808bdafadcf67c077a9fa4bb543c4b425dc892ed699d73d067b1698b377018e293dde6b0cde9c7057a1ffb0bf4462d42f8ad9f8947739c6fdea9cc79961e1ad08047ae77cb484b07af5207ee121999236d151568cf7212d99876c313072529f7a479e4d73df7a4691d6421b09844dd1a9bf38bcb69ec2b5f8da3fadb44e7d9149de830a5056ae5f2dc1f4b719cb7b9d55fdd3043fdc61722bb8fc790a8527ba7270fc62b61888d215f186b4358a9fe0e34ce7b1608c371adde4412745000c38c844b02514a6bdeecde9c742c356f5e02c5184285f701ad782bd10586b66a07c01341b1b9186e03d5a9b0e4dc32aaea0577412e0e14846bea82a75d5a90cb4efbeb9e7d7a1e55939461df5d983e4e74a02f95b837e395053c2b08b488b82c28abbddf8cc9e98f9aebd4a70416b2734ae739a177315b1073cb46bce298c4fa8e6a82bc7d63a950a7c20074e886b24560eaffbc837d609a43155856200240e213e0c6578fb415ea778228be065bd72eca2582b174e73eb90560a62f6a2f2f38d201b3bba728a171bff676d10480f3a9f31e6dea54353ea41bb24d90e96e929e65aa93dba7e649a2141d9fcfeff3f31be30154f0271ae6b49fd5abdd97199d9d92c0994516412f15a0f6cebbf5e4783e7d739d6c2d279d83d3ae04b9752abd68eb2c6c4e0bba037be2571c57e1e01906fc13619448a0cd3577f7ce3d2a05f7530152553f9ba363e4f2c617168723738ed1e144c09736857e0ecefb1032e17981d30b81f53f3c23e16f98a39eed1f8a62ec943ade2654b91397281dae75f00079b572b29d9cd676b8358c380b384ea2422271cc2936c4eed234594eb43201bb5621938b7dc591ce2b20235f1e8543c1490ace10e414ab60157c6432d88d90be951da002e02a91dd0fe4d064255a8abd2addf55a4f71b641e3120fc4f7a831fc9b0c8a27dcd9fdd12a05c5728cd381ccddebef0296b1cf37fa83ce51af2990f20bb284845141114b7bbaac2fe8e2ea4149d49e071f4da8788ac8ccefb49f260487c81155b45799f3dd00726f74281e95969bb44f6d099fabb875e1a3e92a296efa093f2d44326464ba4652add2662727e0988afd50836e592837f57bd88f2530a20bbc29fc3d08f6c561775498a603e7ba562a06b2e7c7f257d6b21a34eec1df01d465641ce9257145833a419abdbe6b0daac71ba91b40f7f9060e06614819b1d02d3f6f2ff5c02bd0542256ca93a604142a590e812c8919278139692d638323185eafed048c51eb5e1f605e6355d281b45e9cee158081425b81389234dd5889e70a34af4b389d90019fd33ac58c1e1343934302c2ac779223c4a69eb133695f236aa8521097c46d8b43d1412b422a08498606f2bbc6ea4540d841ecba008b7b1aa5e85257d526c12a852883d2bae2d433eb97d116269cfeca700c621ca944953029f55c43547882c2bfed8750478ca257022a9f1e1315ced868237d10901a42c1dcf32b4033e58577e93f3cb2ce51d0e3274669c44f3b8c62a7cfaa00e7acf044573521b2bfdd680deacee54cca27657c5322c717180c4cf19d8e20be524918cb5149a69d6dc37c5a4d6a0039214dca5bddbded62b3e63aaa4bde6fd41626c2566fe11fe633a5694012d6b353f175c9eb19ca1ad25d02aeb9586dae999dd0dedbe4623904f94f5a4c0741e192bc4c294bb651ee3d7310d99323537924296ea8e18f8d9616b94e009a07e6e4ebe59281ed31f6f08164b9ab9ddcb9570e3423d81e3a6e4faea3520d3f461cf4767890701b72ef60b237dbe061ccce5b35555088fbcf534febf62b357809990d5db3250a471962287b122ed40c6a0d9e71ea82c98a91cd7bf0d3c4d118a8250c4d1c5987c4581a6febe334fb895be2703491befa598047a64ed0aa95d201327f8e124399f5ce00ba7739a7b90d7d9aff7c58e5036032060c970a700d851dc23706dc530f2c140dd8bbb56072a4453aed64ba043db8ac739cb987e064535c761801d30d58c77810def7bcedd4904a2252c94ec37a020f5ffe87aaf872d8f6bd0e2470af0b4594823e6a7643e291024c3978b8ad40f8963670091493a660937294efa581ca8c8c5684339a09357a69f9bc0baa4649c974234d77222c8e3b76fc2ab08b6b5c4267ab6697d120ff8a635830324149bf55fc705fc09f8e4905e1c5071af1f6a56478bf4f15001e39a1b9c128ac8f38719750e0ef800b2d8dcdcb391ea9973a0f2be11408a1e2c63b84d178b23e249289cdabc22171cf505f9d130401a12d1f87977027da0ab7316ded4b0512fae9d927e9621c5c5b61c9928a42286506869b360665fdb5d8efc1c1974d861e59600e45cadff72ccdc3a97e2ffcb918ac0f3a7d0fc0a822959b047d253e044b464ebb6ca1d0812466a784111608ab4d6ed7b6b2f62a5a948d276318a8e71323f18bf4b20481ac87081105e5b4c7ec85cb0740fbec8161feea796c045c2df71f8744501588fbd535195b5745ae74ca43058c35db926bf634f37a51a655db4b7bbe9de709eaf5b9c800e6c2a5292eec9d91562718905d7021b8fee40c2c7261cf3ab0032f811c6473d603ac2b46e26c19ee9e6a2947f674d9e409e5fe482fbf9e06a8b2cfea35fa3ea11a6ca73a1b63ec8cfafc0f27f1171dcb0df4f8b2aab849e3ac57f08cfb1b25a4ae2a9e822bf6742771c2878c8ff70abde039a7c47d69c49b2ea781efbc70eea9d7bf8eb4fd5addf64e901f3d0d43bdee6fd5182214109961f297f4e7300cd0cf52b92174227b917d23a7138cb595254849a700fab5d105b7659f40cd262441f98ea0f6dd8fd78b53f32ae6fbf04f988a568190f22597a6ffabf185e125b0a319e249a5e5c683a34acb214e1751c57af9692a1c2aa6f16227ef263ab41c9fcdb461e7ec25402bdb81d462b1ca3d4f8e2071a66a481908da9df287bb73d55e6734504a6cdd67cd22b96a4a13088a7af58abdfd4c900d1f12e11519a5a2f8b7b36e46a0c5fd34d954fa7ac0d1328d6a3bca429da0c86cfe7e1b808d3c08bdf50e964cb14bad649e8834916d583e4ede40d1203d6b8bede74e934ac8c3fa8ca1d44573e2efb031a0698bedea92f3ded65c7e5917985540e95e503e27b2b4f508f4668ea2b8e2ebb411c8845e9d34598495854f992753cde7636ea726d4c86630eb55aeeb64b834ce1bf6649f56b593bdf92337ca9c33d51533e333e18046a5e6d8935d3740d5040a17536d5c4d6c906d66fe98ccd872edb95cc836c1b79288d51ccaac765be4f6459537485f7c3862f6c40bb9aeeee91724e23357bcbbd4faba9402f4ab4b4643172c8c3c1aa6190757e8078d3235168eaf6be22f5d0c56381909b1f5b52b1c77f52423d32742dc1ee0221c1941b4840f91224bdd5a309454c274783d3887f86f6b551bb1d118742d8f015e9cc604d5499e04dd1117123e2bcdc17f7c13c493355c517a5237f7c2f7c094008da4b01c2722594451d901bfa24a3245a6a6ca254257be50dc176493dd90ca686fec55b599a4f8cdadd0411bb23628d0fabb30ccb766eb78020b42bfd53044551b9c6869654dc48ac4e8009bd931b697888fc1cd93750f63dec02f454f84d1158a958433de339a90d23d5b84f3fd845c2671d3015fc910800aae14fb8ee77ff854fd9d7b612a194ec1f837eb3e88dc524c20a483cebf9d5bce81cde1c7157f9dfc429a32c63a61ad94851a28cceebb0be32062fe9f340c9636d54f328de21bbf354ce014b9f6019ae16e361c2b36bfc2008d8e21da688bf12c56d4b8a7b0cd6b3250c35a53c8b96d05093ddf9df547466cb4a1ff6fc383f4fcc5fa081af5a79fd68749c2d56ab3900ab53c7386867815ed5ce1dc3a0555730efc0110910a21ab2ce7f5d0c5213244f0a7a2d09335e842b2650f23108d1d582e004aeec5f3fdcfe5816693de0b43746e0338215599c82c510a97d7b6d4801b76d55f7dba28f936297a3a8679707ac0c2b66cfe1cd0b3df50e4f8c6398097b78833f061557849ae3b6a723c4088eb57eee80069782d66cc158fd9b87a7328b8762adaf633aaf73f53328e6d6ce5451c37a7e4f67a215f60a6d41e973e895de8afbe1f86212dc104cebf1150912cbc05cb65b2e89e407bdd4596932f04d724bf2f326b00ba31b01cfcdb6d2ec2f060a30125dd250c7114641235ce85344d37441f421106ce88b4c1e181f2c8c9193ee968b369c40ea369baf0b48541c3852992327879cf6414b9ae9865d1ccecffb7cbacc360f693d2ce8e0827619889f73f6dc2ab629cf98d5224ad5e1f11f4cc10874a6b85c1586308a4fef486ca092c569027bc4693d76c616655f6e69c85892633df24ceb124dce334a2701859bf771091b7a9c60af8c4bf3060e120b7824e8791a3ab75063b360d307976eab29a22b027f905b2fce02069070de1ca8acb816f352c62e7ff2068e57a4106a6a5f416e6fcb6d63a8e78e0d89790f30879fc96a0b711e98b4c24210174522a076f6f4d34c0f5e87569ec9823da547063ef0bfc023690ef2de0359c5c10f72f68944baf21614c8244b6d327adc2c5dde567094a0e864d7e7f16367fa6d4450e080de2fe724010097ae5b91bba4f03b17b059d8f0b1cb8c08a36b00c11a2607c67af2c8b623c44b0738c98cfb1f49a41293c968b79a88ad3f6979adb7faeb584cbdd64ce0e2a86299c37995fd1004a477e22e112fca54a2497f5f8b53ab671a312cd250de6a9b7390463b2fc740bcc326ac29b837c8623a70a29e340dccc0a39c1c2c25af5d25d3d0537ddb4212fb55dac0c14ab0404260d60ca53e7db7bb4761ecf898078d86df602d6c816b8c8a930f0baba9b3220ad5ce418569518f45e9bae8e94c0deae6c9bdd31219649e25accc3b91ffee7c455f88af4ba88066539f968702906f74e670ef1273b74770cb47af75af202c0b1859541585036493bdd79db7ad1d0416481604b7814b3198cee24f900bb59c1e48645f117d2f2248971821950a0c75ff38cee086621c31c2d499d571ab2e947031d82e2564fac92dec4462d3dbd303aa03ae2ff1bdd91798f5d48cf68620d3c6ce7412d54a928a7daac937d70ad43063c78dd9d9f5ef1a9e60312b5f561481b021102ef371d4832082792fbc6afc37de3c954881672ff39c578d63c86cd3a4e5a841b003e65a9416f723fa8a499e56ce44df44095bd0aa738ee99675c56b98770430d16a30e9bc5f80a7581014a43f9371418d78a8f5a4c994f4ecef1a3ac7b6e7109b57eff9938895f9b203aeba58f9a4c0612c349510dd91f1854d4d8205f7c60864207ba4f9dc082f626fd18187a0935f1bcc422ddbb05ab2fa2aca7cf3645a91861010925209e6330258b33a054e3cd591871c413111dcfdee71d43faffa1e6b33b5e90de2e1e3f5d4c20b175739eea7787c667ed2800bdc8bb4633fcc4aad77b4cf031c1f2b9b790da2d2791816b2781be77ee73e2119044914e6fa934b3e9b635b5f80f28eef14f138763c76c6f7c013b7593cfa967d5ea5f1811b6c9fbadfcb5a654ec8996e0ed9a470354e2341a134a61854010f9e30412e59b5a2339e154c7f4935141a705faa58ce7a9be08633df21754b00cb7e297b919f6aa4fa704b050278c457440b187a14ce7ef5fdc83fdf0e97de1838871c18580dc0b5747214b354878a23b9729416404798453fa721817e19bf20689020a44e139cad0947866847a6369bea7b9aa5c54ddf10e668d311ab4ca94a0de82a6a6209e18c8a2e010f09be64f9276740d66e45916219e3cf5df6bcbb0e73d75aef789478cbe93cd0337656258bf3e3ec34138bce746840f77db75aa5a86701dda9c6f98b0fdee31044522a26172cb42e5f685f759f60ab90810b82c9ea7716f7ed3d8078cbf2f5878db9e16a5cd69ac240ebc99b17dc37b7b1e3bf25065fb8d735d3f4c665283189fb0a7bd373da0f1c9d70e444cf311c2ae5e032c6d63e9266710e33ad7d1724bf7fbd7df4950a0196d9b6558a1bd24aa28a4dcc41b4049ed4bb0259bd4444372d26163be1fc56f029ddd24ced93a441404fa523fa9592052ba92160366452bf55cb0e97813116ee22bc6c94bffca56f16e5559efa5cfabf4b3efa637db33f8ba01079c049b59dc1cccd1018818e49a4e6dd915850f970da4c33e81b4a15b2df6fe64fbdb6eb2112292882412d9dd3b670ae809490af897e8713b8da7daf36c8479d4475bfc6c6824b9e96d364b6f6cec8fa8e538f9c4cb1177d9e24711b2ce84ec3599bbcc379f79759a8cbab18a6ca5525765d6d6c5548c5d0a8c38c2977d747bdfe2b6594ab9544ab3b667c78f60c411ec33ac7f48c1487447ef199c37194e1eac3f248aca4698107fe4e9eeb52be276d45f77ee1c77da01e12896a5a48a0759c4ec6ff8bac11395ddb3e12b4b1afb064e36e8f24492cd6decb346d9f4892d1b68c3d7134eb6135f9cb8b25f4e98d9777ba74f4ebbaab2fb8cee3bb5b038971514f3678cd709d976e47e313831ecf489eb806097fb2a67c7b2fc0d4b27e7068ecb2ec8e6e91b8336a63f11eef5eebdf7685bddfb39e3bd79ef8ffac8dc47de371cefdb731c47e7a04f38f42782d30dfdb5b0c8d1dfb9e8684cdf680cc3f4f661ff8a44ecea230f495e782bf7617a4a25579cf14bda7bd234f6278f248fe665d858fe26d1dc5ea4f1979a99fb8b459fdc933e8db9ffb86799dedec3937152da2ef273b303b61afb7cf68fd34e2d2c7656b30c63f912b0b303ecec5fcf4f369fb11b14364dc7cc6bfde55c3e33623b944dbe8ddf0b3b1488e6bba279ec62e208d8a38edf76546702c6ba97f527c2750e8d43dfd0d1f1725e67fedb3486bdeb4c983fbafa8b5c57c4ed1967fc6603bb0df69ac389c2de615a47db28ac1c7372a4f203635352311b3b273471325ee5ec3739751bd926d7e4295d9e125445a0b9a67aec4cc00f9a2a115a97da0b82d8b2b67387fb67dba8bf9eadd2471a1d1347c062d498f6fbf8abdd93d9a3fee2d63e2b5b7463285cd53d6df547b74ab3b663cfa6882042abf5af56184fd9b5de63457eb6f7afc8cfb6d9872453b933c1fb91d5cfba226e7b8f53aed8b5c65af557e467dfa3b4f5f40783d2beef3e268e009f368d547e603be5ee4cd33151044cd71bf15464077ca48840546c65832b58b4506a832d5e36a5d4670a9b7dc1b264d30d5f58b234c92beeccf1e1d8572ce6248fc9552136bd22aee0c29dcfe26fb5213c3dcd93ea8db6270cd3aeaaddde95dde8be62a737c2c7be356def747cdab48bdabbe0e4c96ed49dbed66f3752bdfe56b1f9bda6a3938bc9a8e6d8b186b50bb3da9569976b3ba5bf37ca4edf4549bdbba7e3cbb5845d751b1dab68573d8d76a9b4cbd3aeeed57dbbd52e4c9e69647f9f695776ef291da368173d4abbbc8da9d3435c4c46d6b569abf13432afe392a66393a7a626ab1ea72af831ca939d56af9c328d5f98b670884a95529496062569ed93956d49a7c95397e0482153e6b96ba61b27d350c250528e67f20cce53ce99e4eb3061691d6b73305e59b90b1cdd5c07647b2aa5e50fc9cef947f34f3ee12e246988434caaabaecc3552bf6084e33a371ac0710e757e63001a87eb5aec6abadb78771b2ca2ee35ef5ef32fae56f9a3a9afb7389989ea178c7070985e3062dd429d84a35d3867e9e86af3d19112d658438dcd22b2f9fdbe918b7008dee423f1dec6ef6de82331758445745ff3fb1a1dc3dc47341a69eeedaded39bd5cd06244bb988c70ae733879a68e76c5a5c9630500ffa0d3ce3ff814807f71291e27eb3c66239ceb68d78ff39cec521acab9ec5829e81ce72ae8e8b310a3b0f4e150cebcd0c4c949b1ca0f1e9a62d3e49142beba95b6d4b263e1c7d170f2a8f4a73404bba2baa3feba6f3a5e484eee29948e61eeafb58f3211b6b90b200720c79d1c0130ffac91ec4f7e4751234da4f9b4271c7af2443eb13e1fcfcc1e399f974b4c668f1b9f389fef201cba91bb915d37be246bcfc9ae9c2fc9ff75b24be74bf271e064263897fa9b5bea642638ff8dcc04e73890e696cfc94c703413d5474ab052c7806daa8fb2c3262baccf39ad1cd150c290ce6f1ceaed39c7718d898e4ecaf1df18300e34bdf6927c9d7f48d34ac42b2c4f76d3ab298d3d2fafc82cfbb34712ebf0c6710e7b363c843ad739eb1fbea17b2e3479e66f5ce75f0f0f38394f9221e738f452ce718ef31b1ac3974eee52804339dd378f94b0b3e74c43094339a85e92e3377e37fd82dc71498ee7fcb3975696e4388e469a3bea18b09de91f726810e0b83cd49fc51f8eac63c0f6a7b46b8e9e37b40c337bd8c9ca70f254a27aaa8f7e609dfe36edd805f9010470f2c81ffd00829cd7d8a4731d5db35cda9766d8b459190470f244100cc9d17669e7681c9b36ec800cc1c9387a3579e269964b1ba56443e2f0e0c243f8dd451bbea0d9e3f242b3c787e44f7000f431f7c7452c53e2d676b025eca20df8eb69e2cb96efc15265cbffc811e02893502eb62a9c86c4d7acb4561a033c5fb489268268c2c9fe7a74e043056b1bbe9ab081be86acc044174c7871824b0d5b986842079f2498486209134664b4d229993001134b30c4daa52030c316b2d5aa47c0998a022c51b0ecfa0bc5880029f0079b5cc059411fd06aab5d85e66b6a8a80720840037fb00908242279481e95053060b2b4eb19007dc0d7ae2b2e76b562d72876fd8d14c334843be69fb01b4ac0f1d662bc5ab564c5b4769bf363070cc3ec4ffbfd5704864121d3df1e959eec2230cc8eda6e1a763f89e50c8b9bd48e28c58d6134c058c54261d579129a0d342b888082224206decc420413229c6c4a978288b035f0411da2467a1d2db02752508b704277aaf090c3dca9520315d82077aad4008711ec54a194d660864de94e15aa9d765785b54267f52a42e8b47067605a6c6070972b32d8362aa84154512285812b55007184173be0620713381304133960228b921eaac4c0561142aba20a94ad4a0eda18585fb9e17a012b034ea8a2c4a31408dc18d888297402a57576f1c0d894ae4ee9c459363da53488884ae910486c4a8768da94061161972063892eda933842dd3e5d28b0b3e333034a777c8cc8b20db0e16b0927fba268963f78d7ae179ad216d563fa8b5b22c92d3fa52d239729289f2370dc1185b8e313c95525b4eced4a58d905d8f09584182131ee69f3bb62fde636728eb8a3ce3df5cb659aa7328e476db151c7d992c495bd85952e7b764434c7ddf1b1ae08758e7b2a7ff4de551aca16772fe59d267faac33780758ce3b277d83d206ed6c7ddd33fa460eea9430eeb50a039ea5cfe5c9b3bd6a1903a667547e98f55a4ba873ad61fabd3442a7c4f43d982dbb5f139d531efb53b216ed651f9439d46ff9082511ae9f49e3e62bdd34738475d48aa3292dca8e3fc711a496e564a3f677642e251971d118ea6b2b5d2af47e9ef86ca960d8c02d6f7b043c1c6267f32ec8b4f947542bed4512e6c6ec60e85cf467f49fbfe6679f1539ad35ffd47650b85aad19fd2bebae6f553764472d7fce61afdc57d31d6dff30f291869e26b192902c14874df7b9f23304b48dc18a5bb47cd228a477ddbdc9748b2756cf852c2cbbe9243e523ae23da3686dcb78bb3d743fcf60ba3140c5f4950b994f33e1894b6779a1f21c94df30f49a29ea21d90237263bb5347e50fc9d6392a759d0c254f774f958fd0d0e818e2c617929edcf8b1131289f037fd71c75eec8a20d65f4465d553797644a8abf4678f83c8ed1d13a13495adee42f2887bbc9e78dbfcd90f896b437a24438a443638b2ebc167082d8286d8893e49f80031848f148c377c59e1617fd6c7072b4da658492203cc9df901892f4150e10303acdaf085041256304c48563aa58f95132cb604d8f0754419bb6ef83a22ccfede83217c1082089f247c5e7044184778e16345067144174778c9b26c5a894d3a9a75cfb2dac8e8ea14f0e9599956019fb254407d1ef5790b5f5d9c366469df5459deb29653c0561ba19ed22ed45dd9e331861b8544b77cd355da646a87ed99b5b388c8ce8ed92d83b295692212881a585ebee7320ae3eea87b94574473d53dee6a221aadd2a977345e4ea5bc73f744b677cc5a8c8f53879bc8c7a2d1442a0d652b4564639497baa9a352fa88751a7d84a3f2581cc62814eadc2f87cf79a8e373a8638f439de3388ee37e6a4061fc8b51a81ba9dfe0501c8ae3baa3bc5c94c22bd9eac1919d152a7ce06226cbb2df8fc8ae199cb1e39ee51a372b05efdd59f074a75920b2eb390de76a489195dc0117263f4a5e672c9729dbfe7279f2daa2691deade2f2ad3dccbdc95d7152a2f28535c99c2088ee6dcb1ae081fc552a13c8f46b3543a9c87facd5feaf00de8aeba90441d760f88287c8fc8461deb50f0ce1da6fee18dd22c152b751658d7390b387e83a5d2ac944e81759d1bb15437c2a18d6cee37b4918d0f3791cd1df5a58ea1bae3fce1d3e81f5230d6dd51faa8bb2a958bac36ea5cfeb02eb2da9da7bf222b8ed334cdfbf5340d750f6b9aa7691a7714d6bc739ea6e51a0d3dcdf3bc6f9aa7fd28413d6ed4397d746fb9cbe50b172bfbe8dea2eeadd743bc7de1b0ad97bfd526b2b9d3a868f48759787b1ee779455638c71037eaa8c75bfddda3b21759600185ba779d7ce3ac8ce35cfe5646ace3d0421395e5e6b244b152b8719d1bddd070eae8ee462c7d395d64b5afb687f80b225264b56358494f8b299abca6b061db0d5f5388006ff89282cc042e94372e31e5b7fff8c0efbceddaefb474f468a9aa69536a52d69e546edd8bca19b6bbba718e70c7dc9ea37540badfaedbb6d32d27dddaf45059ee2ed38c43e5e8c572282f53d9c2027f32abe4258fd67197b272d73295dc29a7e70f29da6f9472b3a754db186737d8dbe196ad17787bdcae0e15b89e62cbc10fef6dabdfb60ec8c64d1226094c3e2449c0563a9247bdc738560af3a8ab20efcda3b412794f2b6d7bede658656f14bbd0cc2cdd24fd72e18efcbfabbf9d7d8fdda3f9b7e2b4cf0eb0f6968e9e1feeaad707491edbab2ac5d1642f73dae6ef7ede723a7a528af1259bdd61be10c20e42a5265bd31e08dadf09bbd347dc8fb220967e9cfe8236f7eccbc08e447634826447d74ff679c2f79b86f01a841aea8fe3f0d6d91f52dd5387a45d401c08c105c01e3d43a09c3e5df0079b668692e7dbd9518a57145b6cd8491f2b4ea8913e64e06ec397145fa470dadfa5944fe8cc36953c62165b7bb65a33fa8870473ca45ad435c36c96ef6dfe8aac36fddd74576d6e2a49b09287cf13fee09ce77216b7cf15f883673224ae1823f7511886481123485c71e92b12b4b3d74fcc6e1d102c6a31bb5624087a4d6c0ca2d8b21f8faeec7996904965cf4701656797b7f9966d5e938fb2d75c95bf2c88d24edd9a61f4526e764084a476e1859cd024ba37375f6516857bf5d801615de7c66fc7d1bcdd38ec80dcc834e87c491cba192b7ff77abfbfab4c7f93e96fec724e8fbad39cdf6ce334e37bad2b9aa7b75d515644732db372be3adc38fa53da38a71adcd115cd7ff7343bd5ac1c1d4469a7ce12b23acd57a7b1ef72cdb5acfacddb7172761d9ab7d4bdfaf36ef3a1be5d69dbe8cf4e1e1a1894f67655feb663f5a97c84d2757ebb9025d7b8b7b3dcedced923619b7b81ebee397b35843aec8581dadfac7541b2cfdface5799a370eeea8d7f251f6a9a96cc9d7d3cfc39d7a23b7268e561eb7594c4905e61120f3d8110b6c43aafe62cc5f6c22672bf1b0e331e8058ddbfa04618b962419ba132005f2d5c313552a5b42137645557f2e561078fa2378aa8fb8f6b785a4ec8e2049328308963684ad1d3f8da041b246721dd48ec48ee362dceeddecdfd936efd97624ea7851478fdd51fbf0007ff069af668f303e42c0f0d662bc5afdad1696f6516f7bb3dbb75f9f27fcc529565056f2c4d5d55bb6d73412ddd8b38c490a63cbad8179a25327b717dc0885290a81511bc0f138e0186fc0d1a70418beb658796da9b285871dcfc933d9d2c296a5fdad342c74f0b477f69c3c6496da870ca6b16aeba305fe562b9d4f2a49d0b4dad94a27bb90cc32ec93ad745cc8322089c411e2ce6e44f2883b3b12922c2e51b9f00318be745065c3570eae6c046cf8cac10836aea134531f329862d1fa6c813fbceb577021fa80e586af1c30914f9e7cc4c0579e9ec68c799aa187bb72ab95db58cd863a1be3bed6c6303ac687cbcfcfd6341d539bb82d1d3966736adb7728d5eccc324c764815367d87521a67a492d208ab27246b0a2e9438d802c5192d62f64541c943fed0272a06d231f4893e4d2ea8f4e9a2cee9d34545a25aac5634537bf953bdd29152876cc9960db363d6034bccc8873b20f01489e2cf66914aa15c150f03b3e2122b26912d192346aba83d70ad5947b624ecaac0c25ec3ea87342fdc34eb4f8698b3f81dd9337f716f1927a5b55ab4519a89d5e70bfe7aa8de628cf160e021df4dd8a98ff712b46bf4f9d2b3c2a2bdab95a6bfbfd5d25f4f0ff65827555a01ee90ff59c23a3e5f62f5c1e92f4c00ae59562b77bd4dcb3e6b0704f58bc2284c2ba6b57a1d77374dbe46292f51713e719ce39c9e7e71dbbb715d7743a18f223b37f987141c7ff39d5d23ce611771223d464f753ddc517f936bb046e5ef1b754353b3e9716ef3d5a9fe50e71e73d60159fdae705638d4014a1b4723cd7d73db65b85737366c32ac895d10d4bd8e76403c8f5aaf3bd67595ee9b21ddb10b82bdfe2bb2136147239553ae22b6e5e86ef77231bef3b0c71c638c18e6b43bef7e76dfed990776f651466de630ad06a6a75d9c47d03a049abd46214955cb198282beb036c829ba00c397962de62be6450f99f3f6d7eea00c20823e3ce823ee2d1b3b4b483c5623f3871d6159c8ea5cd95aa85cda01f1e2cdb4ea98d601f1bee56f66348770c75693bd7fd9bdd4b78c9fe579d869c7f9f3be9d267fdeeb5540bc228dcbf2b59cd4367645d83ffb69b337848638b4e51bb91aedde85644d26f228f6f91dfb6f3ef5ae0b92bddaae68162915d95feff7c2283b1332fbed9dfdacd15ea35942ae77e7356d6a150d71689ee636179a4232eb8ab07f93fec004c203d8dc8cd29fd2468a5b7b7d2a1f798f1be7a3ee9a4db693d2d7ae08d3813ee2ccf61cf41161207261c7a8b11c678dcdadfe28dd73c38e082fc99d75416096a5a5fcc211f8b4b4b49154fb7e2271fbdec8ce7ea4842de10eed48d8de5ed88e8415b9b0f3911c474ad811eed00ebb0628ed4c43a8a31ea9ba138436843a3efbfa4bc3c52e88bdfdbca6f387833be86f07e18ecfdbb2254354a2915d167a7b9a53bf1c77f3ed9bfe90a88e9d09f71bb560c4305c0724da5f0defefb5da3bb5dfec66bf517c19b685d6467ba1696dbc08461c617bec8e5f844fa33fb9bb0b499b6d74fc76ed3f1badddf451777c1b23de365372519bc5db2f9ad95ff62d1fd9cbb023a539bb47ef89d9ba9acc7bf6fb8ffb76fdc5303b3bfed66924b93b8d243f2ed2536b6df7a85901d109fcc5a6262fa08f786b27c6371bfb77b323122d76bef015bbfebbd91f3c5302d4513fb305a08e0a9d56f01235303f4758f9159808c1f8b0060f2598810c7ec0021614c1c5c91548fcc08b2e9aecec8b0b384d8c199a80d8624509bca051c30fd4d882ca0e8c78a2cc17253bfbb26403e30339802ec8444106142808c21939f4408c332a50e3883174c0e2e60b0ea60d255b5c21054c13a62a2c40a60b33388c79a2c2e4c9268c277e86505a81134d40b0a18c13229a28cde01406184ed228a3039430a0c01a183f557e88d8d99901a53b456c4198008c2084b0860e3a685f888082167029c3ca184560a1811d2184102248881dba43a57ee1e454c6174f3f4c9913154e5ba215779e8b2f18576ee6eef74a3aad8d3c3d31acde84a961c520dc1996e98fea3fa2238d7544f1f5db96e31a1bfb16e7943048b6640f367f61fee88630d3c7fa8a75ddbd87a751739ce71d7e72f947ca3d8ffbe1017f3db0076c4291802281c9873009f81063d41adeb9cf1c77e745259cc61a4f1e78a9490dcb853b5766dfaed18c7dcb4653d346f499057a4cbbe21065a580691538dab152c01e6f84e97a267108abf59c4eb2a48421ec4ce250fdbd8cb04aea0d993cf637bb3079ace5b158cfe4b1937ed24b3c79ec65b48fda7e6a973dfd6093b4ffc1031e9a111f0ecd7f3d127b3d7c4a32bf5d88bacc2e2623ee5e69e47d6a57bc77eedd7ff020a3c693273bfda6e73f2c6f6ec3e637b7b9cce88de2297dbca68d562b3d44bb988c6c3c6a57cfe4c96ee337da46761b996ddebbcc6917938bfb764fbb6293c99345cd6997912bb5ebea26f251dec564142f5d4c46dca376b93079b273ef8e7b52874334d0490587620687b60c8750d969b24abbe2d04de2be1de72426234ec78dfb0f1e30f67a75ac3279b21f2dc03ac9c58425d54338d46538c4393971aab22f94507a3fb0866087001cc2ae421caa43536640c0df2d43d7c6a01356316e66fb592ecc6c389160977669dfb46b7be1bb623fa2e0aa8fc0212881be7f48f6cc47368de62311d3476636b3fd91eaa614ab139bc78eb2ecf4b3c66ec2212d26b970436a84cc026faf71d1850b1f367c71e1647ff6c545926d670f97bd8497fa08dece1e59b742122881967cc84e1e7aa882b6934776459bc6471fd4306da7a653c3b8c96903d4a7b463ac97f76a5d8dc13b94add915d54b7d245fa33e92bf7a45b917906c0cd8f16d57245f9f7545a8630ca5574af2488e7de43dc7f60ef78e6c6d9a5623b34dc69387ca861bee2ee2e35fd439f9786b70d024be459db3d101499d090e53a86ce181bd6fd57d28fbd8c9df6823a5f93daac67b69912dfa186514fcc5299ba6452bd14a53bc92e553ca74fc60f30fe9b52bd22ef5a7fde2fba8575d11eabb1a06a55d138fffdac2cc86af2dbebcb6d8b2b5f0a2c5d38ef7f2a87bdf7545f8567ff2f89c0e79140c4adbb5b15e15d5441f88e26bf4470345b6e8553946e9017f9149f4b2297d8f117cc8e6f276dfd35fec5ed555511d82260f3d928f932dea923c92401f30763f30c02ce013260f152234737e60803ff8a2b0e92d481e14678026c960e337b739bdd14b363493bc8dcd6dfc36329440401f92c697f4eaa37b18528f9ad32179a474769ae252f03b2dd42de63829f1b58ee81e3f5e14c517cf1e4356903c28925c6148137c2f8f332a23c97d513ca4fc0105a38ec2871d0a4b944692fae85eae648b9efb8d50248f289247fd21459e939ce4a4e00fbe86481ef4b0eb80c827b2883ceae618621179799c85cc22a8b38ec47d1f519ea73770bbfc030ae6501d10d955aefe8082f5a649cd3e6eab8f2aec50a897c71d10a92d24924f3a50e68fe4e1c6fed12419ecb76f7ac95ed349a004fa3ef9f909b7c6c41212f7b3fc1d21c93db12c8315b284542985c0e8f1a164d8a547bba27aecae4f1e7e4aae6d7f330ed77341ea512c21f4dee9bdc36df391ccdc6b3ef20eb7d0f43e24c9414942aaa2f4a7c4e443291252da052550bd2b7519a004c28712c87b95f7a83c74b1f68ae8b10dd2dd6aee35a32ef391978fb86bef324b083d777a0ecad6b4b6d6cf935505978b1beb340d013854f32c2321493e4a8ba9744da5e48d66499b24978d6692af391cf22ea526539aea577553dfdcd90552ebb3ac6219966159ed82a872964dcfc3ef3a554a95b7e30c4be54f69a7bcfcb9b6f79accc4d2f40b46355f6996a6fd46b3344da4baf6a49adbe8241bb7f9cd935c3634938a49bbf7da15a9687e6972dcaa0ce3539a46438355a97c258af3b08cc3b22cc3320cabaf4a29a93d016567368b5bfe81e6f772b3c33eb93197cb4857947d461ed885a691adebb4631c0ae5e5adc36e6932930fd11ce7cfb531ca238146bb9852773175a7b9762da5fa55e5b85358d5a57e5339ee0e759597daeebdbc9d66630d953fd746927b56bbd56b9a24d2bea5b459dc5bfe010e5dd899a02969bf5d5176ecd7eb3af97b2f5dfbb347923bfb94f6b7891d02d1b485188c1bd35f3c969b1979ecb5c5951d5f7f51174a1e94d61c7f2326257dac4a3bca599d5c282f844e4932cc63b335c33684e9a549a130da86e8675eda8628cdb15baa3a49060a05d44ba92fba88811a499696e6125e5e66b2d0b204e4a8083cf6cf2edaf07d853f1d5b5a1d23dbc6b407e07944a26dcfc3ec816dcf1884c5d251914983bc98a1517b8082c1b4b4a186934a16658ed0440e17cc19b24822f3a205405ecc641165671bbeb2584194f8ccc667750b1ceae99978f9b2af98b2e5575744d9f23a3b5b12c1635dd1a6230c2854dc046b871ad611d5798ba726a590a37a63575435d570c7981f14fce4f07382a01f124829b3f8214116990e3b3b7745921d5e42f04705599609714493306ef0e2822784c852bc10c2194d8af86228f3c289173bcbb29f12d82b3b67c3d70ebcece025e3d08d39c9c20e0242526618b37dec6088001cb287001c4ab2dd2aa9a18421fbed32c3a1ed4a9422ec22a034a45d48c6cd9ac7eeadd1f6bb7d6616e2904d127f5943e2210087e255981c9311f7ecf1469976d11087b2739a491cca22a4927af542f2e69e1c93f0d00bc99f2943f6f435cb7d638efbd609c021ed37564c6693c2683d25f1f23627d92bb1ba86128692b22bc9740d250cc9cb21b2895a895e24e9e2043f4e76764c500269450d4c76baa861c38f09b49f145831841551b42ccb320d66d0f2b8d2c8c6f06a6778dacf22514bb7f6f816910f0fd9f6d0624a56b6bdf183af0db56c7b2e1f99db03ab6df547647317925aa691e8966bfc2cc9b22ae3a7720a6314ea34b7874a43385f9da5b4b367f6579593289311bdcd93f06db4abe6aa5566455456011f7515e8575a27ad926a4e8fca49aba352a039be658adaa8e654db9c4473acb5635902b19367fbd492a4b44b69c8c5645519df6623d5b176290da9aa547166cba7308f3a0bab63cc6281e6f84635da6b346d4473ac5d718826764392a4aefab32c1b91ffe2ce5298c76761656d97a62d9f75796d016cf8eac264c70e082b05d4f18d50c72e9aa33436c2a7da5573acede4b1d9b767f2591e62e3701bdddce6ab0cf38f16dc1bd1e31bcda3fe93047f3f41bb26a96ef3544e5a3df56bb56f1e53f7544e4add86ccecb7da597c9637a619a96e430394c86599d345e4cd696a6e3493bc0d9d9454739adbe8a4d455359a6569342b53920a4159c418638c598c31c61863ccf4c4e2568a6131d68a61316211ab955ec6901d5eeaa884c5e9e5c29d1d49335aeb9c73ce39b38c6a669c70643279b6cb1371411ff45ece1bd1e6d4b2b8946924d84537a02fb0d19c3a8679dd017fb0099f7500fc8baf0f75fdf91588cc1ef99ff0853383809c7e481e3d33c76d7c5e5e813e521fc0a7003e03f09dec72fe6b7db0494e9146ecf97f41fb93518a0465d981c8c03164f34063e0bb923c6e3e7f6fe4d2b6973fa4e0082406fee20b1392587e8102d9ea8dfeac46ca76d475b3915b3579856c04896be94995e8c4dd55cf3c61f24c9e7800e4c824e76865f2c43f3e47c691e1bc91e1e4d1c92c9cecc50be7541346e7cc9ed80d5c7250c68e2f1a31acd61831cc09d7af9ef3b9820b48d0c7f6f921d8e53c91f9132c76a02ff0d7d3f31384946d31712923c12ed94a02773c912d33f14c1ab215737264b2ca677684af189665524a2925a63fa49ac636235b11491a510c50185846f9c24a36125503eb823e3017dc6126b6207bb42079e2aba5e1663348c805e8e31ede62873dadc9935d803be269160aba91ec617482ec718254aa325419c36841b66234027db8e08ef89eb3feb57870b485a0d9e3468ecee1ef0d1c5a76e46e7f2f7694dd3b96bf1332eda50ea5ea96e650f2b0719b9baf50c791a1e459e90c25cf0d8d5ea9f4a7b3ed53fadbd91cddabcfd31ca7c3ca391927cf1e4060e0afc76616462866474d245372e79d827af610d9a25e2884104208213c9d9773880b77bef88261b05a2b952abb2a4dd91586b115868161c20081c1da83c8add530518b1208120322035fb9b4698467ceec6f05f405cbc7ec36bf40b7ac083c83248b9413ac6177cc70dbd963839227864c72b1deef76398ee3907ab6952dbbb7fbead9c35bb94cca6f405e60d435d9e2ce5d1e95b9fcd9a346f270af5a6b179abf79cb5f8da663f6229522681fd14349bf1e0d95e9bc0ed983e618d61d431d8ba1ee23949d3d54c76832943cf7febab0b5f8691ad5a219a52f5bdabd2eaff28e953cda53f9e64fc7d6ec61b769da5e6862b9eaa951728631c0ce0e03e8280036ba566a02600350fd51f97a2821a4af32b376b5e2c1f22c4cdbd5048282abb69763dc961e40adb5d6facb161016acf39d56c5e41440cf0d37004109e839408b8762814d59248c52a8d2aeb168ee588fb34e48102519845e5ea2203f8f75422ab0e967fe3451ae972790829a347efeaba7b205299124a2d087d28c5207a57d2b57b1675548083a22ba8fb0df8cab99882e2d499a752870510479da0171c17257e8c35e7635c77d35aec620b785d4cb88b0cf7f714ea69d0fef8ae18eea826cd5b7205b404de00f7ad97585165cc880cc2fc4f81967a400a84b3fe8b2e10b0d34829078e215602a5b700aac7f9897536bfcd9f84486f3ad1ed99ad7215bf34055e0afd5d2d1f313f4185cb803b140b8639e205bf343646bde01b2350fd4057f50cb090e1862b4420e27326e90b871e0799b1b380953d558b65612a8a60658dedaa0862038d9f170070853367c9d99b27f367cfda0ccd63a5848c21d58c265c17c7356731e7e163fc19d9df45808fdac9562194196fdf5ec2da0961624161bbc6cf99fcdc9fe388b691dc9037718503fa9e4114f5f310502c7cb1da79c582764bebe3a65a1342801df0136dc7085633548e9672744a909788492399032dbf1b41322a1943a8201a7e4f1c59c190f4405feec96ab0887e431563b201895672eceea26a24c803b4bb1ea515b55b359f5acaa5f88f6da0686f0b10c6d83e1956274c0adc13728a574c56d202cf85bad74bed3daf1f1324639697a96dc203b1766154ec2449f844962d8b31ae07912dc1486343748dc18c30ce575181e6297c390de0d27619224c8f788526b38c6045c8b21bd7007b3b28561d88198c09fb51caef92a6e7a49e5cc703d3d16a55462398b0513c1851d10a8235b404db8b6e29697a752ff80d188e98a857083c44d5377e22024d9f0050433db001bbe809025d66395ab814052f06777bdcc2fc48dddcac61e336c5a926563fa878cc60c5f40b0b26127332d7112a6f9ec71bfa08f488fbdeeaa8f9ec68dd8bb11e2244c5102c9276182f1300152b25aaf84258f4c488c59f64e48083a229a554a543fbf68821216650f17c22c05011a7c883f10b3e3068a828178c0dff756c38e3a6e88315f01781e1eb993250f5f3ff8b2a16cc5166e06ee85f2c62f79cd9ca41cec4ca072ca5af53c1681a8987352298605524a29e5bc625aa8c10dde16e0ec14cfbb59f85da9d0ac5886552139aa8ea16e3ae7950b630ff318961b9a24ee214f65ab13328f55298fa8c638b850beced852e646208230e2c79a6008f11607108c114c910228410091c60c361a9a88e1258b27747046131a009100880452ced9544512507cb1c60d94e4009d84c8c20564845072260b1a4f303476e0e40b4870d15082860dd68c1143b8679801ca81564a2b174d3421e50614bc5eb082231278c922075d74310111676cb1e9194e0158a3045ea6988122849911ecf81b9dc057992b1bdedbf055260d1d73c0f055468c6ac357192f3bdec289833ce38a192d765f548414835a78c803e530eb9c1506610c210a1aa0714610a6984f2d788205116768c082c3d0d4d4d8734ea012c421ec2f684b26a0d0018a2c6cc0c10d86ca74d1d228638504c478e203174454f18329e8502cb3250b33c8284961811638a8b2449632d47041143b20b103304b38f1414b26843245604d6894794199271bd235d688f409374d5ae604fbeb2181106c9a6468a5b492e141a605218d322348e3876b0619648650cd98c10281cc188c8c19ed4ca5539a6186156640914412643e2e3a20e344162ac400338134a4e8818a22aa7841165efc408b12a5090d3330101bc3a10732579c6146141ecc2053e5a906642ab064054ed830e6890e6c70801252760863c3191a0c41660aab99015a32c3dbf065060accc02196e06600cd0044c3ce0e900828dd39cd992fc40d7dd00d218f17e446ca9047d594523bf79e7149562075d2ec81261e544b9f9cf86087d57e526263d84f98a7294f3ecc566bd7d34927a5b5169133ce6c770ab8e8e2cb181a6465b6b03e18c1be29e3033d410a70700310383011030e810a199cc0075de85004122f509931868c30c694d1e48b0f4498e0e9098d14f0a004a5043d40220a134e3730c11814992dc21873650c97313ad0420b2d88c8c458a15263dff0a08721166841c10d4968610196059401e50b1354a9a12989981eb0f624851562ba886245971296f081921aa450a1c4175cd4f8020c0a66f0c3097c20c649a7f9400a2bc43021c68ab4c2f6200b192f10a0b102272faec8e18a2c3d4003c7982692681201152f3af01004296c0fb4e84197d71417108295304778d1a5890765643248908145cc93234ea001a05bad073bee000a0f2d30838b993286729c44c96203334488a93214bf8a5129c6f84414514411f4050825c8304293810b11fc6083982f960801bde8b203130b72204616598061020393c5061d03e6080b86ca06a6874bc610645801c30232c8786283aa584bc60c592031060f5a30440e5cf01a83899d21e81845e4a035e95cc089918159b28169b28d41c30c4e8820892596941053c419595cd1c41332b8b0f2a5cbfde2c500b4301b182ddc172b5f9c08aa628f68d2c3184db6329cae60f94435acd259b38516a721f18584cdaccd7e94c0dc862f317e9610c306fb83af9f24c45882be86ac204611664419e5ec01f1a41c11232f44db44853c1d0c7b46d8a2b5567d29077f2795717a9f127dd2c4700156c68ab1c40a78041866be58c34a501007d0a8c2cc1445486922822651aeec50a50628510051a5ab01a7f160f52cc3a6ec2c0c2a5804550923092a8c1c6c7eaa683f47801103660b347cc4687fcc006cb823ee8cd08a22c87f19ee903d3adabf7710987b08262184624329e113d9a251a204a12804e415db22c0b4fbc3f312c9e491974df10670c7d402b6e465cc22e39538426c8a22c84b2fb6ac62886d03b843fe8b4cec11d4c0f1557ff148fc086ae020f501a240a814adb4c54d4b32e6d00c0001003314002028140a888422b1502c1c12865ded148009a1ae4266469507b31c874114830800c6184300010400024006888c580184030b8a11ee2968b940770392ae982df38a65c01af9dcbed90e116e2947167cbbff800c1621919eb31bfd26ac969e587c6c52e55b9b561170a7608b0c9f7fbbd188bc154cf992be1ab71074820ffe6694d43548f28e701dc1fc2221b790d6ed5ca39e5e5d6f315c738f9063194029d00ea9ad2e477160c049f029a127dbe8d17606dd40b052548847c14e2bab6e9ce23e9aa6b71ff45edd42388ee72c44c06a4c794a187e009e56719b253e94f64c1e34ddfd78d18e52768c547cc02058e96cbde0305b4e93d08ea1497af560109d4b74833692f903680f302cd7653f29fb6fdc01116c25ad0d1bee49e65d8fa147e899d1c5b3e8759bcdf4536553bbdad201a7b26d1c827fd9145bb5554bbc1d42a2518971f515bc077b8406d0a63a66a1b5aaf4363727b687b0c89b573a718381bd2a0c50a34ac36507727ac7cf54b670bf1dba282fcc702871e846eff600628fa8059dec2412cd4ef147e961ee53bcf144b5b27bc2c757dfa8eb7969e45e0a5a11f851fe3c1ee64cd23b4110e511660d661df61daef6e5825c968b373fa2d092c8687033818b8a2f92390e7aba30c927c55bad23d5abda633e573a684ce9c2e585edf4314e0e34f9616ec76102f2f4c400c705cdc53e61b496275633c350de0f30f31379ca6b9a10cd47a4b7aa7783c41a103ebf116ec7eb07c6fe73b734a8933a71a77d0467d3aa46fa900b3373f3e945862de0e9bc1c4322de237843e76e031b8e53583def47dc166af611c518bc089c721eaa874e8a910dbe3293f390d2dc0eb0c360e1fde1116f95b2d59d358f8e99b7ea7d137acd11630dbe4d44d39a140c97a128cdce99a3af2764ad0b780c5ce4c6beca9dba81251bfb0edd3e8abf288bbe0a795d87b39be773d558cd3475f69412886caaff2cbf7b086f8941d96176706e7aefe74a241ca2ac6dbae623ee528661ad5f8c43e61db4994cf7e08ff6e8051b2ee2f9b5c04cac0289e28ca5ec18362d05aea9cd3cbb8f2c9e1926ca6bf80eb21513216fc90ea4c6cd21178c2adb747ce8ba17f53cb64603d6a84e8dde1e385a36b2aa4bd6f6c8c8c84ade3b4e5c4e3ba13a348ebab430ad565adb61bc6c8e6f54562c2d0c9ef59c4a557eb2f044ac0771f83ba26eb8bb4cb6bd88e1f40a89ae435baafc75237f1e1b4128cf88f6222c7e471def96d7a3d7229b7e004193bd13143fb071e8d369b492741d1d45825795913039948f15ec1446c30c61b3a3d49e7e90e74f5a91304213e7a80de3e544b544aa8146356ebdfa59a6a9ee4fdc1d7ce28f174f0ea0bb42a5a87c178ebcda1048a45eec64ed02df809ff532ea65a878d2e70d75d33be7b802a7189f7c3b0ea02b65d7cb8375b2853146dd5a9878afb93f3ff3a85b047320244a16b138f79819c356098718aa71de02fea86731d99e52c1d17a15c49aaf8cf320c6d59be77c41cd682cead514cc0bfc12304d386ddf1e3397032b0e11727faf331a69b93d03a60bcf09a2bba7945c3391e9be07da9d3c423ee6d88b6f8e856e8d356b15bee320d8e940a18391f82925c0280dfe0fe44ce8099de4c62e855dd6847242370b4f533ab982ecc871f103d95a298f9d7139ddd1f26d607e8501c29baa0033ce62e30d13ac39cf8d6ffbf3a48aefab54d63127d1e5ebb0a5c8845b390430f78506b707d8025f916fa7a244b12e0989c3cb73bdfded0708443420723eee4475c082766ea487acc89bb089af58c9c743bd4f39d1d5f1071dbd3a70cb311ba6763ee45f0bfbd84607cdfae4b6dba655374a08a5fa44fc4085bc5e5b74fff40ac6864e29c218cc0f298278f306bfeec07f835e3b87e62291e8b535fc94b0d37dbd48e7eb1aac045e4f8c1eb86b0779f7829457f328ca1b5becd778c0bcc11a398834e61c47750e91d782b7d8635dde86b30df75e0bbd691c0bc19de32ea511d993ab3e27e4dc6ad18dfa227f1024562dc060fa834d8f622b67367d080d8fed2eac4814c22328da7377616dfbc936b7ec11a0c2258f3bf0a50d02af55799be906736f86e17c0c20c296aaa1a3e783191c93e22fdae58074046d65d1916d24a18587a8d8c2465038470db0ba7a074023f73749f12807833958232d3d663e697353f87677c09789c92b6b9108b847e5fdfe9e52ecba59e177cb8ca9725e067fe127be7a3929898c66ed139bedc44c67a16f0421ace5eb3fc0ec547ef1e326ccad2737225d905797bd81993982e85654b839ef330ebc8b6dca9b9fd505857b8840a5a66422858fc67c9b2cfba35ed68923e15076eccc347196921e5388342c50a01b52259bbb5987d927916cf18464a320c639a7face711016160d8652d5ad2a6518fcd8a0263680c63f09da26605c75de811c97a64cc46fb93061cfde419a5e31143d28f66b1e1ebca380c8209706e30a6805d9914a4f4efb22f1936b4201c9160cdd45d5dee30dd0adcce3c9a24a4aef1db00ddc2388427e7a2290fec4206dc7086d88615523169a53647a9714630163c3d6b3ed63a8d5bc8128ba3588b5559978f982bc1969298b72cc8a804b12720253bcde9d1def5f360eff95bb08b7102fda401707dde9b1df38ff9ff8db3e53aca767e658084c0c3e10316cc99d89a6a45332b1c4110e001e6839905fbe98d4895c034fcdefed0fb054f1483d933cb7e6fc0bc65ea3734d40054fc87afbd9201a4545e5839c84e62e7394a11fccc681062779b99d3fffb0d3d281c75f824b1aac002c7b4dee3cdb1ee9f3b2d30b7bc1b55d4339340815185ca8e6934c812e827acac5fee8aaa4f7d926c9e01311584d941101ef3387fa2e21f469eb462823d211fae463c118eb4dbcf15dca843a3d1155576d030c138a9391eda410da681f3a006a681d8cbb9dacc15259b0e07db48969cff9178c420d8cc747029f1396850bbbc7e5303e77108f6bcc0badcac0bc786a21771327cfd89a7d4b61fb021bb309ec3e84c348b2c90fb3950104123cf89abe001ec5e5fd8661a8b986d329d039cb9af93fa7dd1e193c31ddb53a5df9a16b7e4ba8e56c7cd91c974670cdc48ec0f8461014d11910b864eef43ec091404201c5227c0183f04ff59b43e86b23846840490a7a2f00fcc661b170944a1b247a228e3770e220249487007cfa9de4f841e302e343dd1016d9783cb4d01a2bd3c126f6cbed5fb8a4499e25286d0b9d66fefd6c2cfd224a0b815a1634682824f55beddc7bff24dd8a3066661b74c278225516d91a3af88cc88d4c5abc0df71cdb368ee62b886598b6f9699eeab3627081f57432dc55a6c344c68a2207624b5042647c97e9493acf6f16ab35b81bfd4201a0bf47ed666b371432d0f4e25f5a400afac97c2aebd3f1a810e365e6853164c1fcf7d10c77190bab441af549d37d5dd92ef9dac688822c4fd35815240580afd55d70588a57b911a72458b30d2634b397076fdbc85c36ecd7e0a228c6d23cf8662816eddca1f3a069e44c23a4ba871e8c800006b49d593f320c14e768c65e65bf0d88ce434ad017ea297bd2d32d13649ad9e510eb81a4d2cd1ca4bdbd4c6be72d7f230bb346a15e0620060b42f9c8633a23ad8a3c0d959f8a8aea9ca0c6cd9a61b6850e270e7cb50dd38b9c5c0d2d9232fefe9612b1a1f3f0e5fc01d63ebc372d80e52e888be164c512430566f7e3d92e62dc378e30f713691b4c2b879299695e76d3b829c592225a7b8f4cdd9cf1fff26fea0e38da1728c54eb9d79e8c652ad810c927bb64ddb759f29f9ab5fc2be7b69d50b8d0c006846bc9a4e861331a8cc944ab4aaa3434a90fa354921d6b1b89953ee5b4e1e9f48ca1cfada374f222e95ffe0299725fe20447ffc4c83d6e81e36fe75b9a48640eb42460f4321eec38fdf139e39825fd9d45c914dec32ad46e7c830de96b5198f531ce02b59bcd8c62450d9894e80b194d86aaef7b2b590475c444fc8878403e8a2814dc7b2cc9003e2a9f0a1c7afeae6684a9d334700bc0f54c0c11dd54ba0129ece5107a2ebb9d74bec8daaa6ce39eca3f23a73e55a8d3e2658718400132e7205223a9cc0949dcb2cef5ad3dd0491a52c386f8a52430eebe38cf05522a68ee64ae2f48d73a1ca94dd116ee37b027a6821fb9b8b52c8642145a411da04cf0ca92b00e92986bf784333dc1a6c44bace7da04476bf87cb4b4f7e5fee9ec9d1d890b927200e6c354e26ddcbf6332e8be4e55b46c7a006d3bc4ac417986c08f5b683378be203fa11174c0317d512acdf69f21e60dc9b84f1b13269982f8a8ab791ea139e0863c1ba3df9e244cfb7f9a77252240b7b67a635f465023a979d0fdb48e06538900c1c369a2edf1a1fb1805cebbcf7e40ce349a97f08feb352d74da6d2598cd52bb62b17165fd9faa520228aa7542feaf43ee96fda4061aa53d1a5052a9117ccba8b934fc57e4a95dc72a3df0047ddb27ea6f014fc4ad3fd35b8df696658872a2c94b9dd9f9123bd51767053388796d46b6cc2dc01c7f22b2e39865f8139ea9801625d6af8146543cac25f6912fa09fc43d716bf2a0da800e7e52a1287331a2a7f768831e217f617f9322a6d4dead7e303e4c4ac10c1bfea117028501897e1348df84123a28e4b5b8b9f1a942a89a24121d131ab11ab8671fc0afd9018f2b5b3d82c6120bbbe0d28daa1f54c744310fb0dc86cca45b38a6c0fd596788f6ca02aa0dfc65fb5ec49a455dfe81bc84ba122abdb26e0709d1f36500a9bab4a604fb5f1aeafaade5a68089b463d217405dc0b4852f17e72255bbca4e08bea2b2491c3ef4809d22c687a15790e1cbb00b7a0388cc2ed28679b2dd18878fc8fa1d478c0fe83193e6f0f7897a21d6332ce954437c0ac1f4d72e574c952af63d763e82b6e47e4db1714ecf72759d4aa778073aded9c7399a8c334036f7cd5e3e359270fd7e3364294c9770cbf1307434d285fa11835b83ff738d6f3c0be1b21442ef2bf555ae6d99f0bcb78601b2c3faff51ad0de1f656efd0ac42bc8cacc278b8ccc5b2110ab7afa32afae6c54f3a03e857a3011caeee18cf867bda6516fde0ee6d14137f26f8126f4aafc9d9aede2c4f471e11bd6ae48d2a3e12fd53837c5f0f72565163494587e116ca66c0939e8fd8b79769802e541f616cec6d958eb1b245f86fb85dcda23c663b222bdf5c81509292e5521c5ee6cb965517e6c9a07fda0b2768c84db19c2f540982be12cef661772804bb38863beeb2d1b51235e694d782975cf3008690dbe803d5234fde5975b2a2e689fdfaef1f61eadb1c4e6c16f511a9c5a4ad192366647e60c5f008069e7623001abde309642034370b33899986656b1d48e78c81e6eab3d6f95a524aa7d930c982f06436096286077688224c4cc27eb88bd953e9a72a9e9d9bf0d9655f0113cb2c6cde1e14867e1b6a4d8b88fdc9199759d7ca62bb85aa5468f6bbf83bc58aeeddaa90463787a37ff547fd43700021c822905acfc1e226dc542a60188da032ac02c7835c8cf18ebc8e791ac64d22d50e72c2353f54da7e4c4e6c59b7e91628963564a8ab8d89514c6b6437b97dececa815e38e626f8816d1e7abf5113421aca9aabb853509f49b7b81f0ec7b3b2dcd9c09af284b28a0e07a4afd8c38dd9d604f94f8e50ce06bb51e74dfb421c5eae43350874ae8a0f28de2e905d9231bb0477358b3e4de89b2d918ec623ce71979cb1301a74e0eb067801648e8b3f8d04be990187b29c38ee0c68379d828321d39dd5df856fa16ecd5fbf48f021ef6fa81de461cf443b143fb5db0b81281ba70965025a62f7b6135c908b3d29f207edf2736415b1c0f9791d17591825283a78caf262c2cfef1699e30ba4c04d005c4e0a83389c73033f0488d5263881b90d39218d64dd138af3e1396060956fb5f34a4e832fd2923bac124e27d3893f98f9071d912feb3194c8794f0986d5e475268ce15753654d7a5c42e8f3d7a4e0a398736944ef5819fc9fd126aa7af23c566e2cd8dd287a6cf4681614e7c00cb3a45fe807a37048ad83f42b50c50a47dc8db9ba25038ab56e66afb9c66bf5eb12fa616bb27b3e016e8a6a60eaeca15c1feadcab4a38f83a283ab5831e5c2844bebfd6bc15fea7b10bdad924a36cd7130532305f6e9f68b24d096c3a4f4eaac87f4674ad3028eb3143f3ac25f01c03b0a9cb0a81915ec84eb2664dfd0670a7db7055ed1d799b4609ec2d05147116ed5cb53780e34b791c06ef3736ce2d3d35be2effc33c14cc43dcfd731ad2979a2bbd3b6a4b42f6cc9e0794d5bfbd350ec7a65f3d4afe1750a901db56170fbbd9c89cc73fefa0cb6d88606b5859bfcee19d695c5d45a91117371359bc579b6bb2f0b51dfd3253ba79ed99c4885faa36a213f3fd2b7a00ee5dfebd7b225e3c6e584bc9b9cb421454084d54b0908dc8148aad8b121fcace4af0a129742c6cfc40a8be1fc523cc777515c5fc54cbefb7a90ff9081d63af5bf44bd344b431bc48a760bb242fe4b1345ee90c5303d5453d2c7bb12448fa003240bbdb8f1dd23acafc052d6f2c8a28f8dd5ce7b41a02f58e82bb912d6dffa8eaf917b2b24cdb3eab91678b0c44dfbd00453ebfc322ee3995bec5a7df83f532e90bff7a122a2525edf8f649949f8ac29a62724c615c7b918912a5a70d3683936ce96ec11474a207f54234cdfe43fa77cfeebb1666efb40ba55a18815a284c1c9d80c643c0ef51685ef3a97705e6b50e9df679d91a7f359cb56577402eafc5fb566ed393e4031db8f5dade5fab563c8404ae577f778a10106ac636d241a38615c1c93a0c9141fdd615f820c5455d256b8426b874c21e4633e1968d5c9737e82f3744c758c69267b93c2052b7e237b09664493aa78690334065d7732421c9811c44c27acf50a00087b9f803351961d6072ea0507f2745d4a88ed20ed405cc7c465abec0fd42d440a6a94eb90d698b78b6807b80046564d5bd8a38e420f71434a076a3a10fec61d973b62255cdf1485a421e795a8fb7cb8d11fa2096d61e58603c791ae24f6f63533f4dd26c410ab7d43abdcd85a883d434b5a1cbc2d0b869ccb661d833448c00a695d5ca96e63f5b1f75cd7ed4c0f77c03f632137179b63a2c858ae07089eaca1c4321cf81a30238e07ca0652a97a9de39608c6259c26df1a0210db572ef1df0506f8ff76e0a0077f86cf0b43d2861e9e2405024fd03484d0448747e9dae5a9fd8f5de2d2f5e39046b17789e65326bf0917d41f90867009898f1f72dfd8c8c6eac58cd4ddb3de8972b9040cb4f888025e13ed5c7374bbb3fd85049621304c783822b676a0cd0ff007f28446bc53a550ba3008496c345df6d78b0fd8ce89c4d1ba02b0ce41cca410d1203b0ccf5cd34d74ec61007b0f556834a2fd4c7fe436dc98c5aaa1db758bc4d15d772da40d6fd043badef544a83463271a44110484d275c8aab553528e16125f106351ad2e6fe74c50fc591ff5132291e62a058c0a45e3e219e8edc4124a6e65c7c9d5a9c6056e3f78ebbefc2dad8027fec51de0e5d8d4577bf5c27d84c18b211870c0f2f193083d272f82ce47da66fe35f7ea7378eef3c39bbc0d4e05eec9a4da4b0c26b686e5af375114c04f5b579e952b237854c81ccc6ea8fb976082ba6d122334473a995b8569725f48a8c68d2c9113a7f0a4c248639f9cf7b884e0ef61a0d0e1f59552766c67ce034209ce0da17500aaff1e320ddb55c680d1bca118c16cd18af20bd17225a393dcfaa41bbfe4095a5daf4057ef32075c5982e4e8a7073931cdc8ca58cf2cba70822ed2ddd69b9b65a2ca70baa4da579802620fcd28104e246b4c6cd526453d25af386ce19e931d44e5140921fe4989c85b92ea8b20e344a41b2573c56fc6dcd4e66f49b2edefcf39a5579fa1861f129df4b0b07c833a922fc86675b46dd549cf5f1e790bf0ef0dcdb37d773ab4914651029acfc6c045b962cf3815acebc5802765ebdd7110f36f645cc0e5868ee16c2a3734f6fdef511d34da9a187483db05880f41608c8ee58247dd89c7e38a5a9463c4b798b182148b524b69ddc1631fcadf7935082f0a3da49f589a2b3f62e5dee3a508e744122f3c3c863785adfbaa50f3c448827f2c93afe028b43d48a93d0a5b3bf83de34f0041a5a16efc89e4aae8e6a1b5c6d09ad8232d4faf475c2a02b1219e917eb1028f589f7f9c881c48b6d79bb8d9c14af6dacecfb04a4467b9c710fe5dd795e9dd8416b29211def557e3b71bf1de9d090d4c307ec8c509970359cd77cd7c546a6842fa4bc1272da3ecefaa86136eeecdaa030ac6f2537d0b4171b14e2335382321c90482cf072c1d641710e5f3390d7841bf066877a8ff7e1c894d2bba8e6bfe087801680cc76655601f870883e951fa1e088577c79e3df593869a645fc968330f599842228323ef9e6f6790856354967441631583141461834305d2dfad0131c1a32e5c2dd9b22bd8d961ebc3838bfe43826172d2fea6f94db69f090cfe5c46add8da162b029ac98628f2d6f69a93921e8c0506d7166949c8e501ba2e7b4606035c13c6df3e322b96406917f98960c921ffb6f2a1b47e17d99846ac7fe07d3a78a293a51861cdf210e70894b0a5d511d703f97d88d350bbece678e68ac0c19fb1e1b351f89561d198808ba13ef4aedd8d37355b706dc279482b753a4067fc000a7da177a74120141d9f4320bc8636837314186f8368ace17bb4064cef76144e2709718d29dac578ede55bbe11871ab15cc1f2581713afbd7c3a13b07647aef77d5d84744dd20fcfa3ff0df8eab2fc9b74b5854ac201ebda5e5268bd4963d4d2e56f59ed499b18e6bba0fc25be342afb8a9471802d0fe86c984352adf7b9e0f933aed1b558e186b2f58dc4e778e62a2408ffed59d9d9a2a5ec5a88644ef4e2adb842409c58538d4c8bc90d64e0406322fb9cf73875e00f7daf6f8d586669d272b3899eb8b43b7eaf08ddb0d8ad49e24d3a4963849bcc0bee897b5655c93068b803adb7232a6cd43e8c64d0b2aa9bf507a410b6f730079f87e936d4b338ff890f86f310db718c5aa35f2102abe258d187fcd08f63fab1e4dc3fe9d448e50d99b325e863727fe5d8dcc3dac5f34c744bbca4726dc1fa0339622d93d3f72026ab52e9493c8d652243a4c0572e5594d43633a5e62ff2dfeb51db5c6670593dcfd59862e399e88913111590391803b294d3efa55051109fdb7dffa5f1a4761910abd0a6459e07f088b2b298d7261f6c03bb8d3176a163201de35589adcfbd2c5a76c1ac27439a376ea0cec82b4f03f9b23137211f0257b82d8dc91840097aaa7bb03b5ef20415abfa35f8288abb80bffc8f50bef5c343f9657ffa5133dcc61b0f10d79aff59222a95fb31ae507050f125ba1f3b04b9b92c9feda1a70c0923a8e1829b5faaccb4efb4f70a3f05b18b63276ad04dea5f56a300de1a7a8b241ac352deb3581d460d5a2a0f63d287de6a9d7e595183d447181540a978084e3e179b3d34f65cc898ed8a5bafa08a0e903f0d4339f019825f50d6db591bdcf2626196a9d6db4139b66ca93197ecef5f13a018d5da421cf99106362984bed59e40f61f9f8b6dd7a9afb4279d21d86244026620646833433f1b9da21044519f15c93143a77e0209868bbe4cefe294398e03b73a4063597e7aae0a424f4e45eba165909284fe1527d208af3a285d23aedbb6bae6a565fbf5454b1e0dd77427e7c4b2d766873661726c8dc83f2c7da6eb941a04818e0d841691d28c517ba3f6083dec8610b7460ea7bee7c3c77c9eaa19ee25015decc06ff1d8cbf725e6369246d09c75d842b6d25e4e90291d2dcfdcd52be1b3ea7f217733cd45e81aed25dd32daf057bae6092936071fa8eef4daaf199c023438b59a313f694b5150f0629b0bc344dd71c9ca558974bb74345f88734b7a7308b0c3d1d46a4861754a82bef77c0a7c6b04ca9e213618708b1a5e3d28963b32372d0e94cdfdf20b1d706f1d6fc277039a2ef5fcf91fc9ff6439bc599e6339bfde5c48f8da216c8599c3ccdae216d06462083fee1f13c5b4f77d84e2a0fb2a55cd2f705e27bce1a6a5bd97d82d78af51b616af341838cb36012dcfdd89db0dbaa6e45683fb44d60d6e17c546fedbf57d878976c07de238ca1503b34899befb3286feda9a585dbda9150690b826b2b2299b90f48bbcdd01f2df94f2dcb54397c08556af216785770137ad409c485253fb8e1b582b33d36be967bd2e2e45e7f6e663656466d1def01905650972cf6793f8511836036c90f72bfa314fbefef7c47d56248f318a0565fd13330867dbe5563e416ae1db8189db8c872d4bed6ea4c2879e5eb67be73bc5e431aa587b7ef90075e5ecbfdc96e58e35ec4fd8108521e0865c724a0115b927bcdc869caeddbfa399ae9f96390ae63b73ad1a4c1aabeb3e16e4920ed8919acb1eadc79f20579c03cfcd62b3f84d1094d389ac2e660b172fdae8fcd9f412591dc313dd750bf586cdf2566bbb8220218134f4e488df2e94bf51270b2fa860c09f1a6c7edcdb7872d4441d3b79801cc9a139602fb24e8f491a16407152216594990bf9bbc83211c84aa5f5052407bf62ae16410a64e21b36dbe13205df83ad758dfcddc70b65a8d35fd384d3ee899409350c9dab46af1c8fc3bf7fcdec1e5d903fc60122d2027793553b5bdd7b7e4d3c227f3ad0ada9a5c74a882a45d5acab07dfbb433c752d7d357e4549f392fd8958bbb208fca220d1ec9265ede4bd1535285cc820b765667ac19763c1e8671d4a6a50fea840abb910d27856d94e0d3f7a84fcdb7b276300ab2f08be473108125bac98a8e35b4bf542b1c4bf7c713269d1f9d16164563b7b7b62b2466fd26240301f5d09ec6418ed1170ba0607a68544dd61e64115c0a67ff12fbfbd1d375e745b1961c9c07e1a58b05493ce038feec81c6524a61b0db613491cef26fb0d7d926268d7c7760ff84b41fb1cef1f4242f833ca3f6d45306f2380b67b3a8f824e9464470d542c0fdc0a0928262268a779c027979d0678fc884069847bafddd7bdbf8f534a2f132c79fd9a737af15cb35c001947b3d19569c54873f20613201c3ffc5427d7aa870a7d8a78f106d20e771c7615a1b8f38e34b86f642a770193e1564641281c428c066a7910de2dfb22820be9974adb4ac435771f80f15ff982d334776a16bbd9f6ccb1e2809a0d20e66509925e7810d92365945d987666952492f4c390ce0701168743193ded5c1e87148965fa1101e0888b90549b34d3c45c59a9d23e2384634c0375698f3648e48c4b3641a3e57a77a234f9fe555a0a227b3e094c7aa7b7075227faa1d1034a20408ae7fed648a45e1bb05c3729e98b97f286506f0973af41665b42caf690180cad8f1ebfa0421bba971cf5cb3b6493305174d47022b0df2b31cbc767d8e128f81241413d7ea9714a6bf7b3065237e2aa199fa0548741e3fd2a3a93a283497e0a6507d071ab5d29348a03847b273c5a45612ec20998bca7bd97eb1bfdbdcbd16950bb56720bb41b2ff9d8534582e056c938af0b8fcc148270894a2eac1cde5caa8f9c6eabe4c6c105212a2886543c777f38a84e499a198157113b73a0946c394fd84e3d214fc5a271ca344c91a78856afed88325563cab0b4b6a5d86f75c4ef6da011ec160e817c67c7a1fdef016bd82fc1b7eed78109c0280ec5845a7a8f52d39ef3186b90d70be1ca06ed06182e8fffbe8371fae19067d91f0a70a2127b2e4eaa55a473b8de2060679018828db38aeebf987395eab10f398504734ad86c9fe1989c14b7a130461bd3747524ee2cf5dfaacc3db5650035f8cbf3655b123d8eb6906a478d925897d55b5c56af64d6e910e0039c551b4eacc1c385ef0f55987a80aaa4423bb797f82cbfcb62723dc4f9757544a370c3c012b4a707e9c4f66de422864f5c49e53035556371f5b6df54b3d5971eb68502144cedc80a2c007075297ff24898594e4739f1ba5421b5a0e17d69161a5d2919c53e84fdd08cc9a1684a04f9daf481467186cd0d2c0582004e458a11fa8c5503e97ee9cd0670fcecbf7e38e66cc4538a4c48a7f9c7a5afb1a6f62d300b6c11ada78cf51569678a60c815bd55c95f78a1e9c095795a2cb67ae7d02ff75bfde5156c22a3f616b52cf142d313137d3d59974146642db0efe24d8a815269814668ac8245e4d0ca75214065d51da03703a3f94239a9b859c0031a22439310648ba3666eb77e83d6dc29a64ace22dbd27e4543f084e474f80cd1e2e64406743babd08ab88d51f0949dd326ab8d15c20d1c353244457cbda80a85f2336eab181ca33fd4bd3bb2cbc632e44adf19cf4e441fa3731198ba212a98ba014ac36c70596656a0e493b0ccc5ab91748c03b4ebfcb147c3529cf3a8474d9e990ccdbec7bbdfbd563d523c172eb81936840b28717d5e13ba25e3037d2c94a734e1ec34d5d96a9abcd49e1180d10418dd7b14e8634a38fb87ef98cff579a0cef21ccc1ea47afa401e42003a0203190b18c72450d829319920302609784e97968b2cdfa84c816199f06544e95b9897689965c36351270f3bf56e01b9134ba945311cc3bc2f60dad02ff86f1a40cf2c15b79416b36582a1cd0bdf31559649c0b221a5634f30cfa64eefa49e1172d885a2d4d1c7f2e092ccb17f2ab8fe1e9cbdfea4c77c3571b191f50cd2da43db9fe9c3bba583a3e5720dad1ffca478881b8b758cb554de1bc94bd0c6c2e39521a6f8588ba453b67ff2e5d607842139a76e195b33196fe2d68b23dfed52fb04ddf12da5d3ab55598524a1caa36c11e513458b72969cce3d75be1ef2a935ada9788918c425ae63b90d259dcd4c8f828bacebac9f6676954f0c0b9a2c5926ac857511127988acae14b3f7d563bdb3b3d8f273aad0cc372b01c6c1c9780d0db1c98fcbaa117473b623d4e5123a9c38a57a69451bad0d81ac8a66692fb885f860d8e4b76df7afb1b168a62bec4d4b1655dadd00ae16a5b497df0b80b6e7a1661891f8d5d07649015f40b5e6a3da8b86fdcbc7a0f5b2b8d6e786c7ca7f65c30ae75bc0fe24dcf4f74421a9c764ecdd89729acb62ddac4f7bb07ae5452790f1ed2bab926967ef8cc764ebf903fd2319ba2aed545adc4728b8f0a7d44a727c91668bb84ae649fcfef8d0a00746e68120f69079866be9b343bc27975dc5c08fe7e94bd77385abbb57e611586e3771e556b8147c104bb42fc8ed25da1b66bdcb63859c3433f5f13f163e5ad0682289914550d03a0bf0ee3c48b10d3d3621ae2531f03f89b25f539b890a609fba3167dd071013da943b61ac4091fef198dd3f6807239a85f6bb40e5845f33474a2fd2b939cf511aff9b1499eda1bf58897dde428a47d29ce648cc121a2c75000d59782f6bcd53bd30d49096d3e158691f06ba1cae4bd4c5e402f7a36336949ff8845a3a9035d2a8c9287ef7eb8b5f4a925045de0fed6b4926347a6c9fb2cb6e41e13bdaa46a9399b7f45c931cc1cf7b13839cd75e64c7a8cfa9a73bf3432e1a8a999b88e9c0165034c2ef34f64a223f618db00e02d829f12b0463759294a230da4e874d81c2b13107ba43326412565a0fae074f120deea0a97f51cc3284f0d0f302432c050a5014014d65b65d8981fb1d31f6e1bdf0e0b4f13d1cd0a21cd6387ace1ccfe0909ca49040d10d07c6d17f25ef75120072c15dd7648cf24a20c4e8362e2f6f50110de213c6d77ebaecbcf482870937ebe88a0bc49ee8349052650929cabd73f645d7f027cd67fc6837489f83e65cd732aedd7eb0167683d64b0cf11974b0e042ed67962bde41e3c9e33cdfd6c12204d854695785a176057d788234ad8f7f50f893ef9969e0a9b311e0445235148b5fa5d79126e50d7090071a41386890981f9d3301458dc8a9a87cf723a82824458e8dae4ab5e8bf590050024d4862e377da56c3f23721e8ab74c952d125b72b36533a2ab29bd3bdb6402e68abf719d80980c561b938546c1be55530a74234c7bd8dcebcc781d781f5e91e96f80062190c14575a8d006a9ca2c41968b0e996fea83ee0abf2c20b99e350299f87232caa9a41ed06864fcf3fc186af50913ea0702ec9d0f1a45f4b6635a6d2c46ac2d259d88c7ffc9e0d2905e86c279cb0ee7e409747602cdcad00b2f12660cacb919ee4d6e34b808a746a3fe4aa63dfe529d5bc4e21c8c5b464b331112241b74a79af82ae531925330e6de36254da380943dcd83a9285c14b13b9494c98fb5c914845a195ab4ad8b57088c23356d36b36046afe0ee21402f96387dbfea2907ada2939f6d360a0539aeb1e4d7b315eb22c99c19bc59758cc0bcd89f4fccdefaab882aaed802c336c0fcb9a6fc29a047a6bd3f098ed0180f335a33902f9628b7aadac11500000042be841a7b09681e08912fc7741e07458e98e2c6a74719247f3978e48f278a92c1b860cee85f0c5e1697a1235168d8bca225009e86409e75630e0d8d0b7a6cca656f8183b6c8f608b9965ac55a75ab04231d42d9f91da342566a633b346256cb53c7da49c7ab39e4d79e1c573003172f2c36600c70db9b47b6c8fbf7084746a7157bc2341e595fbde178d88694d058f76742823a024e3cabcc0b86035794ff291044780129c9ccb8a09e3496c988c540e118e9748cdefb311cac071e0b1155bb4b5d37126cf9ad1bd3c410a1939ae3b40ee47f718e918b8d65b205d3162255b923f35ecb906f0cd4efd127ca1075648e4d4ecb669e96ef870f63c26252fdd688e790971e8d1dd28387098b7e3df5707f7ee7a92e893f341f826f44a594f28cb71ad99c8a94ba95c1e3f21348901a5a8a5c4323de4b2c278d8d206d06562022eac5921c630a4105ebf061bb1bc8f4c4ca14be67c05bf089f4898c06b23a3b74f17742d64d756cade818c152eb6fe8ace8e3a446097beb51354cfb30cc49a6eb3e070271a70ed4b1ff2218e1017e97a07bd510cdcd3571256b1d4438b6c72512f9ebc34c8f4b73fb5be7dfa6569a355dab7485a1a472f8ee627b6df735dbf63be0d1bc001300e18ef2e3f0b79a945ae91b539f7536f8f3e130180315949cdde9bc8bad1c558e9e5ab698e2e80c79ec6887cbb550c6d2362172387e0dbb5aaaa94e54fe572155259046ee6d0d00e9d9e37d963b03f946ba2112d20222e25a5915b8658c2ca3dadc2ab80bbe37249be45ff7d84abba74289c00d8654d0a2a783650fd1db0e42460b8af613572d1144660bed6b1a8f683962030104809fca6b4b3bfd127ed6b902b961a46d981a7d3805111728180aab7b5dc3f863dfbbead53a84cb57a91b6eadb4e0d3f3041fb0d502d20085aa84a12c98afb51c3885d68bd0f8bb2cb3e681754b7a0692ae29a3f1a42fa5afbdcb9d65778b8bbdc3ec3697c04ed963db8b7b506ef4681ba9f1778483e346d400e43d815901a5fad8df28f600f8359e753ae5211e7c5608a80bff8bc88127a8d9445a5bb7f551cba02be0cf6b6dc152cd2baf138a3de46034036678f341c86c975bc7c31f620740f65830ce4f872e15b5e359dd80d5f021a26335e93eb2b6e9b138ee8bf5aa1f211b65e8ac80cf71b525762e136f239d2f0f356b9617fd3923e56a1e347d733d390ca44fc030af62ed8cb36a2e9c7b05f69ff7d0a69f6110190a8ceda3a0c4d143188d03d6eb4e083bc56bdf4f1fe56ed53660cae1805de188da75a9fc2be14e0236665ef3bde08e714581019f3bebfc4cd70612c625068a53a6bb38b2eec05f58a80b8a12b1a2ba94ccf6d00ddb07ec539e860750471dcceac67370697fc3b88ae1e90110e0296e1e27be597176f6d325cf2cbfd575ddfa5b4d1c31087cdbf4eb46c6f8e1c6cd666fdcf0ad19565585a307afa39a1b45c956238e4e15090a86bf6d02bad8d9ba3861641ab6462d1c2f0bb429c0227cb2fe3122c8ca25dc3aaf426e9700883d29ac74a92e472058a9b35ed1bb8bbefdefc0ce4bbf9d5ec81897e338866a71242b7895391c10983e2e82fcab90d5b90346010157d56ef8349173ee31a202ecf00649f010a33748da9f7b9764e53d4b182aea4a6a26d919c02a45bf360200e7f45493f45d374ff2bdbf0eebfe88ad74187159df6c17dc9d079e27e3d7c95bdb677b365a2c93a607ce64b04e757f3d452c6146cb544bb26d1a0c94c238628de8e325c8fe1cafae2cb4d666e0bc758a50f4d15c3eeacc5f183fb8c7474f2253e0933cd97ac5fa110b774d296fc03dc45b5b3d769dc94eab1279971d0db99b1bb58245c378e52cfbcfc15fc02b643f81388422c478da0463092f2ae429c2475dc76ee515669c28a9693c9a6b20e19a0baa1d77d1195fd63119a6c8113fa9367fab9457130f535832a65288f0257b4304e181ab5f9241a3b46b747bef26248eae3f4cae633df56e4de574dd0000de3014fcffd331d670436e31a56c057b638871aea913dbb7a5e51fe056bc33afaa81733edb6053d842eeb7c23a46504fba6d6de4cd76c02dd8f117c9803acdbabb5d8f0b37db12c5392414b0d29ea6a6eabb258dbc501d4fda3653e379a9a04fd835c0d019db02a792be1f9b0177807e0f2ee4caddc89f54cd6740f9c1507996167cb081aa3c94b482c90f36871372ac04560722019a0b5bf68ec7f46385b96d90f9932dbfbd023c26136573cf465f32dffee5b8e896409a07c7ea8889f514b906218c5f931b4900e1f82a5828d66747dd23ae77314a3bcf9366c13c945c0925143ea0621d3f559e4fa1f6ea84787d0c2d904b829bdbbdd61a22cd03756712d66b71f9e1cbb449c35de07da41c6a36d4238ce00048e85a3d2ebabd4d282606a1a039d6c0f2556336247c5002d98db040f2b558fbcd0138b1cf0937228e42df18603aa1fafc35039b4cb1e203bf542abc63e6b83a8f16f84e718b81915e42558c64dcc8db3399972db241c55a8c49926bdd3ad5c35b0b72562802eff62dc30cc6a993a5247a6df41baf86d3134f612d833810fe23ae6d4a7e89ced9c2c94d6c6e674823a2f3c09c9c0dcc5d03765a4151d1bcaaa117d5f5390181b4b6b475e571c95384d8828160d17d8d40f14d45ad2bafcd8849a242698eb32001657198676359c8679a3f48e9dee47735a2b5f58e1d82c07115a3ed6def5b6b5473df42ca3253269c0f70a84f0e3e8398c17445ab368f76951016b44a7bf7d9cfc39d2e83166b83a602907214da32bb2dcfcc62d302b3a0fd06b4b89658ab70d08df9e6bd9fcbc6cfd25bca186bb97716def62dfaaaf02a6737e27f8647ccf78c5843494428c6b21a73708e41e6fc5ad35871330294630fe81b7fda703bf24581f0428e4aa6ea0d124bed48d102dcde0df8d6b3c5bab1dffe0370d53da9aa25c1cb5a161276d4b9c939a1669b0d0bb31ce3470a17fee2b0da0f01a7f72b83414d579ad7dd4db80fe5bd07fd46bf5ad405cbdf6649ba14ed0075695da6b3b52255e5ca4159d2ab9b91d0b4057e530d8a58105778c57c887846e749ecb7688e261bbebe1e62c890879e5bdc00eb344a0196c72c0c341396252069a7c9a625ff99c29ea8a9027e7a9767e9a4fc9c06db743c3e7651ea069c82faf611a0003183268cfb319d2d3c57fef90fdad2cc2e6f3b06a9a3fc8525d4497afc49d104d7e79e3dafb2d77e1fb4dfa5808fdd336a36ef597e09a57a5fd96d4d9fd93489796f602d492eed95553fe5e13b45ce1acd4608eea5a979715273921c066455e301cdc79f8b3ef28ff9632c60fafb6567b5dc2e2a68c6bc7748d791b46347bcad1ba62b69e2d8e8800b6fed7b68033a40a66e0c6254d94e9e870d6975d20ab756f80f1834ef6cb14223d930358cb427f2b88361189e8bec09a1f34088f8ed031e40c0bc2bced026bed894f2497c59eddeb1e176d06cf8d3e75c3a4d37d0c9696a3f9c51bf83039d9798c81b52e6685d160ad317914a7b02ad45f6ba38b22fce013f823d67608eb96d4547af7a7f50c68494776556433386b9f3b1c977459d94b61f949411c4036bab0e0fd183432c1257da0a2edcae135b737fa175f063db0e5229f384e16758ba497608e150c4800482e27c7bbb2b656f3e2c6d593e0f9f6c634cf182b54e17746b449795545d10ae957557bb8b4c208aabd82a4aa9ea9581c2e9cd6703c05e84b7a3565524f4bef3ead3042508b2e90e20024a2599921fc185cd5e6c19ca0ae09e5177e35f072fdba4669099f5c20df3478861275e6313fb8e7d47cdf8d724dd9404026b2581ed44aef671a582c1792b9094b3320e3eca91e9f1096527e0809bfe3fcc810feda5de3ab3c3239728a3a0bf0c67d07c690f9cd4d2935129dc9690836dc5d5faa8e4f4e8d0eb3956a9bcf40b44a5d6b4313a73027e7f791595b48551c712253e713a8ef0c4ec56b887c53b951254e0d0b6ed85a3cf24d5b960937fad1b01dd583f4c0fc465e6242135599fe96131c9fe94bc7cff1d6a6583797c00c85d150b59183813af6499394b6df7816311c9e0b6858381e4d92c9cc43955271d1f108ab89809b61d2c9b86fb7407c69d5d09f9e0e2ee3419918ed196e9b1cf88e8f7ee66dcd27438f9b6cb1fa579918bba8d4eb961ba193eb87ea7b6a0c496994a2b85574e622278dd3bebd5c2ebdbd23d403f1a46c64c2226bd3fab38821f94f88aae56f90903f7ea9397c3c7bfe9f84b58ffd4e04aca3fab654ea57242a369e6416cd473a0a3569eb889b65c5547cb7416590f2cece1254e815bc1eed2142d638162de6368956264e8790dc44ca5012fd5178cb088a91a7767b61d73f14c4389d9e320322edddb297ee4e5370979a0daa136a1266a1a669c36e18328e680529843ea7c460ca6d748276cf29d7d9f3fbfeccc0dd0f4b89ee4bc410ee954638a55b2e85350041be4e2d4cf17930214eb179250483efd0cca98ade02e89fc7c9a2fbad9b0f50f591e09b763b88a65e21fd35a97b149ad75fc16da7d52cb806ce380897fe62fbe58076d3048bb663fc357b9e0bd74f31db5bb7af22272b0d7fbebca85969e56dc50bb5b269f72eb9ecff601a940ab92c465922dd4f2fca77ba55243879e1b9c78c47fe38c17fe4e03551ad118d4441d50c198517090c628d4ce14c0a44369413612c1300a9c37d068b11003c000f96fb33fb7366aa72b2bf5e91c50c3db626f1152b7225a3f9b4c7970ab63d96b41819f22458d144d4a3f1f33264e8303b5851b3402b5da9f548576f82c309c49d15ed502a0510b4619ed2e1502b08d05382a36ea99c64b325ac8ac96f0f27f186e0ae4e3bccf5f54a22b660c4e9490edae06241bc1daeddaf78353eba71261901c366eb6cecacd428df76c90c2a352d40c3583c1aa60c1465f0d87903bec12b75e76e92e157285cda428b1dc5e7c0734a6bfb2d060f4dbb371f5a7e35367c143c29a2354c144fb387be3a6897b8370a9eca1c1fae0325b1963191fbd4c8827cdf41da124913a3ddf0c9db20819be2f0932159b2f8fbbef875a3c722d1d22cd4b67fbabea0e2d56901c4cb710ecf8f044226b52a25a8eb6524d0f084856af29abdf94c2406c9ee08302ce28c7b631fe8ceb850904234f2b3869567cb699d63e2b8183756047828041ada118a4c5ed7516e55e63b8a4354228068d503b482ceb082f4268705a65c121a38abf3be68ab82ade2b85083908c4aa82e7cc0cc290b611d030607f0bb1309c0212a17e4026e8bcdc73f88a21dbc1ddadfce266c36d30dd38df2b6386db38d4c627b27ffa0179e43350d4a22624e15e10fdac3ef758f173572bfaafc44046f72d63a762e9236cd37c7bdfe1a058c0c89bf2d9f0a255ed1ad5af76aa1171c38295358f77c98912ee66c668f98dc64e6405ab0f6bc5bd1187b9f252e6a287a91c95a7cdb288338a5505a624024ca90512cbda6c6f94a208cbee06ca89e4a608f56d6fff704aa46985bf6b928591fc500df6f2346a40c70d8bf4b8b6c0e98d8b18b0407d8cca6971f661d4c842828f7e3d7ee215cdf89f61f7e8312c14d44b5b9ed3e0d15bffe40892830ca508005fec27cc5944555805d23b6bf5b23c563b78ca00b3abb8292d1c205db5324a6fc89206450c4aee4738e422fa14abc3ef19110e563c7d7482c4bf645ef3e4d0fac247ef6480b3ad0f5323195e4b412ca169d9d28e5838754812ae5ebd9bd1a680be32eb71343abb681042f1cfa9bcb8fa7b5fae63aa360c95ac54908b159e6dac6007f810c09b8cd560babad3bbc63dca7b3a35c2bfb4c5513d28ad34072dc7e3a794a6f03de212e12070fa0e04b217237f3caba77e21986bc7cdc7dc459cbd979d2b41a2eecd515360f79a238598ab3da495d80f64a4d1949e95e38340a86afc28eeefdffe685930aa5bbabf0d68c7abb0151580c873b2a0d7adcfb0e8a9f1455740e4de61c4ae3b6a3bd4d620a7331ddf6ed6d5a027b32afcd59fcf3fbda82ddeacacb5f5a9b505f2f38822982a6bb5299d030bbb8d916b7529b18ebe79aa5415451e285c6d6439dc0650b9d76339adcad3401f1a34bf498c38c0658b88ce61851f288b58b61cb9fd48b85277348064bc0818c416612b91749ce3e534a98f55341dfc6f6d8473196bb2499dc89ae2c98c104881995de83b2a207c9a7e9c1e4ee4c7f66995fa7143a83b4fe24ea2700312c2c6cacbfe77c3155c9ec122387436eb337edbf471eedd40788804a08ea04b21626138f89d4a0a0ba167703c9dfd0be6e7731f54c7b2ee303241f84f9c36b11a898322827b84f1d88a2f76c003600db30defddc9dc33e27b7fcdbe235789335b7e2f1396273657827028e9fd762a9f6c790744e42ef085eb35a19ccc58b6374046e08a5679aa0abe5c5df3b2f59501ed59f97c73b8cd9e73db6d729950114000217f592b68f4877fdd3b23c2b07a15754f9a3c2b2f13905d74bcbc4b04f60303a0a272cc565fb4d0fb9df143928276c688c099c745eb4bb8090484cd07cc13129c2fa3e0bc1d2084b938df09886d6f39eead6ee080fd06f7eb31985cd9bf7856d8640689da3355d1779d308a839662516f312592205ed247ed71c4ecbb0801ca8db6abe31a801cf4f339690df6d965a6f79e5645f047650047c60d1793e82795f4531ef23088626c01b2bb4a05bc79295c0a840a6d8423a9b9d14484f1ba5fe74fbccb25f17589dba51326c539f81b494aa37a5bce14f6621f5de4dd490f57e7d45437177d095fce151fb6af39636aa979dbefb870f088ccf543ae38343f70cadd7bcbae32e746cbc98ac9c64b42025859d1647b23194a695453c1ff8c2008edf85b02212c405cc60bc97f194203fb3aa6c4d76c6e02fb1d2f3c792cc3bad01c772a2ea16d263d65bff6d4ea30d872e250c7f45c39420e13c4ebaf54785ab1b66ddd032aacc11f7696536228a59cba24beaea32d0b3a0ff5d42b0321cb3d613dc8892c4f41359ac52f6edbc7006afc0ed3f9cf10ae0bfe389310b3f995953f440a2bb43972db1155931a083482d8f0c85d68ebd6c2cc832d100971dad3526b8eeb0fd3d6c2961b7968ee4c6dd22b2f0f7e097be18fb5d2e035a740a47501261ba1cc2e2b500d25175e1d166da09e5914878b003f4339acb8c1063a31f2f80812dc416066cbf0b3e20f9307116e1f0985ced773533aca5d3a812bdf265019c62c182310885c925a039f77cad41d1d390f9be2fa9b2c69db406a3e256380705be3019615e5af3f1e738b1554ee0349599313b58a8acd2051c96a4a94114052c41ea2f4ef8ff78645619ba6eabf826a5950ecc7f4b3cf866a2f07e315400d37b4faa378d28533d491b8d111f6cf3c7ee52e7672d5886e912db401837d3bad46b48864592ecad788fbdb191d248f0aa13e813eb11e88b86a29d2567ea107c64073c608b1e19926bdf1a937ab5f41e356556df1e6b6a92807d69e4c7bdfccbb4ed7f1adc39049d5eaa1169490c7bd0af960117abb430a2a289fd0ac52620447403b6a70fadb87dcc95ff8913aa58e59ecccfad187f01554bc6faee89d4d8e0ddaa2726dd932028c09c849522860e815492392834483ef5588111eead79066ba101cca8edd4f991f65f19e04fb04738005b75e8df44f04c0c8e88381afb2b61ee5c0b3643dea8d4ca43d300e55a70c46f4cdfc8165c2a76c3bcbb7728e26cadd169fcd3094e3e84d7c7d43325e7ed20c6579a334b26763588407265629fc99f24b9d936a0622cc5eee39f660aba19b720c634704d847465c8436cc1b455b27111899cf0fac8a69ef76d7f1eb64b32221ed875877ece36cc2f89b2f66f4b43fb20900e2c50efe9057daec87c7c123338618e343e7f37aa615aef7e65121e4046c30050e6284f781a3317cb053d89cab1cde7468eda6b8fc94657af7c62d3bcf8f48054d5aa2b7a5b934a208332939b457b4a956896a3c1cd666d75e0f71a16430b777f4b7f22cd024723177cbea4cdb0a5b1da2b649dba5e6dfff83ef4f3abb22d6b3bc6cbd4155956b727232a81ca9ee0a55fc89f1955ddb6b1260e935a12a3508a2b20e9f658105b9281d393b3f80e2e3f36fb42f4e154071a3601d2376c92d18b50c8b5a2d12ec6109a5877d127e168ad34fe045b29a310f116b229187116d23fa0d8fa1d2c3bcf2e4ab648794eb5eb650ea59f2cd6cefd9070e97f029530430ccf16da45fddbd4a84c778e6e226b4e2b1c6e45fef16d3d0f87bec99a02f23b0b6169bfe4b3be8439f698e8bc6895706d123dd9e298d1025c46244d260c9f4e393ba94cd28e9bd4c5878da1e519f83816679d97ffc74cb18ce61404c75b56955bd265e83c8c1350ec0245f3843ccf11b71e9c38008a6dcc78d728363648597e86705b25c5ca06801779bd8cb821a6365ef593aa442e214ed37e043b7e803abfb4ca309cc865dc42c1962e8742646104288613e01a2c707418400fb7b404bf2e0d37f0c6661961b25a648c46783a31607709974a94235e7587a61b66ae4ac846475a53ec946fc486243235e0eb24c511c4584b22abc26c43d2eaf433e21f48ec6c8793f060666a4ed1751deea314aa7c7c4ed8b9755491ca1c37fb11eba554fe08c058505d2ec8b53210e1ea744814441a85e4d5e9c0e8ba99b350bea28c47c1bc7d41045ae4ab2adf56efc8e9295a1e9fb8866f1ea599d2a9290ae28f3034d684ec22e24533c45458c216db28282e06abde31351ec9afc6aa452e601b884eddaabc50e4a9a9426e423ba074829a9a0d28e8964ff2359acfe46508e99c3b4c3de4f81d5ed16ee89b1b5d8b0144fa83d4c3e8692a058d3ab639d2ab2497b19c1aa2c11c9759adaf61b0c52ef8bab47388302f7f6c678477d23d872029cbb7f470460dfbe5e9d0677dc5c3da359596285cec6ce0109d6b35a05a702084eca8bec734597f5a1be295efbceeca630b6af3a2d4d790a83683d58c1d4f128c3bfe0fe8d6f6005283dead907225bb0dba88bc9ff124f9c00fb2eae10d10414e6ff29e66a8da221790b8b4d21312549f10f6f46c8289b4628aad84e568fad21e0a7950a9e1ec75f1bf502680da1553f80189fc0d56ac7ddea82ae50a13517ec3b75467f15e702b890855e5fa55fbb9873c713594a206d5a2260fca74add26729fe12480b4eacc31c288f3ac9aca1c4ac4becc603dd5068756aa964ab81a0ba0d2f4c5e0149e0f4182d3c79fb2082431f6010bb5acda8b41998fc8e019dcad50aba1a9166a1b5d206e77ab49362eb673c6d00d27f9c65cf9eb5e1263d50d7f1382023a5b09726c6b8c983cab88cefa4490b82e809e87d32f12ece162e5f944f1b64b02f75a5d05843b251241fea6618e5f42262107d9ea54456e33086b4cc5a7269f44b27e4b809b67c105ca0e13ac7af73138f5c42998c4efa19befcca6b6bc64f684cf43cded400ad2fdd4ceb064e19e8863526e586e2da9270aaf340768c21e2a7ae0294aed4a352cac9429294b3dbf2535a95d801528845df116f8d835a4222635ebf88372bea507e0cc02a2d3a16867b64c1a4306e87bc0ab4d33e3cf4dd83adcd2a15764ecee1221c3a0cb4663a09a90ed53e2044fb3c3111d2c767e014edca50488115269de0ddaba2c8e5c041dc5b45a8a33eebc40db75a0f4609db6df2fe445d9c0598a926125fe9908e421f4b24abf6d1f089e61556bb572c082e588680961934baeaa4c97c4e83b5c8a0daa5fc520364e7dcb3083675330bd38d507da74000df6876b8fcfe4ae815b9906c79e6ee7aefe84976da9582277ef53a241f155d8eaae5e95b1d1c5a363a95f28e81ee27d7b656ee077fbe8d8c0b7a2f0b25604e7161431e0a625d054dbafe59d3fef6fc8c347d1d946f959b70186a2c80b6534306fcca4b70fd08d18d3e397a3e78a91f3334e65d33bcd88fd75e7b804ea1d241a8bc39bd28a8eeb96cef105b23a10f4fd670fdc2a2ea8496366a2af5471f85a29fa909b3c208756472955556fd404c9bff37cd656b705615d1aab6bcc0333190879186f8eed19be5b12e2527a763c0b3bca7f3211afa4add20d660d67dab8b6a69bdc377d90de4b6f7209a92673f52462c9267514206955bd1f0f8ebfec83f0b9cc41b9fe9cda81cf0b1652fa3c2aa145171d7de367971a794a7100e388f9944c662e54584380fffc243a787a84e85d155f7086b0f0c317db07d21681a82e25f400d1f44c99aca07c9eb19ca7d4ea7dd61cf3ac526e2d399483f7d3461335df1fc2a22e94dde6f9d8024afd0957dde955999df58cbeb4ba2da26eadb7c19ea464b6f2bc3f9d849fcfb250e42d2756c260276a8dd3402fa555351bb85d09e852d00804cd1860f3b3bca96769fe9cd527a9c505b768370b66427d31250919c1388b8220d6ed94885267dd42e8cde809da3e0bbd01caa17adb95213c62e34df07cb100056d54eff8fab232c25fa8e3bde04782795df7edc585eef396db37d1f3baf8a7fb0f3e651660a90765641ab1f1362465725b1aaabe6e643f9ab48c0ecbb05e91002e65ae2e33cc9ea9655a3180407c733842c9326de9da968f5948ef345c36a04c4c68d3da0aad83c8189507508656954414223c35d541fd0179dc66a7b6d200153de9d184ba31b72afe07f40bc0ed0ed01e44f15323a47c724a8cb36ec3817d208002845887126c761fb1f2dc22ded8c61d37ca2cf501cf6074f4b196b1dfc28b081ddd588ea2f2479b9593acb390a77785f39eb238bb0c70b8c7d8a14f095f5ec21574457d40e860c07f925f1a0293cc101d8fac9a58ded7c10b1fe517aa4cd17f9b960bf698015e35974429aacde3ad9dfeb0c4825a029c866a37bffeb9284b4475bc47c77bc99b8832d57defac3ffd1c4a18ab8b5a98d9bd3ba30725a1d9f23da6228ac82f067a1ab761a48caefc5b0e04a7f230a1fa58e1bd1a239387284d6981df8b8f9756d04515ee0a35c3e959c037d548047eda7278bef0b98ca1fd7cd5f861a9467a06d5b83b0b4f99a785d9ccde960b850dbfcba75d2de4cb5955d542ad25e3dd032068423f61ff086408e0faee155ca00a38e276d31f4d402a0cebe98f2a10d3d95c92c4f255b7f4f8aa060c7953c626cf12c26c60b363d2555e4f0d3c9b4d376c1a3f35ced71680bb720b96d52b4c7b531d11ee2b3d50ff0b31ba0478ec30de017e9e4bde5571d36131f0be7efc4826e6008b2d5a19ebf9c30b1fd59545d1c97dcb757e71c512c8a05ef1e32714306ab4302979daedc16be72ff5f93209195e95a634c42c57cb1377018aa020b41f9e70ab940041877891c916a95a359176ef705a6a29892665a1a80c4c8a874db6f8eb3854c9a37472e01c18df34148a1409815ae0a7488835024b08a0f4d00f8c2803bd0b9d100129ef68538a2d98700de603d1be11ea0a6aa0abd2de7f8253be6e8ec0ccd97b81f779dbf6a0126b24b044c88e6c964c45da8384e9752d738beb870a2e7902c85011286ab7d222eea73e505a387f0f788b27a1311026d4e064b3040880b5dd9d17d4d75085ba5268f7d0a0820215caabcbae91c280197598286eac2ad7549344621d638a27f640627dc408f4fdd7bed7be9fd7172fbdcc8b35fd17329b509bfa1456c10b40890787207e3c11c61da304f76ef51e5d4c40f68a8a8c2eb01932003778a0b63b05f5430bcbb4a141a922253b04176bb0e1c01f03934a913ebc1063eda07eae6dab94a0650beacab2e27f7610cdd4216deea56adce8104aa12c9f0670d5f3eaddac38f2226b8d8f8b698bb8f5b6c1947ec885794ff30abb65c5fd4fb34727ad2a5961a1dc15e4c67a0346ca1c15fa07522b852b7298c59ab67a3a01afb3e81524552e1d563c8847b5d6057fe8e8e5f0ca98bd8807cf9b38721606975bb7cdfeb2ac608572d53fba95de6bfacbbd0728fb9d0770f9e099d25d835691e37ea4d465f96d8ef61362c4ddb58213faa2afa42e85a2ce856fc16a644b436eb59b8eb87ea26a6926208c70ddd94b27e86615256a63bbd1aa5162634bbb2dd8152ed453a24716c36fd4fb669644e154dfb0d49643ee962884ba5fc388b0e21159f4da242e36ac98897653941a782ee565ea7953ac237df2064004d178850aced5cf8b5c304aa9deff9b6d176211fdf98b4152997d6480e027ba8d3c58f710b68eef923960f704dc95dd5e5c3746f1f42cd6cf56414b801500e145abb30d875c102c6e3558fb7cf0eeb88dab4c7ebb76287d9e4497fa6a3feed72fbc751bd1fde326524f9c229bf6aefe869c7af43c74ec1d7c7c3927dda100fd8c408f1227037c0d268ff31c2384731afa71068caa39163e62c915a5514b7bca19ba132b34b78cf4f8ce4d798c5720c593c83c84788f56181cc5fb28a7f2c7be0eed65b0090f0cbb00ba7f0219dbddc4a2ca0cefbb554bf5dff23623bd87420df4cd8cc3c62965b35186de4afd0318ab851c94c6428831d7f32e910f793c199faea8afe4c5b9f719a76c0a04858007436c7a01ac436e0b2a93d2249f00ba2b5ab4403fd59fb6a0b4f612c108e28f0425a822d100592364a03be18ca37460c5cfc20cbb4a11d477c344264bc1671bd39b2373b35421335c2a78a26915a67fe71dfac52d6c3fd500aee219bd3dcfdc5685580490d5520ce00308c472a2a2cb473297210a1d01a26c86b80ec072a8b802791f855ec22cb67362a9bfc049130767dc7404d1ec4f19c6875799db87e395f82aa6c50a304447dffe52e357c419238e43154675b5022f30df355238a6b02c04707d226cd999f1ed37b419813b396d20ae120e6cca58724b5b6080aec5d162a1efd275dba2ec495c998ef65e7a470517711fcbc5cd0036c434906b67ecb7920bfc0f8a49f4794032694454fe46abe1a649602aa00085ccabe860c62edd83822a0b808dd66fda0194c0afd642a6d531f5b5ca15a72a0c46b75696d43d035b23ca016f6f8fbb3f4b8c3d8ec3a793580afd748caeed32071a0d32e7090d6bad68b4c2ac7c5507572ab2d72e10ae34a4a74b766927016b9fa45a24f9f5182cf65f5c5cc2d3937810e83405056195817cf3fb7a8efa7231e9d4a821b35443baae10f2307201dcf36081c1f92ed7656051d55153574514b401b2088e635b5b8b724c58cf27d1a90dbbf83bd0445f3675d89de1f92a51b144ce2962496b3c9d24a7b0f0678aa8699c2140ab75730dfe74f0a2180119438777271dbf97e25950905c09767cc6f0465236ff2ba22294043ed0acb970679dec3eca94d8d01071b2bdc460e2c8020146cbcb55faffaff2e51d0d291ff99716239e4d70c0ab273e53d88a3ecc3a076d438f799e2fd0117f4086e63e251c54096591a1903a9d2713ec8f34d652eaf086e06cff07986bd87adecba1b32e4981c7eed9076da42fb0a7bfa73b5e4cc65c63aef02c567d8251f1bad6614a069258907d8c9470240d0ca7811cb270411fe99760a5fde9b128347a6deb0e6a241f8aaa90bd45df19da7158ee2d25cb2f5016a62df338af0e981b85c0eef71a85d6b73d951965544d082619579eafa963fd4cef3e67ab02cc57ab9e5204494975166859c77efd058d825f18f5133cb5f0c1700fe388680f36b51201c4bae4896d6c8ee237f2eec6e43d08d8967337971a551d8dfa2e373794ed42676069eead2bbf03adb81381e8af6b36a619f2e21bfeca9693051e231c335e120733f9836fe8cdec42eadad24754022e04e87840e9a651a89ffc8575a5911823eb3a78d50229e3eaff7b7d45f4f525739a5d5fa1d088fc027404e606d069c6a88e18aff3cf859e02aa8c02624dc5fb96d560824034c652f30fdd11f2a6aff1999fe46fd0b03480e25e7213bd0e01b66d947d79f8c4fd1e38cd41bb013d2e2111e31e28c3beceb2037268b1ed681957b3dce75807f2e9524470f72ec4ca98a3905f2af272e0b07843bd650bf06f11599261f1ec619b858246302cc4aaad718fa9a6e97f7f2885e48dc251a1b0518bef347edf079a9a7984ee7fbf4331c0ebb1da80942e7ae2833bc790b52dda7927fd05d5e213d80c60eabc6bf65a0192c2a566009ce332034bd212b1ed378c1fc3f2cd32c3c841d7a38fdcf3464030775ec6ab9260fbd8b6ae609cd828a140e0b98a1b2f248f6ae6b6722dbe891b2655b2ca68b80d4140c7fcd0e910d213a2556c1da51c4a7ac3b0641048b51006c2385d78ea44ac815ce069c730be2053a272f5f6360fca1274e0da8a8939dd6824faea95ab4c9f60742097ee32968148cf2899e7a681ec7c30b200e701ad3d054343483e0e65700eaa7a91fe44d06d2d955d48564d78893d2d9d28aa3d3d0d0d7ed2c5454be9537050931a14036962892762a838d4ec5dd7da5dd41590fe84b9bf60f7e2421d1bbaa5e3b48e1f290ffff46f1850af51bad93e7e65b3e0b8485b7cb0292f1f99c9bd20e82ee8bd54a80d2ba2f3e88c398d53141a55c6ba028525f99355560a970b0cf0815734699c1d48ed9c3b361062e3ed4d424f6802d7c7ef8629f2612563bec54213bbb8296808c133855ac7816e0e42b05df6cffcf3b4f4fa7430b875340d60d51c9ed33adaa097abd744028a331e861b17c1a134b7ec3beeadea4344e517184b8ca72788da2f9ea5c52f9c9cd038cdee507753f13c7416e4b84318588e481c634f212dc27654d6d4a3b7df22e5c7bb5683ecb812abd7056c8f57e0e3d59dad44909d54ddbae456c11bb1ec78678f576472f761ede6b512ed53d4c29bab88e145499184159180bd3f015374ec24ec8903ed8e9c96fe398542e256e9668481790c4fd58004ad539a52a7aba2e962661409bd4a809db7757e2fe9154148c71af5cfa8a5b5a6087d63d2d7ffb308a07d73374cab1d799c0ccc403d6566b62c64b1825af2d661e90c2d75ee1ffea5084eb954d7ce3bf20eb9503d59afcf779f957ad2245832185846225857f7b75789cca8ba0034abf8a8af3b8c45070698c8373ab9063243137c3295353a07b1f7e6d08ebe34ab0600035a28cf2529433ff06d8acbda4d0ad7d33c9e463383ff357f09d39ddefd23ec6190955777012029e466c03db2736a697e44cd9de1c46df1cfc3303254e1a34b64d3c76ff6d80009c771b80e519c339ecd5c5520b36816e80130b4cd23ecfb18b0c15366f9e40e5aae4e3ea9effbbeaf33f3dc0d16f594f6f126d37de6e95a33404e74ab8f1807c2e2860ff36f19e47965a21f22cf79b6bd3a0544077d7f892773b92a29dc686a3ca5acaa4ebbf78e780ea1fa464fcc7a28e0bb9777b72ea95de11fa5ab736e45410c2adea685573abf0e2c3af3bcb65df0038e55c61ba6590bf707a5298c8cc9ef2a139b437c2344870d8f079260058c2b103d47d408a50683632d3e568bcb907278990cb21097596ad011324c06d5396492d1af0459409f2558f44bf060c1e1d455cc03a78c24b3bfe13667e75ad07822bb6daf1e427eeecb80a0c87310a1a053e62d091f31714dbc5f727212d1544fe46227c0764281321cb5ab3a2e76c157a8403bdff841e3fce55df2a0be63ae59a8d957421f650180d8d58031c5d131517e7743472a3b0c7713dbdc4035d330d4a11d1cc9401da6e93d3e57ec93f542d720aaf09cd90666191ad874d9ac381cebaff0b0c16fe50d3595e74d36c010f2b2928c51e3889c6e3e860c8fc9ef447bae031a83f422213dfbd859dd5b091b5f4bda85eed0bf677cea477a258f382a83a99ddbdddb08f700dd2962e35ec993a6c70eb5acb703c718460c09eaef7a16fb412ad8ae6a11fc8c04ed51443e8275bfe9f0b06bb009ad12250427543b988bbe5d215b03bea09e19d7b548c5c6709bf3764a14bd183168e208a1886a1a77f194ecec95072cf8b131368fc29e92d14b13468c332abf4a9e38be43e65728ee5e956f7b180230c2ba3b8370d3e07f0bd5507bec2cc111c92a2391068a5efebe162c049e9880cb761f50ec32c3c6907b4a494fc5dc64ed9fe43490b410d9ad7f31ffae2b2093889ecdb46b9a347de34d5331b2823081146e2f8d0db2cead6aec667032272cd4338afc00e4f390570db8b8cdbf7fc6baffe39b0d7ab390363b5a119fc9d844942be14da6ad85328a04d917845974cf83b55d0d0a39408149dcad7b3023bf4d5d72725da35a5614c7a9de131ad769e21bbc6b97806a47090273d33aaf7d2630d493d1d44af5409940d88b5d2820eb1f39d81f885f2b1cc936f6d03027f3028ca00de6839e0650566e24a7517faa048019b6c315404b616d86117179871c4f596ba02561dce55882bfc4e44a99517fd02910d7b445e257aa2c19c1c8ff37a6b51caad7aecb09fa83f7e06c7fb7d474aeb4acd2932f0c77b71e90270738f6d5322b7e3209b4291ae9114fd94076613b403f17166eb644fe3c5601aedd27b492ec57c023fd9d530a373cd8ee979ce4011a8756c2a08507758adf0162022de26116e3c01213ab203a50f6f788cfe6a7620c5be6ad45d9d5ac066c8193458e2bb34a123ac0fde420082f084dce791f23ee131ff1f19836ffb6d9969d11c302c0b51a5774472a2576469eb4008ed5480228c81f3e010b5fb00dd266c627e52bd40642693488c8cfc32a6cd2930f13f1959abe20cabbc0f06fa52f2697c164b32c0ffb6ffc38618917f28458b1b9f4e06c9af6f4365f4c19ea2671c18e607cba041cca86e69e46e5d232364d41112d9d89b3808776504eb42acf7026e36e015ca933cf472ae2a42e7fc7c5b82ab494f6a62b8548ec177a5b4338ebe4857f6ce8e52b755feb60a5b289a70fd5f63bd020add4d1aba33a56a65b7358425c3a14398e9c7d0c386eb6dbfd975060a3e8fc801137f37927cde1bb60ba5d51796184b3cc4ade3384e45ec42a089ee92a5a87570efbb455880932f98d6241f719005e8bdcd7592e27e959928228b28270dd3aaf2f5cf88c5d09631ffbd7c68573a809a26ecb6dea9cd1bfe18820d96fe34df0e2832eacbb800318ec1f17b484f3e5d1596083a6c0f0516f10301bda34747c5f4e5f102fcc50f004640ed4b5868b65d3d5074d75d47116303ae074d1ff6713f677ea4fdfb57b1df2524edefc97c3aff2dfab9925674e03c60d0d1b16f59d569536a7167b7b7ae6f82bee430c7355f07c9bbfa70344853e0c75162f75c3b24d84e55dcc78eb2f0b3baeb6a02b7ba715509680901c105b72957ec17be5e60926f5770cb0268114ee4d67d4a41a248b22c2e84962f4644e96c837176b8afacce9d40211356dd9f7e0a82831c229ca665ad4c37ff636ecf18c011d20acaf8458a9a958cc25919c8b4a9b2137c2e3032ee30408cda09a4b13892af8eba5698ffecba6365eb6de9d8379823cdfaecb54c1670427b98e3773216c1dc11dbe04faa7e1c71a153ee6f72930caabe3e43bd398b7ac3052ada62345854caa30f27ff31d49ff5770d8567071922f812731a8512a696a1b40a3039188cdc88ac00d3a99b47bdc6104882456ae061b508ea4a62519aad5aaa51b653743a6294ade4fec38f43874d6bc1c75db6046308ac20ec1f721430b509fba26b1ab49957c76b990ae8baf12eed5e3bd7e448c2cb74ed7bbbe9337b971e74309863711ff257b6a3346b48f6bbea9cb86adc80895ed34e4f8f0ef621dde0d01c2cfc009ac3ac2d525010b53a0441001058c98599a8925ba159813995851515a345a608480300000060996666b68438153db32cb422b2a80100b4a8016823c84d967b1cd020cd1dff3d30559b3121f28f19235c37cb5a29654a01c702ae02cc025438c440b625b3d4fa2bf814ffe238a6a1fab007c3108c761a690e0eda69ac9976477b1bb23f70a86b8f723c0bb85f433157a4cc8e154a0783ec850c989190a029ab119b6f31a0621f504350f36e2c623b6175d90bd44d457d8a5ffdb8d89adbdabd3a7b00baeea83b4c4fb2b3a72fec06ea2c2cb46663c09c4ccae60a4875705bd32615d29012a1d4943c257363fb94c5b033a86758ba5004354f332349a0ee393c4ac976b181345849acb0cef076b0da33a9946d6a845632c21445d5eddacab6a03e1172b6b12b7b83daae46afb6b041386a63dd53741432b29e958cee4667e56d713de50bac73299ef5a8b96c23bb1b675550f7360eea3bfba3e6d971eab841419dc6ec90556a3a3ad7266a881dac5a7b0dc76e8f6c2e66629ba8793a5c877234ced59140575b6af3bc0eeafe9274f1c5c25661732e07a0be6d10358febcd5e675d2ab3ef1126555fe95e6af6da04751d64cf35b643c5d8559d6daae7212d593dede1462d43cbe6306c335bb9bdfa10b5883ae49daa61779c27fbf59aaab3a3ec0b6a621d663b4bec355b8bc103359f5182a9ca16a026ea54fe0443cd2a2667772db90239951fa768cf08897279bba8e23a49ce548250549d46813acda6da06d4c13acc1e515bf51638eec5cf3a558fcd638cd9c53aa42ff98baed5a0ee2daa3601f50a14b6af96a096f3c4e4e013360f0b82cae574ba0dd77e2351d3b07f3998f58cd4886867ca784ea0444f3b30b9b53b8466055500742cec397b867d072ca0be45aaa6b2ec7e4601a7aaa53a0d98bb79e87808d45836efb27b0ed460f20efd04a1a172bd38ec3e7e05f7debb147655bc1910b00787af782472e0a90041e47af14e6e030ff9219623722291e31d7147f6373ec7de75fd23b1c876678d3b9aead08fa63a1841b0236a01daa0fcec4c52643b9167b7ab66d23d23e98a95a875c1926a5dac1fc74610b5b86bc50ab5b1dcf375749f752e5d1a39a802244dee2e7579b68c20dcdb1947cbf4aad27abb7a9baa7f699a5b20cd7541febae39366685640ec549c612ac8bb6e8ab7f96d3b47b55e5e1e484bc5756651f5214d5f487337c82fed98cc90ade8a438cd5290535da63796ed1ca2f5785e2db468e575a4eac79ac890e62af117e451107f3b509a99aac5bb41fd0d4a09c81d4ac8ac53c327dd7cc9f61f7976fe3919fbc78fec8fffe0f903f68061f8e4ebc3c5f883ff0f36122d84fc15cad06988bdc87901501ea891072ff24f967f57679bcfce4d8b9d247182a26ba885b6e7e9ece7713f04bd7c9fdf50a88f30e6e8e0e808c1d11182a323445c3c12ffcb01770e8e78088b803fc7ebe18e9e081f7fe15e4f1ec62fb0078b10018e833d6051e8130a3742d0208608177fef0751fefc36a9e31fbdd879704304e1db391c7b808d84dc481bc5c35f7fe1e1effde8ef558f471c43fce3ef97808b2f7c9b88c5fc3c464032f9fac712330d93ae99ce24eb1f4bd410a5fe019fdf26402638ff588c97c2fee3f1efedc1b188235cecf0db7b271ebb872cf4742a011e99fc72506417a7ef8942d27b269fb193ee093bb94f67d68945794a6d0c018d4628e3d3b9e41c3a879a42273358453c726a0bb126f6b054145f53c9d9e349896730e291539b49e64bf7145302373ebf973f580f241e3ebf9d50ddb9f4063fbf9d434e1e27cbed4cf6e08267be6657132bdf4daa500908e2f3bbc9136cb2dc4d9358d84c0e3fbf99746762df033c03637ddeccfaf904e0f39b99f5a35f465c16c8eb7e998672bb4e742c5d574694585c1e15ef8a86f15cc4afa70b6f762f59d03137b2fec1cfef65d4f7f0f9bd444162f8e59b597f7e33adbf17f9024c219ecf6f26cb3ffe98cfcfefa5dd3f0e97735ff8f9bde4ae80bd9f74f1fb165320de48965fe2463e7f87b107adff49d83f8f6f284bf21ed9ca8a3fbf47ae1e27c4740c9d6234d227716ca4519c5c25636455d434b16c7488f4ee60ca9e907db2ece1818f71421a5d398102d6841358e9137b0205e932db838ae6318351ee0a309a7dfebea231a25529be688a945ed4681947839470b487246a53129549028b9fdf3d753d6751c0f780201f1637f84f04fe3f38890f4ee267340243188dc5df4384888b7047c7eb7baf788461f87d455a8528177dc5d8e3059f889708530979cca563f9efed43b5a1e3d7d0f1df5edca1771ad878568515f87c02863de0789b55c1051ee5415605165c80a118ffae41c7e7378edb873fbf71d6feb1e37b1a65fff8210256b3e5749d77a0e633260fc36845bab5f14e2df310ce5305074997d4cd476df3cdd9360c8eeb09cb3345f998a6196d4a2f7fbc12c7375fb037d0b9eb463927d905bd45d13c8ad88a2e1959fe9e9ada20dd2465909cce8b180f658302675392c5eb899bd3b833576bc7e0b97d9e756d450e0aaf3234c7f1506768a4ca3c55c3a4aa09f1d43922b5998dcc937c6d69cdb595dad38b143eaac8e74467b91a2b0bdda471ce6e3423c81a56b679c9ccd7c9f0382ee6eb95b6eacf54881dbdf8cc3c37739b8b979a31a14966925a5ddfe66642432d3bc378313b59d023527ea89e579040ea806672b060ba644e62c890c8dcc9c0f9a6edec6286170c9f861da39a974e564a53a1ad1d3f276317932214b34491172537a7a4cd2d63655e1edbc2ae8a6652bdcf521651568f0fa3295981c234e73c1891393affb33637cb6811307fb235cfb8d680f22e9866db683692a6d8d0b4dcbd3c8f5e901ea6ce23db66a2b3192accb3e0f2715877d448d30c339d517e5c708a5e7cd605ea6bb828672522d06e7e2202df3cd6d65b696717af59aae595ae5e5b9d819ae62024a3202fb423360382b7e2a52dce9316e44cd2c57bf333b64dda59c42b46cb3bafc92a485ed3bc86343f833cd20e77662e5af14b16a7476341ce3eddf9ea6d674b27b5beeae5536831afb34d754593ef419ab9825876ae66e6b215cfb6e2045b41aae87aadb7592a3ed857c39694541acf8a6b51875dc781d961353e3fb7f82c967bc48ede0b4a9c303a64cf8ec95559a54577f3b491c87ef049efce9714aff9a62a3ea7a5b44a899ac509c342b745c9fa2a6c0a530cb65969f07029e1e484de58a852b8e7c916cc6a859424847304807bdc977f672e98744b6d5c64564eaea5c9a8e53991c9493240b0c1d35b0eaebd30174802773b7772337263765dca709542aa6371c078a6ef409484b547646872480620015b6a55f1a6bea6701b6aef51de34992673816849213d984b76774d4a86e545c590eee01930370d9e967cf99e367fc12d0c26155c1d2ccad7dd06d36b9c8ab713269c0fe423b22f1f718168dc1705167321745865d4189e32c74e56afb692941c347a4773bf33aad88434b8ec9a6b2323695ac9745bd59127221653a5180d9899e8f1f26df5606c6cb4f49f90b7918fcc7dbd2223a2da2678409d5491c918171992ab2ccbcc29f5e6c459f9dd463370b08cfe2aa27e013ebf8bf08ae88e6fef3f7804875fd883c3233ab2c4715862062dfa8d21832099545dcb96c7ceca421b32c6c396693a4134aa5a1a70ef8d8986368854bc203201002055a36c47868ab6cc415b2e5dc5a1a889109dc4442b5a22e4706ea8050117832a8a64a6945655b5a66585915d73922127f4415ce451c07b086b83839f100a211fb17b28e916a2c231b7fa645f318e637a2b8e83337690a288e17a7391c352750b495fa0595953352d4ebaf0e7b7104fa7af6e2094fb8766c687360f5741ae61508cb662926f202a6d0554f43f7c7e03e170abe82cffbfd7bf2f09c51488c7df4ff21f7ffd6d41c8ffb117611a8b087bfcde8ff8b3fd19e47f03001b1f3ef5dfe0db10d1ff909be8c52e02fe1b37f83e374143cffe1b86bf17dfa2076e82861ef267e8e06f9450ecbdd8a742fef2179a06421231018ab870d05011250d3d6145617972a1255834b7a00c1a9af522001afa237e7efb24b1c0aad46e299d534ad94a80540f469086cd335dffc8b1018a05a894895d76992998045014b319000003201c48721c86514814d10314800432ba4c5c543c44948c201286826150180c04040261000000008401804010100e85c2b1b990ee03c2817c31ce6825660ec688091fc0e3c9e6a86584e2f0e01ad0c31738fad419e1278f954b2c88c61cad542eb686b24b815a55b11098461082ce2040104b916820eddcee1f734c5f6e04acb792c1a3fd619f8cfbb7d28774032a4d3141d251c101aac70fc61e81e7df15668412021c0d2788fbe877f439a9dfe3948a160670d234c37154393c879eb5cc757183ebbb2c30db4c2d9bedd09fe4909d8a34b8df2ba821ebadf95348ddb7d954bc2861b1b5ba1a3b1cc40c6abc3357b4055a1e4316932a1671e382300fe71826960b42c08386d2892058490a7b7a2788394de48789dd22d9c995a6ef58c8e8690cb193c6dda3c88a4a1f005335cd6150941298f3e4e64a20c08eb447eb658ec1fbb98b0da8ca7fab496b25a9c633cfc61dd5f12b4ea9d70ff8920935f41272fc02404f44367e59d5cdc97af750ccab3fcfb6d63593fd9b56190c0630c0270920d5b3cd4df4b2fec4e68daaeb6b41a69b1fbdc066c19de53e3217938c61a09caa825e3d255289927872ae985806647853d596210eaf6a8d646986fbcc7dacc171318b925a2d6458b9ceed3cf26b37da35b00ceb597d8e9dbd6c6b386ad360f94248acaa8c1b7a69f8866c0b84aeb71fd9bd065b9a56c81a6be902417a2886fef4e16e7d0333fca6d3e47c7c35154a18d391c319d24cbf5ca96a3ce3d5d5d2eff8826d23e737afea3714696e5eeb1e0d65163fc9950a5318b7d00de11b50fb717d0f6b2c95ec5167e10b5afc245b782d2da696ae6cf746dd82211a5f403fbe068ae30d7a785d4a6debf4e77ae57667841a510775c41399629279c05cb05a0bd1dc3354ff1635342a028a7de030173387103b7f9ff416880d47f0de9bb08ed2412d301979143ce1cf2f3db44761e036b8f7bc18ee1e0598e3e32477803e700111e2a3f84325d86309d64de9565737035f8b7b67491136b9bc51c4ee3da86f445c947a46d094a19821e15b2a921137ed3df4add0a6c4292d1d817a10d131d0926e0788fa4fafd199d6a5621d26388bea719bf6f55d3ec943e740632abf7a24edeffc69306e866c8fee66f2c436535d1c145a8e5b05f52d2b260beb9e8f3139b2bfb8783eadeef9a5e48e4c9effc3a1c6204e329f8c3a3e67d1f8981e4b9140197e4a372be819ace7f4932d6cd81674d19ecbf3755e0124dc968ab3bb30d616e933b33c87942a3a725e5edf481475b7fd069a479bc1e3dfb583317f0dd3ffa73d96bea4d6155f7eebff2bb9fadbcb1fc0e7afa9e5f140286c41159a700b4fb948f011d6b0dcf00b6b5f3daa8424f85b3326c84ee8e61053ee95e0fa680deb3e24c1114f66977440d490a17118a11896ad0b5445b7bb1efb99047fa688f107efe469124cd8a58a14d96458a460c222d2eab5432bb1bc2c2bcc4a6c055322c997eb0aecf0f53189484c1289fef8b43904ae2cb5704737fff84955626c4615c4799ccc05fff761414b24abb442fe13eb1f047350e9744e41439b587db6323f57105053e92f5f3cce973950418c4b00e33ae7177e8144d143367a322a06dd395540a31a7f944279bb44e0eb4741df2ae36d93584e954090e8ec4b16e07cce434473cc5476c9fd2a9bfcd5566b46a6e2413e12a9f0b4cd2e1aa61a2ac19b8d6381f2811f998b0663f633b66f5ac883d784aba823587536769f7fbbb141d03871ddd7ca37e99760ed245be2cff85a5b602c4acf1504d17ada740da15107d4a5369ea15692aedf396704acb3148991d76c45629d9ed989f336efbd03ed361280249545f336433583f43552ff3b38f4e2a6743f05dc68e682531a13ad86985741b7e632edc489fa82bc0dbeef4e10cf5c98313c9266ac71c2127420d8911234421eb4b880502a6338fefad8df202e47e8b4b0258ecdb0975a1cf9c9b065b31384012bc7d75f7fa349408b28591b20b8e3c5cd0add60382a12aef7b5fcf90e1d6612eead2ba8b9271861be3e4dd16f956c036819dbcea46d462e11bad826effc220ef225a52b7cca4c2603886376b63c60406379460c11fbf49b6c56194b0407bf2d11dc65f4c8a7d60bf9f287aba75121c70832095b9eb01cb5ea9a687590185ed6b7c4d8a99656be5f7068428f92618f1488c11911489f1e976b3c96847885d082069b10231ff97d84d31c47ce85a87161bf2b12b4784ddce297ffda65f3e2b6d034488c484e9796bd201cc4da309dec1301916c6cd953f9f232f37bfc58a5b4a4ea0b6c6dec596923e820c7760f69df724c89fa09e40c7d3ed3950035d591ea8116154533ce0db7aef25317f2baa539ec377fbb7555d59bf71b569569b1b581bec7da442c5e0b4d629f9fce0478bdc40237d457c36663d9d9c3a6f60d9b80c0b7fed3a6464c03376fe36d3946e811faa1eefaea5f4e287ddca5f07b9dee2b049772e00611207630f2aceb63472289538d6bc268f54cc1d1d6a927472902f601050bc489096d13b964f224e18ec600f5412496c6344e60427ad5ab343a7a2bdc8191ea6c2adca3ed6d432b0d0d638448ee794d05cd2c9d91a2541759275a9d988268b229a3c0bc48cd4750b245b16d000c8cb9c877b73c79c91da26e7920b16b8e0dd0b43a58e1605ec4a64b28d9c410912417044bf3b6c3c90ad7f076b0acf56c313310ba08d456358110d4b7f305192e44182d93ba68e4229adddf3149f823682913830c155e08e011340f9231c02f54a5bfe2028d493c8239227521d94e28dc57345dcd096fc1fdf68b982017fa4b188c6c4e637252bb74d79280a1b97d947c06c0fea239bdbe41c85c84a612e1862747a5ece48e707c16688ffdf660c90edcd336d6b1e57636e4a14631056404da0b6904e117aa1256dbce2ea6375f244d5e77b0fb589334dc8b8356b4cfe4ec707bf36cc03e3370ddb333d14a66cac34ecd1e8e49697467077c28f60aa9022a8758428c1f4006c86e7dff622c40e3ba53e9c7c2efa8da74a2c7ced1bc6e85857107c3544ecbd4c55c9e57c3241f81236a059d7c3eef737788ab116567398af47211050b4b04e40646dbd3099824f309faff2ff095c4c4d5af00a617599cc7fc1e21fc856785dfab9162feb535e06393810c2cbf3e4faeb2db481f09bc54bf5bb392c8ba0988e4e377ca3642141ec4fed997eca94d0448c599595626abed054b9d3c5c65b6ae6ebf47ee7f7c7f1eabeb3004ddd318aef21114f46c6d02d3f98145cb42d1b78a5320285c0edf62cae572775d050ddfbfa4bbac92e1ae7fae53eb6802f813cff50c0435d0b7d25a0d9d3438574a564334589c5a523ff524a670e6aa70ea8e6aa9ed94e1f72e1977d4223b3f329526fceccd942998b575930a2ec9aea4eb5fb63984d964067cf236a458e6ea952ab547978bbc8f8012deef6640c035c8647d8c6c0062bdd840d3b99cb85267989c06da7065957a9fbd55ed8cddc88087a739c7d255add77fc023cf8faa44b9543dfd3d08bc1494d947e4e300dcd0501b3f552d85963c83879ada345c86bd7b3ef339446b9c255541014ab38d2cd0d7254a380cb2506fc31fe8d39b30b69c036debfe1a18c46b70978423bdc415536d0446679c470f40c6455eea049e3075d478c3483e71c2620f8131e2325e41fa6b2f8d44178e1702be22d9072c1b31a10b0c011dd8a56df9ce197027d705a11d4226d9c571c031fde8929b470b8090d1175337ed9d080a27db53d2a343d52dee0e50cc6fd7327245672280733938b11338bc531f02b1619a487a71a31ba0590449e6b000d8deb1749ad7246e953b978faf04ebb96582fd20c7e42734c8b2d121c4118d0aaf4d0222aa1e1a2b3f8d18d87862147f0b8332b6e034dceb66eb6817ab5ebfd5ee2335c30aadc0ca2eabf03678841d0cdea6901ca6d66ef44dfb24c82423c0cf769322c2145a554fda619ed6bc8203f90e9540897fc6905407d05f5a589a3bf0a848802b4a9cddc96ad1afc2d5f039c501b0ea64b25117db8926a03156640b1557a57daa2cdd2c26bd456b1c33b0e99319cd7aeb535a5f5ccd9a43ac6a31ea20dcc90f140ad5375fca4cc39ed0d6ef12d894140d99f6380ef01e2f237adabe544136cb244b8b2d1364f41ef61798cc3eb870b1311f1db966684f8c5315ed4e78169e6d8a28132d373f97b622a170b8a76df533eaeb5fb87730315fd008905e57e0d58b1c3e21f19967d45fd7925919688e910de589d50797a35a96773bb8f479b931d49f41b21cea9ed02508fe000c51b836676e2200bbc34d388cb43000916da90c81a429d56a0a8169272cc357b3932dd4d559b034c33dd6a7deea205ab33e3b238a16cd115e8995b7b366bd7c166c8e5089edc0acb58cecd728406778686781639c805defc796f2c12f9dd08650b0db7beea371888b973509fba2ebc93dc79f1c138d09503352c56efffca3d9a95ad4b9931963a26dd5e29f72840e3539501243fdf4cefc2823483ced29f6ffecb49a9f9cf8741bb94fde083be04beb47555112274bfda5286e68b85ad58a21cad2d853f77e455cf5de7c16b2ab0d053c9de6625629dfdcccc4b2023b47caf694d1ed9f72a98cf8276d3e0b0898f47ecfe0be02583b8e5dce3867980f7511dfb84ae2b0876b110e976e281e7e56491c78b8fde8b9f8db2a5f0c73d734d50285accc85c8995c9782e1abb59616958e56f252340ce06276b1a265baa142219fc6085ce4e7cc086d219223e079e0e252141e5eac150598cb922a82d8577d4a38c84af92f19adf1e8ccf6b24a542fdda876264fab8b55b92756dd28c91482dd6f550eeaabedb190d37a54fa20107eb737ae9421a3da2cfb092823e03a1171639a6b59368a6a6a2223d74adc88712913ad1fe81d97884ee2449f0dd88b439c33b4cf1517b8c655dead9a95bc178fdaa59c6d745a98b8231977e33bb67b63dc131ded58aa28e4f67c82f1671c638b752c6f8cab24abe77cb024404e0985ce79d10670a15954d8378b7c8ed2259a12bd95616bcb934ec89b888f2e263021917a8ade37d821888de90e28b79511e311367fcae90a0d4b6a24b3a1583205f9057dafa41450726a7ca9234ed28b126a525fc832f1bd5768c99a6691cb563e6cba23c49fb37f77b874c71fd8b9fc5c27dda1d1c9eba4afcf51e896d011ff912ad7c5bda483dfd5f9e49b0e7fa001290db205ce1bfda0e2ace96fd63e3fbf06526b40a8311400c7ce3bbc1d35bee3cf732ef583827304c6cea20e4247ed4c76bc3b6747ab68a1b7028ff4bc567d0396fcc90cf8a1cfe3abae13f49e41fbdfc099e651e8bc1d9421f488d4e68e16c101c71dc7ebe7fd7f107ddec0d179f4a819b43f4af876fb4775fd27d81c20d1262020740778395f21bd33fd8e3fca19ee0fdacf3d503b5674ec2f4b07903be3ffd999a27de0b58ed0f633cc5be850bf23c4d69910bc77b4b0c83a1b6f4d0f7d65065da37d82656d5f6fd37652bddf9937d8db7b86d70795e70b101d4e9d75a7f41d7fac33c43fe83eaf7076e474d67707089c33fb76758264e7e1fdeff27cfd860e7cc7dd415c9f0c15abd37b431262d87ac4445d76f58250a6f3ee2c3b0d1d7b87ae238ff620eddc74141d303ae2e9fc66ce7b8310f7fc2af0289ce130e8283b9ee800aacedf2f6b1dc3ad925935f20e1e949367e26d0e2fa8b382f388728e3a875e5940e5759d12491d282efb6774b49db07602e9b3df9f7ae7b77adee283f0e70a823a359d699d97ceb38eb253da4ea07d5618fc3e9dad9fac0e20e1d9fde20ebeb377b6da09043a6b1bfc09cfd15f40939f660790e0ac3dfc6e492648da327d4279921922f7b6fd41a7a7d35864b9044bc3400e190854be5a506b2721ecfc8841b0243ac2530663a2290491f6ee033a1ddfb9c7fb0c002c757cd3738081b6e13c9305bf3be7523e487a2e00adc3cb4e20fd4c0f089dbaa3a763ef00bdb46832c3434673efea07bb110424c09ef5f74827a83aff41ec784a122826272f70e641b0853b54d765c2e13987d0482c48073a72b18e7b2433883008c8daa020244fb460abfd3a7482bae73c9f41cd597b01a15c11acf05d23ae3c5d676488cc418825328c89bad0dbbe93dc73d63c384918ca0fcef90619c5b191075d90672380efe4fcf80f72cf0f7d40f2ac0804ea9e554861073ff4b3f9a09ff25c804427171ddfed5cfb699db763cdceefe5ecc407e59e1718eba875e69dcfced9597742db11bacf31fed6f12b74acdf6f4e14372e43dd88bf4de777fcfc0ae44238430c7ccfb3ba3fc89ec1f901d167bda1c7ce79d40f92cf4548d3a93b1b3a161d7a1d9176023de7cd5778e7f7757ed80759e70a6e3a3374ff473a9dd3f97d9f035fe86821b6c12addb3ae1f249f8b41d01d0ca867b83e483f3fffb9e0a40ce7e312df27009ab2245268503a8530c3eece72878d64fa19b7a78881c80221497904d28264356eeff9cc4e4c9d707684d69f1954af03087cd6168e0c9f07fa8a66e164439a3040edf7a80e25eee2baa34e37bdc2b33be20e4365d67bdf0702e18b7369a09fe01c01a5a3521d61ccb34082224591d0d9041162e0d6fcdb23fc76ce0b3fe95074ac42030a7fe1842dce0540a031feea4c75e0744e9def7400a967bd8210f25cca07149ca397974e9073c6a03ab490d8a18f190ef6bc6e4d93a59c0fdaea2f96842c47fdf14240ea3ea33258ea1a26958118d336eb56e221c903842740d2650e8abca351fd3e88daa56277e6e9fdd9656a85f4446948aa65f71546cea5f7a754004f64c45a37066b8a929d19997bd2294386507b8cb17386024358b942d0b20794e9a573200853957c575a6c3e130d3048479e2cd5db7221322643cd4d3aecece746dc2f7cac1634482c87f6b770e120f26626a9c67438039337c3a7b64e6ac1008e9dc240a20c6d237a2ec139108428f99be1ef5e1c074c6294658ab39cd788a959a3eec6b71702d8fe46c781ac364ddd6a5e092df7db83c33a966765dc6d05e53c00b3804c5f2c472c8159ae4e1ab49d2b0dc52d65c9333cb94e736913e1723c8732a45f82412dd25f882399d38471488e45d9b681cdad5811cf8402e072455659a9ffe94e92aca773531d080458e85c0739419ce1e129f8062a04611677156c3097ccb5d6912dfe2df35e6a41bdb2cbd1d182d8aa609d53a9ccd3224573b9812a8463096a41d6b2209075bd589a83ee78d4ae12ab4295fb8d36fd2379a7b68ab90c85026ad475615f40125a4d07926d422dcf43dc484799044a99eb108cb557339bedbfb77c5f1495777c9ddc827c7e57b762a0b9629d226154affb5f0b109143ed1bfbb991453a6b13166172e09f95017c18940f7b09f1f1dd2410635b6fb2f7965b4a999294017906b205f005b3c57dff4ac42ace6543487e70c48dd37fda59a64fa03ca11fdcbe1f53732e9d90fb80cfb2e5a6cbd2177b84fb49eb943c5b9f65563f2dac648d252cbfcc305bfdc2d32d95e8d5306bfa57a20764d6f4bfcc306fbc1f2f3c7de395ddaac17bc17475dfefd1305ddef77b42bc193c1778413c1e0fc87485df6f4b9781e271af05d375bfdf9361ba76bc1f9e0f2f06af9cae94a50a883d09a4578e108b4102eb499527eb05b0c61d3b7a3c09acef11830499577dff67afcbcb4fb0460a21a05bf2e9531001ce29808f42f1e04c47776fe74ef553a2b54d9bfe18d13a316dfa61446be545b438b046a27d62da40316dfa43d146316dfa51a23d9a36fd27d1e660daf483a2d5c1b4e93789f68a6891442b859d62daf463d152316d6e9d807e19714e03902a96382902c8989c57a28d62d6f4ab444bc5acb1544c1973da236beb8904b59cf8225b9bcbf0ca59d38f711896b756cec6cd53cc4d7a25379e68d3a6df250b49144fee27bd32f7bb2075bb1c39914c49148f109d9d971d3a4b997449ca1d94c91088e47e140f4a084a06828b47724c7b507ca81a230409eb6c4629a5943a80cb608dee6e2fbd74f7a6d5f05a6b58cbdd5a0d8c7344e7f98b4421d7ef7e9af322d7b1ce725ee41efffa94b9fee7d222ecf7af3e763dd3e55fab8f9527ec6a61b15cbd12e8ebff5a9c2c56d7f9b77f8f78dae018119235d38fb9912958a3e789cc19c370da4c98eeafef0ddaa8a3e8a3d8e0cb053ff73c3c1a09e0e7fe04cc7923ee46ece02bb441473a6de8376a867d9d4cc027e071c638e305bee8cfe7401b74ac1cf8fa66e6c09c98ee18313a10e7879082af2ebb47b137db3bd59f21a05bae1d4340c911e449b7640e07dfbf6c587f4e5c7fceeab59ec2473d8fd383e1a3fe84704485e0eb7573fdd95b5edd7b94524a298540aac6e6777777bf38f79e4ce7bbbb7b055f377f4747b5beaca5f3adb5177cd53bffde9773dc880325ec5e6d451c281df8f2dcef7f238d0d143e94e93f93fd4e245b2ae4bcc8f7735e643bbe788147b216b9588bf2b537d7166f5e41bdf1b5a9fc4ce0c2908226dcb7143ce18e78c45eb325a3684a98b9ff35738f377283fdf8d49f5b859797fd5f5ea65bf2045f9e978574d944284fa2263da63c899a20a9b489904ce22665a6df0496c98a6d9e443e5964fa552953a40fe7a7a788e24c1f8300867012f90c65d234897c84f224f2e9c97912f9f4e0a383099f0f916906ce5f4f509e443d434846540a37d4404bdc07223e08a93ee8f8c0033359cae2c3e9e123825a9762887b5bc81105d1101f687003951800c04654048825183081e2e3e1e4e21d5dc589206850bea0038a26464cd4a00517105144468bf97608717689129808b710dce55ae829305ee8b06006430d6234281949a2c6440983d1133a9f0e274b863c7cb4849639672205ea87104278bde252b8dcaee5f15305cc081341722c2922cb941d90d072832c98c04ece63081a440d40923c3184092c7c14e1e10644c800851652787c399e7078079c96d5d5c103804852c321c9952458c04316a01854d95102067e382ad6800c8ac00289500b2f284183081a4cf103050c33f47cb0192a4a5b7676c8420a904f8a2080b40c5142871477cb9576242b8e540413235c860072b96108cbc140831c749e8002ab02841018600287233b5c208203ec861abe2894071be76d41b10d6a98b28323db0f0e2ad3ca0b3e9e60a108901e598aa0b49881039621927414c103061304dd7c724cf9b192c41215ea992bb42d5908e19061082ba440f1e343d4630b26444c5547ff205ba285161da8e4a872031ab60022881938a1e50a2bac1b2d092242104df450c40a123510c1f2444626035a1354ccc0a78622ec65318412290a40b92c4294a80ac124c827c4954173188f18cb124c9a4009295172a4db00196650030f233dbc3034e489a1248a08a2b16024968ba076100016e8200a0d663a58986201263d9a00b282051c5070705a62b4c7951fa2c0c109901e769853b4f8d163871638b48045c90f272527c1852156627812a588942119ac006d61238276039399c9631863f18c68f9388ee3eac5583a9e2e82cb6227125a9e10d9e2881990da11b12a925002cb9392b653b10061052b907e0c69c202202670840db12850344113fafc8710f8864e922a4410d14356e589d913259ad0804410131e9ef030832b2e3044942b43362458c4b058f2821c10d1928389e6a1e7264d31a9382a12c48f155ba62c01a40543418250b223040f5180b4852b4f601886a03532eca0c20a2778a04287034531620643961e587af001009e154290013d2ee3ab5d26b787bbe42ab93cdc2477365d31b5af277567a9275599bcbd8054edabd5a7d12e731bfc061fe244bc881b711ca64d7b0e0e03d7c19170a2c7e08a2e03df61daf4a7c41e314870594e009755a2e390127d87eb3bb8cc26754dffd28e5067e70ac94d4448957db4371c1b849f0da6310c60a5e0eb4a80c532993c26e685c11ad6592b6732e11875e00841c2faf5d5ddea6647bb93bdb63ecdd52bd0e74fa290bb863325bc94c4392b7577734a4742eeeecd7324546bad61ad25294fbe9733791321e7224e1321f724ae39f471a6f46b8d118284f431f80273a64db754c849e5a472fd222a924d6547a624c618635ccb51cd69e3017d384f501ae31e76cb446977b7cbf6c78b6362ac0d438c6bb52cd6051f70efb54fe9c781ce8125cc8c82df97fd8b824007be68ee7047bb7baf8d2e05d90a8d3f0af4e7fb78697bb5f7f230fd77c209347d1cc7711c0df728dc1182843477c4cf514a2905f37d21a1ee7677f74a6badb687f6587b692c76397216155109fd6d2b53f7fbdde87df734da411bde3853f97e4e2a95cad78aadc471dc0aea8dae4d40aa6e2d7d5223b5d1fc96e2b0e9aa59fb2ea6f27ff691ac41dbabbd1ceebccf049e50a1cb0b4c4c4ab562c9cc9035ec48336da875316de80b0fa6cd0776084b09a95823a4dc4fb64810fad6ada46e298d16878a90b4b79b854d1b0ab3b7dcde8dcb41b7d4244949132632c9aab50854e02746afd769113e110a097d94743431228a1a8ddbad40e38522547050840e11235b163e3610e18328098b92167c2b648ee38a601ba6c534d70a2a24f1602b1242168bc988d56008922542372deec2f7f598516446114ca3518296104bc8e549e44344888f4f8e4c3b30e4424f10772fb6d59b46a3841e2bde86837027ec396aa22f519221a2d914578f194d84383d426e68061421396b33b00849164e0b218bab618cf10760b0a6e78ad13c897a74324d9e443d39f408b13b7352da187745bd6cd43afb065ba97b5ec517d9eb38aec39c576b8739cc5955d7ec89de10a5b74ebfe1b3ecf3fe75b5105e7f3bd6b16bdda28e95a86e373a79be6561def4b4319f3cbff64c5be70db5386cadd36271b8f82277e2e926e4324e4a5ed38f8ad5c8d3cd5a8b390b6343a7e548264f4aa79bd7348c0ce614d4b4a5a42424242312e646c2dc4c9984b9e57e981b4c128c52df58ff691d366d54d93d366da837d435ed09754ba85bde50b7ba366dfa4fb7dcfd2858df90b696fb9674523a2dd9da74d5d72afbd835d5ed1a7d3842b4e83fcb384fd06799467707beef04f4b1481d05f8fd3a1eebccc513d287c56a122fca7646a7f8686cccb7ff9afe1f8ad26e378da66ed5da737430a74e6f5a6bb7288d7dff1bf963b9779d6d6badb58db18d971549f041b6ef9dd375d20ec117aee0abbeade2b4b1e0ebd24880fbfb3cba118fd86b2ef8b2373207beea9cb8f33cefeb3acf046e9c31c36b3acf7e2cd6770277ada9ebac7defb3b556dbd58eb8035f3de7e781fd2670d4148626d004eeeffb09dc75da2058c2ccf6bd561bf6c11ccf53c1cbd5cb76f44cfeba913fd064c2604e074e0c7a273b8215d41bb5515abe0fe5600ed78f430bb9fefe9e2b61e62e04748bf7642d9a73ce39bb1e4a29a57d94fbbbbb9b1331cf91bb574ea8d66a95ec25ef910b8e9c882314e2efc00670d9f41f3ed96faf28f70b6f24bd586ed216e51ec9a25cc713d4f207ec0c7fb433e2038a5156508aba514a53312bec2c53d495d96ad451b7fafd5522eb57a28ceda9e5c64a666bc6aa4fe78d3d6267b9569791b0d61aeaa86fec91465d79127f1464bdea79c8fc6a842028d03eca84199f5953d3e06bc667b6fa532ccc97979f13ccb9f98a37af54aa5ffd09aa5ffdfdd5988218dc51e6579f02d68c0744f87dea29f8bad9ff657cd3cff4ccd05c86ccf7cf27fd5d3e25a662a9d265acbe4f3acc8cfa396f2ceaa7cb50a5626478cb0d23439f95c365b74a5e46b266da373d2394fb6b0a944cc55a994cc5f28ccf4c6dc696fb3daf4672b3ccf164abc3bc99b3696ddbf6770c28383e6250e549f4039597e96f71cc9b599fd6d92c6a1db3e50deb963f5d3a922443e231a2df525a46c7ecd3fb747c91ed0f5b5a9b8d756bded8d2da74cc1bce85d9f276aef41a7f8cc390c5f27fae63d8c6fac6e2982d177664fa8e63b668f9f647d98eb1fbdbc0d2bd7419268f2b3dae2c3dae2c3d7f972c747159fa24165268d4a8f255f15b9bcb7031ea1a7f2ad8284462257dcb51dc1d5d6cdd720f5566af494eb4e9e2de3d160363a8ef6f327a7fb09c2e12dcc9bef4492ca4d0a8f115bbd6521c078e15619d4d9707d45fc216666481c3c29359042af04313225a2c8ba0737fadec4a12b6b2d77ba2a38f61166d4ec156ea9303b94d9824f40b3838b6d09e944f703c9145c1d172b560d25d0b9e4d1b142e8ffeafbdbaadf55a6befbdf75e1192dd57dc6e34da1521e8006e621a1b6d977cb55a4ccb64302f349770da6c289c242a050bebc2c6dcbe96e535d439b046b517b3c20fdf4d17db6bed75569e79526b4f9c8bb56bfa5d65d87ebaa40af21dc3cf3aedea74d569d38139b46210e7d05c614cb56f6a9e38871d24659295058b0c6e394ca18434850eb61c0d093b4f22275038b1b2a3cb93c80916486005e89821c70d499438c243451622b454119bb2240553e97c8b12239c2cc91ee449e424c80910b2465bd637a7555ada69a3b171237fd7d513ba7a4213155d3de1a4e37c8bf44d18e4a5b4ab575b6db7d76a6d554a22eb9212aed5d6cbd9da9155286848a813c92aa5e6711cae9d672b4fdde956935548e5f9becf04f69c4c0de604d18e8c908e926ea1c965874eee7f89c190bee3e567a2a403f5e4d0ec555badf734da6d5d6ff781a89718950c8d18e3525b6d8484a49700c5088a3194e7b78c26b58c0b2cd97a47ebd87aa785dcfaa60b6a19b42c874a292595d28822e5f99446a34268940812da920d60a43089b5b423d3f2027d739fca4b610d853308846e99c790d135afe9af34994c269369b29e7a9d7d2e21ea04fad7a56841341a8d1653ee946559c2dc97d96cc96c369b59186c070c068381207a7081699bb669a3952693c96432c5a8c5c4f8dcd6a14ea07f311df60f86164473da47a3bd943ba5975f599abcd96cc96c369bcd6c0400c345984ccbb22c4bdaf77ddff75d80c42cd3b4751875024dee82b92ea405d168b48ef695e54e599665e93637729bdbdce636b711c034809a4b69341a8dd6755dd775655896256e1a079afc43711777345a108d46a3d1ca72a72ccbb22c808d4ba6b71bc618635c6badb5e28f46e36e1734f9d79d2ef6aa47a305d168341a2d86a900269c2942ced432996cacb5d65af38dde2e95795666655666c15a6f35e9abb7aede70bd71f576ebcd0026031ce0000808c1840030278404dc24e0e6c69bdecca7798e5d6bfdb3586158233126db23d2dd489a4c782463625cb8800102085d7319964a91910d29694909cc76b4351f42f2c26a300ce61a07623833273a5800948593156c2e001d013d01248513201d80860029612baf1c61a40a103f7cb25421b28203c90e33004559800c44704005902355645562e03132d402161bb86009084a04682d48f8e9b0330488098e0ed00e0e0c403a4042706af8a1fdf8fcf0f083e4c7c88f0d3f3c3f527eaaf8313a32297ee5cc1b4da9109892e55dc3311cc3b18e61201c73192095d28807499674045cca7ddad1f3e960a00face1d8c05ff5c20e56f98adf8e0fc624244f1f2cf79f76fa26d63754a7ec1b3ad66ee1578529ad111f8d0d7ffb2f77af31316d9a31dd634869e8a3306f70360c31ee582cafc13726c664b2ee6168eba3605fa08dfe9cf9feafe91c9883c1ea15f9e6bd1cf77ddcc77d5ceb82353e9c1b8424ceaacc44f88da4cf32f71c48d33d0af8bfefb08dfbbc37cd39e79c4a99a39452da42dddded962773efee5e8f32c77dadd5f6587b39ee1b71ac7c228e951338d2d8c0197c9cc191542183a7c7f934922aa0fef40eda40dda2ccfd89fbc856ba4732e745991bc9fb954c412d7fc067eedf8f9751a98b494886dda2e324bf0983fc6c4a7da3ca534995faa566ed087576582e8445647e3d5ed3f88bf209752b25de1fae0fb3a6ff8b326fae92d9ea3bebd60fd3f5e43ab940f7e736b93eb7e7faf005f48adcdf39a599a0e4fe10e35a6bed3e2cb91ff50dc9fdd493e2c96842b522db114c2693c964324fe6c93c9927f3643299a8cb819574107ea765ed8872ff89fb463ce227bb1cb95fa6c3235692318c9654468fcc7a60aa9aaaa6aaa96aaa9aaa06c3300cc3300cfb804b92fb5af991fb29875979dab824b7d65a2b375e2b370683c160b0d99dddd99ddd55cf6ac90a68d5b39aad8256b4d52a3bf1a45bb92e6c2e6c2e63babb7017eec25dd85cd872d7d13a315bcd7c06a68542acda17ad66eedf0726550a34dbd14529029090b4b072babb85b985b985792abf2881120c06b4178ee0243152865284378447c25b484b82029467f22482920494a13aa3b6f6d29b566f96ca44a02124674d041a44a021cb47deb079d9b41b95a15c800d88794133d327d2041eccc8b0fc449ee003d64a95aa2712059f8a817971b1273205a24b883a81a67b22553072277205f903c0884ff49d501a5bcfb84c65ddecaa6360d9679efe8cc074b19e3e954dd7eae9d3db74a99e7ed3a62bf5f4bd9cae7efa6ec34fbdcf04be5841852e9c28f31feb93adb28c917c45f6bfe1152e4c4fba04657f97201af0c9971fd93ff563e6f4e48b95ecaf7ab12283fa154c1256f8e427cb2eff7598e671a699e1b0a8ca32224b8cc0b4a1bf12a96cdad05789f4d6342fa7cd7cf92a7ab569e3301e6cdab866e83ee667625e441791a49388a436645a4543f118bceedad63e596dba30fe640eb03132d4b3599897e0ed9ac46da5f764d36ea6b73335a70d25674d4669a4979c3be1b669435dd0883332b26b6a9125ae44957db226a1c1f98e16e6e16061d34583b305dfd2b1cea64d43a0fee8ce3d69719c66d3c66135142c4848c6d43c58f68fa94d17f8fe1f28da1428be744fcedc6664d3c653321790909cb9a564d9ff03d9745924ac586731b099543689337f286fe696fd3b919cb121fbc7881e2ce5373e5a63df84f1b9704a63f39e7197f1cabab95de5116e08abf76cba54798e9c85e5394be55967350c834c63060dd519e58e64fa3466c00021841166908d27bc508ce4989e35173040086184dca50321d2b4da821b6e56061995dd1d28659e5074326d89a694e9879896942907c4e7dae1388ee3b898cc72d1555b6db5b533e1119b8884336323524f73cc48dec831eff2a42311410e99ba7841ba0eeb3d84bc0221d318230018404325fe18c3650060f41e1fc7e935420c98371e65b6ba040f5e8c242bbb18c9a719c99628fa12b3a6ff45af326bfabd47a86fbce651e68dd760b6fa5389377b0de68deafb7d8979c3c4bc513dcd7f18ac41c3d1d8fc357bcda917a944cf5e53899dbdd6bec49451650173268074c9f4e7440099ca7484390d40ae32fd17ee44f32a71662e8b4f33ce69937a99d4cbfcea3f0bd658a56e6a9592f98f9a4a984253680a4da16e257aad5bfd33a24be956d195f90855ef7d4c0bc98830d9e5c3f432e24b76f9005f4674e99111431acbf4512f23a2b2cb4748ce2219f1945d3ec2d4935e5b3dceab915421af5e04334b1c53313022e946a44391fb5d447266a99175068aa42d2dcddec82bab5d221f70ea3e6b40efb0c487071df6f8e60cda4d296d1a826fcec0f83b91b433941d22a3f4b169b4474e47ca1e3b64ded89fd9ea7f7ba4a7dffecc9b391f14a78fa4fdeb8d73da74f66d27dec7a2bfb53f414ba9a5d60554ca2d031568952dec816fc22057b014f5fefbba2321cd9e572937697697aeeb52a48b08b110043eb3ae2a216969a4cf704fdfa8c4fb3b54919b1c4fb26913149230355986a9e19e6ef98c96fb245bc16c163454cd3ffc170fc6f33ccff33c2421cde1f74ff0157e3d7da3c38c642bbf4c1aed329ac6cf1bb323f19efa8e4e2fb5d2580305d21c87e048b23e7ba38fd48ea49dad72f74ec3380c59acff16e934a56eec8e70aa84a4776fd3657338fb34dc937548a817ea21b5fb0c09c24994044c042e18b87270e950de2056056683157c24551c1400b2e50710cd05447240e1c1e10a154a8c96f0174e58a600fde063840f1f275b6049c2a30821555ed09f909d2237cce0882c3d3f24cc9f1dc02132638bfa03964cc6b8efd4c504c602ee57f70ad6e86e6b0397dea8e4ad9506350a9532010000a00003160000281810898442a124c8b3244fdd0714800a6e824e644e3a92c782b12c478118c3310c87100000310010438c310659552b081e3502e61e98f8688a58863c2983f088bcb7c28124f4c45ed41a8fd9dbc25478644e18d60859ac28139cb6e672293e5a7a4021f5546cb85aaeed57a3ef4fceabd58f55c4124e8fcd13b6b0ce40f77eeb5821787638f9a1d7b24df8a62b4fe5220c72a6cdf8934076fdcc656ce070103a2a659401d472f31afc94866db417d773f464d7760b882579f98473809d9a0346495f63984d1f4c83dbc171dc5f7c7eda58cd898b7f03f8e5113ddd900171bc6c2a58ff497bafc72564aa00f16e2eb881a442d49fae200a2d3c1620797e308f5f32489920a2476e05d15aa83f2817e586a1de9f153a46a5e462a04cdf74a68053b4e90bb48bbe0157bb2220c1b452d2840f86af649eebb2148e6173ca6ea0da7ffd26bf8957d1691cb88a08ca7c8e99725a75481a802b337c558044307215211b2f0be6ded7fa4c2e1e0cb8e6158169d1330845ef16b9d70557a9dbc185aa1acb85dca354d72d648b3a7510ae151b7a5fb247c66e62d513c4a3f97779c607b75da857367aa3cf573e84709955743fc77b05878e6de47190d3ed2ff204228de8ce51a75393373474fcf038de872279d4806498ff33d032c0e3e47e6ec5b2025b79209638623c9fe4cbef595a1a613eaf5fc1ea036af5e7d9962c3102ca1d1b4ecf45e75996958746d1e2611f772ba63046eff9393a19eabaaf4a265dee5a2819ad70e8ffec65d74593f3145cd41918d2529e4dbb261a157051386289a95b9202fe68cef45791dbfd78d6968005fa968e0d51bedffbc2416ff4fe739c01475d3ba359c32c065a0cb7a83356906f7d4b4817ac73bd6591a2e12f432dbd8a4362766771a83bc80e323555dd03b7b8bc20897e0acb164af3b23d4b7782b775a2bcc45723c22df40a3c64d61ee27dd24cc992325825f2f19d8927a061cb34bf44914166c9d2e52a95d45a3b333dd9608933832c3bd4406066dabb656cd1102f6d71dad651bc003669180945ff75e0bb1a65fe93426c2ad675fe3eccf93a2a32439e195d129c9e61db5ee7994709726799e05be491b1e79c0af17e3ba2bfa652938639faed79f7744bd2b8bf7f3f22ccefbf35f1719fdc49954fca9e66ea9f41c245993bb8fd7f1f6f80a0929fd76e92dc7f812619d2941ac065e847db9a329d007444a9fe0d42864ded0332c5ea6709e7582b85f97478922032219544f2b543a21af4a3566f5613ef2f7d1c28a2f5da03b0cab00248355b85688fc223b07bf5801449a19678e3e0e93e06531e37f6b64b4963f12d5a79b1a420ca8a102d4b8388e3a1c825bb43e03f97154ad4e705e29052074f5a139f677aa9f0991f8bd77e309c0d1338cabe582549a6a898d391dbf0267a083650cc40399762ef653f210c45f3cffd7be7c44656aba975e878b6b2471820bf1cf4faaceb2595d30735da39bd9d079398149eaeb9016516e4e5df5eadc73df35ab3668614e5770aa3595dec2d2b968fda6d7edb0bb7d9c160fd429d91940f80daade70045d03eaaf6e63e4671219c7ab76f86e2123433457a8fe8c41a928686cdb46b5c34bd989d18cbb7fcce83994c93009ff6381261b234093ad060816cc3f991aae0cf5796021bb4a08bdac4a14348df3cf574f5e1f637ef93586f1abe28d51d1f6ceca3a4e893fb05245767eb006c96a25cd42efe5f60037c568529d6e6881339c1f09242aa380b7463a187af986cc0e710060145c1269c0178d68a6c631dcfdd88d18760e88390b1eec3a011182d9a0d9a2c641c5bf9085373c96aafedf16a4ce31b14b1d880be7ac9c1a2007cbcf0c53cfa88cbe5121b4ec02507ec03b9c349e7491b0ffe154fea28b8d0bfd90abd04bd452498962d47016bccf66085d514e61ba596011cbd6bfa14a32dbf960a4fb5cfc25a5d18048a1a9c6296612d9483dd399d8a12d8e889d80f3918fda6a4285651cc9d44f9973ab9ebc17700db9cbeb0509f256bbae9533996681f21b17c0b53f6f3d032b4f8ec91e219d4076b00a492637e02dccfd2e6115ab8822313bbbc1ffc0f027da2aeab42b11c7a2c2ff9a1b3efa09046da4218fbc54a519f9e542e46263454a777855ea3f6e09071f27086986e3c605371d3ece83ed67072509d96ad8f39466684d4ff0cfba2c29c8ce333c749ee424a20061ca18d8a63ef6e33263344e58dbbff0ebde57831e5f346f4a73d73893dc02109dcb3a765aff460655c88694a9158233a5a65b231ce46698d5412e1d5a99f0890bd74500fbcd06d1e99b502bd6edc14717b6907ec15520c9d2daf929c80d926e45735b9e83319ddff22cc220ebf70963d32402e587ba00c83cc70c03453ee06282e26cd4b5095ec3046b08a4e96259a857d9ae90f8e074bc60bef335ab43c177a12281dfdd151016a93b1660c55cb39bad07cce8affbbf3ee151662219d03e1fd983070871668007d9daaade9263130520a601fea65b306401c02f6f1802259b168c955c842f178bbd194926a73848e10b53cc3812856ec79cdc13529926c1a104ec9984d00f2e0c6c20272c880c86e0fc7d790b6778dafcf57e82143c2f423dc48cc0fe8fd4d4e5ace262d366c4bd79ef308a11cf50afbeb50338dd0f13d33e8524e15da75c03dde7902feaf405e240dd859a9e60ade4c0951c86d39d1693203467a1a9f494a34636b091f6e23cdbd8fad841565a57f34c09302eb4b62f5a5e48ef5e298ed23982e1f023ad47517af95cd26ad19e8b66c1e792e9df53d6ea79381c58ac1e872d563a11e68cf07a538b0bb28a73251c99e0ab18840274b977eb82e935c2a97edc076ac2e60720eb06047edc0ccbcf78bb8c386761bd0e12febd57631f6b3911c2ff6ae673b6b5b5b6430bf765ab7db82755ae8bb38d72809a586acbc2019351ec6d7916d249d0d26525fa4d44e145801dc9017d4c92a2611be3e5f002e5f9ee8ccef1b2bfacd377908fb434b194214ba5e4f51bf97c9cbd4829f393011c4bc0ff36339fbe488cc9c3ab4a014b404d7da245762af4257b80b4be92e09bd066060c9a8b07eb99fd930b45d07e0d57030ca41139cce9e6a65b4b94ae135b005021a1339943ea4e18eb0a63e3517004860d92493c4207959d446959887a19f3db966fe0cc431c8602f53e3f731e91170fb8cd1f38a6c8591b4385f97d3950d0ea025ebd4d33285dedbff8bfd010daa015d241b4461ad12685c7deb3ab368b97ebdebd9ff2c1b66c4b40b21c3e90df6628fa85650a40bedcfb8959d740b618b6b12373336a879878ca3173aba58ecc9f964290b84522eb10b4a22be481d8431fc708d325004e929167059663f3972435cb947ca41d868cc1227c3cc0b4a3b6f76b8228587cd59d61a56039aad6c63c9ad6701d24905854435eda1aec0375fd734ff72a24ebd4fce3d8e6caeb4d4a388172a5cab4a0a46b23d4b9b1650f71251fb045690f2944dc9c28098a0da4631d6e465a063d866ac1e3bca334640bcfa50bc051441d2f348c22f580400a4d116a88d4acc89db1ed5c0c750aa5248ceb16ce842c99582a83fbfd12c08926357da05833740ad9dc054005d861a00ec23878f7ddd447bbd588a770e9af99dfe5ae7c24e921105abce7e3abcb9518fbd34ecee78519c35523dd1a40696fdb6dc318bb2b58578ea8cc8bd1115a88e795c0abb7f1e477e7f230fd76d786e91b57f7bcea69ddf4e4e96a2e7100a9f3472e3455fcb74604c3428754d4980193a76e0bf608345f7235371f7162b261f301375360762e6253f7dded1e4dca4d213f60f9156bc7f428c433b54b689642e016861cda44d7e2d5afea809b3bd93df268852c7607af3a6c0c41f9541a985a202e9f776736008bc7139a1979a2220ef3202b88d7402ffa2c21ed91fac7c29c30ca1ba4a01593721dec84ed10645f8e72bc77a6381364402321aba14ba6a4a3c34120d9d6f087d00b1b5b14b0bbfa9055dc3be3c1d3be88f664be17f5888189e2b6db449949e4217e74231bd936017e08a54010d6021e4f781f16345a2a4c655ec611a5e44e21cb42157cfbd9a24b846c4d9e5822ac1626ee7a3f0b436c56375d297bdd82e86b36ce822fc7843ead5c0906672cd6f1a7705cd293dd71671b1aee6d3715d968d843b3b0253c655de98b3a703f3db633cb5f8607d54dcbc6e73594285dd4c1a6600d67fb2cdd80387a48369e5c320fa2e6f5fb583bb774b93a03ba88849859397a68f3923100ac7e283793859511b6c40e5d2a3cd05bb51f0b810900e4060dbd0a030a64c2b522f4114401cccc10d6a72cf5cb57b7a3a3901c5524ab0d7eecadd4df03681694e8636c4a73476e0b7f462d9f90ae32383964c268ca375e7bf5dfd576241757aa3e3ce77fa685a77519e43b108c22e07a266fb63acbf335095514eaf292adb6de42447460946b7d1b592d0b5144cbe2be3401835cb768c002ccb791184b63234bb6435305d2ca951b34a6131a1b530309b86951099a5559b2d7a8fd02209a39fd7afb895b838a5903faa8bec3464bc2a267356e42e8cac2c214783a4b164470f5af623d9224cb7bcd165276e491396319a7745021c26c2fd987b9ca3dc53802d543b4286b89fee0552fb9c35a7a0f69eefa33065a61ecdea61631188d279a55cf33a7d90fe609b355498f681142689c9225d6bb87a8532463baf21e3d4a8b0838af62aefb0c4611a7381e25eeeabe504185e97946fa910e2308d71a812ee17957cfe916ffd127a7066851151a687402c8c8bbe3e67a2226e005accd45485a6947e590b4a6d7c7a6fb694016de4c94fb67596da94ee6d871ec415e63c962f31e2d31c485668c22e2003c04aa8b11fe27cff3864c0bf4a8d6423b690bb49762fd1a67c15c219c2ec3a9c49be3c05f97ba803e34319d817f7a827a38bbd9f56a77ecd0544d15d3e72f0179c5acc58052837726161325090eb25d43042764fbf27c07bf7c41a6444883a75bbb71a97af5dc35d4b3dbb9742416e0985be1fcb99b68ad837c2f2fdf5e020a4de476f9b52b91f474af4b1184ef31dbdf1856c887aa84ac96cb8f25f215212c0037f6eb439e72a9bfb243cc1331c1c2f0f53c78613c2d2e62908f0293113f088766fcce17cbda756325cdbb63b8742ed1737b114d3146e1f40f23ae77201fc5a7ccffe72811a8252b7c779b8c368753ab2fc2004b01521f6ff41047be2b2a8dccdeee062889b73f8c727bed6c3cacd6f271ea0973963361d116c5bca8270b8d2bf44c0b03eaea92f223088262929067f973a3365a9a712094f33ca12b7a52a9513e1009f32370adb1042410233efa5524da5f63195a2d1fd887c4cecb8ac0193b5069933f89044e288a43cc31890aa5213212e1f2ec41816c97941208cf0f78a18f64bdd1d7f543b2f093d0c198a787961c4be77bc2c0d560da218c20be8589b822712bb52314d29429b5900e8ec1409b7924750193d132ba852d138c7fbf68a9ef1eab8b554f1032a30ccedb15db82dcdd2cfa242bc3cd1ceb93c8785b8ee63c28e7281dd53bb0809a091400506adec8442bad4e8764fed922f95c085dcec005b43d631695935e5c9c55cee75eb9d1fe91b75096821d5621a0f88c5d432b310e5ae699deb41c0c71291358bbc8b3433b5f329d5a28916a80b1085497cadc46a73b924fb43a81de0bc020fb5e1a911aaa4f037b86b189bf8e08be2042dc02fd10071ebcdc3c22f089439544f8e2e93b7921969806b1c517919c2b28d40501cd664f42282da4398f220b591327f8049a5c2f7118e91d01da60fa891dc317630c7487d02c0603466a2e5bfd41d4fd7bde1c34764fde848ab0c001f00c21a083ad42b982466c2d2d04e5703c6ec1ded0468e9d367312b524f6699f6e8e69138af7500dfe5657d2518aaf2a4e25a826310416cdad76bc8dbf3ab2890e8f242cb440928aba996ecedff21f3c522ba02830aaf4fb7691109c40167f350e20c0822913aa32a2bcce10c2220d5ee1044109dd67f6fdee9f16c568f24321a4093bbdcd1b9e42b7e52d05a7021c7f12e9bdadcbfafe315802e030b5ff38da2c363e09448c37dce93fe51a11946abc4c7a18fb6785a267e5cda2c0ed83be6eabbf5cdc8ffa1ff76ec7bfcaf956a5853ec5b7bc05e4d5db4facca00e3c69628c259e6efc632da8ccd4c6aaa018af73e2e6fb80e67ef511b1691cb9f60bc9986a107dcb5d95fc897c2f98dd80129ae4d7cc3f5713f154a4548c50555497de531e456d92a32240de1c8720a6e8aba9385a69dfbae3098f44582f73a94725d28b4fd08c4c8ba84bd80900fad4283893b00608bfdb6e0af1916b6dd83a1b6a5105bc2ac9462bef77802c8d0565b0de59d95f59b61e1418f907c9aa5bc67ee5e3978c2d291b39da9712154f8470700e0758b544f735d293774daf6ae4c28a88c4fe6bd1664274e712a82be17cd92f36c27716376bd0c83f98d3731d20aac4bce0b39d839d1890557d11ff119e9b5025322f76fc1c142a75b45af25bee1c1a15291b9efb8f0e0e3fd98b197a8ee18767e5f6c2028be8eeab24dbb7dc854ee580d6fdbbd827c667bd0abcddfcb5af7cedfa4cc3dfaf94f014e6bc5ddbaddce677081349bc97d5b5442b4524bc8a33a4baf16804fa7616787761d63139193d00c1f30273ef79d182b1ab845e6b415db84aa2cb36ef8e1b561babe5f1629340947e2f081b485708189505851036f170c19ede8ce3b4f810a26a212a41f8e3ebc0cea8670e0fea8294e9b4b6caf68d0a55649496798a2dcf5a5f1d802c05413eec7c07791a22ed1e1e4ffa292ade5191e1fef608fdd06ea0a037bb75737265f7d65763abb236d94ab391a86a42d1e76bbcaab366239efb8f674440b2b482415150176ab56c1c78f91e8f38b9cf233380785d1eefcbc9d80be0aa3301edd2e3941ec4256e0287bf2586e4150d22b13d67c33d6a563c45b33c8bb7e8f255890e1e1f53be7eee4d248e8c74c8ebac7327a5ba6228d3b3e245371139a4ab285e772a3d65d6313c81ce112f3117480eb56faff35fde859c07cf485c1dda993a225e40ed269268752cc0ad820e210e05ede153ec0b39088a102ac6c211061f3511c46e517a508e86dfa79c7fa0b1dd146f03538cc5d93cb1c42291041a816a376a0867faa4026ea784bf9d1c342874d6c1cc84afd5af8b0f022930d38ad0d26f0fd0e324583c4c4d79373e4c2e5debefec099bc70427a984f858fe08119f00afe03d877ae8b3a9a09b55647003e4e3574e9c5032ad2563385197211eb15e49f2996e5b9d7b027f7b29eb18129fc89f737dce3d7fef87fa0e7f799bae7791e6ba4d290f1e0c823d66fdcb6a9922239cbcb7e2bf21aaa3d2c918f38ac848492e83e2a8a6b100d063cd69342ddee9b88971004c628ecf7d449435ac047224cf8b88d8eb1c39f147c92529ae51716b2565f1f3a80803089bd0d115230e85fe188980c0cfb890192468664b795f40a098cc3334de628a400c7a3c394da3d5be6d91b6fd0c390c5398041e3ce81a5a212d9aad90b4c1f1bbf21d068ba49876a32b0f5f0cf0466e137019304d7313d90a4a1e1447fcfb5f810f11377ef560d0e8b5979423203880578fca7580c03606b1a50ad9442e88fe6d534dec2f7e1d0c0d373996f4ed879f818d50c0536f870623359a4a918b5b58731f7d0ccb17292b8c0649f8f548ab31aabf52e41c9957f02a67e216c5ee4cee4da063fa056d353fa0e823b013acc0bf4432f0e6127e985cfbad581b4e80be9c8e1ca352b4adc61623686cddc7c8633706cbd1ce1272e3167cbf77d7e051271dbd5399e6accf2cc557494036c563b223cb751fa3da75a74be512eb76132f1952044ca699b4740e64be3072a6be324194be0800686cf23907a0e15e5da2f7c56beb49a41db80da4d268dc2764e4ec86b8b3f05d37ee42f01814e226524610cc915ec36f82402ea8334d62bbb5edfe01b8970ac741af6a16432aec1ceb3897ad3a21d93fb6784ff01207cabe726e486e29e86c48cd549349c26c86f8c363cda566c266251131409319224a5c134a139d81342b545a97e98ce350256fd4212599efedb30c8ed66dfa42cfcb0159e14bcf4c5291521f9e83165ffe2ef3cabce8be0d161343c16ba373e0d0c41b8c4add8eae825a052b73bc201ae40c3fdbbf6b36551ec4d10531ffa8d5f33b6975a79ba9825ed8ab0f338824263d82e0888b75f5117c01b40ca4c5f3811a040635e5f01a965bc6d8d93b98a547d828089eb00efe3dc1b5ce6e382e4bdd71b7c464e36e668edbe07d2cd99fc15ea3a94d6a01b4908cdfd7e710114a598ccf052d4e73863889ab55d19d659d0e6574dd5f7afa87ce4aeb6d96894f1e5643e637844fed00d7f21759bf02dced1141ff3dfbd050279c24c8156ba0042cc9b7c040324639b33f451924b90e67ec62ab3e8cb0a2ad58fa94dd31eaea3ca4d337fde809a84d91a5ae288efd29feab8be6f08dd34ff0af4e050ef65654dad99072ff07eee2ef10a8c51cf85ab8a9c727fdccae3a5815ed6f95817a118e3fcc5d26135471292559bdbe94c369eb2c8ae74d961f7044ca1f3b5e76885f82ad27039c997533480fa66feac364b26a4e06206d5820dacd83a3d56491dec32174060e14c65a825e5bb19815aff7f84c8075f83772ba0eaa064884bcaabc43f8776c0ceac65fe6d2a7097246db66c0e7859077f03ef3a6781d90489ae69b8f549301a74b46c1b9acd1fe152f5f6fc57bdc956b2fcb96507f2e8f382f40e423d4eafc081ee5a7291d8fa24de134b7a12310f043461d115f03c51f6f32623e93b7c3e64ab540021311a388819ec4118776b7352c6c19ebc64f46ddacb0145cdb2c70d682541c77166056122474a2c01a518a2178186f0d179e28a609e9584de77d11986ac94ff6730614fc9a4babc8c07a6ffcb8d5a233a085d57bfe7a0bb3388a405720ecb72522d0d184e1dab81ad49b11244163ceeee16920f05ae7c1c79a2ea827e7e6f9e40f9e51c5ca371ea7eb40e7be5d51befe797421264414f8372c10d0721e7cda223932750e7c8f6f45c87970ff9dfea057836fa746c7afe4dc2af5fc886672d92d435bf076ab9c0bd9059a6f659f2e4c5c29c75dac3702ed0fbc04f1bf10a181cbd936b95946701499f12b0601923f2f05c21cf0070a4c22c35f6ade71eba00f8a13c946a53e38ef3a09ce0271e616ee0b0857d70668a7b97a0525a096d416b70c44c97f01f553d6a08ca1a3f565209f77660c2865bb0ccc8f3ac5ae35161e084450e8635d229e5470b4cd8538bc6b59990d7618343b3ec5d12b5e8e72d66a5459f38cedbbe413f1bd3250bb48c835040d0531cc904040215a96089b9f6522692dadbe010ec3428b1d6eee43662fc7c8cc2eafb5d6bba2a89cfcc711424452eaef1899498ac6b711b097526dac0461ed5f80d061ea7b0072be99c1fe70d4323a07463738e2f2e06fb88c390a4029d754dbe58c49c625923216b17a6db6237a7ebe63cf4a4c7afa38e72406eadf7256aec8ae4d097c3c09a160ff03732ace668b412e23276e7688e805efcdd345d7042e5671874bc7bdee32af4a959281ce66a809e2ed629878f01d8584d99f996ecb8997df850975acd0fbc14ab6dc63e2b3958bd2de3e2707b5dbdcf4bd39e4feb4e62bb1e30610bf039ff38d0c722d1c7210743dde8837a5bc3cf176171fa44ac8ca9aef1c07b95703be19e7fd66fe3bcf2def6f38de52bed7699ccac0cb114de5b891bd87863da3689ea18a9348c75362726a4d382ac81c81df6df093686292fc26ebd3d3ed9e1555066831f590487b35fc4d306b0813f685bccff9a6c32f24d8698e89c5283626f4a81d3a7642e5aa7fe9df8ba0c83880f6c6266745f396c71c0b7ed2d75147752f339dd4f1461fe7a27f8e04f4863d9bd3e5959211360f3e4ccb36ecae379c12bf68b078c09739ab796cc6fb97d520da6a53b56514d939c4f5272974a3ec28116f4ae11185b1153c54053ffda291201dd6067ef5728e7f155c61ace9b23ca1326b6b3fb0d87d35609c3383260eb7266102cb0d30c83d766fdf8ecd17abd8c95f178704a44727ae99a5e2e402803c823fa0a114d2f32c3abd927cb12c13df717a93d2ce143e2d4381f6c8d67cc21c762c4e15d3f9e3a1ed57f89849ef2e6e85a7d004df5e88e2ce383b058f6f2ae9608d835b0a3ac6eeda2574f9a7f0021ce35cfff8c091229b5348ea81077df377deeedcd7f2ea2a980a15a249c67eab0033d442c58a37a471b79e150728686ea73b52f1d16595f66d5faf8f0146ddc6567c033d818a42efeebeb33461286015385af79de81d1e292dd5f40e032de0150c0f93244ab80a0f73023bfb9c4f27167abe47c96a31a731f8e4a53ace8516003f9fb7f7a93b674106df4b57e183734bf567f0848739e137519dde835858ddcfe7979e3c47dbb1851508b64ba549ae9f09105c2169405bfdb7b011cca3655c24281895e0c14cd072a304aabd2838d4cc8f6b75bf4407ae3557dde33e67d714b4988adc827c08175f9a665f0aed135b31ec340c65f75ef2b1de38ca13c586b72ca185fecb3223c0dd04690c9070e741c62068a0aaa1a91634a3c90140c23ba369544dafe6dd5e384f0af77bf0de1972f2cbba39e76e7fca62284b2aeadbc6c1a23fc43c4157677e69927f7f5e1c94c1ae9872825c1698a03a496bf5f7d7be5002e417c7cc9a58706125a956d5f7f7966392c30d943c92cc53d8444dc9f7d7705f370cfcb0fe6f6c084ec7bebf4b3e88182d902187689ed4f9fe8255b75ad41d6a84dd8642407ebad7f7f79df5e35f1bf499f11c4de58681d1a4f728ecde34d3487f665e1dcec83ffa17f0c74c0cfd23f6e21741085b6c0c428af0339fc794aa7fb6d19a6ffb2b7f46638efcce122c930dfc9924ee6cbe5e97a2855f4a64c3db51646be61a9abaa6cfb3d86d468b06b69d7baeb02c6032266e4a2c2206aa411e4d5298af650bc13497b3a521b5813d4cde61f70e15b35eee481bfa3a077021e8f0d75b6c4dbaa98ada5bf4a4c6363a9cf38029d54c2a0544d909bfbdb482fd51d5abdb6d3621d660aa19f72b150bd00383063e7fd83114f04aafd0e95a0b73689ea7718d662dc66eb76656bee957f6699ea355e213d3ae740790086a1d9e70449bad94c9fd4aeb63c4decf589e697969019c649c89c9f1af3d31c912fc734f7bf9dee9f05bfde5cebb3045918848eb2f331108444765e907265bed4bbeee363ed96568ffd879f7295e29877eff19be98bee1251cd637c3d0b3823db258c240cc33f65dd9552000577f19b24cd10d47bcb3b65aecf7851c5d0e53fa162a4d8b7b54ad7bdcbea8b1796c3a2b8b238c12663baa01e79f49e8845f2413effa29d8cbd02f38d45ec2ef562169a21c227be5ba3c407d885f25907651947970ac4dea71e2a3ea22634a28761996986397c0fc46b577ff9b30c64593e304bf4cf8dd0ad99a271dc82f57ff01ec7d8ab203164653f2536e81a1fa299340275d423a52df2ae20441aa0156009695945fe359b52756164ccc98df44867b94c3b53895c2744c9b52f2c31e4ce3e1570f991f7cc23118e7a4a12c15ca8d3a602d91f181cfd22a5cb3bc771fbcf8ba7cc430ff95fc9a63826c7a1305e158df1a8e72aa8bffd15d0ef66778109c2b6ac1800c135910b0f094838c558a734639fffc882992332f4d6c2a688e6ba388a43a42d1cfea9b0a80f83fc0d699f1bfc9cf564812f5e14b26b2537b3f5220d79890e182f9ec2d8e4d07a9f11f89e57c9262ef9a1ffe2450d72d81c724a60e067d9dca93cc5705f14f13bea3e007e22e0c9e1c1b7e9e68803acde305998d676d490fe960bf59c418399007879e9f8c20e7cc5405d16cd9fe5bf291b069be5d06ccad2ce623800281b8804758a0646d6d46897774b6068b6169594de85ce5053b86dd130b1d606b48d5c4bda9f5334a1c7a4985e2dbc844839309bea74c049d65e2e898097b9bcb1bf874ad8af8910901788a68dba1fd831a360a7a1ce2c6dc48f826a9dc7659de242512cc19804549f8e47f715c13b6f608171113496f1e513f90040b10514d59c3c34e982ce8377eba7c9ba944bc0ed14a1e503be2175824a7224989700490e1fe763ce1a183e0cc28c88ce45a4493b49a2e11995766defb8cb73dbf939ed668411b5078b717cb488f3d140d19cf0f69410eb4fcd7738894ad63c56262209e3446ca5338fbebca375b2a3f7cdb609b327ea93288312fe2d62479c25e934b66fd1d35b462d1e7dac1788c5dec96030bad13a6522ae86ad4b7c3c903a8b867bb8f8a6c34dc0feba9413f31c6554a507d94d8d5f157f0845847cf2b51ab332da3b2057718d9984424a3e374491ab14b3cd21e1913aae28b2be53f83695c9c6c5522b1e42becef728eee1fee31fdef22bfc8daefc6c7a935d1801acc1d0cc54e66086446bb1178806e7cf9bcb1bf66266c4310380f016a3fa94d37ee38c9146ea2452dae99a9f85bcc4d71f762862d0e48285d84baadcdc58aa4bf7190406cd1a4106a3cb093f58d47a11528cf0aa0590da1feb5637c54f407d8a977a2bd8f7b498d813b5f4dbaf1fd158d306be4ce8294467a283cc4041bf55f5a8a159d5ded6c1cfa9ae518be83dbf873ef4bd58d17e01b8d7f007a467ffefc37c3ce6a0124f5f909b23495313541132b7bbb1d06b20c0cda56acb1fe1bb8954da5d02fc77177ca31340536676ffcd22b577b2d3fb92c06c3caa1529198638b2a4541fac6b1759a05488207ca617782d9d7dc57f986ce290497533db1cf2584e78157f9fb6cebda12ebeb92d928ee6770a9e6244ba0708059a41bace83666f7c63ea90f7006e63e38f66f8846d710c5f99faafdb3bde142784b5c152f077938587bc32ef849d97f4b35d6157719bb5769c73be09163fb3ec5a83b9c4422c88cd8f26122d32659b51c818dd714ae4e7ce881ab52fc67de101fc678464dc4a73cd06db2af85788752861983bb134d0235dc1cb88c0b9174d9a1f8d2c13fd28dc069ade5233c1b5ccef1a17a525375d9347bff1c9e5d8f43b4f4ed7d3da6ea2fd06ec8c6f086418ec7e0091a4a704771130e569fcc5d6e521677c7211a8f6042b54538c177e8897120ce29c5424c371aa269e36c8463df31aa980c1798ec09bac1b48ddc4837b0742363cf33fa884333e625502c713bd751794691409f6ad880c0210ecc570212edcca2db1ef7f2ae00328bf198e783439ccfa4d8f737d65d614a1227da0218ce07692b6613f2963e4f508dea5909195aca8b6bf1440cbfda7bb48fc4ef7c2308988da2ef31a4ef9418dff3c1ace94fe5021182950b247d3e1776eaba3cb86f3de97fedc8d1c32db0d39aaf7707a62c2f9d6ee7920586988682200438b0c902f51b2cbba8d55b90c7ca05405458d9d7f57cc340a89ea3e3f0e99d423bf6e1b9ad5b5e6b574c82c30ff95080132446477052e7e011aead81f393682a7b22dac234294ce484e24ad203e0617f03af7682b075d3d9d320bd399d2ef86f5944d6f5fd909c68e05e99b34d6adc1a8af65a429e61e9f44e2d1049690a2134be5fcc79370b62efeb19867c55e92ef7bde37f5abac87d5f16c6697589a74eded8010ccd55bffa2109727cb561b408b1ed5f4ef11cab817b57dd3cfc1221eae71c00888e03e0209547548263069849dece4fc5de39268174f71d280cb899667f4644fc6eaaed86935ffcfee2660b08f7a05187dd596a8348f9c15003298783c950191e5eb872e9e57a8c47c3050fd07c8b32499dc241764ba1ac17079f419031b9f1a56ec285456b4138a810541e471c532cc600ca31bc6eb7cfafc52f3f4231ab87e8f067dc71f0e876a1514832820c30409bdb8537a17fc0703eed63fd7302ac8bb7e8188ed8046b41819884e3760a97c9a0ccfac50fb3a67b70ecc9ce79ba7a50ad100e6d59d33fb68fdbb381d22f7e7eb83988daea692cbd510e4a7a2de17b5957cb3e20c78ba797d7fbf3136a7912f77d76425fa3d69b8b67048ed3821cb8ea92d195a4154ab9f03cee48bc9cf817076c745115e0cf257610586fb9e7eb43a0cd65a108c3851504bab21fed3565d3486f91e9c46d5780aac4e2c9aa298a59413060b0fb32105629ca32329a44d521e7473377272f8daa2be3917a2dab3a622624ae5f7d99388b603d32b68e21bbef985f70e28f0fad7ab3136b8090bb77442aba9dba62804a2d39cc85bf01121c9233faeea984e0139583d42aedee69428755a9e3096455c171ab3281d4c98055355c22705636ac4d690996b1d577fc308757414d32ab5d67e27bb6402d9297d4339b653d206b8c9d1baed4798549a7448e9ed21e1cd05c4ea485963ad7a54ef0200bf8801b732ff552ecb70b17cda0976fb3dcee42cef965696d289c9027aab62421e5688de0e6167348625a16aad29dd09318f0df52da6eb7769caa76d35fb52b274ed3f81724e0ea3c17634c869e36eb58696165cf851d67693640e3f0625b63259a631fb3b9f80a04aa0aabae12f995792437b99ba7901fadf9fe4d9f0c6417ffe65ed25af91f770f7c18c4d2e2dc940e66f40538aad8313126bffd06244ac07bf360ce5ae60c9b6b147b93195115f0af689581d2ebbf110a0517f6a4df10620dd3169dc5fcee797371419a2565f02fdea037bd0390848e6f17a01730f9bc6d373c58349ebf2590ed0c2d87c2b25859ccdeb8266606b5f17b4fb83747f716f242c6f8c07b1fe472a74bdac516806ac4dc097f7a8346937b2dd104c4b74ed962a9adf7e01cda4978c2af433e69e37dbef5c8a6034fcd3553453401b0bbdf33cc75eb6abca36ba534b3b826af0eaf3b58dc41a1102befbbe3c9852d8339b9363851155cd0bab8280e4fdccc2ff2d058fb9fa271a041dbdf79f7501b965f90391ba6380718dfa5b51637d2b4f101fd2a0601c7e83d0adc98a139a1d5ee9744454c36ca3bc06e422b1cc22ecfdaa137dc51ae7b09a6339ce7acac52b57ccf7c67c2a2e3998b9f5d04194e412475d7bd400585fdb08baec85623dd7b71c3c305d375f22221b29f3ac24fc1c93a972eb299f734230ce8390af762f3c15356df1ca9803cdc9d1d13b415a8e4affcf3925c02a65783ffaa6e811a32e60eb62165ac7245f565d53b039d7302a0597b8778431b263a6b18fd633e8797ec54a47d9df56b6fc40b7816f5332e42fef811368c0459704db0450f7ca3ffdc55e5d80338cf1fd3303beed470d2ba5a2046a688115d91e1ca103c694016880546c477be01c67d23a1b279252a98a7959b74dd1a1c948c770412ac48fa8d8f1d92f3415b190121ee20b7940191feb711e61257bbcd2f11a84f8836707f90a04ee447186f52f670fe8c3512f3f6beeabdc9661bcde4cedd9b86374732d1f8276c702e28a56dfd95218218679947d8b54baca6d549fa93596abf08d73dc7d5028e560045ecd25dd0745c7466e015ffe08bdfd13f84abaa938eeb3b0bcb6a2338e485e4bced911c38c2f7352db289bdab1ae2a9a986f29788518d50aeb233f5f15c357499b264e66fd84c0df65e641d078942f92c265ccfb7eb2cbd6cb9370494f1c44c9c3d96aa4a54e5aad72fa7a7f6070ca9d0947658f1e467de66296750535a19201aae112197b318e8e57cdf03cb657677c4ce871b9f8b4e2e223bb50954a999f66618f71fea25174d9d7cd6e47803f127829771800a94dc1b47f93b92f26ed74226da9654d2e5ea9264f0474d537eae778a8107c3bd580a3280f9005ada51181a055545e7011431957ed7c514a265cd103e2effe3ecd0d930920b55c3b2d32f223f6467d4dec47494c29c500a6743afacef7dc23d93c6ccc4cd5867d12119a157e032535d02ffde24936adba64af2709710d81a7f4768059db1a3af21f8bcde0905ba17a90dd4c3c965f3b113d379bd3c8a32efeca074012aa90c1b66c9bdd95788abfc83b1818370569e4a7622de395add87d09b01b863df011daed74695d66706c72ff8b02ac607de56f3c005fe3b0bbf6a6938deb7210c496cad68209e9306cc48af64de7eaed44a3c8e3254846addf001bc918348a51fca426ea3d20da72c64dabd62f63f0cde8a0c26d3f5eb54042d593b7c0758f9e6c401925b3fcf367f1b93ef1cd95b5c202b4d91fc6e83965286ef6b85a6262643a031c361412ed2159881c8366a5fccbeb6037f7d82adb8a35534c82570905a40bcddb787bf64c998a8a313ea47941d10045722f28808c593255dd39279605727a00773f4a6bbd1f21bd3a83b0d3f461416c842b24bfc8c2ae206a83777280153913373d1b063b6c26964e8559ec9a19957ac35562bf9e31b84d03f37267aafea80cb7a234c2173606818c6662a14c50478066836df275f97c0b922c2a2256b2d5e0d226149c536ea00e79adff708e7938ebcf447ab3a783413e9538f534560b817706de2529d730f2c9e20a5e124fc31f3aff0a99d061ecd480b196916fb9556b504295e5aa7ac0b1a65ead5f0acc284107350fa47b29189fadb46aedcdaa254fa223fb58a06988387efb783c3d49bbc1e13bf898b63d1d83060a480ffde6b314e236495061228172543388364847af127e3cc2951b6a4ede1b29d55209a76f5e845ceaacfc94c2da4a3e6f06f65f1c7087b05412eabcd53b9924a148f1fe206646a4df44de3b8218a31a44c78e89fc0472720085e4d79df044da364fa2bc5d8f1cab38ed2e2c466d1623dfa0c071bb65b59fc8d0859a2c1700227b34def10f04a9e05e92047065b416f272d8f71292ad9bb4e6fe59b7b8f2e1419541d8b80d90f82b5aec0e44568d3bdb077178049e0da79f3a50b242aa52f92bf876a339a234753075dcef88d35a2a5d0c1c72e9dd7c4c0b885cc4726ff53c27f39ac164cfb2398b4f42cde4ba1069d7753a7a9e16aa6f43e7e8fcbc9c39ce94a9f20b20a0f9772f4a07b01b41d669341cf838f27c2012a45c5e3cd08bdc1a473cb2f485afc4b94dd3652834160e28dda50fd2bdae481fc92dede88363d52fd4306dadff99a5f76732aa0ebab8e88fac89acd94b366a334188a093a34f045be831e511a42f3733bb10b7cefb79bd3c14da0f65902326e6ea77669a2a1c89a959767fec3370953a0149840fefab36c52ff457a925cb29dbb42335c99b083f7f2509245d95b5017111533d5f43dd8e7e7328bc3b52490eeffddda87241355c4696235c2db09cc72efb2b394455b75875f1a94dda246a800da6f84716508e3f9d2f6c5baa9bafd7995ddb85e0b14fa4dad06fd2642d425dd101bd74241db113efec586978764f022389e8934a699207a43a96357cc43af63b7f4c966c90b6ac3f59ecd5a8e47f2db758f1563768b4e200f6ddbc896e735e7fce17079b39cc7dbee73e6ac75cc547798bad63974f905d336e7fdf434bcd76dd4f8ae8f297b4257097c197eff10f051b9b70a3bef23ae822be9f8fb702eb56b1c6089b3ba33eb443920bfa7efa5443c572cc7bb5ff6dcceee64b92c656ea68583d130ea19d098645f72ad84e331699c493a5da10e41b05d404f8edd2ec06ffd367bb665812f081891594cf591bdec166ccfab2e99632ac67a4adc63e4587bc22377c99bb5cd3e462307f7c867dbf76f800cc24d64e5f6508fc4bda598e94fbe854adeb21603d79afa80cf60a666409209aca231b43eb85d27f9b1f37cb020ea4432dd8461c41428774fbd45bca075cb357ef61e8e940c869e3daa278d1f521ae8e7300cd43756871cd990a9808862d763e1710c7c710575ecfaac888081899557d937c34b642183102647b6591aad3d6e589349c9913d2e904458258d8e9e42ae808c6ee0b552393f06495cd44047bce30ff510613f367b24e3296eaa9e7dd2f96eb720bf611981e4c6dfd21246339f800a08e23c4214cffe8b050862ffbeac03a4124d89ab933475bb33a3d5602f21f735491093f0fca214e398d1b216fcd58bb46060f37f3530ceaf846f840603765f451a8598594264191e68de3e2e26edcaeb217f683a91d05eb76ca8eead8c9679ad29b2562478901fa94d2923c0419b10846ef60c7b27eccdd3289f403c80dbfbd214077f21747f0877d2da931bf43200be39e977ab51c5c6a17d938155c79d04121548f96b1b78f564fce82ef17957866d2191bdf7e9f53ab60fe9309842ee58280229ec875422f055f78459d9ae70322c2785f901ab154d759634985256dde8696ee5d87204fddba16297173ca09bc4da18b6f106fee8e58e53a57cad23187de465d780f467dcdef8ce10c6fd98471712920919687445b09803f93ce4bc73a1f1a9d376520222587c435e18736b096fe519f40b544df44960963b96ab48541ac0045373dbdff7cf1c294462b5326b12dde2d2306c448f150f485c3dce4bc766e9da2043e73fcf8d2c27e0b18defd81d31c38e2105af100b101379613c51b49f3d318849e96a42dbf5b2c21c1d25ad9e212055148df3b51f05412ddf0d5e666efa0e478b8d06ba10bc80fdc4532297bd29dc5e38b754c2aa118c7a1fabb60544b087cd02587862eb1ff2c03384f478d82f89b276fa4f47c02400e6e31877fa27d7d7f52fc0933f3033fa5928fb06294e1df961a3cc332de3c24cb8f23611b565106808054344333fece06fc4085373e3abf85108d83aecd78985bb91d8774f61d43ca17a2f7e38769c5e008c102b5a5879d27050894d3315aab128315e3652b51b3c9304d258a42d7bd5dca76323f6a3d6044a4389e786064f03c38558d352c8d5ee1013f7642288d1fb152ab9274183b5d2126ff6cac7f30b199ed274d6c70c6b222606aff24feb647dbd0bb578f5fa5ad6433ec9dc86038b21712f0e4bf19af28ff1c6eeff3da2681d20357ed5468f6afa6d7e5526ca3172c9072ed177172992ea760f01161fad206473a2ab071981341f10bb91e5f84adb5767e2b984662e05b2284b7486922f49b1f20d97749775f12ba7127001cf0ca8af147d986fa5db0798a9dde355198c7b7e7360967e29e5f7a2962fad52631ce4fd2906ae1714bfedc0be07371ff4dcd9e1a5b7e627076885dd075d4fad077d08978514e39ede18fed677db5d416579f73f9f483777fb17791fe4ded4485101d39809cfd31c37ae8a29ec1491f4c47f73b29256e3e9504a9d4bf8aef37dbeb0d00cef6d62213b4b370a3b5a0436e2bc91dc85f948f6a3cd67bc02757e3981c7d2294c20b3b8c785a3b93cbe7b145317efac114c1068f09b27c547e6f04f2321f3eb4665279afcae6789c957b3f3182ad6c186d9e993c4d2e3a63724778192905e4ff6613d2b57bebfd33ca1a4fce03e723063258c60d051aed599d3326667965cc62f66f5579aafe64cd257ecba5119ea6806136fb6612f649b2f5c705908e0a44d1ff55df651ddfd0ca23290be6f04820f7df006b542bafde29db376622120c9885e16875143adb344ef6a9e06f4985c74df4bce5ed9d4ebcc82bdcdbd2a301df2dc0dff01c14f4649d5ff939212f5037e5c08018a430d02818f31974be9e5f8c887cc177a51fe69bb78e73e1a9ff4de9cc704ca4f9aa5d4e4caaa3e0e82d8d2ea91119e60b00fe2eeecbeaf9abe9b6f174491cf4cdd1b62c97cbb7fce4dd8cae668d6270e8fc9a2ac86b57f10904d966d9b1cdcfc7ae28c22ee35f463df412151044b6396427126b134f1187667e9c9b03308bd32cc88335566df43eed5bac514a4e3c175782a4bac2ee77ed8b729fc69204bb114a58f39787d862dd76f72b5f4580b5ead94836befb504d45c8bdef127baf7837d0215710f6384b86949716cc4864ada8b4f0bfb8b09d26bd7da9e3af15bbe066d9ed9cd6570b61eb08fdc4472b9f83796471138279e944fc70d0153bd0e6ecca1335c67f5380e35d4980bfdcfe41f9f58b7dccff5cb7948b20efdf5ea04fbe18219e81eaabb9fc63734bba2fa56ac00570f4e990abdd696ae2e3227b0fb3ee4e7e0a11744f9437adf603af968ec036f597ba185e9a08d3fd206ea229e564d07500a95e7381cc961315ae34657dc833fa80c08afcde7e114b0cc7274e4e78cafe5cd9a05761b9e7275892f06bde73d56db59719b6f10238e625368825137b3290e9b6f902327402bb19b913a1be57f56b86df7140aa81605246638828c32a41574b973824789eed926afa25c2dc9917426e2d6cb520fd107ea1078f54ae9213d5cfadcd3932597db9c08beaf3e889d4fa61cc1bdf303a7734c144e6334bde936e7c5e869d87c14b57e1e6af74cf5d6ea389b73c7ccba49e32b9b1e0998644047b6497bd4647b7abd1a2b7620656c39daf6ca45ac25857b215f341b57d4cae357ec80630262ac783dab7e6342f4d15a58ece676e5a30604904a6aa06f03e46857d3f631137a2503c459032a7e8e805c30d659491bd6476d36a53e30432a746064503d35ec74a6bf46fbacf987d35da8718069a337ee22846add30225dcf271b3faecdeca1ccdce77ded1b5a40c301542c60f29e511665f5ffd09b78154d6c5d476b9b88904d24b2bbbb7b076d08fc077b0744d0268e34c1441349ae0ba0407975c87b481994dad04967a7c188f1dcbe1a7a7d01d8b86fd5394010b780c295de837eec71eb174c1dd339ee635a87006064d9cce8563fd33a04d0a97dd1b144cdd8407bff76b8f2930bb6d797bdf2af029d9ebeb2c0c361890e23d18d2ac73875accc9da923adc4c933554e5a4ea0b49c14110d4929a79c52d26a2fa6184f19b1b417631263599659d6f446b76dcab845a949bd719aec3627f0b996ec3ee94476252216cac2326564913f728824f22387c8221248fa2051a98244c54ad2110b491216122743427115796290b88a3c7148f4892a098b2a3c8a52ce49698d724e4a6bb5b45a7b2fc6d8c5189665396b59d634adb78dd35adcb4c8c51dbb2f850e234b0b6d699932b6b0b4b8b8985278e95a527059e14b0127caee45e3c40e436160a68c30261c65e43269852d5c4574a00c04e1faa1b35c4180b8880001072a1c21021f29e222e1460b38e8d11606d862954ec9bad142eb88b440ec0fb69ed8396c23ec2a5b0845767087ae561116b4866c1dba5a3cd14a4e8264a2c83d120211d7a14b08435408424208eaf1b52a842227176c053a3ec10cf6aa7f314960420b26b2c8a115830584000475b1c0c2114626951b00d549e5063f64031801c2144392a0f8210a252daef8a1c8094058810e4f381285e71563dd688109284c181d651823803e6bc81a78c77ebedd0d1fd0d1d1d1c1613f532865942fdb7c1c77cb1909882af49842876fd3a7c699e953e7d0e4471a2396a8191dd2e7409f2b1011041f0a044105270061e458441012466ad00155bed162c7d7820004218d1b27187184099c24c5d7a213253de20b141184c44dd2f3b06dff70108a9840d2a30d1bf08bd1341a6d48ff686878fa57d33fcc4491fe21a03771f384d68d09af94b2284af9cf842e1fcbe8d0e5a4e524a875440c6e90c10ff90433b8f9912ac2e5848912176c27495c4e7a10a79db556d5a641132070682284da264049f98bdd8d111bde18b1e14beea3ff9c734a89d17972615aa043f92486da9a73a658b084871896a880dc69812b76b6051396903a81d14b0951144a845aa0c4c80a94106955990e5d4b585da87f2b50a49ba31d1f721f10c28fca8ff65297b44be9d221a733524a29d7115de24947ec127eb1df7b71374286ecf596a7cc1dc4a1fd781c2cc804444a33168ccc596bd6add065b2cf293becac4eeb59b7a3c950bf07eef773ec768c582376fe0d69847ebedd08708f8ffd08623af61726eab8d1120cb4c336eb208edcd5c84f0fb19e3b8cfbec225627d735d329f1b511db62bf9d76194b4a29a553ea1418e1441599bce612492c4c50263ba85b153633c2d59e60ac0996b132db9728a22d118476e9d0b5840e5b4b87ae265a34c162377995e2671625432d1a0d5ce26713131d5eb62b305a16315915325887aea1d7cc4b87aea1d3d00d34d7d0102e86820ced7453872e259200237e7e97124f946862ead0d56407319a145123d3a14b891f32627afc0c00dda16b091401582225000090511383770fe3858bd2b6d9ee0ef9e6c8773d7690fed1601528d9e9f179e4d8b33a27d6b394524668b34169d3a4079998988fa17917c4f4a044500b9478010f4cae9c78f28f069ce0832d74c0e1093f30f141832194f0003d690152528cd2ca499da8780800132a7a000093570e326eb44053446c960e5d4c98b8c164073dc458a20a6e0414744f78be0910184bb05ee8e0878b223d344b049182d3123a0499d1a207192c7862627ebe7a606eb400594d5aab9798a72c1c3141a27f9045031e17263fb430d161c572a5a79405cfae0205dc0bc8d64488bec10ab4a12154908782ec9025018b4287273b3401b28315b89a14c1e352e2870f58f840a1836b8914d512a00ed3a1cb890f2e265acc40c2c89b33a8084537434cb91982851124a9d209d0a12b49928abb2883706743883d1fced0eaf6ad087e6a50bc42c4dd0831842575ecb30b08472e200c61f9ace5e55f64def42e315ade3afdb9e3e26b40e0e9dac358d15c4950498227899d3e9f6adcb67dde3eefd29679bd65fe86d476d6affdb6bdd50ec8cf9d4aaff1ef8413af70fc3b95b88e78a56ffecdd0eadc7f33b4baf6b0cfd0da5f750bb1cbbcd67dda6b6e4388ad711e3588fee63932af790e4c4993f9cc611e7e8c8b06f3d2f2f535fe995c5cb296cf5ab2962cdb72966d2d59966d39d35832cec2f259c792715cc671f5b9c71997e1cf6e84c030579223ae242a0cc3302c3ee64aa2a8635ae9b5dfb4dfbeb4fdfc1b73c3302c6a1caddb3fb5df3af47f3528736cf7db7d35e89ba155837cc0444d4a86359f8b559771d21a67b597e298ee436a2fc6329a6553c6acda18238c3a94d68bb12cd39ca78cd9befcdc97168ccec5b2ac693d65d42c8d63d26e8c31c2086f8cd8caa7cbcf418674f99a8f26d2e56f3f1ce5b82923b7d238a65e5943499c0cb14c504c2d168e49dfcf52332599aa98ac9892a209e97bd9d13121c51f202245807e8e04fd44ec5f885ea0bc14bd10bdb4e88bd1cb8bf297968e6a8747a51364451feaf9394a3927a59896695bc31a06a34d18058a0e7fbe620c78e8f0e72a32e1071d623289123ac4b387203afcf813450cf0c63e84a71fcc2c61660819283238c4e000868abf93d24d6b0f678d19f9e9075dbe9d73ce2ca1cbaf599637ad65999b19a2cbc7b04d6b589681d2e5532e8343ecf35e2d6777c6e0a0cb97d65a6b2d0c15b1babc6082648bc562b158acd56ab55aadba7c9797cbcbe5e5f27279b9bc768985a5a5c5057f7631bdd0979729e30ba6b1b4d2b424c192050b91d2104a3f60942ebfe62c7f78e8f2698629a574b624c19245976f5988c8d2104a3f764bfe000509b1867e582c168bd5daaddddaadddda2de8a2419349832f104683d6853661ecdd06ee884d8a2d88561263d0e5d34c6b996e1bbafc89d1989867714774f912e70cf3c93729baac5acee616442bd12ba424a9a35afd98904c482624139209c984d4e5eb955ee9955ee9955e6933790979880c4adb5f9e739f7b4ec3a0c9fbfaa5c6526e58fb80f1d5f7a5d6589debaea9e3aa24e9bf6f7517f74f98c7dca754190ff61535ce8bcef990f160ed9758f297e2a67feb89de7c9ebf1e74c6061019cfc19e0b8ada01a5d7fc8b18c7f9d0ed971d717f5ffb1955577d31a86b0fd3bd741a73380ab73955c7bfdd544faa27a54af5d8d48a439236f6b38eab226decc721a57a5c7eb6843902abd3cf424628f24aea2b74e87a0d75230c29020f455debf5e37b432e2988f40474e88a824a8f0249b7e9d01545901e85939ebbe6f974dcf3a679abacc61863dc3e7a59e360f5ad10cf27d3392acc8fbd8bfc70d6a8413230a6969226adb5f97bc89cf835f4bab50bb3636bed6b12f39cba350c19fe5c3f7ecd40b00601ab0f524a5c1f6a8d3ba07ead41967f3bf22fa6d6a0ca3fda7dbbfba0a186a87d26e8f535ee11e939fa6be8b5fb668fdd85c63d1c1f736f087eabb9e039f8350c2fdeca5a5f68fc9e4f6ff9af02c1af4060fdaa82372fbe72aaf1ebff78d817b4e5271811672f2ee6f7e9691ce0427c6f2551f2ed7b41a257df72d96bb55f0ff9793e1acfb1af3f6b18b42867f6c7bcd0bc4b7e99eb671aff22a661d06a7d9afaa7fabb3e577394d91bea60741b898d5df3d7af9fbbf837e60bd85d74dad374a76e771df456d94b867f3886c3f06fbf7013ff625c780bff60b0e46a79d4c8527aa9716ccf5eb3c69ed92bab2a5149e2c7726ee9bfd243fd422cf113fefab1af660d1b8e7fd8c62666af3acd2afb557ffdecb7aeaa348ef6399a7f4f350c5ad7de66d6a840809408c207ca03ff78548d7f21f0d1ed676fe509891ef47cfa16da48b650a7967b42a20ad54413659c8821b6e494d55a6bb4b3d82d6badb5d65a6ba9ecac0a13884e20a484edc3cfa662b79e40d2c95fec32ab53ce70c19675524a3569eb97c07ea9535aa9853df69710c0e876a6db986e615e14d0338665a85e6567eab45216728fb647dae3ec02e8794a1967b6c5e71d6a1d19c59a1c40c7b20340c71de3189d77d8a4e73ca78c31c60873b6cd4fa25fad23eb184b8b558ae11653ac38d19a82c594d71429535c53964ce1c194235fccce96b2c594266011a7bce41429738a6bca121e4c39d2c3942114f3c829278898942d7adcc15e6ba5d820059814a014e0225b8a0d3f2c147edc68c1da2a39c4bcb5d65a6badb5d65a6badb5d65a18ec0fb27ad8529c4899424a51ff56d002762b4416668dece1c4d8eba8753bf97f601c3b46e6c5af7dee220eacc35d8434eecf4e421af763d7afc4a18d00cfaed471f71d5441beed62c000c01972c6a7429770865cc0bfa50f2bee28a411f3ad5dc470601c1747c65aee2c8e8a23675aee288e9cb5dc4d1c59bb5ab64117dced9eb186753771e44cd39dc491738432d38d6e2e7246d453424d37dd6538b4ac35bd652eef08e5a61bfdace76ecb2e77db516913b9ec622c1dfb504410122c3d8825237207a00e5d4ef8f4b9828e75c0b3c70955b781d2daa16b8a1521b2b77ae2f544eb0916920e5d4f14792208f984cf7c224887ae2776fad77558b445519522a322a2a221a1688b80260c341f6e0df2e1a21f5ad14e872e27aa3841c58915a61071820804f14aa730c85e8f0da400f56ce892e223650729394c293650281055b144585ca223ec4054d4a18bc849efd045d40322a11e77874440fda344437a7c8ae12b93c81e4f8c73ce39618c4872b0ba9456747a7d796447561e8cf1c517637c31c6f8628cf1c517637cf195b4da4bedc518c6b28cb59ad5ace94cab9aae9a56bb2feed4fadbce0625aa6afdbdb55abf246db5d5566c6b8dadb6625bbf7eec2149109144271f6040972f19305b883c35861b318834f2e15304a9f8f8f97cacba8c52ce49698d724e4a6bb5b45a7b2fc6d8c5189665396b59d634adb78dc3d7765f0d4696e334bb6da9a459167c6bf7ad504d26adbebc681506e38e65855ebfc5a8d77739c2dd97028e49c3dd67a3d77fa901a9cb7bbf1a5ebdd620d4e57f71a8174048d4894205baa2587111f1c42fc268071f7420c40db6d89982a7095454d1c288ec83285644b98215a54914564d9c3a58e707082b66800d38a000c8106e5022ca0b7c40c41278d0041e9ac880aa2ec6517ea23c20c39ac96f5cb0bfdaea998e8672525a390c69d0a755870d0efd12b4810f75b8481bf8dc739be7da411a50c7b083030e55eb889006cd58b33a077dfc19c3b29cb5ec6a08b03067d08799c6c93af7396b5143808bd17404740efaf9a7e6b8fddc2e614803624803af90354ed6f7674d8b1a027b6b1d013c67c0fd79d3a6ce41f77e4de370bf39ae23c3b40e9ae19bd96c2b3d0b879006fd12ff44d03787dce3ce6dfc66c8ce897f636a8975f8598775b8bb954fdeea30868f52cc82305d4480d2a16b08500ad0a16b083f7dcb107bbed98d0ffb5ed38f96d9d0e7df8e6b411ad5868aabe55c7d7e66bac2bf6a4397913e0cf2f482845d6a1da6ceb5208dc965fba745316cfff4f9f8f6cffee9851cfc9d5ce8d8e905fcd8633ce7f2aac34f2ef4fbb0736a428a22df0cd9f4e1674e86fa33ea9b1f76fd793364cb9b211a07d2188b80f1c205cd69462606e6c5e4628b5a584a9b12e136ade50cc348aead14ca191c89b28aa2ead04584a44e042510c14802743f7639020a0fa01c8192040a0cfa075b46a008a902ca0a0acf4e112b7c9e2ca17f31503c8142c81329373c69e249107080b2b3458458dacc19f91412b11f35354f7cd06f0d1146f4e019e4709f08a1b23544dcfb8405fdd610f1a4873ec10108035a98e2898e951ddba1eb8912190a127ed4103185eade7b59b062d70e5d4dbc9eb8a1892c6810b11f35354d0cd1efac21a28916f45b43040afaad21a2899d9a2c2c2c92857bcd229fd32f75a954fadc95a494527a3bd8f8f5a63fbe765d01c5750590969cab0a2b3d7b4d73dcf6bafb78549ffe692fe54bc941c9bfa8bbfd53fbc84fff7da97548d9a1a783bd6ddfb7eed39c47f5f139f2f961820b2088573aa7235ef94df10860f40a04475ef83be09cf1b4de5a55381107b640a59014924252480a492129e4803be79c73524a29a5b5d65aadb5f6de8bb190f484e4e4736a1d35481f5566377837f08e701e0e788c31c60883a9cf979452ca3be79cd3524a69ad15beb5d3c38187831d1fba203975c1f2aa43e7eb70d1290daaf13a21bf84120565d63ab23c3fd22d2bc66901762d7badbbfa0481d26fbfa3c4339e3df7f96ef66fc8fa9aaeaa9e7947ec8c7fd863b9cb78545e5590c6dcaa4f9f40fafc1aa88f4cce23f85049b1a73cb5c258d7434581523ca99e8c950d654173c67c9b25e9f36f26d42b500d80523d35c85a545a1bf22a48a2229594c3ebf9489b4b65a434d2ceca2ed6ac93557dfe0d9977faf48cb8c81a34fc781c9e907880f8f337af39acad4676a515e755918ae4f3d52155356b6c3106630d138939c87621627fb5b55bb3460d0a6a05b5825a42ec1cfc15071d91284c08092bc9109652fd645d2502f44344a2322018d04a84d65410186cfbb1fe007942ec186fc5329aa57a52ab1651500b4a51aa274ba9b254cfeb0ee3987f511593eaa94819d2fc1a2451541ee931f21839a443c01ef38d1ed0fe2ad27c8c9190bc20760ef635d716942289ca2f89aaa878c43f1ff7a1ced15bbd403bc66fcc9a44da0cd521899290566f085a87f4f85e0cb6fd2189faa217c48e56240ad3b13b12a59228ec82d6bfd83f4f485401e33ea068cdaf491549a2a00b8aa13ebf5691a858adf4f9517b42a20a913e0cb5d748ebccb7ed2a1322e288816282d83e60ab3367d8c7da638e7d604f88e71329f6843c52b7415091a8b7bf8f24ea771589c23cbe240a49a29224eab791f5841475fb5e14fbab42dd3ed44e2c7ce33c1d95deeacc1a3968ceb06ff18ed9ffc1b016a8db123ff90461d86ffec1e0f8178305a9884c479086fd132bc3e8f927ff60e4cbbf0d4fac6e4f47ddbec7846dffdbafdc7da62abafdecdbaf6e4f3e746b751e69cb61ce39affd7b398ec316637cb77b31c6b82785b19df7de7931c6f8ce0923a8c7fb75ef48948e44ddcd235138b5e292244ab55712f59c9578314ed2e37b50ecaf6e333238e96c1d7e25f4982d6bc6076a1d2f3afe1b328a126458d97f79c8a43367c4dff9bf1919b800411af1a4d3e37b59ecf81f97d4677e4c2a17199856323fe8f1084650cec10c62ce7a7fce39a727c49362c71823fd0ac3bdf70af17ca8ce71abcd6123bd9e8f44dd6bb12724466fe509c1d99027c5ae9e8f27a4bead1912ae42deabb57ea8b5fe942ad2dfb66df22f760dc3be34d6578f31474a95e2b13687a53ac78c114a944ea9523c30b592289bbb4ea960e784e60cac6dff5519748cffd5f1674f889d63bf76fc485440e9596a58a270dc3112851f738c33d49ed1b6ff65413a5e50fc1c13db652a9874fcd3da68ff45871a47bf7dc19bfbf8b7ce06101b52d1e76336cb3428a8e3c7d5676a1dfa617416bc8141599dee3469833956c79fbb6f77fa838e1f0bea98db10624f1a78b7f4aded5e9ad498e839e7dcaab49b94deca0b223d2259e334ed3729e5a66d766aa9bd0ef28478423c215628a7861eebad75abbf5d7c2d967da66d9bb67d8a273524276b5af65a7b9d452ee7d7369d7d53975e6f9afe52b7b38f87a6cd2d6bafb596c98c43aca310f3ef5ade3779896cba6ddb56e9bdb75efcdbf57c3c1f4fc86fb3446910ba7da9cbe12ee577fbd2fdd24629c7fda5a5cbb7ba9f7f21f0f1d5cdb7a7ef09e13a4f485461f38478423c215cbc3924e902e8d045854fa742d5230f1dbaa6a8421f3fcc258ee3b89c2f7b1b63bdf86ffd1eb25b9c7fc686101956f5df90a99e3bf773a512572addcfd9389612e7b8cdfdbe250e82d94b5f0d3d27c2b03df73764e97bc81eb9df746f0e4469bf4b4b867df6b95ae25f087c7c56a873bfe3873bcd5f7feb387c21f7ccbf0ce32e7ff3759136d156ae71f26631f5fc9be2b9fadeabf5d5fade8b69fdf752ad756a75b5d6586774465fec3ea6f9966efebb71ad69f9de8cf7a01d4bf5acee15a3a339edf79079ce298cba013a744d1134e77c29679f1304b34bea83861abcd594f369ec608c543e1d42239de2c728c90610db669923f917b39c61d745ab41968a154ead28c613538c53ab2cb5c218e3c71ece1a1bce19cfd26955cfcf7dbab46d97ed6319c7523da99e544faa67b5497b83144552d4583bbf23ea07e8d06544a4c7fe45fe490dd7f915de2e0704344cedc2ecb546f8d1de437ed1a74fa3203d001dbaa4a0d2f3d5ec85f7670dea19ffc9713e1f3918ffe29cf527cf8937f7cabff8747289456ac59662a5562e3fdd7ed6b487f9978ffcabfcf22f6a8d936ffc942ad5935a659de346fdf95bf45720125562c915487d71dce55243ba9ced6be811b77cc638a54af1702d2e8fff86bcd99bde857fb26f6fe2a955bff4bb37c6c8f19cecb78fdcbc57dfc7fa3e4e0de99fc6b793997e0e3ff7a534e65e63fea5565dbfccb0cca494327e3166596ab15b97ed3ee65fcc9ee327d3eb977ff9e7c2fd944aa2e4e34ff144ef3ee6dcfd2fb54aadb6c6099ee7799e943e7e8e9092f0935a61b183842c1280042324245804394589692c7d4c63a9b57cc634b1bf4a1445038c86e66580b499fb28e986b499bb481aedcb6d48f9f28cb0bfad7348ae499b1a9b6813491494f6b5a9b4919456a1d401f73ff9564e18ee7f37543e5dfe0b1afb7213cd18dd87bf1de33d91d248a264d1ab52a948336af8c58baf3ac70b1a1a1a1a3b338331a639c28c6950e3e4af12e5cfb448538d728e35319ecedf3af9b5755e7c628c18cff9d77c0da7a1e6797e55226a7afdeced7b7951f5d9f233432f3e2f435e7e5e88bca8f44fad6241a2287f4ae98d41344f3f22a1d4f492a88f0593914451131589ea7e726f52cdd726da451245f3f3f76b1bf9bc788ce7bf23c673fe316a3ec6c7e034c4f81aaa6352411a73fff7e243d3eda2dbedd736923652ebe6ada617bc913fdf74346bc4807918f3dfec2f0fa729e65f384df7f9e8349faff63438c6f842ca1793a6b341637427f9fc637c4d778200ff187f927c07ff18a71d35326864d474313ade51ce42cc5e9046c63b4ce7d845d2663ea6379111fbab4432ff6da22e331367f6cc9d99a1f94d3423fff0befc8bc12f5ee47061ed7d1a1811c68bbf2f9e76df07fa0b9677d1953ed339581e3f4df7d9747ceabeeaa392e9b4eeab4060baaffe00f9ea8fcb8bcf8b4f89e823ea26a33ee76754d3b1678c6213a39bbd17d680a71f18925f69e5a9bd27f6a7cd54a059a30a5596c4da738f83feed4d6044be09b4e7feeadffe6acffd0eed39be43ff46a3ce8131fe9ac9a00f744af10402c08afea7499a81bf9ff367195f731a3a0d9d86628d8cc75e5a128629697b69c5aefb1dff9d8cffaff99f41daa40663c6d37ffff2d9ba1876647ccd67af08f62df01fe32dd03d7f1318d951f3fc4d20839fe6c7f8ff186f821a7eb21c0231feff34f98e187fdaf1dd738e7d963651e7a82aef883da5dc7c74d96941df07baf637a696e1fdec973f99923a6df957b9fc89e854747a9d8c4c49a6a7a6a40c6c007b3c6bfc6398c4b089611f8f44242955aa07937124511f7cc9a02251988c2a12f5e229cccb88f97f693f31a5272256970fbbffdfd1fdff4bce77f0efbee33474cfb90f1c72c2fcf7d292b1823468cc7f27a27f491bfa5af7467ff454a48d8c2319482efe9b7d4686e667302c4fed4537adb516d37803d206c32437b54c44f389cdf2a616bc914fdf44346b744f399a871065878036f45d4c3bf917a3ea3a3c67e47f3b04a4f6273fddefa2fb66a7e15f0592757b68f799eeab4064baaffec474dffdeb33759f0c2a9dbe4bc7d27ddac763973a1a3a12489b287a50893c3c28f6bf1ea9d391a8f99e131bce19704812edcf02c98003fbfc4b658d7ff89f56a3fb6fc760fb5da7c919df4139237e545d0b3467d0977bc80259200b64812cd08daaa8028a3c2b890a626db4402fba8c1511f667813e18e3c4feeffedb43ff82baeb641ccd19f41fa953192b226c208992424312851fb3456c903d629158964d2251925369036570c0fc0fe32e5e14f55f328ee08d859ae61ec21a2722195c98ff7940dad0bf5f54eda14eff44249f9e88f4890846978f9d88ba05eaf4935145a71f2d9771e40d617f99c56ae59744e124434e321489ca44b948a224ef9aa5d603d286bed11195af92f456246af20d481bfa300332c4fe451db74bfe2f4883267dffeaf4ad9e4f02f954d248e9661fc64837fbf2350f5401c08a46026005af3e6d9f339f4d67ea587868b76046f45ba9ea1c2c9f69b7a3dea512f62c33e68cca98332aabce51d238db36bdcbef307117bea38543ad0304f1ca5755de115bce90d4e3b3f8e891e5659775df077ac5bed471ffd5a01cb8d3dd877bb5c9199531773680c8a88c5193f26b3da26b90b489ef19b1e3dbc71fffdefb3680a899d7cf38e595470dc262b769f6da524b82174512326b6bedb0b7134ec8eba775e841b17794add9e8943e02ae4b0c4d8d4d0a54839408cf5e3ffbcb5efdbfeca392cf0df90c91a89a89a8a691aac6ced9df788f481492d8ef5d65afcc283ba21205fb4c056fe257a222239decf3ea31fb1df9334e8391cb3f1cf937676781e864aa9f4ee45389a44d91b47949af6867f233a49e2575a9718ed85f0cea58557df4c8a3aa7a9d1ff70fbe21c31a38e4b560010fecd8400362c040063c29fbf24ff3a6ecf8106768536c39b798129bfe9450f607873c28fbfb39776c40a226a70d8841a22aec94d22e056ae177786535423ee1c89ff433fd537c1f1df21048aec305d7e1422ee15eafba0409c92e4142ba4b962cc14b2eb6042fc996689c483f6b2963d4c253e229f194784a3c25d8122e335ed1fe32211924f41c6bd6a8aa3963fe6ae5e3f353815c7c84b823eec8c5873bc25eaa8984820c6b3e2ea8cfcf9e13bbbe9dc1f66595582b0322fa01fa01ca806806d4e7474eabe48294f8a0073de8410e0f092504f1606b6d7fd6b8bcbd1682b83d24b6c6fdd634fd59dbb4bd7d49d33e8d7b486c1c76eeb52d731e9cc673f467bdf7f678e021e121e121315fa236032a41ae30a2f66bfdfcd107eb629fc17f31ff6638eaf7af8f1541cba7fec81c582c168bc562b1582c168bc562b1582c168bc562b1582c168bc562b1582c162bc9dbc1b6e228bbc2a767af5da1b20209cb6753ccc3e8e73a99d73a195f5b51a4eb7f19e2b282a788ab0a22322ff9c743bec5895734eee5732f5fdb2fd37d1b17f3325cccdf90bafbb8df5c9379fd1c274fdb6bc9bfd3f6cd70d4e57f331c75fdb0cf70d4bf2de665b80d2176cc6f9e13f3323ce7e5350fd97d5c0c87d2c6f41a7f79f8302dfac5c4f2f535ff5c5a5aa40ca105a8c4edb556b9848a46000010006316000028140c888342b120c9d23cadba0314800e7a884e5e4a998a24c22046611405710c62c6180200218410119a199a150016746f29383d0d840dc709ec8cf6838b134d1662ae6aebdac3757f84124e5012adbefda5b5c71de54d05c286ab9b3ecfb47f2327e615f4b7d6dc88bf4d14a6afbcf4e5af7fb417f7665a5fe48f5a062b9e70983c97fed46c03ddd643afbd4047f261326e27ecde9414704ac0d2283dcb3b98616210d049210816bea8a0a1882156a616c4b7f18cba3e07fbf48a50062aeba67dbedeed8493ae7b00a2ea99e4a39430300d02116bb548e5f2f0ff7610117965a0b09048557824061c4367148e4c481a18158ebdbda96bbe9cddc0fad2db5518a617674b122d490195f8765d2c227938a6f93a090cf5d220110d8a50d8c9492b3d2e0732f7665f6749e320168770424e02df44fd78f7e6bf6e64d97e2ad8cefc94ef6f79a5bf58961dcb8b5fa33efc2e7da567bdae2b756f158725c963d737c71dbf14aca4d8156e7aa2ba17fe86b48dd4cf9086b32a7edffea193a8b38b2f95a063568b29cad0b0e4ba770c1f13a2a8527e488870b7b1f1ffe46c0879ed63a7e0b110826cc8cd794daf946ddc097a15f0a6c2b0e0503c6d4429d07d84f9cb85f8f5470f83eb019a824d3ccb0758ecd01f6fccf2f077ddab530c3022563f46787999e8ff633a1b1327debe9fdad829d0a2faab44fc1c6c4ab3cac76c1aef87973a452b43812fd9d1712f7c1a983510e740f76e26f5fe90dc425ea7cf30e6eaef343327a2322db876fb62b1e4f3809f0666406981e73f27b374ef8b902a6e105efb7d8c869a8f3454a1647c64724a66f9606559e793819d87ef044cd152a4814dec59c955068c7d729aef69c87e562092e757c3cb0e28680ee3a16a90cd9b7b246c01b316e91d9a3df33d3fd248ff54808398b0ff496967f5bc6e853d12c0aa6e1681c5f38b2dd90798eda3637925d441b822d86280c186379ef000830570b9a10053a0d82d91f83b05a093df6409d501de73fee9cfcf6292d0a6a85c6f2f6f7f6c20f0fd2c7e09f436fd0a32fab4a167e32108428061bf1d7e0f30521820605a75fff368a5f8be7f94dde7bf90298969b9d7ab504048ad1a617a814fe3a1e4e24d1970a1bd9363f2e0d4a6b7db2b55e2b3c0d773bb1180809303791de6dfc0e4712f81c0278e140be549303d336a1f842cafbc1b087c4506bd0217987314f0a3fef6047de30a5348c188cb4957dd57059d34179510f5adbf74a1e26c121025f251872e8256d3dee55f187fbe247ea03eced85e72f379ede6c655353d18cc5975319ea151d34ffd24ee1483299fb71444a652a83a778099ebab75ee4d3e2061d40c99aa8228846ebc215f2174e1a7ea319e614f8dba0117f0b0d4ba559d590da289310774ff8198b561c4571c59edbe16ff5cf2ea0f5a0280b362242c9a6f93710677affc80a587970cfe175e50f6d335d158534858600a2286a29e39530b8416be068029be67f53d69ed025b440865c11085050beec8cea6c05137622435c361b482ae4d672d7d5945addc570683b0e40395ea592b28aad340b842e2b1b9f93068fd02ebcddb89e24bedb69e2372142b10c3296c918771d9d075bf5c37ea8cf0b97aceeb6fd3c97733ce8773c3c932239135099563803d683b855aa67e5e1d58184fca3544226bd5e3e9e8830dd24f4c155a8db17e30deca4cc48dd6afa301c7d95e6cab039346aa44ff06d560525bed1bd4c75c158ce9a65cee7c98220c11971ae640471531d610ed4fdcf8e1c2d8e704d449931ead47118893c3f4016264d8a134806f3010c29a3316f4c5c8ace1c06f79eb219163f9c68a477fb1acace17d170768ec8056cfd71a551791b2e5705d62fdb226de8127662e3ed264466b587846c24b2a7116f58bf6e0db4e1c99321f1958980e6d81bade5450b942c4ac969590f926bf847774771d313914a308005eab2cd6aab57dacd5f5fc0ead8a5b7a78036a0fdbeadc8acf5a884771052f19c27796dfe4c4a95b04220b03bd88d7be24a0b27711c05bbcc836ae40011392dc2e22ab0a89fbbec0d936429ecedcfcead7ca18202f3bf4c920b62d44e387bb6be47de0dd71e2eefc84124413710309570143efc45cedce0117372179a2a80f64e2d57face0223f4666a1ddaa857dc2bbfe5ffe9f59732a92bc9c7c22a04ea233c8d11f2ddef343e9f6538f70311e48ff3d1d1a81f8540e5dd4694a5cbf13dc841fb3c86342b49c2c0b0515f6b0ca6488a2d3cd8b6a23fa1e82349c0bfc137d435b57f05d2c539bfc6b27f08703ef0ea147a35c13bf4c3a7fa7ec901a667755c273c94445652b6cce154df5020ec86b2556189d42bc4df0f5f03333a3d3e2dcf2a17fabc5aee8104ba83fbcd5cc2ee539e6120cec9b36eccf876e0aa967c24880f8cd5f151ce5538effc77d6d48c2c4923fb1805603c8eeb0288081885b67b89e8b5fbf4217ab1afd4ddf1baefdd036f6c1e195d9fdab1262d34c8b255ea0bd8641a2bb5a62f631ff7aafcdfa36821cdba46f0769c7978348994f884fa4f667179568557871518c1f03f83c036fcfc3118d572c2e0f25f8c09ee212bd9dc5eb96b37ef1bcf5ba122bbf7656a7708131afa8d82ba5a6c766f3cea2070a36eb498215551be26d8f2cf851b1aebdfba68c6ca7c4fae74a83059cee8fc3d1c939f7e3a27d8732d2823a8a41eb8484142936e7c4295238706d9c32ffe589453b40abe4113e408237d41740a2ae223a8af0c526770381940fc45cc879181f6d545de69e87e0658bcff7b41d32d19a81473beeb959f80a5ddb120777e8753b4bc7b42fca70813f52cd3c01e5da42a19eab6dcc5b8dcae70b37848b804a5a5442da01a38101885cd8d00c3c78d36d2cf53c5d81b45f809a1f89ae0f2e72c3a0838ec8c0056dad64797b876244f6d3d9ebd9296d713ec88a17fe86b26dccb145ba693af40ea1951107901ce8cf93d65479ff394f8fa740cec2fadbb8eef0316b4218d0d7a8df02199834a11441e213d9df48b39654cdee648f815701b66877f99b7590ec7e73b766932078292d4ef9a7ba5e0db1bb8dcf088e48ca33680d4d5beac44062ccc316703691015f00915678911d056faa79a5b178c494d43a58e6b926ba2db91940f04a999b4b162dbf8988df8e077ee10678f6cc3bfeafe28c00f6b1d5742d997e0695f70d2f0968f72ebedf55ed870c95ba78bad2aecdec7110be19ec12e0c916bf4564540abf7848b9367a7245b67f38ca5f7f15e6aa980f11cb7256e44a08832cf488d17c9a7765f1926744c8924b8e0cd239d1cc860dd166a6db4332c2efc46f0da7d7cd89062a122235f82f4346cf55f9b138a3c2b4e9fcea88077bfb1b08f93ee1d93676af5a59f7b7b32cd9e8a94b9e01969e50494dbf33a8cfe238395ee058f9efeb02ae94524e6479022294fefed7a8f9c58f10b582eb8d57280112c30b0d6620ef761c0e4d86a4ddaf45770b0b660ff5535fa75d29ddad0d08c8a511d0d70626217072c4900fbe0191a1fcfd0d82f288cbffc656ee76f1dc67d60143acac0929229449b00b8f1478acff452d1051597a57a306a790c0c5d9405d880940da9c7594276c55df5a7aec41fe5f1f8bc9f5df679d93a8d8ad4189418a65bbbc632fd630ef78ac2aa91ae8d4b061351b94f3b1fef4f31e5052b9f2e48b7c64032d7b008670d064073f1509a2c7204a658084364ffca156da6580762318384307be631d2f8f1d28d26d37d44a1e8ab5cef2a8eb8507bc6e6080074ad4be7ecdd960614e03eb8f1781abd5d859454be80236cbe3aca9aa3c3e654e56c841f24aa756a1f0e40ef5293920cc896b2e6b96f7ce652f2bc85a6f88b3122dab401456586557aa865504975a42f13ae8ddb0ec542b4234aabdeaf8068e079e5ca6102ad401434113a9b835078490b466c4e71546814bf8dc2f1aa44c3260f9278f063e542083b7ef790ee48184e0e26977074ab612b4aef0c209fdad25a4fd14f8e0959a97953b90701d99214726c947d2a8c5ed4c1841cd99c49b82df9f586787a40157709a83db960a871ad5f04304650b3fba464e1d2dd31bcae22a3a6c58caf20c44eb8f772f8aff0b05d62e01d3d3159eff36710fecb601110fb761d5207854a3b82f70d3deee36a7b68fc43ef2f7c16656a2a95ea2265005a94c1554eab1362196743455ff3ee472faeee4afb63ab62fadeebac6b9077a9f218aa228e0be8e28edfaae5fe13a7a16cf0fae1641c45da46e33d62537b9be7dc6f8241d57f2489d38617e22c6c0aff4237956865d8c7ed1952ff8ea3cc8e3ae44b007cba3c9bf9d209b83e4e15c801d251cbac5c57bcd6b4e0f7118a21a56771264921b3826c820d17ee9abd924e7cac946515e690953e0853856d2660f52025ecd66d85c5f89e7308d37f71a97e9a6ae47cc98f922cf340576edafc4a889176df848dcdd247568ab9c84babebf072f218bf3edafdeaf8111ac3db6aa62e9ffa18109ed15052aa54dab132a1dafabbb9542b219cb40a043854953603d3de10f8e0a4ac977009936ccec845e87d586590c79702c387aa2470da9b359e9193997ce8f166c4a9d403830853b85b4ef643ae7db2ac0235f962b3583cac655662c196622b3c8e9f24932a5b36155dc4709f8779c399594d5c22ed6e3b5e43b09d6b590e666a7866e2b9bd7f1b646125c07daa5d011a7074fa5eb18979be52876be147c248ec3ae45cffd5cefb2119cf2d9b29c7e850afd7273faee26f49211afa0e87189c31df7ebcf11f92af03b5eb2044d728e084f2cad67159f067ad5d34e3108b13008a2f0c23e03e79dc37aed53eb30181435c31f6e27f7a530608ecc0eee2a0be3cd01fe2cf6df5a64a038f85a9bdf8912706ea56cdeea893fc40e4b4ded585c3aa3e5a665d9147362f23ea2848117105a485fe83c27474d3b25121bc8df972e74bb3d0d58184946e62f2c0322ae155dbd9f43e7a12e96e7790cc4ad1e2fe6db5d2c9791cdc458190579325a7224fded9e77bb099b17b7fd908563270ade1c180c4672bf94af64731434498390386d50ddf3f31a7ecf4d8fe8e34b73abbc178b07d9b5370383420e690b7a4b897269e7d6183d1180c7b65d8516edce37d2bf2b30b7036f1d111deb26daae7a805632821038e794dd4b22a81fbc84e3788f9722eccb2e81ef6847db888e1125002970572b1ad015ed08ccea71d72a141f39c41ae2d7d9341a83710d4ad3fdb4c2259c3ffee26a6c815d8dc86737d00f81c392dcb3ca2a85f5608bd5e22799e5beb40c5a8a2e140cd6f50fc8f0c73af029527a64b07b4f4cf0046f50c2331f73162ba1b592f4e3aa2cb87e62eacd8506468de3900ccccbad44eb0f009594cc43e3177d8234fa5d756167c548ef7885b38f1c1c93c3cbc6457548f7b485ab55dadd36467d3fd106d49cc335322b2ace62d7d7442ebe8a639442aa5eed34b82f5bd6ac0a85bdd0b95daf4affca92d65346962b5facfb452ea0fcfc6c7e42ecfb19e7c8b8f0a8948a264514951901e0836ac28ce1719e238e45110ac7d5c05bf6ca6d8025c31a12935efc5d1983b893c94c616615443d8ad994bbbe7db4b103c74195ae20a9f55dd62cfe1a3d9baa3a42b29f5b8acad78d217927eeff1ec31d13e8c5bfbe22df6f93fe57bb7da7b6bc4d86eb46f5982b6f251595b9133533189bbdd6dbb0c79bbfe6c144285895ee87ccdd911424c8a4d6239b51946f3addc948af972993f27d22e9fd2457c18b8cdc60b4e02dba79c7376ad74e9d4f2181a84ff0f0524b14c8d9a558c005614cba3cae1c407d90d213a8814c064e5baa8a32e6568ccb353dd57d89fbdb881038ffb24846ffea6d2b65eff1a3ad42c17d1829b14ed48cc50b966a14074e9afdbc20f21f88d957bb7909343c3077e0c4adad665a1714d0d62b4450dfad113c9b3e7b19c8fcfebb3ccea3190cb80875199a7f3bb9d50910b6ce202c316a96101258c1dd4bcafb75663ec5964eb8547e9169ba93e970b58d0ad3d07468c5afe9961717339f4b4bcc7d3d6c969340ec9a4ef3924511c9519d1542562f364b67f5ca19e39cc1995f57c2ba6e665dd37965e32dfbe08d4bb0e93210a77f65a9952781d54772764b72ad8c5290c5ed2fa19325429c8e2d4a7462cb2172ebff1fc0ee74a1a14a67bbaf9c229a9c5cc2f6343ad2e21419374eae015f0a4cbdf563a56f24308a96fbc1b75ab1f8ef7418d7d49d1a9129652cf6749679fce9d8bec3a04ee3189c451aed54db8c16a418d9de22474d9059febd075b45677f3f9558ba2c6285b8fb6087ec5a947903fc08a26d92c3e34f82eb49c74aeeb1cd79afd81ea5f58943a1c7e52edaf600918038b20dc6d6ff2cb85ba8b46b165c1366e06aa1c2be3990b5a86267068c6666c0d34ae5182deaec9892ab1d4530d02c16fcd4e3cf502b4910481aafdec353fe95e0906d83276d1c7267e6ee224d0b70beddca1f20d158b1215a42b6c1494fd5bccb05b69dbabf10b678dcf9b81a68746f42b6c7bf0350a96d1460a1b6a8963a957ddf6e0b0aaf2ef6202669a57aa6cd0342aa77f54c63c1ee8522da1d002fbe4c178e3dd32a66d917a2578a122ccf34631236b5b274c8f0b300a3b1569daf66b91982ae65743a8558e98a6182835c2aac3c7376c3a35ce9e662a3ae634048bb0285e8ffa7a811b8816d1053a868d644d80d8fac2b341daa34298e7ec31af6870b962737c52034f0a4ea867708ff132ecac6db2cff49d400fc847d68b1ec89c6809f0c293f24a4e5a717a0905afb660b135d9ec39d7b82065c739132ed04544966e7b783b2f92add39b22854ca30a734dc9ddc25988a6e172dd11611cb1e2c567a438909dfd082a770fc4a57de0cc51c5e8bff740a59dd812ede98261ffdfc2bdac12a716d3d2375133d0835eef0792b99e09b4f887919b9ee129224bc5f2929068af88deb0dc8d05d4170fba2f11be1b76873330a668558f69bcbfe0fd61dc0af3c1ac57b92022f1d345e8717e26708c6029479b13592612cf2b893fa8f5fe1e987aae703de3fdd74a2c713e39a950a32700c1220b6e9435d99fc8150698f5930034e1b0ab9f970b71347b06219ed3f07873cbec3840cc90ffd585b1539f8f8a9b7e328b16c2cc2eea16f1169311f2cd0da8b066f591c3fae37d5c5c866694e3d41bd96cb64cf83558a33cbd0e647df6f81352d3e1d4447aa3e064cd08a4337c16a5e13d2731d403855896eddc06874044855b390193dbb3be9f2db1b4232a4a1fe3750034effe6e4f9ba33db6fb157689c581d163a0975307fbe316c557d32b7d654ec995c89ae19756b2ecdff76c8a10c4f1b6a169c054af196c377c81ac281b80d150380fcbb06e8c0e07df372d9d43c3005fdbbed2c60fb46c228cd621f092ca269324dad49093e34ab1fc0e359ed9e017e596e8b46b2ed9becb978c6a3904ac896a4635003ba24893ec6999ce8c383f04ff74d1591a2d2f700f4fe3037f0a4fee8b3c8133882bebf0acd051f73ba54a4afe9b1f947bad2f7cb530a00c98386e6c65bcb3671f7f06c10c2d3c3c67e668aee1a3f4ec0fc944116bd19af5351b5311b684354b58de49d2fcf29fbca854190d6de12a280634ec90058ccb857c45cbdbbcf164752091df17523e62132650c4454f4b83ae87d0c5e78eec96c77f739b4a7517a42777d3b7d0ec2b322b2c4e9f195ab4e839afd81d326f54f09fa0665ea4e4d7d7d361bb89a88901b1b5df8ea9c1206cf39f970c7ad22344312f0644c25625fd7f7e0b37e543667684cfbb9ab18ec542106cf8620cc9fd60e599fa13a16cfd115fa0f8dc7afae05f3d8229771717be106d9aa72fdf3139619837b29d63f65a922d13097fe4c21469a68e0c511ad7f3106c473739d800e703f422640b91e41bcb4ec225cde177eab3606f793a9610d48563392cc927441ee32228a4509538c89ed172502fb32d2674bb268dc3e6ef50d641d4d3e2307abf3cac7f659f820b21327d6e64b4cc058b276d8f60fae2e9ea47ea858b9201aa10b3399820f73b29dc330128d70b0285696ae643944dafdab372525c7b2ccc21ab719316a6ee85b461b4a9f600ed42ea38bca80f5ff9939115107f73fbc7150460ad1f9bd137de19976e73527ff1359cc42edb130afc18f2efc8319cf8d1fc2c91d7c12b58b2a1ad1218cb697c182169b75c3d807bc1b7138f42354d6ec476f7ef7ea241c5c66536d8532e01d428e1ea1b81be6e70a8829d61e73418cacca4b5d8744aaeae666fb866aeecb8c609c158bac7287b009b60b0209024567a7fd7a47d431a414ac4816a3fa2b2f114c2b5fe105eee1c54d404ab9ba502a016523df30ae708ff487307e0a1d9d849f7b0534081f0f74e58ae3df57bc3bcf5ccc22d05a6ce2571b0b93d56cd4cc5761982a55d91e7b604eb2d10735b32cd2141c462745e0504a02bbe94788d18be5cda6129906ad322e5c02a78afc4091f8d50d07c71454ed7a498dba01733a091c7c9c3fa6564af770a7ab5c425566c966db22616591a36be16b931b4fbdd2adf7326ad89450160ad2b1f7cfe43625a3e729abbd6872b97ae6b9caeba459a2cceca813abcdb4b143ea058cb1327db3cf528cb6d784d93711f7c954e6093500eb591b7336021b1a93ff39bdec478f0cf357fd8f873ec522857b170b5423f94bd4d5e3e2d6b32000794518591c4820bab0df0e19e99816cd9b391937d335a70e6fd91d411d71c18861d16e7edf883c8790666093f4bc6f0e893669c84cbbc3ff64b8852d5ceabfd7e5c382879affec9e32e4e1671ab6a4c466b462ad7832a5a9c407a5fd940eb4ba7382e689b68282b458f78f1458f9e629c99b698251067d8dfb7cbb706562241d749fde02f25259dcd5f3e60050044696af814df2cd7a07e86f886e972d02c62c200ab73b15944d7bab129584ee3b6fedb7bb3617de1edcf3ababfd3bfeb44962c97f62c0b66240e55ccfc04b16746f7f59f15e4030fd50d4063a2006e34cce18518df21c05c8d8fe9861ac70879c3e52fd81b8322de0e3079aff0740527d1530873eb1988768c4304c7d9b3e37ef054bb117a017d58d64e9c589fa64c5fa89013a106461f3ac25027e53f5e8fda123df454a53f2c56984c94d1644653055a906301627a33dd53f8ad4282dd495b48eac3020060d7a802f4948ba168bbd70de3c4a15bca102dd79e59f97f4b21e978e5e60129ec524502d1aa7f1b4ba798d1b32f443e8ee6817a65eca4040204cdc43d9de625d88c039779851cf04d480a7efacee1336abf1044584975f85af493c32d37e6fc79e563543a16382eb23a3670d804e8f861ae0892090a8a098f647d78738648f210ac9c72500c6b869d8746610dbea4f7f87f5f6efa479eb003187722c1f030a7366360b8e9856ef1183b756ee565a488abc77866e87f7a967f28e4e72b5cf5264f9584c9713fb05b23624166d9322b4234d068ba464d693941b34f3d2524afea11953cf3843f6d16a01bc0745643af3013f35e1377c0169fd3374d56ac005133008404c29423b23be5df5b8c8e8eb331e6891a298a6a5ec2cee8995325715cac3ef3a8d3a1d1d5e2a3c3fab1ef27ae298ba0ab836abac101f9dc4e5047343774f9a9cb810020dfab251c58d33847d4d6ac33d13dbd39c0a26140275c3a7a0892e3b789687f3d78c24c5b5283e01e03b87634222d2b56b61555d1916f8389935468982f86798321f62e413ac608086318e21cc555e57acaa1282a7cc03ea2ef2ba7f666bc666f5e8e7c6661a0e4c562c3abaf79eb217797535caca59984fae8adea9d6b6d908cfbbc0fa32670c06e105e3e2a84f51f896622ad9e943c79d3b4e4ede9e40a30d440afef991a143ab696d018d96090d6741f3bf991d18cf6eb188cc154e1012f901ab806e3c7bdfa2c440d57c0e7ecaf12e720ffe9bff21d32ec8c51867e03eb75329ff2baeede0bae2762fe5ef6e14a6f2e5c4a4211b85122de4c31400e29f1f60353cfa9a8271961fe87c8b49327e899b82b586a70e0dfc906d4ac9a55144ec901aa69d737634b6138fc48ebf106ac246253bab965d2193a9ae6b41cba4637bfb24a3def3bead22799eb0c892f5b942e844bd9270565ea987b53eaec3712da17d8d02b122cd018b7e2ec5bf74a5645189ef94071cb72b1de6a2d4ad1cb793f472aaba35d85810d84efd0d0a0743d56420a036ea3e5e50080b0bd90b28f0d34db804302654b1bf72d0546ad662034bd05756d4041c43da69105631aa70882ca7d735fbf841dc4134cd6034204caac578f7e454f8f391e1b86a19602b9347af8dfcffd6d520b4a7e9f686dbc774492073561759add12ca4a72ec34fa6b680923ef80b6488aa15c06fc5aedefba624f371cd356488fde14dd275a752639168609bc9c599e10968344308df29f241bba255b04ae7b599c49a1a85ac672e75e41b3b8740c7bc5d3dad53a67f7c71883ff4f6b196aaa756ec2b65892a825154d42df39a088447287d2b57b153418605f88fd12fdf6dbd953e9df8e4a7eb8b6055b839dea4c2d97a7876315842aba14d442852a1e77d05cd1422249c8c37a4e171b2f4385a1bdfd4619d79d12ce3c792fb1f6981c9061ba22b64c3d2b424a194d3cfcb49fea34849709219303ba19ef500ac059cde62b99125c4c474deff3b312170cb44d8a337d05f8fab0c580e915239c7a7a453d7f4aa264fb0ad5a5683c647d69a56ce2c44648438d2291b01befc6ad122dab84704576abeb2dd9aa672b26016b832c8dc0f08385e221bb7fd3af45aa74bb3b659d77c1733b92a839ef11260a8532d11b6a8550d462d561091a02c7cd2f3840891781bf6564f7e7e5d62c206092d4d68715c6f4f498cf84fe945cb7cbc0225656a2df9f500494f8943d867175653b254a0496e227fdd85121d036feeb3396b5802b437384d3abdf5a09bbfdd41e969e1380e30d293191e4f36a8ef56458261207655718326afa2204767ef9ba1fe84a2b3f4127ed156719e89e2430d342361c579c1ac2c017ae24355952220b6d3a7a402047a758c4b666c038cde7e38056fcf7a0014ad0f01b8b399244fabd55dad16fe2c803a98d6c1377a3a56ffd3577c876e2861df25b56e6b00f5da31d67c8d5ee7e20edee3fd526b32260d0cfe63eb70478bed74236d0c86eae7e2959d09e9f075e8c7c580f91aa81fdd7c9c0547a33da6ca26ca904356ca2698d643ab6ebbdc390bf3d4a3316cf5bf17cd9907d02fccb45e346c07ae9bf0c66d2586b18021411011e8596af42ee70997fb4efbe0b1139d713cf969b972d2e121ed17f32a174e436d1410389d76c7f8859713770d27a45dadf0f67a9cfb59a4acf800986a60d02be5f65596c5c143428bf1afce8cd163b93adb2aa3d918a57d2682636cd883127c6cc50b21f6c6531bc8e6f78d23c564d643c4201044261e922ce5db476abd3faf73357ab4ffcb7f17b4ff3c17db448779bcb17bdf462df7671e906763a5884e15e34c5d2d9c7305b02193ba91c6475e3581afd7631da05cff357c042addd71db71eb3221c5a020b086f595c2e25f4c1141058ad917000684b972d33f6ed73136b292f9ab3cf6536d84a6426959bd0dc0a01fe75c44096a845f15c5d9c3dd0e9e0b4061d4251867c1cb869c478e60cdb43efe3ef597747e9e4a7c6b03b0b3a4d2dc5a21546accfb5d4970c20711bfb09b8e36b9a4569755b4c538c024585deb2b14adbbae01b208f2f93772546069c27875fbfddc39f3bee901610358d1da045080ea7bd1f29083ebb2687c8412ff9a703eb986143bcc9c6de02644ab9a4aa257fe62e265faa6cc0218341dc5d96eaaf5a628c08dc123f0646d58386e7c2794c946c2af61e4d595fc6f8f28c0e013b39c4262ee07411f0dbfdcca7ef0d7c19ceb2346c58486d1fef7827722c014eda3e10c6429f396ca2b9740053042845ecf6f78aa62866db7d767b952360306a0230de0d903158a624e24340437c6e462145fd804bc04067b69d0d80fed5810c91ed1d09039507b7d1c418c4269ecdfa61f5fc3fe1a71514d663cc4d794e6e6c0a9bce7bc42fcabae0582337d5127e523271538dbfbb0a864b1f10efc42530b409a8b3d7268fb0d24cbdd43e079c8e019ec6419b39ce3d8b5b66047c173bcbb105a8955c5e61d5948f4900489cedda2ab5a47d254fc65c0b721f9fdf322e5afa3d6c4299e090ea15cc924ed2e20c5d6624e958d3f6f86607cfcaa59be49d7a2da68195ebe40bda5895ed9b55a797bff32e02fed511ae16c6de1a37f44d91edbef43755fcd851e6cdef68e9791d0ed4cdb9ab1193c377620e50132ddac47dcb914286c4eb7045c43c5c8e6d38009a2f6e0306619b97c37606845a09018647974a49a0c78535cc17a90d6f5270a4385aa8904a826d48a220701048b54a275c67a9de7fc6537305ac3cd3aef1d57bd60925a64b05a641de708dd51221cc590b8ee1f6eadc17e128c000f6b8c1dc65f585bab50fb96eb787b51e93deb27eee2084665042d4b95bd06b326f0492f4d8a75b506dd1fe946659b893c001c966293e1190ecb5683de40c8fa39d07ea596a6612f2fc2b90da6010227635d5bf8b55f3eeedf586fd5f854313c65a3376c31491a6289927cb74c3cf45cfb6e3667fa7628a2b312e79b9a8e4867f3b7eb1317e6e9c34ac083bd577783db5424b119cc1df58948ea4dc4d79ea68e5449ad0a6d48730a75e6d018115f9541532ba4a9f9e0823ffdcd8388e9ea2e5b1634ec3970cfe76683453f7b506a31196371e91f96f9f221ad2062740ca5f2cd59e490536b6f1811baad1d24765d41385de8102e7b52f5e8a4600d4a95698d06ce2448a14e0d771b4c55908abdca3430d645d2520c7409f7aa922a603f9dc5a684b7632ffcc39266033bba05921e9d158062cac6164015f00db3868888acaae727b51ff7a961a6d88ce0b6a2dcb9d178bf1ecf4c9903b2bdee6f944bd2c2b52c15d0c8f0dced01be228103d3ebdff011dc2ddd719aaa1b0f5395db162aaa37c8c7451d7a2b1db9231374db4d544382510b4fcf811c86d2848a2c06a219e9dc21a844c65e5b1dc8378423a531722b44db503b4269a6d4381aa311801a8ff7b707738ac07e34bb243e396bce6d6b64233cfa179648fcb55689fcf2a22096056d8481083aa748e77713e2288bed4b3f51e9709233f02be3854cae2ce206acbd67f4bd9ef708c73aca894bd756240e94340164be18bb37f909c601a92725cf2c79072d9dc57871781486b9f15fbdd82289621352857e28549778869b7ddadcfacdaf96454f72d3d87cab1cd6b1b923efa100fe7416cac92cb13cf577e7484d039ec7e5d5e2af2897815ed7fa428ca2bebde590d140d529964754fadbb392d1654d556ff172afe4491f3abc4d3c3de9b2b04ec3b51053af6b562673976b0b048844ca1def81b465df6ccccf86f6b0f862b10b187c1f38e35917bb8a9671c85f9f7f91b26ec52f46e3ef00ccdccaabbff03fa07e4bace15d6d0d8ceee0504796758c61c6dfb8a765251fa62d1d9fb23e401dc755793610693e875184386f82d1b2946eaa8eec5c8907967597bad25b2bcd461ea5775e89ea5599d86b421bb0d2f00418f0b211a0e9d93671a6fc38818099a59d37451ececc9e9145b936d197203e47c242678d663498736d1895f0857e238c9c2c40db020e9b186e53d954bac50c325bb2508f558035805c1fdc8fd69e50139cdeb0235549a5980d33226d814b24da58c4b8bb6da1ac85281a0c0eaf99ee99b85fbd5aed98e6df9822cc24fb12d699ab7e81ccb200b820573947612d0fdb21fbef4e3ba0b22ecd8b61cb190e2bd69e93a6415c20ab5526ce0b58c442c1ba5f2f6abe1736cfa274e649be78f4b4db48d258d8e188ff7f123359b89914d546e7a911dd3ce1cdbcf8490982b2acdf22b05d1243208e9e958aebbc8b1a6264440584a7900c0498aba51271495702c097ab873165babfa6347ddb2ce944402cb487ce4b981fbfeb76b354c0dc9ff6f3338deb8b1ba9e827a74cf5e0a67b7b1d15f9460d9661b7bee425fd3c216f060a5a1b01cfd9db3602b985a7819bf91e2f377aba1bc9eaed819552d91a3fce60d5aff67f11c7bb67ce60635219e8dced839918f1b5b33d540a2e422fabc28898ce387e78128302ec185c965e72c9c7de6c6927ebf52fbe847113db22b93a0b37d4c78d26091381f817929a2060e9e68cb046085af62bb8f45b05a39cc989da0d9e79b8a5559f986bb2e615d457793a5d9719fb2df9319bcb13199000bd25a8da4968944af8028039de478f28a8b7c4eba8a6f66136746eed85a2c1aa01631629a0562b31618664eb282d6ab52cec54cd5bd3100ce7b8038bf8c7ba1497bf2aaac7d98426dd85f838823b394dc94acf91a83a68e819260e990c2031f318a01de1672fe1fe421a7e32f52107c8f515031b5bddb0628810107ff991efd2325bcc0ee94dcfc2912a2c51e286ac8a4619aa0b74c5c89515dd9ba74bda78d10b41088074b24b54286e32aaaa59a0ea7ca8fd2f921431f83b08afb9c874fee134793230f8b47e23e577785089a8171be57c980bae49525a0d89c917501af40a35006a6952a12d397571aedef0acdc3c052dc108562086c9337b8f0139b707280e74cbf5f93fce1b0c5f453ac6d1d64eb4e607ff57ed83028b1b3948618b6880fdbba6f12e44894a80e20ea75c672c959625f1f578234895ed87d32815c75aca312963112fdf247e87762eb38ef7d620b73f42563969956c47c8decc4e7ec6d8ec1ab22e2be61227e7a40c2503b7048ac4aa8f527aab32280c14668eb87814f5c1e6b3e6a09f0917327606ed30bf837577c5d2202102b27e9e08ce0dfe0afe109ebb85447f9761b820c9f4387d79f8dee35c4d63b07e9d57e2ec7f169c4ef37a1d865f061d6d163ed3544e19b2d2ab5d8a9e345f39a273f02e6880ee0ff4ae2a93fbf77793423aa5407dc96211fac4360007db3c5cfd2dfd44799447719dabceaa9129fb7d496ca284c2f082aa00dd6d567cfc254b9c68a3cfd6bda96059e5f4bc4ea96ff453a1f27698a302d851cf1cb48b9c523eabd5c3482bd5c2400046cf54c1275be83111b6d4a73b18324810eeea20e2278e72af314d9f1223fabc88ecbc34dd78e87d8628d8a9af7700bbcae8565774abea996a5b2632d8dc80616fc9e79a4cd5c096658567698dcb83e1b87b80ec2a0b9e47e0805998657667234407d04bfe3b48ec1dee80e30b63546edab78a43cf65875e91cb8923cd47ae47d20799df580a10582ac1f042ac23e2ca273956153f7f1e4100f69baab4b70bfa24c76eb0a7778cf75c5e252b8ab40b543e91f51569b7e24b6dec55201b9ff2f4c92aac19e08eab3136b32e8038ea1979c3ac56dfc86086707162d587ada2f6029a4048becc2ebe0913cee99adeddb323a806dcfb0715658b57a2577d758db26767bc1166c87c7a23c487c30262a2b6c3938ce39d27fc00b840224f300081d2828d8cf6390f3284b89c6c1c4ad37921598ae40b47a51d7dc3cedb2a37f3744ae7a06bf07d8cd861343063b9ba79cbedefec5b7a5ce7aca283e1e60131d26a366c2064a9479bb8a7a7f376fd7426929bcc1c8c133b4f02d1060325324c050025ffa8cf6b9880a31dde9726f77fe8503e0606ace643f1c4a0c0065f857d483d0006405270ad2ee3a31fe0a40647b37428d905b47b99aebaa17b50657cecb63dc16a2f5bf860c5d1a9b866f0b6011e53f4263d81fa9b4a2039186d1c4e405052c97b9dd2c17f5f34112cec4a312af78844896942d2c6a67e1745068a7d4fbd1a172f668f4952a480a4b0397d61c7e26d8e0e2994caf7177e2d931f91ad0833ac6c37aa96238d4b3da6373c765af79d71ca3b1376008bcb43d9763d28936d771b5deb372655f727152393e08defb500cf80e24922a24c84959558dcc5eab90047d7358ae037632498dd604fb0254a30d3842b5798abe0fd4385af61f763d1ba82da5c94f1f80d25aeaa4425eecaa02f944ca7f65d98aed94e09370d95a46fb7b6eac3f6a2f005d3eeba3f0851d59dc0bd0c7c7e612875042df57a5c1aa19079963ca53d88493af0daf0039c41cad70c8e5c81e54069a23c369c3f854c1f7d8bc0a3b2ee78be89887edd8354323d5d315b89590b8c7f154c5d168ba1309512d8490a90c82fe5baf3008b249e9d8eaa026d66e49318c946cb1eecbb666f793f279faf26d535c49b5d8f0dd972c5852027dde6ad154292e6fe9579051da73b843b181e55da0c93151e63decd5e127a5dc101940eaaf828984e906b68faf359ff15480b5fb50c4341d5fd0691cb4f487bc740ef5bae6c31fc23b20a1d0c7e799e2202c2c552570624503c2be80c14840b43a498a95dd2e831032cfb24c4e2e52decacdca18072a58024ec55d77856cd28bf4267a0ce9a53a5e60a7fcfc08c6858f043037d0b3964e0ef0e41794bbbe4a0281bd0256704d58d6ece3146b51516d6aa51bf84a95c55d698675881988241ee1361c486c96be211a0b126553015c2d25f59aa2c9e41d4cb3652ab8d6223ffde68c539c77fa443e35cd0580938ffe9a6430f42d7ac4dbdb99de900ee1e9aacc1f1f967639c4ede0b49de2d04abbf4f06439095d5688c210e746f4e243de45e3f4fcbaa9de4c4151ef4ab7d0e6d2dc13dc4fbfe2fefb7dde0740c60391f4582379ac00bad02ee35715270e30e0b02a7b60465ee98865e44698e32f77b72583e37b1d134ca3b61cfbda09facc441f1f442fee90089ce9b032dce0f38a3b47dabf2fe6993b0c9569d0ef3d6deca94d8a8f18c3549806400461f206a0bc1e6a361e8a78eedc97fcd56cd5781f03d6c15be3e6056b2b18ce69c894e2dae990fc67269db490d2fbd25a1e25e1507854ea864d6467e378039e069df6a916ff66611b9766d9ac5c36a1368f250f684916e58637caac9d174314a902984553ec47e87e867de1130f97510aba6d2524c35b237188b7c9ab62d7400c6a88edf5a502618670d030065c778b50fae6b30a498315ff264597503df77edf919adf2e95ebcecf145cc98f7a7e4d502e7c8da3101c5482415a554c0b6597d33d1ea161871ae813645296c71fd4fbd1f4fdd575030b8109496989c242078a446da84aef0bcc0e3c2538c2ae41f74c2c3713f81e91519ee235ad219873ef13d4e48a1b94775edc7ffd63ec6186f21f823c123053002f7351de8644d3a2446e43406701c440ba1e7b85f257dfcc40778e5e64175768bffb6302a29881c4c724facc2787dcd058af29cdce695daadddd235240c573989101f1c5d74bfe1970e97fd99ed3ce4767c0d4a2e10e5021da245cac22d09d3f826620435b741a7c5d8d779b2eef10690251748122f300d519edb8cdc15727375bd8309129a764607b7f97a03cf1b93e1a2fd7fd5117dec0724b0e2f2eb88b082a48fcfa12f2e29a1040b98514a9a595405e4a849135fe93a27e0e028c4e16f18eea85db8e0337b03e6ae8ad959e0248f0fccb62380b608c75e120f00e704d0a039d282b0448cc040fe1b43216c5f47c81dfbe65234f7bb6d5bcc2c43d77a8f205076816198046c46ad8bc679e3bdb36ce2c2cdf4df4453220e2fce341f83fb533686485290324645b3fcf17962d432efa4434badfadd15dec2cdc894680dd49297832d651218cdd6874ccde6a694f062e8864620f3d9ddc2910ecd245bd5baeb2e87f5f3f61644a31066a97528d9d12a5957726f66095b3d06c322b4da4bc2a678415586ab92e369dc9a9414ae7348eb1b3d28a2af9f51956990fc8accc559697b323bd253c553e058e00114c384a7f10bc3cf934f989da232721e8f5563572eba478224b281756c8f0990a01290725e68451120905be6814102590449e0023d1103e0744483e6244cfe84a2a4a24b3143b7356a42e4a5e1210101fed069a516efa98f3734729b23404442cd9af75ec127d19a1fc70f7ea15232c3cc3dd960be4f9a223069d8f6b1d9ad99694e8fdbc5f911ca89401485d42d75cd5ff4e4d249e4e4679490a21f02bfd4b34ef3bd1dd22a84acd6249ea38dc3f438725fa4e12c7138a04f17c9028ded29f886b7a25bfe8c4ec14191fe2f2af7c00fa33e45805a304197e315e0621272afc7b0615f631fc86808bff153a8b92328ae7d7444391e7b28975ff932c03fd3f5e260aba7da520df1a8d8906c52b25bc2d1eeb9500afba35a2bb8b821abf027cdaec929753b8179c529073b1b84798bc9ca9fc8cb8040876280791f32c0e361cc6417d356de37cea00f2ea84e3cb53a5cc8fbf4641e89402bfcce892af9f780f7840bd82e71a9300385255c39e9dc50089ca21651ae0809ea195e90af20f6bf7a82a43f37716f9c9797ad48774d3cc376082f8eddc18c75e447c807dd586ac8383794cf310f85513251ce4910962aafd64704d1019876391ce2d63640010c894792260328610b1100f1066f8970a35735fdb4b38f37600e476e6e17c8ac523f205c51c01fe45d823781675ad38d55e7edc9eecd24a5b229609750ef16ae67d50e05c140a4059841c74a0d3299dc29c56d22bcb0f0d950dd619af2b6fc976af2e30cbdc102494948a494dbe01f204ea73e5fa784442e5503fd638c6625a481594ecc12a42f2112455277660f7493ab0af9a65d633624f80e2bd96c6111f021ffbcc05ed33153976ab88fd6759882816b287265559c9fc9ee0d53901786d6909ea905a13b74e5e9e433389d1cf6e37d88021bc3640a25b984f133bb624eda7c3efd7fcdbd0d8079182a84a718e1a0850778b89dced7eaff80bd73b4ec6dd545aa56d40ca3e40a0814456a0171eb01b62a5706c4010870a1986287f399a5fd96366a5fedb6a75658dc14f8e3598a5050c41fae10333d97db3d83fe11aff671b5e14dcae3fdeb0b04e05de83e754138666a698bba716ea13da3d29ada19e94de623fa15085087599a917f467fa9b36ec78062ea9584400ff2135a1333c5b181c470d40e993b46210522a035e059402f51e5c306a0690ac557c5c097574b7186d6b884de17e152b922f312e5c55c4ac76fcf0fdadb6cc9523b724fae1d791e2ab8a7498844d31010042aeb259090000f7abe69faffbef695fd85ffe57e6dfd9a81ac01e9f3e8c1f7c2a28f9d8303440328d120743285afa821649239874a1cec2f000deddc6b71becf7fde123dd36291195f373601be07ef666fe45cecdf757b0f85cc8d0df359bef42b7f0f7a1c0a9198b2fd8b3ef01f2ff5278d2c3bb0c81a1602c223ebf0c59fa979b874ef30335fe27350d05bf63f2b9a4d4bf12ea862ff3336734f97f9ba36f25e89a7767beea6113e97dd12bb753b560b3ab80820e2c6ab49162d20e5bbed72fcd1a919c2562fb8950fe871e4363f488a3e46eb7f533e71a639ade40e070d844263939e4f0848d7f804ed151c4211a4a1c44a38853ecda27707e0ac11ce8343a820cae825e30c10d72ffe88e4ea7d1a0ec3062237dfd8c4b579e298a051635814d8163bcddbbdabfd117bd999285f757257764126bbeaa52538d089b3465525d81e03889c4cb87021bed6804a7bce9ab7598c6495af89f64611fb02f59dcbbd873617a4b49f7aec17fe2a470ff552df09c658aeec5b9e23b3a60aa549a46196f02441fa705279b4bf66bb2f4d641b1924d3be627b23c748386a3d869152737ca570cf76db3f5771e503b0d406fa57f794121003ab1c36d8cc1cb621bf866e3c6aef2dcb890bb8fcde4a6f3c4da17a2e30ed48d9420bd0e622539ddda82529e1f776536f2419a764392310aaff2fcc241600ef62612537652fc69d5b0d859664488d4d6df02bf32ed49ef397b410b2c1da722520532cd1006250d57ab890db0416c57706cfd88f3254880c8bfeaa37a4192238ee25dee7734c7140b300176a0a06fbbd7b37d315f0a4373e9b495a256d742df3f560bab5926465235f183332c9ad687525216b54410a513e22c76bd1ca6c9fdd39a70126324a89ebaddbb6de31851f492125b6165085a848ed7993094cbd212e7c4a3cf7dd33a5e92fa223b1f2376360923b43954d7a3b204b14732aabff09e467f8b5b42b086216db8025a4aa8c88747e4915d79bc7f8d32bcc69fc625e54b7aca89ab9f881ee1fc542bb6463e6e669439f2c5ee761aec972f102ab0503ddfb0462baa8419052d3e467ddc6657731badb93d8888fbcfab99e6f2566c4a76a53431fd9fb5a77446f3121ebedb0e003729f5680e05a651b269326b310332c496228e27b50237a36e0376aaa0b75bed0cd0733c25cfe472c033586276a901224aeca3bc082f091d3641485e9d430cf5e81f68b18e9d3e7013387a9926a54c2b84997dfa612e591760e1e615f5e0805347da66165a47c9d987b49967ef04939c99ee9bffb20020306bad6096c9b179a8ae7cc1c69894cf4bb16a6c56ac75e71721d11e2f41510c7ac1955d7f588d8e8b013635c0606991bb2b77afbf0ce245d284ef8c526738125cbc5e3b490394d63ef73258b498a1303003511ff20d2332b31a8fbf238e0164df40507f190def9cae633246a13f6ee5790bff9894f11a71764edc77202e00d006db993fbae0948642c532acb83c43328c8f511a8a88cdb076780dc8300ea36c28228661d5e51992c53864a64694b6216bb638c1c550d8df18259262904aa35659b2a702905c6ab23ebe03315bfe04344392d07a3def91b1ab51bf090ba8eb4785ff55244340401e3574cec583227071e055a8915fb38b1ca4a7686ec850c4671460de5e01a015ddcfa8ef9e26da2953eed598480e78a3ec9efdb099e9d066e73d0c3de5632a177fc84d9a4716950202eb2d2c58c57174624e05010a80f357144a7f088d3b750699d75d4f7d1d1d49885390fa566da2e285eea4939100e77edd50fc203603246d820769c5020ea1e8ba94bcd0989f374b704f1318667ea6657754b69f21863bd4492b538c86ecc7fa343fa207ba99028f05bcdbc49d5f3c823a01480ad54383cf9a16c04d5329437494fda03302b8b4f470d021c98309d8cc1fa8d3660100304be25886c4941f6820adc00d502a7b8b3bf8bdf2f0603b380b86a50aa5d2a53d0d425dc21ad434cb55365b24cbcb33d674ac7c3a8f70baf46e17b2a52f362ee21c268f9fbc1b4902b9844dd40252f194820a2e165093ee5b0b50c5530250387140efcf6f5940298f5248c1c1813ea3e42d251936a3d392c416352ca0a09c88ba4ad9e60ab8261a0185c8e275a50d5886c714a4b9de8d97d086a2d2f00029b28aad685715113decfb5331137b6fa125b9508d20e94f102f2d0ab322ace5d388972038f152dbd78f3b08cb4e15a9881f8d946999a6fde8138b59c2afe9403f9e20992f46c7a9e97fe808871a4d4b8fb9f9e2c846cf3f31689d2d70ab898d38e40d1c90494cf0cc68d83970aab8de9a6d02176214628bf361e0cfb4abbcb5580f85a4ba99bd8c218eaa70c1957b0f61058ad88bacd0ece5c62c95244443562ab438c929f5e1cf0b243c1c102f83a477b5815113fdb98a43a509a2a8acbc9548550733ffb8c13765ded4805d7cc7a125b397133b3a423f094955352c1f56d455c6659466f7e0f8d5a0395d7c29bbccc635eb6c3060a6fb26b501dbd4171803787c3f75d94fc4d1a8b5a33378edee6a1fe6bc459cdc62fd65ad2cd04ebb90e88007549a8a13a21ff2073128463096e6e614ba452bddfa394a5110a4d109ccbe04b61914868469c769e298289291f9a8caf1c7448c04bd27da70eecc1f5c5ddbf24bb5ffa8225e02bb7f1b237a2c96ae0694ba2830ad7cd7f7c3ef96e8d0a4c05ae0276b068032d11ebc9b4004f4de911f918887da54325ed2fd9308b6033398e1a29ee7c1609cfc4f057a422b4c14cdbbaaba312c1bd382711f45b0e6d0f26b8b0e6b1bded7148cc159e9709d9199252fcfbe64d507b5ffcd6991d34ec689c16b5df9295da443c61b6a398b35c7773a98c99d2f38d7df330ff63a177caea4fe9089cf6b79c6ac3ad0cfe0fe2c07aa5d4396b774c28f540e48c128089eee982dec514938187fcce2ca699d154d037161fc73c8b0682f018183fcae071990c7b401e867b25e910e833cb2e980f6d408d787995a97b76255b3f54001598dfa244765fd0dc954dda31eab1caadffb539622b3410d0b2076e415a77a32c6a5d7f7ef8e1be3eccaf3be13eb12e87bed2f56d95f8c271635fc83711f8826f0d585dca7e4b20b9d6a7609da41d4a270a95c28afa22d342625ec5965cc28589c85502e311b1e9fcfb58a51ae898981ced82501e00fc2c64708f03f24a539c54f1fb172438cfcf17d7c1fd28a90554adfbdcb7291ae62f4bc206b393d0ede23e28842759c24ad60b638721321c83d6580d5132ae8a77e7c9bfc8f68638dfdbfeb4d45fa017cb1ec9bae341fecfc995beabafaea5a3caceb65acabf3a59c96e6592dc0662d3eb2443da658803fbd9df02d2d80eff49c5575d00154fea82962cb17c3cdcc46c5fa8fcc806d1800778b5a793f6559511afe43c411420f05ca604500de2564d268a3f8059966ad2d70051774a0f50619a8ab081c1d65b28cd6307a0944d42a403c1e1425e52e2bc528f1f944d4e8910f5d7d313d38ad29388df227c06ca9656bbfa8aceb75452f57e20f4e68310b72139c7b57d7d32b85a857e26df6103e23bc1e601b73251fc68615b183d56c7918a7b5b8d88aca19cfba5e0c78e5fd53bb5aac171e4280e94f71a6397207831ac881943c57c68f871a7e3867d37559da4190a60a14b0a37a56fd50ac555b188a6093fc330b24825e7bec9be005f1c83c3057de29b675deb8981d4cbcda4402968f5087c18db8d848d3a1a0e9739fb66d4fc2a85b435994fdd8e1a11b2477a484948f3c2a3acd98184e23c9764833173ecfae8554c9c3844dd381f6fa201aa6bf31587504188be2b4f003981df20f0c96c8c07fd2b42c2f07f9ab4707671c4d880346f5700a14594f1a91b907a8e83e4d480595ff5d9d888a4ef3abfac96335582efff6c0c4f045431ab5f9a8c73f5618be91deb75f0595fcf6613142842a7bd73370105947a413df93efb0e28a5809432acba0ed872c27d0347f44c259a80fc2aa8066cca1fad7ea58baa824af49b0cc233eaf434d25bed02445fce6df1b3a03a695f3d41c070a6e77de98f63a10f154c4b9d1062f035681b30fb0aea60ec061326c84f09af0d58da6e7e6a251a31932dd4e1c994f45211528790cc3f54911dad60407e394f4ed2c78bf2dcbee98ce505e18ab020ba32f3841ed4c2506f731bb9f5dbe033c6ad8947e741b033cd616e1c0b89a66179d5017e1085d2f8e67ea88351fc9e6809fb038449f23405cc31d8f936bce9771b12d29357038caf541b355d61912509941e3436a67cf835cb7c70e8562c96ac54f3d28ccbc55bb1d0ed5b8da6cc37b1a45aeb6efb3fabe3f066b71e3ef03909d847d8e275ea1d18d74d398bf75d7b4a0783f2b56e82cf9c97fcd26e1842b82617152b288cef51232317d23beac17345208c283831468516e1f564847f497e0034c59818545a285b8156ab4fec2c7e5558848f14175acd725e566fa8e48ba931e92a6577fcf978f8b31e1a6032af8aeee3392f4f40f0a58f6cec702cbe54dfa55b77427226eecd41afacb184c82e32f84759d02ca46063eed729d4434b89130feead8944cf699e2ae3d9cc23befe27776befafe9ec4050672b2c41464e55ae903839a9cd511651ef4dcd3a16153280071c69e0dbbb236631c48b3520f0392dc9664c3141d16f0c6a13f88ade2baf264221c4ead2e05981eb9f27c8fd813a2dd7f51dad8232204d5cd8f48aec739c9623c1e325fe6acc8b52c0aa4d7481f71a16d51c52ccab6586c5bbdae539d7942940cf11826a23040e5a1f680c4f099e7c108ed92cb454071adb31be9e092811dd1c7da14ac644d98225e4b7497f2e3f8d5bd7887fd8dc7b0e4b82547bd6384000162c8f13c0b9b4c3c8e449f3c9a81ac0bdae0511ae89f6c911196d2d22d89f3daf060759517067f495adce5c2016529c8a1c257153a11d40172ca61519e56c280db00a74271366e2daa284c4edceccdab0f382a3d22b5dd85c1d742f0d1dfbe98fb124d81a7c24055af40ef2cb9cda9b217a2a08b6584a5b2b39c2e1b76e1c6ccd6813431e44b082df25efca0399f144230570902ac9098650755c2474b8abab818eb292518fa1bfd6701f1eda42ecbb7d741f9b8ae17706389c9c8c7f252da64a7266d2013bca316398d9db6c5312337836bdb604d89f6f8ee38c7f3ffc1e29f51a1fb21ab77e6861016a1bf1c4f0b5cdf22cfcf795708b82704bac6a75887112e591c72ff537e347ff16b48043b0e38535d63d061179abf9d0f61f7329dd2f540331aefd506a7d85720250280adf085c8d6934617ad5184b5b6b3a0b59b1daf4d8413b8b6e09057e3a0e225e89a63c37b23e6024eb338cb03c3a3e00d8354baa811e3027026d04644b3f9088ac26fe9b0602a3442101b1e87efb57544093092fc3182dafa6a3f70c84ae130a2484745362dfaaa231f1f74360c69dd14dec35666442c1f1479b1ad746ea3b9fc96afc88b4729bde88a7081cbac0ef23a3c0abc18b4c2858e8803b8978543de351c05e09d407194d8879127234b08c45bc6f0f448ea0dd0d1cf84c529628f8390ef3761d133b90d1e41473f098b53c43f0ea1fb5994a8d5a1768a2d4a62fe65c22253b8ef8dd0305e2fd84c12417328390583c3a097b67213769c305f24e2fccd1fd5739005700faa3a9fa25d4c5d202fc2bb06e4183183088b14637439772d58d5e65d63602996fe40f793f545a367eef19151d2943cf66425fe1116babc7d46acc9ec958f6d0a13957ee39720eefe8ad8d916cd07b25c1d16b1ab6ca91f762eb6c5ca8d16470aa88440d5cfeb07e12f209352cff84a104fede055f6fede7d9d979188396bab259d2a5c39c19b8ad0014a2c64ec92595e9ccc0c8548793436c72d11e737093b7062aaa0733aa33955b26ee936b475582a6aa74a350a31726f0d2aee2d82bc74adf08b1d1bdd7a83e8ea5872b708010cd6e15d26cf5b20ef6f12ac43a6f48fc320ac76f419ad815667e9c364c089541a750ae617f8735e5c66ed2d97d3a0b2b461a248312cd7596afb261eef2a3f960586c988ca56df8e50686211d16e3209c8d904bdb3a202d5c9b959b7e69ef5aca5d8f66feeb792676d3f01ffad7eb91da17200cf859bb234f47468823c8b64d899e9de50dec61befd9e2668030208035dce1901fc99ea1c2d7c75405cda030200462c7b348e24e85a28a688bfb0b9c0b96dedc3518defe4e10c2ea5889bce0186a819551f872a07f6b43f2a803671a3deee1903d986d32d8640879ba21e416b8bec6162607140a04177b256312daa1600a2eb5ce93918bb9c125bf2ecb9a021031ee33c08e22d27f3635d7e002e26a70edcbc781aa940f8d97e969e258a76f882223f2c26b3fdf13b7332b5c09a459a65808914ffa73ca32414ef07ef8b7e4369be8e57ca233932e91b577737740d836fd8964d1b9e024e7c831bacdece3850309d35e9d6c2a04c06c900959c123203eebacb4f5f2fa0c6940af3ec58716fea9433ea41d81a4c569a20dfd3bf094bcd327a4ebd82b68f8cb325cb07fd432232be8f8f4ac746b80fa8038059ea6709313caf9fd21d3ee569edf07bfa3cdcbe78f5f0c8184257c2cc6dfb51b6fc6a766e612d94484292303b90233fe43bbd6e8c49261de4d45caba580997fbce9d5fdf52a78673243b835c783b0031c78e406dc7ce1d329219b27696500f2fd59611c2eb2d1ea2a7d5053605043c52eb75e7c975b218e3c734b612c4142a631fffb222ac10e70b3771cccf0e36e8fa74d853c8107bd8d1915648b2be683fcf5372d215c891a0d463acc776dc994ee1f43bea1cf760a4af23483b7c44ee9d3b0d66a3f60fe7854f43ea291c9509ad901ca029c062168cafa404c5d44146c41e18c8d584900fdf33df76228fce8ec52907f77568922fc901a9520bc653d5bd4938e5bae1345325da493283053f4fc1944dc84ab9dc9871df0441c11e94a9f2e2819ecbd840c74af1642437b17981c0a9549189d324b24c9644c758901747095e0efe05e88ed7c86601b20e3686ec320299f8ec3dea99cfe70459c785ce506b4dd8bb3b585984c0ca0df4a6f675f7a74af714a83d515780f8c04e7cd64a833446ce8b5001857cd947f020c087dcfa5acc68c0c7c1e0e013d0a448d7a76eed847dd352eb0394a2b78e69123533a8ab588077468f0fea3be9878394de5e706e7ce7daf8ac43fdf8a2b4412d71748ab8c429be670a8600839b782bba5c754dccd0944e685f4ab38e9d9426f21704018ec3ccf41485e8eb57beb334b03495a0a12f9b24e11883752a296b61f59b62365223c34704e9e90aab272f442289243c5c3796cee73a78c76a62eea043e21d66671e64d7c14ff3d7430777cd5f29c961bb83f3dba395e19d2be02f4187f8882f82c46307ceb78814432e0e8400e9579a8278083f8284fe3f6049ac013658b08324b3cc661b6d2c368600e0a3a381cc54500d419a0d1c3cfae4b845cc67e87ca36e1432d77fcdd40c16ee0912adf599e53d044a3c9c7a19046509f69d97dd2d6643e1c5a5c75070340bcc303c1e6b6c3aec3a0df89e7ca320a0ebbb623f469cb4e03320d7d08bdd1dfca1ef01fec74f71125d9cf41beea28d7963a9df6faf6637e7e12a5fb317bd79ed3d985c29380879cc8cd12e966f8b29c62e1c86dabd9d613eab886a70c16140bb7e148bd9b3882b067e66cfd8dec18a2aaa80644246b615a0ee180c0602fa8c5264b4a26aa63b068c62191c8e248044a18547b40b29707a992132da486292c95ca8c28251a507e7aee2d0b8cc0b6516861d8310c9edcf9710590810562b0833a1605baf07b767e9224b9c74b5e90ae465f0291d9f181763a7027648108ae773db085dcc9c1b40054df69f803770f958b1ea68089dada617e06c4c399f800947353c4a1e3aaf036af75f5164bc6653540046007d43b669ba00935b7574b71c789ee4b25c3f1599e3bee192f774e7bd770cb1143f76acc32e8bb10918377974429cd71eaf0cdf7c0fa3eb6f9a8ef3893f94041c74f9242ad6f4678faa0ce0e8b9cf47b80f79e0e3fea328d89880475d71e58fb51326e7a3c55c85675e2a6318d7721951416bed6ac9415f0cfc050edbc5b813571058c96a3af08b8e86152e07f5536c597e30ebbac3d22721200eddf3c5d65f51ef5de2ee7b83c42d25d916d9c5976e24ed6ec9dc5a63b3eb9a6cc8163ff11c4e4d8f482cd768c79e131c03c6b909d8ffffbb096db2770a6915a012b4134286006730044ca4bee5083870c1952fac90a0ca79435ae2c910a01a68f081af1965ecd0e1b2d162f1b00392a22d280043052e288103c589200a98410b52d085073270850476843120e0dc4ae80c8433b4881106185c5c208800f4b0640725488a8808b9d0020a276cb18120aea0400080105958015f176acc40064c724f5b8e2084202ce10185135e2de862041ff0400630808512491821f6a0430e488a88528082119c8003508660f0821474c185073a2005048e20420067686941181ee8c0161568f2f424d190978c1632c6980109b8d88006b2986289023c29f6b084871c888600f1a979c9a400052468c0029668f2640f3610090d01e2648c9102149460041bd04016584cc144124d946c20a201888f0f1e393524bc40df42c6183228010946c0c5063480c5144c2c9144019ad440545a52c38e308d22182962a3460c567882c4114a72c089a20047ec9cac70d9923ac2670455113e22b4aa00a9f8a6f824e0e97c39291c2f84b949cd90a227b8f0ade0cdb05448a5e0e10051f04e7029810412522ed0464c0d978c0a3129c0acf0f27261b556ac6fb552a9529eaf9abcc0608227960bcb05acd1e261b9a458884912a208d68aad5e5a3d608c0d0f8c5989a02e25937a815704a080a7558d8fe502dac8b0d04381a797243124685343888fc78c0a07d05bfd80e0890623425e02f8c017d0488d109e6e30840c5684f0543e2f178208f1812aa46e4c8f46a69a218ca7d5ca05a32626d8002222454488013ca5e09243cc8d981b3e089960b6f44b109e7eb60b111a132e964b4a46c5041a8184102db0c68a05cfc68e103888c24506163469453951e383510579b17e80459e87294394a08abd08bdc4b05a02cac0fa318416501019820a254e70e13491198d1040044f30a76d010290d1b1e1255921058495028f102fac1fab97568d940f222229165e8482f0f4c22292416a0dcd0c117a71198a41227fb4784201b4e17959f99420b37a811902eb418aa797d6500bb5239e542997ca5bddb07a510159c9200382395d74b486562fac146484b8e4008b5c50b8b1420c2a9c20436b8622473108f1d20a593b323b3e888c5831204b05954f8ac72506d44183b05c582fe00e5511b02788a79a49d1a84ec071698da91f4380e068bd9ce0e24388a022181ead189803ccb1a2e249861aa010d50caa165c72802bd8c849b130c453b8c34bd95237604a0500580f4d7400df2b2860056aca15544080004100e1a4996c3fd27a58b283922438201919b161c8a7672716030c2f5070045c6c31830738a84106034c851458514ee08504a4b861480d3e2c981003b312c1161ab0428a0420f00028a2340087988e1de102149084b4250d346a3b9851860744fc6242096b930179614959e2890f17175c99428acf0f1e146ec0bcb4582bcc6400034b9e4452640391d0101f1e393524bc6ac8804f2461c40048c0d283035750808a207248e28307c70df00b2b3f4822a381c785b5c2a2080d54200a04347104001cf0c4134a20299201d111e3152145842f0ad442cbf8b28038f868a0c2e2bde06bc1c702973062c0f8bef852f06df16191bae2630297bc1c9e0b2d16542bc8a4f0a1f099e0dd7895a022e17b792eb0c607a37af95c5aad8fc5527da097f2bc95bbbff0b8bb0a2e189c2f6b755f9bcd1d4fe2d1de9305772f05037adefa050c776de94b142f4fb85e9eb8d3e8eddac7e9680da5e187c668f859b9eba5e67ad9c1f5c2c3f55243a3364c6b688cd227adbe966a4bb7a54076b9e68a1b69b574ef6a4a6118f2408eab1e38eeaa2430950bee728180eb1cdefc7b577347de7a0677b9541723d76efd24a51b5faa1f93b426f6633eb176095d7038797f57aa5dad28f2beab3582561614b76ff74b6ac3f4475b6a9ab16de7ba35fddbadd9dd5b7077b9e132f367deafcb6a638d00dcee3ed3aa71f798bbeb78c1d3c18f1c2aa3399fa30c3f7ef4fcf0f183e7478f1f3c7eecfcd0f991f303e7c78f9e9e1e1f3d3c3d3d7a78f4ecf4e8f4e4f4e0f4fcf0d1e3c3870f1e1f3d7cf0f0b1e343c7478e0f1c1f3f787a787cf0f0f0f4e0e1c1b3c3a3c393c383c3f3a3474f0f1f3d787af4e8c1a3c74e0f9d1e393d707afce0d1c3c3070f1e1e3d78f0e0b1c34387470e0f1c1e3f767a767cecf0ecf4d8e1b1b3b3a3b393b383b3f343a747c7870e8f4e0f1d1e3a3b3a3a3a393a383a3f727a727ce4f0e4f4c8e191b393a39393938393f303a707c7070e0f4e0f1c1e383b383a3839383838351c271cebee33ee62d9b8ce61eebc50c6702fabddb99ab452dcb63a9cb6d702b97be8ae1503dc77cf44f5b6f433b67d8d569afa56a5cea1ce4f96f6ac09b96bb582fb5f4cf5896dd367933eee4b338747ba4f8cdb56477779dfa766cd51dd5f5ae9b5b7af51bc29f9dbccfdde5509002f362fb118101488179b1700506a43014029a5d4dd63707718dc3d87bb832fa8ae501f5516d487a662c2a52a62ab78701a8dfabcd8d068d467dbbea4f48556b371379b29ddb62f6334eaa3ab7edcb86b8e061a1a6850d9b85431ee54db7dd628d5f671dbdd61342e300759a88d0e1d74574a3ed3686f35a178f071dfe7918c731ea2c339632077ffdc052ae1ee61b873b6cf7804777081482e30e635f4e787e6c4e85f5c44a317db6d2271edee3bb82bc501d739acd972675cabee26b813b9832f30550209ee2e77b7e1ee35dcc5dd6380bb2f71d76700d79746caa98f26ef896bd486e93e31c55bdcb63435a5e187ba7b8cbbc3b8fb8bce214ed823ccb9d961cbc1c1e991536b351d1dd2478e8de706db50ffae790c77796b7bfbbc03df721dc31db58cf7f8b81d3abfc6610709eefa70ee658d8282bbbe98bbdf70d777c369c8afaf55e9bc2c3a8724ceed18bfc9e5798101773fc15d5e1477bf5fd21ffa354a9627c6695a43b58d51a727afa5df84739f45b3eab4bbebb87b3cb87b0aeef2b6bbeb1c8e77a75b7f96b589d2721e42a3b7d28b29ad2666d6acbba78fcf2e4ffd47344a7e59d6d1dda9fb5773771ceef25c3a873b6763b9bbe72bcf38e7a7b752f26f3ef7e77d3ff7a599b374db9a37bd5dbb77cd656ca3350af720e0ee2ab8cb69ee2ee32e57c2dd4d7097fb407e69ab8deadfbb1aa5364c2fb6bbd25dc6b9cfe91c7e31eebe82bb1c87bbbfdce53cdc5d874b5bfe264c77db8ed5aca3cee1f84da2aee61d63d63b465c9675c72ed75cb3a5bbab52a0bb7f2408f104e3504f4f334f91d0240d18989f03e07015e115013671e2d1225a291f3ffc10808fa668003c0ad21facf8a189130aa44a72a12aeab01f8aa0402ad80f0158d11798130a94a22eb000b4604e2890475594050bc00aa682a5a8d300acbc0bf383809647e3c40a2635cc524ec0264e4098154c6af898785e0e303f4efc28c005d21a340e042343f3d1181a8fcac8a43e994f6646884599583009ec3eba82496097a235cc3c189aefa32f30d48907f4d22446e6060772691203c40bf55c60405891f4423d0a040c958909e285a6881a80521486a689130f28458ba8010804a90c4d4a7940df07b6649ad4a02d9a263034c6833d0111801485813da59a38a14032f493d94186068800a4680dd8538a7e3724a58c5e689c78402fd485c68907e44261689a145103d00b75e201c150988f46867a343337805486ee90a231b445d3c42587208060a8478178a10102867aa91a156db9d0146105931a662bea01fd447131f12db3158c4902bb8f4a6007ba8c3eeac9ccdc60045297314ad1d4b7b200cc0f14d7476154b0541029af088c83ad264308f1d10f74f7498160cabb008c7b1e93b7c4a22d26a9990b0882a00b65b540960b9862b15c582e2d18530a04413095fac1f3a80bf5886872d34755b09da22aba822dfd9052fdd052c1364d31a954302616fd5654f5518f8689f5c30ca4dfa7ca02c6534b140646e544a9990aa4aba68fae401a2638664c2bfa0263f2c3cc857e344c70cc56b09d1af266add4cc05b604520fc6e487190bc6945a02614c7ce6d4658ad8987d1f08c018087a204c4a86c6a456ab9915a5595232ab99a9621f4f6cf6e998adb60ab6b4a22d153c6999a5e86a66453d5a0316cbe0cc2ac69a69d16f15f3e80360fc1381e7ee35dd82987d9e02541fcf962733ffe208516633ccfc0b23a031db82c6163066302996475330a918157ea268cd107d94d5527d9e8c0d41cc58d465843ecaa22a1a9721aa997d54c5a29951d15891ef0818cfe1c57c2bea4259608bc2b0965474453dd053aa948aba8a36dd8298ad666656b1996a2947ca0b18cfb1a452d1964a45795855bc453f18168da702411655b158b09916f55a30cfa32e9e475d582cea59f97854d43d30664ea15a312eac15930b25728181aa97992753244559acd58aaa562baaca020250a4282b8a31662cda022345554610c14c455725dc28a104125c2b982023e441cd58e04ca582c1c0d034b16249455f2e26992229baa236648854307045552b98d0376351976fe64213832307cecf5012ad746af2840152bc2c604e8041e1938981408c4b8b7e2dbab4a25f11ef0b2380d192216ab9be98d7a251c9144911ad60e007a63cff54353c281b4fc0781f08033f2118ff56de6aa55a5198956aa54aa5220093a23029f7d920119c5c9979959943997d54458508ab551338642605a3817119a729e2cdbc20664efdc891156c29889914ac480a4cad28cd076e30055bf22828736429889994cd8c07b30153a994cdacd542e279f4a3aa09b45c38e8a78281a9a80a86aa543f4cb8be99960a46e5f281339715cc05d6e4a932a8a2b0a5184e18511ef51c9ecdc7935ad1d58a45419921abef9b61d1158bc666332cd88a49457f06e00269cb8769f5425d56d405e6023bb2a22e2cf08b28b3144d3d897a9af94c457f80840f9e6cc9624b94d9f66629eab27261d1150b06ce409818bac199c752b9a8284b15d36aa5605232475474059bf1668ac466ab214b29baa24b3345623315934757744635a3821589b15654b5fa59c2b5a23e43f4cd56b4e9b694a21e9d69d198079b69c18ac462309b8f67459760331e0c66a5f29915f556aa9567e3376e63e6f98d67f3c5bc55050c8c8d8d162c5666206d2da5542f2e34e502cad0189a92194265a8476363d604ce58748333f005b6e4d1189aa25992f1b4cc9ac019483dea4b2d3ac3c49bbdc0664090824ddecc85b6609e0b55510f64c2e54e552dea026bc1fc26b5145b026752d4b3f9620eb3f962319e2a0c213c99398ccdc7e3335eec8b398dcdc099bb0f134fcbcc832d7d7486459d7eb019162cb55a31ad28ab45615c601e136b49073103bf9922b199473d580cb6a48231f198bc0f66332445809894d06acbc808c1f80b9d9189f12331a09782155951b7f9c2804905218b598bc280d485b6102ed80c9d49c18ac462e817fb62423331b4096c0267eef48589770406d6441d968209cdb0a8477d86052b128b11ad60365fccf38f044c290ac6c82cada8cb80344d2acfc30226cb2c45c12b369654948945895c33fd511818caa2590ac08c459754747b600ac6046382a133a75982a12e4304d214fdaacc521444e38b211860a6a22a2636667ac847d3e435792d22302ed4973c242bca4a51907ab026af95a22c181348b7375bc140150c1462e2cd9878b30fd67af1400980942505a42d345a2e1c34e6b0a519a29a99cf6c66a9235ca91485c9204d5190e6278a15055b542543b4a22c50954aa552291006f20c013d19a29a9907635ad116fd689640daa22b19a21513afe96bf2662acaa482b57200e339987860eabbe13155933703b780b1e1371eb5e13e4e3d1360fcf3a6a0220b175a606185191552c081c20926dc28818497cb460d9918981797d6173e78b2811082108420b0a86a096fe5ada847554f5230400e7e3552d4035de0a76ab15a2d560c250286bec8d0189a2413034b8281bd106911b5601e90b3605354535ea8f88dcac9142a53a6b88831f3281517171a4fd5b03e150ca2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a6cc5a4881543c164859340e04040dd7920a8a0ae652c56fbe166080b0e09bf295000a1715f80114205a54aa2f0715ae58cc0687874a05e53eacd9b78217b3852b453dcf7503d7522c16530901c71950ce4062e65f6849e30b2d65681963e6d198235ac0d062458b1737b0a82045e50296475920751fb8bc190e5c00e5c16e40bda0f1e4050d951752a4489122e57bcdbe540aa6ba81e549140da4b430036137a03ee05a3241ca0aeac992095266dfd28d0fcc3e1b52fc064a0528550fa07eb856ad8fc7fb814acb929d79206dc196cc99075b62d1f8e725e17cb16f46e89bb16892cb10e1f1c580d4c0f502ca28f1d80f62be305c2f7409e6090fd7c7e3fa623318d8922b01b32ab3179a251e51ae2955587ec5794aa8a15aa95454055b4a097d9ee7f862de2cea8a0b4a9494d7b241e38a422365e69a92aaf95c09f0121830fbe84745852720cc05254597609ecc7200a181cbb301c502974797609ecc3e28972198007502901ac47832deca6379aaa854cde70416c5e50c973783f93ed5075bc211038b2202330150e5cd54556026f0f1bcc096543c507d55be2a5f95afca5765f6d1568c14284010474ae5040635035d70a1062e0f08085c4b30535c54dc27a5025d98a9b04c8101612664f19b8f0209238a77c213170f688f06081964789ee7b947c6f77dac8f0c2d5a52a9558a0c5005822a32522b2064b83c4ae4034206abf579df9016133242eeb1580cbdc1819a10e1a99aa5184a440daa7a92aa89a1435a32371831ab415546784d0c85597d9e832995ca89dfd4a03232d465808ce1aa019ba9416da8f01385cb8556a9542a4555506cd0d88889a931631159b5545358ab198b3a711f191a43dd975c68c13c06e664a5a2aa29a997147589b9a23282456158f44586c5125ad11818136f8b0a8afbb066535c683e150d9499eb050b4b014010f0e3c9941a30186a0354d12cb9662a570c8bb65a322e160bf62309168b92f00a92aae1d1158dcc0f03cc3c1ad372c5bcd0146dfd70f20389179a98161697cb8f275fec47125b159ba5a64cf991c40f273f9258bd40cb04a4b8a4605c5c680c58837e35e84b0d1919984a86880c6d5118204f802040e6bb8163f5a272690179f2b56866e062c18040f910007e54f57d2a20568a00e339965c56106961060303fbf1c4f35841803668544f3eefe54b3921e23960804071a79e0b7ef3d1964aca15ae2517b0b8e03e58b2ac66402ce0f2684bcb18365a748a1507b896b04479c1e2bd80e1535801e3ca8b942a383ef0a2c50b16b1282a900aad664c5e288be6050d9ad56c1595c68b16201648799f0bb32f6ab69ab2840defc5c91753d9983253a9e80b9417245ea0785e4b85256fcaece5e5e5e5c5c90b124f523e64a8ca658afbbc50952a86aaa8aac9c7c3522101f442534e655e625e54f485c60566b55ad980c2c05315ae9694094cc982ca165022a8e2851530ae8881658c2c3c785a0010282b20137081f45b6d0125021811577901c68c47b18c9185071f8f0b8c081a5133abcfa3711ab0d572ea7935509acc6866fe050db0ccbc1f4e603f3c8f7e535c4bacd5c762a562626258302a2729964a05f591a10232856b69855989c57d5c9801d1024816fad18f5a492901048819505872f03445044ba56a0a90200276df8a6f1d4b5bc56af36f9bc76a6abd4f739f382cebc9e475ec1eb7777f31a5b7dad45c4d5cab364c44a9288a228dfccf4234aa2d253fbfcd56aa7765cd559ff4565c564a23896aae46b48b2df95b5b9d3f776a5d492bd65c4d31cce99aab79ebf89f856848be35cf71c43463fa9f6d343f6edba79f83e8ad4d5f7ea6ba8eb42c2bbd587cdcce58eb1fc793866d97e8c83c712642a3e9f0a5d15d997013fdad314e476d9852b2b4b8713f6e637387a34fbeae354abe0f16dbaec5599b27ce374a1fb743bd9f08b5e66c67fb070a494bf7ebfd9ab4e27ebd5f8bfb356daadaeabf7f1377b6ff1af93bb4a5fad4f7f5c523d54168c810201dcdb923e11e6bfe7d2dc522f9966a7b43bf498907fa178fa2ad89d2681bd728fd5cd5f42b8eea1f2d792db5d52f2979ebc5a6aea7d6f46b368fdbaf4f6d6b5b5f5b6f7567b3f99350fd4df5cbd7e5101ad22eede8c8111b5acd8d7d0e23511a68d44644434d6df935ad3463dbd19123e2d5e1cb246efd352ddaea9762cee5b40d8b39970b7118677a74e408b5e6ec6b1508b7ad4e14c5703bfdbe78bc9696a7ed2f121ad6dc7c6e32571cd53666e9ed5a9ba5e12d7c6aa34b5b6dfbf3a89932d6edbff4715bd791ac38d7e8d7f08d9575c4a5f9e358e9d7c85bbf5643a35d5b770dc768a5196fbaa928ee9e09d3dbb519b7c97f7deacfe73e85687831fd265b526d7f57baafc526dddd4ac30f2db5d9b89bbd5dfb354a62dcfe1ca6fa6df99992f5b7ae36e37bb3547faee66a525d296de352d398eaa64446b3db89719bfa0c4942524443dd644f266f73b553d38cb5fef12ffd5cb50de36c44c37c92946eaa2ddd3d53c5bbaca3cd786b8b2bcd138fe549e2dcdfaa3ee99766ae3a3dbd336a5a9cce7f75d886a90de36cd24ab5ddb8d44e7fc31af2c77be27b2bdd953bb27e2e2bd55517a34e7fc95c6f9fe9c5f65a31c48dd8fc5dcb7008d00ce8bc595ba5b76b77db96f5667b32f94cad59294d641a1f37ea106f4a7338dbe82ed7265a965647ebaea47edcb56f0b85e8ae34cd1a6e7c69c6d752f2c9b31c2dd5ff393cee539fe5782dbd955a73960392d09a95e667ca5827da6c38a44cba6e7a6dc58d37361ba68fdb48343f591fa7775f24b46645a264b87b263a03d5d5ac6da977cff4241e5f88de3d93780b4d4a3e1d6264168444c9dff5feaee3c5e3110d35a5f5d8f649a3957e8dfcbfeea17ebb6d596ffeedf4d53651f235369d9e7ec67beb90fcd7bbb2ac58ef9ec986f3df2742c97f6d856b4892bb933008c922e40b979029ee4e69f8a1d48776bbf6da9afb4ca35b5793fe786b1379eb383e89838cd85d9e4764b29ef367fc243f0540e288a0330440801c5c414b70767ad0d1facbddad547f2e578a1b77cd19793db83b93bb80b0b8cee1f823e409776e6e687017101477279f0905a2717e1fe8edda5d793125eb97146faab7ad516bb39d9a92bf6d16a75f5f6cea3fca8d4aaebdd98ca44442ded94ee7b736bb7111d2e746a75bdd48377bc4dd7392e024b935cd94f20c87ae3a5a9675a43357902aae204e1597fb6de997b76b359db9820471f7509f355790cfddf1257576ce1ab870771cdc5583e96159be4e576a73bb76eb6c6dd486290d776579dae89396a4faa90d53991152602bf5a558df8ab58259e919f9b9df44b9df441ab76d0e41e4ffbe44b87157201a36e0eebecf9c8730211992a49364dfb7e39de524d9f773bb6a8aafeb977ff463068f9c9b6bbbb9b9b936318b3f36b6c992d4c7bdc4b8467e0e8f321e3ce78fec67fc2419db6c8a38bf0f17df9d752daba96b1e524d2420405cefaaae9cfdc5ba2ffa3163a1dbb563c642daf6d9c8963509b65da2bac799bed544ba5dcb54b7167d82fcf081875bffed766aba71379ba36fee76a5f96bb9f3d2c76d9bc7eddc79cb15b7e953dd1721fdf0c00f9cb28e389b76fc2312e976adae9fc71792eb5f6ba492f7f3db3e37e533c87d273af1e02122dfc90a65bcc76a23b1cd6f9abf3579bfb43f668c4b5b59da5a7ea39e19f78d4ba4cfd9a43f99541309796d11f25a261f502e1fdae5c3050f95966c5c6a1bf25a261e9684fa71ba969a92e46fabb1f9e4bedff4545b5c120d372e35fd9cddb466b4363bbea96fc86b9962e4ad9426bea6bd69561ca591d732d1e8fd267ba34ffeced8fc4ce9ad545bfaf97149487c80e19bbb4df5db6c98e7ec71c6aecc7d3622bf2c6d119cae1b897cf262205d626c543b91c85c757f54626c54626c445e5cbb385ca21fab8984bb9df80ed9b8e89bacbe6f7b21b76b77386ffd42c8bf6f22f95db72e3136c51cbe395bcb20f7b29ce96a2671dbe6704f6ce67090d65f929fdf4834cdeae3f3d796178b3977bf378b34dab2761af9f8fcc8e9d14327eb1d32769c765a3a6578febdad2ee96f354794ff76ad9079332a6dcd0dd9d81ed53e87b2dedec8ae96a6b64292b605b262dd55084f0f9dff8c4b2ac2d343a7e290787ae8605dd233d512486747578f727674f5086747578f7ee8e8ea518f8eae1ef9d0d1d5231e1d5d3deaa1a3ab473c7474f5684747578f747474f5084747578f7ee4e8ea514f8eae1ef9c8d1d5239e1c5d3dea91a3ab473c7274f5682747578f747274f5282747578f7ee8ea518fae1ef9d0d5231e5d3deaa1ab473c74f56847578f7474f52847578f7074f5e82f2e12da6dbbef93443942fcc848bfd57d11ce0edc8669eebc4f6934fab8241f9f6a92b7e6dbbedf34c4dd75b80b278a87e4e74efd192787e4ebb39afb7ece67d0d6d6c86cbf165d38b1907c5b9a66a5f7bf4675c5d1fd9aeeb6cd18b7cf4a755fb4e302e110778534b82bece1ae7087bbc2586b757110f9e43d315012f9d9ea17a2c346304efaadcfbc2f0ee43df1d1673c22e5df38c911af17877d4f1c74bb38e7a0b373ba7bd2fe5baedbdd918efc7db0481989289ae269e67090fb6ebc7fcb9f1bc234d65a5956211cc8bf378b746dc5055d243716c8df5462249d333e828b92cab2069d2546223fe38d848b8c98466c04c96edbd2d6acae2f54a499c6c77d16bad86a6d73b513a9c4d8e8cb5a2d6d6d9a5d1c36c669b35e24773fe23e3ef9af3df237f7bc795d71e68ddde599dbb1db3657738605b83bf1f4e0b1a3e394433ed3888d04f96fdce4e373bb76e326db74f1f81b27f98b8b7a74724e7daba550cddb694859ce466d8ffc0be93777dbc747eb8b75fd231994dc55b3c55d353f70574d0edc5583c5dd635eb6b6a58fcfd6b614f5add644535b234ede130b6d5cf4242eba55a0cf55a44bb471517edb547f68e3a2df75b4a5790aa929dd7c7d93c3a4931a166a40a778534dfeed56dc6862fae7b5fbe2d1e64d4facc3123277c5ac701d573f26b5c56d8a1b2d4e476d6eb5b4d124f2f5494fac83ff80137bd091864e8813e6340d014272948444f3dc93e2e91c7e0de7ad73429c50e7e6da327fce06a2978e9dcea135abf8bb36d96c8aa24ec823c4b9b9e1f2f3b9ebdb284ddf536c62a2e993925fd6ea7dfa35baf599a3e4adb4acfb7ecee69bdd33b93b11f721ee8a210d213b88c18826a908072441a290221c901c894262915950d09022f456dca6f945f29e38d3198f3027c4b961aae3f84c4f2defe94988a79727f7a727ff9e3cf5e4463cf91147b8bfb88bbb3b7127c093bb3c3d09f174c493fbd31301fc690877f798fbe82e1856d0097772429c9babc3b986d31597238a529cae43eeee3974e0eee3638469c425907b59b8fbbbeb852aeebec7cf198f74e3ab29b5594afe1edf747aaa2d7d5c12054213331669e403e91cea6d73b6bc0902ba5d9b379268eacfd5bbc76ae670908fcff8241e9b74764e597e232de6379d6a8e69c4652d9775e32d3ad59c53cdbdf8bad6643a3be78f53cde56e128621717fb2e69b89c3d1d6d588ddf612f960cb7aadd068f5b5793b0dd995a639cbfdd63b5d7146f98ddc3f8791fee2a2241815dcdd88bb688cb80371f71edc45c5f0dffaaf88b34d1cdfd43bc66fa2509e003c1d2fa786a772b99086eb1ce6df56ccd5cf5bdbca276d8e24da5f96d5c8e762abf3274172f71f3d6575b5c0847b18863af69736bb7ebed18d8b68a869596b798e651d6fd792f7a49f2bfd1c7cca6ffa52acfad6bc7d70bae25af871df19e334c578e374c5519fd7f99b3e53d296b5e912e91ce274c535e5d77ff49f85466d733e3efa3e13dee7ae1be9c75bcb0ae4ee64f95687fc4d48c8bf3808911548e77c7cf4dfaad67f44eb1f71de1a1b61db25da65bc2b79eb91b2e637919435977111a977d54846be69dabcb54f4e57dd8fcee18e7047e7e606a72bee9e3e3e3b63d33cb5f8b99a6d7fab196fede3f339bbef99574061051356f068d4874677a5bfebd667ce380817e9d741a98702e9397990fd88653df758f5ae1afddb44bdaba6d6b50cc28dbb9ada0ad1ba96630e1b77b3aff30ab86668709d43b2b4274e4ea873836dd7de7c59474dfebd59ad71bb1a7511850a552c208618280ea58186eed7369b2a861862a034c828795fe523893bb80107ad08e20e0e000c721008873b380379b87b91bb5268ed8cf50e170e1e3891bbef70ff50f8747077d15d389876bdd9a6ea3d0e2d81ef7be291d220a3f74b71dbbe14c912b356d89aa434d0406b62f7cb70ccfa8be2ee3cb80b051e27f1ddf19f776d4789c71d3b23ce68c3e961e3b1edece4f0203f8787ccf951499e1e3d7a7aecd47478ec8c3f5e67ec41ee18b73ef30e1d9288826646b320fa439d489bff5e9bc3eed6231827b98336f949db84b351dd5588bbdfd8b8bed8eef1cb211a9b4521f9e4ffe8f454e7dfb8897e3ef51de25f905c03b8c86df19546a28b203b9da75220042af574e42923a3261962e083af166080182a70860e9e6a6a62c6075478389884f544004828ce6a01912074a0a9c73d01e01727ae5081b38210850800e1cc16676dd9b265cb161eb80a0147f8b8c20602f7a814d73a31857f392057d8b08bb90a01a910ecc4d0c3553b5021a40747a87b0d4b86236690b9337951021cca98817b2108e24905564d9c9542152380220d240eba7cc000e2079a9c554495274b88b0c55737c0541aaa6c7167d56f6fa1c4bf1d0c8539ace087bb11a878520aa20315e541410d30b0c5593f463046f9d980ab701005cd83aa714f4a0c880997039e3a52821274707a807f547486c034587016110790e2c9c813ce32a30706702086e61e4604d2500247037c35a3c3861a51a6b8fb0cf766cbfa3e58bd4fbcab465ccb13cd593996830ed2e0392b8a47aa5e1ecb6d78aa13bc1a5e4b07f4e12903802db0be564b05a63ccf51f842ffdcbb01acc173161095db5879ee799e4ac5e383f9dcf3bcd437e4792ceff3bcd4ca87cffb3cd677c291e7ad3c967f9e4c98fa56abd80bf5c0ef039f78a0e77d2f1e103ecfc5551ef8a5def3963ecffb5a387c339ff7adc0cfdbc1e77d9ef779deeaf36ef050f0bcd4d70a812ac5aae19de07926b4525e03bccf5b7d9ef7a544ef035f5c787c319eace5a972a468f058df07e6a45ebecfabb1c1e30100cc81114a197d0efa17010f4cc540d5e7a55c7500af069e7fee72b9bccf2bc36b7d3278dea7fabe94e7ed783bbc9617f37ddfb70af2747c1ff87938a9102685560c9e0c092a7c4e78aa2f050ff4529e7f461f7df140ffecca29cd2b40ca6942e07990117c296fe5812c4fc64b41f9be964fcaeb4979df07d22fc625e6a93c8f8503e6b880e37d2fabd6e7799ec7f23c1feffb60905a792e1f986af260dfe979a91bcf05e6f35a2cef0bf2b5f0b5f0b190f2947b36bcd5f77932a02b057e297cdfe7ad3cd0e6b9e4f060549fa7f26af898f98858302e9f576bc5b03eef06afe579a9cf03bd218f85efc7b7c207f3b13e9597f23eef83f958a09752a53ccf8797f2629e8b17e3791e10af85ef25c5f25aa0f7799e8fe7ad4ef86e78e0e7a940efc707e3a5be16bc54eafbc00ff47e7c9fa7c2f7e2b9782df0f33ccfc763e17b61a53cb0057a9ee7837a386468fda466507929cfc50371782e2e3e303cccb4e0bd007adfb7fa58efb94cc08119ba71969bbe43109e161dca70aa031459ace84033060f72a835a0000e34c0e249342f702428be78a49a023f82628b061cd160243740b1a4c5a8f20862048a167e305457c0f901058e1d3f6a8d2a7cd04026870f9a178f87069e1178685b9ee0a147f0905684166e68523e1b2c52601b8a23c30c516ccc708146635ca02890e0514a50729a6a09a7a000dc53eeee32b8832fd400702e711e2f23661df5db9e1c6bf5478f0f9e1e3c767472709618b95c68e81cd65d4951c6466a26d58127b0004fa091950cd46301410897144f2a550c2b86170801429103a21003812a560478028db89cf0b2020e013cb9e800ee906a0166af625a42ad9ed54b0a088e1795099e5a549e6e2c11c2c547aa013e8899c1936acb932a4605163cc56c58079e5c70d4f85840543b54067062024c0a4e0c0122f16483c6064deae6b5a385e3b56508d50cad1ed64fca46eaa70581a7d4919c1dab97d5049e52302b15401b989c9407c680362f42aa19583aac5458bdac96626ec4dc70c1e172024b8794118658a9909a3284aaca130c4b608e8a30b000fac4d858bd402f75930a028d6250587dac1c5833ac22950935bc154c4a05c6a452a92fc54ab5522e2f2b8029160e2c126c18bdbc582b969762813cc01da0d74a213523848b0baa2a9e6a4c4088d70a9c6905da1e79008b97a5c5690f480971db7c92ee2e91130f98a1078c4234aaae0307e8801007b4b8873b74a55ac75f4ca3519b8badad29baf608ed62a06b8fd0e8bf8eed6ab627bfc49972c0021ca085b6aaa9be5d8bdbd4fc67ba5dcb44561a2671a027db6a0671c015fec9c4de9392b7e67e136d8125d44cf7c48df4d6a02da0729fc35b244165329d1d991099cecee954739624c98b6bd7e6f4ada61673e7d5b6cfe6c53531e33d569b6cdf1ff31bfd9848c812e3f467dc16afb87f59abf96f79365d0d48717751034e1a18daaf3540b5d0e255d4c28a1653eee9a45e5cd382965f8b9e9a5fd4fdd5c2259af573162a55dca944712aa6fb7e4da547fc31632aaeb2c4795f2c19a0e2eebb6a8afb7506a0e0dae70c98ee62595adc98812039bfed4971cc40cb3de3bf160359dc47cb84010ed4fc22beb27dad598ffce724205bf3cb340e4ef588ec87e99eb8201c6ed7e274dd5a86dbe60bd9f9f824511bf3d4d78a5aa6b76cfc24b29ad8cff8494c998cdafc7c3e35369d8694f5246d93252f910e5fa015d22cae78b8ef898510d18c8bb288a22d898bb2d83dd35116397cc45a578a77ce52ad635b8a35b5f9265b7e116d012dee16a86281271742f43e600b0c795873cf18354f6a810a68a9c094b28eb756c0e9d60a186d5d05647c07f99f6de4bd3597df4c727af26271c5fdb30d8b291e2ad1b078e2c37fb661616261c4b178b9a658710f6bc86fb2e514281e6e5dcb8bb1a65893ae29495352b8220c770f6b3e77ea18de7446ca64d4e68abab5f81bdf5d45525644fe6c6d8dbc75fa5b54d6fbe55f5b5ea28cc5ff7c9a148080939502d8dd7db88b023d1e3a3d79de6c2d57b525b7d5fdcda74967564485b6aaabeeceac80f2d06657eed18a28332b9cdc37bed40a23775d29196445cc277045eb7873b7f704a0ecca7d31de3675368121bf7d699b6c367fd7b28efac99ac337a88aa830b7e3ae2ab4a03c39e7d55553cb70ba0e95650daa02c7ab68854a34920a2c960a2aee4e92958a274efb264c054e4edce32f7da6c2354516a87c9abfebedcb277165fd8cd375c8c72729e39c1fdb2ed11431774d319372855e2452a854d23c694fe2d18726058a597533293329d4435d714fdad19e4812f022572f05729704b04b023cdc3d4c92312deb38648f156f2a01971450a1eeefbf6e7f8d3ea9c3652905ee964f338733ceefc3cd75490183fbe3765f44ef9ea9766ad9ee99647f9635c9ee8be47edb27f11801288fc0935089a6f7899fc46304704ed6f6d964c15d11085296962902383c54a2e9bf555334ebe3c4fd1a025ac4fd1a02563c54a241200a77a65c739f87c8924cbcb5ed8f74aebb92350ff1f1497a1207d576f588ac86dae8ec2cd5cfd5bb6fd591444565b581bc9649276ebb941fdb2e51c6d7e644f1da1c796d9137775bccd8be0f563fd368b71ef265edc442b6c6397157d1a9e672a393e854734e3527f44df6c683e794e5ad2fd1cff849749a7e72a3121f9f9d26998f4f924896180a27f7d0e96da7a6da922506423e6edc56a79596e4c7e4a9795802c590d2121e96d090099f4b1493a7a6332866307922d5c8fb48babfb2a41f1b12f364c2d80889713a270c549675a4122ad176cf24eed751a0844a345dc528b87b8a983c7594214c9e5a8b03a0ec3c7300ce2bf9db0128689bf10bd94e5f2493c9927e3216fa19f2e384818cf25f1d92dd17f1f149723de1e44f0479e286874ab4bfdd5a9ab2fc47393f999f14b2f5df2cb94f13a7ebd0be9fab29fb193f89ce4fd61fc2b64bb4c9ab84a3a3a4b3c30012e4dbb5bb6aea7f1b4f0f9d219cae38243e3e5bffaea58f4f6e74fa5c6e549271ce3d6759d611a7eb506e54425a716b3c9244a415650b704101555c010450008f0460f19056436d48229b188d3655dc4902a96702aa48409204b8879fb37b674c6d6afea6bfb76b6bcd41b1425d509c3cd4e47d283d5e8fa0f83b6102279a9ca0e659bf86b78d4dfe5d4b8a00331000055f9af84c75d3285911d072f70364f170dfd7b3031820aca1e54625e47d24da6f1b8ed1fc3aaaff850e707400d03dcc914906f8c2003f78583f1b40c900373cd4fad426de9aee9a43132c68e2c9c323414ab2cde930a564c53561c43dacd48687b89818828b8929357f33aec5e8b6e1d7eeccc584c9840a5d2ca15b42e75a22c7132b7ebf4631bed15d59ab359ccdfb4d375ad6f3d6233424df34712677fac4f96fb7923564fd7cbb96d658a8afadb9214f9a9e50770fb7b6396d67e1b694fc8c2fc6bdee4890127a32c1297103259ee88aa32e2592dccd9a37dd3fd6d27429e1ae24a84842497932b1fb6fe4e954b5b54dda924d49d4e0ee39b8ab0059dc3ddc96e26dc3745775145f5a4b6af365ade2c6faf99e54575d01a0e8b3e2b6c515c00c0bd013ea8a43428b7bdd75e3dce71f772121c577fea66b491a0d89a14dfe3de9e3928ec8b51c9fc48db7de7c0e3a828a87e43db18d923fd2c7251de1b4bb3547314e3ac228dc6d33f7f4b34e721d11730f09a0c5afcde5af691d4904a84200a7b0e64890126ac3648dee3a7e2d4680210ff3efd2da86dc65445448fefe6c7e59a3251911e5663f7f13bed5658411f770ff8f34e34dda6a7e92112d2aeeb6b336569369ac25ad549b1597df28d4f45a5a73bb769f3863f2d4da525d752e274b2ea698a458477735655963f4eaf0e392643f4e1848262bcb1a24d33b24aa2b4dfaf9c98d4ab6cd592322f9a4cece99fb6dc57f5b6e54229bed72b5a3b5fdfddad64fca6435b19fdca8c409030da0e52ae28aab8828453895bf8b18ca8d4e38dc55442c54a2b988d092e32e22a67ccde651d74e7d4f229edc9568175b7ddfd4a7167fccd8e622622854a2e9fd5a5731bf4d249f08193c54a291f7b58f0f8d07fa8fd4a39373ee7be25ccdcf34e252a69dea116a53d3a39373521ba2a0d8cff849f2e74e7d3f67b7aca6e7a4363d67ece79618d7446d65f96dfe9389287e6d5773e4eb22a6119740bbe6273f89eaa59261b5582eee3d66eeae7abd72bcf0824b8daa874ac5549a4c37b8bb0a869c1eaf1eeefe82e1e505090c50e9188153821120a20c99e0c3dcb163811577f7187ec822cadd5bac95ca25f5793eb87b0dee6242777956db93942cf15059d620d2e224f9cbb206dd6f1af2a4c5294ddca749c3c1f57400ff8b371eef496d849e905c4f323869cbbaab8d5e7ca9ee2fb5a19fc34866cdb4382de6f0d6a76edc3b279ccd9bd535912c2d4e64dabadab0cea499cb386c13b5a8b3a8372eb5aea3489e48b79ab39f8dbe5681ead69fb3494ea4677ac891e4887c974f5d3f272539d25bdbf2b7c5e5a093380e37abdd5dbfddf7c44064697141aead91fc1b2779a6baf5c561eb2ae4ee41eeb9e290888872763327c4cce5216cabb91ae9df67cdbae66ae4fb7e6e567335caf5c8ddf5c62592f68a4b528773901be54625eeee983c917cf0c1dddbb97b4e9eaec9dd3bbdadddbdccc4dd333dec3e3e2b7fbc35e8469e368cb7defc4d98487edbc545bb3b7b7397b473c2d9c8079395a9c446494884d05d35dab2858c21844058400dce51e00494123530e38a6b810016402db1445d0a2a4d88219e11e507e082ac2f1441c58b38820720282054dc29064f88210113d0bc6c620c166c6003385e8d2882d3f9423d0707883120f042891761e0052b2880060c70210cecb0e5388115af81082f9ab861450cfec3d5a4044a7c208aef041d1da5e06ae2a14c0b44a480dabca607258638a37e0e43cd0bb430d108719a5a03683ca080390b46b8c01764c8f0e42a9c9143074570798e63c78c264550200137a103608f190c40052fc1c90b4d180942c95f479440054d249181db08017d4201352a4f35623610c3043f513c86ca929d12a0ce7882c919a1596419fc25688ca72c5570c05d7ab4387aa206503cb5863200011e54e09e584c5620a288243af8ea8a920c29b648c257de1542f40122573da5e16106368280573080021397251cdc81092526104413415239986d19c00d864a09a0290a53872b9e5231300a2c5e8882fac2908114412710e0e94382840b984c5e501f51179985224488f2d5f8e1441143977a57b4d8c009657481e349a1828816505e583c2080b082024510c3c84b92c50710d0c309eaedc460880b4ac8428b77e389026e68379e78542e4091344ef0e433c039326e0568f12a41ec185e98028a67c1c20c11c02dc41c8a13d89dc40870bec40e34a4204636e44334c14303352660c54f235d1809020446dc8a30d84d718228dec31446635491c593e3e09200db0448166e845c22c30f4c7721516025945b20e13448f1b2829a26aefba8460420f7038fe740c5544338d1e236581881011381171e8b8185a817a0ace0395e37353c79c089539b9317441089c15998d9a902082568c05580810a100709a081a300448e260ef85101372105145460070174380968384006481fa880bf58424c452441e4368c989093b4012d2ec3011f8800070e311ea3c4062c8606428739e1081ab8392306ee1204a5a01b362082b77e50020d9070e2f3d64b0c3050ea0012ceeac932f3840a20f8ea07338e3481040f5c0506095c801101ebaa9f0aebc10b443898c58c01127003000efe989201287478c05326c880109680aa9ec2f50007811c74e129572823c51737f807b503f0023dfae71446168f06a27f3d4124512027c6bd2358008806549e70af045d6c192ce1718f01b02258400440b8977f761520c270cf089473478c08eec950a505174c10732fe6c6029b0f28b89fd1020caa1f10e07e45015350118203b883e0003bb4ec92b853c08b175c81048b7b0358a416167cdc0be06ab2016cc13d005ab42cf00410ee4c5820030fa480bb4d48aa898a8c3b0f32a87d67e0c0fd68003a80a1420eee36bc80cbf18026ee400960818e133fb80301c3dc32acdc7b7860888e0d5ee08e3364bd20018ffb0c221765e423b8c7108431649822e5fe827731ba0005ee2e30118111472080fb0a35d07e7c0ce09e026d479622b0e07e82093af092c2c5fd860f4c6831b8e04ec23709d1920277571154c0c00921b8d7b882c40bacd881bb4c921a1dba30dd618a5083062b31b8bf848006fa810bb8bb54e142123350e1debae207266460e1ce3ac14d800942e0cefa968405d081fbaa88192fa70d779514991620f1b9ab6e8481050740eee093033409238a3b9842173e1ae0857bea0a99d60952b8a790d840b90114eedf10608648e2c6fdab6202251c200bf7cf26c31f39c1fdd3a1c5160280e2ee99114518c4e7ee714026809905dc3d2588503406cbddbb19fddc12e0ee015569920615778f46084af40872f7c01f2a600207dc5d071e0e11057757010f54099670f72d5a004528c9dda78040154980ee0ee5731eb8c1dd9de0e055a5e6ee3e14010d2e9cdcfd02008503b8b8fb588314272cb9bb0e31025c51c4dd8d70c45c51c2dd89fc60d78475f720518021821eb8bb8f8b89273877e75193031626ee1ec2e0032307dc5d061c383890c0dd61b0c2801f9edc9d266a69053cb87b0b4d869678dc7d66c9e5450877c711c5882049b8fb092e34616ec0dd4b98411753c4dcfdf50222b2a0c0dd5d2e2f58000edcbd062e7763e6ee31676012a4c0dd61a230d1c3e5ee2f4cd048618abbbbd45498c9e2eead9c0d027004776731f1540606dc7d7582b2c708775fad401343c8dd5540e0d0438fbb83659c4466dc1dbc3350404440c1164df8304087bba778422203b8fb570327b090e2ee9f134f4ffcc0ddbf249831dcdd7972f658b15964c9b3de9bae3557f3e29cfd5aaddd9a6f4cb60da020eeee4c487777171331a5837954451837b6fcc0dd4129514c6154c206aaf87077af06267840f2c488066ce1eede113b70a126ab7eaa70770f04a920566003183ee0dcfd1bc00b371c6003184a6002774f01a1073d5460025c24a007eefe2949810aa6e09841184bb87b6a4a0c019266aed00191bbab68c8c9a270e2b3b9c2dd3f23436a20e3081d4708c1dd53262861a50c27a06ca10277f74c090000030f5062060e77f72c40819acb6c21050aeebe52c001a23c307c8e10c4ddbf3180b0c40f693861031fee0e2e80c64a00ce044b3370f7fd77ebdf373bf48e1df875358f1c0969b45d49259af6f1b1958f44e381bec6818636e45b4aa3d5fd39fab89d7150b875ed447aba2b8ef6190759fb341c1296650d0af7695e4b3f8769ff423889f66f2bcb3a86b4b2ac41b4b7d9d01bac3933fab789788be2bf0ddbae15ef97dbe9b4e6ac28d455c45bb489babf22de228dda70599becc984b46289b1298619db3e87713a6bce7ebcb828c45b0c6924d18c76f11dab356748629871a51b9f4ca8b6e64cc86e9669373bf67d5b8e346bd65dfe263c6a4bd29d7e1b3aed9ade6622e1aeb4d930792bee5a32633c9678c41b67f335b6d55d5973789f955e1b5d446db8fae8fd9ade6c98964fe29c3ec3d7373bbbf1edafcd3567b3495a5dfb1b596f74b77f5f4b6434a3a47d223b31ccb9d196dacad7db52a0300ca98dce0c40d4266706a0d8be95bc3fea2729c5dbe66da90dd07d5c0e34245f6b1c79928f1bb56975766624a468764424cc6fcbd8f6e79f3724917fbbd5249af8f9c98ce99735ab3536e9ee6bfa4b9396189bb98c6d35baab395a96ba6aab6934adffee116ffa4c35a74b5bf793f4e65ded9df6d678c828d4bbfc27139ab12d69dfafe567aa4f7c71c68d77a7abd611d65c4d1b164591469f24ce26c54228cdedda5bdd9a56aaad0de7ce9cbd368771ba70f74c2553fe1cada415f5df33ecf177ebad7159c58cf5f84e7f312dcbfad914456d6b32c3fd3ae35d4d28899128da9acc126393d27bbbf6e6b34985921851bd735b639b0ea55b631cadd96afae5aebce4db2cb5c13651bc38d4e43795587fa6e4c64d63ad519bdfa7a92d49dfac7944dad544bdad68e6324e0cb12e6b0edf201a6afd99242bd5e5df5b71a30deb7d45263932fcf34f263457ddd561925ebbad69c7cf5accfbe6d7db86ab2886afad2e3f6e631aed7318e94dfabb9296bc75a4d5b43f7e162fb6b4d9671c4422a131ed6afb85c48c6d62480bba5d7b6f168916bb287fedaa7848afdd39d905710fb5cb5f3b1c4e3a70024198b1cd673cd24ae94ce76b3fe37157271c1d16d74d2971d24b0745a7c4dd431d4e531996d2260334d9b82b2db9d8f2b08486e49f669c5bd07036a4e56109cd76ee70aab05bb0a15c1dbe4cb89b3d6bf9cd53d34dbb38ec8bb5160de5617e3bdab2e668d54fa1d6b8480fb910dd3ab17898a1f8c599c93da90d4ed7a1d8cf9f4c5ec8f849b28bc995ad9938b94c94d86c98898bbe501223f3cac64d2614770f4d236a33b6bd4c9793d80a7e52f19273e7b6c62da7dff7c7cfe2d638a7cf7b8acf84cd28d7f47a7bdc6fcb8a489235072557864a34f1f7df6a7e3223d9164826d3670db23519ed2aee2aee2a927fcf6cca8a7ef4e7fe2867f70f5e06f110e3235a6dd3ac0469c83481274e537a1faca636363cd042f2df44c25463c2e14e5322ad68b3abf1400b773b5773571bf56b498beb25289c7554dba527e412cd7d0927c4a1da2e799285b24f3686d3e2e10bf7c4dd71381ede4b77b5a0db15cf46478e86e8adf4f6a4e486e3a1b6b6a8c76d9b140fa9cdba7ba849224b7e9587e25ebf1c9f86132ad1ee9bf83d54a291564828aef58f24ce4325dab6d79247c6a890fca6fa1aa5f8683a39f6b81e5d3530f2635a7715a26b4feed78c3cd4fdad51aff55571b5a7d2aad09ed094b8d3622e6a0995682215f1491cf2dd787f6bab7535772fb1872baf1ea2844ab41ecc1e7094b2b8bf94a428392929f912a85089b6c4892f315a42b5f000e5a112ed89f340e30167872828dfc1c90e467edfc43be0c0e23a4cd1c1e9a5431071bf7ee9e00a95684aaab82b49c0dee27e5deeed5473e5dea2adc9041aef4ecf709bb43a11b7717e1ff4e3c6cf39ac7518a7457caf0e9325c623169d6a8eac8fab4c1897e2ed5afd3e58fd198fb56a736d12ff6d22796b4e6b118bb6dc84b7689e4914c039c987a41c18a7a3d15dcde6df4669a6dd341ba636b47f1b6df66f13df26fedb44d36ed90bbd4d1472d3b95dc57d5fcb7a7c7cc8ad4553231d45b9d2121b8cd3f1b08486d9525d71afa3291e9219db6cb5261d3d31d5dc5f7c64e4ae752d6f88da9556d7bfc18a1b947878d26c48fdb8bd77d5157703ce0d2e6c6474c5bddcb8c9c8c93dd4e16b3414deae110e47228393bc78acd844a2c41d090e1297874ab4571116772f82c24d4550eea97f57f5aedeb26e6bd651466d6a904cffc6497ee3243625368ac984e88b654c233652b7aed55dbdbf71925d3da2041f71b97b686b46b46821a8b29c198152564d8de0fc766d90911e0f33d67d0e465a78cf5e45aa58f22bc5bbc8114582e8dd33d1cf61b2de8acb2238f6ef6c88720f5f365c711b9edc3d5f9bfdbb970d384ea33b4462ec94445c109961dd95d888863b920867084bb6fbf3d0d3eb9bad66ae8e43b4d7b61ee9be8846af1db22102e5eed5081127770fb52532e44ec415bede2fa12bee4e858e92d0c7ed22212a7eda849cb8bb108ebd65ac752409b53cc4b9cfbd8654d1f80e6142f6f9dc15e73e37c408e73e2792641d12fb212d0f956858a0c81702458813133685e4102ad1c87b0ac982b6ae466a365bf2b5add9ac63d0134146dc431a92b616240344c543f25a4a968fa37ab4ba0df4837ba8236ded05640372e56839d0ccd758a7a38f4b9a51a129f140a3f96f179794246d4de835abcd5c1e925f9a6726ebc9843ede543f699bc80f72c53d2ccb199dbd8240710f5f41704186dc83d01aa2dc6bb8524307dc3da477e352d76054434cd4fdada15593594942220449064566ba6c4826030d57dcab99b344e8ae9a3a3e3f2e898628428868c8cd643531912cf16713a96a721c227eee1c2223af2df213c5fde7c943255a55f2135e9b1349206284b76baf92c8030d0802800c79f944b987485a47cdd53471cdb70a859a56ddcca7caf54180bfae6f24fb0409956864aeb876b198ff76b379fbb8dcbd2cabf8238b157f40b907fd781242441f97f4e347cbc39a1825eb67dceec1e2eede0315d26ad403c5ddebe360fcd5e3430fddd5529f179bf6d61c2e8bf8cb8795abc3a40f06f8c09b9a55a7730dcdaad3f4cb7137becd967407ddb8d495478bbf266fcd3d797379ac78f8241e79a4f06822dddf27f198abc9d3727792ac3daef498d2e38959753d708e4bf17bf4bc7ae0e06146fe5bbd35272b31aefdb57889fc5d370f283c682f1e74070b6db66380d70e939d15dc43ddeb50a19383ce9743829c04e40ce5cc7078f0c27902c7c9c3cf66ee4f4d146e4babbd5dbbcbfaa7d63735345a8c925fbb79e1e0e0f01d68cc5e3bb4d831ee9809d1f030c762bb6b7e5dab3638d06a697316c96a4bd2b27d85458434f7b086d694658dd15daefb9e256e2aeb2cecf1b035bbc18087341ac59bd2688931ade6a7d19b21f25e5c74432d5e32b18d96106f7c6da0be66f3a4c886e64eadcd8f87336c99610ab5c17b776b89b1f99a8166337dcd80c3dd5f3290b62646673248e0258391bb87369a31c59b2ad17c7c683cd06b6732b4428a77cd150f31ae89e2e1aed4fa4bf27546af1aa3d0de7049751d2f119dbd6a78b8bb117fc5b2843adb1a8be2545f3127d727bd3616f39c2df3697be9d0e21e9e4c9eace34b47938e1e2f1d5f0c54c26bb10dd71ea11da1e1edd2c88b6b7809efcfc5f044bb6fc4a91ef1f1a1f1f08a01c7b7ae653e75d10b862c7bac4e0f0403153f77909696181bc1b084a9e6f2d7d4170c405e39b2fcc5b8fcdaaee66898038a53cd51ada3c43887510edd5fb1ac355cdec451f757f757a6fbab4b4c5e0c54f376fa6bf192786daec4d88441b9bf60513c54a2c14caf39f1051b7ac170b8bb4893c59d06ea4513c5dde2251ad3dd69863c54a2bd687078a8f4a258442a2275628afb69cd43259a1665245189b1517e1f9f249953cdc9763d0ada358b5fee8d336ecb6a623259d20f0ed44696f4935f74aa39f15a594d7f898fbec459cb927ef2b300e5ce42149c3b0b41dc672cb4aea52b507177aa4768fab5822edcdde8feee482b92565c2196b335ea3553e535e3805089b623c9fa1b37c95e6c3e1b511bf2f5296a23375f2a68ab823b4d218a3e639496312d0523f7508fb629bfdef8e6570aa0df5e38aebc700ca1c43f97f5dcf79bcad7b9c76dfcfbd65ccf1bddf8e2c613e9666ce67e536d71e4f8f185e3e585c2c6979a1585261ebeb61485174ec8e21eee9b5dc36dc95b5f2770e0568dde078be4e393f4b76afead9a2728799db0e332d98fce27aee16ef6af48debf55a3ad6b29a336ef834532eb0ffde8ece080f725a2363da7591ff73e58245df413a4f7b5356f7df12833fa193f8953cdbd0f56d7e0741d2a8ac98e72e715cddc293ad59c282bfa193f0929abd927126a232bfa3162e6f21091993bc56b7340331cb6ad1d0919ca2f931d0919fa01fa193f09d3586b1907c98aa8cdd08f98b14dd455c4a3ace86797f71da26bae55ef13977b376113e32cd6d75a8b2253ae3a1ccc530f8965dd17cb9ec441babf75572164fd21b23e4ef65223aba136d446d4fb9190481c94442489c6266c8aa425ebe332deb52c71503e75cdbf1baf4dc4a279e24ce4cb9a354f9c6fbb8a385d87649f71d089c58b45f3940dfd64ac716ada888f8f8fcff84ddbd6a84dcf6981867eb6191424bbf8f79eedb6fd193fc96edb1bfc5543898daef977e5ab46cb3dd4a5c5e9e8cdd6d5466fd7daa02e253592974c14f7f02523e41563860e7fc554f18bcd9386b87157f27eae396aa369483492a675b82cb18dea5dd5bd629ed036beaf1825af981a3058dcc3fab917cc130fb57dc104a93f44afbde70b4626246bfe172ca112edf532e5f55284fb93ef8345aa91c9927e6ed73ad523e4cb92509b22e2c625d2c6a596511b9288dafcc64990643f41fb7e4e964412ed4722ad115971b9aba6ac06370d91d9ea1bf9193fc993b8e867fc24d87689b6c646b86d7378b5a4b887af16cd29ee6671af56ab159a486e701105f26299c02b2eef97a2d5892f96d38bd5c372bd566290efe393a4c377558167d2358949d72d7bad68af55cbc3b23c6de4fd9beeef67aa82c2b55bc7970ae7faa51af21f6ff84b15235f20969cdddbfe1148c5c1a76a1279814174ddf8e6627d82383c24aded73b58849d7242aafd493570af74af588e42b7582874ab4ddb63299918fcfe7aa7d7de6eb1b7a7d36fef2b2501ba6943e2ec9c727e9e54df1a497f78471ba97470bf7cb6bd5ec6a2fb7425f1ee5e546ee2f8fb9d7d9cb7dcb94708b93708b120fb7d02364f1f075c599478072f790e9084f47303a020e235871cf9da4119c84f67746a0b95f6c049a3bc934b2501d6940699bc693ffc5370d2569c4c2671a6b2e42d417218afe7cc3d9a4d7d24a6db8160157041e398caf0ca7eb104e575c59d69c48045a7edddbc4bf9808389e3bff469e77085157863065084f3e04a350892692f7b47d1e028e330d01a62696cbb5acfa73368969ac7997650d92c9aedd9565dd329bd98bcdae2c6b12b509a236d40609d3587352eebc5d4bdaf149d1a9e6aecdd9f1c97d3fb7c77a5fbc36776d6edfcfe123e2e76a7e36f3d674b4bb6ead2386dbb5b94cca64493f5f9a6752109e8434928816841e5bda6c185f0c84a81cc67706842be1c54540b800258764a639a3ba529208083beef56b3bd7c7dd2f458ddbe74cd4fd4503aad6e85f7c040d21704023481dab6da62d89cbbf71375bfe20ea075642259a0d8bfbe26c13af15f769fe60caadd63e377d36f39be629de6acdc7476fa7bfa5ad9bac8fab41a236496262fe265c9698247fd7cf3e802a4b7cad0fa0f800e7a10f5ca5ddf7a4f99b30d565893310a433b2787806957cd6f289a90d9ff1343b03577406f5d7e15ba91634b47ca1e529dcf7cdc76d1d2ea9b65a9478e8f4e4252a2b69452d38ccc8e20e152ad14433a298e1142ad1cc30720fcd885d33dcbd0c28f75cc6b6fca60f65e0ca5012de722da3e5feb9f6600ad575ebdb83c777f7a0c7bd07ae5089b6f1bd270fb0f0c002f7d4d9a9b95d7bbb9607d4fdbcede02ffe1cd63b98e2e10e9e76d083ef0edc756fd381955007523ccc98eae840498d6e1d97ea80963607583cac4772f064476d70510e681ed29acf3817a3bb5b7340ab2e8b95da2e0b942c41bceaf2937786031db8cbb67e9bf11671309423e280de408bff0d40e0bafe7cce26ddc7e96433a77a24e31be0d4bc3fdf405732b0e419194f9c8c21df6d3270b87b38c695319eb8ebb2ac3b8f61e43e1ba3860daaecb6ad399b77353f531bf2673690e2eeb8c8064e5c36303d24ff856c30a45d36c0d96d3337a40659dc77b7527da406556a20c53dacc19310a21ae0c21a04a135a035700fb5ae9dfae52e1a60d9318d7623d120cab65992d2c0bc3a7c533b916810730ff3398e98062cf7daa965b7cf3232563bf5bf4d74aa39a3fb194bcc3596195c99a27f064e2ccdddb73d99cca047dcaf67e0f2da29832c1e2ad172fd72a402c526039324ab0c82883258c16ba71649b2ce9cea116cbb56a665d4462f695b696bee6536d1bc991bdb4cd65e57d6db9764f95688dad4d45d85509bbaab9058dd55c816438b7b98abe3d74831a0c488a2fb22319ecaf2472b4690fc3a204b62e070874116baab516d61b085d66104032618189d78476d76358a936630c0f1f008067eb335fa822be10ba4d48c695935368b5e5056d70b70d0abc3e4be27d63a76bc7541155aba40cadb5c5084bb93567c5246446d5c80e38256ae3a2c4d389b66bd42c5b7f9e358af3cc15de9c9d6747a71777f3bfd1597bfc6e5d88231c4ddbe27067a5b7e8d4d9c4d7157f14f52cc78d77c8efffacce16cc3bb05389c0569380b2cc0029abbe3cd82180b5a619630a6b88761863561d0ad698c86e12109c61530a884e4eb2a6e17711718344b7e1e5d60e0086b32b64d15f7356d73596a2b80e261cd0a701eee6b7354d71c5e410dde314aaac00c1544d1b8a4fbb4113284ea70f935bacf9a0a9c76353a5301ad3d496d68b4b26e9cf48596b2b44c3abae30b28f79026f4c5d3ec8b2054e7ad3fd3ddb6a43d99d8f28bd637e5ff5cbd56a0ee5b7982bb591cc5785bc1b9877fdeb7d273f3d904b2d2a269b2c443e46bb2c4b822b2c4ff19c8c72729055452e0c4c533054a5210732fab68fe33a1206ad7f1b3f9a4a851602554a2a1208a5d9f1445928869c44648ab75d6b75adafeb3ed043c38019418bd27ddb62f1f9764e909701e2a2de16109bdb313e09879818517d7d2fc391a44c3bf98b4619a76aa476854575c92cb0b1c5e7526c8620229b4ea6639d87689744c80638256a844d325b82296608a6ffd4d2520c2bd29bf49deae2d01908f4fd28f1306aa65594712946515a790c049937d52d495e64e82202448c171dbeac83fb148da6a3a3de9f46425712edbc4f7c1ea2aa6bbae42436ddad16edd4506ba20bbc0f1f099eaded9ecca5addb88856dbf44a4b6c785842bb68f908b0d4cfef83d523785213a323a0b98737fab9ea18818c8b280fc937c9f2c463cd7f675c40dd6b69cd8f8b28174e98b8182282dc496a4bc98bc7d73719dbfd9ad62c821188c0741a8de276c63a4ad315278220ee8e13c10d4de25c08ca0801145af3db7cb66fda643c0b01ce3dd4f67154575c08703c2473d5659c04022baef39bf4fe787e7e0d8228198f9f29089e4090e40281cc07a8b87fc089bb7bc639fa015ad10782b88764aeba9adff58196bb3b75414d710f7518f739acbf56d60b6586b46b8f7c49773d42431d2e29c64150d46b7e1aabffa864308a26c29e754ca11901180000000033110030302c188f886432c96c4a7a553714800377c06288569d49a3288721849c31c02002000000000000002000d692dd89a2067f7b6868408e3cef9c792264d6a9893ecb9337f547eb263b8f97f54c86315561b9af52f180927b3928bb8ed6b64fc379229d0329e7ef4f1d0bd5123c5316ccd7a42fa5fe962db51f80a6364c218bef71d9802ca7ac1b03567ee934a32814b2985c97aac948a27a9fa1407cf43097be70896858a65dacccab0417aad0049f2c5032fe0865e686883715965e84b5fdcae6a94a193bdc42205a996bb002b5b2f1d2285723d172634ff5c379bbefdff2fc4396cb92b4d533f16b0a9e3cc01e14d7801312ae61756c3b234ae4f231dd9b38795e28ac6d6c4c1c4800fbf44b3ac03664957aa7100bd196d044e9494ad1a70909ee631401b06a2b5e18a463501a1781b35319b560ddeb94ee37d49dbe0c64f8f6a2a5e285a446ff43a164abeaa0ddc014b095694f76b5ce674bd987dae255e07ca50f7d361219dabc1322cc951c5435786746b9c1903d70bca4bd3de12919f4259031458abf4bb8d5d9ab27b4898a5e2df74190481f739570342e47ccfff9c25c4012d27e95939597204807e26ef580c429348e7251fdd0139bbf9def2210e8787b7768bb077004baeb212a0406bdefbdef162f660deea2f8f91f2d8da70f765e52cc5832a4756b3684bfc646930a113510e8eef12c50399c5561e11a72c361b48a5b8c5dc704e1d5e4c8ea0d2a180ce46c350434917d4eec13cecc98adb182d8005e12bf46c4cdd21ce40d37179071eb607c25857d15b3337e16155a860716f6de7743fb0e3b70320801387d5d96267d9000fdccab6b97a8bbeb8c3c5349f06c34ade9d55e478f81cf4c953b5934f568925651b39fbc1189ce152e6ee806b22c5e7228d32053171ce65b73a9a6d7123cfccf7213cbfc25588d19e4e9b461c4324d9a187277e73bf3e7c4992988101f2b10a078bf6931c8addb5a967977d9b5cd8da716e2cd1e9dcb72e9b7c27dd17d1d6ba01123c2874bd7b7880e82867db26d3e06c5c781dd9ca7658f5570345ecb37fc5a30334450e6605eb604ca95389613868a9db08d3ea8ad11be6ba665ee423b54a8f15d6141eb13e674f13062d694e73b333cda5c3675f4116ae51d0088ffc0c0d573538947e23c1a9f468da6754b3d537c844458a7774236bd371a690e1e207ba46bd6d36d6db941695cdc27f32faa4fc41d2a2407a00da6839f2a3266dfc4e9b8cdb2363a7f9051c65dc0ab40946a59774037094f5586975b7c00fca6dece8d16d04ae44c45483c7aafb3f439c2b3284b644d3e9ec26763e9731996de31b4dbd32d991aa9eaa80f8f2beddffe196a3b23b017d4448882024b0cd9a0cd46dd4fddb96292ce6f711ed2262f5289a3505557ff459f37ed732795ef85dae12c369b7300314bce1d16332b1298136fdab84903ef32f5ce1f95a2e217afc9741b96e17476853659e391c1f7860758f5c7f2b7478c1dfcf2b941d9e350476aa7fe7bad4f8309219c304495c98eeca0f59cec3a247b242b173945deb09e7cb36fa18386962351fc38dd2c9603252194de5c5d53dc0b9618c51af3f8d84e28deedc1e1ec0ed5bff7479e2dd16308e459f9f9231d684756250f0b6598ca40a318f27783ce9c19dfe45eb6b1a66323a9ed862650c7874bc3942e1c7cf22d91df652d0f529025578b4ea18a5f6877e226cc74b627dba170cf7ca4e5affe79b45a9fa38d35819d6906792f99eb1c4bad48d8e15664619dbdd46782aec32a17ac2d0ed4379ceae582bffd104a0c16e4fbae119c9706f852bde270bc1f530300aeef99bab4127a2e48124c6f9cd6df15ecbd751562aa42861b6f15b5e4ddf3c043061cfcfe2d961558b497d965eb88d77ae3de2898e0e1bc43a8955f41478aac0b73b34d04c0ad7adea8b94febfa0dcc3bf528f9d15c8ce45bb309aef887b25cd9bfc4b0f812c30c352881eb7a3a97a424031c85b5a65f162c1fa24f3917568c2d31a77f6e9ed8f333a84626584156ce6eb3a87a3ce448db525c8072669dd6b05a3a2451eeba631781cc2cc221d8d93eec7236bdcacfc99790598db92681842e4e81c9e0b8be7d75af87964f38606d640832ffa6bcf08e164962e524d60df90587e0248663bc1968e11ac416acc60ceee93536e010f638d28cd350999fc4d30a5eb05695fb7c1468b104e680d2713c00a338df5506a4a30012f38e463fc80a8c406f3dd2d73cbe528951181c316c0f79a79f5b96a7dacdfb96e9334b9db791e42618cbfd378d0f7891d46c6c0c86159163b5c94ddce1bc71634fde9c1f13b09b6fb40043071ff639d4957e714efdc8226790f68afa6c176b39a40529ed207b66d4f81c71518a4b6ca03cfdd2b1dbf8ec998581e0469abe2eacc3469de065e0cd865953059a6e24014ce1caf569ffdb333b2868abf01fbb42e2e0e512c409aead7d76d906d0280e151dd0b43f3ad491ec7903087095a3c7f1f175983020bd029e912a7f2bbb27803a37535ed42e9cad87410f0b115b864630ae285c5c7924d048c7110e0c6ea9af3ca215edf2ccb7c3995a0d3de5992671e4719eb3d9c1819eb16e9ebd8fa91f813845c532f8f0f04e76b31fb2ee172bce5c3f637f32bc75afd2a42797610a5be64427be1dd8d08a16342cdad3799aa2094a52f07a2fb0ae6a5b9593451ba1c4f84206b91af30bed3d8fedd2ad3bc2ac36655b539b07841801777e295f8b6a748744b08239b056a0e9976d4057b36385efb800317c07f726e3856156d5bb43620abf7b8f0e4a73f3a8a0edf4980ba59cb1473cd846011734061cb871cd393264deabdd6b4019d8b9bde92a56c08ded488d180e2bd1633e18ad28fdb400888bc0daf858edddd488d98100914dbb3f92f8510904172cc9731750c9d494add9b5ab7b66bae94c2ad31f9203d0d333cc64f70a9b1e244e1f347052e73406ab32298a26accd9a8b8c3250c6293e6e84cd6f24fc938a5e03503076185c4840b651bb9ae8b623100ffa6c170e818fccba81fc2f07b822e0a3383aa802231612ef81e0542ca13b2edcfd502279a07682ba47d5cf8617ff750f7e718041eeb9a6254534cdaf75ef291d72e7b8dd0fbdf57676f3734c400d159c385541fbc36bbd54572876b727fad6db4501cea8d5cbe0cecd41e6c28f3f4b19a5069832c0ba05d85f1c64d940f0785a3b294e4566ca81d66e9ae38e27e49edae11c4849fec4709cc064c8ff54d931414403b7d7ec72ebdfcee3ec27c360f091bb0e73dcc7a598c05812631a26c8d84af20a8ff0756a6e4ced63785e79c3169aa533c108f78555279cc61d7612276f8e886418efb8d951992c02aa0e131ecbb524ad68609d25663680df767aea52523482d78ef6f2da448bc51204f46c61b7c120a4c9f1e305a61c73044febc879f31ce44d8d1a173d2691ce5c6ef093aac95245405e5d63f6e5ce8c5884a75313826d3b824cdf636d496f457f4ec94e4922068f0c282209532d0b23a696f4c1c4b9fb8b1815f732ea36bdd06c1c374321214922d31233a500c9b46235f97f40f413188c717609a7e33308f296782b3a9fcaa10f27a01e7e8ae498ad90a81522d06de26c91a800c3099a615ccfe986fc466e232c15b9fb97dcbd6ffda675fadc5f21724bf1c90410aff2bfcf6d765a0475326c5ac1c599a52c79ecae4ae2595dd1aa454b56b4f332d81ad75be1bd4f651064a0ad015244d134cb321832b7ea7b48a89130aaef2e0a3765b517e05f29804f028d192519677b71c2a65ae8352cc71a311e91aaf5b480dd6c620bcb96a0582c95cf9b845331e4820ac7db96f526525f3863d4c65edec4255d6e091a039268caf0f38fcf88f4998aeb39f8d5321cabbd7060bfcc8f1580336c5cb6e8fffcfd00c5c0feea9824252e2ab5ab793c1cec4ba206e962cccefe55d55b700e5be40d0617f52cefa172d2a25474d0480a9b44036a34e1966f9ca779b374451c3ad4e05010704242a017b33e60f9ef88c277b9ccddf0c6d07adbf319c9b428c72fe21406d19b95c5ccff68aaf8e9cfc066245943bc05302e53543fcb7765fac3c5a684f29b1b3d7ea13404b7d905dcf85c126eb1b63176dc3f1ebf4d01774a9c0b95049163346d172b77bbbcbdce79338885ab3751e8a2bbd98aa40c435d2ff3e0f7b8333faecffffd5cd509d6fd852e63354d234d24a54f0d16ab89342be0bde77a0612e735c52f62ac18c1b557a850ddf85b689a682cede2c646ff4c57e2911a800e3db01a30ee5c4b20e876aafa71489c7c18e2049ab20cfd82522c210a160416fc426c50983101753bf39abf65684e1fd1da6e546652c312eeae89f6eab8d1722d4d4c5516877f522024e79a38022844bc16844fd28238a8114b11f1b6478e5d82d224ecde4ab18abdcb1584aad43f6bf0877e6ce77717655fba907edff2a6adf1672871b2833aa4f5786e468ec7fcf87f77f5a5cd0bca068ec62b7f1e50d13eb16303f1fc9ad7c1ec73439a41248a2424d0af2735298335c619b9c63b89dfa247ca3a42e8d04334cb0239258df2fe310c6a5578c4793df17447986cd41ba1a012db8edaf09f0a74e622ea01d893ff3a67249c9e0bac52b734eea15c422a8fa84004fc3bf91dd3dd004334d4f61e700a19ee9934803449b5272c8594c16a4a82fee224c2f94b09cb7a59309e29bc97d613e0fbe8876fe0b449b6e98fd207db11278c2bcac673ad076e4dcf9b4690a0318ab7c885664fc355277360d80107ac9dd00166da455c52883fe224de2c1e58639b5c72b45502c91ac2b8c5a0e37b5fb7b918f58037ead859dd27937fff82daf8fc280c6ef60eec708f3d16607292b6b566015f62c6b9347d71ce205cedcb41eeff61fbbfbd43f457ebf209761ebc1e00dd024a6a37065daf3e06aa5514d470a2b2dbcb363f4eb37b5e351595b7612f043b9f72af906cd725e5884fb73f19c201e625df7468c089242efe7e21ad677e56df6e7385ee0224e2841034398f46f58647cd33e1376a2f3dd665d6f3c20056c8c80a191bd015d21452e4e8c7c2a43decbe279af5c3d321b5b15112aa1b49882f1bb6eb604303ee11cb1c5ba5e45322d89e3b6d463028915bdf4b0155f47d127483af7238abf5ccc77ea30da2bcb5bc0688b4a028465dac7209ef3f59b18ee676930680818db0d00e9bc1f2ef747efa46fb74dc0942ac96f37993b345d1cf0ce981713f4cc0882c313139f32ba8aa0b8c79296d210d3d6b3c4f6b6e65fff1fbe9f50e1f573b4ea1d907377c9b6c87666aebdb9f0e34921dd45b509fd5224243a4ee17a6b500a69dd2b42417d2abd954754c3abb6d24668f99cfbe4ed879265bf8db384bb25bf52c9e010e7714029b9c8abb735130592a33191e8e6f739421113b8f1347d2d64fe558ef084738c390622e8fcee36875436742264c52d6749f0239dbff42c936ab380a136032f94305bf9e5b828231aa82a4ad801e0a56c1837fb3b761168e84db526247b7146e43f3bc39bcb4d956b26e78bbf770d22d8460019579af65d3432eb21e84b2ce4af85ba5ed1d4e3fdce89b7a1495c09665f5f1d8b5fc5e414ded8ddaf5d17ede24c172c633cab581d69613b550bceae86a243b65a2aa2bb5145fa1f2075d23676d84989c0ea7192d28d30e5cfb611addb1dfdcfc38247df832f02a615bffd7e9090054979df5544103d7c009eb2f0c97d36f22afb67f48a3d31b9b19372dbe81791fe6bb51e71ca9a2cf50eb859f8eb5d526fbbf8274c18db9648687b676840916c314cf498609f55db69d86cb07bd428071bd648007a187d04e2fe65c255feffe7723b17fddf02b78087df57864f36d17064976402f61c3cfbb200dc7d504f6087bd5f8742a673a54a7bedb1064543569159491cb8e66cacdadae10a4fe48aa854d3e2a3022ed38b420e42e96bafa616399106c6b0fc1ac83894b45ddb6d88d49a3f576472bc7ae1e887b0b09045ec44c14b4834fd6b865a47a64f71a853c84d1e131414a941ba5ac509cb556df5ee452a46895626506979672a023a8b3e8f718590152a7654274eeb772d68f512d7eff1a80a91399436d0d41e7d94ce31dba3131b9062a13dbf2e5be6987900e99ddde5bafa122aea35c210b77679c876c74cd111675762a84be7631a4fd9661eb0c21a4a0301cdec30275d2929116a2ffa19f50cc1e65610ea94b55114aa973255216f7b83bb7edebf092963a700b832493bb8620257cd16150b4495c90484e6d8536865f2bf3a7c06bf3ff79bf067df0c1e9e6a70bb1301a99a92bfaf9bb8bcea2b20145220cc0afc0f10cce828396e89019cf57071638856a966c84a40e9bbd741a4502b4e88acbffbf39c8e3e3d6c2458016c678fa588aeca103d20719cf82e358890b00c2654358361a021f224c680ecd596cf725e973af931e74db1e8eb73b799c3fcb91faa685a201c6a5336404d71b9afd72d2b74d5be80d6a0c52c00bce4b6d52b412deff49da6fe558563fd9a653b3f443465856d2e7a8721d6745d777ce6f2d6958af6fe91cbd489580905a639d20094916c0f171d72136874e4c3c2244c77bd5ac10cf9dc20ea6f28f818af27451907f0bf02320830ac0ea99aa58da056b01af064940a38f32507f03af3ffd773f84e270deb07a7bc0f22effd3e14fbaf7445abc51191556dae1aa1582a3897b75430bf7df88f98a5577f9cea29117be6a1d4d578768a5e0358c661d52f3b2319024546145937467224d2fbd9a6ab75764560245d533c77374316f89bc7ac32afe312e3bc0e102f7e74db087e70b7ed29110aa0e6f1b3e31ecf8699769baa465aa496ac453828b7912e36769c9ba06fe99e12f2c17990bf55ea20dd6e225aca9eb106b9300c8180461e1168cf025c28dfbe6c5d13d200b4e2aa4b81191c4a801b5783a5a46292bded8b21ede724351570094458e03dee6c9c9bb75539b86d22acd600fcd075ca71b0b6458457a8555b6413cfb2403853e81307577ab586e49842ade485779c6853fed15cc1da2a61d2e5dd2834379efde389ecc9cfd99738893b28bae06105ea9b72d9d59cc5898b91e93cfab75c208897245c7c4ddcfe864b72d39de6a96f082e2177d50f5bedec4a22d44f58517619aa814ea747434893c8a757f645ac97fe79de2085bcc9d0d9aa6fa407072c9a3cea7ca7f863792b42dc5354a7120168c5085d6069b9a1b796e59a76e1b54123654b0291f90d64ed25eafafe81673d35a682a077003904d9e5aa194d3e24649c3990ab32b176dc58381e44505ce42ee792bfddb4be6c093fd42321fd83529d9ad1b37843a7392c3029a4a07f004cf179e7b7b287cd477a5da187c20d971679efa0dd2d02a888146fca9036134eafd9236cb5ff7407e3ef1cecb60a014d5efa4a68fa0b271103d584f258a0b7a8e31f8a5d692d7f91b165a505efd59c2b63e9efb4e46e2b33426c70594b9ed7309d676c96b9ec4ce82113fda531e46852e25343d65342fecfeacace8630935b4a520f340304ad55d54d0a11915563a8705d265f404c5b399ae313be65d41de0e074a1876c7be57cac69139c0cf42855097306fc1e930467cc540ca0ce60b0bcc47d172cc3a48cb72335713399636bf6f7191f53d3bf77c2644ad2da57838256b2f94f609201e1675d1a28550421942e9e7d1bb37646b3cab793b4f10c9342105312ac5f41551b0085c4b460664139b5820041a64502fb14f75a4680e517adfa42ff7ead1de5e4754407c42cc239fbba19f41cd6e7d3ce50235f084d70e0930c03e6fbe4769637cc0996ddc9142a7be9b9077a758057a1bd9fd15ea85598a6306d574e851a003eb124d01f227ef13c6d67392d0b5c14903ee672ead8d6ca558ff0c4e78024c888763afc1f731d74194b7933f06601b91c728801837952e683bb15327e9815b74420a84edf96736c59d47fe5953b82850fea506205627d2a9862e2b9ca52d602893c6c66ab85843ac046f2835bfcf384b15309e1eadd7e40c54db38ebde76736d7a34f4cae109fd1571c488d26c1bc20ff47563241488b9700cf227db5c10585b35c76c3c1f445eeb5c9121aa8ac0bbdcd95f35a0ce8b4f353a92cdc680128de035b614b43a3b2ff2b83a4841e7bf63db986ce1a6d8a664432f238545b160c0b2442fec0a8ab389915c3ecf0606afd76f105300fc505cc00d533634a581e6803154a89b9c1b6ed49be65335b7021b4607fcbd0ee135394600f3ec68cdf530ec3f1f914c74ca5d02770bab283e297583e629040790b7a72b8ad2e5dd3cada93ed5e878baeb05215770fbccb2f250456d8c8a6cd3e54c135f5e16ce08a7407526dacc992fd8ba0b5b7d1a6ead82fadfbfb453bbaac76a14b5c5062436b6a66768589dfa7de18a75bf74f2e672828e0fe166d1a148ceae954063ef922a0c586dfe00da81a82d060437bdf7c1731a8644e552c4b3c7688e13ea891840b7f7b1171cda475d4a6291f4383ac25da4bab0802dab6a8e50ce03d3d91b280a65c6eb141c1cd32e4d625122697914e9e90665f66eb960274903719ffd75992c97b82ceb31a4ee4c0856ff2dceb058175f10743c3d8981e79521b3d1552603c440446311e3170d66fdb271015af6c983a3a6b0c69920e88b388d83789010877aafccbc8c92cb42c6bea2c359e34ba9ac82a1f49163a545bec01f6242361bcb9bafb8c6c5c482ff672d1db032361e8ea805ee1e4d949fe486054f487554fa56aeea972a015bc40eaf33804848889effa72d6117b33ba0947c13e9b06426095264a4e3914fd7764dfcde39aceb7797a0d2374db0b335be381acd50ffd64aa375d20ca56ebc59d3631fff38f0d42c93f2a9f7eaef12c9a1a7dfe11105c7a86b045beae61a9123239c2f725df263d154ed7cdd0be06a8417cef1a9af0b7c745bdae38cbe98dca6d8e0bd546c8de3ca47ab0541b7fee0eea3d0a0c096c8dcf95b6c34e4aceec201d85d25e0d9c460af3a17c9eb0d1807fc2d871c877ac8f556996ba3c80a6d43f8858187acaf866b68a143db8601c3daa06cc3bb14a73617c3c6cf6a6b77c7c152b82b587bb9a03f480b53255ca5dc86201436023d740c13c5988f7766ef155ce320ce0fe77f718f867011162c4c2ee5864ad41dfad3fdbd2c36cf56cd7c468da0f69203a912cee4608f52d9aabaf8b81d5224c5a3786c1c601fad2d7070c45973fa8448957be63bac6f50ba3685521d04891bd225e3abbbe5442cbed448e050b870e1362576ef10108ca0a70b8239f6a637a0462ce1becee6b3a9c800c37c86f8f72a549f70a0575373ea6e01170d4df75a563c1caff18268dd36d50069a13c3225d5b2988a306fb8d7d934a7b4f67f671f39e00f587fac140665b879f005e27a6b6cc6524141377d0c6191e6e534172affffc9976124701a3681a2ce395c7e1ae7aae5c147ca799df11308f16d98c186f6e0b1b59e226ab43d90731882c9a8ce361360e299a0174f7308a51798a244cdf3518329837edee9cd701631e806092a07b2cb87b321ae198250a7e3235103b136d81619e362783364a756a0f8bfb3dd1ede1afbe3be1eca091b71f2c437f1e8a56ced8479d48830488575029c59d72bd9b942be5a88ff2bb9a451bf8e8d4263e4e1c0ba85dfd0591d9ada146f9784c35748a3bd05f7104962012ff6e3aff5f65eea6963640bd6a6ccd0c09fb0c9bcdfd46d88c3cfffa16f09586fb6d6f20256ef4b2d5f88c9d4edf57f26787315e78efd0f5a808913bd3c016d753504e3f793b6ad001f624981f5704f764376d9cca78f1c60408c2b3469ca016eddb2ad279fc8c5af5de0d90c69dc19b412ef43400051f42687dacc64d0367ab8e1dbb5d3f76050ad93f0e1e9262ebd79cec9d219509ac0f21750283e57b8c0942e38af600abe4abf2132ea67c904c610da4fe76d09b104335b6d67bd34ac676335d857362e7c2c564b6e0ccbc7271f92a57372b7c9ca079143663c05500820ce372429fb448ae18d8a1d7c5b60d52418ee9c11072035b779f9158082811d473001bb0e46674dfa88d47f39301818bc15b7e9a9a731ab2d9ab621646e3ec51aa0ca1d1347aa852efb6eb3e67a2e19a9ecd364edbd4a1b6747034c536cec00ca6c90f7182ab8e8c00b719665ddeb231ab2ca31df3e830881c7ce8c1d2da603c6cdf1db1781a9cff36a1d86a798b2250f9095885816c59b7ae419e33543c35ba9533dae441fd109a46d036c88f0f468c0a6c49bea41ec18f320291d3b274dddcda7b170b7712cae036b4845e0c652078f69e7651d9bd97c7fac8fbf3e533ba4a757da34a73bfa7e2fea8f6d950bdee5df158f07174e22a6f3c9392f37275426dab6240b5e06c5671c3edcb50dfaf6246730408b89f4bbfd0971dc8d19c4ab9b7e6a9d4320bca1a77efb05083e29d040fda88883789df0281bdd46506e13c0062528648e0e0293cc40e004a4cf342df98f8d4e8286fec94d5f6bd40f804c2f2b00c687af67e214ce2e4202da16c0cfb6a68e252cd01507737c531613a94be624316b5824b089cfe043aed0c21677d251ecc05105c29d561e6fe6106be4dff004b18c6e3e7b0777a2f25f5c2ae29b1ed3825c2b8bf7ce331f5890fb64f32cf71a94cfed05cf4f76dc985f6f84e1d7d74f29d508e5ed32dcec103bbd6e1e1acf5bc23109a3116a172fe275320ad8d681214fec5ecc5ebdd0a18614e40da19d8f370743c61cb8e8911d008df0915806210942103874b4853faf938b694f09b3cf753d7918dc8efe665955f85ecfb239e0bb0efbf0b83971aca6a21fc7b5850c502b10ace0a8d9afbb069ebf844eee01c7191bae6a68f4a2224fcaf6e23eb58dad1631cc113e2c67b0ea031af5921af81cf61d4af209ccb4c70521ef61fac7a412db67a30b4014dd9da5582ccdaee4460c45eee091d93079ebf993c5efd0e4837b719abb9dde887e95aae9316bf1edf08c626bb3ab2a98e67ffe55b0ca66069460c74bcb37bca42e42d59a233b7e554bb27513e32958ebfb04280b44ead9bbc433eb160da116c30f3693b46969c572a9da5ccde2e3e36f85a5c7a8db98c072a83fb4c2904503ebb21bebab497e1f89aabde79e1db4c7d8e6e5aafe633ba773cfefbf1aa17647b4a625c36bbf0b8ea929754b67d35439b71bb345b871837698b54dc303498f4804601737296d8ff7a5ca884581741bf00b761cc0b171f50280318f6c54e31d8c648b65b07efdf28220659b05761b00edb2b3f734fdacf25888d8e194596b654cda7284b3d57110fec4d5bd05be07c1a45987272677779d7322ee6b107c292f973e786dcb092aeffa9ec191a3fb01df002e3adec76a73e6a6b3243d77a83e37bc8bc20856c1f9658fb710a6e43e417423a5c5db093972a83733a4bd4752abd7c1e4ccc97baf772f9494426aabd2988f060f70226f9ad32a7129f9322f5f03a05ef01a7764811335a4f57c47518dcc69cbfe7041c5075a8e4fed6eb510f0a7d283d1951be4c6bacce8d2927b03dde1d38070d3faa3b26e1cdba50d90dc641a78e78eca96a4979ab6faeb0e1a3837ee9a2a1bc535b0108dc8cc321601946fad79d6b6fa2c0e22aec3aa911bb208e61289c0a1528cc2a3ec824d6d17881e51745d4924ec126b607cb5ddc3ca4d33f7805bcf7f1f369c4f62fe5a7ce0fe53282fea88fc676cecac58c96641e7f22e1eea4aa9aab2a4b97b6a197a2a2370fffc791c5cc2a0b2d33718ddcea4f97c11a490d454145bf162a2117dce86672afe6cca8dc70f961bed2149f694680ec8eaeea075a3c5471dc2f0ff6d5c8fa6c31a074a0c7ca3f07d2ed3cc76fcf22e3175cb5edcd57dcd3d11a763a5d94f9b500c006094a1bf0e7a7f32a728cf9e83283636b19f9dd6419d320c1d18c3cd3066f705adb6e59ab500f0f0d992dd6647ffd76427854f61c85295c61d6f035e12e0c8a041edf77e6b4e1bf8bd58d85785de72568a32c6782de63c489c1e615cfb6feea25a16e9ecd019e9f0c416bc3f205f035ac43eb239f63b87e4394ba8b845746483d05b7744c17c6113090e64619231b7a41ede21d115cd64b8ca036b3765522fd764e55dddf925087013221353af6193e25c4171887334c8b45a86f814651b9986fa2e75d9658052058f6c1324a17fa934194a72964312562a47b4c4b36dd46b373f67f1eee87dbec414deff0707c240b3b71824ef42210ad8bb5fd282817374d07165644e4a24aa3f43d195f0c85445fd951317b265b1a5f3ec013927b5cf44aecaa24060d25a26c795953cd0ba4dce3251d529cc0076eebe6bad17d5efcb7ccd9b21c1ed9efaba2e8ef109b5cdf949aeff3444b2def3dc7eb67452d93582b9160445667703bcd5c8b2155c0b69c6c515581b4f433621eb2f3457b0964514992490de25a7d911bc2f4a534224ec69c019a8065d2ace735febf8c7db14139c890983fe19e81d8300e025e24dd9d2aeabf22bf16a183e71af5e4bdcf2fb5edd38571829d907139755c2abe51a30ce629c271063d622697f4764b7aa88cf2b11a666bef68f03c359ed4e0401bd3a117215c84d74d9fd56cdce188383c1182eacdd0149cdcc56ceee64bf45eb35ad23ed1499eb744755b8fba566312992ada51ff2dc136ef3f5cc666e36f05c8b0ad68d5767073542c63a63441016cfca9d1f821aced831a451961bc27af3bf1b22d60a6dd286adc19005e79ec5f916fb611799394a334795f41e1c27f214ab65e3ca5f8798701003711762592bc55799e1ae4a5ea0f75e9b15e4793f3957315206610e819ed558367fc55d6b6c7db97f7c676302a6425aa305658cec8b4e261f1703e096e1d6563ff049a6c4b2ef33db3779ac0bd0f435013d3c9224c445cecb3c03ccc59ba62cc46599412317ced2c212cf968759d61f339d71de3f35eaad51ba9471a98839d8fbcad2b60253f275e6528e6ec972c54edb22cfcee39cd7722fc6b3021d2e8b588e54850a72a3f620c390985f2d4ff0d171a4b674c28161884778268cb7b93f0bdd600a0a6ae84cb8d7e3c4f730c867900396c23b13249fdcf70f69e4e5043eda095216db6e5f5bbd35ebd6e88c119e320774a9c0baea5cab6d866f822ae4d79f88fb0d4567733820cf7883da7eacba9976844b323e1ff8980d0d62be90f38d3a8bb526436c5190592bf08c4d350d3a54d85069103cd0b28604f083ad047793338e8f852018571fef004a0248288f6076c84933032cacac02a879cdb3010bfc487cc6361352717c2db4b8d542cd67dc761ef3aa4ee88faa46d9bd3813d3ffb9c9ba80716c80f966525cbd60854a8992a04575f075ae0423c36ff8269e4c528bc46dd1b67c73c258431078f7af81602e76f17a045f47e32dca309fd5de35fc7d117d0d617cb84be88c28514a0f816a8d37bbf50ccc0bf7cbe0d34729b8c3740cdb4136a0b4d1b1146b041da8882a23f07dd932385122ca069bde25618ba96e867c7994bdcb774475ea1c84654bd88f07e170a0c3d3589dc4a78f0fb3c49c50c1b600ddad3eb0c90ab0b50fb86d4ff11d6e0bd155de7370eae3f9c853223f30af395e62ff84d6b63829e9a1f782f67764cc8a9429ca3c059e84c64096a7a34a7cffcefe328547c025a9f79b54fc8823b930ef078e21c8f94e0bd38fa98b5bbc14ea71c6c70e152aa6edba4d40add7ee3171072d091ca9b63be6d7b3d015fa55f1fd3ddc32dea2a70cc289f15f63d01089ff56fd4b64e72c33d2a0dc9b0426eee2762efe234ff54d89b1fd7aebc04fd783ef7c0d5d034ba8337cc3b9d251ca6e523afea8e71719cbf2bd85ad0bbc542287200aaedb865babf5e0c0d902081e6df944155baedb24e54d3436fa2fc8608cb66001ff4bfed3633ced17b38d34301206df25f1cc6a8945147eef20e0516f14564a1d9431eb6682eeb2f6fa6fe40c5174beeb90ed3499afd894b1593f2d2d7d05da7fd5e171327f5f492f8c293afd11e4d55cd7f573dc8c621d3c4ef6a0ff81fe0bc7057f3b02239ae521773558560becad8b4af2ea5ffddcaa39bcbbfc4002ecc370d3e05a6705ffe398331ebbff0a251736d848feb922f8e185132cc8998153847a18c9c119787cafda1717b5d3181a9e37b5f837240be2f6e56537d17e776db5fa263bed3b7792b3c5f1a6d510bad1fb3529eee500a51772b6c010d1624c69bfa9d5a9f766f7cc9ea397c3a156f058e2a218b78d1cf49265f11244207632a212705b8984d3afcf355ac33f71aa0b227eca9f86e27daf0e2862f885264aefa63628c187b3ea09525e40b5aa30ee504d6ed2d9ac4013eacda90411f83a4736209166da428ba68a1aa56dfab2cc1aa1493840a1cd69b6a1a51fae0402146b8aa447b52cd0024663f9a7a3082915561846b787af984ecb90c926f968cda8825e2029cb627f699aa87adf871b23fd14d97af1540d776edd03785d7aab90c4914417fb8190288c1d75de49a306a5a4b48e124feb24c75ad0a4494c3278627238926884851c6985815d184b218a32e700bf0374d538d8e4a70ae455552cadbabb450ab7d8db0d2222d559876eb8688f5c4603aa57149ba99c2b1721f96ef91f82ca4dd1b06be0d9144d2b97feaaa5a1f16365861aabc1a9c332721cb4b93fcea6aae849570d5028d36b82a1e9ca24a0ffc20f05e862cc2bfb2899becc43ea3264269bf4cb88f140b8da2296a201057558966d7c039ad296410b5659e1b55b6e33883c6bcc6724f98c106770c50dd6c555cadc4c5b07d2a3a81fbe1ed672a4c2776dc8f9ac45174b5671ad6105d0ee6938cbb2055a62244726d70952e4e2ade2dcdfff0ed176916de34570153ddcd05f762c79ed9d6dfb82391296d91002c34307747300ee38ebc92236f7d4246d96c2bbd18d9bdbe5a76363988fc0b67639199ddc7053e1ef9d338b3f658cfa32b12df363a4ad87d69c51f0c0eedb7c0522bab02bdf2dedb835adbd91f64831a582304bcbabea915b6fdaa16edf708277c1daedc343b4c932989d2e79bb340d1e3d6bc1830da8e03ef29b05e1162666505231af48249bce78a562e12201edd9dbb4062380757f76dd9f8e052165e78c7478033ab7d7d111912861f7477be502b7f839e14c02bd7c3622e09ce8381f3afcd884e26fcfb83f4752e78735813d38ef8fd9a21c0f2a0d6139c2b508ffd20c0c3f5e6526e02f25bf1e0afbc84b15ef174b227023e605b9850af33a9cb2d17a4e040defaeba4ac35ec5f3d363d822f680650ee722cb407d022fbcf524fee25114f218808d617fb676f0bf762f77e253b1ef1a5512edc394d328beebdb972f7cbd24f0c6dcc152eadd18bf56e50ee0f3ac18a95244d06b016b221d128379d41d6cc017b8ddfd195fd1c1dc9f807cb75a9872d0749b85e27d8facc5f461103825d149bfd516b32182a769e16de3673bbd8db2bed7f23dfad6f6459446e6a276531e4a161b1469bae6314bfe248617329ce6c1209bb8f902bd8d903b2278a5cd9f367e441683bb81cad4ea6778240685d79d1b88113240bf0a97172c773703f9158064ea64fe4c2f4292f3d9fd2082779dd282e7f9aec53c882b41b40c4d42e2d100a8f651e6db9820acbbadfd8286bf802b7724978b19dbf92658f46f265cb15cee4175eaa42f364eb5d66a7b2064a26c10aeb381c443a1a717942110c1cc67e0c7533a1d2d5b653ad3930f952e45eb7311acec8aacb8ac6c6eba6a4c9bafa27cc04c270cd7b9678538d5ff2302b3f99aa554fbdd15a4e209983d60e4fd4c6c12c216cd8d770acabc0551a99290b681d11dd58c6efb199091042d28c665366b4ed0ac35dbb3846c310f52f62084d82c55b0a92558893926c90d8b32fc7960b197133dc2f666a9057469db3e968e36802017fb801836fb26131b8626b3a061fc73233cbbac745d45124fbfa36445bb6f3bdd19f8ac595f1a60c20d7c872eca07b86f1fc20d2e4a55ba38d7f296d78ddd6771af10cbcb93d37d35f4af67e33a32043e1dcb1a787997069850e609b74d196da0992076b9e404739ff38af40c94bfb1b88cee9f984c993d11532826113c7344c17f2c593048883830e7bbe8750204c270b00b4dd384f93a406b5d23ce895b7243761f45517b3d46fef80b8143aa0613417c7f71a96e80cec9f7bd71b36fa3c64af1d15b0a9987fa66ca7b443d5f00f39d2ed8000e4c05c47a67c2afccc7379ae6281afb21111f57c47490fa7020fcfcc25710dd40a6b084a7399d7a7354538e7996a6b4126d4668a405834f4e3686ccf45cde00aa9cfb2618ab4a5ad8899ba07d79cd85c79c42b7ed9f3c267c50e49b61c0264b22d762314c73e77de76b5ff649825e026000c5dc68ed967c0e6d652d4c26e7fdb9a3be8c64dd27df6a7c1b55e41bf8dd9254b31640c776474d3b04094357e60901dafb573c9c6da05ee60d54e506e31c785e594ba693cf7c20138546e59e912908752284134dba3e52014dbf9b48fd39800841806dd05cedbefa3ba386276064bb1570e07728b77e9eef470a6766883ee7fecc3822c4c61761db8b373e441a102b7b1c032f667d19168840355ebe1f6ccea31a7c902c8d60c4cb015066301d5af094acc4c04c9d5b75bb52e0b5aba0da9ae5096bd81b5e325df69a7e1b0ca9c827a4902fc4b5da0733872d4d8606df5a2ecfd9d9d19b9f7e351ebf2da402aaa03a1c77cbe16e222894dcd043f730c7b89aa36a8b585eec403b69a4f62d0578bf9b3c0cbb984ca9a697a73a47c205612092b3105f6ab5d5aea6fcc7465591cdb9263dc0330351085c787ba0cf192ae0344783fbf060eba3a7bdbf15a8c68ddac44c6c5c6462e35d8be1ef4cf0ad6dbf85b75eb3581173089744257f98494d9d65b5384b717bbf8e7099a747451dbfba52c60d091f7b3d77ee41b6ddefe1b8d86d5e266ba5842a616a86b94fbc9bb757ecdf0433dc992afe08596d58da5362410974ef0a792b5f434e2d8b00052d8a8ff5f777bcd789a4ed9f619624791093ec9c86d012b09859d3276281991ed5dc5e18bcac27042bc347618daa3b48162dd2482f9a9c225b7fd4eb9a7b7d10dfe3d4270fd32cec23b776d0be60e0d2bd6f2abea0feecfbb8439386d416db73a38cc73fe648a58fdd823e27763b05955575de47d855943863bb314b611465614474834c1c4a7f87903ba6d2ffc5132599c688bafd58ec6f28b8e717b5ea0bb1653dd517c15c00420166ba130fe9e42dde84cbc6744fbaef5977bd17218032b22b0d4807a23ac5bf220925c1a7960c3468fc82383bad9020e22e491974a60869883d3db8473cd5f505b3433e858f30b213d7b77a261526704da34e2cf722e0d0a4952bdcc80c154a580e5a7a027e698015829caca8b824d551d45a082626ae71ca263de9d3927298188509f64d82b998077a587363294f0cba7bc35f12f461707c421a87947d44177356f6a13734bbb10c7e29e6bcfb0c4531f45e8fa293efd48e78fd515335d6960a5f71784460846bd6a30687163dbaaabaa410c1f581af9bf6b3b66ccafde309c4565deffdd24b6b2e6d1f2d5f9d452697b563007cedc5155f5839d6c211690e8f82f0cea22e42a09adf21ea86c6b54895584049e6617b41fca16a97dcc4ec09bfd4abab60d6e65d9403ac8836af7f68ead51456a7605a6d14b438f2bf8a2370cca0df9811a7e1d966256a4b21e90060c3bbefb1a8148fd48e4c160f8c11b58100ac11eee836f94f5ed150c6012ade08da277b3389c53f5f82ac8e24d62576da106b05e4f307bd01f208d83f4319c6a0264250fd61e5a3b58e2df578143d1a9db5f8d4ecb10cd58969975c3025183f4e701773eb7ac113f1b31befe3eb1d98e11d462bbd40ffb76e61860b0f35fd3bbb72ebeda48975df67bb60e2187e13c498c1bf81d736aeaf9e3b14582ab5a6b0e7d511f69f099cc17b1e816b0c8091a51c96b85837cc70716114d7c7dd71af98b2beb54c7157e7a8fc99307073e4ff45c09f2e1c45b6c4ebf9841aee17cc3626ce99c429f1c11e9558ec644caf42063ae5d11b760b7a724e2ab48bbd4cf1418f7edab664ac7436d65682efcbdca79101773271f673900e7eb83e6a3867481b9de99bcbc6aa854134063415821b1e04666dd18e52fbf4360ce420c4418116941a6e8fd6b114544377069ee2c419a89069e18591246af36935c52bdfac49d973757206994ec669fce20b3bface0f25a69964b02aae63648b753d8b296cdd014547d5e8d69aa8cf7179ca81f2d207481e3a47e510cdd9bb7930d5a46a5a17f2f7e7b079b07a85d15e5408576daba48656d013c45dc4cebce0f1dac1b0497d703164cb57ab687399d148dd49dc430942c759565c0b056945ee2a64c97e67e6bc4819b5dc89bb408b59a9765bfe33f3bc6a08f5da75b847579b6d533e4af9ac7f94b7620051e4b3640e095b6d3e282064aae4f97631b2388bfc9181f18274471ae9ee34490921ead08a2023bcee3e67e25f4b115f5bd7d9dcd6475dc8784e44cfb58fbb8ed660e352af9e467568aa7a6de8fa4e6de8c785cef2ee8c6e7a0b36e4eb9bb53b7b7321a037198c4d30129b0c0f638b0d5968983f0e138d1c5b45cc6b39720900f707d59feed717256cc2341cb28edb4b974ebf51fac3fec9edbbd572aa5fd96f73290f8fa39e92216a9cb06629c8d79083a5f2098a74d112c3e4016d9a2430f9529dcd0dc05789b4c517ee166fbf62b9c21f45027245fc452a3fccfca450c7117e23918d012fda738a23407fdec53882a80f03431612bd3ec1e17ae8c87c2268e7cbd18c763cf9b075399da6b9ad494102f5ef8531ecf130e81016467fc79ffd72e7d52a6c18dc275dda76278558b7e780f102138deb0dc90bd66054c7bac7037e2707bb0b972d72786cb67dd7196b30e7275255dd1127bd6d8db72532f13c753182b920b8d346c7723046036cd52dbb316f0f829aaa98595fa86edd1662835fb3294ce1c819c823948604c563241845a31d0786be7957ecfbf0e87059a34fe30a5a7344bd0d8786f5dff2f0685853401ff9dfc6667805794ed40c79394b764298df48011a521f90e346138e9f69749a98a091bd087be9f94a8a26b2a4ec9e5030f4395c0c0808398054e77179db13ffe1744d554fd437d70beb2f51268b91ee315eb7cbf96960a24f1f32d2216da8ae3dc664a929e96cad0ea00aac094da530521a7b12c52aada4ea5c2ae5acf8a3d349d2591547cf8ce2cb313ae0517c8aecab31b2fb4c755611a9f07d9c6b10d4f2799315b0b0ef8d16027b26148479eebe0f348dfeb922cb8a61ff11c03607f98a86202ba4e4f9158ba5561f9bb00759ff55a9bf5590c58b593e3ece55f5ebfed04cd9d128c381c71d72897dd94c4c6ef4f621d7bbd79267fc3332de3f05ce3b3859df76032606cf32fcfb7d68ce6597311fed4064fe87e231227dcc40185f0d7306c5b430dc83462f03b7a847019dd583a4df0c52de23336a980d84dfb2af7ba11c963ee8d9d72031ea9d818d247a2eb83a0c3e8e27856ce324cc6c78b0b3ffd4774872a2150dd301e14e473bb8248acb86904274b1987549b01cc033974353547435b51b747dabf3c348db65cd701caaf62a6b08ec29ea7ff119674acb8cd81bfd186ed1319686158684d484dfe69e628cb05d0c5ec9c1e63c6f98d67e48a296977112e2661188f8a92789170d3361f4f6f15ff930b9573e5549bb9e68a591e13968d68ded6ccba9f498735d142e2ceda1fd5fc8b9b306162dc327c3a26dae9b42f43bb79ccf419e82d0a80c701b709692881f6efa7f00ee5ef4bddf95cfa8c36432ebece689d3f113b5e513dbd8e10263d1b79a98bd5f9a1bfbb0b315e37168d248b1d4cd72b85287f864eb95c903189785c5d704cf3582360934cf29d9d775636ef971fac1545d40338771aed5c77ed2a620a262b58913335da03f148d9ccc07ca94612f8368b183de65c1395339150cf7c69a657afdd20c9f469f06633be92a7f469ebff3edc82f13e70e0dd07a0b394461b8c1991079a52df31430b873db1f5d3701790fed938c0b2888ece2df23a6f44e70e580809178025cd8947a47ad15eeb07772477eccd366c11e043f5922d9bf04fc3450d6aecb4f544cb4442fef217c2f7db6b51857e122ab6e58c39dd369989ede7f0b69440d8a6d0726e2c8328d9896d304c790ddf09aa610b9200d70ce522edabc6123a89327a60bb3e597839fcc2da3b4a6a22155ba1cee8eb6224d4f7862b86c75b3bf518d22ff061cef0f01a5ef68a7b92948a182b59d8fb32ea6e6a1da86eaa1df2c370448e9d93a1dd2bd5593daec6ef0990c06d8e0300ee93d635a0f9d53408f4b806e7e38bee12521d309ab776087cd9a37495194b6d32c3cb94a2ab9c8ea567eb6aff2edd6280ecd1d17793837a3db2da9a9c33d2fbfcd90ebdc08a5199bd0164e058a1de38d45a0ecdbd67d6700962d530cf4baf5792a499b43a2d2c383864624429d08f9a0c1ff18c1cd18bbbf4208cf8280f2083b3c1e897f3e7b8d56a96857865a0637150062bceb9c714dbb7f803cba8f1d7c26e00c4c73abff5c4442b0cefcba549dafe6f0da63d03c09ef20351cbb13c7a0237a5d83111c31b55b1382c4dabe1b10af440c4115ee23f6b3ec1c233b9943220079ef2225046dcab25e1c2836bc3226364e3da6df1f1fcb36316b3a07170a36ccdfea5f3d9c0f3d5e75dfdb32c130645f0118a245ded62cddd065052cfcf806422b7380761620c50010b97bcdc7c626fc0e93ad02dfc4a6c96292655eaff4f90532f2c24b7106b25f2e54c7e23b5dfda345b26766aebeda9ca78c93855c7e20aaccc3af00689bb1f1a427e7df7b4e9b37935c61b17c6d9004d6809fefc011f6c060d7e808c793e9de35fa3039baaea499f8c6048b0166194a1fbe42daa2c4842026bc0585f917f540fce614512aef1ec8983ea0bed647d0db83ecc27a386605ec94bb7627de399262e6fc603c374ccb0bcc2a41b767a9c32a7da16b865f4f10d9c7f611963f6f7e82d88f15290b313ee0e338ff66b722f01117947d1f0fcfd82d74124d1f7ded47f54891f10e6e59f1329d487afdf9c2584918083a7fc7d1c38901ed14df02657ad3d43f8dc38c73d50aa271547e5669af7e07989d6dd8aab1161d903a44da8f50f1cb535b42acb1aec7d429f10bfde9b20a246df8cc3cd97b2149b22e631ddd2336912044bdac180653dbdbd9e166f669fdf93b1c75f1e27d1ba010b67c51154db4c1546189c17b475449a0c69f84afbbc3786a2d5ab3e3fd6c2bb7c7744e35b6587753c4a7907ecbc8064e3349328e1a681497bc8abd956963150b6fb125ebec489b631a4d5f51703ba77ae4bbabf8f1cbf5fc248aba904813f07b1ff7193a893cfac2ad1b11b481400f0a684e518e1901b9e154d87d6949cdfb72700f0503d08b7d10025ace1c5884c3dfbb00d9ccec88c4103a2b8a0b92723ddf39950f699e66c853066486a365b10d51ec75223e92d7ad4681173b1dc79b4967293f3f413cc8521bf01ff8533b001c380a89b8e86f7f47161671b44de5b4ae1c1607246c27b71cb027eadfaad1ff84e47df05588a0fbb98720e9137f1e7cadafbee0d86c30dc175b4135b9a3a005a99a2f1b4b5053489099e7dc81d500a700bfacaf324c0a1bb373490ae3490e289508239ea7074aff54f1b9baec8d55e4b7b2627cfea5dd491243b3d34cd47dc29a711fd01f6da9e0b057c6d33f8f1e50b96a9a76c4079baa2107492c4da4b1f9d3938508345504f21a93bd4b5c4ebf75a55b0ab25f123f9f46e985f7e58490ed05d194d9d084b79c6f4a6e5c4d102964bb13cc576041158364bed5470c03c52cb60e3466c2b06a95157e9be192ad7f46b947c47cfe2aaf60da08a8befa4bd67eddaa9adaf4c8e778cb759349ff302a8d530b32fb1a5dee688fa70168cd2b90a43691403318f495b6397bd794ed5d5e3beda7004313a2986cc07d9305851e09b30a4ef2895058eb0e9ca22e80be677a15e03903818491d66d17aab5c86c207344cb01c679980eba63ff75df7a074351385bcb1b805e393ab9beefa05a746de06cf5dcb1af48cecdaee0f0c18b7071996ed7b613b6d0e850a7ab4b847352d8bacc7a9a00d65be27094f30194c93b0d2528809658412583caac8aef4add0059ff81a10d3f24dafc58739dd06db8a8c3562a9b21854a69a68dee22860fe192be791622f215cb5891ea9933aafeb9015a8400f2bf5c98a203802efb29fd9eeaa70b06ff8b3d7d1c140bde5692da0e375d7d233b02cdbc5b067ea6691dc42ac7a470db5d236d14c2bd227b171de4722db7b912ccb0f66aab8c1a44b61cb255c890ad765c4371b3396cdc86f198e3fd28b0287a2b2f35d4184b40543c871fc63a6255de5a766237d6db62b8d0845baaf9780cc3ddcc77663fada52390fe35be01154407ce2b5b98c64f0306a8ac4a84f7667bdcbbd2bf691931e51155171ee0766fc602d9d5ffdcfbc6e0a6d0b2f5585d80559309cbe1f5eceb4908c5e0530c0ca6628c480072d32259ab078951042053476c84e95bedb0d50fbb3d2c386a7c0cb6e94292dbc28376e8a2809c8302e8d6c417aac250e1f75a06c54b71ec1578147b1147b365efb77e269c370084f633453a29eb9f73b73d76d81ec6a1ce46d5b929572386799e7f7f1087ee10a4c6b7cd0a8aaf2664d367b0bfca7ce9e18c79f372fb3c037d0e97df0d68246da3ca98b3c39282c7a9994d67ee67b89709f7cb396dee1d99e7ba219de9c18eadb9c6911215b55266a7506c1950c76fccb2e41a75f80e595e4d2ab0786f15e579bf817ad3c4a6ee00b2f908270af02571c135d92a6cc59b9db66753fd32819f73da748c5c39a77235977181675e8fd2f9ee5e6d0d67786b48bcc92a448cfb4bb32d04f3f560cccc0fcab7c20ec7b4740a9db580d493a12520127e1176144cdee11427cd944725d2749c4bb88c1e6a021f6306ffcfd9082b652a274903d438e117ca7e0dbeed27adf79483382d8c3fa7a8030e4c965ce9f0a23411e76d80f020d8a97cee04311b8a7f87542b335d7098f26e70873279a3db4f6c05859c9a52c30a8b6501be30a9057249791ca95725ebe971e2c68d50cbcf1a038497a1f2e3006ece13e65c0db13931a939bf0b7eb720ebaa72a04738f019730d274a50d19a2e8f868fd5f7867f27fa384f1b509433789315b5fa54db8741ff86e1001e04a832b5e42a3f88afe7d3cdf692a9264d625f3d18516e96cc6bc050000b08334a46ce491bf38e90ec1900161cbe50ee14ca9558fe161ca327cfc46c3f35ccf0e6cff3ce613af9bfc66bf5aac736874bcb16c0b6972e669e89cd603571e026b6a05d2abd874349c1bf035cb2543eefb6c884e31641a1310adfd1c9f741655ee2e582afbac162fd7f0ab7de71f4e139f453576d54e2aba00333ef7cc1808ed4e593481e6894c75f8e0419462d348f2515c25ff81433c3fffff78bf74fb8d5757287fcb14c02cadcb46510641c549a5d34126eac0074da99deab907af5e2032ef4ea28d82a76134ef3cccf741bc4df7ce60e0e5e1b9179505a47fb439afbf9e2eea04a3a4890858a6e152e11110227184619e00d3fd1c1f3d2915e73b7dbc71a28e4d999974a03657ccd204225be76bf09407ea326f27a80dc8a8a0332a2a10e1da25a59a7caa01e455e8b0c172512b78a4565bb71723a3298f102d0eda9b3938e6ea1a9da0d43572cfdb99edb087c9e3e4e91a32e9fb2f05cf55e996325c66225fb4b234d084d164b3a57c969f294cc25cba81e259d193b9dea1a5a0eb69b19b60dcce42fa63e2b6bf596c07ce8ae596be540571f7574f3ebe464ccdbf4a9019d8f8933293b4bc32ece653297247d3f8469235d33008d10e471cd1f49bc339c923905664300b7d59856c549213ae290b35aca4321546f87ddbab92a63efae8559fbbeb1367fc5dcb3b86612a9639fa5b5e39edd37a0618d94b310406e89bb77a87a311a04ae3b91b8837f69df2a6219a45208ae8223ad5beedd0b671e2a4051e3472b1481d221a34e3475f33c02fa376ab003561b56b0b13103655f2d5203de05e30e5c041b9ab15a96be4a5c6f793e6d7707852ddf36c142d0f622ee751f6703c0a71bbd36892e9d9468818945ec12392c69a99d31b61c654a7f89fde57e8a3055c90c00c88affa4384987929b26c09443ea8e1ca038a878bece09e8d887d9080b4794ce048297c6f203221d1ff699fae68e68ce4da87460142bb85f3c3bd08c23309b5e3b5b165233c54aa5b574c3cf1ce1b16c9ede0e236fc2b1495a450caa2acc1d4620af5a1b07692498df450f9dfa36a64307d2743c5698a0c3a78143ce0be0fea427a37c0a82af743233dc1ef8290b31543bfe1efe8af43364e168ccc826c49e00cc6bdba1976e7951314be087fda3bc769c75b01c3f9c753d43eb49b416d025c23346edb31ed7882a80075e52a0506ab684bc2fd437abe907ed3fea3df2890cf544971afd75211b8c48242469a424e299016a3a728b85348feb9e28bee1972e8321fd598f764323a5ee3db28b7ece06d90ba08bd5c72ca9c804ec7fcb4ac1c6bd3bb0143ac007889e97023900f906cadd05177694d3818e08b600b40bc34a02d138345f415754f601d75c1b5ad74e500b709adfd94677d72b455e36a74c7625a5720d77d396a02eebaba8065bfa57e0b286293622e0d50c4b550e32b2f683fd806958618ed9e6dfafa2999e20aec8c0a49d3a7479b2ea153c81343bee58ac66224a57f81a036ea4eead46e7fbd9b480af637ac312bdd2ad1cd0937092efdadbf5f31ad44cf14d907fb60e3cceec5d14aefc42a7020c0f6c0f2f41e1c2bf210742b4e1664c3032b63fc2551499ec7de04d9b214de0443fd6e14d9651b4057494a16ff0b17644552ca35cd0bbea2e694112c50f011e91359783c27898aed31694a9340f49060b60d8174964c62a7e7251e109a7bac249e11a31f98a7f5a196629ee403038b80298d789858a4e8c435072d6dc79244b5435342dc0a718634900b7c2ba7cdb44305e8a005fc4e41185217109ed3a0fe4414ff91d73a58820762964cc58e01c7b16d34b391922eea680969ceb05577c48471e88d9b5fea22f421e7ed7139f138f62bba538dcdab6834b8e8857bd1d71dfa0890c73a76e6313657cbda9a87c3706739e4bb532edcb277aa6779e214fae8a2d6ca75164e210fbdf21216b160aae3a4a66fce8d69f82ca9750f7c06a55f4593e353dbff8ca140e189ea6ca3f31275dc5d7f59633657320e60fb4e29e3cd99ea6cfb80091cae9285a4f7644d0788a4bb870ce10d42d1c6ba3b06add8d5461085f379d7057d6fd51cf32ab8505bdbe6280e1262163cc1e308cb09f2706c563d9c8ca22994c08f276472ac4417c877e5b4ceadf002ded60bf26aed2d840a030d0cab904be4abe98992673b481a87a5081ddbcea8be408702190f2172f79c8f1df5163314d6ed2c40116925d52787470850d53485a429492bbda66882ce34e00d078b1c99d0d4d02f092af514ff48acc48e003e8571a9e7443741cdd0d21b4e680ac26a753db9f4f25b2172e22f86e65c00de0b91d57e772f1159d3f60fe2161b38482d08f40caaacf52fa0916eaa6f13dfc433331285c9667221865461752fbb523d5de30a75e4866001ee29af227a5976d7968a7bb2ec9eb6c9cb591afd05b52eb005b427fc5748cf98679fe30748c117a055b4df4ca591e8000626a5856a1a2ad00b2ab9bb6e4039f16cdbe9e1281c69ec73f85b1e075d9514f90fc9c164160758c440829727604392842be1db7bc719fc5d1b34784c7a408becbbfd8101832a9ab314d942d35dde56f98afb8e54eae3bae907242f66552d3da82a1710a53e27c1251f46ac44c8c4acc2663dd0cbf56ba33a705971c8321a43fbad07c8c950f5765e2b8069bc5534cf4f252abb37d15e98fd901c82000f080f46f6ce2aa043296299e04dbef6da501496e83e0926722dc4458d3169b5aaaf00703e84ec99f698dd165ff07e644870044c0af4fd2d988b842075966bc14148350874146d075e76073b3442bd477e74c286d79061205c7668da52feb339d47a331fabdb5b8aaeeb0e85655f28697a052e699c51df596a0ef7fb0da54b3d0e43359ce4785954f3b9b009183837b9cce775ac1c4c5311953f81357413bce0d76879fa0c468c8a622d181da4461c9853a002c252e599441bbc045424ec8a98b8577e765fe8c87577b3cf42af5d1db5cfc4e6fdaaabde7d4e4f52b7a15c9d9dd125f1566c6c8a7cd7264597e6690a21f176518dcd4c1c0eaa90f15cebc065b290438de01fba71e6a4bcf62f3b651b4a4252741b17ddfcd7757cdd4788ce90e9fbc09879d4bf6435b6818b915f50759f5d2d1b5d102600215b190c40c6dbf26b70d2ad95e7273dc397b091413cb91cc79d3e6d3c99fdd06f13726e03fcfa9271f76331e4abdd3e0f972b51f04d87e87145e945cf3e4b3994fc72173e89a9af42b14f717d9eb727c525cf41a0e0b3dd70ad9acbf1235813c5e452cc97ea09eb763c20021452150258356bf6920c42b65c295e6de4e5a311bed4f4c9bfdedb8b125cfad0ad4cd9e53c9b2cadb4fe3edeadb08158b49821c7edb70dc138169d7f01715239a5cb9d821159c5551526f8bbd873af240d55878516839f0a20cc924912e06bd19efc319044dd690523af2f2e4dc16bcb1142168fda488da43ca9914beab340b13a5848c6916060fdb77803d0d391d1484a3226b111aa703215c4438b4d3a79736da8910e43d02a6c3781fe94fb813adaf6a09286aa79748a82d08f614c402056bdf71d77d8ca0c24315729a407f326c433ed6b830f8b48063f1c7ff1f5e411315957b93cfff3cd52f55d3328748045b2c9068f116d9d3ef8ce6e133ce86fa3b215fbe6265b545a5f1fb924efe335da16bc34b7ccfa8c5616bccf506636da3f0f07a170d0e7af4c62aba2007f0a8aae166e0c1dab14230d2eccc601d35ecce958138485b390bee6fd9d9147888e15b8f8e0d0c1112f78ff5799072004c7f17c1d82f02ac2738b6ff7814e01590adc44680a8ce38e2cb07a3fb7bfd63de4de4fabef00f75a6d371fd88f24e0e254e87bd5a102dce3cdba1697da1487b37cd8080112e7ae0c22231190be88c996b7fba16a2232ef86924149ffa04a9ae6281bdd2839a92e3d8300679e94698c8b0914383b391b4d52fcc41d3196517f0b0d6baa84027ac13a5f63b3fc25800c97788fef1d237dbecafccc85e09b972ec41f6895ef536fe412c4ce758127574e20083d8f4b78059a6e8d66a36f07bbc047db7c125cee9b5f4ccb649e40dee6660188db9d6654275ba0113ba6d3201bcebfe2b9dd721f6555a0fd0c5d1000374ae2fefafbd293de7e3917b73b44df518c984c1059660ecaa1bd2ec0c660adbeb3dacc475ac6f0deedc0402a1278f4a5009739d028717fac6458ac0710c04770ad399fdeff3fea4e6c08ace59f8a0b3b7308c4d7b9d66881c4f8be2ee521ca6a5bb47793fa6a69c8e15180801af9b7d8e80eefa92752c3732ae82464f194eb1806ff0fff66da1ed875fc01a7c88d0be6b49da93c9fe45ca662e55302a5bd8b6be86d3ac3182f7235a33a4e4dbc932e34e8f48c0cdc37062dcfef36e0c302b7cec88625cd0eb2b270a2e1ed92f80e75ead0ebce901d50e60b3c36819b240fff97da9b8faf45d663145500ff179dbd85824f400682b60faa6d2429fc240f605bdfa0d502628cb63e07cd0a67a1c625f772dee2f0c96823da22e50a12f2e8cfe378793aeaa216ecfa353de01e30436370f9eb3960ee029ede19741b3fa5242ec4049efba96c288c4b32b24d1e6c179a018cbdd6e748f06039d95359ab91aedce0e58d9379d071758b7086611e13a54a3caf6b284d6bc96ee38807bb2dee819d36040aed1e86b8014d67f11f08b110893186383e48d19b63e4a1c375d915b005e9ee77c190e46e71dd377483e6462aaf5ffac9dfbacdbd89617e95e81248a6e5ccc76332b0a06729f460bacd2454a74ca2c9a3fe440a6bee708f2b768e9658ba66690e10d0b800b1c0fd08bf3497cd0598b56e597b54cf566a5e63429d1e00ad96c6979648ec215a38262d2c4c6a28b1a03aafe582456f9c6fcaa9fb61250da0e372079c3e97a23f152060af0aee0e8a34ce048eafd954f8fdaef7891e9f9b977889dcb430a7a3861b7cefb056231e9e34891918bf3f1ebcb97b60f6c637fa11972b528224a7a84dfc1bc3ce7f19f73112e8b85a3031e1fb782d1709307ca182deb8a09a49951f0e24aafa75da75e8e282371e0e631b7aaf6515b368ef8621d1522d3f52b8b6175a384934db47ea5c0ed049df5db3e62a177ac37d6e572606114a388cf2b62f7456f29693f2349285ae42f22c343c173edb1d555a77c065ea57f8598bf88f3dc40f55ffc11322159f25c24762ae58a25ba38977b27e4018fc4cb97d77111e72755ed7257dce66273c5fd98770c9877fe8b45fb734cb1e0b772bf7535ff784faba594ee78789f0f5d11c5a0ebc658d2020c8bcbff1d1c062af2d5bf2579b640be1735322dc2dab9cdd950a4b73c226e8a179a8e248bbd9ba4be8f9f4f33ca38db4ed8c4d192cf7074eca1f15827ffd15d17a05bb02fdbc35ba0904cf147e8238e6bf609e59bb1c298a18651134f5833f3f9af793874c3396d01de3908dc17a62fb1009f091676268e55e788d0d7e6f8a96675c04f6ec1faf00c0684f76bb1d4c83f9325b5442a1fd777aab8ce5e8aa57f4680316ec47a61631771ee7aa872d002b00fa29a2409e038a22f430c180b7c61dde3bb964183e64658b59edfcd65c8b4bcbf05fd98f6649cca26165773ff0ae96444219d4d932741efe38b40e336112accca18d24c1c4d2fc5e27f73b177655da545cfa9b74b9a5a7a60d1df66cfa3f6ecafbd14b57537c46ee461d34d30b1ff3bfa67ba5d90bdac1b26a89845cda9c3094bc69e37620aa5ab5b79858a56033d462c9ad3ec9798a231a198be66de611a1c7dcc79a21b5ee9622d0b2bd6628aaafd9f0939245c0f22e8db9cf0c1e9b87f8342e864302fe5435c18095dfe343333bb6575d9d8335de3380e9f463da6f3082c3bf962e8e09a97159bbc76e69ddd94feb261b22b960885f8cb19985dcfd53663a7fd3e2b458689b81dd32e5e3f22adf60046fca248d24eecefdac3a3930cd76fd04db592a8cb35f9e3e6341885b74babb7a406f3bf97e1a37d4c5feec7bf015dadf9fc788f9af39775499f87c5179f93dedd9df2b2cd3a917907bbdb306041f72029a9b5c23973e7618aa6300f98848309c79897a2c57016d4f2985cdea8bcf912883c5ef5b89db27fe8c5efb6eadef06129357c7cffa56c18517da52c36a23f064f5cfcc0fe59d21bcd476af86f43e03adee0bfa60f3ed6141642f31f1e101996bafc99f9cf9a7f013fa8e607339c8fd1689440b061c8a101fd04f0150ab6aa7c4ee0b1f9039b9919ccfe110b25f0d5f9c70f62312988d0c58a92747cd84c8d5c3748e4593f0547edb141a62c988f9bf4bfcf0103eac4a14f3b136a536781d73d7815f5e352b78ba9ac76465daaa577688b7ddf70eefd7d8e557bf5e8b8df7a973a18a2f503b3fae94326cec6b042bceabcc305fc6145d6afd59adf02721fead2a95f524a1eeabb0c94faeffe35ea4bcf7a13994ce92bdef7a547ffa915c3dcf43d1932924c3d2e5662e92fb440a1c69cb5657bd88cc3ca32acb861fc7189e364d78f7fbec1f977e5e93232c03783f658d0c2e56cd38fd1f4cf064e7abcdb9dcc5177ad1e260a42e9cf4fe13492c4bf4f7dce62febed15dc67a8ea971407d236042e0770c177bfba5dc8df3366b643a0826e80a60e241a6bc876117ca53b2ed6dfe8227fc4c6ae7d149eee82611046e10743f8a7c894621eedbe986ca1470b265d9430e4187fce5e4af086b7abeff2d1b0db7df703769776cfe16f90e7bd08f7c0cf0db148e532f1e4e21edfc4aeb7e90d03399bde9ad3ed470f8285533e7252fe79afced2914de96df568f01f2bcc3f440d258d7da53b1f729a47088da9ff1a5d97daba6526523fb6fc3da71b0fdcf2c3a5f933e404e60f1294da8ba52f0cf82db042376a71e55f1009d414258f663f3bcef18691d9a5f0ec607fd56fec814a55cc466754436d059f4c8a3aff605592606510bcc83ca3c67273d58babadd226b7da6c2ff34a730e77ad80011fe977373ed7f11d0bcf36fe73fd1219325f01cbeba900e9e7f8dea516a0cb4e762eec91cee36015ae35fa5164b6ac6ad2e9b56ff8b52f0ecd30b5217955f6470e669bdcf530bfcf953c606969431fcf6825d550c49c6927497f122ff0cb61acef46e7756f46e226a1a698ea7845925ffe4200ca8b4272cad857dde776ba84015fdbfb9f0370e1b0d5446cffbc5df93919396125564cd16332ad64565721e0ac67d223c0900cd67b61199a656946a0270c4c0620129b537a6334e91f5ddf1c6276d2bfaa7342b1ab796eed4d7d6c5c18ea05d45c3944b803dad3400e5401d8374487270edc3ea4222f574b02a9deeb29a6e178eb230698d2cbfae5824943575d0767625d6caae45d133317c5fd94f9f92fd881cf0b82490a72e314a9b6f31c96a9b9496ab8882780d47071f5c671cb97d2f9221b01f1681429eb70f06282d3f7e34ca06b64b7369172e77d83c0e03057c63228b70010074497ea78fbe74a8c1a922f8d2cc17e36bf6e216d355f2dc3796ab40a2ba4302b26fe0a23cc8ddf78aa27ccfbdba9356abdc9f5fb494c11718c0932c65eafda926e107640ce1dbba8a12a40bab185b05c2c5853cd1017d3984020c1db9d614dd4dc7d160ad8a484d7362c86900272d949ff550812d80e329daf8e57d7a653612b84c8be59972175beb84d6b21a8a4288dcfbcd81e03c0e192cc021ed440cdc301aa6f8b5caf2622d3cec3928c122c789f5ee1041c580acae24c485a16175a6ad1b2b5d2d6e1783578a6815645c88aa0c91d5dd218ac268b4bea0a56d562f961000344954b5c93b56d33ab9a2a8e3617118146fcf82013f34c4cd1119d3999deb28bee6422c04cd2fd6145839232a85d3ebaae4e3710017840a78ba582c132258fa4bbb27bd6ef6e207f7b738438176e62fbe746b66918c163377de5c171f4fd7537c70263244440e0c17cb3016cd3e9a222d439b3a1251acc8b3e25dd6a2d85d1ed7122ca9dac882c1172bcd02e8d694e3877472a94a0b5bf6c3d6dfbc882866287a6c61b154e99e960750bb0e37862c72e9b9a262c21ca1567bbf55c65fd0f9ecd4cbd6e28b422d5c46c27e17947a4aabe4755a289abe8ce720ea554cc256e1aec55fb03406948e6650d2d3842fe476205562590179377b73194f1d80fbc8c3f7afd2d056b05ee83dbe0cba746d116d90c5708029b9dfc2c20692606e90ee87f83d4fce2621648c0fb9a587836af1a2c6b422e26f12323552214e4ebc023ac474dd555d0ed02e913f373787b2ca03933146a91a93c130baa6156aa96bb373415a09ef16d4d6f9583eb7ac7c460e6ca1c28ba40dd8953439e012157b92c5f1b2e305e860038f8ee0208a62091b85a9fe5ad088e7530249bd04e293a548613489953f2461c132f9a12ab8261bc4535d6e7f0e150c5cd3067909b996d770985ee8808147952ac6776a46240a83f3cba900086904dc7bcfe46c5eefb8b8e8fcd914ed27b410274e82819e109759ef8a366cef94ea95ff58adfc82a2299e382e13b1415e2d4c7c395d683b5b39f245a508ce51c31057b11f9647c3e301490f10fdeb909c1c00ee162bf057ff419e60d8a0f80b359e383f8f419a57b60e219703195b20a8a6a9264edcbeac280a5b9ca87ef02723d0403d8e761faa3f0b19e9399130d347659e99d11ba0e0ff0edc9c8b7be5c983982af3dab4f6599962489a1e2f82b940a02b64f3b2cd34c51541d2372ab6b057f95490c701e07cb46733b7b71223a16ee65a24a1257f0ddd5934d0e862804698bc35c896b108ffb481dd2009ebc9db0d111aa4e9c4652e3f33d6fc967d14a0f0d4e3b7e6306884446319449019c457a6ec83abb4a7d197fe96b3b9549910799dca51362636b30469114f2db38895c3ae7dbce69410a18beda545e0a92d731f636e04abddd971d4082e48610b314cf213cf9703876fb27eb0c3ab3f70c101e0c06e8835ffce03b0190577e13ac8c011d1c9a4d31623400d3df54400a24c740a974f7d25082c1eb62099b611c4c47a7fb11c5dc94fb5ac99f5d1adc3bc6858baba74f3800a2b66f5594b9176c2affe078d4ff281baf3b4ccfffaa19839e963c770bce10b62a7aecdd2a42c3256415279c73d5062005ab5fe834e4103eb511a0f3a013c7072a6930b3252884f37aa4079d39eac52b6366d94a3b03d4567e6a47edc2f8438b2563981bea903b323c0c60c3aa4242eb7755f5a39c26dcf05317414100c57a07ac6f3d4de0d6ecd7e6cfd9cd7f2bdee68ffc4ca360fcdd236425526586b9495525fa22b34decb95b18be4b0dc272f3dfc2114bec9b9580d0b0846e57d6a4e122f00cdbe95e8feeb9030b639a48521418bda43de92b2dea0a9b5108c7df43c893727d3885160e9652da9d8bd1cda7731abf4b7675d24a624419ea82f2f98040a5f374f98648fdd6205793f77d843c744928c317921556a4ba41e897d0a7348a991b15df0f14753ea6f0ecc37ce69ce61f44ef4924b2503a0519842a366f4ac81a79e1a97c92043bdfa7403b9cee1e7e6922e53da2e60e02c6078128278806a5dc338dc200652c05094973cb152d83ffb46ac6e1ad6cebf879e28eb6b6afb13ad359581e6e7dd051c1a3b4606aa078a75e670a17ad6c956400fc55202c0e2fc29f6715fdc31524d57c0f34ed7c75ec4952aad65e75e53905bcf7e1e14360868248273ffe9b15060a5b1a977271fc80b1f4e66b5ca4852c9b8f300b7faba05a19a06b3fbdbb0cb91b79e5b7801908e98f042f0b26425f2c23541e0ce7197d11cfb90cee99dbbe45cfcb20f5f8659602d3bd7487276661a754870f746a40658bf1f00535059dbfc810eeb8f8ba6580751891f4f13fa5f7a9a2037e67c602e8986fb149e5555d1db66c0ba72d078a0bdb69a0ee32d5e4eedc8148fc8bbeab4469c04bff49c5f746632191a55ea485ee6d5c9226156c647276022e6d6963e8b03f225315cfdb4f09ec54e976a148c7e328a4328d84886df869b94298b02d21e1564c49ed8076bc1736576f317085040a4c2d4481276f187e980d4f3ca7484425087f5836ec12f34ce98e107ce0923206204a0cf6c51b43bfebd0776519c886fb21c1297637c7222dcf47a44c80d618ed7e144b44d49c7d0b5f9058cb50b37de66621fa34da89121957abb6d984e013a73edc8e971d7664cb9818e614dbe29dd692a271754f6e191a83a142099718df86db7cbfb7b41f49a62c76e0c034c0506efc9d5cc932272a2c9b59cfc803b673e8da9d3501063925886b4b56adf6511607ece176eff08b47d262965b70edf97822ebdb875839a1d100362d0103a64391c812f078199bdb3107470b533c014b85426ad548c4159c51e84dbc572f89a4eb1c6ec226901638a1848aeeb28027f9ada36685846e91efa773447403acd48a00d66b053971cdbd87380b4eca5125b25a8af2758d6951b74a35b12f677ba3e6b6b6d5c56c4a88a98426d1b9eb0da88bc057f9502562dc4fa4fe73685ef318ff32c36673fa4919f965fc3e0699216d18d60176c5a9c7310a6063cd4ea8abfd57944bf1ab3d8ba865f69fff105f6c97cd7e8c11706aca015fccbfd1327969be2aeea177a469c19844d2ac92051d0c99063de06c19c2ca7b0aca1a9edb86a3595db02f65b962807c3829e80460d72953ff07343652cb5c549040bb019e2c1aeb4d597acd6b971f15b6818abf35cdb5f2553b998491205742cddc6d47a387221418b47fbc7e1cefe919a39f8d554a058b08ee29f175a095212f2d41204b1b5acfa5de1677c6af70d6193f830e10d438fc0fb31541101244f50b38a584d30584ed4d406c7f70759faeb81377245495f019a37e2da5d89f60f8ddb537d3661a49f7b4d2cbb29a1bac547504dbc8dca5e3ff3c69de044f2f6c79a9d4106d9a46077da4e36260bb42686e84fece4e7b6a4766eccd21b03f2309f24193184bed4c6e390a8abd8a70d5417970c9290e2251a40e8979ed02034fa657817c9e3f1563fe98ecd83a6755e027204f5eb02b8b7ba9e42c132594b8f77a2dee9061001770364684238834f8543b8c1daa4a03f0022cdb508a5a217dff39afa2cc7d48cc3b97f76774bb2d6bf9c432776a5acae2d01c58b699cb023bd29851e43d0166d6d97ab030ade74aa9b73e1734909221fb24c55b957ce62971252c2130deca751e402031794ec5fdf160496c685c1baeec70769b1396122c46e1b4f7c9590831df02dc264052644eca7d0403b3aa896fbed9f5f0bdb316521732ca00b3fdee2dd26ab2b9bb9fb993035ddd2eef59ac864505cf88f8c4e4a4ca5b3b279210a75f96789f48f5ed6a1f5a403e7a749bfdfec456135435dc9eb9f638a61033641560d3e14e50c03bd55a722cfb595cbc73752bf2fb0e3943709e622b649e1bf32667633b2edf77650f7c6729a5537f03ccd3607ee9d379a6999c9b133ab0699095e1c07349abbfb0556a354ad783134aaa5ab7f2e36332385d56ff1e11c45ace283d8e58666f4c335c9530b96863dafe5ad75e5bbdcc2d681536b888b87795afb11325ae6d8c6a2ebf7de806161fe42f3a7afcc289dc9777ef9e4f8e2dc2905b6ef8358e47f85963c6746ffc57c342f26d33544d073a6e7cce35f6fed121414fc54440b02dd9c6885363df63d948a479b264a2410041b06cd44255ba6a53f6a0b427bf7f2a04ed6dcbb5901b4e3f52e9167aa193c0a446e64cfaff9d639da55d4f433245929b1c5482f05c7d9cd6bb0b049a46583c42e2a7ab1d5604420588b921d50d38bc5b2e8a3b920969e2b4e5371e09848a9e4927b5dfcd5b988de333ce4a618f6384e5b24e874b9e238278c6202c2fc47d25e4871beb84b6d308e0871c7d67118692e72fcc9671b2f7187701d44643d3dcb40f257f650dd2945f8f56fd5960b71b206d1308d66d39987da9ec6f07c36226bd01b061378a574f728ed7ca964ed385eb8c99d35cf20781cb88e5f20c91969e2f8e5bab707b0a21aa21691121f6a69e54249ed5398bfa51e4551ceb6745acce9ec984132b825c085018f072a6790a0c956d6167423710950b3e338e23773a3f2d4a93d458034323789d22f9ab954480b927254e715ee3dcd0f403b116aa31bf4d07ab5b29dda079acb2ffa7713e356eadd4709f672eb37a043a21aa7a204fbc77e6dc7d653878eb1ad34ee2bdccf38c221f5e7be14dd27424bd6a59aba34c8dd4fef108e477d88cbdac83ec55715d02eb5acd6a98d2a6235e54b33cb8bd2bdccf86cc156bfe949186423da9d9455b46eefa17285ba1f00e42e6cab008107f130ccef64322b89b0d57d3e51b856d6cb29936f4621ab1e39476cc690710b10d71771c47bea224ebfb6794c88c50e11fdb9742f634b26d0a94335678d2bdadacf6b54249d0bc12b4afb542ced06e8d658d1cda2c362895b4a37c5ae6e181d888dde24ba2b967613746b2ce966d16db1c4a51ec294cac8a8f544e1da58834731a4aa910c60ffc53b2dde83edcc0f214845c805fb2875fd4aefedab573af7d0b45c9d22072dcb508784072552ee9608bb4b425194e7209713c992d4249f73b5e947e32e139dcbbe4f8270e36b5a4f470db0e67e48d79bbc64ed26abb4890d785268ec800784e5004c6d95688214da12a315be1654a9f51a0f6360062a35097d9509bd1f1d5664a59930f9d126cf95571b44a89e00c664305ada760ac26461c3e8db02f58da859013f52c07c062a26b847b67c5ce669e91a0e80161403ffc0da2e1a522e6bb03fc68a60ed5ba1e2784340f90fa43e78132748d509b3a823571369d74dad1a52d8173e93f18e4d3943fe434a1475d0a230fc9a85f42ff41b7a80aea29bd059e81adae8a2964aa5ae525aa928b596a4280bdd864ea0abe82634ab4b2b354aa9a552e98aa2a10b742b9ad46595da4a224a459bba10e79c42477095306f7e1ef7cfcb2bff5e291717a211d0d8fd9a27bff2a438a4e9b83f8b705a128bb4591445300bf6768ecf27a7bd29422f85ba50a7247695525f2b90c86114b2ce08273166bf6d77143dda681488412970748cadd81c9a0a72b2e1abb9d58a1edbef9d3a3a355be466880a851c1873186b42d9719de885d29f4c3eeef75e725f559ff1e5064d19e7c35081d9bd02e5055b4c54fea1125aaab3045c6828850a202b9be4f07ca845e6545d65deebf39c11b459d6169380e016005dba69671957b8d6a5fb08bd4d1358a5576f301c0915ac3cb12a658760b74f112f9b1abba40d598472bc70541b1a18c65ca2c186c1968b94e385e1f5c05854ed767d0a8311e33bb62b5848d762a9e197624a7f919f2ef09c79f321ceab20205df491499b43f287afe641d233efdbf9e31c6c3ed034e14084ce2ba35e16ce075d3dfca146bb8850822585e6bda886c1eaf24b08de2758791f9d5707d5c711cc83a068c8a1020306709aadbbe5f66ec8317d08aa2c401ce030f4e5dd101878093bf57f847e2d296dc27e832ea93a1be75e1daa553aa4843a081b217e8e2b13c2f920037e7780a6da5d9e8e771c674a3a66cfce263a5f891302be59d60280a5c2d8e92c6bfd03ebacb091aa043e20536cc5493ea67f8b59094b8fde8981bbd45369835a56726a902d83af9603fc4de2cf19eae53f3642689b7e7220cb82f13a6afcc6038e038bf8542967765bb5f20f5e51a9a280975b0d89569f4c639b1bcec1083d739d0a0907ee25945982b51786d5b272b630ab65caa3222ba04192ac89446dd1ed7e440e01e92e068020d7aa59af47bc9c93ee26da6ebd1adb642dce0ab29ce9a52804c3fd89a0a141d2b2269b9e91a1e8ddb9415e99afca280a8e7e732af1ffad456e6c2ebd45f9af6669f6d0a0961246293e1491fb6bb9913d997d0342a713abfe86c9051382eddfdb6cc2b81cfaa6ac86ec98b796a57cf28993290c1a7a4e155fcd454c6b53294e154f0e6d0cb4dca7843fd9a61e5e0063871e47fa6e7c1cea8d5c83aa52325252f470c7d84e07262b046d2d97dc5ef2ef874a815a763703a627c6e00a96daee982174f6c515c28ece7828c528d498996816af9760556395a213fce64981eb15fdebcc2647659279e1f0cc9488393c3f4c0b3831b26d002dd005fb345b1ab79de85fdd79a6a9b191f3583f4dd5352965f617cc2cbbe39b6fd5405a044f7eb5a09ed65ddbbdf41f37bcf81a13b305ea1ab461a2018fc062bf310980f4609887fb46310b1713ad0f8b1d2380cdb129fcf96c13e2677a4e31724b60480375ef6818fbec2254545d6ec42a8f58293b484bf385a4cface27ccb4597f2e25b86cfeb1c20f22571c2e63e768d6fc505948c924ed631ba3451408189d092110d2283dbde5005ee4b2b6936f195f53d98c414b9723cbcb63d42688256503448488b038dc2edbf51e5d456ceb539f1b2ad507c23459ac9d6a469e39b965a465f2e86c435af6fd32172e662e579b072ff002fc32095b207646a27743dd22bc89ca6f3f2e53d5341bac2c815397e5b442edd5c231609d85dc9c151eee5399f2cd974a73181ad34c8764d90eed5382fc1dc58370d57effc630a301e39de2ee4cc0ed26451c17741c69879f6871b9dae4aba1f72c6f5a279e91d1b3e70dad9eab2111df1ae454f72cf0a5adb3991bdea66384c2d846ba3a2c31c87ed3507676cbe22fd7cdd041e427d902a4583ccc8406ed34d93e990e9b15a1cbc0c256c6dc3e166393cb47417eb88c2539d557722668c96893292113b9e08b71c0109d4f4ea5649759a7dc0378e0ec44addc948c04b395d740b6c2d1eab4b4fde135161f30aa6e762894b3d4d53a4044551cb41838e51296539fd0e697123370cadc6d832a785412f3e35072d8c9334fb57de398d3ee0569f58275286026f9993fe004fc70bac486cf1b2697a9a314f440361fc298eeea6506d8f639a9cb369573deab1336f31b0f8f7f24b49faa2fa3c9210d8e843ac39b7eced03726dceaf7a350f5d5a7973ee723852c72e2fa35db66929ea92534a975cb45991974d32c228e228a58e9ce451b9f57e9cb51ac66e943294626cc90059ce093bbb3a20705a49621ba7f455b241e27ab9289f08293fa172e06cd56aae43c75a8c2733875f1418e4f2b1498c9d26ff16e23ecc93540c700daf077737044b06b19e591e34dfa643991030d9f95f3beecd2be18e644b8255b1aff366b49792b68e4b465d7424801c974d321183b3038834e37b583f854dde9773c65258b69a0bcce989e44a3ecfad9eb52eb702b38632e873b77b56cf95a2d191680261aa9156b2fbcdb2c715f5fa065705feba0257bdaf8603dce4c646ed5350668c6bc68ccf1123abea00940702ef9046170015b00a70b68f00849e1ecc15b8f394e06528d1e92c402f6a5bbb4c1351cd88c2350d95ad31a7408fe7d58f991705668ccdfcd1a27c692bbf23c64568b129061325f1aae0a8ef7e92ef7f53fc54822bd68bc3bdf29d33426c7337e85765a4ce3246cec445b9b36f13f6b26fe77f00d88f5d6f87ef3290fe1cbd17d6ef5086450f7e4ed673375cc4f1af5427fd2dba82e31d9fb748a84c4bd7f455ae37e4f9417556acd1ac739228711546258a2b31cdb65dafb24672c9ce6c991757f9f66750e529a1833c3b6bb2c40b6a34411ddf7527940e4a5f32a645edeaead5e842a8ed58f40eff4ed33dc485aa8981970fe0c61619c99bcbf3abe750c88b10463a159482a8a531a81b0a3b254a464d5172b6148121afa6b06f1ef7db4bc173932c7946f7d42931d8f2b7eff691f852e1385c45e4c248da71cc29d1ed4ea7e4ffa8ee414c671d67b0db054db794ca98338d59bd38258b38fdf3029d823b672b0216f0902ab4ab4cf95a37f5adfafa5fc507ca32d3243192eecc30bcbb5875c9cf0005de4742802db12c757ee1132024a1685ee8e593ba771673718f441b06830ba71b5596d8d9af5e458a96da6e815056e268c0c044c9682c052cdfeedf1cc84ce4a3194a976c140919bb987b564473f81449692624c3e991b814bdc816e6071ad70d29950876479e7783edfc4d0c3324745867b857fc4048ee1da162ff9128b806876d034cb902a580a5df95b1a0cf2d650ce995145a2a5e4d2d6a1dfc6d97dda676fdaf5e79e795441012b06d3e72c0a0df6a154be4f6a3c945502186e1aa77f81f56ac79ee944fdc74212472f74ae03df733252ebcd6077dfacf443ba6e6f0f37f4686433165ad11af678d61806abd21644eaac9d85cbf16b9ec8d5ed39e099161de7791a66bf3992c369ee0099a4f6308d5c0bd5243f5ec671247b53a780f53643da83179f27cd655666f4fac175bf0ba4ec9437b5c3a485e727f2f493a5a670ebb34b0fff07246c5d00a5c3197334842a70055504abd3e54904351496e541510a1636d608f4f63c3e83440c12afdb39ae7bfe9d9422d8b365c0fc902607b13b6e82d71720d9a6fb45654553f19656e8cdf222c0b0293035172b669ea46fd8f1323b3064c7de0a8ad94a0f5b6e0bc1308c9b8774c5b4ef718fd4803c678db3291c89f56af0db989430517536ea7ae335976027a8717fc100c1936703c400d6103c5673092120712f1cd24b9f186824c4b0c42b81c4f6fd389e8099eb66d8949a0aa872c74d85c1a8eda6676eb8dcacf08667e0d82a1904810d9bafec2e6489b518e6ba35c1a5f8e7842be1a2f4779fcef99dd7fb89ee5c1a0d1dbcd3aab940eb29b84865be2a60bfb627a434f53b5b83a854e704ee0b8ee86b526980a30b36f86b01d913346c061ae6a9031f1c055dc216a26ee0920b868cb584baa46c19bc4c971d2092c9ce48614da27c15f4c77508a129a34fcd5928b12929f921f7e374f311212183d0aa09069a498b8966d297b91b0e838738fb2d662a1b6d6c190033ad69a92f58f2b8bc9397a7fc1645dc7901d9ea51d16e1220052a7803e58ec35ab0125d013ffbcfc95d303c9aa028cfdad92dac829e245ed32b94812416d4a79b2ba5587d46429b488cf2832fd3f3771f9c500bfb4699647a1f0290c5475b738cd30d449ac62f361a3096a4a5a6d901a895da1f982d690da9a63c79411bf101550e1156076dab504a7d604f73c2c2f4bda5c19ec137f4c9e8a2a240f14548e37b16ffe509c62c180132a2a38aa5093963840b32013b13eea95d1973ce4c216b4102ced405059a8e29dacd144df34cfff2df89ce61611ceb03c903ec9ec59ae6402b0bad4dfe3c829d9c1feefeae1b12c3fb692ccaf088f8f1dd4ba11a2dc3a406f72b024447b14244de142e389dd9c83a24304e6520c762da41a09ee58c19de18b3d78d89226c303c65dad3778c6a7d80320ae03d35565515c4ab27c546f1e24eee4124594dc8755951e46add99e823a449408bc803b42517215801fbfa4a4253be2b3aa1544bde71cc32f2bc5e5fe78c4c54f3c0686a47158352211717fca64713428ac6e1f66dd3ea397878d283dbdf0792d762e04f9d711a89366740a40268b1d5d13aaeb5c283af2dea84be9cabf063b06d6a7c2f02143bc4f19ba569acd3a3285ebaf051269c00e66bb44dffb89915aeedf4c251d83b09d9036865ec86f6537526c8b5448b73acd594774d2277d92d85bc2e97d106357bbea24b591ae8dfeb360ffe15f49f0c1c2cdc3209c301ddfe921014492da429c4e1555e31a783d8678e446859f9f00c74afcbe6a7f01e6fff817fd3ae0015cc804da2cafa6551048cc381d80f3b9d4d11d834457e1d7e0b20710eb5a011870b10f40ee0daa4462f51dbd4a257f4ba5d922fb5fa146d2ee26f79785fcccd446386708fcd3461013450a43b88a03f31c750572ad774df2c66d8cc8a6f209240a630a37980c3329dce0055768381c197a4ed889c243101626d0462fa1a177d874778d150cf8418b09ac34571ad30310e39b490550e042c3278a93328ae8ee965118ada4e2d3eecb28ec3e49d2fc1ac749c3f7fe482b4792ce70cd8ee3c7ee574a13e7fd71ccd65e1274f75173a5d76d01178e464ce3741a4d33823057774f698e262729c7d349a530c71c73d098c0cd90e96e77e2fea53285d97477019a9b594049b3a1122f2755e55643e241c383338575d3e8d0dd33549c3feba699d17c775f519c4bca1ddc4c0d3753e266ba9d386a27d39eee2e71dd34abeea6d25c5fe91d1e2bf196783929b1ce2b42378d0168a0d0c0b0c20c9019ef5294520b073428908302d7629a44c5d9342a394af87ab34cefc4c1a4f590ae8d79b771e68c21e4d0f87a4852291a1e0e68531c20e81966f8698e0aeb0d8fd0c33ffcfa3e531c16888304c1387684208710da10c2170d85e54df34692854d6f1a2a4a414c572ce96ed40de6953ecae84ed60c53a22824ed0a8d629d42344c8966780a61fa53fb95687d5e76e790b5aebf41f34e564f3dfa7a748bb0bd8fc338f27c15da62dde6f846387f0b7f486f513d32f251dd609e1fa3c8af9346f192b4b04fb23deee171de4f7a8c6595624c892ed1e752a4ad1f6595b65ab1a25c16e12e6581457703d1a61f04a16f8fe4b61afd08e5b2487c1a7589d7fafcdfa0bfd68725ae8494247d1ffe249a298d5eb21bd08331b24a6dbac736f5c086af579f269b77a6832485b1256b8ac434329a588b0fcb250d5f8f46b3a19829464aebf81bc1ca96f8a2c823fe91910f0f986859a5a8988d669b44eedd72fe4bce221cd5cabd1f15ccb16d1279f7490fc90b8ecb91f41f95d39bef8fca6d689fe98cc14aecdd27438a67cfcbc09b6a69b6341f6958cbdbf848743cb98cc2328aa766dbe7893395d9ec6e045662236304acace14f8a2a9d3dcc6da6f93ea9f29f6b83bed68ab25dc1ca95f81f94edaae5aa5fc5de20f1c57fb1d210bf914a346af510fd181d89f933b64319973fe2d7ac588154d8b34da2d036893c9af1df462ff2d5667a4b9b34633eed8f3bcde66c7bdcb3d46fe9f965b749c3a5bdb5ec8f5fab03597a7dd27eb566fd96f6e679bac56e452feded6b560814ff5529e87586f67e153f721bc7f9f7484ce5458a8c8070f0158f90cb8d805c3c40463538508fbb5a4744eed527edb4be724fac288c5b98dec66a4110dfeebcb9522cab769c20e818856331fbe2bcd967b907042d1e67690be02008ba0fee8e72d40e8dce5ae4246a791377771edc7fa424cd86b6822008e42ab8c599d692065168e0a3bb3d1b1bff44fa42e1d85fef6edb263522e07d6ce25cc2ea87486129da9b2bfdbe599bd410e2e1520771f2b88791423be42eceff91552ad6276d4078685e20f181be0a6de16f3d4bfca027f5ef8f381689229dac7981d038ea468385eef6663046cf59ae93dee9338ed3c7287f8bb476e57ad5fb48b73a81dca332c52708ae10e4de2c9c7f3ff63fde2dd35a5a544ecb3dd40de6fe77e639fbf1ae8dfdbc40f759f849c71eae65b2bcb0f992551a96e25f70de27ed252b05417f10f42fad8cc6aaacc654ee81af943b7110e6bda7720ff4eecbc41f021d05da5ec41f88f2944f01bd526a83dcbb51f76ec023b649049efc063c726d1078729c7c677e5ccefbf9659e55eecdc23b1ffbedce4a61e56dac799ee423392e023d461dd7470ae7bd271c9bb79f5101bf8f089c12d61bc613267ecdce7c92781ab917bb19df60d582549cc94b56e9bfa680fefdbcf37e8a662f494e9af3b8f777fa0da695e1a5a1dfeebcd9760b63160447b1ce19c65494d5795fb444392af76ef302b52c0ef59fe57c23d612ac350bba38fec9bd8c5f26a33007af75816129e2169e17089c82471f9f3355a68e55feb38c348e23d2740fa4e2284ff9bc405340bf7ff381422e16d0cfab004645ae964a08ccf7baca66f7329d79b52f3a6ade77db8bb73afd069b721b3f4f2a3e8d466f318ae7907bf7fb40bf9f64ff5a22f748f0e6e4dd9cf0ed91be0f046f636d96692df517357e2cf6d7c6fc4b7a73bb3dd20b83494f6f46e1799d0af87d33dbf206708a639b5c4b5b451b5672d25054ac9f1f63b2666f1e366b9db553befd13f75e9cb437e237e29f50600ee836c754a3bd76163e76a0209e564a4a511497632346263989dcc3377e4b1bab9d7c9c333c3f7c283e1ddb64957b7e1b6134e4714f7cb1dad07325720f5f3bce17697ea26c57781ce9cc678ab307d749b67c8ec4cad3ca76856345d6352f9058cb20bb12abedc12c9e6c57d9aeb6006d5952a97b410653ecc517d78b097811841756bc5872e398a24efeedd3c50eba10c3e7fdf9230aff2d9b4601dd4d6b531659ba6964a079f17770265f856272d2fc6b262ed8e8a611bbbb8626e482092e14c0850f5c8cd851f85ab2ba8b262ebabb66ab631f66f878f00f04bdd20f088aaab5020a02fad9428be721e13b9f24278f7f33ca1638ec785bf8f89616678ca1c5175a3871e3959edc2b75163c66e7fdffb418f23c5d8b153c8b37ea1c497cbd91a4d12c7c4c2190a1bbdfb380c005c43815ec2e2cc43416572c49c2c15992f47ddf7785077a0914eb7a22b742dff77ae1ebe5a452df87c910d8d0ddaa29d70b31a5e1e01053dacbd6a60f7cc1381867167ea6d9d08395b34c6538381f26739e799ea43311ff96a24fd307b6f0400efe218514e98e239d8562b5300ff8e86e2c49ac318ef37fe68789036e74e3ebc1521cd8e96ed5eba563aa828a55edab0e0bf8507340a19a3869e23736919e72ec873ea35b94633fe4ee5e73e0c16d227dbd4cfcd70e1e3b46d82143184d765cd051a6348333ba2609980e225324e9ee176dfdc4f72f091d4f3569e2decbc467c20126296c30806d8a020cbe1e0e3969226c618ac287c6d72327cd8a65cbe70a4dd8ec71d2dab936681ca7cf387dbeaf87482d94efebf17ddff7627d51a4b31725e78bc4f8611376edbd562c31266977f6d2f950e5a889df090c3a3a3a3a3a3a393a322c89010625495e40a2b3337776761e367b761e3661d6f57d2b389ebef31f7b25fd25bf2f0b155960598010610222762062004388f1be5a9ab517fcf26670b4160682dfe7026f30bfb6e8ef574a4381374fb32dd031080e9145b737c42c1c4248777b54082e8480a23d914e9cedaaf208418410a76a6f75e6fcb0712cc974fbb5b1283e4558ba747b58528e429a5fc3328b8285863b1f0b8fee927605cc952cfa0a11f5b1acda1be58a5f0952a78308a0c16d9231274eee571a2bdaa94da31d9bed79d5e6c52f1b0262af20bad4d21404134184ffca8f9dec840fe59d60db24c22d7c537b1c355a0b3bb90e80c401c003b8a340f778ac98d1ed59b1e2e2b5b2d30d440f80a002db64fafdb56108440b0822deb535db4a3d759b363f88f1fc872cba7f384276e70fff43906e0ff4418d54b5f5e455e50313ed89749c3ecc72e90311db8b353cf5a0821b2fcc5f4b474dd8a9071b257b58d23c8069f7afd6c4438b072448f7e181ecf678106a0f8a657995515c854c7797978a55be10aa0236ec5afc235905dbf87a295a684b7ddffd172db47d1f2db40981012122d0fea1fccb8fb1f8585c58680873bafdf3be965eff621b66fad7e66b48ee51a15440f1bf6fca0de6282ae08f227d559b678f7b54c0fb2cebc2f302d926116ca272621693ff4f289e54c059a91bf9f28d04b1421042fcb38e7271c6074fc37f0024ba1b536a7b6f1a295caca6bbd473749707400d803bc08f8c2df9334c3a0a0a2618ff688595283e2b51ac1059b9b182821522dee485f226fe42e187620a4fb6ab719c3ee27ca16c57e3580665bb7a424edaebff66d8f77932a47e48c10f53e0bffea40db1e7f0935e48f36bdff7121f697e946e1a39ba6986d04d1347378d10babb068ef6ea742b827605fa885f646355452b896cacaa90c2795d2d28a26a257459f87a38a99758e70bdfc279434b7bb18716ba8b54b12bf1b35d89cfe31ebe30bfd5787a8cc42a495af5238d95a85958bfe747d8e75bcf7dd6ccf3c7ab969234fb7fbe556a9b73d2e67dea81954e2210cfd9c7f2c474d66edef4fcb7db2c9c229e421f54fac8688fb52edb8b19267265097906ddcde505544ae481d76bbfc2b49a1dca3cb24aed0af3e8eedb78d78821861dc4c480af97ad35aa4da3fbd9bec69c5fd5c6fe8786aff77d2ca29f3992f87a2285a5ec7d1c5424ed0ad92611adacb472659b44e2db26d10babe6cc85f10fdde6f83f4996659b44b649d44d23813b855eacd959cbaeec7d1cec7d1c82ec7d1cf0f566780addc42565ba9b0799eeeea6e14110babbd291465d62e5c9f8938a9020d45c0f1534d7434a7323a8d1dc083331cdf1b0f1e8e64420a239118234b70389e67678733aa0e074e43457934597686668667600496107023a89233956142145036ac4b084088898f095617b018c154f5041c5131ce8a691a28a6e1a273610451354503185145140f144378d13dd344d74d368e9a6d140374d06ba6930c0bdc6702f303417e8a65982468917182f2ddd48a11dc379e5057b4971eee5bd84747b51dcf33c0db0836e6fe6ba397106c8a2862e2dd144c98952144d93848fa612032a6674fb2c53ffc0042e20c6a9a830a54b19e0a810a552292f895ef24517702d74392431e37218bbfd8b2591399cba48c674c87fe69ad246ff486735b2746e0a14dd0de371efc5295670ec2f3745ca14af671c0e26f059ec96290eef1e0b9c32c55154c03c6bd365773a3ec2d54e9f13c83d6c43b2a4b376b163fc21be39f9d7fc6be3e471ef2f256d99ce9acf1b0cc855b42a5118f3b8370b33ad619c6f29ce9495f9f45ad26879be406fe26496e98f6a9277e6faee2539be4fa2c4fb36dfc6e9a8da4c2c2f19c53d7c29fe1a0cb3dca35191a278dca372eb79d1fa80537cb0a5dda44e9e6def37f53f2aafe3386354b4348f59a702fe2c9c3ee014af45a17b182427d1670b2596c402e9486d25cf9c214dfa4a3de55f55f98b367c1d60652b5b283f45dc100fff6cf61ffc6407dc932c50fe656c594faef41329fd2405ce063036a080b30109ce861c3aff751467c34edb2080af42b18a0c3e529dd1ed9f6d1241d1aa2054334ee5eaf6cf05c7fe7d15da529d542570426c74b77f98ca14210f74b7cd294e8886eef62fa77aa17b17ab583c3640443c464199ce329d4e6e4e20ceb62f8baa2ac808cac366cf0ce580b80072a2dbc3b5c7e7220a4c38d315dd343574774dd35800db95f89ecdfea3f2594efd65e13c91dc9b8ea188f5285b965de129442b8d5e365962056279b711c4df874b7babb7f1a9ec47da977eab739c59fc1b8ace7219dec4ac47058c15d51fb24d22708ad3db384e6cadcc79fc65d3e6974db286e78fb091de60572f9b2cbb32ca76c5cde0a35aeae3840d5a918c38275abadba7385fe85b6077a39a73427ef9b77186ff06714e5c68f1fbc23501e361637477acb92643dddcce986e5ceef04e583b3355d0ec74f74c051a566212767874977ac031814017f11e7a70f1bd8913efa107bf3961eafddf181ec7e9f37a79781c27acc4adc38a0fecaf846f8693a18c0c626450c39bf8383f47cd88e16418298084873f69fe187e928b3f71aed4b2808efefb422a7e3d12c5bf254b7c2e06bb04067086a710e8a8e9ddc0a0e3b75b3ecdd4d36866d92651ccde9c3c77c191bc0082d7e67006d52f672ef740f1667bf2f1ecf1c447026f6260bd4d3c6f8f74f29b0df577628b63e3b7ec4aac472cbbfa1136eb51507ebd5e383772142d4d7ca3daafca964e087412d0dd2d20d61225ad168dc590dcbb652379d2ae0d5aa2046323be4449abe532f76ca2c07ebc47b221c61336f31467780ad5f2c5d19ebc0fb7e03310db588d51bf39f9e37106b9072279a104c1f2badc03dfcbd88eb89c65b715046b1ea3e00dea04e2bf311b73fc98d6390be91d537926a3a3e39092168794e471e2dad9719ddc2791834e5c3bf869e2fc8b4b1d6236954beb56e6d9569bed71ef762ded69a94a7d42b1ac58a52a2f1de58137334fd1867672f7f093932897e4382bcdd6e7588634cb2649ce9f7d23b5f771a876e5f353ed2a87d6f52aebc2e27ff864eb6973153ed9d2810517a44777e74c6086e76c95391b03dc50c4d9ecd8e0d89c6c52b0e961c3c346846ed2fa0d0b27ff6e587047b1e01f691dc5829fdc3f14762a535a28836bc18b6eff6e4eaf7973a5df4b9c3f7bd186dc0a5bb8155a708de3f4c1d436896c93c89f465dde0dccfe7530f494eb80270743b094d1184da2378734633a52fc30a01e9e1ed9bc9f34dd430ae985f1c82af5c9788ea8986d6fda8c3fa44fb3db3c5dfc9c8ae5e4e8e009826ef309e5592769fd4c6b56c83d4c478c6ab977832fccbaf8361abb9136966dad51c015a8935a815f6789025fd58237d3b34d3d553a47d26f77624bde273d46fd26366f2ef76e13c9de9755b0058239e02da37ccd519816b9070ab96a58010dbd5c2b20a21a805aaf5591cae57364e46aad8a7c5e46403fab23b0f498957deaa3b8178bd9d4fc286eb40aa787635626fe58b3152955a90d53135bf296e4ed4e7c3f499c3e6ff0f87f7d3a9e390a877446ce2bb399bcddb0d202f010d1ee67cfd7ce7039c62a95553ac26853e6d7c65c45ab32fc96abe0de915cff2bcd8a75de1c3b3ef56c4fd2192ecd84a0b59ce59904060320f1903e8c1b4286f723681a1950ff9a33c174165a1288260c4ffc1d9cc92d7f6ce21b2681a1f41875265faddf604e4b0243e978ce88e8eed91c9008701d190d663bb640c7d7e338305eaa529c31dbfab0d9934a650be52f2b96c4c2d7fb6040924a3d8c0332c4fd3001f7630aee8796eee67eacf02348b77fddfdfa5be2a72ff64c1be922ed9d4213a087b490f6d140fa4797d024748f223a4637cccc703333a49592068c8ea0610a397e0540a52454ca192b76788cac200a1bbcb91a258e4cc067a4580117194c53ce1b33538a08c04700bb1b498321a3892045388d949ec9f2c509424960b81a214490c4063e4840c90c453485992c5f74e16aaae0a000483a4447886c516688ea012a02ecb0e9191370310a6968ae860e99c9f245090c5773244408089321d880e4e940bad100cd980c14e00757e342095c8dfb1881c8ea32d15ccd092488c0a347ea4644136faea64796d902da0d0f8ccd52a15921c7278ceeee1ee20caabbc56e1c7e10a40a0dcd95c804377819a179704878680e09d91c12b13924539a43c26a0e8950734868680e890cddeda36b009c8e68ee84a5b9d36dee74d4dc8949732754770b216282079a332189e64cb8d29c0923abbb7d98482841473747421862736148692e8c55736130692e0c9ce6c260a1b93050682e0c1ecd7581a3b92e3c68ae8b0d9aeb6246735dbc34d7c58be6ba64d15c17269aebd280e6ba08d15c171e9ac32475917486593fce90d659f8ae9186b5b43e3fb39016da706ee410ab1704b568ab38edd85d5222090b5400caad0300d86d45f2f6edd0e96e2adda120a643618c0e852e1d0a18a732256dd8a150e4c806d45da280f822909a1c744418d0e38748648a5653b19bd411396affbeaf3be188eefe1212e304241081234a4604d14800020f28c201a5062880e60025069416d07d73aa2962c8986e24dd0d61a2bb1b92d30d39754352e86e17ba1332a6bb6f4e3727dbdf9c50379d90d907bafb87eeba18e0ebe18c239d25e8eeb2bbee84413c613b7426b0bb33b1d0dd9e37d25c8e3497df87d35d10a17e8c64adab4b39219d6109a7034244770744a803524207e4ceba1f23e86e9bef7bd91489eff9fd5c5a7722aac28702fafd5ae79d5130c644ee5d9b72af52599d620572140bc8036f588e529d40f74a4115c80253fe61bf819ddcc395facd3c91f771aaca3d679d30fd3e1cc3344f0cb2404f8d9407f5e5cda0f8b47b23c551af13f835292078f3729494138841bf36466b98ffc9a3598f1edbd12757fcf6077f15daca944687b08fbd8f83ab3e593423801788d1de0de6ff313abe68f30dd14908f4d4ec013de5ded76b3f10f494ff056f6e884e428eea7194387f7602fda321553afadc40e4f87ee80e816ef22684ed0da29b59015f4f4912ce48d2e88b92134bd746c0c1cd1104ec5efd71af89e78940a0bfac66c54a6bf67a36463e1def7c10a372a5d36dbedad40d968ac5ac4db5977459a5792651158b87c7ad1882d843c019020a2b30dd4d3617c70edd294e0839e8c62ce8c462761c674c08a7eef6af39d0ddb53938a248a5b27501a00c1825ca885d90a941f7b5f70229a0bb6f73647e96e08290c50e1e8cf63e8cf644a3b5acfbb5e2bf416f802127ed8df08da12fa0c84192c2a3b564ea0d21f9f5d4bb02409840372527ad7c1f63c4748fe9625228e37c232549df37c798d267fa832d1a5fefc5f9808cee15e641f282e883246a3e187d20d4ed1faa847d1fc647ed0506557a10c7e732d32856465910af1e2471f13f4ab62b3c8ed3a71efd65f58321427bb85a7fd8bc258fcc7bf790aead15c0bd3b4972fa9123b3f63deee1197076d0f3b4b9128570838999aa748cc5446b6b18471e548e597fcce31e069db87644704eff1263ca0396e17c6c51a0ed57e04867ccaf7ba328529a8fe38cd566e1e441b28e6d441e1abac30c09d3034c0ec0ace1df8d7f27f7cfa9f837050727633a64c20198ee1496cd04000738d5031b5edc60fc6283310d470fdd3dd3830d6aba7be6f9c3c6c5b566eb137cbd7c3fa9891a2400d53335106b40430d96b8cdfe7969540313d608b3c6083cc7b41641f1ee357237598f320de948da80d648a2c23c2125ad2b7f4b56e948e775d1a00d914ed6f8493f445a170d92ba6dacd24e428d2e24506388a6080b92e8eed4ede69202ba4b09e8ee9a5296d2104f6fee346669e45012a2bbeb57b17f83bc5889642dcbaeb25df51b221022828692ee4ee51958e9f6cfc6aa6a064e3a08dfc229fb212cfeebb52a52b58c80a0bc806ae0312a72b55eaf0f5fcfc66cac529c970c86fa5bc18c6e9a146210b365da0be3fbf4de092bd2dd3f9a834107835277a3d09c193968bf7f513e48d385af67c6c90c1b3352e8d6e2890b8871c7d72b03a90c270d45052e20c68bac80582c1e2137028202b4f29fa1a322a196b754422ef7fcda187867244d145fa49da91c1956705e48c2d2dd5dbad25a1dd9f092c24364ab5f4b5fe2f7b5408e16fcc08a656b2648b66289bb8a167423857684bdecda5cf95aa25855e52041f2426d1a892adb8b75963615bb4995a66237c928c808ca0c4fa1202328792cc9796b2b1800396fcd6dc69385ff836e46b71be80c22bac56876e1fa48d3f5d848b4e1dfc499ebb7c44f4199829f6e2cd6c763983e8bf38dacf7b35c837b5f1b615456cba097cd77af662b12121d6beec5acd3bee618e797dd49ef4cc24398c8478a7b37e338a9ebdc4ccb1b100625481cc923cd11f6c43d8c8faa0a29b443f83fa8956b76a8d5b22b71f6cc9e37ba9f34c2663d0a12ebb78eba69c61202faef0346d0a5bb69a1ed25d6c7277801770215e017c17023f638cf87e5db3941342db48dc00a1b3680a37480ee9eb122042e4e74dfc61c72664ca3f9ffc69afbe2f4858f2f52349a440e384a5b8ee81f61305a6340e2e5a460a20861705b58e8ee9417a417d30b17bfa5705db00185eb6288eb228748921360a03201213f4177370a615e1021071d38a0074a0dbabb878ede5103a561186341cc6c02c0d494910289110c00c42354c1c1a1224698729c7a4036808a6e1774370e4074e1c5c6853882d4506201a6fdc30204fef5e01f163ffe61e1a3dbbf2bce68ffae80c2d62b48ffae48826f8f14822f2110bb3b0e102ca01b042b7c208bfe80d0077cb47f1e30c3035ada3f0f2ce9ee0019dd8122bafbc57560a7db3f32382b462b503d72608c7f1c20e2c58115ba0a30b3e6aab055b1a4fddbc097ee0d14e1df0686dabf0d10a1e20cffa880c2c6a8d899824cfbf79a020c1d9a9b823545104e0a32fc93e2886effa458a1fbc545914547c18a4200fe7150a80145b77f64b77f1c144bbafd83a2c63ffc840afc7b0209fff013a8ee76828c7f4e2c003be1f2af09315c13593471a5dbbf1d5ab8269234a74505ddb9246dfdc4d933c71c73d4cc90e00225ba4af70e7d460690d08095eeb6ab1a0ad41fbd89132767f8aa7dad198367c6886362d65de2f17ad15624a9a737a748dc610656e8d6498dd35e9a545f84d20e0ccc98888309369830c187c4d3d1f17452e127e9d84273444da94433c30677811ebafb06dded43734bb4d15d9ae96e1e64ca3a456e899c76fb333c48284a68d1ddfeaad9ea54a6dc608e23e2f9a58ac5436ff6feda1a8ae23287f34f1ee847dcfdc35fd6b23fb6a969f330cf7702b98794a3e3797ed7e63a2ba55931272747279593a3e3b659765a893ff131acc49f7210836eeffc5ae41e9ee1ebbd5e111042b7ac84f9f48a403bf398751a1bcb98cd8e2758a9cf04a0bb939a3ba28a54aa68e627e28c40a23be349915adddda4392358dd5da48733c2877fa5c841608b96552afe87a3ad8f642facf6b59f4fbb85379cc5f96124795a3cf2b887e944d14f12e964fd07b93c6c3326291d49da04419256c2dcb6dcc318974c6e5c70261fe447dc05ff8fbd4edec4499a5ff947263724cda7709ce1ab89e71f7170282ae3f28220e89eff0d49f3c971ea36d6f2a656a0cf6aa9ecaf574bdae9363b0882d85a2324cd97261901c709fab5b14cc792c6e37f6d48d2ae90acd26ca1fc20857688c593e948b6c43bc53a738be6079a2bddbd4373315a378d0c3cc4df41c75d701d40670252df711d40b0c5620df98e53b0487c1e91f6609fb1759f857d5e56a751b6630b7bccba0a4d1cc4711074cf9bf6e6bf3a9e722639b8054be5d8e4e88814766f52b0129f46073222d677231497b694d15c5ab2ccf5ef8cb9c59ecb221128db159eacaa62054e9620d12977beefb64677fba28bd49aadf1514342111566dddd4e687cbddb23bdc6195a277a8a06d0c820d0d996d4f4e81ec7f942b2a4a53d8d8a7fff052848a0d042296518e5165a684be5cf36a987a0820ee3d05d2554349beb9cb15e2fffecf7e191cefc3f1f36d146743ba881af777f16e649434002ba1dc0ce8edd0e3cdab661cdb0665c4f0855a1855000b410daa385d020426ec2167233d4426e92747710213eb485f83869213e3c5a480f0d5a48cf095a48cf132da4a7012da4274a0be981a185f4b860e331f41019ed21ba2fa33dae719cb4d6bd41ae4a69627729ca0d4a3628b1d15daac1d3662dadd15da2811adda53468b8e0ffd804c699ebac7d12957597d098c119d7e6fb25198ce30c63509292f18bdd345b1ae7120cfa3e35a3bb5446774902dda517b8808c4e871c09d4b480041e9e351faf14e8ab5d79a5d786e284d1a1eea62e5015684d76947f347b63677477778bd0dd0c8c9915ed9f9dd51c3563402e6d3733c04cd5ed1f0a5f8f9ce27c7532b0c14a5faf3a4330a6466c8ca236fcc3e411be5f6b69c4b2fd6a26013341b060860122c696ac2ff22b89c0021874ba3fd23ff663b315bb505818c90b4740b77fc2226a65dbaf3ed31f5c4355cd0b24aa78c46a7b2a25bafd137cfb27b77f22de591371aea5c5980ed9d80c4fa15c5f28db7e8569a1143c85700fc65f3fcf21a36cfbd538d2d9ad3f84438a2bc58fe7cc368944550e7db2ed7f24e97559d7c726ae47f8f64f5478b4b6171fdb118b63ab067c6dd07df16b9685e705b2fd0aff643be2a719d3a18ce790acbbb40215a4600c31be784141185dba7482ee9209ba4b25e82e81512241abc4faa451aeb7ec7218d24d6143e5c5a8e3a7168fa2ea4745ef8b28d1e6963ddedf49629687c54fa22cb1da1e0f299c30d82d749bf1cc769569d47fb4a929ffac8a524649c1deaf92f3c4a05707417184d1fb359ce991233f2a8f5937921f1ba1b4fb1676cad87a37a83c4ffedd5a6e5bd6ba3a160edddfe702b2b1aa2ac2938e2f5a22fc1f94bf35c2e667bb420aed50be769c47375f4bbe7044da489efcadfbdfc2e548deb2752b7b846c40ff337fb4a34f7db2c8a7f62b927685de25fe1b2149fa3e5a28a51b1a3314a669a16dc83514c3900a6efb2756bafba5b3a49b4812dd3e565deaacf44c962f807ca0fb08866e214c46c441c487f6647ecc518930f2faf121e90a0006554516564638c1a5862949078a084202104400396105338ee0843b84142105183a2cc1a140091129f0a1552105093a2136ba779c7eb9c38baff0934a1b681a09744252babd70c6629dd04efb77857cd0d8743578a981033534d1dd2eab34ace5cd8ec2e338439c2036ae084aa2f1f5be2fd50551099ad219d7d21ab5a868254a5394a488c58a6acfebf562a9627f59f7937e857b8648970aa2bbc5daaf6e309b3d9cf11ce9bc798e344f54aa5a5a5fbc964ce5d88f36d323f96f47fcce98e3e937e2b527c7f326633ad6798295d8718d8d7346c7597ae2cded84c517451ecfab5e6a8e25ede6e39e97edea966b697b6097467b91781cc7e9e3e4eb4e813e1de636b9fe8fbbe02549f24f21f1bf1397962625497227297f2169c8bd6a51a2caedaa06c84905dded93bf25be85f1b48c7eba2f04dd2704f6f0389368778e3c9ef8b396955a7cdfaf8de1e0fb48b3e6b9cef20677d523ef31be5199689b441eadbc393908d24ad15f366177ca7c8e34d60a62a9fc5a2769a5e8aba2960d181cbf0f043df6e2a7608e2aeb9c13693acaaeecaa1eadec8f75c92a9d65bb9a3de3580689780a65bb22f3d3dd50d1dd5d122c5d1225deed915c7050543d7973ddfe09385fd582b747b2c17816529aad36fcf1be5829ce2b7cfb277675fb273c76d5e93869d005c6dae80e53c60badc8ea7ee3c74c00ba9c24bd442715d2fcb414897188003b1c237c3d1ccf072f7b523aefa7c799edc97d7cff40d08776281a3a14aa43a1bafd6bccf397751b478ad41d39c18f11bac728f2040a205e6cd071c3d6dd1d845aa182ee6e1e31cca0f86102102001e8dc20eace8d9eeedc48d29d1b2d74e74690eeda28d35d1b39e8ae8d32ba6b8304ddb5b181eeda58a2bb36b074d7860eddb5c10a32a401d00109e2440430a40538ca36c1f16d8263a84d70c8d026388ab4090e1f6d2ad3469bcab0a04d6544d0a6321f68531926da54c6016d2af3439bcad03695c1a14d6582da540686369551a14d654a6d22c383369129830c176d2233459bc854a04d64b0b4890cac4d6476da440605225d778400ba3ba2a63b2378d09d1132e8ce882edd1961059020270081499b80706a1310506813104468d31820b4698c0dda34e64b9bc684a04d6390e86e1357c484128876343724a6b921349a1b324173434d3437d480e6866ccd0dd1e68672686e68d5dc104f7343495228120002082085165448410718dde968a23b1d0ae84e870edde978d29d8e9cee7408e9aee68dee6a5cd05d8d16ddd528d15dcd0fddd5c0baabd9e9ae8600dd9584d05d498dee4a22e8ae444577ffe0ba20123417b44473410b682e08371704361754030f1f5958d05d96137497258beeb258d15d160d74970502dd65c1d25d96dc5d96ef2e4b01bacb724377596ae8ee127c002181041e4450e8b084a03b2c1ae80e0b04bac32244775892bac3227687e5a83b2caaeeb0c8d01d16af3b2c46bac332a4bb2b737477654c775770d0dd95167477c54b7757b6e8eeca14dd5d31a2bb2bb6eeaebcbabbb2eaee20266e881334370417cd0dd181e68660a2b9211cd0dc103f343704adbb83f87c69cea78be67cac68ce8789e67c8a68cec736820f93194bba3303a73b3356e8ce0c0074674609dd9521477765eca0bb3268d05d192ee8ae8c1274578616dd95d181eeca60a2bb328ce8ee8e840e052236438694f0230827c65177620875274693eec4b0e94e0c21dd7d79a3bb2f33e8ee0b97eebe3cd1dd9722bafb42bbfb0276f7e549775f60e8ee0b0bdd7d39a1bb2f40bafbc2a33b2f3fe8ce0b0ebaf31283eebc8cd19d172fbaf34245775e2cd09d170674e72577e745ecce8b51775e68e8ce4b9246a1738283ee9c8cd19d132d76cc5081e66608a2b919c8e666d0a1b91970686e861b9a9be1497333d0f083b3d2a33920e6680e88379a03024c7340c8a03920c628d5f0280901226689e6c424a03931b73931617362581c8f1d1ddd31f941774ccee88e0919dd31016304127a744a88ba53f2d39d1226dd29c1e94e090bdd292140774abaee94ece82e491cdd2521d35d1231dd25b9417749cee8ee2e4827654977528c742785eb4eca8eeea2e4a0bb7df8e02107cdf1b045733cacd11c0f2e688e87309ae38104dd5d0207a46b42a7bb268c74d70490ee9a98e94ecb1bdd69d941775a6ed09d961674a7654b775aa8e84e8b05bad39280eeb4f0d09d16a401fc2801480f1ea61c94d0a63062da14064c9bc2ac1166056d0a03469bc260d1a63053b4298c05da1486016d0af3439bc2e43685d1a14d61c0368591d2a630ac36857122a4041f417c943968ae7c4173a518cd955f34575ed15c2945736512cd950b68aeb4d25c999b2ba50039811b92460fda94860dda94c60bda94869736a5018236a5f1449bd288409bd208a24d69d0da94c68bc8109c9bc33934875bcd6127cde1244146e0f103003e82f8e0c185076de292469bb88cd1262e5eb489cb06dac4454b9bb848a04d5c1ad0262eb6367109dbc4e5a84d5c7cdac4e5a64d5c4e68131712dab4458e366df1419bb6acd1a62d2b68d31612b4690b166dda22459bb628d1a62d1168d31622769cd0e950d19d8e13dde950a03b9d20bad349e281460c9a432305689ca03934ba28a1336aa13ba31fddddf0467737f0a0bb1bd6e8ee86177417a9cca864d467eaa6850a91080000000000b31000303824188bc582d1805846a5f901140002609662aa624617894914a314420618038001000003000000834309920a57e5639f5b9345ffedd4cd68d6c0c19b77c8486438460023d037399665f13586fd89bafe7725a66221bb533374af2a36ca09872ee5509e6a6a6f3dffa874a5b882903e1f1321fe13f0c3740f129b618bc3c26748f93dca8515badd9a1e3c6d86e3a5d7f418c0179c25a6e37782e3ed2e430576cc74662500d984e006be07a1303b1a5e717515aba0b07ba46d37869e1302c41fbbbcb518c52dcef09e4a0f2845f392ad5da7d69a53244c7f867ac283be38fd37ad44ba7834423826600241d7c80e09c31bdbbd2cf9e81c99139ab1c0e3d260c5851617b96aa4abdd44fb71b66f204d3fc2127c534a688cb97a6c09a522833496f39ef1ce1ab887b90bbf0dabc57897a3bd2ffbdf08e3afd408a7f3f150d9c399de27a1c289a9d174e9940d4f447f4f325c471aa6a672a65d945ae4926f75a148feb47b70db0cae3980b1a162b7e172de00a9c983a65fa3b2260271925103a7e4b10e184e0aeeeecb814bcfc8c59d735940448efa8b0e52dd75e39cfb75503e7e3dc586ca985f2c3fb1dbcd965b4de979d108fc7d83dceb911da33ec72ab947640c1bb11f3712117a53f4fbf57d43406b1f6aa4905c1c294bf7bd502b784c9f303dc5f485cba169264f76cef16a86fca14030ba77e20eb58d286ab86f067a603955f8b58b130871eb21f5ea43c2f951f55b0791ee2661478fe59037932e704a0110815f51175a0a68ed83bb0c5f0e39d9d32977b42182db6437fd074590b996a7bd4c87921fe25aec31199a0f8d8ccc0f537f4f8b1324ba7b8befc89f5322e58fc2d4e425e3fc8746314399859a4bd734b822a2f547706d66e1caf1bff456aafe69aa7666c6513d186db0fd77e82b0da53044ce7ce25fb821e1c736d2c83838a2ffba7b3e86aea173bc126ea2fb0cdb42b7f01b785aeff613f75782ee11235d80dc1b137017548f673e6ce49bcd3fa16fb6c66b9afcd2c0c1ef9f7fd789ff09d91b42f6ba4c39c4fd7d37dd71705125df42db4f41c8e43350897ac2ade1e130eedd35f9e56a19605bb0db89b351e22f68482613d8228141bd8fb307f12ec1fc671a8df1940ac5ed2b35d7c362ea4aabc7c96109f066ab7673c77861eb4663bb848276c47b475bc9376a77da5718a558c82e81d5a7b9df5b915270e615eb0759abc8245d7d4ef8f8153953d3c1b0df03c598ffcabc085fb59667d66cc27e5a12d392c0d9097514f6aff3e62b21baab6a54871b1df3f32aea491704deae14880738cb28313f4de3e9fe7c6c79c2337b0265f957393f2ed6392f6848fb95ca41ce8c90bdeeb9278cfa77e3dabb152761065346e9ad6e338d1834eccf5ac79f3c753c4393e7afd7cee93e4ad1649a3511ef13e45cc947b753bffdf35d23936453d3ed1694878cb1fc0997e844565ac1a97905a4003327f25041fde0d93aed8e0cf059045d626240136ec9c12c7342e166590a2fdef08636f3854d333bd1088c90e9a2d106278b12719ed49d252d5136441a91ff58fc255f502f141bf4fc344e0942c20b22a6bfb18d8c83f67b704ca8bbf17a487a86bc878e042c67eb98ba062615506ae546087d4c6f324aae086ea46e2a1a49a40be43d32c6c21c53dc573c3b2db0f0b02cbaf7b8ff101f57830311a5b250794ff5e6cbdb181932bf1ead39dc1356aa8a3432d53b78a4079c866a3a5eaa29fad82506c0fccf832d5a77fe9332dc9bc1220d57fbfbe6b4b5bb34e23cf63836144b799c160fe9c201e9e650f7e0a936b7bca9ed598f28564460bdd06e185a20cab05027956f3e7ed54acb0e57abf5f652210a7c49259e4951c6d45faaeebd77de7d92a453a09e42d4a39a84f88282dcd24a420d9bdaa1467f6915cab897d6d3b31e18d92c8091576c845ba2a32aeb1ba9c91cd45c556a550e22970230b385cf0bc527c010809e8816b89fbccd9df7f9ae06dbd09f344c6444fec1d8535372beec2c3760ba7b19d16371d945600066b72e313405b57515eed30238642f23ada7328fee909451724371a3b62e82be51a8672c7164f0a0a1391d41d441bc69bc6c1a244187965be29f180674d19e1912f04591260008421a66f9da636bf74c600fb5cae2056c0793c192fd43dc906f1bf1f8e5e4e3c65c640599223f08cd6e2da729989bb72fd697de8c12ec21d9c92a1afe53384b4d8d3fd173931c4ccb8338f19c777cd46437cc0b501fe3e61fd20fbee96efd0c5fdd28849e2594554b125eb44268194ab3310ff3af6ef78e1ad01da6374b719471ca87a5d0d839ffff5de11c0f7cb0f2dbe1c4c021ef2047b699a02426ee774c6162eb0ee0c7aaef16d512c19dd07783561077bb59cf45a15e7217876a80df76bb804716ee097302cd7ff464a597436d73fb0443196b360d4975a9979e39be158f19f0f8f947fe178f4ce6884483e66012b1aabfabadcc6dea260f4a270e906f30702109d02d5cd85bf5fd4bd96ff6dae244dfed366316cb4f9a3e90bd259c6cc2e4b699c244a38c6e26c10fa8f3b8922c4645358e2299c72be95c33251f10dd21517dac1890b4a40546dfc42ff66ec594709a5f060ff76c3c8a07ee25c4f6610a6fe2bd547406e45062d4a746b4346acfe450bf73f00db7974b486703ed40577a8450a598b98f64467aff72f77858464fda8e8ea7e09f578e9827fb905894898b1d40fb6130cf068fa87b4bf38220e397217afaeea9bdd51c15a5af22e1920aaf0ed12572ca55aeccd73818ac8862883f3aaf7bb84845da66c9ef0925dfba00110b3940c229efa20fc6505465a12409c458a4ba22912e17e532e29fe065336542e22bc0b140198154e7421354302f0dcc841ef2d8ad31457716b063c6f8755586ed9976ea30a3ceec25b3b52b3be0ace68ee80c6e4bbbf84e677c4fe700805d3c964a67e8c27c9372827b161a5d8b9191e262a962354d1c0f7d683b6e58d671a891c1df22fa023a8a0849fa597e1036f759058e5c8ff6758b9a215fd110ad6b8458f84aea9b57ce6a480bec8f8198087ff005f2dfd751ff3de033b73279a89d30f1ff297efc2a3bc9cd49469f48114e03e7c77e7ad71bc7dcf7a80a26afff58b6f53746f02da1a28d3b337aba9ee9b0a46ee74c8302963204c8bc9ef9c05690bda8d12f127701a9380866d700d08e34e6665e2cea264801adfcdaf769fce3bac75dcfde455dcf5fb5542355cb8306ddee4f33e6a5b466042c77b06abbcaa192dc6a02d7e39d75beae96d9a1c716894e9014b65b899fc872f812d8dc392d9e7a64a89aa56ddf1f4b8e9fdbc123093a21140af728907e70b5ad054fb944c887147aac51f02eb3566e6afd790682f7e055fb30d482273e32a1fcaa6567e1cea4f0de82cfb2b8e7fe2f743faa296570e32d539cb14f92b9e0d1f6d255ea44437e7268ac0ac69286509ac56b8ae2b99a2cd80b2559a2f085c41318a1cca84a6efd19b1cb07420bb83ff4f9b36b0f34edc340ece18cc316b258efe83c2bde118f7fedf3d77e2372e1b6236edb9faf25321fbaab3a1907ab68eff2c9dd12e3d577c639c3888bb9fa231ce5979ce4f1e34adb3d443fb7530120bfd0f63acbbd2da09c95d428356b83b0cff5b3af0c9718871b9bfab2ce575b71d1838efa0c50bcb414063e60fc02b7c70b127875f47ed461cbd14dc8c9d6c7a63a2c93b0aac4168f41feebeabdfe7839e9638fb401c0989c5bbc02061ae81e6ac4b2804fa61b430f6f40724857c04443df436e0008681b87d516d6bb8dac5c9cd9fe10fbc2b87cf63587ef2d53164f4b39248403673e462b7dff7baec28718eb5657734463bf28c7564577fc9f6dd112e8d5d7e13f4f799d2653bdfe29750142bd00861a0029cd3e9b710897688c04ebe3ec1016357d9b9c6c65229c1677fbc73a2db1175cd1dd1fdc73339ad395e18e0e0c7fdb48c798d19a6d550cbd8c1fb46d1e6e3c660dbdcbcc0b88885c13b8daf943f2dbf9e5beb1646e8a84d6dfcb9f3b80fdaac7108d8101961cce1c05e2d9e4a9f30bc77414be2bf6b140dc476d346783f44b7a217cc308af379e021046da59e2973294c1d4518c0ff76b534d40938bba985c2eba946a95148129cf641b635b0594386e929201744ddfdacf2d66118fcf596f92a5ff4f00fe32226d89e6e6093bc0ef65670e00f6853b17ce1397a4c1a7fb6e80afa521f3d5538de30f9cbca0fd74842711b21c062f6d5c7f51ce866958ef6b4eb8e7cf2e035e6668ddee8a8e2e2ab0a2cddb205b49a49c54a7c9966b0df3b5f7d976d34498477b05480248ae8ac8800767cac3628d2fbd70744a3ab16e51f6a587f6798e67a949be389ce2253122e477f86885d957f94f7fb5692373346019dde302d3cc243a94495444a6f86dd9d835ff63ff2b84a24b580181b1f7bd95941638af3fc66a81b985dd04929e89369d9064fab54e320e1442c2b7581fe67ad04c2056904fdcd65816c4822cd5d8b529a278074abb23b978b7bae2b5c93be9b514416591d83ad789bbf2453b2312023c0691f5aaf55c2adbd2abac0185d9e78ad34c2722b09542c1612c5cdcc2324f770f63df1bbf775c616f26ea3cb42f8c4ad68e3c183a0f6884160f6fe5f011b09f6e04b9ce082c77fb323b542c3310ac4a89665c4b58a6f2747a71aeab644a87eee3655a56509d32ca144be292cf9a0e1e08f19028dc91aceb6fa66b6041f0880db4530e8c2bd80923a224828976ab0cbb3689402242ddc92d08374fd0bb75aff3c84bce5dd64b6215fe1353a93ce58eb9472a205e178afdc28c0c23c8431f11e789af69c47ed80e3404a71755cec1d2a43b081ace7bc3af160fe6396dd8f1072ce6399f27ba07592897fcc42ea25664e8e3a9176ce759f961413b4878d4431c97c008a21a080635d0ee98c4b4cca9eb84bed17c3fa8992b9887282528a69b479d8573cbe6c8b383f65074ba7344ad2ba06026599825119cf840b49cc30cfc6d0a8f9115ef95b3bd87c02c863c932ce4a382a3feab064839322c0affd334c05164e4758df06c883c4bd03f4c7fbb9d615d7d34751741b6d93ca8397b81d72ab52395a06e71437046f3ddb8bab175b4277375c09fcc610ad75770acfbc60a24fb72809f426f817a78e656bd4aaf1e6eeadd6756bdaf1b1d018c64972dd5182224b7307f8ca0b90305f01d4e3fba825f43eee83e0dc59c207668957caa025ec37f4b1e1100c59265d1b16e0a25634e4168cdbf3d37f063cb117a6ae893ca0b008eaaeff72e0791bc24d535979475215a2f3b16a326e5f6f895c99021e6204b31734a697ddefb0583fce0c45b6dc5b8ac0ed184b334f03ad735b12d9759f0a0c64d9a76d0dd6a827c67208861304bbba464ae923d9cfc813baad71ac471775ccea3646c92d9d6f36e729202086ff9829b3a845177ffd99b195a1fffc8a227c5729b78581e0dfe4febb39ab39f7746fdfa9b158e29c413dc2a4c13304475057bf596de5dca23c557eb607e5306df23e937b1d31daba48df9c04b800c7c8582793cca7e4103d7408bd544fb2f85515be12ccd7ddb387ba8aa1762079a2d0154ffcdbce93e989b016518a18fcc1a5409e258c28e494459d6b05386ecc019399b6c8c148c3823082bdf5df8415735ce863d6fb8df0875e316042cda95a2379620042cbb7483cb2ab79afbcf3dda6822ccdb0d1ffa0ef8ad836bd58d11badda7dc4104ea60d1b596be5ca9b21ec7caacbd60d99a4ab2adb39340fb428064f3066a03ed995fb2ca946011aea6ce84e849f517a3b340420d4324704e37f787db4e4c2c470635b321b7650c3e51ca6d23f8728d73b7a38db91d5bb4c5ea71dffe1866b3bad51fa0a64eccf7f7faba042543a0fa7c9cda84dad6ff59d9cfd0a938ab810d824e1d6b51a1355d418d34e06b03f47236579c4760af997bc7c5b2cc5e957afc055f5c01c0cda8aea679cb2f804a4ea757765ce07b6a73b2f239aa3c42b75c46d0f0c45464d3ecff48793b32363349e30f605abf19112e229aa80f55325e237a4465087179e846b12efb062e26527844f2c4f2a751c2cc1900d03f047516529509229552f4a9f7907101126b9d9391ee12c6efd716ee650bbc89635b5a795809ba0c46308c542d53f839046b37fb1406f41d29e32e852e80a0b4f1d510222391b49dc88d9f1e64c04d8baf08de9f2190a572b6c84646ba81f54beed944d7baa34d49c229adf095c21f3907f0f9fb5cc9a120ea13ff7c4131e95aaef687215bec1c22eaff2472ce9b359e97e1584ae87f7b3a2ef8b2076ca758d71f3d24cd905eac2cbbe6e61691ba36a5c48474402256272cda11cfbf4d3e4f1c404f32a8fa6e738d4e4702214e435ad19921a1dec233b04ef658e8fef41d1e5d190ca82c23a87b63f2219d9d9f46b4fc03c1ab04380404bd1253dc4c50f6241c19bb31250af81f34328f7a1faf05fa7f1c03f96d1683fdec3034c830c4f065d131d681c518f7b8f2604411fcc0b3f508fdbd12da81235384c7d9d6821394845e0590dd87ef9ac8749970170c29e080b8e5e6819309ccd39febe47ed6719c4e6ba0995a49a4668991f721029f43f6dfcdb4fe7f1408cf2d254fe170ed05ac3a601f67bf62fb43aa0b5b3ee30267477cbd87f6f6fae6f0d96d9119e1fc477b5f98ef527a3199ee3c552acf0e84b7faad82f2bba08970ef19293c8074b992932fe43d5ee869e439c6d921ceef4ee781721542f109aab0f6eaa47a963ac60d4cde780eaf981045f3310a8d96a77fdd143e8a727fb81fd150dd0bea9ac61e5eb26f0789ae33f790c190166df3185e18c1ef2f674f3af889882f1412a6526e58359721d1ea1dfe7ce5db767883b1adfce51e1206048d20be77504edf970447885ba51f966b4f2bdfe3b778b7f63f0699c70ff9fcf6967ed1e17c35e81cd2cc15afbfff1ff61fe2bd44183e7f5237e9299e45da4590ab5a04fa4c9cbc7371b3236ed30abfef3e3c0ebf77794adaaf40bc173d1f07b30e01f7cf9fca94fac0437cdaa9b77ff44e3452f4f4b2d3d9363a1c11c3c3b48e723f846ca3bb1c5cd62943bf6e67708e1512cee5dcc343c909d8d3450cf458c278e70d1e6ed1737d2285e046e53909405bf0ca730546839918ec6d97f20f821f367fb9e4cdeb562b798e39c214e7ff3fa4a59f131d6beb4dcf8f1867d21b6f8c95d4e3a2b51ac39541d431cb074e6503cc2ba1598d5ddb827860b6e72d93e9f6700d5d1157bfaaf2c0677db5c538c079c7f683d02f8932439c5fc74c88424b1fd3787e3fc6cff73147196ccb21504dafd2360755d8b14f778b0b3509e3be00eaf9352e29b166485571ef314d5f829a5f6342b177ce9c7e738aab7b0bc5ff1b95783e340198b79491bf9a224311780bf4d881cf9ef2d8cb45d1fb461fec0570adf17ffa0d9c4f5f5ec71ea09790aaaf5fc0edade74633b3aa0e67ae07dc8397390cd3e7cc59e6fc71909f9465cf89dc2316f80bbf8771bf76e1db6036d6e89c63ba634e286a52178cd4286e73100fb8359995098ae0225e3bb3c538f31f893356b1162256f70ad777c5b5676b0046b4d42a969335dcf454eff6023fbdbc9806e6c51c67964990c0ca6c8b07acbd111d714065146c70791d7ae7a651be353bf57d06b8d777384bcc8da3ec3b8bbb65546bd9b619caf0be9c04b7f3c38445ca7ebf40841f5e824f9bb90418dda334179ed9398c7a983ff837a4a0b55167ce43fea1decc079b061f43f28c9c3f3b0606e7b130f4a1c626868a970e542a1c55abb6706f729fc8a4e97b1e4ecd4703bc50d64ff285fc1851644c4e6b1fc60c6fc50f4a26bc995a64cb568242fc403649fa3992e650035f7a5b35b43aa71c64ebcb09912ea4f0f4c9cd00a857fce4a43983e14b05f44ae0cb20ac484f866650ff78d72eeb2725a56e7d1a0a830f8df9beb640805b5dbd1058e2dc380c73d58dbbffd9beafede2c53909bc617672d2ae317fc402058571343bbe872824218daa8d55817fccb95b1e4b2daf8ece6b87579c8627f005b4a228843b2470d54947fcb7c03727ae4c30a88220c664e9d33c0e9748c9a80c5a39e438dff5e0dfd08d236a130f89c326081d914c343e08e15c3254d720225f92b9eaa61253cd19903e8e1f9f8d6f0bf587a8a20e6e0f11738a06f4b355e524cea79bb7d0d2fe64e41f5952eb406c95c2911a978b8bd788c1922401e6f4abed369cb46a329eebeb64ad8976632f384254825ee941477c44f174e4ed1c70be635cf4506e17e08d3222826e5cecf5bb5880d5d20e53e778ce73259254e0525c823393e063340f75e550b883e628d133e86dde5d40c13eb38a863fbad2220870959123deea5bac66937b41a2fe3468f403502ae8b03e27ce335cb305694a5fe7895277ae249bf00f9e2db224dac0a05120182f58773899d643bb5880f74c25afd12bd9afb8603aa4fccc95f324ae5f86d2b6e3a1aa017b002cabdbe25fe0d63f8191609d048e98bd60d81fa4d66a10e287b4f33d4593ea2e326a4f516c1321d9ddc008887fef067e4dbf0c141cd4ffe5a6951e3270a09762cbb78fc6b9b58795691fea4d478e2ceac866d0f9d350844ece0c159528aa622b37a85d247beac06787f5fde7ec14b8152b942615493523f15e53457516acd6c021e7c766ff7c6ea2871b64b50151b99304fde9f456d350ff2ab93dbe6aceae9b46f27f1c705c6349acdecc1153ca7aec9c831510c7b38afdcf29a94400c2b113ce6c32a68a28ac05ba34f5cefa54658c9350fc517f1f13f96320514d0d153f299e96d1b2e35589f1b2a06fa6b3d71d29c4d6c0aa58ea1e0ae910ce1b63f9dd7a16ba1c48a02272a306570e68c3ae0f9a09c7a6de9a19ebb95a5da0642c441720c806edcada3a01d94a653d5738c720d58417adb24edd350e2cb1daac54c13ab61774581168ffdddddba71012e3be3bc3f19c03549905076ebedc98121c77709c56ce0998ee387716db907904aaa2fbdcb8cf6a853a61d32cc3bb8ffe9d775505c1a6294e52b52d0d3121ba5db7a3d354fcf05e3422563e396cd6e1f2f91934e0d1db89e7f1ffe98b4a8c8e3ba53ca743e435a74959b4d77f826d69922815f2c6bc496e094cfc839dcabed12bf49804bbf454c487b66e660956295064a367b5995a36b415724ec88c0f11116e6ca55c77aa1c36188ac200b193bfacd48f431ada94ca6c8e103552f739913a7519836bcd252dd07d4fe819711471671416947d787c874ce9a58395c14f54151f9dea9350dbb9942ccd9309037c5ab1da2b1b1089d444c21abe0baf853bc349032303d744652ae10cb26ed5ecb61425be40b271fd82ca96961a10196fe35aa3917e919b0fd949795527a3b4b90bb30d3c290c4e99eb5872deb15034cb6fd02ad823b5c2aa9fdd12c7db8bd6308e0b33819b3f875f9e3f19fce17cd0dc6d5adeb8f64745f24f6f254c5ddb901f9dd5b958169298f67e5cbff44225a3ab692dd63cd451089c83d7a4f31a08edc726a793ca51e4c03af30691cea619a9892cf2a289934cee986aaabca466b1bd2348534dcd511c177ecc411fcf85498635a9615a9023b47e50a464142598a2638c73335a62fdfecff3f4b25924e755cf846e498c15fa48615ca330d363976bee0086b4e3441615ba41a9a5ed608ae054ecbad0e26239b8e2233540fa0cc8462ef68f8eeb14df7534c774c1c8abe51a93f818b34255c61ff2a01a3c0fdcd97f0c2b10f7fc1d86169edab7c2f6fed3af0f9e633eb7c5ce3b1be0a322f0f87b1383309de0635f89df4dc9205f7d37671e07e04411af2916c633453f24ec15cc9bc544fc78f7af699ebbb10ef9e7ea8be052800b7f56bb68257c4c407bf8b5d8b7bf15c19b51e573883937e35d2449f18584b84e21780754c901bf11d728da0870d7d7a45c2ae6715b612be868f90372e5e4054dcbe93391bd55f4413030c461af6ea964412deaa07db79dd709633d366caa91b46ecf4c30e7a410f94c2b83168778cb44b6f0c41ec8e18e7d0764c6f8e503b9e77610d639d08911fdee066a384550e30ebe38bc063a4babcad8c972a56f046a751c0c3336a393cd2083c04674a6aed22f04215b22afbf58e21c31be910c7d530235658780492f5677df8a5de7945a92ca078aadaf57217ac802b8604fae60ecfa544af7067be053dc8882c967dae3b61effba521c798d6cf7eaa192671bae3715b6a7922d8490ec23d8624454c1c059051022e805acc76bfb3f1fce0bf5de4f29b927fbe021b9e0891b18229c7200c07f85d065e4bed746fa94de0a25750fb5fe4283b57d7c7208b1db46b8635e20e35cdea4942caf0b611dde31e5ea3606ef99ce22fb33e734434f256c75a650da66dd7d9586e98510051d7a12ca72056459cf1346960e7433b604aafb0006a15a2084398affceede9c93994894a86a54bafe18a350bc6596ac7c5232c964e22557bf2f207a2c6c855211245b28fa84bc232d552bfb6505f73d96133c4925063402b766fa19534a0297a83c6307f0064f5f99222facc5d925f217c5ff5444c51d4ffdbd648d11b1974d15e3ccfc860744fca6cf86565dc9bcbe1a0d29812a2f36c85f859aa45fec0636d8400f359c9b0a8f4563af9ebb516b860076ff34d156ecc3e087888bccf8d17810d6eb8e0db8fc012a12ebf25e725091515645697918b96544bae6918cd633ab6c858619b085b6bd51027fbbc944e02671d8134cf7479cb50223ffd88d6d41914d3123fd089d6a5d012059732848bc96daa4599c17ca095c46f4162acbe175058c2e7ca89481447885817bf33f7815f13565fa325a5bddb2eac79903bd8e5e295b4ee20a961730b4c1190edb6e504ecba300d8005c40edbd6962e5f2691c8cd610f20a5f5b32a194b0d22f6d8f0d99ede2bc654859d1502df1f4ec45218aba9ec0a17a30a5b91324214cf16061e77c1ed3bb11fc16bf0ea46872173e139a8eaced493d5720630834e5c5b231d7d3fa9a78232cfb9fceac7e8d0b95285c08f0e66f070a59b4fa9f13cc6c8fddf2172eb127b696de14f5352922206b88334a0db78c55632866d07ef59e9d08e602b1e8d6d836b7b1a7238d72cce7b8ca11e29059c47c171759261078caef753368f41223a56fd3b48de1bb64c9ee3b07dbfc3c4843b5f08b77a425f1bb485c55465f537e13dcd5386f74c28a67970dd11b061a91c73c4fcc2677aa3795e0159d13a6ba914d4e50ddcb1c1bc4ee493e34635855d98f06ae360baf05993d501f965177e22957ea7cd481a4a7b95afd02574151cdb8c5d85e086eed1e14d6113a7e1eb528b1facf37220742c206999b5a15fd7e6277252c33d46646736aa7061ac4628734b59f82bf4435208cb87355ad2344a046546f5429d1018239150567d9a747b60e60b1d19f2f1fff55c17f67bc8ca6d50cc02da976afee6cad5585bc8c707f7fc9259ad80f120aef231b7558cb729a6fef71447e57b6aeddd6bc13e5f4f82d58f9fcaf5c7f58efcd5102e180a1dca2cf4806505ec56f2b2b05c5aa250f2eb28e04b3120c771ffee5aac0d397b362882d03840d3db913e03ac2fcc9406e7c80d6aeb9c4ce0a08be214db9b26fa7a09846ae903221c9c62072efec4cf7d31e4e862196f2636fe45a90fa017782574af6c7dcefe0f0d706178dad3a3c494c89a4ba1dc158a8c8dfc78f4b614d80190e32d7abaf9f9a20dc40deac5431d54bd6a5b730a183228cae2718bbb13cbd973a95ea07522184bbc18530767e2d2f3af3fb6b476258c40a48d263a75ef5d81dfc2e481e49b03c2f2d3fcf83f6b78e4f712a38b42ddedad4961df0fef1f7997c2ad00900604265280a7cf35457b7901d8fa466dc1e725c3d53b37f57f748b6063581edb2f77bf5ec7fbfa05cb3d8db5bf176a0834064851d57c4fc3982d124729e044fedadd181ebf035c22697588a8e9e25483db57a80cb35045c53e1402c86d6ffaf7946d0b5ad3fc63e1cbacbb479c8309b2ee17108de40fbb666d0a3c6e01035c4dbd0a0a47f55c31d62bbf7a532c1d1aa231d258c37bb767d068f6264174c328510ed359e58760753f2e749eb74eda7c9c2b289ca154362f4e6c0c07a03b98b55adac6bb8ff19740247556840af80eb4ecbea9d54989c22d2a62df40c0fb783ff247570fb5a3c2142ec055f4952c53d45d3aef2c2f6fa2b27e76da87302ef852a63ede2e5b58469166083020c06ce1b62788ae52bcb16fc29a71b6e256941af4b3bfe37417582fa5dfec3f71e6c736c3cc8d81abfc8cd273f129c825caab7eb1b0a263b241b525995f8c40b3a3ad1c7693fab5ca9e5aa2efab194e46992d6bd5b96e25bbf2ae16289f0a2c4a23c64fe4608d05f5d9bcda17c0427e1a5fad01662abf4808ecac568c60da63c267e0dee3a1d73a54922b1ce48ec8c75afc8ab0da93ffa8bb187aae57582f65b6a41e07f4f672cf08a70f21ac6f0f94e745c6402f11ae5638ccee29c477be216a0aceb4d5b743d70867e741393a89511ea205ae82bfc604c4062464a35fa02fb946374bfa6e469262ff396f1f76a12c9a87f86c83c7ac67e9e08598483618c43f66d837386380306d69cc4f5e7f5507cbfb26f00ef8811abba53fc9565a67fe46144f70b3709a24cb0b315d64a881c77476d6a579ab5d1f665d51814b1b2234ef81834cc4dfb430f4a343b48f92b36439ae497ee3504b41d860326a827bab02c98c03f1a8d58067f7106c023ccd460abc50ff2b12366d3f69bf8ca612a411ee50dc0ed1aae9b1574634bcb0ddc8ecc1159e9a70fa473c0bd89adf435155033313ea0677733aca31c73b19da001b2f24561cfa56a37a3e751d1c4105cc0936cb0aceca14c751d1019c9bc192b555485200f934049b15cd84979754d00c02b127f90664abc82cabb8b3968d7eb44657880098f328b150bc8a939a5ccb35c07c4d8b78a2ba56125589ba39ee19fc5ec290364d82c61950d4d1de5b8773c048ee304908c0fd11147d45aed9489d6bae3a97448fdd668af511067fd291cb8b1996744f89781f091ff65fdc1c09a789ef918acb25f7f060b109c6011196ee6720a825de760f06102fcf13ec750f61b51a97ed47c6ff3263ec682375c368ff280f72c4c8575e60c4f561bde4979df268db83f32988faad10ed7dd62a173dd23bd9d13ce67d66200439e313a7731fb66148ca665cc1df2d9d6eae3164a2a88b6e546f3b889ddefd9e91e1999ef1af21ef047bd7390d61b63b82b59ce503059ac0d707b1fb8f9a3e974f107755a7297891b601c237b8563235f9d1ba5d98f369c292dabdd8b97020787d2a74f8257fe872a90f3e1a70513de0770a843ea329ad7def16577e816b86fdb0a0f853a4c6fc492f77b9859cdd4c07b1a37b83206265d5661ecf6bb2ca0a47eb19abc15d7cd18f78266a69e3ed21f409685b9dc2635a9dab028335c1952f06b8ff8934b83d064c8f876b0311084cf33b2ebc123240e7b36a463ba517a68387faa0713f518b31629e215eab8c1b86960bfa0fe63bc3d1bfe8372b207a950c05c7061f979ec312fef6efc75a856042c30acfb8a19c555415418dc734e4c390f0eea91a256952a5886b88c160aa30f6d24d9037b9c4cb328fca42d16e7d8d75bc61bdebe91c5706f8cbbe3620124efc477eaa144470d201db015520c13085d7277fd8a5f5f3022cbffbcc05ef9102a39684662f1a880953e4daeed7429e63147e364bc954c509d81271277db43b4cf45b4e896d17bfb187a390749db02bc76166243d36581722b4b65fc36b7e174831998dcf5c1a2e42f48e709d722b08e88d9a485fc978f0036f14e2ed3ea80d047e145d543626ecec4e2a6fb3d386897e787108ded6f448b10c589be0f3ee08df3888a79fc3f36046bf32cd4fe433b8cd71102da8805ccb93fd9217010d12c034eb837104b87165e5587cf953284a1d828d6b0e0da5b40721729df88a369cd12e158360084f46b00c88d3aec9ff145d1e2edc23b2966a714124e216c57aa5818e07ddeb1703650bf350cf1ab886304e93c1b9bdbdc8e1d4a012c28847c0c443dea4d789f7a0238db5c8fb9d453beafae77e3c1ad68ed7b659cd218db7f06f8014e77fd15852e99fd4d5346e9f55741a8e1761df28a0e5e38f9fbb5766aaa520b2173e31e806be87245da5b30fe921ced4214ee5e8bef370ac3c7cbdd4331b0f5aa92ded6dd9e761a50d6e49d9d5eac2af511c25dade8bac70600f9735b096610d52328d1577694ef801fa79b44c9c33daa80fe8b4e0679debf80fed7b964fe13fd3c9233dc7ef4eeb9ab65e63ab7c58b79743737f42b9d09550f35e6e407f79bdd96d10eb07d58694991afc691be9bb6eb94f6ab3d283dda89a971066c0b5f46c058e699ff6182fda03bb54875816527d6108b30b86cba205c274edc0b9eb11d3ad386ef41ba2304a22655352c8617e8b7194b8dc75277d3f25b48eafcbfda58464806bc34c1f25fcda87d515b7520c250cc827619c37e786050a277f1e77d29b2ef331dc5c9774d260a669b2e9fc13eb5f61a467f3582471a15d9067161d09a9a697cc96eeb5bd9a07897584e6350e01a646e4aa5e7daa65d63d5194d2b51b1c6e777b38d586dc01b8679b6eeb65700cfeb0cf4159cfe338d1e04c5fd670ad1f66bc581f0f8c2fb773f066ef4822e88c2fb26078e332b93d31300cc252b37466fdab5361a8a46336f3cf1a0475bc638f0ebf2b9b5ed5060ab3b7661018ed007fa2b07adcdf1a05531b3f662a110f0d5e878e5774e142d7e29e94cc73e2f008c18029e9fd85c55c611cd5222cc1fe076616be02e0f89bebf1268d5b45f41e024c2e9872db2d791a97c5935a7e6d2b66e2090dfce48699b149217ad2fd3ceacbec48280a7e7258eb7682fec4f7cf964fb84a6e7f02380b29ddedf59823e73885a0881d5b30171aaacda2decd918a8f15b7908a1f5adc46b314000807e07a6fddf4d3443a9201f4d861d1e7abc09500595c3e3b9b37acff105acca0b01c1faf4c71ded8ef1b63db154a6cfc5c6ee1ea580d45280d59e8da831497a6f73f6bfac36dd2c7f71334b0e3a6be5d9cbc3adaa5452526b532e09c3ea318298c684079beba37382b0c4373309ae664be5477fb477df65740df72b67b04d0e90590b1d7dd040f7ebd65e1b133a0d0f04051c1dd2533290493bc88b69ee3f3e2b3f591e9f04d267bd9ce2b438b611b0cf45e370c5744b6abed45f2cd512f75fad2ddc641faa2f690bfb1bb5ebb89d0496c9c9c4e5bc323a6c524686d4892dcf6c654addb8d68ddab36bfab7a9db3679eb0db8df15b98579b766c74142a217835c4679f08ef6a9fc5173e42d6bb7e438cdeda6a58229ad14d2278a81f4ad40dd65d772857e01e3f7fef6707e86fb846bc796e321b87c867641bf35505b4f6f96ee4b6ea44eda7a97f06588d96588ad6ec47744e780b75b3633aec815e9e4b8d9fefebf8b393dbba286a28eeb86054584e62cefc9b430a75c9ead330e3969831e2f7cce84dcf14f2de736b1f286825f4cb9a053d808589c30cf3c61f9a23d99e67793b1f8e8b3f5442363e9e3d245c6f913a2c0e48eb189faef662f8e0b029e88f4b6fbe0c29d03cfdfb45d73be8e4a84e3d5574e72779814c41f9424e4c0067fc084686b62a1a082a2398aba7b35cddbd3f4e18761082f9c0b9140200e0ca6e3431d734a069aa818d7b3033c84540b52563b7a67dec11c83bba033d2c76feea55bfcdd1b2dde85db74ac8d57fcc48d68be2bf5f9b854a798b61951d7a942694bb8632ebdb9fbc63e823309bb3b02c5a6321653f519dd00d4bdefeeca5fb31e6bb1a855fe7f2c72a44f2cc638f481d95bac6ab97d83a75e5ad05cfb9d1b0d7a93b46f443782edd82165c02202761b2acd9cfce60c309e95b5af8ad4c59d77f7aa9917415f15d29b56c406350124601ea01f1095d5720fbc4359cbda72c57c5b61e8bf1c5558b21a8a28a9fd814ed7410302bbe3465010b6cf0aafb8b373a95831448994a9c1d93453705d147219397ef238b69d1b2227a7551ed0be15b12609bfed7b511a8c7d1cecf8c53e5944e707e170786c8a314a6449f197cd2b4b9d7b0404f93bb2bef431c8fc1f07a74eb08462042b50174617020e331ab397be036a610e90becf0f8d0a4c66502caf73da2a9a42a153460d2013647ea86d6632d7c4ad3917925b4b30ac5715b57cda60530c5f8116a87a7a89f25fe406a8d4c7e2a1cec29d7322cdb66080aed4fe230e528501162921a10c9f0f5d93157cb0e4565619b18d71fce51890817bd39f530280b5058507f16d1cd74059ebac6b0c84725cbbd9b17f74e14e11ff2319ae89104ca36e8ee7f65f10200df7cab55371946b6aa5ee8800627e880e9fe04cbdf7ada9b6741328359590eb01af70ac0dc5aed8273c1bc90ca0181736466ef09d807b5e508debfccaa53fc1139ef1b3987551ac98382cfcd7109486e00cf8548f969981dc99b0f468ab8503d30cf541ddeaa00dd66aef254933a691dd9f43418b499e54db2634e6864d5959161faff71e460ae8a707141aef4e168b1016a693b86637e3c0924a33a93c98d7ac028f983c52f122ba6ac2befb64b5edf75482cfb7f4c7d8e6ce382d67d5ae21aec0e389a19f00e7f3bc7113e952ecfed33684859d26a5c8b17be659d0c8e6b740bac68966f3beb453bd1311e32a327165a2ceae0983df46cb130c3f9194474e7c46bf7f5f4dab79234de6e0968ba4f16d2f52a6cb92c54cd976cc84a1e2958db7a86e310db0b0ac24c10b1dcabb7e0f978462b8ef0e5cf4fac6b0c9f48273ce2d144360cf8e3ad8b1d852619b448d4bf01b84f119a53f316d1eff8917cd5c4248553125a796ee100204329c4a5c8d6b36bb857357ea2d69e52bfcc3959a01c2139fe05111f88b136fd076706a5822bbd5f6915e4209b7b885e49997c3e5b173b1f932c40a88fde3cb4bb73508ae369a23162c3c138907a750ffd2387c12e99bca952b94ba469b13e2c01cd33416485701586e5316779dac4961cef5babf706a90e1cf6461a89d939c930fbf497f1556df2f9233368b1a79b1d8ec4f29d5629803bfa6312bcdd8cba48970d92e74b1466fa47891cd1422504eca9a438d2b23854fc2971d412a1f4b84b01a06249d0aaaeed28ea1e2216cbd03f65de57ebd13bf58bfdf108a66d2a7e769015ec9b201bff10443864f6bab91639dd8315f9c9bb1aecb26032fb65bb1c5dea1b2bdb84ad0beb7cf376dc796484de51832d3a6055d30e54de8fb3c4fea44d784a92e4c33e7148b0ca8325943b9a6ddc665fe7eae2d8a49e078af8498e960cb1423d05656c6fbb7059f10288d67543f46b6fc7ea9b0e8b518bef437dcad17ffb8e2f63ee4d18939047b4a61ab49389d0bba4bebe29810daca5dcf927d09fb13f9352d638140226878a945650cf7a14d2a08b9220e45857edc16cf9c0113d1c3a1f95319e61935ce97d98ee85534f18ad52a98150920760a5eb11607c9f3167e087df8b58afe41fc524c3d32117c7654d424d3a7496f4bc07c22677b9ae3edaa3453457632746071c8b41fe1d65c51c2a29151bd2e57d12d1fa21aa6d78b49992ed759914c298e4138917e67f1a49cb425cbb94fe111db70f470021cee1ab1977e5568e9c0ecf49ded764d3852a04150e1e55cd177e8b856adeaa5b26891581caaef3284e8f838b98c2959cdb193b66ad046559d546882747081ed6d2983c9bb41050bd3639a743f98896711bceed68b35c6661c4520d4c8b704d5a45231c165036cc08f06f41b7985879d5f58daaaadb85c67897660439d68fc59ed7ca23c5e29beebddfd3416cc7011fa1f839421f0cb03c62632007f969a0bc825d1d8db4684e865338dc0ad177f70ccd1cd3023d9536e6d4846d70417fb21af881cdff073ee34a4321f4353f32646286bdf30dd5f64899d206928c4a0dd6f33d6140e4d01a21b1fd16c07d2170489dd245fee1478782aee1e00b0d84347640a0882db1de91c87b3241d2a5957efa08fc5abe909e91681e3a41966c344993ef9b89a1042076c0c9ed9e024350c3801b31ac3b6150503d673839a858af2baeafd9c3c5c388510c90784fcd708e27d50bb1767a74cfd8db58e0bd67eeaf0d00417c87cf84e0c1e97900700a5c2a03a653ec4ea05ea9c655e63cda0b2ed8ee8716e8e572b95b631333bcb43c0f0c53ae5ed43a9a9d6e4e2263eb06aced8e17f994d3f2ff644eb77b2023980a4e3fcd87eaf20e14f81626a63ccda8b4f48ed7fcbaba6fd408296a30a94b6cd2dd385508582f4b4ba86e67784f1574c2d5b6d6030d6db14e072a69d4c24503fa02d331fc8221f662eb8fb5cacc473be6b360423d459654a87bdb9225538ca5184cb5fb995580612c1faf405a9e6e41cb5a1b4a858756c355704317bc1ebe9a79d1c30dc7d4b3b59d7ceac3d607f2e9e3e271584dae1df4432036b6abe4316b9fd1f19683dbe74ea8cfdfa62856042fc5f3de9ac13307ed977319adf29974141c986a46f6893f562eeb72463397776cfc8da1273869bc7d76a83838bd6321ff53a5ffe767286f3b77375b6f747abe4055b91c11b176aed6ac29be7b91c32971bb530cd595705f6a33d96e57571b6584054f2ee9e484bc9578c24420c9cd681f79ae0f795799346b3d34fd88a95ec8b51a9b3a9e05245893a721e88090c788ca16f3824f5e6be5fe14e4d0274b058c928208dd461676ce5a1de8867b214ccae5156733a4c4b43818a0a47e8f2ee283dcc54211bc6d8b7f91dd737cf06e70efb9bffc260bec09e40976aed384e45359f871e1b92dea4b9571265fd2d2099630c57d0d322a5e86c12bd592fa007b327a1e281816a7e85f0cfda3cd24edb27f16a2c42d342a9527f0517db934e45f414fbf086720457b8f0c9e36c04438f048616b547df19f58ccff3e9b555841f90b4d17f2e8a42e80f3be03c2277593b065f4fa5cea0a1b503421d227b0ca6a19579d2eee4b9ac8233023c413d116d94f3075a7ac23c60feef13cb484011c09cdd6105d05205b4b5429dc65764bd2e47898807cab354e17b41231dd81849d0b04094416c20c7b0684c65fd65e8d3ee988b86864a7071f58e58c2ae6db80ce552288eb199f5004ab7b4e9e41e0a3911b542544fd86ad26a59528470c3774a1ce71c463dbf5e2be0f18655396bfab8820be25217b4d4219abae733b89437b544743053922e176b5bfb34870dee20b84a852ebfbf0a6de26161f9e7042ac4273ba95da9b94f365c9a70f8f046fa85c329ff69efdb06de81b8f6b459838f791a1da2b317b8e3a4fd6be33af364d3e09a167f4e4b56dbda793432d1717960321d8a23a999165f29d11d71dade6db1ddace6512ce170c352f0c87681aea09383e25f9435eaf055a6313dc9e4a15481617e82119c2b34eb496a1d7a6339872f323afe8317c05a372031663a4a41f01dda7c4267f530b9e82dea4d58de591df0ee8fba05b9b8e87ef0b8571973b5e5551a4d58f54ad7b6fd88dd73226ac4047d37f41571c26f041b18a990e8db4180169d426221d70dd2c5a10394a961074c0cf27f896376533484f2176823fbcc1b2b281c1fc52c2b368ce4905f38f09454f54aeb0ca926504392d1a4834399189332d4ee75bc64d1fb1251f8b55e4c5c616298460be291ab986ed78d99cd29b66a9a46f2c29f445d3116579c7c655c6d54c035baf0d5d6eb83be83a95f1ad286e4578d7d26c8b490d7bb2a6e4cd6a08cdaea5890ab6dc953855bc001a9ed0c7a841428a692da6ec243c32c15f3a592ee6cf072c1bfc1e671e0fad43871b98b895154827951c394a009a0bdc8080ce421ef34a2a028470f1390905f172dec18c495ae2ade9b789174a35678d3549f2e8d1c7d42f49d8d4acdb6b29be6ffb1436e7d29cb4c1cfb1fb3a76312a8899a93f0706bcdb4ab569d04bc1975d7e22b014f7e98f3786180c5e8d5c1abd91392aed7caeb3b2da20ae9c279c2bca013a1a8b75863eabfc5946aabb1df5213a95c517acd2c396e6dbde5036ff36ba826d00370a88c64bd2b9412fbaf779f0a08fd01e6cd8c18dbc3f18ff740597d8f9772df8c654d7d392d09252477f1902785e99c6e2681a56f5955a8813a084e4102e104e18cd0330980e52fe1f4035569b036027e091126e59840f999bf8206c78d0e702e2a338e4bb4d0c033d70ba51c293a490e8064ab82d01917fa4d77e0242077cac40afaab951e1f7460b5430a3edaf11d4486941e06bcc7c8c7116f6417f1e2e78650b5489aae312adf110dc36c75b0589cf9785a5ac6482e3d690e0211f244739080212ce40146b0b8c1151c018be7660b924c1523d3af4dc80c6ddf2dcf26c26cc44e74d9cdb73cb4b811bef76d760e224de938446e4cda2025b018f7065b4e487049d0051859830b9ede40f85b5fcc0375bdc196673d4f74824a1411018dad3044ea55051e5aa5eab0670e7efbae48ea51b25c570e36b85274a97699fd90219fa8602593c248166e87d2ea8c64d57c796f9770e917eb825d283b4153a5f9618b84ba50b6d070b425f4e8a77b4f158a82022f67f9b03fc1f8c38785d418ed71c92c776e475ad9a6af7a922acb38378eeb13a66e80efc89c55d8a255691a400a427d61b7e1077886b6000c0df7b62c784de243bc727b61a15b7d08051547aa3f5bb2ee021089528bd1f4db050c0d7428facf10a5428c35551ddcf23bed8ced931b86c4c3d2f4df343783448c716edcd4df67e20c11a012a0e369527963febca08acccd86fc6085877d18c7ff152604697d278fcf97cba1882c2c3e84ca4c8aeb1bee089bd770ad06877828062386bcf6ee7796b7706bbb69dd29091ebd347d7a58046b67b9cca346da21180453e8743ebbe6fd362c9a07ebfa87f2ffbad7dd960f1e5a9d22fc960633ccf3705501e5f9a9d08dc6aa8b0d072db296365f2a6f59f99097efddd715847c67be7c49bbddcf6a8dea79449c73c669a2f8fea32488cbcf35b0d803fb37d13fbead5929a3ce05a3d76cee5ec100d7b0f626188a7470fc85b0f4ff1f592e4fbec41e043f60f906bccfffb56f7ee4ecd8796a48683850fbc1d20fdbbd29299962e936270afee6fcc7f236cf0e0096f6970c8fda531408fd5e3a902380a51e07e0c9d761d05fbb1d1e9a56c14d8cefdbba2c8b5ad8f844aef138a5a9a58ca91b96816e2b3c0e62bf53e0321612c9e64420bdf37bca02cd2cdf78f60664f6f90128f5d55569e67fe1e9f5a862bfa7f6b3b069db9f1dd3d6fcdccb052d8a957aa7b00f4586f28da94da704b2c0406e0528de8cded497b0a09801ee646bbba8ace0467b82399b84d6700512e91b911f5ada6446d6cb83f61741832a390dd177a9fdb9ea47bebad105cd303bc3588180a23928af8e6df208287b03fef6da9d274b96a191e0b1bddc1136e16d3960f240f6632487101274039a5681923d406c73768bd8ec2e447ec841ed6048e37655004ab440e3284e714f4b0bf8cd8f3ffe6f3707e1fd5c95ea5d863e2074d78ef0209f2b7bf88cf2590606e8288e2abaf3c92db071c3dd78293f64b030a218b8bfafb8fa868946478e26c07d12200505948db3b00abf45dde5513d22863a14c70757ca394a15a7f558e9d03a7a05120e400e489883ff247aa5795f01042b592ca8144db5e9288c12df9bae1a43692bb45fc9f218c460ad70aac1333720179a957b42de510481c15feaff5e5c171670c2bcb7742ee01ba457f5ea4bb3d4c80cf9d13bc1aeb1f2b559fe09e68a3de0ff91ef230f12388cf31e2d4b0570f95d70ccffec5c3a69939f598174b19d16ec3dc615a5d8f5eab80b65faf3db0f5f880975a7382c9d21cf39cce6780bc842e249c50427bfc55f101a2d56c9fd5b975ab4e96e6d03620039793701263b4db0189009c2607e9f8a2b968961644c02648370938fc777e77276fe99785cdfb5152dcffee36c9f80fc4cb42d27797c7e818eb3b2201be219a067af033be32090e49044fc7db8545904a897a4f5df5c1998f4d2ac3f8c0bc3d3f82203213b4eb6346c5577370b9d6e94101ae095c78584fa272c32a4e9e33397ad273e3d22fcbcbe4d8e49c3383e0237a73c95e08be58f53b3aadd4d8f743806e064d8874c40a5691142feb021c1e889c457849121d6f4874c30fab86e83207e0cde39f9abe600f3becbf5c82bd3d91a8425c4516c284db98c9996b672ae0d50bb30e28443fa19384f799ecac3cb6bcb1f6b5fb39c47ee25bcc8db6d50aca5be5441dbef2df3f1add02462c3d1c4d8e76e7840b1b23017af18ed0c92939f3eb6dcc516336aab7d31bd37ddb837795f607d700d8d35cb5c4cab26be830d18900209e4547262e11a72f6507af8b2354785e7c8d03027df6076fbed42f1c7e577a52b5bfef7b7c9d32344d82195a5f1a2cd2c27eb7e8ed73200681ed112f1ab8547caddaf949043fb9d6ca73c86ce273529260c890f123e481738ee6f2e979bc9747f4d491bdaf032ca60ae12f163be3af029da1bbcf54b58206c4d86c72c2b544a3e842f11babc660e7472ae877bea243d366841b19cd6960333486770cef4fe4a3429803987fd0406b5061cdc7469faf7a0f8861069c2ed8241b2a79610ecb13ad1508e5ec46470f0e3cce1ac6625dde41c3cf1f0052f0af3fbdeb7bb9671240154959b6c19d61f28146b3fd3fa52e3cacf69d9536ff3dd52e3d24225a7eebbabd69bd5596e1ecfbb2a851d8b32ec22a99e682257d9067ec1127b1cd20fad809c0450f966e32dfdb61ab7b3d9f14c05361bdcf8fee2e1dc0008176dd88911bcecf8e4dbb53bf2bd162176fce29099680b7481b4cd3349dcfb6ff2c2544369b7f56f98a6a26b65e65313d17db0b2547905b238f01fe45880b1be2441333902dc15072223971452f969189a920a4a81c12ab535fa6b0d8a1e7cb470a31241ca16aba5444912295609d5137bf3275c9e04f4ede1f4f13541a18c7f7c4e6b217b3f6b7c60388e2de350f2d51ea429dd70f0ffd05be00a616dd0c65ad5f4105d5b01663bfe81599273624bf680abfa90bd33f63ce8b8b46bae95297a49803ad066e39e4f05ada03d4ac365c32463fdfc93d65bda754c251fe2771041b3bb12fc7a3ab2194205b1ea150f5348f8905bed5679516c2a3305af5737fec21cb22f8bb5056313b1e6081cc78be4956008db5331e9029edc5505f7ad15f3fade4a4a4f2de91b33cb0e6ff20fff2b7befcc82de26e7810f67ee8a437014344eee8007c76450e58ab15eb16087412b5f4809e39d0d2e7c9b1007ae3b31fde6c1aaa8e257e90762cd2000f8f766827ef114f53f2f2af9dead75606dbf3dc7c360318b71cfbc95fe1303aa172d7f97e84cb4d03abe02a74b6ed839bfaf31c343829f3c5a07099783895f735ea1bd69f321f363a4a5678331a1b4f61d6729c7e6c501905e28602b3a7149b44b105a8c4644717e3f9c325ea85af2bc311ea0ca8091a80de4c07138057c6caf9d4d80eca4f27fa355f16bedfc85df5f9dc43b53b0f058c38a633bc16d8eb8dc81b69b173143d1b15f2a1e1a1faadfd4dbd083372c8a35d042d3057310127a62a1443647e3bbfa8e82e565c11611136f880b2030e870518494363ffb20ce0eb0a6e2a7171269065b681050028193999763ed3ceb14fca79b17339c67673db3aa7ceb85881a7a27543f945f451a9ec4c9c86ace8a84a5de1a9ebfcc298cf3dce81f380ebcdcc5e5ff8b11e7335e77a7f991611727b4ba124e17fdbcb0fc97b157784b422b0bea61f8d6095c088d333bb4fc4b147fa7ee53f7f13e86e33c3b1b58618a8ef5c85bba8a632534d87385c283de451c41fdb49452d6dc284cd059d81379d84f6710f465f7f014cdbf213888e2dbd26dcfd655f31e6aec0ef5df0df71fac09a94d06afce5cd23ae732f3960b9220ce0d7349b10766a53951a02348e00bca528bc8ddbce02d81372ca11a9d751a0fb290b936bd864f19f87dd02d3b5100134fd8f86d05a53db87e684569622180e579738a80fbb8ab08dce17e2cda7449ac2ad85666c646ce0d7191bf4242c3289f25c7af22337ddb05db5010c543692ce7325ab08affea98b542eaf7d286cfd502b34ba69f40fe7d1bbf6c270183fc6f093056a0b583945d89e39be75ab40b563aee1bc48e89680e7ba4f5573e2d1de6d9b20e3ba8dd3dacc6c9d4918e3652ecf3969c6d0058eda49ce2ca830c7dd991612eb161b9f60f15656e4eb3ada95e3ae02fd88411385c16deea0baf428324d2ed17048aae1abb460e440d049a1d561910a1038e1af0be4c338342d185b6a93cb207cf7d857407a365b7c520ceb275893e28e602ba70ee1c0c27a26916ec0bcb21c4ec43f57166ae8a392606b6f295a1e7f37fa4c58136552c92e9d2306f2d4c9085041b2377c6cee603c08ce3db49b49307d9b547c0e447196a377d50375f687a41a2e385da0d03e31b1ff59c0b63568399de009b02572fd42e19dc57cfcf0811e3fca9ce8bf0e8dac16c3f3410bff6f921d2a9c0644463667bd54a7cfed38d1f725259ebc68665ba0d918052485873dcdd203678efc63332d2accf9115e885ea7eeda49a7727122c7b1cd0543f6c9b4a868bc8e14962ae140875e2baebed072283575bed8d36dbf59e1efff79ed3d27ec05d09c422daa9ce89c1bd1670822c9a9eff37f25b8554282f387a7c78223e90bda0d0619a4a9d8d339e23b8ce52d53f20777644d49578ce345e1839688cb0f3996d49dac68d75a6e7d8c966d7ff56f4f17216aa92c952bf9b0867b6c59f02b3d28cf869e317340aa7ac45c5524ddde9b87556dc29d55809ad7b4436b35dd2894dae2a6fc04d537d3665b78dd5c6b5e5cc8462500df71ccf70356a0889afbdcd9783211299b849fffe2588d0ae2d3921959156191762ff728dc9467e886f8f8c480067e0107b90d632fdf487bec9a7ffeaaa3f1db888f16d8d7ee2c2a8688af79a6ba46f4b06881291d8c6c16f8fc650e48a9ae0c7819dee75a0cc444155947001c52ed1b5cc273f9e3b5a541f629dc394bb33b40cfd52be022ceb8e6583fa3097a29468d4ff4fe8065ffb6dadb0eb37cd95ea6f639db3e0a9c19f1a589dbbf0f3a77c15edc636f8858301e01d6923cba4512769647b4a63e814186b975db8cd191014241fbc8caaa69ed739a2f166312161c750c699cbebc9ca288d8d74a29acd4ad49a7f9092dc39fd8ec373c014a762352d095bf222761387f2e956249d46aef2f9abc2719c4fc58522623ece8b1e5b11db2202c6da8cbe54ceab5a1d7df922b96d7b6410240bd3436d971045989320c74709bbc516792cfc453c0d4f84b05d0ae96eef634110d8ac2622029efadc408d41d0b118ba0c62130eef353dcf938b7e354432b322f78cc3a47b5c6110d9b02a398e469b07e33d230634404e3af65115348221079ce56c64051ae2505b95f54c9b4d0e4dea8e218deb9b38ee2be60746becc353d89cf6bc175e962f6994ed62eeec91850d5429992c2a783592c7d1b28132f8b4ee8a8cea22fcf1a99e36d5610e31fedc6d98618ef59a80d869848c6f1c8cb4a218bb328fc0f87b5fd36c1f779c7c65dc75bf0b934fb5c03b8a5b83928651bc996c36308a6c9670d2aee5400984b76a8a7b9814f04aa6ee4fae5de308395d6320985a84a83072f02403b3b8e8696c0fb70b83100eb0b4bed01eebd5ce19cbbe0c17ad47fb26bc39e1a4bb7b183c024bc7d48a423cd71fff8487514a2beecc8006f9ce0616f22292f1ba71f0009cc24fe86314dfab9c709a620b3fd116ba6d284f1a057a9eeb6bf2002a10614aee6add1eb75ff323aa784a3cb2e615ede82e344c804be57c76c4d233e7baab476e254d7ab6485cb20a7bdde13de9935c1b4e5d5acd08e558e93ff1d725494ee4fe694c6518ab125da9a04b4c2c286527364118309865c1d3dd4c5f155c882d606e6ef7555f9553b43b192836c864c038c2124144e5f632e30d5310cb6635407d6ef8027568c8df152a8ec0902de1cd2ec3383b4361c9a1bad081a88b174da7d86268d1822f2cadaadded8a688898e1fb8762b00ad48942f7f32a28e980bced32d729cba5573f0b3db44952c7a192cc56232e830f43ab4acea33b520cafb17ac4da247153c8ccfe86e4a888c956948343f45b52bf19dca8511535c09d5f1e5220fa67c1a9045f1641c6b1a1c317f3cd5fb34545ac6d405da18bd3af36c1d4c565af3efab040945abbd7b7d0af0c72290cab03d2a16367387179fd1198622b87172f3605bcf515ae42560cbc9f670f328ef0a2c52f54309369e226048af96a596c0c3abebde913dbfc0e9f02c2cfc8b7741d036d0f1bd6044bde65c86e477993dbc22203b2dfd37dbca034628e6d8ee9280c8e724b943037fd0495682108a6df4baf5056dd991831edc49719d80185a262f3b5ec04a06864cfb61d07fe3c6e24424fae25ddb6508df9772992df3a507efba4dd14b5c52d59ee2e0a302b9c6e913c4c6a4e183446c6a7a3f3d9ca325f62a86fa9347161ec83bb1cadb148da9b42896e797af176b60a7bb2cbe20cf2ca056250061d6c882c584431f1f6c08a482617bf48ec9656c3442b5671cb5a0211c6675bfe7bead6ecccf20f02cf53e05c5250c4ab96db5a67239e7f8acae55e1a47ad4858151dc07c3b2fbecca62b896d75a69b08cbce99cca1725017b1301342c617c0cba34d195333209951015d0ee682bf14141b60aeda17e6fe2997da65adf7d47e7e3858a51533d4dfc3442d3eca21a5feb752b7eeec9a0c5497ec67fd1b131c5b6f8233ecce4a887982f8ce9c35e32d95d0e565e95f7f4fe41f1aa166907a0b1b3c59a72280d6816c68bccf553600372cad8ea56142f00e9b8fdc70f1688b2491eab0ace10354a284873eed197d6ec7117c7c0fc6b83d60a4056115b96d2312eebaa7805749ee674f26062b55ff71a5198fa46db34a80cf6f59a5b4cd2aef36fbd856c010716f49914cf5689b30337694186787022f7be551110da7211da2a1607ba8be6dac2d86434b9add96715a1d62b45fe90692c8cbdf6f4d1d980061ca2241af7ca82ffad0a6361b11d65b535446f259cc5040804856b2c414d11e81ceaa26702076756bc847fd96bf9e1a96997b2742eea9219cb233be349909cb5a02f3b236c8a184654ad8475630a8ee597efd6e8aa9055b2b2ac97b4afd7ad498f59afd3be16589ed3537c13795c1899330a87f1b9fb5a046b2bf45438771eeeffb0025f232ee6d77723b9d331a56f970a3c8d958ea5444012c7c40387d4d3b6345e74a2d35f5342d919ca3d385b00734c43351202f7359a4ecf5bfc3aa7b068614525eabde95d157481fca6a20457c0fb588e54196b0f44c255f74660c57436155bc89680d859543baa8069a4eb957b901eb811660833a9d70098ffdfe3b118804be3619014cc3ba57dd7353c466ae0f5f686fe1df1fc1957e55fa1fed6e4a435f9a5647d2b4aba1316f161e996d0de1d28370bfb488407bc2cf95938e7fe0a09dbd817682a70cedaab4f03e2e3f09db1e24b707c273979d183ac0566618b867648d5efa233a9ae008fb70895c5984840d96e29160bbad83ec6724d74487e90936087b84a74a4162e04d89d04b0d5f8f0a17fda02d34a232d7aadbd68c811322a6eec025367e4839ee1aea634e4b29630488d069cd798fb5da57b873e887b2ff24056f66fa23529f00a8b989a7b69b26d9a3936977c91f9bc7c9c80141d97f2151c0ae3303e5123cd58043c5245c0e03022407d2b4146ebe0f960236df9846d1f5ab7de14e4aa48c16f8f928e20c533e6ec3a54259b6b9092581dd294f5426ca8bc43201dd722523553dd640740319b4bbe93bfbcb83430cfd3b2cc60ee6383246f4cc1bf5822a78d7310a92a0c1a497e603741f78db44a4a09a470e6ad052900c37e40db10e2d38e2c8069a6eb874e5b301023fd258507041150b17308a94fbe7cdbcab512743fc9c9df32f6472a72c8809511697209ed5f260e8bd56604b7046213f95ca9de0a9e1cb526d62a19231ff4c9e5f0c75a919c1732438c75300af9a1ed1aba3e2051a6e84136192e4cd0d4592a772b978d6352f3d8f9fa57f2c6ffa0809bf9c32452b3db5e4aec00933037aff654a4640e9d8600845006d3d928c9d530cd6752e26adecc47b0b056cf0a2bb233c3eaccafd32be1d23984003c9960e946329cd13e9d9c6376401b6ea96bc4328f7126fb090827c7ca73a92049822d6968a648336bb2d2e5a928304b1f3459cd3c11ea5d24fcc3b9acef1b5fdc57d84566d56cf8048aacbb32be748ee9b9136ef6a00b4d286fef92a2d3e408fcacdfe094321645fbf50fe48e3b720cffcfb3a17ad22c305ac078e2675038018d3fb0421226502a0e2808885bd044c2b78e9d03a946537302ab009b90df4a3c86d48b5f2644b7b563b5a1006d18131949e46c5bc590370e09802b0c36940ccdd74ca7e28eabb19b749e61d03eb62ceb7c242be6711dd18ffbf6f4852c0a7aa2c599208785078acb4d3c7e23403ce3494de7f0a0088d37954508d3993c79be9e138c9311f4ef0d79b67024ac3c6f1751e7ab21f5347b2f53d1c082b24d5958a775ea6a4c3d5a5650bf9ee0e2dfde83defe78244f4301e24f13b6722b8c5ed97f556c100b241f9c94b8b338b94407cd9e877024ac16156723243cc298704e73dfbf82d7d72283197c009b448a2f318a5b5fc75bfd5cbc5de516c039c2f2dddb06a1260eb9832456e8de7d3c0cc51da176acdee7bc4c227173690927a76c2b13ca9afdd7bc79a155d587f23ac0fe9cfb332a3ac9ee3ae7161fb558903b45921c1eaad34fe80fbda1b7680ccc064ac97e5a46046939d578e765edebe80d7a5ff8c3851d8d325a681c93e99e2ecba94c20670fb023eebfd1b591288da909e029353df1dc663eb27b32068378ad8a39f38155b0c293c5417024c46b0ec74e27746d87b431db03d4630c7d2ce6cd7b8441f7397b734c1e70c8d21c29b28d0db00b64005580310bd1b2212f4fec35fc53ca31b535efdbaa33b36b6854dd1f6db38867017de13ab688ae174db07fdc4840754e89a3a8f7f24a81807ed24869f9dd9193c03463ea3107186cd1620686fe6ce90615ec7185577fb40fc4320603a917eee798633d324e21c4cc4a8860b1d045c3e7c27067789086c6efef9d554623b40272cac7dbcd29985d52618f4189a0600c4661b15271b6d58e4de831417d7e537197f645cd81a069e81ef809fd76a304f034fbb966108ca1d24a7784f3b7b8591fe58f5c7ec86272567a8e786507f3aa67e272eb7f8d1b063354c88f0079e010db85f0b6153afd5756f2b18016eebb4852f35f9e7162ac2372862e203fc37e89b71115f1140b7a198819e6b0865400d61c4b142754f7000d5e01c95d69d1364c09a28ece3965fc389cdd2b53f0387f0fdcae6fb5eeefd9e5cc9d0482e08b06520a3cc4c60fb32f214389599e901107bcf9a47f99180e5074f009ab16d14bc311018ca03b0ccecd4f60804fffa2b9ed59297ad24278a7ada31e0ffced5b86033ea49029b8214cc14ece47072f42b1214d99076351be3fcecb10ed13368c7ca88bb6e93505c4616a38c5af7d10552e6785cf060925f0e7797aef8170f021fff4f2cc550760b71f63efb2679e6f783ff3e406bf147d6413ba260edb661c01ae6f35bc8dc45ec7f030959610ed1cc62c8874ca4fcba8b57867067509cdf167b34ef5da8523515a6c635bbfbc1b5ec1ea1d2ba241d00d5feae71bc9a4f160f1f7390d99e255d402c197d5b9d6afdfc0f546eab8768a3fe78ad66b4ab2616bb9658679aff8311d1dd7868043e2655a749e38c72d01faa29f571d1003350209caf303da7f3b19dd3d39c06ef60910fcba537180d79e6571ab0b47f16b92bcf077cf07c56a16e0d80b3f26794ad01c0a1d19927cb078fb25e5296fb909d118c933f819d743e6078727619fe5e727183fb5f14ae1f85a33fb968b96a3d7c72e063ca0e686b7ad37215049bee88fa305069740ce0fc68be65fd3e42d042ef37b79cf3fb1f81fff0ee3c7844b4341d684effd5a8fc89a026b783f092645a9e5373317ccaff2c64a67fe3497bf80b5bbd6bb1ca62eff8a19d9311d1895095cee5bd113798e497fa8f6e477eecad06e52913d8b75c921cab02fd6c263f0e42efa46bb951d47300bde06d6cd6a686748b7de24c123bfe45be025543a150e6d2fbc7c7dd3c17668c0347769f48d703f972c712af4d1324e0c8e30f4651b0567ab4fc2b3ff0ab974bb0d571b259bc226c49052c74ecbbe4b5fb37afe4b703cb5948312a79b3d8daa1d8978f4ecd0c8c2b7a74e0302db2314838cb4a0b33261c5c302c99a21c7409c6e95632e548d1f324c02b8bb0aaa6d22d9ebd3c9a5fb1e50e17a291693a4cba87379f953ff5b3c441a68629ea512576e4fa52d30d3bc1676c7da150059b4d47529848403b4cc6af5a96a90ed8ff66d88bcd41f2f32c475585391a3d90cdff0ada764694a931ef6c4a47e6a0646bcb1d9118a9c6c6714c92bf63dbc6cc6de93c257c032c614513931434b1249cee3a0114aff46c1651ba4b5017486cf10eccb579b871799bdef68869a86f3fdd04cdfed6a5f4f7c54db3a11d09ea37927a1daedac52fde9519bbd8b84fbd5af71dbe04f2e7bce25eada84130c3d1411fe3bf6f1287af50b596778dce4ded2ad4afb53803c2c5dcb3b3268bc0268293b70857fd3050d60436352381f4955af88c44413de9f00e3c6d26410218f755a255aa97727efb617d7b264e26e8599eb1a31caafca6020f067f251ee748e2fcb5f3cf18513da11a241a81cea6ffc47c594ea2dba2667c3c441f9bf113781b6f40c2930da29ef3b0ac9951809b81dbd26b0b50a0c38d0263b5e6cf7e9c8e43e994f743978b0ca0ebebb804bb8a3df37e98fae70ab2e93bcfb5fa7985328f6497011c828016254bb7189e262312c746f0bd62bdc6ec33551a40a4c245b8fed9a247abb8a4a9070642f6ae48eb2c7dcbd334828d35c86029c11637cca5cd53fb6c3a2807b2fd341860029491907d89cf804abbdb187fdc18fb9bd1ce5f67a73bc365ec6e1d7d4bd819a20d55df4f5995448ba4feef4317afced441dfbd3b45c1378b81f3cc0790fcbcc36fccfeb0df0b332badf633273d28e16623f53ded6c5bc4110e826492e240a7f4db66e8344f82815ccda13f30337f57e167122f32f371abdce9c07133435c703f79c133578024fa80ae6900b66438fca89b1779e8d635eed178b4b1772b558265b1531992da5fa16302cfa07b46a21c6ddf656605e31aa219fc4f9e9d77cad815136f6847d84e2d95f2adab6ae059c00811d5d5e1b739283178129e849d6bac04bf8a61a2c6b339c612be1b12ee987b236e4962e8b3c81473a88e9aaae51f709d6a6e5506d9b41343bbc2583639dfb153268ac2666a75d82e50fb3164638cac33a8841ec05a796424f1edddb20dcfb353e72ce23acbf16d4dc5a4f8c54e13d802cb180bca18c9183ccd1720e6b01a1e2e849bed92e69ca3149295d76e4bc3536134082fdbba727c2763af50b1b84077edeefacf984acad2d8df31dd9800c984295c544864ae74767791b4fdd4f4a16727f6458dbffd4e4bbfe51d222cd88a4035c076f9b4c4e34138b45e3e1c9b2940892cf30e599ac57228bc16d768782002ec118d0861824fb10260ea2fc5051c4c2785323448344903d2aca7b598acbdfa2a10dab8319659a0d4611ca8c482a29646b5c33feda57aa5112aa506831ec5ec410e7f69195548eaf87060c4b731aa48d633e0bad71a2b47bcf138b4663aba863aeb2f4e7d65ca36727b1b82c36566539338e1299bfce6ab018ecd724d6f00053fa2125560ca30ee1fe50e436c9de57dfae887d08d5115de756a9c514e86d2c74123f4805cbf054224cad1a28ab12a3d57b1bc9c9523421801153090a350508b5285a9462c6efe8451619e9dc01684310da8c0ee55109050c54804f037c1294843d65bc2d65d5c63591f291b315369fbd9e82422b5acc7724cf64454293012d6437d0aa4bbad1a6094ba04ac6041d904bc397778a9c5b6fccd94f1da80cf69365ecd85d881e4c8b0a2360321f74f0e2b3fccbc011c79fb28c32a988d586cfb1d73b897c113f2413e36cacbc1fe25263b7683929ba1b03d052336f12472e8bca0d4e1573806dddd4db8f5555dac6caca40297088b4612ba90e0ff0252b08428f38db33b7d75d51599de8ba087195b39906ef0b22b321ffac66172c5e91b5399023a9f88a34057b672440a098a6be91337f5e676c985922d926eaf58b9f01d4e986b52b8acba3ef4279a9be9242d1d6cbf337cf2aa30c721c0905de9c02e19056a16512ce0b3d3d579d404af11526a36eabd7a083d166b1fc626a53de85e56d4b10ba9045ba20bc7b29c6b06815368570b727b31d32ec1c4d0e743446797b32f30a8d501b5561b897775e64c453b1b7b42ff564380cd422f141800887b0698b0bb0026660f600159f92ed1a238f52dbc42b37e99ddab981390ec6faaaf167a99bbd37d87930064ab87e710138458ac01510964f8624c9dae77bb283744a3da846a876514ea7c0bf3137248ec744a6aafb6522db90222489b7eda90c0b9d6093033153e73dec541e6a2e50644627467bb6258f9a6ea6626cf81f4f7b19424694e5d9fbc31c02885502dbf233a7a93d7fc53865ccb11169bce715529f6aa0eacc0d4605713cfe8d08598c3e8a81d0fdb8a01c1c99d78e615bfa1a22834a6c6e817020998c0aebcb2c3483fd39221ee7dadab2d5952e93833b870fc7225e45284c770f888d0a963d91c2d4d15d7d39391f27098e46be68747d1d33aa417805f9d95b7d899d02140b94c81172fc3d79ae1dea3e03bac4c4571c0b72d1a420bda479613deacfa7dbf6b4843c1d5116496126c1813208356937d60e12d719bfe5648a0f7f72f07408394a1f75389b2d7fe0aa904f5c7c0fed77a28c6ade1bb1499048a8f02e223bc5fa9b2be0291496a7835e1b7b8b9a3a7759f7507557196709c2f2d51f9ec88b4482afa64195800857c909c6d006d2d7c4ab64533f26a3f27914817209420e80ebe8f09a20e27e16cd1f019ce78501b2c9c731fe9c860097b491a583032e8646925b8bc9b645369a25a093e12b3083c880a96d5c5258eb05a4608f0399be9ece916011a1a400ad420f8236fa543c26433ea938c19c4531e05252f99e440010c5d10b53cbdc2ed814ae7c251aac8f8239a9518d9b24ae7d75d16271c5a48d271040a7ac5e7be4d5c2ad130d5ecb8e710beefefb91bf2527366058e12f4fb514f8bd8f5a1c44a0857f3adc86e2f34eb29d081b1848e7ea62c513ccf8a5c0258cb20b4a6601dfcee6bfa88e493e836f35642d1e4b2d3e032c919fe0ba22021de0b089dd55b051ca9b90caf1eeeba700e345823e92c1169d97d9c75eaeee55d1e7e2d0769b9fe2f34fddcd448dc563e54552601d65cf8291accbf117ece2ad906573a52dc020f004b8daba9a62de2c21eab9d5262871529b86393de595f782ae216a330f78101c93d95514587d369c037bccfe2fcef3303c24d001f7d81ea76c68d6c28c7094e1002f82bd0a1757b979717c6306b3de3d3fc4588b1023fe8721fa3015aacb7bb83ac3ec75c53a9f55fee53ea84d50c70281f0ffd0f9592afd41f65057293100be03c9fd7fa0b28f613d52a0f98bbd6ce6c5eee477e680d037bf315e37622846c4b5173c120f7a65d23b95654f8ceb59722c8090cc2e7a7b3a1f1265f4b0027e94d925a2109970e59eabc4e54d7c86a9a998e9cc08ca121df19a94460b6045424b7583793589bab38adaecb81e42360cef437980e685b549dbd967aa89710de5b99a5469405d56a3eb7a99d0c882e7e47420b60b4a794487be2d04bf9cb986eef644e8ca0d8ed139f713075409c1a1ba08aa4ea2532ffa6c266cfa4b52f5ec45e67cb07be70d0003b49795f15bd9c708bbfbc7f4621c3475d2bb0a9c35affde49bc72b00abf023d0f64dd29a4651bfe68832a4215cc0a61a7aaef7b843ff570ff14a32fb72c9a1e6a822a89cdc098b4011a9577063269cee391c702ef67e880c312e831368a1c6e0229fb9f34d7323c1c940fb40483b852d86b5549fada64d48a91fd0b688bf437166881729154de2289931359520b4c0f7421f0e15737043c4b3712bfef60a2e83ef9772706b1ee0fe4ddba895636c195e174140b450572bde16cc4ef5322cce7c6b00c7be23f307bf424a743f7deb5d3d1e5f2b6def323a48d05eb21f411f43b45593ba4d5ee43df59299e913067ce909f62e1ff62be49d15cc8ec17c649daa50b97e2d9cd53612ace05abeab7ab8613e145cf54b01b06bd837f98193f139f1a9a80733c66c2d52297baee344040375c67f077d69978d541103565c6d0b8fa7d9fb18926d95451836478229640802386c824a9ea12ee44736a36678064614a30790964ae04718faa31a16e88e1e88ed07a58be39ba3ae2000f43008aa76d98afd564b6774e84968fdd8ebc0dcd7e7004c2ef31ee93d65871a94924db3b4c7057cee6e52b5f0e01e6615d8affa5179eb6edc994c2e05c3fc010b3a4b19ca1054d0c000389520f5ad4ebd82bb3ef34081bd7622d00fb43a481a0c08e3ee872e38b3abee215892df41023b9a36be9cf13e25e5d266c3ab183b79b345a52d8f15ab3f045ec71ae385a47bf5492907c89e349ec8b430e4a3a73a8afc4b56a4279213a3de40d080804c104c27f1057745bbfdb0a884f6efab326f032e4a1e210f8dc012c1b0d31e3ee54517431a24d76c5347e7d5140da543314c3ca2000e2f1a1c87a1455d8d96f4a7b98fd801833261197af23f50520263c11dcd5169943c906bd7254acfebc75b6f98973fe8c6894cc90dbe9689b6291c625eeec48523e910920492ab4470f61335475e59dc6ccc2bbbdf16a00092961bf488900694b8c10e19229fe60aaed92aa73a6ff5c94516490279a2f4851bf5e89e7cdb5df5e21e0f8c64b4840ed2c595307c2a7ae4b1470ae404f7736d932d2eac00a5044966ff499d8ded8699163fc90542f3ed048017d2d86956a6fbb51609b0771d4a5456282dcf63681729f0c3bb25ea5012f185b9f2074bc19fb10f4ed7be0873443d3f94a62f0ac3e2b5128970ab5cabf9971dda7d50aac41bb37532c629286ec7b496e59bea8109ec89384df70caafa5492468aa0895795e3ba226688000d35d957627d5ee0ed103a19aec243f17b52e40a08a0b4a1edabb55a555fbfa050c36dd8b1bbd89e086529869f1030e2b1ff5a8f719e3948d2dc2c36ea99b56c22c645bda35d921ee398f44131edd4c50c0217ece13935016cd67aca4cf72caafd059f8eb68ee517296bf8dbda08d7c9328e643681d8ed3590d28b18758ee76e04d35aec4cd65769fb84ff510f13ca44ad36546e8713c31b9705f115954b8c7e00914e28ef411f05d91d980afbb32b6df9e043f19ca00b4e71104dd2092f9649f3e2aaf08dfa1e347742afe613638b5e5c74bd18b1e2d91e06e9f50bb7da5657b8032661838e31f348d40ac15fcb4c80fc6074104acaaf511fd0c27da4534dd116002870dd98ba42e35073462b95efcfb378be7a9d9b09d6ba524897a7bc6b2d2f98e8f042649e7dee4ce9e57e116a86c18e0a318317c1c554e1f6607154038cab48517b0d4881b1b0da9f37d66b69a7309ba5c35782bded400af1805aa2a54045cc396e32ca6ba5af19c58215eb7fc7fe42fc72afd9e08153dab07002abf80998feb32413ff910692469551ddca9fa0932fc664ad6ebd29b702570ece51efcf6e11c2a746272e6600f2fa85d26a4afde38db90bba29bc33573056b5daa3f7644e8af305bdbacab73da49c774fa65bbfa930f0689cfa02149cce3dd61d4bd9708f85cb7ff0304afe93a41f8989a556f938155cc0a57eb5148eb004e178d500d495a05e42bb4aab714f6558c4c1f1f82642b410a1c2448f1934e715b50c2e2e57f7e08f8e87a28450028cf1c4e6775bc270a695db9f839521204dd3e7ae815a1e943381c17c957101c4c174bfce55446e2b78c268d2a6e09cd02a641378e489db6a1b8f41777a86cdcc9216f7768f7b3565392bf93fa7340196978eb978a1f3bca4b0338a4f9915113d9fa2bd525099340ac02057fdc4fc00e62c8e41db9597baf4e0a780a4ed7d2f8e995720b3f5d0f573dc45d2752c4efbb7e6d46862c2d52f133ca8d1d49fd1df85102e036a6d1a57adf40fbaaf3944ce2406679451ed402939dfdbce8eec03db1f2d71112e27dddf100807549ec04b6d711f6c8f51e6d01a6ea0c7d43f2ff14aa56b1cb1fd09239c055fbf897573d252d4a23b407bd41dffc5f9c58be3d8d2116b5ed826c209c596734cb5c56fa3490fc5449c38edabb87f56014800f8aa250eb6436bfa5f7def10813bda0e5675d729404c72572b09640cb6aca4a3d2dd6345177f3067bf971d35af725a9d20d7acb7372d0db1e07417a4506dd425bbb903bbb50f38b8a8fe4ce29b9e105cdc3484f090fd0a2b193150cb713fa534fbb809fd667897a9cd67b7e9ae64bd84956faf6b6ead09f37bd3e1612a35c21f49c5f2141ec479a7f4b60a80a90df5288f628339ece80fa577a007b180362fb4295f418fedf3f3260c5a55ec1726dc66dec961abbb86c44ecb478f19763b316c23669db2dea5d7d9a9f50a5ffaef8ad96376a633c689194680568d29dcf7c9ac627935a107617282f940ceea89b6ec4166a1454d64ef6601ff011716a42b9ced359178976b77d1a6feefe8455ea62a7e5d846d9c8a0053c7787a6a3ac8553383fcd3413af2a83b5bf4dca287f1f3e14db9803580fd0710c95560abc3a03d1158aebec3ce678b4edcc49aee94cf293a088562fbf2b6b21a727d2633d4f434d1b4430b8410dd4acb1ddeb80006ae6c5576cab65702143c4d34690350bea4c503433a774eb5eeb5f73fb9cab93845dfaf3ccd1039be89bbbe1827e69e6609ac68d596155de634b7bb3da5346e04baedaf1e3237d614c5fd7781e58dcf70f95fcc39c084f5a1ce599046fcd6d3ee84cf076054ddaf562eb7e111f824214b365f46013eabf3fad7f2df3b2757ed57bef2358b1fb3f446d904c2af059fba709665e50564e24ee219e46662ee227e0729bd8dcd478a10ff15d0725de5d0a2da23b52ea3dad919ec3e3b701ecff7a477150143187162fea8697018cbc53bb9d248e3e5523cafa335ad743379bd0316469b17fe8810ed228c228e79844e75b748274681d9bf2a30e43948c6262d12d1fe802e9d16223ad5b1d2fce7fd44a9fef10addb78b1218ee33ba80bba67b568cd4732470f9b9cc409eb67ff8a59b616a843cc556c5f7ae038e3aca221cb75024aac481c534f3205fe665919b8fd97da4dccae37f67404f3f7bc220849e3922f08b8a512495c76e0f64f136a3201571c2914f15069bf87f23ad8c14a983e97a11494ad982a7dc5bce161e02a3c50d9f9e60bbab2e42f30c4a79cbc63aa0d008ede05f84ba9ff257110d0fd1796160758fe9632703a113e8f75f7fd58f21c0fb1b94d20f341d9729983933d33fc74a3f3acd86ecb3a279ad21cc540c680d919973aaea522f7d27536e6cbc14cc1dddbcf8c2e606e7d76d875004b6068b2cb5334493d5e4112d76118b2144801396a25792c0b32de5784d4e1d58ed898651841a359bb189482c772a16e115b2fdcd688805757cb1b11ed3a9e7da98853f0454c2cf4f8ca583d356fd8ca240051e4ba115d6dd4f9ecc35f7c63e8740c586e54b7b7019d6866bf8402cf84b0bfa8ef934f9e71317f3927cfa6b8dfc960f0c1e64f22568f4c909110d8835b122be92153eaf4ce01628434956446054c31d7faa86855e578960a185de37b4b91668be4cd82b73557f9e83e22b8562a5674219a3811fb569f6125701f07027fe62c3ac1e93f905501eb867056443a46c83298bef0e868bc56f3201a356017bb109bc5ebeb73a0b288557c1cc89883d55e015bda153622e265f0e28d62ba7fd267ce26abe86daa46981f1057d4c970f76eb4a27190257f333a5e3e77b62080c2e3844a81352e089faa7841cdc234eaeba20cf2a139d1d537b5cfd4f1e7fb415437ff259930c3ef8c754415ac150ad048627dbb1a8335096f6451ea9eeaca74b853e8a17ae3a2572a78b2158377f1418a1b6dee3573c46a98df12ce719b8b852168e0dd829bc147c807bac736a302ed60f46ddfb0c8dd891cfd2d9f81e39a2ba7d6048e3d35ca6e820be106be42f6181965a7a482092f42e8d00591e482254ccea7107ca442c1c254badfb39210376c8ee223187f0a06af7e8a1e2c25c8d2c99fd03afe441ee6cdd7118796a19d0849242f27825e34c81c1b1ec642fa29d8cc687884cb16e459c963d36a3b6c34126198d00906942d1f171771bc226764d0c291063e5927eea180b1e4a52c0bb91cc482355e9d7588d035e5059fd720b202019d12a1c0e76fbfc3f6f566aaaa402844fb0043f7051275ffb3b610a1b2f6b850eb7de0f3052a501cb4623af8bfc48d7210450794655495056e7f964c1d2e5fa746ba04c0196ef03761a14e2a5f69dcd450c5e981424589404c7d76e802b66c2bb6a90e5530aa2c1c834a4ce33e0b7339224d150e130a0f2e16d40e5ca5ad857b6f23c31da77950475a5c6263aeaa5a6442ded4c28b3d861b4416be458e32e22110fea2f66c62ec21040226540c14052d58267770482eecf4e93024a402adc0a82072f2ba7533135473583fbd2c8360b79202bb36a7ef836a529a7f4061eca4f73ed603da7868564ee1441bfa5e2355433b6db1ec81ebba53884da63918a973a3fe469c2e0cea38b2dea32e5f85a49a04c0a5ed79cea49d366abccfebc4e65b9e86461c50529815e1be49ce3941126002824700a061a3e4229c0c0acb62768c1d4ef74f51a3bb23999019267c2ac33b991db4395329b4400f87bbcb0f5e35d1c95a0a68bc03cdbe16cb3d789347d83b4f32fcdeaf0eda432c522f7420e53d0b592bd4901a8ae6fde033cc093f25cb8fa4b48b334a70a8fed0b26641708b19da179ecb6cbde197d7838a104219c28039b66a7fc987949c1d940faafadbab954bd2ff57526f79b1a594a1d2286e7d2a915a0bcd1a8cc22ff3903a10bacdec4c568920efe17272bf672109cc4c5557f0f815a0ab7beb8d4909bedd12adcb0fde5ac299e6e713bcab9ccd1e21428df8de0633a61d1d9d97713acf07bb530b999633450b6bafdf16862cef5e636fa02ea326e0c63a793d155f69613545030e1acc85a380e4b2ef2d2bedc4671622ec40a3dd8cfe8a832d0c7f6be95ed15e867386f7554fe454d9ec8b6aa5b4f810cd72fae0c2b38904b91976fe335c88f79db46899057c479350864f869116e5eb3b715fa32b9f2385c23310f4725018314817ad99a075973b44a836d346342931e577deff7e1e8fc04165a4d3396064728cff9abb8fc91206d70d88209df3ee9fd74c65b5105df0611af067630181913c2c0f7d53892f9569f0494233eb7cc3b60555f27249d0a71d260970e2270fc46870647a7f3c9272502935433541a507ecb4ad091123c20a6826ea733695a3350564ff438791b401e7e5e878d31c865f032357632aa7a22eb5ecdeae41b351edabf0effcc546e176b63d734895037ab6208f01c253d19bfa79db7efd775e150df5b0ecd300a9bbb9d3f6d9337e08ad142368f5125cc79abf5a9c2a60ef52b62889e47d9dbe2a167ea7b964b7e2a38e0062cf3f4618cb847cce32c641c20ea9b26ba616e9df47ceba2f631e7dbd81f715d76c1ec3cf72c4e23b9c8c566994724b43ba155214c617262c6a7eaf0ff5c570f070c56c7ae7cba5426288a4ac1791108d2a713a2168d1611e049673da9e29009510671e01b8ea929f1c005071e34b4d2dd8c3133bdf129b7fff19b8e7e61963f0adda7f2f2af3c59929573314433425948b917a0a19351898afccca67216a5ef7e61ab9207d93265a0a2fe8f4cd1992b84fdff01e800f516c4db607d8eb5ff1afbd9d70812e6b3f9a5a371dfb7fd22aaa5a9f479cd7a8be6e7548d412ffee59620581e399ef5b1e5bcef804121d848176037220a89309ebe9aaebeebe553181e915e53e85b98314b8d10bbb2e59a75219949bced1a5bc35d08c969d1e24814e9a3b167dd1e9bc0d14d35d74a15ca9882f330908d405aa92f7f2a1da63d910df66797d0dbd453bc53d36e3763a033da54d65ba0635415b1b70434cb274f8745107ca363b0f6140a4582a5b03db02a72ba4cb5e9aaa515e861ff8aff2d97c634b08aacfb67ff40c1825dc6878105097cb399f57af40cb14ba3aa4976308f61080bf1e91d7bb5c28aaae4349fb908be57df8c4108c841e17978c9adab3910c9e6c6018e05574d9e9ee021453046eb704440e471098459336c0c040a3d6434f5818f0c9b22cf6d53a913c6e81502324a391d6da505bc995dddb293e0c03a8fac0b24088227a58fcb2bbbb7bb34999229d5dc75466e1c2ccd0b71dc910320e010d5884c136586cb6c142036c8345136c83c500d8c6481ed8c6c811d8c6880bac0b12eaa5fa1efaf6f07155538fdbe3f3b93e3a16151347b2eee3fbe158d77355d3944397139a7238f91d72b9d5dfcac618638c114208218410c2f7de7befbdf79c73ce39e79c73777777776faeb9e69a6baeb9e69a6baeb95a6badb5d65a29a594524a299d73ce39e79c534a29a59452ca18638c31c6182184104208217cefbdf7de7bcf39e79c73ce39777777f7d65a6badb5d6bcd65a6badb5564a29a594524ae79c73ce39e794524a29a59432c618638c3146082184104208df7befbdf7de73ce39e79c73cedddddddd9b573a6584cff9e75e154504e4c2814d902a1863b7fa29a400836da46001db485102952d3cf0b98fa4329227b601c2836d8038db0069826d8030806d8030c1364052d8c63a826dac20d8c6b6c136360d4c1440bffe50424249aeb7ac96cff798f9848a50477c58c654967cae0a0754b460d6912c86a86c80cddc21d40b632a4a9230c6546018639a238c31c65490a81cf90a89bd9e277fc47ab93d2c6ba238d70887268efe497f74f533f7c3f9dccf1d71a18205632a46185329d20203c6545e185321c298ca10b669d15730a6b20117b6855de282a4f3c3fe28e2e3c5763a7648921f458874b46821f2a164ae88aa44439af090b930469e090543848e7880fe7291a13e3ce8c8e543934cc85a231f19d8428587158ca954c154a8d05fdd1117560734facf45c203e6b2bec7be3f64664fd52bb92610e8c8f5413cd6c888a8489eaabf3f246499155030c67244a161ac314d11aaa00cabe8c0000bddd0049aaa6c4dbaa2420f0c0d1b2a3cd5774c3b00f0b932c8ba4e3894907bdd1f9d0f45e199cb84e251c5bdae664c452565c3c281239625b22aaaaaec0fc6545054348c59f7ba3f9a7bda454f9412c6d8ae9ab0b0ec4a49915d2981728bf7207d81723497c0188b7e6946e75a3a3f8f1c3ce848bbccecff1ef1b20e90234a74a805df1e9ad1e301034d9793c9f668e9e14a801c5162312633f1c8f1388a09cad1457d2ceba23e560e28471c1d508ea2c8408e28f9184172592f6364574a3abb6ae24c30d64a20ba22abc8660a20303661eabce3de48fa888ea8f31194d3144e50e7234d1d26a60141a5b1da10411b44c0010b58932b363b446c2433e12001b062a3e30bc63e1fe949aa378208f684554956a77ad146878e4b87033050040cc48051e74996f0c1468a2b30c6c282012818b32aa9a3ffc9b43b22ab8531d623c92687c57eb4845a36505c6103850f52eac006fb5495cdd4e7d3d2b035e4028320c2e6021b60cc021563296bcc8185f1441a9e5882554b364f0cb9a229662e3acce108c7735d11e7f3696161d9acd880a54c2234a1e2848d05d6d858c0033ed838c1c4a60913308e1c988c4ec886330536842ef0c0494105f470814d05f4c018eb3c691bcec418732eb51d3b44a024325fc9c8b430f6b9991a62c9542db7ba463e04d34704aa3c1b09786023810c5c95d67f512ffabe1027ffa2ef0bd944c0061148d940a00e10c80193fea29a58d789c7ba423610d862038125623cd6f564204a8c70eeadae68f3802d30eb4ec1360ff8820a6d1e20a4e5014b30e600351863317a32325d119a98747868cbc5059424730192b13a1f8ac2d9bd51037a6c18b0b26100130c6082310e0b753eb2ee16515efb223972ed6b858049e6c0cb912020a03c2068b99e8c5ec92f997302a264c9a40513101c79302130b201219eedb94498582d727c4097479a429258861c491205479a7991fbe1162ded0f0f8c0d223292248b249e3c2da9921c993e8913bd64511f1209c30507b4f00cc2c2d32204c9f595cce5cc60171c2ed828a00d1b051861a38032360ad0c1460134b03e32161e15e8e40005f2115c684305e070f590851a2031c6133a0e20da30d156b08735d070c3049c4530f98c7a5c4f2cc8ba593c93fcb94cf2bd9010614aae1089ae48743b2c2c1b04186283802e3608d8ec0afd14fa2115b5955c0172b91e897e1d9aaef80a486cb23140caa6004e5827f49f4e27ff13bb9b18d1a1e90a1194238f75857c280a8778f0e0e8e0a15d3a3cb48bfe7fabc383078f1c5154915f890e4d5720d1a1e90a3df1d02e2e3cdc1bf1d0555833cf44bb34094d577c0e4d4cacbf46226b09679676716fa45d84f8606109000c020001211eeb0ad900004401c61eb1516983f9610e8cb10fb08d4acc2645114c0cfa372967489b942752689a9ca454b1b9b1871c53141caca9cd8d2aa46a73e3011b94456c50d0d81b941730c6a0601b14cdc2a9a8bde32b9109d846a386a68c8d66c846b384b4615bd8b02f366cc8f5d5862d61058723e94aa481b3e32b512568c0e940255c2ad17870382c1c8e136c80a31208a844007050c2101c1d1c4a8c8112656440070e4a0891383a28a1b22761c5240cc0a25feb07a2bfa22e2492208331c6246125eb39a129128ba8de88e79291627244c1c20213c3a1ae98188e355d333baa37627dc5c212d3014e2887b55d38f7c925e2e4439dcb1a813a9c113533a2667458574f5493cba12e6ce409cc11e999782e4b743f1f39978ec8b1a6fba21e121227aabfaec74e70ac0b9a389bfac19162622e1ff737be2e14505c4e45714253b56387384f282730216a141303c3912c8e745d0e68531cd07baec9c9358db8906242534c4c14319c4de968e1485fbdc7c5125d22d657a3eb5a9c99ebb95a118e745d77e25071672a10b5454c4c07383f71b68889e9803551d7a642238a338339928e8ad271598f6338a2d1570fb2a890cc9db40e5d3b5e8f36c7cae256562644554e72114f1e11a128986be4f3190161727fe6535dab8701086293872f04a20d10e7af90953a81786be2688e0efd15132615e58449a63e80900403c41638a1175180a00120520088108b8ed88485c53561619944f76238fe50848ada327f600063d80f9a31b663478a1218630ed071b9689747a2848585593e34a2734dd88a2a461c0b33e18310c6d80464f670c6198c85ae08498b1ef4c0f250823c8c40094aaec0831d581346eea01f0625741540819444c884a626526ac0580a194b04a048455595a6184ba1016329638871461b0eb8540ebd937fe9811dd6084d1f2555b0c30d18b38311096bd4e081963a9c810896883ae8b02ae9006c5c818d19e06023056ca0808d1184a84906efe05c0e0218634eb0b159630dc6ac08fd87a343c602ade1e8b004c71a1c3a54820e85608c55c170d0c1094a8430c131872bb0221eeb0a718284f3fa72724894c5a17444c1917e46749d20a17cdcea1af9f1a1282332a149eb1fd7a8889eee1bcc8079a2556aaa6ee8b3c541c2919ca53967535e514e387f8d3e5468ba9e4ef31135c3d1d19cba704c8c355953458940530c27445d3b2c3d41019aee47445dd3547deec4b1aec96af97c6ef54d2c1e599787a3e3529bf3202adfcfd7b29a4c204a474c0c87874c753d1f8acad6a427275768c4f9cfc789075d99d0c4c47242748da8eb5217f542bdc08046a049ff8fd00f79728958dfe40ab9a817eba91f17f512ba3e422f2a5215a99e499eaeb8a8978a62624d47a81e33a1960f4519a1607a9061c3382c478a1c3962394d34d1841a6750e3098c75aa8e0e0ecb145647871a6170acb0c20a35b4608ca9b1a2c603c6a008c661094dd66505631a2eae18c31bc30e58889a44f73d1c960fc184c4258d277058d270018e344690061769146139240e4b13fa7b74ae497f07949f88010c31a4400c23e0b0e48e07b868012363084dc882198c3110c311861446857376581d2a1c10bc08a30a621802181cc0d88d00b047300d002ef0852730c6a41c390a0ec659e04217345c9064469803f25c5345c97c2e27e643931dd2a345888f167be425c9243495a82852682a1113e4c63014260ce58322330386f2416182a30b65c8c85cfc47ba702f6b1ad2851d5c58438af9cb9a86c470ae9b0547d2ffa14454e6709e63018e94afe7e870a7a339a7a2384d4f9cd0168fa3c3717478076238143ac99fb4e61c87baa66f42e1cbd1315072642ebe46a2cf272647141d6b9258582c99db6307c79a2c998b774c617da66b34458ce813c32486f3f9e4700e069e5c9094a32305678b28385658c1d9c23918e030e9408c8e3c55d783a897e15c1fa27c702cc091663e94934b874539b980e0ab450b11169618cea674e8529548e6e2175d1c3d71583a3b26d05b1d8e357174e8175594058596b9b8ba7ae2845a7435d22e97c5b13ed3bdae3caa6e0f998b6574e8be682ac4b146435e2a690a504c01005370aecb094d9d101582f91ca242a2ebc4ea40a10bd6381715b22ac99bb0b0e07802179e608624499c8adafa9f10191b85260a0454f54bd068038d32a05106c6382c3268683102612178c1189b49c2188b3896d08614ab620ce5105cecd8a1843d304b66b280848a582f738d58b7b221171f936882d9457611075c8031266238ccf0006348ac99af2820d40eb984ae0889e5b1ae90904511b13efabea7850a62fdcf5c23d68bae132414102b025542fe81fcb0ac9f2a0a0835f9b0f61f115d22967575e8655aac9f7611eb87b58bec22d58b46482a0a0845f5b0be326259fabe90d05796e812691922737b5cd63444263415b1f008494b450171f22f445001bba6d0280707031c782b6a534ea821d4934b24e43255949307425130d48b35a23e4455951d61e1841af2d7fd4151413e89f5e4120951d305841886861f74c198e882585858587eb088bfeea7a22e0d193f20007b1085446602c1571c2a1a87c7680394e1304302f7351bcc074db0911e7158867c88fa61b5507a8444cffc157ad23d6803470fce601c4a8f9058b743e911a547560f9c31c612c0832f30c672fc68e69944c152f6c018cbc170f0e0048c735d263cd082071a608cc3c2b9eecc3349d93b00c30e0250064c13280778f2c4856287282020892206808223780660884230c10227d2c0002cd08032c6aaf0c318386954a0063d9680e2107e5802021798431a9813444460103908928302e420004224216a8c31160197323c8cb18d093230882b30c6363b88c4166630c6981ca3038e605625791316961c7cc19837e15017a722e28223070588030e1ca4c1188e1b48425397435dfa521fe962c65216c058ca2605a4df9a58585e67c652703096a2000368c0067b48b991925f090e1be8c051832dd4600835f8823186e270d4c0016400828c32ac4a0a51d385830665b40089792492f52e2e42c6d8c1185d30360587e5832f68d484c22d631861d635e18e154d74588224e931461d638531c6b1a2898bc57004631c1631d610430e38c40802632c07a70a4e144d70582eeb7e4238c400430ccd3856f0e070582c3134c0981812601c16315066408819881863d60e6b87feb776a07421045d608c594da84ab27e5f4e35ca997aeb3f31f995c4bccc05fd35b570249910f5b1a6916585a8c91aed50889a3820eb73ff7a99184e4c8e28a2b8de8ab15a3892f5219840d325aa804cd515baa04742ed90cb03e93c109de48712e933dd2bf41ccbd211bab7a23c16870a711ebb48319c7f8bc332f3a9be09c599b9a0109599640af415952fcbba9f10d5e4f6b0acfbf1a18ffccb45bd84aee740f517a5e4a25e28180af444ecf5997ae8cfa7c58893d75f08e18b2f66fe8a98cfcde1840e8d769e6246e40496228a7cdda91251954814c3a9a6095794be9ecf877ad99592904b0b057a223f2898197d2dea054855444f3d2eeaa54911d1f5415130216a2a12b2211b72a92e108a82b9ac6a0242ed90f5d38e1d3b44160a8a30c6383d4e9006564953ece8585e80c19864ea93e435f5e20223737b781131e04505bc58801706f0a200321773f46fd1014e193348490063294ca41c20c5005d14813126737b70a890b99810265803634c92b998a3a990654d1f1c2678c23ed3d513e571c2c2e203a5007a28c11349238812e8608c713897046d304ba34c3848e00346e120418cc4c508fcc0186b32e118c115248c63045d541c2300526bc521821656450174ba0ca360e34797aea99b98ea1ca1060a56bb4c61a315cb871b4a77cd13e06e8f6afa1a7eedc28c3dd3a19c22d0f45c344eb037c38ae38591ba8ceb7d4db0af7fd318ddf3bfeae685311a26585de1dc744a7c5f7e0e3f346c96db3d8cb0f97651e308e512ec9c18ba6ccfcd5ca7acae042b9fbba8a3db78cab963126c951a6fa8ab768777f333acc3bc65dbf83918f17e29fd753448b04ec7ac31a65747e7da8c22519223d8f8f0ae8e33c4aee5a7f3c90ba9de0935e4031a23d8dcfeba0de3ac19cff8ead4a379405304dbaa7687ae5d1ce3eb8d93489224814621f9b2a4833544b0f969eceadbede5e6fe310ec1bed71ef7735763ffa8bf53a3460876ca361bd6cda1feece443af7a24d585c47d5697f5aa260896fe7b3047c7f7e656dd047542ff993e23a272741d472b302b388e56dc6b806065d3bbfd6db051d79b66a701cd0facacdb6dc75adf6ffb5b7ed843638675b16dec707bd7e17dba5ea0dda9fe697c609fd64f52ead7b9ceb0beebe981bdf866ec0fe576947a9d41343cb0593e18a7abeeb59bf1a45e5dd60eac944f65ceee1f218dd4559755869df5dd87a586f3669829a5280c5f9674846874606b7559679413c64df7cd9ee9a20bca81ddf766f74fe7ddd0f7b4a64f731c58bff331a538cbe92dce6dddc0ea9aff1b7ee8b5ddbc5db481bd3b53d71c7fbbedba4b610d6cf3b97dcedfd929ce5a3f322caeee379d79c28b9fa3dbd1c0eaa751c32ba53f7d69c5d16ac6b014efad1bdc1eabbb9fb367bafbd48861fd46e75b561d7fbb1bb135037b6b7e38fadb5a4a39217d61d858e77caed5fbf42774e9c0b0dadb9cdbbdf8ad4a7967a7239d0cecc6cda5d4336a7723f5e799eea8260696cadfb369287d579f128a44231858dd74cb39ca58b3be5a46cff427efe954ef84fa44d7bcc0c2bfeebed1ad5f9c147eae0b2cf59befcd98ce37e97e6f818dd7dfc3f1b508a55339a1e60bdbe473ed0fbfce279f939ee9fa8a9ea461818def390de9ccfe1c965b7ba63bcd0aacd4fb3aeae2cbd2d51aa367ba4569e8b1ba2c8d0aac7baa357d70dfb6b3db3805163f7aa3df97efefb62bd6a0c04299ef84713b7c6ece173527b04f5dad33bbf932d678568d17d66d2aefdeed494d639e2d0b4d17f6337c9f57e60d63cd7a7ba67f6e25a3afc879f517904a4d10299634d237756bf8a7673a753eea5ca04d8194c0be0863bd186fbf9eefced4f9eb90a0e5eda8ae6e3aedefb2673a7585a60e94a3ce5bd51541078d428fc8086ccb93c2fdee67a5957a135d22b48ac036faed5108f7bc717b082c8d3ac72cef7d8ce5aed5337de6993cd12542e58c759b3a296b93f9d6d974135d22f4899578e7e77cbbf9dc9e2f5ed634a43ab10dc69aa97675ea8ab3feeb296de2304f7db78675be97a3677a8419d978ab7bbd9f8452ba9a75abe2b2d7fdcefe6486ad5ead9b65e9750fdbbf4c5f432db367bab4a2b2fe5a7d18cbe65ce28da1677a44e24e2fd07e5c5818dbbb0ce55787abb7d8a2af1189ca97251d199175dff561fd36aba6b8d6e7f22b2a3bca62dcb096355e28a76cfb4732554b8d2f4b3a5a84a84bf8aeacf86795f039f8b2a41303b2d7c5afb4bae95cc77c6f752a3ebfe7ae1162f8a0bbf8c2c4e6465f83fbb986cde5083dd32d1d1a5118043662fc6a73f77f5daed83dd3a38ec9e6e88dce7c69c5184ed8746872121a51a10e75168fcd396be8d037c4f29d5611687a58c662fcdcbb2f259e54e6fc2edb3aa7f37bf355bcb784fbbabc75fe76bd7ee97e9cef84d40eb9e429748d48d29b2f4b3a5b5b97dff2acf06ead6ba453263431a92d5b8733c6d1b5b8277cb44e08b17dec1e7df7f1cdd5eba4cfa5bb763ae7f31d76e7bcfe949ee9a1ff74a48bf1478c75f4b55cefd6f8b5dc66f4cc8e3539af1dfb68a3126287bf3af9707ba64bdd79d07b44b79289eefc3595a7d0686ee003d66985f1757d1937e9f13dd3af27df9119e1d71d793dce9ad5f564b885757837f9195fdab2a64e9f7c59d2f180076cac73df7db36b10d3a7556af2654987031db0f9dbf53b23c53edf9cce335d4f9d17cd5c730526c8ca4ca8459266421746be97251dbbc44ed772d57abbf85eefb93dd3e305da17e0c09335d20af56b19fabecebd36d2c25618738c34df9fcf719e4fb4cb0acc90154992244b44757cbe2ce9e82cec6cfffeafce31bbf610075132ef53e80eb56ef373759ee92b30415668f5978b0f49d23f89a82c499624557f4d408eacb8e6d525d2fc20c9acb0d246a3934fba196b6bf5b1ab2ee68da5ef8f907aa67f0e51f573a8480888a496884202635bfdac5f95d5b9ac145614be164042e1866dd7ea6f664ab3bbcb114867f3e6a4c7595b7d9ee9ef0a2cdece8eea78b5bf87733bdf8011f95e6e5a9b7315666faf3a16452e6cf7e1db309cda79959ee99a42f252f17d5953e7d05fa3ee5a534788b88f2e1bae58ba2cdfbdcd1d19626184b9c6292bfe87bd5dcf743aafc83fb3debea58b176fdfcec16cc052072f9d7a36fcf47eb079ace9d22546095ddb58de26e95717fa26dfa140542572874488dd30dffb153e776b8c2f7aa68ba84a7b0b62db9e91eef82ed49ac68f9ee9f99b74a8eb39107b657e8831a6d54157a7f64cff10f5c99decdcad7d0e4fe9d29b7c59bb163dd3698f1f165f19e3bf5c5dffc5707aa62449d28bd525cac247a4eeafb984d1b9d92894d2b3538ab7fbc3f5e5abafa2b6cc08ff8f8f0834b9081a85de05da4e7a587774368f32b6fbea762df6a196afeefade6e34e32d5d34601b6db3359825d653be47cff4ce73979ebf49910c58795dc36f57fcbfe37eed99dec9d5752f87662cf883876df0e1db22cd544ad83acee505da4f5a619bceef9ffac38be184d533bdf96b2a04a37b4b15208aea7c74d5e908800a2bb75397eb96b1e647aff34cbf2aaa01160ba1a3fbdd74eeddd4587ba67726ad364f37f1c69aee8cf586d0339dfaa456d71bb73bf7d93996727aa63ba12a998ebb69fdab9bfe2ddfea17fe65498ba77651527cf78b2e3ef44cbf40bb268936be5be795d061941acff74cd7a14b69779303d0be97f3d5aff3b1935537cf748f3512752a6acb96151f2b302b304356f22b71728f549ff1654967c8b3d7df778bd1e5da3ec3ec995e5d4feeb4bfa62a0beb66b1d2dc68e51a8156801c59598119b2d269f16549270be72e657b51cefaa874f95c4e97404d3e1fc9e2fbd74dec5ace7aafae927481b634df757babae784bd75ee3eaa2f44c5f8109b2f22198acd10acc90954e68ea88a84a4b575da2f71595e1cb924e15ad638daa4e070253389413909e504e2e0aec6072737e263a64741c0c38f770a4d0df91d175a4c80145143aa080e202fa3379728e27dc8acc643561819967d289c289ea89261c88ca93e3be49c799c8e844f92d1d15b000053a9d2726d07972411590800cf591a13e0f88000702a007f8575b020ee0342007039c588015c5260a1c500199fa4891808aba6ea7733d790b30e14e2080ee6b3481038046210378e73d9d4eb62690040a000501ac070cc0515405010150522ca1ef57100800050070bdcc03542c09a47814379c132811d078e7769ebc0458154060c9431b340fbaa114163db8803102b035a4a00d2d713042a55346f89cb729a41d52e488028a0b3cb16201279ae05480021390400420f000073480010bc0a180043081800318a0000418800096604c40b10463028a1579a30d04606bf0a28b1646892e524cd0185ba20e84609a4168e6a0c182e60a25f8a11252616309190c618cb9b00d09604002cf86049e9c0116f4288109be08c266048df5800765b0a045053cae8a4d3d385438ce139cd07f1ac78a2638a1ffc410a4c108452461093d701006184c09339281993200408819a90a8eca1a74604c650e292750794307949f30a6220738a00ce20deca7ab7a1537b4c1491a94e024032a6c5803632c8c261eab922c35a0c1984a1a984c68fa8caceb8c2b8ccdc536a22f587c00065506aa0a95227c2ea63e68b8aa2b624ce50c4b98a10c337f450b1853990219669e099413632a6a34cd98ca184054880c151730994214916b0a25f98ce19305507ee2b1ee57948fd044e56b02421654a632754487feb23e0bc654d21003634c2a0a480cc2c0189be9c1980a18bec0988a17bac0980a17b68004267f13c6b280852b6c40a007152b30a652052a3095294821743d305f51443e4f9492cf9df2a4471bf85893cc0fb1a1241774add11533a116d784854587a62b269f2e00ca4faab746d58b46485e18538902cb9388ba18538182ca1318d353c709ec7a02ca5ae8ff5012c65484a0d2041526c830813115345496a0a284eb33b26ef0f98c5ef427a1a2aaeac960210304949f9071860a12185339c21bd45086cd85cdc6c2664f81a9c4008c22a81061088c2161ec09e13ff8ea0f25f9dcff41bd3cb1fa33a2b278d07dc92f7a623f9f1110ea458792886e05237a621f08f5a0e97e7c505528c95551a21ffa4309f90fbe2f8ca904e1f31901d11f4a929fc81000e527d5e863e4132a32f3577c9a9bf92b548000e593d721f7547ea0428619b7003185b0401023859833984a0f18735a30a6c2831d30c6184341805580ce12e694e84e013a1a06ca4f9e128b6a31b5905a442da0164f0ba7856bf1d50bf9c0123e1004cb6d81862dbe80a94f0ff7a4531ce8cf9405632a3778c6546c5003fd453e1f32f47f68b08b7c3e2d9f4f0b632a63588ca988111a6dc65466f005632a61303db980f1d7573944f9604c450621eae53faf090b8b0702d0014930a61203f76020fd05d40570e6562df882b15012cbbd262c2cee356161714d58585e136a875c2c6a875c78847e088fd00f79d07d72893ce886ee75350feb7ab44be835880a59153559224b7fa62c7854915f4968b4b5747a55d7c88ca67a5cd5156997194df5e0c1c3bd2adc4b72e9a0a32aa8f3111dcd3c132847937b231e5a8040dfafa01cb9261d968ec8cac20e9b2cd490c5167270583afbab28b220c246577e25d6246ab126d1fd884093122c2c1827940f168447921eb0ce4fd50593840e63ac022d6c73640c6c73e40bb639128431d6002aa4a840dd1c696c830520d8068b25b04d9456fd996648d8dfaded8ab58b14cfed8fb037be1a377653664765738eb050427ff351a7afdefe8db0df2494f1377e7a5f8d9d115637ff3b1d8defedfaf422acab74e3bf98527729c68ab09a467c73db386fafd85b22ec86b7bea7dbf96d3e9d4b120c501061f1dbb7cadfcf219cd445234356e408e510f6b5fdef49e922c6ef74348485bbbe9c77d4afe5cef015e24d29618d113a74f2c5e9e0ccf9697698b697b7bac484b0f452badd73f5c93c1f66cff4ea2b4686ac482b3a1c84d555c37f72ef3adf7b924e4158f77b4ffc8e27a5133b0d84dd8fe2f7d41dfaaddaa37bc79f155000615dc23ca7d4f549b71d6749aa7fb0f1c936e97bb95dfd176bc79f80e207abeb9eb2fda35afb8e353b41e983d61e6fc477be973574d5258241e1c31ee29bf545877036e8cda1bfe9dac9d8f67618ee06dbbd287ab06dea889d431827745f6b1e6c960f7f6e583fd96083120fd655399fabcf61dddfda06e2f56549e709ca1d6c7daf46279f7a743a57d886fdf9f4bfe5a71b3b9c9b1d6c6bb8be1cf16e33ceebaf0e767f9b3ad628f7c652cbc9867df79bbaa86bc4f33637d78093521addf3369fcbe7a0cbaf9bd6e7d863c639df7a050a1d2c7e30361ab7cc34370c9b244912d54c01650e4dce5a9bc72725ac4fbebadf5feda673136af7b1032872b02efede58eb8b237ef8bf76bc39744a408983d5ee395f5c5d95d121fd8b522249d27c59d211a1c0c17afb17b337e7383e7cfa0d36df88a786f13df98e9d76c374281b74d8a484b5c6d6e4bb5a567ab3be3f73be75b7c16e77f3d6991da55043cf6cb06dded70fb1c6535377db1aecd595ee07e3d42e9b6b570d764b579d3b77e862f40de30c50d260b184b15db8f5cbafd04d499224daf168453458fcbc365cb1c30f7b7d7806db1ac5b83dedaee7e79a51cc60b1a6f7a95ff9d34dfa50868152060bb17b584297df750fff254992320a19ecbc32477f7ac2ec52ef7f99d07f3ed793a31af6debdb7c6f23508df431f83950ef3def5c9fb13e20ad3bc390b4a1a96d698f7e74bb79675ead6094d328f29758ea4cad0f4a91e245f9674288a18ec75ef4df9f4779c323e9430d8df9e677d38a7d68efa9b56a080c1d6aae36df735cf9462975fb0bfe3843736ea60cc8fa9674ea2db792f4b3a128a17ec7bdde884b8e9bca36bfed97103285db0f5bdfb18ce78df5528437d1e0a17ecde17fef33def7bf0bdea992e430dd982737142dadc7cae515a6b9cede55cf5ce3f3d53ddf0cbc8504344d48b24c9509ff8aa4b84a20537298475b62963756f90ce569bbcb9e2d89e7f75d0335dd4a1f26549a705250b166e8773d6ede8942e3f2c5818abbb784ffdaf4e78dd156c8d5a4e0a6fcb0dcfead20a36d2f93484ff784aa7b3ab428db23e176bac74b658a1d7e96accde34c513d6d62e15ec67773faf732fe25c698b60a04cc1bed44d4fec326ae920aeb50c1429d8f8ea7efffa395dff522c064a14ac9b4eeaa871751b5efa7c800205bb6996377a8b94ba863ac6f9b2a4e3417982f5f7bb5ff5ebb67caf66da91018a13ec3bd8aeae77dfabf57cd9d3a336c1d2ab73c3cfb76b90ba43cf746a39973e509810619cee39950eab8b9446aa67cc9756b827adda9f675a49926a44c3560c258e50c3e9dae3c34bb0336b5ab38c373625d85677943b6e095b7bd83e09764eaa21d5edeafcef9e3cc33e95f16186b73eaadf83936a5c808204eb6a8bdb9de7f66e361c256976dcf9cb92ce0cca11acfb94f0b62cb7dbb2ba68041ba3d718efdbb2491210508a60bfbe4c658c777ad59ea3ac44b018625873846efa75af710856d38d5b9dde5ecbb4d52d043b61bd10c7363d4257e50b82f556e17409e7fcafaf518050e16d0f6e9721843fb0ade918319cf4eb7ef78f6286757a77ce1be3bffa56977d601bf6ac33ddeeed86b3a63db04dd3adf1d40dbe79a3933cb0f5e64d279c0d3fc75aefafe00a4c10941d389d6e3af86295d4a7cb09a7862dc7f75aeefbb26acf7499093fbf46a2908b73f70aa50cbbb3dffa6a8b4fb6ac67d32e1dd781cd324a27bfd17f3fdf274a0e5cba6bd04527658befb953f8983649a1f47c9dcbd3331dc89115575da2a7040507d69f7c137fa5f061fc3a72484b8f22438248e940506e60b3ab6db6c772d62669d3d51d05f95c49a2b50b141b5857abc3a6b796f33a7ec16f9600a506f6e926e1fda6b59624a979c7b33581a04c020a193656a7ef5ef937d7173dd3c0babf7cf5abd5ef7679c2316cac2dd3a8779bd8abbc62d85767731b6a4d31c4b3350ea0ccc0c69bf7f597d06de7d0c130acfc2c71c553ea28b1dc0e0c08df7b95d2189bac8f5609676e4d62edaf6e7f1419d81a27aceeeafdb729ceb0c6c04a7ce9f6187196783eded4baa65b5df20c1418d877a57fd4144ab9df73974519711507282fb078e38bdf376def6d664dc9407181a5aff98c0def7c9fba7ceb7e7cd41aa0b4c0ea1b69c5faeeed9e9ed23dd3a91db266ae11499224da718af285adb746d89eebfd1e694575ae97814e67101416d81971ceae69bb8de68b618f151f2b2dd3f5f890ad05282bb01a6f8d5b8338dff96f6e1558cfbae1595b7e75d7aaa367c20c59b1a6c71f2829b0dba99bdb5dbfaf3d8e1405f6e24627ac94b6a861845eab172827b0d23dfacc7e31d63abe2d49f46549a7098a17b6b9df37678f0fbf8475d78ed300a50babe76fb8ebd31bd7a869f64c9732039a4ad8b7bdc128e1c35bbbbbe9824434d2ff43bf68a45fbf2268286175a33f1f95534b4863d33043de246cf577133be72ff3d4f985c6d048c252d7decf08efd3325f7c6186ac4c303491b0db63ddf8dded4ed74cab04030d24eccdb5c97ba9fcfb68bcfa1136cb58bdc2d8e06c4e296ca1ae90cba57184755bbb747aba87b33f293dd3affd9f4ee7aa28ab135d236cd5eebe89bd4937ab83b1f9555116648475d939beadd6dff1bd5b84adb2c1ab5d6dae23baa0e93315a58ab0f546dc689310c2fafc3ad10c8c24d50e2d439308fb3e25d6cff13f89f3db4a24a979c72722acce716b19bf5ea7ebbed29233976b3fd01cc2bed4b039fab9bd7377f820d10c8c24cd5c92d4f199cbe9406308ebf65fbde9cc2fd677f52e84d533e68c3fcffddc3f6342d888b5c4efb5fdce3d6239085beb751ddddc2f42f9f487da90ba2e90151f2ba24b44924417e49a0bc2d6f6efda9bb966fd506f178fa00984757f5d8e32620ab58b384640d88bb3deee5b7ef9dcc33f58b96fc6f9622cb7bef83dcff42b34793a1f82c98a31fac13a591f6e0e3b87d3d1996312347db08d3ea9a57e776ee7375f3ed8bba384f9ea5aa5ce0ddfd7ccd0ecc162d864ac3ace99af8bfb453d58b79da6b54d89b7abb079f31868f260ddbddc78cf17ab4b4863c48395ba55d8f0bf99a17cef833bd4159a5e4519a1ae908b24b51368ee60ff46df7a63f7dadcd81b7425d4a10f4d1bd66386f7bec732ef19a9d694e77a2293a825d28886c60ef65539dd673765d4f3c12895a0a9839d98fe8e6fce077d6b4c914812f42692e4de71e84d1e1bd6e5d86ad5f775d331b617e51a96ce2a5df3fbe24f389d337264a51d59f1b1d2b058313264c54a92247d44a0496a78a0a183fd8df3d6cddf7b0aa38e4c3e235cab22fbfe101f2b4a567cac5cb1e2634592aa67525dd0fe23afa91749922469ba6484660e565648e776f03d75d92af64c0751d5865f19912410550191a4cba11b412307ebda94b2dde8e8bb8d21ad2f4b3a1a1568e26061cb55bf7ef567bbfbe16029d454d20cdf95f2bebcdf60af7c5db77c9e5d75cf19aec004591151d50f491251956e1a37d89bf76eae4149e57db80d36632adfa5cfeaf46bf1b2c16e4d23dc2e7f9e75e737d76031a412de87ad6d6fd3dd33bdba541590fc22249294bf096dded19ca15183d579ba0863a412bba6b469d230a34b07a38cd4e78c3e9d378d2b8538faadd263fcad4183c50d537dfda7cb7ac2d07f3a54a8533dbfa672cc1dea67b0ae758c50ba6f714247a914a2a44e87be2ce970a131836d6e6a186b94fa62d774037264e55597e895c1e6b8e763ea669cd0694906fb307e14d6f99aabefdc55c3e2561d3619238ef7e5fb3e3fbd7c13aa32224972bad5713e09cd18ecc6f5797b74fbaecf697db56eb192a47f089a346cfc5b5dfcdda6ce1a7b6e4ec5607374da6074b44aece0969ee94cf215c12849d30b4d18ec6baf35eff972bfb7eb7ba6239195e4a10183c558bbbd6573f5df749f9ee95c7ce7ca710526c80a17f641138c0f49facf85479624d197259d91e60b75d6e9e073fea43f27db742d5618a76beeeaf7d7e47e764766fa7c5eba271a2fd897718e6fbbff4ce1bfd32b345146e0104d172c75744fddf6bd7eefa6ef99feba1df1519dbaaeaf2e6baa2a8c860b56fba3b24a3cfd9dd6fa79a67f46a0f719119124ba06cd16ec9ef941ec8e1f560d31f54c67e29c50a2a885460b16fbeb57e547af51e2a953716139499224284792244992343532d064c16aec9a7cee7cefa7ffa8673a75518f10cad1fbdca97a4bead064f29fabbf4935cad25f96743aa0c182a517c6e862c38f3e89efbb82bd4ed74865d6704feca07c59d2d942630589777d34ffbe53ff8b585305dbb0c6b0667c2f84f4c646052b67c3d99da3b07e8cb569a6603de356dfc50e7abc39cb9114ac84b06e077ffe0c3b48b983cdf35e3a238cdff4dc5b7aa6d3a8e4b2284f67125d6b8c9436ec8d70ca7777bbbea55bd12549923ecf5c9324499224f95899d97baa5e09911649f2b1e29e2449529e24a9e4935f4b92c822400b52ecd0a88c59e3bb3dd3aba9e33e6152ea60f39b18378a5bd478ef0da363a362c7d5ddd590e635ac6389aba34ef574173e6d410a1dec6e57ba7ea7b47aa657175d8109b2925b7cac54a1070139b23253c1b8f8b2a4938246ca1c6c73d7b3bfd32d4edd60f44c87374891839df94109b78cfadd8deea6c4c13629379e323f74bf31de70b0dfe474aaa5cbf23d0ce71b6c94543fbcdd7ced36f8a417296eb0bf37de185e59a7f37a771b2c9c517b7498278e373e6483ddb76977e7cb4f238c4e6ed59c4bca1aec76f2b1934ee97ef90d7aa653f72a52d4606bd37257ada77bcbb865e77aa4a4c166b775d6b5550a37a4fe32cfa513844a4183ddf27fdfec4e7d42bfd59fc1e68ba1aed7f17cb8357d9ee92ff39f4904cacf45201f461e1252cc60f19d13cb4cdfbf37df9c2ef76987281f3f2489da920463829432b43cdf6d774d1dc63547eb65494783821432d858ff6dfcdecf9ce77e77922402c91435ecf69cf77b74bf7734ee36065b5fad58ca48e1c3f73e9a868dff93e67befbc7262872962b05afafcafd76d1997a484c1d6862f748ec2ebe4ce1ace9802060b6fae19bf07a5a4f0ea27540f49d2ff84c26e7ee1b584d8bd08ebcceda9673a04418a17e8c7ede8d70d9b769fcfed486e87942e40a7b9fa7eeef2d6ee28a5254992669e50956877b4be9234dd0aa655575d22943aa470c1a593d8b9765b9ef39d674a1288aab624adc00459a1710e295bb8587adcef71e24da98e5ab0ad3de9d37da4fa41bcab67ba24b91da464e1a386f4fa7d2a9dacd36141c6dd60f62a6b7e7d85fde294ee4eb9ef74dc3cd357f41cad80268f253559c173b4d2a015a0d68f237c31be46a5ce9ee9d708d4f97c6ea6d788480848c422a50af645d78fde6ff95bfdb83dd3a37b0a156ca3534e59ab3fbc5752e7991e2d4af4e2dcdfcb920e929429449f703a6caecde9edc1f8a6cbd06d1ae7f629efbb5aa448c1ea9f4ed6ec4fe7745daf67fa8b82750e71d3514bba277c170a36c6cfde5ceea7ae3eaded09d62b6e533f8f314efad279a6cb10a438c1e61c77d37e9fa637eb1782c9729ed204fbb6d3747aa4feb03d779bc2042bbf51ad61adb76e77cd1d0d1b1de6b804ebedaf7ecff0b9ada3739fcff5f15e90a2049bf1eb58617dd16712a2d709a7cbeab2b9e79ca49c61e19377bbedaaf4ab6b7d2948b015bea9dbeb8b31cc2d7e4555d5a71cc1d22d75931352e7bae997be821423d81677832d4ba7637c72d32258a8ab96f1d5e6ad627c2111ecb7bd23add0f38b3466390aa50c81462adde3a7b9e297d14a11827c50c2592195d4ebf32825f45a5b8eaeb79e75d61904ab73ded5bd86bd3989bfa500c1bab6bfc6ff47f36cf06bca0f6cc62fbfe7f08e504e7d3dc50c38ab6baf52d76a6dad3638a1847753f897ba79e1c7ea03db5a93916637357d87ef7ba6531e293db0d5259c99c2fbdea3d39207b6e5475f6cee77dcfb6998b2032bf1d3b3c2791dfdd8fea59461a1db2deb9af1cb534eadc148d181750ecff9fe1c8ecec1a6afea1aa93248c98195f8ffe148a73fb8a5ab0e0716de3863ad1a53a8bded7b034b6b76dedaadb4fe76b8dac0be7e7862e7314ed8aabf1ad83df593b149f9cecd3c9d8c7bddc6efd5877ac64d039b5fdf0d33a5f81f53776b6c2963449fb0be37e574d249fafc4df8f7797bf3c514312c762af1de305289b5ce50cec0c629b1ceb9d53db56fbd619012868554c71731863fdd4138c1b050bf99fdd2ad21ae7a3b507ed2526460a3acfa1d958e3ec6b3c5cbfaeb0e01e5278d052931b011ee2631769bb638b5969204841418d887d251df4ec65869abfbbd175838a38bf7b96b3ce1762e4992f49a77fcf96b428a0b6c9e70defbd9b16e54cb2a4992e49a4812754d9c9b91d2021b5b7c0ffa6db46ae8365441ca17563a0a6f9c1f3f43f77d535860f7dcf5b6e81aad583fd5292bb0f8de7b7d6387ef6189af4b5181c5f475ccf5c9863d4f083bde454a0aec9bf8394ce77e5c67d328b095c28d5bbc38c7b837dc27b0b4eaaaa76becafa5dcd90b4bbfe29ff365ea974e7d53bab0bab6989de30c1bc4aeb94a5848e3733bc2ba299dda514ad8f6dfe8e74b33a5726bf8ea359a84cd17b7f9343b17f7de5b5b496ac10d4958df4e57092786b5662775246c8e34d256ddc52dd3e8141214beea5e95d5fdcd1aa9d388e5d4cee59cf95b8cf73dc2d60a5ba474d288dbf3fc92249d23ac94b335979b6ed335f86823ec7dd1f37b0e697c35436784c5994e8de957ad3dd33b33b8d364d2d17bdc588475705ebdb3bbede44f473dd3afa97aab53519f997b75a20bed4e471196b64b65acf8fd4d848d5fabacb5ee0769830f23c246a861bd58df0bdb75181fc2d2f6e0efb6e3ccb0ca070d115d95b0396dee39e7b3d2eade863f5dddee6decfbbd10b6c196f56e75e64b08eb627691ea26e7c337e134c18d41d8a91bdd524a7ae7fd3c2b1041d87f92cefaa0bf8db7cbec2bb811089ba9dcae3d381fc377f812206c9e77e2e9f4ad735fdcae244952ed38bdc18d3f58b72f6cf3522add0d3f58e74dbfec74c44ee7199df6c1e687f1db5efdea5d2bad7cb0cee17d387594f2d6e96d0f56d6d72a9eb2bab99d8e520f16b65a1b94ef9ee7e74e5a37f260ebccd0e56c72bff67a3e1e6c9539bf1ca9d3b1b536e51d2cc6174efd52d2886186af0ddb1e8d7753a9df84f96a9776b07b4ef9eeb50dbdeef7eb993e733faf231537ea60e7744ddfabf59e51d2a83dd3eb37a9463ee2cdc00d36ac9ece497c5f94ffe6c4bf8695eee78db55d9ae3cd35d2c1e246dd3dc2572fae2dba3958e7323b9cde9a5f791b96836d35c2e9393a775d377c6fc4c1cee7746eb3d557f18bd16fc0c1ea396f86546eecde63279d4619dc78839d6def1d77fe2d659e53df708395946ee820761e9f74fbb5c1c2e7b9b9d72a1bc5cfdf0d36d808efdfeb33ea776fad8f0837d660db7dfcf8c5f81252ea1c1de3861a2c7e39ab74beb3fc0bef96a4daf18ebb9106fb26a5ede2ed3acf4a9b43839bb5b9da28ac4e4ed93c46f748b7f6c754d38af78efa0cd65fae78cbf7efebc78a9970c30c967eae97eaa8759418ff96244992a4da71f75906bbafcef7e5bcb061b7b52683a5fbdf761f5b93bbbad8d4b01bd7fa26a6b7652a9bdc63b09246985f6eeecafaf3b634ee9571d6e9ea6dbbfeab6278de7cba39278ccddd94d5bdbae384d8f79692bafd37c260718dfb5df48a5fa4504f305898e73f9cf3a3bedd9cee0b76df77d5a33bafcf6bf41b5eb091b6975a66da72d50ebf1b5db02fe69739eb27b7d6aebd1b5c90deaaacb2c629a96b2fc249a3638f5a468df1fbf9e28d2d5888ebbc15c32c37a4fe9e0a6e68c1d20c5f6aeaad6687299e59b0dadd3c73ad2feaafb3b91b58b0f9c61bf3beb2bd2b9fd6f4c615acde57c3f9748ef5c1989b15eca492d6d63ce7b6e9c4efb95105aba33fe759cf3831cc17afc004f93173bd2ce95437a860e1db6ed388b76bf1edb8a760ab86f3bfea7dfdc29782fd37bfd6a9e794adca1d05abf37fc453ee1963c4170a563fa7b5ccaee6f7d2d127583875fe1bf3bbecaa7682cdb256fab7c12c9b9c4db010cb0cbf6e47378d4d32c1ba19e5f307a996b565f768d877ad52f85b6378e7747a09366ad97ccf0a6bfea795609f83aee9e6a4cc3edfe72458a775c296a5bfdcda7bf80cebda5f6b95c29763dd8f04bbdbddade926bf69baf5116cd415c7eceef5fcfdf11ac1c28af37de7e43b5c1d17c142bfd25d877f43183d5622d8bff0fdbe7e8feb747a0ec1c2bbb3ab7052f7995e298560dfbcdbc1765fdbdbbd0f82bdf349bae9dd71bbfc40b097eacfb7c147b57cf8c51f58aadde559a76eb945bfd00c2b6fa5fbdd86d851edf8f9c0423aefcc7246a877a5b707f6e1ed1ed6fb23d6d2511e583ce97e7acb99378e74eec04a4cf7d4317ed30e7a7465d88c778d9ef36ed255ea5c07f6ba1aa1c7e8247df0e11cd89fb5392adb7bd9a5531cd8a99d467c639cf7d6175b096edcc0c6166b9eda69df57c76d032b6bae10e73775854ec31a58fc3abb5b699e0fd3e7645899e76dfbffe77dedb54d03eb1ac6b1c928316d4ebe1ec366d77c36ecf2fbb765cc62581ae57450c2ea1f1f3f3c03eb5c7defe93c356efaff8661a5ce3037fdb9563a271876d3483dbbf84e7e83b9cac0de989fd3d2b5eaf4e318035b63a5eea8cf786f7d8dc2c0fee787317b6d335e89e70becfedbb0c718dd731fa70bec8375467f77594e59630b2cf4bf384afaeaa4983ef985dd124aac277cdabd5c290b6c84f2ee776fd6bf7bcb15d888a99b0e63778d2f842ab0573f87fa418d65c3db630aecbbf7e618a3d30e3a5c5160e56becb1d61d7deb4c4f603d3a9cf2cdfd1c74ef7961bfbea6dde7831043e76e74615f6e504fbc5dbae7708395b02e23f6ed5fb7a41b6b4ad84b9d8357435cb196314fc2de485bcc7fdf6c7ae3f6246131940ea3ce38ba7ddd47c24689779e309eafa8f431a9859116a390310000009c981b007312003038241c8d05a3d18054b08ceb031400034e7e62aa4e3c18c963c124c7511044318c410618420021c600820c54383702793513a7c91280b511ad6d9522735c2a27fb90867409efcc57c540f375e0df93c56a4aa99b00f0b687cf290c6430a2c704aa3713da25c89357278fe7aeb8ae9831238410c75c8156fb3c0aeb32f7272093abc6ffd74562a6b1f62a2d66c855d36359165dc2bfbb1d3448f276fde3125139830313506d8b4858696eef3ec9130110aec8d2f4598463dc699a0484b7d2ac7e029f2da19398ccfd9ec5bcc6d76f7f76200eee0489a088b9365e455de0523f88925cd55faa1318fa2080be3dc0800640606e4986d574e0d20c65483a7f9d834ad9f82fc1755e186ecea81f4ec956c6a16ca63c983909a3c5785038f4517bd0eabce68274c0ed259d38b641179a77ea3a8f5c575de2402aa5f4ca0821f7404aa342ccca0685a32e7851aa47ee15a5ef917434fe4a0e1cc7926e52a154f11e639645053d2210db98da3041a373196d1cb787b14092e5ad5d14b767377b1b211f649a3282f60f27a4f066abc76a400abe786a6f05a5f69b1078d6b235ec9a03cb42483835160b5102533cfb5553b55090a90f4120246fb80868356dcbfe4fe9284069e3c54af047d54085465254f8d745299a9faf708cc33ff2d9f53a887160412750c28afc215e41ac7ae23ea32922002e1c4d81f58e14804efb764d6448b4f709be151de2d7563f715bc62c04c0582c56072f720e96770ccf051f3952080c60ac05e67c2b75d1829ae8c6dcd9744da0434cc3c54824af50d409b510196c0f9fd95e776178473d06f990e10d40c824e905316cb4582d439cd19012e60e75788d83239936be6c6493b5b3247dfa9e84d4f8ea8750b0d166d3b4bb4433f091e54b64f8d3c2d7a24e8a2118b20c8cdd7b109e3a9b3bd1e18f3f3c262278dfc5aaa71379f7b7a5cd50b25dc86233b515ccc85a0c371a7d4bf760e9872a0c36582aadaff6ec64ff6d93c1065c8a08e35b8c4100fcdba2f7cbfdfa898019fa1bbf62a15fc1a9281bf1450d7b634cb1f4ee5338e7db74bea9d551f5dd58f9487ed6dd329ca2e63853c5ba40247885a8b56e7ec513de484e2f4e4b8a36b58f2e53c91aa2485244342e87685b102a77048e605202f7a226b1ec97b1ee41e2b0a6925055cbcb0aad792f9b12aa802ad172a6b900cb17508a031dd99399d219f2cd6e609d9b9d902d970907a2ca25ec65cdc9df384c1376cdfe783be9fae93bed866a6c498ec4302f684e3b66a1a5a30ab709a72fbc6d35959bca8655ca6e4270cfd6dc7402b81e23531ca8925b869d6397cbd7f14d4c4aa6f6e6616017f45ef52fcfff988d5f6fcd68e00c4c93b88741f012db69f1c72f655f1a4e3bc14101016e0f3a653eef6ab5a23fccd4f8782f126c87b36222b94af66af6cfbcc0f813f5048447503c6428de763159bb58d1ab927a01f75338954a2f68efdc1b16a3c3787721416ae4a1b676598191c29d5b160bf1e243d16affe6b0cfb5d154ecccc1a43a857c0876cfdd497acd49ee672d57f166a2675091235ff8f6b720734cb7321ab7a3f1d2162b4857af567f3916151cc5aec76886e284bf0a471cfe129dd6659ccee403eb5854d19932322edc84b6094050f0bd35ad0c498139a16496a11f738f1b235220028fd169f4b6d6354c9c10dedd3d8d50f1605c91f48d2de10b6968c9ee5f86566b694051855b234b5c69e25f8abf930864a48e39226f312ebafbad0446fcf0f02b719a89081974f24d65eaaae7baef2914ec544dd9562f59b4102da6f4a4a0e0e34cbd7c79a8682b6582df639b14947f795f5321e3cebfcc765bbc4f5bfb1580953458ad428071dad0ffc7c5bd079134fdd3e49618450e50f7b263e0029dab5fd56d5b9a923623fb5c52024e9b0971e35a5229009a948ef4f0a3429838b5b80ee476689e07b23131a5c25067dfb62f971535bdbcc452b63cc9b5399fe475f93a12a17b9a4cdb0c131155044f5c42159b76fa99541f80b9f6a00b0f3defdf3a69abf1130be6fc6590a531e2ec6d8445dda86ca3dbabc958f62cc0890846dc3219e6b0fd0e7364bc45f3f2596aa799cdafd59081485482ce9704172900e0904f5066d2d43f1e4b0681df5dea48a39fd636747e06ead356548cf52c385109ea9419dac9e3921ef7b4034ef9aef5bd59795f48b63db86d615a12aba3051f9bdecbe1905f4f743cd2a5d84f5cc562b70bf037623d719bda0e98e96420c13509c40cafc9c81d2a1beb8d8a682b2f07cf11a1c4f26ee88492a4efd6f0e138a9d73617a1e60257e5a3f8a175d85f3cd7585b0aa8fe50ded5bd36096b4aefb2df450a5e9d814da5878d0849ecf35fc246cde79196be9053da8379dbeea9644f0ac0f9b16ffa9ee264f56893e46a38d0f3ab792142ac01c09800b0b02de543031d6329132bb9db4e465a73cd6a0e93270a7a9413b87103a4f8121d9aa1de54813ccd3a39b0912453e244b31a13361d6012472ac43c5f1f5d7d77dd0c3cd246d4d507843353e0e2ed44467ac0aa659702e9795bc23dd2920e4865af9652899f80c38d77da4aa6cd1cc0a52493e73b4b311f0f694e63e71d7d22c04d8d27948142a758eab8e285741978573f782cd1f35cc3b2be4d3b1cf5dd6219f7d927893f83b5b6f9983636d9ca34d1a8c47cd03722701419ace2de470bfc31d4cbd09c3c1f83569c66805b74577c5ae85451b3d2460be611f7a32d8a1a064ab0722ef688ebc402795973692c48925842fb8cea03eefcc7a24b38f87156cfa68f75210cbd99174b8b0e2cf728e2a342fc30ab83d3076b44c97e78afd4b366d3a2920f95e4a0d2ab9d73c93c39ddee2e53f6e62d849a3cb9d7591663e2a6bc7b83e2aa90dd88f5fde5b9cd7861041d17e8fc4cb5a6eb99241ef2e0408e9475b92f4d918bc370db807fe88e2ad34c1cbf2bdddc629761222d2b629a5bb70bdf30ea19f46a7b9da61a3007749374d4cef4937666ee8328b7a7dfb5801d4434ea57c3d44703b47b6a65d1e94c0055c10720aa1f5cad919df032d1a81e20d2d3da024febe8ceb87850832c3531d32c1e4524ce0810ae93a1f7e66b171b66b48c69ce96c0e734b6334d6f7f74a15ec7a7b8fbcdfc8164fe1c01b089c41eed818d025d410edda7fed860c3a129bd86b34a7a7309da8be3cf0814d8f9913591dbbbd1186516ac455da3fb0fbbef2cdcae681a784cc447c654ea1e1c71e1c24ba02f8ee1e3d0ae776e8ebeeaa9d9ccbc62d04266e317b87f9c789573871b3b07970b2525a943f1346c5811125ee30b3ff98430c545e914f8783b0105a6f371065e58c858e0c175544ff7e47ba0431b303f85627f63a0c76e2e68e3aadb7e6b3469932b7aecec02ad15c676e330d29d3bda62ba56d69566f36db4683f1b7a687437f6d5c6bcf7ac344833ba73b916dbca62bcadecb466136d6e7587e6d5c6baf76c346863e8ae11ee466ba3ca3bf271b3e37507661953384cf331b1c6741647ee1d2b5b659cb6fc49ab2745d6bcefc394f72a0e74f6bbabd6367652dccdbb3c9e38d0d5efe5daaa58cd6d3746d9afe3c6d3075deb6e66ad6ad7367952c49977fe3045d79bac26db67f107d70ccc4a28100e796de497ca0bb45f1a5add4c8449c737e49a5c80d4cc953a4eecffa4f2a0e987d98cc1600e26d015d22da4f5e3dacbd6fd4988d0f21d5eb8f1e2930e34f12405da45c889e441887416708c2a9d9960dc04948fcd2d05e3dd0d5930366d89a7b946463902005062ef18024557ff428d5ae50b26a0859aeee721810bfdf7118366747db528f63b64987663661e4acfef73748d955d5748581aa49f5e6dc4e440a8c2cea011909ae288396dd4d73124d34fe8ea10658a84a648c17390fdad525eeb71958a289fec1632d286704f91e99b42f17921ffccf887ff96ceae4de398e6077d18c285bdc70889613da721eafbb940f3b2a3a3f2cb8d0708a607db79a6f530b8055d8c2c94ea47d3666ddc3545568bea9f7ca609f0e19c29d51502a00a9803105d98d6c2a9aee04c7327499e9d344333b4f8a6d0e641f1ba3d7235b12c6b231aa699208001809f0299f019051e78dfc9079670099cab082d2245dda47e132701f1c4753a61b5485f2a740b65666507854fc5558941f582719ba5237dfe2c277b2551430f9b16890d596d23e2a4b4115d7b896ededcaa75991d178485f7f352cb99a0df0d80b9e25c4cfa016861053ced9048b757473f1af2f14c1bcd500af1d9dc59846552cb2320dc91e715d5190c03da1794d7965954fa157e453fb7f64e5522eb9f19bd8a2ea3573f1a437d58ccc43d5391b45b59ae3e89ea75bfc89cbcd995a5d38dd35d39ab3a0155c6bc0bfddf1209401e23aaf9728ce6d5d9d8382461a856852153602d6eda42c9fea3acb4bff512922c85c9aa0a009b2accc7d3231746c6bb179a8f2749071e2712c8402205b16dcfb2c4aeb40f186d0c608945f20f84fccf11ea4707040f5c37d25098a0870c45eb51f73d3d9d9d26472b9532ddf5851f055b2ec657ef597d38791431009fd343d4ba98f5259a659a24d588f99703d858befc39cc1c9077a0120b01c4028be575683c543c272f2a0423fb21270be2ad87020bbf5b436f638db3c28561f3a9a2b773afe6395ec3307b5ba647af7ad57351593a5b8d9012099acf38b4d4601fa16f87d335e90f29b61f82969189fa99e18645954e0905bc3bf8ffa4d32b781ae41e3b4198ba0819a1d393a85e05c98a6fd56185be131e01a2775afe6a7d76717381f88018ed8307d435f916b502209fc7268e438f65ff6551b569ab54b6b6312e59aaf670f971c11a065e26bcbcfd5d610b1326f353477900780028153d277b09fac131bd8343531d40b8b73ec074943065d27aed712e44a850f8bdb3d34b3d1018b56071a2686403a66f00abd25a711e687393034e29b502f8347ab3888258d436086b512a12be70b13346633679607626d0f953c39fc847d4c0c0c3543b0c3289b5fe5a70381e2ce6b728a38fae2a81b63c4133a6ad518a8e0f1d7ac9701612144cb4bf2e30467f9468056ef7e2abfaccdecc6a4c7e5c58fedde0b7223729bf79cb0d27979ad06012786d9366303bf430a439c558f4d53fdb9593da1e6350210e4685fa88cd547207548de6b6e20f6f0c4ac1f614e754499fe95d198725e73e180b158cb6c457ab65c578b94fb2971903bf7d602e6959f0289f2420c652728d8db4111bc541407e74c86595e14806ec684235d524ead9a8fde34ffe0c324f547de9970c6f8bfc71a6119b33dd14d155f2a4b5bc3bd0c3e9f0a90ab707083804c08edf6c1460c5778efb31d3d637529022553b1818be6b6bca62a8f351d81500fd33e003299f4a6e06cb81489d1edc68a4e164385c852a8ca44a14659d0f9dff71266cb0bb2e734f62968853a0b02504c22aa22e29bda8815c19e462181223edfbb554fec8c949f3ee6a22de385a9f13a93e1563d987cc90937662027cbc9519006e46f3cc330815f415655a4b6de936183917a778485d36abd1b639de4cfa8876c6d7635fd3f682bec27a74d39feb2ea2ee81a2362cd536265c6092cae4261f166d68eac46cf260f21a60ab5b32626bea08a8104b9ffb6ae61fca23d28d3edc1df0f38f513863cb3618ffef3944090c6da5e3d5662c090426649094612acc4d203207ddd9e2896f0170d5aee8a8e2110d7d4700e0bf354cd351089ddc4c466291601ae76683342d5f99e101d01077b0efc1d612f51f60bbe247c09fd3369f4352ed134f0837c7b913e58d41ea81de4131b2fca2072dc09465d8cf5cb05d913ac01e55ff5df6095b4d5c0da916cdaadc3c8a04f08147ad6d21016fc65013fea71612f66a845f6ba7bc2062fcab7373ac96a311ec9eca95bf76022548c6c5bd64e8246bd5be4e6954d8fff0ca8ad0318e95f1a054b92ef4b7448f96a10c0593e8c424d500fbe63bf06ba96f7dd86ca6d30d9b58996c034dde42d1f72a76d0bc313ab81496eaa656b27b3ada7003e0ef06183490627554f4c4ad4aec9480626570a321b0d26d00a8c3420123b9c64696ca95ffe1c429db52f09c07963807d3cb3950007405a75019681fb8855649cefed86225b02e805635cad7ff005548bd26c574c57f368635f606431ccdb6da59575d7753980e56e6fdaa68dfe228310d437d847ad119668c15b05d5e4b3072b125692b37dfdc604a9b060168944d6abec8220ab9f474626530f35f436cccb85d7c539b1507bb7504dabf68d916253588760bcdfe603c2bd208f429550934bced60889c119b7519ad602f42fead85a7e10923aeab41582bf2ea4347a91902b90204c94dfb1d9f9cbe25e1a5ef06aa0495f3ceac8b828ad2163ddb248f6dab047cdbd01c26ac4726e9301ed20aa80617fc5855c6821044bf2805f0fd932bc6ea9494d85007fe273cb38b36742a34b719b13d7d2a714ac073962786b319d10f9ff6877eac0b36517bbce71477b1bd6e6db658206a2573e0d2473dd5904f9847cb3166d74c3ad13ad19b36a337ddcc4817a1797f88e82c2abab80331188e2c2a9f3e9289fc88964276ddc97ea8704564fa7fee0a1b1230c3292c5f38ba4d0f065d1b898bc8bbd7300fe641b46e902240545e3f923d54583f25a2bfad1a5ce4d122ebd3bc013eb277b77d28f134dea958739a46dfc16c70870dcc0cb3b5b5873312378e0dbaffdb04a823ac499a1ab6200e82633a55488a07b6355834c7551a6352cbdcfa846689decfc447bcc6d7498ceaecf89bc452d3f6bd3c5ec97862ddf43b1460c6ec6fe3fb7828590f6386058883da9d6deba3d947be3d3955570f24e01dd4b0d0833401bbddc1bc70cef5a0ad44d5b51a925504053a6dd01781f91df85f35d8ee4c4bdf70a6ffd68202277ad18bca07a7ccce43b6fc542020d34ad20f02e6cb6af6dd370d8edc5de04d77faa771aa0416c226ad561396d67fb5d63d35aa1e0614dd2a78a0cdbc34144a925cd383b7eae179a40518540fdeb95351814482209c1838780f8ae1422cfb04524e1e0924cb03262875f9fab569c8eaf45a15605dde2b0a77e3651cdc238a314dff4aa6ea8b218ce4b3b32cbe05b967a052c7761ecaf9d04c9e6e35e5ee3969c4a17da77c9a46656122286a1ebc851274586613c385c886b3c80d3700afa7fd2d04fc647a896b7a38465ed98672de9ae674e5264f1ffb534bb5cf069e0e64154f1ecb5b9de158d0a1119b63b0e4ba071e637a942cab89c9202e068b3e0da06851dc2c631f59707963ac6e1f9dff8de90691244371b246f9c9df2dde81d6e620fe11469720e8d23bd48dce150cbcacf14f03be4849c8a605374a18d6e26ea9ce9919d4c04baad165ce00a55552eb526cacc80c9097a96a886866d7cacdc5d67abf308b543896543f5f7050199cc6aa81944115dbac64e3f36e15cfad801e37b9fef4edde1ede15d5925ec8c1db9d7e4209642a9365805b4e40f146709e1a07020a805a2ad75790d1094d246a980326fc0ad6ce19ea6a5a2e9abd46d2a62b861a07febe277820599d470293ce30b895636280443c7c2a330e0b75c0207aa3479a12c393cb01c9898a17344675e299ad7ad3b1c518d0bc8bc9ded1de1afcb31b05ac9dbefadd5f82e23320d728da96bd8fb878e956decf6488e83414128b10d353f0c83078d32f99da710f3937fd8523b935d3e7f93006a8f3c620c6cc87b254b656cd95988142ad89e253c319d417aea2df56b1c4dcb20dd046b19be8cd722ad48fbc47beac459501bc01b0547c1eb3c0c9124799ea35cdf41a304147808ddf5e3bd92d51f3fcf3a9634cb8beebd712eb05c6cc58422dcba67d0c7cf9eb49c6f6794ae7d841e469ed1f96cea13e0c13820941c9a6319bb991853aa62259d7e7f7c0a4e149117fe7c37410178558420ba01b622504a3242742e6f96e6247896de25624b0cd24e45f90b608580c79b4381e3e459315929574b707b88cb9025ac4494d54c23f69e00d43b16bd1b7fad16c8bc725d92010bfb75bd9c02952f1d67d8de2e45023b31a72ee4fcfa1e3bc2f6a4b7ac04cb2b4db983d44e50f1f37d48834b8432f79e543c29cc1b743f679b37c76b08340e7b98771fe3e1923e871b761747c6af30fd289a59c559ddb490407121ca5d1c64a73fcf85ec115748dbebabd072c01301723b0090b0227f9ffe63835d851742092c8cba1550b7c9d6b0604bcc5f0e24560c33b1ae10d03238d2752babd8baf89ba70ee978cfffb0bd1bf8efb265357487ba1272cc8902329ed88a6b234e22dec346d240dc08517bd44d663b407e8669245a25d8a4b25f8a73cc6e792bd1120b5956080ec508a180d55562660836299d460eac87968bbe258ac7a59720951b8a919562e7e20b09cc693bb45d41eec1f4f5410a5ce80a60c692b302bf829582a553e7e13f548809335e9d34a6ea3dbfc2af295099e28987fafb9447af65cac9c64cfde2f840bb5719592200cb648846eca88f045480afeef76d32e12b314cc76e058e77a24811972b05de1a2d41848b5166451c2410ca5767aec0bea4f5b334db61344d4f87b3390ed355e98102ad5c76321300456f9358c251d5341d1c36755774c36a8746aa7b7863ad13bc00d59bb33ffc24825cf6054c702ef2b2e34239532e23c349ab70b492331f13cd32a4df0ae95641e5ad5c021dceca41a517b4cc87e71f11fc9d285a7c957123547746ea78f86a00094ba4f62f3fd8841085fa4f716611c6f016674db065de55aa15a9ebec6ed1a704f7bcfc0fc57fb146ed0563040d4d037b77022c5135392246584c204bacb313ddad640a767a77b82b21fb721c0b5a8a5c8cc094269ea351058975613d4c268b147086a8025281b1b165c6e01e85fea5d8781ec8e9023a204ded0beeccdd228a2073e15a1c023be9f568193aae64aa41ee31952845ebfc6283d0f5fd0e7130243ad3d5f0f0553a062583caea3729e70e5f2891c100bbf7c7c8da64252f6a7b48b4164301abb8a984b617de792f9ed9a69033cc5490b00708e288bb402d43692ce15b1a13b22e2912b91667240808c872d2d111e4a1d2aeb5118739e5a9449d9156d871c4f50f6b33092e558145bf4a19d4e8078709d75a7f88bdd9981947f89597d5d158a81dcb265e13b3328c14d820490a6b2fd1c733ce08cbf501b12a02a0f54eca8263409966759c364690d83baa2438f1a00f24f203db8a50d7ceb1900d6161061052eba5fbaf56a8560ea377b8331fbb64d9e77c0ca4c1f22a8827a65d33d48d28408b7e26c4fc885ff6856c224c334612f51eb0ff45c056fb776bfbcb5a5e0d4fbca4fa046e0d1f7d4ecd29d281a3493c7dd24421ace52a4abfee7100676437ab565794a5ce713a6e92044937dd3dcfa39782b689b004173414bcf67b722146e3390c334ae26d699014a7362040930b9350702cde3265ca77ee9798f2bd9f696555476205dcf8533a8faebb0c04c90554cde7529e232e56cce0724147497729e8363d28aba4e9bc5d96189c52ed2cde3f4b0542177a9a7e6ea9c3072027a303d832cdfb61079f849d2351fd71a46b7e7c75f6b8caa93ba0eb9dbf76f1b96bd3383f60d90bd039fbe7fcb6f8c0cc4c5ae35e1f7b2e4cd3b35cc9ccf9cb96ca4788a33fafb1a45b95620f1686ecce9301f9de7126a8b03782e8f356d7e2df1a1ecf5aebe45d740f6b79cdb565e8e64a10284fbb2e41d958945a585791227e83b6f1161f62f0e049c9d702fcec3cecafa074aee256d07e04b196e376905cb474ded3191330a2c5d3ceca6a1eefd05729d294edac1fbdeb367b326d970596357f5b21ea5c0838334da66fef77ae6be61bf37def4846efed2f4abf51bfa498c65ed13e87c4ef3efd806c00916c66f00c08f87d907ebcc1f6eee6b9424ff8ed9334f3692387bd3a1ca22b1b0bb8c1f9af5113bf22a90bfa69021195b4f729dfb82ab856060ce321270af04dfa71619067c08a9a18f28dfcf10a9ccdc783beff6d801eaf741589f579bca49e030a5d3ee9ae809c7f0a76408eee03df83c998fa517d05322df3235a7cd6f739e16ab52687f02e83e06a4d1c1ff75a00e7a901303bbe751375137c7752560c48960cde80b3aa3492f295c9fc0b4dc98451f9650047cb54195b2c72166c33a1dffb185427e46d9e10cfe675341b8e36f9eefc29a792688211de6d6e10765435a801eacd3fc4684edf4d08ce9a38998ed3ba8669caa0376314143c08d8f682c66d464e54ea26b232caa6b1f71db09197e558c2b4b076fce8ad7e3104a3e37164c38119939616c4e400ebfcd75e0667a39aad2bb67fed0590301278eb95f285640c00706f503fa3aa3fde4c6ee4e487c7869122433fe3e12a58deb7fcaafc621ff22149314114d22466dd3f3a18ea38dc955392a140dcbe0a41cb0c07343589debe9717ebb61879450d9f6bcb5193bdbbf829ce737b3e2f77182fc5336d91a32c89bfb0f7313ef582c023353794461b6c0c599ca23e8b92624f53f9f5207c8208ff1af3e2d2e0d69754033fcc734ed20258fdad9241d76dfb498de17e8f4b19840dec658d354dc90379fc6f4b7f7053cb777879c5952c7763f2bd844f51cec05d1a8d8ff99048e42c71850316c7a14c23b265b7d0424db4bfb0304a0ae25244930886f36b304a66d68dfdd2421a4e566f4dbd4aa151b9a432d511ef1f08ab4f879b5e09217dcebc5fa6b188796e0c121eb6379145d07d2a4ec54318b515e5854f4423eeeec39a4685b754fbab2139816a0123d5cd22bd2dfeb0369060903937d0d6c0e5a9d169899f8c4b261aea3bbbac96f5e10f063dd9f2e245dea01d6eae6482d43c570e65fc53c3b7b42290e31cf30f8f520069d2ba0b146b5cb28dce0b514668a16f7dce4c24e6895b776e483a85f6f72b9183e9ea9974abb080f7fc2c19ee77fbd3936eaeccec1bacde75df40af69a71344d579ad8a84d14e8c00f8d832a7beb24672030fc2cb4b26e21a191604b398e6bfcfa33d471f240ceb27316017b31cc4394b25ad471e24eca4c394858b450d0d5ead804d9d8e50d2a3089c2fa751eea7d9157bc110098dfb1b9ff8e3084d0990a9baae0f28e87354ea0e4ccb10144926b8bf4ff0baa6768300af34900c8f3b782c76c22e4f64b1cd5a79c94c8b9a4c79873c6ee11f2c4e666debbff5acbc34b8d121ec2c363a1614cf3631f4ce2efbaa094446650a428fe5f6272b8256cb2fb2a36ebd612d34844eeddc5fe4815739bd72330a71640f62080cc08faa9c6886edcfe68971e7acdfd26074943b77dfb7b08c9ea5ffe3e1e147b7481770228dfbb042dc3cb995a29c773da4b7965c29caf050ffc4065017f92b0f4a72d6fc0cc7f2255806d7ce6f7390f93526709b23972a57aa569f635bb8ad1bfce84abc5b9001c8c30eedecbaba9b2fa034e07005297fd1377445a66cd93a31a2caa068073eabdb32461a9e3cad949bb15ddde5d8025bef1ccde57ba0671eb284db6dc3e6866007483a2c87ea286d610860bd42e335678d22b01f8cd451e110a5708cd3fbfa2d0608ee647fcf75a607b851ab7a735107ebc8c7ee739a50033612a34f0f183ce7e1ad4fe7a13e236ee5fdcc7273a32c44cdd70bfd80d697c241583275c28f6f3f001f9be8a7ff3a68c01ef29e7333fb1ae435a1bb4d7daaf570ecc7521a28d21c2bcfda89e9c630f2fcb8dbdec4eb120d814ab4cdada240d252303a2adda42b927ef64b1bfc1a71c6c89bab1b1b770b13ccde68d867b09b4916a30b04ec96fcedd8c5ce3fb8a51f7f3527cdb6eb96c9b0c43f51968656c18b5bad57b2debbad2ed732eb704087a3b830b798d0ce480807b6e00a5e9b35d76ec55bf2d9b985eb2d37d9f72dec506f95ceb5d0cc38a25a258c86b735fe3759e1ef1c22189295f53a610dba88238259e30a213d4306d93ba8131c03ee6cd51f58b8c14218ea0ac9f6d161ad936471d1f20c3be10587a34443570541b9b010dd04592d5ca196e10d46611e37caa3e841372f03b9452dd3e31fdeea79cd1b1d47b67041fc450dd99355144dd46cdada8ce484a0fb1c3e69f97ed7664be60a8a854d7fb381ba20bbadb2cfa7e19c28b022ec02e4d673fe1e00c0abd7c53d5fc9d8fa86ba56406ed8428a8590ae2a96cc6841cc1718b9cad47a5f190dbfe1fc0125198151e99d955b065833bd68650e86d951b141b28c08851a23ccf2898f88a09a0d4d03995de5e97a0ebdc5ab0ba8ba1102967924b10a78a977919a3cef5511d88eeea965a68fad121fe764ae223c6c7bbf7080493fa00d9f86c36a617b82d5e0630e249c403bb99134649a74f495e66bbc9955a7a27146e751283c73167f5c8c9061d06fcbe1d276a1958157f1b7af1363764516ae090c2f42d65c0d79888163094bce8e88145f23c987c944169623dc8ba86f10ffd053a4f969dd33dc4f12454bcd1d8080e54260499db40c4d04545d119f989a40fddf7a65d3527227901d4a24da809aad9c6a20f17014c6ac92c942b9c700e494253f12175a9e2e44eb5a836699efed6a3ed9a07cdcd2cf037545a4806853016bdc27c4916cdd7be912242328954bba8f909259e4267f4030037659e05085e6d996620e5782c7a4e663e635afcee0487668040ba91fc8ae19d8469110fea340a8a7a01df41dd594e3dbf008c1e8fc54b3e27ae0d5d3b035c1e9a7b4d5a9632fe153a3829ee267e3169f519dd60f0e259ec59b2c2f7d0b5292b726d4ed0e962da5b257f33f18d49adcfea068d1fcb6ad79011ba9fae0d0e3d839c1f2d7d4c1b97327e483a3a2ef7907c356cf5096c61f8ec2df152bac8f7e0d0c4b17bc8e589b5a7696301cb4f71a793ca5ec2af86063fc1cd8c2f3ea51e4b173c4e643aca2ec76ce9869b6eb639a44dcb6cfe62d372cd1b6b1aace612355dc55fa9ade9216414422fd5d678123822e17715353c0c1b91f885ba7a9e068f22f44e754dcf01a3117babbacea3b09109beaba8e759e8c844afd4d5f6303c02b1776a6b7b0a1a8df04bf51a9e858d4af84d553d8f0347137827ba895c69c6114250911ec29ca91ba464ca2f3418f6fbeef0f9e9b1b679a670a9afdd966218a02c9f9c08e9d8b17caf03f7d8a6482ae9eb154831d357681805ed777cbf03ee1d8aab64e93a2bebc02029955b3c7bfa7711b803380ecdc3b3cf1b5131cde100cccac7454a9f6e0301c5cac9ee63c8d06c6cd2078f21cd44ee1ba2168c3b2989ed2959d40c49d06b1e488703e84084a918fe181e6a84e4160cde5c3a18fedf9331dcf2e6cfc832720f7f672fab0ecf6304b5b3b224b7a517a0702dce6a09530cf3fd4f282a5b50842b71a45babf81b31467ae62373883649b0534a8cdc625b4348857076190aa38d14b82f5ee68fc741a361f7293ab56d9bffc7b2a9e3feb20577554e4e50417cc5d57727bdd73831473628289d9ace1148c29a67cabe3bc8514f1a40d21713036db8a05cb78ce399216b7067b55e15d698c2d36a5b0825b673ffd78ccd0aa1ca3433eef47d06d1ab4132734f6cf2ba11c783e7f75047763dea67e92d09396f66750eda688ae26ada64ddd02f19efec8dda4ecd05c1eb9f8535acb46e271e0c75e8351ab4906a3d35c3b1907a288b52a9b08744b7c16402938f669e95c0fe2631f2f2cc11caff01824213a695a6d215c221e9e89e93409d38eb2394dbf179597e48e7ceca4d0210fef76b36dbef2413b71cf430443bab29808bb97330880db7fefca0a333e19f4d857f842d5707a201d1754d4bf0e678a1ab57cbda0f8c9a6b7a9934695fe8f7b770c2eeab3f7341c46b914bfc43cbc5e362192a581b175586e3a29564df5959fdaffe044685896489e5a103edfcacabbabd4b304d551f70ee1965c4862e59d09c43756f0e0e99038fbe66d5debda60b344ac4450899e546059a7e6cf5f214ce1f8c4858baef651648185b1f93998526d14194a8e99e92ae80d45a9db24a2cc871cae5f45011b9fc01289e468b533f0779db6830fe83349c55a829b12800e9b46573c84d8ecbf5ee3ed7fd64b07622824a96e51caa3a4e01fa979455191e0d75a3494390a6f4bfe1dde47817551abbef31f587c96551b71d7307ecab4487c5e461b8a027a582139ac821ac303d40cd9fffcf1f8b2446a08b07b0e6c8f8e061ca5800fe6e058a1fc8b4e6183aab3be87a12fdd93498855c2f8a3746b7f6989dec4c0ed433ae02de0810d4d136df9638bcdcebf7d872de248b262322e06d64b5a7797bf9b38a8e249c2bc2976801f52a8d46feff8455751888ddb4098355fc18f7fd883d2ecdf10b7322c44d8ad3fd0b339251ae4ac3f021001b00c710a48db83bd32d55b9b998de7c2fa87ac93c99bb93879fb078282a82c89478072ac70813c53946fce73cff8378f6669a80236f161cf74cb507b9a29509167247a60f314fa44cba4c0c8514729919854042c14c277ec0614a5f437eb00d52d999b56a3dff72690e72b21402732eed4911092a024ee1793547ab0eb3d5b54e033587f729ec2688d1702f157cf8bb844378fe0ac3f261ca1eee5c65f3ccbafada603065cf7daf35f9e092a12195d771f49e80553c3131dbed6ce3432189eaff451dfcc71973c6f002f987f9ca10744af5d6bc43a28f1cac3050c845d9a9c0ed271781cb299604f3c5a8595c8622de59d2cc165530e8739f51df3fd815563b254ce91888b70f8f73e124751821114661c8ddb50a46be157d39ab764c4b15134ebd5e8a432588392043c3189447a2b0ccc1c4d74c5a74e1fc888fea255be9ce2857c8381555a16da5902934af36ad58de9334d17b529d6c7b23ff3f149b8c97f65ea4138cf77d78ec8b2f7e6eb1e280aecd9d30e923575de827f1909baf3f2c70857f1a8bb4c7fb35964eddd5c695bb64bb58888311835bedb47da5dc4ff515a18e7c970ba780c473c65473640e1f3e9032ed9c875c7836d15b46107c3362fac3e0b4a31d3621191ddf6b73b2f963362cbd7a67b8e687001ab34c55bd55d9e752cf1b86e8eba61a52b5bc30e8be19c3e58b9cd48c98e5fef2a639b6d947c40875f6a4acbb903c3af4e4ba8037e58e15ae31a0c26c2e87e5846076099812ed65292be56f97a988e4cb5c10fda2ad873fbd7acfca3475ae9509cd2140ceca897458cecb1f91d24a461f54bdcdf1b425a95df79f9b71280f0985f2ed1f726cbefa916f454793374f0fd3113339252071311bb436e6cf26b864b7e9e6ccfe7e84fbbdcaeffc89abbc64c4ba75a063de028752b092eb96a03642f933b742c43c5e783c2776cb26e0b0c101c624c3759c9ef05c3ce06878d415b6cc8148238f72e9e50ead0c09dd2eb50db7d68d1d9adf23ca83dd3d6bfd51c3e28c9db8344b28d098689d1bfe44bab94f3d566bfea0509010193ad62805eaef6e00988f00f28dcebd07f678101807824f8b55ae3b2e366b1a685836dadd41fa3e97e62c9d79804070ff9acbff366792a5c4f2ec7f3b24f3f3bf95021b0bb492a6061fb47827046f37097823750c9b77d8a92df4c3477dc6aa99a4a79ac2859ab165d7555aba7229a977a853f17b90eb196143cec4cc61c1d312f24c303502d1aa8970c3bf7f88bc8130dc028d8b58b338b1cbe0229ffd308416f676aec9b429443afad1c4440f092aa3eac920ae3542213264238f4a93182e35b9c2d7c9574812c6ab934ea13a3090339266f6a3c401165d4627fe56c8b6c4577c7a49bcc7608b9084dcd7025b45949bed19991c8441c1b81ba0e65f842fb721710dff1369e144eb3fa7dfaf44d6d22ba6cc774e1949ae065031f86688f19f48b33bfd4346f60f50e4b4c734909f3789f02aa60879bcadb32be2b3e83c28daad90077158ffbe79e8caddf7b627792c1cb74b21e39a41b4b015ad774d9861db09e48f519aaa6ad3dbbb7ed54c34b4297e1727433f385bb7e46bd9510dcc3fad3d56d249e0b62eec47ab02c1ef9a373ab57c0855b0092728a469fa1da0e817043244261eb85c7bbb04f0d3b22f6992c2779b19a5b750a7724f03321eae55faadbb4c818284922854da6d6cfd4368311dfac42006f58b66e39a064322708ddd1da9e5f82ac9480775cb1e16ca37372b33c2931a7b1d3d18de1237aef6a71d13f28b872e6ef50c99248fa57dfd2a7bc7040c941e0ee428effda06d36f52b54550326d67d71fad726c3aaa726e92e4ed5cac8e9d797aab19a259ed415c2ddadc463f55eef98f2728bfe24726ee179edd1727f2bac6f3a274663835ac2497d1e42211ae8c90044f350a4bf97355a8be49bfbcdcb38229cc26850fc297f991b924d3df5b8115ca3be2220187780a1a9ccacad0af65efeee5433108360bdae2458e9093a8ffa8b0896760f02b7c4c4d6dd1acf43aee5c26f3056a43cfb56d4981388f2d3ab47b8518df5f7a55265f283daa7304474bb3de1a6265d7ea7e3bddcbe07b84f1e31cb3aa9c32c126829e137c6aab0f6f658634719a2e96a5f438891f78cd6afab39212059f23b0bb15c722177a04bc1339b89a41fc1503d27bf7bccf1fe7db41fef7364f98386ac6d4e0f1d63d93923c9f585da88ab7eca1dbc954a52b5fd564ce28c4d7bc3a7486dac979d6bcb9a97a35a3b47add727f443498cb8998a6923e6dc78f3face0f09c4e819916cadc8b9bc5812e13fbd161897167ac7f85d1334f683eed2fc2e731fe4b71c377b63ac8abb4f33a541c4098faab7a4773abda227f65cf8fd10d57d295413629ea9008357742206b28d4c6100a6c1450072ac52766d580d16c253e16e57e4418490f5559039d92108d053d86d231e92910185de0c6522db9393fdfd62aee0a5973371ba7637e44f741a722ab53813b428dd7f2372e62ec9523dea8765624ea65e698c45bd8a0abad8a669d191f5737aa6409f96eef7d7f95b3250e5ef745f04f8f44a4fdfcb9e0cdda5ca1d4c9e23cb372b07f808b422f274235410d0c5c25a03adba156f18e722edbc5b26ef4988ce90ae4f965cc195ba0e32dcfd3e9855ee03136ad8f7fb25dc48aceda482787c5bc1cbc2e314a8d72b042e571aca7d4bfd04979ead16098420047db8e32394aa6754c56c47d0a549967cdcea5af1c46c35169ee121436724f976b4ec83dd3fd8c237b5a7e91024652c4e4cf1a2fae336f5bc5276cc6a350bf0cdf0fae07c5102f85f3dcf0dd6d312143eda9aa5ee49a960a53af4d34dab428b918f0eeecea0edcf452216ccc44613400d6c22cea339d49639321d67416aabae6734db0a0ba0438b7e391340602491e1f541b01afe6c35a00177ef898d5f8c703085e346e05df4406896f18cade50852fc130eeed0589dc9eca9671c136172332e07241842be47aeee33daf3754017338c7518f75b7373981e99c78aeb353059af6b030e4c607a31f86c88bbf0133cdd0b42086d0076cee68985c74fec1d93359fc3252189f9c34c9630d04867afbf6e846e389ba3fdb6377d6243eb995bbee3f09de1a406021606f1306caa852734f18795adbfcd7a9e2355f3093966ac0718b812c105f0d0eb987caed73ea55e035c11d37ef2c48f09c840c359c294e82a0808089cfbeef400d87bbcfaf433769af38fc19d1eb7ab439c15a9afb4e3becdcc861ee5b9475a458027f1a17da5fb17eac2d66708f237c758c47d5edcea2170b9e4f74cad5a8f6eafd93eb61e0a81349f15cfe8f10b1f42c873baefb0726a09818e53079c6f27e63117c20c2ab06ea1795f2df802eaeb3b1536d3cfb7397a963ae1d71e127fcfb5b6fb3ba2ca12a840a492032de8cd698b21534fd50dea84fb812afe95008914112d29f278b9fe7cb83044de9a358144cae3537c00ce605d1429e9bef7ba3b2229c2ccd59e5173e9b620eaf603bf661ed45dab2d7e6825af00e910a8a3990b22e73b94c12807ca0716b5de89d4402712f666a846a3aff3ad01628f522d7815178115ed2d1035a8d4f702fa4ca0d3253e4f2426e8b4e8f703259ab51e60347e5ff746f06f42eb51474b4d6bc93ea6d4064d046682961bc3932a0e47d8159762addd97fd1ac06c08e5c2bf594db641e9061860f14a02ec1e5d35154ce49d9197488004ee354ef3d0c8f092a19dd7cad02fd0ca7f33cdec684972a339ba694010b14ddc1ffd5f7717d298daacde5de4bc4c006fe878d531d78d696b90a14599c10978036ab1648cbcde6911a78dfa0e4dfdbab8dcdae56fca06f556dd75b3b2be043ef855595f20b09618e206be2a4987cdd34be7fedcd9411db2506d83eee0b9b7b5b43ff19459027b899f7ca6a3b4a1dc08f78e8a897111acc455f8281f80f296e82b8650ec1e2d625f43c0143ae40c15f8bd3c9eb063dcfdaec189b21f95c6f1f0bac31a5c49704205c7795bda28396fa7baee3579232262a1214facc07b5698bbadd447a7c714dd82bc863754672afe722683ac8e0fc7a95babb2c288e8ac45d61f53707f13f3ae3812102b0fb7c11879871d6a8310c0470a34febea19c72e12fa2f2accbee2dce7da5c05810064800d3900a2ee3810b62758a43f8b91df5fed942413f5e6e231a733b254cbc8ef2b96623f75a0c52d5b54d6733df6f14f0abd374f75a7d8adf810b77f7805067d472130fb921a70dc504409300b4aa0eea154392c503b6721b56147a4ca6f234bcc2376be27cfb09fa27721f58878d66ac2231e36114baa0506da92412f3a8c5f8f4549a8a99f2ef3245c316868660645bfc578285766392577244f2c4d7735d37e1bb6e879b3183cefdd047b1ad25aed046b80f2eb7f8d01a2070004758501ca245c3c1361dd166f6b255d7218720e4aa7793293946d17106641b15feeec1dd108c5ff616352306ac4e82f6463eeb81d14ef50eaad9e4941176ee900ae50170a0b6c54d4a7fbab1b42c5af16b8316864ebccac868063a4dc27d83c3c36a4274d45ba2fdb9eeafea711421fb1440c8a63429c5c255f56db7e58a33893f91f3fd9cd2664fd413aab7b948f3910c2ea2c7991481a5763b700316508342db76f8bef3d24116674c721ba6f6c9b9aea7d71b82927b9a137a7969ea5ff70ac10bb5f302114c36fd50e96ee5049ef42b879825d73df1035a93b1e179c288060b26878101725c1094852e7dae06db36ce0f805ffaad192916ab077f6ad6c443c82db1f7501338e43aa7733b061dc739c970cf1145d32030781cba955999049613489acb3536ef03dd5a90179e8ef78e23161e779c175bbecafbb2ee627a77ad512c00ec668eed1fe6092156c404dc78823c5d62460a7c01f16f25e3094e59f511a017694b95e2294740dfd9603005d16b120c6160950b3d6a90f4110b7b1ba949991ae56741935771b024a8397ab3e9c23e8d77618670273fa99666b6d3d851ab3223782685de7aa3c617a92ada5a9dc98d10b78a7969cffb0961ead3b3eb0ed5ad2600a19965c568d55643e0d35759e1ed21d152a44cbf0c569fd6cc9f9ca5a2f81866318e40aaab5124ef7caeca3fe7f7df413736e4d96d8b0c1f4c654e4239c2c9906b47ac868671c39fdbec5292fe9b7c90234001983527fe752798d311efb6fa769fffd3355c37344a98826f3e9c4d6fd57a09e750f2478e022885b48cd300059c67f60eb297b2ca49088f2714398cbbbe106c64de7dd7fb7c1bfb032ad3303b16991e7b6c30aa890923ae3081f172e5de0644fe4c528728cc94b90b90550644943ea34e9e7c78c3a888240da26524dc5f67fc460242f4f79ca10bfc0d9222bc58b528fecd19cf09e48f364484cf837592eb3c8fe2443c1ed2c80bdff153737efef0f6a0f8f684ff5212faaee3f0a3dd2ea8e8bfa80f36d70bf9ce786bef909a2d94c37c47fdddd0e3bf32c4e92f85dc3607b39c4fe8b76e7b72527f6928ef0139f9f8404b283fe55dacf40b6f192a5e1ecec1e672c2cd2f7fa932452c46743a5672a1bba62af3c1a892d8b2363cf2719965f7d0106c8694421e5cfe4dae0f294f71d02230e80eb3aebf84ac8a7da52433ea05e66774efd602a0e5c4d804bbd7d776f485f176331931efb1bad80f6aee3c6b4277a8ba9f4f1cc7da4148a7f826e876301c4e927a8644e32843e70e7560bcdd29e24cf3eca24499bb71da440ccc831288db73b8fa73f33956a0eb3228e77024f772fbd227f8c818e8ee0a1cb05ee37ab3f37e002881fc7799d1995221cd4dceae4e10df738ea815040c10f16a116362065cb159e1cd019f8f3d12c717c3db15f4f0667929d4c4e97fdac5036f9ee3101752792fc00e6ff1f391663cb881878e7cf0eac818ce4bde908439431962c79bf3c3c9fd018ce55c63e7d0254619b3028f27e49cd3285eff08f2086c85b18974646c3967e0d32588801383f9510a0d7d89a46e138bb501a03ea8be809a013128190082e256e9330ef90381c60161bec78efe7b8da1c62c8c7fb250bd71988e9cfb28600a48ccfc80c33839a4832f3b8c330ecf3da44861b6a3a37468ec1116fd800d06ac8758dfd7c5b4aa5170869386e06c68eeb86cc45358ee8ea798a873efe05310a3f2b470a0eb544b6e26cc831fe11eb3de2067dbe212d686868a328367b8eaccbd24aeb4e5d45deb5f3f36107db7da07fa4f1cc030930b9c0922f72f339b897c505a3efb284066a74b6c3e6f45c5cf88eb87a0ddf31fa1b8e36926aaec98ddec110c764cef07c715b216a48d9a61e41103d7de33542201ad6299d7c53a19ebfabcf5d45d85e73e089b2bf493504de334324160b4c012224ce77a3c5e394aea91d00a1d7bccc4a9be8923e3d7568e994d04fc5b876e1b60251d931ae44cfa28d374f1ee1f3fd4dc2a0e95314465980bb1151d6bd3cb26715018915c022a853dd11ef5dd9db953eb6f9480aa3458f1effb3661eed29d9b24fcf9baa19398fd9fbb07e3717e5e17432161fb278d0a8d464c5eca1e1310efaf420a9769cb819470976946edd6cca63e2b38a7a4ea06c63be22e45227cb3075efa6fdc24f62ca7425d8c0d2191ffb0f30ddf3ecacf97d472bfb2be7dd47f20ccc90387fef311d8153a79e32daf8b2684913137e777f64dcfbcdb91cd136071c50e714511a3efd3e731aed09456fa13fa9961dc093d722527c6f61cfc59c0a665079b08f34bb1edc3ba1d91041682cbbe4656d408c9843150bbc04e34fd7ccee36e4f8b80dc7cd5e5510187db841d80677a023155ec042b9464933aa804eb93b580cfca62890b368425bad0104a64f73cdac6b07c6d00c36bd919cbf743fd0e42a91af98743512352313b0d275cdc8bda81b44ca5724c4f7f6e19e63c3feae7d8ed9d1530eeab32ff1f041fa96ddecd7118d3bfaf5e8aafc3545a7e627a968d528b1281f2152f0c1687bf9e56ca7e23d77b11bfa113e69c3f2997a6eec1aa5812b90698c6cc7f00c8057982bc51115379d22d4085f10465b03747d0b9b0b47b834a580f4ba97dcdd22a2c1a3017150d32204275117fd7e2c75ed356923292ea34386d5fdec8edf89c19116c31d560006a1493847f22119c66085bdced7c85fc9b604e955d6fb9d99828cab25ad723088a71d1f64e064bb0837dec016b1e9028c997013e27ad4a3cdfb872ec387175b4f0c62cd651a396078d9d8b5d9d873600928e7d75aaa462f23bb546376090701801d6a7a3c4c1c1a7b76a6240914658d661b1c293925327e0bd10d5666532a47fbf599e65ca86943319e740dfb503599149581d0f0d19548aca10561a3ccba8ea6722e260eae1ca79e4fd398f8424e6464a912c270c836fba4ed438cddefbe23c92e272996d0521273036272fd5f21a2c3c1bf32170d94096aae6dbf7d0003f039fcc0b8e7cfe104066b71693e4057d366954f5134abd0b48c25ffd36dfee0601b72d921d0915892222a4ead1bd63c605c790f414259ec9ea7ee883c83579358134563511a0e570e95b97046f8df4bb002d9092e1f4d703516dcde7084a3cdcdc3d4345470549eb8e72ae9046b3d30c3ea471d915bb9f673fd1a7bc23b09f9e2c1080047a3c306e20bbd2a7314e4a38fc8cb393cbdb824d4a7e411525c230c9abdc2d7ca6ca73b216a554148c0ae5445424b84971bad083322997fb0da8fc29bca9dd20c8f642ef825a7de880248bfe94e960803381e0cde36dc5a71fd9b053f6c8d7a19e54ab327104658a430b018426344f1dddb23f8fe64d60031ce0cf4a1019d2907cfb3d4198c207a9af60bba635aa4caec049fba537dafc2c098410761f1a0fec9165dd119e9f6d96f3ae6b3eee46daa66d7995f7f7213ac826966c33aaf261c73b20a40b91d0517bb9afc1fc6a3a74faceef666a4074aaee104c135c7f45a44083c0d35fd13c7a2842dd37389a069454793eb79b5823a25202a9a8bacfd1e6d5cd1d71a7fd61775b426d1b0c17ac6d0730b8107c248edb807bde1000419cbf75348140c26b8dc31693765e35cc251599510cba8c5415ca9deb3d5d4c512d4b2c18d7eb1dbcd790ef68f716675703bc785a395971d77c3a5bf81d804c36d7bcfc5eb372127b30c90a6b38edbae78a3f53322d55aadfd0f837e849f90b2ec8eda5ee519b8be19a1aaaecfd1e98c635a0d5038bd234c58eb10b3ee5ce23123f8454c586bc1f19b2121f30e3bf15932d6068bb52cc0691f24bbedb566ba458cc5b95ffd4187edaa63d8b1667085e06d4a5d22e3a5cfa2c162586d05a63620a8b080e8eb61243c0d771a80e17cdba05f39a81359ee619f6f3cd42ed566b087c1c1a11a02871a1324e47680c8e59ae17705ad5b1ecf505c191d3561877317f7b44d255ae952e81f6bcc0e59e0c401ec738ac01a159d42dfc7066b0de86951e2b77317719c3d88177bda7ac7159a65c62742ff582e91664d130352e48ef13c927fefdece2c41192bda9173887749e2ee26efb68d69213a3bdbbc87eafb47fa723d806d6b45333ce1a2348cf566a6d975e9d52c664ad93c4b050325f868a364fd2e8c89f0db9a3347c7c53b4edaa71268e70a28caa492ae34ec4d9570454b596592de9641d3f7b624b0f45dddde2222dfe8485d22ebbd65583c17211246951fd3f809f1f9a3ff0acf1e7fb9ea6b759e7ea31bcde103afd35f290e48b944c380a4dd3ef669aa49c98a2f01629c1b2919427345e27fc0b29b36f24c7212208fbe36c531e73bf2313ad5544a76e1812adb662ff95486a6e73d8e02f5bd52bbca26ef0b499ee09c8d7741a1fd0b92865e27e73dd4f8bcc541b1d6e9ce821dbd8e1cf2c47e31d4fc81df97a3df7268f4bad1f56db0509190359f71c7fe70ee313d49be16f6976ca636d801a52c9a1288a5d7fae906e29460ccd6d86b196a65c5cf1c6356870a897ba030ff73fa03688bcff69c1fa04393ca81ba3263b117a99a84ddfef97b0023ab0fa7d928b5dadd813dadf27087691897a9edf70740f81d881e306f118f9b9fa445f7d108954e9b39df5e5630128d7be14805b8f5c817a2be64484bb12f07f08b1c7cd06557959a4b7b12063b6b0b5647c1964aa074a86ca043c5d20f1a272bde11fd475ff133ab2c3e561fb69f2a38322ba33a150ea6fff6efe2e80d51754946e5abcd4a17da47a2e026046d578268862e78b5891fa442ac16a2081f232631e00227acabb25e9630159d7b16e613d5f86d1827f9d195e6fd7b883de7f388efcfc60b3ece61873acf55bd35a5154df1067f367f8b553d8cc8a34be71431a571abe6499629721625f72def0423920baa11fc414a6a0a60668e5e90f0a37fbb33635c7e4613ea9da64a8598448b464727c549e4665069c6354f33bdb8857c1721c78834313b1a79e2609c57ec3b80b90827fdf66be60b7a42fe56bbb94e486cfc3c1f437029ee582864b5b713d5493d2f326fbc595d38e523bd9dd412adbc27bdd8c05631188242261c01b3adddbd7d9400d5fa3e6fe6287b0c6ea7ebaf72a70b29d18ceab746d200b1ba8ec6669f6c12669c240e70372d8339743859fe111c5d2fb710ec8f934d15e14c909030fc719ae936bcb71df5970e805e3097fa2bae76cd4878edc6a0f62d1df707190892e330b9688b82e5964b469c2330b30f1243229591c64553f5c35b91aa33f694eb44cd370f13a3368e92954d04a0c3811da854cbd88f580f0be567d37e35292919e197b0df2497af534ab9dbd24150732e7a6797290d7d82b6f700c4053bf62ab7963c19329cbaa398ba0c63367cf3b88ef06b30214ec3b5a77b1de8d858330b58c6e5e6549a9200d252bf092e38c252b918b7d30eb96a727ffa6c503cfcabcedf12c4150b3b74c9da6f2faa908490d73815bdc31d2102f6241ff1f8071bf237ad2de32d775d0937147c5b8e4db2277743728af110b8e198a18d347edbba689dec3b9c4ad7415d1336d672d7d57b2ed2686bf73e45958e31c85780b853b2c35f156102b219bb72f91b253ff69405fd51da95c45c00a15fcc512779ab51f6606b33f895467449ab5bcd4987f54d7b94f320b056eab033cc912f2b72e8ec588b9a69433cd6378d39cceee621d5e9e65b6674db1998b7808c3cf7e7184c84a232d0a2bb2fbcad0b69a5155c4983057a34bf802e7162cafecd5501a4aa699bef00c1427102362a8ee0c939405437052eb7795d6ab6afe7028aec2a30fdba416552a6707aacb7170f34a704e472407cc21c99b3910df1e83e901f143a2cb403010532b56578dc00a89670cd9fe0b32947cb546dcd472f35ee47153379b2d6d1afb0dab18901a51ab339a1547bf0bd6e7a8f453b0773335b20ddcc30450c38d856b29546c784195e017bbb6ebd2f18fb980f95cb2e17f34427923b66f8eac1c0a5e00628e4f0e6ceaf7f2223133c751626e0cad48b1ebbd40a9d3f8451a6286c2484d96312d33c9c663f96ec3bc7d7a4b874b3a6114493d07447e55465f87a686d76414c9f8d41fca0110b8f41a67488a849df41a09a2870190032e6beda58ed15b395f94b5eb991b750453ca8fe1ac1c45d8d762c1af8399df75c9eaa13ed143ae5fa1d50720dbef1e25249f13f9df9355e3f7b7f01c80145469a51dce75cadb9b971beb1f7cb8b4bfdfe93d8f4d000e773dd876dc3f21f83ac0694dcc7d3415804ae94ee2974ee5030ef561611cea9e37af168c2d5cc82a5ddbcacafd8414eed8424e1db5bb90130706e3a66979f7487431ccd5d77dcfddc9973c9f4914bc1a69a00e89d42b42cffcb51c5b0dfa7544f3076912f0c86694cf3f9ddd9d9a2dba554e3a0dba44d7ce7adcbb544155e9b4755d63ba3a9a15e231ea305b8f9f8e96b737dd5d54202409e9fb9078c462f6264ece361a9100cda0a13b30a2ebb3678113e69b658dc0dac79d1775edf30ed69459652c508cbfe026694699caafe0be51f9258a294506d5a87f4a6a0e7b92dfc1cd7c588971fe782c896e8574b8a08d873384581d40d46ce9b2f60042788cb456813c5492396e0ddecae8c3082a661baf294b357372b5e9a2a598e248f68d28868d479f22c65cb7beab16e5cc7dc0f862bf332c582639b2e0e7220d2292710e8834e88066c56c97a5aad6d6eeb052a217ea88203a01d501bfa1d2508f6641783d99cb3050470fb18a2e3578684d8dcd87b85b17f67e6d6ea4540b40895686107cb4ce002aedb8f8d4c973525f032f98d9c982254bb6ad5e736987e45e351d934785faf8bf4aab7613ecb62b9afb418e4cee10f02025870f4074d205ecd2525c82cd35990cf449e342c82295ab8629dee5af22aba75085a2489096754bb4816a3f1d9432a5ad13a4dd391c3e23e499521c4b8195b70c7176ad7ecd9c583aae92cee1f829af01fb2217fd7eef27e64d8e99818164b80375ced2c3ed0fffba81703610afe19034cb4131074e3db8752672c2cab30468bf1427817623dc7071d01eb47bd2164051828ff43a77f5714a2c4d352757fd4939057bc8c035c51986116857f914bcaea17b378cdae49032c5702927370d8bc3599bdc931e397a88c3f3df5cb95ee34d52891c3af724f7be0de3219b189860a0898122d5f7eab63258a0d221feeafedd7bab567c0978141fb340c0d4c34ae803564d95c7c2cdadc6cd336c0f72fcc303e813d4a08252312a9db9bb90606c139608ba8a135cbddab032710a7cf22066fd6cce4f502c4e15766e446ccbfdcd7d251f64c8a559a21aa4473458fd25fb93a0302cf94d205af18d0c780f3a6dbc993e0b023ce889cea9971532a2020ba1d38c6990f1920360bf2f5a29db2c3b8fa0942fe78ac3d5b0f55ff1cf3ed0ecc5a05f147d84712a8a38109581b464024b738ef21217f610922fa399c9d2886135430420a711ca9da665d81ce954bbb3f4b7720100c233c2582cc4001b12b5af189f8980a3c5f85ba9de928918c1a3853e09fd00de8163cef1484e0a97fc82ae7e2fe1b4d4ff8b2e308c20c8d3358eafa7eff2ec5f2ddb6187a9f9cb18b9666d27819ac8f527a24e0916a69dbdbfc0335d29878ccff9bdbe0cc3e30aaa16d48fe5c12002a726609bef45fcf3cc4e1cc7fa57bb37d575b2fb9d6a4a920b346992c9fb3d840f6c3ffb087786288fe2e0168f1fe2040e2c30f54b10dd92aa1968e723d5231aa469e5faf0107d6f5f8ed0dcbd6897774d785e5566b381ecfb99ee9e92dd40a7dcfd958bf02277b2ddc9cc8dcb66c3e7406fed30e2288a68b2dcad8fa7833110bd5f9ccdabd5305ef01009e9f682a4b75f7298dedeb0701de6e23b816823121be786a364dbb354f323637348b56ff0001cc35b6a880e3254d05750c57ce50a77bf9a3150cd7a7ab1a6b591bfd6914baad0d97fe7fddb750cb3a3c43f91163710eb1b229d5b98bb5b2fbe48a368146c181325d16d018a54a8ec7b1f7e82c6ee76355d4ed5db9502805f4482a2c2945887773230d599dd2af2b7cdd24cf5ac719b2a88c2147882455a23f903128f8147533cd9c0ac57e64432557de6f43b34450e2d93372881097f53513374f009071de6ad8e74091e5d8ebbb23aa49f85626e2f5136199c8acd9fc2d29e45677f4d575361aa760f38afbc3b6f46acba5f83f2afa6e1fbbdef2ac707a241700d9e3e5eb7942be4db30ee7befaefa715d413fbdcda29f39b66d4f528ef49c507fc63e2bae63b07485d9a733ec50b3c557f6a6f1e7b21fc6ec3b0a46d704f059e09b77952c69f882af484d1df1f3ab0d61ec0dadc8ff3c9aa5a7549e5bc9a1cc9403268736fb5b880c2ff7441e0d642b1fb7cc1ceb62bbc215118d0035e80fbbd06bdb1e5c807e504721701d413cf90c93fef433d3cfdf90b971ad8ca5602da2a158523553cc8103e451a4d4481e366fb694bee73c512d11b0c1466fd032640c1dcc8ffe367ef10510f6f56cd9ac461af82c84aa9c29ffbaa08772b0834dc7e3c8c94992ccf3d85f9c30eb055897945fe3f8f5c20d075412270200ede7ee04431cffd05a3bd6030c6519b2e9354274183d3bebe84df615bb801f5323b56866fe6724c71389c59ab9c8e5659067289db9309a50b67e00dffdf9491214e75fb039732712c8713509ced0131c0b4a70195ba4c8ec0b283a22c7b0dbf1fc6412ed2c4de82859e6abd3f51180d0d804e499c2ee45002613fb99f968590283d52786289f149ece6461deed0759ac6e676a0a9b298aa163f0e24c96a0a98e0c0852029aa42dfdab437bda93efb23edea1a740ce383cb35d81744fcd9a6be9ef47e2542f680061bbbe4975e70d160ea6f6f4e737178e789e45eade59957130d30e5aae84a40444da2b6f30c458054be0608abc611221c80d3a194cc2d2566904c4acd1ee8382caef84095c2e80f4ddbd836fab2a862108b2adef33ac9471aec7765a995367a901fb3af6b40309937d205777154a7262d31cb03b48f16438a9187b742410d097dcdf4807278fc11c35304fc9fc868ef696323aa5f91aad21ece631fb45dbcca5d43f5c9ff259d530c306e5110677aa345a0c11dfb973ddd11a4c50198f826ee3306febb5c547a17816183f00237edc975b99e3288bedb4c40a9b91b95e65be7e1241ec820eea49089dcd61019cbe44f6869f6be914b33965f82e20c672815c3e932e2bbd637e38034572e01b9c38a9b80a30eb4af10d4152773487211da08141226a60f818a1cab2304ce7f435e03154bb801491fffeccff1f8c0c02236c5a52201e7731cb094fb0061d569e5066e1c28f37f6ddbf859dfa58b4588a93b1213981e291efc2995fe312611349407e3c6c9e27cda85453f72c75f611bb62bd1d664df72a04d9f7c54434f64561a4279373b901dcbdcd9e56a20630d838b2d76ccd5112389233ec95006d87476c9e3ccd6c376f4035fa58e8ab9e1d60959172e6626dce3ff6db707678e462f99579557d2dd4b71b353fe48e9ec7ad04c55576a4971e8b2bba9426f56db399d1d25923a3c71e7c0655904af4139a1aaeef2453ef0d6420becbce48d6e5041e0410423e9742403c92db38e65cdb7dc290c2a92f63380f85ec463a07b28960fc40456909ba96e5d40816b2e1cff8ce8898871de7feb394c739a1520437ebe9694e7119a08d221019cf12846dc58c1451bea34307450277311b01e2898618102670acc8a388819becff18ae7b9e900fe53e894b380a0a6e60a897e910d81229d525a9180029147f02cbb0366ebc698d4d80fd0a18697e69f883d6dac496a5b6ffc42aa9763a417db0a37d9941c116c311e04fdcd96f01b84ed058c7cfbb2a07763927e43b936d3e312eab38e802890736be6303755f5fdd8ac8be41842987d5d9f6d0afd87006448cd51b90ffb430c93e5e0692007b82dba29bc0c138c6ef8343fad430a23146ed6df1bbf1b36df6dd18056e31bf29040782cebda4e26c9ade103a369bf8acc57d07034460f170b6b80a0d90708f9e0d6ddfaf7996944715304ad3e42a5cc88825806eb276aee9df8e20e1b533cfc0b9f9269caf8c0b32d8056057ef670b96af062537027cac8cc8bba92c5a0da4eea4880358eb62bbeb5d76e792e1c586d6837de51e42629d15a493a874c9feb8b7d84ff5864a8fadc03367c980e5d98e6c5bd3383c677e485f0c0c9dc9f49fdf2edff43e8ee514e5000fe2423f1c65e437c4426aecebb0bca3022b6324f219bd824d42c8b981564c802993bf3844ac9703b2d2fe32d6f0d555575df68cf1ac49bb28cc564aeab893942a2acb4810ec39017f7df1fa9f276a25ae59dadc7e298ad6e40b82c1f79e1fbcd49e01de4e754a311f4df428b31bb2aa251bc4fe3cd0be4a23c32b1eaed0c29e930a1161cdaf51e8439452bf928e0a600a4b3048c52c00d1927fbdffcbda35015ba1c93e1331c7612243b4998151df56e69c169678da810eb54cd7b4566dec78d3089be15016fe9bd9e0784c7e94e44039aa2941a77afd746fe51e152fd4963479267a3a1c4299ae0ee2346dc75eb9c3d06210e7b4394370c59cc613e0af1a5f316228fc035ef1027b34b8cef22fe179a3c345799fee6932d953abedf3e53653c76157beed0c142f8a1d8ed079b03be5fb7bc6a60c62cac42e0a905e0193c4a9a74a128772542d53327a93b30e8001efe9abf705c9410bac1d760b56c387476481c1d0df996ee4147c48026e7f455f0d2e70b4940e000c30c000030c30c040701bc4752559a950a60e5ccede00be297353923649d64684c6c584c6c544d897280dfb0c000d05ed5499fcb1d38535a554176aa11e68870b6b94665a4a39d336ce16762063e1289e77485a0bcbf763b06a394a867016f6a0a124ba41ca560c16f6d090524cc4fd87e60a8b98c74dbe98f1038d1536adbe3c9141f0303155d87a6433da870e2467a8b0e583f45929b443be4c618b616258f0c813d3228575f3665075932be31a851da4bfb42133c5e42814f68e8aa1b19369a7e813b654f21bf2857558d4097b4a9bf667724e8b6a135675f0e3d739440a95093bca9cd191ee66ca74095bc8e819c4ce500fa712b634d1247476ce679984451c3df4f0649f538984e53af94dbc494f361e6133ef1053e6ede41434c2de536279e1a25a148bb0aa7fbef88cfb954388b07ee78be915ab264d86b0dee76c469d84b0c8a6e431ea2058fa82b0dc7f8e270f4b293d20ec3874ff7aa63187fe602de99c29a59136c407fb477218728c959ab31eac394ff97ccc5be9613cd8ce2cf5aad442a2b5832dc7a4b75329a399a583e533234bff9841c8930172b0fd4f55e8983efd840c8083e5bc1ae5905421f171b7582d47c621ce655c639c2d164bc761364cc7f25aecddd38f52861ab369b145e98f161be57cc82cf6f848ac742ea31f2f8b75eb51f288561d8d8fc55a53e2d7a1428aecb05852089509e5601ee4bc62739072d347470fbf2b76b019e628c374f5add8bee4f45148f2b164c5e679b36773c4375cc51a3e690c2162e287a862d190d97cfe7399eb54ec91a4bc7388d628d4a8d87e3b83c9b08f297d8ad573778d468d1d6a36c5ea481dd7a50e530f2ec5a6910ea4322d334f8a1d260797d636a35852f22e4e44bbca1851ec69aae1c86842b1a79cead7b1b73e2fa0583c273d7f4a939d2b9fd8734da806a2e7a12b9ed8530e15fdf42af32a9dd8434f07cda9441dad9c5827751fc7910faad14dec2867fcb02ecf63a99a58b3a763ced80f39a29958d671b2f0305486a59858249a477e9834b29397d8c637d287c6a88aa625d61cdd0f5134c334c94a6c15b1751acb623f92124bae7cf995a1e67f9cc412d2776a5487710d94c41a5592f92627843889c45e9f699e515d5e4c0c123b88aae0e7e9d693c41eb1559ee4e0323d4c133147ec99e38ce48a9ebe41ac119b8f748639724a393f63c4f6d9695e9363657eb6882d8e87cd8b8b569f3345ac23b1573967ee5bcf12b167383a13f3731ecd0c119b55eaabd4cb0eb18fa7147975693aa7cc107bd57d946b9c9d725921d61ab5e429555c8a6584d81a555cc9f7d13a5636882de7adcf61bcba246482d8f741b050f952c77a58203689fcb70e33cc3f0d03c416ca3f47fe154ff1ffc3fafb4173e77da4fcfdb04ff0d987d1326bfd3eeca82e4b4394f59fcf871d6c4e0e298a8778fe1e96dbda33e9cc49e1eb618d670f3523cb98f29e873df49283941482e4381e36cf0f8bf92476def81db69493c5d5c6edb0d5a6bb543db9abf13a2c9727258960f137753aec18259f8c7ce28788cf613d9fc69b1646d473e4b0f85ddeff10711c3d71d82e35e2ee79180b1e38ac99127426de7a65e70d5b8cdbf399d9fbd071c376df602d754637d269c39663dc0f590e1dc6cd86cd6a3338ee8c61d45ec3963a1e8349b61a36cba01bb7eb81c5390deb48e78d52c93c9bd1b0683786a9216fa283cfb0f8fa5eb81c331cd99861dbb416222733dfcd9461d57a64319d5694a892618f9bb89624fc9ea56358524d4a8f66f1d24f31ec3f2529634ebb1acf30ec3977c671a25c4828c1b0a7e47e102155731abff044cc5d3ea917d65bbd9c3982e897d885a52c83c88e8f9d4ae4c256b93bc7875e1346dcc2f255617ba2c4ca19a8853dd4c49dd5ca8cf3240bcb967aa43507192b040b6bbc4751663999577285fd73520f19828578c40a6b380a31e2a7d5ff5558cbe2e4763263f4478535c66a2a5c5e68ce4d61f5600ee3b2f23a7d52d853e3c38771738aeaa2b0c69b0ffb413c8dc4a0b06dac987346b292883d61998699a28350966b39618925317743c918aad5843d775d7549c4ea9298b0a39d108337c8891a2d6169904c3a2c342eaa843dc5466fa41c27c624ac9df341485d740c1c85842585c6186a6ed411b6f99fbd14339ea9514658344c741ea4f7638c2ac296d2e15d296e34cf136199b48c3a0f618d31fe31ba861d2c21ec3096c3cb39e37482b0566d74c5ac0fe17820ecd92176e56b3cd73fd82ec64f396348c3a0fb60ad7ea41227e142da83451ea61033720ac08325544d8a0c96a2d95d801d2c1b3fdc4f28a94ebb003ad8318e9956536c0cba2e400e960a1126963bd267740170b0c6e4b43197f76a946fb176c67b96b9f63ac6b6d84c2576a73e5f655c8b653d4ee70c2ff9584c8b1de30a911752a46ef02c96480fd9fcec8cf6228b3df23988c6f7917389c516e3db30733e896481c58e7f27f67e87ba4d5fb139d0941f5f3e134b5db103d3f91424667492b662ef0caf4769a51b0759b1fad4a4b58c1ae7435ac5e2e11beb40a28a6d2e39c83417835f24156b4d458a7ab15b61546c19761e9cf9a71c3dc5969eca27363b5c77a6d8636eea2abba641578a1df959663c8f437c1c29f6f975247b711df2a3d87335c7ca7361cd12c5162b678e21fe61842c14ebda4efe74112c3f0c147b4d9cde7851970efbc4be13e42e478f647979624b0eb3663a74fcae4e2cb97445d3993a2a8b137b9e079b06b1bc6edac45a963c4ffde4a6106962c7f74124a47a5c7c502616bddcbfd21dadf44198582c4f77dca43d870ebac412ac3776f9c3ee14b7c4d2a5e163b2bc128bc967eeaff59bc829b18ccce44d886b933a93582b6e58debb25b185d0919a364e4c3947620da39bf5a71d6c65482c9db1a278ccfe8dfc88b5c43f5d64c6114b839fb0fa7c639569c47a295a44bdb39c7761c496f158c81fd52ee28bd82c34869a8e50b95645ecd1cc37755c7314d6446cd9207cf0d40fe5828865662a4b743ed3cb432cfa71fb61868f9bd210aba7b85f617305b30bb1a6f8a3f59fba4626c4123b2af56fed1a3c883568aac7f315991205b17f657c1a92a8fe858158a3efc983ca30860c882dcae391bf8bd5dc1f765ce791cc42e50e113f6c96e366eee5f4d4207d58fbe1c5f015dabff2619dbc6a3b261e72c6ed61cf30e7b8f126421ca787c5566206b1b63c2c957cc77c754d420c0f7bf8943b6c712cf6c5eb7c55db61f35ecd9206331eaec3f655b934e3dbdfade8b04c9a989b6993327e0e9be7443bff9afc1539ecd034348d58e59d71d8b2e124d1b81c3ac261adac06761d7fddd11bb68ef09533885bde1983006ed87322d89ddc46cd8d418036ac1e1b3b88444a960408c0862d4543481995976d0ca1600513d01f10600d8b0319db2093fb779a1ab63ce65b36aa21fbcc34ecf126c48498baa48d44c372a725b6197d4f86e619d6bce099a387ceeee3cdb046ccfdb9a365ad46cbb0a5f8112cd53ee5873964581b79c74893fc316ce3fd3de952df474f11c31653ca19f129457c180dc31e72c8c1319c8e866a83614d0f7d43c31cf2c6a45f58a42c536723672e632face93398d81c3b6d90ac0b7bc89342c4703184d91c2eac9ae26ae5aa0c4254b6b07aedddf5a418e3fad2c2163545cb0d926f8810cac252576ab9d16a83890c16b6cbab1fbde63979feafb0848a948408f99cda5961f59433c80df3868fe1afc28e7392182af67174f354d852bd26658a2893616a0a6bc67ac31a3bc7791f52d81b843bab8a5d0e421d853d2393fa0fba97620e0afb6e7a1c62901cc7317bc2de20868df9d2f56f8613969ad114a22ba514626cc292fbd51399e7e36764c27e1ab1bb3db621265ec21a628c68a59537295f4a58aa73ef61a93a46399f84f556d622747e3ed33c12b6b492379d6e78ae0e1f619bfdc87916834e679411b6d3bca4258d2a348c14617b942a67982274c8771161b14fab9bc299e6c9c710364db964b13f3444c684b05f8e6de6cc254d2b08cbfc8649588c81b0d7f8846af4e330eee6074b8a21a709d98d514ff4c1323956bec6ff1121a4f460abea502a9eae31e88e077b8c4b55191deb1ce4110602ec601ded4855172202e860499b9252e13fc25c740402e46079a81229b9763f790a0170b074d894d21ac78f1acf5bac71a4bea76763ca61b4c51a35e432de18a29bacc5a6fe95c36426dda7a88b2477010b8a1ae0010934400603a0c59641e2c579873497fe2e924810031658bd008906cb05174918c02cd65f8fae39914f196764b184de182533ccb8514563b179ce04d10d916e7463b0d87724c5bc21f2c6c8f52b763cea492b65fe3421870424a852c107b8e0e2b8627b60bfbf73bf5569d245b66000ad58ea42ca182b4e5acc1d566c92e2a6484448367bbd20cb0a0358c5de51d9931d43e3b3e3820b2ee8c83000556c1a2fdc54c678c31fa5048901a4629033a34da83cfdd56100a858ca833eca959df92e499e18c02976508f3e44c84dfcf1060330c5a28d43ce5f49cfd5ad60052e381b0ca014dbf5a410cff5ab0fbd0b1898600624a8810b60408aad73c2e4f8a35e1922198511e14ea52b1c7cb84571c73aac0c0e73582d4331ccc04348040b8f195d2491402b0e0300c516631d9f69b0e0d5e84fac132a95536708d2bf27b698fc2936bed00d1d547430804e6c8d62431bad4611ba2e41924b209513abe9570a9f112d838cef22899c02ac30804dec1f35ca490899722caf8b24a30201176cd8d93026c01603d0c426391e43caf829a730779194d48047a59c647a25aec5527fe9b36be5f77e5aec31ff8659cff711f3b3d846bdfb2667fb4697c59af3e86cf0c46219cbc956ab33241b168b87c6385191af635eb1748c1b1f4d39e6e9b862b58c8268ce70b5fb56ac5a77feb0f1831066c53a1f63e83ecad4995ec55ed538a7c9fdf31b55ec13f395d12f5f2653b16846a978d428a6a8d852af466654351be1536c51bb73944e9e6636c55a9f1fc6bb20569f29c5b6f7b53966902bf3a4583b37a7fea38e4f65145bfe89fb979b2aafa2585546f6470dc512bd9dfb3705c51e12e18d24c61c1ffa897de24f9aa54b590ff5c45a91b1ebc852cae9ecc46a5ebf2144c65f8de4c4fe513dc4c6c84d6c7e214a324cf98851138bf674c8f834124cccc412f949f22e6cfa0431b177348cdd19677a849758d5373b36840ccb134b6ca124a6fafc71f34e2ab14c6ac817663c544d28b1ca58aa58df603b9149ec3978be4ef96a6296c472393b7fc3d91d4b24f61836ce94cdc89790d81e6720df971632788f584dd4d374ee1cb148ce31e3f14979d93562d5b4fa59131969ea18b15d48c6304dbe936d8bd8624e3925ad4d114baa0e87b192e6299588354a82c6fc78b43e64109147d499c6c0436cb9f25583f1943cc4107bc7cae8e4d1603f2ac4e63f9e7191552127c4fa38e61446a3366dc30c62b15ac9e9c19c9e2c882d797566673e106b5a70b8a13e20b693b5cb7c12bee5ff61499e522ac731ac96ef87a542cab519a4f8efbd0ffbcdcce8898a65c7f9b0398e347dd040d53f7bd8cf51aeaef9e861cd31628d67d967f0c9c376174efe73f00762e361ef0eb6295ef4dad577d83c37cea441fd9285edb06eacbe6062bf31721dd658d1a38427cdc9603a6c17c33236389fcdcd1cd66816b476ac268e460e4ba5c638492a5fbc4b1cb6df87979b1f776b81c39e3592b78df2864d42ae7c9b232319ea862d9949a4998841fb6cc31a196ec4e6c986c5c3a5feeaf7e99e6bd86fc5d1399028614935ec1bf3b73e1766d54cc38ec2c7eac73b4234d1b0a8c77f18d723c68f67d83a95a743f918d38666d8d16dec9fa95f0d6119f64915334c32068e2464d892e7dc17a28a448e8c618fd0f827aa7374fc8961cb1837ba685b09c3baf115352b0d65b602861d68482fadc6d50f2b5f583f2cdf84a34891afe2853d069bffaba9ed5c952eacb7c972f0cdb5dea970618d2126db88ff9f9dca16961c336abda6a2852dc5eee77f0c246ea86461e9eb0e15a1bc3ba382854543c7a1a7ebfd6e942bac9e32d3c7186a217f2bec21dee57421ade6f3555844c23fa4b39826fd545873755aafb89515fd29ac93529a87be558d7c292cd9992b792384c53e0a5b10930e26b936a70e14f6d56d90b1c3c7dce927ec316d0ed9ef9dcfb413b60c3484495d3761afba88a9193bae6a26ec388ec95aa545a37a096b8ce5ba5b0d6f1456c2de1f2db2c2ae3a9093b0a35ac993cbf9338891b0361ec9cb1834bca839c216ab2992e28f34678cb04c76a68fc690f12fc29e365a8f873f693c11b6aa10266cc6b13e9721ec733392734873131721ac5fa7e297813548294158aba2c9e7a3924a1520acbf93243da3b7a7f283a59269ca41e24bf33e58ec41a6a9dceb9bd3832dda4f883c2977f0280278b0ff5ee5f4c8e7955104ec608b19c97c5d97851045800e56cf4ebe9d6299738a801c6c716386b312bb41a508c0c16af17a93a64f92ff164b94ebbb9aa8e39fb4c556a16725324acd256bb1a4eae30fbd239a91b4d8828626fdedcbd9c859ec38424cbfa375a991b25822ee57aa3439e283b158d334d07218586cbf123c743da45a5eb139cef738cd53da892b960ed9cb21e63851c3562caa3943d39aa3641356ec239ea7a366154b9e942a1fe454b147cf1c5224f865ea52b194cce5b89ba262cffd382bc2764cfd145b2ae98d8e7ae41753ec5024efa224e52ec55af1657bd9513384146b7408212d1bc51ad2591ccf9728d6aeacd47741bcf7506c8e73e5301a3bf9028abda2846838213dc027f6888bbc1e32ee8b911ec0136b08d188121bc132d2037462bf0e1f8e7727ff3ff4009c586a37a6f90c73307fe80136b167146df4a327a131f4009a58834e0cda251d1eff3c4026d618a283ce31430dd1cf0360e25cd1559a3c7e8965746f1f68f949d82db14666485cf1c9a03f95d874e74183f8bff93e94d87a2a04e90de5a13e9358e6335dc836b0981d49ac12194d324b0fad3a9158435fca641907124b06d950a695c236ce23b692b80e0fcb31fded88cdf6e2679038315cba117b1a7b1cc239128d3523f6bb48760f6c337a5ec4be92515e6786e96256c41abd32ce4bf14146e1442c5d95923be39d451811dbe5efca9faa0a0e3ec4e62973621b9ec96e0cb19f7ea67edc2863630ab147d1185b497d326208b1effe4c1895988e5306b1d876678a91d151b40862ed2889bf890c2a5902b1d874434d21d7e7ad006247d3402d7c472b2a7fd841046918aa3622ed87d547e3428ed13e2c65b63983101e68a87c587eea91e7b29b1ca27bd853a6f054b929744ef5b065fe6dd00b396ea779d8f3d443234de261c95ddde06294e79a77582ac3a6b046da289976d8b74e4d1dc38e3dc93aacc11ce859ac250d493aac79266ef525dda8720ecbd823094b87e96694c38eaf2e73c957d605e3b0a9eae71c9dd2540ac2614917464ced33e804dfb09e45eda05b13e34337eca1a9438947a8aab00d7baad439847a663c13362c929ea2e73cd0d39035ec9df283454ffa713e35ec9dce2a04f5d2b0c34819f94af09c3e8686ad4fe28e5e3890a89d618721671425e59d5eccb079f8e47839c6f0c132ac19aed9655a473f293260f1315ccc498d61ab5249763a6993a4c4b05c9fc5ad1022564d61582e05bf702b816139cbd9d028cfd1a32f6cea31392af7331a79618d379636560caf88b20b4b8889c550f2bdfb482e2cb13fac9ae5871b1fb985cd3ec3c38ffbf8f3482d2cb3ab318ce6308f31320b5b4e9744c98f852df25fc887edb4a9bfc2aafb99992167f5f456587a52491cd3d4cd7b15f65a4775eb337351a7c276e2b03b4e3e85354c8f79752c8db814961c42dc466cd0e88cc2f628eed677070f00852567e5a4691e91b7830778c21ec74b6e5732e6d8c103386149bd13ca77ea31b4f1004dd86f3b1b6a3fb45c361e8009cba39fdd94da388f8d0758c252398f5264733e8ff1004ad86e3a2e9cd458c8311e2009fbda4ed0289ab2418c0740c2a6152ec4048d1554830738c2de207de7c349d5bfe0018cb0e61c9af267301f6ac1031461091f62ea376f70b4e20188b0e6da8cc23d1a8d18c5030c61d194ceb7a50a61b1b5ad0b750661fb8e6143c7bbce198130a8cc907e55fcc1127d3f9e7bf28508fa60f5b5681b927e4851d2832568df65b47121ec3cb81fc79bfb77b0a58c69213d67c3e860cfdb151b7b3d400e96b2c94857e223e51d0007dbfd48a89e3ca972768b7dd5f38ee39afdce6cb1a31cfcf427858f19568bed72ea39e9ad897ad16293f39861ca29eb4ccd62cdd395e4d46b2e4e288b3d6f9e60d2e9cc73c2582c75e63166bc19250d212c96185fcb9cbf9626e12bb69c733226e9bc5284ae58e26a1fcd442b9d085bb179080f7a67e26667202b96f8d15297c370153b0a8d5453ce43890355b1fc8fa40df3eb1119988a6ded53658e1e3a1a0351b1c84f0872c1d2b7f2a7d8422ad86759340edf14cb8547319f8dec69bc148b65eaa6648cc7d049b1894dc67b2144f0948f62ebee4ca331a95fe5a2d86c1d79fe78a9817f42b18e7db24671506ca9f6fd199fbf95f38975c34f94a67e8c7a3cb1060f693431e6957827f61cb5e3f943d6d6e6c4a6d73d12eec46ced4d2c55e52867aac760d5c4269fda8de44c2c5965d921c3fc39c29858536c5fd2de5c624b522b49631bc56f2cb154ce5f1a3661276d2ab1afcd9de7891c7e6228b1a7cd172773ca24360d2158fedddf6d8a24f68ce9f651c550566b24f65a554d9b3435c50a89a5ca1c84e4fdf853f4118b6f04d1305147acea20a9545a6a546923f65c16733ac598349a8cd8c41f36383dbd1ccc45ecf5b86625a78c7ea522d6d47fb19f730c5a6522b6896e9842c768dd48442ca9f364975f66f8e3219692d5a0a325653168886d4f52646da7a8060bb18f8664cc2135981c12628933990e574bb627835852c56ab89489a113416c97cf5278a393fa9040ac9b32c6a02f425f0e01c46adb9b72da8f19a3e40f7b9e67caff5ffa7d7e583e2744b4b895337d7d5822ca91c64bd78d727c583544da4e92f27ef4f6b0aee4c438793e76e4881ed6d0933d92e7618d25294dd9a7fd0f1ef6483e216dcc11efb0f7895dfc9439d8d861eb7e541ab623545a8735a409f61ff61b11a3c36a974e3b836a0a7f0eeb6a8f8488b127c672d853488efe99b451340edbe795e4d168f37170d8c17e5c6b942759f5863534ca75e4b1ac23372c3f52792449a87e541b3687314f88bb7131cd86fde397e4dfacb0f21af68b5137aa4a3b64d4b05cacdf8c9deb23250dcbad57081d1f693e090d7b681c2c6784b49e9f618bb6beda2985ca7410332c5d8e3393c668ee65d81f6a6f49e8c9b05d74905e8e1fc30e76337b636cf8ed625837e6a4214489162d1e861de647a99f622b630e8625d337befc112b5dfe854d73de65c4950ef3f1c2623901c730620ff37c514aef2a52cc2296c8ce69424e1a45ec0f75a2f67188bc9f49c492f930fce9a790c36610b16cd09eb234de4062e610fb7f86863146ca81650cb149059d8d9de12ff885d832c9e70a652a51e2845873566d79dac66cf8205629499fa17daeccb720d687299c4d705c9f7609c41a9226c79f9769765200b1f4e41b1d89e89192f287256dbeb55011ffce2a7ed8614446a15921e51c953eecb8e1a4d1eff061e9895c29ead54e83f7b04684e80e25b1f662f4b07a074b41336c8798c9c3bef317d3e799583178581e654926b3e81d96067b3fd1f234c8286a87f5a2247892681df6b878d31d3e3cff74d8b3635a0c63153f3e873d23f198f1e54c43510ecb74f9e6cd30e16c8bc396d3a598b995983282c3a68f71bc9442649cbe619d7895f2cec3ba1aea86d5be472337693dfedbb0558a0731cdace4e4b061b91cbdad98699f2a6bd8d226cbbb7fa61af69c274a3d4ea661fd1f8719ce4e37b81d0deb679cdbdbf29ee8f20c8b7d4ec6e1b0ce264b33ecd133c6299f858a69651916f9cc49d27888c1a724c36293a17fe5f5cd9d8f61ed5a9b50e56863c3c5b0e6a4e1624d8361d8a266d81d7167be3782618bb5db195ace68f1c62fec33293936a485301bbdb0a379dce9c2966b4246611d1bb5825cd8ce2ba3989437132a6e61e9b8dca892a8af03510b4bb060d62103adbd0966619ba8d28ee9195906211696ca46c922437dacff0a6b55c6f9f92c6f4e6d8575d3fb1e63183dc67015968c4b2337373f480ca1c2268ed4f174660afb7f0a957bd24c8f2a85ede344d3c8eab1948cc262de9db224a7be0d42614f3391affcec234dc813769c7bfbe96a423b77c20ef3520acb9c262c66bf377d1f1396f9e495750d3245be25ec713a1983efc7b2ca296149df101d79f549d841fe10294a277d4591b006497d65124d37e38eb077659857e1c633a233c252a9518458a8149a22ecb5b35e0f744ea226c21aea828d444a589587b003c98c34ca6e798c11c2525f31c3f40d323a084ba6de182e7f8647251096f1cf30c2564c57f783351b6ad4985259a1f960db8956a9bb95aa523dd853f014a258cea011321e6ce291a324cd6b013bd83e47cda386ac289e5a800eb60cb18e838447fbe15a400ed694f24fb49c28f6a85a000eb64c6191c1d37e77fe16ab434d66162ee2b3b7c5d2b30e3dc40549294eb5d86fb3fac3e595be9c68b19ffd87895439a2159ac5ea67612b394e165b9a4b1f2c796c4a9158ec69995327e386c620b058638a9e2b2d6af27eafd8f7611a95d017519e2bd6f38e07174a6d3c45ad582758c7cef9493e11b1623b8fbeb291779bff556c2156c8884893d8b32af689dca99a3aca844b2ad675bce17188a062d174c171943dbbbc4eb1df57c50e490d66a4d12e83421150802996fd30d628fea5d83b86c50a29b3c3e749b1a5b90af97248079b1fc5d2f854373c66c8e78b6271e0b981ffc79c1d0fc596267542ed038ac5e3d1388a948629e7133b0e1b37c73a584d8e27961c711afd934a659c4eac1b620c9d2a3ace7173622f930c2d641e4fda9b5847524cbb18e283ae35b19437b8ae8f924bea4cac65f753d561b177c6c416f3e463b4ff481bbec492d342dca45a62b5d0fce515e162ae12dbea86f558bdba8d12db77debe70d560229ec49ad24863604953081a492cd28f91a5ab901f47622d4d1548ac3dbbbf31541eb1464694aed9d06177c466b11d526f32394b23b60f9b62a60a893732621d0733e2218b58e6d641aa862a62af4c964ea74fc416613e469024229647d2b0435e6708e221d69426ebfc426a730cb1aa4daef1e4fd13420ab1a6899f642f78c39e106ba874755e6e109b7ab83a890b6253db911c2fe6759c0ac4e629efc60815c2d70588c543e51ce3868320d71f765039f9cf4442aae587358d993acc70629dd5873de683bcdd49e2a9e2c35a25bb8eeeb2fa517b582dc7545efb180bd3eb613d0d4f07abba51a2cfc3a6c96ecdfac482831e0f3b8cbbd05fe7c13ee3efb038bc48692e7658f62794c50c09ffd5614b13aa33ea6d108df174584aba629c9677a7e7b087f8142ba3fc992c72583de6ca8d466e373e0ecb8d239d95780fd370503c2d74a58abf615bd10a731437acd152def4dfbf29da86352aaef4639832946c58e6ef2b88e79c10f2356c41d7f72b6546891a96d4e5f3fd795d7d1ab6afd06192a4c9ced1b0e449de9cf4af7a67d834e7d5e0316dde479b61a994277aee285b269761898a0e1573ec1827c372314e63d8558de231acb71dab3f3a859411c3aa62b1715f09c3925386292b3560d8b6f4f3e53c29a3a0f9c22a992c839a8b3fa2f1c2d68dbc1e89478dae74616b50df1fac324c58b9b0f8c48bfdcbc9e1935bd8917aea14c4fa2f9d5a5826340852a1324c9a59d8fcd2c68dc91cc5142c6c1333ba88d7711f8257d82b2d8d4e9ad30da11596d3a825e93a27c9c02aec97fa3987daefdf0915f6bf58eb4e21c2563285bdf303d51c97c2f20dc38f66ca30c75c14f6c97b287ef2d59983c2d6f0aee2ab7bc21ebab327373e98ec841dc4c5ba3039358e69c2d2158f626ec6841da3281d92c954a86c098be6aad4133257c28e265c474c51cdd3e549d8b781657f389f102b47c23639237385a89b6cf223ac327be629a60d31496e84e5520c8fbc242fc25622b99d36a529464e8435c771ae49da1cebf021ecf511aef38370937a212c61eb8d56ebab3a1e846d73e2d759e38b290e8425c5143267dce391fa0f169bd0e05198d59e8f0ff672e4f13cc5c5c7b3027ab04699afdae827d559013c58f326cb191d6d928615b0832d265377e3ccb8152b4007cb360e8f10436ee6aa801c2cfd38537d46954ca20ac0c15e3a1e739dc7c993de62cdad3189e5a91b9fb6d8bec2a74e6a195f2a6bb15fe49832ef75860d69b167bafa9b4e31ea338b1d754c8988f37d18228b4d5384b514b527a42416db49ece7111f8d232149c7c94477e4153bb85d47121f99c7882bd614cd8b7e0c3f3d482bb608b5bbd98f157beaf570a531375e6f155b86abe937f5559e9c2ab6d8d4798f5652ba4bc572d531b9277e4e5da1628f9571c7f4e19286ea9c629f90142b32ca147b0c1593a71c078d97623b1d8dfaa9d2e69c144bc830e7c60ddad7a1516c71e3a428bfcb4d9328968f1c954f426caae88462d59c63a894122a6fc601c5e6492e45868fbfd2389f583d4cc641abc38def8955f536cdfbefc472f1626c459c649ae7c45299bbe2fda938ee37b17c4afb9c51ae893dfcc606417272203999d822c64fce9c422cef6062fb92780cad73892d244f51e38f4ce6b425f6198bdb09c9f6c25d89fdd2362c4544cc375362952947d99dd1250d4f62ef18613ec7b1f41396c49e4b42ba18e28ec41239e7bc9b361b48dc9058ce3ce78ca99b9cc1fd882dcca61ce5724434b81db1449a8fc89d6ed26fba116bd84c4d89916357d38c583a864544ce48f3677a115b08291ec66684cb29ad8845baf4538670ff38a513b1038f7c8ef26394cbd2885852081f5b29967358fa104bc6ca3039b633ce4a1b62078e343772daa8a37621682039729da784d82f454aef8839c44d07b1e6af3efebd9cd25410fbc44aa7cae9df280dc41a6c52fee6b28c7902620f5a9bb3db319d27ffb0740ef59989609a4e3f2cd16023a9858894d987a561a75fcc0fee82c987fde63ad7694d5724f7b0449e9033a9f4e62cf5b0c60c3f458b49cba4ccc3aa17938a6674f778c4c37e133ea56eccb972b0efb0c389f5892926cb19db0eab4857494abe394fec3aac5269438ee9b0c4e694abb9215e6c0eab7c2ecd8e97e22f94c3e2683e38c8eb1036240e6b663091f13027c361cda41ad3b86a92e70dcb8590a16e5969c4b8614792172ff7cd4dd8866d33aa935fe9fc216c5825a64b521a7aea352cb99f41bcde6d903b35ace1a257f4f81f3a9786fd6156686479fe618686bd2a8ccf9adfe7adceb048fe34d950ff936d86fd4b3c87d775984c65d8420edac82e4fee9d0cab44c83d09f99b312c6329c73cc2579262d8723dccd049a395637418d618f7dff5201efa60583d9437a81c72c2fd8525c5c7991f529c49df0bcb8587f1fbbc0b5bdc3c1361762eacd13f39ec324f2cbe85fd626398464c86e1a3852565d31c83b0deef6461ff181ecaae465d76b0b05fa8b2991c827c4a5f61cd949163c6cab3acadb07de658cc89d069741516c92f3fb3a9b0e4e568f138e3633d857d7767a62fdcdcca52582afef7764ff62e8ec296163d8ef72e86104361cf981a2cc661448e79c216a7123756e5ccbf386129698c27c6eda7579ab0caa649394c4f532a4c582cec7f3cda941f5fc2da25219eb9e1495c95b0c4e954417334093b8a29fe420a364155242c65e5252a61a38749028eb0384c162a4886a1159200236c7ed2d31bc276d59780222ca923fc24f756e3e3041061154f21aea5ced2cc710286b05752cba87e552e739c0021ec0fa4e2ee324a548e1310843d1fd9454e991dea710280b08414adc9387f773a4ec00fd60a5a3231a74f998e13e0833537869a71875f0a1a27a0079b75ee88996d0c43e304f060f3907108870ec23fe304ec609ff9aeda061956be38013ad86634e7f43974647c710272b07d59ec7c879c3ea5380138d8bec3f6c7073a3927bec5e2113233c47c1063886db1f76a01add89238fcf81cd2a39dbe00600a2f58b163f08fc7539d5c881329bc58c516d71d33e791a8624991c2c31c134242f924206178918a4dcc53dda48e8f1f47e98517a8583d736292c6a1a1c6f8293655efcf94185277a74db13fcecc9052b0b0cd5729f6a8d1232944bfb263a4d8a32b422efc8f7dce20c12816c73087bb8c57626e85043570010ccafe8528d6b3549231acfbcf60d4e217a158258790e1743e49f00214cb5f4ccf547a86fd2110bcf8c492fc235a841ce73e5078e1893da4df4ed468a3d2e82e926250a4f0a2135b7d6a282977c61b22e582725d78c1897552a8186b9f3a7d215d249d2319b8c01d056c0e0b2f36b136bce84a2911628e9226b689167e3ac3908b6a3951145e64624b973c85cda4626219079e5743e4fd9ed5451209000e5e5c62b9d09b3187e069a2c7241885841796d8e72fde36865136fcd645920a4c9723bca8c466a93f766caeb8b0124a2cbebb579d396359407831892d9957ca9c3ad245d22001f0c10b492c993663cf077f3495466291f4d131b936191e975c7801893d4aa4a4f3ca186bd7472c8dd2586cce2d8bbdaae18523f6699c61ce8c3cee71f20c2f1ab1d576e40839e646fe388c58620a932c262783ab7e117b9c18d7334a8459add3f04211ebf9862bbdb449c47e397dc8609365a4e15210b17a6510657cf37d69fc105b4eae8de5302feaa986582bc46c824546131e2cc492616708562a19a4d458a001426c5a13bff142b8a4497a41991783d8720631e4575e8be4591749242082186dc63087a549aac12e925e70825e415141c18b40acaae1d693e72b38a38af002104583177fd873f4b3953899469d7ae1873d324c35890611e6b33e2c613425c93036f3dd860fdb3786e17266906190bf8b2412ace0a4105eec61b3cbcf61d2ea6d8eea610b9f0b639b1c85f54e1749e71812ace060105ee46187db30e307cf94e0380a4810033cecc11c64a03313e793683e787187e5c349ae9fe87a1cf50c5c0524a8810b605088082fecb047069736e2790ee509fc057a012eb8185475d87118090f7aa2a3137791448215c0400526a04ac20b3a2c9954477be2c40ebbf8094c0ae6b07f8e7c716b71efec413c7821872da7a3e7788afec6413376f0220ecb4a04e98ed520ea3e5d249140052b18c405172f40010a62100108a840035c70e1ad02ff5f01175ccce1051cf628be212be7d18ef9ee22a9b8e1c51b76d420a73f43927b90d2459209000d2fdce0dcc3fbfc1479b1c28b362cfe295658c5909b50cfc055c086f5bfba2f6c6688fdbce0054ece021380195eac61cde82ce34e3157aa8a6af1420d9b95747430e9b0348c5d241d2f46bc48c3f2f062d48f8dd58d9f857881861767c0426692eecc2078ba48fa1590c08c62811996fc79c2a414f57b37c661b0021990a0042a204436881765d86ac3e2a71ad50ce32a861764583c3f1cc4f0f74943ed22897ce1c518d6bc35cb104bef942c5d241d4f36bc10c31a2fc42ae5073f270a5c008326410d98046158bc4159f4faa7b30c0d0c5b659c3279d0989c097791645c40f4854dea33280b1d63cf372fbcb05d488dca3f1ee4adb58b2415ac40b9e0e2052758c10aba502eb8501970c1450ab8e0e2008087175d584cd2f6f24fba3ce3b1a3820f18135e70610b67f79d1aa44fc9e82e921c059c14e0820b2e4635e1c516b6d310a164733c3afaba48520ca00bf23ff0420b9f58cec0e2a701d8c18b2cac1e2a7ea7b047673a0a6000037f0162e00516b6a435938385751edbbb487281090c0bce022faeb0fc5428875ffa1721e7820b3222c20b2bac69424d9a2a999099430297010a56a035680dc18b2aecd01fe4db70631749480df0c0051ae06568784185e5ae3a055deb8731a6acc0054f822e5e1003160caa4091c08b29ac6129e586f4518b1927853d5987bc39329c63e8e8c28b28ec18874d953cb42b99264d1abc33c9a6f6842d8aee49e465b4f2d50b8a69e18513f6bdcc394622ec2289a80110680041a00103052c8881071ab0810b34805e3461ab5fdfe0eb0fe278a68b2412bcc0044cd8a224ffd754886078b184fdac34cf726aee06a12e926a800214c4e08b9020f042098a75a5acd095a156d2486ada7934e4460e63a4035c70c1823cbc48c292563a2be6dd5d2471c185111256c9a0b31ae1f4d1668eb0686d860e73a3c4616060108314e03b073c062830442ff0c2084bee78d2e9a234a7ff226cde5bead1a6cb40032040667841844d2b92e778c86826f5212c976a1a3f1ad56ff20861d5281d2b66488bbf38087ba5ff10162468ac1d08eb4fcca7f6d9153b8527c00213783b07b8e082c8800b2e880caabcf8c1ea18fa654e0ef4c112bef32348ec66b0b8073bfc1829aa5229ae7a78b05f8a85b27cfec5e0c50ef6186308f590318d4d8a0ef6338b39f3a7fb1c07f1052860410a4cf082173958bb1fa45c9e3b3eaef3841738587fc256f8fbf81d7331e0168b244f31194a08ffdb6db154cc343122d35d2491801ae0010934200529a000962e8201b558755267e9c65d000312101dbd00008d60002d36ddbab92819b398f259ec912f622da98653ffc8629d349662869863baa8c4628dbfd1a27c4a9f690a8b55fb61c55784cb07e92bd69473a24f34dabc7974c5fadfc143f4a41e520cb66251fbb815a1222b960dbe39a7a5345d9956b1a6a53d7e307529a7d145920c4ea0380906a8628fbd9f3a3b6a6f739f8a254226dbc655411a7550b1ae75557ceae831fa29b61869fec6b4c2d366536c39ce678c7fab1bff528a1daae50809b98c272a29f60d19ef73b84c6739a3587d2269c69582a25822844916dd5928160dc9931983b8dda8ba4892c10948f0c68f406001520128040340b159cc104c63084b79f29f58d2a5ac1cb9d51043de137bcf5834ceda10bdd29d5866afbb1b464ed88ce6c4e6f3703f7c2791ec79135b7cf8594aff68f36f34b176daed1e073963a49a4cac7d9bfab152e6a8c860624f36e97208cf25d69bb8306ab395528a25f674f16383588651334a2596082595419edbdb0d526253dd08192dc3d998dd24d6cb18c9c69bc3068d2496589f54c5310cd131060322b1e59cce51c6b36477372416dd4fadb8a27695c28f5843de0cb43b928e5835f4a50c53c69b4e93466c13ce6635678c581d3f8a1b42855634c82296c99f19eef9d6fca614b1a30c71120d53a30429114b0c2b13e75513f3f288d881c5c9fd9f64f1727e88fdc327ad1895342c850db1a7fe4ecf28e768c9510ab1e70b0f378689c3092221d6c69f39194d5ca9944150da9d628c0c15c4922bcd675cd8240fb140ecc1b7af6c33e8380948ad800180586387a812cf32c3e3f41f96b9b0a1f633d64cbef861d978f9f3a147b9cbf461cd1d2e46cee70ba1221fd686b92af626526f7c7b58e2585f8eb5e9306d7ad8c363cc9b6dd09955e561199b3f4751442e4e78d8d13d08a376375df1bfc33631c43a2bc964c4c70e8b8ee68c227ea9c39629ea31058b871e4d3a2ce697d9694fee53c739102c4f3a327ca01c5647291da242cc518c83123f4a9ac3a8e0b0c69e9061fc9ede6afc1bb65aef4ffa3192eced6e58735e77e7c7c8a16c6d58bd7ee341c64b15d161c33e9b3bcca488d7b0766ee3dffdd5b07e880db5535506319386756432c44ea2924c336858e6fbcce358ece59433ac75b521e5ddafce1334c372712232580b29c3621beb62bf716458257756a48668dad0c6b0c7ad3c917037922a13c3bab1ff1c5fa7c2b06aee10e65265581d074393249e2735f90b8b7fa806990cf52cf4c2be62e141a3415d58d28710c7ba739c89a38ba412a8c0042f70410cda5d4002a4129c9dc0202a30800b6b79ce0dbd6a2ee474749184f40cd8c272392a95c687601e1517780a48b082a3236080164e0fe1f555350a8cc3e0052438010a62c08420f080527e56a082634016760c6dbd43deaea8cd5901098e0a0c096860822783410c30400313bc0500080cc0c212c252ce290e4b36af2403222081081870852daf6e860a9ed67f7334062403222041081860857d3e9a8ee6acfc9bf22aecf073fa509f3bc7ca5b790003a8b04c06217dc5e0abea2853d81f67b4dcd9717a7d94c2fef5382544bbc9182451d83a626c183a74c8d98603180085ad3ee3854b9a538ad43f61077f7b9bfe710626b11316bbeaba9c223cfc7c69c25ea931f2a829ca578c3261cf172dcbe7c2252ce1ff743c592cab8e28618d51965358924bff86041890842deee690bd0f3673b091b0a4a39875e93b837b94236ce981a94e88f1de4c8db0a30bd921ac995cae598455c43f74fd451b4d95084bbc990df96c7b42cc86b096e58784b0a7687e79ca736557461036b5082162f2e9067120ec20e332cfdeb578f80fb64e1e0fc626c689b40fb6080e6ba363701b323d584b2e4f0e21dfda5478b04ceef0a82bddc15e1716ad73c8d570ea6053f1b11453e8c48be5e0a918561fc3c73300076b6d3eaa9a90b18cf1b7d8f3a4f0f90d7463c76db1a43c0a2134ea9474bc16ab8f75facdbca7233b2d360d52f7fb17ed2ff6596c71f229ea850c25322e8b65bcbcce41fef44ff158ecb3a9e4c4237e7574582c758db7a256bfd7f357ac8f61e54d39b92b160fa6714b4cc54ef256ec79316568ff3879aa59b1deae6d8ae44689bc8af574673c1da8a8c4a862c7b7f321c5907661928a1da5aa4a75944a3a878a753d23d9982be568df2976e418a79d1497d4a3638a2d53d3c70d19a7149b9a68a6b0cc99cc27c592529cdc4a8e6289f86b192693a9ea8862071e345dee878c3a1d8abd52ca7d94c9d16750ec93be6193570a71e4fec43af9d5e13ffc66baf7c46a395c5fec8f144f3bb16ea4cf29864e6d3827f66b7495ab1f3219b689a53a348c9a2fa5fd686275dc38c6ca8ccec40e45d567729e31b1560a193c367cbcbdf325f68c7e42da99e81e0f5b6253ff1434f3757478732576188d2c7326cb7a664a6c92b344ff24fb3ac293d8cf6b2e56d4d2183c96c49241ce4146277caa184762d158954422df86943124168d9211f9e02b558a1fb14c54c6a6d0e91c8b1db1e359556d94aa4ee3a6116b574c6333a3dee88881a844c7c3c22161010141c1e0e0a04060606074ed3a43180800000463419023511244e9031480023c1e163c16120e1010080a0c0a080a08060a040004060604080006060800040400060608e01193106e00025a44a1e8520a8b62820fa961834b99f6b917f1673915de9f0ab5a9bedae966be543a985d1e661d2fe43cf5c0b7316041545642de9af1178bcd3a801c76354a272f2985d2145e964ebfc952cdcac9ca79ea314e497b453aab2ed292cdc6f4d16a59f8699717ff12978530f93597ba65aa6292af40611dd1e312c801988d1f10f392e8e73a92ef9bae42d8298dffc9ebe3098265d241e826f965f620249a02f5cf29669951bb3333030e16f9ecb2710fdc5bf616ffe4187b1d1230ede51c38ed24864727d44c2c60229aa9d42be91db70fab943e4065116c1135ac7f3a270ec9274c123125e2b8cafe454da6c05397b2f88224f516799844975a5a704559d38320c907c5677b31e95caa838515ad02fd1a81665f2030f0fb8c0775cfcb50ec89090e7a50f6410b6908d9ee0f50dab9cc6cc9169abd4e86bb2410d4f2995c8b151bc64739aed26d05569bb6a8c7040d03a81bee8bbe3770e90ddf517a9e72436171ee02fb223bd84dd046a6a1d790c65ff27081acd5e3555781030b011751e6c067d0822a32907841844345b29e5eb04ad13609b5760159546b76b43dc6b264c7f25920a5e974f0d6589c1b0828a08ea77eb25f4b7ddc46f159205cfe2c4e0babf8ec4dd9045d83f25b91544aa74694a654201b5af50b73f860fd926a576dd4235eff7719e0926b97d7a4c52b667540efcf0e611bba3fb3f24e7ae24776e775ffdc15f818dafda524faf0590ac466fc69118caffedea723b5c701b802856118596fe05b83c97ca796223e14727b99730e6e5c0e1225235c55a5cca7710904e75f0ef9a65d2e0f71c55cb5156ca5eb1aa6ca12a57acbd594b9aa7bf62abab3ec99043fd20e89ff796852be7c554c637d8e37099aae14be69500467e674b03cedddc67ab4948b1a7a087fe014450669da4a1fa39666a21c0c4ec60974f58d6313346731c5fa8d8bce872f2716d2baa53851f265bc155a8be2b14ca02594493294026ecc7986b0bca5f33466acee0d8f0e6e1bb38ac2fe2ae9001c1614a320b4a6df7bab1c176bc400c313e9a9f91d1376eb8d4fa9dd80721a8b6043f1601a42916ad2776d1be8a16e2e29a4ab0b6de6d4a08f66f6a07a04efff2abf12c635db7b5ea277f3370207f599577a79ddcee44a71132325ce1b28b01894c200f733990b6b7de23aeaa7df7b37268d8d67b0e2f56a7c26dac1d6ef36bcef370ce5b3ff2d8d245fe8a15662afbb8edd0e2b839fd467bc8d5d2799acf3ac25d00d7ed8660d8ea7d8fb5fbd20bba166424ed0e82d8af1b156d682519c32cb61ce161f46bc9a14a7cdca9e0c7f4cf6be2d0346a3f8a6afc8c992e582832729160f1d883c5743ab756903b850c174a98005b85c4674c64d680927758fb36f8affd40785114ca363f7515250997d7d47053c808bdd1dcc091b38e119ff75cb7b87a4336ed92c4174f3f3eb4854fdb2dd16460d8c0004fb28064e56481e89b16c08e715e78a93304dfe2373a8ec4d63afe24f06ee9325bf18657d85b8b0d4ce50c43da93e04cd58014e4d6ef70769c9466873e496cf5154e080e85b86888622a3ce1715022b5f28b40a0a7d7a172ef0b93756dfd66247e4bedc84c7941a8c1b07fe0f4548e696463d6e5c86c8ade04de1b8d25fcb6e10009f0421304f3751a47fe2133bcee87f89b063451a4b58a6609286b6092f1d639eedb5a06bd1dfb996831bc4276d691535a5c2403f22dc0157f16db7eedc77a050c5a9dc37e389f5bcd28b1e7c0d6c442a7c4d8a09ff83dc51f899aa4db139344599916ca6a99b5df28fb157161db5095393e7deeebb85782958fc748016b340150b73e9e80a88d0d0aea3e43183e74d9c9808233cc3e28227acbd538319e3085a73786bdf5899337f23c1370287a3fafbeda6f94e90af02dd6da13060e8e06425976a8e60c9b4792df36ddaa4a9f2ed55135d84cac94bd496ddbb36162c0cacc026af234ee9b28aece305e1866102a48eee0fd41fdf2c030aac4cbdb3d40f84acbef731830dc11a388aa5a979e649a5db2ed3a9cd080bddb3b537b9bf29f89b3b6dfa4377144929449c214ff4999adeaeb23e8a2c86329e13af36db025dcd1d35543f47614d2e459190b6254fce4d2a7511ac1a857aa98ba49469fdf7855e152d6a775f6eba0c9cc50ae4c18a191d67a7b783e905a2c72d79d2e7ea7f96bd11f40dbe97c667dd7477f8cf343ec3fc526a6f848345fd45e8860627f294220ef0d02fc659c1468ec1b13fa527290b29920682ed980a75585b23e416e7657fe9c241ebade4978b6763d9874a9f92ab3745e29660438934a555ba91af89c5dda7f9e1e1e2608c4ff0a2f2d8cd93ddeb3337f8af9dabc0c218022ab52f63f074e6e4d5f3fcb0fb0eb5e4ccc3be0574c23c2fde1ac44e6e44483d6256d34225e1878e9fa09f26ba86f001aa38df59fe061792185a7157ba18859355eed26409bc5259ee2958f9da64323abc85fdaa02e2e406319018e96d587127345377c92bfcd7a568a511c598af1d3b3b2122d15d5329791fa9e4ec68391ffe94e42b9a64f1da904d428f6d2a2a2eedf952731e43071f2b234d14a35fd310677bb516d4e13ce22395ef116b42ceebfb24813f29494cf25587ca70c04384b5b2c1152ea2b171cf78c702675011ec7b9fd0e82209b1a43ba06cc80ea044cf82f302e6dfc797c208f29af17692230194209bf5c5742a9713b95df8aa8b90d677ff893f70eab7efeb9a94a5863a3ef936e4dfd877391f2fbd5d25cdba7d008ad1ad4e208965f69204a1698dcc21098b1bd3ef48e5e4593a92edca3193643b95bcbad13858a767992945497c4a5750da47a97b4a6f94025a5b299869fb2b85299d0e57128bf4350f7475b19baa2a8101b05d042b9b8e3253aac3539f273d3d4be2e986e72d0fa0b987ea53dc771fa1f050a18a540d4b209e878a54274eb56dde3b4e38924b3a4e441af9fc7b73e46a8f4a55a69caffc41995206af8c5306aa2c34b2fc9d47bf7998366798c195f7ca83cba6c4dc9f54f2830e562c9cba3b8452670012d692b51d110fc57230352c6d69c707c75a0764341e8dbfceaff16ce3d7cf1ec39c856fdcccc6ae77e10a2bed316f24876153e50f8ed122e95e00ab6d643e9d516723b3e98c32f389c5b3148b9a31704ecc0e2d46a36861083199db4c73b8ca1e82897fa8f5df60b51b150ded2d60e1fd42740c5ebf16461c67e250e58685d1486bd89db70ccb62e0bef01ec344f185112a728e53902e8c05753dfe0ab540cf02a08f852156888cc1ef0ba7048621c64c91d62303b93cc3c8fd68f01950f70b2378bfa181cdf419ca53cd822348033acbf3a25289468de8c19be0bf0269ff3269c4794f4c5fb2549b64f00da4df843ee2219713f3ea986b5ce444b5aee9d462577ce0c2e1d72909347dab80f2c5a67ffefb1987427568d3a3adb6209c21fe07c946dd8bcd853828195edd6e78c2fdc51324ca9a0003744638943fe40985dcd21c8f8640e6b074cc02caa2cf5228f667d92f633bb49d14c7f10b08a9028a1eea4b7d9af2ad99b38aff75d4cba87954e9eab9f47228c676410dd16635288de73e3c34664cd4c8e364ca0a2f184825385507a6a296ab5e9f267035961b363627f566522382ef2ea365ea33d14e3f27570f1d07366e5caceda4341262641e2bd17e6775b758bdca8a09e86c424a70bb25dfd6b8eeaf6bf045cba9b7abcb9b49f8343c916100dbd89a90a221c581811f03172d44f4c8a8911019009e9c877c681078f05216f69b35e1e2c7c6c84f38175816e0dbbb1d9d3157e889fac3c443a44923f7ef548ba9bfbcabc99eb7d9ae2f8373d3c65e814b92ce29522ab05123a226ae99ef893bd5720952049872ec668a3e16e884c8830e15775c9cd4dd368d92b95172d23636db4bb4b3de94c1108375b57a2d19d539120d418f01047d56fd99dbaa866a5f589afabfc53ddb1a9ec8ec62abb85f39bb6eb1a31436a6207633ac37499bcbad1d9a2839361b0bad04e6596a1b5f99b7719c87513f13c31041a69d380d4a9c9aea006e8e2ed7f7966c20976079797d787fb9399c8f0bae5524fc0fb1f8134fe6512ab9be4824bc3152dcdc87f3e33eab111ddf13157edcd93a4b2accdd3a2041a66cbbb78312332bd07c6ebd30cab5aba02277e2018fdcd5c40b1b4924ee17572707d7f6e969532d8abec0fbc50903641a768e014445c20d86b2f53cf77f92aee3a3e4480b2d146ece0d1bab5062a6c2ac14aa7ae5598ac9371f1dae6f2ef7372cf917a7ebcbf0b0612e4fa572c7d0c3f2297479db9026461484c5bbcbf699557ffc3dd1404e9b545182c05bf0a71122941d5eae5f6f53a3a54e4991e6707bd3483f3e99f07f70ece8fe70ad2d504aaaf787d76776df2528196055bc319273c724192add06b1844782e6f2de3fac77276be3c231d55ad7dd78f74747d7eb7b8539a01dd8939410151102525a0464884993458ed83921b2e3cbe9c9f1f4eceafae472df449838a3c8f4abc0cc51e53c29062a1c2a0a405932251eb16758f93c7e7c3a42e68a29cb383aa60ddb611a0d56787774bfbc3f5e4e1a445ff2930a36f1fe747373bfb3cc1819d61eca90fdf1953ec5823133d3e74f9c333f6376e284b1f95454a58793a0368717bbf9a0b1534c7859664496b2644a34c95a0d50c18bffef6f9999bf2884d0f1e5f526779e7310cc1ddd3ddf1d9e2fef0f361878c2c33e748ba58483fb59e4b05ddf7b0007ac5db52ef6171d8d4ab08fa73aa682c1483120d78af89d42f5e2e0ee70263888dcf58a87ef9707c7dbabd3f9c9b9e4b0cc6e0a4337075683689a600b39b83d9e1dafa7c7a77321a35f1acd29449b92300922c53e797e308e0e46984859793cbbb85fdfdd5de916909cdd9ff4a3cbddede5fcce1a957a383f381aa024d1a04d91ae927f71beb9b890cc2cf2f80330323ef7ee748a7c3b3ae43d23d09b88f91de7992dfdfc7a432483afb74d4566db1f9d72d86e6b89b131363095522637614306a68c0c1f1fb15cce67699f5d8ececf8b7d43ca830e5efaf1f5e0e0bc3d3a1f9f9f9d9d5ceeaeee76fc69e622aa081ede2f6ffb8d052ff23ec5cde1f5eef678f7fa3a39391f9d236b5d078f3b1fbc3023a4cc6183b1a3925f5c26b6a36d9a3de2c5a91d7a12a4b450325b2c083e3a3a3e131eb66cfb2668880d45a5709319e2a8a17946c60e1a99333f7ad6fc900307e921b6b48886e154c22bb88f43789e8307a7762523cd56a46834ea7faaec173b629484dbcbf13196f4296952a0a2e828886c77d40d09ede4c5f3ede9e4fc1c35faed60c3c279ae0c29378b4146144205727c48b68fd58f16dae1d19d313357cbe683f3cbeb4d6ed685d3156be7e4beb8399e5eddefdfdfae6e2e7757e7eb5b16ae645d6dbfc315d1a772a3d22d302878f0a85403321a35c9089dfa35155449cfa24753c984d175e1f0f87674757d797fbdb9b85e9e5defaf0f6707a7a3b3f3f1e5edcefde5f61eda92b5686782dbc91a9bfee8767b83ae0a050d0f33f7fc7269422121a3d38f4c522fac0a196912442a9dd92ee76a881376878757a7c7dbebd3cdc57976f6747c6e9408291d4aca34e9d1154a17d27b1025212a3214542951a50f4f27c787d383eb6e570742d9a6babe3c5e5edc8f6787f3138677f5358179f1f1f4fa3a39389e9d3d1d7523331c10009d7b71beb8badfbbb4659fdc6f2ee7d3a3f3737304643f7d500db616a24d58c98e3e6ceea46488047b639963c04b458a6dae5c10a6234c8e78767e3e3dbb5ddfb9dd5fb077c93a2a8ff3d37716e48cbb6b0b2afde4707c723abec19e02b5ce1ad63bd52bfca3036c1f91b848029d5fde59ceba0d0f866f793db6cf5dd9c88e8c90e8890713f4cc4d9bec1df5229ec093e6cffc26c838a1e9b7a6b8a8711386183c4a4a650acc788fa12c194a3a99affce85400d5731292bc635e8cf69a0621dc5147107430b453cf96492615c038c0e460bcc3a431a16414ab18d55b4086ed4ac120c0c060f2618260746104c02c3153649a3015613a60ce60fe618e605e6132603c60d23121326c0c40192d8c7018258c441806182a187e9833985f9803300f987ccc7d40866c67e2f8f08479c260c37c803912067640cfaf531d411a110aaf7e341e0f01326f6e22ec1f72426ecd688f689e63a3b95b82595a38fbc86bd2a8d7148208b80a7fed0ef236490be9ca14c22c2a363c4ce1c1491be2d9f95f404b98c1595cfe6eb3416c82a2d581f86048e6585dc72c19387f7636098c888170688adea8fddf93300f993b61bfcb3824270b386fe942b62549293f22855e09e987abfa351d5d3c58a86ac2f5301dbab4b50c6c8f0e0d25d4e0a873621631c3440359b2048f50f4aecf00d6ba37a0e637a02523d2b356ca9e9d39918c4b88a2e768dd1478ee317523441b64a184b9956f59bdc9ef867dd6fae5143914d1d809f143ba9260fa0ab2049eb60a2c40be8a866426f45964011b45767b5d84679c417083cd5b47373236c48b928756f04d6b814cfffca634c85ea25c4ea1f49101889019331249ac308e4bd7e14a62d9715b1b0a9bcaf804bdc33006ab1bac6e9f1dfc1f57a7b9143024272a27ed3ecb1a03d95af2a8c0523c49dbe0166cb4f8a5b066681fb88489772673010dd63f2dd1dc5e1b2ba1e79fb49b44cce40813884b99a0b76719c5168673ee51dc2e507c9e182a4bc598cee1850aa3e9e3e8639e4297e1142e645962877e8037efb0c198ffe9a05a63fbccc3b2658638885227721f513946b5f5b58ef6711f36b6cbe301b68b07b00f42f906509e66001c4b19eb2299209aed6e7f26f9951637611c330addc80b4c8d7b15a0f1399ec56ae51a7c4da69f83170a465357194bfd6f97c182998f3717aa4cbde6ec15825561a4ef1d83b6939b0764b31e04973a510bc5918558068813a386010a428e5e7190813bbdca99e0477b7f5395f49f024e6884460b3660b46439330f0f0f0f0f0f0f8f1c341352db36863bc82465aacbd52715719a644a49a614096726bef47466e2834248b2daea90200c107d0bbf0a3f0b04d31b445d26c4400413828757c9ea501fcf4330c93e87b4a2be84d29212420c43706b79fef77bf739941f885108d6479a65ddfd1434330bc420049be95992fbdef45278b6821883e0ee73c5cf2482902af687d6aa20062a48cf134deec701238c3f14c408049f84d2f8938f1880e0449bdf76486dadba3eb4c23801d020c61fd8cbd85fa3f7f3456d90cb8183eb6211c30f58a66b4a2155a55c9a2b98ed4589a723efed05001bc4e803dfa5946aaece27d7b4e60bde8005e01031f8c0684aebddca1e91ae97ffe2020030458c3d702674c90841490f6c0915cb245c447574f0f3c076f4ecba9c60cab5475f7491ded1175d38a0468df731c4c0039b722e19d3dd5a0789c6b8037ba537940eb9b603a3b57fbb9a99eab4ac1875e047c99c2a59f0ea9cc4187440d7a9d7d4687de478cd3322c498031b73fa4e1eb445e618fb62053852d0457b7162c881bf58316853192252fd94408c387031a909696732a7519a1a350811030e4c8e2125b99952d0374bb2b13570548c37309276554458b2eb9cfed02a165c210662b8811339b424bd177234951c428c36b0265ba2f5a78e3f42ffa165c35780436d0b0e3c20023288c186b486a474d8e5465e808b2d2ad0616c2047170c28f6458781a3dcc811c64120c61ad2daa136691efd5b63e00531d4c086b81e62f54bca3d211f5afa91238c479986537ef1d14df79c835c10030d8c484127d5e952a97cc1f002c7b9b100ab03c43803e7624a521e51cdc0594e29377f78f89615a30c2955b1d664f1636f67b6e0c0034cf085066ad4f81b606880587f70d11f5cd4a8d11fa76f680c3270294c44cecc6f18fd1f26c618f410cf62ec1cead71862e043545de5ec90b4a39ac6458c30b01e428c947a3fccb2e1457791e34687610a880186d247244b2eed3eb470d84041ff0d2f1ae30b6c4a6ad2a41e59e771f702af9da285794abede5217b8d371d2684d2217d8a8db6d766a7273fd2d3039c964d59e41923c951698745bb9b4049511cbcf02ab5f252a05d398d1372cf056393a4ba5068b94bf023b16263387c9cf685b812b1122a28e968e575215b85135e1bba2aa3187a8c058660e6921a6da4841536093a992a331660eea7529b097c44c5bb3a490e189021fc3e34ed4496a69d450e0eccbee74dace13d88fc954c8d7c1d55a27b049b3797c1b3b1d546c022779c553a635abcb2913b8f3be9cd3c9509d447409fcd7d5e41c39727b25b0412fa8aefb4af2c926810df1d24f969062882b12b849e11a27a5c449b23a022bea43c5cd9da5439246e04e9908112fc44e3a761178ed2d95f4f847ce151281ed9874c86fe5f1f44543e05253e7b7f3a4a3bf10b8f2b4ffd3247350c90f021f22c974d7510102a394ae6edd248f11253f60ff5782b611161fb0d9732651f2c77214951ef017723adf10bd35d58807ac6e8acc6699e6f9bf183be05424f5d59363f2ce493174c056249184dfe5911dba183960d42ee656656acad91203077c90317a34af98fef28b7103de52fa76532d3a6cbd1836e022689e2c32a5f6d2b11835e03da878973e67468ac5a00193524e6a447c1d9f189b051f2985f2ed2f65c107bf2c31ac8f055b6ee32122c5359931b060939a48114998eb8ff50ac6c72599bc1d6b5129aee8379ed457ff5bc1a52913d5d198163ac20abe528be8a6d12a185331bc82bed839a9d7a10a2e2bf533ea9ff649928e541cd95247af6497640645c0058fbee8a2468d2d3840811c2970000c5ed002fd41072a4cddcf4bcdcaff6fe408a3e314fbe6601bf5324c654cc1a7c73f152274edc47cc1404729dc283992e5b8a15df7400729388d75e92f9508cf3e8d821fc9bb9a5387a524f3a260d3d8a6da1c1e3cfbed0805dfa3ad5d418d6e24610728788fa2b39436bf361903015a4004366c747c82c91d849c9882550e161d9ee044ea8a20440a32c8cbb3a3134c1ad5f92124a8f86b8e2e1c2f90a30b06d4a891a30baf5103471860acc0021d9c60eda45dc4d1412c947043d1b109ee5214f541f4cd4ea63fb4ce0460131d9ae0ad7c47092deaf67767828fe17ad639c5bcb136356ad4a8416aebd08109d6d6467256f2282af71f5a557ce8b80423dc5462aa0cc28172bc09b45882f7dd30d35529c875cb875617613429956072be7c3aecc60f2d7294e0325f3368cad23927ef43cbc61760d8c84970ff9e525257fe29e9ca246150e2dd16163da7f1838b2d120d02356a4482bfa46d3d871053bbf61f5a37f2025be75970690111d848400724b8f033774f3b49a4463eb470982c743c828ba739b3a95e8c76a62318e997363a66379d4ffaa1d5885346d36d1677b4093fb46c2023381de3c8097696d38d4e7f5166118c1c097e26648ab82e32448722b88e51a485c4a042ea89036983e848043fa2134d95a7effa2822b8dc99e2e4ac3a4a85e4e5a1e310fc87bafaf68d7cd16d3c741882af4f5253c52075ef3a1f1d8560db62a6cc18c2d25660648d1ae802356aec133a08718b10bddab123a60f83ff1b29c78d67c1df3070e8180477b1ae4c7de4fd9319213a04c14955c96a499d644d203a02c19f320d5516bb362d0f086e2d6b59865231e44bdda1e30f5c6e34addba6d13e0801c00e1d7ee06488189ec94e525f4c1fd8d0e5a3ba2d6ee4bf3bf8c0a9654f7fa5e627cb1c8d6ce4b801468e3e5d7871872ae8d803fb1ad2d3d8954af57b81bab0f105186a81f70245c0b0a1430f9c9bdcaf131b3f4a391074e4818ba9933acddef0c08da44a1d63f9c7b1a4bd03d7a2692b8a10ae1df80a66dda24af3a5d775e06a3484f46c39a85f45075e3f645091a78642c71cd8aa1132e5d5aa5a47ed0d1d72604589de98c272960e2283868e3870ba3ca914254f77f30f0746cb3d698e99dba3c6e8193adec07a76ce489ae75763ba818da927bea15fb2cbc23630e69b354df48e58fa2a6ce0437554d71fb58f555b1b3ad6c0bb999498b32f439250193ad4c0d9a55f4ef70e3abc948635d7a7d2da776dfd436b0b0e3c206fe40863061d68d074943c1d96d2d3c5fca1e30cdca80c59cd3658d66b06de53d0b78e23f7ee8365e082cacf4aba77c12d880c8c6e0ad716e57f5dda18b8084208f51ed5e89a1103a7134f926f454c3555030df8424718b80cb9a2e489deb9ae57173ac0c07b9ef426625db0b7f20b8c8c3e322584a4c73762bcd0e105eeba438c49bf46bb9ec9858e2eb01d3fbea5f47e153ac22d7470814f9f837b7a3b9d31695be07386ebe56c29dbd36d58e8d00227c146865d08e964be6e80c1454716b890cf539259b592c820a2850e2cb077a94984944ee468fa2bb056f1f428175159626a054664f537e549e47dc6add05105fe4f7abaf44f8391c8143aa8c0c48ab8d93a482f1dc40a43c714b8ff1a69b92dc4ac9e4a81d31d246b0a4a59c53b21d01105fe2686c81c3f26494906053e746c1d4d2241759e743c81d1c9fff57749abaac7098cb0d5f3589362aebc37811139a6605e3967c9f732818b1a1a3d44769ec7940314d88280a163095c56f10aaab4365ba4046ebffd54bcfcb2ef4a125833cbd7b5f2fc681b12f8d5dd683a73aae3088c105a9234dd9a3f49aac308fca41482b79d6e3dff011d456025c914fa37e930e0c6d04104fe627b321173ffe9895da16308ec96d2df24924708dcd876decc14496f073140858e20343169bc09faa27e1d404873c99442744fd1a4297ee8f801377a5b2eba42be604aa1c307bc7fc7f024457750627280025b180f64d928c3025bb55874f4802fdb8b13b57f47d308012543070fb8a4238ad29126b32d85828e1df0bf15fc2e548f1e059d6303d745870e18db4c61b513b535a673c0c6939f792786365e7023c761600b2eea705499a003076cf48e9ecd5489630b2ecab43a6ec065351791f81afb42018ece61e3adbce82fba30c18dae80a90974d880d76413db7c4be65fb5065c08a2c793654e6a33892ca183069cd0a583fa175b4d3f1a25146016dca80bb2bdb44416dcb57e1d0b5ef775344b50124d5558b0219f8e17f354438fe915bcbfe44cf5e61a4fa45dc1e75edad4f4ef2afd5bc1fea8265193af5e93598502b0829127bd52a80ee1080ab00a46694d0b96217812f368a000aae03d2ba78bfebc2145c81601e6a878f164cc33c1c6317dda930e9245e598e0f57ced2be774d5a74b7097f574ecd35b82b1ee8941f763d2ec5782af0d7ee31b3a25f694e03d72d64b1541758b26c168a43832898f69ee48821d753159e505fd7723c1c8faae20c294c66b21c1ef4b88ba1da64d691ec14a52916afe9ffca48e60440c1a94a9da083ec52d2f5756cd3219c128df5cbee1b13fa9f422d8d4229e928688242dad08eef5ac35868ca823441f365af05f1c130c9008f625f6c6906123821fe59eee1a82ce54e94370d5ab3fe9df4e74922198b4a66bf5f773f72f049774b449ab6a9264040310821f15d15f8d6a396510dcf65ec5a4c6fbbf23084ea346941cb9b4da81e0577faf444de2a41c105c8cab162c8aca49f7073efae91025d2e4aada0feca98f8994d93ef09b162992d4f081edf2ee90ef6f9edf03a35349cfe0a3bfe2aa077e3c44c516bddb319a074647e4b4a2430a42a878e03c477753f1256512de81b7cf49f3438e25ddb303a3b953453ca14c9d6cebc087607d794dff636b4b072626ef31110be5f1da39b0e527a29d4892036366415850b2d674290e4c3c597ec1cc5573090eecc68eb973ff377025d2940a9153481372037f3a3d249b90eb1dd4063ea96c67e2a672d60e1b986cb72754dcb417d93570d1b2c53c35f03189121de2c4badaa481bd131293e9e9ca1d0dbcc555cd1938dda1aac64c6306de42fcb41873b61fcf9481b718c7cd926b0cca3264e0b38b789d5fdacd791d03db697abc4527d3e9ab18f8a03545c9ea583a8e340c8c7e4b48137d3345a460e07cbbb368ac8949d5fa055e628b8a9344c5de56bdc09f75327353119469b40bdc64cfb395d4126d552e30c2f26dfaef8f9852dd026f71b546639aac9b542d301ad15c545cfb1047cd022bc184b6242ab75d50b1c0a6e8e7254345afc0061f4f5a82c8daa5a915f8adf74f5fe2b56baa0227d26d3ffbf79999a8c0e58f57b0b25c4af353e0ead376c414ba7abe147897241a3fe507534f14184b551acb3a6e58070a9c4cf73e3183e510ea27707947f28e260b2d5a27703ac64ebbd72efa671378fdae28c9433a3b9309dca85548cdef341f97c0c8188296f81a279645095c14535a4f7d49e064e9d41dd1a0af3724709742b552fc088ce9674f9e35d3ab32022b5a32a98f481621e9fad6218e9e08fc6584d49522f2a1b53930c010584d9e56e3d952e62021f0af2939e7b248b5a320f012276daa241544040181d7b40b21d5e7078c12d3e731ebe76c393e602475d25fca8248a2f780bd78fac2ecf387cc79c09fc54a6679ef80499944a69cab93ceb50e783f53e36d750eb8fc92e4a91444bb5a1cb0316da58ee419d773033e93f237fd5cbfd7d8800f226dbf45bd28b9ac01674997ceeb8d393f67001a70e126b34adad287565512b498059bd362361d72b2604cc44e95ebfcd72eb1e02de8e96cd9251f5a36120bc2b091e3f4176f0a169cfaca93e4b5b8980c5fc1660fca7288952bdf86aee023db4ff2cffd2b2a6c057796af2a5a0459c147f7687aa9eb459e56c1aa45ab2433472de555c18ec69c2d949ee89a54b0961a5425a5a4238850c127d5289625a5e4163a052792a7ce21454fd26e53b4ef9eadfdb4a5e0eb7308a9229252ba2505a792f75fb39a85acd0875618675a8c829ff45992455562881105e3976df94eb388a8a1e0b2d3dde34791ae8282d1163bd31552a849f9043749976dfaf81f774fb09154bcd510f53aa613fce54d1284d29d524a71824f21493c84a46c82fd4f293397af092675dae892f299e0fc740e3a6da48f108409ce2a8250fb25187335dfdfbdf1505b821f553a2be75b093ee968d5f15e478a28c1498ac1ac3bf4f23e093ec554d5186324c1c9ceff29fe1a09feec738ea4bc7b4f90e0762d24a193a5bf2b1fc1d7644d16e48852330495de6904bf7a6ae3ed8c6082b632d5f922681317c15dbfa608f6574d4ad63fb7950a5a24821fb718728352a1a73a4430da22d6c591b816876093de8aa94292f7137286e0c7e366c5c89f744c8560f5630769b1358f2511228716836023aaa890f51704235abe9744b3ae8702c18f440f22eebfa91f40b0fbb152d095e4f875fec0a9318f1e927ee094875c49b78e0ccffbc07548b5137f4bc598e583da9b1bdbc321e51c2cc6a492693a045ae8818fd9af7a2d4ed23998075e741491c654decc100f9c9a34653b52e385e01d3865b9fa4c594515153bf0b1b3c8fc21259df55207cecc3af669f7986a313a702e9a9a93de1ecf98cd81512e2aa8d35e1599991c78c9f9f3ca92d234497160afc39489e910ea3a70e0a39da5a8d137b0297abaf8e72f1d750327f3eb65f698d49ddd06f6db3d57acd9c07d9260e9bb4fbdb9064e45331552a26a40811669e03b85dc63b14ae49e85064ed88fb6d7926b1aac33b0e5e957937d881938a9a17cebf6ea23576560fc23d6e898a6648914193811ab0e55419decfc18981462d61cedd22678c4c0559031689d4821a54e18b83411bd2a930eeae3606063b0c8922ff5bbf62f70418d593ccfd376dd0b5c4cbf1b323772c77817388b234d921e5b372d17f8dec91272faf4e8e916d80ca252f792527f530b7cbef5e815611638d71493b64987948258e04dc97cd954635ea75c8151aabe6b79f72d58acc0e498f527a355e0fa3d44f28b51ed3a2a70dade7b2cc8ef60da14b8f34f22e4a0720c5a29f0c92d96c7aba2c075eccf5ed741bdf691a38b166479d107054e5b9a52f1d34f60ef3a694cef7addb972029fd6a38dd09126b0fab993a4d86b22e6cd045e63b64ff1d496c06826e9397e0ca141b44a6045f27fdbb99f04f6935a8aeb223f942791c08f461babea1083d6f5087cc5105cebc33eedc98cc05a5dcec9955dd172faa1b50fd0a208eca6de60264d746948118153ddd39b2a6e4e33915d408b2170ba6a64daadcc4d3126d042088cdb76b29822cb34a81f5a7f63056198175a0481913124d539846801042ebf2849ca3ae507fc697fcbd12da95b8b82163ee0a4c78b9944599ce89e1e30218d8c98a2e59f74192768c103b63fe73f9dbd73fa10b40336f97745d04207bc26a1935e0b1e226891033e7bc4a0a4989d088be2800b719d94d495d670c914b4b8011364101d3c8d84686103b63545ce39a6f4a1950dd0a2069c2719727a309121aa03022d68c07bd87da45097c474e5434b67c1a9bcd51f21e9023164c175f0d4542a84d48e4d20462c18193ae295cd62b5e71f5a8f0316dc6accd11253ffa155afe033fa5daa0c12af332688e10a3ed3a458eaad7713df0f2d1cc55a118315bca5dc904aa612a59ab120c62af88c96ad52cd847c0d7e6899a28118aa60d74eec47448b05e6458c5470e942a50cee3978ed3150c1fb66041dd51d740aee09629c82133a4b483accbd54670aaef3d45bd456ce60a55230a653482985e0bfa63e2938d32429b88dd8af3d0a3e849cc7cc93d0912a48148c7bca964a6bc938c2130ac6b4e60829c74c9d4a07051b21ae898afb9929da4ff09ea3e3a931b3daa027b8efab14bb903ae9443bc1ee788a05fd2fed6be104a32267a864e229c45236c15f1ea554cc9682658c26d25bb6a3fe920cc6c804a7bd2c05b529bf9fa99860345fd2397758aeabea430b7d189d021ba81819625ca21c23a914f2a706d5170e1c7fb6e0c0034ac5b004f7aeab57b264ba51a20fad4730c4a804279e5b553c424e13744ab06af71b62e53d09c64b6dc4a4795983144982534f7133fb7f6ff03312dcc93c794d5f82f7a60e12ac794e9f3e95f608364c8d778ef9b385987704971b3435a7dfc70b7623f89cab4e7345df45ef19c1a818b362f97f9b4c2a8be0e2e6a819939260999d22b8d3a045bf9ef011ed13c15801e994b9c3b380113c16c1c65cdad1ae2d9e9dc543116c2e8fe796dae291cc148051812db8f8824722d8cc39bcdd7f63dd8f08b635724e91dc82a6f7108c4aaaa3a95dc94e22670826a61275c22c79f068290467ad1d3176cc3c6a1282bb4f3a597d8212d99fcf01060b8a0d33780c822f135e1523f5e66b26084682ce1632b49e0c3194038cf40804eb9ea97d848912952d4030f2cd64496f55fbbdacc0e30f8c7f8bb6e8bf39a71f79f881512a37c7b2b5d4dbd0078e86c0165cdc78d4ff91ead1075674722ba5739c0f8c59bde80e1142a491ee81d5bc10b4e46cea818ba2b294f6e9f3aa0a53e09107d6ccbd538cf69bcd843cf0c025fdfea54ad4d9c78a0d1c78dca1d42c12f3a460c1c30ec50e4d1d9e29b44ce9521ea20bfe8b2978d48195a06fdb6dc77ddffb020c0482071d4c1d83be0499e43cf098031f2d7b04e5515c0f397091e2c690511c60a42fba401c7fce024a297777777777666666666655555555556b0111d818002b3ce2c0e7935c3539a5943d6f8788584a29a59452cadddddddd9d999999995955555555add1c472c605016ce00107c66d74c40d225b79d0be81d1218b52f24252e929f370035715337d88071dbdea36b026294733ed49da95e078137481960d7c4a6dd12228fd937d8d81c71ad8245f635996eea1062e33a7dde82a168f34702145cfd221367034291c0bb080086cc8c0030d4c50ef41e420c16f63cec0c752fddd9baae2f67f689981cda582a6d8f9542999b61761a4e0b5051e65e0b67db4e7100b3cc8c067ced71cb2651aef13021f381a021f38fac30113d8828b2d2080c3630c7c0e2bcd6d21a877bd376eb0054460e386871818913aeaea7da38bf91e6128a648c2444c3a33040f30787c81db187935585ea3461838b66cc0c30b55c30b8f2e7027712f293db8c0266d7af3640d1b13da17fc043e703404b86040036ad47064230559377284e1078f2df04982aa0ed354abd975408d1a356ae0e8818716d8ec2c3d62a2cb834cc902db56d1b325396ede211e58e0d365882245fa25f5ca8796a60be0594004360470038f2bf0a25e1a42557712325bf0b00263d972ca66419a54fd3eb4aac047b54893844abaa236bee8a35af0a042298fa898315a58089902d7d5a1ea6288177f1b5260bfafb44dae09959dce8247141811e61694a80f0d7d17f380021fb5327f091dd2f7877c68d9b86156058f2770fe1ba3051f1596f9a2df8b63a36f74181e4e30ff8794256ffe3bff432b25306ca0e0748102b3068f26f0eb5fa7e11f9ab1ec0fadf4376c28143c98c0554e173a223a6e5970091e4be03e47580ef6d9a64dad044ef98945be9df4c12309dc066ff56c1b93ea344202a339f4844874cd273b7af03802ebfad14ec7d1b0d14d8dc06e6acf5359a392834711b8f24a0ddda4525ccbd5387810810f6d27e468a79231533c86c08b0cb2bc7644cedaa30f2d05c343086c4a309d838cbe961b02e3989dc023087cb2d8a53ca432536f910710b8a89ad27d693395bb8484c70f98949d3629d58c0fd81c4292ecad9e36c2a3078cd656883967da2bbd192584070f18a13b24efcbb8762ac563077c29d7947553ea0b0f1dd81d456f341a004178e480517639896422eb5fc4160f1e386062ef46b07c6ed5c1e3068c6a66ee3ca9216a49efe06103469e8fb41293bfb5690d386d77bbb416f248be8a0e1e34e0278b594c1bf4e820f159f0e9175b447fd498265a830c59b09b41075d766927c8520d3262c1a68b119389dcd3033260c1890e2964a9c49c40c62bd834dd3a1949dd325cc1a68c5e4982b68d6a221f5ad60a36768d4ab7d458c19f1279641c954766dfaa206315e96c262f97481a2ac850051f54ac501ba4a9e06386e829760551c19b925e962a654a66b15370a15534f2a6460c324cc19659ce24936ba560738c37395e34c9162cfd820c52b0ae112b692b2df6b9b1a360b5ffdf6c2c7fbbd72590210a3e624b4e7d1f4ba7a0f30119a1e03a47ed1a797254243f1f21031464915c2e7192e50e055facc08609bee8e2868d17a0fe1b5a9fe04d8fa5a4d0195154f4a115c67f0b7054056ad4c0610150840c4f7032e48b9f04991e7deb045f979e1355e4b6c97f192083139c6de974b35751d15d199b60d3c49c227834c15bd7dd4bbc904162454626d81d611d72ca4947d09cc4049bb946dbfd6d4edfa3bc04afaef7f93bc492e9271f5a5b708002a907199660625e4dcc29d5460bd14a703e964cf89feeecb212e32083125c5a0dbe296be8e80899049f74c85c5141a4041992e032bf6afda424093222c17f4a3a658d8d57c40e093220c1fe569bd68e7916eb3c823b0dfe49b74a168baa23b8ccbee96934ace4688d6054eee4bca21af4540e23d8339db542ba3c41d43a05198b603b5688e575f1ffe4f6a1e585176d6323204311dcc9de6f4fe9eb767a4c046f7b7a2be96f0c9e768e760ac840046fbe673a5dcad433559140c621f810238527e1915a4c936108466565cb1123662ca310bc6d0e52a377974e8b2c136410824b5a2cb87fa8a047eb41309a440effcf923b952708f664acdaf68ec9080497dedd42f22c32820c407017a4a7b8276a635c8d1064fc8109e9d3de7966164f1e1064f881356549eea4353bd116197d60846d8ae4f676191864f08115cfee792c6fdc8c49d805197be054a9dcf9734dd497857a602bad8ad075091764e481abd2797eb983e83d0f1ed857134fae1ddb32cd92051989767e134b0b32ecc0c51ff1ba4e42c60a32eac0895c2a2619720aa14edd820b3a705b2acdd5da44529986828c39707e1a5cd734be5a7fd6041972e0d547fde69d9c64ddd1403d41461cb8cea27b530e95b1b5c76181e2041970e0afe3996975d31c826c0b2eb6e0620b2eaec87803779d82d488b15479de0dec28bbfca9a48fe5b4816f8dd77d2f2906391b58179d93a879aacdd01ab828de1d324cc68c1c35702a9578f260daeef334b0a1b73b4a369b988306269e06a525620cabfa0cfc8ff2cdeb9dd3a4d70cbc8be7491a8465e083d4f494ee918137d188391a82ba680c5c6eade039bf572f62e06b93b85ea691266261e0329bbe9cb3fb8350253030aa3607bff1bfc08b70eba032c80b5c52da4759deda309d2e706fa61a29bd2ce40e1738d31872ec982ca68cff1678cdf335ddf77f417f2df01a3d585969b3c0a4f0d6cbef0a5f150b9c8c9a46c85379f7ce2bf026d4f9ebe5bb4fa61538cfceb4203aba2e590526070b11cc829abf4705b6a4e9111d9ddc253705de6bd56e82c8c9bb49810b39a490e29ae797581458ff8f904e459896eaa1c0e84e9e7bc15310abfe096c9d9d4a7fb5f15bf24ee0767b7265ea975ec93781b52c3992498826a2338193591a7563fbe9c497909e145c2530d2d5e3a7be69bc98243049e5893caf20494f24f03909cdd39e75948947e0bcaa73480c8dc004ededf9ab3456a61481df2e1fcbd8ad712c44e02be6cb93c52749d00d818f64228a090f09814f955fbe6f393ae45010584f6f166c7b842a19020227f33dd6a6e0416d433fe0b652be536bf980d333dd1126e46711413d60a3db55bcee9165f2800931fb5494dcf1b3b403fe33ab2b59f624261db05f13fc3f8559b99f032ed25f8ebb796e4138e0440e326e1a492ad47303467dfa4ca12ea96cb50db80a4a7b968d4ea15403469fb4d8bb2ef97e1934605b3f7494c7cc82d1904b7b26b7adac9105ef21c44a9e31867c31b1e0ed7458a5cff51115166c1a13159de72bf8fb9c73ff4b869c82ae6074b438714daa886c6905dfe97ef4ee54a74861051fc95c0517b384a02594ce1a9e2af8b0187289a43aa9e054c6f466691d5430f233d3988c3f399d4ec15fde3ef548aa524ca6e07dcf825076edba9d52f0f13cbdf89e5f659a14ace811667563aaa4eda360749a0575b628989c639bb0d016a96442c186caa1a12c6a5279e3a06094d4e4edff962b6efc13ec862acfabe399b4e99e608370aff6f47b428ade09b6f5ff4a47c70f0d9d13fcad6615dd8f99e2c537c1e5cf1ba23d082bddb926d8cb90123402c198ae18720a410d08d62e555dfa9341e7ba7f60628abe971354fa11cf0f9ceefe4925c53ef0d9d74679c7ec0ef181cd71434413727793957b607594f77e0879eb31470f7c86a452de88631e909d6fef62877860cbdc4582cc19218feec08a6e9a5b471e3b91b303632aa9cfe9636a7c6375e0458fd229fa8a08ca5374e04b9366520f0f53ca39b049273d316651d39003a75c37ed2c84d08803a79dfaa5b9b342ab080e6c5dcc39efe45bf57534dec0c9f4e9638812531242ee06fe26e6ac643a525bbe68b44151fdb14d24a55d5727d060031bb2c7ff8a2a925334a540630d8b8c222adfb0a0a1067e93ca194787fc3a4a9b038d3470fe2987949fa9076ad450a58106ae641addbff6c281c619b8d1dc1d26326d50495fca24689881eb8e24cf83280d992d607491c3062a031b3a4b42d07bb71c6c32f09d2d6af2a01b2e493806be3f4b2fb3a47422420f460e3d0d010d31b095ae335ed2b10c34c2708c95a379549e95b6468d74830537d20d16dc8d33d00003eb973feb44481d3bfefadefa05466f23c46f33652e711a5ee03daa9976515a97a2e443ab58036874614b26258ed89746521a6870413d791b7c27869ed816105e7a4a77a8795006400a34b4c0c6113245543ce420ef00d4804616d8147eda563f29dd22d556d0c0026791ea4ab374b6e0620b2eb6400058058d2b30297bbede4c1e2b7017e397eed17c7b5977028d2ab0f563215ca04105c64b68ef1193c614d852be9d95f4a510b1f5c0161ca0c016526094feec27926ff29324f4c50a1ce085170be800fa6201356ae0481a51e07c83ef77b85785e61ffa5e78d15c7ce4e8e20336684081ad929b4474d2255d251a4fe0bc336e9dded0c13549c309ec9fb9f9a6642a814613388bec7ae9f626059d3e1a4c60745fa3b504258d253016fc5554f654095c69dd14f5784269cb6181461258cda22bfe4b8ee636228149da6ef5b46f4fe355051a47e0ee4646fb9114925462046e528f055bb5f4a6361f5a29b071030c1be8bf304117388acd804611b8d6e4d9c3f257fbc60534885069aa4b14ed9492867ea03104ce2e242926833ebf8a87818610b8d87dbaf92e78051a416093768cd63d1a3765030cb4f5021a40e0f347ef2a0bd50f18a56ce377d22d1368f88089e16974c5d7bb98e4a8011a3de07feb22e9bdcb537ef3804997a21593bd7de8b903367704d5a9dbf49bcce1e822dd4043077c3a7ded79422607fcd9e9ab10326adea938e0ba368230a1466492e40db80b1a342b45fbd0ba0146187fa3a0b181860db85c179692decc165c7061822e70bc81460df894630e691de94c3b1a346084c848e9ae828994cf9805af29fb7259ccf298643364c17efa744a7d5e67c4824d6acff24b8cb13d68062c18350f325d53e5158c2ebd6ceaa2549094e40aaecdde84867a9138d919ade07fd36cad3cc60a4e8520ba3e7956e9cb601666ac829316ccd49b50934d8e3666a882911f39978590fc37ad54702ab2ebfc6d9f810ac69446cc340b4b9517e4f86205572d98710abe36e8add48e23820a3fb4aa0f334c418e685ee1a271e0c8f14517608461e3061a624629d8b0a4224308cab3a6f5a1f59fcebeb840072ec0010aa4e0051d86e11f6690828f9424c58ba61a9a320a6ebfda52b5883344c1a83ca23747b1d82544c28c5070263d2fad94d9e6e5d80c50b04985dc4ddd1235c493cdf8047f9f624e74fb4ca6923cc1ea49bbaeeda04ea051b3051de45a3338c1885ed0a99e3aae6e76c626989cba2b72cc925a7ecdd0042f32a5b49b6189604626b8bd55915dee39488ec1046ff2aed3d896fead9d81199760337f5ce3a80a8be0f9d0524bb0298e8a1c159d4cb50106322c98510926ed5eb8e9fcdcb0f743553083128c90629541c6f4a1a5d436cebbe00bc198310936b7a8750f9a5a29671f5a36106a62409821092ed6a9904c7afda185c50133010b02b0831991e063aa0921a94376f1941990e02e4f4ffa7e6ed64faad261c623f84c2af366b6103289a02ac30c47b023b36e7fa7688561462338b190435031062543adc8083e9b8a52cd4e178448c101062e82b75c25dbef93ffc88961618622b820a93eeade29a1b24f047732f5ef4e9b5b6b66ae30031128987108b652efb3a978a4c298610836e75c1d7fc31c1ba0c08c42303aaaa5ea87cc1e51f01f4679c18d5f4106ae70ec0c4270ee61fa6fdad24a6c808131983108ae247ee4d1397474c9ff0b30ca243043108ce63da1a15de27f84dc308196b180086c2060462078f136a56434fd432b53f03922b0003eef811a350e10fce937a5ef69f1acd63c38ccf8037779622e1df378b258316898e1074e575a4b2693d9074e63ce996e29a253b06ad4d8c20c3ee06d392d4735f7c0957bea694b59d2a4d0f4c0459710f4a7548d1a17851979603ce5fe967249fb1e84073ec5c817b3a9fc0efc5f789a9e4c71534eedc08fee2c298dbed48191577a5bb6a1031b2c0611838f2c259436074e43fe2492d96d7a901cd80a69d94ac51c925ac8066ad4a851a30933e2c0e776a5e89dc6dcf40f07aeac946f86f44a0bd137b07e27fa64d647fde8bb81f110a4a765fd0df6c936f09174882725641f69aa05c30c363039688b23b3ba3ed7690d5ccca91964e4513289781766a88111f7bc937eff3430d93be7d6490b5144101a98e09b54b2a89cc72327c7e3c091e371e068525a98710626f578c55c328d7d9ca819f8fc1e47e78df6505a512bcc28039b279a92a9a9d44ede050e30304006ce334f74ca0c9683ca47c0046f811a3570d4a831630c279821063eea696bbbb7242b34230c9c55e5dfaadc26f4da1b5d34066a9412668081dbfea4e6bb6f95b4fa05764f249be8be5d5b671b6678c10ea1361253cc7781ab102fe4303dfae31e3135cce0026fa3b6e4a61ca480195be0bfa3e897e72c26d2a3052e69b1accbb0189e6cd4050bdaeaac0433b2c0e6ac71737b98fe901b3bccc002bb92840e9e33bba65ebf00a30b626e987105367dd5bac72039283d5981ab204f7b664c1518bf13cbb8b15e84a8c077b4322d5d229b58d014186527a69e213753480adc25ef28b69373486f1478d3365ea91b63c4140aac88b85b59c9f3043e4427652ab3e304467829b53d794af9c79bc0de5a5aa47ce59334ce043e88d024645497c0f5e59d6d59b49c4525f0a694f9e4d75c5b9627818d9ccc94aa18b525d54860bdb29b6b57fefcd111b81bf5973ca946e0dc46c7f16411169d74b89590086c5f92dc69164a2bb83386c007f5699e237d454cd91942608245ba9843fcf879db1941607312cbba9359253b660610b8983fea7afa5c79bb99f1034e7ac4f7329997a69b193ee025073d32855ad1989b193de07334ab246f3463bcd31766f080dba4843e1163362ba9d9019b9245b0909374c0e58b46b3f6e480c9563a42ea1c074cba8a183d96f2d518bd01df51f399a8e02b221b30bee12f7af3d4ac643a478d1a2c30336ac09a567b724bcfa0013bc2337eec1cd94ab3e04226d74ccd64aa29b2e06490496bbdeb7790652c38a95127ed7710165ce8145b24085522eafe0ab65c7365be64df96ba82cbb8ef1d622a6c2dad6072ce27428c9b929a8a15fc6ad2bc69ea3d720457c18550ba2d795d25758b2a58db6c97b285647b7f2a9890f3b8ae97870adecbe458e64e6bac8d5282c729b851d79842f48be8eb4cc15f2991d3268b1b538e9582330d22e6bed348c1c44ad72443b4b8288d822d1d5293d02551b09b2539a9f0ea607d28d8344b3b93dbdd220705a3ea72ad289ddeaefc04bf397a4f8aba59c1e2093ea99074c671ed64b9ea04af75e6b15a249ce0d2f47fab7d69135ca76589be9bbf93724d70fe399e946dec902ce891095ea36849cae48f08e9c5045f39df9952a14d786d2ec1ee2895f304cb3b3ae6e90c3c2cc1b69daae78b5ccdf9af0497429a9df00d3278e45082dd572ba12b7766c8492eb6e0c2bcc063127c04ff4d5f5f9204df31245f59cecd5ac21a356c54223c22c1276dbaa23f7520c1694d297edace11697d041b3d47595987183cd28ee0c32cd8ed4a3209aa6d047beb96c482e8cc0f414630f95e4bb5018dfaa87470c6e25028100744c16010c3307c625d01f313080010302a0dc782d160a448d23c148003502a244a282812262a181830160746824018100805046150201408854481504820516b9db90671c09953c3ff104d00fea586362b38cecec9445044aa0a4ca281ddce3e486c0678d35135c259486dc1bb74c8fed6d5ff3bd463c4a6c50a504555056e23c2e9c7192efeaa0a612982df0e8b904d972f162419298ac77027d99444e53f450ed1135dcc41cba0d9619d326d012b65833afb40ec70e01baa7a120186a375aabfcf87a7a23f3eba2603196357ac0f389d023232e5f8b66a49778c742b00cd5aba133b897241428069ed9b4b1bbd2e1317b5b9341ea880a2a32905b70b39d21f98b07136f58c70e9519fb82151005ab1a277142ddb283ddbec027c22ceb100242e780f78fa174b4f35008b97855e708c0eddb90fd3f91709a481dea4d6d52aafb7040a31e85c8eb153ac29e84c923722efe921a6211336db4df45b6a43b8fbe1a34d1e87a5a5415a76fda794a08cee2e4c2816dd070b881679812e3f47444fbb0f02a6c221058b1effbe44a2106fff080044615dc9d963569084c9b3f443f5ed648248c752577d994b8b638af5134417471c8cf4fab456e27efa89a3421eadf740d12f384b1ed83ec46960881238a0939d7446ed081f83879228a65c93285d3b44c7cf25502600a128303e884b14ac43a5b9a356105862fbabc62658c92581bda1011044592eec764c341840db3e845ce61f51f0dbdaeefc575fb6f15d5e96df9f9bdc14c32e3405caf196a8e5fff0fb092d3532eead586ddf67dd359741a18f356b4a54c7c271e0f6c004b2af0e5b2294d1b98053c8cc10611eb1a6fb73bceccc9300eb2a55b77a0b3555304b1913ac5877f58720078bec2c7de64e98f6c3b90c089ee25d7b4de69b9f6a9d9443099f77f96aa5c15a6ffa08155d4b7b44be316f233810756f9793ad41eb39bb5dbd4cb803cb84db18f2b4f3e9c41f6d1252a4bb8d783ed61e3b2765666daf4c528f2d48bc2dc80254490f8b18c04c7cfda26c0c6d4ea9bcd0a2ec5fc75c9e868ecb0ce614aa098e4e6046ed61d49a9d78fb3fee61cbc4e24f0ea590ba18dae721d933fa054edda17cb664c62fa9622d2ea6f1ad4638af0423e64ef35dc4db046f451a90262b46c1e263b6ceed43472720a8103589da56455e4f7c175c9b8b9d5ebbb3d7d6c49097c338a68ae8f607cc0ed9fb547c892f550cdde50409f4503b8d1a5e19aad4dd03d4637aa4055f5defe022ba2967389a4d138e64d07989661306df9c4c5818346bb6b633dd99065e0cc92d8642ef6519dc1b6b2f6cd841b6a2bfa3d66e0c695d8e1603484b71000af7d81da913ff2e147142c4c2e7c642cf1be37c0061b3fd8a584eda3f3a104c0a60f2cb14b5293226056a8593014b4950e3bf1eca4a7cc00c2e38a284800fc1a32b387dc20a8d97b2bd49091a7a3d80c5f9f4475ffd13b74515abe0d53a41cf9ce88d3df9e9e2d8512c8e1735675d7eec7edc107270057a63af1b49b0aedfabbe2dfc8c5a9dbccb7475a902dc3c06efc57e2531f4c01a619f2988b8314719404a9bb119e35966ce8a28b1f06baf70c24778ece25004df46d380e78452ca7856958f1b65fd7f047c27a5a62a126c1f14053e25fdae7b43287cab1f90c8c7eb60b19519673bf735e60fb2ec49a60575f4f268258278204908eeacaeec5da3f24a9fb7e007975710690750abba1739531228416cd633d0522a515847ffc5a4b89285b0d6f57c182cf9f1bc62d2aed976cc91bc7940f30e336168a8a3d18ec33a859ac0678d866eb6674c7354b85a0dd78156a01a93cd094d2b429deee6bf07108e4e5c87ec2409ada82cb4a14f2eb4cacd7ad71dc1471b25c4903935386089370108d7a9f2503580724093b98ff2778353f635d72a5aa0f39db6f1785d30329578ae80b49968d31941774ae6052a42bc5c0461aefd76238ad4028c9ab003f5acda9a7d97f90011b2c3326ffe97b7f8f5a4e675a2d0eef6d94a3e4fbe25ba24b65d769edb2b02b4389d12c329ad3e52e8587d135355f048e498cc171bcb8a3d79a78995a7820be964ea809f65fb0c7b830eb4a410ceed29828dca0f17dc081c17521c1145d995484ab5cc44654faaab8191985c43c5ee37bde7ab1237f4726758c876e2cd56c2b65828e94a65c7428654a159e273790fb1d1c37272ef8158d7a647b55ec8e3fd42e07b08b91961573286ef5686fa610ff29fafb8f64e7cdf37adc40fe15080115162f8e93d3d950003811d4adba50b3268cb34422b65394ff059c064ccd4a2158543b335b9688426310273a3f29f7d07b295f3d4fc0481a391661f1d135900c9266df5f36e529a66163d89b6818f467cfa796e273f5af0f2e58ca8ef2f6cdb7c89e405e6ebcac1b501d62a65d542cb1e8682d552945ed1b34834548b938a3dbb387b127d80d8beda4e130bd9ea48f391556bacdee887b736768391cf985df19906d42dc2c99dac3642384e95595b3688c969c57c95171945aa5bbabc883e8b0aa6f2693a9a14185a540e1adc4a40d382c753f3ac6b43e0f34105e6b5c7770bed5e6becc3df970a2ccc958cf24369f26db8e112f8f317102cce41d84ac03d8ff9f2425f6f12ca1c093a70fdf94c998991f4498035afee45682c884c871575395f9d045481e31a0504fe6a802e48c2cb45a2da057f53a952369ac1ccc9db427dafafaf37a0900595d8ff332302314023b6f52280be63f258e1feb3155500337e70cef42c77a555dc56a5679cb371e61fd3d0927b00fd7656769ecc78876ef20ff2c28a41c42258a88d7536c3bb2503a4b63600bcc1c4faaad06b12c042284ae2c21057088b51345965fae18ee323339b08c7f7921b82f0a2198bf565cec607202c8d8e0da17c57398adf9e4012fdbfc1ba420e6ef17270d7bcdc1e685636e61056630a86e2c76a05a07d563cf731b944caf91412711c2eb17a26004932a0b9eeb6b4a32504a1b1ab50a31b14a5a2142175728ef190c816a2c76f5c47980de15c8400d97a2927907b086d581003be50825f111227010abcd950c237b80234c92debe9425b33ba6c38971661bdba0976e4b1c172ae82b5b7a563bbdc4860aacdfeb499b1109660a87026c7cd40d2d64f696ec0c08a3bd9f1c4c49902b9bdba128222c4ab189a2aad5e19b8ac08505cfd1ef8ea8ca1ceb708aeaea2e3c3895bd88bbb8d7d64e8c0d48bda453aca201abc5327d5e0e205c7115364564a214ceb80cc0090636b6c5240cca98699fa3606c9ccbee99f7164d4e3f5bd4a535d43a92d3724a6de1269a8efd5c9fd0d92b95b41ac95fe66db8dabc01f4fa9f8ede3451fe27e9f3eda4bba9c56218b9a62387d0ff377a36a87505142ffe3d8b15eefd67a582860fe9baf165a70be8412edd110f5595068a5de4757df737a9673ddc6fadaadfcd7741b10965b68a001e15f35975c8e31379ba1f9fe818bf7f17d92e0e24aaa10403e4e423cc01743787b1a4b55141a19b44a2185212faad04a1f1d24cc0f6980129ce2f02684f8eb45a1711ea7c10696822128cdf9fbd07c56a86a638191a67c6dd89acd5219f783c51bc3a29ea2c619903dd01d507c3485f540c08c3ab752a36a800a397515a90cde3437366b2bc3ee43e182ff2b0354ac7fb28265fcf02cf16ce759eb99ebecfd59d219f76f37e25c27a090b81dbec97cd20b905b94413938023a5665582f60469cab15985afccb232aaaa00c68607f1e6a7a179678bf03258ad56c9ad06103416e9b18754798cf9a33a7bc49ee2f97ceb2c5428deb5386f3812a086c343dd597e763a4c1422502c4772b10ccb439a18a5825cc7aa5ae5fb3be72ad168636214d6656192f486b85fdd1d716268adf6b289681ace3b317d5208ea7165e58adaa80ef59dd53985def1ae8247a6da51508d2b169e714185873ba6236d98c5695ad0c5b6b6dcda9e69b94f12cabcfc5c1a8a38483129b03bb9498e36c9e45ac698ac58b11e0fbd306d4e3523dd91cadedfc62ee2f3dce3739febfa5c9d863434060548266286590b2e943091ab81b28c323cec734f7a532ac56646677ae1a49eb2808718221acc6be168d087980de3034b26802a10cd861c034c5614414218b92cfe0bec11551df9833e6ff001acb508906b4cd08873a6932dc6ded81c94d13092ba10ad9d0f8d08ebdd490181efbdc169f5d79663ffa454ea593942248d6cf5ab8c86afdf4f9a7485053b318683cf0587038b98c039790213de8c46e659f4c115b8bed15c7de0791b79a183645ab0407b8e2419f6e0fe2abacd5b07854eefc94b63fab9cd7d0af28e16899a527f7a790807b7271ad89397bcbb0de85ecfc31d3d4ecb1330351a16fb005f26082d4d9258cf2b5c4fc0439d328b78ced27ea1bc82fc3c0d8df6cfbf9c3573dfbc5ba9f6a91e9e0acfe0f04ae569b5b2e3dcc5769c178c1c69586f1d34dee7f0fd29454f3b749edb0bdfd5870a7ac05829b9410a0e50909fbce0f7039e988247c1b42a839549c63cff5f8b4d796fc3b3f7fa03073824c707e51f46849eb276244b9eefee56a2f5ec27a28012f402f21f6e13eb4ab305c93949ac782911f96efc2d68b0339ba816cb4461de7043419afd9d004c8d616c8080aad947c6e6dfa1d3118510bab20614eadf51945b0d10a503151ce5af6236a7011145829c4d7e9809515f63eb18d8c55986cd2b21ef23df30d007439b121ecce08954f32616f6e5d4a7283209c47b48766ad29f3d036a8e80bab92748e6df41b2d08110baa2c612edaf6174190910a5631895cdbf52188ad43b1806c3581aa57a4408a1ab7d3174cf09586be2af3574aa76752ea9b643d241bd935f4202de6dfb70be87b8c9a8e47ad4a985887fdf63b2325d21707d850364348cf80271cb8bd357079ac1404e6be451909c809322a4d316c255b4c9051fff2925b7e3e3f3a3b684dafb14f4259188ea01a870cfb626daa30e1bcfd88fb48e0a570b0551c5731e9051bf11c126b2fe6ecf7ed8ba83b6b7f23102934ada797279ab4aba50accd2afad3da38165405a28f26382ad0e615a572c7b9b660ce6d585cc3f114be7619f48c31ff0ba3ad1d8a533497e79c81ddbbf035a34148b708f8650f3cd04207496df515497cc0ca3cb5dba4dd52185b95e05e10f5fa2ec6b09725b038744e72abf6667fdc243d71b042f8a620d7da8ee0f7a4438f8557ad7dd5376b8d8028120a2a964e15b3cb1d4d7489b56097a134b3552eee43b302dd7ba1e2557e57d077af7c6e5dd2390ba2da1df7a82b93895a9657d9283b4dead1f4260a1380f9681103bd80b0c8486fcb36580a087cc6d83c9b4834477c3738b5d51faacc947d58281d8aad7fe0ba097dda19e6528ee76f8a28d144caca626488638a5b53432d15bfe621bdafb68eafef5732614bb89fb5008ec5f284e20b04f70ec4a23f477effccd106c558d4b7528961ed1efb8d4260cb52d3fbf7b5b464492a59b1b6b89ecb2b89821738c5f1f4346ea65da82dd1bb2b6c4cc1aaab6dbd29931f4aa28d640b3c75c8715ce4b27740b1d21d91fb18353d829da93991ae9401c52b7cc10efa66074ca0c55c9adab29182eaba080fd268d5aa27a606a8c8044810bfede14391b0159f6fe2f1b5e3ad614fb840d11a1337edcc3b14e14e334eb5ae9e906a7a3f727324c7903d69e5a06d6f66834dc55e002eafe4b62e102bf0d00190785bbb50666223394995cf037a9fd6a4d8e252587113952dcb53168e7afdce3b4391422051405721324461c147173ebcdb2da312ee322724eea8a93c0c60bd01c5b2ad24b730ce4194dec0c4887642d330e65d8d5ce3a9001cf57676cf8410030cda597dc28a5db60ff1f8d362ceac28453e2f7d29142ef9ce2114f91995ab4ab90e489c395e324de814952a3fc5d42037cb81ef4559090e02cd96fa1661275d15b90b0ca8ac6767265b25d3d73fe7e4d3906890801aa08ddb45a01f3e9117bbf3e60ab358efd2c0f1d1d5e2eb954f70756415da71eba7e204bc54a5809b4729720e2ecb74291bdb02d647d2279b06f05f1fc62c1df57c8af658b2e788744dc72995bccca4a843a00245b21bbf3387649a3614a904465a19c02d6bad4239222943e5b74418243ebf135975a51cf7019453a60ddd1eb68313a266720f3fb6ca69dcb875a26f464fbb0ba77c640f822f7e85076bd5892c2107f9108181efd56e152d7da11e2135ef215183e04e0340e8e30734c49c00c093c688b0106d8d2c50e049ee6c51b4eae27f71fac90af2a67dc89e6d18dc48ed617666d190d3e1a942501ac53d4c6e0001b894aa2fe0fc67e15158513dc63e931bfc13594cc1078a515a97aa2973c6fff1bb38e0b1c9b3c0d195ee7ff0e93d8ca57ee1390661031d1b2b6e1ec99a250ebf336fc4c68f1af6eb48d4f7c9caee76638c351585287e0cfe2b52e27801d845759866dc9cfb444235bb482ef72dc0db2a7594aba409d403c86a99d7c95fda41ff85e7b8177144e2b72a1d395dda85a06ce9836df559f8558ab3421d084351adf4177208f867faf6136c55f9f6dba67821bd954eeb526abf45bf84d29819f628336995f4e2ae3011537654913f478cddeb70b6f664452f1e58962c16f6b1b9918912e87ba6b53c9c7cbf2e587f065c7be079524a075bcbaebe38e21b0c3c48b9db5960256e430f5a38eca08740f362fa4500ff6665613aeb4bbac517cd4da283983ee8f399e652385899fcf0e043cdc6e1c9d4811b56baae5d1c13ceffe7e3bda7a184e1d815537a4bc7d87d7c0f60455f0dfcb502453186945564c1141b93b233133671d1822738901afbcb8e1d3b0eae4dbdb6372e07787e874b019088196aa785b4383d0bdb7ce1aa0d86422b834d5b37e3d4901f8af6d14d397ac8069404b98a873443b1bf53f248744791af015bf4081571549b992fa65048dbe96fd209b4e228729fe1f6eb07c938c32b53b33423838c19c17c044453767577888f52e72486ab91075da28a86047aa355d74c1bc4c5dab3af560ee7c937b78df01d828f166480a6937950b21750705552d1e85c2df02aa6168c8282353bf98f752d25e3f99f92f17c844df8c40d1c650a5becc793f3af4683c28efd6078cfce002b12f5bc0f0355e003214122850d29320c32af2003b5355a5e49ad767110b0a77203522fb01254baf2eb290f6a45688cec6accaec892a0536d96724d9ca274942f66c8c603ab316b695092c70d74aa0961a6421763092bd024014ff860bb0932156b32a39815275fcc78b4ef9a47d334161b9cbf31372273998bda3985bc1f0d6d44d66b369addcfa4712b2ed5495f453c020484c1d9b4f8a46d570a923d4d17d48812c3faebd77090799cce6d6611cec7e5b8cd0fc3640f8bad70b1447a69bd50a21cdb4d5602fe5d4f6128b9f6c10eacaa281609eb3d0e844f758a6d2a0063d42baa5a0df7c09c801bde26f48d1acbce83d33883235562505ac6c520fba8b7ac99df2820bd35e1c687f00c1ce601558ba14275342b69184b188a0c3abbf9faa2a1dd283d62112cd6f5fcd5681b23c3cc5c4e5969f6e360e83e0f25eb476d67a6347ef718b465750602b10dbe5ce3ab07004173777cdde3056aead6436acb193eeacf78f7abc3b621fc7b77a70d968806924e36627d6f7f6ca3da787ed373d48cff077faaa1feab2bdb76fcc4b883a3454d39730a7dead11ebd8c64b89fd948dc8ceb49db458c44b8e94eb82ac02e8acfe439b58d785085233d86af7c561292f06bb781dda017be72ad39554d901c4be5bf4007d087909a7d3abebd4f39ab0ce5a9f1e2857f8d0f312ee016457f09d3b4dc6fb0e2b732ade2cd0da510bdc72ac37763b8c863d147a9f61e8da4f236a486da979b549bbd7241b8db85cc7fc0c91bc036879ed593b6bafb5a2b6b935462f35c05ad67d2dc55c68ae7a2e27a1ca3bb0cce318921275caedb677d5e572df213f179ad691e04072eef01fc0170e411fe891e12b7f06cff8ffa0ab624e5ace757d93d5dba2b1e61e7f5e8fd7cf7ab77ecd1f759d5e77cd5768ed316d0fd2c0883bf7c64410cf8eac8fedcb7d389c2fb44cd27d434b7948af36e3dc521bb47123b0d1283eb88eb2fea9cc9a4b9b6b474da875b5488dd6425a5d436a448d58ec1f71e2d3bf5072d6429bd7d2dab796ad456ac7ad4fbb6d6f5a49abc327ca9d097f7d3b803e190d393df97f665d65b29a6a955b89c8d9b594a1055bdbe494a1d76e5e2dd9b376a06c6d43a169f76414376a3b1bedf5b05a5b34f41e34bde4110f38d96c53fb4a7f463e396e606f657d43ee1cabcfd2f0dcb1d8672fb705b92bb0514161d9cfe029827c900af52d3c7f4a2117f6bb86622d4e59e81d64365390d09a14208101f21c158fee11e64518ca1a8c01d14f063850b07a8636884cdbad5a7a4d1e437bde1ab646a44f9dd36386d536b9cd4c4b3a08b8b3890a4e20a48e184d46882c9b14738920f06e9b07a2de3ea79a1c96fbfeb01417e3dd0257c3ef2bf7bab55ac6014d55ce2776605e486bd3f0e97c176c90459f4ccebfad0eb5225ee0f8cf2bab27824cd99c7f88a3290372a62f4baed68ff3ea7b0095199c0e44c37e7a2e01a99c1fcf7785a3dbc9060a84a45ce7733f6c71c7c36f58e597a0a66e17b0794cf0878a10deb8c087aa31e32f17112bd030f2961169fe15d92f497de993a850b3f573a6fcd287529a40c4b113b5cf2d8df3649d101b8ebf562bb22b8128ccb9ddbd39a87632a105bf04607f23ebfeac015205115c5ced692b693bda08cb57636a73456bb1e93ca0395021aff1a5974fc4b49ecc87ecc65fd5d11849517185ac84fd99cb73cc7d13a10dea53b42d34c2bf4df4064d69e5b420eda2856809ed41bb6bcf5a59bb6e5e7ac80be090b4303db7cf4d5ec20e74620a7f9ca6c219e32b33438b40cb5ca15e0f399c8bcc54d3230ea58db7edc5c7466c1a47a5698824270b0400e9cfb1b3fad1ba4892d7ef11bb3578f8d71a266739b324295096769ad2d94affae31a4b83507974544f29768935a3e09f0932e0d817ed744f735379f04f8aa47a814690392b38729d9c2ef6adbc6d5a9c63f5018db5217b6da72b0816210b3ae8253de93ce7992fe99c8bec06559045333f394ed4200227b18ae668809cfc576f4e4843ab26eb54618285e21b52365625fb9168096a5f01aefc3bb020de372da8580a4e9e6c5eaecd99a415a7f107cea08217cc2b08c440831707036383ae0a936c800dc3c345260c616932a9797e079784efcf222147223f8584775ff098d1375a0ce0efd7a27956e64568995456120910ae00781893fe35a97a3092e6ed116ff994e136175cb24e3ca16765f6a7cc5795f1acd95dfe7504c320807483c395077f530aef978f66c2b06cb9cd506af04f49155baf74636ccf0e2167d73f2b82e26693eb4a1720ed0286043c2ee753111929098a21205254009d09f8a21140fd0137ddedd3ad4e567969e0cc0bb03e7f36abf8f58cdcffa2a4f2ecc6649917d28123d4d64b654191026aab6fe056445cbd1475dc994e39e1f043be3a88598b3a0556dbf894e0f42878031a66be13601fc75d561cdafd6b43de415056f50e21c38c80e95d805722f7da4e73fbc11883df950ef7c789b0f7bf843bd0222b7f9b861bdf421037afca39b40b0d71fd13fc8923d13c012670421a732100769fc29b3f139cd166ae3d5b0d4c409bb2c7fdc5437af4bece30226eb454b8752052bf91abb1f45c51ee744ad21297779cc27a3c8b2cabbb58859d51f424d9c58627a5b6b06ff419f1f28050da7db27722fb5931aa4e865952c681422137d31f0211f1a1164372dbb84f0e19746184ed30bf362a57efd2f9c1f0437d1028a7a1e343566049640d3178015080a544f3313d0912a0b7e84149b453d6cafc14e1a2547e167320f7b27987443022bc8f2f049a9e8c8433bd07605b3c61974092abb440480d2e3e408c83365a50acb3e12fb86b77b0c329725726d56b882d199d82814c995539acf380a117523d9d18bd7b5c2f361e09192ede1a3573f486bfa625daa49a65221517548b60e6dbf237dc6e833162bbbc3be59df41aed2ab573cb4aba8dd20c908b29e30961546fea57a2f57cb687070fc26d48686f973a1b31108bf7b5a89bcaf05ab75ad721119aa3829ae44cca0fd639a6fe64e9d849992f3f1a5b9eb20c8f31fe37c78561302de5844dab0220fc6caea10e8e82a7b087643c8eec32ac173aa428dedc0e24c7138a01bf9f57383ab7f9fdf77fbbf79f2d3347b27cc9635f29f8005c750bfa46e9e1e8f0e726551afc822f669e45c6b4c97a743b30ae54e447552ac3dd06d9bfb66fcf619f0d8f28373c87e2a605db71633ae50fddebc98154964fe9d06804d3abc5277a3cb6a4c8a87065940e347db0d622bb567a266252c8c7833970fc1842ae10b00cbc00e4ca6a0c77864530ced6ce1be5ff0da1f88826fc183314c4e954d90dcd8aa7c7e54203ac1b8af22a7f541f4426b8cf32ad48bb27a5caee1b306a3a0e431c04ff4c286ef60464b1964b1a123cc6653dbe3954e771e6a017dfa4a554048058a3c2d140104e07985b8eb3c485dfabf245a842340729470a7f8c4de2b95ddb90b3fc3e1b95cb3be2af561ae66b622807600fe357a06277cdeb8c16a8392d29cde82152e4e8e40848fb8704bbc7432b1ebd2de0c543ad8792d17f5d764fdeb1c8981cf913a93431bff7d5d090c59f49cef89cd4a518568a192a3c1abb6fdc825054bd4c573cfcc033189254eaf3ca651c46560adb0a86287f471b2edf1a462d0288f73450acba92af8f657568f896e6d705126d0d205162b31eba5372cf27245dab01e0d23e11eb8422fa6dc3608aae30bc49778a46d603dc2be5b0b1065557b84e48ca2afd2153a2b56d58e290557bfea3f4b5e2581e95642b635797fb6657792c66b16cefcb6cf31e768681fed91bc454b704edfcbc3069e817f65e52861c4128ce22bd2b7dee7dd20d96f1482497ef54cbfc610d503ba68cd6fe4809678be64e9f215a91057ca0dc12a0db24e28dbaefb3d5e140380022c3ef403f3fffbc6e817a82bf5458da690abb680d3f7103ecb65bacbb51a86d6e38f289a2edb763e1315067a2e2eb041cc64ca72538680a09e90ad10a87231f6ddb3076af7794135422118ab0beeb22f3c6820cf88dbbe89d91fba72d92b476df60060e4b5f43e20a59ab30038b0c15b6b7545477fb76f8ad20af2930d96e6418411644b9b655300e1916e39a5d95a8658a97890958a07616955c4620d205b1708b37631f86c79bfe4b3d133c1c5911234a3dc82ce29d5d01ca40e9a538ea043e5193a1fa1057cf04d412400bb13c00b00d2a4642f2db48610d29ce82e5cf9e9da9f252f9fb81aae537fff496497b80ef963265c802c432434a6990b1f9f2afb5105a56af86c619d0670f8dcf7b443e6d3502bdfa3fa86241c4dfca4b6da7463234df8ba2f6aa8bd5825b8e0aba27d8ff1dd16ba820e7206db36fdb11c8780568129077ad8c41d6f92f2dc92036a1b2d6bae6092179eb5950f93a6f32f7387c0414e87e82cffcbb925d63fe8ad58e6f164c704e442f515810852d3b64ffa2f18bc825995686d884b8995407a1afadee0c90b6dacc15ff944152faf00b465f4383dabc6d51bba8771497ca61edaa267134215427fca7935a3b69b8623945c121f31c2791e134bd2f27f4655eb04a9f2e612f5bbf18589760eb9bdfa31aba5e1c77d277b96783fbb1369a089b169812be5872e7e664930316976ec9f5ce23466130cc9ffb2e73728a81decbfedeb6309a6cce6d9c329b4f554b6bce7827e9ed715f8beccd19027034aec642873ec274ca2a0f3ad83c480193a82e9b64f93c60cea05ae0570ef36ec6b28654da989c2ba23d92a8e4d33dd0e342593529103975cc045cb1a2487a0986a964ae4f0e296ecf28f648709371b1f6cef56749dffe25f9a30795aebad5b5c9010f1bc047ee18ed33a27252bfce0d450fd47f2c6ab8aa639825e49dc60262fa4e687ff22512340a6f9ee55eb554f1005da0101d5169120f400020a8db9d4fc29ccc027da88d6e1823de7e0737c00863d560eb8647c253093ddfb325895ffe98bb85257d46f134edd9093c98c5a86c47985db8a87531ef088416fe913f23af53aa04ff36ce9ba4b5e875d07f7329d48be80516c8fcdb9d969c6be203f1bca5828bd9e3c15d693a0d1d818e164f6a5a8e92135b1e6ab8facdefefd226a481d2fda8f85393f95ac26eff5e58f6a8890742320283ab06ef742484aa991bb8518adc3d737183209f99736feb9b6af47c38580566a323c7dc83c86493e4fe59464adc57fe921ade491764a505a19dcf8f3bddc5d62616382333b2bb6912738804de0403817ee5f489ecdf736794a44f369148f6ee5ff86b55fbd811cf927d64d5da56ba0166962526b9b965c768d78783e007dbd16da08a9a2c40ea536295094b628413f91735b8722f624894d9826fb2707a7a96a08409e0d038b191ed390466fe2960c37939aaa965857e6a98d16d62b0e9e4f6124a0668f56921b27c3f56d153649e83800d9642fefad6dc3ce75839539cad08c8aa7e9a1eb5656529fa2e5056f2d6df57ebc9e6203f01c243c66c1254dfaf390c061c2396db5cc370b8b6f36716d557fcfb1d3157a20a0edda966d4aa8608ae9022f92993b630a9833942354cb008e5835318d1b3d5481248c231c96a170883d759495308914badb17ab570b9cf99258def046fddbc207c5cfa1a0ae27998f1ba9467ace564ec90dcd2802bb6c229fedea82fd1566fe33f0292a8845d1fa8108eac63475ffe698d790df940296d02c2867083f4acbfd8946725101bd724cf3d1abe2605dbe12f7a11e47d5f9e336f8125f6e079b8e2dd21063da7401316879d14156f55bbc69c957a1e1555c61a1a4309120ae8f45992f724d513d63a076c8ed11e65467df206ebfd5c458d0789b67a3df0cd0f58b4df1d390d8a274a2ad7a3bd2f7e8333217b023b8960a70beb9f28eca20b5fbbc91e14b7809bb49b7220fab838671f26c4314e971aba26e2f1ae632d8d68b8927908c14a09695e26d4649eb0b31888d140bc83c3bfb8bebf3b5e31ffc515bd01f7fa15aa8eec78c13490c25bdb24b51c7ab537e172ef932d598b162efd652e5cf40ff66c732b67b7015f2da70da6219841bb6c1680407f1cbbc9a87e8911ca490b15cf24d594c7ea0020acd3f7f5d8d4ecf1d2a2fed909576ff5235a024472940d377c635dec7ac03e201de21f7536d29535bde74cf35947d418036644f22563265cdaf8400a32591cd99225554030935af495df074ea6b0ff0d79991e98647a169ea43d2489dc9cf0862b78beecd57bf044039b6da167d7a6a74336246d413084b45159f37dabb61eac9da9234f225492eaadcbdc46dff2e404ea372f6cea4294c88369d4f5bd4e4641cea28d70ef6f0d050eb3d7910b461733cc6921bdf241313e441b1ddc07b44065b9593a5456e3cb587af1568f738f552f3aeceb1aae1e11857ec9e7fb0a4f956cdb16d9bba8b303afb84b546a9ac28f2bf091db83ee0691e62a348b71d8be86c3d3ca1f5e3860cab25be3b25262b7e17db076308de42f392d3e55601ae3f8854462c343310e4573ed5940710462e20fc8b351514a1e7058214db871ac7ac484750f98d8711fe14e8a4a2cc477ab2d2775dc15f6f8ac36c559a678313d97e74bb93e8b8816af731eade6eb485111b0157c2b254bb3c6729758596a3e244fb43a754706edffa34d9eb04a8743aeeef20d012a8bc24afffa0442b43e240b6091b0db882186798f699a07ab6dc5fec039e8b6802e05526c66b59c4c1697c0410cd89141cdccd8b989c279f0bb53182421e3f246a4df53176df5304b510190354654388a3d629e34263c890b8248a35fcf248ee12c554290b25a794d8e4ca046c0990ac8e6ea41e0dc85648912b9a952e13a3b7d29623ddf4d47ce21f7574ee80f24d56f4c554c7e2b2e9991037641c9220288c65edd16e0d2f2cc14cd956c613a77df4db331e96e5e84258b0bddd03c7e00d4c2a0faebc317006c7e487799f31ea0a047467022e1c44ab0a3540b0a6d0013c0c3c0c3c0c3c0c2c25e2b73e4d7af9da90524a8bd43b3c1de2c04529934c49a6c879c3ac66d452f21bdc77f0139c0b870aee0a310a854aa762d20892003207dd73acbe34c2e4ebd741e4a088d92d319f6495c491c741f7f2923a9d7cec32e1a0c834cac5ee3ec917f10d9ae456f2fcf725694b03e206c5bbd3bdd5bd69fb7806206dd042e652a68389bb8f61f0300dc2063d3e434b6d8d89a7533f7a74d90d40d6c0688955a3ff6b6bc39115c6c80d1ec90d7ca43ad6041035b401240d6ad222fc4eb824a8fc972c40122263e950c93ca6c617366c60dd4005c819f4ed7c5b328e92a43433090a6b006206dd5468083dda366efe560990328090a18c41cf9592ccdd1fb7416cf400220645d6ec8d5d856bb0510e1e61308f202c80844133cb7f5f96e498e27cc30718fe23c78dbb3e808041edf13af96df367d0231c593a74e4781fee3dfe0b9a5c397d0ef9985f5d738e0e63a418102fa8a779f20591c13783205d502f3ec5da33613ac50947d60d8306102e28a3f9bf5a1ff971c3ce00b205ddc2b505fd6515b7651c59c540b4a0c9e5b2a0e84bc29fe6d62432806041bf6c6964e85b6ce966844718233d6670ca868d30805c41bbac3896d9db4e92e11e09078815d4780d0f72378c23ab0afa7ff925a5e48cc8970a9a9787b757e63b49467a4718470390292832427e3ed19795c4947b20191029289b24bd212b2eff940847d68d1b3e121c685150debd47787c2c592ee2c8f21e090f3046f8c68d3e5b010814cac608409e5036409ca0279917795e1fe34e0e47968f19243a3690f5029026a8276bda52a54d05bdf0880d6ef84870201026684a496afe734a154c6084d163248c1a1c9025b00044096dfe3e7e8ce8d091ec0def51cc4e0e2049d072c36f65e5b9ce998223eb86ef08a3078f2a5c1024e8259cf09777dcf652ca7170f2858f3317801c41513e173abdec49e1bc94048811f4f36c9739e8f854693644801441319de4b4ca4d7e002182daa62f7cf25879b32d0e41afaffca9a41c1e5377425063952499a49312574a5977000982ba9f93ee75be98f01004089cd262d927a3a503c80ff4aa5266fae3652be6417ca09e2a693f77a86ca57d2fd45282de2ac1e3ef97ce0b3ddfd989f2b877a149e5efd77b4a9235dbba504ec5978e27732e349fed8ac144a6379b71a129794f85d2a5ae4f12be85266b2c081da19795846da1a7fc4fee3ae7192eae85ae497c0c9ae7d242773fb9f9cb7251d6ce42532a46c57de7e43926b2d02b55dd78be66e82b89855ea663491e0fb14987856e494e2513377deaec2bf4fc7d9663c49de833b94237edfb315f4a501ba65668b9742c654a09b142d1e13c8312511b4e08ad42b17cea243f13a942ada052ced1a6046d73a642d9ad0f319bf5a7c3890a4553790ede5e9e427dff9a93afc2e8fca6d036655399f76b47d9a550b4648a29db6de7a60e29743129732f957ca8d38d4233d1ed1b71a1377651a83966f8937193c53f149aec5c17ea3ce54e6a50e841e6bf5f4c56dee8139a5ef99e8efb29f1f3842627fc6ce3640db37542df6cd3aa5c194e282a29fb96d7d826d43c935d297da6092de6c82044c62561de4c686e1b2be4c905994e4ce8a5f34f85ddd28c99ba849e355c4abacd74b96f094533097a1793c92ca64ae8290973b127587064ddb861490945886f8ca9e4e42e773209c53ea86427eb2409f52d539b244bf75d9e988811095d2c7e50e1ce83849674a6e46ee1f55df22314fbce976d634ac229b523b4d1f3ac2594a811da66998a4f5a67849a33ff4edf8e129be38b504c4fc99833c594795184fa7992a753594b63a612a16997df697193252549661103118a9097192c7699958871082df46696d898ef8e3d9311310ca1c9aa79fd8d27a57cf91b310aa15edc984de69210eae6b0f04b8297709af71163107a5cdcce509e3adbcf59871882d062c85142b7fcc39529108a6f968c6162731266b3430c4068bffd9ac2de27b1242a36c4f8831ae72f96e017630751f231fca0cbccc84bbe190c62f4414bd6a137a8cad7797f1c5923bfa31021061fcc6e61f468923df438b26e90f4b8e1fe670c31f6a0e9cd202fc4c8a0bcc4c821861e14fd49febdd1d121461e142df5db69bbc2e9330b0e31f0a07dd055496c07257d6dc3c6d91df40afdb6a0bae352109f638a1876d04eb7443ba5aa734d8da8801331eaa0e7584acc9adde1f359e38be48b48c4a083be2789927dd93389883107bd832ce94a4962fae4d7430c39689a723c2567cf1bd796e2438c3868c162d649ee41377e0947d6485f1562c041cdbc93415dedeee78f72c47883ba23f325bda61ccb466588186ed0ff642bc6d80b6132598388d106358bdcfc88cbef2986d8a0689127736b0e7262726b50c3bcafa42c5342570a11430dbad9999843bd8c85c5d3a0a67adb0ddd2941c4408396bdfde249ca5492b8cfa0d798591293e6511e629841930df2e4922ff55d123cc42883769e5be393f0f457a58e1864d03f84be8c7349ecf01ec7188316e63efd8566fe14ceb1375c47ef4870941862503b75f68c121f619c63c020000c214618b454fa75bf259afcd4d6871860d0775f4eb8f686764cd71c627c4193b2c71474578e79410b11a74c70cf5cb2635dd0cf24f9ab45bfa90b31b8a0d56dbec60cda29a81c17626c41133ea9aef23c354a92b286185ad024fdb8a7d29c681c3f636441ff0e26ac861858d036680aca33bce43c7d05b53d56c99b51f9253231aca0e860d946553a121ce7811855d04cc924425c5e7e90a51b31a8a02929a7b798bb6497950a624c414f2a73c885b6d3374d0c2928b22e6f443bc6135eddc8418f1fa5cc8011230a9a8a4997ca27c3f5a9180acac62433e992bcbdf4c4868d3c448c2768233b9da73659c4b6ec043d85ec1376b931956413d41c3e4e924a774cd0e653095399742ac558821a3c5da6d0f6f01f39ce470e10b4ffe831e23e5260c34628622841ff30f661d225193a49d0ff5e4fb864fb61498f044d7909d1b9374bcd47d08476b93ff9ffb7736e043d2eeb248b93929ce32e82a694f68dddf21f934d04cdb3c7ab3ec443d0fee38d7fee08419b131774bcd0de650541933be1535cbf9b5c18085ad89f16d34157a8fa07ea499b77a3421bc3076a78e9747a96fee4d8f642998d41999e6b79a17d988939446b4edf26bbd063d6b895e4555941892ef4fedaecff36a12e95e44213ae662bc9ac27f49f70a14931bbbf9297a0e6f65b683a9f788bec6da1c91d6ef704192cfc9e6aa1664dd94f58124eb624d1420bfff93e252963a6529a85163bc5c9b15292ec5e92857a423457d60f2f56c742db54f632a35399bd0816ea7b2a2985b6e8156a49f992cc418968d22157e86ea2def292f86f06b5420fa6e193492ae497ce0aed7484b90a45e3c77d0cc25325a95485eeadf993aee01597ca54285faf5963de830af583984ee26e5fb0d23985229a3a5bd29d5726774ca1e79cfacdb94e29f4123cf6937462182588145ab06ebb581e4a8ee32894bf3f6daa928f0a1e8a422d5946874aff71627ea1d03ffc28b9dfb66684a0d024a5b2895d927892d8994f281647872939782649f784a6b4e4d16f7eb7b9c64ea89b426e68dea446979ca84c64689bd035639ca066fee682a609f524415805d764424b42c8c73b1f73bb1826b492ec94b09cf53d9c5c42d97039db2b7512c42b4b28ee9e63492f42c95a57424b196c2e3bc87639a18426e6cb13ee4b85acd22434c124933da71c49683a4ae4ec4824c9fc152b73ae4042cd549b3926f9c40ea17f84fe5ff5a6324999c4ab1d81926d11ee17021aa1f559ccc8fbd1fce96584f6d59f5e3eb34d6a8dfb0823f98247a54c427dec924f2c6313cae7d71d1584af09f36e6c3b26994ec7d94e0e6464c224496e5b4a5c0707323081d0712f07f997c421e312c6860e19969051093df35e9924891b541e1513c8a0c4495899f65839c9e96e063226a15858d22e3188a7934b7064f1e091fcc8480f12f8f831a2c3c7156b850c49940d37c8884442a61cf4bb32a50c3e8e2c0f2026c88084625a5ccbb6b249c2c7c8788426f75e0e77ab3cfa6f309805a57afce0a104198e281b25a3116523063218a1cb9c7815c3de245d2e42d1d029bfb28fbe5b5011ea8953f1429669785f3212a1ed094ace2bede460930c44a859692c957decedd2ca38847a61179e3ac9106a9fa04cc6a432320aa1be7fc9d99784109a123af7e710e56e9b3308e524d9ff4a7b0a17640842f913132659388fcb3c105a9c11a2275e1240e85f4a369be5301efef983f66e7d5756e149939705197ed0aa33bc99ce7bb14bf7412f31f7cdf01f3e203a93668cb933197b50dc2d3be68d952a147cd106197ab09468529654a88c1b84a60bf64987699091074de87676504a6df00ae14191d9957b4c93244931e601114441c61df4d1295f7938ff4eb71db49ffdac33425fda741db498cd5fb7ea467e121d3425e5d225ef680eeaa76022d47c5be82907cd2c74ac2d6937651c1479d29f9cc634547770d09364e289ea246c56ec0db95529b1f5f26e383cd69fa05b6e83266eac8cfb41bbc5c4064d25bd7d929cb5925c5a83665bb5259aa43d41a706e5d49b92299934baba34a8a1b24a9727b1910d1ad41c3669bef039b3c6f81994372df9beb65499cccda0e524dcf6cd2469b3f232285a4266781d3de92f4e06b583f26aeb0a77221e833e4aece0ad49e7681331685b614b3e53183431b9c4d47573314b60d05c6c838b9d942a73fa0b8a6e3b6529dedf79d75ed0474e32f94b7517b48a25b246cb8f884a73412bd939f5bcb988ce6f412be966d375da119fb5a06c68ce5c820967414b72777df6eb91f3612ca0ce37aee237be82de29796a11a2ccc4fc5a41f9182b7fee2b49967daba0ebf829d9bca482a2a32be67de4959ced14d424937c79620959722f05fda4509225e9bb6477a3a0668ba3a7d932fe29a1a0cb9f2c3bba4d643c414f3165c78a39bcc43191e104459d4abaa53541b7dc8d1daf4a98a0658c9f4689755a82664227e92beb42e7392941376d573979de943c494a829aa4fc5e2a49262468250865679752ffc9a42368c9b4e7abb7a4476c23a895eb839ccb65655c04dd6b339d8a39e4e88988a009eae5723666f724918c216879f3f6ed9ef86c492443088ad6c82b553229133c484610d4203e2595a7af6f2392010435285131c7db860b1692f103357fac2a994d92aa4629c3078ae713945e78fe98b2ec852247f8869cc9e185a6c3b89b263f49dcd2d985b21d7bf51f3ce9af8e2e3475274c94384adcb29c5ca8d9af63e84afa35e61117da07752f2797ee1473bc85224c16b14126415be81ee4cf49792a4fd6602d744f31279c9eaa581ad242339b51be751b7ef29b8526931ee557329c7293938526c444fb09a658a8d94c2ad9e4da931882859a67628971f9717bbe421375299868670c973c57e859c372f06b7f17e1b5420de3c9538925c751e1b142d3e6e39a3e5bd056b955e8616286ca9e2cdc6aa30a4d4e922a19aee3f5cf26159a6d8bcc1bce73f2924185223b37974a9d5d548c3985963f57f2ce639beca6d0664c5b3a39e860e92e853e722577a8203e889614ea87e5a0938e173a996c145a9724bc779844a18789d9741c6527fe69a1d08328ddcb95639f7c1928d42c268c12bfec92e7d427d4f8383a7fb4f284e673f2f535f394387742dd2e4f62cecc094dfb34c9b9c74de8a3c41ccc338658864513da888d31c4b44c6871e63ae7dc944eac144ca897e7e393b8dffe4e5f422b490675a725cf7e344be8a595736967395382be128afefa64a1eae2564aa18416468385f8be749ae94968bdb1c42bfdd141fd48126af80997e2999cfa3b13094d3a4d6d6382362569112434bfcda6c35ce611faf5954a88234c1742c5aa342a25a4112584113a74245f2c421113084944044210716301218770de31b2e386488821141052081f39701a302084103e3c470e1e186040c82030441035420251801040fc2101217eb87180903e4420840f28640f21217ab8918307066884e4c1870fd7211282070384dcc10e0708a90318211840081d1e1032871322871feee3461c6e2020040e3f7c8ce878030342dca08090362020840d387c24381a10b2060784a841022169981182861cec8d3398a10c3ec020c3c8cb08198318b8022161f01e619110307c0102215e78404817768ce4e01181102e846c814688167cf418f9e163a403202159b8410e1e18e8e134e831b202068460c1291072851c3c668458a10a3542a8e023070f0c3420640a52f0b18090282420040a399811f2041f32429c3023a409040861020d7844c7014296e005085182004292a0ec0837fb947749522624a89d4b10f3163f4750636c3f29269d72e56cbcc71508428ca008ade7d9db4614f43049042db8f1454811b4fe3031094a9874a72c2144d02b864ba7c424c6bb7221435053880ca6c438b252082142b82ae74f76334111b4e0460f4282a0a7ec0c32da0481a0c8ad6dcba23d8f92319fb88ec483901fe8169ff4b7999d9ca10ff181da9b4909dfa0ee85fe1aac64f39452cb563a7673ecb8918c00c20b353f637adfcc17e7cc5d28bf6757e9f45d07cfb92080e842134cdcfc4fa2930bad5a94971c93aa0d73d9710307ef487a6c550d147020478f1f3778f8f001082e948f29bca60a9d7763fe169a870813c284c7246cb7855679fb1f766c949cf45a682af4a9aa4d7a2fd34d0bfde4c81c7a631273c7f42cb436d17c52d92b0bcd3a93cc2243b5273b8985fedf713b097e9f354e60a1998eefedf8a33509df2bf4927ae45d083deac473857a49e5944975d989d8b44211facd24b1db58a1e67f73b3985cc4696d15da9bfacc1bada942efb6af1b1574dc184a85a2174f2cc9456997e951a1c9cc31c8cf5f82de703985221f37e265769f2336856e6eb2c8c8709de2684ba16752fa8269119fed5b5268212e7897fef6dc95c5cdd36871f9a444149a987b92ec736ed74d120a2d5b0a1f42971028b4a0e3c9cbb8be85ff094d8c9554cc524a558c7b42afcf8a59e64b5f077542b90db7259b1c73722839a15696f925f9839fd272137a92ad245c529a9729a9097dc455c89994529cf16442d70df32763f7557e0713fa5e7c3993bd3f5fe8127aac5026f97789418b67092d466b0e5bc1249584570945571e7dadb3d849779450540621468f2c7d7e32935084b02a5355b2e2e94d128ae9b07757c2c228218b84dea9c2684eb290504cca9d739217da96aa47e895ff317492fc74ec38424f5adac7e37fbd5605a4117ae6247e5bee0b27c6082d7d5ef0cd7862ef912fbc474490d87b844b6ddff01c3d7ea4085a70e302208b50e4ba9389951fff4a0cb3a077f8098c0d52116523116aced667ffbb51b9734468594e9665d13dcae37d08f54d2a294bbb089d1d0da1081372f129e3e62c2984b6592deae492648aaf0e211425efe7a67c131c800c4293bfdb2a36fee960158820f4f4268fffc504428d6f71728e38296605d9b081c347820310fae934275e09ee49c7fb07cde3d3ec75ee1b31fa41ef124a8ab1c4756bde3e2489d212258e1f3b0c1f4a4a7fb860a5754f6838b272b0374ac900640f7a99b4d9e289d224b72f12e500440fc7cd31d4ff7c32258eac1a2400418d1c02481ef6132d154a5ee57064f9e831b2011ce4f81f1cb861c3068f14e8486c3002869b60c7084860822f4e00d60082072df5c86cf2e71277211f207730103b249790ed41c60152072d499933b38df8c9325900840e5aac2fb92a1d698c95ed8e34da31a312fa99c536fba083501a836c9841094d8965f5659ba48310c291c563e4878feb61d63063129a24c74a928f093349d4304312fa5f92d118c3e47023c69135831d397eb0f3c081a3f2861991d04bb2114206a56297e6cd496640a26c1c13cc7844096638c21f0b42e67f3ddd98d188198c281d3316a16df8e9e624844a1d4f7064f5b8b16344115a5c8ad5a77465f37d80c138b88186198950c62d477f78539d7d4684f659cf928a2ffab74f0ea185373967ec5e5efa922134693c9dc526332f259e422816a7f93e7d5092d6f908a18689bb7af37751791c842685ad2ba94799c9bddd3304a1cda8f6cdef49c593c47768126604421d1f3d42eff38c550c106a752c19de22df6e7bfb832e724176d55c75f61c8eace30146f2c3878391c39c30c30f7a0e26ffde44449b3cf5413bb71b4f928e30830f9ab9c98f1d25bfee896fa4cdd8c331068d9952652a0bd6413c430f5a188f2987af1c0fccc8837e55f264c778d9b267061ef44cfff69bd96325569971072de5932544f676d0bf5d2f8941d8898bd7410bea3f67b3183463a6837ef2b493f0497a93da39e8a6217ec47e2f07cde2a974b22a1f074d49a9840f11257f901d0e5ab565d17163c34cf437a861fc7afc43878ccfdda0496249496ad3bc0dea98b527398c901f8bb3418b318c78977b78d0f41a347dd38a2bd10b66b51af4b2fecd1afa171e4f8316a676b33ebb68d04a9f9cb9b47dfb09da3368b2155bb69d19f4dfb025277d1f3ea9b60cfaa994e13bece5d12f193419971b2ed78e38e5183471e37f98d34a661e31682a7858270f933068e26f54b9a8246050f3257562684a9f4696bea0c97c25530a2b7941933663d5bfb9b799a80beaa63eabb13a416889b8a0684e6209febe6dc9425bd0449be0f6be4950ad6b412dcf1f3c8307cba4ce2ca841a58d21f245c68f492ca86153ce3361dcb4c4f20a5a329dfd35894f4abea41534dba045e9511b5e7a15d42bd93022d40913642a68e299ca29c98e915b3a53d0ba5cc4e7ba6a2c74a4a007a1a537dbc84441cbcacea5842ec96376a0a067adb11226c70977e3137493359db21a11154cd009ca8b6a934a99b0095a2a6d39f9e8e9b91099a0de99e778c2a7ccdbe01294374d1b4eb2787a62a704bdf43f6cdc14b3687549d03cb79cc76fcb265c0e09fa96e4c9f63376de6f8ea0c89361495f4ed7a61b23e8c1bcfda47011143b8fcb6d82f0bd96087ad09c7385097f4bca21e82264aeb11cbf84d0118226059b1d79b90fb7982068bb5f234ad05def0341d1b145654b79de74fa077a922f6c4efa7929c5337ca0982849d6a912eb853e72949d249dec9d4dc60be563124c4e72ea54776c17dabf85e5fafe8b3d9a2e34b9840a2d71366f36960b4dd0c9eaad93185449315ce8b5a97cb369cca14bea169a6455a764d7166ace774e165c2e4f580b4d8e38d9cc0493cb52450b4d9e6ebd3a9d472f3d0b65ccf7bf4653fc14cb42bdcc23375bcab261120b4dfc2e07ad8ce1e207165a8a99bab4c39db0ec15ba9ec69c6496fc217785a235a762b8ca1012d00afd62948e999b8395b88b90005668257c7ef68a9fddfa2ab42c63baee9224890422b061830448042db8f12301aad04d89b3394c38155ac793249d41b3466850a1c9ef687e0bb714db9f421384c688d6daccfa9ae2ca702756c7949742fd0da7f4f387149a97aebdac31c6f7d7a3d0a4204c7fe598b29d49149a6cf3b4593a9edc16874293e1bac49dbbf8d94940a15e12374f09a22d63d22774b35c428e9ed331959ed044846e9edbf2f0c1d4092dc3cb292194f8fd9a137a87ef78e2e41832bb9b5093e42698590ae2bf26d424348fac3862428632a1c91cbfebed4dd0a7c484a667fb4edc903d1b73095d932629748caf4d62c812eae8abd39354291dcb4a28b293d0b12ca7b8cf0e2574d161a595db9b8496414c94969124d49493c6a046cf98de98486c7db2894142d13e717b8496afa418324b786ddb119a8e3031c9f8cd1bb711caa864736727fc2fa518a156ba1894a758d52e971d0958842698a5b0a94f8ad03c5e4ac224e194dfe889d08498949f9d468426c89279bc663e849a239bd4e5e6cbcf0da157dc8c31cb6ed2a9c34228974cebfb7674aada08a1de59577e53ed3907550206a15bca783ff63196ac5e109a29b1b537c5986413f64068b245954ac265125ed380d0ff7f932cfd41718d493ef95e9377c90f8a8e9bd2ee39585e0cf7413b99fb6327311ff4373b4f9bc23d68a5c4245d923fc5b14e0fe6ec77f69dcae641cf9952f6a4743ce8a553f0eb1753f9e13b68f9ba52669d9ef4257650b3694ac2ff7f66c7923a184a8e25897739871fe1f891634ddd48001db49cc44ca61c47dcc473d02c276da52f891c344b4927d73a495292a01407e5ff94e0a026716c7f2f36492627bd41dd39b919f12508d9233728174f7ecfbd501b14b5a53fc33e095adf6483a2e5e4fa9c8227a14db906ed64c7cbec9cb3767dd4a06c9ee6934ad025a720a641d730c1e4913188064d36d9d8a0a772c7cc9d41b30bcf0b9a54d2261b3328f2ec72ff32883b212b837e498e79f30f611726832684be6066a71dd4a8c6a0e96062fa6352c11d233abeb0612337016250b67ce7820eed79e40e83362774dc7c379df2de0480417f8ba1ad4efc0b5a0effcf1ce4cb9cb85ed0c4bd7069948dce25ef82fa3d2aa73fedb275422e68a12d660c262b85cdde1634f74b9b93b6926465b4a0e909153fa524b2c93231484016f44d15c455ba3c16d40d5ddd2332c3c4daafa0c91bb48fcf5fa7206e2b2832b2c1ccdac2c9555093bcaa198b2f6993980aca2529a93c69be36944c41cba5b64e127b4f5097958226293965a65c924441eff132135d92a0806eda11b921db2768df9682de305ec27cc9098abe503a7b2aa59117d404e5332bc70695e674944cd03a336b77bc7f4dca5c821aab92149abe5482f619ba24931f93f49f044d9b684d39c8aa127f24a8d94d3bf7fd7f4a82ce11b4249f97ce255db992c7087ae56c3a164fbe7dee14411383face110b1693e61041139358b28c9f5071461c8296c13a3c650b85a057e78e977c5b39c8170445775d503949a7ba6420a8b136b7e4f67c5207fd009539967c429d26013ed0ed2441a5e4415bee8ae985764a8f0621175e282707b74c25c5b4a9b35d68a72d5c073bb55592ba5073c96e494ee28f8f4c2ef4da5ccadc2b949658c9c085e2b22979f2fe4d95958c5b285a5f744a952c7cf08b0c5b28b23905937f628f901735c8a88516ac24e1bb5c941021740119b4506e940cfab66983e93d0b4dccf8c83ecf14eb3e5968a66f4e89a50d8eac630119b15047334b598c5fd22c2cf4d14966ce9a3d6bcf7e85a2e775bf4eb8309a94aed04aa8bbd441e79875a715fa49d9a4bdb0b1b5c302c68d1d39921fdea314b242d9986fcfe4f555e85aa6e6ddae4d2cb755a17e86cdb4f14ff7254985f6ae95b3092d41859adbe4a04d104fa1bd7e1eb1db30696c630ac504537ae4550ae59352e22461430a35ec9496effbc9b65a2063148acca5831a954c5909a215c81085e2e6a6735e12a6849e8442cf7143a83f75cacc64648042dfa4a438e13447e8cf2712c332999262b8f0a799270a157c4c448b230bcf0e647402d1400627b4f8023974f88f1c386e30073dc5b4298986f8e8a493837682de9d8e7cb951c72193dfb32363787050de3da96465311a6f50b38406792989dca0b8c5b6923cfb9879b20dbac5e592c3a72094f86c507393cafcebbcd9caed1dc90d543aa0b1062da70fcb63b93f1f94d4a0f5f66fde24deddbe491af41b39569e3288d80c1ab47793cfebca847bb1cfa06ef0f1dcc1524aa3c49841dd7fd72c11d1942cc98d3a8ba051062db3e23f633f932ecb87839188c0d48d0a040d32684a9c3331c6207325d363d0f7cf84ef28f5f01ba6470fbfc1230123eb0234c4a0e7fb92c27826d1c1ac30e839fe8cced26252ce1318b44d822633415f8dcaf9b9028d2f68a2fcb7a4515692e0392bd0f082265be5b5c9f0ca40a30bba6ed6745662e4eecc3b5cd04b8ef915e3ec87c616b42bb55fc292a44fa8b1c240430b6ace494425d9f3c4895216d46457165e5ac7d2854e40030b8a5d50c2f8c6cf545dc681c6158c8615d40ce11e26d0a842e9c4dc7d19c6913e72a423d0a0822654787ec7fc7064dd701d33d891c347190f684c4193e9c4bc15962d576e181168484193b56ff1fe047541230aeafd8c987c2dca53071a50d03447afc297491b667a82268cf8ff64a3366f0ae9487c8cf0e0020d27e8364af2d29e2429e5679ba0de9e0eb541bc62de4cd045db6989490c9fe458db1b682c41b78d5f328bce589d9494a05e7bd89c6e5bc692fccc814612b493e56385d83ff165773ad04082324ae87810fa11b4fc939412b46d030d2368d556aa24993c455063b2aacefdd9a674308c113030d01e3062a04104cd4d8ebb2054d0bd7169c0232018829e2975886d10e3ef498560a0210465b4fbc59445739624bb1a01168d2068ba55c1fa24a51dfa03c113443e46433c347e90bca9fadb3a5ebc638707bea871a3060a4050a368f840f3924678295197356370649df7f01d366cf858fc83482fd4ea707a7eb2c70a4ae485b64169124df983c7be204264178a9631cb79fe614cc9db1e4474a1c61d13339f9d3ae4960b4db8ff70620a9627639ff820820b45868961a9c44b723fdee293213a496ee2c81f232326f01f232228c303115b68412bce2cd5c8ef367164e5a0878751835a684afb644a92f6b528d960a4070f1c81082d1479794ddf39c746a5e0c8ca1c22b3d0f4fffee5fad8264fb7cad1030533d851acac4044168ac921cc93944f495fb3482cb4a033f65472cd16fb24020bb553a514c3dfc7d75c0a22afd0ea4aec2c49363942f33088b842392574d55ffaf3ac39d60a4dcf7f6326995366f722acd09418df545275d7179bc82af4934cde2a0f23f25e52857ec9c5d49724c9a5ffd3059154682925cf6d9795843b2954689a83fdd8e8d315a2738a94d4b59df4c92b49396e60cc98640a4d5d0a8f2f6d954bff31a2021dc98f1114d4f8a2868f1ce8d0919442f1a04a744cf2a4d08250b22589dd6d6f3f0abda4ace17305d7b39645a1e80e22348b1b0a3dc6f7987ab4575d0a02855e26c43c2e66372b3fa1d94942d9dbc88de31d4f283fb715cf53d7092f3f6486ccdf38a17fdc701d2bde832ce94d68a7c424cfe8eb5c357a4d68724adcf426e68fbb3a138aeacace999d6315c498d0ce669387123fc6c7b8c825b41c3c67c5d767bf8f2da105132ad397d8754ab812baa8cc8bd3ba4ae13d9450b4537fd2935fe4c571129a077db1d2bfa8d3614928eac44cb15f772a099a4868d9f143ffc8f064260512493a8c98bbfaea72f0830718da3b727ca0c617353c0c1d890a6a7c512307fe8146a8bc20f208ed925516d7e42dbe7147e825979426b1d00e228dd094991cf1e7e92ab4ce08fd622cab2d290917165e841a94a62a9341c91c4414a157dcc6d820dcc418df4722b4fca43d65b16041891f115a79d073ae733984baed77a77233ffb7c8108a264ba599820aa19e26cd745a9384d082924bfcc87110ba65ffdddb174f721205a1ec27a5c3c92313087dc3644ee94bee181d40e8c9c42c95836566d7ec0feaa6fa12762fdcee587ed05476cbee1a2b227d504f25db50d93b6609973588f0418df3b02e11e149d01d8e2cac81020e341a44f6a09c4e4a86e59631f144e731821a5fd4e031d243033b4650e38b1a19b061430f5a6ecd22724ff897e57164f918493550c0011b360c1944f2a09cb79e0c3bbbf1438607adb2e4741a44eea0ff5f882729a72d13da413ddd72d7bdf184cbba0e7ae7cd24349e69e7c574d0468f2ed5a9626f3ca139e8314e6f0efbf3d9cd23074d535e8a3b715c0dd4f8a20609dc06236058a941240eea9879e7dbde24efac1b3a92ee24ad0c2270d0339f66bb8c77b2f8e70d8a2a25336ed0c3a8ec254e4bc6fc961644daa0271584d55e26ab9c713668e29a2c267163d757bd06b5aa34a6b34de14cb646440d7a7e0db29318f3cb09118eacdee1c69840240d8a52594b7bccd0b129354283e48795590b44d0a0987caa54976e88926bd5800d1b366c5c9d415359dc32094a689635d261f818e9307c9841f77caf5562f09c41a40c8af789b9254ac4850ff7c061c3860819b424f9b999b88c75e2c70a22635053d2b1b92fe5cc41867064f918e161b820220645e6a42e4e949c3d69587b0b2261d064ebbc9719b3fe6512e7f0b123c7221b44c0a0e59f580c2ae87ba592c817b4136e43a8fc4ac40beab69b92245562c8ac1dc408912e28ef1abfa4ca301c44b8a05dd2399e8c28d11ab41fc710225b504f763139c85538ddb17c94d28236e697b4286d725031dd238c1fc98d248864c13f51a344369f902082052da9ac983fc959534e572a82c815b47416540ab2f26be671c70c729c20044610b1826e25b9c711bfef29b68410a982729784b608cb53414b499d943c743261df0a84c814f4ccf2d85e2e16df495677109182e295b9597ef2547c3f0a9a9864386933f42971311414cfbaa727681df43a4686ffdc25e904bd3d67d9044d8f0e9eb5043d35b2668222536cae3ccab2681b97a058da13222fcb4c259b0a224a50bb933621771ff3f44624095a097ecae2e5bcdba20409da88c5c5af125ef425891c41711346948aa134840c1231823e7ea6a12a43bfe90de540a408ea57779c2496799ff644d0458c903fa2a487a076fd9d78d85908ea9f12de20a81d3f89db9c4d2953920041f9fc71caf265c473d00f3449653149dca0880f94cb7faa82c6ceac782ff494bf7b5fcf753f7bbcd0629f2468cacf4a5a5246464880bbd0435c12aec43179e92fbad0d39bd020535d9b52995c28d6994b86fe1617ea770942278b99339df88c5b68d226e1b4492fca3f9966d842b3f22f0b2d622df4785d735bfe9b213c38b28c47d26305366cd04213af4b7b94269985163f552e9d64076161862cd44da3824a9ff4b69e5b8519b1d06eb38df7cdb6553ec1424f2e3a7ee4c9afd0e382ef88f5e90a335ca15ce9cb964cc6d7aea45668a5e95a4fb6850a3358a1872f492f09d9599e3b1466ac427993b594ec3e69c20c55284a0795bb94797d70532a342f255d7c0925c9c89425cc4085163ad678978af313944a98710a4d186bdbbcd211334ca1cc5c296d76b1ade2efe1355000821add6384dd04c804624629741394c8519dc3eb9c1029b41bdd24f39b280d3346a1e906ad11323a6698210addf2c40eeac6467672c13023147a923166bb0cd3f1abe4c20c5068aaf4840cbb4f08667c42d3174b5912764c5737cc6aa080036dc346f200a3cd0c4f6829976eb1badeebf3744253b1c1736e903562c43338a1c66cf71a36df6383199b5093126d23229b478ef07eccd084a23aeef69618c6919583cff1636487598f199950f355fd58e8cc6d9e2f42f0458d15b4610626124f50713f9d97ca30e312bab9f8a7f6f976b31c1c39624aad4e30c3127a028c02a8b44f6542692c108983c150401000902967020314000018141a14064391501487b1b27d1480035024264834321a241e16141a140bc6815020100807c26050200c0a8401a140483c160514c51f2a0b03b21b2907336d4d86b1b3357316ff74bc3a89be68d11babb4fc3139e8b6ed9afb5377d70b290a7d1e78112f0e279636b09344c54a306e772c682751ffae301657f81a6bc2f6c02dad2a7252aa095e98c3de30d44d65925d58dc8144291b9ee629cb93487625f78486ad8e3d968c5e31ceb8dd112247166988216601ab2caee188600e0f956ece9ebc32a66054eb9a2be905dea7190e1b30a907f7713755db4fae420700e5638065575554f83958b3cac301847e96937039688df8d0e8ea6c2601f9da195a50c0d5c9bc39e422460dac55f1ed92207e79da3df21f25489c60cf81a3c4aac0e235bb965a04ca965bdc2833078a6b52ca50dc4c5ca93ab323b68b56732c0909d2846fe41be491912a5a31dd8607241d4eebcac1fae35caf6cfdaa917a83ba46d67036aa7f082f3647e101b3995a35b0d4408e51287333e97c85a87a23e86dfd3c5daee58d18985b6a013094eaee226b1b015c798bd426f1c5802047ff5827a5476ca9f178553fcbaae770fb30a8c1eff39edbe39b4eb3e2e4cd856ef2032dcbce2db8f1c1c751f369a6e66535ad76561a0d8d58388669d5b7de337cd92d3df4a80f4304e0a98dfa5ce08e58ef71d997a3e25eaf86b7ada391e9ba8b5211c3667ca574a8e4511d0238adb3e12c491970e0e32e0c0ef91f81de1d04a9031c79c3016ece8eb604c5141cc101861c2f0721a526d81b5cdc6c31ed2f54f251f27d6008bedee7decb2ef955908ae1cb0422a7465c2fbcb1214452a75a1c46e76d3c894818b12c4940620e50d5cef6710073566e967fedcb580304eb438e61d6c2e5609cc72968da8a0414aa072dd63632d97c25a462f8c4cf0a0cb68d67e0df97981b2208d983a105635546f26b2411aeb96b4387975bb0fa4b0b0ba0925dfc1cb8e4f871c8aa3cba8c78b0bcd906b41ea0b1b09472edd069933016070e3a7cce5835facdef99f242997e75c422344765b2fa015f9807cbac0d14c4e80b456a0bed96057194da3129d4b1d6e81b59f32347933b4cf1fc927098fda6a62ad940db909df320758e2c2cce080f2f434b3ab662a87a53d8e5fc48355967070115952a5391576774bfd5d64219e0dc8071a40abe439731ff72cd60b3710ab439079d8f5bfd57e5f2cd2cbed9036c3112f2c7e004fe2418b55ddb66e8afe54d69d97adcf343f4e61b6ede9e402925f9e1af7e1f5ee5e50c9c1852e653077e4694f73846600fb4f3ffd195fbc486fe395c8631a888c5dc862b449f2762c4aed3e6c496974ce265474f66231cf55780acae835de591e5c9c987d87a12c43703fd71e5ff03ce9c86ffa10cc0870da0184f4f1e93f2a252852d16bcd986eda0cbd6ffc0ac60ec0d32488a01712b2905ac750047e67a486dce893dcc7088d1c77624c955413b009caa123fb2168877d6970298743958d22edfea9626b0884450f260f1440b3d2d076bf91fea0c99d9bb8b53f7360d9ccb1dfc10627452edd4a94fe9840ca0716bc0421973945b8fd3c1a2557413a29db4c68d8963b45a3b251026d98fdec07499f72c4ede494bca1af83d7ab51e2f4301015102c4625390624da23ffa962c9e7d68c268394cc38d88020290487ffea81c3aaab90a2def116c0cfcea0bac18c213a0b1986d56f751750576cb468ea5017896fa9622954e285c476cfe3c3c51cae0c7a0fb6e81c166a061eff73283947493eea8877b3b21540959c9261fcbee5651797a68d78805913787cd19d84adba56a87aa6c9a1af312675670a838ff30e109e109be67db8a8041300b52e995173950849e12d9043d9c180724348da84052c746520dd18e455c6a9a21ca69c3f5393cc45ce943fc99fea851e49748e989c47027260374a81194493c885479a41d4932455279e32bbce50994f219373859fd4f781d36843fe59e7f4b52fef4f9d24d54921fc5e7dc9adb5ad502bc6e0eb284e9d88fe61c04e03901353d87e46bea3f27c71232847efaf58102c357ef42e6c2f907be8706c9ec44e295387f4951624656f773d064103fd9da6240269489245dae4e4f1723c6acca88aa3ac183bbada800a35d037c0d6eb334e2c57bb0855a7c699109495b5eac8b25dbfe98990f89178037f53e5ae8bbc596fa7f083c1a00105f84335264afa8774c3786615e5e1de817c44438e9f5f48e2ba203a4960f9f2e2a22347932c78d1675553b3f22a7d3a50cf7de2b39d1a345c3246c52b31518b12f00435ae20e65f80d13b44dcdb6837c6dcf97615c6106f793f549defaa95ef4f2d58b474b499bdc36e55f06cbb8f3996b0e560fb93bb777adf6ccb3f5af9bc8a2980ebd136064e51433b0af367a6b90ed417f5164c836ffcbf865c4c804e26f6108ce03237a924f9402f78cf8bd60d5e43a32e35e88d3d4263c9b9c36e9be996559a6b5bc7a6abf496280a38804dbf933312b64ad1791889b3396c25e3b81490ccc0550a9c190b5dc4e7eb20ba6764f9fad973d7e91ecc631b41bcb21f85b8cd01ebb60a3e9b285c5883e427da7db511eecf1d8d28466a635e28916614062901385fcb0ae8a9854d91b24d2cc701b86aadb80daef95da16f6abd15809733cee3378082e11d8b222f9351ef98a0ed9c1ed5e3e550c6cc201d8d80c2b6e304ec3a6de951cf812623f59b935160785de776820e35e363a8b22c3b2b6f08a7e6f56e8f2c2230f9b70553723ba693761d03273605da017bf9b41a21e68bf89886504cb77471404d6bfc046e752506fc16c57d0a433612496883504ca5da0c81514a2d6fff03ca0569e92bdca28390710af7e80a7e21602c8002bcf8f2a038511b708f80cf1e6be3f80ded122e082ce4e3ac0874da9e981b0a8eab198ee8938318f3e354006291945009a5e7a0b03a70ceef3ff9cf96aee7b5d937b84ac0e4f63891442ec4e5dae090804cc930a6d65943b72015cbeda2e47f8845c07277d1154dd4db9024fcfed6bb3860061272e21e1b2042745694c6f344d5309f204e006191207bc5693415b9ab550828e43fb586f1d42e0eda887f86eb07db352f56223a0896b2749ab01b5ea6920052f39132ed15ca4cea1a343a1f2170da464437b1b819620cb398589a6c352667954007f135b8a18f8fa1593cd0b2601a3e1a8a5048375a8082eb1d7a93c706e069f25533a1621d46953735cfd8e4f456ddffa0474ea1eb534df3eb1816168b2aaed4f8d625842d40af1e5e17b1a4b43b44e099e2d71502b480727b70c4711dc41aae6a2f143039415868ab8c3d8d7b46f542643b0cb30e4cfb04add480d9454e025e401bdfd26883a78776480d78ee463789022240248b7c84b3d5dff9ddfb939d13833fc8899746145fa8d9d405fe924ebd867ff8ad6cfc6dabd12c9e8606aab6f84348e3d76c8c790b9b004b390fd0483c3bbf1ed8c98058b1a9a05533083e45db01012a135f6cf1cf16d3e1fe92cc4f38b9e05524f4cbb2cb370fe42252f6c00980dae4d8831f0f754e68eb8c27377b00cee4e6683736259a94f2b06fc95761397ed241820995c236f7cba120170aad24e8b3da4958f3ca56d87274f075367221bba823c7cc95f15a672d8049040e1ea4c45b8c6731010b5cf47f0830059862af2f7a93bac90e61a12e0ac42c76bdfe9b79941645d7126a8531e3b96521bce1452a1f409130105072cf7a94f081579154704b5489a0679b382daf6b79630b495a156a82ae61413e22bdf7bc86822b30e1facf24670bb4de644fb48e14d9d8094ed02cf689334d73601ac498aeb4aca97131a7846ecd02b11a0301b0bb2d7556162a2cd728a721dad32f55135df965955f1ca31e99682093cbe56870c66666f3144247571c3e8f8142fed0ef74fc021fe823f88c7c3a29762be820917a41519ae5baa25c21ec369546af336ca86caf177a86c24003b02cb983df6484c6bdc1c32fac341856c1882318ea3d835813e2a47fe4eb93346090536889c11f616682154343420a9180de08f5abe4bb8d1ee23c8f3a358a53f6006655a24acbcbba2fc546a66f0649a8c5a0cb49a33b7504ce804c343a6bf715871b8096144bb7aa3a4328b9424bc0dc869b271defcd0f451d411c50c07ec40fe686761a00b9521e198556f2258a65191590e1fb8be71625330032b56d7815561768c89ad6aae70ad74aadbdc7c3c8251c85abe1970ce8db9eb7e382231e6100f542cf545f49315c6461000df04ad13f8ceada5a60b3e0b0b383ef6d9e38669201d2dd43d8f25f180c7e801327dd3d46b029abb31fe2fe72187740f2689eca214d6849a4721a1fde78c44373124d0bbd7fe0734a734f3ccfc1d68bedd02e943cad8200d12808ddbed06fece959c12ffbd6e54fdf5525d10f1702dccecbf72d8e8b617c4a1b227419b853342cb4384e2d89331de7b3a113c2b82a9fb025f7289cbeced3df00e122073a54bb962eeeb96a335c74e6b6b7f912a390fd6f8c0865b1071eccd433ae65e97e6eebfa75fe93df789cd2e43d9ca38a42dc50a6ee6d963b243d563be9fc931b34db65b845cfc4f6e28147878d41aa8a19420a8fa1f0fe31f93787acdd7d3178e4c224e0fb32d16d2c8e1827a864bc26961482a234b2165a87bf250fb7ae53522c3743436c8981c20a87a8c0ee8e4394e2efc614070417a5860956c64ecf698692f0f26d587768fe6b2cbcddf13b7576b8b7e175e23654a90a2aba0398066d860aeb8d4cd100bdf8efe8b66deec63044db3b73fad48ac600c506a40e3e59bb8c0a39ab92800e6818597086141f19b534190d0d8e48d4a0872fd6ff29b8ad3464038e8d6e1eec39054de17c9a277b046644f46241b4eec20e19e97847b6585b673ac30d71c1f52915adca14f9179c0ec6fb8b91ce717701bf9b20a41bf741e84aa49b78fcffb48734356c29266b80f6b12044c6790456d981712c64ae1db4c8a24e6abeb55b6ccd5010651b4a2bee6b286ceca4820de3397193d31a38a46fc500de80c7a415358e30e2e1e33ea807e41a7a617d95aa194be2668a096348535d8b68a24aaeed299ebe8d6ab50190ed4192060d28a0391e1a318a5be28be4e82a84267f401e582464d43aaf56879341b4d20cd738d5643a0d11c7aa202041e61142c5a8dc6e4e812b334027a8f5aa264d16c3481e646a3d18a065dc28b42381bec1595b5973559ed011aa52def1a5d28030a56994151b3e835ba88fe4597d1fb04a5260466da51b58b2aefd2986be9d62a6bba6bbf6a09b68cbe0346411fd7e9df0571062125a99ef58d55aabe5a947cac81e0097e2cd4ebd01ea38c3bf4da0ca0af06ce497f1707db8916d0b8074545e6a305c19f27b3d20e726e88d58644473427b1a697918704b587091ff7a994081a455909dd246e4b8e971d918e6c8ed81d191d811e613b923b521db939221d64324d2c8390a8255c9e731d09ba46b54ccff29fd28102025cdef1b1c023b2eb9d5b3e3247604ba8831cd384a0b202beb88171ea474ad20319130e9827c3d7a3608835a2da67539b48ff269f018071c1f7d2847187382ea21ed918c39000890025339079a430cb97ab45580c50b5a3ac04eb11cd484c30d63e9c53a008e0e004b50a327b797a78110c95192725d0980325dfca16078412d295adf8737a586f98bf7550d680259a96b10db6083a11647c3a3e7c88a7dd16045802dc0c817fbff28d8213ec03fe079f803b3284e15f0920228254c0f2e099c37be2380714583a85558790c11a32861ecd314883415367431565fcdaa07859f4b84406af7d276fa2efc5539b6f59936164676267dac8e2d80d0fdd4b0ac1903e98d8a69b8d4d3629eb74e8a61a4e96dbcc3656151c35b89945020c3e16bf993beae5e457b241dbc6c7c6b68d1bef66304017b5a5a21b62cfc98553cf26459bae36d56c727933cc82357ec5ae0d41de74939306ce2c35f4abf05163426d86311a28811b6728371bcd390ece2cad9a7f35356b3203d3c611ade3071122985c9127ae0900538819d1906208608b491ec169a610984b8e2b9fe53a551c2b01027d957c543a496904df4b42638faca11de353f304f16f3a17a288bd2c903cfb16fe2243d0346f5a483041f0b593bd60cc27882f6cf6d8cbafb9e634684ad6a72efb69beafb781601c28772c11a7cbfbf194aa3f8950ad731d04b3021ec20284b720b312994f52a1d4f8985674d5b82c6cab4fdc084938d1e642e861418074bf1c5f552fa02a97e42bb0df345b6888225888318b756788d28b1f8b6a4be3ea530c3dc928eb45eac923fd079ea4f56cb424068d3c349a30a5cb3d20f9e158f1c11aede25b180c7e428a6bb58195e94a1fc7ef0492015948ab5d1596bb63076b8ebb0a7d67a9ab35350d850f002552f19bbcf180f11053cbea22158215772abe736529cb7b3ada6f04764ef0b815401168a1466bef78baf7cd7ace53b5982e95faae0a5180ff08003bc880f4610c63ea595497e38a2ce30c68ba7d1c90dccf0e51882b992430e1165604bcacf7793cc191b5cdef43b4e27730a323c106116d4581e7fd292abe7b82623d912a631e60ff5a00ba69017e379f9ab6a4f23b98e92f8b0a89a7c93df06dd606da70349c0bddd32000060897aef56ad683e4e88368d3a8e3acec301b22900a6eec7c984e62d943bfea7bc240d2e82e374a9fd216fc5fcd35846d4e170ac181842208f10ff99b141948219a4b16a20c765890a813ac1f13f5c0a12553ec4374e6d09237c282aec5c8b0626989f3f4d4284666fa3cbb1ac358b230a105fcb5511347fa44ad5d6a0c4158b7d496510535f3c9c20618c729bb1050a0addee42dc74a1c8ce7a995b38f301570583765499aa65912cfeb0ec332028ded76c942901b3542788651b9c3d536a5c148adf45e94e39ed638863ff98a1a19252bd1b5f84c08be303ce0a51af96ba388504c7097f14978e5f010b1481debb23723645b3fe367d8dfd06d5842187b980aacf31abdac5e9caf7b617484138e5eede8620c71c0242f7cf0263c41db3db3f7ece3200cb270814069d036f0d6f91e37cf49cccd63f6f46aef92d195326005597d9d0ba384c10b1e72c547b5d54de510560f0221030fda7441b9adf7980229f9f59ea347b17b1b01f69b34e52dfbf119c682e5bd9a2ebdebd3a5777dbaa8ae267a1b21daf3300b0fd22bebd579895e7c7f972e8b8d6b28deab77cd5bf4d6f7fbdd38620e25f5104dd8038cf14a79755e8ad7cb4bf37a7a51beb28455c264301e78134abf9600ca209b57c50be9de023da2a9e2e90367c3034ecc43f3e0c21518c88681a0c0f7e810bdd8d6418817b57758b537a7d9d8eac51b9d8d28bc3b82fed86bba3947b432fe8d4e7c08b1137548e50edec1fc66abc029bab4eecb9e056ee3f8c33b24c90a782254d3e050cdbcd479170e7d901a93e7164e07970cb325016104261c7d72ec8e134e0c8d7a0b88a3b233deedd18ed13a3a7d458d08633f06ed19fbd7ecd397d9b4ef6bc8fc06822596db0e396c582009a9a33cc19b77e4d0ada023b2602bfa6a12eddc9d837614693a137acebbb1cdf3e1d7cfdbe2d18ea99c644b93f46613aab4dd5e445c34f72620c5a5b97a6c5f3e91f7e910623bd7c69d0bad5509da3b7dcd2fdfd7defa5c8fa940e53e483d0a1707ee21ac053ac32706dfee3fa3d6b9b52430423c0c2e626718aab7db6271cfc7156e24dc3116c01606e27593e49daccfd337470fc7f5f5dbde4a91ab67df6c199661c8632646106c6f093feedb0e6af37bf7ed664004f5346e6ccd8e411cde467317c945e5834aa499ca716b6f9583f26b81f8937be724a2977d4f341e097cb84a54f37dd87cd25d43a3f73b4a544b3c880c8dacf858eca3bc303661f5f881774bfb46185d787c1cf2709e91799b61d6c5b300ee18c0b64a9a6b6192f15a3405017deeb2ba3986f33e02f848b7a5b1a2555dc7e7011ca2aa4aba0970d6139071e0671a8e9e665a43431c345428ad2f6be9248d720fafe77ac636617c1410980664a0181fe9aa0aea86d804a3cb191e3e2b9411ac8f02cfd9e3607333cb48776261f90278bc63513de7926c2e6c1542ba9278ffe52637e78161b219b091146222f28996ac7d40d693ea8d49716de5b444be99b5ac47be0d58d0a7ab5d5c03a8167e401199f5b39214a01fc665d150d5a89bd5bcb63d62b25dfa97492ca7d9447b4c2f09a600352573fae1dbbe7c20c068d0969c76975839e09b0088eb3506bd3713079d1f3238d418040556640ad8109cb4a0e532861f4ea376ad4164d1153ac40e6cb50fd57903e0a1fb0049099bdbcc308f3cdfa4c28a037930627175f643486f472bf70e5fb5c062a2864dd23c058104a50a262a1f4c10f0bc6a1d30d519725e29abe4b15a461c91afc81cb29026d9b03207ce3e12898845aa911224b693faeaabc4ea807c88b8b1215d195123a151424278f543da2051f02bc009a480e21f1bba1780398cddf1363d207af8b398a53845dc098bc25f614df8799827ce0857c282f0475812eec7a16cb114a1d20835c3fd893c3004e197530804869c735561020ee78ad890500ad81e62664638dc634cbaa25bd3daa63e4b2892d802d07fcc6acecfd2d12a69fdde7a75ef78218447a39d9386bb067a11c352389f6224f00e8826490b709777967a0abc9cc19078446423d598e54b0564080b9169a157cc2c025a4f6079b21876ffcad7c9e811c522cb8723209b43fb1dce718882b649398ce1220e1a4b7445e76d4373990a04986e76897bb8fe10d44cd7c7def0e014a20112bd323b0bb5893132180b105d812d8f634a43d068d86e886f78f284b843748f57ff17184852627f5d402adc092ad5f34016904dcf928bb9845f4de6b4c8344912c3c84b18e25d6222d2cba12d302a032c0c4b4f8bfea1461b26b19b503cce78714784c9b036df4a79561c334e10372f08a8e744820e7a21b90351da009eb44a7d95bb47dcaa7ff643da2592fc89445f43f8b5e30501d2d74ee4226d225b2fe799ce13dee84eb810bff4f4d4bf0a8441e694d6278123dc85f1bdb757ad96b4739d91221125090f8dc87ad8b6db19f77bf3787cf695a3a0f5dbe4d384a547d6086b364466c87c24ef118c04a4b475f55305d5f6591d685c167e3cf14e74410668d025da5c3cd1f5a458f4b5ae676fc9d395356fd0400883b4c00e4b8b12612ee18b94ba03f8d37e229384ad250821e93d825d9fd40b904628e5812e8611e83d6065b077abc7246df0f814da40d2e9626e4fdb4692a6babd72906eb5ecdb6b80b8d74392ad90d825bb64a5a8cdfe4ff2e470075dff711c781e05065d1f43aad017fa74974b2a72b0621e52e2d70db693a0cf3cfe59e2f3a6b0379e0dd50c592f1eaa4087c4d29a04d0f2ac20a56ddcee1b694a66580bf246c3b6ea4e3d6546a85c668c3da391ad888d4030f78df098fbfd09d6d76e5c3c4adf646ffe866f1d7bfb1e8a5050a6f8338f6583741b5454388e6f40311001bf510693f294bacf2a58a5188b22ebf827a95d5b2413bf4f320d3f12aacf676d8a3a21c21824d6464860f8120948b7717c49cfd65d3831485d939da9bb9b799d206fdc85ab497590c467ee71ab401fc74154477a0f33e2d9d89a0ef734584b1a04029c476b02e1cf48a881d87837b607b1e81a2e5a50400ef37eeef2df4bdf5fb513b42100687b529948b9cba8676a795acb347a25881913d35bff2505a26ac4b6fdbcc7f48ddff25b83056eb688e197de7d37368f655579074bfe228b98aad3c8087391e0aa0c60eb6892cd5cd7cf95ab90be7a99675e524c3f2d54b5cdb31473374af901c361a5ccc01334b5ffde371079eda096ed64f0b02cbe7afc993613a2a25ad8cbbc6cf06c1985b193f9ce4ff43ad02c4cb54c5aa406798290170e11c12fa81cc844da4e22d45b27e16fa93c41c64ba525e9318d6bda0f2eb679043555479b5553216d0eb40588836f79a04351454db044174d514f2befebc1b2456fed8a413852727d852fe8f1c93223976eded7fd13e34709802c08736d6dae109bbcf789240e6f156873c3212cb9feb1d1ae45f0f131658e607f6dd93d3ecebc08da3d001e20658e3199ffe3eeef560af2f118b50203da4e53208f3962554d0ccb47563e54113e0216097f28f431ee6f7ed49938a952d403671a60d44dd015a162076df240136b22a9fa6fda13fa39440680ca19539bccb8fe64f8ad5a696326047093130c2a0013ea282c1ec457a8b6137afb957785fe4269b950642449ded816325ce8b3d00da1a5fcd06c66e7ff6b9bd00721ca422c1c226b1177223a42a8b4d0dbde28f94393d0dd295482735482e510d22d74c818aa5ec72d470d870859e495a8be502a0d754a54bda3768aca50d4b1a2742acaad8e1401f83a944b79d089381b3a0266ba90fe6ea835454211653314bfa2e89128b74e961e4fb52000fec40379d6803516704dcdb8faf870459a1156fd61d50d8605c94ec018ec4a1e2be885ef55e3298c92e1698f983c2282c2f8090b77dabbd260815c03d92741e61f1996ffaf859c5cb227d0e96014e68cc9dc94bf8dcc3f256d478783d7bffb8178fb73c1dcb046f42cab28264fad098154a90c33f43c54c1961bb6206e7f818650f3ec6bbc778cc6cf964586a45af6de16f5208504c80375c5179ee9d24edfc0c5a8f139a3ebb064e588bfac2e980935b4b92e266ff1a3f44b108eac472bf6aa232c31d4658259e9e098ae22cb3b47708e6c3649b483334bfc00c731f264fb9280b515a1d55759a922e3716560ea4eb315c755d49a122a3199a0d7c457311fb0b02a05d5a07256f03aea6e12d0c4f1a563a5cc213a51f74e32d0fb9d2add8af5d45672c4639284c1c7c3e0eac0740f73948be82091cd8de125f55ceb550069d9f20e1705d70355322c880511c2a8177bd84e6f385fbaf9ab2cd096b3469c91b87180595a0c621b3561613a388cf265c51cb14b10824e6cc1ea331dcf8c07852644d06cea3ed078146229e01a721ecf62f6da953c4d05fee4d76b300bd235439e57041fc4dae95b0c52ce8c52677cd6da8a30fdd302d7ba4ff6b7ead59a05dd1fc52d726cd3701c4f4f395c8771085b8408a64ac526bbb49c71f03fa3a6e2225ac462d58dcda60be753000cd9b2b0949a17af0b50b9dfe083917431477764e18436fe3195a115fa701b50b1884c6e698b9be83469417c17affd3959e0e064367c74fa746b44d335c56cc6952773d4925051b3f90e979361d28eb55438dbc3eceadb60c0e01da1ada008117099a9b81527492554db9c3d1bba00207e94eb1328f59b2d61acd4a2e157f8cb440ae3dbba744cdc38435eae167df2da716308abf45742f809cdedf32d386d0bc9fea5bf723404efa203851768c60766bdd1acf9afbea9805f3182524a84c82af9cfa28032dfd49a5d553da6a75cb5b8dbdb26421877b35b3d75374258ddb1d2411bb113c3dd6e5592d2e40566c35c8aee449e1e1e1648a86d01ba135fcd4362bcfede1387f5f5c40400c76f3fbac0e76004c1dd15913797764d795dfcbe3377c15c8067b78e16625d582c27eb49c5dcb5c1b64699e3ebe49538cbc51751c3091ccdb936c81d94ce666ba609004d62a9a90d246efa0175afae4f6500e8f8801bb0eb738fc09f181235a362c4bca2a2a64f364f3ec486645dd802170b2939613efc1ed21a326a34f83306fd0afde51ea7fb5b769f05d30a0fea525bbd4cab691858caf236924f8c1130b04f7a62218b2edb12f3496fe11bb4355e4adff858a8568e1eb8b645a532d084beac38d253fce7f1547debb5c4b3fe22c2b4614fce0d7de157cf93344ba82d0b3af8c08a05916958b3c5116bf2d47ece637339b35108b50f4e5a8383b20f1e81f00f06f9a56b05be0b1c8eed162ac3d515d24edccedde11f36bf6b3a0b8a631f7259f2eb5fa25fa558d2bf74d9952aae8e8261138d2b6276c22a94f95daf66f9917d1af074940025fd080dc710d94390a5e61811b23f2cc2f903e62673932377eea58baf5434f10949f7f1284865da1cae6e4b91d065e204a0e1b32d60cf54420b650bead31b41b47249cd9fa3b9c5051d6d913bce6ca23c426e9bbf1caea335807ed66f98b8ab889db91733399da871f5dbddcdc5a0162e6d07381e72a08eb505cee45c9697cb002335f5524297f292c6854fe4a64ad1c2c7a27fd4a49130279efec07b34235b922bfd158d435fbe92e6db700fbd359a8154620c66da75148dc17d00f24e282dbd2cfe05a4bddbd2170bb3f4c9d64fb3704f90694577618ede99d55d1cb38f360a6bc3a5d2295049f105a2be2f29786cc4b870f551ac38c9dfead41a6922bd2298632c207d48ac4902aaf8b3a13860b46439330f0f0f0f0f0f0f8f1c1ddd08a98d7c02a932c924c9921e4b2db325c994529229929ece70b1db6e7b98f87466e2d39989182801570a080a280a17f8303474e4809166a26e2953144d29d9c08d2e68a8a02390451635be2a081d38e0e2b7588aa7534c210483c60d5233504001a3808e1ba0bc374a0e2946f3aac8205d74d880b5329dcfd2422899ba7340470d8c1d94a753daf7633ed180cbd11de1173972f067c0f78996c8abb6f823197032c6f2bced343aa463c09f8a9713bc279592c180df9ccf36d70675bce0d815ad72f24f50d976b8802f99225570512bd80c9ed39584104735c30ade2fe6a9fd2b1399e52a98a86d6d61b5c93eaa6043ca2a9a49c454a4226a568d4a4a79083850c1f6a6978b5bffc4ef148c2acd0e4956670a5ef285124169a560d47b98caa7eda96291821d153f988d7dbe9846c1bdad6a5e2a192dc78882b5bbcabd8e9f739da1605762cceda9645249231ca060a36d7a499a193260d4a091bae0c260c0460d1a0bb05183c61618b8097c004600eec0f10936fdf77fd0bce9a17749048727f8f8dde29a8400b8034727b89c16d4c4ada0b2bb3c46707082cda9abf27ecf543c15ec04c726d8aa9821249def1945706882916ab23e4b73cd049f7fbcb2a4c5f4acc9220b0e4c307afd3de531cb2dd3f3392ec1e6c7184ccbb625a64d2dc159eedc9543568db89bb2858c1b5d7880a3125ce8cc7a42588b7fdc500b1c94e0947a9c1c6a524f3312438b548163125ca54922b783d28edb9004eb163bb3edefa88d158e48f0a9f5b63e4c24758cd210c18d2e68c0a8f13752c34083020724f878a5be15477f3fe378049bb9d61fd404339d238ee042a307193a6769897a0cad1b5dd06804fb93640427a6ff5490da496457091c8ba83cdf95adc99db41b4570e9e2394410c5d0328e4470d953fceeba1a1d6318434b91c0810846e46bc81aa24f57d0cf98d185081c8760dc93a8c81579b4c645096ac42041e03004a35450d513c1dc7d5365078e42b0d71eb207b51b4465afe6c04108ae64dccb5b7d1a049fa74a87be5504c16e578974fa59154d1308f6c2f3f37e4d23c9910310ecb725939d6df9037b3a44d39e34d7a75f86c30f6cdfe9cd1aa28e068e3ef09d22f4248d9f4608720e3eb06d3adaa592b68bba468cdb03bf9de9441051a52c1394006d0f38f4c0e56071dc425f478fa12f85230f4cf0a473da1b2162d03c2a78e0bb94da0a7529875111f8d0e2e3021f5a7c183b04c71d38ff18f3e5c8bb61a27124e3de97dc8ec05107768428193c530e071d38213c83c83a5f060c159835c71c185d13dd89571939701ed2f8e65ae7ac93230e65652eebce9b02074e7889c6985a938a5831b462dc9081827c031b5abd4ae888ea2538dcc04591f94e780afaa795bd4a70b481cba05793965c95bb3f071bf837afcd50429d8813df82630d9ce9ac5b5adb520c9d188d34438601800b1c6ae052c8a64b94085a3770a4810b7a54a6ceff914e9d0a00c2c081063e8fae47bcfab51c2d05709c81c9e693f479997cd30b963a7098810d2962869d5ab19c3d1fe02803d79bd38a05e5a71d1978d5094945edd06cb9bf81630cec9a6a939c83921838695bc254ed29256eb181230c5c85baaad20ea5e612303041866eb06a0bc172b4c1f10546994aba3d66bcc0485272ff745c4f9dfa337074812f1d6b3b948edc1282830bbc46494166beb6cb169d2038b6c0654b9a18e358b01399f4038716783f699f357c93f0af141b37686c6103f9c0910576f3e64cafd71659afc38a040716d8d7fbcff5bdae9dd457d8f35269f00e8db102a3f4c9bdfb8db4215e4c1558d72dfd112de94dd24205b693fa50b5496bc8949f029b528a5e5eb514f8ea50952d88769ed24781093945f95824ad9322287031888892495350efe92774193a98040e2770ca835a3da1fb44594a1318252d620a0ff5d7baa98183098c8fc85d00c1c0b10426779f9b6a532b7d1d871218c99a9abd429f042ea9bd9cea2ca68d7f41021f3ba61c8356ce14f4b6058e23b099293fe7efbf0b5293050e2370327a8a9147d2c5d46963058e22f05b23714dc7dd98f12702ab6642ee8f09e521a22130d93ce6bdd15a086cf2fd72ebecaec011042606ffece64135e8242070a6624bb677d30fb8d54ea50f586b8ba555e47fdcba078c08f9dfa45eefa9c703ce6c4b56b2242a16eb1db031cdbd5c2c6796a80e585b0da52b5a1e8f174281a402470e1865a783899c3236050e1cf0bfd9a244cf9c2570dc8091b5f6966310396cc09dfb5bb999f2b353e1a801a324866c9a216920e0a0016bc28350d125399f8763066ce92416294d76d4f148e034b6e800870cb8bba4d72353c8fe8b60860c0e70c480b1ee58a51ff9185ac80103367f7448f6ab2692462cc0f102ce3c77904909379d825025c0e1022e7b65d10e328790658aa1653cf0a2155cd24f4a7f0e1d224c419700062bf8bd90a35dec12321378b10a2ea395ca654a5d446b94a9820f79fe39e6f63cbafb185aa9e035e99a6a8b674617da8117a86023a860a6ef239e82b54d75ed1b5b9474510c2d1b68461709069a41e3c050418d1ba78117a6e034bf830e7943c60bda185afea82ef0a2145c56330f51ef2e2f48c17b58564b927e4f9df15cd080f107068d1b304840a3ec28385d2ae9cd1f4c7454ad17a2e0f363caec5d3a4b726aca5e84822d1dbf5474074191a634b937bf983ec1fd9f7bfe68a3af49e409be84d4f893532df2ef9d6023d5b9e87cadabb1c30936f8d88d4693be92de4d70234bc7e8d17f847257139cc864a9ff9dabc4a399e052f49893420531c14ab2fc99d6c4f7f4e512fcc62821bea6490cadb304779a3c6216e9c1844e4ac38b4a304295be982e4a1691934507b4f8d8c087161f1840c0ff02d0c320f689179438ef7b6825517a4fe8b560f06212dca87e04bf1064f4a08ba105a3b048824d7aa7d378a8e98ad987161f1f1988c087068ccde14524383da64255b07613df8941825fbf0a2aa748316ae93c823ffdd274a63a82bd9c34055d7d1d6a9d4670c2355e0cda67694ae70c2f18c17b5a64f7d4680c55418d1b551f5a68a1821a1938412d82cf2e31e7a07e62aa184570d92d77dc90f375f2cebb8c2d6cf89f00c60d194713c1c934d16b6236a152362182499ec294c823c754820ec1e6cdfb1fb4c9d4c17343f05152d24acb9596fe168297543ad72b8e84e0acf6525ceba0244507c1452aa13daa2b077821082e5f554e7a82a851eb03c15edeff4d6326bd3f258b2c00c1276b8b596a343f349545165b022c3c81d1e1c51ff8f10dd139ba882431c70fbc7b4e27515392d9d5ee036bbaee44b2cdf281913ff9fe94c7ec81bd49fee94a947a60fbfb4496e0ee1d1ae6819f9819fea3bf1778e0ee945e13b523ffea7b71072659b42439a4989eb731e0851d146dd749e5ea59cc8b29195b60e0868c0494c578510746857c36b6fe1943ed1774e0ce53740826f574f2620e5c9956ce21b7c9f01255c0092fe4c0a94b6fcb599b23bb1307f6775cd3d6c53c2aaf2fe0c05ba86c931f4bef4632092fdec0d89ad00a65e757229d1bc8228bcc176ee0924a49a974e9c545a5112fdac08d6a88dc3152a86b644005fea1c5c7ff023e3210810fa388176c60efce32c81695bc41478b176bf053e84692d47af27b2d3eb58d191ac87aa106aeb7b725772e59a7160c1b32ac108d09948ac08b34f03169537d2b4a77a930032fd080d2d41673a9cf258d1767e02dcb83dd051d748766e0e379f88e0c6a4706a132f0aa9adc75e2646062e5e849fae578277d697b0d7b0b0d2e607499e20a5170fa9fe139726ec836cea001c3466fb1058d0a5429ae0805dbd59162fe741af3bf4b842b40c18656980955b24424c9159f60d3d9a7f3b3d6ced35ee1093e0625b3e48c1d561902852b3ac1c4ca2023c69894d0ae73829f5461c22f879790e0159be093a84baa6c52aed004132c3f9e3e15bd086a74c1050c1aa898e08a4c98fdf783a5a6941a7339b80213dc983c0da121b546306368d950c1938006bae2128c720b4968548f2598541a6d6c929b0c3247aea8041f4276eb85549af27994e037ee8598dc4e05b86212ace7f6ead4c963c82349f07e2aa56aaddfe47b24d80e4aeaaddf0912bc05f97b225cb5ab4d8fe053799adc6772041b53cce9463da90ba9d408fe3d05ebb32d9d17498c606257e55b532675b28be07368c8e135123fa68ae0dfc582d29062267d2582f1743177a89986783d22b88d2523e4b38b69933f04fb1273aecc0ca54cf286e0e4afc91cea540856fdca74909bc4439e10bce44bdd35d24e643e086e73745aee08828f64ea52558accd10e04abb1a387582b39f405045f39299612eef9039742df3ec50f5ca508aed9f2f729ad0f6c886b1674121af388203eb09bb729a92e6dcffa1eb80e094147f6f4163d7ae093ce1d4dc5be3cf09f95d3932e1d0f9c7acb8a647f7a21f33b709d93b4b899d2d74adc0e4c90e631e55778b6531df8ac184c9549074665cba5155210dfce1cb8148b5e1aabbf4c39f0137442ec1db597db38b016e4b649ffe0c0c8fbdc79035f32c818e9f3b4a8cb710323422c11b3a4b481c9994743b65ca27236f0551a93e6ff77d3c8590393cd44ac15a564502635f0a576927df654b97f1a981083ad48cb49881c4703935a3c3d861c9243e70c7c474ab51918cd0f1dedc32e035fc97bcdb3fcbec22603933d548912cd92aaef18d81472ae1cc9e4e6cf5931b0567a225ff0b318ec0d031793599dbcb08b965e30f0eae321779c9c64b8fb0524d7a7c9ce7881bd2043f3744c9b57e90217227345996ed0967181efb3ed94bc82b0a0da2d304ad4a7bb9bdb7a9216b8daa49daf5f22d79f056e736a13b699d53c58e03fa9ec371621a778aec0a768aa9c533f8e69adc06d784c15b8ec98d62c260d1538ada46576ba29f01dc44de96e568f9914d8785e9bd4ed73d494890227ba2cdb53fcac3c3150e042f30999d784d6d23f81cb417d3ed18cd7277782e6d7ae368137a979934fba9ce32a13b83c3375099cd059ee2992f4c55502ff9eeed2d7abaadb24705b5a16722599b36a90c07769e48c5fe611783bcd7efa4383cc5c1a81b78b5c71641ab178c922f09354104d2a7b3c694922f0315a929d42ca10d80b25a647f57be92b85c04dd4ce6ea173570ac920b063675b79132b651f089cfc4ac97523498a3f60bda4a94dba2ab11c1f702b29c6c8abb9ae3a3d607465b5dd85c803563be490d9941c1109ee80d56ce7bbf9a403f62568d2185f4f257d0e583da162568a1fd47a1cf019736392ddb1c6736ec07daadc9f57214751db80cbb7ba11ad32d7c6d4808919c1b72a86067cf6e7fea4435cc9e20c38ed39a2a2464f4b940123938ea93a2f065c106e395b7d4ee3c2804b0b26376becae7801efdf566b694ba86a77850bb8949299885a933edab582f5dc0f4134a495ce8e1524ff1f95dead5b05db21778c12723295d4a982ebaa0d9a6f2d3c28a58293982b9bc4ce21c4132af88e1f256f6dd87ed229b8ca5641a525eb137a5330495bcaf6d43b32eaa5604ba7ae936ea7846749c1897e5bef4a12bda3a3e0b4938e41435544c1d9c59226e4a8c9d92aa1605483eadecc75112705147c59d0d7c97d82af20920e3f7929a93dc18b5e6477b7745af54e709deda292aa34125c4eb09d217c2b2da49d36c1259926961764aadf6882bbcdb425f96299605452dad339c730c1c4929eff64b25392b24b705a4c8876893c39059525f00ad78ea5ef4ab0fa9bc74c3c457b0c2598e0a24b548c981dd4493022a74f4a8dbfc71325c155c57affefbcb9ba48b09fc4ba7df27548554182fb8829b3ba72e7f9084e254f929f3ba6912547703769e37bdaeb8adf084e86cebb5c91a47b9a118c27cba0d29dd2695416c1a9d3d2e5a7228ae04d5b68d9e6fbba3c13c19788ef113d29bf1a5d2944b0a516479f521942ce762955a7e310a89c3f6d26ad37c18371c37f8b2fa6353a0cc1c8989bfc2b8a285d5b472188d51553ae98992d5a34af83108c14bb203762121d34a804fd356e60a0c6ff16ed1d8360cdccd5cab2998c95a41d82e03475f88dd5e7dfb004823d4d32e68a9a4ee507299047e80004a7f536c1239866670d091d7fe02769d28a9fb6634fcc0fdcc98a51db93e49844ca09ac0fac660dd53b1183bb09f9c06ee648692f84001d7be02a646cfdd1594a8dbe081d7ae037246fbaf76ecd9d5542471e18cd49be564ad7930836a1030f7c57ec8b216dbf52ba63e8b803b7f7e19ea38f9e6ec60e5cc6f16c5522e6a99246e8a8032792a8d251fda703177eb5495f4aca1ed473d037b5e7484fb1430e9ce7ac26a49655b84688d01107ee73c6ca2dd5bf1872e0c0c7586a495bd03a15d42e4cf00af0df401659bc8109f2534e92d3c4edeb7003dfeb39344fbd8898343506b2c80246093c05a5b2d0d1067e52e70675f69dabd10e36b05d2997b04cffded56dc71ab8ba2ef37c4993e518a21d6a60ddb2e58a9535a550ab230d8cc9ce1623648d79226da3cdd9053ad0c0887493a405bf4b4f39039fae527e09c99dba7733303a66ae7ca2338578fa04ce7494814b615a991e5bf25afa67a080060c1a37b008023ac8c0a9202db774794dca76dd1838f5710bda84b288a6150357c2a3897888c433191a37b850410dcf8e30581639820e3070d9adbc349fdcdf3f7d814deeb132774c27e9e35ee054d5a9b5f7a8219ada05464834b720ac639650ce9001e3468db761a353021d5c60b7af6b8324e9a34afb3568dcb06166746cc1d21a59594d08e96f0103358d2df423030c405bbc8cd4a105de6ef472c77893535a6368d1b821e3c0e0c206b6a0230be898d2a69ed6bf788da0b1d0e694b33d23336494e00add7699a68a6c21a36f5841bdf60da9efe26dea3baa6059165193b119339a0b1b860aa5ac912db43f8724e3a2048f0a283643469780064a10744c81bd3cf7d551efe3698471c3460d1909061736c8b1b1c5c9f0e7c2c695144a3d3652bf17c18caeb1828e2870e61252e54f2f14f8d3ff79747aaae45c660a3a9ec0c50e1e439dc8e9b4b74ee0b2c474a5496a3a9ac0291d247ac8a629d9c9fd025964e1353a98c0b68daeed3896534236860d195b9c2e96c02511bb10b2ab8848994445871258959822774aaae367d1231927602d2af0a1c5c7054af0376ca8000338810fc01080293a92c0da26e9292108099c92994734e7e8c9d51e8153114cab44a932f0a1c587063e2a10810e2370228db4ac103adfbd45e0f3455eede744e0f2928ee9cce347909b8e21b07f42e8a0efc6b4b60e215c1616840e2074fce054229b3c4b9db51e1b30b8b071dee183ca420f1d3db00e1e70d1ffaa72e5654b5ec70ef8be4951b3c4a4189a75b00b52a6d0a103745ae94d10030b08089941e44c21b85eb151594aae69c71082f5f3772bfd6339773a0826df5def9afd498a141e602108ce724d909793fa839653c022107c09655563dfadf92f2058d341db7fd0a163f2fb0f6c86a04fa8f02b9de5f98131939ca7c2b38230953e706d39d9b727496397e703bf9bd934c6b49493577be037fb4dd00efa19935e0f7cc8dd3af194fe7ac9f2c0ff4608baea43a37a89072ec748d22477cebcdfefc0283da12327a92c2aff76e0b3a7d98ffa5607d63ac9764f4176955de8c0678e2357d25fa648e21c580922a6102986c881eb984c569e8a395fb271e0277a3ea1467b10b11f0efce7a6b03135327a0a7a03ab957547fba75c5a5b37701bff2b25db531bb8ca6e5ba9e31f399b6ce026c620b455af6be0dca36b16a1d496696a7280851ad86fb5cb4cd31d9ea6810ba2e5754ac354751c0ddce6e9de3c31ff46edcec057c5b81938a5f58204193b62faa50c7c7e0d16344c53de7892810ffdcc154cf787f2c7c0a549d9fae6a546db2906762c4710416990a53d250c8c8d8e683a49f2df8d8281d1b912bfb4bd5eb9e40b9cada47d0b9e7981578ff93a396834937617f8d2fd1972375f26ad7081d1ad52e963ce6d01911973d56dd6b4c0589910faef192a31cf02ab9a8248d1b3b0c06aca513fc7ac89972357e0438520667bba2e4f6805fe3ab3084b52444b24abc09e77ff0919a454e05237d3d5cdf45afedc80c5147891ef21a4cedf5f7d41010b2970d9f3c9abbccc12da5b2ca2c0664d2b1b3f3d144ca521a5570b583c81496ff79e5ff357c57702afd1d6cb7bac2b60d1043e83bc98844ab6ae923281cd31636a4a7ab48b0a9760b29831585aca992955724f356925da41870558288153f913438f4e99619104d6ac9212c964ceeb8d085820816f3b114f31e890db2a45c0e2089cd614f7fd63c4cd31c908ec8f5ecaf51dfd6ed92270b9275d2c2f75501625029772e8750d323904ee4c57ff5a704fcb50084c74915ab9f9633ecf30086c48ae8c19520ce941580081499aa65468baca77610219333410e3860c0e7441a34601018b1ff09f4b259768d36f16f201ef7d69e2bd254fa5dc1e709d29e6d51c736d0c010b1ef0766a379eb2b403366b68d60cbe7f3af63a60bbeaab1c70218368ad166d49671c077c0e22ec93c9d40d7877ddd441c41855c4b60197ff2caa4a36cb2a3135e032db778a5731550a190df86c222283fa6c065c3c59b974694c6b6ec9807b1349e9b064c580cfe6a5c337b7e7aa30e0369d0cfa5308b194456080c50bb8d19f374dccb921ee63e1024e851cd32d9a23e718f36845ea57fedf780658b981072b3859adc12f3776d0cd7bac8211f5ba1e4baa7877ae8a5473788bed910a26c91494dd77e5810a544cc9f2ee15434b055cd0f09de1718a75842e75167259ce14edfee9312584849482cf8d9593ccd476b2871448fc5297acbfb2c551f0e9d7418bab77f08fed073c44c189bc976f7edbd16df30805ef5954f6156dcf9a030aae459fdb8518327daa57011e9f502b9648c8c172f0fdadac2710315f5afbd223bfd289fe56bf37c595061e9c285b099dd6d26a46b0041e9bf0d0047a6482cd29a851d142dc132ee0021f66000f4cf0a3552a098929e814ff127c4ffe64379edc94cc2dc17a122a6b5e51594d5e8963faba0cb1ee43092e8f6aeaaa20e396951e93e044a7b47c7b4188e8100a9c068c2db6904102f5808724d84ba22a7869c40e6524780bda216c5377fb4a23860d2e7a868d19324aa066c303124c0cc24d6e88cef6e6c5d05a8f47304a5f503a9fc960fadd185a30b46448e01cc16afe10ff1c4f0cad3f41db281af06804b76f2a552dcccc3788114c764927496ee69e1c5b0497ad777784167bcbf901c243115b8fa6fe6bc77c6851fce091084ed55a2a3d2948070f44b0bb3a7e1e2a72886b37622e0311f8d8337818827b53e369b1a25a083e664d32e6903323011f19884021047b3b62164c4a8ab92f41780c82d3b5b765c96214b315e9e021882e3c02c1a57fa46bd5fc1fa365e4e001083e95df25efeedf08528f3ff0b124a87aff3d117537c2c30f8cae131509d9d6c2cf220b8f3eb016a947877e6f03c6165bb40ac1830f7c458bed92fd74f218327b60d3e51482b688e9da84dae0a10736e50e22e790224b25cb1b3cf2c086286ad4861a6d89371ed8ae2aa544648c0d1e77e05f734c3d326976604c24e74c2fdd6b9bc43178d4814d0d2a58b54b1a9592c4e0410746fe9d0ee1f5234f486478cc81bfccd0d2194fccf427075e63dccecadfca0b82d1970a8f38f0a1995e5bd22d3e86d5200500a7f080035b19da2799509d949d6fe0d463a41054f333438e1bd80b2294346db5f95d03058f36301a3177be2c965254fb050f36b041729c98b2e6a0237d960b1e6b609412b947e7566a60d2e8d22b31e3af28791a38ab71d11b42ed050f343021e90ade2984604a68cec077bac82b315132f82a630b1e66e04ac57cf5e22996eea4f62803afa2e79f43349efc4c073cc8c0e94839a65a2c51175b1510a381c71858db893a71abd42ad819788881fff829c46a2c58e7340c5cb2cee295d524a7918381cf9ce0a2169e2ff0d979f9659af4029bba27d4259b5d6092b20d15fbf2c532e9001e5ce053f6db0cba390615bb2db0214f4c66114134a38b05649145c1284d7868810f22638c589ddf2f52b2c008dbf23f594ac6a08405f66d3f6d2c4d497550795c81dddc217fe7a474e8146405f5e8ee641ecbb792f4a00293b35987b0507f8d4504784c8151adacab0f9922fc6582871438d7abf6da0bf1b48aa2c0656d49771b7d99b74982071448fa3f9308315bb4f82014f8d0e2834ce0438b0f22810f2d3e48043eb4f82010f8d0e2833ce0a30211f84038783c81b74cb52649250b13490f3c9cc0dde90b2248fbbdbb206d0257428fbcd3715f4bed2cb240820713f84d3b4ae764bec712f81b13f184b66ac8537b28818d90e4785dacacfaf148021752f5e4d1be1d12989c2625966d527d263a29781c813b915d52ddd26e92ae11b83d69eef5dd16810b2aab7bae591e4458a3ed66cf69538f2130425e4a96827e45fc140f2170fe49d389085a6b32c42308fc6435e191e2e8999918f000025b5e963534a495547d0cad46335e040d838b2eee041e3f609259d613f2be734e4231b44860a36f901a1e3e6052927ea9a162ddc7b4075c27199205b770091e3c604cc8984dd74495e0b1037e3dbb7fb0ae7895643ae0346445885aaab26a6d3c7280fa87c89ff63ec60a1e38e0c44de7516dff98d972036ec54ca6a436670376847f753e93b7f7162c78d4800f965da2868896ee9b068c5453515284ca63065c8f9815020f19b0eba3b3d58807b7ba39e011034e87c79cf2e8580c2d187f83c68c6231f080019b34976b16a5c70b38113b2449b5a33b3f1943ebc6dbd81b1e2e6093fb2469f5bb69c115ade0b353c5646a7249af6005a32949c5e0914cf85d31b468a02a1b57ac824d972f42fa1f8f952a31b468a0e5e20a55707e95238d8a575992c4d0423066c810c19fbb2215dcda899979ede48a0aae40059f4306a13e7b506a3fe714ac5a89ce8ddda0710215d4b8a15798821141927749cb943ea5af28051f4787b6b7cc49c1d56fce12156c1825a8d15bf40aae18059b0d79e073e76b51bf232a95f0c006993a92a7a40bd2bf03eba1af6208db0edce5c574f1d25d07ce2ce9ef1dff1024b87460e37664f30efa83773b07bef259bee5bca15b5b397067a16f44c4b32cd5c6810d13d56b8d1cabad85039b52d2b3afd3d1b4316fe0c47e7783bc5e3bd5b8814be9f91144c6d44949dbc02955e5a174bfaa69940d6c5ff018cd43d093e95c03a742dc738a199e8226d5c055864cb2eb539b8f99062e6ed09e22a79cb4298906f64e2b338a8a67e0cbf454a46bdaf210cdc048ba6c9d39f3457fcac0fe77a51b2b61a25932304944feb4e8e9413537063e8af868268ff99b3931706de56dd1be0b03934a7f9a976a056d3930b06d31afb46ba8b8e5bec088a023e7d039b5798e17188f9f763f49d5e0ab7581fb53d984ca271718fd9e2a4cb7afe95be04fe6756eada0a3a44c0bec07d179962c45104f16588d316dbe1aedadb11b5d94000bdc55d05bf27ffffdfe2b3076d9b28d0995dd64c80a2418401518196429aba0f399be3815380fa97256acd45cb19e0263a9b347fb4a6308610052e0d247d446518b99a451e0d276c54f6f295a47870277e9b33e94081694a89fc0a964a372923e3142cc3a814bb66ff9552a791ab309bcfbb59acedcef51c304b6328a9ed610f9b404dfc612d8149194c9ac2b814931fb7fc4ed24a8edd984a61b91c0b7a7a495b4e83c41d7476083cea6d3b4e99f7dd6085c9e20da23a77b3591290297736fac98fa2a8d9212814df13ef628a58352691b02f7b14595ceab58a511022347db4f866ce6494160938838329708da64b2bae002088c8768d2bb797ff5f23f6093deb3d01ef29936910f18a1546ec4ad3c3a47de032e770ef6956b2a09f5f280b3dc14ca4305214adc1d70ca628e17216e74c08552394ae9cd39684de6804d29d45a893ce28011bf1095c27303367b907d4a9e0edbb3d8806d0dfba0ad6b74da3560dc7a2b2506eb0703a001ff1352e575d3a0d7f60cb8504927f9514d984618f05d6cd127c01d800cf8f6acc172aa517ab431e0b46753bd2e21f2ce84015762424a2e59d9c2c62d603cfa2eac1a30801730512c85770a52826f5c04351010e3860c0eec0706e002bef3adc47c71f2d10a266fe614b407510cb5ea02c60c1aa564ec083e58c14a9069e365cc91c9f2555416e943157518f84805139404d1498fdcd229523e50c1fac7d19899f2970499b2027c9c82f76426a25b4952cc66df6805d028a6c0d4894a173fc330c2f0510a4e0711f4df69ac48e183149c505a47638c96b393e8071fa3607fec5d3f74e2e48f8b820929b3291dcda6e3e942c18812ea5577d55e3a0814ac9b7a6a095993bd7f3ec195768922749a56d0b82718952cbe28cf124385d9095e4d37e207fd6cadb11d7c7082db7135d5d9442e31d5c726f80f919cf746f7c1872658d3f608b1bbb353ce67821092c8bb172bc5061f98e04446f40a52fb2a89d02578cd1ff474ad64094eed9b99eefdbd5eb7127c08da3ea55de91ca2e683124cb44e275a2dc6838f4970f26f928824d53dd49404232d8f5aafbb23c1ad7d0ab1ee36e60bb5103e20c1a90fc256bba4650d2139f87804a74e997a1e193926e539828b62f655ae57426fdf087e4524041d254670265c943e4b9d4570bd591f6fc7a208fead73ec34759b6c2d119c049d43d2eb8d082e54c7e837eea1b7ec43705a342695ebd26bb49221b89422c73f4f2a87e4ab106ca69ab08fc1738d6409c14fcefc1cbaa345d1f72078f1985bef7346106caccad79c64a7533a24105cc8197940f0ea15a286d8fb1fb82be9a729fc22a8e4fdc0d8a492e895b6496ed60776fc4f09d951628f88f381fff11033d3b949f4dc031f399ea63655e9b9463db0f1b22d6249ce11f39507cedd3e63698dfc963f1e78d53c2ae418730726c8bfb64fb264072e2ff7c53c5d06f99d3a707b497b97a8470726474f11b93be90e1634072e5a485be54175a44b0e6c7489f959cd1207464773ade9e8c181b1916a9ea37d033f59946dcef92a7e8e1b38cf18335f636766e4b481ab202d458dbab9d5216ce045861835bf275d56fb1af86c31a769b5a01a78f7b498be924c4969350dac7d94f4225a77c9333430a6f49598d2330fc13370b24d84a8f135035f3282d68e25722f5a65e0bd35e9ecf14b7fe84a062e64c6aed17b595fdf18189923b6e40d1203675fc2644ce139f461e0644e39a91271639dbd60e04229a52ed742bec06fe61c82fe94d62152bcc07e9c20316fea3c2ae5bac007116c33e89c74cc762eb0a9459dda4f132184942db02164e98598a916389dfe7792560c49044916f8641a3725ff94d2670d16d8f461b2b4c4d1619db9021766aa64b434ad595356e06b4c349569ad0a7c069534f6a6a5d15da102dfed962c7bbca7c0a5ebcd4da9475aa490149854425aae52f9826e370a6ce82e6191440a14b8dad2117472b2cc9b3d81c95eb5a6a7544fbecc09dcedc60b397375bb3d4de0be3a8b54bf99c077e4245334dd2cc97c094c48695d35a994a03b51025b3a064d22a734e2499204aeee3e9ae605d30e151278b38a9a297fc811d86c49930c3aa71cf48f8cc0e8e991e3f9626a6aa5089c0aa63939e72e917c1381911163f7e660c2c7ab21b02a2289bc2f3361a710388d185a82106910d8ca48a7828c1cc2f207089c88e74e0aaaf699dc1f30528369ce67a9a3f7e7032ea6124aef88b29c35a407ec9b10adf5764bd2823c602da4f13449a6ec8057fd0edf538bff1dd1017ba956ff64e9e449c2470eb8fbbbda943d2f287b71c02465712c5a9e99f8770356e4475691fca7c57d1b3029fa6b05cd7aa577d78031f5d1ef4d9f96c7140df8dddd12daa4a5a887cc8089b1e28e74d71c554d19f031758359e527066c9de80a06dc8a48d041f9a70c217c01273f7f76b1353f5cc0e6b825738654bda7b782ebcfdef696f5528a145630b69293298bd62a1c2b6d5a446dbba2a96094ecf7ca3d13156c0a7549c4763b05bf7549f76db5a660df7593870eba52b0a73a650a8fd9a1244a0a4e44d717211a44c5108e82b314754be95acc9d2da26057636b49dcfba42e1a0a4eaf266dce2e41c1644dd64987c84906937f82c939481029d9dd3b7f9ee03fb2a7d0a72753077d2798205bb332ffa720630a2738d1ebb89bf29a9278d9043f499ad03129fdbd26d104a7dc5643b3ffd8e99e092ec48af95444523d398f0926a7d1d2796385975a976094dcac7b135f4b309a214f361dda64969c4ab0b153e6355922079d1f25f8516e6a41de65a89c3c031693604496972691dd92ce6212b09004e39df43da4ba9160d4b4d2266d1016b46627304830a2566b2a65fa083ebae8ac4fef9872a53882c9bd7b3a3fe5b74ea6ed01168d602ced4a2e7d59a2df89118cbc189645f069c992d9249322f888e6c1d32389b6f789e03d7e5af1151d118cade82bd91937c71ca3032c0ec1870ed31bcb93d2d1d48660f73529bd078b59a8746f650259341288040251381088e162de2300031408001034288e4622c1601e69baf401148003512e1e3a2e2a141c221614161a148983e16018180c86c1a04018180a0703c100421e44399c1ecf3621fc13368bb43dc1793085894982986a70654067d06650cea0cfa0da206a506e90df20dba0c120c5a0863c981c2b691ad606df1ce099d0809062504334d8177b704c3b836f06980e1a11aadcc19884946d6f1d614d8133076594b099bd88c9b1323d63f7555b15d2800e423bf476563f2859891c3ddeacc1fc30d8bc0a504638208ba2fa7748601cc025c015c09380b3027ff78da2310714e50e4c0160c82694e5c42aeb259d0236e423d941001c401d401d403340e901fd0b54c5b0437737c0690600640002002c00600dd0c293ab513e02f0de00cc29520997b10184c326db53ab40ac005f0f60aa140307735f66cac75da310a07adfd85652bbb31e09124a594099c2384cd9801f7a8b048e3b7eb92fd889a6355a1b593928302fe8b983b113363fae1da64f4db427f1a6dd835458f880cdef902c4d9b5e415673361a887305290c1a94da7c0b14b46f6dcf1bf5d7330f1076c8b0646ff8b1801c68d10a5ba43048b39da9da42bbf6e890f567b4fd539d02ee507fbf2c83ce9185583072a16426b80802833941ba8fc67bf7a2f568d019eda3772a8f4ebf57125648eeb22a351747bfecb3ad58da24850709176281e0a873161770c2fa6b08415dd32926b91b7a219e2d995c4173e450c80257c1a2de2f537cf5166323ed1ab3060f16b7790d949ea5a1b6a9b5394db763335d74142bb413304c40245ebe683fb298cf5faf90ea6ab216c17857f66600669b1743c7e2aa7fb1c7e8ad0f3043e6e4600363158aff284e29e0d95885a2b5c8f05b0f28b6150bd09d47d7d7c7801d2ef3e2a7835a9780c6b7c3aaeadce158f5fb8011791151e1e753f39e32ad6123466247541c44a6528dec5f4e1dd55643a5d26fa238a3e0123d1378904c073106eb25326731055f4b7e247b6e38a455729beb20a1686b5075c999d07cfcec75bb8b30a61bd32458bb59a3a4ec541fa0f03f0a0822511ca5f2511cb4cb7c46ac48914d9430a902aa1c849a625284845982223f052cdf40cd439540850b545fab3bcd61110a1ea180ef44284579fb437bd47a184e62a71b4a18d427d415d415d42bd451a86b8b02a2c1a8908403aa2f1d6743d4826a6fa274a8e4ad890fd57194334f017b4674c4071430f8d5f5d61e8a769221e4511fd529350df500759a46dd9b1f4706e081fa037575d45598ba371457534d43d543494339c0a29abbf5d2d651ea3fd53551c0b2972edb10a026a0a0a112a03ae628da52f33eac81ea85b280aa868a41541f029c6299076a0187d2018744497da178416565d4bd6f3df4bd0a3505050785e251b409ee8033a55641f141d540950a541cbe953d94289413d43aa81fa82b51a7ec541cd98bc19838141b546aa82e543d54f4a3fab0a0624531a839505473ad9353544e4115ea0c6a1bd41ed41bd431d49fa38017121c701e8deacb78f970f2df14b301e54228e013f304a6734290aa4ba380e4105c894a1410af53be5995026d8102c6ce7f5c17b20802454301a1179d23e943019b9315d786a180e4b4f102d3a368c3b206ec0b8a3f940d5415542404aa6c85d6b32ea84528b050b550b550b1504150bd50b550d183d2d1bb67903002e508f50a14f00f4f5a2ea4c8eaa43e5dd3aa06b3dec11e269bf23c7bdf7f40183c34c630feaef1c0a724bfb12354a0e9fe1fb3f29628ec9bb754af1ceef1b02a47668ca534e370a78f886b610f65000c7704d3826ecf14172aa857c8bba7e076ef6c59d9296cfa0ed97befe9d67646c33db5e4b1b3296cc32e56883afeb013f98a145940e48624b6b249d119e5aff5f39528acd9517c24aec86ee12bd2018e4e46a7f1705947251e9acb299446846e5e7e5e745e745ecc5e845ef45ef45ed05e08f0ebc99cf85ffcbd78bdb4efd7f56f9ecfa37b4178c578257925e2afc5c7a924b6e9a5cebed0521d6bc6972582b3effe8c32ab1ac211c477f5ab0ca58a246da2bcb2e548fe9332ec54558a86160cb057c65f31608d5e895e3dbd6ab9d7936cf80bbd7a517989f2eba8f33143ffbf1621cc030d5caf35fb3511da187c30f362f02ae295caafe37652f297e8756cb2ff38c29c8abeb26fa0a096bfb810ba02919eb461e6639a95c2875d3fc6acf7122c1bda691862a591b05db357fa66d6d16117dace9a1236aeb3338223513ca8c2ef889320fb8681d180e1e5c35fae0c01d99ccb581d545ab7afd2d281f0e5b8d682b1c6379149fc701614157e1751158ab67c5edbd4e7ed9e782b7e4499a2657348c211c510f5103b9f38b8a5e8c4ea5273f9f725992346d7e63feea1fb81e99f2cbac0ba8202a304ae5be0c5fbd02ce420009d40e69c56138265692f72978717ba72a743389246019663f117cb39f9fc8743176f0387c1d5b25ce26903aa4f676cab19bd526aa8ce400aebcc18904b84bd39880c52953a44004b04ce5909f480d6d5840a17acf033a254c0e58d0a6bc6977d15faef13986630b26fc9986a108d99f16104ecf5b3f35dcb4d44fa1b377c498ca0443445b18a51bbf880fbfad91b8b3fdef860b0afbe061c173399c00ae791c9008a039f5094fe1207a6c08f36bfd9308b8ea376a1ad16768e2c7f0155b6c897a220741a13a496ba139edeeda09130f8da532091ed6d3806e3c2a0623f800677adf312143ac81e0762d3cc2864befba4b14629011335d81fead98c69a744547948f10d977500f515fd3d08badc967aaf793276e10dc199f78ddbc1d811af15c164dad86a708fd4ee7e42d6af1bb82e5d5cb724003113fd360aad505a9eb86c0ce861f94c113d87aa5a148569c455a53f252b7d35404a0ca69af4ac6e5a31c616eb730582b18be988440ce1501e795c0d0a16020f77517e6f84745c09d9816b3b6d63ac4e5050f0f0bc077188ecded05fc790e7926382ca3af3b2ddac2f52c02b92f82124b39956203e88333eda90f3e907137d00bcad1f943f48cf21b14ae75bc1bb9a78e39e9b34e51c866c6df82ee33a68069f1404cf46d41c22cb507b7fc46542799ae7e1bdf453f8197f81031a69725f06688384434e656d70817f1d6fc7eac7c619fb43421384f27278b3b8204eef7295968e0939fbe58711456eb9038b27d3719afd864427cd6c2b7766d9944e4736339f2db39c7e39a51946c04aa995430bbe0e74380b189881029652e2ac5d66a81e508afed713a76d6e9baa2ca14eedcd17481e1769cd23a146e4958ea06e6ab4aab00e90d31c47dda97b135a14fd67e993636af726f47e1967f828c7e265d9f98b7e5e1d374de306bc8c245504de9fbe8924ce83373ca3c1653e6fe8f0ee2e2a54780ec7d992b0d43605fa8c39d1c5f15414776263a3040636919635fbadf36ebde856d8c2c0e02cebc9ad68019631e9cc6894ea7c4da0c9245acce6012841c9d004947a389d7240177597499d9e6e1700a19a381edf59556101e5b8597d981369af489487d700fa9f9e2da10ab5f44efc86d16a34853f2fbaa7cabbaa3f084552a46d349af8abcc804260f6ce43ba17cbec89f3d528c4ed9e17ec57375c8ee42e72a4184a9bfbbb48e76bcb65916ccbc6838d8cde355e0e8be1459058195976d4e400f695c561c271b72fafdfb76e065423e492688b78b5a2d05671674700b7c8096dbc0e88d2ae4db209ffeb99eb4b9e41a1d71f4cfce865e0297ef39c2dddca0ea9f050879cab4af6212ff2c3bbd751b6e4549d959f2694ee4f252db9192ea0f13cafbddf1b0999103eb1e7d12401c480defce89cc7edb1ac00ffc1d11d55e04ac55594e7d7a5064c805e5a65337dbd495bba123c56d06ae304afdf320106ed5e21b6e8a23ed1eab0388a172afcc1dfbdf3a0aaabf17eb4fe0c7649210398b3947c7df6bce0be3a19affeabb5706282367b163403b1a6a58dbfa60447b300bb718635db3c6990e03db0ff36107f0d42b9fca17b40e499c8659f9b25238f187db8c41760684af5a5383412757422cadab5da0f59c80e9b8a1e03b6a822f4d179c0a6cf1e0204d63dadbd94628ec5688c7f1564ea7b9e8047122784151f7081328082804fc7d1a8b20887999f3cae091c54ac35b609d1afbd728cd256b56d5e6ed3efea8a66d66d8a3faca705da88435c1895ab0f5cd4e57c4160470f51b36a68647004de59d20abfe9685cc5ff3d0344964c6d1ddd9c01c404603ad2eb909d485a115dc1ae7c292b15455feb70af5ce3a6b469407efa09e1e52d591a3ec37d7de609ea64ad73f1d40f1f028531ad123c115fdc20b2ba5eb7ac14c16e36c8168e6946232139ce7b4406fa0a3c8542444b98499bc1fbfbacc8b45cc102935473ec3afd721d1ec74ed393453e0e984786eeefb6ad288e622d0d668f44b2d92f309f2c49ca5c22d7a7891ae448ed102b7b5e20ad5448eb65371f0e2b914cf9543e1c76bac6ab484d5f4a2868e87ecc97d5f102e3360659a935f1e040d82c33ab2aea48019506aa3d6ef953c5add18476f563d60072967426d756f1b4f1803e263e2d072377988fbc2dba89d3f58689b30146a084dab04eb41e98f3b4bcb2d22abd314cf41ffc169ecd2fb9b7c86a188bdaaa11df6694e594f6cb3ea8365eb28d31c95517f9518343badb38d06b050c1b9e007ee837fb1e000eb8e303780f161f5237312af388b2969fd2db42916e6744fa8104f54fb0d1146346cab827c8d6d7628492fa79101ede960211bf72390958809e8139d7bd4386a606a7e69059a9ab232f52e6c253eee181c6425dabfea8d79a75927d826b2f06ef10fd4628e809ad3914568d35629c7c656e2013fb06ca91497c458ec120d9585cdbf18035120101d761c0a038005d5cc1666ad98f1745400a4175d048838ede6c2fe7df6945d6456839dac46e6d9714cd5a8ba729f9e9f2272e1cbf0412a3782ce9a2bd965dcaa68f72e42cad2e9eec0040d6f9580148a14746a32946695d9bd14ff6910d81901b2aac38dee6c2b51a7ed474f0d020aa075c30920d5c1837693f27be836992b929b9f7ea21beccdf7eadae08251ade11a35f965fc8c17a7ab2007bbd46aac9fc672f299523e33bb845c678e317b93f4d2d868e44afa9663965eddc13e0ee06105f5b22439f82f998e31cf529588ec8749d98332133f90abdea3da267fb3008ba2802e90af13284385d9bff0cd166f5dc77a5e5fa4d7b6a0c84eee8780f3cb37e5c3f1ed022ad0039947e2aba2c9a87153efcfbbd87497ff1ddb1e06dbf5ca243c5545aac95b6098637246cd193a408eba48f021eb528a2cc895dfd53747d592dfdf5d0a8488f62a48c5827a3227e099d737bdf8d0adec1a2b7f446edd8b8bd99ce0bd966ffd8dced97ae45ceb4707c560f43dd2cbd6a890aefeaa4df71a01bff8897d432d45049448e51cc613a4aea029f28a01866016f9f747728acc2ea797688c747e2657d774cc819780f916b7d01db878d0f24d712b5f967397e5aafa90fec3891078e4f3f988643ac2ea713fe85e0bccb544ae490c2e2da36b64babe21628b32bc9e02af4179d76896f219ae65edea0bfeb0547ec2f38be18f7aec050d3954e86ad7c2db481d943419417eaad29bcbe0e5a43fae0ecfdf27c3f213f9aedd169e0947796552e2783e0c182bd13f59efeea5c6b607b2a8de9db3731b89d850981d2b444e45e7b9a68a8fd61448077190d2c1a8503acfc9de694211cf10b562958b501bd57cef3a453b7025ace16a001a3413a5cdbe815d1f9ba5af4dd91235cdbc459b9caaffcea6ec51027b88a71e9a1254d020cc8f29d409e6745030de01edd4a6ad08560831a42067087eeb86d901493d51787eaef6daf77f2073fb99eedd8da562e88d95e8d694869b36d79352ffad39e21eef1c29b8c128151209587aa4d2057569ac644ee884220cfb32280fe7fce417095ea64dd6deba87787579d5d7ad7ecc1fa09dafbb865e3a0a2a122b37fea3e2b322063f3c05cf9cce9277774c8b91743567ff195552827687d5321ead36aa8ded6fdb7d7f6ba87a6345e00b9eefad53e138f51f0625a3e16fa00d3207fb67f1906f5ea53a4d22a192df25ac1d038f4ead5b594291da548da0982c3b310283365e26540c7c9604351b5b4451b66e1315d9d7faed4930e44c759096811f19a305a2826e36e7b2448b6e477b86b9a48dc3cce6e4100279fd4c64c7693c5e05a2c9e2eaf85caaa88bc0d1ff4dd74d9d261416d3ec89f8b780a9c6e522219fed7fde73a831ac8b1a199ee9d411ff4c17524450bd84fa4405283e5c39c8348ad24d02778b7ea799f4998bc8b568308c00c01cd74a1114c59c583dc98c68bee81656303d231d0a67ee86554c1563177476c065d12500e10ad7072e898fd309b6960d405d91acbc7a31fabcafc4593f717439ae7fad7390b1e25363c87496cce9b4931eabc92121ce8ed653881634c47156c41c48aff5510e8da5378810c32c64aefd160c12340ce4455b6c29bf604133c9460a1092463467a37cd64b4033dc0ecf039686cd577cfc67b45ac58a25f560592bbe2990883261b7b39fc03c9c2c247418c11e18b72e25233261c7b1dc4b20938c0dc1827c33c6d99b40d581681d1158472e364bd8b3c9649891971f181dbb89c59679036e5c3f11f578f7b462a6cf8aa24582b632ac90a70d41a8caade04789b6dde3e4b276536af0f7ab6cd6587ed9707d335210646c958b9c8bd28c326da35f1fbd21d138860f3487ec9dce0ad3ca85b8ab872cbf183fa5118861939bbf4cd6d291ee3c353619b1ba9d256b87e40bbd10b142be2d3ed6400fc9dc4aba3cf40a056c5e6b021b9281da68905cb8b9e371d30f57a4528e960fa3e43bc86fc437b1c3ce824d53067e9c5141b399d2951ce852f0146cf3ecd96a4e22b6ec08dc0790b63890c5a0e6142190b683078b164579bb1d29f820c88c0dcc7bc8a5bc3aef229a8484fc7082370dfa46348c156192943a1f8b2412e0a0403e85d5c98e0a1eb75b7f4ad0422c5b7eabbb16c1ce89c71591a19c42425626b5d8066449dbddea5b3522b4f79e31c3ef25ff2f5330fbb696a2a0204bc9a7b7df46de83fc8d577da88c59b385ae6762a6675f2c9de66d9ddb7ce8b9505ce0727814cb06b7052832237c6c33c5c2207100ecd7bf26ae292d11d0184e170cfa154b3327b9360e0326bdf8460519a48cca6dee901a92df33b7913e5d0360a2e94dc175a04fc5b39e052f4945909a670185f7957a6964c28c35e6b3a250403ca2d0c5d61007c50ae191f8041149035dd831e6116001b51631575ddcc851b66680a9872a757a8305e988506062bcf6241678a6b0af071db832bdc174886441e1a40a6fc9177b8042c79d93409b17f15372670dc395b93184ef642924b13f4de5e0a05c798702d549b42a883da678ceaf96b0afb188301f9509e60fa8c4020665d553493c04bb496eab290e270b3cff361fb031e138a92a87714f9bf4dd6838a814c77be55fd7660c9318883fdea12da67f6aa768927f4babfc35d99395438cb7edd1caa1de02cdb40fd19eb29561b3e65b2e910b83a146ab3ad507c454ee67167ad8fe81a2e838796ba6ce2d78a382052b9e9b5a218d549798d6b5747d107e8a419a46276ad3b8bcdebb37c08d05ce48faf42dacb43e01b7637952c129ae596364879fca85bee6d8c7f5628332024f9c921060f8d3f5398f4d5d5d55b68fd263b4fdaf9f223881a0d0e9e574939dfa179a5987be194c7fbff03c7c9c7d7655f668f69e3cf1b3f0df59ed40e7b2d1567627503970225c4eb0d75f3ecc09dddb41fe5266139a3cda8978b33c3fc488d4d7eb3fda7af694c762044a016a8de25207edd8fd1c571e661061003b0a2618e0dffe08a3515d54f2103e593b2d8ad6c34eb136877476e6c0ca167cf2ed3ea0bb103e6e898361fa5805674e60d10b83ede3685b9c92d7c1ecc65e04b7a64dc046e954ecbb57512424a08f0f12d203bc7ba52e0a646c058fcd3b52fb202f01aea8ce0df99cc9eaa292ee68c4ef5dc408fa66c6e06c658eba4ee2dbc52953157be27e987ccc1056e75adbfd5c075112ee9e5c6ea844b02db43e78b835464df8b26c7207f334bdf8a23845ae6cb3ae0c4b8ea8963f0d0fd48311606571c4770b22ee910147cc825d4a204244d770120f796d2e7346c590f2cb9b28112094637d7a417d401505cfba9f0190a8aed59bbd67b4dc7b4b4e0fcdfc0c5153480976c12bd84985f43b99d13a28b989bab1d6016ab02461b453ccf8a05ad8173f62806fb50ab689f91ef66ea1f9b4f75f1ca6389f6c4f6e9480e312359a29b00a7a1a41fea454b47555b2d28464efe0ed77a0efe53b309ca6481914ec0adafe115c026c72213b20be76b16947beace79ff90e5c848e8c5c2152cf148c9b22bba3ee3a556c87005d49f268c7349306317a8f3f9077720340b5489b27e52274db89e6d0ba570591b5ff17ae0101e93fa9f1a6024137d3b079c0b312d3a403941348075daaabfb8c35043de75949844e2ba3b1529023bea5d8ce0f7f6dd11bdc6c1abd3a7af574709c256cf61904be356c73bd026a053f24079d474440c39339e2f0b02efba80a636bae759bacf9f9948181c0d60895ff870425cbe59e5ae710de2534bb8f3f7de68cdca134a7d94ff4469dea045fbac79cb7cf1b522ee2f90337454a386c4b097830210ea31f762daccc424cf4ac822548a98210ef61e3bd42f730da373f834f32b0f362784a85520dc49fa82c15bd266f30544ccaa66cb13ed2e3898a8a3f3aac826e94c608b497827786aa8c25da2f5600ca1b01901452fa8d87d3c587af771832e3b4ef3ee866bca201875a44cf54a0912a0dbb5d6a56d9d270e482528cd8ec243dcd1b23b53a3b11b1a2dc49aefa08095df45fdbde26ebb751415405de50a10d5791d10efd8dbe5bc9906f24a7394061ddce5513251bad244455199f503d81484ff11fbfe4e3c9bfe93dea10bb5812dab028f37ae5c39d5a645d72c32c53006881ee69555524bd178af4d4c512d283ad51c7f0a4c26deaa6660c93189d0cd4f35ae130abe0bc974ea36b919401409bfa28505a9eb5a3365dd45cf3ea9411f3fe74113a7bf9b5159893b0bc1e4a366245824eb8c02ed05f676baa6717df6aa8781263018af539d7b23320252e1b25b6e33509fdd9a34d67ab0f45f9d66c6e149a11941cf887528616f5a721e60e2397fd1f407b7bf21893d505c0b82a3bff0837489e74d2e56cb8481119bd8f5aa7cc167191d70e6718f1a97d8f629b42de93fa2284c33f42d5e44d20d39e2e147544592a469a8bc394511ee813ddd2887348ed23479973b1b62b58133f05f487fa6cab6382711cb3a351aa40f562b165bb17aaa5fb931897d0157ce76a9a2941a41ba08a36b816486e9724615223ef4fbdc49cd4eff68ecca28814eb2780a3293bd355b3a6c9c67a16c9634dcd73b8dd5207bebb8c4a8c95b02dccdf09e3b9aa39c5d5d6b494d3f8b2d1dc1b45e4dc0ecdd9eaf74f8fc4593222e68c56ebf8389963c41dc8d8165d325aaa11f0036808347ebd155fd324aedcd2f0a63e705526a15efd59e331ed97fb4cedd5796db744e2ee51e4a28f614ccba63e6b71ce4a105bc23269148c15f1727dacda23aa466f515231d4f26e1351da4e70c9376e4c261326334a614108941cebd7dace8341751ab44553d449251bc56da73f3b102d5adcc409da1b8b0dd6ba9ed841c4cd44e59b91d1f0744ae0befebb853dd42d4509df12f8417e1543d15c8d206420101fea167d28aa49f19174b435ba50f193615fc7763c0f60609af547b9e1b8fa85b2d1cb7124531a8ad8a3b8f1b8096ffa57bb1679096dbd13de15b1222467945f80427cdf351c4a6ef384e6840610cb0d22e88e420edf3629640c4021f3b3ec7b0e94c7904d150685164ab1069ee6f7ccd7ddb6b7397714b51e63dbd05dff21047de3356c3d85bc01ae33fda37790a28d2ebe69a7b1dae7a7bb940db87e4165840b3a0da3a65845ca72b802e6d6820b1dc2ced750bb970f98ce1534857a2950dc876d1e53fb5134d271cda1d4a2268820d14fd798d00950210dabf2cb29f1aabde0bd2f7a7ea71c36db020b54baa037406a10f290c58c6c04c9dbcd969c7c0c89ccadeaad5efc4bbf26f181f77384b0b8bca61227fa5aaeaa29f17e3cd7c56846cd964797fe50596d607d58894d37b603e7d2a6fd8c1cd9d0da9284e752a972c55e0cd6a5522bee50e2565819f3a0e5b9ad1482842bd978cb8a079e71f2b631f8c8ea789136797b119dbae23d92e4c7436cac1cb5caef920892d51a095cb5e301c6970a106cf76ec668ba006878daac83a3fed4be9db17cda7599232116638674b8ed64a2031a1ce04d744874fdf9cf4775eea52ee93ceef9e8eb998ad1a6986a243ec27055cf499da0739fc70f235bfdfce9fed2eb7527a03dd161dc23d89afdd25d77cade96a78be6d9201632ee86729864614166e49c1f0d047ab206ffbe8c7d918a13da212f2f810942b8cb8f775dc8994389037873c4651ead3cec31d010460548bcc127cf63f56b9eef8a7e8f9c0743a82f0599bc14cbcf1b190795a18b500915bb0201d8d7536e763fe40dabee31924ed85e8f09e5741e8b46c3c8b42865ab7c74b1d6a07fcd12690d557ecd6b9cdfa7e9a1075135e39eef400a23c83ffdf779d6b9ff68925c69e7584075813393fd6376e47b275752bfc9b76c364614358e98b9f93067b131b129b818d8ccd171bfbb00b50ff4da42d6cefc5262846a3f7287ceaa602b98f7e6c4b10083927625a49717256f1ca9e92d2b63c0ee50b6d757f742e04cd39294b608a5fd8f486bf0313f69af348d1a5761b6915a212918092d166e3246df3f6ae405618b686e6aa184ba5ff15e9dff8a908c31d05a053020067f3d479534507aa048e55ed86340c87627dcc015d7332a37c984e723ac0624ada9d6805f6eef8f25441d8117f0845be44339beec49fa8c735aa212892479c89ce9979839006d014ffc9a7d4cce3584692187e1795042265cb4f9672ddc25a01b849088efc664a42e635400899db12e577a29ccb3e1220b89fce1732d066acec716e60d0140356509e19f7edf6624cc8decbe0b157f3331774fb5bf45d1a63ed833c5e0ed712dde89e952924e96cbbe3d6743887c3b2350b86c8f663f9f7f9eb54d019641243279d0710eb3ebb255056a21b963dfa0ab51b25bce9a738c7e3ba5cc2b957d3a80b1db3525da812aefefd0ed5734899aa18147e8b740cb2301eb58166e969b5420d371e1fb1275851361b764f7196fe34e9f537ab6e6d835c2a1665035aa54d14d11851c138a2b3ec708a21e22e3b10761d60526639189b080fdade1ee52e8d87122e0ca3f37e8c32f6b5f1a99b91021bda98dbe51d38532f548d09a1cdb5dcd46658fb76dd36334ab15be947ba424c43386c7a0080c5f44b23da6fcb963a75dd223ea12209ad779f61a72246ea9ccc4540fea6feeffca7b9df83493d502feda7518226a359829ce0477311dcb99164b4ff958869cbab0d2df5260117797ac97d6d4410dbf8bf6ff38f2485fe9e821d6944542f52794dfb212b43cf865f85e844e750f6090589d6cc7e7da68cce627ca1ce9e7b7832e2ac89f700aca7cf31dff3736a601a9dcbc1ac5a4160ff4229701e7d163d2013e924964a424cf54ea74c4028f9813375fbc48419fe392862ad47a6fdea7e526753a0fd3424e84d57f0e259a74ada54a5abcc1b3131161d65f33b15265d35b173154e42d251119436b5816a8f5b3e924b36cd4ea3a85eeaa86cfd1cabc94b9b6fc4b7d0004cdc9c9ac7cd6c51d7639b5a01fc35027ace779b3a70eea4900330fcf0c30f3ffcf0c30f3ffcf0eb6c867010f2899864a5f53bbfbaaa12e07c5352529225e3ffcd9afdb58192b58192b58192977301c809ba09ec09dc041a51388a318368881355a537281ce4a489a975abba2a963ad078c281678a68e7042bcde128d070c2417f458ced1fe9734836e1c8ab8348975dd896cb84a3ac90528610dc251cbef9db55aa38f9bf42071a4a3870dded8e3192241cffc5ce5c59e3c4949b31300e349070ac72b1ba3fbdc43e4738f6b40c2ec13724ebd3084771ad315fd7ed5e4d07071a4538f64f997f626c79df9408079f534c44f53895e319c241bea8bbaed1df95c34238baf6fcb2399b37d008c29187aa30297badc518ad25d000c251b6da75d2aca41df70f0e52d9a494c2fe56bb436da0e183e324599e54e37c47cf3d3836fb0e99fa3c8fbf448307c753b177a272bccfadbb50fac298c0ef0a0c2cc08b118401460abe088302ee0ad8224301347670f86d5bf93a03c38b2eb8e8a20c2fea28023474705829931db1ba39f864061a3938cece1d53a64e1a2345c6400307c7dbdb2dd9624b688959e00b2f4c199e002559a07183e3a01f793a3d675a521dd0b0c1f10613cbdee9bf539a6b7098fd5cacfd3f06d194185e74b1c51662d0a0c1e169b0b9b0b9cfe028e594ffd2b6cdd52eed820cc380531a3238d28d17374e3a06c7a12fdaabec2777ec6170185c3527c99025636d1a2f384a5fa9544f1b267dd170c181e7be8d8a752b8e3cc22c44c6d3bd25597110fd838618e65a12fb2a8e3f86e8c857a9e2a8bb66c3654e638e792a8e5b3a6fa7246e39114105111db7d5f33ea720759c8b7c9574531cbf67f7596a67cbf629c5417c38d11cbbda181e521c4bdca60d369fa4da328a8318d5d6c3edde778e8be2c8e32f3f227abb2a1b8ae38c5e19aa5b06c5815fae10be2dc987463f7118249ea7f5ac1a91a3278e37c7e43421afe710cc4e1cec25ed4d2bc9737a72e2e82eddce972689eddc260e531a89ea2995ba99451387496e2fa4a46ca163cbc4b15d566baee9f1d462e2e853dc2cd3fd1247e672b7258e4454b2598ab1119daac4f1ba5ec9685429713cf152fb74e50d5fd1248ea742d6185583258d39ca82199238ca27392c67a9eb98934dc18c481cc5f7f017d65f636c0e240eb4b354ca39da8f38eacca056b69d230ebddf2f86d68c097d69c4a1d4d5665c488c388eed39e782c5b7757ec6220ea2c5f55d2eaba4bf228e3cd6aac54c7d333b1371bc99c973ecbbb890d50c441cd444d59290b3bbb855d630e31047ab9a1635db45fed28b2e4ec005d5126618e2f8e5365b6e25de7e5d8863adb4292d4fbeb9d210e230ddab425acd64b71c185e708134c61661c6208e63e608b2192e7d292988e390c4726d4d6c2e99e00e6604e2d03f3b62f3b576a42c200ce311b5152efdc3917dafdcade58f34d6e960861f0e3edc55c879b3a7caedc3819edae585ca4ec93d3e1c455275cb6ed916550d66ece1683a68dfcd4868598f1e8ef4ce3ccfa2bf66b9f37090824e7ccbabf07020dde942fcd94b293e7738d87c9353feca4acd6887c3f01424746d5787c39a4da12d4987430f579b3df5ae25324283197338928fc934989a64282113c40c391c5caa6fd7e8751ddf8a718130cc88c3b1ba94a5bed411fb61381cee5b878c99adb255e50d4726c9fad7226279e686a374e93ca5da6a660adb70a4417246c4325b8cb2e12865c4f210e56ae73b0117472e460a3030868b218619630334630d4712e2a41599520d475b71c368a8cd01c3c510c300291803032a989186e3b2b5dbd8de561a73c0988186e3eb584936c53c3191679cc1142554f2915630c30c4761d63c429e18f2e614045ca181321c65f78c776e91cbe292e1d826c56cedcc6c259d318643ef0c3df71417c3a15eb8301552864929010ca4c08c3186c111cc08c371fa178d27613fbb2c60388823a5913fdf2f1c680a121e36c69c1a572ffc51ff428ad9b40b076fe2997261316a835c380afb13ed1566b3764280195b389e8938a9dea5fd3205ccd042cec8c20c2cd88c2b1c6f4fa70b1953dbeda60bb2c10c2b582f155656d1aec271cc10cba3946dc5149d4185a3e825d3eeb7a156df195338d0df8a613d74e2c8b9c5dcf84b148e2a7bd6226c9414c30b858332db18aee17dc271fc93919c75b256ad4e380ecf3dcf39984d38f2983bbf8fcd84e392d9d4f8d12c395bc2a145da1295e99470901de653d6c4241cef8b5c05fdcd4a63876006120ea345b38aa142473892987c2e963e4f9ec80c231ccd5f4ce599725cb26d46118e474743a8a89d9d4184a3f87962c4d3b7bd75c80d3386701ce5d7c724cd62fe344308c7122a9475e4db88a6705146185f9041e802338270b4712d2e9746cf1872188514338070d0957feab33b7fc6bfa398f183a310affb21a7dcb1ac77a154a898e183e310d54a9164f454564bc58c1e1c6e52d51cffc22ef4b6d8826a060f0e3ca7cb4e69a3329c0c320c174766eca048b699543fba020157943106152ee43043074793bbfa1fa7cfc1919dec06c9d291c2a57170e89da583fbcb0598718363a92ef7fabd9552cf06872135723aead1276d336a7094aa43dce68f1049f2193438cafe5292c3b6e97bc88c191c7647f35bcf4b020e0203c6d20c191cefe5b0d9a12e5ceda70a4303553362709cbff22de494cf192b46980183831ec9e7912e58678b21c28c171c74b6b8aeeca40c672b1f238cbe805960860b8e55fa3e5c5ebe1469314a508693518b452b0ec62ed2737e168bfc75a144069162c18aa388b963ac74a5efd757715ca1f19db9557e2d57c5e1dae6f53f3b1507b1a5b37fb060db72a3e240c73eec36068dcf9de270e6d3f6aee62b09b1298edb737df4518933c9528a63f7d47d1a1245d75b52b0baf5f12b257514873abd19c2ce298a0399a929cfcd122262a138ae781d425bcefec983e2305d4a9a3a68bac5479f3876971855be725ac83d7110cebe724821fed175e22885a4decc9359246d06c83d60c1092653fc7787486ee2c0be3d9f6b7bcebe7b12b0d0c451d8478ecbd9629189a3f71d71a9aef50a5860e228dbba4328d5bc948c52018b4b1c458dfd69928f9ce62c712c12161b3fbc953848de9d327f904c11c90a5850e2a826a564b521c60d396712c7c92564fb5871bdabe014b090c471987c7943e62f4e2a2eb088c481f489a58b9ab579251690389098375de73fb7a78dc5238e937e5e0ca55f712a8485230e76be3ff67c52b77c5834e2d0276626a509352a592c1871b41b3fdf8589394d368b38c89ba2640caba08085220e364fb21039ccfa053711c7156ccceeb25820e228a78f1cfd439ef05ab138c471e7a78ae43a626188a38d1fa298449aca21195814e278ce25c49a24b1c6a21a032c0861b49887ebfa8e3977a19474050616a080086cb1051765f8180d46180820a3015b6c01868b208c32b820c3bbe832cc08b6d8e20a81c520120b41d41669012c02715c316d45962f9f109518b000c44168c4f88dc4d8e4fd0f873b3a93f93256dce97e387ea908093926e193f7e160bca67aee2d72b60e1f0eece4a34cb26f9ba6f7709c51b36c3e8d583bb51e0e3dda2464edb902c5d4f14206c7a592d2535e772306d85aaa7e083617611c33c2060c0e82c445996dfbe4f2e161e305c716e4c286a0125762aceab0e182e3ac96d3e3c4e83db915c73329b769635b3b3dac38c8d1f6e34ae73307e90e3556a14f879aeafca22a8e4362f8df963c99ea151d6aa4e2482384a94e91e36b8ea3e2207dca4f9f776ba8718ae371cb496fb255438de74cc52adb7e32821aa53870b98b14e3fdb4e69453831447fe1ff3e65fd653737ca8318a4379f34df1696bdf3936871aa238b4d777d9d149a6a146280e255b0e5972f5559e078a438d9d577721dd48ced5f8c4f1e430e551328e4ccb353cf1c712579b19895577a164c5458d4e1c6b44ce4c13529ac389c3fb2f359fa96c7241f2428d4d1c564e1f7b36d6f9e5790935347114dadccc2bcf52cc452a13c713baa13b84ad6a60e228f47b9cea9a4eed145f3441d5b8c4d199c4601236a68ebd5be2b8eb35fee323e5264f258e2b58985429ceaefd4289a3f0d3112a9ac673b44ce2b0b2a7dfa04be228947fa409fdd12c66240e434cfbbd1e41481cada59c923d498d471cc745ced92ff78e38e891e90d9f2fbce802015b6c61871a8d388ae8d192a43a31d5c8888370933d048831823118506311c7512521de8615711c23630a8bed127118b5e22df27888e620e2e863b4354971da620b2e680ca23ed43844b16fd925e8668638b890d917b9558d421c65b6cc38e177421ce967399becf40e151fc461d4bc2e2e3171fec3823875ccb10ac4e178acf06c31b946a580b8cfceea65d63f1c65b110abf553629e9c1f8edf2b59fccd55a30fc73efea163eaf0e1c87cae636e088f22fb1e0efbbc6298dfb49fd37a380a973fa887b314e39787430b1727630ef36b13e3e1b023844fbe361252e70e873bd39233bcfd556a8763b9ce29576c5e52d0d4e1207bf96910eb4c532d1d8e7385dad8bd8a7f967338d23cc1c77d2363ba723854977ab5ef8ae12c8cc341be9bab5a7e7038dedbe8d099e80dc7394d75c4c76bb8e1a04d3d9687aa0e95ff361c88f77bfa059f0d871b345b48ef49a6d5d770a8d97a13cb72351caaff8724a9b6663d69383a598b1e47f744d368389e9c2f597e7cce70109521449b6ff7189d828d6d88fb4d97e1f02396e7df6c93e128c532472a87cfb163c67010529658fa8d21846dc4701055634d8b59c2701cf9630e995a51fa5c301c556d76c6fbf40b079e5f2155bea07179f4c271ce8edf3bf18d1463178e92776b97c620178e63929e84e0aff6f56de138e9869c5a96da9c4e0b073962ea31454ebc3a0b471d3fe7ce28dff063e150f7ef6388664c86af7018f3fca4a5f859216c5638923cea9fc43f5d3aabc2d1cba46c39db4585c30b979496f348b6650a87f17ccfea3629901937d5870945e1305665bd4908b9734628e0841a50387c996c91ba50952dfe84a3b4f927ac63f4387e3be1205d45f65fda84a398653ae387dc92ae64c2519a14d613a992a6c8985063093594709c26bd496cdef06076128e572c5744434d420d241cdb2593e91021644ccc165b24a2c6110e63ec7d798e560d775da00b320c03b6c4302368400d231c77089751e4bb752fa70ba5d43dd4284241f466f334590a11ea820cd3450b6a10e15872bbf94f495d1c2ec8f0130618554609260001f7620c0c44a2c6108e72689e48aa1217ca2c01185c7c185c143ac0058c12033584701442a4d8926123d49b65841a413888b8f296d3e7f89714a10610acc60f0eff33f5e674d91a3e386c0b9749222da446df838308d1a7245894681b91e145a3c07970fc9f4683a7a9608451480335767064725927fad96a7661400d1d1cba46f0b8f96fc64732bc80805aa0460e8eafbb64ffd3f8565d5c5081c01c206be0e070b4c76cc257ba02030b2003015720e08a11340746d0284841d4b8c181ce861863746517ff9830be98000afc9049a0860d8e7ace3dbc2f6ae6e732d4a8c161ef7eeeff7618dfc5508306879a73d8c8fb35166acce0306454d1ef490acaa0420d19d488c1d1fbff7a923ca163c8156ac0e030e7c64af91023776a6bbce02845d98a7ad9a328d470c15143889938217a1446185d70e15f1c2faae440a31587dba6e19b4429d060c561be3e310f32792d3a739153a0b18a2389902d87c47fb8185d8181055c81802fbe30c01508b8e20a045c81802baec00004ae3842484315871f646d5435dead30cae0a2d8a1918a833edb7429345071d449ed3f367ec4c5fa1487973729f227e7943c6c8ae38db1a93ba5e9d130699482a49f3727cd9e1447616f8fe2e8628adc985b3e8b4514071be347a59a90fca2a1388c1cd594f2098a0309414270a9a0f1beffc461e7f348df515e040d4f1cda9bcd67080ffa7e278e34e588bf5f9d1307994322cb8d9b380e9e36e25dcaf915714d1c74b6f20ddbcf1cda4c1cc4ac9c72a2b59321260e2fce7aa60e5eea1f305c0c329006342e719031db8ab4b744eb8e250e5c35f7c7958694a3af5860d0a8c4816feb670c6f97824d8963d50916ae159244a52771acc973db54a42571bc7677a3295f6c951c89e30abf7142b6061247a7bf633753f5ee96471c6807b949a92cd5cae28803cb9f53af4efa5fcf36e2302f3674b46b95ad282390eff8f198451cf465b01c62bbac2355c451468a41b3dde59a9f88e32f8bca2013f31f4e44f02f751bf25e52e81007d933cbdde6d21007f9537db3536fd9948538ca29499d7e4a65a424c461ca8c7fd13e9645080771bcb7ba6a84be4b3182388cba4bb9914b547302711c1b2e4be53be2d20788c38d512da79918d467fbc3b1f8f7e68743b50ae9672e75f0b90f59fda64cf3e148e2be47ce3a27c37b388cf163a755ce33cdea81d2ac88112d9bc46e694b6331735a5a1a7938e84acb1ce2e3e6070f87dfd17edfbcc35168ff878ef1d9e1287f287dddb0ea70909eb94bf32ceee5890e8733229f65253a334c7338885e4992c3a1c65deafafbbb55e370e85ffbea9e63789e70388a8be1a266fd247fc38124af0ad3792923e786c36ce956a3d47957aa0dc777933b9494ae278974a1f485e93020a054be30ce001a6c3890dc2ab5b85149048d351c656edbee564abf9e622168a8e150a26f5f6ba64d1992693894ba370f99fdeb316d1034d070947749c25dd4dc8c92331c5a081312566e62de6a82a06186630b952a345f321a65384ae9589241da527fa0418623cdb952c8e0395b9e681f688ce1d83ebce4fcb3c470e861f795c9a3bb028d301c7a4c92942bfe5c555f78a00186c3b8e43b9dd2e6b8b5d41d687ce1502e877cfa59a33d5776a0e18523e9918ff943a206ff74e128c7f1e95c325924d24fa0c185e3d6c9dd1472660b473ddbb17b1a936c8c160e7cc2aa9b84350dab6864e1e0ab53be0db92c5838b2948a663d10685ce1e07273ecbcf217e5fa20d0b0c251d4df6b5ee9b0b81df5018d2a1cbef8a7b55cb129c5910a47e923319bc685b4eb7e40630a07e36eedb26d569a39522858bcceba3505ff08b562ec237b1c717cbf9f5dd45463da4e2308d1e5b2456eef9411c739c4cd9c38d71666594466771b36deefa688239b75b9fa34713c3e8697e105f9175cc89045228e275d67c7bd8f88e3b0103153f38738f0778fc1ae2b346537c4b1e7f1b8a7655196ff421c76ca7c9fe28734e631843898f73c57cbd11f561bc4a1ca464e2e270ae2b86b375eccf96ee90fc441dc1ce2a5a496cb920b88c309f1b7f442f787030d2512266462852cfc70f4912466f1cd92b61c15b2e8c36148e4d13c9bda436f3e1c5584cb6f2bf71e8e538c9242f9c5cb1c347a3872ebb1903bce29649187e3ca95cb37a7c6c9f778f823733e79cf8fef70d019b723a2f67638d45039bfa4742e3ca70e66d188fa91f2fad0e13866afc55c58f988f21cb424933fe74a9d28a21c7e491d267b860e9a389839ef62957e957587c371c886247759bee1307b947c51f5fce9a31b8e6d4279e6a9fbce93e342c590451b8e534cb66dbaa1e3f767c351fd5d48db4b9765fb1a8edc83a678baf799425c0d0753c942cadc95341c7bad680e9573f6b48986a3902168b29043fb3b7886c3087957aa524e91309917b230c3f1b807c949e2a9bde4cb7094f62fc242eb64384e1b2d660d97c770f479727ebb8488b7ba9085180e4ca34506cdffba97301c780833daf9413786070cc731c678ddf3ec41f2fec2e159dbe518c3b5536cc43023302164e1858378b1226972dcc686d403ae40c015b4852cba508aca87fe5c20ad66e58466cb6f61119da4160e5a15c92f5976784e16e8ce326bf5c1c293653d7c0849f2397d857334a70f5f5b414b8b1b1727882797ab80578e4125eba496af50c1168f199ffad2296471e42fcb7bfe4b2a852ca290c6c4f821426558a0c09d4b2e9f1c29f3e6134ad2154542e54caba1135eb58939b2c7bc3781e8f3e830010b51c29c47ffc92de1e80fb7c430114812c38c8050164ae824f24892a425d62661496732f3a97a4124f0ffd2b9ad53ecacfe0887fe9e7e26669d560c3302338215c483844a112a8dcde9adf3d7f94984e3d7dc1de2f93f0462ded3fa678cb13f11c2d1e5e4312dfa6948bf8390471ccfffe7934b201c44ee78994359d0989a1f1c73ce88ef1f363e8961a8c43023301464e183c310c627e374787e64c53011200ac8a2078719dca6d345b4f5869590050f0e4b935f8e1ae39f67548e90c50e8ec542dd7da4570707215d57ec85c866b63938b4ac97cc36d13f5f2c0b1c1c59eead8cadac7fa96f5017c8c206c712f383060db211b2a8c171f0bd903b711331d9343890dccc9a1dc52c66703819a12e6ad6d07b1bf1200b191ce5a8a9623e5fd765450259c4e0f03b3e46d2e225ef1f06ae86883291f80b8ef642c6780b1ed43d59b8e0f8e24588776561333cad38ae38ee12be66561c78da7b8fd7cd2a0e3504ef934d2b1131ab8a239df8254923a74fd6a6e238438694ac3e7bbf43541c8a465df5cf8bf7f24e71641df675ab62a638a8ce52d1f98feb77290e5a3a4e72d25ef20d290e4354e7d2ae7f6a1cc5f17f8d864b88f4ae4a14c7929b5fdb326e6ea62b104043d808c5d1e67b08dd8f31467fdc8b313000840d501c7af6e99308fb13c75eb9c23544d0f27cf1c451862db795b93a71f439447db0c189a35c21438b474ac815c1266c6c02f7ae1a8d6d399a3848371fb7fc6236327174ff5e1f721213ce6d7fcc921aa15ee2482da3dadd9f77e66f894f633d4f5e34a5b21207617b5248ad6da93713c30625485e492ba668967a0e9527c58e21ae863cd898c471cd848b6e1ddea96a0afc0144071b9238cee4f312237754c4c546240e424e9d57622c4d529e0bac2b30b000481cc4ec9273bc30dd73b90b2511981194016c3ce23842497fc4297dadc9011b8e403c6d46cc2a13361a4165c48edc9a236c30828a9418c26511476ddbd44abf4cd8091b8a38482d757f9d92a55bab0b2531cc09c8e0028c30d49ce002604ca0c060231187a9233605cf253f1e93820d441c7988202924d94fdfb00ba5431c7587b7f31cdd5b47c086218ee3b6db59f478751eed08360a716463a92b5134edbc49888388c1628fe5fafc772645606310074136e78a5e9f0c22f7628c32ca86200ebba2b337ed53cfe60371a8fa7d15af9b2ecd03c4f1988fe69c6270bb0f75a104c6182528c3c7e0028ce7620464981370e165981d60e30fc779bb32e49430ca00c38b30ca00a30108d8f0c3d16bafc686cc9a3378fa709cd672fcbcb27615637c38d670ad1f5d2112427e0fc75145fa639818212bb8a18783f81a215351fa537784aa4660ee6394c19887a3b0d7f119c52585d137f0702813c532e7247a33dee1e8ca5228f95f759d330a1b7638bcad482100034fd8a8c361e5930b66b7d77e5117ba4107fd62630e1b72b88d387c71f597434cb30b2513b805b6d8c2043e86d118658831870d381c24bbd408db1095dfe5c2bb68d36294800b118411c6175a812b10700504aec000040a895102118411c61769828d371ca54edf99899812c3e586a3149952c7b451fdd8461b8ea5e29378c4deb8e0b3e1f0fd6f3eba2ba6edb2b186a388d6f1acd06d194236d4709cb62a67cf29de85d2175f983e301450b491860d341c6fca1db5245ffd59772c60812db6d8620b0bb802ba28c38b0d7c613c015b6c7126810e701180196c9ce15036a6cbeb36792a8e8a880d331cd5879e44fb66888d3294a2aba628eb621c36c870541937e4c771af6c5a17aa63d818c351dffac776486c9ca87a61430c59a7541eb9c984c446188e2bbfe65ec9e1dad906c371089735c49bd8ecb869c4c617dcfa9a49d974ae0d2f1cd9495db089af8b3407b1d185032bff9c75593136b87014d264ab562b4d39c7c3b0b18503f7aa0c1bd2ffe306b9d0f262430b47be53ad1acfe25accc828c3c9e0820c343248b09185839c5233d3354264cbf062045e7ca1b58185a36c17f364fdcd9ec7eb42e9ca031b5738d2368d3ea562bb096cc30a47551e652d2419f53a27b05185e3cd1bff2a567bac3418a60c2f38c130c00615a83eb031850d6c48e120ccbd6fc4f5603617ae022fcaf0020c4346148ee44c3ba4a5c66ab31cd880c2517a5bcbb0b5cb30031b4f38b6904a8be2fe86138ed3e4911d17b509c729a55e464846974d02b1c184a30ed1b9db431e09f737967070c15add50027935b5f744e1b09184a394db35a5a26a3e6d01400b1b48380c2106d9cd6f4f175d0f1b4738ec2f0f71f1b42b1070870d231c7d8ed48cd9381d368a706cd123b5a37bae4bd15d010db0336c10e1f82f8c26877094e34f2cf9ae741aa7f2c186100eea3359ca60317c8535061b4138f61ad3c8da6631edd38552d960030847d52126fc4d48b0bc651a367e7078d9d61f924dd20c1b3e38966a5f95b6b4d29ceac1f1a66c716f721e1c58f0105541f42be244c3c60e0ee4b54227fee965fd75908464912ebb3595612307075ddf9b2df9e85dcce3e0705266518f7a51bb3b17366e70a4f9e4375feb4362b4850d1b1c746af0d9beb58b8d1a1c7b4feadf90950d1a1ce4dad98e91f62b8278c0702eaa9eb03183a3241772eac91ba38538b61c430e218e3a4eda85dc64d43f8823cf9f5ff553942cad200efd23547bee609d6a208e73b28fb9b2dc144a401ce67b8e78baf51fb60f1ef5c371bd9b59a69cb69ee43e1caaac078d9af626850f473219d2e9bc8847b8ece1b057eb5752c6ca13d74376bdf1e38770f3706c1722a9a7440c77119455bb09480402c0c391590af183d84e5c8adee120445cf675b57bf4b4c3e16699d998d4cf8dc13a1c764af5a73e221d0eef35cd4d67a227787338ced9fec37c7c64b9981c8e45fd4dba2bc7b0d4e270686a2eed49f33eda0587a3b594ce2ac525b8586f38c81432bf3dab7ffcca0d47fa90192278d586434fbfbe9afe39569c0dc72e6979d3b3e9fabc868348b969530c799962d4701c63f927a6cfaba7a4e128a864eb56b4447b091a8e3dc5599c8de8caea194a618200cc709452a9955de7feb05418045086a36097e29ee5dc8200c870902eab64ffa8ceadfe188eb3b52d623369cee28b21552080301c4b444fda49ddd6b407c3e17ef6982a13c2bec7bf7014736b887d1173b4b8170e2e3fdbef8b65cf90d385a30da51d3af675a4ce85239d57179fec9683d85b3808d1abfa43b01444d7c2412e0f1ffae349eb270b07e313536889aeeb2c168e3fa5ed226b854ce6150e83dc869853c888b9d80a073b315b16cf3199dda60a07eff37eb91e2a1c47ae98e52a2667a9a770a096b25ca572799d8b14b82080281c581e0bf36351a2d20285a3b4ae0cba339fb5ef138e3cd7d4bf7deb84839a3bcf8aec1b35679b70e89a1e524f3e091a2f130e3fb56a5287bc90769770147453ecb3144ff351251ccccf47b08e7d351d4dc2b17e65bdd67c22e120633d44ccf811729347384e9ea142e6b3f89da6110e425fc6a9cf482f5f16e16832b28789cb20d913e1783a582c8b380ee1b862e8aca9b2536c8b42384c563a9ef3e618a1c4201ced5e8b450c9a5e3404c271ae54f937e33f380c1d3359be5c2652fbe038e44ebf8926f3d7ac0747e79a576f26e647cd8343b7b218521ae66da91d1cc56e468b3f9621d93a38eef0f09250520e8e2d86692f7bedc9530802c0c171a57ccb46caf9463b3738d4ed4c7e29c4c9b2c906c7b1eabd6b9f1a1c8418fb556f7e1317018200687090c7375dda28ff1f9dc191c549cb46f60290c1c16a6cac4a91211db900627050377a393f2adbd72d00181c9ccb84dc506962620be00587924e3d7d9ad5bc712300171cdf7ec76b780a6e49d38a83db942e0679d9bcf9c28a834f51c6e723969ba9ab384e1b61f935a93b7a5015c72a16eeb2bfef44c3541cd8ece578e3f23bf9a1e228cb974c083dfa397ca73814d1f87eb14d71ac11733e64d5965aad1447e173ae7c0f51f972487190ab6343c59da5d0d5288ec7c2697e96f0db5e140731c753ca1fde452d148a43df9c8e6f313ae74d7ea005288e3b494599b8b93b4bffc4714cfb9669d1633eed78e2d8436593ebb5c65ba71307314c9af5b49f2288cd89c398dfa6fcf4673ed59b388ea32986060f6d31c89a3848b1c2f52c993892d048a9f9ee7dcf60e2d852fa2037f21337572e712c9f247464a994435c4b1c7affbb9ea85ad48a56e2404265980c7195a6494a1c86d49e2c6e0527716ce13d224e779238f233d3ca5e31a2a72271181b2ad9c7ce49b3a4207160dda2f351723add896af188438b5d5dc9fa71c4f1a48a714f377948921b712439cc6f366fa6998611471a3ec2f2878b38cc15e1a288a3a82771c3b78938cc39d23a37aca6fb1511871d72df6396aaaf8c1ee2a083df5bddd746c8a4218e6225c9187a8285380adda89dd112296f23c4c1e888c479ea920dd9204892ece7553b411c7d0e1ab59348758654200eec36dcec66251f0f01e238b537766eaecdceffe1c077f32685ad9b684925410b3f1c49dd5897c7b40beff7e120236c48be7af3e13866b1b1d6eddff89c3d1c8a4d8c9c7252b2183b7a38e89832417bdf2e375ae4e17066433a06cbe0e1208799feb9206935297738ae12ed942f2475623b1c76b0fe984919cb531d8e2356d60afa6d5d214987a32449e72a559cc3d159b4f42159ab679a1c8a4921c76474ffd20f681187a370c12f66cb294fc6eb085ac0416fd79435ff063bb3d747f4dda0c8c67aeadbb0b998f8ffce865bcfc42d89e5ec6b3827a64aa49025e36a30f676ae054f73f134e8298c7464d2d1e04f84fc92f3331ca8e56fe690c1a26d6e062a4f7542fd46b14f194e77cdec519d31870cc6892198e668318627b45ecbcc7f3d5a88c1aa30b739a7b71661b0bc27c6b9c140dabb87103e97fd052605cf0f963eadec0566f4829ee4cc1d2b77817c5b1d4fe6029e2c34f4cae9c95b28f684c7c9d0295f8cb5405cb5eb94fa353fe12c50626146cbe5eb7d838533478c61b3588ef93657387cd287b9e8b2adc08ae4f5b61039e4520564d52ee9bcd5cd4b05de6c3eee8674b92964b6f9d22fd8064b490199a45b9a3da4c46811854c23c7be58f8bf40a134edda3d398dfd5c9eb0db6d8cd709e734217872abe8952d4d30a56fab182ca6a58509e6644d87a73c31e525102635f59d9c89ac0422866db647db3c4d12f8efd8bfe322e1b0b3657a8ece123a5c2eb438c2eea17eed3ba56e8df077f028d94263cad416c1149235fee7ddd410216dcbee901cc1552d8670b23c8f3abef1a91642e8642e5ebeec99522d8270e65f8a9de4b6974e2d8070be8f97e47e67ffc0ac9a318ace87257d60e467448f75d7f6608921578ca6163cb073a76a3f679a9fa9c50eeebc4e99f37265a9850e9cb9b09d69e6c0eae41d9752750707c565d590df6dbcc11943cab72c0bdaa0d457e639895ad4e0304b56aa129e29895ad0e04b3a3197e6fe0effb498c19d431aed8976f5692183f29cba8ee5dfbdcd6911032ab746c829e197725ac0e014726589ea1452ac9c162f58626e4ebca22e8ba7850b4aaf3fbf89edd7b5c2d515950a413656d86f397a8cda2a0e5fb319b3dcfc97a68a46bcd24f07cd525108a94c156e6f2164a83035697a0c6143f7750a6fbcdb93ca8598e3650a33c74497938aea54299a1416e3666bcd96278569b329fd37add38fc264f1bcd24417855f975dc59365fa864291ffe4cbaccde58282cad50c5149f737fc04122b5d33ed7d4e21e4896f7ddf6b346f759d784fbd53facee939e1a4fbaed59ba0ef43dbc284945813c6e011424c33b1578a9b4298206d8c692e52c72f4188398748cf1294b44bba50258c6351f53743c948082576fbf288eafbb1f2320926ce44952d092ae73dcf712b7fa81909e37e3edda56472fe42e23869ae46c81334b41a8aeda8440ea70279301689c4e16040144861e171015314000000001414862281402c4b637df601148003512a1a3e2c2a141e1c0e121414121e100643616060201083c26050200c0803c221713132c5d503dd27811ecad464223f1da44db89bf03621e5089b6644bbc918837db09376505c91b648237866b0a960e9e03461135bf52dc29a34c8014f20171c2cd79c5ccfd2fae43303c2d22332e29cfc75303abdf04a0dfe0cce518315c246d807d380646066d0c120dfa09a816707fbe20c1521e18165091c27e449f0baf91168056a14d2ef517f27a0c79e37a55664b352fdc6d127d02f59c5518aa7142605b74ea93a9d3401dbc3a3a68ec89cb6701a9b91d74909bc2f4a48bc0987828112a23767da76e06fd2a7546fdf58bb21efd36fcde3bc95476f1039b82a685916490e045c656d3d0cbc2438340037aef9a856cb303e38336f70f6c02f827d071f08181ca824bc9a1043421a42aa81871f6c1e2ac9190d4d98414658e1a0bca6b7ec2143dbc6257b83f95a7c03c57530b909e26029379031b83760668035b822837d26a180c1cf8096c1d9e0f541924f1873b2f397c4da80cdc06070f50d263b4ed6c18c09ec08ea1f182108767023844dcc776b50fdc1a6657847ec5c2701200864ba64d9933823c844ca4194f186f100c68ee863fffc59ffdbfe0aff8b7e220009c078806e805e808c00fe131038aef12ea9080416d017b8362046a02d004ca31c3207bbaa1e6c1e101ee41ec11efefd637b5e887a8d21d2eaa1e861e4e1c9e34e1fa7c849c626351fef106447e9f18318e4018fd33f332b9accf120492569abe8e407bfcffe64973a8ad51448a510093770dba2162209f766cdfa23ee46a8f893f2919ef2518450f853b8d90a7079ca195b5294539c73c59f901455dbb120a5185c61660b117e70cc28702436153e0c200e619f05076eeae11744a488f0aa62f769ca46756f8b10a28db869e25302a61120856db1af0c02a48e8801f457dac41adc94b3b6529de4c17ea1b9922c3710101ef9938520e9e8eeefb9841465976d8d91eef24c1384a6645ef5e7a8a3a3f0e3cf48a4d16d703fa5a2c2f9d0e187dbe7e6decbfa714f730115f7686937cf4d8ae307a63e4d6dd0fd6cd9811f2d5264f634cd52d59f71e58d9313f3c2d5ba10bd9b0cb294671ad99be1aef8f27b3b2b83b1cf21ca9fd5f776054bf9b7dab508ea3bf45a5326092e26ee24fcd06109126b777f7552e9edbc5ea5e615aab5afb0badf3b5908219e4c8ee04b24e84e7ed72d7a1802b9996490dc2f59e5052ecb93494c12ec2e7700a5d558493808b37596dea30a74b96219bd170ab05aad9ae07590fef17c49d99567a4b6eaeadbc46bb4b58f2ee3e7fda11372933b515ac8be15cbec3e565bd3ce2507c481dffb5dc9758e82f74946f2c34863162375eceaf1fc5dc9e127b49fbc3fee17b9cf4c72bd0b26f01aa2c02f6931fc0899594f1aa9721cc124a941dcbb87faddc8e8d34bdd9b274ee9ea31aa473e81dfb0bbfd76ee1d16cee38e6325008a9c6ca2c74a1795139abe71e0d6890753c9cf6d92f6ecd8d4c46f4c5381737779c208cd219a422e6e2a5769c9a72b84c46da759c2a180e3fd4774756038896e236ab5637843f323f97df09fdf032fddfa2beffa4d248d7e37e3994de1f657a92aff65d7dbd7f34befd7bbc89b16ee79259fda4ac9e883d41d2e5b19f7e58e7ac9b10eefd041cf6719cf67b68d1332ee51439d87c0527a3fb8ca37801ecff1c6d6f2b9fb64cc641535cbc3c0a8fb49a111b74a2cc7a24cf60ecf3a68b795e96cf6915b41d621fa3d893f8e5f001da51187b51101f7808a581d22946a6f2901bd16804f4a1368529766b6f5a15779df16f7fa1c50578bde9910ae397ee16afeb241bcbca2f340bbaad7571c07262dc598c1b1e712cae5a5f593c8ff390e9071a7365e7a60ced4cb0b3f8b5d240d9757857fb4cc8cad00dd5a589dd7849a6ea10b28559bcf44331885d550587a71245c29d293b740303a31ade02ba5e367bbd73dce3aca60e4be3a3ab6c4b3b601c0113913e37ae7799e4fdad080d37488a0d1ddb8f69cd2e11ae160b42b65fc77e9726b1c82f1c327dbc7b7f87cc8742940203ef93e56fbdc8e0bba11465855168a01401edbde726381707b6c90098a27421e5bb004a18989f43fd18c481deb706837e1300d1dce765fd09b1e97262aabfd470ee12edc4c4bd091bd44618e9fc083a1cc5cb4c64a487085516390502d0aa0b0c35b2b975f331d52fd635f1a3a0d28954812a48bbd6c1b790919c6d0be6110fde1ae09a8bf42fcbf0e6745cc200a00878f0d4f85c6d82a983e24db7bac0ce8cc20f7f1a6413f5c353ccd22e6e0bb1b093326f2b76c1abb0cf2d077841abd0a11622305c3e88ae2afac8681940fc8d1e3b93428d0f860b6730ae026fb2508d3292a279edf4c3432a3379e4de856262cbe5a92386e499bd0dd9346c5e5013d41c20a54dc3edd423afa6c6b3f90b5e14517ab172bd6b7ce0c92740c41ce09a2661d64238542c267de48c5d8e16fd6c77dd4c434b644b08d0ca41f2f6160a99f84a9173aad59328ed18a7865b67b4093a49f93c776e758e6681024d4c196d13d4bf478d459b1546d6d3eb97fd3f84cccd55878c55c445b956918bdf068403366118861740197a832f57338d8c719bc188d596c009d5635871418c704a5a6ca00622ce610c90f4a213a01e848fdaa8991443e6f69dccbce6afa502f3d9271da449127959b25c6b161db1fd9ad4d2026f776aef0a5ef5a2a32a1c579af3ad85c6069109cd5cbe4c7f0ef15ea8bf4b8bcad720df2ac2eab5a43973fbdf9accb66d8bf6c7543c56c63420dcac4a58761815382cc091c8d5f156bf3048069f8177c1581146110c10fe1992600d4610ada589560c0c2370bfa0aaf113831d3f31890f290398f58ba0bc92b33de29035700cb5fb4d0ef66dac0252c2ed8e1a84e62d8df365dc0da7fc8fe85e7c6f64fe97f01fdd58b41fc6e59ff0302704a7e6d91048032a95028e81a4e9085959b03b74a6fc81f0ef4f460f388980d17149c4f99612f8a00cb10bc50898d9da235c07a5131242e6ca3e0b08f2e6166704f1fc477d463210fa4c909231d62c92c5821c57c157504e3d5436e2b9de0f1889cf68eb6c2a225cc6699044fab221f8fa75e219d545199aaf515c6d4a5ee12447f7aaa80143b51d4f01ff622a2d4af5bf20eb2fda988e25d2260d8314e5eae625e8d331b5bf7383a5135b6e09cf45c9835a3f3bccdf59fe9b35eadb13e1dcdb6653b4d9a94008842458d40f8e0dcf8446f4ff51ed07490bc047df61f5af3834ad28c6001afa4f62cc2169f8f053dd088da2760099cb9a1daa11c1f299f386725c2821b538aa02c657a346e259c60851d08a7c59d739e098ab8813a1436de750253df2ca4381cd60812eebf332cb7afccaa3e1284aa36c2a3b34f06614fda8fae05f568795a9378e31e9b274d40257c66d91d44a1f3e474bc00a0dcca899795c165f0921842a63937cc355526c78b06b10f5f2c17828acbd8296ff346606ac3808de57b8b3b62920c1cb71fbe8433f4a5a51976f78efa109a645fcdd04bffd17c04c31e638095295dd80457144c3adb9104ace2400896e813f27a2352f4c809fa5f458e4edd0601ea1dca3226202f0d628410774d66c426f217a48033d7a858bbc408319c35889f963acd33c6a7d1f213c079fdda76d30bb0e60e2691640b2ea708006413252dfb896b17403ca512394b0054d43f60254605e547e1e33f35d5edcee8f2eb4a81b7c45007872ea944eb5ebb12ae238dd0bac42380372b0cb59edefa12878cb091ad584a54bc12420778d9dd5836c8c485eca46f917c110a57760c3a1627aa1dda02d67d604972cf1ab771123311e80a1803b5de1b6b67536cf7ff4cba8ab4150254255c52cff51475aa03ec11b232cc686e7aba3cf5889e91f0ff29d847d52968fe6f81650828da4590d5bad1c9dddeadb786529d6108ae2f28e51284a1c0675b52994cc2e6987f014bf22fd5200655713e267463a371af5c5e7c8594f4b251c431a0cec258aa1dc4e63ec2d281693fc76973229837cab2aebe61d0cfcb322e65238cda88278322a67c954f06ec413c256ddef1e928ebf0319f046d1b0c43ee3c256f16d0822a1291880c8098b8f735895aa42c4badc0004aae48ca6b45f2001bd5e4cc3f7ea130f42d8f243502c62b02396ce759e272ee60c189c0c1aed80a36ae3c21778e3f07c8ab3db012538a894d5332b3ade3f7735415a5827afb2063b79624ea56e56e925cf7f80abe5bdeca6333bbdb10cdc9056d588b15e85490c869be8ea12a38ec0fd1cb87eb98b8ef3e76c15733a144326fbc8d412efe284b91c1b86f217627d2d0d9865606cda32bed71abb95d41e8992ee4e44d5c754b90a671701c00a45be2cb36c24a4eb95e48976332a56f1401f59a1b90712e9d9c039bb1a9805e1151dc55b97806f127c1092566578c3fd82b5f4578c1eaacfb52807a42836e86f38728abe82c2e3ed576d62a29908a353492b1c4c98e599b08e8d80b02c7e538e73e6319fe593f02975677870e5665485595ca0f6ee48d14c9e2333a6d50c989daeba0e80e03ccae9d2d0cc6a45aacea04a0982c485af5aa40a42c2f1e1758606694d39cdc504404064185fc16157b5326eb911827f41d7d9c6078c3b8723c657c9b5580c13169798871dc8cab07026dd65dcdafa36920192c5941c4507ac977d5a062e147b5a39fc502defe9fb049ca45e749d3093b95a4c14f6a3984da382fc0b7ee5c3c605f0bc4760aa80e3f9661e02700892590c5948106357d5fef4d55fc3830e518f1a6c7ad8c95953567e49fd23ab61e4e1f2c3e6b424c2404f95dcc39be5b25f81e0db5cdfe6aea333891b71a79f26956d5d83ef12fd04733829084258ac78f533c28ee80a0e2b3d4b94cd940e319292f1e06596feee67bfdd49cfa855428f6831ff20e4553a7f58d446b6c5d9cfdeb219428b4455196ecc5620142fcde9aa518316dc458256768e6e099e988ec075eec9f88bb8f3cef5bc594614cd6247b52c1b82b4fc25aac7479430fab9f8bccf0337582730510b7b8b469a54d1b6b347593acb391dc0fc493feedec1033482544641db7f7092a678a8469ce2895e8e0a352ae6eb81b23c51ee3f9ecc10b2f224bb8215d78bd799b2274a693d377da9fc52a1f80200ba6928c8fde84b4152ac792688d833b5a5052ebfaa15deca2a67a270881adb166a30ade07bb55239ebe5f0eb0c1e4a3f02a598dfb58f74b09f8399689346b9fb5c18b4bfa96756925c2665d35de089713dc7018adc788881a7c4b1033a2d24b6bee7ecf91fe829a0410e80bb48f2230d1b801b6360ca4eb88721918c8d0251dc8cb99b4ad27055fac3e872d634cafbf87afbb13e3e0923df632f968f58b60d8c9fc0c3707c6942123c0f87e3f4c8bc54760ab694701bb85a914334cb9a368b8baa9bd047af4c36a5ff154020f966688fed0d7a65737993553b59c0947edcb880374c9e1b972870efd733e65dce1329ce9a262a90307c592b0f2ebed554bbeabb3d994ac6f6c506b368b4b0c564a307d85a829231624060300f53ea0cdbc12ac53c7412cfdf08e07c4f367ed79f59f8f5e86a8c6d3f6e64b1641defa0e7854a42e8064fddcade5ac89ce6be386decdb7f790d823569fa985493be0ab5c122a4490eb249b5abef836a6440db64b82cb478e2747da7694ddd7209c7810fcf214ad4a74bb1fe939d4d18fd846c604f65674bba2a32d4a04129b1f87f6dd734ec393dfaf717ba9f97e403363d7736d7dd4640ac4fba7c0ccf2ec2c81ead9326e3ddb7a943a0bb0288cd9dcdac90d11efb4e177b4bba2d27b6d4f415708961a6242b703696b73587eff434b7fa373be9f5f724a90aca72b4390ca8829478b40e54aa0c0bca18abf53f99acb17bc8009c3218f8fd28856c7cebf337a5033928201315652a7eeb88be8e72e59b582959b3d218b61a3416fe7d39e6e69c24a8f4538f512bdea90b45ad8361a341c6548936bc380e98e8a2f452bc5d2a1e2d60748dea2d155b0267a3c1e15c8682d08a601646bd9f32b4f7e37c35e5bfe8cda7b019b9037e9319944e1bbd12cf7ff62ddb100693d708f77ee7ad8e217d8bcdb21749ec41d15475602d06af27fdc50ce04a643d2411a9189b8a1b28cb4467c9b87de20362c820b567cb38dd8d49e57e9d8d2996b0be992096d604cf2e3c306667a97eb69d46b807466876ad96fa26cc3e3f7f7b2fb9c9339aaf00a85d6d1a6b7c98cb7bb621831216afeb009eb9cde0d5a447c6c797e4e3e0493121abf9ee60b0826a14bfc39d164e259c9a6137a133cc93549eb2511261540a23af6346e62275181240f35432c51d92fdef7a195e09eaada63496efac97af7d8d7c7fbccf89bdee203e1909411b28487d3294d8fe43121c54e12457abcefae9c087c1e561e3fc46389544a4e308f63f67847d6d49b20e3f1eb31cc1e23b9d4573324f4f0f6d8e58362109c917b90668fc6f26309f9575210f2111b2127334fa90290933a008be2db1ea73be36091dd406b34e9c27198cb486729d6b88a8da31d4735bda1412f8dd2b479ac882b4fa379736b1e9accce1c1dea27902944685554e1191811f70475497f26ddc7816395e388996b7f80279d651f836a68c9024a0983bb08f4309761ad01682ae2ba614b8683bf1ce9623f97516600d282d36db8d20f91b0940e1a4d6eb365c29824bb20ee6f2077f20f7e89f84304858c26d01ac57265cf3504467aa693081dd0c5fa9b557aa03a411c3eafc41b138a0c956ee4f188ae38c8816ffe2023a59d5c547165c3f2187ad35f845695a95a3b76374bb15ea26336bb40524c506d3a8e091405736f1b4f6f3ff7ce871071d7435711b78572e0524a9fec1dc469261e9cc54276a5466c3571fa14f6bbd1679d819dacd2db3fbc178e916dd07074b3fb23afcec126313973568d944fb152fe8d5d17760dcf0fa296af4f33eb4d676ade974e103192ce317c1aea447f165543be8ebc11179ccd222565ff03c2decf3a2725d62e1f0c529838d9a0bed5b1e79e9cf316a23b4b39c5f71110b41a568f45246d6393f70419725757a480319703f587707a1838754c22c258021605f19d8b8f39f171cf93caefc00d3cd249644959ed0f9af9408ab6d210ad5e543ba8a2c0777ce23f3b1898d0f20eaa8f7cd742dc46c0e91842963eb78ab9abfb972e36c9a3e58cd10a4ef9f96c3f88f4229b0ea1538e40f934c8a28ed66ad1423a1e01cc703686f8325823abc051cb364eb4cc192dfdc89bc8a6c365d1fe3f1efa61f2cd1f981802107d39fd912c234f056808a688eebad2002e85dfba2fb545b093c3a1284c15efc8d0d576d770b7a003eaab3683b77e2e8e8ba3811f85e1a560b41c59f1ac035090534284975bcb65a8fb2132f7ce6e02bf2ddf482fe4cd25611d60adae3fc78d8df4620b5ec14d3e07cdc0336da2b108154d91ac94c4678d0e23c1d399d32f73159f06137d6135fbd14b93788a2e1fb37266fadf75bff099a65da7231078b4c86f79994281578788a00902c215f467d7dc280e877cccc92f3378111fe64a46fbfcfb9ffa4eb5d2c8eebf3b646bbfe4f04feb2415eda1ee0a2590f81c6cf3b7e71d84c61cbe2341ce681d854cc68582b14df18bf789fb4eb175ecc1bc2305c6c076ac53bbb9ef6b531589dd6505b0ed2044af82e049bb04036875e93b90b5bacead9ab1ba79c69992d3c9804a8c7b9cd17a561a4c35fb106b160b8bcf0d5b9e4a872e0326ac41e636af0695d8f6a52ede33b312479a6876003e84ca706c677438172ec0f5c5f71eaca8e7b4ca0e3ee6ca6b51546b953effe36de0f6553a9cb20a9c2aaaa6d8252801f3efb884ff75c494ff40a9587bf0e9c177142a3b9c78044cd79370e8c4f8a21f26e72c70c8f9153adcc0ee988e2a0d3c985c7c0c3fd41cc589a52e36d8cf0ccc69602f3ad49f791f1381b29782dfc11d227771ce62b9cc468136be8eea633bc1b92e9c890541772b7a97de07c91c35c5100910ce52fbdd6900314a516b561c878ed1a222b890178c1a1d23525baf61aa14358aed1b695988ba955087ed67c55e5b672603b6399e8e2d5cf7247b065ac99d0ef64d441ab5c7c6cdaf2ca9f5e09a0410f2826056b1243b504798443d4916795395dca3d61d06fd0c72677f412b560560d8c0b870aa05e41fb43f4e79a4c59e2e50381be8e0cc42d95f564c9544fbe55ac43041da584e009dec7acd8cac89abc76971cbbdfbd858ee5d84f159087dfa00b44b6810d4bd1d119738bb8f00ee28cae74693b9e187a3b4cc9ad7442645992222dac213ccdba62a11171c95992f53e5048e30a9d18b74bcadbc1bca24906b62b08a9b0ba87cdd5ac1d8f6ad414a0ca6e6059456ad6909198cd020f06b0fc83963fa88cfd4c9fb01307f10e0837de74787db13b62fab28ea3021396f2f0787e2f5246dda4328c01f7cc2795541a79f02f971a40d89e68897a092487072eca84b46d251a510524b186b3c5e46d64a9bbee03fbb7df308613424c577dad82d9ad600720555b4d3b3629e2d4d7327ed0bb8e3d8e0470eb8666703cc42841de9191f2f6ab237d8471d028bcc76fc195400095e1b453c2a399c4599ad299497f2c96e172075916f40453300159a7d53a401908ae4c4bd14f1f21484a29afd9388cdadb8a75729ba110a89ed641ac02363fc533d59f1d31f242028e3b964d3395ff0400d294a53d9a95265e79c9a141ae695483bad43710dc15bc19a1139c08e17fbc3bbcfb29adc241414daf705230b8a592ae295225ab0a8c085b20181c8a4d0910c6752dd06b012a89728cf3564ea2fdf094fb8b289b2c61d93b504f5bb67c050fab294ef779d33e473d489ae462a0488c0bc0c8015afd5f707b7022359588547f5adea36c432542a9b29b3a52da93dff8264e58200bf9435d888e532adcbe000cb4e1af7e7c821bf53ac63e9e2728892747621f833c328ac884e4504e25a69da60c85428307709e38eefeb6e97023765955437acd6234c5db9d179fd28339154dc6be1cdce57c6e9109061c4a41cdd4d3f8355a40d87e63e5d0a52408fc07176b0d90e10a37473a258076d858120a77f8a4c59cca0182b475bb1f5573d463fae8ebe634e3e2c02d3c55983dde1100d13540f3dc31290bb31ecd2ceb1d8440730ccc8d9082bc7a7b1dcf441c0974b5811404c080a05122b0f1c8bd60f0094c2b6c41a6b1807a0cee304fe13a8dc6f8a90383805bd5fab9944d8178f9d9a8e743f68074560328ec8008182ad324a1cd4801ab844e0c4acf421c7120b013f9a1ac188643063e7a46081d5c6e8e61abd5a0daeb82975345bd959837375907dd1efb5bf955a57d3210f2c65688dc15321872aefac90733dd4a394b6de8008a082a50629cc343a77eff84ce76389e46dcf5cacc629dd4cb28fbfea41ff8553f7e481017c423c91e052d8f1c9833c93874e15223ab32eca3abe39292b34691203f87f3332f5fadd98876428b242b682305224cb43e438be0aeb6a43e59d7bc35bd6c6f1bd090b9cdb282e42ae0dced789384aa2d01dfcca3e1134fb09af7908a4f655492cd59800f4b319d627f2dc007717837654a985319fbe59063f8cc1a696ebddfc94f71d797332dc919a53e186471caf2ab80a4b1a1a534c23eddf6d9ebf2eede844eb809ba6391da3de765e41ba888ee68ec55e603dd554d4150ba54880903ef9a602512abc2ae1acb2ce81bd6cf983004cebdd15425734b987ba603593449a4d9e6c136593f2ca3f92692ae0a3a737798680255d264f3c5a9349204062298d59e7005ed49881eb7a8fdb225551910630b00993e8a2dbbdf98d5dd98f5127f36c25a03a9d38e83a8c0b6045b3aefb95b8fd87a80b84486e38d44a0752635734c6b50608220daef1cef4bc9a3c4ca66ea83475e79b1d367a1f05ea417000d8a97428bcde4758e3a21050500a3855a003330d44279eaf051753b6d823c57d3d62011ada10b144c8a1977181242b2cb5ccc92566ebbe8b36471283dc47239f0933b272354e1a818f903a4e7e663ac8b72a911da030a4fcf75cb641dbe8b7285dbc5b55fbec626432fab2f512d15dd99fd0f5fa6dca6f6610a4642fc4591cef01bb9dd3cd976d4092c4db8621776e9ceac44315f38ef006dd47580454c068546ae7a01ab5f9486a58b81eaad422ee97739db1bd02c97e6daef4c4a11f5b228382cab1adacecc4bc11d85e1a399d03f209ef056c37953429a70fb81192ac00ff0f24d40d282239e119d6c06cb14bb0eee758a341ebc4236593cc74e0a1e115d634e56b606c056049897b94afeb0b010eb3a53cdeebc0e08cf5765b111970f493b019432f94ffe7318266fa1adc147138385d52c137194f76f3fcbee4e160c691ba8f0734931a38800fab6ad12bbba14aa516fc03246682a85c095f1e24e51cccb9ca453a602cfb5b26df07796a8c7b1701f791da2011ee4c3b6a2650b266501d9ac389e300db9e19374319ad726a84f9ca885ddfea18e38cf18e6b306202ee1da226de0e9c38a507631125ddc76da756c86f14437cd7bab541514abee48d2868046ac3a3c1c28bd889aebc37e9420a8d59a64b635440ceb44cd19c7cb8bc47b0abcf30939085a2248e5a900d8bcae36c93d9db60fe1826a120ab81887faa9243069e65c4e7a1cb70ac245f714562448aab28c6d8ec9e17ed82675f00cef408094df71563ee1106f5132f55cb6e40cbee7cb3ee61b6171f5da1ea69a2ef7cb08ec70c7d78295578004ef0bb989107ae0c4746d36c2c6913d259393a7ed3b8c1f183119f5135a37791750a279c8c4ac46a7970958c631bbd3866d5d89d476f3964f375156de041987b68a08290834aa40c7b8a74efba82e416b0d653c9b0a034d8fc14881480d61a9fbeeca975831c345346475315395d3a0c8e2bfef4c2bfeb1512390d0fb7a80e7adbeb2904b20d2f64f89102e81749b80491a1ecdd0f1e46042dc23de92a80b0ac8bb4ec6bb11c1ab20c086ab5b4e7036f282b65028749c6915c6bb778811fbeb9295d5e17c24b94d8f32a14ce251456db1c0a865c52b8a6db97e70ee76b2eafd42cabf3fbf3dc30802cf75335b100151cc14dc0cbb97f26a05b24b40033027d8b17d1d088dccc4cc62642a7605d3dc87a9ea9dfd23b276e96a60fcd73ddaca3861f77a85bf4447b25c7d94752468ab2f23b9871e3044c826680d5abc3b4fc7d72019ced1881b61b4e5cd2123bd7fe384409e2dd99ded6f6b6ff6a141c7704773c03cf017130e3b7db3df62487024ab02d3d683ca1ba6b865a9f4368d6e52cf63e7d1fa0372ef6f9655499b227a7414f0ca3288612562450eadb4b5f73ba1e265796183113d2129a9f5186b2e12a1add7c94414a39a0208046b6022657ea9bcf666d20119b086dc4cbabaf53c9d24aa2fd0855b0157f6ff036f359505a4920187c472e284e2568db416d08a32ba4411f0c2a0d6c06530665060dd06019e76d4291c8db544c1c47027ab3c7953870b0a490033ccff33ccff33ccff33ccff3a4c3f82cfba2012ddd95528a4db68fe4787b654acacc94767bdbc079d6064ad6064ad6a2972c173709e90906096fe0c326d323471ee89987165e7091812e8c1714d0d098e105175f94175c7ce106b6534ef5c12aa859693092944ce9a21a6d604d2cb8a7450e11181c34d8c0a69021c71392639cd4a0b106bee3fc8e27a923a93466a0a80d1a6a60eabb3dd6b81e4f48510d0d4368a4818d93a38f8310a9e921ed002cb4c0a2015868810503b0d0028b056881850282a08106c6cf53a447f5719c7f730626c70fd94166df0c7c6aba8a761fe4fbc82e031329a525c494d52003935352ccf7749ec12391a03106cea326777f485911aa1134c46059842af15055c2d4ac2255cacd62843c41230cfc5a8ced71a74ad9dd6980818f2d66dfbb0857e30bbc57cce97b3babc5cf16687881cb39770e0f935b5dca7581cf7bbbd61c6a25cb6a704135b6c0648ad261ed66010d2d1440230ba581056e23e55b09d3c8a0710576928795be9f1558bdf590277fd4dfe4abc05fce1d586a123bcb9c0adc45d85360b3f92611894b810d096bcd9fa28792de28f039cc2a2cacda4e5b28701d713aac723c674f9fc07814f247e6b40fd45227709bde92fb2c47d13f4713b89052eccf2815426b90099c9f5fc668ab9cb20397c0789c2c91337d68a239257015e3aa98c476d1ac2581cbbe9f36b790c06ef4b05c9a5287f67df162bd660f6254b402533ed03002df31460e42ac4e4d993f81171ed0280223212de264ae602499e782043370031a44e03aaaa895e81c4a8e1c20d0180253961a429d94da4b15070d217063f91266fa0902d36b19b1376288521d2030b93b0e2d6a073d99343f60cf527f9e6ce271a5dd077ac78de91e7039aed4ae46b7acee20b325692fdbfe76c0474cf3131ff1d8a3e88049dd2959c8154cabb31c301e07cfaf29830356efa3f4216add80d58fa38af162fa38ac0919346cc0791c4808cf8e7f739c1ab041cc3d4ab569c077f6facd0f351d3e03b2a48f21dd960167395a58e8e6a89cc6800fdf835a87390d18301243446b115fc187fca11fc4ebb882cd4c3e2989755ac18a7a9c232973c7c80c2bc87457992fb7ba23cb2ab868d93e2a91510513246285942226154c5eda8fc33899f5f247051fe5ab3009effe08fd29d8d50f5d93987e4c216f0a7e3c2da492a6145c966c63712f6a5a4f0a2ee6b4a6a0e3be9a8f82c91dae1ba17151b0ed5178fee4f9f1a91e0abe3b4ae594afbca27450b079ee152d1dc729e4f04ff0d51bc72b6bee09deb5725b8365bae674825bebcb99030fb2959c60fb737bbcf3d1eecc2618ebeff8d2de45cfa109fe53c8d1744727afcf04275231bade8b09ce5f6332ddcfaa8a964b70191a52e2c7d3bd61b144eddbc927755482cfa167caea37091d4ab09fa9c3f1c9ae14b23209734eabe017edd62209c6efd6362ce47ec44482dbe49fb14a8704b9b732f75e86b6d21ec17e18729cdd2e7a67ba1dc146cccf412a9fa021ec4694cb03db3082cbb9357e54ef4bdb16c1a7e8bb2966f2b073d58ae0529969f568bd552611ec8464493a36bd8f5310c146aa37dd1c07a631b287e0aeec33846e0825a64f885721d89a541ea79a7d9021041fc79acf23c7ab299641f061ff5175fcf45d1541709f226f8e730776b90e049b5e242b8ab9f77800c28b1eff7ba8903bceffc0ea76da6f0f2f62877e60cd7255faf4d79a26da073e8a389aee23ca07c632794e31276b857e0f4cd77a1c5c4b3db09731628c7439b47f79e0d424ef8744120fe46fbefe0e7cea28e8e6702547530f3b70ebe1244bbf60d3c1a30e4c59e705f12cd281f1524ff9d133376a9c03e71de640cbbb445244b453485aaf9b348d033b113dbbc34ba96c85036f9562471dfb062e749821ba7854896ee062ce1135d7f48510b6810f2c6c22a2a71c438e0dfcf988f649c8ad8135ddd448193570a6db21b35a2cc24f0363532639da4c13394703ab99d3e4ffe50c7cb486142d5accc05df6b1fc8fcf2b65cbc07ecedb710739b41e910c5ca6945bc8098d971b03bf9252c8f6415f6a4f0cfcdf5977af785c71b230f0d92dc5ac223130f071d420751e4fa5b37c810b413fdac3d60b7cc6cca9c374163c8e76818ff2a6a5df8f3a8a8e0b4c87529ee3fa6d0b59effd65ebd8a6ed43165ae052a5fedbe4120d992c302107962262d6ebcb63814bf13abb459fdecd15184d592dfee6a59da4151889e943ad60f5517b5015f8968ec96d3799b9870adc6578dc2b8931477f0adc9dfe6aaccbdf624a81bff4f1a7f40995d19128b0b1a3a421fae4654879220b2870b93ab61452d6b37842b9bb52fe68cb7702d37963ca802c9ac0fa76fa7f4e31223aca043ed2a94e6fcd8a1d57592c815d095539deecc98ea612d8e492dd512445fdaf64649104ce76d52e4eec0f621324b0f93bb847c8e208dc5f55be4c6963842c8cc0df079ec314bf3645acad0a5914814dad51ee5ad61e1137296441042684fc5635d92395854360bb628a9cd683c7e88a10988c979d25a7b3eeffb20802539792393615089c76b9b9bba959fc80fd1ca68ff4e69162c7b3f0011f5d4e6cbd98b42fdd59f4808978c1a7efdd93651546d217db45163c602bcd3a6d6cff20f20641163be0ce52c78ab9b7534ae21647163ae03fafb62e65bde6f08491e4458ccc22074ce69f0ecfe3d8573d471638e07fdf626d86e4a5d11059dc808f7b16d473678a13a92c6c4054b4ffa05248481259d480d33ad7cecf9b43c762b5210b1af016a2071d07dd6731033e3c560bfe6927642103366acce1eaed7928a1958108862c62c0c791985bee847538b1222d64010377daacd202015ec1c7ef1f378b672a893e1908e00a2e7614c7243b8e6c528700ad209a076a79a304600513a1afd3add722301700030156c1c791d346adcd174d827d0201547179fb985ac81f74ba54b89b5a7cd3dfc355543c659ff2f8e7b39c428f5323e8e6ca080f9ac2fac83b996847da4b81c4dfeb0e2d52fcd61d27744671d03595923253145b08939cfc21e9070142f178243629766619014041d6209635887ef009f08973a8f92dfb3a9c009ee036af064d1fe49de04bafe3d4714e1781009ce0573d2f63ca994db0eeae5193c55f139c87a196c2c31c9607954cf0ad1695d42b68ed07135c8885b4a9332774d825f80f4d3c94456d096e3cfac8fe104d21a42bc17ef697d841524ab04943529446ec8bef31093e8e6e24244991041f4b08e581e46f971c4682f1142121da7290e0f5aea3b60949bb9c47b06192c3d3e28ee06388901ed75f62d01bc1c53efdd46d31fde3388ce03a8ace91379245709aff93dfa733d18d5104e7ad5f153dff89e0adae537e88b839fa23822f0d2179ff73dc9bfe0ad87d18b3638bc101ebf1df927f9c2db676033e69645d9e728d48d980094956398888c888718e08901af0ed71d8dd92e6620b2206101a64e5716c9e69191625a1d5313deccae36e4180cc80499f4b721ca88664c1560191019772b6e4a569315cc8c8c01724067c90f437d2f6ff91ad5d1018b0c1637806cb1c09621819f90ac42f4ef96f7eae735dc16d4a6f3ac61614d868c5e9b96e4368c998333092cc062bf86076b92ba5420a9d55e829aec65455f52055b091b276f49f1453f79f0a267ddecd96089a72082a38f33c0d9fca39051ff7b5d84a889c2998643b9e9fd293869c2635818d52f021eb7778e8231ef331ba40076c90828d34c9f996a38de9dd517029512f744ac1df1005d7977ddd1ff6c12d9711630bd3051724e8828b6bc0462898289b7897d5eb3fca4b1043c689c0023640c1c590a31ee975ccf11a01116c7c828df61d42c5ab2bf4054692118162910106601181126c7882c9d17bc4f3e9956fbbd1093e4aee61fbfd6f7082cb083fcfe9b1a224cf26f85ebfff2855721869a309d6735a0d2d95c17dcf04bf6d6621c92d5f678f09d63b2484e46b2fc14d8476983d5c2dc1c6ae8c394f5909fefe2a5fc78e1f2c623092483728416563122b898d48f05148d1723c966024998fd1c5160686b76b6820ba610312b8f108b6628896db6fa37d593547602945f4844a6321193166cc30ee2798a1a131c388806670a1c5db09b0c0a2d409ba58c17fd1245880864617e68b2f9a0487d00074b0d1083eae643ee9283bdad812206c30829fb2f47bafd7d7546e2c828ddf1b35f95fb08c16860b1919204590462298d4396bf2b8330779d31b8860438e5d24f190ad51da3804a2eb3975903bd61c2ed830041da5f56eb2bbacaf22700209244001860b191950da426c1082c996a349fe21e6d0df0c82d194bc7c697cf7c34bdd862098143f3454dade3f991b8160c34ae2f925cf6a4152850d40307ac9a34aabac9bbc0346d2a1a131e30b2e62880001303c86176cbaa0400962c858c005343434344cb923003dd8f80357514435e74c1da60fb561c30f6cafb77e678bc8d63f8ca453c1461fb2db8e4c91c2879154ac88a560830f4ceac89124ff405fa7930058c4c61e38f58faeb4fe96f4b9406ce881dbf8bd1da5102500017bd8c803174347297ee8f883d537026f1919d8c00397a33a24fb24a13a856670617407267f684dbf8bed512723c6162da3bd283b3092d7e369cad9e6d92d62fc151836eac087cad19ee39a426a195fc4882163b7b04107be434b478e6adbc717f35bc478abc2c61cb8ced6105457425e955fcce04204b421073e694fb68f42a4b011073e45a5f562a59aeabc1e62030eac77b01aefa8a284d878036f593f26e96e491e39dcc0a97e5a4d9bd8461b180b6a6945731cdb6003a31a39954f471fb6ea83d85803d359faaa3a871e5954037b2bf55d1ef77f760937d2c07674c83144f3f2b081065e542be4be1472f0c8fc62e30c4cb5875e8b7d8e60c30cdc2531b78b0ab96c7306067d9b06a4c04ca00b0a6868cc681965e0fdd38bbd78a051e22336c8904f7eff30cd0e46d23962630c4ceee817f3aa7d65de328c08ba8b2e810c456c8881296bb728355922453f0c7cac13928859469dfcda1536c0c06e8966ccbdee9672c70a1b5f603d98d64ef6d3f1b47a81b1249dfa2adb326c74818bb8314ae68e5f3a54480c1b5ce0e3dde4c7efd4ebfbd8021f49f5eae724f639da68815fbfb41b3c859fda071936b2c0b6684e08416c3c7f1e0b7cd83aeec9450fc3c61558577fd1f4493bec5f34dfb0025777a9a7c9537288b0031b556033bbc37c1d5a46c60b36a8c05a08b1ddc94f818f42b0c82147bdffcd772105265d787e60daaaf98e023be671981a3fac1834bd0105462f871d78dae6474fe0fbf2e8fe7a188ca4de1c6c3881cb1ee7acfaca8191d404be3b9eb416cb41dd5bc478acc10613b8cd4c192c7ecc3cd571022f96c04b7a45d718d3836cb0a104eecc43750f546b435ebe3022a0dac14612f81cb9474f9a238b1f76c048da220612f8e81fa7183bfb47963730928c8ec055e4f31c2bfbe7a7b8451731340318e8820216803a6c1881cdb4cbea619c72061b4560f5ed5f2d5b4cf6f16b5c8b2d627c31430b0d8d2fcc8c197d28e30b2f0410870d22d4c610f8da2f8fbcdae2a8df10026731599846d9871add185ecc3032b2021b41b00d20f0dbb5298690efc2cc38096cfc806fdd501d45b51b3eb08d1ef0fd1e065df5305275a3801b2f3090010c6cf080d31c6991e57e606307ec454afb30de8729c109669413cc381d3039f4a3686a5357d2c148ca015b1f35a6e6f8fa5b53b888f1050e38a910e36be7899e2b3732086560e30666fecbc6f0628b2fba7ac3065cde5ef39c4306234941b05103c6247d7986fca17b98ad0d6cd0804b131dbafd7e42c81e5e703182193360dfe31cbb674b8191340263bc2094a5810d193071f2887bbeea70425e0f8f3cf2db850d18b039fe4bfd6ffd5141e315bc4e30fb4074a2ccc32968b88273ed8e3efcd257ed38165a606158c00506ba0250d068059723092b984e116f246eada0d05805275a9ff93a97879f3fa85ca1a10a76d27eeab84bea643434283552c176349ebfca3b54f01a31dd3354d0dcf147151aa76062448a6ec922962b33858629d8e8d0157d9b552c2224550a8d52b09f7353edd5be7d55aaa0410a3eceabb6927112068d5130593a0afdc1c3cc9e155170962468e84e87826d5d5f41c1861cc749894c7e82cffb358ff891c4c9be274866d92ab9e77acba14e706d213d457ffa40a5c2000d4e70b7e6414f4c2e6ab1b3096e42ec4b9df36d57520d4d18a7f2a69d0ed7c8441ed579420e8385164a1a98e0325f740f22728d4bf0913eb29af4b91c342c711a95300d4a70a3c1a2eb473c34ed4930923f858ee5a995b205237325c1ee4e7425df5c2e4023124c67d6bde0f1fb5d2630338a14011a90e042878bb507d6713a341ec1abfe4ba88e1dc7070d47f0c9722b25af1ac125df646693c5ef427730828fda3395e7dc3516c1b9a4b68bdbd1b1050d45301d7dfa452d7920f52a163412c17bde143c79fc89ed1a22b88fcf3dfc737308f683eed036ba87189ad7828621b8b158ed13b35d084e35a87f68e9d42004eb6ea6fa91466f4e798d41f0b13f487973491a82e03b538721e528072349045d8c8c2d62bc462018b7bbcd10530523493140072a3400c1afa70e3f8f04a9b8e11f98183ce8eca8530cfb9c1f98ced9e17d347a9192260a8d3ef039b42fb57d6021b80ac0420b2c52a1c1073e8c75da6194eae8b2c3038d3d705e672144bdbcba21898516584c008b0c44008b2668e8811bcd0ee3c721c13b8422e8820b181990612aa00fd0c80353639ebace727a477ed185713c709a3447b5531309b8281a1a8434eec076cc62a17f2f2a40c30e7c94a45e8277e7f4c84d83461d986831666e32cf1ea544830e6c7ecb71f54b688b188f7534e6c087ffc9c30f91aaa22107de63c8f9d284783abb68c481abcaecffa3954d42a40107264344a8dc4b5b12aa12f8060c09d664154612161960c020a8126cf4bdb5865676db4705a204f7217f98cb352f3387c3487aa3059a047b75925325b7c8b106072409f6aa6af3e5b7782221e76246978a0413b37d64676223081276e5bafa047a04db9f73cc1cb2b3725439c8116c889683643a21c4185ac3f0620b3a1ac185d0932ea9dc27530c122398e415762be9032d826fbdeb8fe95fb254ae0826c5bbaefdcd6c1828115ca4a4aa9fd2e6204508768110c185a60a39ba2479274e0ec1daa4785b52d519e961000a6408ce7b729c3ca72e8186c6175d981210d24230f16a34b7ba49081a4308de2f43c5f0ca6fab318106c15bece8125abb20f8f036bd05cd716416b740705f1ff880e053a5b479b40dfd81cb51a7ee5845223ff0d9eda3607dc93b2f657ea03e709239d99f239b0f4c5d4cb17e4b5afae3a03db01772b2f58f1ca407ae72182edab107e581cda17b300f2285d402080f6cee9876561e861c7e312bd01db8ca5b49b73e7790a3253bf029c68c2b96592dd581d1aeb28a5739dacbb61733b6aeb8203af01fed25b47b8ce6c06777e4f074729c8cbdd101480ea76039b6e514d59213e3f5021407d65306530d562e412b2203101cb86f0dfe1766bf81e91ced9dc8b46d1b9303b9810f1a1e8767394a09b8c800a281dac07de4da3142f0fff8fe19880dbc4884ecc177fa3fce5903efe6e52979583de5ab813fcd2d5e177214d5e3d3c07e89dda4e6942c391af8149196733b3d036fa5ab1fa9c6cdc0b47510b5b1d6831ccb32f0a271cdc6373742f420031753de38064e6fdbb5dc72ca8f1103a31bc93c92328d395461e043b87706d3af541e0303e337f1356bb06a0dd12fb06e1e6e46e51615cf626980bcc074ea252f0fd9247fb60b7cce22116d4d4223ab051c077ce12de30b15307181affc93317424a212fb2ad01698489f6b3d8ac8a142ad054e22e6f4e039d87fa859e0a7fcfb5643720b9fec17080b9c89667eaacc0f335957e0cfb4828690e3c72d215e202b3051272572471d35e7982af049bba773da1646125181cbde6ead8af9030a0b05b5059a02921498fab795b6caaa19341405367e8eac82d87dfa8d5619080a7cd414636e36c9a178f813168f71dc63eedc09dce6ce95721cd216b3a509ac0756bb1dc5e49e9d66026fa11ea1a72d2fba5c02af2e759bc3e2a8888712b8acd192a54bfb417d30097cec2ab669dbf2a78b04ae23e5cfc89bd53e6947e0525aba8cc00689a9515a5d3af668085404d6a3dcf73ac4cc39bd0cf385037c4044e072730739c412dda0608b2fb82809041a021b56b7f93b640fe347086cfcfad461c8b105cf05814f73cdd9de464b420c084cc89105bf8cff807f29ed8e7208d29d2c27900ff87409e96f17af3fd41ef0a92dd94fc3ab343d78c0e4b11ce610a364c60a0c0476c0c792f387398ef17733ae03de827ac7ec38ce1cb39203ae925d264fd36d173f1cf0d17244b3c73b594d764037e0f2fdc4f420741bb0796e69a123bd065c879419c13a9418834a03366a8e83befcb1860e429a01933bf6a88398b70cb8b0c9ea75d2bd69121728068c5f8ca9a3068b60c04a6df8c7397ccf71947b055f9635788e95524a7063405cc1fe7938213fe87a5dddc8c84002b8405ac17ee84a3a799233b82801ea80b082cbd2db6dd7b166f1cf2a989c2ab7f3a474dc9d57051fc46334a95495c8a4e2a83aeeb8e1b7a382dbe89837a7e3b8ae7e0a264ea5d8595542cab74d71f428c64f9363e6a458a5d8df6a2ca60c0984142403328a14053b62ee5639aba1e0fa92e5a0627c0b21a8a0c04a2bd7231b8fbe136d817c82eb94932677f38f90739e6082bdbe670e2b27dbad137cd6e6987a226a24ad38c1a5a9f6ffbca14db02946f1a0593139209a609358cee9e328472f9a93094ea53dbb4287470b3d269814db2ee468f51492e5126cf20fd2c7faee761e4bf0e1992da587a13287e94ab01235c3f52c071d7728c1c5d7ab099d4e02c97b1f8bebb892e03f6709e9307987461b093e489734b84814127c9ea85c5fc9adf77d04dbc1a33a67241dc17e9aac93af2467df4723f8d05b438accf04df160045d7b9a3d658b242ff0880359046b39c7328f2afc3f3a456c193950130f42f542b25d748a0824117c99e5eeefa86fba1722f88f3eced1a3b01c82bbfedc5dff5183e98660cc72d0da91466a400ac158471d897bab79b81e42f015e92445c871f4d39d4130de933427858e20b809d2765eff397a318160bc2ff4ff23ad50391e10bc76aca123ddfa07a6a53692546b5e44eb07263a8f78fccf999acf3e309ed9396d47aa18c4e4039b37e53aa6e71ef83843578cb13a3bca0f3d301eeba6ea0b1d218f471ed849269a25d4e5403ac403f781a5fc81475f9b72eec04e6ad4e02731a5aed881d1e4f133796a75e03b7a6cef2a9bb887d1a1adcab55f97b239701e927b11c39388587260d7e2fe44d0a4515ac5819b4a697375648eec8703d3e935dab93624fbdfc0a6bb1b49aef71915377096e6faa1a36e8eb00dec65ad5d30fb78c5233630fea1c774712f3bea680dac4844889ac34c1b2f6ae0554f72693a0d7c1029f1930e926934f0b17f7965ab8eea2242c5017206beac2b34eaa57c621f33b0121e976fbca9bc8d2903179a6df3c67ffba41b32f015bce2a4a4f2d8ca31b0aef7c1f72c96e4efc5c077f4d921e5ab680ef930b09725dd67c941ce610e18d81c126a1525f4c4f205be25236fb0885ebf8b171849fa696b24ba3357bac088e85b9547e5d3172eb09d5f42ccb14ee4b4f616981c62de1d0b397248b616d8c98db9fd773d472959e0bffa72faa7edf68ec502933bf2cde3df16438ebd029f636c6d8b1ebd3b45adc0c4c89e9022744871d32ab0b9d62cc7202515989ceb63c77cb5be2da7c0e7649a28214b29b0135288f49fe6fec8230a5c656e59bda79f7e0714d8ddf0e491997da80f9fc06ece3b81b7adb5e4bf9a3eca36812fcfc9a388fc69553d26b0266da9b652fc8fc55b02d3aa91d1b6b3d332a6042e48ca1e25f645f0cc92c087e3351d7aa890c08e4fe445e53c393a7504ce34b354f6d83202a31e58b97b300bf5a822b07ebd390c75eaafc1220297a472c6b897a287590d81adee5bcb9da5d73f0981730b8fd4427db25928088c7e106d2b59fee86204043e4e1aed393c2e55fd077ca6b10f35afae07711f30b9625afd383d6072fe9843ce69924fcb033ecc3de9a963cd79e70ed8dc716488e0b16f3a74c07db4dc71d23f55793960da0607b64347a6799fd717ec0d5ca5ff2c792cd8a769e6067e2f072155f5a598279583b581b5bb6c9303b3c869b6b1810f7e53133c5ef6a8ec05d6c07a4e79299ac5ba4d7dcbe80aa881bdbd0a71cbbcd353c6d2c0e49c223937672f4fd5d0c0a598b356b2beec0c7c87f6b1e3bf471d3f970033031bf3669a1ce68c905919d81c76f83132701ee24f6d5fec487fc2011b031f420af983fcf3906b17836252e90b037ffb418cf5b137c7d08161898ec3ae96d8914a8479b7bde7d0edfa025b9f3be5fc0d9a17d8b8db39af8354b7a176812dc979a382778e23ebb9c09e6b8e830a8fb242dab7c08aaef956eeb5a7e85ae02a8b5ffc30ef9ae64b1658899f7ba3c79f1823050bfc068f2f5aa8abd6745f818b8ec2229d8715f820dad2255e8c91c4a30adcc76158b6caa151819da01d2af2274deb9f4d814b165cc5f753f6d4a40093029ba274e728d6e4ef2c11c1a2c0586a53c99f1f94752814f8e0977773ca1e95c6d69ec06659d84ebc8934c1e2043ec8f464c9bb530e6d6b0237c163ce0bdd3381cd219dc51cfaa310f55802aff539d0948e94709c6e1eb8b6c761b6aaa7b687b4392f2b596e49602c7c2cf6a651bd5aaf6048e03ed774e4df7158d6e4da11d88a1d37c724ba29416246607220ea21fa9ddb33e5820b9fd15bc4f8a288ad08aca587794fdad3c7d685c188c0c7904aa3ff524ef13a8d86c08418cf624e71eb367721f096f91bb2050b029bba2985e5282d32994060e2a9ea4dc574efb0c37ec0c41cfde65297b0201df3017fe741e6ba18faa32911c1603d60739c25e4cfb7798387f180eb6439f2b55def38d11ad80eb8eb69e98fba9ce9804f59fd63771ded1c8e96838389e520451cecd7c943f4ec0dc89ce3b4cb91794808cc0649cc9167e7f0bc950f580d923a499390d180148b6ce95a216d1ea61d48bcb475761de26fc666c064aacef1338b5b45330726032e7fc7fcfeb8bfd67532580cd8fce19feb8726e56f210603ce83b879b173cc4835d42b78c9da19252f347baca4a15cc19b65dab1542e7aa7562bd8c9da71dc6aabf2f68a156cecb842d417d70b2ffabe50abe03d332333cb5f2f942af8514ff9514bb22a15acfdefa60e721433df4705531562c4b4ea9d72903c05bbf79a63a48ba66033a3693291d49d1fa44ac176e569d9644b396287144c7ec5b5d3eab6942ea360ad2648485fab28b820e26a7e7b71a042c1dfa44eb12daee8550e144c48e93fa305c9270889e9418e52cb924ccd16735ad66ad7137c94e891abdf5483ea04af5162dc4f95e6445e9deb2e44886d826bed382457ee9ee690d2043b31e40e428e9d4cf0df313305c9794cb011ed3b7d56e219f14bf0e18636dd8e2b5156b104a341f3a4abd7a7d46825d8942ce657d987f92b4a7076ed418aaaddac21a7a58f82052fc98824b80a31dc3222ad365b1509d672189da34f6dd341c78d0a1812fc4753176e1a4f53c247f0215aa76095d287acb412ca11ec4796f6524a5289508de07245cbcbd17a18c15b9288d28cf2e4a1bd50a845301de88be998ba7b7b10a008de62da5823798301265422f8fb4d6d1b4c63f2c85a150a11bc7d65e6d68b9f9b3653a843f06186851ceb86e063f4e8641dd5b35db9842a04b711f726ca2b6aaa1c02b408c14dfeb753cf4a6a1fba46a84130adc9235279a0174345105c48880e3faa0f2a76f431840a04973a6ed671be0aad1023238616422840f0fb6184accbfcd184fa03133b6f75ea4829b11541175c7421022e662061e00b6f2f4a10438be302e507aef7b6849134a31e507de03fe67a8c929572947580e2039736453f77f6e64c933d3031c5cdca8f2b3df01f4a76e532b1913611055579e0c2ca7a2ae55af9a7ef048507c6d5e385d0c922e409cda83b70531d87ea1511428d47d981bd4e698face3a896993a30e225da1b6a752ca7d081f1f03c5448e6e8ddef39f01322ddb56e85b9bb72e0fb27783a49691cf8bb24314a6a120ebc6fd20e6348d91b188f7eef501a5ab3a3dcc04f587ae574543127d5063e568485b0c9d2dc101bd8095937eb725a0317377f6cb1c2ed63aa06b6eec3fda41e5140a581f11cfb77544e9e9a0f834203f7a75521647eca95fa0ca4680df138f2f4282a06dfcfedffc9393330d522551f5e249d94920d55063eebdf674e9af227cd2d188a0cace6797abe5bacd059d518d82c2152a24555db5062602c3f88cc6d37b679f9360d20541506de2ee4cb61473e59db0203dbaf2a39fbdaf5055e720e4c63d4aabcc06a6ad4ec944aed3d2709d505363f0e9359b63c8c242e70bb9373ce1eba72ca879194b9053e9ee6b1cc315ad9335280a487d2021f7ac77bbfdca134ef2cf016573a77c7103554120b5cfec0cd74732821abe40aac787e475449b963cfb1023f1e27b65bfa9c737daac0874c3142b742052ef57853e03be838ceee289202372a2a11ea3a6349260aec5bd4f18b1c50e0d6b3c34deb717a079fc095c7af129af73fcc9cc0648bd1722cbf31957413f870a5e28771254877cb04ceed235967bd943dca1278cdb5b1e3f84395c04fca1572591493c07eb0315f0b93c89203097cf4b1f27d7497e33875476072afeebf685999a78cc0e43046f309a12a027ff13f56b43411813b314f579ac78d410d010f72acba3e42a83c0e839084ec1b53ee5d0723e90433b6b02b1410f81a9f56c93942b444eb076c44f2acedee0fb5bfb7987122e8e28bc6005941f9808f73aa8a1f73f4caabea81f93bbdf54716e701272ed2ab1ded0ef8b83a74b490a00eb89decb01c709b135df92f5bff3738607af2fb454f6c3cfc1bf0a12db6e40eda80b5d35d8fea9e9bbb5203c637b333feda6e7e2c0db8dc0dd123de6f6466c0c4a90e22cc3c6bc8910117b77348ffd1c52f47c6809198c226060b96275a30606a3bbca81b8f5770992a738578314db6cf154ce5edb8f3a51b9b94b4828befa669f2f346318d15844a40ab2055f0bf21e27d5c16a2a40211c042c6175e3880527118205454a1531899828f5e42f03e099ab9a314dca5be3deff28c2958a4e0442c04fb48fba21a8c028d828f6e9ed365845c59d287a440a2606bbdbfeed7fa0385824ffd29e8c79d452b3c8e0f040a3e3bf26065962aa4f43f419e6042d4d66ecf39ecc5dd037582eb919cd27bfb69cc4138c176e7d4c72aa14db0b1d3efdb875c2a129126584b1b528ad95fa44cb03f962247ff1f3d5b5a036182af5429657fbebda04bb0ef933b92c3bce3e593257893ce928bd0a8a45085f27824100904e2601c46414c721f73130800182c288d46428168248e747d1e140004522c1a3c342a1a1e2616101812140f44016128140c8602c13020140c0343c1b0404020eaa07cf2cdb1e36203f813c3de3996eb261d7ba03988f65daba5415ac7309a1cbbb53e1ccf24af984892e28bd2bd81a83c43302efb50865e1eb751907b48fba9e3ccbf0c1fec0cedd9d0ea3054529cf2272947349fa71ecfa108761e8529f6ebf5913097f7f1b444a1d8ac5847961408c3650bcc215cf4de721271cbdbe93a5bc45fc6bcc99b9bbb01e190ac368f6e7f6cab7143178dcf8da5346537719bb90264e98677dbcc8989fab2b497185428977fdcbcc88d178cc54921f2ce43419817aec350de4b6d6b639e4f49a4de4ac901bc4b67b62223cfc758a4aa373646cccd3f60a310dd8d5b141b73910921c45a8ace8f152ff4e396a3469228f46dc289b8dc1559aa9a29a413b4a394d50e98599d3e67d91039a5b80baddcf86c19a1825be8b4b2a4341b18859a913bee7a0bc82435eb4719a1710873a86ce199adc9792669ad9c035d5cc9019c0eb73eb95850d899e4d7123e81aadb6e9967016a91fbaa14e677602a06f05ee5e7dd75be5ab12b6a1b9fd76ce6184d4dc6acb1503b41754d661ad73b9af2965b00efc9d71f4a1913b3f065d9bdc77cd6c651dc5b3a146288d0698f8c7748ec4eaf68e7c0af30f71a97f02903231d93170b9f212f2477f87c9f017fce5e2293d1e264062e1b5664a62d63dcdb42d97eda47d44fe32ae8408130c866779cd82a46e32b1420722f62234775666d2ed0647e405614db73d0541f0a5f4c273f71f34407eb0e4046570629aa912ce4321f29a10b6121a86790d1215b4cd538542fb3255a76e4ab35f5273f052e1383222464eb435e407d0f88e90988a2ac0a65cec823f398592cdaddb2055ef7c21268fdcdef345257f112977fa513314faac5dd27636fda71a142d9e9409e54b04ea3c9a6b3231d8ad7a4a9c6ed3f1e672cc05422274df5e1fbd5da0e5c16978bdf5ab0715b11b58d2a3d41d20d3f3ac3b9d191ce62889065d22ce1ba3ce8e63af6a3be36d5ac6702c84b675f4ba56a77690fef264cb038cb9374d92908b0d5a1305048c5a28fc0689b744af10385dded6cefdef0c17455f5a45b351fe6a2df7ecfcdc0f831079e40e9433b69e7661a23dbd0fca9df94caa9a4b72f2d63ed343ad702ae261f6b2a10c69e322961f487fbf815553cc289ea51d441b67571a9b87201d869e7a823d8acdea099a00aafd8b40d925604c99794576d5e1e7483ea7dd07f93114ee0beb189164433d3930151b82fc9dd428397816d6c8fa39262399e2457b5cbd98b1bcb6ecfacc3423695d5cbc7570ad4a4b933967f8421cd53328d1ff908420e3c32dca474a8b432b6ad59d8e4d0600ad5e009e1d080c2c6cc5c5e399f3789f26df418c0c3263cd5bcf1f7acfe8d08a2f00e9ea5a35212f38227d1cea24f511f14a0440a0356ced3b2ea2c09ad4084c0e70d55ec642ba70edc676af39deb1f7868891de5c68d1e55f79d84b0fc1413cab8581a8f13ef8f16476d4145ef5636e31aea6cec40434b9bb331ed941bf04dcec8613868eac753c316b0db302918665808e6e1959533dc9e0a51fa0a6456a58a4a4fc5c6e70c9cd7e345eb819e113068e8d97d04a71c21d7e1d6eca26ea7de34e4672e929007b80ec184ac447668a4236c94afd068a6690ca66eceba90e62519af45bb513a4bd7f1c8e0632c8063c0f9138d8d111acc94e2bd4c06db4dcafbd844480c1f89e18b2bb70b8f1178f870b82a1099f0a3fa3b082e80af977b4c914d0d511e99442c3dcb14fb30f64d9119d852f8e8a9923834663082864ff4bfd8a38063d9dd2cd06cad1a050d7432d8b1558343059d99014d66e40b4795d54fabd8faafe7fd138ae98ff1b62bad29de7f5e2cb0dbae3ec5559e1b470cd5312ea59dcc331af14815127a397eb5641e36f4d38e441151ca2b458fb657e16629d0f4663eada38fffd37d144049d8f417a1bd157139c9c5b1aaf03f53a899575f029218e3b696911e0697ee9a58c335089e3e6a923f2a26a8805cf6daf014ea2672a234aa8b325855aa1ef4e274637ad7483643efbbe2dac0fdc1d02390e62a1acc238e83da6589b02fb1a0d3487563314970c1776efa4b271192fca7ce2e8791024673db82223765a51313f0a918f91a51a77706704b51931006d140fdc744c3262b00c1209eaafca20851b54778daee61cc88461ee872963cba644c537304502f80bc8c301a60a7af9a331c3c63765864ae0824740bade94452d706fe60bc3ead9bf595617812fd99b66f570ec37f62b8cd6885ee70e6cd09a42d54bda930ff9f765a0fb5398baf1aa92b00f63fd011c3b1374d618e64bfbf297692fcc29325ada3215968bea2ff591aa1d347afacc6da39238a15897490371125e40051ea7c524718062f0f02abb97a24fb923c818b1cdd865f22b2ea0b4fa36d1a823722b2f722e0acaad0e03236048216526a777e6c994db330e4610e8613a9b1c2e35e22f9414e0a536c44c860f03df8999021aa5ea8374c7c198392074a085e1dc9b55e1174ef144d8e06225c14dda309db2014591291cdc2240f06854ca5c955651d2c42f0392705d2d45d989f966d2583053c420658ce4a77cd320fb83f2d421664763968af5f36adfd0e05c54b8b15a80d3b072dc368b1105dbb8119d7a880813caa8930416a4f85db91bd927a9a31e6ce8d74f88bf4496cee8fcf351d066395b0464403cfd394274e3b192f08f781b89647b40b8303bdcc0122c7caf2423f32873e7d0fed35ba36939cb903a6d47795a88836104735f2d42b26c50a71e1baf2886ba2b388b8e1618058cbf64630de7d9fb910676afc24dd5ff3728422b1d96a44ce935569431b2ef5f0d674314cbcd9961d998ff6f697d7aa959cd02406012866b63a81ef468a420a050d13b21e2b98935db6697f1592ed3f511668eaf98abd09fa16471392c29f85a823a7743a7be343c1b6d0630c0ca730a99b54221df5fc758aca2979454a75846b20e9c6aee9cdd53a68c3381718a7e47488964125c87727c907dd1fc2a4ce5f20ef2af931f1d15a002461a3de26c92dcb3dc26e3971b4cdead392378be78b4498c941d864cfcff481e7c7d5f491093d9092cb0b00eef777070c1d3927a738c17a280af858ad63a8827fc7a7fd4b7a3e5a2a0dfcc4e8fbc7a57d8ca433d4f7305791205d13572177b01e3c1e738ab25d30ec1140db57c3a1e49a06d7718a5b193402feb69b5dda5159d22c2012e3a10f21d39ce6000acc3b7ddbad10779b32338f92789c790c868b03429ed1bf8fa14cb1f5f62d6296b5013d4b6d9f96118ee4a9afc2295a8980385ce5fbfe72541e06e3de475025dfc73b3cd11281ab2bb237cbde884b76f84183d823c927920238724d8d048803adb332eb8ce4a3f6404ac1c52fc1bbfcb933bfc31c0bb3cf81fc30079873a3475bf518de50411633ff94675c93d794ecd4289846f96f5fb7422c8dee710834b65f89f45a6895a565b8358244dabacfad2019d90f3e4a237d63e1bfc21ec358e11c1345c744913dde614a4e052d3316727ccbdba821a4ce84dd1f1981eacb1444fd8d6692ac3e2e5602ab6e50b081432f30ac86dac657e6b98da6e4002e830db0e111cfc7b9f22a04cfc19b70012928d186c517bf5f355d21ccd28872fa6ab05a204fb74b8f967a962f464deb2609b1b6e0c651e822c7cf9d0f91815a2a03f07a6f425e36e72a5a8585955685a26bba577e4c2fac1d38a0d6223cdcc5a17126f92679e7a47d6797c44b8a9c99531abd5561cc947c0db1b8141c0b89ca5d22a1acfc442cbd2c3d2363ed1d703f443281c96c96dbc0e8628f2b489adda89a3609d401161472a94c1810cfe82634723cb744a063ce9c0b66c9a0d7ef4e26dcae0a8e9aefff143db0e149b5dfdb6b248603e6ae0aee57e47720a77a3f28de98ed19992a28faeacc5919c3edba34a2f0b58202d2e6986c0c8d567b4149d61d2ef5cfb68e213c5d306f6fb4619cf87d5f99ace70fd28026c52c79fa8d6148c8eaa8314ad9ae46ec6611cc40ac6ec3023fddc932c3d387c191f7eaec2a61174bca56083d980191f4bdde1edb86ddc4891c2c2154632e1862df5a4b3f19629dd5344b0d2e0b14a6ca106473870d87017e5c21e791cef5bb82b101669e2b3c0a92440c41701ee378c2ede02b5dc120c799c6b35612a174379538bc7513953933ed7baacf77db4c88b0ced3c9c6cb06e6dd8c811f2310f08d761bdedc1acecc4c1b1c4056830a24337dd9b46dfc89c51e52c8c69cbf26efd2a000486bb4a40e9fb40ccdbae4ce94e168aab512a0f578ff421ad60a08e2ca775fe598f582d4e727a1cf6233a5bb8b20ffecf601220f7e3cb6a394a0b526e0202a44eecf8c31c7ead4e0ebe7ba719b21716fe0b20755f3706df507ec8360baaaa1459f55d34c87a60f0b2cf08bb3f906e9dbc393c1966d33873aff1a74c521d77513fcdfd1518dd31097dd25e00ce21b83dba07b48d2edcfd8c3d38f4979b61a68e89936527088d10cb4a7970450d9f80009729cd0434f56b08b47ddeb128c4068025704fb0978127063411483df10682060ae27da27bf4e4ea0017de723506a4152ebdabfb8af13853725dbacc062e84b137f707aba58f24e1c62fd9de9f4ae4ab81654f82121d5f1c581f96d943c39ac13ce513ab81b5a69bc6015a0a0acdd109de4651d45b220130fbd93253bf3ff90c52bdbba1853ee4569906916a325bbdfaa90a43a527cea257a187e7ca7565eb1601bf79a41251882377c8956e653048a0580352fd9bcc553da0ffe070d854771d306c81854e6e4736240724a6c7e3e82b14f1b9cfd87ccb4965f3ff8eb4bc0e8a3bf1d829087360343167cd9946391d02e8baec3f4adb718754a4155c520360fcca4f9db1c5a38393144cce0d8cefdb3ca1823fce4311433a48d849a533fc11d6dc1bd0d93ed1445cef95b1fa390f1f4c50da9f909499483e61ef0932ae1abd4966cf805597fb284ab487791528e0f41f0655f8519df190c4db9b56326ac62feeed85764400b0f40332be62e3499cfb60b33ead6f06a929b4362d8e7e32ca48cd75d860d6b6d9dabba9121f45808034cb8a02f0b767ceced61d4d5fe69199d78c9c1130f676f3044e95b0238511832996e2a32ca75a0a4d4827a8a1e44ca8cca42693c54194a593a2b865c2bbe59448369e82893569023bd89e44f15ef9531a1e5ac841e65d04f545bc114a7a84a031bc2e79edaa029865a3c797a778f79b5d13fcb93dd761624b39cbd684e2186b37684b4eb59baff1536c60e25258194aafe28bfe48d80063b156d2420593674d0343507598ef19c47584d2b68a5f07594f4105e19543d1e8c8e7289115acbc30279974b7d1785dec06ea33bcfc1cadd9c26afe23d670ec7a21bacde8ae0d9036bb896dd782efbde10c61729dc791de48586eed16e33755409e419b70b37a2b76691f1911d47be20f91d8bde308b3b62cf7a5089a01fca6ffa8f222c24bb2800cd9dc81095498c22f62ef6ec1dd22ee5f6f786b19fb877b567efd0bb14db8d9bc40eb19b608b0ad56c4b9549c8ea553fcb60a7223df41bcd38455fd5ed454af26d103526f0d4337488ba283dba862b0415ede40db42c29658ee73d0a5509536217518d604590a3072b6d3addd6f6f89345172098547b64e141d231a663837dab537b4f8a6d0184491e8f7b3e8a74e7da549a63f7a0f0e0253245e355ef891fa7d9212302d41bf8c75a8011eceb5a827e53c0ff6b782134c5d47d3f35c7d4b6a778aae084cca96732b5fadd5b3408801de52cf02a0057c5619ab06111df936484afbd192a537f4d03d87f0f03a83dba5cf490460da26d88b3b9fcde68da611f5f8deeddca0870bf0fbc73b884849c29f86514bab08064ed54f24df57c43289d4dd9101ba72a47aba7a3fbab705023e857bc6a445567d2de401e56e880d2e33171c0be7b55a12b6773d86394873dbd32850c584ccbcfb4309d247f50e400849306150b4dc58336c436c98d2b07f8bf78df1996998015fa3181ba6c40c38ad9f61ab4c08eec5a0e210090f9a23259a4cea55ef4c397b6679cb689b73ba8040690b473f2461bc218d582afa6b8b884d0783c0970bf524b4767e09e240ea49f3124aa82bfe232ad98cb655dc201346165344e27f12e5cf60cbc856913213bd44e51a3e950e6b5daec25edfcf42ce8de22a18754fec05efcee94f026bb8bf8b9fbe2911e568586d73d6fba54dc2ee490f60c0dd466785ac31a947c86d21552e96a496eb8a21eea37780a0f3da57230e5505d7639db0065a33c811b33ba3cf55fe3346d7dd240b3669a3dd92076c180926dd860689ebc87cee31021746fa2e2beafa94d2dd9a0197da10e4ddd6f39945ca4ec6c579166b67cc97c90a37dc2a5027bf477f061851a9832b9e1782253f213e14da4a8b2d19eb564374ecce2983af85bc1f17a9c58f05ec7c6de59c8ff5d1aef7ec120b1400d13b66b71082e138e2a13b311cf49161758515a6c5c5ad085a1b2509f92288134edabdb01758368847f33c97cda82b0dbbabe691569d1386b13fc95a95517a3d33d482914e1a87e8e971fb3ed20090951e97bb02f25114d2747c33a4b5e33af88995e7d458d8d66259ad8e4697b098679cc368eec48bee8e08838ab342edf69a15791c115a140060c673d4df3dda44c59f905b83b46b72d7b6845f37f82fc52e921a21abae8875f824142f455e21d3693636964c2b6451b620c43e47fc11451b4772159786a9c4109d39059f35e2d3974b5a7602e1fd25bf33090b928104e5307b5786d3ab73f3dd4a3efb377bc64898062ad8f15c4c31a3f30a374e5ba8911bd8a0ce429e4ca73aeb9876cc366292f74035189d8982c920a04e415975e8f6365f5196204d1096ed8355e0a3f4bec795e961a5429ed47beb70f6327fbd5be641f8e162f87c31e3a29ebaf1098243786e5940ce2776cb83006911aae69fe807bdfcac108f6dd0e4bccd37b95d614ab8740694c0968d0ad9a3c6917c2a26b6ab58d0e271cb04c23adae4bb58745057620c9fbd8f69cbe997397f9607241f2cfe3eef7902690c6fa77e1efa2c5d1d00fcde4f5a6fa57bac54a2b6d9054081e48763a84a14132531ee747949f95568d304ab5d234b0d1351849506d5c445ae589384d2a832437e11d7b129154578117339edebf9c8b0abd1b999d5bfcb742265683b4b9cde02bcb82c2d9560d6792d99d5bcf1a1c24d9f2f2a524631b02a8e23ec2d570b2031d5c3f5e2b6813cf0e2610b0cb67d17954f5845730cbdbc2aacc933a430854c5ee4d04a259ea36983d3c8c531c8049f3107040969c3f99db999082db86b7e45f5ceba337393569b6de994bd1128ef3d3d3589c2c15bbba7e2602d5057d9e70075438dacfd203b3ecf235a8518416eedb7946cd3f2c1773f4792264e7fab9139e9f5b7b609b68d53b6b637831506beb659824140154eece0efd13fa02f790a19f4b2fa65612859d3c213d50e2e8eb2db839811e20d9dcd1e5ce2489c7177f265943d2c15d3987542424e1da8a45606a86ecc0441670011712630fdc3fa7cca2a951cc6819da05916236c78c5772c29539c2010865a7468eb63bdba52904c065c0edd9adefaf65f15e755ffeae3a12e706d42d0e371874c1fef8d21942e7a0d37ed8553976821b6668fa5b4136caf77036f30856b9615b7571677d8dcf1ac3008a605d5b4e8cd0f9153400c3e31551ba0414a7d49d0997bdb9df4413ca586a89b673a6327f55cc6b5848289b7297d21aed8849564e8ff8544992779541b89599fc287fffec52364f0dd8f927693d0748b25a74a0c034410038977008e8401cd369967755a747713ea8ba26368cd8e223602378755757ec3bb1b9f56a933edd130c73f4f11dee373ae82409c1cef937c1cd080e1c326e4c4838a0ce49bcc71c879098e90d2e95ac39be8129ddbc0ac0f1aa31822dd8bdcc8632cc9d9943b2d8e8624c4008ffe565d0d584e378e637602ec268cadc8d6408515ad15240cec75e253ff53c8e7a5c70e092b0113d788628d66d0136fb430470c943835e061b4e865c1677d9e1ad2be0ac7354487e2e7eec5070e3778988315ca02590762c814db2b273742a84638a73f0459fe24ab289321a05c93eac64d65ff3536cbff66899ec43a81e080c0acbbc114d7414953db6083c1b0b006fca3453c03d03b89a0af33f0fc28436b3b4e33977e23fbf3322c6a939bce512e555e4821ef61aaa946bb33cd1a746e64ce9b910e594bb0b578e0571d5c63db0e8503e5efffe102221bc219cfc4be2f25411d3ecbcaeccf8c34a0c97111d89ca1c2c2b865aa18e71ea04957f7caed9fc4a92c9324568bc27707444153fba68bfe981d4311be0254185ffade440d93dcb2a50168859bc3365258fb31e275f8ff99257d5e903a822b12c963ab6b2148232fafff35ac7e3acff2192e95d8aa4c144c606f2862884f7fc6123a2b997794df98f1608335a5cf635e9a669b6aab023242e5f432b709822b542e6028347de19383f6b1098dd6a0071005715886abe00bf319abfc79b87e4474c69c35041d58815105e477483aae9a49d37714a8fdf81e2d463e0419a9df50211a7499126bad287c23e80c4003f120ac41d8f21ac17dd94990234de1e43ad28899a8837da3638eb1c937b0e35cd618c2808ff702d2319879ad980dbfdf110e4df37ceb257b99a769b5e5629ac52347ac0632353d25adcd0ead6fe9077a390af20a5e451b1f4916a0f51e328f8573ca98b63c86d9cd88141f8efacc78dc4c77e5aecf799f8e39c3e41e0489be672a7d5ae585ed0148759c89b8cec9c222910a1c44a0dea3dd0495fc7cf765b9c28d102d0719dafb6079ba36d1867882b8c899c6749e0afc9542f4d4656fab531974c7867bad5d6adfa250ecd4d826a0058c73e1276f6ff023c094ea2e1e47f0cbcc54e81a2f304d75bde5d2d5fefaa928a06faa114cdb7253307c503c50cde8bbf9eed8141d1058c0a936bfa2171d149eaab0fbca2f371c9a0d241b928d541a684baf8c3e2d4a942bb5c2f3fa5ac620e9725088130c7ac4315a05ea82c36aac1f64d65112427b5b8a21fb233d12aaf82d4459288c354601d5fdf38ec72c3d602736e0235a273da40c67aa371e3411c388bd6714fd18c3b97066b851fcf90b566b64f1ca976b725b903f0ef0063127f281f24f07050703d682d901474b5411e8564953c4082507d427ddf2df8e3d2bcf8c552a383c22e4061f083a5a983bf9fa1ee8a777d6579f4659b17762c2844ad140528ba27a737bf393c1a755c931c111ab34384943dbec967a282a19fb5e3f4f5cd5454e4db7cee10d9a491952a18f3a4c3cafaead3d52449ebb34039b45a0f04ff76ae4e0295b31e01da7ba4e1a748bce51618c3e349f4e96d64a641910bf845955f92d231d331cf229d7c0a84a05f12346ee5966c85aec3bbbce73888d145e65e4c7a5756a34ecbac3972de26fa4254b0f9d08e2850337a3cb5e77c1fb5c6728005cc87653462a5996ed93d6462f84d36315579d98d1bbafae3c09af6b2b8805891e3c832eb964188734610eb59ceec1266efab71623954c294e569d358d7d91e587611e4ffe7cb7d948521eedf7113b361f3283c75cfcfdc1dd2194c322a5c213c816cba08d5657c6992ffa6faeacecf0d7ecb437104fd08b655305c0d5a17c69870de6d2b1c526f05583e4d01580e176abc43fbe453c454c511310605d42670dd439180210130c6077f8e0ccf3251fc82ba3418222362360b7fd7049e09683dba978f45dce7844a885d555e46494b989639eec82ac2951cc165d55411ed37c78178233397ba3649f175e955e6c3315add70708445e430adde5cd5fb1acbdd12dc73e5e1f8f628361f749ac2bc729e9a26c229720e3dd927554613d48c5c7b0d7a83f9d6013c77cc799f145f1220a6eee63349409534d015ddcc8a793f67621176f575881c98f540b23d1387bcd10df0923250e53836a542bd287a7cb1d722da55a5e479e93645dbd9d7e9ddd131bd2788d658b405b887ff177a1f82ef3d3277e141bb05d7b5314e7eb13bd235732eed451a5c84217fb759c8c8c1a54d659b98c1f218100b5d68c403631cb05dc4db536802450a48b5770b0031a19551b2fba68ba105a897b9572a75e15178703d406f56552f99c288f68c43883bfcb00efd55938af14c65baa02d040c8214586081adc69821ce2d55181c068a82ca0f456e101ab3e931cd72ab6d93b9397a30d72fccd54855d9f83a1b422f0c01385e6c832bb87a8a9a8a74179ea5ad4ee4a01f1178dedf808360f0b4c6b6028ea1a423dcc42f599a27421837fa83b04551c29602c4e244a15a12540c13758df4c1f6f7f127457f932e771b26c345fd882a9e631f977ab28ef399d34fd796151de28da83a02b5d51e473514e8a2503c67ac4d72a276cee39ad6d650ea52410c48ea561ec1a547ce1d73bedaa2a58fc627ebd83b9f254ded057a5d497843f77349fcbdf608722fe2bee7ee4a2c5a9e19eeec49a44374ba3ddb632e677a1e50ed2fdee202c945e36b75178da7b73ba6e585bc42fc01a1a97675bf241f718f1e6e21242fd25ffa7a0c4098ca86c7c89506465d9dfcbe078d59da708439c619dbc0d1116e0574fa2e2fb39d4f18d79916115b9907bf009e8f811fdcf0462a6111850e18f84c9d50c3c80a1e8c73fe39224106fee133b2e1d59e9c051ae667d500fe2fd22534e5dc6d2a27f25069db667eeffa98b45f11a3c7194169a9e314dbfab7c245ee33e1fca3eab3d6dfd3d7bfbe5bfd29f479f599d20f02f54f980798ea9ded4ff757708a946da14f5753304f9f29c0ce3d5e0f38565b7270943e43e7bf81569a080caf10ac1669ed9642e75a4c19da6358de05f1e7f2f878d9309555fdf30d64b0cccf2a92ed49e623003e9505bad11e6eecd33e7f047fde970972dd3b90ea065ac07441e2a119174be267148c0e314acbe87b47736dfb59ea29f289608d72234d3e71c3dc910fd2a471b4d36ec5cc4043da117ccbd314dc68770c215d95b75fc56f5ac5e446c77b1ec9c6c5ddc86bcadff35fa1052025bf5fd55bf722334cb8a46eb616eadb01eaad5342d0dc290bbbaa378954f22dba6fbbfbc1d76ce44a491a7c556074973bf01c51c2e8caa80b541754ef66e33a8d66b67aa3b95759f62e778d5b9c45dd0975317507a8ee6b8d8bcec58c5deff0a2f9f796bc32ff2a6ade7a364ddbc49457352375e5769f05692be8f396d132c28544dfba9dbbc69eee0c6a3ceb235ccc845775bc8afb6d5e1b4d9e46b8a4d4dd8b5dc7d4a77d6bada3b91eff11a079bc41dc9daade7abc77ecd621f6e31fd2fb5ed57128d2e00e90d3aaac30720cf6bf19f74e50bde7e8f666b317d77014a4579f73e4d16ef38a87eeb9ef029563d7dd6cc57b5e3640b5feeef5c2de3adb878ea18bbad98a1a7ed922c668765d1774a1d76eb6c0d407fbd4bd5137d07d8dd0ed67411f811608e25d9efeaa5b4ad297a8bff0eb0507de7a613348f33abbd9b84012a170efddb6def8f0d6f5037c05efd3fb9a15dcc7e091de55f58eb757b3ded5797199ee79784b4a7bcdaee1e9cd163ac61b915cafab7ad1bbb76e8708a7dbd4955b1b844a4517b5ff14e1e8e070a0273f1dd011409233af1aaddd18f96da35edf5b23f5ce893798bf2b796fb68b672f05f5d6651b6f2b9e1c0db8320448386a820f65792efe5f06acb70214e00ae84595304a788a0f27b742551243b580d02331d1ad25b0dca62c7e2d10e5045bb31835e83346d716a4aee499f812c0173cf1e048461e1c71459e4d286ac2b424ae15a8830f7c27b3a90437fc3c4331f13b62bcc82b37c472bd19be8e507d2f34344089327c2a9e1289503b48368a4b55fe803a9b681666d953497fdf6f685a830e063459490d16e0521ae6699e4d3f06706f77bdfcf34055c5c37f3482b29367b1d17ca3add2d0c42b3ababac2822d49077e79323d866bb148695f0815319182540c326cbb40019c23553a683b44936c172f2742b84da9d32ec753cc6c776bca3487007b190edd78b026380b949b04b544c1cda4bf707b1a542a3919fb68d33d21cdb5b718d1ef935af6564b3a313f0c7dd1723a4ef034ba107465f92bbe1fda2ae0378e8e46ac1fde2789bb4d5823eca0a4421275d962d117633450c85531bea80b4b7c9f108024f42c1e9199240366b629a03202400c2426baf08b30474cdac186c933c6c616bcb05288e74cadd1981233a28a28c9ae611d7df6447748d284d0ede01f3599c23ceb0526189fb2c326fa65d1ef6d3a11ad483d656ae5b1e1180270e0bcd1cbca1acd1925cc565deee5b574bccb1dab4deebd6a448e3f6a33bbe722afb835da29133e39e61b84634dfdd02d982fd671510f3c097fca68a58aa4fd58b3e8dc461be3feaaf51de3879b2dfa4c2cd0181c089c45769d8c2466aa7bf8c051a2d8bb1bda9ee5892c9087b340f9e69832845f81e7440a37ab43c640e23c5e0a007e90514427e54323bb8669bbf0f9a3c013ad0180519145d2914001080ee63a54a530fb8dc9d9235657656451257842cebf8f3e86fd8694df5f4b8621717cdd51e70d5f5335c7637fb2e0aec8644de0404e36e64909b19d32d903dc4dae934278a6c198c89a10ea93c1dd07694bf83d0833ad42c428560c206f83195805ac96d0c0970e66842e06720502d426e349cf00b8b4e943d790a5aa8bbe84e5577e5288b0842708b04f831988ae2bf918503d83651779be604042039ddafc086fadb602fec504090c2f40b46439f003fc003fc00f00d546486ca4f5dea2943291872a34966a7ba794524a29258d9a5f618fdab7563758d31f050c890ba50bdaa5970c368472a4cc816347f14098ca938cd31910c42432c733f75031eb3469e8e817e81ffa0e21f3e5991f48d2acd4264f59ddb974f481a459713f43c9d0967f363e1074a99176f641b79cf88aee81a0324744c38bd012ab1e0af5b1b464b0e9b871d629d03c90745af114f91a97944e062b1e0842956af16016196c9d76a46163c7197720771062f27f2e470daa33ec40ecb6a05f6baf037945cae8a59bd66c99ad054a8774c996d12c22d5310762cc6d672a1a54a73ee540cafb5a3aa50bb31d1a196c757120aa78cecc2d2a3dfe4c3810ac931261e2de5425cd8e1fa033fa0d041de3971026be2dbb8944dd404a42b8c825cba715d536903a5b4ccb531e196c37cc6003e9b26fee10f6fa6f1b0a740d24399d4d1debf255d540f4f6a4e43be775d8d8a10307193f407b21d03410cf45c5d654a532cab841821a3570dcc071c3061a3a140d0cd0333472d49d6a4d15161d65201b3970d840a39861a6335033e4217aae745c1ee10e1d367494f51d66a4116819f6dab1bad5d3fdd8d82e8d21d62947a78d5132b86b1f3bfa1e72f1e6418c089d8e8118c46f08afa07245fd37524a117ce00366e8283ea851a30b2a06e2c78eaab0e09673ba61e0be633f45cc52af5e3bd3a3c6a4b3acbbb56020e934a1928d96fa05923e1994bee6e097d2a65e206dd820c3fc6dfccb942f03da05924c5e315367cf0572e6f914a6f4ac5b20266d8b265bb1d4eaa95a20accc55e768e65dffcf41b34010a641a8db942e6b778a05c2f59850531b446faef50a4419958416617aae4aa45620c59c938719b13908eb5401dd1bc4af54383c4fdb5e633bd5cbe54429df6cab383729ea1488731523e635533a4b3b41a54078df909ff2254fa3401c594ae62b7a5ca05020f66f7af2687f1eb38f0a037d0249f8860835f11dc425c960eb4ea3d509445db18ca26f25833681a0275bd1e349d19e7f3e823281a4652ab355e6740904551ea6479766e693512510a4e6c6286d49492088d85213d2458a0472697fe6ed6496693632d023106d456a846bb75c443bf57464cfb45406d51fab4184a618c4cd1a7af43abee824dfc0f18235238941ccba39ec37da68ff6f021fe430c815ed3fc7a42f6110df2a3dcde6d4bb9c0c06d1b29a8cff99524cf10706d94d8c145153e6d0f10b82a7f424b43bd6be206aba13ad27568d1a28472f8863e21734ba7f12b9b708c90be2aff6e5cd31f56ad63a84dc0549fdf49a3a999e6006367690ba208d75e62f9dfe2ad6290d3476a48102cc5cb8a24dd6c5f77141b48fd1ca6e4f6e32fd16e49c63ca417c3b5b90b37c686a4b41e7a0d4b52086986a326161faee4e5a1c35f8eefdcd3c8b62ce61a6ea74f32e8bede56bd6d5755ccbd43d6f3e68e64fc682d829998ca73bf4b452b0205958f65bd22b88c953e756343f5d41f88f31767839a599df6c05413b948d921ebaf3476bd45042b282f8e9978329a5bd348f70ece809b40004385a50a3860f70e4c8b10a82503166ad9ea40a62e5143206b1e184f4945490ffb2cfeba92c17640c2a08c275d5dd4f94a88fc390a720c7fa243fa5a6759694b990a62005fd362a8858b9b47c66214b41ce7ef3659bebc3e38714c4fe70fb2848a79deeb377e69aec884271ad2e75bb2ecdca34ad1dfb5d913114a4caa1d5b2e7808220e593b4501bfa57e4999f20e5a4f72d7b05336d9a43437a82a0f39da95741e6ebbe13c4bdbd38a17644e860d6a851a3061012420c848450738258f731fb687e6c46cd264863d5b677ba2ffec6344190f5a173b2ca4c902d74d43ff9204c103de9670ab364b2d6b904f9f2c7a07aa5640982c6783f7f67a9192d9520eaadbdbfa94bab16a684ab7b6d2936db21eaa5f3e92c934e234323c84910bc36054df94979e5daa69092205667f8e938daba8219099218194329d12a32fd270909095252ad499ab8171f4114151e548c8a5962b53b829431eebea74adae7fb834236821cb64e6d3ba8cdb859a34615921104259f6f4de5466ef8f8224849d5c83b5323db534f4510b48d46d3eda3a5211341361d3b67b8a86b0d890852f670b5f87f7974776dc84310de82de20f425cb908660ef63cf6ae4562dee435b3499a65ccd3d6721c8369a9dd7922c0941f4cea59ac74646093b8d1c04e1c37ff80eb58c95194110e5346f762ce15e0a044966d1cacbba39655f4090cfbf46aaee698d46f30fa4522f723ebde5b9a0fb812052e3c68ac80b5b671fc816c446e68f4c1defe303b1db34c57baff84178f640ee94a9f3c68eae1de3d403492651b27193f2f0981815335e8ee381987d76498fb8ef403ed3df9b92b8ef3c253b1066d447dfb01dfeb9ab4396a2baf795dd9917a759d51aa6b2d281346e2553326161addb39701f3776eef965395696ad927220e8471fdd70617ec405d1d071977120a9755caab89d45d130e331070a6e05356ac081982e624ff54030368050061a8933c8375c296a90634a293de07d763ccd0dc45479ee9450ff78db408cb54adbda7d8d1a356a241b089a8492269742caf81e835c03c1ec949870ab133f911a48a7f4adca8476f6f8c61969d8e8c18dd31bc834103e5abb5e5b4ca6530e1a083a5d5fccb4a92eb52683cda4a1e30ca4936b42d6564c11eae39166209a4af16ebef36f68dcc8b1434719bbe38c1b2ec8c10d0e306720cb40b0a4c26baac7b49a3c4192816836efa22aa82043bd1d67dc48830764e41808a284ca4cf7991103d96459ca202e571e3d4f8681e8ade3e999c49b868b196020e720e6c5733e9333970b520bf90592ef664ccbc9644afbf92da41748e9d4a6f352dbd339b30b244d5984322f9da593960bc44db72989921d349e9a5b20250b3a5b5032a65bab520be460b2632619c733ed260be4aaa062874e97c402514ff505ada42d6474f30a441f55eda5de6655565a8160267b3f6ac7d0157f568124347b92af8b97296f5460b3c43ec52a3566e53a4b4e88f6bc7d42cb9c02f92f55c905fd859402c1920cdb1f64d6ac3956a38617320ae498ca3eace949065b0f6e1c1b69e0c851972b82b6152414c8a54de624d355d2fba2b6b1847c02417754c8523b4bc2fb7402e964cc9d797f9a60b09fcf9cc4e74c20a5119d74f4ad7c749c4b20b727ff186aee5402f1ed636f688bcff7d10e641288b5bae18e347000a194914820ee75d4949dfb3c02b97565449385d30884cd31b798fe9d8f226423070e1b682260cb81ba1824f1a04ae9c89a12f71f3156fbecdee22976c320ba58dc6c4a16534b5a4647096ad4c8814e0e1c9c041406f13edc466586898341d20ee73ffa473676e4b8916347e7d8a1031804f5b24c2a5fb6a472f35f104d76eda68b73c9d3bae10b8272abba4f7ac254f47b41d0ce3998e5c5ef9e3c9717e4f4a44f689cd7d66dbb204991c9b53ac76845cf7207a80b8290c1dd2be5cfe7b9823ac05c9433f98d12dab265e0c061ca405c90ec842caddae06edb271d69e4b0a1e3469b8e326eec48e316885b13b62dc89d5a1e573389bea0af0529ccbe27759eeee37fb42026edeae2e16b19f7096660a3e02c08f79b72d0d739a65596461928071adc280ba2bff9b6ce8dcedd07e88cb558903cc38a9814259b53908c1d250549c68e82460261c725c282a0f2d354efa85a9ce7152725fa3e9fbe8f80aef0d2ce643ef5146cc51d2d3372e0f8801939702420ec4056ecf1a965ad73f65a318fd214fee44fc68e92821a35c8d851d0a85103083b6ad438347015248d1fb5edb327bf18a12ac8fbb1a131a754103669d337fb410549dcc46e86cd9c82b06a5f632384d40cb229c8a9a4974a571afe93b614dc99ec7db9a56e5a7dd6c659af8514a4b54f1e5b4c681404cbffbe2c3597621705a9b7c3e383e8aecf78763a8d96d4e442501054122aa7ef9ece2ccb27889d22fda456083d4192bf5277f59d139ec14e10cdcbd3aabf6a3419cb0139414a95e6734cb39ade933641d474b229a9769a20a86439c78c10d6fa5526087315839d7e7b519d8309f2ca8fb8f50a1fa62bbc44666e792e73969a15e484e798f4af328b96207cbe24fcb7f208f57425d0d8b91cd18ebbcb3a3a36479c8a174a10346e9cf0e4e224482767e572527af62c896d984ae2cc7b522ec5284682b4bda1c3ddee7a21a851030608095267fd7d0e13a765001f4152295c43e8ce5d1a7e32d80e0e336cf4e0c6e1d573412a3a821c74ca1c84144f9549acd80882f00f4f0f42870764042966d2a3ab821061a7838b2026d3e3a645a7db075404495b96b20b3d424c0459ed8452d2dddb032282984cc9a54d2b196c27c04310c632cc461bb18ba62683ad8e056d256b0e9ee9acc38cb3206f8c1fbe2b5cbe4559105cc4a5751363419e97ff182d68b4d2c1829869bda34e9ddaff5e418c319d1ccf5629beb98220ef3389d4cd2063ac15a4d3cb58414eed2997297d41855641b84c4ad367d1dc29aa205aa59196d5e43d9c0a726fcca8280ba182f4962eaac742a720e668d234d8fb5c523205513efe53366d4a359582a453a687b689535b2205c1437c5768d02888972ffe8fd2f75d17511035a935f50d2fd50905295e4c2fa92b32950e0a92959c4dedf527485a4d6cb4c69e20e776f8d5f67cfaa29d20beba9690bb4bad1c27083227eaefd904412df7e652314d105c3429f54da5396999205fa878ad973a9820263d1e536c8c49ab752e41badc9fbd848c25c8c9d572c9d5660a329520574cd522ca4e996528418a27db83be5e2861990431d9691765154990f6e2894bda8352a28e04c1a348cbe0516bd40d09d2969213279eacc4fc0882dddce989bbc691df11045de9831637cb7fda6f0429f7df722cf71941708f39f557fef7d45f0441cf7e88f051e3d97c45904545abd5b0495e8f9f0862acdb14e3daeda1840852e9e46341f9e88be910e4906f320431cee7e41b4f58f07c2108a357fff6c3299d79429044e64d1bbf7932fe83205f774a3957ae50b31104a9dc36ed69261044b998a73626ddb60104b142f7fe27fd67fa033975257129ee072f65b9784cf7816049df94de9b0f2499c64c07bd29dc650fe41fcd0b4ace7a20a93d370f861297725493e2816c3986f43842cb7a66ee409af5a0977f3b90d4f5b8a639d58124a3e379b14407f2aec630db0ce6999f0339c7b9a8a1e32835bf1c48c22f677378beba1863e97cf587dcc081f01d83d20b973710335569d695ccc5c50dc4f63a695f9b2c064b1bc8f37331ee92d87d9d0dc4cfa3fe548fa6737d0da4ef6b93d9aa1a8839b7c12ec7301bec3410f3560e95eb294bb56820eab847134a7a78909e8120c469d7e5b598819875e4a664c14efe57ca407ab9d24ee12b64208b985b870e96d35e650ca4984f51a76fa4b655c4400c2a4267514b3d161406f29526d3de4af92fea3e97d20f4289be40ce5f428eb787b7d37b81184c9f9fff794a4ad305922e95c5ff4f9d073117c8f977e64ea7ff52e12d90d7c63ca9b09be35f2d90b2adcef73a69cc6d16c8996fc4e37a0665522c90827fd063bd6d3a46af407cf5d94fe623f3aa5620c8e8f88ffefb23d42a9052dea4695029943ca940caf09a57f79c02e1bc2fcdc25a769b5220ff797acf6929c38f1e05c255f43b2d7b8bf1732810b3b25a786511cbcd9f403639b97a3aaf9bcedc09a43ebb98c6466f0261ec5ceed774909f092459d671f1dec4529640121d457918b957655202399b98ee11d77131ea53fef02081245c2fc6147304d2bac5b01108eb15f36c4c2906b992ac4eba6a71de420cd269f5344ab4cbc6f430084ac3e26c270c622cad619f95ab310e0649e4e2fefa0b0cac42f429f52fc855a5820ef63adfad2fc8e133abc8cda0e3aabd2059aa32a57a362f08a3936aad127b17a478d25c6430517abcd605f96e7365178f317dac7341ec2837f7a793f2f3e08268326387926a5bc2bf0539c8aa68a67a5b10ed4cd7bfcfcd94a616c4179391e1839552a205316809773d4d376dce82a0e9d932a3e96541d2a0c4792719c782944fe78b41ae8b186141f03b195b43b5fb0a62d7eab87509a91bb4eb8a3679aa309d7e2b484a7e8e9bf37ff68b6105d1f3269331b47898cb2a481593c95d564a5b155590e2e8f3d0a9f4c9d153411ecdae29cb4605c93e875032daa3997c0a82cd555bacac29c899642e194f952a9196822c1ab272deae4f5a2505792cc958323457267514a48dada4535cd0b6e48a826095537eb89aac186e2888d739f462e5161424137bf1544c6349bcfd0431d489ce14dd43cec91364bbccba8b712f9ea813a4932fbd65329c20587e5e55f82c1f6336410eb253a7feabbab26882b49a2ac4e75e6f2b9920683b33dba0425def9820fa584eee79c4e5e79720c6c50b15ffb60441cf5b67858f94712b416abf52a61eb3a7922a25c8696bf4de4f25b1a64e82742e6327835b8e6ba924aa341b3cc9b33412c4d6bf4c1b8388d7132488f39d2c5338354a7f1e41d2a3d47ffcec9fad7104b2ac77353ede088206f70af1d123bb2723c899f4c4b3d68b20c851df39a65db55a459044bdb68ba7d76e8920dfa6381627db7f2922c869b2eefbe2865df210e4cd9c62ba3c6af7350439de8f7c10195e55652108d264b55650b2661d42904526bdaad20f82f851b7f4658d20889b646faa58024112a64eaec7fced8f038224933653297cde3eff03f9ab94526b613f9035dccbc6a99231c57d20ebaa7f4aa53bbb593e903367d7a42ea8bc9c7b2006dbf89e643d90948ceeefb3add4661e885194ee381663951d1ec8311fa36536ec738677205c080b2ae9d881984bc6f486a60e64fbf0111bf1ef23a703e14f7e4edf06798bcf8194c462909ff4df4d2d077207f16aba6c3cb41c07727f901be99f729fe040ccd98446b3209a83f60d24bf10623d643cbc75034169aa8fcd987f94da40509596f6dfe19bc30662caa5f3a792b249bd0692f6b7f9b8d92b9fa906824a5aebd62fdf6b990692a6e67a10292a6f4403c9d433952af1ce406ecd98454689ab8d9d1988e71b833e93958198199ff3be2d9b1b19489a5fa6d3f87ee8756320a90f1fd9b97350729d18089b625c8d135739ac0b0349e42769f5f184fa2430903cc6ddcf31285d26f405e2787b9e0f425e20cf9cb0f3d4d1ca5117c8499f7dab67b9e0395c20fa9d572afbb740124df7d142c669d66881604a09cbe9f12c10adf42c5d732c9043f75e73b6de5ff90aa4bcbe19b7a0f2a6a815c85b1f5681dcad26c42553294d5420afc811693987b5a56c0ae47b791d4f9314881e2eb6a8d2890241c6b051cc35c62003058257fc98838ae8a69827105eb35ebacb9fccc609e49c4f8d5213481b4caf7e3d69bc0b13487ef91d57354b20b67db5e7ab537fdf73e2a724902b69dabda60799470279adf793fa7f86cd11086a7492799dd708e44d967490ba79b4520c728625ebda1c3c7a490cb2f5ce9ed0e33008ee2fcabb3d6110c32bf496824130e5315faa97028324638575b42d7f41b892d1a2a5dcf54cc928a82f482a05a5e35c455d737d7bd15cccb8f2826849d7736a53919f71dd057bd71e7b9b63e6032bd0c5a94489f272ec402317868bc263db4cefe4cbd66c4dffffe618bd0541ecc7f8f2259f6ab5da8214efb4e70cfd5e416b41bc94f547b3c5e8fe3c2d527396e459983157b173b1ad177b973d3aa58be1634f5c64b1856b95a959a988957ec6edb88a4c6341ca499c698a413fa8496141ead5242e87783f191b3b500e72e0488776fe0af2563ccb9b94d6a5ae20878bd97d2c08fdca9f6a2b4839a38cd00e9de332489515e45139ff294f5f259e6522741564cd244aa51d154625ad07356aac400438727c20ad0798e3860d3474a4a92ac8b9373b9d584a1d94a6a9209f9fc7f8d8747d99840a62d62d59eb9d34e6844e41fed038ffab9ac53ba6205c766e92d5510e5a0aa2e8bf5cd1b020ce6511282948b132e5912722578f827c26d426f5f3c1f26664b08982f06126324241929f3f86ac650c0a720ed1d9a77142a8ac92c186876ee4d8919f2089bddf749e2d3a3c41d00b23d4c4e63a410c7ac294527f49dee89c20955cd21a73b4c711ca32d228091dbb4d904b657c8a97452b658c26c839f6d9c3c2b7678a6a26887e1d378993c12a7f4931410e9e27729e847a0962d62ebbee58822422376ca80595635a2b41da0f955184ee1cb486942099d8caafd1d286dae4e0868e3b814e8218f24e9accf3bf4b8214642bd6c4856c073f12c450dab4546b4619d3b191781412c49341ca85f250eac67b04b164ce39e7583932d8d21104a5a2358772ad11d7e5d1ffe799716584ad5523662127f316eebe2e826c614b9556a7ec6da21ea03214410a9d3f65d3d8397a84a38c1c9c71230739c05146228896928ea652a6f67cd91fa073c60d1b6926504490535bc7d021df5f9774085297253932658d214c29c9e41943ae85208839dd99f67c44e79010e4b8ae146ed388b0940e822cdaf5847988af65c60a826ceaefda1a93017f68d978548120adc76c73ce983cc650812862639cde8e61a3dc1e53209b36153f684ee59d9692ca99f27a4421cdbccf1915fdd0b11c3da040124ae52cd67208956e8f279054c56b3295dd0944dbe42b9f723c9a40529e2983fd688d32e51c3c9840b8647aa6b205bf64992510a396b053151d5fe3ae04e2d5e8171de4ed91048236a59dcd62ca5979f3400231c920b6dcbd944cf03802d9e295e94eeaa9335369781881ac23dabda39ae44ef56290b265e83771f22da8d507eeff57b4d0518741d28f2f5bab68da4e9b2e0cf2d85a507f353a668748f0603430147f914e9f23772dba3c4d705ff49b2e674273017082f7c26ae0c07961356e17a4da37e1f5fa93c1375c178caee65d99d6ae89bc7b4ee963dda7f8792e482a9adfd66d2abdf9202ec82175e457fdccc468b7207d96514ff24188f8b8db82f8967b2184cc5f972c1d580b62c6a0aa2bfbfc8c5075064e0bf2573c9ddd4bf674e7dccd8274a64344a998bd9305416b1c13eafec1ba4e77b12086f9a80a7a3dfac3098be26d4ad3dd2b08aabd9731464f217c73e70a72b9968789c8ee5a41dc4e953c3c68ee58413cf5d173c7d8574110d777b1e4c54cf3f7f5a0460d55902afb63ca183578a9207ca6d92833f15041de0a17eff7e19d82982b2b2fe6309e2948a15c378c1ecfa6c388570ab2798ae756b7fd3ed639298849e6ab5c956459a9f10c1c67d8d071432f053e0ab2458f27377d9faeca4e14040f4fd22a7575a12085b00f55d14aa970f9030539e710fa1b534e8988c9603bfe09623c254ebce6d19ec13d41ead4902237293d26bb2c837782207432d1bef24ecae504c94fc82b15543bdc5661f04d903d89dd76136dfd5479c13541acd8121953c9f34c56f24c90bd4acc5aae909ac131412af7f0aa4f71cf63e60cbf044168664fa94a8515598ac12d41cc9b5375ae18273f9b64f04aa45382a0692c46a78df9fcfb240842c8e5666bf9bc7a3925844b82d8d59d99428e4c0f51dac1234118addaa3f3f86a798d69704890b7a3f6a57b6966adc344e08f20a9a826177b3263071533d6e18e2056ce6bc1849039f6834e9f0ebb813782148458cdd9a7789b3d7b704690544c2ded39ebce231d86be0892597eb8be9c4b051ba9c11581d649cf7162fee924024f046134fc49ed641592c01141780d3a5a7ab3752d49077e08925ecbeb68feacb636dd10c9bf564e23636ce085209dd4de2e5de153bcfb9d1055bddc79c89197b43976a041041fc45bbaaf3f9bd2665073ec40c30541700ffbb1d33589fcda0c0f443999a7fc1602046e5fa9326ae7da69eab27a1019447afc0fc4af7effbcf5f881a0738c5dab10fb40beb2ec8e11b11e543e10637d9c4ed329a74d7e19be07d2a9cbd822de4456ae5c0fc4f4aef240f0bdb5b4793c5367f0404aeb24c36626dd81e86fe3a7740addb4273b1045f34b3be4d9f99dea4012b75c8be1a203d93395ecd0d19a97990349d6abe6d02de23f2e07b25c75ccca1b83cf1d0752d46d8fe29fa1d3860329584e9d84c86f20a9d4adabb4ed16ad1b48793466d968a5e449db40b0e0e619326503a94c4ebcb5764635d740acf472c24c5403417fc924dd3c94862e06693a8e06e2cb7c979a8f0f3ae8fd0c2971196433be66207fca315fd920747a6520bc69553c15c5faaf2703b95743a8aa2037ba3f066265b58d49566f8ab71808969dcb6a4b13067210975fd5010371bb3f75f3abb28cbe408aee5ba23ae405f205d198be5665ed77817c5266ad820c17c81d5498cc9bf4a9a0d9023145f5f67c580bc40b9d4c8f56f317390b24ed30333a69b1408c9992c9a44aad97ae40ccffd0d2cbf8e361b182212c9c5dbc8b5781b461cb5cfc762a907b37e798694ea5a64f81a04dfc678fbdaa2f92025153c55c4afdc8dd942890848a31e7641528904fe59615556a4f459f40b8b09be2e2677c994e20a5f8398ce95c2c2d7613c86e9d367754261054caaa945f0251c5c2265d8fb2494a20e8092581d4f1fa3fee3112c8d735d2adb3eb47208cfc665d5279b45feb46207bbda9cf894c793b5a0cc2ed8d522a5f07b5192506b1332b3f6c59592e0d83bc59db2a6cd68be1220cd209952153fb6cf2381864ad34aabc030ca22597d139c810afdc2f08be612cbca678cff105297f4ad18467d90b92e9cbffb959f282dcf147a91cca3e9c7641109b44f6c8cdbea6d105c1ea93ce1594e5b44b2e487fa382b820fb26692ae5740b82cc4b9bb5185293c916c43816c32a3e2bcba8164437cd6831644cc1a3b420c6d45232a6a710b5de2cc849db94bce9280b82da4dda634aa7e962170ba255b0202f740c173e2c92ef2943bde8af20ee25eddd90c993d7ae20998c12d6b7c2a46445d6ac20cb96d0187dda2ac8c1f2bdfc25135a3d5305499c5ed22965a9209ca77bc614d4c7b951414ad77cf9a544b6cca720d8788e39a7cfd3b3a6206bba5e4e3257a520c8bf956d8df13da98a14243df22dea92d997aa4641f4eed3105aa2f7ed228af2b6acdadc5eace3433d9bcaa1a55090d365483f5913963cdda020e68c9e39dcec6c92fe274896a9b5d19e20c6264fdbfc3cd96745a13b410c0b29971e7f7382949f49fcaf6e7cd09b2085d2a1b7f671bd5ad604b3592f2faf9b2e2aaf6f898cd4ac726682b4a16abd52cef1934927c004514e45f524ffbe04312e6c4d98702d414a312f7c0e35e2d6ae4a90c46793b86bf19cc4f3018e0e34258e2a2ae78afd397a12ab97c6e688dd78c7184fd4cb2d0952bb09b19231afa7a78bd09120a89cf36f7fbef841a74082e4a9b275764cea47107407e11ed3cd74acb423c816f52abd424f295d3682b01fedb4debc997f8611e437f75251b2baa01741d2dd5839784ca92248192fef29fd9cd7edb8910306356a9c4e04d9e2aafbdbd998ce4d238258b91eb7b6b38720a6df28f770f22ab99f230d1c2c6843102be998f9a57d0f5d08c2854d53393c8bf00b2204b173f014f73feb315bead083207710cd38b2648ffc0f3ae3865941905b4f78aacb4d8e1d3a26f081154ce004d181206ec9146da25d0e52a346c9816e34208e13336b2adbe2a6ff40907e1d5ee1d3089da91441fb81e01a5fed93ae080dbb0f6dcd5c7797657dcdd8669ccf88695b9a0f84bf8b716d2eedf8a8f64098bbf0fe63592eb41ec8997fe921ddf258280d1ea092060f48e581a899e6db3fd56ee4e67820299d61d4c594d2d17720ac86d6aba03734b968056e0772ffc970a6da29e5f31884ae03395426f99af62342d3a1b6605a4554ba760e7c502a670eab17125a0ec4bebee4a2555f695b1cc8ff63e16732e74029848603d1a4881f8bf55811bdc10d849141b7a5e8fbb42143ef361047efdcdb890faa7ba341b3e192a541274fa2a45cd21ab8f33457d793cb96f93465fed9eee9154438683590cec2945eccf1a23e990682f2a45f2363644c6f2cc8c1193772ecd09146a381a493d8b4fbf3995b7dd467205aee60e97aff360341c74bf2e7531853e7e128410eba0c84194d7e229ff97aa6264363dade7167e53632cea69c76b374ea942007ca410e7094d163207f0e63973786b74a4d8b81a0ad964c4e3f1d0682f6125fd7f4f8643718482553faadf0cd17c89db7c17f562f103dec586fa59915d5cda1bb408c97b2ac4f27ada5c2058252b2bf6490a32d10355dd42066bfc9d25a20d6eb9c32137156a5d459488610c202e95c64573688cfad9fbe0279b4688ad3fa512af3b60229889a6b4e198f7b7557812c3ffb75bb518120933c19ddbe140f734f81a8ba1a3f6bd229bf342924b7612d7255a61d3a53fa7c263633bd1d0552c89f4c2a26d54d27289044b54c9697f4d2a12790d42fdb680e9f749c40d013bd29bd6f02416c2ae56926107fc7b2657e9f497d09e4bc37627b5d25903d9dd411b7ec717712c89b3129bd13238164edd69bf3db4720e9faec70aada8458db0844fbb34e333391578b41161563aa69e877391331c8a7e29cc558a6611034dd955229ba299784516eaed798350a0631465febb424b75b040c82a6cae5f9cfda72ce2f889ad2e99cc673d8cffb82f041bba6d3b1518fe90539bf776cb19893bc0c2f48f1c942bafd78ecf82e882766d183cbba2087951f13f95332da5ca4e46e161784f1a0c472465e737b0b8246d3ddd6fd0d4a690ba22621bc2ea5c6e8662d08326797b71cef61262d08673b7e803070460e6ee84080057460009561ca8e34cc58800504a003e548634741c9c6022e00800aa061c00b03787512487c47f77a282001ab715cc322f03b74a491a300010082c1800200b043471a2b204000500e4a1a3938a394810001e8c8813e8d8323071a09300100000000000000023bd0193b74a01d18481c484741c3010b581d37d0280a08c02700a04387a9410001941de5d4284000ca8e7270e4a87100000ce00109c8816cece0cb0e1d366c2c00000168c0018080c6a3016fec3838cc3803ded85106193a8a0d1b0918c30c68c30602c628c301c62003ded891830c1d65d8b0918031c6b003e91003ded8d1831b270d1b36123046188816d69470118d0ceeb334cecc40396cf4e0c6b17190db8e336ea8600c30a061c69b81cc30c0185f20e8dc79d39fc89cceaf1708a73ee5dc271bef63ba0b64994b1f362753157d339019bc63708124c49a6fd64040e3cd4066ac19c80cd471c68d1d65dc48c3d8c881cab8818619389ec7d802397768c6b31cacecc007563001c5c4185ab8f262b372f46ac618592067326d9bf4be6ccef1118c8105763edfda4d4f3635afb5f35cd7cdac538e8b79c1185720c95c5a4ec65cd9308615484a0579425dfa649c51ca48018e510582d4747aa49ece630c2a74a3f9b9b6adaf2aa6962534c69802d93cee8acab11cbba61d40e0c01852283eceeac4e958ba41860ed781060e1b65638c110582aa1653e62f1a030ac45b339533bee51e43378cf104f288fc494f3a7bae99c8602ba31c1a610c2790dcf49ab9566b9a678cd1047218d9d3b1b9a45926c76002f93f7488ddc564b099eb608c251094e672f5afd678f300dda317c0610c2590c49cdf46f7510f6ce8480249a5d116645f38c6400249e84bf5fa0ee618e308e44bda7749b6770ca192c1b6630c239052533369c88c63070b4e50a386192ac3981b677c148394794e5ddc26fd0f70181b3bf852867154c60e93868e3dc10c6c50e0831804cb731bfb339ca71171063e8641cca3f36ea53cdf76b63048a5f3bb3ba6390bf50583ac633a837a348f5a32609037e9b8b1262fe7f8d52fd017d88be305d12b988c9f847eaedceea2b40ef90c1b59af58d37541dad4fc7f49a598a222c881768800e5200732015e0eb4e3060d3e7241ce5a9b5a2cac54460817e458fbef93717fdc22f18e1bad4fd92dcf1fcb243aa9ceb388e0c31624ad171fa488ef9c4e5bb5f8e4586b78eaf8f1410be2596b4aca46d6d73d356a04e16316a856edbfb834c70e34caf89005ebb194de0df12c5ac1472c3e6071bcc85a958bcced9a5e4134d963c2f3c5ae2065f52fcd9cb5561065d49eba911b35b7fa6005af829d1bf990d335af9456e16545679ca361c67fa8821c1e64c95aaa956f2c15e40d5e6a541037c8d1b9449c3e4e9152aa53e5d914a98ebc9ddb7ab97b9669f7eb67cb9a72e8efe6868f5290720e6a6327a1270531ee737c17a5320a82d6f4399fdc5bdd7b51104b27f1a4dbd4342ea1205655f8133eb796930e0a62ca4d7932e73f3e41eebdecb94f79e9670c196c4038f30439c7a3e9649d4ba993a11c3e3a41f8caa1b4f4e864edc90f4e90d3bd46cb87aaecd8c72608fb991d5bf5624e7bf9d004c1f63e8648afd218692648e1a3417b4eda287c60822033df8cca89eef9a44b9092e9cc3949179d4e5996c0dd52fea6f94e06db1937ccc00d7c54826cd94bc89327b4107c5002a143b33c5968b4d828a3dcc72448a7bcfaa266d78b9f5b093e244132a9174bf55d1ef8880471fcb545456b360f2232d8b8031f9020ff952c21fc633a56c70e127c3ce2686e296f9a551b2a7c38e2a311c788fd580429e5735d4df946d59f1f8a305667aeea544d2dc5650ba11d94167c248224fabe4fb73268563e104132b7945f4944a814ef8f439c21ae1056e38310a4946336a535bda74aa7419074109de149853eb59d2048a59982f8abe823101f80209da650b927ad8f3f580d3f10fc84921dc4a898f4693efa6035f860353ef64092a139ede9257de88168f5f9c645e9cc29ab7948eb2e5de33ccfaaf24eaf9227fda50f3c10f76aab4aa86f131f77207c4cc2faa2ad1f76209dcaa4b25d78cd9fd50dc5471d08b316e29376d5581f4407f27ebe2d71154fe2630e049713337e3517a39ee440929babd5152ddd2d1f075296b1b958a247a5a7e1404e62ecf6e2b51f6f2026fbd7f87e354277ee06528aa2efb25b2e9552f7d106821ea135cdd7ca0692c8f4b71e9ac2f1b106625d2e5329cf5c8a22aa819c5310ba54b5f343bf4d0341c7abd3adfa95660e1a489fcf627dac31ab289e8198f4b48dddb6a8ac9d19180b99655c7efb280349a98aafeb759dacd46420c7d9ac9a4176a90cf23110542d699805216ae1430cc4f81f5e4259a792d13bc3471808bac13d4ded52a54a26163ec0407cfdda2edbb1b0bd7f7c81b8bea34bf8756ee80b6ee1c30be43c0b23a3a6743edd1c7e748161f0c1858f2db0e0430b1f59e00f2ca4f17105e2e69ab63b6d41ee345650def3b3eada3e3f5545db543e65f09c8f2a9063877c4ae541bc6d863ea840d41474780a441721c75e3c8408157d488118bf735fa768e2e7b52810454d9f7291660b3ea04050bf79a699f9780277aefbb26297651ee2d6da1783f2ccfc1de1c309e44d152c77877afb68f968024183bd09d7768c0f26240e3e96401e91d916febc73685f09c41621d52ff7ac83d03f9240d4a0e362ce8e955e412410ee4ce858f44e0f1f472067bda074eea9ebe87f101f462027d9f9b1ef4d448f6290ae754f7452970da5e2f02006f1d33586e9cba4b3591968987143479fc06318a4b469423e93c8d0a582d04039d03816780883203298d82453f2e4a943069b8d73460946fdc98312977f040c721ab57e49bbff054145b6b66c967d412acbbf5eb1429856e7d10bab8135f0e04542e325d11d2fc9c8f9051ebbb8e793bcfb67557f051eba40f374c642d4dddee1910b52a5ffe0a9da3705d1e082186ffbaeca32e5bc7b0b7267d18c97f79d3e856c412c7751cffd7baa345f0ba228f1ea13fde915a4b4207b101d57419cd0b1c6591063949d89ac7e859179c88220bad4e5c8a0c74f055d2c882995ced46ee2312a2c08caa4098b13d2f22ebe57103cd3cb3d43e73c5c415e8dd760314aa9cc6f16c1a31504959358f04f6e496ff36045fbbfc92bc5b96f156455d9b6bf96cfd8d5a982a0b1f4f5a83ef152417ed7b09f53389fb71415c4dfec9c63f46cb1a69d8218eefa4e68ec9c41a83c4cc17b65a6a59db8553625aef4681c4d7729c8a63d8aa97ad2a3a6f22005495c37e5d84dd19b725682c72848663d1ef62a03050f519036b869e7a02f8d672714c4142d544ca5336d52a507288817d73475cc6f63e16d82c72788415564cca42ad883f3f004496b924978a7453d3a41f6b9d2246ed473820727eafc2b9e6db4f6d804313745bdd3fedf53b28726cadefdb153d2a426830f356f7864c24ab9a284125f690b3c30a1566cce3bfb27f081154ca0f3e07109b2c655f1b9d936e119a804c88c72821f1413a481050f4bf4c0a3125b94207caef8f3a72fc594a1499c2ffe4207d714ae732441d0a699eee7f247d31f09fdb4fb3bd38951810449e7d89129c8683258ca2388f79d19c5e64437c81dd1c5daabdd65765b7d79e7ec7f41de3e66c68b4723482aea76c8ad476d9311445ddb4ec972fc46d78b207caf7bbaf476f192154116a554543a3bf5bd6c2248dafd728c9fca7d224490e257cee6858fa9510f411e8b99c383ec39a193862068cff0979385356aac39223c0a4154cdfc23bd5c47c61221484287f8242b5e09130f8268e6e94da5c84bf01004496b8ea174ca8dcaa9f408045145548cdc478bd7900720c8415ea520e393b65f0b1e7f2028dde953bf525e0dab871f8841c9a6e0493ece275d0c1e7d20e9a7a926ed0d1964d8830fc4f8f3b9298392b99dd380c71e8816542cfb7dca95cd71e0a10782ee5d586d0a3f56ca03e1c4858b3796c60329b553959081c1e30e045535a5e5fb82ccf9bab303794fc9b0f7acd37b9a3a10740a9584e9fce05ff2143ce840eceaffa43da7c8a0931e73202595732d737c10791d399046779d1ab9216c94e240d698bdf675720f381034a68c59cb2f99b4ebf106b25cca3699a2a653baf3700331ae840ee257b781189a733983ee3cd840ac2e95af4dc7bf51298f3590477e9af533a9871ab4f1ac5ab9d2b890bdefd129f622f74803414ffcf5c85c36163f0f349082ad9cacabee710682852ead7ecf3ccc402a4f1a32ca82d2e92c1e6520a7e7ff36152cc728150f321cc1630ce4a09256ca3978cc24f3d3808718c81f7f514ec8ce120d59083cc24010dae453a8fc98ff520c8e327260e38c1b64ecb8e1033074f3b1f7b2f99d9a21a3997764980c96f43370e44836d874f817481ab43b7aa5663125cd5176d8b88123f0f00231fdcb092927fb2ba7e82865d8d871a30c34747874012f612eda1c3bd0f0e00229e998f94d5a0a3db6401221e641aaa6cd6dc6430b2415af4559b01a3bf0c002b12c7335d9598cfcf415089f1ad393c554e2ceb50249937ec7c5038d29a8e4adc5620269381a8903e290200c6234cb0300531308001048260d85a2c158a0278a2e0f14800265281c32322622221c12101a20120cc6816018100c84c2803018100c0643a1504044932bce07066fef0303c11ff11f9d3889dfb30c4b3f4a3e670479edbc0733577a0c4bd77d5ee85e670879e8d0b770cf16223f7bc053f6b195abc85909870887b294a546888bf39175b7cd72b90f667cdae405902051c8365f4dadb69eea9403f2776c5704abb428ffd6f1c626b596a5e4bcb8b17aa8632e22fe70c1521dfbcfbc3af061037b4f467a220da0af29db8ba8f4a7564c1fa6d9cd901aee26bdd1ba2cc481814c302857da5619568ad7f1e4a2e75a200902f84281f602c92e46d636ebaac596af63c0c1da93cdd924970a4480c6655546a94a93b46a025740141e40b539b62065d686510d178f736baf1a7fc99fda72b77557b9224f50f4af7809f9e9e8d0d28da65a1da0c48cb64829495fbad0e112e280557e307863af2f410bc34b8ba5d2b5d0b1f5e3a96dd9121660e9f24fdfc59752d1424cb5ba63203741de0839dca5ac9a0972342732ab0967eb82b3fdf6e9687ac8dd4ec0276114ba00c5bc11c4327572bb3bd97d8c1a9f692d2698c68d37f4104bbe61c9a7303b3ad6a8f3914a2f25d0f105fa6f664028a01840da1bc71814327c36e8aca0dc813e082e1b555a3af6631a06d16202de3446ad3834666c0acc54128c381352d1cc02bf28a777740e82b418d1270261295e5eafb445276b01c269191be79b82c588e6e6c030f8c1da4cb67e54b6c0b6d07b368beb2d9ee1c1864e2e519d4de8d7ac2043c3604a861fdbdcc18c7b7f5180c9b936bf7df1911822993700952889173d61aa8f1a4d404fe5ab316d8473cd2e058dfe1fb231bd8b52810c976e243bb6cc4ad072988c91762f490cfc361a9958ca01ca611292c5fb5c115b41046422dfc4a246d3d57ff53bdca185b3044c0e7a20dec3d84dd971b2385e9916b654d8c334f8111b270c4bd58d4356fea8202b91709de3c81ef38721cee1730fc4b70daf121d56434cc08452a634c82720616516d2af59247ceebd3e4121b2c2f70d2c190cde740453be5b687183949117cbb135ae85e873aa98d7943107a7184caa1468a68c2df6be6b692823cf98267ea235d193751dc526e9804798dcc1c69b63c9206d5f068714ed37312519bd4771721a00b2215177291abbe7265b850f4d685e6f5042049f1e11846413c40edddb9923a4afcfb0ad89d9112a54fbfdb505339abefffc0bb7bd50c43e4719521aa8b2c2d0f3a2ae370eb8dcf76a1b78108122a1f994b610710cc1d5be206024814541c471b857a16ac0c00ae6baa4160b1f32593cdef769e5d642d45ae76adbb12e788e922062e5826bb7b798d89531413dd1d7b2c08305745560d4f3731d10609a1f60f24b5a140565355b8f38e0945ea476e91b71c99ec9aaa6868b74476f49054c7689ee155d5898f679f7ef830e44fec494928e6d9647069c7cc1f05e340d7adfaab8cf8d38f0f1a812853ee99c88af0f453d57e05d47ecbe2d8daef170d53ad76d8a772e8dcd905215ab7844ebf7d5a8a49d420548221ff69c4bb916be28dda17117588e456d17c22a35038c6eb3cbd9d257fc9f9038785ec4bd7df15d6a3fceab0310cb33ae23e760e52c66027ab73865a902a01134cdbe39b6ad2776e555b489d44f46f387f51efcca928070cac0d6dbfb85136369e55949746fcc81bcdd7cfc69b04616f54bd0edb985a158f181038680dfdd581619aa18acd7d84a91ad6e50bc7d8663b42a72b8c8ce419c624d386fab6a3a944431afa3ae1fbb24c027db5bab92767c6eced47d3e73a5a03adda3e99085286bc7b273f1760be7272ff2fdcc9a9b94ec28df682d6b517d0146767cd8e7d93021729d9777945fe2027fba39de8a91b146370c98d2ccb5bef184d85cf3ff24e3f171ac240a4594cdd65c9c443dd0d77dd53896fe0134160f9547134422bcc3287fdddb4481cecad4a51cf45feec3eff4c4564cc6b29fe867c7705c4907c86cce85a5e6ae79c799a569c5121848da4545ecb8ddc4cfd16ba30830ba32ebe2e497f034234b08990edf62351b8d700b2394347d16268c0a23777783ddec430992b2eaa9f40bf4b5d50f611dbe912799d0904186c284669b6912672ac389229047fb954d5057994670a618da7e872381f8bcaae9bffbe3d3ae97f06225fadd479e2965fdb4e35bb578809721ceda25a68c5756440baeeebd75a4d604db0ab993737ec22e5976b075c7581a7eabbdf34108ab28fae0a75a27feb878d4bf340013581fc9550915f98a3d2d6ecbeebe04c3975905267c75b7a9fb078e8d274383225346eec3ae5bc34e6042b9bf8dcb507f11e3c2b487e758ffd2583b9b2cda72a4cef0a55fe15d935a35abc3acbda8b7121c6dc054c465ae62a8addb82cc8c823489440196ef960297a90400b38d4c2b2193dc082cfecce00e4d3e9ba91fcb4a4920a63c0dc3ef60a72cb31806f0e410c99b4987165926b760035c66ccefbdbe094a59986be3aee4be51c4d16e53b2b5c08797a08746d5c000a7afc0c906e960896299cb0d4bbc3adfb6842c67dea212c7236527f5ca00f2ed44ebd53d27bcd44da15b6a18fa45496aafa8fa42ba0de9b87070fba47e2a87082c118cff77c54c71892dba80a573851e51700110d28c18c3bf81611729d7b7deb34badc3a1f1b4f358c0c2e6df6867cb9d752824dbb9f647ed1f5ee74f5e00961e77630012ae8360ee681524988902cf8239356bc613b9adc5d11f1b6f5da2c45e8b5da092d082fdc1a98ed5b88107a315e6390c596204f17b9533688c91899092b1ec76b11c11f27188b6dbea77f675b64f6e43217248769b8126141db2d7af336181c2f0c8bde9b2710b971e07445845712144f58b81a7fe47549e2b10c6fdfd0d545ebe7e21f5ca831bd0fad8f3d26b0ea4831af98076ddf6472ee2a532f044426a1b90978a5069af43084d048ee86a309e02aa5a26c228d2711461468d8b2bf25bc2464a2962500df72c6d300108d9ecaf9ca596b6029e5152742c9e2dc0abb22ee890b98a1294a5eb69444e46760c11ff2dcbb2190939ff2c003001b1946f7b6a82bdb7dab6a68d1415a822a8e6f5514b64e3e8489edeaf0cd5932f12675e1515631bf22911f4cef040f7e915da379964e66e9a383933015bf39cf4edd8f217debe0d1fd2411c83a5fb0c61df22c43c481ec2b7df0956b86e597117d26c75a4a3f934bb6494f978e8be525f4aac458011292dfc5a85db1b11b1fcf68ad2fdbbeff640bb60bdd4c881220c82d140ec3deea901a6db38d540a3063a92e2e3d8d29316417c39bb87d8a1f2e41d3702a1f0fd27d36d940bc6acfd16634febca0caef30234c79f295520f4eddf7a68be4195f569440f4e62e3a9d26212d88f4c73242c072a29e94f5b36520c9a9944173433b44e97961a5babb38a76d8f67aeb1ea973434770f7190d65bb56bed3786ad40eb248daf410e57488492d8ac7ed9050b846e6e6c5b99df6ea3e155110e218943b99df6a1e78a70ecdca200bcaf0e47b4fdc5eaa4f85fcb648890e6fc51301db6d4e6f71255ad2aed3eab5932d47d66bdd7a98965fb868aa0c27f811310efe56cc8e6ec7dc087a9b52584d5288c28d14aca24c3341259a4a4a68c86ca276f855539c81e9b65e22f67273d1c7e1eea0f7d609b5c1f420a5648493a7c82191e8fae335643f2ad93455dc95113c506ebe6186525c978724c5d8b0a4c6d9fce0abff4fcf0f3379461c111473a9f36ef9a5ac616368303861d648005d9b8e7845454ed5d4903e1ddaf502ee6aeacb4bc14fc2e19651355e6cd365239c7fd3af535a2ea9ded3c2b1a3fc10ce0580e9d99ed881fd647479120fc36462618259ec23dee403869393efb9f2a903f83902608212ce32daf72e7d6b011e23ead7895e7c90327e60d3b1a5da8323f2a5f6dc7de754373be5f383bcce2fbcac543dd3bb8dd9a8b777807868eec247d2ec7b01d517c9e33e3354e3445e5a85a16ca6bf8485aba962420868b8fe5dd17af3790127e95136d51b15ab0ffde8fc186d56a0a354d21882f3d086426e03711c695b5d5b2aae89422d9230b0c464b5c1e35753db9c7e6768f31a2eda8b1ecdd7b6277dbd10c7830f332e982867bd9191b10c758cadfc5f868e074bdd7902432def49522a0e442cd3e00fe22a3e9c008a8882cdb4bcc001e074802af8cd022338cb60b45064424c5667de87414d37fde9f97b656cf8a9e42b6e660709875103767455fb1ad31f899f706fd729c0c0c3d69b703087c6a43367989fd3b74a7c1807fa3dcdc677f89e650cd8ec4061ae2dfcad7183d00becbca6e94d0dffe5410eeec75335fafef9958a92cb51785cdbf60deb086023123968e0b642be2c0412f45e4021055db6a5966ce57a7db88a068776ad98e4818ad39c21ab84342d1ac04504b94ad97006f07200786c5c7022135f190ca6d7ab13b1de7bee3d47510d73cc42cbd487ba677d6556105368e74b02171d61a766b9e54ddd720edb2dc9eda484f6fedb874c0dca7b0b0809ade5d88598fa15a74a1c7246570586f2450e966109be4dcdcf6a7543143b3f1b6d942bf12adcfddd5e927c88f9dcea2b65f4a8796484d912f6e4b090a016b1025fa518c092a52ac34e9721b020f5892620c1be5abafe8593374e5892b3dbac34369326b47e02e88dcd10ae2f46168f814e5da8c567781582384cf57326c48e81730488de44b9a8ec879b3b1d0898409582e7f4c63597944009f5d4de3389097644aea4a8195bcf04f287d6d2496065902990b0ca9a3c21976421210ad5b2f3baad261a9912a359722935686a265a417d65c1340e41bd86cb8fa8b0e195769952739517549f02018406edd99564336f0d68e1114d3a1f49d023ecfbb056e13c886f3ace5985c1dbdfecb59bbdd33fcd6aeac504a4176fa0db62c29302885734a1d333c44b6a751c3a301be55ccba68b38b0b7d23d65635a1f11c7c1cbcc296b8ec0d864c1b158e36821b187ac835fc6b29b4461819eaade0b47415f181d9e576ae8946d35ef7c93ca80d6c619156a274d5099eb3d0b8ec06e473f2973f8dd74a398489da11b2622c776c753c7f96b14584c9f0f44def1e32a03a52e3fe4cc481ee7c19a0d0427b5b7ddf10df767c68210c7dc1f1786cf99bc7ad45e99404d1e4ac79d40cf56a93a83ed4d23c746a97bde8408f0dcc0566d3a2ae39e2eece61cbd32864bd79cca0d2491cb67c71f8ada966227e0b5e48627f6ffd45f9900d60519e3d3ebfb7d7e8364c91328dc963924aab88801ca24b4a56dc74a093dea79e319bce210f7cdbe7e91da78645369d5eae03da6bb17a2d3b0a5b2e6a26d55ddbe843902c2960683353a97d3bbfb2e41d668a7203f6549f4e205c6ec908a5da04b5eb93b93891f74973cb58ebc320f8d572090a287f5531d576fb3a95856105aed3f62873dba5ff7b8004d895be3a4dcf0aa128bd4eeb3911724c2746357b02dcb4c05901652e8a9b5fa03de23ecac378f438785171f3743a4d1f7e23d91c6bf2e94197665c28b574d226bc136e19674e9ee66b8ba29274997e12dfd0eb7a4d0218c5c6342261c2463e0e0c45fe1c50b8c64ead59f263f528fe46a765b1077a8580ea9c496a290b6da44b7cf415ca60e624db40cda020ff6258e2906a9c53988a1175a776667369348808b230b52bacde0ddc4901b6a98810ed765dc4ff669b2d3be0b5ac0061a707d5563ca126cb0cc8d44c0c04154d113f8649504e890306fcbcfdb1a631234d0d3d7113fac59b62a90d0ab8c071da718b97d8412c64a17833334a905b15d37e1b9e10d2e9a4b83840ae8490a338af30e7d6b3fb372a9f69ada74bc4d1579f478dc6612e6d9cc956a0843060bfd91c14f74b29ee532d805b4f483c0be262f471ac3ce8d11940bbd912694d94a6e3ef7357f0baee5f9322cd7568f1ae26c276d1631eda438101f0e53bc04aeabf6dc94f10c85ac3b522c60803279298c060740d402ec4cf4661bebb273669a2243b67ebca222d676bc2b99dc71112ebc4022bee6b70873774f23ba665a84631592ca7d1934ec8d083092ba95a16aa5d04cc250ec923ecc6173b05288d2e132791f66385212f80b7b66966252e064e01384b748cb4130b45513980ac92589a71914f94f549ac9e06dfc496926eb41be6378ca4841b9bf0cf7cdc39ec5ebf4024969ce9acae021c27a5c1f3a5d993727c471b29dd499d23f42a700d78855c86ce0cf2a6272c6ddff3331d3a3b0fdb340704258b95b4449af0bb474da3bc5818d2de2574548d208911e8ff18574347faf4f3b217d4492ccf1fc65ff75387d36ecd3a2498221822418cd149b06624deb422473d8dc30186eb066080f602511d444b410473b57889ff92cc84b3617a93669bd329ee5814df7facc80358392b3f149787eb8bb9cd68d4bac2f8ad8b9626df7a90c1de6ee575172aa93dc460227c6f2e01c7284c6c2840b93f3ded9d2b37ac3ed340dcee67f8fbb43e3aca7095c44861b888bce7cae1ca96374e8e993fcd4cfbf5ef482b804106d017d582723473b12adebdc136fbd40b00d642e7fd0c88e5b3ad36b39b807fc5c4f40178318ba682d5b26c9dce39e4672406b1c92b80b390f0326656444aa616c663ac39a5a82215ae784a3f0a01c05146140098a98d5c9cf0015ad791dfd48857b25f082eb6b22d89ad3a434ceb1e3f44d6b7d81ec12e2a8753559ea13de01cf7ada2699596203d14d2323c3015ac0bb948f1d6158922358d5bb3392f0a0842d79f2320eae6003616c63806bde99a45fad3d6cc0e4965469cf5118256b1ee049d7d398e863959878bc6d1adee5456c8a0135e0ff118f8f192d84e93e42ae9d3831522d5fa1fa65467a84c207b06fb66df9e7ce9d6d058c8e426705370d1564159a9ff8008ac239a73a9313feadbdae2a54bbd9ea472b5ca9f033c46c46119440bffeeca85081f94b214b69a3f64d1cbd82e2d44c58a9f4f031343743430dd4eaff2908067ef30b12fe24d5fcfe798577cb590afdfe632167973848ea10986f63cdeb2914d9cab14434c2400320083ea62ab529ea8a3bdcb48fe741c4ee6013029d7ab0e6ef3b2018b5255c5802805e9ee1e1ba40551eb7abf5126794a0cf87c9bfa7bc6a4b247ed3a7d266ca7eb731af1c98d9e0f4b365f03225a8182a6843d23f2b139aba2fd3bd7a72b6210d64484e7a2a23b5df30a7cb605de1c941f778f80961853e00bef583ee941f79c6ed1468c742662ec7ce6237096ae3a1e8cc3bc42ac31ec427e3af4ea5a355594792fcb693410b0a9f4b5467f683b90cbc35af7b601f60c6e1acd003fcf1841514dd57c6628c6c093b61f460fa755ea7ccf4c10c8ed88872dcc697fb3971e4c0f683d6a9758ea0eaff74d06c71647e59a418fce529d916a236d006011a4a7ed5ac5d494093c66ed378b230abcbc7a805d45aec33a552a0118313ef189c1d2a97572ec7516465c660bac060f45cc21d2b2882edbf36c83a22579ab2f958ab1a4b510b6dc9a478b3ca95e6fdb1138a57fcb0b3a387894072cab46753fa3543e28b2720b544c2a6fcf737ca0978963768f7fa498eb3dd6f177976c8b465438a0c9bcaf1af2789ca1787d623018a461f70687cafc4dfbba97778989012ebe1e7dea188c274bb454001da9be987c1b651c56f01bf50b7e49abca086c5770ff586b927291b67eca844ad9f523f1008b05d14542206646da77204b999afe2351a6cdcb08b50fbe4fa2d8b5d84ffae31e495d694bf248763c6a7a028e9883cc2a53cf62a2bf22fba16d3d6a8466f707107bbb9e1e7ff0c5c94f7e46ab148971b8ee2b8cb36faad4b63100bb6ed1ecf93e49e5ce419beb13533ef161913b8e657ff3c9c43da07cee679e77d190535267805851c9522f86f09aaf4919dab212a520fcef3ac621f7159cb1c6157689b536a4da9f766a71030ec5a513bbf1fe3656b53cadc6233e519e1e1d79a6257138b0c1c068680a9d6fa881dcf740f37e60a2fa95eebfd090a2afa929c930b69232a23b5c6bf93d419a8135d27cde5e5bcf8ccf24aac8fde9ab1e0663920277876366c179e60609615301dbcca487778607a4a179700c31e1749372b0a6f8dfc5309ccec1bf0d29425075f28e6cae7e32251bc89ac1ca3572901cac1c5915151c6b67e06ed234725c98b3df4e7e0c27b9e3ea59461664d0ad6e67fc5df815379a6b88f1889fb8b5f8bf338af5e846fad7510404928b335e8fd00a50464552eef3381984f0ee65d9ee6f37696dffb937116e7eacd39c133d46b8d9ac9e6e4c3301f098d5c90d19159204b8e33524954924e6e35c5af4b3d8f47284c143185debc63cf543251f6d0a45cfef84e2916fe7788f91cbd685c040e864427754cf1856dd7d6312c715584d036cbc9e529df8f78b58dccf47848a64a72813295df735af681bb848d2ee7ac85a5a6790af921d6fd8a3839702aec6958a838a2e5d830db461fc27970f56a9ff6cbff5e61913bf2b5e0fd30e35a17a49d52baebfdc09f8ab9274f461f497bb6eba4d225f1b27aad56b34d17e28b709217cb7db9029f59d4d15bce65983f52243fd1471061030e591eaff0a96c9107120d808f81aa76ec746d70c49d35d626254fca62c34f5558a688487d3cf19a529b96cce46c58261c1a58b16b726ebe0fb44e571cf49c5ffd53867d2c446d200c92f1d2b2f792627f3c1991012b2f5a01fec87c244ec5393d3f7699bace9b59872f514a9a9c2b37f22282083a6708e497137d0a5619ea7cf3a64b56645fad3f0134acf724dade07e42cf2222b390c05720d6d0a46ed6c0ae5b09f556bd1fb713dc70c4b7c075e49eb5c54da3622d652bc72d99871631fe10b2e4c6e2396cacff617e91d8c3eb05bf9a73c65069bf2293ccce31ab2d9c57ac028b246dfa1323de5b0f35429d313a760f956a5929c82600b02ab660acee4a0968f55a822b6967ac6253f0d880fe23dc5d243bcf3ec2dd112716798c51065c67ef965d46e9bb79806134e1aff2a839dd02fbe86d58bf46e245884b52ed46378c1c058a897e4bfdc8336814033dabc5bb8af792ec057b52fba2bdf05027bd0489daeaeeaa9ac11aa0aed9f390dfac6761eef6fcafcc18c3df59d70e6e1b975e3974d1af22920d3deab24f731ba74c199382151ffb9fc03de46fb77a68fa88e797f301bc5ff6fffb8c748ef15dc2aa02afbc66818e6eeb14c37e114cbae2476962dbcd66a9d8807f71f60297e8ca89eb7e23e0157da7f697d426a748adafab0bab1637dd3b0b553f732bdbd274aea1dc9a2cc2b8eae52495801df6ebfb4b63f271b9d309f3242c4900e494c1ed3d141f612713bff316c6968a0199a43549b2deb429de218ef86902048e1b5cc2944abc4c63eef25c2ba1bcb680ee3b7d78c7d6168973a32df5a428bd499244199c66a91ee9ee068263fa6462ba799a8752e723be31c45b23e6e40c73dad314f2eea937a635769f7da508f1ba00367264748cb68a5a203afbf3b4469045b9640a119101e242e7db682e77c367093b39bd9aa0f3be17eb095d4f45400a552141777ba522caddeb0a8aadda77a41b920c3561f9a28f043436c2dde10745e4ce00b74741aa44c94b67e612a8423305440bb54a067c35820066773643bd2655442e291b085d97f65e1ea864aff1093a7eec6eeba2242f82d0b9653f84179b6b6cfa1efab84eb403d83e2a261d31db7dadb0814fbe4fa64664eb38519571639ef2ebca2041324471a6e631105f338c23497cea88cc4426d64709ba2ead38d69931b32da2ab74139855d33247d9dbaa8c9d351ce3ad857bd1458e44583759d3fb50d16da62d508dbec3072dc92154a91be59096668a4102f6f09dd9199e4bd9dd15f04b2d9258737602584f3b7cfaf4fa1ffef459870e874aa52436ef1bec08e6fe2566b07c191fab2c019eff5191b3acd8813ee3551fea999863db20b5f894b5553b34f85fc9bbba1a8a490e39201073584fc4eed9873fb85c011fb80ffd5d7d6ece18d878ac0d565db715f84a809e2ffc574a750e650bfe2b26f855cdfb3b6fdc08952732e78e64c697cf041bd00d50bba26751106cf581dbb1bf9f69f05e9fcaedbcff378cd13ebfc4818fa10a86fb1b7112a15ddd19a5d1f765f764259f44ec92df0d18ca9463deb4bc5694f070c39d3e581b853f4ada70a353c00d697b684a177ec7e4254f2a497e45aca8eae11e706c763a5d2010bc9c53b02466dd6ea4df68bb03608aa6328703d0caf9b4232f089bd76c188fbe4421396082f4230c183af89e58981954432d444c8a345a463ba21cdfd067542e8ca671587e422431a2d581a7432f711acdc9250ca406da4e20ba8b560e4d2b50ff7942a5a2f17aeb66ccecbc51bd599efbf63605fb8df38300914f811c2fb519a1a182427bf9a1492974d22189edce32ac2fcd8e08572b78401b46753f0af5a9501342b5f73f4bda4ad8d6a1b8bb1c76c798206519de02b5c3593dcd1faf046be35d54ea47e6914d01bd4aa4bdaed451d0894a8c2ebb99ad0b174eec3e194e55dc2e5b2b464bcd05662c9040b61316feb3f00203879d90b06e05373433e85ab8a3abde181b123c625b2a7db1f2e496cd6d8d5d7173ed37ab833d4d8172d29efe1e8226f6907327f6f4b51876438bff7d61a121706f78954cb01adbdb3308a6b6c3db41282f70c9d3892c869dff43a3e91561b199344068f7385b08917c67a42653dee7510fd4ed25954ac82c1f2261f9e484dc70b525392a64dd4c3e260b8490f8207f7939e45d42ab8764e78a8d222e9d8f9dbe78ea57921b51b3e1828f66a1c6cae8393a159a7c705b43f3b479bfbc0884d432a45d7c158f2c59c40b1584b7e85bc2b42e0cad3467c784a57038c89cfe69d7bc99fe1b6be71e024844a2ca4016e73150a25f67861ca23d2c92ba0b6112218e9f5fdebb547c933843b9c7b6a9899179d2291850a40e7e88076d9f7be3ad8e75b42da4411831fc8c7c786b4c0076c26082a44b9bdf4683ea83d88ddf48028b1888ce18c9e1bccb9bd483c4a07b3f311788f3c7812111e63bd498cc0bc4dbc588a32ab218d8074c54fdbc6e9cfaf07aeaa0032864f0bb7c101424c504bdc332838642edb67b2d04a1c9ee10ea60a3e32fe51b5b7376de42f078b1ffebfa48378b70407d808a54b835f188f4be8d90cb962a2efc34855d66b93167828576ee54cf895a068edba81f8b46281250e09e462a7c4565e315590ec525ccf8df45cf9e55c6b1befeb1de10117408c3222ecc6508150b7ff4207b90e3d67ef0719749ece71440eca79f2af172738795e0267d199f76e81daaa19569323b64d86b4aedac7be78771576c429fd2ce8c86f338234c371499603bb4605a808783acbc38c4929eb5adc22ec3e177b3e4ad89f6bbf9b63e6e2d9d1f10b9eb0432fe428672ca5db0d7948d9dd36314ed7e0645c9e05d0e7940f2a748061bbd0ebb23d0c032aa9e01b13a099752d9973c9209963112a999e2830abc2bfcf2e9448a2618218cb9cb9694add0cb852fa13e86f7748519bea86fb98f7e8714224220a0d8cc7632c299ad6e1518037a6bd83266560f546d43c14a98e59d332dcc409f0d131f679c8708ec41eee3a57621bc9b1e78a5daf1e4f1816f7ce68692581b3accb433bbac62b18c6d92ee31c5b904db62af3dfe1ecb541eeebd1fb06e500e3db1e03901df7514447eaebac56bf0577039e89807594c0bb5879e2d87a82502101c4c46043582343149bf9d062c35fe190f329be6fca881252623f451d823f7e6dc6d96cc2f202a5e17bf8147584e82ec11acde393672cec0c7b60a9a0b86e99fafce1d61e8f3c6e11cdf109ce4b06e2491861feee21e71143c9a82730c81ebf045ac5aefb6d1748112203af1aeed4c06a289381feafcbea4a602f65371d4bb91920baaf105b10ec1d8aed012e98554783fb24f8c3e757b89da60de9cde2500841ba217fdf43d39ec66041097b6e84c20090c23860bb284a02660f080c53c13b9584bd1cac9e3dcae9d051a92704dd5adafe089d0b40bb7fbc537f892dfef6f22df8200a55a9278a93339c448adb0fcd6f190ebb814353b471d6293abdc31dd88d6008a53d69347ee9e411a10ed2a5b322aa0322e0cc1acd92d0c6d5a5bd3e5208cc1fa0b487b6fa2225ba454b2dfcb8e83dac4183f5b6311c96f53341d237bb9e993b212bb6170712ef5a21cbd1990c4fb675aa1c5077808ffc518f2a16dc253e1ca65a96c3170f338939c54f5c65728e7a608e41fc74c75c8c44ddf06cfe3dd4844a2b3a9610c765145810f5fde301700b690fbc4a9c122c01308925bb33a89dc237fb8e985d19370aa7fb5f54e94c5220f4fa297a8e8824aa41ee8bb675e9be80a66d3451db297ab9b4d0b6e973ecae4b014662e60df65b5ebe3e565af545a01b282d41e803b828a7bba8f4202a18c22531fbdd153ef08e99f8a24f433d0b024ad59213bf1d5150c849004b3aff41df0ec4a10f9a3cf213476a81456fc0031a8d37219d185878a6f90d0ce7b0193aa3e2d12074e16f4a214d70ca2142c85f50059f41a30e04fb1e7b988b2fa871969d3197dce3d0d47773d179d3a5f856f795c0d61cde365c34699bb2c673c346b2d75296b2e471edb09164e42a25e7a4b3564691669a6c585bfffba718796d0f64033488ee54b7c902375c7044aeb3d77e58f50e46faab5ee720acc6c60107529bc08117d48207f40773b5553e56959e34fe01640854564bd6da721ec652e9888ee24af81bbd4d0ca6d5278bffb798cd0c97e6bfdf922dde75de337275437f5f1bd733ceb98374e4f83900bdef5cd09063cbd02bcd4c0c385174b05058a116a2a0bc155ded02123a647b9f21044cecd3960424d70a66aef4b36bf408b37f49d00a728f5f7f4350b71272e36667130e789d27aba6b66f6e37a940b4d5a683a6a9716a123262f55d2116da74309515f25766a5473ea08ed1b8b1e417c80be254402a8e44d8ce8a96cac1207408847c547aa74114b483a682771ce5f9330424dbf8da2e2b306a8a31ceb4428272aa93df0b069bbb8cc7aa8cc2b6d94877624c59096111581540161b73998ebcad9ed0935bb06f2927a337dd763a1d95c2c4bb8c656a86460705594c6c0fc523c6a7829dd432e5e82dfbc1b0c4c858da77bd50833afe2bace65ea28dd3b5cf70f3a63dd2003201082f40323e9d103b85542266ce1da1bca00413820bbb0182599af116e00896a8a7cdf36d15c997fc03a1ab301ee2b6daf3c0a55e5f88df85989a1ec298442fdbd03157689aa7df5b29cc5b32c4502d890387c900888887d23af1e1b07dc0d4e8ef180158fff5f89d8b4d093a6aed2f3992a63e820fd3657f500b403dc7fe7b4a48bb97bd7e142d6dcd614cd67fea9b120fc57beee57136c945991c931ca9c4ec57c30affa2bce01f02e6fc34cb83c945b01faebb4c929a0cf519b441fc1870997b319ffbf6cd2884e2dd3734bae1e3ab2937b1a2a43c36c5e24850e446d3447df4d214d2aa8a29e3cf55f71e4d2742bd0919d0dcd4ee9af06b717bd71aa657e8075e0fc440706846812e0a0bc351bbac3fbce92331d37c2bb230825e880282a5aeff880f213dd821db2b37b6fb485e16872ea5190e1548df147de29c9d72f0c178944cdf86ff14908109fb300b1d57f78487dabeb62137c3fb6b254b2f1bbb1ba60bb8e5462c3d0e0f68c16fde1f807b0800f254b42a9d47b9e1be9f32912d149cc3320f6e6e69d52b19fb0d92d23c9fce91f1699a603e82497f530c2a5255e351154e91ad07bd0a683558ca681b9b874512c12ec7b3d2be9d75746a14376f59b6df816bb8dfc7fd7bec4ef79f8ee93b1bd2890fc6fc8da7dc6fe8fbf737735ac834ba99798bbc838e25506c79d048f9f656c7ca29496e7b6a816054a0ae46858ac9dbc89d97f967311540ad71508954d4d90e49c529d4cb3065f08000e747c88ed9db4a4ede9f042b2640698e3d2ec24b13", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3a6772616e6470615f617574686f726974696573": "0x0110c8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce801000000000000008270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5001000000000000004c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0a0100000000000000c9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbf0100000000000000", + "0x3d9cad2baf702e20b136f4c8900cd8024e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x3db7a24cfdc9de785974746c14a99df94e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3fba98689ebed1138735e0e7a5a790ab4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x42b50b77ef717947e7043bb52127d6654e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x47c9410b11325752265d54845357656f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x4da2c41eaffa8e1a791c5d65beeefd1f028685274e698e781f7f2766cba0cc8300000000": "0x1003000000010000000000000002000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a0600000010ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c682253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b000000000000000000000000000000000000000100000000000000", + "0x4da2c41eaffa8e1a791c5d65beeefd1f4e5747352ae927817a9171156fb3da7f00000000": "0x00", + "0x4da2c41eaffa8e1a791c5d65beeefd1f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x4da2c41eaffa8e1a791c5d65beeefd1f5762b52ec4f696c1235b20491a567f8500000000": "0x00", + "0x4da2c41eaffa8e1a791c5d65beeefd1fff4a51b74593c3708682038efe5323b5": "0x00000000", + "0x50e709b04947c0cd2f04727ef76e88f64e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x7e939ef17e229e9a29210d95cb0b607e0030d54899c05f791a62d5c6f4557659", + "0x5f27b51b5ec208ee9cb25b55d8728243308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x5f27b51b5ec208ee9cb25b55d87282434e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca0b6a45321efae92aea15e0740ec7afe7": "0x00000000", + "0x5f3e4907f716ac89b6347d15ececedca138e71612491192d68deab7e6f563fe1": "0x04000000", + "0x5f3e4907f716ac89b6347d15ececedca28dccb559b95c40168a1b2696581b5a7": "0x00000000000000000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe700e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc40ffdb9747f7a8bfa200ea2291eea6bdf9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f0f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc44d176c4abc6d2410c43423c2714b909c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11520f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc465bb58c29c9825c1f6727fe8c41650ac82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c0f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4829f3f4d6d7c991993e0c044ae97410b043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0f0080c6a47e8d030f0080c6a47e8d030000", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a000000000e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca487df464e44a534ba6b0cbb32407b587": "0x0000000000", + "0x5f3e4907f716ac89b6347d15ececedca4e7b9012096b41c4eb3aaf947f6ea429": "0x0d00", + "0x5f3e4907f716ac89b6347d15ececedca5579297f4dfb9609e7e4c2ebab9ce40a": "0x10043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11529492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0x5f3e4907f716ac89b6347d15ececedca666fdcbb473985b3ac933d13f4acff8d": "0x0080c6a47e8d03000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a000000000e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca6ddc7809c6da9bb6093ee22e0fda4ba8": "0x04000000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e169030e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0000", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a000000000e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x0f0080c6a47e8d030f0080c6a47e8d0300", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade980e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x00", + "0x5f3e4907f716ac89b6347d15ececedca98c2640cda6c0d801194a8a61c699224": "0xc8000000", + "0x5f3e4907f716ac89b6347d15ececedcaa141c4fe67c2d11f4a10c6aca7a79a04b4def25cfda6ef3a00000000": "0x00001a93fa350e000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaad811cd65a470ddc5f1d628ff0550982b4def25cfda6ef3a00000000": "0x00000000", + "0x5f3e4907f716ac89b6347d15ececedcab49a2738eeb30896aacb8b3fb46471bd": "0x02000000", + "0x5f3e4907f716ac89b6347d15ececedcac0d39ff577af2cc6b67ac3641fa9c4e7": "0x01000000", + "0x5f3e4907f716ac89b6347d15ececedcac29a0310e1bb45d20cace77ccb62c97d": "0x00e1f505", + "0x5f3e4907f716ac89b6347d15ececedcaea07de2b8f010516dca3f7ef52f7ac5a": "0x040000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaed441ceb81326c56263efbb60c95c2e4": "0x00a89c13460200000000000000000000", + "0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3": "0x00", + "0x5f3e4907f716ac89b6347d15ececedcafab86d26e629e39b4903db94786fac74": "0xffffffffffffffff0000000000000000", + "0x5f9cc45b7a00c5899361e1c6099678dc4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x5f9cc45b7a00c5899361e1c6099678dc8a2d09463effcc78a22d75b9cb87dffc": "0x0000000000000000", + "0x5f9cc45b7a00c5899361e1c6099678dcd47cb8f5328af743ddfb361e7180e7fcbb1bdbcacd6ac9340000000000000000": "0x00000000", + "0x63f78c98723ddc9073523ef3beefda0c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6835e433a91d195a7e84cba463249e274e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6a0da05ca59913bc38a8630590f2627c2a351b6a99a5b21324516e668bb86a57": "0x00", + "0x6a0da05ca59913bc38a8630590f2627c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6ac983d82528bf1595ab26438ae5b2cf4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x74dd702da46f77d7acf77f5a48d4af7d4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b150e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c01043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d0132eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e115261c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d000182c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11520182c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c019492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f61c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f0132eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11520061c091f7b7cb03000080c6a47e8d0300", + "0x74dd702da46f77d7acf77f5a48d4af7d7a6dc62e324093ba1331bf49fdb2f24a": "0x04000000", + "0x74dd702da46f77d7acf77f5a48d4af7de5c03730c8f59f00941607850b6633d80863c87cd8a129fa61c091f7b7cb0300": "0x01043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d019492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0x7a6d38deaa01cb6e76ee69889f1696272be9a4e88368a2188d2b9100a9f3cd43": "0x00000000000000000000000000000000", + "0x7a6d38deaa01cb6e76ee69889f16962730256ea2c545a3e5e3744665ffb2ed28": "0x00020000", + "0x7a6d38deaa01cb6e76ee69889f1696273f0d64e1907361c689834a9c1cb0fbe0": "0x20000000", + "0x7a6d38deaa01cb6e76ee69889f16962749d67997de33812a1cc37310f765b82e": "0x00000000000000000000000000000000", + "0x7a6d38deaa01cb6e76ee69889f1696274e7b9012096b41c4eb3aaf947f6ea429": "0x0500", + "0x7a6d38deaa01cb6e76ee69889f169627ba93302f3b868c50785e6ade45c6a1d8": "0x10000000", + "0x89d139e01a5eb2256f222e5fc5dbe6b34e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x94eadf0156a8ad5156507773d0471e4a16973e1142f5bd30d9464076794007db": "0x00", + "0x94eadf0156a8ad5156507773d0471e4a4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x94eadf0156a8ad5156507773d0471e4a9ce0310edffce7a01a96c2039f92dd10": "0x01000000", + "0x94eadf0156a8ad5156507773d0471e4ab8ebad86f546c7e0b135a4212aace339": "0x00", + "0x9c5d795d0297be56027a4b2464e333974e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x9c5d795d0297be56027a4b2464e33397f43d6436dec51f09c3b71287a8fc9d48": "0x00000000000000000000000000000000", + "0xa0eb495036d368196a2b6c51d9d788814e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa37f719efab16103103a0c8c2c784ce14e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xb341e3a63e58a188839b242d17f8c9f82586833f834350b4d435d5fd269ecc8b": "0x1003000000010000000000000002000000", + "0xb341e3a63e58a188839b242d17f8c9f84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xb341e3a63e58a188839b242d17f8c9f87a50c904b368210021127f9238883a6e": "0x10ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c682253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b", + "0xb341e3a63e58a188839b242d17f8c9f89d1fb17def62216d598940d64654f69e": "0x0000000000", + "0xb341e3a63e58a188839b242d17f8c9f8b5cab3380174032968897a4c3ce57c0a": "0x00000000", + "0xb8753e9383841da95f7b8871e5de32694e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc60ffdb9747f7a8bfa200ea2291eea6bdf9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc64d176c4abc6d2410c43423c2714b909c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc665bb58c29c9825c1f6727fe8c41650ac82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6829f3f4d6d7c991993e0c044ae97410b043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0x047374616b696e67200080c6a47e8d0300000000000000000002", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00e473ba7fd26e0e0000000000000000", + "0xca32a41f4b3ed515863dc0a38697f84e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcd710b30bd2eab0352ddcc26417aa1944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcd710b30bd2eab0352ddcc26417aa1949f4993f016e2d2f8e5f43be7bb259486": "0x00", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30e8cb253c2fa33f382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c": "0x4c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0afacb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b054462253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124bf0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f0426e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3572aae990801cce1043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d": "0xc8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a650edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e8768", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d12736684524574c32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152": "0x8270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe32330274bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584ead90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451cc083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff84877992cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a543", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3deca1f47c95ad9e99492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f": "0xc9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbfae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e24553306bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19501153f927dde4f0796173676e80f0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f04": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195016d5d491326e20327061726180ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195022df7698c94798dc6261626580facb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19502d8e55cbf27bd5fa6175646980763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195047d6f38d0fa588e4617564698026e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19504d5070934a9a468e6261626580b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a65": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505bb3161fb65601ff626162658058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe323302": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195064cc468ab603f8926772616e80c9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbf": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507466b8cede2256a27061726180ad90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451c": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950749c8bce91e643ae6772616e808270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f50": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19508cec3cc48ae13ec86173676e80def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950961fc927b0be61a66175646980f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e8768": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509c34aadd3112cff46772616e80c8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509d726f19fb7e918f6173676e80c083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff848779": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509f08b41b3412bdf7696d6f6e8074bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584e": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509f796bfaa6092095696d6f6e800edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a69397f216a7497a6261626580ae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e245533": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ab8402a39318e10f70617261802253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124b": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ae71ab667a6367956173676e80bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950aee25d4290ea4d846772616e804c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0a": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bad6b6ae80666780696d6f6e8006bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58": "0x9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d74338095b2b3a5f7061726180161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68": "0x043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950dfe86daa30500684696d6f6e80ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b05446": "0x82c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950e326352daf693946617564698092cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a543": "0x32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e1152", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x10043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170d32eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e115282c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20f", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x10043393e76c137dfdc403a6fd9a2d6129d470d51c5a67bd40517378030c87170dc8caee6f6eddc41c6cc55e554343392cbc13d2a8a57b97f6f85fc965bdd20ce8b07d600e3487e2712dcc3879c7b17c9b29cd2243b45f0d9343c591b89cf82a650edf2a41cb81178704560b02c35f5e01a5a97a568ebc10c025ade18b6ab2fa1d161d0af40e6efc165c17d0189bd2d770bdfa0a9b8393cb89113f473a2e948c68def964eed9a73f8a6610f1a0373378dca6f277eb7787869ed5841893105ad930f89c97bf5b2c07c05c84eebce4ffc7b28766946c03741fd1a71fdae0942e876832eebacd223f4aef33d98a667a68f9e371f40384257c6d31030952b9d94e11528270a62b61639ee56113834aecec01de6cda91413a5111b89f74d6585da34f5058108e1651614afc6a535c426fc013945e93533faa33819fe4e69423fe32330274bd654c470ed9b94972c1f997593fab7bdd4d6b85e3cf49401265668142584ead90a2c3fa2c756f974628dd279adb87935f7ea509856276e3b86f759b22451cc083b0d0bd7d6ffd14562b4c9e28738b087ccc32262170c633c18359ff84877992cb05c48fc643f057626c669604675c5ad5a836266f260ae7030c6fdc17a54382c3105dbd4bb206428d8a8b7ea1f19965a0668dd583b06c3b75daa181fe654c4c669b04865e9acaf7b72bdfcb0099d70d9ec63c8c2d6b8cb0552815d7b50a0afacb2f987caac6c1290a9784b1efdba78343d39aed805addb12945efbe444000ca3c2703db1633a27eff681d979967988c3a6752c669fd41f1abde10f3b054462253ee3c02d89582602ca5b0570cfc01dc82cc8d1b9d2071eb5db6318749124bf0e6c42698fffc28f9fc769fddcdf165af54c171cde43690cc8f73c853de1f0426e2fc857945d01520797a75388c58e710c9fefedd28387af70880f1682be41e9492b8c38442c79061bdbb8d38dcd28138938a7fd476edf89ecdec06a5a9d20fc9a68a26e9aa37ba6334f1a20275e3be7d3a9d4aa988627eadac8ea0d0a2dfbfae240842b74e5dd778972e451558134f434c7a1d8a52bc70519f38054e24553306bd8fd81e50cda2bd67bf6893d921d1aae5cb08409ae43e0bff4d54e1830e58ea9400f05e7fb75a3f7a92febbf58e5a3060dd06132ed6d5d68a3d75ec452826bed3b452f869d187be58a4ba98588611084283810728fa75981e792beaec4151763d070989ead31f265b40cc7a0cd29d47799b766d6a7f084e44c82baedfc01e", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5c41b52a371aa36c9254ce34324f2a54e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd8f314b7f4e6b095f0f8ee4656a448254e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xede8e4fdc3c8b556f0ce2f77fc2575e34e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xedfb05b766f199ce00df85317e33050e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf5a4963e4efb097983d7a693b0c1ee454e7b9012096b41c4eb3aaf947f6ea429": "0x0100" + }, + "childrenDefault": {} + } + } +} diff --git a/polkadot/node/service/chain-specs/polkadot.json b/polkadot/node/service/chain-specs/polkadot.json index 71dbb9004038d1394cebe568627e1e6163ee9049..c235cdd09d8b6eee517909052556a02427495d09 100644 --- a/polkadot/node/service/chain-specs/polkadot.json +++ b/polkadot/node/service/chain-specs/polkadot.json @@ -17,8 +17,8 @@ "/dns/boot-node.helikon.io/tcp/7072/wss/p2p/12D3KooWS9ZcvRxyzrSf6p63QfTCWs12nLoNKhGux865crgxVA4H", "/dns/polkadot.bootnode.amforc.com/tcp/30333/p2p/12D3KooWAsuCEVCzUVUrtib8W82Yne3jgVGhQZN3hizko5FTnDg3", "/dns/polkadot.bootnode.amforc.com/tcp/30334/wss/p2p/12D3KooWAsuCEVCzUVUrtib8W82Yne3jgVGhQZN3hizko5FTnDg3", - "/dns/polkadot-bootnode.polkadotters.com/tcp/30333/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", - "/dns/polkadot-bootnode.polkadotters.com/tcp/30334/wss/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", + "/dns/polkadot.bootnodes.polkadotters.com/tcp/30314/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", + "/dns/polkadot.bootnodes.polkadotters.com/tcp/30316/wss/p2p/12D3KooWPAVUgBaBk6n8SztLrMk8ESByncbAfRKUdxY1nygb9zG3", "/dns/boot-cr.gatotech.network/tcp/33100/p2p/12D3KooWK4E16jKk9nRhvC4RfrDVgcZzExg8Q3Q2G7ABUUitks1w", "/dns/boot-cr.gatotech.network/tcp/35100/wss/p2p/12D3KooWK4E16jKk9nRhvC4RfrDVgcZzExg8Q3Q2G7ABUUitks1w", "/dns/boot-polkadot.metaspan.io/tcp/13012/p2p/12D3KooWRjHFApinuqSBjoaDjQHvxwubQSpEVy5hrgC9Smvh92WF", diff --git a/polkadot/node/service/chain-specs/westend.json b/polkadot/node/service/chain-specs/westend.json index 697675871fcd7b4b11cac5a25e71c704d471cbde..775f3e72ac75578a0f0166cba96531244af760c9 100644 --- a/polkadot/node/service/chain-specs/westend.json +++ b/polkadot/node/service/chain-specs/westend.json @@ -14,8 +14,8 @@ "/dns/boot-node.helikon.io/tcp/7082/wss/p2p/12D3KooWRFDPyT8vA8mLzh6dJoyujn4QNjeqi6Ch79eSMz9beKXC", "/dns/westend.bootnode.amforc.com/tcp/30333/p2p/12D3KooWJ5y9ZgVepBQNW4aabrxgmnrApdVnscqgKWiUu4BNJbC8", "/dns/westend.bootnode.amforc.com/tcp/30334/wss/p2p/12D3KooWJ5y9ZgVepBQNW4aabrxgmnrApdVnscqgKWiUu4BNJbC8", - "/dns/westend-bootnode.polkadotters.com/tcp/30333/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", - "/dns/westend-bootnode.polkadotters.com/tcp/30334/wss/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", + "/dns/westend.bootnodes.polkadotters.com/tcp/30308/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", + "/dns/westend.bootnodes.polkadotters.com/tcp/30310/wss/p2p/12D3KooWHPHb64jXMtSRJDrYFATWeLnvChL8NtWVttY67DCH1eC5", "/dns/boot-cr.gatotech.network/tcp/33300/p2p/12D3KooWQGR1vUhoy6mvQorFp3bZFn6NNezhQZ6NWnVV7tpFgoPd", "/dns/boot-cr.gatotech.network/tcp/35300/wss/p2p/12D3KooWQGR1vUhoy6mvQorFp3bZFn6NNezhQZ6NWnVV7tpFgoPd", "/dns/boot-westend.metaspan.io/tcp/33012/p2p/12D3KooWNTau7iG4G9cUJSwwt2QJP1W88pUf2SgqsHjRU2RL8pfa", diff --git a/polkadot/node/service/src/benchmarking.rs b/polkadot/node/service/src/benchmarking.rs index 400daf1aee3448f841f9030f24b78a0029fc0830..29acc5c3ca8c3af835e5ea46e14a31e3361162af 100644 --- a/polkadot/node/service/src/benchmarking.rs +++ b/polkadot/node/service/src/benchmarking.rs @@ -189,7 +189,7 @@ fn westend_sign_call( use sp_core::Pair; use westend_runtime as runtime; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -201,11 +201,12 @@ fn westend_sign_call( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), - ); + ) + .into(); let payload = runtime::SignedPayload::from_raw( call.clone(), - extra.clone(), + tx_ext.clone(), ( (), runtime::VERSION.spec_version, @@ -223,7 +224,7 @@ fn westend_sign_call( call, sp_runtime::AccountId32::from(acc.public()).into(), polkadot_core_primitives::Signature::Sr25519(signature.clone()), - extra, + tx_ext, ) .into() } @@ -241,7 +242,7 @@ fn rococo_sign_call( use rococo_runtime as runtime; use sp_core::Pair; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -253,11 +254,12 @@ fn rococo_sign_call( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), - ); + ) + .into(); let payload = runtime::SignedPayload::from_raw( call.clone(), - extra.clone(), + tx_ext.clone(), ( (), runtime::VERSION.spec_version, @@ -275,7 +277,7 @@ fn rococo_sign_call( call, sp_runtime::AccountId32::from(acc.public()).into(), polkadot_core_primitives::Signature::Sr25519(signature.clone()), - extra, + tx_ext, ) .into() } diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index fd35a4aaf6ab1780f9f6a1510c6d83a3939925c8..1c44b17b6fd24810bf896a64953a1b2f53b490da 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -24,6 +24,8 @@ use polkadot_primitives::{AccountId, AccountPublic, AssignmentId, ValidatorId}; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; +#[cfg(any(feature = "rococo-native", feature = "westend-native",))] +use polkadot_primitives::vstaging::SchedulerParams; #[cfg(feature = "rococo-native")] use rococo_runtime as rococo; #[cfg(feature = "rococo-native")] @@ -102,6 +104,10 @@ pub fn westend_config() -> Result { WestendChainSpec::from_json_bytes(&include_bytes!("../chain-specs/westend.json")[..]) } +pub fn paseo_config() -> Result { + GenericChainSpec::from_json_bytes(&include_bytes!("../chain-specs/paseo.json")[..]) +} + pub fn rococo_config() -> Result { RococoChainSpec::from_json_bytes(&include_bytes!("../chain-specs/rococo.json")[..]) } @@ -116,7 +122,9 @@ pub fn wococo_config() -> Result { fn default_parachains_host_configuration( ) -> polkadot_runtime_parachains::configuration::HostConfiguration { - use polkadot_primitives::{MAX_CODE_SIZE, MAX_POV_SIZE}; + use polkadot_primitives::{ + vstaging::node_features::FeatureIndex, AsyncBackingParams, MAX_CODE_SIZE, MAX_POV_SIZE, + }; polkadot_runtime_parachains::configuration::HostConfiguration { validation_upgrade_cooldown: 2u32, @@ -125,8 +133,6 @@ fn default_parachains_host_configuration( max_code_size: MAX_CODE_SIZE, max_pov_size: MAX_POV_SIZE, max_head_data_size: 32 * 1024, - group_rotation_frequency: 20, - paras_availability_period: 4, max_upward_queue_count: 8, max_upward_queue_size: 1024 * 1024, max_downward_message_size: 1024 * 1024, @@ -147,6 +153,19 @@ fn default_parachains_host_configuration( relay_vrf_modulo_samples: 2, zeroth_delay_tranche_width: 0, minimum_validation_upgrade_delay: 5, + async_backing_params: AsyncBackingParams { + max_candidate_depth: 3, + allowed_ancestry_len: 2, + }, + node_features: bitvec::vec::BitVec::from_element( + 1u8 << (FeatureIndex::ElasticScalingMVP as usize), + ), + scheduler_params: SchedulerParams { + lookahead: 2, + group_rotation_frequency: 20, + paras_availability_period: 4, + ..Default::default() + }, ..Default::default() } } @@ -882,7 +901,10 @@ pub fn rococo_testnet_genesis( "sudo": { "key": Some(root_key.clone()) }, "configuration": { "config": polkadot_runtime_parachains::configuration::HostConfiguration { - max_validators_per_core: Some(1), + scheduler_params: SchedulerParams { + max_validators_per_core: Some(1), + ..default_parachains_host_configuration().scheduler_params + }, ..default_parachains_host_configuration() }, }, diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index ccc3da22400dfc38f5e94aa4f6e89969499dbee8..085ea93fdc78526f793b726bf385124ec6418e5d 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -60,7 +60,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index 1aadef03ce791064819834eda2188fc0cddbf65c..83a0afc077e7edfb4a60fb8ed4109157f020695b 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -31,7 +31,10 @@ pub mod overseer; pub mod workers; #[cfg(feature = "full-node")] -pub use self::overseer::{OverseerGen, OverseerGenArgs, RealOverseerGen}; +pub use self::overseer::{ + CollatorOverseerGen, ExtendedOverseerGenArgs, OverseerGen, OverseerGenArgs, + ValidatorOverseerGen, +}; #[cfg(test)] mod tests; @@ -89,6 +92,7 @@ pub use chain_spec::{GenericChainSpec, RococoChainSpec, WestendChainSpec}; pub use consensus_common::{Proposal, SelectChain}; use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use mmr_gadget::MmrGadget; +use polkadot_node_subsystem_types::DefaultSubsystemClient; pub use polkadot_primitives::{Block, BlockId, BlockNumber, CollatorPair, Hash, Id as ParaId}; pub use sc_client_api::{Backend, CallExecutor}; pub use sc_consensus::{BlockImport, LongestChain}; @@ -775,8 +779,6 @@ pub fn new_full( let keystore = basics.keystore_container.local_keystore(); let auth_or_collator = role.is_authority() || is_parachain_node.is_collator(); - // We only need to enable the pvf checker when this is a validator. - let pvf_checker_enabled = role.is_authority(); let select_chain = if auth_or_collator { let metrics = @@ -867,10 +869,6 @@ pub fn new_full( let req_protocol_names = ReqProtocolNames::new(&genesis_hash, config.chain_spec.fork_id()); - let (pov_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); - net_config.add_request_response_protocol(cfg); - let (chunk_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); - net_config.add_request_response_protocol(cfg); let (collation_req_v1_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); net_config.add_request_response_protocol(cfg); @@ -880,12 +878,9 @@ pub fn new_full( let (available_data_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); net_config.add_request_response_protocol(cfg); - let (statement_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); - net_config.add_request_response_protocol(cfg); - let (candidate_req_v2_receiver, cfg) = - IncomingRequest::get_config_receiver(&req_protocol_names); + let (pov_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); net_config.add_request_response_protocol(cfg); - let (dispute_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); + let (chunk_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); net_config.add_request_response_protocol(cfg); let grandpa_hard_forks = if config.chain_spec.is_kusama() { @@ -900,6 +895,69 @@ pub fn new_full( grandpa_hard_forks, )); + let ext_overseer_args = if is_parachain_node.is_running_alongside_parachain_node() { + None + } else { + let parachains_db = open_database(&config.database)?; + let candidate_validation_config = if role.is_authority() { + let (prep_worker_path, exec_worker_path) = workers::determine_workers_paths( + workers_path, + workers_names, + node_version.clone(), + )?; + log::info!("🚀 Using prepare-worker binary at: {:?}", prep_worker_path); + log::info!("🚀 Using execute-worker binary at: {:?}", exec_worker_path); + + Some(CandidateValidationConfig { + artifacts_cache_path: config + .database + .path() + .ok_or(Error::DatabasePathRequired)? + .join("pvf-artifacts"), + node_version, + secure_validator_mode, + prep_worker_path, + exec_worker_path, + }) + } else { + None + }; + let (statement_req_receiver, cfg) = + IncomingRequest::get_config_receiver(&req_protocol_names); + net_config.add_request_response_protocol(cfg); + let (candidate_req_v2_receiver, cfg) = + IncomingRequest::get_config_receiver(&req_protocol_names); + net_config.add_request_response_protocol(cfg); + let (dispute_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names); + net_config.add_request_response_protocol(cfg); + let approval_voting_config = ApprovalVotingConfig { + col_approval_data: parachains_db::REAL_COLUMNS.col_approval_data, + slot_duration_millis: slot_duration.as_millis() as u64, + }; + let dispute_coordinator_config = DisputeCoordinatorConfig { + col_dispute_data: parachains_db::REAL_COLUMNS.col_dispute_coordinator_data, + }; + let chain_selection_config = ChainSelectionConfig { + col_data: parachains_db::REAL_COLUMNS.col_chain_selection_data, + stagnant_check_interval: Default::default(), + stagnant_check_mode: chain_selection_subsystem::StagnantCheckMode::PruneOnly, + }; + Some(ExtendedOverseerGenArgs { + keystore, + parachains_db, + candidate_validation_config, + availability_config: AVAILABILITY_CONFIG, + pov_req_receiver, + chunk_req_receiver, + statement_req_receiver, + candidate_req_v2_receiver, + approval_voting_config, + dispute_req_receiver, + dispute_coordinator_config, + chain_selection_config, + }) + }; + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = service::build_network(service::BuildNetworkParams { config: &config, @@ -936,44 +994,6 @@ pub fn new_full( ); } - let parachains_db = open_database(&config.database)?; - - let approval_voting_config = ApprovalVotingConfig { - col_approval_data: parachains_db::REAL_COLUMNS.col_approval_data, - slot_duration_millis: slot_duration.as_millis() as u64, - }; - - let candidate_validation_config = if role.is_authority() { - let (prep_worker_path, exec_worker_path) = - workers::determine_workers_paths(workers_path, workers_names, node_version.clone())?; - log::info!("🚀 Using prepare-worker binary at: {:?}", prep_worker_path); - log::info!("🚀 Using execute-worker binary at: {:?}", exec_worker_path); - - Some(CandidateValidationConfig { - artifacts_cache_path: config - .database - .path() - .ok_or(Error::DatabasePathRequired)? - .join("pvf-artifacts"), - node_version, - secure_validator_mode, - prep_worker_path, - exec_worker_path, - }) - } else { - None - }; - - let chain_selection_config = ChainSelectionConfig { - col_data: parachains_db::REAL_COLUMNS.col_chain_selection_data, - stagnant_check_interval: Default::default(), - stagnant_check_mode: chain_selection_subsystem::StagnantCheckMode::PruneOnly, - }; - - let dispute_coordinator_config = DisputeCoordinatorConfig { - col_dispute_data: parachains_db::REAL_COLUMNS.col_dispute_coordinator_data, - }; - let rpc_handlers = service::spawn_tasks(service::SpawnTasksParams { config, backend: backend.clone(), @@ -1062,42 +1082,32 @@ pub fn new_full( None }; + let runtime_client = Arc::new(DefaultSubsystemClient::new( + overseer_client.clone(), + OffchainTransactionPoolFactory::new(transaction_pool.clone()), + )); + let overseer_handle = if let Some(authority_discovery_service) = authority_discovery_service { let (overseer, overseer_handle) = overseer_gen - .generate::( + .generate::>( overseer_connector, OverseerGenArgs { - keystore, - runtime_client: overseer_client.clone(), - parachains_db, + runtime_client, network_service: network.clone(), sync_service: sync_service.clone(), authority_discovery_service, - pov_req_receiver, - chunk_req_receiver, collation_req_v1_receiver, collation_req_v2_receiver, available_data_req_receiver, - statement_req_receiver, - candidate_req_v2_receiver, - dispute_req_receiver, registry: prometheus_registry.as_ref(), spawner, is_parachain_node, - approval_voting_config, - availability_config: AVAILABILITY_CONFIG, - candidate_validation_config, - chain_selection_config, - dispute_coordinator_config, - pvf_checker_enabled, overseer_message_channel_capacity_override, req_protocol_names, peerset_protocol_names, - offchain_transaction_pool_factory: OffchainTransactionPoolFactory::new( - transaction_pool.clone(), - ), notification_services, }, + ext_overseer_args, ) .map_err(|e| { gum::error!("Failed to init overseer: {}", e); diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs index 599563d64549246247d29c6f4f1d297f3d08f042..a8167370464937ac91ae44056ea0b17b7184927a 100644 --- a/polkadot/node/service/src/overseer.rs +++ b/polkadot/node/service/src/overseer.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use super::{AuthorityDiscoveryApi, Block, Error, Hash, IsParachainNode, Registry}; -use polkadot_node_subsystem_types::DefaultSubsystemClient; -use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use super::{Block, Error, Hash, IsParachainNode, Registry}; +use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; +use polkadot_overseer::{DummySubsystem, InitializedOverseerBuilder, SubsystemError}; use sp_core::traits::SpawnNamed; use polkadot_availability_distribution::IncomingRequestReceivers; @@ -32,24 +32,17 @@ use polkadot_node_network_protocol::{ }, }; #[cfg(any(feature = "malus", test))] -pub use polkadot_overseer::{ - dummy::{dummy_overseer_builder, DummySubsystem}, - HeadSupportsParachains, -}; +pub use polkadot_overseer::{dummy::dummy_overseer_builder, HeadSupportsParachains}; use polkadot_overseer::{ - metrics::Metrics as OverseerMetrics, InitializedOverseerBuilder, MetricsTrait, Overseer, - OverseerConnector, OverseerHandle, SpawnGlue, + metrics::Metrics as OverseerMetrics, MetricsTrait, Overseer, OverseerConnector, OverseerHandle, + SpawnGlue, }; use parking_lot::Mutex; -use polkadot_primitives::runtime_api::ParachainHost; use sc_authority_discovery::Service as AuthorityDiscoveryService; use sc_client_api::AuxStore; use sc_keystore::LocalKeystore; use sc_network::{NetworkStateInfo, NotificationService}; -use sp_api::ProvideRuntimeApi; -use sp_blockchain::HeaderBackend; -use sp_consensus_babe::BabeApi; use std::{collections::HashMap, sync::Arc}; pub use polkadot_approval_distribution::ApprovalDistribution as ApprovalDistributionSubsystem; @@ -82,26 +75,16 @@ pub use polkadot_statement_distribution::StatementDistributionSubsystem; /// Arguments passed for overseer construction. pub struct OverseerGenArgs<'a, Spawner, RuntimeClient> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, Spawner: 'static + SpawnNamed + Clone + Unpin, { - /// The keystore to use for i.e. validator keys. - pub keystore: Arc, /// Runtime client generic, providing the `ProvieRuntimeApi` trait besides others. pub runtime_client: Arc, - /// The underlying key value store for the parachains. - pub parachains_db: Arc, /// Underlying network service implementation. pub network_service: Arc>, /// Underlying syncing service implementation. - pub sync_service: Arc>, + pub sync_service: Arc, /// Underlying authority discovery service. pub authority_discovery_service: AuthorityDiscoveryService, - /// POV request receiver. - pub pov_req_receiver: IncomingRequestReceiver, - /// Erasure chunks request receiver. - pub chunk_req_receiver: IncomingRequestReceiver, /// Collations request receiver for network protocol v1. pub collation_req_v1_receiver: IncomingRequestReceiver, /// Collations request receiver for network protocol v2. @@ -109,79 +92,85 @@ where /// Receiver for available data requests. pub available_data_req_receiver: IncomingRequestReceiver, - /// Receiver for incoming large statement requests. - pub statement_req_receiver: IncomingRequestReceiver, - /// Receiver for incoming candidate requests. - pub candidate_req_v2_receiver: IncomingRequestReceiver, - /// Receiver for incoming disputes. - pub dispute_req_receiver: IncomingRequestReceiver, /// Prometheus registry, commonly used for production systems, less so for test. pub registry: Option<&'a Registry>, /// Task spawner to be used throughout the overseer and the APIs it provides. pub spawner: Spawner, /// Determines the behavior of the collator. pub is_parachain_node: IsParachainNode, - /// Configuration for the approval voting subsystem. - pub approval_voting_config: ApprovalVotingConfig, - /// Configuration for the availability store subsystem. - pub availability_config: AvailabilityConfig, - /// Configuration for the candidate validation subsystem. - pub candidate_validation_config: Option, - /// Configuration for the chain selection subsystem. - pub chain_selection_config: ChainSelectionConfig, - /// Configuration for the dispute coordinator subsystem. - pub dispute_coordinator_config: DisputeCoordinatorConfig, - /// Enable PVF pre-checking - pub pvf_checker_enabled: bool, /// Overseer channel capacity override. pub overseer_message_channel_capacity_override: Option, /// Request-response protocol names source. pub req_protocol_names: ReqProtocolNames, /// `PeerSet` protocol names to protocols mapping. pub peerset_protocol_names: PeerSetProtocolNames, - /// The offchain transaction pool factory. - pub offchain_transaction_pool_factory: OffchainTransactionPoolFactory, /// Notification services for validation/collation protocols. pub notification_services: HashMap>, } -/// Obtain a prepared `OverseerBuilder`, that is initialized -/// with all default values. -pub fn prepared_overseer_builder( +pub struct ExtendedOverseerGenArgs { + /// The keystore to use for i.e. validator keys. + pub keystore: Arc, + /// The underlying key value store for the parachains. + pub parachains_db: Arc, + /// Configuration for the candidate validation subsystem. + pub candidate_validation_config: Option, + /// Configuration for the availability store subsystem. + pub availability_config: AvailabilityConfig, + /// POV request receiver. + pub pov_req_receiver: IncomingRequestReceiver, + /// Erasure chunks request receiver. + pub chunk_req_receiver: IncomingRequestReceiver, + /// Receiver for incoming large statement requests. + pub statement_req_receiver: IncomingRequestReceiver, + /// Receiver for incoming candidate requests. + pub candidate_req_v2_receiver: IncomingRequestReceiver, + /// Configuration for the approval voting subsystem. + pub approval_voting_config: ApprovalVotingConfig, + /// Receiver for incoming disputes. + pub dispute_req_receiver: IncomingRequestReceiver, + /// Configuration for the dispute coordinator subsystem. + pub dispute_coordinator_config: DisputeCoordinatorConfig, + /// Configuration for the chain selection subsystem. + pub chain_selection_config: ChainSelectionConfig, +} + +/// Obtain a prepared validator `Overseer`, that is initialized with all default values. +pub fn validator_overseer_builder( OverseerGenArgs { - keystore, runtime_client, - parachains_db, network_service, sync_service, authority_discovery_service, - pov_req_receiver, - chunk_req_receiver, - collation_req_v1_receiver, - collation_req_v2_receiver, + collation_req_v1_receiver: _, + collation_req_v2_receiver: _, available_data_req_receiver, - statement_req_receiver, - candidate_req_v2_receiver, - dispute_req_receiver, registry, spawner, is_parachain_node, - approval_voting_config, - availability_config, - candidate_validation_config, - chain_selection_config, - dispute_coordinator_config, - pvf_checker_enabled, overseer_message_channel_capacity_override, req_protocol_names, peerset_protocol_names, - offchain_transaction_pool_factory, notification_services, }: OverseerGenArgs, + ExtendedOverseerGenArgs { + keystore, + parachains_db, + candidate_validation_config, + availability_config, + pov_req_receiver, + chunk_req_receiver, + statement_req_receiver, + candidate_req_v2_receiver, + approval_voting_config, + dispute_req_receiver, + dispute_coordinator_config, + chain_selection_config, + }: ExtendedOverseerGenArgs, ) -> Result< InitializedOverseerBuilder< SpawnGlue, - Arc>, + Arc, CandidateValidationSubsystem, PvfCheckerSubsystem, CandidateBackingSubsystem, @@ -191,7 +180,7 @@ pub fn prepared_overseer_builder( BitfieldSigningSubsystem, BitfieldDistributionSubsystem, ProvisionerSubsystem, - RuntimeApiSubsystem>, + RuntimeApiSubsystem, AvailabilityStoreSubsystem, NetworkBridgeRxSubsystem< Arc>, @@ -215,8 +204,7 @@ pub fn prepared_overseer_builder( Error, > where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { use polkadot_node_subsystem_util::metrics::Metrics; @@ -228,11 +216,6 @@ where let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; - let runtime_api_client = Arc::new(DefaultSubsystemClient::new( - runtime_client.clone(), - offchain_transaction_pool_factory, - )); - let builder = Overseer::builder() .network_bridge_tx(NetworkBridgeTxSubsystem::new( network_service.clone(), @@ -280,23 +263,15 @@ where Metrics::register(registry)?, // candidate-validation metrics Metrics::register(registry)?, // validation host metrics )) - .pvf_checker(PvfCheckerSubsystem::new( - pvf_checker_enabled, - keystore.clone(), - Metrics::register(registry)?, - )) + .pvf_checker(PvfCheckerSubsystem::new(keystore.clone(), Metrics::register(registry)?)) .chain_api(ChainApiSubsystem::new(runtime_client.clone(), Metrics::register(registry)?)) .collation_generation(CollationGenerationSubsystem::new(Metrics::register(registry)?)) .collator_protocol({ let side = match is_parachain_node { - IsParachainNode::Collator(collator_pair) => ProtocolSide::Collator { - peer_id: network_service.local_peer_id(), - collator_pair, - request_receiver_v1: collation_req_v1_receiver, - request_receiver_v2: collation_req_v2_receiver, - metrics: Metrics::register(registry)?, - }, - IsParachainNode::FullNode => ProtocolSide::None, + IsParachainNode::Collator(_) | IsParachainNode::FullNode => + return Err(Error::Overseer(SubsystemError::Context( + "build validator overseer for parachain node".to_owned(), + ))), IsParachainNode::No => ProtocolSide::Validator { keystore: keystore.clone(), eviction_policy: Default::default(), @@ -307,7 +282,7 @@ where }) .provisioner(ProvisionerSubsystem::new(Metrics::register(registry)?)) .runtime_api(RuntimeApiSubsystem::new( - runtime_api_client.clone(), + runtime_client.clone(), Metrics::register(registry)?, spawner.clone(), )) @@ -348,62 +323,218 @@ where .activation_external_listeners(Default::default()) .span_per_active_leaf(Default::default()) .active_leaves(Default::default()) - .supports_parachains(runtime_api_client) + .supports_parachains(runtime_client) .metrics(metrics) .spawner(spawner); - if let Some(capacity) = overseer_message_channel_capacity_override { - Ok(builder.message_channel_capacity(capacity)) + let builder = if let Some(capacity) = overseer_message_channel_capacity_override { + builder.message_channel_capacity(capacity) } else { - Ok(builder) - } + builder + }; + Ok(builder) +} + +/// Obtain a prepared collator `Overseer`, that is initialized with all default values. +pub fn collator_overseer_builder( + OverseerGenArgs { + runtime_client, + network_service, + sync_service, + authority_discovery_service, + collation_req_v1_receiver, + collation_req_v2_receiver, + available_data_req_receiver, + registry, + spawner, + is_parachain_node, + overseer_message_channel_capacity_override, + req_protocol_names, + peerset_protocol_names, + notification_services, + }: OverseerGenArgs, +) -> Result< + InitializedOverseerBuilder< + SpawnGlue, + Arc, + DummySubsystem, + DummySubsystem, + DummySubsystem, + DummySubsystem, + DummySubsystem, + AvailabilityRecoverySubsystem, + DummySubsystem, + DummySubsystem, + DummySubsystem, + RuntimeApiSubsystem, + DummySubsystem, + NetworkBridgeRxSubsystem< + Arc>, + AuthorityDiscoveryService, + >, + NetworkBridgeTxSubsystem< + Arc>, + AuthorityDiscoveryService, + >, + ChainApiSubsystem, + CollationGenerationSubsystem, + CollatorProtocolSubsystem, + DummySubsystem, + DummySubsystem, + DummySubsystem, + DummySubsystem, + DummySubsystem, + DummySubsystem, + ProspectiveParachainsSubsystem, + >, + Error, +> +where + Spawner: 'static + SpawnNamed + Clone + Unpin, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, +{ + use polkadot_node_subsystem_util::metrics::Metrics; + + let notification_sinks = Arc::new(Mutex::new(HashMap::new())); + + let spawner = SpawnGlue(spawner); + + let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; + + let builder = Overseer::builder() + .network_bridge_tx(NetworkBridgeTxSubsystem::new( + network_service.clone(), + authority_discovery_service.clone(), + network_bridge_metrics.clone(), + req_protocol_names, + peerset_protocol_names.clone(), + notification_sinks.clone(), + )) + .network_bridge_rx(NetworkBridgeRxSubsystem::new( + network_service.clone(), + authority_discovery_service.clone(), + Box::new(sync_service.clone()), + network_bridge_metrics, + peerset_protocol_names, + notification_services, + notification_sinks, + )) + .availability_distribution(DummySubsystem) + .availability_recovery(AvailabilityRecoverySubsystem::for_collator( + available_data_req_receiver, + Metrics::register(registry)?, + )) + .availability_store(DummySubsystem) + .bitfield_distribution(DummySubsystem) + .bitfield_signing(DummySubsystem) + .candidate_backing(DummySubsystem) + .candidate_validation(DummySubsystem) + .pvf_checker(DummySubsystem) + .chain_api(ChainApiSubsystem::new(runtime_client.clone(), Metrics::register(registry)?)) + .collation_generation(CollationGenerationSubsystem::new(Metrics::register(registry)?)) + .collator_protocol({ + let side = match is_parachain_node { + IsParachainNode::No => + return Err(Error::Overseer(SubsystemError::Context( + "build parachain node overseer for validator".to_owned(), + ))), + IsParachainNode::Collator(collator_pair) => ProtocolSide::Collator { + peer_id: network_service.local_peer_id(), + collator_pair, + request_receiver_v1: collation_req_v1_receiver, + request_receiver_v2: collation_req_v2_receiver, + metrics: Metrics::register(registry)?, + }, + IsParachainNode::FullNode => ProtocolSide::None, + }; + CollatorProtocolSubsystem::new(side) + }) + .provisioner(DummySubsystem) + .runtime_api(RuntimeApiSubsystem::new( + runtime_client.clone(), + Metrics::register(registry)?, + spawner.clone(), + )) + .statement_distribution(DummySubsystem) + .approval_distribution(DummySubsystem) + .approval_voting(DummySubsystem) + .gossip_support(DummySubsystem) + .dispute_coordinator(DummySubsystem) + .dispute_distribution(DummySubsystem) + .chain_selection(DummySubsystem) + .prospective_parachains(ProspectiveParachainsSubsystem::new(Metrics::register(registry)?)) + .activation_external_listeners(Default::default()) + .span_per_active_leaf(Default::default()) + .active_leaves(Default::default()) + .supports_parachains(runtime_client) + .metrics(Metrics::register(registry)?) + .spawner(spawner); + + let builder = if let Some(capacity) = overseer_message_channel_capacity_override { + builder.message_channel_capacity(capacity) + } else { + builder + }; + Ok(builder) } /// Trait for the `fn` generating the overseer. -/// -/// Default behavior is to create an unmodified overseer, as `RealOverseerGen` -/// would do. pub trait OverseerGen { /// Overwrite the full generation of the overseer, including the subsystems. fn generate( &self, connector: OverseerConnector, args: OverseerGenArgs, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, - Spawner: 'static + SpawnNamed + Clone + Unpin, - { - let gen = RealOverseerGen; - RealOverseerGen::generate::(&gen, connector, args) - } + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, + Spawner: 'static + SpawnNamed + Clone + Unpin; + // It would be nice to make `create_subsystems` part of this trait, // but the amount of generic arguments that would be required as // as consequence make this rather annoying to implement and use. } /// The regular set of subsystems. -pub struct RealOverseerGen; +pub struct ValidatorOverseerGen; + +impl OverseerGen for ValidatorOverseerGen { + fn generate( + &self, + connector: OverseerConnector, + args: OverseerGenArgs, + ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> + where + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, + Spawner: 'static + SpawnNamed + Clone + Unpin, + { + let ext_args = ext_args.ok_or(Error::Overseer(SubsystemError::Context( + "create validator overseer as mandatory extended arguments were not provided" + .to_owned(), + )))?; + validator_overseer_builder(args, ext_args)? + .build_with_connector(connector) + .map_err(|e| e.into()) + } +} + +/// Reduced set of subsystems, to use in collator and collator's full node. +pub struct CollatorOverseerGen; -impl OverseerGen for RealOverseerGen { +impl OverseerGen for CollatorOverseerGen { fn generate( &self, connector: OverseerConnector, args: OverseerGenArgs, - ) -> Result< - (Overseer, Arc>>, OverseerHandle), - Error, - > + _ext_args: Option, + ) -> Result<(Overseer, Arc>, OverseerHandle), Error> where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { - prepared_overseer_builder(args)? + collator_overseer_builder(args)? .build_with_connector(connector) .map_err(|e| e.into()) } diff --git a/polkadot/node/service/src/parachains_db/upgrade.rs b/polkadot/node/service/src/parachains_db/upgrade.rs index d22eebb5c8d4edebdd2174f3cb1ba144fea0b130..2eceb391b15c278825b243fdb8dc21e8ef540390 100644 --- a/polkadot/node/service/src/parachains_db/upgrade.rs +++ b/polkadot/node/service/src/parachains_db/upgrade.rs @@ -93,7 +93,7 @@ pub(crate) fn try_upgrade_db( } /// Try upgrading parachain's database to the next version. -/// If successfull, it returns the current version. +/// If successful, it returns the current version. pub(crate) fn try_upgrade_db_to_next_version( db_path: &Path, db_kind: DatabaseKind, diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 750f7a7e2f83afc00a83a295911ce68748ca975d..7c60ff48269c41c291b1649fe58c75056cc50f9a 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -8,9 +8,13 @@ license.workspace = true readme = "README.md" publish = false +[lib] +name = "polkadot_subsystem_bench" +path = "src/lib/lib.rs" + [[bin]] name = "subsystem-bench" -path = "src/subsystem-bench.rs" +path = "src/cli/subsystem-bench.rs" # Prevent rustdoc error. Already documented from top-level Cargo.toml. doc = false @@ -23,22 +27,34 @@ polkadot-node-primitives = { path = "../primitives" } polkadot-primitives = { path = "../../primitives" } polkadot-node-network-protocol = { path = "../network/protocol" } polkadot-availability-recovery = { path = "../network/availability-recovery", features = ["subsystem-benchmarks"] } +polkadot-availability-distribution = { path = "../network/availability-distribution" } +polkadot-node-core-av-store = { path = "../core/av-store" } +polkadot-node-core-chain-api = { path = "../core/chain-api" } +polkadot-availability-bitfield-distribution = { path = "../network/bitfield-distribution" } color-eyre = { version = "0.6.1", default-features = false } polkadot-overseer = { path = "../overseer" } colored = "2.0.4" assert_matches = "1.5" -async-trait = "0.1.74" +async-trait = "0.1.57" sp-keystore = { path = "../../../substrate/primitives/keystore" } sc-keystore = { path = "../../../substrate/client/keystore" } sp-core = { path = "../../../substrate/primitives/core" } -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" +bincode = "1.3.3" +sha1 = "0.10.6" +hex = "0.4.3" gum = { package = "tracing-gum", path = "../gum" } polkadot-erasure-coding = { package = "polkadot-erasure-coding", path = "../../erasure-coding" } -log = "0.4.17" +log = { workspace = true, default-features = true } env_logger = "0.9.0" rand = "0.8.5" +# `rand` only supports uniform distribution, we need normal distribution for latency. +rand_distr = "0.4.3" +bitvec = "1.0.1" +kvdb-memorydb = "0.13.0" + parity-scale-codec = { version = "3.6.1", features = ["derive", "std"] } tokio = "1.24.2" clap-num = "1.0.2" @@ -47,15 +63,27 @@ sp-keyring = { path = "../../../substrate/primitives/keyring" } sp-application-crypto = { path = "../../../substrate/primitives/application-crypto" } sc-network = { path = "../../../substrate/client/network" } sc-service = { path = "../../../substrate/client/service" } +sp-consensus = { path = "../../../substrate/primitives/consensus/common" } polkadot-node-metrics = { path = "../metrics" } itertools = "0.11.0" polkadot-primitives-test-helpers = { path = "../../primitives/test-helpers" } prometheus_endpoint = { package = "substrate-prometheus-endpoint", path = "../../../substrate/utils/prometheus" } prometheus = { version = "0.13.0", default-features = false } -serde = "1.0.195" -serde_yaml = "0.9" +serde = { workspace = true, default-features = true } +serde_yaml = { workspace = true } + +polkadot-node-core-approval-voting = { path = "../core/approval-voting" } +polkadot-approval-distribution = { path = "../network/approval-distribution" } +sp-consensus-babe = { path = "../../../substrate/primitives/consensus/babe" } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } +sp-timestamp = { path = "../../../substrate/primitives/timestamp" } + +schnorrkel = { version = "0.11.4", default-features = false } +# rand_core should match schnorrkel +rand_core = "0.6.2" +rand_chacha = { version = "0.3.1" } paste = "1.0.14" -orchestra = { version = "0.3.4", default-features = false, features = ["futures_channel"] } +orchestra = { version = "0.3.5", default-features = false, features = ["futures_channel"] } pyroscope = "0.5.7" pyroscope_pprofrs = "0.2.7" diff --git a/polkadot/node/subsystem-bench/README.md b/polkadot/node/subsystem-bench/README.md index 1ff5b129e1e4cd78a6b3834a64f27083a6905458..3aac2810ad58f8fe732155aa5d8be611c44426c2 100644 --- a/polkadot/node/subsystem-bench/README.md +++ b/polkadot/node/subsystem-bench/README.md @@ -1,6 +1,6 @@ # Subsystem benchmark client -Run parachain consensus stress and performance tests on your development machine. +Run parachain consensus stress and performance tests on your development machine or in CI. ## Motivation @@ -32,7 +32,8 @@ a local Grafana/Prometheus stack is needed. ### Run Prometheus, Pyroscope and Graphana in Docker -If docker is not usable, then follow the next sections to manually install Prometheus, Pyroscope and Graphana on your machine. +If docker is not usable, then follow the next sections to manually install Prometheus, Pyroscope and Graphana +on your machine. ```bash cd polkadot/node/subsystem-bench/docker @@ -95,69 +96,34 @@ If you are running the servers in Docker, use the following URLs: Follow [this guide](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#export-and-import-dashboards) to import the dashboards from the repository `grafana` folder. -## How to run a test - -To run a test, you need to first choose a test objective. Currently, we support the following: - -``` -target/testnet/subsystem-bench --help -The almighty Subsystem Benchmark Tool™️ - -Usage: subsystem-bench [OPTIONS] - -Commands: - data-availability-read Benchmark availability recovery strategies +### Standard test options ``` +$ subsystem-bench --help +Usage: subsystem-bench [OPTIONS] -Note: `test-sequence` is a special test objective that wraps up an arbitrary number of test objectives. It is tipically -used to run a suite of tests defined in a `yaml` file like in this [example](examples/availability_read.yaml). +Arguments: + Path to the test sequence configuration file -### Standard test options - -``` Options: - --network The type of network to be emulated [default: ideal] [possible - values: ideal, healthy, degraded] - --n-cores Number of cores to fetch availability for [default: 100] - --n-validators Number of validators to fetch chunks from [default: 500] - --min-pov-size The minimum pov size in KiB [default: 5120] - --max-pov-size The maximum pov size bytes [default: 5120] --n, --num-blocks The number of blocks the test is going to run [default: 1] --p, --peer-bandwidth The bandwidth of simulated remote peers in KiB --b, --bandwidth The bandwidth of our simulated node in KiB - --peer-error Simulated conection error ratio [0-100] - --peer-min-latency Minimum remote peer latency in milliseconds [0-5000] - --peer-max-latency Maximum remote peer latency in milliseconds [0-5000] - --profile Enable CPU Profiling with Pyroscope - --pyroscope-url Pyroscope Server URL [default: http://localhost:4040] - --pyroscope-sample-rate Pyroscope Sample Rate [default: 113] - --cache-misses Enable Cache Misses Profiling with Valgrind. Linux only, Valgrind - must be in the PATH --h, --help Print help + --profile Enable CPU Profiling with Pyroscope + --pyroscope-url Pyroscope Server URL [default: http://localhost:4040] + --pyroscope-sample-rate Pyroscope Sample Rate [default: 113] + --cache-misses Enable Cache Misses Profiling with Valgrind. Linux only, Valgrind must be in the PATH + -h, --help Print help ``` -These apply to all test objectives, except `test-sequence` which relies on the values being specified in a file. - -### Test objectives - -Each test objective can have it's specific configuration options, in contrast with the standard test options. +## How to run a test -For `data-availability-read` the recovery strategy to be used is configurable. +To run a test, you need to use a path to a test objective: ``` -target/testnet/subsystem-bench data-availability-read --help -Benchmark availability recovery strategies - -Usage: subsystem-bench data-availability-read [OPTIONS] - -Options: - -f, --fetch-from-backers Turbo boost AD Read by fetching the full availability datafrom backers first. Saves CPU - as we don't need to re-construct from chunks. Tipically this is only faster if nodes - have enough bandwidth - -h, --help Print help +target/testnet/subsystem-bench polkadot/node/subsystem-bench/examples/availability_read.yaml ``` +Note: test objectives may be wrapped up into a test sequence. +It is tipically used to run a suite of tests like in this [example](examples/availability_read.yaml). + ### Understanding the test configuration A single test configuration `TestConfiguration` struct applies to a single run of a certain test objective. @@ -177,36 +143,65 @@ the test is started. ### Example run -Let's run an availabilty read test which will recover availability for 10 cores with max PoV size on a 500 +Let's run an availabilty read test which will recover availability for 200 cores with max PoV size on a 1000 node validator network. + + ``` - target/testnet/subsystem-bench --n-cores 10 data-availability-read -[2023-11-28T09:01:59Z INFO subsystem_bench::core::display] n_validators = 500, n_cores = 10, pov_size = 5120 - 5120, - error = 0, latency = None -[2023-11-28T09:01:59Z INFO subsystem-bench::availability] Generating template candidate index=0 pov_size=5242880 -[2023-11-28T09:01:59Z INFO subsystem-bench::availability] Created test environment. -[2023-11-28T09:01:59Z INFO subsystem-bench::availability] Pre-generating 10 candidates. -[2023-11-28T09:02:01Z INFO subsystem-bench::core] Initializing network emulation for 500 peers. -[2023-11-28T09:02:01Z INFO substrate_prometheus_endpoint] 〽️ Prometheus exporter started at 127.0.0.1:9999 -[2023-11-28T09:02:01Z INFO subsystem-bench::availability] Current block 1/1 -[2023-11-28T09:02:01Z INFO subsystem_bench::availability] 10 recoveries pending -[2023-11-28T09:02:04Z INFO subsystem_bench::availability] Block time 3231ms -[2023-11-28T09:02:04Z INFO subsystem-bench::availability] Sleeping till end of block (2768ms) -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] All blocks processed in 6001ms -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] Throughput: 51200 KiB/block -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] Block time: 6001 ms -[2023-11-28T09:02:07Z INFO subsystem_bench::availability] - - Total received from network: 66 MiB - Total sent to network: 58 KiB - Total subsystem CPU usage 4.16s - CPU usage per block 4.16s - Total test environment CPU usage 0.00s - CPU usage per block 0.00s +target/testnet/subsystem-bench polkadot/node/subsystem-bench/examples/availability_write.yaml +[2024-02-19T14:10:32.981Z INFO subsystem_bench] Sequence contains 1 step(s) +[2024-02-19T14:10:32.981Z INFO subsystem-bench::cli] Step 1/1 +[2024-02-19T14:10:32.981Z INFO subsystem-bench::cli] [objective = DataAvailabilityWrite] n_validators = 1000, n_cores = 200, pov_size = 5120 - 5120, connectivity = 75, latency = Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 }) +[2024-02-19T14:10:32.982Z INFO subsystem-bench::availability] Generating template candidate index=0 pov_size=5242880 +[2024-02-19T14:10:33.106Z INFO subsystem-bench::availability] Created test environment. +[2024-02-19T14:10:33.106Z INFO subsystem-bench::availability] Pre-generating 600 candidates. +[2024-02-19T14:10:34.096Z INFO subsystem-bench::network] Initializing emulation for a 1000 peer network. +[2024-02-19T14:10:34.096Z INFO subsystem-bench::network] connectivity 75%, latency Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 }) +[2024-02-19T14:10:34.098Z INFO subsystem-bench::network] Network created, connected validator count 749 +[2024-02-19T14:10:34.099Z INFO subsystem-bench::availability] Seeding availability store with candidates ... +[2024-02-19T14:10:34.100Z INFO substrate_prometheus_endpoint] 〽️ Prometheus exporter started at 127.0.0.1:9999 +[2024-02-19T14:10:34.387Z INFO subsystem-bench::availability] Done +[2024-02-19T14:10:34.387Z INFO subsystem-bench::availability] Current block #1 +[2024-02-19T14:10:34.389Z INFO subsystem-bench::availability] Waiting for all emulated peers to receive their chunk from us ... +[2024-02-19T14:10:34.625Z INFO subsystem-bench::availability] All chunks received in 237ms +[2024-02-19T14:10:34.626Z INFO polkadot_subsystem_bench::availability] Waiting for 749 bitfields to be received and processed +[2024-02-19T14:10:35.710Z INFO subsystem-bench::availability] All bitfields processed +[2024-02-19T14:10:35.710Z INFO subsystem-bench::availability] All work for block completed in 1322ms +[2024-02-19T14:10:35.710Z INFO subsystem-bench::availability] Current block #2 +[2024-02-19T14:10:35.712Z INFO subsystem-bench::availability] Waiting for all emulated peers to receive their chunk from us ... +[2024-02-19T14:10:35.947Z INFO subsystem-bench::availability] All chunks received in 236ms +[2024-02-19T14:10:35.947Z INFO polkadot_subsystem_bench::availability] Waiting for 749 bitfields to be received and processed +[2024-02-19T14:10:37.038Z INFO subsystem-bench::availability] All bitfields processed +[2024-02-19T14:10:37.038Z INFO subsystem-bench::availability] All work for block completed in 1328ms +[2024-02-19T14:10:37.039Z INFO subsystem-bench::availability] Current block #3 +[2024-02-19T14:10:37.040Z INFO subsystem-bench::availability] Waiting for all emulated peers to receive their chunk from us ... +[2024-02-19T14:10:37.276Z INFO subsystem-bench::availability] All chunks received in 237ms +[2024-02-19T14:10:37.276Z INFO polkadot_subsystem_bench::availability] Waiting for 749 bitfields to be received and processed +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] All bitfields processed +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] All work for block completed in 1323ms +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] All blocks processed in 3974ms +[2024-02-19T14:10:38.362Z INFO subsystem-bench::availability] Avg block time: 1324 ms +[2024-02-19T14:10:38.362Z INFO parachain::availability-store] received `Conclude` signal, exiting +[2024-02-19T14:10:38.362Z INFO parachain::bitfield-distribution] Conclude +[2024-02-19T14:10:38.362Z INFO subsystem-bench::network] Downlink channel closed, network interface task exiting + +polkadot/node/subsystem-bench/examples/availability_write.yaml #1 DataAvailabilityWrite + +Network usage, KiB total per block +Received from peers 12922.000 4307.333 +Sent to peers 47705.000 15901.667 + +CPU usage, seconds total per block +availability-distribution 0.045 0.015 +bitfield-distribution 0.104 0.035 +availability-store 0.304 0.101 +Test environment 3.213 1.071 ``` -`Block time` in the context of `data-availability-read` has a different meaning. It measures the amount of time it + + +`Block time` in the current context has a different meaning. It measures the amount of time it took the subsystem to finish processing all of the messages sent in the context of the current test block. ### Test logs @@ -235,8 +230,9 @@ Since the execution will be very slow, it's recommended not to run it together w benchmark results into account. A report is saved in a file `cachegrind_report.txt`. Example run results: + ``` -$ target/testnet/subsystem-bench --n-cores 10 --cache-misses data-availability-read +$ target/testnet/subsystem-bench --cache-misses cache-misses-data-availability-read.yaml $ cat cachegrind_report.txt I refs: 64,622,081,485 I1 misses: 3,018,168 @@ -277,7 +273,7 @@ happy and negative scenarios (low bandwidth, network errors and low connectivity To faster write a new test objective you need to use some higher level wrappers and logic: `TestEnvironment`, `TestConfiguration`, `TestAuthorities`, `NetworkEmulator`. To create the `TestEnvironment` you will -need to also build an `Overseer`, but that should be easy using the mockups for subsystems in`core::mock`. +need to also build an `Overseer`, but that should be easy using the mockups for subsystems in `mock`. ### Mocking diff --git a/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml b/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml new file mode 100644 index 0000000000000000000000000000000000000000..146da57d44c4aaf973e13c886a357028cdbe3559 --- /dev/null +++ b/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml @@ -0,0 +1,18 @@ +TestConfiguration: +# Test 1 +- objective: !ApprovalVoting + coalesce_mean: 3.0 + coalesce_std_dev: 1.0 + enable_assignments_v2: true + last_considered_tranche: 89 + stop_when_approved: true + coalesce_tranche_diff: 12 + num_no_shows_per_candidate: 10 + workdir_prefix: "/tmp/" + n_validators: 500 + n_cores: 100 + min_pov_size: 1120 + max_pov_size: 5120 + peer_bandwidth: 524288000000 + bandwidth: 524288000000 + num_blocks: 10 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6b17e62c20aa3f69153fb596d1a303a2e0320ddd --- /dev/null +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml @@ -0,0 +1,18 @@ +TestConfiguration: +# Test 1 +- objective: !ApprovalVoting + coalesce_mean: 3.0 + coalesce_std_dev: 1.0 + enable_assignments_v2: true + last_considered_tranche: 89 + stop_when_approved: false + coalesce_tranche_diff: 12 + num_no_shows_per_candidate: 0 + workdir_prefix: "/tmp" + n_validators: 500 + n_cores: 100 + min_pov_size: 1120 + max_pov_size: 5120 + peer_bandwidth: 524288000000 + bandwidth: 524288000000 + num_blocks: 10 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e946c28e8ef5d4e38736ffc21e56d8b1c6cd0ddc --- /dev/null +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml @@ -0,0 +1,18 @@ +TestConfiguration: +# Test 1 +- objective: !ApprovalVoting + coalesce_mean: 3.0 + coalesce_std_dev: 1.0 + enable_assignments_v2: true + last_considered_tranche: 89 + stop_when_approved: true + coalesce_tranche_diff: 12 + num_no_shows_per_candidate: 0 + workdir_prefix: "/tmp/" + n_validators: 500 + n_cores: 100 + min_pov_size: 1120 + max_pov_size: 5120 + peer_bandwidth: 524288000000 + bandwidth: 524288000000 + num_blocks: 10 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput_no_optimisations_enabled.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput_no_optimisations_enabled.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8f4b050e72f27dd4b5bb0c52bd49162cd0bb83ec --- /dev/null +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput_no_optimisations_enabled.yaml @@ -0,0 +1,18 @@ +TestConfiguration: +# Test 1 +- objective: !ApprovalVoting + coalesce_mean: 1.0 + coalesce_std_dev: 0.0 + enable_assignments_v2: false + last_considered_tranche: 89 + stop_when_approved: false + coalesce_tranche_diff: 12 + num_no_shows_per_candidate: 0 + workdir_prefix: "/tmp/" + n_validators: 500 + n_cores: 100 + min_pov_size: 1120 + max_pov_size: 5120 + peer_bandwidth: 524288000000 + bandwidth: 524288000000 + num_blocks: 10 diff --git a/polkadot/node/subsystem-bench/examples/availability_read.yaml b/polkadot/node/subsystem-bench/examples/availability_read.yaml index 311ea972141fc339367d30234cdf0c60911dd824..82355b0e2973aaff490a5c2d3ed54d37c61430de 100644 --- a/polkadot/node/subsystem-bench/examples/availability_read.yaml +++ b/polkadot/node/subsystem-bench/examples/availability_read.yaml @@ -1,7 +1,7 @@ TestConfiguration: # Test 1 - objective: !DataAvailabilityRead - fetch_from_backers: false + fetch_from_backers: true n_validators: 300 n_cores: 20 min_pov_size: 5120 @@ -9,18 +9,14 @@ TestConfiguration: peer_bandwidth: 52428800 bandwidth: 52428800 latency: - min_latency: - secs: 0 - nanos: 1000000 - max_latency: - secs: 0 - nanos: 100000000 - error: 3 + mean_latency_ms: 100 + std_dev: 1 num_blocks: 3 + connectivity: 90 # Test 2 - objective: !DataAvailabilityRead - fetch_from_backers: false + fetch_from_backers: true n_validators: 500 n_cores: 20 min_pov_size: 5120 @@ -28,18 +24,14 @@ TestConfiguration: peer_bandwidth: 52428800 bandwidth: 52428800 latency: - min_latency: - secs: 0 - nanos: 1000000 - max_latency: - secs: 0 - nanos: 100000000 - error: 3 + mean_latency_ms: 100 + std_dev: 1 num_blocks: 3 + connectivity: 90 # Test 3 - objective: !DataAvailabilityRead - fetch_from_backers: false + fetch_from_backers: true n_validators: 1000 n_cores: 20 min_pov_size: 5120 @@ -47,11 +39,7 @@ TestConfiguration: peer_bandwidth: 52428800 bandwidth: 52428800 latency: - min_latency: - secs: 0 - nanos: 1000000 - max_latency: - secs: 0 - nanos: 100000000 - error: 3 + mean_latency_ms: 100 + std_dev: 1 num_blocks: 3 + connectivity: 90 diff --git a/polkadot/node/subsystem-bench/examples/availability_write.yaml b/polkadot/node/subsystem-bench/examples/availability_write.yaml new file mode 100644 index 0000000000000000000000000000000000000000..64e07d76969239bf230146d50cae11cfe5d781d6 --- /dev/null +++ b/polkadot/node/subsystem-bench/examples/availability_write.yaml @@ -0,0 +1,15 @@ +TestConfiguration: +# Test 1kV, 200 cores, max Pov +- objective: DataAvailabilityWrite + n_validators: 1000 + n_cores: 200 + max_validators_per_core: 5 + min_pov_size: 5120 + max_pov_size: 5120 + peer_bandwidth: 52428800 + bandwidth: 52428800 + latency: + mean_latency_ms: 30 + std_dev: 2.0 + connectivity: 75 + num_blocks: 3 diff --git a/polkadot/node/subsystem-bench/src/availability/cli.rs b/polkadot/node/subsystem-bench/src/availability/cli.rs deleted file mode 100644 index 65df8c1552aa8266497eb2738cc562f656050b68..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/availability/cli.rs +++ /dev/null @@ -1,37 +0,0 @@ -// 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 serde::{Deserialize, Serialize}; - -#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq)] -#[value(rename_all = "kebab-case")] -#[non_exhaustive] -pub enum NetworkEmulation { - Ideal, - Healthy, - Degraded, -} - -#[derive(Debug, Clone, Serialize, Deserialize, clap::Parser)] -#[clap(rename_all = "kebab-case")] -#[allow(missing_docs)] -pub struct DataAvailabilityReadOptions { - #[clap(short, long, default_value_t = false)] - /// Turbo boost AD Read by fetching the full availability datafrom backers first. Saves CPU as - /// we don't need to re-construct from chunks. Tipically this is only faster if nodes have - /// enough bandwidth. - pub fetch_from_backers: bool, -} diff --git a/polkadot/node/subsystem-bench/src/availability/mod.rs b/polkadot/node/subsystem-bench/src/availability/mod.rs deleted file mode 100644 index faedccdf3e42e19d1428e5348f859baa6f0664dc..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/availability/mod.rs +++ /dev/null @@ -1,341 +0,0 @@ -// 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 itertools::Itertools; -use std::{collections::HashMap, iter::Cycle, ops::Sub, sync::Arc, time::Instant}; - -use crate::TestEnvironment; -use polkadot_node_subsystem::{Overseer, OverseerConnector, SpawnGlue}; -use polkadot_node_subsystem_test_helpers::derive_erasure_chunks_with_proofs_and_root; -use polkadot_overseer::Handle as OverseerHandle; -use sc_network::request_responses::ProtocolConfig; - -use colored::Colorize; - -use futures::{channel::oneshot, stream::FuturesUnordered, StreamExt}; -use polkadot_node_metrics::metrics::Metrics; - -use polkadot_availability_recovery::AvailabilityRecoverySubsystem; - -use crate::GENESIS_HASH; -use parity_scale_codec::Encode; -use polkadot_node_network_protocol::request_response::{IncomingRequest, ReqProtocolNames}; -use polkadot_node_primitives::{BlockData, PoV}; -use polkadot_node_subsystem::messages::{AllMessages, AvailabilityRecoveryMessage}; - -use crate::core::{ - environment::TestEnvironmentDependencies, - mock::{ - av_store, - network_bridge::{self, MockNetworkBridgeTx, NetworkAvailabilityState}, - runtime_api, MockAvailabilityStore, MockRuntimeApi, - }, -}; - -use super::core::{configuration::TestConfiguration, mock::dummy_builder, network::*}; - -const LOG_TARGET: &str = "subsystem-bench::availability"; - -use polkadot_node_primitives::{AvailableData, ErasureChunk}; - -use super::{cli::TestObjective, core::mock::AlwaysSupportsParachains}; -use polkadot_node_subsystem_test_helpers::mock::new_block_import_info; -use polkadot_primitives::{ - CandidateHash, CandidateReceipt, GroupIndex, Hash, HeadData, PersistedValidationData, -}; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; -use sc_service::SpawnTaskHandle; - -mod cli; -pub use cli::{DataAvailabilityReadOptions, NetworkEmulation}; - -fn build_overseer( - spawn_task_handle: SpawnTaskHandle, - runtime_api: MockRuntimeApi, - av_store: MockAvailabilityStore, - network_bridge: MockNetworkBridgeTx, - availability_recovery: AvailabilityRecoverySubsystem, -) -> (Overseer, AlwaysSupportsParachains>, OverseerHandle) { - let overseer_connector = OverseerConnector::with_event_capacity(64000); - let dummy = dummy_builder!(spawn_task_handle); - let builder = dummy - .replace_runtime_api(|_| runtime_api) - .replace_availability_store(|_| av_store) - .replace_network_bridge_tx(|_| network_bridge) - .replace_availability_recovery(|_| availability_recovery); - - let (overseer, raw_handle) = - builder.build_with_connector(overseer_connector).expect("Should not fail"); - - (overseer, OverseerHandle::new(raw_handle)) -} - -/// Takes a test configuration and uses it to creates the `TestEnvironment`. -pub fn prepare_test( - config: TestConfiguration, - state: &mut TestState, -) -> (TestEnvironment, ProtocolConfig) { - prepare_test_inner(config, state, TestEnvironmentDependencies::default()) -} - -fn prepare_test_inner( - config: TestConfiguration, - state: &mut TestState, - dependencies: TestEnvironmentDependencies, -) -> (TestEnvironment, ProtocolConfig) { - // Generate test authorities. - let test_authorities = config.generate_authorities(); - - let runtime_api = runtime_api::MockRuntimeApi::new(config.clone(), test_authorities.clone()); - - let av_store = - av_store::MockAvailabilityStore::new(state.chunks.clone(), state.candidate_hashes.clone()); - - let availability_state = NetworkAvailabilityState { - candidate_hashes: state.candidate_hashes.clone(), - available_data: state.available_data.clone(), - chunks: state.chunks.clone(), - }; - - let req_protocol_names = ReqProtocolNames::new(GENESIS_HASH, None); - let (collation_req_receiver, req_cfg) = - IncomingRequest::get_config_receiver(&req_protocol_names); - - let network = - NetworkEmulator::new(&config, &dependencies, &test_authorities, req_protocol_names); - - let network_bridge_tx = network_bridge::MockNetworkBridgeTx::new( - config.clone(), - availability_state, - network.clone(), - ); - - let use_fast_path = match &state.config().objective { - TestObjective::DataAvailabilityRead(options) => options.fetch_from_backers, - _ => panic!("Unexpected objective"), - }; - - let subsystem = if use_fast_path { - AvailabilityRecoverySubsystem::with_fast_path( - collation_req_receiver, - Metrics::try_register(&dependencies.registry).unwrap(), - ) - } else { - AvailabilityRecoverySubsystem::with_chunks_only( - collation_req_receiver, - Metrics::try_register(&dependencies.registry).unwrap(), - ) - }; - - let (overseer, overseer_handle) = build_overseer( - dependencies.task_manager.spawn_handle(), - runtime_api, - av_store, - network_bridge_tx, - subsystem, - ); - - (TestEnvironment::new(dependencies, config, network, overseer, overseer_handle), req_cfg) -} - -#[derive(Clone)] -pub struct TestState { - // Full test configuration - config: TestConfiguration, - // A cycle iterator on all PoV sizes used in the test. - pov_sizes: Cycle>, - // Generated candidate receipts to be used in the test - candidates: Cycle>, - // Map from pov size to candidate index - pov_size_to_candidate: HashMap, - // Map from generated candidate hashes to candidate index in `available_data` - // and `chunks`. - candidate_hashes: HashMap, - // Per candidate index receipts. - candidate_receipt_templates: Vec, - // Per candidate index `AvailableData` - available_data: Vec, - // Per candiadte index chunks - chunks: Vec>, -} - -impl TestState { - fn config(&self) -> &TestConfiguration { - &self.config - } - - pub fn next_candidate(&mut self) -> Option { - let candidate = self.candidates.next(); - let candidate_hash = candidate.as_ref().unwrap().hash(); - gum::trace!(target: LOG_TARGET, "Next candidate selected {:?}", candidate_hash); - candidate - } - - /// Generate candidates to be used in the test. - fn generate_candidates(&mut self) { - let count = self.config.n_cores * self.config.num_blocks; - gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {} candidates.", count).bright_blue()); - - // Generate all candidates - self.candidates = (0..count) - .map(|index| { - let pov_size = self.pov_sizes.next().expect("This is a cycle; qed"); - let candidate_index = *self - .pov_size_to_candidate - .get(&pov_size) - .expect("pov_size always exists; qed"); - let mut candidate_receipt = - self.candidate_receipt_templates[candidate_index].clone(); - - // Make it unique. - candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); - // Store the new candidate in the state - self.candidate_hashes.insert(candidate_receipt.hash(), candidate_index); - - gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate"); - - candidate_receipt - }) - .collect::>() - .into_iter() - .cycle(); - } - - pub fn new(config: &TestConfiguration) -> Self { - let config = config.clone(); - - let mut chunks = Vec::new(); - let mut available_data = Vec::new(); - let mut candidate_receipt_templates = Vec::new(); - let mut pov_size_to_candidate = HashMap::new(); - - // we use it for all candidates. - let persisted_validation_data = PersistedValidationData { - parent_head: HeadData(vec![7, 8, 9]), - relay_parent_number: Default::default(), - max_pov_size: 1024, - relay_parent_storage_root: Default::default(), - }; - - // For each unique pov we create a candidate receipt. - for (index, pov_size) in config.pov_sizes().iter().cloned().unique().enumerate() { - gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue()); - - let mut candidate_receipt = dummy_candidate_receipt(dummy_hash()); - let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) }; - - let new_available_data = AvailableData { - validation_data: persisted_validation_data.clone(), - pov: Arc::new(pov), - }; - - let (new_chunks, erasure_root) = derive_erasure_chunks_with_proofs_and_root( - config.n_validators, - &new_available_data, - |_, _| {}, - ); - - candidate_receipt.descriptor.erasure_root = erasure_root; - - chunks.push(new_chunks); - available_data.push(new_available_data); - pov_size_to_candidate.insert(pov_size, index); - candidate_receipt_templates.push(candidate_receipt); - } - - let pov_sizes = config.pov_sizes().to_owned(); - let pov_sizes = pov_sizes.into_iter().cycle(); - gum::info!(target: LOG_TARGET, "{}","Created test environment.".bright_blue()); - - let mut _self = Self { - config, - available_data, - candidate_receipt_templates, - chunks, - pov_size_to_candidate, - pov_sizes, - candidate_hashes: HashMap::new(), - candidates: Vec::new().into_iter().cycle(), - }; - - _self.generate_candidates(); - _self - } -} - -pub async fn benchmark_availability_read(env: &mut TestEnvironment, mut state: TestState) { - let config = env.config().clone(); - - env.import_block(new_block_import_info(Hash::repeat_byte(1), 1)).await; - - let start_marker = Instant::now(); - let mut batch = FuturesUnordered::new(); - let mut availability_bytes = 0u128; - - env.metrics().set_n_validators(config.n_validators); - env.metrics().set_n_cores(config.n_cores); - - for block_num in 0..env.config().num_blocks { - gum::info!(target: LOG_TARGET, "Current block {}/{}", block_num + 1, env.config().num_blocks); - env.metrics().set_current_block(block_num); - - let block_start_ts = Instant::now(); - for candidate_num in 0..config.n_cores as u64 { - let candidate = - state.next_candidate().expect("We always send up to n_cores*num_blocks; qed"); - let (tx, rx) = oneshot::channel(); - batch.push(rx); - - let message = AllMessages::AvailabilityRecovery( - AvailabilityRecoveryMessage::RecoverAvailableData( - candidate.clone(), - 1, - Some(GroupIndex( - candidate_num as u32 % (std::cmp::max(5, config.n_cores) / 5) as u32, - )), - tx, - ), - ); - env.send_message(message).await; - } - - gum::info!("{}", format!("{} recoveries pending", batch.len()).bright_black()); - while let Some(completed) = batch.next().await { - let available_data = completed.unwrap().unwrap(); - env.metrics().on_pov_size(available_data.encoded_size()); - availability_bytes += available_data.encoded_size() as u128; - } - - let block_time = Instant::now().sub(block_start_ts).as_millis() as u64; - env.metrics().set_block_time(block_time); - gum::info!("All work for block completed in {}", format!("{:?}ms", block_time).cyan()); - } - - let duration: u128 = start_marker.elapsed().as_millis(); - let availability_bytes = availability_bytes / 1024; - gum::info!("All blocks processed in {}", format!("{:?}ms", duration).cyan()); - gum::info!( - "Throughput: {}", - format!("{} KiB/block", availability_bytes / env.config().num_blocks as u128).bright_red() - ); - gum::info!( - "Block time: {}", - format!("{} ms", start_marker.elapsed().as_millis() / env.config().num_blocks as u128) - .red() - ); - - gum::info!("{}", &env); - env.stop().await; -} diff --git a/polkadot/node/subsystem-bench/src/cli.rs b/polkadot/node/subsystem-bench/src/cli.rs deleted file mode 100644 index 3352f33a3503bcdb53cd4ba5f0bc789b9d4cf159..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/cli.rs +++ /dev/null @@ -1,60 +0,0 @@ -// 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::availability::DataAvailabilityReadOptions; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize, clap::Parser)] -#[clap(rename_all = "kebab-case")] -#[allow(missing_docs)] -pub struct TestSequenceOptions { - #[clap(short, long, ignore_case = true)] - pub path: String, -} - -/// Define the supported benchmarks targets -#[derive(Debug, Clone, clap::Parser, Serialize, Deserialize)] -#[command(rename_all = "kebab-case")] -pub enum TestObjective { - /// Benchmark availability recovery strategies. - DataAvailabilityRead(DataAvailabilityReadOptions), - /// Run a test sequence specified in a file - TestSequence(TestSequenceOptions), -} - -#[derive(Debug, clap::Parser)] -#[clap(rename_all = "kebab-case")] -#[allow(missing_docs)] -pub struct StandardTestOptions { - #[clap(long, ignore_case = true, default_value_t = 100)] - /// Number of cores to fetch availability for. - pub n_cores: usize, - - #[clap(long, ignore_case = true, default_value_t = 500)] - /// Number of validators to fetch chunks from. - pub n_validators: usize, - - #[clap(long, ignore_case = true, default_value_t = 5120)] - /// The minimum pov size in KiB - pub min_pov_size: usize, - - #[clap(long, ignore_case = true, default_value_t = 5120)] - /// The maximum pov size bytes - pub max_pov_size: usize, - - #[clap(short, long, ignore_case = true, default_value_t = 1)] - /// The number of blocks the test is going to run. - pub num_blocks: usize, -} diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..deb351360d74ede1ea78494a34f7c459dc544cdd --- /dev/null +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -0,0 +1,202 @@ +// 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 . + +//! A tool for running subsystem benchmark tests +//! designed for development and CI regression testing. + +use clap::Parser; +use color_eyre::eyre; +use colored::Colorize; +use polkadot_subsystem_bench::{approval, availability, configuration}; +use pyroscope::PyroscopeAgent; +use pyroscope_pprofrs::{pprof_backend, PprofConfig}; +use serde::{Deserialize, Serialize}; +use std::path::Path; + +mod valgrind; + +const LOG_TARGET: &str = "subsystem-bench::cli"; + +/// Supported test objectives +#[derive(Debug, Clone, Parser, Serialize, Deserialize)] +#[command(rename_all = "kebab-case")] +pub enum TestObjective { + /// Benchmark availability recovery strategies. + DataAvailabilityRead(availability::DataAvailabilityReadOptions), + /// Benchmark availability and bitfield distribution. + DataAvailabilityWrite, + /// Benchmark the approval-voting and approval-distribution subsystems. + ApprovalVoting(approval::ApprovalsOptions), +} + +impl std::fmt::Display for TestObjective { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::DataAvailabilityRead(_) => "DataAvailabilityRead", + Self::DataAvailabilityWrite => "DataAvailabilityWrite", + Self::ApprovalVoting(_) => "ApprovalVoting", + } + ) + } +} + +/// The test input parameters +#[derive(Clone, Debug, Serialize, Deserialize)] +struct CliTestConfiguration { + /// Test Objective + pub objective: TestObjective, + /// Test Configuration + #[serde(flatten)] + pub test_config: configuration::TestConfiguration, +} + +#[derive(Serialize, Deserialize)] +pub struct TestSequence { + #[serde(rename(serialize = "TestConfiguration", deserialize = "TestConfiguration"))] + test_configurations: Vec, +} + +impl TestSequence { + fn new_from_file(path: &Path) -> std::io::Result { + let string = String::from_utf8(std::fs::read(path)?).expect("File is valid UTF8"); + Ok(serde_yaml::from_str(&string).expect("File is valid test sequence YA")) + } +} + +#[derive(Debug, Parser)] +#[allow(missing_docs)] +struct BenchCli { + #[clap(long, default_value_t = false)] + /// Enable CPU Profiling with Pyroscope + pub profile: bool, + + #[clap(long, requires = "profile", default_value_t = String::from("http://localhost:4040"))] + /// Pyroscope Server URL + pub pyroscope_url: String, + + #[clap(long, requires = "profile", default_value_t = 113)] + /// Pyroscope Sample Rate + pub pyroscope_sample_rate: u32, + + #[clap(long, default_value_t = false)] + /// Enable Cache Misses Profiling with Valgrind. Linux only, Valgrind must be in the PATH + pub cache_misses: bool, + + #[arg(required = true)] + /// Path to the test sequence configuration file + pub path: String, +} + +impl BenchCli { + fn launch(self) -> eyre::Result<()> { + let is_valgrind_running = valgrind::is_valgrind_running(); + if !is_valgrind_running && self.cache_misses { + return valgrind::relaunch_in_valgrind_mode() + } + + let agent_running = if self.profile { + let agent = PyroscopeAgent::builder(self.pyroscope_url.as_str(), "subsystem-bench") + .backend(pprof_backend(PprofConfig::new().sample_rate(self.pyroscope_sample_rate))) + .build()?; + + Some(agent.start()?) + } else { + None + }; + + let test_sequence = TestSequence::new_from_file(Path::new(&self.path)) + .expect("File exists") + .test_configurations; + let num_steps = test_sequence.len(); + gum::info!("{}", format!("Sequence contains {} step(s)", num_steps).bright_purple()); + + for (index, CliTestConfiguration { objective, mut test_config }) in + test_sequence.into_iter().enumerate() + { + let benchmark_name = format!("{} #{} {}", &self.path, index + 1, objective); + gum::info!(target: LOG_TARGET, "{}", format!("Step {}/{}", index + 1, num_steps).bright_purple(),); + gum::info!(target: LOG_TARGET, "[{}] {}", format!("objective = {:?}", objective).green(), test_config); + test_config.generate_pov_sizes(); + + let usage = match objective { + TestObjective::DataAvailabilityRead(opts) => { + let mut state = availability::TestState::new(&test_config); + let (mut env, _protocol_config) = availability::prepare_test( + test_config, + &mut state, + availability::TestDataAvailability::Read(opts), + true, + ); + env.runtime().block_on(availability::benchmark_availability_read( + &benchmark_name, + &mut env, + state, + )) + }, + TestObjective::DataAvailabilityWrite => { + let mut state = availability::TestState::new(&test_config); + let (mut env, _protocol_config) = availability::prepare_test( + test_config, + &mut state, + availability::TestDataAvailability::Write, + true, + ); + env.runtime().block_on(availability::benchmark_availability_write( + &benchmark_name, + &mut env, + state, + )) + }, + TestObjective::ApprovalVoting(ref options) => { + let (mut env, state) = + approval::prepare_test(test_config.clone(), options.clone(), true); + env.runtime().block_on(approval::bench_approvals( + &benchmark_name, + &mut env, + state, + )) + }, + }; + println!("{}", usage); + } + + if let Some(agent_running) = agent_running { + let agent_ready = agent_running.stop()?; + agent_ready.shutdown(); + } + + Ok(()) + } +} + +fn main() -> eyre::Result<()> { + color_eyre::install()?; + env_logger::builder() + .filter(Some("hyper"), log::LevelFilter::Info) + // Avoid `Terminating due to subsystem exit subsystem` warnings + .filter(Some("polkadot_overseer"), log::LevelFilter::Error) + .filter(None, log::LevelFilter::Info) + .format_timestamp_millis() + .try_init() + .unwrap(); + + let cli: BenchCli = BenchCli::parse(); + cli.launch()?; + Ok(()) +} diff --git a/polkadot/node/subsystem-bench/src/valgrind.rs b/polkadot/node/subsystem-bench/src/cli/valgrind.rs similarity index 100% rename from polkadot/node/subsystem-bench/src/valgrind.rs rename to polkadot/node/subsystem-bench/src/cli/valgrind.rs diff --git a/polkadot/node/subsystem-bench/src/core/configuration.rs b/polkadot/node/subsystem-bench/src/core/configuration.rs deleted file mode 100644 index 164addb51900656a278dba2eafc19a7ef558037b..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/core/configuration.rs +++ /dev/null @@ -1,262 +0,0 @@ -// 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 . -// -//! Test configuration definition and helpers. -use super::*; -use keyring::Keyring; -use std::{path::Path, time::Duration}; - -pub use crate::cli::TestObjective; -use polkadot_primitives::{AuthorityDiscoveryId, ValidatorId}; -use rand::{distributions::Uniform, prelude::Distribution, thread_rng}; -use serde::{Deserialize, Serialize}; - -pub fn random_pov_size(min_pov_size: usize, max_pov_size: usize) -> usize { - random_uniform_sample(min_pov_size, max_pov_size) -} - -fn random_uniform_sample + From>(min_value: T, max_value: T) -> T { - Uniform::from(min_value.into()..=max_value.into()) - .sample(&mut thread_rng()) - .into() -} - -/// Peer response latency configuration. -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct PeerLatency { - /// Min latency for `NetworkAction` completion. - pub min_latency: Duration, - /// Max latency or `NetworkAction` completion. - pub max_latency: Duration, -} - -// Default PoV size in KiB. -fn default_pov_size() -> usize { - 5120 -} - -// Default bandwidth in bytes -fn default_bandwidth() -> usize { - 52428800 -} - -// Default connectivity percentage -fn default_connectivity() -> usize { - 100 -} - -/// The test input parameters -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TestConfiguration { - /// The test objective - pub objective: TestObjective, - /// Number of validators - pub n_validators: usize, - /// Number of cores - pub n_cores: usize, - /// The min PoV size - #[serde(default = "default_pov_size")] - pub min_pov_size: usize, - /// The max PoV size, - #[serde(default = "default_pov_size")] - pub max_pov_size: usize, - /// Randomly sampled pov_sizes - #[serde(skip)] - pov_sizes: Vec, - /// The amount of bandiwdth remote validators have. - #[serde(default = "default_bandwidth")] - pub peer_bandwidth: usize, - /// The amount of bandiwdth our node has. - #[serde(default = "default_bandwidth")] - pub bandwidth: usize, - /// Optional peer emulation latency - #[serde(default)] - pub latency: Option, - /// Error probability, applies to sending messages to the emulated network peers - #[serde(default)] - pub error: usize, - /// Connectivity ratio, the percentage of peers we are not connected to, but ar part of - /// the topology. - #[serde(default = "default_connectivity")] - pub connectivity: usize, - /// Number of blocks to run the test for - pub num_blocks: usize, -} - -fn generate_pov_sizes(count: usize, min_kib: usize, max_kib: usize) -> Vec { - (0..count).map(|_| random_pov_size(min_kib * 1024, max_kib * 1024)).collect() -} - -#[derive(Serialize, Deserialize)] -pub struct TestSequence { - #[serde(rename(serialize = "TestConfiguration", deserialize = "TestConfiguration"))] - test_configurations: Vec, -} - -impl TestSequence { - pub fn into_vec(self) -> Vec { - self.test_configurations - .into_iter() - .map(|mut config| { - config.pov_sizes = - generate_pov_sizes(config.n_cores, config.min_pov_size, config.max_pov_size); - config - }) - .collect() - } -} - -impl TestSequence { - pub fn new_from_file(path: &Path) -> std::io::Result { - let string = String::from_utf8(std::fs::read(path)?).expect("File is valid UTF8"); - Ok(serde_yaml::from_str(&string).expect("File is valid test sequence YA")) - } -} - -/// Helper struct for authority related state. -#[derive(Clone)] -pub struct TestAuthorities { - pub keyrings: Vec, - pub validator_public: Vec, - pub validator_authority_id: Vec, -} - -impl TestConfiguration { - #[allow(unused)] - pub fn write_to_disk(&self) { - // Serialize a slice of configurations - let yaml = serde_yaml::to_string(&TestSequence { test_configurations: vec![self.clone()] }) - .unwrap(); - std::fs::write("last_test.yaml", yaml).unwrap(); - } - - pub fn pov_sizes(&self) -> &[usize] { - &self.pov_sizes - } - - /// Generates the authority keys we need for the network emulation. - pub fn generate_authorities(&self) -> TestAuthorities { - let keyrings = (0..self.n_validators) - .map(|peer_index| Keyring::new(format!("Node{}", peer_index))) - .collect::>(); - - // Generate `AuthorityDiscoveryId`` for each peer - let validator_public: Vec = keyrings - .iter() - .map(|keyring: &Keyring| keyring.clone().public().into()) - .collect::>(); - - let validator_authority_id: Vec = keyrings - .iter() - .map(|keyring| keyring.clone().public().into()) - .collect::>(); - - TestAuthorities { keyrings, validator_public, validator_authority_id } - } - - /// An unconstrained standard configuration matching Polkadot/Kusama - pub fn ideal_network( - objective: TestObjective, - num_blocks: usize, - n_validators: usize, - n_cores: usize, - min_pov_size: usize, - max_pov_size: usize, - ) -> TestConfiguration { - Self { - objective, - n_cores, - n_validators, - pov_sizes: generate_pov_sizes(n_cores, min_pov_size, max_pov_size), - bandwidth: 50 * 1024 * 1024, - peer_bandwidth: 50 * 1024 * 1024, - // No latency - latency: None, - error: 0, - num_blocks, - min_pov_size, - max_pov_size, - connectivity: 100, - } - } - - pub fn healthy_network( - objective: TestObjective, - num_blocks: usize, - n_validators: usize, - n_cores: usize, - min_pov_size: usize, - max_pov_size: usize, - ) -> TestConfiguration { - Self { - objective, - n_cores, - n_validators, - pov_sizes: generate_pov_sizes(n_cores, min_pov_size, max_pov_size), - bandwidth: 50 * 1024 * 1024, - peer_bandwidth: 50 * 1024 * 1024, - latency: Some(PeerLatency { - min_latency: Duration::from_millis(1), - max_latency: Duration::from_millis(100), - }), - error: 3, - num_blocks, - min_pov_size, - max_pov_size, - connectivity: 95, - } - } - - pub fn degraded_network( - objective: TestObjective, - num_blocks: usize, - n_validators: usize, - n_cores: usize, - min_pov_size: usize, - max_pov_size: usize, - ) -> TestConfiguration { - Self { - objective, - n_cores, - n_validators, - pov_sizes: generate_pov_sizes(n_cores, min_pov_size, max_pov_size), - bandwidth: 50 * 1024 * 1024, - peer_bandwidth: 50 * 1024 * 1024, - latency: Some(PeerLatency { - min_latency: Duration::from_millis(10), - max_latency: Duration::from_millis(500), - }), - error: 33, - num_blocks, - min_pov_size, - max_pov_size, - connectivity: 67, - } - } -} - -/// Produce a randomized duration between `min` and `max`. -pub fn random_latency(maybe_peer_latency: Option<&PeerLatency>) -> Option { - maybe_peer_latency.map(|peer_latency| { - Uniform::from(peer_latency.min_latency..=peer_latency.max_latency).sample(&mut thread_rng()) - }) -} - -/// Generate a random error based on `probability`. -/// `probability` should be a number between 0 and 100. -pub fn random_error(probability: usize) -> bool { - Uniform::from(0..=99).sample(&mut thread_rng()) < probability -} diff --git a/polkadot/node/subsystem-bench/src/core/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/core/mock/network_bridge.rs deleted file mode 100644 index 5d534e37c9915e23ea3b1c9bfab41fb8c3c83a8f..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/core/mock/network_bridge.rs +++ /dev/null @@ -1,333 +0,0 @@ -// 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 . -//! -//! A generic av store subsystem mockup suitable to be used in benchmarks. - -use futures::Future; -use parity_scale_codec::Encode; -use polkadot_node_subsystem_types::OverseerSignal; -use std::{collections::HashMap, pin::Pin}; - -use futures::FutureExt; - -use polkadot_node_primitives::{AvailableData, ErasureChunk}; - -use polkadot_primitives::CandidateHash; -use sc_network::{OutboundFailure, RequestFailure}; - -use polkadot_node_subsystem::{ - messages::NetworkBridgeTxMessage, overseer, SpawnedSubsystem, SubsystemError, -}; - -use polkadot_node_network_protocol::request_response::{ - self as req_res, - v1::{AvailableDataFetchingRequest, ChunkFetchingRequest, ChunkResponse}, - IsRequest, Requests, -}; -use polkadot_primitives::AuthorityDiscoveryId; - -use crate::core::{ - configuration::{random_error, random_latency, TestConfiguration}, - network::{NetworkAction, NetworkEmulator, RateLimit}, -}; - -/// The availability store state of all emulated peers. -/// The network bridge tx mock will respond to requests as if the request is being serviced -/// by a remote peer on the network -pub struct NetworkAvailabilityState { - pub candidate_hashes: HashMap, - pub available_data: Vec, - pub chunks: Vec>, -} - -const LOG_TARGET: &str = "subsystem-bench::network-bridge-tx-mock"; - -/// A mock of the network bridge tx subsystem. -pub struct MockNetworkBridgeTx { - /// The test configurationg - config: TestConfiguration, - /// The network availability state - availabilty: NetworkAvailabilityState, - /// A network emulator instance - network: NetworkEmulator, -} - -impl MockNetworkBridgeTx { - pub fn new( - config: TestConfiguration, - availabilty: NetworkAvailabilityState, - network: NetworkEmulator, - ) -> MockNetworkBridgeTx { - Self { config, availabilty, network } - } - - fn not_connected_response( - &self, - authority_discovery_id: &AuthorityDiscoveryId, - future: Pin + Send>>, - ) -> NetworkAction { - // The network action will send the error after a random delay expires. - return NetworkAction::new( - authority_discovery_id.clone(), - future, - 0, - // Generate a random latency based on configuration. - random_latency(self.config.latency.as_ref()), - ) - } - /// Returns an `NetworkAction` corresponding to the peer sending the response. If - /// the peer is connected, the error is sent with a randomized latency as defined in - /// configuration. - fn respond_to_send_request( - &mut self, - request: Requests, - ingress_tx: &mut tokio::sync::mpsc::UnboundedSender, - ) -> NetworkAction { - let ingress_tx = ingress_tx.clone(); - - match request { - Requests::ChunkFetchingV1(outgoing_request) => { - let authority_discovery_id = match outgoing_request.peer { - req_res::Recipient::Authority(authority_discovery_id) => authority_discovery_id, - _ => unimplemented!("Peer recipient not supported yet"), - }; - // Account our sent request bytes. - self.network.peer_stats(0).inc_sent(outgoing_request.payload.encoded_size()); - - // If peer is disconnected return an error - if !self.network.is_peer_connected(&authority_discovery_id) { - // We always send `NotConnected` error and we ignore `IfDisconnected` value in - // the caller. - let future = async move { - let _ = outgoing_request - .pending_response - .send(Err(RequestFailure::NotConnected)); - } - .boxed(); - return self.not_connected_response(&authority_discovery_id, future) - } - - // Account for remote received request bytes. - self.network - .peer_stats_by_id(&authority_discovery_id) - .inc_received(outgoing_request.payload.encoded_size()); - - let validator_index: usize = outgoing_request.payload.index.0 as usize; - let candidate_hash = outgoing_request.payload.candidate_hash; - - let candidate_index = self - .availabilty - .candidate_hashes - .get(&candidate_hash) - .expect("candidate was generated previously; qed"); - gum::warn!(target: LOG_TARGET, ?candidate_hash, candidate_index, "Candidate mapped to index"); - - let chunk: ChunkResponse = self.availabilty.chunks.get(*candidate_index).unwrap() - [validator_index] - .clone() - .into(); - let mut size = chunk.encoded_size(); - - let response = if random_error(self.config.error) { - // Error will not account to any bandwidth used. - size = 0; - Err(RequestFailure::Network(OutboundFailure::ConnectionClosed)) - } else { - Ok(( - req_res::v1::ChunkFetchingResponse::from(Some(chunk)).encode(), - self.network.req_protocol_names().get_name(ChunkFetchingRequest::PROTOCOL), - )) - }; - - let authority_discovery_id_clone = authority_discovery_id.clone(); - - let future = async move { - let _ = outgoing_request.pending_response.send(response); - } - .boxed(); - - let future_wrapper = async move { - // Forward the response to the ingress channel of our node. - // On receive side we apply our node receiving rate limit. - let action = - NetworkAction::new(authority_discovery_id_clone, future, size, None); - ingress_tx.send(action).unwrap(); - } - .boxed(); - - NetworkAction::new( - authority_discovery_id, - future_wrapper, - size, - // Generate a random latency based on configuration. - random_latency(self.config.latency.as_ref()), - ) - }, - Requests::AvailableDataFetchingV1(outgoing_request) => { - let candidate_hash = outgoing_request.payload.candidate_hash; - let candidate_index = self - .availabilty - .candidate_hashes - .get(&candidate_hash) - .expect("candidate was generated previously; qed"); - gum::debug!(target: LOG_TARGET, ?candidate_hash, candidate_index, "Candidate mapped to index"); - - let authority_discovery_id = match outgoing_request.peer { - req_res::Recipient::Authority(authority_discovery_id) => authority_discovery_id, - _ => unimplemented!("Peer recipient not supported yet"), - }; - - // Account our sent request bytes. - self.network.peer_stats(0).inc_sent(outgoing_request.payload.encoded_size()); - - // If peer is disconnected return an error - if !self.network.is_peer_connected(&authority_discovery_id) { - let future = async move { - let _ = outgoing_request - .pending_response - .send(Err(RequestFailure::NotConnected)); - } - .boxed(); - return self.not_connected_response(&authority_discovery_id, future) - } - - // Account for remote received request bytes. - self.network - .peer_stats_by_id(&authority_discovery_id) - .inc_received(outgoing_request.payload.encoded_size()); - - let available_data = - self.availabilty.available_data.get(*candidate_index).unwrap().clone(); - - let size = available_data.encoded_size(); - - let response = if random_error(self.config.error) { - Err(RequestFailure::Network(OutboundFailure::ConnectionClosed)) - } else { - Ok(( - req_res::v1::AvailableDataFetchingResponse::from(Some(available_data)) - .encode(), - self.network - .req_protocol_names() - .get_name(AvailableDataFetchingRequest::PROTOCOL), - )) - }; - - let future = async move { - let _ = outgoing_request.pending_response.send(response); - } - .boxed(); - - let authority_discovery_id_clone = authority_discovery_id.clone(); - - let future_wrapper = async move { - // Forward the response to the ingress channel of our node. - // On receive side we apply our node receiving rate limit. - let action = - NetworkAction::new(authority_discovery_id_clone, future, size, None); - ingress_tx.send(action).unwrap(); - } - .boxed(); - - NetworkAction::new( - authority_discovery_id, - future_wrapper, - size, - // Generate a random latency based on configuration. - random_latency(self.config.latency.as_ref()), - ) - }, - _ => panic!("received an unexpected request"), - } - } -} - -#[overseer::subsystem(NetworkBridgeTx, error=SubsystemError, prefix=self::overseer)] -impl MockNetworkBridgeTx { - fn start(self, ctx: Context) -> SpawnedSubsystem { - let future = self.run(ctx).map(|_| Ok(())).boxed(); - - SpawnedSubsystem { name: "test-environment", future } - } -} - -#[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)] -impl MockNetworkBridgeTx { - async fn run(mut self, mut ctx: Context) { - let (mut ingress_tx, mut ingress_rx) = - tokio::sync::mpsc::unbounded_channel::(); - - // Initialize our node bandwidth limits. - let mut rx_limiter = RateLimit::new(10, self.config.bandwidth); - - let our_network = self.network.clone(); - - // This task will handle node messages receipt from the simulated network. - ctx.spawn_blocking( - "network-receive", - async move { - while let Some(action) = ingress_rx.recv().await { - let size = action.size(); - - // account for our node receiving the data. - our_network.inc_received(size); - rx_limiter.reap(size).await; - action.run().await; - } - } - .boxed(), - ) - .expect("We never fail to spawn tasks"); - - // Main subsystem loop. - loop { - let msg = ctx.recv().await.expect("Overseer never fails us"); - - match msg { - orchestra::FromOrchestra::Signal(signal) => - if signal == OverseerSignal::Conclude { - return - }, - orchestra::FromOrchestra::Communication { msg } => match msg { - NetworkBridgeTxMessage::SendRequests(requests, _if_disconnected) => { - for request in requests { - gum::debug!(target: LOG_TARGET, request = ?request, "Processing request"); - self.network.inc_sent(request_size(&request)); - let action = self.respond_to_send_request(request, &mut ingress_tx); - - // Will account for our node sending the request over the emulated - // network. - self.network.submit_peer_action(action.peer(), action); - } - }, - _ => { - unimplemented!("Unexpected network bridge message") - }, - }, - } - } - } -} - -// A helper to determine the request payload size. -fn request_size(request: &Requests) -> usize { - match request { - Requests::ChunkFetchingV1(outgoing_request) => outgoing_request.payload.encoded_size(), - Requests::AvailableDataFetchingV1(outgoing_request) => - outgoing_request.payload.encoded_size(), - _ => unimplemented!("received an unexpected request"), - } -} diff --git a/polkadot/node/subsystem-bench/src/core/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/core/mock/runtime_api.rs deleted file mode 100644 index d664ebead3cc416c502d32c0a1922b49b408eb29..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/core/mock/runtime_api.rs +++ /dev/null @@ -1,110 +0,0 @@ -// 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 . -//! -//! A generic runtime api subsystem mockup suitable to be used in benchmarks. - -use polkadot_primitives::{GroupIndex, IndexedVec, SessionInfo, ValidatorIndex}; - -use polkadot_node_subsystem::{ - messages::{RuntimeApiMessage, RuntimeApiRequest}, - overseer, SpawnedSubsystem, SubsystemError, -}; -use polkadot_node_subsystem_types::OverseerSignal; - -use crate::core::configuration::{TestAuthorities, TestConfiguration}; -use futures::FutureExt; - -const LOG_TARGET: &str = "subsystem-bench::runtime-api-mock"; - -pub struct RuntimeApiState { - authorities: TestAuthorities, -} - -pub struct MockRuntimeApi { - state: RuntimeApiState, - config: TestConfiguration, -} - -impl MockRuntimeApi { - pub fn new(config: TestConfiguration, authorities: TestAuthorities) -> MockRuntimeApi { - Self { state: RuntimeApiState { authorities }, config } - } - - fn session_info(&self) -> SessionInfo { - let all_validators = (0..self.config.n_validators) - .map(|i| ValidatorIndex(i as _)) - .collect::>(); - - let validator_groups = all_validators.chunks(5).map(Vec::from).collect::>(); - - SessionInfo { - validators: self.state.authorities.validator_public.clone().into(), - discovery_keys: self.state.authorities.validator_authority_id.clone(), - validator_groups: IndexedVec::>::from(validator_groups), - assignment_keys: vec![], - n_cores: self.config.n_cores as u32, - zeroth_delay_tranche_width: 0, - relay_vrf_modulo_samples: 0, - n_delay_tranches: 0, - no_show_slots: 0, - needed_approvals: 0, - active_validator_indices: vec![], - dispute_period: 6, - random_seed: [0u8; 32], - } - } -} - -#[overseer::subsystem(RuntimeApi, error=SubsystemError, prefix=self::overseer)] -impl MockRuntimeApi { - fn start(self, ctx: Context) -> SpawnedSubsystem { - let future = self.run(ctx).map(|_| Ok(())).boxed(); - - SpawnedSubsystem { name: "test-environment", future } - } -} - -#[overseer::contextbounds(RuntimeApi, prefix = self::overseer)] -impl MockRuntimeApi { - async fn run(self, mut ctx: Context) { - loop { - let msg = ctx.recv().await.expect("Overseer never fails us"); - - match msg { - orchestra::FromOrchestra::Signal(signal) => - if signal == OverseerSignal::Conclude { - return - }, - orchestra::FromOrchestra::Communication { msg } => { - gum::debug!(target: LOG_TARGET, msg=?msg, "recv message"); - - match msg { - RuntimeApiMessage::Request( - _request, - RuntimeApiRequest::SessionInfo(_session_index, sender), - ) => { - let _ = sender.send(Ok(Some(self.session_info()))); - }, - // Long term TODO: implement more as needed. - _ => { - unimplemented!("Unexpected runtime-api message") - }, - } - }, - } - } - } -} diff --git a/polkadot/node/subsystem-bench/src/core/network.rs b/polkadot/node/subsystem-bench/src/core/network.rs deleted file mode 100644 index bbf61425f73d0022b0c63b7796be348925b337f8..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/core/network.rs +++ /dev/null @@ -1,499 +0,0 @@ -// 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::{ - configuration::{TestAuthorities, TestConfiguration}, - environment::TestEnvironmentDependencies, - *, -}; -use colored::Colorize; -use polkadot_node_network_protocol::request_response::ReqProtocolNames; -use polkadot_primitives::AuthorityDiscoveryId; -use prometheus_endpoint::U64; -use rand::{seq::SliceRandom, thread_rng}; -use sc_service::SpawnTaskHandle; -use std::{ - collections::HashMap, - sync::{ - atomic::{AtomicU64, Ordering}, - Arc, - }, - time::{Duration, Instant}, -}; -use tokio::sync::mpsc::UnboundedSender; - -// An emulated node egress traffic rate_limiter. -#[derive(Debug)] -pub struct RateLimit { - // How often we refill credits in buckets - tick_rate: usize, - // Total ticks - total_ticks: usize, - // Max refill per tick - max_refill: usize, - // Available credit. We allow for bursts over 1/tick_rate of `cps` budget, but we - // account it by negative credit. - credits: isize, - // When last refilled. - last_refill: Instant, -} - -impl RateLimit { - // Create a new `RateLimit` from a `cps` (credits per second) budget and - // `tick_rate`. - pub fn new(tick_rate: usize, cps: usize) -> Self { - // Compute how much refill for each tick - let max_refill = cps / tick_rate; - RateLimit { - tick_rate, - total_ticks: 0, - max_refill, - // A fresh start - credits: max_refill as isize, - last_refill: Instant::now(), - } - } - - pub async fn refill(&mut self) { - // If this is called to early, we need to sleep until next tick. - let now = Instant::now(); - let next_tick_delta = - (self.last_refill + Duration::from_millis(1000 / self.tick_rate as u64)) - now; - - // Sleep until next tick. - if !next_tick_delta.is_zero() { - gum::trace!(target: LOG_TARGET, "need to sleep {}ms", next_tick_delta.as_millis()); - tokio::time::sleep(next_tick_delta).await; - } - - self.total_ticks += 1; - self.credits += self.max_refill as isize; - self.last_refill = Instant::now(); - } - - // Reap credits from the bucket. - // Blocks if credits budged goes negative during call. - pub async fn reap(&mut self, amount: usize) { - self.credits -= amount as isize; - - if self.credits >= 0 { - return - } - - while self.credits < 0 { - gum::trace!(target: LOG_TARGET, "Before refill: {:?}", &self); - self.refill().await; - gum::trace!(target: LOG_TARGET, "After refill: {:?}", &self); - } - } -} - -#[cfg(test)] -mod tests { - use std::time::Instant; - - use super::RateLimit; - - #[tokio::test] - async fn test_expected_rate() { - let tick_rate = 200; - let budget = 1_000_000; - // rate must not exceeed 100 credits per second - let mut rate_limiter = RateLimit::new(tick_rate, budget); - let mut total_sent = 0usize; - let start = Instant::now(); - - let mut reap_amount = 0; - while rate_limiter.total_ticks < tick_rate { - reap_amount += 1; - reap_amount %= 100; - - rate_limiter.reap(reap_amount).await; - total_sent += reap_amount; - } - - let end = Instant::now(); - - println!("duration: {}", (end - start).as_millis()); - - // Allow up to `budget/max_refill` error tolerance - let lower_bound = budget as u128 * ((end - start).as_millis() / 1000u128); - let upper_bound = budget as u128 * - ((end - start).as_millis() / 1000u128 + rate_limiter.max_refill as u128); - assert!(total_sent as u128 >= lower_bound); - assert!(total_sent as u128 <= upper_bound); - } -} - -// A network peer emulator. It spawns a task that accepts `NetworkActions` and -// executes them with a configurable delay and bandwidth constraints. Tipically -// these actions wrap a future that performs a channel send to the subsystem(s) under test. -#[derive(Clone)] -struct PeerEmulator { - // The queue of requests waiting to be served by the emulator - actions_tx: UnboundedSender, -} - -impl PeerEmulator { - pub fn new( - bandwidth: usize, - spawn_task_handle: SpawnTaskHandle, - stats: Arc, - ) -> Self { - let (actions_tx, mut actions_rx) = tokio::sync::mpsc::unbounded_channel(); - - spawn_task_handle - .clone() - .spawn("peer-emulator", "test-environment", async move { - // Rate limit peer send. - let mut rate_limiter = RateLimit::new(10, bandwidth); - loop { - let stats_clone = stats.clone(); - let maybe_action: Option = actions_rx.recv().await; - if let Some(action) = maybe_action { - let size = action.size(); - rate_limiter.reap(size).await; - if let Some(latency) = action.latency { - spawn_task_handle.spawn( - "peer-emulator-latency", - "test-environment", - async move { - tokio::time::sleep(latency).await; - action.run().await; - stats_clone.inc_sent(size); - }, - ) - } else { - action.run().await; - stats_clone.inc_sent(size); - } - } else { - break - } - } - }); - - Self { actions_tx } - } - - // Queue a send request from the emulated peer. - pub fn send(&mut self, action: NetworkAction) { - self.actions_tx.send(action).expect("peer emulator task lives"); - } -} - -pub type ActionFuture = std::pin::Pin + std::marker::Send>>; -/// An network action to be completed by the emulator task. -pub struct NetworkAction { - // The function that performs the action - run: ActionFuture, - // The payload size that we simulate sending/receiving from a peer - size: usize, - // Peer which should run the action. - peer: AuthorityDiscoveryId, - // The amount of time to delay the polling `run` - latency: Option, -} - -unsafe impl Send for NetworkAction {} - -/// Book keeping of sent and received bytes. -pub struct PeerEmulatorStats { - rx_bytes_total: AtomicU64, - tx_bytes_total: AtomicU64, - metrics: Metrics, - peer_index: usize, -} - -impl PeerEmulatorStats { - pub(crate) fn new(peer_index: usize, metrics: Metrics) -> Self { - Self { - metrics, - rx_bytes_total: AtomicU64::from(0), - tx_bytes_total: AtomicU64::from(0), - peer_index, - } - } - - pub fn inc_sent(&self, bytes: usize) { - self.tx_bytes_total.fetch_add(bytes as u64, Ordering::Relaxed); - self.metrics.on_peer_sent(self.peer_index, bytes); - } - - pub fn inc_received(&self, bytes: usize) { - self.rx_bytes_total.fetch_add(bytes as u64, Ordering::Relaxed); - self.metrics.on_peer_received(self.peer_index, bytes); - } - - pub fn sent(&self) -> u64 { - self.tx_bytes_total.load(Ordering::Relaxed) - } - - pub fn received(&self) -> u64 { - self.rx_bytes_total.load(Ordering::Relaxed) - } -} - -#[derive(Debug, Default)] -pub struct PeerStats { - pub rx_bytes_total: u64, - pub tx_bytes_total: u64, -} -impl NetworkAction { - pub fn new( - peer: AuthorityDiscoveryId, - run: ActionFuture, - size: usize, - latency: Option, - ) -> Self { - Self { run, size, peer, latency } - } - - pub fn size(&self) -> usize { - self.size - } - - pub async fn run(self) { - self.run.await; - } - - pub fn peer(&self) -> AuthorityDiscoveryId { - self.peer.clone() - } -} - -/// The state of a peer on the emulated network. -#[derive(Clone)] -enum Peer { - Connected(PeerEmulator), - Disconnected(PeerEmulator), -} - -impl Peer { - pub fn disconnect(&mut self) { - let new_self = match self { - Peer::Connected(peer) => Peer::Disconnected(peer.clone()), - _ => return, - }; - *self = new_self; - } - - pub fn is_connected(&self) -> bool { - matches!(self, Peer::Connected(_)) - } - - pub fn emulator(&mut self) -> &mut PeerEmulator { - match self { - Peer::Connected(ref mut emulator) => emulator, - Peer::Disconnected(ref mut emulator) => emulator, - } - } -} - -/// Mocks the network bridge and an arbitrary number of connected peer nodes. -/// Implements network latency, bandwidth and connection errors. -#[derive(Clone)] -pub struct NetworkEmulator { - // Per peer network emulation. - peers: Vec, - /// Per peer stats. - stats: Vec>, - /// Each emulated peer is a validator. - validator_authority_ids: HashMap, - /// Request protocol names - req_protocol_names: ReqProtocolNames, -} - -impl NetworkEmulator { - pub fn new( - config: &TestConfiguration, - dependencies: &TestEnvironmentDependencies, - authorities: &TestAuthorities, - req_protocol_names: ReqProtocolNames, - ) -> Self { - let n_peers = config.n_validators; - gum::info!(target: LOG_TARGET, "{}",format!("Initializing emulation for a {} peer network.", n_peers).bright_blue()); - gum::info!(target: LOG_TARGET, "{}",format!("connectivity {}%, error {}%", config.connectivity, config.error).bright_black()); - - let metrics = - Metrics::new(&dependencies.registry).expect("Metrics always register succesfully"); - let mut validator_authority_id_mapping = HashMap::new(); - - // Create a `PeerEmulator` for each peer. - let (stats, mut peers): (_, Vec<_>) = (0..n_peers) - .zip(authorities.validator_authority_id.clone()) - .map(|(peer_index, authority_id)| { - validator_authority_id_mapping.insert(authority_id, peer_index); - let stats = Arc::new(PeerEmulatorStats::new(peer_index, metrics.clone())); - ( - stats.clone(), - Peer::Connected(PeerEmulator::new( - config.peer_bandwidth, - dependencies.task_manager.spawn_handle(), - stats, - )), - ) - }) - .unzip(); - - let connected_count = config.n_validators as f64 / (100.0 / config.connectivity as f64); - - let (_connected, to_disconnect) = - peers.partial_shuffle(&mut thread_rng(), connected_count as usize); - - for peer in to_disconnect { - peer.disconnect(); - } - - gum::info!(target: LOG_TARGET, "{}",format!("Network created, connected validator count {}", connected_count).bright_black()); - - Self { - peers, - stats, - validator_authority_ids: validator_authority_id_mapping, - req_protocol_names, - } - } - - pub fn is_peer_connected(&self, peer: &AuthorityDiscoveryId) -> bool { - self.peer(peer).is_connected() - } - - pub fn submit_peer_action(&mut self, peer: AuthorityDiscoveryId, action: NetworkAction) { - let index = self - .validator_authority_ids - .get(&peer) - .expect("all test authorities are valid; qed"); - - let peer = self.peers.get_mut(*index).expect("We just retrieved the index above; qed"); - - // Only actions of size 0 are allowed on disconnected peers. - // Typically this are delayed error response sends. - if action.size() > 0 && !peer.is_connected() { - gum::warn!(target: LOG_TARGET, peer_index = index, "Attempted to send data from a disconnected peer, operation ignored"); - return - } - - peer.emulator().send(action); - } - - // Returns the sent/received stats for `peer_index`. - pub fn peer_stats(&self, peer_index: usize) -> Arc { - self.stats[peer_index].clone() - } - - // Helper to get peer index by `AuthorityDiscoveryId` - fn peer_index(&self, peer: &AuthorityDiscoveryId) -> usize { - *self - .validator_authority_ids - .get(peer) - .expect("all test authorities are valid; qed") - } - - // Return the Peer entry for a given `AuthorityDiscoveryId`. - fn peer(&self, peer: &AuthorityDiscoveryId) -> &Peer { - &self.peers[self.peer_index(peer)] - } - // Returns the sent/received stats for `peer`. - pub fn peer_stats_by_id(&mut self, peer: &AuthorityDiscoveryId) -> Arc { - let peer_index = self.peer_index(peer); - - self.stats[peer_index].clone() - } - - // Returns the sent/received stats for all peers. - pub fn stats(&self) -> Vec { - let r = self - .stats - .iter() - .map(|stats| PeerStats { - rx_bytes_total: stats.received(), - tx_bytes_total: stats.sent(), - }) - .collect::>(); - r - } - - // Increment bytes sent by our node (the node that contains the subsystem under test) - pub fn inc_sent(&self, bytes: usize) { - // Our node always is peer 0. - self.peer_stats(0).inc_sent(bytes); - } - - // Increment bytes received by our node (the node that contains the subsystem under test) - pub fn inc_received(&self, bytes: usize) { - // Our node always is peer 0. - self.peer_stats(0).inc_received(bytes); - } - - // Get the request protocol names - pub fn req_protocol_names(&self) -> &ReqProtocolNames { - &self.req_protocol_names - } -} - -use polkadot_node_subsystem_util::metrics::prometheus::{ - self, CounterVec, Opts, PrometheusError, Registry, -}; - -/// Emulated network metrics. -#[derive(Clone)] -pub(crate) struct Metrics { - /// Number of bytes sent per peer. - peer_total_sent: CounterVec, - /// Number of received sent per peer. - peer_total_received: CounterVec, -} - -impl Metrics { - pub fn new(registry: &Registry) -> Result { - Ok(Self { - peer_total_sent: prometheus::register( - CounterVec::new( - Opts::new( - "subsystem_benchmark_network_peer_total_bytes_sent", - "Total number of bytes a peer has sent.", - ), - &["peer"], - )?, - registry, - )?, - peer_total_received: prometheus::register( - CounterVec::new( - Opts::new( - "subsystem_benchmark_network_peer_total_bytes_received", - "Total number of bytes a peer has received.", - ), - &["peer"], - )?, - registry, - )?, - }) - } - - /// Increment total sent for a peer. - pub fn on_peer_sent(&self, peer_index: usize, bytes: usize) { - self.peer_total_sent - .with_label_values(vec![format!("node{}", peer_index).as_str()].as_slice()) - .inc_by(bytes as u64); - } - - /// Increment total receioved for a peer. - pub fn on_peer_received(&self, peer_index: usize, bytes: usize) { - self.peer_total_received - .with_label_values(vec![format!("node{}", peer_index).as_str()].as_slice()) - .inc_by(bytes as u64); - } -} diff --git a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..af5ff5aa1facc11f2d0caa8ec77276976a0554a5 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs @@ -0,0 +1,207 @@ +// 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 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_subsystem_types::messages::{ + network_bridge_event::NewGossipTopology, ApprovalDistributionMessage, NetworkBridgeEvent, +}; +use polkadot_overseer::AllMessages; +use polkadot_primitives::{ + BlockNumber, CandidateEvent, CandidateReceipt, CoreIndex, GroupIndex, Hash, Header, + Id as ParaId, Slot, ValidatorIndex, +}; +use polkadot_primitives_test_helpers::dummy_candidate_receipt_bad_sig; +use rand::{seq::SliceRandom, SeedableRng}; +use rand_chacha::ChaCha20Rng; +use sc_network::PeerId; +use sp_consensus_babe::{ + digests::{CompatibleDigestItem, PreDigest, SecondaryVRFPreDigest}, + AllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch, VrfSignature, VrfTranscript, +}; +use sp_core::crypto::VrfSecret; +use sp_keyring::sr25519::Keyring as Sr25519Keyring; +use sp_runtime::{Digest, DigestItem}; +use std::sync::{atomic::AtomicU64, Arc}; + +/// A fake system clock used for driving the approval voting and make +/// it process blocks, assignments and approvals from the past. +#[derive(Clone)] +pub struct PastSystemClock { + /// The real system clock + real_system_clock: SystemClock, + /// The difference in ticks between the real system clock and the current clock. + delta_ticks: Arc, +} + +impl PastSystemClock { + /// Creates a new fake system clock with `delta_ticks` between the real time and the fake one. + pub fn new(real_system_clock: SystemClock, delta_ticks: Arc) -> Self { + PastSystemClock { real_system_clock, delta_ticks } + } +} + +impl Clock for PastSystemClock { + fn tick_now(&self) -> Tick { + self.real_system_clock.tick_now() - + self.delta_ticks.load(std::sync::atomic::Ordering::SeqCst) + } + + fn wait( + &self, + tick: Tick, + ) -> std::pin::Pin + Send + 'static>> { + self.real_system_clock + .wait(tick + self.delta_ticks.load(std::sync::atomic::Ordering::SeqCst)) + } +} + +/// Helper function to generate a babe epoch for this benchmark. +/// It does not change for the duration of the test. +pub fn generate_babe_epoch(current_slot: Slot, authorities: TestAuthorities) -> BabeEpoch { + let authorities = authorities + .validator_babe_id + .into_iter() + .enumerate() + .map(|(index, public)| (public, index as u64)) + .collect_vec(); + BabeEpoch { + epoch_index: 1, + start_slot: current_slot.saturating_sub(1u64), + duration: 200, + authorities, + randomness: [0xde; 32], + config: BabeEpochConfiguration { c: (1, 4), allowed_slots: AllowedSlots::PrimarySlots }, + } +} + +/// Generates a topology to be used for this benchmark. +pub fn generate_topology(test_authorities: &TestAuthorities) -> SessionGridTopology { + let keyrings = test_authorities + .validator_authority_id + .clone() + .into_iter() + .zip(test_authorities.peer_ids.clone()) + .collect_vec(); + + let topology = keyrings + .clone() + .into_iter() + .enumerate() + .map(|(index, (discovery_id, peer_id))| TopologyPeerInfo { + peer_ids: vec![peer_id], + validator_index: ValidatorIndex(index as u32), + discovery_id, + }) + .collect_vec(); + let shuffled = (0..keyrings.len()).collect_vec(); + + SessionGridTopology::new(shuffled, topology) +} + +/// Generates new session topology message. +pub fn generate_new_session_topology( + test_authorities: &TestAuthorities, + test_node: ValidatorIndex, +) -> Vec { + let topology = generate_topology(test_authorities); + + let event = NetworkBridgeEvent::NewGossipTopology(NewGossipTopology { + session: 1, + topology, + local_index: Some(test_node), + }); + vec![AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(event))] +} + +/// Generates a peer view change for the passed `block_hash` +pub fn generate_peer_view_change_for(block_hash: Hash, peer_id: PeerId) -> AllMessages { + let network = NetworkBridgeEvent::PeerViewChange(peer_id, View::new([block_hash], 0)); + + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(network)) +} + +/// Helper function to create a a signature for the block header. +fn garbage_vrf_signature() -> VrfSignature { + let transcript = VrfTranscript::new(b"test-garbage", &[]); + Sr25519Keyring::Alice.pair().vrf_sign(&transcript.into()) +} + +/// Helper function to create a block header. +pub fn make_header(parent_hash: Hash, slot: Slot, number: u32) -> Header { + let digest = + { + let mut digest = Digest::default(); + let vrf_signature = garbage_vrf_signature(); + digest.push(DigestItem::babe_pre_digest(PreDigest::SecondaryVRF( + SecondaryVRFPreDigest { authority_index: 0, slot, vrf_signature }, + ))); + digest + }; + + Header { + digest, + extrinsics_root: Default::default(), + number, + state_root: Default::default(), + parent_hash, + } +} + +/// Helper function to create a candidate receipt. +fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceipt { + let mut r = dummy_candidate_receipt_bad_sig(*hash, Some(Default::default())); + r.descriptor.para_id = para_id; + r +} + +/// Helper function to create a list of candidates that are included in the block +pub fn make_candidates( + block_hash: Hash, + block_number: BlockNumber, + num_cores: u32, + num_candidates: u32, +) -> Vec { + let seed = [block_number as u8; 32]; + let mut rand_chacha = ChaCha20Rng::from_seed(seed); + let mut candidates = (0..num_cores) + .map(|core| { + CandidateEvent::CandidateIncluded( + make_candidate(ParaId::from(core), &block_hash), + Vec::new().into(), + CoreIndex(core), + GroupIndex(core), + ) + }) + .collect_vec(); + let (candidates, _) = candidates.partial_shuffle(&mut rand_chacha, num_candidates as usize); + candidates + .iter_mut() + .map(|val| val.clone()) + .sorted_by(|a, b| match (a, b) { + ( + CandidateEvent::CandidateIncluded(_, _, core_a, _), + CandidateEvent::CandidateIncluded(_, _, core_b, _), + ) => core_a.0.cmp(&core_b.0), + (_, _) => todo!("Should not happen"), + }) + .collect_vec() +} diff --git a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs new file mode 100644 index 0000000000000000000000000000000000000000..c1b31a509f6dd21c3f23bb44afec8859e51fa76a --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs @@ -0,0 +1,676 @@ +// 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 crate::{ + approval::{ + helpers::{generate_babe_epoch, generate_topology}, + test_message::{MessagesBundle, TestMessageInfo}, + ApprovalTestState, ApprovalsOptions, BlockTestData, GeneratedState, + BUFFER_FOR_GENERATION_MILLIS, LOG_TARGET, SLOT_DURATION_MILLIS, + }, + configuration::{TestAuthorities, TestConfiguration}, + mock::runtime_api::session_info_for_peers, + NODE_UNDER_TEST, +}; +use futures::SinkExt; +use itertools::Itertools; +use parity_scale_codec::Encode; +use polkadot_node_core_approval_voting::{ + criteria::{compute_assignments, Config}, + time::tranche_to_tick, +}; +use polkadot_node_network_protocol::{ + grid_topology::{GridNeighbors, RandomRouting, RequiredRouting, SessionGridTopology}, + v3 as protocol_v3, +}; +use polkadot_node_primitives::approval::{ + self, + v2::{CoreBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, +}; +use polkadot_primitives::{ + vstaging::ApprovalVoteMultipleCandidates, CandidateEvent, CandidateHash, CandidateIndex, + CoreIndex, Hash, SessionInfo, Slot, ValidatorId, ValidatorIndex, ASSIGNMENT_KEY_TYPE_ID, +}; +use rand::{seq::SliceRandom, RngCore, SeedableRng}; +use rand_chacha::ChaCha20Rng; +use rand_distr::{Distribution, Normal}; +use sc_keystore::LocalKeystore; +use sc_network::PeerId; +use sc_service::SpawnTaskHandle; +use sha1::Digest; +use sp_application_crypto::AppCrypto; +use sp_consensus_babe::SlotDuration; +use sp_keystore::Keystore; +use sp_timestamp::Timestamp; +use std::{ + cmp::max, + collections::{BTreeMap, HashSet}, + fs, + io::Write, + path::{Path, PathBuf}, + time::Duration, +}; + +/// A generator of messages coming from a given Peer/Validator +pub struct PeerMessagesGenerator { + /// The grid neighbors of the node under test. + pub topology_node_under_test: GridNeighbors, + /// The topology of the network for the epoch under test. + pub topology: SessionGridTopology, + /// The validator index for this object generates the messages. + pub validator_index: ValidatorIndex, + /// An array of pre-generated random samplings, that is used to determine, which nodes would + /// send a given assignment, to the node under test because of the random samplings. + /// As an optimization we generate this sampling at the begining of the test and just pick + /// one randomly, because always taking the samples would be too expensive for benchamrk. + pub random_samplings: Vec>, + /// Channel for sending the generated messages to the aggregator + pub tx_messages: futures::channel::mpsc::UnboundedSender<(Hash, Vec)>, + /// The list of test authorities + pub test_authorities: TestAuthorities, + //// The session info used for the test. + pub session_info: SessionInfo, + /// The blocks used for testing + pub blocks: Vec, + /// Approval options params. + pub options: ApprovalsOptions, +} + +impl PeerMessagesGenerator { + /// Generates messages by spawning a blocking task in the background which begins creating + /// the assignments/approvals and peer view changes at the begining of each block. + pub fn generate_messages(mut self, spawn_task_handle: &SpawnTaskHandle) { + spawn_task_handle.spawn("generate-messages", "generate-messages", async move { + for block_info in &self.blocks { + let assignments = self.generate_assignments(block_info); + + let bytes = self.validator_index.0.to_be_bytes(); + let seed = [ + bytes[0], bytes[1], bytes[2], bytes[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + + let mut rand_chacha = ChaCha20Rng::from_seed(seed); + let approvals = issue_approvals( + assignments, + block_info.hash, + &self.test_authorities.validator_public, + block_info.candidates.clone(), + &self.options, + &mut rand_chacha, + self.test_authorities.keyring.keystore_ref(), + ); + + self.tx_messages + .send((block_info.hash, approvals)) + .await + .expect("Should not fail"); + } + }) + } + + // Builds the messages finger print corresponding to this configuration. + // When the finger print exists already on disk the messages are not re-generated. + fn messages_fingerprint( + configuration: &TestConfiguration, + options: &ApprovalsOptions, + ) -> String { + let mut fingerprint = options.fingerprint(); + let configuration_bytes = bincode::serialize(&configuration).unwrap(); + fingerprint.extend(configuration_bytes); + let mut sha1 = sha1::Sha1::new(); + sha1.update(fingerprint); + let result = sha1.finalize(); + hex::encode(result) + } + + /// Generate all messages(Assignments & Approvals) needed for approving `blocks``. + pub fn generate_messages_if_needed( + configuration: &TestConfiguration, + test_authorities: &TestAuthorities, + options: &ApprovalsOptions, + spawn_task_handle: &SpawnTaskHandle, + ) -> PathBuf { + let path_name = format!( + "{}/{}", + options.workdir_prefix, + Self::messages_fingerprint(configuration, options) + ); + + let path = Path::new(&path_name); + if path.exists() { + return path.to_path_buf(); + } + + gum::info!("Generate message because file does not exist"); + let delta_to_first_slot_under_test = Timestamp::new(BUFFER_FOR_GENERATION_MILLIS); + let initial_slot = Slot::from_timestamp( + (*Timestamp::current() - *delta_to_first_slot_under_test).into(), + SlotDuration::from_millis(SLOT_DURATION_MILLIS), + ); + + let babe_epoch = generate_babe_epoch(initial_slot, test_authorities.clone()); + let session_info = session_info_for_peers(configuration, test_authorities); + let blocks = ApprovalTestState::generate_blocks_information( + configuration, + &babe_epoch, + initial_slot, + ); + + gum::info!(target: LOG_TARGET, "Generate messages"); + let topology = generate_topology(test_authorities); + + let random_samplings = random_samplings_to_node( + ValidatorIndex(NODE_UNDER_TEST), + test_authorities.validator_public.len(), + test_authorities.validator_public.len() * 2, + ); + + let topology_node_under_test = + topology.compute_grid_neighbors_for(ValidatorIndex(NODE_UNDER_TEST)).unwrap(); + + let (tx, mut rx) = futures::channel::mpsc::unbounded(); + + // Spawn a thread to generate the messages for each validator, so that we speed up the + // generation. + for current_validator_index in 1..test_authorities.validator_public.len() { + let peer_message_source = PeerMessagesGenerator { + topology_node_under_test: topology_node_under_test.clone(), + topology: topology.clone(), + validator_index: ValidatorIndex(current_validator_index as u32), + test_authorities: test_authorities.clone(), + session_info: session_info.clone(), + blocks: blocks.clone(), + tx_messages: tx.clone(), + random_samplings: random_samplings.clone(), + options: options.clone(), + }; + + peer_message_source.generate_messages(spawn_task_handle); + } + + std::mem::drop(tx); + + let seed = [0x32; 32]; + let mut rand_chacha = ChaCha20Rng::from_seed(seed); + + let mut all_messages: BTreeMap> = BTreeMap::new(); + // Receive all messages and sort them by Tick they have to be sent. + loop { + match rx.try_next() { + Ok(Some((block_hash, messages))) => + for message in messages { + let block_info = blocks + .iter() + .find(|val| val.hash == block_hash) + .expect("Should find blocks"); + let tick_to_send = tranche_to_tick( + SLOT_DURATION_MILLIS, + block_info.slot, + message.tranche_to_send(), + ); + let to_add = all_messages.entry(tick_to_send).or_default(); + to_add.push(message); + }, + Ok(None) => break, + Err(_) => { + std::thread::sleep(Duration::from_millis(50)); + }, + } + } + let all_messages = all_messages + .into_iter() + .flat_map(|(_, mut messages)| { + // Shuffle the messages inside the same tick, so that we don't priorites messages + // for older nodes. we try to simulate the same behaviour as in real world. + messages.shuffle(&mut rand_chacha); + messages + }) + .collect_vec(); + + gum::info!("Generated a number of {:} unique messages", all_messages.len()); + + let generated_state = GeneratedState { all_messages: Some(all_messages), initial_slot }; + + let mut messages_file = fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .unwrap(); + + messages_file + .write_all(&generated_state.encode()) + .expect("Could not update message file"); + path.to_path_buf() + } + + /// Generates assignments for the given `current_validator_index` + /// Returns a list of assignments to be sent sorted by tranche. + fn generate_assignments(&self, block_info: &BlockTestData) -> Vec { + let config = Config::from(&self.session_info); + + let leaving_cores = block_info + .candidates + .clone() + .into_iter() + .map(|candidate_event| { + if let CandidateEvent::CandidateIncluded(candidate, _, core_index, group_index) = + candidate_event + { + (candidate.hash(), core_index, group_index) + } else { + todo!("Variant is never created in this benchmark") + } + }) + .collect_vec(); + + let mut assignments_by_tranche = BTreeMap::new(); + + let bytes = self.validator_index.0.to_be_bytes(); + let seed = [ + bytes[0], bytes[1], bytes[2], bytes[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + let mut rand_chacha = ChaCha20Rng::from_seed(seed); + + let to_be_sent_by = neighbours_that_would_sent_message( + &self.test_authorities.peer_ids, + self.validator_index.0, + &self.topology_node_under_test, + &self.topology, + ); + + let leaving_cores = leaving_cores + .clone() + .into_iter() + .filter(|(_, core_index, _group_index)| core_index.0 != self.validator_index.0) + .collect_vec(); + + let store = LocalKeystore::in_memory(); + let _public = store + .sr25519_generate_new( + ASSIGNMENT_KEY_TYPE_ID, + Some(self.test_authorities.key_seeds[self.validator_index.0 as usize].as_str()), + ) + .expect("should not fail"); + let assignments = compute_assignments( + &store, + block_info.relay_vrf_story.clone(), + &config, + leaving_cores.clone(), + self.options.enable_assignments_v2, + ); + + let random_sending_nodes = self + .random_samplings + .get(rand_chacha.next_u32() as usize % self.random_samplings.len()) + .unwrap(); + let random_sending_peer_ids = random_sending_nodes + .iter() + .map(|validator| (*validator, self.test_authorities.peer_ids[validator.0 as usize])) + .collect_vec(); + + let mut unique_assignments = HashSet::new(); + for (core_index, assignment) in assignments { + let assigned_cores = match &assignment.cert().kind { + approval::v2::AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => + core_bitfield.iter_ones().map(|val| CoreIndex::from(val as u32)).collect_vec(), + approval::v2::AssignmentCertKindV2::RelayVRFDelay { core_index } => + vec![*core_index], + approval::v2::AssignmentCertKindV2::RelayVRFModulo { sample: _ } => + vec![core_index], + }; + + let bitfiled: CoreBitfield = assigned_cores.clone().try_into().unwrap(); + + // For the cases where tranch0 assignments are in a single certificate we need to make + // sure we create a single message. + if unique_assignments.insert(bitfiled) { + let this_tranche_assignments = + assignments_by_tranche.entry(assignment.tranche()).or_insert_with(Vec::new); + + this_tranche_assignments.push(( + IndirectAssignmentCertV2 { + block_hash: block_info.hash, + validator: self.validator_index, + cert: assignment.cert().clone(), + }, + block_info + .candidates + .iter() + .enumerate() + .filter(|(_index, candidate)| { + if let CandidateEvent::CandidateIncluded(_, _, core, _) = candidate { + assigned_cores.contains(core) + } else { + panic!("Should not happen"); + } + }) + .map(|(index, _)| index as u32) + .collect_vec() + .try_into() + .unwrap(), + to_be_sent_by + .iter() + .chain(random_sending_peer_ids.iter()) + .copied() + .collect::>(), + assignment.tranche(), + )); + } + } + + assignments_by_tranche + .into_values() + .flat_map(|assignments| assignments.into_iter()) + .map(|assignment| { + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment.0, + assignment.1, + )]); + TestMessageInfo { + msg, + sent_by: assignment + .2 + .into_iter() + .map(|(validator_index, _)| validator_index) + .collect_vec(), + tranche: assignment.3, + block_hash: block_info.hash, + } + }) + .collect_vec() + } +} + +/// A list of random samplings that we use to determine which nodes should send a given message to +/// the node under test. +/// We can not sample every time for all the messages because that would be too expensive to +/// perform, so pre-generate a list of samples for a given network size. +/// - result[i] give us as a list of random nodes that would send a given message to the node under +/// test. +fn random_samplings_to_node( + node_under_test: ValidatorIndex, + num_validators: usize, + num_samplings: usize, +) -> Vec> { + let seed = [7u8; 32]; + let mut rand_chacha = ChaCha20Rng::from_seed(seed); + + (0..num_samplings) + .map(|_| { + (0..num_validators) + .filter(|sending_validator_index| { + *sending_validator_index != NODE_UNDER_TEST as usize + }) + .flat_map(|sending_validator_index| { + let mut validators = (0..num_validators).collect_vec(); + validators.shuffle(&mut rand_chacha); + + let mut random_routing = RandomRouting::default(); + validators + .into_iter() + .flat_map(|validator_to_send| { + if random_routing.sample(num_validators, &mut rand_chacha) { + random_routing.inc_sent(); + if validator_to_send == node_under_test.0 as usize { + Some(ValidatorIndex(sending_validator_index as u32)) + } else { + None + } + } else { + None + } + }) + .collect_vec() + }) + .collect_vec() + }) + .collect_vec() +} + +/// Helper function to randomly determine how many approvals we coalesce together in a single +/// message. +fn coalesce_approvals_len( + coalesce_mean: f32, + coalesce_std_dev: f32, + rand_chacha: &mut ChaCha20Rng, +) -> usize { + max( + 1, + Normal::new(coalesce_mean, coalesce_std_dev) + .expect("normal distribution parameters are good") + .sample(rand_chacha) + .round() as i32, + ) as usize +} + +/// Helper function to create approvals signatures for all assignments passed as arguments. +/// Returns a list of Approvals messages that need to be sent. +fn issue_approvals( + assignments: Vec, + block_hash: Hash, + validator_ids: &[ValidatorId], + candidates: Vec, + options: &ApprovalsOptions, + rand_chacha: &mut ChaCha20Rng, + store: &LocalKeystore, +) -> Vec { + let mut queued_to_sign: Vec = Vec::new(); + let mut num_coalesce = + coalesce_approvals_len(options.coalesce_mean, options.coalesce_std_dev, rand_chacha); + let result = assignments + .iter() + .enumerate() + .map(|(_index, message)| match &message.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => { + let mut approvals_to_create = Vec::new(); + + let current_validator_index = queued_to_sign + .first() + .map(|msg| msg.validator_index) + .unwrap_or(ValidatorIndex(99999)); + + // Invariant for this benchmark. + assert_eq!(assignments.len(), 1); + + let assignment = assignments.first().unwrap(); + + let earliest_tranche = queued_to_sign + .first() + .map(|val| val.assignment.tranche) + .unwrap_or(message.tranche); + + if queued_to_sign.len() >= num_coalesce || + (!queued_to_sign.is_empty() && + current_validator_index != assignment.0.validator) || + message.tranche - earliest_tranche >= options.coalesce_tranche_diff + { + approvals_to_create.push(TestSignInfo::sign_candidates( + &mut queued_to_sign, + validator_ids, + block_hash, + num_coalesce, + store, + )); + num_coalesce = coalesce_approvals_len( + options.coalesce_mean, + options.coalesce_std_dev, + rand_chacha, + ); + } + + // If more that one candidate was in the assignment queue all of them for issuing + // approvals + for candidate_index in assignment.1.iter_ones() { + let candidate = candidates.get(candidate_index).unwrap(); + if let CandidateEvent::CandidateIncluded(candidate, _, _, _) = candidate { + queued_to_sign.push(TestSignInfo { + candidate_hash: candidate.hash(), + candidate_index: candidate_index as CandidateIndex, + validator_index: assignment.0.validator, + assignment: message.clone(), + }); + } else { + todo!("Other enum variants are not used in this benchmark"); + } + } + approvals_to_create + }, + _ => { + todo!("Other enum variants are not used in this benchmark"); + }, + }) + .collect_vec(); + + let mut messages = result.into_iter().flatten().collect_vec(); + + if !queued_to_sign.is_empty() { + messages.push(TestSignInfo::sign_candidates( + &mut queued_to_sign, + validator_ids, + block_hash, + num_coalesce, + store, + )); + } + messages +} + +/// Helper struct to gather information about more than one candidate an sign it in a single +/// approval message. +struct TestSignInfo { + /// The candidate hash + candidate_hash: CandidateHash, + /// The candidate index + candidate_index: CandidateIndex, + /// The validator sending the assignments + validator_index: ValidatorIndex, + /// The assignments convering this candidate + assignment: TestMessageInfo, +} + +impl TestSignInfo { + /// Helper function to create a signture for all candidates in `to_sign` parameter. + /// Returns a TestMessage + fn sign_candidates( + to_sign: &mut Vec, + validator_ids: &[ValidatorId], + block_hash: Hash, + num_coalesce: usize, + store: &LocalKeystore, + ) -> MessagesBundle { + let current_validator_index = to_sign.first().map(|val| val.validator_index).unwrap(); + let tranche_approval_can_be_sent = + to_sign.iter().map(|val| val.assignment.tranche).max().unwrap(); + let validator_id = validator_ids.get(current_validator_index.0 as usize).unwrap().clone(); + + let unique_assignments: HashSet = + to_sign.iter().map(|info| info.assignment.clone()).collect(); + + let mut to_sign = to_sign + .drain(..) + .sorted_by(|val1, val2| val1.candidate_index.cmp(&val2.candidate_index)) + .peekable(); + + let mut bundle = MessagesBundle { + assignments: unique_assignments.into_iter().collect_vec(), + approvals: Vec::new(), + }; + + while to_sign.peek().is_some() { + let to_sign = to_sign.by_ref().take(num_coalesce).collect_vec(); + + let hashes = to_sign.iter().map(|val| val.candidate_hash).collect_vec(); + let candidate_indices = to_sign.iter().map(|val| val.candidate_index).collect_vec(); + + let sent_by = to_sign + .iter() + .flat_map(|val| val.assignment.sent_by.iter()) + .copied() + .collect::>(); + + let payload = ApprovalVoteMultipleCandidates(&hashes).signing_payload(1); + + let signature = store + .sr25519_sign(ValidatorId::ID, &validator_id.clone().into(), &payload[..]) + .unwrap() + .unwrap() + .into(); + let indirect = IndirectSignedApprovalVoteV2 { + block_hash, + candidate_indices: candidate_indices.try_into().unwrap(), + validator: current_validator_index, + signature, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![indirect]); + + bundle.approvals.push(TestMessageInfo { + msg, + sent_by: sent_by.into_iter().collect_vec(), + tranche: tranche_approval_can_be_sent, + block_hash, + }); + } + bundle + } +} + +/// Determine what neighbours would send a given message to the node under test. +fn neighbours_that_would_sent_message( + peer_ids: &[PeerId], + current_validator_index: u32, + topology_node_under_test: &GridNeighbors, + topology: &SessionGridTopology, +) -> Vec<(ValidatorIndex, PeerId)> { + let topology_originator = topology + .compute_grid_neighbors_for(ValidatorIndex(current_validator_index)) + .unwrap(); + + let originator_y = topology_originator.validator_indices_y.iter().find(|validator| { + topology_node_under_test.required_routing_by_index(**validator, false) == + RequiredRouting::GridY + }); + + assert!(originator_y != Some(&ValidatorIndex(NODE_UNDER_TEST))); + + let originator_x = topology_originator.validator_indices_x.iter().find(|validator| { + topology_node_under_test.required_routing_by_index(**validator, false) == + RequiredRouting::GridX + }); + + assert!(originator_x != Some(&ValidatorIndex(NODE_UNDER_TEST))); + + let is_neighbour = topology_originator + .validator_indices_x + .contains(&ValidatorIndex(NODE_UNDER_TEST)) || + topology_originator + .validator_indices_y + .contains(&ValidatorIndex(NODE_UNDER_TEST)); + + let mut to_be_sent_by = originator_y + .into_iter() + .chain(originator_x) + .map(|val| (*val, peer_ids[val.0 as usize])) + .collect_vec(); + + if is_neighbour { + to_be_sent_by.push((ValidatorIndex(current_validator_index), peer_ids[0])); + } + + to_be_sent_by +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..77ba80d4b2bbcbc7616c00ad6f5069ceb22979bc --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs @@ -0,0 +1,64 @@ +// 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 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_subsystem::{overseer, SpawnedSubsystem, SubsystemError}; +use polkadot_node_subsystem_types::messages::ChainSelectionMessage; + +/// Mock ChainSelection subsystem used to answer request made by the approval-voting subsystem, +/// during benchmark. All the necessary information to answer the requests is stored in the `state` +pub struct MockChainSelection { + pub state: ApprovalTestState, + pub clock: PastSystemClock, +} + +#[overseer::subsystem(ChainSelection, error=SubsystemError, prefix=self::overseer)] +impl MockChainSelection { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self.run(ctx).map(|_| Ok(())).boxed(); + + SpawnedSubsystem { name: "mock-chain-subsystem", future } + } +} + +#[overseer::contextbounds(ChainSelection, prefix = self::overseer)] +impl MockChainSelection { + async fn run(self, mut ctx: Context) { + loop { + let msg = ctx.recv().await.expect("Should not fail"); + match msg { + orchestra::FromOrchestra::Signal(_) => {}, + orchestra::FromOrchestra::Communication { msg } => + if let ChainSelectionMessage::Approved(hash) = msg { + let block_info = self.state.get_info_by_hash(hash); + let approved_number = block_info.block_number; + + block_info.approved.store(true, std::sync::atomic::Ordering::SeqCst); + self.state + .last_approved_block + .store(approved_number, std::sync::atomic::Ordering::SeqCst); + + let approved_in_tick = self.clock.tick_now() - + slot_number_to_tick(SLOT_DURATION_MILLIS, block_info.slot); + + gum::info!(target: LOG_TARGET, ?hash, "Chain selection approved after {:} ms", approved_in_tick * TICK_DURATION_MILLIS); + }, + } + } + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..450faf06123f61db9ee91d1d935721d3c128e701 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -0,0 +1,1072 @@ +// 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 crate::{ + approval::{ + helpers::{ + generate_babe_epoch, generate_new_session_topology, generate_peer_view_change_for, + make_header, PastSystemClock, + }, + message_generator::PeerMessagesGenerator, + mock_chain_selection::MockChainSelection, + test_message::{MessagesBundle, TestMessageInfo}, + }, + configuration::{TestAuthorities, TestConfiguration}, + dummy_builder, + environment::{TestEnvironment, TestEnvironmentDependencies, MAX_TIME_OF_FLIGHT}, + mock::{ + chain_api::{ChainApiState, MockChainApi}, + network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, + runtime_api::MockRuntimeApi, + AlwaysSupportsParachains, TestSyncOracle, + }, + network::{ + new_network, HandleNetworkMessage, NetworkEmulatorHandle, NetworkInterface, + NetworkInterfaceReceiver, + }, + usage::BenchmarkUsage, + NODE_UNDER_TEST, +}; +use colored::Colorize; +use futures::channel::oneshot; +use itertools::Itertools; +use orchestra::TimeoutExt; +use overseer::{metrics::Metrics as OverseerMetrics, MetricsTrait}; +use parity_scale_codec::{Decode, Encode}; +use polkadot_approval_distribution::ApprovalDistribution; +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, +}; +use polkadot_node_network_protocol::v3 as protocol_v3; +use polkadot_node_primitives::approval::{self, v1::RelayVRFStory}; +use polkadot_node_subsystem::{overseer, AllMessages, Overseer, OverseerConnector, SpawnGlue}; +use polkadot_node_subsystem_test_helpers::mock::new_block_import_info; +use polkadot_node_subsystem_types::messages::{ApprovalDistributionMessage, ApprovalVotingMessage}; +use polkadot_node_subsystem_util::metrics::Metrics; +use polkadot_overseer::Handle as OverseerHandleReal; +use polkadot_primitives::{ + BlockNumber, CandidateEvent, CandidateIndex, CandidateReceipt, Hash, Header, Slot, + ValidatorIndex, +}; +use prometheus::Registry; +use sc_keystore::LocalKeystore; +use sc_service::SpawnTaskHandle; +use serde::{Deserialize, Serialize}; +use sp_consensus_babe::Epoch as BabeEpoch; +use sp_core::H256; +use std::{ + cmp::max, + collections::{HashMap, HashSet}, + fs, + io::Read, + ops::Sub, + sync::{ + atomic::{AtomicBool, AtomicU32, AtomicU64}, + Arc, + }, + time::{Duration, Instant}, +}; +use tokio::time::sleep; + +mod helpers; +mod message_generator; +mod mock_chain_selection; +mod test_message; + +pub(crate) const LOG_TARGET: &str = "subsystem-bench::approval"; +pub(crate) const NUM_COLUMNS: u32 = 1; +pub(crate) const SLOT_DURATION_MILLIS: u64 = 6000; +pub(crate) const TEST_CONFIG: ApprovalVotingConfig = ApprovalVotingConfig { + col_approval_data: DATA_COL, + slot_duration_millis: SLOT_DURATION_MILLIS, +}; + +const DATA_COL: u32 = 0; + +/// Start generating messages for a slot into the future, so that the +/// generation nevers falls behind the current slot. +const BUFFER_FOR_GENERATION_MILLIS: u64 = 30_000; + +/// Parameters specific to the approvals benchmark +#[derive(Debug, Clone, Serialize, Deserialize, clap::Parser)] +#[clap(rename_all = "kebab-case")] +#[allow(missing_docs)] +pub struct ApprovalsOptions { + #[clap(short, long, default_value_t = 89)] + /// The last considered tranche for which we send the message. + pub last_considered_tranche: u32, + #[clap(short, long, default_value_t = 1.0)] + /// Min candidates to be signed in a single approval. + pub coalesce_mean: f32, + #[clap(short, long, default_value_t = 1.0)] + /// Max candidate to be signed in a single approval. + pub coalesce_std_dev: f32, + /// The maximum tranche diff between approvals coalesced toghther. + pub coalesce_tranche_diff: u32, + #[clap(short, long, default_value_t = false)] + /// Enable assignments v2. + pub enable_assignments_v2: bool, + #[clap(short, long, default_value_t = true)] + /// Sends messages only till block is approved. + pub stop_when_approved: bool, + #[clap(short, long)] + /// Work directory. + #[clap(short, long, default_value_t = format!("/tmp"))] + pub workdir_prefix: String, + /// The number of no shows per candidate + #[clap(short, long, default_value_t = 0)] + pub num_no_shows_per_candidate: u32, +} + +impl ApprovalsOptions { + // Generates a fingerprint use to determine if messages need to be re-generated. + fn fingerprint(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.extend(self.coalesce_mean.to_be_bytes()); + bytes.extend(self.coalesce_std_dev.to_be_bytes()); + bytes.extend(self.coalesce_tranche_diff.to_be_bytes()); + bytes.extend((self.enable_assignments_v2 as i32).to_be_bytes()); + bytes + } +} + +/// Information about a block. It is part of test state and it is used by the mock +/// subsystems to be able to answer the calls approval-voting and approval-distribution +/// do into the outside world. +#[derive(Clone, Debug)] +struct BlockTestData { + /// The slot this block occupies, see implementer's guide to understand what a slot + /// is in the context of polkadot. + slot: Slot, + /// The hash of the block. + hash: Hash, + /// The block number. + block_number: BlockNumber, + /// The list of candidates included in this block. + candidates: Vec, + /// The block header. + header: Header, + /// The vrf story for the given block. + relay_vrf_story: RelayVRFStory, + /// If the block has been approved by the approval-voting subsystem. + /// This set on `true` when ChainSelectionMessage::Approved is received inside the chain + /// selection mock subsystem. + approved: Arc, + /// The total number of candidates before this block. + total_candidates_before: u64, + /// The votes we sent. + /// votes[validator_index][candidate_index] tells if validator sent vote for candidate. + /// We use this to mark the test as succesfull if GetApprovalSignatures returns all the votes + /// from here. + votes: Arc>>, +} + +/// Candidate information used during the test to decide if more messages are needed. +#[derive(Debug)] +struct CandidateTestData { + /// The configured maximum number of no-shows for this candidate. + max_no_shows: u32, + /// The last tranche where we had a no-show. + last_tranche_with_no_show: u32, + /// The number of sent assignments. + sent_assignment: u32, + /// The number of no-shows. + num_no_shows: u32, + /// The maximum tranche were we covered the needed approvals + max_tranche: u32, + /// Minimum needed votes to approve candidate. + needed_approvals: u32, +} + +impl CandidateTestData { + /// If message in this tranche needs to be sent. + fn should_send_tranche(&self, tranche: u32) -> bool { + self.sent_assignment <= self.needed_approvals || + tranche <= self.max_tranche + self.num_no_shows + } + + /// Sets max tranche + fn set_max_tranche(&mut self, tranche: u32) { + self.max_tranche = max(tranche, self.max_tranche); + } + + /// Records no-show for candidate. + fn record_no_show(&mut self, tranche: u32) { + self.num_no_shows += 1; + self.last_tranche_with_no_show = max(tranche, self.last_tranche_with_no_show); + } + + /// Marks an assignment sent. + fn mark_sent_assignment(&mut self, tranche: u32) { + if self.sent_assignment < self.needed_approvals { + self.set_max_tranche(tranche); + } + + self.sent_assignment += 1; + } + + /// Tells if a message in this tranche should be a no-show. + fn should_no_show(&self, tranche: u32) -> bool { + (self.num_no_shows < self.max_no_shows && self.last_tranche_with_no_show < tranche) || + (tranche == 0 && self.num_no_shows == 0 && self.max_no_shows > 0) + } +} + +/// Test state that is pre-generated and loaded from a file that matches the fingerprint +/// of the TestConfiguration. +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] +struct GeneratedState { + /// All assignments and approvals + all_messages: Option>, + /// The first slot in the test. + initial_slot: Slot, +} + +/// Approval test state used by all mock subsystems to be able to answer messages emitted +/// by the approval-voting and approval-distribution-subystems. +/// +/// This gets cloned across all mock subsystems, so if there is any information that gets +/// updated between subsystems, they would have to be wrapped in Arc's. +#[derive(Clone)] +pub struct ApprovalTestState { + /// The main test configuration + configuration: TestConfiguration, + /// The specific test configurations passed when starting the benchmark. + options: ApprovalsOptions, + /// The list of blocks used for testing. + blocks: Vec, + /// The babe epoch used during testing. + babe_epoch: BabeEpoch, + /// The pre-generated state. + generated_state: GeneratedState, + /// The test authorities + test_authorities: TestAuthorities, + /// Last approved block number. + last_approved_block: Arc, + /// Total sent messages from peers to node + total_sent_messages_to_node: Arc, + /// Total sent messages from test node to other peers + total_sent_messages_from_node: Arc, + /// Total unique sent messages. + total_unique_messages: Arc, + /// Approval voting metrics. + approval_voting_metrics: ApprovalVotingMetrics, + /// The delta ticks from the tick the messages were generated to the the time we start this + /// message. + delta_tick_from_generated: Arc, +} + +impl ApprovalTestState { + /// Build a new `ApprovalTestState` object out of the configurations passed when the benchmark + /// was tested. + fn new( + configuration: &TestConfiguration, + options: ApprovalsOptions, + dependencies: &TestEnvironmentDependencies, + ) -> Self { + let test_authorities = configuration.generate_authorities(); + let start = Instant::now(); + + let messages_path = PeerMessagesGenerator::generate_messages_if_needed( + configuration, + &test_authorities, + &options, + &dependencies.task_manager.spawn_handle(), + ); + + let mut messages_file = + fs::OpenOptions::new().read(true).open(messages_path.as_path()).unwrap(); + let mut messages_bytes = Vec::::with_capacity(2000000); + + messages_file + .read_to_end(&mut messages_bytes) + .expect("Could not initialize list of messages"); + let generated_state: GeneratedState = + Decode::decode(&mut messages_bytes.as_slice()).expect("Could not decode messages"); + + gum::info!( + "It took {:?} ms to load {:?} unique messages", + start.elapsed().as_millis(), + generated_state.all_messages.as_ref().map(|val| val.len()).unwrap_or_default() + ); + + let babe_epoch = + generate_babe_epoch(generated_state.initial_slot, test_authorities.clone()); + let blocks = Self::generate_blocks_information( + configuration, + &babe_epoch, + generated_state.initial_slot, + ); + + let state = ApprovalTestState { + blocks, + babe_epoch: babe_epoch.clone(), + generated_state, + test_authorities, + last_approved_block: Arc::new(AtomicU32::new(0)), + total_sent_messages_to_node: Arc::new(AtomicU64::new(0)), + total_sent_messages_from_node: Arc::new(AtomicU64::new(0)), + total_unique_messages: Arc::new(AtomicU64::new(0)), + options, + approval_voting_metrics: ApprovalVotingMetrics::try_register(&dependencies.registry) + .unwrap(), + delta_tick_from_generated: Arc::new(AtomicU64::new(630720000)), + configuration: configuration.clone(), + }; + + gum::info!("Built testing state"); + + state + } + + /// Generates the blocks and the information about the blocks that will be used + /// to drive this test. + fn generate_blocks_information( + configuration: &TestConfiguration, + babe_epoch: &BabeEpoch, + initial_slot: Slot, + ) -> Vec { + let mut per_block_heads: Vec = Vec::new(); + let mut prev_candidates = 0; + for block_number in 1..=configuration.num_blocks { + let block_hash = Hash::repeat_byte(block_number as u8); + let parent_hash = + per_block_heads.last().map(|val| val.hash).unwrap_or(Hash::repeat_byte(0xde)); + let slot_for_block = initial_slot + (block_number as u64 - 1); + + let header = make_header(parent_hash, slot_for_block, block_number as u32); + + let unsafe_vrf = approval::v1::babe_unsafe_vrf_info(&header) + .expect("Can not continue without vrf generator"); + let relay_vrf_story = unsafe_vrf + .compute_randomness( + &babe_epoch.authorities, + &babe_epoch.randomness, + babe_epoch.epoch_index, + ) + .expect("Can not continue without vrf story"); + let block_info = BlockTestData { + slot: slot_for_block, + block_number: block_number as BlockNumber, + hash: block_hash, + header, + candidates: helpers::make_candidates( + block_hash, + block_number as BlockNumber, + configuration.n_cores as u32, + configuration.n_cores as u32, + ), + relay_vrf_story, + approved: Arc::new(AtomicBool::new(false)), + total_candidates_before: prev_candidates, + votes: Arc::new( + (0..configuration.n_validators) + .map(|_| { + (0..configuration.n_cores).map(|_| AtomicBool::new(false)).collect_vec() + }) + .collect_vec(), + ), + }; + prev_candidates += block_info.candidates.len() as u64; + per_block_heads.push(block_info) + } + per_block_heads + } + + /// Starts the generation of messages(Assignments & Approvals) needed for approving blocks. + async fn start_message_production( + &mut self, + network_emulator: &NetworkEmulatorHandle, + overseer_handle: OverseerHandleReal, + env: &TestEnvironment, + registry: Registry, + ) -> oneshot::Receiver<()> { + gum::info!(target: LOG_TARGET, "Start assignments/approvals production"); + + let (producer_tx, producer_rx) = oneshot::channel(); + let peer_message_source = PeerMessageProducer { + network: network_emulator.clone(), + overseer_handle: overseer_handle.clone(), + state: self.clone(), + options: self.options.clone(), + notify_done: producer_tx, + registry, + }; + + peer_message_source + .produce_messages(env, self.generated_state.all_messages.take().unwrap()); + producer_rx + } + + // Generates a ChainApiState used for driving MockChainApi + fn build_chain_api_state(&self) -> ChainApiState { + ChainApiState { + block_headers: self + .blocks + .iter() + .map(|block| (block.hash, block.header.clone())) + .collect(), + } + } + + // Builds a map with the list of candidate events per-block. + fn candidate_events_by_block(&self) -> HashMap> { + self.blocks.iter().map(|block| (block.hash, block.candidates.clone())).collect() + } + + // Builds a map with the list of candidate hashes per-block. + fn candidate_hashes_by_block(&self) -> HashMap> { + self.blocks + .iter() + .map(|block| { + ( + block.hash, + block + .candidates + .iter() + .map(|candidate_event| match candidate_event { + CandidateEvent::CandidateBacked(_, _, _, _) => todo!(), + CandidateEvent::CandidateIncluded(receipt, _, _, _) => receipt.clone(), + CandidateEvent::CandidateTimedOut(_, _, _) => todo!(), + }) + .collect_vec(), + ) + }) + .collect() + } +} + +impl ApprovalTestState { + /// Returns test data for the given hash + fn get_info_by_hash(&self, requested_hash: Hash) -> &BlockTestData { + self.blocks + .iter() + .find(|block| block.hash == requested_hash) + .expect("Mocks should not use unknown hashes") + } + + /// Returns test data for the given slot + fn get_info_by_slot(&self, slot: Slot) -> Option<&BlockTestData> { + self.blocks.iter().find(|block| block.slot == slot) + } +} + +impl HandleNetworkMessage for ApprovalTestState { + fn handle( + &self, + _message: crate::network::NetworkMessage, + _node_sender: &mut futures::channel::mpsc::UnboundedSender, + ) -> Option { + self.total_sent_messages_from_node + .as_ref() + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); + None + } +} + +/// A generator of messages coming from a given Peer/Validator +struct PeerMessageProducer { + /// The state state used to know what messages to generate. + state: ApprovalTestState, + /// Configuration options, passed at the beginning of the test. + options: ApprovalsOptions, + /// A reference to the network emulator + network: NetworkEmulatorHandle, + /// A handle to the overseer, used for sending messages to the node + /// under test. + overseer_handle: OverseerHandleReal, + /// Channel for producer to notify main loop it finished sending + /// all messages and they have been processed. + notify_done: oneshot::Sender<()>, + /// The metrics registry. + registry: Registry, +} + +impl PeerMessageProducer { + /// Generates messages by spawning a blocking task in the background which begins creating + /// the assignments/approvals and peer view changes at the begining of each block. + fn produce_messages( + mut self, + env: &TestEnvironment, + all_messages: Vec, + ) { + env.spawn_blocking("produce-messages", async move { + let mut initialized_blocks = HashSet::new(); + let mut per_candidate_data: HashMap<(Hash, CandidateIndex), CandidateTestData> = + self.initialize_candidates_test_data(); + let mut skipped_messages: Vec = Vec::new(); + let mut re_process_skipped = false; + + let system_clock = + PastSystemClock::new(SystemClock {}, self.state.delta_tick_from_generated.clone()); + let mut all_messages = all_messages.into_iter().peekable(); + + while all_messages.peek().is_some() { + let current_slot = + tick_to_slot_number(SLOT_DURATION_MILLIS, system_clock.tick_now()); + let block_to_initialize = self + .state + .blocks + .iter() + .filter(|block_info| { + block_info.slot <= current_slot && + !initialized_blocks.contains(&block_info.hash) + }) + .cloned() + .collect_vec(); + for block_info in block_to_initialize { + if !TestEnvironment::metric_lower_than( + &self.registry, + "polkadot_parachain_imported_candidates_total", + (block_info.total_candidates_before + block_info.candidates.len() as u64 - + 1) as f64, + ) { + initialized_blocks.insert(block_info.hash); + self.initialize_block(&block_info).await; + } + } + + let mut maybe_need_skip = if re_process_skipped { + skipped_messages.clone().into_iter().peekable() + } else { + vec![].into_iter().peekable() + }; + + let progressing_iterator = if !re_process_skipped { + &mut all_messages + } else { + re_process_skipped = false; + skipped_messages.clear(); + &mut maybe_need_skip + }; + + while progressing_iterator + .peek() + .map(|bundle| { + self.time_to_process_message( + bundle, + current_slot, + &initialized_blocks, + &system_clock, + &per_candidate_data, + ) + }) + .unwrap_or_default() + { + let bundle = progressing_iterator.next().unwrap(); + re_process_skipped = self.process_message( + bundle, + &mut per_candidate_data, + &mut skipped_messages, + ) || re_process_skipped; + } + // Sleep, so that we don't busy wait in this loop when don't have anything to send. + sleep(Duration::from_millis(50)).await; + } + + gum::info!( + "All messages sent max_tranche {:?} last_tranche_with_no_show {:?}", + per_candidate_data.values().map(|data| data.max_tranche).max(), + per_candidate_data.values().map(|data| data.last_tranche_with_no_show).max() + ); + sleep(Duration::from_secs(6)).await; + // Send an empty GetApprovalSignatures as the last message + // so when the approval-distribution answered to it, we know it doesn't have anything + // else to process. + let (tx, rx) = oneshot::channel(); + let msg = ApprovalDistributionMessage::GetApprovalSignatures(HashSet::new(), tx); + self.send_overseer_message( + AllMessages::ApprovalDistribution(msg), + ValidatorIndex(0), + None, + ) + .await; + rx.await.expect("Failed to get signatures"); + self.notify_done.send(()).expect("Failed to notify main loop"); + gum::info!("All messages processed "); + }); + } + + // Processes a single message bundle and queue the messages to be sent by the peers that would + // send the message in our simulation. + pub fn process_message( + &mut self, + bundle: test_message::MessagesBundle, + per_candidate_data: &mut HashMap<(Hash, CandidateIndex), CandidateTestData>, + skipped_messages: &mut Vec, + ) -> bool { + let mut reprocess_skipped = false; + let block_info = self + .state + .get_info_by_hash(bundle.assignments.first().unwrap().block_hash) + .clone(); + + if bundle.should_send(per_candidate_data, &self.options) { + bundle.record_sent_assignment(per_candidate_data); + + let assignments = bundle.assignments.clone(); + + for message in bundle.assignments.into_iter().chain(bundle.approvals.into_iter()) { + if message.no_show_if_required(&assignments, per_candidate_data) { + reprocess_skipped = true; + continue; + } else { + message.record_vote(&block_info); + } + self.state + .total_unique_messages + .as_ref() + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); + for (peer, messages) in + message.clone().split_by_peer_id(&self.state.test_authorities) + { + for message in messages { + self.state + .total_sent_messages_to_node + .as_ref() + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); + self.queue_message_from_peer(message, peer.0) + } + } + } + } else if !block_info.approved.load(std::sync::atomic::Ordering::SeqCst) && + self.options.num_no_shows_per_candidate > 0 + { + skipped_messages.push(bundle); + } + reprocess_skipped + } + + // Tells if it is the time to process a message. + pub fn time_to_process_message( + &self, + bundle: &MessagesBundle, + current_slot: Slot, + initialized_blocks: &HashSet, + system_clock: &PastSystemClock, + per_candidate_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>, + ) -> bool { + let block_info = + self.state.get_info_by_hash(bundle.assignments.first().unwrap().block_hash); + let tranche_now = system_clock.tranche_now(SLOT_DURATION_MILLIS, block_info.slot); + + Self::is_past_tranche( + bundle, + tranche_now, + current_slot, + block_info, + initialized_blocks.contains(&block_info.hash), + ) || !bundle.should_send(per_candidate_data, &self.options) + } + + // Tells if the tranche where the bundle should be sent has passed. + pub fn is_past_tranche( + bundle: &MessagesBundle, + tranche_now: u32, + current_slot: Slot, + block_info: &BlockTestData, + block_initialized: bool, + ) -> bool { + bundle.tranche_to_send() <= tranche_now && + current_slot >= block_info.slot && + block_initialized + } + + // Queue message to be sent by validator `sent_by` + fn queue_message_from_peer(&mut self, message: TestMessageInfo, sent_by: ValidatorIndex) { + let peer_authority_id = self + .state + .test_authorities + .validator_authority_id + .get(sent_by.0 as usize) + .expect("We can't handle unknown peers") + .clone(); + + self.network + .send_message_from_peer( + &peer_authority_id, + protocol_v3::ValidationProtocol::ApprovalDistribution(message.msg).into(), + ) + .unwrap_or_else(|_| panic!("Network should be up and running {:?}", sent_by)); + } + + // Queues a message to be sent by the peer identified by the `sent_by` value. + async fn send_overseer_message( + &mut self, + message: AllMessages, + _sent_by: ValidatorIndex, + _latency: Option, + ) { + self.overseer_handle + .send_msg(message, LOG_TARGET) + .timeout(MAX_TIME_OF_FLIGHT) + .await + .unwrap_or_else(|| { + panic!("{} ms maximum time of flight breached", MAX_TIME_OF_FLIGHT.as_millis()) + }); + } + + // Sends the messages needed by approval-distribution and approval-voting for processing a + // message. E.g: PeerViewChange. + async fn initialize_block(&mut self, block_info: &BlockTestData) { + gum::info!("Initialize block {:?}", block_info.hash); + let (tx, rx) = oneshot::channel(); + self.overseer_handle.wait_for_activation(block_info.hash, tx).await; + + rx.await + .expect("We should not fail waiting for block to be activated") + .expect("We should not fail waiting for block to be activated"); + + for validator in 1..self.state.test_authorities.validator_authority_id.len() as u32 { + let peer_id = self.state.test_authorities.peer_ids.get(validator as usize).unwrap(); + let validator = ValidatorIndex(validator); + let view_update = generate_peer_view_change_for(block_info.hash, *peer_id); + + self.send_overseer_message(view_update, validator, None).await; + } + } + + // Initializes the candidates test data. This is used for bookeeping if more assignments and + // approvals would be needed. + fn initialize_candidates_test_data( + &self, + ) -> HashMap<(Hash, CandidateIndex), CandidateTestData> { + let mut per_candidate_data: HashMap<(Hash, CandidateIndex), CandidateTestData> = + HashMap::new(); + for block_info in self.state.blocks.iter() { + for (candidate_index, _) in block_info.candidates.iter().enumerate() { + per_candidate_data.insert( + (block_info.hash, candidate_index as CandidateIndex), + CandidateTestData { + max_no_shows: self.options.num_no_shows_per_candidate, + last_tranche_with_no_show: 0, + sent_assignment: 0, + num_no_shows: 0, + max_tranche: 0, + needed_approvals: self.state.configuration.needed_approvals as u32, + }, + ); + } + } + per_candidate_data + } +} + +/// Helper function to build an overseer with the real implementation for `ApprovalDistribution` and +/// `ApprovalVoting` subystems and mock subsytems for all others. +fn build_overseer( + state: &ApprovalTestState, + network: &NetworkEmulatorHandle, + config: &TestConfiguration, + dependencies: &TestEnvironmentDependencies, + network_interface: &NetworkInterface, + network_receiver: NetworkInterfaceReceiver, +) -> (Overseer, AlwaysSupportsParachains>, OverseerHandleReal) { + let overseer_connector = OverseerConnector::with_event_capacity(6400000); + + let spawn_task_handle = dependencies.task_manager.spawn_handle(); + + let db = kvdb_memorydb::create(NUM_COLUMNS); + let db: polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter = + polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(db, &[]); + let keystore = LocalKeystore::in_memory(); + + let system_clock = + PastSystemClock::new(SystemClock {}, state.delta_tick_from_generated.clone()); + let approval_voting = ApprovalVotingSubsystem::with_config_and_clock( + TEST_CONFIG, + Arc::new(db), + Arc::new(keystore), + Box::new(TestSyncOracle {}), + state.approval_voting_metrics.clone(), + Box::new(system_clock.clone()), + ); + + let approval_distribution = + ApprovalDistribution::new(Metrics::register(Some(&dependencies.registry)).unwrap()); + 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( + config.clone(), + state.test_authorities.clone(), + state.candidate_hashes_by_block(), + state.candidate_events_by_block(), + Some(state.babe_epoch.clone()), + 1, + ); + let mock_tx_bridge = MockNetworkBridgeTx::new( + network.clone(), + network_interface.subsystem_sender(), + state.test_authorities.clone(), + ); + let mock_rx_bridge = MockNetworkBridgeRx::new(network_receiver, None); + let overseer_metrics = OverseerMetrics::try_register(&dependencies.registry).unwrap(); + let dummy = dummy_builder!(spawn_task_handle, overseer_metrics) + .replace_approval_distribution(|_| approval_distribution) + .replace_approval_voting(|_| approval_voting) + .replace_chain_api(|_| mock_chain_api) + .replace_chain_selection(|_| mock_chain_selection) + .replace_runtime_api(|_| mock_runtime_api) + .replace_network_bridge_tx(|_| mock_tx_bridge) + .replace_network_bridge_rx(|_| mock_rx_bridge); + + let (overseer, raw_handle) = + dummy.build_with_connector(overseer_connector).expect("Should not fail"); + + let overseer_handle = OverseerHandleReal::new(raw_handle); + (overseer, overseer_handle) +} + +/// Takes a test configuration and uses it to creates the `TestEnvironment`. +pub fn prepare_test( + config: TestConfiguration, + options: ApprovalsOptions, + with_prometheus_endpoint: bool, +) -> (TestEnvironment, ApprovalTestState) { + prepare_test_inner( + config, + TestEnvironmentDependencies::default(), + options, + with_prometheus_endpoint, + ) +} + +/// Build the test environment for an Approval benchmark. +fn prepare_test_inner( + config: TestConfiguration, + dependencies: TestEnvironmentDependencies, + options: ApprovalsOptions, + with_prometheus_endpoint: bool, +) -> (TestEnvironment, ApprovalTestState) { + gum::info!("Prepare test state"); + let state = ApprovalTestState::new(&config, options, &dependencies); + + gum::info!("Build network emulator"); + + let (network, network_interface, network_receiver) = + new_network(&config, &dependencies, &state.test_authorities, vec![Arc::new(state.clone())]); + + gum::info!("Build overseer"); + + let (overseer, overseer_handle) = build_overseer( + &state, + &network, + &config, + &dependencies, + &network_interface, + network_receiver, + ); + + ( + TestEnvironment::new( + dependencies, + config, + network, + overseer, + overseer_handle, + state.test_authorities.clone(), + with_prometheus_endpoint, + ), + state, + ) +} + +pub async fn bench_approvals( + benchmark_name: &str, + env: &mut TestEnvironment, + mut state: ApprovalTestState, +) -> BenchmarkUsage { + let producer_rx = state + .start_message_production( + env.network(), + env.overseer_handle().clone(), + env, + env.registry().clone(), + ) + .await; + bench_approvals_run(benchmark_name, env, state, producer_rx).await +} + +/// Runs the approval benchmark. +pub async fn bench_approvals_run( + benchmark_name: &str, + env: &mut TestEnvironment, + state: ApprovalTestState, + producer_rx: oneshot::Receiver<()>, +) -> BenchmarkUsage { + let config = env.config().clone(); + + env.metrics().set_n_validators(config.n_validators); + env.metrics().set_n_cores(config.n_cores); + + // First create the initialization messages that make sure that then node under + // tests receives notifications about the topology used and the connected peers. + let mut initialization_messages = env.network().generate_peer_connected(); + initialization_messages.extend(generate_new_session_topology( + &state.test_authorities, + ValidatorIndex(NODE_UNDER_TEST), + )); + for message in initialization_messages { + env.send_message(message).await; + } + + let start_marker = Instant::now(); + let real_clock = SystemClock {}; + state.delta_tick_from_generated.store( + real_clock.tick_now() - + slot_number_to_tick(SLOT_DURATION_MILLIS, state.generated_state.initial_slot), + std::sync::atomic::Ordering::SeqCst, + ); + let system_clock = PastSystemClock::new(real_clock, state.delta_tick_from_generated.clone()); + + for block_num in 0..env.config().num_blocks { + let mut current_slot = tick_to_slot_number(SLOT_DURATION_MILLIS, system_clock.tick_now()); + + // Wait untill the time arrieves at the first slot under test. + while current_slot < state.generated_state.initial_slot { + sleep(Duration::from_millis(5)).await; + current_slot = tick_to_slot_number(SLOT_DURATION_MILLIS, system_clock.tick_now()); + } + + gum::info!(target: LOG_TARGET, "Current block {}/{}", block_num + 1, env.config().num_blocks); + env.metrics().set_current_block(block_num); + let block_start_ts = Instant::now(); + + if let Some(block_info) = state.get_info_by_slot(current_slot) { + env.import_block(new_block_import_info(block_info.hash, block_info.block_number)) + .await; + } + + let block_time = Instant::now().sub(block_start_ts).as_millis() as u64; + env.metrics().set_block_time(block_time); + gum::info!("Block time {}", format!("{:?}ms", block_time).cyan()); + + system_clock + .wait(slot_number_to_tick(SLOT_DURATION_MILLIS, current_slot + 1)) + .await; + } + + // Wait for all blocks to be approved before exiting. + // This is an invariant of the benchmark, if this does not happen something went teribbly wrong. + while state.last_approved_block.load(std::sync::atomic::Ordering::SeqCst) < + env.config().num_blocks as u32 + { + gum::info!( + "Waiting for all blocks to be approved current approved {:} num_sent {:} num_unique {:}", + state.last_approved_block.load(std::sync::atomic::Ordering::SeqCst), + state.total_sent_messages_to_node.load(std::sync::atomic::Ordering::SeqCst), + state.total_unique_messages.load(std::sync::atomic::Ordering::SeqCst) + ); + tokio::time::sleep(Duration::from_secs(6)).await; + } + + gum::info!("Awaiting producer to signal done"); + + producer_rx.await.expect("Failed to receive done from message producer"); + + gum::info!("Awaiting polkadot_parachain_subsystem_bounded_received to tells us the messages have been processed"); + let at_least_messages = + state.total_sent_messages_to_node.load(std::sync::atomic::Ordering::SeqCst) as usize; + env.wait_until_metric( + "polkadot_parachain_subsystem_bounded_received", + Some(("subsystem_name", "approval-distribution-subsystem")), + |value| { + gum::info!(target: LOG_TARGET, ?value, ?at_least_messages, "Waiting metric"); + value >= at_least_messages as f64 + }, + ) + .await; + gum::info!("Requesting approval votes ms"); + + for info in &state.blocks { + for (index, candidates) in info.candidates.iter().enumerate() { + match candidates { + CandidateEvent::CandidateBacked(_, _, _, _) => todo!(), + CandidateEvent::CandidateIncluded(receipt_fetch, _head, _, _) => { + let (tx, rx) = oneshot::channel(); + + let msg = ApprovalVotingMessage::GetApprovalSignaturesForCandidate( + receipt_fetch.hash(), + tx, + ); + env.send_message(AllMessages::ApprovalVoting(msg)).await; + + let result = rx.await.unwrap(); + + for (validator, _) in result.iter() { + info.votes + .get(validator.0 as usize) + .unwrap() + .get(index) + .unwrap() + .store(false, std::sync::atomic::Ordering::SeqCst); + } + }, + + CandidateEvent::CandidateTimedOut(_, _, _) => todo!(), + }; + } + } + + gum::info!("Awaiting polkadot_parachain_subsystem_bounded_received to tells us the messages have been processed"); + let at_least_messages = + state.total_sent_messages_to_node.load(std::sync::atomic::Ordering::SeqCst) as usize; + env.wait_until_metric( + "polkadot_parachain_subsystem_bounded_received", + Some(("subsystem_name", "approval-distribution-subsystem")), + |value| { + gum::info!(target: LOG_TARGET, ?value, ?at_least_messages, "Waiting metric"); + value >= at_least_messages as f64 + }, + ) + .await; + + for state in &state.blocks { + for (validator, votes) in state + .votes + .as_ref() + .iter() + .enumerate() + .filter(|(validator, _)| *validator != NODE_UNDER_TEST as usize) + { + for (index, candidate) in votes.iter().enumerate() { + assert_eq!( + ( + validator, + index, + candidate.load(std::sync::atomic::Ordering::SeqCst), + state.hash + ), + (validator, index, false, state.hash) + ); + } + } + } + + env.stop().await; + + let duration: u128 = start_marker.elapsed().as_millis(); + gum::info!( + "All blocks processed in {} total_sent_messages_to_node {} total_sent_messages_from_node {} num_unique_messages {}", + format!("{:?}ms", duration).cyan(), + state.total_sent_messages_to_node.load(std::sync::atomic::Ordering::SeqCst), + state.total_sent_messages_from_node.load(std::sync::atomic::Ordering::SeqCst), + state.total_unique_messages.load(std::sync::atomic::Ordering::SeqCst) + ); + + env.collect_resource_usage(benchmark_name, &["approval-distribution", "approval-voting"]) +} diff --git a/polkadot/node/subsystem-bench/src/lib/approval/test_message.rs b/polkadot/node/subsystem-bench/src/lib/approval/test_message.rs new file mode 100644 index 0000000000000000000000000000000000000000..63e383509be9ff6da6e1d5dd61baebc99c574bc4 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/approval/test_message.rs @@ -0,0 +1,305 @@ +// 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 crate::{ + approval::{ApprovalsOptions, BlockTestData, CandidateTestData}, + configuration::TestAuthorities, +}; +use itertools::Itertools; +use parity_scale_codec::{Decode, Encode}; +use polkadot_node_network_protocol::v3 as protocol_v3; +use polkadot_primitives::{CandidateIndex, Hash, ValidatorIndex}; +use sc_network::PeerId; +use std::collections::{HashMap, HashSet}; + +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] +pub struct TestMessageInfo { + /// The actual message + pub msg: protocol_v3::ApprovalDistributionMessage, + /// The list of peers that would sends this message in a real topology. + /// It includes both the peers that would send the message because of the topology + /// or because of randomly chosing so. + pub sent_by: Vec, + /// The tranche at which this message should be sent. + pub tranche: u32, + /// The block hash this message refers to. + pub block_hash: Hash, +} + +impl std::hash::Hash for TestMessageInfo { + fn hash(&self, state: &mut H) { + match &self.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => { + for (assignment, candidates) in assignments { + (assignment.block_hash, assignment.validator).hash(state); + candidates.hash(state); + } + }, + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => { + for approval in approvals { + (approval.block_hash, approval.validator).hash(state); + approval.candidate_indices.hash(state); + } + }, + }; + } +} + +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] +/// A list of messages that depend of each-other, approvals cover one of the assignments and +/// vice-versa. +pub struct MessagesBundle { + pub assignments: Vec, + pub approvals: Vec, +} + +impl MessagesBundle { + /// The tranche when this bundle can be sent correctly, so no assignments or approvals will be + /// from the future. + pub fn tranche_to_send(&self) -> u32 { + self.assignments + .iter() + .chain(self.approvals.iter()) + .max_by(|a, b| a.tranche.cmp(&b.tranche)) + .unwrap() + .tranche + } + + /// The min tranche in the bundle. + pub fn min_tranche(&self) -> u32 { + self.assignments + .iter() + .chain(self.approvals.iter()) + .min_by(|a, b| a.tranche.cmp(&b.tranche)) + .unwrap() + .tranche + } + + /// Tells if the bundle is needed for sending. + /// We either send it because we need more assignments and approvals to approve the candidates + /// or because we configured the test to send messages untill a given tranche. + pub fn should_send( + &self, + candidates_test_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>, + options: &ApprovalsOptions, + ) -> bool { + self.needed_for_approval(candidates_test_data) || + (!options.stop_when_approved && + self.min_tranche() <= options.last_considered_tranche) + } + + /// Tells if the bundle is needed because we need more messages to approve the candidates. + pub fn needed_for_approval( + &self, + candidates_test_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>, + ) -> bool { + self.assignments + .iter() + .any(|message| message.needed_for_approval(candidates_test_data)) + } + + /// Mark the assignments in the bundle as sent. + pub fn record_sent_assignment( + &self, + candidates_test_data: &mut HashMap<(Hash, CandidateIndex), CandidateTestData>, + ) { + self.assignments + .iter() + .for_each(|assignment| assignment.record_sent_assignment(candidates_test_data)); + } +} + +impl TestMessageInfo { + /// Tells if the message is an approval. + fn is_approval(&self) -> bool { + match self.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(_) => false, + protocol_v3::ApprovalDistributionMessage::Approvals(_) => true, + } + } + + /// Records an approval. + /// We use this to check after all messages have been processed that we didn't loose any + /// message. + pub fn record_vote(&self, state: &BlockTestData) { + if self.is_approval() { + match &self.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(_) => todo!(), + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => + for approval in approvals { + for candidate_index in approval.candidate_indices.iter_ones() { + state + .votes + .get(approval.validator.0 as usize) + .unwrap() + .get(candidate_index) + .unwrap() + .store(true, std::sync::atomic::Ordering::SeqCst); + } + }, + } + } + } + + /// Mark the assignments in the message as sent. + pub fn record_sent_assignment( + &self, + candidates_test_data: &mut HashMap<(Hash, CandidateIndex), CandidateTestData>, + ) { + match &self.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => { + for (assignment, candidate_indices) in assignments { + for candidate_index in candidate_indices.iter_ones() { + let candidate_test_data = candidates_test_data + .get_mut(&(assignment.block_hash, candidate_index as CandidateIndex)) + .unwrap(); + candidate_test_data.mark_sent_assignment(self.tranche) + } + } + }, + protocol_v3::ApprovalDistributionMessage::Approvals(_approvals) => todo!(), + } + } + + /// Returns a list of candidates indicies in this message + pub fn candidate_indices(&self) -> HashSet { + let mut unique_candidate_indicies = HashSet::new(); + match &self.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => + for (_assignment, candidate_indices) in assignments { + for candidate_index in candidate_indices.iter_ones() { + unique_candidate_indicies.insert(candidate_index); + } + }, + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => + for approval in approvals { + for candidate_index in approval.candidate_indices.iter_ones() { + unique_candidate_indicies.insert(candidate_index); + } + }, + } + unique_candidate_indicies + } + + /// Marks this message as no-shows if the number of configured no-shows is above the registered + /// no-shows. + /// Returns true if the message is a no-show. + pub fn no_show_if_required( + &self, + assignments: &[TestMessageInfo], + candidates_test_data: &mut HashMap<(Hash, CandidateIndex), CandidateTestData>, + ) -> bool { + let mut should_no_show = false; + if self.is_approval() { + let covered_candidates = assignments + .iter() + .map(|assignment| (assignment, assignment.candidate_indices())) + .collect_vec(); + + match &self.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(_) => todo!(), + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => { + assert_eq!(approvals.len(), 1); + + for approval in approvals { + should_no_show = should_no_show || + approval.candidate_indices.iter_ones().all(|candidate_index| { + let candidate_test_data = candidates_test_data + .get_mut(&( + approval.block_hash, + candidate_index as CandidateIndex, + )) + .unwrap(); + let assignment = covered_candidates + .iter() + .find(|(_assignment, candidates)| { + candidates.contains(&candidate_index) + }) + .unwrap(); + candidate_test_data.should_no_show(assignment.0.tranche) + }); + + if should_no_show { + for candidate_index in approval.candidate_indices.iter_ones() { + let candidate_test_data = candidates_test_data + .get_mut(&( + approval.block_hash, + candidate_index as CandidateIndex, + )) + .unwrap(); + let assignment = covered_candidates + .iter() + .find(|(_assignment, candidates)| { + candidates.contains(&candidate_index) + }) + .unwrap(); + candidate_test_data.record_no_show(assignment.0.tranche) + } + } + } + }, + } + } + should_no_show + } + + /// Tells if a message is needed for approval + pub fn needed_for_approval( + &self, + candidates_test_data: &HashMap<(Hash, CandidateIndex), CandidateTestData>, + ) -> bool { + match &self.msg { + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => + assignments.iter().any(|(assignment, candidate_indices)| { + candidate_indices.iter_ones().any(|candidate_index| { + candidates_test_data + .get(&(assignment.block_hash, candidate_index as CandidateIndex)) + .map(|data| data.should_send_tranche(self.tranche)) + .unwrap_or_default() + }) + }), + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) => + approvals.iter().any(|approval| { + approval.candidate_indices.iter_ones().any(|candidate_index| { + candidates_test_data + .get(&(approval.block_hash, candidate_index as CandidateIndex)) + .map(|data| data.should_send_tranche(self.tranche)) + .unwrap_or_default() + }) + }), + } + } + + /// Splits a message into multiple messages based on what peers should send this message. + /// It build a HashMap of messages that should be sent by each peer. + pub fn split_by_peer_id( + self, + authorities: &TestAuthorities, + ) -> HashMap<(ValidatorIndex, PeerId), Vec> { + let mut result: HashMap<(ValidatorIndex, PeerId), Vec> = HashMap::new(); + + for validator_index in &self.sent_by { + let peer = authorities.peer_ids.get(validator_index.0 as usize).unwrap(); + result.entry((*validator_index, *peer)).or_default().push(TestMessageInfo { + msg: self.msg.clone(), + sent_by: Default::default(), + tranche: self.tranche, + block_hash: self.block_hash, + }); + } + result + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/availability/av_store_helpers.rs b/polkadot/node/subsystem-bench/src/lib/availability/av_store_helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..3300def2235ee0ea90f466316f9bc5a7a965869c --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/availability/av_store_helpers.rs @@ -0,0 +1,42 @@ +// 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 crate::{environment::TestEnvironmentDependencies, mock::TestSyncOracle}; +use polkadot_node_core_av_store::{AvailabilityStoreSubsystem, Config}; +use polkadot_node_metrics::metrics::Metrics; +use polkadot_node_subsystem_util::database::Database; +use std::sync::Arc; + +mod columns { + pub const DATA: u32 = 0; + pub const META: u32 = 1; + pub const NUM_COLUMNS: u32 = 2; +} + +const TEST_CONFIG: Config = Config { col_data: columns::DATA, col_meta: columns::META }; + +pub fn new_av_store(dependencies: &TestEnvironmentDependencies) -> AvailabilityStoreSubsystem { + let metrics = Metrics::try_register(&dependencies.registry).unwrap(); + + AvailabilityStoreSubsystem::new(test_store(), TEST_CONFIG, Box::new(TestSyncOracle {}), metrics) +} + +fn test_store() -> Arc { + let db = kvdb_memorydb::create(columns::NUM_COLUMNS); + let db = + polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(db, &[columns::META]); + Arc::new(db) +} diff --git a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..f012a5a907edac40ea8a2b9d8c8ebd1a1dc4b7e1 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs @@ -0,0 +1,695 @@ +// 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 crate::{ + configuration::TestConfiguration, + dummy_builder, + environment::{TestEnvironment, TestEnvironmentDependencies, GENESIS_HASH}, + mock::{ + av_store::{self, MockAvailabilityStore}, + chain_api::{ChainApiState, MockChainApi}, + network_bridge::{self, MockNetworkBridgeRx, MockNetworkBridgeTx}, + runtime_api::{self, MockRuntimeApi}, + AlwaysSupportsParachains, + }, + network::new_network, + usage::BenchmarkUsage, +}; +use av_store::NetworkAvailabilityState; +use av_store_helpers::new_av_store; +use bitvec::bitvec; +use colored::Colorize; +use futures::{channel::oneshot, stream::FuturesUnordered, StreamExt}; +use itertools::Itertools; +use parity_scale_codec::Encode; +use polkadot_availability_bitfield_distribution::BitfieldDistribution; +use polkadot_availability_distribution::{ + AvailabilityDistributionSubsystem, IncomingRequestReceivers, +}; +use polkadot_availability_recovery::AvailabilityRecoverySubsystem; +use polkadot_node_core_av_store::AvailabilityStoreSubsystem; +use polkadot_node_metrics::metrics::Metrics; +use polkadot_node_network_protocol::{ + request_response::{v1::ChunkFetchingRequest, IncomingRequest, ReqProtocolNames}, + OurView, Versioned, VersionedValidationProtocol, +}; +use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV}; +use polkadot_node_subsystem::{ + messages::{AllMessages, AvailabilityRecoveryMessage}, + Overseer, OverseerConnector, SpawnGlue, +}; +use polkadot_node_subsystem_test_helpers::{ + derive_erasure_chunks_with_proofs_and_root, mock::new_block_import_info, +}; +use polkadot_node_subsystem_types::{ + messages::{AvailabilityStoreMessage, NetworkBridgeEvent}, + Span, +}; +use polkadot_overseer::{metrics::Metrics as OverseerMetrics, Handle as OverseerHandle}; +use polkadot_primitives::{ + AvailabilityBitfield, BlockNumber, CandidateHash, CandidateReceipt, GroupIndex, Hash, HeadData, + Header, PersistedValidationData, Signed, SigningContext, ValidatorIndex, +}; +use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; +use sc_network::{ + request_responses::{IncomingRequest as RawIncomingRequest, ProtocolConfig}, + PeerId, +}; +use sc_service::SpawnTaskHandle; +use serde::{Deserialize, Serialize}; +use sp_core::H256; +use std::{collections::HashMap, iter::Cycle, ops::Sub, sync::Arc, time::Instant}; + +mod av_store_helpers; + +const LOG_TARGET: &str = "subsystem-bench::availability"; + +#[derive(Debug, Clone, Serialize, Deserialize, clap::Parser)] +#[clap(rename_all = "kebab-case")] +#[allow(missing_docs)] +pub struct DataAvailabilityReadOptions { + #[clap(short, long, default_value_t = false)] + /// Turbo boost AD Read by fetching the full availability datafrom backers first. Saves CPU as + /// we don't need to re-construct from chunks. Tipically this is only faster if nodes have + /// enough bandwidth. + pub fetch_from_backers: bool, +} + +pub enum TestDataAvailability { + Read(DataAvailabilityReadOptions), + Write, +} + +fn build_overseer_for_availability_read( + spawn_task_handle: SpawnTaskHandle, + runtime_api: MockRuntimeApi, + av_store: MockAvailabilityStore, + network_bridge: (MockNetworkBridgeTx, MockNetworkBridgeRx), + availability_recovery: AvailabilityRecoverySubsystem, + dependencies: &TestEnvironmentDependencies, +) -> (Overseer, AlwaysSupportsParachains>, OverseerHandle) { + let overseer_connector = OverseerConnector::with_event_capacity(64000); + let overseer_metrics = OverseerMetrics::try_register(&dependencies.registry).unwrap(); + + let dummy = dummy_builder!(spawn_task_handle, overseer_metrics); + let builder = dummy + .replace_runtime_api(|_| runtime_api) + .replace_availability_store(|_| av_store) + .replace_network_bridge_tx(|_| network_bridge.0) + .replace_network_bridge_rx(|_| network_bridge.1) + .replace_availability_recovery(|_| availability_recovery); + + let (overseer, raw_handle) = + builder.build_with_connector(overseer_connector).expect("Should not fail"); + + (overseer, OverseerHandle::new(raw_handle)) +} + +#[allow(clippy::too_many_arguments)] +fn build_overseer_for_availability_write( + spawn_task_handle: SpawnTaskHandle, + runtime_api: MockRuntimeApi, + network_bridge: (MockNetworkBridgeTx, MockNetworkBridgeRx), + availability_distribution: AvailabilityDistributionSubsystem, + chain_api: MockChainApi, + availability_store: AvailabilityStoreSubsystem, + bitfield_distribution: BitfieldDistribution, + dependencies: &TestEnvironmentDependencies, +) -> (Overseer, AlwaysSupportsParachains>, OverseerHandle) { + let overseer_connector = OverseerConnector::with_event_capacity(64000); + let overseer_metrics = OverseerMetrics::try_register(&dependencies.registry).unwrap(); + + let dummy = dummy_builder!(spawn_task_handle, overseer_metrics); + let builder = dummy + .replace_runtime_api(|_| runtime_api) + .replace_availability_store(|_| availability_store) + .replace_network_bridge_tx(|_| network_bridge.0) + .replace_network_bridge_rx(|_| network_bridge.1) + .replace_chain_api(|_| chain_api) + .replace_bitfield_distribution(|_| bitfield_distribution) + // This is needed to test own chunk recovery for `n_cores`. + .replace_availability_distribution(|_| availability_distribution); + + let (overseer, raw_handle) = + builder.build_with_connector(overseer_connector).expect("Should not fail"); + + (overseer, OverseerHandle::new(raw_handle)) +} + +/// Takes a test configuration and uses it to create the `TestEnvironment`. +pub fn prepare_test( + config: TestConfiguration, + state: &mut TestState, + mode: TestDataAvailability, + with_prometheus_endpoint: bool, +) -> (TestEnvironment, Vec) { + prepare_test_inner( + config, + state, + mode, + TestEnvironmentDependencies::default(), + with_prometheus_endpoint, + ) +} + +fn prepare_test_inner( + config: TestConfiguration, + state: &mut TestState, + mode: TestDataAvailability, + dependencies: TestEnvironmentDependencies, + with_prometheus_endpoint: bool, +) -> (TestEnvironment, Vec) { + // Generate test authorities. + let test_authorities = config.generate_authorities(); + + let mut candidate_hashes: HashMap> = HashMap::new(); + + // Prepare per block candidates. + // Genesis block is always finalized, so we start at 1. + for block_num in 1..=config.num_blocks { + for _ in 0..config.n_cores { + candidate_hashes + .entry(Hash::repeat_byte(block_num as u8)) + .or_default() + .push(state.next_candidate().expect("Cycle iterator")) + } + + // First candidate is our backed candidate. + state.backed_candidates.push( + candidate_hashes + .get(&Hash::repeat_byte(block_num as u8)) + .expect("just inserted above") + .first() + .expect("just inserted above") + .clone(), + ); + } + + let runtime_api = runtime_api::MockRuntimeApi::new( + config.clone(), + test_authorities.clone(), + candidate_hashes, + Default::default(), + Default::default(), + 0, + ); + + let availability_state = NetworkAvailabilityState { + candidate_hashes: state.candidate_hashes.clone(), + available_data: state.available_data.clone(), + chunks: state.chunks.clone(), + }; + + let mut req_cfgs = Vec::new(); + + let (collation_req_receiver, collation_req_cfg) = + IncomingRequest::get_config_receiver(&ReqProtocolNames::new(GENESIS_HASH, None)); + req_cfgs.push(collation_req_cfg); + + let (pov_req_receiver, pov_req_cfg) = + IncomingRequest::get_config_receiver(&ReqProtocolNames::new(GENESIS_HASH, None)); + + let (chunk_req_receiver, chunk_req_cfg) = + IncomingRequest::get_config_receiver(&ReqProtocolNames::new(GENESIS_HASH, None)); + req_cfgs.push(pov_req_cfg); + + let (network, network_interface, network_receiver) = + new_network(&config, &dependencies, &test_authorities, vec![Arc::new(availability_state)]); + + let network_bridge_tx = network_bridge::MockNetworkBridgeTx::new( + network.clone(), + network_interface.subsystem_sender(), + test_authorities.clone(), + ); + + let network_bridge_rx = + network_bridge::MockNetworkBridgeRx::new(network_receiver, Some(chunk_req_cfg.clone())); + + let (overseer, overseer_handle) = match &mode { + TestDataAvailability::Read(options) => { + let use_fast_path = options.fetch_from_backers; + + let subsystem = if use_fast_path { + AvailabilityRecoverySubsystem::with_fast_path( + collation_req_receiver, + Metrics::try_register(&dependencies.registry).unwrap(), + ) + } else { + AvailabilityRecoverySubsystem::with_chunks_only( + collation_req_receiver, + Metrics::try_register(&dependencies.registry).unwrap(), + ) + }; + + // Use a mocked av-store. + let av_store = av_store::MockAvailabilityStore::new( + state.chunks.clone(), + state.candidate_hashes.clone(), + ); + + build_overseer_for_availability_read( + dependencies.task_manager.spawn_handle(), + runtime_api, + av_store, + (network_bridge_tx, network_bridge_rx), + subsystem, + &dependencies, + ) + }, + TestDataAvailability::Write => { + let availability_distribution = AvailabilityDistributionSubsystem::new( + test_authorities.keyring.keystore(), + IncomingRequestReceivers { pov_req_receiver, chunk_req_receiver }, + Metrics::try_register(&dependencies.registry).unwrap(), + ); + + let block_headers = (1..=config.num_blocks) + .map(|block_number| { + ( + Hash::repeat_byte(block_number as u8), + Header { + digest: Default::default(), + number: block_number as BlockNumber, + parent_hash: Default::default(), + extrinsics_root: Default::default(), + state_root: Default::default(), + }, + ) + }) + .collect::>(); + + let chain_api_state = ChainApiState { block_headers }; + let chain_api = MockChainApi::new(chain_api_state); + let bitfield_distribution = + BitfieldDistribution::new(Metrics::try_register(&dependencies.registry).unwrap()); + build_overseer_for_availability_write( + dependencies.task_manager.spawn_handle(), + runtime_api, + (network_bridge_tx, network_bridge_rx), + availability_distribution, + chain_api, + new_av_store(&dependencies), + bitfield_distribution, + &dependencies, + ) + }, + }; + + ( + TestEnvironment::new( + dependencies, + config, + network, + overseer, + overseer_handle, + test_authorities, + with_prometheus_endpoint, + ), + req_cfgs, + ) +} + +#[derive(Clone)] +pub struct TestState { + // Full test configuration + config: TestConfiguration, + // A cycle iterator on all PoV sizes used in the test. + pov_sizes: Cycle>, + // Generated candidate receipts to be used in the test + candidates: Cycle>, + // Map from pov size to candidate index + pov_size_to_candidate: HashMap, + // Map from generated candidate hashes to candidate index in `available_data` + // and `chunks`. + candidate_hashes: HashMap, + // Per candidate index receipts. + candidate_receipt_templates: Vec, + // Per candidate index `AvailableData` + available_data: Vec, + // Per candiadte index chunks + chunks: Vec>, + // Per relay chain block - candidate backed by our backing group + backed_candidates: Vec, +} + +impl TestState { + pub fn next_candidate(&mut self) -> Option { + let candidate = self.candidates.next(); + let candidate_hash = candidate.as_ref().unwrap().hash(); + gum::trace!(target: LOG_TARGET, "Next candidate selected {:?}", candidate_hash); + candidate + } + + /// Generate candidates to be used in the test. + fn generate_candidates(&mut self) { + let count = self.config.n_cores * self.config.num_blocks; + gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {} candidates.", count).bright_blue()); + + // Generate all candidates + self.candidates = (0..count) + .map(|index| { + let pov_size = self.pov_sizes.next().expect("This is a cycle; qed"); + let candidate_index = *self + .pov_size_to_candidate + .get(&pov_size) + .expect("pov_size always exists; qed"); + let mut candidate_receipt = + self.candidate_receipt_templates[candidate_index].clone(); + + // Make it unique. + candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); + // Store the new candidate in the state + self.candidate_hashes.insert(candidate_receipt.hash(), candidate_index); + + gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate"); + + candidate_receipt + }) + .collect::>() + .into_iter() + .cycle(); + } + + pub fn new(config: &TestConfiguration) -> Self { + let config = config.clone(); + + let mut chunks = Vec::new(); + let mut available_data = Vec::new(); + let mut candidate_receipt_templates = Vec::new(); + let mut pov_size_to_candidate = HashMap::new(); + + // we use it for all candidates. + let persisted_validation_data = PersistedValidationData { + parent_head: HeadData(vec![7, 8, 9]), + relay_parent_number: Default::default(), + max_pov_size: 1024, + relay_parent_storage_root: Default::default(), + }; + + // For each unique pov we create a candidate receipt. + for (index, pov_size) in config.pov_sizes().iter().cloned().unique().enumerate() { + gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue()); + + let mut candidate_receipt = dummy_candidate_receipt(dummy_hash()); + let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) }; + + let new_available_data = AvailableData { + validation_data: persisted_validation_data.clone(), + pov: Arc::new(pov), + }; + + let (new_chunks, erasure_root) = derive_erasure_chunks_with_proofs_and_root( + config.n_validators, + &new_available_data, + |_, _| {}, + ); + + candidate_receipt.descriptor.erasure_root = erasure_root; + + chunks.push(new_chunks); + available_data.push(new_available_data); + pov_size_to_candidate.insert(pov_size, index); + candidate_receipt_templates.push(candidate_receipt); + } + + gum::info!(target: LOG_TARGET, "{}","Created test environment.".bright_blue()); + + let mut _self = Self { + available_data, + candidate_receipt_templates, + chunks, + pov_size_to_candidate, + pov_sizes: Vec::from(config.pov_sizes()).into_iter().cycle(), + candidate_hashes: HashMap::new(), + candidates: Vec::new().into_iter().cycle(), + backed_candidates: Vec::new(), + config, + }; + + _self.generate_candidates(); + _self + } + + pub fn backed_candidates(&mut self) -> &mut Vec { + &mut self.backed_candidates + } +} + +pub async fn benchmark_availability_read( + benchmark_name: &str, + env: &mut TestEnvironment, + mut state: TestState, +) -> BenchmarkUsage { + let config = env.config().clone(); + + env.import_block(new_block_import_info(Hash::repeat_byte(1), 1)).await; + + let test_start = Instant::now(); + let mut batch = FuturesUnordered::new(); + let mut availability_bytes = 0u128; + + env.metrics().set_n_validators(config.n_validators); + env.metrics().set_n_cores(config.n_cores); + + for block_num in 1..=env.config().num_blocks { + gum::info!(target: LOG_TARGET, "Current block {}/{}", block_num, env.config().num_blocks); + env.metrics().set_current_block(block_num); + + let block_start_ts = Instant::now(); + for candidate_num in 0..config.n_cores as u64 { + let candidate = + state.next_candidate().expect("We always send up to n_cores*num_blocks; qed"); + let (tx, rx) = oneshot::channel(); + batch.push(rx); + + let message = AllMessages::AvailabilityRecovery( + AvailabilityRecoveryMessage::RecoverAvailableData( + candidate.clone(), + 1, + Some(GroupIndex( + candidate_num as u32 % (std::cmp::max(5, config.n_cores) / 5) as u32, + )), + tx, + ), + ); + env.send_message(message).await; + } + + gum::info!(target: LOG_TARGET, "{}", format!("{} recoveries pending", batch.len()).bright_black()); + while let Some(completed) = batch.next().await { + let available_data = completed.unwrap().unwrap(); + env.metrics().on_pov_size(available_data.encoded_size()); + availability_bytes += available_data.encoded_size() as u128; + } + + let block_time = Instant::now().sub(block_start_ts).as_millis() as u64; + env.metrics().set_block_time(block_time); + gum::info!(target: LOG_TARGET, "All work for block completed in {}", format!("{:?}ms", block_time).cyan()); + } + + let duration: u128 = test_start.elapsed().as_millis(); + let availability_bytes = availability_bytes / 1024; + gum::info!(target: LOG_TARGET, "All blocks processed in {}", format!("{:?}ms", duration).cyan()); + gum::info!(target: LOG_TARGET, + "Throughput: {}", + format!("{} KiB/block", availability_bytes / env.config().num_blocks as u128).bright_red() + ); + gum::info!(target: LOG_TARGET, + "Avg block time: {}", + format!("{} ms", test_start.elapsed().as_millis() / env.config().num_blocks as u128).red() + ); + + env.stop().await; + env.collect_resource_usage(benchmark_name, &["availability-recovery"]) +} + +pub async fn benchmark_availability_write( + benchmark_name: &str, + env: &mut TestEnvironment, + mut state: TestState, +) -> BenchmarkUsage { + let config = env.config().clone(); + + env.metrics().set_n_validators(config.n_validators); + env.metrics().set_n_cores(config.n_cores); + + gum::info!(target: LOG_TARGET, "Seeding availability store with candidates ..."); + for backed_candidate in state.backed_candidates().clone() { + let candidate_index = *state.candidate_hashes.get(&backed_candidate.hash()).unwrap(); + let available_data = state.available_data[candidate_index].clone(); + let (tx, rx) = oneshot::channel(); + env.send_message(AllMessages::AvailabilityStore( + AvailabilityStoreMessage::StoreAvailableData { + candidate_hash: backed_candidate.hash(), + n_validators: config.n_validators as u32, + available_data, + expected_erasure_root: backed_candidate.descriptor().erasure_root, + tx, + }, + )) + .await; + + rx.await + .unwrap() + .expect("Test candidates are stored nicely in availability store"); + } + + gum::info!(target: LOG_TARGET, "Done"); + + let test_start = Instant::now(); + + for block_num in 1..=env.config().num_blocks { + gum::info!(target: LOG_TARGET, "Current block #{}", block_num); + env.metrics().set_current_block(block_num); + + let block_start_ts = Instant::now(); + let relay_block_hash = Hash::repeat_byte(block_num as u8); + env.import_block(new_block_import_info(relay_block_hash, block_num as BlockNumber)) + .await; + + // Inform bitfield distribution about our view of current test block + let message = polkadot_node_subsystem_types::messages::BitfieldDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::OurViewChange(OurView::new(vec![(relay_block_hash, Arc::new(Span::Disabled))], 0)) + ); + env.send_message(AllMessages::BitfieldDistribution(message)).await; + + let chunk_fetch_start_ts = Instant::now(); + + // Request chunks of our own backed candidate from all other validators. + let mut receivers = Vec::new(); + for index in 1..config.n_validators { + let (pending_response, pending_response_receiver) = oneshot::channel(); + + let request = RawIncomingRequest { + peer: PeerId::random(), + payload: ChunkFetchingRequest { + candidate_hash: state.backed_candidates()[block_num - 1].hash(), + index: ValidatorIndex(index as u32), + } + .encode(), + pending_response, + }; + + let peer = env + .authorities() + .validator_authority_id + .get(index) + .expect("all validators have keys"); + + if env.network().is_peer_connected(peer) && + env.network().send_request_from_peer(peer, request).is_ok() + { + receivers.push(pending_response_receiver); + } + } + + gum::info!(target: LOG_TARGET, "Waiting for all emulated peers to receive their chunk from us ..."); + for receiver in receivers.into_iter() { + let response = receiver.await.expect("Chunk is always served succesfully"); + // TODO: check if chunk is the one the peer expects to receive. + assert!(response.result.is_ok()); + } + + let chunk_fetch_duration = Instant::now().sub(chunk_fetch_start_ts).as_millis(); + + gum::info!(target: LOG_TARGET, "All chunks received in {}ms", chunk_fetch_duration); + + let signing_context = SigningContext { session_index: 0, parent_hash: relay_block_hash }; + let network = env.network().clone(); + let authorities = env.authorities().clone(); + let n_validators = config.n_validators; + + // Spawn a task that will generate `n_validator` - 1 signed bitfiends and + // send them from the emulated peers to the subsystem. + // TODO: Implement topology. + env.spawn_blocking("send-bitfields", async move { + for index in 1..n_validators { + let validator_public = + authorities.validator_public.get(index).expect("All validator keys are known"); + + // Node has all the chunks in the world. + let payload: AvailabilityBitfield = + AvailabilityBitfield(bitvec![u8, bitvec::order::Lsb0; 1u8; 32]); + // TODO(soon): Use pre-signed messages. This is quite intensive on the CPU. + let signed_bitfield = Signed::::sign( + &authorities.keyring.keystore(), + payload, + &signing_context, + ValidatorIndex(index as u32), + validator_public, + ) + .ok() + .flatten() + .expect("should be signed"); + + let from_peer = &authorities.validator_authority_id[index]; + + let message = peer_bitfield_message_v2(relay_block_hash, signed_bitfield); + + // Send the action from peer only if it is connected to our node. + if network.is_peer_connected(from_peer) { + let _ = network.send_message_from_peer(from_peer, message); + } + } + }); + + gum::info!( + "Waiting for {} bitfields to be received and processed", + config.connected_count() + ); + + // Wait for all bitfields to be processed. + env.wait_until_metric( + "polkadot_parachain_received_availabilty_bitfields_total", + None, + |value| value == (config.connected_count() * block_num) as f64, + ) + .await; + + gum::info!(target: LOG_TARGET, "All bitfields processed"); + + let block_time = Instant::now().sub(block_start_ts).as_millis() as u64; + env.metrics().set_block_time(block_time); + gum::info!(target: LOG_TARGET, "All work for block completed in {}", format!("{:?}ms", block_time).cyan()); + } + + let duration: u128 = test_start.elapsed().as_millis(); + gum::info!(target: LOG_TARGET, "All blocks processed in {}", format!("{:?}ms", duration).cyan()); + gum::info!(target: LOG_TARGET, + "Avg block time: {}", + format!("{} ms", test_start.elapsed().as_millis() / env.config().num_blocks as u128).red() + ); + + env.stop().await; + env.collect_resource_usage( + benchmark_name, + &["availability-distribution", "bitfield-distribution", "availability-store"], + ) +} + +pub fn peer_bitfield_message_v2( + relay_hash: H256, + signed_bitfield: Signed, +) -> VersionedValidationProtocol { + let bitfield = polkadot_node_network_protocol::v2::BitfieldDistributionMessage::Bitfield( + relay_hash, + signed_bitfield.into(), + ); + + Versioned::V2(polkadot_node_network_protocol::v2::ValidationProtocol::BitfieldDistribution( + bitfield, + )) +} diff --git a/polkadot/node/subsystem-bench/src/lib/configuration.rs b/polkadot/node/subsystem-bench/src/lib/configuration.rs new file mode 100644 index 0000000000000000000000000000000000000000..c76933085271aa8a3fea01a1bab137d77b104fed --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/configuration.rs @@ -0,0 +1,243 @@ +// 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 . + +//! Test configuration definition and helpers. + +use crate::keyring::Keyring; +use itertools::Itertools; +use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId}; +use rand::thread_rng; +use rand_distr::{Distribution, Normal, Uniform}; +use sc_network::PeerId; +use serde::{Deserialize, Serialize}; +use sp_consensus_babe::AuthorityId; +use std::collections::HashMap; + +/// Peer networking latency configuration. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct PeerLatency { + /// The mean latency(milliseconds) of the peers. + pub mean_latency_ms: usize, + /// The standard deviation + pub std_dev: f64, +} + +// Default PoV size in KiB. +fn default_pov_size() -> usize { + 5120 +} + +// Default bandwidth in bytes +fn default_bandwidth() -> usize { + 52428800 +} + +// Default connectivity percentage +fn default_connectivity() -> usize { + 100 +} + +// Default backing group size +fn default_backing_group_size() -> usize { + 5 +} + +// Default needed approvals +fn default_needed_approvals() -> usize { + 30 +} + +fn default_zeroth_delay_tranche_width() -> usize { + 0 +} +fn default_relay_vrf_modulo_samples() -> usize { + 6 +} + +fn default_n_delay_tranches() -> usize { + 89 +} +fn default_no_show_slots() -> usize { + 3 +} + +/// The test input parameters +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TestConfiguration { + /// Number of validators + pub n_validators: usize, + /// Number of cores + pub n_cores: usize, + /// The number of needed votes to approve a candidate. + #[serde(default = "default_needed_approvals")] + pub needed_approvals: usize, + #[serde(default = "default_zeroth_delay_tranche_width")] + pub zeroth_delay_tranche_width: usize, + #[serde(default = "default_relay_vrf_modulo_samples")] + pub relay_vrf_modulo_samples: usize, + #[serde(default = "default_n_delay_tranches")] + pub n_delay_tranches: usize, + #[serde(default = "default_no_show_slots")] + pub no_show_slots: usize, + /// Maximum backing group size + #[serde(default = "default_backing_group_size")] + pub max_validators_per_core: usize, + /// The min PoV size + #[serde(default = "default_pov_size")] + pub min_pov_size: usize, + /// The max PoV size, + #[serde(default = "default_pov_size")] + pub max_pov_size: usize, + /// Randomly sampled pov_sizes + #[serde(skip)] + pub pov_sizes: Vec, + /// The amount of bandiwdth remote validators have. + #[serde(default = "default_bandwidth")] + pub peer_bandwidth: usize, + /// The amount of bandiwdth our node has. + #[serde(default = "default_bandwidth")] + pub bandwidth: usize, + /// Optional peer emulation latency (round trip time) wrt node under test + #[serde(default)] + pub latency: Option, + /// Connectivity ratio, the percentage of peers we are not connected to, but ar part of + /// the topology. + #[serde(default = "default_connectivity")] + pub connectivity: usize, + /// Number of blocks to run the test for + pub num_blocks: usize, +} + +impl Default for TestConfiguration { + fn default() -> Self { + Self { + n_validators: Default::default(), + n_cores: Default::default(), + needed_approvals: default_needed_approvals(), + zeroth_delay_tranche_width: default_zeroth_delay_tranche_width(), + relay_vrf_modulo_samples: default_relay_vrf_modulo_samples(), + n_delay_tranches: default_n_delay_tranches(), + no_show_slots: default_no_show_slots(), + max_validators_per_core: default_backing_group_size(), + min_pov_size: default_pov_size(), + max_pov_size: default_pov_size(), + pov_sizes: Default::default(), + peer_bandwidth: default_bandwidth(), + bandwidth: default_bandwidth(), + latency: Default::default(), + connectivity: default_connectivity(), + num_blocks: Default::default(), + } + } +} + +impl TestConfiguration { + pub fn generate_pov_sizes(&mut self) { + self.pov_sizes = generate_pov_sizes(self.n_cores, self.min_pov_size, self.max_pov_size); + } + + pub fn pov_sizes(&self) -> &[usize] { + &self.pov_sizes + } + /// Return the number of peers connected to our node. + pub fn connected_count(&self) -> usize { + ((self.n_validators - 1) as f64 / (100.0 / self.connectivity as f64)) as usize + } + + /// Generates the authority keys we need for the network emulation. + pub fn generate_authorities(&self) -> TestAuthorities { + let keyring = Keyring::default(); + + let key_seeds = (0..self.n_validators) + .map(|peer_index| format!("//Node{}", peer_index)) + .collect_vec(); + + let keys = key_seeds + .iter() + .map(|seed| keyring.sr25519_new(seed.as_str())) + .collect::>(); + + // Generate keys and peers ids in each of the format needed by the tests. + let validator_public: Vec = + keys.iter().map(|key| (*key).into()).collect::>(); + + let validator_authority_id: Vec = + keys.iter().map(|key| (*key).into()).collect::>(); + + let validator_babe_id: Vec = + keys.iter().map(|key| (*key).into()).collect::>(); + + let validator_assignment_id: Vec = + keys.iter().map(|key| (*key).into()).collect::>(); + let peer_ids: Vec = keys.iter().map(|_| PeerId::random()).collect::>(); + + let peer_id_to_authority = peer_ids + .iter() + .zip(validator_authority_id.iter()) + .map(|(peer_id, authorithy_id)| (*peer_id, authorithy_id.clone())) + .collect(); + + TestAuthorities { + keyring, + validator_public, + validator_authority_id, + peer_ids, + validator_babe_id, + validator_assignment_id, + key_seeds, + peer_id_to_authority, + } + } +} + +fn random_uniform_sample + From>(min_value: T, max_value: T) -> T { + Uniform::from(min_value.into()..=max_value.into()) + .sample(&mut thread_rng()) + .into() +} + +fn random_pov_size(min_pov_size: usize, max_pov_size: usize) -> usize { + random_uniform_sample(min_pov_size, max_pov_size) +} + +fn generate_pov_sizes(count: usize, min_kib: usize, max_kib: usize) -> Vec { + (0..count).map(|_| random_pov_size(min_kib * 1024, max_kib * 1024)).collect() +} + +/// Helper struct for authority related state. +#[derive(Clone)] +pub struct TestAuthorities { + pub keyring: Keyring, + pub validator_public: Vec, + pub validator_authority_id: Vec, + pub validator_babe_id: Vec, + pub validator_assignment_id: Vec, + pub key_seeds: Vec, + pub peer_ids: Vec, + pub peer_id_to_authority: HashMap, +} + +/// Sample latency (in milliseconds) from a normal distribution with parameters +/// specified in `maybe_peer_latency`. +pub fn random_latency(maybe_peer_latency: Option<&PeerLatency>) -> usize { + maybe_peer_latency + .map(|latency_config| { + Normal::new(latency_config.mean_latency_ms as f64, latency_config.std_dev) + .expect("normal distribution parameters are good") + .sample(&mut thread_rng()) + }) + .unwrap_or(0.0) as usize +} diff --git a/polkadot/node/subsystem-bench/src/core/display.rs b/polkadot/node/subsystem-bench/src/lib/display.rs similarity index 84% rename from polkadot/node/subsystem-bench/src/core/display.rs rename to polkadot/node/subsystem-bench/src/lib/display.rs index d600cc484c14a45361c19621213dbf666475a778..b153d54a7c36f43bc110dde9443f2413a34d9af6 100644 --- a/polkadot/node/subsystem-bench/src/core/display.rs +++ b/polkadot/node/subsystem-bench/src/lib/display.rs @@ -13,12 +13,13 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -// + //! Display implementations and helper methods for parsing prometheus metrics //! to a format that can be displayed in the CLI. //! //! Currently histogram buckets are skipped. -use super::{configuration::TestConfiguration, LOG_TARGET}; + +use crate::configuration::TestConfiguration; use colored::Colorize; use prometheus::{ proto::{MetricFamily, MetricType}, @@ -26,7 +27,9 @@ use prometheus::{ }; use std::fmt::Display; -#[derive(Default)] +const LOG_TARGET: &str = "subsystem-bench::display"; + +#[derive(Default, Debug)] pub struct MetricCollection(Vec); impl From> for MetricCollection { @@ -49,6 +52,11 @@ impl MetricCollection { .sum() } + /// Tells if entries in bucket metric is lower than `value` + pub fn metric_lower_than(&self, metric_name: &str, value: f64) -> bool { + self.sum_by(metric_name) < value + } + pub fn subset_with_label_value(&self, label_name: &str, label_value: &str) -> MetricCollection { self.0 .iter() @@ -79,6 +87,7 @@ impl Display for MetricCollection { Ok(()) } } + #[derive(Debug, Clone)] pub struct TestMetric { name: String, @@ -163,7 +172,7 @@ pub fn parse_metrics(registry: &Registry) -> MetricCollection { name: h_name, label_names, label_values, - value: h.get_sample_sum(), + value: h.get_sample_count() as f64, }); }, MetricType::SUMMARY => { @@ -178,14 +187,16 @@ pub fn parse_metrics(registry: &Registry) -> MetricCollection { test_metrics.into() } -pub fn display_configuration(test_config: &TestConfiguration) { - gum::info!( - "{}, {}, {}, {}, {}", - format!("n_validators = {}", test_config.n_validators).blue(), - format!("n_cores = {}", test_config.n_cores).blue(), - format!("pov_size = {} - {}", test_config.min_pov_size, test_config.max_pov_size) - .bright_black(), - format!("error = {}", test_config.error).bright_black(), - format!("latency = {:?}", test_config.latency).bright_black(), - ); +impl Display for TestConfiguration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}, {}, {}, {}, {}", + format!("n_validators = {}", self.n_validators).blue(), + format!("n_cores = {}", self.n_cores).blue(), + format!("pov_size = {} - {}", self.min_pov_size, self.max_pov_size).bright_black(), + format!("connectivity = {}", self.connectivity).bright_black(), + format!("latency = {:?}", self.latency).bright_black(), + ) + } } diff --git a/polkadot/node/subsystem-bench/src/core/environment.rs b/polkadot/node/subsystem-bench/src/lib/environment.rs similarity index 60% rename from polkadot/node/subsystem-bench/src/core/environment.rs rename to polkadot/node/subsystem-bench/src/lib/environment.rs index 247596474078ef73a74f1762c30b56b52ce4417f..958ed50d0894b76ddb66978950971620dceb4aa0 100644 --- a/polkadot/node/subsystem-bench/src/core/environment.rs +++ b/polkadot/node/subsystem-bench/src/lib/environment.rs @@ -13,31 +13,28 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . + //! Test environment implementation + use crate::{ - core::{mock::AlwaysSupportsParachains, network::NetworkEmulator}, - TestConfiguration, + configuration::{TestAuthorities, TestConfiguration}, + mock::AlwaysSupportsParachains, + network::NetworkEmulatorHandle, + usage::{BenchmarkUsage, ResourceUsage}, }; -use colored::Colorize; use core::time::Duration; -use futures::FutureExt; -use polkadot_overseer::{BlockInfo, Handle as OverseerHandle}; - +use futures::{Future, FutureExt}; use polkadot_node_subsystem::{messages::AllMessages, Overseer, SpawnGlue, TimeoutExt}; use polkadot_node_subsystem_types::Hash; use polkadot_node_subsystem_util::metrics::prometheus::{ self, Gauge, Histogram, PrometheusError, Registry, U64, }; - -use sc_network::peer_store::LOG_TARGET; +use polkadot_overseer::{BlockInfo, Handle as OverseerHandle}; use sc_service::{SpawnTaskHandle, TaskManager}; -use std::{ - fmt::Display, - net::{Ipv4Addr, SocketAddr}, -}; +use std::net::{Ipv4Addr, SocketAddr}; use tokio::runtime::Handle; -const MIB: f64 = 1024.0 * 1024.0; +const LOG_TARGET: &str = "subsystem-bench::environment"; /// Test environment/configuration metrics #[derive(Clone)] @@ -56,9 +53,8 @@ pub struct TestEnvironmentMetrics { impl TestEnvironmentMetrics { pub fn new(registry: &Registry) -> Result { - let mut buckets = prometheus::exponential_buckets(16384.0, 2.0, 9) + let buckets = prometheus::exponential_buckets(16384.0, 2.0, 9) .expect("arguments are always valid; qed"); - buckets.extend(vec![5.0 * MIB, 6.0 * MIB, 7.0 * MIB, 8.0 * MIB, 9.0 * MIB, 10.0 * MIB]); Ok(Self { n_validators: prometheus::register( @@ -150,7 +146,7 @@ pub const GENESIS_HASH: Hash = Hash::repeat_byte(0xff); // We use this to bail out sending messages to the subsystem if it is overloaded such that // the time of flight is breaches 5s. // This should eventually be a test parameter. -const MAX_TIME_OF_FLIGHT: Duration = Duration::from_millis(5000); +pub const MAX_TIME_OF_FLIGHT: Duration = Duration::from_millis(5000); /// The test environment is the high level wrapper of all things required to test /// a certain subsystem. @@ -189,9 +185,11 @@ pub struct TestEnvironment { /// The test configuration. config: TestConfiguration, /// A handle to the network emulator. - network: NetworkEmulator, + network: NetworkEmulatorHandle, /// Configuration/env metrics metrics: TestEnvironmentMetrics, + /// Test authorities generated from the configuration. + authorities: TestAuthorities, } impl TestEnvironment { @@ -199,9 +197,11 @@ impl TestEnvironment { pub fn new( dependencies: TestEnvironmentDependencies, config: TestConfiguration, - network: NetworkEmulator, + network: NetworkEmulatorHandle, overseer: Overseer, AlwaysSupportsParachains>, overseer_handle: OverseerHandle, + authorities: TestAuthorities, + with_prometheus_endpoint: bool, ) -> Self { let metrics = TestEnvironmentMetrics::new(&dependencies.registry) .expect("Metrics need to be registered"); @@ -209,19 +209,21 @@ impl TestEnvironment { let spawn_handle = dependencies.task_manager.spawn_handle(); spawn_handle.spawn_blocking("overseer", "overseer", overseer.run().boxed()); - let registry_clone = dependencies.registry.clone(); - dependencies.task_manager.spawn_handle().spawn_blocking( - "prometheus", - "test-environment", - async move { - prometheus_endpoint::init_prometheus( - SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), 9999), - registry_clone, - ) - .await - .unwrap(); - }, - ); + if with_prometheus_endpoint { + let registry_clone = dependencies.registry.clone(); + dependencies.task_manager.spawn_handle().spawn_blocking( + "prometheus", + "test-environment", + async move { + prometheus_endpoint::init_prometheus( + SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), 9999), + registry_clone, + ) + .await + .unwrap(); + }, + ); + } TestEnvironment { runtime_handle: dependencies.runtime.handle().clone(), @@ -230,30 +232,67 @@ impl TestEnvironment { config, network, metrics, + authorities, } } + /// Returns the test configuration. pub fn config(&self) -> &TestConfiguration { &self.config } - pub fn network(&self) -> &NetworkEmulator { + /// Returns a reference to the inner network emulator handle. + pub fn network(&self) -> &NetworkEmulatorHandle { &self.network } + /// Returns a reference to the overseer handle. + pub fn overseer_handle(&self) -> &OverseerHandle { + &self.overseer_handle + } + + /// Returns the Prometheus registry. pub fn registry(&self) -> &Registry { &self.dependencies.registry } + /// Spawn a named task in the `test-environment` task group. + #[allow(unused)] + pub fn spawn(&self, name: &'static str, task: impl Future + Send + 'static) { + self.dependencies + .task_manager + .spawn_handle() + .spawn(name, "test-environment", task); + } + + /// Spawn a blocking named task in the `test-environment` task group. + pub fn spawn_blocking( + &self, + name: &'static str, + task: impl Future + Send + 'static, + ) { + self.dependencies.task_manager.spawn_handle().spawn_blocking( + name, + "test-environment", + task, + ); + } + /// Returns a reference to the test environment metrics instance pub fn metrics(&self) -> &TestEnvironmentMetrics { &self.metrics } + /// Returns a handle to the tokio runtime. pub fn runtime(&self) -> Handle { self.runtime_handle.clone() } - // Send a message to the subsystem under test environment. + /// Returns a reference to the authority keys used in the test. + pub fn authorities(&self) -> &TestAuthorities { + &self.authorities + } + + /// Send a message to the subsystem under test environment. pub async fn send_message(&mut self, msg: AllMessages) { self.overseer_handle .send_msg(msg, LOG_TARGET) @@ -264,7 +303,7 @@ impl TestEnvironment { }); } - // Send an `ActiveLeavesUpdate` signal to all subsystems under test. + /// Send an `ActiveLeavesUpdate` signal to all subsystems under test. pub async fn import_block(&mut self, block: BlockInfo) { self.overseer_handle .block_imported(block) @@ -275,59 +314,100 @@ impl TestEnvironment { }); } - // Stop overseer and subsystems. + /// Stop overseer and subsystems. pub async fn stop(&mut self) { self.overseer_handle.stop().await; } -} -impl Display for TestEnvironment { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let stats = self.network().stats(); - - writeln!(f, "\n")?; - writeln!( - f, - "Total received from network: {}", - format!( - "{} MiB", - stats - .iter() - .enumerate() - .map(|(_index, stats)| stats.tx_bytes_total as u128) - .sum::() / (1024 * 1024) - ) - .cyan() - )?; - writeln!( - f, - "Total sent to network: {}", - format!("{} KiB", stats[0].tx_bytes_total / (1024)).cyan() - )?; + /// Tells if entries in bucket metric is lower than `value` + pub fn metric_lower_than(registry: &Registry, metric_name: &str, value: f64) -> bool { + let test_metrics = super::display::parse_metrics(registry); + test_metrics.metric_lower_than(metric_name, value) + } + + /// Blocks until `metric_name` >= `value` + pub async fn wait_until_metric( + &self, + metric_name: &str, + label: Option<(&str, &str)>, + condition: impl Fn(f64) -> bool, + ) { + loop { + let test_metrics = if let Some((label_name, label_value)) = label { + super::display::parse_metrics(self.registry()) + .subset_with_label_value(label_name, label_value) + } else { + super::display::parse_metrics(self.registry()) + }; + let current_value = test_metrics.sum_by(metric_name); + + gum::debug!(target: LOG_TARGET, metric_name, current_value, "Waiting for metric"); + if condition(current_value) { + break + } + // Check value every 50ms. + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + } + } + + pub fn collect_resource_usage( + &self, + benchmark_name: &str, + subsystems_under_test: &[&str], + ) -> BenchmarkUsage { + BenchmarkUsage { + benchmark_name: benchmark_name.to_string(), + network_usage: self.network_usage(), + cpu_usage: self.cpu_usage(subsystems_under_test), + } + } + + fn network_usage(&self) -> Vec { + let stats = self.network().peer_stats(0); + let total_node_received = (stats.received() / 1024) as f64; + let total_node_sent = (stats.sent() / 1024) as f64; + let num_blocks = self.config().num_blocks as f64; + + vec![ + ResourceUsage { + resource_name: "Received from peers".to_string(), + total: total_node_received, + per_block: total_node_received / num_blocks, + }, + ResourceUsage { + resource_name: "Sent to peers".to_string(), + total: total_node_sent, + per_block: total_node_sent / num_blocks, + }, + ] + } + fn cpu_usage(&self, subsystems_under_test: &[&str]) -> Vec { let test_metrics = super::display::parse_metrics(self.registry()); - let subsystem_cpu_metrics = - test_metrics.subset_with_label_value("task_group", "availability-recovery"); - let total_cpu = subsystem_cpu_metrics.sum_by("substrate_tasks_polling_duration_sum"); - writeln!(f, "Total subsystem CPU usage {}", format!("{:.2}s", total_cpu).bright_purple())?; - writeln!( - f, - "CPU usage per block {}", - format!("{:.2}s", total_cpu / self.config().num_blocks as f64).bright_purple() - )?; + let mut usage = vec![]; + let num_blocks = self.config().num_blocks as f64; + + for subsystem in subsystems_under_test.iter() { + let subsystem_cpu_metrics = + test_metrics.subset_with_label_value("task_group", subsystem); + let total_cpu = subsystem_cpu_metrics.sum_by("substrate_tasks_polling_duration_sum"); + usage.push(ResourceUsage { + resource_name: subsystem.to_string(), + total: total_cpu, + per_block: total_cpu / num_blocks, + }); + } let test_env_cpu_metrics = test_metrics.subset_with_label_value("task_group", "test-environment"); let total_cpu = test_env_cpu_metrics.sum_by("substrate_tasks_polling_duration_sum"); - writeln!( - f, - "Total test environment CPU usage {}", - format!("{:.2}s", total_cpu).bright_purple() - )?; - writeln!( - f, - "CPU usage per block {}", - format!("{:.2}s", total_cpu / self.config().num_blocks as f64).bright_purple() - ) + + usage.push(ResourceUsage { + resource_name: "Test environment".to_string(), + total: total_cpu, + per_block: total_cpu / num_blocks, + }); + + usage } } diff --git a/polkadot/node/subsystem-bench/src/core/keyring.rs b/polkadot/node/subsystem-bench/src/lib/keyring.rs similarity index 51% rename from polkadot/node/subsystem-bench/src/core/keyring.rs rename to polkadot/node/subsystem-bench/src/lib/keyring.rs index 68e78069a918b602f2e5d03845e00b9614bd3ba6..c290d30b46fbe1a2a0db04155d8460c288fd98ff 100644 --- a/polkadot/node/subsystem-bench/src/core/keyring.rs +++ b/polkadot/node/subsystem-bench/src/lib/keyring.rs @@ -14,26 +14,37 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use sp_core::{ - sr25519::{Pair, Public}, - Pair as PairT, -}; -/// Set of test accounts. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +use polkadot_primitives::ValidatorId; +use sc_keystore::LocalKeystore; +use sp_application_crypto::AppCrypto; +use sp_core::sr25519::Public; +use sp_keystore::Keystore; +use std::sync::Arc; + +/// Set of test accounts generated and kept safe by a keystore. +#[derive(Clone)] pub struct Keyring { - name: String, + keystore: Arc, +} + +impl Default for Keyring { + fn default() -> Self { + Self { keystore: Arc::new(LocalKeystore::in_memory()) } + } } impl Keyring { - pub fn new(name: String) -> Keyring { - Self { name } + pub fn sr25519_new(&self, seed: &str) -> Public { + self.keystore + .sr25519_generate_new(ValidatorId::ID, Some(seed)) + .expect("Insert key into keystore") } - pub fn pair(self) -> Pair { - Pair::from_string(&format!("//{}", self.name), None).expect("input is always good; qed") + pub fn keystore(&self) -> Arc { + self.keystore.clone() } - pub fn public(self) -> Public { - self.pair().public() + pub fn keystore_ref(&self) -> &LocalKeystore { + self.keystore.as_ref() } } diff --git a/polkadot/node/subsystem-bench/src/core/mod.rs b/polkadot/node/subsystem-bench/src/lib/lib.rs similarity index 72% rename from polkadot/node/subsystem-bench/src/core/mod.rs rename to polkadot/node/subsystem-bench/src/lib/lib.rs index 282788d143b44a9a2444533f1eda756e0385c0a2..d06f2822a8958169a15f449e71251e3cc62eb9d6 100644 --- a/polkadot/node/subsystem-bench/src/core/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/lib.rs @@ -14,11 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -const LOG_TARGET: &str = "subsystem-bench::core"; +// The validator index that represent the node that is under test. +pub const NODE_UNDER_TEST: u32 = 0; +pub mod approval; +pub mod availability; pub mod configuration; -pub mod display; -pub mod environment; -pub mod keyring; -pub mod mock; -pub mod network; +pub(crate) mod display; +pub(crate) mod environment; +pub(crate) mod keyring; +pub(crate) mod mock; +pub(crate) mod network; +pub mod usage; diff --git a/polkadot/node/subsystem-bench/src/core/mock/av_store.rs b/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs similarity index 59% rename from polkadot/node/subsystem-bench/src/core/mock/av_store.rs rename to polkadot/node/subsystem-bench/src/lib/mock/av_store.rs index a471230f1b3f0e5be27494988f04590ff4aaa78e..41c4fe2cbadc0d71e5130ba12f048c0328231e82 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/av_store.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs @@ -13,23 +13,24 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! -//! A generic av store subsystem mockup suitable to be used in benchmarks. - -use parity_scale_codec::Encode; -use polkadot_primitives::CandidateHash; -use std::collections::HashMap; +//! A generic av store subsystem mockup suitable to be used in benchmarks. +use crate::network::{HandleNetworkMessage, NetworkMessage}; use futures::{channel::oneshot, FutureExt}; - -use polkadot_node_primitives::ErasureChunk; - +use parity_scale_codec::Encode; +use polkadot_node_network_protocol::request_response::{ + v1::{AvailableDataFetchingResponse, ChunkFetchingResponse, ChunkResponse}, + Requests, +}; +use polkadot_node_primitives::{AvailableData, ErasureChunk}; use polkadot_node_subsystem::{ messages::AvailabilityStoreMessage, overseer, SpawnedSubsystem, SubsystemError, }; - use polkadot_node_subsystem_types::OverseerSignal; +use polkadot_primitives::CandidateHash; +use sc_network::ProtocolName; +use std::collections::HashMap; pub struct AvailabilityStoreState { candidate_hashes: HashMap, @@ -38,6 +39,75 @@ pub struct AvailabilityStoreState { const LOG_TARGET: &str = "subsystem-bench::av-store-mock"; +/// Mockup helper. Contains Ccunks and full availability data of all parachain blocks +/// used in a test. +pub struct NetworkAvailabilityState { + pub candidate_hashes: HashMap, + pub available_data: Vec, + pub chunks: Vec>, +} + +// Implement access to the state. +impl HandleNetworkMessage for NetworkAvailabilityState { + fn handle( + &self, + message: NetworkMessage, + _node_sender: &mut futures::channel::mpsc::UnboundedSender, + ) -> Option { + match message { + NetworkMessage::RequestFromNode(peer, request) => match request { + Requests::ChunkFetchingV1(outgoing_request) => { + gum::debug!(target: LOG_TARGET, request = ?outgoing_request, "Received `RequestFromNode`"); + let validator_index: usize = outgoing_request.payload.index.0 as usize; + let candidate_hash = outgoing_request.payload.candidate_hash; + + let candidate_index = self + .candidate_hashes + .get(&candidate_hash) + .expect("candidate was generated previously; qed"); + gum::warn!(target: LOG_TARGET, ?candidate_hash, candidate_index, "Candidate mapped to index"); + + let chunk: ChunkResponse = + self.chunks.get(*candidate_index).unwrap()[validator_index].clone().into(); + let response = Ok(( + ChunkFetchingResponse::from(Some(chunk)).encode(), + ProtocolName::Static("dummy"), + )); + + if let Err(err) = outgoing_request.pending_response.send(response) { + gum::error!(target: LOG_TARGET, ?err, "Failed to send `ChunkFetchingResponse`"); + } + + None + }, + Requests::AvailableDataFetchingV1(outgoing_request) => { + let candidate_hash = outgoing_request.payload.candidate_hash; + let candidate_index = self + .candidate_hashes + .get(&candidate_hash) + .expect("candidate was generated previously; qed"); + gum::debug!(target: LOG_TARGET, ?candidate_hash, candidate_index, "Candidate mapped to index"); + + let available_data = self.available_data.get(*candidate_index).unwrap().clone(); + + let response = Ok(( + AvailableDataFetchingResponse::from(Some(available_data)).encode(), + ProtocolName::Static("dummy"), + )); + outgoing_request + .pending_response + .send(response) + .expect("Response is always sent succesfully"); + None + }, + _ => Some(NetworkMessage::RequestFromNode(peer, request)), + }, + + message => Some(message), + } + } +} + /// A mock of the availability store subsystem. This one also generates all the /// candidates that a pub struct MockAvailabilityStore { @@ -127,6 +197,10 @@ impl MockAvailabilityStore { self.state.chunks.get(*candidate_index).unwrap()[0].encoded_size(); let _ = tx.send(Some(chunk_size)); }, + AvailabilityStoreMessage::StoreChunk { candidate_hash, chunk, tx } => { + gum::debug!(target: LOG_TARGET, chunk_index = ?chunk.index ,candidate_hash = ?candidate_hash, "Responding to StoreChunk"); + let _ = tx.send(Ok(())); + }, _ => { unimplemented!("Unexpected av-store message") }, diff --git a/polkadot/node/subsystem-bench/src/lib/mock/chain_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/chain_api.rs new file mode 100644 index 0000000000000000000000000000000000000000..bee15c3cefdfbda061a1313db6c631e52fd91c12 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/mock/chain_api.rs @@ -0,0 +1,132 @@ +// 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 . + +//! A generic runtime api subsystem mockup suitable to be used in benchmarks. + +use futures::FutureExt; +use itertools::Itertools; +use polkadot_node_subsystem::{ + messages::ChainApiMessage, overseer, SpawnedSubsystem, SubsystemError, +}; +use polkadot_node_subsystem_types::OverseerSignal; +use polkadot_primitives::Header; +use sp_core::H256; +use std::collections::HashMap; + +const LOG_TARGET: &str = "subsystem-bench::chain-api-mock"; + +/// State used to respond to `BlockHeader` requests. +pub struct ChainApiState { + pub block_headers: HashMap, +} + +pub struct MockChainApi { + state: ChainApiState, +} + +impl ChainApiState { + fn get_header_by_number(&self, requested_number: u32) -> Option<&Header> { + self.block_headers.values().find(|header| header.number == requested_number) + } +} + +impl MockChainApi { + pub fn new(state: ChainApiState) -> MockChainApi { + Self { state } + } +} + +#[overseer::subsystem(ChainApi, error=SubsystemError, prefix=self::overseer)] +impl MockChainApi { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self.run(ctx).map(|_| Ok(())).boxed(); + + SpawnedSubsystem { name: "test-environment", future } + } +} + +#[overseer::contextbounds(ChainApi, prefix = self::overseer)] +impl MockChainApi { + async fn run(self, mut ctx: Context) { + loop { + let msg = ctx.recv().await.expect("Overseer never fails us"); + + match msg { + orchestra::FromOrchestra::Signal(signal) => + if signal == OverseerSignal::Conclude { + return + }, + orchestra::FromOrchestra::Communication { msg } => { + gum::debug!(target: LOG_TARGET, msg=?msg, "recv message"); + + match msg { + ChainApiMessage::BlockHeader(hash, response_channel) => { + let _ = response_channel.send(Ok(Some( + self.state + .block_headers + .get(&hash) + .cloned() + .expect("Relay chain block hashes are known"), + ))); + }, + ChainApiMessage::FinalizedBlockNumber(val) => { + val.send(Ok(0)).unwrap(); + }, + ChainApiMessage::FinalizedBlockHash(requested_number, sender) => { + let hash = self + .state + .get_header_by_number(requested_number) + .expect("Unknow block number") + .hash(); + sender.send(Ok(Some(hash))).unwrap(); + }, + ChainApiMessage::BlockNumber(requested_hash, sender) => { + sender + .send(Ok(Some( + self.state + .block_headers + .get(&requested_hash) + .expect("Unknown block hash") + .number, + ))) + .unwrap(); + }, + ChainApiMessage::Ancestors { hash, k: _, response_channel } => { + let block_number = self + .state + .block_headers + .get(&hash) + .expect("Unknown block hash") + .number; + let ancestors = self + .state + .block_headers + .iter() + .filter(|(_, header)| header.number < block_number) + .sorted_by(|a, b| a.1.number.cmp(&b.1.number)) + .map(|(hash, _)| *hash) + .collect_vec(); + response_channel.send(Ok(ancestors)).unwrap(); + }, + _ => { + unimplemented!("Unexpected chain-api message") + }, + } + }, + } + } + } +} diff --git a/polkadot/node/subsystem-bench/src/core/mock/dummy.rs b/polkadot/node/subsystem-bench/src/lib/mock/dummy.rs similarity index 98% rename from polkadot/node/subsystem-bench/src/core/mock/dummy.rs rename to polkadot/node/subsystem-bench/src/lib/mock/dummy.rs index 0628368a49c08af69077ba558b5dc8b34f8b57bd..8783b35f1c04a9c59bf415340689b1a9ae29ee6a 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/dummy.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/dummy.rs @@ -13,10 +13,11 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . + //! Dummy subsystem mocks. -use paste::paste; use futures::FutureExt; +use paste::paste; use polkadot_node_subsystem::{overseer, SpawnedSubsystem, SubsystemError}; use std::time::Duration; use tokio::time::sleep; @@ -73,6 +74,7 @@ macro_rules! mock { }; } +// Generate dummy implementation for all subsystems mock!(AvailabilityStore); mock!(StatementDistribution); mock!(BitfieldSigning); diff --git a/polkadot/node/subsystem-bench/src/core/mock/mod.rs b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs similarity index 87% rename from polkadot/node/subsystem-bench/src/core/mock/mod.rs rename to polkadot/node/subsystem-bench/src/lib/mock/mod.rs index 76fd581c3fb65df4b5d5c02b5314801ada4087d4..6dda9a47d398f3e6e952cd054bce9769e8942e70 100644 --- a/polkadot/node/subsystem-bench/src/core/mock/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs @@ -16,16 +16,16 @@ use polkadot_node_subsystem::HeadSupportsParachains; use polkadot_node_subsystem_types::Hash; +use sp_consensus::SyncOracle; pub mod av_store; +pub mod chain_api; pub mod dummy; pub mod network_bridge; pub mod runtime_api; -pub use av_store::*; -pub use runtime_api::*; - pub struct AlwaysSupportsParachains {} + #[async_trait::async_trait] impl HeadSupportsParachains for AlwaysSupportsParachains { async fn head_supports_parachains(&self, _head: &Hash) -> bool { @@ -34,9 +34,10 @@ impl HeadSupportsParachains for AlwaysSupportsParachains { } // An orchestra with dummy subsystems +#[macro_export] macro_rules! dummy_builder { - ($spawn_task_handle: ident) => {{ - use super::core::mock::dummy::*; + ($spawn_task_handle: ident, $metrics: ident) => {{ + use $crate::mock::dummy::*; // Initialize a mock overseer. // All subsystem except approval_voting and approval_distribution are mock subsystems. @@ -67,10 +68,21 @@ macro_rules! dummy_builder { .activation_external_listeners(Default::default()) .span_per_active_leaf(Default::default()) .active_leaves(Default::default()) - .metrics(Default::default()) + .metrics($metrics) .supports_parachains(AlwaysSupportsParachains {}) .spawner(SpawnGlue($spawn_task_handle)) }}; } -pub(crate) use dummy_builder; +#[derive(Clone)] +pub struct TestSyncOracle {} + +impl SyncOracle for TestSyncOracle { + fn is_major_syncing(&self) -> bool { + false + } + + fn is_offline(&self) -> bool { + unimplemented!("not used by subsystem benchmarks") + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs new file mode 100644 index 0000000000000000000000000000000000000000..d598f6447d3d43ece5e1d0fa6364508403c032e9 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs @@ -0,0 +1,210 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Mocked `network-bridge` subsystems that uses a `NetworkInterface` to access +//! the emulated network. + +use crate::{ + configuration::TestAuthorities, + network::{NetworkEmulatorHandle, NetworkInterfaceReceiver, NetworkMessage, RequestExt}, +}; +use futures::{channel::mpsc::UnboundedSender, FutureExt, StreamExt}; +use polkadot_node_network_protocol::Versioned; +use polkadot_node_subsystem::{ + messages::NetworkBridgeTxMessage, overseer, SpawnedSubsystem, SubsystemError, +}; +use polkadot_node_subsystem_types::{ + messages::{ApprovalDistributionMessage, BitfieldDistributionMessage, NetworkBridgeEvent}, + OverseerSignal, +}; +use sc_network::{request_responses::ProtocolConfig, RequestFailure}; + +const LOG_TARGET: &str = "subsystem-bench::network-bridge"; +const CHUNK_REQ_PROTOCOL_NAME_V1: &str = + "/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/req_chunk/1"; + +/// A mock of the network bridge tx subsystem. +pub struct MockNetworkBridgeTx { + /// A network emulator handle + network: NetworkEmulatorHandle, + /// A channel to the network interface, + to_network_interface: UnboundedSender, + /// Test authorithies + test_authorithies: TestAuthorities, +} + +/// A mock of the network bridge tx subsystem. +pub struct MockNetworkBridgeRx { + /// A network interface receiver + network_receiver: NetworkInterfaceReceiver, + /// Chunk request sender + chunk_request_sender: Option, +} + +impl MockNetworkBridgeTx { + pub fn new( + network: NetworkEmulatorHandle, + to_network_interface: UnboundedSender, + test_authorithies: TestAuthorities, + ) -> MockNetworkBridgeTx { + Self { network, to_network_interface, test_authorithies } + } +} + +impl MockNetworkBridgeRx { + pub fn new( + network_receiver: NetworkInterfaceReceiver, + chunk_request_sender: Option, + ) -> MockNetworkBridgeRx { + Self { network_receiver, chunk_request_sender } + } +} + +#[overseer::subsystem(NetworkBridgeTx, error=SubsystemError, prefix=self::overseer)] +impl MockNetworkBridgeTx { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self.run(ctx).map(|_| Ok(())).boxed(); + + SpawnedSubsystem { name: "network-bridge-tx", future } + } +} + +#[overseer::subsystem(NetworkBridgeRx, error=SubsystemError, prefix=self::overseer)] +impl MockNetworkBridgeRx { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self.run(ctx).map(|_| Ok(())).boxed(); + + SpawnedSubsystem { name: "network-bridge-rx", future } + } +} + +#[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)] +impl MockNetworkBridgeTx { + async fn run(self, mut ctx: Context) { + // Main subsystem loop. + loop { + let subsystem_message = ctx.recv().await.expect("Overseer never fails us"); + match subsystem_message { + orchestra::FromOrchestra::Signal(signal) => + if signal == OverseerSignal::Conclude { + return + }, + orchestra::FromOrchestra::Communication { msg } => match msg { + NetworkBridgeTxMessage::SendRequests(requests, _if_disconnected) => { + for request in requests { + gum::debug!(target: LOG_TARGET, request = ?request, "Processing request"); + let peer_id = + request.authority_id().expect("all nodes are authorities").clone(); + + if !self.network.is_peer_connected(&peer_id) { + // Attempting to send a request to a disconnected peer. + request + .into_response_sender() + .send(Err(RequestFailure::NotConnected)) + .expect("send never fails"); + continue + } + + let peer_message = + NetworkMessage::RequestFromNode(peer_id.clone(), request); + + let _ = self.to_network_interface.unbounded_send(peer_message); + } + }, + NetworkBridgeTxMessage::ReportPeer(_) => { + // ingore rep changes + }, + NetworkBridgeTxMessage::SendValidationMessage(peers, message) => { + for peer in peers { + self.to_network_interface + .unbounded_send(NetworkMessage::MessageFromNode( + self.test_authorithies + .peer_id_to_authority + .get(&peer) + .unwrap() + .clone(), + message.clone(), + )) + .expect("Should not fail"); + } + }, + _ => unimplemented!("Unexpected network bridge message"), + }, + } + } + } +} + +#[overseer::contextbounds(NetworkBridgeRx, prefix = self::overseer)] +impl MockNetworkBridgeRx { + async fn run(mut self, mut ctx: Context) { + // Main subsystem loop. + let mut from_network_interface = self.network_receiver.0; + loop { + futures::select! { + maybe_peer_message = from_network_interface.next() => { + if let Some(message) = maybe_peer_message { + match message { + NetworkMessage::MessageFromPeer(peer_id, message) => match message { + Versioned::V2( + polkadot_node_network_protocol::v2::ValidationProtocol::BitfieldDistribution( + bitfield, + ), + ) => { + ctx.send_message( + BitfieldDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage(peer_id, polkadot_node_network_protocol::Versioned::V2(bitfield))) + ).await; + }, + Versioned::V3( + polkadot_node_network_protocol::v3::ValidationProtocol::ApprovalDistribution(msg) + ) => { + ctx.send_message( + ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage(peer_id, polkadot_node_network_protocol::Versioned::V3(msg))) + ).await; + } + _ => { + unimplemented!("We only talk v2 network protocol") + }, + }, + NetworkMessage::RequestFromPeer(request) => { + if let Some(protocol) = self.chunk_request_sender.as_mut() { + assert_eq!(&*protocol.name, CHUNK_REQ_PROTOCOL_NAME_V1); + if let Some(inbound_queue) = protocol.inbound_queue.as_ref() { + inbound_queue + .send(request) + .await + .expect("Forwarding requests to subsystem never fails"); + } + } + }, + _ => { + panic!("NetworkMessage::RequestFromNode is not expected to be received from a peer") + } + } + } + }, + subsystem_message = ctx.recv().fuse() => { + match subsystem_message.expect("Overseer never fails us") { + orchestra::FromOrchestra::Signal(signal) => if signal == OverseerSignal::Conclude { return }, + _ => { + unimplemented!("Unexpected network bridge rx message") + }, + } + } + } + } + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs new file mode 100644 index 0000000000000000000000000000000000000000..53faf562f03c005238d5297c29117fd261b49eca --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -0,0 +1,233 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! A generic runtime api subsystem mockup suitable to be used in benchmarks. + +use crate::configuration::{TestAuthorities, TestConfiguration}; +use bitvec::prelude::BitVec; +use futures::FutureExt; +use itertools::Itertools; +use polkadot_node_subsystem::{ + messages::{RuntimeApiMessage, RuntimeApiRequest}, + overseer, SpawnedSubsystem, SubsystemError, +}; +use polkadot_node_subsystem_types::OverseerSignal; +use polkadot_primitives::{ + vstaging::NodeFeatures, CandidateEvent, CandidateReceipt, CoreState, GroupIndex, IndexedVec, + OccupiedCore, SessionIndex, SessionInfo, ValidatorIndex, +}; +use sp_consensus_babe::Epoch as BabeEpoch; +use sp_core::H256; +use std::collections::HashMap; + +const LOG_TARGET: &str = "subsystem-bench::runtime-api-mock"; + +/// Minimal state to answer requests. +pub struct RuntimeApiState { + // All authorities in the test, + authorities: TestAuthorities, + // Candidate hashes per block + candidate_hashes: HashMap>, + // Included candidates per bock + included_candidates: HashMap>, + babe_epoch: Option, + // The session child index, + session_index: SessionIndex, +} + +/// A mocked `runtime-api` subsystem. +pub struct MockRuntimeApi { + state: RuntimeApiState, + config: TestConfiguration, +} + +impl MockRuntimeApi { + pub fn new( + config: TestConfiguration, + authorities: TestAuthorities, + candidate_hashes: HashMap>, + included_candidates: HashMap>, + babe_epoch: Option, + session_index: SessionIndex, + ) -> MockRuntimeApi { + Self { + state: RuntimeApiState { + authorities, + candidate_hashes, + included_candidates, + babe_epoch, + session_index, + }, + config, + } + } + + fn session_info(&self) -> SessionInfo { + session_info_for_peers(&self.config, &self.state.authorities) + } +} + +/// Generates a test session info with all passed authorities as consensus validators. +pub fn session_info_for_peers( + configuration: &TestConfiguration, + authorities: &TestAuthorities, +) -> SessionInfo { + let all_validators = (0..configuration.n_validators) + .map(|i| ValidatorIndex(i as _)) + .collect::>(); + + let validator_groups = all_validators + .chunks(configuration.max_validators_per_core) + .map(Vec::from) + .collect::>(); + + SessionInfo { + validators: authorities.validator_public.iter().cloned().collect(), + discovery_keys: authorities.validator_authority_id.to_vec(), + assignment_keys: authorities.validator_assignment_id.to_vec(), + validator_groups: IndexedVec::>::from(validator_groups), + n_cores: configuration.n_cores as u32, + needed_approvals: configuration.needed_approvals as u32, + zeroth_delay_tranche_width: configuration.zeroth_delay_tranche_width as u32, + relay_vrf_modulo_samples: configuration.relay_vrf_modulo_samples as u32, + n_delay_tranches: configuration.n_delay_tranches as u32, + no_show_slots: configuration.no_show_slots as u32, + active_validator_indices: (0..authorities.validator_authority_id.len()) + .map(|index| ValidatorIndex(index as u32)) + .collect_vec(), + dispute_period: 6, + random_seed: [0u8; 32], + } +} + +#[overseer::subsystem(RuntimeApi, error=SubsystemError, prefix=self::overseer)] +impl MockRuntimeApi { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self.run(ctx).map(|_| Ok(())).boxed(); + + SpawnedSubsystem { name: "test-environment", future } + } +} + +#[overseer::contextbounds(RuntimeApi, prefix = self::overseer)] +impl MockRuntimeApi { + async fn run(self, mut ctx: Context) { + let validator_group_count = self.session_info().validator_groups.len(); + + loop { + let msg = ctx.recv().await.expect("Overseer never fails us"); + + match msg { + orchestra::FromOrchestra::Signal(signal) => + if signal == OverseerSignal::Conclude { + return + }, + orchestra::FromOrchestra::Communication { msg } => { + gum::debug!(target: LOG_TARGET, msg=?msg, "recv message"); + + match msg { + RuntimeApiMessage::Request( + request, + RuntimeApiRequest::CandidateEvents(sender), + ) => { + let candidate_events = self.state.included_candidates.get(&request); + let _ = sender.send(Ok(candidate_events.cloned().unwrap_or_default())); + }, + RuntimeApiMessage::Request( + _block_hash, + RuntimeApiRequest::SessionInfo(_session_index, sender), + ) => { + let _ = sender.send(Ok(Some(self.session_info()))); + }, + RuntimeApiMessage::Request( + _block_hash, + RuntimeApiRequest::SessionExecutorParams(_session_index, sender), + ) => { + let _ = sender.send(Ok(Some(Default::default()))); + }, + RuntimeApiMessage::Request( + _request, + RuntimeApiRequest::NodeFeatures(_session_index, sender), + ) => { + let _ = sender.send(Ok(NodeFeatures::EMPTY)); + }, + RuntimeApiMessage::Request( + _block_hash, + RuntimeApiRequest::Validators(sender), + ) => { + let _ = + sender.send(Ok(self.state.authorities.validator_public.clone())); + }, + RuntimeApiMessage::Request( + _block_hash, + RuntimeApiRequest::SessionIndexForChild(sender), + ) => { + // Session is always the same. + let _ = sender.send(Ok(self.state.session_index)); + }, + RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::AvailabilityCores(sender), + ) => { + let candidate_hashes = self + .state + .candidate_hashes + .get(&block_hash) + .expect("Relay chain block hashes are generated at test start"); + + // All cores are always occupied. + let cores = candidate_hashes + .iter() + .enumerate() + .map(|(index, candidate_receipt)| { + // Ensure test breaks if badly configured. + assert!(index < validator_group_count); + + CoreState::Occupied(OccupiedCore { + next_up_on_available: None, + occupied_since: 0, + time_out_at: 0, + next_up_on_time_out: None, + availability: BitVec::default(), + group_responsible: GroupIndex(index as u32), + candidate_hash: candidate_receipt.hash(), + candidate_descriptor: candidate_receipt.descriptor.clone(), + }) + }) + .collect::>(); + + let _ = sender.send(Ok(cores)); + }, + RuntimeApiMessage::Request( + _request, + RuntimeApiRequest::CurrentBabeEpoch(sender), + ) => { + let _ = sender.send(Ok(self + .state + .babe_epoch + .clone() + .expect("Babe epoch unpopulated"))); + }, + // Long term TODO: implement more as needed. + message => { + unimplemented!("Unexpected runtime-api message: {:?}", message) + }, + } + }, + } + } + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs new file mode 100644 index 0000000000000000000000000000000000000000..1e09441792d59ee79f14297b34375ef7452c935e --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -0,0 +1,1069 @@ +// 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 . + +//! Implements network emulation and interfaces to control and specialize +//! network peer behaviour. + +// [TestEnvironment] +// [NetworkEmulatorHandle] +// || +// +-------+--||--+-------+ +// | | | | +// Peer1 Peer2 Peer3 Peer4 +// \ | | / +// \ | | / +// \ | | / +// \ | | / +// \ | | / +// [Network Interface] +// | +// [Emulated Network Bridge] +// | +// Subsystems under test + +use crate::{ + configuration::{random_latency, TestAuthorities, TestConfiguration}, + environment::TestEnvironmentDependencies, + NODE_UNDER_TEST, +}; +use colored::Colorize; +use futures::{ + channel::{ + mpsc, + mpsc::{UnboundedReceiver, UnboundedSender}, + oneshot, + }, + lock::Mutex, + stream::FuturesUnordered, + Future, FutureExt, StreamExt, +}; +use itertools::Itertools; +use net_protocol::{ + peer_set::{ProtocolVersion, ValidationVersion}, + request_response::{Recipient, Requests, ResponseSender}, + ObservedRole, VersionedValidationProtocol, +}; +use parity_scale_codec::Encode; +use polkadot_node_network_protocol::{self as net_protocol, Versioned}; +use polkadot_node_subsystem_types::messages::{ApprovalDistributionMessage, NetworkBridgeEvent}; +use polkadot_node_subsystem_util::metrics::prometheus::{ + self, CounterVec, Opts, PrometheusError, Registry, +}; +use polkadot_overseer::AllMessages; +use polkadot_primitives::AuthorityDiscoveryId; +use prometheus_endpoint::U64; +use rand::{seq::SliceRandom, thread_rng}; +use sc_network::{ + request_responses::{IncomingRequest, OutgoingResponse}, + PeerId, RequestFailure, +}; +use sc_service::SpawnTaskHandle; +use std::{ + collections::HashMap, + sync::Arc, + task::Poll, + time::{Duration, Instant}, +}; + +const LOG_TARGET: &str = "subsystem-bench::network"; + +// An emulated node egress traffic rate_limiter. +#[derive(Debug)] +pub struct RateLimit { + // How often we refill credits in buckets + tick_rate: usize, + // Total ticks + total_ticks: usize, + // Max refill per tick + max_refill: usize, + // Available credit. We allow for bursts over 1/tick_rate of `cps` budget, but we + // account it by negative credit. + credits: isize, + // When last refilled. + last_refill: Instant, +} + +impl RateLimit { + // Create a new `RateLimit` from a `cps` (credits per second) budget and + // `tick_rate`. + pub fn new(tick_rate: usize, cps: usize) -> Self { + // Compute how much refill for each tick + let max_refill = cps / tick_rate; + RateLimit { + tick_rate, + total_ticks: 0, + max_refill, + // A fresh start + credits: max_refill as isize, + last_refill: Instant::now(), + } + } + + pub async fn refill(&mut self) { + // If this is called to early, we need to sleep until next tick. + let now = Instant::now(); + let next_tick_delta = + (self.last_refill + Duration::from_millis(1000 / self.tick_rate as u64)) - now; + + // Sleep until next tick. + if !next_tick_delta.is_zero() { + gum::trace!(target: LOG_TARGET, "need to sleep {}ms", next_tick_delta.as_millis()); + tokio::time::sleep(next_tick_delta).await; + } + + self.total_ticks += 1; + self.credits += self.max_refill as isize; + self.last_refill = Instant::now(); + } + + // Reap credits from the bucket. + // Blocks if credits budged goes negative during call. + pub async fn reap(&mut self, amount: usize) { + self.credits -= amount as isize; + + if self.credits >= 0 { + return + } + + while self.credits < 0 { + gum::trace!(target: LOG_TARGET, "Before refill: {:?}", &self); + self.refill().await; + gum::trace!(target: LOG_TARGET, "After refill: {:?}", &self); + } + } +} + +/// A wrapper for both gossip and request/response protocols along with the destination +/// peer(`AuthorityDiscoveryId``). +pub enum NetworkMessage { + /// A gossip message from peer to node. + MessageFromPeer(PeerId, VersionedValidationProtocol), + /// A gossip message from node to a peer. + MessageFromNode(AuthorityDiscoveryId, VersionedValidationProtocol), + /// A request originating from our node + RequestFromNode(AuthorityDiscoveryId, Requests), + /// A request originating from an emultated peer + RequestFromPeer(IncomingRequest), +} + +impl NetworkMessage { + /// Returns the size of the encoded message or request + pub fn size(&self) -> usize { + match &self { + NetworkMessage::MessageFromPeer(_, Versioned::V2(message)) => message.encoded_size(), + NetworkMessage::MessageFromPeer(_, Versioned::V1(message)) => message.encoded_size(), + NetworkMessage::MessageFromPeer(_, Versioned::V3(message)) => message.encoded_size(), + NetworkMessage::MessageFromNode(_peer_id, Versioned::V2(message)) => + message.encoded_size(), + NetworkMessage::MessageFromNode(_peer_id, Versioned::V1(message)) => + message.encoded_size(), + NetworkMessage::MessageFromNode(_peer_id, Versioned::V3(message)) => + message.encoded_size(), + NetworkMessage::RequestFromNode(_peer_id, incoming) => incoming.size(), + NetworkMessage::RequestFromPeer(request) => request.payload.encoded_size(), + } + } + + /// Returns the destination peer from the message or `None` if it originates from a peer. + pub fn peer(&self) -> Option<&AuthorityDiscoveryId> { + match &self { + NetworkMessage::MessageFromNode(peer_id, _) | + NetworkMessage::RequestFromNode(peer_id, _) => Some(peer_id), + _ => None, + } + } +} + +/// A network interface of the node under test. +pub struct NetworkInterface { + // Sender for subsystems. + bridge_to_interface_sender: UnboundedSender, +} + +// Wraps the receiving side of a interface to bridge channel. It is a required +// parameter of the `network-bridge` mock. +pub struct NetworkInterfaceReceiver(pub UnboundedReceiver); + +struct ProxiedRequest { + sender: Option>, + receiver: oneshot::Receiver, +} + +struct ProxiedResponse { + pub sender: oneshot::Sender, + pub result: Result, RequestFailure>, +} + +impl Future for ProxiedRequest { + // The sender and result. + type Output = ProxiedResponse; + + fn poll( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + match self.receiver.poll_unpin(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(response) => Poll::Ready(ProxiedResponse { + sender: self.sender.take().expect("sender already used"), + result: response + .expect("Response is always succesfully received.") + .result + .map_err(|_| RequestFailure::Refused), + }), + } + } +} + +impl NetworkInterface { + /// Create a new `NetworkInterface` + pub fn new( + spawn_task_handle: SpawnTaskHandle, + network: NetworkEmulatorHandle, + bandwidth_bps: usize, + mut from_network: UnboundedReceiver, + ) -> (NetworkInterface, NetworkInterfaceReceiver) { + let rx_limiter = Arc::new(Mutex::new(RateLimit::new(10, bandwidth_bps))); + let tx_limiter = Arc::new(Mutex::new(RateLimit::new(10, bandwidth_bps))); + + // Channel for receiving messages from the network bridge subsystem. + let (bridge_to_interface_sender, mut bridge_to_interface_receiver) = + mpsc::unbounded::(); + + // Channel for forwarding messages to the network bridge subsystem. + let (interface_to_bridge_sender, interface_to_bridge_receiver) = + mpsc::unbounded::(); + + let rx_network = network.clone(); + let tx_network = network; + + let rx_task_bridge_sender = interface_to_bridge_sender.clone(); + + let task_rx_limiter = rx_limiter.clone(); + let task_tx_limiter = tx_limiter.clone(); + + // A task that forwards messages from emulated peers to the node (emulated network bridge). + let rx_task = async move { + let mut proxied_requests = FuturesUnordered::new(); + + loop { + let mut from_network = from_network.next().fuse(); + futures::select! { + maybe_peer_message = from_network => { + if let Some(peer_message) = maybe_peer_message { + let size = peer_message.size(); + task_rx_limiter.lock().await.reap(size).await; + rx_network.inc_received(size); + + // To be able to apply the configured bandwidth limits for responses being sent + // over channels, we need to implement a simple proxy that allows this loop + // to receive the response and enforce the configured bandwidth before + // sending it to the original recipient. + if let NetworkMessage::RequestFromPeer(request) = peer_message { + let (response_sender, response_receiver) = oneshot::channel(); + + // Create a new `IncomingRequest` that we forward to the network bridge. + let new_request = IncomingRequest {payload: request.payload, peer: request.peer, pending_response: response_sender}; + proxied_requests.push(ProxiedRequest {sender: Some(request.pending_response), receiver: response_receiver}); + + // Send the new message to network bridge subsystem. + rx_task_bridge_sender + .unbounded_send(NetworkMessage::RequestFromPeer(new_request)) + .expect("network bridge subsystem is alive"); + continue + } + + // Forward the message to the bridge. + rx_task_bridge_sender + .unbounded_send(peer_message) + .expect("network bridge subsystem is alive"); + } else { + gum::info!(target: LOG_TARGET, "Uplink channel closed, network interface task exiting"); + break + } + }, + proxied_request = proxied_requests.next() => { + if let Some(proxied_request) = proxied_request { + match proxied_request.result { + Ok(result) => { + let bytes = result.encoded_size(); + gum::trace!(target: LOG_TARGET, size = bytes, "proxied request completed"); + + // Enforce bandwidth based on the response the node has sent. + // TODO: Fix the stall of RX when TX lock() takes a while to refill + // the token bucket. Good idea would be to create a task for each request. + task_tx_limiter.lock().await.reap(bytes).await; + rx_network.inc_sent(bytes); + + // Forward the response to original recipient. + proxied_request.sender.send( + OutgoingResponse { + reputation_changes: Vec::new(), + result: Ok(result), + sent_feedback: None + } + ).expect("network is alive"); + } + Err(e) => { + gum::warn!(target: LOG_TARGET, "Node req/response failure: {:?}", e) + } + } + } else { + gum::debug!(target: LOG_TARGET, "No more active proxied requests"); + // break + } + } + } + } + } + .boxed(); + + let task_spawn_handle = spawn_task_handle.clone(); + let task_rx_limiter = rx_limiter.clone(); + let task_tx_limiter = tx_limiter.clone(); + + // A task that forwards messages from the node to emulated peers. + let tx_task = async move { + // Wrap it in an `Arc` to avoid `clone()` the inner data as we need to share it across + // many send tasks. + let tx_network = Arc::new(tx_network); + + loop { + if let Some(peer_message) = bridge_to_interface_receiver.next().await { + let size = peer_message.size(); + // Ensure bandwidth used is limited. + task_tx_limiter.lock().await.reap(size).await; + + match peer_message { + NetworkMessage::MessageFromNode(peer, message) => + tx_network.send_message_to_peer(&peer, message), + NetworkMessage::RequestFromNode(peer, request) => { + // Send request through a proxy so we can account and limit bandwidth + // usage for the node. + let send_task = Self::proxy_send_request( + peer.clone(), + request, + tx_network.clone(), + task_rx_limiter.clone(), + ) + .boxed(); + + task_spawn_handle.spawn("request-proxy", "test-environment", send_task); + }, + _ => panic!( + "Unexpected network message received from emulated network bridge" + ), + } + + tx_network.inc_sent(size); + } else { + gum::info!(target: LOG_TARGET, "Downlink channel closed, network interface task exiting"); + break + } + } + } + .boxed(); + + spawn_task_handle.spawn("network-interface-rx", "test-environment", rx_task); + spawn_task_handle.spawn("network-interface-tx", "test-environment", tx_task); + + ( + Self { bridge_to_interface_sender }, + NetworkInterfaceReceiver(interface_to_bridge_receiver), + ) + } + + /// Get a sender that can be used by a subsystem to send network actions to the network. + pub fn subsystem_sender(&self) -> UnboundedSender { + self.bridge_to_interface_sender.clone() + } + + /// Helper method that proxies a request from node to peer and implements rate limiting and + /// accounting. + async fn proxy_send_request( + peer: AuthorityDiscoveryId, + mut request: Requests, + tx_network: Arc, + task_rx_limiter: Arc>, + ) { + let (proxy_sender, proxy_receiver) = oneshot::channel(); + + // Modify the request response sender so we can intercept the answer + let sender = request.swap_response_sender(proxy_sender); + + // Send the modified request to the peer. + tx_network.send_request_to_peer(&peer, request); + + // Wait for answer (intercept the response). + match proxy_receiver.await { + Err(_) => { + panic!("Emulated peer hangup"); + }, + Ok(Err(err)) => { + sender.send(Err(err)).expect("Oneshot send always works."); + }, + Ok(Ok((response, protocol_name))) => { + let response_size = response.encoded_size(); + task_rx_limiter.lock().await.reap(response_size).await; + tx_network.inc_received(response_size); + + // Send the response to the original request sender. + if sender.send(Ok((response, protocol_name))).is_err() { + gum::warn!(target: LOG_TARGET, response_size, "response oneshot canceled by node") + } + }, + }; + } +} + +/// A handle for controlling an emulated peer. +#[derive(Clone)] +pub struct EmulatedPeerHandle { + /// Send messages to be processed by the peer. + messages_tx: UnboundedSender, + /// Send actions to be performed by the peer. + actions_tx: UnboundedSender, + peer_id: PeerId, +} + +impl EmulatedPeerHandle { + /// Receive and process a message from the node. + pub fn receive(&self, message: NetworkMessage) { + self.messages_tx.unbounded_send(message).expect("Peer message channel hangup"); + } + + /// Send a message to the node. + pub fn send_message(&self, message: VersionedValidationProtocol) { + self.actions_tx + .unbounded_send(NetworkMessage::MessageFromPeer(self.peer_id, message)) + .expect("Peer action channel hangup"); + } + + /// Send a `request` to the node. + pub fn send_request(&self, request: IncomingRequest) { + self.actions_tx + .unbounded_send(NetworkMessage::RequestFromPeer(request)) + .expect("Peer action channel hangup"); + } +} + +// A network peer emulator. +struct EmulatedPeer { + spawn_handle: SpawnTaskHandle, + to_node: UnboundedSender, + tx_limiter: RateLimit, + rx_limiter: RateLimit, + latency_ms: usize, +} + +impl EmulatedPeer { + /// Send a message to the node. + pub async fn send_message(&mut self, message: NetworkMessage) { + self.tx_limiter.reap(message.size()).await; + + if self.latency_ms == 0 { + self.to_node.unbounded_send(message).expect("Sending to the node never fails"); + } else { + let to_node = self.to_node.clone(); + let latency_ms = std::time::Duration::from_millis(self.latency_ms as u64); + + // Emulate RTT latency + self.spawn_handle + .spawn("peer-latency-emulator", "test-environment", async move { + tokio::time::sleep(latency_ms).await; + to_node.unbounded_send(message).expect("Sending to the node never fails"); + }); + } + } + + /// Returns the rx bandwidth limiter. + pub fn rx_limiter(&mut self) -> &mut RateLimit { + &mut self.rx_limiter + } +} + +/// Interceptor pattern for handling messages. +pub trait HandleNetworkMessage { + /// Returns `None` if the message was handled, or the `message` + /// otherwise. + /// + /// `node_sender` allows sending of messages to the node in response + /// to the handled message. + fn handle( + &self, + message: NetworkMessage, + node_sender: &mut UnboundedSender, + ) -> Option; +} + +impl HandleNetworkMessage for Arc +where + T: HandleNetworkMessage, +{ + fn handle( + &self, + message: NetworkMessage, + node_sender: &mut UnboundedSender, + ) -> Option { + self.as_ref().handle(message, node_sender) + } +} + +// This loop is responsible for handling of messages/requests between the peer and the node. +async fn emulated_peer_loop( + handlers: Vec>, + stats: Arc, + mut emulated_peer: EmulatedPeer, + messages_rx: UnboundedReceiver, + actions_rx: UnboundedReceiver, + mut to_network_interface: UnboundedSender, +) { + let mut proxied_requests = FuturesUnordered::new(); + let mut messages_rx = messages_rx.fuse(); + let mut actions_rx = actions_rx.fuse(); + + loop { + futures::select! { + maybe_peer_message = messages_rx.next() => { + if let Some(peer_message) = maybe_peer_message { + let size = peer_message.size(); + + emulated_peer.rx_limiter().reap(size).await; + stats.inc_received(size); + + let mut message = Some(peer_message); + + // Try all handlers until the message gets processed. + // Panic if the message is not consumed. + for handler in handlers.iter() { + // The check below guarantees that message is always `Some`: we are still + // inside the loop. + message = handler.handle(message.unwrap(), &mut to_network_interface); + if message.is_none() { + break + } + } + if let Some(message) = message { + panic!("Emulated message from peer {:?} not handled", message.peer()); + } + } else { + gum::debug!(target: LOG_TARGET, "Downlink channel closed, peer task exiting"); + break + } + }, + maybe_action = actions_rx.next() => { + match maybe_action { + // We proxy any request being sent to the node to limit bandwidth as we + // do in the `NetworkInterface` task. + Some(NetworkMessage::RequestFromPeer(request)) => { + let (response_sender, response_receiver) = oneshot::channel(); + // Create a new `IncomingRequest` that we forward to the network interface. + let new_request = IncomingRequest {payload: request.payload, peer: request.peer, pending_response: response_sender}; + + proxied_requests.push(ProxiedRequest {sender: Some(request.pending_response), receiver: response_receiver}); + + emulated_peer.send_message(NetworkMessage::RequestFromPeer(new_request)).await; + }, + Some(message) => emulated_peer.send_message(message).await, + None => { + gum::debug!(target: LOG_TARGET, "Action channel closed, peer task exiting"); + break + } + } + }, + proxied_request = proxied_requests.next() => { + if let Some(proxied_request) = proxied_request { + match proxied_request.result { + Ok(result) => { + let bytes = result.encoded_size(); + gum::trace!(target: LOG_TARGET, size = bytes, "Peer proxied request completed"); + + emulated_peer.rx_limiter().reap(bytes).await; + stats.inc_received(bytes); + + proxied_request.sender.send( + OutgoingResponse { + reputation_changes: Vec::new(), + result: Ok(result), + sent_feedback: None + } + ).expect("network is alive"); + } + Err(e) => { + gum::warn!(target: LOG_TARGET, "Node req/response failure: {:?}", e) + } + } + } + } + } + } +} + +/// Creates a new peer emulator task and returns a handle to it. +pub fn new_peer( + bandwidth: usize, + spawn_task_handle: SpawnTaskHandle, + handlers: Vec>, + stats: Arc, + to_network_interface: UnboundedSender, + latency_ms: usize, + peer_id: PeerId, +) -> EmulatedPeerHandle { + let (messages_tx, messages_rx) = mpsc::unbounded::(); + let (actions_tx, actions_rx) = mpsc::unbounded::(); + + let rx_limiter = RateLimit::new(10, bandwidth); + let tx_limiter = RateLimit::new(10, bandwidth); + let emulated_peer = EmulatedPeer { + spawn_handle: spawn_task_handle.clone(), + rx_limiter, + tx_limiter, + to_node: to_network_interface.clone(), + latency_ms, + }; + + spawn_task_handle.clone().spawn( + "peer-emulator", + "test-environment", + emulated_peer_loop( + handlers, + stats, + emulated_peer, + messages_rx, + actions_rx, + to_network_interface, + ) + .boxed(), + ); + + EmulatedPeerHandle { messages_tx, actions_tx, peer_id } +} + +/// Book keeping of sent and received bytes. +pub struct PeerEmulatorStats { + metrics: Metrics, + peer_index: usize, +} + +impl PeerEmulatorStats { + pub(crate) fn new(peer_index: usize, metrics: Metrics) -> Self { + Self { metrics, peer_index } + } + + pub fn inc_sent(&self, bytes: usize) { + self.metrics.on_peer_sent(self.peer_index, bytes); + } + + pub fn inc_received(&self, bytes: usize) { + self.metrics.on_peer_received(self.peer_index, bytes); + } + + pub fn sent(&self) -> usize { + self.metrics + .peer_total_sent + .get_metric_with_label_values(&[&format!("node{}", self.peer_index)]) + .expect("Metric exists") + .get() as usize + } + + pub fn received(&self) -> usize { + self.metrics + .peer_total_received + .get_metric_with_label_values(&[&format!("node{}", self.peer_index)]) + .expect("Metric exists") + .get() as usize + } +} + +/// The state of a peer on the emulated network. +#[derive(Clone)] +enum Peer { + Connected(EmulatedPeerHandle), + Disconnected(EmulatedPeerHandle), +} + +impl Peer { + pub fn disconnect(&mut self) { + let new_self = match self { + Peer::Connected(peer) => Peer::Disconnected(peer.clone()), + _ => return, + }; + *self = new_self; + } + + pub fn is_connected(&self) -> bool { + matches!(self, Peer::Connected(_)) + } + + pub fn handle(&self) -> &EmulatedPeerHandle { + match self { + Peer::Connected(ref emulator) => emulator, + Peer::Disconnected(ref emulator) => emulator, + } + } +} + +/// A ha emulated network implementation. +#[derive(Clone)] +pub struct NetworkEmulatorHandle { + // Per peer network emulation. + peers: Vec, + /// Per peer stats. + stats: Vec>, + /// Each emulated peer is a validator. + validator_authority_ids: HashMap, +} + +impl NetworkEmulatorHandle { + /// Generates peer_connected messages for all peers in `test_authorities` + pub fn generate_peer_connected(&self) -> Vec { + self.peers + .iter() + .filter(|peer| peer.is_connected()) + .map(|peer| { + let network = NetworkBridgeEvent::PeerConnected( + peer.handle().peer_id, + ObservedRole::Full, + ProtocolVersion::from(ValidationVersion::V3), + None, + ); + + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate( + network, + )) + }) + .collect_vec() + } +} + +/// Create a new emulated network based on `config`. +/// Each emulated peer will run the specified `handlers` to process incoming messages. +pub fn new_network( + config: &TestConfiguration, + dependencies: &TestEnvironmentDependencies, + authorities: &TestAuthorities, + handlers: Vec>, +) -> (NetworkEmulatorHandle, NetworkInterface, NetworkInterfaceReceiver) { + let n_peers = config.n_validators; + gum::info!(target: LOG_TARGET, "{}",format!("Initializing emulation for a {} peer network.", n_peers).bright_blue()); + gum::info!(target: LOG_TARGET, "{}",format!("connectivity {}%, latency {:?}", config.connectivity, config.latency).bright_black()); + + let metrics = + Metrics::new(&dependencies.registry).expect("Metrics always register succesfully"); + let mut validator_authority_id_mapping = HashMap::new(); + + // Create the channel from `peer` to `NetworkInterface` . + let (to_network_interface, from_network) = mpsc::unbounded(); + + // Create a `PeerEmulator` for each peer. + let (stats, mut peers): (_, Vec<_>) = (0..n_peers) + .zip(authorities.validator_authority_id.clone()) + .map(|(peer_index, authority_id)| { + validator_authority_id_mapping.insert(authority_id, peer_index); + let stats = Arc::new(PeerEmulatorStats::new(peer_index, metrics.clone())); + ( + stats.clone(), + Peer::Connected(new_peer( + config.peer_bandwidth, + dependencies.task_manager.spawn_handle(), + handlers.clone(), + stats, + to_network_interface.clone(), + random_latency(config.latency.as_ref()), + *authorities.peer_ids.get(peer_index).unwrap(), + )), + ) + }) + .unzip(); + + let connected_count = config.connected_count(); + + let mut peers_indicies = (0..n_peers).collect_vec(); + let (_connected, to_disconnect) = + peers_indicies.partial_shuffle(&mut thread_rng(), connected_count); + + // Node under test is always mark as disconnected. + peers[NODE_UNDER_TEST as usize].disconnect(); + for peer in to_disconnect.iter().skip(1) { + peers[*peer].disconnect(); + } + + gum::info!(target: LOG_TARGET, "{}",format!("Network created, connected validator count {}", connected_count).bright_black()); + + let handle = NetworkEmulatorHandle { + peers, + stats, + validator_authority_ids: validator_authority_id_mapping, + }; + + // Finally create the `NetworkInterface` with the `from_network` receiver. + let (network_interface, network_interface_receiver) = NetworkInterface::new( + dependencies.task_manager.spawn_handle(), + handle.clone(), + config.bandwidth, + from_network, + ); + + (handle, network_interface, network_interface_receiver) +} + +/// Errors that can happen when sending data to emulated peers. +#[derive(Clone, Debug)] +pub enum EmulatedPeerError { + NotConnected, +} + +impl NetworkEmulatorHandle { + /// Returns true if the emulated peer is connected to the node under test. + pub fn is_peer_connected(&self, peer: &AuthorityDiscoveryId) -> bool { + self.peer(peer).is_connected() + } + + /// Forward notification `message` to an emulated `peer`. + /// Panics if peer is not connected. + pub fn send_message_to_peer( + &self, + peer_id: &AuthorityDiscoveryId, + message: VersionedValidationProtocol, + ) { + let peer = self.peer(peer_id); + assert!(peer.is_connected(), "forward message only for connected peers."); + peer.handle().receive(NetworkMessage::MessageFromNode(peer_id.clone(), message)); + } + + /// Forward a `request`` to an emulated `peer`. + /// Panics if peer is not connected. + pub fn send_request_to_peer(&self, peer_id: &AuthorityDiscoveryId, request: Requests) { + let peer = self.peer(peer_id); + assert!(peer.is_connected(), "forward request only for connected peers."); + peer.handle().receive(NetworkMessage::RequestFromNode(peer_id.clone(), request)); + } + + /// Send a message from a peer to the node. + pub fn send_message_from_peer( + &self, + from_peer: &AuthorityDiscoveryId, + message: VersionedValidationProtocol, + ) -> Result<(), EmulatedPeerError> { + let dst_peer = self.peer(from_peer); + + if !dst_peer.is_connected() { + gum::warn!(target: LOG_TARGET, "Attempted to send message from a peer not connected to our node, operation ignored"); + return Err(EmulatedPeerError::NotConnected) + } + + dst_peer.handle().send_message(message); + Ok(()) + } + + /// Send a request from a peer to the node. + pub fn send_request_from_peer( + &self, + from_peer: &AuthorityDiscoveryId, + request: IncomingRequest, + ) -> Result<(), EmulatedPeerError> { + let dst_peer = self.peer(from_peer); + + if !dst_peer.is_connected() { + gum::warn!(target: LOG_TARGET, "Attempted to send request from a peer not connected to our node, operation ignored"); + return Err(EmulatedPeerError::NotConnected) + } + + dst_peer.handle().send_request(request); + Ok(()) + } + + // Returns the sent/received stats for `peer_index`. + pub fn peer_stats(&self, peer_index: usize) -> Arc { + self.stats[peer_index].clone() + } + + // Helper to get peer index by `AuthorityDiscoveryId` + fn peer_index(&self, peer: &AuthorityDiscoveryId) -> usize { + *self + .validator_authority_ids + .get(peer) + .expect("all test authorities are valid; qed") + } + + // Return the Peer entry for a given `AuthorityDiscoveryId`. + fn peer(&self, peer: &AuthorityDiscoveryId) -> &Peer { + &self.peers[self.peer_index(peer)] + } + + // Increment bytes sent by our node (the node that contains the subsystem under test) + pub fn inc_sent(&self, bytes: usize) { + // Our node is always peer 0. + self.peer_stats(0).inc_sent(bytes); + } + + // Increment bytes received by our node (the node that contains the subsystem under test) + pub fn inc_received(&self, bytes: usize) { + // Our node is always peer 0. + self.peer_stats(0).inc_received(bytes); + } +} + +/// Emulated network metrics. +#[derive(Clone)] +pub(crate) struct Metrics { + /// Number of bytes sent per peer. + peer_total_sent: CounterVec, + /// Number of received sent per peer. + peer_total_received: CounterVec, +} + +impl Metrics { + pub fn new(registry: &Registry) -> Result { + Ok(Self { + peer_total_sent: prometheus::register( + CounterVec::new( + Opts::new( + "subsystem_benchmark_network_peer_total_bytes_sent", + "Total number of bytes a peer has sent.", + ), + &["peer"], + )?, + registry, + )?, + peer_total_received: prometheus::register( + CounterVec::new( + Opts::new( + "subsystem_benchmark_network_peer_total_bytes_received", + "Total number of bytes a peer has received.", + ), + &["peer"], + )?, + registry, + )?, + }) + } + + /// Increment total sent for a peer. + pub fn on_peer_sent(&self, peer_index: usize, bytes: usize) { + self.peer_total_sent + .with_label_values(vec![format!("node{}", peer_index).as_str()].as_slice()) + .inc_by(bytes as u64); + } + + /// Increment total receioved for a peer. + pub fn on_peer_received(&self, peer_index: usize, bytes: usize) { + self.peer_total_received + .with_label_values(vec![format!("node{}", peer_index).as_str()].as_slice()) + .inc_by(bytes as u64); + } +} + +// Helper trait for low level access to `Requests` variants. +pub trait RequestExt { + /// Get the authority id if any from the request. + fn authority_id(&self) -> Option<&AuthorityDiscoveryId>; + /// Consume self and return the response sender. + fn into_response_sender(self) -> ResponseSender; + /// Allows to change the `ResponseSender` in place. + fn swap_response_sender(&mut self, new_sender: ResponseSender) -> ResponseSender; + /// Returns the size in bytes of the request payload. + fn size(&self) -> usize; +} + +impl RequestExt for Requests { + fn authority_id(&self) -> Option<&AuthorityDiscoveryId> { + match self { + Requests::ChunkFetchingV1(request) => { + if let Recipient::Authority(authority_id) = &request.peer { + Some(authority_id) + } else { + None + } + }, + Requests::AvailableDataFetchingV1(request) => { + if let Recipient::Authority(authority_id) = &request.peer { + Some(authority_id) + } else { + None + } + }, + request => { + unimplemented!("RequestAuthority not implemented for {:?}", request) + }, + } + } + + fn into_response_sender(self) -> ResponseSender { + match self { + Requests::ChunkFetchingV1(outgoing_request) => outgoing_request.pending_response, + Requests::AvailableDataFetchingV1(outgoing_request) => + outgoing_request.pending_response, + _ => unimplemented!("unsupported request type"), + } + } + + /// Swaps the `ResponseSender` and returns the previous value. + fn swap_response_sender(&mut self, new_sender: ResponseSender) -> ResponseSender { + match self { + Requests::ChunkFetchingV1(outgoing_request) => + std::mem::replace(&mut outgoing_request.pending_response, new_sender), + Requests::AvailableDataFetchingV1(outgoing_request) => + std::mem::replace(&mut outgoing_request.pending_response, new_sender), + _ => unimplemented!("unsupported request type"), + } + } + + /// Returns the size in bytes of the request payload. + fn size(&self) -> usize { + match self { + Requests::ChunkFetchingV1(outgoing_request) => outgoing_request.payload.encoded_size(), + Requests::AvailableDataFetchingV1(outgoing_request) => + outgoing_request.payload.encoded_size(), + _ => unimplemented!("received an unexpected request"), + } + } +} + +#[cfg(test)] +mod tests { + use super::RateLimit; + use std::time::Instant; + + #[tokio::test] + async fn test_expected_rate() { + let tick_rate = 200; + let budget = 1_000_000; + // rate must not exceeed 100 credits per second + let mut rate_limiter = RateLimit::new(tick_rate, budget); + let mut total_sent = 0usize; + let start = Instant::now(); + + let mut reap_amount = 0; + while rate_limiter.total_ticks < tick_rate { + reap_amount += 1; + reap_amount %= 100; + + rate_limiter.reap(reap_amount).await; + total_sent += reap_amount; + } + + let end = Instant::now(); + + println!("duration: {}", (end - start).as_millis()); + + // Allow up to `budget/max_refill` error tolerance + let lower_bound = budget as u128 * ((end - start).as_millis() / 1000u128); + let upper_bound = budget as u128 * + ((end - start).as_millis() / 1000u128 + rate_limiter.max_refill as u128); + assert!(total_sent as u128 >= lower_bound); + assert!(total_sent as u128 <= upper_bound); + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/usage.rs b/polkadot/node/subsystem-bench/src/lib/usage.rs new file mode 100644 index 0000000000000000000000000000000000000000..b83ef7d98d918e2c398f9f4b28558b1b93851051 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/usage.rs @@ -0,0 +1,146 @@ +// 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 . + +//! Test usage implementation + +use colored::Colorize; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Debug, Serialize, Deserialize)] +pub struct BenchmarkUsage { + pub benchmark_name: String, + pub network_usage: Vec, + pub cpu_usage: Vec, +} + +impl std::fmt::Display for BenchmarkUsage { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "\n{}\n\n{}\n{}\n\n{}\n{}\n", + self.benchmark_name.purple(), + format!("{:<32}{:>12}{:>12}", "Network usage, KiB", "total", "per block").blue(), + self.network_usage + .iter() + .map(|v| v.to_string()) + .collect::>() + .join("\n"), + format!("{:<32}{:>12}{:>12}", "CPU usage, seconds", "total", "per block").blue(), + self.cpu_usage.iter().map(|v| v.to_string()).collect::>().join("\n") + ) + } +} + +impl BenchmarkUsage { + pub fn average(usages: &[Self]) -> Self { + let all_network_usages: Vec<&ResourceUsage> = + usages.iter().flat_map(|v| &v.network_usage).collect(); + let all_cpu_usage: Vec<&ResourceUsage> = usages.iter().flat_map(|v| &v.cpu_usage).collect(); + + Self { + benchmark_name: usages.first().map(|v| v.benchmark_name.clone()).unwrap_or_default(), + network_usage: ResourceUsage::average_by_resource_name(&all_network_usages), + cpu_usage: ResourceUsage::average_by_resource_name(&all_cpu_usage), + } + } + + pub fn check_network_usage(&self, checks: &[ResourceUsageCheck]) -> Vec { + check_usage(&self.benchmark_name, &self.network_usage, checks) + } + + pub fn check_cpu_usage(&self, checks: &[ResourceUsageCheck]) -> Vec { + check_usage(&self.benchmark_name, &self.cpu_usage, checks) + } + + pub fn cpu_usage_diff(&self, other: &Self, resource_name: &str) -> Option { + let self_res = self.cpu_usage.iter().find(|v| v.resource_name == resource_name); + let other_res = other.cpu_usage.iter().find(|v| v.resource_name == resource_name); + + match (self_res, other_res) { + (Some(self_res), Some(other_res)) => Some(self_res.diff(other_res)), + _ => None, + } + } +} + +fn check_usage( + benchmark_name: &str, + usage: &[ResourceUsage], + checks: &[ResourceUsageCheck], +) -> Vec { + checks + .iter() + .filter_map(|check| { + check_resource_usage(usage, check) + .map(|message| format!("{}: {}", benchmark_name, message)) + }) + .collect() +} + +fn check_resource_usage( + usage: &[ResourceUsage], + (resource_name, base, precision): &ResourceUsageCheck, +) -> Option { + if let Some(usage) = usage.iter().find(|v| v.resource_name == *resource_name) { + let diff = (base - usage.per_block).abs() / base; + if diff < *precision { + None + } else { + Some(format!( + "The resource `{}` is expected to be equal to {} with a precision {}, but the current value is {}", + resource_name, base, precision, usage.per_block + )) + } + } else { + Some(format!("The resource `{}` is not found", resource_name)) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ResourceUsage { + pub resource_name: String, + pub total: f64, + pub per_block: f64, +} + +impl std::fmt::Display for ResourceUsage { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:<32}{:>12.3}{:>12.3}", self.resource_name.cyan(), self.total, self.per_block) + } +} + +impl ResourceUsage { + fn average_by_resource_name(usages: &[&Self]) -> Vec { + let mut by_name: HashMap> = Default::default(); + for usage in usages { + by_name.entry(usage.resource_name.clone()).or_default().push(usage); + } + let mut average = vec![]; + for (resource_name, values) in by_name { + let total = values.iter().map(|v| v.total).sum::() / values.len() as f64; + let per_block = values.iter().map(|v| v.per_block).sum::() / values.len() as f64; + average.push(Self { resource_name, total, per_block }); + } + average + } + + fn diff(&self, other: &Self) -> f64 { + (self.per_block - other.per_block).abs() / self.per_block + } +} + +type ResourceUsageCheck<'a> = (&'a str, f64, f64); diff --git a/polkadot/node/subsystem-bench/src/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/subsystem-bench.rs deleted file mode 100644 index 8669ee4e8b1d8c2bc5dae8ddb6f5611fe7cd5e36..0000000000000000000000000000000000000000 --- a/polkadot/node/subsystem-bench/src/subsystem-bench.rs +++ /dev/null @@ -1,226 +0,0 @@ -// 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 . - -//! A tool for running subsystem benchmark tests designed for development and -//! CI regression testing. - -use clap::Parser; -use color_eyre::eyre; -use pyroscope::PyroscopeAgent; -use pyroscope_pprofrs::{pprof_backend, PprofConfig}; - -use colored::Colorize; -use std::{path::Path, time::Duration}; - -pub(crate) mod availability; -pub(crate) mod cli; -pub(crate) mod core; -mod valgrind; - -use availability::{prepare_test, NetworkEmulation, TestState}; -use cli::TestObjective; - -use core::{ - configuration::TestConfiguration, - environment::{TestEnvironment, GENESIS_HASH}, -}; - -use clap_num::number_range; - -use crate::core::display::display_configuration; - -fn le_100(s: &str) -> Result { - number_range(s, 0, 100) -} - -fn le_5000(s: &str) -> Result { - number_range(s, 0, 5000) -} - -#[derive(Debug, Parser)] -#[allow(missing_docs)] -struct BenchCli { - #[arg(long, value_enum, ignore_case = true, default_value_t = NetworkEmulation::Ideal)] - /// The type of network to be emulated - pub network: NetworkEmulation, - - #[clap(flatten)] - pub standard_configuration: cli::StandardTestOptions, - - #[clap(short, long)] - /// The bandwidth of simulated remote peers in KiB - pub peer_bandwidth: Option, - - #[clap(short, long)] - /// The bandwidth of our simulated node in KiB - pub bandwidth: Option, - - #[clap(long, value_parser=le_100)] - /// Simulated conection error ratio [0-100]. - pub peer_error: Option, - - #[clap(long, value_parser=le_5000)] - /// Minimum remote peer latency in milliseconds [0-5000]. - pub peer_min_latency: Option, - - #[clap(long, value_parser=le_5000)] - /// Maximum remote peer latency in milliseconds [0-5000]. - pub peer_max_latency: Option, - - #[clap(long, default_value_t = false)] - /// Enable CPU Profiling with Pyroscope - pub profile: bool, - - #[clap(long, requires = "profile", default_value_t = String::from("http://localhost:4040"))] - /// Pyroscope Server URL - pub pyroscope_url: String, - - #[clap(long, requires = "profile", default_value_t = 113)] - /// Pyroscope Sample Rate - pub pyroscope_sample_rate: u32, - - #[clap(long, default_value_t = false)] - /// Enable Cache Misses Profiling with Valgrind. Linux only, Valgrind must be in the PATH - pub cache_misses: bool, - - #[command(subcommand)] - pub objective: cli::TestObjective, -} - -impl BenchCli { - fn launch(self) -> eyre::Result<()> { - let is_valgrind_running = valgrind::is_valgrind_running(); - if !is_valgrind_running && self.cache_misses { - return valgrind::relaunch_in_valgrind_mode() - } - - let agent_running = if self.profile { - let agent = PyroscopeAgent::builder(self.pyroscope_url.as_str(), "subsystem-bench") - .backend(pprof_backend(PprofConfig::new().sample_rate(self.pyroscope_sample_rate))) - .build()?; - - Some(agent.start()?) - } else { - None - }; - - let configuration = self.standard_configuration; - let mut test_config = match self.objective { - TestObjective::TestSequence(options) => { - let test_sequence = - core::configuration::TestSequence::new_from_file(Path::new(&options.path)) - .expect("File exists") - .into_vec(); - let num_steps = test_sequence.len(); - gum::info!( - "{}", - format!("Sequence contains {} step(s)", num_steps).bright_purple() - ); - for (index, test_config) in test_sequence.into_iter().enumerate() { - gum::info!("{}", format!("Step {}/{}", index + 1, num_steps).bright_purple(),); - display_configuration(&test_config); - - let mut state = TestState::new(&test_config); - let (mut env, _protocol_config) = prepare_test(test_config, &mut state); - env.runtime() - .block_on(availability::benchmark_availability_read(&mut env, state)); - } - return Ok(()) - }, - TestObjective::DataAvailabilityRead(ref _options) => match self.network { - NetworkEmulation::Healthy => TestConfiguration::healthy_network( - self.objective, - configuration.num_blocks, - configuration.n_validators, - configuration.n_cores, - configuration.min_pov_size, - configuration.max_pov_size, - ), - NetworkEmulation::Degraded => TestConfiguration::degraded_network( - self.objective, - configuration.num_blocks, - configuration.n_validators, - configuration.n_cores, - configuration.min_pov_size, - configuration.max_pov_size, - ), - NetworkEmulation::Ideal => TestConfiguration::ideal_network( - self.objective, - configuration.num_blocks, - configuration.n_validators, - configuration.n_cores, - configuration.min_pov_size, - configuration.max_pov_size, - ), - }, - }; - - let mut latency_config = test_config.latency.clone().unwrap_or_default(); - - if let Some(latency) = self.peer_min_latency { - latency_config.min_latency = Duration::from_millis(latency); - } - - if let Some(latency) = self.peer_max_latency { - latency_config.max_latency = Duration::from_millis(latency); - } - - if let Some(error) = self.peer_error { - test_config.error = error; - } - - if let Some(bandwidth) = self.peer_bandwidth { - // CLI expects bw in KiB - test_config.peer_bandwidth = bandwidth * 1024; - } - - if let Some(bandwidth) = self.bandwidth { - // CLI expects bw in KiB - test_config.bandwidth = bandwidth * 1024; - } - - display_configuration(&test_config); - - let mut state = TestState::new(&test_config); - let (mut env, _protocol_config) = prepare_test(test_config, &mut state); - - env.runtime() - .block_on(availability::benchmark_availability_read(&mut env, state)); - - if let Some(agent_running) = agent_running { - let agent_ready = agent_running.stop()?; - agent_ready.shutdown(); - } - - Ok(()) - } -} - -fn main() -> eyre::Result<()> { - color_eyre::install()?; - env_logger::builder() - .filter(Some("hyper"), log::LevelFilter::Info) - // Avoid `Terminating due to subsystem exit subsystem` warnings - .filter(Some("polkadot_overseer"), log::LevelFilter::Error) - .filter(None, log::LevelFilter::Info) - // .filter(None, log::LevelFilter::Trace) - .try_init() - .unwrap(); - - let cli: BenchCli = BenchCli::parse(); - cli.launch()?; - Ok(()) -} diff --git a/polkadot/node/subsystem-types/Cargo.toml b/polkadot/node/subsystem-types/Cargo.toml index 181ef54b4c6c2346e9cf63327e7ddcd15f287907..54c8f7e2ade773f1df84fc8cef1825c266364633 100644 --- a/polkadot/node/subsystem-types/Cargo.toml +++ b/polkadot/node/subsystem-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-subsystem-types" description = "Subsystem traits and message definitions" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -17,7 +17,7 @@ polkadot-node-primitives = { path = "../primitives" } polkadot-node-network-protocol = { path = "../network/protocol" } polkadot-statement-table = { path = "../../statement-table" } polkadot-node-jaeger = { path = "../jaeger" } -orchestra = { version = "0.3.4", default-features = false, features = ["futures_channel"] } +orchestra = { version = "0.3.5", default-features = false, features = ["futures_channel"] } sc-network = { path = "../../../substrate/client/network" } sp-api = { path = "../../../substrate/primitives/api" } sp-blockchain = { path = "../../../substrate/primitives/blockchain" } @@ -28,6 +28,6 @@ sc-client-api = { path = "../../../substrate/client/api" } sc-transaction-pool-api = { path = "../../../substrate/client/transaction-pool/api" } smallvec = "1.8.0" substrate-prometheus-endpoint = { path = "../../../substrate/utils/prometheus" } -thiserror = "1.0.48" +thiserror = { workspace = true } async-trait = "0.1.74" bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 1d5d82b57fdfb482383bc943d22ee61a512f035b..f11291fd0ea8c157d72a24349e94196178529446 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -1112,6 +1112,9 @@ pub struct ProspectiveValidationDataRequest { /// is present in and the depths of that tree the candidate is present in. pub type FragmentTreeMembership = Vec<(Hash, Vec)>; +/// A collection of ancestor candidates of a parachain. +pub type Ancestors = HashSet; + /// Messages sent to the Prospective Parachains subsystem. #[derive(Debug)] pub enum ProspectiveParachainsMessage { @@ -1128,14 +1131,19 @@ pub enum ProspectiveParachainsMessage { /// has been backed. This requires that the candidate was successfully introduced in /// the past. CandidateBacked(ParaId, CandidateHash), - /// Get a backable candidate hash along with its relay parent for the given parachain, - /// under the given relay-parent hash, which is a descendant of the given candidate hashes. - /// Returns `None` on the channel if no such candidate exists. - GetBackableCandidate( + /// Try getting N backable candidate hashes along with their relay parents for the given + /// parachain, under the given relay-parent hash, which is a descendant of the given ancestors. + /// Timed out ancestors should not be included in the collection. + /// N should represent the number of scheduled cores of this ParaId. + /// A timed out ancestor frees the cores of all of its descendants, so if there's a hole in the + /// supplied ancestor path, we'll get candidates that backfill those timed out slots first. It + /// may also return less/no candidates, if there aren't enough backable candidates recorded. + GetBackableCandidates( Hash, ParaId, - Vec, - oneshot::Sender>, + u32, + Ancestors, + oneshot::Sender>, ), /// Get the hypothetical frontier membership of candidates with the given properties /// under the specified active leaves' fragment trees. diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index 7f6183076101b4474e8059435b42a69b108fbb05..4039fc9127da95e19e0aca427740793c39131ab9 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -26,13 +26,13 @@ use polkadot_primitives::{ PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; -use sc_client_api::HeaderBackend; +use sc_client_api::{AuxStore, HeaderBackend}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sp_api::{ApiError, ApiExt, ProvideRuntimeApi}; use sp_authority_discovery::AuthorityDiscoveryApi; -use sp_blockchain::Info; +use sp_blockchain::{BlockStatus, Info}; use sp_consensus_babe::{BabeApi, Epoch}; -use sp_runtime::traits::{Header as HeaderT, NumberFor}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use std::{collections::BTreeMap, sync::Arc}; /// Offers header utilities. @@ -595,3 +595,61 @@ where self.client.runtime_api().approval_voting_params(at) } } + +impl HeaderBackend for DefaultSubsystemClient +where + Client: HeaderBackend, + Block: sp_runtime::traits::Block, +{ + fn header( + &self, + hash: Block::Hash, + ) -> sc_client_api::blockchain::Result> { + self.client.header(hash) + } + + fn info(&self) -> Info { + self.client.info() + } + + fn status(&self, hash: Block::Hash) -> sc_client_api::blockchain::Result { + self.client.status(hash) + } + + fn number( + &self, + hash: Block::Hash, + ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> { + self.client.number(hash) + } + + fn hash( + &self, + number: NumberFor, + ) -> sc_client_api::blockchain::Result> { + self.client.hash(number) + } +} + +impl AuxStore for DefaultSubsystemClient +where + Client: AuxStore, +{ + fn insert_aux< + 'a, + 'b: 'a, + 'c: 'a, + I: IntoIterator, + D: IntoIterator, + >( + &self, + insert: I, + delete: D, + ) -> sp_blockchain::Result<()> { + self.client.insert_aux(insert, delete) + } + + fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result>> { + self.client.get_aux(key) + } +} diff --git a/polkadot/node/subsystem-util/Cargo.toml b/polkadot/node/subsystem-util/Cargo.toml index 68a834d46e3e8de4be29e699108575de2a8c0ba6..a668f8de76a0bdf15b1e4498924279b5fc467d3e 100644 --- a/polkadot/node/subsystem-util/Cargo.toml +++ b/polkadot/node/subsystem-util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-subsystem-util" description = "Subsystem traits and message definitions" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -18,7 +18,7 @@ parity-scale-codec = { version = "3.6.1", default-features = false, features = [ parking_lot = "0.12.1" pin-project = "1.0.9" rand = "0.8.5" -thiserror = "1.0.48" +thiserror = { workspace = true } fatality = "0.0.6" gum = { package = "tracing-gum", path = "../gum" } derive_more = "0.99.17" @@ -46,7 +46,7 @@ parity-db = { version = "0.4.12" } assert_matches = "1.4.0" env_logger = "0.9.0" futures = { version = "0.3.21", features = ["thread-pool"] } -log = "0.4.17" +log = { workspace = true, default-features = true } polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } lazy_static = "1.4.0" polkadot-primitives-test-helpers = { path = "../../primitives/test-helpers" } diff --git a/polkadot/node/subsystem/Cargo.toml b/polkadot/node/subsystem/Cargo.toml index b0b396d7f62b91fc1c97e34a743264d0c11f9667..c59c1f88e33995aef7578da58e28d086668f14ee 100644 --- a/polkadot/node/subsystem/Cargo.toml +++ b/polkadot/node/subsystem/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "polkadot-node-subsystem" description = "Subsystem traits and message definitions and the generated overseer" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/node/test/service/Cargo.toml b/polkadot/node/test/service/Cargo.toml index f04108537995f1caa0eb72833af2ca558fe931ec..02b227e6f345758d8b53b7da0b0cf3ddc2b77d05 100644 --- a/polkadot/node/test/service/Cargo.toml +++ b/polkadot/node/test/service/Cargo.toml @@ -14,7 +14,7 @@ futures = "0.3.21" hex = "0.4.3" gum = { package = "tracing-gum", path = "../../gum" } rand = "0.8.5" -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } tempfile = "3.2.0" tokio = "1.24.2" @@ -71,6 +71,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-staking/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", diff --git a/polkadot/node/test/service/src/chain_spec.rs b/polkadot/node/test/service/src/chain_spec.rs index 0295090b9521551533d012addeda064595dbeac3..f14fa9fde58b4426690cd1bd0e16129f095c34ec 100644 --- a/polkadot/node/test/service/src/chain_spec.rs +++ b/polkadot/node/test/service/src/chain_spec.rs @@ -19,7 +19,9 @@ use babe_primitives::AuthorityId as BabeId; use grandpa::AuthorityId as GrandpaId; use pallet_staking::Forcing; -use polkadot_primitives::{AccountId, AssignmentId, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE}; +use polkadot_primitives::{ + vstaging::SchedulerParams, AccountId, AssignmentId, 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; use sc_chain_spec::{ChainSpec, ChainType}; @@ -165,10 +167,14 @@ fn polkadot_testnet_genesis( max_code_size: MAX_CODE_SIZE, max_pov_size: MAX_POV_SIZE, max_head_data_size: 32 * 1024, - group_rotation_frequency: 20, - paras_availability_period: 4, no_show_slots: 10, minimum_validation_upgrade_delay: 5, + max_downward_message_size: 1024, + scheduler_params: SchedulerParams { + group_rotation_frequency: 20, + paras_availability_period: 4, + ..Default::default() + }, ..Default::default() }, } diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index e9423d513bf023c59887c2c8c459eef2299ee269..1876595c7b40b6b8a9311a606a8430e6fcd42a08 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -28,9 +28,11 @@ use polkadot_overseer::Handle; use polkadot_primitives::{Balance, CollatorPair, HeadData, Id as ParaId, ValidationCode}; use polkadot_runtime_common::BlockHashCount; use polkadot_runtime_parachains::paras::{ParaGenesisArgs, ParaKind}; -use polkadot_service::{Error, FullClient, IsParachainNode, NewFull, PrometheusConfig}; +use polkadot_service::{ + Error, FullClient, IsParachainNode, NewFull, OverseerGen, PrometheusConfig, +}; use polkadot_test_runtime::{ - ParasCall, ParasSudoWrapperCall, Runtime, SignedExtra, SignedPayload, SudoCall, + ParasCall, ParasSudoWrapperCall, Runtime, SignedPayload, SudoCall, TxExtension, UncheckedExtrinsic, VERSION, }; @@ -42,8 +44,8 @@ use sc_network::{ }; use sc_service::{ config::{ - DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, WasmExecutionMethod, - WasmtimeInstantiationStrategy, + DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, RpcBatchRequestConfig, + WasmExecutionMethod, WasmtimeInstantiationStrategy, }, BasePath, BlocksPruning, Configuration, Role, RpcHandlers, TaskManager, }; @@ -69,10 +71,11 @@ pub use polkadot_service::{FullBackend, GetLastTimestamp}; /// Create a new full node. #[sc_tracing::logging::prefix_logs_with(config.network.node_name.as_str())] -pub fn new_full( +pub fn new_full( config: Configuration, is_parachain_node: IsParachainNode, workers_path: Option, + overseer_gen: OverseerGenerator, ) -> Result { let workers_path = Some(workers_path.unwrap_or_else(get_relative_workers_path_for_test)); @@ -88,7 +91,7 @@ pub fn new_full( secure_validator_mode: false, workers_path, workers_names: None, - overseer_gen: polkadot_service::RealOverseerGen, + overseer_gen, overseer_message_channel_capacity_override: None, malus_finality_delay: None, hwbench: None, @@ -182,6 +185,9 @@ pub fn node_config( 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, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, @@ -206,9 +212,13 @@ pub fn run_validator_node( worker_program_path: Option, ) -> PolkadotTestNode { let multiaddr = config.network.listen_addresses[0].clone(); - let NewFull { task_manager, client, network, rpc_handlers, overseer_handle, .. } = - new_full(config, IsParachainNode::No, worker_program_path) - .expect("could not create Polkadot test service"); + let NewFull { task_manager, client, network, rpc_handlers, overseer_handle, .. } = new_full( + config, + IsParachainNode::No, + worker_program_path, + polkadot_service::ValidatorOverseerGen, + ) + .expect("could not create Polkadot test service"); let overseer_handle = overseer_handle.expect("test node must have an overseer handle"); let peer_id = network.local_peer_id(); @@ -238,9 +248,13 @@ pub fn run_collator_node( ) -> PolkadotTestNode { let config = node_config(storage_update_func, tokio_handle, key, boot_nodes, false); let multiaddr = config.network.listen_addresses[0].clone(); - let NewFull { task_manager, client, network, rpc_handlers, overseer_handle, .. } = - new_full(config, IsParachainNode::Collator(collator_pair), None) - .expect("could not create Polkadot test service"); + let NewFull { task_manager, client, network, rpc_handlers, overseer_handle, .. } = new_full( + config, + IsParachainNode::Collator(collator_pair), + None, + polkadot_service::CollatorOverseerGen, + ) + .expect("could not create Polkadot test service"); let overseer_handle = overseer_handle.expect("test node must have an overseer handle"); let peer_id = network.local_peer_id(); @@ -368,7 +382,7 @@ pub fn construct_extrinsic( let period = BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -377,10 +391,11 @@ pub fn construct_extrinsic( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); + ) + .into(); let raw_payload = SignedPayload::from_raw( function.clone(), - extra.clone(), + tx_ext.clone(), ( (), VERSION.spec_version, @@ -397,7 +412,7 @@ pub fn construct_extrinsic( function.clone(), polkadot_test_runtime::Address::Id(caller.public().into()), polkadot_primitives::Signature::Sr25519(signature.clone()), - extra.clone(), + tx_ext.clone(), ) } diff --git a/polkadot/node/tracking-allocator/Cargo.toml b/polkadot/node/tracking-allocator/Cargo.toml index 486346e1fe1c00c7d5fa37b4a2a58540b003bc62..d98377e53759c9df35e5eb96f2de3860555a87b8 100644 --- a/polkadot/node/tracking-allocator/Cargo.toml +++ b/polkadot/node/tracking-allocator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "staging-tracking-allocator" description = "Tracking allocator to control the amount of memory consumed by the process" -version = "1.0.0" +version = "2.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/node/zombienet-backchannel/Cargo.toml b/polkadot/node/zombienet-backchannel/Cargo.toml index 6af7a8d6e380a0180bb83f670fd15deee7dfc21b..fa99490a997434eedee977121f13d8dcc1a9b5a1 100644 --- a/polkadot/node/zombienet-backchannel/Cargo.toml +++ b/polkadot/node/zombienet-backchannel/Cargo.toml @@ -19,7 +19,7 @@ futures-util = "0.3.30" lazy_static = "1.4.0" parity-scale-codec = { version = "3.6.1", features = ["derive"] } reqwest = { version = "0.11", features = ["rustls-tls"], default-features = false } -thiserror = "1.0.48" +thiserror = { workspace = true } gum = { package = "tracing-gum", path = "../gum" } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.111" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } diff --git a/polkadot/parachain/Cargo.toml b/polkadot/parachain/Cargo.toml index 418fe0e6117350fc814cd79bf8718444b86b1b01..d8c3cea7ad8b16062f346c12842d5f652936797c 100644 --- a/polkadot/parachain/Cargo.toml +++ b/polkadot/parachain/Cargo.toml @@ -4,7 +4,7 @@ description = "Types and utilities for creating and working with parachains" authors.workspace = true edition.workspace = true license.workspace = true -version = "1.0.0" +version = "6.0.0" [lints] workspace = true @@ -21,10 +21,10 @@ sp-core = { path = "../../substrate/primitives/core", default-features = false, sp-weights = { path = "../../substrate/primitives/weights", default-features = false } polkadot-core-primitives = { path = "../core-primitives", default-features = false } derive_more = "0.99.11" -bounded-collections = { version = "0.1.8", default-features = false, features = ["serde"] } +bounded-collections = { version = "0.2.0", default-features = false, features = ["serde"] } # all optional crates. -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"] } +serde = { features = ["alloc", "derive"], workspace = true } [features] default = ["std"] diff --git a/polkadot/parachain/test-parachains/adder/collator/Cargo.toml b/polkadot/parachain/test-parachains/adder/collator/Cargo.toml index 7dd0d9a563c5087b9295d5de5d6a6ee812033578..8ce4ceb47c245b621dcbf0199b7a73ad29b1a6f5 100644 --- a/polkadot/parachain/test-parachains/adder/collator/Cargo.toml +++ b/polkadot/parachain/test-parachains/adder/collator/Cargo.toml @@ -16,10 +16,10 @@ path = "src/main.rs" [dependencies] parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" -log = "0.4.17" +log = { workspace = true, default-features = true } test-parachain-adder = { path = ".." } polkadot-primitives = { path = "../../../../primitives" } diff --git a/polkadot/parachain/test-parachains/adder/collator/src/main.rs b/polkadot/parachain/test-parachains/adder/collator/src/main.rs index 5d9384d49561e555666c722048844b73967473e9..fec90fc41cdb160f1a90a8ceab7a15da81e96bb2 100644 --- a/polkadot/parachain/test-parachains/adder/collator/src/main.rs +++ b/polkadot/parachain/test-parachains/adder/collator/src/main.rs @@ -91,7 +91,7 @@ fn main() -> Result<()> { workers_path: None, workers_names: None, - overseer_gen: polkadot_service::RealOverseerGen, + overseer_gen: polkadot_service::CollatorOverseerGen, overseer_message_channel_capacity_override: None, malus_finality_delay: None, hwbench: None, diff --git a/polkadot/parachain/test-parachains/undying/Cargo.toml b/polkadot/parachain/test-parachains/undying/Cargo.toml index 19e1261db1e7c4f17308061929d559df34943159..82ceebcf4eee99f36140908580b41c13b04ea87e 100644 --- a/polkadot/parachain/test-parachains/undying/Cargo.toml +++ b/polkadot/parachain/test-parachains/undying/Cargo.toml @@ -17,7 +17,7 @@ parity-scale-codec = { version = "3.6.1", default-features = false, features = [ sp-std = { path = "../../../../substrate/primitives/std", default-features = false } tiny-keccak = { version = "2.0.2", features = ["keccak"] } dlmalloc = { version = "0.2.4", features = ["global"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } # We need to make sure the global allocator is disabled until we have support of full substrate externalities sp-io = { path = "../../../../substrate/primitives/io", default-features = false, features = ["disable_allocator"] } diff --git a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml index 001c48476b58929852075fd914154b0b86daa93a..25fdbfa74ea08ae3a2b389927073aeadd066cb7d 100644 --- a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml +++ b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml @@ -16,10 +16,10 @@ path = "src/main.rs" [dependencies] parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" -log = "0.4.17" +log = { workspace = true, default-features = true } test-parachain-undying = { path = ".." } polkadot-primitives = { path = "../../../../primitives" } diff --git a/polkadot/parachain/test-parachains/undying/collator/src/main.rs b/polkadot/parachain/test-parachains/undying/collator/src/main.rs index d6fb9a04ab302b36c33bc7e781dc5e847fb6b5df..45f21e7b859631ec8763304c18f01cf6f3d04d10 100644 --- a/polkadot/parachain/test-parachains/undying/collator/src/main.rs +++ b/polkadot/parachain/test-parachains/undying/collator/src/main.rs @@ -93,7 +93,7 @@ fn main() -> Result<()> { workers_path: None, workers_names: None, - overseer_gen: polkadot_service::RealOverseerGen, + overseer_gen: polkadot_service::CollatorOverseerGen, overseer_message_channel_capacity_override: None, malus_finality_delay: None, hwbench: None, diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 2552b9abc40b09902da255a8fe6f1baa4416e7c9..e63fb621c7880c03fe0282a5500df69d20528f99 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-primitives" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -14,7 +14,8 @@ bitvec = { version = "1.0.0", default-features = false, features = ["alloc", "se hex-literal = "0.4.1" parity-scale-codec = { version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["bit-vec", "derive", "serde"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"] } +log = { workspace = true, default-features = false } +serde = { features = ["alloc", "derive"], workspace = true } application-crypto = { package = "sp-application-crypto", path = "../../substrate/primitives/application-crypto", default-features = false, features = ["serde"] } inherents = { package = "sp-inherents", path = "../../substrate/primitives/inherents", default-features = false } @@ -38,6 +39,7 @@ std = [ "application-crypto/std", "bitvec/std", "inherents/std", + "log/std", "parity-scale-codec/std", "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 2570bcadf606ab8ef0809a1f258a6068263f1db1..2ddd9b58dfe45d6560e9dc5c62f3d047ad8d9850 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -57,8 +57,8 @@ pub use v6::{ 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, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, - PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, + MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, MIN_CODE_SIZE, + ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, }; #[cfg(feature = "std")] diff --git a/polkadot/primitives/src/v6/executor_params.rs b/polkadot/primitives/src/v6/executor_params.rs index 112a529f62b0570e9270d1f09e397e6b9dc358b0..1e19f3b23fec9b9889d5976380f52ab6b66072b4 100644 --- a/polkadot/primitives/src/v6/executor_params.rs +++ b/polkadot/primitives/src/v6/executor_params.rs @@ -159,7 +159,9 @@ impl sp_std::fmt::LowerHex for ExecutorParamsHash { // into individual fields of the structure. Thus, complex migrations shall be avoided when adding // new entries and removing old ones. At the moment, there's no mandatory parameters defined. If // they show up, they must be clearly documented as mandatory ones. -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, Serialize, Deserialize)] +#[derive( + Clone, Debug, Default, Encode, Decode, PartialEq, Eq, TypeInfo, Serialize, Deserialize, +)] pub struct ExecutorParams(Vec); impl ExecutorParams { @@ -334,9 +336,3 @@ impl From<&[ExecutorParam]> for ExecutorParams { ExecutorParams(arr.to_vec()) } } - -impl Default for ExecutorParams { - fn default() -> Self { - ExecutorParams(vec![]) - } -} diff --git a/polkadot/primitives/src/v6/mod.rs b/polkadot/primitives/src/v6/mod.rs index fd0b32db799434d1b76071d53edf1e20a491c109..89431f7801f7ae94390763f0ef710b54cb301718 100644 --- a/polkadot/primitives/src/v6/mod.rs +++ b/polkadot/primitives/src/v6/mod.rs @@ -16,7 +16,7 @@ //! `V6` Primitives. -use bitvec::vec::BitVec; +use bitvec::{field::BitField, slice::BitSlice, vec::BitVec}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{ @@ -72,6 +72,7 @@ pub use metrics::{ /// The key type ID for a collator key. pub const COLLATOR_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"coll"); +const LOG_TARGET: &str = "runtime::primitives"; mod collator_app { use application_crypto::{app_crypto, sr25519}; @@ -362,6 +363,9 @@ pub const PARACHAINS_INHERENT_IDENTIFIER: InherentIdentifier = *b"parachn0"; /// The key type ID for parachain assignment key. pub const ASSIGNMENT_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"asgn"); +/// Compressed or not the wasm blob can never be less than 9 bytes. +pub const MIN_CODE_SIZE: u32 = 9; + /// Maximum compressed code size we support right now. /// At the moment we have runtime upgrade on chain, which restricts scalability severely. If we want /// to have bigger values, we should fix that first. @@ -703,19 +707,50 @@ pub type UncheckedSignedAvailabilityBitfields = Vec { /// The candidate referred to. - pub candidate: CommittedCandidateReceipt, + candidate: CommittedCandidateReceipt, /// The validity votes themselves, expressed as signatures. - pub validity_votes: Vec, - /// The indices of the validators within the group, expressed as a bitfield. - pub validator_indices: BitVec, + 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, } impl BackedCandidate { - /// Get a reference to the descriptor of the para. + /// Constructor + pub fn new( + candidate: CommittedCandidateReceipt, + 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 descriptor of the candidate. pub fn descriptor(&self) -> &CandidateDescriptor { &self.candidate.descriptor } + /// Get a reference to the committed candidate receipt of the candidate. + pub fn candidate(&self) -> &CommittedCandidateReceipt { + &self.candidate + } + + /// 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 @@ -731,6 +766,48 @@ impl BackedCandidate { { 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); + } + } } /// Verify the backing of the given candidate. @@ -743,43 +820,65 @@ impl BackedCandidate { /// /// Returns either an error, indicating that one of the signatures was invalid or that the index /// was out-of-bounds, or the number of signatures checked. -pub fn check_candidate_backing + Clone + Encode>( - backed: &BackedCandidate, +pub fn check_candidate_backing + Clone + Encode + core::fmt::Debug>( + candidate_hash: CandidateHash, + validity_votes: &[ValidityAttestation], + validator_indices: &BitSlice, signing_context: &SigningContext, group_len: usize, validator_lookup: impl Fn(usize) -> Option, ) -> Result { - if backed.validator_indices.len() != group_len { + if validator_indices.len() != group_len { + log::debug!( + target: LOG_TARGET, + "Check candidate backing: indices mismatch: group_len = {} , indices_len = {}", + group_len, + validator_indices.len(), + ); return Err(()) } - if backed.validity_votes.len() > group_len { + if validity_votes.len() > group_len { + log::debug!( + target: LOG_TARGET, + "Check candidate backing: Too many votes, expected: {}, found: {}", + group_len, + validity_votes.len(), + ); return Err(()) } - // this is known, even in runtime, to be blake2-256. - let hash = backed.candidate.hash(); - let mut signed = 0; - for ((val_in_group_idx, _), attestation) in backed - .validator_indices + for ((val_in_group_idx, _), attestation) in validator_indices .iter() .enumerate() .filter(|(_, signed)| **signed) - .zip(backed.validity_votes.iter()) + .zip(validity_votes.iter()) { let validator_id = validator_lookup(val_in_group_idx).ok_or(())?; - let payload = attestation.signed_payload(hash, signing_context); + let payload = attestation.signed_payload(candidate_hash, signing_context); let sig = attestation.signature(); if sig.verify(&payload[..], &validator_id) { signed += 1; } else { + log::debug!( + target: LOG_TARGET, + "Check candidate backing: Invalid signature. validator_id = {:?}, validator_index = {} ", + validator_id, + val_in_group_idx, + ); return Err(()) } } - if signed != backed.validity_votes.len() { + if signed != validity_votes.len() { + log::error!( + target: LOG_TARGET, + "Check candidate backing: Too many signatures, expected = {}, found = {}", + validity_votes.len(), + signed, + ); return Err(()) } @@ -1856,6 +1955,34 @@ pub enum PvfExecKind { #[cfg(test)] mod tests { use super::*; + use bitvec::bitvec; + use primitives::sr25519; + + pub fn dummy_committed_candidate_receipt() -> CommittedCandidateReceipt { + let zeros = Hash::zero(); + + CommittedCandidateReceipt { + descriptor: CandidateDescriptor { + para_id: 0.into(), + relay_parent: zeros, + collator: CollatorId::from(sr25519::Public::from_raw([0; 32])), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + signature: CollatorSignature::from(sr25519::Signature([0u8; 64])), + 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 group_rotation_info_calculations() { @@ -1930,4 +2057,73 @@ mod tests { assert!(zero_b.leading_zeros() >= zero_u.leading_zeros()); } + + #[test] + fn test_backed_candidate_injected_core_index() { + let initial_validator_indices = bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1]; + let mut candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + initial_validator_indices.clone(), + None, + ); + + // No core index supplied, ElasticScalingMVP is off. + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(false); + assert_eq!(validator_indices, initial_validator_indices.as_bitslice()); + assert!(core_index.is_none()); + + // No core index supplied, ElasticScalingMVP is on. Still, decoding will be ok if backing + // group size is <= 8, to give a chance to parachains that don't have multiple cores + // assigned. + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(true); + assert_eq!(validator_indices, initial_validator_indices.as_bitslice()); + assert!(core_index.is_none()); + + let encoded_validator_indices = candidate.validator_indices.clone(); + candidate.set_validator_indices_and_core_index(validator_indices.into(), core_index); + assert_eq!(candidate.validator_indices, encoded_validator_indices); + + // No core index supplied, ElasticScalingMVP is on. Decoding is corrupted if backing group + // size larger than 8. + let candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1, 0, 1, 0, 1, 0], + None, + ); + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(true); + assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0].as_bitslice()); + assert!(core_index.is_some()); + + // Core index supplied, ElasticScalingMVP is off. Core index will be treated as normal + // validator indices. Runtime will check against this. + let candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1], + Some(CoreIndex(10)), + ); + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(false); + assert_eq!( + validator_indices, + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0] + ); + assert!(core_index.is_none()); + + // Core index supplied, ElasticScalingMVP is on. + let mut candidate = BackedCandidate::new( + dummy_committed_candidate_receipt(), + vec![], + bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1], + Some(CoreIndex(10)), + ); + let (validator_indices, core_index) = candidate.validator_indices_and_core_index(true); + assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1]); + assert_eq!(core_index, Some(CoreIndex(10))); + + let encoded_validator_indices = candidate.validator_indices.clone(); + candidate.set_validator_indices_and_core_index(validator_indices.into(), core_index); + assert_eq!(candidate.validator_indices, encoded_validator_indices); + } } diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 630bcf8679ad3046ce734042a1557058ea440110..bd2a5106c44de5895fb327957161c84f13a983b0 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -23,6 +23,7 @@ use sp_std::prelude::*; use parity_scale_codec::{Decode, Encode}; use primitives::RuntimeDebug; use scale_info::TypeInfo; +use sp_arithmetic::Perbill; /// Approval voting configuration parameters #[derive( @@ -50,6 +51,81 @@ impl Default for ApprovalVotingParams { } } +/// 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 claimqueue 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(), + } + } +} + use bitvec::vec::BitVec; /// Bit indices in the `HostConfiguration.node_features` that correspond to different node features. @@ -64,9 +140,13 @@ pub mod node_features { /// Tells if tranch0 assignments could be sent in a single certificate. /// Reserved for: `` EnableAssignmentsV2 = 0, + /// This feature enables the extension of `BackedCandidate::validator_indices` by 8 bits. + /// The value stored there represents the assumed core index where the candidates + /// are backed. This is needed for the elastic scaling MVP. + ElasticScalingMVP = 1, /// First unassigned feature bit. /// Every time a new feature flag is assigned it should take this value. /// and this should be incremented. - FirstUnassigned = 1, + FirstUnassigned = 2, } } diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index d532d6ff57f4a34c53d7099d011f1ca68bf6cf5f..c0118f5960a47eabe205c4d2cf6bcf6d295ce347 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -126,7 +126,7 @@ pub fn dummy_candidate_descriptor>(relay_parent: H) -> CandidateD /// Create meaningless validation code. pub fn dummy_validation_code() -> ValidationCode { - ValidationCode(vec![1, 2, 3]) + ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]) } /// Create meaningless head data. diff --git a/polkadot/roadmap/implementers-guide/src/node/backing/prospective-parachains.md b/polkadot/roadmap/implementers-guide/src/node/backing/prospective-parachains.md index 286aeddb986d3db3c7077d8d6227ebcf979073cb..8f00ff084941cc260dea6a9e76c0ff30d3770caf 100644 --- a/polkadot/roadmap/implementers-guide/src/node/backing/prospective-parachains.md +++ b/polkadot/roadmap/implementers-guide/src/node/backing/prospective-parachains.md @@ -92,9 +92,10 @@ prospective validation data. This is unlikely to change. been backed. - Sent by the Backing Subsystem after it successfully imports a statement giving a candidate the necessary quorum of backing votes. -- `ProspectiveParachainsMessage::GetBackableCandidate` - - Get a backable candidate hash along with its relay parent for a given parachain, - under a given relay-parent (leaf) hash, which is a descendant of given candidate hashes. +- `ProspectiveParachainsMessage::GetBackableCandidates` + - Get the requested number of backable candidate hashes along with their relay parent for a given + parachain,under a given relay-parent (leaf) hash, which are descendants of given candidate + hashes. - Sent by the Provisioner when requesting backable candidates, when selecting candidates for a given relay-parent. - `ProspectiveParachainsMessage::GetHypotheticalFrontier` diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md b/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md index 0b4fe6a458732de10c2dcd23466ba30f9a46924c..b017259da8c0863e47deb87e916076d20ed95996 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md @@ -187,16 +187,16 @@ this process is a vector of `CandidateHash`s, sorted in order of their core inde #### Required Path -Required path is a parameter for `ProspectiveParachainsMessage::GetBackableCandidate`, which the provisioner sends in +Required path is a parameter for `ProspectiveParachainsMessage::GetBackableCandidates`, which the provisioner sends in candidate selection. -An empty required path indicates that the requested candidate should be a direct child of the most recently included +An empty required path indicates that the requested candidate chain should start with the most recently included parablock for the given `para_id` as of the given relay parent. In contrast, a required path with one or more entries prompts [prospective parachains](../backing/prospective-parachains.md) to step forward through its fragment tree for the given `para_id` and -relay parent until the desired parablock is reached. We then select a direct child of that parablock to pass to the -provisioner. +relay parent until the desired parablock is reached. We then select the chain starting with the direct child of that +parablock to pass to the provisioner. The parablocks making up a required path do not need to have been previously seen as included in relay chain blocks. Thus the ability to provision backable candidates based on a required path effectively decouples backing from inclusion. diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md index e0984bd58d1dd8ea22b145a15b4a3bab8779de80..39a317a07c847aa734e78f85de524f5541c8ddf6 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md @@ -125,6 +125,14 @@ execution request: reason, which may or may not be independent of the candidate or PVF. 5. **Internal errors:** See "Internal Errors" section. In this case, after the retry we abstain from voting. +6. **RuntimeConstruction** error. The precheck handles a general case of a wrong + artifact but doesn't guarantee its consistency between the preparation and + the execution. If something happened with the artifact between + the preparation of the artifact and its execution (e.g. the artifact was + corrupted on disk or a dirty node upgrade happened when the prepare worker + has a wasmtime version different from the execute worker's wasmtime version). + We treat such an error as possibly transient due to local issues and retry + one time. ### Preparation timeouts diff --git a/polkadot/roadmap/implementers-guide/src/types/runtime.md b/polkadot/roadmap/implementers-guide/src/types/runtime.md index 4b97409f8df3e4f3581161ef47713bfdfbcb9654..1dfabd96db7cdb929c416201eeeb233af11f5dbb 100644 --- a/polkadot/roadmap/implementers-guide/src/types/runtime.md +++ b/polkadot/roadmap/implementers-guide/src/types/runtime.md @@ -4,106 +4,24 @@ Types used within the runtime exclusively and pervasively. ## Host Configuration -The internal-to-runtime configuration of the parachain host. This is expected to be altered only by governance procedures. - -```rust -struct HostConfiguration { - /// The minimum period, in blocks, between which parachains can update their validation code. - pub validation_upgrade_cooldown: BlockNumber, - /// The delay, in blocks, before a validation upgrade is applied. - pub validation_upgrade_delay: BlockNumber, - /// How long to keep code on-chain, in blocks. This should be sufficiently long that disputes - /// have concluded. - pub code_retention_period: BlockNumber, - /// The maximum validation code size, in bytes. - pub max_code_size: u32, - /// The maximum head-data size, in bytes. - pub max_head_data_size: u32, - /// The amount of availability cores to dedicate to parathreads (on-demand parachains). - pub parathread_cores: u32, - /// The number of retries that a parathread (on-demand parachain) author has to submit their block. - pub parathread_retries: u32, - /// How often parachain groups should be rotated across parachains. - pub group_rotation_frequency: BlockNumber, - /// The availability period, in blocks, for parachains. This is the amount of blocks - /// after inclusion that validators have to make the block available and signal its availability to - /// the chain. Must be at least 1. - pub chain_availability_period: BlockNumber, - /// The availability period, in blocks, for parathreads (on-demand parachains). Same as the `chain_availability_period`, - /// but a differing timeout due to differing requirements. Must be at least 1. - pub thread_availability_period: BlockNumber, - /// The amount of blocks ahead to schedule on-demand parachains. - pub scheduling_lookahead: u32, - /// The maximum number of validators to have per core. `None` means no maximum. - pub max_validators_per_core: Option, - /// The maximum number of validators to use for parachains, in total. `None` means no maximum. - pub max_validators: Option, - /// The amount of sessions to keep for disputes. - pub dispute_period: SessionIndex, - /// How long after dispute conclusion to accept statements. - pub dispute_post_conclusion_acceptance_period: BlockNumber, - /// The maximum number of dispute spam slots - pub dispute_max_spam_slots: u32, - /// The amount of consensus slots that must pass between submitting an assignment and - /// submitting an approval vote before a validator is considered a no-show. - /// Must be at least 1. - pub no_show_slots: u32, - /// The number of delay tranches in total. - pub n_delay_tranches: u32, - /// The width of the zeroth delay tranche for approval assignments. This many delay tranches - /// beyond 0 are all consolidated to form a wide 0 tranche. - pub zeroth_delay_tranche_width: u32, - /// The number of validators needed to approve a block. - pub needed_approvals: u32, - /// The number of samples to use in `RelayVRFModulo` or `RelayVRFModuloCompact` approval assignment criterions. - pub relay_vrf_modulo_samples: u32, - /// Total number of individual messages allowed in the parachain -> relay-chain message queue. - pub max_upward_queue_count: u32, - /// Total size of messages allowed in the parachain -> relay-chain message queue before which - /// no further messages may be added to it. If it exceeds this then the queue may contain only - /// a single message. - pub max_upward_queue_size: u32, - /// The maximum size of an upward message that can be sent by a candidate. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub max_upward_message_size: u32, - /// The maximum number of messages that a candidate can contain. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub max_upward_message_num_per_candidate: u32, - /// The maximum size of a message that can be put in a downward message queue. - /// - /// Since we require receiving at least one DMP message the obvious upper bound of the size is - /// the PoV size. Of course, there is a lot of other different things that a parachain may - /// decide to do with its PoV so this value in practice will be picked as a fraction of the PoV - /// size. - pub max_downward_message_size: u32, - /// The deposit that the sender should provide for opening an HRMP channel. - pub hrmp_sender_deposit: u32, - /// The deposit that the recipient should provide for accepting opening an HRMP channel. - pub hrmp_recipient_deposit: u32, - /// The maximum number of messages allowed in an HRMP channel at once. - pub hrmp_channel_max_capacity: u32, - /// The maximum total size of messages in bytes allowed in an HRMP channel at once. - pub hrmp_channel_max_total_size: u32, - /// The maximum number of inbound HRMP channels a parachain is allowed to accept. - pub hrmp_max_parachain_inbound_channels: u32, - /// The maximum number of inbound HRMP channels a parathread (on-demand parachain) is allowed to accept. - pub hrmp_max_parathread_inbound_channels: u32, - /// The maximum size of a message that could ever be put into an HRMP channel. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub hrmp_channel_max_message_size: u32, - /// The maximum number of outbound HRMP channels a parachain is allowed to open. - pub hrmp_max_parachain_outbound_channels: u32, - /// The maximum number of outbound HRMP channels a parathread (on-demand parachain) is allowed to open. - pub hrmp_max_parathread_outbound_channels: u32, - /// The maximum number of outbound HRMP messages can be sent by a candidate. - /// - /// This parameter affects the upper bound of size of `CandidateCommitments`. - pub hrmp_max_message_num_per_candidate: u32, -} -``` +The internal-to-runtime configuration of the parachain host is kept in `struct HostConfiguration`. This is expected to +be altered only by governance procedures or via migrations from the Polkadot-SDK codebase. The latest definition of +`HostConfiguration` can be found in the project repo +[here](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/configuration.rs). Each +parameter has got a doc comment so for any details please refer to the code. + +Some related parameters in `HostConfiguration` are grouped together so that they can be managed easily. These are: +* `async_backing_params` in `struct AsyncBackingParams` +* `executor_params` in `struct ExecutorParams` +* `approval_voting_params` in `struct ApprovalVotingParams` +* `scheduler_params` in `struct SchedulerParams` + +Check the definitions of these structs for further details. + +### Configuration migrations +Modifying `HostConfiguration` requires a storage migration. These migrations are located in the +[`migrations`](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/configuration.rs) +subfolder of Polkadot-SDK repo. ## ParaInherentData diff --git a/polkadot/rpc/Cargo.toml b/polkadot/rpc/Cargo.toml index dcb7a13f6f31147123fe855b4d302205d8347a61..5af5e63b175380f7e695deb973ea48df52c326e4 100644 --- a/polkadot/rpc/Cargo.toml +++ b/polkadot/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-rpc" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -10,7 +10,7 @@ description = "Polkadot specific RPC functionality." workspace = true [dependencies] -jsonrpsee = { version = "0.16.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } polkadot-primitives = { path = "../primitives" } sc-client-api = { path = "../../substrate/client/api" } sp-blockchain = { path = "../../substrate/primitives/blockchain" } diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index f05963091dd65f87ef6837dd4d8df46ad154ddb4..c2205938295e38f8e683a211a09b6e3bce777e38 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-runtime-common" -version = "1.0.0" +version = "7.0.0" description = "Pallets and constants used in Relay Chain networks." authors.workspace = true edition.workspace = true @@ -13,11 +13,11 @@ workspace = true impl-trait-for-tuples = "0.2.2" bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } rustc-hex = { version = "2.1.0", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc"] } -serde_derive = { version = "1.0.117" } +serde = { features = ["alloc"], workspace = true } +serde_derive = { workspace = true } static_assertions = "1.1.0" sp-api = { path = "../../../substrate/primitives/api", default-features = false } @@ -58,8 +58,6 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac slot-range-helper = { path = "slot_range_helper", default-features = false } xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false, optional = true } - -pallet-xcm-benchmarks = { path = "../../xcm/pallet-xcm-benchmarks", default-features = false, optional = true } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } [dev-dependencies] @@ -69,7 +67,7 @@ pallet-babe = { path = "../../../substrate/frame/babe" } pallet-treasury = { path = "../../../substrate/frame/treasury" } sp-keystore = { path = "../../../substrate/primitives/keystore" } sp-keyring = { path = "../../../substrate/primitives/keyring" } -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } libsecp256k1 = "0.7.0" test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../primitives/test-helpers" } @@ -99,7 +97,6 @@ std = [ "pallet-transaction-payment/std", "pallet-treasury/std", "pallet-vesting/std", - "pallet-xcm-benchmarks/std", "parity-scale-codec/std", "primitives/std", "runtime-parachains/std", @@ -135,9 +132,9 @@ runtime-benchmarks = [ "pallet-identity/runtime-benchmarks", "pallet-staking/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", - "pallet-xcm-benchmarks/runtime-benchmarks", "primitives/runtime-benchmarks", "runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/polkadot/runtime/common/slot_range_helper/Cargo.toml b/polkadot/runtime/common/slot_range_helper/Cargo.toml index 3a402d011961f041214f82ca0d40d20318a9d88f..cacafd8ed3b746d35b5b064ca3d6662b313f8b86 100644 --- a/polkadot/runtime/common/slot_range_helper/Cargo.toml +++ b/polkadot/runtime/common/slot_range_helper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "slot-range-helper" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/runtime/common/src/assigned_slots/migration.rs b/polkadot/runtime/common/src/assigned_slots/migration.rs index ba3108c0aa38b9b9a2dabba0559d74ec3c97d0ed..def6bad692a235d3282774b0db3fabdaf33c89b3 100644 --- a/polkadot/runtime/common/src/assigned_slots/migration.rs +++ b/polkadot/runtime/common/src/assigned_slots/migration.rs @@ -29,14 +29,14 @@ pub mod v1 { impl OnRuntimeUpgrade for VersionUncheckedMigrateToV1 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - let onchain_version = Pallet::::on_chain_storage_version(); - ensure!(onchain_version < 1, "assigned_slots::MigrateToV1 migration can be deleted"); + let on_chain_version = Pallet::::on_chain_storage_version(); + ensure!(on_chain_version < 1, "assigned_slots::MigrateToV1 migration can be deleted"); Ok(Default::default()) } fn on_runtime_upgrade() -> frame_support::weights::Weight { - let onchain_version = Pallet::::on_chain_storage_version(); - if onchain_version < 1 { + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version < 1 { const MAX_PERMANENT_SLOTS: u32 = 100; const MAX_TEMPORARY_SLOTS: u32 = 100; @@ -52,8 +52,8 @@ pub mod v1 { #[cfg(feature = "try-runtime")] fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let onchain_version = Pallet::::on_chain_storage_version(); - ensure!(onchain_version == 1, "assigned_slots::MigrateToV1 needs to be run"); + let on_chain_version = Pallet::::on_chain_storage_version(); + ensure!(on_chain_version == 1, "assigned_slots::MigrateToV1 needs to be run"); assert_eq!(>::get(), 100); assert_eq!(>::get(), 100); Ok(()) diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index 9976a7be548fec845738f3e3e6c773b062ded413..4f032f4dfa3bd76b2e3ced899cec3a0bb5b06ffe 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -107,7 +107,7 @@ type LeasePeriodOf = <::Leaser as Leaser>>::Le pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -724,7 +724,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } diff --git a/polkadot/runtime/common/src/auctions.rs b/polkadot/runtime/common/src/auctions.rs index 0bd5ed1e733e04ea65c46a0c7d4e974a176483f2..46ab673a7a0cb9c685b486c89599c2d9dd324a0d 100644 --- a/polkadot/runtime/common/src/auctions.rs +++ b/polkadot/runtime/common/src/auctions.rs @@ -752,7 +752,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } diff --git a/polkadot/runtime/common/src/claims.rs b/polkadot/runtime/common/src/claims.rs index 5b87cc9619ed988037afa4777c08e057940aca98..57a95b22af3801dc22ba19cdee61b26c1cfe6f78 100644 --- a/polkadot/runtime/common/src/claims.rs +++ b/polkadot/runtime/common/src/claims.rs @@ -29,7 +29,11 @@ use scale_info::TypeInfo; use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256}; use sp_runtime::{ - traits::{CheckedSub, DispatchInfoOf, SignedExtension, Zero}, + impl_tx_ext_default, + traits::{ + AsSystemOriginSigner, CheckedSub, DispatchInfoOf, Dispatchable, TransactionExtension, + TransactionExtensionBase, Zero, + }, transaction_validity::{ InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, }, @@ -50,6 +54,7 @@ pub trait WeightInfo { fn claim_attest() -> Weight; fn attest() -> Weight; fn move_claim() -> Weight; + fn prevalidate_attests() -> Weight; } pub struct TestWeightInfo; @@ -69,6 +74,9 @@ impl WeightInfo for TestWeightInfo { fn move_claim() -> Weight { Weight::zero() } + fn prevalidate_attests() -> Weight { + Weight::zero() + } } /// The kind of statement an account needs to make for a claim to be valid. @@ -84,7 +92,7 @@ pub enum StatementKind { impl StatementKind { /// Convert this to the (English) statement it represents. - fn to_text(self) -> &'static [u8] { + pub fn to_text(self) -> &'static [u8] { match self { StatementKind::Regular => &b"I hereby agree to the terms of the statement whose SHA-256 multihash is \ @@ -166,6 +174,13 @@ pub mod pallet { #[pallet::without_storage_info] pub struct Pallet(_); + #[cfg(feature = "runtime-benchmarks")] + /// Helper trait to benchmark the `PrevalidateAttests` transaction extension. + pub trait BenchmarkHelperTrait { + /// `Call` to be used when benchmarking the transaction extension. + fn default_call_and_info() -> (RuntimeCall, DispatchInfo); + } + /// Configuration trait. #[pallet::config] pub trait Config: frame_system::Config { @@ -176,6 +191,12 @@ pub mod pallet { type Prefix: Get<&'static [u8]>; type MoveClaimOrigin: EnsureOrigin; type WeightInfo: WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + /// Benchmark helper + type BenchmarkHelper: BenchmarkHelperTrait< + Self::RuntimeCall, + DispatchInfoOf, + >; } #[pallet::event] @@ -402,7 +423,7 @@ pub mod pallet { /// Attest to a statement, needed to finalize the claims process. /// /// WARNING: Insecure unless your chain includes `PrevalidateAttests` as a - /// `SignedExtension`. + /// `TransactionExtension`. /// /// Unsigned Validation: /// A call to attest is deemed valid if the sender has a `Preclaim` registered @@ -612,46 +633,46 @@ impl PrevalidateAttests where ::RuntimeCall: IsSubType>, { - /// Create new `SignedExtension` to check runtime version. + /// Create new `TransactionExtension` to check runtime version. pub fn new() -> Self { Self(sp_std::marker::PhantomData) } } -impl SignedExtension for PrevalidateAttests +impl TransactionExtensionBase for PrevalidateAttests where ::RuntimeCall: IsSubType>, { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = (); - type Pre = (); const IDENTIFIER: &'static str = "PrevalidateAttests"; + type Implicit = (); +} - fn additional_signed(&self) -> Result { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } +impl TransactionExtension for PrevalidateAttests +where + ::RuntimeCall: IsSubType>, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner + Clone, +{ + type Pre = (); + type Val = (); // // The weight of this logic is included in the `attest` dispatchable. // fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &T::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; if let Some(local_call) = call.is_sub_type() { if let Call::attest { statement: attested_statement } = local_call { let signer = Preclaims::::get(who) @@ -662,8 +683,9 @@ where } } } - Ok(ValidTransaction::default()) + Ok((ValidTransaction::default(), (), origin)) } + impl_tx_ext_default!(T::RuntimeCall; Context; prepare); } #[cfg(any(test, feature = "runtime-benchmarks"))] @@ -696,13 +718,12 @@ mod secp_utils { } #[cfg(test)] -mod tests { +pub(super) mod tests { use super::*; use hex_literal::hex; use secp_utils::*; use parity_scale_codec::Encode; - use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use crate::claims; @@ -715,7 +736,7 @@ mod tests { }; use pallet_balances; use sp_runtime::{ - traits::{BlakeTwo256, Identity, IdentityLookup}, + traits::{DispatchTransaction, Identity}, transaction_validity::TransactionLongevity, BuildStorage, DispatchError::BadOrigin, @@ -734,34 +755,13 @@ mod tests { } ); - parameter_types! { - pub const BlockHashCount: u32 = 250; - } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; } @@ -782,7 +782,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } @@ -816,6 +815,19 @@ mod tests { type Prefix = Prefix; type MoveClaimOrigin = frame_system::EnsureSignedBy; type WeightInfo = TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); + } + + #[cfg(feature = "runtime-benchmarks")] + impl BenchmarkHelperTrait> for () { + fn default_call_and_info() -> (RuntimeCall, DispatchInfoOf) { + let call = RuntimeCall::Claims(crate::claims::Call::attest { + statement: StatementKind::Regular.to_text().to_vec(), + }); + let info = call.get_dispatch_info(); + (call, info) + } } fn alice() -> libsecp256k1::SecretKey { @@ -1097,8 +1109,8 @@ mod tests { }); let di = c.get_dispatch_info(); assert_eq!(di.pays_fee, Pays::No); - let r = p.validate(&42, &c, &di, 20); - assert_eq!(r, TransactionValidity::Ok(ValidTransaction::default())); + let r = p.validate_only(Some(42).into(), &c, &di, 20); + assert_eq!(r.unwrap().0, ValidTransaction::default()); }); } @@ -1110,13 +1122,13 @@ mod tests { statement: StatementKind::Regular.to_text().to_vec(), }); let di = c.get_dispatch_info(); - let r = p.validate(&42, &c, &di, 20); + let r = p.validate_only(Some(42).into(), &c, &di, 20); assert!(r.is_err()); let c = RuntimeCall::Claims(ClaimsCall::attest { statement: StatementKind::Saft.to_text().to_vec(), }); let di = c.get_dispatch_info(); - let r = p.validate(&69, &c, &di, 20); + let r = p.validate_only(Some(69).into(), &c, &di, 20); assert!(r.is_err()); }); } @@ -1470,14 +1482,17 @@ mod tests { } #[cfg(feature = "runtime-benchmarks")] -mod benchmarking { +pub(super) mod benchmarking { use super::*; use crate::claims::Call; use frame_benchmarking::{account, benchmarks}; use frame_support::traits::UnfilteredDispatchable; use frame_system::RawOrigin; use secp_utils::*; - use sp_runtime::{traits::ValidateUnsigned, DispatchResult}; + use sp_runtime::{ + traits::{DispatchTransaction, ValidateUnsigned}, + DispatchResult, + }; const SEED: u32 = 0; @@ -1513,6 +1528,11 @@ mod benchmarking { } benchmarks! { + where_clause { where ::RuntimeCall: IsSubType>, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner + Clone, + <::RuntimeCall as Dispatchable>::PostInfo: Default, + } + // Benchmark `claim` including `validate_unsigned` logic. claim { let c = MAX_CLAIMS; @@ -1691,6 +1711,38 @@ mod benchmarking { } } + prevalidate_attests { + let c = MAX_CLAIMS; + + for i in 0 .. c / 2 { + create_claim::(c)?; + create_claim_attest::(u32::MAX - c)?; + } + + let ext = PrevalidateAttests::::new(); + let (call, info) = T::BenchmarkHelper::default_call_and_info(); + let attest_c = u32::MAX - c; + let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); + let eth_address = eth(&secret_key); + let account: T::AccountId = account("user", c, SEED); + let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into())); + let statement = StatementKind::Regular; + let signature = sig::(&secret_key, &account.encode(), statement.to_text()); + super::Pallet::::mint_claim(RawOrigin::Root.into(), eth_address, VALUE.into(), vesting, Some(statement))?; + Preclaims::::insert(&account, eth_address); + assert_eq!(Claims::::get(eth_address), Some(VALUE.into())); + }: { + assert!(ext.test_run( + RawOrigin::Signed(account).into(), + &call, + &info, + 0, + |_| { + Ok(Default::default()) + } + ).unwrap().is_ok()); + } + impl_benchmark_test_suite!( Pallet, crate::claims::tests::new_test_ext(), diff --git a/polkadot/runtime/common/src/crowdloan/migration.rs b/polkadot/runtime/common/src/crowdloan/migration.rs index 5133c14ada9276a56ae91f38e8a67b4846979866..3afd6b3fbc94b5b30bad162cfcf8236a029b77da 100644 --- a/polkadot/runtime/common/src/crowdloan/migration.rs +++ b/polkadot/runtime/common/src/crowdloan/migration.rs @@ -24,9 +24,9 @@ use frame_support::{ pub struct MigrateToTrackInactiveV2(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToTrackInactiveV2 { fn on_runtime_upgrade() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 1 { + if on_chain_version == 1 { let mut translated = 0u64; for item in Funds::::iter_values() { let b = diff --git a/polkadot/runtime/common/src/crowdloan/mod.rs b/polkadot/runtime/common/src/crowdloan/mod.rs index b7dd06aee3a49f096608b881d207b1ffb83c1e65..35d075f2ff6c2f5ee3c1fb4a159eed346d6c6781 100644 --- a/polkadot/runtime/common/src/crowdloan/mod.rs +++ b/polkadot/runtime/common/src/crowdloan/mod.rs @@ -180,7 +180,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::{ensure_root, ensure_signed, pallet_prelude::*}; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); #[pallet::pallet] @@ -944,7 +944,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } diff --git a/polkadot/runtime/common/src/identity_migrator.rs b/polkadot/runtime/common/src/identity_migrator.rs index 0dfb03b06ba32b65941165ec993bc694b84a1389..bf334a63e9588411f57ecc8b6ee2fca72861d7aa 100644 --- a/polkadot/runtime/common/src/identity_migrator.rs +++ b/polkadot/runtime/common/src/identity_migrator.rs @@ -31,7 +31,7 @@ use pallet_identity; use sp_core::Get; #[cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}; +use frame_benchmarking::{account, v2::*, BenchmarkError}; pub trait WeightInfo { fn reap_identity(r: u32, s: u32) -> Weight; diff --git a/polkadot/runtime/common/src/impls.rs b/polkadot/runtime/common/src/impls.rs index 79ace0972adef57cda9a1b640040ba0a97365f64..4ba85a3c8353565ad17cfb5db29c3d4e1dfa8a44 100644 --- a/polkadot/runtime/common/src/impls.rs +++ b/polkadot/runtime/common/src/impls.rs @@ -107,7 +107,7 @@ pub fn era_payout( )] pub enum VersionedLocatableAsset { #[codec(index = 3)] - V3 { location: xcm::v3::MultiLocation, asset_id: xcm::v3::AssetId }, + V3 { location: xcm::v3::Location, asset_id: xcm::v3::AssetId }, #[codec(index = 4)] V4 { location: xcm::v4::Location, asset_id: xcm::v4::AssetId }, } @@ -140,7 +140,7 @@ impl TryConvert<&VersionedLocation, xcm::latest::Location> for VersionedLocation ) -> Result { let latest = match location.clone() { VersionedLocation::V2(l) => { - let v3: xcm::v3::MultiLocation = l.try_into().map_err(|_| location)?; + let v3: xcm::v3::Location = l.try_into().map_err(|_| location)?; v3.try_into().map_err(|_| location)? }, VersionedLocation::V3(l) => l.try_into().map_err(|_| location)?, @@ -191,11 +191,11 @@ pub mod benchmarks { { fn create_asset_kind(seed: u32) -> VersionedLocatableAsset { VersionedLocatableAsset::V3 { - location: xcm::v3::MultiLocation::new( + location: xcm::v3::Location::new( Parents::get(), [xcm::v3::Junction::Parachain(ParaId::get())], ), - asset_id: xcm::v3::MultiLocation::new( + asset_id: xcm::v3::Location::new( 0, [ xcm::v3::Junction::PalletInstance(seed.try_into().unwrap()), @@ -304,7 +304,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 2bf21bbde951e46d8e97f335a6cdbe5963f53692..3aa291f0f1f3075670cea40cee25645456057bdb 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -36,6 +36,7 @@ use pallet_identity::{self, legacy::IdentityInfo}; use parity_scale_codec::Encode; use primitives::{ BlockNumber, HeadData, Id as ParaId, SessionIndex, ValidationCode, LOWEST_PUBLIC_ID, + MAX_CODE_SIZE, }; use runtime_parachains::{ configuration, origin, paras, shared, Origin as ParaOrigin, ParaLifecycle, @@ -189,7 +190,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -316,7 +316,7 @@ pub fn new_test_ext() -> TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); configuration::GenesisConfig:: { config: configuration::HostConfiguration { - max_code_size: 2 * 1024 * 1024, // 2 MB + max_code_size: MAX_CODE_SIZE, max_head_data_size: 1 * 1024 * 1024, // 1 MB ..Default::default() }, @@ -930,8 +930,18 @@ fn basic_swap_works() { // Deposit is appropriately taken // ----------------------------------------- para deposit --- crowdloan - assert_eq!(Balances::reserved_balance(&account_id(1)), (500 + 10 * 2 * 1) + 100); - assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1); + let crowdloan_deposit = 100; + let para_id_deposit = ::ParaDeposit::get(); + let code_deposit = configuration::Pallet::::config().max_code_size * + ::DataDepositPerByte::get(); + + // Para 2000 has a genesis head size of 10. + assert_eq!( + Balances::reserved_balance(&account_id(1)), + crowdloan_deposit + para_id_deposit + code_deposit + 10 + ); + // Para 2001 has a genesis head size of 20. + assert_eq!(Balances::reserved_balance(&account_id(2)), para_id_deposit + code_deposit + 20); assert_eq!(Balances::reserved_balance(&crowdloan_account), total); // Crowdloan is appropriately set assert!(Crowdloan::funds(ParaId::from(2000)).is_some()); @@ -973,8 +983,8 @@ fn basic_swap_works() { // Deregister on-demand parachain assert_ok!(Registrar::deregister(para_origin(2000).into(), ParaId::from(2000))); // Correct deposit is unreserved - assert_eq!(Balances::reserved_balance(&account_id(1)), 100); // crowdloan deposit left over - assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1); + assert_eq!(Balances::reserved_balance(&account_id(1)), crowdloan_deposit); + assert_eq!(Balances::reserved_balance(&account_id(2)), para_id_deposit + code_deposit + 20); // Crowdloan ownership is swapped assert!(Crowdloan::funds(ParaId::from(2000)).is_none()); assert!(Crowdloan::funds(ParaId::from(2001)).is_some()); @@ -1005,7 +1015,7 @@ fn basic_swap_works() { // Dissolve returns the balance of the person who put a deposit for crowdloan assert_ok!(Crowdloan::dissolve(signed(1), ParaId::from(2001))); assert_eq!(Balances::reserved_balance(&account_id(1)), 0); - assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1); + assert_eq!(Balances::reserved_balance(&account_id(2)), para_id_deposit + code_deposit + 20); // Final deregister sets everything back to the start assert_ok!(Registrar::deregister(para_origin(2001).into(), ParaId::from(2001))); diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index a3bdc1b1b0f5bf2cedfe576a26e081c786c13732..4541b88ec6fe187988a949cda1338de59f258541 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -26,7 +26,7 @@ use frame_support::{ traits::{Currency, Get, ReservableCurrency}, }; use frame_system::{self, ensure_root, ensure_signed}; -use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; +use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID, MIN_CODE_SIZE}; use runtime_parachains::{ configuration, ensure_parachain, paras::{self, ParaGenesisArgs, SetGoAhead}, @@ -106,7 +106,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -182,8 +182,8 @@ pub mod pallet { ParaLocked, /// The ID given for registration has not been reserved. NotReserved, - /// Registering parachain with empty code is not allowed. - EmptyCode, + /// The validation code is invalid. + InvalidCode, /// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras /// are correct for the swap to work. CannotSwap, @@ -239,7 +239,13 @@ pub mod pallet { /// - `validation_code`: The initial validation code of the parachain/thread. /// /// ## Deposits/Fees - /// The origin signed account must reserve a corresponding deposit for the registration. + /// The account with the originating signature must reserve a deposit. + /// + /// The deposit is required to cover the costs associated with storing the genesis head + /// data and the validation code. + /// This accounts for the potential to store validation code of a size up to the + /// `max_code_size`, as defined in the configuration pallet + /// /// Anything already reserved previously for this para ID is accounted for. /// /// ## Events @@ -651,7 +657,7 @@ impl Pallet { para_kind: ParaKind, ) -> Result<(ParaGenesisArgs, BalanceOf), sp_runtime::DispatchError> { let config = configuration::Pallet::::config(); - ensure!(validation_code.0.len() > 0, Error::::EmptyCode); + ensure!(validation_code.0.len() >= MIN_CODE_SIZE as usize, Error::::InvalidCode); ensure!(validation_code.0.len() <= config.max_code_size as usize, Error::::CodeTooLarge); ensure!( genesis_head.0.len() <= config.max_head_data_size as usize, @@ -661,7 +667,7 @@ impl Pallet { let per_byte_fee = T::DataDepositPerByte::get(); let deposit = T::ParaDeposit::get() .saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into())) - .saturating_add(per_byte_fee.saturating_mul((validation_code.0.len() as u32).into())); + .saturating_add(per_byte_fee.saturating_mul(config.max_code_size.into())); Ok((ParaGenesisArgs { genesis_head, validation_code, para_kind }, deposit)) } @@ -706,7 +712,7 @@ mod tests { }; use frame_system::limits; use pallet_balances::Error as BalancesError; - use primitives::{Balance, BlockNumber, SessionIndex}; + use primitives::{Balance, BlockNumber, SessionIndex, MAX_CODE_SIZE}; use runtime_parachains::{configuration, origin, shared}; use sp_core::H256; use sp_io::TestExternalities; @@ -795,7 +801,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } @@ -844,7 +849,7 @@ mod tests { configuration::GenesisConfig:: { config: configuration::HostConfiguration { - max_code_size: 2 * 1024 * 1024, // 2 MB + max_code_size: MAX_CODE_SIZE, max_head_data_size: 1 * 1024 * 1024, // 1 MB ..Default::default() }, @@ -1013,10 +1018,61 @@ mod tests { run_to_session(START_SESSION_INDEX + 2); assert!(Parachains::is_parathread(para_id)); + // Even though the registered validation code has a smaller size than the maximum the + // para manager's deposit is reserved as though they registered the maximum-sized code. + // Consequently, they can upgrade their code to the maximum size at any point without + // additional cost. + let validation_code_deposit = + max_code_size() as BalanceOf * ::DataDepositPerByte::get(); + let head_deposit = 32 * ::DataDepositPerByte::get(); assert_eq!( Balances::reserved_balance(&1), - ::ParaDeposit::get() + - 64 * ::DataDepositPerByte::get() + ::ParaDeposit::get() + head_deposit + validation_code_deposit + ); + }); + } + + #[test] + fn schedule_code_upgrade_validates_code() { + new_test_ext().execute_with(|| { + const START_SESSION_INDEX: SessionIndex = 1; + run_to_session(START_SESSION_INDEX); + + let para_id = LOWEST_PUBLIC_ID; + assert!(!Parachains::is_parathread(para_id)); + + let validation_code = test_validation_code(32); + assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1))); + assert_eq!(Balances::reserved_balance(&1), ::ParaDeposit::get()); + assert_ok!(Registrar::register( + RuntimeOrigin::signed(1), + para_id, + test_genesis_head(32), + validation_code.clone(), + )); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + + run_to_session(START_SESSION_INDEX + 2); + assert!(Parachains::is_parathread(para_id)); + + let new_code = test_validation_code(0); + assert_noop!( + Registrar::schedule_code_upgrade( + RuntimeOrigin::signed(1), + para_id, + new_code.clone(), + ), + paras::Error::::InvalidCode + ); + + let new_code = test_validation_code(max_code_size() as usize + 1); + assert_noop!( + Registrar::schedule_code_upgrade( + RuntimeOrigin::signed(1), + para_id, + new_code.clone(), + ), + paras::Error::::InvalidCode ); }); } @@ -1299,7 +1355,7 @@ mod tests { RuntimeOrigin::signed(1), para_id, vec![1; 3].into(), - vec![1, 2, 3].into(), + test_validation_code(32) )); assert_noop!(Registrar::add_lock(RuntimeOrigin::signed(2), para_id), BadOrigin); @@ -1462,7 +1518,7 @@ mod benchmarking { use crate::traits::Registrar as RegistrarT; use frame_support::assert_ok; use frame_system::RawOrigin; - use primitives::{MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE}; + use primitives::{MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MIN_CODE_SIZE}; use runtime_parachains::{paras, shared, Origin as ParaOrigin}; use sp_runtime::traits::Bounded; @@ -1593,7 +1649,7 @@ mod benchmarking { } schedule_code_upgrade { - let b in 1 .. MAX_CODE_SIZE; + let b in MIN_CODE_SIZE .. MAX_CODE_SIZE; let new_code = ValidationCode(vec![0; b as usize]); let para_id = ParaId::from(1000); }: _(RawOrigin::Root, para_id, new_code) diff --git a/polkadot/runtime/common/src/purchase.rs b/polkadot/runtime/common/src/purchase.rs index 146a90fca866abab147e4da0a77c7e296773b10f..301f1f21fbce90d311c9b06ae376f2c99931ef27 100644 --- a/polkadot/runtime/common/src/purchase.rs +++ b/polkadot/runtime/common/src/purchase.rs @@ -556,7 +556,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } diff --git a/polkadot/runtime/common/src/slots/mod.rs b/polkadot/runtime/common/src/slots/mod.rs index be02aa9961cc292b45e7c8fba522608fd3d05f6a..51bd0ba4debe6531da9cca604bb6aa069ad016f3 100644 --- a/polkadot/runtime/common/src/slots/mod.rs +++ b/polkadot/runtime/common/src/slots/mod.rs @@ -574,7 +574,6 @@ mod tests { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<1>; type MaxFreezes = ConstU32<1>; } diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index 7f1100a13619a8c5e1048afd4a34d2d977aa8914..46d09afcfb2c80215ed7ab18eb7be5750de1fcf6 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -136,7 +136,7 @@ where } } -/// Implementation of `pallet_xcm_benchmarks::EnsureDelivery` which helps to ensure delivery to the +/// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the /// `ParaId` parachain (sibling or child). Deposits existential deposit for origin (if needed). /// Deposits estimated fee to the origin account (if needed). /// Allows to trigger additional logic for specific `ParaId` (e.g. open HRMP channel) (if neeeded). @@ -164,7 +164,7 @@ impl< PriceForDelivery: PriceForMessageDelivery, Parachain: Get, ToParachainHelper: EnsureForParachain, - > pallet_xcm_benchmarks::EnsureDelivery + > xcm_builder::EnsureDelivery for ToParachainDeliveryHelper< XcmConfig, ExistentialDeposit, @@ -175,7 +175,7 @@ impl< { fn ensure_successful_delivery( origin_ref: &Location, - _dest: &Location, + dest: &Location, fee_reason: xcm_executor::traits::FeeReason, ) -> (Option, Option) { use xcm_executor::{ @@ -183,6 +183,15 @@ impl< FeesMode, }; + // check if the destination matches the expected `Parachain`. + if let Some(Parachain(para_id)) = dest.first_interior() { + if ParaId::from(*para_id) != Parachain::get().into() { + return (None, None) + } + } else { + return (None, None) + } + let mut fees_mode = None; if !XcmConfig::FeeManager::is_waived(Some(origin_ref), fee_reason) { // if not waived, we need to set up accounts for paying and receiving fees diff --git a/polkadot/runtime/metrics/Cargo.toml b/polkadot/runtime/metrics/Cargo.toml index 9a16749bf602f9c27bcc39c6ebe71138eeb4cc07..481627542865e1e79e4be790c3ccf46cc5878c07 100644 --- a/polkadot/runtime/metrics/Cargo.toml +++ b/polkadot/runtime/metrics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-runtime-metrics" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index dcfb7108dd25c7b6ac2324a70d6bebc2373d0fc2..6104014547638dbf13c5b080a280e7cf369293c9 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-runtime-parachains" -version = "1.0.0" +version = "7.0.0" description = "Relay Chain runtime code responsible for Parachains." authors.workspace = true edition.workspace = true @@ -13,10 +13,10 @@ workspace = true impl-trait-for-tuples = "0.2.2" bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } rustc-hex = { version = "2.1.0", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"] } +serde = { features = ["alloc", "derive"], workspace = true } derive_more = "0.99.17" bitflags = "1.3.2" @@ -66,9 +66,11 @@ frame-support-test = { path = "../../../substrate/frame/support/test" } sc-keystore = { path = "../../../substrate/client/keystore" } test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../primitives/test-helpers" } sp-tracing = { path = "../../../substrate/primitives/tracing" } +sp-crypto-hashing = { path = "../../../substrate/primitives/crypto/hashing" } thousands = "0.2.0" assert_matches = "1" -serde_json = "1.0.111" +rstest = "0.18.2" +serde_json = { workspace = true, default-features = true } [features] default = ["std"] diff --git a/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs b/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs index 71c3f1fa39f7c6c127ac954b8299fb16a323e72d..e2ba0b4f7ea5d5ca2eb49c903afb764229e0491f 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/mock_helpers.rs @@ -64,11 +64,12 @@ impl GenesisConfigBuilder { pub(super) fn build(self) -> MockGenesisConfig { let mut genesis = default_genesis_config(); let config = &mut genesis.configuration.config; - config.coretime_cores = self.on_demand_cores; - config.on_demand_base_fee = self.on_demand_base_fee; - config.on_demand_fee_variability = self.on_demand_fee_variability; - config.on_demand_queue_max_size = self.on_demand_max_queue_size; - config.on_demand_target_queue_utilization = self.on_demand_target_queue_utilization; + config.scheduler_params.num_cores = self.on_demand_cores; + config.scheduler_params.on_demand_base_fee = self.on_demand_base_fee; + config.scheduler_params.on_demand_fee_variability = self.on_demand_fee_variability; + config.scheduler_params.on_demand_queue_max_size = self.on_demand_max_queue_size; + config.scheduler_params.on_demand_target_queue_utilization = + self.on_demand_target_queue_utilization; let paras = &mut genesis.paras.paras; for para_id in self.onboarded_on_demand_chains { diff --git a/polkadot/runtime/parachains/src/assigner_coretime/mod.rs b/polkadot/runtime/parachains/src/assigner_coretime/mod.rs index 9da81dc816cabeb7019e44b8f88c0f526582830d..e2da89fadd4800f5de32650178766e1ed85bbd17 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/mod.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/mod.rs @@ -30,7 +30,7 @@ mod tests; use crate::{ assigner_on_demand, configuration, paras::AssignCoretime, - scheduler::common::{Assignment, AssignmentProvider, AssignmentProviderConfig}, + scheduler::common::{Assignment, AssignmentProvider}, ParaId, }; @@ -316,14 +316,6 @@ impl AssignmentProvider> for Pallet { } } - fn get_provider_config(_core_idx: CoreIndex) -> AssignmentProviderConfig> { - let config = >::config(); - AssignmentProviderConfig { - max_availability_timeouts: config.on_demand_retries, - ttl: config.on_demand_ttl, - } - } - #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(_: CoreIndex, para_id: primitives::Id) -> Assignment { // Given that we are not tracking anything in `Bulk` assignments, it is safe to always @@ -333,7 +325,7 @@ impl AssignmentProvider> for Pallet { fn session_core_count() -> u32 { let config = >::config(); - config.coretime_cores + config.scheduler_params.num_cores } } @@ -482,8 +474,8 @@ impl AssignCoretime for Pallet { // Add a new core and assign the para to it. let mut config = >::config(); - let core = config.coretime_cores; - config.coretime_cores.saturating_inc(); + let core = config.scheduler_params.num_cores; + config.scheduler_params.num_cores.saturating_inc(); // `assign_coretime` is only called at genesis or by root, so setting the active // config here is fine. diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs b/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs index 5a6060cd2b4eab88867088dee30b6fb1047bdf20..8360e7a78d0a5a155f5b9d63ffae05bb85e44f32 100644 --- a/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs +++ b/polkadot/runtime/parachains/src/assigner_on_demand/benchmarking.rs @@ -43,7 +43,7 @@ where { ParasShared::::set_session_index(SESSION_INDEX); let mut config = HostConfiguration::default(); - config.coretime_cores = 1; + config.scheduler_params.num_cores = 1; ConfigurationPallet::::force_set_active_config(config); let mut parachains = ParachainsCache::new(); ParasPallet::::initialize_para_now( diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs b/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs index de30330ac84e0a7715799d71d26fb42ce48efff8..f8d1a894f0e4d5c619377ba4c34158c8f6d60597 100644 --- a/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs +++ b/polkadot/runtime/parachains/src/assigner_on_demand/mock_helpers.rs @@ -63,11 +63,12 @@ impl GenesisConfigBuilder { pub(super) fn build(self) -> MockGenesisConfig { let mut genesis = default_genesis_config(); let config = &mut genesis.configuration.config; - config.coretime_cores = self.on_demand_cores; - config.on_demand_base_fee = self.on_demand_base_fee; - config.on_demand_fee_variability = self.on_demand_fee_variability; - config.on_demand_queue_max_size = self.on_demand_max_queue_size; - config.on_demand_target_queue_utilization = self.on_demand_target_queue_utilization; + config.scheduler_params.num_cores = self.on_demand_cores; + config.scheduler_params.on_demand_base_fee = self.on_demand_base_fee; + config.scheduler_params.on_demand_fee_variability = self.on_demand_fee_variability; + config.scheduler_params.on_demand_queue_max_size = self.on_demand_max_queue_size; + config.scheduler_params.on_demand_target_queue_utilization = + self.on_demand_target_queue_utilization; let paras = &mut genesis.paras.paras; for para_id in self.onboarded_on_demand_chains { diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs b/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs index 1b746e88694c9f105db119d351a76e336fd3fdba..bc450dc78129ad7404627e49b9882e2723c09f08 100644 --- a/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs +++ b/polkadot/runtime/parachains/src/assigner_on_demand/mod.rs @@ -201,10 +201,10 @@ pub mod pallet { let old_traffic = SpotTraffic::::get(); match Self::calculate_spot_traffic( old_traffic, - config.on_demand_queue_max_size, + config.scheduler_params.on_demand_queue_max_size, Self::queue_size(), - config.on_demand_target_queue_utilization, - config.on_demand_fee_variability, + config.scheduler_params.on_demand_target_queue_utilization, + config.scheduler_params.on_demand_fee_variability, ) { Ok(new_traffic) => { // Only update storage on change @@ -330,8 +330,9 @@ where let traffic = SpotTraffic::::get(); // Calculate spot price - let spot_price: BalanceOf = - traffic.saturating_mul_int(config.on_demand_base_fee.saturated_into::>()); + let spot_price: BalanceOf = traffic.saturating_mul_int( + config.scheduler_params.on_demand_base_fee.saturated_into::>(), + ); // Is the current price higher than `max_amount` ensure!(spot_price.le(&max_amount), Error::::SpotPriceHigherThanMaxAmount); @@ -450,7 +451,10 @@ where OnDemandQueue::::try_mutate(|queue| { // Abort transaction if queue is too large - ensure!(Self::queue_size() < config.on_demand_queue_max_size, Error::::QueueFull); + ensure!( + Self::queue_size() < config.scheduler_params.on_demand_queue_max_size, + Error::::QueueFull + ); match location { QueuePushDirection::Back => queue.push_back(order), QueuePushDirection::Front => queue.push_front(order), @@ -472,7 +476,7 @@ where target: LOG_TARGET, "Failed to fetch the on demand queue size, returning the max size." ); - return config.on_demand_queue_max_size + return config.scheduler_params.on_demand_queue_max_size }, } } diff --git a/polkadot/runtime/parachains/src/assigner_parachains.rs b/polkadot/runtime/parachains/src/assigner_parachains.rs index 34b5d3c1ec51811e8e4c50255592b1e9344014d8..b5f342563e9763e9dba0fa8cbc7afd8e84dc59da 100644 --- a/polkadot/runtime/parachains/src/assigner_parachains.rs +++ b/polkadot/runtime/parachains/src/assigner_parachains.rs @@ -27,7 +27,7 @@ use primitives::CoreIndex; use crate::{ configuration, paras, - scheduler::common::{Assignment, AssignmentProvider, AssignmentProviderConfig}, + scheduler::common::{Assignment, AssignmentProvider}, }; pub use pallet::*; @@ -58,16 +58,6 @@ impl AssignmentProvider> for Pallet { /// this is a no-op in the case of a bulk assignment slot. fn push_back_assignment(_: Assignment) {} - fn get_provider_config(_core_idx: CoreIndex) -> AssignmentProviderConfig> { - AssignmentProviderConfig { - // The next assignment already goes to the same [`ParaId`], no timeout tracking needed. - max_availability_timeouts: 0, - // The next assignment already goes to the same [`ParaId`], this can be any number - // that's high enough to clear the time it takes to clear backing/availability. - ttl: 10u32.into(), - } - } - #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(_: CoreIndex, para_id: primitives::Id) -> Assignment { Assignment::Bulk(para_id) diff --git a/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs b/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs index e6e9fb074aa97e87e6fe92819cc57e5bfa2ca656..a46e114daeaf458fb6bc985a5bb2ebad951d7e3b 100644 --- a/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs +++ b/polkadot/runtime/parachains/src/assigner_parachains/mock_helpers.rs @@ -60,11 +60,12 @@ impl GenesisConfigBuilder { pub(super) fn build(self) -> MockGenesisConfig { let mut genesis = default_genesis_config(); let config = &mut genesis.configuration.config; - config.coretime_cores = self.on_demand_cores; - config.on_demand_base_fee = self.on_demand_base_fee; - config.on_demand_fee_variability = self.on_demand_fee_variability; - config.on_demand_queue_max_size = self.on_demand_max_queue_size; - config.on_demand_target_queue_utilization = self.on_demand_target_queue_utilization; + config.scheduler_params.num_cores = self.on_demand_cores; + config.scheduler_params.on_demand_base_fee = self.on_demand_base_fee; + config.scheduler_params.on_demand_fee_variability = self.on_demand_fee_variability; + config.scheduler_params.on_demand_queue_max_size = self.on_demand_max_queue_size; + config.scheduler_params.on_demand_target_queue_utilization = + self.on_demand_target_queue_utilization; let paras = &mut genesis.paras.paras; for para_id in self.onboarded_on_demand_chains { diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 016b3fca589a5b845110d9f25199cc8b8aef5bfe..5b218addb1a2552d3f9daa3dbb7baf352023f919 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -18,23 +18,20 @@ use crate::{ configuration, inclusion, initializer, paras, paras::ParaKind, paras_inherent, - scheduler::{ - self, - common::{AssignmentProvider, AssignmentProviderConfig}, - CoreOccupied, ParasEntry, - }, + scheduler::{self, common::AssignmentProvider, CoreOccupied, ParasEntry}, session_info, shared, }; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use primitives::{ - collator_signature_payload, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, - CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, - PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, - ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, + collator_signature_payload, vstaging::node_features::FeatureIndex, AvailabilityBitfield, + BackedCandidate, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, + CollatorSignature, CommittedCandidateReceipt, CompactStatement, CoreIndex, DisputeStatement, + DisputeStatementSet, GroupIndex, HeadData, Id as ParaId, IndexedVec, + InherentData as ParachainsInherentData, InvalidDisputeStatementKind, PersistedValidationData, + SessionIndex, SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, + ValidatorId, ValidatorIndex, ValidityAttestation, }; use sp_core::{sr25519, H256}; use sp_runtime::{ @@ -197,7 +194,10 @@ impl BenchBuilder { /// Maximum number of validators per core (a.k.a. max validators per group). This value is used /// if none is explicitly set on the builder. pub(crate) fn fallback_max_validators_per_core() -> u32 { - configuration::Pallet::::config().max_validators_per_core.unwrap_or(5) + configuration::Pallet::::config() + .scheduler_params + .max_validators_per_core + .unwrap_or(5) } /// Specify a mapping of core index/ para id to the number of dispute statements for the @@ -510,7 +510,7 @@ impl BenchBuilder { .iter() .map(|(seed, num_votes)| { assert!(*num_votes <= validators.len() as u32); - let (para_id, _core_idx, group_idx) = self.create_indexes(*seed); + let (para_id, core_idx, group_idx) = self.create_indexes(*seed); // This generates a pair and adds it to the keystore, returning just the public. let collator_public = CollatorId::generate_pair(None); @@ -587,11 +587,19 @@ impl BenchBuilder { }) .collect(); - BackedCandidate:: { + // Check if the elastic scaling bit is set, if so we need to supply the core index + // in the generated candidate. + let core_idx = configuration::Pallet::::config() + .node_features + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|_the_bit| core_idx); + + BackedCandidate::::new( candidate, validity_votes, - validator_indices: bitvec::bitvec![u8, bitvec::order::Lsb0; 1; group_validators.len()], - } + bitvec::bitvec![u8, bitvec::order::Lsb0; 1; group_validators.len()], + core_idx, + ) }) .collect() } @@ -683,7 +691,7 @@ impl BenchBuilder { // We are currently in Session 0, so these changes will take effect in Session 2. Self::setup_para_ids(used_cores); configuration::ActiveConfig::::mutate(|c| { - c.coretime_cores = used_cores; + c.scheduler_params.num_cores = used_cores; }); let validator_ids = Self::generate_validator_pairs(self.max_validators()); @@ -714,8 +722,7 @@ impl BenchBuilder { let cores = (0..used_cores) .into_iter() .map(|i| { - let AssignmentProviderConfig { ttl, .. } = - scheduler::Pallet::::assignment_provider_config(CoreIndex(i)); + let ttl = configuration::Pallet::::config().scheduler_params.ttl; // Load an assignment into provider so that one is present to pop let assignment = ::AssignmentProvider::get_mock_assignment( CoreIndex(i), @@ -730,8 +737,7 @@ impl BenchBuilder { let cores = (0..used_cores) .into_iter() .map(|i| { - let AssignmentProviderConfig { ttl, .. } = - scheduler::Pallet::::assignment_provider_config(CoreIndex(i)); + let ttl = configuration::Pallet::::config().scheduler_params.ttl; // Load an assignment into provider so that one is present to pop let assignment = ::AssignmentProvider::get_mock_assignment( diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index 4619313590ebc2d5d92dfc85a4e845c381c61607..364a15215d38c5eae477b3a73987a43d0216278b 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -29,7 +29,6 @@ use primitives::{ vstaging::{ApprovalVotingParams, NodeFeatures}, AsyncBackingParams, Balance, ExecutorParamError, ExecutorParams, SessionIndex, LEGACY_MIN_BACKING_VOTES, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, - ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, }; use sp_runtime::{traits::Zero, Perbill}; use sp_std::prelude::*; @@ -43,6 +42,7 @@ mod benchmarking; pub mod migration; pub use pallet::*; +use primitives::vstaging::SchedulerParams; const LOG_TARGET: &str = "runtime::configuration"; @@ -118,9 +118,9 @@ pub struct HostConfiguration { /// been completed. /// /// Note, there are situations in which `expected_at` in the past. For example, if - /// [`paras_availability_period`](Self::paras_availability_period) is less than the delay set - /// by this field or if PVF pre-check took more time than the delay. In such cases, the upgrade - /// is further at the earliest possible time determined by + /// [`paras_availability_period`](SchedulerParams::paras_availability_period) is less than the + /// delay set by this field or if PVF pre-check took more time than the delay. In such cases, + /// the upgrade is further at the earliest possible time determined by /// [`minimum_validation_upgrade_delay`](Self::minimum_validation_upgrade_delay). /// /// The rationale for this delay has to do with relay-chain reversions. In case there is an @@ -172,48 +172,7 @@ pub struct HostConfiguration { /// How long to keep code on-chain, in blocks. This should be sufficiently long that disputes /// have concluded. pub code_retention_period: BlockNumber, - /// How many cores are managed by the coretime chain. - pub coretime_cores: u32, - /// The number of retries that a on demand author has to submit their block. - pub on_demand_retries: 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 an on demand claim stays in the scheduler's claimqueue before getting - /// cleared. This number should go reasonably higher than the number of blocks in the async - /// backing lookahead. - pub on_demand_ttl: BlockNumber, - /// How often parachain groups should be rotated across parachains. - /// - /// Must be non-zero. - pub group_rotation_frequency: BlockNumber, - /// The minimum availability period, in blocks. - /// - /// This is the minimum 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 amount of blocks ahead to schedule paras. - pub scheduling_lookahead: u32, - /// The maximum number of validators to have per core. - /// - /// `None` means no maximum. - pub max_validators_per_core: Option, + /// The maximum number of validators to use for parachain consensus, period. /// /// `None` means no maximum. @@ -257,7 +216,7 @@ pub struct HostConfiguration { /// scheduled. This number is controlled by this field. /// /// This value should be greater than - /// [`paras_availability_period`](Self::paras_availability_period). + /// [`paras_availability_period`](SchedulerParams::paras_availability_period). pub minimum_validation_upgrade_delay: BlockNumber, /// The minimum number of valid backing statements required to consider a parachain candidate /// backable. @@ -266,6 +225,8 @@ pub struct HostConfiguration { pub node_features: NodeFeatures, /// Params used by approval-voting pub approval_voting_params: ApprovalVotingParams, + /// Scheduler parameters + pub scheduler_params: SchedulerParams, } impl> Default for HostConfiguration { @@ -275,19 +236,13 @@ impl> Default for HostConfiguration> Default for HostConfiguration { ZeroMinimumBackingVotes, /// `executor_params` are inconsistent. InconsistentExecutorParams { inner: ExecutorParamError }, + /// TTL should be bigger than lookahead + LookaheadExceedsTTL, } impl HostConfiguration @@ -373,11 +326,11 @@ where pub fn check_consistency(&self) -> Result<(), InconsistentError> { use InconsistentError::*; - if self.group_rotation_frequency.is_zero() { + if self.scheduler_params.group_rotation_frequency.is_zero() { return Err(ZeroGroupRotationFrequency) } - if self.paras_availability_period.is_zero() { + if self.scheduler_params.paras_availability_period.is_zero() { return Err(ZeroParasAvailabilityPeriod) } @@ -399,10 +352,11 @@ where return Err(MaxPovSizeExceedHardLimit { max_pov_size: self.max_pov_size }) } - if self.minimum_validation_upgrade_delay <= self.paras_availability_period { + if self.minimum_validation_upgrade_delay <= self.scheduler_params.paras_availability_period + { return Err(MinimumValidationUpgradeDelayLessThanChainAvailabilityPeriod { minimum_validation_upgrade_delay: self.minimum_validation_upgrade_delay.clone(), - paras_availability_period: self.paras_availability_period.clone(), + paras_availability_period: self.scheduler_params.paras_availability_period.clone(), }) } @@ -447,6 +401,10 @@ where return Err(InconsistentExecutorParams { inner }) } + if self.scheduler_params.ttl < self.scheduler_params.lookahead.into() { + return Err(LookaheadExceedsTTL) + } + Ok(()) } @@ -471,6 +429,7 @@ pub trait WeightInfo { fn set_config_with_executor_params() -> Weight; fn set_config_with_perbill() -> Weight; fn set_node_feature() -> Weight; + fn set_config_with_scheduler_params() -> Weight; } pub struct TestWeightInfo; @@ -499,13 +458,16 @@ impl WeightInfo for TestWeightInfo { fn set_node_feature() -> Weight { Weight::MAX } + fn set_config_with_scheduler_params() -> Weight { + Weight::MAX + } } #[frame_support::pallet] pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. /// /// v0-v1: /// v1-v2: @@ -520,7 +482,8 @@ pub mod pallet { /// v8-v9: /// v9-v10: /// v10-11: - const STORAGE_VERSION: StorageVersion = StorageVersion::new(11); + /// v11-12: + const STORAGE_VERSION: StorageVersion = StorageVersion::new(12); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -679,16 +642,16 @@ pub mod pallet { Self::set_coretime_cores_unchecked(new) } - /// Set the number of retries for a particular on demand. + /// Set the max number of times a claim may timeout on a core before it is abandoned #[pallet::call_index(7)] #[pallet::weight(( T::WeightInfo::set_config_with_u32(), DispatchClass::Operational, ))] - pub fn set_on_demand_retries(origin: OriginFor, new: u32) -> DispatchResult { + pub fn set_max_availability_timeouts(origin: OriginFor, new: u32) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_retries = new; + config.scheduler_params.max_availability_timeouts = new; }) } @@ -704,7 +667,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.group_rotation_frequency = new; + config.scheduler_params.group_rotation_frequency = new; }) } @@ -720,7 +683,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.paras_availability_period = new; + config.scheduler_params.paras_availability_period = new; }) } @@ -733,7 +696,7 @@ pub mod pallet { pub fn set_scheduling_lookahead(origin: OriginFor, new: u32) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.scheduling_lookahead = new; + config.scheduler_params.lookahead = new; }) } @@ -749,7 +712,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.max_validators_per_core = new; + config.scheduler_params.max_validators_per_core = new; }) } @@ -1141,7 +1104,7 @@ pub mod pallet { pub fn set_on_demand_base_fee(origin: OriginFor, new: Balance) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_base_fee = new; + config.scheduler_params.on_demand_base_fee = new; }) } @@ -1154,7 +1117,7 @@ pub mod pallet { pub fn set_on_demand_fee_variability(origin: OriginFor, new: Perbill) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_fee_variability = new; + config.scheduler_params.on_demand_fee_variability = new; }) } @@ -1167,7 +1130,7 @@ pub mod pallet { pub fn set_on_demand_queue_max_size(origin: OriginFor, new: u32) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_queue_max_size = new; + config.scheduler_params.on_demand_queue_max_size = new; }) } /// Set the on demand (parathreads) fee variability. @@ -1182,7 +1145,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_target_queue_utilization = new; + config.scheduler_params.on_demand_target_queue_utilization = new; }) } /// Set the on demand (parathreads) ttl in the claimqueue. @@ -1194,7 +1157,7 @@ pub mod pallet { pub fn set_on_demand_ttl(origin: OriginFor, new: BlockNumberFor) -> DispatchResult { ensure_root(origin)?; Self::schedule_config_update(|config| { - config.on_demand_ttl = new; + config.scheduler_params.ttl = new; }) } @@ -1244,6 +1207,22 @@ pub mod pallet { config.approval_voting_params = new; }) } + + /// Set scheduler-params. + #[pallet::call_index(55)] + #[pallet::weight(( + T::WeightInfo::set_config_with_scheduler_params(), + DispatchClass::Operational, + ))] + pub fn set_scheduler_params( + origin: OriginFor, + new: SchedulerParams>, + ) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.scheduler_params = new; + }) + } } impl Pallet { @@ -1252,7 +1231,7 @@ pub mod pallet { /// To be used if authorization is checked otherwise. pub fn set_coretime_cores_unchecked(new: u32) -> DispatchResult { Self::schedule_config_update(|config| { - config.coretime_cores = new; + config.scheduler_params.num_cores = new; }) } } @@ -1393,7 +1372,7 @@ impl Pallet { let base_config_consistent = base_config.check_consistency().is_ok(); // Now, we need to decide what the new configuration should be. - // We also move the `base_config` to `new_config` to empahsize that the base config was + // We also move the `base_config` to `new_config` to emphasize that the base config was // destroyed by the `updater`. updater(&mut base_config); let new_config = base_config; diff --git a/polkadot/runtime/parachains/src/configuration/benchmarking.rs b/polkadot/runtime/parachains/src/configuration/benchmarking.rs index 67daf1c459884d42378056b6528605da29578c95..882b5aab096ad2f8227aa7ad0efaddfd2078c3ad 100644 --- a/polkadot/runtime/parachains/src/configuration/benchmarking.rs +++ b/polkadot/runtime/parachains/src/configuration/benchmarking.rs @@ -51,6 +51,8 @@ benchmarks! { set_node_feature{}: set_node_feature(RawOrigin::Root, 255, true) + set_config_with_scheduler_params {} : set_scheduler_params(RawOrigin::Root, SchedulerParams::default()) + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext(Default::default()), diff --git a/polkadot/runtime/parachains/src/configuration/migration.rs b/polkadot/runtime/parachains/src/configuration/migration.rs index 2838b73092dbab4a029a684948a85123aa489906..87b30b177e735bdc1ec618a6e403c4c74a801533 100644 --- a/polkadot/runtime/parachains/src/configuration/migration.rs +++ b/polkadot/runtime/parachains/src/configuration/migration.rs @@ -18,6 +18,7 @@ pub mod v10; pub mod v11; +pub mod v12; pub mod v6; pub mod v7; pub mod v8; diff --git a/polkadot/runtime/parachains/src/configuration/migration/v11.rs b/polkadot/runtime/parachains/src/configuration/migration/v11.rs index f4db9196b1a089723cd061897f4a6638ce1c615f..f6e0e0431640ba271f592759308cc0d07efcfa71 100644 --- a/polkadot/runtime/parachains/src/configuration/migration/v11.rs +++ b/polkadot/runtime/parachains/src/configuration/migration/v11.rs @@ -21,13 +21,122 @@ use frame_support::{ migrations::VersionedMigration, pallet_prelude::*, traits::Defensive, weights::Weight, }; use frame_system::pallet_prelude::BlockNumberFor; -use primitives::{vstaging::ApprovalVotingParams, SessionIndex}; +use primitives::{ + vstaging::ApprovalVotingParams, AsyncBackingParams, ExecutorParams, SessionIndex, + LEGACY_MIN_BACKING_VOTES, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, +}; use sp_std::vec::Vec; use frame_support::traits::OnRuntimeUpgrade; +use polkadot_core_primitives::Balance; +use primitives::vstaging::NodeFeatures; +use sp_arithmetic::Perbill; use super::v10::V10HostConfiguration; -type V11HostConfiguration = configuration::HostConfiguration; + +#[derive(Clone, Encode, PartialEq, Decode, Debug)] +pub struct V11HostConfiguration { + pub max_code_size: u32, + pub max_head_data_size: u32, + pub max_upward_queue_count: u32, + pub max_upward_queue_size: u32, + pub max_upward_message_size: u32, + pub max_upward_message_num_per_candidate: u32, + pub hrmp_max_message_num_per_candidate: u32, + pub validation_upgrade_cooldown: BlockNumber, + pub validation_upgrade_delay: BlockNumber, + pub async_backing_params: AsyncBackingParams, + pub max_pov_size: u32, + pub max_downward_message_size: u32, + pub hrmp_max_parachain_outbound_channels: u32, + pub hrmp_sender_deposit: Balance, + pub hrmp_recipient_deposit: Balance, + pub hrmp_channel_max_capacity: u32, + pub hrmp_channel_max_total_size: u32, + pub hrmp_max_parachain_inbound_channels: u32, + pub hrmp_channel_max_message_size: u32, + pub executor_params: ExecutorParams, + pub code_retention_period: BlockNumber, + pub coretime_cores: u32, + pub on_demand_retries: u32, + pub on_demand_queue_max_size: u32, + pub on_demand_target_queue_utilization: Perbill, + pub on_demand_fee_variability: Perbill, + pub on_demand_base_fee: Balance, + pub on_demand_ttl: BlockNumber, + pub group_rotation_frequency: BlockNumber, + pub paras_availability_period: BlockNumber, + pub scheduling_lookahead: u32, + pub max_validators_per_core: Option, + pub max_validators: Option, + pub dispute_period: SessionIndex, + pub dispute_post_conclusion_acceptance_period: BlockNumber, + pub no_show_slots: u32, + pub n_delay_tranches: u32, + pub zeroth_delay_tranche_width: u32, + pub needed_approvals: u32, + pub relay_vrf_modulo_samples: u32, + pub pvf_voting_ttl: SessionIndex, + pub minimum_validation_upgrade_delay: BlockNumber, + pub minimum_backing_votes: u32, + pub node_features: NodeFeatures, + pub approval_voting_params: ApprovalVotingParams, +} + +impl> Default for V11HostConfiguration { + fn default() -> Self { + Self { + async_backing_params: AsyncBackingParams { + max_candidate_depth: 0, + allowed_ancestry_len: 0, + }, + group_rotation_frequency: 1u32.into(), + paras_availability_period: 1u32.into(), + no_show_slots: 1u32.into(), + validation_upgrade_cooldown: Default::default(), + validation_upgrade_delay: 2u32.into(), + code_retention_period: Default::default(), + max_code_size: Default::default(), + max_pov_size: Default::default(), + max_head_data_size: Default::default(), + coretime_cores: Default::default(), + on_demand_retries: Default::default(), + scheduling_lookahead: 1, + max_validators_per_core: Default::default(), + max_validators: None, + dispute_period: 6, + dispute_post_conclusion_acceptance_period: 100.into(), + n_delay_tranches: Default::default(), + zeroth_delay_tranche_width: Default::default(), + needed_approvals: Default::default(), + relay_vrf_modulo_samples: Default::default(), + max_upward_queue_count: Default::default(), + max_upward_queue_size: Default::default(), + max_downward_message_size: Default::default(), + max_upward_message_size: Default::default(), + max_upward_message_num_per_candidate: Default::default(), + hrmp_sender_deposit: Default::default(), + hrmp_recipient_deposit: Default::default(), + hrmp_channel_max_capacity: Default::default(), + hrmp_channel_max_total_size: Default::default(), + hrmp_max_parachain_inbound_channels: Default::default(), + hrmp_channel_max_message_size: Default::default(), + hrmp_max_parachain_outbound_channels: Default::default(), + hrmp_max_message_num_per_candidate: Default::default(), + pvf_voting_ttl: 2u32.into(), + minimum_validation_upgrade_delay: 2.into(), + executor_params: Default::default(), + approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 1 }, + on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + on_demand_base_fee: 10_000_000u128, + on_demand_fee_variability: Perbill::from_percent(3), + on_demand_target_queue_utilization: Perbill::from_percent(25), + on_demand_ttl: 5u32.into(), + minimum_backing_votes: LEGACY_MIN_BACKING_VOTES, + node_features: NodeFeatures::EMPTY, + } + } +} mod v10 { use super::*; @@ -253,7 +362,7 @@ mod tests { new_test_ext(Default::default()).execute_with(|| { // Implant the v10 version in the state. - v10::ActiveConfig::::set(Some(v10)); + v10::ActiveConfig::::set(Some(v10.clone())); v10::PendingConfigs::::set(Some(pending_configs)); migrate_to_v11::(); @@ -264,7 +373,7 @@ mod tests { let mut configs_to_check = v11::PendingConfigs::::get().unwrap(); configs_to_check.push((0, v11.clone())); - for (_, v10) in configs_to_check { + for (_, v11) in configs_to_check { #[rustfmt::skip] { assert_eq!(v10.max_code_size , v11.max_code_size); @@ -286,7 +395,7 @@ mod tests { assert_eq!(v10.hrmp_max_parachain_inbound_channels , v11.hrmp_max_parachain_inbound_channels); assert_eq!(v10.hrmp_channel_max_message_size , v11.hrmp_channel_max_message_size); assert_eq!(v10.code_retention_period , v11.code_retention_period); - assert_eq!(v10.coretime_cores , v11.coretime_cores); + assert_eq!(v10.on_demand_cores , v11.coretime_cores); assert_eq!(v10.on_demand_retries , v11.on_demand_retries); assert_eq!(v10.group_rotation_frequency , v11.group_rotation_frequency); assert_eq!(v10.paras_availability_period , v11.paras_availability_period); @@ -303,8 +412,8 @@ mod tests { assert_eq!(v10.minimum_validation_upgrade_delay , v11.minimum_validation_upgrade_delay); assert_eq!(v10.async_backing_params.allowed_ancestry_len, v11.async_backing_params.allowed_ancestry_len); assert_eq!(v10.async_backing_params.max_candidate_depth , v11.async_backing_params.max_candidate_depth); - assert_eq!(v10.executor_params , v11.executor_params); - assert_eq!(v10.minimum_backing_votes , v11.minimum_backing_votes); + assert_eq!(v10.executor_params , v11.executor_params); + assert_eq!(v10.minimum_backing_votes , v11.minimum_backing_votes); }; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression. } }); diff --git a/polkadot/runtime/parachains/src/configuration/migration/v12.rs b/polkadot/runtime/parachains/src/configuration/migration/v12.rs new file mode 100644 index 0000000000000000000000000000000000000000..4295a79893e8ee52632d54772bd108b4de37cc3f --- /dev/null +++ b/polkadot/runtime/parachains/src/configuration/migration/v12.rs @@ -0,0 +1,349 @@ +// 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 . + +//! A module that is responsible for migration of storage. + +use crate::configuration::{self, migration::v11::V11HostConfiguration, Config, Pallet}; +use frame_support::{ + migrations::VersionedMigration, + pallet_prelude::*, + traits::{Defensive, OnRuntimeUpgrade}, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use primitives::vstaging::SchedulerParams; +use sp_core::Get; +use sp_staking::SessionIndex; +use sp_std::vec::Vec; + +type V12HostConfiguration = configuration::HostConfiguration; + +mod v11 { + use super::*; + + #[frame_support::storage_alias] + pub(crate) type ActiveConfig = + StorageValue, V11HostConfiguration>, OptionQuery>; + + #[frame_support::storage_alias] + pub(crate) type PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V11HostConfiguration>)>, + OptionQuery, + >; +} + +mod v12 { + use super::*; + + #[frame_support::storage_alias] + pub(crate) type ActiveConfig = + StorageValue, V12HostConfiguration>, OptionQuery>; + + #[frame_support::storage_alias] + pub(crate) type PendingConfigs = StorageValue< + Pallet, + Vec<(SessionIndex, V12HostConfiguration>)>, + OptionQuery, + >; +} + +pub type MigrateToV12 = VersionedMigration< + 11, + 12, + UncheckedMigrateToV12, + Pallet, + ::DbWeight, +>; + +pub struct UncheckedMigrateToV12(sp_std::marker::PhantomData); + +impl OnRuntimeUpgrade for UncheckedMigrateToV12 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade() for HostConfiguration MigrateToV12"); + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + log::info!(target: configuration::LOG_TARGET, "HostConfiguration MigrateToV12 started"); + let weight_consumed = migrate_to_v12::(); + + log::info!(target: configuration::LOG_TARGET, "HostConfiguration MigrateToV12 executed successfully"); + + weight_consumed + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + log::trace!(target: crate::configuration::LOG_TARGET, "Running post_upgrade() for HostConfiguration MigrateToV12"); + ensure!( + StorageVersion::get::>() >= 12, + "Storage version should be >= 12 after the migration" + ); + + Ok(()) + } +} + +fn migrate_to_v12() -> Weight { + // Unusual formatting is justified: + // - make it easier to verify that fields assign what they supposed to assign. + // - this code is transient and will be removed after all migrations are done. + // - this code is important enough to optimize for legibility sacrificing consistency. + #[rustfmt::skip] + let translate = + |pre: V11HostConfiguration>| -> + V12HostConfiguration> + { + V12HostConfiguration { + max_code_size : pre.max_code_size, + max_head_data_size : pre.max_head_data_size, + max_upward_queue_count : pre.max_upward_queue_count, + max_upward_queue_size : pre.max_upward_queue_size, + max_upward_message_size : pre.max_upward_message_size, + max_upward_message_num_per_candidate : pre.max_upward_message_num_per_candidate, + hrmp_max_message_num_per_candidate : pre.hrmp_max_message_num_per_candidate, + validation_upgrade_cooldown : pre.validation_upgrade_cooldown, + validation_upgrade_delay : pre.validation_upgrade_delay, + max_pov_size : pre.max_pov_size, + max_downward_message_size : pre.max_downward_message_size, + hrmp_sender_deposit : pre.hrmp_sender_deposit, + hrmp_recipient_deposit : pre.hrmp_recipient_deposit, + hrmp_channel_max_capacity : pre.hrmp_channel_max_capacity, + hrmp_channel_max_total_size : pre.hrmp_channel_max_total_size, + hrmp_max_parachain_inbound_channels : pre.hrmp_max_parachain_inbound_channels, + hrmp_max_parachain_outbound_channels : pre.hrmp_max_parachain_outbound_channels, + hrmp_channel_max_message_size : pre.hrmp_channel_max_message_size, + code_retention_period : pre.code_retention_period, + max_validators : pre.max_validators, + dispute_period : pre.dispute_period, + dispute_post_conclusion_acceptance_period: pre.dispute_post_conclusion_acceptance_period, + no_show_slots : pre.no_show_slots, + n_delay_tranches : pre.n_delay_tranches, + zeroth_delay_tranche_width : pre.zeroth_delay_tranche_width, + needed_approvals : pre.needed_approvals, + relay_vrf_modulo_samples : pre.relay_vrf_modulo_samples, + pvf_voting_ttl : pre.pvf_voting_ttl, + minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay, + async_backing_params : pre.async_backing_params, + executor_params : pre.executor_params, + minimum_backing_votes : pre.minimum_backing_votes, + node_features : pre.node_features, + approval_voting_params : pre.approval_voting_params, + scheduler_params: SchedulerParams { + group_rotation_frequency : pre.group_rotation_frequency, + paras_availability_period : pre.paras_availability_period, + max_validators_per_core : pre.max_validators_per_core, + lookahead : pre.scheduling_lookahead, + num_cores : pre.coretime_cores, + max_availability_timeouts : pre.on_demand_retries, + on_demand_queue_max_size : pre.on_demand_queue_max_size, + on_demand_target_queue_utilization : pre.on_demand_target_queue_utilization, + on_demand_fee_variability : pre.on_demand_fee_variability, + on_demand_base_fee : pre.on_demand_base_fee, + ttl : pre.on_demand_ttl, + } + } + }; + + let v11 = v11::ActiveConfig::::get() + .defensive_proof("Could not decode old config") + .unwrap_or_default(); + let v12 = translate(v11); + v12::ActiveConfig::::set(Some(v12)); + + // Allowed to be empty. + let pending_v11 = v11::PendingConfigs::::get().unwrap_or_default(); + let mut pending_v12 = Vec::new(); + + for (session, v11) in pending_v11.into_iter() { + let v12 = translate(v11); + pending_v12.push((session, v12)); + } + v12::PendingConfigs::::set(Some(pending_v12.clone())); + + let num_configs = (pending_v12.len() + 1) as u64; + T::DbWeight::get().reads_writes(num_configs, num_configs) +} + +#[cfg(test)] +mod tests { + use primitives::LEGACY_MIN_BACKING_VOTES; + use sp_arithmetic::Perbill; + + use super::*; + use crate::mock::{new_test_ext, Test}; + + #[test] + fn v12_deserialized_from_actual_data() { + // Example how to get new `raw_config`: + // We'll obtain the raw_config at a specified a block + // Steps: + // 1. Go to Polkadot.js -> Developer -> Chain state -> Storage: https://polkadot.js.org/apps/#/chainstate + // 2. Set these parameters: + // 2.1. selected state query: configuration; activeConfig(): + // PolkadotRuntimeParachainsConfigurationHostConfiguration + // 2.2. blockhash to query at: + // 0xf89d3ab5312c5f70d396dc59612f0aa65806c798346f9db4b35278baed2e0e53 (the hash of + // the block) + // 2.3. Note the value of encoded storage key -> + // 0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385 for the + // referenced block. + // 2.4. You'll also need the decoded values to update the test. + // 3. Go to Polkadot.js -> Developer -> Chain state -> Raw storage + // 3.1 Enter the encoded storage key and you get the raw config. + + // This exceeds the maximal line width length, but that's fine, since this is not code and + // doesn't need to be read and also leaving it as one line allows to easily copy it. + let raw_config = + hex_literal::hex![ + "0000300000800000080000000000100000c8000005000000050000000200000002000000000000000000000000005000000010000400000000000000000000000000000000000000000000000000000000000000000000000800000000200000040000000000100000b004000000060000006400000002000000190000000000000002000000020000000200000005000000020000000001000000140000000400000001010000000100000001000000000000001027000080b2e60e80c3c9018096980000000000000000000000000005000000" + ]; + + let v12 = + V12HostConfiguration::::decode(&mut &raw_config[..]).unwrap(); + + // We check only a sample of the values here. If we missed any fields or messed up data + // types that would skew all the fields coming after. + assert_eq!(v12.max_code_size, 3_145_728); + assert_eq!(v12.validation_upgrade_cooldown, 2); + assert_eq!(v12.max_pov_size, 5_242_880); + assert_eq!(v12.hrmp_channel_max_message_size, 1_048_576); + assert_eq!(v12.n_delay_tranches, 25); + assert_eq!(v12.minimum_validation_upgrade_delay, 5); + assert_eq!(v12.minimum_backing_votes, LEGACY_MIN_BACKING_VOTES); + assert_eq!(v12.approval_voting_params.max_approval_coalesce_count, 1); + assert_eq!(v12.scheduler_params.group_rotation_frequency, 20); + assert_eq!(v12.scheduler_params.paras_availability_period, 4); + assert_eq!(v12.scheduler_params.lookahead, 1); + assert_eq!(v12.scheduler_params.num_cores, 1); + assert_eq!(v12.scheduler_params.max_availability_timeouts, 0); + assert_eq!(v12.scheduler_params.on_demand_queue_max_size, 10_000); + assert_eq!( + v12.scheduler_params.on_demand_target_queue_utilization, + Perbill::from_percent(25) + ); + assert_eq!(v12.scheduler_params.on_demand_fee_variability, Perbill::from_percent(3)); + assert_eq!(v12.scheduler_params.on_demand_base_fee, 10_000_000); + assert_eq!(v12.scheduler_params.ttl, 5); + } + + #[test] + fn test_migrate_to_v12() { + // Host configuration has lots of fields. However, in this migration we only add one + // field. The most important part to check are a couple of the last fields. We also pick + // extra fields to check arbitrarily, e.g. depending on their position (i.e. the middle) and + // also their type. + // + // We specify only the picked fields and the rest should be provided by the `Default` + // implementation. That implementation is copied over between the two types and should work + // fine. + let v11 = V11HostConfiguration:: { + needed_approvals: 69, + paras_availability_period: 55, + hrmp_recipient_deposit: 1337, + max_pov_size: 1111, + minimum_validation_upgrade_delay: 20, + on_demand_ttl: 3, + on_demand_retries: 10, + ..Default::default() + }; + + let mut pending_configs = Vec::new(); + pending_configs.push((100, v11.clone())); + pending_configs.push((300, v11.clone())); + + new_test_ext(Default::default()).execute_with(|| { + // Implant the v10 version in the state. + v11::ActiveConfig::::set(Some(v11.clone())); + v11::PendingConfigs::::set(Some(pending_configs)); + + migrate_to_v12::(); + + let v12 = v12::ActiveConfig::::get().unwrap(); + assert_eq!(v12.approval_voting_params.max_approval_coalesce_count, 1); + + let mut configs_to_check = v12::PendingConfigs::::get().unwrap(); + configs_to_check.push((0, v12.clone())); + + for (_, v12) in configs_to_check { + #[rustfmt::skip] + { + assert_eq!(v11.max_code_size , v12.max_code_size); + assert_eq!(v11.max_head_data_size , v12.max_head_data_size); + assert_eq!(v11.max_upward_queue_count , v12.max_upward_queue_count); + assert_eq!(v11.max_upward_queue_size , v12.max_upward_queue_size); + assert_eq!(v11.max_upward_message_size , v12.max_upward_message_size); + assert_eq!(v11.max_upward_message_num_per_candidate , v12.max_upward_message_num_per_candidate); + assert_eq!(v11.hrmp_max_message_num_per_candidate , v12.hrmp_max_message_num_per_candidate); + assert_eq!(v11.validation_upgrade_cooldown , v12.validation_upgrade_cooldown); + assert_eq!(v11.validation_upgrade_delay , v12.validation_upgrade_delay); + assert_eq!(v11.max_pov_size , v12.max_pov_size); + assert_eq!(v11.max_downward_message_size , v12.max_downward_message_size); + assert_eq!(v11.hrmp_max_parachain_outbound_channels , v12.hrmp_max_parachain_outbound_channels); + assert_eq!(v11.hrmp_sender_deposit , v12.hrmp_sender_deposit); + assert_eq!(v11.hrmp_recipient_deposit , v12.hrmp_recipient_deposit); + assert_eq!(v11.hrmp_channel_max_capacity , v12.hrmp_channel_max_capacity); + assert_eq!(v11.hrmp_channel_max_total_size , v12.hrmp_channel_max_total_size); + assert_eq!(v11.hrmp_max_parachain_inbound_channels , v12.hrmp_max_parachain_inbound_channels); + assert_eq!(v11.hrmp_channel_max_message_size , v12.hrmp_channel_max_message_size); + assert_eq!(v11.code_retention_period , v12.code_retention_period); + assert_eq!(v11.max_validators , v12.max_validators); + assert_eq!(v11.dispute_period , v12.dispute_period); + assert_eq!(v11.no_show_slots , v12.no_show_slots); + assert_eq!(v11.n_delay_tranches , v12.n_delay_tranches); + assert_eq!(v11.zeroth_delay_tranche_width , v12.zeroth_delay_tranche_width); + assert_eq!(v11.needed_approvals , v12.needed_approvals); + assert_eq!(v11.relay_vrf_modulo_samples , v12.relay_vrf_modulo_samples); + assert_eq!(v11.pvf_voting_ttl , v12.pvf_voting_ttl); + assert_eq!(v11.minimum_validation_upgrade_delay , v12.minimum_validation_upgrade_delay); + assert_eq!(v11.async_backing_params.allowed_ancestry_len, v12.async_backing_params.allowed_ancestry_len); + assert_eq!(v11.async_backing_params.max_candidate_depth , v12.async_backing_params.max_candidate_depth); + assert_eq!(v11.executor_params , v12.executor_params); + assert_eq!(v11.minimum_backing_votes , v12.minimum_backing_votes); + assert_eq!(v11.group_rotation_frequency , v12.scheduler_params.group_rotation_frequency); + assert_eq!(v11.paras_availability_period , v12.scheduler_params.paras_availability_period); + assert_eq!(v11.max_validators_per_core , v12.scheduler_params.max_validators_per_core); + assert_eq!(v11.scheduling_lookahead , v12.scheduler_params.lookahead); + assert_eq!(v11.coretime_cores , v12.scheduler_params.num_cores); + assert_eq!(v11.on_demand_retries , v12.scheduler_params.max_availability_timeouts); + assert_eq!(v11.on_demand_queue_max_size , v12.scheduler_params.on_demand_queue_max_size); + assert_eq!(v11.on_demand_target_queue_utilization , v12.scheduler_params.on_demand_target_queue_utilization); + assert_eq!(v11.on_demand_fee_variability , v12.scheduler_params.on_demand_fee_variability); + assert_eq!(v11.on_demand_base_fee , v12.scheduler_params.on_demand_base_fee); + assert_eq!(v11.on_demand_ttl , v12.scheduler_params.ttl); + }; // ; makes this a statement. `rustfmt::skip` cannot be put on an expression. + } + }); + } + + // Test that migration doesn't panic in case there are no pending configurations upgrades in + // pallet's storage. + #[test] + fn test_migrate_to_v12_no_pending() { + let v11 = V11HostConfiguration::::default(); + + new_test_ext(Default::default()).execute_with(|| { + // Implant the v10 version in the state. + v11::ActiveConfig::::set(Some(v11)); + // Ensure there are no pending configs. + v12::PendingConfigs::::set(None); + + // Shouldn't fail. + migrate_to_v12::(); + }); + } +} diff --git a/polkadot/runtime/parachains/src/configuration/tests.rs b/polkadot/runtime/parachains/src/configuration/tests.rs index c915eb12a0ca1712a1d923d126daac959a40cd09..254511231cac1729981706775bb9f844ca9f1c5c 100644 --- a/polkadot/runtime/parachains/src/configuration/tests.rs +++ b/polkadot/runtime/parachains/src/configuration/tests.rs @@ -38,7 +38,7 @@ fn default_is_consistent() { fn scheduled_session_is_two_sessions_from_now() { new_test_ext(Default::default()).execute_with(|| { // The logic here is really tested only with scheduled_session = 2. It should work - // with other values, but that should receive a more rigorious testing. + // with other values, but that should receive a more rigorous testing. on_new_session(1); assert_eq!(Configuration::scheduled_session(), 3); }); @@ -136,7 +136,7 @@ fn pending_next_session_but_we_upgrade_once_more() { // update. assert_ok!(Configuration::set_validation_upgrade_cooldown(RuntimeOrigin::root(), 99)); - // This should result in yet another configiguration change scheduled. + // This should result in yet another configuration change scheduled. assert_eq!(Configuration::config(), initial_config); assert_eq!( PendingConfigs::::get(), @@ -179,7 +179,7 @@ fn scheduled_session_config_update_while_next_session_pending() { assert_ok!(Configuration::set_validation_upgrade_cooldown(RuntimeOrigin::root(), 99)); assert_ok!(Configuration::set_code_retention_period(RuntimeOrigin::root(), 98)); - // This should result in yet another configiguration change scheduled. + // This should result in yet another configuration change scheduled. assert_eq!(Configuration::config(), initial_config); assert_eq!( PendingConfigs::::get(), @@ -226,8 +226,11 @@ fn invariants() { ); ActiveConfig::::put(HostConfiguration { - paras_availability_period: 10, minimum_validation_upgrade_delay: 11, + scheduler_params: SchedulerParams { + paras_availability_period: 10, + ..Default::default() + }, ..Default::default() }); assert_err!( @@ -283,12 +286,6 @@ fn setting_pending_config_members() { max_code_size: 100_000, max_pov_size: 1024, max_head_data_size: 1_000, - coretime_cores: 2, - on_demand_retries: 5, - group_rotation_frequency: 20, - paras_availability_period: 10, - scheduling_lookahead: 3, - max_validators_per_core: None, max_validators: None, dispute_period: 239, dispute_post_conclusion_acceptance_period: 10, @@ -314,13 +311,21 @@ fn setting_pending_config_members() { minimum_validation_upgrade_delay: 20, executor_params: Default::default(), approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 1 }, - on_demand_queue_max_size: 10_000u32, - on_demand_base_fee: 10_000_000u128, - on_demand_fee_variability: Perbill::from_percent(3), - on_demand_target_queue_utilization: Perbill::from_percent(25), - on_demand_ttl: 5u32, minimum_backing_votes: 5, node_features: bitvec![u8, Lsb0; 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + scheduler_params: SchedulerParams { + group_rotation_frequency: 20, + paras_availability_period: 10, + max_validators_per_core: None, + lookahead: 3, + num_cores: 2, + max_availability_timeouts: 5, + on_demand_queue_max_size: 10_000u32, + on_demand_base_fee: 10_000_000u128, + on_demand_fee_variability: Perbill::from_percent(3), + on_demand_target_queue_utilization: Perbill::from_percent(25), + ttl: 5u32, + }, }; Configuration::set_validation_upgrade_cooldown( @@ -342,13 +347,19 @@ fn setting_pending_config_members() { Configuration::set_max_pov_size(RuntimeOrigin::root(), new_config.max_pov_size).unwrap(); Configuration::set_max_head_data_size(RuntimeOrigin::root(), new_config.max_head_data_size) .unwrap(); - Configuration::set_coretime_cores(RuntimeOrigin::root(), new_config.coretime_cores) - .unwrap(); - Configuration::set_on_demand_retries(RuntimeOrigin::root(), new_config.on_demand_retries) - .unwrap(); + Configuration::set_coretime_cores( + RuntimeOrigin::root(), + new_config.scheduler_params.num_cores, + ) + .unwrap(); + Configuration::set_max_availability_timeouts( + RuntimeOrigin::root(), + new_config.scheduler_params.max_availability_timeouts, + ) + .unwrap(); Configuration::set_group_rotation_frequency( RuntimeOrigin::root(), - new_config.group_rotation_frequency, + new_config.scheduler_params.group_rotation_frequency, ) .unwrap(); // This comes out of order to satisfy the validity criteria for the chain and thread @@ -360,17 +371,17 @@ fn setting_pending_config_members() { .unwrap(); Configuration::set_paras_availability_period( RuntimeOrigin::root(), - new_config.paras_availability_period, + new_config.scheduler_params.paras_availability_period, ) .unwrap(); Configuration::set_scheduling_lookahead( RuntimeOrigin::root(), - new_config.scheduling_lookahead, + new_config.scheduler_params.lookahead, ) .unwrap(); Configuration::set_max_validators_per_core( RuntimeOrigin::root(), - new_config.max_validators_per_core, + new_config.scheduler_params.max_validators_per_core, ) .unwrap(); Configuration::set_max_validators(RuntimeOrigin::root(), new_config.max_validators) diff --git a/polkadot/runtime/parachains/src/coretime/migration.rs b/polkadot/runtime/parachains/src/coretime/migration.rs index e64d3fbd6a9ee53df561cf61199a66b537b0db64..03fecc58570fba65df79ca48930acb3346d3f556 100644 --- a/polkadot/runtime/parachains/src/coretime/migration.rs +++ b/polkadot/runtime/parachains/src/coretime/migration.rs @@ -74,7 +74,7 @@ mod v_coretime { loop { match sp_io::storage::next_key(&next_key) { - // StorageVersion is initialized before, so we need to ingore it. + // StorageVersion is initialized before, so we need to ignore it. Some(key) if &key == &storage_version_key => { next_key = key; }, @@ -114,7 +114,7 @@ mod v_coretime { let legacy_paras = paras::Parachains::::get(); let config = >::config(); - let total_core_count = config.coretime_cores + legacy_paras.len() as u32; + let total_core_count = config.scheduler_params.num_cores + legacy_paras.len() as u32; let dmp_queue_size = crate::dmp::Pallet::::dmq_contents(T::BrokerId::get().into()).len() as u32; @@ -150,7 +150,7 @@ mod v_coretime { // Migrate to Coretime. // - // NOTE: Also migrates coretime_cores config value in configuration::ActiveConfig. + // NOTE: Also migrates `num_cores` config value in configuration::ActiveConfig. fn migrate_to_coretime< T: Config, SendXcm: xcm::v4::SendXcm, @@ -176,8 +176,8 @@ mod v_coretime { } let config = >::config(); - // coretime_cores was on_demand_cores until now: - for on_demand in 0..config.coretime_cores { + // 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( core, @@ -189,9 +189,9 @@ mod v_coretime { log::error!("Creating assignment for existing on-demand core, failed: {:?}", err); } } - let total_cores = config.coretime_cores + legacy_count; + let total_cores = config.scheduler_params.num_cores + legacy_count; configuration::ActiveConfig::::mutate(|c| { - c.coretime_cores = total_cores; + c.scheduler_params.num_cores = total_cores; }); if let Err(err) = migrate_send_assignments_to_coretime_chain::() { @@ -200,7 +200,9 @@ mod v_coretime { let single_weight = ::WeightInfo::assign_core(1); single_weight - .saturating_mul(u64::from(legacy_count.saturating_add(config.coretime_cores))) + .saturating_mul(u64::from( + legacy_count.saturating_add(config.scheduler_params.num_cores), + )) // Second read from sending assignments to the coretime chain. .saturating_add(T::DbWeight::get().reads_writes(2, 1)) } @@ -244,7 +246,8 @@ mod v_coretime { Some(mk_coretime_call(crate::coretime::CoretimeCalls::SetLease(p.into(), time_slice))) }); - let core_count: u16 = configuration::Pallet::::config().coretime_cores.saturated_into(); + let core_count: u16 = + configuration::Pallet::::config().scheduler_params.num_cores.saturated_into(); let set_core_count = iter::once(mk_coretime_call( crate::coretime::CoretimeCalls::NotifyCoreCount(core_count), )); diff --git a/polkadot/runtime/parachains/src/coretime/mod.rs b/polkadot/runtime/parachains/src/coretime/mod.rs index 531f5c2e4e470095989bbb73429cc2180ff4f321..eb9646d7e869d35763a5b0cdf8a408de66126b78 100644 --- a/polkadot/runtime/parachains/src/coretime/mod.rs +++ b/polkadot/runtime/parachains/src/coretime/mod.rs @@ -214,8 +214,8 @@ impl Pallet { } pub fn initializer_on_new_session(notification: &SessionChangeNotification>) { - let old_core_count = notification.prev_config.coretime_cores; - let new_core_count = notification.new_config.coretime_cores; + let old_core_count = notification.prev_config.scheduler_params.num_cores; + let new_core_count = notification.new_config.scheduler_params.num_cores; if new_core_count != old_core_count { let core_count: u16 = new_core_count.saturated_into(); let message = Xcm(vec![ diff --git a/polkadot/runtime/parachains/src/disputes.rs b/polkadot/runtime/parachains/src/disputes.rs index c2383dad3053882b7abec959446b23d149aa4a5f..da95b060c14a3fad03e98f5e2a75ac54eef1928c 100644 --- a/polkadot/runtime/parachains/src/disputes.rs +++ b/polkadot/runtime/parachains/src/disputes.rs @@ -379,7 +379,7 @@ pub mod pallet { type WeightInfo: WeightInfo; } - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs index 2cb49c88d437cb3e0a4c01a40d343a179316ec9d..c6baf2f30cf58279b58b644df76e5ea255f7ec4f 100644 --- a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs +++ b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs @@ -22,7 +22,7 @@ use crate::{ paras::{Pallet as Paras, ParaKind, ParachainsCache}, shared::Pallet as Shared, }; -use frame_benchmarking::{impl_benchmark_test_suite, v2::*, whitelisted_caller}; +use frame_benchmarking::{v2::*, whitelisted_caller}; use frame_support::{assert_ok, traits::Currency}; type BalanceOf = diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 90af9cde00a8faaec13101b490ea3ba92f4a827c..16e2e93b5617f2c85bbb308ff5d057c7ed99db9c 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -47,10 +47,7 @@ use scale_info::TypeInfo; use sp_runtime::{traits::One, DispatchError, SaturatedConversion, Saturating}; #[cfg(feature = "std")] use sp_std::fmt; -use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - prelude::*, -}; +use sp_std::{collections::btree_set::BTreeSet, prelude::*}; pub use pallet::*; @@ -601,18 +598,16 @@ impl Pallet { /// scheduled cores. If these conditions are not met, the execution of the function fails. pub(crate) fn process_candidates( allowed_relay_parents: &AllowedRelayParentsTracker>, - candidates: Vec>, - scheduled: &BTreeMap, + candidates: Vec<(BackedCandidate, CoreIndex)>, group_validators: GV, + core_index_enabled: bool, ) -> Result, DispatchError> where GV: Fn(GroupIndex) -> Option>, { let now = >::block_number(); - ensure!(candidates.len() <= scheduled.len(), Error::::UnscheduledCandidate); - - if scheduled.is_empty() { + if candidates.is_empty() { return Ok(ProcessedCandidates::default()) } @@ -648,7 +643,7 @@ impl Pallet { // // In the meantime, we do certain sanity checks on the candidates and on the scheduled // list. - for (candidate_idx, backed_candidate) in candidates.iter().enumerate() { + for (candidate_idx, (backed_candidate, core_index)) in candidates.iter().enumerate() { let relay_parent_hash = backed_candidate.descriptor().relay_parent; let para_id = backed_candidate.descriptor().para_id; @@ -663,7 +658,7 @@ impl Pallet { let relay_parent_number = match check_ctx.verify_backed_candidate( &allowed_relay_parents, candidate_idx, - backed_candidate, + backed_candidate.candidate(), )? { Err(FailedToCreatePVD) => { log::debug!( @@ -679,11 +674,22 @@ impl Pallet { Ok(rpn) => rpn, }; - let para_id = backed_candidate.descriptor().para_id; + let (validator_indices, _) = + backed_candidate.validator_indices_and_core_index(core_index_enabled); + + log::debug!( + target: LOG_TARGET, + "Candidate {:?} on {:?}, + core_index_enabled = {}", + backed_candidate.hash(), + core_index, + core_index_enabled + ); + + check_assignment_in_order(core_index)?; + let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; - let core_idx = *scheduled.get(¶_id).ok_or(Error::::UnscheduledCandidate)?; - check_assignment_in_order(core_idx)?; ensure!( >::get(¶_id).is_none() && >::get(¶_id).is_none(), @@ -694,7 +700,7 @@ impl Pallet { // assigned to core at block `N + 1`. Thus, `relay_parent_number + 1` // will always land in the current session. let group_idx = >::group_assigned_to_core( - core_idx, + *core_index, relay_parent_number + One::one(), ) .ok_or_else(|| { @@ -711,7 +717,9 @@ impl Pallet { // check the signatures in the backing and that it is a majority. { let maybe_amount_validated = primitives::check_candidate_backing( - &backed_candidate, + backed_candidate.candidate().hash(), + backed_candidate.validity_votes(), + validator_indices, &signing_context, group_vals.len(), |intra_group_vi| { @@ -738,16 +746,15 @@ impl Pallet { let mut backer_idx_and_attestation = Vec::<(ValidatorIndex, ValidityAttestation)>::with_capacity( - backed_candidate.validator_indices.count_ones(), + validator_indices.count_ones(), ); let candidate_receipt = backed_candidate.receipt(); - for ((bit_idx, _), attestation) in backed_candidate - .validator_indices + for ((bit_idx, _), attestation) in validator_indices .iter() .enumerate() .filter(|(_, signed)| **signed) - .zip(backed_candidate.validity_votes.iter().cloned()) + .zip(backed_candidate.validity_votes().iter().cloned()) { let val_idx = group_vals.get(bit_idx).expect("this query succeeded above; qed"); @@ -760,7 +767,7 @@ impl Pallet { } core_indices_and_backers.push(( - (core_idx, para_id), + (*core_index, para_id), backers, group_idx, relay_parent_number, @@ -772,7 +779,7 @@ impl Pallet { // one more sweep for actually writing to storage. let core_indices = core_indices_and_backers.iter().map(|(c, ..)| *c).collect(); - for (candidate, (core, backers, group, relay_parent_number)) in + for ((candidate, _), (core, backers, group, relay_parent_number)) in candidates.into_iter().zip(core_indices_and_backers) { let para_id = candidate.descriptor().para_id; @@ -782,16 +789,18 @@ impl Pallet { bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; Self::deposit_event(Event::::CandidateBacked( - candidate.candidate.to_plain(), - candidate.candidate.commitments.head_data.clone(), + candidate.candidate().to_plain(), + candidate.candidate().commitments.head_data.clone(), core.0, group, )); - let candidate_hash = candidate.candidate.hash(); + let candidate_hash = candidate.candidate().hash(); - let (descriptor, commitments) = - (candidate.candidate.descriptor, candidate.candidate.commitments); + let (descriptor, commitments) = ( + candidate.candidate().descriptor.clone(), + candidate.candidate().commitments.clone(), + ); >::insert( ¶_id, @@ -1195,10 +1204,10 @@ impl CandidateCheckContext { &self, allowed_relay_parents: &AllowedRelayParentsTracker>, candidate_idx: usize, - backed_candidate: &BackedCandidate<::Hash>, + backed_candidate_receipt: &CommittedCandidateReceipt<::Hash>, ) -> Result, FailedToCreatePVD>, Error> { - let para_id = backed_candidate.descriptor().para_id; - let relay_parent = backed_candidate.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) = { @@ -1223,13 +1232,13 @@ impl CandidateCheckContext { let expected = persisted_validation_data.hash(); ensure!( - expected == backed_candidate.descriptor().persisted_validation_data_hash, + expected == backed_candidate_receipt.descriptor().persisted_validation_data_hash, Error::::ValidationDataHashMismatch, ); } ensure!( - backed_candidate.descriptor().check_collator_signature().is_ok(), + backed_candidate_receipt.descriptor().check_collator_signature().is_ok(), Error::::NotCollatorSigned, ); @@ -1237,25 +1246,25 @@ impl CandidateCheckContext { // A candidate for a parachain without current validation code is not scheduled. .ok_or_else(|| Error::::UnscheduledCandidate)?; ensure!( - backed_candidate.descriptor().validation_code_hash == validation_code_hash, + backed_candidate_receipt.descriptor().validation_code_hash == validation_code_hash, Error::::InvalidValidationCodeHash, ); ensure!( - backed_candidate.descriptor().para_head == - backed_candidate.candidate.commitments.head_data.hash(), + backed_candidate_receipt.descriptor().para_head == + backed_candidate_receipt.commitments.head_data.hash(), Error::::ParaHeadMismatch, ); if let Err(err) = self.check_validation_outputs( para_id, relay_parent_number, - &backed_candidate.candidate.commitments.head_data, - &backed_candidate.candidate.commitments.new_validation_code, - backed_candidate.candidate.commitments.processed_downward_messages, - &backed_candidate.candidate.commitments.upward_messages, - BlockNumberFor::::from(backed_candidate.candidate.commitments.hrmp_watermark), - &backed_candidate.candidate.commitments.horizontal_messages, + &backed_candidate_receipt.commitments.head_data, + &backed_candidate_receipt.commitments.new_validation_code, + backed_candidate_receipt.commitments.processed_downward_messages, + &backed_candidate_receipt.commitments.upward_messages, + BlockNumberFor::::from(backed_candidate_receipt.commitments.hrmp_watermark), + &backed_candidate_receipt.commitments.horizontal_messages, ) { log::debug!( target: LOG_TARGET, diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 232e65d78ed2aef86aa7903d88cbe99236ba9ec1..3fe7d7f0c7d4698c6b8301a41dca552e9c067509 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -47,10 +47,10 @@ use test_helpers::{dummy_collator, dummy_collator_signature, dummy_validation_co fn default_config() -> HostConfiguration { let mut config = HostConfiguration::default(); - config.coretime_cores = 1; + config.scheduler_params.num_cores = 1; config.max_code_size = 0b100000; config.max_head_data_size = 0b100000; - config.group_rotation_frequency = u32::MAX; + config.scheduler_params.group_rotation_frequency = u32::MAX; config } @@ -120,6 +120,7 @@ pub(crate) fn back_candidate( keystore: &KeystorePtr, signing_context: &SigningContext, kind: BackingKind, + core_index: Option, ) -> BackedCandidate { let mut validator_indices = bitvec::bitvec![u8, BitOrderLsb0; 0; group.len()]; let threshold = effective_minimum_backing_votes( @@ -155,15 +156,20 @@ pub(crate) fn back_candidate( validity_votes.push(ValidityAttestation::Explicit(signature).into()); } - let backed = BackedCandidate { candidate, validity_votes, validator_indices }; + let backed = + BackedCandidate::new(candidate, validity_votes, validator_indices.clone(), core_index); - let successfully_backed = - primitives::check_candidate_backing(&backed, signing_context, group.len(), |i| { - Some(validators[group[i].0 as usize].public().into()) - }) - .ok() - .unwrap_or(0) >= - threshold; + let successfully_backed = primitives::check_candidate_backing( + backed.candidate().hash(), + backed.validity_votes(), + validator_indices.as_bitslice(), + signing_context, + group.len(), + |i| Some(validators[group[i].0 as usize].public().into()), + ) + .ok() + .unwrap_or(0) >= + threshold; match kind { BackingKind::Unanimous | BackingKind::Threshold => assert!(successfully_backed), @@ -218,7 +224,7 @@ pub(crate) fn run_to_block( } pub(crate) fn expected_bits() -> usize { - Paras::parachains().len() + Configuration::config().coretime_cores as usize + Paras::parachains().len() + Configuration::config().scheduler_params.num_cores as usize } fn default_bitfield() -> AvailabilityBitfield { @@ -380,7 +386,7 @@ fn collect_pending_cleans_up_pending() { (thread_a, ParaKind::Parathread), ]; let mut config = genesis_config(paras); - config.configuration.config.group_rotation_frequency = 3; + config.configuration.config.scheduler_params.group_rotation_frequency = 3; new_test_ext(config).execute_with(|| { let default_candidate = TestCandidateBuilder::default().build(); >::insert( @@ -919,38 +925,16 @@ fn candidate_checks() { let thread_a_assignment = (thread_a, CoreIndex::from(2)); let allowed_relay_parents = default_allowed_relay_parent_tracker(); - // unscheduled candidate. - { - let mut candidate = TestCandidateBuilder { - para_id: chain_a, - relay_parent: System::parent_hash(), - pov_hash: Hash::repeat_byte(1), - persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), - hrmp_watermark: RELAY_PARENT_NUM, - ..Default::default() - } - .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - - let backed = back_candidate( - candidate, - &validators, - group_validators(GroupIndex::from(0)).unwrap().as_ref(), - &keystore, - &signing_context, - BackingKind::Threshold, - ); - - assert_noop!( - ParaInclusion::process_candidates( - &allowed_relay_parents, - vec![backed], - &[chain_b_assignment].into_iter().collect(), - &group_validators, - ), - Error::::UnscheduledCandidate - ); - } + // no candidates. + assert_eq!( + ParaInclusion::process_candidates( + &allowed_relay_parents, + vec![], + &group_validators, + false + ), + Ok(ProcessedCandidates::default()) + ); // candidates out of order. { @@ -984,6 +968,7 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let backed_b = back_candidate( @@ -993,15 +978,16 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); // out-of-order manifests as unscheduled. assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_b, backed_a], - &[chain_a_assignment, chain_b_assignment].into_iter().collect(), + vec![(backed_b, chain_b_assignment.1), (backed_a, chain_a_assignment.1)], &group_validators, + false ), Error::::ScheduledOutOfOrder ); @@ -1027,14 +1013,15 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Lacking, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + vec![(backed, chain_a_assignment.1)], &group_validators, + false ), Error::::InsufficientBacking ); @@ -1075,6 +1062,7 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let backed_b = back_candidate( @@ -1084,14 +1072,15 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_b, backed_a], - &[chain_a_assignment, chain_b_assignment].into_iter().collect(), + vec![(backed_b, chain_b_assignment.1), (backed_a, chain_a_assignment.1)], &group_validators, + false ), Error::::DisallowedRelayParent ); @@ -1122,14 +1111,15 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[thread_a_assignment].into_iter().collect(), + vec![(backed, thread_a_assignment.1)], &group_validators, + false ), Error::::NotCollatorSigned ); @@ -1156,6 +1146,7 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let candidate = TestCandidateBuilder::default().build(); @@ -1177,9 +1168,9 @@ fn candidate_checks() { assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + vec![(backed, chain_a_assignment.1)], &group_validators, + false ), Error::::CandidateScheduledBeforeParaFree ); @@ -1212,14 +1203,15 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + vec![(backed, chain_a_assignment.1)], &group_validators, + false ), Error::::CandidateScheduledBeforeParaFree ); @@ -1233,7 +1225,7 @@ fn candidate_checks() { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), - new_validation_code: Some(vec![5, 6, 7, 8].into()), + new_validation_code: Some(dummy_validation_code()), persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), hrmp_watermark: RELAY_PARENT_NUM, ..Default::default() @@ -1249,6 +1241,7 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); { @@ -1257,7 +1250,7 @@ fn candidate_checks() { assert_eq!(expected_at, 12); Paras::schedule_code_upgrade( chain_a, - vec![1, 2, 3, 4].into(), + vec![9, 8, 7, 6, 5, 4, 3, 2, 1].into(), expected_at, &cfg, SetGoAhead::Yes, @@ -1267,9 +1260,9 @@ fn candidate_checks() { assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + vec![(backed, chain_a_assignment.1)], &group_validators, + false ), Error::::PrematureCodeUpgrade ); @@ -1296,14 +1289,15 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_eq!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + vec![(backed, chain_a_assignment.1)], &group_validators, + false ), Err(Error::::ValidationDataHashMismatch.into()), ); @@ -1317,7 +1311,7 @@ fn candidate_checks() { pov_hash: Hash::repeat_byte(1), persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), hrmp_watermark: RELAY_PARENT_NUM, - validation_code: ValidationCode(vec![1]), + validation_code: ValidationCode(vec![9, 8, 7, 6, 5, 4, 3, 2, 1]), ..Default::default() } .build(); @@ -1331,14 +1325,15 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + vec![(backed, chain_a_assignment.1)], &group_validators, + false ), Error::::InvalidValidationCodeHash ); @@ -1366,14 +1361,15 @@ fn candidate_checks() { &keystore, &signing_context, BackingKind::Threshold, + None, ); assert_noop!( ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed], - &[chain_a_assignment].into_iter().collect(), + vec![(backed, chain_a_assignment.1)], &group_validators, + false ), Error::::ParaHeadMismatch ); @@ -1486,6 +1482,7 @@ fn backing_works() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let backed_b = back_candidate( @@ -1495,6 +1492,7 @@ fn backing_works() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let backed_c = back_candidate( @@ -1504,15 +1502,20 @@ fn backing_works() { &keystore, &signing_context, BackingKind::Threshold, + None, ); - let backed_candidates = vec![backed_a.clone(), backed_b.clone(), backed_c]; + let backed_candidates = vec![ + (backed_a.clone(), chain_a_assignment.1), + (backed_b.clone(), chain_b_assignment.1), + (backed_c, thread_a_assignment.1), + ]; let get_backing_group_idx = { // the order defines the group implicitly for this test case let backed_candidates_with_groups = backed_candidates .iter() .enumerate() - .map(|(idx, backed_candidate)| (backed_candidate.hash(), GroupIndex(idx as _))) + .map(|(idx, (backed_candidate, _))| (backed_candidate.hash(), GroupIndex(idx as _))) .collect::>(); move |candidate_hash_x: CandidateHash| -> Option { @@ -1532,10 +1535,8 @@ fn backing_works() { } = ParaInclusion::process_candidates( &allowed_relay_parents, backed_candidates.clone(), - &[chain_a_assignment, chain_b_assignment, thread_a_assignment] - .into_iter() - .collect(), &group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); @@ -1554,22 +1555,22 @@ fn backing_works() { CandidateHash, (CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>), >::new(); - backed_candidates.into_iter().for_each(|backed_candidate| { + backed_candidates.into_iter().for_each(|(backed_candidate, _)| { let candidate_receipt_with_backers = intermediate .entry(backed_candidate.hash()) .or_insert_with(|| (backed_candidate.receipt(), Vec::new())); - - assert_eq!( - backed_candidate.validity_votes.len(), - backed_candidate.validator_indices.count_ones() - ); + let (validator_indices, None) = + backed_candidate.validator_indices_and_core_index(false) + else { + panic!("Expected no injected core index") + }; + assert_eq!(backed_candidate.validity_votes().len(), validator_indices.count_ones()); candidate_receipt_with_backers.1.extend( - backed_candidate - .validator_indices + validator_indices .iter() .enumerate() .filter(|(_, signed)| **signed) - .zip(backed_candidate.validity_votes.iter().cloned()) + .zip(backed_candidate.validity_votes().iter().cloned()) .filter_map(|((validator_index_within_group, _), attestation)| { let grp_idx = get_backing_group_idx(backed_candidate.hash()).unwrap(); group_validators(grp_idx).map(|validator_indices| { @@ -1666,6 +1667,257 @@ fn backing_works() { }); } +#[test] +fn backing_works_with_elastic_scaling_mvp() { + let chain_a = ParaId::from(1_u32); + let chain_b = ParaId::from(2_u32); + let thread_a = ParaId::from(3_u32); + + // The block number of the relay-parent for testing. + const RELAY_PARENT_NUM: BlockNumber = 4; + + let paras = vec![ + (chain_a, ParaKind::Parachain), + (chain_b, ParaKind::Parachain), + (thread_a, ParaKind::Parathread), + ]; + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Ferdie, + ]; + let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); + for validator in validators.iter() { + Keystore::sr25519_generate_new( + &*keystore, + PARACHAIN_KEY_TYPE_ID, + Some(&validator.to_seed()), + ) + .unwrap(); + } + let validator_public = validator_pubkeys(&validators); + + new_test_ext(genesis_config(paras)).execute_with(|| { + shared::Pallet::::set_active_validators_ascending(validator_public.clone()); + shared::Pallet::::set_session_index(5); + + run_to_block(5, |_| None); + + let signing_context = + SigningContext { parent_hash: System::parent_hash(), session_index: 5 }; + + let group_validators = |group_index: GroupIndex| { + match group_index { + group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]), + group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]), + group_index if group_index == GroupIndex::from(2) => Some(vec![4]), + _ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"), + } + .map(|vs| vs.into_iter().map(ValidatorIndex).collect::>()) + }; + + // When processing candidates, we compute the group index from scheduler. + let validator_groups = vec![ + vec![ValidatorIndex(0), ValidatorIndex(1)], + vec![ValidatorIndex(2), ValidatorIndex(3)], + vec![ValidatorIndex(4)], + ]; + Scheduler::set_validator_groups(validator_groups); + + let allowed_relay_parents = default_allowed_relay_parent_tracker(); + + let mut candidate_a = TestCandidateBuilder { + para_id: chain_a, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(1), + persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); + + let mut candidate_b_1 = TestCandidateBuilder { + para_id: chain_b, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(2), + persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_1); + + let mut candidate_b_2 = TestCandidateBuilder { + para_id: chain_b, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(3), + persisted_validation_data_hash: make_vdata_hash(chain_b).unwrap(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_2); + + let backed_a = back_candidate( + candidate_a.clone(), + &validators, + group_validators(GroupIndex::from(0)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + None, + ); + + let backed_b_1 = back_candidate( + candidate_b_1.clone(), + &validators, + group_validators(GroupIndex::from(1)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + Some(CoreIndex(1)), + ); + + let backed_b_2 = back_candidate( + candidate_b_2.clone(), + &validators, + group_validators(GroupIndex::from(2)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + Some(CoreIndex(2)), + ); + + let backed_candidates = vec![ + (backed_a.clone(), CoreIndex(0)), + (backed_b_1.clone(), CoreIndex(1)), + (backed_b_2.clone(), CoreIndex(2)), + ]; + let get_backing_group_idx = { + // the order defines the group implicitly for this test case + let backed_candidates_with_groups = backed_candidates + .iter() + .enumerate() + .map(|(idx, (backed_candidate, _))| (backed_candidate.hash(), GroupIndex(idx as _))) + .collect::>(); + + move |candidate_hash_x: CandidateHash| -> Option { + backed_candidates_with_groups.iter().find_map(|(candidate_hash, grp)| { + if *candidate_hash == candidate_hash_x { + Some(*grp) + } else { + None + } + }) + } + }; + + let ProcessedCandidates { + core_indices: occupied_cores, + candidate_receipt_with_backing_validator_indices, + } = ParaInclusion::process_candidates( + &allowed_relay_parents, + backed_candidates.clone(), + &group_validators, + true, + ) + .expect("candidates scheduled, in order, and backed"); + + // Both b candidates will be backed. However, only one will be recorded on-chain and proceed + // with being made available. + assert_eq!( + occupied_cores, + vec![ + (CoreIndex::from(0), chain_a), + (CoreIndex::from(1), chain_b), + (CoreIndex::from(2), chain_b), + ] + ); + + // Transform the votes into the setup we expect + let mut expected = std::collections::HashMap::< + CandidateHash, + (CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>), + >::new(); + backed_candidates.into_iter().for_each(|(backed_candidate, _)| { + let candidate_receipt_with_backers = expected + .entry(backed_candidate.hash()) + .or_insert_with(|| (backed_candidate.receipt(), Vec::new())); + let (validator_indices, _maybe_core_index) = + backed_candidate.validator_indices_and_core_index(true); + assert_eq!(backed_candidate.validity_votes().len(), validator_indices.count_ones()); + candidate_receipt_with_backers.1.extend( + validator_indices + .iter() + .enumerate() + .filter(|(_, signed)| **signed) + .zip(backed_candidate.validity_votes().iter().cloned()) + .filter_map(|((validator_index_within_group, _), attestation)| { + let grp_idx = get_backing_group_idx(backed_candidate.hash()).unwrap(); + group_validators(grp_idx).map(|validator_indices| { + (validator_indices[validator_index_within_group], attestation) + }) + }), + ); + }); + + assert_eq!( + expected, + candidate_receipt_with_backing_validator_indices + .into_iter() + .map(|c| (c.0.hash(), c)) + .collect() + ); + + let backers = { + let num_backers = effective_minimum_backing_votes( + group_validators(GroupIndex(0)).unwrap().len(), + configuration::Pallet::::config().minimum_backing_votes, + ); + backing_bitfield(&(0..num_backers).collect::>()) + }; + assert_eq!( + >::get(&chain_a), + Some(CandidatePendingAvailability { + core: CoreIndex::from(0), + hash: candidate_a.hash(), + descriptor: candidate_a.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers, + backing_group: GroupIndex::from(0), + }) + ); + assert_eq!( + >::get(&chain_a), + Some(candidate_a.commitments), + ); + + // Only one candidate for b will be recorded on chain. + assert_eq!( + >::get(&chain_b), + Some(CandidatePendingAvailability { + core: CoreIndex::from(2), + hash: candidate_b_2.hash(), + descriptor: candidate_b_2.descriptor, + availability_votes: default_availability_votes(), + relay_parent_number: System::block_number() - 1, + backed_in_number: System::block_number(), + backers: backing_bitfield(&[4]), + backing_group: GroupIndex::from(2), + }) + ); + assert_eq!( + >::get(&chain_b), + Some(candidate_b_2.commitments), + ); + }); +} + #[test] fn can_include_candidate_with_ok_code_upgrade() { let chain_a = ParaId::from(1_u32); @@ -1726,7 +1978,7 @@ fn can_include_candidate_with_ok_code_upgrade() { relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), - new_validation_code: Some(vec![1, 2, 3].into()), + new_validation_code: Some(vec![9, 8, 7, 6, 5, 4, 3, 2, 1].into()), hrmp_watermark: RELAY_PARENT_NUM, ..Default::default() } @@ -1740,14 +1992,15 @@ fn can_include_candidate_with_ok_code_upgrade() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let ProcessedCandidates { core_indices: occupied_cores, .. } = ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_a], - &[chain_a_assignment].into_iter().collect(), + vec![(backed_a, chain_a_assignment.1)], &group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); @@ -1809,7 +2062,7 @@ fn check_allowed_relay_parents() { } let validator_public = validator_pubkeys(&validators); let mut config = genesis_config(paras); - config.configuration.config.group_rotation_frequency = 1; + config.configuration.config.scheduler_params.group_rotation_frequency = 1; new_test_ext(config).execute_with(|| { shared::Pallet::::set_active_validators_ascending(validator_public.clone()); @@ -1932,6 +2185,7 @@ fn check_allowed_relay_parents() { &keystore, &signing_context_a, BackingKind::Threshold, + None, ); let backed_b = back_candidate( @@ -1941,6 +2195,7 @@ fn check_allowed_relay_parents() { &keystore, &signing_context_b, BackingKind::Threshold, + None, ); let backed_c = back_candidate( @@ -1950,17 +2205,20 @@ fn check_allowed_relay_parents() { &keystore, &signing_context_c, BackingKind::Threshold, + None, ); - let backed_candidates = vec![backed_a, backed_b, backed_c]; + let backed_candidates = vec![ + (backed_a, chain_a_assignment.1), + (backed_b, chain_b_assignment.1), + (backed_c, thread_a_assignment.1), + ]; ParaInclusion::process_candidates( &allowed_relay_parents, backed_candidates.clone(), - &[chain_a_assignment, chain_b_assignment, thread_a_assignment] - .into_iter() - .collect(), &group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); }); @@ -2133,7 +2391,7 @@ fn para_upgrade_delay_scheduled_from_inclusion() { shared::Pallet::::set_active_validators_ascending(validator_public.clone()); shared::Pallet::::set_session_index(5); - let new_validation_code: ValidationCode = vec![1, 2, 3, 4, 5].into(); + let new_validation_code: ValidationCode = vec![9, 8, 7, 6, 5, 4, 3, 2, 1].into(); let new_validation_code_hash = new_validation_code.hash(); // Otherwise upgrade is no-op. @@ -2189,14 +2447,15 @@ fn para_upgrade_delay_scheduled_from_inclusion() { &keystore, &signing_context, BackingKind::Threshold, + None, ); let ProcessedCandidates { core_indices: occupied_cores, .. } = ParaInclusion::process_candidates( &allowed_relay_parents, - vec![backed_a], - &[chain_a_assignment].into_iter().collect(), + vec![(backed_a, chain_a_assignment.1)], &group_validators, + false, ) .expect("candidates scheduled, in order, and backed"); diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index e3fcf7dd603f3ca513536a22e6b07263132f8ff8..e18be03bdeb230d8959e3a1e76828d499c1c52bb 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -23,7 +23,7 @@ use crate::{ initializer, origin, paras, paras::ParaKind, paras_inherent, scheduler, - scheduler::common::{AssignmentProvider, AssignmentProviderConfig}, + scheduler::common::AssignmentProvider, session_info, shared, ParaId, }; use frame_support::pallet_prelude::*; @@ -147,7 +147,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -464,10 +463,6 @@ pub mod mock_assigner { pub(super) type MockAssignmentQueue = StorageValue<_, VecDeque, ValueQuery>; - #[pallet::storage] - pub(super) type MockProviderConfig = - StorageValue<_, AssignmentProviderConfig, OptionQuery>; - #[pallet::storage] pub(super) type MockCoreCount = StorageValue<_, u32, OptionQuery>; } @@ -479,12 +474,6 @@ pub mod mock_assigner { MockAssignmentQueue::::mutate(|queue| queue.push_back(assignment)); } - // This configuration needs to be customized to service `get_provider_config` in - // scheduler tests. - pub fn set_assignment_provider_config(config: AssignmentProviderConfig) { - MockProviderConfig::::set(Some(config)); - } - // Allows for customized core count in scheduler tests, rather than a core count // derived from on-demand config + parachain count. pub fn set_core_count(count: u32) { @@ -513,17 +502,6 @@ pub mod mock_assigner { // in the mock assigner. fn push_back_assignment(_assignment: Assignment) {} - // Gets the provider config we set earlier using `set_assignment_provider_config`, falling - // back to the on demand parachain configuration if none was set. - fn get_provider_config(_core_idx: CoreIndex) -> AssignmentProviderConfig { - match MockProviderConfig::::get() { - Some(config) => config, - None => AssignmentProviderConfig { - max_availability_timeouts: 1, - ttl: BlockNumber::from(5u32), - }, - } - } #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(_: CoreIndex, para_id: ParaId) -> Assignment { Assignment::Bulk(para_id) diff --git a/polkadot/runtime/parachains/src/paras/benchmarking/pvf_check.rs b/polkadot/runtime/parachains/src/paras/benchmarking/pvf_check.rs index 05c4c9c37b4d9241a64539c13a1960f93c80d7c1..53ccc35c477c093f69d06f8c9d6902e41627e0af 100644 --- a/polkadot/runtime/parachains/src/paras/benchmarking/pvf_check.rs +++ b/polkadot/runtime/parachains/src/paras/benchmarking/pvf_check.rs @@ -27,10 +27,10 @@ const SESSION_INDEX: SessionIndex = 1; const VALIDATOR_NUM: usize = 800; const CAUSES_NUM: usize = 100; fn validation_code() -> ValidationCode { - ValidationCode(vec![0]) + ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]) } fn old_validation_code() -> ValidationCode { - ValidationCode(vec![1]) + ValidationCode(vec![9, 8, 7, 6, 5, 4, 3, 2, 1]) } /// Prepares the PVF check statement and the validator signature to pass into diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index e97df8e4a2b3aaeee4e4d69c3c39262c23780588..39371391b1f09cc57d919ea85a7591689e2e04d6 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -119,7 +119,7 @@ use frame_system::pallet_prelude::*; use parity_scale_codec::{Decode, Encode}; use primitives::{ ConsensusLog, HeadData, Id as ParaId, PvfCheckStatement, SessionIndex, UpgradeGoAhead, - UpgradeRestriction, ValidationCode, ValidationCodeHash, ValidatorSignature, + UpgradeRestriction, ValidationCode, ValidationCodeHash, ValidatorSignature, MIN_CODE_SIZE, }; use scale_info::{Type, TypeInfo}; use sp_core::RuntimeDebug; @@ -679,6 +679,8 @@ pub mod pallet { PvfCheckSubjectInvalid, /// Parachain cannot currently schedule a code upgrade. CannotUpgradeCode, + /// Invalid validation code size. + InvalidCode, } /// All currently active PVF pre-checking votes. @@ -1230,6 +1232,10 @@ impl Pallet { // Check that we can schedule an upgrade at all. ensure!(Self::can_upgrade_validation_code(id), Error::::CannotUpgradeCode); let config = configuration::Pallet::::config(); + // Validation code sanity checks: + ensure!(new_code.0.len() >= MIN_CODE_SIZE as usize, Error::::InvalidCode); + ensure!(new_code.0.len() <= config.max_code_size as usize, Error::::InvalidCode); + let current_block = frame_system::Pallet::::block_number(); // Schedule the upgrade with a delay just like if a parachain triggered the upgrade. let upgrade_block = current_block.saturating_add(config.validation_upgrade_delay); @@ -1890,7 +1896,14 @@ impl Pallet { ) -> Weight { let mut weight = T::DbWeight::get().reads(1); - // Enacting this should be prevented by the `can_schedule_upgrade` + // 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 + } + + // Enacting this should be prevented by the `can_upgrade_validation_code` if FutureCodeHash::::contains_key(&id) { // This branch should never be reached. Signalling an upgrade is disallowed for a para // that already has one upgrade scheduled. diff --git a/polkadot/runtime/parachains/src/paras/tests.rs b/polkadot/runtime/parachains/src/paras/tests.rs index cca200c2765e361120b4c5ea8db3dfc7c82bdc13..262ec9d3fdba95c0368d4cfe03b288ccaeb049d1 100644 --- a/polkadot/runtime/parachains/src/paras/tests.rs +++ b/polkadot/runtime/parachains/src/paras/tests.rs @@ -17,7 +17,7 @@ use super::*; use frame_support::{assert_err, assert_ok, assert_storage_noop}; use keyring::Sr25519Keyring; -use primitives::{BlockNumber, PARACHAIN_KEY_TYPE_ID}; +use primitives::{vstaging::SchedulerParams, BlockNumber, PARACHAIN_KEY_TYPE_ID}; use sc_keystore::LocalKeystore; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -67,6 +67,16 @@ fn submit_super_majority_pvf_votes( .for_each(sign_and_include_pvf_check_statement); } +fn test_validation_code_1() -> ValidationCode { + let validation_code = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; + ValidationCode(validation_code) +} + +fn test_validation_code_2() -> ValidationCode { + let validation_code = vec![9, 8, 7, 6, 5, 4, 3, 2, 1]; + ValidationCode(validation_code) +} + fn run_to_block(to: BlockNumber, new_session: Option>) { let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); for validator in VALIDATORS.iter() { @@ -284,7 +294,7 @@ fn para_past_code_pruning_in_initialize() { let id = ParaId::from(0u32); let at_block: BlockNumber = 10; let included_block: BlockNumber = 12; - let validation_code = ValidationCode(vec![4, 5, 6]); + let validation_code = test_validation_code_2(); Paras::increase_code_ref(&validation_code.hash(), &validation_code); PastCodeHash::::insert(&(id, at_block), &validation_code.hash()); @@ -377,8 +387,8 @@ fn note_past_code_sets_up_pruning_correctly() { let id_a = ParaId::from(0u32); let id_b = ParaId::from(1u32); - Paras::note_past_code(id_a, 10, 12, ValidationCode(vec![1, 2, 3]).hash()); - Paras::note_past_code(id_b, 20, 23, ValidationCode(vec![4, 5, 6]).hash()); + Paras::note_past_code(id_a, 10, 12, test_validation_code_1().hash()); + Paras::note_past_code(id_b, 20, 23, test_validation_code_2().hash()); assert_eq!(PastCodePruning::::get(), vec![(id_a, 12), (id_b, 23)]); assert_eq!( @@ -398,7 +408,7 @@ fn code_upgrade_applied_after_delay() { let validation_upgrade_delay = 5; let validation_upgrade_cooldown = 10; - let original_code = ValidationCode(vec![1, 2, 3]); + let original_code = test_validation_code_1(); let paras = vec![( 0u32.into(), ParaGenesisArgs { @@ -425,7 +435,7 @@ fn code_upgrade_applied_after_delay() { check_code_is_stored(&original_code); let para_id = ParaId::from(0); - let new_code = ValidationCode(vec![4, 5, 6]); + let new_code = test_validation_code_2(); // Wait for at least one session change to set active validators. const EXPECTED_SESSION: SessionIndex = 1; @@ -516,7 +526,7 @@ fn code_upgrade_applied_without_setting_go_ahead_signal() { let validation_upgrade_delay = 5; let validation_upgrade_cooldown = 10; - let original_code = ValidationCode(vec![1, 2, 3]); + let original_code = test_validation_code_1(); let paras = vec![( 0u32.into(), ParaGenesisArgs { @@ -543,7 +553,7 @@ fn code_upgrade_applied_without_setting_go_ahead_signal() { check_code_is_stored(&original_code); let para_id = ParaId::from(0); - let new_code = ValidationCode(vec![4, 5, 6]); + let new_code = test_validation_code_2(); // Wait for at least one session change to set active validators. const EXPECTED_SESSION: SessionIndex = 1; @@ -637,7 +647,7 @@ fn code_upgrade_applied_after_delay_even_when_late() { let validation_upgrade_delay = 5; let validation_upgrade_cooldown = 10; - let original_code = ValidationCode(vec![1, 2, 3]); + let original_code = test_validation_code_1(); let paras = vec![( 0u32.into(), ParaGenesisArgs { @@ -662,7 +672,7 @@ fn code_upgrade_applied_after_delay_even_when_late() { new_test_ext(genesis_config).execute_with(|| { let para_id = ParaId::from(0); - let new_code = ValidationCode(vec![4, 5, 6]); + let new_code = test_validation_code_2(); // Wait for at least one session change to set active validators. const EXPECTED_SESSION: SessionIndex = 1; @@ -750,8 +760,8 @@ fn submit_code_change_when_not_allowed_is_err() { new_test_ext(genesis_config).execute_with(|| { let para_id = ParaId::from(0); - let new_code = ValidationCode(vec![4, 5, 6]); - let newer_code = ValidationCode(vec![4, 5, 6, 7]); + let new_code = test_validation_code_1(); + let newer_code = test_validation_code_2(); // Wait for at least one session change to set active validators. const EXPECTED_SESSION: SessionIndex = 1; @@ -832,8 +842,8 @@ fn upgrade_restriction_elapsed_doesnt_mean_can_upgrade() { new_test_ext(genesis_config).execute_with(|| { let para_id = 0u32.into(); - let new_code = ValidationCode(vec![4, 5, 6]); - let newer_code = ValidationCode(vec![4, 5, 6, 7]); + let new_code = test_validation_code_1(); + let newer_code = test_validation_code_2(); // Wait for at least one session change to set active validators. const EXPECTED_SESSION: SessionIndex = 1; @@ -880,7 +890,7 @@ fn full_parachain_cleanup_storage() { let code_retention_period = 20; let validation_upgrade_delay = 1 + 5; - let original_code = ValidationCode(vec![1, 2, 3]); + let original_code = test_validation_code_1(); let paras = vec![( 0u32.into(), ParaGenesisArgs { @@ -899,7 +909,10 @@ fn full_parachain_cleanup_storage() { minimum_validation_upgrade_delay: 2, // Those are not relevant to this test. However, HostConfiguration is still a // subject for the consistency check. - paras_availability_period: 1, + scheduler_params: SchedulerParams { + paras_availability_period: 1, + ..Default::default() + }, ..Default::default() }, }, @@ -910,7 +923,7 @@ fn full_parachain_cleanup_storage() { check_code_is_stored(&original_code); let para_id = ParaId::from(0); - let new_code = ValidationCode(vec![4, 5, 6]); + let new_code = test_validation_code_2(); // Wait for at least one session change to set active validators. const EXPECTED_SESSION: SessionIndex = 1; @@ -993,8 +1006,8 @@ fn full_parachain_cleanup_storage() { fn cannot_offboard_ongoing_pvf_check() { let para_id = ParaId::from(0); - let existing_code: ValidationCode = vec![1, 2, 3].into(); - let new_code: ValidationCode = vec![3, 2, 1].into(); + let existing_code = test_validation_code_1(); + let new_code = test_validation_code_2(); let paras = vec![( para_id, @@ -1152,7 +1165,7 @@ fn code_hash_at_returns_up_to_end_of_code_retention_period() { ParaGenesisArgs { para_kind: ParaKind::Parachain, genesis_head: dummy_head_data(), - validation_code: vec![1, 2, 3].into(), + validation_code: test_validation_code_1(), }, )]; @@ -1174,8 +1187,8 @@ fn code_hash_at_returns_up_to_end_of_code_retention_period() { const EXPECTED_SESSION: SessionIndex = 1; let para_id = ParaId::from(0); - let old_code: ValidationCode = vec![1, 2, 3].into(); - let new_code: ValidationCode = vec![4, 5, 6].into(); + let old_code = test_validation_code_1(); + let new_code = test_validation_code_2(); Paras::schedule_code_upgrade( para_id, new_code.clone(), @@ -1219,7 +1232,7 @@ fn code_hash_at_returns_up_to_end_of_code_retention_period() { #[test] fn code_ref_is_cleaned_correctly() { new_test_ext(Default::default()).execute_with(|| { - let code: ValidationCode = vec![1, 2, 3].into(); + let code = test_validation_code_1(); Paras::increase_code_ref(&code.hash(), &code); Paras::increase_code_ref(&code.hash(), &code); @@ -1244,8 +1257,8 @@ fn pvf_check_coalescing_onboarding_and_upgrade() { let a = ParaId::from(111); let b = ParaId::from(222); - let existing_code: ValidationCode = vec![1, 2, 3].into(); - let validation_code: ValidationCode = vec![3, 2, 1].into(); + let existing_code = test_validation_code_1(); + let validation_code = test_validation_code_2(); let paras = vec![( a, @@ -1320,7 +1333,7 @@ fn pvf_check_coalescing_onboarding_and_upgrade() { fn pvf_check_onboarding_reject_on_expiry() { let pvf_voting_ttl = 2; let a = ParaId::from(111); - let validation_code: ValidationCode = vec![3, 2, 1].into(); + let validation_code = test_validation_code_1(); let genesis_config = MockGenesisConfig { configuration: crate::configuration::GenesisConfig { @@ -1368,8 +1381,8 @@ fn pvf_check_onboarding_reject_on_expiry() { #[test] fn pvf_check_upgrade_reject() { let a = ParaId::from(111); - let old_code: ValidationCode = vec![1, 2, 3].into(); - let new_code: ValidationCode = vec![3, 2, 1].into(); + let old_code = test_validation_code_1(); + let new_code = test_validation_code_2(); let paras = vec![( a, @@ -1437,8 +1450,8 @@ fn pvf_check_upgrade_reject() { #[test] fn pvf_check_submit_vote() { - let code_a: ValidationCode = vec![3, 2, 1].into(); - let code_b: ValidationCode = vec![1, 2, 3].into(); + let code_a = test_validation_code_1(); + let code_b = test_validation_code_2(); let check = |stmt: PvfCheckStatement| -> (Result<_, _>, Result<_, _>) { let validators = &[ @@ -1554,8 +1567,8 @@ fn pvf_check_submit_vote() { #[test] fn include_pvf_check_statement_refunds_weight() { let a = ParaId::from(111); - let old_code: ValidationCode = vec![1, 2, 3].into(); - let new_code: ValidationCode = vec![3, 2, 1].into(); + let old_code = test_validation_code_1(); + let new_code = test_validation_code_2(); let paras = vec![( a, @@ -1620,7 +1633,7 @@ fn include_pvf_check_statement_refunds_weight() { fn add_trusted_validation_code_inserts_with_no_users() { // This test is to ensure that trusted validation code is inserted into the storage // with the reference count equal to 0. - let validation_code = ValidationCode(vec![1, 2, 3]); + let validation_code = test_validation_code_1(); new_test_ext(Default::default()).execute_with(|| { assert_ok!(Paras::add_trusted_validation_code( RuntimeOrigin::root(), @@ -1634,7 +1647,7 @@ fn add_trusted_validation_code_inserts_with_no_users() { fn add_trusted_validation_code_idempotent() { // This test makes sure that calling add_trusted_validation_code twice with the same // parameters is a no-op. - let validation_code = ValidationCode(vec![1, 2, 3]); + let validation_code = test_validation_code_1(); new_test_ext(Default::default()).execute_with(|| { assert_ok!(Paras::add_trusted_validation_code( RuntimeOrigin::root(), @@ -1653,7 +1666,7 @@ fn add_trusted_validation_code_idempotent() { fn poke_unused_validation_code_removes_code_cleanly() { // This test makes sure that calling poke_unused_validation_code with a code that is currently // in the storage but has no users will remove it cleanly from the storage. - let validation_code = ValidationCode(vec![1, 2, 3]); + let validation_code = test_validation_code_1(); new_test_ext(Default::default()).execute_with(|| { assert_ok!(Paras::add_trusted_validation_code( RuntimeOrigin::root(), @@ -1672,7 +1685,7 @@ fn poke_unused_validation_code_removes_code_cleanly() { #[test] fn poke_unused_validation_code_doesnt_remove_code_with_users() { let para_id = 100.into(); - let validation_code = ValidationCode(vec![1, 2, 3]); + let validation_code = test_validation_code_1(); new_test_ext(Default::default()).execute_with(|| { // First we add the code to the storage. assert_ok!(Paras::add_trusted_validation_code( @@ -1708,7 +1721,7 @@ fn increase_code_ref_doesnt_have_allergy_on_add_trusted_validation_code() { // to a disaster. // NOTE that this test is extra paranoid, as it is not really possible to hit // `decrease_code_ref` without calling `increase_code_ref` first. - let code = ValidationCode(vec![1, 2, 3]); + let code = test_validation_code_1(); new_test_ext(Default::default()).execute_with(|| { assert_ok!(Paras::add_trusted_validation_code(RuntimeOrigin::root(), code.clone())); @@ -1732,7 +1745,7 @@ fn add_trusted_validation_code_insta_approval() { // `add_trusted_validation_code` and uses the `CodeByHash::contains_key` which is what // `add_trusted_validation_code` uses. let para_id = 100.into(); - let validation_code = ValidationCode(vec![1, 2, 3]); + let validation_code = test_validation_code_1(); let validation_upgrade_delay = 25; let minimum_validation_upgrade_delay = 2; let genesis_config = MockGenesisConfig { @@ -1779,7 +1792,7 @@ fn add_trusted_validation_code_enacts_existing_pvf_vote() { // already going through PVF pre-checking voting will conclude the voting and enact the // code upgrade. let para_id = 100.into(); - let validation_code = ValidationCode(vec![1, 2, 3]); + let validation_code = test_validation_code_1(); let validation_upgrade_delay = 25; let minimum_validation_upgrade_delay = 2; let genesis_config = MockGenesisConfig { @@ -1868,7 +1881,7 @@ fn verify_para_head_is_externally_accessible() { #[test] fn most_recent_context() { - let validation_code: ValidationCode = vec![1, 2, 3].into(); + let validation_code = test_validation_code_1(); let genesis_config = MockGenesisConfig::default(); diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index 0f6b23ae1b39213f8afbd37798f8f7f31a024cf9..ad3fa8e0dc712b64141231250e251498803c52b1 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -120,7 +120,7 @@ benchmarks! { // 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); + assert_eq!(benchmark.backed_candidates.get(0).unwrap().validity_votes().len(), votes); benchmark.bitfields.clear(); benchmark.disputes.clear(); @@ -177,7 +177,7 @@ benchmarks! { // There is 1 backed assert_eq!(benchmark.backed_candidates.len(), 1); assert_eq!( - benchmark.backed_candidates.get(0).unwrap().validity_votes.len(), + benchmark.backed_candidates.get(0).unwrap().validity_votes().len(), votes, ); diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index 81e092f0a991cd216bac3c1d8e48ae8bcea59144..723a15bdba7a88d73a2610b90910f53c6150475f 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -43,15 +43,14 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_babe::{self, ParentBlockRandomness}; use primitives::{ - effective_minimum_backing_votes, BackedCandidate, CandidateHash, CandidateReceipt, - CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, DisputeStatementSet, - InherentData as ParachainsInherentData, MultiDisputeStatementSet, ScrapedOnChainVotes, - SessionIndex, SignedAvailabilityBitfields, SigningContext, UncheckedSignedAvailabilityBitfield, - UncheckedSignedAvailabilityBitfields, ValidatorId, ValidatorIndex, ValidityAttestation, - PARACHAINS_INHERENT_IDENTIFIER, + effective_minimum_backing_votes, vstaging::node_features::FeatureIndex, BackedCandidate, + CandidateHash, CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, + CoreIndex, DisputeStatementSet, InherentData as ParachainsInherentData, + MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, + SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, + ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, }; use rand::{seq::SliceRandom, SeedableRng}; - use scale_info::TypeInfo; use sp_runtime::traits::{Header as HeaderT, One}; use sp_std::{ @@ -145,6 +144,10 @@ pub mod pallet { DisputeInvalid, /// A candidate was backed by a disabled validator BackedByDisabled, + /// A candidate was backed even though the paraid was not scheduled. + BackedOnUnscheduledCore, + /// Too many candidates supplied. + UnscheduledCandidate, } /// Whether the paras inherent was included within this block. @@ -585,25 +588,39 @@ impl Pallet { let freed = collect_all_freed_cores::(freed_concluded.iter().cloned()); >::free_cores_and_fill_claimqueue(freed, now); - let scheduled = >::scheduled_paras() - .map(|(core_idx, para_id)| (para_id, core_idx)) - .collect(); METRICS.on_candidates_processed_total(backed_candidates.len() as u64); - let SanitizedBackedCandidates { backed_candidates, votes_from_disabled_were_dropped } = - sanitize_backed_candidates::( - backed_candidates, - &allowed_relay_parents, - |candidate_idx: usize, - backed_candidate: &BackedCandidate<::Hash>| - -> bool { - let para_id = backed_candidate.descriptor().para_id; - let prev_context = >::para_most_recent_context(para_id); - let check_ctx = CandidateCheckContext::::new(prev_context); - - // never include a concluded-invalid candidate - current_concluded_invalid_disputes.contains(&backed_candidate.hash()) || + let core_index_enabled = configuration::Pallet::::config() + .node_features + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|b| *b) + .unwrap_or(false); + + let mut scheduled: BTreeMap> = BTreeMap::new(); + let mut total_scheduled_cores = 0; + + for (core_idx, para_id) in >::scheduled_paras() { + total_scheduled_cores += 1; + scheduled.entry(para_id).or_default().insert(core_idx); + } + + let SanitizedBackedCandidates { + backed_candidates_with_core, + votes_from_disabled_were_dropped, + dropped_unscheduled_candidates, + } = sanitize_backed_candidates::( + backed_candidates, + &allowed_relay_parents, + |candidate_idx: usize, + backed_candidate: &BackedCandidate<::Hash>| + -> bool { + let para_id = backed_candidate.descriptor().para_id; + let prev_context = >::para_most_recent_context(para_id); + let check_ctx = CandidateCheckContext::::new(prev_context); + + // never include a concluded-invalid candidate + current_concluded_invalid_disputes.contains(&backed_candidate.hash()) || // Instead of checking the candidates with code upgrades twice // move the checking up here and skip it in the training wheels fallback. // That way we avoid possible duplicate checks while assuring all @@ -611,13 +628,19 @@ impl Pallet { // // NOTE: this is the only place where we check the relay-parent. check_ctx - .verify_backed_candidate(&allowed_relay_parents, candidate_idx, backed_candidate) + .verify_backed_candidate(&allowed_relay_parents, candidate_idx, backed_candidate.candidate()) .is_err() - }, - &scheduled, - ); + }, + scheduled, + core_index_enabled, + ); - METRICS.on_candidates_sanitized(backed_candidates.len() as u64); + ensure!( + backed_candidates_with_core.len() <= total_scheduled_cores, + Error::::UnscheduledCandidate + ); + + METRICS.on_candidates_sanitized(backed_candidates_with_core.len() as u64); // In `Enter` context (invoked during execution) there should be no backing votes from // disabled validators because they should have been filtered out during inherent data @@ -626,15 +649,22 @@ impl Pallet { ensure!(!votes_from_disabled_were_dropped, Error::::BackedByDisabled); } + // In `Enter` context (invoked during execution) we shouldn't have filtered any candidates + // due to a para not being scheduled. They have been filtered during inherent data + // preparation (`ProvideInherent` context). Abort in such cases. + if context == ProcessInherentDataContext::Enter { + ensure!(!dropped_unscheduled_candidates, Error::::BackedOnUnscheduledCore); + } + // Process backed candidates according to scheduled cores. let inclusion::ProcessedCandidates::< as HeaderT>::Hash> { core_indices: occupied, candidate_receipt_with_backing_validator_indices, } = >::process_candidates( &allowed_relay_parents, - backed_candidates.clone(), - &scheduled, + backed_candidates_with_core.clone(), >::group_validators, + core_index_enabled, )?; // Note which of the scheduled cores were actually occupied by a backed candidate. >::occupied(occupied.into_iter().map(|e| (e.0, e.1)).collect()); @@ -651,8 +681,15 @@ impl Pallet { let bitfields = bitfields.into_iter().map(|v| v.into_unchecked()).collect(); - let processed = - ParachainsInherentData { bitfields, backed_candidates, disputes, parent_header }; + let processed = ParachainsInherentData { + bitfields, + backed_candidates: backed_candidates_with_core + .into_iter() + .map(|(candidate, _)| candidate) + .collect(), + disputes, + parent_header, + }; Ok((processed, Some(all_weight_after).into())) } } @@ -751,7 +788,7 @@ fn random_sel Weight>( /// Assumes disputes are already filtered by the time this is called. /// /// Returns the total weight consumed by `bitfields` and `candidates`. -fn apply_weight_limit( +pub(crate) fn apply_weight_limit( candidates: &mut Vec::Hash>>, bitfields: &mut UncheckedSignedAvailabilityBitfields, max_consumable_weight: Weight, @@ -768,35 +805,71 @@ fn apply_weight_limit( return total } - // Prefer code upgrades, they tend to be large and hence stand no chance to be picked - // late while maintaining the weight bounds. - let preferred_indices = candidates + // Invariant: block author provides candidate in the order in which they form a chain + // wrt elastic scaling. If the invariant is broken, we'd fail later when filtering candidates + // which are unchained. + + let mut chained_candidates: Vec> = Vec::new(); + let mut current_para_id = None; + + for candidate in sp_std::mem::take(candidates).into_iter() { + let candidate_para_id = candidate.descriptor().para_id; + if Some(candidate_para_id) == current_para_id { + let chain = chained_candidates + .last_mut() + .expect("if the current_para_id is Some, then vec is not empty; qed"); + chain.push(candidate); + } else { + current_para_id = Some(candidate_para_id); + chained_candidates.push(vec![candidate]); + } + } + + // Elastic scaling: we prefer chains that have a code upgrade among the candidates, + // as the candidates containing the upgrade tend to be large and hence stand no chance to + // be picked late while maintaining the weight bounds. + // + // Limitations: For simplicity if total weight of a chain of candidates is larger than + // the remaining weight, the chain will still not be included while it could still be possible + // to include part of that chain. + let preferred_chain_indices = chained_candidates .iter() .enumerate() - .filter_map(|(idx, candidate)| { - candidate.candidate.commitments.new_validation_code.as_ref().map(|_code| idx) + .filter_map(|(idx, candidates)| { + // Check if any of the candidate in chain contains a code upgrade. + if candidates + .iter() + .any(|candidate| candidate.candidate().commitments.new_validation_code.is_some()) + { + Some(idx) + } else { + None + } }) .collect::>(); - // There is weight remaining to be consumed by a subset of candidates + // There is weight remaining to be consumed by a subset of chained candidates // which are going to be picked now. if let Some(max_consumable_by_candidates) = max_consumable_weight.checked_sub(&total_bitfields_weight) { - let (acc_candidate_weight, indices) = - random_sel::::Hash>, _>( + let (acc_candidate_weight, chained_indices) = + random_sel::::Hash>>, _>( rng, - &candidates, - preferred_indices, - |c| backed_candidate_weight::(c), + &chained_candidates, + preferred_chain_indices, + |candidates| backed_candidates_weight::(&candidates), max_consumable_by_candidates, ); - log::debug!(target: LOG_TARGET, "Indices Candidates: {:?}, size: {}", indices, candidates.len()); - candidates.indexed_retain(|idx, _backed_candidate| indices.binary_search(&idx).is_ok()); + log::debug!(target: LOG_TARGET, "Indices Candidates: {:?}, size: {}", chained_indices, candidates.len()); + chained_candidates + .indexed_retain(|idx, _backed_candidates| chained_indices.binary_search(&idx).is_ok()); // pick all bitfields, and // fill the remaining space with candidates let total_consumed = acc_candidate_weight.saturating_add(total_bitfields_weight); + *candidates = chained_candidates.into_iter().flatten().collect::>(); + return total_consumed } @@ -916,16 +989,22 @@ pub(crate) fn sanitize_bitfields( // Result from `sanitize_backed_candidates` #[derive(Debug, PartialEq)] struct SanitizedBackedCandidates { - // Sanitized backed candidates. The `Vec` is sorted according to the occupied core index. - backed_candidates: Vec>, + // Sanitized backed candidates along with the assigned core. The `Vec` is sorted according to + // the occupied core index. + backed_candidates_with_core: Vec<(BackedCandidate, CoreIndex)>, // Set to true if any votes from disabled validators were dropped from the input. votes_from_disabled_were_dropped: bool, + // Set to true if any candidates were dropped due to filtering done in + // `map_candidates_to_cores` + dropped_unscheduled_candidates: bool, } /// Filter out: /// 1. any candidates that have a concluded invalid dispute -/// 2. all backing votes from disabled validators -/// 3. any candidates that end up with less than `effective_minimum_backing_votes` backing votes +/// 2. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned +/// but have no injected core index. +/// 3. all backing votes from disabled validators +/// 4. any candidates that end up with less than `effective_minimum_backing_votes` backing votes /// /// `scheduled` follows the same naming scheme as provided in the /// guide: Currently `free` but might become `occupied`. @@ -944,7 +1023,8 @@ fn sanitize_backed_candidates< mut backed_candidates: Vec>, allowed_relay_parents: &AllowedRelayParentsTracker>, mut candidate_has_concluded_invalid_dispute_or_is_invalid: F, - scheduled: &BTreeMap, + scheduled: BTreeMap>, + core_index_enabled: bool, ) -> SanitizedBackedCandidates { // Remove any candidates that were concluded invalid. // This does not assume sorting. @@ -952,22 +1032,23 @@ fn sanitize_backed_candidates< !candidate_has_concluded_invalid_dispute_or_is_invalid(candidate_idx, backed_candidate) }); - // Assure the backed candidate's `ParaId`'s core is free. - // This holds under the assumption that `Scheduler::schedule` is called _before_. - // We don't check the relay-parent because this is done in the closure when - // constructing the inherent and during actual processing otherwise. - - backed_candidates.retain(|backed_candidate| { - let desc = backed_candidate.descriptor(); + let initial_candidate_count = backed_candidates.len(); + // Map candidates to scheduled cores. Filter out any unscheduled candidates. + let mut backed_candidates_with_core = map_candidates_to_cores::( + &allowed_relay_parents, + scheduled, + core_index_enabled, + backed_candidates, + ); - scheduled.get(&desc.para_id).is_some() - }); + let dropped_unscheduled_candidates = + initial_candidate_count != backed_candidates_with_core.len(); // Filter out backing statements from disabled validators - let dropped_disabled = filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + let votes_from_disabled_were_dropped = filter_backed_statements_from_disabled_validators::( + &mut backed_candidates_with_core, &allowed_relay_parents, - scheduled, + core_index_enabled, ); // Sort the `Vec` last, once there is a guarantee that these @@ -975,14 +1056,12 @@ fn sanitize_backed_candidates< // but more importantly are scheduled for a free core. // This both avoids extra work for obviously invalid candidates, // but also allows this to be done in place. - backed_candidates.sort_by(|x, y| { - // Never panics, since we filtered all panic arguments out in the previous `fn retain`. - scheduled[&x.descriptor().para_id].cmp(&scheduled[&y.descriptor().para_id]) - }); + backed_candidates_with_core.sort_by(|(_x, core_x), (_y, core_y)| core_x.cmp(&core_y)); SanitizedBackedCandidates { - backed_candidates, - votes_from_disabled_were_dropped: dropped_disabled, + dropped_unscheduled_candidates, + votes_from_disabled_were_dropped, + backed_candidates_with_core, } } @@ -1071,9 +1150,12 @@ fn limit_and_sanitize_disputes< // few more sanity checks. Returns `true` if at least one statement is removed and `false` // otherwise. fn filter_backed_statements_from_disabled_validators( - backed_candidates: &mut Vec::Hash>>, + backed_candidates_with_core: &mut Vec<( + BackedCandidate<::Hash>, + CoreIndex, + )>, allowed_relay_parents: &AllowedRelayParentsTracker>, - scheduled: &BTreeMap, + core_index_enabled: bool, ) -> bool { let disabled_validators = BTreeSet::<_>::from_iter(shared::Pallet::::disabled_validators().into_iter()); @@ -1083,7 +1165,7 @@ fn filter_backed_statements_from_disabled_validators *core_idx, - None => { - log::debug!(target: LOG_TARGET, "Can't get core idx of a backed candidate for para id {:?}. Dropping the candidate.", bc.descriptor().para_id); - return false - } - }; + backed_candidates_with_core.retain_mut(|(bc, core_idx)| { + let (validator_indices, maybe_core_index) = bc.validator_indices_and_core_index(core_index_enabled); + let mut validator_indices = BitVec::<_>::from(validator_indices); // 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 @@ -1116,7 +1192,7 @@ fn filter_backed_statements_from_disabled_validators>::group_assigned_to_core( - core_idx, + *core_idx, relay_parent_block_number + One::one(), ) { Some(group_idx) => group_idx, @@ -1138,12 +1214,15 @@ fn filter_backed_statements_from_disabled_validators::from_iter(validator_group.iter().map(|idx| disabled_validators.contains(idx))); // The indices of statements from disabled validators in `BackedCandidate`. We have to drop these. - let indices_to_drop = disabled_indices.clone() & &bc.validator_indices; + let indices_to_drop = disabled_indices.clone() & &validator_indices; // Apply the bitmask to drop the disabled validator from `validator_indices` - bc.validator_indices &= !disabled_indices; + validator_indices &= !disabled_indices; + // Update the backed candidate + bc.set_validator_indices_and_core_index(validator_indices, maybe_core_index); + // Remove the corresponding votes from `validity_votes` for idx in indices_to_drop.iter_ones().rev() { - bc.validity_votes.remove(idx); + bc.validity_votes_mut().remove(idx); } // If at least one statement was dropped we need to return `true` @@ -1154,10 +1233,9 @@ fn filter_backed_statements_from_disabled_validators( + allowed_relay_parents: &AllowedRelayParentsTracker>, + mut scheduled: BTreeMap>, + core_index_enabled: bool, + candidates: Vec>, +) -> Vec<(BackedCandidate, CoreIndex)> { + let mut backed_candidates_with_core = Vec::with_capacity(candidates.len()); + + // We keep a candidate if the parachain has only one core assigned or if + // a core index is provided by block author and it's indeed scheduled. + for backed_candidate in candidates { + let maybe_injected_core_index = get_injected_core_index::( + allowed_relay_parents, + &backed_candidate, + core_index_enabled, + ); + + let scheduled_cores = scheduled.get_mut(&backed_candidate.descriptor().para_id); + // Candidates without scheduled cores are silently filtered out. + if let Some(scheduled_cores) = scheduled_cores { + if let Some(core_idx) = maybe_injected_core_index { + if scheduled_cores.contains(&core_idx) { + scheduled_cores.remove(&core_idx); + backed_candidates_with_core.push((backed_candidate, core_idx)); + } + } else if scheduled_cores.len() == 1 { + backed_candidates_with_core + .push((backed_candidate, scheduled_cores.pop_first().expect("Length is 1"))); + } + } + } + + backed_candidates_with_core +} + +fn get_injected_core_index( + allowed_relay_parents: &AllowedRelayParentsTracker>, + candidate: &BackedCandidate, + core_index_enabled: bool, +) -> Option { + // After stripping the 8 bit extensions, the `validator_indices` field length is expected + // to be equal to backing group size. If these don't match, the `CoreIndex` is badly encoded, + // or not supported. + let (validator_indices, maybe_core_idx) = + candidate.validator_indices_and_core_index(core_index_enabled); + + let Some(core_idx) = maybe_core_idx else { return None }; + + let relay_parent_block_number = + match allowed_relay_parents.acquire_info(candidate.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.", + candidate.descriptor().relay_parent, + candidate.candidate().hash(), + ); + return None + }, + }; + + // Get the backing group of the candidate backed at `core_idx`. + let group_idx = match >::group_assigned_to_core( + core_idx, + relay_parent_block_number + One::one(), + ) { + Some(group_idx) => group_idx, + None => { + log::debug!( + target: LOG_TARGET, + "Can't get the group index for core idx {:?}. Dropping the candidate {:?}.", + core_idx, + candidate.candidate().hash(), + ); + return None + }, + }; + + let group_validators = match >::group_validators(group_idx) { + Some(validators) => validators, + None => return None, + }; + + if group_validators.len() == validator_indices.len() { + Some(core_idx) + } else { + None + } } diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 6f3eac35685a82b32c69b27e5379620988c956b7..fb4d1bd2226ed77c406a062c78a1ca26c4958e40 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -22,15 +22,19 @@ use super::*; #[cfg(not(feature = "runtime-benchmarks"))] mod enter { - use super::*; + use super::{inclusion::tests::TestCandidateBuilder, *}; use crate::{ builder::{Bench, BenchBuilder}, mock::{mock_assigner, new_test_ext, BlockLength, BlockWeights, MockGenesisConfig, Test}, - scheduler::common::Assignment, + scheduler::{ + common::{Assignment, AssignmentProvider}, + ParasEntry, + }, }; use assert_matches::assert_matches; use frame_support::assert_ok; use frame_system::limits; + use primitives::vstaging::SchedulerParams; use sp_runtime::Perbill; use sp_std::collections::btree_map::BTreeMap; @@ -84,7 +88,7 @@ mod enter { // `create_inherent` and will not cause `enter` to early. fn include_backed_candidates() { let config = MockGenesisConfig::default(); - assert!(config.configuration.config.scheduling_lookahead > 0); + assert!(config.configuration.config.scheduler_params.lookahead > 0); new_test_ext(config).execute_with(|| { let dispute_statements = BTreeMap::new(); @@ -622,7 +626,7 @@ mod enter { #[test] fn limit_candidates_over_weight_1() { let config = MockGenesisConfig::default(); - assert!(config.configuration.config.scheduling_lookahead > 0); + assert!(config.configuration.config.scheduler_params.lookahead > 0); new_test_ext(config).execute_with(|| { // Create the inherent data for this block @@ -697,6 +701,25 @@ mod enter { 2 ); + // One core was scheduled. We should put the assignment back, before calling enter(). + let now = >::block_number() + 1; + let used_cores = 5; + let cores = (0..used_cores) + .into_iter() + .map(|i| { + let SchedulerParams { ttl, .. } = + >::config().scheduler_params; + // Load an assignment into provider so that one is present to pop + let assignment = + ::AssignmentProvider::get_mock_assignment( + CoreIndex(i), + ParaId::from(i), + ); + (CoreIndex(i), [ParasEntry::new(assignment, now + ttl)].into()) + }) + .collect(); + scheduler::ClaimQueue::::set(cores); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), limit_inherent_data, @@ -902,6 +925,129 @@ mod enter { }); } + // Helper fn that builds chained dummy candidates for elastic scaling tests + fn build_backed_candidate_chain( + para_id: ParaId, + len: usize, + start_core_index: usize, + code_upgrade_index: Option, + ) -> Vec { + if let Some(code_upgrade_index) = code_upgrade_index { + assert!(code_upgrade_index < len, "Code upgrade index out of bounds"); + } + + (0..len) + .into_iter() + .map(|idx| { + let mut builder = TestCandidateBuilder::default(); + builder.para_id = para_id; + let mut ccr = builder.build(); + + if Some(idx) == code_upgrade_index { + ccr.commitments.new_validation_code = Some(vec![1, 2, 3, 4].into()); + } + + ccr.commitments.processed_downward_messages = idx as u32; + let core_index = start_core_index + idx; + + BackedCandidate::new( + ccr.into(), + Default::default(), + Default::default(), + Some(CoreIndex(core_index as u32)), + ) + }) + .collect::>() + } + + // Ensure that overweight parachain inherents are always rejected by the runtime. + // Runtime should panic and return `InherentOverweight` error. + #[test] + fn test_backed_candidates_apply_weight_works_for_elastic_scaling() { + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + let seed = [ + 1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, + 0, 0, 0, 0, 0, 2, 92, + ]; + let mut rng = rand_chacha::ChaChaRng::from_seed(seed); + + // Create an overweight inherent and oversized block + let mut backed_and_concluding = BTreeMap::new(); + + for i in 0..30 { + backed_and_concluding.insert(i, i); + } + + let scenario = make_inherent_data(TestConfig { + dispute_statements: Default::default(), + dispute_sessions: vec![], // 3 cores with disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: false, + }); + + let mut para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 30 backed candidates, 0 disputes + // => 5*30 = 150) + assert_eq!(para_inherent_data.bitfields.len(), 150); + // * 30 backed candidates + assert_eq!(para_inherent_data.backed_candidates.len(), 30); + + let mut input_candidates = + build_backed_candidate_chain(ParaId::from(1000), 3, 0, Some(1)); + let chained_candidates_weight = backed_candidates_weight::(&input_candidates); + + input_candidates.append(&mut para_inherent_data.backed_candidates); + let input_bitfields = para_inherent_data.bitfields; + + // Test if weight insufficient even for 1 candidate (which doesn't contain a code + // upgrade). + let max_weight = backed_candidate_weight::(&input_candidates[0]) + + signed_bitfields_weight::(&input_bitfields); + let mut backed_candidates = input_candidates.clone(); + let mut bitfields = input_bitfields.clone(); + apply_weight_limit::( + &mut backed_candidates, + &mut bitfields, + max_weight, + &mut rng, + ); + + // 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)); + + // All bitfields are kept. + assert_eq!(bitfields.len(), 150); + + // Test if para_id 1000 chained candidates make it if there is enough room for its 3 + // candidates. + let max_weight = + chained_candidates_weight + signed_bitfields_weight::(&input_bitfields); + let mut backed_candidates = input_candidates.clone(); + let mut bitfields = input_bitfields.clone(); + apply_weight_limit::( + &mut backed_candidates, + &mut bitfields, + max_weight, + &mut rng, + ); + + // 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)); + + // All bitfields are kept. + assert_eq!(bitfields.len(), 150); + }); + } + // Ensure that overweight parachain inherents are always rejected by the runtime. // Runtime should panic and return `InherentOverweight` error. #[test] @@ -980,6 +1126,7 @@ mod sanitizers { AvailabilityBitfield, GroupIndex, Hash, Id as ParaId, SignedAvailabilityBitfield, ValidatorIndex, }; + use rstest::rstest; use sp_core::crypto::UncheckedFrom; use crate::mock::Test; @@ -1238,12 +1385,13 @@ mod sanitizers { // Backed candidates and scheduled parachains used for `sanitize_backed_candidates` testing struct TestData { backed_candidates: Vec, - scheduled_paras: BTreeMap, + all_backed_candidates_with_core: Vec<(BackedCandidate, CoreIndex)>, + scheduled_paras: BTreeMap>, } // Generate test data for the candidates and assert that the evnironment is set as expected // (check the comments for details) - fn get_test_data() -> TestData { + fn get_test_data(core_index_enabled: bool) -> TestData { const RELAY_PARENT_NUM: u32 = 3; // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing @@ -1285,9 +1433,14 @@ mod sanitizers { shared::Pallet::::set_active_validators_ascending(validator_ids); // Two scheduled parachains - ParaId(1) on CoreIndex(0) and ParaId(2) on CoreIndex(1) - let scheduled = (0_usize..2) + let scheduled: BTreeMap> = (0_usize..2) .into_iter() - .map(|idx| (ParaId::from(1_u32 + idx as u32), CoreIndex::from(idx as u32))) + .map(|idx| { + ( + ParaId::from(1_u32 + idx as u32), + [CoreIndex::from(idx as u32)].into_iter().collect(), + ) + }) .collect::>(); // Set the validator groups in `scheduler` @@ -1301,7 +1454,7 @@ mod sanitizers { ( CoreIndex::from(0), VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, RELAY_PARENT_NUM, )]), ), @@ -1319,12 +1472,12 @@ mod sanitizers { match group_index { group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]), group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]), - _ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"), + _ => panic!("Group index out of bounds"), } .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) }; - // Two backed candidates from each parachain + // One backed candidate from each parachain let backed_candidates = (0_usize..2) .into_iter() .map(|idx0| { @@ -1348,6 +1501,7 @@ mod sanitizers { &keystore, &signing_context, BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(idx0 as u32)), ); backed }) @@ -1369,13 +1523,373 @@ mod sanitizers { ] ); - TestData { backed_candidates, scheduled_paras: scheduled } + let all_backed_candidates_with_core = backed_candidates + .iter() + .map(|candidate| { + // Only one entry for this test data. + ( + candidate.clone(), + scheduled + .get(&candidate.descriptor().para_id) + .unwrap() + .first() + .copied() + .unwrap(), + ) + }) + .collect(); + + TestData { + backed_candidates, + scheduled_paras: scheduled, + all_backed_candidates_with_core, + } + } + + // Generate test data for the candidates and assert that the evnironment is set as expected + // (check the comments for details) + // Para 1 scheduled on core 0 and core 1. Two candidates are supplied. + // Para 2 scheduled on cores 2 and 3. One candidate supplied. + // Para 3 scheduled on core 4. One candidate supplied. + // Para 4 scheduled on core 5. Two candidates supplied. + // Para 5 scheduled on core 6. No candidates supplied. + fn get_test_data_multiple_cores_per_para(core_index_enabled: bool) -> TestData { + const RELAY_PARENT_NUM: u32 = 3; + + // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing + // votes) won't behave correctly + shared::Pallet::::add_allowed_relay_parent( + default_header().hash(), + Default::default(), + RELAY_PARENT_NUM, + 1, + ); + + let header = default_header(); + let relay_parent = header.hash(); + let session_index = SessionIndex::from(0_u32); + + let keystore = LocalKeystore::in_memory(); + let keystore = Arc::new(keystore) as KeystorePtr; + let signing_context = SigningContext { parent_hash: relay_parent, session_index }; + + let validators = vec![ + keyring::Sr25519Keyring::Alice, + keyring::Sr25519Keyring::Bob, + keyring::Sr25519Keyring::Charlie, + keyring::Sr25519Keyring::Dave, + keyring::Sr25519Keyring::Eve, + keyring::Sr25519Keyring::Ferdie, + keyring::Sr25519Keyring::One, + ]; + for validator in validators.iter() { + Keystore::sr25519_generate_new( + &*keystore, + PARACHAIN_KEY_TYPE_ID, + Some(&validator.to_seed()), + ) + .unwrap(); + } + + // Set active validators in `shared` pallet + let validator_ids = + validators.iter().map(|v| v.public().into()).collect::>(); + shared::Pallet::::set_active_validators_ascending(validator_ids); + + // Set the validator groups in `scheduler` + scheduler::Pallet::::set_validator_groups(vec![ + vec![ValidatorIndex(0)], + vec![ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3)], + vec![ValidatorIndex(4)], + vec![ValidatorIndex(5)], + vec![ValidatorIndex(6)], + ]); + + // Update scheduler's claimqueue with the parachains + scheduler::Pallet::::set_claimqueue(BTreeMap::from([ + ( + CoreIndex::from(0), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(1), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(2), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(2) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(3), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(3) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(4), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 3.into(), core_index: CoreIndex(4) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(5), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 4.into(), core_index: CoreIndex(5) }, + RELAY_PARENT_NUM, + )]), + ), + ( + CoreIndex::from(6), + VecDeque::from([ParasEntry::new( + Assignment::Pool { para_id: 5.into(), core_index: CoreIndex(6) }, + RELAY_PARENT_NUM, + )]), + ), + ])); + + // Callback used for backing candidates + let group_validators = |group_index: GroupIndex| { + match group_index { + group_index if group_index == GroupIndex::from(0) => Some(vec![0]), + group_index if group_index == GroupIndex::from(1) => Some(vec![1]), + group_index if group_index == GroupIndex::from(2) => Some(vec![2]), + group_index if group_index == GroupIndex::from(3) => Some(vec![3]), + group_index if group_index == GroupIndex::from(4) => Some(vec![4]), + group_index if group_index == GroupIndex::from(5) => Some(vec![5]), + group_index if group_index == GroupIndex::from(6) => Some(vec![6]), + + _ => panic!("Group index out of bounds"), + } + .map(|m| m.into_iter().map(ValidatorIndex).collect::>()) + }; + + let mut backed_candidates = vec![]; + let mut all_backed_candidates_with_core = vec![]; + + // Para 1 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(1 as u8), + persisted_validation_data_hash: [42u8; 32].into(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed: BackedCandidate = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(0 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(0 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + all_backed_candidates_with_core.push((backed, CoreIndex(0))); + } + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(1), + relay_parent, + pov_hash: Hash::repeat_byte(2 as u8), + persisted_validation_data_hash: [42u8; 32].into(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(1 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(1 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + all_backed_candidates_with_core.push((backed, CoreIndex(1))); + } + } + + // Para 2 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(2), + relay_parent, + pov_hash: Hash::repeat_byte(3 as u8), + persisted_validation_data_hash: [42u8; 32].into(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(2 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(2 as u32)), + ); + backed_candidates.push(backed.clone()); + if core_index_enabled { + all_backed_candidates_with_core.push((backed, CoreIndex(2))); + } + } + + // Para 3 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(3), + relay_parent, + pov_hash: Hash::repeat_byte(4 as u8), + persisted_validation_data_hash: [42u8; 32].into(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(4 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(4 as u32)), + ); + backed_candidates.push(backed.clone()); + all_backed_candidates_with_core.push((backed, CoreIndex(4))); + } + + // Para 4 + { + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(4), + relay_parent, + pov_hash: Hash::repeat_byte(5 as u8), + persisted_validation_data_hash: [42u8; 32].into(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + None, + ); + backed_candidates.push(backed.clone()); + all_backed_candidates_with_core.push((backed, CoreIndex(5))); + + let mut candidate = TestCandidateBuilder { + para_id: ParaId::from(4), + relay_parent, + pov_hash: Hash::repeat_byte(6 as u8), + persisted_validation_data_hash: [42u8; 32].into(), + hrmp_watermark: RELAY_PARENT_NUM, + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(5 as u32)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + core_index_enabled.then_some(CoreIndex(5 as u32)), + ); + backed_candidates.push(backed.clone()); + } + + // No candidate for para 5. + + // State sanity checks + assert_eq!( + >::scheduled_paras().collect::>(), + vec![ + (CoreIndex(0), ParaId::from(1)), + (CoreIndex(1), ParaId::from(1)), + (CoreIndex(2), ParaId::from(2)), + (CoreIndex(3), ParaId::from(2)), + (CoreIndex(4), ParaId::from(3)), + (CoreIndex(5), ParaId::from(4)), + (CoreIndex(6), ParaId::from(5)), + ] + ); + let mut scheduled: BTreeMap> = BTreeMap::new(); + for (core_idx, para_id) in >::scheduled_paras() { + scheduled.entry(para_id).or_default().insert(core_idx); + } + + assert_eq!( + shared::Pallet::::active_validator_indices(), + vec![ + ValidatorIndex(0), + ValidatorIndex(1), + ValidatorIndex(2), + ValidatorIndex(3), + ValidatorIndex(4), + ValidatorIndex(5), + ValidatorIndex(6), + ] + ); + + TestData { + backed_candidates, + scheduled_paras: scheduled, + all_backed_candidates_with_core, + } } - #[test] - fn happy_path() { + #[rstest] + #[case(false)] + #[case(true)] + fn happy_path(#[case] core_index_enabled: bool) { new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { backed_candidates, scheduled_paras: scheduled } = get_test_data(); + let TestData { + backed_candidates, + all_backed_candidates_with_core, + scheduled_paras: scheduled, + } = get_test_data(core_index_enabled); let has_concluded_invalid = |_idx: usize, _backed_candidate: &BackedCandidate| -> bool { false }; @@ -1385,47 +1899,95 @@ mod sanitizers { backed_candidates.clone(), &>::allowed_relay_parents(), has_concluded_invalid, - &scheduled + scheduled, + core_index_enabled ), SanitizedBackedCandidates { - backed_candidates, - votes_from_disabled_were_dropped: false + backed_candidates_with_core: all_backed_candidates_with_core, + votes_from_disabled_were_dropped: false, + dropped_unscheduled_candidates: false } ); + }); + } - {} + #[rstest] + #[case(false)] + #[case(true)] + fn test_with_multiple_cores_per_para(#[case] core_index_enabled: bool) { + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + let TestData { + backed_candidates, + all_backed_candidates_with_core: expected_all_backed_candidates_with_core, + scheduled_paras: scheduled, + } = get_test_data_multiple_cores_per_para(core_index_enabled); + + let has_concluded_invalid = + |_idx: usize, _backed_candidate: &BackedCandidate| -> bool { false }; + + assert_eq!( + sanitize_backed_candidates::( + backed_candidates.clone(), + &>::allowed_relay_parents(), + has_concluded_invalid, + scheduled, + core_index_enabled + ), + SanitizedBackedCandidates { + backed_candidates_with_core: expected_all_backed_candidates_with_core, + votes_from_disabled_were_dropped: false, + dropped_unscheduled_candidates: true + } + ); }); } // nothing is scheduled, so no paraids match, thus all backed candidates are skipped - #[test] - fn nothing_scheduled() { + #[rstest] + #[case(false, false)] + #[case(true, true)] + #[case(false, true)] + #[case(true, false)] + fn nothing_scheduled( + #[case] core_index_enabled: bool, + #[case] multiple_cores_per_para: bool, + ) { new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { backed_candidates, scheduled_paras: _ } = get_test_data(); - let scheduled = &BTreeMap::new(); + let TestData { backed_candidates, .. } = if multiple_cores_per_para { + get_test_data_multiple_cores_per_para(core_index_enabled) + } else { + get_test_data(core_index_enabled) + }; + let scheduled = BTreeMap::new(); let has_concluded_invalid = |_idx: usize, _backed_candidate: &BackedCandidate| -> bool { false }; let SanitizedBackedCandidates { - backed_candidates: sanitized_backed_candidates, + backed_candidates_with_core: sanitized_backed_candidates, votes_from_disabled_were_dropped, + dropped_unscheduled_candidates, } = sanitize_backed_candidates::( backed_candidates.clone(), &>::allowed_relay_parents(), has_concluded_invalid, - &scheduled, + scheduled, + core_index_enabled, ); assert!(sanitized_backed_candidates.is_empty()); assert!(!votes_from_disabled_were_dropped); + assert!(dropped_unscheduled_candidates); }); } // candidates that have concluded as invalid are filtered out - #[test] - fn invalid_are_filtered_out() { + #[rstest] + #[case(false)] + #[case(true)] + fn invalid_are_filtered_out(#[case] core_index_enabled: bool) { new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { backed_candidates, scheduled_paras: scheduled } = get_test_data(); + let TestData { backed_candidates, scheduled_paras: scheduled, .. } = + get_test_data(core_index_enabled); // mark every second one as concluded invalid let set = { @@ -1440,45 +2002,55 @@ mod sanitizers { let has_concluded_invalid = |_idx: usize, candidate: &BackedCandidate| set.contains(&candidate.hash()); let SanitizedBackedCandidates { - backed_candidates: sanitized_backed_candidates, + backed_candidates_with_core: sanitized_backed_candidates, votes_from_disabled_were_dropped, + dropped_unscheduled_candidates, } = sanitize_backed_candidates::( backed_candidates.clone(), &>::allowed_relay_parents(), has_concluded_invalid, - &scheduled, + scheduled, + core_index_enabled, ); assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2); assert!(!votes_from_disabled_were_dropped); + assert!(!dropped_unscheduled_candidates); }); } - #[test] - fn disabled_non_signing_validator_doesnt_get_filtered() { + #[rstest] + #[case(false)] + #[case(true)] + fn disabled_non_signing_validator_doesnt_get_filtered(#[case] core_index_enabled: bool) { new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { mut backed_candidates, scheduled_paras } = get_test_data(); + let TestData { mut all_backed_candidates_with_core, .. } = + get_test_data(core_index_enabled); // Disable Eve set_disabled_validators(vec![4]); - let before = backed_candidates.clone(); + let before = all_backed_candidates_with_core.clone(); // Eve is disabled but no backing statement is signed by it so nothing should be // filtered assert!(!filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + &mut all_backed_candidates_with_core, &>::allowed_relay_parents(), - &scheduled_paras + core_index_enabled )); - assert_eq!(backed_candidates, before); + assert_eq!(all_backed_candidates_with_core, before); }); } - - #[test] - fn drop_statements_from_disabled_without_dropping_candidate() { + #[rstest] + #[case(false)] + #[case(true)] + fn drop_statements_from_disabled_without_dropping_candidate( + #[case] core_index_enabled: bool, + ) { new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { mut backed_candidates, scheduled_paras } = get_test_data(); + let TestData { mut all_backed_candidates_with_core, .. } = + get_test_data(core_index_enabled); // Disable Alice set_disabled_validators(vec![0]); @@ -1491,61 +2063,83 @@ mod sanitizers { configuration::Pallet::::force_set_active_config(hc); // Verify the initial state is as expected - assert_eq!(backed_candidates.get(0).unwrap().validity_votes.len(), 2); - assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(0).unwrap(), - true - ); assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(1).unwrap(), - true + all_backed_candidates_with_core.get(0).unwrap().0.validity_votes().len(), + 2 ); - let untouched = backed_candidates.get(1).unwrap().clone(); + let (validator_indices, maybe_core_index) = all_backed_candidates_with_core + .get(0) + .unwrap() + .0 + .validator_indices_and_core_index(core_index_enabled); + if core_index_enabled { + assert!(maybe_core_index.is_some()); + } else { + assert!(maybe_core_index.is_none()); + } + + assert_eq!(validator_indices.get(0).unwrap(), true); + assert_eq!(validator_indices.get(1).unwrap(), true); + let untouched = all_backed_candidates_with_core.get(1).unwrap().0.clone(); assert!(filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + &mut all_backed_candidates_with_core, &>::allowed_relay_parents(), - &scheduled_paras + core_index_enabled )); + let (validator_indices, maybe_core_index) = all_backed_candidates_with_core + .get(0) + .unwrap() + .0 + .validator_indices_and_core_index(core_index_enabled); + if core_index_enabled { + assert!(maybe_core_index.is_some()); + } else { + assert!(maybe_core_index.is_none()); + } + // there should still be two backed candidates - assert_eq!(backed_candidates.len(), 2); + assert_eq!(all_backed_candidates_with_core.len(), 2); // but the first one should have only one validity vote - assert_eq!(backed_candidates.get(0).unwrap().validity_votes.len(), 1); - // Validator 0 vote should be dropped, validator 1 - retained - assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(0).unwrap(), - false - ); assert_eq!( - backed_candidates.get(0).unwrap().validator_indices.get(1).unwrap(), - true + all_backed_candidates_with_core.get(0).unwrap().0.validity_votes().len(), + 1 ); + // Validator 0 vote should be dropped, validator 1 - retained + assert_eq!(validator_indices.get(0).unwrap(), false); + assert_eq!(validator_indices.get(1).unwrap(), true); // the second candidate shouldn't be modified - assert_eq!(*backed_candidates.get(1).unwrap(), untouched); + assert_eq!(all_backed_candidates_with_core.get(1).unwrap().0, untouched); }); } - #[test] - fn drop_candidate_if_all_statements_are_from_disabled() { + #[rstest] + #[case(false)] + #[case(true)] + fn drop_candidate_if_all_statements_are_from_disabled(#[case] core_index_enabled: bool) { new_test_ext(MockGenesisConfig::default()).execute_with(|| { - let TestData { mut backed_candidates, scheduled_paras } = get_test_data(); + let TestData { mut all_backed_candidates_with_core, .. } = + get_test_data(core_index_enabled); // Disable Alice and Bob set_disabled_validators(vec![0, 1]); // Verify the initial state is as expected - assert_eq!(backed_candidates.get(0).unwrap().validity_votes.len(), 2); - let untouched = backed_candidates.get(1).unwrap().clone(); + assert_eq!( + all_backed_candidates_with_core.get(0).unwrap().0.validity_votes().len(), + 2 + ); + let untouched = all_backed_candidates_with_core.get(1).unwrap().0.clone(); assert!(filter_backed_statements_from_disabled_validators::( - &mut backed_candidates, + &mut all_backed_candidates_with_core, &>::allowed_relay_parents(), - &scheduled_paras + core_index_enabled )); - assert_eq!(backed_candidates.len(), 1); - assert_eq!(*backed_candidates.get(0).unwrap(), untouched); + assert_eq!(all_backed_candidates_with_core.len(), 1); + assert_eq!(all_backed_candidates_with_core.get(0).unwrap().0, untouched); }); } } diff --git a/polkadot/runtime/parachains/src/paras_inherent/weights.rs b/polkadot/runtime/parachains/src/paras_inherent/weights.rs index 05cc53fae04652c188d62b57fa5281aa6bb88e22..0f4e5be572a66d16c1476ada7671c495a27bbc83 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/weights.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/weights.rs @@ -149,11 +149,11 @@ pub fn backed_candidate_weight( candidate: &BackedCandidate, ) -> Weight { set_proof_size_to_tx_size( - if candidate.candidate.commitments.new_validation_code.is_some() { + if candidate.candidate().commitments.new_validation_code.is_some() { <::WeightInfo as WeightInfo>::enter_backed_candidate_code_upgrade() } else { <::WeightInfo as WeightInfo>::enter_backed_candidates_variable( - candidate.validity_votes.len() as u32, + candidate.validity_votes().len() as u32, ) }, candidate, diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs index b3a060e1cb8a05439de7f48fdd6ff1a84c554496..1bbd4dfb716f1f541b40bca3e3a434b21c5a7a81 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v7.rs @@ -45,6 +45,8 @@ pub fn validators() -> Vec { /// Implementation for the `validator_groups` function of the runtime API. pub fn validator_groups( ) -> (Vec>, GroupRotationInfo>) { + // This formula needs to be the same as the one we use + // when populating group_responsible in `availability_cores` let now = >::block_number() + One::one(); let groups = >::validator_groups(); @@ -95,6 +97,11 @@ pub fn availability_cores() -> Vec>::next_up_on_available(CoreIndex( i as u32, @@ -106,7 +113,7 @@ pub fn availability_cores() -> Vec), } - /// Conveninece type alias for `CoreOccupied`. + /// Convenience type alias for `CoreOccupied`. pub type CoreOccupiedType = CoreOccupied>; impl CoreOccupied { @@ -145,9 +145,7 @@ pub mod pallet { pub(crate) type SessionStartBlock = StorageValue<_, BlockNumberFor, ValueQuery>; /// One entry for each availability core. The `VecDeque` represents the assignments to be - /// scheduled on that core. `None` is used to signal to not schedule the next para of the core - /// as there is one currently being scheduled. Not using `None` here would overwrite the - /// `CoreState` in the runtime API. The value contained here will not be valid after the end of + /// scheduled on that core. The value contained here will not be valid after the end of /// a block. Runtime APIs should be used to determine scheduled cores/ for the upcoming block. #[pallet::storage] #[pallet::getter(fn claimqueue)] @@ -224,7 +222,7 @@ impl Pallet { let n_cores = core::cmp::max( T::AssignmentProvider::session_core_count(), - match config.max_validators_per_core { + match config.scheduler_params.max_validators_per_core { Some(x) if x != 0 => validators.len() as u32 / x, _ => 0, }, @@ -352,6 +350,7 @@ impl Pallet { fn drop_expired_claims_from_claimqueue() { let now = >::block_number(); let availability_cores = AvailabilityCores::::get(); + let ttl = >::config().scheduler_params.ttl; ClaimQueue::::mutate(|cq| { for (idx, _) in (0u32..).zip(availability_cores) { @@ -384,8 +383,6 @@ impl Pallet { if let Some(assignment) = T::AssignmentProvider::pop_assignment_for_core(core_idx) { - let AssignmentProviderConfig { ttl, .. } = - T::AssignmentProvider::get_provider_config(core_idx); core_claimqueue.push_back(ParasEntry::new(assignment, now + ttl)); } } @@ -430,7 +427,7 @@ impl Pallet { } let rotations_since_session_start: BlockNumberFor = - (at - session_start_block) / config.group_rotation_frequency; + (at - session_start_block) / config.scheduler_params.group_rotation_frequency; let rotations_since_session_start = as TryInto>::try_into(rotations_since_session_start) @@ -462,9 +459,9 @@ impl Pallet { // Note: blocks backed in this rotation will never time out here as backed_in + // config.paras_availability_period will always be > now for these blocks, as // otherwise above condition would not be true. - pending_since + config.paras_availability_period + pending_since + config.scheduler_params.paras_availability_period } else { - next_rotation + config.paras_availability_period + next_rotation + config.scheduler_params.paras_availability_period }; AvailabilityTimeoutStatus { timed_out: time_out_at <= now, live_until: time_out_at } @@ -480,7 +477,8 @@ impl Pallet { let now = >::block_number() + One::one(); let rotation_info = Self::group_rotation_info(now); - let current_window = rotation_info.last_rotation_at() + config.paras_availability_period; + let current_window = + rotation_info.last_rotation_at() + config.scheduler_params.paras_availability_period; now < current_window } @@ -490,7 +488,7 @@ impl Pallet { ) -> GroupRotationInfo> { let session_start_block = Self::session_start_block(); let group_rotation_frequency = - >::config().group_rotation_frequency; + >::config().scheduler_params.group_rotation_frequency; GroupRotationInfo { session_start_block, now, group_rotation_frequency } } @@ -510,6 +508,8 @@ impl Pallet { /// Return the next thing that will be scheduled on this core assuming it is currently /// occupied and the candidate occupying it times out. pub(crate) fn next_up_on_time_out(core: CoreIndex) -> Option { + let max_availability_timeouts = + >::config().scheduler_params.max_availability_timeouts; Self::next_up_on_available(core).or_else(|| { // Or, if none, the claim currently occupying the core, // as it would be put back on the queue after timing out if number of retries is not at @@ -517,16 +517,12 @@ impl Pallet { let cores = AvailabilityCores::::get(); cores.get(core.0 as usize).and_then(|c| match c { CoreOccupied::Free => None, - CoreOccupied::Paras(pe) => { - let AssignmentProviderConfig { max_availability_timeouts, .. } = - T::AssignmentProvider::get_provider_config(core); - + CoreOccupied::Paras(pe) => if pe.availability_timeouts < max_availability_timeouts { Some(Self::paras_entry_to_scheduled_core(pe)) } else { None - } - }, + }, }) }) } @@ -568,7 +564,7 @@ impl Pallet { // ClaimQueue related functions // fn claimqueue_lookahead() -> u32 { - >::config().scheduling_lookahead + >::config().scheduler_params.lookahead } /// Frees cores and fills the free claimqueue spots by popping from the `AssignmentProvider`. @@ -587,15 +583,15 @@ impl Pallet { let n_lookahead = Self::claimqueue_lookahead().max(1); let n_session_cores = T::AssignmentProvider::session_core_count(); let cq = ClaimQueue::::get(); - let ttl = >::config().on_demand_ttl; + let config = >::config(); + let max_availability_timeouts = config.scheduler_params.max_availability_timeouts; + let ttl = config.scheduler_params.ttl; for core_idx in 0..n_session_cores { let core_idx = CoreIndex::from(core_idx); // add previously timedout paras back into the queue if let Some(mut entry) = timedout_paras.remove(&core_idx) { - let AssignmentProviderConfig { max_availability_timeouts, .. } = - T::AssignmentProvider::get_provider_config(core_idx); if entry.availability_timeouts < max_availability_timeouts { // Increment the timeout counter. entry.availability_timeouts += 1; @@ -670,13 +666,6 @@ impl Pallet { .filter_map(|(core_idx, v)| v.front().map(|e| (core_idx, e.assignment.para_id()))) } - #[cfg(any(feature = "runtime-benchmarks", test))] - pub(crate) fn assignment_provider_config( - core_idx: CoreIndex, - ) -> AssignmentProviderConfig> { - T::AssignmentProvider::get_provider_config(core_idx) - } - #[cfg(any(feature = "try-runtime", test))] fn claimqueue_len() -> usize { ClaimQueue::::get().iter().map(|la_vec| la_vec.1.len()).sum() diff --git a/polkadot/runtime/parachains/src/scheduler/common.rs b/polkadot/runtime/parachains/src/scheduler/common.rs index 2eb73385803c6e62a88fc55526e3ba18218cf06d..66a4e6d30be0830650295d5311b7d7e1fc3b1d20 100644 --- a/polkadot/runtime/parachains/src/scheduler/common.rs +++ b/polkadot/runtime/parachains/src/scheduler/common.rs @@ -48,22 +48,10 @@ impl Assignment { } } -#[derive(Encode, Decode, TypeInfo)] -/// A set of variables required by the scheduler in order to operate. -pub struct AssignmentProviderConfig { - /// How many times a collation can time out on availability. - /// Zero timeouts still means that a collation can be provided as per the slot auction - /// assignment provider. - pub max_availability_timeouts: u32, - - /// How long the collator has to provide a collation to the backing group before being dropped. - pub ttl: BlockNumber, -} - pub trait AssignmentProvider { /// Pops an [`Assignment`] from the provider for a specified [`CoreIndex`]. /// - /// This is where assignments come into existance. + /// This is where assignments come into existence. fn pop_assignment_for_core(core_idx: CoreIndex) -> Option; /// A previously popped `Assignment` has been fully processed. @@ -77,14 +65,11 @@ pub trait AssignmentProvider { /// Push back a previously popped assignment. /// /// If the assignment could not be processed within the current session, it can be pushed back - /// to the assignment provider in order to be poppped again later. + /// to the assignment provider in order to be popped again later. /// /// This is the second way the life of an assignment can come to an end. fn push_back_assignment(assignment: Assignment); - /// Returns a set of variables needed by the scheduler - fn get_provider_config(core_idx: CoreIndex) -> AssignmentProviderConfig; - /// Push some assignment for mocking/benchmarks purposes. /// /// Useful for benchmarks and testing. The returned assignment is "valid" and can if need be diff --git a/polkadot/runtime/parachains/src/scheduler/tests.rs b/polkadot/runtime/parachains/src/scheduler/tests.rs index 9af23ce64bd67ab0901dd1a03e849d51cfffe342..28b3a6b4f5d61cd2e1934b6fa723e5abe5c8bd4b 100644 --- a/polkadot/runtime/parachains/src/scheduler/tests.rs +++ b/polkadot/runtime/parachains/src/scheduler/tests.rs @@ -18,7 +18,9 @@ use super::*; use frame_support::assert_ok; use keyring::Sr25519Keyring; -use primitives::{BlockNumber, SessionIndex, ValidationCode, ValidatorId}; +use primitives::{ + vstaging::SchedulerParams, BlockNumber, SessionIndex, ValidationCode, ValidatorId, +}; use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use crate::{ @@ -103,15 +105,19 @@ fn run_to_end_of_block( fn default_config() -> HostConfiguration { HostConfiguration { - coretime_cores: 3, - group_rotation_frequency: 10, - paras_availability_period: 3, - scheduling_lookahead: 2, // This field does not affect anything that scheduler does. However, `HostConfiguration` // is still a subject to consistency test. It requires that // `minimum_validation_upgrade_delay` is greater than `chain_availability_period` and // `thread_availability_period`. minimum_validation_upgrade_delay: 6, + scheduler_params: SchedulerParams { + group_rotation_frequency: 10, + paras_availability_period: 3, + lookahead: 2, + num_cores: 3, + max_availability_timeouts: 1, + ..Default::default() + }, ..Default::default() } } @@ -155,7 +161,7 @@ fn scheduled_entries() -> impl Iterator(vec![para_a])); } else { assert!(!claimqueue_contains_para_ids::(vec![para_a])); @@ -822,7 +825,7 @@ fn on_demand_claims_are_pruned_after_timing_out() { assert!(!availability_cores_contains_para_ids::(vec![para_a])); // #25 - now += max_retries + 2; + now += max_timeouts + 2; // Add assignment back to the mix. MockAssigner::add_test_assignment(assignment_a.clone()); @@ -833,7 +836,7 @@ fn on_demand_claims_are_pruned_after_timing_out() { // #26 now += 1; // Run to block #n but this time have group 1 conclude the availabilty. - for n in now..=(now + max_retries + 1) { + for n in now..=(now + max_timeouts + 1) { // #n run_to_block(n, |_| None); // Time out core 0 if group 0 is assigned to it, if group 1 is assigned, conclude. @@ -874,10 +877,8 @@ fn on_demand_claims_are_pruned_after_timing_out() { fn availability_predicate_works() { let genesis_config = genesis_config(&default_config()); - let HostConfiguration { group_rotation_frequency, paras_availability_period, .. } = - default_config(); - - assert!(paras_availability_period < group_rotation_frequency); + let SchedulerParams { group_rotation_frequency, paras_availability_period, .. } = + default_config().scheduler_params; new_test_ext(genesis_config).execute_with(|| { run_to_block(1 + paras_availability_period, |_| None); @@ -1044,7 +1045,7 @@ fn next_up_on_time_out_reuses_claim_if_nothing_queued() { #[test] fn session_change_requires_reschedule_dropping_removed_paras() { let mut config = default_config(); - config.scheduling_lookahead = 1; + config.scheduler_params.lookahead = 1; let genesis_config = genesis_config(&config); let para_a = ParaId::from(1_u32); @@ -1056,7 +1057,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { new_test_ext(genesis_config).execute_with(|| { // Setting explicit core count MockAssigner::set_core_count(5); - let assignment_provider_ttl = MockAssigner::get_provider_config(CoreIndex::from(0)).ttl; + let coretime_ttl = >::config().scheduler_params.ttl; schedule_blank_para(para_a); schedule_blank_para(para_b); @@ -1120,7 +1121,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { vec![ParasEntry::new( Assignment::Bulk(para_a), // At end of block 2 - assignment_provider_ttl + 2 + coretime_ttl + 2 )] .into_iter() .collect() @@ -1169,7 +1170,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { vec![ParasEntry::new( Assignment::Bulk(para_a), // At block 3 - assignment_provider_ttl + 3 + coretime_ttl + 3 )] .into_iter() .collect() @@ -1179,7 +1180,7 @@ fn session_change_requires_reschedule_dropping_removed_paras() { vec![ParasEntry::new( Assignment::Bulk(para_b), // At block 3 - assignment_provider_ttl + 3 + coretime_ttl + 3 )] .into_iter() .collect() diff --git a/polkadot/runtime/parachains/src/session_info/migration.rs b/polkadot/runtime/parachains/src/session_info/migration.rs index 228c1e3bb2515b14f5fe59a89578b03be065955d..ea6f81834b5db6eb7336521d3d471a6c3df49985 100644 --- a/polkadot/runtime/parachains/src/session_info/migration.rs +++ b/polkadot/runtime/parachains/src/session_info/migration.rs @@ -18,5 +18,5 @@ use frame_support::traits::StorageVersion; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); diff --git a/polkadot/runtime/parachains/src/session_info/tests.rs b/polkadot/runtime/parachains/src/session_info/tests.rs index 92a50575deda8413abb18f892680d693c148d0cb..a5bfeae0745599c00de718684420fc377a9184f1 100644 --- a/polkadot/runtime/parachains/src/session_info/tests.rs +++ b/polkadot/runtime/parachains/src/session_info/tests.rs @@ -25,7 +25,7 @@ use crate::{ util::take_active_subset, }; use keyring::Sr25519Keyring; -use primitives::{BlockNumber, ValidatorId, ValidatorIndex}; +use primitives::{vstaging::SchedulerParams, BlockNumber, ValidatorId, ValidatorIndex}; fn run_to_block( to: BlockNumber, @@ -62,9 +62,9 @@ fn run_to_block( fn default_config() -> HostConfiguration { HostConfiguration { - coretime_cores: 1, dispute_period: 2, needed_approvals: 3, + scheduler_params: SchedulerParams { num_cores: 1, ..Default::default() }, ..Default::default() } } diff --git a/polkadot/runtime/parachains/src/ump_tests.rs b/polkadot/runtime/parachains/src/ump_tests.rs index 426993ffa65a73b83d4077ee31b64217c41fb168..2ed1d64336b31e42d1fd0363143a7d17a66c7b97 100644 --- a/polkadot/runtime/parachains/src/ump_tests.rs +++ b/polkadot/runtime/parachains/src/ump_tests.rs @@ -31,8 +31,7 @@ use frame_support::{ weights::Weight, }; use primitives::{well_known_keys, Id as ParaId, UpwardMessage}; -use sp_core::twox_64; -use sp_io::hashing::blake2_256; +use sp_crypto_hashing::{blake2_256, twox_64}; use sp_runtime::traits::Bounded; use sp_std::prelude::*; @@ -505,6 +504,10 @@ fn overweight_queue_works() { let a_msg_2 = (501u32, "a_msg_2").encode(); let a_msg_3 = (501u32, "a_msg_3").encode(); + let hash_1 = blake2_256(&a_msg_1[..]); + let hash_2 = blake2_256(&a_msg_2[..]); + let hash_3 = blake2_256(&a_msg_3[..]); + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { // HACK: Start with the block number 1. This is needed because should an event be // emitted during the genesis block they will be implicitly wiped. @@ -517,9 +520,6 @@ fn overweight_queue_works() { queue_upward_msg(para_a, a_msg_3.clone()); MessageQueue::service_queues(Weight::from_parts(500, 500)); - let hash_1 = blake2_256(&a_msg_1[..]); - let hash_2 = blake2_256(&a_msg_2[..]); - let hash_3 = blake2_256(&a_msg_3[..]); assert_last_events( [ pallet_message_queue::Event::::Processed { diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 992cc0b708afa2114aa9e71433cc2d87295c9e01..61ca1635ac9567cfdebbb05c4e92c97489387a91 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rococo-runtime" build = "build.rs" -version = "1.0.0" +version = "7.0.0" description = "Rococo testnet Relay Chain runtime." authors.workspace = true edition.workspace = true @@ -13,9 +13,9 @@ workspace = true [dependencies] parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } -serde = { version = "1.0.195", default-features = false } -serde_derive = { version = "1.0.117", optional = true } +log = { workspace = true } +serde = { workspace = true } +serde_derive = { optional = true, workspace = true } static_assertions = "1.1.0" smallvec = "1.8.0" @@ -111,7 +111,7 @@ keyring = { package = "sp-keyring", path = "../../../substrate/primitives/keyrin remote-externalities = { package = "frame-remote-externalities", path = "../../../substrate/utils/frame/remote-externalities" } sp-trie = { path = "../../../substrate/primitives/trie" } separator = "0.4.1" -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } sp-tracing = { path = "../../../substrate/primitives/tracing", default-features = false } tokio = { version = "1.24.2", features = ["macros"] } @@ -246,6 +246,7 @@ runtime-benchmarks = [ "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-tips/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", @@ -322,5 +323,5 @@ runtime-metrics = ["runtime-parachains/runtime-metrics", "sp-io/with-tracing"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] diff --git a/polkadot/runtime/rococo/constants/Cargo.toml b/polkadot/runtime/rococo/constants/Cargo.toml index 1e6b0a5f903c7880b2e69665a700ee1c2dcaeb5b..3ca3877a7650e453c25851ddb35899478a67faeb 100644 --- a/polkadot/runtime/rococo/constants/Cargo.toml +++ b/polkadot/runtime/rococo/constants/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rococo-runtime-constants" -version = "1.0.0" +version = "7.0.0" description = "Constants used throughout the Rococo network." authors.workspace = true edition.workspace = true diff --git a/polkadot/runtime/rococo/constants/src/weights/block_weights.rs b/polkadot/runtime/rococo/constants/src/weights/block_weights.rs index e2aa4a6cab7febc90418c3222819109e256a18c8..f7dc2f19316d5e6b13deb76044fb0ebf43b25d3b 100644 --- a/polkadot/runtime/rococo/constants/src/weights/block_weights.rs +++ b/polkadot/runtime/rococo/constants/src/weights/block_weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26 (Y/M/D) -//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29 (Y/M/D) +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! //! SHORT-NAME: `block`, LONG-NAME: `BlockExecution`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` -//! WEIGHT-PATH: `runtime/rococo/constants/src/weights/` +//! WEIGHT-PATH: `./polkadot/runtime/rococo/constants/src/weights/` //! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1.0`, WEIGHT-ADD: `0` // Executed Command: @@ -28,12 +28,11 @@ // benchmark // overhead // --chain=rococo-dev -// --execution=wasm // --wasm-execution=compiled -// --weight-path=runtime/rococo/constants/src/weights/ +// --weight-path=./polkadot/runtime/rococo/constants/src/weights/ // --warmup=10 // --repeat=100 -// --header=./file_header.txt +// --header=./polkadot/file_header.txt use sp_core::parameter_types; use sp_weights::{constants::WEIGHT_REF_TIME_PER_NANOS, Weight}; @@ -43,17 +42,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 408_659, 450_716 - /// Average: 417_412 - /// Median: 411_177 - /// Std-Dev: 12242.31 + /// Min, Max: 440_142, 476_907 + /// Average: 450_240 + /// Median: 448_633 + /// Std-Dev: 7301.18 /// /// Percentiles nanoseconds: - /// 99th: 445_142 - /// 95th: 442_275 - /// 75th: 414_217 + /// 99th: 470_733 + /// 95th: 465_082 + /// 75th: 452_536 pub const BlockExecutionWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(417_412), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(450_240), 0); } #[cfg(test)] diff --git a/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs b/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs index adce840ebbc120a4e7905ad6312b9d27b1e8d3fb..000cee8a237c3ef306e3356f9733422765b67306 100644 --- a/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs +++ b/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26 (Y/M/D) -//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29 (Y/M/D) +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! //! SHORT-NAME: `extrinsic`, LONG-NAME: `ExtrinsicBase`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` -//! WEIGHT-PATH: `runtime/rococo/constants/src/weights/` +//! WEIGHT-PATH: `./polkadot/runtime/rococo/constants/src/weights/` //! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1.0`, WEIGHT-ADD: `0` // Executed Command: @@ -28,12 +28,11 @@ // benchmark // overhead // --chain=rococo-dev -// --execution=wasm // --wasm-execution=compiled -// --weight-path=runtime/rococo/constants/src/weights/ +// --weight-path=./polkadot/runtime/rococo/constants/src/weights/ // --warmup=10 // --repeat=100 -// --header=./file_header.txt +// --header=./polkadot/file_header.txt use sp_core::parameter_types; use sp_weights::{constants::WEIGHT_REF_TIME_PER_NANOS, Weight}; @@ -43,17 +42,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 97_574, 100_119 - /// Average: 98_236 - /// Median: 98_179 - /// Std-Dev: 394.9 + /// Min, Max: 92_961, 94_143 + /// Average: 93_369 + /// Median: 93_331 + /// Std-Dev: 217.39 /// /// Percentiles nanoseconds: - /// 99th: 99_893 - /// 95th: 98_850 - /// 75th: 98_318 + /// 99th: 93_848 + /// 95th: 93_691 + /// 75th: 93_514 pub const ExtrinsicBaseWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(98_236), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(93_369), 0); } #[cfg(test)] diff --git a/polkadot/runtime/rococo/src/governance/fellowship.rs b/polkadot/runtime/rococo/src/governance/fellowship.rs index b5df6cf2df341c5563f31ea6f49f1eeae5b81fc1..a589b768afde2c0757e74a6535509228f1613a82 100644 --- a/polkadot/runtime/rococo/src/governance/fellowship.rs +++ b/polkadot/runtime/rococo/src/governance/fellowship.rs @@ -17,7 +17,7 @@ //! Elements of governance concerning the Rococo Fellowship. use frame_support::traits::{MapSuccess, TryMapSuccess}; -use sp_runtime::traits::{CheckedReduceBy, ConstU16, Replace}; +use sp_runtime::traits::{CheckedReduceBy, ConstU16, Replace, ReplaceWithDefault}; use super::*; use crate::{CENTS, DAYS}; @@ -315,6 +315,11 @@ pub type FellowshipCollectiveInstance = pallet_ranked_collective::Instance1; impl pallet_ranked_collective::Config for Runtime { type WeightInfo = weights::pallet_ranked_collective::WeightInfo; type RuntimeEvent = RuntimeEvent; + // Adding is by any of: + // - Root. + // - the FellowshipAdmin origin. + // - a Fellowship origin. + type AddOrigin = MapSuccess>; // Promotion is by any of: // - Root can demote arbitrarily. // - the FellowshipAdmin origin (i.e. token holder referendum); @@ -326,6 +331,11 @@ impl pallet_ranked_collective::Config for Runtime TryMapSuccess>>, >, >; + // Removing is by any of: + // - Root can remove arbitrarily. + // - the FellowshipAdmin origin (i.e. token holder referendum); + // - a vote by the rank two above the current rank. + type RemoveOrigin = Self::DemoteOrigin; // Demotion is by any of: // - Root can demote arbitrarily. // - the FellowshipAdmin origin (i.e. token holder referendum); @@ -337,7 +347,15 @@ impl pallet_ranked_collective::Config for Runtime TryMapSuccess>>, >, >; + // Exchange is by any of: + // - Root can exchange arbitrarily. + // - the Fellows origin; + type ExchangeOrigin = + EitherOf>, Fellows>; type Polls = FellowshipReferenda; type MinRankOfClass = sp_runtime::traits::Identity; + type MemberSwappedHandler = (); type VoteWeight = pallet_ranked_collective::Geometric; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = (); } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 2d49f0b99e951156ee555fd3fd41861466529436..ff54c7776701c1ccaa512518e0470655fcd0afde 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -46,9 +46,8 @@ use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*}; use runtime_parachains::{ assigner_coretime as parachains_assigner_coretime, - assigner_on_demand as parachains_assigner_on_demand, - assigner_parachains as parachains_assigner_parachains, - configuration as parachains_configuration, coretime, disputes as parachains_disputes, + assigner_on_demand as parachains_assigner_on_demand, configuration as parachains_configuration, + coretime, disputes as parachains_disputes, disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, inclusion::{AggregateMessageOrigin, UmpQueueId}, @@ -150,7 +149,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("rococo"), impl_name: create_runtime_str!("parity-rococo-v2.0"), authoring_version: 0, - spec_version: 1_006_002, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 24, @@ -200,6 +199,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type MaxConsumers = frame_support::traits::ConstU32<16>; } @@ -309,12 +309,11 @@ impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; - type WeightInfo = weights::pallet_balances::WeightInfo; + type WeightInfo = weights::pallet_balances_balances::WeightInfo; type FreezeIdentifier = (); - type MaxFreezes = ConstU32<1>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = ConstU32<2>; + type MaxFreezes = ConstU32<1>; } parameter_types! { @@ -331,6 +330,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -593,7 +593,7 @@ where // so the actual block number is `n`. .saturating_sub(1); let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -605,16 +605,17 @@ where frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); - let raw_payload = SignedPayload::new(call, extra) + ) + .into(); + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; - let (call, extra, _) = raw_payload.deconstruct(); + let (call, tx_ext, _) = raw_payload.deconstruct(); let address = ::Lookup::unlookup(account); - Some((call, (address, signature, extra))) + Some((call, (address, signature, tx_ext))) } } @@ -635,12 +636,32 @@ parameter_types! { pub Prefix: &'static [u8] = b"Pay ROCs to the Rococo account:"; } +#[cfg(feature = "runtime-benchmarks")] +pub struct ClaimsHelper; + +#[cfg(feature = "runtime-benchmarks")] +use frame_support::dispatch::DispatchInfo; + +#[cfg(feature = "runtime-benchmarks")] +impl claims::BenchmarkHelperTrait for ClaimsHelper { + fn default_call_and_info() -> (RuntimeCall, DispatchInfo) { + use frame_support::dispatch::GetDispatchInfo; + let call = RuntimeCall::Claims(claims::Call::attest { + statement: claims::StatementKind::Regular.to_text().to_vec(), + }); + let info = call.get_dispatch_info(); + (call, info) + } +} + impl claims::Config for Runtime { type RuntimeEvent = RuntimeEvent; type VestingSchedule = Vesting; type Prefix = Prefix; type MoveClaimOrigin = EnsureRoot; type WeightInfo = weights::runtime_common_claims::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ClaimsHelper; } parameter_types! { @@ -1039,8 +1060,6 @@ impl parachains_assigner_on_demand::Config for Runtime { type WeightInfo = weights::runtime_parachains_assigner_on_demand::WeightInfo; } -impl parachains_assigner_parachains::Config for Runtime {} - impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { @@ -1167,7 +1186,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<2>; type MaxFreezes = ConstU32<1>; } @@ -1331,13 +1349,6 @@ construct_runtime! { Offences: pallet_offences = 7, Historical: session_historical = 34, - // BEEFY Bridges support. - Beefy: pallet_beefy = 240, - // MMR leaf construction must be before session in order to have leaf contents - // refer to block consistently. see substrate issue #11797 for details. - Mmr: pallet_mmr = 241, - MmrLeaf: pallet_beefy_mmr = 242, - Session: pallet_session = 8, Grandpa: pallet_grandpa = 10, AuthorityDiscovery: pallet_authority_discovery = 12, @@ -1410,7 +1421,6 @@ construct_runtime! { ParasSlashing: parachains_slashing = 63, MessageQueue: pallet_message_queue = 64, OnDemandAssignmentProvider: parachains_assigner_on_demand = 66, - ParachainsAssignmentProvider: parachains_assigner_parachains = 67, CoretimeAssignmentProvider: parachains_assigner_coretime = 68, // Parachain Onboarding Pallets. Start indices at 70 to leave room. @@ -1423,6 +1433,13 @@ construct_runtime! { // Pallet for sending XCM. XcmPallet: pallet_xcm = 99, + // BEEFY Bridges support. + Beefy: pallet_beefy = 240, + // MMR leaf construction must be after session in order to have a leaf's next_auth_set + // refer to block. See issue polkadot-fellows/runtimes#160 for details. + Mmr: pallet_mmr = 241, + MmrLeaf: pallet_beefy_mmr = 242, + // Pallet for migrating Identity to a parachain. To be removed post-migration. IdentityMigrator: identity_migrator = 248, @@ -1453,8 +1470,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// `BlockId` type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `SignedExtension` to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -1467,7 +1484,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// All migrations that will run on the next runtime upgrade. /// @@ -1664,6 +1681,10 @@ pub mod migrations { 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, + + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); } @@ -1677,7 +1698,7 @@ pub type Executive = frame_executive::Executive< Migrations, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; parameter_types! { // The deposit configuration for the singed migration. Specially if you want to allow any signed account to do the migration (see `SignedFilter`, these deposits should be high) @@ -1689,6 +1710,7 @@ parameter_types! { impl pallet_state_trie_migration::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; type SignedDepositPerItem = MigrationSignedDepositPerItem; type SignedDepositBase = MigrationSignedDepositBase; type ControlOrigin = EnsureRoot; @@ -1747,7 +1769,9 @@ mod benches { [pallet_scheduler, Scheduler] [pallet_sudo, Sudo] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_treasury, Treasury] [pallet_utility, Utility] [pallet_vesting, Vesting] @@ -1770,7 +1794,7 @@ sp_api::impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1822,6 +1846,12 @@ sp_api::impl_runtime_apis! { impl offchain_primitives::OffchainWorkerApi for Runtime { fn offchain_worker(header: &::Header) { + use sp_runtime::{traits::Header, DigestItem}; + + if header.digest().logs().iter().any(|di| di == &DigestItem::RuntimeEnvironmentUpdated) { + pallet_im_online::migration::clear_offchain_storage(Session::validators().len() as u32); + } + Executive::offchain_worker(header) } } @@ -2235,6 +2265,7 @@ sp_api::impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use frame_benchmarking::baseline::Pallet as Baseline; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -2255,6 +2286,7 @@ sp_api::impl_runtime_apis! { use frame_support::traits::WhitelistedStorageKeys; use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use frame_benchmarking::baseline::Pallet as Baseline; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use sp_storage::TrackedStorageKey; @@ -2268,12 +2300,30 @@ sp_api::impl_runtime_apis! { TokenLocation::get(), ExistentialDeposit::get() ).into()); - pub ToParachain: ParaId = rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + pub AssetHubParaId: ParaId = rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + pub const RandomParaId: ParaId = ParaId::new(43211234); } impl frame_system_benchmarking::Config for Runtime {} impl frame_benchmarking::baseline::Config for Runtime {} impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + runtime_common::xcm_sender::ToParachainDeliveryHelper< + XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + AssetHubParaId, + (), + >, + runtime_common::xcm_sender::ToParachainDeliveryHelper< + XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + RandomParaId, + (), + > + ); + fn reachable_dest() -> Option { Some(crate::xcm_config::AssetHub::get()) } @@ -2282,7 +2332,7 @@ sp_api::impl_runtime_apis! { // Relay/native token can be teleported to/from AH. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, crate::xcm_config::AssetHub::get(), @@ -2293,10 +2343,10 @@ sp_api::impl_runtime_apis! { // Relay can reserve transfer native token to some random parachain. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, - Parachain(43211234).into(), + Parachain(RandomParaId::get().into()).into(), )) } @@ -2313,6 +2363,13 @@ sp_api::impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::here()), + fun: Fungible(ExistentialDeposit::get()), + } + } } impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = XcmConfig; @@ -2321,7 +2378,7 @@ sp_api::impl_runtime_apis! { XcmConfig, ExistentialDepositAsset, xcm_config::PriceForChildParachainDelivery, - ToParachain, + AssetHubParaId, (), >; fn valid_destination() -> Result { @@ -2392,6 +2449,13 @@ sp_api::impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { // Rococo doesn't support asset locking Err(BenchmarkError::Skip) diff --git a/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs b/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs index dfba0cfc4aa90938c70376796117eea9ff00b676..0f68a5c6fb373b5d7b871aba540aa7210d710a53 100644 --- a/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs +++ b/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `frame_benchmarking::baseline` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=frame_benchmarking::baseline // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/frame_benchmarking_baseline.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,8 +55,8 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 157_000 picoseconds. - Weight::from_parts(175_233, 0) + // Minimum execution time: 172_000 picoseconds. + Weight::from_parts(199_481, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 1000000]`. @@ -61,8 +64,8 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 149_000 picoseconds. - Weight::from_parts(183_285, 0) + // Minimum execution time: 171_000 picoseconds. + Weight::from_parts(197_821, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 1000000]`. @@ -70,8 +73,8 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 158_000 picoseconds. - Weight::from_parts(184_720, 0) + // Minimum execution time: 172_000 picoseconds. + Weight::from_parts(200_942, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 1000000]`. @@ -79,16 +82,16 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 152_000 picoseconds. - Weight::from_parts(177_496, 0) + // Minimum execution time: 170_000 picoseconds. + Weight::from_parts(196_906, 0) .saturating_add(Weight::from_parts(0, 0)) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 19_907_376_000 picoseconds. - Weight::from_parts(19_988_727_000, 0) + // Minimum execution time: 23_346_876_000 picoseconds. + Weight::from_parts(23_363_744_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 100]`. @@ -96,10 +99,10 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 198_000 picoseconds. - Weight::from_parts(228_000, 0) + // Minimum execution time: 201_000 picoseconds. + Weight::from_parts(219_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 20_467 - .saturating_add(Weight::from_parts(47_443_635, 0).saturating_mul(i.into())) + // Standard Error: 14_372 + .saturating_add(Weight::from_parts(45_375_800, 0).saturating_mul(i.into())) } } diff --git a/polkadot/runtime/rococo/src/weights/frame_system.rs b/polkadot/runtime/rococo/src/weights/frame_system.rs index 2e49483dcc62728f3554bb1364efd740f8b03fd2..1742a761ca77baa50c79f51cb4ac854cba0fa274 100644 --- a/polkadot/runtime/rococo/src/weights/frame_system.rs +++ b/polkadot/runtime/rococo/src/weights/frame_system.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `frame_system` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=frame_system // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,91 +55,91 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_283_000 picoseconds. - Weight::from_parts(2_305_000, 0) + // Minimum execution time: 1_541_000 picoseconds. + Weight::from_parts(2_581_470, 0) .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 0 - .saturating_add(Weight::from_parts(366, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(387, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_435_000 picoseconds. - Weight::from_parts(7_581_000, 0) + // Minimum execution time: 5_060_000 picoseconds. + Weight::from_parts(5_167_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_408, 0).saturating_mul(b.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_696, 0).saturating_mul(b.into())) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a686561707061676573` (r:0 w:1) - /// Proof Skipped: unknown `0x3a686561707061676573` (r:0 w:1) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) fn set_heap_pages() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 4_010_000 picoseconds. - Weight::from_parts(4_112_000, 0) + // Minimum execution time: 2_649_000 picoseconds. + Weight::from_parts(2_909_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a636f6465` (r:0 w:1) - /// Proof Skipped: unknown `0x3a636f6465` (r:0 w:1) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) fn set_code() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 80_405_511_000 picoseconds. - Weight::from_parts(83_066_478_000, 0) + // Minimum execution time: 88_417_540_000 picoseconds. + Weight::from_parts(91_809_291_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn set_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_210_000 picoseconds. - Weight::from_parts(2_247_000, 0) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_589_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_058 - .saturating_add(Weight::from_parts(673_943, 0).saturating_mul(i.into())) + // Standard Error: 1_740 + .saturating_add(Weight::from_parts(730_941, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn kill_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_125_000 picoseconds. - Weight::from_parts(2_154_000, 0) + // Minimum execution time: 1_567_000 picoseconds. + Weight::from_parts(1_750_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 816 - .saturating_add(Weight::from_parts(491_194, 0).saturating_mul(i.into())) + // Standard Error: 835 + .saturating_add(Weight::from_parts(543_218, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `p` is `[0, 1000]`. fn kill_prefix(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `129 + p * (69 ±0)` - // Estimated: `125 + p * (70 ±0)` - // Minimum execution time: 4_002_000 picoseconds. - Weight::from_parts(4_145_000, 0) - .saturating_add(Weight::from_parts(0, 125)) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(1_014_971, 0).saturating_mul(p.into())) + // Measured: `80 + p * (69 ±0)` + // Estimated: `83 + p * (70 ±0)` + // Minimum execution time: 3_412_000 picoseconds. + Weight::from_parts(3_448_000, 0) + .saturating_add(Weight::from_parts(0, 83)) + // Standard Error: 1_395 + .saturating_add(Weight::from_parts(1_142_347, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) @@ -147,8 +150,8 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 33_027_000 picoseconds. - Weight::from_parts(33_027_000, 0) + // Minimum execution time: 9_178_000 picoseconds. + Weight::from_parts(9_780_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -162,8 +165,8 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `22` // Estimated: `1518` - // Minimum execution time: 118_101_992_000 picoseconds. - Weight::from_parts(118_101_992_000, 0) + // Minimum execution time: 94_523_563_000 picoseconds. + Weight::from_parts(96_983_131_000, 0) .saturating_add(Weight::from_parts(0, 1518)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) diff --git a/polkadot/runtime/rococo/src/weights/frame_system_extensions.rs b/polkadot/runtime/rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..40520591f533f8722b0d3cd9f68847288c28eb38 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,123 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=frame_system_extensions +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_262_000 picoseconds. + Weight::from_parts(3_497_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_416_000 picoseconds. + Weight::from_parts(5_690_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 471_000 picoseconds. + Weight::from_parts(552_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 4_847_000 picoseconds. + Weight::from_parts(5_091_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 388_000 picoseconds. + Weight::from_parts(421_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 378_000 picoseconds. + Weight::from_parts(440_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 3_402_000 picoseconds. + Weight::from_parts(3_627_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/mod.rs b/polkadot/runtime/rococo/src/weights/mod.rs index 3613fb4305ba0f7a35190a7ef788979c2f241207..128a3083a9c7139854fd2c41d75f82422617f1e4 100644 --- a/polkadot/runtime/rococo/src/weights/mod.rs +++ b/polkadot/runtime/rococo/src/weights/mod.rs @@ -16,14 +16,14 @@ //! A list of the different weight modules for our runtime. pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_rate; -pub mod pallet_balances; +pub mod pallet_balances_balances; pub mod pallet_balances_nis_counterpart_balances; pub mod pallet_bounties; pub mod pallet_child_bounties; pub mod pallet_conviction_voting; pub mod pallet_identity; -pub mod pallet_im_online; pub mod pallet_indices; pub mod pallet_message_queue; pub mod pallet_multisig; @@ -37,6 +37,7 @@ pub mod pallet_scheduler; pub mod pallet_session; pub mod pallet_sudo; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_treasury; pub mod pallet_utility; pub mod pallet_vesting; diff --git a/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs b/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs index da2d1958cefcfeb41bcdba7ed2dd9c6b4272e873..56b1e2cbc5717fa932d9a5419773ce5fbd246d7f 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs @@ -16,25 +16,28 @@ //! Autogenerated weights for `pallet_asset_rate` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-03, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cob`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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/debug/polkadot +// ./target/production/polkadot // benchmark // pallet // --chain=rococo-dev // --steps=50 -// --repeat=2 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_asset_rate // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --output=./runtime/rococo/src/weights/ -// --header=./file_header.txt +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,39 +50,39 @@ use core::marker::PhantomData; /// Weight functions for `pallet_asset_rate`. pub struct WeightInfo(PhantomData); impl pallet_asset_rate::WeightInfo for WeightInfo { - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `42` - // Estimated: `4702` - // Minimum execution time: 143_000_000 picoseconds. - Weight::from_parts(155_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `142` + // Estimated: `4703` + // Minimum execution time: 10_277_000 picoseconds. + Weight::from_parts(10_487_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) fn update() -> Weight { // Proof Size summary in bytes: - // Measured: `110` - // Estimated: `4702` - // Minimum execution time: 156_000_000 picoseconds. - Weight::from_parts(172_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `210` + // Estimated: `4703` + // Minimum execution time: 10_917_000 picoseconds. + Weight::from_parts(11_249_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) fn remove() -> Weight { // Proof Size summary in bytes: - // Measured: `110` - // Estimated: `4702` - // Minimum execution time: 150_000_000 picoseconds. - Weight::from_parts(160_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `210` + // Estimated: `4703` + // Minimum execution time: 11_332_000 picoseconds. + Weight::from_parts(11_866_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_balances.rs b/polkadot/runtime/rococo/src/weights/pallet_balances.rs deleted file mode 100644 index 972c097372f4bf75c5891f575cb4ebd0ec59b8f2..0000000000000000000000000000000000000000 --- a/polkadot/runtime/rococo/src/weights/pallet_balances.rs +++ /dev/null @@ -1,99 +0,0 @@ -// 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_balances` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=rococo-dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_balances -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; - -/// Weight functions for `pallet_balances`. -pub struct WeightInfo(PhantomData); -impl pallet_balances::WeightInfo for WeightInfo { - // Storage: System Account (r:1 w:1) - fn transfer_allow_death() -> Weight { - // Minimum execution time: 40_106 nanoseconds. - Weight::from_parts(40_750_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: System Account (r:1 w:1) - fn transfer_keep_alive() -> Weight { - // Minimum execution time: 30_737 nanoseconds. - Weight::from_parts(31_295_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: System Account (r:1 w:1) - fn force_set_balance_creating() -> Weight { - // Minimum execution time: 23_902 nanoseconds. - Weight::from_parts(24_338_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: System Account (r:1 w:1) - fn force_set_balance_killing() -> Weight { - // Minimum execution time: 26_492 nanoseconds. - Weight::from_parts(26_866_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: System Account (r:2 w:2) - fn force_transfer() -> Weight { - // Minimum execution time: 40_384 nanoseconds. - Weight::from_parts(41_000_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: System Account (r:1 w:1) - fn transfer_all() -> Weight { - // Minimum execution time: 35_115 nanoseconds. - Weight::from_parts(35_696_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: System Account (r:1 w:1) - fn force_unreserve() -> Weight { - // Minimum execution time: 20_274 nanoseconds. - Weight::from_parts(20_885_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - fn upgrade_accounts(_: u32) -> Weight { - Weight::from_parts(0, 0) - } -} diff --git a/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs b/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs new file mode 100644 index 0000000000000000000000000000000000000000..3fa54f2c36972b90b5304ef3ab3a962d9866ed27 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs @@ -0,0 +1,162 @@ +// 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_balances` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_balances +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --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_balances`. +pub struct WeightInfo(PhantomData); +impl pallet_balances::WeightInfo for WeightInfo { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_allow_death() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 45_336_000 picoseconds. + Weight::from_parts(46_189_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn transfer_keep_alive() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 34_880_000 picoseconds. + Weight::from_parts(35_770_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn force_set_balance_creating() -> Weight { + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 12_904_000 picoseconds. + Weight::from_parts(13_260_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn force_set_balance_killing() -> Weight { + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 17_669_000 picoseconds. + Weight::from_parts(18_228_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn force_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `103` + // Estimated: `6196` + // Minimum execution time: 46_492_000 picoseconds. + Weight::from_parts(47_639_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_all() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 44_342_000 picoseconds. + Weight::from_parts(45_144_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn force_unreserve() -> Weight { + // Proof Size summary in bytes: + // Measured: `174` + // Estimated: `3593` + // Minimum execution time: 15_260_000 picoseconds. + Weight::from_parts(15_775_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `u` is `[1, 1000]`. + /// The range of component `u` is `[1, 1000]`. + fn upgrade_accounts(u: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + u * (135 ±0)` + // Estimated: `990 + u * (2603 ±0)` + // Minimum execution time: 14_703_000 picoseconds. + Weight::from_parts(14_950_000, 0) + .saturating_add(Weight::from_parts(0, 990)) + // Standard Error: 7_665 + .saturating_add(Weight::from_parts(13_335_803, 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())) + } + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_506_000 picoseconds. + Weight::from_parts(5_753_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs b/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs index 597a67de4b99d7d67e2e221a3b1a905427339254..1852ba6c2c4da6c814ca520b4d73a6b4654f84f7 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_balances` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_balances // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,129 +50,127 @@ use core::marker::PhantomData; /// Weight functions for `pallet_balances`. pub struct WeightInfo(PhantomData); impl pallet_balances::WeightInfo for WeightInfo { - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:2 w:2) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:2 w:2) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_allow_death() -> Weight { // Proof Size summary in bytes: - // Measured: `219` + // Measured: `103` // Estimated: `6164` - // Minimum execution time: 54_122_000 picoseconds. - Weight::from_parts(54_834_000, 0) + // Minimum execution time: 42_443_000 picoseconds. + Weight::from_parts(43_250_000, 0) .saturating_add(Weight::from_parts(0, 6164)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:0) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:2 w:2) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:2 w:2) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: - // Measured: `219` + // Measured: `103` // Estimated: `6164` - // Minimum execution time: 41_749_000 picoseconds. - Weight::from_parts(42_193_000, 0) + // Minimum execution time: 32_417_000 picoseconds. + Weight::from_parts(33_247_000, 0) .saturating_add(Weight::from_parts(0, 6164)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) fn force_set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `217` + // Measured: `103` // Estimated: `3577` - // Minimum execution time: 16_008_000 picoseconds. - Weight::from_parts(16_328_000, 0) + // Minimum execution time: 10_091_000 picoseconds. + Weight::from_parts(10_426_000, 0) .saturating_add(Weight::from_parts(0, 3577)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// 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: `393` + // Measured: `277` // Estimated: `3593` - // Minimum execution time: 26_277_000 picoseconds. - Weight::from_parts(26_932_000, 0) + // Minimum execution time: 16_546_000 picoseconds. + Weight::from_parts(17_259_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:2 w:2) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:2 w:2) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `322` + // Measured: `206` // Estimated: `6196` - // Minimum execution time: 57_020_000 picoseconds. - Weight::from_parts(57_661_000, 0) + // Minimum execution time: 44_322_000 picoseconds. + Weight::from_parts(45_319_000, 0) .saturating_add(Weight::from_parts(0, 6196)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: NisCounterpartBalances Account (r:2 w:2) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:0) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:2 w:2) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_all() -> Weight { // Proof Size summary in bytes: - // Measured: `219` + // Measured: `103` // Estimated: `6164` - // Minimum execution time: 50_630_000 picoseconds. - Weight::from_parts(51_191_000, 0) + // Minimum execution time: 40_852_000 picoseconds. + Weight::from_parts(42_205_000, 0) .saturating_add(Weight::from_parts(0, 6164)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `391` + // Measured: `277` // Estimated: `3593` - // Minimum execution time: 21_915_000 picoseconds. - Weight::from_parts(22_295_000, 0) + // Minimum execution time: 15_050_000 picoseconds. + Weight::from_parts(15_813_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: NisCounterpartBalances Account (r:999 w:999) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: System Account (r:999 w:999) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `NisCounterpartBalances::Account` (r:999 w:999) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `u` is `[1, 1000]`. /// The range of component `u` is `[1, 1000]`. fn upgrade_accounts(u: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + u * (256 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 21_290_000 picoseconds. - Weight::from_parts(21_622_000, 0) + // Minimum execution time: 14_830_000 picoseconds. + Weight::from_parts(15_061_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 13_372 - .saturating_add(Weight::from_parts(15_527_611, 0).saturating_mul(u.into())) + // Standard Error: 16_072 + .saturating_add(Weight::from_parts(14_981_430, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) } + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_344_000 picoseconds. + Weight::from_parts(5_735_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_bounties.rs b/polkadot/runtime/rococo/src/weights/pallet_bounties.rs index 38d3645316f25faba74fff9d37c66db2ab4f08d9..8f8be5f2386f7983d42dbad355a266ff68cf332a 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_bounties.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_bounties.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_bounties` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_bounties // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,118 +50,181 @@ use core::marker::PhantomData; /// Weight functions for `pallet_bounties`. pub struct WeightInfo(PhantomData); impl pallet_bounties::WeightInfo for WeightInfo { - /// Storage: Bounties BountyCount (r:1 w:1) - /// Proof: Bounties BountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(16400), added: 18875, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:0 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyCount` (r:1 w:1) + /// Proof: `Bounties::BountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:0 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 16384]`. fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `210` // Estimated: `3593` - // Minimum execution time: 28_907_000 picoseconds. - Weight::from_parts(31_356_074, 0) + // Minimum execution time: 21_772_000 picoseconds. + Weight::from_parts(22_861_341, 0) .saturating_add(Weight::from_parts(0, 3593)) - // Standard Error: 18 - .saturating_add(Weight::from_parts(606, 0).saturating_mul(d.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(721, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn approve_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `302` + // Estimated: `3642` + // Minimum execution time: 11_218_000 picoseconds. + Weight::from_parts(11_796_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `322` + // Estimated: `3642` + // Minimum execution time: 10_959_000 picoseconds. + Weight::from_parts(11_658_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `498` + // Estimated: `3642` + // Minimum execution time: 37_419_000 picoseconds. + Weight::from_parts(38_362_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `494` + // Estimated: `3642` + // Minimum execution time: 27_328_000 picoseconds. + Weight::from_parts(27_661_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `400` + // Estimated: `3642` + // Minimum execution time: 16_067_000 picoseconds. + Weight::from_parts(16_865_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `764` + // Estimated: `8799` + // Minimum execution time: 101_153_000 picoseconds. + Weight::from_parts(102_480_000, 0) + .saturating_add(Weight::from_parts(0, 8799)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(16400), added: 18875, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `482` + // Measured: `444` // Estimated: `3642` - // Minimum execution time: 46_020_000 picoseconds. - Weight::from_parts(46_711_000, 0) + // Minimum execution time: 38_838_000 picoseconds. + Weight::from_parts(39_549_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `680` + // Estimated: `6196` + // Minimum execution time: 68_592_000 picoseconds. + Weight::from_parts(70_727_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn extend_bounty_expiry() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `358` + // Estimated: `3642` + // Minimum execution time: 11_272_000 picoseconds. + Weight::from_parts(11_592_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:100 w:100) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:200 w:200) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `b` is `[0, 100]`. - fn spend_funds(_b: u32, ) -> Weight { + fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `1887` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(2_405_233, 0) + // Measured: `0 + b * (297 ±0)` + // Estimated: `1887 + b * (5206 ±0)` + // Minimum execution time: 2_844_000 picoseconds. + Weight::from_parts(2_900_000, 0) .saturating_add(Weight::from_parts(0, 1887)) + // Standard Error: 9_467 + .saturating_add(Weight::from_parts(32_326_595, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 5206).saturating_mul(b.into())) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs b/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs index e8c798d45e727618b892e4ebd7e11d7ba81f66ca..47ae3a5c90d1e8524b914bb3b4b5bbaeb71f3c25 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_child_bounties` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_child_bounties // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,69 +50,153 @@ use core::marker::PhantomData; /// Weight functions for `pallet_child_bounties`. pub struct WeightInfo(PhantomData); impl pallet_child_bounties::WeightInfo for WeightInfo { + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyCount` (r:1 w:1) + /// Proof: `ChildBounties::ChildBountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:0 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 16384]`. - fn add_child_bounty(_d: u32, ) -> Weight { + fn add_child_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `540` + // Estimated: `6196` + // Minimum execution time: 57_964_000 picoseconds. + Weight::from_parts(59_559_565, 0) + .saturating_add(Weight::from_parts(0, 6196)) + // Standard Error: 11 + .saturating_add(Weight::from_parts(697, 0).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(6)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `594` + // Estimated: `3642` + // Minimum execution time: 17_527_000 picoseconds. + Weight::from_parts(18_257_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `740` + // Estimated: `3642` + // Minimum execution time: 29_354_000 picoseconds. + Weight::from_parts(30_629_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `740` + // Estimated: `3642` + // Minimum execution time: 40_643_000 picoseconds. + Weight::from_parts(42_072_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) fn award_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `637` + // Estimated: `3642` + // Minimum execution time: 18_616_000 picoseconds. + Weight::from_parts(19_316_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `576` + // Estimated: `8799` + // Minimum execution time: 96_376_000 picoseconds. + Weight::from_parts(98_476_000, 0) + .saturating_add(Weight::from_parts(0, 8799)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(6)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `840` + // Estimated: `6196` + // Minimum execution time: 64_640_000 picoseconds. + Weight::from_parts(66_174_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `1027` + // Estimated: `8799` + // Minimum execution time: 78_159_000 picoseconds. + Weight::from_parts(79_820_000, 0) + .saturating_add(Weight::from_parts(0, 8799)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(7)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs b/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs index ba505737f1b070d21931c7e467b4853de1f119d2..5d92c158df44ee17d9e473cc222e4bae76597b67 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs @@ -16,17 +16,17 @@ //! Autogenerated weights for `pallet_conviction_voting` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 -// --chain=kusama-dev +// --chain=rococo-dev // --steps=50 // --repeat=20 // --no-storage-info @@ -36,8 +36,8 @@ // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/kusama/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,144 +50,152 @@ use core::marker::PhantomData; /// Weight functions for `pallet_conviction_voting`. pub struct WeightInfo(PhantomData); impl pallet_conviction_voting::WeightInfo for WeightInfo { - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `13445` + // Measured: `13407` // Estimated: `42428` - // Minimum execution time: 151_077_000 picoseconds. - Weight::from_parts(165_283_000, 0) + // Minimum execution time: 128_378_000 picoseconds. + Weight::from_parts(131_028_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `14166` + // Measured: `14128` // Estimated: `83866` - // Minimum execution time: 232_420_000 picoseconds. - Weight::from_parts(244_439_000, 0) + // Minimum execution time: 155_379_000 picoseconds. + Weight::from_parts(161_597_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(7)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn remove_vote() -> Weight { // Proof Size summary in bytes: // Measured: `13918` // Estimated: `83866` - // Minimum execution time: 205_017_000 picoseconds. - Weight::from_parts(216_594_000, 0) + // Minimum execution time: 130_885_000 picoseconds. + Weight::from_parts(138_080_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn remove_other_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `13004` + // Measured: `13005` // Estimated: `30706` - // Minimum execution time: 84_226_000 picoseconds. - Weight::from_parts(91_255_000, 0) + // Minimum execution time: 71_743_000 picoseconds. + Weight::from_parts(75_170_000, 0) .saturating_add(Weight::from_parts(0, 30706)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:512 w:512) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:512 w:512) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:50) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 512]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `29640 + r * (365 ±0)` + // Measured: `29602 + r * (365 ±0)` // Estimated: `83866 + r * (3411 ±0)` - // Minimum execution time: 78_708_000 picoseconds. - Weight::from_parts(2_053_488_615, 0) + // Minimum execution time: 58_504_000 picoseconds. + Weight::from_parts(814_301_018, 0) .saturating_add(Weight::from_parts(0, 83866)) - // Standard Error: 179_271 - .saturating_add(Weight::from_parts(47_806_482, 0).saturating_mul(r.into())) + // Standard Error: 59_961 + .saturating_add(Weight::from_parts(20_002_833, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(45)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 3411).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:512 w:512) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:512 w:512) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:50) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 512]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `29555 + r * (365 ±0)` // Estimated: `83866 + r * (3411 ±0)` - // Minimum execution time: 45_232_000 picoseconds. - Weight::from_parts(2_045_021_014, 0) + // Minimum execution time: 34_970_000 picoseconds. + Weight::from_parts(771_155_804, 0) .saturating_add(Weight::from_parts(0, 83866)) - // Standard Error: 185_130 - .saturating_add(Weight::from_parts(47_896_011, 0).saturating_mul(r.into())) + // Standard Error: 57_795 + .saturating_add(Weight::from_parts(19_781_645, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(43)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 3411).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) fn unlock() -> Weight { // Proof Size summary in bytes: - // Measured: `12218` + // Measured: `12180` // Estimated: `30706` - // Minimum execution time: 116_446_000 picoseconds. - Weight::from_parts(124_043_000, 0) + // Minimum execution time: 89_648_000 picoseconds. + Weight::from_parts(97_144_000, 0) .saturating_add(Weight::from_parts(0, 30706)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_identity.rs b/polkadot/runtime/rococo/src/weights/pallet_identity.rs index b334e21ea03127a749ff1bf2455f69627f832922..6df16351f2c28f96f807bb197f9059b51e19f703 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_identity.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_identity.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_identity` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_identity // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,290 +50,291 @@ use core::marker::PhantomData; /// Weight functions for `pallet_identity`. pub struct WeightInfo(PhantomData); impl pallet_identity::WeightInfo for WeightInfo { - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 12_290_000 picoseconds. - Weight::from_parts(12_664_362, 0) + // Minimum execution time: 7_673_000 picoseconds. + Weight::from_parts(8_351_866, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 1_347 - .saturating_add(Weight::from_parts(88_179, 0).saturating_mul(r.into())) + // Standard Error: 1_302 + .saturating_add(Weight::from_parts(79_198, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn set_identity(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `442 + r * (5 ±0)` - // Estimated: `11003` - // Minimum execution time: 31_373_000 picoseconds. - Weight::from_parts(30_435_545, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_307 - .saturating_add(Weight::from_parts(92_753, 0).saturating_mul(r.into())) + // Measured: `6978 + r * (5 ±0)` + // Estimated: `11037` + // Minimum execution time: 111_646_000 picoseconds. + Weight::from_parts(113_254_991, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 6_611 + .saturating_add(Weight::from_parts(162_119, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:100 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:100 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `11003 + s * (2589 ±0)` - // Minimum execution time: 9_251_000 picoseconds. - Weight::from_parts(22_039_210, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 40_779 - .saturating_add(Weight::from_parts(2_898_525, 0).saturating_mul(s.into())) + // Estimated: `11037 + s * (2589 ±0)` + // Minimum execution time: 8_010_000 picoseconds. + Weight::from_parts(19_868_412, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 5_018 + .saturating_add(Weight::from_parts(3_115_007, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 2589).saturating_mul(s.into())) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `194 + p * (32 ±0)` - // Estimated: `11003` - // Minimum execution time: 9_329_000 picoseconds. - Weight::from_parts(24_055_061, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 3_428 - .saturating_add(Weight::from_parts(1_130_604, 0).saturating_mul(p.into())) + // Estimated: `11037` + // Minimum execution time: 8_111_000 picoseconds. + Weight::from_parts(19_482_392, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 3_156 + .saturating_add(Weight::from_parts(1_305_890, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - fn clear_identity(_r: u32, s: u32, ) -> Weight { + fn clear_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 53_365_000 picoseconds. - Weight::from_parts(35_391_422, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 1_353 - .saturating_add(Weight::from_parts(1_074_019, 0).saturating_mul(s.into())) + // Measured: `7070 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 54_107_000 picoseconds. + Weight::from_parts(56_347_715, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 10_944 + .saturating_add(Weight::from_parts(191_321, 0).saturating_mul(r.into())) + // Standard Error: 2_135 + .saturating_add(Weight::from_parts(1_295_872, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `367 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 32_509_000 picoseconds. - Weight::from_parts(31_745_585, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_214 - .saturating_add(Weight::from_parts(83_822, 0).saturating_mul(r.into())) - + // Measured: `6968 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 75_780_000 picoseconds. + Weight::from_parts(76_869_773, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 5_456 + .saturating_add(Weight::from_parts(135_316, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `398 + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 29_609_000 picoseconds. - Weight::from_parts(28_572_602, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_528 - .saturating_add(Weight::from_parts(85_593, 0).saturating_mul(r.into())) + // Measured: `6999` + // Estimated: `11037` + // Minimum execution time: 75_769_000 picoseconds. + Weight::from_parts(76_805_143, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 3_598 + .saturating_add(Weight::from_parts(84_593, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_793_000 picoseconds. - Weight::from_parts(8_173_888, 0) + // Minimum execution time: 5_357_000 picoseconds. + Weight::from_parts(5_732_132, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 1_569 - .saturating_add(Weight::from_parts(72_367, 0).saturating_mul(r.into())) + // Standard Error: 927 + .saturating_add(Weight::from_parts(70_832, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_708_000 picoseconds. - Weight::from_parts(8_091_149, 0) + // Minimum execution time: 5_484_000 picoseconds. + Weight::from_parts(5_892_704, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 869 - .saturating_add(Weight::from_parts(87_993, 0).saturating_mul(r.into())) + // Standard Error: 947 + .saturating_add(Weight::from_parts(71_231, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_601_000 picoseconds. - Weight::from_parts(8_038_414, 0) + // Minimum execution time: 5_310_000 picoseconds. + Weight::from_parts(5_766_651, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 1_041 - .saturating_add(Weight::from_parts(82_588, 0).saturating_mul(r.into())) + // Standard Error: 916 + .saturating_add(Weight::from_parts(74_776, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `445 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 23_114_000 picoseconds. - Weight::from_parts(22_076_548, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_881 - .saturating_add(Weight::from_parts(109_812, 0).saturating_mul(r.into())) + // Measured: `7046 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 98_200_000 picoseconds. + Weight::from_parts(100_105_482, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 6_152 + .saturating_add(Weight::from_parts(58_906, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 70_007_000 picoseconds. - Weight::from_parts(50_186_495, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 6_533 - .saturating_add(Weight::from_parts(15_486, 0).saturating_mul(r.into())) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(1_085_117, 0).saturating_mul(s.into())) + // Measured: `7277 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 64_647_000 picoseconds. + Weight::from_parts(68_877_027, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 9_965 + .saturating_add(Weight::from_parts(135_044, 0).saturating_mul(r.into())) + // Standard Error: 1_944 + .saturating_add(Weight::from_parts(1_388_151, 0).saturating_mul(s.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(s.into()))) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `475 + s * (36 ±0)` - // Estimated: `11003` - // Minimum execution time: 28_453_000 picoseconds. - Weight::from_parts(33_165_934, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(65_401, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 23_550_000 picoseconds. + Weight::from_parts(29_439_842, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 1_453 + .saturating_add(Weight::from_parts(96_324, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + s * (3 ±0)` - // Estimated: `11003` - // Minimum execution time: 12_846_000 picoseconds. - Weight::from_parts(14_710_284, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 496 - .saturating_add(Weight::from_parts(19_539, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 13_704_000 picoseconds. + Weight::from_parts(15_241_441, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 498 + .saturating_add(Weight::from_parts(40_973, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `638 + s * (35 ±0)` - // Estimated: `11003` - // Minimum execution time: 32_183_000 picoseconds. - Weight::from_parts(35_296_731, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 854 - .saturating_add(Weight::from_parts(52_028, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 29_310_000 picoseconds. + Weight::from_parts(31_712_666, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 967 + .saturating_add(Weight::from_parts(81_250, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `704 + s * (37 ±0)` // Estimated: `6723` - // Minimum execution time: 24_941_000 picoseconds. - Weight::from_parts(27_433_059, 0) + // Minimum execution time: 22_906_000 picoseconds. + Weight::from_parts(24_638_729, 0) .saturating_add(Weight::from_parts(0, 6723)) - // Standard Error: 856 - .saturating_add(Weight::from_parts(57_463, 0).saturating_mul(s.into())) + // Standard Error: 645 + .saturating_add(Weight::from_parts(75_121, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -340,90 +344,93 @@ impl pallet_identity::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_873_000 picoseconds. - Weight::from_parts(13_873_000, 0) + // Minimum execution time: 6_056_000 picoseconds. + Weight::from_parts(6_349_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Identity::UsernameAuthorities` (r:0 w:1) + /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn remove_username_authority() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 10_653_000 picoseconds. - Weight::from_parts(10_653_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 9_003_000 picoseconds. + Weight::from_parts(9_276_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Identity::PendingUsernames` (r:1 w:0) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_username_for() -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` - // Minimum execution time: 75_928_000 picoseconds. - Weight::from_parts(75_928_000, 0) + // Minimum execution time: 64_724_000 picoseconds. + Weight::from_parts(66_597_000, 0) .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:0 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn accept_username() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `115` // Estimated: `11037` - // Minimum execution time: 38_157_000 picoseconds. - Weight::from_parts(38_157_000, 0) + // Minimum execution time: 19_538_000 picoseconds. + Weight::from_parts(20_204_000, 0) .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn remove_expired_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `3542` - // Minimum execution time: 46_821_000 picoseconds. - Weight::from_parts(46_821_000, 0) - .saturating_add(Weight::from_parts(0, 3542)) + // Measured: `115` + // Estimated: `3550` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(19_354_000, 0) + .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:0) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_primary_username() -> Weight { // Proof Size summary in bytes: - // Measured: `247` + // Measured: `257` // Estimated: `11037` - // Minimum execution time: 22_515_000 picoseconds. - Weight::from_parts(22_515_000, 0) + // Minimum execution time: 15_298_000 picoseconds. + Weight::from_parts(15_760_000, 0) .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:0) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn remove_dangling_username() -> Weight { // Proof Size summary in bytes: - // Measured: `126` + // Measured: `98` // Estimated: `11037` - // Minimum execution time: 15_997_000 picoseconds. - Weight::from_parts(15_997_000, 0) + // Minimum execution time: 10_829_000 picoseconds. + Weight::from_parts(11_113_000, 0) .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_im_online.rs b/polkadot/runtime/rococo/src/weights/pallet_im_online.rs deleted file mode 100644 index b866426de52a3abc7608f96bb192a1ae4fbb1d90..0000000000000000000000000000000000000000 --- a/polkadot/runtime/rococo/src/weights/pallet_im_online.rs +++ /dev/null @@ -1,76 +0,0 @@ -// 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_im_online` -//! -//! 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: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `build-host`, CPU: `AMD EPYC 7601 32-Core Processor` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=rococo-dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_im_online -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./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_im_online`. -pub struct WeightInfo(PhantomData); -impl pallet_im_online::WeightInfo for WeightInfo { - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ImOnline Keys (r:1 w:0) - /// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen) - /// Storage: unknown `0x39e295d143ed41353167609a3d816584` (r:1 w:0) - /// Proof Skipped: unknown `0x39e295d143ed41353167609a3d816584` (r:1 w:0) - /// Storage: ImOnline ReceivedHeartbeats (r:1 w:1) - /// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(1028), added: 3503, mode: MaxEncodedLen) - /// Storage: ImOnline AuthoredBlocks (r:1 w:0) - /// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) - /// The range of component `k` is `[1, 1000]`. - fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `394 + k * (32 ±0)` - // Estimated: `321487 + k * (1761 ±0)` - // Minimum execution time: 132_910_000 picoseconds. - Weight::from_parts(149_854_501, 0) - .saturating_add(Weight::from_parts(0, 321487)) - // Standard Error: 3_317 - .saturating_add(Weight::from_parts(61_141, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(Weight::from_parts(0, 1761).saturating_mul(k.into())) - } -} diff --git a/polkadot/runtime/rococo/src/weights/pallet_indices.rs b/polkadot/runtime/rococo/src/weights/pallet_indices.rs index 99ffd3210ed24f04e170a66c57b01ea9232b0475..434db97d4a79faa0d51dcad13c16d6840f357483 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_indices.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_indices.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_indices` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_indices // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,66 +50,66 @@ use core::marker::PhantomData; /// Weight functions for `pallet_indices`. pub struct WeightInfo(PhantomData); impl pallet_indices::WeightInfo for WeightInfo { - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn claim() -> Weight { // Proof Size summary in bytes: - // Measured: `142` + // Measured: `4` // Estimated: `3534` - // Minimum execution time: 25_107_000 picoseconds. - Weight::from_parts(25_655_000, 0) + // Minimum execution time: 18_092_000 picoseconds. + Weight::from_parts(18_533_000, 0) .saturating_add(Weight::from_parts(0, 3534)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `341` + // Measured: `203` // Estimated: `3593` - // Minimum execution time: 36_208_000 picoseconds. - Weight::from_parts(36_521_000, 0) + // Minimum execution time: 31_616_000 picoseconds. + Weight::from_parts(32_556_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn free() -> Weight { // Proof Size summary in bytes: - // Measured: `238` + // Measured: `100` // Estimated: `3534` - // Minimum execution time: 25_915_000 picoseconds. - Weight::from_parts(26_220_000, 0) + // Minimum execution time: 19_593_000 picoseconds. + Weight::from_parts(20_100_000, 0) .saturating_add(Weight::from_parts(0, 3534)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `341` + // Measured: `203` // Estimated: `3593` - // Minimum execution time: 28_232_000 picoseconds. - Weight::from_parts(28_845_000, 0) + // Minimum execution time: 21_429_000 picoseconds. + Weight::from_parts(22_146_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `238` + // Measured: `100` // Estimated: `3534` - // Minimum execution time: 27_282_000 picoseconds. - Weight::from_parts(27_754_000, 0) + // Minimum execution time: 20_425_000 picoseconds. + Weight::from_parts(21_023_000, 0) .saturating_add(Weight::from_parts(0, 3534)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs b/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs index e1e360d374a0646b4fed8a29f3509565309d89c0..6ebfcd060b642d03c6eebdf7918d032253041e28 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_message_queue` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_message_queue // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,150 +50,149 @@ use core::marker::PhantomData; /// Weight functions for `pallet_message_queue`. pub struct WeightInfo(PhantomData); impl pallet_message_queue::WeightInfo for WeightInfo { - /// Storage: MessageQueue ServiceHead (r:1 w:0) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(6), added: 501, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:0) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(6), added: 501, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `248` + // Measured: `281` // Estimated: `6050` - // Minimum execution time: 12_106_000 picoseconds. - Weight::from_parts(12_387_000, 0) + // Minimum execution time: 12_830_000 picoseconds. + Weight::from_parts(13_476_000, 0) .saturating_add(Weight::from_parts(0, 6050)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(6), added: 501, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(6), added: 501, mode: `MaxEncodedLen`) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `248` + // Measured: `281` // Estimated: `6050` - // Minimum execution time: 11_227_000 picoseconds. - Weight::from_parts(11_616_000, 0) + // Minimum execution time: 11_583_000 picoseconds. + Weight::from_parts(11_902_000, 0) .saturating_add(Weight::from_parts(0, 6050)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) fn service_queue_base() -> Weight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3520` - // Minimum execution time: 5_052_000 picoseconds. - Weight::from_parts(5_216_000, 0) + // Minimum execution time: 3_801_000 picoseconds. + Weight::from_parts(3_943_000, 0) .saturating_add(Weight::from_parts(0, 3520)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: // Measured: `115` // Estimated: `36283` - // Minimum execution time: 6_522_000 picoseconds. - Weight::from_parts(6_794_000, 0) + // Minimum execution time: 5_517_000 picoseconds. + Weight::from_parts(5_861_000, 0) .saturating_add(Weight::from_parts(0, 36283)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: // Measured: `115` // Estimated: `36283` - // Minimum execution time: 6_918_000 picoseconds. - Weight::from_parts(7_083_000, 0) + // Minimum execution time: 5_870_000 picoseconds. + Weight::from_parts(6_028_000, 0) .saturating_add(Weight::from_parts(0, 36283)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `MessageQueue::BookStateFor` (r:0 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) fn service_page_item() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 28_445_000 picoseconds. - Weight::from_parts(28_659_000, 0) + // Minimum execution time: 80_681_000 picoseconds. + Weight::from_parts(81_818_000, 0) .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(6), added: 501, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(6), added: 501, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `149` + // Measured: `220` // Estimated: `3520` - // Minimum execution time: 7_224_000 picoseconds. - Weight::from_parts(7_441_000, 0) + // Minimum execution time: 8_641_000 picoseconds. + Weight::from_parts(8_995_000, 0) .saturating_add(Weight::from_parts(0, 3520)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// 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:1) - /// 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) + /// 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:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `33232` + // Measured: `32945` // Estimated: `36283` - // Minimum execution time: 45_211_000 picoseconds. - Weight::from_parts(45_505_000, 0) + // Minimum execution time: 38_473_000 picoseconds. + Weight::from_parts(39_831_000, 0) .saturating_add(Weight::from_parts(0, 36283)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } - /// 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:1) - /// 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) + /// 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:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `33232` + // Measured: `32945` // Estimated: `36283` - // Minimum execution time: 52_346_000 picoseconds. - Weight::from_parts(52_745_000, 0) + // Minimum execution time: 48_717_000 picoseconds. + Weight::from_parts(49_724_000, 0) .saturating_add(Weight::from_parts(0, 36283)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } - /// 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:1) - /// 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) + /// 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:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `33232` + // Measured: `32945` // Estimated: `36283` - // Minimum execution time: 72_567_000 picoseconds. - Weight::from_parts(73_300_000, 0) + // Minimum execution time: 72_718_000 picoseconds. + Weight::from_parts(74_081_000, 0) .saturating_add(Weight::from_parts(0, 36283)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_multisig.rs b/polkadot/runtime/rococo/src/weights/pallet_multisig.rs index a4f33fe198ca167e9b8112a13b1549ba727da354..f1b81759ece6c4c360a4bfb61afafb76836d11b2 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_multisig.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_multisig.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_multisig` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_multisig // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,110 +55,110 @@ impl pallet_multisig::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_475_000 picoseconds. - Weight::from_parts(11_904_745, 0) + // Minimum execution time: 12_023_000 picoseconds. + Weight::from_parts(12_643_116, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(492, 0).saturating_mul(z.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(582, 0).saturating_mul(z.into())) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + s * (2 ±0)` + // Measured: `229 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 38_857_000 picoseconds. - Weight::from_parts(33_611_791, 0) + // Minimum execution time: 39_339_000 picoseconds. + Weight::from_parts(27_243_033, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 400 - .saturating_add(Weight::from_parts(59_263, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_211, 0).saturating_mul(z.into())) + // Standard Error: 1_319 + .saturating_add(Weight::from_parts(142_212, 0).saturating_mul(s.into())) + // Standard Error: 12 + .saturating_add(Weight::from_parts(1_592, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[3, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `211` + // Measured: `248` // Estimated: `6811` - // Minimum execution time: 25_715_000 picoseconds. - Weight::from_parts(20_607_294, 0) + // Minimum execution time: 27_647_000 picoseconds. + Weight::from_parts(15_828_725, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 285 - .saturating_add(Weight::from_parts(58_225, 0).saturating_mul(s.into())) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_160, 0).saturating_mul(z.into())) + // Standard Error: 908 + .saturating_add(Weight::from_parts(130_880, 0).saturating_mul(s.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(1_532, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, 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 `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `317 + s * (33 ±0)` + // Measured: `354 + s * (33 ±0)` // Estimated: `6811` - // Minimum execution time: 43_751_000 picoseconds. - Weight::from_parts(37_398_513, 0) + // Minimum execution time: 46_971_000 picoseconds. + Weight::from_parts(32_150_393, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 426 - .saturating_add(Weight::from_parts(70_904, 0).saturating_mul(s.into())) - // Standard Error: 4 - .saturating_add(Weight::from_parts(1_235, 0).saturating_mul(z.into())) + // Standard Error: 1_129 + .saturating_add(Weight::from_parts(154_796, 0).saturating_mul(s.into())) + // Standard Error: 11 + .saturating_add(Weight::from_parts(1_603, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + s * (2 ±0)` + // Measured: `229 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 31_278_000 picoseconds. - Weight::from_parts(32_075_573, 0) + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_497_183, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 452 - .saturating_add(Weight::from_parts(62_018, 0).saturating_mul(s.into())) + // Standard Error: 1_615 + .saturating_add(Weight::from_parts(147_071, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `211` + // Measured: `248` // Estimated: `6811` - // Minimum execution time: 18_178_000 picoseconds. - Weight::from_parts(18_649_867, 0) + // Minimum execution time: 13_897_000 picoseconds. + Weight::from_parts(14_828_339, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 293 - .saturating_add(Weight::from_parts(56_475, 0).saturating_mul(s.into())) + // Standard Error: 1_136 + .saturating_add(Weight::from_parts(133_925, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `383 + s * (1 ±0)` + // Measured: `420 + s * (1 ±0)` // Estimated: `6811` - // Minimum execution time: 32_265_000 picoseconds. - Weight::from_parts(32_984_014, 0) + // Minimum execution time: 28_984_000 picoseconds. + Weight::from_parts(29_853_232, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 452 - .saturating_add(Weight::from_parts(59_934, 0).saturating_mul(s.into())) + // Standard Error: 650 + .saturating_add(Weight::from_parts(113_440, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_nis.rs b/polkadot/runtime/rococo/src/weights/pallet_nis.rs index 35dad482129e6c63e0aca0aceb544c66fba78ee7..38b41f3a8e241185e1ccb40332bab8e96a846d2b 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_nis.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_nis.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_nis` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_nis // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,202 +50,186 @@ use core::marker::PhantomData; /// Weight functions for `pallet_nis`. pub struct WeightInfo(PhantomData); impl pallet_nis::WeightInfo for WeightInfo { - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6209 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 44_704_000 picoseconds. - Weight::from_parts(44_933_886, 0) + // Minimum execution time: 39_592_000 picoseconds. + Weight::from_parts(38_234_037, 0) .saturating_add(Weight::from_parts(0, 51487)) - // Standard Error: 712 - .saturating_add(Weight::from_parts(71_570, 0).saturating_mul(l.into())) + // Standard Error: 1_237 + .saturating_add(Weight::from_parts(88_816, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn place_bid_max() -> Weight { // Proof Size summary in bytes: // Measured: `54211` // Estimated: `51487` - // Minimum execution time: 126_544_000 picoseconds. - Weight::from_parts(128_271_000, 0) + // Minimum execution time: 134_847_000 picoseconds. + Weight::from_parts(139_510_000, 0) .saturating_add(Weight::from_parts(0, 51487)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6209 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 47_640_000 picoseconds. - Weight::from_parts(42_214_261, 0) + // Minimum execution time: 43_330_000 picoseconds. + Weight::from_parts(35_097_881, 0) .saturating_add(Weight::from_parts(0, 51487)) - // Standard Error: 732 - .saturating_add(Weight::from_parts(87_277, 0).saturating_mul(l.into())) + // Standard Error: 1_119 + .saturating_add(Weight::from_parts(73_640, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Summary (r:1 w:0) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:0) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn fund_deficit() -> Weight { // Proof Size summary in bytes: // Measured: `225` // Estimated: `3593` - // Minimum execution time: 38_031_000 picoseconds. - Weight::from_parts(38_441_000, 0) + // Minimum execution time: 29_989_000 picoseconds. + Weight::from_parts(30_865_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) fn communify() -> Weight { // Proof Size summary in bytes: - // Measured: `469` + // Measured: `387` // Estimated: `3593` - // Minimum execution time: 69_269_000 picoseconds. - Weight::from_parts(70_000_000, 0) + // Minimum execution time: 58_114_000 picoseconds. + Weight::from_parts(59_540_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) fn privatize() -> Weight { // Proof Size summary in bytes: - // Measured: `659` + // Measured: `543` // Estimated: `3593` - // Minimum execution time: 85_763_000 picoseconds. - Weight::from_parts(86_707_000, 0) + // Minimum execution time: 75_780_000 picoseconds. + Weight::from_parts(77_097_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// 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(67), added: 2542, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// 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(103), added: 2578, mode: `MaxEncodedLen`) fn thaw_private() -> Weight { // Proof Size summary in bytes: // Measured: `387` // Estimated: `3593` - // Minimum execution time: 47_336_000 picoseconds. - Weight::from_parts(47_623_000, 0) + // Minimum execution time: 46_133_000 picoseconds. + Weight::from_parts(47_250_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn thaw_communal() -> Weight { // Proof Size summary in bytes: - // Measured: `604` + // Measured: `488` // Estimated: `3593` - // Minimum execution time: 90_972_000 picoseconds. - Weight::from_parts(92_074_000, 0) + // Minimum execution time: 77_916_000 picoseconds. + Weight::from_parts(79_427_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn process_queues() -> Weight { // Proof Size summary in bytes: // Measured: `6658` // Estimated: `7487` - // Minimum execution time: 21_469_000 picoseconds. - Weight::from_parts(21_983_000, 0) + // Minimum execution time: 22_992_000 picoseconds. + Weight::from_parts(24_112_000, 0) .saturating_add(Weight::from_parts(0, 7487)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) fn process_queue() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `51487` - // Minimum execution time: 4_912_000 picoseconds. - Weight::from_parts(5_013_000, 0) + // Minimum execution time: 3_856_000 picoseconds. + Weight::from_parts(4_125_000, 0) .saturating_add(Weight::from_parts(0, 51487)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Nis Receipts (r:0 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:0 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn process_bid() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_048_000 picoseconds. - Weight::from_parts(7_278_000, 0) + // Minimum execution time: 4_344_000 picoseconds. + Weight::from_parts(4_545_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_preimage.rs b/polkadot/runtime/rococo/src/weights/pallet_preimage.rs index e051ebd5bbab84cf98d34853666926a0f281e5c1..7a2b77b84d80cad3067f41a63da845af86e16816 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_preimage.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_preimage.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_preimage` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_preimage // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,184 +50,219 @@ use core::marker::PhantomData; /// Weight functions for `pallet_preimage`. pub struct WeightInfo(PhantomData); impl pallet_preimage::WeightInfo for WeightInfo { - fn ensure_updated(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `193 + n * (91 ±0)` - // Estimated: `3593 + n * (2566 ±0)` - // Minimum execution time: 2_000_000 picoseconds. - Weight::from_parts(2_000_000, 3593) - // Standard Error: 13_720 - .saturating_add(Weight::from_parts(17_309_199, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2566).saturating_mul(n.into())) - } - - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `3556` - // Minimum execution time: 31_040_000 picoseconds. - Weight::from_parts(31_236_000, 0) - .saturating_add(Weight::from_parts(0, 3556)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_974, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `114` + // Estimated: `3568` + // Minimum execution time: 40_363_000 picoseconds. + Weight::from_parts(41_052_000, 0) + .saturating_add(Weight::from_parts(0, 3568)) + // Standard Error: 6 + .saturating_add(Weight::from_parts(2_298, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 18_025_000 picoseconds. - Weight::from_parts(18_264_000, 0) + // Minimum execution time: 14_570_000 picoseconds. + Weight::from_parts(14_890_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_974, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_364, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 17_122_000 picoseconds. - Weight::from_parts(17_332_000, 0) + // Minimum execution time: 13_933_000 picoseconds. + Weight::from_parts(14_290_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_968, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_349, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `361` - // Estimated: `3556` - // Minimum execution time: 38_218_000 picoseconds. - Weight::from_parts(39_841_000, 0) - .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `315` + // Estimated: `3568` + // Minimum execution time: 54_373_000 picoseconds. + Weight::from_parts(58_205_000, 0) + .saturating_add(Weight::from_parts(0, 3568)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3556` - // Minimum execution time: 23_217_000 picoseconds. - Weight::from_parts(24_246_000, 0) + // Minimum execution time: 24_267_000 picoseconds. + Weight::from_parts(27_063_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `260` // Estimated: `3556` - // Minimum execution time: 21_032_000 picoseconds. - Weight::from_parts(21_844_000, 0) + // Minimum execution time: 25_569_000 picoseconds. + Weight::from_parts(27_895_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3556` - // Minimum execution time: 13_954_000 picoseconds. - Weight::from_parts(14_501_000, 0) + // Minimum execution time: 14_182_000 picoseconds. + Weight::from_parts(16_098_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `114` // Estimated: `3556` - // Minimum execution time: 14_874_000 picoseconds. - Weight::from_parts(15_380_000, 0) + // Minimum execution time: 14_681_000 picoseconds. + Weight::from_parts(15_549_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_requested_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 10_199_000 picoseconds. - Weight::from_parts(10_493_000, 0) + // Minimum execution time: 9_577_000 picoseconds. + Weight::from_parts(10_146_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unrequest_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3556` - // Minimum execution time: 21_772_000 picoseconds. - Weight::from_parts(22_554_000, 0) + // Minimum execution time: 21_003_000 picoseconds. + Weight::from_parts(23_549_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 10_115_000 picoseconds. - Weight::from_parts(10_452_000, 0) + // Minimum execution time: 9_507_000 picoseconds. + Weight::from_parts(10_013_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_multi_referenced_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 10_031_000 picoseconds. - Weight::from_parts(10_310_000, 0) + // Minimum execution time: 9_293_000 picoseconds. + Weight::from_parts(10_055_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Preimage::StatusFor` (r:1023 w:1023) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1023 w:1023) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1023 w:1023) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:0 w:1023) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1024]`. + fn ensure_updated(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (227 ±0)` + // Estimated: `990 + n * (2603 ±0)` + // Minimum execution time: 48_846_000 picoseconds. + Weight::from_parts(49_378_000, 0) + .saturating_add(Weight::from_parts(0, 990)) + // Standard Error: 38_493 + .saturating_add(Weight::from_parts(47_418_285, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(n.into())) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_proxy.rs b/polkadot/runtime/rococo/src/weights/pallet_proxy.rs index d9737a85c05ad2b55759c141539f34fc60d3c808..c92025930950e61c3d4d253b2ae33e153ef70ccb 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_proxy.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_proxy.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_proxy` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_proxy // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,172 +50,176 @@ 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) + /// 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: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 15_956_000 picoseconds. - Weight::from_parts(16_300_358, 0) + // Minimum execution time: 11_267_000 picoseconds. + Weight::from_parts(11_798_007, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 652 - .saturating_add(Weight::from_parts(30_807, 0).saturating_mul(p.into())) + // Standard Error: 858 + .saturating_add(Weight::from_parts(43_735, 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) + /// 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: `554 + a * (68 ±0) + p * (37 ±0)` + // Measured: `416 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(37_858_207, 0) + // Minimum execution time: 32_791_000 picoseconds. + Weight::from_parts(32_776_904, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_868 - .saturating_add(Weight::from_parts(148_967, 0).saturating_mul(a.into())) - // Standard Error: 1_930 - .saturating_add(Weight::from_parts(13_017, 0).saturating_mul(p.into())) + // Standard Error: 2_382 + .saturating_add(Weight::from_parts(143_857, 0).saturating_mul(a.into())) + // Standard Error: 2_461 + .saturating_add(Weight::from_parts(40_024, 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) + /// 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 { + fn remove_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `469 + a * (68 ±0)` + // Measured: `331 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 24_642_000 picoseconds. - Weight::from_parts(25_526_588, 0) + // Minimum execution time: 21_831_000 picoseconds. + Weight::from_parts(22_479_938, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_138 - .saturating_add(Weight::from_parts(131_157, 0).saturating_mul(a.into())) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(146_532, 0).saturating_mul(a.into())) + // Standard Error: 1_796 + .saturating_add(Weight::from_parts(7_499, 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) + /// 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 { + fn reject_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `469 + a * (68 ±0)` + // Measured: `331 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 24_377_000 picoseconds. - Weight::from_parts(25_464_033, 0) + // Minimum execution time: 21_776_000 picoseconds. + Weight::from_parts(22_762_843, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_116 - .saturating_add(Weight::from_parts(130_722, 0).saturating_mul(a.into())) + // Standard Error: 1_402 + .saturating_add(Weight::from_parts(137_512, 0).saturating_mul(a.into())) + // Standard Error: 1_449 + .saturating_add(Weight::from_parts(3_645, 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) + /// 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: `486 + a * (68 ±0) + p * (37 ±0)` + // Measured: `348 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 34_202_000 picoseconds. - Weight::from_parts(34_610_079, 0) + // Minimum execution time: 29_108_000 picoseconds. + Weight::from_parts(29_508_910, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_234 - .saturating_add(Weight::from_parts(134_197, 0).saturating_mul(a.into())) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(15_970, 0).saturating_mul(p.into())) + // Standard Error: 2_268 + .saturating_add(Weight::from_parts(144_770, 0).saturating_mul(a.into())) + // Standard Error: 2_343 + .saturating_add(Weight::from_parts(25_851, 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) + /// 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: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_492_000 picoseconds. - Weight::from_parts(25_984_867, 0) + // Minimum execution time: 18_942_000 picoseconds. + Weight::from_parts(19_518_812, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 893 - .saturating_add(Weight::from_parts(51_868, 0).saturating_mul(p.into())) + // Standard Error: 1_078 + .saturating_add(Weight::from_parts(46_147, 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) + /// 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: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_492_000 picoseconds. - Weight::from_parts(26_283_445, 0) + // Minimum execution time: 18_993_000 picoseconds. + Weight::from_parts(19_871_741, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 1_442 - .saturating_add(Weight::from_parts(53_504, 0).saturating_mul(p.into())) + // Standard Error: 1_883 + .saturating_add(Weight::from_parts(46_033, 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) + /// 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: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 22_083_000 picoseconds. - Weight::from_parts(22_688_835, 0) + // Minimum execution time: 17_849_000 picoseconds. + Weight::from_parts(18_776_170, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 994 - .saturating_add(Weight::from_parts(32_994, 0).saturating_mul(p.into())) + // Standard Error: 1_239 + .saturating_add(Weight::from_parts(27_960, 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) + /// 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: `239` + // Measured: `101` // Estimated: `4706` - // Minimum execution time: 27_042_000 picoseconds. - Weight::from_parts(27_624_587, 0) + // Minimum execution time: 20_049_000 picoseconds. + Weight::from_parts(20_881_515, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 671 - .saturating_add(Weight::from_parts(5_888, 0).saturating_mul(p.into())) + // Standard Error: 952 + .saturating_add(Weight::from_parts(5_970, 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) + /// 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: `264 + p * (37 ±0)` + // Measured: `126 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 23_396_000 picoseconds. - Weight::from_parts(24_003_080, 0) + // Minimum execution time: 18_528_000 picoseconds. + Weight::from_parts(19_384_189, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 684 - .saturating_add(Weight::from_parts(29_878, 0).saturating_mul(p.into())) + // Standard Error: 1_106 + .saturating_add(Weight::from_parts(35_698, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs b/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs index 8a556c3a248ef46fdb2b5b5449c4b0c75972a9fd..fa2decb16716656c9329050c6a211b30ccf24769 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs @@ -16,27 +16,28 @@ //! Autogenerated weights for `pallet_ranked_collective` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-xerhrdyb-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: `Some(Wasm)`, WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_ranked_collective // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=pallet_ranked_collective -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -61,8 +62,8 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 17_632_000 picoseconds. - Weight::from_parts(18_252_000, 0) + // Minimum execution time: 13_428_000 picoseconds. + Weight::from_parts(14_019_000, 0) .saturating_add(Weight::from_parts(0, 3507)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) @@ -71,24 +72,24 @@ impl pallet_ranked_collective::WeightInfo for WeightInf /// Proof: `FellowshipCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) /// Storage: `FellowshipCollective::MemberCount` (r:11 w:11) /// Proof: `FellowshipCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `FellowshipCollective::IdToIndex` (r:11 w:11) + /// Storage: `FellowshipCollective::IdToIndex` (r:11 w:22) /// Proof: `FellowshipCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `FellowshipCollective::IndexToId` (r:11 w:11) + /// Storage: `FellowshipCollective::IndexToId` (r:11 w:22) /// Proof: `FellowshipCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn remove_member(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `517 + r * (281 ±0)` + // Measured: `516 + r * (281 ±0)` // Estimated: `3519 + r * (2529 ±0)` - // Minimum execution time: 27_960_000 picoseconds. - Weight::from_parts(30_632_408, 0) + // Minimum execution time: 28_566_000 picoseconds. + Weight::from_parts(29_346_952, 0) .saturating_add(Weight::from_parts(0, 3519)) - // Standard Error: 22_806 - .saturating_add(Weight::from_parts(13_000_901, 0).saturating_mul(r.into())) + // Standard Error: 21_068 + .saturating_add(Weight::from_parts(14_471_237, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(4)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 2529).saturating_mul(r.into())) } /// Storage: `FellowshipCollective::Members` (r:1 w:1) @@ -104,11 +105,11 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `214 + r * (17 ±0)` // Estimated: `3507` - // Minimum execution time: 19_900_000 picoseconds. - Weight::from_parts(20_908_316, 0) + // Minimum execution time: 16_161_000 picoseconds. + Weight::from_parts(16_981_334, 0) .saturating_add(Weight::from_parts(0, 3507)) - // Standard Error: 4_878 - .saturating_add(Weight::from_parts(330_385, 0).saturating_mul(r.into())) + // Standard Error: 4_596 + .saturating_add(Weight::from_parts(313_386, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -116,22 +117,22 @@ impl pallet_ranked_collective::WeightInfo for WeightInf /// Proof: `FellowshipCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) /// Storage: `FellowshipCollective::MemberCount` (r:1 w:1) /// Proof: `FellowshipCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `FellowshipCollective::IdToIndex` (r:1 w:1) + /// Storage: `FellowshipCollective::IdToIndex` (r:1 w:2) /// Proof: `FellowshipCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) - /// Storage: `FellowshipCollective::IndexToId` (r:1 w:1) + /// Storage: `FellowshipCollective::IndexToId` (r:1 w:2) /// Proof: `FellowshipCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn demote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `532 + r * (72 ±0)` // Estimated: `3519` - // Minimum execution time: 27_697_000 picoseconds. - Weight::from_parts(30_341_815, 0) + // Minimum execution time: 28_406_000 picoseconds. + Weight::from_parts(31_178_557, 0) .saturating_add(Weight::from_parts(0, 3519)) - // Standard Error: 17_010 - .saturating_add(Weight::from_parts(642_213, 0).saturating_mul(r.into())) + // Standard Error: 17_737 + .saturating_add(Weight::from_parts(627_757, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `FellowshipCollective::Members` (r:1 w:0) /// Proof: `FellowshipCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) @@ -141,15 +142,17 @@ impl pallet_ranked_collective::WeightInfo for WeightInf /// Proof: `FellowshipCollective::Voting` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote() -> Weight { // Proof Size summary in bytes: - // Measured: `638` + // Measured: `603` // Estimated: `83866` - // Minimum execution time: 48_275_000 picoseconds. - Weight::from_parts(49_326_000, 0) + // Minimum execution time: 41_164_000 picoseconds. + Weight::from_parts(42_163_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:0) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -160,16 +163,34 @@ impl pallet_ranked_collective::WeightInfo for WeightInf /// The range of component `n` is `[0, 100]`. fn cleanup_poll(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `434 + n * (50 ±0)` + // Measured: `400 + n * (50 ±0)` // Estimated: `4365 + n * (2540 ±0)` - // Minimum execution time: 15_506_000 picoseconds. - Weight::from_parts(17_634_029, 0) + // Minimum execution time: 13_183_000 picoseconds. + Weight::from_parts(15_604_064, 0) .saturating_add(Weight::from_parts(0, 4365)) - // Standard Error: 2_117 - .saturating_add(Weight::from_parts(1_126_879, 0).saturating_mul(n.into())) + // Standard Error: 2_018 + .saturating_add(Weight::from_parts(1_101_088, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2540).saturating_mul(n.into())) } + /// Storage: `FellowshipCollective::Members` (r:2 w:2) + /// Proof: `FellowshipCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `FellowshipCollective::MemberCount` (r:2 w:2) + /// Proof: `FellowshipCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `FellowshipCollective::IdToIndex` (r:2 w:4) + /// Proof: `FellowshipCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `FellowshipCollective::IndexToId` (r:0 w:2) + /// Proof: `FellowshipCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + fn exchange_member() -> Weight { + // Proof Size summary in bytes: + // Measured: `337` + // Estimated: `6048` + // Minimum execution time: 43_603_000 picoseconds. + Weight::from_parts(44_809_000, 0) + .saturating_add(Weight::from_parts(0, 6048)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(10)) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_recovery.rs b/polkadot/runtime/rococo/src/weights/pallet_recovery.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed79aa2b1f175d65d33efad4901d1800fb5cbc28 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_recovery.rs @@ -0,0 +1,186 @@ +// 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_recovery` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_recovery +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --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_recovery`. +pub struct WeightInfo(PhantomData); +impl pallet_recovery::WeightInfo for WeightInfo { + /// Storage: `Recovery::Proxy` (r:1 w:0) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + fn as_recovered() -> Weight { + // Proof Size summary in bytes: + // Measured: `215` + // Estimated: `3545` + // Minimum execution time: 7_899_000 picoseconds. + Weight::from_parts(8_205_000, 0) + .saturating_add(Weight::from_parts(0, 3545)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Recovery::Proxy` (r:0 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + fn set_recovered() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_258_000 picoseconds. + Weight::from_parts(6_494_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn create_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3816` + // Minimum execution time: 19_369_000 picoseconds. + Weight::from_parts(20_185_132, 0) + .saturating_add(Weight::from_parts(0, 3816)) + // Standard Error: 4_275 + .saturating_add(Weight::from_parts(78_024, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + fn initiate_recovery() -> Weight { + // Proof Size summary in bytes: + // Measured: `206` + // Estimated: `3854` + // Minimum execution time: 22_425_000 picoseconds. + Weight::from_parts(23_171_000, 0) + .saturating_add(Weight::from_parts(0, 3854)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn vouch_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `294 + n * (64 ±0)` + // Estimated: `3854` + // Minimum execution time: 17_308_000 picoseconds. + Weight::from_parts(18_118_782, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 4_309 + .saturating_add(Weight::from_parts(126_278, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn claim_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `326 + n * (64 ±0)` + // Estimated: `3854` + // Minimum execution time: 20_755_000 picoseconds. + Weight::from_parts(21_821_713, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 4_550 + .saturating_add(Weight::from_parts(101_916, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, 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 `n` is `[1, 9]`. + fn close_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `447 + n * (32 ±0)` + // Estimated: `3854` + // Minimum execution time: 29_957_000 picoseconds. + Weight::from_parts(31_010_309, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 5_913 + .saturating_add(Weight::from_parts(110_070, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn remove_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `204 + n * (32 ±0)` + // Estimated: `3854` + // Minimum execution time: 24_430_000 picoseconds. + Weight::from_parts(24_462_856, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 13_646 + .saturating_add(Weight::from_parts(507_715, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + fn cancel_recovered() -> Weight { + // Proof Size summary in bytes: + // Measured: `215` + // Estimated: `3545` + // Minimum execution time: 9_686_000 picoseconds. + Weight::from_parts(10_071_000, 0) + .saturating_add(Weight::from_parts(0, 3545)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs b/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs index 96f172230e13f5aed92b47338b2387f4db79fa27..6dfcea2b8327a853927c45aeef76036752650718 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs @@ -16,27 +16,28 @@ //! Autogenerated weights for `pallet_referenda` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-xerhrdyb-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: `Some(Wasm)`, WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_referenda // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=pallet_referenda -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -59,10 +60,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `327` + // Measured: `292` // Estimated: `42428` - // Minimum execution time: 29_909_000 picoseconds. - Weight::from_parts(30_645_000, 0) + // Minimum execution time: 24_053_000 picoseconds. + Weight::from_parts(25_121_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) @@ -71,15 +72,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `438` + // Measured: `403` // Estimated: `83866` - // Minimum execution time: 54_405_000 picoseconds. - Weight::from_parts(55_583_000, 0) + // Minimum execution time: 45_064_000 picoseconds. + Weight::from_parts(46_112_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -89,15 +92,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `2076` + // Measured: `2041` // Estimated: `42428` - // Minimum execution time: 110_477_000 picoseconds. - Weight::from_parts(119_187_000, 0) + // Minimum execution time: 94_146_000 picoseconds. + Weight::from_parts(98_587_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -107,15 +112,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `2117` + // Measured: `2082` // Estimated: `42428` - // Minimum execution time: 111_467_000 picoseconds. - Weight::from_parts(117_758_000, 0) + // Minimum execution time: 93_002_000 picoseconds. + Weight::from_parts(96_924_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -125,15 +132,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `774` + // Measured: `739` // Estimated: `83866` - // Minimum execution time: 191_135_000 picoseconds. - Weight::from_parts(210_535_000, 0) + // Minimum execution time: 160_918_000 picoseconds. + Weight::from_parts(175_603_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -143,24 +152,26 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `639` + // Measured: `604` // Estimated: `83866` - // Minimum execution time: 67_168_000 picoseconds. - Weight::from_parts(68_895_000, 0) + // Minimum execution time: 55_253_000 picoseconds. + Weight::from_parts(56_488_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `351` + // Measured: `317` // Estimated: `4365` - // Minimum execution time: 31_298_000 picoseconds. - Weight::from_parts(32_570_000, 0) + // Minimum execution time: 24_497_000 picoseconds. + Weight::from_parts(25_280_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -169,10 +180,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `201` + // Measured: `167` // Estimated: `4365` - // Minimum execution time: 15_674_000 picoseconds. - Weight::from_parts(16_190_000, 0) + // Minimum execution time: 11_374_000 picoseconds. + Weight::from_parts(11_817_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -181,15 +192,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `348` // Estimated: `83866` - // Minimum execution time: 38_927_000 picoseconds. - Weight::from_parts(40_545_000, 0) + // Minimum execution time: 31_805_000 picoseconds. + Weight::from_parts(32_622_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -197,15 +210,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `FellowshipReferenda::MetadataOf` (r:1 w:0) /// Proof: `FellowshipReferenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `484` + // Measured: `449` // Estimated: `83866` - // Minimum execution time: 80_209_000 picoseconds. - Weight::from_parts(82_084_000, 0) + // Minimum execution time: 62_364_000 picoseconds. + Weight::from_parts(63_798_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::TrackQueue` (r:1 w:0) /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) @@ -213,10 +228,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `140` // Estimated: `4277` - // Minimum execution time: 9_520_000 picoseconds. - Weight::from_parts(10_088_000, 0) + // Minimum execution time: 8_811_000 picoseconds. + Weight::from_parts(9_224_000, 0) .saturating_add(Weight::from_parts(0, 4277)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -231,10 +246,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `2376` + // Measured: `2341` // Estimated: `42428` - // Minimum execution time: 93_893_000 picoseconds. - Weight::from_parts(101_065_000, 0) + // Minimum execution time: 83_292_000 picoseconds. + Weight::from_parts(89_114_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -249,10 +264,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `2362` + // Measured: `2327` // Estimated: `42428` - // Minimum execution time: 98_811_000 picoseconds. - Weight::from_parts(103_590_000, 0) + // Minimum execution time: 84_648_000 picoseconds. + Weight::from_parts(89_332_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -263,10 +278,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `1841` + // Measured: `1807` // Estimated: `4365` - // Minimum execution time: 43_230_000 picoseconds. - Weight::from_parts(46_120_000, 0) + // Minimum execution time: 40_529_000 picoseconds. + Weight::from_parts(45_217_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -277,10 +292,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `1808` + // Measured: `1774` // Estimated: `4365` - // Minimum execution time: 43_092_000 picoseconds. - Weight::from_parts(46_018_000, 0) + // Minimum execution time: 40_894_000 picoseconds. + Weight::from_parts(45_726_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -293,10 +308,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `1824` + // Measured: `1790` // Estimated: `4365` - // Minimum execution time: 49_697_000 picoseconds. - Weight::from_parts(53_795_000, 0) + // Minimum execution time: 48_187_000 picoseconds. + Weight::from_parts(52_655_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -309,10 +324,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `1865` + // Measured: `1831` // Estimated: `4365` - // Minimum execution time: 50_417_000 picoseconds. - Weight::from_parts(53_214_000, 0) + // Minimum execution time: 47_548_000 picoseconds. + Weight::from_parts(51_547_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -323,10 +338,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `335` + // Measured: `300` // Estimated: `42428` - // Minimum execution time: 25_688_000 picoseconds. - Weight::from_parts(26_575_000, 0) + // Minimum execution time: 20_959_000 picoseconds. + Weight::from_parts(21_837_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -337,10 +352,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `348` // Estimated: `42428` - // Minimum execution time: 26_230_000 picoseconds. - Weight::from_parts(27_235_000, 0) + // Minimum execution time: 21_628_000 picoseconds. + Weight::from_parts(22_192_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -349,10 +364,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `242` + // Measured: `208` // Estimated: `4365` - // Minimum execution time: 17_585_000 picoseconds. - Weight::from_parts(18_225_000, 0) + // Minimum execution time: 12_309_000 picoseconds. + Weight::from_parts(12_644_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -367,10 +382,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `584` + // Measured: `549` // Estimated: `42428` - // Minimum execution time: 38_243_000 picoseconds. - Weight::from_parts(39_959_000, 0) + // Minimum execution time: 31_871_000 picoseconds. + Weight::from_parts(33_123_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -385,10 +400,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `719` + // Measured: `684` // Estimated: `42428` - // Minimum execution time: 88_424_000 picoseconds. - Weight::from_parts(92_969_000, 0) + // Minimum execution time: 73_715_000 picoseconds. + Weight::from_parts(79_980_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -401,10 +416,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `770` + // Measured: `735` // Estimated: `42428` - // Minimum execution time: 138_207_000 picoseconds. - Weight::from_parts(151_726_000, 0) + // Minimum execution time: 128_564_000 picoseconds. + Weight::from_parts(138_536_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -417,10 +432,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `755` + // Measured: `720` // Estimated: `42428` - // Minimum execution time: 131_001_000 picoseconds. - Weight::from_parts(148_651_000, 0) + // Minimum execution time: 129_775_000 picoseconds. + Weight::from_parts(139_001_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -433,10 +448,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `770` + // Measured: `735` // Estimated: `42428` - // Minimum execution time: 109_612_000 picoseconds. - Weight::from_parts(143_626_000, 0) + // Minimum execution time: 128_233_000 picoseconds. + Weight::from_parts(135_796_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -449,10 +464,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `776` + // Measured: `741` // Estimated: `42428` - // Minimum execution time: 71_754_000 picoseconds. - Weight::from_parts(77_329_000, 0) + // Minimum execution time: 66_995_000 picoseconds. + Weight::from_parts(72_678_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -467,10 +482,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `776` + // Measured: `741` // Estimated: `83866` - // Minimum execution time: 153_244_000 picoseconds. - Weight::from_parts(169_961_000, 0) + // Minimum execution time: 137_764_000 picoseconds. + Weight::from_parts(152_260_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -483,10 +498,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `772` + // Measured: `737` // Estimated: `42428` - // Minimum execution time: 137_997_000 picoseconds. - Weight::from_parts(157_862_000, 0) + // Minimum execution time: 119_992_000 picoseconds. + Weight::from_parts(134_805_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -495,16 +510,18 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `FellowshipReferenda::MetadataOf` (r:0 w:1) /// Proof: `FellowshipReferenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `458` + // Measured: `424` // Estimated: `4365` - // Minimum execution time: 21_794_000 picoseconds. - Weight::from_parts(22_341_000, 0) + // Minimum execution time: 20_927_000 picoseconds. + Weight::from_parts(21_802_000, 0) .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:0) @@ -513,10 +530,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `319` + // Measured: `285` // Estimated: `4365` - // Minimum execution time: 18_458_000 picoseconds. - Weight::from_parts(19_097_000, 0) + // Minimum execution time: 14_253_000 picoseconds. + Weight::from_parts(15_031_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs b/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs index b7cc5df28b91cc2cc36cade14784b41d7d34ff71..c35925198f9d0d3c615416aa8dbb3e8e371eecca 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs @@ -16,27 +16,28 @@ //! Autogenerated weights for `pallet_referenda` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-xerhrdyb-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: `Some(Wasm)`, WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_referenda // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=pallet_referenda -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -57,10 +58,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `324` + // Measured: `185` // Estimated: `42428` - // Minimum execution time: 39_852_000 picoseconds. - Weight::from_parts(41_610_000, 0) + // Minimum execution time: 28_612_000 picoseconds. + Weight::from_parts(30_060_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) @@ -69,15 +70,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `577` + // Measured: `438` // Estimated: `83866` - // Minimum execution time: 52_588_000 picoseconds. - Weight::from_parts(54_154_000, 0) + // Minimum execution time: 42_827_000 picoseconds. + Weight::from_parts(44_072_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) @@ -87,15 +90,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3334` + // Measured: `3225` // Estimated: `42428` - // Minimum execution time: 70_483_000 picoseconds. - Weight::from_parts(72_731_000, 0) + // Minimum execution time: 56_475_000 picoseconds. + Weight::from_parts(58_888_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) @@ -105,60 +110,62 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3354` + // Measured: `3245` // Estimated: `42428` - // Minimum execution time: 68_099_000 picoseconds. - Weight::from_parts(71_560_000, 0) + // Minimum execution time: 56_542_000 picoseconds. + Weight::from_parts(58_616_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `577` + // Measured: `438` // Estimated: `83866` - // Minimum execution time: 64_357_000 picoseconds. - Weight::from_parts(66_081_000, 0) + // Minimum execution time: 51_218_000 picoseconds. + Weight::from_parts(53_148_000, 0) .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `577` + // Measured: `438` // Estimated: `83866` - // Minimum execution time: 62_709_000 picoseconds. - Weight::from_parts(64_534_000, 0) + // Minimum execution time: 49_097_000 picoseconds. + Weight::from_parts(50_796_000, 0) .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `279` // Estimated: `4401` - // Minimum execution time: 31_296_000 picoseconds. - Weight::from_parts(32_221_000, 0) + // Minimum execution time: 23_720_000 picoseconds. + Weight::from_parts(24_327_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -167,10 +174,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `407` + // Measured: `269` // Estimated: `4401` - // Minimum execution time: 31_209_000 picoseconds. - Weight::from_parts(32_168_000, 0) + // Minimum execution time: 24_089_000 picoseconds. + Weight::from_parts(24_556_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -179,15 +186,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `83866` - // Minimum execution time: 38_887_000 picoseconds. - Weight::from_parts(40_193_000, 0) + // Minimum execution time: 29_022_000 picoseconds. + Weight::from_parts(29_590_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) @@ -195,15 +204,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Referenda::MetadataOf` (r:1 w:0) /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `726` + // Measured: `587` // Estimated: `83866` - // Minimum execution time: 106_054_000 picoseconds. - Weight::from_parts(108_318_000, 0) + // Minimum execution time: 81_920_000 picoseconds. + Weight::from_parts(84_492_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::TrackQueue` (r:1 w:0) /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) @@ -211,10 +222,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `240` + // Measured: `102` // Estimated: `5477` - // Minimum execution time: 9_263_000 picoseconds. - Weight::from_parts(9_763_000, 0) + // Minimum execution time: 8_134_000 picoseconds. + Weight::from_parts(8_574_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -223,36 +234,32 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `3254` + // Measured: `3115` // Estimated: `42428` - // Minimum execution time: 50_080_000 picoseconds. - Weight::from_parts(51_858_000, 0) + // Minimum execution time: 39_932_000 picoseconds. + Weight::from_parts(42_086_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::TrackQueue` (r:1 w:1) /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `3254` + // Measured: `3115` // Estimated: `42428` - // Minimum execution time: 53_889_000 picoseconds. - Weight::from_parts(55_959_000, 0) + // Minimum execution time: 42_727_000 picoseconds. + Weight::from_parts(44_280_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) @@ -261,10 +268,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `3077` + // Measured: `2939` // Estimated: `5477` - // Minimum execution time: 23_266_000 picoseconds. - Weight::from_parts(24_624_000, 0) + // Minimum execution time: 20_918_000 picoseconds. + Weight::from_parts(22_180_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -275,10 +282,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `3077` + // Measured: `2939` // Estimated: `5477` - // Minimum execution time: 22_846_000 picoseconds. - Weight::from_parts(24_793_000, 0) + // Minimum execution time: 20_943_000 picoseconds. + Weight::from_parts(21_932_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -291,10 +298,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3081` + // Measured: `2943` // Estimated: `5477` - // Minimum execution time: 28_284_000 picoseconds. - Weight::from_parts(29_940_000, 0) + // Minimum execution time: 25_197_000 picoseconds. + Weight::from_parts(26_083_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -307,10 +314,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3101` + // Measured: `2963` // Estimated: `5477` - // Minimum execution time: 28_133_000 picoseconds. - Weight::from_parts(29_638_000, 0) + // Minimum execution time: 24_969_000 picoseconds. + Weight::from_parts(26_096_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -321,10 +328,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `437` + // Measured: `298` // Estimated: `42428` - // Minimum execution time: 25_710_000 picoseconds. - Weight::from_parts(26_500_000, 0) + // Minimum execution time: 18_050_000 picoseconds. + Weight::from_parts(18_790_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -335,10 +342,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `42428` - // Minimum execution time: 25_935_000 picoseconds. - Weight::from_parts(26_803_000, 0) + // Minimum execution time: 18_357_000 picoseconds. + Weight::from_parts(18_957_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -347,10 +354,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `344` + // Measured: `206` // Estimated: `4401` - // Minimum execution time: 17_390_000 picoseconds. - Weight::from_parts(18_042_000, 0) + // Minimum execution time: 11_479_000 picoseconds. + Weight::from_parts(11_968_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -359,150 +366,136 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `42428` - // Minimum execution time: 35_141_000 picoseconds. - Weight::from_parts(36_318_000, 0) + // Minimum execution time: 24_471_000 picoseconds. + Weight::from_parts(25_440_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `42428` - // Minimum execution time: 37_815_000 picoseconds. - Weight::from_parts(39_243_000, 0) + // Minimum execution time: 26_580_000 picoseconds. + Weight::from_parts(27_570_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `538` + // Measured: `399` // Estimated: `42428` - // Minimum execution time: 30_779_000 picoseconds. - Weight::from_parts(31_845_000, 0) + // Minimum execution time: 24_331_000 picoseconds. + Weight::from_parts(25_291_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `521` + // Measured: `382` // Estimated: `42428` - // Minimum execution time: 31_908_000 picoseconds. - Weight::from_parts(33_253_000, 0) + // Minimum execution time: 24_768_000 picoseconds. + Weight::from_parts(25_746_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `538` + // Measured: `399` // Estimated: `42428` - // Minimum execution time: 28_951_000 picoseconds. - Weight::from_parts(30_004_000, 0) + // Minimum execution time: 23_171_000 picoseconds. + Weight::from_parts(24_161_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `542` + // Measured: `403` // Estimated: `42428` - // Minimum execution time: 27_750_000 picoseconds. - Weight::from_parts(28_588_000, 0) + // Minimum execution time: 22_263_000 picoseconds. + Weight::from_parts(23_062_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Lookup` (r:1 w:1) /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `542` + // Measured: `403` // Estimated: `83866` - // Minimum execution time: 43_950_000 picoseconds. - Weight::from_parts(46_164_000, 0) + // Minimum execution time: 33_710_000 picoseconds. + Weight::from_parts(34_871_000, 0) .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `538` + // Measured: `399` // Estimated: `42428` - // Minimum execution time: 31_050_000 picoseconds. - Weight::from_parts(32_169_000, 0) + // Minimum execution time: 24_260_000 picoseconds. + Weight::from_parts(25_104_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Referenda::MetadataOf` (r:0 w:1) /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `560` + // Measured: `422` // Estimated: `4401` - // Minimum execution time: 21_193_000 picoseconds. - Weight::from_parts(22_116_000, 0) + // Minimum execution time: 19_821_000 picoseconds. + Weight::from_parts(20_641_000, 0) .saturating_add(Weight::from_parts(0, 4401)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) @@ -511,10 +504,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `421` + // Measured: `283` // Estimated: `4401` - // Minimum execution time: 18_065_000 picoseconds. - Weight::from_parts(18_781_000, 0) + // Minimum execution time: 13_411_000 picoseconds. + Weight::from_parts(14_070_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs b/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs index e4732a2d17dca5180a8e07d5cddd19acd55f1b76..5f6b41d2b54ea95b398877b0ebcaccfbfce313ac 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_scheduler` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_scheduler // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,30 +50,30 @@ use core::marker::PhantomData; /// Weight functions for `pallet_scheduler`. pub struct WeightInfo(PhantomData); impl pallet_scheduler::WeightInfo for WeightInfo { - /// Storage: Scheduler IncompleteSince (r:1 w:1) - /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Scheduler::IncompleteSince` (r:1 w:1) + /// Proof: `Scheduler::IncompleteSince` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn service_agendas_base() -> Weight { // Proof Size summary in bytes: - // Measured: `69` + // Measured: `68` // Estimated: `1489` - // Minimum execution time: 4_741_000 picoseconds. - Weight::from_parts(4_939_000, 0) + // Minimum execution time: 3_114_000 picoseconds. + Weight::from_parts(3_245_000, 0) .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 50]`. fn service_agenda_base(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `116 + s * (177 ±0)` + // Measured: `115 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 4_504_000 picoseconds. - Weight::from_parts(7_569_333, 0) + // Minimum execution time: 3_430_000 picoseconds. + Weight::from_parts(6_250_920, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 1_818 - .saturating_add(Weight::from_parts(771_180, 0).saturating_mul(s.into())) + // Standard Error: 1_350 + .saturating_add(Weight::from_parts(333_245, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -78,36 +81,38 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_709_000 picoseconds. - Weight::from_parts(5_929_000, 0) + // Minimum execution time: 3_166_000 picoseconds. + Weight::from_parts(3_295_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Preimage PreimageFor (r:1 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `251 + s * (1 ±0)` // Estimated: `3716 + s * (1 ±0)` - // Minimum execution time: 20_710_000 picoseconds. - Weight::from_parts(20_918_000, 0) + // Minimum execution time: 17_072_000 picoseconds. + Weight::from_parts(17_393_000, 0) .saturating_add(Weight::from_parts(0, 3716)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(1_257, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2)) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_204, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) } - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn service_task_named() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_262_000 picoseconds. - Weight::from_parts(7_412_000, 0) + // Minimum execution time: 4_566_000 picoseconds. + Weight::from_parts(4_775_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -115,90 +120,171 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_774_000 picoseconds. - Weight::from_parts(5_887_000, 0) + // Minimum execution time: 3_180_000 picoseconds. + Weight::from_parts(3_339_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_777_000 picoseconds. - Weight::from_parts(2_865_000, 0) + // Minimum execution time: 1_656_000 picoseconds. + Weight::from_parts(1_829_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_739_000 picoseconds. - Weight::from_parts(2_827_000, 0) + // Minimum execution time: 1_628_000 picoseconds. + Weight::from_parts(1_840_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 49]`. fn schedule(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `116 + s * (177 ±0)` + // Measured: `115 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 14_788_000 picoseconds. - Weight::from_parts(17_705_748, 0) + // Minimum execution time: 9_523_000 picoseconds. + Weight::from_parts(12_482_434, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 1_703 - .saturating_add(Weight::from_parts(760_991, 0).saturating_mul(s.into())) + // Standard Error: 1_663 + .saturating_add(Weight::from_parts(370_122, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 50]`. fn cancel(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `116 + s * (177 ±0)` + // Measured: `115 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 18_716_000 picoseconds. - Weight::from_parts(18_220_022, 0) + // Minimum execution time: 14_649_000 picoseconds. + Weight::from_parts(14_705_132, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 1_508 - .saturating_add(Weight::from_parts(1_357_835, 0).saturating_mul(s.into())) + // Standard Error: 1_126 + .saturating_add(Weight::from_parts(547_438, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 49]`. fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `293 + s * (185 ±0)` + // Measured: `292 + s * (185 ±0)` // Estimated: `42428` - // Minimum execution time: 17_719_000 picoseconds. - Weight::from_parts(21_657_806, 0) + // Minimum execution time: 12_335_000 picoseconds. + Weight::from_parts(16_144_217, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 2_645 - .saturating_add(Weight::from_parts(794_184, 0).saturating_mul(s.into())) + // Standard Error: 3_533 + .saturating_add(Weight::from_parts(413_823, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 50]`. fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `319 + s * (185 ±0)` + // Measured: `318 + s * (185 ±0)` // Estimated: `42428` - // Minimum execution time: 20_225_000 picoseconds. - Weight::from_parts(20_494_405, 0) + // Minimum execution time: 16_906_000 picoseconds. + Weight::from_parts(17_846_662, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 1_890 - .saturating_add(Weight::from_parts(1_379_025, 0).saturating_mul(s.into())) + // Standard Error: 2_687 + .saturating_add(Weight::from_parts(613_356, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 50]`. + fn schedule_retry(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `155` + // Estimated: `42428` + // Minimum execution time: 8_988_000 picoseconds. + Weight::from_parts(9_527_838, 0) + .saturating_add(Weight::from_parts(0, 42428)) + // Standard Error: 523 + .saturating_add(Weight::from_parts(25_453, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `8965` + // Estimated: `42428` + // Minimum execution time: 23_337_000 picoseconds. + Weight::from_parts(24_255_000, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `9643` + // Estimated: `42428` + // Minimum execution time: 30_704_000 picoseconds. + Weight::from_parts(31_646_000, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `8977` + // Estimated: `42428` + // Minimum execution time: 22_279_000 picoseconds. + Weight::from_parts(23_106_000, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `9655` + // Estimated: `42428` + // Minimum execution time: 29_649_000 picoseconds. + Weight::from_parts(30_472_000, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_sudo.rs b/polkadot/runtime/rococo/src/weights/pallet_sudo.rs index 694174954fc78b1eeb4d93f78e6b26d5bab22b83..ecc31dc3fa9df6c17a5541fcd5a6d957f6ca2e2d 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_sudo.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_sudo.rs @@ -16,24 +16,26 @@ //! Autogenerated weights for `pallet_sudo` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, 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-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_sudo // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_sudo -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -54,8 +56,8 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 8_432_000 picoseconds. - Weight::from_parts(8_757_000, 0) + // Minimum execution time: 8_336_000 picoseconds. + Weight::from_parts(8_569_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +68,8 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 9_167_000 picoseconds. - Weight::from_parts(9_397_000, 0) + // Minimum execution time: 8_858_000 picoseconds. + Weight::from_parts(9_238_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) } @@ -77,8 +79,8 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 9_133_000 picoseconds. - Weight::from_parts(9_573_000, 0) + // Minimum execution time: 8_921_000 picoseconds. + Weight::from_parts(9_324_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) } @@ -88,10 +90,21 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 7_374_000 picoseconds. - Weight::from_parts(7_702_000, 0) + // Minimum execution time: 7_398_000 picoseconds. + Weight::from_parts(7_869_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `132` + // Estimated: `1517` + // Minimum execution time: 3_146_000 picoseconds. + Weight::from_parts(3_314_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs b/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs index 1bb2e227ab7d540ba8cfd9e4609e957bf025d57c..7d79621b9e65f959221fe382254db93db6237ff7 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_timestamp` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_timestamp // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,26 +50,26 @@ use core::marker::PhantomData; /// Weight functions for `pallet_timestamp`. pub struct WeightInfo(PhantomData); impl pallet_timestamp::WeightInfo for WeightInfo { - /// Storage: Timestamp Now (r:1 w:1) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe CurrentSlot (r:1 w:0) - /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::CurrentSlot` (r:1 w:0) + /// Proof: `Babe::CurrentSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set() -> Weight { // Proof Size summary in bytes: - // Measured: `311` + // Measured: `137` // Estimated: `1493` - // Minimum execution time: 10_103_000 picoseconds. - Weight::from_parts(10_597_000, 0) + // Minimum execution time: 5_596_000 picoseconds. + Weight::from_parts(5_823_000, 0) .saturating_add(Weight::from_parts(0, 1493)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } fn on_finalize() -> Weight { // Proof Size summary in bytes: - // Measured: `94` + // Measured: `57` // Estimated: `0` - // Minimum execution time: 4_718_000 picoseconds. - Weight::from_parts(4_839_000, 0) + // Minimum execution time: 2_777_000 picoseconds. + Weight::from_parts(2_900_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_transaction_payment.rs b/polkadot/runtime/rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..44dfab289fb2dd22fd6bc81cfaae81769d14313f --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,68 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_transaction_payment +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `1737` + // Minimum execution time: 33_070_000 picoseconds. + Weight::from_parts(33_730_000, 0) + .saturating_add(Weight::from_parts(0, 1737)) + .saturating_add(T::DbWeight::get().reads(3)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/pallet_treasury.rs b/polkadot/runtime/rococo/src/weights/pallet_treasury.rs index 144e9d5b872382b7381e2d77c6fb08a8fbece4fa..acf09989afca1fe41fb5648d3b500116b1acdeed 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_treasury.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_treasury.rs @@ -16,25 +16,28 @@ //! Autogenerated weights for `pallet_treasury` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-07, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cob`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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/debug/polkadot +// ./target/production/polkadot // benchmark // pallet // --chain=rococo-dev // --steps=50 -// --repeat=2 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_treasury // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --output=./runtime/rococo/src/weights/ -// --header=./file_header.txt +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,176 +50,168 @@ use core::marker::PhantomData; /// Weight functions for `pallet_treasury`. pub struct WeightInfo(PhantomData); impl pallet_treasury::WeightInfo for WeightInfo { - /// Storage: Treasury ProposalCount (r:1 w:1) - /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:0 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) fn spend_local() -> Weight { // Proof Size summary in bytes: - // Measured: `42` + // Measured: `142` // Estimated: `1887` - // Minimum execution time: 177_000_000 picoseconds. - Weight::from_parts(191_000_000, 0) + // Minimum execution time: 9_928_000 picoseconds. + Weight::from_parts(10_560_000, 0) .saturating_add(Weight::from_parts(0, 1887)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Treasury ProposalCount (r:1 w:1) - /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:0 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) fn propose_spend() -> Weight { // Proof Size summary in bytes: - // Measured: `143` + // Measured: `243` // Estimated: `1489` - // Minimum execution time: 354_000_000 picoseconds. - Weight::from_parts(376_000_000, 0) + // Minimum execution time: 20_714_000 picoseconds. + Weight::from_parts(21_137_000, 0) .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Treasury Proposals (r:1 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Treasury::Proposals` (r:1 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn reject_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `301` + // Measured: `401` // Estimated: `3593` - // Minimum execution time: 547_000_000 picoseconds. - Weight::from_parts(550_000_000, 0) + // Minimum execution time: 31_665_000 picoseconds. + Weight::from_parts(32_442_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Treasury Proposals (r:1 w:0) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Proposals` (r:1 w:0) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 99]`. fn approve_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `470 + p * (8 ±0)` + // Measured: `570 + p * (8 ±0)` // Estimated: `3573` - // Minimum execution time: 104_000_000 picoseconds. - Weight::from_parts(121_184_402, 0) + // Minimum execution time: 6_988_000 picoseconds. + Weight::from_parts(11_464_972, 0) .saturating_add(Weight::from_parts(0, 3573)) - // Standard Error: 42_854 - .saturating_add(Weight::from_parts(153_112, 0).saturating_mul(p.into())) + // Standard Error: 1_722 + .saturating_add(Weight::from_parts(84_536, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn remove_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `127` + // Measured: `227` // Estimated: `1887` - // Minimum execution time: 80_000_000 picoseconds. - Weight::from_parts(82_000_000, 0) + // Minimum execution time: 5_386_000 picoseconds. + Weight::from_parts(5_585_000, 0) .saturating_add(Weight::from_parts(0, 1887)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Treasury Deactivated (r:1 w:1) - /// Proof: Treasury Deactivated (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:1) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:99 w:99) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: System Account (r:199 w:199) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Deactivated` (r:1 w:1) + /// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:99 w:99) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:199 w:199) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 99]`. fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `331 + p * (251 ±0)` + // Measured: `431 + p * (251 ±0)` // Estimated: `3593 + p * (5206 ±0)` - // Minimum execution time: 887_000_000 picoseconds. - Weight::from_parts(828_616_021, 0) + // Minimum execution time: 43_737_000 picoseconds. + Weight::from_parts(39_883_021, 0) .saturating_add(Weight::from_parts(0, 3593)) - // Standard Error: 695_351 - .saturating_add(Weight::from_parts(566_114_524, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Standard Error: 12_917 + .saturating_add(Weight::from_parts(31_796_205, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:0) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) - /// Storage: Treasury SpendCount (r:1 w:1) - /// Proof: Treasury SpendCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Spends (r:0 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:0) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) + /// Storage: `Treasury::SpendCount` (r:1 w:1) + /// Proof: `Treasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Spends` (r:0 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) fn spend() -> Weight { // Proof Size summary in bytes: - // Measured: `114` - // Estimated: `4702` - // Minimum execution time: 208_000_000 picoseconds. - Weight::from_parts(222_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `215` + // Estimated: `4703` + // Minimum execution time: 16_829_000 picoseconds. + Weight::from_parts(17_251_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) - /// Storage: XcmPallet QueryCounter (r:1 w:1) - /// Proof Skipped: XcmPallet QueryCounter (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet Queries (r:0 w:1) - /// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) + /// Storage: `XcmPallet::QueryCounter` (r:1 w:1) + /// Proof: `XcmPallet::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::Queries` (r:0 w:1) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `737` - // Estimated: `5313` - // Minimum execution time: 551_000_000 picoseconds. - Weight::from_parts(569_000_000, 0) - .saturating_add(Weight::from_parts(0, 5313)) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(6)) + // Measured: `458` + // Estimated: `5318` + // Minimum execution time: 41_554_000 picoseconds. + Weight::from_parts(42_451_000, 0) + .saturating_add(Weight::from_parts(0, 5318)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) - /// Storage: XcmPallet Queries (r:1 w:1) - /// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) + /// Storage: `XcmPallet::Queries` (r:1 w:1) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn check_status() -> Weight { // Proof Size summary in bytes: - // Measured: `442` - // Estimated: `5313` - // Minimum execution time: 245_000_000 picoseconds. - Weight::from_parts(281_000_000, 0) - .saturating_add(Weight::from_parts(0, 5313)) + // Measured: `306` + // Estimated: `5318` + // Minimum execution time: 22_546_000 picoseconds. + Weight::from_parts(23_151_000, 0) + .saturating_add(Weight::from_parts(0, 5318)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) fn void_spend() -> Weight { // Proof Size summary in bytes: - // Measured: `172` - // Estimated: `5313` - // Minimum execution time: 147_000_000 picoseconds. - Weight::from_parts(160_000_000, 0) - .saturating_add(Weight::from_parts(0, 5313)) + // Measured: `278` + // Estimated: `5318` + // Minimum execution time: 12_169_000 picoseconds. + Weight::from_parts(12_484_000, 0) + .saturating_add(Weight::from_parts(0, 5318)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_utility.rs b/polkadot/runtime/rococo/src/weights/pallet_utility.rs index f50f60eaad7fec2c09e9d5620db0551f1c7b1364..6f2a374247f8f28d1deb281b8256393a74294fad 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_utility.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_utility.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_utility` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_utility // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,18 +55,18 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_738_000 picoseconds. - Weight::from_parts(2_704_821, 0) + // Minimum execution time: 4_041_000 picoseconds. + Weight::from_parts(5_685_496, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_999 - .saturating_add(Weight::from_parts(4_627_278, 0).saturating_mul(c.into())) + // Standard Error: 810 + .saturating_add(Weight::from_parts(3_177_197, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_294_000 picoseconds. - Weight::from_parts(5_467_000, 0) + // Minimum execution time: 3_667_000 picoseconds. + Weight::from_parts(3_871_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `c` is `[0, 1000]`. @@ -71,18 +74,18 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_828_000 picoseconds. - Weight::from_parts(4_650_678, 0) + // Minimum execution time: 4_116_000 picoseconds. + Weight::from_parts(6_453_932, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_789 - .saturating_add(Weight::from_parts(4_885_004, 0).saturating_mul(c.into())) + // Standard Error: 825 + .saturating_add(Weight::from_parts(3_366_112, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_020_000 picoseconds. - Weight::from_parts(9_205_000, 0) + // Minimum execution time: 5_630_000 picoseconds. + Weight::from_parts(5_956_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `c` is `[0, 1000]`. @@ -90,10 +93,10 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_852_000 picoseconds. - Weight::from_parts(20_703_134, 0) + // Minimum execution time: 4_165_000 picoseconds. + Weight::from_parts(5_442_561, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_924 - .saturating_add(Weight::from_parts(4_604_529, 0).saturating_mul(c.into())) + // Standard Error: 460 + .saturating_add(Weight::from_parts(3_173_577, 0).saturating_mul(c.into())) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_vesting.rs b/polkadot/runtime/rococo/src/weights/pallet_vesting.rs index 2596207d5837df6776177b71480c57af61ac02ce..c21ab0877742019b238adf33bec4a378d4cb82ea 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_vesting.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_vesting.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_vesting` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_vesting // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,143 +50,143 @@ use core::marker::PhantomData; /// Weight functions for `pallet_vesting`. pub struct WeightInfo(PhantomData); impl pallet_vesting::WeightInfo for WeightInfo { - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `277 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 32_820_000 picoseconds. - Weight::from_parts(31_640_992, 0) + // Minimum execution time: 29_288_000 picoseconds. + Weight::from_parts(29_095_507, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 449 - .saturating_add(Weight::from_parts(45_254, 0).saturating_mul(l.into())) - // Standard Error: 800 - .saturating_add(Weight::from_parts(72_178, 0).saturating_mul(s.into())) + // Standard Error: 1_679 + .saturating_add(Weight::from_parts(33_164, 0).saturating_mul(l.into())) + // Standard Error: 2_988 + .saturating_add(Weight::from_parts(67_092, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `277 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 36_054_000 picoseconds. - Weight::from_parts(35_825_428, 0) + // Minimum execution time: 31_003_000 picoseconds. + Weight::from_parts(30_528_438, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 749 - .saturating_add(Weight::from_parts(31_738, 0).saturating_mul(l.into())) - // Standard Error: 1_333 - .saturating_add(Weight::from_parts(40_580, 0).saturating_mul(s.into())) + // Standard Error: 1_586 + .saturating_add(Weight::from_parts(35_429, 0).saturating_mul(l.into())) + // Standard Error: 2_823 + .saturating_add(Weight::from_parts(76_505, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, 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 `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `380 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 35_440_000 picoseconds. - Weight::from_parts(34_652_647, 0) + // Minimum execution time: 31_269_000 picoseconds. + Weight::from_parts(30_661_898, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 517 - .saturating_add(Weight::from_parts(41_942, 0).saturating_mul(l.into())) - // Standard Error: 920 - .saturating_add(Weight::from_parts(66_074, 0).saturating_mul(s.into())) + // Standard Error: 1_394 + .saturating_add(Weight::from_parts(39_300, 0).saturating_mul(l.into())) + // Standard Error: 2_480 + .saturating_add(Weight::from_parts(78_849, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, 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 `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `380 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 38_880_000 picoseconds. - Weight::from_parts(39_625_819, 0) + // Minimum execution time: 33_040_000 picoseconds. + Weight::from_parts(32_469_674, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 1_032 - .saturating_add(Weight::from_parts(29_856, 0).saturating_mul(l.into())) - // Standard Error: 1_837 - .saturating_add(Weight::from_parts(6_210, 0).saturating_mul(s.into())) + // Standard Error: 1_418 + .saturating_add(Weight::from_parts(44_206, 0).saturating_mul(l.into())) + // Standard Error: 2_523 + .saturating_add(Weight::from_parts(74_224, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `451 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 68_294_000 picoseconds. - Weight::from_parts(68_313_394, 0) + // Minimum execution time: 62_032_000 picoseconds. + Weight::from_parts(63_305_621, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 983 - .saturating_add(Weight::from_parts(48_156, 0).saturating_mul(l.into())) - // Standard Error: 1_750 - .saturating_add(Weight::from_parts(87_719, 0).saturating_mul(s.into())) + // Standard Error: 2_277 + .saturating_add(Weight::from_parts(42_767, 0).saturating_mul(l.into())) + // Standard Error: 4_051 + .saturating_add(Weight::from_parts(65_487, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `554 + l * (25 ±0) + s * (36 ±0)` // Estimated: `6196` - // Minimum execution time: 70_529_000 picoseconds. - Weight::from_parts(70_619_962, 0) + // Minimum execution time: 63_303_000 picoseconds. + Weight::from_parts(65_180_847, 0) .saturating_add(Weight::from_parts(0, 6196)) - // Standard Error: 1_259 - .saturating_add(Weight::from_parts(50_685, 0).saturating_mul(l.into())) - // Standard Error: 2_241 - .saturating_add(Weight::from_parts(91_444, 0).saturating_mul(s.into())) + // Standard Error: 2_220 + .saturating_add(Weight::from_parts(28_829, 0).saturating_mul(l.into())) + // Standard Error: 3_951 + .saturating_add(Weight::from_parts(84_970, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,59 +195,70 @@ impl pallet_vesting::WeightInfo for WeightInfo { /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, 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 `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. - fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `555 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `4764` - // Minimum execution time: 41_497_000 picoseconds. - Weight::from_parts(38_763_834, 4764) - // Standard Error: 2_030 - .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) - // Standard Error: 3_750 - .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `378 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 36_428_000 picoseconds. - Weight::from_parts(35_604_430, 0) + // Minimum execution time: 31_440_000 picoseconds. + Weight::from_parts(30_773_053, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 504 - .saturating_add(Weight::from_parts(43_191, 0).saturating_mul(l.into())) - // Standard Error: 931 - .saturating_add(Weight::from_parts(66_795, 0).saturating_mul(s.into())) + // Standard Error: 1_474 + .saturating_add(Weight::from_parts(43_019, 0).saturating_mul(l.into())) + // Standard Error: 2_723 + .saturating_add(Weight::from_parts(73_360, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, 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 `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `378 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 40_696_000 picoseconds. - Weight::from_parts(39_741_284, 0) + // Minimum execution time: 34_221_000 picoseconds. + Weight::from_parts(33_201_125, 0) + .saturating_add(Weight::from_parts(0, 4764)) + // Standard Error: 1_751 + .saturating_add(Weight::from_parts(44_088, 0).saturating_mul(l.into())) + // Standard Error: 3_234 + .saturating_add(Weight::from_parts(86_228, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, 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 `l` is `[0, 49]`. + /// The range of component `s` is `[2, 28]`. + fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `451 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `4764` + // Minimum execution time: 35_553_000 picoseconds. + Weight::from_parts(34_974_083, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 478 - .saturating_add(Weight::from_parts(43_792, 0).saturating_mul(l.into())) - // Standard Error: 883 - .saturating_add(Weight::from_parts(66_540, 0).saturating_mul(s.into())) + // Standard Error: 1_560 + .saturating_add(Weight::from_parts(34_615, 0).saturating_mul(l.into())) + // Standard Error: 2_882 + .saturating_add(Weight::from_parts(83_419, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs b/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs index 7c307deec4c6f8480019b5559117cfc47ee84b40..ec67268d1449952be992bd321154dd676c2a8a4d 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_whitelist` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-aahe6cbd-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_whitelist // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=pallet_whitelist -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,67 +52,75 @@ pub struct WeightInfo(PhantomData); impl pallet_whitelist::WeightInfo for WeightInfo { /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn whitelist_call() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `3556` - // Minimum execution time: 20_035_000 picoseconds. - Weight::from_parts(20_452_000, 0) + // Minimum execution time: 16_686_000 picoseconds. + Weight::from_parts(17_042_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: // Measured: `352` // Estimated: `3556` - // Minimum execution time: 20_247_000 picoseconds. - Weight::from_parts(20_808_000, 0) + // Minimum execution time: 18_250_000 picoseconds. + Weight::from_parts(19_026_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:1 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `428 + n * (1 ±0)` // Estimated: `3892 + n * (1 ±0)` - // Minimum execution time: 32_633_000 picoseconds. - Weight::from_parts(32_855_000, 0) + // Minimum execution time: 28_741_000 picoseconds. + Weight::from_parts(29_024_000, 0) .saturating_add(Weight::from_parts(0, 3892)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_223, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(3)) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_305, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 10000]`. fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `352` // Estimated: `3556` - // Minimum execution time: 23_833_000 picoseconds. - Weight::from_parts(24_698_994, 0) + // Minimum execution time: 21_670_000 picoseconds. + Weight::from_parts(22_561_364, 0) .saturating_add(Weight::from_parts(0, 3556)) // Standard Error: 4 - .saturating_add(Weight::from_parts(1_454, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(Weight::from_parts(1_468, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs index 177407ef7088b5c9bdcc460f36cb7d6485743a71..d5cf33515e6be23e7cde0d892b97c8c1d07d8c2f 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs @@ -16,24 +16,26 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, 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-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_xcm // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_xcm -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -48,10 +50,6 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - fn transfer_assets() -> Weight { - // TODO: run benchmarks - Weight::zero() - } /// 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) @@ -62,36 +60,80 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn send() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 27_328_000 picoseconds. - Weight::from_parts(27_976_000, 0) - .saturating_add(Weight::from_parts(0, 3607)) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 25_521_000 picoseconds. + Weight::from_parts(25_922_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) } + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn teleport_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 16_280_000 picoseconds. - Weight::from_parts(16_904_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 112_185_000 picoseconds. + Weight::from_parts(115_991_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .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`) + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reserve_transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 15_869_000 picoseconds. - Weight::from_parts(16_264_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `232` + // Estimated: `3697` + // Minimum execution time: 108_693_000 picoseconds. + Weight::from_parts(111_853_000, 0) + .saturating_add(Weight::from_parts(0, 3697)) + .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`) + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 113_040_000 picoseconds. + Weight::from_parts(115_635_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } fn execute() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_923_000 picoseconds. - Weight::from_parts(7_432_000, 0) + // Minimum execution time: 6_979_000 picoseconds. + Weight::from_parts(7_342_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::SupportedVersion` (r:0 w:1) @@ -100,8 +142,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_333_000 picoseconds. - Weight::from_parts(7_566_000, 0) + // Minimum execution time: 7_144_000 picoseconds. + Weight::from_parts(7_297_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -109,8 +151,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_219_000 picoseconds. - Weight::from_parts(2_375_000, 0) + // Minimum execution time: 1_886_000 picoseconds. + Weight::from_parts(1_995_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1) @@ -129,11 +171,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 30_650_000 picoseconds. - Weight::from_parts(31_683_000, 0) - .saturating_add(Weight::from_parts(0, 3607)) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 31_238_000 picoseconds. + Weight::from_parts(31_955_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -151,11 +193,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `322` - // Estimated: `3787` - // Minimum execution time: 37_666_000 picoseconds. - Weight::from_parts(38_920_000, 0) - .saturating_add(Weight::from_parts(0, 3787)) + // Measured: `360` + // Estimated: `3825` + // Minimum execution time: 37_237_000 picoseconds. + Weight::from_parts(38_569_000, 0) + .saturating_add(Weight::from_parts(0, 3825)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -165,45 +207,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_244_000 picoseconds. - Weight::from_parts(2_425_000, 0) + // Minimum execution time: 1_884_000 picoseconds. + Weight::from_parts(2_028_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `XcmPallet::SupportedVersion` (r:4 w:2) + /// Storage: `XcmPallet::SupportedVersion` (r:5 w:2) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `26` - // Estimated: `10916` - // Minimum execution time: 14_710_000 picoseconds. - Weight::from_parts(15_156_000, 0) - .saturating_add(Weight::from_parts(0, 10916)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `22` + // Estimated: `13387` + // Minimum execution time: 16_048_000 picoseconds. + Weight::from_parts(16_617_000, 0) + .saturating_add(Weight::from_parts(0, 13387)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifiers` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifiers` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `30` - // Estimated: `10920` - // Minimum execution time: 14_630_000 picoseconds. - Weight::from_parts(15_290_000, 0) - .saturating_add(Weight::from_parts(0, 10920)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `26` + // Estimated: `13391` + // Minimum execution time: 16_073_000 picoseconds. + Weight::from_parts(16_672_000, 0) + .saturating_add(Weight::from_parts(0, 13391)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:6 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `40` - // Estimated: `13405` - // Minimum execution time: 16_686_000 picoseconds. - Weight::from_parts(17_332_000, 0) - .saturating_add(Weight::from_parts(0, 13405)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15880` + // Minimum execution time: 18_422_000 picoseconds. + Weight::from_parts(18_900_000, 0) + .saturating_add(Weight::from_parts(0, 15880)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `XcmPallet::VersionNotifyTargets` (r:2 w:1) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -217,38 +259,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `178` - // Estimated: `6118` - // Minimum execution time: 30_180_000 picoseconds. - Weight::from_parts(31_351_000, 0) - .saturating_add(Weight::from_parts(0, 6118)) + // Measured: `216` + // Estimated: `6156` + // Minimum execution time: 30_373_000 picoseconds. + Weight::from_parts(30_972_000, 0) + .saturating_add(Weight::from_parts(0, 6156)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:3 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `69` - // Estimated: `8484` - // Minimum execution time: 9_624_000 picoseconds. - Weight::from_parts(10_029_000, 0) - .saturating_add(Weight::from_parts(0, 8484)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `10959` + // Minimum execution time: 11_863_000 picoseconds. + Weight::from_parts(12_270_000, 0) + .saturating_add(Weight::from_parts(0, 10959)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `37` - // Estimated: `10927` - // Minimum execution time: 15_139_000 picoseconds. - Weight::from_parts(15_575_000, 0) - .saturating_add(Weight::from_parts(0, 10927)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `33` + // Estimated: `13398` + // Minimum execution time: 16_733_000 picoseconds. + Weight::from_parts(17_094_000, 0) + .saturating_add(Weight::from_parts(0, 13398)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -260,12 +302,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `182` - // Estimated: `11072` - // Minimum execution time: 37_871_000 picoseconds. - Weight::from_parts(38_940_000, 0) - .saturating_add(Weight::from_parts(0, 11072)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `216` + // Estimated: `13581` + // Minimum execution time: 39_236_000 picoseconds. + Weight::from_parts(40_587_000, 0) + .saturating_add(Weight::from_parts(0, 13581)) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `XcmPallet::QueryCounter` (r:1 w:1) @@ -276,8 +318,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 2_732_000 picoseconds. - Weight::from_parts(2_892_000, 0) + // Minimum execution time: 2_145_000 picoseconds. + Weight::from_parts(2_255_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -288,10 +330,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7576` // Estimated: `11041` - // Minimum execution time: 23_813_000 picoseconds. - Weight::from_parts(24_201_000, 0) + // Minimum execution time: 22_518_000 picoseconds. + Weight::from_parts(22_926_000, 0) .saturating_add(Weight::from_parts(0, 11041)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 34_438_000 picoseconds. + Weight::from_parts(35_514_000, 0) + .saturating_add(Weight::from_parts(0, 3488)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs new file mode 100644 index 0000000000000000000000000000000000000000..dc5e5d8ca4b1ca7ba0cceb6fdd8ddedc0d7e3c28 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs @@ -0,0 +1,191 @@ +// 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_xcm_benchmarks::fungible` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_xcm_benchmarks::fungible +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_benchmarks::fungible`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_benchmarks::fungible::WeightInfo for WeightInfo { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn withdraw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 27_223_000 picoseconds. + Weight::from_parts(27_947_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn transfer_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `6196` + // Minimum execution time: 36_502_000 picoseconds. + Weight::from_parts(37_023_000, 0) + .saturating_add(Weight::from_parts(0, 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: `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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn transfer_reserve_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `6196` + // Minimum execution time: 85_152_000 picoseconds. + Weight::from_parts(86_442_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .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`) + 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) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 56_571_000 picoseconds. + Weight::from_parts(58_163_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn receive_teleported_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `103` + // Estimated: `3593` + // Minimum execution time: 27_411_000 picoseconds. + Weight::from_parts(27_953_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn deposit_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 20_776_000 picoseconds. + Weight::from_parts(21_145_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// 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`) + fn deposit_reserve_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 51_738_000 picoseconds. + Weight::from_parts(53_251_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .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`) + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn initiate_teleport() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 39_333_000 picoseconds. + Weight::from_parts(40_515_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs new file mode 100644 index 0000000000000000000000000000000000000000..b62f36172baf545ac83e55137f11e6bd5e5ad74f --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs @@ -0,0 +1,347 @@ +// 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_xcm_benchmarks::generic` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_xcm_benchmarks::generic +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_benchmarks::generic`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_benchmarks::generic::WeightInfo for WeightInfo { + /// 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`) + fn report_holding() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 55_210_000 picoseconds. + Weight::from_parts(56_613_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn buy_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_246_000 picoseconds. + Weight::from_parts(1_339_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `XcmPallet::Queries` (r:1 w:0) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn query_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3465` + // Minimum execution time: 5_377_000 picoseconds. + Weight::from_parts(5_549_000, 0) + .saturating_add(Weight::from_parts(0, 3465)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn transact() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_008_000 picoseconds. + Weight::from_parts(7_361_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn refund_surplus() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(1_848_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_error_handler() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_198_000 picoseconds. + Weight::from_parts(1_265_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_appendix() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_267_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn clear_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_193_000 picoseconds. + Weight::from_parts(1_258_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn descend_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_268_000 picoseconds. + Weight::from_parts(1_342_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn clear_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_173_000 picoseconds. + Weight::from_parts(1_248_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn report_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 53_715_000 picoseconds. + Weight::from_parts(54_860_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 8_621_000 picoseconds. + Weight::from_parts(8_903_000, 0) + .saturating_add(Weight::from_parts(0, 3488)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn trap() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_211_000 picoseconds. + Weight::from_parts(1_281_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `XcmPallet::VersionNotifyTargets` (r:1 w:1) + /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn subscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 26_448_000 picoseconds. + Weight::from_parts(27_057_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `XcmPallet::VersionNotifyTargets` (r:0 w:1) + /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn unsubscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_498_000 picoseconds. + Weight::from_parts(3_614_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn burn_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_575_000 picoseconds. + Weight::from_parts(1_698_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_334_000 picoseconds. + Weight::from_parts(1_435_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_244_000 picoseconds. + Weight::from_parts(1_337_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_244_000 picoseconds. + Weight::from_parts(1_331_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_407_000 picoseconds. + Weight::from_parts(1_522_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn query_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 62_963_000 picoseconds. + Weight::from_parts(64_556_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn expect_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_458_000 picoseconds. + Weight::from_parts(8_741_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn report_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 54_068_000 picoseconds. + Weight::from_parts(55_665_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn clear_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_290_000 picoseconds. + Weight::from_parts(1_348_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_189_000 picoseconds. + Weight::from_parts(1_268_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn clear_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_276_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_fees_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_253_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn unpaid_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_250_000 picoseconds. + Weight::from_parts(1_354_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs b/polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs index a6beeded428649fdda8530fda4c10492867f11a2..f41a5d4ebf8f080eb7a9a11016296e97e3db3a1a 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs @@ -16,26 +16,28 @@ //! Autogenerated weights for `runtime_common::assigned_slots` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ynta1nyy-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=runtime_common::assigned_slots // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=runtime_common::assigned_slots -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,7 +50,7 @@ use core::marker::PhantomData; /// Weight functions for `runtime_common::assigned_slots`. pub struct WeightInfo(PhantomData); impl runtime_common::assigned_slots::WeightInfo for WeightInfo { - /// Storage: `Registrar::Paras` (r:1 w:1) + /// Storage: `Registrar::Paras` (r:1 w:0) /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::ParaLifecycles` (r:1 w:1) /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -68,15 +70,15 @@ impl runtime_common::assigned_slots::WeightInfo for Wei /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn assign_perm_parachain_slot() -> Weight { // Proof Size summary in bytes: - // Measured: `673` - // Estimated: `4138` - // Minimum execution time: 84_646_000 picoseconds. - Weight::from_parts(91_791_000, 0) - .saturating_add(Weight::from_parts(0, 4138)) + // Measured: `730` + // Estimated: `4195` + // Minimum execution time: 71_337_000 picoseconds. + Weight::from_parts(80_807_000, 0) + .saturating_add(Weight::from_parts(0, 4195)) .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: `Registrar::Paras` (r:1 w:1) + /// Storage: `Registrar::Paras` (r:1 w:0) /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::ParaLifecycles` (r:1 w:1) /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -98,13 +100,13 @@ impl runtime_common::assigned_slots::WeightInfo for Wei /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn assign_temp_parachain_slot() -> Weight { // Proof Size summary in bytes: - // Measured: `673` - // Estimated: `4138` - // Minimum execution time: 68_091_000 picoseconds. - Weight::from_parts(77_310_000, 0) - .saturating_add(Weight::from_parts(0, 4138)) + // Measured: `730` + // Estimated: `4195` + // Minimum execution time: 60_188_000 picoseconds. + Weight::from_parts(63_932_000, 0) + .saturating_add(Weight::from_parts(0, 4195)) .saturating_add(T::DbWeight::get().reads(10)) - .saturating_add(T::DbWeight::get().writes(7)) + .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `AssignedSlots::PermanentSlots` (r:1 w:0) /// Proof: `AssignedSlots::PermanentSlots` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) @@ -118,11 +120,11 @@ impl runtime_common::assigned_slots::WeightInfo for Wei /// Proof: `AssignedSlots::TemporarySlotCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn unassign_parachain_slot() -> Weight { // Proof Size summary in bytes: - // Measured: `823` - // Estimated: `4288` - // Minimum execution time: 38_081_000 picoseconds. - Weight::from_parts(40_987_000, 0) - .saturating_add(Weight::from_parts(0, 4288)) + // Measured: `856` + // Estimated: `4321` + // Minimum execution time: 35_764_000 picoseconds. + Weight::from_parts(38_355_000, 0) + .saturating_add(Weight::from_parts(0, 4321)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -132,8 +134,8 @@ impl runtime_common::assigned_slots::WeightInfo for Wei // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_182_000 picoseconds. - Weight::from_parts(7_437_000, 0) + // Minimum execution time: 4_634_000 picoseconds. + Weight::from_parts(4_852_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -143,8 +145,8 @@ impl runtime_common::assigned_slots::WeightInfo for Wei // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_153_000 picoseconds. - Weight::from_parts(7_456_000, 0) + // Minimum execution time: 4_563_000 picoseconds. + Weight::from_parts(4_829_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs b/polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs index 3cd7c7a47e90ee1bf6590d0421faf580c1c776f6..2b756802289488173ce7dbf4cc8e15e4a8ae0f9d 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::auctions` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::auctions // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_auctions.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,92 +50,90 @@ use core::marker::PhantomData; /// Weight functions for `runtime_common::auctions`. pub struct WeightInfo(PhantomData); impl runtime_common::auctions::WeightInfo for WeightInfo { - /// Storage: Auctions AuctionInfo (r:1 w:1) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Auctions AuctionCounter (r:1 w:1) - /// Proof: Auctions AuctionCounter (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Auctions::AuctionInfo` (r:1 w:1) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Auctions::AuctionCounter` (r:1 w:1) + /// Proof: `Auctions::AuctionCounter` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn new_auction() -> Weight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1493` - // Minimum execution time: 12_805_000 picoseconds. - Weight::from_parts(13_153_000, 0) + // Minimum execution time: 7_307_000 picoseconds. + Weight::from_parts(7_680_000, 0) .saturating_add(Weight::from_parts(0, 1493)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Paras ParaLifecycles (r:1 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions AuctionCounter (r:1 w:0) - /// Proof: Auctions AuctionCounter (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Auctions AuctionInfo (r:1 w:0) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Slots Leases (r:1 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions Winning (r:1 w:1) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions ReservedAmounts (r:2 w:2) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::AuctionCounter` (r:1 w:0) + /// Proof: `Auctions::AuctionCounter` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Auctions::AuctionInfo` (r:1 w:0) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Slots::Leases` (r:1 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::Winning` (r:1 w:1) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::ReservedAmounts` (r:2 w:2) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn bid() -> Weight { // Proof Size summary in bytes: - // Measured: `728` + // Measured: `761` // Estimated: `6060` - // Minimum execution time: 77_380_000 picoseconds. - Weight::from_parts(80_503_000, 0) + // Minimum execution time: 75_448_000 picoseconds. + Weight::from_parts(78_716_000, 0) .saturating_add(Weight::from_parts(0, 6060)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Auctions AuctionInfo (r:1 w:1) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe NextRandomness (r:1 w:0) - /// Proof: Babe NextRandomness (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: Babe EpochStart (r:1 w:0) - /// Proof: Babe EpochStart (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Auctions AuctionCounter (r:1 w:0) - /// Proof: Auctions AuctionCounter (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Auctions Winning (r:3600 w:3600) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions ReservedAmounts (r:37 w:36) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:36 w:36) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Slots Leases (r:2 w:2) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) + /// Storage: `Auctions::AuctionInfo` (r:1 w:1) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::NextRandomness` (r:1 w:0) + /// Proof: `Babe::NextRandomness` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Babe::EpochStart` (r:1 w:0) + /// Proof: `Babe::EpochStart` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Auctions::AuctionCounter` (r:1 w:0) + /// Proof: `Auctions::AuctionCounter` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Auctions::Winning` (r:3600 w:3600) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::ReservedAmounts` (r:37 w:36) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:36 w:36) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Slots::Leases` (r:2 w:2) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `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: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn on_initialize() -> Weight { // Proof Size summary in bytes: - // Measured: `6947789` + // Measured: `6947017` // Estimated: `15822990` - // Minimum execution time: 6_311_055_000 picoseconds. - Weight::from_parts(6_409_142_000, 0) + // Minimum execution time: 7_120_207_000 picoseconds. + Weight::from_parts(7_273_496_000, 0) .saturating_add(Weight::from_parts(0, 15822990)) - .saturating_add(T::DbWeight::get().reads(3683)) - .saturating_add(T::DbWeight::get().writes(3678)) + .saturating_add(T::DbWeight::get().reads(3682)) + .saturating_add(T::DbWeight::get().writes(3677)) } - /// Storage: Auctions ReservedAmounts (r:37 w:36) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:36 w:36) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Auctions Winning (r:3600 w:3600) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions AuctionInfo (r:0 w:1) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Auctions::ReservedAmounts` (r:37 w:36) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:36 w:36) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Auctions::Winning` (r:3600 w:3600) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::AuctionInfo` (r:0 w:1) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn cancel_auction() -> Weight { // Proof Size summary in bytes: // Measured: `177732` // Estimated: `15822990` - // Minimum execution time: 4_849_561_000 picoseconds. - Weight::from_parts(4_955_226_000, 0) + // Minimum execution time: 5_536_281_000 picoseconds. + Weight::from_parts(5_675_163_000, 0) .saturating_add(Weight::from_parts(0, 15822990)) .saturating_add(T::DbWeight::get().reads(3673)) .saturating_add(T::DbWeight::get().writes(3673)) diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_claims.rs b/polkadot/runtime/rococo/src/weights/runtime_common_claims.rs index 52e0dd24afa0aa5e4cd75d53f53ca7d032ca9588..de2bb71933b7a4e94f9b0f31eb417f75228c6602 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_claims.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_claims.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::claims` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::claims // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_claims.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_claims.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,120 +50,133 @@ use core::marker::PhantomData; /// Weight functions for `runtime_common::claims`. pub struct WeightInfo(PhantomData); impl runtime_common::claims::WeightInfo for WeightInfo { - /// Storage: Claims Claims (r:1 w:1) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:1 w:1) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Total (r:1 w:1) - /// Proof Skipped: Claims Total (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:1 w:1) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Claims::Claims` (r:1 w:1) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:1) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Total` (r:1 w:1) + /// Proof: `Claims::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:1 w:1) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) fn claim() -> Weight { // Proof Size summary in bytes: // Measured: `558` // Estimated: `4764` - // Minimum execution time: 144_931_000 picoseconds. - Weight::from_parts(156_550_000, 0) + // Minimum execution time: 181_028_000 picoseconds. + Weight::from_parts(194_590_000, 0) .saturating_add(Weight::from_parts(0, 4764)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Claims Total (r:1 w:1) - /// Proof Skipped: Claims Total (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:0 w:1) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Claims (r:0 w:1) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:0 w:1) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) + /// Storage: `Claims::Total` (r:1 w:1) + /// Proof: `Claims::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:0 w:1) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Claims` (r:0 w:1) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:0 w:1) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) fn mint_claim() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `1701` - // Minimum execution time: 11_300_000 picoseconds. - Weight::from_parts(11_642_000, 0) + // Minimum execution time: 11_224_000 picoseconds. + Weight::from_parts(13_342_000, 0) .saturating_add(Weight::from_parts(0, 1701)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Claims Claims (r:1 w:1) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:1 w:1) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Total (r:1 w:1) - /// Proof Skipped: Claims Total (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:1 w:1) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Claims::Claims` (r:1 w:1) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:1) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Total` (r:1 w:1) + /// Proof: `Claims::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:1 w:1) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) fn claim_attest() -> Weight { // Proof Size summary in bytes: // Measured: `558` // Estimated: `4764` - // Minimum execution time: 149_112_000 picoseconds. - Weight::from_parts(153_872_000, 0) + // Minimum execution time: 187_964_000 picoseconds. + Weight::from_parts(202_553_000, 0) .saturating_add(Weight::from_parts(0, 4764)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Claims Preclaims (r:1 w:1) - /// Proof Skipped: Claims Preclaims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:1 w:1) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Claims (r:1 w:1) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Total (r:1 w:1) - /// Proof Skipped: Claims Total (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:1 w:1) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Claims::Preclaims` (r:1 w:1) + /// Proof: `Claims::Preclaims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:1) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Claims` (r:1 w:1) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Total` (r:1 w:1) + /// Proof: `Claims::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:1 w:1) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) fn attest() -> Weight { // Proof Size summary in bytes: // Measured: `632` // Estimated: `4764` - // Minimum execution time: 69_619_000 picoseconds. - Weight::from_parts(79_242_000, 0) + // Minimum execution time: 78_210_000 picoseconds. + Weight::from_parts(84_581_000, 0) .saturating_add(Weight::from_parts(0, 4764)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } - /// Storage: Claims Claims (r:1 w:2) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:1 w:2) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:1 w:2) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Preclaims (r:1 w:1) - /// Proof Skipped: Claims Preclaims (max_values: None, max_size: None, mode: Measured) + /// Storage: `Claims::Claims` (r:1 w:2) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:1 w:2) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:2) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Preclaims` (r:1 w:1) + /// Proof: `Claims::Preclaims` (`max_values`: None, `max_size`: None, mode: `Measured`) fn move_claim() -> Weight { // Proof Size summary in bytes: // Measured: `440` // Estimated: `3905` - // Minimum execution time: 22_066_000 picoseconds. - Weight::from_parts(22_483_000, 0) + // Minimum execution time: 33_940_000 picoseconds. + Weight::from_parts(48_438_000, 0) .saturating_add(Weight::from_parts(0, 3905)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(7)) } + /// Storage: `Claims::Preclaims` (r:1 w:0) + /// Proof: `Claims::Preclaims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:0) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn prevalidate_attests() -> Weight { + // Proof Size summary in bytes: + // Measured: `296` + // Estimated: `3761` + // Minimum execution time: 9_025_000 picoseconds. + Weight::from_parts(10_563_000, 0) + .saturating_add(Weight::from_parts(0, 3761)) + .saturating_add(T::DbWeight::get().reads(2)) + } } diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs b/polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs new file mode 100644 index 0000000000000000000000000000000000000000..d068f07e7594a80a9b728e0776649e52f88a7bf5 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs @@ -0,0 +1,86 @@ +// 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 `runtime_common::coretime` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=runtime_common::coretime +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `runtime_common::coretime`. +pub struct WeightInfo(PhantomData); +impl runtime_common::coretime::WeightInfo for WeightInfo { + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`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`) + fn request_core_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `151` + // Estimated: `1636` + // Minimum execution time: 7_543_000 picoseconds. + Weight::from_parts(7_745_000, 0) + .saturating_add(Weight::from_parts(0, 1636)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreSchedules` (r:0 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreSchedules` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `s` is `[1, 100]`. + fn assign_core(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 9_367_000 picoseconds. + Weight::from_parts(9_932_305, 0) + .saturating_add(Weight::from_parts(0, 3645)) + // Standard Error: 231 + .saturating_add(Weight::from_parts(12_947, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs b/polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs index 0e7420cba2e6c981d4f9c0dd9e3c95c9ce26f1c5..8ebab3d551e69e54832f4da00c302a616cd96354 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::crowdloan` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::crowdloan // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_crowdloan.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,172 +50,168 @@ use core::marker::PhantomData; /// Weight functions for `runtime_common::crowdloan`. pub struct WeightInfo(PhantomData); impl runtime_common::crowdloan::WeightInfo for WeightInfo { - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Crowdloan NextFundIndex (r:1 w:1) - /// Proof Skipped: Crowdloan NextFundIndex (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: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`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: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::NextFundIndex` (r:1 w:1) + /// Proof: `Crowdloan::NextFundIndex` (`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`) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `438` // Estimated: `3903` - // Minimum execution time: 50_399_000 picoseconds. - Weight::from_parts(51_641_000, 0) + // Minimum execution time: 46_095_000 picoseconds. + Weight::from_parts(48_111_000, 0) .saturating_add(Weight::from_parts(0, 3903)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Slots Leases (r:1 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions AuctionInfo (r:1 w:0) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:1) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Crowdloan EndingsCount (r:1 w:0) - /// Proof Skipped: Crowdloan EndingsCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Crowdloan NewRaise (r:1 w:1) - /// Proof Skipped: Crowdloan NewRaise (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) - /// Proof Skipped: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Storage: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:1 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::AuctionInfo` (r:1 w:0) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Crowdloan::EndingsCount` (r:1 w:0) + /// Proof: `Crowdloan::EndingsCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::NewRaise` (r:1 w:1) + /// Proof: `Crowdloan::NewRaise` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) fn contribute() -> Weight { // Proof Size summary in bytes: - // Measured: `530` - // Estimated: `3995` - // Minimum execution time: 128_898_000 picoseconds. - Weight::from_parts(130_277_000, 0) - .saturating_add(Weight::from_parts(0, 3995)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(5)) + // Measured: `563` + // Estimated: `4028` + // Minimum execution time: 133_059_000 picoseconds. + Weight::from_parts(136_515_000, 0) + .saturating_add(Weight::from_parts(0, 4028)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (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: Balances InactiveIssuance (r:1 w:1) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: unknown `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) - /// Proof Skipped: unknown `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) + /// Storage: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`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: UNKNOWN KEY `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) fn withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `689` + // Measured: `687` // Estimated: `6196` - // Minimum execution time: 69_543_000 picoseconds. - Weight::from_parts(71_522_000, 0) + // Minimum execution time: 71_733_000 picoseconds. + Weight::from_parts(74_034_000, 0) .saturating_add(Weight::from_parts(0, 6196)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// 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, 1000]`. fn refund(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `127 + k * (189 ±0)` - // Estimated: `140 + k * (189 ±0)` - // Minimum execution time: 50_735_000 picoseconds. - Weight::from_parts(52_282_000, 0) - .saturating_add(Weight::from_parts(0, 140)) - // Standard Error: 21_607 - .saturating_add(Weight::from_parts(38_955_985, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `125 + k * (189 ±0)` + // Estimated: `138 + k * (189 ±0)` + // Minimum execution time: 46_016_000 picoseconds. + Weight::from_parts(48_260_000, 0) + .saturating_add(Weight::from_parts(0, 138)) + // Standard Error: 21_140 + .saturating_add(Weight::from_parts(39_141_925, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(k.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 189).saturating_mul(k.into())) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (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: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`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`) fn dissolve() -> Weight { // Proof Size summary in bytes: - // Measured: `515` + // Measured: `514` // Estimated: `6196` - // Minimum execution time: 43_100_000 picoseconds. - Weight::from_parts(44_272_000, 0) + // Minimum execution time: 44_724_000 picoseconds. + Weight::from_parts(47_931_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) + /// Storage: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) fn edit() -> Weight { // Proof Size summary in bytes: - // Measured: `235` - // Estimated: `3700` - // Minimum execution time: 18_702_000 picoseconds. - Weight::from_parts(19_408_000, 0) - .saturating_add(Weight::from_parts(0, 3700)) + // Measured: `234` + // Estimated: `3699` + // Minimum execution time: 19_512_000 picoseconds. + Weight::from_parts(21_129_000, 0) + .saturating_add(Weight::from_parts(0, 3699)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Crowdloan Funds (r:1 w:0) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) - /// Proof Skipped: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Storage: `Crowdloan::Funds` (r:1 w:0) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) fn add_memo() -> Weight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 25_568_000 picoseconds. - Weight::from_parts(26_203_000, 0) + // Minimum execution time: 33_529_000 picoseconds. + Weight::from_parts(37_082_000, 0) .saturating_add(Weight::from_parts(0, 3877)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Crowdloan Funds (r:1 w:0) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Crowdloan NewRaise (r:1 w:1) - /// Proof Skipped: Crowdloan NewRaise (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Crowdloan::Funds` (r:1 w:0) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::NewRaise` (r:1 w:1) + /// Proof: `Crowdloan::NewRaise` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn poke() -> Weight { // Proof Size summary in bytes: - // Measured: `239` - // Estimated: `3704` - // Minimum execution time: 17_832_000 picoseconds. - Weight::from_parts(18_769_000, 0) - .saturating_add(Weight::from_parts(0, 3704)) + // Measured: `238` + // Estimated: `3703` + // Minimum execution time: 23_153_000 picoseconds. + Weight::from_parts(24_181_000, 0) + .saturating_add(Weight::from_parts(0, 3703)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Auctions AuctionInfo (r:1 w:0) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Crowdloan EndingsCount (r:1 w:1) - /// Proof Skipped: Crowdloan EndingsCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Crowdloan NewRaise (r:1 w:1) - /// Proof Skipped: Crowdloan NewRaise (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Crowdloan Funds (r:100 w:0) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions AuctionCounter (r:1 w:0) - /// Proof: Auctions AuctionCounter (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Paras ParaLifecycles (r:100 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Slots Leases (r:100 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions Winning (r:1 w:1) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions ReservedAmounts (r:100 w:100) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:100 w:100) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Auctions::AuctionInfo` (r:1 w:0) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Crowdloan::EndingsCount` (r:1 w:1) + /// Proof: `Crowdloan::EndingsCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::NewRaise` (r:1 w:1) + /// Proof: `Crowdloan::NewRaise` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::Funds` (r:100 w:0) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::AuctionCounter` (r:1 w:0) + /// Proof: `Auctions::AuctionCounter` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Paras::ParaLifecycles` (r:100 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:100 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::Winning` (r:1 w:1) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::ReservedAmounts` (r:100 w:100) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:100 w:100) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `n` is `[2, 100]`. fn on_initialize(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `197 + n * (356 ±0)` + // Measured: `229 + n * (356 ±0)` // Estimated: `5385 + n * (2832 ±0)` - // Minimum execution time: 128_319_000 picoseconds. - Weight::from_parts(130_877_000, 0) + // Minimum execution time: 120_164_000 picoseconds. + Weight::from_parts(3_390_119, 0) .saturating_add(Weight::from_parts(0, 5385)) - // Standard Error: 61_381 - .saturating_add(Weight::from_parts(60_209_202, 0).saturating_mul(n.into())) + // Standard Error: 41_727 + .saturating_add(Weight::from_parts(54_453_016, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3)) diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs b/polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs index cec357453b67be400c0191ac7d5c12e6961a4bee..9b0cb98e6c01f8cb560ea8a99cd42cce9a04fb24 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs @@ -1,36 +1,43 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Polkadot. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// 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 `runtime_common::identity_migrator` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `sbtb`, CPU: `13th Gen Intel(R) Core(TM) i7-1365U` +//! HOSTNAME: `runner-bn-ce5rx-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/release/polkadot +// ./target/production/polkadot // benchmark // pallet // --chain=rococo-dev -// --steps=2 -// --repeat=1 +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::identity_migrator // --extrinsic=* -// --output=./migrator-release.rs +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -44,7 +51,7 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl runtime_common::identity_migrator::WeightInfo for WeightInfo { /// Storage: `Identity::IdentityOf` (r:1 w:1) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7538), added: 10013, mode: `MaxEncodedLen`) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `Identity::SubsOf` (r:1 w:1) /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:2 w:2) @@ -63,34 +70,34 @@ impl runtime_common::identity_migrator::WeightInfo for /// The range of component `s` is `[0, 100]`. fn reap_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `7292 + r * (8 ±0) + s * (32 ±0)` - // Estimated: `11003 + r * (8 ±0) + s * (33 ±0)` - // Minimum execution time: 163_756_000 picoseconds. - Weight::from_parts(158_982_500, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 1_143_629 - .saturating_add(Weight::from_parts(238_675, 0).saturating_mul(r.into())) - // Standard Error: 228_725 - .saturating_add(Weight::from_parts(1_529_645, 0).saturating_mul(s.into())) + // Measured: `7457 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037 + r * (7 ±0) + s * (32 ±0)` + // Minimum execution time: 157_343_000 picoseconds. + Weight::from_parts(159_289_236, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 16_439 + .saturating_add(Weight::from_parts(224_293, 0).saturating_mul(r.into())) + // Standard Error: 3_367 + .saturating_add(Weight::from_parts(1_383_637, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes(6)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) - .saturating_add(Weight::from_parts(0, 33).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(s.into())) } /// Storage: `Identity::IdentityOf` (r:1 w:1) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7538), added: 10013, mode: `MaxEncodedLen`) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Identity::SubsOf` (r:1 w:1) /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) fn poke_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `7229` - // Estimated: `11003` - // Minimum execution time: 137_570_000 picoseconds. - Weight::from_parts(137_570_000, 0) - .saturating_add(Weight::from_parts(0, 11003)) + // Measured: `7242` + // Estimated: `11037` + // Minimum execution time: 114_384_000 picoseconds. + Weight::from_parts(115_741_000, 0) + .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs index 0a56562a1a95f24e9efddc6c26e16c4feeaf37c2..e066106e13450c78159994a808254b44cac43eff 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::paras_registrar` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::paras_registrar // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_paras_registrar.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,175 +50,169 @@ use core::marker::PhantomData; /// Weight functions for `runtime_common::paras_registrar`. pub struct WeightInfo(PhantomData); impl runtime_common::paras_registrar::WeightInfo for WeightInfo { - /// Storage: Registrar NextFreeParaId (r:1 w:1) - /// Proof Skipped: Registrar NextFreeParaId (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::NextFreeParaId` (r:1 w:1) + /// Proof: `Registrar::NextFreeParaId` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paras` (r:1 w:1) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reserve() -> Weight { // Proof Size summary in bytes: - // Measured: `97` - // Estimated: `3562` - // Minimum execution time: 29_948_000 picoseconds. - Weight::from_parts(30_433_000, 0) - .saturating_add(Weight::from_parts(0, 3562)) + // Measured: `96` + // Estimated: `3561` + // Minimum execution time: 24_109_000 picoseconds. + Weight::from_parts(24_922_000, 0) + .saturating_add(Weight::from_parts(0, 3561)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:0 w:1) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpcomingParasGenesis (r:0 w:1) - /// Proof Skipped: Paras UpcomingParasGenesis (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::Paras` (r:1 w:1) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `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: `Paras::CurrentCodeHash` (r:0 w:1) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpcomingParasGenesis` (r:0 w:1) + /// Proof: `Paras::UpcomingParasGenesis` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `616` - // Estimated: `4081` - // Minimum execution time: 6_332_113_000 picoseconds. - Weight::from_parts(6_407_158_000, 0) - .saturating_add(Weight::from_parts(0, 4081)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `352` + // Estimated: `3817` + // Minimum execution time: 7_207_580_000 picoseconds. + Weight::from_parts(7_298_567_000, 0) + .saturating_add(Weight::from_parts(0, 3817)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(8)) } - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:0 w:1) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpcomingParasGenesis (r:0 w:1) - /// Proof Skipped: Paras UpcomingParasGenesis (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::Paras` (r:1 w:1) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `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: `Paras::CurrentCodeHash` (r:0 w:1) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpcomingParasGenesis` (r:0 w:1) + /// Proof: `Paras::UpcomingParasGenesis` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_register() -> Weight { // Proof Size summary in bytes: - // Measured: `533` - // Estimated: `3998` - // Minimum execution time: 6_245_403_000 picoseconds. - Weight::from_parts(6_289_575_000, 0) - .saturating_add(Weight::from_parts(0, 3998)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `269` + // Estimated: `3734` + // Minimum execution time: 7_196_460_000 picoseconds. + Weight::from_parts(7_385_729_000, 0) + .saturating_add(Weight::from_parts(0, 3734)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(8)) } - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras FutureCodeHash (r:1 w:0) - /// Proof Skipped: Paras FutureCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: Registrar PendingSwap (r:0 w:1) - /// Proof Skipped: Registrar PendingSwap (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::Paras` (r:1 w:1) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeHash` (r:1 w:0) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `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: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `Registrar::PendingSwap` (r:0 w:1) + /// Proof: `Registrar::PendingSwap` (`max_values`: None, `max_size`: None, mode: `Measured`) fn deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `476` - // Estimated: `3941` - // Minimum execution time: 49_822_000 picoseconds. - Weight::from_parts(50_604_000, 0) - .saturating_add(Weight::from_parts(0, 3941)) + // Measured: `499` + // Estimated: `3964` + // Minimum execution time: 54_761_000 picoseconds. + Weight::from_parts(57_931_000, 0) + .saturating_add(Weight::from_parts(0, 3964)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Registrar Paras (r:1 w:0) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:2 w:2) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar PendingSwap (r:1 w:1) - /// Proof Skipped: Registrar PendingSwap (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Crowdloan Funds (r:2 w:2) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Slots Leases (r:2 w:2) - /// Proof Skipped: Slots Leases (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: `Paras::ParaLifecycles` (r:2 w:2) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingSwap` (r:1 w:1) + /// Proof: `Registrar::PendingSwap` (`max_values`: None, `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: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::Funds` (r:2 w:2) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:2 w:2) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap() -> Weight { // Proof Size summary in bytes: - // Measured: `780` - // Estimated: `6720` - // Minimum execution time: 55_166_000 picoseconds. - Weight::from_parts(56_913_000, 0) - .saturating_add(Weight::from_parts(0, 6720)) + // Measured: `837` + // Estimated: `6777` + // Minimum execution time: 59_564_000 picoseconds. + Weight::from_parts(62_910_000, 0) + .saturating_add(Weight::from_parts(0, 6777)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(8)) } - /// Storage: Paras FutureCodeHash (r:1 w:1) - /// Proof Skipped: Paras FutureCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeRestrictionSignal (r:1 w:1) - /// Proof Skipped: Paras UpgradeRestrictionSignal (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:1 w:0) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeCooldowns (r:1 w:1) - /// Proof Skipped: Paras UpgradeCooldowns (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// The range of component `b` is `[1, 3145728]`. + /// Storage: `Paras::FutureCodeHash` (r:1 w:1) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:1 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`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`) + /// The range of component `b` is `[9, 3145728]`. fn schedule_code_upgrade(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `464` - // Estimated: `3929` - // Minimum execution time: 43_650_000 picoseconds. - Weight::from_parts(43_918_000, 0) - .saturating_add(Weight::from_parts(0, 3929)) - // Standard Error: 6 - .saturating_add(Weight::from_parts(2_041, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `201` + // Estimated: `3666` + // Minimum execution time: 33_106_000 picoseconds. + Weight::from_parts(33_526_000, 0) + .saturating_add(Weight::from_parts(0, 3666)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_334, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (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`) /// The range of component `b` is `[1, 1048576]`. fn set_current_head(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_666_000 picoseconds. - Weight::from_parts(8_893_000, 0) + // Minimum execution time: 5_992_000 picoseconds. + Weight::from_parts(12_059_689, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2 - .saturating_add(Weight::from_parts(855, 0).saturating_mul(b.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(959, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().writes(1)) } } diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_slots.rs b/polkadot/runtime/rococo/src/weights/runtime_common_slots.rs index 23ab1ed3ee0efff9e95fd3526d022c9becad7b93..dd10dbbf1f112d51f0d1270663299baab7af2f59 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_slots.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_slots.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::slots` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::slots // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_slots.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_slots.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,86 +50,82 @@ use core::marker::PhantomData; /// Weight functions for `runtime_common::slots`. pub struct WeightInfo(PhantomData); impl runtime_common::slots::WeightInfo for WeightInfo { - /// Storage: Slots Leases (r:1 w:1) - /// Proof Skipped: Slots Leases (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: `Slots::Leases` (r:1 w:1) + /// Proof: `Slots::Leases` (`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`) fn force_lease() -> Weight { // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `3752` - // Minimum execution time: 29_932_000 picoseconds. - Weight::from_parts(30_334_000, 0) - .saturating_add(Weight::from_parts(0, 3752)) + // Measured: `320` + // Estimated: `3785` + // Minimum execution time: 26_570_000 picoseconds. + Weight::from_parts(27_619_000, 0) + .saturating_add(Weight::from_parts(0, 3785)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Paras Parachains (r:1 w:0) - /// Proof Skipped: Paras Parachains (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Slots Leases (r:101 w:100) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:200 w:200) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:100 w:100) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::Parachains` (r:1 w:0) + /// Proof: `Paras::Parachains` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:101 w:100) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:200 w:200) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `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: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[0, 100]`. /// The range of component `t` is `[0, 100]`. fn manage_lease_period_start(c: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `26 + c * (47 ±0) + t * (308 ±0)` - // Estimated: `2800 + c * (2526 ±0) + t * (2789 ±0)` - // Minimum execution time: 634_547_000 picoseconds. - Weight::from_parts(643_045_000, 0) - .saturating_add(Weight::from_parts(0, 2800)) - // Standard Error: 81_521 - .saturating_add(Weight::from_parts(2_705_219, 0).saturating_mul(c.into())) - // Standard Error: 81_521 - .saturating_add(Weight::from_parts(11_464_132, 0).saturating_mul(t.into())) + // Measured: `594 + c * (20 ±0) + t * (234 ±0)` + // Estimated: `4065 + c * (2496 ±0) + t * (2709 ±0)` + // Minimum execution time: 729_793_000 picoseconds. + Weight::from_parts(740_820_000, 0) + .saturating_add(Weight::from_parts(0, 4065)) + // Standard Error: 88_206 + .saturating_add(Weight::from_parts(2_793_142, 0).saturating_mul(c.into())) + // Standard Error: 88_206 + .saturating_add(Weight::from_parts(8_933_065, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2526).saturating_mul(c.into())) - .saturating_add(Weight::from_parts(0, 2789).saturating_mul(t.into())) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2496).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2709).saturating_mul(t.into())) } - /// Storage: Slots Leases (r:1 w:1) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: System Account (r:8 w:8) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Slots::Leases` (r:1 w:1) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:8 w:8) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn clear_all_leases() -> Weight { // Proof Size summary in bytes: - // Measured: `2759` + // Measured: `2792` // Estimated: `21814` - // Minimum execution time: 129_756_000 picoseconds. - Weight::from_parts(131_810_000, 0) + // Minimum execution time: 123_888_000 picoseconds. + Weight::from_parts(131_245_000, 0) .saturating_add(Weight::from_parts(0, 21814)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(9)) } - /// Storage: Slots Leases (r:1 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) + /// Storage: `Slots::Leases` (r:1 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `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: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn trigger_onboard() -> Weight { // Proof Size summary in bytes: - // Measured: `707` - // Estimated: `4172` - // Minimum execution time: 29_527_000 picoseconds. - Weight::from_parts(30_055_000, 0) - .saturating_add(Weight::from_parts(0, 4172)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) + // Measured: `612` + // Estimated: `4077` + // Minimum execution time: 27_341_000 picoseconds. + Weight::from_parts(28_697_000, 0) + .saturating_add(Weight::from_parts(0, 4077)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) } } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs index ac0f05301b486dbdbb8c0ca004e195ab47171ff3..653e1009f31c3285b7de50fe9b7071d0e4050394 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs @@ -16,26 +16,28 @@ //! Autogenerated weights for `runtime_parachains::assigner_on_demand` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-fljshgub-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=runtime_parachains::assigner_on_demand // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=runtime_parachains::assigner_on_demand -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_assigner_on_demand.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -57,13 +59,13 @@ impl runtime_parachains::assigner_on_demand::WeightInfo /// The range of component `s` is `[1, 9999]`. fn place_order_keep_alive(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `297 + s * (4 ±0)` - // Estimated: `3762 + s * (4 ±0)` - // Minimum execution time: 33_522_000 picoseconds. - Weight::from_parts(35_436_835, 0) - .saturating_add(Weight::from_parts(0, 3762)) - // Standard Error: 129 - .saturating_add(Weight::from_parts(14_041, 0).saturating_mul(s.into())) + // Measured: `363 + s * (4 ±0)` + // Estimated: `3828 + s * (4 ±0)` + // Minimum execution time: 25_298_000 picoseconds. + Weight::from_parts(21_486_098, 0) + .saturating_add(Weight::from_parts(0, 3828)) + // Standard Error: 136 + .saturating_add(Weight::from_parts(13_943, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) @@ -77,13 +79,13 @@ impl runtime_parachains::assigner_on_demand::WeightInfo /// The range of component `s` is `[1, 9999]`. fn place_order_allow_death(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `297 + s * (4 ±0)` - // Estimated: `3762 + s * (4 ±0)` - // Minimum execution time: 33_488_000 picoseconds. - Weight::from_parts(34_848_934, 0) - .saturating_add(Weight::from_parts(0, 3762)) - // Standard Error: 143 - .saturating_add(Weight::from_parts(14_215, 0).saturating_mul(s.into())) + // Measured: `363 + s * (4 ±0)` + // Estimated: `3828 + s * (4 ±0)` + // Minimum execution time: 25_421_000 picoseconds. + Weight::from_parts(21_828_043, 0) + .saturating_add(Weight::from_parts(0, 3828)) + // Standard Error: 133 + .saturating_add(Weight::from_parts(13_831, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs index 34541b83597e6284a401a171e703b200366114a0..caad090ae15bf7d7d79a696d6a178cffeeca1507 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs @@ -16,26 +16,28 @@ //! Autogenerated weights for `runtime_parachains::configuration` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, 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-bn-ce5rx-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 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=runtime_parachains::configuration // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=runtime_parachains::configuration -// --chain=rococo-dev // --header=./polkadot/file_header.txt -// --output=./polkadot/runtime/rococo/src/weights/ +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -58,8 +60,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_793_000 picoseconds. - Weight::from_parts(8_192_000, 0) + // Minimum execution time: 7_689_000 picoseconds. + Weight::from_parts(8_089_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -74,8 +76,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_819_000 picoseconds. - Weight::from_parts(8_004_000, 0) + // Minimum execution time: 7_735_000 picoseconds. + Weight::from_parts(8_150_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +92,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_760_000 picoseconds. - Weight::from_parts(8_174_000, 0) + // Minimum execution time: 7_902_000 picoseconds. + Weight::from_parts(8_196_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -116,8 +118,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_814_000 picoseconds. - Weight::from_parts(8_098_000, 0) + // Minimum execution time: 7_634_000 picoseconds. + Weight::from_parts(7_983_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -132,8 +134,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_028_000 picoseconds. - Weight::from_parts(10_386_000, 0) + // Minimum execution time: 9_580_000 picoseconds. + Weight::from_parts(9_989_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -148,8 +150,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_867_000 picoseconds. - Weight::from_parts(8_191_000, 0) + // Minimum execution time: 7_787_000 picoseconds. + Weight::from_parts(8_008_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -164,8 +166,24 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_158_000 picoseconds. - Weight::from_parts(10_430_000, 0) + // Minimum execution time: 9_557_000 picoseconds. + Weight::from_parts(9_994_000, 0) + .saturating_add(Weight::from_parts(0, 1636)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`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`) + fn set_config_with_scheduler_params() -> Weight { + // Proof Size summary in bytes: + // Measured: `151` + // Estimated: `1636` + // Minimum execution time: 7_775_000 picoseconds. + Weight::from_parts(7_989_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs index 63a8c3addc7da4296041627ee8492945945bce7f..cf1aa36e10e704ab0074730896f6b5d2f9a4fedf 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::disputes` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::disputes // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_disputes.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,14 +50,14 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::disputes`. pub struct WeightInfo(PhantomData); impl runtime_parachains::disputes::WeightInfo for WeightInfo { - /// Storage: ParasDisputes Frozen (r:0 w:1) - /// Proof Skipped: ParasDisputes Frozen (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ParasDisputes::Frozen` (r:0 w:1) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn force_unfreeze() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_937_000 picoseconds. - Weight::from_parts(3_082_000, 0) + // Minimum execution time: 1_855_000 picoseconds. + Weight::from_parts(2_015_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs index 417820e6627f6e39c4107df722f4b83f9cca6a4b..a3b912491e5ec56c340b3dfd3b46e95f39f57e3c 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::hrmp` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::hrmp // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_hrmp.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,105 +50,97 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::hrmp`. pub struct WeightInfo(PhantomData); impl runtime_parachains::hrmp::WeightInfo for WeightInfo { - /// Storage: Paras ParaLifecycles (r:2 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequests (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannels (r:1 w:0) - /// Proof Skipped: Hrmp HrmpChannels (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpEgressChannelsIndex (r:1 w:0) - /// Proof Skipped: Hrmp HrmpEgressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestCount (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestCount (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestsList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:1 w:0) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0) + /// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `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::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn hrmp_init_open_channel() -> Weight { // Proof Size summary in bytes: - // Measured: `704` - // Estimated: `6644` - // Minimum execution time: 41_564_000 picoseconds. - Weight::from_parts(42_048_000, 0) - .saturating_add(Weight::from_parts(0, 6644)) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `488` + // Estimated: `3953` + // Minimum execution time: 34_911_000 picoseconds. + Weight::from_parts(35_762_000, 0) + .saturating_add(Weight::from_parts(0, 3953)) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequests (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpIngressChannelsIndex (r:1 w:0) - /// Proof Skipped: Hrmp HrmpIngressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpAcceptedChannelRequestCount (r:1 w:1) - /// Proof Skipped: Hrmp HrmpAcceptedChannelRequestCount (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:1 w:0) + /// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1) + /// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`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::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn hrmp_accept_open_channel() -> Weight { // Proof Size summary in bytes: - // Measured: `936` - // Estimated: `4401` - // Minimum execution time: 43_570_000 picoseconds. - Weight::from_parts(44_089_000, 0) - .saturating_add(Weight::from_parts(0, 4401)) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `478` + // Estimated: `3943` + // Minimum execution time: 31_483_000 picoseconds. + Weight::from_parts(32_230_000, 0) + .saturating_add(Weight::from_parts(0, 3943)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Hrmp HrmpChannels (r:1 w:0) - /// Proof Skipped: Hrmp HrmpChannels (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpCloseChannelRequests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpCloseChannelRequests (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpCloseChannelRequestsList (r:1 w:1) - /// Proof Skipped: Hrmp HrmpCloseChannelRequestsList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Hrmp::HrmpChannels` (r:1 w:0) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpCloseChannelRequests` (r:1 w:1) + /// Proof: `Hrmp::HrmpCloseChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpCloseChannelRequestsList` (r:1 w:1) + /// Proof: `Hrmp::HrmpCloseChannelRequestsList` (`max_values`: Some(1), `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::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn hrmp_close_channel() -> Weight { // Proof Size summary in bytes: - // Measured: `807` - // Estimated: `4272` - // Minimum execution time: 36_594_000 picoseconds. - Weight::from_parts(37_090_000, 0) - .saturating_add(Weight::from_parts(0, 4272)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `591` + // Estimated: `4056` + // Minimum execution time: 32_153_000 picoseconds. + Weight::from_parts(32_982_000, 0) + .saturating_add(Weight::from_parts(0, 4056)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Hrmp HrmpIngressChannelsIndex (r:128 w:128) - /// Proof Skipped: Hrmp HrmpIngressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpEgressChannelsIndex (r:128 w:128) - /// Proof Skipped: Hrmp HrmpEgressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannels (r:254 w:254) - /// Proof Skipped: Hrmp HrmpChannels (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpAcceptedChannelRequestCount (r:0 w:1) - /// Proof Skipped: Hrmp HrmpAcceptedChannelRequestCount (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannelContents (r:0 w:254) - /// Proof Skipped: Hrmp HrmpChannelContents (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestCount (r:0 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestCount (max_values: None, max_size: None, mode: Measured) + /// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:128 w:128) + /// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:128 w:128) + /// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:254 w:254) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:0 w:1) + /// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelContents` (r:0 w:254) + /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:0 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 127]`. /// The range of component `e` is `[0, 127]`. fn force_clean_hrmp(i: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `264 + e * (100 ±0) + i * (100 ±0)` - // Estimated: `3726 + e * (2575 ±0) + i * (2575 ±0)` - // Minimum execution time: 1_085_140_000 picoseconds. - Weight::from_parts(1_100_901_000, 0) - .saturating_add(Weight::from_parts(0, 3726)) - // Standard Error: 98_982 - .saturating_add(Weight::from_parts(3_229_112, 0).saturating_mul(i.into())) - // Standard Error: 98_982 - .saturating_add(Weight::from_parts(3_210_944, 0).saturating_mul(e.into())) + // Measured: `297 + e * (100 ±0) + i * (100 ±0)` + // Estimated: `3759 + e * (2575 ±0) + i * (2575 ±0)` + // Minimum execution time: 1_240_769_000 picoseconds. + Weight::from_parts(1_249_285_000, 0) + .saturating_add(Weight::from_parts(0, 3759)) + // Standard Error: 112_346 + .saturating_add(Weight::from_parts(3_449_114, 0).saturating_mul(i.into())) + // Standard Error: 112_346 + .saturating_add(Weight::from_parts(3_569_184, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(e.into()))) @@ -155,139 +150,139 @@ impl runtime_parachains::hrmp::WeightInfo for WeightInf .saturating_add(Weight::from_parts(0, 2575).saturating_mul(e.into())) .saturating_add(Weight::from_parts(0, 2575).saturating_mul(i.into())) } - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestsList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequests (r:128 w:128) - /// Proof Skipped: Hrmp HrmpOpenChannelRequests (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:256 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpIngressChannelsIndex (r:128 w:128) - /// Proof Skipped: Hrmp HrmpIngressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpEgressChannelsIndex (r:128 w:128) - /// Proof Skipped: Hrmp HrmpEgressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestCount (r:128 w:128) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestCount (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpAcceptedChannelRequestCount (r:128 w:128) - /// Proof Skipped: Hrmp HrmpAcceptedChannelRequestCount (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannels (r:0 w:128) - /// Proof Skipped: Hrmp HrmpChannels (max_values: None, max_size: None, mode: Measured) + /// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequests` (r:128 w:128) + /// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:256 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:128 w:128) + /// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:128 w:128) + /// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:128 w:128) + /// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:128 w:128) + /// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:0 w:128) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[0, 128]`. fn force_process_hrmp_open(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `779 + c * (136 ±0)` - // Estimated: `2234 + c * (5086 ±0)` - // Minimum execution time: 10_497_000 picoseconds. - Weight::from_parts(6_987_455, 0) - .saturating_add(Weight::from_parts(0, 2234)) - // Standard Error: 18_540 - .saturating_add(Weight::from_parts(18_788_534, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(2)) + // Measured: `525 + c * (136 ±0)` + // Estimated: `1980 + c * (5086 ±0)` + // Minimum execution time: 6_026_000 picoseconds. + Weight::from_parts(6_257_000, 0) + .saturating_add(Weight::from_parts(0, 1980)) + // Standard Error: 9_732 + .saturating_add(Weight::from_parts(21_049_890, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((6_u64).saturating_mul(c.into()))) .saturating_add(Weight::from_parts(0, 5086).saturating_mul(c.into())) } - /// Storage: Hrmp HrmpCloseChannelRequestsList (r:1 w:1) - /// Proof Skipped: Hrmp HrmpCloseChannelRequestsList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannels (r:128 w:128) - /// Proof Skipped: Hrmp HrmpChannels (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpEgressChannelsIndex (r:128 w:128) - /// Proof Skipped: Hrmp HrmpEgressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpIngressChannelsIndex (r:128 w:128) - /// Proof Skipped: Hrmp HrmpIngressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpCloseChannelRequests (r:0 w:128) - /// Proof Skipped: Hrmp HrmpCloseChannelRequests (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannelContents (r:0 w:128) - /// Proof Skipped: Hrmp HrmpChannelContents (max_values: None, max_size: None, mode: Measured) + /// Storage: `Hrmp::HrmpCloseChannelRequestsList` (r:1 w:1) + /// Proof: `Hrmp::HrmpCloseChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:128 w:128) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:128 w:128) + /// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:128 w:128) + /// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpCloseChannelRequests` (r:0 w:128) + /// Proof: `Hrmp::HrmpCloseChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannelContents` (r:0 w:128) + /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[0, 128]`. fn force_process_hrmp_close(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `335 + c * (124 ±0)` - // Estimated: `1795 + c * (2600 ±0)` - // Minimum execution time: 6_575_000 picoseconds. - Weight::from_parts(1_228_642, 0) - .saturating_add(Weight::from_parts(0, 1795)) - // Standard Error: 14_826 - .saturating_add(Weight::from_parts(11_604_038, 0).saturating_mul(c.into())) + // Measured: `368 + c * (124 ±0)` + // Estimated: `1828 + c * (2600 ±0)` + // Minimum execution time: 4_991_000 picoseconds. + Weight::from_parts(984_758, 0) + .saturating_add(Weight::from_parts(0, 1828)) + // Standard Error: 11_918 + .saturating_add(Weight::from_parts(13_018_813, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(c.into()))) .saturating_add(Weight::from_parts(0, 2600).saturating_mul(c.into())) } - /// Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestsList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequests (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestCount (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestCount (max_values: None, max_size: None, mode: Measured) + /// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[0, 128]`. fn hrmp_cancel_open_request(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1026 + c * (13 ±0)` - // Estimated: `4295 + c * (15 ±0)` - // Minimum execution time: 22_301_000 picoseconds. - Weight::from_parts(26_131_473, 0) - .saturating_add(Weight::from_parts(0, 4295)) - // Standard Error: 830 - .saturating_add(Weight::from_parts(49_448, 0).saturating_mul(c.into())) + // Measured: `1059 + c * (13 ±0)` + // Estimated: `4328 + c * (15 ±0)` + // Minimum execution time: 17_299_000 picoseconds. + Weight::from_parts(27_621_478, 0) + .saturating_add(Weight::from_parts(0, 4328)) + // Standard Error: 2_527 + .saturating_add(Weight::from_parts(121_149, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 15).saturating_mul(c.into())) } - /// Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestsList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequests (r:128 w:128) - /// Proof Skipped: Hrmp HrmpOpenChannelRequests (max_values: None, max_size: None, mode: Measured) + /// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequests` (r:128 w:128) + /// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[0, 128]`. fn clean_open_channel_requests(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `243 + c * (63 ±0)` - // Estimated: `1722 + c * (2538 ±0)` - // Minimum execution time: 5_234_000 picoseconds. - Weight::from_parts(7_350_270, 0) - .saturating_add(Weight::from_parts(0, 1722)) - // Standard Error: 3_105 - .saturating_add(Weight::from_parts(2_981_935, 0).saturating_mul(c.into())) + // Measured: `276 + c * (63 ±0)` + // Estimated: `1755 + c * (2538 ±0)` + // Minimum execution time: 3_764_000 picoseconds. + Weight::from_parts(5_935_301, 0) + .saturating_add(Weight::from_parts(0, 1755)) + // Standard Error: 3_761 + .saturating_add(Weight::from_parts(3_290_277, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) .saturating_add(Weight::from_parts(0, 2538).saturating_mul(c.into())) } - /// Storage: Paras ParaLifecycles (r:2 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequests (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequests (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpChannels (r:1 w:0) - /// Proof Skipped: Hrmp HrmpChannels (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpEgressChannelsIndex (r:1 w:0) - /// Proof Skipped: Hrmp HrmpEgressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestCount (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestCount (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpOpenChannelRequestsList (r:1 w:1) - /// Proof Skipped: Hrmp HrmpOpenChannelRequestsList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:2 w:2) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:2 w:2) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpIngressChannelsIndex (r:1 w:0) - /// Proof Skipped: Hrmp HrmpIngressChannelsIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: Hrmp HrmpAcceptedChannelRequestCount (r:1 w:1) - /// Proof Skipped: Hrmp HrmpAcceptedChannelRequestCount (max_values: None, max_size: None, mode: Measured) - fn force_open_hrmp_channel(_c: u32, ) -> Weight { + /// Storage: `Hrmp::HrmpOpenChannelRequests` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequestsList` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestsList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpOpenChannelRequestCount` (r:1 w:1) + /// Proof: `Hrmp::HrmpOpenChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:1 w:0) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpEgressChannelsIndex` (r:1 w:0) + /// Proof: `Hrmp::HrmpEgressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:2 w:2) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:2 w:2) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpIngressChannelsIndex` (r:1 w:0) + /// Proof: `Hrmp::HrmpIngressChannelsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpAcceptedChannelRequestCount` (r:1 w:1) + /// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `c` is `[0, 1]`. + fn force_open_hrmp_channel(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `704` - // Estimated: `6644` - // Minimum execution time: 55_611_000 picoseconds. - Weight::from_parts(56_488_000, 0) - .saturating_add(Weight::from_parts(0, 6644)) - .saturating_add(T::DbWeight::get().reads(14)) + // Measured: `488 + c * (235 ±0)` + // Estimated: `6428 + c * (235 ±0)` + // Minimum execution time: 49_506_000 picoseconds. + Weight::from_parts(51_253_075, 0) + .saturating_add(Weight::from_parts(0, 6428)) + // Standard Error: 144_082 + .saturating_add(Weight::from_parts(12_862_224, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(8)) + .saturating_add(Weight::from_parts(0, 235).saturating_mul(c.into())) } /// Storage: `Paras::ParaLifecycles` (r:1 w:0) /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -311,11 +306,11 @@ impl runtime_parachains::hrmp::WeightInfo for WeightInf /// Proof: `Hrmp::HrmpAcceptedChannelRequestCount` (`max_values`: None, `max_size`: None, mode: `Measured`) fn establish_system_channel() -> Weight { // Proof Size summary in bytes: - // Measured: `417` - // Estimated: `6357` - // Minimum execution time: 629_674_000 picoseconds. - Weight::from_parts(640_174_000, 0) - .saturating_add(Weight::from_parts(0, 6357)) + // Measured: `488` + // Estimated: `6428` + // Minimum execution time: 50_016_000 picoseconds. + Weight::from_parts(50_933_000, 0) + .saturating_add(Weight::from_parts(0, 6428)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(8)) } @@ -323,11 +318,11 @@ impl runtime_parachains::hrmp::WeightInfo for WeightInf /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) fn poke_channel_deposits() -> Weight { // Proof Size summary in bytes: - // Measured: `263` - // Estimated: `3728` - // Minimum execution time: 173_371_000 picoseconds. - Weight::from_parts(175_860_000, 0) - .saturating_add(Weight::from_parts(0, 3728)) + // Measured: `296` + // Estimated: `3761` + // Minimum execution time: 12_280_000 picoseconds. + Weight::from_parts(12_863_000, 0) + .saturating_add(Weight::from_parts(0, 3761)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs index a121ad774cefecf91e57838e03107fde22674f94..b9ec7565bd555d1b5b06fd9db01e7ca2ee765187 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-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/rococo/src/weights/runtime_parachains_inclusion.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,27 +50,25 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::inclusion`. pub struct WeightInfo(PhantomData); impl 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) + /// 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: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) /// The range of component `i` is `[1, 1000]`. fn receive_upward_messages(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `33280` + // Measured: `32993` // Estimated: `36283` - // Minimum execution time: 71_094_000 picoseconds. - Weight::from_parts(71_436_000, 0) + // Minimum execution time: 72_675_000 picoseconds. + Weight::from_parts(73_290_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)) + // Standard Error: 16_067 + .saturating_add(Weight::from_parts(57_735_739, 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()))) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs index 5c627507dfb682a603152fee2155b620773dcd49..e8c554610c999dd93bbc6cb10025933438adc968 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::initializer` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::initializer // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_initializer.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,18 +50,18 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::initializer`. pub struct WeightInfo(PhantomData); impl runtime_parachains::initializer::WeightInfo for WeightInfo { - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `d` is `[0, 65536]`. fn force_approve(d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + d * (11 ±0)` // Estimated: `1480 + d * (11 ±0)` - // Minimum execution time: 3_771_000 picoseconds. - Weight::from_parts(6_491_437, 0) + // Minimum execution time: 2_634_000 picoseconds. + Weight::from_parts(2_728_000, 0) .saturating_add(Weight::from_parts(0, 1480)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(1_356, 0).saturating_mul(d.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(2_499, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 11).saturating_mul(d.into())) diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs index dfd95006dc7d0a6cedeeab9908d99a2c1c7e561a..af26bfc9ae9b25f5a3a50938ceb1b978646c65eb 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::paras` //! -//! 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-02-29, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::paras // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_paras.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,247 +50,248 @@ use core::marker::PhantomData; /// Weight functions for `runtime_parachains::paras`. pub struct WeightInfo(PhantomData); impl runtime_parachains::paras::WeightInfo for WeightInfo { - /// Storage: Paras CurrentCodeHash (r:1 w:1) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PastCodeMeta (r:1 w:1) - /// Proof Skipped: Paras PastCodeMeta (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PastCodePruning (r:1 w:1) - /// Proof Skipped: Paras PastCodePruning (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PastCodeHash (r:0 w:1) - /// Proof Skipped: Paras PastCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:0 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:1) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PastCodeMeta` (r:1 w:1) + /// Proof: `Paras::PastCodeMeta` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PastCodePruning` (r:1 w:1) + /// Proof: `Paras::PastCodePruning` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PastCodeHash` (r:0 w:1) + /// Proof: `Paras::PastCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:0 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 3145728]`. fn force_set_current_code(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `8309` // Estimated: `11774` - // Minimum execution time: 31_941_000 picoseconds. - Weight::from_parts(32_139_000, 0) + // Minimum execution time: 27_488_000 picoseconds. + Weight::from_parts(27_810_000, 0) .saturating_add(Weight::from_parts(0, 11774)) - // Standard Error: 5 - .saturating_add(Weight::from_parts(2_011, 0).saturating_mul(c.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(2_189, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (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`) /// The range of component `s` is `[1, 1048576]`. fn force_set_current_head(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_275_000 picoseconds. - Weight::from_parts(8_321_000, 0) + // Minimum execution time: 5_793_000 picoseconds. + Weight::from_parts(7_987_606, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2 - .saturating_add(Weight::from_parts(858, 0).saturating_mul(s.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(971, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: Paras Heads (r:0 w:1) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_set_most_recent_context() -> Weight { - Weight::from_parts(10_155_000, 0) - // Standard Error: 0 - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_733_000 picoseconds. + Weight::from_parts(2_954_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras FutureCodeHash (r:1 w:1) - /// Proof Skipped: Paras FutureCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:1 w:0) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeCooldowns (r:1 w:1) - /// Proof Skipped: Paras UpgradeCooldowns (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeRestrictionSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeRestrictionSignal (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: `Paras::UpgradeRestrictionSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 3145728]`. fn force_schedule_code_upgrade(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `8715` - // Estimated: `12180` - // Minimum execution time: 49_923_000 picoseconds. - Weight::from_parts(50_688_000, 0) - .saturating_add(Weight::from_parts(0, 12180)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_976, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `8452` + // Estimated: `11917` + // Minimum execution time: 6_072_000 picoseconds. + Weight::from_parts(6_128_000, 0) + .saturating_add(Weight::from_parts(0, 11917)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_334, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Paras FutureCodeUpgrades (r:1 w:0) - /// Proof Skipped: Paras FutureCodeUpgrades (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeGoAheadSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeGoAheadSignal (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: `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`) /// The range of component `s` is `[1, 1048576]`. fn force_note_new_head(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `3560` - // Minimum execution time: 14_408_000 picoseconds. - Weight::from_parts(14_647_000, 0) - .saturating_add(Weight::from_parts(0, 3560)) - // Standard Error: 2 - .saturating_add(Weight::from_parts(858, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `163` + // Estimated: `3628` + // Minimum execution time: 15_166_000 picoseconds. + Weight::from_parts(21_398_053, 0) + .saturating_add(Weight::from_parts(0, 3628)) + // Standard Error: 1 + .saturating_add(Weight::from_parts(976, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, 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: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_queue_action() -> Weight { // Proof Size summary in bytes: - // Measured: `4288` - // Estimated: `7753` - // Minimum execution time: 20_009_000 picoseconds. - Weight::from_parts(20_518_000, 0) - .saturating_add(Weight::from_parts(0, 7753)) + // Measured: `4312` + // Estimated: `7777` + // Minimum execution time: 16_345_000 picoseconds. + Weight::from_parts(16_712_000, 0) + .saturating_add(Weight::from_parts(0, 7777)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, 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::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`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: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 3145728]`. fn add_trusted_validation_code(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `946` - // Estimated: `4411` - // Minimum execution time: 80_626_000 picoseconds. - Weight::from_parts(52_721_755, 0) - .saturating_add(Weight::from_parts(0, 4411)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_443, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `683` + // Estimated: `4148` + // Minimum execution time: 78_076_000 picoseconds. + Weight::from_parts(123_193_814, 0) + .saturating_add(Weight::from_parts(0, 4148)) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_770, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Paras CodeByHashRefs (r:1 w:0) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:0 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:0) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:0 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) fn poke_unused_validation_code() -> Weight { // Proof Size summary in bytes: // Measured: `28` // Estimated: `3493` - // Minimum execution time: 6_692_000 picoseconds. - Weight::from_parts(7_009_000, 0) + // Minimum execution time: 5_184_000 picoseconds. + Weight::from_parts(5_430_000, 0) .saturating_add(Weight::from_parts(0, 3493)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (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: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`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`) fn include_pvf_check_statement() -> Weight { // Proof Size summary in bytes: - // Measured: `26682` - // Estimated: `30147` - // Minimum execution time: 87_994_000 picoseconds. - Weight::from_parts(89_933_000, 0) - .saturating_add(Weight::from_parts(0, 30147)) + // Measured: `26706` + // Estimated: `30171` + // Minimum execution time: 102_995_000 picoseconds. + Weight::from_parts(108_977_000, 0) + .saturating_add(Weight::from_parts(0, 30171)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras UpcomingUpgrades (r:1 w:1) - /// Proof Skipped: Paras UpcomingUpgrades (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras FutureCodeUpgrades (r:0 w:100) - /// Proof Skipped: Paras FutureCodeUpgrades (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: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`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::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpcomingUpgrades` (r:1 w:1) + /// Proof: `Paras::UpcomingUpgrades` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:0 w:100) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight { // Proof Size summary in bytes: - // Measured: `27523` - // Estimated: `30988` - // Minimum execution time: 783_222_000 picoseconds. - Weight::from_parts(794_959_000, 0) - .saturating_add(Weight::from_parts(0, 30988)) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `27360` + // Estimated: `30825` + // Minimum execution time: 709_433_000 picoseconds. + Weight::from_parts(725_074_000, 0) + .saturating_add(Weight::from_parts(0, 30825)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(104)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (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: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`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`) fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight { // Proof Size summary in bytes: - // Measured: `27214` - // Estimated: `30679` - // Minimum execution time: 87_424_000 picoseconds. - Weight::from_parts(88_737_000, 0) - .saturating_add(Weight::from_parts(0, 30679)) + // Measured: `27338` + // Estimated: `30803` + // Minimum execution time: 98_973_000 picoseconds. + Weight::from_parts(104_715_000, 0) + .saturating_add(Weight::from_parts(0, 30803)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (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: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`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::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight { // Proof Size summary in bytes: - // Measured: `26991` - // Estimated: `30456` - // Minimum execution time: 612_485_000 picoseconds. - Weight::from_parts(621_670_000, 0) - .saturating_add(Weight::from_parts(0, 30456)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `26728` + // Estimated: `30193` + // Minimum execution time: 550_958_000 picoseconds. + Weight::from_parts(564_497_000, 0) + .saturating_add(Weight::from_parts(0, 30193)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (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: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`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`) fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight { // Proof Size summary in bytes: - // Measured: `26682` - // Estimated: `30147` - // Minimum execution time: 86_673_000 picoseconds. - Weight::from_parts(87_424_000, 0) - .saturating_add(Weight::from_parts(0, 30147)) + // Measured: `26706` + // Estimated: `30171` + // Minimum execution time: 97_088_000 picoseconds. + Weight::from_parts(103_617_000, 0) + .saturating_add(Weight::from_parts(0, 30171)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs index a102d1903b2f2b4a757edf3bb96809c982f31383..374927f8470de51d6c8c9ab4a0818f03fe849e97 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs @@ -13,161 +13,334 @@ // 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` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-11-20, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 128 +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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/release/polkadot +// ./target/production/polkadot // benchmark +// pallet // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::paras_inherent // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --output=./runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs -// --header=./file_header.txt +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `runtime_parachains::paras_inherent`. pub struct WeightInfo(PhantomData); impl runtime_parachains::paras_inherent::WeightInfo for WeightInfo { - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParaSessionInfo Sessions (r:1 w:0) - // Storage: ParasDisputes Disputes (r:1 w:1) - // Storage: ParasDisputes Included (r:1 w:1) - // Storage: ParasDisputes SpamSlots (r:1 w:1) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) + /// 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: `ParaSessionInfo::Sessions` (r:1 w:0) + /// Proof: `ParaSessionInfo::Sessions` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Disputes` (r:1 w:1) + /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::BackersOnDisputes` (r:1 w:1) + /// Proof: `ParasDisputes::BackersOnDisputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Included` (r:1 w:1) + /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// 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::PendingAvailability` (r:2 w:1) + /// Proof: `ParaInclusion::PendingAvailability` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::PendingAvailabilityCommitments` (r:1 w:1) + /// Proof: `ParaInclusion::PendingAvailabilityCommitments` (`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: `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: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `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`) + /// 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`) + /// The range of component `v` is `[10, 200]`. fn enter_variable_disputes(v: u32, ) -> Weight { - Weight::from_parts(352_590_000 as u64, 0) - // Standard Error: 13_000 - .saturating_add(Weight::from_parts(49_254_000 as u64, 0).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(24 as u64)) - .saturating_add(T::DbWeight::get().writes(16 as u64)) + // Proof Size summary in bytes: + // Measured: `67819` + // Estimated: `73759 + v * (23 ±0)` + // Minimum execution time: 874_229_000 picoseconds. + Weight::from_parts(486_359_072, 0) + .saturating_add(Weight::from_parts(0, 73759)) + // Standard Error: 19_197 + .saturating_add(Weight::from_parts(41_842_161, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(26)) + .saturating_add(T::DbWeight::get().writes(16)) + .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) } - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParasDisputes Disputes (r:1 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInclusion AvailabilityBitfields (r:0 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: ParasDisputes Included (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) + /// 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::PendingAvailability` (r:2 w:1) + /// Proof: `ParaInclusion::PendingAvailability` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::PendingAvailabilityCommitments` (r:1 w:1) + /// Proof: `ParaInclusion::PendingAvailabilityCommitments` (`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) + /// 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: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `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`) + /// Storage: `ParaInclusion::AvailabilityBitfields` (r:0 w:1) + /// Proof: `ParaInclusion::AvailabilityBitfields` (`max_values`: None, `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 { - Weight::from_parts(299_878_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(21 as u64)) - .saturating_add(T::DbWeight::get().writes(15 as u64)) + // Proof Size summary in bytes: + // Measured: `42791` + // Estimated: `48731` + // Minimum execution time: 428_757_000 picoseconds. + Weight::from_parts(449_681_000, 0) + .saturating_add(Weight::from_parts(0, 48731)) + .saturating_add(T::DbWeight::get().reads(24)) + .saturating_add(T::DbWeight::get().writes(17)) } - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParasDisputes Disputes (r:2 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Paras PastCodeMeta (r:1 w:0) - // Storage: Paras CurrentCodeHash (r:1 w:0) - // Storage: Ump RelayDispatchQueueSize (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: ParasDisputes Included (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) - fn enter_backed_candidates_variable(_v: u32) -> Weight { - Weight::from_parts(442_472_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(25 as u64)) - .saturating_add(T::DbWeight::get().writes(14 as u64)) + /// 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::PendingAvailability` (r:2 w:1) + /// Proof: `ParaInclusion::PendingAvailability` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::PendingAvailabilityCommitments` (r:1 w:1) + /// Proof: `ParaInclusion::PendingAvailabilityCommitments` (`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) + /// 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: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`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::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// 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`) + /// 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`) + /// The range of component `v` is `[101, 200]`. + fn enter_backed_candidates_variable(v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `42863` + // Estimated: `48803` + // Minimum execution time: 1_276_079_000 picoseconds. + Weight::from_parts(1_313_585_212, 0) + .saturating_add(Weight::from_parts(0, 48803)) + // Standard Error: 18_279 + .saturating_add(Weight::from_parts(43_528, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(27)) + .saturating_add(T::DbWeight::get().writes(16)) } - // Storage: ParaInherent Included (r:1 w:1) - // Storage: System ParentHash (r:1 w:0) - // Storage: ParaScheduler AvailabilityCores (r:1 w:1) - // Storage: ParasShared CurrentSessionIndex (r:1 w:0) - // Storage: Configuration ActiveConfig (r:1 w:0) - // Storage: ParasDisputes Frozen (r:1 w:0) - // Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - // Storage: Paras Parachains (r:1 w:0) - // Storage: ParaInclusion PendingAvailability (r:2 w:1) - // Storage: ParaInclusion PendingAvailabilityCommitments (r:1 w:1) - // Storage: Dmp DownwardMessageQueues (r:1 w:1) - // Storage: Hrmp HrmpChannelDigests (r:1 w:1) - // Storage: Paras FutureCodeUpgrades (r:1 w:0) - // Storage: ParasDisputes Disputes (r:2 w:0) - // Storage: ParaScheduler SessionStartBlock (r:1 w:0) - // Storage: ParaScheduler ParathreadQueue (r:1 w:1) - // Storage: ParaScheduler Scheduled (r:1 w:1) - // Storage: ParaScheduler ValidatorGroups (r:1 w:0) - // Storage: Paras PastCodeMeta (r:1 w:0) - // Storage: Paras CurrentCodeHash (r:1 w:0) - // Storage: Ump RelayDispatchQueueSize (r:1 w:0) - // Storage: Ump NeedsDispatch (r:1 w:1) - // Storage: Ump NextDispatchRoundStartWith (r:1 w:1) - // Storage: ParaInherent OnChainVotes (r:0 w:1) - // Storage: ParasDisputes Included (r:0 w:1) - // Storage: Hrmp HrmpWatermarks (r:0 w:1) - // Storage: Paras Heads (r:0 w:1) + /// 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::PendingAvailability` (r:2 w:1) + /// Proof: `ParaInclusion::PendingAvailability` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::PendingAvailabilityCommitments` (r:1 w:1) + /// Proof: `ParaInclusion::PendingAvailabilityCommitments` (`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) + /// 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: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`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::FutureCodeHash` (r:1 w:0) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:1 w:0) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// 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`) + /// 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_backed_candidate_code_upgrade() -> Weight { - Weight::from_parts(36_903_411_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(25 as u64)) - .saturating_add(T::DbWeight::get().writes(14 as u64)) + // Proof Size summary in bytes: + // Measured: `42876` + // Estimated: `48816` + // Minimum execution time: 34_352_245_000 picoseconds. + Weight::from_parts(34_587_559_000, 0) + .saturating_add(Weight::from_parts(0, 48816)) + .saturating_add(T::DbWeight::get().reads(29)) + .saturating_add(T::DbWeight::get().writes(16)) } } diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index bfa9beb82090f53b4feccd4969fd73f72037d77e..af8981ddcc146573bdf2179c9baae856269e4b93 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -36,15 +36,14 @@ use runtime_common::{ }; use sp_core::ConstU32; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, FixedWeightBounds, - HashedDescription, IsChildSystemParachain, IsConcrete, MintLocation, OriginToPluralityVoice, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsChildSystemParachain, + IsConcrete, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -72,8 +71,7 @@ pub type LocationConverter = ( /// point of view of XCM-only concepts like `Location` and `Asset`. /// /// Ours is only aware of the Balances pallet, which is mapped to `RocLocation`. -#[allow(deprecated)] -pub type LocalAssetTransactor = XcmCurrencyAdapter< +pub type LocalAssetTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -222,6 +220,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } parameter_types! { diff --git a/polkadot/runtime/test-runtime/Cargo.toml b/polkadot/runtime/test-runtime/Cargo.toml index 9778ff82439494767b83d5acb6f7f9f57ac89619..bdf688646052133124f58f2ece16672c9e1c64fa 100644 --- a/polkadot/runtime/test-runtime/Cargo.toml +++ b/polkadot/runtime/test-runtime/Cargo.toml @@ -13,11 +13,11 @@ workspace = true [dependencies] bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } rustc-hex = { version = "2.1.0", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false } -serde_derive = { version = "1.0.117", optional = true } +serde = { workspace = true } +serde_derive = { optional = true, workspace = true } smallvec = "1.8.0" authority-discovery-primitives = { package = "sp-authority-discovery", path = "../../../substrate/primitives/authority-discovery", default-features = false } @@ -74,7 +74,7 @@ hex-literal = "0.4.1" tiny-keccak = { version = "2.0.2", features = ["keccak"] } keyring = { package = "sp-keyring", path = "../../../substrate/primitives/keyring" } sp-trie = { path = "../../../substrate/primitives/trie" } -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } [build-dependencies] substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder" } @@ -155,6 +155,7 @@ runtime-benchmarks = [ "pallet-staking/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 51c199b7a054fd96e67f765fb7885de5bf7965d2..a0617b3108f8135bede66a6e082c41095a2e9704 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -221,7 +221,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -239,6 +238,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = frame_support::weights::ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = (); } parameter_types! { @@ -394,7 +394,7 @@ where let current_block = System::block_number().saturated_into::().saturating_sub(1); let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -406,16 +406,17 @@ where frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); - let raw_payload = SignedPayload::new(call, extra) + ) + .into(); + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; - let (call, extra, _) = raw_payload.deconstruct(); + let (call, tx_ext, _) = raw_payload.deconstruct(); let address = Indices::unlookup(account); - Some((call, (address, signature, extra))) + Some((call, (address, signature, tx_ext))) } } @@ -443,12 +444,32 @@ parameter_types! { pub Prefix: &'static [u8] = b"Pay KSMs to the Kusama account:"; } +#[cfg(feature = "runtime-benchmarks")] +pub struct ClaimsHelper; + +#[cfg(feature = "runtime-benchmarks")] +use frame_support::dispatch::DispatchInfo; + +#[cfg(feature = "runtime-benchmarks")] +impl claims::BenchmarkHelperTrait for ClaimsHelper { + fn default_call_and_info() -> (RuntimeCall, DispatchInfo) { + use frame_support::dispatch::GetDispatchInfo; + let call = RuntimeCall::Claims(claims::Call::attest { + statement: claims::StatementKind::Regular.to_text().to_vec(), + }); + let info = call.get_dispatch_info(); + (call, info) + } +} + impl claims::Config for Runtime { type RuntimeEvent = RuntimeEvent; type VestingSchedule = Vesting; type Prefix = Prefix; type MoveClaimOrigin = frame_system::EnsureRoot; type WeightInfo = claims::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = ClaimsHelper; } parameter_types! { @@ -729,8 +750,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// `BlockId` type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `SignedExtension` to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -742,7 +763,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -753,7 +774,7 @@ pub type Executive = frame_executive::Executive< AllPalletsWithSystem, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; pub type Hash = ::Hash; pub type Extrinsic = ::Extrinsic; @@ -768,7 +789,7 @@ sp_api::impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs index d9d3d6e0752dcdd86792f04e8b8a010a2087f90f..a48bca17e9ff988da3d0ca4601003f3f6d96a71d 100644 --- a/polkadot/runtime/test-runtime/src/xcm_config.rs +++ b/polkadot/runtime/test-runtime/src/xcm_config.rs @@ -16,14 +16,16 @@ use frame_support::{ parameter_types, - traits::{Everything, Nothing}, + traits::{Everything, Get, Nothing}, weights::Weight, }; use frame_system::EnsureRoot; +use polkadot_runtime_parachains::FeeTracker; +use runtime_common::xcm_sender::{ChildParachainRouter, PriceForMessageDelivery}; use xcm::latest::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, SignedAccountId32AsNative, - SignedToAccountId32, + AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, + SignedAccountId32AsNative, SignedToAccountId32, WithUniqueTopic, }; use xcm_executor::{ traits::{TransactAsset, WeightTrader}, @@ -36,6 +38,8 @@ parameter_types! { pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 16; pub const UniversalLocation: xcm::latest::InteriorLocation = xcm::latest::Junctions::Here; + pub TokenLocation: Location = Here.into_location(); + pub FeeAssetId: AssetId = AssetId(TokenLocation::get()); } /// Type to convert an `Origin` type value into a `Location` value which represents an interior @@ -45,17 +49,43 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); -pub struct DoNothingRouter; -impl SendXcm for DoNothingRouter { - type Ticket = (); - fn validate(_dest: &mut Option, _msg: &mut Option>) -> SendResult<()> { - Ok(((), Assets::new())) - } - fn deliver(_: ()) -> Result { - Ok([0; 32]) +/// Implementation of [`PriceForMessageDelivery`], returning a different price +/// based on whether a message contains a reanchored asset or not. +/// This implementation ensures that messages with non-reanchored assets return higher +/// prices than messages with reanchored assets. +/// Useful for `deposit_reserve_asset_works_for_any_xcm_sender` integration test. +pub struct TestDeliveryPrice(sp_std::marker::PhantomData<(A, F)>); +impl, F: FeeTracker> PriceForMessageDelivery for TestDeliveryPrice { + type Id = F::Id; + + fn price_for_delivery(_: Self::Id, msg: &Xcm<()>) -> Assets { + let base_fee: super::Balance = 1_000_000; + + let parents = msg.iter().find_map(|xcm| match xcm { + ReserveAssetDeposited(assets) => { + let AssetId(location) = &assets.inner().first().unwrap().id; + Some(location.parents) + }, + _ => None, + }); + + // If no asset is found, price defaults to `base_fee`. + let amount = base_fee + .saturating_add(base_fee.saturating_mul(parents.unwrap_or(0) as super::Balance)); + + (A::get(), amount).into() } } +pub type PriceForChildParachainDelivery = TestDeliveryPrice; + +/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our +/// individual routers. +pub type XcmRouter = WithUniqueTopic< + // Only one router so far - use DMP to communicate with child parachains. + ChildParachainRouter, +>; + pub type Barrier = AllowUnpaidExecutionFrom; pub struct DummyAssetTransactor; @@ -74,6 +104,7 @@ impl TransactAsset for DummyAssetTransactor { } } +#[derive(Clone)] pub struct DummyWeightTrader; impl WeightTrader for DummyWeightTrader { fn new() -> Self { @@ -98,7 +129,7 @@ type OriginConverter = ( pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = super::RuntimeCall; - type XcmSender = DoNothingRouter; + type XcmSender = XcmRouter; type AssetTransactor = DummyAssetTransactor; type OriginConverter = OriginConverter; type IsReserve = (); @@ -121,6 +152,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = super::RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl pallet_xcm::Config for crate::Runtime { @@ -131,7 +163,7 @@ impl pallet_xcm::Config for crate::Runtime { type UniversalLocation = UniversalLocation; type SendXcmOrigin = EnsureXcmOrigin; type Weigher = FixedWeightBounds; - type XcmRouter = DoNothingRouter; + type XcmRouter = XcmRouter; type XcmExecuteFilter = Everything; type XcmExecutor = xcm_executor::XcmExecutor; type XcmTeleportFilter = Everything; diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 8e442d0f8538626722bb8e5a0dd5cbaf800b624c..6899edeeaeb8041f9eeb2cd3391efe89f3e15ed4 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "westend-runtime" build = "build.rs" -version = "1.0.0" +version = "7.0.0" description = "Westend testnet Relay Chain runtime." authors.workspace = true edition.workspace = true @@ -14,10 +14,10 @@ workspace = true bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } rustc-hex = { version = "2.1.0", default-features = false } -serde = { version = "1.0.195", default-features = false } -serde_derive = { version = "1.0.117", optional = true } +serde = { workspace = true } +serde_derive = { optional = true, workspace = true } smallvec = "1.8.0" authority-discovery-primitives = { package = "sp-authority-discovery", path = "../../../substrate/primitives/authority-discovery", default-features = false } @@ -119,7 +119,7 @@ xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", hex-literal = "0.4.1" tiny-keccak = { version = "2.0.2", features = ["keccak"] } keyring = { package = "sp-keyring", path = "../../../substrate/primitives/keyring" } -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } remote-externalities = { package = "frame-remote-externalities", path = "../../../substrate/utils/frame/remote-externalities" } tokio = { version = "1.24.2", features = ["macros"] } sp-tracing = { path = "../../../substrate/primitives/tracing", default-features = false } @@ -270,6 +270,7 @@ runtime-benchmarks = [ "pallet-state-trie-migration/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", @@ -346,5 +347,5 @@ runtime-metrics = ["runtime-parachains/runtime-metrics", "sp-io/with-tracing"] # 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. +# to make it smaller, like logging for example. on-chain-release-build = ["sp-api/disable-logging"] diff --git a/polkadot/runtime/westend/constants/Cargo.toml b/polkadot/runtime/westend/constants/Cargo.toml index d2e86970e509389d491b29298b7f66f790696d45..81df8f4f024dd32f23edd679b651e2edc4d32687 100644 --- a/polkadot/runtime/westend/constants/Cargo.toml +++ b/polkadot/runtime/westend/constants/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "westend-runtime-constants" -version = "1.0.0" +version = "7.0.0" description = "Constants used throughout the Westend network." authors.workspace = true edition.workspace = true diff --git a/polkadot/runtime/westend/constants/src/lib.rs b/polkadot/runtime/westend/constants/src/lib.rs index 848cccd559dc3e50cbd2a50c4df6a643910fe35c..c98f4b114fd88241dea1e2e5ffcf694848448007 100644 --- a/polkadot/runtime/westend/constants/src/lib.rs +++ b/polkadot/runtime/westend/constants/src/lib.rs @@ -107,6 +107,8 @@ pub mod system_parachain { pub const COLLECTIVES_ID: u32 = 1001; /// BridgeHub parachain ID. pub const BRIDGE_HUB_ID: u32 = 1002; + /// Encointer parachain ID. + pub const ENCOINTER_ID: u32 = 1003; /// People Chain parachain ID. pub const PEOPLE_ID: u32 = 1004; /// Brokerage parachain ID. @@ -128,6 +130,8 @@ pub mod xcm { const ROOT_INDEX: u32 = 0; // The bodies corresponding to the Polkadot OpenGov Origins. pub const FELLOWSHIP_ADMIN_INDEX: u32 = 1; + #[deprecated = "Will be removed after August 2024; Use `xcm::latest::BodyId::Treasury` \ + instead"] pub const TREASURER_INDEX: u32 = 2; } } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index adfb2083254ada79c9ef319c2d2cab91aaf109e6..45bbd0260e5e6a0f826b030b0452e81c96fc61cb 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -38,7 +38,7 @@ use frame_support::{ weights::{ConstantMultiplier, WeightMeter}, PalletId, }; -use frame_system::EnsureRoot; +use frame_system::{EnsureRoot, EnsureSigned}; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; use pallet_identity::legacy::IdentityInfo; use pallet_session::historical as session_historical; @@ -61,12 +61,15 @@ use runtime_common::{ impls::{ LocatableAssetConverter, ToAuthor, VersionedLocatableAsset, VersionedLocationConverter, }, - paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, BalanceToU256, BlockHashCount, - BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance, + paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, + traits::Leaser, + BalanceToU256, BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, + U256ToBalance, }; use runtime_parachains::{ - assigner_parachains as parachains_assigner_parachains, - configuration as parachains_configuration, disputes as parachains_disputes, + assigner_coretime as parachains_assigner_coretime, + assigner_on_demand as parachains_assigner_on_demand, configuration as parachains_configuration, + coretime, disputes as parachains_disputes, disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, inclusion::{AggregateMessageOrigin, UmpQueueId}, @@ -147,7 +150,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westend"), impl_name: create_runtime_str!("parity-westend"), authoring_version: 2, - spec_version: 1_006_000, + spec_version: 1_008_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 24, @@ -197,6 +200,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type MaxConsumers = frame_support::traits::ConstU32<16>; } @@ -304,7 +308,6 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = ConstU32<1>; - type MaxHolds = ConstU32<1>; } parameter_types! { @@ -383,6 +386,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -818,7 +822,7 @@ where // so the actual block number is `n`. .saturating_sub(1); let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -830,16 +834,17 @@ where frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); - let raw_payload = SignedPayload::new(call, extra) + ) + .into(); + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; - let (call, extra, _) = raw_payload.deconstruct(); + let (call, tx_ext, _) = raw_payload.deconstruct(); let address = ::Lookup::unlookup(account); - Some((call, (address, signature, extra))) + Some((call, (address, signature, tx_ext))) } } @@ -1144,7 +1149,7 @@ impl parachains_paras::Config for Runtime { type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; type OnNewHead = (); - type AssignCoretime = (); + type AssignCoretime = CoretimeAssignmentProvider; } parameter_types! { @@ -1213,20 +1218,40 @@ impl parachains_paras_inherent::Config for Runtime { impl parachains_scheduler::Config for Runtime { // If you change this, make sure the `Assignment` type of the new provider is binary compatible, // otherwise provide a migration. - type AssignmentProvider = ParachainsAssignmentProvider; + type AssignmentProvider = CoretimeAssignmentProvider; } parameter_types! { pub const BrokerId: u32 = BROKER_ID; } -impl parachains_assigner_parachains::Config for Runtime {} +impl coretime::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type BrokerId = BrokerId; + type WeightInfo = weights::runtime_parachains_coretime::WeightInfo; + type SendXcm = crate::xcm_config::XcmRouter; +} + +parameter_types! { + pub const OnDemandTrafficDefaultValue: FixedU128 = FixedU128::from_u32(1); +} + +impl parachains_assigner_on_demand::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type TrafficDefaultValue = OnDemandTrafficDefaultValue; + type WeightInfo = weights::runtime_parachains_assigner_on_demand::WeightInfo; +} + +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 CoretimeOnNewSession = (); + type CoretimeOnNewSession = Coretime; } impl paras_sudo_wrapper::Config for Runtime {} @@ -1342,8 +1367,7 @@ impl auctions::Config for Runtime { impl identity_migrator::Config for Runtime { type RuntimeEvent = RuntimeEvent; - // To be changed to `EnsureSigned` once there is a People Chain to migrate to. - type Reaper = EnsureRoot; + type Reaper = EnsureSigned; type ReapIdentityHandler = ToParachainIdentityReaper; type WeightInfo = weights::runtime_common_identity_migrator::WeightInfo; } @@ -1414,13 +1438,6 @@ construct_runtime! { Offences: pallet_offences = 7, Historical: session_historical = 27, - // BEEFY Bridges support. - Beefy: pallet_beefy = 200, - // MMR leaf construction must be before session in order to have leaf contents refer to - // block consistently. see substrate issue #11797 for details. - Mmr: pallet_mmr = 201, - BeefyMmrLeaf: pallet_beefy_mmr = 202, - Session: pallet_session = 8, Grandpa: pallet_grandpa = 10, AuthorityDiscovery: pallet_authority_discovery = 12, @@ -1488,7 +1505,8 @@ construct_runtime! { ParaSessionInfo: parachains_session_info = 52, ParasDisputes: parachains_disputes = 53, ParasSlashing: parachains_slashing = 54, - ParachainsAssignmentProvider: parachains_assigner_parachains = 55, + OnDemandAssignmentProvider: parachains_assigner_on_demand = 56, + CoretimeAssignmentProvider: parachains_assigner_coretime = 57, // Parachain Onboarding Pallets. Start indices at 60 to leave room. Registrar: paras_registrar = 60, @@ -1497,6 +1515,7 @@ construct_runtime! { Auctions: auctions = 63, Crowdloan: crowdloan = 64, AssignedSlots: assigned_slots = 65, + Coretime: coretime = 66, // Pallet for sending XCM. XcmPallet: pallet_xcm = 99, @@ -1510,6 +1529,13 @@ construct_runtime! { // Root testing pallet. RootTesting: pallet_root_testing = 102, + // BEEFY Bridges support. + Beefy: pallet_beefy = 200, + // MMR leaf construction must be after session in order to have a leaf's next_auth_set + // refer to block. See issue polkadot-fellows/runtimes#160 for details. + Mmr: pallet_mmr = 201, + BeefyMmrLeaf: pallet_beefy_mmr = 202, + // Pallet for migrating Identity to a parachain. To be removed post-migration. IdentityMigrator: identity_migrator = 248, } @@ -1525,8 +1551,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// `BlockId` type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `SignedExtension` to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -1557,6 +1583,24 @@ pub mod migrations { #[cfg(feature = "try-runtime")] use sp_core::crypto::ByteArray; + 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::Pallet::::lease(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())) + } + } + parameter_types! { pub const ImOnlinePalletName: &'static str = "ImOnline"; } @@ -1649,7 +1693,7 @@ pub mod migrations { pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, - pallet_nomination_pools::migration::versioned::V7ToV8, + pallet_nomination_pools::migration::unversioned::TotalValueLockedSync, UpgradeSessionKeys, frame_support::migrations::RemovePallet< ImOnlinePalletName, @@ -1658,12 +1702,21 @@ pub mod migrations { // Migrate Identity pallet for Usernames pallet_identity::migration::versioned::V0ToV1, parachains_configuration::migration::v11::MigrateToV11, + parachains_configuration::migration::v12::MigrateToV12, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, + // Migrate from legacy lease to coretime. Needs to run after configuration v11 + coretime::migration::MigrateToCoretime< + Runtime, + crate::xcm_config::XcmRouter, + GetLegacyLeaseImpl, + >, ); } /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -1674,7 +1727,7 @@ pub type Executive = frame_executive::Executive< Migrations, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; #[cfg(feature = "runtime-benchmarks")] mod benches { @@ -1696,6 +1749,8 @@ mod benches { [runtime_parachains::initializer, Initializer] [runtime_parachains::paras, Paras] [runtime_parachains::paras_inherent, ParaInherent] + [runtime_parachains::assigner_on_demand, OnDemandAssignmentProvider] + [runtime_parachains::coretime, Coretime] // Substrate [pallet_bags_list, VoterList] [pallet_balances, Balances] @@ -1718,7 +1773,9 @@ mod benches { [pallet_staking, Staking] [pallet_sudo, Sudo] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_treasury, Treasury] [pallet_utility, Utility] [pallet_vesting, Vesting] @@ -1742,7 +1799,7 @@ sp_api::impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -1794,6 +1851,12 @@ sp_api::impl_runtime_apis! { impl offchain_primitives::OffchainWorkerApi for Runtime { fn offchain_worker(header: &::Header) { + use sp_runtime::{traits::Header, DigestItem}; + + if header.digest().logs().iter().any(|di| di == &DigestItem::RuntimeEnvironmentUpdated) { + pallet_im_online::migration::clear_offchain_storage(Session::validators().len() as u32); + } + Executive::offchain_worker(header) } } @@ -2254,6 +2317,7 @@ sp_api::impl_runtime_apis! { use pallet_election_provider_support_benchmarking::Pallet as ElectionProviderBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; @@ -2282,12 +2346,42 @@ sp_api::impl_runtime_apis! { use pallet_election_provider_support_benchmarking::Pallet as ElectionProviderBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; impl pallet_session_benchmarking::Config for Runtime {} impl pallet_offences_benchmarking::Config for Runtime {} impl pallet_election_provider_support_benchmarking::Config for Runtime {} + + use xcm_config::{AssetHub, TokenLocation}; + + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + pub AssetHubParaId: ParaId = westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + pub const RandomParaId: ParaId = ParaId::new(43211234); + } + impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = ( + runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + AssetHubParaId, + (), + >, + runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForChildParachainDelivery, + RandomParaId, + (), + > + ); + fn reachable_dest() -> Option { Some(crate::xcm_config::AssetHub::get()) } @@ -2295,7 +2389,7 @@ sp_api::impl_runtime_apis! { fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { // Relay/native token can be teleported to/from AH. Some(( - Asset { fun: Fungible(EXISTENTIAL_DEPOSIT), id: AssetId(Here.into()) }, + Asset { fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, crate::xcm_config::AssetHub::get(), )) } @@ -2304,10 +2398,10 @@ sp_api::impl_runtime_apis! { // Relay can reserve transfer native token to some random parachain. Some(( Asset { - fun: Fungible(EXISTENTIAL_DEPOSIT), + fun: Fungible(ExistentialDeposit::get()), id: AssetId(Here.into()) }, - crate::Junction::Parachain(43211234).into(), + crate::Junction::Parachain(RandomParaId::get().into()).into(), )) } @@ -2325,6 +2419,13 @@ sp_api::impl_runtime_apis! { dest ) } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::here()), + fun: Fungible(ExistentialDeposit::get()), + } + } } impl frame_system_benchmarking::Config for Runtime {} impl pallet_nomination_pools_benchmarking::Config for Runtime {} @@ -2334,15 +2435,6 @@ sp_api::impl_runtime_apis! { AssetId, Fungibility::*, InteriorLocation, Junction, Junctions::*, Asset, Assets, Location, NetworkId, Response, }; - use xcm_config::{AssetHub, TokenLocation}; - - parameter_types! { - pub ExistentialDepositAsset: Option = Some(( - TokenLocation::get(), - ExistentialDeposit::get() - ).into()); - pub ToParachain: ParaId = westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(); - } impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; @@ -2351,7 +2443,7 @@ sp_api::impl_runtime_apis! { xcm_config::XcmConfig, ExistentialDepositAsset, xcm_config::PriceForChildParachainDelivery, - ToParachain, + AssetHubParaId, (), >; fn valid_destination() -> Result { @@ -2422,6 +2514,13 @@ sp_api::impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { + id: AssetId(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { // Westend doesn't support asset locking Err(BenchmarkError::Skip) diff --git a/polkadot/runtime/westend/src/weights/frame_system_extensions.rs b/polkadot/runtime/westend/src/weights/frame_system_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..e4e1a799ec6e6213e2d7613c6a18a25948298a11 --- /dev/null +++ b/polkadot/runtime/westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,116 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-20, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --steps=2 +// --repeat=2 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --pallet=frame-system-extensions +// --chain=westend-dev +// --output=./polkadot/runtime/westend/src/weights/ +// --header=./polkadot/file_header.txt + +#![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 `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_356_000 picoseconds. + Weight::from_parts(7_805_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_510_000 picoseconds. + Weight::from_parts(10_009_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 761_000 picoseconds. + Weight::from_parts(4_308_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_396_000 picoseconds. + Weight::from_parts(7_585_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 521_000 picoseconds. + Weight::from_parts(4_168_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 541_000 picoseconds. + Weight::from_parts(4_228_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 3_807_000 picoseconds. + Weight::from_parts(8_025_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index d8a2ae5d2da6fab158898e6cb6548f5f56aa612c..8aeb42165504998c06ea5e73d4e2edc452d05b45 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -17,6 +17,7 @@ pub mod frame_election_provider_support; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_rate; pub mod pallet_bags_list; pub mod pallet_balances; @@ -24,7 +25,6 @@ pub mod pallet_conviction_voting; pub mod pallet_election_provider_multi_phase; pub mod pallet_fast_unstake; pub mod pallet_identity; -pub mod pallet_im_online; pub mod pallet_indices; pub mod pallet_message_queue; pub mod pallet_multisig; @@ -38,6 +38,7 @@ pub mod pallet_session; pub mod pallet_staking; pub mod pallet_sudo; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_treasury; pub mod pallet_utility; pub mod pallet_vesting; diff --git a/polkadot/runtime/westend/src/weights/pallet_balances.rs b/polkadot/runtime/westend/src/weights/pallet_balances.rs index 4508507206c27ae10137194d62d1981c6a59b23d..25626e940209d50859a57dee60f483b15a8db257 100644 --- a/polkadot/runtime/westend/src/weights/pallet_balances.rs +++ b/polkadot/runtime/westend/src/weights/pallet_balances.rs @@ -17,27 +17,25 @@ //! Autogenerated weights for `pallet_balances` //! //! 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: `[]` +//! DATE: 2024-01-20, 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-j8vvqcjr-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=pallet_balances // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/westend/src/weights/ +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_balances +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,104 +48,112 @@ use core::marker::PhantomData; /// Weight functions for `pallet_balances`. pub struct WeightInfo(PhantomData); impl pallet_balances::WeightInfo for 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`) fn transfer_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 57_163_000 picoseconds. - Weight::from_parts(58_105_000, 0) + // Minimum execution time: 43_680_000 picoseconds. + Weight::from_parts(45_012_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 43_085_000 picoseconds. - Weight::from_parts(43_779_000, 0) + // Minimum execution time: 34_038_000 picoseconds. + Weight::from_parts(35_771_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// 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` // Estimated: `3593` - // Minimum execution time: 16_153_000 picoseconds. - Weight::from_parts(16_725_000, 0) + // Minimum execution time: 12_609_000 picoseconds. + Weight::from_parts(13_142_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// 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` // Estimated: `3593` - // Minimum execution time: 23_335_000 picoseconds. - Weight::from_parts(23_715_000, 0) + // Minimum execution time: 17_533_000 picoseconds. + Weight::from_parts(18_061_000, 0) .saturating_add(Weight::from_parts(0, 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`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 58_776_000 picoseconds. - Weight::from_parts(59_353_000, 0) + // Minimum execution time: 45_278_000 picoseconds. + Weight::from_parts(46_670_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// 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`) fn transfer_all() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 52_826_000 picoseconds. - Weight::from_parts(53_816_000, 0) + // Minimum execution time: 43_125_000 picoseconds. + Weight::from_parts(43_925_000, 0) .saturating_add(Weight::from_parts(0, 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: `System::Account` (r:1 w:1) + /// 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` // Estimated: `3593` - // Minimum execution time: 19_400_000 picoseconds. - Weight::from_parts(19_746_000, 0) + // Minimum execution time: 15_580_000 picoseconds. + Weight::from_parts(16_023_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: System Account (r:999 w:999) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `System::Account` (r:999 w:999) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `u` is `[1, 1000]`. fn upgrade_accounts(u: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + u * (135 ±0)` + // Measured: `0 + u * (136 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 18_465_000 picoseconds. - Weight::from_parts(18_670_000, 0) + // Minimum execution time: 14_868_000 picoseconds. + Weight::from_parts(15_130_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 22_827 - .saturating_add(Weight::from_parts(17_357_501, 0).saturating_mul(u.into())) + // Standard Error: 10_719 + .saturating_add(Weight::from_parts(13_394_926, 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())) } + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_174_000 picoseconds. + Weight::from_parts(5_457_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } } diff --git a/polkadot/runtime/westend/src/weights/pallet_im_online.rs b/polkadot/runtime/westend/src/weights/pallet_im_online.rs deleted file mode 100644 index a83cd43804dfdc1ca8827a1988221dd0c6f4ee4d..0000000000000000000000000000000000000000 --- a/polkadot/runtime/westend/src/weights/pallet_im_online.rs +++ /dev/null @@ -1,77 +0,0 @@ -// 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_im_online` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-30, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `build-host`, CPU: `AMD EPYC 7601 32-Core Processor` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=westend-dev -// --steps=50 -// --repeat=20 -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --pallet=pallet_im_online -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./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_im_online`. -pub struct WeightInfo(PhantomData); -impl pallet_im_online::WeightInfo for WeightInfo { - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ImOnline Keys (r:1 w:0) - /// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen) - /// Storage: ImOnline ReceivedHeartbeats (r:1 w:1) - /// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(1028), added: 3503, mode: MaxEncodedLen) - /// Storage: ImOnline AuthoredBlocks (r:1 w:0) - /// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) - /// The range of component `k` is `[1, 1000]`. - fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `361 + k * (32 ±0)` - // Estimated: `321487 + k * (1761 ±0)` - // Minimum execution time: 122_571_000 picoseconds. - Weight::from_parts(162_954_849, 0) - .saturating_add(Weight::from_parts(0, 321487)) - // Standard Error: 8_676 - .saturating_add(Weight::from_parts(11_122, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(1)) - .saturating_add(Weight::from_parts(0, 1761).saturating_mul(k.into())) - } -} diff --git a/polkadot/runtime/westend/src/weights/pallet_scheduler.rs b/polkadot/runtime/westend/src/weights/pallet_scheduler.rs index 7291b98093300ef887d422051dcf6be7274c0d12..beef3796dea6e5ab3a47a62238422df652f30797 100644 --- a/polkadot/runtime/westend/src/weights/pallet_scheduler.rs +++ b/polkadot/runtime/westend/src/weights/pallet_scheduler.rs @@ -17,27 +17,25 @@ //! Autogenerated weights for `pallet_scheduler` //! //! 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: `[]` +//! DATE: 2024-01-25, 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-grjcggob-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=pallet_scheduler // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/westend/src/weights/ +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_scheduler +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,30 +48,30 @@ use core::marker::PhantomData; /// Weight functions for `pallet_scheduler`. pub struct WeightInfo(PhantomData); impl pallet_scheduler::WeightInfo for WeightInfo { - /// Storage: Scheduler IncompleteSince (r:1 w:1) - /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Scheduler::IncompleteSince` (r:1 w:1) + /// Proof: `Scheduler::IncompleteSince` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn service_agendas_base() -> Weight { // Proof Size summary in bytes: // Measured: `69` // Estimated: `1489` - // Minimum execution time: 3_991_000 picoseconds. - Weight::from_parts(4_160_000, 0) + // Minimum execution time: 3_220_000 picoseconds. + Weight::from_parts(3_512_000, 0) .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 50]`. fn service_agenda_base(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `116 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 3_647_000 picoseconds. - Weight::from_parts(6_608_270, 0) + // Minimum execution time: 3_565_000 picoseconds. + Weight::from_parts(6_102_216, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 2_516 - .saturating_add(Weight::from_parts(892_866, 0).saturating_mul(s.into())) + // Standard Error: 1_413 + .saturating_add(Weight::from_parts(339_016, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -81,36 +79,38 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_552_000 picoseconds. - Weight::from_parts(5_836_000, 0) + // Minimum execution time: 2_940_000 picoseconds. + Weight::from_parts(3_070_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Preimage PreimageFor (r:1 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `217 + s * (1 ±0)` // Estimated: `3682 + s * (1 ±0)` - // Minimum execution time: 20_583_000 picoseconds. - Weight::from_parts(20_771_000, 0) + // Minimum execution time: 16_602_000 picoseconds. + Weight::from_parts(16_834_000, 0) .saturating_add(Weight::from_parts(0, 3682)) - // Standard Error: 11 - .saturating_add(Weight::from_parts(2_250, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2)) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_307, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) } - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn service_task_named() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_271_000 picoseconds. - Weight::from_parts(7_447_000, 0) + // Minimum execution time: 4_202_000 picoseconds. + Weight::from_parts(4_383_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -118,90 +118,169 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_547_000 picoseconds. - Weight::from_parts(5_776_000, 0) + // Minimum execution time: 2_917_000 picoseconds. + Weight::from_parts(3_043_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_480_000 picoseconds. - Weight::from_parts(2_628_000, 0) + // Minimum execution time: 1_707_000 picoseconds. + Weight::from_parts(1_802_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_479_000 picoseconds. - Weight::from_parts(2_626_000, 0) + // Minimum execution time: 1_671_000 picoseconds. + Weight::from_parts(1_796_000, 0) .saturating_add(Weight::from_parts(0, 0)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 49]`. fn schedule(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `116 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 13_350_000 picoseconds. - Weight::from_parts(15_289_847, 0) + // Minimum execution time: 9_313_000 picoseconds. + Weight::from_parts(12_146_613, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 5_375 - .saturating_add(Weight::from_parts(974_567, 0).saturating_mul(s.into())) + // Standard Error: 1_381 + .saturating_add(Weight::from_parts(360_418, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 50]`. fn cancel(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `116 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 17_646_000 picoseconds. - Weight::from_parts(15_858_434, 0) + // Minimum execution time: 13_079_000 picoseconds. + Weight::from_parts(12_921_017, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 5_354 - .saturating_add(Weight::from_parts(1_697_642, 0).saturating_mul(s.into())) + // Standard Error: 1_112 + .saturating_add(Weight::from_parts(538_089, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 49]`. fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `293 + s * (185 ±0)` // Estimated: `42428` - // Minimum execution time: 16_419_000 picoseconds. - Weight::from_parts(19_868_760, 0) + // Minimum execution time: 12_458_000 picoseconds. + Weight::from_parts(16_009_539, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 6_915 - .saturating_add(Weight::from_parts(1_010_225, 0).saturating_mul(s.into())) + // Standard Error: 2_260 + .saturating_add(Weight::from_parts(399_245, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 50]`. fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `319 + s * (185 ±0)` // Estimated: `42428` - // Minimum execution time: 19_574_000 picoseconds. - Weight::from_parts(18_453_197, 0) + // Minimum execution time: 15_173_000 picoseconds. + Weight::from_parts(15_602_728, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 6_009 - .saturating_add(Weight::from_parts(1_707_130, 0).saturating_mul(s.into())) + // Standard Error: 1_302 + .saturating_add(Weight::from_parts(557_878, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Scheduler::Retries` (r:1 w:2) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 50]`. + fn schedule_retry(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `197` + // Estimated: `42428` + // Minimum execution time: 13_531_000 picoseconds. + Weight::from_parts(13_985_249, 0) + .saturating_add(Weight::from_parts(0, 42428)) + // Standard Error: 619 + .saturating_add(Weight::from_parts(39_068, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `116 + s * (177 ±0)` + // Estimated: `42428` + // Minimum execution time: 8_050_000 picoseconds. + Weight::from_parts(8_440_627, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `325 + s * (185 ±0)` + // Estimated: `42428` + // Minimum execution time: 10_876_000 picoseconds. + Weight::from_parts(11_708_172, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `116 + s * (177 ±0)` + // Estimated: `42428` + // Minimum execution time: 8_050_000 picoseconds. + Weight::from_parts(8_440_627, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `325 + s * (185 ±0)` + // Estimated: `42428` + // Minimum execution time: 10_876_000 picoseconds. + Weight::from_parts(11_708_172, 0) + .saturating_add(Weight::from_parts(0, 42428)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/westend/src/weights/pallet_staking.rs b/polkadot/runtime/westend/src/weights/pallet_staking.rs index 1ecd44747ef5140b0f83f5226d8368a9231085d0..7a641e36a126bada81cecceadd9c22f57e5b88e9 100644 --- a/polkadot/runtime/westend/src/weights/pallet_staking.rs +++ b/polkadot/runtime/westend/src/weights/pallet_staking.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_staking` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-01-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-itmxxexx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-8idpd4bs-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -62,8 +62,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `894` // Estimated: `4764` - // Minimum execution time: 38_316_000 picoseconds. - Weight::from_parts(40_022_000, 0) + // Minimum execution time: 37_340_000 picoseconds. + Weight::from_parts(38_930_000, 0) .saturating_add(Weight::from_parts(0, 4764)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(4)) @@ -84,8 +84,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1921` // Estimated: `8877` - // Minimum execution time: 81_027_000 picoseconds. - Weight::from_parts(83_964_000, 0) + // Minimum execution time: 80_630_000 picoseconds. + Weight::from_parts(82_196_000, 0) .saturating_add(Weight::from_parts(0, 8877)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) @@ -112,8 +112,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2128` // Estimated: `8877` - // Minimum execution time: 85_585_000 picoseconds. - Weight::from_parts(87_256_000, 0) + // Minimum execution time: 83_523_000 picoseconds. + Weight::from_parts(86_639_000, 0) .saturating_add(Weight::from_parts(0, 8877)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(7)) @@ -133,11 +133,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1075` // Estimated: `4764` - // Minimum execution time: 39_520_000 picoseconds. - Weight::from_parts(41_551_548, 0) + // Minimum execution time: 38_636_000 picoseconds. + Weight::from_parts(40_399_283, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 1_094 - .saturating_add(Weight::from_parts(50_426, 0).saturating_mul(s.into())) + // Standard Error: 869 + .saturating_add(Weight::from_parts(37_752, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -174,11 +174,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2127 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 82_915_000 picoseconds. - Weight::from_parts(89_597_160, 0) + // Minimum execution time: 81_301_000 picoseconds. + Weight::from_parts(88_609_205, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 3_146 - .saturating_add(Weight::from_parts(1_228_061, 0).saturating_mul(s.into())) + // Standard Error: 3_388 + .saturating_add(Weight::from_parts(1_253_692, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13)) .saturating_add(T::DbWeight::get().writes(11)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -210,8 +210,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1301` // Estimated: `4556` - // Minimum execution time: 48_070_000 picoseconds. - Weight::from_parts(49_226_000, 0) + // Minimum execution time: 47_292_000 picoseconds. + Weight::from_parts(48_566_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(5)) @@ -225,11 +225,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1243 + k * (569 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 29_140_000 picoseconds. - Weight::from_parts(30_225_579, 0) + // Minimum execution time: 28_840_000 picoseconds. + Weight::from_parts(27_510_817, 0) .saturating_add(Weight::from_parts(0, 4556)) - // Standard Error: 5_394 - .saturating_add(Weight::from_parts(6_401_367, 0).saturating_mul(k.into())) + // Standard Error: 6_603 + .saturating_add(Weight::from_parts(6_268_853, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -262,11 +262,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1797 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 59_287_000 picoseconds. - Weight::from_parts(58_285_052, 0) + // Minimum execution time: 57_537_000 picoseconds. + Weight::from_parts(55_854_233, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 14_556 - .saturating_add(Weight::from_parts(3_863_008, 0).saturating_mul(n.into())) + // Standard Error: 14_427 + .saturating_add(Weight::from_parts(3_844_957, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6)) @@ -290,8 +290,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1581` // Estimated: `6248` - // Minimum execution time: 51_035_000 picoseconds. - Weight::from_parts(52_163_000, 0) + // Minimum execution time: 49_997_000 picoseconds. + Weight::from_parts(51_266_000, 0) .saturating_add(Weight::from_parts(0, 6248)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(6)) @@ -306,8 +306,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `865` // Estimated: `4556` - // Minimum execution time: 15_809_000 picoseconds. - Weight::from_parts(16_451_000, 0) + // Minimum execution time: 15_342_000 picoseconds. + Weight::from_parts(15_970_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -322,8 +322,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `932` // Estimated: `4556` - // Minimum execution time: 21_695_000 picoseconds. - Weight::from_parts(22_351_000, 0) + // Minimum execution time: 20_719_000 picoseconds. + Weight::from_parts(21_373_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -336,8 +336,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `865` // Estimated: `4556` - // Minimum execution time: 18_548_000 picoseconds. - Weight::from_parts(19_205_000, 0) + // Minimum execution time: 18_237_000 picoseconds. + Weight::from_parts(18_896_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) @@ -348,8 +348,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_193_000 picoseconds. - Weight::from_parts(2_408_000, 0) + // Minimum execution time: 1_946_000 picoseconds. + Weight::from_parts(2_131_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -359,8 +359,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_475_000 picoseconds. - Weight::from_parts(7_874_000, 0) + // Minimum execution time: 6_840_000 picoseconds. + Weight::from_parts(7_208_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -370,8 +370,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_393_000 picoseconds. - Weight::from_parts(7_643_000, 0) + // Minimum execution time: 6_812_000 picoseconds. + Weight::from_parts(7_254_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -381,8 +381,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_474_000 picoseconds. - Weight::from_parts(7_814_000, 0) + // Minimum execution time: 6_787_000 picoseconds. + Weight::from_parts(7_206_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -393,11 +393,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_358_000 picoseconds. - Weight::from_parts(2_589_423, 0) + // Minimum execution time: 2_045_000 picoseconds. + Weight::from_parts(2_281_841, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 81 - .saturating_add(Weight::from_parts(13_612, 0).saturating_mul(v.into())) + // Standard Error: 70 + .saturating_add(Weight::from_parts(11_592, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Staking::Ledger` (r:751 w:1502) @@ -411,11 +411,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `668 + i * (148 ±0)` // Estimated: `990 + i * (3566 ±0)` - // Minimum execution time: 1_934_000 picoseconds. - Weight::from_parts(2_070_000, 0) + // Minimum execution time: 1_657_000 picoseconds. + Weight::from_parts(1_702_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 19_129 - .saturating_add(Weight::from_parts(13_231_580, 0).saturating_mul(i.into())) + // Standard Error: 20_041 + .saturating_add(Weight::from_parts(13_165_254, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3566).saturating_mul(i.into())) @@ -453,11 +453,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2127 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 80_290_000 picoseconds. - Weight::from_parts(87_901_664, 0) + // Minimum execution time: 78_774_000 picoseconds. + Weight::from_parts(85_770_713, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 2_960 - .saturating_add(Weight::from_parts(1_195_050, 0).saturating_mul(s.into())) + // Standard Error: 2_815 + .saturating_add(Weight::from_parts(1_244_494, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13)) .saturating_add(T::DbWeight::get().writes(12)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -470,11 +470,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `66639` // Estimated: `70104` - // Minimum execution time: 132_682_000 picoseconds. - Weight::from_parts(932_504_297, 0) + // Minimum execution time: 129_905_000 picoseconds. + Weight::from_parts(932_195_554, 0) .saturating_add(Weight::from_parts(0, 70104)) - // Standard Error: 57_593 - .saturating_add(Weight::from_parts(4_829_705, 0).saturating_mul(s.into())) + // Standard Error: 57_492 + .saturating_add(Weight::from_parts(4_826_754, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -510,12 +510,12 @@ impl pallet_staking::WeightInfo for WeightInfo { fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `8249 + n * (396 ±0)` - // Estimated: `10779 + n * (3774 ±3)` - // Minimum execution time: 129_091_000 picoseconds. - Weight::from_parts(166_186_167, 0) + // Estimated: `10779 + n * (3774 ±0)` + // Minimum execution time: 127_094_000 picoseconds. + Weight::from_parts(160_088_053, 0) .saturating_add(Weight::from_parts(0, 10779)) - // Standard Error: 36_242 - .saturating_add(Weight::from_parts(40_467_481, 0).saturating_mul(n.into())) + // Standard Error: 32_978 + .saturating_add(Weight::from_parts(39_845_710, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4)) @@ -539,11 +539,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1922 + l * (5 ±0)` // Estimated: `8877` - // Minimum execution time: 77_461_000 picoseconds. - Weight::from_parts(80_118_021, 0) + // Minimum execution time: 75_672_000 picoseconds. + Weight::from_parts(78_708_335, 0) .saturating_add(Weight::from_parts(0, 8877)) - // Standard Error: 4_343 - .saturating_add(Weight::from_parts(59_113, 0).saturating_mul(l.into())) + // Standard Error: 3_387 + .saturating_add(Weight::from_parts(37_084, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -578,11 +578,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `2127 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 89_366_000 picoseconds. - Weight::from_parts(91_964_557, 0) + // Minimum execution time: 87_991_000 picoseconds. + Weight::from_parts(90_272_005, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 2_799 - .saturating_add(Weight::from_parts(1_206_123, 0).saturating_mul(s.into())) + // Standard Error: 2_815 + .saturating_add(Weight::from_parts(1_232_322, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(11)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -627,14 +627,14 @@ impl pallet_staking::WeightInfo for WeightInfo { fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (716 ±0) + v * (3594 ±0)` - // Estimated: `456136 + n * (3566 ±4) + v * (3566 ±40)` - // Minimum execution time: 520_430_000 picoseconds. - Weight::from_parts(527_125_000, 0) + // Estimated: `456136 + n * (3566 ±0) + v * (3566 ±0)` + // Minimum execution time: 528_862_000 picoseconds. + Weight::from_parts(534_620_000, 0) .saturating_add(Weight::from_parts(0, 456136)) - // Standard Error: 1_974_092 - .saturating_add(Weight::from_parts(64_885_491, 0).saturating_mul(v.into())) - // Standard Error: 196_707 - .saturating_add(Weight::from_parts(18_100_326, 0).saturating_mul(n.into())) + // Standard Error: 2_005_553 + .saturating_add(Weight::from_parts(65_586_008, 0).saturating_mul(v.into())) + // Standard Error: 199_842 + .saturating_add(Weight::from_parts(18_155_389, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(184)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -665,13 +665,13 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `3108 + n * (907 ±0) + v * (391 ±0)` // Estimated: `456136 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 33_917_323_000 picoseconds. - Weight::from_parts(34_173_565_000, 0) + // Minimum execution time: 33_532_110_000 picoseconds. + Weight::from_parts(33_926_321_000, 0) .saturating_add(Weight::from_parts(0, 456136)) - // Standard Error: 367_135 - .saturating_add(Weight::from_parts(4_696_840, 0).saturating_mul(v.into())) - // Standard Error: 367_135 - .saturating_add(Weight::from_parts(3_889_075, 0).saturating_mul(n.into())) + // Standard Error: 374_134 + .saturating_add(Weight::from_parts(4_627_629, 0).saturating_mul(v.into())) + // Standard Error: 374_134 + .saturating_add(Weight::from_parts(4_068_168, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(179)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -688,11 +688,11 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `946 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_447_197_000 picoseconds. - Weight::from_parts(13_003_614, 0) + // Minimum execution time: 2_395_956_000 picoseconds. + Weight::from_parts(88_416_870, 0) .saturating_add(Weight::from_parts(0, 3510)) - // Standard Error: 9_738 - .saturating_add(Weight::from_parts(4_953_442, 0).saturating_mul(v.into())) + // Standard Error: 8_731 + .saturating_add(Weight::from_parts(4_750_956, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -703,6 +703,8 @@ impl pallet_staking::WeightInfo for WeightInfo { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -713,10 +715,10 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_714_000 picoseconds. - Weight::from_parts(3_956_000, 0) + // Minimum execution time: 3_761_000 picoseconds. + Weight::from_parts(4_013_000, 0) .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `Staking::MinCommission` (r:0 w:1) /// Proof: `Staking::MinCommission` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -724,6 +726,8 @@ impl pallet_staking::WeightInfo for WeightInfo { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -734,10 +738,10 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_361_000 picoseconds. - Weight::from_parts(3_632_000, 0) + // Minimum execution time: 3_325_000 picoseconds. + Weight::from_parts(3_519_000, 0) .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) @@ -765,8 +769,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `1870` // Estimated: `6248` - // Minimum execution time: 65_329_000 picoseconds. - Weight::from_parts(67_247_000, 0) + // Minimum execution time: 63_583_000 picoseconds. + Weight::from_parts(65_917_000, 0) .saturating_add(Weight::from_parts(0, 6248)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(6)) @@ -779,8 +783,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `658` // Estimated: `3510` - // Minimum execution time: 11_760_000 picoseconds. - Weight::from_parts(12_095_000, 0) + // Minimum execution time: 10_975_000 picoseconds. + Weight::from_parts(11_328_000, 0) .saturating_add(Weight::from_parts(0, 3510)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -791,8 +795,8 @@ impl pallet_staking::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_256_000 picoseconds. - Weight::from_parts(2_378_000, 0) + // Minimum execution time: 1_954_000 picoseconds. + Weight::from_parts(2_081_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/westend/src/weights/pallet_sudo.rs b/polkadot/runtime/westend/src/weights/pallet_sudo.rs index e9ab3ad37a4cca6483dc7fdeb98562dd6f1937e6..649c43e031dc4a194bed6564e8e01dc823890c8a 100644 --- a/polkadot/runtime/westend/src/weights/pallet_sudo.rs +++ b/polkadot/runtime/westend/src/weights/pallet_sudo.rs @@ -94,4 +94,15 @@ impl pallet_sudo::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `132` + // Estimated: `1517` + // Minimum execution time: 2_875_000 picoseconds. + Weight::from_parts(6_803_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/polkadot/runtime/westend/src/weights/pallet_transaction_payment.rs b/polkadot/runtime/westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 0000000000000000000000000000000000000000..001a09f103dafcba1955ef14e70f8bbd8113fcb8 --- /dev/null +++ b/polkadot/runtime/westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,65 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot +// benchmark +// pallet +// --steps=2 +// --repeat=2 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --pallet=pallet_transaction_payment +// --chain=westend-dev +// --output=./polkadot/runtime/westend/src/weights/ +// --header=./polkadot/file_header.txt + +#![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_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `156` + // Estimated: `1641` + // Minimum execution time: 30_888_000 picoseconds. + Weight::from_parts(41_308_000, 0) + .saturating_add(Weight::from_parts(0, 1641)) + .saturating_add(T::DbWeight::get().reads(3)) + } +} diff --git a/polkadot/runtime/westend/src/weights/pallet_xcm.rs b/polkadot/runtime/westend/src/weights/pallet_xcm.rs index 493acd0f9e7bdfbfd1e716b7e82a474643f1050b..10725cecf24995e34b3254baa23535cb91dd9981 100644 --- a/polkadot/runtime/westend/src/weights/pallet_xcm.rs +++ b/polkadot/runtime/westend/src/weights/pallet_xcm.rs @@ -16,26 +16,24 @@ //! Autogenerated weights for `pallet_xcm` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-20, 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-bn-ce5rx-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=pallet_xcm // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=westend-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/westend/src/weights/ @@ -50,10 +48,6 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm`. pub struct WeightInfo(PhantomData); impl pallet_xcm::WeightInfo for WeightInfo { - fn transfer_assets() -> Weight { - // TODO: run benchmarks - Weight::zero() - } /// 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) @@ -64,29 +58,73 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn send() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3574` - // Minimum execution time: 28_098_000 picoseconds. - Weight::from_parts(28_887_000, 0) - .saturating_add(Weight::from_parts(0, 3574)) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 25_725_000 picoseconds. + Weight::from_parts(26_174_000, 0) + .saturating_add(Weight::from_parts(0, 3612)) .saturating_add(T::DbWeight::get().reads(4)) .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: `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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn teleport_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 17_609_000 picoseconds. - Weight::from_parts(18_000_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `250` + // Estimated: `6196` + // Minimum execution time: 113_140_000 picoseconds. + Weight::from_parts(116_204_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) } + /// Storage: `System::Account` (r:2 w:2) + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reserve_transfer_assets() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 17_007_000 picoseconds. - Weight::from_parts(17_471_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `302` + // Estimated: `6196` + // Minimum execution time: 108_571_000 picoseconds. + Weight::from_parts(110_650_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `System::Account` (r:2 w:2) + /// 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: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn transfer_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `250` + // Estimated: `6196` + // Minimum execution time: 111_836_000 picoseconds. + Weight::from_parts(114_435_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .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`) @@ -104,8 +142,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_444_000 picoseconds. - Weight::from_parts(7_671_000, 0) + // Minimum execution time: 7_160_000 picoseconds. + Weight::from_parts(7_477_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -113,8 +151,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_126_000 picoseconds. - Weight::from_parts(2_253_000, 0) + // Minimum execution time: 1_934_000 picoseconds. + Weight::from_parts(2_053_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1) @@ -133,11 +171,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_subscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3574` - // Minimum execution time: 31_318_000 picoseconds. - Weight::from_parts(32_413_000, 0) - .saturating_add(Weight::from_parts(0, 3574)) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 31_123_000 picoseconds. + Weight::from_parts(31_798_000, 0) + .saturating_add(Weight::from_parts(0, 3612)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -155,11 +193,11 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_unsubscribe_version_notify() -> Weight { // Proof Size summary in bytes: - // Measured: `289` - // Estimated: `3754` - // Minimum execution time: 35_282_000 picoseconds. - Weight::from_parts(35_969_000, 0) - .saturating_add(Weight::from_parts(0, 3754)) + // Measured: `327` + // Estimated: `3792` + // Minimum execution time: 35_175_000 picoseconds. + Weight::from_parts(36_098_000, 0) + .saturating_add(Weight::from_parts(0, 3792)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -169,45 +207,45 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_247_000 picoseconds. - Weight::from_parts(2_381_000, 0) + // Minimum execution time: 1_974_000 picoseconds. + Weight::from_parts(2_096_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `XcmPallet::SupportedVersion` (r:4 w:2) + /// Storage: `XcmPallet::SupportedVersion` (r:5 w:2) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_supported_version() -> Weight { // Proof Size summary in bytes: - // Measured: `26` - // Estimated: `10916` - // Minimum execution time: 14_512_000 picoseconds. - Weight::from_parts(15_042_000, 0) - .saturating_add(Weight::from_parts(0, 10916)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `22` + // Estimated: `13387` + // Minimum execution time: 16_626_000 picoseconds. + Weight::from_parts(17_170_000, 0) + .saturating_add(Weight::from_parts(0, 13387)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifiers` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifiers` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifiers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notifiers() -> Weight { // Proof Size summary in bytes: - // Measured: `30` - // Estimated: `10920` - // Minimum execution time: 14_659_000 picoseconds. - Weight::from_parts(15_164_000, 0) - .saturating_add(Weight::from_parts(0, 10920)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `26` + // Estimated: `13391` + // Minimum execution time: 16_937_000 picoseconds. + Weight::from_parts(17_447_000, 0) + .saturating_add(Weight::from_parts(0, 13391)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:6 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn already_notified_target() -> Weight { // Proof Size summary in bytes: // Measured: `40` - // Estimated: `13405` - // Minimum execution time: 16_261_000 picoseconds. - Weight::from_parts(16_986_000, 0) - .saturating_add(Weight::from_parts(0, 13405)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `15880` + // Minimum execution time: 19_157_000 picoseconds. + Weight::from_parts(19_659_000, 0) + .saturating_add(Weight::from_parts(0, 15880)) + .saturating_add(T::DbWeight::get().reads(6)) } /// Storage: `XcmPallet::VersionNotifyTargets` (r:2 w:1) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -221,38 +259,38 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_current_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `145` - // Estimated: `6085` - // Minimum execution time: 30_539_000 picoseconds. - Weight::from_parts(31_117_000, 0) - .saturating_add(Weight::from_parts(0, 6085)) + // Measured: `183` + // Estimated: `6123` + // Minimum execution time: 30_699_000 picoseconds. + Weight::from_parts(31_537_000, 0) + .saturating_add(Weight::from_parts(0, 6123)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:3 w:0) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:0) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn notify_target_migration_fail() -> Weight { // Proof Size summary in bytes: // Measured: `69` - // Estimated: `8484` - // Minimum execution time: 9_463_000 picoseconds. - Weight::from_parts(9_728_000, 0) - .saturating_add(Weight::from_parts(0, 8484)) - .saturating_add(T::DbWeight::get().reads(3)) + // Estimated: `10959` + // Minimum execution time: 12_303_000 picoseconds. + Weight::from_parts(12_670_000, 0) + .saturating_add(Weight::from_parts(0, 10959)) + .saturating_add(T::DbWeight::get().reads(4)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_version_notify_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `37` - // Estimated: `10927` - // Minimum execution time: 15_169_000 picoseconds. - Weight::from_parts(15_694_000, 0) - .saturating_add(Weight::from_parts(0, 10927)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `33` + // Estimated: `13398` + // Minimum execution time: 17_129_000 picoseconds. + Weight::from_parts(17_668_000, 0) + .saturating_add(Weight::from_parts(0, 13398)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `XcmPallet::VersionNotifyTargets` (r:4 w:2) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:5 w:2) /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -264,12 +302,12 @@ impl pallet_xcm::WeightInfo for WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) fn migrate_and_notify_old_targets() -> Weight { // Proof Size summary in bytes: - // Measured: `149` - // Estimated: `11039` - // Minimum execution time: 37_549_000 picoseconds. - Weight::from_parts(38_203_000, 0) - .saturating_add(Weight::from_parts(0, 11039)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `183` + // Estimated: `13548` + // Minimum execution time: 39_960_000 picoseconds. + Weight::from_parts(41_068_000, 0) + .saturating_add(Weight::from_parts(0, 13548)) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `XcmPallet::QueryCounter` (r:1 w:1) @@ -280,8 +318,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 2_947_000 picoseconds. - Weight::from_parts(3_117_000, 0) + // Minimum execution time: 2_333_000 picoseconds. + Weight::from_parts(2_504_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -292,10 +330,22 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7576` // Estimated: `11041` - // Minimum execution time: 24_595_000 picoseconds. - Weight::from_parts(24_907_000, 0) + // Minimum execution time: 22_932_000 picoseconds. + Weight::from_parts(23_307_000, 0) .saturating_add(Weight::from_parts(0, 11041)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_assets() -> Weight { + // Proof Size summary in bytes: + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 34_558_000 picoseconds. + Weight::from_parts(35_299_000, 0) + .saturating_add(Weight::from_parts(0, 3488)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs index 3a4813b667c68ea6400c9ad58cea8fd1bfece5d0..8fa3207c6446082a97877f488913d9c063114fca 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs +++ b/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `runtime_parachains::configuration` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-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-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -58,8 +58,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 8_065_000 picoseconds. - Weight::from_parts(8_389_000, 0) + // Minimum execution time: 7_775_000 picoseconds. + Weight::from_parts(8_036_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -74,8 +74,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 8_038_000 picoseconds. - Weight::from_parts(8_463_000, 0) + // Minimum execution time: 7_708_000 picoseconds. + Weight::from_parts(7_971_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +90,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_843_000 picoseconds. - Weight::from_parts(8_216_000, 0) + // Minimum execution time: 7_746_000 picoseconds. + Weight::from_parts(8_028_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -116,8 +116,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_969_000 picoseconds. - Weight::from_parts(8_362_000, 0) + // Minimum execution time: 7_729_000 picoseconds. + Weight::from_parts(7_954_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -132,8 +132,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_084_000 picoseconds. - Weight::from_parts(10_451_000, 0) + // Minimum execution time: 9_871_000 picoseconds. + Weight::from_parts(10_075_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -148,8 +148,8 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_948_000 picoseconds. - Weight::from_parts(8_268_000, 0) + // Minimum execution time: 7_869_000 picoseconds. + Weight::from_parts(8_000_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -164,8 +164,24 @@ impl runtime_parachains::configuration::WeightInfo for // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 10_257_000 picoseconds. - Weight::from_parts(10_584_000, 0) + // Minimum execution time: 9_797_000 picoseconds. + Weight::from_parts(10_373_000, 0) + .saturating_add(Weight::from_parts(0, 1636)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`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`) + fn set_config_with_scheduler_params() -> Weight { + // Proof Size summary in bytes: + // Measured: `151` + // Estimated: `1636` + // Minimum execution time: 7_718_000 picoseconds. + Weight::from_parts(7_984_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs index d9f2d45207b923e3afe661a6021629cb8441970e..aa65a2e9034a7c9f10a3d596db9cf8d167f561ac 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs +++ b/polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::coretime` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-r43aesjn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 @@ -32,10 +32,10 @@ // --wasm-execution=compiled // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=runtime_common::coretime -// --chain=rococo-dev +// --pallet=runtime_parachains::coretime +// --chain=westend-dev // --header=./polkadot/file_header.txt -// --output=./polkadot/runtime/rococo/src/weights/ +// --output=./polkadot/runtime/westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -45,28 +45,39 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -use runtime_parachains::configuration::{self, WeightInfo as ConfigWeightInfo}; - -/// Weight functions for `runtime_common::coretime`. +/// Weight functions for `runtime_parachains::coretime`. pub struct WeightInfo(PhantomData); -impl runtime_parachains::coretime::WeightInfo for WeightInfo { +impl runtime_parachains::coretime::WeightInfo for WeightInfo { + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`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`) fn request_core_count() -> Weight { - ::WeightInfo::set_config_with_u32() + // Proof Size summary in bytes: + // Measured: `151` + // Estimated: `1636` + // Minimum execution time: 7_486_000 picoseconds. + Weight::from_parts(7_889_000, 0) + .saturating_add(Weight::from_parts(0, 1636)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `CoreTimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoreTimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `CoreTimeAssignmentProvider::CoreSchedules` (r:0 w:1) - /// Proof: `CoreTimeAssignmentProvider::CoreSchedules` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreSchedules` (r:0 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreSchedules` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `s` is `[1, 100]`. fn assign_core(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `76` - // Estimated: `3541` - // Minimum execution time: 6_275_000 picoseconds. - Weight::from_parts(6_883_543, 0) - .saturating_add(Weight::from_parts(0, 3541)) - // Standard Error: 202 - .saturating_add(Weight::from_parts(15_028, 0).saturating_mul(s.into())) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 9_409_000 picoseconds. + Weight::from_parts(10_177_115, 0) + .saturating_add(Weight::from_parts(0, 3612)) + // Standard Error: 259 + .saturating_add(Weight::from_parts(13_932, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index 3c052057d909d209f75a094515a38d53a1ac2211..c57af987ca78050e129ab37736bfb8df7ea46834 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -34,21 +34,17 @@ use runtime_common::{ }; use sp_core::ConstU32; use westend_runtime_constants::{ - currency::CENTS, - system_parachain::*, - xcm::body::{FELLOWSHIP_ADMIN_INDEX, TREASURER_INDEX}, + currency::CENTS, system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX, }; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, - ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, HashedDescription, IsConcrete, - MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, - XcmFeeToAccount, + ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, FrameTransactionalProcessor, + FungibleAdapter, HashedDescription, IsConcrete, MintLocation, OriginToPluralityVoice, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -75,8 +71,7 @@ pub type LocationConverter = ( HashedDescription>, ); -#[allow(deprecated)] -pub type LocalAssetTransactor = XcmCurrencyAdapter< +pub type LocalAssetTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -117,12 +112,16 @@ parameter_types! { pub AssetHub: Location = Parachain(ASSET_HUB_ID).into_location(); pub Collectives: Location = Parachain(COLLECTIVES_ID).into_location(); pub BridgeHub: Location = Parachain(BRIDGE_HUB_ID).into_location(); + pub Encointer: Location = Parachain(ENCOINTER_ID).into_location(); pub People: Location = Parachain(PEOPLE_ID).into_location(); + pub Broker: Location = Parachain(BROKER_ID).into_location(); pub Wnd: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(TokenLocation::get()) }); pub WndForAssetHub: (AssetFilter, Location) = (Wnd::get(), AssetHub::get()); pub WndForCollectives: (AssetFilter, Location) = (Wnd::get(), Collectives::get()); pub WndForBridgeHub: (AssetFilter, Location) = (Wnd::get(), BridgeHub::get()); + pub WndForEncointer: (AssetFilter, Location) = (Wnd::get(), Encointer::get()); pub WndForPeople: (AssetFilter, Location) = (Wnd::get(), People::get()); + pub WndForBroker: (AssetFilter, Location) = (Wnd::get(), Broker::get()); pub MaxInstructions: u32 = 100; pub MaxAssetsIntoHolding: u32 = 64; } @@ -131,7 +130,9 @@ pub type TrustedTeleporters = ( xcm_builder::Case, xcm_builder::Case, xcm_builder::Case, + xcm_builder::Case, xcm_builder::Case, + xcm_builder::Case, ); pub struct OnlyParachains; @@ -217,6 +218,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } parameter_types! { @@ -227,7 +229,7 @@ parameter_types! { // FellowshipAdmin pluralistic body. pub const FellowshipAdminBodyId: BodyId = BodyId::Index(FELLOWSHIP_ADMIN_INDEX); // `Treasurer` pluralistic body. - pub const TreasurerBodyId: BodyId = BodyId::Index(TREASURER_INDEX); + pub const TreasurerBodyId: BodyId = BodyId::Treasury; } /// Type to convert the `GeneralAdmin` origin to a Plurality `Location` value. @@ -261,7 +263,7 @@ pub type LocalPalletOriginToLocation = ( StakingAdminToPlurality, // FellowshipAdmin origin to be used in XCM as a corresponding Plurality `Location` value. FellowshipAdminToPlurality, - // `Treasurer` origin to be used in XCM as a corresponding Plurality `MultiLocation` value. + // `Treasurer` origin to be used in XCM as a corresponding Plurality `Location` value. TreasurerToPlurality, ); diff --git a/polkadot/scripts/build-only-wasm.sh b/polkadot/scripts/build-only-wasm.sh index b6da3319c8214aeca3ca54b76fda87d83077eec5..50b786dab41014dd53cd3e4dea3e02f66fd8b42d 100755 --- a/polkadot/scripts/build-only-wasm.sh +++ b/polkadot/scripts/build-only-wasm.sh @@ -13,6 +13,14 @@ fi WASM_BUILDER_RUNNER="$PROJECT_ROOT/target/release/wbuild-runner/$1" +fl_cargo () { + if command -v forklift >/dev/null 2>&1; then + forklift cargo "$@"; + else + cargo "$@"; + fi +} + if [ -z "$2" ]; then export WASM_TARGET_DIRECTORY=$(pwd) else @@ -22,8 +30,8 @@ fi if [ -d $WASM_BUILDER_RUNNER ]; then export DEBUG=false export OUT_DIR="$PROJECT_ROOT/target/release/build" - cargo run --release --manifest-path="$WASM_BUILDER_RUNNER/Cargo.toml" \ + fl_cargo run --release --manifest-path="$WASM_BUILDER_RUNNER/Cargo.toml" \ | grep -vE "cargo:rerun-if-|Executing build command" else - cargo build --release -p $1 + fl_cargo build --release -p $1 fi diff --git a/polkadot/statement-table/Cargo.toml b/polkadot/statement-table/Cargo.toml index 9a313882da71ca2c4dc9a7c3f171bc7bab1f05bf..37b8a99d640a2d23e0d7a532adc0c43b04bba1f2 100644 --- a/polkadot/statement-table/Cargo.toml +++ b/polkadot/statement-table/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-statement-table" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -13,3 +13,4 @@ workspace = true parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } sp-core = { path = "../../substrate/primitives/core" } primitives = { package = "polkadot-primitives", path = "../primitives" } +gum = { package = "tracing-gum", path = "../node/gum" } diff --git a/polkadot/statement-table/src/generic.rs b/polkadot/statement-table/src/generic.rs index 22bffde5acc11b8fdbea565d054949caf40d788f..2ee6f6a4f781842866728e2105d6c23e2fa1cd70 100644 --- a/polkadot/statement-table/src/generic.rs +++ b/polkadot/statement-table/src/generic.rs @@ -36,6 +36,7 @@ use primitives::{ }; use parity_scale_codec::{Decode, Encode}; +const LOG_TARGET: &str = "parachain::statement-table"; /// Context for the statement table. pub trait Context { @@ -53,9 +54,6 @@ pub trait Context { /// get the digest of a candidate. fn candidate_digest(candidate: &Self::Candidate) -> Self::Digest; - /// get the group of a candidate. - fn candidate_group(candidate: &Self::Candidate) -> Self::GroupId; - /// Whether a authority is a member of a group. /// Members are meant to submit candidates and vote on validity. fn is_member_of(&self, authority: &Self::AuthorityId, group: &Self::GroupId) -> bool; @@ -342,13 +340,13 @@ impl Table { pub fn import_statement( &mut self, context: &Ctx, + group_id: Ctx::GroupId, statement: SignedStatement, ) -> Option> { let SignedStatement { statement, signature, sender: signer } = statement; - let res = match statement { Statement::Seconded(candidate) => - self.import_candidate(context, signer.clone(), candidate, signature), + self.import_candidate(context, signer.clone(), candidate, signature, group_id), Statement::Valid(digest) => self.validity_vote(context, signer.clone(), digest, ValidityVote::Valid(signature)), }; @@ -387,9 +385,10 @@ impl Table { authority: Ctx::AuthorityId, candidate: Ctx::Candidate, signature: Ctx::Signature, + group: Ctx::GroupId, ) -> ImportResult { - let group = Ctx::candidate_group(&candidate); if !context.is_member_of(&authority, &group) { + gum::debug!(target: LOG_TARGET, authority = ?authority, group = ?group, "New `Misbehavior::UnauthorizedStatement`, candidate backed by validator that doesn't belong to expected group" ); return Err(Misbehavior::UnauthorizedStatement(UnauthorizedStatement { statement: SignedStatement { signature, @@ -634,10 +633,6 @@ mod tests { Digest(candidate.1) } - fn candidate_group(candidate: &Candidate) -> GroupId { - GroupId(candidate.0) - } - fn is_member_of(&self, authority: &AuthorityId, group: &GroupId) -> bool { self.authorities.get(authority).map(|v| v == group).unwrap_or(false) } @@ -675,10 +670,10 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement_a); + table.import_statement(&context, GroupId(2), statement_a); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); - table.import_statement(&context, statement_b); + table.import_statement(&context, GroupId(2), statement_b); assert_eq!( table.detected_misbehavior[&AuthorityId(1)][0], Misbehavior::MultipleCandidates(MultipleCandidates { @@ -711,10 +706,10 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement_a); + table.import_statement(&context, GroupId(2), statement_a); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); - table.import_statement(&context, statement_b); + table.import_statement(&context, GroupId(2), statement_b); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); } @@ -735,7 +730,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert_eq!( table.detected_misbehavior[&AuthorityId(1)][0], @@ -769,7 +764,7 @@ mod tests { }; let candidate_a_digest = Digest(100); - table.import_statement(&context, candidate_a); + table.import_statement(&context, GroupId(2), candidate_a); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2))); @@ -779,7 +774,7 @@ mod tests { signature: Signature(2), sender: AuthorityId(2), }; - table.import_statement(&context, bad_validity_vote); + table.import_statement(&context, GroupId(3), bad_validity_vote); assert_eq!( table.detected_misbehavior[&AuthorityId(2)][0], @@ -811,7 +806,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); let invalid_statement = SignedStatement { @@ -820,7 +815,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, invalid_statement); + table.import_statement(&context, GroupId(2), invalid_statement); assert!(table.detected_misbehavior.contains_key(&AuthorityId(1))); } @@ -842,7 +837,7 @@ mod tests { }; let candidate_digest = Digest(100); - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); let extra_vote = SignedStatement { @@ -851,7 +846,7 @@ mod tests { sender: AuthorityId(1), }; - table.import_statement(&context, extra_vote); + table.import_statement(&context, GroupId(2), extra_vote); assert_eq!( table.detected_misbehavior[&AuthorityId(1)][0], Misbehavior::ValidityDoubleVote(ValidityDoubleVote::IssuedAndValidity( @@ -910,7 +905,7 @@ mod tests { }; let candidate_digest = Digest(100); - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); assert!(table.attested_candidate(&candidate_digest, &context, 2).is_none()); @@ -921,7 +916,7 @@ mod tests { sender: AuthorityId(2), }; - table.import_statement(&context, vote); + table.import_statement(&context, GroupId(2), vote); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2))); assert!(table.attested_candidate(&candidate_digest, &context, 2).is_some()); } @@ -944,7 +939,7 @@ mod tests { }; let summary = table - .import_statement(&context, statement) + .import_statement(&context, GroupId(2), statement) .expect("candidate import to give summary"); assert_eq!(summary.candidate, Digest(100)); @@ -971,7 +966,7 @@ mod tests { }; let candidate_digest = Digest(100); - table.import_statement(&context, statement); + table.import_statement(&context, GroupId(2), statement); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1))); let vote = SignedStatement { @@ -980,8 +975,9 @@ mod tests { sender: AuthorityId(2), }; - let summary = - table.import_statement(&context, vote).expect("candidate vote to give summary"); + let summary = table + .import_statement(&context, GroupId(2), vote) + .expect("candidate vote to give summary"); assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2))); diff --git a/polkadot/statement-table/src/lib.rs b/polkadot/statement-table/src/lib.rs index d4629330ac01512e2dc2b0f441d2302f0fd38624..3740d15cc4f326962b962557e61ca14e9163de7d 100644 --- a/polkadot/statement-table/src/lib.rs +++ b/polkadot/statement-table/src/lib.rs @@ -35,8 +35,8 @@ pub use generic::{Config, Context, Table}; pub mod v2 { use crate::generic; use primitives::{ - CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement, Id, - ValidatorIndex, ValidatorSignature, + CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement, + CoreIndex, ValidatorIndex, ValidatorSignature, }; /// Statements about candidates on the network. @@ -59,7 +59,7 @@ pub mod v2 { >; /// A summary of import of a statement. - pub type Summary = generic::Summary; + pub type Summary = generic::Summary; impl<'a> From<&'a Statement> for PrimitiveStatement { fn from(s: &'a Statement) -> PrimitiveStatement { diff --git a/polkadot/utils/generate-bags/Cargo.toml b/polkadot/utils/generate-bags/Cargo.toml index 0f5ee43d86d8ebdd0996ab4a2392cbecd77a7c70..c0e9bd332df7ccca511bd199221405ce4d43d025 100644 --- a/polkadot/utils/generate-bags/Cargo.toml +++ b/polkadot/utils/generate-bags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-voter-bags" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -10,7 +10,7 @@ description = "CLI to generate voter bags for Polkadot runtimes" workspace = true [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } generate-bags = { path = "../../../substrate/utils/frame/generate-bags" } sp-io = { path = "../../../substrate/primitives/io" } diff --git a/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml b/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml index f8190e6aefa941f328a31c46b21bda7d8f089872..ddc5af97a166af253e660d0523b7ac6e357c367c 100644 --- a/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml +++ b/polkadot/utils/remote-ext-tests/bags-list/Cargo.toml @@ -18,6 +18,6 @@ sp-tracing = { path = "../../../../substrate/primitives/tracing" } frame-system = { path = "../../../../substrate/frame/system" } sp-core = { path = "../../../../substrate/primitives/core" } -clap = { version = "4.4.18", features = ["derive"] } -log = "0.4.17" +clap = { version = "4.5.1", features = ["derive"] } +log = { workspace = true, default-features = true } tokio = { version = "1.24.2", features = ["macros"] } diff --git a/polkadot/xcm/Cargo.toml b/polkadot/xcm/Cargo.toml index 1103e07adbbf7df9db10046999fbc9234a741a99..f9ccfb9833a6b276f001491ff29daa183f0f8e22 100644 --- a/polkadot/xcm/Cargo.toml +++ b/polkadot/xcm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "staging-xcm" description = "The basic XCM datastructures." -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -11,14 +11,14 @@ workspace = true [dependencies] array-bytes = "6.1" -bounded-collections = { version = "0.1.9", default-features = false, features = ["serde"] } +bounded-collections = { version = "0.2.0", default-features = false, features = ["serde"] } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } impl-trait-for-tuples = "0.2.2" -log = { version = "0.4.17", default-features = false } +log = { workspace = true } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } sp-weights = { path = "../../substrate/primitives/weights", default-features = false, features = ["serde"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive", "rc"] } +serde = { features = ["alloc", "derive", "rc"], workspace = true } schemars = { version = "0.8.13", default-features = true, optional = true } xcm-procedural = { path = "procedural" } environmental = { version = "1.1.4", default-features = false } diff --git a/polkadot/xcm/pallet-xcm-benchmarks/Cargo.toml b/polkadot/xcm/pallet-xcm-benchmarks/Cargo.toml index d9cc7e34c06c22e5a03a994466622a38449e73a7..80f2d1deedf724c28ca71508a2b21411b57096ee 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/Cargo.toml +++ b/polkadot/xcm/pallet-xcm-benchmarks/Cargo.toml @@ -3,7 +3,7 @@ name = "pallet-xcm-benchmarks" authors.workspace = true edition.workspace = true license.workspace = true -version = "1.0.0" +version = "7.0.0" description = "Benchmarks for the XCM pallet" [lints] @@ -24,7 +24,7 @@ xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", def frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false } xcm = { package = "staging-xcm", path = "..", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } -log = "0.4.17" +log = { workspace = true, default-features = true } [dev-dependencies] pallet-balances = { path = "../../../substrate/frame/balances" } diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs index e96ec48fcba46f2aaf668445ad25ecc5888a7d7a..4b77199069d341320a67f196719604cedcc35157 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs @@ -64,13 +64,16 @@ benchmarks_instance_pallet! { transfer_asset { let (sender_account, sender_location) = account_and_location::(1); let asset = T::get_asset(); - let assets: Assets = vec![ asset.clone() ].into(); + let assets: Assets = vec![asset.clone()].into(); // this xcm doesn't use holding let dest_location = T::valid_destination()?; let dest_account = T::AccountIdConverter::convert_location(&dest_location).unwrap(); >::deposit_asset(&asset, &sender_location, None).unwrap(); + // We deposit the asset twice so we have enough for ED after transferring + >::deposit_asset(&asset, &sender_location, None).unwrap(); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); assert!(T::TransactAsset::balance(&dest_account).is_zero()); let mut executor = new_executor::(sender_location); @@ -79,7 +82,7 @@ benchmarks_instance_pallet! { }: { executor.bench_process(xcm)?; } verify { - assert!(T::TransactAsset::balance(&sender_account).is_zero()); + assert!(T::TransactAsset::balance(&sender_account) < sender_account_balance_before); assert!(!T::TransactAsset::balance(&dest_account).is_zero()); } @@ -93,11 +96,12 @@ benchmarks_instance_pallet! { &dest_location, FeeReason::TransferReserveAsset ); - let sender_account_balance_before = T::TransactAsset::balance(&sender_account); let asset = T::get_asset(); >::deposit_asset(&asset, &sender_location, None).unwrap(); - assert!(T::TransactAsset::balance(&sender_account) > sender_account_balance_before); + // We deposit the asset twice so we have enough for ED after transferring + >::deposit_asset(&asset, &sender_location, None).unwrap(); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); let assets: Assets = vec![asset].into(); assert!(T::TransactAsset::balance(&dest_account).is_zero()); diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index f1d1b2fdef3ee8e81144ade1f99f35afdad53452..637446832fdca7d836f4df6b8565a46682f96213 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -26,7 +26,7 @@ use frame_support::{ use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use xcm::latest::prelude::*; -use xcm_builder::{AllowUnpaidExecutionFrom, MintLocation}; +use xcm_builder::{AllowUnpaidExecutionFrom, FrameTransactionalProcessor, MintLocation}; type Block = frame_system::mocking::MockBlock; @@ -103,8 +103,7 @@ impl xcm_executor::traits::MatchesFungible for MatchAnyFungible { } // Use balances as the asset transactor. -#[allow(deprecated)] -pub type AssetTransactor = xcm_builder::CurrencyAdapter< +pub type AssetTransactor = xcm_builder::FungibleAdapter< Balances, MatchAnyFungible, AccountIdConverter, @@ -145,6 +144,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl crate::Config for Test { @@ -191,8 +191,7 @@ impl xcm_balances_benchmark::Config for Test { type TrustedReserve = TrustedReserve; fn get_asset() -> Asset { - let amount = - >::minimum_balance() as u128; + let amount = 1_000_000_000_000; Asset { id: AssetId(Here.into()), fun: Fungible(amount) } } } diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs index 14d53c4ebf539e46e8af380764f1947af25abb5d..8c6ed4b5d0e0245a1758820bf5ab9ec9f7d095e9 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs @@ -125,11 +125,15 @@ benchmarks! { } refund_surplus { - let holding = T::worst_case_holding(0).into(); let mut executor = new_executor::(Default::default()); - executor.set_holding(holding); + let holding_assets = T::worst_case_holding(1); + // We can already buy execution since we'll load the holding register manually + let asset_for_fees = T::fee_asset().unwrap(); + let previous_xcm = Xcm(vec![BuyExecution { fees: asset_for_fees, weight_limit: Limited(Weight::from_parts(1337, 1337)) }]); + executor.set_holding(holding_assets.into()); executor.set_total_surplus(Weight::from_parts(1337, 1337)); executor.set_total_refunded(Weight::zero()); + executor.bench_process(previous_xcm).expect("Holding has been loaded, so we can buy execution here"); let instruction = Instruction::>::RefundSurplus; let xcm = Xcm(vec![instruction]); diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index 656c675b63072889e047a93414e60231f2bddb09..c84f062a8d169b2ca25704e13b08449573a97006 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -30,7 +30,7 @@ use xcm_builder::{ AssetsInHolding, TestAssetExchanger, TestAssetLocker, TestAssetTrap, TestSubscriptionService, TestUniversalAliases, }, - AliasForeignAccountId32, AllowUnpaidExecutionFrom, + AliasForeignAccountId32, AllowUnpaidExecutionFrom, FrameTransactionalProcessor, }; use xcm_executor::traits::ConvertOrigin; @@ -134,6 +134,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Aliasers; + type TransactionalProcessor = FrameTransactionalProcessor; } parameter_types! { @@ -196,6 +197,10 @@ impl generic::Config for Test { Ok((Default::default(), ticket, assets)) } + fn fee_asset() -> Result { + Ok(Asset { id: AssetId(Here.into()), fun: Fungible(1_000_000) }) + } + fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError> { let assets: Asset = (AssetId(Here.into()), 100).into(); Ok((Default::default(), account_id_junction::(1).into(), assets)) diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs index 3728baea983312898d5f928632d32403758e0ce3..b514eaa4727276588724be1e74a305d54a684629 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs @@ -73,6 +73,10 @@ pub mod pallet { /// Return an origin, ticket, and assets that can be trapped and claimed. fn claimable_asset() -> Result<(Location, Location, Assets), BenchmarkError>; + /// Asset used to pay for fees. Used to buy weight in benchmarks, for example in + /// `refund_surplus`. + fn fee_asset() -> Result; + /// Return an unlocker, owner and assets that can be locked and unlocked. fn unlockable_asset() -> Result<(Location, Location, Asset), BenchmarkError>; diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs index 6ce8d3e99e8e54114048ff35d2c2a7c38b01b9c4..63ed0ac0ca736450077a4677d29d65a81e9eaf3b 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -22,10 +22,8 @@ use codec::Encode; use frame_benchmarking::{account, BenchmarkError}; use sp_std::prelude::*; use xcm::latest::prelude::*; -use xcm_executor::{ - traits::{ConvertLocation, FeeReason}, - Config as XcmConfig, FeesMode, -}; +use xcm_builder::EnsureDelivery; +use xcm_executor::{traits::ConvertLocation, Config as XcmConfig}; pub mod fungible; pub mod generic; @@ -114,29 +112,3 @@ pub fn account_and_location(index: u32) -> (T::AccountId, Location) { (account, location) } - -/// Trait for a type which ensures all requirements for successful delivery with XCM transport -/// layers. -pub trait EnsureDelivery { - /// Prepare all requirements for successful `XcmSender: SendXcm` passing (accounts, balances, - /// channels ...). Returns: - /// - possible `FeesMode` which is expected to be set to executor - /// - possible `Assets` which are expected to be subsume to the Holding Register - fn ensure_successful_delivery( - origin_ref: &Location, - dest: &Location, - fee_reason: FeeReason, - ) -> (Option, Option); -} - -/// `()` implementation does nothing which means no special requirements for environment. -impl EnsureDelivery for () { - fn ensure_successful_delivery( - _origin_ref: &Location, - _dest: &Location, - _fee_reason: FeeReason, - ) -> (Option, Option) { - // doing nothing - (None, None) - } -} diff --git a/polkadot/xcm/pallet-xcm/Cargo.toml b/polkadot/xcm/pallet-xcm/Cargo.toml index b3e57d1d091ffdc137141d1fa55128e254b1a5bf..4840b6127f55425de91eea85099649d4dfda0881 100644 --- a/polkadot/xcm/pallet-xcm/Cargo.toml +++ b/polkadot/xcm/pallet-xcm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-xcm" -version = "1.0.0" +version = "7.0.0" description = "A pallet for handling XCM programs." authors.workspace = true edition.workspace = true @@ -10,11 +10,11 @@ license.workspace = true workspace = true [dependencies] -bounded-collections = { version = "0.1.8", default-features = false } +bounded-collections = { version = "0.2.0", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +log = { workspace = true } frame-support = { path = "../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../substrate/frame/system", default-features = false } diff --git a/polkadot/xcm/pallet-xcm/src/benchmarking.rs b/polkadot/xcm/pallet-xcm/src/benchmarking.rs index c7d8fb24e9df320f8439616591c467f3f6d7aa1e..ed42f93692b406ea07b2edbda0fbdb315c2b4071 100644 --- a/polkadot/xcm/pallet-xcm/src/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm/src/benchmarking.rs @@ -17,21 +17,26 @@ use super::*; use bounded_collections::{ConstU32, WeakBoundedVec}; use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError, BenchmarkResult}; -use frame_support::{traits::Currency, weights::Weight}; +use frame_support::{ + traits::fungible::{Inspect, Mutate}, + weights::Weight, +}; use frame_system::RawOrigin; use sp_std::prelude::*; use xcm::{latest::prelude::*, v2}; +use xcm_builder::EnsureDelivery; +use xcm_executor::traits::FeeReason; type RuntimeOrigin = ::RuntimeOrigin; -// existential deposit multiplier -const ED_MULTIPLIER: u32 = 100; - /// Pallet we're benchmarking here. pub struct Pallet(crate::Pallet); /// Trait that must be implemented by runtime to be able to benchmark pallet properly. pub trait Config: crate::Config { + /// Helper that ensures successful delivery for extrinsics/benchmarks which need `SendXcm`. + type DeliveryHelper: EnsureDelivery; + /// A `Location` that can be reached via `XcmRouter`. Used only in benchmarks. /// /// If `None`, the benchmarks that depend on a reachable destination will be skipped. @@ -74,6 +79,13 @@ pub trait Config: crate::Config { fn set_up_complex_asset_transfer() -> Option<(Assets, u32, Location, Box)> { None } + + /// Gets an asset that can be handled by the AssetTransactor. + /// + /// Used only in benchmarks. + /// + /// Used, for example, in the benchmark for `claim_assets`. + fn get_asset() -> Asset; } benchmarks! { @@ -107,23 +119,29 @@ benchmarks! { }.into(); let assets: Assets = asset.into(); - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - assert!(balance >= transferred_amount); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - // verify initial balance - assert_eq!(pallet_balances::Pallet::::free_balance(&caller), balance); - + let caller: T::AccountId = whitelisted_caller(); let send_origin = RawOrigin::Signed(caller.clone()); let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into()) .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; - if !T::XcmTeleportFilter::contains(&(origin_location, assets.clone().into_inner())) { + if !T::XcmTeleportFilter::contains(&(origin_location.clone(), assets.clone().into_inner())) { return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) } + // Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...) + let (_, _) = T::DeliveryHelper::ensure_successful_delivery( + &origin_location, + &destination, + FeeReason::ChargeFees, + ); + + // Actual balance (e.g. `ensure_successful_delivery` could drip delivery fees, ...) + let balance = as Inspect<_>>::balance(&caller); + // Add transferred_amount to origin + as Mutate<_>>::mint_into(&caller, transferred_amount)?; + // verify initial balance + let balance = balance + transferred_amount; + assert_eq!( as Inspect<_>>::balance(&caller), balance); + let recipient = [0u8; 32]; let versioned_dest: VersionedLocation = destination.into(); let versioned_beneficiary: VersionedLocation = @@ -132,7 +150,7 @@ benchmarks! { }: _>(send_origin.into(), Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0) verify { // verify balance after transfer, decreased by transferred amount (+ maybe XCM delivery fees) - assert!(pallet_balances::Pallet::::free_balance(&caller) <= balance - transferred_amount); + assert!( as Inspect<_>>::balance(&caller) <= balance - transferred_amount); } reserve_transfer_assets { @@ -146,23 +164,29 @@ benchmarks! { }.into(); let assets: Assets = asset.into(); - let existential_deposit = T::ExistentialDeposit::get(); - let caller = whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - assert!(balance >= transferred_amount); - let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - // verify initial balance - assert_eq!(pallet_balances::Pallet::::free_balance(&caller), balance); - + let caller: T::AccountId = whitelisted_caller(); let send_origin = RawOrigin::Signed(caller.clone()); let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into()) .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; - if !T::XcmReserveTransferFilter::contains(&(origin_location, assets.clone().into_inner())) { + if !T::XcmReserveTransferFilter::contains(&(origin_location.clone(), assets.clone().into_inner())) { return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))) } + // Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...) + let (_, _) = T::DeliveryHelper::ensure_successful_delivery( + &origin_location, + &destination, + FeeReason::ChargeFees, + ); + + // Actual balance (e.g. `ensure_successful_delivery` could drip delivery fees, ...) + let balance = as Inspect<_>>::balance(&caller); + // Add transferred_amount to origin + as Mutate<_>>::mint_into(&caller, transferred_amount)?; + // verify initial balance + let balance = balance + transferred_amount; + assert_eq!( as Inspect<_>>::balance(&caller), balance); + let recipient = [0u8; 32]; let versioned_dest: VersionedLocation = destination.into(); let versioned_beneficiary: VersionedLocation = @@ -171,7 +195,7 @@ benchmarks! { }: _>(send_origin.into(), Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0) verify { // verify balance after transfer, decreased by transferred amount (+ maybe XCM delivery fees) - assert!(pallet_balances::Pallet::::free_balance(&caller) <= balance - transferred_amount); + assert!( as Inspect<_>>::balance(&caller) <= balance - transferred_amount); } transfer_assets { @@ -324,11 +348,23 @@ benchmarks! { u32::MAX, ).unwrap()).collect::>(); crate::Pallet::::expect_response(query_id, Response::PalletsInfo(infos.try_into().unwrap())); - }: { as QueryHandler>::take_response(query_id); } + claim_assets { + let claim_origin = RawOrigin::Signed(whitelisted_caller()); + let claim_location = T::ExecuteXcmOrigin::try_origin(claim_origin.clone().into()).map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let asset: Asset = T::get_asset(); + // Trap assets for claiming later + crate::Pallet::::drop_assets( + &claim_location, + asset.clone().into(), + &XcmContext { origin: None, message_id: [0u8; 32], topic: None } + ); + let versioned_assets = VersionedAssets::V4(asset.into()); + }: _>(claim_origin.into(), Box::new(versioned_assets), Box::new(VersionedLocation::V4(claim_location))) + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext_with_balances(Vec::new()), diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 55154198a9b2d3ed537de187c489632b18e76610..1a1f8b402a39041ffb5852dd56e9725a550afc7c 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -29,7 +29,7 @@ pub mod migration; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use frame_support::{ - dispatch::GetDispatchInfo, + dispatch::{DispatchErrorWithPostInfo, GetDispatchInfo, WithPostDispatchInfo}, pallet_prelude::*, traits::{ Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Get, LockableCurrency, @@ -62,6 +62,9 @@ use xcm_executor::{ AssetsInHolding, }; +#[cfg(any(feature = "try-runtime", test))] +use sp_runtime::TryRuntimeError; + pub trait WeightInfo { fn send() -> Weight; fn teleport_assets() -> Weight; @@ -82,6 +85,7 @@ pub trait WeightInfo { fn migrate_and_notify_old_targets() -> Weight; fn new_query() -> Weight; fn take_response() -> Weight; + fn claim_assets() -> Weight; } /// fallback implementation @@ -162,6 +166,10 @@ impl WeightInfo for TestWeightInfo { fn take_response() -> Weight { Weight::from_parts(100_000_000, 0) } + + fn claim_assets() -> Weight { + Weight::from_parts(100_000_000, 0) + } } #[frame_support::pallet] @@ -288,22 +296,38 @@ pub mod pallet { origin: OriginFor, message: Box::RuntimeCall>>, max_weight: Weight, - ) -> Result { - let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; - let mut hash = message.using_encoded(sp_io::hashing::blake2_256); - let message = (*message).try_into().map_err(|()| Error::::BadVersion)?; - let value = (origin_location, message); - ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); - let (origin_location, message) = value; - let outcome = T::XcmExecutor::prepare_and_execute( - origin_location, - message, - &mut hash, - max_weight, - max_weight, - ); + ) -> Result { + log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight); + let outcome = (|| { + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + let mut hash = message.using_encoded(sp_io::hashing::blake2_256); + let message = (*message).try_into().map_err(|()| Error::::BadVersion)?; + let value = (origin_location, message); + ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); + let (origin_location, message) = value; + Ok(T::XcmExecutor::prepare_and_execute( + origin_location, + message, + &mut hash, + max_weight, + max_weight, + )) + })() + .map_err(|e: DispatchError| { + e.with_weight(::execute()) + })?; + Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); - Ok(outcome) + let weight_used = outcome.weight_used(); + outcome.ensure_complete().map_err(|error| { + log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error); + Error::::LocalExecutionIncomplete.with_weight( + weight_used.saturating_add( + ::execute(), + ), + ) + })?; + Ok(weight_used) } } @@ -464,6 +488,8 @@ pub mod pallet { FeesPaid { paying: Location, fees: Assets }, /// Some assets have been claimed from an asset trap AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets }, + /// A XCM version migration finished. + VersionMigrationFinished { version: XcmVersion }, } #[pallet::origin] @@ -765,6 +791,9 @@ pub mod pallet { // Consume 10% of block at most let max_weight = T::BlockWeights::get().max_block / 10; let (w, maybe_migration) = Self::check_xcm_version_change(migration, max_weight); + if maybe_migration.is_none() { + Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION }); + } CurrentMigration::::set(maybe_migration); weight_used.saturating_accrue(w); } @@ -791,6 +820,11 @@ pub mod pallet { } weight_used } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), TryRuntimeError> { + Self::do_try_state() + } } pub mod migrations { @@ -996,9 +1030,6 @@ pub mod pallet { /// No more than `max_weight` will be used in its attempted execution. If this is less than /// the maximum amount of weight that the message could take to be executed, then no /// execution attempt will be made. - /// - /// NOTE: A successful return to this does *not* imply that the `msg` was executed - /// successfully to completion; only that it was attempted. #[pallet::call_index(3)] #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))] pub fn execute( @@ -1006,13 +1037,8 @@ pub mod pallet { message: Box::RuntimeCall>>, max_weight: Weight, ) -> DispatchResultWithPostInfo { - log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight); - let outcome = >::execute(origin, message, max_weight)?; - let weight_used = outcome.weight_used(); - outcome.ensure_complete().map_err(|error| { - log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error); - Error::::LocalExecutionIncomplete - })?; + let weight_used = + >::execute(origin, message, max_weight)?; Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into()) } @@ -1365,6 +1391,64 @@ pub mod pallet { weight_limit, ) } + + /// Claims assets trapped on this pallet because of leftover assets during XCM execution. + /// + /// - `origin`: Anyone can call this extrinsic. + /// - `assets`: The exact assets that were trapped. Use the version to specify what version + /// was the latest when they were trapped. + /// - `beneficiary`: The location/account where the claimed assets will be deposited. + #[pallet::call_index(12)] + #[pallet::weight({ + let assets_version = assets.identify_version(); + let maybe_assets: Result = (*assets.clone()).try_into(); + let maybe_beneficiary: Result = (*beneficiary.clone()).try_into(); + match (maybe_assets, maybe_beneficiary) { + (Ok(assets), Ok(beneficiary)) => { + let ticket: Location = GeneralIndex(assets_version as u128).into(); + let mut message = Xcm(vec![ + ClaimAsset { assets: assets.clone(), ticket }, + DepositAsset { assets: AllCounted(assets.len() as u32).into(), beneficiary }, + ]); + T::Weigher::weight(&mut message).map_or(Weight::MAX, |w| T::WeightInfo::claim_assets().saturating_add(w)) + } + _ => Weight::MAX + } + })] + pub fn claim_assets( + origin: OriginFor, + assets: Box, + beneficiary: Box, + ) -> DispatchResult { + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + log::debug!(target: "xcm::pallet_xcm::claim_assets", "origin: {:?}, assets: {:?}, beneficiary: {:?}", origin_location, assets, beneficiary); + // Extract version from `assets`. + let assets_version = assets.identify_version(); + let assets: Assets = (*assets).try_into().map_err(|()| Error::::BadVersion)?; + let number_of_assets = assets.len() as u32; + let beneficiary: Location = + (*beneficiary).try_into().map_err(|()| Error::::BadVersion)?; + let ticket: Location = GeneralIndex(assets_version as u128).into(); + let mut message = Xcm(vec![ + ClaimAsset { assets, ticket }, + DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary }, + ]); + let weight = + T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; + let mut hash = message.using_encoded(sp_io::hashing::blake2_256); + let outcome = T::XcmExecutor::prepare_and_execute( + origin_location, + message, + &mut hash, + weight, + weight, + ); + outcome.ensure_complete().map_err(|error| { + log::error!(target: "xcm::pallet_xcm::claim_assets", "XCM execution failed with error: {:?}", error); + Error::::LocalExecutionIncomplete + })?; + Ok(()) + } } } @@ -2387,6 +2471,48 @@ impl Pallet { Self::deposit_event(Event::FeesPaid { paying: location, fees: assets }); Ok(()) } + + /// Ensure the correctness of the state of this pallet. + /// + /// This should be valid before and after each state transition of this pallet. + /// + /// ## Invariants + /// + /// All entries stored in the `SupportedVersion` / `VersionNotifiers` / `VersionNotifyTargets` + /// need to be migrated to the `XCM_VERSION`. If they are not, then `CurrentMigration` has to be + /// set. + #[cfg(any(feature = "try-runtime", test))] + pub fn do_try_state() -> Result<(), TryRuntimeError> { + // if migration has been already scheduled, everything is ok and data will be eventually + // migrated + if CurrentMigration::::exists() { + return Ok(()) + } + + // if migration has NOT been scheduled yet, we need to check all operational data + for v in 0..XCM_VERSION { + ensure!( + SupportedVersion::::iter_prefix(v).next().is_none(), + TryRuntimeError::Other( + "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`" + ) + ); + ensure!( + VersionNotifiers::::iter_prefix(v).next().is_none(), + TryRuntimeError::Other( + "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`" + ) + ); + ensure!( + VersionNotifyTargets::::iter_prefix(v).next().is_none(), + TryRuntimeError::Other( + "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`" + ) + ); + } + + Ok(()) + } } pub struct LockTicket { diff --git a/polkadot/xcm/pallet-xcm/src/migration.rs b/polkadot/xcm/pallet-xcm/src/migration.rs index 2793afcc910484948f7d3a611c88b2ca59beb766..018436aa3c93a9291a99820a57118d119f5a0828 100644 --- a/polkadot/xcm/pallet-xcm/src/migration.rs +++ b/polkadot/xcm/pallet-xcm/src/migration.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::{Config, Pallet, VersionNotifyTargets}; +use crate::{ + pallet::CurrentMigration, Config, Pallet, VersionMigrationStage, VersionNotifyTargets, +}; use frame_support::{ pallet_prelude::*, traits::{OnRuntimeUpgrade, StorageVersion}, @@ -73,3 +75,16 @@ pub mod v1 { ::DbWeight, >; } + +/// When adding a new XCM version, we need to run this migration for `pallet_xcm` to ensure that all +/// previously stored data with subkey prefix `XCM_VERSION-1` (and below) are migrated to the +/// `XCM_VERSION`. +/// +/// NOTE: This migration can be permanently added to the runtime migrations. +pub struct MigrateToLatestXcmVersion(sp_std::marker::PhantomData); +impl OnRuntimeUpgrade for MigrateToLatestXcmVersion { + fn on_runtime_upgrade() -> Weight { + CurrentMigration::::put(VersionMigrationStage::default()); + T::DbWeight::get().writes(1) + } +} diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index 1ca30963d595696490773274fa48291413d1dc29..d5ba7312a3a55dcddbc24c64e613985fca0c7bb9 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -30,15 +30,13 @@ use sp_core::H256; use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage}; pub use sp_std::cell::RefCell; use xcm::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, DescribeAllTerminal, FixedRateOfFungible, FixedWeightBounds, - FungiblesAdapter, HashedDescription, IsConcrete, MatchedConvertedConcreteId, NoChecking, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - XcmFeeManagerFromComponents, XcmFeeToAccount, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, + MatchedConvertedConcreteId, NoChecking, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{ traits::{Identity, JustTry}, @@ -174,7 +172,7 @@ impl SendXcm for TestSendXcm { msg: &mut Option>, ) -> SendResult<(Location, Xcm<()>)> { if FAIL_SEND_XCM.with(|q| *q.borrow()) { - return Err(SendError::Transport("Intentional send failure used in tests")) + return Err(SendError::Transport("Intentional send failure used in tests")); } let pair = (dest.take().unwrap(), msg.take().unwrap()); Ok((pair, Assets::new())) @@ -289,7 +287,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -425,9 +422,8 @@ pub type ForeignAssetsConvertedConcreteId = MatchedConvertedConcreteId< JustTry, >; -#[allow(deprecated)] pub type AssetTransactors = ( - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>, + FungibleAdapter, SovereignAccountOf, AccountId, ()>, FungiblesAdapter< AssetsPallet, ForeignAssetsConvertedConcreteId, @@ -516,6 +512,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type LocalOriginToLocation = SignedToAccountId32; @@ -566,8 +563,30 @@ impl pallet_test_notifier::Config for Test { type RuntimeCall = RuntimeCall; } +#[cfg(feature = "runtime-benchmarks")] +pub struct TestDeliveryHelper; +#[cfg(feature = "runtime-benchmarks")] +impl xcm_builder::EnsureDelivery for TestDeliveryHelper { + fn ensure_successful_delivery( + origin_ref: &Location, + _dest: &Location, + _fee_reason: xcm_executor::traits::FeeReason, + ) -> (Option, Option) { + use xcm_executor::traits::ConvertLocation; + let account = SovereignAccountOf::convert_location(origin_ref).expect("Valid location"); + // Give the existential deposit at least + let balance = ExistentialDeposit::get(); + let _ = >::make_free_balance_be( + &account, balance, + ); + (None, None) + } +} + #[cfg(feature = "runtime-benchmarks")] impl super::benchmarking::Config for Test { + type DeliveryHelper = TestDeliveryHelper; + fn reachable_dest() -> Option { Some(Parachain(1000).into()) } @@ -635,6 +654,10 @@ impl super::benchmarking::Config for Test { }); Some((assets, fee_index as u32, dest, verify)) } + + fn get_asset() -> Asset { + Asset { id: AssetId(Location::here()), fun: Fungible(ExistentialDeposit::get()) } + } } pub(crate) fn last_event() -> RuntimeEvent { diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 5f9c86ed7b3f0bd99dc9064e1c53ef9872a799ce..13022d9a8b1f69f528393ee8ac16e3d62151bae9 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -19,11 +19,13 @@ pub(crate) mod assets_transfer; use crate::{ - mock::*, AssetTraps, CurrentMigration, Error, LatestVersionedLocation, Queries, QueryStatus, + mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, + ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, + WeightInfo, }; use frame_support::{ - assert_noop, assert_ok, + assert_err_ignore_postinfo, assert_noop, assert_ok, traits::{Currency, Hooks}, weights::Weight, }; @@ -449,19 +451,70 @@ fn trapped_assets_can_be_claimed() { assert_eq!(Balances::total_balance(&BOB), INITIAL_BALANCE + SEND_AMOUNT); assert_eq!(AssetTraps::::iter().collect::>(), vec![]); - let weight = BaseXcmWeight::get() * 3; - assert_ok!(>::execute( + // Can't claim twice. + assert_err_ignore_postinfo!( + XcmPallet::execute( + RuntimeOrigin::signed(ALICE), + Box::new(VersionedXcm::from(Xcm(vec![ + ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() }, + buy_execution((Here, SEND_AMOUNT)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: dest }, + ]))), + weight + ), + Error::::LocalExecutionIncomplete + ); + }); +} + +// Like `trapped_assets_can_be_claimed` but using the `claim_assets` extrinsic. +#[test] +fn claim_assets_works() { + let balances = vec![(ALICE, INITIAL_BALANCE)]; + new_test_ext_with_balances(balances).execute_with(|| { + // First trap some assets. + let trapping_program = + Xcm::builder_unsafe().withdraw_asset((Here, SEND_AMOUNT).into()).build(); + // Even though assets are trapped, the extrinsic returns success. + assert_ok!(XcmPallet::execute( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::from(Xcm(vec![ - ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() }, - buy_execution((Here, SEND_AMOUNT)), - DepositAsset { assets: AllCounted(1).into(), beneficiary: dest }, - ]))), - weight + Box::new(VersionedXcm::V4(trapping_program)), + BaseXcmWeight::get() * 2, + )); + assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); + + // Expected `AssetsTrapped` event info. + let source: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); + let versioned_assets = VersionedAssets::V4(Assets::from((Here, SEND_AMOUNT))); + let hash = BlakeTwo256::hash_of(&(source.clone(), versioned_assets.clone())); + + // Assets were indeed trapped. + assert_eq!( + last_events(2), + vec![ + RuntimeEvent::XcmPallet(crate::Event::AssetsTrapped { + hash, + origin: source, + assets: versioned_assets + }), + RuntimeEvent::XcmPallet(crate::Event::Attempted { + outcome: Outcome::Complete { used: BaseXcmWeight::get() * 1 } + }) + ], + ); + let trapped = AssetTraps::::iter().collect::>(); + assert_eq!(trapped, vec![(hash, 1)]); + + // Now claim them with the extrinsic. + assert_ok!(XcmPallet::claim_assets( + RuntimeOrigin::signed(ALICE), + Box::new(VersionedAssets::V4((Here, SEND_AMOUNT).into())), + Box::new(VersionedLocation::V4( + AccountId32 { network: None, id: ALICE.clone().into() }.into() + )), )); - let outcome = - Outcome::Incomplete { used: BaseXcmWeight::get(), error: XcmError::UnknownClaim }; - assert_eq!(last_event(), RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome })); + assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); + assert_eq!(AssetTraps::::iter().collect::>(), vec![]); }); } @@ -494,11 +547,14 @@ fn incomplete_execute_reverts_side_effects() { // all effects are reverted and balances unchanged for either sender or receiver assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_eq!(Balances::total_balance(&BOB), INITIAL_BALANCE); + assert_eq!( result, Err(sp_runtime::DispatchErrorWithPostInfo { post_info: frame_support::dispatch::PostDispatchInfo { - actual_weight: None, + actual_weight: Some( + as ExecuteControllerWeightInfo>::execute() + weight + ), pays_fee: frame_support::dispatch::Pays::Yes, }, error: sp_runtime::DispatchError::Module(sp_runtime::ModuleError { @@ -1113,3 +1169,83 @@ fn get_and_wrap_version_works() { assert_eq!(VersionDiscoveryQueue::::get().into_inner(), vec![(remote_b.into(), 2)]); }) } + +#[test] +fn multistage_migration_works() { + new_test_ext_with_balances(vec![]).execute_with(|| { + // An entry from a previous runtime with v3 XCM. + let v3_location = VersionedLocation::V3(xcm::v3::Junction::Parachain(1001).into()); + let v3_version = xcm::v3::VERSION; + SupportedVersion::::insert(v3_version, v3_location.clone(), v3_version); + VersionNotifiers::::insert(v3_version, v3_location.clone(), 1); + VersionNotifyTargets::::insert( + v3_version, + v3_location, + (70, Weight::zero(), v3_version), + ); + // A version to advertise. + AdvertisedXcmVersion::set(4); + + // check `try-state` + assert!(Pallet::::do_try_state().is_err()); + + // closure simulates a multistage migration process + let migrate = |expected_cycle_count| { + // A runtime upgrade which alters the version does send notifications. + CurrentMigration::::put(VersionMigrationStage::default()); + let mut maybe_migration = CurrentMigration::::take(); + let mut counter = 0; + let mut weight_used = Weight::zero(); + while let Some(migration) = maybe_migration.take() { + counter += 1; + let (w, m) = XcmPallet::check_xcm_version_change(migration, Weight::zero()); + maybe_migration = m; + weight_used.saturating_accrue(w); + } + assert_eq!(counter, expected_cycle_count); + weight_used + }; + + // run migration for the first time + let _ = migrate(4); + + // check xcm sent + assert_eq!( + take_sent_xcm(), + vec![( + Parachain(1001).into(), + Xcm(vec![QueryResponse { + query_id: 70, + max_weight: Weight::zero(), + response: Response::Version(AdvertisedXcmVersion::get()), + querier: None, + }]) + ),] + ); + + // check migrated data + assert_eq!( + SupportedVersion::::iter().collect::>(), + vec![(XCM_VERSION, Parachain(1001).into_versioned(), v3_version),] + ); + assert_eq!( + VersionNotifiers::::iter().collect::>(), + vec![(XCM_VERSION, Parachain(1001).into_versioned(), 1),] + ); + assert_eq!( + VersionNotifyTargets::::iter().collect::>(), + vec![(XCM_VERSION, Parachain(1001).into_versioned(), (70, Weight::zero(), 4)),] + ); + + // run migration again to check it can run multiple time without any harm or double sending + // messages. + let weight_used = migrate(1); + assert_eq!(weight_used, 1_u8 * ::WeightInfo::already_notified_target()); + + // check no xcm sent + assert_eq!(take_sent_xcm(), vec![]); + + // check `try-state` + assert!(Pallet::::do_try_state().is_ok()); + }) +} diff --git a/polkadot/xcm/procedural/Cargo.toml b/polkadot/xcm/procedural/Cargo.toml index e88f4b0c846c10b0378f66de1a25f01ee8d17abb..ca9fb351bd3cad1f805106e405cfdcc496c9d8a8 100644 --- a/polkadot/xcm/procedural/Cargo.toml +++ b/polkadot/xcm/procedural/Cargo.toml @@ -4,7 +4,7 @@ description = "Procedural macros for XCM" authors.workspace = true edition.workspace = true license.workspace = true -version = "1.0.0" +version = "7.0.0" publish = true [lints] @@ -15,8 +15,8 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.56" -quote = "1.0.28" -syn = "2.0.48" +quote = { workspace = true } +syn = { workspace = true } Inflector = "0.11.4" [dev-dependencies] diff --git a/polkadot/xcm/procedural/src/v2.rs b/polkadot/xcm/procedural/src/v2.rs index 1a2f281a4982938c58437dd842277bec9fb8ab89..6878f7755cc70b9b540074a63480615338e456aa 100644 --- a/polkadot/xcm/procedural/src/v2.rs +++ b/polkadot/xcm/procedural/src/v2.rs @@ -189,7 +189,7 @@ pub mod junctions { pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { if !input.is_empty() { - return Err(syn::Error::new(Span::call_site(), "No arguments expected")); + return Err(syn::Error::new(Span::call_site(), "No arguments expected")) } let from_slice_syntax = generate_conversion_from_slice_syntax(); diff --git a/polkadot/xcm/src/lib.rs b/polkadot/xcm/src/lib.rs index f0cbe985331e977d743abfbea51c6312baf105fa..ba8d726aecff4989417373fc9e14c174d5a5277f 100644 --- a/polkadot/xcm/src/lib.rs +++ b/polkadot/xcm/src/lib.rs @@ -165,6 +165,15 @@ macro_rules! versioned_type { <$v3>::max_encoded_len() } } + impl IdentifyVersion for $n { + fn identify_version(&self) -> Version { + use $n::*; + match self { + V3(_) => v3::VERSION, + V4(_) => v4::VERSION, + } + } + } }; ($(#[$attr:meta])* pub enum $n:ident { @@ -287,6 +296,16 @@ macro_rules! versioned_type { <$v3>::max_encoded_len() } } + impl IdentifyVersion for $n { + fn identify_version(&self) -> Version { + use $n::*; + match self { + V2(_) => v2::VERSION, + V3(_) => v3::VERSION, + V4(_) => v4::VERSION, + } + } + } }; } @@ -493,8 +512,14 @@ pub trait WrapVersion { ) -> Result, ()>; } +/// Used to get the version out of a versioned type. +// TODO(XCMv5): This could be `GetVersion` and we change the current one to `GetVersionFor`. +pub trait IdentifyVersion { + fn identify_version(&self) -> Version; +} + /// Check and return the `Version` that should be used for the `Xcm` datum for the destination -/// `MultiLocation`, which will interpret it. +/// `Location`, which will interpret it. pub trait GetVersion { fn get_version_for(dest: &latest::Location) -> Option; } @@ -572,9 +597,9 @@ pub type AlwaysLts = AlwaysV4; pub mod prelude { pub use super::{ latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, AlwaysV4, GetVersion, - IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset, VersionedAssetId, - VersionedAssets, VersionedInteriorLocation, VersionedLocation, VersionedResponse, - VersionedXcm, WrapVersion, + IdentifyVersion, IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset, + VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation, + VersionedResponse, VersionedXcm, WrapVersion, }; } @@ -658,7 +683,7 @@ fn size_limits() { (crate::latest::Junctions, 16), (crate::latest::Junction, 88), (crate::latest::Response, 40), - (crate::latest::AssetInstance, 40), + (crate::latest::AssetInstance, 48), (crate::latest::NetworkId, 48), (crate::latest::BodyId, 32), (crate::latest::Assets, 24), diff --git a/polkadot/xcm/src/v2/mod.rs b/polkadot/xcm/src/v2/mod.rs index 188b7f0b5c9384b7395ed08b27c4c8378719be8f..347f3f2c29206222ca546b7d543409da83cbedcf 100644 --- a/polkadot/xcm/src/v2/mod.rs +++ b/polkadot/xcm/src/v2/mod.rs @@ -1134,7 +1134,10 @@ impl TryFrom> for Instruction Self::UnsubscribeVersion, - _ => return Err(()), + i => { + log::debug!(target: "xcm::v3tov2", "`{i:?}` not supported by v2"); + return Err(()); + }, }) } } diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs index 1172cbf43e6f0b46877bdff7ff8507443989f479..d4e2da07a25ac9dbf4ca7177ed09f0abd26b46d3 100644 --- a/polkadot/xcm/src/v3/mod.rs +++ b/polkadot/xcm/src/v3/mod.rs @@ -69,7 +69,6 @@ pub const VERSION: super::Version = 3; /// An identifier for a query. pub type QueryId = u64; -// TODO (v4): Use `BoundedVec` instead of `Vec` #[derive(Derivative, Default, Encode, TypeInfo)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] diff --git a/polkadot/xcm/src/v3/multiasset.rs b/polkadot/xcm/src/v3/multiasset.rs index 557ffa568a4c116d29279df2a0cb682f0b816d43..f9041ecd81bac1b383b65ea8920920ff95e14419 100644 --- a/polkadot/xcm/src/v3/multiasset.rs +++ b/polkadot/xcm/src/v3/multiasset.rs @@ -51,9 +51,20 @@ use scale_info::TypeInfo; /// A general identifier for an instance of a non-fungible asset class. #[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub enum AssetInstance { @@ -264,8 +275,19 @@ impl TryFrom for u128 { /// Classification of whether an asset is fungible or not, along with a mandatory amount or /// instance. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub enum Fungibility { @@ -345,9 +367,20 @@ impl TryFrom for Fungibility { /// Classification of whether an asset is fungible or not. #[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub enum WildFungibility { @@ -381,9 +414,20 @@ impl TryFrom for WildFungibility { /// Classification of an asset being concrete or abstract. #[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub enum AssetId { @@ -465,8 +509,18 @@ impl AssetId { } /// Either an amount of a single fungible asset, or a single well-identified non-fungible asset. -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub struct MultiAsset { @@ -567,12 +621,23 @@ impl TryFrom for MultiAsset { /// A `Vec` of `MultiAsset`s. /// /// There are a number of invariants which the construction and mutation functions must ensure are -/// maintained: +/// maintained in order to maintain polynomial time complexity during iteration: /// - It may contain no items of duplicate asset class; /// - All items must be ordered; /// - The number of items should grow no larger than `MAX_ITEMS_IN_MULTIASSETS`. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, Default)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + TypeInfo, + Default, + serde::Serialize, + serde::Deserialize, +)] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub struct MultiAssets(Vec); @@ -784,8 +849,20 @@ impl MultiAssets { } /// A wildcard representing a set of assets. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub enum WildMultiAsset { @@ -912,8 +989,20 @@ impl, B: Into> From<(A, B)> for WildMultiAsset } /// `MultiAsset` collection, defined either by a number of `MultiAssets` or a single wildcard. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub enum MultiAssetFilter { diff --git a/polkadot/xcm/src/v3/traits.rs b/polkadot/xcm/src/v3/traits.rs index 9e9b983fdf854730ee23dda2872fb6e1698509cd..cfe387df1a86c3aa21da69ce37eadb307df1c51b 100644 --- a/polkadot/xcm/src/v3/traits.rs +++ b/polkadot/xcm/src/v3/traits.rs @@ -470,7 +470,7 @@ pub trait SendXcm { /// Intermediate value which connects the two phases of the send operation. type Ticket; - /// Check whether the given `_message` is deliverable to the given `_destination` and if so + /// Check whether the given `message` is deliverable to the given `destination` and if so /// determine the cost which will be paid by this chain to do so, returning a `Validated` token /// which can be used to enact delivery. /// diff --git a/polkadot/xcm/src/v4/asset.rs b/polkadot/xcm/src/v4/asset.rs index 8aa1cc61437c56db7c7cf4e2bc4a93a2087075aa..bdff0c272306aced0fbf5f2372a70d66ac37e841 100644 --- a/polkadot/xcm/src/v4/asset.rs +++ b/polkadot/xcm/src/v4/asset.rs @@ -33,6 +33,7 @@ use crate::v3::{ WildFungibility as OldWildFungibility, WildMultiAsset as OldWildAsset, }; use alloc::{vec, vec::Vec}; +use bounded_collections::{BoundedVec, ConstU32}; use core::{ cmp::Ordering, convert::{TryFrom, TryInto}, @@ -42,9 +43,20 @@ use scale_info::TypeInfo; /// A general identifier for an instance of a non-fungible asset class. #[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum AssetInstance { /// Undefined - used if the non-fungible asset class has only one instance. Undefined, @@ -237,8 +249,19 @@ impl TryFrom for u128 { /// Classification of whether an asset is fungible or not, along with a mandatory amount or /// instance. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] pub enum Fungibility { /// A fungible asset; we record a number of units, as a `u128` in the inner item. Fungible(#[codec(compact)] u128), @@ -305,9 +328,20 @@ impl TryFrom for Fungibility { /// Classification of whether an asset is fungible or not. #[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, )] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum WildFungibility { /// The asset is fungible. Fungible, @@ -327,8 +361,20 @@ impl TryFrom for WildFungibility { } /// Location to identify an asset. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] pub struct AssetId(pub Location); impl> From for AssetId { @@ -387,8 +433,18 @@ impl Reanchorable for AssetId { } /// Either an amount of a single fungible asset, or a single well-identified non-fungible asset. -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] pub struct Asset { /// The overall asset identity (aka *class*, in the case of a non-fungible). pub id: AssetId, @@ -480,8 +536,19 @@ impl TryFrom for Asset { /// - It may contain no items of duplicate asset class; /// - All items must be ordered; /// - The number of items should grow no larger than `MAX_ITEMS_IN_ASSETS`. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, Default)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + TypeInfo, + Default, + serde::Serialize, + serde::Deserialize, +)] pub struct Assets(Vec); /// Maximum number of items we expect in a single `Assets` value. Note this is not (yet) @@ -496,7 +563,9 @@ impl MaxEncodedLen for Assets { impl Decode for Assets { fn decode(input: &mut I) -> Result { - Self::from_sorted_and_deduplicated(Vec::::decode(input)?) + let bounded_instructions = + BoundedVec::>::decode(input)?; + Self::from_sorted_and_deduplicated(bounded_instructions.into_inner()) .map_err(|()| "Out of order".into()) } } @@ -681,8 +750,20 @@ impl Reanchorable for Assets { } /// A wildcard representing a set of assets. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] pub enum WildAsset { /// All assets in Holding. All, @@ -779,8 +860,20 @@ impl, B: Into> From<(A, B)> for WildAsset { } /// `Asset` collection, defined either by a number of `Assets` or a single wildcard. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] pub enum AssetFilter { /// Specify the filter as being everything contained by the given `Assets` inner. Definite(Assets), @@ -912,4 +1005,64 @@ mod tests { let r = Assets::from_sorted_and_deduplicated(mixed_bad); assert!(r.is_err()); } + + #[test] + fn reanchor_preserves_sorting() { + use super::*; + use alloc::vec; + + let reanchor_context: Junctions = Parachain(2000).into(); + let dest = Location::new(1, []); + + let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into(); + let mut asset_1_reanchored = asset_1.clone(); + assert!(asset_1_reanchored.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!( + asset_1_reanchored, + (Location::new(0, [Parachain(2000), PalletInstance(50), GeneralIndex(1)]), 10).into() + ); + + let asset_2: Asset = (Location::new(1, []), 10).into(); + let mut asset_2_reanchored = asset_2.clone(); + assert!(asset_2_reanchored.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!(asset_2_reanchored, (Location::new(0, []), 10).into()); + + let asset_3: Asset = (Location::new(1, [Parachain(1000)]), 10).into(); + let mut asset_3_reanchored = asset_3.clone(); + assert!(asset_3_reanchored.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!(asset_3_reanchored, (Location::new(0, [Parachain(1000)]), 10).into()); + + let mut assets: Assets = vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into(); + assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into()); + + assert!(assets.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!(assets, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored].into()); + } + + #[test] + fn decoding_respects_limit() { + use super::*; + + // Having lots of one asset will work since they are deduplicated + let lots_of_one_asset: Assets = + vec![(GeneralIndex(1), 1u128).into(); MAX_ITEMS_IN_ASSETS + 1].into(); + let encoded = lots_of_one_asset.encode(); + assert!(Assets::decode(&mut &encoded[..]).is_ok()); + + // Fewer assets than the limit works + let mut few_assets: Assets = Vec::new().into(); + for i in 0..MAX_ITEMS_IN_ASSETS { + few_assets.push((GeneralIndex(i as u128), 1u128).into()); + } + let encoded = few_assets.encode(); + assert!(Assets::decode(&mut &encoded[..]).is_ok()); + + // Having lots of different assets will not work + let mut too_many_different_assets: Assets = Vec::new().into(); + for i in 0..MAX_ITEMS_IN_ASSETS + 1 { + too_many_different_assets.push((GeneralIndex(i as u128), 1u128).into()); + } + let encoded = too_many_different_assets.encode(); + assert!(Assets::decode(&mut &encoded[..]).is_err()); + } } diff --git a/polkadot/xcm/src/v4/mod.rs b/polkadot/xcm/src/v4/mod.rs index 3b57ba1b1371917fa5fd074fd651abddaaa2dea5..30ee485589a28191e61fc351eda0f91d2773d589 100644 --- a/polkadot/xcm/src/v4/mod.rs +++ b/polkadot/xcm/src/v4/mod.rs @@ -30,7 +30,10 @@ use core::{ result, }; use derivative::Derivative; -use parity_scale_codec::{self, Decode, Encode, MaxEncodedLen}; +use parity_scale_codec::{ + self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput, + MaxEncodedLen, +}; use scale_info::TypeInfo; mod asset; @@ -59,7 +62,7 @@ pub const VERSION: super::Version = 4; /// An identifier for a query. pub type QueryId = u64; -#[derive(Derivative, Default, Encode, Decode, TypeInfo)] +#[derive(Derivative, Default, Encode, TypeInfo)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] @@ -68,6 +71,26 @@ pub struct Xcm(pub Vec>); pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100; +environmental::environmental!(instructions_count: u8); + +impl Decode for Xcm { + fn decode(input: &mut I) -> core::result::Result { + instructions_count::using_once(&mut 0, || { + let number_of_instructions: u32 = >::decode(input)?.into(); + instructions_count::with(|count| { + *count = count.saturating_add(number_of_instructions as u8); + if *count > MAX_INSTRUCTIONS_TO_DECODE { + return Err(CodecError::from("Max instructions exceeded")) + } + Ok(()) + }) + .expect("Called in `using` context and thus can not return `None`; qed")?; + let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?; + Ok(Self(decoded_instructions)) + }) + } +} + impl Xcm { /// Create an empty instance. pub fn new() -> Self { @@ -1454,4 +1477,33 @@ mod tests { let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); assert_eq!(new_xcm, xcm); } + + #[test] + fn decoding_respects_limit() { + let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]); + let encoded = max_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok()); + + let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]); + let encoded = big_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + + let nested_xcm = Xcm::<()>(vec![ + DepositReserveAsset { + assets: All.into(), + dest: Here.into(), + xcm: max_xcm, + }; + (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize + ]); + let encoded = nested_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + + let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]); + let encoded = even_more_nested_xcm.encode(); + assert_eq!(encoded.len(), 342530); + // This should not decode since the limit is 100 + assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition"); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + } } diff --git a/polkadot/xcm/xcm-builder/Cargo.toml b/polkadot/xcm/xcm-builder/Cargo.toml index ff528d7d07522dc4a64120029609277c87bd1364..660a30605e53afcabc76d43335d131297a1fa23c 100644 --- a/polkadot/xcm/xcm-builder/Cargo.toml +++ b/polkadot/xcm/xcm-builder/Cargo.toml @@ -4,7 +4,7 @@ description = "Tools & types for building with XCM and its executor." authors.workspace = true edition.workspace = true license.workspace = true -version = "1.0.0" +version = "7.0.0" [lints] workspace = true @@ -23,7 +23,7 @@ sp-weights = { path = "../../../substrate/primitives/weights", default-features frame-support = { path = "../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../substrate/frame/system", default-features = false } pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } # Polkadot dependencies polkadot-parachain-primitives = { path = "../../parachain", default-features = false } @@ -47,6 +47,7 @@ runtime-benchmarks = [ "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-salary/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", diff --git a/polkadot/xcm/xcm-builder/src/controller.rs b/polkadot/xcm/xcm-builder/src/controller.rs index 8ead18b5bd7fb4d64967123c13246ce60f46e437..ba2b1fb44b8eeb0916fdec24d97e36121bc18c8a 100644 --- a/polkadot/xcm/xcm-builder/src/controller.rs +++ b/polkadot/xcm/xcm-builder/src/controller.rs @@ -18,7 +18,10 @@ //! Controller traits defined in this module are high-level traits that will rely on other traits //! from `xcm-executor` to perform their tasks. -use frame_support::pallet_prelude::DispatchError; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, WithPostDispatchInfo}, + pallet_prelude::DispatchError, +}; use sp_std::boxed::Box; use xcm::prelude::*; pub use xcm_executor::traits::QueryHandler; @@ -52,7 +55,8 @@ pub trait ExecuteController { /// Weight information for ExecuteController functions. type WeightInfo: ExecuteControllerWeightInfo; - /// Attempt to execute an XCM locally, and return the outcome. + /// Attempt to execute an XCM locally, returns Ok with the weight consumed if the execution + /// complete successfully, Err otherwise. /// /// # Parameters /// @@ -63,7 +67,7 @@ pub trait ExecuteController { origin: Origin, message: Box>, max_weight: Weight, - ) -> Result; + ) -> Result; } /// Weight functions needed for [`SendController`]. @@ -137,8 +141,9 @@ impl ExecuteController for () { _origin: Origin, _message: Box>, _max_weight: Weight, - ) -> Result { - Ok(Outcome::Error { error: XcmError::Unimplemented }) + ) -> Result { + Err(DispatchError::Other("ExecuteController::execute not implemented") + .with_weight(Weight::zero())) } } diff --git a/polkadot/xcm/xcm-builder/src/currency_adapter.rs b/polkadot/xcm/xcm-builder/src/currency_adapter.rs index fe26b7319bb18b2d4ad33774d389afd0f834eb50..24261ac06583c4ca284f52071a4ee80afae2a6ec 100644 --- a/polkadot/xcm/xcm-builder/src/currency_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/currency_adapter.rs @@ -170,7 +170,7 @@ impl< } fn can_check_out(_dest: &Location, what: &Asset, _context: &XcmContext) -> Result { - log::trace!(target: "xcm::currency_adapter", "check_out dest: {:?}, what: {:?}", _dest, what); + log::trace!(target: "xcm::currency_adapter", "can_check_out dest: {:?}, what: {:?}", _dest, what); let amount = Matcher::matches_fungible(what).ok_or(Error::AssetNotHandled)?; match CheckedAccount::get() { Some((checked_account, MintLocation::Local)) => diff --git a/polkadot/xcm/xcm-builder/src/fungible_adapter.rs b/polkadot/xcm/xcm-builder/src/fungible_adapter.rs index 7bea8cdf957e169585c182d3a3489519ee2a14ed..21c828922b333e85e9332d16c768aef0d34cbd9f 100644 --- a/polkadot/xcm/xcm-builder/src/fungible_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/fungible_adapter.rs @@ -151,7 +151,7 @@ impl< fn can_check_out(_dest: &Location, what: &Asset, _context: &XcmContext) -> XcmResult { log::trace!( target: "xcm::fungible_adapter", - "check_out dest: {:?}, what: {:?}", + "can_check_out dest: {:?}, what: {:?}", _dest, what ); @@ -204,7 +204,7 @@ impl< ) -> result::Result { log::trace!( target: "xcm::fungible_adapter", - "deposit_asset what: {:?}, who: {:?}", + "withdraw_asset what: {:?}, who: {:?}", what, who, ); let amount = Matcher::matches_fungible(what).ok_or(MatchError::AssetNotHandled)?; diff --git a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs index 4574d5ed4c682c7bb62b6f20b6f9397dc37e4098..b4c418ebf1c9cf7d9d26540e998f966665993585 100644 --- a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs @@ -235,7 +235,7 @@ impl< fn can_check_out(_origin: &Location, what: &Asset, _context: &XcmContext) -> XcmResult { log::trace!( target: "xcm::fungibles_adapter", - "can_check_in origin: {:?}, what: {:?}", + "can_check_out origin: {:?}, what: {:?}", _origin, what ); // Check we handle this asset. diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index c00cd62e872697449267b2ca6d24cd7d8cedc8e9..e2af8187136e8eba2b42d7d4a667a69830556032 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -26,26 +26,6 @@ mod tests; #[cfg(feature = "std")] pub mod test_utils; -mod location_conversion; -#[allow(deprecated)] -pub use location_conversion::ForeignChainAliasAccount; -pub use location_conversion::{ - Account32Hash, AccountId32Aliases, AccountKey20Aliases, AliasesIntoAccountId32, - ChildParachainConvertsVia, DescribeAccountId32Terminal, DescribeAccountIdTerminal, - DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeBodyTerminal, DescribeFamily, - DescribeLocation, DescribePalletTerminal, DescribeTerminus, DescribeTreasuryVoiceTerminal, - GlobalConsensusConvertsFor, GlobalConsensusParachainConvertsFor, HashedDescription, - LocalTreasuryVoiceConvertsVia, ParentIsPreset, SiblingParachainConvertsVia, -}; - -mod origin_conversion; -pub use origin_conversion::{ - BackingToPlurality, ChildParachainAsNative, ChildSystemParachainAsSuperuser, EnsureXcmOrigin, - OriginToPluralityVoice, ParentAsSuperuser, RelayChainAsNative, SiblingParachainAsNative, - SiblingSystemParachainAsSuperuser, SignedAccountId32AsNative, SignedAccountKey20AsNative, - SignedToAccountId32, SovereignSignedViaLocation, -}; - mod asset_conversion; #[allow(deprecated)] pub use asset_conversion::ConvertedConcreteAssetId; @@ -61,8 +41,11 @@ pub use barriers::{ WithComputedOrigin, }; -mod process_xcm_message; -pub use process_xcm_message::ProcessXcmMessage; +mod controller; +pub use controller::{ + Controller, ExecuteController, ExecuteControllerWeightInfo, QueryController, + QueryControllerWeightInfo, QueryHandler, SendController, SendControllerWeightInfo, +}; mod currency_adapter; #[allow(deprecated)] @@ -73,6 +56,9 @@ pub use fee_handling::{ deposit_or_burn_fee, HandleFee, XcmFeeManagerFromComponents, XcmFeeToAccount, }; +mod filter_asset_location; +pub use filter_asset_location::{AllAssets, Case, LocationWithAssetFilters, NativeAsset}; + mod fungible_adapter; pub use fungible_adapter::{FungibleAdapter, FungibleMutateAdapter, FungibleTransferAdapter}; @@ -82,14 +68,16 @@ pub use fungibles_adapter::{ LocalMint, MintLocation, NoChecking, NonLocalMint, }; -mod nonfungibles_adapter; -pub use nonfungibles_adapter::{ - NonFungiblesAdapter, NonFungiblesMutateAdapter, NonFungiblesTransferAdapter, -}; - -mod weight; -pub use weight::{ - FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents, WeightInfoBounds, +mod location_conversion; +#[allow(deprecated)] +pub use location_conversion::ForeignChainAliasAccount; +pub use location_conversion::{ + Account32Hash, AccountId32Aliases, AccountKey20Aliases, AliasesIntoAccountId32, + ChildParachainConvertsVia, DescribeAccountId32Terminal, DescribeAccountIdTerminal, + DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeBodyTerminal, DescribeFamily, + DescribeLocation, DescribePalletTerminal, DescribeTerminus, DescribeTreasuryVoiceTerminal, + GlobalConsensusConvertsFor, GlobalConsensusParachainConvertsFor, HashedDescription, + LocalTreasuryVoiceConvertsVia, ParentIsPreset, SiblingParachainConvertsVia, }; mod matches_location; @@ -101,11 +89,38 @@ pub use matches_token::IsConcrete; mod matcher; pub use matcher::{CreateMatcher, MatchXcm, Matcher}; -mod filter_asset_location; -pub use filter_asset_location::{AllAssets, Case, LocationWithAssetFilters, NativeAsset}; +mod nonfungibles_adapter; +pub use nonfungibles_adapter::{ + NonFungiblesAdapter, NonFungiblesMutateAdapter, NonFungiblesTransferAdapter, +}; + +mod nonfungible_adapter; +pub use nonfungible_adapter::{ + NonFungibleAdapter, NonFungibleMutateAdapter, NonFungibleTransferAdapter, +}; + +mod origin_aliases; +pub use origin_aliases::AliasForeignAccountId32; + +mod origin_conversion; +pub use origin_conversion::{ + BackingToPlurality, ChildParachainAsNative, ChildSystemParachainAsSuperuser, EnsureXcmOrigin, + OriginToPluralityVoice, ParentAsSuperuser, RelayChainAsNative, SiblingParachainAsNative, + SiblingSystemParachainAsSuperuser, SignedAccountId32AsNative, SignedAccountKey20AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; + +mod pay; +pub use pay::{FixedLocation, LocatableAssetId, PayAccountId32OnChainOverXcm, PayOverXcm}; + +mod process_xcm_message; +pub use process_xcm_message::ProcessXcmMessage; mod routing; -pub use routing::{WithTopicSource, WithUniqueTopic}; +pub use routing::{EnsureDelivery, WithTopicSource, WithUniqueTopic}; + +mod transactional; +pub use transactional::FrameTransactionalProcessor; mod universal_exports; pub use universal_exports::{ @@ -114,14 +129,7 @@ pub use universal_exports::{ NetworkExportTableItem, SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter, }; -mod origin_aliases; -pub use origin_aliases::AliasForeignAccountId32; - -mod pay; -pub use pay::{FixedLocation, LocatableAssetId, PayAccountId32OnChainOverXcm, PayOverXcm}; - -mod controller; -pub use controller::{ - Controller, ExecuteController, ExecuteControllerWeightInfo, QueryController, - QueryControllerWeightInfo, QueryHandler, SendController, SendControllerWeightInfo, +mod weight; +pub use weight::{ + FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents, WeightInfoBounds, }; diff --git a/polkadot/xcm/xcm-builder/src/matches_location.rs b/polkadot/xcm/xcm-builder/src/matches_location.rs index a96df9e92de33703d087159daaa8ebecb6dbdff9..1664c24772909a8a287cf620da308b07158270bb 100644 --- a/polkadot/xcm/xcm-builder/src/matches_location.rs +++ b/polkadot/xcm/xcm-builder/src/matches_location.rs @@ -25,16 +25,9 @@ use xcm::latest::{InteriorLocation, Location, NetworkId}; pub struct StartsWith(sp_std::marker::PhantomData<(T, L)>); impl, L: TryInto + Clone> Contains for StartsWith { fn contains(location: &L) -> bool { - let latest_location: Location = if let Ok(location) = (*location).clone().try_into() { - location - } else { - return false; - }; - let latest_t = if let Ok(location) = T::get().try_into() { - location - } else { - return false; - }; + let latest_location: Location = + if let Ok(location) = (*location).clone().try_into() { location } else { return false }; + let latest_t = if let Ok(location) = T::get().try_into() { location } else { return false }; latest_location.starts_with(&latest_t) } } diff --git a/polkadot/xcm/xcm-builder/src/nonfungible_adapter.rs b/polkadot/xcm/xcm-builder/src/nonfungible_adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..b69002eafc5b9945c9991a0fd3c981dd3d922338 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/nonfungible_adapter.rs @@ -0,0 +1,326 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Adapters to work with [`frame_support::traits::tokens::nonfungible`] through XCM. + +use crate::MintLocation; +use frame_support::{ + ensure, + traits::{tokens::nonfungible, Get}, +}; +use sp_std::{marker::PhantomData, prelude::*, result}; +use xcm::latest::prelude::*; +use xcm_executor::traits::{ + ConvertLocation, Error as MatchError, MatchesNonFungible, TransactAsset, +}; + +const LOG_TARGET: &str = "xcm::nonfungible_adapter"; + +/// [`TransactAsset`] implementation that allows the use of a [`nonfungible`] implementation for +/// handling an asset in the XCM executor. +/// Only works for transfers. +pub struct NonFungibleTransferAdapter( + PhantomData<(NonFungible, Matcher, AccountIdConverter, AccountId)>, +); +impl< + NonFungible: nonfungible::Transfer, + Matcher: MatchesNonFungible, + AccountIdConverter: ConvertLocation, + AccountId: Clone, // can't get away without it since Currency is generic over it. + > TransactAsset + for NonFungibleTransferAdapter +{ + fn transfer_asset( + what: &Asset, + from: &Location, + to: &Location, + context: &XcmContext, + ) -> result::Result { + log::trace!( + target: LOG_TARGET, + "transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", + what, + from, + to, + context, + ); + // Check we handle this asset. + let instance = Matcher::matches_nonfungible(what).ok_or(MatchError::AssetNotHandled)?; + let destination = AccountIdConverter::convert_location(to) + .ok_or(MatchError::AccountIdConversionFailed)?; + NonFungible::transfer(&instance, &destination) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + Ok(what.clone().into()) + } +} + +/// [`TransactAsset`] implementation that allows the use of a [`nonfungible`] implementation for +/// handling an asset in the XCM executor. +/// Works for teleport bookkeeping. +pub struct NonFungibleMutateAdapter< + NonFungible, + Matcher, + AccountIdConverter, + AccountId, + CheckingAccount, +>(PhantomData<(NonFungible, Matcher, AccountIdConverter, AccountId, CheckingAccount)>); + +impl< + NonFungible: nonfungible::Mutate, + Matcher: MatchesNonFungible, + AccountIdConverter: ConvertLocation, + AccountId: Clone + Eq, // can't get away without it since Currency is generic over it. + CheckingAccount: Get>, + > NonFungibleMutateAdapter +{ + fn can_accrue_checked(instance: NonFungible::ItemId) -> XcmResult { + ensure!(NonFungible::owner(&instance).is_none(), XcmError::NotDepositable); + Ok(()) + } + fn can_reduce_checked(checking_account: AccountId, instance: NonFungible::ItemId) -> XcmResult { + // This is an asset whose teleports we track. + let owner = NonFungible::owner(&instance); + ensure!(owner == Some(checking_account), XcmError::NotWithdrawable); + ensure!(NonFungible::can_transfer(&instance), XcmError::NotWithdrawable); + Ok(()) + } + fn accrue_checked(checking_account: AccountId, instance: NonFungible::ItemId) { + let ok = NonFungible::mint_into(&instance, &checking_account).is_ok(); + debug_assert!(ok, "`mint_into` cannot generally fail; qed"); + } + fn reduce_checked(instance: NonFungible::ItemId) { + let ok = NonFungible::burn(&instance, None).is_ok(); + debug_assert!(ok, "`can_check_in` must have returned `true` immediately prior; qed"); + } +} + +impl< + NonFungible: nonfungible::Mutate, + Matcher: MatchesNonFungible, + AccountIdConverter: ConvertLocation, + AccountId: Clone + Eq, // can't get away without it since Currency is generic over it. + CheckingAccount: Get>, + > TransactAsset + for NonFungibleMutateAdapter +{ + fn can_check_in(_origin: &Location, what: &Asset, context: &XcmContext) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "can_check_in origin: {:?}, what: {:?}, context: {:?}", + _origin, + what, + context, + ); + // Check we handle this asset. + let instance = Matcher::matches_nonfungible(what).ok_or(MatchError::AssetNotHandled)?; + match CheckingAccount::get() { + // We track this asset's teleports to ensure no more come in than have gone out. + Some((checking_account, MintLocation::Local)) => + Self::can_reduce_checked(checking_account, instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some((_, MintLocation::NonLocal)) => Self::can_accrue_checked(instance), + _ => Ok(()), + } + } + + fn check_in(_origin: &Location, what: &Asset, context: &XcmContext) { + log::trace!( + target: LOG_TARGET, + "check_in origin: {:?}, what: {:?}, context: {:?}", + _origin, + what, + context, + ); + if let Some(instance) = Matcher::matches_nonfungible(what) { + match CheckingAccount::get() { + // We track this asset's teleports to ensure no more come in than have gone out. + Some((_, MintLocation::Local)) => Self::reduce_checked(instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some((checking_account, MintLocation::NonLocal)) => + Self::accrue_checked(checking_account, instance), + _ => (), + } + } + } + + fn can_check_out(_dest: &Location, what: &Asset, context: &XcmContext) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "can_check_out dest: {:?}, what: {:?}, context: {:?}", + _dest, + what, + context, + ); + // Check we handle this asset. + let instance = Matcher::matches_nonfungible(what).ok_or(MatchError::AssetNotHandled)?; + match CheckingAccount::get() { + // We track this asset's teleports to ensure no more come in than have gone out. + Some((_, MintLocation::Local)) => Self::can_accrue_checked(instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some((checking_account, MintLocation::NonLocal)) => + Self::can_reduce_checked(checking_account, instance), + _ => Ok(()), + } + } + + fn check_out(_dest: &Location, what: &Asset, context: &XcmContext) { + log::trace!( + target: LOG_TARGET, + "check_out dest: {:?}, what: {:?}, context: {:?}", + _dest, + what, + context, + ); + if let Some(instance) = Matcher::matches_nonfungible(what) { + match CheckingAccount::get() { + // We track this asset's teleports to ensure no more come in than have gone out. + Some((checking_account, MintLocation::Local)) => + Self::accrue_checked(checking_account, instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some((_, MintLocation::NonLocal)) => Self::reduce_checked(instance), + _ => (), + } + } + } + + fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + context, + ); + // Check we handle this asset. + let instance = Matcher::matches_nonfungible(what).ok_or(MatchError::AssetNotHandled)?; + let who = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + NonFungible::mint_into(&instance, &who) + .map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + + fn withdraw_asset( + what: &Asset, + who: &Location, + maybe_context: Option<&XcmContext>, + ) -> result::Result { + log::trace!( + target: LOG_TARGET, + "withdraw_asset what: {:?}, who: {:?}, maybe_context: {:?}", + what, + who, + maybe_context, + ); + // Check we handle this asset. + let who = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + let instance = Matcher::matches_nonfungible(what).ok_or(MatchError::AssetNotHandled)?; + NonFungible::burn(&instance, Some(&who)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + Ok(what.clone().into()) + } +} + +/// [`TransactAsset`] implementation that allows the use of a [`nonfungible`] implementation for +/// handling an asset in the XCM executor. +/// Works for everything. +pub struct NonFungibleAdapter( + PhantomData<(NonFungible, Matcher, AccountIdConverter, AccountId, CheckingAccount)>, +); +impl< + NonFungible: nonfungible::Mutate + nonfungible::Transfer, + Matcher: MatchesNonFungible, + AccountIdConverter: ConvertLocation, + AccountId: Clone + Eq, // can't get away without it since Currency is generic over it. + CheckingAccount: Get>, + > TransactAsset + for NonFungibleAdapter +{ + fn can_check_in(origin: &Location, what: &Asset, context: &XcmContext) -> XcmResult { + NonFungibleMutateAdapter::< + NonFungible, + Matcher, + AccountIdConverter, + AccountId, + CheckingAccount, + >::can_check_in(origin, what, context) + } + + fn check_in(origin: &Location, what: &Asset, context: &XcmContext) { + NonFungibleMutateAdapter::< + NonFungible, + Matcher, + AccountIdConverter, + AccountId, + CheckingAccount, + >::check_in(origin, what, context) + } + + fn can_check_out(dest: &Location, what: &Asset, context: &XcmContext) -> XcmResult { + NonFungibleMutateAdapter::< + NonFungible, + Matcher, + AccountIdConverter, + AccountId, + CheckingAccount, + >::can_check_out(dest, what, context) + } + + fn check_out(dest: &Location, what: &Asset, context: &XcmContext) { + NonFungibleMutateAdapter::< + NonFungible, + Matcher, + AccountIdConverter, + AccountId, + CheckingAccount, + >::check_out(dest, what, context) + } + + fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { + NonFungibleMutateAdapter::< + NonFungible, + Matcher, + AccountIdConverter, + AccountId, + CheckingAccount, + >::deposit_asset(what, who, context) + } + + fn withdraw_asset( + what: &Asset, + who: &Location, + maybe_context: Option<&XcmContext>, + ) -> result::Result { + NonFungibleMutateAdapter::< + NonFungible, + Matcher, + AccountIdConverter, + AccountId, + CheckingAccount, + >::withdraw_asset(what, who, maybe_context) + } + + fn transfer_asset( + what: &Asset, + from: &Location, + to: &Location, + context: &XcmContext, + ) -> result::Result { + NonFungibleTransferAdapter::::transfer_asset( + what, from, to, context, + ) + } +} diff --git a/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs index b4801d3a23a16dd2d57aac3771f3e90fe43254cd..3fce953848ebdaf0021d84a333fdb00c346b5096 100644 --- a/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Adapters to work with [`frame_support::traits::fungibles`] through XCM. +//! Adapters to work with [`frame_support::traits::tokens::nonfungibles`] through XCM. use crate::{AssetChecking, MintLocation}; use frame_support::{ @@ -29,6 +29,9 @@ use xcm_executor::traits::{ const LOG_TARGET: &str = "xcm::nonfungibles_adapter"; +/// [`TransactAsset`] implementation that allows the use of a [`nonfungibles`] implementation for +/// handling an asset in the XCM executor. +/// Only works for transfers. pub struct NonFungiblesTransferAdapter( PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>, ); @@ -63,6 +66,9 @@ impl< } } +/// [`TransactAsset`] implementation that allows the use of a [`nonfungibles`] implementation for +/// handling an asset in the XCM executor. +/// Only works for teleport bookkeeping. pub struct NonFungiblesMutateAdapter< Assets, Matcher, @@ -245,6 +251,9 @@ impl< } } +/// [`TransactAsset`] implementation that allows the use of a [`nonfungibles`] implementation for +/// handling an asset in the XCM executor. +/// Works for everything. pub struct NonFungiblesAdapter< Assets, Matcher, diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs index 9c0302baee06b81ec662bc3a0e25eb9308fc0a3a..529ef80c15ff11d3c0d2629aeb4fa9506cb37a28 100644 --- a/polkadot/xcm/xcm-builder/src/routing.rs +++ b/polkadot/xcm/xcm-builder/src/routing.rs @@ -20,6 +20,7 @@ use frame_system::unique; use parity_scale_codec::Encode; use sp_std::{marker::PhantomData, result::Result}; use xcm::prelude::*; +use xcm_executor::{traits::FeeReason, FeesMode}; /// Wrapper router which, if the message does not already end with a `SetTopic` instruction, /// appends one to the message filled with a universally unique ID. This ID is returned from a @@ -104,3 +105,37 @@ impl SendXcm for WithTopicSource (Option, Option); +} + +/// Tuple implementation for `EnsureDelivery`. +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl EnsureDelivery for Tuple { + fn ensure_successful_delivery( + origin_ref: &Location, + dest: &Location, + fee_reason: FeeReason, + ) -> (Option, Option) { + for_tuples!( #( + // If the implementation returns something, we're done; if not, let others try. + match Tuple::ensure_successful_delivery(origin_ref, dest, fee_reason.clone()) { + r @ (Some(_), Some(_)) | r @ (Some(_), None) | r @ (None, Some(_)) => return r, + (None, None) => (), + } + )* ); + // doing nothing + (None, None) + } +} diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs index 11f0044fbcaf1aa36eef7922c1998e09f9edef94..767575e7f2dd9efa29cc1441a8cc2bf2cdaf3d19 100644 --- a/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs +++ b/polkadot/xcm/xcm-builder/src/tests/bridging/mod.rs @@ -33,6 +33,7 @@ mod paid_remote_relay_relay; mod remote_para_para; mod remote_para_para_via_relay; mod remote_relay_relay; +mod universal_exports; parameter_types! { pub Local: NetworkId = ByGenesis([0; 32]); diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs index 85d6524fb68ea996973edf73f30b25f5e08697bc..f9fa0c18c1f51c6682f8b49be9fe51c613e65350 100644 --- a/polkadot/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs +++ b/polkadot/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs @@ -24,9 +24,9 @@ use super::*; parameter_types! { - // 100 to use the bridge (export) and 80 for the remote execution weight (4 instructions x (10 + + // 100 to use the bridge (export) and 80 for the remote execution weight (5 instructions x (10 + // 10) weight each). - pub SendOverBridgePrice: u128 = 180u128 + if UsingTopic::get() { 20 } else { 0 }; + pub SendOverBridgePrice: u128 = 200u128 + if UsingTopic::get() { 20 } else { 0 }; pub UniversalLocation: Junctions = [GlobalConsensus(Local::get()), Parachain(100)].into(); pub RelayUniversalLocation: Junctions = [GlobalConsensus(Local::get())].into(); pub RemoteUniversalLocation: Junctions = [GlobalConsensus(Remote::get())].into(); @@ -101,15 +101,18 @@ fn sending_to_bridged_chain_works() { vec![ WithdrawAsset(Asset::from((Here, price)).into()), BuyExecution { fees: (Here, price).into(), weight_limit: Unlimited }, + SetAppendix(Xcm(vec![DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: Parachain(100).into(), + }])), ExportMessage { network: ByGenesis([1; 32]), destination: Here, xcm: xcm_with_topic([0; 32], vec![Trap(1)]), }, - DepositAsset { assets: Wild(All), beneficiary: Parachain(100).into() }, ], ), - outcome: Outcome::Complete { used: test_weight(4) }, + outcome: Outcome::Complete { used: test_weight(5) }, paid: true, }; assert_eq!(RoutingLog::take(), vec![entry]); @@ -175,15 +178,18 @@ fn sending_to_parachain_of_bridged_chain_works() { vec![ WithdrawAsset(Asset::from((Here, price)).into()), BuyExecution { fees: (Here, price).into(), weight_limit: Unlimited }, + SetAppendix(Xcm(vec![DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: Parachain(100).into(), + }])), ExportMessage { network: ByGenesis([1; 32]), destination: Parachain(100).into(), xcm: xcm_with_topic([0; 32], vec![Trap(1)]), }, - DepositAsset { assets: Wild(All), beneficiary: Parachain(100).into() }, ], ), - outcome: Outcome::Complete { used: test_weight(4) }, + outcome: Outcome::Complete { used: test_weight(5) }, paid: true, }; assert_eq!(RoutingLog::take(), vec![entry]); diff --git a/polkadot/xcm/xcm-builder/src/tests/bridging/universal_exports.rs b/polkadot/xcm/xcm-builder/src/tests/bridging/universal_exports.rs new file mode 100644 index 0000000000000000000000000000000000000000..160c6c9af50fd3b966fa70ac504e674e0fa175e9 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/tests/bridging/universal_exports.rs @@ -0,0 +1,108 @@ +// 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 crate::test_utils::TrappedAssets; + +#[test] +fn sovereign_paid_remote_exporter_produces_xcm_which_does_not_trap_assets() { + frame_support::parameter_types! { + pub BridgeFeeAsset: Location = Parent.into(); + pub LocalNetwork: NetworkId = ExecutorUniversalLocation::get().global_consensus().expect("valid `NetworkId`"); + pub LocalBridgeLocation: Location = match &ExecutorUniversalLocation::get().split_global() { + Ok((_, junctions)) => Location::new(1, junctions.clone()), + _ => panic!("unexpected location format") + }; + pub RemoteNetwork: NetworkId = ByGenesis([1; 32]); + pub SendOverBridgePrice: u128 = 333; + pub BridgeTable: Vec = vec![ + NetworkExportTableItem::new( + RemoteNetwork::get(), + None, + LocalBridgeLocation::get(), + Some((BridgeFeeAsset::get(), SendOverBridgePrice::get()).into()) + ) + ]; + pub static SenderUniversalLocation: InteriorLocation = (LocalNetwork::get(), Parachain(50)).into(); + } + + // `SovereignPaidRemoteExporter` e.g. used on sibling of `ExecutorUniversalLocation` + type Exporter = SovereignPaidRemoteExporter< + NetworkExportTable, + TestMessageSender, + SenderUniversalLocation, + >; + + // prepare message on sending chain with tested `Exporter` and translate it to the executor + // message type + let message = Exporter::validate( + &mut Some(Location::new(2, [GlobalConsensus(RemoteNetwork::get())])), + &mut Some(Xcm(vec![])), + ) + .expect("valid message"); + let message = Xcm::::from(message.0 .1); + let mut message_id = message.using_encoded(sp_io::hashing::blake2_256); + + // allow origin to pass barrier + let origin = Location::new(1, Parachain(50)); + AllowPaidFrom::set(vec![origin.clone()]); + + // fund origin + add_asset(origin.clone(), (AssetId(BridgeFeeAsset::get()), SendOverBridgePrice::get() * 2)); + WeightPrice::set((BridgeFeeAsset::get().into(), 1_000_000_000_000, 1024 * 1024)); + + // check before + assert!(TrappedAssets::get().is_empty()); + assert_eq!(exported_xcm(), vec![]); + + // execute XCM with overrides for `MessageExporter` behavior to return `Unroutable` error on + // validate + set_exporter_override( + |_, _, _, _, _| Err(SendError::Unroutable), + |_, _, _, _, _| Err(SendError::Transport("not allowed to call here")), + ); + let r = XcmExecutor::::prepare_and_execute( + origin.clone(), + message.clone(), + &mut message_id, + Weight::from_parts(2_000_000_000_000, 2_000_000_000_000), + Weight::zero(), + ); + assert_eq!( + r, + Outcome::Incomplete { used: Weight::from_parts(50, 50), error: XcmError::Unroutable } + ); + // check empty trapped assets + assert!(TrappedAssets::get().is_empty()); + // no xcm exported + assert_eq!(exported_xcm(), vec![]); + + // execute XCM again with clear `MessageExporter` overrides behavior to expect delivery + clear_exporter_override(); + let r = XcmExecutor::::prepare_and_execute( + origin.clone(), + message, + &mut message_id, + Weight::from_parts(2_000_000_000_000, 2_000_000_000_000), + Weight::zero(), + ); + assert_eq!(r, Outcome::Complete { used: Weight::from_parts(50, 50) }); + + // check empty trapped assets + assert!(TrappedAssets::get().is_empty()); + // xcm exported + assert_eq!(exported_xcm().len(), 1); +} diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index f561c7d3bd4e0ba269fd1fda77ecc32b5e19cce0..4521d5e92a42ba3c370e0de6c05b1fa6da158ce6 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -742,6 +742,7 @@ impl Config for TestConfig { type CallDispatcher = TestCall; type SafeCallFilter = Everything; type Aliasers = AliasForeignAccountId32; + type TransactionalProcessor = (); } pub fn fungible_multi_asset(location: Location, amount: u128) -> Asset { diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 76198f84ed6895e73f4555c2c5b94a86f3f1e27f..3bd2fa4b4f5309eac6bd2ac54ece79851c134613 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -25,14 +25,22 @@ use frame_support::{ traits::{ConstU32, Everything}, }; use frame_system::{EnsureRoot, EnsureSigned}; -use polkadot_test_runtime::SignedExtra; use primitives::{AccountIndex, BlakeTwo256, Signature}; use sp_runtime::{generic, traits::MaybeEquivalence, AccountId32, BuildStorage}; use xcm_executor::{traits::ConvertLocation, XcmExecutor}; +pub type TxExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckMortality, + frame_system::CheckNonce, + frame_system::CheckWeight, +); pub type Address = sp_runtime::MultiAddress; pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; pub type Header = generic::Header; pub type Block = generic::Block; @@ -77,7 +85,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -175,6 +182,7 @@ type OriginConverter = ( ); type Barrier = AllowUnpaidExecutionFrom; +#[derive(Clone)] pub struct DummyWeightTrader; impl WeightTrader for DummyWeightTrader { fn new() -> Self { @@ -217,6 +225,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = (); } parameter_types! { diff --git a/polkadot/xcm/xcm-builder/src/transactional.rs b/polkadot/xcm/xcm-builder/src/transactional.rs new file mode 100644 index 0000000000000000000000000000000000000000..ffe379b0ed41daa299a3ffc01b58176684628b14 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/transactional.rs @@ -0,0 +1,40 @@ +// Copyright 2021 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::storage::{with_transaction, TransactionOutcome}; +use sp_runtime::DispatchError; +use xcm::latest::prelude::*; +use xcm_executor::traits::ProcessTransaction; + +/// Transactional processor implementation using frame transactional layers. +pub struct FrameTransactionalProcessor; +impl ProcessTransaction for FrameTransactionalProcessor { + const IS_TRANSACTIONAL: bool = true; + + fn process(f: F) -> Result<(), XcmError> + where + F: FnOnce() -> Result<(), XcmError>, + { + with_transaction(|| -> TransactionOutcome> { + let output = f(); + match &output { + Ok(()) => TransactionOutcome::Commit(Ok(output)), + _ => TransactionOutcome::Rollback(Ok(output)), + } + }) + .map_err(|_| XcmError::ExceedsStackLimit)? + } +} diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index 1d084e022c92e081c42212a69ba43d2e4e4f6d74..6e031cdbc270d73c49fcd7960430473a9bb9c9d5 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -305,8 +305,14 @@ impl Option { - log::trace!(target: "xcm::weight", "UsingComponents::refund_weight weight: {:?}, context: {:?}", weight, context); + log::trace!(target: "xcm::weight", "UsingComponents::refund_weight weight: {:?}, context: {:?}, available weight: {:?}, available amount: {:?}", weight, context, self.0, self.1); let weight = weight.min(self.0); let amount = WeightToFee::weight_to_fee(&weight); self.0 -= weight; self.1 = self.1.saturating_sub(amount); let amount: u128 = amount.saturated_into(); + log::trace!(target: "xcm::weight", "UsingComponents::refund_weight amount to refund: {:?}", amount); if amount > 0 { Some((AssetIdValue::get(), amount).into()) } else { diff --git a/polkadot/xcm/xcm-builder/tests/mock/mod.rs b/polkadot/xcm/xcm-builder/tests/mock/mod.rs index 73df22561d12966c0afbf0b6a7d9c00ca1745b77..06cedb9c35775898e264a58e175ebe81e06d10a4 100644 --- a/polkadot/xcm/xcm-builder/tests/mock/mod.rs +++ b/polkadot/xcm/xcm-builder/tests/mock/mod.rs @@ -32,14 +32,12 @@ use xcm_executor::XcmExecutor; use staging_xcm_builder as xcm_builder; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - FixedRateOfFungible, FixedWeightBounds, IsChildSystemParachain, IsConcrete, MintLocation, - RespectSuspension, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, - TakeWeightCredit, + FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, IsChildSystemParachain, IsConcrete, + MintLocation, RespectSuspension, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, }; pub type AccountId = AccountId32; @@ -124,7 +122,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -147,14 +144,8 @@ parameter_types! { pub type SovereignAccountOf = (ChildParachainConvertsVia, AccountId32Aliases); -#[allow(deprecated)] -pub type LocalCurrencyAdapter = XcmCurrencyAdapter< - Balances, - IsConcrete, - SovereignAccountOf, - AccountId, - CheckAccount, ->; +pub type LocalCurrencyAdapter = + FungibleAdapter, SovereignAccountOf, AccountId, CheckAccount>; pub type LocalAssetTransactor = (LocalCurrencyAdapter,); @@ -212,6 +203,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-executor/Cargo.toml b/polkadot/xcm/xcm-executor/Cargo.toml index 32fa6669c0abc6b96e2cb50d201f87b749240306..71bd58073db6d7247cf5048d4a684aa19ea79bfd 100644 --- a/polkadot/xcm/xcm-executor/Cargo.toml +++ b/polkadot/xcm/xcm-executor/Cargo.toml @@ -4,7 +4,7 @@ description = "An abstract and configurable XCM message executor." authors.workspace = true edition.workspace = true license.workspace = true -version = "1.0.0" +version = "7.0.0" [lints] workspace = true @@ -22,7 +22,7 @@ sp-core = { path = "../../../substrate/primitives/core", default-features = fals sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } sp-weights = { path = "../../../substrate/primitives/weights", default-features = false } frame-support = { path = "../../../substrate/frame/support", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } [features] diff --git a/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml b/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml index cafe12dc587f883a31ac3539aced38e8de29a89e..1e572e6210a27a7469f827bbe5d076c01c84f032 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml +++ b/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml @@ -20,6 +20,7 @@ pallet-xcm = { path = "../../pallet-xcm" } polkadot-test-client = { path = "../../../node/test/client" } polkadot-test-runtime = { path = "../../../runtime/test-runtime" } polkadot-test-service = { path = "../../../node/test/service" } +polkadot-service = { path = "../../../node/service" } sp-consensus = { path = "../../../../substrate/primitives/consensus/common" } sp-keyring = { path = "../../../../substrate/primitives/keyring" } sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } @@ -27,6 +28,7 @@ sp-state-machine = { path = "../../../../substrate/primitives/state-machine" } xcm = { package = "staging-xcm", path = "../..", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = ".." } sp-tracing = { path = "../../../../substrate/primitives/tracing" } +sp-core = { path = "../../../../substrate/primitives/core" } [features] default = ["std"] diff --git a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs index 79d6cb1c411b12da2a9f6b81a01b93a44c97ebe9..da7fc0d97825f24e1eea91e441cecef11a136e5f 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs @@ -19,12 +19,14 @@ use codec::Encode; use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use polkadot_service::chain_spec::get_account_id_from_seed; use polkadot_test_client::{ BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt, }; use polkadot_test_runtime::{pallet_test_notifier, xcm_config::XcmConfig}; use polkadot_test_service::construct_extrinsic; +use sp_core::sr25519; use sp_runtime::traits::Block; use sp_state_machine::InspectState; use xcm::{latest::prelude::*, VersionedResponse, VersionedXcm}; @@ -323,3 +325,84 @@ fn query_response_elicits_handler() { ))); }); } + +/// Simulates a cross-chain message from Parachain to Parachain through Relay Chain +/// that deposits assets into the reserve of the destination. +/// Regression test for `DepostiReserveAsset` changes in +/// +#[test] +fn deposit_reserve_asset_works_for_any_xcm_sender() { + sp_tracing::try_init_simple(); + let mut client = TestClientBuilder::new().build(); + + // Init values for the simulated origin Parachain + let amount_to_send: u128 = 1_000_000_000_000; + let assets: Assets = (Parent, amount_to_send).into(); + let fee_asset_item = 0; + let max_assets = assets.len() as u32; + let fees = assets.get(fee_asset_item as usize).unwrap().clone(); + let weight_limit = Unlimited; + let reserve = Location::parent(); + let dest = Location::new(1, [Parachain(2000)]); + let beneficiary_id = get_account_id_from_seed::("Alice"); + let beneficiary = Location::new(0, [AccountId32 { network: None, id: beneficiary_id.into() }]); + + // spends up to half of fees for execution on reserve and other half for execution on + // destination + let fee1 = amount_to_send.saturating_div(2); + let fee2 = amount_to_send.saturating_sub(fee1); + let fees_half_1 = Asset::from((fees.id.clone(), Fungible(fee1))); + let fees_half_2 = Asset::from((fees.id.clone(), Fungible(fee2))); + + let reserve_context = ::UniversalLocation::get(); + // identifies fee item as seen by `reserve` - to be used at reserve chain + let reserve_fees = fees_half_1.reanchored(&reserve, &reserve_context).unwrap(); + // identifies fee item as seen by `dest` - to be used at destination chain + let dest_fees = fees_half_2.reanchored(&dest, &reserve_context).unwrap(); + // identifies assets as seen by `reserve` - to be used at reserve chain + let assets_reanchored = assets.reanchored(&reserve, &reserve_context).unwrap(); + // identifies `dest` as seen by `reserve` + let dest = dest.reanchored(&reserve, &reserve_context).unwrap(); + // xcm to be executed at dest + let xcm_on_dest = Xcm(vec![ + BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }, + DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }, + ]); + // xcm to be executed at reserve + let msg = Xcm(vec![ + WithdrawAsset(assets_reanchored), + ClearOrigin, + BuyExecution { fees: reserve_fees, weight_limit }, + DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest }, + ]); + + let mut block_builder = client.init_polkadot_block_builder(); + + // Simulate execution of an incoming XCM message at the reserve chain + let execute = construct_extrinsic( + &client, + polkadot_test_runtime::RuntimeCall::Xcm(pallet_xcm::Call::execute { + message: Box::new(VersionedXcm::from(msg)), + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + }), + sp_keyring::Sr25519Keyring::Alice, + 0, + ); + + block_builder.push_polkadot_extrinsic(execute).expect("pushes extrinsic"); + + let block = block_builder.build().expect("Finalizes the block").block; + let block_hash = block.hash(); + + futures::executor::block_on(client.import(sp_consensus::BlockOrigin::Own, block)) + .expect("imports the block"); + + client.state_at(block_hash).expect("state should exist").inspect_state(|| { + assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( + r.event, + polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted { + outcome: Outcome::Complete { .. } + }), + ))); + }); +} diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index 3f1ea6d1fb8e2c2105afcb1cbac2818a0d7c44c5..ebe532a42fd396c64d64590bfb18e6778995bd4a 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -16,8 +16,8 @@ use crate::traits::{ AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, - FeeManager, OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, - WeightTrader, + FeeManager, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset, + VersionChangeNotifier, WeightBounds, WeightTrader, }; use frame_support::{ dispatch::{GetDispatchInfo, Parameter, PostDispatchInfo}, @@ -111,4 +111,7 @@ pub trait Config { /// Use this type to explicitly whitelist calls that cannot undergo recursion. This is a /// temporary measure until we properly account for proof size weights for XCM instructions. type SafeCallFilter: Contains; + + /// Transactional processor for XCM instructions. + type TransactionalProcessor: ProcessTransaction; } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 1f5f2eba5e2f65a3ee8d161011c7e67dede9bfcc..c61e1e1d15bcd8fd78ffe47c415abeaf8c1cb4cd 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -19,7 +19,7 @@ use frame_support::{ dispatch::GetDispatchInfo, ensure, - traits::{Contains, ContainsPair, Get, PalletsInfoAccess}, + traits::{Contains, ContainsPair, Defensive, Get, PalletsInfoAccess}, }; use parity_scale_codec::{Decode, Encode}; use sp_core::defer; @@ -31,8 +31,9 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, - DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, Properties, ShouldExecute, - TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, XcmAssetTransfers, + DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, ProcessTransaction, + Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + XcmAssetTransfers, }; mod assets; @@ -373,33 +374,53 @@ impl XcmExecutor { r } - fn subsume_asset(&mut self, asset: Asset) -> Result<(), XcmError> { + fn ensure_can_subsume_assets(&self, assets_length: usize) -> Result<(), XcmError> { // worst-case, holding.len becomes 2 * holding_limit. - ensure!(self.holding.len() < self.holding_limit * 2, XcmError::HoldingWouldOverflow); - self.holding.subsume(asset); - Ok(()) - } - - fn subsume_assets(&mut self, assets: AssetsInHolding) -> Result<(), XcmError> { - // worst-case, holding.len becomes 2 * holding_limit. - // this guarantees that if holding.len() == holding_limit and you have holding_limit more - // items (which has a best case outcome of holding.len() == holding_limit), then you'll - // be guaranteed of making the operation. - let worst_case_holding_len = self.holding.len() + assets.len(); + // this guarantees that if holding.len() == holding_limit and you have more than + // `holding_limit` items (which has a best case outcome of holding.len() == holding_limit), + // then the operation is guaranteed to succeed. + let worst_case_holding_len = self.holding.len() + assets_length; + log::trace!(target: "xcm::ensure_can_subsume_assets", "worst_case_holding_len: {:?}, holding_limit: {:?}", worst_case_holding_len, self.holding_limit); ensure!(worst_case_holding_len <= self.holding_limit * 2, XcmError::HoldingWouldOverflow); - self.holding.subsume_assets(assets); Ok(()) } /// Refund any unused weight. fn refund_surplus(&mut self) -> Result<(), XcmError> { let current_surplus = self.total_surplus.saturating_sub(self.total_refunded); + log::trace!( + target: "xcm::refund_surplus", + "total_surplus: {:?}, total_refunded: {:?}, current_surplus: {:?}", + self.total_surplus, + self.total_refunded, + current_surplus, + ); if current_surplus.any_gt(Weight::zero()) { - self.total_refunded.saturating_accrue(current_surplus); if let Some(w) = self.trader.refund_weight(current_surplus, &self.context) { - self.subsume_asset(w)?; + if !self.holding.contains_asset(&(w.id.clone(), 1).into()) && + self.ensure_can_subsume_assets(1).is_err() + { + let _ = self + .trader + .buy_weight(current_surplus, w.into(), &self.context) + .defensive_proof( + "refund_weight returned an asset capable of buying weight; qed", + ); + log::error!( + target: "xcm::refund_surplus", + "error: HoldingWouldOverflow", + ); + return Err(XcmError::HoldingWouldOverflow) + } + self.total_refunded.saturating_accrue(current_surplus); + self.holding.subsume_assets(w.into()); } } + log::trace!( + target: "xcm::refund_surplus", + "total_refunded: {:?}", + self.total_refunded, + ); Ok(()) } @@ -554,75 +575,101 @@ impl XcmExecutor { ); match instr { WithdrawAsset(assets) => { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - // Take `assets` from the origin account (on-chain) and place in holding. - for asset in assets.into_inner().into_iter() { - Config::AssetTransactor::withdraw_asset(&asset, &origin, Some(&self.context))?; - self.subsume_asset(asset)?; - } - Ok(()) + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; + Config::TransactionalProcessor::process(|| { + // Take `assets` from the origin account (on-chain)... + for asset in assets.inner() { + Config::AssetTransactor::withdraw_asset( + asset, + origin, + Some(&self.context), + )?; + } + Ok(()) + }) + .and_then(|_| { + // ...and place into holding. + self.holding.subsume_assets(assets.into()); + Ok(()) + }) }, ReserveAssetDeposited(assets) => { // check whether we trust origin to be our reserve location for this asset. - for asset in assets.into_inner().into_iter() { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; + for asset in assets.inner() { // Must ensure that we recognise the asset as being managed by the origin. ensure!( - Config::IsReserve::contains(&asset, &origin), + Config::IsReserve::contains(asset, origin), XcmError::UntrustedReserveLocation ); - self.subsume_asset(asset)?; } + self.holding.subsume_assets(assets.into()); Ok(()) }, TransferAsset { assets, beneficiary } => { - // Take `assets` from the origin account (on-chain) and place into dest account. - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in assets.inner() { - Config::AssetTransactor::transfer_asset( - &asset, - origin, - &beneficiary, - &self.context, - )?; - } - Ok(()) + Config::TransactionalProcessor::process(|| { + // Take `assets` from the origin account (on-chain) and place into dest account. + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + for asset in assets.inner() { + Config::AssetTransactor::transfer_asset( + &asset, + origin, + &beneficiary, + &self.context, + )?; + } + Ok(()) + }) }, TransferReserveAsset { mut assets, dest, xcm } => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - // Take `assets` from the origin account (on-chain) and place into dest account. - for asset in assets.inner() { - Config::AssetTransactor::transfer_asset(asset, origin, &dest, &self.context)?; - } - let reanchor_context = Config::UniversalLocation::get(); - assets.reanchor(&dest, &reanchor_context).map_err(|()| XcmError::LocationFull)?; - let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?; - Ok(()) + Config::TransactionalProcessor::process(|| { + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + // Take `assets` from the origin account (on-chain) and place into dest account. + for asset in assets.inner() { + Config::AssetTransactor::transfer_asset( + asset, + origin, + &dest, + &self.context, + )?; + } + let reanchor_context = Config::UniversalLocation::get(); + assets + .reanchor(&dest, &reanchor_context) + .map_err(|()| XcmError::LocationFull)?; + let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?; + Ok(()) + }) }, ReceiveTeleportedAsset(assets) => { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - // check whether we trust origin to teleport this asset to us via config trait. - for asset in assets.inner() { - // We only trust the origin to send us assets that they identify as their - // sovereign assets. - ensure!( - Config::IsTeleporter::contains(asset, &origin), - XcmError::UntrustedTeleportLocation - ); - // We should check that the asset can actually be teleported in (for this to be - // in error, there would need to be an accounting violation by one of the - // trusted chains, so it's unlikely, but we don't want to punish a possibly - // innocent chain/user). - Config::AssetTransactor::can_check_in(&origin, asset, &self.context)?; - } - for asset in assets.into_inner().into_iter() { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - Config::AssetTransactor::check_in(&origin, &asset, &self.context); - self.subsume_asset(asset)?; - } - Ok(()) + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; + Config::TransactionalProcessor::process(|| { + // check whether we trust origin to teleport this asset to us via config trait. + for asset in assets.inner() { + // We only trust the origin to send us assets that they identify as their + // sovereign assets. + ensure!( + Config::IsTeleporter::contains(asset, origin), + XcmError::UntrustedTeleportLocation + ); + // We should check that the asset can actually be teleported in (for this to + // be in error, there would need to be an accounting violation by one of the + // trusted chains, so it's unlikely, but we don't want to punish a possibly + // innocent chain/user). + Config::AssetTransactor::can_check_in(origin, asset, &self.context)?; + Config::AssetTransactor::check_in(origin, asset, &self.context); + } + Ok(()) + }) + .and_then(|_| { + self.holding.subsume_assets(assets.into()); + Ok(()) + }) }, Transact { origin_kind, require_weight_at_most, mut call } => { // We assume that the Relay-chain is allowed to use transact on this parachain. @@ -755,62 +802,108 @@ impl XcmExecutor { Ok(()) }, DepositAsset { assets, beneficiary } => { - let deposited = self.holding.saturating_take(assets); - for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset( - &asset, - &beneficiary, - Some(&self.context), - )?; + 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(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; } - Ok(()) + result }, DepositReserveAsset { assets, dest, xcm } => { - let deposited = self.holding.saturating_take(assets); - for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + // we need to do this take/put cycle to solve wildcards and get exact assets to + // be weighed + let to_weigh = self.holding.saturating_take(assets.clone()); + self.holding.subsume_assets(to_weigh.clone()); + let to_weigh_reanchored = Self::reanchored(to_weigh, &dest, None); + let mut message_to_weigh = + vec![ReserveAssetDeposited(to_weigh_reanchored), ClearOrigin]; + 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 deposited = self.holding.saturating_take(assets); + for asset in deposited.assets_iter() { + Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; + } + // 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. + 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); + self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; } - // 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. - let assets = Self::reanchored(deposited, &dest, None); - let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; - Ok(()) + result }, InitiateReserveWithdraw { assets, reserve, xcm } => { - // Note that here we are able to place any assets which could not be reanchored - // back into Holding. - let assets = Self::reanchored( - self.holding.saturating_take(assets), - &reserve, - Some(&mut self.holding), - ); - let mut message = vec![WithdrawAsset(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + // Note that here we are able to place any assets which could not be reanchored + // back into Holding. + let assets = Self::reanchored( + self.holding.saturating_take(assets), + &reserve, + Some(&mut self.holding), + ); + let mut message = vec![WithdrawAsset(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, InitiateTeleport { assets, dest, xcm } => { - // We must do this first in order to resolve wildcards. - let assets = self.holding.saturating_take(assets); - for asset in assets.assets_iter() { - // We should check that the asset can actually be teleported out (for this to - // be in error, there would need to be an accounting violation by ourselves, - // so it's unlikely, but we don't want to allow that kind of bug to leak into - // a trusted chain. - Config::AssetTransactor::can_check_out(&dest, &asset, &self.context)?; - } - for asset in assets.assets_iter() { - Config::AssetTransactor::check_out(&dest, &asset, &self.context); + let old_holding = self.holding.clone(); + let result = (|| -> Result<(), XcmError> { + // We must do this first in order to resolve wildcards. + let assets = self.holding.saturating_take(assets); + for asset in assets.assets_iter() { + // We should check that the asset can actually be teleported out (for this + // to be in error, there would need to be an accounting violation by + // ourselves, so it's unlikely, but we don't want to allow that kind of bug + // to leak into a trusted chain. + Config::AssetTransactor::can_check_out(&dest, &asset, &self.context)?; + } + // Note that we pass `None` as `maybe_failed_bin` and drop any assets which + // cannot be reanchored because we have already checked all assets out. + let reanchored_assets = Self::reanchored(assets.clone(), &dest, None); + let mut message = vec![ReceiveTeleportedAsset(reanchored_assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + self.send(dest.clone(), Xcm(message), FeeReason::InitiateTeleport)?; + + for asset in assets.assets_iter() { + Config::AssetTransactor::check_out(&dest, &asset, &self.context); + } + Ok(()) + })(); + if result.is_err() { + self.holding = old_holding; } - // Note that we pass `None` as `maybe_failed_bin` and drop any assets which cannot - // be reanchored because we have already checked all assets out. - let assets = Self::reanchored(assets, &dest, None); - let mut message = vec![ReceiveTeleportedAsset(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest, Xcm(message), FeeReason::InitiateTeleport)?; - Ok(()) + result }, ReportHolding { response_info, assets } => { // Note that we pass `None` as `maybe_failed_bin` since no assets were ever removed @@ -826,18 +919,24 @@ impl XcmExecutor { Ok(()) }, BuyExecution { fees, weight_limit } => { - // There is no need to buy any weight is `weight_limit` is `Unlimited` since it + // There is no need to buy any weight if `weight_limit` is `Unlimited` since it // would indicate that `AllowTopLevelPaidExecutionFrom` was unused for execution // and thus there is some other reason why it has been determined that this XCM // should be executed. - if let Some(weight) = Option::::from(weight_limit) { - // pay for `weight` using up to `fees` of the holding register. - let max_fee = - self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; + let Some(weight) = Option::::from(weight_limit) else { return Ok(()) }; + let old_holding = self.holding.clone(); + // pay for `weight` using up to `fees` of the holding register. + let max_fee = + self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; + let result = || -> Result<(), XcmError> { let unspent = self.trader.buy_weight(weight, max_fee, &self.context)?; - self.subsume_assets(unspent)?; + self.holding.subsume_assets(unspent); + Ok(()) + }(); + if result.is_err() { + self.holding = old_holding; } - Ok(()) + result }, RefundSurplus => self.refund_surplus(), SetErrorHandler(mut handler) => { @@ -862,11 +961,10 @@ impl XcmExecutor { }, ClaimAsset { assets, ticket } => { let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; let ok = Config::AssetClaims::claim_assets(origin, &ticket, &assets, &self.context); ensure!(ok, XcmError::UnknownClaim); - for asset in assets.into_inner().into_iter() { - self.subsume_asset(asset)?; - } + self.holding.subsume_assets(assets.into()); Ok(()) }, Trap(code) => Err(XcmError::Trap(code)), @@ -984,8 +1082,8 @@ impl XcmExecutor { let hash = (self.origin_ref(), &destination).using_encoded(blake2_128); let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); // Hash identifies the lane on the exporter which we use. We use the pairwise - // combination of the origin and destination to ensure origin/destination pairs will - // generally have their own lanes. + // combination of the origin and destination to ensure origin/destination pairs + // will generally have their own lanes. let (ticket, fee) = validate_export::( network, channel, @@ -993,23 +1091,41 @@ impl XcmExecutor { destination.clone(), xcm, )?; - self.take_fee(fee, FeeReason::Export { network, destination })?; - Config::MessageExporter::deliver(ticket)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + self.take_fee(fee, FeeReason::Export { network, destination })?; + let _ = Config::MessageExporter::deliver(ticket).defensive_proof( + "`deliver` called immediately after `validate_export`; \ + `take_fee` does not affect the validity of the ticket; qed", + ); + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, LockAsset { asset, unlocker } => { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?; - let lock_ticket = - Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?; - let owner = - origin.reanchored(&unlocker, &context).map_err(|_| XcmError::ReanchorFailed)?; - let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]); - let (ticket, price) = validate_send::(unlocker, msg)?; - self.take_fee(price, FeeReason::LockAsset)?; - lock_ticket.enact()?; - Config::XcmSender::deliver(ticket)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; + let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?; + let lock_ticket = + Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?; + let owner = origin + .reanchored(&unlocker, &context) + .map_err(|_| XcmError::ReanchorFailed)?; + let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]); + let (ticket, price) = validate_send::(unlocker, msg)?; + self.take_fee(price, FeeReason::LockAsset)?; + lock_ticket.enact()?; + Config::XcmSender::deliver(ticket)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, UnlockAsset { asset, target } => { let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; @@ -1033,25 +1149,40 @@ impl XcmExecutor { let msg = Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: remote_target }]); let (ticket, price) = validate_send::(locker, msg)?; - self.take_fee(price, FeeReason::RequestUnlock)?; - reduce_ticket.enact()?; - Config::XcmSender::deliver(ticket)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + self.take_fee(price, FeeReason::RequestUnlock)?; + reduce_ticket.enact()?; + Config::XcmSender::deliver(ticket)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, ExchangeAsset { give, want, maximal } => { + let old_holding = self.holding.clone(); let give = self.holding.saturating_take(give); - let r = - Config::AssetExchanger::exchange_asset(self.origin_ref(), give, &want, maximal); - let completed = r.is_ok(); - let received = r.unwrap_or_else(|a| a); - for asset in received.into_assets_iter() { - self.holding.subsume(asset); - } - if completed { - Ok(()) - } else { - Err(XcmError::NoDeal) + let result = (|| -> Result<(), XcmError> { + self.ensure_can_subsume_assets(want.len())?; + let exchange_result = Config::AssetExchanger::exchange_asset( + self.origin_ref(), + give, + &want, + maximal, + ); + if let Ok(received) = exchange_result { + self.holding.subsume_assets(received.into()); + Ok(()) + } else { + Err(XcmError::NoDeal) + } + })(); + if result.is_err() { + self.holding = old_holding; } + result }, SetFeesMode { jit_withdraw } => { self.fees_mode = FeesMode { jit_withdraw }; diff --git a/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs b/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs index 1fca84f36e22a45faa4f449c72d64619c744096b..5da3d1da37c8117d2f0f754c4cfea1b3e638cb06 100644 --- a/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs +++ b/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs @@ -85,3 +85,12 @@ pub trait XcmAssetTransfers { } } } + +impl XcmAssetTransfers for () { + type IsReserve = (); + type IsTeleporter = (); + type AssetTransactor = (); + fn determine_for(_: &Asset, _: &Location) -> Result { + return Err(Error::UnknownReserve); + } +} diff --git a/polkadot/xcm/xcm-executor/src/traits/mod.rs b/polkadot/xcm/xcm-executor/src/traits/mod.rs index 71e75c77e9394129c65505cc1daddb825fd4c077..b445e84d39120bd7970dbfff599a061f863ab2d9 100644 --- a/polkadot/xcm/xcm-executor/src/traits/mod.rs +++ b/polkadot/xcm/xcm-executor/src/traits/mod.rs @@ -39,6 +39,8 @@ pub use token_matching::{ }; mod on_response; pub use on_response::{OnResponse, QueryHandler, QueryResponseStatus, VersionChangeNotifier}; +mod process_transaction; +pub use process_transaction::ProcessTransaction; mod should_execute; pub use should_execute::{CheckSuspension, Properties, ShouldExecute}; mod transact_asset; @@ -52,8 +54,9 @@ pub mod prelude { pub use super::{ export_xcm, validate_export, AssetExchange, AssetLock, ClaimAssets, ConvertOrigin, DropAssets, Enact, Error, ExportXcm, FeeManager, FeeReason, LockError, MatchesFungible, - MatchesFungibles, MatchesNonFungible, MatchesNonFungibles, OnResponse, ShouldExecute, - TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, WithOriginFilter, + MatchesFungibles, MatchesNonFungible, MatchesNonFungibles, OnResponse, ProcessTransaction, + ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + WithOriginFilter, }; #[allow(deprecated)] pub use super::{Identity, JustTry}; diff --git a/polkadot/xcm/xcm-executor/src/traits/process_transaction.rs b/polkadot/xcm/xcm-executor/src/traits/process_transaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..22ad8755b9c90fac13fd414c52ee44d6e6792c70 --- /dev/null +++ b/polkadot/xcm/xcm-executor/src/traits/process_transaction.rs @@ -0,0 +1,57 @@ +// 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 xcm::latest::prelude::*; + +/// Provides mechanisms for transactional processing of XCM instructions. +/// +/// This trait defines the behavior required to process XCM instructions in a transactional +/// manner. Implementers of this trait can ensure that XCM instructions are executed +/// atomically, meaning they either fully succeed or fully fail without any partial effects. +/// +/// Implementers of this trait can also choose to not process XCM instructions transactionally. +/// This is useful for cases where the implementer is not able to provide transactional guarantees. +/// In this case the `IS_TRANSACTIONAL` constant should be set to `false`. +/// The `()` type implements this trait in a non-transactional manner. +pub trait ProcessTransaction { + /// Whether or not the implementor of the this trait is actually transactional. + const IS_TRANSACTIONAL: bool; + + /// Processes an XCM instruction encapsulated within the provided closure. Responsible for + /// processing an XCM instruction transactionally. If the closure returns an error, any + /// changes made during its execution should be rolled back. In the case where the + /// implementer is not able to provide transactional guarantees, the closure should be + /// executed as is. + /// # Parameters + /// - `f`: A closure that encapsulates the XCM instruction being processed. It will return a + /// `Result` indicating the success or failure of the instruction. + /// + /// # Returns + /// - A `Result` indicating the overall success or failure of the transactional process. + fn process(f: F) -> Result<(), XcmError> + where + F: FnOnce() -> Result<(), XcmError>; +} + +impl ProcessTransaction for () { + const IS_TRANSACTIONAL: bool = false; + fn process(f: F) -> Result<(), XcmError> + where + F: FnOnce() -> Result<(), XcmError>, + { + f() + } +} diff --git a/polkadot/xcm/xcm-simulator/Cargo.toml b/polkadot/xcm/xcm-simulator/Cargo.toml index 051e9752f6e485333a6b3d0507f4360000c9a8de..c1c48b6d4c5eb6195a60152f29bdb61e2ad80a79 100644 --- a/polkadot/xcm/xcm-simulator/Cargo.toml +++ b/polkadot/xcm/xcm-simulator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "xcm-simulator" description = "Test kit to simulate cross-chain message passing and XCM execution" -version = "1.0.0" +version = "7.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/polkadot/xcm/xcm-simulator/example/Cargo.toml b/polkadot/xcm/xcm-simulator/example/Cargo.toml index 522b7855837008a77f202fe0b2cbd4d65cb2d3f6..af471df60aba4dbc4d03692e7bfb84d99eb16420 100644 --- a/polkadot/xcm/xcm-simulator/example/Cargo.toml +++ b/polkadot/xcm/xcm-simulator/example/Cargo.toml @@ -4,7 +4,7 @@ description = "Examples of xcm-simulator usage." authors.workspace = true edition.workspace = true license.workspace = true -version = "1.0.0" +version = "7.0.0" [lints] workspace = true @@ -12,7 +12,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } scale-info = { version = "2.10.0", features = ["derive"] } -log = { version = "0.4.14", default-features = false } +log = { workspace = true } frame-system = { path = "../../../../substrate/frame/system" } frame-support = { path = "../../../../substrate/frame/support" } diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs index 3adfa95d2e85ed5152d52edf73de50add27b3adc..64333b4d5815018381f86eca10199c0d90feb6e1 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain.rs @@ -40,9 +40,10 @@ use polkadot_parachain_primitives::primitives::{ use xcm::{latest::prelude::*, VersionedXcm}; use xcm_builder::{ Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId, - EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, IsConcrete, - NativeAsset, NoChecking, NonFungiblesAdapter, ParentIsPreset, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor, + FungibleAdapter, IsConcrete, NativeAsset, NoChecking, NonFungiblesAdapter, ParentIsPreset, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, }; use xcm_executor::{ traits::{ConvertLocation, JustTry}, @@ -108,7 +109,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -250,6 +250,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } #[frame_support::pallet] diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs index d9ed6e800aa2e4c19ada4f2ae694557b13485ab1..54c5657c00d7f6c38ade3aadaf38056504abcc76 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs @@ -36,9 +36,9 @@ use xcm::latest::prelude::*; use xcm_builder::{ Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - ConvertedConcreteId, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, IsConcrete, - NoChecking, NonFungiblesAdapter, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, + ConvertedConcreteId, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor, + FungibleAdapter, IsConcrete, NoChecking, NonFungiblesAdapter, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -95,7 +95,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -198,6 +197,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml b/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml index 1d13c76f17103ed84346d15b7495665fbdbd46d7..30644dc0e0a53fe485261f9d0e52533ba0641603 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml +++ b/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml @@ -13,11 +13,13 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } honggfuzz = "0.5.55" -arbitrary = "1.2.0" +arbitrary = "1.3.2" scale-info = { version = "2.10.0", features = ["derive"] } frame-system = { path = "../../../../substrate/frame/system" } frame-support = { path = "../../../../substrate/frame/support" } +frame-executive = { path = "../../../../substrate/frame/executive" } +frame-try-runtime = { path = "../../../../substrate/frame/try-runtime" } pallet-balances = { path = "../../../../substrate/frame/balances" } pallet-message-queue = { path = "../../../../substrate/frame/message-queue" } sp-std = { path = "../../../../substrate/primitives/std" } @@ -35,6 +37,17 @@ polkadot-runtime-parachains = { path = "../../../runtime/parachains" } polkadot-parachain-primitives = { path = "../../../parachain" } [features] +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-xcm/try-runtime", + "polkadot-runtime-parachains/try-runtime", + "sp-runtime/try-runtime", +] runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", diff --git a/polkadot/xcm/xcm-simulator/fuzzer/README.md b/polkadot/xcm/xcm-simulator/fuzzer/README.md index 0b3fdd8ec776d5e78d37aa5b57e6c0762af21320..9c15ee881c1b6f5f9a1c30215a16e74abdda2b62 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/README.md +++ b/polkadot/xcm/xcm-simulator/fuzzer/README.md @@ -14,7 +14,7 @@ cargo install honggfuzz In this directory, run this command: ``` -cargo hfuzz run xcm-fuzzer +HFUZZ_BUILD_ARGS="--features=try-runtime" cargo hfuzz run xcm-fuzzer ``` ## Run a single input @@ -22,7 +22,7 @@ cargo hfuzz run xcm-fuzzer In this directory, run this command: ``` -cargo hfuzz run-debug xcm-fuzzer hfuzz_workspace/xcm-fuzzer/fuzzer_input_file +cargo run --features=try-runtime -- hfuzz_workspace/xcm-fuzzer/fuzzer_input_file ``` ## Generate coverage @@ -31,7 +31,7 @@ In this directory, run these four commands: ``` RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" \ -CARGO_INCREMENTAL=0 SKIP_WASM_BUILD=1 CARGO_HOME=./cargo cargo build +CARGO_INCREMENTAL=0 SKIP_WASM_BUILD=1 CARGO_HOME=./cargo cargo build --features=try-runtime ../../../target/debug/xcm-fuzzer hfuzz_workspace/xcm-fuzzer/input/ zip -0 ccov.zip `find ../../../target/ \( -name "*.gc*" -o -name "test-*.gc*" \) -print` grcov ccov.zip -s ../../../ -t html --llvm --branch --ignore-not-existing -o ./coverage diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs index 7026d5467c8b9049eaa86f94e9cd927321d10d68..adf6cacd278b9f25a02f9199fbdabc0af3434414 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs @@ -23,7 +23,9 @@ use polkadot_parachain_primitives::primitives::Id as ParaId; use sp_runtime::{traits::AccountIdConversion, BuildStorage}; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; -use frame_support::assert_ok; +#[cfg(feature = "try-runtime")] +use frame_support::traits::{TryState, TryStateSelect::All}; +use frame_support::{assert_ok, traits::IntegrityTest}; use xcm::{latest::prelude::*, MAX_XCM_DECODE_DEPTH}; use arbitrary::{Arbitrary, Error, Unstructured}; @@ -98,7 +100,7 @@ impl<'a> Arbitrary<'a> for XcmMessage { if let Ok(message) = DecodeLimit::decode_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut encoded_message) { - return Ok(XcmMessage { source, destination, message }) + return Ok(XcmMessage { source, destination, message }); } Err(Error::IncorrectFormat) } @@ -148,6 +150,21 @@ pub fn relay_ext() -> sp_io::TestExternalities { pub type RelayChainPalletXcm = pallet_xcm::Pallet; pub type ParachainPalletXcm = pallet_xcm::Pallet; +// We check XCM messages recursively for blocklisted messages +fn recursively_matches_blocklisted_messages(message: &Instruction<()>) -> bool { + match message { + DepositReserveAsset { xcm, .. } | + ExportMessage { xcm, .. } | + InitiateReserveWithdraw { xcm, .. } | + InitiateTeleport { xcm, .. } | + TransferReserveAsset { xcm, .. } | + SetErrorHandler(xcm) | + SetAppendix(xcm) => xcm.iter().any(recursively_matches_blocklisted_messages), + // The blocklisted message is the Transact instruction. + m => matches!(m, Transact { .. }), + } +} + fn run_input(xcm_messages: [XcmMessage; 5]) { MockNet::reset(); @@ -155,6 +172,11 @@ fn run_input(xcm_messages: [XcmMessage; 5]) { println!(); for xcm_message in xcm_messages { + if xcm_message.message.iter().any(recursively_matches_blocklisted_messages) { + println!(" skipping message\n"); + continue; + } + if xcm_message.source % 4 == 0 { // We get the destination for the message let parachain_id = (xcm_message.destination % 3) + 1; @@ -197,8 +219,22 @@ fn run_input(xcm_messages: [XcmMessage; 5]) { } #[cfg(not(fuzzing))] println!(); + // We run integrity tests and try_runtime invariants + [ParaA::execute_with, ParaB::execute_with, ParaC::execute_with].iter().for_each( + |execute_with| { + execute_with(|| { + #[cfg(feature = "try-runtime")] + parachain::AllPalletsWithSystem::try_state(Default::default(), All).unwrap(); + parachain::AllPalletsWithSystem::integrity_test(); + }); + }, + ); + Relay::execute_with(|| { + #[cfg(feature = "try-runtime")] + relay_chain::AllPalletsWithSystem::try_state(Default::default(), All).unwrap(); + relay_chain::AllPalletsWithSystem::integrity_test(); + }); } - Relay::execute_with(|| {}); } fn main() { diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs index 8a89f8cb0e76269272e57bccbf591dd7486e2c1b..f953e54111c2e4cae5058b6a201b1a15e4ceb5f4 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -24,10 +24,11 @@ use frame_support::{ }; use frame_system::EnsureRoot; -use sp_core::{ConstU32, H256}; +use sp_core::ConstU32; use sp_runtime::{ - traits::{Hash, IdentityLookup}, - AccountId32, + generic, + traits::{AccountIdLookup, BlakeTwo256, Hash, IdentifyAccount, Verify}, + MultiAddress, MultiSignature, }; use sp_std::prelude::*; @@ -37,47 +38,37 @@ use polkadot_parachain_primitives::primitives::{ DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, }; use xcm::{latest::prelude::*, VersionedXcm}; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedRateOfFungible, - FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, + FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, + ParentIsPreset, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, }; use xcm_executor::{Config, XcmExecutor}; -pub type AccountId = AccountId32; +pub type TxExtension = (frame_system::CheckNonZeroSender,); + +pub type BlockNumber = u64; +pub type Address = MultiAddress; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; pub type Balance = u128; parameter_types! { - pub const BlockHashCount: u64 = 250; + pub const BlockHashCount: u32 = 250; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = AccountId; - type Lookup = IdentityLookup; + type Lookup = AccountIdLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - 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 = frame_support::traits::ConstU32<16>; } parameter_types! { @@ -99,7 +90,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -133,9 +123,8 @@ parameter_types! { pub const MaxAssetsIntoHolding: u32 = 64; } -#[allow(deprecated)] pub type LocalAssetTransactor = - XcmCurrencyAdapter, LocationToAccountId, AccountId, ()>; + FungibleAdapter, LocationToAccountId, AccountId, ()>; pub type XcmRouter = super::ParachainXcmRouter; pub type Barrier = AllowUnpaidExecutionFrom; @@ -166,6 +155,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } #[frame_support::pallet] @@ -355,8 +345,6 @@ impl pallet_xcm::Config for Runtime { type AdminOrigin = EnsureRoot; } -type Block = frame_system::mocking::MockBlock; - construct_runtime!( pub enum Runtime { diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 143f8974f3e892ccc1ea482509828fe6953feecd..a11e535492cb696c47981de5b5d3a81e297b8762 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -23,8 +23,12 @@ use frame_support::{ }; use frame_system::EnsureRoot; -use sp_core::{ConstU32, H256}; -use sp_runtime::{traits::IdentityLookup, AccountId32}; +use sp_core::ConstU32; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiAddress, MultiSignature, +}; use polkadot_parachain_primitives::primitives::Id as ParaId; use polkadot_runtime_parachains::{ @@ -33,48 +37,37 @@ use polkadot_runtime_parachains::{ origin, shared, }; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, FixedRateOfFungible, - FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, + FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, }; use xcm_executor::{Config, XcmExecutor}; -pub type AccountId = AccountId32; +pub type TxExtension = (frame_system::CheckNonZeroSender,); + +pub type BlockNumber = u64; +pub type Address = MultiAddress; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; pub type Balance = u128; parameter_types! { - pub const BlockHashCount: u64 = 250; + pub const BlockHashCount: u32 = 250; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = AccountId; - type Lookup = IdentityLookup; + type Lookup = sp_runtime::traits::AccountIdLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - 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! { @@ -96,7 +89,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -118,9 +110,8 @@ parameter_types! { pub type SovereignAccountOf = (ChildParachainConvertsVia, AccountId32Aliases); -#[allow(deprecated)] pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + FungibleAdapter, SovereignAccountOf, AccountId, ()>; type LocalOriginConverter = ( SovereignSignedViaLocation, @@ -165,6 +156,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type LocalOriginToLocation = SignedToAccountId32; @@ -202,8 +194,6 @@ parameter_types! { impl origin::Config for Runtime {} -type Block = frame_system::mocking::MockBlock; - 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); diff --git a/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml b/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml index f6bdfeb4877e1dbd5f293f8c0ec864f4f7f1b80d..2561661de1f8408b2ed2808f1f5e41b4286ce97e 100644 --- a/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml +++ b/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml @@ -2,9 +2,11 @@ timeout = 1000 [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 5 needed_approvals = 8 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 5 + [relaychain.genesis.runtimeGenesis.patch.configuration.config.approval_voting_params] max_approval_coalesce_count = 5 diff --git a/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml b/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml index 5d6f299d46133e52c645bd128fbdcc8171490d4e..a2a2621f8426d2e8deb32f524d23e666ab1b5c00 100644 --- a/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml +++ b/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml @@ -2,8 +2,10 @@ timeout = 1000 bootnode = true -[relaychain.genesis.runtimeGenesis.patch.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] max_validators_per_core = 1 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config] needed_approvals = 2 [relaychain] diff --git a/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml b/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml index e2fbec079b1a81ec50054a30ef89d049bb717482..a3bbc82e74ba6d9d5c442742afc2dd18f31c7305 100644 --- a/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml +++ b/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml @@ -3,8 +3,10 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 2 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 group_rotation_frequency = 2 [relaychain] diff --git a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml index bef54cb8ca416fb3210849fb9801c44a31e846a4..858f87b9cfe52c10c9cff9885dbeb57b0dcba2f5 100644 --- a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml +++ b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml @@ -3,10 +3,12 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 7 relay_vrf_modulo_samples = 5 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml b/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml index 69eb0804d8cb70c58fbc70abdb091af3a197d2fb..573ccf961385fa2c721caa0bb67ed97b94deb5a1 100644 --- a/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml +++ b/polkadot/zombienet_tests/functional/0007-dispute-freshly-finalized.toml @@ -2,9 +2,11 @@ timeout = 1000 [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 1 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml b/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml index 1ea385c3a42ee8fdf89b7595151a98c26d9b011b..ea1c93a1403fb837b45a6da01d0920c516e43ae6 100644 --- a/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml +++ b/polkadot/zombienet_tests/functional/0008-dispute-old-finalized.toml @@ -2,9 +2,11 @@ timeout = 1000 [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 1 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/functional/0010-validator-disabling.toml b/polkadot/zombienet_tests/functional/0010-validator-disabling.toml index 6701d60d74d147d517fc55dd3f1253171f6b807f..c9d79c5f8f236918cf409fd684b7d0b8d9792d33 100644 --- a/polkadot/zombienet_tests/functional/0010-validator-disabling.toml +++ b/polkadot/zombienet_tests/functional/0010-validator-disabling.toml @@ -3,8 +3,10 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 2 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 group_rotation_frequency = 10 [relaychain] diff --git a/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.toml b/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.toml new file mode 100644 index 0000000000000000000000000000000000000000..b776622fdce33df2e9a78debc31ee3e62ae4805d --- /dev/null +++ b/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.toml @@ -0,0 +1,54 @@ +[settings] +timeout = 1000 + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +chain = "rococo-local" + +[relaychain.genesis.runtimeGenesis.patch.configuration.config] + needed_approvals = 4 + relay_vrf_modulo_samples = 6 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.async_backing_params] + max_candidate_depth = 3 + allowed_ancestry_len = 2 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + lookahead = 2 + group_rotation_frequency = 4 + + +[relaychain.default_resources] +limits = { memory = "4G", cpu = "2" } +requests = { memory = "2G", cpu = "1" } + + [[relaychain.node_groups]] + name = "alice" + args = [ "-lparachain=debug" ] + count = 12 + +[[parachains]] +id = 2000 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=1" + + [parachains.collator] + name = "collator01" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--pvf-complexity=1", "--parachain-id=2000"] + +[[parachains]] +id = 2001 +cumulus_based = true + + [parachains.collator] + name = "collator02" + image = "{{CUMULUS_IMAGE}}" + command = "polkadot-parachain" + args = ["-lparachain=debug"] + +[types.Header] +number = "u64" +parent_hash = "Hash" +post_state = "Hash" \ No newline at end of file diff --git a/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.zndsl b/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..0d01af82833e36afd3c38b2e00e9d604ace46797 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0011-async-backing-6-seconds-rate.zndsl @@ -0,0 +1,20 @@ +Description: Test we are producing blocks at 6 seconds clip +Network: ./0011-async-backing-6-seconds-rate.toml +Creds: config + +# Check authority status. +alice: reports node_roles is 4 + +# Ensure parachains are registered. +alice: parachain 2000 is registered within 60 seconds +alice: parachain 2001 is registered within 60 seconds + +# Ensure parachains made progress. +alice: reports substrate_block_height{status="finalized"} is at least 10 within 100 seconds + +# This parachains should produce blocks at 6s clip, let's assume an 8s rate, allowing for +# some slots to be missed on slower machines +alice: parachain 2000 block height is at least 30 within 240 seconds +# This should already have produced the needed blocks +alice: parachain 2001 block height is at least 30 within 6 seconds + diff --git a/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.toml b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.toml new file mode 100644 index 0000000000000000000000000000000000000000..9b3576eaa3c212bd9490095c12ae4bca65f6fa54 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.toml @@ -0,0 +1,40 @@ +[settings] +timeout = 1000 +bootnode = true + +[relaychain.genesis.runtimeGenesis.patch.configuration.config] + needed_approvals = 4 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 2 + num_cores = 2 + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +chain = "rococo-local" +default_command = "polkadot" + +[relaychain.default_resources] +limits = { memory = "4G", cpu = "2" } +requests = { memory = "2G", cpu = "1" } + + [[relaychain.nodes]] + name = "alice" + validator = "true" + + [[relaychain.node_groups]] + name = "validator" + count = 3 + args = [ "-lparachain=debug,runtime=debug"] + +[[parachains]] +id = 2000 +default_command = "polkadot-parachain" +add_to_genesis = false +register_para = true +onboard_as_parachain = false + + [parachains.collator] + name = "collator2000" + command = "polkadot-parachain" + args = [ "-lparachain=debug" ] diff --git a/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.zndsl b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..edc87c802a0965b6fd685db44ccbf50e807f1ed3 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0012-elastic-scaling-mvp.zndsl @@ -0,0 +1,19 @@ +Description: Test that a paraid acquiring multiple cores does not brick itself if ElasticScalingMVP feature is enabled in genesis +Network: ./0012-elastic-scaling-mvp.toml +Creds: config + +# Check authority status. +validator: reports node_roles is 4 + +validator: reports substrate_block_height{status="finalized"} is at least 10 within 100 seconds + +# Ensure parachain was able to make progress. +validator: parachain 2000 block height is at least 10 within 200 seconds + +# Register the second core assigned to this parachain. +alice: js-script ./0012-register-para.js return is 0 within 600 seconds + +validator: reports substrate_block_height{status="finalized"} is at least 35 within 100 seconds + +# Ensure parachain is now making progress. +validator: parachain 2000 block height is at least 30 within 200 seconds diff --git a/polkadot/zombienet_tests/functional/0012-register-para.js b/polkadot/zombienet_tests/functional/0012-register-para.js new file mode 100644 index 0000000000000000000000000000000000000000..25c7e4f5ffddcf087edab4df37e696af92fe6d3f --- /dev/null +++ b/polkadot/zombienet_tests/functional/0012-register-para.js @@ -0,0 +1,37 @@ +async function run(nodeName, networkInfo, _jsArgs) { + const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + + await zombie.util.cryptoWaitReady(); + + // account to submit tx + const keyring = new zombie.Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + + await new Promise(async (resolve, reject) => { + const unsub = await api.tx.sudo + .sudo(api.tx.coretime.assignCore(0, 35, [[{ task: 2000 }, 57600]], null)) + .signAndSend(alice, ({ status, isError }) => { + if (status.isInBlock) { + console.log( + `Transaction included at blockhash ${status.asInBlock}`, + ); + } else if (status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${status.asFinalized}`, + ); + unsub(); + return resolve(); + } else if (isError) { + console.log(`Transaction error`); + reject(`Transaction error`); + } + }); + }); + + + + return 0; +} + +module.exports = { run }; diff --git a/polkadot/zombienet_tests/misc/0001-paritydb.toml b/polkadot/zombienet_tests/misc/0001-paritydb.toml index 399f848d3ac49e7e9950617c170c13d5c63593dd..b3ce2081b1119ec237c1b3d6e14e1e4aed8322c5 100644 --- a/polkadot/zombienet_tests/misc/0001-paritydb.toml +++ b/polkadot/zombienet_tests/misc/0001-paritydb.toml @@ -3,9 +3,11 @@ timeout = 1000 bootnode = true [relaychain.genesis.runtimeGenesis.patch.configuration.config] - max_validators_per_core = 1 needed_approvals = 3 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" diff --git a/polkadot/zombienet_tests/misc/0001-paritydb.zndsl b/polkadot/zombienet_tests/misc/0001-paritydb.zndsl index 4a22311de764c1936be2f5812ffb32a7471b5f8e..e0260cb9fdde18882bd9e1cb1ec88a12cdb7a3b9 100644 --- a/polkadot/zombienet_tests/misc/0001-paritydb.zndsl +++ b/polkadot/zombienet_tests/misc/0001-paritydb.zndsl @@ -31,28 +31,28 @@ validator-0: parachain 2008 is registered validator-0: parachain 2009 is registered # Ensure parachains made some progress. -validator-0: parachain 2000 block height is at least 3 within 30 seconds -validator-0: parachain 2001 block height is at least 3 within 30 seconds -validator-0: parachain 2002 block height is at least 3 within 30 seconds -validator-0: parachain 2003 block height is at least 3 within 30 seconds -validator-0: parachain 2004 block height is at least 3 within 30 seconds -validator-0: parachain 2005 block height is at least 3 within 30 seconds -validator-0: parachain 2006 block height is at least 3 within 30 seconds -validator-0: parachain 2007 block height is at least 3 within 30 seconds -validator-0: parachain 2008 block height is at least 3 within 30 seconds -validator-0: parachain 2009 block height is at least 3 within 30 seconds +validator-0: parachain 2000 block height is at least 3 within 60 seconds +validator-0: parachain 2001 block height is at least 3 within 60 seconds +validator-0: parachain 2002 block height is at least 3 within 60 seconds +validator-0: parachain 2003 block height is at least 3 within 60 seconds +validator-0: parachain 2004 block height is at least 3 within 60 seconds +validator-0: parachain 2005 block height is at least 3 within 60 seconds +validator-0: parachain 2006 block height is at least 3 within 60 seconds +validator-0: parachain 2007 block height is at least 3 within 60 seconds +validator-0: parachain 2008 block height is at least 3 within 60 seconds +validator-0: parachain 2009 block height is at least 3 within 60 seconds # Check lag - approval -validator-0: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-1: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-2: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-3: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-4: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-5: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-6: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-7: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-8: reports polkadot_parachain_approval_checking_finality_lag is 0 -validator-9: reports polkadot_parachain_approval_checking_finality_lag is 0 +validator-0: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-1: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-2: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-3: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-4: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-5: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-6: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-7: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-8: reports polkadot_parachain_approval_checking_finality_lag <= 1 +validator-9: reports polkadot_parachain_approval_checking_finality_lag <= 1 # Check lag - dispute conclusion validator-0: reports polkadot_parachain_candidate_disputes_total is 0 diff --git a/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl b/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl index db0a60ac1df617e5c89dc6a1385c4c106c1ead05..5fe1b2ad2f1a7589a28f8b1b8da05d6f35d3d59b 100644 --- a/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl +++ b/polkadot/zombienet_tests/misc/0002-upgrade-node.zndsl @@ -10,9 +10,8 @@ dave: parachain 2001 block height is at least 10 within 200 seconds # POLKADOT_PR_ARTIFACTS_URL=https://gitlab.parity.io/parity/mirrors/polkadot/-/jobs/1842869/artifacts/raw/artifacts # with the version of polkadot you want to download. -# avg 30s in our infra -alice: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 60 seconds -bob: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 60 seconds +alice: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 240 seconds +bob: run ./0002-download-polkadot-from-pr.sh with "{{POLKADOT_PR_ARTIFACTS_URL}}" within 240 seconds # update the cmd to add the flag '--insecure-validator-i-know-what-i-do' # once the base image include the version with this flag we can remove this logic. alice: run ./0002-update-cmd.sh within 60 seconds diff --git a/polkadot/zombienet_tests/smoke/0004-configure-broker.js b/polkadot/zombienet_tests/smoke/0004-configure-broker.js index 889861f5c52e3d8c6a56ca44bc0b2d378d153011..52a32b8a7c802e33ed41acceb155024be2da7ca0 100644 --- a/polkadot/zombienet_tests/smoke/0004-configure-broker.js +++ b/polkadot/zombienet_tests/smoke/0004-configure-broker.js @@ -54,9 +54,11 @@ async function run(nodeName, networkInfo, _jsArgs) { unsub(); return resolve(); } else if (result.isError) { - console.log(`Transaction Error`); + // Probably happens because of: https://github.com/paritytech/polkadot-sdk/issues/1202. + console.log(`Transaction error`); + // We ignore the error because it is very likely misleading, because of the issue mentioned above. unsub(); - return reject(); + return resolve(); } }); }); diff --git a/prdoc/1.4.0/pr_1246.prdoc b/prdoc/1.4.0/pr_1246.prdoc index a4d270c45cb5915760ddfbd60e5e4b3b7c08cd4a..3b5c2017f22acfba25471595c50015ae14c3e5cd 100644 --- a/prdoc/1.4.0/pr_1246.prdoc +++ b/prdoc/1.4.0/pr_1246.prdoc @@ -11,7 +11,7 @@ migrations: description: "Messages from the DMP dispatch queue will be moved over to the MQ pallet via `on_initialize`. This happens over multiple blocks and emits a `Completed` event at the end. The pallet can be un-deployed and deleted afterwards. Note that the migration reverses the order of messages, which should be acceptable as a one-off." crates: - - name: cumulus_pallet_xcmp_queue + - name: cumulus-pallet-xcmp-queue note: Pallet config must be altered according to the MR description. host_functions: [] diff --git a/prdoc/1.6.0/pr_2689.prdoc b/prdoc/1.6.0/pr_2689.prdoc index 847c3e8026cef9dfdf63f044a1b773be85b12921..5d3081e3a4ce71d872c8eb8a96fda5b2e273b684 100644 --- a/prdoc/1.6.0/pr_2689.prdoc +++ b/prdoc/1.6.0/pr_2689.prdoc @@ -1,7 +1,7 @@ # Schema: Parity PR Documentation Schema (prdoc) # See doc at https://github.com/paritytech/prdoc -title: BEEFY: Support compatibility with Warp Sync - Allow Warp Sync for Validators +title: "BEEFY: Support compatibility with Warp Sync - Allow Warp Sync for Validators" doc: - audience: Node Operator diff --git a/prdoc/1.6.0/pr_2771.prdoc b/prdoc/1.6.0/pr_2771.prdoc index 1b49162e4392ba1ad1a77d61e5b2289474b0ffbe..50fb99556ecddf39adbcece77d9b83e5d98651b4 100644 --- a/prdoc/1.6.0/pr_2771.prdoc +++ b/prdoc/1.6.0/pr_2771.prdoc @@ -6,4 +6,4 @@ doc: Enable better req-response protocol versioning, by allowing for fallback requests on different protocols. crates: - - name: sc_network + - name: sc-network diff --git a/prdoc/1.7.0/pr_1222.prdoc b/prdoc/1.7.0/pr_1222.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..82eb341649bc27d6dbd03fde384587414d12b990 --- /dev/null +++ b/prdoc/1.7.0/pr_1222.prdoc @@ -0,0 +1,33 @@ +title: Transactional processing for XCM + +doc: + - audience: Runtime Dev + description: | + Transactional processing was introduced for certain XCM instructions. They are: + - WithdrawAsset + - ReserveAssetDeposited + - TransferAsset + - TransferReserveAsset + - ReceiveTeleportedAsset + - DepositAsset + - DepositReserveAsset + - InitiateReserveWithdraw + - InitiateTeleport + - BuyExecution + - ClaimAsset + - ExportMessage + - LockAsset + - UnlockAsset + - RequestUnlock + Developers must specify a `TransactionalProcessor` when configuring their XCM executor. + FRAME-based runtimes would simply need to configure it with `FrameTransactionalProcessor` to + enable transactional processing. To disable transactional processing of XCMs, `()` may also be + specified as the type for `TransactionalProcessor`. + For runtimes that are not FRAME-based but would like to still harness transactional processing + of XCMs, a type implementing the `ProcessTransaction` trait must be specified as the type for + `TransactionalProcessor`. This trait is for the purpose of connecting the chain's runtime + transactional processor with the XCM executor -- any implementation of `ProcessTransaction` is + possible to be assigned as the `TransactionalProcessor` for the XCM executor. + +crates: + - name: staging-xcm-executor diff --git a/prdoc/pr_1230.prdoc b/prdoc/1.7.0/pr_1230.prdoc similarity index 100% rename from prdoc/pr_1230.prdoc rename to prdoc/1.7.0/pr_1230.prdoc diff --git a/prdoc/pr_1296.prdoc b/prdoc/1.7.0/pr_1296.prdoc similarity index 100% rename from prdoc/pr_1296.prdoc rename to prdoc/1.7.0/pr_1296.prdoc diff --git a/prdoc/1.7.0/pr_1313.prdoc b/prdoc/1.7.0/pr_1313.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0ee91da41a9abe3251a041e503377660eb448002 --- /dev/null +++ b/prdoc/1.7.0/pr_1313.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: backpressured JSON-RPC server (upgrade jsonrpsee) + +doc: + - audience: Node Operator + description: | + Modifies the jsonrpc server to be "backpressured" and it's possible to configure + how many messages can be "buffered" via the CLI `--rpc_message_buffer_capacity`. + + Major changes in this PR: + - The subscriptions are now bounded and if subscription can't keep up with the server it is dropped + - CLI: add parameter to configure the jsonrpc server bounded message buffer (default is 64) + - Add our own subscription helper to deal with the unbounded streams in substrate + +crates: +- name: sc-rpc-server diff --git a/prdoc/pr_1845.prdoc b/prdoc/1.7.0/pr_1845.prdoc similarity index 100% rename from prdoc/pr_1845.prdoc rename to prdoc/1.7.0/pr_1845.prdoc diff --git a/prdoc/pr_1871.prdoc b/prdoc/1.7.0/pr_1871.prdoc similarity index 100% rename from prdoc/pr_1871.prdoc rename to prdoc/1.7.0/pr_1871.prdoc diff --git a/prdoc/pr_2125.prdoc b/prdoc/1.7.0/pr_2125.prdoc similarity index 100% rename from prdoc/pr_2125.prdoc rename to prdoc/1.7.0/pr_2125.prdoc diff --git a/prdoc/pr_2467.prdoc b/prdoc/1.7.0/pr_2467.prdoc similarity index 100% rename from prdoc/pr_2467.prdoc rename to prdoc/1.7.0/pr_2467.prdoc diff --git a/prdoc/pr_2477-use-clone-instead-of-fork-on-pvf.prdoc b/prdoc/1.7.0/pr_2477-use-clone-instead-of-fork-on-pvf.prdoc similarity index 100% rename from prdoc/pr_2477-use-clone-instead-of-fork-on-pvf.prdoc rename to prdoc/1.7.0/pr_2477-use-clone-instead-of-fork-on-pvf.prdoc diff --git a/prdoc/1.7.0/pr_2587.prdoc b/prdoc/1.7.0/pr_2587.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1ea23d5cca7ba88df9fb2a106fbff11bd7367716 --- /dev/null +++ b/prdoc/1.7.0/pr_2587.prdoc @@ -0,0 +1,9 @@ +title: Allow fellowship members to swap their AccountIds. + +doc: + - audience: Runtime User + description: | + Add a `exchange_member` extrinsic to the `ranked-collective` pallet that allows to vote on + swapping the AccountId of a member. This can be used to recover lost access to a collective. + +crates: [ ] diff --git a/prdoc/1.7.0/pr_2657.prdoc b/prdoc/1.7.0/pr_2657.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9c5edf4ceee633c00583248fe380aa5f66a59358 --- /dev/null +++ b/prdoc/1.7.0/pr_2657.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[frame] `#[pallet::composite_enum]` improved variant count handling + removed `pallet_balances`'s `MaxHolds` config" + +doc: + - audience: Runtime Dev + description: | + The implementation of the `VariantCount` trait for aggregate composite enums, + such as `RuntimeHoldReason` and `RuntimeFreezeReason`, has been fixed. + It is now calculated as the sum of `VariantCount::VARIANT_COUNT` for all corresponding `#[pallet::composite_enum]`. + The `Balances` pallet's `Config` item `type MaxHolds` has been removed, + and `type Holds` is now bound to the variant count of the composite enum `RuntimeHoldReason`. + Consequently, the runtime does not need to consider setting the correct value for `MaxHolds`. + + notes: + - Remove `type MaxHolds` from the `impl pallet_balances::Config for Runtime` in the runtime. + - When holds are expected to be used, ensure that `type RuntimeHoldReason = RuntimeHoldReason` is set for `impl pallet_balances::Config for Runtime`. + +crates: + - name: pallet-balances diff --git a/prdoc/pr_2796.prdoc b/prdoc/1.7.0/pr_2796.prdoc similarity index 80% rename from prdoc/pr_2796.prdoc rename to prdoc/1.7.0/pr_2796.prdoc index 15cb005d286f9b5f57eac275561f0892cc246509..0530c9ad7140f9fc5d0b9b41129910b09f074c48 100644 --- a/prdoc/pr_2796.prdoc +++ b/prdoc/1.7.0/pr_2796.prdoc @@ -1,4 +1,4 @@ -title: Rococo and Westend Asset-Hub: XCM Transfers with Pallet-Uniques +title: "Rococo and Westend Asset-Hub: XCM Transfers with Pallet-Uniques" doc: - audience: Runtime User diff --git a/prdoc/pr_2826.prdoc b/prdoc/1.7.0/pr_2826.prdoc similarity index 100% rename from prdoc/pr_2826.prdoc rename to prdoc/1.7.0/pr_2826.prdoc diff --git a/prdoc/pr_2889.prdoc b/prdoc/1.7.0/pr_2889.prdoc similarity index 100% rename from prdoc/pr_2889.prdoc rename to prdoc/1.7.0/pr_2889.prdoc diff --git a/prdoc/pr_2920.prdoc b/prdoc/1.7.0/pr_2920.prdoc similarity index 74% rename from prdoc/pr_2920.prdoc rename to prdoc/1.7.0/pr_2920.prdoc index 7745838ab5c0c08cb499f3691fa0d1dd712e4106..d41227ee8b8a4abb8f9c4bf7c2d1946278e89ecd 100644 --- a/prdoc/pr_2920.prdoc +++ b/prdoc/1.7.0/pr_2920.prdoc @@ -1,11 +1,12 @@ # 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: Contracts: Stabilize sr25519_verify host function +title: "Contracts: Stabilize sr25519_verify host function" doc: - audience: Runtime Dev description: | Removed the `#[unstable]` attrribute on `sr25519_verify` host function. -crates: ["pallet-contracts"] +crates: + - name: "pallet-contracts" diff --git a/prdoc/1.7.0/pr_2924.prdoc b/prdoc/1.7.0/pr_2924.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f22c5365b46de1f6cc6a55564bc61dabb5402997 --- /dev/null +++ b/prdoc/1.7.0/pr_2924.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 NonFungibleAdapter + +doc: + - audience: Runtime Dev + description: | + Introduces a new adapter, `NonFungibleAdapter`, to work with `frame_support::traits::tokens::nonfungible` + through XCM. + +crates: + - name: staging-xcm-builder diff --git a/prdoc/1.7.0/pr_2942.prdoc b/prdoc/1.7.0/pr_2942.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..caa276c099b395371025a23aa76734b186a1675e --- /dev/null +++ b/prdoc/1.7.0/pr_2942.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 pallet-nomination-pools v6 to v7 migration + +doc: + - audience: Node Dev + description: | + Restores the behaviour of the nomination pools `V6ToV7` migration so that it still works when + the pallet will be upgraded to V8 afterwards. + +crates: + - name: "pallet-nomination-pools" diff --git a/prdoc/1.7.0/pr_2970.prdoc b/prdoc/1.7.0/pr_2970.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1db8f7bb334d5b9106f4594f61ccef780db2a313 --- /dev/null +++ b/prdoc/1.7.0/pr_2970.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 `availability-distribution` and `biftield-distribution` subsystem benchmark + +doc: + - audience: Node Dev + description: | + The new subsystem benchmark test objective (`DataAvailabilityWrite`) is designed to stress + test the part of the pipeline that takes as input a backed candidate and then distributes + it as erasure coded chunks to other validators. The test pulls in the `av-store`, + `bitfield-distribution` and `availability-distribution` subsystems while the whole network and rest + of the node subsystems are emulated. + +crates: [ ] diff --git a/prdoc/1.7.0/pr_3001.prdoc b/prdoc/1.7.0/pr_3001.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..32f5a6d05af55fa842584b4d715510ffc23fd964 --- /dev/null +++ b/prdoc/1.7.0/pr_3001.prdoc @@ -0,0 +1,9 @@ +title: "Introduce `Balances::force_adjust_total_issuance`" + +doc: + - audience: Runtime Dev + description: | + Introduce a root extrinsic to forcefully adjust the Total Issuance. Should only be used to fix historic errors. + +crates: + - name: "pallet-balances" diff --git a/prdoc/pr_3009.prdoc b/prdoc/1.7.0/pr_3009.prdoc similarity index 100% rename from prdoc/pr_3009.prdoc rename to prdoc/1.7.0/pr_3009.prdoc diff --git a/prdoc/1.7.0/pr_3020.prdoc b/prdoc/1.7.0/pr_3020.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b605a2f2f0ff5f22fdd3ce0edcb75ff5f1bf1d45 --- /dev/null +++ b/prdoc/1.7.0/pr_3020.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: Para registration deposit covering max code size + +doc: + - audience: Runtime User + description: | + With this PR all newly registered parachains must pay a deposit equivalent to the cost of + registering validation code of the maximum size. Consequently, they can upgrade their code + to the maximum size at any point without additional cost. + +crates: + - name: polkadot-runtime-common diff --git a/prdoc/1.7.0/pr_3029.prdoc b/prdoc/1.7.0/pr_3029.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f17bd8be18fc7e694c35e17956b2a94588c84883 --- /dev/null +++ b/prdoc/1.7.0/pr_3029.prdoc @@ -0,0 +1,10 @@ +title: "Snowbridge Ethereum Deneb fork preparation" + +doc: + - audience: Runtime Dev + description: | + Changes the Rococo runtime Ethereum fork config to include the Deneb hard-fork. Updates the Snowbridge + subtree with preparation for Deneb. Removes the `beacon-minimal-spec` feature. + +crates: + - name: bridge-hub-rococo-runtime diff --git a/prdoc/1.7.0/pr_3040.prdoc b/prdoc/1.7.0/pr_3040.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..01f3d731c69c8757a5e7e565c66f7ca2ae35bcb7 --- /dev/null +++ b/prdoc/1.7.0/pr_3040.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: Rename transaction to transactionWatch + +doc: + - audience: Node Dev + description: | + Renamed `transaction_unstable_submitAndWatch` to `transactionWatch_unstable_submitAndWatch`, + `transaction_unstable_watchEvent` to `transactionWatch_unstable_watchEvent` and + `transaction_unstable_unwatch` to `transactionWatch_unstable_unwatch`. + +crates: +- name: sc-rpc-spec-v2 diff --git a/prdoc/1.7.0/pr_3057.prdoc b/prdoc/1.7.0/pr_3057.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2e0da0078cde89b0beb15f2d6c63990a0e854de3 --- /dev/null +++ b/prdoc/1.7.0/pr_3057.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: Serialize/Deserialize trait implemented in no-std for XCM assets types + +doc: + - audience: Runtime Dev + description: | + Serialize/Deserialize trait implemented in no-std for XCM v3 and v4 + assets types + +crates: + - name: staging-xcm diff --git a/prdoc/1.7.0/pr_3061.prdoc b/prdoc/1.7.0/pr_3061.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..977a9b6fd799280da528832b8cb4309e4b178664 --- /dev/null +++ b/prdoc/1.7.0/pr_3061.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: Do not run unneeded subsystems on collator and its alongside node + +doc: + - audience: Node Dev + description: | + Optimizes overseer building strategy to only include subsystems needed to run the given + type of node. Reduces overseer overhead and also solves the problem with unused subsystems + getting stalled from time to time. + +crates: + - name: polkadot-overseer + - name: polkadot-service diff --git a/prdoc/1.7.0/pr_3077.prdoc b/prdoc/1.7.0/pr_3077.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d82ecffb937f88c0c9615a48d9d016564655e607 --- /dev/null +++ b/prdoc/1.7.0/pr_3077.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: Enable cross-chain Coretime region transfers on Rococo Coretime chain + +doc: + - audience: Runtime User + description: | + This PR allows Coretime regions to be cross-chain transferred from the Rococo Coretime chain. + +crates: + - name: pallet-broker + - name: coretime-rococo-runtime diff --git a/prdoc/1.7.0/pr_3108.prdoc b/prdoc/1.7.0/pr_3108.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3f3259b1004f7498f84606b95b48aa90cd47d0e7 --- /dev/null +++ b/prdoc/1.7.0/pr_3108.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: revert paritytech/polkadot#6577 & related changes + +doc: + - audience: Node Dev + description: | + Moves BEEFY related pallets behind `session_pallet` for Rococo and Westend runtimes. + Effects that each `MmrLeaf` in the MMR generated by `mmr_pallet` for `block` references the `next_auth_set` of `block` and not `block`. + Breaking change for proofs generated by `mmr_generateProof` + - audience: Runtime Dev + description: | + Moves BEEFY related pallets behind `session_pallet` for Rococo and Westend runtimes. + Effects that each `MmrLeaf` in the MMR generated by `mmr_pallet` for `block` references the `next_auth_set` of `block` and not `block`. + + +crates: [] diff --git a/prdoc/1.7.0/pr_3110.prdoc b/prdoc/1.7.0/pr_3110.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..06c0fafea04f2f8ef88b648889336b3abe598d71 --- /dev/null +++ b/prdoc/1.7.0/pr_3110.prdoc @@ -0,0 +1,12 @@ +title: Nomination pools Fix payout destination in permissionless unbond + +doc: + - audience: Runtime Dev + description: | + This PR fixes an issue whereby when a nomination pool allowed for permissionless unbonding of + funds, the implicit claimed rewards were mistakenly sent to the caller of the `unbond`, and + not the actual member. A nomination pool only allows permissionless unbonding when its state + was set into `Destroying` by the operator + +crates: + - name: pallet-nomination-pools diff --git a/prdoc/1.7.0/pr_3156.prdoc b/prdoc/1.7.0/pr_3156.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c456b0a67332b55f06ff6f859e93202fb49d1e77 --- /dev/null +++ b/prdoc/1.7.0/pr_3156.prdoc @@ -0,0 +1,13 @@ +title: "Adapt core-fellowship and salary pallets for swapped members" + +doc: + - audience: Runtime Dev + description: | + The ranked-collective pallet got the ability to swap members but the core-fellowship and + salary pallets would not be notified of this change. This MR adds `MemberSwappedHandler` to + the collective that is implemented by both pallets. + +crates: + - name: "pallet-ranked-collective" + - name: "pallet-core-fellowship" + - name: "pallet-salary" diff --git a/prdoc/1.7.0/pr_3228.prdoc b/prdoc/1.7.0/pr_3228.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..42a4893af3620057bbb4351459f26bebdefe79f9 --- /dev/null +++ b/prdoc/1.7.0/pr_3228.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: "[pallet_xcm] Forgotten migration to XCMv4 + added `try-state` to the `pallet_xcm`" + +doc: + - audience: Runtime Dev + description: | + The current release includes the new `XCMv4`, so the runtimes must incorporate + a migration `pallet_xcm::migration::MigrateToLatestXcmVersion` to ensure + proper data migration in `pallet_xcm`. + +crates: + - name: pallet-xcm diff --git a/prdoc/1.8.0/pr_1660.prdoc b/prdoc/1.8.0/pr_1660.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dda38c9549595526bb438e9358ed792c6bc1406c --- /dev/null +++ b/prdoc/1.8.0/pr_1660.prdoc @@ -0,0 +1,12 @@ +title: Implements a percentage cap on staking rewards from era inflation + +doc: + - audience: Runtime Dev + description: | + The `pallet-staking` exposes a new perbill configuration, `MaxStakersRewards`, which caps the + amount of era inflation that is distributed to the stakers. The remainder of the era + inflation is minted directly into `T::RewardRemainder` account. This allows the runtime to be + configured to assign a minimum inflation value per era to a specific account (e.g. treasury). + +crates: + - name: pallet-staking diff --git a/prdoc/1.8.0/pr_2061.prdoc b/prdoc/1.8.0/pr_2061.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..07df11ca0543a5c90a60996f73e96b059a7e8fe4 --- /dev/null +++ b/prdoc/1.8.0/pr_2061.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: Add Parameters Pallet + +doc: + - audience: Runtime Dev + description: | + Adds `pallet-parameters` that allows to have parameters for pallet configs that dynamically change at runtime. Allows to be permissioned on a per-key basis and is compatible with ORML macros. + +crates: + - name: "pallet-parameters" + - name: "frame-support" + - name: "frame-support-procedural" diff --git a/prdoc/1.8.0/pr_2290.prdoc b/prdoc/1.8.0/pr_2290.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9f0476e9152641fa0bacd3e71af77ca92bcd142f --- /dev/null +++ b/prdoc/1.8.0/pr_2290.prdoc @@ -0,0 +1,10 @@ +title: im-online pallet offcain storage cleanup + +doc: + - audience: Runtime Dev + description: | + Adds a function `clear_offchain_storage` to `pallet-im-online`. This function can be used + after the pallet was removed to clear its offchain storage. + +crates: + - name: pallet-im-online diff --git a/prdoc/1.8.0/pr_2903.prdoc b/prdoc/1.8.0/pr_2903.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dd22fa0eea3754436e1dca0a9657eb1be8fb8151 --- /dev/null +++ b/prdoc/1.8.0/pr_2903.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: "Implement `ConversionToAssetBalance` in asset-rate" + +doc: + - audience: Runtime Dev + description: | + Implements the `ConversionToAssetBalance` trait to the asset-rate pallet. + + Previously only the `ConversionFromAssetBalance` trait was implemented, which would allow to convert an asset balance into the corresponding native balance. + + The `ConversionToAssetBalance` allows to use pallet-asset-rate, e.g., as a mechanism to charge XCM fees in an asset that is not the native. +crates: [ ] + diff --git a/prdoc/1.8.0/pr_2949-async-backing-on-all-testnet-system-chains.prdoc b/prdoc/1.8.0/pr_2949-async-backing-on-all-testnet-system-chains.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..265940815bad021bf28d549ea9a23e2fd8ce584c --- /dev/null +++ b/prdoc/1.8.0/pr_2949-async-backing-on-all-testnet-system-chains.prdoc @@ -0,0 +1,23 @@ +title: Enable async backing on all testnet system chains + +doc: + - audience: Runtime User + description: | + Async backing has been enabled on all testnet system chains: asset-hub-westend, + bridge-hub-westend, bridge-hub-rococo, collectives-westend, contracts-rococo, + coretime-westend, coretime-rococo, people-westend, and people-rococo. These now target 6s + block times. + For the running coretime chains, that requires updating the configuration after the runtime + upgrade but before the end of the current region. + +crates: + - name: asset-hub-westend-runtime + - name: bridge-hub-westend-runtime + - name: bridge-hub-rococo-runtime + - name: collectives-westend-runtime + - name: contracts-rococo-runtime + - name: coretime-westend-runtime + - name: coretime-rococo-runtime + - name: people-westend-runtime + - name: people-rococo-runtime + - name: polkadot-parachain-bin diff --git a/prdoc/1.8.0/pr_3007.prdoc b/prdoc/1.8.0/pr_3007.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..17870469a21988f46a8338599f53562e4cd9acff --- /dev/null +++ b/prdoc/1.8.0/pr_3007.prdoc @@ -0,0 +1,16 @@ +title: Try State Hook for Ranked Collective. + +doc: + - audience: Runtime User + description: | + Invariants for storage items in the ranked collective pallet. Enforces the following Invariants: + 1. Total number of `Members` in storage should be >= [`MemberIndex`] of a [`Rank`] in `MemberCount`. + 2. `Rank` in Members should be in `MemberCount`. + 3.`Sum` of `MemberCount` index should be the same as the sum of all the index attained for + rank possessed by `Members` + 4. `Member` in storage of `IdToIndex` should be the same as `Member` in `IndexToId`. + 5. `Rank` in `IdToIndex` should be the same as the the `Rank` in `IndexToId`. + 6. `Rank` of the member `who` in `IdToIndex` should be the same as the `Rank` of + the member `who` in `Members` +crates: +- name: pallet-ranked-collective diff --git a/prdoc/1.8.0/pr_3052.prdoc b/prdoc/1.8.0/pr_3052.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..09f3cf59e4da467e277aeb3d36c737cd8e6a2a85 --- /dev/null +++ b/prdoc/1.8.0/pr_3052.prdoc @@ -0,0 +1,15 @@ +title: "Fixes a scenario where a nomination pool's `TotalValueLocked` is out of sync due to staking's implicit withdraw" + +doc: + - audience: Runtime Dev + description: | + The nomination pools pallet `TotalValueLocked` may get out of sync if the staking pallet + does implicit withdrawal of unlocking chunks belonging to a bonded pool stash. This fix + is based on a new method on the `OnStakingUpdate` traits, `on_withdraw`, which allows the + nomination pools pallet to adjust the `TotalValueLocked` every time there is an implicit or + explicit withdrawal from a bonded pool's stash. + +crates: + - name: "pallet-nomination-pools" + - name: "pallet-staking" + - name: "sp-staking" diff --git a/prdoc/1.8.0/pr_3060.prdoc b/prdoc/1.8.0/pr_3060.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4cd6674ebb2e91b03ed0f4c5d1626bae58c82286 --- /dev/null +++ b/prdoc/1.8.0/pr_3060.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 mechanics to `pallet-scheduler` + +doc: + - audience: Runtime Dev + description: | + This PR adds retry mechanics to pallet-scheduler, as described in the issue above. + Users can now set a retry configuration for a task so that, in case its scheduled run fails, it will be retried after a number of blocks, for a specified number of times or until it succeeds. + If a retried task runs successfully before running out of retries, its remaining retry counter will be reset to the initial value. If a retried task runs out of retries, it will be removed from the schedule. + Tasks which need to be scheduled for a retry are still subject to weight metering and agenda space, same as a regular task. Periodic tasks will have their periodic schedule put on hold while the task is retrying. + +crates: + - name: pallet-scheduler diff --git a/prdoc/1.8.0/pr_3079.prdoc b/prdoc/1.8.0/pr_3079.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c745c1ffbfe5775f205dc5510a637dbfd4e6f56c --- /dev/null +++ b/prdoc/1.8.0/pr_3079.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: Implement transaction_unstable_broadcast and transaction_unstable_stop + +doc: + - audience: Node Dev + description: | + A new RPC class is added to handle transactions. The `transaction_unstable_broadcast` broadcasts + the provided transaction to the peers of the node, until the `transaction_unstable_stop` is called. + The APIs are marked as unstable and subject to change in the future. + To know if the transaction was added to the chain, users can decode the bodies of announced finalized blocks. + This is a low-level approach for `transactionWatch_unstable_submitAndWatch`. + +crates: [ ] diff --git a/prdoc/1.8.0/pr_3154.prdoc b/prdoc/1.8.0/pr_3154.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2dfc1b27810cf75683a374cd15f4bcb7b11105f0 --- /dev/null +++ b/prdoc/1.8.0/pr_3154.prdoc @@ -0,0 +1,9 @@ +title: "Contracts: Stabilize caller_is_root API" + +doc: + - audience: Runtime Dev + description: | + Removed the `#[unstable]` attrribute on `caller_is_root` host function. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.8.0/pr_3160.prdoc b/prdoc/1.8.0/pr_3160.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..22305b6635aa505f4416b88d5b9f83ebc6163f28 --- /dev/null +++ b/prdoc/1.8.0/pr_3160.prdoc @@ -0,0 +1,12 @@ +title: "prospective-parachains: allow requesting a chain of backable candidates" + +topic: Node + +doc: + - audience: Node Dev + description: | + Enable requesting a chain of multiple backable candidates. Will be used by the provisioner + to build paras inherent data for elastic scaling. + +crates: + - name: polkadot-node-core-prospective-parachains diff --git a/prdoc/1.8.0/pr_3166.prdoc b/prdoc/1.8.0/pr_3166.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..adf7f50e4e965325cfc2cbc25d9f66467483f802 --- /dev/null +++ b/prdoc/1.8.0/pr_3166.prdoc @@ -0,0 +1,9 @@ +title: Expose internal functions used by `spawn_tasks` + +doc: + - audience: Node Dev + description: | + This allows to build a custom version of `spawn_tasks` with less copy-paste required + +crates: + - name: sc-service diff --git a/prdoc/1.8.0/pr_3184.prdoc b/prdoc/1.8.0/pr_3184.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..36ba92bcf5a79ac75934f4fa08e1e02d78b6175e --- /dev/null +++ b/prdoc/1.8.0/pr_3184.prdoc @@ -0,0 +1,10 @@ +title: "Contracts: Remove no longer enforced limits from the Schedule" + +doc: + - audience: Runtime Dev + description: | + The limits are no longer in use and do nothing. Every builder overwritting them + can just adapt their code to remove them without any consequence. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.8.0/pr_3212.prdoc b/prdoc/1.8.0/pr_3212.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a9de1e28cdfe2294948fb3c954b9b417c09d149b --- /dev/null +++ b/prdoc/1.8.0/pr_3212.prdoc @@ -0,0 +1,9 @@ +title: "Ranked collective introduce `Add` and `Remove` origins" + +doc: + - audience: Runtime Dev + description: | + Add two new origins to the ranked-collective pallet. One to add new members and one to remove members, named `AddOrigin` and `RemoveOrigin` respectively. + +crates: + - name: pallet-ranked-collective diff --git a/prdoc/1.8.0/pr_3225.prdoc b/prdoc/1.8.0/pr_3225.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1dec40ccfbe96f5457aeea6235143b95f1ec9754 --- /dev/null +++ b/prdoc/1.8.0/pr_3225.prdoc @@ -0,0 +1,11 @@ +title: "Introduce submit_finality_proof_ex call to bridges GRANDPA pallet" + +doc: + - audience: Runtime Dev + description: | + New call has been added to pallet-bridge-grandpa: submit_finality_proof_ex. It should be + used instead of deprecated submit_finality_proof. submit_finality_proof will be removed + later. + +crates: + - name: "pallet-bridge-grandpa" diff --git a/prdoc/1.8.0/pr_3230.prdoc b/prdoc/1.8.0/pr_3230.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e6d32f918d3911d691e90ff83fd6bc6695da4c25 --- /dev/null +++ b/prdoc/1.8.0/pr_3230.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: rpc server remove prometheus metrics `substrate_rpc_requests_started/finished` and refactor WS ping/pongs. + +doc: + - audience: Node Operator + description: | + This PR updates the rpc server library to `jsonrpsee v0.22` to utilize new APIs. + + Breaking changes: + - Remove prometheus RPC metrics `substrate_rpc_requests_started` and `substrate_rpc_requests_finished`. + - The RPC server now disconnects inactive peers that didn't acknowledge WebSocket + pings more than three times in time. + + Added: + - Add prometheus RPC `substrate_rpc_sessions_time` to collect the duration for each WebSocket + session. +crates: [ ] diff --git a/prdoc/1.8.0/pr_3232.prdoc b/prdoc/1.8.0/pr_3232.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c6a339931c6f56772e0fbaa3a4a8992e7b3050c9 --- /dev/null +++ b/prdoc/1.8.0/pr_3232.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: Validate code when scheduling uprades + +doc: + - audience: Runtime User + description: | + Adds checks to ensure that the validation code is valid before scheduling + a code upgrade. + +crates: + - name: polkadot-runtime-parachains diff --git a/prdoc/1.8.0/pr_3243.prdoc b/prdoc/1.8.0/pr_3243.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..5bad985a2672453b56ee3828483d7035b71d4b79 --- /dev/null +++ b/prdoc/1.8.0/pr_3243.prdoc @@ -0,0 +1,13 @@ +title: Don't fail fast if the weight limit of a cross contract call is too big + +doc: + - audience: Runtime Dev + description: | + Cross contracts calls will now be executed even if the supplied weight + limit is bigger than the reamining weight. If the **actual** weight is too low + they will fail in the cross contract call and roll back. This is different from the + old behaviour where the limit for the cross contract call must be smaller than + the remaining weight. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.8.0/pr_3244.prdoc b/prdoc/1.8.0/pr_3244.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3d851c8afe0d2453a943caa1f0342d7ec76fe53c --- /dev/null +++ b/prdoc/1.8.0/pr_3244.prdoc @@ -0,0 +1,18 @@ +title: "Make the `benchmark pallet` command only require a Hasher" + +doc: + - audience: Node Dev + description: | + Currently the `benchmark pallet` command requires a `Block` type, while only using its hasher. + Now this is changed to only require the hasher. This means to use `HashingFor` in the + place where `Block` was required. + Example patch for your node with `cmd` being `BenchmarkCmd::Pallet(cmd)`: + + ```patch + - cmd.run::(config) + + cmd.run::, ()>(config) + ``` + +crates: + - name: sc-client-db + - name: frame-benchmarking-cli diff --git a/prdoc/1.8.0/pr_3272.prdoc b/prdoc/1.8.0/pr_3272.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a03113cf9739b741a37c5d8b4fb41dd59ede0d9f --- /dev/null +++ b/prdoc/1.8.0/pr_3272.prdoc @@ -0,0 +1,11 @@ +# 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 Coretime Runtime + +doc: + - audience: Runtime User + description: | + Add the Broker pallet to the Westend Coretime Chain runtime for the main functionality and sales to start. + +crates: [ ] diff --git a/prdoc/1.8.0/pr_3301.prdoc b/prdoc/1.8.0/pr_3301.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..19d1c5f1c18907cd1996603aade66990c358efeb --- /dev/null +++ b/prdoc/1.8.0/pr_3301.prdoc @@ -0,0 +1,11 @@ +# 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 server add rate limiting. + +doc: + - audience: Node Operator + description: | + Add rate limiting for RPC server which can be utilized by the CLI `--rpc-rate-limit ` + The rate-limiting is disabled by default. +crates: [ ] diff --git a/prdoc/1.8.0/pr_3308.prdoc b/prdoc/1.8.0/pr_3308.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..386cbd6230b42144741f9a85490cddb009221ab2 --- /dev/null +++ b/prdoc/1.8.0/pr_3308.prdoc @@ -0,0 +1,14 @@ +title: "Parachains-Aura: Only produce once per slot" + +doc: + - audience: Node Dev + description: | + With the introduction of asynchronous backing the relay chain allows parachain to include blocks every 6 seconds. + The Cumulus Aura implementations, besides the lookahead collator, are building blocks when there is a free slot for + the parachain in the relay chain. Most parachains are still running with a 12s slot duration and not allowing + to build multiple blocks per slot. But, the block production logic will be triggered every 6s, resulting in error + logs like: "no space left for the block in the unincluded segment". This is solved by ensuring that we don't build + multiple blocks per slot. + +crates: + - name: "cumulus-client-consensus-aura" diff --git a/prdoc/1.8.0/pr_3319.prdoc b/prdoc/1.8.0/pr_3319.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b84ec25a22eda2a561fb44eb1e732a3c36921418 --- /dev/null +++ b/prdoc/1.8.0/pr_3319.prdoc @@ -0,0 +1,11 @@ +# 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 Coretime to Westend + +doc: + - audience: Runtime User + description: | + Add the on demand and coretime assigners and migrate from legacy parachain auctions to coretime. + +crates: [ ] diff --git a/prdoc/1.8.0/pr_3325.prdoc b/prdoc/1.8.0/pr_3325.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..eb8126dc912520c049f44fb760bb759caf6336f9 --- /dev/null +++ b/prdoc/1.8.0/pr_3325.prdoc @@ -0,0 +1,10 @@ +title: "Ensure `TracksInfo` tracks are sorted by ID." + +doc: + - audience: Runtime Dev + description: | + Add a `integrity_check` function to trait `TracksInfo` and explicitly state that tracks must + always be sorted by ID. The referenda pallet now also uses this check in its `integrity_test`. + +crates: + - name: pallet-referenda diff --git a/prdoc/1.8.0/pr_3358.prdoc b/prdoc/1.8.0/pr_3358.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b6a03b3872f4e7d24305dfd924ccb8424f6e39b2 --- /dev/null +++ b/prdoc/1.8.0/pr_3358.prdoc @@ -0,0 +1,11 @@ +title: Do not stall finality on spam disputes + +doc: + - audience: Node Operator + description: | + This PR fixes the issue that periodically caused + finality stalls on Kusama due to disputes happening + there in combination with disputes spam protection mechanism. + See: https://github.com/paritytech/polkadot-sdk/issues/3345 + +crates: [ ] diff --git a/prdoc/1.8.0/pr_3361.prdoc b/prdoc/1.8.0/pr_3361.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..65baa9e94a0e38a4af16bf6f69aab7526c4ad920 --- /dev/null +++ b/prdoc/1.8.0/pr_3361.prdoc @@ -0,0 +1,10 @@ +title: Fix double charge of host function weight + +doc: + - audience: Runtime Dev + description: | + Fixed a double charge which can lead to quadratic gas consumption of + the `call` and `instantiate` host functions. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.8.0/pr_3364.prdoc b/prdoc/1.8.0/pr_3364.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1e7a6a5278d730a71a0bf5a928815812d8576386 --- /dev/null +++ b/prdoc/1.8.0/pr_3364.prdoc @@ -0,0 +1,12 @@ +# 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 server expose batch request configuration + +doc: + - audience: Node Operator + description: | + Add functionality to limit RPC batch requests by two new CLI options: + --rpc-disable-batch-request - disable batch requests on the server + --rpc-max-batch-request-len - limit batches to LEN on the server +crates: [ ] diff --git a/prdoc/1.8.0/pr_3370.prdoc b/prdoc/1.8.0/pr_3370.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f40d3821b9fed3eae49803128a9ff61058cd5d02 --- /dev/null +++ b/prdoc/1.8.0/pr_3370.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: "Remove `key` getter from pallet-sudo" + +doc: + - audience: Runtime Dev + description: | + Removed the `key` getter function from the sudo pallet. There is no replacement for getting + the key currently. + +crates: + - name: pallet-sudo diff --git a/prdoc/1.8.0/pr_3384.prdoc b/prdoc/1.8.0/pr_3384.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c19464977582a37b11760c81491a7ea7cf3ebced --- /dev/null +++ b/prdoc/1.8.0/pr_3384.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: "[pallet_contracts] stabilize `call_v2`, `instantiate_v2`, `lock_dependency` and `unlock_dependency`" + +doc: + - audience: Runtime Dev + description: | + These APIs are currently unstable and are being stabilized in this PR. + Note: `add_delegate_dependency` and `remove_delegate_dependency` have been renamed to `lock_dependency` and `unlock_dependency` respectively. + +crates: + - name: pallet-contracts-uapi + - name: pallet-contracts diff --git a/prdoc/1.8.0/pr_3395.prdoc b/prdoc/1.8.0/pr_3395.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a10fb84fd7f56211602fadaf656a25ea5d382255 --- /dev/null +++ b/prdoc/1.8.0/pr_3395.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: "`benchmarking-cli` `pallet` subcommand: refactor `--list` and add `--all` option" + +doc: + - audience: Node Dev + description: | + `pallet` subcommand's `--list` now accepts two values: "all" and "pallets". The former will list all available benchmarks, the latter will list only pallets. + Also adds `--all` to run all the available benchmarks and `--no-csv-header` to omit the csv-style header in the output. + NOTE: changes are backward compatible. + +crates: + - name: frame-benchmarking-cli diff --git a/prdoc/1.8.0/pr_3415.prdoc b/prdoc/1.8.0/pr_3415.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c56a5d3ffaa129d4aba36e1e11905e9d72b936cb --- /dev/null +++ b/prdoc/1.8.0/pr_3415.prdoc @@ -0,0 +1,9 @@ +title: "[pallet-contracts] Add APIVersion to the config." + +doc: + - audience: Runtime Dev + description: | + Add `APIVersion` to the config to communicate the state of the Host functions exposed by the pallet. + +crates: + - name: pallet-contracts diff --git a/prdoc/1.8.0/pr_3435.prdoc b/prdoc/1.8.0/pr_3435.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7d7896bff7d405a0a1dd34f09bc2aff0834b9ff2 --- /dev/null +++ b/prdoc/1.8.0/pr_3435.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix BEEFY-related gossip messages error logs + +doc: + - audience: Node Operator + description: | + Added logic to pump the gossip engine while waiting for other things + to make sure gossiped messages get consumed (practically discarded + until worker is fully initialized). + This fixes an issue where node operators saw error logs, and fixes + potential RAM bloat when BEEFY initialization takes a long time + (i.e. during clean sync). +crates: + - name: sc-consensus-beefy diff --git a/prdoc/1.8.0/pr_3477.prdoc b/prdoc/1.8.0/pr_3477.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..26e96d3635b14634ca523797d741ddaf224aa9ae --- /dev/null +++ b/prdoc/1.8.0/pr_3477.prdoc @@ -0,0 +1,11 @@ +title: Allow parachain which acquires multiple coretime cores to make progress + +doc: + - audience: Node Operator + description: | + Adds the needed changes so that parachains which acquire multiple coretime cores can still make progress. + Only one of the cores will be able to be occupied at a time. + Only works if the ElasticScalingMVP node feature is enabled in the runtime and the block author validator is + updated to include this change. + +crates: [ ] diff --git a/prdoc/pr_1554.prdoc b/prdoc/pr_1554.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bfce7c5edafd9df39ff9235f18536591b1d47158 --- /dev/null +++ b/prdoc/pr_1554.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: Runtime Upgrade ref docs and Single Block Migration example pallet + +doc: + - audience: Runtime Dev + description: | + `frame_support::traits::GetStorageVersion::current_storage_version` has been renamed `frame_support::traits::GetStorageVersion::in_code_storage_version`. + A simple find-replace is sufficient to handle this change. + +crates: + - name: "frame-support" + diff --git a/prdoc/pr_1781.prdoc b/prdoc/pr_1781.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e3560842d15ab249539ac2de172dc5914e92cc19 --- /dev/null +++ b/prdoc/pr_1781.prdoc @@ -0,0 +1,45 @@ +# 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: "Multi-Block-Migrations, `poll` hook and new System Callbacks" + +doc: + - audience: Runtime Dev + description: | + The major things that this MR touches are: + + **Multi-Block-Migrations**: `pallet-migrations` is introduced that can be configured in the + `System` of a runtime to act as multi-block migrator. The `migrations` pallet then in turn + receives the list of MBMs as config parameter. The list of migrations can be an aggregated + tuple of `SteppedMigration` trait implementation. + It is paramount that the `migrations` pallet is configured in `System` once it is deployed. A + test is in place to double check this. + + To integrate this into your runtime, it is only necessary to change the return type of + `initialize_block` to `RuntimeExecutiveMode`. For extended info please see + https://github.com/paritytech/polkadot-sdk/pull/1781. + + **poll**: a new pallet hook named `poll` is added. This can be used for places where the code + that should be executed is not deadline critical. Runtime devs are advised to skim their usage + of `on_initialize` and `on_finalize` to see whether they can be replace with `poll`. `poll` is + not guaranteed to be called each block. In fact it will not be called when MBMs are ongoing. + + **System Callbacks**: The `system` pallet gets five new config items - all of which can be + safely set to `()` as default. They are: + - `SingleBlockMigrations`: replaces the `Executive` now for configuring migrations. + - `MultiBlockMigrator`: the `pallet-migrations` would be set here, if deployed. + - `PreInherents`: a hook that runs before any inherent. + - `PostInherents`: a hook to run between inherents and `poll`/MBM logic. + - `PostTransactions`: a hook to run after all transactions but before `on_idle`. + +crates: + - name: frame-executive + - name: frame-system + - name: frame-support + - name: frame-support-procedural + - name: pallet-migrations + - name: sc-basic-authorship + - name: sc-block-builder + - name: sp-api + - name: sp-api-proc-macro + - name: sp-runtime diff --git a/prdoc/pr_2280.prdoc b/prdoc/pr_2280.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3026dc254e64b4e97f847f010afab2b42e9647c5 --- /dev/null +++ b/prdoc/pr_2280.prdoc @@ -0,0 +1,144 @@ +# 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: FRAME Create `TransactionExtension` as a replacement for `SignedExtension` + +doc: + - audience: Runtime User + description: | + Introduces a new trait `TransactionExtension` to replace `SignedExtension`. Introduce the + idea of transactions which obey the runtime's extensions and have according Extension data + (né Extra data) yet do not have hard-coded signatures. + + Deprecate the terminology of "Unsigned" when used for transactions/extrinsics owing to there + now being "proper" unsigned transactions which obey the extension framework and "old-style" + unsigned which do not. Instead we have `General` for the former and `Bare` for the latter. + Unsigned will be phased out as a type of transaction, and `Bare` will only be used for + Inherents. + + Types of extrinsic are now therefore + - Bare (no hardcoded signature, no Extra data; used to be known as "Unsigned") + - Bare transactions (deprecated) - Gossiped, validated with `ValidateUnsigned` + (deprecated) and the `_bare_compat` bits of `TransactionExtension` (deprecated). + - Inherents - Not gossiped, validated with `ProvideInherent`. + - Extended (Extra data) - Gossiped, validated via `TransactionExtension`. + - Signed transactions (with a hardcoded signature). + - General transactions (without a hardcoded signature). + + Notable information on `TransactionExtension` and the differences from `SignedExtension` + - `AdditionalSigned`/`additional_signed` is renamed to `Implicit`/`implicit`. It is encoded + for the entire transaction and passed in to each extension as a new argument to validate. + - `pre_dispatch` is renamed to `prepare`. + - `validate` runs transaction validation logic both off-chain and on-chain, and is + non-mutating. + - `prepare` runs on-chain pre-execution logic using information extracted during validation + and is mutating. + - `validate` and `prepare` are now passed an `Origin` rather than an `AccountId`. If the + extension logic presumes an `AccountId`, consider using the trait function + `AsSystemOriginSigner::as_system_origin_signer`. + - A signature on the underlying transaction may validly not be present. + - The origin may be altered during validation. + - Validation functionality present in `validate` should not be repeated in `prepare`. + Useful information obtained during `validate` should now be passsed in to `prepare` using + the new user-specifiable type `Val`. + - Unsigned logic should be migrated from the old `*_unsigned` functions into the regular + versions of the new functions where the `Origin` is `None`. + - The `Call` type defining the runtime call is now a type parameter. + - `TransactionExtension` now takes a `Context` type parameter. This defines some arbitrary + contextual data that is injected into the transaction extension logic. It is unused in + instances migrated from `SignedExtension`. + - Extensions now track the weight they consume during valdiation, preparation and + post-dispatch through the `TransactionExtensionBase::weight` function. + - `TestXt` was removed and its usage in tests was replaced with `UncheckedExtrinsic` + instances. + + To fix the build issues introduced by this change, use the `AsTransactionExtension` adapter + to wrap existing `SignedExtension`s by converting them using the `From` + generic implementation for `AsTransactionExtension`. More details on migrating existing + `SignedExtension` implementations to `TransactionExtension` in the PR description. + +crates: + - name: bridge-runtime-common + - name: bp-bridge-hub-cumulus + - name: bp-kusama + - name: bp-polkadot-bulletin + - name: bp-polkadot + - name: bp-rococo + - name: bp-westend + - name: bp-polkadot-core + - name: bp-runtime + - name: snowbridge-pallet-inbound-queue + - name: snowbridge-pallet-outbound-queue + - name: snowbridge-pallet-system + - name: snowbridge-runtime-test-common + - name: parachain-template-runtime + - name: asset-hub-rococo-runtime + - name: asset-hub-westend-runtime + - name: bridge-hub-rococo-runtime + - name: bridge-hub-westend-runtime + - name: collectives-westend-runtime + - name: contracts-rococo-runtime + - name: coretime-rococo-runtime + - name: coretime-westend-runtime + - name: glutton-westend-runtime + - name: people-rococo-runtime + - name: people-westend-runtime + - name: seedling-runtime + - name: shell-runtime + - name: penpal-runtime + - name: rococo-parachain-runtime + - name: polkadot-parachain-bin + - name: cumulus-primitives-storage-weight-reclaim + - name: cumulus-test-client + - name: cumulus-test-runtime + - name: cumulus-test-service + - name: polkadot-sdk-docs + - name: polkadot-service + - name: polkadot-test-service + - name: polkadot-runtime-common + - name: rococo-runtime + - name: polkadot-test-runtime + - name: westend-runtime + - name: staging-xcm-builder + - name: minimal-runtime + - name: node-template + - name: node-template-runtime + - name: staging-node-cli + - name: kitchensink-runtime + - name: node-testing + - name: sc-client-api + - name: sc-client-db + - name: sc-network-gossip + - name: sc-network-sync + - name: sc-transaction-pool + - name: frame + - name: pallet-babe + - name: pallet-balances + - name: pallet-beefy + - name: pallet-collective + - name: pallet-election-provider-multi-phase + - name: pallet-elections-phragmen + - name: pallet-example-basic + - name: pallet-example-offchain-worker + - name: frame-executive + - name: pallet-grandpa + - name: pallet-im-online + - name: pallet-offences + - name: pallet-sassafras + - name: pallet-state-trie-migration + - name: pallet-sudo + - name: frame-support-procedural + - name: frame-support + - name: frame-system + - name: frame-system-benchmarking + - name: pallet-transaction-payment + - name: pallet-asset-conversion-tx-payment + - name: pallet-asset-tx-payment + - name: pallet-skip-feeless-payment + - name: sp-inherents + - name: sp-metadata-ir + - name: sp-runtime + - name: substrate-test-runtime + - name: frame-benchmarking-cli + - name: frame-remote-externalities + - name: substrate-rpc-client diff --git a/prdoc/pr_2393.prdoc b/prdoc/pr_2393.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..708d017fafd50d103c0470997c3a7571e0ff30c1 --- /dev/null +++ b/prdoc/pr_2393.prdoc @@ -0,0 +1,16 @@ +title: "[XCMP] Use the number of 'ready' pages in XCMP suspend logic" + +doc: + - audience: Runtime Dev + description: | + Semantics of the suspension logic in the XCMP queue pallet change from using the number of + total pages to the number of 'ready' pages. The number of ready pages is now also exposed by + the `MessageQueue` pallet to downstream via the queue `footprint`. + +crates: + - name: cumulus-pallet-xcmp-queue + bump: patch + - name: pallet-message-queue + bump: patch + - name: frame-support + bump: major diff --git a/prdoc/pr_3002.prdoc b/prdoc/pr_3002.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..511a07e39c47d5055bd161fda0e748f96e4a9997 --- /dev/null +++ b/prdoc/pr_3002.prdoc @@ -0,0 +1,29 @@ +# 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 Runtime Side +author: skunert +topic: runtime +doc: + - audience: Runtime Dev + description: | + Adds a mechanism to reclaim proof size weight. + 1. Introduces a new `SignedExtension` that reclaims the difference + between benchmarked proof size weight and actual consumed proof size weight. + 2. Introduces a manual mechanism, `StorageWeightReclaimer`, to reclaim excess storage weight for situations + that require manual weight management. The most prominent case is the `on_idle` hook. + 3. Adds the `storage_proof_size` host function to the PVF. Parachain nodes should add it to ensure compatibility. + + To enable proof size reclaiming, add the host `storage_proof_size` host function to the parachain node. Add the + `StorageWeightReclaim` `SignedExtension` to your runtime and enable proof recording during block import. + + +crates: + - name: "cumulus-primitives-storage-weight-reclaim" +host_functions: + - name: "storage_proof_size" + description: | + This host function is used to pass the current size of the storage proof to the runtime. + It was introduced before but becomes relevant now. + Note: This host function is intended to be used through `cumulus_primitives_storage_weight_reclaim::get_proof_size`. + Direct usage is not recommended. diff --git a/prdoc/pr_3187.prdoc b/prdoc/pr_3187.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bda41142d86c8f9c1af8398e0cf21cdfeebebfa7 --- /dev/null +++ b/prdoc/pr_3187.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Retrying an execution on failed runtime construction + +doc: + - audience: Node Dev + description: | + If a runtime construction error happened during the execution request, then the artifact is re-prepared + and the execution request is retried at most once. See also the related issue. + +crates: + - name: polkadot-node-core-candidate-validation + - name: polkadot-node-core-pvf + - name: polkadot-node-core-pvf-execute-worker + - name: polkadot-node-core-pvf-common diff --git a/prdoc/pr_3231.prdoc b/prdoc/pr_3231.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..26e96d3635b14634ca523797d741ddaf224aa9ae --- /dev/null +++ b/prdoc/pr_3231.prdoc @@ -0,0 +1,11 @@ +title: Allow parachain which acquires multiple coretime cores to make progress + +doc: + - audience: Node Operator + description: | + Adds the needed changes so that parachains which acquire multiple coretime cores can still make progress. + Only one of the cores will be able to be occupied at a time. + Only works if the ElasticScalingMVP node feature is enabled in the runtime and the block author validator is + updated to include this change. + +crates: [ ] diff --git a/prdoc/pr_3233.prdoc b/prdoc/pr_3233.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ed4e8cce31f75b4bd98f91f7d15ec3fae0761354 --- /dev/null +++ b/prdoc/pr_3233.prdoc @@ -0,0 +1,12 @@ +title: "provisioner: allow multiple cores assigned to the same para" + +topic: Node + +doc: + - audience: Node Dev + description: | + Enable supplying multiple backable candidates to the paras_inherent pallet for the same paraid. + +crates: + - name: polkadot-node-core-prospective-parachains + - name: polkadot-node-core-provisioner diff --git a/prdoc/pr_3324.prdoc b/prdoc/pr_3324.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0425fbf317c8620125e9fe64f96a0eb99e4595f0 --- /dev/null +++ b/prdoc/pr_3324.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 weight calculation and event emission in pallet-membership" + +doc: + - audience: Runtime Dev + description: | + Bug fix for the membership pallet to use correct weights. Also no event will be emitted + anymore when `change_key` is called with identical accounts. + +crates: + - name: pallet-membership diff --git a/prdoc/pr_3371.prdoc b/prdoc/pr_3371.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..605d540772f0b249962088463237dda2e6690d6c --- /dev/null +++ b/prdoc/pr_3371.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: removed `pallet::getter` from example pallets + +doc: + - audience: Runtime Dev + description: | + This PR removes all the `pallet::getter` usages from the template pallets found in the Substrate and Cumulus template nodes, and from the Substrate example pallets. + The purpose is to discourage developers to use this macro, that is currently being removed and soon will be deprecated. + +crates: + - name: pallet-template + - name: pallet-parachain-template + - name: pallet-example-basic + - name: pallet-example-kitchensink + - name: pallet-example-offchain-worker + - name: pallet-example-split + diff --git a/prdoc/pr_3377.prdoc b/prdoc/pr_3377.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..8e5b3935512b1b2dd12836e940a5d95040df8cf2 --- /dev/null +++ b/prdoc/pr_3377.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: Permissioned contract deployment + +doc: + - audience: Runtime Dev + description: | + This PR introduces two new config types that specify the origins allowed to + upload and instantiate contract code. However, this check is not enforced when + a contract instantiates another contract. + +crates: +- name: pallet-contracts diff --git a/prdoc/pr_3378.prdoc b/prdoc/pr_3378.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2470fc158519e31e297a7cbefcae3bc54eb8f708 --- /dev/null +++ b/prdoc/pr_3378.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: Remove deprecated GenesisConfig + +doc: + - audience: Runtime Dev + description: | + Removes deprecated type `GenesisConfig`, it was replaced by `RuntimeGenesisConfig` on May 24 of 2023. + The type `GenesisConfig` was deprecated on May 24 of 2023 [#14210](https://github.com/paritytech/substrate/pull/14210) + +crates: + - name: frame-support-procedural \ No newline at end of file diff --git a/prdoc/pr_3403.prdoc b/prdoc/pr_3403.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..086e769da3c39c4d3def42791c18a82948676386 --- /dev/null +++ b/prdoc/pr_3403.prdoc @@ -0,0 +1,22 @@ +# 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 `claim_assets` extrinsic to `pallet-xcm`" + +doc: + - audience: Runtime User + description: | + There's a new extrinsic in `pallet-xcm` for claiming assets. + This means that if your assets ever get trapped while teleporting or doing reserve asset transfers, + you can easily claim them by calling this new extrinsic. + - audience: Runtime Dev + description: | + There's a new extrinsic in `pallet-xcm` that needs a new configuration item for its benchmarks. + It's a simple function in `pallet_xcm::benchmarking::Config`, `get_asset`, that returns a valid asset + handled by the AssetTransactor of the chain. + If you're already using `pallet-xcm-benchmarks`, then you already have this function there and can + just copy and paste it. + +crates: + - name: pallet-xcm + - name: staging-xcm diff --git a/prdoc/pr_3411.prdoc b/prdoc/pr_3411.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d847d6756ac7f019cf12e4d9e7993ebeda1273ab --- /dev/null +++ b/prdoc/pr_3411.prdoc @@ -0,0 +1,11 @@ +# 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 Encointer as trusted teleporter for Westend + +doc: + - audience: Runtime Dev + description: | + add Encointer as trusted teleporter for Westend with ParaId 1003 + +crates: [ ] diff --git a/prdoc/pr_3412.prdoc b/prdoc/pr_3412.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1ee6edfeb837f81646d32633ed009d2cded4dcfe --- /dev/null +++ b/prdoc/pr_3412.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: "[FRAME] Add genesis test and remove some checks" + +doc: + - audience: Runtime Dev + description: | + The construct_runtime macro now generates a test to assert that all `GenesisConfig`s of all + pallets can be build within the runtime. This ensures that the `BuildGenesisConfig` runtime + API works. + Further, some checks from a few pallets were removed to make this pass. + +crates: + - name: pallet-babe + - name: cumulus-pallet-aura-ext + - name: pallet-session diff --git a/prdoc/pr_3447.prdoc b/prdoc/pr_3447.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1d8d4f409f77cf29247d35d196bccff390896881 --- /dev/null +++ b/prdoc/pr_3447.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: Use generic hash for runtime wasm in resolve_state_version_from_wasm + +doc: + - audience: Node Dev + description: | + Changes the runtime hash algorithm used in resolve_state_version_from_wasm from DefaultHasher to a caller-provided + one (usually HashingFor). Fixes a bug where the runtime wasm was being compiled again when it was not + needed, because the hash did not match +crates: + - name: sc-chain-spec diff --git a/prdoc/pr_3453.prdoc b/prdoc/pr_3453.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1f01440f722fb9984231f690a20c517b9d4bf0a1 --- /dev/null +++ b/prdoc/pr_3453.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-nomination-pools]: `chill` is permissionless if depositor's stake is less than `min_nominator_bond`" + +doc: + - audience: Runtime Dev + description: | + Nomination pools currently have an issue whereby member funds cannot be unbonded if the depositor bonded amount is less than `MinNominatorBond`. + This PR makes the `chill` function permissionless if this condition is met. + Consequently, `nominate` function also checks for `depositor` to have at least `MinNominatorBond`, and returns `MinimumBondNotMet` error if not. +crates: + - name: pallet-nomination-pools diff --git a/prdoc/pr_3454.prdoc b/prdoc/pr_3454.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7f564258f23a57f3784c2ee055176153f006734f --- /dev/null +++ b/prdoc/pr_3454.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: "Introduce storage attr macro #[disable_try_decode_storage] and set it on System::Events and ParachainSystem::HostConfiguration" + +doc: + - audience: Runtime Dev + description: | + Allows marking storage items with \#[disable_try_decode_storage], which disables that storage item from being decoded + during try_decode_entire_state calls. + + Applied the attribute to System::Events to close https://github.com/paritytech/polkadot-sdk/issues/2560. + Applied the attribute to ParachainSystem::HostConfiguration to resolve periodic issues with it. + +crates: + - name: frame-support-procedural + - name: frame-system + - name: cumulus-pallet-parachain-system + diff --git a/prdoc/pr_3456.prdoc b/prdoc/pr_3456.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c7327e17e57d74614fd1b593cc222faf4a87192d --- /dev/null +++ b/prdoc/pr_3456.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: Removed `pallet::getter` usage from `pallet-collective` + +doc: + - audience: Runtime Dev + description: | + This PR removes `pallet::getter` usage from `pallet-collective`, and updates dependant code accordingly. + The syntax `StorageItem::::get()` should be used instead. + +crates: + - name: pallet-collective + - name: kitchensink-runtime + - name: collectives-westend-runtime diff --git a/prdoc/pr_3460.prdoc b/prdoc/pr_3460.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1f16fe0890836dd25bfcaa75d5fc647688230dd8 --- /dev/null +++ b/prdoc/pr_3460.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: Repot all templates + +doc: + - audience: Runtime Dev + description: | + This PR moves all templates into a single folder in the polkadot-sdk repo (`/templates`) and + unifies their crate names as well. Most notably, the crate name for what was formerly known + as `node-template` is no `solochain-template-node`. The other two crates in the template are + consequently called: `solochain-runtime-template` and `pallet-solochain-template`. + The other two template crate names follow a similar patter, just replacing `solochain` with + `parachain` or `minimal`. + + This PR is part of a bigger step toward automating the template repositories, see the + following: https://github.com/paritytech/polkadot-sdk/issues/3155 + +# the following crates are removed and renamed, although none are released. +crates: + - name: minimal-template-runtime # formerly called minimal-runtime + - name: minimal-template-node # formerly called minimal-node + - name: solochain-template-node # formerly called node-template + - name: solochain-template-runtime # formerly called node-template-runtime + - name: parachain-template-runtime # formerly called parachain-runtime + - name: parachain-template-runtime # formerly called parachain-node diff --git a/prdoc/pr_3491.prdoc b/prdoc/pr_3491.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e36afb916a6226c3609b1e2eb8aacf6af8127d32 --- /dev/null +++ b/prdoc/pr_3491.prdoc @@ -0,0 +1,12 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove Deprecated OldWeight + +doc: + - audience: Runtime Dev + description: | + Removed deprecated sp_weights::OldWeight type. Use [`weight_v2::Weight`] instead. + +crates: + - name: frame-support diff --git a/prdoc/pr_3504.prdoc b/prdoc/pr_3504.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..90a8ddd38b8f7f8159199ac6423ffee3dc645291 --- /dev/null +++ b/prdoc/pr_3504.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 prometheus label "is_rate_limited" to rpc calls + +doc: + - audience: Node Operator + description: | + This PR adds a label "is_rate_limited" to the prometheus metrics "substrate_rpc_calls_time" and "substrate_rpc_calls_finished" + than can be used to distinguish rate-limited RPC calls from other RPC calls. Because rate-limited RPC calls may take + tens of seconds. + +crates: [ ] diff --git a/prdoc/pr_3505.prdoc b/prdoc/pr_3505.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..08ad909739c7d761371e0e1e40d575794fc4e217 --- /dev/null +++ b/prdoc/pr_3505.prdoc @@ -0,0 +1,36 @@ +# 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: Removes `as [disambiguation_path]` from the required syntax in `derive_impl` + +doc: + - audience: Runtime Dev + description: | + This PR removes the need to specify `as [disambiguation_path]` for cases where the trait + definition resides within the same scope as default impl path. + + For example, in the following macro invocation + ```rust + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Runtime { + ... + } + ``` + the trait `DefaultConfig` lies within the `frame_system` scope and `TestDefaultConfig` impls + the `DefaultConfig` trait. + + Using this information, we can compute the disambiguation path internally, thus removing the + need of an explicit specification: + ```rust + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + ... + } + ``` + + In cases where the trait lies outside this scope, we would still need to specify it explicitly, + but this should take care of most (if not all) uses of `derive_impl` within FRAME's context. + +crates: + - name: frame-support-procedural + - name: pallet-default-config-example diff --git a/prdoc/pr_3510.prdoc b/prdoc/pr_3510.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..6ee2f9a81f750ef0c64a2b8d8197e1e44d7e9f0c --- /dev/null +++ b/prdoc/pr_3510.prdoc @@ -0,0 +1,13 @@ +title: "Fix multi-collator parachain transition to async backing" + +doc: + - audience: Node Operator + description: | + The dynamic Aura slot duration, introduced in PR#3211, didn't take the block import pipeline + into account. The result was the parachain backed by multiple collators not being able to + keep producing blocks after its runtime was upgraded to support async backing, requiring to + restart all the collator nodes. This change fixes the issue, introducing the dynamic Aura + slot duration into the block import pipeline. + +crates: + - name: "polkadot-parachain-bin" diff --git a/prdoc/pr_3513.prdoc b/prdoc/pr_3513.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e1f2afe280a5bc2e8d341f9ab5ac73315f1676e8 --- /dev/null +++ b/prdoc/pr_3513.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 call enum's metadata regression + +doc: + - audience: Runtime Dev + - audience: Runtime User + description: | + This PR fixes an issue where in the metadata of all FRAME-based chains, the documentation for + all extrinsic/call/transactions has been replaced by a single line saying "see Pallet::name". + + Many wallets display the metadata of transactions to the user before signing, and this bug + might have affected this process. + +crates: + - name: frame + - name: frame-support diff --git a/prdoc/pr_3523.prdoc b/prdoc/pr_3523.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c31d9d096dc00797254f7946fd44984636cb1547 --- /dev/null +++ b/prdoc/pr_3523.prdoc @@ -0,0 +1,19 @@ +title: Fix crash of synced parachain node run with `--sync=warp` + +doc: + - audience: Node Operator + description: | + Fix crash of `SyncingEngine` when an already synced parachain node is run with `--sync=warp` + (issue https://github.com/paritytech/polkadot-sdk/issues/3496). + The issue manifests itself by errors in the logs: + ``` + [Parachain] Cannot set warp sync target block: no warp sync strategy is active. + [Parachain] Failed to set warp sync target block header, terminating `SyncingEngine`. + ``` + Followed by a stream of messages: + ``` + [Parachain] Protocol command streams have been shut down + ``` + +crates: + - name: sc-network-sync diff --git a/prdoc/pr_3540.prdoc b/prdoc/pr_3540.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d0a91882b6bf4e357fde899c2d939ddb7de62f34 --- /dev/null +++ b/prdoc/pr_3540.prdoc @@ -0,0 +1,10 @@ +title: "[pallet-contracts] Only allow non-deterministic code to be uploaded with Determinism::Relaxed" + +doc: + - audience: Runtime Dev + description: | + The `upload_code` extrinsic, will now only allow non-deterministic code to be uploaded with the `Determinism::Relaxed` flag. + This prevent an attacker from uploading "deterministic" code with the `Determinism::Relaxed` flag, preventing the code to be instantiated for on-chain execution. + +crates: + - name: pallet-contracts diff --git a/prdoc/pr_3574.prdoc b/prdoc/pr_3574.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..99868ea8a132a6440b9daff0d9f7deaa329ca4b3 --- /dev/null +++ b/prdoc/pr_3574.prdoc @@ -0,0 +1,23 @@ +title: Generate test functions for each benchmark with benchmarking v2 + +doc: + - audience: Runtime Dev + description: | + This PR fixes an issue where using `impl_benchmark_test_suite` macro + within modules that use the benchmarking v2 macros (`#[benchmarks]` + and `#[instance_benchmarks]`) always produced a single test called + `test_benchmarks` instead of a separate benchmark test for every + benchmark (noted with the `#[benchmark]` macro). + + By using this macro from now on, new tests will be created named + `test_benchmark_{name}` where `name` is the name of the benchmark + function. Those tests will be nested inside the module intended for + benchmark functions. + + Also, when using `impl_benchmark_test_suite` inside the module, + the import of such marco will not be necessary, so any explicit + import of it will be marked as unused, the same way it works for + v1 macros so far. + +crates: + - name: frame-support-procedural diff --git a/prdoc/pr_3606.prdoc b/prdoc/pr_3606.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..18b71de9477ea516de2bcd5ecfab52a6a5479afb --- /dev/null +++ b/prdoc/pr_3606.prdoc @@ -0,0 +1,9 @@ +title: "[pallet_contracts] mark lock/unlock_delegate_dependency as stable" + +doc: + - audience: Runtime Dev + description: | + Lock and unlock delegate dependency are stable now, so we can mark them as such. + +crates: + - name: pallet-contracts diff --git a/prdoc/schema_user.json b/prdoc/schema_user.json index 82215d51866b35895b5e840a8f3a900b161a9cf6..1bd0b3b93ee45ea75d9781ef1485898dd86c7ff3 100644 --- a/prdoc/schema_user.json +++ b/prdoc/schema_user.json @@ -3,9 +3,8 @@ "$id": "https://raw.githubusercontent.com/paritytech/prdoc/master/prdoc_schema_user.json", "version": { "major": 1, - "minor": 0, - "patch": 0, - "timestamp": 20230817152351 + "minor": 1, + "patch": 0 }, "title": "Polkadot SDK PRDoc Schema", "description": "JSON Schema definition for the Polkadot SDK PR documentation", @@ -125,10 +124,16 @@ "name": { "type": "string" }, + "bump": { + "$ref": "#/$defs/semver_bump" + }, "note": { "type": "string" } - } + }, + "required": [ + "name" + ] }, "migration_db": { "type": "object", @@ -165,6 +170,26 @@ "description" ] }, + "semver_bump": { + "description": "The type of bump to apply to the crate version according to Cargo SemVer: https://doc.rust-lang.org/cargo/reference/semver.html. Please check docs/RELEASE.md for more information.", + "oneOf": [ + { + "const": "major", + "title": "Major", + "description": "A bump to the leftmost non-zero digit of the version number." + }, + { + "const": "minor", + "title": "Minor", + "description": "A bump to the second leftmost non-zero digit of the version number." + }, + { + "const": "patch", + "title": "Patch", + "description": "A bump to the third leftmost non-zero digit of the version number." + } + ] + }, "doc": { "type": "object", "description": "You have the the option to provide different description of your PR for different audiences.", diff --git a/substrate/.maintain/frame-weight-template.hbs b/substrate/.maintain/frame-weight-template.hbs index ecd384a514563cfb4ecb0e9f364cdf473f5b5cec..ec9eee205cee37b534eca5187ece031daa54dd99 100644 --- a/substrate/.maintain/frame-weight-template.hbs +++ b/substrate/.maintain/frame-weight-template.hbs @@ -33,7 +33,7 @@ pub trait WeightInfo { /// Weights for `{{pallet}}` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); -{{#if (eq pallet "frame_system")}} +{{#if (or (eq pallet "frame_system") (eq pallet "frame_system_extensions"))}} impl WeightInfo for SubstrateWeight { {{else}} impl WeightInfo for SubstrateWeight { diff --git a/substrate/bin/minimal/node/Cargo.toml b/substrate/bin/minimal/node/Cargo.toml deleted file mode 100644 index cc00988dcfef1e495bbc02cbc7070ba7ef718932..0000000000000000000000000000000000000000 --- a/substrate/bin/minimal/node/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "minimal-node" -version = "4.0.0-dev" -description = "A fresh FRAME-based Substrate node, ready for hacking." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io/" -edition = "2021" -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" -build = "build.rs" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[[bin]] -name = "minimal-node" - -[dependencies] -clap = { version = "4.4.18", features = ["derive"] } -futures = { version = "0.3.21", features = ["thread-pool"] } -futures-timer = "3.0.1" -jsonrpsee = { version = "0.16.2", features = ["server"] } -serde_json = "1.0.111" - -sc-cli = { path = "../../../client/cli" } -sc-executor = { path = "../../../client/executor" } -sc-network = { path = "../../../client/network" } -sc-service = { path = "../../../client/service" } -sc-telemetry = { path = "../../../client/telemetry" } -sc-transaction-pool = { path = "../../../client/transaction-pool" } -sc-transaction-pool-api = { path = "../../../client/transaction-pool/api" } -sc-consensus = { path = "../../../client/consensus/common" } -sc-consensus-manual-seal = { path = "../../../client/consensus/manual-seal" } -sc-rpc-api = { path = "../../../client/rpc-api" } -sc-basic-authorship = { path = "../../../client/basic-authorship" } -sc-offchain = { path = "../../../client/offchain" } -sc-client-api = { path = "../../../client/api" } - -sp-timestamp = { path = "../../../primitives/timestamp" } -sp-keyring = { path = "../../../primitives/keyring" } -sp-api = { path = "../../../primitives/api" } -sp-blockchain = { path = "../../../primitives/blockchain" } -sp-block-builder = { path = "../../../primitives/block-builder" } -sp-io = { path = "../../../primitives/io" } -sp-runtime = { path = "../../../primitives/runtime" } - -substrate-frame-rpc-system = { path = "../../../utils/frame/rpc/system" } - -frame = { path = "../../../frame", features = ["experimental", "runtime"] } -runtime = { package = "minimal-runtime", path = "../runtime" } - -[build-dependencies] -substrate-build-script-utils = { version = "3.0.0", path = "../../../utils/build-script-utils" } - -[features] -default = [] diff --git a/substrate/bin/minimal/runtime/Cargo.toml b/substrate/bin/minimal/runtime/Cargo.toml deleted file mode 100644 index 296106544bbfdbbb1f7f76b86d5c8ddefb54f917..0000000000000000000000000000000000000000 --- a/substrate/bin/minimal/runtime/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "minimal-runtime" -version = "0.1.0" -authors.workspace = true -description = "A minimal Substrate example runtime" -edition.workspace = true -repository.workspace = true -license.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -parity-scale-codec = { version = "3.0.0", default-features = false } -scale-info = { version = "2.6.0", default-features = false } - -# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. -frame = { path = "../../../frame", default-features = false, features = ["experimental", "runtime"] } -frame-support = { path = "../../../frame/support", default-features = false } - -# pallets that we want to use -pallet-balances = { path = "../../../frame/balances", default-features = false } -pallet-sudo = { path = "../../../frame/sudo", default-features = false } -pallet-timestamp = { path = "../../../frame/timestamp", default-features = false } -pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } -pallet-transaction-payment-rpc-runtime-api = { path = "../../../frame/transaction-payment/rpc/runtime-api", default-features = false } - -# genesis builder that allows us to interacto with runtime genesis config -sp-genesis-builder = { path = "../../../primitives/genesis-builder", default-features = false } - - -[build-dependencies] -substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true } - -[features] -default = ["std"] -std = [ - "frame-support/std", - "frame/std", - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "parity-scale-codec/std", - "scale-info/std", - "sp-genesis-builder/std", - "substrate-wasm-builder", -] diff --git a/substrate/bin/node-template/.editorconfig b/substrate/bin/node-template/.editorconfig deleted file mode 100644 index 5adac74ca24b3422222b2cb886b2a50cd22b6d1b..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -root = true - -[*] -indent_style=space -indent_size=2 -tab_width=2 -end_of_line=lf -charset=utf-8 -trim_trailing_whitespace=true -insert_final_newline = true - -[*.{rs,toml}] -indent_style=tab -indent_size=tab -tab_width=4 -max_line_length=100 diff --git a/substrate/bin/node-template/.envrc b/substrate/bin/node-template/.envrc deleted file mode 100644 index 3550a30f2de389e537ee40ca5e64a77dc185c79b..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml deleted file mode 100644 index 36c2f9f8b7062b31155bc782a3f2aad38a51fe66..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/node/Cargo.toml +++ /dev/null @@ -1,92 +0,0 @@ -[package] -name = "node-template" -version = "4.0.0-dev" -description = "A fresh FRAME-based Substrate node, ready for hacking." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io/" -edition.workspace = true -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" -build = "build.rs" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[[bin]] -name = "node-template" - -[dependencies] -clap = { version = "4.4.18", features = ["derive"] } -futures = { version = "0.3.21", features = ["thread-pool"] } -serde_json = "1.0.111" - -sc-cli = { path = "../../../client/cli" } -sp-core = { path = "../../../primitives/core" } -sc-executor = { path = "../../../client/executor" } -sc-network = { path = "../../../client/network" } -sc-service = { path = "../../../client/service" } -sc-telemetry = { path = "../../../client/telemetry" } -sc-transaction-pool = { path = "../../../client/transaction-pool" } -sc-transaction-pool-api = { path = "../../../client/transaction-pool/api" } -sc-offchain = { path = "../../../client/offchain" } -sc-consensus-aura = { path = "../../../client/consensus/aura" } -sp-consensus-aura = { path = "../../../primitives/consensus/aura" } -sc-consensus = { path = "../../../client/consensus/common" } -sc-consensus-grandpa = { path = "../../../client/consensus/grandpa" } -sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa" } -sc-client-api = { path = "../../../client/api" } -sp-runtime = { path = "../../../primitives/runtime" } -sp-io = { path = "../../../primitives/io" } -sp-timestamp = { path = "../../../primitives/timestamp" } -sp-inherents = { path = "../../../primitives/inherents" } -sp-keyring = { path = "../../../primitives/keyring" } -frame-system = { path = "../../../frame/system" } -pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } - -# These dependencies are used for the node template's RPCs -jsonrpsee = { version = "0.16.2", features = ["server"] } -sp-api = { path = "../../../primitives/api" } -sc-rpc-api = { path = "../../../client/rpc-api" } -sp-blockchain = { path = "../../../primitives/blockchain" } -sp-block-builder = { path = "../../../primitives/block-builder" } -sc-basic-authorship = { path = "../../../client/basic-authorship" } -substrate-frame-rpc-system = { path = "../../../utils/frame/rpc/system" } -pallet-transaction-payment-rpc = { path = "../../../frame/transaction-payment/rpc" } - -# These dependencies are used for runtime benchmarking -frame-benchmarking = { path = "../../../frame/benchmarking" } -frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli" } - -# Local Dependencies -node-template-runtime = { path = "../runtime" } - -# CLI-specific dependencies -try-runtime-cli = { path = "../../../utils/frame/try-runtime/cli", optional = true } - -[build-dependencies] -substrate-build-script-utils = { path = "../../../utils/build-script-utils" } - -[features] -default = [] -# Dependencies that are only required if runtime benchmarking should be build. -runtime-benchmarks = [ - "frame-benchmarking-cli/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "node-template-runtime/runtime-benchmarks", - "sc-service/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -# Enable features that allow the runtime to be tried and debugged. Name might be subject to change -# in the near future. -try-runtime = [ - "frame-system/try-runtime", - "node-template-runtime/try-runtime", - "pallet-transaction-payment/try-runtime", - "sp-runtime/try-runtime", - "try-runtime-cli/try-runtime", -] diff --git a/substrate/bin/node-template/runtime/Cargo.toml b/substrate/bin/node-template/runtime/Cargo.toml deleted file mode 100644 index a7b93a230ca85dd0b400884b4788a50d24db0402..0000000000000000000000000000000000000000 --- a/substrate/bin/node-template/runtime/Cargo.toml +++ /dev/null @@ -1,125 +0,0 @@ -[package] -name = "node-template-runtime" -version = "4.0.0-dev" -description = "A fresh FRAME-based Substrate node, ready for hacking." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io/" -edition.workspace = true -license = "MIT-0" -publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } - -pallet-aura = { path = "../../../frame/aura", default-features = false } -pallet-balances = { path = "../../../frame/balances", default-features = false } -frame-support = { path = "../../../frame/support", default-features = false } -pallet-grandpa = { path = "../../../frame/grandpa", default-features = false } -pallet-sudo = { path = "../../../frame/sudo", default-features = false } -frame-system = { path = "../../../frame/system", default-features = false } -frame-try-runtime = { path = "../../../frame/try-runtime", default-features = false, optional = true } -pallet-timestamp = { path = "../../../frame/timestamp", default-features = false } -pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } -frame-executive = { path = "../../../frame/executive", default-features = false } -sp-api = { path = "../../../primitives/api", default-features = false } -sp-block-builder = { path = "../../../primitives/block-builder", default-features = false } -sp-consensus-aura = { path = "../../../primitives/consensus/aura", default-features = false, features = ["serde"] } -sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false, features = ["serde"] } -sp-core = { path = "../../../primitives/core", default-features = false, features = ["serde"] } -sp-inherents = { path = "../../../primitives/inherents", default-features = false } -sp-offchain = { path = "../../../primitives/offchain", default-features = false } -sp-runtime = { path = "../../../primitives/runtime", default-features = false, features = ["serde"] } -sp-session = { path = "../../../primitives/session", default-features = false } -sp-std = { path = "../../../primitives/std", default-features = false } -sp-storage = { path = "../../../primitives/storage", default-features = false } -sp-transaction-pool = { path = "../../../primitives/transaction-pool", default-features = false } -sp-version = { path = "../../../primitives/version", default-features = false, features = ["serde"] } -serde_json = { version = "1.0.111", default-features = false, features = ["alloc"] } -sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" } - -# Used for the node template's RPCs -frame-system-rpc-runtime-api = { path = "../../../frame/system/rpc/runtime-api", default-features = false } -pallet-transaction-payment-rpc-runtime-api = { path = "../../../frame/transaction-payment/rpc/runtime-api", default-features = false } - -# Used for runtime benchmarking -frame-benchmarking = { path = "../../../frame/benchmarking", default-features = false, optional = true } -frame-system-benchmarking = { path = "../../../frame/system/benchmarking", default-features = false, optional = true } - -# Local Dependencies -pallet-template = { path = "../pallets/template", default-features = false } - -[build-dependencies] -substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-executive/std", - "frame-support/std", - "frame-system-benchmarking?/std", - "frame-system-rpc-runtime-api/std", - "frame-system/std", - "frame-try-runtime?/std", - "pallet-aura/std", - "pallet-balances/std", - "pallet-grandpa/std", - "pallet-sudo/std", - "pallet-template/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "scale-info/std", - "serde_json/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-consensus-grandpa/std", - "sp-core/std", - "sp-genesis-builder/std", - "sp-inherents/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-std/std", - "sp-storage/std", - "sp-transaction-pool/std", - "sp-version/std", - "substrate-wasm-builder", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-grandpa/runtime-benchmarks", - "pallet-sudo/runtime-benchmarks", - "pallet-template/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-executive/try-runtime", - "frame-support/try-runtime", - "frame-system/try-runtime", - "frame-try-runtime/try-runtime", - "pallet-aura/try-runtime", - "pallet-balances/try-runtime", - "pallet-grandpa/try-runtime", - "pallet-sudo/try-runtime", - "pallet-template/try-runtime", - "pallet-timestamp/try-runtime", - "pallet-transaction-payment/try-runtime", - "sp-runtime/try-runtime", -] -experimental = ["pallet-aura/experimental"] diff --git a/substrate/bin/node/bench/Cargo.toml b/substrate/bin/node/bench/Cargo.toml index 42af802d716b7181b91310318137928896c6bd66..8e0f7fb93b3904ac20902662326a7cba067fbf21 100644 --- a/substrate/bin/node/bench/Cargo.toml +++ b/substrate/bin/node/bench/Cargo.toml @@ -16,16 +16,16 @@ workspace = true [dependencies] array-bytes = "6.1" -clap = { version = "4.4.18", features = ["derive"] } -log = "0.4.17" +clap = { version = "4.5.1", features = ["derive"] } +log = { workspace = true, default-features = true } node-primitives = { path = "../primitives" } node-testing = { path = "../testing" } kitchensink-runtime = { path = "../runtime" } sc-client-api = { path = "../../../client/api" } sp-runtime = { path = "../../../primitives/runtime" } sp-state-machine = { path = "../../../primitives/state-machine" } -serde = "1.0.195" -serde_json = "1.0.111" +serde = { workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } derive_more = { version = "0.99.17", default-features = false, features = ["display"] } kvdb = "0.13.0" kvdb-rocksdb = "0.19.0" diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 061c9684c22602f5736ad81b5ce068a7429137bd..c91a83e6d6eab210a7a54a0eb4cc156c1ad79e23 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -41,12 +41,12 @@ crate-type = ["cdylib", "rlib"] [dependencies] # third-party dependencies array-bytes = "6.1" -clap = { version = "4.4.18", features = ["derive"], optional = true } +clap = { version = "4.5.1", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } -serde = { version = "1.0.195", features = ["derive"] } -jsonrpsee = { version = "0.16.2", features = ["server"] } +serde = { features = ["derive"], workspace = true, default-features = true } +jsonrpsee = { version = "0.22", features = ["server"] } futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } rand = "0.8" # primitives @@ -116,7 +116,7 @@ sc-cli = { path = "../../../client/cli", optional = true } frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli", optional = true } node-inspect = { package = "staging-node-inspect", path = "../inspect", optional = true } try-runtime-cli = { path = "../../../utils/frame/try-runtime/cli", optional = true } -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } [dev-dependencies] sc-keystore = { path = "../../../client/keystore" } @@ -128,6 +128,7 @@ sc-service-test = { path = "../../../client/service/test" } sc-block-builder = { path = "../../../client/block-builder" } sp-tracing = { path = "../../../primitives/tracing" } sp-blockchain = { path = "../../../primitives/blockchain" } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } futures = "0.3.21" tempfile = "3.1.0" assert_cmd = "2.0.2" @@ -158,13 +159,13 @@ sp-consensus-babe = { path = "../../../primitives/consensus/babe" } sp-externalities = { path = "../../../primitives/externalities" } sp-keyring = { path = "../../../primitives/keyring" } sp-runtime = { path = "../../../primitives/runtime" } -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } scale-info = { version = "2.10.0", features = ["derive", "serde"] } sp-trie = { path = "../../../primitives/trie" } sp-state-machine = { path = "../../../primitives/state-machine" } [build-dependencies] -clap = { version = "4.4.18", optional = true } +clap = { version = "4.5.1", optional = true } clap_complete = { version = "4.0.2", optional = true } node-inspect = { package = "staging-node-inspect", path = "../inspect", optional = true } frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli", optional = true } @@ -195,6 +196,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "kitchensink-runtime/runtime-benchmarks", "node-inspect?/runtime-benchmarks", + "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-tx-payment/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -204,6 +206,7 @@ runtime-benchmarks = [ "pallet-skip-feeless-payment/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "sc-client-db/runtime-benchmarks", "sc-service/runtime-benchmarks", diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index f59a125e1c05f27f64a45b9085fa736e98a22bc0..b98a321c338e93e6fb773cecf67d13a978d1a7bd 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -28,7 +28,7 @@ use sc_consensus::{ use sc_service::{ config::{ BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig, - PruningMode, WasmExecutionMethod, WasmtimeInstantiationStrategy, + PruningMode, RpcBatchRequestConfig, WasmExecutionMethod, WasmtimeInstantiationStrategy, }, BasePath, Configuration, Role, }; @@ -83,6 +83,9 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { 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, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, @@ -107,7 +110,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { fn extrinsic_set_time(now: u64) -> OpaqueExtrinsic { kitchensink_runtime::UncheckedExtrinsic { - signature: None, + preamble: sp_runtime::generic::Preamble::Bare, function: kitchensink_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set { now }), } .into() diff --git a/substrate/bin/node/cli/benches/executor.rs b/substrate/bin/node/cli/benches/executor.rs index a326e1a79ea347f169e372581d07dc4f43848e24..e13d34f9657acea48f0b53aa021296e9897f4780 100644 --- a/substrate/bin/node/cli/benches/executor.rs +++ b/substrate/bin/node/cli/benches/executor.rs @@ -29,7 +29,7 @@ use sp_core::{ storage::well_known_keys, traits::{CallContext, CodeExecutor, RuntimeCode}, }; -use sp_runtime::traits::BlakeTwo256; +use sp_runtime::{generic::ExtrinsicFormat, traits::BlakeTwo256}; use sp_state_machine::TestExternalities as CoreTestExternalities; use staging_node_cli::service::RuntimeExecutor; @@ -144,11 +144,11 @@ fn test_blocks( ) -> Vec<(Vec, Hash)> { let mut test_ext = new_test_ext(genesis_config); let mut block1_extrinsics = vec![CheckedExtrinsic { - signed: None, + format: ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: 0 }), }]; block1_extrinsics.extend((0..20).map(|i| CheckedExtrinsic { - signed: Some((alice(), signed_extra(i, 0))), + format: ExtrinsicFormat::Signed(alice(), tx_ext(i, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 1 * DOLLARS, diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs index 1cf71f8872f30d53a748a695c905f76f0e123f20..de4eef1944d41bc6f37d06819ad70ca0a6dad109 100644 --- a/substrate/bin/node/cli/benches/transaction_pool.rs +++ b/substrate/bin/node/cli/benches/transaction_pool.rs @@ -26,7 +26,7 @@ use node_primitives::AccountId; use sc_service::{ config::{ BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig, - PruningMode, TransactionPoolOptions, + PruningMode, RpcBatchRequestConfig, TransactionPoolOptions, }, BasePath, Configuration, Role, }; @@ -79,6 +79,9 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { 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, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/bin/node/cli/src/command.rs b/substrate/bin/node/cli/src/command.rs index dc28705c2aea9323d0ce84ae901d0206fe513efe..9645173202866a7334ab3eaacd948132d9b3c9d7 100644 --- a/substrate/bin/node/cli/src/command.rs +++ b/substrate/bin/node/cli/src/command.rs @@ -28,6 +28,7 @@ use node_primitives::Block; use sc_cli::{Result, SubstrateCli}; use sc_service::PartialComponents; use sp_keyring::Sr25519Keyring; +use sp_runtime::traits::HashingFor; use std::sync::Arc; @@ -106,7 +107,7 @@ pub fn run() -> Result<()> { ) } - cmd.run::(config) + cmd.run::, sp_statement_store::runtime_api::HostFunctions>(config) }, BenchmarkCmd::Block(cmd) => { // ensure that we keep the task manager alive diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 8f2aba6b44cd0a980771ec7d84eb383421551a6b..157f278fb5345526727f812b3bc5efec5d782000 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -107,18 +107,21 @@ pub fn create_extrinsic( .map(|c| c / 2) .unwrap_or(2) as u64; let tip = 0; - let extra: kitchensink_runtime::SignedExtra = + let tx_ext: kitchensink_runtime::TxExtension = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal( - period, - best_block.saturated_into(), - )), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::mortal( + period, + best_block.saturated_into(), + )), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + ) + .into(), pallet_skip_feeless_payment::SkipCheckIfFeeless::from( pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::< kitchensink_runtime::Runtime, @@ -128,15 +131,17 @@ pub fn create_extrinsic( let raw_payload = kitchensink_runtime::SignedPayload::from_raw( function.clone(), - extra.clone(), + tx_ext.clone(), ( - (), - kitchensink_runtime::VERSION.spec_version, - kitchensink_runtime::VERSION.transaction_version, - genesis_hash, - best_hash, - (), - (), + ( + (), + kitchensink_runtime::VERSION.spec_version, + kitchensink_runtime::VERSION.transaction_version, + genesis_hash, + best_hash, + (), + (), + ), (), ), ); @@ -146,7 +151,7 @@ pub fn create_extrinsic( function, sp_runtime::AccountId32::from(sender.public()).into(), kitchensink_runtime::Signature::Sr25519(signature), - extra, + tx_ext, ) } @@ -791,7 +796,7 @@ mod tests { use codec::Encode; use kitchensink_runtime::{ constants::{currency::CENTS, time::SLOT_DURATION}, - Address, BalancesCall, RuntimeCall, UncheckedExtrinsic, + Address, BalancesCall, RuntimeCall, TxExtension, UncheckedExtrinsic, }; use node_primitives::{Block, DigestItem, Signature}; use sc_client_api::BlockBackend; @@ -993,25 +998,31 @@ mod tests { let tx_payment = pallet_skip_feeless_payment::SkipCheckIfFeeless::from( pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(0, None), ); - let extra = ( - check_non_zero_sender, - check_spec_version, - check_tx_version, - check_genesis, - check_era, - check_nonce, - check_weight, + let tx_ext: TxExtension = ( + ( + check_non_zero_sender, + check_spec_version, + check_tx_version, + check_genesis, + check_era, + check_nonce, + check_weight, + ) + .into(), tx_payment, ); let raw_payload = SignedPayload::from_raw( function, - extra, - ((), spec_version, transaction_version, genesis_hash, genesis_hash, (), (), ()), + tx_ext, + ( + ((), spec_version, transaction_version, genesis_hash, genesis_hash, (), ()), + (), + ), ); let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let (function, extra, _) = raw_payload.deconstruct(); + let (function, tx_ext, _) = raw_payload.deconstruct(); index += 1; - UncheckedExtrinsic::new_signed(function, from.into(), signature.into(), extra) + UncheckedExtrinsic::new_signed(function, from.into(), signature.into(), tx_ext) .into() }, ); diff --git a/substrate/bin/node/cli/tests/basic.rs b/substrate/bin/node/cli/tests/basic.rs index 525ab2e39c1287edcf235fe3ac6381e3f0fc8e10..5fcb2295ef009adaeb84017534cf2965917405b9 100644 --- a/substrate/bin/node/cli/tests/basic.rs +++ b/substrate/bin/node/cli/tests/basic.rs @@ -67,7 +67,7 @@ fn transfer_fee(extrinsic: &E) -> Balance { fn xt() -> UncheckedExtrinsic { sign(CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)), function: RuntimeCall::Balances(default_transfer_call()), }) } @@ -84,11 +84,11 @@ fn changes_trie_block() -> (Vec, Hash) { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 69 * DOLLARS, @@ -111,11 +111,11 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time1 }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 69 * DOLLARS, @@ -131,18 +131,18 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { block1.1, vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time2 }), }, CheckedExtrinsic { - signed: Some((bob(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(bob(), tx_ext(0, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: alice().into(), value: 5 * DOLLARS, }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(1, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(1, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 15 * DOLLARS, @@ -166,11 +166,11 @@ fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time * 1000 }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(nonce, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(nonce, 0)), function: RuntimeCall::System(frame_system::Call::remark { remark: vec![0; size] }), }, ], @@ -677,11 +677,11 @@ fn deploying_wasm_contract_should_work() { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(0, 0)), function: RuntimeCall::Contracts(pallet_contracts::Call::instantiate_with_code::< Runtime, > { @@ -694,7 +694,7 @@ fn deploying_wasm_contract_should_work() { }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(1, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(1, 0)), function: RuntimeCall::Contracts(pallet_contracts::Call::call:: { dest: sp_runtime::MultiAddress::Id(addr.clone()), value: 10, diff --git a/substrate/bin/node/cli/tests/common.rs b/substrate/bin/node/cli/tests/common.rs index 9019594ff627f2aae89f58e837f55341b6793df0..2d74cdd5a0418aa7f93d7ebadfe3598036fb38ab 100644 --- a/substrate/bin/node/cli/tests/common.rs +++ b/substrate/bin/node/cli/tests/common.rs @@ -112,7 +112,7 @@ pub fn executor_call( let heap_pages = t.storage(sp_core::storage::well_known_keys::HEAP_PAGES); let runtime_code = RuntimeCode { code_fetcher: &sp_core::traits::WrappedRuntimeCode(code.as_slice().into()), - hash: sp_core::blake2_256(&code).to_vec(), + hash: sp_crypto_hashing::blake2_256(&code).to_vec(), heap_pages: heap_pages.and_then(|hp| Decode::decode(&mut &hp[..]).ok()), }; sp_tracing::try_init_simple(); diff --git a/substrate/bin/node/cli/tests/fees.rs b/substrate/bin/node/cli/tests/fees.rs index 8c7b3c873157770a8f156a019aa7e43e66460bae..9d6407067a37a51791a9d74ed987d1928926442e 100644 --- a/substrate/bin/node/cli/tests/fees.rs +++ b/substrate/bin/node/cli/tests/fees.rs @@ -54,11 +54,11 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time1 }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(0, 0)), function: RuntimeCall::Sudo(pallet_sudo::Call::sudo { call: Box::new(RuntimeCall::RootTesting( pallet_root_testing::Call::fill_block { ratio: Perbill::from_percent(60) }, @@ -77,11 +77,11 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { block1.1, vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time2 }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(1, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(1, 0)), function: RuntimeCall::System(frame_system::Call::remark { remark: vec![0; 1] }), }, ], @@ -147,7 +147,7 @@ fn transaction_fee_is_correct() { let tip = 1_000_000; let xt = sign(CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, tip))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, tip)), function: RuntimeCall::Balances(default_transfer_call()), }); @@ -211,7 +211,10 @@ fn block_weight_capacity_report() { let num_transfers = block_number * factor; let mut xts = (0..num_transfers) .map(|i| CheckedExtrinsic { - signed: Some((charlie(), signed_extra(nonce + i as Nonce, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed( + charlie(), + tx_ext(nonce + i as Nonce, 0), + ), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 0, @@ -222,7 +225,7 @@ fn block_weight_capacity_report() { xts.insert( 0, CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time * 1000 }), }, ); @@ -285,13 +288,16 @@ fn block_length_capacity_report() { previous_hash, vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time * 1000, }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(nonce, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed( + charlie(), + tx_ext(nonce, 0), + ), function: RuntimeCall::System(frame_system::Call::remark { remark: vec![0u8; (block_number * factor) as usize], }), diff --git a/substrate/bin/node/cli/tests/res/default_genesis_config.json b/substrate/bin/node/cli/tests/res/default_genesis_config.json index 1465a6497cadb3d79f75910dc2e084c168559c62..e21fbb47da8c4619e0923c85bf3470828cd80b23 100644 --- a/substrate/bin/node/cli/tests/res/default_genesis_config.json +++ b/substrate/bin/node/cli/tests/res/default_genesis_config.json @@ -2,7 +2,13 @@ "system": {}, "babe": { "authorities": [], - "epochConfig": null + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } }, "indices": { "indices": [] diff --git a/substrate/bin/node/cli/tests/submit_transaction.rs b/substrate/bin/node/cli/tests/submit_transaction.rs index 5cbb0103d471b96902bb341bcd796e6cf09eac76..f3a5bac8fb52bd06d49703faeeaa18ff724a977b 100644 --- a/substrate/bin/node/cli/tests/submit_transaction.rs +++ b/substrate/bin/node/cli/tests/submit_transaction.rs @@ -130,8 +130,8 @@ fn should_submit_signed_twice_from_the_same_account() { // now check that the transaction nonces are not equal let s = state.read(); fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { - let extra = tx.signature.unwrap().2; - extra.5 + let extra = tx.preamble.to_signed().unwrap().2; + (extra.0).5 } let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap()); @@ -179,8 +179,8 @@ fn should_submit_signed_twice_from_all_accounts() { // now check that the transaction nonces are not equal let s = state.read(); fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { - let extra = tx.signature.unwrap().2; - extra.5 + let extra = tx.preamble.to_signed().unwrap().2; + (extra.0).5 } let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap()); @@ -236,7 +236,7 @@ fn submitted_transaction_should_be_valid() { let source = TransactionSource::External; let extrinsic = UncheckedExtrinsic::decode(&mut &*tx0).unwrap(); // add balance to the account - let author = extrinsic.signature.clone().unwrap().0; + let author = extrinsic.preamble.clone().to_signed().clone().unwrap().0; let address = Indices::lookup(author).unwrap(); let data = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() }; let account = frame_system::AccountInfo { providers: 1, data, ..Default::default() }; diff --git a/substrate/bin/node/inspect/Cargo.toml b/substrate/bin/node/inspect/Cargo.toml index 860295b055341422e04fa669860aac5674953acf..7db6e3efd3a0fccc7ddd624e6ecb029c82637c03 100644 --- a/substrate/bin/node/inspect/Cargo.toml +++ b/substrate/bin/node/inspect/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "staging-node-inspect" -version = "0.9.0-dev" +version = "0.12.0" authors.workspace = true description = "Substrate node block inspection tool." edition.workspace = true @@ -15,9 +15,9 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.6.1" } -thiserror = "1.0" +thiserror = { workspace = true } sc-cli = { path = "../../../client/cli" } sc-client-api = { path = "../../../client/api" } sc-service = { path = "../../../client/service", default-features = false } diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index 66bd6e9a0a53decb48a694c0dee791c922c884f8..894dbf0da85ca56d412087adf01fa12c7983ae7a 100644 --- a/substrate/bin/node/rpc/Cargo.toml +++ b/substrate/bin/node/rpc/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } node-primitives = { path = "../primitives" } pallet-transaction-payment-rpc = { path = "../../../frame/transaction-payment/rpc" } mmr-rpc = { path = "../../../client/merkle-mountain-range/rpc" } diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 4bb5fed2b09a210ff4915609b1b825746ed4f514..bdef42474d09b7cb23d53b7574e8a4186c0429c1 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -25,8 +25,8 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = ] } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } static_assertions = "1.1.0" -log = { version = "0.4.17", default-features = false } -serde_json = { version = "1.0.111", default-features = false, features = ["alloc", "arbitrary_precision"] } +log = { workspace = true } +serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true } # pallet-asset-conversion: turn on "num-traits" feature primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "num-traits", "scale-info"] } @@ -88,6 +88,7 @@ pallet-election-provider-support-benchmarking = { path = "../../../frame/electio pallet-elections-phragmen = { path = "../../../frame/elections-phragmen", default-features = false } pallet-example-tasks = { path = "../../../frame/examples/tasks", default-features = false } pallet-fast-unstake = { path = "../../../frame/fast-unstake", default-features = false } +pallet-migrations = { path = "../../../frame/migrations", default-features = false } pallet-nis = { path = "../../../frame/nis", default-features = false } pallet-grandpa = { path = "../../../frame/grandpa", default-features = false } pallet-im-online = { path = "../../../frame/im-online", default-features = false } @@ -142,6 +143,7 @@ pallet-vesting = { path = "../../../frame/vesting", default-features = false } pallet-whitelist = { path = "../../../frame/whitelist", default-features = false } pallet-tx-pause = { path = "../../../frame/tx-pause", default-features = false } pallet-safe-mode = { path = "../../../frame/safe-mode", default-features = false } +pallet-parameters = { path = "../../../frame/parameters", default-features = false } [build-dependencies] substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true } @@ -197,6 +199,7 @@ std = [ "pallet-lottery/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-migrations/std", "pallet-mixnet/std", "pallet-mmr/std", "pallet-multisig/std", @@ -209,6 +212,7 @@ std = [ "pallet-nomination-pools/std", "pallet-offences-benchmarking?/std", "pallet-offences/std", + "pallet-parameters/std", "pallet-preimage/std", "pallet-proxy/std", "pallet-ranked-collective/std", @@ -272,6 +276,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-alliance/runtime-benchmarks", + "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-asset-rate/runtime-benchmarks", "pallet-asset-tx-payment/runtime-benchmarks", @@ -300,6 +305,7 @@ runtime-benchmarks = [ "pallet-lottery/runtime-benchmarks", "pallet-membership/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", + "pallet-migrations/runtime-benchmarks", "pallet-mixnet/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", @@ -310,6 +316,7 @@ runtime-benchmarks = [ "pallet-nomination-pools/runtime-benchmarks", "pallet-offences-benchmarking/runtime-benchmarks", "pallet-offences/runtime-benchmarks", + "pallet-parameters/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-ranked-collective/runtime-benchmarks", @@ -327,6 +334,7 @@ runtime-benchmarks = [ "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-tips/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-transaction-storage/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-tx-pause/runtime-benchmarks", @@ -378,6 +386,7 @@ try-runtime = [ "pallet-lottery/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-migrations/try-runtime", "pallet-mixnet/try-runtime", "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", @@ -386,6 +395,7 @@ try-runtime = [ "pallet-nis/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", + "pallet-parameters/try-runtime", "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", "pallet-ranked-collective/try-runtime", diff --git a/substrate/bin/node/runtime/src/impls.rs b/substrate/bin/node/runtime/src/impls.rs index 7ff52a758b3dd756dc4536df5928d3b2bb68a148..34f043b33a4edfe2d8cdbe154896e937826110ca 100644 --- a/substrate/bin/node/runtime/src/impls.rs +++ b/substrate/bin/node/runtime/src/impls.rs @@ -30,8 +30,8 @@ use pallet_identity::legacy::IdentityField; use sp_std::prelude::*; use crate::{ - AccountId, AllianceMotion, Assets, Authorship, Balances, Hash, NegativeImbalance, Runtime, - RuntimeCall, + AccountId, AllianceCollective, AllianceMotion, Assets, Authorship, Balances, Hash, + NegativeImbalance, Runtime, RuntimeCall, }; pub struct Author; @@ -107,7 +107,7 @@ impl ProposalProvider for AllianceProposalProvider } fn proposal_of(proposal_hash: Hash) -> Option { - AllianceMotion::proposal_of(proposal_hash) + pallet_collective::ProposalOf::::get(proposal_hash) } } @@ -276,7 +276,7 @@ mod multiplier_tests { let next = runtime_multiplier_update(fm); fm = next; if fm == min_multiplier() { - break + break; } iterations += 1; } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index d7598adb5a22d6024895fb05c844aeef4c425192..18ce13cff8015ea83f25703888837283ce270281 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -30,6 +30,7 @@ use frame_election_provider_support::{ use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, + dynamic_params::{dynamic_pallet_params, dynamic_params}, genesis_builder_helper::{build_config, create_default_config}, instances::{Instance1, Instance2}, ord_parameter_types, @@ -44,9 +45,9 @@ use frame_support::{ GetSalary, PayFromAccount, }, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, - EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, - KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing, OnUnbalanced, - WithdrawReasons, + EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth, + InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing, + OnUnbalanced, WithdrawReasons, }, weights::{ constants::{ @@ -309,6 +310,7 @@ impl frame_system::Config for Runtime { type SystemWeightInfo = frame_system::weights::SubstrateWeight; type SS58Prefix = ConstU16<42>; type MaxConsumers = ConstU32<16>; + type MultiBlockMigrator = MultiBlockMigrations; } impl pallet_insecure_randomness_collective_flip::Config for Runtime {} @@ -457,9 +459,6 @@ impl pallet_glutton::Config for Runtime { } parameter_types! { - pub const PreimageBaseDeposit: Balance = 1 * DOLLARS; - // One cent: $10,000 / MB - pub const PreimageByteDeposit: Balance = 1 * CENTS; pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); } @@ -472,7 +471,11 @@ impl pallet_preimage::Config for Runtime { AccountId, Balances, PreimageHoldReason, - LinearStoragePrice, + LinearStoragePrice< + dynamic_params::storage::BaseDeposit, + dynamic_params::storage::ByteDeposit, + Balance, + >, >; } @@ -533,7 +536,6 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = ConstU32<1>; - type MaxHolds = ConstU32<6>; } parameter_types! { @@ -558,6 +560,7 @@ impl pallet_transaction_payment::Config for Runtime { MinimumMultiplier, MaximumMultiplier, >; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } impl pallet_asset_tx_payment::Config for Runtime { @@ -567,6 +570,9 @@ impl pallet_asset_tx_payment::Config for Runtime { pallet_assets::BalanceToAssetBalance, CreditToBlockAuthor, >; + type WeightInfo = pallet_asset_tx_payment::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetTxHelper; } impl pallet_asset_conversion_tx_payment::Config for Runtime { @@ -577,6 +583,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { AssetConversion, Native, >; + type WeightInfo = pallet_asset_conversion_tx_payment::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetConversionTxHelper; } impl pallet_skip_feeless_payment::Config for Runtime { @@ -1014,11 +1023,17 @@ impl pallet_referenda::Config for Runtime { impl pallet_ranked_collective::Config for Runtime { type WeightInfo = pallet_ranked_collective::weights::SubstrateWeight; type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRoot; + type RemoveOrigin = Self::DemoteOrigin; type PromoteOrigin = EnsureRootWithSuccess>; type DemoteOrigin = EnsureRootWithSuccess>; + type ExchangeOrigin = EnsureRootWithSuccess>; type Polls = RankedPolls; type MinRankOfClass = traits::Identity; type VoteWeight = pallet_ranked_collective::Geometric; + type MemberSwappedHandler = (CoreFellowship, Salary); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = (CoreFellowship, Salary); } impl pallet_remark::Config for Runtime { @@ -1321,9 +1336,6 @@ impl pallet_tips::Config for Runtime { } parameter_types! { - pub const DepositPerItem: Balance = deposit(1, 0); - pub const DepositPerByte: Balance = deposit(0, 1); - pub const DefaultDepositLimit: Balance = deposit(1024, 1024 * 1024); pub Schedule: pallet_contracts::Schedule = Default::default(); pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30); } @@ -1341,9 +1353,9 @@ impl pallet_contracts::Config for Runtime { /// change because that would break already deployed contracts. The `Call` structure itself /// is not allowed to change the indices of existing pallets, too. type CallFilter = Nothing; - type DepositPerItem = DepositPerItem; - type DepositPerByte = DepositPerByte; - type DefaultDepositLimit = DefaultDepositLimit; + type DepositPerItem = dynamic_params::contracts::DepositPerItem; + type DepositPerByte = dynamic_params::contracts::DepositPerByte; + type DefaultDepositLimit = dynamic_params::contracts::DefaultDepositLimit; type CallStack = [pallet_contracts::Frame; 5]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; @@ -1353,6 +1365,8 @@ impl pallet_contracts::Config for Runtime { type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type RuntimeHoldReason = RuntimeHoldReason; #[cfg(not(feature = "runtime-benchmarks"))] @@ -1363,6 +1377,7 @@ impl pallet_contracts::Config for Runtime { type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type Debug = (); type Environment = (); + type ApiVersion = (); type Xcm = (); } @@ -1401,29 +1416,33 @@ where // so the actual block number is `n`. .saturating_sub(1); let era = Era::mortal(period, current_block); - let extra = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(era), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), + let tx_ext: TxExtension = ( + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(era), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + ) + .into(), pallet_skip_feeless_payment::SkipCheckIfFeeless::from( pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::::from( tip, None, ), ), ); - let raw_payload = SignedPayload::new(call, extra) + + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; let address = Indices::unlookup(account); - let (call, extra, _) = raw_payload.deconstruct(); - Some((call, (address, signature, extra))) + let (call, tx_ext, _) = raw_payload.deconstruct(); + Some((call, (address, signature, tx_ext))) } } @@ -1901,6 +1920,7 @@ impl pallet_state_trie_migration::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ControlOrigin = EnsureRoot; type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; type MaxKeyLen = MigrationMaxKeyLen; type SignedDepositPerItem = MigrationSignedDepositPerItem; type SignedDepositBase = MigrationSignedDepositBase; @@ -2001,6 +2021,25 @@ impl pallet_statement::Config for Runtime { type MaxAllowedBytes = MaxAllowedBytes; } +parameter_types! { + pub MbmServiceWeight: Weight = Perbill::from_percent(80) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_migrations::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = (); + // Benchmarks need mocked migrations to guarantee that they succeed. + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = (); + type FailedMigrationHandler = frame_support::migrations::FreezeChainOnFailedMigration; + type MaxServiceWeight = MbmServiceWeight; + type WeightInfo = pallet_migrations::weights::SubstrateWeight; +} + parameter_types! { pub const BrokerPalletId: PalletId = PalletId(*b"py/broke"); } @@ -2022,7 +2061,7 @@ pub struct CoretimeProvider; impl CoretimeInterface for CoretimeProvider { type AccountId = AccountId; type Balance = Balance; - type RealyChainBlockNumberProvider = System; + type RelayChainBlockNumberProvider = System; fn request_core_count(_count: CoreIndex) {} fn request_revenue_info_at(_when: u32) {} fn credit_account(_who: Self::AccountId, _amount: Self::Balance) {} @@ -2082,6 +2121,81 @@ impl pallet_mixnet::Config for Runtime { type MinMixnodes = ConstU32<7>; // Low to allow small testing networks } +/// Dynamic parameters that can be changed at runtime through the +/// `pallet_parameters::set_parameter`. +#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::)] +pub mod dynamic_params { + use super::*; + + #[dynamic_pallet_params] + #[codec(index = 0)] + pub mod storage { + /// Configures the base deposit of storing some data. + #[codec(index = 0)] + pub static BaseDeposit: Balance = 1 * DOLLARS; + + /// Configures the per-byte deposit of storing some data. + #[codec(index = 1)] + pub static ByteDeposit: Balance = 1 * CENTS; + } + + #[dynamic_pallet_params] + #[codec(index = 1)] + pub mod contracts { + #[codec(index = 0)] + pub static DepositPerItem: Balance = deposit(1, 0); + + #[codec(index = 1)] + pub static DepositPerByte: Balance = deposit(0, 1); + + #[codec(index = 2)] + pub static DefaultDepositLimit: Balance = deposit(1024, 1024 * 1024); + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default for RuntimeParameters { + fn default() -> Self { + RuntimeParameters::Storage(dynamic_params::storage::Parameters::BaseDeposit( + dynamic_params::storage::BaseDeposit, + Some(1 * DOLLARS), + )) + } +} + +pub struct DynamicParametersManagerOrigin; +impl EnsureOriginWithArg for DynamicParametersManagerOrigin { + type Success = (); + + fn try_origin( + origin: RuntimeOrigin, + key: &RuntimeParametersKey, + ) -> Result { + match key { + RuntimeParametersKey::Storage(_) => { + frame_system::ensure_root(origin.clone()).map_err(|_| origin)?; + return Ok(()) + }, + RuntimeParametersKey::Contract(_) => { + frame_system::ensure_root(origin.clone()).map_err(|_| origin)?; + return Ok(()) + }, + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_key: &RuntimeParametersKey) -> Result { + Ok(RuntimeOrigin::root()) + } +} + +impl pallet_parameters::Config for Runtime { + type RuntimeParameters = RuntimeParameters; + type RuntimeEvent = RuntimeEvent; + type AdminOrigin = DynamicParametersManagerOrigin; + type WeightInfo = (); +} + construct_runtime!( pub enum Runtime { System: frame_system, @@ -2098,11 +2212,6 @@ construct_runtime!( AssetConversionTxPayment: pallet_asset_conversion_tx_payment, ElectionProviderMultiPhase: pallet_election_provider_multi_phase, Staking: pallet_staking, - Beefy: pallet_beefy, - // MMR leaf construction must be before session in order to have leaf contents - // refer to block consistently. see substrate issue #11797 for details. - Mmr: pallet_mmr, - MmrLeaf: pallet_beefy_mmr, Session: pallet_session, Democracy: pallet_democracy, Council: pallet_collective::, @@ -2132,6 +2241,11 @@ construct_runtime!( Tips: pallet_tips, Assets: pallet_assets::, PoolAssets: pallet_assets::, + Beefy: pallet_beefy, + // MMR leaf construction must be after session in order to have a leaf's next_auth_set + // refer to block. See issue polkadot-fellows/runtimes#160 for details. + Mmr: pallet_mmr, + MmrLeaf: pallet_beefy_mmr, Lottery: pallet_lottery, Nis: pallet_nis, Uniques: pallet_uniques, @@ -2160,9 +2274,11 @@ construct_runtime!( TxPause: pallet_tx_pause, SafeMode: pallet_safe_mode, Statement: pallet_statement, + MultiBlockMigrations: pallet_migrations, Broker: pallet_broker, TasksExample: pallet_example_tasks, Mixnet: pallet_mixnet, + Parameters: pallet_parameters, SkipFeelessPayment: pallet_skip_feeless_payment, } ); @@ -2177,19 +2293,21 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. +/// The TransactionExtension to the basic transaction logic. /// /// When you change this, you **MUST** modify [`sign`] in `bin/node/testing/src/keyring.rs`! /// /// [`sign`]: <../../testing/src/keyring.rs.html> -pub type SignedExtra = ( - frame_system::CheckNonZeroSender, - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, +pub type TxExtension = ( + ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + ), pallet_skip_feeless_payment::SkipCheckIfFeeless< Runtime, pallet_asset_conversion_tx_payment::ChargeAssetTxPayment, @@ -2198,11 +2316,11 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -2257,6 +2375,106 @@ mod mmr { pub type Hashing = ::Hashing; } +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetConversionTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_conversion_tx_payment::BenchmarkHelperTrait + for AssetConversionTxHelper +{ + fn create_asset_id_parameter(seed: u32) -> (u32, u32) { + (seed, seed) + } + + fn setup_balances_and_pool(asset_id: u32, account: AccountId) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + account.clone().into(), /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = account.clone(); + let _ = Balances::deposit_creating(&lp_provider, ((u64::MAX as u128) * 100).into()); + assert_ok!(Assets::mint_into( + asset_id.into(), + &lp_provider, + ((u64::MAX as u128) * 100).into() + )); + + let token_native = Box::new(NativeOrWithId::Native); + let token_second = Box::new(NativeOrWithId::WithId(asset_id)); + + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider.clone()), + token_native.clone(), + token_second.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider.clone()), + token_native, + token_second, + u64::MAX.into(), // 1 desired + u64::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider, + )); + } +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_tx_payment::BenchmarkHelperTrait for AssetTxHelper { + fn create_asset_id_parameter(seed: u32) -> (u32, u32) { + (seed, seed) + } + + fn setup_balances_and_pool(asset_id: u32, account: AccountId) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + account.clone().into(), /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = account.clone(); + let _ = Balances::deposit_creating(&lp_provider, ((u64::MAX as u128) * 100).into()); + assert_ok!(Assets::mint_into( + asset_id.into(), + &lp_provider, + ((u64::MAX as u128) * 100).into() + )); + + let token_native = Box::new(NativeOrWithId::Native); + let token_second = Box::new(NativeOrWithId::WithId(asset_id)); + + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider.clone()), + token_native.clone(), + token_second.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider.clone()), + token_native, + token_second, + u64::MAX.into(), // 1 desired + u64::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider, + )); + } +} + #[cfg(feature = "runtime-benchmarks")] mod benches { frame_benchmarking::define_benchmarks!( @@ -2277,11 +2495,15 @@ mod benches { [tasks_example, TasksExample] [pallet_democracy, Democracy] [pallet_asset_conversion, AssetConversion] + [pallet_asset_conversion_tx_payment, AssetConversionTxPayment] + [pallet_asset_tx_payment, AssetTxPayment] + [pallet_transaction_payment, TransactionPayment] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] [pallet_election_provider_support_benchmarking, EPSBench::] [pallet_elections_phragmen, Elections] [pallet_fast_unstake, FastUnstake] [pallet_nis, Nis] + [pallet_parameters, Parameters] [pallet_grandpa, Grandpa] [pallet_identity, Identity] [pallet_im_online, ImOnline] @@ -2289,6 +2511,7 @@ mod benches { [pallet_lottery, Lottery] [pallet_membership, TechnicalMembership] [pallet_message_queue, MessageQueue] + [pallet_migrations, MultiBlockMigrations] [pallet_mmr, Mmr] [pallet_multisig, Multisig] [pallet_nomination_pools, NominationPoolsBench::] @@ -2308,6 +2531,7 @@ mod benches { [pallet_state_trie_migration, StateTrieMigration] [pallet_sudo, Sudo] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_timestamp, Timestamp] [pallet_tips, Tips] [pallet_transaction_storage, TransactionStorage] @@ -2334,7 +2558,7 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -2855,6 +3079,7 @@ impl_runtime_apis! { use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; @@ -2879,6 +3104,7 @@ impl_runtime_apis! { use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; diff --git a/substrate/bin/node/testing/Cargo.toml b/substrate/bin/node/testing/Cargo.toml index 76188ed446c0870229bcfebb772b6c6d98e09e64..31f8689d46ca30546482d438938c09457a433bf5 100644 --- a/substrate/bin/node/testing/Cargo.toml +++ b/substrate/bin/node/testing/Cargo.toml @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1" } fs_extra = "1" futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } tempfile = "3.1.0" frame-system = { path = "../../../frame/system" } node-cli = { package = "staging-node-cli", path = "../cli" } @@ -35,15 +35,13 @@ sc-client-api = { path = "../../../client/api" } sc-client-db = { path = "../../../client/db", features = ["rocksdb"] } sc-consensus = { path = "../../../client/consensus/common" } sc-executor = { path = "../../../client/executor" } -sc-service = { path = "../../../client/service", features = [ - "rocksdb", - "test-helpers", -] } +sc-service = { path = "../../../client/service", features = ["rocksdb", "test-helpers"] } sp-api = { path = "../../../primitives/api" } sp-block-builder = { path = "../../../primitives/block-builder" } sp-blockchain = { path = "../../../primitives/blockchain" } sp-consensus = { path = "../../../primitives/consensus/common" } sp-core = { path = "../../../primitives/core" } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } sp-inherents = { path = "../../../primitives/inherents" } sp-io = { path = "../../../primitives/io" } sp-keyring = { path = "../../../primitives/keyring" } diff --git a/substrate/bin/node/testing/src/bench.rs b/substrate/bin/node/testing/src/bench.rs index 98d3b968a358a3c46830760219dfe62f314ac496..ccf79eed8847d8a2acdb4b5f96c15d021f750b65 100644 --- a/substrate/bin/node/testing/src/bench.rs +++ b/substrate/bin/node/testing/src/bench.rs @@ -47,9 +47,11 @@ use sc_executor::{WasmExecutionMethod, WasmtimeInstantiationStrategy}; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_consensus::BlockOrigin; -use sp_core::{blake2_256, ed25519, sr25519, traits::SpawnNamed, Pair, Public}; +use sp_core::{ed25519, sr25519, traits::SpawnNamed, Pair, Public}; +use sp_crypto_hashing::blake2_256; use sp_inherents::InherentData; use sp_runtime::{ + generic::{ExtrinsicFormat, Preamble}, traits::{Block as BlockT, IdentifyAccount, Verify}, OpaqueExtrinsic, }; @@ -294,10 +296,10 @@ impl<'a> Iterator for BlockContentIterator<'a> { let signed = self.keyring.sign( CheckedExtrinsic { - signed: Some(( + format: ExtrinsicFormat::Signed( sender, - signed_extra(0, kitchensink_runtime::ExistentialDeposit::get() + 1), - )), + tx_ext(0, kitchensink_runtime::ExistentialDeposit::get() + 1), + ), function: match self.content.block_type { BlockType::RandomTransfersKeepAlive => RuntimeCall::Balances(BalancesCall::transfer_keep_alive { @@ -561,11 +563,11 @@ impl BenchKeyring { tx_version: u32, genesis_hash: [u8; 32], ) -> UncheckedExtrinsic { - match xt.signed { - Some((signed, extra)) => { + match xt.format { + ExtrinsicFormat::Signed(signed, tx_ext) => { let payload = ( xt.function, - extra.clone(), + tx_ext.clone(), spec_version, tx_version, genesis_hash, @@ -574,17 +576,26 @@ impl BenchKeyring { let key = self.accounts.get(&signed).expect("Account id not found in keyring"); let signature = payload.using_encoded(|b| { if b.len() > 256 { - key.sign(&sp_io::hashing::blake2_256(b)) + key.sign(&blake2_256(b)) } else { key.sign(b) } }); UncheckedExtrinsic { - signature: Some((sp_runtime::MultiAddress::Id(signed), signature, extra)), + preamble: Preamble::Signed( + sp_runtime::MultiAddress::Id(signed), + signature, + tx_ext, + ), function: payload.0, } }, - None => UncheckedExtrinsic { signature: None, function: xt.function }, + ExtrinsicFormat::Bare => + UncheckedExtrinsic { preamble: Preamble::Bare, function: xt.function }, + ExtrinsicFormat::General(tx_ext) => UncheckedExtrinsic { + preamble: sp_runtime::generic::Preamble::General(tx_ext), + function: xt.function, + }, } } diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs index 6ec21fbe09342d1f704a4ed1a6f79b6160c27a01..c79612d68444c8bd64ad18c3b0a74ceed176eff8 100644 --- a/substrate/bin/node/testing/src/genesis.rs +++ b/substrate/bin/node/testing/src/genesis.rs @@ -20,9 +20,8 @@ use crate::keyring::*; use kitchensink_runtime::{ - constants::currency::*, AccountId, AssetsConfig, BabeConfig, BalancesConfig, GluttonConfig, - GrandpaConfig, IndicesConfig, RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus, - StakingConfig, BABE_GENESIS_EPOCH_CONFIG, + constants::currency::*, AccountId, AssetsConfig, BalancesConfig, IndicesConfig, + RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus, StakingConfig, }; use sp_keyring::Ed25519Keyring; use sp_runtime::Perbill; @@ -47,7 +46,6 @@ pub fn config_endowed(extra_endowed: Vec) -> RuntimeGenesisConfig { endowed.extend(extra_endowed.into_iter().map(|endowed| (endowed, 100 * DOLLARS))); RuntimeGenesisConfig { - system: Default::default(), indices: IndicesConfig { indices: vec![] }, balances: BalancesConfig { balances: endowed }, session: SessionConfig { @@ -69,39 +67,8 @@ pub fn config_endowed(extra_endowed: Vec) -> RuntimeGenesisConfig { invulnerables: vec![alice(), bob(), charlie()], ..Default::default() }, - babe: BabeConfig { - authorities: vec![], - epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() - }, - grandpa: GrandpaConfig { authorities: vec![], _config: Default::default() }, - beefy: Default::default(), - im_online: Default::default(), - authority_discovery: Default::default(), - democracy: Default::default(), - council: Default::default(), - technical_committee: Default::default(), - technical_membership: Default::default(), - elections: Default::default(), - sudo: Default::default(), - treasury: Default::default(), society: SocietyConfig { pot: 0 }, - vesting: Default::default(), assets: AssetsConfig { assets: vec![(9, alice(), true, 1)], ..Default::default() }, - pool_assets: Default::default(), - transaction_storage: Default::default(), - transaction_payment: Default::default(), - alliance: Default::default(), - alliance_motion: Default::default(), - nomination_pools: Default::default(), - safe_mode: Default::default(), - tx_pause: Default::default(), - glutton: GluttonConfig { - compute: Default::default(), - storage: Default::default(), - trash_data_count: Default::default(), - ..Default::default() - }, - mixnet: Default::default(), + ..Default::default() } } diff --git a/substrate/bin/node/testing/src/keyring.rs b/substrate/bin/node/testing/src/keyring.rs index 6c885cc039a1511f3dcf33726d13e6d81ecbddd3..13daf915325db370698e563e8a23140136e84a44 100644 --- a/substrate/bin/node/testing/src/keyring.rs +++ b/substrate/bin/node/testing/src/keyring.rs @@ -19,12 +19,13 @@ //! Test accounts. use codec::Encode; -use kitchensink_runtime::{CheckedExtrinsic, SessionKeys, SignedExtra, UncheckedExtrinsic}; +use kitchensink_runtime::{CheckedExtrinsic, SessionKeys, TxExtension, UncheckedExtrinsic}; use node_cli::chain_spec::get_from_seed; use node_primitives::{AccountId, Balance, Nonce}; use sp_core::{ecdsa, ed25519, sr25519}; +use sp_crypto_hashing::blake2_256; use sp_keyring::AccountKeyring; -use sp_runtime::generic::Era; +use sp_runtime::generic::{Era, ExtrinsicFormat}; /// Alice's account id. pub fn alice() -> AccountId { @@ -69,15 +70,18 @@ pub fn session_keys_from_seed(seed: &str) -> SessionKeys { } /// Returns transaction extra. -pub fn signed_extra(nonce: Nonce, extra_fee: Balance) -> SignedExtra { +pub fn tx_ext(nonce: Nonce, extra_fee: Balance) -> TxExtension { ( - frame_system::CheckNonZeroSender::new(), - frame_system::CheckSpecVersion::new(), - frame_system::CheckTxVersion::new(), - frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::mortal(256, 0)), - frame_system::CheckNonce::from(nonce), - frame_system::CheckWeight::new(), + ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::mortal(256, 0)), + frame_system::CheckNonce::from(nonce), + frame_system::CheckWeight::new(), + ) + .into(), pallet_skip_feeless_payment::SkipCheckIfFeeless::from( pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(extra_fee, None), ), @@ -91,25 +95,37 @@ pub fn sign( tx_version: u32, genesis_hash: [u8; 32], ) -> UncheckedExtrinsic { - match xt.signed { - Some((signed, extra)) => { + match xt.format { + ExtrinsicFormat::Signed(signed, tx_ext) => { let payload = - (xt.function, extra.clone(), spec_version, tx_version, genesis_hash, genesis_hash); + (xt.function, tx_ext.clone(), spec_version, tx_version, genesis_hash, genesis_hash); let key = AccountKeyring::from_account_id(&signed).unwrap(); - let signature = payload - .using_encoded(|b| { - if b.len() > 256 { - key.sign(&sp_io::hashing::blake2_256(b)) - } else { - key.sign(b) - } - }) - .into(); + let signature = + payload + .using_encoded(|b| { + if b.len() > 256 { + key.sign(&blake2_256(b)) + } else { + key.sign(b) + } + }) + .into(); UncheckedExtrinsic { - signature: Some((sp_runtime::MultiAddress::Id(signed), signature, extra)), + preamble: sp_runtime::generic::Preamble::Signed( + sp_runtime::MultiAddress::Id(signed), + signature, + tx_ext, + ), function: payload.0, } }, - None => UncheckedExtrinsic { signature: None, function: xt.function }, + ExtrinsicFormat::Bare => UncheckedExtrinsic { + preamble: sp_runtime::generic::Preamble::Bare, + function: xt.function, + }, + ExtrinsicFormat::General(tx_ext) => UncheckedExtrinsic { + preamble: sp_runtime::generic::Preamble::General(tx_ext), + function: xt.function, + }, } } diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index 06a0a3a1a4a3638e99367e0fc3a595e01c763f2e..996372c8a0f8638ad4420dc8e21c8f1d418bb60e 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -23,8 +23,8 @@ name = "chain-spec-builder" crate-type = ["rlib"] [dependencies] -clap = { version = "4.4.18", features = ["derive"] } -log = "0.4.17" +clap = { version = "4.5.1", features = ["derive"] } +log = { workspace = true, default-features = true } sc-chain-spec = { path = "../../../client/chain-spec" } -serde_json = "1.0.111" -sp-tracing = { version = "10.0.0", path = "../../../primitives/tracing" } +serde_json = { workspace = true, default-features = true } +sp-tracing = { path = "../../../primitives/tracing" } diff --git a/substrate/bin/utils/subkey/Cargo.toml b/substrate/bin/utils/subkey/Cargo.toml index b53bae0b6a17441a90f778d9aa2a0ae60db89838..93b1368ca757ad8f383a4c0945c69d0055a8c1ff 100644 --- a/substrate/bin/utils/subkey/Cargo.toml +++ b/substrate/bin/utils/subkey/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "subkey" -version = "3.0.0" +version = "9.0.0" authors.workspace = true description = "Generate and restore keys for Substrate based chains such as Polkadot, Kusama and a growing number of parachains and Substrate based projects." edition.workspace = true @@ -20,5 +20,5 @@ path = "src/main.rs" name = "subkey" [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/bin/utils/subkey/README.md b/substrate/bin/utils/subkey/README.md index 3232c82958727555a44168351309b6039a708d89..a5f27cfd3707d6278ac9f78b727021a305a18457 100644 --- a/substrate/bin/utils/subkey/README.md +++ b/substrate/bin/utils/subkey/README.md @@ -91,7 +91,7 @@ SS58 addresses are: ### Json output -`subkey` can calso generate the output as *json*. This is useful for automation. +`subkey` can also generate the output as *json*. This is useful for automation. command: diff --git a/substrate/client/allocator/Cargo.toml b/substrate/client/allocator/Cargo.toml index ef13c1a4573f36665e42b84f3bcbca8436afe45d..2c268b548ea9c32fabdc82226c77e82a7ef59cea 100644 --- a/substrate/client/allocator/Cargo.toml +++ b/substrate/client/allocator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-allocator" -version = "4.1.0-dev" +version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = "0.4.17" -thiserror = "1.0.48" +log = { workspace = true, default-features = true } +thiserror = { workspace = true } sp-core = { path = "../../primitives/core" } sp-wasm-interface = { path = "../../primitives/wasm-interface" } diff --git a/substrate/client/api/Cargo.toml b/substrate/client/api/Cargo.toml index 8c50b872914419478c51ca5d498e45243b64082f..cd7b613e277f3952304d70c3bf63c36d09432bc2 100644 --- a/substrate/client/api/Cargo.toml +++ b/substrate/client/api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-client-api" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -22,7 +22,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = ] } fnv = "1.0.6" futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } sc-executor = { path = "../executor" } @@ -41,6 +41,6 @@ sp-storage = { path = "../../primitives/storage" } sp-trie = { path = "../../primitives/trie" } [dev-dependencies] -thiserror = "1.0.48" +thiserror = { workspace = true } sp-test-primitives = { path = "../../primitives/test-primitives" } substrate-test-runtime = { path = "../../test-utils/runtime" } diff --git a/substrate/client/api/src/client.rs b/substrate/client/api/src/client.rs index 46232c74539c6c230652a753b5fcd4b6761600d8..2de09840e4dfdc15e1f1d1acaa5f3e438de0caca 100644 --- a/substrate/client/api/src/client.rs +++ b/substrate/client/api/src/client.rs @@ -278,7 +278,7 @@ impl fmt::Display for UsageInfo { pub struct UnpinHandleInner { /// Hash of the block pinned by this handle hash: Block::Hash, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, } impl Debug for UnpinHandleInner { @@ -291,7 +291,7 @@ impl UnpinHandleInner { /// Create a new [`UnpinHandleInner`] pub fn new( hash: Block::Hash, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> Self { Self { hash, unpin_worker_sender } } @@ -299,12 +299,25 @@ impl UnpinHandleInner { impl Drop for UnpinHandleInner { fn drop(&mut self) { - if let Err(err) = self.unpin_worker_sender.unbounded_send(self.hash) { + if let Err(err) = + self.unpin_worker_sender.unbounded_send(UnpinWorkerMessage::Unpin(self.hash)) + { log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err); }; } } +/// Message that signals notification-based pinning actions to the pinning-worker. +/// +/// When the notification is dropped, an `Unpin` message should be sent to the worker. +#[derive(Debug)] +pub enum UnpinWorkerMessage { + /// Should be sent when a import or finality notification is created. + AnnouncePin(Block::Hash), + /// Should be sent when a import or finality notification is dropped. + Unpin(Block::Hash), +} + /// Keeps a specific block pinned while the handle is alive. /// Once the last handle instance for a given block is dropped, the /// block is unpinned in the [`Backend`](crate::backend::Backend::unpin_block). @@ -315,7 +328,7 @@ impl UnpinHandle { /// Create a new [`UnpinHandle`] pub fn new( hash: Block::Hash, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> UnpinHandle { UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender))) } @@ -353,7 +366,7 @@ impl BlockImportNotification { header: Block::Header, is_new_best: bool, tree_route: Option>>, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> Self { Self { hash, @@ -412,7 +425,7 @@ impl FinalityNotification { /// Create finality notification from finality summary. pub fn from_summary( mut summary: FinalizeSummary, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> FinalityNotification { let hash = summary.finalized.pop().unwrap_or_default(); FinalityNotification { @@ -436,7 +449,7 @@ impl BlockImportNotification { /// Create finality notification from finality summary. pub fn from_summary( summary: ImportSummary, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, ) -> BlockImportNotification { let hash = summary.hash; BlockImportNotification { diff --git a/substrate/client/api/src/notifications/tests.rs b/substrate/client/api/src/notifications/tests.rs index fba829b1cf9020034529b034f52015fb423608d8..7afdcbd95438f54a8a19eb03c29113288488eb54 100644 --- a/substrate/client/api/src/notifications/tests.rs +++ b/substrate/client/api/src/notifications/tests.rs @@ -18,7 +18,10 @@ use super::*; -use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper, H256 as Hash}; +use sp_runtime::{ + generic::UncheckedExtrinsic, + testing::{Block as RawBlock, H256 as Hash}, +}; use std::iter::{empty, Empty}; type TestChangeSet = ( @@ -50,7 +53,7 @@ impl PartialEq for StorageChangeSet { } } -type Block = RawBlock>; +type Block = RawBlock>; #[test] fn triggering_change_should_notify_wildcard_listeners() { diff --git a/substrate/client/authority-discovery/Cargo.toml b/substrate/client/authority-discovery/Cargo.toml index e7aead99c0249ebced1cb68fdc9923e23204cc8f..cdd4052f0b0998f283ede885f7e7de7509ecd169 100644 --- a/substrate/client/authority-discovery/Cargo.toml +++ b/substrate/client/authority-discovery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-authority-discovery" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true edition.workspace = true build = "build.rs" @@ -29,10 +29,10 @@ multihash = { version = "0.18.1", default-features = false, features = [ "sha2", "std", ] } -log = "0.4.17" +log = { workspace = true, default-features = true } prost = "0.12" rand = "0.8.5" -thiserror = "1.0" +thiserror = { workspace = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } sc-client-api = { path = "../api" } sc-network = { path = "../network" } diff --git a/substrate/client/basic-authorship/Cargo.toml b/substrate/client/basic-authorship/Cargo.toml index 926909ec7b764ed4f0c90c23f5b2a0c91c101311..51a06464d0d6d5df0ccf960509f4df46d8b12207 100644 --- a/substrate/client/basic-authorship/Cargo.toml +++ b/substrate/client/basic-authorship/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-basic-authorship" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" futures-timer = "3.0.1" -log = "0.4.17" +log = { workspace = true, default-features = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } sc-block-builder = { path = "../block-builder" } sc-proposer-metrics = { path = "../proposer-metrics" } diff --git a/substrate/client/basic-authorship/src/basic_authorship.rs b/substrate/client/basic-authorship/src/basic_authorship.rs index c07f3e639c3e15b572dbb470c1518b9ddfddae32..932287ac86577b0e1a9b2c057320aeda9d12319a 100644 --- a/substrate/client/basic-authorship/src/basic_authorship.rs +++ b/substrate/client/basic-authorship/src/basic_authorship.rs @@ -38,7 +38,7 @@ use sp_core::traits::SpawnNamed; use sp_inherents::InherentData; use sp_runtime::{ traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as HeaderT}, - Digest, Percent, SaturatedConversion, + Digest, ExtrinsicInclusionMode, Percent, SaturatedConversion, }; use std::{marker::PhantomData, pin::Pin, sync::Arc, time}; @@ -87,6 +87,22 @@ pub struct ProposerFactory { _phantom: PhantomData, } +impl Clone for ProposerFactory { + fn clone(&self) -> Self { + Self { + spawn_handle: self.spawn_handle.clone(), + client: self.client.clone(), + transaction_pool: self.transaction_pool.clone(), + metrics: self.metrics.clone(), + default_block_size_limit: self.default_block_size_limit, + soft_deadline_percent: self.soft_deadline_percent, + telemetry: self.telemetry.clone(), + include_proof_in_block_size_estimation: self.include_proof_in_block_size_estimation, + _phantom: self._phantom, + } + } +} + impl ProposerFactory { /// Create a new proposer factory. /// @@ -319,11 +335,12 @@ where self.apply_inherents(&mut block_builder, inherent_data)?; - // TODO call `after_inherents` and check if we should apply extrinsincs here - // - - let end_reason = - self.apply_extrinsics(&mut block_builder, deadline, block_size_limit).await?; + let mode = block_builder.extrinsic_inclusion_mode(); + let end_reason = match mode { + ExtrinsicInclusionMode::AllExtrinsics => + self.apply_extrinsics(&mut block_builder, deadline, block_size_limit).await?, + ExtrinsicInclusionMode::OnlyInherents => EndProposingReason::TransactionForbidden, + }; let (block, storage_changes, proof) = block_builder.build()?.into_inner(); let block_took = block_timer.elapsed(); diff --git a/substrate/client/block-builder/Cargo.toml b/substrate/client/block-builder/Cargo.toml index 4477f5f1d776c43ebd9129f4350216d6d638e5c9..e74d587d9b40fc57e0575b0fae7ecb0c5a177c15 100644 --- a/substrate/client/block-builder/Cargo.toml +++ b/substrate/client/block-builder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-block-builder" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/substrate/client/block-builder/src/lib.rs b/substrate/client/block-builder/src/lib.rs index 258e39d962b2de2397d85a54223c155e821ddfa3..2f22cd42591fc60bf1665d00a1c5b2b548ad3aea 100644 --- a/substrate/client/block-builder/src/lib.rs +++ b/substrate/client/block-builder/src/lib.rs @@ -37,7 +37,7 @@ use sp_core::traits::CallContext; use sp_runtime::{ legacy, traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One}, - Digest, + Digest, ExtrinsicInclusionMode, }; use std::marker::PhantomData; @@ -198,10 +198,12 @@ pub struct BlockBuilder<'a, Block: BlockT, C: ProvideRuntimeApi + 'a> { extrinsics: Vec, api: ApiRef<'a, C::Api>, call_api_at: &'a C, + /// Version of the [`BlockBuilderApi`] runtime API. version: u32, parent_hash: Block::Hash, /// The estimated size of the block header. estimated_header_size: usize, + extrinsic_inclusion_mode: ExtrinsicInclusionMode, } impl<'a, Block, C> BlockBuilder<'a, Block, C> @@ -244,9 +246,19 @@ where api.set_call_context(CallContext::Onchain); - api.initialize_block(parent_hash, &header)?; + let core_version = api + .api_version::>(parent_hash)? + .ok_or_else(|| Error::VersionInvalid("Core".to_string()))?; - let version = api + let extrinsic_inclusion_mode = if core_version >= 5 { + api.initialize_block(parent_hash, &header)? + } else { + #[allow(deprecated)] + api.initialize_block_before_version_5(parent_hash, &header)?; + ExtrinsicInclusionMode::AllExtrinsics + }; + + let bb_version = api .api_version::>(parent_hash)? .ok_or_else(|| Error::VersionInvalid("BlockBuilderApi".to_string()))?; @@ -254,12 +266,18 @@ where parent_hash, extrinsics: Vec::new(), api, - version, + version: bb_version, estimated_header_size, call_api_at, + extrinsic_inclusion_mode, }) } + /// The extrinsic inclusion mode of the runtime for this block. + pub fn extrinsic_inclusion_mode(&self) -> ExtrinsicInclusionMode { + self.extrinsic_inclusion_mode + } + /// Push onto the block's list of extrinsics. /// /// This will ensure the extrinsic can be validly executed (by executing it). diff --git a/substrate/client/chain-spec/Cargo.toml b/substrate/client/chain-spec/Cargo.toml index 765e3e9f7c77de1762bb01d93ddb5860d197e565..f2138c07d71ad4c524e12d33ffb73d453d707fbb 100644 --- a/substrate/client/chain-spec/Cargo.toml +++ b/substrate/client/chain-spec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-chain-spec" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -18,8 +18,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } memmap2 = "0.9.3" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } sc-client-api = { path = "../api" } sc-chain-spec-derive = { path = "derive" } sc-executor = { path = "../executor" } @@ -28,12 +28,13 @@ sc-network = { path = "../network" } sc-telemetry = { path = "../telemetry" } sp-blockchain = { path = "../../primitives/blockchain" } sp-core = { path = "../../primitives/core" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } sp-genesis-builder = { path = "../../primitives/genesis-builder" } sp-runtime = { path = "../../primitives/runtime" } sp-state-machine = { path = "../../primitives/state-machine" } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } array-bytes = { version = "6.1" } -docify = "0.2.0" +docify = "0.2.7" [dev-dependencies] substrate-test-runtime = { path = "../../test-utils/runtime" } diff --git a/substrate/client/chain-spec/derive/Cargo.toml b/substrate/client/chain-spec/derive/Cargo.toml index f9e291f897c9814fbff4d8de5e36657de230f240..521eee578ecae3b03cf86a3b4e3630bb7cd22f02 100644 --- a/substrate/client/chain-spec/derive/Cargo.toml +++ b/substrate/client/chain-spec/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-chain-spec-derive" -version = "4.0.0-dev" +version = "11.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -20,5 +20,5 @@ proc-macro = true [dependencies] proc-macro-crate = "3.0.0" proc-macro2 = "1.0.56" -quote = "1.0.28" -syn = "2.0.48" +quote = { workspace = true } +syn = { workspace = true } diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index fe8fcfda216e1fa86215daf93add2aa6adc78bd9..78e81e10d2b613d83c98e8b689cba2fd1356287c 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -1237,15 +1237,7 @@ mod tests { "TestName", "test", ChainType::Local, - move || substrate_test_runtime::RuntimeGenesisConfig { - babe: substrate_test_runtime::BabeConfig { - epoch_config: Some( - substrate_test_runtime::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION, - ), - ..Default::default() - }, - ..Default::default() - }, + || Default::default(), Vec::new(), None, None, diff --git a/substrate/client/chain-spec/src/genesis_block.rs b/substrate/client/chain-spec/src/genesis_block.rs index 6aa156a620a7933034643f8a7bdbf8e0d617ad97..3c7b9f64dcd6bc0babff662747571f9d31853eec 100644 --- a/substrate/client/chain-spec/src/genesis_block.rs +++ b/substrate/client/chain-spec/src/genesis_block.rs @@ -18,23 +18,25 @@ //! Tool for creating the genesis block. -use std::{collections::hash_map::DefaultHasher, marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, sync::Arc}; +use codec::Encode; use sc_client_api::{backend::Backend, BlockImportOperation}; use sc_executor::RuntimeVersionOf; use sp_core::storage::{well_known_keys, StateVersion, Storage}; use sp_runtime::{ - traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Zero}, + traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, Zero}, BuildStorage, }; /// Return the state version given the genesis storage and executor. -pub fn resolve_state_version_from_wasm( +pub fn resolve_state_version_from_wasm( storage: &Storage, executor: &E, ) -> sp_blockchain::Result where E: RuntimeVersionOf, + H: HashT, { if let Some(wasm) = storage.top.get(well_known_keys::CODE) { let mut ext = sp_state_machine::BasicExternalities::new_empty(); // just to read runtime version. @@ -43,12 +45,7 @@ where let runtime_code = sp_core::traits::RuntimeCode { code_fetcher: &code_fetcher, heap_pages: None, - hash: { - use std::hash::{Hash, Hasher}; - let mut state = DefaultHasher::new(); - wasm.hash(&mut state); - state.finish().to_le_bytes().to_vec() - }, + hash: ::hash(wasm).encode(), }; let runtime_version = RuntimeVersionOf::runtime_version(executor, &mut ext, &runtime_code) .map_err(|e| sp_blockchain::Error::VersionInvalid(e.to_string()))?; @@ -129,7 +126,8 @@ impl, E: RuntimeVersionOf> BuildGenesisBlock sp_blockchain::Result<(Block, Self::BlockImportOperation)> { let Self { genesis_storage, commit_genesis_state, backend, executor, _phantom } = self; - let genesis_state_version = resolve_state_version_from_wasm(&genesis_storage, &executor)?; + let genesis_state_version = + resolve_state_version_from_wasm::<_, HashingFor>(&genesis_storage, &executor)?; let mut op = backend.begin_operation()?; let state_root = op.set_genesis_state(genesis_storage, commit_genesis_state, genesis_state_version)?; diff --git a/substrate/client/chain-spec/src/genesis_config_builder.rs b/substrate/client/chain-spec/src/genesis_config_builder.rs index d6ef99fafdd0325b0136f3013453465f361bf2bb..c8b54f66be6f52ffb6900fafd2e8ec1652030976 100644 --- a/substrate/client/chain-spec/src/genesis_config_builder.rs +++ b/substrate/client/chain-spec/src/genesis_config_builder.rs @@ -62,7 +62,7 @@ where pub fn new(code: &'a [u8]) -> Self { GenesisConfigBuilderRuntimeCaller { code: code.into(), - code_hash: sp_core::blake2_256(code).to_vec(), + code_hash: sp_crypto_hashing::blake2_256(code).to_vec(), executor: WasmExecutor::<(sp_io::SubstrateHostFunctions, EHF)>::builder() .with_allow_missing_host_functions(true) .build(), @@ -81,7 +81,8 @@ where .0 } - /// Returns the default `GenesisConfig` provided by the `runtime`. + /// Returns a json representation of the default `RuntimeGenesisConfig` provided by the + /// `runtime`. /// /// Calls [`GenesisBuilder::create_default_config`](sp_genesis_builder::GenesisBuilder::create_default_config) in the `runtime`. pub fn get_default_config(&self) -> core::result::Result { @@ -94,7 +95,7 @@ where Ok(from_slice(&default_config[..]).expect("returned value is json. qed.")) } - /// Build the given `GenesisConfig` and returns the genesis state. + /// Builds `RuntimeGenesisConfig` from given json blob and returns the genesis state. /// /// Calls [`GenesisBuilder::build_config`](sp_genesis_builder::GenesisBuilder::build_config) /// provided by the `runtime`. @@ -111,25 +112,26 @@ where Ok(ext.into_storages()) } - /// Creates the genesis state by patching the default `GenesisConfig` and applying it. + /// Creates the genesis state by patching the default `RuntimeGenesisConfig`. /// - /// This function generates the `GenesisConfig` for the runtime by applying a provided JSON - /// patch. The patch modifies the default `GenesisConfig` allowing customization of the specific - /// keys. The resulting `GenesisConfig` is then deserialized from the patched JSON - /// representation and stored in the storage. + /// This function generates the `RuntimeGenesisConfig` for the runtime by applying a provided + /// JSON patch. The patch modifies the default `RuntimeGenesisConfig` allowing customization of + /// the specific keys. The resulting `RuntimeGenesisConfig` is then deserialized from the + /// patched JSON representation and stored in the storage. /// /// If the provided JSON patch is incorrect or the deserialization fails the error will be /// returned. /// - /// The patching process modifies the default `GenesisConfig` according to the following rules: + /// The patching process modifies the default `RuntimeGenesisConfig` according to the following + /// rules: /// 1. Existing keys in the default configuration will be overridden by the corresponding values /// in the patch. /// 2. If a key exists in the patch but not in the default configuration, it will be added to - /// the resulting `GenesisConfig`. + /// the resulting `RuntimeGenesisConfig`. /// 3. Keys in the default configuration that have null values in the patch will be removed from - /// the resulting `GenesisConfig`. This is helpful for changing enum variant value. + /// the resulting `RuntimeGenesisConfig`. This is helpful for changing enum variant value. /// - /// Please note that the patch may contain full `GenesisConfig`. + /// Please note that the patch may contain full `RuntimeGenesisConfig`. pub fn get_storage_for_patch(&self, patch: Value) -> core::result::Result { let mut config = self.get_default_config()?; crate::json_patch::merge(&mut config, patch); @@ -149,7 +151,7 @@ mod tests { ::new(substrate_test_runtime::wasm_binary_unwrap()) .get_default_config() .unwrap(); - let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; + let expected = r#"{"babe": {"authorities": [], "epochConfig": {"allowed_slots": "PrimaryAndSecondaryVRFSlots", "c": [1, 4]}}, "balances": {"balances": []}, "substrateTest": {"authorities": []}, "system": {}}"#; assert_eq!(from_str::(expected).unwrap(), config); } diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index 6a922e7b40b264505f76f4553fcaa0ae211e069a..eab5f789f29a0589d68c3c0aa86952c75f112afa 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -338,6 +338,7 @@ pub use self::{ GenesisBlockBuilder, }, genesis_config_builder::GenesisConfigBuilderRuntimeCaller, + json_patch::merge as json_merge, }; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; diff --git a/substrate/client/cli/Cargo.toml b/substrate/client/cli/Cargo.toml index d64973baf837f145f30f86cc5c304c67cfa957d6..0582018283c6e49882c71106d36f73d6ba7cedb2 100644 --- a/substrate/client/cli/Cargo.toml +++ b/substrate/client/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-cli" -version = "0.10.0-dev" +version = "0.36.0" authors.workspace = true description = "Substrate CLI interface." edition.workspace = true @@ -18,20 +18,20 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "6.1" chrono = "0.4.31" -clap = { version = "4.4.18", features = ["derive", "string", "wrap_help"] } +clap = { version = "4.5.1", features = ["derive", "string", "wrap_help"] } fdlimit = "0.3.0" futures = "0.3.21" itertools = "0.10.3" libp2p-identity = { version = "0.1.3", features = ["ed25519", "peerid"] } -log = "0.4.17" +log = { workspace = true, default-features = true } names = { version = "0.14.0", default-features = false } parity-scale-codec = "3.6.1" rand = "0.8.5" regex = "1.6.0" rpassword = "7.0.0" -serde = "1.0.195" -serde_json = "1.0.111" -thiserror = "1.0.48" +serde = { workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } bip39 = "2.0.0" tokio = { version = "1.22.0", features = ["parking_lot", "rt-multi-thread", "signal"] } sc-client-api = { path = "../api" } diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index 4ac10a03cd446ec0e6b2c6437841b1b4163a060e..221c32affd5a91d680f23ed11eb5f7a9a4051da2 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -25,16 +25,19 @@ use crate::{ }, CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams, RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, - RPC_DEFAULT_MAX_SUBS_PER_CONN, + RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN, }; use clap::Parser; use regex::Regex; use sc_service::{ - config::{BasePath, PrometheusConfig, TransactionPoolOptions}, + config::{BasePath, PrometheusConfig, RpcBatchRequestConfig, TransactionPoolOptions}, ChainSpec, Role, }; use sc_telemetry::TelemetryEndpoints; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + num::NonZeroU32, +}; /// The `run` command used to run a node. #[derive(Debug, Clone, Parser)] @@ -59,7 +62,7 @@ pub struct RunCmd { /// Not all RPC methods are safe to be exposed publicly. /// /// Use an RPC proxy server to filter out dangerous methods. More details: - /// . + /// . /// /// Use `--unsafe-rpc-external` to suppress the warning if you understand the risks. #[arg(long)] @@ -82,6 +85,15 @@ pub struct RunCmd { )] pub rpc_methods: RpcMethods, + /// RPC rate limiting (calls/minute) for each connection. + /// + /// This is disabled by default. + /// + /// For example `--rpc-rate-limit 10` will maximum allow + /// 10 calls per minute per connection. + #[arg(long)] + pub rpc_rate_limit: Option, + /// Set the maximum RPC request payload size for both HTTP and WS in megabytes. #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] pub rpc_max_request_size: u32, @@ -102,9 +114,28 @@ pub struct RunCmd { #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] pub rpc_max_connections: u32, - /// Specify browser *origins* allowed to access the HTTP and WS RPC servers. + /// The number of messages the RPC server is allowed to keep in memory. + /// + /// If the buffer becomes full then the server will not process + /// new messages until the connected client start reading the + /// underlying messages. + /// + /// This applies per connection which includes both + /// JSON-RPC methods calls and subscriptions. + #[arg(long, default_value_t = RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)] + pub rpc_message_buffer_capacity_per_connection: u32, + + /// Disable RPC batch requests + #[arg(long, alias = "rpc_no_batch_requests", conflicts_with_all = &["rpc_max_batch_request_len"])] + pub rpc_disable_batch_requests: bool, + + /// Limit the max length per RPC batch request + #[arg(long, conflicts_with_all = &["rpc_disable_batch_requests"], value_name = "LEN")] + pub rpc_max_batch_request_len: Option, + + /// Specify browser *origins* allowed to access the HTTP & WS RPC servers. /// - /// A comma-separated list of origins (`protocol://domain` or special `null` + /// A comma-separated list of origins (protocol://domain or special `null` /// value). Value of `all` will disable origin validation. Default is to /// allow localhost and origins. When running in /// `--dev` mode the default is to allow all origins. @@ -388,6 +419,26 @@ impl CliConfiguration for RunCmd { Ok(self.rpc_max_subscriptions_per_connection) } + fn rpc_buffer_capacity_per_connection(&self) -> Result { + Ok(self.rpc_message_buffer_capacity_per_connection) + } + + fn rpc_batch_config(&self) -> Result { + let cfg = if self.rpc_disable_batch_requests { + RpcBatchRequestConfig::Disabled + } else if let Some(l) = self.rpc_max_batch_request_len { + RpcBatchRequestConfig::Limit(l) + } else { + RpcBatchRequestConfig::Unlimited + }; + + Ok(cfg) + } + + fn rpc_rate_limit(&self) -> Result> { + Ok(self.rpc_rate_limit) + } + fn transaction_pool(&self, is_dev: bool) -> Result { Ok(self.pool_config.transaction_pool(is_dev)) } diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index 170173c8c28b12f0015b2266899971e9b021e897..5def9ce9b72620eb7942ac6ee68b16493f6b8053 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -28,12 +28,13 @@ use sc_service::{ config::{ BasePath, Configuration, DatabaseSource, KeystoreConfig, NetworkConfiguration, NodeKeyConfig, OffchainWorkerConfig, OutputFormat, PrometheusConfig, PruningMode, Role, - RpcMethods, TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, + RpcBatchRequestConfig, RpcMethods, TelemetryEndpoints, TransactionPoolOptions, + WasmExecutionMethod, }, BlocksPruning, ChainSpec, TracingReceiver, }; use sc_tracing::logging::LoggerBuilder; -use std::{net::SocketAddr, path::PathBuf}; +use std::{net::SocketAddr, num::NonZeroU32, path::PathBuf}; /// The maximum number of characters for a node name. pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; @@ -52,8 +53,11 @@ pub const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024; pub const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15; /// The default max response size in MB. pub const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 15; -/// The default number of connection.. +/// The default concurrent connection limit. pub const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 100; +/// The default number of messages the RPC server +/// is allowed to keep in memory per connection. +pub const RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN: u32 = 64; /// Default configuration values used by Substrate /// @@ -330,6 +334,21 @@ pub trait CliConfiguration: Sized { Ok(RPC_DEFAULT_MAX_SUBS_PER_CONN) } + /// The number of messages the RPC server is allowed to keep in memory per connection. + fn rpc_buffer_capacity_per_connection(&self) -> Result { + Ok(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN) + } + + /// RPC server batch request configuration. + fn rpc_batch_config(&self) -> Result { + Ok(RpcBatchRequestConfig::Unlimited) + } + + /// RPC rate limit configuration. + fn rpc_rate_limit(&self) -> Result> { + Ok(None) + } + /// Get the prometheus configuration (`None` if disabled) /// /// By default this is `None`. @@ -501,6 +520,9 @@ pub trait CliConfiguration: Sized { 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()?, prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, telemetry_endpoints, diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index 1707a76cbe78955ff6fb641866db07e6324c06e8..4201a0f4062fb0063621d85dd02feb0deade24bc 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -269,7 +269,10 @@ mod tests { 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, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/client/consensus/aura/Cargo.toml b/substrate/client/consensus/aura/Cargo.toml index 89a63a944166250cfa3fd6ca603ba9b7dc3fe4a3..213f75974da0c99cc5c39248a8a789c4989d8047 100644 --- a/substrate/client/consensus/aura/Cargo.toml +++ b/substrate/client/consensus/aura/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-aura" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true description = "Aura consensus algorithm for substrate" edition.workspace = true @@ -19,8 +19,8 @@ targets = ["x86_64-unknown-linux-gnu"] async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" -log = "0.4.17" -thiserror = "1.0" +log = { workspace = true, default-features = true } +thiserror = { workspace = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-block-builder = { path = "../../block-builder" } sc-client-api = { path = "../../api" } diff --git a/substrate/client/consensus/babe/Cargo.toml b/substrate/client/consensus/babe/Cargo.toml index 40c69d5780a537fb63c37601c0403712960c0bea..c98fb7112b7c25bd39d980cab20c188f30b42eff 100644 --- a/substrate/client/consensus/babe/Cargo.toml +++ b/substrate/client/consensus/babe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-babe" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true description = "BABE consensus algorithm for substrate" edition.workspace = true @@ -20,12 +20,12 @@ targets = ["x86_64-unknown-linux-gnu"] async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } num-bigint = "0.4.3" num-rational = "0.4.1" num-traits = "0.2.17" parking_lot = "0.12.1" -thiserror = "1.0" +thiserror = { workspace = true } fork-tree = { path = "../../../utils/fork-tree" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-client-api = { path = "../../api" } @@ -42,6 +42,7 @@ sp-consensus = { path = "../../../primitives/consensus/common" } sp-consensus-babe = { path = "../../../primitives/consensus/babe" } sp-consensus-slots = { path = "../../../primitives/consensus/slots" } sp-core = { path = "../../../primitives/core" } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } sp-inherents = { path = "../../../primitives/inherents" } sp-keystore = { path = "../../../primitives/keystore" } sp-runtime = { path = "../../../primitives/runtime" } diff --git a/substrate/client/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml index 753f8fbc821d0d747e8cfcb056aad134c1162c96..043b566673e7bd5aa581e3043dfa1280aa04d53f 100644 --- a/substrate/client/consensus/babe/rpc/Cargo.toml +++ b/substrate/client/consensus/babe/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-babe-rpc" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true description = "RPC extensions for the BABE consensus algorithm" edition.workspace = true @@ -16,10 +16,10 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } futures = "0.3.21" -serde = { version = "1.0.195", features = ["derive"] } -thiserror = "1.0" +serde = { features = ["derive"], workspace = true, default-features = true } +thiserror = { workspace = true } sc-consensus-babe = { path = ".." } sc-consensus-epochs = { path = "../../epochs" } sc-rpc-api = { path = "../../../rpc-api" } @@ -33,7 +33,7 @@ sp-keystore = { path = "../../../../primitives/keystore" } sp-runtime = { path = "../../../../primitives/runtime" } [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } tokio = "1.22.0" sc-consensus = { path = "../../common" } sc-keystore = { path = "../../../keystore" } diff --git a/substrate/client/consensus/babe/rpc/src/lib.rs b/substrate/client/consensus/babe/rpc/src/lib.rs index bffe026ea6ef6bd9efa282dd71b587aaf03471b3..a3e811baecffd6de1b9f94952be40d00d8b61de3 100644 --- a/substrate/client/consensus/babe/rpc/src/lib.rs +++ b/substrate/client/consensus/babe/rpc/src/lib.rs @@ -22,15 +22,15 @@ use std::{collections::HashMap, sync::Arc}; use futures::TryFutureExt; use jsonrpsee::{ - core::{async_trait, Error as JsonRpseeError, RpcResult}, + core::async_trait, proc_macros::rpc, - types::{error::CallError, ErrorObject}, + types::{ErrorObject, ErrorObjectOwned}, }; use serde::{Deserialize, Serialize}; use sc_consensus_babe::{authorship, BabeWorkerHandle}; use sc_consensus_epochs::Epoch as EpochT; -use sc_rpc_api::DenyUnsafe; +use sc_rpc_api::{DenyUnsafe, UnsafeRpcError}; use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppCrypto; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; @@ -48,7 +48,7 @@ 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")] - async fn epoch_authorship(&self) -> RpcResult>; + async fn epoch_authorship(&self) -> Result, Error>; } /// Provides RPC methods for interacting with Babe. @@ -89,7 +89,7 @@ where C::Api: BabeRuntimeApi, SC: SelectChain + Clone + 'static, { - async fn epoch_authorship(&self) -> RpcResult> { + async fn epoch_authorship(&self) -> Result, Error> { self.deny_unsafe.check_if_safe()?; let best_header = self.select_chain.best_chain().map_err(Error::SelectChain).await?; @@ -147,7 +147,7 @@ where } /// Holds information about the `slot`'s that can be claimed by a given key. -#[derive(Default, Debug, Deserialize, Serialize)] +#[derive(Clone, Default, Debug, Deserialize, Serialize)] pub struct EpochAuthorship { /// the array of primary slots that can be claimed primary: Vec, @@ -166,20 +166,26 @@ pub enum Error { /// Failed to fetch epoch data. #[error("Failed to fetch epoch data")] FetchEpoch, + /// Consensus error + #[error(transparent)] + Consensus(#[from] ConsensusError), + /// Errors that can be formatted as a String + #[error("{0}")] + StringError(String), + /// Call to an unsafe RPC was denied. + #[error(transparent)] + UnsafeRpcCalled(#[from] UnsafeRpcError), } -impl From for JsonRpseeError { +impl From for ErrorObjectOwned { fn from(error: Error) -> Self { - let error_code = match error { - Error::SelectChain(_) => 1, - Error::FetchEpoch => 2, - }; - - JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( - BABE_ERROR + error_code, - error.to_string(), - Some(format!("{:?}", error)), - ))) + match error { + Error::SelectChain(e) => ErrorObject::owned(BABE_ERROR + 1, e.to_string(), None::<()>), + Error::FetchEpoch => ErrorObject::owned(BABE_ERROR + 2, error.to_string(), None::<()>), + Error::Consensus(e) => ErrorObject::owned(BABE_ERROR + 3, e.to_string(), None::<()>), + Error::StringError(e) => ErrorObject::owned(BABE_ERROR + 4, e, None::<()>), + Error::UnsafeRpcCalled(e) => e.into(), + } } } @@ -251,10 +257,10 @@ mod tests { let api = babe_rpc.into_rpc(); let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#; - let (response, _) = api.raw_json_request(request).await.unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[1,2,4],"secondary_vrf":[]}},"id":1}"#; + 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}"#; - assert_eq!(&response.result, expected); + assert_eq!(response, expected); } #[tokio::test] @@ -263,9 +269,9 @@ mod tests { let api = babe_rpc.into_rpc(); let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params":[],"id":1}"#; - let (response, _) = api.raw_json_request(request).await.unwrap(); + 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}"#; - assert_eq!(&response.result, expected); + assert_eq!(response, expected); } } diff --git a/substrate/client/consensus/babe/src/authorship.rs b/substrate/client/consensus/babe/src/authorship.rs index fb1722398012b92701f66bcbe5c6c8c292d9084e..11f5233abc6b384ef034acaa1f26a1833aee124a 100644 --- a/substrate/client/consensus/babe/src/authorship.rs +++ b/substrate/client/consensus/babe/src/authorship.rs @@ -27,7 +27,6 @@ use sp_consensus_babe::{ make_vrf_sign_data, AuthorityId, BabeAuthorityWeight, Randomness, Slot, }; use sp_core::{ - blake2_256, crypto::{ByteArray, Wraps}, U256, }; @@ -109,7 +108,7 @@ pub(super) fn secondary_slot_author( return None } - let rand = U256::from((randomness, slot).using_encoded(blake2_256)); + let rand = U256::from((randomness, slot).using_encoded(sp_crypto_hashing::blake2_256)); let authorities_len = U256::from(authorities.len()); let idx = rand % authorities_len; diff --git a/substrate/client/consensus/beefy/Cargo.toml b/substrate/client/consensus/beefy/Cargo.toml index c54452faebe9659b4d40476fd7d5db0637e5cd54..8552a49002239aaeaf232c0aebf4180cec207337 100644 --- a/substrate/client/consensus/beefy/Cargo.toml +++ b/substrate/client/consensus/beefy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-beefy" -version = "4.0.0-dev" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -18,9 +18,9 @@ async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } fnv = "1.0.6" futures = "0.3" -log = "0.4" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" -thiserror = "1.0" +thiserror = { workspace = true } wasm-timer = "0.2.5" prometheus = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-client-api = { path = "../../api" } @@ -36,6 +36,7 @@ sp-blockchain = { path = "../../../primitives/blockchain" } sp-consensus = { path = "../../../primitives/consensus/common" } sp-consensus-beefy = { path = "../../../primitives/consensus/beefy" } sp-core = { path = "../../../primitives/core" } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } sp-keystore = { path = "../../../primitives/keystore" } sp-mmr-primitives = { path = "../../../primitives/merkle-mountain-range" } sp-runtime = { path = "../../../primitives/runtime" } @@ -43,7 +44,7 @@ tokio = "1.22.0" [dev-dependencies] -serde = "1.0.195" +serde = { workspace = true, default-features = true } tempfile = "3.1.0" sc-block-builder = { path = "../../block-builder" } sc-network-test = { path = "../../network/test" } @@ -51,3 +52,12 @@ sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa" } sp-keyring = { path = "../../../primitives/keyring" } sp-tracing = { path = "../../../primitives/tracing" } substrate-test-runtime-client = { path = "../../../test-utils/runtime/client" } + +[features] +# This feature adds BLS crypto primitives. It should not be used in production since +# the BLS implementation and interface may still be subject to significant change. +bls-experimental = [ + "sp-application-crypto/bls-experimental", + "sp-consensus-beefy/bls-experimental", + "sp-core/bls-experimental", +] diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index 198d3d81642203cfe2bb1ac909fa891d26e175a0..bb2ae4a08966707fd93afd48b625ccc4f760762b 100644 --- a/substrate/client/consensus/beefy/rpc/Cargo.toml +++ b/substrate/client/consensus/beefy/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-beefy-rpc" -version = "4.0.0-dev" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -14,11 +14,11 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } futures = "0.3.21" -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } -log = "0.4" +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } +log = { workspace = true, default-features = true } parking_lot = "0.12.1" -serde = { version = "1.0.195", features = ["derive"] } -thiserror = "1.0" +serde = { features = ["derive"], workspace = true, default-features = true } +thiserror = { workspace = true } sc-consensus-beefy = { path = ".." } sp-consensus-beefy = { path = "../../../../primitives/consensus/beefy" } sc-rpc = { path = "../../../rpc" } @@ -26,7 +26,7 @@ sp-core = { path = "../../../../primitives/core" } sp-runtime = { path = "../../../../primitives/runtime" } [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } sc-rpc = { path = "../../../rpc", features = ["test-helpers"] } substrate-test-runtime-client = { path = "../../../../test-utils/runtime/client" } tokio = { version = "1.22.0", features = ["macros"] } diff --git a/substrate/client/consensus/beefy/rpc/src/lib.rs b/substrate/client/consensus/beefy/rpc/src/lib.rs index f5c0ff32627d5e1a7b67d05e1d836e23c0ba9402..f01baee2d6ece9a9d1dd36e1524a1d004e9b0401 100644 --- a/substrate/client/consensus/beefy/rpc/src/lib.rs +++ b/substrate/client/consensus/beefy/rpc/src/lib.rs @@ -23,15 +23,15 @@ use parking_lot::RwLock; use std::sync::Arc; -use sc_rpc::SubscriptionTaskExecutor; +use sc_rpc::{utils::pipe_from_stream, SubscriptionTaskExecutor}; use sp_runtime::traits::Block as BlockT; use futures::{task::SpawnError, FutureExt, StreamExt}; use jsonrpsee::{ - core::{async_trait, Error as JsonRpseeError, RpcResult}, + core::async_trait, proc_macros::rpc, - types::{error::CallError, ErrorObject, SubscriptionResult}, - SubscriptionSink, + types::{ErrorObject, ErrorObjectOwned}, + PendingSubscriptionSink, }; use log::warn; @@ -69,15 +69,11 @@ impl From for ErrorCode { } } -impl From for JsonRpseeError { +impl From for ErrorObjectOwned { fn from(error: Error) -> Self { let message = error.to_string(); let code = ErrorCode::from(error); - JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( - code as i32, - message, - None::<()>, - ))) + ErrorObject::owned(code as i32, message, None::<()>) } } @@ -98,7 +94,7 @@ pub trait BeefyApi { /// in the network or if the client is still initializing or syncing with the network. /// In such case an error would be returned. #[method(name = "beefy_getFinalizedHead")] - async fn latest_finalized(&self) -> RpcResult; + async fn latest_finalized(&self) -> Result; } /// Implements the BeefyApi RPC trait for interacting with BEEFY. @@ -138,27 +134,17 @@ impl BeefyApiServer SubscriptionResult { + fn subscribe_justifications(&self, pending: PendingSubscriptionSink) { let stream = self .finality_proof_stream .subscribe(100_000) .map(|vfp| notification::EncodedVersionedFinalityProof::new::(vfp)); - let fut = async move { - sink.pipe_from_stream(stream).await; - }; - - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - Ok(()) + sc_rpc::utils::spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); } - async fn latest_finalized(&self) -> RpcResult { - self.beefy_best_block - .read() - .as_ref() - .cloned() - .ok_or(Error::EndpointNotReady) - .map_err(Into::into) + async fn latest_finalized(&self) -> Result { + self.beefy_best_block.read().as_ref().cloned().ok_or(Error::EndpointNotReady) } } @@ -167,7 +153,7 @@ mod tests { use super::*; use codec::{Decode, Encode}; - use jsonrpsee::{types::EmptyServerParams as EmptyParams, RpcModule}; + use jsonrpsee::{core::EmptyServerParams as EmptyParams, RpcModule}; use sc_consensus_beefy::{ communication::notification::BeefyVersionedFinalityProofSender, justification::BeefyVersionedFinalityProof, @@ -198,10 +184,10 @@ 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}"#.to_string(); - let (response, _) = rpc.raw_json_request(&request).await.unwrap(); + let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#; + let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); - assert_eq!(expected_response, response.result); + assert_eq!(expected_response, response); } #[tokio::test] @@ -219,24 +205,22 @@ mod tests { \"jsonrpc\":\"2.0\",\ \"result\":\"0x2f0039e93a27221fcf657fb877a1d4f60307106113e885096cb44a461cd0afbf\",\ \"id\":1\ - }" - .to_string(); + }"; let not_ready = "{\ \"jsonrpc\":\"2.0\",\ \"error\":{\"code\":1,\"message\":\"BEEFY RPC endpoint not ready\"},\ \"id\":1\ - }" - .to_string(); + }"; let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); while std::time::Instant::now() < deadline { - let (response, _) = io.raw_json_request(request).await.expect("RPC requests work"); - if response.result != not_ready { - assert_eq!(response.result, expected); + let (response, _) = io.raw_json_request(request, 1).await.expect("RPC requests work"); + if response != not_ready { + assert_eq!(response, expected); // Success return } - std::thread::sleep(std::time::Duration::from_millis(50)) + tokio::time::sleep(std::time::Duration::from_millis(50)).await; } panic!( @@ -249,7 +233,7 @@ mod tests { let (rpc, _) = setup_io_handler(); // Subscribe call. let _sub = rpc - .subscribe("beefy_subscribeJustifications", EmptyParams::new()) + .subscribe_unbounded("beefy_subscribeJustifications", EmptyParams::new()) .await .unwrap(); @@ -257,12 +241,13 @@ mod tests { let (response, _) = rpc .raw_json_request( r#"{"jsonrpc":"2.0","method":"beefy_unsubscribeJustifications","params":["FOO"],"id":1}"#, + 1, ) .await .unwrap(); let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(response.result, expected); + assert_eq!(response, expected); } fn create_finality_proof() -> BeefyVersionedFinalityProof { @@ -284,7 +269,7 @@ mod tests { // Subscribe let mut sub = rpc - .subscribe("beefy_subscribeJustifications", EmptyParams::new()) + .subscribe_unbounded("beefy_subscribeJustifications", EmptyParams::new()) .await .unwrap(); diff --git a/substrate/client/consensus/beefy/src/aux_schema.rs b/substrate/client/consensus/beefy/src/aux_schema.rs index 409eb30d09ab947f3beb997f81f926eb337efec1..534f668ae69c2996064bef086e2958b23f48caf0 100644 --- a/substrate/client/consensus/beefy/src/aux_schema.rs +++ b/substrate/client/consensus/beefy/src/aux_schema.rs @@ -18,11 +18,10 @@ //! Schema for BEEFY state persisted in the aux-db. -use crate::{worker::PersistedState, LOG_TARGET}; +use crate::{error::Error, worker::PersistedState, LOG_TARGET}; use codec::{Decode, Encode}; -use log::{info, trace}; +use log::{debug, trace}; use sc_client_api::{backend::AuxStore, Backend}; -use sp_blockchain::{Error as ClientError, Result as ClientResult}; use sp_runtime::traits::Block as BlockT; const VERSION_KEY: &[u8] = b"beefy_auxschema_version"; @@ -30,31 +29,33 @@ const WORKER_STATE_KEY: &[u8] = b"beefy_voter_state"; const CURRENT_VERSION: u32 = 4; -pub(crate) fn write_current_version(backend: &BE) -> ClientResult<()> { - info!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); +pub(crate) fn write_current_version(backend: &BE) -> Result<(), Error> { + debug!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION); AuxStore::insert_aux(backend, &[(VERSION_KEY, CURRENT_VERSION.encode().as_slice())], &[]) + .map_err(|e| Error::Backend(e.to_string())) } /// Write voter state. pub(crate) fn write_voter_state( backend: &BE, state: &PersistedState, -) -> ClientResult<()> { +) -> Result<(), Error> { trace!(target: LOG_TARGET, "🥩 persisting {:?}", state); AuxStore::insert_aux(backend, &[(WORKER_STATE_KEY, state.encode().as_slice())], &[]) + .map_err(|e| Error::Backend(e.to_string())) } -fn load_decode(backend: &BE, key: &[u8]) -> ClientResult> { - match backend.get_aux(key)? { +fn load_decode(backend: &BE, key: &[u8]) -> Result, Error> { + match backend.get_aux(key).map_err(|e| Error::Backend(e.to_string()))? { None => Ok(None), Some(t) => T::decode(&mut &t[..]) - .map_err(|e| ClientError::Backend(format!("BEEFY DB is corrupted: {}", e))) + .map_err(|e| Error::Backend(format!("BEEFY DB is corrupted: {}", e))) .map(Some), } } /// Load or initialize persistent data from backend. -pub(crate) fn load_persistent(backend: &BE) -> ClientResult>> +pub(crate) fn load_persistent(backend: &BE) -> Result>, Error> where B: BlockT, BE: Backend, @@ -65,8 +66,7 @@ where None => (), Some(1) | Some(2) | Some(3) => (), // versions 1, 2 & 3 are obsolete and should be ignored Some(4) => return load_decode::<_, PersistedState>(backend, WORKER_STATE_KEY), - other => - return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))), + other => return Err(Error::Backend(format!("Unsupported BEEFY DB version: {:?}", other))), } // No persistent state found in DB. diff --git a/substrate/client/consensus/beefy/src/communication/gossip.rs b/substrate/client/consensus/beefy/src/communication/gossip.rs index 645a10b2a1d43f9bb879a799ac9625b2f623506c..eb43c9173d751bf75bc4973aca248d80fb68ca48 100644 --- a/substrate/client/consensus/beefy/src/communication/gossip.rs +++ b/substrate/client/consensus/beefy/src/communication/gossip.rs @@ -56,6 +56,8 @@ pub(super) enum Action { Keep(H, ReputationChange), // discard, applying cost/benefit to originator. Discard(ReputationChange), + // ignore, no cost/benefit applied to originator. + DiscardNoReport, } /// An outcome of examining a message. @@ -68,7 +70,7 @@ enum Consider { /// Message is from the future. Reject. RejectFuture, /// Message cannot be evaluated. Reject. - RejectOutOfScope, + CannotEvaluate, } /// BEEFY gossip message type that gets encoded and sent on the network. @@ -168,18 +170,14 @@ impl Filter { .as_ref() .map(|f| // only from current set and only [filter.start, filter.end] - if set_id < f.validator_set.id() { + if set_id < f.validator_set.id() || round < f.start { Consider::RejectPast - } else if set_id > f.validator_set.id() { - Consider::RejectFuture - } else if round < f.start { - Consider::RejectPast - } else if round > f.end { + } else if set_id > f.validator_set.id() || round > f.end { Consider::RejectFuture } else { Consider::Accept }) - .unwrap_or(Consider::RejectOutOfScope) + .unwrap_or(Consider::CannotEvaluate) } /// Return true if `round` is >= than `max(session_start, best_beefy)`, @@ -199,7 +197,7 @@ impl Filter { Consider::Accept } ) - .unwrap_or(Consider::RejectOutOfScope) + .unwrap_or(Consider::CannotEvaluate) } /// Add new _known_ `round` to the set of seen valid justifications. @@ -244,7 +242,7 @@ where pub(crate) fn new( known_peers: Arc>>, ) -> (GossipValidator, TracingUnboundedReceiver) { - let (tx, rx) = tracing_unbounded("mpsc_beefy_gossip_validator", 10_000); + let (tx, rx) = tracing_unbounded("mpsc_beefy_gossip_validator", 100_000); let val = GossipValidator { votes_topic: votes_topic::(), justifs_topic: proofs_topic::(), @@ -289,7 +287,9 @@ where match filter.consider_vote(round, set_id) { Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE), Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE), - Consider::RejectOutOfScope => return Action::Discard(cost::OUT_OF_SCOPE_MESSAGE), + // When we can't evaluate, it's our fault (e.g. filter not initialized yet), we + // discard the vote without punishing or rewarding the sending peer. + Consider::CannotEvaluate => return Action::DiscardNoReport, Consider::Accept => {}, } @@ -330,7 +330,9 @@ where match guard.consider_finality_proof(round, set_id) { Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE), Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE), - Consider::RejectOutOfScope => return Action::Discard(cost::OUT_OF_SCOPE_MESSAGE), + // When we can't evaluate, it's our fault (e.g. filter not initialized yet), we + // discard the proof without punishing or rewarding the sending peer. + Consider::CannotEvaluate => return Action::DiscardNoReport, Consider::Accept => {}, } @@ -357,7 +359,9 @@ where Action::Keep(self.justifs_topic, benefit::VALIDATED_PROOF) } }) - .unwrap_or(Action::Discard(cost::OUT_OF_SCOPE_MESSAGE)) + // When we can't evaluate, it's our fault (e.g. filter not initialized yet), we + // discard the proof without punishing or rewarding the sending peer. + .unwrap_or(Action::DiscardNoReport) }; if matches!(action, Action::Keep(_, _)) { self.gossip_filter.write().mark_round_as_proven(round); @@ -404,6 +408,7 @@ where self.report(*sender, cb); ValidationResult::Discard }, + Action::DiscardNoReport => ValidationResult::Discard, } } @@ -485,8 +490,8 @@ pub(crate) mod tests { use sc_network_test::Block; use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE; use sp_consensus_beefy::{ - ecdsa_crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload, - SignedCommitment, VoteMessage, + ecdsa_crypto::Signature, known_payloads, test_utils::Keyring, Commitment, MmrRootHash, + Payload, SignedCommitment, VoteMessage, }; use sp_keystore::{testing::MemoryKeystore, Keystore}; @@ -507,10 +512,13 @@ pub(crate) mod tests { } } - pub fn sign_commitment(who: &Keyring, commitment: &Commitment) -> Signature { + pub fn sign_commitment( + who: &Keyring, + commitment: &Commitment, + ) -> Signature { let store = MemoryKeystore::new(); store.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&who.to_seed())).unwrap(); - let beefy_keystore: BeefyKeystore = Some(store.into()).into(); + let beefy_keystore: BeefyKeystore = Some(store.into()).into(); beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap() } @@ -538,7 +546,10 @@ pub(crate) mod tests { .validators() .iter() .map(|validator: &AuthorityId| { - Some(sign_commitment(&Keyring::from_public(validator).unwrap(), &commitment)) + Some(sign_commitment( + &Keyring::::from_public(validator).unwrap(), + &commitment, + )) }) .collect(); @@ -547,7 +558,7 @@ pub(crate) mod tests { #[test] fn should_validate_messages() { - let keys = vec![Keyring::Alice.public()]; + let keys = vec![Keyring::::Alice.public()]; let validator_set = ValidatorSet::::new(keys.clone(), 0).unwrap(); let (gv, mut report_stream) = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new()))); @@ -573,8 +584,8 @@ pub(crate) mod tests { // filter not initialized let res = gv.validate(&mut context, &sender, &encoded); assert!(matches!(res, ValidationResult::Discard)); - expected_report.cost_benefit = cost::OUT_OF_SCOPE_MESSAGE; - assert_eq!(report_stream.try_recv().unwrap(), expected_report); + // nothing reported + assert!(report_stream.try_recv().is_err()); gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set }); // nothing in cache first time diff --git a/substrate/client/consensus/beefy/src/communication/mod.rs b/substrate/client/consensus/beefy/src/communication/mod.rs index 3827559057dde856d201ebb9c9ff71a1d7d11f47..6fda63688e6952839c18e1bbc9ff50ff0c5f7c21 100644 --- a/substrate/client/consensus/beefy/src/communication/mod.rs +++ b/substrate/client/consensus/beefy/src/communication/mod.rs @@ -90,8 +90,6 @@ mod cost { pub(super) const BAD_SIGNATURE: Rep = Rep::new(-100, "BEEFY: Bad signature"); // Message received with vote from voter not in validator set. pub(super) const UNKNOWN_VOTER: Rep = Rep::new(-150, "BEEFY: Unknown voter"); - // A message received that cannot be evaluated relative to our current state. - pub(super) const OUT_OF_SCOPE_MESSAGE: Rep = Rep::new(-500, "BEEFY: Out-of-scope message"); // Message containing invalid proof. pub(super) const INVALID_PROOF: Rep = Rep::new(-5000, "BEEFY: Invalid commit"); // Reputation cost per signature checked for invalid proof. diff --git a/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs b/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs index 71c5c49b3690ccfbd3cb06242d6733528a880964..d856e9748a101b4ad71a39957579c41c0c75aaf4 100644 --- a/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs +++ b/substrate/client/consensus/beefy/src/communication/request_response/incoming_requests_handler.rs @@ -201,7 +201,7 @@ where let peer = request.peer; match self.handle_request(request) { Ok(()) => { - metric_inc!(self, beefy_successful_justification_responses); + metric_inc!(self.metrics, beefy_successful_justification_responses); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 Handled BEEFY justification request from {:?}.", peer @@ -209,7 +209,7 @@ where }, Err(e) => { // peer reputation changes already applied in `self.handle_request()` - metric_inc!(self, beefy_failed_justification_responses); + metric_inc!(self.metrics, beefy_failed_justification_responses); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 Failed to handle BEEFY justification request from {:?}: {}", peer, e, diff --git a/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs b/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs index 7121410ea109bcfd57134de487bb37b7f82dc89f..992b9fa08c093cfbb6bc65e83915997253c6c1dd 100644 --- a/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -148,7 +148,7 @@ impl OnDemandJustificationsEngine { if let Some(peer) = self.try_next_peer() { self.request_from_peer(peer, RequestInfo { block, active_set }); } else { - metric_inc!(self, beefy_on_demand_justification_no_peer_to_request_from); + metric_inc!(self.metrics, beefy_on_demand_justification_no_peer_to_request_from); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 no good peers to request justif #{:?} from", block @@ -194,13 +194,13 @@ impl OnDemandJustificationsEngine { ); match e { RequestFailure::Refused => { - metric_inc!(self, beefy_on_demand_justification_peer_refused); + metric_inc!(self.metrics, beefy_on_demand_justification_peer_refused); let peer_report = PeerReport { who: *peer, cost_benefit: cost::REFUSAL_RESPONSE }; Error::InvalidResponse(peer_report) }, _ => { - metric_inc!(self, beefy_on_demand_justification_peer_error); + metric_inc!(self.metrics, beefy_on_demand_justification_peer_error); Error::ResponseError }, } @@ -212,7 +212,7 @@ impl OnDemandJustificationsEngine { &req_info.active_set, ) .map_err(|(err, signatures_checked)| { - metric_inc!(self, beefy_on_demand_justification_invalid_proof); + metric_inc!(self.metrics, beefy_on_demand_justification_invalid_proof); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 for on demand justification #{:?}, peer {:?} responded with invalid proof: {:?}", @@ -261,7 +261,7 @@ impl OnDemandJustificationsEngine { } }, Ok(proof) => { - metric_inc!(self, beefy_on_demand_justification_good_proof); + metric_inc!(self.metrics, beefy_on_demand_justification_good_proof); debug!( target: BEEFY_SYNC_LOG_TARGET, "🥩 received valid on-demand justif #{:?} from {:?}", block, peer diff --git a/substrate/client/consensus/beefy/src/import.rs b/substrate/client/consensus/beefy/src/import.rs index 6eced17b58ffb275429e48db08032199373efa7a..ed8ed68c4e8d0d378728ba87d1ffe726f6c4c11a 100644 --- a/substrate/client/consensus/beefy/src/import.rs +++ b/substrate/client/consensus/beefy/src/import.rs @@ -159,13 +159,13 @@ where // The proof is valid and the block is imported and final, we can import. debug!( target: LOG_TARGET, - "🥩 import justif {:?} for block number {:?}.", proof, number + "🥩 import justif {} for block number {:?}.", proof, number ); // Send the justification to the BEEFY voter for processing. self.justification_sender .notify(|| Ok::<_, ()>(proof)) .expect("the closure always returns Ok; qed."); - metric_inc!(self, beefy_good_justification_imports); + metric_inc!(self.metrics, beefy_good_justification_imports); }, Err(err) => { debug!( @@ -174,7 +174,7 @@ where number, err, ); - metric_inc!(self, beefy_bad_justification_imports); + metric_inc!(self.metrics, beefy_bad_justification_imports); }, } }, diff --git a/substrate/client/consensus/beefy/src/justification.rs b/substrate/client/consensus/beefy/src/justification.rs index 483184e2374a2b9239554e4ce2709cd7a294cb8c..7f1b9e5237c3970eb8cba4936debd18c77117288 100644 --- a/substrate/client/consensus/beefy/src/justification.rs +++ b/substrate/client/consensus/beefy/src/justification.rs @@ -76,7 +76,7 @@ pub(crate) fn verify_with_validator_set( .as_ref() .map(|sig| { signatures_checked += 1; - BeefyKeystore::verify(id, sig, &message[..]) + BeefyKeystore::verify(*id, sig, &message[..]) }) .unwrap_or(false) }) @@ -93,7 +93,8 @@ pub(crate) fn verify_with_validator_set( #[cfg(test)] pub(crate) mod tests { use sp_consensus_beefy::{ - known_payloads, Commitment, Keyring, Payload, SignedCommitment, VersionedFinalityProof, + known_payloads, test_utils::Keyring, Commitment, Payload, SignedCommitment, + VersionedFinalityProof, }; use substrate_test_runtime_client::runtime::Block; @@ -103,7 +104,7 @@ pub(crate) mod tests { pub(crate) fn new_finality_proof( block_num: NumberFor, validator_set: &ValidatorSet, - keys: &[Keyring], + keys: &[Keyring], ) -> BeefyVersionedFinalityProof { let commitment = Commitment { payload: Payload::from_single_entry(known_payloads::MMR_ROOT_ID, vec![]), @@ -174,7 +175,7 @@ pub(crate) mod tests { }; // change a signature to a different key *bad_signed_commitment.signatures.first_mut().unwrap() = - Some(Keyring::Dave.sign(&bad_signed_commitment.commitment.encode())); + Some(Keyring::::Dave.sign(&bad_signed_commitment.commitment.encode())); match verify_with_validator_set::(block_num, &validator_set, &bad_proof.into()) { Err((ConsensusError::InvalidJustification, 3)) => (), e => assert!(false, "Got unexpected {:?}", e), diff --git a/substrate/client/consensus/beefy/src/keystore.rs b/substrate/client/consensus/beefy/src/keystore.rs index 925bb08828220fa6720f222e71979721c26ffc62..2ddc938fbc6ce33aa74c62d051cf77a519c634ee 100644 --- a/substrate/client/consensus/beefy/src/keystore.rs +++ b/substrate/client/consensus/beefy/src/keystore.rs @@ -16,41 +16,45 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, RuntimeAppPublic}; -use sp_core::keccak_256; +use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, AppCrypto, RuntimeAppPublic}; +use sp_consensus_beefy::{AuthorityIdBound, BeefyAuthorityId, BeefySignatureHasher}; +use sp_core::ecdsa; +#[cfg(feature = "bls-experimental")] +use sp_core::ecdsa_bls377; +use sp_crypto_hashing::keccak_256; use sp_keystore::KeystorePtr; +use codec::Decode; use log::warn; - -use sp_consensus_beefy::{ - ecdsa_crypto::{Public, Signature}, - BeefyAuthorityId, -}; +use std::marker::PhantomData; use crate::{error, LOG_TARGET}; -/// Hasher used for BEEFY signatures. -pub(crate) type BeefySignatureHasher = sp_runtime::traits::Keccak256; - /// A BEEFY specific keystore implemented as a `Newtype`. This is basically a /// wrapper around [`sp_keystore::Keystore`] and allows to customize /// common cryptographic functionality. -pub(crate) struct BeefyKeystore(Option); +pub(crate) struct BeefyKeystore( + Option, + PhantomData AuthorityId>, +); -impl BeefyKeystore { +impl BeefyKeystore { /// Check if the keystore contains a private key for one of the public keys /// contained in `keys`. A public key with a matching private key is known /// as a local authority id. /// /// Return the public key for which we also do have a private key. If no /// matching private key is found, `None` will be returned. - pub fn authority_id(&self, keys: &[Public]) -> Option { + pub fn authority_id(&self, keys: &[AuthorityId]) -> Option { let store = self.0.clone()?; // we do check for multiple private keys as a key store sanity check. - let public: Vec = keys + let public: Vec = keys .iter() - .filter(|k| store.has_keys(&[(k.to_raw_vec(), BEEFY_KEY_TYPE)])) + .filter(|k| { + store + .has_keys(&[(::to_raw_vec(k), BEEFY_KEY_TYPE)]) + }) .cloned() .collect(); @@ -71,55 +75,125 @@ impl BeefyKeystore { /// Note that `message` usually will be pre-hashed before being signed. /// /// Return the message signature or an error in case of failure. - pub fn sign(&self, public: &Public, message: &[u8]) -> Result { + pub fn sign( + &self, + public: &AuthorityId, + message: &[u8], + ) -> Result<::Signature, error::Error> { let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?; - let msg = keccak_256(message); - let public = public.as_ref(); - - let sig = store - .ecdsa_sign_prehashed(BEEFY_KEY_TYPE, public, &msg) - .map_err(|e| error::Error::Keystore(e.to_string()))? - .ok_or_else(|| error::Error::Signature("ecdsa_sign_prehashed() failed".to_string()))?; - - // check that `sig` has the expected result type - let sig = sig.clone().try_into().map_err(|_| { - error::Error::Signature(format!("invalid signature {:?} for key {:?}", sig, public)) + // ECDSA should use ecdsa_sign_prehashed since it needs to be hashed by keccak_256 instead + // of blake2. As such we need to deal with producing the signatures case-by-case + let signature_byte_array: Vec = match ::CRYPTO_ID { + ecdsa::CRYPTO_ID => { + let msg_hash = keccak_256(message); + let public: ecdsa::Public = ecdsa::Public::try_from(public.as_slice()).unwrap(); + + let sig = store + .ecdsa_sign_prehashed(BEEFY_KEY_TYPE, &public, &msg_hash) + .map_err(|e| error::Error::Keystore(e.to_string()))? + .ok_or_else(|| { + error::Error::Signature("ecdsa_sign_prehashed() failed".to_string()) + })?; + let sig_ref: &[u8] = sig.as_ref(); + sig_ref.to_vec() + }, + + #[cfg(feature = "bls-experimental")] + ecdsa_bls377::CRYPTO_ID => { + let public: ecdsa_bls377::Public = + ecdsa_bls377::Public::try_from(public.as_slice()).unwrap(); + let sig = store + .ecdsa_bls377_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &message) + .map_err(|e| error::Error::Keystore(e.to_string()))? + .ok_or_else(|| error::Error::Signature("bls377_sign() failed".to_string()))?; + let sig_ref: &[u8] = sig.as_ref(); + sig_ref.to_vec() + }, + + _ => Err(error::Error::Keystore("key type is not supported by BEEFY Keystore".into()))?, + }; + + //check that `sig` has the expected result type + let signature = ::Signature::decode( + &mut signature_byte_array.as_slice(), + ) + .map_err(|_| { + error::Error::Signature(format!( + "invalid signature {:?} for key {:?}", + signature_byte_array, public + )) })?; - Ok(sig) + Ok(signature) } /// Returns a vector of [`sp_consensus_beefy::crypto::Public`] keys which are currently /// supported (i.e. found in the keystore). - pub fn public_keys(&self) -> Result, error::Error> { + pub fn public_keys(&self) -> Result, error::Error> { let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?; - let pk: Vec = - store.ecdsa_public_keys(BEEFY_KEY_TYPE).drain(..).map(Public::from).collect(); - - Ok(pk) + let pk = match ::CRYPTO_ID { + ecdsa::CRYPTO_ID => store + .ecdsa_public_keys(BEEFY_KEY_TYPE) + .drain(..) + .map(|pk| AuthorityId::try_from(pk.as_ref())) + .collect::, _>>() + .or_else(|_| { + Err(error::Error::Keystore( + "unable to convert public key into authority id".into(), + )) + }), + + #[cfg(feature = "bls-experimental")] + ecdsa_bls377::CRYPTO_ID => store + .ecdsa_bls377_public_keys(BEEFY_KEY_TYPE) + .drain(..) + .map(|pk| AuthorityId::try_from(pk.as_ref())) + .collect::, _>>() + .or_else(|_| { + Err(error::Error::Keystore( + "unable to convert public key into authority id".into(), + )) + }), + + _ => Err(error::Error::Keystore("key type is not supported by BEEFY Keystore".into())), + }; + + pk } /// Use the `public` key to verify that `sig` is a valid signature for `message`. /// /// Return `true` if the signature is authentic, `false` otherwise. - pub fn verify(public: &Public, sig: &Signature, message: &[u8]) -> bool { + pub fn verify( + public: &AuthorityId, + sig: &::Signature, + message: &[u8], + ) -> bool { BeefyAuthorityId::::verify(public, sig, message) } } -impl From> for BeefyKeystore { - fn from(store: Option) -> BeefyKeystore { - BeefyKeystore(store) +impl From> for BeefyKeystore +where + ::Signature: Send + Sync, +{ + fn from(store: Option) -> BeefyKeystore { + BeefyKeystore(store, PhantomData) } } #[cfg(test)] pub mod tests { - use sp_consensus_beefy::{ecdsa_crypto, Keyring}; - use sp_core::{ecdsa, Pair}; - use sp_keystore::testing::MemoryKeystore; + #[cfg(feature = "bls-experimental")] + use sp_consensus_beefy::ecdsa_bls_crypto; + use sp_consensus_beefy::{ + ecdsa_crypto, + test_utils::{BeefySignerAuthority, Keyring}, + }; + use sp_core::Pair as PairT; + use sp_keystore::{testing::MemoryKeystore, Keystore}; use super::*; use crate::error::Error; @@ -128,152 +202,265 @@ pub mod tests { MemoryKeystore::new().into() } - #[test] - fn verify_should_work() { - let msg = keccak_256(b"I am Alice!"); - let sig = Keyring::Alice.sign(b"I am Alice!"); - - assert!(ecdsa::Pair::verify_prehashed( - &sig.clone().into(), - &msg, - &Keyring::Alice.public().into(), + fn pair_verify_should_work< + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + >() + where + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + ::Pair: BeefySignerAuthority, + { + let msg = b"I am Alice!"; + let sig = Keyring::::Alice.sign(b"I am Alice!"); + + assert!(>::verify( + &Keyring::Alice.public(), + &sig, + &msg.as_slice(), )); // different public key -> fail - assert!(!ecdsa::Pair::verify_prehashed( - &sig.clone().into(), - &msg, - &Keyring::Bob.public().into(), + assert!(!>::verify( + &Keyring::Bob.public(), + &sig, + &msg.as_slice(), )); - let msg = keccak_256(b"I am not Alice!"); + let msg = b"I am not Alice!"; // different msg -> fail - assert!( - !ecdsa::Pair::verify_prehashed(&sig.into(), &msg, &Keyring::Alice.public().into(),) - ); + assert!(!>::verify( + &Keyring::Alice.public(), + &sig, + &msg.as_slice(), + )); + } + + /// Generate key pair in the given store using the provided seed + fn generate_in_store( + store: KeystorePtr, + key_type: sp_application_crypto::KeyTypeId, + owner: Option>, + ) -> AuthorityId + where + AuthorityId: + AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + ::Pair: BeefySignerAuthority, + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + { + let optional_seed: Option = owner.map(|owner| owner.to_seed()); + + match ::CRYPTO_ID { + ecdsa::CRYPTO_ID => { + let pk = store.ecdsa_generate_new(key_type, optional_seed.as_deref()).ok().unwrap(); + AuthorityId::decode(&mut pk.as_ref()).unwrap() + }, + #[cfg(feature = "bls-experimental")] + ecdsa_bls377::CRYPTO_ID => { + let pk = store + .ecdsa_bls377_generate_new(key_type, optional_seed.as_deref()) + .ok() + .unwrap(); + AuthorityId::decode(&mut pk.as_ref()).unwrap() + }, + _ => panic!("Requested CRYPTO_ID is not supported by the BEEFY Keyring"), + } } #[test] - fn pair_works() { - let want = ecdsa_crypto::Pair::from_string("//Alice", None) + fn pair_verify_should_work_ecdsa() { + pair_verify_should_work::(); + } + + #[cfg(feature = "bls-experimental")] + #[test] + fn pair_verify_should_work_ecdsa_n_bls() { + pair_verify_should_work::(); + } + + fn pair_works< + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + >() + where + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + ::Pair: BeefySignerAuthority, + { + let want = ::Pair::from_string("//Alice", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::Alice.pair().to_raw_vec(); + let got = Keyring::::Alice.pair().to_raw_vec(); assert_eq!(want, got); - let want = ecdsa_crypto::Pair::from_string("//Bob", None) + let want = ::Pair::from_string("//Bob", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::Bob.pair().to_raw_vec(); + let got = Keyring::::Bob.pair().to_raw_vec(); assert_eq!(want, got); - let want = ecdsa_crypto::Pair::from_string("//Charlie", None) + let want = ::Pair::from_string("//Charlie", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::Charlie.pair().to_raw_vec(); + let got = Keyring::::Charlie.pair().to_raw_vec(); assert_eq!(want, got); - let want = ecdsa_crypto::Pair::from_string("//Dave", None) + let want = ::Pair::from_string("//Dave", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::Dave.pair().to_raw_vec(); + let got = Keyring::::Dave.pair().to_raw_vec(); assert_eq!(want, got); - let want = ecdsa_crypto::Pair::from_string("//Eve", None) + let want = ::Pair::from_string("//Eve", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::Eve.pair().to_raw_vec(); + let got = Keyring::::Eve.pair().to_raw_vec(); assert_eq!(want, got); - let want = ecdsa_crypto::Pair::from_string("//Ferdie", None) + let want = ::Pair::from_string("//Ferdie", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::Ferdie.pair().to_raw_vec(); + let got = Keyring::::Ferdie.pair().to_raw_vec(); assert_eq!(want, got); - let want = ecdsa_crypto::Pair::from_string("//One", None) + let want = ::Pair::from_string("//One", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::One.pair().to_raw_vec(); + let got = Keyring::::One.pair().to_raw_vec(); assert_eq!(want, got); - let want = ecdsa_crypto::Pair::from_string("//Two", None) + let want = ::Pair::from_string("//Two", None) .expect("Pair failed") .to_raw_vec(); - let got = Keyring::Two.pair().to_raw_vec(); + let got = Keyring::::Two.pair().to_raw_vec(); assert_eq!(want, got); } #[test] - fn authority_id_works() { + fn ecdsa_pair_works() { + pair_works::(); + } + + #[cfg(feature = "bls-experimental")] + #[test] + fn ecdsa_n_bls_pair_works() { + pair_works::(); + } + + fn authority_id_works< + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + >() + where + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + ::Pair: BeefySignerAuthority, + { let store = keystore(); - let alice: ecdsa_crypto::Public = store - .ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed())) - .ok() - .unwrap() - .into(); + generate_in_store::(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice)); + + let alice = Keyring::::Alice.public(); let bob = Keyring::Bob.public(); let charlie = Keyring::Charlie.public(); - let store: BeefyKeystore = Some(store).into(); + let beefy_store: BeefyKeystore = Some(store).into(); let mut keys = vec![bob, charlie]; - let id = store.authority_id(keys.as_slice()); + let id = beefy_store.authority_id(keys.as_slice()); assert!(id.is_none()); keys.push(alice.clone()); - let id = store.authority_id(keys.as_slice()).unwrap(); + let id = beefy_store.authority_id(keys.as_slice()).unwrap(); assert_eq!(id, alice); } #[test] - fn sign_works() { + fn authority_id_works_for_ecdsa() { + authority_id_works::(); + } + + #[cfg(feature = "bls-experimental")] + #[test] + fn authority_id_works_for_ecdsa_n_bls() { + authority_id_works::(); + } + + fn sign_works< + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + >() + where + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + ::Pair: BeefySignerAuthority, + { let store = keystore(); - let alice: ecdsa_crypto::Public = store - .ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed())) - .ok() - .unwrap() - .into(); + generate_in_store::(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice)); - let store: BeefyKeystore = Some(store).into(); + let alice = Keyring::Alice.public(); + + let store: BeefyKeystore = Some(store).into(); let msg = b"are you involved or commited?"; let sig1 = store.sign(&alice, msg).unwrap(); - let sig2 = Keyring::Alice.sign(msg); + let sig2 = Keyring::::Alice.sign(msg); assert_eq!(sig1, sig2); } #[test] - fn sign_error() { + fn sign_works_for_ecdsa() { + sign_works::(); + } + + #[cfg(feature = "bls-experimental")] + #[test] + fn sign_works_for_ecdsa_n_bls() { + sign_works::(); + } + + fn sign_error< + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + >( + expected_error_message: &str, + ) where + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + ::Pair: BeefySignerAuthority, + { let store = keystore(); - store - .ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Bob.to_seed())) - .ok() - .unwrap(); + generate_in_store::(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Bob)); - let store: BeefyKeystore = Some(store).into(); + let store: BeefyKeystore = Some(store).into(); let alice = Keyring::Alice.public(); let msg = b"are you involved or commited?"; let sig = store.sign(&alice, msg).err().unwrap(); - let err = Error::Signature("ecdsa_sign_prehashed() failed".to_string()); + let err = Error::Signature(expected_error_message.to_string()); assert_eq!(sig, err); } + #[test] + fn sign_error_for_ecdsa() { + sign_error::("ecdsa_sign_prehashed() failed"); + } + + #[cfg(feature = "bls-experimental")] + #[test] + fn sign_error_for_ecdsa_n_bls() { + sign_error::("bls377_sign() failed"); + } + #[test] fn sign_no_keystore() { - let store: BeefyKeystore = None.into(); + let store: BeefyKeystore = None.into(); let alice = Keyring::Alice.public(); let msg = b"are you involved or commited"; @@ -283,17 +470,21 @@ pub mod tests { assert_eq!(sig, err); } - #[test] - fn verify_works() { + fn verify_works< + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + >() + where + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + ::Pair: BeefySignerAuthority, + { let store = keystore(); - let alice: ecdsa_crypto::Public = store - .ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed())) - .ok() - .unwrap() - .into(); + generate_in_store::(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice)); - let store: BeefyKeystore = Some(store).into(); + let store: BeefyKeystore = Some(store).into(); + + let alice = Keyring::Alice.public(); // `msg` and `sig` match let msg = b"are you involved or commited?"; @@ -305,32 +496,48 @@ pub mod tests { assert!(!BeefyKeystore::verify(&alice, &sig, msg)); } - // Note that we use keys with and without a seed for this test. #[test] - fn public_keys_works() { + fn verify_works_for_ecdsa() { + verify_works::(); + } + + #[cfg(feature = "bls-experimental")] + #[test] + + fn verify_works_for_ecdsa_n_bls() { + verify_works::(); + } + + // Note that we use keys with and without a seed for this test. + fn public_keys_works< + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + >() + where + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, + ::Pair: BeefySignerAuthority, + { const TEST_TYPE: sp_application_crypto::KeyTypeId = sp_application_crypto::KeyTypeId(*b"test"); let store = keystore(); - let add_key = - |key_type, seed: Option<&str>| store.ecdsa_generate_new(key_type, seed).unwrap(); - // test keys - let _ = add_key(TEST_TYPE, Some(Keyring::Alice.to_seed().as_str())); - let _ = add_key(TEST_TYPE, Some(Keyring::Bob.to_seed().as_str())); - - let _ = add_key(TEST_TYPE, None); - let _ = add_key(TEST_TYPE, None); + let _ = generate_in_store::(store.clone(), TEST_TYPE, Some(Keyring::Alice)); + let _ = generate_in_store::(store.clone(), TEST_TYPE, Some(Keyring::Bob)); // BEEFY keys - let _ = add_key(BEEFY_KEY_TYPE, Some(Keyring::Dave.to_seed().as_str())); - let _ = add_key(BEEFY_KEY_TYPE, Some(Keyring::Eve.to_seed().as_str())); + let _ = + generate_in_store::(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Dave)); + let _ = generate_in_store::(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Eve)); + + let _ = generate_in_store::(store.clone(), TEST_TYPE, None); + let _ = generate_in_store::(store.clone(), TEST_TYPE, None); - let key1: ecdsa_crypto::Public = add_key(BEEFY_KEY_TYPE, None).into(); - let key2: ecdsa_crypto::Public = add_key(BEEFY_KEY_TYPE, None).into(); + let key1 = generate_in_store::(store.clone(), BEEFY_KEY_TYPE, None); + let key2 = generate_in_store::(store.clone(), BEEFY_KEY_TYPE, None); - let store: BeefyKeystore = Some(store).into(); + let store: BeefyKeystore = Some(store).into(); let keys = store.public_keys().ok().unwrap(); @@ -340,4 +547,16 @@ pub mod tests { assert!(keys.contains(&key1)); assert!(keys.contains(&key2)); } + + #[test] + fn public_keys_works_for_ecdsa_keystore() { + public_keys_works::(); + } + + #[cfg(feature = "bls-experimental")] + #[test] + + fn public_keys_works_for_ecdsa_n_bls() { + public_keys_works::(); + } } diff --git a/substrate/client/consensus/beefy/src/lib.rs b/substrate/client/consensus/beefy/src/lib.rs index 2e2e22288e3b18d9a6db0b0b0007467bf3d8f96c..323af1bc8305c38bf7a34b4690b212954b7b0864 100644 --- a/substrate/client/consensus/beefy/src/lib.rs +++ b/substrate/client/consensus/beefy/src/lib.rs @@ -27,12 +27,11 @@ use crate::{ outgoing_requests_engine::OnDemandJustificationsEngine, BeefyJustifsRequestHandler, }, }, + error::Error, import::BeefyBlockImport, metrics::register_metrics, - round::Rounds, - worker::PersistedState, }; -use futures::{stream::Fuse, StreamExt}; +use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, warn}; use parking_lot::Mutex; use prometheus::Registry; @@ -41,12 +40,10 @@ use sc_consensus::BlockImport; use sc_network::{NetworkRequest, NotificationService, ProtocolName}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork, Syncing as GossipSyncing}; use sp_api::ProvideRuntimeApi; -use sp_blockchain::{ - Backend as BlockchainBackend, Error as ClientError, HeaderBackend, Result as ClientResult, -}; +use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend}; use sp_consensus::{Error as ConsensusError, SyncOracle}; use sp_consensus_beefy::{ - ecdsa_crypto::AuthorityId, BeefyApi, MmrRootHash, PayloadProvider, ValidatorSet, + ecdsa_crypto::AuthorityId, BeefyApi, ConsensusLog, MmrRootHash, PayloadProvider, ValidatorSet, BEEFY_ENGINE_ID, }; use sp_keystore::KeystorePtr; @@ -70,9 +67,19 @@ pub mod communication; pub mod import; pub mod justification; +use crate::{ + communication::{gossip::GossipValidator, peers::PeerReport}, + justification::BeefyVersionedFinalityProof, + keystore::BeefyKeystore, + metrics::VoterMetrics, + round::Rounds, + worker::{BeefyWorker, PersistedState}, +}; pub use communication::beefy_protocol_name::{ gossip_protocol_name, justifications_protocol_name as justifs_protocol_name, }; +use sc_utils::mpsc::TracingUnboundedReceiver; +use sp_runtime::generic::OpaqueDigestItemId; #[cfg(test)] mod tests; @@ -216,6 +223,247 @@ pub struct BeefyParams { /// Handler for incoming BEEFY justifications requests from a remote peer. pub on_demand_justifications_handler: BeefyJustifsRequestHandler, } +/// Helper object holding BEEFY worker communication/gossip components. +/// +/// These are created once, but will be reused if worker is restarted/reinitialized. +pub(crate) struct BeefyComms { + pub gossip_engine: GossipEngine, + pub gossip_validator: Arc>, + pub gossip_report_stream: TracingUnboundedReceiver, + pub on_demand_justifications: OnDemandJustificationsEngine, +} + +/// Helper builder object for building [worker::BeefyWorker]. +/// +/// It has to do it in two steps: initialization and build, because the first step can sleep waiting +/// for certain chain and backend conditions, and while sleeping we still need to pump the +/// GossipEngine. Once initialization is done, the GossipEngine (and other pieces) are added to get +/// the complete [worker::BeefyWorker] object. +pub(crate) struct BeefyWorkerBuilder { + // utilities + backend: Arc, + runtime: Arc, + key_store: BeefyKeystore, + // voter metrics + metrics: Option, + persisted_state: PersistedState, +} + +impl BeefyWorkerBuilder +where + B: Block + codec::Codec, + BE: Backend, + R: ProvideRuntimeApi, + R::Api: BeefyApi, +{ + /// This will wait for the chain to enable BEEFY (if not yet enabled) and also wait for the + /// backend to sync all headers required by the voter to build a contiguous chain of mandatory + /// justifications. Then it builds the initial voter state using a combination of previously + /// persisted state in AUX DB and latest chain information/progress. + /// + /// Returns a sane `BeefyWorkerBuilder` that can build the `BeefyWorker`. + pub async fn async_initialize( + backend: Arc, + runtime: Arc, + key_store: BeefyKeystore, + metrics: Option, + min_block_delta: u32, + gossip_validator: Arc>, + finality_notifications: &mut Fuse>, + ) -> Result { + // Wait for BEEFY pallet to be active before starting voter. + let (beefy_genesis, best_grandpa) = + wait_for_runtime_pallet(&*runtime, finality_notifications).await?; + + let persisted_state = Self::load_or_init_state( + beefy_genesis, + best_grandpa, + min_block_delta, + backend.clone(), + runtime.clone(), + &key_store, + &metrics, + ) + .await?; + // Update the gossip validator with the right starting round and set id. + persisted_state + .gossip_filter_config() + .map(|f| gossip_validator.update_filter(f))?; + + Ok(BeefyWorkerBuilder { backend, runtime, key_store, metrics, persisted_state }) + } + + /// Takes rest of missing pieces as params and builds the `BeefyWorker`. + pub fn build( + self, + payload_provider: P, + sync: Arc, + comms: BeefyComms, + links: BeefyVoterLinks, + pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, + ) -> BeefyWorker { + BeefyWorker { + backend: self.backend, + runtime: self.runtime, + key_store: self.key_store, + metrics: self.metrics, + persisted_state: self.persisted_state, + payload_provider, + sync, + comms, + links, + pending_justifications, + } + } + + // If no persisted state present, walk back the chain from first GRANDPA notification to either: + // - latest BEEFY finalized block, or if none found on the way, + // - BEEFY pallet genesis; + // Enqueue any BEEFY mandatory blocks (session boundaries) found on the way, for voter to + // finalize. + async fn init_state( + beefy_genesis: NumberFor, + best_grandpa: ::Header, + min_block_delta: u32, + backend: Arc, + runtime: Arc, + ) -> Result, Error> { + let blockchain = backend.blockchain(); + + let beefy_genesis = runtime + .runtime_api() + .beefy_genesis(best_grandpa.hash()) + .ok() + .flatten() + .filter(|genesis| *genesis == beefy_genesis) + .ok_or_else(|| Error::Backend("BEEFY pallet expected to be active.".into()))?; + // Walk back the imported blocks and initialize voter either, at the last block with + // a BEEFY justification, or at pallet genesis block; voter will resume from there. + let mut sessions = VecDeque::new(); + let mut header = best_grandpa.clone(); + let state = loop { + if let Some(true) = blockchain + .justifications(header.hash()) + .ok() + .flatten() + .map(|justifs| justifs.get(BEEFY_ENGINE_ID).is_some()) + { + debug!( + target: LOG_TARGET, + "🥩 Initialize BEEFY voter at last BEEFY finalized block: {:?}.", + *header.number() + ); + let best_beefy = *header.number(); + // If no session boundaries detected so far, just initialize new rounds here. + if sessions.is_empty() { + let active_set = + expect_validator_set(runtime.as_ref(), backend.as_ref(), &header).await?; + let mut rounds = Rounds::new(best_beefy, active_set); + // Mark the round as already finalized. + rounds.conclude(best_beefy); + sessions.push_front(rounds); + } + let state = PersistedState::checked_new( + best_grandpa, + best_beefy, + sessions, + min_block_delta, + beefy_genesis, + ) + .ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))?; + break state + } + + if *header.number() == beefy_genesis { + // We've reached BEEFY genesis, initialize voter here. + let genesis_set = + expect_validator_set(runtime.as_ref(), backend.as_ref(), &header).await?; + info!( + target: LOG_TARGET, + "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ + Starting voting rounds at block {:?}, genesis validator set {:?}.", + beefy_genesis, + genesis_set, + ); + + sessions.push_front(Rounds::new(beefy_genesis, genesis_set)); + break PersistedState::checked_new( + best_grandpa, + Zero::zero(), + sessions, + min_block_delta, + beefy_genesis, + ) + .ok_or_else(|| Error::Backend("Invalid BEEFY chain".into()))? + } + + if let Some(active) = find_authorities_change::(&header) { + debug!( + target: LOG_TARGET, + "🥩 Marking block {:?} as BEEFY Mandatory.", + *header.number() + ); + sessions.push_front(Rounds::new(*header.number(), active)); + } + + // Move up the chain. + header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY).await?; + }; + + aux_schema::write_current_version(backend.as_ref())?; + aux_schema::write_voter_state(backend.as_ref(), &state)?; + Ok(state) + } + + async fn load_or_init_state( + beefy_genesis: NumberFor, + best_grandpa: ::Header, + min_block_delta: u32, + backend: Arc, + runtime: Arc, + key_store: &BeefyKeystore, + metrics: &Option, + ) -> Result, Error> { + // Initialize voter state from AUX DB if compatible. + if let Some(mut state) = crate::aux_schema::load_persistent(backend.as_ref())? + // Verify state pallet genesis matches runtime. + .filter(|state| state.pallet_genesis() == beefy_genesis) + { + // Overwrite persisted state with current best GRANDPA block. + state.set_best_grandpa(best_grandpa.clone()); + // Overwrite persisted data with newly provided `min_block_delta`. + state.set_min_block_delta(min_block_delta); + debug!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); + + // Make sure that all the headers that we need have been synced. + let mut new_sessions = vec![]; + let mut header = best_grandpa.clone(); + while *header.number() > state.best_beefy() { + if state.voting_oracle().can_add_session(*header.number()) { + if let Some(active) = find_authorities_change::(&header) { + new_sessions.push((active, *header.number())); + } + } + header = + wait_for_parent_header(backend.blockchain(), header, HEADER_SYNC_DELAY).await?; + } + + // Make sure we didn't miss any sessions during node restart. + for (validator_set, new_session_start) in new_sessions.drain(..).rev() { + debug!( + target: LOG_TARGET, + "🥩 Handling missed BEEFY session after node restart: {:?}.", + new_session_start + ); + state.init_session_at(new_session_start, validator_set, key_store, metrics); + } + return Ok(state) + } + + // No valid voter-state persisted, re-initialize from pallet genesis. + Self::init_state(beefy_genesis, best_grandpa, min_block_delta, backend, runtime).await + } +} /// Start the BEEFY gadget. /// @@ -284,7 +532,7 @@ pub async fn start_beefy_gadget( known_peers, prometheus_registry.clone(), ); - let mut beefy_comms = worker::BeefyComms { + let mut beefy_comms = BeefyComms { gossip_engine, gossip_validator, gossip_report_stream, @@ -294,57 +542,45 @@ pub async fn start_beefy_gadget( // We re-create and re-run the worker in this loop in order to quickly reinit and resume after // select recoverable errors. loop { - // Wait for BEEFY pallet to be active before starting voter. - let (beefy_genesis, best_grandpa) = match wait_for_runtime_pallet( - &*runtime, - &mut beefy_comms.gossip_engine, - &mut finality_notifications, - ) - .await - { - Ok(res) => res, - Err(e) => { - error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); - return - }, - }; - - let persisted_state = match load_or_init_voter_state( - &*backend, - &*runtime, - beefy_genesis, - best_grandpa, - min_block_delta, - ) - .await - { - Ok(state) => state, - Err(e) => { - error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); - return - }, + // Make sure to pump gossip engine while waiting for initialization conditions. + let worker_builder = loop { + futures::select! { + builder_init_result = BeefyWorkerBuilder::async_initialize( + backend.clone(), + runtime.clone(), + key_store.clone().into(), + metrics.clone(), + min_block_delta, + beefy_comms.gossip_validator.clone(), + &mut finality_notifications, + ).fuse() => { + match builder_init_result { + Ok(builder) => break builder, + Err(e) => { + error!(target: LOG_TARGET, "🥩 Error: {:?}. Terminating.", e); + return + }, + } + }, + // Pump peer reports + _ = &mut beefy_comms.gossip_report_stream.next() => { + continue + }, + // Pump gossip engine. + _ = &mut beefy_comms.gossip_engine => { + error!(target: LOG_TARGET, "🥩 Gossip engine has unexpectedly terminated."); + return + } + } }; - // Update the gossip validator with the right starting round and set id. - if let Err(e) = persisted_state - .gossip_filter_config() - .map(|f| beefy_comms.gossip_validator.update_filter(f)) - { - error!(target: LOG_TARGET, "Error: {:?}. Terminating.", e); - return - } - let worker = worker::BeefyWorker { - backend: backend.clone(), - payload_provider: payload_provider.clone(), - runtime: runtime.clone(), - sync: sync.clone(), - key_store: key_store.clone().into(), - comms: beefy_comms, - links: links.clone(), - metrics: metrics.clone(), - pending_justifications: BTreeMap::new(), - persisted_state, - }; + let worker = worker_builder.build( + payload_provider.clone(), + sync.clone(), + beefy_comms, + links.clone(), + BTreeMap::new(), + ); match futures::future::select( Box::pin(worker.run(&mut block_import_justif, &mut finality_notifications)), @@ -368,43 +604,6 @@ pub async fn start_beefy_gadget( } } -async fn load_or_init_voter_state( - backend: &BE, - runtime: &R, - beefy_genesis: NumberFor, - best_grandpa: ::Header, - min_block_delta: u32, -) -> ClientResult> -where - B: Block, - BE: Backend, - R: ProvideRuntimeApi, - R::Api: BeefyApi, -{ - // Initialize voter state from AUX DB if compatible. - if let Some(mut state) = crate::aux_schema::load_persistent(backend)? - // Verify state pallet genesis matches runtime. - .filter(|state| state.pallet_genesis() == beefy_genesis) - { - // Overwrite persisted state with current best GRANDPA block. - state.set_best_grandpa(best_grandpa.clone()); - // Overwrite persisted data with newly provided `min_block_delta`. - state.set_min_block_delta(min_block_delta); - info!(target: LOG_TARGET, "🥩 Loading BEEFY voter state from db: {:?}.", state); - - // Make sure that all the headers that we need have been synced. - let mut header = best_grandpa.clone(); - while *header.number() > state.best_beefy() { - header = - wait_for_parent_header(backend.blockchain(), header, HEADER_SYNC_DELAY).await?; - } - return Ok(state) - } - - // No valid voter-state persisted, re-initialize from pallet genesis. - initialize_voter_state(backend, runtime, beefy_genesis, best_grandpa, min_block_delta).await -} - /// Waits until the parent header of `current` is available and returns it. /// /// When the node uses GRANDPA warp sync it initially downloads only the mandatory GRANDPA headers. @@ -415,7 +614,7 @@ async fn wait_for_parent_header( blockchain: &BC, current: ::Header, delay: Duration, -) -> ClientResult<::Header> +) -> Result<::Header, Error> where B: Block, BC: BlockchainBackend, @@ -423,10 +622,13 @@ where if *current.number() == Zero::zero() { let msg = format!("header {} is Genesis, there is no parent for it", current.hash()); warn!(target: LOG_TARGET, "{}", msg); - return Err(ClientError::UnknownBlock(msg)) + return Err(Error::Backend(msg)); } loop { - match blockchain.header(*current.parent_hash())? { + match blockchain + .header(*current.parent_hash()) + .map_err(|e| Error::Backend(e.to_string()))? + { Some(parent) => return Ok(parent), None => { info!( @@ -441,115 +643,12 @@ where } } -// If no persisted state present, walk back the chain from first GRANDPA notification to either: -// - latest BEEFY finalized block, or if none found on the way, -// - BEEFY pallet genesis; -// Enqueue any BEEFY mandatory blocks (session boundaries) found on the way, for voter to finalize. -async fn initialize_voter_state( - backend: &BE, - runtime: &R, - beefy_genesis: NumberFor, - best_grandpa: ::Header, - min_block_delta: u32, -) -> ClientResult> -where - B: Block, - BE: Backend, - R: ProvideRuntimeApi, - R::Api: BeefyApi, -{ - let blockchain = backend.blockchain(); - - let beefy_genesis = runtime - .runtime_api() - .beefy_genesis(best_grandpa.hash()) - .ok() - .flatten() - .filter(|genesis| *genesis == beefy_genesis) - .ok_or_else(|| ClientError::Backend("BEEFY pallet expected to be active.".into()))?; - // Walk back the imported blocks and initialize voter either, at the last block with - // a BEEFY justification, or at pallet genesis block; voter will resume from there. - let mut sessions = VecDeque::new(); - let mut header = best_grandpa.clone(); - let state = loop { - if let Some(true) = blockchain - .justifications(header.hash()) - .ok() - .flatten() - .map(|justifs| justifs.get(BEEFY_ENGINE_ID).is_some()) - { - info!( - target: LOG_TARGET, - "🥩 Initialize BEEFY voter at last BEEFY finalized block: {:?}.", - *header.number() - ); - let best_beefy = *header.number(); - // If no session boundaries detected so far, just initialize new rounds here. - if sessions.is_empty() { - let active_set = expect_validator_set(runtime, backend, &header).await?; - let mut rounds = Rounds::new(best_beefy, active_set); - // Mark the round as already finalized. - rounds.conclude(best_beefy); - sessions.push_front(rounds); - } - let state = PersistedState::checked_new( - best_grandpa, - best_beefy, - sessions, - min_block_delta, - beefy_genesis, - ) - .ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))?; - break state - } - - if *header.number() == beefy_genesis { - // We've reached BEEFY genesis, initialize voter here. - let genesis_set = expect_validator_set(runtime, backend, &header).await?; - info!( - target: LOG_TARGET, - "🥩 Loading BEEFY voter state from genesis on what appears to be first startup. \ - Starting voting rounds at block {:?}, genesis validator set {:?}.", - beefy_genesis, - genesis_set, - ); - - sessions.push_front(Rounds::new(beefy_genesis, genesis_set)); - break PersistedState::checked_new( - best_grandpa, - Zero::zero(), - sessions, - min_block_delta, - beefy_genesis, - ) - .ok_or_else(|| ClientError::Backend("Invalid BEEFY chain".into()))? - } - - if let Some(active) = worker::find_authorities_change::(&header) { - info!( - target: LOG_TARGET, - "🥩 Marking block {:?} as BEEFY Mandatory.", - *header.number() - ); - sessions.push_front(Rounds::new(*header.number(), active)); - } - - // Move up the chain. - header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY).await?; - }; - - aux_schema::write_current_version(backend)?; - aux_schema::write_voter_state(backend, &state)?; - Ok(state) -} - /// Wait for BEEFY runtime pallet to be available, return active validator set. /// Should be called only once during worker initialization. async fn wait_for_runtime_pallet( runtime: &R, - mut gossip_engine: &mut GossipEngine, finality: &mut Fuse>, -) -> ClientResult<(NumberFor, ::Header)> +) -> Result<(NumberFor, ::Header), Error> where B: Block, R: ProvideRuntimeApi, @@ -557,33 +656,24 @@ where { info!(target: LOG_TARGET, "🥩 BEEFY gadget waiting for BEEFY pallet to become available..."); loop { - futures::select! { - notif = finality.next() => { - let notif = match notif { - Some(notif) => notif, - None => break - }; - let at = notif.header.hash(); - if let Some(start) = runtime.runtime_api().beefy_genesis(at).ok().flatten() { - if *notif.header.number() >= start { - // Beefy pallet available, return header for best grandpa at the time. - info!( - target: LOG_TARGET, - "🥩 BEEFY pallet available: block {:?} beefy genesis {:?}", - notif.header.number(), start - ); - return Ok((start, notif.header)) - } - } - }, - _ = gossip_engine => { - break + let notif = finality.next().await.ok_or_else(|| { + let err_msg = "🥩 Finality stream has unexpectedly terminated.".into(); + error!(target: LOG_TARGET, "{}", err_msg); + Error::Backend(err_msg) + })?; + let at = notif.header.hash(); + if let Some(start) = runtime.runtime_api().beefy_genesis(at).ok().flatten() { + if *notif.header.number() >= start { + // Beefy pallet available, return header for best grandpa at the time. + info!( + target: LOG_TARGET, + "🥩 BEEFY pallet available: block {:?} beefy genesis {:?}", + notif.header.number(), start + ); + return Ok((start, notif.header)) } } } - let err_msg = "🥩 Gossip engine has unexpectedly terminated.".into(); - error!(target: LOG_TARGET, "{}", err_msg); - Err(ClientError::Backend(err_msg)) } /// Provides validator set active `at_header`. It tries to get it from state, otherwise falls @@ -595,7 +685,7 @@ async fn expect_validator_set( runtime: &R, backend: &BE, at_header: &B::Header, -) -> ClientResult> +) -> Result, Error> where B: Block, BE: Backend, @@ -603,23 +693,44 @@ where R::Api: BeefyApi, { let blockchain = backend.blockchain(); - // Walk up the chain looking for the validator set active at 'at_header'. Process both state and // header digests. - debug!(target: LOG_TARGET, "🥩 Trying to find validator set active at header: {:?}", at_header); + debug!( + target: LOG_TARGET, + "🥩 Trying to find validator set active at header(number {:?}, hash {:?})", + at_header.number(), + at_header.hash() + ); let mut header = at_header.clone(); loop { debug!(target: LOG_TARGET, "🥩 Looking for auth set change at block number: {:?}", *header.number()); if let Ok(Some(active)) = runtime.runtime_api().validator_set(header.hash()) { return Ok(active) } else { - match worker::find_authorities_change::(&header) { + match find_authorities_change::(&header) { Some(active) => return Ok(active), // Move up the chain. Ultimately we'll get it from chain genesis state, or error out // there. None => - header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY).await?, + header = wait_for_parent_header(blockchain, header, HEADER_SYNC_DELAY) + .await + .map_err(|e| Error::Backend(e.to_string()))?, } } } } + +/// Scan the `header` digest log for a BEEFY validator set change. Return either the new +/// validator set or `None` in case no validator set change has been signaled. +pub(crate) fn find_authorities_change(header: &B::Header) -> Option> +where + B: Block, +{ + let id = OpaqueDigestItemId::Consensus(&BEEFY_ENGINE_ID); + + let filter = |log: ConsensusLog| match log { + ConsensusLog::AuthoritiesChange(validator_set) => Some(validator_set), + _ => None, + }; + header.digest().convert_first(|l| l.try_to(id).and_then(filter)) +} diff --git a/substrate/client/consensus/beefy/src/metrics.rs b/substrate/client/consensus/beefy/src/metrics.rs index 031748bdceab5d3553f35b27fca4b6a2afdf70c9..ef3928d79faaaee7f0aacf0af583347e825a30bb 100644 --- a/substrate/client/consensus/beefy/src/metrics.rs +++ b/substrate/client/consensus/beefy/src/metrics.rs @@ -305,10 +305,10 @@ pub(crate) fn register_metrics( // if expr does not derive `Display`. #[macro_export] macro_rules! metric_set { - ($self:ident, $m:ident, $v:expr) => {{ + ($metrics:expr, $m:ident, $v:expr) => {{ let val: u64 = format!("{}", $v).parse().unwrap(); - if let Some(metrics) = $self.metrics.as_ref() { + if let Some(metrics) = $metrics.as_ref() { metrics.$m.set(val); } }}; @@ -316,8 +316,8 @@ macro_rules! metric_set { #[macro_export] macro_rules! metric_inc { - ($self:ident, $m:ident) => {{ - if let Some(metrics) = $self.metrics.as_ref() { + ($metrics:expr, $m:ident) => {{ + if let Some(metrics) = $metrics.as_ref() { metrics.$m.inc(); } }}; @@ -325,8 +325,8 @@ macro_rules! metric_inc { #[macro_export] macro_rules! metric_get { - ($self:ident, $m:ident) => {{ - $self.metrics.as_ref().map(|metrics| metrics.$m.clone()) + ($metrics:expr, $m:ident) => {{ + $metrics.as_ref().map(|metrics| metrics.$m.clone()) }}; } diff --git a/substrate/client/consensus/beefy/src/round.rs b/substrate/client/consensus/beefy/src/round.rs index 47414c60fdb5fc8fc94e60136481ebaefd4d7560..0045dc70c260ee43ca1137d7d7f92b097220aeaf 100644 --- a/substrate/client/consensus/beefy/src/round.rs +++ b/substrate/client/consensus/beefy/src/round.rs @@ -207,7 +207,7 @@ mod tests { use sc_network_test::Block; use sp_consensus_beefy::{ - known_payloads::MMR_ROOT_ID, Commitment, EquivocationProof, Keyring, Payload, + known_payloads::MMR_ROOT_ID, test_utils::Keyring, Commitment, EquivocationProof, Payload, SignedCommitment, ValidatorSet, VoteMessage, }; @@ -226,7 +226,7 @@ mod tests { #[test] fn round_tracker() { let mut rt = RoundTracker::default(); - let bob_vote = (Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed")); + let bob_vote = (Keyring::Bob.public(), Keyring::::Bob.sign(b"I am committed")); let threshold = 2; // adding new vote allowed @@ -237,7 +237,8 @@ mod tests { // vote is not done assert!(!rt.is_done(threshold)); - let alice_vote = (Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")); + let alice_vote = + (Keyring::Alice.public(), Keyring::::Alice.sign(b"I am committed")); // adding new vote (self vote this time) allowed assert!(rt.add_vote(alice_vote)); @@ -271,7 +272,11 @@ mod tests { assert_eq!(42, rounds.validator_set_id()); assert_eq!(1, rounds.session_start()); assert_eq!( - &vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()], + &vec![ + Keyring::::Alice.public(), + Keyring::::Bob.public(), + Keyring::::Charlie.public() + ], rounds.validators() ); } @@ -301,7 +306,7 @@ mod tests { let mut vote = VoteMessage { id: Keyring::Alice.public(), commitment: commitment.clone(), - signature: Keyring::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; // add 1st good vote assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); @@ -310,26 +315,26 @@ mod tests { assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); vote.id = Keyring::Dave.public(); - vote.signature = Keyring::Dave.sign(b"I am committed"); + vote.signature = Keyring::::Dave.sign(b"I am committed"); // invalid vote (Dave is not a validator) assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Invalid); vote.id = Keyring::Bob.public(); - vote.signature = Keyring::Bob.sign(b"I am committed"); + vote.signature = Keyring::::Bob.sign(b"I am committed"); // add 2nd good vote assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok); vote.id = Keyring::Charlie.public(); - vote.signature = Keyring::Charlie.sign(b"I am committed"); + vote.signature = Keyring::::Charlie.sign(b"I am committed"); // add 3rd good vote -> round concluded -> signatures present assert_eq!( rounds.add_vote(vote.clone()), VoteImportResult::RoundConcluded(SignedCommitment { commitment, signatures: vec![ - Some(Keyring::Alice.sign(b"I am committed")), - Some(Keyring::Bob.sign(b"I am committed")), - Some(Keyring::Charlie.sign(b"I am committed")), + Some(Keyring::::Alice.sign(b"I am committed")), + Some(Keyring::::Bob.sign(b"I am committed")), + Some(Keyring::::Charlie.sign(b"I am committed")), None, ] }) @@ -337,7 +342,7 @@ mod tests { rounds.conclude(block_number); vote.id = Keyring::Eve.public(); - vote.signature = Keyring::Eve.sign(b"I am committed"); + vote.signature = Keyring::::Eve.sign(b"I am committed"); // Eve is a validator, but round was concluded, adding vote disallowed assert_eq!(rounds.add_vote(vote), VoteImportResult::Stale); } @@ -364,7 +369,7 @@ mod tests { let mut vote = VoteMessage { id: Keyring::Alice.public(), commitment, - signature: Keyring::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; // add vote for previous session, should fail assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Stale); @@ -407,22 +412,22 @@ mod tests { let mut alice_vote = VoteMessage { id: Keyring::Alice.public(), commitment: commitment.clone(), - signature: Keyring::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; let mut bob_vote = VoteMessage { id: Keyring::Bob.public(), commitment: commitment.clone(), - signature: Keyring::Bob.sign(b"I am committed"), + signature: Keyring::::Bob.sign(b"I am committed"), }; let mut charlie_vote = VoteMessage { id: Keyring::Charlie.public(), commitment, - signature: Keyring::Charlie.sign(b"I am committed"), + signature: Keyring::::Charlie.sign(b"I am committed"), }; let expected_signatures = vec![ - Some(Keyring::Alice.sign(b"I am committed")), - Some(Keyring::Bob.sign(b"I am committed")), - Some(Keyring::Charlie.sign(b"I am committed")), + Some(Keyring::::Alice.sign(b"I am committed")), + Some(Keyring::::Bob.sign(b"I am committed")), + Some(Keyring::::Charlie.sign(b"I am committed")), ]; // round 1 - only 2 out of 3 vote @@ -484,7 +489,7 @@ mod tests { let alice_vote1 = VoteMessage { id: Keyring::Alice.public(), commitment: commitment1, - signature: Keyring::Alice.sign(b"I am committed"), + signature: Keyring::::Alice.sign(b"I am committed"), }; let mut alice_vote2 = alice_vote1.clone(); alice_vote2.commitment = commitment2; diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs index 17065432564268a4c91c22226083d67229cb9d9c..d106c9dcd88165f929085972483e090dbf78c559 100644 --- a/substrate/client/consensus/beefy/src/tests.rs +++ b/substrate/client/consensus/beefy/src/tests.rs @@ -28,10 +28,12 @@ use crate::{ }, request_response::{on_demand_justifications_protocol_config, BeefyJustifsRequestHandler}, }, + error::Error, gossip_protocol_name, justification::*, - load_or_init_voter_state, wait_for_runtime_pallet, BeefyRPCLinks, BeefyVoterLinks, KnownPeers, - PersistedState, + wait_for_runtime_pallet, + worker::PersistedState, + BeefyRPCLinks, BeefyVoterLinks, BeefyWorkerBuilder, KnownPeers, }; use futures::{future, stream::FuturesUnordered, Future, FutureExt, StreamExt}; use parking_lot::Mutex; @@ -55,9 +57,10 @@ use sp_consensus_beefy::{ ecdsa_crypto::{AuthorityId, Signature}, known_payloads, mmr::{find_mmr_root_digest, MmrRootProvider}, - BeefyApi, Commitment, ConsensusLog, EquivocationProof, Keyring as BeefyKeyring, MmrRootHash, - OpaqueKeyOwnershipProof, Payload, SignedCommitment, ValidatorSet, ValidatorSetId, - VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, + test_utils::Keyring as BeefyKeyring, + BeefyApi, Commitment, ConsensusLog, EquivocationProof, MmrRootHash, OpaqueKeyOwnershipProof, + Payload, SignedCommitment, ValidatorSet, ValidatorSetId, VersionedFinalityProof, VoteMessage, + BEEFY_ENGINE_ID, }; use sp_core::H256; use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr}; @@ -347,11 +350,11 @@ fn add_auth_change_digest(builder: &mut impl BlockBuilderExt, new_auth_set: Beef .unwrap(); } -pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec { - keys.iter().map(|&key| key.public().into()).collect() +pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec { + keys.iter().map(|key| key.public().into()).collect() } -pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> KeystorePtr { +pub(crate) fn create_beefy_keystore(authority: &BeefyKeyring) -> KeystorePtr { let keystore = MemoryKeystore::new(); keystore .ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&authority.to_seed())) @@ -363,28 +366,27 @@ async fn voter_init_setup( net: &mut BeefyTestNet, finality: &mut futures::stream::Fuse>, api: &TestApi, -) -> sp_blockchain::Result> { +) -> Result, Error> { let backend = net.peer(0).client().as_backend(); - let known_peers = Arc::new(Mutex::new(KnownPeers::new())); - let (gossip_validator, _) = GossipValidator::new(known_peers); - let gossip_validator = Arc::new(gossip_validator); - let mut gossip_engine = sc_network_gossip::GossipEngine::new( - net.peer(0).network_service().clone(), - net.peer(0).sync_service().clone(), - net.peer(0).take_notification_service(&beefy_gossip_proto_name()).unwrap(), - "/beefy/whatever", - gossip_validator, - None, - ); - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(api, &mut gossip_engine, finality).await.unwrap(); - load_or_init_voter_state(&*backend, api, beefy_genesis, best_grandpa, 1).await + let (beefy_genesis, best_grandpa) = wait_for_runtime_pallet(api, finality).await.unwrap(); + let key_store = None.into(); + let metrics = None; + BeefyWorkerBuilder::load_or_init_state( + beefy_genesis, + best_grandpa, + 1, + backend, + Arc::new(api.clone()), + &key_store, + &metrics, + ) + .await } // Spawns beefy voters. Returns a future to spawn on the runtime. fn initialize_beefy( net: &mut BeefyTestNet, - peers: Vec<(usize, &BeefyKeyring, Arc)>, + peers: Vec<(usize, &BeefyKeyring, Arc)>, min_block_delta: u32, ) -> impl Future where @@ -404,7 +406,7 @@ where for (peer_id, key, api) in peers.into_iter() { let peer = &net.peers[peer_id]; - let keystore = create_beefy_keystore(*key); + let keystore = create_beefy_keystore(key); let (_, _, peer_data) = net.make_block_import(peer.client().clone()); let PeerData { beefy_rpc_links, beefy_voter_links, .. } = peer_data; @@ -462,7 +464,7 @@ async fn run_for(duration: Duration, net: &Arc>) { pub(crate) fn get_beefy_streams( net: &mut BeefyTestNet, // peer index and key - peers: impl Iterator, + peers: impl Iterator)>, ) -> (Vec>, Vec>>) { let mut best_block_streams = Vec::new(); @@ -560,7 +562,7 @@ async fn streams_empty_after_timeout( async fn finalize_block_and_wait_for_beefy( net: &Arc>, // peer index and key - peers: impl Iterator + Clone, + peers: impl Iterator)> + Clone, finalize_target: &H256, expected_beefy: &[u64], ) { @@ -1055,26 +1057,7 @@ async fn should_initialize_voter_at_custom_genesis() { net.peer(0).client().as_client().finalize_block(hashes[8], None).unwrap(); // load persistent state - nothing in DB, should init at genesis - // - // NOTE: code from `voter_init_setup()` is moved here because the new network event system - // doesn't allow creating a new `GossipEngine` as the notification handle is consumed by the - // first `GossipEngine` - let known_peers = Arc::new(Mutex::new(KnownPeers::new())); - let (gossip_validator, _) = GossipValidator::new(known_peers); - let gossip_validator = Arc::new(gossip_validator); - let mut gossip_engine = sc_network_gossip::GossipEngine::new( - net.peer(0).network_service().clone(), - net.peer(0).sync_service().clone(), - net.peer(0).take_notification_service(&beefy_gossip_proto_name()).unwrap(), - "/beefy/whatever", - gossip_validator, - None, - ); - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap(); - let persisted_state = load_or_init_voter_state(&*backend, &api, beefy_genesis, best_grandpa, 1) - .await - .unwrap(); + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // Test initialization at session boundary. // verify voter initialized with single session starting at block `custom_pallet_genesis` (7) @@ -1104,13 +1087,7 @@ async fn should_initialize_voter_at_custom_genesis() { net.peer(0).client().as_client().finalize_block(hashes[10], None).unwrap(); // load persistent state - state preset in DB, but with different pallet genesis - // the network state persists and uses the old `GossipEngine` initialized for `peer(0)` - let (beefy_genesis, best_grandpa) = - wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap(); - let new_persisted_state = - load_or_init_voter_state(&*backend, &api, beefy_genesis, best_grandpa, 1) - .await - .unwrap(); + let new_persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); // verify voter initialized with single session starting at block `new_pallet_genesis` (10) let sessions = new_persisted_state.voting_oracle().sessions(); @@ -1285,6 +1262,68 @@ async fn should_initialize_voter_at_custom_genesis_when_state_unavailable() { assert_eq!(state, persisted_state); } +#[tokio::test] +async fn should_catch_up_when_loading_saved_voter_state() { + let keys = &[BeefyKeyring::Alice]; + let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); + let mut net = BeefyTestNet::new(1); + let backend = net.peer(0).client().as_backend(); + + // push 30 blocks with `AuthorityChange` digests every 10 blocks + let hashes = net.generate_blocks_and_sync(30, 10, &validator_set, false).await; + let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse(); + // finalize 13 without justifications + net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap(); + + let api = TestApi::with_validator_set(&validator_set); + + // load persistent state - nothing in DB, should init at genesis + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + + // Test initialization at session boundary. + // verify voter initialized with two sessions starting at blocks 1 and 10 + let sessions = persisted_state.voting_oracle().sessions(); + assert_eq!(sessions.len(), 2); + assert_eq!(sessions[0].session_start(), 1); + assert_eq!(sessions[1].session_start(), 10); + let rounds = persisted_state.active_round().unwrap(); + assert_eq!(rounds.session_start(), 1); + assert_eq!(rounds.validator_set_id(), validator_set.id()); + + // verify next vote target is mandatory block 1 + assert_eq!(persisted_state.best_beefy(), 0); + assert_eq!(persisted_state.best_grandpa_number(), 13); + assert_eq!(persisted_state.voting_oracle().voting_target(), Some(1)); + + // verify state also saved to db + assert!(verify_persisted_version(&*backend)); + let state = load_persistent(&*backend).unwrap().unwrap(); + assert_eq!(state, persisted_state); + + // now let's consider that the node goes offline, and then it restarts after a while + + // finalize 25 without justifications + net.peer(0).client().as_client().finalize_block(hashes[25], None).unwrap(); + // load persistent state - state preset in DB + let persisted_state = voter_init_setup(&mut net, &mut finality, &api).await.unwrap(); + + // Verify voter initialized with old sessions plus a new one starting at block 20. + // There shouldn't be any duplicates. + let sessions = persisted_state.voting_oracle().sessions(); + assert_eq!(sessions.len(), 3); + assert_eq!(sessions[0].session_start(), 1); + assert_eq!(sessions[1].session_start(), 10); + assert_eq!(sessions[2].session_start(), 20); + let rounds = persisted_state.active_round().unwrap(); + assert_eq!(rounds.session_start(), 1); + assert_eq!(rounds.validator_set_id(), validator_set.id()); + + // verify next vote target is mandatory block 1 + assert_eq!(persisted_state.best_beefy(), 0); + assert_eq!(persisted_state.best_grandpa_number(), 25); + assert_eq!(persisted_state.voting_oracle().voting_target(), Some(1)); +} + #[tokio::test] async fn beefy_finalizing_after_pallet_genesis() { sp_tracing::try_init_simple(); diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs index 26f940f05f189e27acac763b0ea446116a2a9411..c8eb19621ba5a6e45ea2be93e1e2e830492a9b81 100644 --- a/substrate/client/consensus/beefy/src/worker.rs +++ b/substrate/client/consensus/beefy/src/worker.rs @@ -18,35 +18,35 @@ use crate::{ communication::{ - gossip::{proofs_topic, votes_topic, GossipFilterCfg, GossipMessage, GossipValidator}, + gossip::{proofs_topic, votes_topic, GossipFilterCfg, GossipMessage}, peers::PeerReport, - request_response::outgoing_requests_engine::{OnDemandJustificationsEngine, ResponseInfo}, + request_response::outgoing_requests_engine::ResponseInfo, }, error::Error, + find_authorities_change, justification::BeefyVersionedFinalityProof, - keystore::{BeefyKeystore, BeefySignatureHasher}, + keystore::BeefyKeystore, metric_inc, metric_set, metrics::VoterMetrics, round::{Rounds, VoteImportResult}, - BeefyVoterLinks, LOG_TARGET, + BeefyComms, BeefyVoterLinks, LOG_TARGET, }; use codec::{Codec, Decode, DecodeAll, Encode}; use futures::{stream::Fuse, FutureExt, StreamExt}; use log::{debug, error, info, log_enabled, trace, warn}; use sc_client_api::{Backend, FinalityNotification, FinalityNotifications, HeaderBackend}; -use sc_network_gossip::GossipEngine; -use sc_utils::{mpsc::TracingUnboundedReceiver, notification::NotificationReceiver}; +use sc_utils::notification::NotificationReceiver; use sp_api::ProvideRuntimeApi; use sp_arithmetic::traits::{AtLeast32Bit, Saturating}; use sp_consensus::SyncOracle; use sp_consensus_beefy::{ check_equivocation_proof, ecdsa_crypto::{AuthorityId, Signature}, - BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet, + BeefyApi, BeefySignatureHasher, Commitment, EquivocationProof, PayloadProvider, ValidatorSet, VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID, }; use sp_runtime::{ - generic::{BlockId, OpaqueDigestItemId}, + generic::BlockId, traits::{Block, Header, NumberFor, Zero}, SaturatedConversion, }; @@ -176,6 +176,13 @@ impl VoterOracle { } } + /// Check if an observed session can be added to the Oracle. + pub fn can_add_session(&self, session_start: NumberFor) -> bool { + let latest_known_session_start = + self.sessions.back().map(|session| session.session_start()); + Some(session_start) > latest_known_session_start + } + /// Add new observed session to the Oracle. pub fn add_session(&mut self, rounds: Rounds) { self.sessions.push_back(rounds); @@ -236,12 +243,10 @@ impl VoterOracle { /// Return `Some(number)` if we should be voting on block `number`, /// return `None` if there is no block we should vote on. pub fn voting_target(&self) -> Option> { - let rounds = if let Some(r) = self.sessions.front() { - r - } else { + let rounds = self.sessions.front().or_else(|| { debug!(target: LOG_TARGET, "🥩 No voting round started"); - return None - }; + None + })?; let best_grandpa = *self.best_grandpa_block_header.number(); let best_beefy = self.best_beefy_block; @@ -310,31 +315,66 @@ impl PersistedState { self.voting_oracle.best_grandpa_block_header = best_grandpa; } + pub fn voting_oracle(&self) -> &VoterOracle { + &self.voting_oracle + } + pub(crate) fn gossip_filter_config(&self) -> Result, Error> { let (start, end) = self.voting_oracle.accepted_interval()?; let validator_set = self.voting_oracle.current_validator_set()?; Ok(GossipFilterCfg { start, end, validator_set }) } -} -/// Helper object holding BEEFY worker communication/gossip components. -/// -/// These are created once, but will be reused if worker is restarted/reinitialized. -pub(crate) struct BeefyComms { - pub gossip_engine: GossipEngine, - pub gossip_validator: Arc>, - pub gossip_report_stream: TracingUnboundedReceiver, - pub on_demand_justifications: OnDemandJustificationsEngine, + /// Handle session changes by starting new voting round for mandatory blocks. + pub fn init_session_at( + &mut self, + new_session_start: NumberFor, + validator_set: ValidatorSet, + key_store: &BeefyKeystore, + metrics: &Option, + ) { + debug!(target: LOG_TARGET, "🥩 New active validator set: {:?}", validator_set); + + // BEEFY should finalize a mandatory block during each session. + if let Ok(active_session) = self.voting_oracle.active_rounds() { + if !active_session.mandatory_done() { + debug!( + target: LOG_TARGET, + "🥩 New session {} while active session {} is still lagging.", + validator_set.id(), + active_session.validator_set_id(), + ); + metric_inc!(metrics, beefy_lagging_sessions); + } + } + + if log_enabled!(target: LOG_TARGET, log::Level::Debug) { + // verify the new validator set - only do it if we're also logging the warning + if verify_validator_set::(&new_session_start, &validator_set, key_store).is_err() { + metric_inc!(metrics, beefy_no_authority_found_in_store); + } + } + + let id = validator_set.id(); + self.voting_oracle.add_session(Rounds::new(new_session_start, validator_set)); + metric_set!(metrics, beefy_validator_set_id, id); + info!( + target: LOG_TARGET, + "🥩 New Rounds for validator set id: {:?} with session_start {:?}", + id, + new_session_start + ); + } } -/// A BEEFY worker plays the BEEFY protocol +/// A BEEFY worker/voter that follows the BEEFY protocol pub(crate) struct BeefyWorker { // utilities pub backend: Arc, - pub payload_provider: P, pub runtime: Arc, + pub key_store: BeefyKeystore, + pub payload_provider: P, pub sync: Arc, - pub key_store: BeefyKeystore, // communication (created once, but returned and reused if worker is restarted/reinitialized) pub comms: BeefyComms, @@ -344,12 +384,12 @@ pub(crate) struct BeefyWorker { pub links: BeefyVoterLinks, // voter state - /// BEEFY client metrics. - pub metrics: Option, /// Buffer holding justifications for future processing. pub pending_justifications: BTreeMap, BeefyVersionedFinalityProof>, /// Persisted voter state. pub persisted_state: PersistedState, + /// BEEFY voter metrics + pub metrics: Option, } impl BeefyWorker @@ -369,74 +409,22 @@ where &self.persisted_state.voting_oracle } + #[cfg(test)] fn active_rounds(&mut self) -> Result<&Rounds, Error> { self.persisted_state.voting_oracle.active_rounds() } - /// Verify `active` validator set for `block` against the key store - /// - /// We want to make sure that we have _at least one_ key in our keystore that - /// is part of the validator set, that's because if there are no local keys - /// then we can't perform our job as a validator. - /// - /// Note that for a non-authority node there will be no keystore, and we will - /// return an error and don't check. The error can usually be ignored. - fn verify_validator_set( - &self, - block: &NumberFor, - active: &ValidatorSet, - ) -> Result<(), Error> { - let active: BTreeSet<&AuthorityId> = active.validators().iter().collect(); - - let public_keys = self.key_store.public_keys()?; - let store: BTreeSet<&AuthorityId> = public_keys.iter().collect(); - - if store.intersection(&active).count() == 0 { - let msg = "no authority public key found in store".to_string(); - debug!(target: LOG_TARGET, "🥩 for block {:?} {}", block, msg); - metric_inc!(self, beefy_no_authority_found_in_store); - Err(Error::Keystore(msg)) - } else { - Ok(()) - } - } - /// Handle session changes by starting new voting round for mandatory blocks. fn init_session_at( &mut self, validator_set: ValidatorSet, new_session_start: NumberFor, ) { - debug!(target: LOG_TARGET, "🥩 New active validator set: {:?}", validator_set); - - // BEEFY should finalize a mandatory block during each session. - if let Ok(active_session) = self.active_rounds() { - if !active_session.mandatory_done() { - debug!( - target: LOG_TARGET, - "🥩 New session {} while active session {} is still lagging.", - validator_set.id(), - active_session.validator_set_id(), - ); - metric_inc!(self, beefy_lagging_sessions); - } - } - - if log_enabled!(target: LOG_TARGET, log::Level::Debug) { - // verify the new validator set - only do it if we're also logging the warning - let _ = self.verify_validator_set(&new_session_start, &validator_set); - } - - let id = validator_set.id(); - self.persisted_state - .voting_oracle - .add_session(Rounds::new(new_session_start, validator_set)); - metric_set!(self, beefy_validator_set_id, id); - info!( - target: LOG_TARGET, - "🥩 New Rounds for validator set id: {:?} with session_start {:?}", - id, - new_session_start + self.persisted_state.init_session_at( + new_session_start, + validator_set, + &self.key_store, + &self.metrics, ); } @@ -444,13 +432,14 @@ where &mut self, notification: &FinalityNotification, ) -> Result<(), Error> { + let header = ¬ification.header; debug!( target: LOG_TARGET, - "🥩 Finality notification: header {:?} tree_route {:?}", - notification.header, + "🥩 Finality notification: header(number {:?}, hash {:?}) tree_route {:?}", + header.number(), + header.hash(), notification.tree_route, ); - let header = ¬ification.header; self.runtime .runtime_api() @@ -519,7 +508,7 @@ where true, ); }, - RoundAction::Drop => metric_inc!(self, beefy_stale_votes), + RoundAction::Drop => metric_inc!(self.metrics, beefy_stale_votes), RoundAction::Enqueue => error!(target: LOG_TARGET, "🥩 unexpected vote: {:?}.", vote), }; Ok(()) @@ -539,23 +528,23 @@ where match self.voting_oracle().triage_round(block_num)? { RoundAction::Process => { debug!(target: LOG_TARGET, "🥩 Process justification for round: {:?}.", block_num); - metric_inc!(self, beefy_imported_justifications); + metric_inc!(self.metrics, beefy_imported_justifications); self.finalize(justification)? }, RoundAction::Enqueue => { debug!(target: LOG_TARGET, "🥩 Buffer justification for round: {:?}.", block_num); if self.pending_justifications.len() < MAX_BUFFERED_JUSTIFICATIONS { self.pending_justifications.entry(block_num).or_insert(justification); - metric_inc!(self, beefy_buffered_justifications); + metric_inc!(self.metrics, beefy_buffered_justifications); } else { - metric_inc!(self, beefy_buffered_justifications_dropped); + metric_inc!(self.metrics, beefy_buffered_justifications_dropped); warn!( target: LOG_TARGET, "🥩 Buffer justification dropped for round: {:?}.", block_num ); } }, - RoundAction::Drop => metric_inc!(self, beefy_stale_justifications), + RoundAction::Drop => metric_inc!(self.metrics, beefy_stale_justifications), }; Ok(()) } @@ -570,14 +559,14 @@ where match rounds.add_vote(vote) { VoteImportResult::RoundConcluded(signed_commitment) => { let finality_proof = VersionedFinalityProof::V1(signed_commitment); - info!( + debug!( target: LOG_TARGET, "🥩 Round #{} concluded, finality_proof: {:?}.", block_number, finality_proof ); // We created the `finality_proof` and know to be valid. // New state is persisted after finalization. self.finalize(finality_proof.clone())?; - metric_inc!(self, beefy_good_votes_processed); + metric_inc!(self.metrics, beefy_good_votes_processed); return Ok(Some(finality_proof)) }, VoteImportResult::Ok => { @@ -591,14 +580,14 @@ where crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string()))?; } - metric_inc!(self, beefy_good_votes_processed); + metric_inc!(self.metrics, beefy_good_votes_processed); }, VoteImportResult::Equivocation(proof) => { - metric_inc!(self, beefy_equivocation_votes); + metric_inc!(self.metrics, beefy_equivocation_votes); self.report_equivocation(proof)?; }, - VoteImportResult::Invalid => metric_inc!(self, beefy_invalid_votes), - VoteImportResult::Stale => metric_inc!(self, beefy_stale_votes), + VoteImportResult::Invalid => metric_inc!(self.metrics, beefy_invalid_votes), + VoteImportResult::Stale => metric_inc!(self.metrics, beefy_stale_votes), }; Ok(None) } @@ -628,7 +617,7 @@ where crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string()))?; - metric_set!(self, beefy_best_block, block_num); + metric_set!(self.metrics, beefy_best_block, block_num); self.comms.on_demand_justifications.cancel_requests_older_than(block_num); @@ -679,12 +668,16 @@ where for (num, justification) in justifs_to_process.into_iter() { debug!(target: LOG_TARGET, "🥩 Handle buffered justification for: {:?}.", num); - metric_inc!(self, beefy_imported_justifications); + metric_inc!(self.metrics, beefy_imported_justifications); if let Err(err) = self.finalize(justification) { error!(target: LOG_TARGET, "🥩 Error finalizing block: {}", err); } } - metric_set!(self, beefy_buffered_justifications, self.pending_justifications.len()); + metric_set!( + self.metrics, + beefy_buffered_justifications, + self.pending_justifications.len() + ); } Ok(()) } @@ -693,7 +686,7 @@ where fn try_to_vote(&mut self) -> Result<(), Error> { // Vote if there's now a new vote target. if let Some(target) = self.voting_oracle().voting_target() { - metric_set!(self, beefy_should_vote_on, target); + metric_set!(self.metrics, beefy_should_vote_on, target); if target > self.persisted_state.best_voted { self.do_vote(target)?; } @@ -783,7 +776,7 @@ where .gossip_engine .gossip_message(proofs_topic::(), encoded_proof, true); } else { - metric_inc!(self, beefy_votes_sent); + metric_inc!(self.metrics, beefy_votes_sent); debug!(target: LOG_TARGET, "🥩 Sent vote message: {:?}", vote); let encoded_vote = GossipMessage::::Vote(vote).encode(); self.comms.gossip_engine.gossip_message(votes_topic::(), encoded_vote, false); @@ -791,7 +784,7 @@ where // Persist state after vote to avoid double voting in case of voter restarts. self.persisted_state.best_voted = target_number; - metric_set!(self, beefy_best_voted, target_number); + metric_set!(self.metrics, beefy_best_voted, target_number); crate::aux_schema::write_voter_state(&*self.backend, &self.persisted_state) .map_err(|e| Error::Backend(e.to_string())) } @@ -968,7 +961,7 @@ where return Ok(()) } else if let Some(local_id) = self.key_store.authority_id(validators) { if offender_id == local_id { - debug!(target: LOG_TARGET, "🥩 Skip equivocation report for own equivocation"); + warn!(target: LOG_TARGET, "🥩 Skip equivocation report for own equivocation"); return Ok(()) } } @@ -1011,24 +1004,9 @@ where } } -/// Scan the `header` digest log for a BEEFY validator set change. Return either the new -/// validator set or `None` in case no validator set change has been signaled. -pub(crate) fn find_authorities_change(header: &B::Header) -> Option> -where - B: Block, -{ - let id = OpaqueDigestItemId::Consensus(&BEEFY_ENGINE_ID); - - let filter = |log: ConsensusLog| match log { - ConsensusLog::AuthoritiesChange(validator_set) => Some(validator_set), - _ => None, - }; - header.digest().convert_first(|l| l.try_to(id).and_then(filter)) -} - /// Calculate next block number to vote on. /// -/// Return `None` if there is no voteable target yet. +/// Return `None` if there is no votable target yet. fn vote_target(best_grandpa: N, best_beefy: N, session_start: N, min_delta: u32) -> Option where N: AtLeast32Bit + Copy + Debug, @@ -1036,7 +1014,7 @@ where // if the mandatory block (session_start) does not have a beefy justification yet, // we vote on it let target = if best_beefy < session_start { - debug!(target: LOG_TARGET, "🥩 vote target - mandatory block: #{:?}", session_start,); + debug!(target: LOG_TARGET, "🥩 vote target - mandatory block: #{:?}", session_start); session_start } else { let diff = best_grandpa.saturating_sub(best_beefy) + 1u32.into(); @@ -1062,11 +1040,42 @@ where } } +/// Verify `active` validator set for `block` against the key store +/// +/// We want to make sure that we have _at least one_ key in our keystore that +/// is part of the validator set, that's because if there are no local keys +/// then we can't perform our job as a validator. +/// +/// Note that for a non-authority node there will be no keystore, and we will +/// return an error and don't check. The error can usually be ignored. +fn verify_validator_set( + block: &NumberFor, + active: &ValidatorSet, + key_store: &BeefyKeystore, +) -> Result<(), Error> { + let active: BTreeSet<&AuthorityId> = active.validators().iter().collect(); + + let public_keys = key_store.public_keys()?; + let store: BTreeSet<&AuthorityId> = public_keys.iter().collect(); + + if store.intersection(&active).count() == 0 { + let msg = "no authority public key found in store".to_string(); + debug!(target: LOG_TARGET, "🥩 for block {:?} {}", block, msg); + Err(Error::Keystore(msg)) + } else { + Ok(()) + } +} + #[cfg(test)] pub(crate) mod tests { use super::*; use crate::{ - communication::notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream}, + communication::{ + gossip::GossipValidator, + notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream}, + request_response::outgoing_requests_engine::OnDemandJustificationsEngine, + }, tests::{ create_beefy_keystore, get_beefy_streams, make_beefy_ids, BeefyPeer, BeefyTestNet, TestApi, @@ -1076,12 +1085,16 @@ pub(crate) mod tests { use futures::{future::poll_fn, task::Poll}; use parking_lot::Mutex; use sc_client_api::{Backend as BackendT, HeaderBackend}; + use sc_network_gossip::GossipEngine; use sc_network_sync::SyncingService; use sc_network_test::TestNetFactory; use sp_blockchain::Backend as BlockchainBackendT; use sp_consensus_beefy::{ - generate_equivocation_proof, known_payloads, known_payloads::MMR_ROOT_ID, - mmr::MmrRootProvider, Keyring, Payload, SignedCommitment, + known_payloads, + known_payloads::MMR_ROOT_ID, + mmr::MmrRootProvider, + test_utils::{generate_equivocation_proof, Keyring}, + ConsensusLog, Payload, SignedCommitment, }; use sp_runtime::traits::{Header as HeaderT, One}; use substrate_test_runtime_client::{ @@ -1090,10 +1103,6 @@ pub(crate) mod tests { }; impl PersistedState { - pub fn voting_oracle(&self) -> &VoterOracle { - &self.voting_oracle - } - pub fn active_round(&self) -> Result<&Rounds, Error> { self.voting_oracle.active_rounds() } @@ -1111,7 +1120,7 @@ pub(crate) mod tests { fn create_beefy_worker( peer: &mut BeefyPeer, - key: &Keyring, + key: &Keyring, min_block_delta: u32, genesis_validator_set: ValidatorSet, ) -> BeefyWorker< @@ -1121,7 +1130,7 @@ pub(crate) mod tests { TestApi, Arc>, > { - let keystore = create_beefy_keystore(*key); + let keystore = create_beefy_keystore(key); let (to_rpc_justif_sender, from_voter_justif_stream) = BeefyVersionedFinalityProofStream::::channel(); @@ -1190,13 +1199,13 @@ pub(crate) mod tests { }; BeefyWorker { backend, - payload_provider, runtime: api, key_store: Some(keystore).into(), - links, - comms, metrics, + payload_provider, sync: Arc::new(sync), + links, + comms, pending_justifications: BTreeMap::new(), persisted_state, } @@ -1470,19 +1479,22 @@ pub(crate) mod tests { let mut worker = create_beefy_worker(net.peer(0), &keys[0], 1, validator_set.clone()); // keystore doesn't contain other keys than validators' - assert_eq!(worker.verify_validator_set(&1, &validator_set), Ok(())); + assert_eq!(verify_validator_set::(&1, &validator_set, &worker.key_store), Ok(())); // unknown `Bob` key let keys = &[Keyring::Bob]; let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap(); let err_msg = "no authority public key found in store".to_string(); let expected = Err(Error::Keystore(err_msg)); - assert_eq!(worker.verify_validator_set(&1, &validator_set), expected); + assert_eq!(verify_validator_set::(&1, &validator_set, &worker.key_store), expected); // worker has no keystore worker.key_store = None.into(); let expected_err = Err(Error::Keystore("no Keystore".into())); - assert_eq!(worker.verify_validator_set(&1, &validator_set), expected_err); + assert_eq!( + verify_validator_set::(&1, &validator_set, &worker.key_store), + expected_err + ); } #[tokio::test] diff --git a/substrate/client/consensus/common/Cargo.toml b/substrate/client/consensus/common/Cargo.toml index ba0147b895245bce8f1d55919ec4de203e977e3b..7f36dfc09ef8451927eb32ebd651258647abee3b 100644 --- a/substrate/client/consensus/common/Cargo.toml +++ b/substrate/client/consensus/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -20,11 +20,11 @@ async-trait = "0.1.74" futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" libp2p-identity = { version = "0.1.3", features = ["ed25519", "peerid"] } -log = "0.4.17" +log = { workspace = true, default-features = true } mockall = "0.11.3" parking_lot = "0.12.1" -serde = { version = "1.0", features = ["derive"] } -thiserror = "1.0.48" +serde = { features = ["derive"], workspace = true, default-features = true } +thiserror = { workspace = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-client-api = { path = "../../api" } sc-utils = { path = "../../utils" } diff --git a/substrate/client/consensus/common/src/block_import.rs b/substrate/client/consensus/common/src/block_import.rs index a451692ad478e41fb48a055cd2a5a5ae3c12c510..d91851aea62cf4564464b67bcd0bdcd5d712e139 100644 --- a/substrate/client/consensus/common/src/block_import.rs +++ b/substrate/client/consensus/common/src/block_import.rs @@ -43,7 +43,7 @@ pub enum ImportResult { } /// Auxiliary data associated with an imported block result. -#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ImportedAux { /// Only the header has been imported. Block body verification was skipped. pub header_only: bool, diff --git a/substrate/client/consensus/epochs/Cargo.toml b/substrate/client/consensus/epochs/Cargo.toml index 76e4c05a67344c7052cf673127f6e02139402d97..ff6bf86a6a441d49a8950c38d2333bd0e067724d 100644 --- a/substrate/client/consensus/epochs/Cargo.toml +++ b/substrate/client/consensus/epochs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-epochs" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true description = "Generic epochs-based utilities for consensus" edition.workspace = true diff --git a/substrate/client/consensus/grandpa/Cargo.toml b/substrate/client/consensus/grandpa/Cargo.toml index a6aacd564854bcfb7f2428b2616eefef3e080736..1ab953525220b82a500e5a23be0b94a673be51a2 100644 --- a/substrate/client/consensus/grandpa/Cargo.toml +++ b/substrate/client/consensus/grandpa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-grandpa" -version = "0.10.0-dev" +version = "0.19.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -24,12 +24,12 @@ dyn-clone = "1.0" finality-grandpa = { version = "0.16.2", features = ["derive-codec"] } futures = "0.3.21" futures-timer = "3.0.1" -log = "0.4.17" +log = { workspace = true, default-features = true } parity-scale-codec = { version = "3.6.1", features = ["derive"] } parking_lot = "0.12.1" rand = "0.8.5" -serde_json = "1.0.111" -thiserror = "1.0" +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } fork-tree = { path = "../../../utils/fork-tree" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-block-builder = { path = "../../block-builder" } @@ -49,6 +49,7 @@ sp-arithmetic = { path = "../../../primitives/arithmetic" } sp-blockchain = { path = "../../../primitives/blockchain" } sp-consensus = { path = "../../../primitives/consensus/common" } sp-core = { path = "../../../primitives/core" } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa" } sp-keystore = { path = "../../../primitives/keystore" } sp-runtime = { path = "../../../primitives/runtime" } @@ -56,7 +57,7 @@ sp-runtime = { path = "../../../primitives/runtime" } [dev-dependencies] assert_matches = "1.3.0" finality-grandpa = { version = "0.16.2", features = ["derive-codec", "test-helpers"] } -serde = "1.0.195" +serde = { workspace = true, default-features = true } tokio = "1.22.0" sc-network = { path = "../../network" } sc-network-test = { path = "../../network/test" } diff --git a/substrate/client/consensus/grandpa/rpc/Cargo.toml b/substrate/client/consensus/grandpa/rpc/Cargo.toml index 9cfc9616cbc081222af760e32db42706132b752c..f7e87415448e8c44952b957cf365fb36b089e4b0 100644 --- a/substrate/client/consensus/grandpa/rpc/Cargo.toml +++ b/substrate/client/consensus/grandpa/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-grandpa-rpc" -version = "0.10.0-dev" +version = "0.19.0" authors.workspace = true description = "RPC extensions for the GRANDPA finality gadget" repository.workspace = true @@ -15,11 +15,11 @@ workspace = true [dependencies] finality-grandpa = { version = "0.16.2", features = ["derive-codec"] } futures = "0.3.16" -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } -log = "0.4.8" +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } +log = { workspace = true, default-features = true } parity-scale-codec = { version = "3.6.1", features = ["derive"] } -serde = { version = "1.0.195", features = ["derive"] } -thiserror = "1.0" +serde = { features = ["derive"], workspace = true, default-features = true } +thiserror = { workspace = true } sc-client-api = { path = "../../../api" } sc-consensus-grandpa = { path = ".." } sc-rpc = { path = "../../../rpc" } diff --git a/substrate/client/consensus/grandpa/rpc/src/error.rs b/substrate/client/consensus/grandpa/rpc/src/error.rs index 4884380cd22d0797c8ab2174ec21fdec3026f72e..795077804a4b05dc94ec93c4f06915eaa3da35ac 100644 --- a/substrate/client/consensus/grandpa/rpc/src/error.rs +++ b/substrate/client/consensus/grandpa/rpc/src/error.rs @@ -16,10 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; #[derive(Debug, thiserror::Error)] /// Top-level error type for the RPC handler @@ -61,15 +58,11 @@ impl From for ErrorCode { } } -impl From for JsonRpseeError { +impl From for ErrorObjectOwned { fn from(error: Error) -> Self { let message = error.to_string(); let code = ErrorCode::from(error); - JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( - code as i32, - message, - None::<()>, - ))) + ErrorObject::owned(code as i32, message, None::<()>) } } diff --git a/substrate/client/consensus/grandpa/rpc/src/finality.rs b/substrate/client/consensus/grandpa/rpc/src/finality.rs index f8ec01781ac6b4bedd86a71103ca1d8fa0d0901b..93f6c46e411ec7659b49aca73035b4a1657511cd 100644 --- a/substrate/client/consensus/grandpa/rpc/src/finality.rs +++ b/substrate/client/consensus/grandpa/rpc/src/finality.rs @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize}; use sc_consensus_grandpa::FinalityProofProvider; use sp_runtime::traits::{Block as BlockT, NumberFor}; -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct EncodedFinalityProof(pub sp_core::Bytes); /// Local trait mainly to allow mocking in tests. diff --git a/substrate/client/consensus/grandpa/rpc/src/lib.rs b/substrate/client/consensus/grandpa/rpc/src/lib.rs index a7daefaab8eb6d94583852621e5d114ee5d5fa4b..0557eab93e2956b56956f50edea118c9c30cbd76 100644 --- a/substrate/client/consensus/grandpa/rpc/src/lib.rs +++ b/substrate/client/consensus/grandpa/rpc/src/lib.rs @@ -19,15 +19,13 @@ //! RPC API for GRANDPA. #![warn(missing_docs)] -use futures::{FutureExt, StreamExt}; +use futures::StreamExt; use log::warn; use std::sync::Arc; use jsonrpsee::{ - core::{async_trait, RpcResult}, + core::{async_trait, server::PendingSubscriptionSink}, proc_macros::rpc, - types::SubscriptionResult, - SubscriptionSink, }; mod error; @@ -35,13 +33,13 @@ mod finality; mod notification; mod report; -use sc_consensus_grandpa::GrandpaJustificationStream; -use sc_rpc::SubscriptionTaskExecutor; -use sp_runtime::traits::{Block as BlockT, NumberFor}; - +use error::Error; use finality::{EncodedFinalityProof, RpcFinalityProofProvider}; use notification::JustificationNotification; use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates}; +use sc_consensus_grandpa::GrandpaJustificationStream; +use sc_rpc::{utils::pipe_from_stream, SubscriptionTaskExecutor}; +use sp_runtime::traits::{Block as BlockT, NumberFor}; /// Provides RPC methods for interacting with GRANDPA. #[rpc(client, server)] @@ -49,7 +47,7 @@ pub trait GrandpaApi { /// Returns the state of the current best round state as well as the /// ongoing background rounds. #[method(name = "grandpa_roundState")] - async fn round_state(&self) -> RpcResult; + async fn round_state(&self) -> Result; /// Returns the block most recently finalized by Grandpa, alongside /// side its justification. @@ -63,7 +61,7 @@ pub trait GrandpaApi { /// Prove finality for the given block number by returning the Justification for the last block /// in the set and all the intermediary headers to link them together. #[method(name = "grandpa_proveFinality")] - async fn prove_finality(&self, block: Number) -> RpcResult>; + async fn prove_finality(&self, block: Number) -> Result, Error>; } /// Provides RPC methods for interacting with GRANDPA. @@ -99,36 +97,28 @@ where Block: BlockT, ProofProvider: RpcFinalityProofProvider + Send + Sync + 'static, { - async fn round_state(&self) -> RpcResult { - ReportedRoundStates::from(&self.authority_set, &self.voter_state).map_err(Into::into) + async fn round_state(&self) -> Result { + ReportedRoundStates::from(&self.authority_set, &self.voter_state) } - fn subscribe_justifications(&self, mut sink: SubscriptionSink) -> SubscriptionResult { + fn subscribe_justifications(&self, pending: PendingSubscriptionSink) { let stream = self.justification_stream.subscribe(100_000).map( |x: sc_consensus_grandpa::GrandpaJustification| { JustificationNotification::from(x) }, ); - let fut = async move { - sink.pipe_from_stream(stream).await; - }; - - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - Ok(()) + sc_rpc::utils::spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); } async fn prove_finality( &self, block: NumberFor, - ) -> RpcResult> { - self.finality_proof_provider - .rpc_prove_finality(block) - .map_err(|e| { - warn!("Error proving finality: {}", e); - error::Error::ProveFinalityFailed(e) - }) - .map_err(Into::into) + ) -> Result, Error> { + self.finality_proof_provider.rpc_prove_finality(block).map_err(|e| { + warn!("Error proving finality: {}", e); + error::Error::ProveFinalityFailed(e) + }) } } @@ -137,17 +127,15 @@ mod tests { use super::*; use std::{collections::HashSet, convert::TryInto, sync::Arc}; - use jsonrpsee::{ - types::{EmptyServerParams as EmptyParams, SubscriptionId}, - RpcModule, - }; + use jsonrpsee::{core::EmptyServerParams as EmptyParams, types::SubscriptionId, RpcModule}; use parity_scale_codec::{Decode, Encode}; use sc_block_builder::BlockBuilderBuilder; use sc_consensus_grandpa::{ report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender, }; + use sc_rpc::testing::test_executor; use sp_blockchain::HeaderBackend; - use sp_core::{crypto::ByteArray, testing::TaskExecutor}; + use sp_core::crypto::ByteArray; use sp_keyring::Ed25519Keyring; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use substrate_test_runtime_client::{ @@ -264,7 +252,7 @@ mod tests { { let (justification_sender, justification_stream) = GrandpaJustificationStream::channel(); let finality_proof_provider = Arc::new(TestFinalityProofProvider { finality_proof }); - let executor = Arc::new(TaskExecutor::default()); + let executor = test_executor(); let rpc = Grandpa::new( executor, @@ -283,9 +271,9 @@ mod tests { 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 request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; - let (response, _) = rpc.raw_json_request(&request).await.unwrap(); + let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); - assert_eq!(expected_response, response.result); + assert_eq!(expected_response, response); } #[tokio::test] @@ -306,8 +294,8 @@ mod tests { },\"id\":0}".to_string(); let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; - let (response, _) = rpc.raw_json_request(&request).await.unwrap(); - assert_eq!(expected_response, response.result); + let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); + assert_eq!(expected_response, response); } #[tokio::test] @@ -315,7 +303,7 @@ mod tests { let (rpc, _) = setup_io_handler(TestVoterState); // Subscribe call. let _sub = rpc - .subscribe("grandpa_subscribeJustifications", EmptyParams::new()) + .subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new()) .await .unwrap(); @@ -323,12 +311,13 @@ mod tests { let (response, _) = rpc .raw_json_request( r#"{"jsonrpc":"2.0","method":"grandpa_unsubscribeJustifications","params":["FOO"],"id":1}"#, + 1, ) .await .unwrap(); let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(response.result, expected); + assert_eq!(response, expected); } fn create_justification() -> GrandpaJustification { @@ -385,7 +374,7 @@ mod tests { let (rpc, justification_sender) = setup_io_handler(TestVoterState); let mut sub = rpc - .subscribe("grandpa_subscribeJustifications", EmptyParams::new()) + .subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new()) .await .unwrap(); diff --git a/substrate/client/consensus/grandpa/rpc/src/report.rs b/substrate/client/consensus/grandpa/rpc/src/report.rs index ae4fd76d2857a49f29cd807ee257fe3bac3e7f27..b41d090afac8528a902ebfbf20cb3fef4c10dc12 100644 --- a/substrate/client/consensus/grandpa/rpc/src/report.rs +++ b/substrate/client/consensus/grandpa/rpc/src/report.rs @@ -57,21 +57,21 @@ impl ReportVoterState for SharedVoterState { } } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct Prevotes { current_weight: u32, missing: BTreeSet, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct Precommits { current_weight: u32, missing: BTreeSet, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct RoundState { round: u32, @@ -111,7 +111,7 @@ impl RoundState { /// The state of the current best round, as well as the background rounds in a /// form suitable for serialization. -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ReportedRoundStates { set_id: u32, diff --git a/substrate/client/consensus/grandpa/src/import.rs b/substrate/client/consensus/grandpa/src/import.rs index ca5b7c400bfb2f83be8c1b22459ee0730072ada0..bc2983569c533bcefbac2d98cad908e5ee6bcd01 100644 --- a/substrate/client/consensus/grandpa/src/import.rs +++ b/substrate/client/consensus/grandpa/src/import.rs @@ -32,7 +32,6 @@ use sp_api::{Core, RuntimeApiInfo}; use sp_blockchain::BlockStatus; use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain}; use sp_consensus_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID}; -use sp_core::hashing::twox_128; use sp_runtime::{ generic::OpaqueDigestItemId, traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}, @@ -438,7 +437,11 @@ where // The new API is not supported in this runtime. Try reading directly from storage. // This code may be removed once warp sync to an old runtime is no longer needed. for prefix in ["GrandpaFinality", "Grandpa"] { - let k = [twox_128(prefix.as_bytes()), twox_128(b"CurrentSetId")].concat(); + let k = [ + sp_crypto_hashing::twox_128(prefix.as_bytes()), + sp_crypto_hashing::twox_128(b"CurrentSetId"), + ] + .concat(); if let Ok(Some(id)) = self.inner.storage(hash, &sc_client_api::StorageKey(k.to_vec())) { diff --git a/substrate/client/consensus/manual-seal/Cargo.toml b/substrate/client/consensus/manual-seal/Cargo.toml index 77cd88dfc194a5d804e300d3bf6a3cc75a3c2c8f..ac32fed72289fd6a296de2720a75fb36c65ad1f8 100644 --- a/substrate/client/consensus/manual-seal/Cargo.toml +++ b/substrate/client/consensus/manual-seal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-manual-seal" -version = "0.10.0-dev" +version = "0.35.0" authors.workspace = true description = "Manual sealing engine for Substrate" edition.workspace = true @@ -16,15 +16,15 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } assert_matches = "1.3.0" async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" futures-timer = "3.0.1" -log = "0.4.17" -serde = { version = "1.0", features = ["derive"] } -thiserror = "1.0" +log = { workspace = true, default-features = true } +serde = { features = ["derive"], workspace = true, default-features = true } +thiserror = { workspace = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-client-api = { path = "../../api" } sc-consensus = { path = "../common" } diff --git a/substrate/client/consensus/manual-seal/src/error.rs b/substrate/client/consensus/manual-seal/src/error.rs index eeae1d153e81bcdd0ebf25bb117749c52ef128fc..d7bb00eff6b73f8cd7c0c98bf62fcada613f14b1 100644 --- a/substrate/client/consensus/manual-seal/src/error.rs +++ b/substrate/client/consensus/manual-seal/src/error.rs @@ -20,10 +20,7 @@ //! This is suitable for a testing environment. use futures::channel::{mpsc::SendError, oneshot}; -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; use sc_consensus::ImportResult; use sp_blockchain::Error as BlockchainError; use sp_consensus::Error as ConsensusError; @@ -106,8 +103,8 @@ impl Error { } } -impl From for JsonRpseeError { +impl From for ErrorObjectOwned { fn from(err: Error) -> Self { - CallError::Custom(ErrorObject::owned(err.to_code(), err.to_string(), None::<()>)).into() + ErrorObject::owned(err.to_code(), err.to_string(), None::<()>) } } diff --git a/substrate/client/consensus/manual-seal/src/lib.rs b/substrate/client/consensus/manual-seal/src/lib.rs index e3608f6716c2631e5079c08e59ef991903b12d73..c3d360f071974ab38c6f1d112c23d6705f937bd3 100644 --- a/substrate/client/consensus/manual-seal/src/lib.rs +++ b/substrate/client/consensus/manual-seal/src/lib.rs @@ -429,7 +429,9 @@ mod tests { sender, } }); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -438,12 +440,8 @@ mod tests { select_chain, create_inherent_data_providers: |_, _| async { Ok(()) }, consensus_data_provider: None, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future); - }); + })); + // submit a transaction to pool. let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await; // assert that it was successfully imported @@ -469,7 +467,10 @@ mod tests { assert_eq!(client.header(created_block.hash).unwrap().unwrap().number, 1) } - #[tokio::test] + // TODO: enable once the flakiness is fixed + // See https://github.com/paritytech/polkadot-sdk/issues/3603 + //#[tokio::test] + #[allow(unused)] async fn instant_seal_delayed_finalize() { let builder = TestClientBuilder::new(); let (client, select_chain) = builder.build_with_longest_chain(); @@ -507,7 +508,8 @@ mod tests { } }); - let future_instant_seal = run_manual_seal(ManualSealParams { + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), commands_stream, env, @@ -516,24 +518,16 @@ mod tests { select_chain, create_inherent_data_providers: |_, _| async { Ok(()) }, consensus_data_provider: None, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future_instant_seal); - }); + })); let delay_sec = 5; - let future_delayed_finalize = run_delayed_finalize(DelayedFinalizeParams { + + // spawn the background finality task + tokio::spawn(run_delayed_finalize(DelayedFinalizeParams { client: client.clone(), delay_sec, spawn_handle: spawner, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future_delayed_finalize); - }); + })); let mut finality_stream = client.finality_notification_stream(); // submit a transaction to pool. @@ -589,7 +583,9 @@ mod tests { // this test checks that blocks are created as soon as an engine command is sent over the // stream. let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -598,12 +594,8 @@ mod tests { select_chain, consensus_data_provider: None, create_inherent_data_providers: |_, _| async { Ok(()) }, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future); - }); + })); + // submit a transaction to pool. let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await; // assert that it was successfully imported @@ -675,7 +667,9 @@ mod tests { // this test checks that blocks are created as soon as an engine command is sent over the // stream. let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -684,12 +678,8 @@ mod tests { select_chain, consensus_data_provider: None, create_inherent_data_providers: |_, _| async { Ok(()) }, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - // spawn the background authorship task - rt.block_on(future); - }); + })); + // submit a transaction to pool. let result = pool.submit_one(genesis_hash, SOURCE, uxt(Alice, 0)).await; // assert that it was successfully imported @@ -781,7 +771,9 @@ mod tests { let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None); let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); - let future = run_manual_seal(ManualSealParams { + + // spawn the background authorship task + tokio::spawn(run_manual_seal(ManualSealParams { block_import: client.clone(), env, client: client.clone(), @@ -791,11 +783,8 @@ mod tests { // use a provider that pushes some post digest data consensus_data_provider: Some(Box::new(TestDigestProvider { _client: client.clone() })), create_inherent_data_providers: |_, _| async { Ok(()) }, - }); - std::thread::spawn(|| { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(future); - }); + })); + let (tx, rx) = futures::channel::oneshot::channel(); sink.send(EngineCommand::SealNewBlock { parent_hash: None, diff --git a/substrate/client/consensus/manual-seal/src/rpc.rs b/substrate/client/consensus/manual-seal/src/rpc.rs index c0b3af69bedf40884598c19eea16defbdf1ae679..6018c3ab092a1de0c9a9ff847ac7c437511fa540 100644 --- a/substrate/client/consensus/manual-seal/src/rpc.rs +++ b/substrate/client/consensus/manual-seal/src/rpc.rs @@ -23,10 +23,7 @@ use futures::{ channel::{mpsc, oneshot}, SinkExt, }; -use jsonrpsee::{ - core::{async_trait, Error as JsonRpseeError, RpcResult}, - proc_macros::rpc, -}; +use jsonrpsee::{core::async_trait, proc_macros::rpc}; use sc_consensus::ImportedAux; use serde::{Deserialize, Serialize}; use sp_runtime::EncodedJustification; @@ -74,7 +71,7 @@ pub trait ManualSealApi { create_empty: bool, finalize: bool, parent_hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Instructs the manual-seal authorship task to finalize a block #[method(name = "engine_finalizeBlock")] @@ -82,7 +79,7 @@ pub trait ManualSealApi { &self, hash: Hash, justification: Option, - ) -> RpcResult; + ) -> Result; } /// A struct that implements the [`ManualSealApiServer`]. @@ -91,7 +88,7 @@ pub struct ManualSeal { } /// return type of `engine_createBlock` -#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct CreatedBlock { /// hash of the created block. pub hash: Hash, @@ -115,7 +112,7 @@ impl ManualSealApiServer for ManualSeal { create_empty: bool, finalize: bool, parent_hash: Option, - ) -> RpcResult> { + ) -> Result, Error> { let mut sink = self.import_block_channel.clone(); let (sender, receiver) = oneshot::channel(); // NOTE: this sends a Result over the channel. @@ -131,7 +128,7 @@ impl ManualSealApiServer for ManualSeal { match receiver.await { Ok(Ok(rx)) => Ok(rx), Ok(Err(e)) => Err(e.into()), - Err(e) => Err(JsonRpseeError::to_call_error(e)), + Err(e) => Err(e.into()), } } @@ -139,12 +136,12 @@ impl ManualSealApiServer for ManualSeal { &self, hash: Hash, justification: Option, - ) -> RpcResult { + ) -> Result { let mut sink = self.import_block_channel.clone(); let (sender, receiver) = oneshot::channel(); let command = EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification }; sink.send(command).await?; - receiver.await.map(|_| true).map_err(|e| JsonRpseeError::to_call_error(e)) + receiver.await.map(|_| true).map_err(Into::into) } } diff --git a/substrate/client/consensus/pow/Cargo.toml b/substrate/client/consensus/pow/Cargo.toml index 7077fb84babec0e0a5febd6abac2a0a560e958d9..0791514035b43c83492175bcac67e2a6eb6aa9c6 100644 --- a/substrate/client/consensus/pow/Cargo.toml +++ b/substrate/client/consensus/pow/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-pow" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true description = "PoW consensus algorithm for substrate" edition.workspace = true @@ -20,9 +20,9 @@ async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.1" -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" -thiserror = "1.0" +thiserror = { workspace = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-client-api = { path = "../../api" } sc-consensus = { path = "../common" } diff --git a/substrate/client/consensus/slots/Cargo.toml b/substrate/client/consensus/slots/Cargo.toml index 801558a276a57a471520515671a5eaebb7e44db5..75f8b29a2fd755c18d68b817499e2dfb75ea232c 100644 --- a/substrate/client/consensus/slots/Cargo.toml +++ b/substrate/client/consensus/slots/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-slots" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true description = "Generic slots-based utilities for consensus" edition.workspace = true @@ -21,7 +21,7 @@ async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" futures-timer = "3.0.1" -log = "0.4.17" +log = { workspace = true, default-features = true } sc-client-api = { path = "../../api" } sc-consensus = { path = "../common" } sc-telemetry = { path = "../../telemetry" } diff --git a/substrate/client/db/Cargo.toml b/substrate/client/db/Cargo.toml index e833b90b3edeb2b2fd7da2e090aa9183e87ae8a1..57ee1a8ad3315036f44e5b560fb55fa3f31c97e5 100644 --- a/substrate/client/db/Cargo.toml +++ b/substrate/client/db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-client-db" -version = "0.10.0-dev" +version = "0.35.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -24,7 +24,7 @@ kvdb = "0.13.0" kvdb-memorydb = "0.13.0" kvdb-rocksdb = { version = "0.19.0", optional = true } linked-hash-map = "0.5.4" -log = "0.4.17" +log = { workspace = true, default-features = true } parity-db = "0.4.12" parking_lot = "0.12.1" sc-client-api = { path = "../api" } diff --git a/substrate/client/db/benches/state_access.rs b/substrate/client/db/benches/state_access.rs index e47559e710df1e21dab3d2a57b0c6434dd790289..9f3b8ca77c25e50986de42c5803f8615bbf3d00f 100644 --- a/substrate/client/db/benches/state_access.rs +++ b/substrate/client/db/benches/state_access.rs @@ -22,12 +22,13 @@ use sc_client_api::{Backend as _, BlockImportOperation, NewBlockState, StateBack use sc_client_db::{Backend, BlocksPruning, DatabaseSettings, DatabaseSource, PruningMode}; use sp_core::H256; use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, Header}, + generic::UncheckedExtrinsic, + testing::{Block as RawBlock, Header, MockCallU64}, StateVersion, Storage, }; use tempfile::TempDir; -pub(crate) type Block = RawBlock>; +pub(crate) type Block = RawBlock>; fn insert_blocks(db: &Backend, storage: Vec<(Vec, Vec)>) -> H256 { let mut op = db.begin_operation().unwrap(); diff --git a/substrate/client/db/src/bench.rs b/substrate/client/db/src/bench.rs index 03ad4817b53bcea2cb349d4d05c190bb239530fa..32503cf63c0ad7eeae1ab502d38b09aa210d9bc9 100644 --- a/substrate/client/db/src/bench.rs +++ b/substrate/client/db/src/bench.rs @@ -19,7 +19,7 @@ //! State backend that's useful for benchmarking use crate::{DbState, DbStateBuilder}; -use hash_db::{Hasher, Prefix}; +use hash_db::{Hasher as DbHasher, Prefix}; use kvdb::{DBTransaction, KeyValueDB}; use linked_hash_map::LinkedHashMap; use parking_lot::Mutex; @@ -27,10 +27,7 @@ use sp_core::{ hexdisplay::HexDisplay, storage::{ChildInfo, TrackedStorageKey}, }; -use sp_runtime::{ - traits::{Block as BlockT, HashingFor}, - StateVersion, Storage, -}; +use sp_runtime::{traits::Hash, StateVersion, Storage}; use sp_state_machine::{ backend::Backend as StateBackend, BackendTransaction, ChildStorageCollection, DBValue, IterArgs, StorageCollection, StorageIterator, StorageKey, StorageValue, @@ -45,16 +42,16 @@ use std::{ sync::Arc, }; -type State = DbState; +type State = DbState; -struct StorageDb { +struct StorageDb { db: Arc, - _block: std::marker::PhantomData, + _phantom: std::marker::PhantomData, } -impl sp_state_machine::Storage> for StorageDb { - fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result, String> { - let prefixed_key = prefixed_key::>(key, prefix); +impl sp_state_machine::Storage for StorageDb { + fn get(&self, key: &Hasher::Output, prefix: Prefix) -> Result, String> { + let prefixed_key = prefixed_key::(key, prefix); self.db .get(0, &prefixed_key) .map_err(|e| format!("Database backend error: {:?}", e)) @@ -75,29 +72,29 @@ struct KeyTracker { } /// State that manages the backend database reference. Allows runtime to control the database. -pub struct BenchmarkingState { - root: Cell, - genesis_root: B::Hash, - state: RefCell>>, +pub struct BenchmarkingState { + root: Cell, + genesis_root: Hasher::Output, + state: RefCell>>, db: Cell>>, genesis: HashMap, (Vec, i32)>, record: Cell>>, key_tracker: Arc>, whitelist: RefCell>, - proof_recorder: Option>>, - proof_recorder_root: Cell, - shared_trie_cache: SharedTrieCache>, + proof_recorder: Option>, + proof_recorder_root: Cell, + shared_trie_cache: SharedTrieCache, } /// A raw iterator over the `BenchmarkingState`. -pub struct RawIter { - inner: as StateBackend>>::RawIter, +pub struct RawIter { + inner: as StateBackend>::RawIter, child_trie: Option>, key_tracker: Arc>, } -impl StorageIterator> for RawIter { - type Backend = BenchmarkingState; +impl StorageIterator for RawIter { + type Backend = BenchmarkingState; type Error = String; fn next_key(&mut self, backend: &Self::Backend) -> Option> { @@ -128,7 +125,7 @@ impl StorageIterator> for RawIter { } } -impl BenchmarkingState { +impl BenchmarkingState { /// Create a new instance that creates a database in a temporary dir. pub fn new( genesis: Storage, @@ -137,9 +134,9 @@ impl BenchmarkingState { enable_tracking: bool, ) -> Result { let state_version = sp_runtime::StateVersion::default(); - let mut root = B::Hash::default(); - let mut mdb = MemoryDB::>::default(); - sp_trie::trie_types::TrieDBMutBuilderV1::>::new(&mut mdb, &mut root).build(); + let mut root = Default::default(); + let mut mdb = MemoryDB::::default(); + sp_trie::trie_types::TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); let mut state = BenchmarkingState { state: RefCell::new(None), @@ -169,7 +166,7 @@ impl BenchmarkingState { child_content.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))), ) }); - let (root, transaction): (B::Hash, _) = + let (root, transaction): (Hasher::Output, _) = state.state.borrow().as_ref().unwrap().full_storage_root( genesis.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))), child_delta, @@ -193,9 +190,9 @@ impl BenchmarkingState { recorder.reset(); self.proof_recorder_root.set(self.root.get()); } - let storage_db = Arc::new(StorageDb:: { db, _block: Default::default() }); + let storage_db = Arc::new(StorageDb:: { db, _phantom: Default::default() }); *self.state.borrow_mut() = Some( - DbStateBuilder::::new(storage_db, self.root.get()) + DbStateBuilder::::new(storage_db, self.root.get()) .with_optional_recorder(self.proof_recorder.clone()) .with_cache(self.shared_trie_cache.local_cache()) .build(), @@ -341,17 +338,17 @@ fn state_err() -> String { "State is not open".into() } -impl StateBackend> for BenchmarkingState { - type Error = as StateBackend>>::Error; - type TrieBackendStorage = as StateBackend>>::TrieBackendStorage; - type RawIter = RawIter; +impl StateBackend for BenchmarkingState { + type Error = as StateBackend>::Error; + type TrieBackendStorage = as StateBackend>::TrieBackendStorage; + type RawIter = RawIter; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { self.add_read_key(None, key); self.state.borrow().as_ref().ok_or_else(state_err)?.storage(key) } - fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { + fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { self.add_read_key(None, key); self.state.borrow().as_ref().ok_or_else(state_err)?.storage_hash(key) } @@ -373,7 +370,7 @@ impl StateBackend> for BenchmarkingState { &self, child_info: &ChildInfo, key: &[u8], - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { self.add_read_key(Some(child_info.storage_key()), key); self.state .borrow() @@ -385,7 +382,7 @@ impl StateBackend> for BenchmarkingState { fn closest_merkle_value( &self, key: &[u8], - ) -> Result>, Self::Error> { + ) -> Result>, Self::Error> { self.add_read_key(None, key); self.state.borrow().as_ref().ok_or_else(state_err)?.closest_merkle_value(key) } @@ -394,7 +391,7 @@ impl StateBackend> for BenchmarkingState { &self, child_info: &ChildInfo, key: &[u8], - ) -> Result>, Self::Error> { + ) -> Result>, Self::Error> { self.add_read_key(None, key); self.state .borrow() @@ -443,7 +440,7 @@ impl StateBackend> for BenchmarkingState { &self, delta: impl Iterator)>, state_version: StateVersion, - ) -> (B::Hash, BackendTransaction>) { + ) -> (Hasher::Output, BackendTransaction) { self.state .borrow() .as_ref() @@ -455,7 +452,7 @@ impl StateBackend> for BenchmarkingState { child_info: &ChildInfo, delta: impl Iterator)>, state_version: StateVersion, - ) -> (B::Hash, bool, BackendTransaction>) { + ) -> (Hasher::Output, bool, BackendTransaction) { self.state .borrow() .as_ref() @@ -479,8 +476,8 @@ impl StateBackend> for BenchmarkingState { fn commit( &self, - storage_root: as Hasher>::Out, - mut transaction: BackendTransaction>, + storage_root: ::Out, + mut transaction: BackendTransaction, main_storage_changes: StorageCollection, child_storage_changes: ChildStorageCollection, ) -> Result<(), Self::Error> { @@ -634,8 +631,7 @@ impl StateBackend> for BenchmarkingState { log::debug!(target: "benchmark", "Some proof size: {}", &proof_size); proof_size } else { - if let Some(size) = proof.encoded_compact_size::>(proof_recorder_root) - { + if let Some(size) = proof.encoded_compact_size::(proof_recorder_root) { size as u32 } else if proof_recorder_root == self.root.get() { log::debug!(target: "benchmark", "No changes - no proof"); @@ -654,7 +650,7 @@ impl StateBackend> for BenchmarkingState { } } -impl std::fmt::Debug for BenchmarkingState { +impl std::fmt::Debug for BenchmarkingState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Bench DB") } @@ -663,6 +659,7 @@ impl std::fmt::Debug for BenchmarkingState { #[cfg(test)] mod test { use crate::bench::BenchmarkingState; + use sp_runtime::traits::HashingFor; use sp_state_machine::backend::Backend as _; fn hex(hex: &str) -> Vec { @@ -681,7 +678,8 @@ mod test { ..sp_runtime::Storage::default() }; let bench_state = - BenchmarkingState::::new(storage, None, false, true).unwrap(); + BenchmarkingState::>::new(storage, None, false, true) + .unwrap(); assert_eq!(bench_state.read_write_count(), (0, 0, 0, 0)); assert_eq!(bench_state.keys(Default::default()).unwrap().count(), 1); @@ -690,9 +688,13 @@ mod test { #[test] fn read_to_main_and_child_tries() { - let bench_state = - BenchmarkingState::::new(Default::default(), None, false, true) - .unwrap(); + let bench_state = BenchmarkingState::>::new( + Default::default(), + None, + false, + true, + ) + .unwrap(); for _ in 0..2 { let child1 = sp_core::storage::ChildInfo::new_default(b"child1"); diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index 194bec8a88eb46fd90537bf61a65c12dc02982c8..f22022ef29a37695e2df1d432dd3b4d4d916c62e 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -101,14 +101,11 @@ pub use bench::BenchmarkingState; const CACHE_HEADERS: usize = 8; /// DB-backed patricia trie state, transaction type is an overlay of changes to commit. -pub type DbState = - sp_state_machine::TrieBackend>>, HashingFor>; +pub type DbState = sp_state_machine::TrieBackend>, H>; /// Builder for [`DbState`]. -pub type DbStateBuilder = sp_state_machine::TrieBackendBuilder< - Arc>>, - HashingFor, ->; +pub type DbStateBuilder = + sp_state_machine::TrieBackendBuilder>, Hasher>; /// Length of a [`DbHash`]. const DB_HASH_LEN: usize = 32; @@ -135,13 +132,17 @@ enum DbExtrinsic { /// It makes sure that the hash we are using stays pinned in storage /// until this structure is dropped. pub struct RefTrackingState { - state: DbState, + state: DbState>, storage: Arc>, parent_hash: Option, } impl RefTrackingState { - fn new(state: DbState, storage: Arc>, parent_hash: Option) -> Self { + fn new( + state: DbState>, + storage: Arc>, + parent_hash: Option, + ) -> Self { RefTrackingState { state, parent_hash, storage } } } @@ -162,12 +163,12 @@ impl std::fmt::Debug for RefTrackingState { /// A raw iterator over the `RefTrackingState`. pub struct RawIter { - inner: as StateBackend>>::RawIter, + inner: > as StateBackend>>::RawIter, } impl StorageIterator> for RawIter { type Backend = RefTrackingState; - type Error = as StateBackend>>::Error; + type Error = > as StateBackend>>::Error; fn next_key(&mut self, backend: &Self::Backend) -> Option> { self.inner.next_key(&backend.state) @@ -186,8 +187,9 @@ impl StorageIterator> for RawIter { } impl StateBackend> for RefTrackingState { - type Error = as StateBackend>>::Error; - type TrieBackendStorage = as StateBackend>>::TrieBackendStorage; + type Error = > as StateBackend>>::Error; + type TrieBackendStorage = + > as StateBackend>>::TrieBackendStorage; type RawIter = RawIter; fn storage(&self, key: &[u8]) -> Result>, Self::Error> { @@ -284,7 +286,8 @@ impl StateBackend> for RefTrackingState { } impl AsTrieBackend> for RefTrackingState { - type TrieBackendStorage = as StateBackend>>::TrieBackendStorage; + type TrieBackendStorage = + > as StateBackend>>::TrieBackendStorage; fn as_trie_backend( &self, @@ -320,6 +323,16 @@ pub enum BlocksPruning { Some(u32), } +impl BlocksPruning { + /// True if this is an archive pruning mode (either KeepAll or KeepFinalized). + pub fn is_archive(&self) -> bool { + match *self { + BlocksPruning::KeepAll | BlocksPruning::KeepFinalized => true, + BlocksPruning::Some(_) => false, + } + } +} + /// Where to find the database.. #[derive(Debug, Clone)] pub enum DatabaseSource { @@ -1926,7 +1939,7 @@ impl Backend { fn empty_state(&self) -> RecordStatsState, Block> { let root = EmptyStorage::::new().0; // Empty trie - let db_state = DbStateBuilder::::new(self.storage.clone(), root) + let db_state = DbStateBuilder::>::new(self.storage.clone(), root) .with_optional_cache(self.shared_trie_cache.as_ref().map(|c| c.local_cache())) .build(); let state = RefTrackingState::new(db_state, self.storage.clone(), None); @@ -2418,9 +2431,12 @@ impl sc_client_api::backend::Backend for Backend { if hash == self.blockchain.meta.read().genesis_hash { if let Some(genesis_state) = &*self.genesis_state.read() { let root = genesis_state.root; - let db_state = DbStateBuilder::::new(genesis_state.clone(), root) - .with_optional_cache(self.shared_trie_cache.as_ref().map(|c| c.local_cache())) - .build(); + let db_state = + DbStateBuilder::>::new(genesis_state.clone(), root) + .with_optional_cache( + self.shared_trie_cache.as_ref().map(|c| c.local_cache()), + ) + .build(); let state = RefTrackingState::new(db_state, self.storage.clone(), None); return Ok(RecordStatsState::new(state, None, self.state_usage.clone())) @@ -2439,11 +2455,12 @@ impl sc_client_api::backend::Backend for Backend { self.storage.state_db.pin(&hash, hdr.number.saturated_into::(), hint) { let root = hdr.state_root; - let db_state = DbStateBuilder::::new(self.storage.clone(), root) - .with_optional_cache( - self.shared_trie_cache.as_ref().map(|c| c.local_cache()), - ) - .build(); + let db_state = + DbStateBuilder::>::new(self.storage.clone(), root) + .with_optional_cache( + self.shared_trie_cache.as_ref().map(|c| c.local_cache()), + ) + .build(); let state = RefTrackingState::new(db_state, self.storage.clone(), Some(hash)); Ok(RecordStatsState::new(state, Some(hash), self.state_usage.clone())) } else { @@ -2514,7 +2531,7 @@ 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!( - "State already discarded for `{:?}`", + "Unable to pin: state already discarded for `{:?}`", hash )) }, @@ -2556,7 +2573,8 @@ pub(crate) mod tests { use sp_blockchain::{lowest_common_ancestor, tree_route}; use sp_core::H256; use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, Header}, + generic::UncheckedExtrinsic, + testing::{Block as RawBlock, Header, MockCallU64}, traits::{BlakeTwo256, Hash}, ConsensusEngineId, StateVersion, }; @@ -2564,7 +2582,8 @@ pub(crate) mod tests { const CONS0_ENGINE_ID: ConsensusEngineId = *b"CON0"; const CONS1_ENGINE_ID: ConsensusEngineId = *b"CON1"; - pub(crate) type Block = RawBlock>; + type UncheckedXt = UncheckedExtrinsic; + pub(crate) type Block = RawBlock; pub fn insert_header( backend: &Backend, @@ -2583,7 +2602,7 @@ pub(crate) mod tests { parent_hash: H256, _changes: Option, Vec)>>, extrinsics_root: H256, - body: Vec>, + body: Vec, transaction_index: Option>, ) -> Result { use sp_runtime::testing::Digest; @@ -3397,7 +3416,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -3419,11 +3438,20 @@ pub(crate) mod tests { assert_eq!(None, bc.body(blocks[0]).unwrap()); assert_eq!(None, bc.body(blocks[1]).unwrap()); assert_eq!(None, bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); } else { for i in 0..5 { - assert_eq!(Some(vec![(i as u64).into()]), bc.body(blocks[i]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction((i as u64).into(), ())]), + bc.body(blocks[i]).unwrap() + ); } } } @@ -3447,7 +3475,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -3456,16 +3484,26 @@ pub(crate) mod tests { } // insert a fork at block 2 - let fork_hash_root = - insert_block(&backend, 2, blocks[1], None, H256::random(), vec![2.into()], None) - .unwrap(); + let fork_hash_root = insert_block( + &backend, + 2, + blocks[1], + None, + H256::random(), + vec![UncheckedXt::new_transaction(2.into(), ())], + None, + ) + .unwrap(); insert_block( &backend, 3, fork_hash_root, None, H256::random(), - vec![3.into(), 11.into()], + vec![ + UncheckedXt::new_transaction(3.into(), ()), + UncheckedXt::new_transaction(11.into(), ()), + ], None, ) .unwrap(); @@ -3475,7 +3513,10 @@ pub(crate) mod tests { backend.commit_operation(op).unwrap(); let bc = backend.blockchain(); - assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(fork_hash_root).unwrap() + ); for i in 1..5 { let mut op = backend.begin_operation().unwrap(); @@ -3489,16 +3530,28 @@ pub(crate) mod tests { assert_eq!(None, bc.body(blocks[1]).unwrap()); assert_eq!(None, bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); } else { for i in 0..5 { - assert_eq!(Some(vec![(i as u64).into()]), bc.body(blocks[i]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction((i as u64).into(), ())]), + bc.body(blocks[i]).unwrap() + ); } } if matches!(pruning, BlocksPruning::KeepAll) { - assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(fork_hash_root).unwrap() + ); } else { assert_eq!(None, bc.body(fork_hash_root).unwrap()); } @@ -3519,8 +3572,16 @@ pub(crate) mod tests { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(10), 10); let make_block = |index, parent, val: u64| { - insert_block(&backend, index, parent, None, H256::random(), vec![val.into()], None) - .unwrap() + insert_block( + &backend, + index, + parent, + None, + H256::random(), + vec![UncheckedXt::new_transaction(val.into(), ())], + None, + ) + .unwrap() }; let block_0 = make_block(0, Default::default(), 0x00); @@ -3548,18 +3609,30 @@ pub(crate) mod tests { let bc = backend.blockchain(); assert_eq!(None, bc.body(block_1b).unwrap()); assert_eq!(None, bc.body(block_2b).unwrap()); - assert_eq!(Some(vec![0x00.into()]), bc.body(block_0).unwrap()); - assert_eq!(Some(vec![0x1a.into()]), bc.body(block_1a).unwrap()); - assert_eq!(Some(vec![0x2a.into()]), bc.body(block_2a).unwrap()); - assert_eq!(Some(vec![0x3a.into()]), bc.body(block_3a).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x00.into(), ())]), + bc.body(block_0).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x1a.into(), ())]), + bc.body(block_1a).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x2a.into(), ())]), + bc.body(block_2a).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x3a.into(), ())]), + bc.body(block_3a).unwrap() + ); } #[test] fn indexed_data_block_body() { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); - let x0 = ExtrinsicWrapper::from(0u64).encode(); - let x1 = ExtrinsicWrapper::from(1u64).encode(); + let x0 = UncheckedXt::new_transaction(0.into(), ()).encode(); + let x1 = UncheckedXt::new_transaction(1.into(), ()).encode(); let x0_hash = as sp_core::Hasher>::hash(&x0[1..]); let x1_hash = as sp_core::Hasher>::hash(&x1[1..]); let index = vec![ @@ -3580,7 +3653,10 @@ pub(crate) mod tests { Default::default(), None, Default::default(), - vec![0u64.into(), 1u64.into()], + vec![ + UncheckedXt::new_transaction(0.into(), ()), + UncheckedXt::new_transaction(1.into(), ()), + ], Some(index), ) .unwrap(); @@ -3602,8 +3678,9 @@ pub(crate) mod tests { fn index_invalid_size() { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); - let x0 = ExtrinsicWrapper::from(0u64).encode(); - let x1 = ExtrinsicWrapper::from(1u64).encode(); + let x0 = UncheckedXt::new_transaction(0.into(), ()).encode(); + let x1 = UncheckedXt::new_transaction(1.into(), ()).encode(); + let x0_hash = as sp_core::Hasher>::hash(&x0[..]); let x1_hash = as sp_core::Hasher>::hash(&x1[..]); let index = vec![ @@ -3624,7 +3701,10 @@ pub(crate) mod tests { Default::default(), None, Default::default(), - vec![0u64.into(), 1u64.into()], + vec![ + UncheckedXt::new_transaction(0.into(), ()), + UncheckedXt::new_transaction(1.into(), ()), + ], Some(index), ) .unwrap(); @@ -3638,7 +3718,7 @@ pub(crate) mod tests { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); - let x1 = ExtrinsicWrapper::from(0u64).encode(); + let x1 = UncheckedXt::new_transaction(0.into(), ()).encode(); let x1_hash = as sp_core::Hasher>::hash(&x1[1..]); for i in 0..10 { let mut index = Vec::new(); @@ -3658,7 +3738,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], Some(index), ) .unwrap(); @@ -3692,7 +3772,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -3707,7 +3787,7 @@ pub(crate) mod tests { blocks[1], None, sp_core::H256::random(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -3721,7 +3801,7 @@ pub(crate) mod tests { blocks[0], None, sp_core::H256::random(), - vec![42.into()], + vec![UncheckedXt::new_transaction(42.into(), ())], None, ) .unwrap(); @@ -4194,7 +4274,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -4209,7 +4289,10 @@ pub(crate) mod tests { // Check that we can properly access values when there is reference count // but no value. - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(1.into(), ())]), + bc.body(blocks[1]).unwrap() + ); // Block 1 gets pinned three times backend.pin_block(blocks[1]).unwrap(); @@ -4226,27 +4309,42 @@ pub(crate) mod tests { // Block 0, 1, 2, 3 are pinned, so all values should be cached. // Block 4 is inside the pruning window, its value is in db. - assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0.into(), ())]), + bc.body(blocks[0]).unwrap() + ); - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(1.into(), ())]), + bc.body(blocks[1]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(1))), bc.justifications(blocks[1]).unwrap() ); - assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(blocks[2]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(2))), bc.justifications(blocks[2]).unwrap() ); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(3))), bc.justifications(blocks[3]).unwrap() ); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(4))), bc.justifications(blocks[4]).unwrap() @@ -4277,7 +4375,10 @@ pub(crate) mod tests { assert!(bc.justifications(blocks[1]).unwrap().is_none()); // Block 4 is inside the pruning window and still kept - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(4))), bc.justifications(blocks[4]).unwrap() @@ -4285,9 +4386,16 @@ pub(crate) mod tests { // Block tree: // 0 -> 1 -> 2 -> 3 -> 4 -> 5 - let hash = - insert_block(&backend, 5, prev_hash, None, Default::default(), vec![5.into()], None) - .unwrap(); + let hash = insert_block( + &backend, + 5, + prev_hash, + None, + Default::default(), + vec![UncheckedXt::new_transaction(5.into(), ())], + None, + ) + .unwrap(); blocks.push(hash); backend.pin_block(blocks[4]).unwrap(); @@ -4302,12 +4410,18 @@ pub(crate) mod tests { assert!(bc.body(blocks[2]).unwrap().is_none()); assert!(bc.body(blocks[3]).unwrap().is_none()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(4))), bc.justifications(blocks[4]).unwrap() ); - assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(5.into(), ())]), + bc.body(blocks[5]).unwrap() + ); assert!(bc.header(blocks[5]).ok().flatten().is_some()); backend.unpin_block(blocks[4]); @@ -4317,9 +4431,16 @@ pub(crate) mod tests { // Append a justification to block 5. backend.append_justification(blocks[5], ([0, 0, 0, 1], vec![42])).unwrap(); - let hash = - insert_block(&backend, 6, blocks[5], None, Default::default(), vec![6.into()], None) - .unwrap(); + let hash = insert_block( + &backend, + 6, + blocks[5], + None, + Default::default(), + vec![UncheckedXt::new_transaction(6.into(), ())], + None, + ) + .unwrap(); blocks.push(hash); // Pin block 5 so it gets loaded into the cache on prune @@ -4332,7 +4453,10 @@ pub(crate) mod tests { op.mark_finalized(blocks[6], None).unwrap(); backend.commit_operation(op).unwrap(); - assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(5.into(), ())]), + bc.body(blocks[5]).unwrap() + ); assert!(bc.header(blocks[5]).ok().flatten().is_some()); let mut expected = Justifications::from(build_justification(5)); expected.append(([0, 0, 0, 1], vec![42])); @@ -4354,7 +4478,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -4370,16 +4494,26 @@ pub(crate) mod tests { // Block tree: // 0 -> 1 -> 2 -> 3 -> 4 // \ -> 2 -> 3 - let fork_hash_root = - insert_block(&backend, 2, blocks[1], None, H256::random(), vec![2.into()], None) - .unwrap(); + let fork_hash_root = insert_block( + &backend, + 2, + blocks[1], + None, + H256::random(), + vec![UncheckedXt::new_transaction(2.into(), ())], + None, + ) + .unwrap(); let fork_hash_3 = insert_block( &backend, 3, fork_hash_root, None, H256::random(), - vec![3.into(), 11.into()], + vec![ + UncheckedXt::new_transaction(3.into(), ()), + UncheckedXt::new_transaction(11.into(), ()), + ], None, ) .unwrap(); @@ -4400,14 +4534,35 @@ pub(crate) mod tests { } let bc = backend.blockchain(); - assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); - assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0.into(), ())]), + bc.body(blocks[0]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(1.into(), ())]), + bc.body(blocks[1]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(blocks[2]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); // Check the fork hashes. assert_eq!(None, bc.body(fork_hash_root).unwrap()); - assert_eq!(Some(vec![3.into(), 11.into()]), bc.body(fork_hash_3).unwrap()); + assert_eq!( + Some(vec![ + UncheckedXt::new_transaction(3.into(), ()), + UncheckedXt::new_transaction(11.into(), ()) + ]), + bc.body(fork_hash_3).unwrap() + ); // Unpin all blocks, except the forked one. for block in &blocks { diff --git a/substrate/client/db/src/pinned_blocks_cache.rs b/substrate/client/db/src/pinned_blocks_cache.rs index 46c9287fb19ac1361153dbb65f4f0b353170042f..ac4aad07765cfbaa4d9c66efa4b44730b0b0a04c 100644 --- a/substrate/client/db/src/pinned_blocks_cache.rs +++ b/substrate/client/db/src/pinned_blocks_cache.rs @@ -20,7 +20,7 @@ use schnellru::{Limiter, LruMap}; use sp_runtime::{traits::Block as BlockT, Justifications}; const LOG_TARGET: &str = "db::pin"; -const PINNING_CACHE_SIZE: usize = 1024; +const PINNING_CACHE_SIZE: usize = 2048; /// Entry for pinned blocks cache. struct PinnedBlockCacheEntry { diff --git a/substrate/client/db/src/utils.rs b/substrate/client/db/src/utils.rs index abf9c4629cee47e31d18a18c827212b48fe31511..d2a5f7e718a6f0af4188fc18ed0c551a51f5c974 100644 --- a/substrate/client/db/src/utils.rs +++ b/substrate/client/db/src/utils.rs @@ -582,14 +582,19 @@ impl<'a, 'b> codec::Input for JoinInput<'a, 'b> { mod tests { use super::*; use codec::Input; - use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper}; - type Block = RawBlock>; + use sp_runtime::{ + generic::UncheckedExtrinsic, + testing::{Block as RawBlock, MockCallU64}, + }; + + pub type UncheckedXt = UncheckedExtrinsic; + type Block = RawBlock; #[cfg(feature = "rocksdb")] #[test] fn database_type_subdir_migration() { use std::path::PathBuf; - type Block = RawBlock>; + type Block = RawBlock; fn check_dir_for_db_type( db_type: DatabaseType, diff --git a/substrate/client/executor/Cargo.toml b/substrate/client/executor/Cargo.toml index aa8e8c9abf295cabcab686d7d2dbfd5e78a41157..4e36de3c4f47c9f4f98f28d694ec3f23cbd81add 100644 --- a/substrate/client/executor/Cargo.toml +++ b/substrate/client/executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-executor" -version = "0.10.0-dev" +version = "0.32.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -40,6 +40,7 @@ assert_matches = "1.3.0" wat = "1.0" sc-runtime-test = { path = "runtime-test" } substrate-test-runtime = { path = "../../test-utils/runtime" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } sp-state-machine = { path = "../../primitives/state-machine" } sp-runtime = { path = "../../primitives/runtime" } sp-maybe-compressed-blob = { path = "../../primitives/maybe-compressed-blob" } diff --git a/substrate/client/executor/common/Cargo.toml b/substrate/client/executor/common/Cargo.toml index b3db6a86a2030ee47c8d241805ee183b7d953af5..bfd1fa6b74014bfbac893bd4e503e70bd76d9cf0 100644 --- a/substrate/client/executor/common/Cargo.toml +++ b/substrate/client/executor/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-executor-common" -version = "0.10.0-dev" +version = "0.29.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -17,8 +17,8 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -thiserror = "1.0.48" -wasm-instrument = "0.3" +thiserror = { workspace = true } +wasm-instrument = "0.4" sc-allocator = { path = "../../allocator" } sp-maybe-compressed-blob = { path = "../../../primitives/maybe-compressed-blob" } sp-wasm-interface = { path = "../../../primitives/wasm-interface" } diff --git a/substrate/client/executor/src/executor.rs b/substrate/client/executor/src/executor.rs index 499bb704b16990de49d2b3311c48fa8ee2c1813e..d56a3b389ef42868d0543303c5fbd63ea8d2d884 100644 --- a/substrate/client/executor/src/executor.rs +++ b/substrate/client/executor/src/executor.rs @@ -518,7 +518,7 @@ where runtime_code, ext, heap_alloc_strategy, - |_, mut instance, _onchain_version, mut ext| { + |_, mut instance, _on_chain_version, mut ext| { with_externalities_safe(&mut **ext, move || instance.call_export(method, data)) }, ); @@ -682,18 +682,18 @@ impl CodeExecutor for NativeElseWasmExecut runtime_code, ext, heap_alloc_strategy, - |_, mut instance, onchain_version, mut ext| { - let onchain_version = - onchain_version.ok_or_else(|| Error::ApiError("Unknown version".into()))?; + |_, mut instance, on_chain_version, mut ext| { + let on_chain_version = + on_chain_version.ok_or_else(|| Error::ApiError("Unknown version".into()))?; let can_call_with = - onchain_version.can_call_with(&self.native_version.runtime_version); + on_chain_version.can_call_with(&self.native_version.runtime_version); if use_native && can_call_with { tracing::trace!( target: "executor", native = %self.native_version.runtime_version, - chain = %onchain_version, + chain = %on_chain_version, "Request for native execution succeeded", ); @@ -705,7 +705,7 @@ impl CodeExecutor for NativeElseWasmExecut tracing::trace!( target: "executor", native = %self.native_version.runtime_version, - chain = %onchain_version, + chain = %on_chain_version, "Request for native execution failed", ); } diff --git a/substrate/client/executor/src/integration_tests/mod.rs b/substrate/client/executor/src/integration_tests/mod.rs index 0bd080c243574c8af93e8f2bd66190bef35ebe24..7f91b3ffe7644e37ebee0bc035faafbf38e21148 100644 --- a/substrate/client/executor/src/integration_tests/mod.rs +++ b/substrate/client/executor/src/integration_tests/mod.rs @@ -25,12 +25,13 @@ use sc_executor_common::{ }; use sc_runtime_test::wasm_binary_unwrap; use sp_core::{ - blake2_128, blake2_256, ed25519, map, + ed25519, map, offchain::{testing, OffchainDbExt, OffchainWorkerExt}, sr25519, traits::Externalities, Pair, }; +use sp_crypto_hashing::{blake2_128, blake2_256, sha2_256, twox_128, twox_256}; use sp_runtime::traits::BlakeTwo256; use sp_state_machine::TestExternalities as CoreTestExternalities; use sp_trie::{LayoutV1 as Layout, TrieConfiguration}; @@ -224,12 +225,12 @@ fn blake2_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); assert_eq!( call_in_wasm("test_blake2_256", &[0], wasm_method, &mut ext,).unwrap(), - blake2_256(&b""[..]).to_vec().encode(), + blake2_256(b"").to_vec().encode(), ); assert_eq!( call_in_wasm("test_blake2_256", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,) .unwrap(), - blake2_256(&b"Hello world!"[..]).to_vec().encode(), + blake2_256(b"Hello world!").to_vec().encode(), ); } @@ -239,12 +240,12 @@ fn blake2_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); assert_eq!( call_in_wasm("test_blake2_128", &[0], wasm_method, &mut ext,).unwrap(), - blake2_128(&b""[..]).to_vec().encode(), + blake2_128(b"").to_vec().encode(), ); assert_eq!( call_in_wasm("test_blake2_128", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,) .unwrap(), - blake2_128(&b"Hello world!"[..]).to_vec().encode(), + blake2_128(b"Hello world!").to_vec().encode(), ); } @@ -254,18 +255,12 @@ fn sha2_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); assert_eq!( call_in_wasm("test_sha2_256", &[0], wasm_method, &mut ext,).unwrap(), - array_bytes::hex2bytes_unchecked( - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - ) - .encode(), + sha2_256(b"").to_vec().encode(), ); assert_eq!( call_in_wasm("test_sha2_256", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,) .unwrap(), - array_bytes::hex2bytes_unchecked( - "c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a" - ) - .encode(), + sha2_256(b"Hello world!").to_vec().encode(), ); } @@ -275,18 +270,12 @@ fn twox_256_should_work(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); assert_eq!( call_in_wasm("test_twox_256", &[0], wasm_method, &mut ext,).unwrap(), - array_bytes::hex2bytes_unchecked( - "99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a" - ) - .encode(), + twox_256(b"").to_vec().encode() ); assert_eq!( call_in_wasm("test_twox_256", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,) .unwrap(), - array_bytes::hex2bytes_unchecked( - "b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74" - ) - .encode(), + twox_256(b"Hello world!").to_vec().encode() ); } @@ -296,12 +285,12 @@ fn twox_128_should_work(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); assert_eq!( call_in_wasm("test_twox_128", &[0], wasm_method, &mut ext,).unwrap(), - array_bytes::hex2bytes_unchecked("99e9d85137db46ef4bbea33613baafd5").encode(), + twox_128(b"").to_vec().encode() ); assert_eq!( call_in_wasm("test_twox_128", &b"Hello world!".to_vec().encode(), wasm_method, &mut ext,) .unwrap(), - array_bytes::hex2bytes_unchecked("b27dfd7f223f177f2a13647b533599af").encode(), + twox_128(b"Hello world!").to_vec().encode() ); } diff --git a/substrate/client/executor/src/lib.rs b/substrate/client/executor/src/lib.rs index 25bad81938f383e66eb1e63fb1c8ddcbea3a387f..6b99f0a6ee03b303d2d97ce05040828e3248312b 100644 --- a/substrate/client/executor/src/lib.rs +++ b/substrate/client/executor/src/lib.rs @@ -29,7 +29,6 @@ //! wasm engine used, instance cache. #![warn(missing_docs)] -#![recursion_limit = "128"] #[macro_use] mod executor; diff --git a/substrate/client/executor/wasmtime/Cargo.toml b/substrate/client/executor/wasmtime/Cargo.toml index c1e866502fc42623eb09268933ce79a2579120ad..75cc76a235430fa33b735e8b234493a24a3e2961 100644 --- a/substrate/client/executor/wasmtime/Cargo.toml +++ b/substrate/client/executor/wasmtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-executor-wasmtime" -version = "0.10.0-dev" +version = "0.29.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = "0.4.17" +log = { workspace = true, default-features = true } cfg-if = "1.0" libc = "0.2.152" parking_lot = "0.12.1" diff --git a/substrate/client/informant/Cargo.toml b/substrate/client/informant/Cargo.toml index 8373e5a54c1b3a689ecca8de9090889db07f6c9c..bd15e94ebafab26c89f76927d9d2f9c576ca1541 100644 --- a/substrate/client/informant/Cargo.toml +++ b/substrate/client/informant/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-informant" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true description = "Substrate informant." edition.workspace = true @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] ansi_term = "0.12.1" futures = "0.3.21" futures-timer = "3.0.1" -log = "0.4.17" +log = { workspace = true, default-features = true } sc-client-api = { path = "../api" } sc-network-common = { path = "../network/common" } sc-network-sync = { path = "../network/sync" } diff --git a/substrate/client/keystore/Cargo.toml b/substrate/client/keystore/Cargo.toml index 8fa6221ff197d4140934870a193ad0e634745bca..908e0aa8f38ced48facaf99d05c0d7771f03fa26 100644 --- a/substrate/client/keystore/Cargo.toml +++ b/substrate/client/keystore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-keystore" -version = "4.0.0-dev" +version = "25.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -19,8 +19,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "6.1" parking_lot = "0.12.1" -serde_json = "1.0.111" -thiserror = "1.0" +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } sp-application-crypto = { path = "../../primitives/application-crypto" } sp-core = { path = "../../primitives/core" } sp-keystore = { path = "../../primitives/keystore" } diff --git a/substrate/client/keystore/src/local.rs b/substrate/client/keystore/src/local.rs index 3b29f435e2a942ffe96dd02a8009af4d33693995..8b922c11cbca990a0f4f87e4e3f25bbec2a55290 100644 --- a/substrate/client/keystore/src/local.rs +++ b/substrate/client/keystore/src/local.rs @@ -37,7 +37,7 @@ use sp_core::bandersnatch; } sp_keystore::bls_experimental_enabled! { -use sp_core::{bls377, bls381, ecdsa_bls377}; +use sp_core::{bls377, bls381, ecdsa_bls377, KeccakHasher}; } use crate::{Error, Result}; @@ -47,6 +47,13 @@ pub struct LocalKeystore(RwLock); impl LocalKeystore { /// Create a local keystore from filesystem. + /// + /// The keystore will be created at `path`. The keystore optionally supports to encrypt/decrypt + /// the keys in the keystore using `password`. + /// + /// NOTE: Even when passing a `password`, the keys on disk appear to look like normal secret + /// uris. However, without having the correct password the secret uri will not generate the + /// correct private key. See [`SecretUri`](sp_core::crypto::SecretUri) for more information. pub fn open>(path: T, password: Option) -> Result { let inner = KeystoreInner::open(path, password)?; Ok(Self(RwLock::new(inner))) @@ -136,6 +143,13 @@ impl LocalKeystore { } impl Keystore for LocalKeystore { + /// Insert a new secret key. + /// + /// WARNING: if the secret keypair has been manually generated using a password + /// (e.g. using methods such as [`sp_core::crypto::Pair::from_phrase`]) then such + /// a password must match the one used to open the keystore via [`LocalKeystore::open`]. + /// If the passwords doesn't match then the inserted key ends up being unusable under + /// the current keystore instance. fn insert( &self, key_type: KeyTypeId, @@ -391,6 +405,20 @@ impl Keystore for LocalKeystore { self.sign::(key_type, public, msg) } + fn ecdsa_bls377_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls377::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + let sig = self.0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.sign_with_hasher::(msg)); + Ok(sig) + } + + } } diff --git a/substrate/client/merkle-mountain-range/Cargo.toml b/substrate/client/merkle-mountain-range/Cargo.toml index f6dbaf86c51562377c6528b3456b65abb2547fee..60232bccb0e08d1fb65aeb6d94723c03fcf77fa1 100644 --- a/substrate/client/merkle-mountain-range/Cargo.toml +++ b/substrate/client/merkle-mountain-range/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mmr-gadget" -version = "4.0.0-dev" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -16,7 +16,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3" -log = "0.4" +log = { workspace = true, default-features = true } sp-api = { path = "../../primitives/api" } sp-blockchain = { path = "../../primitives/blockchain" } sc-client-api = { path = "../api" } diff --git a/substrate/client/merkle-mountain-range/rpc/Cargo.toml b/substrate/client/merkle-mountain-range/rpc/Cargo.toml index 8eb48d65f81e9ca73589a30d95d05f74d3c9be71..9b391b76ea00b5e48700b7213afb691c149167ab 100644 --- a/substrate/client/merkle-mountain-range/rpc/Cargo.toml +++ b/substrate/client/merkle-mountain-range/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mmr-rpc" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,14 +16,13 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } -serde = { version = "1.0.195", features = ["derive"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } +serde = { features = ["derive"], workspace = true, default-features = true } sp-api = { path = "../../../primitives/api" } sp-blockchain = { path = "../../../primitives/blockchain" } sp-core = { path = "../../../primitives/core" } sp-mmr-primitives = { path = "../../../primitives/merkle-mountain-range" } sp-runtime = { path = "../../../primitives/runtime" } -anyhow = "1" [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } diff --git a/substrate/client/merkle-mountain-range/rpc/src/lib.rs b/substrate/client/merkle-mountain-range/rpc/src/lib.rs index 1653749ffab9411a00f58b1ec097e4eef0a43d3d..b4da9848de54cbc10ee549c3bd8cf1b033559722 100644 --- a/substrate/client/merkle-mountain-range/rpc/src/lib.rs +++ b/substrate/client/merkle-mountain-range/rpc/src/lib.rs @@ -26,7 +26,7 @@ use codec::{Codec, Decode, Encode}; use jsonrpsee::{ core::{async_trait, RpcResult}, proc_macros::rpc, - types::error::{CallError, ErrorObject}, + types::{error::ErrorObject, ErrorObjectOwned}, }; use serde::{Deserialize, Serialize}; @@ -189,11 +189,9 @@ where fn verify_proof(&self, proof: LeavesProof<::Hash>) -> RpcResult { let mut api = self.client.runtime_api(); - let leaves = Decode::decode(&mut &proof.leaves.0[..]) - .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; + let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?; - let decoded_proof = Decode::decode(&mut &proof.proof.0[..]) - .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; + let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?; api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); @@ -211,11 +209,9 @@ where ) -> RpcResult { let api = self.client.runtime_api(); - let leaves = Decode::decode(&mut &proof.leaves.0[..]) - .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; + let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?; - let decoded_proof = Decode::decode(&mut &proof.proof.0[..]) - .map_err(|e| CallError::InvalidParams(anyhow::Error::new(e)))?; + let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?; api.verify_proof_stateless(proof.block_hash, mmr_root, leaves, decoded_proof) .map_err(runtime_error_into_rpc_error)? @@ -226,7 +222,7 @@ where } /// Converts an mmr-specific error into a [`CallError`]. -fn mmr_error_into_rpc_error(err: MmrError) -> CallError { +fn mmr_error_into_rpc_error(err: MmrError) -> ErrorObjectOwned { let error_code = MMR_ERROR + match err { MmrError::LeafNotFound => 1, @@ -237,16 +233,20 @@ fn mmr_error_into_rpc_error(err: MmrError) -> CallError { _ => 0, }; - CallError::Custom(ErrorObject::owned(error_code, err.to_string(), Some(format!("{:?}", err)))) + ErrorObject::owned(error_code, err.to_string(), Some(format!("{:?}", err))) } /// Converts a runtime trap into a [`CallError`]. -fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> CallError { - CallError::Custom(ErrorObject::owned( - RUNTIME_ERROR, - "Runtime trapped", - Some(format!("{:?}", err)), - )) +fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> ErrorObjectOwned { + ErrorObject::owned(RUNTIME_ERROR, "Runtime trapped", Some(format!("{:?}", err))) +} + +fn invalid_params(e: impl std::error::Error) -> ErrorObjectOwned { + ErrorObject::owned( + jsonrpsee::types::error::ErrorCode::InvalidParams.code(), + e.to_string(), + None::<()>, + ) } #[cfg(test)] diff --git a/substrate/client/mixnet/Cargo.toml b/substrate/client/mixnet/Cargo.toml index e8543b5bdf2cfb03a156255be29249e5811a22c4..736184f4668c8c96c660502774da90aedd85b731 100644 --- a/substrate/client/mixnet/Cargo.toml +++ b/substrate/client/mixnet/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate mixnet service" name = "sc-mixnet" -version = "0.1.0-dev" +version = "0.4.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition = "2021" @@ -24,7 +24,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = futures = "0.3.25" futures-timer = "3.0.2" libp2p-identity = { version = "0.1.3", features = ["peerid"] } -log = "0.4.17" +log = { workspace = true, default-features = true } mixnet = "0.7.0" multiaddr = "0.17.1" parking_lot = "0.12.1" @@ -37,4 +37,4 @@ sp-core = { path = "../../primitives/core" } sp-keystore = { path = "../../primitives/keystore" } sp-mixnet = { path = "../../primitives/mixnet" } sp-runtime = { path = "../../primitives/runtime" } -thiserror = "1.0" +thiserror = { workspace = true } diff --git a/substrate/client/network-gossip/Cargo.toml b/substrate/client/network-gossip/Cargo.toml index c53c53fb1350fd9c2e213298f5853ecdee9079a4..a14761c0d6e81e4eeda2e1e3d8dacd5fd7b99d4a 100644 --- a/substrate/client/network-gossip/Cargo.toml +++ b/substrate/client/network-gossip/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Gossiping for the Substrate network protocol" name = "sc-network-gossip" -version = "0.10.0-dev" +version = "0.34.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -21,7 +21,7 @@ ahash = "0.8.2" futures = "0.3.21" futures-timer = "3.0.1" libp2p = "0.51.4" -log = "0.4.17" +log = { workspace = true, default-features = true } schnellru = "0.2.1" tracing = "0.1.29" prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } diff --git a/substrate/client/network-gossip/src/state_machine.rs b/substrate/client/network-gossip/src/state_machine.rs index 069d7cdba16599b4b4da0965a5d8e4588478d633..f1c830341ea7337df312e9181ef0ba7caafdc2ce 100644 --- a/substrate/client/network-gossip/src/state_machine.rs +++ b/substrate/client/network-gossip/src/state_machine.rs @@ -550,7 +550,8 @@ mod tests { NotificationSenderError, NotificationSenderT as NotificationSender, ReputationChange, }; use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, H256}, + generic::UncheckedExtrinsic, + testing::{Block as RawBlock, MockCallU64, H256}, traits::NumberFor, }; use std::{ @@ -559,7 +560,7 @@ mod tests { sync::{Arc, Mutex}, }; - type Block = RawBlock>; + type Block = RawBlock>; macro_rules! push_msg { ($consensus:expr, $topic:expr, $hash: expr, $m:expr) => { diff --git a/substrate/client/network/Cargo.toml b/substrate/client/network/Cargo.toml index f2e6f54547b96cd051f4a3c9d727ec1cde8505e9..cbf74440dc1a83933d1e994294a7d37609c72331 100644 --- a/substrate/client/network/Cargo.toml +++ b/substrate/client/network/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate network protocol" name = "sc-network" -version = "0.10.0-dev" +version = "0.34.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -30,16 +30,16 @@ futures-timer = "3.0.2" ip_network = "0.4.1" libp2p = { version = "0.51.4", features = ["dns", "identify", "kad", "macros", "mdns", "noise", "ping", "request-response", "tcp", "tokio", "websocket", "yamux"] } linked_hash_set = "0.1.3" -log = "0.4.17" +log = { workspace = true, default-features = true } mockall = "0.11.3" parking_lot = "0.12.1" partial_sort = "0.2.0" pin-project = "1.0.12" rand = "0.8.5" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } smallvec = "1.11.0" -thiserror = "1.0" +thiserror = { workspace = true } tokio = { version = "1.22.0", features = ["macros", "sync"] } tokio-stream = "0.1.7" unsigned-varint = { version = "0.7.1", features = ["asynchronous_codec", "futures"] } diff --git a/substrate/client/network/bitswap/Cargo.toml b/substrate/client/network/bitswap/Cargo.toml index b004c03e0256ffd3e7358a6f257b93e7c1436ac5..7ef3ea212427848510e2e4b910efbe3d54e01a48 100644 --- a/substrate/client/network/bitswap/Cargo.toml +++ b/substrate/client/network/bitswap/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate bitswap protocol" name = "sc-network-bitswap" -version = "0.10.0-dev" +version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -23,9 +23,9 @@ async-channel = "1.8.0" cid = "0.9.0" futures = "0.3.21" libp2p-identity = { version = "0.1.3", features = ["peerid"] } -log = "0.4.17" +log = { workspace = true, default-features = true } prost = "0.12" -thiserror = "1.0" +thiserror = { workspace = true } unsigned-varint = { version = "0.7.1", features = ["asynchronous_codec", "futures"] } sc-client-api = { path = "../../api" } sc-network = { path = ".." } @@ -36,7 +36,7 @@ sp-runtime = { path = "../../../primitives/runtime" } tokio = { version = "1.22.0", features = ["full"] } sc-block-builder = { path = "../../block-builder" } sc-consensus = { path = "../../consensus/common" } -sp-core = { path = "../../../primitives/core" } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } sp-consensus = { path = "../../../primitives/consensus/common" } substrate-test-runtime = { path = "../../../test-utils/runtime" } substrate-test-runtime-client = { path = "../../../test-utils/runtime/client" } diff --git a/substrate/client/network/bitswap/src/lib.rs b/substrate/client/network/bitswap/src/lib.rs index 9200c2fa7726bbae679a884bdd119955364a585e..0586354d6a0dd4c576d4d326d04bacd69069bcb9 100644 --- a/substrate/client/network/bitswap/src/lib.rs +++ b/substrate/client/network/bitswap/src/lib.rs @@ -500,7 +500,7 @@ mod tests { 0x70, cid::multihash::Multihash::wrap( u64::from(cid::multihash::Code::Blake2b256), - &sp_core::hashing::blake2_256(&ext.encode()[pattern_index..]), + &sp_crypto_hashing::blake2_256(&ext.encode()[pattern_index..]), ) .unwrap(), ) diff --git a/substrate/client/network/common/Cargo.toml b/substrate/client/network/common/Cargo.toml index 8e5ad61d5e4b28f09d97ec3f5c8570a8faa5014d..7a6d904b74b1820234a8fd5f169a4283e5abefc4 100644 --- a/substrate/client/network/common/Cargo.toml +++ b/substrate/client/network/common/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate network common" name = "sc-network-common" -version = "0.10.0-dev" +version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true diff --git a/substrate/client/network/light/Cargo.toml b/substrate/client/network/light/Cargo.toml index d59fde564309970e177e948d610ead4f771d1f2f..c757f727fb71a7c2261090d741e286d7851d8239 100644 --- a/substrate/client/network/light/Cargo.toml +++ b/substrate/client/network/light/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate light network protocol" name = "sc-network-light" -version = "0.10.0-dev" +version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -26,11 +26,11 @@ codec = { package = "parity-scale-codec", version = "3.6.1", features = [ ] } futures = "0.3.21" libp2p-identity = { version = "0.1.3", features = ["peerid"] } -log = "0.4.16" +log = { workspace = true, default-features = true } prost = "0.12" sp-blockchain = { path = "../../../primitives/blockchain" } sc-client-api = { path = "../../api" } sc-network = { path = ".." } sp-core = { path = "../../../primitives/core" } sp-runtime = { path = "../../../primitives/runtime" } -thiserror = "1.0" +thiserror = { workspace = true } diff --git a/substrate/client/network/statement/Cargo.toml b/substrate/client/network/statement/Cargo.toml index d3ce2a63ef14ddca0be86867dabcbd5bf541ec05..b6efee5d9d36ae9deffe9ecd4951176c829de0bc 100644 --- a/substrate/client/network/statement/Cargo.toml +++ b/substrate/client/network/statement/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate statement protocol" name = "sc-network-statement" -version = "0.10.0-dev" +version = "0.16.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -21,7 +21,7 @@ async-channel = "1.8.0" codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } futures = "0.3.21" libp2p = "0.51.4" -log = "0.4.17" +log = { workspace = true, default-features = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-network-common = { path = "../common" } sc-network-sync = { path = "../sync" } diff --git a/substrate/client/network/sync/Cargo.toml b/substrate/client/network/sync/Cargo.toml index abcb4cc8e426760f24f8cdca84076ebec31f1f0d..32ba3b6356c0ceb0dcf38bda7d333ffddf67d238 100644 --- a/substrate/client/network/sync/Cargo.toml +++ b/substrate/client/network/sync/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate sync network protocol" name = "sc-network-sync" -version = "0.10.0-dev" +version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -26,12 +26,12 @@ codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive futures = "0.3.21" futures-timer = "3.0.2" libp2p = "0.51.4" -log = "0.4.17" +log = { workspace = true, default-features = true } mockall = "0.11.3" prost = "0.12" schnellru = "0.2.1" smallvec = "1.11.0" -thiserror = "1.0" +thiserror = { workspace = true } tokio-stream = "0.1.14" tokio = { version = "1.32.0", features = ["macros", "time"] } fork-tree = { path = "../../../utils/fork-tree" } diff --git a/substrate/client/network/sync/src/blocks.rs b/substrate/client/network/sync/src/blocks.rs index 4988045a4786720771ad84d9bd3b3cb0aa96592a..a115ee94767454c14755517be82609dd22409cf4 100644 --- a/substrate/client/network/sync/src/blocks.rs +++ b/substrate/client/network/sync/src/blocks.rs @@ -265,9 +265,12 @@ mod test { use libp2p::PeerId; use sc_network_common::sync::message; use sp_core::H256; - use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper}; + use sp_runtime::{ + generic::UncheckedExtrinsic, + testing::{Block as RawBlock, MockCallU64}, + }; - type Block = RawBlock>; + type Block = RawBlock>; fn is_empty(bc: &BlockCollection) -> bool { bc.blocks.is_empty() && bc.peer_requests.is_empty() diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index 7486c091ebf13d1fef6422ab193cebd2b4dd24f9..24640fdc45576191f30d190a39ee8186d27520c8 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -33,7 +33,7 @@ use crate::{ }, strategy::{ warp::{EncodedProof, WarpProofRequest, WarpSyncParams}, - SyncingAction, SyncingConfig, SyncingStrategy, + StrategyKey, SyncingAction, SyncingConfig, SyncingStrategy, }, types::{ BadPeer, ExtendedPeerInfo, OpaqueStateRequest, OpaqueStateResponse, PeerRequest, SyncEvent, @@ -48,7 +48,7 @@ use futures::{ FutureExt, StreamExt, }; use libp2p::{request_response::OutboundFailure, PeerId}; -use log::{debug, error, trace}; +use log::{debug, error, trace, warn}; use prometheus_endpoint::{ register, Counter, Gauge, MetricSource, Opts, PrometheusError, Registry, SourcedGauge, U64, }; @@ -214,9 +214,6 @@ pub struct SyncingEngine { /// Syncing strategy. strategy: SyncingStrategy, - /// Syncing configuration for startegies. - syncing_config: SyncingConfig, - /// Blockchain client. client: Arc, @@ -441,8 +438,7 @@ where .map_or(futures::future::pending().boxed().fuse(), |rx| rx.boxed().fuse()); // Initialize syncing strategy. - let strategy = - SyncingStrategy::new(syncing_config.clone(), client.clone(), warp_sync_config)?; + let strategy = SyncingStrategy::new(syncing_config, client.clone(), warp_sync_config)?; let block_announce_protocol_name = block_announce_config.protocol_name().clone(); let (tx, service_rx) = tracing_unbounded("mpsc_chain_sync", 100_000); @@ -471,7 +467,6 @@ where roles, client, strategy, - syncing_config, network_service, peers: HashMap::new(), block_announce_data_cache: LruMap::new(ByLength::new(cache_capacity)), @@ -661,8 +656,17 @@ where Some(event) => self.process_notification_event(event), None => return, }, - warp_target_block_header = &mut self.warp_sync_target_block_header_rx_fused => - self.pass_warp_sync_target_block_header(warp_target_block_header), + // 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() => @@ -675,48 +679,61 @@ where // Process actions requested by a syncing strategy. if let Err(e) = self.process_strategy_actions() { - error!("Terminating `SyncingEngine` due to fatal error: {e:?}"); + error!( + target: LOG_TARGET, + "Terminating `SyncingEngine` due to fatal error: {e:?}.", + ); return } } } fn process_strategy_actions(&mut self) -> Result<(), ClientError> { - for action in self.strategy.actions() { + for action in self.strategy.actions()? { match action { - SyncingAction::SendBlockRequest { peer_id, request } => { + SyncingAction::SendBlockRequest { peer_id, key, request } => { // Sending block request implies dropping obsolete pending response as we are // not interested in it anymore (see [`SyncingAction::SendBlockRequest`]). - // Furthermore, only one request at a time is allowed to any peer. - let removed = self.pending_responses.remove(&peer_id); - self.send_block_request(peer_id, request.clone()); - - trace!( - target: LOG_TARGET, - "Processed `ChainSyncAction::SendBlockRequest` to {} with {:?}, stale response removed: {}.", - peer_id, - request, - removed, - ) + let removed = self.pending_responses.remove(peer_id, key); + self.send_block_request(peer_id, key, request.clone()); + + if removed { + warn!( + target: LOG_TARGET, + "Processed `ChainSyncAction::SendBlockRequest` to {} from {:?} with {:?}. \ + Stale response removed!", + peer_id, + key, + request, + ) + } else { + trace!( + target: LOG_TARGET, + "Processed `ChainSyncAction::SendBlockRequest` to {} from {:?} with {:?}.", + peer_id, + key, + request, + ) + } }, - SyncingAction::CancelBlockRequest { peer_id } => { - let removed = self.pending_responses.remove(&peer_id); + SyncingAction::CancelRequest { peer_id, key } => { + let removed = self.pending_responses.remove(peer_id, key); trace!( target: LOG_TARGET, "Processed {action:?}, response removed: {removed}.", ); }, - SyncingAction::SendStateRequest { peer_id, request } => { - self.send_state_request(peer_id, request); + SyncingAction::SendStateRequest { peer_id, key, request } => { + self.send_state_request(peer_id, key, request); trace!( target: LOG_TARGET, - "Processed `ChainSyncAction::SendBlockRequest` to {peer_id}.", + "Processed `ChainSyncAction::SendStateRequest` to {peer_id}.", ); }, - SyncingAction::SendWarpProofRequest { peer_id, request } => { - self.send_warp_proof_request(peer_id, request.clone()); + SyncingAction::SendWarpProofRequest { peer_id, key, request } => { + self.send_warp_proof_request(peer_id, key, request.clone()); trace!( target: LOG_TARGET, @@ -726,7 +743,7 @@ where ); }, SyncingAction::DropPeer(BadPeer(peer_id, rep)) => { - self.pending_responses.remove(&peer_id); + self.pending_responses.remove_all(&peer_id); self.network_service .disconnect_peer(peer_id, self.block_announce_protocol_name.clone()); self.network_service.report_peer(peer_id, rep); @@ -753,20 +770,8 @@ where number, ) }, - SyncingAction::Finished => { - let connected_peers = self.peers.iter().filter_map(|(peer_id, peer)| { - peer.info.roles.is_full().then_some(( - *peer_id, - peer.info.best_hash, - peer.info.best_number, - )) - }); - self.strategy.switch_to_next( - self.syncing_config.clone(), - self.client.clone(), - connected_peers, - )?; - }, + // Nothing to do, this is handled internally by `SyncingStrategy`. + SyncingAction::Finished => {}, } } @@ -948,23 +953,18 @@ where } } - fn pass_warp_sync_target_block_header(&mut self, header: Result) { + fn pass_warp_sync_target_block_header( + &mut self, + header: Result, + ) -> Result<(), ()> { match header { - Ok(header) => - if let SyncingStrategy::WarpSyncStrategy(warp_sync) = &mut self.strategy { - warp_sync.set_target_block(header); - } else { - error!( - target: LOG_TARGET, - "Cannot set warp sync target block: no warp sync strategy is active." - ); - debug_assert!(false); - }, + 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(()) }, } } @@ -1002,7 +1002,7 @@ where } self.strategy.remove_peer(&peer_id); - self.pending_responses.remove(&peer_id); + self.pending_responses.remove_all(&peer_id); self.event_streams .retain(|stream| stream.unbounded_send(SyncEvent::PeerDisconnected(peer_id)).is_ok()); } @@ -1167,7 +1167,7 @@ where Ok(()) } - fn send_block_request(&mut self, peer_id: PeerId, request: BlockRequest) { + fn send_block_request(&mut self, peer_id: PeerId, key: StrategyKey, request: BlockRequest) { if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send block request to unknown peer {peer_id}"); debug_assert!(false); @@ -1178,12 +1178,18 @@ where self.pending_responses.insert( peer_id, + key, PeerRequest::Block(request.clone()), async move { downloader.download_blocks(peer_id, request).await }.boxed(), ); } - fn send_state_request(&mut self, peer_id: PeerId, request: OpaqueStateRequest) { + fn send_state_request( + &mut self, + peer_id: PeerId, + key: StrategyKey, + request: OpaqueStateRequest, + ) { if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send state request to unknown peer {peer_id}"); debug_assert!(false); @@ -1192,7 +1198,7 @@ where let (tx, rx) = oneshot::channel(); - self.pending_responses.insert(peer_id, PeerRequest::State, rx.boxed()); + self.pending_responses.insert(peer_id, key, PeerRequest::State, rx.boxed()); match Self::encode_state_request(&request) { Ok(data) => { @@ -1213,7 +1219,12 @@ where } } - fn send_warp_proof_request(&mut self, peer_id: PeerId, request: WarpProofRequest) { + fn send_warp_proof_request( + &mut self, + peer_id: PeerId, + key: StrategyKey, + request: WarpProofRequest, + ) { if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send warp proof request to unknown peer {peer_id}"); debug_assert!(false); @@ -1222,7 +1233,7 @@ where let (tx, rx) = oneshot::channel(); - self.pending_responses.insert(peer_id, PeerRequest::WarpProof, rx.boxed()); + self.pending_responses.insert(peer_id, key, PeerRequest::WarpProof, rx.boxed()); match &self.warp_sync_protocol_name { Some(name) => self.network_service.start_request( @@ -1259,14 +1270,14 @@ where } fn process_response_event(&mut self, response_event: ResponseEvent) { - let ResponseEvent { peer_id, request, response } = response_event; + let ResponseEvent { peer_id, key, request, response } = response_event; match response { Ok(Ok((resp, _))) => match request { PeerRequest::Block(req) => { match self.block_downloader.block_response_into_blocks(&req, resp) { Ok(blocks) => { - self.strategy.on_block_response(peer_id, req, blocks); + self.strategy.on_block_response(peer_id, key, req, blocks); }, Err(BlockResponseError::DecodeFailed(e)) => { debug!( @@ -1311,10 +1322,10 @@ where }, }; - self.strategy.on_state_response(peer_id, response); + self.strategy.on_state_response(peer_id, key, response); }, PeerRequest::WarpProof => { - self.strategy.on_warp_proof_response(&peer_id, EncodedProof(resp)); + self.strategy.on_warp_proof_response(&peer_id, key, EncodedProof(resp)); }, }, Ok(Err(e)) => { diff --git a/substrate/client/network/sync/src/extra_requests.rs b/substrate/client/network/sync/src/justification_requests.rs similarity index 98% rename from substrate/client/network/sync/src/extra_requests.rs rename to substrate/client/network/sync/src/justification_requests.rs index cd3008d270b1f8b87b186afa4566536216714769..799b6df5831a5783038de15fab2b2eff091d64d9 100644 --- a/substrate/client/network/sync/src/extra_requests.rs +++ b/substrate/client/network/sync/src/justification_requests.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! Justification requests scheduling. [`ExtraRequests`] manages requesting justifications +//! from peers taking into account forks and their finalization (dropping pending requests +//! that don't make sense after one of the forks is finalized). + use crate::{ request_metrics::Metrics, strategy::chain_sync::{PeerSync, PeerSyncState}, diff --git a/substrate/client/network/sync/src/lib.rs b/substrate/client/network/sync/src/lib.rs index 494e3b87aa95514d6377c413cdc8c850c0d06258..9f6c0f45d089ce415f9e8c0ae792de1d7a327407 100644 --- a/substrate/client/network/sync/src/lib.rs +++ b/substrate/client/network/sync/src/lib.rs @@ -23,12 +23,12 @@ pub use strategy::warp::{WarpSyncParams, WarpSyncPhase, WarpSyncProgress}; pub use types::{SyncEvent, SyncEventStream, SyncState, SyncStatus, SyncStatusProvider}; mod block_announce_validator; -mod extra_requests; mod futures_stream; +mod justification_requests; mod pending_responses; mod request_metrics; mod schema; -mod types; +pub mod types; pub mod block_relay_protocol; pub mod block_request_handler; diff --git a/substrate/client/network/sync/src/pending_responses.rs b/substrate/client/network/sync/src/pending_responses.rs index 21e409eb847fe600d07343cc22533af8cf5d8c36..602c69df7ff96b80c9f176dd7646d843f2c63937 100644 --- a/substrate/client/network/sync/src/pending_responses.rs +++ b/substrate/client/network/sync/src/pending_responses.rs @@ -19,7 +19,7 @@ //! [`PendingResponses`] is responsible for keeping track of pending responses and //! polling them. [`Stream`] implemented by [`PendingResponses`] never terminates. -use crate::{types::PeerRequest, LOG_TARGET}; +use crate::{strategy::StrategyKey, types::PeerRequest, LOG_TARGET}; use futures::{ channel::oneshot, future::BoxFuture, @@ -42,6 +42,7 @@ type ResponseFuture = BoxFuture<'static, ResponseResult>; /// An event we receive once a pending response future resolves. pub(crate) struct ResponseEvent { pub peer_id: PeerId, + pub key: StrategyKey, pub request: PeerRequest, pub response: ResponseResult, } @@ -49,7 +50,8 @@ pub(crate) struct ResponseEvent { /// Stream taking care of polling pending responses. pub(crate) struct PendingResponses { /// Pending responses - pending_responses: StreamMap, ResponseResult)>>, + pending_responses: + StreamMap<(PeerId, StrategyKey), BoxStream<'static, (PeerRequest, ResponseResult)>>, /// Waker to implement never terminating stream waker: Option, } @@ -62,6 +64,7 @@ impl PendingResponses { pub fn insert( &mut self, peer_id: PeerId, + key: StrategyKey, request: PeerRequest, response_future: ResponseFuture, ) { @@ -70,7 +73,7 @@ impl PendingResponses { if self .pending_responses .insert( - peer_id, + (peer_id, key), Box::pin(async move { (request, response_future.await) }.into_stream()), ) .is_some() @@ -87,8 +90,20 @@ impl PendingResponses { } } - pub fn remove(&mut self, peer_id: &PeerId) -> bool { - self.pending_responses.remove(peer_id).is_some() + pub fn remove(&mut self, peer_id: PeerId, key: StrategyKey) -> bool { + self.pending_responses.remove(&(peer_id, key)).is_some() + } + + pub fn remove_all(&mut self, peer_id: &PeerId) { + let to_remove = self + .pending_responses + .keys() + .filter(|(peer, _key)| peer == peer_id) + .cloned() + .collect::>(); + to_remove.iter().for_each(|k| { + self.pending_responses.remove(k); + }); } pub fn len(&self) -> usize { @@ -104,13 +119,13 @@ impl Stream for PendingResponses { cx: &mut Context<'_>, ) -> Poll> { match self.pending_responses.poll_next_unpin(cx) { - Poll::Ready(Some((peer_id, (request, response)))) => { + Poll::Ready(Some(((peer_id, key), (request, response)))) => { // We need to manually remove the stream, because `StreamMap` doesn't know yet that // it's going to yield `None`, so may not remove it before the next request is made // to the same peer. - self.pending_responses.remove(&peer_id); + self.pending_responses.remove(&(peer_id, key)); - Poll::Ready(Some(ResponseEvent { peer_id, request, response })) + Poll::Ready(Some(ResponseEvent { peer_id, key, request, response })) }, Poll::Ready(None) | Poll::Pending => { self.waker = Some(cx.waker().clone()); diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index ee99252fc9117a8cc50f649079612f3dbacbbb83..dabcf37ae6321a4501095a4ba3140c1ccf424b74 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -30,7 +30,7 @@ use crate::{ }; use chain_sync::{ChainSync, ChainSyncAction, ChainSyncMode}; use libp2p::PeerId; -use log::{error, info}; +use log::{debug, error, info, warn}; use prometheus_endpoint::Registry; use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; @@ -41,11 +41,11 @@ use sc_network_common::sync::{ use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; use sp_consensus::BlockOrigin; use sp_runtime::{ - traits::{Block as BlockT, NumberFor}, + traits::{Block as BlockT, Header, NumberFor}, Justifications, }; use state::{StateStrategy, StateStrategyAction}; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use warp::{EncodedProof, WarpProofRequest, WarpSync, WarpSyncAction, WarpSyncConfig}; /// Corresponding `ChainSync` mode. @@ -71,16 +71,27 @@ pub struct SyncingConfig { pub metrics_registry: Option, } +/// The key identifying a specific strategy for responses routing. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum StrategyKey { + /// Warp sync initiated this request. + Warp, + /// State sync initiated this request. + State, + /// `ChainSync` initiated this request. + ChainSync, +} + #[derive(Debug)] pub enum SyncingAction { /// Send block request to peer. Always implies dropping a stale block request to the same peer. - SendBlockRequest { peer_id: PeerId, request: BlockRequest }, - /// Drop stale block request. - CancelBlockRequest { peer_id: PeerId }, + SendBlockRequest { peer_id: PeerId, key: StrategyKey, request: BlockRequest }, /// Send state request to peer. - SendStateRequest { peer_id: PeerId, request: OpaqueStateRequest }, + SendStateRequest { peer_id: PeerId, key: StrategyKey, request: OpaqueStateRequest }, /// Send warp proof request to peer. - SendWarpProofRequest { peer_id: PeerId, request: WarpProofRequest }, + SendWarpProofRequest { peer_id: PeerId, key: StrategyKey, request: WarpProofRequest }, + /// Drop stale request. + CancelRequest { peer_id: PeerId, key: StrategyKey }, /// Peer misbehaved. Disconnect, report it and cancel any requests to it. DropPeer(BadPeer), /// Import blocks. @@ -92,15 +103,75 @@ pub enum SyncingAction { number: NumberFor, justifications: Justifications, }, - /// Syncing strategy has finished. + /// Strategy finished. Nothing to do, this is handled by `SyncingStrategy`. Finished, } +impl SyncingAction { + fn is_finished(&self) -> bool { + matches!(self, SyncingAction::Finished) + } +} + +impl From> for SyncingAction { + fn from(action: WarpSyncAction) -> Self { + match action { + WarpSyncAction::SendWarpProofRequest { peer_id, request } => + SyncingAction::SendWarpProofRequest { peer_id, key: StrategyKey::Warp, request }, + WarpSyncAction::SendBlockRequest { peer_id, request } => + SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::Warp, request }, + WarpSyncAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), + WarpSyncAction::Finished => SyncingAction::Finished, + } + } +} + +impl From> for SyncingAction { + fn from(action: StateStrategyAction) -> Self { + match action { + StateStrategyAction::SendStateRequest { peer_id, request } => + SyncingAction::SendStateRequest { peer_id, key: StrategyKey::State, request }, + StateStrategyAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), + StateStrategyAction::ImportBlocks { origin, blocks } => + SyncingAction::ImportBlocks { origin, blocks }, + StateStrategyAction::Finished => SyncingAction::Finished, + } + } +} + +impl From> for SyncingAction { + fn from(action: ChainSyncAction) -> Self { + match action { + ChainSyncAction::SendBlockRequest { peer_id, request } => + SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::ChainSync, request }, + ChainSyncAction::SendStateRequest { peer_id, request } => + SyncingAction::SendStateRequest { peer_id, key: StrategyKey::ChainSync, request }, + ChainSyncAction::CancelRequest { peer_id } => + SyncingAction::CancelRequest { peer_id, key: StrategyKey::ChainSync }, + ChainSyncAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), + ChainSyncAction::ImportBlocks { origin, blocks } => + SyncingAction::ImportBlocks { origin, blocks }, + ChainSyncAction::ImportJustifications { peer_id, hash, number, justifications } => + SyncingAction::ImportJustifications { peer_id, hash, number, justifications }, + } + } +} + /// Proxy to specific syncing strategies. -pub enum SyncingStrategy { - WarpSyncStrategy(WarpSync), - StateSyncStrategy(StateStrategy), - ChainSyncStrategy(ChainSync), +pub struct SyncingStrategy { + /// Initial syncing configuration. + config: SyncingConfig, + /// Client used by syncing strategies. + client: Arc, + /// Warp strategy. + warp: Option>, + /// State strategy. + state: Option>, + /// `ChainSync` strategy.` + chain_sync: Option>, + /// Connected peers and their best blocks used to seed a new strategy when switching to it in + /// [`SyncingStrategy::proceed_to_next`]. + peer_best_blocks: HashMap)>, } impl SyncingStrategy @@ -123,37 +194,51 @@ where if let SyncMode::Warp = config.mode { let warp_sync_config = warp_sync_config .expect("Warp sync configuration must be supplied in warp sync mode."); - Ok(Self::WarpSyncStrategy(WarpSync::new(client.clone(), warp_sync_config))) + let warp_sync = WarpSync::new(client.clone(), warp_sync_config); + Ok(Self { + config, + client, + warp: Some(warp_sync), + state: None, + chain_sync: None, + peer_best_blocks: Default::default(), + }) } else { - Ok(Self::ChainSyncStrategy(ChainSync::new( + let chain_sync = ChainSync::new( chain_sync_mode(config.mode), client.clone(), config.max_parallel_downloads, config.max_blocks_per_request, - config.metrics_registry, - )?)) + config.metrics_registry.clone(), + std::iter::empty(), + )?; + Ok(Self { + config, + client, + warp: None, + state: None, + chain_sync: Some(chain_sync), + peer_best_blocks: Default::default(), + }) } } /// Notify that a new peer has connected. pub fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => - strategy.add_peer(peer_id, best_hash, best_number), - SyncingStrategy::StateSyncStrategy(strategy) => - strategy.add_peer(peer_id, best_hash, best_number), - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.add_peer(peer_id, best_hash, best_number), - } + self.peer_best_blocks.insert(peer_id, (best_hash, best_number)); + + self.warp.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); + self.state.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); + self.chain_sync.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); } /// Notify that a peer has disconnected. pub fn remove_peer(&mut self, peer_id: &PeerId) { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => strategy.remove_peer(peer_id), - SyncingStrategy::StateSyncStrategy(strategy) => strategy.remove_peer(peer_id), - SyncingStrategy::ChainSyncStrategy(strategy) => strategy.remove_peer(peer_id), - } + self.warp.as_mut().map(|s| s.remove_peer(peer_id)); + self.state.as_mut().map(|s| s.remove_peer(peer_id)); + self.chain_sync.as_mut().map(|s| s.remove_peer(peer_id)); + + self.peer_best_blocks.remove(peer_id); } /// Submit a validated block announcement. @@ -165,14 +250,31 @@ where peer_id: PeerId, announce: &BlockAnnounce, ) -> Option<(B::Hash, NumberFor)> { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => - strategy.on_validated_block_announce(is_best, peer_id, announce), - SyncingStrategy::StateSyncStrategy(strategy) => - strategy.on_validated_block_announce(is_best, peer_id, announce), - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.on_validated_block_announce(is_best, peer_id, announce), + let new_best = if let Some(ref mut warp) = self.warp { + warp.on_validated_block_announce(is_best, peer_id, announce) + } else if let Some(ref mut state) = self.state { + state.on_validated_block_announce(is_best, peer_id, announce) + } else if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_validated_block_announce(is_best, peer_id, announce) + } else { + error!(target: LOG_TARGET, "No syncing strategy is active."); + debug_assert!(false); + Some((announce.header.hash(), *announce.header.number())) + }; + + if let Some(new_best) = new_best { + if let Some(best) = self.peer_best_blocks.get_mut(&peer_id) { + *best = new_best; + } else { + debug!( + target: LOG_TARGET, + "Cannot update `peer_best_blocks` as peer {peer_id} is not known to `Strategy` \ + (already disconnected?)", + ); + } } + + new_best } /// Configure an explicit fork sync request in case external code has detected that there is a @@ -183,40 +285,33 @@ where hash: &B::Hash, number: NumberFor, ) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.set_sync_fork_request(peers, hash, number), + // Fork requests are only handled by `ChainSync`. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.set_sync_fork_request(peers.clone(), hash, number); } } /// Request extra justification. pub fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.request_justification(hash, number), + // Justifications can only be requested via `ChainSync`. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.request_justification(hash, number); } } /// Clear extra justification requests. pub fn clear_justification_requests(&mut self) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => strategy.clear_justification_requests(), + // Justification requests can only be cleared by `ChainSync`. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.clear_justification_requests(); } } /// Report a justification import (successful or not). pub fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.on_justification_import(hash, number, success), + // Only `ChainSync` is interested in justification import. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_justification_import(hash, number, success); } } @@ -224,36 +319,65 @@ where pub fn on_block_response( &mut self, peer_id: PeerId, + key: StrategyKey, request: BlockRequest, blocks: Vec>, ) { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => - strategy.on_block_response(peer_id, request, blocks), - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.on_block_response(peer_id, request, blocks), + if let (StrategyKey::Warp, Some(ref mut warp)) = (key, &mut self.warp) { + warp.on_block_response(peer_id, request, blocks); + } else if let (StrategyKey::ChainSync, Some(ref mut chain_sync)) = + (key, &mut self.chain_sync) + { + chain_sync.on_block_response(peer_id, request, blocks); + } else { + error!( + target: LOG_TARGET, + "`on_block_response()` called with unexpected key {key:?} \ + or corresponding strategy is not active.", + ); + debug_assert!(false); } } /// Process state response. - pub fn on_state_response(&mut self, peer_id: PeerId, response: OpaqueStateResponse) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(strategy) => - strategy.on_state_response(peer_id, response), - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.on_state_response(peer_id, response), + pub fn on_state_response( + &mut self, + peer_id: PeerId, + key: StrategyKey, + response: OpaqueStateResponse, + ) { + if let (StrategyKey::State, Some(ref mut state)) = (key, &mut self.state) { + state.on_state_response(peer_id, response); + } else if let (StrategyKey::ChainSync, Some(ref mut chain_sync)) = + (key, &mut self.chain_sync) + { + chain_sync.on_state_response(peer_id, response); + } else { + error!( + target: LOG_TARGET, + "`on_state_response()` called with unexpected key {key:?} \ + or corresponding strategy is not active.", + ); + debug_assert!(false); } } /// Process warp proof response. - pub fn on_warp_proof_response(&mut self, peer_id: &PeerId, response: EncodedProof) { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => - strategy.on_warp_proof_response(peer_id, response), - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(_) => {}, + pub fn on_warp_proof_response( + &mut self, + peer_id: &PeerId, + key: StrategyKey, + response: EncodedProof, + ) { + if let (StrategyKey::Warp, Some(ref mut warp)) = (key, &mut self.warp) { + warp.on_warp_proof_response(peer_id, response); + } else { + error!( + target: LOG_TARGET, + "`on_warp_proof_response()` called with unexpected key {key:?} \ + or warp strategy is not active", + ); + debug_assert!(false); } } @@ -264,226 +388,274 @@ where count: usize, results: Vec<(Result>, BlockImportError>, B::Hash)>, ) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(strategy) => - strategy.on_blocks_processed(imported, count, results), - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.on_blocks_processed(imported, count, results), + // Only `StateStrategy` and `ChainSync` are interested in block processing notifications. + if let Some(ref mut state) = self.state { + state.on_blocks_processed(imported, count, results); + } else if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_blocks_processed(imported, count, results); } } /// Notify a syncing strategy that a block has been finalized. pub fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.on_block_finalized(hash, number), + // Only `ChainSync` is interested in block finalization notifications. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_block_finalized(hash, number); } } /// Inform sync about a new best imported block. pub fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.update_chain_info(best_hash, best_number), + // This is relevant to `ChainSync` only. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.update_chain_info(best_hash, best_number); } } // Are we in major sync mode? pub fn is_major_syncing(&self) -> bool { - match self { - SyncingStrategy::WarpSyncStrategy(_) => true, - SyncingStrategy::StateSyncStrategy(_) => true, - SyncingStrategy::ChainSyncStrategy(strategy) => - strategy.status().state.is_major_syncing(), - } + self.warp.is_some() || + self.state.is_some() || + match self.chain_sync { + Some(ref s) => s.status().state.is_major_syncing(), + None => unreachable!("At least one syncing startegy is active; qed"), + } } /// Get the number of peers known to the syncing strategy. pub fn num_peers(&self) -> usize { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => strategy.num_peers(), - SyncingStrategy::StateSyncStrategy(strategy) => strategy.num_peers(), - SyncingStrategy::ChainSyncStrategy(strategy) => strategy.num_peers(), - } + self.peer_best_blocks.len() } /// Returns the current sync status. pub fn status(&self) -> SyncStatus { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => strategy.status(), - SyncingStrategy::StateSyncStrategy(strategy) => strategy.status(), - SyncingStrategy::ChainSyncStrategy(strategy) => strategy.status(), + // This function presumes that startegies are executed serially and must be refactored + // once we have parallel strategies. + if let Some(ref warp) = self.warp { + warp.status() + } else if let Some(ref state) = self.state { + state.status() + } else if let Some(ref chain_sync) = self.chain_sync { + chain_sync.status() + } else { + unreachable!("At least one syncing startegy is always active; qed") } } /// Get the total number of downloaded blocks. pub fn num_downloaded_blocks(&self) -> usize { - match self { - SyncingStrategy::WarpSyncStrategy(_) => 0, - SyncingStrategy::StateSyncStrategy(_) => 0, - SyncingStrategy::ChainSyncStrategy(strategy) => strategy.num_downloaded_blocks(), - } + self.chain_sync + .as_ref() + .map_or(0, |chain_sync| chain_sync.num_downloaded_blocks()) } /// Get an estimate of the number of parallel sync requests. pub fn num_sync_requests(&self) -> usize { - match self { - SyncingStrategy::WarpSyncStrategy(_) => 0, - SyncingStrategy::StateSyncStrategy(_) => 0, - SyncingStrategy::ChainSyncStrategy(strategy) => strategy.num_sync_requests(), - } + self.chain_sync.as_ref().map_or(0, |chain_sync| chain_sync.num_sync_requests()) } /// Report Prometheus metrics pub fn report_metrics(&self) { - match self { - SyncingStrategy::WarpSyncStrategy(_) => {}, - SyncingStrategy::StateSyncStrategy(_) => {}, - SyncingStrategy::ChainSyncStrategy(strategy) => strategy.report_metrics(), + if let Some(ref chain_sync) = self.chain_sync { + chain_sync.report_metrics(); + } + } + + /// 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) -> Box>> { - match self { - SyncingStrategy::WarpSyncStrategy(strategy) => - Box::new(strategy.actions().map(|action| match action { - WarpSyncAction::SendWarpProofRequest { peer_id, request } => - SyncingAction::SendWarpProofRequest { peer_id, request }, - WarpSyncAction::SendBlockRequest { peer_id, request } => - SyncingAction::SendBlockRequest { peer_id, request }, - WarpSyncAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), - WarpSyncAction::Finished => SyncingAction::Finished, - })), - SyncingStrategy::StateSyncStrategy(strategy) => - Box::new(strategy.actions().map(|action| match action { - StateStrategyAction::SendStateRequest { peer_id, request } => - SyncingAction::SendStateRequest { peer_id, request }, - StateStrategyAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), - StateStrategyAction::ImportBlocks { origin, blocks } => - SyncingAction::ImportBlocks { origin, blocks }, - StateStrategyAction::Finished => SyncingAction::Finished, - })), - SyncingStrategy::ChainSyncStrategy(strategy) => - Box::new(strategy.actions().map(|action| match action { - ChainSyncAction::SendBlockRequest { peer_id, request } => - SyncingAction::SendBlockRequest { peer_id, request }, - ChainSyncAction::CancelBlockRequest { peer_id } => - SyncingAction::CancelBlockRequest { peer_id }, - ChainSyncAction::SendStateRequest { peer_id, request } => - SyncingAction::SendStateRequest { peer_id, request }, - ChainSyncAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), - ChainSyncAction::ImportBlocks { origin, blocks } => - SyncingAction::ImportBlocks { origin, blocks }, - ChainSyncAction::ImportJustifications { - peer_id, - hash, - number, - justifications, - } => SyncingAction::ImportJustifications { - peer_id, - hash, - number, - justifications, - }, - })), + pub fn actions(&mut self) -> Result>, ClientError> { + // This function presumes that strategies are executed serially and must be refactored once + // we have parallel strategies. + let actions: Vec<_> = if let Some(ref mut warp) = self.warp { + warp.actions().map(Into::into).collect() + } else if let Some(ref mut state) = self.state { + state.actions().map(Into::into).collect() + } else if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.actions().map(Into::into).collect() + } else { + unreachable!("At least one syncing strategy is always active; qed") + }; + + if actions.iter().any(SyncingAction::is_finished) { + self.proceed_to_next()?; } + + Ok(actions) } - /// Switch to next strategy if the active one finished. - pub fn switch_to_next( - &mut self, - config: SyncingConfig, - client: Arc, - connected_peers: impl Iterator)>, - ) -> Result<(), ClientError> { - match self { - Self::WarpSyncStrategy(warp_sync) => { - match warp_sync.take_result() { - Some(res) => { - info!( - target: LOG_TARGET, - "Warp sync is complete, continuing with state sync." - ); - let state_sync = StateStrategy::new( - client, - res.target_header, - res.target_body, - res.target_justifications, - // skip proofs, only set to `true` in `FastUnsafe` sync mode - false, - connected_peers - .map(|(peer_id, _best_hash, best_number)| (peer_id, best_number)), - ); - - *self = Self::StateSyncStrategy(state_sync); - }, - None => { - error!( - target: LOG_TARGET, - "Warp sync failed. Falling back to full sync." - ); - let mut chain_sync = match ChainSync::new( - chain_sync_mode(config.mode), - client, - config.max_parallel_downloads, - config.max_blocks_per_request, - config.metrics_registry, - ) { - Ok(chain_sync) => chain_sync, - Err(e) => { - error!(target: LOG_TARGET, "Failed to start `ChainSync`."); - return Err(e) - }, - }; - // Let `ChainSync` know about connected peers. - connected_peers.into_iter().for_each( - |(peer_id, best_hash, best_number)| { - chain_sync.add_peer(peer_id, best_hash, best_number) - }, - ); - - *self = Self::ChainSyncStrategy(chain_sync); - }, - } - }, - Self::StateSyncStrategy(state_sync) => { - if state_sync.is_succeded() { - info!(target: LOG_TARGET, "State sync is complete, continuing with block sync."); - } else { - error!(target: LOG_TARGET, "State sync failed. Falling back to full sync."); - } - let mut chain_sync = match ChainSync::new( - chain_sync_mode(config.mode), - client, - config.max_parallel_downloads, - config.max_blocks_per_request, - config.metrics_registry, - ) { - Ok(chain_sync) => chain_sync, - Err(e) => { - error!(target: LOG_TARGET, "Failed to start `ChainSync`."); - return Err(e); - }, - }; - // Let `ChainSync` know about connected peers. - connected_peers.into_iter().for_each(|(peer_id, best_hash, best_number)| { - chain_sync.add_peer(peer_id, best_hash, best_number) - }); - - *self = Self::ChainSyncStrategy(chain_sync); - }, - Self::ChainSyncStrategy(_) => { - error!(target: LOG_TARGET, "`ChainSyncStrategy` is final startegy, cannot switch to next."); - debug_assert!(false); - }, + /// Proceed with the next strategy if the active one finished. + pub fn proceed_to_next(&mut self) -> Result<(), ClientError> { + // The strategies are switched as `WarpSync` -> `StateStartegy` -> `ChainSync`. + if let Some(ref mut warp) = self.warp { + match warp.take_result() { + Some(res) => { + info!( + target: LOG_TARGET, + "Warp sync is complete, continuing with state sync." + ); + let state_sync = StateStrategy::new( + self.client.clone(), + res.target_header, + res.target_body, + res.target_justifications, + false, + self.peer_best_blocks + .iter() + .map(|(peer_id, (_, best_number))| (*peer_id, *best_number)), + ); + + self.warp = None; + self.state = Some(state_sync); + Ok(()) + }, + None => { + error!( + target: LOG_TARGET, + "Warp sync failed. Continuing with full sync." + ); + let chain_sync = match ChainSync::new( + chain_sync_mode(self.config.mode), + self.client.clone(), + self.config.max_parallel_downloads, + self.config.max_blocks_per_request, + self.config.metrics_registry.clone(), + self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { + (*peer_id, *best_hash, *best_number) + }), + ) { + Ok(chain_sync) => chain_sync, + Err(e) => { + error!(target: LOG_TARGET, "Failed to start `ChainSync`."); + return Err(e) + }, + }; + + self.warp = None; + self.chain_sync = Some(chain_sync); + Ok(()) + }, + } + } else if let Some(state) = &self.state { + if state.is_succeded() { + info!(target: LOG_TARGET, "State sync is complete, continuing with block sync."); + } else { + error!(target: LOG_TARGET, "State sync failed. Falling back to full sync."); + } + let chain_sync = match ChainSync::new( + chain_sync_mode(self.config.mode), + self.client.clone(), + self.config.max_parallel_downloads, + self.config.max_blocks_per_request, + self.config.metrics_registry.clone(), + self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { + (*peer_id, *best_hash, *best_number) + }), + ) { + Ok(chain_sync) => chain_sync, + Err(e) => { + error!(target: LOG_TARGET, "Failed to start `ChainSync`."); + return Err(e); + }, + }; + + self.state = None; + self.chain_sync = Some(chain_sync); + Ok(()) + } else { + unreachable!("Only warp & state strategies can finish; qed") } - Ok(()) + } +} + +#[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 mut 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/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index 62c260d582b5a75bf38c81b9c5b3f5e65df1e8a4..ad0c75363e78ac948b4e10352328c64cefb792a1 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -30,7 +30,7 @@ use crate::{ blocks::BlockCollection, - extra_requests::ExtraRequests, + justification_requests::ExtraRequests, schema::v1::StateResponse, strategy::{ state_sync::{ImportResult, StateSync, StateSyncProvider}, @@ -212,10 +212,10 @@ struct GapSync { pub enum ChainSyncAction { /// Send block request to peer. Always implies dropping a stale block request to the same peer. SendBlockRequest { peer_id: PeerId, request: BlockRequest }, - /// Drop stale block request. - CancelBlockRequest { peer_id: PeerId }, /// Send state request to peer. SendStateRequest { peer_id: PeerId, request: OpaqueStateRequest }, + /// Drop stale request. + CancelRequest { peer_id: PeerId }, /// Peer misbehaved. Disconnect, report it and cancel the block request to it. DropPeer(BadPeer), /// Import blocks. @@ -373,6 +373,7 @@ where max_parallel_downloads: u32, max_blocks_per_request: u32, metrics_registry: Option, + initial_peers: impl Iterator)>, ) -> Result { let mut sync = Self { client, @@ -405,6 +406,10 @@ where }; sync.reset_sync_start_point()?; + initial_peers.for_each(|(peer_id, best_hash, best_number)| { + sync.add_peer(peer_id, best_hash, best_number); + }); + Ok(sync) } @@ -1312,34 +1317,35 @@ where ); let old_peers = std::mem::take(&mut self.peers); - old_peers.into_iter().for_each(|(peer_id, mut p)| { - // peers that were downloading justifications - // should be kept in that state. - if let PeerSyncState::DownloadingJustification(_) = p.state { - // We make sure our commmon number is at least something we have. - trace!( - target: LOG_TARGET, - "Keeping peer {} after restart, updating common number from={} => to={} (our best).", - peer_id, - p.common_number, - self.best_queued_number, - ); - p.common_number = self.best_queued_number; - self.peers.insert(peer_id, p); - return + old_peers.into_iter().for_each(|(peer_id, mut peer_sync)| { + match peer_sync.state { + PeerSyncState::Available => { + self.add_peer(peer_id, peer_sync.best_hash, peer_sync.best_number); + }, + PeerSyncState::AncestorSearch { .. } | + PeerSyncState::DownloadingNew(_) | + PeerSyncState::DownloadingStale(_) | + PeerSyncState::DownloadingGap(_) | + PeerSyncState::DownloadingState => { + // Cancel a request first, as `add_peer` may generate a new request. + self.actions.push(ChainSyncAction::CancelRequest { peer_id }); + self.add_peer(peer_id, peer_sync.best_hash, peer_sync.best_number); + }, + PeerSyncState::DownloadingJustification(_) => { + // Peers that were downloading justifications + // should be kept in that state. + // We make sure our commmon number is at least something we have. + trace!( + target: LOG_TARGET, + "Keeping peer {} after restart, updating common number from={} => to={} (our best).", + peer_id, + peer_sync.common_number, + self.best_queued_number, + ); + peer_sync.common_number = self.best_queued_number; + self.peers.insert(peer_id, peer_sync); + }, } - - // handle peers that were in other states. - let action = match self.add_peer_inner(peer_id, p.best_hash, p.best_number) { - // since the request is not a justification, remove it from pending responses - Ok(None) => ChainSyncAction::CancelBlockRequest { peer_id }, - // update the request if the new one is available - Ok(Some(request)) => ChainSyncAction::SendBlockRequest { peer_id, request }, - // this implies that we need to drop pending response from the peer - Err(bad_peer) => ChainSyncAction::DropPeer(bad_peer), - }; - - self.actions.push(action); }); } 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 c89096bc6c9045ddb42eb7e5bb65485d7a90f851..127b6862f0e0dca6c01a88bc6ee83b1f57e7c4dc 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync/test.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync/test.rs @@ -38,7 +38,9 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { let client = Arc::new(TestClientBuilder::new().build()); let peer_id = PeerId::random(); - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) + .unwrap(); let (a1_hash, a1_number) = { let a1 = BlockBuilderBuilder::new(&*client) @@ -91,7 +93,11 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { fn restart_doesnt_affect_peers_downloading_finality_data() { let mut client = Arc::new(TestClientBuilder::new().build()); - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None).unwrap(); + // we request max 8 blocks to always initiate block requests to both peers for the test to be + // deterministic + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 8, None, std::iter::empty()) + .unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); @@ -122,10 +128,13 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { // we wil send block requests to these peers // for these blocks we don't know about - assert!(sync - .block_requests() - .into_iter() - .all(|(p, _)| { p == peer_id1 || p == peer_id2 })); + let actions = sync.actions().collect::>(); + assert_eq!(actions.len(), 2); + assert!(actions.iter().all(|action| match action { + ChainSyncAction::SendBlockRequest { peer_id, .. } => + peer_id == &peer_id1 || peer_id == &peer_id2, + _ => false, + })); // add a new peer at a known block sync.add_peer(peer_id3, b1_hash, b1_number); @@ -146,22 +155,29 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { PeerSyncState::DownloadingJustification(b1_hash), ); - // clear old actions + // drop old actions let _ = sync.take_actions(); // we restart the sync state sync.restart(); - let actions = sync.take_actions().collect::>(); - // which should make us send out block requests to the first two peers - assert_eq!(actions.len(), 2); + // which should make us cancel and send out again block requests to the first two peers + let actions = sync.actions().collect::>(); + assert_eq!(actions.len(), 4); + let mut cancelled_first = HashSet::new(); assert!(actions.iter().all(|action| match action { - ChainSyncAction::SendBlockRequest { peer_id, .. } => - peer_id == &peer_id1 || peer_id == &peer_id2, + ChainSyncAction::CancelRequest { peer_id, .. } => { + cancelled_first.insert(peer_id); + peer_id == &peer_id1 || peer_id == &peer_id2 + }, + ChainSyncAction::SendBlockRequest { peer_id, .. } => { + assert!(cancelled_first.remove(peer_id)); + peer_id == &peer_id1 || peer_id == &peer_id2 + }, _ => false, })); - // peer 3 should be unaffected it was downloading finality data + // peer 3 should be unaffected as it was downloading finality data assert_eq!( sync.peers.get(&peer_id3).unwrap().state, PeerSyncState::DownloadingJustification(b1_hash), @@ -275,7 +291,9 @@ fn do_ancestor_search_when_common_block_to_best_qeued_gap_is_to_big() { let mut client = Arc::new(TestClientBuilder::new().build()); let info = client.info(); - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) + .unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); @@ -421,7 +439,9 @@ fn can_sync_huge_fork() { let info = client.info(); - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) + .unwrap(); let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone(); let just = (*b"TEST", Vec::new()); @@ -554,7 +574,9 @@ fn syncs_fork_without_duplicate_requests() { let info = client.info(); - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) + .unwrap(); let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone(); let just = (*b"TEST", Vec::new()); @@ -689,7 +711,9 @@ fn removes_target_fork_on_disconnect() { let mut client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..3).map(|_| build_block(&mut client, None, false)).collect::>(); - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) + .unwrap(); let peer_id1 = PeerId::random(); let common_block = blocks[1].clone(); @@ -714,7 +738,9 @@ fn can_import_response_with_missing_blocks() { let empty_client = Arc::new(TestClientBuilder::new().build()); - let mut sync = ChainSync::new(ChainSyncMode::Full, empty_client.clone(), 1, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, empty_client.clone(), 1, 64, None, std::iter::empty()) + .unwrap(); let peer_id1 = PeerId::random(); let best_block = blocks[3].clone(); @@ -745,7 +771,9 @@ fn ancestor_search_repeat() { #[test] fn sync_restart_removes_block_but_not_justification_requests() { let mut client = Arc::new(TestClientBuilder::new().build()); - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) + .unwrap(); let peers = vec![PeerId::random(), PeerId::random()]; @@ -813,7 +841,7 @@ fn sync_restart_removes_block_but_not_justification_requests() { let actions = sync.take_actions().collect::>(); for action in actions.iter() { match action { - ChainSyncAction::CancelBlockRequest { peer_id } => { + ChainSyncAction::CancelRequest { peer_id } => { pending_responses.remove(&peer_id); }, ChainSyncAction::SendBlockRequest { peer_id, .. } => { @@ -887,7 +915,9 @@ fn request_across_forks() { fork_blocks }; - let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None).unwrap(); + let mut sync = + ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) + .unwrap(); // Add the peers, all at the common ancestor 100. let common_block = blocks.last().unwrap(); diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index ae3f7b6005594d020dfc3f32602833586d16ed92..12d36ff9e01a9c16bfee482c5e9e641b13bb3b87 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -330,11 +330,6 @@ impl StateStrategy { } } - /// Get the number of peers known to syncing. - pub fn num_peers(&self) -> usize { - self.peers.len() - } - /// Get actions that should be performed by the owner on [`WarpSync`]'s behalf #[must_use] pub fn actions(&mut self) -> impl Iterator> { diff --git a/substrate/client/network/test/Cargo.toml b/substrate/client/network/test/Cargo.toml index dced6ed673057deb6959eb4b6fb001cff5be301a..4f57287a39cc8da10d3a30f769d7d584e16f0904 100644 --- a/substrate/client/network/test/Cargo.toml +++ b/substrate/client/network/test/Cargo.toml @@ -21,7 +21,7 @@ async-trait = "0.1.74" futures = "0.3.21" futures-timer = "3.0.1" libp2p = "0.51.4" -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" rand = "0.8.5" sc-block-builder = { path = "../../block-builder" } diff --git a/substrate/client/network/transactions/Cargo.toml b/substrate/client/network/transactions/Cargo.toml index 24b5087af1f42fab1e351d8eaa0a15bb5d2805bd..01c8ac8814d6aff1448caa18bbe8f3287139553f 100644 --- a/substrate/client/network/transactions/Cargo.toml +++ b/substrate/client/network/transactions/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate transaction protocol" name = "sc-network-transactions" -version = "0.10.0-dev" +version = "0.33.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -20,7 +20,7 @@ array-bytes = "6.1" codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } futures = "0.3.21" libp2p = "0.51.4" -log = "0.4.17" +log = { workspace = true, default-features = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-network = { path = ".." } sc-network-common = { path = "../common" } diff --git a/substrate/client/offchain/Cargo.toml b/substrate/client/offchain/Cargo.toml index b049ba0a3d89b5f7ecbca0980b4cbd7e2a258161..caa4bb03f40cba37b10444af6820833f8b4a8b84 100644 --- a/substrate/client/offchain/Cargo.toml +++ b/substrate/client/offchain/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate offchain workers" name = "sc-offchain" -version = "4.0.0-dev" +version = "29.0.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -26,7 +26,7 @@ hyper = { version = "0.14.16", features = ["http2", "stream"] } hyper-rustls = { version = "0.24.0", features = ["http2"] } libp2p = "0.51.4" num_cpus = "1.13" -once_cell = "1.8" +once_cell = "1.19" parking_lot = "0.12.1" rand = "0.8.5" threadpool = "1.7" @@ -42,7 +42,7 @@ sp-offchain = { path = "../../primitives/offchain" } sp-runtime = { path = "../../primitives/runtime" } sp-keystore = { path = "../../primitives/keystore" } sp-externalities = { path = "../../primitives/externalities" } -log = "0.4.17" +log = { workspace = true, default-features = true } [dev-dependencies] lazy_static = "1.4.0" diff --git a/substrate/client/proposer-metrics/Cargo.toml b/substrate/client/proposer-metrics/Cargo.toml index 664b72764a3b8b31b81cf437aa69f93c45f97b35..f560ce2d65e6e6336c7fa372618c858616ca134e 100644 --- a/substrate/client/proposer-metrics/Cargo.toml +++ b/substrate/client/proposer-metrics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-proposer-metrics" -version = "0.10.0-dev" +version = "0.17.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -16,5 +16,5 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = "0.4.17" +log = { workspace = true, default-features = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } diff --git a/substrate/client/proposer-metrics/src/lib.rs b/substrate/client/proposer-metrics/src/lib.rs index 012e8ca769a96cb51141c5a13ca7ba04d76a8123..2856300cf8027b45bb0ef32b8781c533ae2e340c 100644 --- a/substrate/client/proposer-metrics/src/lib.rs +++ b/substrate/client/proposer-metrics/src/lib.rs @@ -44,11 +44,14 @@ impl MetricsLink { } /// The reason why proposing a block ended. +#[derive(Clone, Copy, PartialEq, Eq)] pub enum EndProposingReason { NoMoreTransactions, HitDeadline, HitBlockSizeLimit, HitBlockWeightLimit, + /// No transactions are allowed in the block. + TransactionForbidden, } /// Authorship metrics. @@ -112,6 +115,7 @@ impl Metrics { EndProposingReason::NoMoreTransactions => "no_more_transactions", EndProposingReason::HitBlockSizeLimit => "hit_block_size_limit", EndProposingReason::HitBlockWeightLimit => "hit_block_weight_limit", + EndProposingReason::TransactionForbidden => "transactions_forbidden", }; self.end_proposing_reason.with_label_values(&[reason]).inc(); diff --git a/substrate/client/rpc-api/Cargo.toml b/substrate/client/rpc-api/Cargo.toml index 062d25408a14fe25e0c6d8e20c78fc742aac5664..1b7af6a4a52fe650dd325cc38d92daddd9c7a9fa 100644 --- a/substrate/client/rpc-api/Cargo.toml +++ b/substrate/client/rpc-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-rpc-api" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -18,9 +18,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" -thiserror = "1.0" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } sc-chain-spec = { path = "../chain-spec" } sc-mixnet = { path = "../mixnet" } sc-transaction-pool-api = { path = "../transaction-pool/api" } @@ -28,4 +28,4 @@ sp-core = { path = "../../primitives/core" } sp-rpc = { path = "../../primitives/rpc" } sp-runtime = { path = "../../primitives/runtime" } sp-version = { path = "../../primitives/version" } -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } diff --git a/substrate/client/rpc-api/src/author/error.rs b/substrate/client/rpc-api/src/author/error.rs index 648dbb295d8d041d154e154e40cc8a1c53f699df..0ccd82fc0b64c2350e76e9cb7936944af717bff3 100644 --- a/substrate/client/rpc-api/src/author/error.rs +++ b/substrate/client/rpc-api/src/author/error.rs @@ -18,10 +18,7 @@ //! Authoring RPC module errors. -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; use sp_runtime::transaction_validity::InvalidTransaction; /// Author RPC Result type. @@ -86,98 +83,104 @@ const POOL_NO_TAGS: i32 = POOL_INVALID_TX + 9; const POOL_INVALID_BLOCK_ID: i32 = POOL_INVALID_TX + 10; /// The pool is not accepting future transactions. const POOL_FUTURE_TX: i32 = POOL_INVALID_TX + 11; +/// Other error. +const OTHER_ERR: i32 = BASE_ERROR + 40; -impl From for JsonRpseeError { - fn from(e: Error) -> Self { +impl From for ErrorObjectOwned { + fn from(e: Error) -> ErrorObjectOwned { use sc_transaction_pool_api::error::Error as PoolError; match e { - Error::BadFormat(e) => CallError::Custom(ErrorObject::owned( + Error::BadFormat(e) => ErrorObject::owned( BAD_FORMAT, format!("Extrinsic has invalid format: {}", e), None::<()>, - )), - Error::Verification(e) => CallError::Custom(ErrorObject::owned( + ), + Error::Verification(e) => ErrorObject::owned( VERIFICATION_ERROR, format!("Verification Error: {}", e), Some(format!("{:?}", e)), - )), + ), Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_INVALID_TX, "Invalid Transaction", Some(format!("Custom error: {}", e)), - )) + ) }, Error::Pool(PoolError::InvalidTransaction(e)) => { let msg: &str = e.into(); - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_INVALID_TX, "Invalid Transaction", Some(msg), - )) + ) }, Error::Pool(PoolError::UnknownTransaction(e)) => { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_UNKNOWN_VALIDITY, "Unknown Transaction Validity", Some(format!("{:?}", e)), - )) + ) }, Error::Pool(PoolError::TemporarilyBanned) => - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_TEMPORARILY_BANNED, "Transaction is temporarily banned", None::<()>, - )), + ), Error::Pool(PoolError::AlreadyImported(hash)) => - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_ALREADY_IMPORTED, "Transaction Already Imported", Some(format!("{:?}", hash)), - )), - Error::Pool(PoolError::TooLowPriority { old, new }) => CallError::Custom(ErrorObject::owned( + ), + Error::Pool(PoolError::TooLowPriority { old, new }) => ErrorObject::owned( POOL_TOO_LOW_PRIORITY, format!("Priority is too low: ({} vs {})", old, new), Some("The transaction has too low priority to replace another transaction already in the pool.") - )), + ), Error::Pool(PoolError::CycleDetected) => - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_CYCLE_DETECTED, "Cycle Detected", None::<()> - )), - Error::Pool(PoolError::ImmediatelyDropped) => CallError::Custom(ErrorObject::owned( + ), + Error::Pool(PoolError::ImmediatelyDropped) => ErrorObject::owned( POOL_IMMEDIATELY_DROPPED, "Immediately Dropped", Some("The transaction couldn't enter the pool because of the limit"), - )), - Error::Pool(PoolError::Unactionable) => CallError::Custom(ErrorObject::owned( + ), + Error::Pool(PoolError::Unactionable) => ErrorObject::owned( POOL_UNACTIONABLE, "Unactionable", Some("The transaction is unactionable since it is not propagable and \ the local node does not author blocks") - )), - Error::Pool(PoolError::NoTagsProvided) => CallError::Custom(ErrorObject::owned( + ), + Error::Pool(PoolError::NoTagsProvided) => ErrorObject::owned( POOL_NO_TAGS, "No tags provided", Some("Transaction does not provide any tags, so the pool can't identify it") - )), + ), Error::Pool(PoolError::InvalidBlockId(_)) => - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_INVALID_BLOCK_ID, "The provided block ID is not valid", None::<()> - )), + ), Error::Pool(PoolError::RejectedFutureTransaction) => { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( POOL_FUTURE_TX, "The pool is not accepting future transactions", None::<()>, - )) + ) }, Error::UnsafeRpcCalled(e) => e.into(), - e => CallError::Failed(e.into()), - }.into() + other => ErrorObject::owned( + OTHER_ERR, + other.to_string(), + None::<()>, + ) + } } } diff --git a/substrate/client/rpc-api/src/author/mod.rs b/substrate/client/rpc-api/src/author/mod.rs index 55881e62152e3dc573fadea006f0b845415cf3e3..cfc56f4130aba9527e9bb2b748f38314d6d35644 100644 --- a/substrate/client/rpc-api/src/author/mod.rs +++ b/substrate/client/rpc-api/src/author/mod.rs @@ -18,27 +18,28 @@ //! Substrate block-author/full-node API. -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use sc_transaction_pool_api::TransactionStatus; -use sp_core::Bytes; - pub mod error; pub mod hash; +use error::Error; +use jsonrpsee::proc_macros::rpc; +use sc_transaction_pool_api::TransactionStatus; +use sp_core::Bytes; + /// Substrate authoring RPC API #[rpc(client, server)] pub trait AuthorApi { /// Submit hex-encoded extrinsic for inclusion in block. #[method(name = "author_submitExtrinsic")] - async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult; + async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result; /// Insert a key into the keystore. #[method(name = "author_insertKey")] - fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()>; + 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")] - fn rotate_keys(&self) -> RpcResult; + fn rotate_keys(&self) -> Result; /// Checks if the keystore has private keys for the given session public keys. /// @@ -46,24 +47,24 @@ pub trait AuthorApi { /// /// Returns `true` iff all private keys could be found. #[method(name = "author_hasSessionKeys")] - fn has_session_keys(&self, session_keys: Bytes) -> RpcResult; + 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")] - fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult; + fn has_key(&self, public_key: Bytes, key_type: String) -> Result; /// Returns all pending extrinsics, potentially grouped by sender. #[method(name = "author_pendingExtrinsics")] - fn pending_extrinsics(&self) -> RpcResult>; + fn pending_extrinsics(&self) -> Result, Error>; /// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting. #[method(name = "author_removeExtrinsic")] fn remove_extrinsic( &self, bytes_or_hash: Vec>, - ) -> RpcResult>; + ) -> Result, Error>; /// Submit an extrinsic to watch. /// diff --git a/substrate/client/rpc-api/src/chain/error.rs b/substrate/client/rpc-api/src/chain/error.rs index 652192942588856209a37d18cfb6f1c3e22203b6..ff3593557bdbcd7a19ab817d8e9d6f6b009da32c 100644 --- a/substrate/client/rpc-api/src/chain/error.rs +++ b/substrate/client/rpc-api/src/chain/error.rs @@ -18,10 +18,7 @@ //! Error helpers for Chain RPC module. -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::{error::ErrorObject, ErrorObjectOwned}; /// Chain RPC Result type. pub type Result = std::result::Result; @@ -39,12 +36,11 @@ pub enum Error { /// Base error code for all chain errors. const BASE_ERROR: i32 = crate::error::base::CHAIN; -impl From for JsonRpseeError { - fn from(e: Error) -> Self { +impl From for ErrorObjectOwned { + fn from(e: Error) -> ErrorObjectOwned { match e { - Error::Other(message) => - CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, message, None::<()>)).into(), - e => e.into(), + Error::Other(message) => ErrorObject::owned(BASE_ERROR + 1, message, None::<()>), + e => ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>), } } } diff --git a/substrate/client/rpc-api/src/chain/mod.rs b/substrate/client/rpc-api/src/chain/mod.rs index f215cd978f03a0c7d2b95b0c0043e35f63da43cc..e53c2bc5510ab820325ec1362c3d9233705c5588 100644 --- a/substrate/client/rpc-api/src/chain/mod.rs +++ b/substrate/client/rpc-api/src/chain/mod.rs @@ -18,20 +18,21 @@ //! Substrate blockchain API. -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use sp_rpc::{list::ListOrValue, number::NumberOrHex}; - pub mod error; +use error::Error; +use jsonrpsee::proc_macros::rpc; +use sp_rpc::{list::ListOrValue, number::NumberOrHex}; + #[rpc(client, server)] pub trait ChainApi { /// Get header. #[method(name = "chain_getHeader", blocking)] - fn header(&self, hash: Option) -> RpcResult>; + fn header(&self, hash: Option) -> Result, Error>; /// Get header and body of a block. #[method(name = "chain_getBlock", blocking)] - fn block(&self, hash: Option) -> RpcResult>; + fn block(&self, hash: Option) -> Result, Error>; /// Get hash of the n-th block in the canon chain. /// @@ -40,11 +41,11 @@ pub trait ChainApi { fn block_hash( &self, hash: Option>, - ) -> RpcResult>>; + ) -> Result>, Error>; /// Get hash of the last finalized block in the canon chain. #[method(name = "chain_getFinalizedHead", aliases = ["chain_getFinalisedHead"], blocking)] - fn finalized_head(&self) -> RpcResult; + fn finalized_head(&self) -> Result; /// All head subscription. #[subscription( diff --git a/substrate/client/rpc-api/src/child_state/mod.rs b/substrate/client/rpc-api/src/child_state/mod.rs index a184677a721b5f0f98585b0f97317872c1e4b670..70f304ea8e02de9c80f11357f3dc741d7b1f6f9f 100644 --- a/substrate/client/rpc-api/src/child_state/mod.rs +++ b/substrate/client/rpc-api/src/child_state/mod.rs @@ -17,8 +17,8 @@ // along with this program. If not, see . //! Substrate child state API -use crate::state::ReadProof; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use crate::state::{Error, ReadProof}; +use jsonrpsee::proc_macros::rpc; use sp_core::storage::{PrefixedStorageKey, StorageData, StorageKey}; /// Substrate child state API @@ -35,7 +35,7 @@ pub trait ChildStateApi { child_storage_key: PrefixedStorageKey, prefix: StorageKey, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns the keys with prefix from a child storage with pagination support. /// Up to `count` keys will be returned. @@ -48,7 +48,7 @@ pub trait ChildStateApi { count: u32, start_key: Option, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns a child storage entry at a specific block's state. #[method(name = "childstate_getStorage", blocking)] @@ -57,7 +57,7 @@ pub trait ChildStateApi { child_storage_key: PrefixedStorageKey, key: StorageKey, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns child storage entries for multiple keys at a specific block's state. #[method(name = "childstate_getStorageEntries", blocking)] @@ -66,7 +66,7 @@ pub trait ChildStateApi { child_storage_key: PrefixedStorageKey, keys: Vec, hash: Option, - ) -> RpcResult>>; + ) -> Result>, Error>; /// Returns the hash of a child storage entry at a block's state. #[method(name = "childstate_getStorageHash", blocking)] @@ -75,7 +75,7 @@ pub trait ChildStateApi { child_storage_key: PrefixedStorageKey, key: StorageKey, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns the size of a child storage entry at a block's state. #[method(name = "childstate_getStorageSize", blocking)] @@ -84,7 +84,7 @@ pub trait ChildStateApi { child_storage_key: PrefixedStorageKey, key: StorageKey, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns proof of storage for child key entries at a specific block's state. #[method(name = "state_getChildReadProof", blocking)] @@ -93,5 +93,5 @@ pub trait ChildStateApi { child_storage_key: PrefixedStorageKey, keys: Vec, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; } diff --git a/substrate/client/rpc-api/src/dev/error.rs b/substrate/client/rpc-api/src/dev/error.rs index 8e4ddb55e35d77fee79cdb9411ab3b0647b12661..f70e368b8738955bc0201fb03a42c96970184b29 100644 --- a/substrate/client/rpc-api/src/dev/error.rs +++ b/substrate/client/rpc-api/src/dev/error.rs @@ -18,10 +18,10 @@ //! Error helpers for Dev RPC module. -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; + +/// Dev RPC Result type. +pub type Result = std::result::Result; /// Dev RPC errors. #[derive(Debug, thiserror::Error)] @@ -46,21 +46,16 @@ pub enum Error { /// Base error code for all dev errors. const BASE_ERROR: i32 = crate::error::base::DEV; -impl From for JsonRpseeError { +impl From for ErrorObjectOwned { fn from(e: Error) -> Self { let msg = e.to_string(); match e { - Error::BlockQueryError(_) => - CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, msg, None::<()>)), - Error::BlockExecutionFailed => - CallError::Custom(ErrorObject::owned(BASE_ERROR + 3, msg, None::<()>)), - Error::WitnessCompactionFailed => - CallError::Custom(ErrorObject::owned(BASE_ERROR + 4, msg, None::<()>)), - Error::ProofExtractionFailed => - CallError::Custom(ErrorObject::owned(BASE_ERROR + 5, msg, None::<()>)), + Error::BlockQueryError(_) => ErrorObject::owned(BASE_ERROR + 1, msg, None::<()>), + Error::BlockExecutionFailed => ErrorObject::owned(BASE_ERROR + 3, msg, None::<()>), + Error::WitnessCompactionFailed => ErrorObject::owned(BASE_ERROR + 4, msg, None::<()>), + Error::ProofExtractionFailed => ErrorObject::owned(BASE_ERROR + 5, msg, None::<()>), Error::UnsafeRpcCalled(e) => e.into(), } - .into() } } diff --git a/substrate/client/rpc-api/src/dev/mod.rs b/substrate/client/rpc-api/src/dev/mod.rs index bc7216199dd747bb373fc691cdef4b9a32d08456..5bee6df73ba93e34d63f4533b57a2be64f056212 100644 --- a/substrate/client/rpc-api/src/dev/mod.rs +++ b/substrate/client/rpc-api/src/dev/mod.rs @@ -23,7 +23,8 @@ pub mod error; use codec::{Decode, Encode}; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use error::Error; +use jsonrpsee::proc_macros::rpc; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; @@ -59,5 +60,5 @@ pub trait DevApi { /// at the queried node. If either the specified block or the parent is pruned, /// this function will return `None`. #[method(name = "dev_getBlockStats")] - fn block_stats(&self, block_hash: Hash) -> RpcResult>; + 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 32120d37902dd6718c2d95b5f36ef553dfc08ba1..451ebdf7fc00404bbd58abffef5a298cd9d6f310 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; +pub use policy::{DenyUnsafe, UnsafeRpcError}; pub mod author; pub mod chain; diff --git a/substrate/client/rpc-api/src/mixnet/error.rs b/substrate/client/rpc-api/src/mixnet/error.rs index 0dde5f32e6139acae2c867dbf24f53002dfffe56..22352256f91e056f876f15677d548ccb96cf358c 100644 --- a/substrate/client/rpc-api/src/mixnet/error.rs +++ b/substrate/client/rpc-api/src/mixnet/error.rs @@ -18,7 +18,7 @@ //! Mixnet RPC module errors. -use jsonrpsee::types::error::{CallError, ErrorObject}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; use sc_mixnet::{PostErr, RemoteErr, TopologyErr}; /// Mixnet RPC error type. @@ -27,7 +27,7 @@ pub struct Error(pub sc_mixnet::Error); /// Base code for all mixnet errors. const BASE_ERROR: i32 = crate::error::base::MIXNET; -impl From for jsonrpsee::core::Error { +impl From for ErrorObjectOwned { fn from(err: Error) -> Self { let code = match err.0 { sc_mixnet::Error::ServiceUnavailable => BASE_ERROR + 1, @@ -43,6 +43,6 @@ impl From for jsonrpsee::core::Error { sc_mixnet::Error::Remote(RemoteErr::Other(_)) => BASE_ERROR + 200, sc_mixnet::Error::Remote(RemoteErr::Decode(_)) => BASE_ERROR + 201, }; - CallError::Custom(ErrorObject::owned(code, err.0.to_string(), None::<()>)).into() + ErrorObject::owned(code, err.0.to_string(), None::<()>) } } diff --git a/substrate/client/rpc-api/src/mixnet/mod.rs b/substrate/client/rpc-api/src/mixnet/mod.rs index bc478cf3bf334db618b240193ae7c58d732f0cc9..8bd3362ca613bcb0ccb36be01f2c2602c6e25fd8 100644 --- a/substrate/client/rpc-api/src/mixnet/mod.rs +++ b/substrate/client/rpc-api/src/mixnet/mod.rs @@ -20,12 +20,13 @@ pub mod error; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use error::Error; +use jsonrpsee::proc_macros::rpc; use sp_core::Bytes; #[rpc(client, server)] pub trait MixnetApi { /// Submit encoded extrinsic over the mixnet for inclusion in block. #[method(name = "mixnet_submitExtrinsic")] - async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<()>; + async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<(), Error>; } diff --git a/substrate/client/rpc-api/src/offchain/error.rs b/substrate/client/rpc-api/src/offchain/error.rs index 679e100089734357cb27580e43fecf6917985935..ae5771981ea9602fb0286b3a9e715a24b28c5ae2 100644 --- a/substrate/client/rpc-api/src/offchain/error.rs +++ b/substrate/client/rpc-api/src/offchain/error.rs @@ -18,10 +18,7 @@ //! Offchain RPC errors. -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; /// Offchain RPC Result type. pub type Result = std::result::Result; @@ -40,15 +37,14 @@ pub enum Error { /// Base error code for all offchain errors. const BASE_ERROR: i32 = crate::error::base::OFFCHAIN; -impl From for JsonRpseeError { +impl From for ErrorObjectOwned { fn from(e: Error) -> Self { match e { - Error::UnavailableStorageKind => CallError::Custom(ErrorObject::owned( + Error::UnavailableStorageKind => ErrorObject::owned( BASE_ERROR + 1, "This storage kind is not available yet", None::<()>, - )) - .into(), + ), Error::UnsafeRpcCalled(e) => e.into(), } } diff --git a/substrate/client/rpc-api/src/offchain/mod.rs b/substrate/client/rpc-api/src/offchain/mod.rs index cd42d6db350810a302af550fa7711eeef15e2995..469e22d2b3faedf299c16e4809d8ddb046d8981d 100644 --- a/substrate/client/rpc-api/src/offchain/mod.rs +++ b/substrate/client/rpc-api/src/offchain/mod.rs @@ -18,19 +18,20 @@ //! Substrate offchain API. -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use sp_core::{offchain::StorageKind, Bytes}; - pub mod error; +use error::Error; +use jsonrpsee::proc_macros::rpc; +use sp_core::{offchain::StorageKind, Bytes}; + /// Substrate offchain RPC API #[rpc(client, server)] pub trait OffchainApi { /// Set offchain local storage under given key and prefix. #[method(name = "offchain_localStorageSet")] - fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> RpcResult<()>; + 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")] - fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> RpcResult>; + 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 799898fb7cf5371c630c50e35b635ed2bd43da7c..c0847de89d2c613f904e3ff6e6db39cdc2c878fd 100644 --- a/substrate/client/rpc-api/src/policy.rs +++ b/substrate/client/rpc-api/src/policy.rs @@ -21,13 +21,7 @@ //! Contains a `DenyUnsafe` type that can be used to deny potentially unsafe //! RPC when accessed externally. -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::{ - error::{CallError, ErrorCode}, - ErrorObject, - }, -}; +use jsonrpsee::types::{error::ErrorCode, ErrorObject, ErrorObjectOwned}; /// Signifies whether a potentially unsafe RPC should be denied. #[derive(Clone, Copy, Debug)] @@ -61,18 +55,8 @@ impl std::fmt::Display for UnsafeRpcError { impl std::error::Error for UnsafeRpcError {} -impl From for CallError { - fn from(e: UnsafeRpcError) -> CallError { - CallError::Custom(ErrorObject::owned( - ErrorCode::MethodNotFound.code(), - e.to_string(), - None::<()>, - )) - } -} - -impl From for JsonRpseeError { - fn from(e: UnsafeRpcError) -> JsonRpseeError { - JsonRpseeError::Call(e.into()) +impl From for ErrorObjectOwned { + fn from(e: UnsafeRpcError) -> ErrorObjectOwned { + ErrorObject::owned(ErrorCode::MethodNotFound.code(), e.to_string(), None::<()>) } } diff --git a/substrate/client/rpc-api/src/state/error.rs b/substrate/client/rpc-api/src/state/error.rs index 9857784e3545cdd3bf944293542c2a31a373cb60..f2396c63815c54218b401b9cdcf986cdaa3ad384 100644 --- a/substrate/client/rpc-api/src/state/error.rs +++ b/substrate/client/rpc-api/src/state/error.rs @@ -18,10 +18,8 @@ //! State RPC errors. -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; + /// State RPC Result type. pub type Result = std::result::Result; @@ -57,16 +55,14 @@ pub enum Error { /// Base code for all state errors. const BASE_ERROR: i32 = crate::error::base::STATE; -impl From for JsonRpseeError { - fn from(e: Error) -> Self { +impl From for ErrorObjectOwned { + fn from(e: Error) -> ErrorObjectOwned { match e { Error::InvalidBlockRange { .. } => - CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, e.to_string(), None::<()>)) - .into(), + ErrorObject::owned(BASE_ERROR + 1, e.to_string(), None::<()>), Error::InvalidCount { .. } => - CallError::Custom(ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>)) - .into(), - e => Self::to_call_error(e), + ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>), + e => ErrorObject::owned(BASE_ERROR + 3, e.to_string(), None::<()>), } } } diff --git a/substrate/client/rpc-api/src/state/helpers.rs b/substrate/client/rpc-api/src/state/helpers.rs index de20ee6f1bdfc4c66239854ef9fda051ff87a95b..58b79ab64ea38dc4824ab76dce98412758dbaf1d 100644 --- a/substrate/client/rpc-api/src/state/helpers.rs +++ b/substrate/client/rpc-api/src/state/helpers.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; use sp_core::Bytes; /// ReadProof struct returned by the RPC -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ReadProof { /// Block hash used to generate the proof diff --git a/substrate/client/rpc-api/src/state/mod.rs b/substrate/client/rpc-api/src/state/mod.rs index 4acc64def2bd0804b4a08a5823b9e48aef5ef41e..e38e383c4c1523b42beef5f50e7852cc81290fb5 100644 --- a/substrate/client/rpc-api/src/state/mod.rs +++ b/substrate/client/rpc-api/src/state/mod.rs @@ -18,7 +18,7 @@ //! Substrate state API. -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use jsonrpsee::proc_macros::rpc; use sp_core::{ storage::{StorageChangeSet, StorageData, StorageKey}, Bytes, @@ -29,18 +29,23 @@ pub mod error; pub mod helpers; pub use self::helpers::ReadProof; +pub use error::Error; /// Substrate state API #[rpc(client, server)] pub trait StateApi { /// Call a method from the runtime API at a block's state. #[method(name = "state_call", aliases = ["state_callAt"], blocking)] - fn call(&self, name: String, bytes: Bytes, hash: Option) -> RpcResult; + fn call(&self, name: String, bytes: Bytes, hash: Option) -> Result; /// Returns the keys with prefix, leave empty to get all the keys. #[method(name = "state_getKeys", blocking)] #[deprecated(since = "2.0.0", note = "Please use `getKeysPaged` with proper paging support")] - fn storage_keys(&self, prefix: StorageKey, hash: Option) -> RpcResult>; + fn storage_keys( + &self, + prefix: StorageKey, + hash: Option, + ) -> Result, Error>; /// Returns the keys with prefix, leave empty to get all the keys #[method(name = "state_getPairs", blocking)] @@ -48,7 +53,7 @@ pub trait StateApi { &self, prefix: StorageKey, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns the keys with prefix with pagination support. /// Up to `count` keys will be returned. @@ -60,27 +65,28 @@ pub trait StateApi { count: u32, start_key: Option, hash: Option, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns a storage entry at a specific block's state. #[method(name = "state_getStorage", aliases = ["state_getStorageAt"], blocking)] - fn storage(&self, key: StorageKey, hash: Option) -> RpcResult>; + fn storage(&self, key: StorageKey, hash: Option) -> Result, Error>; /// Returns the hash of a storage entry at a block's state. #[method(name = "state_getStorageHash", aliases = ["state_getStorageHashAt"], blocking)] - fn storage_hash(&self, key: StorageKey, hash: Option) -> RpcResult>; + 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"])] - async fn storage_size(&self, key: StorageKey, hash: Option) -> RpcResult>; + async fn storage_size(&self, key: StorageKey, hash: Option) + -> Result, Error>; /// Returns the runtime metadata as an opaque blob. #[method(name = "state_getMetadata", blocking)] - fn metadata(&self, hash: Option) -> RpcResult; + fn metadata(&self, hash: Option) -> Result; /// Get the runtime version. #[method(name = "state_getRuntimeVersion", aliases = ["chain_getRuntimeVersion"], blocking)] - fn runtime_version(&self, hash: Option) -> RpcResult; + fn runtime_version(&self, hash: Option) -> Result; /// Query historical storage entries (by key) starting from a block given as the second /// parameter. @@ -95,7 +101,7 @@ pub trait StateApi { keys: Vec, block: Hash, hash: Option, - ) -> RpcResult>>; + ) -> Result>, Error>; /// Query storage entries (by key) at a block hash given as the second parameter. /// NOTE: Each StorageChangeSet in the result corresponds to exactly one element -- @@ -105,11 +111,15 @@ pub trait StateApi { &self, keys: Vec, at: Option, - ) -> RpcResult>>; + ) -> Result>, Error>; /// Returns proof of storage entries at a specific block's state. #[method(name = "state_getReadProof", blocking)] - fn read_proof(&self, keys: Vec, hash: Option) -> RpcResult>; + fn read_proof( + &self, + keys: Vec, + hash: Option, + ) -> Result, Error>; /// New runtime version subscription #[subscription( @@ -288,5 +298,5 @@ pub trait StateApi { targets: Option, storage_keys: Option, methods: Option, - ) -> RpcResult; + ) -> Result; } diff --git a/substrate/client/rpc-api/src/statement/error.rs b/substrate/client/rpc-api/src/statement/error.rs index 8438cc3ec9e940a9a023bd6510f337d9cc08f4f3..f8041864facad72cdc185ea0b3807efa8c234cd3 100644 --- a/substrate/client/rpc-api/src/statement/error.rs +++ b/substrate/client/rpc-api/src/statement/error.rs @@ -18,10 +18,7 @@ //! Statement RPC errors. -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned}; /// Statement RPC Result type. pub type Result = std::result::Result; @@ -40,15 +37,14 @@ pub enum Error { /// Base error code for all statement errors. const BASE_ERROR: i32 = crate::error::base::STATEMENT; -impl From for JsonRpseeError { +impl From for ErrorObjectOwned { fn from(e: Error) -> Self { match e { - Error::StatementStore(message) => CallError::Custom(ErrorObject::owned( + Error::StatementStore(message) => ErrorObject::owned( BASE_ERROR + 1, format!("Statement store error: {message}"), None::<()>, - )) - .into(), + ), Error::UnsafeRpcCalled(e) => e.into(), } } diff --git a/substrate/client/rpc-api/src/system/error.rs b/substrate/client/rpc-api/src/system/error.rs index 713ade9210d328265c49f8d5d924f92b69e1d2a4..1e826a75ae622f11fe9d5a66490757c6b5b79426 100644 --- a/substrate/client/rpc-api/src/system/error.rs +++ b/substrate/client/rpc-api/src/system/error.rs @@ -19,9 +19,9 @@ //! System RPC module errors. use crate::system::helpers::Health; -use jsonrpsee::{ - core::Error as JsonRpseeError, - types::error::{CallError, ErrorObject}, +use jsonrpsee::types::{ + error::{ErrorCode, ErrorObject}, + ErrorObjectOwned, }; /// System RPC Result type. @@ -36,6 +36,12 @@ pub enum Error { /// Peer argument is malformatted. #[error("{0}")] MalformattedPeerArg(String), + /// Call to an unsafe RPC was denied. + #[error(transparent)] + UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError), + /// Internal error. + #[error("{0}")] + Internal(String), } // Base code for all system errors. @@ -45,17 +51,16 @@ const NOT_HEALTHY_ERROR: i32 = BASE_ERROR + 1; // Peer argument is malformatted. const MALFORMATTED_PEER_ARG_ERROR: i32 = BASE_ERROR + 2; -impl From for JsonRpseeError { - fn from(e: Error) -> Self { +impl From for ErrorObjectOwned { + fn from(e: Error) -> ErrorObjectOwned { match e { Error::NotHealthy(ref h) => - CallError::Custom(ErrorObject::owned(NOT_HEALTHY_ERROR, e.to_string(), Some(h))), - Error::MalformattedPeerArg(e) => CallError::Custom(ErrorObject::owned( - MALFORMATTED_PEER_ARG_ERROR + 2, - e, - None::<()>, - )), + ErrorObject::owned(NOT_HEALTHY_ERROR, e.to_string(), Some(h)), + Error::MalformattedPeerArg(e) => + ErrorObject::owned(MALFORMATTED_PEER_ARG_ERROR, e, None::<()>), + Error::UnsafeRpcCalled(e) => e.into(), + Error::Internal(e) => + ErrorObjectOwned::owned(ErrorCode::InternalError.code(), e, None::<()>), } - .into() } } diff --git a/substrate/client/rpc-api/src/system/helpers.rs b/substrate/client/rpc-api/src/system/helpers.rs index ad56dc3850093eda9a44b98ec498b97612474964..ed642e0679eb330f9843424724478eb5e9ceb440 100644 --- a/substrate/client/rpc-api/src/system/helpers.rs +++ b/substrate/client/rpc-api/src/system/helpers.rs @@ -38,7 +38,7 @@ pub struct SystemInfo { } /// Health struct returned by the RPC -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Health { /// Number of connected peers @@ -58,7 +58,7 @@ impl fmt::Display for Health { } /// Network Peer information -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PeerInfo { /// Peer ID @@ -72,7 +72,7 @@ pub struct PeerInfo { } /// The role the node is running as -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum NodeRole { /// The node is a full node Full, @@ -81,7 +81,7 @@ pub enum NodeRole { } /// The state of the syncing of the node. -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SyncState { /// Height of the block at which syncing started. diff --git a/substrate/client/rpc-api/src/system/mod.rs b/substrate/client/rpc-api/src/system/mod.rs index bf2e92bc27a015a7b569fbbb2fe4ee126e59e5a3..c38fa8f3d817606ce699b31b4f16c2a6e0c7e775 100644 --- a/substrate/client/rpc-api/src/system/mod.rs +++ b/substrate/client/rpc-api/src/system/mod.rs @@ -18,38 +18,36 @@ //! Substrate system API. -use jsonrpsee::{ - core::{JsonValue, RpcResult}, - proc_macros::rpc, -}; - -pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo}; - pub mod error; pub mod helpers; +use jsonrpsee::{core::JsonValue, proc_macros::rpc}; + +pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo}; +pub use error::Error; + /// Substrate system RPC API #[rpc(client, server)] pub trait SystemApi { /// Get the node's implementation name. Plain old string. #[method(name = "system_name")] - fn system_name(&self) -> RpcResult; + fn system_name(&self) -> Result; /// Get the node implementation's version. Should be a semver string. #[method(name = "system_version")] - fn system_version(&self) -> RpcResult; + fn system_version(&self) -> Result; /// Get the chain's name. Given as a string identifier. #[method(name = "system_chain")] - fn system_chain(&self) -> RpcResult; + fn system_chain(&self) -> Result; /// Get the chain's type. #[method(name = "system_chainType")] - fn system_type(&self) -> RpcResult; + fn system_type(&self) -> Result; /// Get a custom set of properties as a JSON object, defined in the chain spec. #[method(name = "system_properties")] - fn system_properties(&self) -> RpcResult; + fn system_properties(&self) -> Result; /// Return health status of the node. /// @@ -57,22 +55,22 @@ pub trait SystemApi { /// - connected to some peers (unless running in dev mode) /// - not performing a major sync #[method(name = "system_health")] - async fn system_health(&self) -> RpcResult; + async fn system_health(&self) -> Result; /// Returns the base58-encoded PeerId of the node. #[method(name = "system_localPeerId")] - async fn system_local_peer_id(&self) -> RpcResult; + async fn system_local_peer_id(&self) -> Result; /// Returns the multi-addresses that the local node is listening on /// /// The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to /// be passed to `addReservedPeer` or as a bootnode address for example. #[method(name = "system_localListenAddresses")] - async fn system_local_listen_addresses(&self) -> RpcResult>; + async fn system_local_listen_addresses(&self) -> Result, Error>; /// Returns currently connected peers #[method(name = "system_peers")] - async fn system_peers(&self) -> RpcResult>>; + async fn system_peers(&self) -> Result>, Error>; /// Returns current state of the network. /// @@ -81,7 +79,7 @@ pub trait SystemApi { // 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")] - async fn system_network_state(&self) -> RpcResult; + async fn system_network_state(&self) -> Result; /// Adds a reserved peer. Returns the empty string or an error. The string /// parameter should encode a `p2p` multiaddr. @@ -89,25 +87,25 @@ 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")] - async fn system_add_reserved_peer(&self, peer: String) -> RpcResult<()>; + 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")] - async fn system_remove_reserved_peer(&self, peer_id: String) -> RpcResult<()>; + async fn system_remove_reserved_peer(&self, peer_id: String) -> Result<(), Error>; /// Returns the list of reserved peers #[method(name = "system_reservedPeers")] - async fn system_reserved_peers(&self) -> RpcResult>; + async fn system_reserved_peers(&self) -> Result, Error>; /// Returns the roles the node is running as. #[method(name = "system_nodeRoles")] - async fn system_node_roles(&self) -> RpcResult>; + async fn system_node_roles(&self) -> Result, Error>; /// Returns the state of the syncing of the node: starting block, current best block, highest /// known block. #[method(name = "system_syncState")] - async fn system_sync_state(&self) -> RpcResult>; + async fn system_sync_state(&self) -> Result, Error>; /// Adds the supplied directives to the current log filter /// @@ -115,9 +113,9 @@ pub trait SystemApi { /// /// `sync=debug,state=trace` #[method(name = "system_addLogFilter")] - fn system_add_log_filter(&self, directives: String) -> RpcResult<()>; + fn system_add_log_filter(&self, directives: String) -> Result<(), Error>; /// Resets the log filter to Substrate defaults #[method(name = "system_resetLogFilter")] - fn system_reset_log_filter(&self) -> RpcResult<()>; + 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 60d999863cabe6db3fb61ae527716ba62aa0d302..3adc81c57d594a355212a88f83682a0441bc7acc 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-rpc-server" -version = "4.0.0-dev" +version = "11.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -16,11 +16,14 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["server"] } -log = "0.4.17" -serde_json = "1.0.111" +jsonrpsee = { version = "0.22", features = ["server"] } +log = { workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["parking_lot"] } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } tower-http = { version = "0.4.0", features = ["cors"] } -tower = "0.4.13" +tower = { version = "0.4.13", features = ["util"] } http = "0.2.8" +hyper = "0.14.27" +futures = "0.3.29" +governor = "0.6.0" diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index dc625c3d6c4cf44d37b865d9e022047066b34e58..ad4b444c7ff425ea84ba25e756d61854c3a4569e 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -22,27 +22,39 @@ pub mod middleware; +use std::{ + convert::Infallible, error::Error as StdError, net::SocketAddr, num::NonZeroU32, time::Duration, +}; + use http::header::HeaderValue; +use hyper::{ + server::conn::AddrStream, + service::{make_service_fn, service_fn}, +}; use jsonrpsee::{ server::{ - middleware::proxy_get_request::ProxyGetRequestLayer, AllowHosts, ServerBuilder, - ServerHandle, + middleware::http::{HostFilterLayer, ProxyGetRequestLayer}, + stop_channel, ws, PingConfig, StopHandle, TowerServiceBuilder, }, - RpcModule, + Methods, RpcModule, }; -use std::{error::Error as StdError, net::SocketAddr}; +use tokio::net::TcpListener; +use tower::Service; use tower_http::cors::{AllowOrigin, CorsLayer}; -pub use crate::middleware::RpcMetrics; -pub use jsonrpsee::core::{ - id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, - traits::IdProvider, +pub use jsonrpsee::{ + core::{ + id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, + traits::IdProvider, + }, + server::{middleware::rpc::RpcServiceBuilder, BatchRequestConfig}, }; +pub use middleware::{Metrics, MiddlewareLayer, RpcMetrics}; const MEGABYTE: u32 = 1024 * 1024; /// Type alias for the JSON-RPC server. -pub type Server = ServerHandle; +pub type Server = jsonrpsee::server::ServerHandle; /// RPC server configuration. #[derive(Debug)] @@ -61,47 +73,77 @@ pub struct Config<'a, M: Send + Sync + 'static> { pub max_payload_out_mb: u32, /// 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>, /// 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, +} + +#[derive(Debug, Clone)] +struct PerConnection { + methods: Methods, + stop_handle: StopHandle, + metrics: Option, + tokio_handle: tokio::runtime::Handle, + service_builder: TowerServiceBuilder, } /// Start RPC server listening on given address. -pub async fn start_server( +pub async fn start_server( config: Config<'_, M>, -) -> Result> { +) -> 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, } = config; - let host_filter = hosts_filtering(cors.is_some(), &addrs); + let std_listener = TcpListener::bind(addrs.as_slice()).await?.into_std()?; + let local_addr = std_listener.local_addr().ok(); + let host_filter = hosts_filtering(cors.is_some(), local_addr); - let middleware = tower::ServiceBuilder::new() + let http_middleware = tower::ServiceBuilder::new() + .option_layer(host_filter) // Proxy `GET /health` requests to internal `system_health` method. .layer(ProxyGetRequestLayer::new("/health", "system_health")?) .layer(try_into_cors(cors)?); - let mut builder = ServerBuilder::new() + 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) - .ping_interval(std::time::Duration::from_secs(30)) - .set_host_filtering(host_filter) - .set_middleware(middleware) - .custom_tokio_runtime(tokio_handle); + .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); @@ -109,37 +151,94 @@ pub async fn start_server( builder = builder.set_id_provider(RandomStringIdProvider::new(16)); }; - let rpc_api = build_rpc_api(rpc_api); - let (handle, addr) = if let Some(metrics) = metrics { - let server = builder.set_logger(metrics).build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) - } else { - let server = builder.build(&addrs[..]).await?; - let addr = server.local_addr(); - (server.start(rpc_api)?, addr) + 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, + stop_handle: stop_handle.clone(), }; + let make_service = make_service_fn(move |_conn: &AddrStream| { + let cfg = cfg.clone(); + + async move { + let cfg = cfg.clone(); + + Ok::<_, Infallible>(service_fn(move |req| { + let PerConnection { service_builder, metrics, tokio_handle, stop_handle, methods } = + cfg.clone(); + + let is_websocket = ws::is_upgrade_request(&req); + let transport_label = if is_websocket { "ws" } else { "http" }; + + let middleware_layer = match (metrics, rate_limit) { + (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)); + }); + } + + svc.call(req).await + } + })) + } + }); + + let server = hyper::Server::from_tcp(std_listener)?.serve(make_service); + + tokio::spawn(async move { + let graceful = server.with_graceful_shutdown(async move { stop_handle.shutdown().await }); + let _ = graceful.await; + }); + log::info!( "Running JSON-RPC server: addr={}, allowed origins={}", - addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()), + local_addr.map_or_else(|| "unknown".to_string(), |a| a.to_string()), format_cors(cors) ); - Ok(handle) + Ok(server_handle) } -fn hosts_filtering(enabled: bool, addrs: &[SocketAddr]) -> AllowHosts { +fn hosts_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()); + if enabled { - // NOTE The listening addresses are whitelisted by default. - let mut hosts = Vec::with_capacity(addrs.len() * 2); - for addr in addrs { - hosts.push(format!("localhost:{}", addr.port()).into()); - hosts.push(format!("127.0.0.1:{}", addr.port()).into()); - } - AllowHosts::Only(hosts) + // NOTE: The listening addresses are whitelisted by default. + let hosts = + [format!("localhost:{port}"), format!("127.0.0.1:{port}"), format!("[::1]:{port}")]; + Some(HostFilterLayer::new(hosts).expect("Valid hosts; qed")) } else { - AllowHosts::Any + None } } @@ -151,9 +250,9 @@ fn build_rpc_api(mut rpc_api: RpcModule) -> RpcModu rpc_api .register_method("rpc_methods", move |_, _| { - Ok(serde_json::json!({ + serde_json::json!({ "methods": available_methods, - })) + }) }) .expect("infallible all other methods have their own address space; qed"); diff --git a/substrate/client/rpc-servers/src/middleware.rs b/substrate/client/rpc-servers/src/middleware/metrics.rs similarity index 55% rename from substrate/client/rpc-servers/src/middleware.rs rename to substrate/client/rpc-servers/src/middleware/metrics.rs index c3e17c7852f1078a4a301aeed0c90038beb8da49..17849dc0c44846c10ed06b0f86d39783c5d29dc1 100644 --- a/substrate/client/rpc-servers/src/middleware.rs +++ b/substrate/client/rpc-servers/src/middleware/metrics.rs @@ -18,12 +18,13 @@ //! RPC middleware to collect prometheus metrics on RPC calls. -use jsonrpsee::server::logger::{HttpRequest, Logger, MethodKind, Params, TransportProtocol}; +use std::time::Instant; + +use jsonrpsee::{types::Request, MethodResponse}; use prometheus_endpoint::{ register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry, U64, }; -use std::net::SocketAddr; /// Histogram time buckets in microseconds. const HISTOGRAM_BUCKETS: [f64; 11] = [ @@ -44,10 +45,6 @@ const HISTOGRAM_BUCKETS: [f64; 11] = [ /// calls started/completed and their timings. #[derive(Debug, Clone)] pub struct RpcMetrics { - /// Number of RPC requests received since the server started. - requests_started: CounterVec, - /// Number of RPC requests completed since the server started. - requests_finished: CounterVec, /// Histogram over RPC execution times. calls_time: HistogramVec, /// Number of calls started. @@ -58,6 +55,8 @@ pub struct RpcMetrics { ws_sessions_opened: Option>, /// Number of Websocket sessions closed. ws_sessions_closed: Option>, + /// Histogram over RPC websocket sessions. + ws_sessions_time: HistogramVec, } impl RpcMetrics { @@ -65,26 +64,6 @@ impl RpcMetrics { pub fn new(metrics_registry: Option<&Registry>) -> Result, PrometheusError> { if let Some(metrics_registry) = metrics_registry { Ok(Some(Self { - requests_started: register( - CounterVec::new( - Opts::new( - "substrate_rpc_requests_started", - "Number of RPC requests (not calls) received by the server.", - ), - &["protocol"], - )?, - metrics_registry, - )?, - requests_finished: register( - CounterVec::new( - Opts::new( - "substrate_rpc_requests_finished", - "Number of RPC requests (not calls) processed by the server.", - ), - &["protocol"], - )?, - metrics_registry, - )?, calls_time: register( HistogramVec::new( HistogramOpts::new( @@ -92,7 +71,7 @@ impl RpcMetrics { "Total time [μs] of processed RPC calls", ) .buckets(HISTOGRAM_BUCKETS.to_vec()), - &["protocol", "method"], + &["protocol", "method", "is_rate_limited"], )?, metrics_registry, )?, @@ -112,7 +91,7 @@ impl RpcMetrics { "substrate_rpc_calls_finished", "Number of processed RPC calls (unique un-batched requests)", ), - &["protocol", "method", "is_error"], + &["protocol", "method", "is_error", "is_rate_limited"], )?, metrics_registry, )?, @@ -132,93 +111,117 @@ impl RpcMetrics { metrics_registry, )? .into(), + ws_sessions_time: register( + HistogramVec::new( + HistogramOpts::new( + "substrate_rpc_sessions_time", + "Total time [s] for each websocket session", + ) + .buckets(HISTOGRAM_BUCKETS.to_vec()), + &["protocol"], + )?, + metrics_registry, + )?, })) } else { Ok(None) } } -} - -impl Logger for RpcMetrics { - type Instant = std::time::Instant; - fn on_connect( - &self, - _remote_addr: SocketAddr, - _request: &HttpRequest, - transport: TransportProtocol, - ) { - if let TransportProtocol::WebSocket = transport { - self.ws_sessions_opened.as_ref().map(|counter| counter.inc()); - } + pub(crate) fn ws_connect(&self) { + self.ws_sessions_opened.as_ref().map(|counter| counter.inc()); } - fn on_request(&self, transport: TransportProtocol) -> Self::Instant { - let transport_label = transport_label_str(transport); - let now = std::time::Instant::now(); - self.requests_started.with_label_values(&[transport_label]).inc(); - now + pub(crate) fn ws_disconnect(&self, now: Instant) { + let micros = now.elapsed().as_secs(); + + self.ws_sessions_closed.as_ref().map(|counter| counter.inc()); + self.ws_sessions_time.with_label_values(&["ws"]).observe(micros as _); } - fn on_call(&self, name: &str, params: Params, kind: MethodKind, transport: TransportProtocol) { - let transport_label = transport_label_str(transport); + pub(crate) fn on_call(&self, req: &Request, transport_label: &'static str) { log::trace!( target: "rpc_metrics", - "[{}] on_call name={} params={:?} kind={}", - transport_label, - name, - params, - kind, + "[{transport_label}] on_call name={} params={:?}", + req.method_name(), + req.params(), ); - self.calls_started.with_label_values(&[transport_label, name]).inc(); + + self.calls_started + .with_label_values(&[transport_label, req.method_name()]) + .inc(); } - fn on_result( + pub(crate) fn on_response( &self, - name: &str, - success: bool, - started_at: Self::Instant, - transport: TransportProtocol, + req: &Request, + rp: &MethodResponse, + is_rate_limited: bool, + transport_label: &'static str, + now: Instant, ) { - let transport_label = transport_label_str(transport); - let micros = started_at.elapsed().as_micros(); + log::trace!(target: "rpc_metrics", "[{transport_label}] on_response started_at={:?}", now); + log::trace!(target: "rpc_metrics::extra", "[{transport_label}] result={}", rp.as_result()); + + let micros = now.elapsed().as_micros(); log::debug!( target: "rpc_metrics", - "[{}] {} call took {} μs", - transport_label, - name, + "[{transport_label}] {} call took {} μs", + req.method_name(), micros, ); - self.calls_time.with_label_values(&[transport_label, name]).observe(micros as _); - + self.calls_time + .with_label_values(&[ + transport_label, + req.method_name(), + if is_rate_limited { "true" } else { "false" }, + ]) + .observe(micros as _); self.calls_finished .with_label_values(&[ transport_label, - name, + req.method_name(), // the label "is_error", so `success` should be regarded as false // and vice-versa to be registrered correctly. - if success { "false" } else { "true" }, + if rp.is_success() { "false" } else { "true" }, + if is_rate_limited { "true" } else { "false" }, ]) .inc(); } +} + +/// Metrics with transport label. +#[derive(Clone, Debug)] +pub struct Metrics { + pub(crate) inner: RpcMetrics, + pub(crate) transport_label: &'static str, +} - fn on_response(&self, result: &str, started_at: Self::Instant, transport: TransportProtocol) { - let transport_label = transport_label_str(transport); - log::trace!(target: "rpc_metrics", "[{}] on_response started_at={:?}", transport_label, started_at); - log::trace!(target: "rpc_metrics::extra", "[{}] result={:?}", transport_label, result); - self.requests_finished.with_label_values(&[transport_label]).inc(); +impl Metrics { + /// Create a new [`Metrics`]. + pub fn new(metrics: RpcMetrics, transport_label: &'static str) -> Self { + Self { inner: metrics, transport_label } } - fn on_disconnect(&self, _remote_addr: SocketAddr, transport: TransportProtocol) { - if let TransportProtocol::WebSocket = transport { - self.ws_sessions_closed.as_ref().map(|counter| counter.inc()); - } + pub(crate) fn ws_connect(&self) { + self.inner.ws_connect(); } -} -fn transport_label_str(t: TransportProtocol) -> &'static str { - match t { - TransportProtocol::Http => "http", - TransportProtocol::WebSocket => "ws", + pub(crate) fn ws_disconnect(&self, now: Instant) { + self.inner.ws_disconnect(now) + } + + pub(crate) fn on_call(&self, req: &Request) { + self.inner.on_call(req, self.transport_label) + } + + pub(crate) fn on_response( + &self, + req: &Request, + rp: &MethodResponse, + is_rate_limited: bool, + now: Instant, + ) { + self.inner.on_response(req, rp, is_rate_limited, self.transport_label, now) } } diff --git a/substrate/client/rpc-servers/src/middleware/mod.rs b/substrate/client/rpc-servers/src/middleware/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..88ed8b2f433580fa2d87f1a70eacc32019027f2a --- /dev/null +++ b/substrate/client/rpc-servers/src/middleware/mod.rs @@ -0,0 +1,148 @@ +// 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 . + +//! JSON-RPC specific middleware. + +use std::{ + num::NonZeroU32, + time::{Duration, Instant}, +}; + +use futures::future::{BoxFuture, FutureExt}; +use governor::{clock::Clock, Jitter}; +use jsonrpsee::{ + server::middleware::rpc::RpcServiceT, + types::{ErrorObject, Id, Request}, + MethodResponse, +}; + +mod metrics; +mod rate_limit; + +pub use metrics::*; +pub use rate_limit::*; + +const MAX_JITTER: Duration = Duration::from_millis(50); +const MAX_RETRIES: usize = 10; + +/// JSON-RPC middleware layer. +#[derive(Debug, Clone, Default)] +pub struct MiddlewareLayer { + rate_limit: Option, + metrics: Option, +} + +impl MiddlewareLayer { + /// Create an empty MiddlewareLayer. + pub fn new() -> Self { + Self::default() + } + + /// Enable new rate limit middleware enforced per minute. + pub fn with_rate_limit_per_minute(self, n: NonZeroU32) -> Self { + Self { rate_limit: Some(RateLimit::per_minute(n)), metrics: self.metrics } + } + + /// Enable metrics middleware. + pub fn with_metrics(self, metrics: Metrics) -> Self { + Self { rate_limit: self.rate_limit, metrics: Some(metrics) } + } + + /// Register a new websocket connection. + pub fn ws_connect(&self) { + self.metrics.as_ref().map(|m| m.ws_connect()); + } + + /// Register that a websocket connection was closed. + pub fn ws_disconnect(&self, now: Instant) { + self.metrics.as_ref().map(|m| m.ws_disconnect(now)); + } +} + +impl tower::Layer for MiddlewareLayer { + type Service = Middleware; + + fn layer(&self, service: S) -> Self::Service { + Middleware { service, rate_limit: self.rate_limit.clone(), metrics: self.metrics.clone() } + } +} + +/// JSON-RPC middleware that handles metrics +/// and rate-limiting. +/// +/// These are part of the same middleware +/// because the metrics needs to know whether +/// a call was rate-limited or not because +/// it will impact the roundtrip for a call. +pub struct Middleware { + service: S, + rate_limit: Option, + metrics: Option, +} + +impl<'a, S> RpcServiceT<'a> for Middleware +where + S: Send + Sync + RpcServiceT<'a> + Clone + 'static, +{ + type Future = BoxFuture<'a, MethodResponse>; + + fn call(&self, req: Request<'a>) -> Self::Future { + let now = Instant::now(); + + self.metrics.as_ref().map(|m| m.on_call(&req)); + + let service = self.service.clone(); + let rate_limit = self.rate_limit.clone(); + let metrics = self.metrics.clone(); + + async move { + let mut is_rate_limited = false; + + if let Some(limit) = rate_limit.as_ref() { + let mut attempts = 0; + let jitter = Jitter::up_to(MAX_JITTER); + + loop { + if attempts >= MAX_RETRIES { + return reject_too_many_calls(req.id); + } + + if let Err(rejected) = limit.inner.check() { + tokio::time::sleep(jitter + rejected.wait_time_from(limit.clock.now())) + .await; + } else { + break; + } + + is_rate_limited = true; + attempts += 1; + } + } + + let rp = service.call(req.clone()).await; + metrics.as_ref().map(|m| m.on_response(&req, &rp, is_rate_limited, now)); + + rp + } + .boxed() + } +} + +fn reject_too_many_calls(id: Id) -> MethodResponse { + MethodResponse::error(id, ErrorObject::owned(-32999, "RPC rate limit exceeded", None::<()>)) +} diff --git a/substrate/client/rpc-servers/src/middleware/rate_limit.rs b/substrate/client/rpc-servers/src/middleware/rate_limit.rs new file mode 100644 index 0000000000000000000000000000000000000000..23335eb37686c30d6bf148a2fd38f75013aef606 --- /dev/null +++ b/substrate/client/rpc-servers/src/middleware/rate_limit.rs @@ -0,0 +1,47 @@ +// 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 . + +//! RPC rate limit. + +use governor::{ + clock::{DefaultClock, QuantaClock}, + middleware::NoOpMiddleware, + state::{InMemoryState, NotKeyed}, + Quota, +}; +use std::{num::NonZeroU32, sync::Arc}; + +type RateLimitInner = governor::RateLimiter; + +/// Rate limit. +#[derive(Debug, Clone)] +pub struct RateLimit { + pub(crate) inner: Arc, + pub(crate) clock: QuantaClock, +} + +impl RateLimit { + /// Create a new `RateLimit` per minute. + pub fn per_minute(n: NonZeroU32) -> Self { + let clock = QuantaClock::default(); + Self { + inner: Arc::new(RateLimitInner::direct_with_clock(Quota::per_minute(n), &clock)), + clock, + } + } +} diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 17412f883ca12a0dcda7d8ddc5bfaa5aed12f8ed..c62b3e789d389047f66004f5f76a6bb947ce8b9e 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-rpc-spec-v2" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } # Internal chain structures for "chain_spec". sc-chain-spec = { path = "../chain-spec" } # Pool for submitting extrinsics required by "transaction" @@ -29,23 +29,26 @@ sp-blockchain = { path = "../../primitives/blockchain" } sp-version = { path = "../../primitives/version" } sc-client-api = { path = "../api" } sc-utils = { path = "../utils" } +sc-rpc = { path = "../rpc" } codec = { package = "parity-scale-codec", version = "3.6.1" } -thiserror = "1.0" -serde = "1.0" +thiserror = { workspace = true } +serde = { workspace = true, default-features = true } hex = "0.4" futures = "0.3.21" parking_lot = "0.12.1" tokio-stream = { version = "0.1.14", features = ["sync"] } tokio = { version = "1.22.0", features = ["sync"] } array-bytes = "6.1" -log = "0.4.17" +log = { workspace = true, default-features = true } futures-util = { version = "0.3.30", default-features = false } +rand = "0.8.5" [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["macros"] } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } substrate-test-runtime = { path = "../../test-utils/runtime" } +substrate-test-runtime-transaction-pool = { path = "../../test-utils/runtime/transaction-pool" } sp-consensus = { path = "../../primitives/consensus/common" } sp-externalities = { path = "../../primitives/externalities" } sp-maybe-compressed-blob = { path = "../../primitives/maybe-compressed-blob" } @@ -53,3 +56,4 @@ sc-block-builder = { path = "../block-builder" } sc-service = { path = "../service", features = ["test-helpers"] } assert_matches = "1.3.0" pretty_assertions = "1.2.1" +sc-transaction-pool = { path = "../transaction-pool" } diff --git a/substrate/client/rpc-spec-v2/src/archive/archive.rs b/substrate/client/rpc-spec-v2/src/archive/archive.rs index c01afb5d779559fba3776cd701ae88f6fe150613..82c6b2cacc2f4b02991dc89faa8e28a43318b223 100644 --- a/substrate/client/rpc-spec-v2/src/archive/archive.rs +++ b/substrate/client/rpc-spec-v2/src/archive/archive.rs @@ -34,7 +34,7 @@ use sp_api::{CallApiAt, CallContext}; use sp_blockchain::{ Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, }; -use sp_core::Bytes; +use sp_core::{Bytes, U256}; use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT, NumberFor}, SaturatedConversion, @@ -43,6 +43,36 @@ use std::{collections::HashSet, marker::PhantomData, sync::Arc}; use super::archive_storage::ArchiveStorage; +/// The configuration of [`Archive`]. +pub struct ArchiveConfig { + /// The maximum number of items the `archive_storage` can return for a descendant query before + /// pagination is required. + pub max_descendant_responses: usize, + /// The maximum number of queried items allowed for the `archive_storage` at a time. + pub max_queried_items: usize, +} + +/// The maximum number of items the `archive_storage` can return for a descendant query before +/// pagination is required. +/// +/// Note: this is identical to the `chainHead` value. +const MAX_DESCENDANT_RESPONSES: usize = 5; + +/// The maximum number of queried items allowed for the `archive_storage` at a time. +/// +/// Note: A queried item can also be a descendant query which can return up to +/// `MAX_DESCENDANT_RESPONSES`. +const MAX_QUERIED_ITEMS: usize = 8; + +impl Default for ArchiveConfig { + fn default() -> Self { + Self { + max_descendant_responses: MAX_DESCENDANT_RESPONSES, + max_queried_items: MAX_QUERIED_ITEMS, + } + } +} + /// An API for archive RPC calls. pub struct Archive, Block: BlockT, Client> { /// Substrate client. @@ -51,8 +81,9 @@ pub struct Archive, Block: BlockT, Client> { backend: Arc, /// The hexadecimal encoded hash of the genesis block. genesis_hash: String, - /// The maximum number of reported items by the `archive_storage` at a time. - storage_max_reported_items: usize, + /// The maximum number of items the `archive_storage` can return for a descendant query before + /// pagination is required. + storage_max_descendant_responses: usize, /// The maximum number of queried items allowed for the `archive_storage` at a time. storage_max_queried_items: usize, /// Phantom member to pin the block type. @@ -65,16 +96,15 @@ impl, Block: BlockT, Client> Archive { client: Arc, backend: Arc, genesis_hash: GenesisHash, - storage_max_reported_items: usize, - storage_max_queried_items: usize, + config: ArchiveConfig, ) -> Self { let genesis_hash = hex_string(&genesis_hash.as_ref()); Self { client, backend, genesis_hash, - storage_max_reported_items, - storage_max_queried_items, + storage_max_descendant_responses: config.max_descendant_responses, + storage_max_queried_items: config.max_queried_items, _phantom: PhantomData, } } @@ -97,7 +127,6 @@ impl ArchiveApiServer for Archive::Header as HeaderT>::Number: From, BE: Backend + 'static, Client: BlockBackend + ExecutorProvider @@ -136,7 +165,10 @@ where } fn archive_unstable_hash_by_height(&self, height: u64) -> RpcResult> { - let height: NumberFor = height.into(); + let height: NumberFor = U256::from(height) + .try_into() + .map_err(|_| ArchiveError::InvalidParam(format!("Invalid block height: {}", height)))?; + let finalized_num = self.client.info().finalized_number; if finalized_num >= height { @@ -240,7 +272,7 @@ where let storage_client = ArchiveStorage::new( self.client.clone(), - self.storage_max_reported_items, + self.storage_max_descendant_responses, self.storage_max_queried_items, ); Ok(storage_client.handle_query(hash, items, child_trie)) diff --git a/substrate/client/rpc-spec-v2/src/archive/archive_storage.rs b/substrate/client/rpc-spec-v2/src/archive/archive_storage.rs index 09415af1ca1346c35d77c496c5338c07aae2e13f..26e7c299de418e499863c796c6566e912ac28f06 100644 --- a/substrate/client/rpc-spec-v2/src/archive/archive_storage.rs +++ b/substrate/client/rpc-spec-v2/src/archive/archive_storage.rs @@ -28,12 +28,12 @@ use crate::common::{ storage::{IterQueryType, QueryIter, Storage}, }; -/// Generates the events of the `chainHead_storage` method. +/// Generates the events of the `archive_storage` method. pub struct ArchiveStorage { /// Storage client. client: Storage, - /// The maximum number of reported items by the `archive_storage` at a time. - storage_max_reported_items: usize, + /// The maximum number of responses the API can return for a descendant query at a time. + storage_max_descendant_responses: usize, /// The maximum number of queried items allowed for the `archive_storage` at a time. storage_max_queried_items: usize, } @@ -42,10 +42,14 @@ impl ArchiveStorage { /// Constructs a new [`ArchiveStorage`]. pub fn new( client: Arc, - storage_max_reported_items: usize, + storage_max_descendant_responses: usize, storage_max_queried_items: usize, ) -> Self { - Self { client: Storage::new(client), storage_max_reported_items, storage_max_queried_items } + Self { + client: Storage::new(client), + storage_max_descendant_responses, + storage_max_queried_items, + } } } @@ -96,7 +100,7 @@ where }, hash, child_key.as_ref(), - self.storage_max_reported_items, + self.storage_max_descendant_responses, ) { Ok((results, _)) => storage_results.extend(results), Err(error) => return ArchiveStorageResult::err(error), @@ -111,7 +115,7 @@ where }, hash, child_key.as_ref(), - self.storage_max_reported_items, + self.storage_max_descendant_responses, ) { Ok((results, _)) => storage_results.extend(results), Err(error) => return ArchiveStorageResult::err(error), diff --git a/substrate/client/rpc-spec-v2/src/archive/error.rs b/substrate/client/rpc-spec-v2/src/archive/error.rs index b858212399ce713abbb76e2182f3ccd8cf8cd880..d631c3fb8e8901fe0879634da618cafdf0998084 100644 --- a/substrate/client/rpc-spec-v2/src/archive/error.rs +++ b/substrate/client/rpc-spec-v2/src/archive/error.rs @@ -18,10 +18,7 @@ //! Error helpers for `archive` RPC module. -use jsonrpsee::{ - core::Error as RpcError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::ErrorObject; /// ChainHead RPC errors. #[derive(Debug, thiserror::Error)] @@ -58,9 +55,3 @@ impl From for ErrorObject<'static> { .into() } } - -impl From for RpcError { - fn from(e: Error) -> Self { - CallError::Custom(e.into()).into() - } -} diff --git a/substrate/client/rpc-spec-v2/src/archive/mod.rs b/substrate/client/rpc-spec-v2/src/archive/mod.rs index e1f45e19a62fd41c6d0c3f498becb1fd865175fe..5f020c203eab4fba2bb20e5a7314ded1092e9b6c 100644 --- a/substrate/client/rpc-spec-v2/src/archive/mod.rs +++ b/substrate/client/rpc-spec-v2/src/archive/mod.rs @@ -32,3 +32,4 @@ pub mod archive; pub mod error; pub use api::ArchiveApiServer; +pub use archive::{Archive, ArchiveConfig}; diff --git a/substrate/client/rpc-spec-v2/src/archive/tests.rs b/substrate/client/rpc-spec-v2/src/archive/tests.rs index 45da8e588e62e9e9be019f44b5974c6359bdf823..1803ffa3a3183628a8eff81a10c05efdddc5e962 100644 --- a/substrate/client/rpc-spec-v2/src/archive/tests.rs +++ b/substrate/client/rpc-spec-v2/src/archive/tests.rs @@ -24,15 +24,15 @@ use crate::{ hex_string, MethodResult, }; -use super::{archive::Archive, *}; +use super::{ + archive::{Archive, ArchiveConfig}, + *, +}; use assert_matches::assert_matches; use codec::{Decode, Encode}; use jsonrpsee::{ - core::error::Error, - rpc_params, - types::{error::CallError, EmptyServerParams as EmptyParams}, - RpcModule, + core::EmptyServerParams as EmptyParams, rpc_params, MethodsError as Error, RpcModule, }; use sc_block_builder::BlockBuilderBuilder; use sc_client_api::ChildInfo; @@ -62,7 +62,7 @@ type Header = substrate_test_runtime_client::runtime::Header; type Block = substrate_test_runtime_client::runtime::Block; fn setup_api( - max_returned_items: usize, + max_descendant_responses: usize, max_queried_items: usize, ) -> (Arc>, RpcModule>>) { let child_info = ChildInfo::new_default(CHILD_STORAGE_KEY); @@ -74,9 +74,13 @@ fn setup_api( let backend = builder.backend(); let client = Arc::new(builder.build()); - let api = - Archive::new(client.clone(), backend, CHAIN_GENESIS, max_returned_items, max_queried_items) - .into_rpc(); + let api = Archive::new( + client.clone(), + backend, + CHAIN_GENESIS, + ArchiveConfig { max_descendant_responses, max_queried_items }, + ) + .into_rpc(); (client, api) } @@ -289,7 +293,7 @@ async fn archive_call() { ) .await .unwrap_err(); - assert_matches!(err, Error::Call(CallError::Custom(ref err)) if err.code() == 3001 && err.message().contains("Invalid parameter")); + assert_matches!(err, Error::JsonRpc(err) if err.code() == 3001 && err.message().contains("Invalid parameter")); // Pass an invalid parameters that cannot be decode. let err = api @@ -300,7 +304,7 @@ async fn archive_call() { ) .await .unwrap_err(); - assert_matches!(err, Error::Call(CallError::Custom(ref err)) if err.code() == 3001 && err.message().contains("Invalid parameter")); + assert_matches!(err, Error::JsonRpc(err) if err.code() == 3001 && err.message().contains("Invalid parameter")); // Invalid hash. let result: MethodResult = api diff --git a/substrate/client/rpc-spec-v2/src/chain_head/api.rs b/substrate/client/rpc-spec-v2/src/chain_head/api.rs index 3d6091b91bd275dfde01f54773c7c93d65cb764c..00000e1fb277bbc49aeb343d233b00d408fc45c4 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/api.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/api.rs @@ -20,10 +20,13 @@ //! API trait of the chain head. use crate::{ - chain_head::event::{FollowEvent, MethodResponse}, + chain_head::{ + error::Error, + event::{FollowEvent, MethodResponse}, + }, common::events::StorageQuery, }; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use jsonrpsee::{proc_macros::rpc, server::ResponsePayload}; use sp_rpc::list::ListOrValue; #[rpc(client, server)] @@ -56,7 +59,7 @@ pub trait ChainHeadApi { &self, follow_subscription: String, hash: Hash, - ) -> RpcResult; + ) -> ResponsePayload<'static, MethodResponse>; /// Retrieves the header of a pinned block. /// @@ -75,7 +78,7 @@ pub trait ChainHeadApi { &self, follow_subscription: String, hash: Hash, - ) -> RpcResult>; + ) -> Result, Error>; /// Returns storage entries at a specific block's state. /// @@ -89,7 +92,7 @@ pub trait ChainHeadApi { hash: Hash, items: Vec>, child_trie: Option, - ) -> RpcResult; + ) -> ResponsePayload<'static, MethodResponse>; /// Call into the Runtime API at a specified block's state. /// @@ -103,7 +106,7 @@ pub trait ChainHeadApi { hash: Hash, function: String, call_parameters: String, - ) -> RpcResult; + ) -> ResponsePayload<'static, MethodResponse>; /// Unpin a block or multiple blocks reported by the `follow` method. /// @@ -120,7 +123,7 @@ pub trait ChainHeadApi { &self, follow_subscription: String, hash_or_hashes: ListOrValue, - ) -> RpcResult<()>; + ) -> Result<(), Error>; /// Resumes a storage fetch started with `chainHead_storage` after it has generated an /// `operationWaitingForContinue` event. @@ -133,7 +136,7 @@ pub trait ChainHeadApi { &self, follow_subscription: String, operation_id: String, - ) -> RpcResult<()>; + ) -> Result<(), Error>; /// Stops an operation started with chainHead_unstable_body, chainHead_unstable_call, or /// chainHead_unstable_storage. If the operation was still in progress, this interrupts it. If @@ -147,5 +150,5 @@ pub trait ChainHeadApi { &self, follow_subscription: String, operation_id: String, - ) -> RpcResult<()>; + ) -> Result<(), Error>; } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs index 6e4d6ade9659d2959b1a9c6f259cf952d3132e10..2bda22b452391e5b3ef3f7367b43331079591aa3 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -36,15 +36,15 @@ use crate::{ use codec::Encode; use futures::future::FutureExt; use jsonrpsee::{ - core::{async_trait, RpcResult}, - types::{SubscriptionEmptyError, SubscriptionId, SubscriptionResult}, - SubscriptionSink, + core::async_trait, server::ResponsePayload, types::SubscriptionId, MethodResponseFuture, + PendingSubscriptionSink, SubscriptionSink, }; use log::debug; use sc_client_api::{ Backend, BlockBackend, BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, StorageKey, StorageProvider, }; +use sc_rpc::utils::to_sub_message; use sp_api::CallApiAt; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_core::{traits::CallContext, Bytes}; @@ -136,27 +136,13 @@ impl, Block: BlockT, Client> ChainHead { _phantom: PhantomData, } } +} - /// Accept the subscription and return the subscription ID on success. - fn accept_subscription( - &self, - sink: &mut SubscriptionSink, - ) -> Result { - // The subscription must be accepted before it can provide a valid subscription ID. - sink.accept()?; - - let Some(sub_id) = sink.subscription_id() else { - // This can only happen if the subscription was not accepted. - return Err(SubscriptionEmptyError) - }; - - // Get the string representation for the subscription. - let sub_id = match sub_id { - SubscriptionId::Num(num) => num.to_string(), - SubscriptionId::Str(id) => id.into_owned().into(), - }; - - Ok(sub_id) +/// Helper to convert the `subscription ID` to a string. +pub fn read_subscription_id_as_string(sink: &SubscriptionSink) -> String { + match sink.subscription_id() { + SubscriptionId::Num(n) => n.to_string(), + SubscriptionId::Str(s) => s.into_owned().into(), } } @@ -190,35 +176,28 @@ where + StorageProvider + 'static, { - fn chain_head_unstable_follow( - &self, - mut sink: SubscriptionSink, - with_runtime: bool, - ) -> SubscriptionResult { - let sub_id = match self.accept_subscription(&mut sink) { - Ok(sub_id) => sub_id, - Err(err) => { - sink.close(ChainHeadRpcError::InternalError( - "Cannot generate subscription ID".into(), - )); - return Err(err) - }, - }; - // Keep track of the subscription. - let Some(sub_data) = self.subscriptions.insert_subscription(sub_id.clone(), with_runtime) - else { - // Inserting the subscription can only fail if the JsonRPSee - // generated a duplicate subscription ID. - debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id); - let _ = sink.send(&FollowEvent::::Stop); - return Ok(()) - }; - debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); - + fn chain_head_unstable_follow(&self, pending: PendingSubscriptionSink, with_runtime: bool) { let subscriptions = self.subscriptions.clone(); let backend = self.backend.clone(); let client = self.client.clone(); + let fut = async move { + let Ok(sink) = pending.accept().await else { return }; + + let sub_id = read_subscription_id_as_string(&sink); + + // Keep track of the subscription. + let Some(sub_data) = subscriptions.insert_subscription(sub_id.clone(), with_runtime) + else { + // Inserting the subscription can only fail if the JsonRPSee + // generated a duplicate subscription ID. + debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id); + let msg = to_sub_message(&sink, &FollowEvent::::Stop); + let _ = sink.send(msg).await; + return + }; + debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); + let mut chain_head_follow = ChainHeadFollower::new( client, backend, @@ -234,23 +213,23 @@ where }; self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - Ok(()) } fn chain_head_unstable_body( &self, follow_subscription: String, hash: Block::Hash, - ) -> RpcResult { + ) -> ResponsePayload<'static, MethodResponse> { let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | - Err(SubscriptionManagementError::ExceededLimits) => return Ok(MethodResponse::LimitReached), + Err(SubscriptionManagementError::ExceededLimits) => + return ResponsePayload::success(MethodResponse::LimitReached), Err(SubscriptionManagementError::BlockHashAbsent) => { // Block is not part of the subscription. - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock); }, - Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), + Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), }; let operation_id = block_guard.operation().operation_id(); @@ -277,7 +256,7 @@ where hash ); self.subscriptions.remove_subscription(&follow_subscription); - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) }, Err(error) => FollowEvent::::OperationError(OperationError { operation_id: operation_id.clone(), @@ -285,15 +264,27 @@ where }), }; - let _ = block_guard.response_sender().unbounded_send(event); - Ok(MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items: None })) + let (rp, rp_fut) = method_started_response(operation_id, None); + + let fut = async move { + // Events should only by generated + // if the response was successfully propagated. + if rp_fut.await.is_err() { + return; + } + let _ = block_guard.response_sender().unbounded_send(event); + }; + + self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + + rp } fn chain_head_unstable_header( &self, follow_subscription: String, hash: Block::Hash, - ) -> RpcResult> { + ) -> Result, ChainHeadRpcError> { let _block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | @@ -309,7 +300,6 @@ where .header(hash) .map(|opt_header| opt_header.map(|h| hex_string(&h.encode()))) .map_err(|err| ChainHeadRpcError::InternalError(err.to_string())) - .map_err(Into::into) } fn chain_head_unstable_storage( @@ -318,31 +308,40 @@ where hash: Block::Hash, items: Vec>, child_trie: Option, - ) -> RpcResult { + ) -> ResponsePayload<'static, MethodResponse> { // Gain control over parameter parsing and returned error. - let items = items + let items = match items .into_iter() .map(|query| { let key = StorageKey(parse_hex_param(query.key)?); Ok(StorageQuery { key, query_type: query.query_type }) }) - .collect::, ChainHeadRpcError>>()?; + .collect::, ChainHeadRpcError>>() + { + Ok(items) => items, + Err(err) => { + return ResponsePayload::error(err); + }, + }; - let child_trie = child_trie - .map(|child_trie| parse_hex_param(child_trie)) - .transpose()? - .map(ChildInfo::new_default_from_vec); + let child_trie = match child_trie.map(|child_trie| parse_hex_param(child_trie)).transpose() + { + Ok(c) => c.map(ChildInfo::new_default_from_vec), + Err(e) => return ResponsePayload::error(e), + }; let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, items.len()) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | - Err(SubscriptionManagementError::ExceededLimits) => return Ok(MethodResponse::LimitReached), + Err(SubscriptionManagementError::ExceededLimits) => { + return ResponsePayload::success(MethodResponse::LimitReached); + }, Err(SubscriptionManagementError::BlockHashAbsent) => { // Block is not part of the subscription. - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) }, - Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), + Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), }; let mut storage_client = ChainHeadStorage::::new( @@ -358,16 +357,21 @@ where let mut items = items; items.truncate(num_operations); + let (rp, rp_is_success) = method_started_response(operation_id, Some(discarded)); + let fut = async move { + // Events should only by generated + // if the response was successfully propagated. + if rp_is_success.await.is_err() { + return; + } storage_client.generate_events(block_guard, hash, items, child_trie).await; }; self.executor .spawn_blocking("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - Ok(MethodResponse::Started(MethodResponseStarted { - operation_id, - discarded_items: Some(discarded), - })) + + rp } fn chain_head_unstable_call( @@ -376,29 +380,31 @@ where hash: Block::Hash, function: String, call_parameters: String, - ) -> RpcResult { - let call_parameters = Bytes::from(parse_hex_param(call_parameters)?); + ) -> ResponsePayload<'static, MethodResponse> { + let call_parameters = match parse_hex_param(call_parameters) { + Ok(hex) => Bytes::from(hex), + Err(err) => return ResponsePayload::error(err), + }; let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | Err(SubscriptionManagementError::ExceededLimits) => { // Invalid invalid subscription ID. - return Ok(MethodResponse::LimitReached) + return ResponsePayload::success(MethodResponse::LimitReached) }, Err(SubscriptionManagementError::BlockHashAbsent) => { // Block is not part of the subscription. - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) }, - Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), + Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), }; // Reject subscription if with_runtime is false. if !block_guard.has_runtime() { - return Err(ChainHeadRpcError::InvalidRuntimeCall( + return ResponsePayload::error(ChainHeadRpcError::InvalidRuntimeCall( "The runtime updates flag must be set".to_string(), - ) - .into()) + )); } let operation_id = block_guard.operation().operation_id(); @@ -419,15 +425,27 @@ where }) }); - let _ = block_guard.response_sender().unbounded_send(event); - Ok(MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items: None })) + let (rp, rp_fut) = method_started_response(operation_id, None); + + let fut = async move { + // Events should only by generated + // if the response was successfully propagated. + if rp_fut.await.is_err() { + return; + } + let _ = block_guard.response_sender().unbounded_send(event); + }; + + self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + + rp } fn chain_head_unstable_unpin( &self, follow_subscription: String, hash_or_hashes: ListOrValue, - ) -> RpcResult<()> { + ) -> Result<(), ChainHeadRpcError> { let result = match hash_or_hashes { ListOrValue::Value(hash) => self.subscriptions.unpin_blocks(&follow_subscription, [hash]), @@ -443,9 +461,11 @@ where }, Err(SubscriptionManagementError::BlockHashAbsent) => { // Block is not part of the subscription. - Err(ChainHeadRpcError::InvalidBlock.into()) + Err(ChainHeadRpcError::InvalidBlock) }, - Err(_) => Err(ChainHeadRpcError::InvalidBlock.into()), + Err(SubscriptionManagementError::DuplicateHashes) => + Err(ChainHeadRpcError::InvalidDuplicateHashes), + Err(_) => Err(ChainHeadRpcError::InvalidBlock), } } @@ -453,7 +473,7 @@ where &self, follow_subscription: String, operation_id: String, - ) -> RpcResult<()> { + ) -> Result<(), ChainHeadRpcError> { let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id) else { return Ok(()) @@ -471,7 +491,7 @@ where &self, follow_subscription: String, operation_id: String, - ) -> RpcResult<()> { + ) -> Result<(), ChainHeadRpcError> { let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id) else { return Ok(()) @@ -482,3 +502,11 @@ where Ok(()) } } + +fn method_started_response( + operation_id: String, + discarded_items: Option, +) -> (ResponsePayload<'static, MethodResponse>, MethodResponseFuture) { + let rp = MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items }); + ResponsePayload::success(rp).notify_on_completion() +} 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 b981e69f2e473a6d3dfa44a08ced9702b86eb326..afa99f3aa1648823aee646304c7c2b2cd71da6e2 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 @@ -24,7 +24,7 @@ use crate::chain_head::{ BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock, RuntimeEvent, RuntimeVersionEvent, }, - subscription::{InsertedSubscriptionData, SubscriptionManagement, SubscriptionManagementError}, + subscription::{SubscriptionManagement, SubscriptionManagementError}, }; use futures::{ channel::oneshot, @@ -36,12 +36,22 @@ use log::{debug, error}; use sc_client_api::{ Backend, BlockBackend, BlockImportNotification, BlockchainEvents, FinalityNotification, }; +use sc_rpc::utils::to_sub_message; use sp_api::CallApiAt; use sp_blockchain::{ Backend as BlockChainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, Info, }; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use std::{collections::HashSet, sync::Arc}; +use std::{ + collections::{HashSet, VecDeque}, + sync::Arc, +}; + +/// The maximum number of finalized blocks provided by the +/// `Initialized` event. +const MAX_FINALIZED_BLOCKS: usize = 16; + +use super::subscription::InsertedSubscriptionData; /// Generates the events of the `chainHead_follow` method. pub struct ChainHeadFollower, Block: BlockT, Client> { @@ -92,6 +102,8 @@ struct InitialBlocks { /// /// It is a tuple of (block hash, parent hash). finalized_block_descendants: Vec<(Block::Hash, Block::Hash)>, + /// Hashes of the last finalized blocks + finalized_block_hashes: VecDeque, /// Blocks that should not be reported as pruned by the `Finalized` event. /// /// Substrate database will perform the pruning of height N at @@ -175,13 +187,14 @@ where } /// Get the in-memory blocks of the client, starting from the provided finalized hash. + /// + /// The reported blocks are pinned by this function. fn get_init_blocks_with_forks( &self, - startup_point: &StartupPoint, + finalized: Block::Hash, ) -> Result, SubscriptionManagementError> { let blockchain = self.backend.blockchain(); let leaves = blockchain.leaves()?; - let finalized = startup_point.finalized_hash; let mut pruned_forks = HashSet::new(); let mut finalized_block_descendants = Vec::new(); let mut unique_descendants = HashSet::new(); @@ -195,17 +208,47 @@ where // Ensure a `NewBlock` event is generated for all children of the // finalized block. Describe the tree route as (child_node, parent_node) // Note: the order of elements matters here. - let parents = std::iter::once(finalized).chain(blocks.clone()); + let mut parent = finalized; + for child in blocks { + let pair = (child, parent); - for pair in blocks.zip(parents) { if unique_descendants.insert(pair) { + // The finalized block is pinned below. + self.sub_handle.pin_block(&self.sub_id, child)?; finalized_block_descendants.push(pair); } + + parent = child; } } } - Ok(InitialBlocks { finalized_block_descendants, pruned_forks }) + let mut current_block = finalized; + // The header of the finalized block must not be pruned. + let Some(header) = blockchain.header(current_block)? else { + return Err(SubscriptionManagementError::BlockHeaderAbsent); + }; + + // Report at most `MAX_FINALIZED_BLOCKS`. Note: The node might not have that many blocks. + let mut finalized_block_hashes = VecDeque::with_capacity(MAX_FINALIZED_BLOCKS); + + // Pin the finalized block. + self.sub_handle.pin_block(&self.sub_id, current_block)?; + finalized_block_hashes.push_front(current_block); + current_block = *header.parent_hash(); + + for _ in 0..MAX_FINALIZED_BLOCKS - 1 { + let Ok(Some(header)) = blockchain.header(current_block) else { break }; + // Block cannot be reported if pinning fails. + if self.sub_handle.pin_block(&self.sub_id, current_block).is_err() { + break + }; + + finalized_block_hashes.push_front(current_block); + current_block = *header.parent_hash(); + } + + Ok(InitialBlocks { finalized_block_descendants, finalized_block_hashes, pruned_forks }) } /// Generate the initial events reported by the RPC `follow` method. @@ -217,18 +260,17 @@ where startup_point: &StartupPoint, ) -> Result<(Vec>, HashSet), SubscriptionManagementError> { - let init = self.get_init_blocks_with_forks(startup_point)?; + let init = self.get_init_blocks_with_forks(startup_point.finalized_hash)?; + // The initialized event is the first one sent. let initial_blocks = init.finalized_block_descendants; + let finalized_block_hashes = init.finalized_block_hashes; - // The initialized event is the first one sent. let finalized_block_hash = startup_point.finalized_hash; - self.sub_handle.pin_block(&self.sub_id, finalized_block_hash)?; - let finalized_block_runtime = self.generate_runtime_event(finalized_block_hash, None); let initialized_event = FollowEvent::Initialized(Initialized { - finalized_block_hash, + finalized_block_hashes: finalized_block_hashes.into(), finalized_block_runtime, with_runtime: self.with_runtime, }); @@ -237,8 +279,6 @@ where finalized_block_descendants.push(initialized_event); for (child, parent) in initial_blocks.into_iter() { - self.sub_handle.pin_block(&self.sub_id, child)?; - let new_runtime = self.generate_runtime_event(child, Some(parent)); let event = FollowEvent::NewBlock(NewBlock { @@ -500,7 +540,7 @@ where startup_point: &StartupPoint, mut stream: EventStream, mut to_ignore: HashSet, - mut sink: SubscriptionSink, + sink: SubscriptionSink, rx_stop: oneshot::Receiver<()>, ) where EventStream: Stream> + Unpin, @@ -529,35 +569,23 @@ where self.sub_id, err ); - let _ = sink.send(&FollowEvent::::Stop); + let msg = to_sub_message(&sink, &FollowEvent::::Stop); + let _ = sink.send(msg).await; return }, }; for event in events { - let result = sink.send(&event); - - // Migration note: the new version of jsonrpsee returns Result<(), DisconnectError> - // The logic from `Err(err)` should be moved when building the new - // `SubscriptionMessage`. - - // For now, jsonrpsee returns: - // Ok(true): message sent - // Ok(false): client disconnected or subscription closed - // Err(err): serder serialization error of the event - if let Err(err) = result { + let msg = to_sub_message(&sink, &event); + if let Err(err) = sink.send(msg).await { // Failed to submit event. debug!( target: LOG_TARGET, "[follow][id={:?}] Failed to send event {:?}", self.sub_id, err ); - let _ = sink.send(&FollowEvent::::Stop); - return - } - - if let Ok(false) = result { - // Client disconnected or subscription was closed. + let msg = to_sub_message(&sink, &FollowEvent::::Stop); + let _ = sink.send(msg).await; return } } @@ -568,13 +596,14 @@ where // If we got here either the substrate streams have closed // or the `Stop` receiver was triggered. - let _ = sink.send(&FollowEvent::::Stop); + let msg = to_sub_message(&sink, &FollowEvent::::Stop); + let _ = sink.send(msg).await; } /// Generate the block events for the `chainHead_follow` method. pub async fn generate_events( &mut self, - mut sink: SubscriptionSink, + sink: SubscriptionSink, sub_data: InsertedSubscriptionData, ) { // Register for the new block and finalized notifications. @@ -602,7 +631,8 @@ where self.sub_id, err ); - let _ = sink.send(&FollowEvent::::Stop); + let msg = to_sub_message(&sink, &FollowEvent::::Stop); + let _ = sink.send(msg).await; return }, }; diff --git a/substrate/client/rpc-spec-v2/src/chain_head/error.rs b/substrate/client/rpc-spec-v2/src/chain_head/error.rs index a9b7d7f96e49b662cac843f9427c667ef8e8e72b..8c50e445aa0cf6480d3a672f7120cd7a492f4c99 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/error.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/error.rs @@ -18,10 +18,7 @@ //! Error helpers for `chainHead` RPC module. -use jsonrpsee::{ - core::Error as RpcError, - types::error::{CallError, ErrorObject}, -}; +use jsonrpsee::types::error::ErrorObject; /// ChainHead RPC errors. #[derive(Debug, thiserror::Error)] @@ -35,6 +32,9 @@ pub enum Error { /// Wait-for-continue event not generated. #[error("Wait for continue event was not generated for the subscription")] InvalidContinue, + /// Received duplicate hashes for the `chainHead_unpin` method. + #[error("Received duplicate hashes for the `chainHead_unpin` method")] + InvalidDuplicateHashes, /// Invalid parameter provided to the RPC method. #[error("Invalid parameter: {0}")] InvalidParam(String), @@ -52,6 +52,8 @@ pub mod rpc_spec_v2 { pub const INVALID_RUNTIME_CALL: i32 = -32802; /// Wait-for-continue event not generated. pub const INVALID_CONTINUE: i32 = -32803; + /// Received duplicate hashes for the `chainHead_unpin` method. + pub const INVALID_DUPLICATE_HASHES: i32 = -32804; } /// General purpose errors, as defined in @@ -74,6 +76,8 @@ impl From for ErrorObject<'static> { ErrorObject::owned(rpc_spec_v2::INVALID_RUNTIME_CALL, msg, None::<()>), Error::InvalidContinue => ErrorObject::owned(rpc_spec_v2::INVALID_CONTINUE, msg, None::<()>), + Error::InvalidDuplicateHashes => + ErrorObject::owned(rpc_spec_v2::INVALID_DUPLICATE_HASHES, msg, None::<()>), Error::InvalidParam(_) => ErrorObject::owned(json_rpc_spec::INVALID_PARAM_ERROR, msg, None::<()>), Error::InternalError(_) => @@ -81,9 +85,3 @@ impl From for ErrorObject<'static> { } } } - -impl From for RpcError { - fn from(e: Error) -> Self { - CallError::Custom(e.into()).into() - } -} diff --git a/substrate/client/rpc-spec-v2/src/chain_head/event.rs b/substrate/client/rpc-spec-v2/src/chain_head/event.rs index 560ab87eab405968e9b7348bfd37792307b87a8f..e0c804d16eb17853876a8b4bfd38d42d4b506f49 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/event.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/event.rs @@ -111,8 +111,8 @@ impl From for RuntimeEvent { #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Initialized { - /// The hash of the latest finalized block. - pub finalized_block_hash: Hash, + /// The hash of the lastest finalized blocks. + pub finalized_block_hashes: Vec, /// The runtime version of the finalized block. /// /// # Note @@ -135,12 +135,12 @@ impl Serialize for Initialized { { if self.with_runtime { let mut state = serializer.serialize_struct("Initialized", 2)?; - state.serialize_field("finalizedBlockHash", &self.finalized_block_hash)?; + state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?; state.serialize_field("finalizedBlockRuntime", &self.finalized_block_runtime)?; state.end() } else { let mut state = serializer.serialize_struct("Initialized", 1)?; - state.serialize_field("finalizedBlockHash", &self.finalized_block_hash)?; + state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?; state.end() } } @@ -348,13 +348,13 @@ mod tests { fn follow_initialized_event_no_updates() { // Runtime flag is false. let event: FollowEvent = FollowEvent::Initialized(Initialized { - finalized_block_hash: "0x1".into(), + finalized_block_hashes: vec!["0x1".into()], finalized_block_runtime: None, with_runtime: false, }); let ser = serde_json::to_string(&event).unwrap(); - let exp = r#"{"event":"initialized","finalizedBlockHash":"0x1"}"#; + let exp = r#"{"event":"initialized","finalizedBlockHashes":["0x1"]}"#; assert_eq!(ser, exp); let event_dec: FollowEvent = serde_json::from_str(exp).unwrap(); @@ -373,7 +373,7 @@ mod tests { let runtime_event = RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.into() }); let mut initialized = Initialized { - finalized_block_hash: "0x1".into(), + finalized_block_hashes: vec!["0x1".into()], finalized_block_runtime: Some(runtime_event), with_runtime: true, }; @@ -381,7 +381,7 @@ mod tests { let ser = serde_json::to_string(&event).unwrap(); let exp = concat!( - r#"{"event":"initialized","finalizedBlockHash":"0x1","#, + r#"{"event":"initialized","finalizedBlockHashes":["0x1"],"#, r#""finalizedBlockRuntime":{"type":"valid","spec":{"specName":"ABC","implName":"Impl","#, r#""specVersion":1,"implVersion":0,"apis":{},"transactionVersion":0}}}"#, ); diff --git a/substrate/client/rpc-spec-v2/src/chain_head/mod.rs b/substrate/client/rpc-spec-v2/src/chain_head/mod.rs index 4cbbd00f64f31f04f44ff83f9e5980e7d22e7308..c9fe19aca2b1898da25e45019e1924256d732d9a 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/mod.rs @@ -23,7 +23,7 @@ //! Methods are prefixed by `chainHead`. #[cfg(test)] -mod test_utils; +pub mod test_utils; #[cfg(test)] mod tests; diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/error.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/error.rs index 38e8fd7384fcbd222bbfc385de602a834613c07d..2c22e51ca4dc70102fca98f4eea7f66d8e044a02 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/error.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/error.rs @@ -38,6 +38,9 @@ pub enum SubscriptionManagementError { /// The specified subscription ID is not present. #[error("Subscription is absent")] SubscriptionAbsent, + /// The unpin method was called with duplicate hashes. + #[error("Duplicate hashes")] + DuplicateHashes, /// Custom error. #[error("Subscription error {0}")] Custom(String), @@ -52,7 +55,8 @@ impl PartialEq for SubscriptionManagementError { (Self::Blockchain(_), Self::Blockchain(_)) | (Self::BlockHashAbsent, Self::BlockHashAbsent) | (Self::BlockHeaderAbsent, Self::BlockHeaderAbsent) | - (Self::SubscriptionAbsent, Self::SubscriptionAbsent) => true, + (Self::SubscriptionAbsent, Self::SubscriptionAbsent) | + (Self::DuplicateHashes, Self::DuplicateHashes) => true, (Self::Custom(lhs), Self::Custom(rhs)) => lhs == rhs, _ => false, } 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 2b250f3dc2cf2d0b52a222256b314eefc035a310..d2879679501fd17eada1b826ff01478cce24b7a3 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 @@ -22,7 +22,7 @@ use sc_client_api::Backend; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_runtime::traits::Block as BlockT; use std::{ - collections::{hash_map::Entry, HashMap}, + collections::{hash_map::Entry, HashMap, HashSet}, sync::{atomic::AtomicBool, Arc}, time::{Duration, Instant}, }; @@ -750,11 +750,27 @@ impl> SubscriptionsInner { } } + /// Ensure the provided hashes are unique. + fn ensure_hash_uniqueness( + hashes: impl IntoIterator + Clone, + ) -> Result<(), SubscriptionManagementError> { + let mut set = HashSet::new(); + hashes.into_iter().try_for_each(|hash| { + if !set.insert(hash) { + Err(SubscriptionManagementError::DuplicateHashes) + } else { + Ok(()) + } + }) + } + pub fn unpin_blocks( &mut self, sub_id: &str, hashes: impl IntoIterator + Clone, ) -> Result<(), SubscriptionManagementError> { + Self::ensure_hash_uniqueness(hashes.clone())?; + let Some(sub) = self.subs.get_mut(sub_id) else { return Err(SubscriptionManagementError::SubscriptionAbsent) }; @@ -985,6 +1001,76 @@ mod tests { assert!(block_state.is_none()); } + #[test] + fn unpin_duplicate_hashes() { + let (backend, mut client) = init_backend(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let hash_1 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_1) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let hash_2 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_2) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; + let hash_3 = block.header.hash(); + futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); + + let mut subs = + SubscriptionsInner::new(10, Duration::from_secs(10), MAX_OPERATIONS_PER_SUB, backend); + let id_1 = "abc".to_string(); + let id_2 = "abcd".to_string(); + + // Pin all blocks for the first subscription. + let _stop = subs.insert_subscription(id_1.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_1, hash_1).unwrap(), true); + assert_eq!(subs.pin_block(&id_1, hash_2).unwrap(), true); + assert_eq!(subs.pin_block(&id_1, hash_3).unwrap(), true); + + // Pin only block 2 for the second subscription. + let _stop = subs.insert_subscription(id_2.clone(), true).unwrap(); + assert_eq!(subs.pin_block(&id_2, hash_2).unwrap(), true); + + // Check reference count. + assert_eq!(*subs.global_blocks.get(&hash_1).unwrap(), 1); + assert_eq!(*subs.global_blocks.get(&hash_2).unwrap(), 2); + assert_eq!(*subs.global_blocks.get(&hash_3).unwrap(), 1); + + // Unpin the same block twice. + let err = subs.unpin_blocks(&id_1, vec![hash_1, hash_1, hash_2, hash_2]).unwrap_err(); + assert_eq!(err, SubscriptionManagementError::DuplicateHashes); + + // Check reference count must be unaltered. + assert_eq!(*subs.global_blocks.get(&hash_1).unwrap(), 1); + assert_eq!(*subs.global_blocks.get(&hash_2).unwrap(), 2); + assert_eq!(*subs.global_blocks.get(&hash_3).unwrap(), 1); + + // Unpin the blocks correctly. + subs.unpin_blocks(&id_1, vec![hash_1, hash_2]).unwrap(); + assert_eq!(subs.global_blocks.get(&hash_1), None); + assert_eq!(*subs.global_blocks.get(&hash_2).unwrap(), 1); + assert_eq!(*subs.global_blocks.get(&hash_3).unwrap(), 1); + } + #[test] fn subscription_lock_block() { let builder = TestClientBuilder::new(); 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 d63a98a5cb0d93b3633864bd3405ad38c2b6e799..e81bd4bfa0b04aa3ad9e25e47e83e9ace1c7f985 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 @@ -63,7 +63,7 @@ impl ChainHeadMockClient { BlockImportNotification::new(header.hash(), BlockOrigin::Own, header, true, None, sink); for sink in self.import_sinks.lock().iter_mut() { - sink.unbounded_send(notification.clone()).unwrap(); + let _ = sink.unbounded_send(notification.clone()); } } @@ -83,7 +83,7 @@ impl ChainHeadMockClient { let notification = FinalityNotification::from_summary(summary, sink); for sink in self.finality_sinks.lock().iter_mut() { - sink.unbounded_send(notification.clone()).unwrap(); + let _ = sink.unbounded_send(notification.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 4859793a8e2ffe7d9a37c468ee8b04b0bcb97a36..4d9dfb24e0a93bc45669ff1af4899eeb27694c18 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -27,10 +27,7 @@ use assert_matches::assert_matches; use codec::{Decode, Encode}; use futures::Future; use jsonrpsee::{ - core::{error::Error, server::rpc_module::Subscription as RpcSubscription}, - rpc_params, - types::error::CallError, - RpcModule, + core::server::Subscription as RpcSubscription, rpc_params, MethodsError as Error, RpcModule, }; use sc_block_builder::BlockBuilderBuilder; use sc_client_api::ChildInfo; @@ -120,7 +117,7 @@ async fn setup_api() -> ( ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); @@ -171,12 +168,12 @@ async fn follow_subscription_produces_blocks() { .into_rpc(); let finalized_hash = client.info().finalized_hash; - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_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_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -239,18 +236,18 @@ async fn follow_with_runtime() { .into_rpc(); let finalized_hash = client.info().finalized_hash; - let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); // Initialized must always be reported first. let event: FollowEvent = get_next_event(&mut sub).await; // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION - let runtime_str = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":0,\ - \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ + let runtime_str = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ + \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",5],\ [\"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\":0}"; + [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":1}"; let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap(); @@ -258,7 +255,7 @@ async fn follow_with_runtime() { Some(RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.clone().into() })); // Runtime must always be reported with the first event. let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime, with_runtime: false, }); @@ -361,7 +358,7 @@ async fn get_header() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Obtain the valid header. @@ -390,7 +387,7 @@ async fn get_body() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Valid call. @@ -475,7 +472,7 @@ async fn call_runtime() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Pass an invalid parameters that cannot be decode. @@ -488,7 +485,7 @@ async fn call_runtime() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message().contains("Invalid parameter") + Error::JsonRpc(err) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message().contains("Invalid parameter") ); // Valid call. @@ -550,7 +547,7 @@ async fn call_runtime_without_flag() { ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); @@ -591,7 +588,7 @@ async fn call_runtime_without_flag() { .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_RUNTIME_CALL && err.message().contains("subscription was started with `withRuntime` set to `false`") + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_RUNTIME_CALL && err.message().contains("subscription was started with `withRuntime` set to `false`") ); } @@ -629,7 +626,7 @@ async fn get_storage_hash() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Valid call without storage at the key. @@ -897,7 +894,7 @@ async fn get_storage_value() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Valid call without storage at the key. @@ -1209,11 +1206,12 @@ async fn separate_operation_ids_for_subscriptions() { .into_rpc(); // Create two separate subscriptions. - let mut sub_first = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap(); + let mut sub_first = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); let sub_id_first = sub_first.subscription_id(); let sub_id_first = serde_json::to_string(&sub_id_first).unwrap(); - let mut sub_second = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap(); + let mut sub_second = + api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); let sub_id_second = sub_second.subscription_id(); let sub_id_second = serde_json::to_string(&sub_id_second).unwrap(); @@ -1341,12 +1339,12 @@ async fn follow_generates_initial_blocks() { let block_2_f_hash = block_2_f.header.hash(); client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_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_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -1450,7 +1448,7 @@ async fn follow_exceeding_pinned_blocks() { ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap(); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -1526,7 +1524,7 @@ async fn follow_with_unpin() { ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); @@ -1572,7 +1570,7 @@ async fn follow_with_unpin() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // To not exceed the number of pinned blocks, we need to unpin before the next import. @@ -1618,6 +1616,108 @@ async fn follow_with_unpin() { assert!(sub.next::>().await.is_none()); } +#[tokio::test] +async fn unpin_duplicate_hashes() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let mut client = Arc::new(builder.build()); + + let api = ChainHead::new( + client.clone(), + backend, + Arc::new(TaskExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: 3, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + operation_max_storage_items: MAX_PAGINATION_LIMIT, + }, + ) + .into_rpc(); + + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap(); + let sub_id = sub.subscription_id(); + let sub_id = serde_json::to_string(&sub_id).unwrap(); + + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_hash = format!("{:?}", block.header.hash()); + client.import(BlockOrigin::Own, block.clone()).await.unwrap(); + + // Ensure the imported block is propagated and pinned for this subscription. + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::Initialized(_) + ); + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::NewBlock(_) + ); + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::BestBlockChanged(_) + ); + + // Try to unpin duplicate hashes. + let err = api + .call::<_, serde_json::Value>( + "chainHead_unstable_unpin", + rpc_params![&sub_id, vec![&block_hash, &block_hash]], + ) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == super::error::rpc_spec_v2::INVALID_DUPLICATE_HASHES && err.message() == "Received duplicate hashes for the `chainHead_unpin` method" + ); + + // Block tree: + // finalized_block -> block -> block2 + let block2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_hash_2 = format!("{:?}", block2.header.hash()); + client.import(BlockOrigin::Own, block2.clone()).await.unwrap(); + + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::NewBlock(_) + ); + + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::BestBlockChanged(_) + ); + + // Try to unpin duplicate hashes. + let err = api + .call::<_, serde_json::Value>( + "chainHead_unstable_unpin", + rpc_params![&sub_id, vec![&block_hash, &block_hash_2, &block_hash]], + ) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == super::error::rpc_spec_v2::INVALID_DUPLICATE_HASHES && err.message() == "Received duplicate hashes for the `chainHead_unpin` method" + ); + + // Can unpin blocks. + let _res: () = api + .call("chainHead_unstable_unpin", rpc_params![&sub_id, vec![&block_hash, &block_hash_2]]) + .await + .unwrap(); +} + #[tokio::test] async fn follow_with_multiple_unpin_hashes() { let builder = TestClientBuilder::new(); @@ -1637,7 +1737,7 @@ async fn follow_with_multiple_unpin_hashes() { ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); @@ -1721,7 +1821,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); let _res: () = api @@ -1738,7 +1838,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Unpin multiple blocks. @@ -1756,7 +1856,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); let err = api @@ -1767,7 +1867,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(CallError::Custom(ref err)) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); } @@ -1791,12 +1891,12 @@ async fn follow_prune_best_block() { .into_rpc(); let finalized_hash = client.info().finalized_hash; - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_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_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -1981,6 +2081,7 @@ async fn follow_forks_pruned_block() { // ^^^ finalized // -> block 1 -> block 2_f -> block 3_f // + let finalized_hash = client.info().finalized_hash; let block_1 = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -1990,6 +2091,7 @@ async fn follow_forks_pruned_block() { .build() .unwrap() .block; + let block_1_hash = block_1.header.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); let block_2 = BlockBuilderBuilder::new(&*client) @@ -2000,6 +2102,7 @@ async fn follow_forks_pruned_block() { .build() .unwrap() .block; + let block_2_hash = block_2.header.hash(); client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); let block_3 = BlockBuilderBuilder::new(&*client) @@ -2051,12 +2154,17 @@ async fn follow_forks_pruned_block() { // Block 2_f and 3_f are not pruned, pruning happens at height (N - 1). client.finalize_block(block_3_hash, None).unwrap(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_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_hash: format!("{:?}", block_3_hash), + finalized_block_hashes: vec![ + format!("{:?}", finalized_hash), + format!("{:?}", block_1_hash), + format!("{:?}", block_2_hash), + format!("{:?}", block_3_hash), + ], finalized_block_runtime: None, with_runtime: false, }); @@ -2205,12 +2313,12 @@ async fn follow_report_multiple_pruned_block() { let block_3_f = block_builder.build().unwrap().block; let block_3_f_hash = block_3_f.hash(); client.import(BlockOrigin::Own, block_3_f.clone()).await.unwrap(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_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_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -2388,7 +2496,7 @@ async fn pin_block_references() { } } - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); @@ -2520,7 +2628,7 @@ async fn follow_finalized_before_new_block() { let block_1_hash = block_1.header.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); - let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap(); // Trigger the `FinalizedNotification` for block 1 before the `BlockImportNotification`, and // expect for the `chainHead` to generate `NewBlock`, `BestBlock` and `Finalized` events. @@ -2532,7 +2640,7 @@ async fn follow_finalized_before_new_block() { let finalized_hash = client.info().finalized_hash; let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Initialized(Initialized { - finalized_block_hash: format!("{:?}", finalized_hash), + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], finalized_block_runtime: None, with_runtime: false, }); @@ -2622,7 +2730,7 @@ async fn ensure_operation_limits_works() { ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); @@ -2726,7 +2834,7 @@ async fn check_continue_operation() { ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); @@ -2908,7 +3016,7 @@ async fn stop_storage_operation() { ) .into_rpc(); - let mut sub = api.subscribe("chainHead_unstable_follow", [true]).await.unwrap(); + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); diff --git a/substrate/client/rpc-spec-v2/src/chain_spec/tests.rs b/substrate/client/rpc-spec-v2/src/chain_spec/tests.rs index 9326fd5f3b738711c3226f5e3d1ab2c4484361e1..3abd9a548a1e9d29319ae75d009ae79250ea9d58 100644 --- a/substrate/client/rpc-spec-v2/src/chain_spec/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_spec/tests.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::*; -use jsonrpsee::{types::EmptyServerParams as EmptyParams, RpcModule}; +use jsonrpsee::{core::EmptyServerParams as EmptyParams, RpcModule}; use sc_chain_spec::Properties; const CHAIN_NAME: &'static str = "TEST_CHAIN_NAME"; diff --git a/substrate/client/rpc-spec-v2/src/lib.rs b/substrate/client/rpc-spec-v2/src/lib.rs index 23ed422cff17d443edde1b6e2aff1d5f997ed1b5..fa822fd446bae50c542418b2ea824355af9679ca 100644 --- a/substrate/client/rpc-spec-v2/src/lib.rs +++ b/substrate/client/rpc-spec-v2/src/lib.rs @@ -37,7 +37,7 @@ pub mod transaction; pub type SubscriptionTaskExecutor = std::sync::Arc; /// The result of an RPC method. -#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(untagged)] pub enum MethodResult { /// Method generated a result. diff --git a/substrate/client/rpc-spec-v2/src/transaction/api.rs b/substrate/client/rpc-spec-v2/src/transaction/api.rs index 3eb6cb204f24db463cabe3ae4d5a9560bf624057..33af9c9533388a1e4d5832a390c8eb96da756905 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/api.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/api.rs @@ -18,8 +18,8 @@ //! API trait for transactions. -use crate::transaction::event::TransactionEvent; -use jsonrpsee::proc_macros::rpc; +use crate::transaction::{error::ErrorBroadcast, event::TransactionEvent}; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use sp_core::Bytes; #[rpc(client, server)] @@ -28,10 +28,33 @@ pub trait TransactionApi { /// /// See [`TransactionEvent`](crate::transaction::event::TransactionEvent) for details on /// transaction life cycle. + /// + /// # Unstable + /// + /// This method is unstable and subject to change in the future. #[subscription( - name = "transaction_unstable_submitAndWatch" => "transaction_unstable_watchEvent", - unsubscribe = "transaction_unstable_unwatch", + name = "transactionWatch_unstable_submitAndWatch" => "transactionWatch_unstable_watchEvent", + unsubscribe = "transactionWatch_unstable_unwatch", item = TransactionEvent, )] fn submit_and_watch(&self, bytes: Bytes); } + +#[rpc(client, server)] +pub trait TransactionBroadcastApi { + /// Broadcast an extrinsic to the chain. + /// + /// # Unstable + /// + /// This method is unstable and subject to change in the future. + #[method(name = "transaction_unstable_broadcast")] + fn broadcast(&self, bytes: Bytes) -> RpcResult>; + + /// Broadcast an extrinsic to the chain. + /// + /// # Unstable + /// + /// This method is unstable and subject to change in the future. + #[method(name = "transaction_unstable_stop")] + fn stop_broadcast(&self, operation_id: String) -> Result<(), ErrorBroadcast>; +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/error.rs b/substrate/client/rpc-spec-v2/src/transaction/error.rs index d2de07afd5955cd2d41148bab0119384b117bf42..116977af66001096ff7cb14775b768451833a866 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/error.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/error.rs @@ -21,6 +21,7 @@ //! Errors are interpreted as transaction events for subscriptions. use crate::transaction::event::{TransactionError, TransactionEvent}; +use jsonrpsee::types::error::ErrorObject; use sc_transaction_pool_api::error::Error as PoolError; use sp_runtime::transaction_validity::InvalidTransaction; @@ -98,3 +99,29 @@ impl From for TransactionEvent { } } } + +/// TransactionBroadcast error. +#[derive(Debug, thiserror::Error)] +pub enum ErrorBroadcast { + /// The provided operation ID is invalid. + #[error("Invalid operation id")] + InvalidOperationID, +} + +/// General purpose errors, as defined in +/// . +pub mod json_rpc_spec { + /// Invalid parameter error. + pub const INVALID_PARAM_ERROR: i32 = -32602; +} + +impl From for ErrorObject<'static> { + fn from(e: ErrorBroadcast) -> Self { + let msg = e.to_string(); + + match e { + ErrorBroadcast::InvalidOperationID => + ErrorObject::owned(json_rpc_spec::INVALID_PARAM_ERROR, msg, None::<()>), + } + } +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/event.rs b/substrate/client/rpc-spec-v2/src/transaction/event.rs index 8b80fcda17beb22cece35c928f10d72175fa9458..882ac8490b07c00432c4296964b7f995329df3dc 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/event.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/event.rs @@ -20,23 +20,6 @@ use serde::{Deserialize, Serialize}; -/// The transaction was broadcasted to a number of peers. -/// -/// # Note -/// -/// The RPC does not guarantee that the peers have received the -/// transaction. -/// -/// When the number of peers is zero, the event guarantees that -/// shutting down the local node will lead to the transaction -/// not being included in the chain. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionBroadcasted { - /// The number of peers the transaction was broadcasted to. - pub num_peers: usize, -} - /// The transaction was included in a block of the chain. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -59,9 +42,6 @@ pub struct TransactionError { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TransactionDropped { - /// True if the transaction was broadcasted to other peers and - /// may still be included in the block. - pub broadcasted: bool, /// Reason of the event. pub error: String, } @@ -70,20 +50,17 @@ pub struct TransactionDropped { /// /// The status events can be grouped based on their kinds as: /// -/// 1. Runtime validated the transaction: +/// 1. Runtime validated the transaction and it entered the pool: /// - `Validated` /// -/// 2. Inside the `Ready` queue: -/// - `Broadcast` -/// -/// 3. Leaving the pool: +/// 2. Leaving the pool: /// - `BestChainBlockIncluded` /// - `Invalid` /// -/// 4. Block finalized: +/// 3. Block finalized: /// - `Finalized` /// -/// 5. At any time: +/// 4. At any time: /// - `Dropped` /// - `Error` /// @@ -101,8 +78,6 @@ pub struct TransactionDropped { pub enum TransactionEvent { /// The transaction was validated by the runtime. Validated, - /// The transaction was broadcasted to a number of peers. - Broadcasted(TransactionBroadcasted), /// The transaction was included in a best block of the chain. /// /// # Note @@ -159,7 +134,6 @@ enum TransactionEventBlockIR { #[serde(tag = "event")] enum TransactionEventNonBlockIR { Validated, - Broadcasted(TransactionBroadcasted), Error(TransactionError), Invalid(TransactionError), Dropped(TransactionDropped), @@ -186,8 +160,6 @@ impl From> for TransactionEventIR { match value { TransactionEvent::Validated => TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Validated), - TransactionEvent::Broadcasted(event) => - TransactionEventIR::NonBlock(TransactionEventNonBlockIR::Broadcasted(event)), TransactionEvent::BestChainBlockIncluded(event) => TransactionEventIR::Block(TransactionEventBlockIR::BestChainBlockIncluded(event)), TransactionEvent::Finalized(event) => @@ -207,8 +179,6 @@ impl From> for TransactionEvent { match value { TransactionEventIR::NonBlock(status) => match status { TransactionEventNonBlockIR::Validated => TransactionEvent::Validated, - TransactionEventNonBlockIR::Broadcasted(event) => - TransactionEvent::Broadcasted(event), TransactionEventNonBlockIR::Error(event) => TransactionEvent::Error(event), TransactionEventNonBlockIR::Invalid(event) => TransactionEvent::Invalid(event), TransactionEventNonBlockIR::Dropped(event) => TransactionEvent::Dropped(event), @@ -239,19 +209,6 @@ mod tests { assert_eq!(event_dec, event); } - #[test] - fn broadcasted_event() { - let event: TransactionEvent<()> = - TransactionEvent::Broadcasted(TransactionBroadcasted { num_peers: 2 }); - let ser = serde_json::to_string(&event).unwrap(); - - let exp = r#"{"event":"broadcasted","numPeers":2}"#; - assert_eq!(ser, exp); - - let event_dec: TransactionEvent<()> = serde_json::from_str(exp).unwrap(); - assert_eq!(event_dec, event); - } - #[test] fn best_chain_event() { let event: TransactionEvent<()> = TransactionEvent::BestChainBlockIncluded(None); @@ -320,13 +277,11 @@ mod tests { #[test] fn dropped_event() { - let event: TransactionEvent<()> = TransactionEvent::Dropped(TransactionDropped { - broadcasted: true, - error: "abc".to_string(), - }); + let event: TransactionEvent<()> = + TransactionEvent::Dropped(TransactionDropped { error: "abc".to_string() }); let ser = serde_json::to_string(&event).unwrap(); - let exp = r#"{"event":"dropped","broadcasted":true,"error":"abc"}"#; + let exp = r#"{"event":"dropped","error":"abc"}"#; assert_eq!(ser, exp); let event_dec: TransactionEvent<()> = serde_json::from_str(exp).unwrap(); diff --git a/substrate/client/rpc-spec-v2/src/transaction/mod.rs b/substrate/client/rpc-spec-v2/src/transaction/mod.rs index 212912ba1c728feb5e01614a74fcab09ce5bc079..514ccf047dc28570c78543755577661a6a7791af 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/mod.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/mod.rs @@ -25,14 +25,16 @@ //! //! Methods are prefixed by `transaction`. +#[cfg(test)] +mod tests; + pub mod api; pub mod error; pub mod event; pub mod transaction; +pub mod transaction_broadcast; -pub use api::TransactionApiServer; -pub use event::{ - TransactionBlock, TransactionBroadcasted, TransactionDropped, TransactionError, - TransactionEvent, -}; +pub use api::{TransactionApiServer, TransactionBroadcastApiServer}; +pub use event::{TransactionBlock, TransactionDropped, TransactionError, TransactionEvent}; pub use transaction::Transaction; +pub use transaction_broadcast::TransactionBroadcast; diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/executor.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/executor.rs new file mode 100644 index 0000000000000000000000000000000000000000..ff9aca79887c3f29025697f9f5832c914735e0d6 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/executor.rs @@ -0,0 +1,100 @@ +// 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 sp_core::{testing::TaskExecutor, traits::SpawnNamed}; +use std::sync::{atomic::AtomicUsize, Arc}; +use tokio::sync::mpsc; + +/// Wrap the `TaskExecutor` to know when the broadcast future is dropped. +#[derive(Clone)] +pub struct TaskExecutorBroadcast { + executor: TaskExecutor, + sender: mpsc::UnboundedSender<()>, + num_tasks: Arc, +} + +/// The channel that receives events when the broadcast futures are dropped. +pub type TaskExecutorRecv = mpsc::UnboundedReceiver<()>; + +/// The state of the `TaskExecutorBroadcast`. +pub struct TaskExecutorState { + pub recv: TaskExecutorRecv, + pub num_tasks: Arc, +} + +impl TaskExecutorState { + pub fn num_tasks(&self) -> usize { + self.num_tasks.load(std::sync::atomic::Ordering::Acquire) + } +} + +impl TaskExecutorBroadcast { + /// Construct a new `TaskExecutorBroadcast` and a receiver to know when the broadcast futures + /// are dropped. + pub fn new() -> (Self, TaskExecutorState) { + let (sender, recv) = mpsc::unbounded_channel(); + let num_tasks = Arc::new(AtomicUsize::new(0)); + + ( + Self { executor: TaskExecutor::new(), sender, num_tasks: num_tasks.clone() }, + TaskExecutorState { recv, num_tasks }, + ) + } +} + +impl SpawnNamed for TaskExecutorBroadcast { + fn spawn( + &self, + name: &'static str, + group: Option<&'static str>, + future: futures::future::BoxFuture<'static, ()>, + ) { + let sender = self.sender.clone(); + let num_tasks = self.num_tasks.clone(); + + let future = Box::pin(async move { + num_tasks.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + future.await; + num_tasks.fetch_sub(1, std::sync::atomic::Ordering::AcqRel); + + let _ = sender.send(()); + }); + + self.executor.spawn(name, group, future) + } + + fn spawn_blocking( + &self, + name: &'static str, + group: Option<&'static str>, + future: futures::future::BoxFuture<'static, ()>, + ) { + let sender = self.sender.clone(); + let num_tasks = self.num_tasks.clone(); + + let future = Box::pin(async move { + num_tasks.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + future.await; + num_tasks.fetch_sub(1, std::sync::atomic::Ordering::AcqRel); + + let _ = sender.send(()); + }); + + self.executor.spawn_blocking(name, group, future) + } +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/middleware_pool.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/middleware_pool.rs new file mode 100644 index 0000000000000000000000000000000000000000..aa8ac572dec9d8d6de9212fcb94c97ce2b804da4 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/middleware_pool.rs @@ -0,0 +1,187 @@ +// 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 codec::Encode; +use futures::Future; +use sc_transaction_pool::BasicPool; +use sc_transaction_pool_api::{ + ImportNotificationStream, PoolFuture, PoolStatus, ReadyTransactions, TransactionFor, + TransactionPool, TransactionSource, TransactionStatusStreamFor, TxHash, +}; + +use crate::hex_string; +use futures::{FutureExt, StreamExt}; + +use sp_runtime::traits::{Block as BlockT, NumberFor}; +use std::{collections::HashMap, pin::Pin, sync::Arc}; +use substrate_test_runtime_transaction_pool::TestApi; +use tokio::sync::mpsc; + +pub type Block = substrate_test_runtime_client::runtime::Block; + +pub type TxTestPool = MiddlewarePool; +pub type TxStatusType = sc_transaction_pool_api::TransactionStatus< + sc_transaction_pool_api::TxHash, + sc_transaction_pool_api::BlockHash, +>; +pub type TxStatusTypeTest = TxStatusType; + +/// The type of the event that the middleware captures. +#[derive(Debug, PartialEq)] +pub enum MiddlewarePoolEvent { + TransactionStatus { + transaction: String, + status: sc_transaction_pool_api::TransactionStatus< + ::Hash, + ::Hash, + >, + }, + PoolError { + transaction: String, + err: String, + }, +} + +/// The channel that receives events when the broadcast futures are dropped. +pub type MiddlewarePoolRecv = mpsc::UnboundedReceiver; + +/// Add a middleware to the transaction pool. +/// +/// This wraps the `submit_and_watch` to gain access to the events. +pub struct MiddlewarePool { + pub inner_pool: Arc>, + /// Send the middleware events to the test. + sender: mpsc::UnboundedSender, +} + +impl MiddlewarePool { + /// Construct a new [`MiddlewarePool`]. + pub fn new(pool: Arc>) -> (Self, MiddlewarePoolRecv) { + let (sender, recv) = mpsc::unbounded_channel(); + (MiddlewarePool { inner_pool: pool, sender }, recv) + } +} + +impl TransactionPool for MiddlewarePool { + type Block = as TransactionPool>::Block; + type Hash = as TransactionPool>::Hash; + type InPoolTransaction = as TransactionPool>::InPoolTransaction; + type Error = as TransactionPool>::Error; + + fn submit_at( + &self, + at: ::Hash, + source: TransactionSource, + xts: Vec>, + ) -> PoolFuture, Self::Error>>, Self::Error> { + self.inner_pool.submit_at(at, source, xts) + } + + fn submit_one( + &self, + at: ::Hash, + source: TransactionSource, + xt: TransactionFor, + ) -> PoolFuture, Self::Error> { + self.inner_pool.submit_one(at, source, xt) + } + + fn submit_and_watch( + &self, + at: ::Hash, + source: TransactionSource, + xt: TransactionFor, + ) -> PoolFuture>>, Self::Error> { + let pool = self.inner_pool.clone(); + let sender = self.sender.clone(); + let transaction = hex_string(&xt.encode()); + + async move { + let watcher = match pool.submit_and_watch(at, source, xt).await { + Ok(watcher) => watcher, + Err(err) => { + let _ = sender.send(MiddlewarePoolEvent::PoolError { + transaction: transaction.clone(), + err: err.to_string(), + }); + return Err(err); + }, + }; + + let watcher = watcher.map(move |status| { + let sender = sender.clone(); + let transaction = transaction.clone(); + + let _ = sender.send(MiddlewarePoolEvent::TransactionStatus { + transaction, + status: status.clone(), + }); + + status + }); + + Ok(watcher.boxed()) + } + .boxed() + } + + fn remove_invalid(&self, hashes: &[TxHash]) -> Vec> { + self.inner_pool.remove_invalid(hashes) + } + + fn status(&self) -> PoolStatus { + self.inner_pool.status() + } + + fn import_notification_stream(&self) -> ImportNotificationStream> { + self.inner_pool.import_notification_stream() + } + + fn hash_of(&self, xt: &TransactionFor) -> TxHash { + self.inner_pool.hash_of(xt) + } + + fn on_broadcasted(&self, propagations: HashMap, Vec>) { + self.inner_pool.on_broadcasted(propagations) + } + + fn ready_transaction(&self, hash: &TxHash) -> Option> { + self.inner_pool.ready_transaction(hash) + } + + fn ready_at( + &self, + at: NumberFor, + ) -> Pin< + Box< + dyn Future< + Output = Box> + Send>, + > + Send, + >, + > { + self.inner_pool.ready_at(at) + } + + fn ready(&self) -> Box> + Send> { + self.inner_pool.ready() + } + + fn futures(&self) -> Vec { + self.inner_pool.futures() + } +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/mod.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..ab0caaf906fd08049ccf6c9f421d3134d3689322 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/mod.rs @@ -0,0 +1,24 @@ +// 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 . + +mod executor; +mod middleware_pool; +#[macro_use] +mod setup; + +mod transaction_broadcast_tests; diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/setup.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/setup.rs new file mode 100644 index 0000000000000000000000000000000000000000..04ee7b9b4c94c4f3eac763ad06ba9b8964c2287e --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/setup.rs @@ -0,0 +1,120 @@ +// 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::{ + chain_head::test_utils::ChainHeadMockClient, + transaction::{ + api::TransactionBroadcastApiServer, + tests::executor::{TaskExecutorBroadcast, TaskExecutorState}, + TransactionBroadcast as RpcTransactionBroadcast, + }, +}; +use futures::Future; +use jsonrpsee::RpcModule; +use sc_transaction_pool::*; +use std::{pin::Pin, sync::Arc}; +use substrate_test_runtime_client::{prelude::*, Client}; +use substrate_test_runtime_transaction_pool::TestApi; + +use crate::transaction::tests::middleware_pool::{MiddlewarePool, MiddlewarePoolRecv}; + +pub type Block = substrate_test_runtime_client::runtime::Block; + +/// Initial Alice account nonce. +pub const ALICE_NONCE: u64 = 209; + +fn create_basic_pool_with_genesis( + test_api: Arc, + options: Options, +) -> (BasicPool, Pin + Send>>) { + let genesis_hash = { + test_api + .chain() + .read() + .block_by_number + .get(&0) + .map(|blocks| blocks[0].0.header.hash()) + .expect("there is block 0. qed") + }; + BasicPool::new_test(test_api, genesis_hash, genesis_hash, options) +} + +fn maintained_pool( + options: Options, +) -> (BasicPool, Arc, futures::executor::ThreadPool) { + let api = Arc::new(TestApi::with_alice_nonce(ALICE_NONCE)); + let (pool, background_task) = create_basic_pool_with_genesis(api.clone(), options); + + let thread_pool = futures::executor::ThreadPool::new().unwrap(); + thread_pool.spawn_ok(background_task); + (pool, api, thread_pool) +} + +pub fn setup_api( + options: Options, +) -> ( + Arc, + Arc, + Arc>>, + RpcModule>>>, + TaskExecutorState, + MiddlewarePoolRecv, +) { + let (pool, api, _) = maintained_pool(options); + let (pool, pool_state) = MiddlewarePool::new(Arc::new(pool).clone()); + let pool = Arc::new(pool); + + let builder = TestClientBuilder::new(); + let client = Arc::new(builder.build()); + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let (task_executor, executor_recv) = TaskExecutorBroadcast::new(); + + let tx_api = + RpcTransactionBroadcast::new(client_mock.clone(), pool.clone(), Arc::new(task_executor)) + .into_rpc(); + + (api, pool, client_mock, tx_api, executor_recv, pool_state) +} + +/// Get the next event from the provided middleware in at most 5 seconds. +macro_rules! get_next_event { + ($middleware:expr) => { + tokio::time::timeout(std::time::Duration::from_secs(5), $middleware.recv()) + .await + .unwrap() + .unwrap() + }; +} + +/// Collect the next number of transaction events from the provided middleware. +macro_rules! get_next_tx_events { + ($middleware:expr, $num:expr) => {{ + let mut events = std::collections::HashMap::new(); + for _ in 0..$num { + let event = get_next_event!($middleware); + match event { + crate::transaction::tests::middleware_pool::MiddlewarePoolEvent::TransactionStatus { transaction, status } => { + events.entry(transaction).or_insert_with(|| vec![]).push(status); + }, + other => panic!("Expected TransactionStatus, received {:?}", other), + }; + } + events + }}; +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_broadcast_tests.rs b/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_broadcast_tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..690a1a64d7460c656f5180c59120c951dfb04472 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/tests/transaction_broadcast_tests.rs @@ -0,0 +1,523 @@ +// 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::{hex_string, transaction::error::json_rpc_spec}; +use assert_matches::assert_matches; +use codec::Encode; +use jsonrpsee::{rpc_params, MethodsError as Error}; +use sc_transaction_pool::{Options, PoolLimit}; +use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool, TransactionPool}; +use std::sync::Arc; +use substrate_test_runtime_client::AccountKeyring::*; +use substrate_test_runtime_transaction_pool::uxt; + +// Test helpers. +use crate::transaction::tests::{ + middleware_pool::{MiddlewarePoolEvent, TxStatusTypeTest}, + setup::{setup_api, ALICE_NONCE}, +}; + +#[tokio::test] +async fn tx_broadcast_enters_pool() { + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(Default::default()); + + // Start at block 1. + let block_1_header = api.push_block(1, vec![], true); + + let uxt = uxt(Alice, ALICE_NONCE); + let xt = hex_string(&uxt.encode()); + + let operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready + } + ); + + assert_eq!(1, pool.inner_pool.status().ready); + assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes); + + // Import block 2 with the transaction included. + let block_2_header = api.push_block(2, vec![uxt.clone()], true); + let block_2 = block_2_header.hash(); + + // Announce block 2 to the pool. + let event = ChainEvent::NewBestBlock { hash: block_2, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::InBlock((block_2, 0)) + } + ); + + // The future broadcast awaits for the finalized status to be reached. + // Force the future to exit by calling stop. + let _: () = tx_api + .call("transaction_unstable_stop", rpc_params![&operation_id]) + .await + .unwrap(); + + // Ensure the broadcast future finishes. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); +} + +#[tokio::test] +async fn tx_broadcast_invalid_tx() { + let (_, pool, _, tx_api, mut exec_middleware, _) = setup_api(Default::default()); + + // Invalid parameters. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_broadcast", [1u8]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid params" + ); + + assert_eq!(0, pool.status().ready); + + // Invalid transaction that cannot be decoded. The broadcast silently exits. + let xt = "0xdeadbeef"; + let operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + assert_eq!(0, pool.status().ready); + + // Await the broadcast future to exit. + // Without this we'd be subject to races, where we try to call the stop before the tx is + // dropped. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); + + // The broadcast future was dropped, and the operation is no longer active. + // When the operation is not active, either from the tx being finalized or a + // terminal error; the stop method should return an error. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_stop", rpc_params![&operation_id]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" + ); +} + +#[tokio::test] +async fn tx_stop_with_invalid_operation_id() { + let (_, _, _, tx_api, _, _) = setup_api(Default::default()); + + // Make an invalid stop call. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_stop", ["invalid_operation_id"]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" + ); +} + +#[tokio::test] +async fn tx_broadcast_resubmits_future_nonce_tx() { + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(Default::default()); + + // Start at block 1. + let block_1_header = api.push_block(1, vec![], true); + let block_1 = block_1_header.hash(); + + let current_uxt = uxt(Alice, ALICE_NONCE); + let current_xt = hex_string(¤t_uxt.encode()); + // This lives in the future. + let future_uxt = uxt(Alice, ALICE_NONCE + 1); + let future_xt = hex_string(&future_uxt.encode()); + + let future_operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![&future_xt]) + .await + .unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: future_xt.clone(), + status: TxStatusTypeTest::Future + } + ); + + let event = ChainEvent::NewBestBlock { hash: block_1, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + // Ensure the tx is in the future. + assert_eq!(1, pool.inner_pool.status().future); + + let block_2_header = api.push_block(2, vec![], true); + let block_2 = block_2_header.hash(); + + let operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![¤t_xt]) + .await + .unwrap(); + assert_ne!(future_operation_id, operation_id); + + // Announce block 2 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_2_header).await; + + // Collect the events of both transactions. + let events = get_next_tx_events!(&mut pool_middleware, 2); + // Transactions entered the ready queue. + assert_eq!(events.get(¤t_xt).unwrap(), &vec![TxStatusTypeTest::Ready]); + assert_eq!(events.get(&future_xt).unwrap(), &vec![TxStatusTypeTest::Ready]); + + let event = ChainEvent::NewBestBlock { hash: block_2, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(2, pool.inner_pool.status().ready); + assert_eq!(0, pool.inner_pool.status().future); + + // Finalize transactions. + let block_3_header = api.push_block(3, vec![current_uxt, future_uxt], true); + let block_3 = block_3_header.hash(); + client_mock.trigger_import_stream(block_3_header).await; + + let event = ChainEvent::Finalized { hash: block_3, tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + assert_eq!(0, pool.inner_pool.status().future); + + let events = get_next_tx_events!(&mut pool_middleware, 4); + assert_eq!( + events.get(¤t_xt).unwrap(), + &vec![TxStatusTypeTest::InBlock((block_3, 0)), TxStatusTypeTest::Finalized((block_3, 0))] + ); + assert_eq!( + events.get(&future_xt).unwrap(), + &vec![TxStatusTypeTest::InBlock((block_3, 1)), TxStatusTypeTest::Finalized((block_3, 1))] + ); + + // Both broadcast futures must exit. + let _ = get_next_event!(&mut exec_middleware.recv); + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); +} + +/// This test is similar to `tx_broadcast_enters_pool` +/// However the last block is announced as finalized to force the +/// broadcast future to exit before the `stop` is called. +#[tokio::test] +async fn tx_broadcast_stop_after_broadcast_finishes() { + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(Default::default()); + + // Start at block 1. + let block_1_header = api.push_block(1, vec![], true); + + let uxt = uxt(Alice, ALICE_NONCE); + let xt = hex_string(&uxt.encode()); + + let operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction + // pool.inner_pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready + } + ); + + assert_eq!(1, pool.inner_pool.status().ready); + assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes); + + // Import block 2 with the transaction included. + let block_2_header = api.push_block(2, vec![uxt.clone()], true); + let block_2 = block_2_header.hash(); + + // Announce block 2 to the pool.inner_pool. + let event = ChainEvent::Finalized { hash: block_2, tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + + assert_eq!(0, pool.inner_pool.status().ready); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::InBlock((block_2, 0)) + } + ); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Finalized((block_2, 0)) + } + ); + + // Ensure the broadcast future terminated properly. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); + + // The operation ID is no longer valid, check that the broadcast future + // cleared out the inner state of the operation. + let err = tx_api + .call::<_, serde_json::Value>("transaction_unstable_stop", rpc_params![&operation_id]) + .await + .unwrap_err(); + assert_matches!(err, + Error::JsonRpc(err) if err.code() == json_rpc_spec::INVALID_PARAM_ERROR && err.message() == "Invalid operation id" + ); +} + +#[tokio::test] +async fn tx_broadcast_resubmits_invalid_tx() { + let limits = PoolLimit { count: 8192, total_bytes: 20 * 1024 * 1024 }; + let options = Options { + ready: limits.clone(), + future: limits, + reject_future_transactions: false, + // This ensures that a transaction is not banned. + ban_time: std::time::Duration::ZERO, + }; + + let (api, pool, client_mock, tx_api, mut exec_middleware, mut pool_middleware) = + setup_api(options); + + let uxt = uxt(Alice, ALICE_NONCE); + let xt = hex_string(&uxt.encode()); + let _operation_id: String = + tx_api.call("transaction_unstable_broadcast", rpc_params![&xt]).await.unwrap(); + + let block_1_header = api.push_block(1, vec![], true); + let block_1 = block_1_header.hash(); + // Announce block 1 to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_1_header).await; + + // Ensure the tx propagated from `transaction_unstable_broadcast` to the transaction pool. + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready, + } + ); + assert_eq!(1, pool.inner_pool.status().ready); + assert_eq!(uxt.encode().len(), pool.inner_pool.status().ready_bytes); + + // Mark the transaction as invalid from the API, causing a temporary ban. + api.add_invalid(&uxt); + + // Push an event to the pool to ensure the transaction is excluded. + let event = ChainEvent::NewBestBlock { hash: block_1, tree_route: None }; + pool.inner_pool.maintain(event).await; + assert_eq!(1, pool.inner_pool.status().ready); + + // Ensure the `transaction_unstable_broadcast` is aware of the invalid transaction. + let event = get_next_event!(&mut pool_middleware); + // Because we have received an `Invalid` status, we try to broadcast the transaction with the + // next announced block. + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Invalid + } + ); + + // Import block 2. + let block_2_header = api.push_block(2, vec![], true); + client_mock.trigger_import_stream(block_2_header).await; + + // Ensure we propagate the temporary ban error to `submit_and_watch`. + // This ensures we'll loop again with the next annmounced block and try to resubmit the + // transaction. The transaction remains temporarily banned until the pool is maintained. + let event = get_next_event!(&mut pool_middleware); + assert_matches!(event, MiddlewarePoolEvent::PoolError { transaction, err } if transaction == xt && err.contains("Transaction temporarily Banned")); + + // Import block 3. + let block_3_header = api.push_block(3, vec![], true); + let block_3 = block_3_header.hash(); + // Remove the invalid transaction from the pool to allow it to pass through. + api.remove_invalid(&uxt); + let event = ChainEvent::NewBestBlock { hash: block_3, tree_route: None }; + // We have to maintain the pool to ensure the transaction is no longer invalid. + // This clears out the banned transactions. + pool.inner_pool.maintain(event).await; + assert_eq!(0, pool.inner_pool.status().ready); + + // Announce block to `transaction_unstable_broadcast`. + client_mock.trigger_import_stream(block_3_header).await; + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Ready, + } + ); + assert_eq!(1, pool.inner_pool.status().ready); + + let block_4_header = api.push_block(4, vec![uxt], true); + let block_4 = block_4_header.hash(); + let event = ChainEvent::Finalized { hash: block_4, tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::InBlock((block_4, 0)), + } + ); + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: xt.clone(), + status: TxStatusTypeTest::Finalized((block_4, 0)), + } + ); + + // Ensure the broadcast future terminated properly. + let _ = get_next_event!(&mut exec_middleware.recv); + assert_eq!(0, exec_middleware.num_tasks()); +} + +/// This is similar to `tx_broadcast_resubmits_invalid_tx`. +/// However, it forces the tx to be resubmited because of the pool +/// limits. Which is a different code path than the invalid tx. +#[tokio::test] +async fn tx_broadcast_resubmits_dropped_tx() { + let limits = PoolLimit { count: 1, total_bytes: 1000 }; + let options = Options { + ready: limits.clone(), + future: limits, + reject_future_transactions: false, + // This ensures that a transaction is not banned. + ban_time: std::time::Duration::ZERO, + }; + + let (api, pool, client_mock, tx_api, _, mut pool_middleware) = setup_api(options); + + let current_uxt = uxt(Alice, ALICE_NONCE); + let current_xt = hex_string(¤t_uxt.encode()); + // This lives in the future. + let future_uxt = uxt(Alice, ALICE_NONCE + 1); + let future_xt = hex_string(&future_uxt.encode()); + + // By default the `validate_transaction` mock uses priority 1 for + // transactions. Bump the priority to ensure other transactions + // are immediately dropped. + api.set_priority(¤t_uxt, 10); + + let current_operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![¤t_xt]) + .await + .unwrap(); + + // Announce block 1 to `transaction_unstable_broadcast`. + let block_1_header = api.push_block(1, vec![], true); + let event = + ChainEvent::Finalized { hash: block_1_header.hash(), tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + client_mock.trigger_import_stream(block_1_header).await; + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::TransactionStatus { + transaction: current_xt.clone(), + status: TxStatusTypeTest::Ready, + } + ); + assert_eq!(1, pool.inner_pool.status().ready); + + // The future tx has priority 2, smaller than the current 10. + api.set_priority(&future_uxt, 2); + let future_operation_id: String = tx_api + .call("transaction_unstable_broadcast", rpc_params![&future_xt]) + .await + .unwrap(); + assert_ne!(current_operation_id, future_operation_id); + + let block_2_header = api.push_block(2, vec![], true); + let event = + ChainEvent::Finalized { hash: block_2_header.hash(), tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + client_mock.trigger_import_stream(block_2_header).await; + + // We must have at most 1 transaction in the pool, as per limits above. + assert_eq!(1, pool.inner_pool.status().ready); + + let event = get_next_event!(&mut pool_middleware); + assert_eq!( + event, + MiddlewarePoolEvent::PoolError { + transaction: future_xt.clone(), + err: "Transaction couldn't enter the pool because of the limit".into() + } + ); + + let block_3_header = api.push_block(3, vec![current_uxt], true); + let event = + ChainEvent::Finalized { hash: block_3_header.hash(), tree_route: Arc::from(vec![]) }; + pool.inner_pool.maintain(event).await; + client_mock.trigger_import_stream(block_3_header.clone()).await; + + // The first tx is in a finalzied block; the future tx must enter the pool. + let events = get_next_tx_events!(&mut pool_middleware, 3); + assert_eq!( + events.get(¤t_xt).unwrap(), + &vec![ + TxStatusTypeTest::InBlock((block_3_header.hash(), 0)), + TxStatusTypeTest::Finalized((block_3_header.hash(), 0)) + ] + ); + // The dropped transaction was resubmitted. + assert_eq!(events.get(&future_xt).unwrap(), &vec![TxStatusTypeTest::Ready]); +} diff --git a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs index fe16310aeffa126cb66ababe68c58f445d9611fe..d44006392dca4a05a6c9f5bbf872c75bc7d5ee52 100644 --- a/substrate/client/rpc-spec-v2/src/transaction/transaction.rs +++ b/substrate/client/rpc-spec-v2/src/transaction/transaction.rs @@ -22,34 +22,22 @@ use crate::{ transaction::{ api::TransactionApiServer, error::Error, - event::{ - TransactionBlock, TransactionBroadcasted, TransactionDropped, TransactionError, - TransactionEvent, - }, + event::{TransactionBlock, TransactionDropped, TransactionError, TransactionEvent}, }, SubscriptionTaskExecutor, }; -use jsonrpsee::{ - core::async_trait, - types::{ - error::{CallError, ErrorObject}, - SubscriptionResult, - }, - SubscriptionSink, -}; +use codec::Decode; +use futures::{StreamExt, TryFutureExt}; +use jsonrpsee::{core::async_trait, types::error::ErrorObject, PendingSubscriptionSink}; +use sc_rpc::utils::pipe_from_stream; use sc_transaction_pool_api::{ error::IntoPoolError, BlockHash, TransactionFor, TransactionPool, TransactionSource, TransactionStatus, }; -use std::sync::Arc; - -use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::Bytes; use sp_runtime::traits::Block as BlockT; - -use codec::Decode; -use futures::{FutureExt, StreamExt, TryFutureExt}; +use std::sync::Arc; /// An API for transaction RPC calls. pub struct Transaction { @@ -88,117 +76,84 @@ where Pool: TransactionPool + Sync + Send + 'static, Pool::Hash: Unpin, ::Hash: Unpin, - Client: HeaderBackend + ProvideRuntimeApi + Send + Sync + 'static, + Client: HeaderBackend + Send + Sync + 'static, { - fn submit_and_watch(&self, mut sink: SubscriptionSink, xt: Bytes) -> SubscriptionResult { - // This is the only place where the RPC server can return an error for this - // subscription. Other defects must be signaled as events to the sink. - let decoded_extrinsic = match TransactionFor::::decode(&mut &xt[..]) { - Ok(decoded_extrinsic) => decoded_extrinsic, - Err(e) => { - let err = CallError::Custom(ErrorObject::owned( - BAD_FORMAT, - format!("Extrinsic has invalid format: {}", e), - None::<()>, - )); - let _ = sink.reject(err); - return Ok(()) - }, - }; + fn submit_and_watch(&self, pending: PendingSubscriptionSink, xt: Bytes) { + let client = self.client.clone(); + let pool = self.pool.clone(); - let best_block_hash = self.client.info().best_hash; + let fut = async move { + // This is the only place where the RPC server can return an error for this + // subscription. Other defects must be signaled as events to the sink. + let decoded_extrinsic = match TransactionFor::::decode(&mut &xt[..]) { + Ok(decoded_extrinsic) => decoded_extrinsic, + Err(e) => { + let err = ErrorObject::owned( + BAD_FORMAT, + format!("Extrinsic has invalid format: {}", e), + None::<()>, + ); + let _ = pending.reject(err).await; + return + }, + }; - let submit = self - .pool - .submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic) - .map_err(|e| { - e.into_pool_error() - .map(Error::from) - .unwrap_or_else(|e| Error::Verification(Box::new(e))) - }); + let best_block_hash = client.info().best_hash; + + let submit = pool + .submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic) + .map_err(|e| { + e.into_pool_error() + .map(Error::from) + .unwrap_or_else(|e| Error::Verification(Box::new(e))) + }); - let fut = async move { match submit.await { Ok(stream) => { - let mut state = TransactionState::new(); - let stream = - stream.filter_map(|event| async move { state.handle_event(event) }); - sink.pipe_from_stream(stream.boxed()).await; + let stream = stream.filter_map(move |event| async move { handle_event(event) }); + pipe_from_stream(pending, stream.boxed()).await; }, Err(err) => { // We have not created an `Watcher` for the tx. Make sure the // error is still propagated as an event. let event: TransactionEvent<::Hash> = err.into(); - sink.pipe_from_stream(futures::stream::once(async { event }).boxed()).await; + pipe_from_stream(pending, futures::stream::once(async { event }).boxed()).await; }, }; }; - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - Ok(()) + sc_rpc::utils::spawn_subscription_task(&self.executor, fut); } } -/// The transaction's state that needs to be preserved between -/// multiple events generated by the transaction-pool. -/// -/// # Note -/// -/// In the future, the RPC server can submit only the last event when multiple -/// identical events happen in a row. -#[derive(Clone, Copy)] -struct TransactionState { - /// True if the transaction was previously broadcasted. - broadcasted: bool, -} - -impl TransactionState { - /// Construct a new [`TransactionState`]. - pub fn new() -> Self { - TransactionState { broadcasted: false } - } - - /// Handle events generated by the transaction-pool and convert them - /// to the new API expected state. - #[inline] - pub fn handle_event( - &mut self, - event: TransactionStatus, - ) -> Option> { - match event { - TransactionStatus::Ready | TransactionStatus::Future => - Some(TransactionEvent::::Validated), - TransactionStatus::Broadcast(peers) => { - // Set the broadcasted flag once if we submitted the transaction to - // at least one peer. - self.broadcasted = self.broadcasted || !peers.is_empty(); - - Some(TransactionEvent::Broadcasted(TransactionBroadcasted { - num_peers: peers.len(), - })) - }, - TransactionStatus::InBlock((hash, index)) => - Some(TransactionEvent::BestChainBlockIncluded(Some(TransactionBlock { - hash, - index, - }))), - TransactionStatus::Retracted(_) => Some(TransactionEvent::BestChainBlockIncluded(None)), - TransactionStatus::FinalityTimeout(_) => - Some(TransactionEvent::Dropped(TransactionDropped { - broadcasted: self.broadcasted, - error: "Maximum number of finality watchers has been reached".into(), - })), - TransactionStatus::Finalized((hash, index)) => - Some(TransactionEvent::Finalized(TransactionBlock { hash, index })), - TransactionStatus::Usurped(_) => Some(TransactionEvent::Invalid(TransactionError { - error: "Extrinsic was rendered invalid by another extrinsic".into(), - })), - TransactionStatus::Dropped => Some(TransactionEvent::Invalid(TransactionError { - error: "Extrinsic dropped from the pool due to exceeding limits".into(), - })), - TransactionStatus::Invalid => Some(TransactionEvent::Invalid(TransactionError { - error: "Extrinsic marked as invalid".into(), +/// Handle events generated by the transaction-pool and convert them +/// to the new API expected state. +#[inline] +pub fn handle_event( + event: TransactionStatus, +) -> Option> { + match event { + TransactionStatus::Ready | TransactionStatus::Future => + Some(TransactionEvent::::Validated), + TransactionStatus::InBlock((hash, index)) => + Some(TransactionEvent::BestChainBlockIncluded(Some(TransactionBlock { hash, index }))), + TransactionStatus::Retracted(_) => Some(TransactionEvent::BestChainBlockIncluded(None)), + TransactionStatus::FinalityTimeout(_) => + Some(TransactionEvent::Dropped(TransactionDropped { + error: "Maximum number of finality watchers has been reached".into(), })), - } + TransactionStatus::Finalized((hash, index)) => + Some(TransactionEvent::Finalized(TransactionBlock { hash, index })), + TransactionStatus::Usurped(_) => Some(TransactionEvent::Invalid(TransactionError { + error: "Extrinsic was rendered invalid by another extrinsic".into(), + })), + TransactionStatus::Dropped => Some(TransactionEvent::Invalid(TransactionError { + error: "Extrinsic dropped from the pool due to exceeding limits".into(), + })), + TransactionStatus::Invalid => Some(TransactionEvent::Invalid(TransactionError { + error: "Extrinsic marked as invalid".into(), + })), + // These are the events that are not supported by the new API. + TransactionStatus::Broadcast(_) => None, } } diff --git a/substrate/client/rpc-spec-v2/src/transaction/transaction_broadcast.rs b/substrate/client/rpc-spec-v2/src/transaction/transaction_broadcast.rs new file mode 100644 index 0000000000000000000000000000000000000000..92c838261874a816335b6d7e82c7df7667e2c235 --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/transaction/transaction_broadcast.rs @@ -0,0 +1,251 @@ +// 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 . + +//! API implementation for broadcasting transactions. + +use crate::{transaction::api::TransactionBroadcastApiServer, SubscriptionTaskExecutor}; +use codec::Decode; +use futures::{FutureExt, Stream, StreamExt}; +use futures_util::stream::AbortHandle; +use jsonrpsee::core::{async_trait, RpcResult}; +use parking_lot::RwLock; +use rand::{distributions::Alphanumeric, Rng}; +use sc_client_api::BlockchainEvents; +use sc_transaction_pool_api::{ + error::IntoPoolError, TransactionFor, TransactionPool, TransactionSource, +}; +use sp_blockchain::HeaderBackend; +use sp_core::Bytes; +use sp_runtime::traits::Block as BlockT; +use std::{collections::HashMap, sync::Arc}; + +use super::error::ErrorBroadcast; + +/// An API for transaction RPC calls. +pub struct TransactionBroadcast { + /// Substrate client. + client: Arc, + /// Transactions pool. + pool: Arc, + /// Executor to spawn subscriptions. + executor: SubscriptionTaskExecutor, + /// The brodcast operation IDs. + broadcast_ids: Arc>>, +} + +/// The state of a broadcast operation. +struct BroadcastState { + /// Handle to abort the running future that broadcasts the transaction. + handle: AbortHandle, +} + +impl TransactionBroadcast { + /// Creates a new [`TransactionBroadcast`]. + pub fn new(client: Arc, pool: Arc, executor: SubscriptionTaskExecutor) -> Self { + TransactionBroadcast { client, pool, executor, broadcast_ids: Default::default() } + } + + /// Generate an unique operation ID for the `transaction_broadcast` RPC method. + pub fn generate_unique_id(&self) -> String { + let generate_operation_id = || { + // The length of the operation ID. + const OPERATION_ID_LEN: usize = 16; + + rand::thread_rng() + .sample_iter(Alphanumeric) + .take(OPERATION_ID_LEN) + .map(char::from) + .collect::() + }; + + let mut id = generate_operation_id(); + + let broadcast_ids = self.broadcast_ids.read(); + + while broadcast_ids.contains_key(&id) { + id = generate_operation_id(); + } + + id + } +} + +/// Currently we treat all RPC transactions as externals. +/// +/// Possibly in the future we could allow opt-in for special treatment +/// of such transactions, so that the block authors can inject +/// some unique transactions via RPC and have them included in the pool. +const TX_SOURCE: TransactionSource = TransactionSource::External; + +#[async_trait] +impl TransactionBroadcastApiServer for TransactionBroadcast +where + Pool: TransactionPool + Sync + Send + 'static, + Pool::Error: IntoPoolError, + ::Hash: Unpin, + Client: HeaderBackend + BlockchainEvents + Send + Sync + 'static, +{ + fn broadcast(&self, bytes: Bytes) -> RpcResult> { + let pool = self.pool.clone(); + + // The unique ID of this operation. + let id = self.generate_unique_id(); + + let mut best_block_import_stream = + Box::pin(self.client.import_notification_stream().filter_map( + |notification| async move { notification.is_new_best.then_some(notification.hash) }, + )); + + let broadcast_transaction_fut = async move { + // There is nothing we could do with an extrinsic of invalid format. + let Ok(decoded_extrinsic) = TransactionFor::::decode(&mut &bytes[..]) else { + return; + }; + + // Flag to determine if the we should broadcast the transaction again. + let mut is_done = false; + + while !is_done { + // Wait for the last block to become available. + let Some(best_block_hash) = + last_stream_element(&mut best_block_import_stream).await + else { + return; + }; + + let mut stream = match pool + .submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic.clone()) + .await + { + Ok(stream) => stream, + // The transaction was not included to the pool. + Err(e) => { + let Ok(pool_err) = e.into_pool_error() else { return }; + + if pool_err.is_retriable() { + // Try to resubmit the transaction at a later block for + // recoverable errors. + continue + } else { + return; + } + }, + }; + + while let Some(event) = stream.next().await { + // Check if the transaction could be submitted again + // at a later time. + if event.is_retriable() { + break; + } + + // Stop if this is the final event of the transaction stream + // and the event is not retriable. + if event.is_final() { + is_done = true; + break; + } + } + } + }; + + // Convert the future into an abortable future, for easily terminating it from the + // `transaction_stop` method. + let (fut, handle) = futures::future::abortable(broadcast_transaction_fut); + let broadcast_ids = self.broadcast_ids.clone(); + let drop_id = id.clone(); + // The future expected by the executor must be `Future` instead of + // `Future>`. + let fut = fut.map(move |_| { + // Remove the entry from the broadcast IDs map. + broadcast_ids.write().remove(&drop_id); + }); + + // Keep track of this entry and the abortable handle. + { + let mut broadcast_ids = self.broadcast_ids.write(); + broadcast_ids.insert(id.clone(), BroadcastState { handle }); + } + + sc_rpc::utils::spawn_subscription_task(&self.executor, fut); + + Ok(Some(id)) + } + + fn stop_broadcast(&self, operation_id: String) -> Result<(), ErrorBroadcast> { + let mut broadcast_ids = self.broadcast_ids.write(); + + let Some(broadcast_state) = broadcast_ids.remove(&operation_id) else { + return Err(ErrorBroadcast::InvalidOperationID) + }; + + broadcast_state.handle.abort(); + + Ok(()) + } +} + +/// Returns the last element of the providided stream, or `None` if the stream is closed. +async fn last_stream_element(stream: &mut S) -> Option +where + S: Stream + Unpin, +{ + let Some(mut element) = stream.next().await else { return None }; + + // We are effectively polling the stream for the last available item at this time. + // The `now_or_never` returns `None` if the stream is `Pending`. + // + // If the stream contains `Hash0x1 Hash0x2 Hash0x3 Hash0x4`, we want only `Hash0x4`. + while let Some(next) = stream.next().now_or_never() { + let Some(next) = next else { + // Nothing to do if the stream terminated. + return None + }; + element = next; + } + + Some(element) +} + +#[cfg(test)] +mod tests { + use super::*; + use tokio_stream::wrappers::ReceiverStream; + + #[tokio::test] + async fn check_last_stream_element() { + let (tx, rx) = tokio::sync::mpsc::channel(16); + + let mut stream = ReceiverStream::new(rx); + // Check the stream with one element queued. + tx.send(1).await.unwrap(); + assert_eq!(last_stream_element(&mut stream).await, Some(1)); + + // Check the stream with multiple elements. + tx.send(1).await.unwrap(); + tx.send(2).await.unwrap(); + tx.send(3).await.unwrap(); + assert_eq!(last_stream_element(&mut stream).await, Some(3)); + + // Drop the stream with some elements + tx.send(1).await.unwrap(); + tx.send(2).await.unwrap(); + drop(tx); + assert_eq!(last_stream_element(&mut stream).await, None); + } +} diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index 47425c6d3549993608f35eff1643f55275a579fc..f65e6c9a59ec1c6c593d00c7639ca4bef26e4319 100644 --- a/substrate/client/rpc/Cargo.toml +++ b/substrate/client/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-rpc" -version = "4.0.0-dev" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -18,10 +18,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" -jsonrpsee = { version = "0.16.2", features = ["server"] } -log = "0.4.17" +jsonrpsee = { version = "0.22", features = ["server"] } +log = { workspace = true, default-features = true } parking_lot = "0.12.1" -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } sc-block-builder = { path = "../block-builder" } sc-chain-spec = { path = "../chain-spec" } sc-client-api = { path = "../api" } @@ -40,7 +40,6 @@ sp-runtime = { path = "../../primitives/runtime" } sp-session = { path = "../../primitives/session" } sp-version = { path = "../../primitives/version" } sp-statement-store = { path = "../../primitives/statement-store" } - tokio = "1.22.0" [dev-dependencies] @@ -51,10 +50,12 @@ sc-network = { path = "../network" } sc-network-common = { path = "../network/common" } sc-transaction-pool = { path = "../transaction-pool" } sp-consensus = { path = "../../primitives/consensus/common" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } tokio = "1.22.0" sp-io = { path = "../../primitives/io" } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } pretty_assertions = "1.2.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } [features] test-helpers = [] diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs index 55d0a504aa678b82a3327683b5d417d61c8ad4c5..975f66406a6a30f56e26095ef13fc05fd8f52a9a 100644 --- a/substrate/client/rpc/src/author/mod.rs +++ b/substrate/client/rpc/src/author/mod.rs @@ -23,15 +23,14 @@ mod tests; use std::sync::Arc; -use crate::SubscriptionTaskExecutor; +use crate::{ + utils::{pipe_from_stream, spawn_subscription_task}, + SubscriptionTaskExecutor, +}; use codec::{Decode, Encode}; -use futures::{FutureExt, TryFutureExt}; -use jsonrpsee::{ - core::{async_trait, Error as JsonRpseeError, RpcResult}, - types::SubscriptionResult, - SubscriptionSink, -}; +use futures::TryFutureExt; +use jsonrpsee::{core::async_trait, types::ErrorObject, PendingSubscriptionSink}; use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::{ error::IntoPoolError, BlockHash, InPoolTransaction, TransactionFor, TransactionPool, @@ -91,7 +90,7 @@ where P::Hash: Unpin, ::Hash: Unpin, { - async fn submit_extrinsic(&self, ext: Bytes) -> RpcResult> { + async fn submit_extrinsic(&self, ext: Bytes) -> Result> { let xt = match Decode::decode(&mut &ext[..]) { Ok(xt) => xt, Err(err) => return Err(Error::Client(Box::new(err)).into()), @@ -105,7 +104,7 @@ where }) } - fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()> { + fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<()> { self.deny_unsafe.check_if_safe()?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; @@ -115,7 +114,7 @@ where Ok(()) } - fn rotate_keys(&self) -> RpcResult { + fn rotate_keys(&self) -> Result { self.deny_unsafe.check_if_safe()?; let best_block_hash = self.client.info().best_hash; @@ -129,7 +128,7 @@ where .map_err(|api_err| Error::Client(Box::new(api_err)).into()) } - fn has_session_keys(&self, session_keys: Bytes) -> RpcResult { + fn has_session_keys(&self, session_keys: Bytes) -> Result { self.deny_unsafe.check_if_safe()?; let best_block_hash = self.client.info().best_hash; @@ -143,21 +142,21 @@ where Ok(self.keystore.has_keys(&keys)) } - fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult { + fn has_key(&self, public_key: Bytes, key_type: String) -> Result { self.deny_unsafe.check_if_safe()?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; Ok(self.keystore.has_keys(&[(public_key.to_vec(), key_type)])) } - fn pending_extrinsics(&self) -> RpcResult> { + fn pending_extrinsics(&self) -> Result> { Ok(self.pool.ready().map(|tx| tx.data().encode().into()).collect()) } fn remove_extrinsic( &self, bytes_or_hash: Vec>>, - ) -> RpcResult>> { + ) -> Result>> { self.deny_unsafe.check_if_safe()?; let hashes = bytes_or_hash .into_iter() @@ -178,13 +177,13 @@ where .collect()) } - fn watch_extrinsic(&self, mut sink: SubscriptionSink, xt: Bytes) -> SubscriptionResult { + fn watch_extrinsic(&self, pending: PendingSubscriptionSink, xt: Bytes) { let best_block_hash = self.client.info().best_hash; let dxt = match TransactionFor::

::decode(&mut &xt[..]).map_err(|e| Error::from(e)) { Ok(dxt) => dxt, Err(e) => { - let _ = sink.reject(JsonRpseeError::from(e)); - return Ok(()) + spawn_subscription_task(&self.executor, pending.reject(e)); + return }, }; @@ -198,15 +197,14 @@ where let stream = match submit.await { Ok(stream) => stream, Err(err) => { - let _ = sink.reject(JsonRpseeError::from(err)); + let _ = pending.reject(ErrorObject::from(err)).await; return }, }; - sink.pipe_from_stream(stream).await; + pipe_from_stream(pending, stream).await; }; - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - Ok(()) + spawn_subscription_task(&self.executor, fut); } } diff --git a/substrate/client/rpc/src/author/tests.rs b/substrate/client/rpc/src/author/tests.rs index f48b2f9571428a8415e9a4a106704f2d662de88c..937870eb53fd9b2ec4800c8eca343bb111560eef 100644 --- a/substrate/client/rpc/src/author/tests.rs +++ b/substrate/client/rpc/src/author/tests.rs @@ -21,21 +21,17 @@ use super::*; use crate::testing::{test_executor, timeout_secs}; use assert_matches::assert_matches; use codec::Encode; -use jsonrpsee::{ - core::Error as RpcError, - types::{error::CallError, EmptyServerParams as EmptyParams}, - RpcModule, -}; +use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError, RpcModule}; use sc_transaction_pool::{BasicPool, FullChainApi}; use sc_transaction_pool_api::TransactionStatus; use sp_core::{ - blake2_256, bytes::to_hex, crypto::{ByteArray, Pair}, ed25519, testing::{ED25519, SR25519}, H256, }; +use sp_crypto_hashing::blake2_256; use sp_keystore::{testing::MemoryKeystore, Keystore}; use sp_runtime::Perbill; use std::sync::Arc; @@ -104,7 +100,7 @@ async fn author_submit_transaction_should_not_cause_error() { assert_matches!( api.call::<_, H256>("author_submitExtrinsic", [xt]).await, - Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Already Imported") && err.code() == 1013 + Err(RpcError::JsonRpc(err)) if err.message().contains("Already Imported") && err.code() == 1013 ); } @@ -119,7 +115,7 @@ async fn author_should_watch_extrinsic() { true, ); - let mut sub = api.subscribe("author_submitAndWatchExtrinsic", [xt]).await.unwrap(); + let mut sub = api.subscribe_unbounded("author_submitAndWatchExtrinsic", [xt]).await.unwrap(); let (tx, sub_id) = timeout_secs(10, sub.next::>()) .await .unwrap() @@ -157,11 +153,11 @@ async fn author_should_return_watch_validation_error() { let invalid_xt = ExtrinsicBuilder::new_fill_block(Perbill::from_percent(100)).build(); let api = TestSetup::into_rpc(); - let failed_sub = api.subscribe(METHOD, [to_hex(&invalid_xt.encode(), true)]).await; + let failed_sub = api.subscribe_unbounded(METHOD, [to_hex(&invalid_xt.encode(), true)]).await; assert_matches!( failed_sub, - Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Invalid Transaction") && err.code() == 1010 + Err(RpcError::JsonRpc(err)) if err.message().contains("Invalid Transaction") && err.code() == 1010 ); } @@ -277,7 +273,7 @@ async fn author_has_session_keys() { assert_matches!( api.call::<_, bool>("author_hasSessionKeys", vec![Bytes::from(vec![1, 2, 3])]).await, - Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Session keys are not encoded correctly") + Err(RpcError::JsonRpc(err)) if err.message().contains("Session keys are not encoded correctly") ); } diff --git a/substrate/client/rpc/src/chain/chain_full.rs b/substrate/client/rpc/src/chain/chain_full.rs index a88291eb7bd350763239a3dec7e0010fbc71c71e..515c0f62c8ad2699b81c411441e1f9a094aee881 100644 --- a/substrate/client/rpc/src/chain/chain_full.rs +++ b/substrate/client/rpc/src/chain/chain_full.rs @@ -19,14 +19,17 @@ //! Blockchain API backend for full nodes. use super::{client_err, ChainBackend, Error}; -use crate::SubscriptionTaskExecutor; +use crate::{ + utils::{pipe_from_stream, spawn_subscription_task}, + SubscriptionTaskExecutor, +}; use std::{marker::PhantomData, sync::Arc}; use futures::{ - future::{self, FutureExt}, + future::{self}, stream::{self, Stream, StreamExt}, }; -use jsonrpsee::SubscriptionSink; +use jsonrpsee::{core::async_trait, PendingSubscriptionSink}; use sc_client_api::{BlockBackend, BlockchainEvents}; use sp_blockchain::HeaderBackend; use sp_runtime::{generic::SignedBlock, traits::Block as BlockT}; @@ -48,6 +51,7 @@ impl FullChain { } } +#[async_trait] impl ChainBackend for FullChain where Block: BlockT + 'static, @@ -66,11 +70,11 @@ where self.client.block(self.unwrap_or_best(hash)).map_err(client_err) } - fn subscribe_all_heads(&self, sink: SubscriptionSink) { + fn subscribe_all_heads(&self, pending: PendingSubscriptionSink) { subscribe_headers( &self.client, &self.executor, - sink, + pending, || self.client().info().best_hash, || { self.client() @@ -80,11 +84,11 @@ where ) } - fn subscribe_new_heads(&self, sink: SubscriptionSink) { + fn subscribe_new_heads(&self, pending: PendingSubscriptionSink) { subscribe_headers( &self.client, &self.executor, - sink, + pending, || self.client().info().best_hash, || { self.client() @@ -95,11 +99,11 @@ where ) } - fn subscribe_finalized_heads(&self, sink: SubscriptionSink) { + fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink) { subscribe_headers( &self.client, &self.executor, - sink, + pending, || self.client().info().finalized_hash, || { self.client() @@ -114,7 +118,7 @@ where fn subscribe_headers( client: &Arc, executor: &SubscriptionTaskExecutor, - mut sink: SubscriptionSink, + pending: PendingSubscriptionSink, best_block_hash: G, stream: F, ) where @@ -139,9 +143,5 @@ fn subscribe_headers( // duplicates at the beginning of the stream though. let stream = stream::iter(maybe_header).chain(stream()); - let fut = async move { - sink.pipe_from_stream(stream).await; - }; - - executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + spawn_subscription_task(executor, pipe_from_stream(pending, stream)); } diff --git a/substrate/client/rpc/src/chain/mod.rs b/substrate/client/rpc/src/chain/mod.rs index 2b5994f217df3d9c6baea3b1364f7f6b62d4ad71..1c74db8642e624d36b71b868abb63a600f402e86 100644 --- a/substrate/client/rpc/src/chain/mod.rs +++ b/substrate/client/rpc/src/chain/mod.rs @@ -27,7 +27,7 @@ use std::sync::Arc; use crate::SubscriptionTaskExecutor; -use jsonrpsee::{core::RpcResult, types::SubscriptionResult, SubscriptionSink}; +use jsonrpsee::{core::async_trait, PendingSubscriptionSink}; use sc_client_api::BlockchainEvents; use sp_rpc::{list::ListOrValue, number::NumberOrHex}; use sp_runtime::{ @@ -42,6 +42,7 @@ pub use sc_rpc_api::chain::*; use sp_blockchain::HeaderBackend; /// Blockchain backend API +#[async_trait] trait ChainBackend: Send + Sync + 'static where Block: BlockT + 'static, @@ -91,13 +92,13 @@ where } /// All new head subscription - fn subscribe_all_heads(&self, sink: SubscriptionSink); + fn subscribe_all_heads(&self, pending: PendingSubscriptionSink); /// New best head subscription - fn subscribe_new_heads(&self, sink: SubscriptionSink); + fn subscribe_new_heads(&self, pending: PendingSubscriptionSink); /// Finalized head subscription - fn subscribe_finalized_heads(&self, sink: SubscriptionSink); + fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink); } /// Create new state API that works on full node. @@ -118,6 +119,7 @@ pub struct Chain { backend: Box>, } +#[async_trait] impl ChainApiServer, Block::Hash, Block::Header, SignedBlock> for Chain where @@ -125,20 +127,20 @@ where Block::Header: Unpin, Client: HeaderBackend + BlockchainEvents + 'static, { - fn header(&self, hash: Option) -> RpcResult> { - self.backend.header(hash).map_err(Into::into) + fn header(&self, hash: Option) -> Result, Error> { + self.backend.header(hash) } - fn block(&self, hash: Option) -> RpcResult>> { - self.backend.block(hash).map_err(Into::into) + fn block(&self, hash: Option) -> Result>, Error> { + self.backend.block(hash) } fn block_hash( &self, number: Option>, - ) -> RpcResult>> { + ) -> Result>, Error> { match number { - None => self.backend.block_hash(None).map(ListOrValue::Value).map_err(Into::into), + None => self.backend.block_hash(None).map(ListOrValue::Value), Some(ListOrValue::Value(number)) => self .backend .block_hash(Some(number)) @@ -152,23 +154,20 @@ where } } - fn finalized_head(&self) -> RpcResult { - self.backend.finalized_head().map_err(Into::into) + fn finalized_head(&self) -> Result { + self.backend.finalized_head() } - fn subscribe_all_heads(&self, sink: SubscriptionSink) -> SubscriptionResult { - self.backend.subscribe_all_heads(sink); - Ok(()) + fn subscribe_all_heads(&self, pending: PendingSubscriptionSink) { + self.backend.subscribe_all_heads(pending); } - fn subscribe_new_heads(&self, sink: SubscriptionSink) -> SubscriptionResult { - self.backend.subscribe_new_heads(sink); - Ok(()) + fn subscribe_new_heads(&self, pending: PendingSubscriptionSink) { + self.backend.subscribe_new_heads(pending) } - fn subscribe_finalized_heads(&self, sink: SubscriptionSink) -> SubscriptionResult { - self.backend.subscribe_finalized_heads(sink); - Ok(()) + fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink) { + self.backend.subscribe_finalized_heads(pending) } } diff --git a/substrate/client/rpc/src/chain/tests.rs b/substrate/client/rpc/src/chain/tests.rs index cff5bf39811c7dd20d553b60af59d9fef5270f36..afb81f709f7d38b60ddc40c5cee6c2acaf642af5 100644 --- a/substrate/client/rpc/src/chain/tests.rs +++ b/substrate/client/rpc/src/chain/tests.rs @@ -19,7 +19,7 @@ use super::*; use crate::testing::{test_executor, timeout_secs}; use assert_matches::assert_matches; -use jsonrpsee::types::EmptyServerParams as EmptyParams; +use jsonrpsee::core::EmptyServerParams as EmptyParams; use sc_block_builder::BlockBuilderBuilder; use sp_consensus::BlockOrigin; use sp_rpc::list::ListOrValue; @@ -252,7 +252,7 @@ async fn test_head_subscription(method: &str) { let mut sub = { let api = new_full(client.clone(), test_executor()).into_rpc(); - let sub = api.subscribe(method, EmptyParams::new()).await.unwrap(); + let sub = api.subscribe_unbounded(method, EmptyParams::new()).await.unwrap(); 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/rpc/src/dev/mod.rs b/substrate/client/rpc/src/dev/mod.rs index 4d2e8618d553ac27d78b0437f8f1ccfa0af86902..424ef72b694e23210cd21df20b78c0aae2433010 100644 --- a/substrate/client/rpc/src/dev/mod.rs +++ b/substrate/client/rpc/src/dev/mod.rs @@ -22,7 +22,6 @@ #[cfg(test)] mod tests; -use jsonrpsee::core::RpcResult; use sc_client_api::{BlockBackend, HeaderBackend}; use sc_rpc_api::{dev::error::Error, DenyUnsafe}; use sp_api::{ApiExt, Core, ProvideRuntimeApi}; @@ -65,7 +64,7 @@ where + 'static, Client::Api: Core, { - fn block_stats(&self, hash: Block::Hash) -> RpcResult> { + fn block_stats(&self, hash: Block::Hash) -> Result, Error> { self.deny_unsafe.check_if_safe()?; let block = { diff --git a/substrate/client/rpc/src/dev/tests.rs b/substrate/client/rpc/src/dev/tests.rs index 448d16274f27e27e5a23b68a0770bcfe8414b7fa..e8f9ba4990d255789cda8c92c17f71cca924399f 100644 --- a/substrate/client/rpc/src/dev/tests.rs +++ b/substrate/client/rpc/src/dev/tests.rs @@ -97,10 +97,10 @@ async fn deny_unsafe_works() { "{{\"jsonrpc\":\"2.0\",\"method\":\"dev_getBlockStats\",\"params\":[{}],\"id\":1}}", best_hash_param ); - let (resp, _) = api.raw_json_request(&request).await.expect("Raw calls should succeed"); + let (resp, _) = api.raw_json_request(&request, 1).await.expect("Raw calls should succeed"); assert_eq!( - resp.result, + resp, r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"# ); } diff --git a/substrate/client/rpc/src/lib.rs b/substrate/client/rpc/src/lib.rs index 94fdb2d734ff01f5876ac92271f99b168d9c4ce3..b40d0341e3212e6955d4d3f37db16f9c444626a3 100644 --- a/substrate/client/rpc/src/lib.rs +++ b/substrate/client/rpc/src/lib.rs @@ -39,6 +39,7 @@ pub mod offchain; pub mod state; pub mod statement; pub mod system; +pub mod utils; #[cfg(any(test, feature = "test-helpers"))] pub mod testing; diff --git a/substrate/client/rpc/src/mixnet/mod.rs b/substrate/client/rpc/src/mixnet/mod.rs index 3f3d9c5aa4526e399a3263eb02624738f7233ebe..64875c6fe7646fc17364df4ef2f44e924a595b0a 100644 --- a/substrate/client/rpc/src/mixnet/mod.rs +++ b/substrate/client/rpc/src/mixnet/mod.rs @@ -18,7 +18,7 @@ //! Substrate mixnet API. -use jsonrpsee::core::{async_trait, RpcResult}; +use jsonrpsee::core::async_trait; use sc_mixnet::Api; use sc_rpc_api::mixnet::error::Error; pub use sc_rpc_api::mixnet::MixnetApiServer; @@ -36,7 +36,7 @@ impl Mixnet { #[async_trait] impl MixnetApiServer for Mixnet { - async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<()> { + async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<(), Error> { // We only hold the lock while pushing the request into the requests channel let fut = { let mut api = self.0.lock().await; diff --git a/substrate/client/rpc/src/offchain/mod.rs b/substrate/client/rpc/src/offchain/mod.rs index de711accdcc12083a2d37b5c2949fddb5fa0be66..66167386605305d2fa93fa877ab8f2c831aa7172 100644 --- a/substrate/client/rpc/src/offchain/mod.rs +++ b/substrate/client/rpc/src/offchain/mod.rs @@ -22,7 +22,7 @@ mod tests; use self::error::Error; -use jsonrpsee::core::{async_trait, Error as JsonRpseeError, RpcResult}; +use jsonrpsee::core::async_trait; use parking_lot::RwLock; /// Re-export the API for backward compatibility. pub use sc_rpc_api::offchain::*; @@ -50,23 +50,23 @@ impl Offchain { #[async_trait] impl OffchainApiServer for Offchain { - fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> RpcResult<()> { + fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error> { self.deny_unsafe.check_if_safe()?; let prefix = match kind { StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, - StorageKind::LOCAL => return Err(JsonRpseeError::from(Error::UnavailableStorageKind)), + StorageKind::LOCAL => return Err(Error::UnavailableStorageKind), }; self.storage.write().set(prefix, &key, &value); Ok(()) } - fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> RpcResult> { + fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result, Error> { self.deny_unsafe.check_if_safe()?; let prefix = match kind { StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, - StorageKind::LOCAL => return Err(JsonRpseeError::from(Error::UnavailableStorageKind)), + StorageKind::LOCAL => return Err(Error::UnavailableStorageKind), }; Ok(self.storage.read().get(prefix, &key).map(Into::into)) diff --git a/substrate/client/rpc/src/offchain/tests.rs b/substrate/client/rpc/src/offchain/tests.rs index 62a846d0887cd4e78f2312b5ffed18ea3c215955..7758fac7fabdbb8168d18f66fe93c9bf801f7bba 100644 --- a/substrate/client/rpc/src/offchain/tests.rs +++ b/substrate/client/rpc/src/offchain/tests.rs @@ -39,7 +39,6 @@ fn local_storage_should_work() { #[test] fn offchain_calls_considered_unsafe() { - use jsonrpsee::types::error::CallError; let storage = InMemOffchainStorage::default(); let offchain = Offchain::new(storage, DenyUnsafe::Yes); let key = Bytes(b"offchain_storage".to_vec()); @@ -47,14 +46,14 @@ fn offchain_calls_considered_unsafe() { assert_matches!( offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()), - Err(JsonRpseeError::Call(CallError::Custom(err))) => { - assert_eq!(err.message(), "RPC call is unsafe to be called externally") + 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), - Err(JsonRpseeError::Call(CallError::Custom(err))) => { - assert_eq!(err.message(), "RPC call is unsafe to be called externally") + 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 057661d6ec7f56336ade8c2c9fe2da3186d5e661..c9a41e25eda8bec3d9b2e7868248699bb2ae984c 100644 --- a/substrate/client/rpc/src/state/mod.rs +++ b/substrate/client/rpc/src/state/mod.rs @@ -24,32 +24,23 @@ mod utils; #[cfg(test)] mod tests; -use std::sync::Arc; - use crate::SubscriptionTaskExecutor; - -use jsonrpsee::{ - core::{async_trait, server::rpc_module::SubscriptionSink, Error as JsonRpseeError, RpcResult}, - types::SubscriptionResult, +use jsonrpsee::{core::async_trait, PendingSubscriptionSink}; +use sc_client_api::{ + Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider, }; - use sc_rpc_api::DenyUnsafe; +use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi}; +use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_core::{ storage::{PrefixedStorageKey, StorageChangeSet, StorageData, StorageKey}, Bytes, }; use sp_runtime::traits::Block as BlockT; use sp_version::RuntimeVersion; +use std::sync::Arc; -use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi}; - -use self::error::Error; - -use sc_client_api::{ - Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider, -}; pub use sc_rpc_api::{child_state::*, state::*}; -use sp_blockchain::{HeaderBackend, HeaderMetadata}; const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000; @@ -158,10 +149,15 @@ where ) -> Result; /// New runtime version subscription - fn subscribe_runtime_version(&self, sink: SubscriptionSink); + fn subscribe_runtime_version(&self, pending: PendingSubscriptionSink); /// New storage subscription - fn subscribe_storage(&self, sink: SubscriptionSink, keys: Option>); + fn subscribe_storage( + &self, + pending: PendingSubscriptionSink, + keys: Option>, + deny_unsafe: DenyUnsafe, + ); } /// Create new state API that works on full node. @@ -207,7 +203,12 @@ where Block: BlockT + 'static, Client: Send + Sync + 'static, { - fn call(&self, method: String, data: Bytes, block: Option) -> RpcResult { + fn call( + &self, + method: String, + data: Bytes, + block: Option, + ) -> Result { self.backend.call(block, method, data).map_err(Into::into) } @@ -215,7 +216,7 @@ where &self, key_prefix: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.storage_keys(block, key_prefix).map_err(Into::into) } @@ -223,7 +224,7 @@ where &self, key_prefix: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.deny_unsafe.check_if_safe()?; self.backend.storage_pairs(block, key_prefix).map_err(Into::into) } @@ -234,12 +235,9 @@ where count: u32, start_key: Option, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { if count > STORAGE_KEYS_PAGED_MAX_COUNT { - return Err(JsonRpseeError::from(Error::InvalidCount { - value: count, - max: STORAGE_KEYS_PAGED_MAX_COUNT, - })) + return Err(Error::InvalidCount { value: count, max: STORAGE_KEYS_PAGED_MAX_COUNT }) } self.backend .storage_keys_paged(block, prefix, count, start_key) @@ -250,7 +248,7 @@ where &self, key: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.storage(block, key).map_err(Into::into) } @@ -258,7 +256,7 @@ where &self, key: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.storage_hash(block, key).map_err(Into::into) } @@ -266,18 +264,18 @@ where &self, key: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend .storage_size(block, key, self.deny_unsafe) .await .map_err(Into::into) } - fn metadata(&self, block: Option) -> RpcResult { + fn metadata(&self, block: Option) -> Result { self.backend.metadata(block).map_err(Into::into) } - fn runtime_version(&self, at: Option) -> RpcResult { + fn runtime_version(&self, at: Option) -> Result { self.backend.runtime_version(at).map_err(Into::into) } @@ -286,7 +284,7 @@ where keys: Vec, from: Block::Hash, to: Option, - ) -> RpcResult>> { + ) -> Result>, Error> { self.deny_unsafe.check_if_safe()?; self.backend.query_storage(from, to, keys).map_err(Into::into) } @@ -295,7 +293,7 @@ where &self, keys: Vec, at: Option, - ) -> RpcResult>> { + ) -> Result>, Error> { self.backend.query_storage_at(keys, at).map_err(Into::into) } @@ -303,7 +301,7 @@ where &self, keys: Vec, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.read_proof(block, keys).map_err(Into::into) } @@ -318,32 +316,19 @@ where targets: Option, storage_keys: Option, methods: Option, - ) -> RpcResult { + ) -> Result { self.deny_unsafe.check_if_safe()?; self.backend .trace_block(block, targets, storage_keys, methods) .map_err(Into::into) } - fn subscribe_runtime_version(&self, sink: SubscriptionSink) -> SubscriptionResult { - self.backend.subscribe_runtime_version(sink); - Ok(()) + fn subscribe_runtime_version(&self, pending: PendingSubscriptionSink) { + self.backend.subscribe_runtime_version(pending) } - fn subscribe_storage( - &self, - mut sink: SubscriptionSink, - keys: Option>, - ) -> SubscriptionResult { - if keys.is_none() { - if let Err(err) = self.deny_unsafe.check_if_safe() { - let _ = sink.reject(JsonRpseeError::from(err)); - return Ok(()) - } - } - - self.backend.subscribe_storage(sink, keys); - Ok(()) + fn subscribe_storage(&self, pending: PendingSubscriptionSink, keys: Option>) { + self.backend.subscribe_storage(pending, keys, self.deny_unsafe) } } @@ -430,7 +415,7 @@ where storage_key: PrefixedStorageKey, key_prefix: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.storage_keys(block, storage_key, key_prefix).map_err(Into::into) } @@ -441,7 +426,7 @@ where count: u32, start_key: Option, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend .storage_keys_paged(block, storage_key, prefix, count, start_key) .map_err(Into::into) @@ -452,7 +437,7 @@ where storage_key: PrefixedStorageKey, key: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.storage(block, storage_key, key).map_err(Into::into) } @@ -461,7 +446,7 @@ where storage_key: PrefixedStorageKey, keys: Vec, block: Option, - ) -> RpcResult>> { + ) -> Result>, Error> { self.backend.storage_entries(block, storage_key, keys).map_err(Into::into) } @@ -470,7 +455,7 @@ where storage_key: PrefixedStorageKey, key: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.storage_hash(block, storage_key, key).map_err(Into::into) } @@ -479,7 +464,7 @@ where storage_key: PrefixedStorageKey, key: StorageKey, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend.storage_size(block, storage_key, key).map_err(Into::into) } @@ -488,7 +473,7 @@ where child_storage_key: PrefixedStorageKey, keys: Vec, block: Option, - ) -> RpcResult> { + ) -> Result, Error> { self.backend .read_child_proof(block, child_storage_key, keys) .map_err(Into::into) diff --git a/substrate/client/rpc/src/state/state_full.rs b/substrate/client/rpc/src/state/state_full.rs index 9604d9165f987aed66474ca58fcf31f3dcd5c549..bda678c1b45ef104e1d8be794ac794f1f8254640 100644 --- a/substrate/client/rpc/src/state/state_full.rs +++ b/substrate/client/rpc/src/state/state_full.rs @@ -25,13 +25,13 @@ use super::{ error::{Error, Result}, ChildStateBackend, StateBackend, }; -use crate::{DenyUnsafe, SubscriptionTaskExecutor}; - -use futures::{future, stream, FutureExt, StreamExt}; -use jsonrpsee::{ - core::{async_trait, Error as JsonRpseeError}, - SubscriptionSink, +use crate::{ + utils::{pipe_from_stream, spawn_subscription_task}, + DenyUnsafe, SubscriptionTaskExecutor, }; + +use futures::{future, stream, StreamExt}; +use jsonrpsee::{core::async_trait, types::ErrorObject, PendingSubscriptionSink}; use sc_client_api::{ Backend, BlockBackend, BlockchainEvents, CallExecutor, ExecutorProvider, ProofProvider, StorageProvider, @@ -371,9 +371,7 @@ where .map_err(client_err) } - fn subscribe_runtime_version(&self, mut sink: SubscriptionSink) { - let client = self.client.clone(); - + fn subscribe_runtime_version(&self, pending: PendingSubscriptionSink) { let initial = match self .block_or_best(None) .and_then(|block| self.client.runtime_version_at(block).map_err(Into::into)) @@ -381,12 +379,13 @@ where { Ok(initial) => initial, Err(e) => { - let _ = sink.reject(JsonRpseeError::from(e)); + spawn_subscription_task(&self.executor, pending.reject(e)); return }, }; let mut previous_version = initial.clone(); + let client = self.client.clone(); // A stream of new versions let version_stream = client @@ -406,24 +405,33 @@ where }); let stream = futures::stream::once(future::ready(initial)).chain(version_stream); - - let fut = async move { - sink.pipe_from_stream(stream).await; - }; - - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); } - fn subscribe_storage(&self, mut sink: SubscriptionSink, keys: Option>) { + fn subscribe_storage( + &self, + pending: PendingSubscriptionSink, + keys: Option>, + deny_unsafe: DenyUnsafe, + ) { + if keys.is_none() { + if let Err(err) = deny_unsafe.check_if_safe() { + spawn_subscription_task(&self.executor, pending.reject(ErrorObject::from(err))); + return + } + } + let stream = match self.client.storage_changes_notification_stream(keys.as_deref(), None) { Ok(stream) => stream, Err(blockchain_err) => { - let _ = sink.reject(JsonRpseeError::from(Error::Client(Box::new(blockchain_err)))); + spawn_subscription_task( + &self.executor, + pending.reject(Error::Client(Box::new(blockchain_err))), + ); return }, }; - // initial values let initial = stream::iter(keys.map(|keys| { let block = self.client.info().best_hash; let changes = keys @@ -436,7 +444,6 @@ where StorageChangeSet { block, changes } })); - // let storage_stream = stream.map(|(block, changes)| StorageChangeSet { let storage_stream = stream.map(|storage_notif| StorageChangeSet { block: storage_notif.block, changes: storage_notif @@ -450,11 +457,7 @@ where .chain(storage_stream) .filter(|storage| future::ready(!storage.changes.is_empty())); - let fut = async move { - sink.pipe_from_stream(stream).await; - }; - - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + spawn_subscription_task(&self.executor, pipe_from_stream(pending, stream)); } fn trace_block( diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index 594f51efba686695b6f99b412dbc8caa62ce6750..dd866e671c5095dca9b5851111340a4e32f71e67 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -21,10 +21,7 @@ use super::*; use crate::testing::{test_executor, timeout_secs}; use assert_matches::assert_matches; use futures::executor; -use jsonrpsee::{ - core::Error as RpcError, - types::{error::CallError as RpcCallError, EmptyServerParams as EmptyParams, ErrorObject}, -}; +use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError}; use sc_block_builder::BlockBuilderBuilder; use sc_rpc_api::DenyUnsafe; use sp_consensus::BlockOrigin; @@ -42,6 +39,14 @@ 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"; @@ -200,22 +205,25 @@ async fn should_call_contract() { let genesis_hash = client.genesis_hash(); let (client, _child) = new_full(client, test_executor(), DenyUnsafe::No); - use jsonrpsee::{core::Error, types::error::CallError}; - assert_matches!( client.call("balanceOf".into(), Bytes(vec![1, 2, 3]), Some(genesis_hash).into()), - Err(Error::Call(CallError::Failed(_))) + Err(Error::Client(_)) ) } #[tokio::test] async fn should_notify_about_storage_changes() { + init_logger(); + let mut sub = { let mut 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(); - let sub = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await.unwrap(); + let sub = api_rpc + .subscribe_unbounded("state_subscribeStorage", EmptyParams::new()) + .await + .unwrap(); // Cause a change: let mut builder = BlockBuilderBuilder::new(&*client) @@ -241,19 +249,20 @@ async fn should_notify_about_storage_changes() { // NOTE: previous versions of the subscription code used to return an empty value for the // "initial" storage change here assert_matches!(timeout_secs(1, sub.next::>()).await, Ok(Some(_))); - assert_matches!(timeout_secs(1, sub.next::>()).await, Ok(None)); } #[tokio::test] 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 (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let alice_balance_key = [ - sp_core::hashing::twox_128(b"System"), - sp_core::hashing::twox_128(b"Account"), - sp_core::hashing::blake2_128(&AccountKeyring::Alice.public()), + sp_crypto_hashing::twox_128(b"System"), + sp_crypto_hashing::twox_128(b"Account"), + sp_crypto_hashing::blake2_128(&AccountKeyring::Alice.public()), ] .concat() .iter() @@ -263,7 +272,10 @@ async fn should_send_initial_storage_changes_and_notifications() { let api_rpc = api.into_rpc(); let sub = api_rpc - .subscribe("state_subscribeStorage", [[StorageKey(alice_balance_key)]]) + .subscribe_unbounded( + "state_subscribeStorage", + [[StorageKey(alice_balance_key.to_vec())]], + ) .await .unwrap(); @@ -288,9 +300,6 @@ async fn should_send_initial_storage_changes_and_notifications() { assert_matches!(timeout_secs(1, sub.next::>()).await, Ok(Some(_))); assert_matches!(timeout_secs(1, sub.next::>()).await, Ok(Some(_))); - - // No more messages to follow - assert_matches!(timeout_secs(1, sub.next::>()).await, Ok(None)); } #[tokio::test] @@ -393,108 +402,48 @@ async fn should_query_storage() { assert_eq!(result.unwrap(), expected); // Inverted range. - let result = api.query_storage(keys.clone(), block1_hash, Some(genesis_hash)); - - assert_eq!( - result.map_err(|e| e.to_string()), - Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned( - 4001, - Error::InvalidBlockRange { - from: format!("1 ({:?})", block1_hash), - to: format!("0 ({:?})", genesis_hash), - details: "from number > to number".to_owned(), - } - .to_string(), - None::<()>, - )))) - .map_err(|e| e.to_string()) + assert_matches!( + api.query_storage(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() ); let random_hash1 = H256::random(); let random_hash2 = H256::random(); // Invalid second hash. - let result = api.query_storage(keys.clone(), genesis_hash, Some(random_hash1)); - - assert_eq!( - result.map_err(|e| e.to_string()), - Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned( - 4001, - Error::InvalidBlockRange { - from: format!("{:?}", genesis_hash), - to: format!("{:?}", Some(random_hash1)), - details: format!( - "UnknownBlock: Header was not found in the database: {:?}", - random_hash1 - ), - } - .to_string(), - None::<()>, - )))) - .map_err(|e| e.to_string()) + assert_matches!( + api.query_storage(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 + ) ); // Invalid first hash with Some other hash. - let result = api.query_storage(keys.clone(), random_hash1, Some(genesis_hash)); - - assert_eq!( - result.map_err(|e| e.to_string()), - Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned( - 4001, - Error::InvalidBlockRange { - from: format!("{:?}", random_hash1), - to: format!("{:?}", Some(genesis_hash)), - details: format!( - "UnknownBlock: Header was not found in the database: {:?}", - random_hash1 - ), - } - .to_string(), - None::<()>, - )))) - .map_err(|e| e.to_string()), + assert_matches!( + api.query_storage(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 + ) ); // Invalid first hash with None. - let result = api.query_storage(keys.clone(), random_hash1, None); - - assert_eq!( - result.map_err(|e| e.to_string()), - Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned( - 4001, - Error::InvalidBlockRange { - from: format!("{:?}", random_hash1), - to: format!("{:?}", Some(block2_hash)), // Best block hash. - details: format!( - "UnknownBlock: Header was not found in the database: {:?}", - random_hash1 - ), - } - .to_string(), - None::<()>, - )))) - .map_err(|e| e.to_string()), + assert_matches!( + api.query_storage(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 + ) ); // Both hashes invalid. - let result = api.query_storage(keys.clone(), random_hash1, Some(random_hash2)); - - assert_eq!( - result.map_err(|e| e.to_string()), - Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned( - 4001, - Error::InvalidBlockRange { - from: format!("{:?}", random_hash1), // First hash not found. - to: format!("{:?}", Some(random_hash2)), - details: format!( - "UnknownBlock: Header was not found in the database: {:?}", - random_hash1 - ), - } - .to_string(), - None::<()> - )))) - .map_err(|e| e.to_string()), + assert_matches!( + api.query_storage(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 + ) ); // single block range @@ -526,7 +475,7 @@ async fn should_return_runtime_version() { // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ - \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\ + \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",5],\ [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ @@ -548,7 +497,7 @@ async fn should_notify_on_runtime_version_initially() { let api_rpc = api.into_rpc(); let sub = api_rpc - .subscribe("state_subscribeRuntimeVersion", EmptyParams::new()) + .subscribe_unbounded("state_subscribeRuntimeVersion", EmptyParams::new()) .await .unwrap(); @@ -557,9 +506,6 @@ async fn should_notify_on_runtime_version_initially() { // assert initial version sent. assert_matches!(timeout_secs(10, sub.next::()).await, Ok(Some(_))); - - sub.close(); - assert_matches!(timeout_secs(10, sub.next::()).await, Ok(None)); } #[test] @@ -572,12 +518,14 @@ 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_rpc = api.into_rpc(); - let err = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await; - assert_matches!(err, Err(RpcError::Call(RpcCallError::Custom(e))) if e.message() == "RPC call is unsafe to be called externally"); + 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"); } #[tokio::test] @@ -587,7 +535,7 @@ async fn concrete_storage_subscriptions_are_rpc_safe() { let api_rpc = api.into_rpc(); let key = StorageKey(STORAGE_KEY.to_vec()); - let sub = api_rpc.subscribe("state_subscribeStorage", [[key]]).await; + let sub = api_rpc.subscribe_unbounded("state_subscribeStorage", [[key]]).await; assert!(sub.is_ok()); } diff --git a/substrate/client/rpc/src/system/mod.rs b/substrate/client/rpc/src/system/mod.rs index 0da4f8d0e211c729a8ebe7eaaac7d6bb735bd5df..8c7510c64cb5199a1ef9ac6ecf7bfbc9a570d368 100644 --- a/substrate/client/rpc/src/system/mod.rs +++ b/substrate/client/rpc/src/system/mod.rs @@ -22,17 +22,12 @@ mod tests; use futures::channel::oneshot; -use jsonrpsee::{ - core::{async_trait, error::Error as JsonRpseeError, JsonValue, RpcResult}, - types::error::{CallError, ErrorCode, ErrorObject}, -}; +use jsonrpsee::core::{async_trait, JsonValue}; use sc_rpc_api::DenyUnsafe; use sc_tracing::logging; use sc_utils::mpsc::TracingUnboundedSender; use sp_runtime::traits::{self, Header as HeaderT}; -use self::error::Result; - pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo}; pub use sc_rpc_api::system::*; @@ -57,9 +52,9 @@ pub enum Request { /// Must return the state of the network. NetworkState(oneshot::Sender), /// Must return any potential parse error. - NetworkAddReservedPeer(String, oneshot::Sender>), + NetworkAddReservedPeer(String, oneshot::Sender>), /// Must return any potential parse error. - NetworkRemoveReservedPeer(String, oneshot::Sender>), + NetworkRemoveReservedPeer(String, oneshot::Sender>), /// Must return the list of reserved peers NetworkReservedPeers(oneshot::Sender>), /// Must return the node role. @@ -84,121 +79,109 @@ impl System { #[async_trait] impl SystemApiServer::Number> for System { - fn system_name(&self) -> RpcResult { + fn system_name(&self) -> Result { Ok(self.info.impl_name.clone()) } - fn system_version(&self) -> RpcResult { + fn system_version(&self) -> Result { Ok(self.info.impl_version.clone()) } - fn system_chain(&self) -> RpcResult { + fn system_chain(&self) -> Result { Ok(self.info.chain_name.clone()) } - fn system_type(&self) -> RpcResult { + fn system_type(&self) -> Result { Ok(self.info.chain_type.clone()) } - fn system_properties(&self) -> RpcResult { + fn system_properties(&self) -> Result { Ok(self.info.properties.clone()) } - async fn system_health(&self) -> RpcResult { + async fn system_health(&self) -> Result { let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::Health(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_local_peer_id(&self) -> RpcResult { + async fn system_local_peer_id(&self) -> Result { let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::LocalPeerId(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_local_listen_addresses(&self) -> RpcResult> { + async fn system_local_listen_addresses(&self) -> Result, Error> { let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::LocalListenAddresses(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } async fn system_peers( &self, - ) -> RpcResult::Number>>> { + ) -> Result::Number>>, Error> { self.deny_unsafe.check_if_safe()?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::Peers(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_network_state(&self) -> RpcResult { + async fn system_network_state(&self) -> Result { self.deny_unsafe.check_if_safe()?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkState(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_add_reserved_peer(&self, peer: String) -> RpcResult<()> { + async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error> { self.deny_unsafe.check_if_safe()?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkAddReservedPeer(peer, tx)); match rx.await { Ok(Ok(())) => Ok(()), - Ok(Err(e)) => Err(JsonRpseeError::from(e)), - Err(e) => Err(JsonRpseeError::to_call_error(e)), + Ok(Err(e)) => Err(e), + Err(e) => Err(Error::Internal(e.to_string())), } } - async fn system_remove_reserved_peer(&self, peer: String) -> RpcResult<()> { + async fn system_remove_reserved_peer(&self, peer: String) -> Result<(), Error> { self.deny_unsafe.check_if_safe()?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkRemoveReservedPeer(peer, tx)); match rx.await { Ok(Ok(())) => Ok(()), - Ok(Err(e)) => Err(JsonRpseeError::from(e)), - Err(e) => Err(JsonRpseeError::to_call_error(e)), + Ok(Err(e)) => Err(e), + Err(e) => Err(Error::Internal(e.to_string())), } } - async fn system_reserved_peers(&self) -> RpcResult> { + async fn system_reserved_peers(&self) -> Result, Error> { let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkReservedPeers(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_node_roles(&self) -> RpcResult> { + async fn system_node_roles(&self) -> Result, Error> { let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NodeRoles(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_sync_state(&self) -> RpcResult::Number>> { + async fn system_sync_state(&self) -> Result::Number>, Error> { let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::SyncState(tx)); - rx.await.map_err(|e| JsonRpseeError::to_call_error(e)) + rx.await.map_err(|e| Error::Internal(e.to_string())) } - fn system_add_log_filter(&self, directives: String) -> RpcResult<()> { + fn system_add_log_filter(&self, directives: String) -> Result<(), Error> { self.deny_unsafe.check_if_safe()?; logging::add_directives(&directives); - logging::reload_filter().map_err(|e| { - JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( - ErrorCode::InternalError.code(), - e, - None::<()>, - ))) - }) + logging::reload_filter().map_err(|e| Error::Internal(e)) } - fn system_reset_log_filter(&self) -> RpcResult<()> { + fn system_reset_log_filter(&self) -> Result<(), Error> { self.deny_unsafe.check_if_safe()?; - logging::reset_log_filter().map_err(|e| { - JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( - ErrorCode::InternalError.code(), - e, - None::<()>, - ))) - }) + 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 b6bfec7736b088f8b42192de10bc07614fb5bd86..03967c63523c79610009b0edb4ac4e6618b99040 100644 --- a/substrate/client/rpc/src/system/tests.rs +++ b/substrate/client/rpc/src/system/tests.rs @@ -19,11 +19,7 @@ use super::{helpers::SyncState, *}; use assert_matches::assert_matches; use futures::prelude::*; -use jsonrpsee::{ - core::Error as RpcError, - types::{error::CallError, EmptyServerParams as EmptyParams}, - RpcModule, -}; +use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError, RpcModule}; use sc_network::{self, config::Role, PeerId}; use sc_rpc_api::system::helpers::PeerInfo; use sc_utils::mpsc::tracing_unbounded; @@ -312,7 +308,7 @@ async fn system_network_add_reserved() { let bad_peer_id = ["/ip4/198.51.100.19/tcp/30333"]; assert_matches!( api(None).call::<_, ()>("system_addReservedPeer", bad_peer_id).await, - Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Peer id is missing from the address") + Err(RpcError::JsonRpc(err)) if err.message().contains("Peer id is missing from the address") ); } @@ -328,7 +324,7 @@ async fn system_network_remove_reserved() { assert_matches!( api(None).call::<_, String>("system_removeReservedPeer", bad_peer_id).await, - Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("base-58 decode error: provided string contained invalid character '/' at byte 0") + Err(RpcError::JsonRpc(err)) if err.message().contains("base-58 decode error: provided string contained invalid character '/' at byte 0") ); } #[tokio::test] diff --git a/substrate/client/rpc/src/testing.rs b/substrate/client/rpc/src/testing.rs index 6b6e1906d44d1f259016b0d98725ded2645e7a70..e04f80a7b9e61d8fc0614e284f4dbab8a7f7dbac 100644 --- a/substrate/client/rpc/src/testing.rs +++ b/substrate/client/rpc/src/testing.rs @@ -20,11 +20,50 @@ use std::{future::Future, sync::Arc}; -use sp_core::testing::TaskExecutor; +/// A task executor that can be used for running RPC tests. +/// +/// Warning: the tokio runtime must be initialized before calling this. +#[derive(Clone)] +pub struct TokioTestExecutor(tokio::runtime::Handle); + +impl TokioTestExecutor { + /// Create a new instance of `Self`. + pub fn new() -> Self { + Self(tokio::runtime::Handle::current()) + } +} + +impl Default for TokioTestExecutor { + fn default() -> Self { + Self::new() + } +} + +impl sp_core::traits::SpawnNamed for TokioTestExecutor { + fn spawn_blocking( + &self, + _name: &'static str, + _group: Option<&'static str>, + future: futures::future::BoxFuture<'static, ()>, + ) { + let handle = self.0.clone(); + self.0.spawn_blocking(move || { + handle.block_on(future); + }); + } + fn spawn( + &self, + _name: &'static str, + _group: Option<&'static str>, + future: futures::future::BoxFuture<'static, ()>, + ) { + self.0.spawn(future); + } +} /// Executor for testing. -pub fn test_executor() -> Arc { - Arc::new(TaskExecutor::default()) +pub fn test_executor() -> Arc { + Arc::new(TokioTestExecutor::default()) } /// Wrap a future in a timeout a little more concisely diff --git a/substrate/client/rpc/src/utils.rs b/substrate/client/rpc/src/utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..6ec48efef846c719db4e009b3a14926db0577d5f --- /dev/null +++ b/substrate/client/rpc/src/utils.rs @@ -0,0 +1,234 @@ +// 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 . + +//! JSON-RPC helpers. + +use crate::SubscriptionTaskExecutor; +use futures::{ + future::{self, Either, Fuse, FusedFuture}, + Future, FutureExt, Stream, StreamExt, +}; +use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage, SubscriptionSink}; +use sp_runtime::Serialize; +use std::collections::VecDeque; + +const DEFAULT_BUF_SIZE: usize = 16; + +/// A simple bounded VecDeque. +struct BoundedVecDeque { + inner: VecDeque, + max_cap: usize, +} + +impl BoundedVecDeque { + /// Create a new bounded VecDeque. + fn new() -> Self { + Self { inner: VecDeque::with_capacity(DEFAULT_BUF_SIZE), max_cap: DEFAULT_BUF_SIZE } + } + + fn push_back(&mut self, item: T) -> Result<(), ()> { + if self.inner.len() >= self.max_cap { + Err(()) + } else { + self.inner.push_back(item); + Ok(()) + } + } + + fn pop_front(&mut self) -> Option { + self.inner.pop_front() + } +} + +/// Feed items to the subscription from the underlying stream. +/// +/// This is bounded because the underlying streams in substrate are +/// unbounded and if the subscription can't keep with stream it can +/// cause the buffer to become very large and consume lots of memory. +/// +/// In such cases the subscription is dropped. +pub async fn pipe_from_stream(pending: PendingSubscriptionSink, mut stream: S) +where + S: Stream + Unpin + Send + 'static, + T: Serialize + Send + 'static, +{ + let mut buf = BoundedVecDeque::new(); + let accept_fut = pending.accept(); + + futures::pin_mut!(accept_fut); + + // Poll the stream while waiting for the subscription to be accepted + // + // If the `max_cap` is exceeded then the subscription is dropped. + let sink = loop { + match future::select(accept_fut, stream.next()).await { + Either::Left((Ok(sink), _)) => break sink, + Either::Right((Some(msg), f)) => { + if buf.push_back(msg).is_err() { + log::warn!(target: "rpc", "Subscription::accept failed buffer limit={} exceeded; dropping subscription", buf.max_cap); + return + } + accept_fut = f; + }, + // The connection was closed or the stream was closed. + _ => return, + } + }; + + inner_pipe_from_stream(sink, stream, buf).await +} + +async fn inner_pipe_from_stream( + sink: SubscriptionSink, + mut stream: S, + mut buf: BoundedVecDeque, +) where + S: Stream + Unpin + Send + 'static, + T: Serialize + Send + 'static, +{ + let mut next_fut = Box::pin(Fuse::terminated()); + let mut next_item = stream.next(); + let closed = sink.closed(); + + futures::pin_mut!(closed); + + loop { + if next_fut.is_terminated() { + if let Some(v) = buf.pop_front() { + let val = to_sub_message(&sink, &v); + next_fut.set(async { sink.send(val).await }.fuse()); + } + } + + match future::select(closed, future::select(next_fut, next_item)).await { + // Send operation finished. + Either::Right((Either::Left((_, n)), c)) => { + next_item = n; + closed = c; + next_fut = Box::pin(Fuse::terminated()); + }, + // New item from the stream + Either::Right((Either::Right((Some(v), n)), c)) => { + if buf.push_back(v).is_err() { + log::warn!( + target: "rpc", + "Subscription buffer limit={} exceeded for subscription={} conn_id={}; dropping subscription", + buf.max_cap, + sink.method_name(), + sink.connection_id() + ); + return + } + + next_fut = n; + closed = c; + next_item = stream.next(); + }, + // Stream "finished". + // + // Process remaining items and terminate. + Either::Right((Either::Right((None, pending_fut)), _)) => { + if pending_fut.await.is_err() { + return; + } + + while let Some(v) = buf.pop_front() { + let val = to_sub_message(&sink, &v); + if sink.send(val).await.is_err() { + return; + } + } + + return; + }, + // Subscription was closed. + Either::Left(_) => return, + } + } +} + +/// Builds a subscription message. +/// +/// # Panics +/// +/// This function panics `Serialize` fails and it is treated a bug. +pub fn to_sub_message(sink: &SubscriptionSink, result: &impl Serialize) -> SubscriptionMessage { + SubscriptionMessage::new(sink.method_name(), sink.subscription_id(), result) + .expect("Serialize infallible; qed") +} + +/// Helper for spawning non-blocking rpc subscription task. +pub fn spawn_subscription_task( + executor: &SubscriptionTaskExecutor, + fut: impl Future + Send + 'static, +) { + executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); +} + +#[cfg(test)] +mod tests { + use super::pipe_from_stream; + use futures::StreamExt; + use jsonrpsee::{core::EmptyServerParams, RpcModule, Subscription}; + + async fn subscribe() -> Subscription { + let mut module = RpcModule::new(()); + module + .register_subscription("sub", "my_sub", "unsub", |_, pending, _| async move { + let stream = futures::stream::iter([0; 16]); + pipe_from_stream(pending, stream).await; + Ok(()) + }) + .unwrap(); + + module.subscribe("sub", EmptyServerParams::new(), 1).await.unwrap() + } + + #[tokio::test] + async fn pipe_from_stream_works() { + let mut sub = subscribe().await; + let mut rx = 0; + + while let Some(Ok(_)) = sub.next::().await { + rx += 1; + } + + assert_eq!(rx, 16); + } + + #[tokio::test] + async fn pipe_from_stream_is_bounded() { + let (tx, mut rx) = futures::channel::mpsc::unbounded::<()>(); + + let mut module = RpcModule::new(tx); + module + .register_subscription("sub", "my_sub", "unsub", |_, pending, ctx| async move { + let stream = futures::stream::iter([0; 32]); + pipe_from_stream(pending, stream).await; + _ = ctx.unbounded_send(()); + Ok(()) + }) + .unwrap(); + + let mut sub = module.subscribe("sub", EmptyServerParams::new(), 1).await.unwrap(); + + // When the 17th item arrives the subscription is dropped + _ = rx.next().await.unwrap(); + assert!(sub.next::().await.is_none()); + } +} diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 576a8aac8e49f8f8e6ef38d62685bcbd2e94d33d..bbf67d1fbd0af6dceada3b73178004558044d0b2 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-service" -version = "0.10.0-dev" +version = "0.35.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -28,17 +28,17 @@ runtime-benchmarks = [ ] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["server"] } -thiserror = "1.0.48" +jsonrpsee = { version = "0.22", features = ["server"] } +thiserror = { workspace = true } futures = "0.3.21" rand = "0.8.5" parking_lot = "0.12.1" -log = "0.4.17" +log = { workspace = true, default-features = true } futures-timer = "3.0.1" exit-future = "0.2.0" pin-project = "1.0.12" -serde = "1.0.195" -serde_json = "1.0.111" +serde = { workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } sc-keystore = { path = "../keystore" } sp-runtime = { path = "../../primitives/runtime" } sp-trie = { path = "../../primitives/trie" } @@ -84,6 +84,7 @@ tokio = { version = "1.22.0", features = ["parking_lot", "rt-multi-thread", "tim tempfile = "3.1.0" directories = "5.0.1" static_init = "1.0.3" +schnellru = "0.2.1" [dev-dependencies] substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 0b8c86be92b54596f2236a24937d7c31ac331e32..31d63c6a81d3a4df2e6eca32260a64f577712aa9 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -63,7 +63,9 @@ use sc_rpc::{ system::SystemApiServer, DenyUnsafe, SubscriptionTaskExecutor, }; -use sc_rpc_spec_v2::{chain_head::ChainHeadApiServer, transaction::TransactionApiServer}; +use sc_rpc_spec_v2::{ + archive::ArchiveApiServer, chain_head::ChainHeadApiServer, transaction::TransactionApiServer, +}; use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO}; use sc_transaction_pool_api::{MaintainedTransactionPool, TransactionPool}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; @@ -458,7 +460,7 @@ where spawn_handle.spawn( "on-transaction-imported", Some("transaction-pool"), - transaction_notifications( + propagate_transaction_notifications( transaction_pool.clone(), tx_handler_controller, telemetry.clone(), @@ -530,7 +532,8 @@ where Ok(rpc_handlers) } -async fn transaction_notifications( +/// Returns a future that forwards imported transactions to the transaction networking protocol. +pub async fn propagate_transaction_notifications( transaction_pool: Arc, tx_handler_controller: sc_network_transactions::TransactionsHandlerController< ::Hash, @@ -558,7 +561,8 @@ async fn transaction_notifications( .await; } -fn init_telemetry( +/// Initialize telemetry with provided configuration and return telemetry handle +pub fn init_telemetry( config: &mut Configuration, network: Network, client: Arc, @@ -596,7 +600,8 @@ where Ok(telemetry.handle()) } -fn gen_rpc_module( +/// Generate RPC module using provided configuration +pub fn gen_rpc_module( deny_unsafe: DenyUnsafe, spawn_handle: SpawnTaskHandle, client: Arc, @@ -664,6 +669,26 @@ where ) .into_rpc(); + // Part of the RPC v2 spec. + // 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(); + if is_archive_node { + let genesis_hash = + client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); + let archive_v2 = sc_rpc_spec_v2::archive::Archive::new( + client.clone(), + backend.clone(), + genesis_hash, + // Defaults to sensible limits for the `Archive`. + sc_rpc_spec_v2::archive::ArchiveConfig::default(), + ) + .into_rpc(); + rpc_api.merge(archive_v2).map_err(|e| Error::Application(e.into()))?; + } + let author = sc_rpc::author::Author::new( client.clone(), transaction_pool, diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index aa9c1b80a29a95bd77efbda35620c132b624bd9b..35e8b53a09cf2d802a0c4a873f7ecb8099764e83 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -19,8 +19,8 @@ //! Substrate Client use super::block_rules::{BlockRules, LookupResult as BlockLookupResult}; -use futures::{FutureExt, StreamExt}; -use log::{error, info, trace, warn}; +use crate::client::notification_pinning::NotificationPinningWorker; +use log::{debug, info, trace, warn}; use parking_lot::{Mutex, RwLock}; use prometheus_endpoint::Registry; use rand::Rng; @@ -38,7 +38,7 @@ use sc_client_api::{ execution_extensions::ExecutionExtensions, notifications::{StorageEventStream, StorageNotifications}, CallExecutor, ExecutorProvider, KeysIter, OnFinalityAction, OnImportAction, PairsIter, - ProofProvider, UsageProvider, + ProofProvider, UnpinWorkerMessage, UsageProvider, }; use sc_consensus::{ BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction, @@ -114,7 +114,7 @@ where block_rules: BlockRules, config: ClientConfig, telemetry: Option, - unpin_worker_sender: TracingUnboundedSender, + unpin_worker_sender: TracingUnboundedSender>, _phantom: PhantomData, } @@ -326,19 +326,35 @@ where // dropped, the block will be unpinned automatically. if let Some(ref notification) = finality_notification { if let Err(err) = self.backend.pin_block(notification.hash) { - error!( + debug!( "Unable to pin block for finality notification. hash: {}, Error: {}", notification.hash, err ); - }; + } else { + let _ = self + .unpin_worker_sender + .unbounded_send(UnpinWorkerMessage::AnnouncePin(notification.hash)) + .map_err(|e| { + log::error!( + "Unable to send AnnouncePin worker message for finality: {e}" + ) + }); + } } if let Some(ref notification) = import_notification { if let Err(err) = self.backend.pin_block(notification.hash) { - error!( + debug!( "Unable to pin block for import notification. hash: {}, Error: {}", notification.hash, err ); + } else { + let _ = self + .unpin_worker_sender + .unbounded_send(UnpinWorkerMessage::AnnouncePin(notification.hash)) + .map_err(|e| { + log::error!("Unable to send AnnouncePin worker message for import: {e}") + }); }; } @@ -416,25 +432,12 @@ where backend.commit_operation(op)?; } - let (unpin_worker_sender, mut rx) = - tracing_unbounded::("unpin-worker-channel", 10_000); - let task_backend = Arc::downgrade(&backend); - spawn_handle.spawn( - "unpin-worker", - None, - async move { - while let Some(message) = rx.next().await { - if let Some(backend) = task_backend.upgrade() { - backend.unpin_block(message); - } else { - log::debug!("Terminating unpin-worker, backend reference was dropped."); - return - } - } - log::debug!("Terminating unpin-worker, stream terminated.") - } - .boxed(), + let (unpin_worker_sender, rx) = tracing_unbounded::>( + "notification-pinning-worker-channel", + 10_000, ); + let unpin_worker = NotificationPinningWorker::new(rx, backend.clone()); + spawn_handle.spawn("notification-pinning-worker", None, Box::pin(unpin_worker.run())); Ok(Client { backend, @@ -675,8 +678,10 @@ where // This is use by fast sync for runtime version to be resolvable from // changes. - let state_version = - resolve_state_version_from_wasm(&storage, &self.executor)?; + let state_version = resolve_state_version_from_wasm::<_, HashingFor>( + &storage, + &self.executor, + )?; let state_root = operation.op.reset_storage(storage, state_version)?; if state_root != *import_headers.post().state_root() { // State root mismatch when importing state. This should not happen in diff --git a/substrate/client/service/src/client/mod.rs b/substrate/client/service/src/client/mod.rs index a13fd4317e1553d379ea068c516e74072d3c8c95..0703cc2b47d144d4e67418cfb9966cd1cd209392 100644 --- a/substrate/client/service/src/client/mod.rs +++ b/substrate/client/service/src/client/mod.rs @@ -47,6 +47,7 @@ mod block_rules; mod call_executor; mod client; +mod notification_pinning; mod wasm_override; mod wasm_substitutes; diff --git a/substrate/client/service/src/client/notification_pinning.rs b/substrate/client/service/src/client/notification_pinning.rs new file mode 100644 index 0000000000000000000000000000000000000000..80de91c02f1ae0273d42edc79d442aa312d09356 --- /dev/null +++ b/substrate/client/service/src/client/notification_pinning.rs @@ -0,0 +1,353 @@ +// 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 . + +//! Notification pinning related logic. +//! +//! This file contains a worker that should be started when a new client instance is created. +//! The goal is to avoid pruning of blocks that have active notifications in the node. Every +//! recipient of notifications should receive the chance to act upon them. In addition, notification +//! listeners can hold onto a [`sc_client_api::UnpinHandle`] to keep a block pinned. Once the handle +//! is dropped, a message is sent and the worker unpins the respective block. +use std::{ + marker::PhantomData, + sync::{Arc, Weak}, +}; + +use futures::StreamExt; +use sc_client_api::{Backend, UnpinWorkerMessage}; + +use sc_utils::mpsc::TracingUnboundedReceiver; +use schnellru::Limiter; +use sp_runtime::traits::Block as BlockT; + +const LOG_TARGET: &str = "db::notification_pinning"; +const NOTIFICATION_PINNING_LIMIT: usize = 1024; + +/// A limiter which automatically unpins blocks that leave the data structure. +#[derive(Clone, Debug)] +struct UnpinningByLengthLimiter> { + max_length: usize, + backend: Weak, + _phantom: PhantomData, +} + +impl> UnpinningByLengthLimiter { + /// Creates a new length limiter with a given `max_length`. + pub fn new(max_length: usize, backend: Weak) -> UnpinningByLengthLimiter { + UnpinningByLengthLimiter { max_length, backend, _phantom: PhantomData::::default() } + } +} + +impl> Limiter + for UnpinningByLengthLimiter +{ + type KeyToInsert<'a> = Block::Hash; + type LinkType = usize; + + fn is_over_the_limit(&self, length: usize) -> bool { + length > self.max_length + } + + fn on_insert( + &mut self, + _length: usize, + key: Self::KeyToInsert<'_>, + value: u32, + ) -> Option<(Block::Hash, u32)> { + log::debug!(target: LOG_TARGET, "Pinning block based on notification. hash = {key}"); + if self.max_length > 0 { + Some((key, value)) + } else { + None + } + } + + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut Block::Hash, + _new_key: Block::Hash, + _old_value: &mut u32, + _new_value: &mut u32, + ) -> bool { + true + } + + fn on_removed(&mut self, key: &mut Block::Hash, references: &mut u32) { + // If reference count was larger than 0 on removal, + // the item was removed due to capacity limitations. + // Since the cache should be large enough for pinned items, + // we want to know about these evictions. + if *references > 0 { + log::warn!( + target: LOG_TARGET, + "Notification block pinning limit reached. Unpinning block with hash = {key:?}" + ); + if let Some(backend) = self.backend.upgrade() { + (0..*references).for_each(|_| backend.unpin_block(*key)); + } + } else { + log::trace!( + target: LOG_TARGET, + "Unpinned block. hash = {key:?}", + ) + } + } + + fn on_cleared(&mut self) {} + + fn on_grow(&mut self, _new_memory_usage: usize) -> bool { + true + } +} + +/// Worker for the handling of notification pinning. +/// +/// It receives messages from a receiver and pins/unpins based on the incoming messages. +/// All notification related unpinning should go through this worker. If the maximum number of +/// notification pins is reached, the block from the oldest notification is unpinned. +pub struct NotificationPinningWorker> { + unpin_message_rx: TracingUnboundedReceiver>, + task_backend: Weak, + pinned_blocks: schnellru::LruMap>, +} + +impl> NotificationPinningWorker { + /// Creates a new `NotificationPinningWorker`. + pub fn new( + unpin_message_rx: TracingUnboundedReceiver>, + task_backend: Arc, + ) -> Self { + let pinned_blocks = + schnellru::LruMap::>::new( + UnpinningByLengthLimiter::new( + NOTIFICATION_PINNING_LIMIT, + Arc::downgrade(&task_backend), + ), + ); + Self { unpin_message_rx, task_backend: Arc::downgrade(&task_backend), pinned_blocks } + } + + fn handle_announce_message(&mut self, hash: Block::Hash) { + if let Some(entry) = self.pinned_blocks.get_or_insert(hash, Default::default) { + *entry = *entry + 1; + } + } + + fn handle_unpin_message(&mut self, hash: Block::Hash) -> Result<(), ()> { + if let Some(refcount) = self.pinned_blocks.peek_mut(&hash) { + *refcount = *refcount - 1; + if *refcount == 0 { + self.pinned_blocks.remove(&hash); + } + if let Some(backend) = self.task_backend.upgrade() { + log::debug!(target: LOG_TARGET, "Reducing pinning refcount for block hash = {hash:?}"); + backend.unpin_block(hash); + } else { + log::debug!(target: LOG_TARGET, "Terminating unpin-worker, backend reference was dropped."); + return Err(()) + } + } else { + log::debug!(target: LOG_TARGET, "Received unpin message for already unpinned block. hash = {hash:?}"); + } + Ok(()) + } + + /// Start working on the received messages. + /// + /// The worker maintains a map which keeps track of the pinned blocks and their reference count. + /// Depending upon the received message, it acts to pin/unpin the block. + pub async fn run(mut self) { + while let Some(message) = self.unpin_message_rx.next().await { + match message { + UnpinWorkerMessage::AnnouncePin(hash) => self.handle_announce_message(hash), + UnpinWorkerMessage::Unpin(hash) => + if self.handle_unpin_message(hash).is_err() { + return + }, + } + } + log::debug!(target: LOG_TARGET, "Terminating unpin-worker, stream terminated.") + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use sc_client_api::{Backend, UnpinWorkerMessage}; + use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; + use sp_core::H256; + use sp_runtime::traits::Block as BlockT; + + type Block = substrate_test_runtime_client::runtime::Block; + + use super::{NotificationPinningWorker, UnpinningByLengthLimiter}; + + impl> NotificationPinningWorker { + fn new_with_limit( + unpin_message_rx: TracingUnboundedReceiver>, + task_backend: Arc, + limit: usize, + ) -> Self { + let pinned_blocks = + schnellru::LruMap::>::new( + UnpinningByLengthLimiter::new(limit, Arc::downgrade(&task_backend)), + ); + Self { unpin_message_rx, task_backend: Arc::downgrade(&task_backend), pinned_blocks } + } + + fn lru( + &self, + ) -> &schnellru::LruMap> { + &self.pinned_blocks + } + } + + #[test] + fn pinning_worker_handles_base_case() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash = H256::random(); + + let mut worker = NotificationPinningWorker::new(rx, backend.clone()); + + // Block got pinned and unpin message should unpin in the backend. + let _ = backend.pin_block(hash); + assert_eq!(backend.pin_refs(&hash), Some(1)); + + worker.handle_announce_message(hash); + assert_eq!(worker.lru().len(), 1); + + let _ = worker.handle_unpin_message(hash); + + assert_eq!(backend.pin_refs(&hash), Some(0)); + assert!(worker.lru().is_empty()); + } + + #[test] + fn pinning_worker_handles_multiple_pins() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash = H256::random(); + + let mut worker = NotificationPinningWorker::new(rx, backend.clone()); + // Block got pinned multiple times. + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + assert_eq!(backend.pin_refs(&hash), Some(3)); + + worker.handle_announce_message(hash); + worker.handle_announce_message(hash); + worker.handle_announce_message(hash); + assert_eq!(worker.lru().len(), 1); + + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(2)); + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(1)); + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(0)); + assert!(worker.lru().is_empty()); + + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(0)); + } + + #[test] + fn pinning_worker_handles_too_many_unpins() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash = H256::random(); + let hash2 = H256::random(); + + let mut worker = NotificationPinningWorker::new(rx, backend.clone()); + // Block was announced once but unpinned multiple times. The worker should ignore the + // additional unpins. + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + let _ = backend.pin_block(hash); + assert_eq!(backend.pin_refs(&hash), Some(3)); + + worker.handle_announce_message(hash); + assert_eq!(worker.lru().len(), 1); + + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(2)); + let _ = worker.handle_unpin_message(hash); + assert_eq!(backend.pin_refs(&hash), Some(2)); + assert!(worker.lru().is_empty()); + + let _ = worker.handle_unpin_message(hash2); + assert!(worker.lru().is_empty()); + assert_eq!(backend.pin_refs(&hash2), None); + } + + #[test] + fn pinning_worker_should_evict_when_limit_reached() { + let (_tx, rx) = tracing_unbounded("testing", 1000); + + let backend = Arc::new(sc_client_api::in_mem::Backend::::new()); + + let hash1 = H256::random(); + let hash2 = H256::random(); + let hash3 = H256::random(); + let hash4 = H256::random(); + + // Only two items fit into the cache. + let mut worker = NotificationPinningWorker::new_with_limit(rx, backend.clone(), 2); + + // Multiple blocks are announced but the cache size is too small. We expect that blocks + // are evicted by the cache and unpinned in the backend. + let _ = backend.pin_block(hash1); + let _ = backend.pin_block(hash2); + let _ = backend.pin_block(hash3); + assert_eq!(backend.pin_refs(&hash1), Some(1)); + assert_eq!(backend.pin_refs(&hash2), Some(1)); + assert_eq!(backend.pin_refs(&hash3), Some(1)); + + worker.handle_announce_message(hash1); + assert!(worker.lru().peek(&hash1).is_some()); + worker.handle_announce_message(hash2); + assert!(worker.lru().peek(&hash2).is_some()); + worker.handle_announce_message(hash3); + assert!(worker.lru().peek(&hash3).is_some()); + assert!(worker.lru().peek(&hash2).is_some()); + assert_eq!(worker.lru().len(), 2); + + // Hash 1 should have gotten unpinned, since its oldest. + assert_eq!(backend.pin_refs(&hash1), Some(0)); + assert_eq!(backend.pin_refs(&hash2), Some(1)); + assert_eq!(backend.pin_refs(&hash3), Some(1)); + + // Hash 2 is getting bumped. + worker.handle_announce_message(hash2); + assert_eq!(worker.lru().peek(&hash2), Some(&2)); + + // Since hash 2 was accessed, evict hash 3. + worker.handle_announce_message(hash4); + assert_eq!(worker.lru().peek(&hash3), None); + } +} diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index c2ce67df7eafc7e327f15844bd559b21bca14f3d..35262ff493b44f07a94f1d35f1a3e765251a1897 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -18,6 +18,7 @@ //! Service configuration. +pub use jsonrpsee::server::BatchRequestConfig as RpcBatchRequestConfig; use prometheus_endpoint::Registry; use sc_chain_spec::ChainSpec; pub use sc_client_db::{BlocksPruning, Database, DatabaseSource, PruningMode}; @@ -39,6 +40,7 @@ use sp_core::crypto::SecretString; use std::{ io, iter, net::SocketAddr, + num::NonZeroU32, path::{Path, PathBuf}, }; use tempfile::TempDir; @@ -100,6 +102,12 @@ pub struct Configuration { 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, /// Prometheus endpoint configuration. `None` if disabled. pub prometheus_config: Option, /// Telemetry service URL. `None` if disabled. diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index bec1044daab4a30b5e38d02447c4ecbc31ec7329..9480d4a0b07276a5131be17579964c04abda1f1d 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -37,8 +37,8 @@ mod task_manager; use std::{collections::HashMap, net::SocketAddr}; use codec::{Decode, Encode}; -use futures::{channel::mpsc, pin_mut, FutureExt, StreamExt}; -use jsonrpsee::{core::Error as JsonRpseeError, RpcModule}; +use futures::{pin_mut, FutureExt, StreamExt}; +use jsonrpsee::RpcModule; use log::{debug, error, warn}; use sc_client_api::{blockchain::HeaderBackend, BlockBackend, BlockchainEvents, ProofProvider}; use sc_network::{ @@ -109,11 +109,14 @@ impl RpcHandlers { pub async fn rpc_query( &self, json_query: &str, - ) -> Result<(String, mpsc::UnboundedReceiver), JsonRpseeError> { - self.0 - .raw_json_request(json_query) - .await - .map(|(method_res, recv)| (method_res.result, recv)) + ) -> Result<(String, tokio::sync::mpsc::Receiver), serde_json::Error> { + // Because `tokio::sync::mpsc::channel` is used under the hood + // it will panic if it's set to usize::MAX. + // + // 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 } /// Provides access to the underlying `RpcModule` @@ -362,7 +365,7 @@ mod waiting { } /// Starts RPC servers. -fn start_rpc_servers( +pub fn start_rpc_servers( config: &Configuration, gen_rpc_module: R, rpc_id_provider: Option>, @@ -390,15 +393,18 @@ where 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))?, metrics, id_provider: rpc_id_provider, cors: config.rpc_cors.as_ref(), tokio_handle: config.tokio_handle.clone(), + rate_limit: config.rpc_rate_limit, }; // TODO: https://github.com/paritytech/substrate/issues/13773 diff --git a/substrate/client/service/test/Cargo.toml b/substrate/client/service/test/Cargo.toml index 625d8286396e7778dd7271db772d143550dc6b5e..ee7e60f6011701f9d12fdb521db8226c883dbfe4 100644 --- a/substrate/client/service/test/Cargo.toml +++ b/substrate/client/service/test/Cargo.toml @@ -19,7 +19,7 @@ async-channel = "1.8.0" array-bytes = "6.1" fdlimit = "0.3.0" futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } parity-scale-codec = "3.6.1" parking_lot = "0.12.1" tempfile = "3.1.0" diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs index 456df73459a31f2a92561b8ee0e57761eb03e5ae..349538965ee1fa04bd0acfff42b517ae40452c1c 100644 --- a/substrate/client/service/test/src/lib.rs +++ b/substrate/client/service/test/src/lib.rs @@ -29,7 +29,7 @@ use sc_network::{ use sc_network_sync::SyncingService; use sc_service::{ client::Client, - config::{BasePath, DatabaseSource, KeystoreConfig}, + config::{BasePath, DatabaseSource, KeystoreConfig, RpcBatchRequestConfig}, BlocksPruning, ChainSpecExtension, Configuration, Error, GenericChainSpec, Role, RuntimeGenesis, SpawnTaskHandle, TaskManager, }; @@ -253,6 +253,9 @@ fn node_config< 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, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/client/state-db/Cargo.toml b/substrate/client/state-db/Cargo.toml index 001ada02ef2f8140dd4619e451517e95f91a136c..400dda20c223443687292315b4974e5125570553 100644 --- a/substrate/client/state-db/Cargo.toml +++ b/substrate/client/state-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-state-db" -version = "0.10.0-dev" +version = "0.30.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -17,6 +17,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" sp-core = { path = "../../primitives/core" } diff --git a/substrate/client/statement-store/Cargo.toml b/substrate/client/statement-store/Cargo.toml index adfd27a1705ad3e3ade840c81af60b23a0d02af1..676f6cb36f67992c86db1174cbbf96e02e53d1b1 100644 --- a/substrate/client/statement-store/Cargo.toml +++ b/substrate/client/statement-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-statement-store" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" parity-db = "0.4.12" tokio = { version = "1.22.0", features = ["time"] } diff --git a/substrate/client/storage-monitor/Cargo.toml b/substrate/client/storage-monitor/Cargo.toml index 6a01ef0fe2bdcfd3b2a9ba0962241ffc0f0fca63..b2120b3efc4396801bdbcaf0a7867ab6692908d3 100644 --- a/substrate/client/storage-monitor/Cargo.toml +++ b/substrate/client/storage-monitor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-storage-monitor" -version = "0.1.0" +version = "0.16.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -12,9 +12,9 @@ homepage = "https://substrate.io" workspace = true [dependencies] -clap = { version = "4.4.18", features = ["derive", "string"] } -log = "0.4.17" +clap = { version = "4.5.1", features = ["derive", "string"] } +log = { workspace = true, default-features = true } fs4 = "0.7.0" sp-core = { path = "../../primitives/core" } tokio = { version = "1.22.0", features = ["time"] } -thiserror = "1.0.48" +thiserror = { workspace = true } diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index b3de9585ab454da2be2e96193d0fe65af234ec6d..09dc611caa044debeabce4f15686465535f076ed 100644 --- a/substrate/client/sync-state-rpc/Cargo.toml +++ b/substrate/client/sync-state-rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-sync-state-rpc" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true description = "A RPC handler to create sync states for light clients." edition.workspace = true @@ -16,10 +16,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" -thiserror = "1.0.48" +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } sc-chain-spec = { path = "../chain-spec" } sc-client-api = { path = "../api" } sc-consensus-babe = { path = "../consensus/babe" } diff --git a/substrate/client/sync-state-rpc/src/lib.rs b/substrate/client/sync-state-rpc/src/lib.rs index dda8a7edfa9bba1838efa34f96e4a3e6470d1fe4..cb737cc6c52b3b362134d8a2291aec9510fb0a9e 100644 --- a/substrate/client/sync-state-rpc/src/lib.rs +++ b/substrate/client/sync-state-rpc/src/lib.rs @@ -44,9 +44,9 @@ use std::sync::Arc; use jsonrpsee::{ - core::{async_trait, Error as JsonRpseeError, RpcResult}, + core::async_trait, proc_macros::rpc, - types::{error::CallError, ErrorObject}, + types::{ErrorObject, ErrorObjectOwned}, }; use sc_client_api::StorageData; @@ -80,13 +80,13 @@ pub enum Error { LightSyncStateExtensionNotFound, } -impl From> for JsonRpseeError { +impl From> for ErrorObjectOwned { fn from(error: Error) -> Self { let message = match error { Error::JsonRpc(s) => s, _ => error.to_string(), }; - CallError::Custom(ErrorObject::owned(1, message, None::<()>)).into() + ErrorObject::owned(1, message, None::<()>) } } @@ -126,10 +126,10 @@ pub struct LightSyncState { /// An api for sync state RPC calls. #[rpc(client, server)] -pub trait SyncStateApi { +pub trait SyncStateApi { /// Returns the JSON serialized chainspec running the node, with a sync state. #[method(name = "sync_state_genSyncSpec")] - async fn system_gen_sync_spec(&self, raw: bool) -> RpcResult; + async fn system_gen_sync_spec(&self, raw: bool) -> Result>; } /// An api for sync state RPC calls. @@ -188,12 +188,12 @@ where } #[async_trait] -impl SyncStateApiServer for SyncState +impl SyncStateApiServer for SyncState where Block: BlockT, Backend: HeaderBackend + sc_client_api::AuxStore + 'static, { - async fn system_gen_sync_spec(&self, raw: bool) -> RpcResult { + async fn system_gen_sync_spec(&self, raw: bool) -> Result> { let current_sync_state = self.build_sync_state().await?; let mut chain_spec = self.chain_spec.cloned_box(); @@ -202,10 +202,11 @@ where ) .ok_or(Error::::LightSyncStateExtensionNotFound)?; - let val = serde_json::to_value(¤t_sync_state)?; + let val = serde_json::to_value(¤t_sync_state) + .map_err(|e| Error::::JsonRpc(e.to_string()))?; *extension = Some(val); let json_str = chain_spec.as_json(raw).map_err(|e| Error::::JsonRpc(e))?; - serde_json::from_str(&json_str).map_err(Into::into) + serde_json::from_str(&json_str).map_err(|e| Error::::JsonRpc(e.to_string())) } } diff --git a/substrate/client/sysinfo/Cargo.toml b/substrate/client/sysinfo/Cargo.toml index 18ac161f1ee1a55246c6c096b22bb599e79f6fcb..ba58452ffb58bfd737235beb285e6e46c992967d 100644 --- a/substrate/client/sysinfo/Cargo.toml +++ b/substrate/client/sysinfo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-sysinfo" -version = "6.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -19,15 +19,16 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] futures = "0.3.19" libc = "0.2" -log = "0.4.17" +log = { workspace = true, default-features = true } rand = "0.8.5" rand_pcg = "0.3.1" derive_more = "0.99" regex = "1" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } sc-telemetry = { path = "../telemetry" } sp-core = { path = "../../primitives/core" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } sp-io = { path = "../../primitives/io" } sp-std = { path = "../../primitives/std" } diff --git a/substrate/client/sysinfo/src/sysinfo.rs b/substrate/client/sysinfo/src/sysinfo.rs index bef87a83e46f6774ca112e4a9ba83b927d8eef4d..3fa583cf7aca1f6dab055e4d498982268f1be084 100644 --- a/substrate/client/sysinfo/src/sysinfo.rs +++ b/substrate/client/sysinfo/src/sysinfo.rs @@ -365,7 +365,7 @@ pub fn benchmark_cpu(limit: ExecutionLimit) -> Throughput { let run = || -> Result<(), ()> { clobber_slice(&mut buffer); - hash = sp_core::hashing::blake2_256(&buffer); + hash = sp_crypto_hashing::blake2_256(&buffer); clobber_slice(&mut hash); Ok(()) diff --git a/substrate/client/telemetry/Cargo.toml b/substrate/client/telemetry/Cargo.toml index ba597ef898e03367e94d57cefb195b0fb16e9d04..8ab00202f0ba6828dbf210e44d6337393ba1d391 100644 --- a/substrate/client/telemetry/Cargo.toml +++ b/substrate/client/telemetry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-telemetry" -version = "4.0.0-dev" +version = "15.0.0" authors.workspace = true description = "Telemetry utils" edition.workspace = true @@ -20,12 +20,12 @@ targets = ["x86_64-unknown-linux-gnu"] chrono = "0.4.31" futures = "0.3.21" libp2p = { version = "0.51.4", features = ["dns", "tcp", "tokio", "wasm-ext", "websocket"] } -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" pin-project = "1.0.12" sc-utils = { path = "../utils" } rand = "0.8.5" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" -thiserror = "1.0.48" +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } wasm-timer = "0.2.5" diff --git a/substrate/client/tracing/Cargo.toml b/substrate/client/tracing/Cargo.toml index bca6110846ada0b1c58c236b81505bd2f2c773fb..61e6f7d0bab5b2422b3354edb10e1753d3ef1e7a 100644 --- a/substrate/client/tracing/Cargo.toml +++ b/substrate/client/tracing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-tracing" -version = "4.0.0-dev" +version = "28.0.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors.workspace = true edition.workspace = true @@ -22,12 +22,12 @@ chrono = "0.4.31" codec = { package = "parity-scale-codec", version = "3.6.1" } lazy_static = "1.4.0" libc = "0.2.152" -log = { version = "0.4.17" } +log = { workspace = true, default-features = true } parking_lot = "0.12.1" regex = "1.6.0" rustc-hash = "1.1.0" -serde = "1.0.195" -thiserror = "1.0.48" +serde = { workspace = true, default-features = true } +thiserror = { workspace = true } tracing = "0.1.29" tracing-log = "0.1.3" tracing-subscriber = { version = "0.2.25", features = ["parking_lot"] } diff --git a/substrate/client/tracing/proc-macro/Cargo.toml b/substrate/client/tracing/proc-macro/Cargo.toml index c293c1834e83b9609ddcc8973da3d2529b2d01eb..fec34aa0bca935e22f0f7d7faea17cac5d8f10bb 100644 --- a/substrate/client/tracing/proc-macro/Cargo.toml +++ b/substrate/client/tracing/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-tracing-proc-macro" -version = "4.0.0-dev" +version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -20,5 +20,5 @@ proc-macro = true [dependencies] proc-macro-crate = "3.0.0" proc-macro2 = "1.0.56" -quote = { version = "1.0.28", features = ["proc-macro"] } -syn = { version = "2.0.48", features = ["extra-traits", "full", "parsing", "proc-macro"] } +quote = { features = ["proc-macro"], workspace = true } +syn = { features = ["extra-traits", "full", "parsing", "proc-macro"], workspace = true } diff --git a/substrate/client/transaction-pool/Cargo.toml b/substrate/client/transaction-pool/Cargo.toml index 8832c0bf5080d548bc1b5724b60d98e285cc5ca2..2ca37afd61b84a7228852290d5075841c993a503 100644 --- a/substrate/client/transaction-pool/Cargo.toml +++ b/substrate/client/transaction-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-transaction-pool" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -21,10 +21,10 @@ codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" futures-timer = "3.0.2" linked-hash-map = "0.5.4" -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" -serde = { version = "1.0.195", features = ["derive"] } -thiserror = "1.0.48" +serde = { features = ["derive"], workspace = true, default-features = true } +thiserror = { workspace = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } sc-client-api = { path = "../api" } sc-transaction-pool-api = { path = "api" } @@ -32,6 +32,7 @@ sc-utils = { path = "../utils" } sp-api = { path = "../../primitives/api" } sp-blockchain = { path = "../../primitives/blockchain" } sp-core = { path = "../../primitives/core" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } sp-runtime = { path = "../../primitives/runtime" } sp-tracing = { path = "../../primitives/tracing" } sp-transaction-pool = { path = "../../primitives/transaction-pool" } diff --git a/substrate/client/transaction-pool/api/Cargo.toml b/substrate/client/transaction-pool/api/Cargo.toml index 2522739cf88707c1a8147db5a62fb899e568435f..d52e4783fabce48992dec01e83c61bc7a3074a20 100644 --- a/substrate/client/transaction-pool/api/Cargo.toml +++ b/substrate/client/transaction-pool/api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-transaction-pool-api" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -15,12 +15,12 @@ workspace = true async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" -log = "0.4.17" -serde = { version = "1.0.195", features = ["derive"] } -thiserror = "1.0.48" +log = { workspace = true, default-features = true } +serde = { features = ["derive"], workspace = true, default-features = true } +thiserror = { workspace = true } sp-blockchain = { path = "../../../primitives/blockchain" } sp-core = { path = "../../../primitives/core", default-features = false } sp-runtime = { path = "../../../primitives/runtime", default-features = false } [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } diff --git a/substrate/client/transaction-pool/api/src/error.rs b/substrate/client/transaction-pool/api/src/error.rs index e521502f66fb1cbd3e85ef4b85ec9839d83e8d9f..d0744bfa3e192bcd5e6795ee96540c503c8ebec5 100644 --- a/substrate/client/transaction-pool/api/src/error.rs +++ b/substrate/client/transaction-pool/api/src/error.rs @@ -71,6 +71,30 @@ pub enum Error { RejectedFutureTransaction, } +impl Error { + /// Returns true if the transaction could be re-submitted to the pool in the future. + /// + /// For example, `Error::ImmediatelyDropped` is retriable, because the transaction + /// may enter the pool if there is space for it in the future. + pub fn is_retriable(&self) -> bool { + match self { + // An invalid transaction is temporarily banned, however it can + // become valid at a later time. + Error::TemporarilyBanned | + // The pool is full at the moment. + Error::ImmediatelyDropped | + // The block id is not known to the pool. + // The node might be lagging behind, or during a warp sync. + Error::InvalidBlockId(_) | + // The pool is configured to not accept future transactions. + Error::RejectedFutureTransaction => { + true + } + _ => false + } + } +} + /// Transaction pool error conversion. pub trait IntoPoolError: std::error::Error + Send + Sized + Sync { /// Try to extract original `Error` diff --git a/substrate/client/transaction-pool/api/src/lib.rs b/substrate/client/transaction-pool/api/src/lib.rs index a795917528f9cca0831068fc77dd346fd2390838..0a313c5b782d90f9dab5e3de326160c37cc8a45d 100644 --- a/substrate/client/transaction-pool/api/src/lib.rs +++ b/substrate/client/transaction-pool/api/src/lib.rs @@ -62,20 +62,27 @@ impl PoolStatus { /// /// The status events can be grouped based on their kinds as: /// 1. Entering/Moving within the pool: -/// - `Future` -/// - `Ready` +/// - [Future](TransactionStatus::Future) +/// - [Ready](TransactionStatus::Ready) /// 2. Inside `Ready` queue: -/// - `Broadcast` +/// - [Broadcast](TransactionStatus::Broadcast) /// 3. Leaving the pool: -/// - `InBlock` -/// - `Invalid` -/// - `Usurped` -/// - `Dropped` +/// - [InBlock](TransactionStatus::InBlock) +/// - [Invalid](TransactionStatus::Invalid) +/// - [Usurped](TransactionStatus::Usurped) +/// - [Dropped](TransactionStatus::Dropped) /// 4. Re-entering the pool: -/// - `Retracted` +/// - [Retracted](TransactionStatus::Retracted) /// 5. Block finalized: -/// - `Finalized` -/// - `FinalityTimeout` +/// - [Finalized](TransactionStatus::Finalized) +/// - [FinalityTimeout](TransactionStatus::FinalityTimeout) +/// +/// Transactions are first placed in either the `Ready` or `Future` queues of the transaction pool. +/// Substrate validates the transaction before it enters the pool. +/// +/// A transaction is placed in the `Future` queue if it will become valid at a future time. +/// For example, submitting a transaction with a higher account nonce than the current +/// expected nonce will place the transaction in the `Future` queue. /// /// The events will always be received in the order described above, however /// there might be cases where transactions alternate between `Future` and `Ready` @@ -88,19 +95,37 @@ impl PoolStatus { /// 1. Due to possible forks, the transaction that ends up being in included /// in one block, may later re-enter the pool or be marked as invalid. /// 2. Transaction `Dropped` at one point, may later re-enter the pool if some other -/// transactions are removed. +/// transactions are removed. A `Dropped` transaction may re-enter the pool only if it is +/// resubmitted. /// 3. `Invalid` transaction may become valid at some point in the future. /// (Note that runtimes are encouraged to use `UnknownValidity` to inform the pool about -/// such case). +/// such case). An `Invalid` transaction may re-enter the pool only if it is resubmitted. /// 4. `Retracted` transactions might be included in some next block. /// -/// The stream is considered finished only when either `Finalized` or `FinalityTimeout` -/// event is triggered. You are however free to unsubscribe from notifications at any point. -/// The first one will be emitted when the block, in which transaction was included gets -/// finalized. The `FinalityTimeout` event will be emitted when the block did not reach finality +/// The `FinalityTimeout` event will be emitted when the block did not reach finality /// within 512 blocks. This either indicates that finality is not available for your chain, /// or that finality gadget is lagging behind. If you choose to wait for finality longer, you can /// re-subscribe for a particular transaction hash manually again. +/// +/// ### Last Event +/// +/// The stream is considered finished when one of the following events happen: +/// - [Finalized](TransactionStatus::Finalized) +/// - [FinalityTimeout](TransactionStatus::FinalityTimeout) +/// - [Usurped](TransactionStatus::Usurped) +/// - [Invalid](TransactionStatus::Invalid) +/// - [Dropped](TransactionStatus::Dropped) +/// +/// See [`TransactionStatus::is_final`] for more details. +/// +/// ### Resubmit Transactions +/// +/// Users might resubmit the transaction at a later time for the following events: +/// - [FinalityTimeout](TransactionStatus::FinalityTimeout) +/// - [Invalid](TransactionStatus::Invalid) +/// - [Dropped](TransactionStatus::Dropped) +/// +/// See [`TransactionStatus::is_retriable`] for more details. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum TransactionStatus { @@ -131,6 +156,38 @@ pub enum TransactionStatus { Invalid, } +impl TransactionStatus { + /// Returns true if this is the last event emitted by [`TransactionStatusStream`]. + pub fn is_final(&self) -> bool { + // The state must be kept in sync with `crate::graph::Sender`. + match self { + Self::Usurped(_) | + Self::Finalized(_) | + Self::FinalityTimeout(_) | + Self::Invalid | + Self::Dropped => true, + _ => false, + } + } + + /// Returns true if the transaction could be re-submitted to the pool in the future. + /// + /// For example, `TransactionStatus::Dropped` is retriable, because the transaction + /// may enter the pool if there is space for it in the future. + pub fn is_retriable(&self) -> bool { + match self { + // The number of finality watchers has been reached. + Self::FinalityTimeout(_) | + // An invalid transaction might be valid at a later time. + Self::Invalid | + // The transaction was dropped because of the limits of the pool. + // It can reenter the pool when other transactions are removed / finalized. + Self::Dropped => true, + _ => false, + } + } +} + /// The stream of transaction events. pub type TransactionStatusStream = dyn Stream> + Send; diff --git a/substrate/client/transaction-pool/benches/basics.rs b/substrate/client/transaction-pool/benches/basics.rs index 0caf00bf29551d009d7c9164a9ad2067040de198..65c83f090535e9b57d843ee367c562ef15008f1a 100644 --- a/substrate/client/transaction-pool/benches/basics.rs +++ b/substrate/client/transaction-pool/benches/basics.rs @@ -24,7 +24,7 @@ use futures::{ future::{ready, Ready}, }; use sc_transaction_pool::*; -use sp_core::blake2_256; +use sp_crypto_hashing::blake2_256; use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, NumberFor}, diff --git a/substrate/client/transaction-pool/src/lib.rs b/substrate/client/transaction-pool/src/lib.rs index faa3f455a580c8713ced21b14818da874fd972a7..730cfe367122b4a92c5d776743deba5136d6fa9e 100644 --- a/substrate/client/transaction-pool/src/lib.rs +++ b/substrate/client/transaction-pool/src/lib.rs @@ -164,8 +164,9 @@ where pool_api: Arc, best_block_hash: Block::Hash, finalized_hash: Block::Hash, + options: graph::Options, ) -> (Self, Pin + Send>>) { - let pool = Arc::new(graph::Pool::new(Default::default(), true.into(), pool_api.clone())); + let pool = Arc::new(graph::Pool::new(options, true.into(), pool_api.clone())); let (revalidation_queue, background_task) = revalidation::RevalidationQueue::new_background( pool_api.clone(), pool.clone(), @@ -658,8 +659,13 @@ where }) .unwrap_or_default() .into_iter() - .filter(|tx| tx.is_signed().unwrap_or(true)); - + // TODO [#2415]: This isn't really what we mean - we really want a + // `tx.is_transaction`, since bare transactions may be gossipped as in the case + // of Frontier txs or claims. This will be sorted once we dispense with the + // concept of bare transactions and make inherents the only possible type of + // extrinsics which are bare. At this point we can change this to + // `tx.is_transaction()`. + .filter(|tx| !tx.is_bare()); let mut resubmitted_to_report = 0; resubmit_transactions.extend(block_transactions.into_iter().filter(|tx| { diff --git a/substrate/client/transaction-pool/tests/pool.rs b/substrate/client/transaction-pool/tests/pool.rs index 6b1a197440c11e69805465ed4c7195f07eaafc0f..461b9860d414a3495dc34bf4f149963fa4d0a903 100644 --- a/substrate/client/transaction-pool/tests/pool.rs +++ b/substrate/client/transaction-pool/tests/pool.rs @@ -73,7 +73,7 @@ fn create_basic_pool_with_genesis( .map(|blocks| blocks[0].0.header.hash()) .expect("there is block 0. qed") }; - BasicPool::new_test(test_api, genesis_hash, genesis_hash) + BasicPool::new_test(test_api, genesis_hash, genesis_hash, Default::default()) } fn create_basic_pool(test_api: TestApi) -> BasicPool { @@ -994,6 +994,7 @@ fn import_notification_to_pool_maintain_works() { )), best_hash, finalized_hash, + Default::default(), ) .0, ); diff --git a/substrate/client/utils/Cargo.toml b/substrate/client/utils/Cargo.toml index a19457ac3d077f548652ee78858023f00303bff1..7f604219bc09a96ebb8114df01e10f7d474a87f4 100644 --- a/substrate/client/utils/Cargo.toml +++ b/substrate/client/utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-utils" -version = "4.0.0-dev" +version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ async-channel = "1.8.0" futures = "0.3.21" futures-timer = "3.0.2" lazy_static = "1.4.0" -log = "0.4" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" prometheus = { version = "0.13.0", default-features = false } sp-arithmetic = { path = "../../primitives/arithmetic", default-features = false } diff --git a/substrate/deprecated/hashing/Cargo.toml b/substrate/deprecated/hashing/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..8695ccc8fca2241b3947e7f2ac6a070174a158a3 --- /dev/null +++ b/substrate/deprecated/hashing/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sp-core-hashing" +version = "15.0.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage = "https://substrate.io" +repository.workspace = true +description = "Hashing primitives (deprecated: use sp-crypto-hashing for new applications)" +documentation = "https://docs.rs/sp-crypto-hashing" + +[badges] +maintenance = { status = "deprecated" } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } + +[features] +default = ["std"] +std = ["sp-crypto-hashing/std"] diff --git a/substrate/deprecated/hashing/README.md b/substrate/deprecated/hashing/README.md new file mode 100644 index 0000000000000000000000000000000000000000..73b83fd451be7fc04268368aba451b2441b1fc65 --- /dev/null +++ b/substrate/deprecated/hashing/README.md @@ -0,0 +1,3 @@ +# Hashing + +This package has been deprecated. Please use `sp-crypto-hashing`. diff --git a/substrate/deprecated/hashing/proc-macro/Cargo.toml b/substrate/deprecated/hashing/proc-macro/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..aa78809241f0394e5f984c5a41a151a9ba247595 --- /dev/null +++ b/substrate/deprecated/hashing/proc-macro/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sp-core-hashing-proc-macro" +version = "15.0.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage = "https://substrate.io" +repository.workspace = true +description = "Procedural macros for calculating static hashes (deprecated in favor of `sp-crypto-hashing-proc-macro`)." +documentation = "https://docs.rs/sp-crypto-hashing-proc-macro" + +[badges] +maintenance = { status = "deprecated" } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +sp-crypto-hashing-proc-macro = { path = "../../../primitives/crypto/hashing/proc-macro" } diff --git a/substrate/deprecated/hashing/proc-macro/README.md b/substrate/deprecated/hashing/proc-macro/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c2d9df72870252557799d7f1924dc5bd7ab79563 --- /dev/null +++ b/substrate/deprecated/hashing/proc-macro/README.md @@ -0,0 +1,3 @@ +# Hashing Macros + +This package has been deprecated. Please use `sp-crypto-hashing-proc-macro`. diff --git a/substrate/frame/contracts/fixtures/contracts/invalid_contract_no_call.rs b/substrate/deprecated/hashing/proc-macro/src/lib.rs similarity index 80% rename from substrate/frame/contracts/fixtures/contracts/invalid_contract_no_call.rs rename to substrate/deprecated/hashing/proc-macro/src/lib.rs index 13af3eb22b1f5fd10c3e2b60061d3f7a6cc37c94..21494b2c515b7833f14755605882cbac3518426d 100644 --- a/substrate/frame/contracts/fixtures/contracts/invalid_contract_no_call.rs +++ b/substrate/deprecated/hashing/proc-macro/src/lib.rs @@ -14,12 +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. -//! Valid module but missing the call function -#![no_std] -#![no_main] -extern crate common; +//! This package has been deprecated. Please use `sp-crypto-hashing-proc-macro`. +//! +//! Removal scheduled after June 2024. -#[no_mangle] -#[polkavm_derive::polkavm_export] -pub extern "C" fn deploy() {} +pub use sp_crypto_hashing_proc_macro::*; diff --git a/substrate/deprecated/hashing/src/lib.rs b/substrate/deprecated/hashing/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..b054312ae2ee761b2103589abc91ad14c91b4c1c --- /dev/null +++ b/substrate/deprecated/hashing/src/lib.rs @@ -0,0 +1,22 @@ +// 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 package has been deprecated. Please use `sp-crypto-hashing`. +//! +//! Removal scheduled after June 2024. + +pub use sp_crypto_hashing::*; diff --git a/substrate/frame/Cargo.toml b/substrate/frame/Cargo.toml index 81642ce14a163af82d53c158d7294d647956ce3a..6746723e72f79498cf764199866936b93373858e 100644 --- a/substrate/frame/Cargo.toml +++ b/substrate/frame/Cargo.toml @@ -19,8 +19,12 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # external deps -parity-scale-codec = { version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } +parity-scale-codec = { version = "3.2.2", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.6.0", default-features = false, features = [ + "derive", +] } # primitive deps, used for developing FRAME pallets. sp-runtime = { default-features = false, path = "../primitives/runtime" } @@ -47,9 +51,8 @@ sp-inherents = { default-features = false, path = "../primitives/inherents", opt frame-executive = { default-features = false, path = "../frame/executive", optional = true } frame-system-rpc-runtime-api = { default-features = false, path = "../frame/system/rpc/runtime-api", optional = true } -docify = "0.2.0" -simple-mermaid = { git = "https://github.com/kianenigma/simple-mermaid.git", rev = "e48b187bcfd5cc75111acd9d241f1bd36604344b", optional = true } -log = { version = "0.4.20", default-features = false } +docify = "0.2.7" +log = { workspace = true } [dev-dependencies] pallet-examples = { path = "./examples" } @@ -58,8 +61,6 @@ pallet-examples = { path = "./examples" } default = ["runtime", "std"] experimental = ["frame-support/experimental"] runtime = [ - "frame-executive", - "frame-system-rpc-runtime-api", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -69,6 +70,9 @@ runtime = [ "sp-session", "sp-transaction-pool", "sp-version", + + "frame-executive", + "frame-system-rpc-runtime-api", ] std = [ "frame-executive?/std", @@ -78,7 +82,6 @@ std = [ "log/std", "parity-scale-codec/std", "scale-info/std", - "simple-mermaid", "sp-api?/std", "sp-arithmetic/std", "sp-block-builder?/std", diff --git a/substrate/frame/alliance/Cargo.toml b/substrate/frame/alliance/Cargo.toml index 39f5a6ceb756c4659d70c9a18e4b4f33d26258af..bc873ad69c803a9f686d5329cffde11c76e8dc35 100644 --- a/substrate/frame/alliance/Cargo.toml +++ b/substrate/frame/alliance/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-alliance" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,14 +17,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { version = "6.1", optional = true } -log = { version = "0.4.14", default-features = false } +log = { workspace = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } sp-std = { path = "../../primitives/std", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } -sp-core-hashing = { path = "../../primitives/core/hashing", default-features = false, optional = true } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing", default-features = false, optional = true } sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } @@ -37,7 +37,7 @@ pallet-collective = { path = "../collective", default-features = false, optional [dev-dependencies] array-bytes = "6.1" -sp-core-hashing = { path = "../../primitives/core/hashing", default-features = false } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing", default-features = false } pallet-balances = { path = "../balances" } pallet-collective = { path = "../collective" } @@ -53,8 +53,8 @@ std = [ "pallet-collective?/std", "pallet-identity/std", "scale-info/std", - "sp-core-hashing?/std", "sp-core/std", + "sp-crypto-hashing?/std", "sp-io/std", "sp-runtime/std", "sp-std/std", @@ -67,7 +67,7 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-collective/runtime-benchmarks", "pallet-identity/runtime-benchmarks", - "sp-core-hashing", + "sp-crypto-hashing", "sp-runtime/runtime-benchmarks", ] try-runtime = [ diff --git a/substrate/frame/alliance/src/benchmarking.rs b/substrate/frame/alliance/src/benchmarking.rs index cb2a04f17c57f47611da5d4a63ec869c6be34c27..4ccd0fc08f6ac1c8ac7e6009b555a470eb4a2779 100644 --- a/substrate/frame/alliance/src/benchmarking.rs +++ b/substrate/frame/alliance/src/benchmarking.rs @@ -19,15 +19,14 @@ #![cfg(feature = "runtime-benchmarks")] -use sp_runtime::traits::{Bounded, Hash, StaticLookup}; -use sp_std::{ +use core::{ cmp, convert::{TryFrom, TryInto}, mem::size_of, - prelude::*, }; +use sp_runtime::traits::{Bounded, Hash, StaticLookup}; -use frame_benchmarking::{account, impl_benchmark_test_suite, v2::*, BenchmarkError}; +use frame_benchmarking::{account, v2::*, BenchmarkError}; use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable}; use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin as SystemOrigin}; @@ -42,7 +41,7 @@ fn assert_last_event, I: 'static>(generic_event: >:: } fn cid(input: impl AsRef<[u8]>) -> Cid { - let result = sp_core_hashing::sha2_256(input.as_ref()); + let result = sp_crypto_hashing::sha2_256(input.as_ref()); Cid::new_v0(result) } diff --git a/substrate/frame/alliance/src/migration.rs b/substrate/frame/alliance/src/migration.rs index e3a44a7887e976cfa6c5d0cfd48c251348edb26b..432f09a16f47772ead741a0f312eefeb795e2d69 100644 --- a/substrate/frame/alliance/src/migration.rs +++ b/substrate/frame/alliance/src/migration.rs @@ -19,19 +19,19 @@ use crate::{Config, Pallet, Weight, LOG_TARGET}; use frame_support::{pallet_prelude::*, storage::migration, traits::OnRuntimeUpgrade}; use log; -/// The current storage version. +/// The in-code storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); /// Wrapper for all migrations of this pallet. pub fn migrate, I: 'static>() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); let mut weight: Weight = Weight::zero(); - if onchain_version < 1 { + if on_chain_version < 1 { weight = weight.saturating_add(v0_to_v1::migrate::()); } - if onchain_version < 2 { + if on_chain_version < 2 { weight = weight.saturating_add(v1_to_v2::migrate::()); } diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs index 22aea9005efaf9ce6857a2547009afd5ff98355c..fd44d33ef93ce3a212d67847f021f2f91077d461 100644 --- a/substrate/frame/alliance/src/mock.rs +++ b/substrate/frame/alliance/src/mock.rs @@ -17,13 +17,13 @@ //! Test utilities +use core::convert::{TryFrom, TryInto}; pub use sp_core::H256; use sp_runtime::traits::Hash; pub use sp_runtime::{ - traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Lazy, Verify}, - BuildStorage, MultiSignature, + traits::{BlakeTwo256, IdentifyAccount, Lazy, Verify}, + BuildStorage, }; -use sp_std::convert::{TryFrom, TryInto}; pub use frame_support::{ assert_noop, assert_ok, derive_impl, ord_parameter_types, parameter_types, @@ -51,7 +51,7 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { type Block = Block; - type AccountData = pallet_balances::AccountData; + type AccountData = pallet_balances::AccountData; } parameter_types! { @@ -72,7 +72,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } const MOTION_DURATION_IN_BLOCKS: BlockNumber = 3; @@ -209,7 +208,7 @@ impl ProposalProvider for AllianceProposalProvider } fn proposal_of(proposal_hash: H256) -> Option { - AllianceMotion::proposal_of(proposal_hash) + pallet_collective::ProposalOf::::get(proposal_hash) } } @@ -388,7 +387,7 @@ pub fn new_bench_ext() -> sp_io::TestExternalities { } pub fn test_cid() -> Cid { - let result = sp_core_hashing::sha2_256(b"hello world"); + let result = sp_crypto_hashing::sha2_256(b"hello world"); Cid::new_v0(result) } diff --git a/substrate/frame/alliance/src/tests.rs b/substrate/frame/alliance/src/tests.rs index 8011627b237af15e8d30379bf14afc2564421050..710de5a54bc95a607b51f4078db94d7c24c92272 100644 --- a/substrate/frame/alliance/src/tests.rs +++ b/substrate/frame/alliance/src/tests.rs @@ -187,8 +187,8 @@ fn propose_works() { Box::new(proposal.clone()), proposal_len )); - assert_eq!(*AllianceMotion::proposals(), vec![hash]); - assert_eq!(AllianceMotion::proposal_of(&hash), Some(proposal)); + assert_eq!(*pallet_collective::Proposals::::get(), vec![hash]); + assert_eq!(pallet_collective::ProposalOf::::get(&hash), Some(proposal)); assert_eq!( System::events(), vec![EventRecord { diff --git a/substrate/frame/alliance/src/weights.rs b/substrate/frame/alliance/src/weights.rs index b5bb50957207f6c75cbca0479d97127ffcca572a..0b2d1fca43ca68d3691ee533cf451cc560108390 100644 --- a/substrate/frame/alliance/src/weights.rs +++ b/substrate/frame/alliance/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_alliance +//! Autogenerated weights for `pallet_alliance` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/alliance/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/alliance/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_alliance. +/// Weight functions needed for `pallet_alliance`. pub trait WeightInfo { fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight; fn vote(m: u32, ) -> Weight; @@ -74,205 +73,209 @@ pub trait WeightInfo { fn abdicate_fellow_status() -> Weight; } -/// Weights for pallet_alliance using the Substrate node and recommended hardware. +/// Weights for `pallet_alliance` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion ProposalOf (r:1 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalCount (r:1 w:1) - /// Proof Skipped: AllianceMotion ProposalCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Voting (r:0 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`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::ProposalCount` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalCount` (`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 `b` is `[1, 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: `653 + m * (32 ±0) + p * (35 ±0)` + // Measured: `654 + m * (32 ±0) + p * (36 ±0)` // Estimated: `6676 + m * (32 ±0) + p * (36 ±0)` - // Minimum execution time: 36_908_000 picoseconds. - Weight::from_parts(39_040_304, 6676) - // Standard Error: 131 - .saturating_add(Weight::from_parts(781, 0).saturating_mul(b.into())) - // Standard Error: 1_375 - .saturating_add(Weight::from_parts(48_745, 0).saturating_mul(m.into())) - // Standard Error: 1_358 - .saturating_add(Weight::from_parts(148_047, 0).saturating_mul(p.into())) + // Minimum execution time: 30_801_000 picoseconds. + Weight::from_parts(32_942_969, 6676) + // Standard Error: 112 + .saturating_add(Weight::from_parts(614, 0).saturating_mul(b.into())) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(45_758, 0).saturating_mul(m.into())) + // Standard Error: 1_162 + .saturating_add(Weight::from_parts(136_600, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1042 + m * (64 ±0)` + // Measured: `1113 + m * (64 ±0)` // Estimated: `6676 + m * (64 ±0)` - // Minimum execution time: 30_166_000 picoseconds. - Weight::from_parts(32_798_454, 6676) - // Standard Error: 1_432 - .saturating_add(Weight::from_parts(83_001, 0).saturating_mul(m.into())) + // Minimum execution time: 29_705_000 picoseconds. + Weight::from_parts(30_274_070, 6676) + // Standard Error: 884 + .saturating_add(Weight::from_parts(71_178, 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())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:0 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `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::ProposalOf` (r:0 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `576 + m * (96 ±0) + p * (36 ±0)` + // Measured: `640 + m * (96 ±0) + p * (36 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` - // Minimum execution time: 45_173_000 picoseconds. - Weight::from_parts(42_192_020, 6676) - // Standard Error: 1_456 - .saturating_add(Weight::from_parts(66_751, 0).saturating_mul(m.into())) - // Standard Error: 1_420 - .saturating_add(Weight::from_parts(158_161, 0).saturating_mul(p.into())) + // Minimum execution time: 38_596_000 picoseconds. + Weight::from_parts(36_445_536, 6676) + // Standard Error: 1_217 + .saturating_add(Weight::from_parts(69_976, 0).saturating_mul(m.into())) + // Standard Error: 1_187 + .saturating_add(Weight::from_parts(149_706, 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, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:1 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:1) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[4, 100]`. /// 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: `1087 + m * (96 ±0) + p * (39 ±0)` + // Measured: `1220 + m * (96 ±0) + p * (39 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (40 ±0)` - // Minimum execution time: 58_290_000 picoseconds. - Weight::from_parts(54_924_919, 6676) - // Standard Error: 157 - .saturating_add(Weight::from_parts(464, 0).saturating_mul(b.into())) - // Standard Error: 1_665 - .saturating_add(Weight::from_parts(73_183, 0).saturating_mul(m.into())) - // Standard Error: 1_623 - .saturating_add(Weight::from_parts(168_318, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Minimum execution time: 57_602_000 picoseconds. + Weight::from_parts(55_147_214, 6676) + // Standard Error: 127 + .saturating_add(Weight::from_parts(1_650, 0).saturating_mul(b.into())) + // Standard Error: 1_346 + .saturating_add(Weight::from_parts(56_056, 0).saturating_mul(m.into())) + // Standard Error: 1_312 + .saturating_add(Weight::from_parts(168_247, 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, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:1 w:0) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:0 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:1 w:0) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `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::ProposalOf` (r:0 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `577 + m * (96 ±0) + p * (36 ±0)` + // Measured: `641 + m * (96 ±0) + p * (36 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` - // Minimum execution time: 46_794_000 picoseconds. - Weight::from_parts(43_092_958, 6676) - // Standard Error: 1_273 - .saturating_add(Weight::from_parts(71_054, 0).saturating_mul(m.into())) - // Standard Error: 1_257 - .saturating_add(Weight::from_parts(152_820, 0).saturating_mul(p.into())) + // Minimum execution time: 40_755_000 picoseconds. + Weight::from_parts(36_953_935, 6676) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(73_240, 0).saturating_mul(m.into())) + // Standard Error: 1_162 + .saturating_add(Weight::from_parts(149_412, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:1 w:0) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:0 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:1 w:0) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `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::ProposalOf` (r:0 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[5, 100]`. /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `684 + m * (96 ±0) + p * (35 ±0)` + // Measured: `694 + m * (96 ±0) + p * (35 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` - // Minimum execution time: 47_338_000 picoseconds. - Weight::from_parts(41_257_479, 6676) - // Standard Error: 119 - .saturating_add(Weight::from_parts(1_019, 0).saturating_mul(b.into())) - // Standard Error: 1_277 - .saturating_add(Weight::from_parts(78_453, 0).saturating_mul(m.into())) - // Standard Error: 1_231 - .saturating_add(Weight::from_parts(150_991, 0).saturating_mul(p.into())) + // Minimum execution time: 41_113_000 picoseconds. + Weight::from_parts(36_610_116, 6676) + // Standard Error: 92 + .saturating_add(Weight::from_parts(1_157, 0).saturating_mul(b.into())) + // Standard Error: 984 + .saturating_add(Weight::from_parts(63_050, 0).saturating_mul(m.into())) + // Standard Error: 949 + .saturating_add(Weight::from_parts(150_420, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:2 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Members (r:1 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:2 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Members` (r:1 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. /// The range of component `z` is `[0, 100]`. fn init_members(m: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `217` + // Measured: `250` // Estimated: `12362` - // Minimum execution time: 35_012_000 picoseconds. - Weight::from_parts(24_288_079, 12362) - // Standard Error: 878 - .saturating_add(Weight::from_parts(153_615, 0).saturating_mul(m.into())) - // Standard Error: 867 - .saturating_add(Weight::from_parts(129_307, 0).saturating_mul(z.into())) + // Minimum execution time: 30_249_000 picoseconds. + Weight::from_parts(21_364_868, 12362) + // Standard Error: 887 + .saturating_add(Weight::from_parts(131_624, 0).saturating_mul(m.into())) + // Standard Error: 877 + .saturating_add(Weight::from_parts(105_379, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Alliance Members (r:2 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Alliance DepositOf (r:200 w:50) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) - /// Storage: System Account (r:50 w:50) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:2 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Alliance::DepositOf` (r:200 w:50) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:50 w:50) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `x` is `[1, 100]`. /// The range of component `y` is `[0, 100]`. /// The range of component `z` is `[0, 50]`. @@ -280,14 +283,14 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (251 ±0)` // Estimated: `12362 + x * (2539 ±0) + y * (2539 ±0) + z * (2603 ±1)` - // Minimum execution time: 309_235_000 picoseconds. - Weight::from_parts(311_279_000, 12362) - // Standard Error: 26_510 - .saturating_add(Weight::from_parts(543_475, 0).saturating_mul(x.into())) - // Standard Error: 26_382 - .saturating_add(Weight::from_parts(603_169, 0).saturating_mul(y.into())) - // Standard Error: 52_716 - .saturating_add(Weight::from_parts(16_264_836, 0).saturating_mul(z.into())) + // Minimum execution time: 307_414_000 picoseconds. + Weight::from_parts(309_960_000, 12362) + // Standard Error: 29_278 + .saturating_add(Weight::from_parts(588_774, 0).saturating_mul(x.into())) + // Standard Error: 29_137 + .saturating_add(Weight::from_parts(563_245, 0).saturating_mul(y.into())) + // Standard Error: 58_221 + .saturating_add(Weight::from_parts(13_947_604, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(y.into()))) @@ -298,397 +301,401 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(0, 2539).saturating_mul(y.into())) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(z.into())) } - /// Storage: Alliance Rule (r:0 w:1) - /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) + /// Storage: `Alliance::Rule` (r:0 w:1) + /// Proof: `Alliance::Rule` (`max_values`: Some(1), `max_size`: Some(87), added: 582, mode: `MaxEncodedLen`) fn set_rule() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_833_000 picoseconds. - Weight::from_parts(9_313_000, 0) + // Minimum execution time: 6_156_000 picoseconds. + Weight::from_parts(6_560_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Alliance Announcements (r:1 w:1) - /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) + /// Storage: `Alliance::Announcements` (r:1 w:1) + /// Proof: `Alliance::Announcements` (`max_values`: Some(1), `max_size`: Some(8702), added: 9197, mode: `MaxEncodedLen`) fn announce() -> Weight { // Proof Size summary in bytes: - // Measured: `246` + // Measured: `279` // Estimated: `10187` - // Minimum execution time: 12_231_000 picoseconds. - Weight::from_parts(12_761_000, 10187) + // Minimum execution time: 8_988_000 picoseconds. + Weight::from_parts(9_476_000, 10187) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Alliance Announcements (r:1 w:1) - /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) + /// Storage: `Alliance::Announcements` (r:1 w:1) + /// Proof: `Alliance::Announcements` (`max_values`: Some(1), `max_size`: Some(8702), added: 9197, mode: `MaxEncodedLen`) fn remove_announcement() -> Weight { // Proof Size summary in bytes: - // Measured: `319` + // Measured: `352` // Estimated: `10187` - // Minimum execution time: 13_079_000 picoseconds. - Weight::from_parts(13_612_000, 10187) + // Minimum execution time: 10_126_000 picoseconds. + Weight::from_parts(10_755_000, 10187) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Alliance Members (r:3 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Alliance DepositOf (r:0 w:1) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: `Alliance::Members` (r:3 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:0) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Alliance::DepositOf` (r:0 w:1) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) fn join_alliance() -> Weight { // Proof Size summary in bytes: - // Measured: `468` + // Measured: `501` // Estimated: `18048` - // Minimum execution time: 44_574_000 picoseconds. - Weight::from_parts(46_157_000, 18048) + // Minimum execution time: 38_878_000 picoseconds. + Weight::from_parts(40_493_000, 18048) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Alliance Members (r:3 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: `Alliance::Members` (r:3 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:0) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) fn nominate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `367` + // Measured: `400` // Estimated: `18048` - // Minimum execution time: 26_114_000 picoseconds. - Weight::from_parts(27_069_000, 18048) + // Minimum execution time: 23_265_000 picoseconds. + Weight::from_parts(24_703_000, 18048) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Alliance Members (r:2 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:2 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn elevate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `443` + // Measured: `476` // Estimated: `12362` - // Minimum execution time: 25_882_000 picoseconds. - Weight::from_parts(26_923_000, 12362) + // Minimum execution time: 23_049_000 picoseconds. + Weight::from_parts(23_875_000, 12362) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Alliance Members (r:4 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Alliance RetiringMembers (r:0 w:1) - /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Alliance::Members` (r:4 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Alliance::RetiringMembers` (r:0 w:1) + /// Proof: `Alliance::RetiringMembers` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn give_retirement_notice() -> Weight { // Proof Size summary in bytes: - // Measured: `443` + // Measured: `476` // Estimated: `23734` - // Minimum execution time: 34_112_000 picoseconds. - Weight::from_parts(35_499_000, 23734) + // Minimum execution time: 29_124_000 picoseconds. + Weight::from_parts(30_369_000, 23734) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Alliance RetiringMembers (r:1 w:1) - /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: Alliance Members (r:1 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: Alliance DepositOf (r:1 w:1) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Alliance::RetiringMembers` (r:1 w:1) + /// Proof: `Alliance::RetiringMembers` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Alliance::Members` (r:1 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `Alliance::DepositOf` (r:1 w:1) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn retire() -> Weight { // Proof Size summary in bytes: - // Measured: `687` + // Measured: `720` // Estimated: `6676` - // Minimum execution time: 41_239_000 picoseconds. - Weight::from_parts(42_764_000, 6676) + // Minimum execution time: 36_376_000 picoseconds. + Weight::from_parts(38_221_000, 6676) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Alliance Members (r:3 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Alliance DepositOf (r:1 w:1) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:3 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Alliance::DepositOf` (r:1 w:1) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn kick_member() -> Weight { // Proof Size summary in bytes: - // Measured: `707` + // Measured: `740` // Estimated: `18048` - // Minimum execution time: 68_071_000 picoseconds. - Weight::from_parts(71_808_000, 18048) + // Minimum execution time: 56_560_000 picoseconds. + Weight::from_parts(58_621_000, 18048) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) - /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousWebsites` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousWebsites` (`max_values`: Some(1), `max_size`: Some(25702), added: 26197, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `246` + // Measured: `279` // Estimated: `27187` - // Minimum execution time: 7_006_000 picoseconds. - Weight::from_parts(7_253_000, 27187) - // Standard Error: 3_403 - .saturating_add(Weight::from_parts(1_680_082, 0).saturating_mul(n.into())) - // Standard Error: 1_333 - .saturating_add(Weight::from_parts(72_943, 0).saturating_mul(l.into())) + // Minimum execution time: 5_191_000 picoseconds. + Weight::from_parts(5_410_000, 27187) + // Standard Error: 3_215 + .saturating_add(Weight::from_parts(1_018_569, 0).saturating_mul(n.into())) + // Standard Error: 1_259 + .saturating_add(Weight::from_parts(68_712, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) - /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousWebsites` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousWebsites` (`max_values`: Some(1), `max_size`: Some(25702), added: 26197, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + l * (100 ±0) + n * (289 ±0)` // Estimated: `27187` - // Minimum execution time: 7_292_000 picoseconds. - Weight::from_parts(7_629_000, 27187) - // Standard Error: 176_225 - .saturating_add(Weight::from_parts(16_646_429, 0).saturating_mul(n.into())) - // Standard Error: 69_017 - .saturating_add(Weight::from_parts(310_978, 0).saturating_mul(l.into())) + // Minimum execution time: 5_361_000 picoseconds. + Weight::from_parts(5_494_000, 27187) + // Standard Error: 181_133 + .saturating_add(Weight::from_parts(16_322_982, 0).saturating_mul(n.into())) + // Standard Error: 70_940 + .saturating_add(Weight::from_parts(343_581, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Alliance Members (r:3 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:3 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn abdicate_fellow_status() -> Weight { // Proof Size summary in bytes: - // Measured: `443` + // Measured: `476` // Estimated: `18048` - // Minimum execution time: 31_798_000 picoseconds. - Weight::from_parts(33_463_000, 18048) + // Minimum execution time: 28_856_000 picoseconds. + Weight::from_parts(29_875_000, 18048) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion ProposalOf (r:1 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalCount (r:1 w:1) - /// Proof Skipped: AllianceMotion ProposalCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Voting (r:0 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`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::ProposalCount` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalCount` (`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 `b` is `[1, 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: `653 + m * (32 ±0) + p * (35 ±0)` + // Measured: `654 + m * (32 ±0) + p * (36 ±0)` // Estimated: `6676 + m * (32 ±0) + p * (36 ±0)` - // Minimum execution time: 36_908_000 picoseconds. - Weight::from_parts(39_040_304, 6676) - // Standard Error: 131 - .saturating_add(Weight::from_parts(781, 0).saturating_mul(b.into())) - // Standard Error: 1_375 - .saturating_add(Weight::from_parts(48_745, 0).saturating_mul(m.into())) - // Standard Error: 1_358 - .saturating_add(Weight::from_parts(148_047, 0).saturating_mul(p.into())) + // Minimum execution time: 30_801_000 picoseconds. + Weight::from_parts(32_942_969, 6676) + // Standard Error: 112 + .saturating_add(Weight::from_parts(614, 0).saturating_mul(b.into())) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(45_758, 0).saturating_mul(m.into())) + // Standard Error: 1_162 + .saturating_add(Weight::from_parts(136_600, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1042 + m * (64 ±0)` + // Measured: `1113 + m * (64 ±0)` // Estimated: `6676 + m * (64 ±0)` - // Minimum execution time: 30_166_000 picoseconds. - Weight::from_parts(32_798_454, 6676) - // Standard Error: 1_432 - .saturating_add(Weight::from_parts(83_001, 0).saturating_mul(m.into())) + // Minimum execution time: 29_705_000 picoseconds. + Weight::from_parts(30_274_070, 6676) + // Standard Error: 884 + .saturating_add(Weight::from_parts(71_178, 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())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:0 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `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::ProposalOf` (r:0 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `576 + m * (96 ±0) + p * (36 ±0)` + // Measured: `640 + m * (96 ±0) + p * (36 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` - // Minimum execution time: 45_173_000 picoseconds. - Weight::from_parts(42_192_020, 6676) - // Standard Error: 1_456 - .saturating_add(Weight::from_parts(66_751, 0).saturating_mul(m.into())) - // Standard Error: 1_420 - .saturating_add(Weight::from_parts(158_161, 0).saturating_mul(p.into())) + // Minimum execution time: 38_596_000 picoseconds. + Weight::from_parts(36_445_536, 6676) + // Standard Error: 1_217 + .saturating_add(Weight::from_parts(69_976, 0).saturating_mul(m.into())) + // Standard Error: 1_187 + .saturating_add(Weight::from_parts(149_706, 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, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:1 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:1) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[4, 100]`. /// 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: `1087 + m * (96 ±0) + p * (39 ±0)` + // Measured: `1220 + m * (96 ±0) + p * (39 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (40 ±0)` - // Minimum execution time: 58_290_000 picoseconds. - Weight::from_parts(54_924_919, 6676) - // Standard Error: 157 - .saturating_add(Weight::from_parts(464, 0).saturating_mul(b.into())) - // Standard Error: 1_665 - .saturating_add(Weight::from_parts(73_183, 0).saturating_mul(m.into())) - // Standard Error: 1_623 - .saturating_add(Weight::from_parts(168_318, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Minimum execution time: 57_602_000 picoseconds. + Weight::from_parts(55_147_214, 6676) + // Standard Error: 127 + .saturating_add(Weight::from_parts(1_650, 0).saturating_mul(b.into())) + // Standard Error: 1_346 + .saturating_add(Weight::from_parts(56_056, 0).saturating_mul(m.into())) + // Standard Error: 1_312 + .saturating_add(Weight::from_parts(168_247, 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, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:1 w:0) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:0 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:1 w:0) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `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::ProposalOf` (r:0 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `577 + m * (96 ±0) + p * (36 ±0)` + // Measured: `641 + m * (96 ±0) + p * (36 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` - // Minimum execution time: 46_794_000 picoseconds. - Weight::from_parts(43_092_958, 6676) - // Standard Error: 1_273 - .saturating_add(Weight::from_parts(71_054, 0).saturating_mul(m.into())) - // Standard Error: 1_257 - .saturating_add(Weight::from_parts(152_820, 0).saturating_mul(p.into())) + // Minimum execution time: 40_755_000 picoseconds. + Weight::from_parts(36_953_935, 6676) + // Standard Error: 1_177 + .saturating_add(Weight::from_parts(73_240, 0).saturating_mul(m.into())) + // Standard Error: 1_162 + .saturating_add(Weight::from_parts(149_412, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:1 w:0) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Voting (r:1 w:1) - /// Proof Skipped: AllianceMotion Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:1 w:0) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:1 w:0) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Proposals (r:1 w:1) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion ProposalOf (r:0 w:1) - /// Proof Skipped: AllianceMotion ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:1 w:0) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Voting` (r:1 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:1 w:0) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:1 w:0) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `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::ProposalOf` (r:0 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 1024]`. /// The range of component `m` is `[5, 100]`. /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `684 + m * (96 ±0) + p * (35 ±0)` + // Measured: `694 + m * (96 ±0) + p * (35 ±0)` // Estimated: `6676 + m * (97 ±0) + p * (36 ±0)` - // Minimum execution time: 47_338_000 picoseconds. - Weight::from_parts(41_257_479, 6676) - // Standard Error: 119 - .saturating_add(Weight::from_parts(1_019, 0).saturating_mul(b.into())) - // Standard Error: 1_277 - .saturating_add(Weight::from_parts(78_453, 0).saturating_mul(m.into())) - // Standard Error: 1_231 - .saturating_add(Weight::from_parts(150_991, 0).saturating_mul(p.into())) + // Minimum execution time: 41_113_000 picoseconds. + Weight::from_parts(36_610_116, 6676) + // Standard Error: 92 + .saturating_add(Weight::from_parts(1_157, 0).saturating_mul(b.into())) + // Standard Error: 984 + .saturating_add(Weight::from_parts(63_050, 0).saturating_mul(m.into())) + // Standard Error: 949 + .saturating_add(Weight::from_parts(150_420, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 97).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Alliance Members (r:2 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Members (r:1 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:2 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Members` (r:1 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. /// The range of component `z` is `[0, 100]`. fn init_members(m: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `217` + // Measured: `250` // Estimated: `12362` - // Minimum execution time: 35_012_000 picoseconds. - Weight::from_parts(24_288_079, 12362) - // Standard Error: 878 - .saturating_add(Weight::from_parts(153_615, 0).saturating_mul(m.into())) - // Standard Error: 867 - .saturating_add(Weight::from_parts(129_307, 0).saturating_mul(z.into())) + // Minimum execution time: 30_249_000 picoseconds. + Weight::from_parts(21_364_868, 12362) + // Standard Error: 887 + .saturating_add(Weight::from_parts(131_624, 0).saturating_mul(m.into())) + // Standard Error: 877 + .saturating_add(Weight::from_parts(105_379, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Alliance Members (r:2 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Alliance DepositOf (r:200 w:50) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) - /// Storage: System Account (r:50 w:50) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:2 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Alliance::DepositOf` (r:200 w:50) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:50 w:50) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `x` is `[1, 100]`. /// The range of component `y` is `[0, 100]`. /// The range of component `z` is `[0, 50]`. @@ -696,14 +703,14 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + x * (50 ±0) + y * (51 ±0) + z * (251 ±0)` // Estimated: `12362 + x * (2539 ±0) + y * (2539 ±0) + z * (2603 ±1)` - // Minimum execution time: 309_235_000 picoseconds. - Weight::from_parts(311_279_000, 12362) - // Standard Error: 26_510 - .saturating_add(Weight::from_parts(543_475, 0).saturating_mul(x.into())) - // Standard Error: 26_382 - .saturating_add(Weight::from_parts(603_169, 0).saturating_mul(y.into())) - // Standard Error: 52_716 - .saturating_add(Weight::from_parts(16_264_836, 0).saturating_mul(z.into())) + // Minimum execution time: 307_414_000 picoseconds. + Weight::from_parts(309_960_000, 12362) + // Standard Error: 29_278 + .saturating_add(Weight::from_parts(588_774, 0).saturating_mul(x.into())) + // Standard Error: 29_137 + .saturating_add(Weight::from_parts(563_245, 0).saturating_mul(y.into())) + // Standard Error: 58_221 + .saturating_add(Weight::from_parts(13_947_604, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(x.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(y.into()))) @@ -714,194 +721,194 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(0, 2539).saturating_mul(y.into())) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(z.into())) } - /// Storage: Alliance Rule (r:0 w:1) - /// Proof: Alliance Rule (max_values: Some(1), max_size: Some(87), added: 582, mode: MaxEncodedLen) + /// Storage: `Alliance::Rule` (r:0 w:1) + /// Proof: `Alliance::Rule` (`max_values`: Some(1), `max_size`: Some(87), added: 582, mode: `MaxEncodedLen`) fn set_rule() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_833_000 picoseconds. - Weight::from_parts(9_313_000, 0) + // Minimum execution time: 6_156_000 picoseconds. + Weight::from_parts(6_560_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Alliance Announcements (r:1 w:1) - /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) + /// Storage: `Alliance::Announcements` (r:1 w:1) + /// Proof: `Alliance::Announcements` (`max_values`: Some(1), `max_size`: Some(8702), added: 9197, mode: `MaxEncodedLen`) fn announce() -> Weight { // Proof Size summary in bytes: - // Measured: `246` + // Measured: `279` // Estimated: `10187` - // Minimum execution time: 12_231_000 picoseconds. - Weight::from_parts(12_761_000, 10187) + // Minimum execution time: 8_988_000 picoseconds. + Weight::from_parts(9_476_000, 10187) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Alliance Announcements (r:1 w:1) - /// Proof: Alliance Announcements (max_values: Some(1), max_size: Some(8702), added: 9197, mode: MaxEncodedLen) + /// Storage: `Alliance::Announcements` (r:1 w:1) + /// Proof: `Alliance::Announcements` (`max_values`: Some(1), `max_size`: Some(8702), added: 9197, mode: `MaxEncodedLen`) fn remove_announcement() -> Weight { // Proof Size summary in bytes: - // Measured: `319` + // Measured: `352` // Estimated: `10187` - // Minimum execution time: 13_079_000 picoseconds. - Weight::from_parts(13_612_000, 10187) + // Minimum execution time: 10_126_000 picoseconds. + Weight::from_parts(10_755_000, 10187) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Alliance Members (r:3 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Alliance DepositOf (r:0 w:1) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// Storage: `Alliance::Members` (r:3 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:0) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Alliance::DepositOf` (r:0 w:1) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) fn join_alliance() -> Weight { // Proof Size summary in bytes: - // Measured: `468` + // Measured: `501` // Estimated: `18048` - // Minimum execution time: 44_574_000 picoseconds. - Weight::from_parts(46_157_000, 18048) + // Minimum execution time: 38_878_000 picoseconds. + Weight::from_parts(40_493_000, 18048) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Alliance Members (r:3 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousAccounts (r:1 w:0) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) + /// Storage: `Alliance::Members` (r:3 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:0) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) fn nominate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `367` + // Measured: `400` // Estimated: `18048` - // Minimum execution time: 26_114_000 picoseconds. - Weight::from_parts(27_069_000, 18048) + // Minimum execution time: 23_265_000 picoseconds. + Weight::from_parts(24_703_000, 18048) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Alliance Members (r:2 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:2 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn elevate_ally() -> Weight { // Proof Size summary in bytes: - // Measured: `443` + // Measured: `476` // Estimated: `12362` - // Minimum execution time: 25_882_000 picoseconds. - Weight::from_parts(26_923_000, 12362) + // Minimum execution time: 23_049_000 picoseconds. + Weight::from_parts(23_875_000, 12362) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Alliance Members (r:4 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Alliance RetiringMembers (r:0 w:1) - /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Alliance::Members` (r:4 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Alliance::RetiringMembers` (r:0 w:1) + /// Proof: `Alliance::RetiringMembers` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn give_retirement_notice() -> Weight { // Proof Size summary in bytes: - // Measured: `443` + // Measured: `476` // Estimated: `23734` - // Minimum execution time: 34_112_000 picoseconds. - Weight::from_parts(35_499_000, 23734) + // Minimum execution time: 29_124_000 picoseconds. + Weight::from_parts(30_369_000, 23734) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Alliance RetiringMembers (r:1 w:1) - /// Proof: Alliance RetiringMembers (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: Alliance Members (r:1 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: Alliance DepositOf (r:1 w:1) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Alliance::RetiringMembers` (r:1 w:1) + /// Proof: `Alliance::RetiringMembers` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Alliance::Members` (r:1 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `Alliance::DepositOf` (r:1 w:1) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn retire() -> Weight { // Proof Size summary in bytes: - // Measured: `687` + // Measured: `720` // Estimated: `6676` - // Minimum execution time: 41_239_000 picoseconds. - Weight::from_parts(42_764_000, 6676) + // Minimum execution time: 36_376_000 picoseconds. + Weight::from_parts(38_221_000, 6676) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Alliance Members (r:3 w:1) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Alliance DepositOf (r:1 w:1) - /// Proof: Alliance DepositOf (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:3 w:1) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Alliance::DepositOf` (r:1 w:1) + /// Proof: `Alliance::DepositOf` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn kick_member() -> Weight { // Proof Size summary in bytes: - // Measured: `707` + // Measured: `740` // Estimated: `18048` - // Minimum execution time: 68_071_000 picoseconds. - Weight::from_parts(71_808_000, 18048) + // Minimum execution time: 56_560_000 picoseconds. + Weight::from_parts(58_621_000, 18048) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) - /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousWebsites` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousWebsites` (`max_values`: Some(1), `max_size`: Some(25702), added: 26197, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn add_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `246` + // Measured: `279` // Estimated: `27187` - // Minimum execution time: 7_006_000 picoseconds. - Weight::from_parts(7_253_000, 27187) - // Standard Error: 3_403 - .saturating_add(Weight::from_parts(1_680_082, 0).saturating_mul(n.into())) - // Standard Error: 1_333 - .saturating_add(Weight::from_parts(72_943, 0).saturating_mul(l.into())) + // Minimum execution time: 5_191_000 picoseconds. + Weight::from_parts(5_410_000, 27187) + // Standard Error: 3_215 + .saturating_add(Weight::from_parts(1_018_569, 0).saturating_mul(n.into())) + // Standard Error: 1_259 + .saturating_add(Weight::from_parts(68_712, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Alliance UnscrupulousAccounts (r:1 w:1) - /// Proof: Alliance UnscrupulousAccounts (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: Alliance UnscrupulousWebsites (r:1 w:1) - /// Proof: Alliance UnscrupulousWebsites (max_values: Some(1), max_size: Some(25702), added: 26197, mode: MaxEncodedLen) + /// Storage: `Alliance::UnscrupulousAccounts` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousAccounts` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `Alliance::UnscrupulousWebsites` (r:1 w:1) + /// Proof: `Alliance::UnscrupulousWebsites` (`max_values`: Some(1), `max_size`: Some(25702), added: 26197, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. /// The range of component `l` is `[0, 255]`. fn remove_unscrupulous_items(n: u32, l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + l * (100 ±0) + n * (289 ±0)` // Estimated: `27187` - // Minimum execution time: 7_292_000 picoseconds. - Weight::from_parts(7_629_000, 27187) - // Standard Error: 176_225 - .saturating_add(Weight::from_parts(16_646_429, 0).saturating_mul(n.into())) - // Standard Error: 69_017 - .saturating_add(Weight::from_parts(310_978, 0).saturating_mul(l.into())) + // Minimum execution time: 5_361_000 picoseconds. + Weight::from_parts(5_494_000, 27187) + // Standard Error: 181_133 + .saturating_add(Weight::from_parts(16_322_982, 0).saturating_mul(n.into())) + // Standard Error: 70_940 + .saturating_add(Weight::from_parts(343_581, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Alliance Members (r:3 w:2) - /// Proof: Alliance Members (max_values: None, max_size: Some(3211), added: 5686, mode: MaxEncodedLen) - /// Storage: AllianceMotion Proposals (r:1 w:0) - /// Proof Skipped: AllianceMotion Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Members (r:0 w:1) - /// Proof Skipped: AllianceMotion Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: AllianceMotion Prime (r:0 w:1) - /// Proof Skipped: AllianceMotion Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Alliance::Members` (r:3 w:2) + /// Proof: `Alliance::Members` (`max_values`: None, `max_size`: Some(3211), added: 5686, mode: `MaxEncodedLen`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:0) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Members` (r:0 w:1) + /// Proof: `AllianceMotion::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Prime` (r:0 w:1) + /// Proof: `AllianceMotion::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn abdicate_fellow_status() -> Weight { // Proof Size summary in bytes: - // Measured: `443` + // Measured: `476` // Estimated: `18048` - // Minimum execution time: 31_798_000 picoseconds. - Weight::from_parts(33_463_000, 18048) + // Minimum execution time: 28_856_000 picoseconds. + Weight::from_parts(29_875_000, 18048) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/substrate/frame/asset-conversion/Cargo.toml b/substrate/frame/asset-conversion/Cargo.toml index 0c7b06abf55d01c68dcf16f08bcdb00dcc04b0ea..1f2db14dac2dbe39dc16ddc206af6eb18fc4ca45 100644 --- a/substrate/frame/asset-conversion/Cargo.toml +++ b/substrate/frame/asset-conversion/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-asset-conversion" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index f0695678fbddf07a4943c4ad0982ac95c4634866..f13d40d3e7e2c74edc805ed2d4cb8154bb507480 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -40,7 +40,7 @@ //! non-native asset 1, you would pass in a path of `[DOT, 1]` or `[1, DOT]`. If you want to swap //! from non-native asset 1 to non-native asset 2, you would pass in a path of `[1, DOT, 2]`. //! -//! (For an example of configuring this pallet to use `MultiLocation` as an asset id, see the +//! (For an example of configuring this pallet to use `Location` as an asset id, see the //! cumulus repo). //! //! Here is an example `state_call` that asks for a quote of a pool of native versus asset 1: diff --git a/substrate/frame/asset-conversion/src/mock.rs b/substrate/frame/asset-conversion/src/mock.rs index 12c8fe2eb42cb4ae0a67dddce9bab40a1b121ebb..870538a68cc7105802672972bdf394e36f7bbc87 100644 --- a/substrate/frame/asset-conversion/src/mock.rs +++ b/substrate/frame/asset-conversion/src/mock.rs @@ -19,6 +19,7 @@ use super::*; use crate as pallet_asset_conversion; +use core::default::Default; use frame_support::{ construct_runtime, derive_impl, instances::{Instance1, Instance2}, @@ -28,18 +29,16 @@ use frame_support::{ fungible::{NativeFromLeft, NativeOrWithId, UnionOf}, imbalance::ResolveAssetTo, }, - AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64, + AsEnsureOriginWithArg, ConstU128, ConstU32, }, PalletId, }; use frame_system::{EnsureSigned, EnsureSignedBy}; use sp_arithmetic::Permill; -use sp_core::H256; use sp_runtime::{ - traits::{AccountIdConversion, BlakeTwo256, IdentityLookup}, + traits::{AccountIdConversion, IdentityLookup}, BuildStorage, }; -use sp_std::default::Default; type Block = frame_system::mocking::MockBlock; @@ -56,29 +55,10 @@ construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = u128; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -95,7 +75,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_assets::Config for Test { diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index fd6d41a55b6139b74453861fd553f0d30d307146..5ee81c2012de226baf6aafbda0e2ec9a380cc1e2 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -17,8 +17,8 @@ use super::*; use codec::{Decode, Encode, MaxEncodedLen}; +use core::marker::PhantomData; use scale_info::TypeInfo; -use sp_std::marker::PhantomData; /// Represents a swap path with associated asset amounts indicating how much of the asset needs to /// be deposited to get the following asset's amount withdrawn (this is inclusive of fees). diff --git a/substrate/frame/asset-conversion/src/weights.rs b/substrate/frame/asset-conversion/src/weights.rs index a0e687f7a4168032c59daf8616249776a123c363..4eaa115c694d27712384a5ad2ccae742609aec76 100644 --- a/substrate/frame/asset-conversion/src/weights.rs +++ b/substrate/frame/asset-conversion/src/weights.rs @@ -17,24 +17,28 @@ //! Autogenerated weights for `pallet_asset_conversion` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-30, STEPS: `5`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cob`, CPU: `` +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/debug/substrate-node +// ./target/production/substrate-node // benchmark // pallet // --chain=dev -// --steps=5 -// --repeat=2 -// --pallet=pallet-asset-conversion +// --steps=50 +// --repeat=20 +// --pallet=pallet_asset_conversion +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 // --output=./substrate/frame/asset-conversion/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -59,11 +63,9 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) + /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:2 w:2) + /// Storage: `Assets::Asset` (r:2 w:0) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -73,12 +75,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn create_pool() -> Weight { // Proof Size summary in bytes: - // Measured: `1081` + // Measured: `910` // Estimated: `6360` - // Minimum execution time: 1_576_000_000 picoseconds. - Weight::from_parts(1_668_000_000, 6360) - .saturating_add(T::DbWeight::get().reads(10_u64)) - .saturating_add(T::DbWeight::get().writes(10_u64)) + // Minimum execution time: 82_941_000 picoseconds. + Weight::from_parts(85_526_000, 6360) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) @@ -86,18 +88,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `Assets::Account` (r:4 w:4) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Account` (r:2 w:2) /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn add_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1761` + // Measured: `1507` // Estimated: `11426` - // Minimum execution time: 1_636_000_000 picoseconds. - Weight::from_parts(1_894_000_000, 11426) - .saturating_add(T::DbWeight::get().reads(10_u64)) - .saturating_add(T::DbWeight::get().writes(9_u64)) + // Minimum execution time: 138_424_000 picoseconds. + Weight::from_parts(142_083_000, 11426) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) @@ -111,10 +115,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn remove_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1750` + // Measured: `1650` // Estimated: `11426` - // Minimum execution time: 1_507_000_000 picoseconds. - Weight::from_parts(1_524_000_000, 11426) + // Minimum execution time: 122_132_000 picoseconds. + Weight::from_parts(125_143_000, 11426) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -125,12 +129,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[2, 4]`. fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + n * (522 ±0)` + // Measured: `89 + n * (419 ±0)` // Estimated: `990 + n * (5218 ±0)` - // Minimum execution time: 937_000_000 picoseconds. - Weight::from_parts(941_000_000, 990) - // Standard Error: 40_863_477 - .saturating_add(Weight::from_parts(205_862_068, 0).saturating_mul(n.into())) + // Minimum execution time: 77_183_000 picoseconds. + Weight::from_parts(78_581_000, 990) + // Standard Error: 306_918 + .saturating_add(Weight::from_parts(10_581_054, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) @@ -142,12 +146,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[2, 4]`. fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + n * (522 ±0)` + // Measured: `89 + n * (419 ±0)` // Estimated: `990 + n * (5218 ±0)` - // Minimum execution time: 935_000_000 picoseconds. - Weight::from_parts(947_000_000, 990) - // Standard Error: 46_904_620 - .saturating_add(Weight::from_parts(218_275_862, 0).saturating_mul(n.into())) + // Minimum execution time: 76_962_000 picoseconds. + Weight::from_parts(78_315_000, 990) + // Standard Error: 311_204 + .saturating_add(Weight::from_parts(10_702_400, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) @@ -158,11 +162,9 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) + /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:2 w:2) + /// Storage: `Assets::Asset` (r:2 w:0) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -172,12 +174,12 @@ impl WeightInfo for () { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn create_pool() -> Weight { // Proof Size summary in bytes: - // Measured: `1081` + // Measured: `910` // Estimated: `6360` - // Minimum execution time: 1_576_000_000 picoseconds. - Weight::from_parts(1_668_000_000, 6360) - .saturating_add(RocksDbWeight::get().reads(10_u64)) - .saturating_add(RocksDbWeight::get().writes(10_u64)) + // Minimum execution time: 82_941_000 picoseconds. + Weight::from_parts(85_526_000, 6360) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) @@ -185,18 +187,20 @@ impl WeightInfo for () { /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `Assets::Account` (r:4 w:4) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Account` (r:2 w:2) /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn add_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1761` + // Measured: `1507` // Estimated: `11426` - // Minimum execution time: 1_636_000_000 picoseconds. - Weight::from_parts(1_894_000_000, 11426) - .saturating_add(RocksDbWeight::get().reads(10_u64)) - .saturating_add(RocksDbWeight::get().writes(9_u64)) + // Minimum execution time: 138_424_000 picoseconds. + Weight::from_parts(142_083_000, 11426) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) @@ -210,10 +214,10 @@ impl WeightInfo for () { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn remove_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1750` + // Measured: `1650` // Estimated: `11426` - // Minimum execution time: 1_507_000_000 picoseconds. - Weight::from_parts(1_524_000_000, 11426) + // Minimum execution time: 122_132_000 picoseconds. + Weight::from_parts(125_143_000, 11426) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -224,12 +228,12 @@ impl WeightInfo for () { /// The range of component `n` is `[2, 4]`. fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + n * (522 ±0)` + // Measured: `89 + n * (419 ±0)` // Estimated: `990 + n * (5218 ±0)` - // Minimum execution time: 937_000_000 picoseconds. - Weight::from_parts(941_000_000, 990) - // Standard Error: 40_863_477 - .saturating_add(Weight::from_parts(205_862_068, 0).saturating_mul(n.into())) + // Minimum execution time: 77_183_000 picoseconds. + Weight::from_parts(78_581_000, 990) + // Standard Error: 306_918 + .saturating_add(Weight::from_parts(10_581_054, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) @@ -241,12 +245,12 @@ impl WeightInfo for () { /// The range of component `n` is `[2, 4]`. fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0 + n * (522 ±0)` + // Measured: `89 + n * (419 ±0)` // Estimated: `990 + n * (5218 ±0)` - // Minimum execution time: 935_000_000 picoseconds. - Weight::from_parts(947_000_000, 990) - // Standard Error: 46_904_620 - .saturating_add(Weight::from_parts(218_275_862, 0).saturating_mul(n.into())) + // Minimum execution time: 76_962_000 picoseconds. + Weight::from_parts(78_315_000, 990) + // Standard Error: 311_204 + .saturating_add(Weight::from_parts(10_702_400, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) diff --git a/substrate/frame/asset-rate/Cargo.toml b/substrate/frame/asset-rate/Cargo.toml index 835a15e8c553ad73df116d7660ef293e6bb3f2da..6e7bbf29fc43219dbe084afeb1254a0783510746 100644 --- a/substrate/frame/asset-rate/Cargo.toml +++ b/substrate/frame/asset-rate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-asset-rate" -version = "4.0.0-dev" +version = "7.0.0" description = "Whitelist non-native assets for treasury spending and provide conversion to native balance" authors.workspace = true homepage = "https://substrate.io" diff --git a/substrate/frame/asset-rate/src/lib.rs b/substrate/frame/asset-rate/src/lib.rs index d4afca8b73c4b2978bbed954c05aee4e2a645ee5..befabfe54aa0e766d498eccf2201de4e9c0f2fc9 100644 --- a/substrate/frame/asset-rate/src/lib.rs +++ b/substrate/frame/asset-rate/src/lib.rs @@ -59,8 +59,14 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::traits::{fungible::Inspect, tokens::ConversionFromAssetBalance}; -use sp_runtime::{traits::Zero, FixedPointNumber, FixedU128}; +use frame_support::traits::{ + fungible::Inspect, + tokens::{ConversionFromAssetBalance, ConversionToAssetBalance}, +}; +use sp_runtime::{ + traits::{CheckedDiv, Zero}, + FixedPointNumber, FixedU128, +}; use sp_std::boxed::Box; pub use pallet::*; @@ -144,6 +150,8 @@ pub mod pallet { UnknownAssetKind, /// The given asset ID already has an assigned conversion rate and cannot be re-created. AlreadyExists, + /// Overflow ocurred when calculating the inverse rate. + Overflow, } #[pallet::call] @@ -246,3 +254,25 @@ where pallet::ConversionRateToNative::::set(asset_id.clone(), Some(1.into())); } } + +/// Exposes conversion of a native balance to an asset balance. +impl ConversionToAssetBalance, AssetKindOf, BalanceOf> for Pallet +where + T: Config, +{ + type Error = pallet::Error; + + fn to_asset_balance( + balance: BalanceOf, + asset_kind: AssetKindOf, + ) -> Result, pallet::Error> { + let rate = pallet::ConversionRateToNative::::get(asset_kind) + .ok_or(pallet::Error::::UnknownAssetKind.into())?; + + // We cannot use `saturating_div` here so we use `checked_div`. + Ok(FixedU128::from_u32(1) + .checked_div(&rate) + .ok_or(pallet::Error::::Overflow.into())? + .saturating_mul_int(balance)) + } +} diff --git a/substrate/frame/asset-rate/src/mock.rs b/substrate/frame/asset-rate/src/mock.rs index 041f37409528059a2e6b47549b2f263818eaaa87..5981b05676414e9ec5251be0bcf2e554d589107a 100644 --- a/substrate/frame/asset-rate/src/mock.rs +++ b/substrate/frame/asset-rate/src/mock.rs @@ -18,15 +18,8 @@ //! The crate's mock. use crate as pallet_asset_rate; -use frame_support::{ - derive_impl, - traits::{ConstU16, ConstU64}, -}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use frame_support::{derive_impl, traits::ConstU64}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -41,29 +34,8 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } impl pallet_balances::Config for Test { @@ -79,7 +51,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = (); type MaxFreezes = (); } diff --git a/substrate/frame/asset-rate/src/tests.rs b/substrate/frame/asset-rate/src/tests.rs index 452265476093b034256907e9d2a232c91041f772..fc7bf2f150311b638ba3a7bc939d6b62ba927cc0 100644 --- a/substrate/frame/asset-rate/src/tests.rs +++ b/substrate/frame/asset-rate/src/tests.rs @@ -131,12 +131,19 @@ fn convert_works() { FixedU128::from_float(2.51) )); - let conversion = , ::AssetKind, BalanceOf, >>::from_asset_balance(10, ASSET_ID); - assert_eq!(conversion.expect("Conversion rate exists for asset"), 25); + assert_eq!(conversion_from_asset.expect("Conversion rate exists for asset"), 25); + + let conversion_to_asset = , + ::AssetKind, + BalanceOf, + >>::to_asset_balance(25, ASSET_ID); + assert_eq!(conversion_to_asset.expect("Conversion rate exists for asset"), 9); }); } @@ -151,3 +158,21 @@ fn convert_unknown_throws() { assert!(conversion.is_err()); }); } + +#[test] +fn convert_overflow_throws() { + new_test_ext().execute_with(|| { + assert_ok!(AssetRate::create( + RuntimeOrigin::root(), + Box::new(ASSET_ID), + FixedU128::from_u32(0) + )); + + let conversion = , + ::AssetKind, + BalanceOf, + >>::to_asset_balance(10, ASSET_ID); + assert!(conversion.is_err()); + }); +} diff --git a/substrate/frame/asset-rate/src/weights.rs b/substrate/frame/asset-rate/src/weights.rs index 582e20e56d7dc7f40a0cbb3838622b309e8223d4..b8723ee3bed5cbc124a8e7c1237e5c1af2e5a154 100644 --- a/substrate/frame/asset-rate/src/weights.rs +++ b/substrate/frame/asset-rate/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_asset_rate +//! Autogenerated weights for `pallet_asset_rate` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/asset-rate/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/asset-rate/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,83 +49,83 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_asset_rate. +/// Weight functions needed for `pallet_asset_rate`. pub trait WeightInfo { fn create() -> Weight; fn update() -> Weight; fn remove() -> Weight; } -/// Weights for pallet_asset_rate using the Substrate node and recommended hardware. +/// Weights for `pallet_asset_rate` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3501` - // Minimum execution time: 11_700_000 picoseconds. - Weight::from_parts(12_158_000, 3501) + // Minimum execution time: 9_447_000 picoseconds. + Weight::from_parts(10_078_000, 3501) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) fn update() -> Weight { // Proof Size summary in bytes: // Measured: `137` // Estimated: `3501` - // Minimum execution time: 12_119_000 picoseconds. - Weight::from_parts(12_548_000, 3501) + // Minimum execution time: 9_844_000 picoseconds. + Weight::from_parts(10_240_000, 3501) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) fn remove() -> Weight { // Proof Size summary in bytes: // Measured: `137` // Estimated: `3501` - // Minimum execution time: 12_541_000 picoseconds. - Weight::from_parts(12_956_000, 3501) + // Minimum execution time: 10_411_000 picoseconds. + Weight::from_parts(10_686_000, 3501) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3501` - // Minimum execution time: 11_700_000 picoseconds. - Weight::from_parts(12_158_000, 3501) + // Minimum execution time: 9_447_000 picoseconds. + Weight::from_parts(10_078_000, 3501) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) fn update() -> Weight { // Proof Size summary in bytes: // Measured: `137` // Estimated: `3501` - // Minimum execution time: 12_119_000 picoseconds. - Weight::from_parts(12_548_000, 3501) + // Minimum execution time: 9_844_000 picoseconds. + Weight::from_parts(10_240_000, 3501) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) fn remove() -> Weight { // Proof Size summary in bytes: // Measured: `137` // Estimated: `3501` - // Minimum execution time: 12_541_000 picoseconds. - Weight::from_parts(12_956_000, 3501) + // Minimum execution time: 10_411_000 picoseconds. + Weight::from_parts(10_686_000, 3501) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/assets/Cargo.toml b/substrate/frame/assets/Cargo.toml index 7b0af2421eaad34c0c48020bb2d24022d231bc33..2efc96348cb5447b792c334256076a395f646719 100644 --- a/substrate/frame/assets/Cargo.toml +++ b/substrate/frame/assets/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-assets" -version = "4.0.0-dev" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } sp-std = { path = "../../primitives/std", default-features = false } # Needed for various traits. In our case, `OnFinalize`. diff --git a/substrate/frame/assets/src/extra_mutator.rs b/substrate/frame/assets/src/extra_mutator.rs index 2a44df5f0c661ffc83b0f9c0df68d14bcfa0bcab..e2f7742c694a3caf94e70e596f667bb7759bb2bb 100644 --- a/substrate/frame/assets/src/extra_mutator.rs +++ b/substrate/frame/assets/src/extra_mutator.rs @@ -38,7 +38,7 @@ impl, I: 'static> Drop for ExtraMutator { } } -impl, I: 'static> sp_std::ops::Deref for ExtraMutator { +impl, I: 'static> core::ops::Deref for ExtraMutator { type Target = T::Extra; fn deref(&self) -> &T::Extra { match self.pending { @@ -48,7 +48,7 @@ impl, I: 'static> sp_std::ops::Deref for ExtraMutator { } } -impl, I: 'static> sp_std::ops::DerefMut for ExtraMutator { +impl, I: 'static> core::ops::DerefMut for ExtraMutator { fn deref_mut(&mut self) -> &mut T::Extra { if self.pending.is_none() { self.pending = Some(self.original.clone()); @@ -60,7 +60,7 @@ impl, I: 'static> sp_std::ops::DerefMut for ExtraMutator { impl, I: 'static> ExtraMutator { pub(super) fn maybe_new( id: T::AssetId, - who: impl sp_std::borrow::Borrow, + who: impl core::borrow::Borrow, ) -> Option> { if let Some(a) = Account::::get(&id, who.borrow()) { Some(ExtraMutator:: { diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index cafe7bb1a3b53df4b48d2a1b9a45cdf14c18cea8..60316f8cdcf1f40eebcaad3ac182aefb513bce39 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -208,7 +208,7 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/assets/src/migration.rs b/substrate/frame/assets/src/migration.rs index efe77714c524d38e9dd822652e39762848fe5367..dd7c12293e80f410301bb47cca013ae4f013e72f 100644 --- a/substrate/frame/assets/src/migration.rs +++ b/substrate/frame/assets/src/migration.rs @@ -64,12 +64,12 @@ pub mod v1 { } } - pub struct MigrateToV1(sp_std::marker::PhantomData); + pub struct MigrateToV1(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 0 && current_version == 1 { + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); + if on_chain_version == 0 && in_code_version == 1 { let mut translated = 0u64; Asset::::translate::< OldAssetDetails>, @@ -78,12 +78,12 @@ pub mod v1 { translated.saturating_inc(); Some(old_value.migrate_to_v1()) }); - current_version.put::>(); + in_code_version.put::>(); log::info!( target: LOG_TARGET, "Upgraded {} pools, storage to version {:?}", translated, - current_version + in_code_version ); T::DbWeight::get().reads_writes(translated + 1, translated + 1) } else { @@ -116,13 +116,13 @@ pub mod v1 { "the asset count before and after the migration should be the same" ); - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - frame_support::ensure!(current_version == 1, "must_upgrade"); + frame_support::ensure!(in_code_version == 1, "must_upgrade"); ensure!( - current_version == onchain_version, - "after migration, the current_version and onchain_version should be the same" + in_code_version == on_chain_version, + "after migration, the in_code_version and on_chain_version should be the same" ); Asset::::iter().try_for_each(|(_id, asset)| -> Result<(), TryRuntimeError> { diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index d60f535833673b3a765f1311a01b08d5c40e4df6..e1722200c35d4c3674db8a56dbee092f1f7ff8a8 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -25,12 +25,8 @@ use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, }; -use sp_core::H256; use sp_io::storage; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -48,28 +44,8 @@ type AssetId = u32; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); type MaxConsumers = ConstU32<3>; } @@ -86,7 +62,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); type RuntimeFreezeReason = (); type FreezeIdentifier = (); - type MaxHolds = (); type MaxFreezes = (); } diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index 67f9bf07f5e7e2dfe1edc1f0a8236d06cd56ef61..11edc7d3fcb585773bc2d49349948fd56299e7d3 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -123,7 +123,7 @@ where return None } if let ExistenceReason::DepositHeld(deposit) = - sp_std::mem::replace(self, ExistenceReason::DepositRefunded) + core::mem::replace(self, ExistenceReason::DepositRefunded) { Some(deposit) } else { @@ -136,7 +136,7 @@ where return None } if let ExistenceReason::DepositFrom(depositor, deposit) = - sp_std::mem::replace(self, ExistenceReason::DepositRefunded) + core::mem::replace(self, ExistenceReason::DepositRefunded) { Some((depositor, deposit)) } else { diff --git a/substrate/frame/assets/src/weights.rs b/substrate/frame/assets/src/weights.rs index f20f7e317cff7c32d280b2e6fa9eaa9aa0fdbd57..f5199105fe354c221779bbacab6a99b33dce3981 100644 --- a/substrate/frame/assets/src/weights.rs +++ b/substrate/frame/assets/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_assets +//! Autogenerated weights for `pallet_assets` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/assets/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/assets/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_assets. +/// Weight functions needed for `pallet_assets`. pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; @@ -86,882 +85,890 @@ pub trait WeightInfo { fn block() -> Weight; } -/// Weights for pallet_assets using the Substrate node and recommended hardware. +/// Weights for `pallet_assets` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `293` // Estimated: `3675` - // Minimum execution time: 31_340_000 picoseconds. - Weight::from_parts(31_977_000, 3675) + // Minimum execution time: 24_690_000 picoseconds. + Weight::from_parts(25_878_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 13_342_000 picoseconds. - Weight::from_parts(13_782_000, 3675) + // Minimum execution time: 10_997_000 picoseconds. + Weight::from_parts(11_369_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn start_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 14_437_000 picoseconds. - Weight::from_parts(14_833_000, 3675) + // Minimum execution time: 11_536_000 picoseconds. + Weight::from_parts(12_309_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1001 w:1000) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1000 w:1000) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1001 w:1000) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1000 w:1000) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + c * (208 ±0)` // Estimated: `3675 + c * (2609 ±0)` - // Minimum execution time: 18_728_000 picoseconds. - Weight::from_parts(18_982_000, 3675) - // Standard Error: 11_708 - .saturating_add(Weight::from_parts(14_363_570, 0).saturating_mul(c.into())) + // Minimum execution time: 15_798_000 picoseconds. + Weight::from_parts(16_005_000, 3675) + // Standard Error: 7_818 + .saturating_add(Weight::from_parts(12_826_278, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(c.into()))) .saturating_add(Weight::from_parts(0, 2609).saturating_mul(c.into())) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1001 w:1000) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1001 w:1000) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) /// The range of component `a` is `[0, 1000]`. fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `522 + a * (86 ±0)` // Estimated: `3675 + a * (2623 ±0)` - // Minimum execution time: 18_611_000 picoseconds. - Weight::from_parts(18_970_000, 3675) - // Standard Error: 13_224 - .saturating_add(Weight::from_parts(16_397_299, 0).saturating_mul(a.into())) + // Minimum execution time: 16_334_000 picoseconds. + Weight::from_parts(16_616_000, 3675) + // Standard Error: 6_402 + .saturating_add(Weight::from_parts(13_502_238, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) .saturating_add(Weight::from_parts(0, 2623).saturating_mul(a.into())) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:0) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn finish_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_504_000 picoseconds. - Weight::from_parts(14_906_000, 3675) + // Minimum execution time: 12_278_000 picoseconds. + Weight::from_parts(12_834_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 26_653_000 picoseconds. - Weight::from_parts(27_260_000, 3675) + // Minimum execution time: 21_793_000 picoseconds. + Weight::from_parts(22_284_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 33_625_000 picoseconds. - Weight::from_parts(34_474_000, 3675) + // Minimum execution time: 28_712_000 picoseconds. + Weight::from_parts(29_710_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` // Estimated: `6208` - // Minimum execution time: 47_609_000 picoseconds. - Weight::from_parts(48_476_000, 6208) + // Minimum execution time: 41_331_000 picoseconds. + Weight::from_parts(42_362_000, 6208) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `498` // Estimated: `6208` - // Minimum execution time: 41_625_000 picoseconds. - Weight::from_parts(43_030_000, 6208) + // Minimum execution time: 37_316_000 picoseconds. + Weight::from_parts(38_200_000, 6208) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` // Estimated: `6208` - // Minimum execution time: 47_661_000 picoseconds. - Weight::from_parts(48_469_000, 6208) + // Minimum execution time: 41_347_000 picoseconds. + Weight::from_parts(42_625_000, 6208) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 17_727_000 picoseconds. - Weight::from_parts(18_384_000, 3675) + // Minimum execution time: 15_072_000 picoseconds. + Weight::from_parts(15_925_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 17_657_000 picoseconds. - Weight::from_parts(18_282_000, 3675) + // Minimum execution time: 14_991_000 picoseconds. + Weight::from_parts(15_987_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn freeze_asset() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 13_743_000 picoseconds. - Weight::from_parts(14_193_000, 3675) + // Minimum execution time: 11_296_000 picoseconds. + Weight::from_parts(12_052_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn thaw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 13_653_000 picoseconds. - Weight::from_parts(14_263_000, 3675) + // Minimum execution time: 11_446_000 picoseconds. + Weight::from_parts(11_791_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:0) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_328_000 picoseconds. - Weight::from_parts(16_042_000, 3675) + // Minimum execution time: 13_134_000 picoseconds. + Weight::from_parts(13_401_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_097_000 picoseconds. - Weight::from_parts(14_641_000, 3675) + // Minimum execution time: 11_395_000 picoseconds. + Weight::from_parts(11_718_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, _s: u32, ) -> Weight { + fn set_metadata(n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 29_535_000 picoseconds. - Weight::from_parts(31_456_892, 3675) + // Minimum execution time: 25_147_000 picoseconds. + Weight::from_parts(26_614_272, 3675) + // Standard Error: 959 + .saturating_add(Weight::from_parts(2_300, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 30_680_000 picoseconds. - Weight::from_parts(31_930_000, 3675) + // Minimum execution time: 26_094_000 picoseconds. + Weight::from_parts(27_199_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn force_set_metadata(n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` // Estimated: `3675` - // Minimum execution time: 14_660_000 picoseconds. - Weight::from_parts(15_718_387, 3675) - // Standard Error: 622 - .saturating_add(Weight::from_parts(2_640, 0).saturating_mul(s.into())) + // Minimum execution time: 11_977_000 picoseconds. + Weight::from_parts(12_719_933, 3675) + // Standard Error: 429 + .saturating_add(Weight::from_parts(3_239, 0).saturating_mul(n.into())) + // Standard Error: 429 + .saturating_add(Weight::from_parts(3_941, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 30_853_000 picoseconds. - Weight::from_parts(31_483_000, 3675) + // Minimum execution time: 25_859_000 picoseconds. + Weight::from_parts(26_654_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn force_asset_status() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 13_632_000 picoseconds. - Weight::from_parts(14_077_000, 3675) + // Minimum execution time: 10_965_000 picoseconds. + Weight::from_parts(11_595_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 33_780_000 picoseconds. - Weight::from_parts(34_533_000, 3675) + // Minimum execution time: 28_427_000 picoseconds. + Weight::from_parts(29_150_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `668` // Estimated: `6208` - // Minimum execution time: 67_712_000 picoseconds. - Weight::from_parts(69_946_000, 6208) + // Minimum execution time: 60_227_000 picoseconds. + Weight::from_parts(61_839_000, 6208) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 36_668_000 picoseconds. - Weight::from_parts(37_637_000, 3675) + // Minimum execution time: 30_738_000 picoseconds. + Weight::from_parts(31_988_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 36_685_000 picoseconds. - Weight::from_parts(37_950_000, 3675) + // Minimum execution time: 31_444_000 picoseconds. + Weight::from_parts(32_126_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn set_min_balance() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_466_000 picoseconds. - Weight::from_parts(14_924_000, 3675) + // Minimum execution time: 11_890_000 picoseconds. + Weight::from_parts(12_462_000, 3675) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn touch() -> Weight { // Proof Size summary in bytes: // Measured: `453` // Estimated: `3675` - // Minimum execution time: 34_874_000 picoseconds. - Weight::from_parts(36_330_000, 3675) + // Minimum execution time: 30_675_000 picoseconds. + Weight::from_parts(31_749_000, 3675) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn touch_other() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 33_278_000 picoseconds. - Weight::from_parts(34_104_000, 3675) + // Minimum execution time: 28_290_000 picoseconds. + Weight::from_parts(29_405_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn refund() -> Weight { // Proof Size summary in bytes: // Measured: `579` // Estimated: `3675` - // Minimum execution time: 32_898_000 picoseconds. - Weight::from_parts(33_489_000, 3675) + // Minimum execution time: 30_195_000 picoseconds. + Weight::from_parts(31_105_000, 3675) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn refund_other() -> Weight { // Proof Size summary in bytes: // Measured: `510` // Estimated: `3675` - // Minimum execution time: 31_243_000 picoseconds. - Weight::from_parts(31_909_000, 3675) + // Minimum execution time: 27_785_000 picoseconds. + Weight::from_parts(28_783_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn block() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 17_692_000 picoseconds. - Weight::from_parts(18_253_000, 3675) + // Minimum execution time: 15_318_000 picoseconds. + Weight::from_parts(16_113_000, 3675) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `293` // Estimated: `3675` - // Minimum execution time: 31_340_000 picoseconds. - Weight::from_parts(31_977_000, 3675) + // Minimum execution time: 24_690_000 picoseconds. + Weight::from_parts(25_878_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `3675` - // Minimum execution time: 13_342_000 picoseconds. - Weight::from_parts(13_782_000, 3675) + // Minimum execution time: 10_997_000 picoseconds. + Weight::from_parts(11_369_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn start_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 14_437_000 picoseconds. - Weight::from_parts(14_833_000, 3675) + // Minimum execution time: 11_536_000 picoseconds. + Weight::from_parts(12_309_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1001 w:1000) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1000 w:1000) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1001 w:1000) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1000 w:1000) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn destroy_accounts(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + c * (208 ±0)` // Estimated: `3675 + c * (2609 ±0)` - // Minimum execution time: 18_728_000 picoseconds. - Weight::from_parts(18_982_000, 3675) - // Standard Error: 11_708 - .saturating_add(Weight::from_parts(14_363_570, 0).saturating_mul(c.into())) + // Minimum execution time: 15_798_000 picoseconds. + Weight::from_parts(16_005_000, 3675) + // Standard Error: 7_818 + .saturating_add(Weight::from_parts(12_826_278, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(c.into()))) .saturating_add(Weight::from_parts(0, 2609).saturating_mul(c.into())) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1001 w:1000) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1001 w:1000) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) /// The range of component `a` is `[0, 1000]`. fn destroy_approvals(a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `522 + a * (86 ±0)` // Estimated: `3675 + a * (2623 ±0)` - // Minimum execution time: 18_611_000 picoseconds. - Weight::from_parts(18_970_000, 3675) - // Standard Error: 13_224 - .saturating_add(Weight::from_parts(16_397_299, 0).saturating_mul(a.into())) + // Minimum execution time: 16_334_000 picoseconds. + Weight::from_parts(16_616_000, 3675) + // Standard Error: 6_402 + .saturating_add(Weight::from_parts(13_502_238, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) .saturating_add(Weight::from_parts(0, 2623).saturating_mul(a.into())) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:0) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn finish_destroy() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_504_000 picoseconds. - Weight::from_parts(14_906_000, 3675) + // Minimum execution time: 12_278_000 picoseconds. + Weight::from_parts(12_834_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 26_653_000 picoseconds. - Weight::from_parts(27_260_000, 3675) + // Minimum execution time: 21_793_000 picoseconds. + Weight::from_parts(22_284_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 33_625_000 picoseconds. - Weight::from_parts(34_474_000, 3675) + // Minimum execution time: 28_712_000 picoseconds. + Weight::from_parts(29_710_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` // Estimated: `6208` - // Minimum execution time: 47_609_000 picoseconds. - Weight::from_parts(48_476_000, 6208) + // Minimum execution time: 41_331_000 picoseconds. + Weight::from_parts(42_362_000, 6208) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `498` // Estimated: `6208` - // Minimum execution time: 41_625_000 picoseconds. - Weight::from_parts(43_030_000, 6208) + // Minimum execution time: 37_316_000 picoseconds. + Weight::from_parts(38_200_000, 6208) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `498` // Estimated: `6208` - // Minimum execution time: 47_661_000 picoseconds. - Weight::from_parts(48_469_000, 6208) + // Minimum execution time: 41_347_000 picoseconds. + Weight::from_parts(42_625_000, 6208) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 17_727_000 picoseconds. - Weight::from_parts(18_384_000, 3675) + // Minimum execution time: 15_072_000 picoseconds. + Weight::from_parts(15_925_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn thaw() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 17_657_000 picoseconds. - Weight::from_parts(18_282_000, 3675) + // Minimum execution time: 14_991_000 picoseconds. + Weight::from_parts(15_987_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn freeze_asset() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 13_743_000 picoseconds. - Weight::from_parts(14_193_000, 3675) + // Minimum execution time: 11_296_000 picoseconds. + Weight::from_parts(12_052_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn thaw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 13_653_000 picoseconds. - Weight::from_parts(14_263_000, 3675) + // Minimum execution time: 11_446_000 picoseconds. + Weight::from_parts(11_791_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:0) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 15_328_000 picoseconds. - Weight::from_parts(16_042_000, 3675) + // Minimum execution time: 13_134_000 picoseconds. + Weight::from_parts(13_401_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_097_000 picoseconds. - Weight::from_parts(14_641_000, 3675) + // Minimum execution time: 11_395_000 picoseconds. + Weight::from_parts(11_718_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn set_metadata(_n: u32, _s: u32, ) -> Weight { + fn set_metadata(n: u32, _s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 29_535_000 picoseconds. - Weight::from_parts(31_456_892, 3675) + // Minimum execution time: 25_147_000 picoseconds. + Weight::from_parts(26_614_272, 3675) + // Standard Error: 959 + .saturating_add(Weight::from_parts(2_300, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 30_680_000 picoseconds. - Weight::from_parts(31_930_000, 3675) + // Minimum execution time: 26_094_000 picoseconds. + Weight::from_parts(27_199_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 50]`. /// The range of component `s` is `[0, 50]`. - fn force_set_metadata(_n: u32, s: u32, ) -> Weight { + fn force_set_metadata(n: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `190` // Estimated: `3675` - // Minimum execution time: 14_660_000 picoseconds. - Weight::from_parts(15_718_387, 3675) - // Standard Error: 622 - .saturating_add(Weight::from_parts(2_640, 0).saturating_mul(s.into())) + // Minimum execution time: 11_977_000 picoseconds. + Weight::from_parts(12_719_933, 3675) + // Standard Error: 429 + .saturating_add(Weight::from_parts(3_239, 0).saturating_mul(n.into())) + // Standard Error: 429 + .saturating_add(Weight::from_parts(3_941, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) fn force_clear_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `515` // Estimated: `3675` - // Minimum execution time: 30_853_000 picoseconds. - Weight::from_parts(31_483_000, 3675) + // Minimum execution time: 25_859_000 picoseconds. + Weight::from_parts(26_654_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn force_asset_status() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 13_632_000 picoseconds. - Weight::from_parts(14_077_000, 3675) + // Minimum execution time: 10_965_000 picoseconds. + Weight::from_parts(11_595_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3675` - // Minimum execution time: 33_780_000 picoseconds. - Weight::from_parts(34_533_000, 3675) + // Minimum execution time: 28_427_000 picoseconds. + Weight::from_parts(29_150_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer_approved() -> Weight { // Proof Size summary in bytes: // Measured: `668` // Estimated: `6208` - // Minimum execution time: 67_712_000 picoseconds. - Weight::from_parts(69_946_000, 6208) + // Minimum execution time: 60_227_000 picoseconds. + Weight::from_parts(61_839_000, 6208) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 36_668_000 picoseconds. - Weight::from_parts(37_637_000, 3675) + // Minimum execution time: 30_738_000 picoseconds. + Weight::from_parts(31_988_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Approvals (r:1 w:1) - /// Proof: Assets Approvals (max_values: None, max_size: Some(148), added: 2623, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) fn force_cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `555` // Estimated: `3675` - // Minimum execution time: 36_685_000 picoseconds. - Weight::from_parts(37_950_000, 3675) + // Minimum execution time: 31_444_000 picoseconds. + Weight::from_parts(32_126_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn set_min_balance() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 14_466_000 picoseconds. - Weight::from_parts(14_924_000, 3675) + // Minimum execution time: 11_890_000 picoseconds. + Weight::from_parts(12_462_000, 3675) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn touch() -> Weight { // Proof Size summary in bytes: // Measured: `453` // Estimated: `3675` - // Minimum execution time: 34_874_000 picoseconds. - Weight::from_parts(36_330_000, 3675) + // Minimum execution time: 30_675_000 picoseconds. + Weight::from_parts(31_749_000, 3675) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn touch_other() -> Weight { // Proof Size summary in bytes: // Measured: `351` // Estimated: `3675` - // Minimum execution time: 33_278_000 picoseconds. - Weight::from_parts(34_104_000, 3675) + // Minimum execution time: 28_290_000 picoseconds. + Weight::from_parts(29_405_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn refund() -> Weight { // Proof Size summary in bytes: // Measured: `579` // Estimated: `3675` - // Minimum execution time: 32_898_000 picoseconds. - Weight::from_parts(33_489_000, 3675) + // Minimum execution time: 30_195_000 picoseconds. + Weight::from_parts(31_105_000, 3675) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) fn refund_other() -> Weight { // Proof Size summary in bytes: // Measured: `510` // Estimated: `3675` - // Minimum execution time: 31_243_000 picoseconds. - Weight::from_parts(31_909_000, 3675) + // Minimum execution time: 27_785_000 picoseconds. + Weight::from_parts(28_783_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Assets Asset (r:1 w:0) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn block() -> Weight { // Proof Size summary in bytes: // Measured: `459` // Estimated: `3675` - // Minimum execution time: 17_692_000 picoseconds. - Weight::from_parts(18_253_000, 3675) + // Minimum execution time: 15_318_000 picoseconds. + Weight::from_parts(16_113_000, 3675) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/atomic-swap/Cargo.toml b/substrate/frame/atomic-swap/Cargo.toml index d34779d8bc09b23990ae1fff1f0606ca4ee2da3f..0283af1d1b06234479cb9f0ad3fe142ba386ccf1 100644 --- a/substrate/frame/atomic-swap/Cargo.toml +++ b/substrate/frame/atomic-swap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-atomic-swap" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/atomic-swap/src/lib.rs b/substrate/frame/atomic-swap/src/lib.rs index 8094c06030120c9a7583aed3f64dc2a5de7260d4..609903e67e3e56f7016a20772a996a41705839d7 100644 --- a/substrate/frame/atomic-swap/src/lib.rs +++ b/substrate/frame/atomic-swap/src/lib.rs @@ -43,6 +43,10 @@ mod tests; use codec::{Decode, Encode}; +use core::{ + marker::PhantomData, + ops::{Deref, DerefMut}, +}; use frame_support::{ dispatch::DispatchResult, pallet_prelude::MaxEncodedLen, @@ -54,11 +58,6 @@ use frame_system::pallet_prelude::BlockNumberFor; use scale_info::TypeInfo; use sp_io::hashing::blake2_256; use sp_runtime::RuntimeDebug; -use sp_std::{ - marker::PhantomData, - ops::{Deref, DerefMut}, - prelude::*, -}; /// Pending atomic swap operation. #[derive(Clone, Eq, PartialEq, RuntimeDebugNoBound, Encode, Decode, TypeInfo, MaxEncodedLen)] diff --git a/substrate/frame/atomic-swap/src/tests.rs b/substrate/frame/atomic-swap/src/tests.rs index 58502aa4f93c2468b0c89e59f5ccd73bcd7dbe67..4b444d888ed5e228a4951258092b0bdbdcf6e597 100644 --- a/substrate/frame/atomic-swap/src/tests.rs +++ b/substrate/frame/atomic-swap/src/tests.rs @@ -24,11 +24,7 @@ use frame_support::{ derive_impl, traits::{ConstU32, ConstU64}, }; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -43,29 +39,8 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } impl pallet_balances::Config for Test { @@ -82,7 +57,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl Config for Test { diff --git a/substrate/frame/aura/Cargo.toml b/substrate/frame/aura/Cargo.toml index e2419933a20ea3d2b41134764884128c04b33e14..de698487efa7fa6c1cc37cc89e872b9603a9f253 100644 --- a/substrate/frame/aura/Cargo.toml +++ b/substrate/frame/aura/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-aura" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/aura/src/migrations.rs b/substrate/frame/aura/src/migrations.rs index b45e4eb7cb5c866920a72601b5db4baa22d16d87..727fe281aa38430a5cd246d0dfba098c36d578e1 100644 --- a/substrate/frame/aura/src/migrations.rs +++ b/substrate/frame/aura/src/migrations.rs @@ -19,7 +19,7 @@ use frame_support::{pallet_prelude::*, traits::Get, weights::Weight}; -struct __LastTimestamp(sp_std::marker::PhantomData); +struct __LastTimestamp(core::marker::PhantomData); impl frame_support::traits::StorageInstance for __LastTimestamp { fn pallet_prefix() -> &'static str { T::PalletPrefix::get() diff --git a/substrate/frame/aura/src/mock.rs b/substrate/frame/aura/src/mock.rs index d87fea8fc81ebcc9ccb2fb03875a4c45959b7057..8bc3e407158317164730e839bf954fd5f8e5e597 100644 --- a/substrate/frame/aura/src/mock.rs +++ b/substrate/frame/aura/src/mock.rs @@ -25,8 +25,7 @@ use frame_support::{ traits::{ConstU32, ConstU64, DisabledValidators}, }; use sp_consensus_aura::{ed25519::AuthorityId, AuthorityIndex}; -use sp_core::H256; -use sp_runtime::{testing::UintAuthorityId, traits::IdentityLookup, BuildStorage}; +use sp_runtime::{testing::UintAuthorityId, BuildStorage}; type Block = frame_system::mocking::MockBlock; @@ -43,29 +42,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } impl pallet_timestamp::Config for Test { diff --git a/substrate/frame/authority-discovery/Cargo.toml b/substrate/frame/authority-discovery/Cargo.toml index a18199657443c2895a21f714fa0da856aaa3eb51..0922007e57e82df51d89500078b02164942b9f45 100644 --- a/substrate/frame/authority-discovery/Cargo.toml +++ b/substrate/frame/authority-discovery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-authority-discovery" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/authority-discovery/src/lib.rs b/substrate/frame/authority-discovery/src/lib.rs index 640c0c8759ebef6c390e9119fb1b96361bc34e25..2b4dfaf1aea8c804af7b4893be7057c53c347896 100644 --- a/substrate/frame/authority-discovery/src/lib.rs +++ b/substrate/frame/authority-discovery/src/lib.rs @@ -168,13 +168,10 @@ impl OneSessionHandler for Pallet { mod tests { use super::*; use crate as pallet_authority_discovery; - use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU32, ConstU64}, - }; + use frame_support::{derive_impl, parameter_types, traits::ConstU32}; use sp_application_crypto::Pair; use sp_authority_discovery::AuthorityPair; - use sp_core::{crypto::key_types, H256}; + use sp_core::crypto::key_types; use sp_io::TestExternalities; use sp_runtime::{ testing::UintAuthorityId, @@ -227,29 +224,9 @@ mod tests { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = AuthorityId; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } pub struct TestSessionHandler; diff --git a/substrate/frame/authorship/Cargo.toml b/substrate/frame/authorship/Cargo.toml index 41d4cf139721d7cd157cb88c9d2b5a34135898ea..4b318f12519eacfbacbff9987b1490064b692b66 100644 --- a/substrate/frame/authorship/Cargo.toml +++ b/substrate/frame/authorship/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-authorship" -version = "4.0.0-dev" +version = "28.0.0" description = "Block and Uncle Author tracking for the FRAME" authors.workspace = true edition.workspace = true diff --git a/substrate/frame/babe/Cargo.toml b/substrate/frame/babe/Cargo.toml index 639b9544b0c5fac66f53a6b0067f37535790bd5f..fc7385efa1f14c371af682304f3e1c8d3c614c2d 100644 --- a/substrate/frame/babe/Cargo.toml +++ b/substrate/frame/babe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-babe" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/babe/src/lib.rs b/substrate/frame/babe/src/lib.rs index a6e44390dbc534e15e3a1658513c6ce9fa25088e..5fb107dde3bad06e6f4812d27ff1159a95194442 100644 --- a/substrate/frame/babe/src/lib.rs +++ b/substrate/frame/babe/src/lib.rs @@ -323,7 +323,7 @@ pub mod pallet { #[pallet::genesis_config] pub struct GenesisConfig { pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>, - pub epoch_config: Option, + pub epoch_config: BabeEpochConfiguration, #[serde(skip)] pub _config: sp_std::marker::PhantomData, } @@ -333,9 +333,7 @@ pub mod pallet { fn build(&self) { SegmentIndex::::put(0); Pallet::::initialize_genesis_authorities(&self.authorities); - EpochConfig::::put( - self.epoch_config.clone().expect("epoch_config must not be None"), - ); + EpochConfig::::put(&self.epoch_config); } } diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index 72abbc805db1a742b08aa3feb9b8f9b0b513600e..1ba0cdd0cc12e91ce11a545a40f7d0288bd3bbe0 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -37,8 +37,9 @@ use sp_core::{ use sp_io; use sp_runtime::{ curve::PiecewiseLinear, + generic::UncheckedExtrinsic, impl_opaque_keys, - testing::{Digest, DigestItem, Header, TestXt}, + testing::{Digest, DigestItem, Header}, traits::{Header as _, OpaqueKeys}, BuildStorage, Perbill, }; @@ -74,7 +75,7 @@ where RuntimeCall: From, { type OverarchingCall = RuntimeCall; - type Extrinsic = TestXt; + type Extrinsic = UncheckedExtrinsic; } impl_opaque_keys! { @@ -126,7 +127,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/substrate/frame/babe/src/randomness.rs b/substrate/frame/babe/src/randomness.rs index d3d1bea2292da949dd1bac3aca20f6d33cb6d258..fd52981d2a1dd46c01e5706d1f8212b621c0b82c 100644 --- a/substrate/frame/babe/src/randomness.rs +++ b/substrate/frame/babe/src/randomness.rs @@ -51,7 +51,7 @@ use sp_runtime::traits::{Hash, One, Saturating}; /// /// Adversaries should not possess many block production slots towards the beginning or /// end of every epoch, but they possess some influence over when they possess more slots. -pub struct RandomnessFromTwoEpochsAgo(sp_std::marker::PhantomData); +pub struct RandomnessFromTwoEpochsAgo(core::marker::PhantomData); /// Randomness usable by on-chain code that **does not depend** upon finality and takes /// action based upon on-chain commitments made during the previous epoch. @@ -79,7 +79,7 @@ pub struct RandomnessFromTwoEpochsAgo(sp_std::marker::PhantomData); /// As an example usage, we determine parachain auctions ending times in Polkadot using /// `RandomnessFromOneEpochAgo` because it reduces bias from `ParentBlockRandomness` and /// does not require the extra finality delay of `RandomnessFromTwoEpochsAgo`. -pub struct RandomnessFromOneEpochAgo(sp_std::marker::PhantomData); +pub struct RandomnessFromOneEpochAgo(core::marker::PhantomData); /// Randomness produced semi-freshly with each block, but inherits limitations of /// `RandomnessFromTwoEpochsAgo` from which it derives. @@ -119,7 +119,7 @@ pub struct RandomnessFromOneEpochAgo(sp_std::marker::PhantomData); /// instead you are using this randomness externally, i.e. after block execution, then /// this randomness will be provided by the "current" block (this stems from the fact that /// we process VRF outputs on block execution finalization, i.e. `on_finalize`). -pub struct ParentBlockRandomness(sp_std::marker::PhantomData); +pub struct ParentBlockRandomness(core::marker::PhantomData); /// Randomness produced semi-freshly with each block, but inherits limitations of /// `RandomnessFromTwoEpochsAgo` from which it derives. @@ -128,7 +128,7 @@ pub struct ParentBlockRandomness(sp_std::marker::PhantomData); #[deprecated(note = "Should not be relied upon for correctness, \ will not provide fresh randomness for the current block. \ Please use `ParentBlockRandomness` instead.")] -pub struct CurrentBlockRandomness(sp_std::marker::PhantomData); +pub struct CurrentBlockRandomness(core::marker::PhantomData); impl RandomnessT> for RandomnessFromTwoEpochsAgo { fn random(subject: &[u8]) -> (T::Hash, BlockNumberFor) { diff --git a/substrate/frame/bags-list/Cargo.toml b/substrate/frame/bags-list/Cargo.toml index bd0d4884b4e93cac0aac42c4431ff73fa3ed75a4..f9ae462e16d729f9652da114f5ebcef4cc2f21b7 100644 --- a/substrate/frame/bags-list/Cargo.toml +++ b/substrate/frame/bags-list/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-bags-list" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -33,8 +33,8 @@ frame-system = { path = "../system", default-features = false } frame-election-provider-support = { path = "../election-provider-support", default-features = false } # third party -log = { version = "0.4.17", default-features = false } -docify = "0.2.6" +log = { workspace = true } +docify = "0.2.7" aquamarine = { version = "0.5.0" } # Optional imports for benchmarking diff --git a/substrate/frame/bags-list/remote-tests/Cargo.toml b/substrate/frame/bags-list/remote-tests/Cargo.toml index fb61a9867783a32084dc363f6ac18c76ed4f3e10..266355f5cabe19214643db024e641339acd60630 100644 --- a/substrate/frame/bags-list/remote-tests/Cargo.toml +++ b/substrate/frame/bags-list/remote-tests/Cargo.toml @@ -34,4 +34,4 @@ sp-std = { path = "../../../primitives/std" } remote-externalities = { package = "frame-remote-externalities", path = "../../../utils/frame/remote-externalities" } # others -log = "0.4.17" +log = { workspace = true, default-features = true } diff --git a/substrate/frame/bags-list/src/weights.rs b/substrate/frame/bags-list/src/weights.rs index d929c6bb95963c3eaee8ce0d16b127adcb64956e..804b702cc9a12c9f6a371b1ad019fd96038c7bb5 100644 --- a/substrate/frame/bags-list/src/weights.rs +++ b/substrate/frame/bags-list/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_bags_list +//! Autogenerated weights for `pallet_bags_list` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/bags-list/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/bags-list/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,123 +49,123 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_bags_list. +/// Weight functions needed for `pallet_bags_list`. pub trait WeightInfo { fn rebag_non_terminal() -> Weight; fn rebag_terminal() -> Weight; fn put_in_front_of() -> Weight; } -/// Weights for pallet_bags_list using the Substrate node and recommended hardware. +/// Weights for `pallet_bags_list` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:4 w:4) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListNodes` (r:4 w:4) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:1 w:1) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn rebag_non_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1724` + // Measured: `1719` // Estimated: `11506` - // Minimum execution time: 62_137_000 picoseconds. - Weight::from_parts(64_050_000, 11506) + // Minimum execution time: 55_856_000 picoseconds. + Weight::from_parts(59_022_000, 11506) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListNodes` (r:3 w:3) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:2 w:2) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn rebag_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1618` + // Measured: `1613` // Estimated: `8877` - // Minimum execution time: 60_880_000 picoseconds. - Weight::from_parts(62_078_000, 8877) + // Minimum execution time: 55_418_000 picoseconds. + Weight::from_parts(57_352_000, 8877) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: VoterList ListNodes (r:4 w:4) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:2 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:2 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: `VoterList::ListNodes` (r:4 w:4) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:2 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:2 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `VoterList::CounterForListNodes` (r:1 w:1) + /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:1 w:1) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn put_in_front_of() -> Weight { // Proof Size summary in bytes: - // Measured: `1930` + // Measured: `1925` // Estimated: `11506` - // Minimum execution time: 68_911_000 picoseconds. - Weight::from_parts(70_592_000, 11506) + // Minimum execution time: 63_820_000 picoseconds. + Weight::from_parts(65_530_000, 11506) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:4 w:4) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListNodes` (r:4 w:4) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:1 w:1) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn rebag_non_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1724` + // Measured: `1719` // Estimated: `11506` - // Minimum execution time: 62_137_000 picoseconds. - Weight::from_parts(64_050_000, 11506) + // Minimum execution time: 55_856_000 picoseconds. + Weight::from_parts(59_022_000, 11506) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:3 w:3) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:2 w:2) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListNodes` (r:3 w:3) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:2 w:2) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn rebag_terminal() -> Weight { // Proof Size summary in bytes: - // Measured: `1618` + // Measured: `1613` // Estimated: `8877` - // Minimum execution time: 60_880_000 picoseconds. - Weight::from_parts(62_078_000, 8877) + // Minimum execution time: 55_418_000 picoseconds. + Weight::from_parts(57_352_000, 8877) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: VoterList ListNodes (r:4 w:4) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:2 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:2 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) + /// Storage: `VoterList::ListNodes` (r:4 w:4) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:2 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:2 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `VoterList::CounterForListNodes` (r:1 w:1) + /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:1 w:1) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn put_in_front_of() -> Weight { // Proof Size summary in bytes: - // Measured: `1930` + // Measured: `1925` // Estimated: `11506` - // Minimum execution time: 68_911_000 picoseconds. - Weight::from_parts(70_592_000, 11506) + // Minimum execution time: 63_820_000 picoseconds. + Weight::from_parts(65_530_000, 11506) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } diff --git a/substrate/frame/balances/Cargo.toml b/substrate/frame/balances/Cargo.toml index 23fe6e5832222b23b9c8b97fcab81688327e2221..db114290ed8650bd59a105ff350cf7291e6b9830 100644 --- a/substrate/frame/balances/Cargo.toml +++ b/substrate/frame/balances/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-balances" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,13 +17,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } +docify = "0.2.6" [dev-dependencies] pallet-transaction-payment = { path = "../transaction-payment" } @@ -52,6 +53,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ diff --git a/substrate/frame/balances/src/benchmarking.rs b/substrate/frame/balances/src/benchmarking.rs index 5641c68516c28a9ad0115dc6decd9ad39904d653..0ce1240eb5d3987e43f7dba2734cec3b6fbea62a 100644 --- a/substrate/frame/balances/src/benchmarking.rs +++ b/substrate/frame/balances/src/benchmarking.rs @@ -286,6 +286,17 @@ mod benchmarks { } } + #[benchmark] + fn force_adjust_total_issuance() { + let ti = Balances::::total_issuance(); + let delta = 123u32.into(); + + #[extrinsic_call] + _(RawOrigin::Root, AdjustmentDirection::Increase, delta); + + assert_eq!(Balances::::total_issuance(), ti + delta); + } + impl_benchmark_test_suite! { Balances, crate::tests::ExtBuilder::default().build(), diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index 843bc351494e3fcad309e600c4571b6e307b8f53..e87aa6f9311e30311066e21e73e8e006787fed90 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -190,7 +190,8 @@ use sp_runtime::{ }; use sp_std::{cmp, fmt::Debug, mem, prelude::*, result}; pub use types::{ - AccountData, BalanceLock, DustCleaner, ExtraFlags, IdAmount, Reasons, ReserveData, + AccountData, AdjustmentDirection, BalanceLock, DustCleaner, ExtraFlags, IdAmount, Reasons, + ReserveData, }; pub use weights::WeightInfo; @@ -205,7 +206,7 @@ pub mod pallet { use super::*; use frame_support::{ pallet_prelude::*, - traits::{fungible::Credit, tokens::Precision, VariantCount}, + traits::{fungible::Credit, tokens::Precision, VariantCount, VariantCountOf}, }; use frame_system::pallet_prelude::*; @@ -241,7 +242,6 @@ pub mod pallet { type MaxLocks = ConstU32<100>; type MaxReserves = ConstU32<100>; type MaxFreezes = ConstU32<100>; - type MaxHolds = ConstU32<100>; type WeightInfo = (); } @@ -315,16 +315,12 @@ pub mod pallet { #[pallet::constant] type MaxReserves: Get; - /// The maximum number of holds that can exist on an account at any time. - #[pallet::constant] - type MaxHolds: Get; - /// The maximum number of individual freeze locks that can exist on an account at any time. #[pallet::constant] type MaxFreezes: Get; } - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: frame_support::traits::StorageVersion = frame_support::traits::StorageVersion::new(1); @@ -384,6 +380,8 @@ pub mod pallet { Frozen { who: T::AccountId, amount: T::Balance }, /// Some balance was thawed. Thawed { who: T::AccountId, amount: T::Balance }, + /// The `TotalIssuance` was forcefully changed. + TotalIssuanceForced { old: T::Balance, new: T::Balance }, } #[pallet::error] @@ -404,10 +402,14 @@ pub mod pallet { DeadAccount, /// Number of named reserves exceed `MaxReserves`. TooManyReserves, - /// Number of holds exceed `MaxHolds`. + /// Number of holds exceed `VariantCountOf`. TooManyHolds, /// Number of freezes exceed `MaxFreezes`. TooManyFreezes, + /// The issuance cannot be modified since it is already deactivated. + IssuanceDeactivated, + /// The delta cannot be zero. + DeltaZero, } /// The total units issued in the system. @@ -480,7 +482,10 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - BoundedVec, T::MaxHolds>, + BoundedVec< + IdAmount, + VariantCountOf, + >, ValueQuery, >; @@ -549,13 +554,6 @@ pub mod pallet { "The existential deposit must be greater than zero!" ); - assert!( - T::MaxHolds::get() >= ::VARIANT_COUNT, - "MaxHolds should be greater than or equal to the number of hold reasons: {} < {}", - T::MaxHolds::get(), - ::VARIANT_COUNT - ); - assert!( T::MaxFreezes::get() >= ::VARIANT_COUNT, "MaxFreezes should be greater than or equal to the number of freeze reasons: {} < {}", @@ -743,6 +741,37 @@ pub mod pallet { Self::deposit_event(Event::BalanceSet { who, free: new_free }); Ok(()) } + + /// Adjust the total issuance in a saturating way. + /// + /// Can only be called by root and always needs a positive `delta`. + /// + /// # Example + #[doc = docify::embed!("./src/tests/dispatchable_tests.rs", force_adjust_total_issuance_example)] + #[pallet::call_index(9)] + #[pallet::weight(T::WeightInfo::force_adjust_total_issuance())] + pub fn force_adjust_total_issuance( + origin: OriginFor, + direction: AdjustmentDirection, + #[pallet::compact] delta: T::Balance, + ) -> DispatchResult { + ensure_root(origin)?; + + ensure!(delta > Zero::zero(), Error::::DeltaZero); + + let old = TotalIssuance::::get(); + let new = match direction { + AdjustmentDirection::Increase => old.saturating_add(delta), + AdjustmentDirection::Decrease => old.saturating_sub(delta), + }; + + ensure!(InactiveIssuance::::get() <= new, Error::::IssuanceDeactivated); + TotalIssuance::::set(new); + + Self::deposit_event(Event::::TotalIssuanceForced { old, new }); + + Ok(()) + } } impl, I: 'static> Pallet { diff --git a/substrate/frame/balances/src/migration.rs b/substrate/frame/balances/src/migration.rs index ba6819ec6e814da69807c8717a088df2451b6cb5..38d9c07ff7e002f6a7a57189b7ebce6830b52c75 100644 --- a/substrate/frame/balances/src/migration.rs +++ b/substrate/frame/balances/src/migration.rs @@ -22,9 +22,9 @@ use frame_support::{ }; fn migrate_v0_to_v1, I: 'static>(accounts: &[T::AccountId]) -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 0 { + if on_chain_version == 0 { let total = accounts .iter() .map(|a| Pallet::::total_balance(a)) @@ -76,9 +76,9 @@ impl, A: Get>, I: 'static> OnRuntimeUpgrade pub struct ResetInactive(PhantomData<(T, I)>); impl, I: 'static> OnRuntimeUpgrade for ResetInactive { fn on_runtime_upgrade() -> Weight { - let onchain_version = Pallet::::on_chain_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); - if onchain_version == 1 { + if on_chain_version == 1 { // Remove the old `StorageVersion` type. frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( Pallet::::name().as_bytes(), diff --git a/substrate/frame/balances/src/tests/currency_tests.rs b/substrate/frame/balances/src/tests/currency_tests.rs index 46a4c4caefc3d72ad45adf58243f3430344d4813..10bb79901f8c2a63f41289b061416ebcf51b8997 100644 --- a/substrate/frame/balances/src/tests/currency_tests.rs +++ b/substrate/frame/balances/src/tests/currency_tests.rs @@ -30,6 +30,7 @@ use frame_support::{ StorageNoopGuard, }; use frame_system::Event as SysEvent; +use sp_runtime::traits::DispatchTransaction; const ID_1: LockIdentifier = *b"1 "; const ID_2: LockIdentifier = *b"2 "; @@ -240,17 +241,17 @@ fn lock_should_work_reserve() { TokenError::Frozen ); assert_noop!(Balances::reserve(&1, 1), Error::::LiquidityRestrictions,); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(1), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, ) .is_err()); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(0), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, @@ -271,17 +272,17 @@ fn lock_should_work_tx_fee() { TokenError::Frozen ); assert_noop!(Balances::reserve(&1, 1), Error::::LiquidityRestrictions,); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(1), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, ) .is_err()); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(0), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, diff --git a/substrate/frame/balances/src/tests/dispatchable_tests.rs b/substrate/frame/balances/src/tests/dispatchable_tests.rs index 8f625a189446b2982e16433db3c2ebae7e43f34d..4be68f61693b4f1c92b066fc396218a48550632e 100644 --- a/substrate/frame/balances/src/tests/dispatchable_tests.rs +++ b/substrate/frame/balances/src/tests/dispatchable_tests.rs @@ -18,9 +18,16 @@ //! Tests regarding the functionality of the dispatchables/extrinsics. use super::*; -use frame_support::traits::tokens::Preservation::Expendable; +use crate::{ + AdjustmentDirection::{Decrease as Dec, Increase as Inc}, + Event, +}; +use frame_support::traits::{fungible::Unbalanced, tokens::Preservation::Expendable}; use fungible::{hold::Mutate as HoldMutate, Inspect, Mutate}; +/// Alice account ID for more readable tests. +const ALICE: u64 = 1; + #[test] fn default_indexing_on_new_accounts_should_not_work2() { ExtBuilder::default() @@ -222,3 +229,109 @@ fn upgrade_accounts_should_work() { assert_eq!(System::consumers(&7), 0); }); } + +#[test] +#[docify::export] +fn force_adjust_total_issuance_example() { + ExtBuilder::default().build_and_execute_with(|| { + // First we set the TotalIssuance to 64 by giving Alice a balance of 64. + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), ALICE, 64)); + let old_ti = Balances::total_issuance(); + assert_eq!(old_ti, 64, "TI should be 64"); + + // Now test the increase: + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32)); + let new_ti = Balances::total_issuance(); + assert_eq!(old_ti + 32, new_ti, "Should increase by 32"); + + // If Alice tries to call it, it errors: + assert_noop!( + Balances::force_adjust_total_issuance(RawOrigin::Signed(ALICE).into(), Inc, 32), + BadOrigin, + ); + }); +} + +#[test] +fn force_adjust_total_issuance_works() { + ExtBuilder::default().build_and_execute_with(|| { + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64)); + let ti = Balances::total_issuance(); + + // Increase works: + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32)); + assert_eq!(Balances::total_issuance(), ti + 32); + System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced { + old: 64, + new: 96, + })); + + // Decrease works: + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 64)); + assert_eq!(Balances::total_issuance(), ti - 32); + System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced { + old: 96, + new: 32, + })); + }); +} + +#[test] +fn force_adjust_total_issuance_saturates() { + ExtBuilder::default().build_and_execute_with(|| { + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64)); + let ti = Balances::total_issuance(); + let max = Balance::max_value(); + assert_eq!(ti, 64); + + // Increment saturates: + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, max)); + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 123)); + assert_eq!(Balances::total_issuance(), max); + + // Decrement saturates: + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, max)); + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 123)); + assert_eq!(Balances::total_issuance(), 0); + }); +} + +#[test] +fn force_adjust_total_issuance_rejects_zero_delta() { + ExtBuilder::default().build_and_execute_with(|| { + assert_noop!( + Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 0), + Error::::DeltaZero, + ); + assert_noop!( + Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 0), + Error::::DeltaZero, + ); + }); +} + +#[test] +fn force_adjust_total_issuance_rejects_more_than_inactive() { + ExtBuilder::default().build_and_execute_with(|| { + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64)); + Balances::deactivate(16u32.into()); + + assert_eq!(Balances::total_issuance(), 64); + assert_eq!(Balances::active_issuance(), 48); + + // Works with up to 48: + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 40),); + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 8),); + assert_eq!(Balances::total_issuance(), 16); + assert_eq!(Balances::active_issuance(), 0); + // Errors with more than 48: + assert_noop!( + Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 1), + Error::::IssuanceDeactivated, + ); + // Increasing again increases the inactive issuance: + assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 10),); + assert_eq!(Balances::total_issuance(), 26); + assert_eq!(Balances::active_issuance(), 10); + }); +} diff --git a/substrate/frame/balances/src/tests/mod.rs b/substrate/frame/balances/src/tests/mod.rs index 708e081fe0bfce302f9cd37e72f92f5757c2ed55..ee076a10fd1085b9c9e6830606e40993db8b3364 100644 --- a/substrate/frame/balances/src/tests/mod.rs +++ b/substrate/frame/balances/src/tests/mod.rs @@ -26,18 +26,18 @@ use frame_support::{ dispatch::{DispatchInfo, GetDispatchInfo}, parameter_types, traits::{ - fungible, ConstU32, ConstU64, ConstU8, Imbalance as ImbalanceT, OnUnbalanced, - StorageMapShim, StoredMap, VariantCount, WhitelistedStorageKeys, + fungible, ConstU32, ConstU8, Imbalance as ImbalanceT, OnUnbalanced, StorageMapShim, + StoredMap, VariantCount, WhitelistedStorageKeys, }, weights::{IdentityFee, Weight}, }; use frame_system::{self as system, RawOrigin}; use pallet_transaction_payment::{ChargeTransactionPayment, CurrencyAdapter, Multiplier}; use scale_info::TypeInfo; -use sp_core::{hexdisplay::HexDisplay, H256}; +use sp_core::hexdisplay::HexDisplay; use sp_io; use sp_runtime::{ - traits::{BadOrigin, IdentityLookup, SignedExtension, Zero}, + traits::{BadOrigin, Zero}, ArithmeticError, BuildStorage, DispatchError, DispatchResult, FixedPointNumber, RuntimeDebug, TokenError, }; @@ -92,42 +92,23 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = super::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig as pallet_transaction_payment::DefaultConfig)] impl pallet_transaction_payment::Config for Test { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter, ()>; type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = IdentityFee; type LengthToFee = IdentityFee; - type FeeMultiplierUpdate = (); } +pub(crate) type Balance = u64; + impl Config for Test { - type Balance = u64; + type Balance = Balance; type DustRemoval = DustTrap; type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = ExistentialDeposit; @@ -140,7 +121,6 @@ impl Config for Test { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = TestId; type MaxFreezes = ConstU32<2>; - type MaxHolds = ConstU32<3>; } #[derive(Clone)] diff --git a/substrate/frame/balances/src/types.rs b/substrate/frame/balances/src/types.rs index af775b8eefe5c259b1f4035485b1ef217cb88749..69d33bb023f397ba7b2464004224a233c4b913a9 100644 --- a/substrate/frame/balances/src/types.rs +++ b/substrate/frame/balances/src/types.rs @@ -152,3 +152,12 @@ impl, I: 'static> Drop for DustCleaner { } } } + +/// Whether something should be interpreted as an increase or a decrease. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub enum AdjustmentDirection { + /// Increase the amount. + Increase, + /// Decrease the amount. + Decrease, +} diff --git a/substrate/frame/balances/src/weights.rs b/substrate/frame/balances/src/weights.rs index 5671374948cdfbd4cd6596dd84726f096598eb5f..6bcfe050af714326aded0eda4fe05c1d5c149e31 100644 --- a/substrate/frame/balances/src/weights.rs +++ b/substrate/frame/balances/src/weights.rs @@ -15,29 +15,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_balances +//! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-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-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-o7yfgx5n-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_balances +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/balances/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/balances/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_balances. +/// Weight functions needed for `pallet_balances`. pub trait WeightInfo { fn transfer_allow_death() -> Weight; fn transfer_keep_alive() -> Weight; @@ -57,9 +59,10 @@ pub trait WeightInfo { fn transfer_all() -> Weight; fn force_unreserve() -> Weight; fn upgrade_accounts(u: u32, ) -> Weight; + fn force_adjust_total_issuance() -> Weight; } -/// Weights for pallet_balances using the Substrate node and recommended hardware. +/// Weights for `pallet_balances` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: `System::Account` (r:1 w:1) @@ -68,8 +71,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 58_474_000 picoseconds. - Weight::from_parts(59_117_000, 3593) + // Minimum execution time: 46_407_000 picoseconds. + Weight::from_parts(47_561_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -79,8 +82,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 44_629_000 picoseconds. - Weight::from_parts(45_798_000, 3593) + // Minimum execution time: 36_574_000 picoseconds. + Weight::from_parts(37_682_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -90,8 +93,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_483_000 picoseconds. - Weight::from_parts(16_939_000, 3593) + // Minimum execution time: 13_990_000 picoseconds. + Weight::from_parts(14_568_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -101,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 24_638_000 picoseconds. - Weight::from_parts(25_487_000, 3593) + // Minimum execution time: 19_594_000 picoseconds. + Weight::from_parts(20_148_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -112,8 +115,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 60_041_000 picoseconds. - Weight::from_parts(63_365_000, 6196) + // Minimum execution time: 47_945_000 picoseconds. + Weight::from_parts(49_363_000, 6196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -123,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 54_445_000 picoseconds. - Weight::from_parts(55_623_000, 3593) + // Minimum execution time: 45_205_000 picoseconds. + Weight::from_parts(45_952_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -134,8 +137,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 19_309_000 picoseconds. - Weight::from_parts(19_953_000, 3593) + // Minimum execution time: 16_593_000 picoseconds. + Weight::from_parts(17_123_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -146,14 +149,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 19_362_000 picoseconds. - Weight::from_parts(19_612_000, 990) - // Standard Error: 13_108 - .saturating_add(Weight::from_parts(16_444_591, 0).saturating_mul(u.into())) + // Minimum execution time: 16_182_000 picoseconds. + Weight::from_parts(16_412_000, 990) + // Standard Error: 10_148 + .saturating_add(Weight::from_parts(13_382_459, 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())) } + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_478_000 picoseconds. + Weight::from_parts(6_830_000, 0) + } } // For backwards compatibility and tests. @@ -164,8 +174,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 58_474_000 picoseconds. - Weight::from_parts(59_117_000, 3593) + // Minimum execution time: 46_407_000 picoseconds. + Weight::from_parts(47_561_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -175,8 +185,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 44_629_000 picoseconds. - Weight::from_parts(45_798_000, 3593) + // Minimum execution time: 36_574_000 picoseconds. + Weight::from_parts(37_682_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -186,8 +196,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 16_483_000 picoseconds. - Weight::from_parts(16_939_000, 3593) + // Minimum execution time: 13_990_000 picoseconds. + Weight::from_parts(14_568_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -197,8 +207,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 24_638_000 picoseconds. - Weight::from_parts(25_487_000, 3593) + // Minimum execution time: 19_594_000 picoseconds. + Weight::from_parts(20_148_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -208,8 +218,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `6196` - // Minimum execution time: 60_041_000 picoseconds. - Weight::from_parts(63_365_000, 6196) + // Minimum execution time: 47_945_000 picoseconds. + Weight::from_parts(49_363_000, 6196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -219,8 +229,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 54_445_000 picoseconds. - Weight::from_parts(55_623_000, 3593) + // Minimum execution time: 45_205_000 picoseconds. + Weight::from_parts(45_952_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -230,8 +240,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 19_309_000 picoseconds. - Weight::from_parts(19_953_000, 3593) + // Minimum execution time: 16_593_000 picoseconds. + Weight::from_parts(17_123_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -242,12 +252,19 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + u * (135 ±0)` // Estimated: `990 + u * (2603 ±0)` - // Minimum execution time: 19_362_000 picoseconds. - Weight::from_parts(19_612_000, 990) - // Standard Error: 13_108 - .saturating_add(Weight::from_parts(16_444_591, 0).saturating_mul(u.into())) + // Minimum execution time: 16_182_000 picoseconds. + Weight::from_parts(16_412_000, 990) + // Standard Error: 10_148 + .saturating_add(Weight::from_parts(13_382_459, 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())) } + fn force_adjust_total_issuance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_478_000 picoseconds. + Weight::from_parts(6_830_000, 0) + } } diff --git a/substrate/frame/beefy-mmr/Cargo.toml b/substrate/frame/beefy-mmr/Cargo.toml index f43e12d3ee7311528fa266f8c4cccfc4c3baa169..17707731773118b7028f3cff8031982c0c5d704b 100644 --- a/substrate/frame/beefy-mmr/Cargo.toml +++ b/substrate/frame/beefy-mmr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-beefy-mmr" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -14,9 +14,9 @@ workspace = true [dependencies] array-bytes = { version = "6.1", optional = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } binary-merkle-tree = { path = "../../utils/binary-merkle-tree", default-features = false } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/beefy/Cargo.toml b/substrate/frame/beefy/Cargo.toml index 999e32ab4508d65dfeec4b1340dbad4ed49c87dc..e38eaa6fb0787aca48b07fad57e0ea0adffbe7e4 100644 --- a/substrate/frame/beefy/Cargo.toml +++ b/substrate/frame/beefy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-beefy" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -13,9 +13,9 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } pallet-authorship = { path = "../authorship", default-features = false } diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index 8828fa3621853a8f3b5a2b0828fc386859ac45fc..5d963d309a4966cae1cb77516dda51c98b7154bf 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -29,8 +29,8 @@ use pallet_session::historical as pallet_session_historical; use sp_core::{crypto::KeyTypeId, ConstU128}; use sp_io::TestExternalities; use sp_runtime::{ - app_crypto::ecdsa::Public, curve::PiecewiseLinear, impl_opaque_keys, testing::TestXt, - traits::OpaqueKeys, BuildStorage, Perbill, + app_crypto::ecdsa::Public, curve::PiecewiseLinear, generic::UncheckedExtrinsic, + impl_opaque_keys, traits::OpaqueKeys, BuildStorage, Perbill, }; use sp_staking::{EraIndex, SessionIndex}; use sp_state_machine::BasicExternalities; @@ -73,7 +73,7 @@ where RuntimeCall: From, { type OverarchingCall = RuntimeCall; - type Extrinsic = TestXt; + type Extrinsic = UncheckedExtrinsic; } parameter_types! { @@ -133,7 +133,6 @@ impl pallet_balances::Config for Test { type WeightInfo = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); type FreezeIdentifier = (); type MaxFreezes = (); } diff --git a/substrate/frame/beefy/src/tests.rs b/substrate/frame/beefy/src/tests.rs index bf5ae19510ce9320c7aac3b705db605db16ecebd..453cf19a4fe156a76ed13a7da4fe59d95d4260f0 100644 --- a/substrate/frame/beefy/src/tests.rs +++ b/substrate/frame/beefy/src/tests.rs @@ -15,21 +15,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::vec; - use codec::Encode; -use sp_consensus_beefy::{ - check_equivocation_proof, generate_equivocation_proof, known_payloads::MMR_ROOT_ID, - Keyring as BeefyKeyring, Payload, ValidatorSet, KEY_TYPE as BEEFY_KEY_TYPE, -}; - -use sp_runtime::DigestItem; +use std::vec; use frame_support::{ assert_err, assert_ok, dispatch::{GetDispatchInfo, Pays}, traits::{Currency, KeyOwnerProofSystem, OnInitialize}, }; +use sp_consensus_beefy::{ + check_equivocation_proof, + known_payloads::MMR_ROOT_ID, + test_utils::{generate_equivocation_proof, Keyring as BeefyKeyring}, + Payload, ValidatorSet, KEY_TYPE as BEEFY_KEY_TYPE, +}; +use sp_runtime::DigestItem; use crate::{mock::*, Call, Config, Error, Weight, WeightInfo}; diff --git a/substrate/frame/benchmarking/Cargo.toml b/substrate/frame/benchmarking/Cargo.toml index 42001c2d08ca06bde5f7d6c0b79893b8de382666..bf42aae979cd0edd3a00ca98f8aacaba55d807dc 100644 --- a/substrate/frame/benchmarking/Cargo.toml +++ b/substrate/frame/benchmarking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,10 +18,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } linregress = { version = "0.5.1", optional = true } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } paste = "1.0" scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } frame-support = { path = "../support", default-features = false } frame-support-procedural = { path = "../support/procedural", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/benchmarking/pov/Cargo.toml b/substrate/frame/benchmarking/pov/Cargo.toml index 7c36b2f8eec3e0bf793afea0a94e8b0e1b515b3d..ce5daeb5b7b4c427e23eca59632f91507f571ae5 100644 --- a/substrate/frame/benchmarking/pov/Cargo.toml +++ b/substrate/frame/benchmarking/pov/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-benchmarking-pallet-pov" -version = "4.0.0-dev" +version = "18.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/benchmarking/src/weights.rs b/substrate/frame/benchmarking/src/weights.rs index 13d73e420cce256080d117e3bab56842d302b3a0..76f9a3fe52b564d552b698ac94e44e8c98660deb 100644 --- a/substrate/frame/benchmarking/src/weights.rs +++ b/substrate/frame/benchmarking/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for frame_benchmarking +//! Autogenerated weights for `frame_benchmarking` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/benchmarking/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/benchmarking/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for frame_benchmarking. +/// Weight functions needed for `frame_benchmarking`. pub trait WeightInfo { fn addition(i: u32, ) -> Weight; fn subtraction(i: u32, ) -> Weight; @@ -60,7 +59,7 @@ pub trait WeightInfo { fn sr25519_verification(i: u32, ) -> Weight; } -/// Weights for frame_benchmarking using the Substrate node and recommended hardware. +/// Weights for `frame_benchmarking` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1000000]`. @@ -68,101 +67,101 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(185_656, 0) + // Minimum execution time: 150_000 picoseconds. + Weight::from_parts(190_440, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 146_000 picoseconds. - Weight::from_parts(189_816, 0) + // Minimum execution time: 151_000 picoseconds. + Weight::from_parts(201_397, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 148_000 picoseconds. - Weight::from_parts(202_367, 0) + // Minimum execution time: 152_000 picoseconds. + Weight::from_parts(187_862, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 143_000 picoseconds. - Weight::from_parts(189_693, 0) + // Minimum execution time: 146_000 picoseconds. + Weight::from_parts(215_191, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 24_167_071_000 picoseconds. - Weight::from_parts(24_391_749_000, 0) + // Minimum execution time: 25_432_823_000 picoseconds. + Weight::from_parts(25_568_846_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 231_000 picoseconds. - Weight::from_parts(2_998_013, 0) - // Standard Error: 6_256 - .saturating_add(Weight::from_parts(55_456_705, 0).saturating_mul(i.into())) + // Minimum execution time: 193_000 picoseconds. + Weight::from_parts(3_846_690, 0) + // Standard Error: 6_694 + .saturating_add(Weight::from_parts(40_647_582, 0).saturating_mul(i.into())) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { /// The range of component `i` is `[0, 1000000]`. fn addition(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(185_656, 0) + // Minimum execution time: 150_000 picoseconds. + Weight::from_parts(190_440, 0) } /// The range of component `i` is `[0, 1000000]`. fn subtraction(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 146_000 picoseconds. - Weight::from_parts(189_816, 0) + // Minimum execution time: 151_000 picoseconds. + Weight::from_parts(201_397, 0) } /// The range of component `i` is `[0, 1000000]`. fn multiplication(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 148_000 picoseconds. - Weight::from_parts(202_367, 0) + // Minimum execution time: 152_000 picoseconds. + Weight::from_parts(187_862, 0) } /// The range of component `i` is `[0, 1000000]`. fn division(_i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 143_000 picoseconds. - Weight::from_parts(189_693, 0) + // Minimum execution time: 146_000 picoseconds. + Weight::from_parts(215_191, 0) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 24_167_071_000 picoseconds. - Weight::from_parts(24_391_749_000, 0) + // Minimum execution time: 25_432_823_000 picoseconds. + Weight::from_parts(25_568_846_000, 0) } /// The range of component `i` is `[0, 100]`. fn sr25519_verification(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 231_000 picoseconds. - Weight::from_parts(2_998_013, 0) - // Standard Error: 6_256 - .saturating_add(Weight::from_parts(55_456_705, 0).saturating_mul(i.into())) + // Minimum execution time: 193_000 picoseconds. + Weight::from_parts(3_846_690, 0) + // Standard Error: 6_694 + .saturating_add(Weight::from_parts(40_647_582, 0).saturating_mul(i.into())) } } diff --git a/substrate/frame/bounties/Cargo.toml b/substrate/frame/bounties/Cargo.toml index 16da862d48847ef6b04c7c669c49cacb972f5060..191a38d20b2faf4a923bc074afd905c523c8a2c1 100644 --- a/substrate/frame/bounties/Cargo.toml +++ b/substrate/frame/bounties/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-bounties" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/bounties/src/migrations/v4.rs b/substrate/frame/bounties/src/migrations/v4.rs index 4e6ba934481628f8aeca3647711cec8a7ab0fd45..71cd4f76be6e2702a1b91a390faef3111fb1a46a 100644 --- a/substrate/frame/bounties/src/migrations/v4.rs +++ b/substrate/frame/bounties/src/migrations/v4.rs @@ -15,6 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::str; use frame_support::{ storage::{generator::StorageValue, StoragePrefixedMap}, traits::{ @@ -25,7 +26,6 @@ use frame_support::{ }; use sp_core::hexdisplay::HexDisplay; use sp_io::{hashing::twox_128, storage}; -use sp_std::str; use crate as pallet_bounties; diff --git a/substrate/frame/bounties/src/tests.rs b/substrate/frame/bounties/src/tests.rs index a7177393a44f1eb3eab41fa96afbb8b73f51239c..da6596617dac4cc893cf27dfd9806436822cf516 100644 --- a/substrate/frame/bounties/src/tests.rs +++ b/substrate/frame/bounties/src/tests.rs @@ -31,9 +31,8 @@ use frame_support::{ PalletId, }; -use sp_core::H256; use sp_runtime::{ - traits::{BadOrigin, BlakeTwo256, IdentityLookup}, + traits::{BadOrigin, IdentityLookup}, BuildStorage, Perbill, Storage, }; @@ -61,29 +60,10 @@ type Balance = u64; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } impl pallet_balances::Config for Test { @@ -100,7 +80,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); diff --git a/substrate/frame/bounties/src/weights.rs b/substrate/frame/bounties/src/weights.rs index a172d15b56cc9a2a4686e3d6af61d9560635d5be..1f0e38b6702e99c4b27229c740abf76acd03f309 100644 --- a/substrate/frame/bounties/src/weights.rs +++ b/substrate/frame/bounties/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_bounties +//! Autogenerated weights for `pallet_bounties` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/bounties/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/bounties/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_bounties. +/// Weight functions needed for `pallet_bounties`. pub trait WeightInfo { fn propose_bounty(d: u32, ) -> Weight; fn approve_bounty() -> Weight; @@ -65,169 +64,169 @@ pub trait WeightInfo { fn spend_funds(b: u32, ) -> Weight; } -/// Weights for pallet_bounties using the Substrate node and recommended hardware. +/// Weights for `pallet_bounties` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Bounties BountyCount (r:1 w:1) - /// Proof: Bounties BountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:0 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyCount` (r:1 w:1) + /// Proof: `Bounties::BountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:0 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 300]`. fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `276` + // Measured: `309` // Estimated: `3593` - // Minimum execution time: 29_384_000 picoseconds. - Weight::from_parts(30_820_018, 3593) - // Standard Error: 298 - .saturating_add(Weight::from_parts(2_920, 0).saturating_mul(d.into())) + // Minimum execution time: 24_286_000 picoseconds. + Weight::from_parts(25_657_314, 3593) + // Standard Error: 215 + .saturating_add(Weight::from_parts(1_116, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn approve_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `368` + // Measured: `401` // Estimated: `3642` - // Minimum execution time: 10_873_000 picoseconds. - Weight::from_parts(11_421_000, 3642) + // Minimum execution time: 12_526_000 picoseconds. + Weight::from_parts(13_373_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `388` + // Measured: `421` // Estimated: `3642` - // Minimum execution time: 9_181_000 picoseconds. - Weight::from_parts(9_726_000, 3642) + // Minimum execution time: 11_807_000 picoseconds. + Weight::from_parts(12_340_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `564` + // Measured: `597` // Estimated: `3642` - // Minimum execution time: 30_257_000 picoseconds. - Weight::from_parts(30_751_000, 3642) + // Minimum execution time: 27_183_000 picoseconds. + Weight::from_parts(28_250_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `560` + // Measured: `593` // Estimated: `3642` - // Minimum execution time: 27_850_000 picoseconds. - Weight::from_parts(28_821_000, 3642) + // Minimum execution time: 26_775_000 picoseconds. + Weight::from_parts(27_667_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `572` + // Measured: `605` // Estimated: `3642` - // Minimum execution time: 19_164_000 picoseconds. - Weight::from_parts(20_136_000, 3642) + // Minimum execution time: 16_089_000 picoseconds. + Weight::from_parts(16_909_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `936` + // Measured: `969` // Estimated: `8799` - // Minimum execution time: 120_235_000 picoseconds. - Weight::from_parts(121_673_000, 8799) + // Minimum execution time: 104_973_000 picoseconds. + Weight::from_parts(107_696_000, 8799) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `616` + // Measured: `649` // Estimated: `3642` - // Minimum execution time: 35_713_000 picoseconds. - Weight::from_parts(37_174_000, 3642) + // Minimum execution time: 30_702_000 picoseconds. + Weight::from_parts(32_615_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `852` + // Measured: `885` // Estimated: `6196` - // Minimum execution time: 81_037_000 picoseconds. - Weight::from_parts(83_294_000, 6196) + // Minimum execution time: 72_055_000 picoseconds. + Weight::from_parts(73_900_000, 6196) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn extend_bounty_expiry() -> Weight { // Proof Size summary in bytes: - // Measured: `424` + // Measured: `457` // Estimated: `3642` - // Minimum execution time: 15_348_000 picoseconds. - Weight::from_parts(15_776_000, 3642) + // Minimum execution time: 12_057_000 picoseconds. + Weight::from_parts(12_744_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:100 w:100) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:200 w:200) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:100 w:100) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:200 w:200) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `4 + b * (297 ±0)` + // Measured: `37 + b * (297 ±0)` // Estimated: `1887 + b * (5206 ±0)` - // Minimum execution time: 5_082_000 picoseconds. - Weight::from_parts(5_126_000, 1887) - // Standard Error: 21_949 - .saturating_add(Weight::from_parts(42_635_308, 0).saturating_mul(b.into())) + // Minimum execution time: 3_489_000 picoseconds. + Weight::from_parts(8_384_129, 1887) + // Standard Error: 18_066 + .saturating_add(Weight::from_parts(31_612_331, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -236,168 +235,168 @@ impl WeightInfo for SubstrateWeight { } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Bounties BountyCount (r:1 w:1) - /// Proof: Bounties BountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:0 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyCount` (r:1 w:1) + /// Proof: `Bounties::BountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:0 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 300]`. fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `276` + // Measured: `309` // Estimated: `3593` - // Minimum execution time: 29_384_000 picoseconds. - Weight::from_parts(30_820_018, 3593) - // Standard Error: 298 - .saturating_add(Weight::from_parts(2_920, 0).saturating_mul(d.into())) + // Minimum execution time: 24_286_000 picoseconds. + Weight::from_parts(25_657_314, 3593) + // Standard Error: 215 + .saturating_add(Weight::from_parts(1_116, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn approve_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `368` + // Measured: `401` // Estimated: `3642` - // Minimum execution time: 10_873_000 picoseconds. - Weight::from_parts(11_421_000, 3642) + // Minimum execution time: 12_526_000 picoseconds. + Weight::from_parts(13_373_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `388` + // Measured: `421` // Estimated: `3642` - // Minimum execution time: 9_181_000 picoseconds. - Weight::from_parts(9_726_000, 3642) + // Minimum execution time: 11_807_000 picoseconds. + Weight::from_parts(12_340_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `564` + // Measured: `597` // Estimated: `3642` - // Minimum execution time: 30_257_000 picoseconds. - Weight::from_parts(30_751_000, 3642) + // Minimum execution time: 27_183_000 picoseconds. + Weight::from_parts(28_250_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `560` + // Measured: `593` // Estimated: `3642` - // Minimum execution time: 27_850_000 picoseconds. - Weight::from_parts(28_821_000, 3642) + // Minimum execution time: 26_775_000 picoseconds. + Weight::from_parts(27_667_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `572` + // Measured: `605` // Estimated: `3642` - // Minimum execution time: 19_164_000 picoseconds. - Weight::from_parts(20_136_000, 3642) + // Minimum execution time: 16_089_000 picoseconds. + Weight::from_parts(16_909_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `936` + // Measured: `969` // Estimated: `8799` - // Minimum execution time: 120_235_000 picoseconds. - Weight::from_parts(121_673_000, 8799) + // Minimum execution time: 104_973_000 picoseconds. + Weight::from_parts(107_696_000, 8799) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `616` + // Measured: `649` // Estimated: `3642` - // Minimum execution time: 35_713_000 picoseconds. - Weight::from_parts(37_174_000, 3642) + // Minimum execution time: 30_702_000 picoseconds. + Weight::from_parts(32_615_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `852` + // Measured: `885` // Estimated: `6196` - // Minimum execution time: 81_037_000 picoseconds. - Weight::from_parts(83_294_000, 6196) + // Minimum execution time: 72_055_000 picoseconds. + Weight::from_parts(73_900_000, 6196) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn extend_bounty_expiry() -> Weight { // Proof Size summary in bytes: - // Measured: `424` + // Measured: `457` // Estimated: `3642` - // Minimum execution time: 15_348_000 picoseconds. - Weight::from_parts(15_776_000, 3642) + // Minimum execution time: 12_057_000 picoseconds. + Weight::from_parts(12_744_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:100 w:100) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:200 w:200) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:100 w:100) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:200 w:200) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `4 + b * (297 ±0)` + // Measured: `37 + b * (297 ±0)` // Estimated: `1887 + b * (5206 ±0)` - // Minimum execution time: 5_082_000 picoseconds. - Weight::from_parts(5_126_000, 1887) - // Standard Error: 21_949 - .saturating_add(Weight::from_parts(42_635_308, 0).saturating_mul(b.into())) + // Minimum execution time: 3_489_000 picoseconds. + Weight::from_parts(8_384_129, 1887) + // Standard Error: 18_066 + .saturating_add(Weight::from_parts(31_612_331, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) diff --git a/substrate/frame/broker/Cargo.toml b/substrate/frame/broker/Cargo.toml index 77757c30463619750fca0f28d0c6229367dd891d..31f9a6b63178d42410c16a3354f43ec35fe00f3d 100644 --- a/substrate/frame/broker/Cargo.toml +++ b/substrate/frame/broker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-broker" -version = "0.1.0" +version = "0.6.0" description = "Brokerage tool for managing Polkadot Core scheduling" authors.workspace = true homepage = "https://substrate.io" diff --git a/substrate/frame/broker/src/coretime_interface.rs b/substrate/frame/broker/src/coretime_interface.rs index 9e853e8f3fe0b1e2f76e3fac57b9ce0b8d03ab07..58efa7fa92bb0fa6c29a45375593f2246f5eaa8a 100644 --- a/substrate/frame/broker/src/coretime_interface.rs +++ b/substrate/frame/broker/src/coretime_interface.rs @@ -51,7 +51,7 @@ pub enum CoreAssignment { pub type RCBlockNumberOf = as BlockNumberProvider>::BlockNumber; /// Relay chain block number provider of `T` that implements [`CoretimeInterface`]. -pub type RCBlockNumberProviderOf = ::RealyChainBlockNumberProvider; +pub type RCBlockNumberProviderOf = ::RelayChainBlockNumberProvider; /// Type able to accept Coretime scheduling instructions and provide certain usage information. /// Generally implemented by the Relay-chain or some means of communicating with it. @@ -65,7 +65,7 @@ pub trait CoretimeInterface { type Balance: AtLeast32BitUnsigned; /// A provider for the relay chain block number. - type RealyChainBlockNumberProvider: BlockNumberProvider; + type RelayChainBlockNumberProvider: BlockNumberProvider; /// Requests the Relay-chain to alter the number of schedulable cores to `count`. Under normal /// operation, the Relay-chain SHOULD send a `notify_core_count(count)` message back. @@ -128,7 +128,7 @@ pub trait CoretimeInterface { impl CoretimeInterface for () { type AccountId = (); type Balance = u64; - type RealyChainBlockNumberProvider = (); + type RelayChainBlockNumberProvider = (); fn request_core_count(_count: CoreIndex) {} fn request_revenue_info_at(_when: RCBlockNumberOf) {} diff --git a/substrate/frame/broker/src/mock.rs b/substrate/frame/broker/src/mock.rs index 19c72340353c66d9bedf5d016e02dad7102116cb..ac327c4143e7b51e5350a0065ab861b1618ca0f5 100644 --- a/substrate/frame/broker/src/mock.rs +++ b/substrate/frame/broker/src/mock.rs @@ -77,7 +77,7 @@ pub struct TestCoretimeProvider; impl CoretimeInterface for TestCoretimeProvider { type AccountId = u64; type Balance = u64; - type RealyChainBlockNumberProvider = System; + type RelayChainBlockNumberProvider = System; fn request_core_count(count: CoreIndex) { CoreCountInbox::::put(count); } diff --git a/substrate/frame/broker/src/nonfungible_impl.rs b/substrate/frame/broker/src/nonfungible_impl.rs index fe95438cb1afa0eaae8a409352ebc2855739435c..b2e88bf09a0e9d59e90648c0e202872969b4f391 100644 --- a/substrate/frame/broker/src/nonfungible_impl.rs +++ b/substrate/frame/broker/src/nonfungible_impl.rs @@ -18,7 +18,7 @@ use super::*; use frame_support::{ pallet_prelude::{DispatchResult, *}, - traits::nonfungible::{Inspect, Transfer}, + traits::nonfungible::{Inspect, Mutate, Transfer}, }; use sp_std::vec::Vec; @@ -50,3 +50,7 @@ impl Transfer for Pallet { Self::do_transfer((*index).into(), None, dest.clone()).map_err(Into::into) } } + +// We don't allow any of the mutate operations, so the default implementation is used, which will +// return `TokenError::Unsupported` in case any of the operations is called. +impl Mutate for Pallet {} diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index 6aa9ca84fc419c4ec94190196939a8bee7501d67..3e1e36f7d4489c2ad6c7e1e555f1c8a40250083a 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -20,11 +20,11 @@ use crate::{core_mask::*, mock::*, *}; use frame_support::{ assert_noop, assert_ok, - traits::nonfungible::{Inspect as NftInspect, Transfer}, + traits::nonfungible::{Inspect as NftInspect, Mutate, Transfer}, BoundedVec, }; use frame_system::RawOrigin::Root; -use sp_runtime::traits::Get; +use sp_runtime::{traits::Get, TokenError}; use CoreAssignment::*; use CoretimeTraceItem::*; use Finality::*; @@ -197,6 +197,26 @@ fn transfer_works() { }); } +#[test] +fn mutate_operations_unsupported_for_regions() { + TestExt::new().execute_with(|| { + let region_id = RegionId { begin: 0, core: 0, mask: CoreMask::complete() }; + assert_noop!( + >::mint_into(®ion_id.into(), &2), + TokenError::Unsupported + ); + assert_noop!(>::burn(®ion_id.into(), None), TokenError::Unsupported); + assert_noop!( + >::set_attribute(®ion_id.into(), &[], &[]), + TokenError::Unsupported + ); + assert_noop!( + >::set_typed_attribute::(®ion_id.into(), &0, &0), + TokenError::Unsupported + ); + }); +} + #[test] fn permanent_is_not_reassignable() { TestExt::new().endow(1, 1000).execute_with(|| { @@ -843,6 +863,29 @@ fn cannot_set_expired_lease() { }); } +#[test] +fn short_leases_are_cleaned() { + TestExt::new().region_length(3).execute_with(|| { + assert_ok!(Broker::do_start_sales(200, 1)); + advance_to(2); + + // New leases are allowed to expire within this region given expiry > `current_timeslice`. + assert_noop!( + Broker::do_set_lease(1000, Broker::current_timeslice()), + Error::::AlreadyExpired + ); + assert_eq!(Leases::::get().len(), 0); + assert_ok!(Broker::do_set_lease(1000, Broker::current_timeslice().saturating_add(1))); + assert_eq!(Leases::::get().len(), 1); + + // But are cleaned up in the next rotate_sale. + let config = Configuration::::get().unwrap(); + let timeslice_period: u64 = ::TimeslicePeriod::get(); + advance_to(timeslice_period.saturating_mul(config.region_length.into())); + assert_eq!(Leases::::get().len(), 0); + }); +} + #[test] fn leases_are_limited() { TestExt::new().execute_with(|| { diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index 8b7860c8e3af6daaaa433d9b7d093c94f292a5a6..388370bce4d4b8045122c02ad758b62b4476fd15 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -216,7 +216,9 @@ impl Pallet { let assignment = CoreAssignment::Task(task); let schedule = BoundedVec::truncate_from(vec![ScheduleItem { mask, assignment }]); Workplan::::insert((region_begin, first_core), &schedule); - let expiring = until >= region_begin && until < region_end; + // Separate these to avoid missed expired leases hanging around forever. + let expired = until < region_end; + let expiring = until >= region_begin && expired; if expiring { // last time for this one - make it renewable. let renewal_id = AllowedRenewalId { core: first_core, when: region_end }; @@ -231,7 +233,7 @@ impl Pallet { Self::deposit_event(Event::LeaseEnding { when: region_end, task }); } first_core.saturating_inc(); - !expiring + !expired }); Leases::::put(&leases); diff --git a/substrate/frame/broker/src/weights.rs b/substrate/frame/broker/src/weights.rs index a8f50eeee6e6ceaf284c1764a68411956753cfff..133ea63e35f7539c69d07cd4d442a4ce97aab80f 100644 --- a/substrate/frame/broker/src/weights.rs +++ b/substrate/frame/broker/src/weights.rs @@ -17,26 +17,28 @@ //! Autogenerated weights for `pallet_broker` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-pzhd7p6z-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_broker +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_broker -// --chain=dev -// --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/broker/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -87,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040_000 picoseconds. - Weight::from_parts(3_344_000, 0) + // Minimum execution time: 2_701_000 picoseconds. + Weight::from_parts(2_902_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -97,8 +99,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 21_259_000 picoseconds. - Weight::from_parts(22_110_000, 7496) + // Minimum execution time: 18_056_000 picoseconds. + Weight::from_parts(19_093_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -108,8 +110,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 20_330_000 picoseconds. - Weight::from_parts(20_826_000, 7496) + // Minimum execution time: 17_233_000 picoseconds. + Weight::from_parts(17_788_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -119,8 +121,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 13_411_000 picoseconds. - Weight::from_parts(13_960_000, 1526) + // Minimum execution time: 9_740_000 picoseconds. + Weight::from_parts(10_504_000, 1526) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -139,14 +141,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 57_770_000 picoseconds. - Weight::from_parts(61_047_512, 8499) - // Standard Error: 165 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Minimum execution time: 49_728_000 picoseconds. + Weight::from_parts(52_765_861, 8499) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)) } @@ -162,10 +162,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `568` - // Estimated: `2053` - // Minimum execution time: 51_196_000 picoseconds. - Weight::from_parts(52_382_000, 2053) + // Measured: `635` + // Estimated: `2120` + // Minimum execution time: 41_986_000 picoseconds. + Weight::from_parts(43_465_000, 2120) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -185,10 +185,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `686` + // Measured: `753` // Estimated: `4698` - // Minimum execution time: 71_636_000 picoseconds. - Weight::from_parts(73_679_000, 4698) + // Minimum execution time: 61_779_000 picoseconds. + Weight::from_parts(62_563_000, 4698) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -198,8 +198,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 19_182_000 picoseconds. - Weight::from_parts(19_775_000, 3550) + // Minimum execution time: 16_962_000 picoseconds. + Weight::from_parts(17_733_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -209,21 +209,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(21_557_000, 3550) + // Minimum execution time: 18_380_000 picoseconds. + Weight::from_parts(19_105_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: `Broker::Regions` (r:1 w:2) + /// Storage: `Broker::Regions` (r:1 w:3) /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 21_190_000 picoseconds. - Weight::from_parts(22_215_000, 3550) + // Minimum execution time: 20_115_000 picoseconds. + Weight::from_parts(20_741_000, 3550) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) @@ -237,8 +237,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `740` // Estimated: `4681` - // Minimum execution time: 34_591_000 picoseconds. - Weight::from_parts(36_227_000, 4681) + // Minimum execution time: 31_339_000 picoseconds. + Weight::from_parts(32_639_000, 4681) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -256,8 +256,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `775` // Estimated: `5996` - // Minimum execution time: 40_346_000 picoseconds. - Weight::from_parts(41_951_000, 5996) + // Minimum execution time: 37_542_000 picoseconds. + Weight::from_parts(38_521_000, 5996) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -272,10 +272,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 75_734_000 picoseconds. - Weight::from_parts(78_168_395, 6196) - // Standard Error: 63_180 - .saturating_add(Weight::from_parts(1_076_259, 0).saturating_mul(m.into())) + // Minimum execution time: 66_176_000 picoseconds. + Weight::from_parts(68_356_745, 6196) + // Standard Error: 68_008 + .saturating_add(Weight::from_parts(1_558_419, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -287,8 +287,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 46_383_000 picoseconds. - Weight::from_parts(47_405_000, 3593) + // Minimum execution time: 41_130_000 picoseconds. + Weight::from_parts(41_914_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -300,8 +300,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `603` // Estimated: `3550` - // Minimum execution time: 30_994_000 picoseconds. - Weight::from_parts(31_979_000, 3550) + // Minimum execution time: 31_042_000 picoseconds. + Weight::from_parts(34_087_000, 3550) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -315,8 +315,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(44_010_000, 3533) + // Minimum execution time: 39_116_000 picoseconds. + Weight::from_parts(39_990_000, 3533) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -330,10 +330,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `830` + // Measured: `995` // Estimated: `3593` - // Minimum execution time: 45_266_000 picoseconds. - Weight::from_parts(48_000_000, 3593) + // Minimum execution time: 47_547_000 picoseconds. + Weight::from_parts(50_274_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -343,34 +343,32 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: - // Measured: `525` + // Measured: `661` // Estimated: `4698` - // Minimum execution time: 25_365_000 picoseconds. - Weight::from_parts(26_920_000, 4698) + // Minimum execution time: 26_707_000 picoseconds. + Weight::from_parts(27_217_000, 4698) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// The range of component `n` is `[0, 1000]`. - fn request_core_count(n: u32, ) -> Weight { + fn request_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_519_000 picoseconds. - Weight::from_parts(7_098_698, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(8, 0).saturating_mul(n.into())) + // Minimum execution time: 4_651_000 picoseconds. + Weight::from_parts(5_231_385, 0) } - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `3563` - // Minimum execution time: 7_608_000 picoseconds. - Weight::from_parts(8_157_815, 3563) - // Standard Error: 26 - .saturating_add(Weight::from_parts(48, 0).saturating_mul(n.into())) + // Measured: `404` + // Estimated: `1487` + // Minimum execution time: 6_806_000 picoseconds. + Weight::from_parts(7_264_002, 1487) + // Standard Error: 21 + .saturating_add(Weight::from_parts(31, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -386,10 +384,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `905` - // Estimated: `4370` - // Minimum execution time: 59_993_000 picoseconds. - Weight::from_parts(61_752_000, 4370) + // Measured: `972` + // Estimated: `4437` + // Minimum execution time: 48_297_000 picoseconds. + Weight::from_parts(49_613_000, 4437) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -408,10 +406,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 41_863_000 picoseconds. - Weight::from_parts(44_033_031, 8499) - // Standard Error: 116 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(n.into())) + // Minimum execution time: 36_715_000 picoseconds. + Weight::from_parts(38_580_380, 8499) + // Standard Error: 91 + .saturating_add(Weight::from_parts(1_163, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } @@ -423,8 +421,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 9_588_000 picoseconds. - Weight::from_parts(9_925_000, 3493) + // Minimum execution time: 7_564_000 picoseconds. + Weight::from_parts(7_932_000, 3493) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -436,8 +434,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(20_482_000, 4681) + // Minimum execution time: 17_082_000 picoseconds. + Weight::from_parts(17_662_000, 4681) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -445,28 +443,35 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(184_000, 0) + // Minimum execution time: 175_000 picoseconds. + Weight::from_parts(223_000, 0) } + /// Storage: `Broker::CoreCountInbox` (r:0 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) fn notify_core_count() -> Weight { - T::DbWeight::get().reads_writes(1, 1) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_432_000 picoseconds. + Weight::from_parts(2_536_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:0) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `699` - // Estimated: `4164` - // Minimum execution time: 19_824_000 picoseconds. - Weight::from_parts(20_983_000, 4164) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 13_080_000 picoseconds. + Weight::from_parts(13_937_000, 4068) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } @@ -478,8 +483,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_040_000 picoseconds. - Weight::from_parts(3_344_000, 0) + // Minimum execution time: 2_701_000 picoseconds. + Weight::from_parts(2_902_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -488,8 +493,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 21_259_000 picoseconds. - Weight::from_parts(22_110_000, 7496) + // Minimum execution time: 18_056_000 picoseconds. + Weight::from_parts(19_093_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -499,8 +504,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 20_330_000 picoseconds. - Weight::from_parts(20_826_000, 7496) + // Minimum execution time: 17_233_000 picoseconds. + Weight::from_parts(17_788_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -510,8 +515,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 13_411_000 picoseconds. - Weight::from_parts(13_960_000, 1526) + // Minimum execution time: 9_740_000 picoseconds. + Weight::from_parts(10_504_000, 1526) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -530,14 +535,12 @@ impl WeightInfo for () { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn start_sales(n: u32, ) -> Weight { + fn start_sales(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 57_770_000 picoseconds. - Weight::from_parts(61_047_512, 8499) - // Standard Error: 165 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(n.into())) + // Minimum execution time: 49_728_000 picoseconds. + Weight::from_parts(52_765_861, 8499) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(16_u64)) } @@ -553,10 +556,10 @@ impl WeightInfo for () { /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `568` - // Estimated: `2053` - // Minimum execution time: 51_196_000 picoseconds. - Weight::from_parts(52_382_000, 2053) + // Measured: `635` + // Estimated: `2120` + // Minimum execution time: 41_986_000 picoseconds. + Weight::from_parts(43_465_000, 2120) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -576,10 +579,10 @@ impl WeightInfo for () { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `686` + // Measured: `753` // Estimated: `4698` - // Minimum execution time: 71_636_000 picoseconds. - Weight::from_parts(73_679_000, 4698) + // Minimum execution time: 61_779_000 picoseconds. + Weight::from_parts(62_563_000, 4698) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -589,8 +592,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 19_182_000 picoseconds. - Weight::from_parts(19_775_000, 3550) + // Minimum execution time: 16_962_000 picoseconds. + Weight::from_parts(17_733_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -600,21 +603,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 20_688_000 picoseconds. - Weight::from_parts(21_557_000, 3550) + // Minimum execution time: 18_380_000 picoseconds. + Weight::from_parts(19_105_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `Broker::Regions` (r:1 w:2) + /// Storage: `Broker::Regions` (r:1 w:3) /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3550` - // Minimum execution time: 21_190_000 picoseconds. - Weight::from_parts(22_215_000, 3550) + // Minimum execution time: 20_115_000 picoseconds. + Weight::from_parts(20_741_000, 3550) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) @@ -628,8 +631,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `740` // Estimated: `4681` - // Minimum execution time: 34_591_000 picoseconds. - Weight::from_parts(36_227_000, 4681) + // Minimum execution time: 31_339_000 picoseconds. + Weight::from_parts(32_639_000, 4681) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -647,8 +650,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `775` // Estimated: `5996` - // Minimum execution time: 40_346_000 picoseconds. - Weight::from_parts(41_951_000, 5996) + // Minimum execution time: 37_542_000 picoseconds. + Weight::from_parts(38_521_000, 5996) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -663,10 +666,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 75_734_000 picoseconds. - Weight::from_parts(78_168_395, 6196) - // Standard Error: 63_180 - .saturating_add(Weight::from_parts(1_076_259, 0).saturating_mul(m.into())) + // Minimum execution time: 66_176_000 picoseconds. + Weight::from_parts(68_356_745, 6196) + // Standard Error: 68_008 + .saturating_add(Weight::from_parts(1_558_419, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -678,8 +681,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 46_383_000 picoseconds. - Weight::from_parts(47_405_000, 3593) + // Minimum execution time: 41_130_000 picoseconds. + Weight::from_parts(41_914_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -691,8 +694,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `603` // Estimated: `3550` - // Minimum execution time: 30_994_000 picoseconds. - Weight::from_parts(31_979_000, 3550) + // Minimum execution time: 31_042_000 picoseconds. + Weight::from_parts(34_087_000, 3550) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -706,8 +709,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(44_010_000, 3533) + // Minimum execution time: 39_116_000 picoseconds. + Weight::from_parts(39_990_000, 3533) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -721,10 +724,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn drop_history() -> Weight { // Proof Size summary in bytes: - // Measured: `830` + // Measured: `995` // Estimated: `3593` - // Minimum execution time: 45_266_000 picoseconds. - Weight::from_parts(48_000_000, 3593) + // Minimum execution time: 47_547_000 picoseconds. + Weight::from_parts(50_274_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -734,34 +737,32 @@ impl WeightInfo for () { /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: - // Measured: `525` + // Measured: `661` // Estimated: `4698` - // Minimum execution time: 25_365_000 picoseconds. - Weight::from_parts(26_920_000, 4698) + // Minimum execution time: 26_707_000 picoseconds. + Weight::from_parts(27_217_000, 4698) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// The range of component `n` is `[0, 1000]`. - fn request_core_count(n: u32, ) -> Weight { + fn request_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_519_000 picoseconds. - Weight::from_parts(7_098_698, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(8, 0).saturating_mul(n.into())) + // Minimum execution time: 4_651_000 picoseconds. + Weight::from_parts(5_231_385, 0) } - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn process_core_count(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `98` - // Estimated: `3563` - // Minimum execution time: 7_608_000 picoseconds. - Weight::from_parts(8_157_815, 3563) - // Standard Error: 26 - .saturating_add(Weight::from_parts(48, 0).saturating_mul(n.into())) + // Measured: `404` + // Estimated: `1487` + // Minimum execution time: 6_806_000 picoseconds. + Weight::from_parts(7_264_002, 1487) + // Standard Error: 21 + .saturating_add(Weight::from_parts(31, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -777,10 +778,10 @@ impl WeightInfo for () { /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn process_revenue() -> Weight { // Proof Size summary in bytes: - // Measured: `905` - // Estimated: `4370` - // Minimum execution time: 59_993_000 picoseconds. - Weight::from_parts(61_752_000, 4370) + // Measured: `972` + // Estimated: `4437` + // Minimum execution time: 48_297_000 picoseconds. + Weight::from_parts(49_613_000, 4437) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -799,10 +800,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 41_863_000 picoseconds. - Weight::from_parts(44_033_031, 8499) - // Standard Error: 116 - .saturating_add(Weight::from_parts(764, 0).saturating_mul(n.into())) + // Minimum execution time: 36_715_000 picoseconds. + Weight::from_parts(38_580_380, 8499) + // Standard Error: 91 + .saturating_add(Weight::from_parts(1_163, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } @@ -814,8 +815,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 9_588_000 picoseconds. - Weight::from_parts(9_925_000, 3493) + // Minimum execution time: 7_564_000 picoseconds. + Weight::from_parts(7_932_000, 3493) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -827,8 +828,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 19_308_000 picoseconds. - Weight::from_parts(20_482_000, 4681) + // Minimum execution time: 17_082_000 picoseconds. + Weight::from_parts(17_662_000, 4681) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -836,28 +837,34 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 147_000 picoseconds. - Weight::from_parts(184_000, 0) + // Minimum execution time: 175_000 picoseconds. + Weight::from_parts(223_000, 0) } + /// Storage: `Broker::CoreCountInbox` (r:0 w:1) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) fn notify_core_count() -> Weight { - RocksDbWeight::get().reads(1) - .saturating_add(RocksDbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_432_000 picoseconds. + Weight::from_parts(2_536_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x18194fcb5c1fcace44d2d0a004272614` (r:1 w:1) + /// Storage: `Broker::CoreCountInbox` (r:1 w:0) + /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) /// Proof: UNKNOWN KEY `0xf308d869daf021a7724e69c557dd8dbe` (r:1 w:1) fn do_tick_base() -> Weight { // Proof Size summary in bytes: - // Measured: `699` - // Estimated: `4164` - // Minimum execution time: 19_824_000 picoseconds. - Weight::from_parts(20_983_000, 4164) + // Measured: `603` + // Estimated: `4068` + // Minimum execution time: 13_080_000 picoseconds. + Weight::from_parts(13_937_000, 4068) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/substrate/frame/child-bounties/Cargo.toml b/substrate/frame/child-bounties/Cargo.toml index 6c1c362dc56fe769a67d61a9b6a559c9cb934206..589ca95a7516148531434fc641af40f0f36796bb 100644 --- a/substrate/frame/child-bounties/Cargo.toml +++ b/substrate/frame/child-bounties/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-child-bounties" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/child-bounties/src/tests.rs b/substrate/frame/child-bounties/src/tests.rs index e57721adad0ca9e10f5936f74533ffc4ff575b8a..276a90a3e29cf78c91e0dc141d57e90c41df7d20 100644 --- a/substrate/frame/child-bounties/src/tests.rs +++ b/substrate/frame/child-bounties/src/tests.rs @@ -32,9 +32,8 @@ use frame_support::{ PalletId, }; -use sp_core::H256; use sp_runtime::{ - traits::{BadOrigin, BlakeTwo256, IdentityLookup}, + traits::{BadOrigin, IdentityLookup}, BuildStorage, Perbill, Permill, TokenError, }; @@ -64,29 +63,10 @@ type Balance = u64; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = u128; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -103,7 +83,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); diff --git a/substrate/frame/child-bounties/src/weights.rs b/substrate/frame/child-bounties/src/weights.rs index e4c1f238e88b7f94e177ab6005fca0beb3e91d1f..6427517b45b040a649e59c90c2a2bd920e1e7621 100644 --- a/substrate/frame/child-bounties/src/weights.rs +++ b/substrate/frame/child-bounties/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_child_bounties +//! Autogenerated weights for `pallet_child_bounties` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/child-bounties/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/child-bounties/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_child_bounties. +/// Weight functions needed for `pallet_child_bounties`. pub trait WeightInfo { fn add_child_bounty(d: u32, ) -> Weight; fn propose_curator() -> Weight; @@ -62,288 +61,288 @@ pub trait WeightInfo { fn close_child_bounty_active() -> Weight; } -/// Weights for pallet_child_bounties using the Substrate node and recommended hardware. +/// Weights for `pallet_child_bounties` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyCount (r:1 w:1) - /// Proof: ChildBounties ChildBountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:0 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyCount` (r:1 w:1) + /// Proof: `ChildBounties::ChildBountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:0 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 300]`. fn add_child_bounty(_d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `712` + // Measured: `745` // Estimated: `6196` - // Minimum execution time: 69_805_000 picoseconds. - Weight::from_parts(73_216_717, 6196) + // Minimum execution time: 60_674_000 picoseconds. + Weight::from_parts(63_477_428, 6196) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `766` + // Measured: `799` // Estimated: `3642` - // Minimum execution time: 18_190_000 picoseconds. - Weight::from_parts(18_932_000, 3642) + // Minimum execution time: 17_754_000 picoseconds. + Weight::from_parts(18_655_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `912` + // Measured: `945` // Estimated: `3642` - // Minimum execution time: 35_035_000 picoseconds. - Weight::from_parts(35_975_000, 3642) + // Minimum execution time: 31_580_000 picoseconds. + Weight::from_parts(32_499_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `912` + // Measured: `945` // Estimated: `3642` - // Minimum execution time: 37_636_000 picoseconds. - Weight::from_parts(38_610_000, 3642) + // Minimum execution time: 33_536_000 picoseconds. + Weight::from_parts(34_102_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) fn award_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `809` + // Measured: `842` // Estimated: `3642` - // Minimum execution time: 22_457_000 picoseconds. - Weight::from_parts(23_691_000, 3642) + // Minimum execution time: 18_295_000 picoseconds. + Weight::from_parts(19_496_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `682` // Estimated: `8799` - // Minimum execution time: 118_272_000 picoseconds. - Weight::from_parts(121_646_000, 8799) + // Minimum execution time: 102_367_000 picoseconds. + Weight::from_parts(104_352_000, 8799) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: - // Measured: `1012` + // Measured: `1045` // Estimated: `6196` - // Minimum execution time: 75_717_000 picoseconds. - Weight::from_parts(77_837_000, 6196) + // Minimum execution time: 69_051_000 picoseconds. + Weight::from_parts(71_297_000, 6196) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `1199` + // Measured: `1232` // Estimated: `8799` - // Minimum execution time: 94_215_000 picoseconds. - Weight::from_parts(97_017_000, 8799) + // Minimum execution time: 84_053_000 picoseconds. + Weight::from_parts(86_072_000, 8799) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyCount (r:1 w:1) - /// Proof: ChildBounties ChildBountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:0 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyCount` (r:1 w:1) + /// Proof: `ChildBounties::ChildBountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:0 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 300]`. fn add_child_bounty(_d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `712` + // Measured: `745` // Estimated: `6196` - // Minimum execution time: 69_805_000 picoseconds. - Weight::from_parts(73_216_717, 6196) + // Minimum execution time: 60_674_000 picoseconds. + Weight::from_parts(63_477_428, 6196) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `766` + // Measured: `799` // Estimated: `3642` - // Minimum execution time: 18_190_000 picoseconds. - Weight::from_parts(18_932_000, 3642) + // Minimum execution time: 17_754_000 picoseconds. + Weight::from_parts(18_655_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `912` + // Measured: `945` // Estimated: `3642` - // Minimum execution time: 35_035_000 picoseconds. - Weight::from_parts(35_975_000, 3642) + // Minimum execution time: 31_580_000 picoseconds. + Weight::from_parts(32_499_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `912` + // Measured: `945` // Estimated: `3642` - // Minimum execution time: 37_636_000 picoseconds. - Weight::from_parts(38_610_000, 3642) + // Minimum execution time: 33_536_000 picoseconds. + Weight::from_parts(34_102_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) fn award_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `809` + // Measured: `842` // Estimated: `3642` - // Minimum execution time: 22_457_000 picoseconds. - Weight::from_parts(23_691_000, 3642) + // Minimum execution time: 18_295_000 picoseconds. + Weight::from_parts(19_496_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: // Measured: `682` // Estimated: `8799` - // Minimum execution time: 118_272_000 picoseconds. - Weight::from_parts(121_646_000, 8799) + // Minimum execution time: 102_367_000 picoseconds. + Weight::from_parts(104_352_000, 8799) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: - // Measured: `1012` + // Measured: `1045` // Estimated: `6196` - // Minimum execution time: 75_717_000 picoseconds. - Weight::from_parts(77_837_000, 6196) + // Minimum execution time: 69_051_000 picoseconds. + Weight::from_parts(71_297_000, 6196) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: Bounties Bounties (r:1 w:0) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBounties (r:1 w:1) - /// Proof: ChildBounties ChildBounties (max_values: None, max_size: Some(145), added: 2620, mode: MaxEncodedLen) - /// Storage: System Account (r:3 w:3) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1) - /// Proof: ChildBounties ChildrenCuratorFees (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:1) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1) - /// Proof: ChildBounties ChildBountyDescriptions (max_values: None, max_size: Some(314), added: 2789, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `1199` + // Measured: `1232` // Estimated: `8799` - // Minimum execution time: 94_215_000 picoseconds. - Weight::from_parts(97_017_000, 8799) + // Minimum execution time: 84_053_000 picoseconds. + Weight::from_parts(86_072_000, 8799) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } diff --git a/substrate/frame/collective/Cargo.toml b/substrate/frame/collective/Cargo.toml index fb0bace740c5d9018835b431056ac8645f096e2a..e19e1496e7b5016819b1efaede408b19ae013c48 100644 --- a/substrate/frame/collective/Cargo.toml +++ b/substrate/frame/collective/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-collective" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/collective/src/benchmarking.rs b/substrate/frame/collective/src/benchmarking.rs index 503d72510530903ba1676fe6630c326ef1a4f770..7b5df17b60a696a17b706dd01f05f2a01d3e787e 100644 --- a/substrate/frame/collective/src/benchmarking.rs +++ b/substrate/frame/collective/src/benchmarking.rs @@ -20,8 +20,8 @@ use super::*; use crate::Pallet as Collective; +use core::mem::size_of; use sp_runtime::traits::Bounded; -use sp_std::mem::size_of; use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_system::{ @@ -105,7 +105,7 @@ benchmarks_instance_pallet! { }: _(SystemOrigin::Root, new_members.clone(), new_members.last().cloned(), T::MaxMembers::get()) verify { new_members.sort(); - assert_eq!(Collective::::members(), new_members); + assert_eq!(Members::::get(), new_members); } execute { @@ -199,14 +199,14 @@ benchmarks_instance_pallet! { )?; } - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + 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(); }: propose(SystemOrigin::Signed(caller.clone()), threshold, Box::new(proposal.clone()), bytes_in_storage) verify { // New proposal is recorded - assert_eq!(Collective::::proposals().len(), p as usize); + 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()); } @@ -269,7 +269,7 @@ benchmarks_instance_pallet! { approve, )?; - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Voter switches vote to nay, but does not kill the vote, just updates + inserts let approve = false; @@ -280,8 +280,8 @@ benchmarks_instance_pallet! { }: _(SystemOrigin::Signed(voter), last_hash, index, approve) verify { // All proposals exist and the last proposal has just been updated. - assert_eq!(Collective::::proposals().len(), p as usize); - let voting = Collective::::voting(&last_hash).ok_or("Proposal Missing")?; + 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); } @@ -344,7 +344,7 @@ benchmarks_instance_pallet! { approve, )?; - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Voter switches vote to nay, which kills the vote let approve = false; @@ -361,7 +361,7 @@ benchmarks_instance_pallet! { }: close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage) verify { // The last proposal is removed. - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); } @@ -428,7 +428,7 @@ benchmarks_instance_pallet! { true, )?; - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); // Caller switches vote to aye, which passes the vote let index = p - 1; @@ -442,7 +442,7 @@ benchmarks_instance_pallet! { }: close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage) verify { // The last proposal is removed. - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); } @@ -519,12 +519,12 @@ benchmarks_instance_pallet! { )?; System::::set_block_number(BlockNumberFor::::max_value()); - assert_eq!(Collective::::proposals().len(), p as usize); + 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 { - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); } @@ -591,12 +591,12 @@ benchmarks_instance_pallet! { // caller is prime, prime already votes aye by creating the proposal System::::set_block_number(BlockNumberFor::::max_value()); - assert_eq!(Collective::::proposals().len(), p as usize); + 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 { - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); } @@ -640,11 +640,11 @@ benchmarks_instance_pallet! { } System::::set_block_number(BlockNumberFor::::max_value()); - assert_eq!(Collective::::proposals().len(), p as usize); + assert_eq!(Proposals::::get().len(), p as usize); }: _(SystemOrigin::Root, last_hash) verify { - assert_eq!(Collective::::proposals().len(), (p - 1) as usize); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); } diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index 10f989e5c4cc6ef92c258c21455d8f227dcf3dbe..882e99a6d0051306a103630a893d9fb44b803072 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -40,7 +40,6 @@ //! If there are not, or if no prime is set, then the motion is dropped without being executed. #![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit = "128"] use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -177,7 +176,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] @@ -262,36 +261,30 @@ pub mod pallet { /// The hashes of the active proposals. #[pallet::storage] - #[pallet::getter(fn proposals)] pub type Proposals, I: 'static = ()> = StorageValue<_, BoundedVec, ValueQuery>; /// Actual proposal for a given hash, if it's current. #[pallet::storage] - #[pallet::getter(fn proposal_of)] pub type ProposalOf, I: 'static = ()> = StorageMap<_, Identity, T::Hash, >::Proposal, OptionQuery>; /// Votes on a given proposal, if it is ongoing. #[pallet::storage] - #[pallet::getter(fn voting)] pub type Voting, I: 'static = ()> = StorageMap<_, Identity, T::Hash, Votes>, OptionQuery>; /// Proposals so far. #[pallet::storage] - #[pallet::getter(fn proposal_count)] pub type ProposalCount, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; /// The current members of the collective. This is stored sorted (just by value). #[pallet::storage] - #[pallet::getter(fn members)] pub type Members, I: 'static = ()> = StorageValue<_, Vec, ValueQuery>; /// The prime member that helps determine the default vote behavior in case of absentations. #[pallet::storage] - #[pallet::getter(fn prime)] pub type Prime, I: 'static = ()> = StorageValue<_, T::AccountId, OptionQuery>; #[pallet::event] @@ -460,7 +453,7 @@ pub mod pallet { #[pallet::compact] length_bound: u32, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let members = Self::members(); + let members = Members::::get(); ensure!(members.contains(&who), Error::::NotMember); let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); @@ -520,7 +513,7 @@ pub mod pallet { #[pallet::compact] length_bound: u32, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let members = Self::members(); + let members = Members::::get(); ensure!(members.contains(&who), Error::::NotMember); if threshold < 2 { @@ -566,7 +559,7 @@ pub mod pallet { approve: bool, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let members = Self::members(); + let members = Members::::get(); ensure!(members.contains(&who), Error::::NotMember); // Detects first vote of the member in the motion @@ -670,7 +663,7 @@ impl, I: 'static> Pallet { pub fn is_member(who: &T::AccountId) -> bool { // Note: The dispatchables *do not* use this to check membership so make sure // to update those if this is changed. - Self::members().contains(who) + Members::::get().contains(who) } /// Execute immediately when adding a new proposal. @@ -689,7 +682,7 @@ impl, I: 'static> Pallet { let proposal_hash = T::Hashing::hash_of(&proposal); ensure!(!>::contains_key(proposal_hash), Error::::DuplicateProposal); - let seats = Self::members().len() as MemberCount; + let seats = Members::::get().len() as MemberCount; let result = proposal.dispatch(RawOrigin::Members(1, seats).into()); Self::deposit_event(Event::Executed { proposal_hash, @@ -722,7 +715,7 @@ impl, I: 'static> Pallet { Ok(proposals.len()) })?; - let index = Self::proposal_count(); + let index = ProposalCount::::get(); >::mutate(|i| *i += 1); >::insert(proposal_hash, proposal); let votes = { @@ -748,7 +741,7 @@ impl, I: 'static> Pallet { index: ProposalIndex, approve: bool, ) -> Result { - let mut voting = Self::voting(&proposal).ok_or(Error::::ProposalMissing)?; + let mut voting = Voting::::get(&proposal).ok_or(Error::::ProposalMissing)?; ensure!(voting.index == index, Error::::WrongIndex); let position_yes = voting.ayes.iter().position(|a| a == &who); @@ -799,12 +792,12 @@ impl, I: 'static> Pallet { proposal_weight_bound: Weight, length_bound: u32, ) -> DispatchResultWithPostInfo { - let voting = Self::voting(&proposal_hash).ok_or(Error::::ProposalMissing)?; + let voting = Voting::::get(&proposal_hash).ok_or(Error::::ProposalMissing)?; ensure!(voting.index == index, Error::::WrongIndex); let mut no_votes = voting.nays.len() as MemberCount; let mut yes_votes = voting.ayes.len() as MemberCount; - let seats = Self::members().len() as MemberCount; + let seats = Members::::get().len() as MemberCount; let approved = yes_votes >= voting.threshold; let disapproved = seats.saturating_sub(no_votes) < voting.threshold; // Allow (dis-)approving the proposal as soon as there are enough votes. @@ -838,7 +831,7 @@ impl, I: 'static> Pallet { // Only allow actual closing of the proposal after the voting period has ended. ensure!(frame_system::Pallet::::block_number() >= voting.end, Error::::TooEarly); - let prime_vote = Self::prime().map(|who| voting.ayes.iter().any(|a| a == &who)); + let prime_vote = Prime::::get().map(|who| voting.ayes.iter().any(|a| a == &who)); // default voting strategy. let default = T::DefaultVote::default_vote(prime_vote, yes_votes, no_votes, seats); @@ -979,29 +972,28 @@ impl, I: 'static> Pallet { /// * The prime account must be a member of the collective. #[cfg(any(feature = "try-runtime", test))] fn do_try_state() -> Result<(), TryRuntimeError> { - Self::proposals() - .into_iter() - .try_for_each(|proposal| -> Result<(), TryRuntimeError> { + Proposals::::get().into_iter().try_for_each( + |proposal| -> Result<(), TryRuntimeError> { ensure!( - Self::proposal_of(proposal).is_some(), + ProposalOf::::get(proposal).is_some(), "Proposal hash from `Proposals` is not found inside the `ProposalOf` mapping." ); Ok(()) - })?; + }, + )?; ensure!( - Self::proposals().into_iter().count() <= Self::proposal_count() as usize, + Proposals::::get().into_iter().count() <= ProposalCount::::get() as usize, "The actual number of proposals is greater than `ProposalCount`" ); ensure!( - Self::proposals().into_iter().count() == >::iter_keys().count(), + Proposals::::get().into_iter().count() == >::iter_keys().count(), "Proposal count inside `Proposals` is not equal to the proposal count in `ProposalOf`" ); - Self::proposals() - .into_iter() - .try_for_each(|proposal| -> Result<(), TryRuntimeError> { - if let Some(votes) = Self::voting(proposal) { + Proposals::::get().into_iter().try_for_each( + |proposal| -> Result<(), TryRuntimeError> { + if let Some(votes) = Voting::::get(proposal) { let ayes = votes.ayes.len(); let nays = votes.nays.len(); @@ -1011,13 +1003,13 @@ impl, I: 'static> Pallet { ); } Ok(()) - })?; + }, + )?; let mut proposal_indices = vec![]; - Self::proposals() - .into_iter() - .try_for_each(|proposal| -> Result<(), TryRuntimeError> { - if let Some(votes) = Self::voting(proposal) { + Proposals::::get().into_iter().try_for_each( + |proposal| -> Result<(), TryRuntimeError> { + if let Some(votes) = Voting::::get(proposal) { let proposal_index = votes.index; ensure!( !proposal_indices.contains(&proposal_index), @@ -1026,12 +1018,13 @@ impl, I: 'static> Pallet { proposal_indices.push(proposal_index); } Ok(()) - })?; + }, + )?; >::iter_keys().try_for_each( |proposal_hash| -> Result<(), TryRuntimeError> { ensure!( - Self::proposals().contains(&proposal_hash), + Proposals::::get().contains(&proposal_hash), "`Proposals` doesn't contain the proposal hash from the `Voting` storage map." ); Ok(()) @@ -1039,17 +1032,17 @@ impl, I: 'static> Pallet { )?; ensure!( - Self::members().len() <= T::MaxMembers::get() as usize, + Members::::get().len() <= T::MaxMembers::get() as usize, "The member count is greater than `MaxMembers`." ); ensure!( - Self::members().windows(2).all(|members| members[0] <= members[1]), + Members::::get().windows(2).all(|members| members[0] <= members[1]), "The members are not sorted by value." ); - if let Some(prime) = Self::prime() { - ensure!(Self::members().contains(&prime), "Prime account is not a member."); + if let Some(prime) = Prime::::get() { + ensure!(Members::::get().contains(&prime), "Prime account is not a member."); } Ok(()) @@ -1083,7 +1076,7 @@ impl, I: 'static> ChangeMembers for Pallet { // remove accounts from all current voting in motions. let mut outgoing = outgoing.to_vec(); outgoing.sort(); - for h in Self::proposals().into_iter() { + for h in Proposals::::get().into_iter() { >::mutate(h, |v| { if let Some(mut votes) = v.take() { votes.ayes = votes diff --git a/substrate/frame/collective/src/tests.rs b/substrate/frame/collective/src/tests.rs index dbc3fbe69b3bf27b2151963979fc1d571d9bcc7c..92418794c5fb70fb44d0b60572ef18e3a617cfd9 100644 --- a/substrate/frame/collective/src/tests.rs +++ b/substrate/frame/collective/src/tests.rs @@ -26,14 +26,10 @@ use frame_support::{ }; use frame_system::{EnsureRoot, EventRecord, Phase}; use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::{testing::Header, traits::BlakeTwo256, BuildStorage}; pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( pub enum Test @@ -93,29 +89,7 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl Config for Test { type RuntimeOrigin = RuntimeOrigin; @@ -219,8 +193,8 @@ fn default_max_proposal_weight() -> Weight { #[test] fn motions_basic_environment_works() { ExtBuilder::default().build_and_execute(|| { - assert_eq!(Collective::members(), vec![1, 2, 3]); - assert_eq!(*Collective::proposals(), Vec::::new()); + assert_eq!(Members::::get(), vec![1, 2, 3]); + assert_eq!(*Proposals::::get(), Vec::::new()); }); } @@ -231,7 +205,7 @@ fn initialize_members_sorts_members() { ExtBuilder::default() .set_collective_members(unsorted_members) .build_and_execute(|| { - assert_eq!(Collective::members(), expected_members); + assert_eq!(Members::::get(), expected_members); }); } @@ -245,8 +219,8 @@ fn set_members_with_prime_works() { Some(3), MaxMembers::get() )); - assert_eq!(Collective::members(), members.clone()); - assert_eq!(Collective::prime(), Some(3)); + assert_eq!(Members::::get(), members.clone()); + assert_eq!(Prime::::get(), Some(3)); assert_noop!( Collective::set_members(RuntimeOrigin::root(), members, Some(4), MaxMembers::get()), Error::::PrimeAccountNotMember @@ -658,12 +632,12 @@ fn removal_of_old_voters_votes_works() { assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 0, true)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![], end }) ); Collective::change_members_sorted(&[4], &[1], &[2, 3, 4]); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![], end }) ); @@ -679,12 +653,12 @@ fn removal_of_old_voters_votes_works() { assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 1, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(3), hash, 1, false)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3], end }) ); Collective::change_members_sorted(&[], &[3], &[2, 4]); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![], end }) ); }); @@ -706,7 +680,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 0, true)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![], end }) ); assert_ok!(Collective::set_members( @@ -716,7 +690,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { MaxMembers::get() )); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![], end }) ); @@ -732,7 +706,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { assert_ok!(Collective::vote(RuntimeOrigin::signed(2), hash, 1, true)); assert_ok!(Collective::vote(RuntimeOrigin::signed(3), hash, 1, false)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3], end }) ); assert_ok!(Collective::set_members( @@ -742,7 +716,7 @@ fn removal_of_old_voters_votes_works_with_set_members() { MaxMembers::get() )); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![], end }) ); }); @@ -761,10 +735,10 @@ fn propose_works() { Box::new(proposal.clone()), proposal_len )); - assert_eq!(*Collective::proposals(), vec![hash]); - assert_eq!(Collective::proposal_of(&hash), Some(proposal)); + assert_eq!(*Proposals::::get(), vec![hash]); + assert_eq!(ProposalOf::::get(&hash), Some(proposal)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![], nays: vec![], end }) ); @@ -924,13 +898,13 @@ fn motions_vote_after_works() { )); // Initially there a no votes when the motion is proposed. assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![], end }) ); // Cast first aye vote. assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![1], nays: vec![], end }) ); // Try to cast a duplicate aye vote. @@ -941,7 +915,7 @@ fn motions_vote_after_works() { // Cast a nay vote. assert_ok!(Collective::vote(RuntimeOrigin::signed(1), hash, 0, false)); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![1], end }) ); // Try to cast a duplicate nay vote. @@ -992,7 +966,7 @@ fn motions_all_first_vote_free_works() { proposal_len, )); assert_eq!( - Collective::voting(&hash), + Voting::::get(&hash), Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![], end }) ); @@ -1057,14 +1031,14 @@ fn motions_reproposing_disapproved_works() { proposal_weight, proposal_len )); - assert_eq!(*Collective::proposals(), vec![]); + assert_eq!(*Proposals::::get(), vec![]); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), 2, Box::new(proposal.clone()), proposal_len )); - assert_eq!(*Collective::proposals(), vec![hash]); + assert_eq!(*Proposals::::get(), vec![hash]); }); } diff --git a/substrate/frame/collective/src/weights.rs b/substrate/frame/collective/src/weights.rs index eece6a006b8f2bda108645e3d8b165a37ad04335..85744b4de9daed455a18b42a16d05933b78d4939 100644 --- a/substrate/frame/collective/src/weights.rs +++ b/substrate/frame/collective/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_collective +//! Autogenerated weights for `pallet_collective` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/collective/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/collective/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_collective. +/// Weight functions needed for `pallet_collective`. pub trait WeightInfo { fn set_members(m: u32, n: u32, p: u32, ) -> Weight; fn execute(b: u32, m: u32, ) -> Weight; @@ -64,30 +63,30 @@ pub trait WeightInfo { fn disapprove_proposal(p: u32, ) -> Weight; } -/// Weights for pallet_collective using the Substrate node and recommended hardware. +/// Weights for `pallet_collective` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Council Members (r:1 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:100 w:100) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Prime (r:0 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:100 w:100) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:0 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[0, 100]`. /// The range of component `n` is `[0, 100]`. /// The range of component `p` is `[0, 100]`. fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` - // Estimated: `15861 + m * (1967 ±24) + p * (4332 ±24)` - // Minimum execution time: 17_506_000 picoseconds. - Weight::from_parts(17_767_000, 15861) - // Standard Error: 60_220 - .saturating_add(Weight::from_parts(4_374_805, 0).saturating_mul(m.into())) - // Standard Error: 60_220 - .saturating_add(Weight::from_parts(8_398_316, 0).saturating_mul(p.into())) + // Estimated: `15894 + m * (1967 ±23) + p * (4332 ±23)` + // Minimum execution time: 15_559_000 picoseconds. + Weight::from_parts(15_870_000, 15894) + // Standard Error: 54_310 + .saturating_add(Weight::from_parts(4_117_753, 0).saturating_mul(m.into())) + // Standard Error: 54_310 + .saturating_add(Weight::from_parts(7_677_627, 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)) @@ -95,245 +94,261 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(0, 1967).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 4332).saturating_mul(p.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `202 + m * (32 ±0)` - // Estimated: `1688 + m * (32 ±0)` - // Minimum execution time: 16_203_000 picoseconds. - Weight::from_parts(15_348_267, 1688) - // Standard Error: 37 - .saturating_add(Weight::from_parts(1_766, 0).saturating_mul(b.into())) - // Standard Error: 382 - .saturating_add(Weight::from_parts(15_765, 0).saturating_mul(m.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `380 + m * (32 ±0)` + // Estimated: `3997 + m * (32 ±0)` + // Minimum execution time: 19_024_000 picoseconds. + Weight::from_parts(18_153_872, 3997) + // Standard Error: 46 + .saturating_add(Weight::from_parts(1_933, 0).saturating_mul(b.into())) + // Standard Error: 478 + .saturating_add(Weight::from_parts(18_872, 0).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:0) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:0) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `202 + m * (32 ±0)` - // Estimated: `3668 + m * (32 ±0)` - // Minimum execution time: 18_642_000 picoseconds. - Weight::from_parts(17_708_609, 3668) - // Standard Error: 58 - .saturating_add(Weight::from_parts(2_285, 0).saturating_mul(b.into())) - // Standard Error: 598 - .saturating_add(Weight::from_parts(30_454, 0).saturating_mul(m.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `380 + m * (32 ±0)` + // Estimated: `3997 + m * (32 ±0)` + // Minimum execution time: 20_895_000 picoseconds. + Weight::from_parts(20_081_761, 3997) + // Standard Error: 57 + .saturating_add(Weight::from_parts(1_982, 0).saturating_mul(b.into())) + // Standard Error: 594 + .saturating_add(Weight::from_parts(32_085, 0).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalCount (r:1 w:1) - /// Proof Skipped: Council ProposalCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:0 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// 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: `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`) /// 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: `492 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `3884 + m * (33 ±0) + p * (36 ±0)` - // Minimum execution time: 27_067_000 picoseconds. - Weight::from_parts(25_456_964, 3884) - // Standard Error: 112 - .saturating_add(Weight::from_parts(3_773, 0).saturating_mul(b.into())) - // Standard Error: 1_177 - .saturating_add(Weight::from_parts(32_783, 0).saturating_mul(m.into())) - // Standard Error: 1_162 - .saturating_add(Weight::from_parts(194_388, 0).saturating_mul(p.into())) + // Measured: `525 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `3917 + m * (33 ±0) + p * (36 ±0)` + // Minimum execution time: 22_068_000 picoseconds. + Weight::from_parts(19_639_088, 3917) + // Standard Error: 104 + .saturating_add(Weight::from_parts(3_896, 0).saturating_mul(b.into())) + // Standard Error: 1_095 + .saturating_add(Weight::from_parts(31_634, 0).saturating_mul(m.into())) + // Standard Error: 1_081 + .saturating_add(Weight::from_parts(178_702, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `941 + m * (64 ±0)` - // Estimated: `4405 + m * (64 ±0)` - // Minimum execution time: 26_055_000 picoseconds. - Weight::from_parts(27_251_907, 4405) - // Standard Error: 1_008 - .saturating_add(Weight::from_parts(65_947, 0).saturating_mul(m.into())) + // Measured: `974 + m * (64 ±0)` + // Estimated: `4438 + m * (64 ±0)` + // Minimum execution time: 22_395_000 picoseconds. + Weight::from_parts(23_025_217, 4438) + // Standard Error: 842 + .saturating_add(Weight::from_parts(58_102, 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())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:0 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `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: `Council::ProposalOf` (r:0 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `530 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `3975 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 28_363_000 picoseconds. - Weight::from_parts(28_733_464, 3975) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(43_236, 0).saturating_mul(m.into())) - // Standard Error: 1_244 - .saturating_add(Weight::from_parts(180_187, 0).saturating_mul(p.into())) + // Measured: `563 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4008 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 24_179_000 picoseconds. + Weight::from_parts(23_846_394, 4008) + // Standard Error: 1_052 + .saturating_add(Weight::from_parts(40_418, 0).saturating_mul(m.into())) + // Standard Error: 1_026 + .saturating_add(Weight::from_parts(171_653, 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())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// 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: `832 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4149 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 40_391_000 picoseconds. - Weight::from_parts(42_695_215, 4149) - // Standard Error: 167 - .saturating_add(Weight::from_parts(3_622, 0).saturating_mul(b.into())) - // Standard Error: 1_772 - .saturating_add(Weight::from_parts(33_830, 0).saturating_mul(m.into())) - // Standard Error: 1_727 - .saturating_add(Weight::from_parts(205_374, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // 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: 42_129_000 picoseconds. + Weight::from_parts(40_808_957, 4327) + // Standard Error: 134 + .saturating_add(Weight::from_parts(3_579, 0).saturating_mul(b.into())) + // Standard Error: 1_425 + .saturating_add(Weight::from_parts(37_166, 0).saturating_mul(m.into())) + // Standard Error: 1_389 + .saturating_add(Weight::from_parts(200_986, 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())) .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:0) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:0 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:0) + /// Proof: `Council::Prime` (`max_values`: Some(1), `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: `Council::ProposalOf` (r:0 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `550 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `3995 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 31_368_000 picoseconds. - Weight::from_parts(32_141_835, 3995) - // Standard Error: 1_451 - .saturating_add(Weight::from_parts(36_372, 0).saturating_mul(m.into())) - // Standard Error: 1_415 - .saturating_add(Weight::from_parts(210_635, 0).saturating_mul(p.into())) + // Measured: `583 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4028 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 26_385_000 picoseconds. + Weight::from_parts(25_713_839, 4028) + // Standard Error: 1_254 + .saturating_add(Weight::from_parts(36_206, 0).saturating_mul(m.into())) + // Standard Error: 1_223 + .saturating_add(Weight::from_parts(195_114, 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())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:0) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:0) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `852 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4169 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 43_271_000 picoseconds. - Weight::from_parts(45_495_648, 4169) - // Standard Error: 174 - .saturating_add(Weight::from_parts(3_034, 0).saturating_mul(b.into())) - // Standard Error: 1_840 - .saturating_add(Weight::from_parts(42_209, 0).saturating_mul(m.into())) - // Standard Error: 1_793 - .saturating_add(Weight::from_parts(207_525, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // 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: 42_903_000 picoseconds. + Weight::from_parts(43_152_907, 4347) + // Standard Error: 146 + .saturating_add(Weight::from_parts(3_459, 0).saturating_mul(b.into())) + // Standard Error: 1_548 + .saturating_add(Weight::from_parts(35_321, 0).saturating_mul(m.into())) + // Standard Error: 1_509 + .saturating_add(Weight::from_parts(202_541, 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())) .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:0 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:0 w:1) - /// Proof Skipped: 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: `Council::Voting` (r:0 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:0 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `359 + p * (32 ±0)` - // Estimated: `1844 + p * (32 ±0)` - // Minimum execution time: 15_170_000 picoseconds. - Weight::from_parts(17_567_243, 1844) - // Standard Error: 1_430 - .saturating_add(Weight::from_parts(169_040, 0).saturating_mul(p.into())) + // Measured: `392 + p * (32 ±0)` + // Estimated: `1877 + p * (32 ±0)` + // Minimum execution time: 12_656_000 picoseconds. + Weight::from_parts(14_032_951, 1877) + // Standard Error: 1_025 + .saturating_add(Weight::from_parts(159_143, 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())) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Council Members (r:1 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:100 w:100) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Prime (r:0 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:100 w:100) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:0 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[0, 100]`. /// The range of component `n` is `[0, 100]`. /// The range of component `p` is `[0, 100]`. fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ±0) + p * (3190 ±0)` - // Estimated: `15861 + m * (1967 ±24) + p * (4332 ±24)` - // Minimum execution time: 17_506_000 picoseconds. - Weight::from_parts(17_767_000, 15861) - // Standard Error: 60_220 - .saturating_add(Weight::from_parts(4_374_805, 0).saturating_mul(m.into())) - // Standard Error: 60_220 - .saturating_add(Weight::from_parts(8_398_316, 0).saturating_mul(p.into())) + // Estimated: `15894 + m * (1967 ±23) + p * (4332 ±23)` + // Minimum execution time: 15_559_000 picoseconds. + Weight::from_parts(15_870_000, 15894) + // Standard Error: 54_310 + .saturating_add(Weight::from_parts(4_117_753, 0).saturating_mul(m.into())) + // Standard Error: 54_310 + .saturating_add(Weight::from_parts(7_677_627, 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)) @@ -341,216 +356,232 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(0, 1967).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 4332).saturating_mul(p.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `202 + m * (32 ±0)` - // Estimated: `1688 + m * (32 ±0)` - // Minimum execution time: 16_203_000 picoseconds. - Weight::from_parts(15_348_267, 1688) - // Standard Error: 37 - .saturating_add(Weight::from_parts(1_766, 0).saturating_mul(b.into())) - // Standard Error: 382 - .saturating_add(Weight::from_parts(15_765, 0).saturating_mul(m.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `380 + m * (32 ±0)` + // Estimated: `3997 + m * (32 ±0)` + // Minimum execution time: 19_024_000 picoseconds. + Weight::from_parts(18_153_872, 3997) + // Standard Error: 46 + .saturating_add(Weight::from_parts(1_933, 0).saturating_mul(b.into())) + // Standard Error: 478 + .saturating_add(Weight::from_parts(18_872, 0).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:0) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:0) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `202 + m * (32 ±0)` - // Estimated: `3668 + m * (32 ±0)` - // Minimum execution time: 18_642_000 picoseconds. - Weight::from_parts(17_708_609, 3668) - // Standard Error: 58 - .saturating_add(Weight::from_parts(2_285, 0).saturating_mul(b.into())) - // Standard Error: 598 - .saturating_add(Weight::from_parts(30_454, 0).saturating_mul(m.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `380 + m * (32 ±0)` + // Estimated: `3997 + m * (32 ±0)` + // Minimum execution time: 20_895_000 picoseconds. + Weight::from_parts(20_081_761, 3997) + // Standard Error: 57 + .saturating_add(Weight::from_parts(1_982, 0).saturating_mul(b.into())) + // Standard Error: 594 + .saturating_add(Weight::from_parts(32_085, 0).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalCount (r:1 w:1) - /// Proof Skipped: Council ProposalCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:0 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// 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: `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`) /// 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: `492 + m * (32 ±0) + p * (36 ±0)` - // Estimated: `3884 + m * (33 ±0) + p * (36 ±0)` - // Minimum execution time: 27_067_000 picoseconds. - Weight::from_parts(25_456_964, 3884) - // Standard Error: 112 - .saturating_add(Weight::from_parts(3_773, 0).saturating_mul(b.into())) - // Standard Error: 1_177 - .saturating_add(Weight::from_parts(32_783, 0).saturating_mul(m.into())) - // Standard Error: 1_162 - .saturating_add(Weight::from_parts(194_388, 0).saturating_mul(p.into())) + // Measured: `525 + m * (32 ±0) + p * (36 ±0)` + // Estimated: `3917 + m * (33 ±0) + p * (36 ±0)` + // Minimum execution time: 22_068_000 picoseconds. + Weight::from_parts(19_639_088, 3917) + // Standard Error: 104 + .saturating_add(Weight::from_parts(3_896, 0).saturating_mul(b.into())) + // Standard Error: 1_095 + .saturating_add(Weight::from_parts(31_634, 0).saturating_mul(m.into())) + // Standard Error: 1_081 + .saturating_add(Weight::from_parts(178_702, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `941 + m * (64 ±0)` - // Estimated: `4405 + m * (64 ±0)` - // Minimum execution time: 26_055_000 picoseconds. - Weight::from_parts(27_251_907, 4405) - // Standard Error: 1_008 - .saturating_add(Weight::from_parts(65_947, 0).saturating_mul(m.into())) + // Measured: `974 + m * (64 ±0)` + // Estimated: `4438 + m * (64 ±0)` + // Minimum execution time: 22_395_000 picoseconds. + Weight::from_parts(23_025_217, 4438) + // Standard Error: 842 + .saturating_add(Weight::from_parts(58_102, 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())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:0 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `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: `Council::ProposalOf` (r:0 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `530 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `3975 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 28_363_000 picoseconds. - Weight::from_parts(28_733_464, 3975) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(43_236, 0).saturating_mul(m.into())) - // Standard Error: 1_244 - .saturating_add(Weight::from_parts(180_187, 0).saturating_mul(p.into())) + // Measured: `563 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4008 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 24_179_000 picoseconds. + Weight::from_parts(23_846_394, 4008) + // Standard Error: 1_052 + .saturating_add(Weight::from_parts(40_418, 0).saturating_mul(m.into())) + // Standard Error: 1_026 + .saturating_add(Weight::from_parts(171_653, 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())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// 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: `832 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4149 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 40_391_000 picoseconds. - Weight::from_parts(42_695_215, 4149) - // Standard Error: 167 - .saturating_add(Weight::from_parts(3_622, 0).saturating_mul(b.into())) - // Standard Error: 1_772 - .saturating_add(Weight::from_parts(33_830, 0).saturating_mul(m.into())) - // Standard Error: 1_727 - .saturating_add(Weight::from_parts(205_374, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // 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: 42_129_000 picoseconds. + Weight::from_parts(40_808_957, 4327) + // Standard Error: 134 + .saturating_add(Weight::from_parts(3_579, 0).saturating_mul(b.into())) + // Standard Error: 1_425 + .saturating_add(Weight::from_parts(37_166, 0).saturating_mul(m.into())) + // Standard Error: 1_389 + .saturating_add(Weight::from_parts(200_986, 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())) .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:0) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:0 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:0) + /// Proof: `Council::Prime` (`max_values`: Some(1), `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: `Council::ProposalOf` (r:0 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `550 + m * (64 ±0) + p * (36 ±0)` - // Estimated: `3995 + m * (65 ±0) + p * (36 ±0)` - // Minimum execution time: 31_368_000 picoseconds. - Weight::from_parts(32_141_835, 3995) - // Standard Error: 1_451 - .saturating_add(Weight::from_parts(36_372, 0).saturating_mul(m.into())) - // Standard Error: 1_415 - .saturating_add(Weight::from_parts(210_635, 0).saturating_mul(p.into())) + // Measured: `583 + m * (64 ±0) + p * (36 ±0)` + // Estimated: `4028 + m * (65 ±0) + p * (36 ±0)` + // Minimum execution time: 26_385_000 picoseconds. + Weight::from_parts(25_713_839, 4028) + // Standard Error: 1_254 + .saturating_add(Weight::from_parts(36_206, 0).saturating_mul(m.into())) + // Standard Error: 1_223 + .saturating_add(Weight::from_parts(195_114, 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())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } - /// Storage: Council Voting (r:1 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Members (r:1 w:0) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:0) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:1 w:1) - /// Proof Skipped: Council ProposalOf (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Council::Voting` (r:1 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:1 w:0) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:0) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[4, 100]`. /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `852 + b * (1 ±0) + m * (64 ±0) + p * (40 ±0)` - // Estimated: `4169 + b * (1 ±0) + m * (66 ±0) + p * (40 ±0)` - // Minimum execution time: 43_271_000 picoseconds. - Weight::from_parts(45_495_648, 4169) - // Standard Error: 174 - .saturating_add(Weight::from_parts(3_034, 0).saturating_mul(b.into())) - // Standard Error: 1_840 - .saturating_add(Weight::from_parts(42_209, 0).saturating_mul(m.into())) - // Standard Error: 1_793 - .saturating_add(Weight::from_parts(207_525, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // 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: 42_903_000 picoseconds. + Weight::from_parts(43_152_907, 4347) + // Standard Error: 146 + .saturating_add(Weight::from_parts(3_459, 0).saturating_mul(b.into())) + // Standard Error: 1_548 + .saturating_add(Weight::from_parts(35_321, 0).saturating_mul(m.into())) + // Standard Error: 1_509 + .saturating_add(Weight::from_parts(202_541, 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())) .saturating_add(Weight::from_parts(0, 66).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 40).saturating_mul(p.into())) } - /// Storage: Council Proposals (r:1 w:1) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Voting (r:0 w:1) - /// Proof Skipped: Council Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council ProposalOf (r:0 w:1) - /// Proof Skipped: 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: `Council::Voting` (r:0 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::ProposalOf` (r:0 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `359 + p * (32 ±0)` - // Estimated: `1844 + p * (32 ±0)` - // Minimum execution time: 15_170_000 picoseconds. - Weight::from_parts(17_567_243, 1844) - // Standard Error: 1_430 - .saturating_add(Weight::from_parts(169_040, 0).saturating_mul(p.into())) + // Measured: `392 + p * (32 ±0)` + // Estimated: `1877 + p * (32 ±0)` + // Minimum execution time: 12_656_000 picoseconds. + Weight::from_parts(14_032_951, 1877) + // Standard Error: 1_025 + .saturating_add(Weight::from_parts(159_143, 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())) diff --git a/substrate/frame/contracts/CHANGELOG.md b/substrate/frame/contracts/CHANGELOG.md deleted file mode 100644 index aca94e5b149152d5c598102c633048baa17d1276..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/CHANGELOG.md +++ /dev/null @@ -1,116 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -The semantic versioning guarantees cover the interface to the Substrate runtime which -includes this pallet as a dependency. This module will also add storage migrations whenever -changes require it. Stability with regard to offchain tooling is explicitly excluded from -this guarantee: For example adding a new field to an in-storage data structure will require -changes to frontends to properly display it. However, those changes will still be regarded -as a minor version bump. - -The interface provided to smart contracts will adhere to semver with one exception: Even -major version bumps will be backwards compatible with regard to already deployed contracts. -In other words: Upgrading this pallet will not break pre-existing contracts. - -## [Unreleased] - -### Added - -- Forbid calling back to contracts after switching to runtime -[#13443](https://github.com/paritytech/substrate/pull/13443) - -- Allow contracts to dispatch calls into the runtime (**unstable**) -[#9276](https://github.com/paritytech/substrate/pull/9276) - -- New version of `seal_call` that offers more features. -[#8909](https://github.com/paritytech/substrate/pull/8909) - -- New `instantiate` RPC that allows clients to dry-run contract instantiation. -[#8451](https://github.com/paritytech/substrate/pull/8451) - -- New version of `seal_random` which exposes additional information. -[#8329](https://github.com/paritytech/substrate/pull/8329) - -### Changed - -- Replaced storage rent with automatic storage deposits -[#9669](https://github.com/paritytech/substrate/pull/9669) -[#10082](https://github.com/paritytech/substrate/pull/10082) - -- Replaced `seal_println` with the `seal_debug_message` API which allows outputting debug -messages to the console and RPC clients. -[#8773](https://github.com/paritytech/substrate/pull/8773) -[#9550](https://github.com/paritytech/substrate/pull/9550) - -- Make storage and fields of `Schedule` private to the crate. -[#8359](https://github.com/paritytech/substrate/pull/8359) - -### Fixed - -- Remove pre-charging which caused wrongly estimated weights -[#8976](https://github.com/paritytech/substrate/pull/8976) - -## [v3.0.0] 2021-02-25 - -This version constitutes the first release that brings any stability guarantees (see above). - -### Added - -- Emit an event when a contract terminates (self-destructs). -[#8014](https://github.com/paritytech/substrate/pull/8014) - -- Charge rent for code stored on the chain in addition to the already existing -rent that is paid for data storage. -[#7935](https://github.com/paritytech/substrate/pull/7935) - -- Allow the runtime to configure per storage item costs in addition -to the already existing per byte costs. -[#7819](https://github.com/paritytech/substrate/pull/7819) - -- Contracts are now deleted lazily so that the user who removes a contract -does not need to pay for the deletion of the contract storage. -[#7740](https://github.com/paritytech/substrate/pull/7740) - -- Allow runtime authors to define chain extensions in order to provide custom -functionality to contracts. -[#7548](https://github.com/paritytech/substrate/pull/7548) -[#8003](https://github.com/paritytech/substrate/pull/8003) - -- Proper weights which are fully automated by benchmarking. -[#6715](https://github.com/paritytech/substrate/pull/6715) -[#7017](https://github.com/paritytech/substrate/pull/7017) -[#7361](https://github.com/paritytech/substrate/pull/7361) - -### Changed - -- Collect the rent for one block during instantiation. -[#7847](https://github.com/paritytech/substrate/pull/7847) - -- Instantiation takes a `salt` argument to allow for easier instantion of the -same code by the same sender. -[#7482](https://github.com/paritytech/substrate/pull/7482) - -- Improve the information returned by the `contracts_call` RPC. -[#7468](https://github.com/paritytech/substrate/pull/7468) - -- Simplify the node configuration necessary to add this module. -[#7409](https://github.com/paritytech/substrate/pull/7409) - -### Fixed - -- Consider the code size of a contract in the weight that is charged for -loading a contract from storage. -[#8086](https://github.com/paritytech/substrate/pull/8086) - -- Fix possible overflow in storage size calculation -[#7885](https://github.com/paritytech/substrate/pull/7885) - -- Cap the surcharge reward that can be claimed. -[#7870](https://github.com/paritytech/substrate/pull/7870) - -- Fix a possible DoS vector where contracts could allocate too large buffers. -[#7818](https://github.com/paritytech/substrate/pull/7818) diff --git a/substrate/frame/contracts/Cargo.toml b/substrate/frame/contracts/Cargo.toml index ece2af8895dc7bc4ee06ef5dbff7c7a6e95e796a..d16646890fe3dd015aae4e5eec1d41142422fd76 100644 --- a/substrate/frame/contracts/Cargo.toml +++ b/substrate/frame/contracts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-contracts" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true build = "build.rs" @@ -24,8 +24,8 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "max-encoded-len", ] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -log = { version = "0.4", default-features = false } -serde = { version = "1", optional = true, features = ["derive"] } +log = { workspace = true } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } smallvec = { version = "1", default-features = false, features = [ "const_generics", ] } @@ -86,7 +86,6 @@ std = [ "frame-system/std", "log/std", "pallet-balances?/std", - "pallet-contracts-proc-macro/full", "pallet-insecure-randomness-collective-flip/std", "pallet-proxy/std", "pallet-timestamp/std", diff --git a/substrate/frame/contracts/README.md b/substrate/frame/contracts/README.md index 0b6548cf6414a2bfbe253c0b18a95814b2dfb63b..6e817292e66c50d5766d5406cca6c053a25c1362 100644 --- a/substrate/frame/contracts/README.md +++ b/substrate/frame/contracts/README.md @@ -34,6 +34,19 @@ calls are reverted. Assuming correct error handling by contract A, A's other cal One `ref_time` `Weight` is defined as one picosecond of execution time on the runtime's reference machine. +#### Schedule + +The `Schedule` is where, among other things, the cost of every action a contract can do is defined. These costs are derived +from the benchmarks of this pallet. Instead of looking at the raw benchmark results it is advised to look at the `Schedule` +if one wants to manually inspect the performance characteristics. The `Schedule` can be printed like this: + +```sh +RUST_LOG=runtime::contracts=info cargo run --features runtime-benchmarks --bin substrate-node -- benchmark pallet --extra -p pallet_contracts -e print_schedule +``` + +Please note that the `Schedule` will be printed multiple times. This is because we are (ab)using a benchmark to print +the struct. + ### Revert Behaviour Contract call failures are not cascading. When failures occur in a sub-call, they do not "bubble up", and the call will diff --git a/substrate/frame/contracts/benchmarks/README.md b/substrate/frame/contracts/benchmarks/README.md deleted file mode 100644 index e4441d6bab2c425a37639830f12ca303db30d1fa..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/benchmarks/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Benchmarks - -This directory contains real world ([ink!](https://use.ink), [solang](https://github.com/hyperledger/solang)) contracts -which are used in macro benchmarks. Those benchmarks are not used to determine weights but rather to compare different -contract languages and execution engines with larger wasm modules. - -Files in this directory are used by `#[extra]` benchmarks in `src/benchmarking`. The json files are for informational -purposes only and are not consumed by the benchmarks. diff --git a/substrate/frame/contracts/benchmarks/ink_erc20.json b/substrate/frame/contracts/benchmarks/ink_erc20.json deleted file mode 100644 index 390dd9b06cd4cd3ee57f83f64c9089508c68464d..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/benchmarks/ink_erc20.json +++ /dev/null @@ -1,819 +0,0 @@ -{ - "metadataVersion": "0.1.0", - "source": { - "hash": "0x6be8492017fe96b7a92bb39b4ede04b96effb8fcaf9237bfdccef7d9e732c760", - "language": "ink! 3.0.0-rc4", - "compiler": "rustc 1.56.0-nightly" - }, - "contract": { - "name": "erc20", - "version": "3.0.0-rc4", - "authors": [ - "Parity Technologies " - ] - }, - "spec": { - "constructors": [ - { - "args": [ - { - "name": "initial_supply", - "type": { - "displayName": [ - "Balance" - ], - "type": 1 - } - } - ], - "docs": [ - "Creates a new ERC-20 contract with the specified initial supply." - ], - "name": [ - "new" - ], - "selector": "0x9bae9d5e" - } - ], - "docs": [], - "events": [ - { - "args": [ - { - "docs": [], - "indexed": true, - "name": "from", - "type": { - "displayName": [ - "Option" - ], - "type": 15 - } - }, - { - "docs": [], - "indexed": true, - "name": "to", - "type": { - "displayName": [ - "Option" - ], - "type": 15 - } - }, - { - "docs": [], - "indexed": false, - "name": "value", - "type": { - "displayName": [ - "Balance" - ], - "type": 1 - } - } - ], - "docs": [ - " Event emitted when a token transfer occurs." - ], - "name": "Transfer" - }, - { - "args": [ - { - "docs": [], - "indexed": true, - "name": "owner", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - }, - { - "docs": [], - "indexed": true, - "name": "spender", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - }, - { - "docs": [], - "indexed": false, - "name": "value", - "type": { - "displayName": [ - "Balance" - ], - "type": 1 - } - } - ], - "docs": [ - " Event emitted when an approval occurs that `spender` is allowed to withdraw", - " up to the amount of `value` tokens from `owner`." - ], - "name": "Approval" - } - ], - "messages": [ - { - "args": [], - "docs": [ - " Returns the total token supply." - ], - "mutates": false, - "name": [ - "total_supply" - ], - "payable": false, - "returnType": { - "displayName": [ - "Balance" - ], - "type": 1 - }, - "selector": "0xdb6375a8" - }, - { - "args": [ - { - "name": "owner", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - } - ], - "docs": [ - " Returns the account balance for the specified `owner`.", - "", - " Returns `0` if the account is non-existent." - ], - "mutates": false, - "name": [ - "balance_of" - ], - "payable": false, - "returnType": { - "displayName": [ - "Balance" - ], - "type": 1 - }, - "selector": "0x0f755a56" - }, - { - "args": [ - { - "name": "owner", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "spender", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - } - ], - "docs": [ - " Returns the amount which `spender` is still allowed to withdraw from `owner`.", - "", - " Returns `0` if no allowance has been set `0`." - ], - "mutates": false, - "name": [ - "allowance" - ], - "payable": false, - "returnType": { - "displayName": [ - "Balance" - ], - "type": 1 - }, - "selector": "0x6a00165e" - }, - { - "args": [ - { - "name": "to", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "value", - "type": { - "displayName": [ - "Balance" - ], - "type": 1 - } - } - ], - "docs": [ - " Transfers `value` amount of tokens from the caller's account to account `to`.", - "", - " On success a `Transfer` event is emitted.", - "", - " # Errors", - "", - " Returns `InsufficientBalance` error if there are not enough tokens on", - " the caller's account balance." - ], - "mutates": true, - "name": [ - "transfer" - ], - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 12 - }, - "selector": "0x84a15da1" - }, - { - "args": [ - { - "name": "spender", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "value", - "type": { - "displayName": [ - "Balance" - ], - "type": 1 - } - } - ], - "docs": [ - " Allows `spender` to withdraw from the caller's account multiple times, up to", - " the `value` amount.", - "", - " If this function is called again it overwrites the current allowance with `value`.", - "", - " An `Approval` event is emitted." - ], - "mutates": true, - "name": [ - "approve" - ], - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 12 - }, - "selector": "0x681266a0" - }, - { - "args": [ - { - "name": "from", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "to", - "type": { - "displayName": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "value", - "type": { - "displayName": [ - "Balance" - ], - "type": 1 - } - } - ], - "docs": [ - " Transfers `value` tokens on the behalf of `from` to the account `to`.", - "", - " This can be used to allow a contract to transfer tokens on ones behalf and/or", - " to charge fees in sub-currencies, for example.", - "", - " On success a `Transfer` event is emitted.", - "", - " # Errors", - "", - " Returns `InsufficientAllowance` error if there are not enough tokens allowed", - " for the caller to withdraw from `from`.", - "", - " Returns `InsufficientBalance` error if there are not enough tokens on", - " the account balance of `from`." - ], - "mutates": true, - "name": [ - "transfer_from" - ], - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 12 - }, - "selector": "0x0b396f18" - } - ] - }, - "storage": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000000", - "ty": 1 - } - }, - "name": "total_supply" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0100000000000000000000000000000000000000000000000000000000000000", - "ty": 2 - } - }, - "name": "header" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0200000000000000000000000000000000000000000000000000000000000000", - "ty": 3 - } - }, - "name": "len" - }, - { - "layout": { - "array": { - "cellsPerElem": 1, - "layout": { - "cell": { - "key": "0x0200000001000000000000000000000000000000000000000000000000000000", - "ty": 4 - } - }, - "len": 4294967295, - "offset": "0x0300000000000000000000000000000000000000000000000000000000000000" - } - }, - "name": "elems" - } - ] - } - }, - "name": "entries" - } - ] - } - }, - "name": "keys" - }, - { - "layout": { - "hash": { - "layout": { - "cell": { - "key": "0x0300000001000000000000000000000000000000000000000000000000000000", - "ty": 9 - } - }, - "offset": "0x0200000001000000000000000000000000000000000000000000000000000000", - "strategy": { - "hasher": "Blake2x256", - "postfix": "", - "prefix": "0x696e6b20686173686d6170" - } - } - }, - "name": "values" - } - ] - } - }, - "name": "balances" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0300000001000000000000000000000000000000000000000000000000000000", - "ty": 2 - } - }, - "name": "header" - }, - { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0400000001000000000000000000000000000000000000000000000000000000", - "ty": 3 - } - }, - "name": "len" - }, - { - "layout": { - "array": { - "cellsPerElem": 1, - "layout": { - "cell": { - "key": "0x0400000002000000000000000000000000000000000000000000000000000000", - "ty": 10 - } - }, - "len": 4294967295, - "offset": "0x0500000001000000000000000000000000000000000000000000000000000000" - } - }, - "name": "elems" - } - ] - } - }, - "name": "entries" - } - ] - } - }, - "name": "keys" - }, - { - "layout": { - "hash": { - "layout": { - "cell": { - "key": "0x0500000002000000000000000000000000000000000000000000000000000000", - "ty": 9 - } - }, - "offset": "0x0400000002000000000000000000000000000000000000000000000000000000", - "strategy": { - "hasher": "Blake2x256", - "postfix": "", - "prefix": "0x696e6b20686173686d6170" - } - } - }, - "name": "values" - } - ] - } - }, - "name": "allowances" - } - ] - } - }, - "types": [ - { - "def": { - "primitive": "u128" - } - }, - { - "def": { - "composite": { - "fields": [ - { - "name": "last_vacant", - "type": 3, - "typeName": "Index" - }, - { - "name": "len", - "type": 3, - "typeName": "u32" - }, - { - "name": "len_entries", - "type": 3, - "typeName": "u32" - } - ] - } - }, - "path": [ - "ink_storage", - "collections", - "stash", - "Header" - ] - }, - { - "def": { - "primitive": "u32" - } - }, - { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8, - "typeName": "VacantEntry" - } - ], - "name": "Vacant" - }, - { - "fields": [ - { - "type": 5, - "typeName": "T" - } - ], - "name": "Occupied" - } - ] - } - }, - "params": [ - 5 - ], - "path": [ - "ink_storage", - "collections", - "stash", - "Entry" - ] - }, - { - "def": { - "composite": { - "fields": [ - { - "type": 6, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_env", - "types", - "AccountId" - ] - }, - { - "def": { - "array": { - "len": 32, - "type": 7 - } - } - }, - { - "def": { - "primitive": "u8" - } - }, - { - "def": { - "composite": { - "fields": [ - { - "name": "next", - "type": 3, - "typeName": "Index" - }, - { - "name": "prev", - "type": 3, - "typeName": "Index" - } - ] - } - }, - "path": [ - "ink_storage", - "collections", - "stash", - "VacantEntry" - ] - }, - { - "def": { - "composite": { - "fields": [ - { - "name": "value", - "type": 1, - "typeName": "V" - }, - { - "name": "key_index", - "type": 3, - "typeName": "KeyIndex" - } - ] - } - }, - "params": [ - 1 - ], - "path": [ - "ink_storage", - "collections", - "hashmap", - "ValueEntry" - ] - }, - { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8, - "typeName": "VacantEntry" - } - ], - "name": "Vacant" - }, - { - "fields": [ - { - "type": 11, - "typeName": "T" - } - ], - "name": "Occupied" - } - ] - } - }, - "params": [ - 11 - ], - "path": [ - "ink_storage", - "collections", - "stash", - "Entry" - ] - }, - { - "def": { - "tuple": [ - 5, - 5 - ] - } - }, - { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 13, - "typeName": "T" - } - ], - "name": "Ok" - }, - { - "fields": [ - { - "type": 14, - "typeName": "E" - } - ], - "name": "Err" - } - ] - } - }, - "params": [ - 13, - 14 - ], - "path": [ - "Result" - ] - }, - { - "def": { - "tuple": [] - } - }, - { - "def": { - "variant": { - "variants": [ - { - "discriminant": 0, - "name": "InsufficientBalance" - }, - { - "discriminant": 1, - "name": "InsufficientAllowance" - } - ] - } - }, - "path": [ - "erc20", - "erc20", - "Error" - ] - }, - { - "def": { - "variant": { - "variants": [ - { - "name": "None" - }, - { - "fields": [ - { - "type": 5, - "typeName": "T" - } - ], - "name": "Some" - } - ] - } - }, - "params": [ - 5 - ], - "path": [ - "Option" - ] - } - ] -} \ No newline at end of file diff --git a/substrate/frame/contracts/benchmarks/ink_erc20.wasm b/substrate/frame/contracts/benchmarks/ink_erc20.wasm deleted file mode 100644 index ffd522760a02dd8416f8ee04db02c32e2172b8ff..0000000000000000000000000000000000000000 Binary files a/substrate/frame/contracts/benchmarks/ink_erc20.wasm and /dev/null differ diff --git a/substrate/frame/contracts/benchmarks/ink_erc20_test.wasm b/substrate/frame/contracts/benchmarks/ink_erc20_test.wasm deleted file mode 100644 index f5d84552960a348db3935e457adc4b6fba249d17..0000000000000000000000000000000000000000 Binary files a/substrate/frame/contracts/benchmarks/ink_erc20_test.wasm and /dev/null differ diff --git a/substrate/frame/contracts/benchmarks/solang_erc20.json b/substrate/frame/contracts/benchmarks/solang_erc20.json deleted file mode 100644 index 9d8fd5ce70e70edb0fdb96439be847369e6ae7e0..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/benchmarks/solang_erc20.json +++ /dev/null @@ -1,581 +0,0 @@ -{ - "contract": { - "authors": [ - "unknown" - ], - "name": "ERC20PresetFixedSupply", - "version": "0.0.1" - }, - "metadataVersion": "0.1.0", - "source": { - "compiler": "solang 0.1.7", - "hash": "0x9c55e342566e89c741eb641eec3af796836da750fc930c55bccc0604a47ef700", - "language": "Solidity 0.1.7" - }, - "spec": { - "constructors": [ - { - "args": [ - { - "name": "name", - "type": { - "display_name": [ - "String" - ], - "type": 2 - } - }, - { - "name": "symbol", - "type": { - "display_name": [ - "String" - ], - "type": 2 - } - }, - { - "name": "initialSupply", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - }, - { - "name": "owner", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - } - ], - "docs": [ - "" - ], - "name": "new", - "selector": "0xa6f1f5e1" - } - ], - "events": [ - { - "args": [ - { - "indexed": true, - "name": "owner", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "indexed": true, - "name": "spender", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "indexed": false, - "name": "value", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "name": "Approval" - }, - { - "args": [ - { - "indexed": true, - "name": "from", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "indexed": true, - "name": "to", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "indexed": false, - "name": "value", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "name": "Transfer" - } - ], - "messages": [ - { - "args": [ - { - "name": "account", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "amount", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "mutates": true, - "name": "burnFrom", - "payable": false, - "return_type": null, - "selector": "0x0f1354f3" - }, - { - "args": [ - { - "name": "account", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - } - ], - "docs": [ - "" - ], - "mutates": false, - "name": "balanceOf", - "payable": false, - "return_type": { - "display_name": [ - "u256" - ], - "type": 1 - }, - "selector": "0x6c7f1542" - }, - { - "args": [], - "docs": [ - "" - ], - "mutates": false, - "name": "totalSupply", - "payable": false, - "return_type": { - "display_name": [ - "u256" - ], - "type": 1 - }, - "selector": "0x18160ddd" - }, - { - "args": [], - "docs": [ - "" - ], - "mutates": false, - "name": "decimals", - "payable": false, - "return_type": { - "display_name": [ - "u8" - ], - "type": 3 - }, - "selector": "0x313ce567" - }, - { - "args": [ - { - "name": "owner", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "spender", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - } - ], - "docs": [ - "" - ], - "mutates": false, - "name": "allowance", - "payable": false, - "return_type": { - "display_name": [ - "u256" - ], - "type": 1 - }, - "selector": "0xf2a9a8c7" - }, - { - "args": [], - "docs": [ - "" - ], - "mutates": false, - "name": "name", - "payable": false, - "return_type": { - "display_name": [ - "String" - ], - "type": 2 - }, - "selector": "0x06fdde03" - }, - { - "args": [ - { - "name": "spender", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "subtractedValue", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "mutates": true, - "name": "decreaseAllowance", - "payable": false, - "return_type": { - "display_name": [ - "bool" - ], - "type": 6 - }, - "selector": "0x4b76697b" - }, - { - "args": [ - { - "name": "sender", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "recipient", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "amount", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "mutates": true, - "name": "transferFrom", - "payable": false, - "return_type": { - "display_name": [ - "bool" - ], - "type": 6 - }, - "selector": "0x2fb840f5" - }, - { - "args": [], - "docs": [ - "" - ], - "mutates": false, - "name": "symbol", - "payable": false, - "return_type": { - "display_name": [ - "String" - ], - "type": 2 - }, - "selector": "0x95d89b41" - }, - { - "args": [ - { - "name": "spender", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "addedValue", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "mutates": true, - "name": "increaseAllowance", - "payable": false, - "return_type": { - "display_name": [ - "bool" - ], - "type": 6 - }, - "selector": "0xb936c899" - }, - { - "args": [ - { - "name": "recipient", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "amount", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "mutates": true, - "name": "transfer", - "payable": false, - "return_type": { - "display_name": [ - "bool" - ], - "type": 6 - }, - "selector": "0x6a467394" - }, - { - "args": [ - { - "name": "spender", - "type": { - "display_name": [ - "AccountId" - ], - "type": 5 - } - }, - { - "name": "amount", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "mutates": true, - "name": "approve", - "payable": false, - "return_type": { - "display_name": [ - "bool" - ], - "type": 6 - }, - "selector": "0x47144421" - }, - { - "args": [ - { - "name": "amount", - "type": { - "display_name": [ - "u256" - ], - "type": 1 - } - } - ], - "docs": [ - "" - ], - "mutates": true, - "name": "burn", - "payable": false, - "return_type": null, - "selector": "0x42966c68" - } - ] - }, - "storage": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000002", - "ty": 1 - } - }, - "name": "_totalSupply" - }, - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000003", - "ty": 2 - } - }, - "name": "_name" - }, - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000004", - "ty": 2 - } - }, - "name": "_symbol" - } - ] - } - }, - "types": [ - { - "def": { - "primitive": "u256" - } - }, - { - "def": { - "primitive": "str" - } - }, - { - "def": { - "primitive": "u8" - } - }, - { - "def": { - "array": { - "len": 32, - "type": 3 - } - } - }, - { - "def": { - "composite": { - "fields": [ - { - "type": 4 - } - ] - } - }, - "path": [ - "AccountId" - ] - }, - { - "def": { - "primitive": "bool" - } - } - ] -} diff --git a/substrate/frame/contracts/benchmarks/solang_erc20.wasm b/substrate/frame/contracts/benchmarks/solang_erc20.wasm deleted file mode 100644 index 0796085d33249b78871f4a336623444a0ef94e85..0000000000000000000000000000000000000000 Binary files a/substrate/frame/contracts/benchmarks/solang_erc20.wasm and /dev/null differ diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 7fdf56a91fcc7c114b0cadd622e9d43e47088f33..9ca9e404c31000569b50ce641619121f68927e34 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -11,7 +11,6 @@ description = "Fixtures for testing contracts pallet." workspace = true [dependencies] -wat = "1" frame-system = { path = "../../system" } sp-runtime = { path = "../../../primitives/runtime" } anyhow = "1.0.0" diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 5f174d200b9f937b5d625c2cbfe21d575e7ca282..12a7805294b673480b6a350b9f242a1de9830e56 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -159,7 +159,7 @@ fn invoke_cargo_fmt<'a>( ) -> Result<()> { // If rustfmt is not installed, skip the check. if !Command::new("rustup") - .args(["nightly-2023-11-01", "run", "rustfmt", "--version"]) + .args(["nightly-2024-01-22", "run", "rustfmt", "--version"]) .output() .map_or(false, |o| o.status.success()) { @@ -167,7 +167,7 @@ fn invoke_cargo_fmt<'a>( } let fmt_res = Command::new("rustup") - .args(["nightly-2023-11-01", "run", "rustfmt", "--check", "--config-path"]) + .args(["nightly-2024-01-22", "run", "rustfmt", "--check", "--config-path"]) .arg(config_path) .args(files) .output() @@ -182,7 +182,7 @@ fn invoke_cargo_fmt<'a>( eprintln!("{}\n{}", stdout, stderr); eprintln!( "Fixtures files are not formatted.\n - Please run `rustup nightly-2023-11-01 run rustfmt --config-path {} {}/*.rs`", + Please run `rustup nightly-2024-01-22 run rustfmt --config-path {} {}/*.rs`", config_path.display(), contract_dir.display() ); @@ -309,7 +309,7 @@ fn find_workspace_root(current_dir: &Path) -> Option { let cargo_toml_contents = std::fs::read_to_string(current_dir.join("Cargo.toml")).ok()?; if cargo_toml_contents.contains("[workspace]") { - return Some(current_dir) + return Some(current_dir); } } diff --git a/substrate/frame/contracts/fixtures/build/Cargo.toml b/substrate/frame/contracts/fixtures/build/Cargo.toml index 1ff6b7ae7f83a396d0b3fc99643cefa0cc6ba51e..d524dbff1ce1b7af5d4dcf404f61154c5f2d1e6f 100644 --- a/substrate/frame/contracts/fixtures/build/Cargo.toml +++ b/substrate/frame/contracts/fixtures/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "contracts" -version = "0.1.0" +version = "0.6.3" edition = "2021" # Binary targets are injected dynamically by the build script. diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index f7d9d862d7465937f71afff998aecdaa9aa1f4bd..535745ffc0bc505c5cc29dc5959fe31a3e1fe509 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -35,11 +35,13 @@ pub extern "C" fn call() { ); // Call the callee - api::call_v1( + api::call_v2( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much gas to devote for the execution. 0 = all. - &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. + &0u64.to_le_bytes(), // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/contracts/fixtures/contracts/call_return_code.rs b/substrate/frame/contracts/fixtures/contracts/call_return_code.rs index 1256588d3b58554edf5b1d00e98572681087f2c7..3d5a1073eaecaedabcf774e336abea48ab0b9001 100644 --- a/substrate/frame/contracts/fixtures/contracts/call_return_code.rs +++ b/substrate/frame/contracts/fixtures/contracts/call_return_code.rs @@ -38,11 +38,13 @@ pub extern "C" fn call() { ); // Call the callee - let err_code = match api::call_v1( + let err_code = match api::call_v2( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much gas to devote for the execution. 0 = all. - &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. + &100u64.to_le_bytes(), // Value transferred to the contract. input, None, ) { diff --git a/substrate/frame/contracts/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/contracts/fixtures/contracts/call_runtime_and_call.rs index fdeb6097e732f9e7cc717f1144a51953c138e7f7..1321d36dc7efa17151cff67cfcd6162c91eecaa6 100644 --- a/substrate/frame/contracts/fixtures/contracts/call_runtime_and_call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call_runtime_and_call.rs @@ -39,11 +39,13 @@ pub extern "C" fn call() { api::call_runtime(call).unwrap(); // Call the callee - api::call_v1( + api::call_v2( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much gas to devote for the execution. 0 = all. - &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. + &0u64.to_le_bytes(), // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/contracts/fixtures/contracts/call_with_limit.rs b/substrate/frame/contracts/fixtures/contracts/call_with_limit.rs index 5e98aa614e95acad471a4146c492ca12cee90da8..f0851f32c75bc13d7b89c1bf366595ef1dbd57ca 100644 --- a/substrate/frame/contracts/fixtures/contracts/call_with_limit.rs +++ b/substrate/frame/contracts/fixtures/contracts/call_with_limit.rs @@ -31,12 +31,13 @@ pub extern "C" fn deploy() {} #[polkavm_derive::polkavm_export] pub extern "C" fn call() { input!( + 256, callee_addr: [u8; 32], ref_time: u64, proof_size: u64, + forwarded_input: [u8], ); - #[allow(deprecated)] api::call_v2( uapi::CallFlags::empty(), callee_addr, @@ -44,7 +45,7 @@ pub extern "C" fn call() { proof_size, None, // No deposit limit. &0u64.to_le_bytes(), // value transferred to the contract. - &[0u8; 0], // input data. + forwarded_input, None, ) .unwrap(); diff --git a/substrate/frame/contracts/fixtures/contracts/caller_contract.rs b/substrate/frame/contracts/fixtures/contracts/caller_contract.rs index c2629e9fa1971fea3da5a77f3c2d3bdfce3ce601..fffdb66a33d7979da3d81f53ec51e4308f5d355b 100644 --- a/substrate/frame/contracts/fixtures/contracts/caller_contract.rs +++ b/substrate/frame/contracts/fixtures/contracts/caller_contract.rs @@ -40,7 +40,6 @@ pub extern "C" fn call() { let reverted_input = [1u8, 34, 51, 68, 85, 102, 119]; // Fail to deploy the contract since it returns a non-zero exit status. - #[allow(deprecated)] let res = api::instantiate_v2( code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. @@ -55,7 +54,6 @@ pub extern "C" fn call() { assert!(matches!(res, Err(ReturnErrorCode::CalleeReverted))); // Fail to deploy the contract due to insufficient ref_time weight. - #[allow(deprecated)] let res = api::instantiate_v2( code_hash, 1u64, // too little ref_time weight 0u64, // How much proof_size weight to devote for the execution. 0 = all. @@ -65,7 +63,6 @@ pub extern "C" fn call() { assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); // Fail to deploy the contract due to insufficient proof_size weight. - #[allow(deprecated)] let res = api::instantiate_v2( code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 1u64, // Too little proof_size weight @@ -78,7 +75,6 @@ pub extern "C" fn call() { let mut callee = [0u8; 32]; let callee = &mut &mut callee[..]; - #[allow(deprecated)] api::instantiate_v2( code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. @@ -94,7 +90,6 @@ pub extern "C" fn call() { assert_eq!(callee.len(), 32); // Call the new contract and expect it to return failing exit code. - #[allow(deprecated)] let res = api::call_v2( uapi::CallFlags::empty(), callee, @@ -108,11 +103,10 @@ pub extern "C" fn call() { assert!(matches!(res, Err(ReturnErrorCode::CalleeReverted))); // Fail to call the contract due to insufficient ref_time weight. - #[allow(deprecated)] let res = api::call_v2( uapi::CallFlags::empty(), callee, - 1u64, // too little ref_time weight + 1u64, // Too little ref_time weight. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. &value, @@ -122,7 +116,6 @@ pub extern "C" fn call() { assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); // Fail to call the contract due to insufficient proof_size weight. - #[allow(deprecated)] let res = api::call_v2( uapi::CallFlags::empty(), callee, @@ -137,7 +130,6 @@ pub extern "C" fn call() { // Call the contract successfully. let mut output = [0u8; 4]; - #[allow(deprecated)] api::call_v2( uapi::CallFlags::empty(), callee, diff --git a/substrate/frame/contracts/fixtures/contracts/caller_is_origin_n.rs b/substrate/frame/contracts/fixtures/contracts/caller_is_origin_n.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd6f59802fa08408105070f37ae64569a32d38a7 --- /dev/null +++ b/substrate/frame/contracts/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/contracts/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/contracts/fixtures/contracts/chain_extension_temp_storage.rs index 1ab08efb3c7ea49ee1e016cb6136b428e86075a3..2e15fb02a12ca5f0cd4bee944af062961d66e8b2 100644 --- a/substrate/frame/contracts/fixtures/contracts/chain_extension_temp_storage.rs +++ b/substrate/frame/contracts/fixtures/contracts/chain_extension_temp_storage.rs @@ -50,11 +50,13 @@ pub extern "C" fn call() { output!(addr, [0u8; 32], api::address,); // call self - api::call_v1( + api::call_v2( uapi::CallFlags::ALLOW_REENTRY, addr, - 0u64, // How much gas to devote for the execution. 0 = all. - &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. + &0u64.to_le_bytes(), // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/contracts/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/contracts/fixtures/contracts/create_storage_and_call.rs index 8b79dd87dffd2f35c2c597f95f4e442a855f69ea..f8ce0ff4fb84831fbf258f489d0bc6935784ff43 100644 --- a/substrate/frame/contracts/fixtures/contracts/create_storage_and_call.rs +++ b/substrate/frame/contracts/fixtures/contracts/create_storage_and_call.rs @@ -40,14 +40,13 @@ pub extern "C" fn call() { api::set_storage(buffer, &[1u8; 4]); // Call the callee - #[allow(deprecated)] api::call_v2( 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. + &0u64.to_le_bytes(), // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/contracts/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/contracts/fixtures/contracts/create_storage_and_instantiate.rs index c68d99eff821e86ecfa399fc6804f1e3a7b2649c..fa3b9000a7ed6bd3f5f094d39b5d025a995a8b4c 100644 --- a/substrate/frame/contracts/fixtures/contracts/create_storage_and_instantiate.rs +++ b/substrate/frame/contracts/fixtures/contracts/create_storage_and_instantiate.rs @@ -40,7 +40,6 @@ pub extern "C" fn call() { let mut address = [0u8; 32]; let address = &mut &mut address[..]; - #[allow(deprecated)] api::instantiate_v2( code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. diff --git a/substrate/frame/contracts/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/contracts/fixtures/contracts/destroy_and_transfer.rs index cfcfd60f21eef1c543e249832f74bd75e5c4a8d3..62fb63b56020455ea775e802b95daeacaefd3658 100644 --- a/substrate/frame/contracts/fixtures/contracts/destroy_and_transfer.rs +++ b/substrate/frame/contracts/fixtures/contracts/destroy_and_transfer.rs @@ -34,7 +34,6 @@ pub extern "C" fn deploy() { let address = &mut &mut address[..]; let salt = [71u8, 17u8]; - #[allow(deprecated)] api::instantiate_v2( code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. @@ -60,7 +59,6 @@ pub extern "C" fn call() { api::get_storage(&ADDRESS_KEY, callee_addr).unwrap(); // Calling the destination contract with non-empty input data should fail. - #[allow(deprecated)] let res = api::call_v2( uapi::CallFlags::empty(), callee_addr, @@ -74,7 +72,6 @@ pub extern "C" fn call() { assert!(matches!(res, Err(uapi::ReturnErrorCode::CalleeTrapped))); // Call the destination contract regularly, forcing it to self-destruct. - #[allow(deprecated)] api::call_v2( uapi::CallFlags::empty(), callee_addr, diff --git a/substrate/frame/contracts/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/contracts/fixtures/contracts/instantiate_return_code.rs index 67cd739d85a6942a046cfaf7cc45301d6570600f..de194abe4840ac5fc67b83c1a377d114e65dfc01 100644 --- a/substrate/frame/contracts/fixtures/contracts/instantiate_return_code.rs +++ b/substrate/frame/contracts/fixtures/contracts/instantiate_return_code.rs @@ -31,7 +31,6 @@ pub extern "C" fn call() { input!(buffer, 36, code_hash: [u8; 32],); let input = &buffer[32..]; - #[allow(deprecated)] let err_code = match api::instantiate_v2( code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. diff --git a/substrate/frame/contracts/fixtures/contracts/add_remove_delegate_dependency.rs b/substrate/frame/contracts/fixtures/contracts/locking_delegate_dependency.rs similarity index 83% rename from substrate/frame/contracts/fixtures/contracts/add_remove_delegate_dependency.rs rename to substrate/frame/contracts/fixtures/contracts/locking_delegate_dependency.rs index 759ff7993740479f9e89b3c047eca27de74dbcc4..bb76c942aad18c511ab2e88bf1597101b307c2b4 100644 --- a/substrate/frame/contracts/fixtures/contracts/add_remove_delegate_dependency.rs +++ b/substrate/frame/contracts/fixtures/contracts/locking_delegate_dependency.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! This contract tests the behavior of adding / removing delegate_dependencies when delegate +//! This contract tests the behavior of locking / unlocking delegate_dependencies when delegate //! calling into a contract. #![no_std] #![no_main] @@ -34,15 +34,13 @@ fn load_input(delegate_call: bool) { ); match action { - // 1 = Add delegate dependency + // 1 = Lock delegate dependency 1 => { - #[allow(deprecated)] - api::add_delegate_dependency(code_hash); + api::lock_delegate_dependency(code_hash); }, - // 2 = Remove delegate dependency + // 2 = Unlock delegate dependency 2 => { - #[allow(deprecated)] - api::remove_delegate_dependency(code_hash); + api::unlock_delegate_dependency(code_hash); }, // 3 = Terminate 3 => { diff --git a/substrate/frame/contracts/fixtures/contracts/recurse.rs b/substrate/frame/contracts/fixtures/contracts/recurse.rs new file mode 100644 index 0000000000000000000000000000000000000000..b1ded608c2fc417323580257b74835a0aaee9aa3 --- /dev/null +++ b/substrate/frame/contracts/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_v2( + 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/contracts/fixtures/contracts/reentrance_count_call.rs b/substrate/frame/contracts/fixtures/contracts/reentrance_count_call.rs index 9812dce2e818d29a352927d2b47e301b06683dae..0acfe017f8df9094ef14aa9e6947b56e183d25be 100644 --- a/substrate/frame/contracts/fixtures/contracts/reentrance_count_call.rs +++ b/substrate/frame/contracts/fixtures/contracts/reentrance_count_call.rs @@ -42,11 +42,13 @@ pub extern "C" fn call() { if expected_reentrance_count != 5 { let count = (expected_reentrance_count + 1).to_le_bytes(); - api::call_v1( + api::call_v2( uapi::CallFlags::ALLOW_REENTRY, addr, - 0u64, // How much gas to devote for the execution. 0 = all. - &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. + &0u64.to_le_bytes(), // Value transferred to the contract. &count, None, ) diff --git a/substrate/frame/contracts/fixtures/contracts/self_destruct.rs b/substrate/frame/contracts/fixtures/contracts/self_destruct.rs index 94c3ac387a0a65228674efa93a477543b807661c..d3836d2d8c734214a67333dba9293067b4b5d9b1 100644 --- a/substrate/frame/contracts/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/contracts/fixtures/contracts/self_destruct.rs @@ -37,10 +37,12 @@ pub extern "C" fn call() { if !input.is_empty() { output!(addr, [0u8; 32], api::address,); - api::call_v1( + api::call_v2( uapi::CallFlags::ALLOW_REENTRY, addr, - 0u64, // How much gas to devote for the execution. 0 = all. + 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, diff --git a/substrate/frame/contracts/fixtures/contracts/xcm_execute.rs b/substrate/frame/contracts/fixtures/contracts/xcm_execute.rs index 09d0b6cf972815e093f066a506e3d06e5b447791..1d570ffead71845c77d664bf5b911bf66794e907 100644 --- a/substrate/frame/contracts/fixtures/contracts/xcm_execute.rs +++ b/substrate/frame/contracts/fixtures/contracts/xcm_execute.rs @@ -30,10 +30,11 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!(512, msg: [u8],); - let mut outcome = [0u8; 512]; - let outcome = &mut &mut outcome[..]; - #[allow(deprecated)] - api::xcm_execute(msg, outcome).unwrap(); - api::return_value(uapi::ReturnFlags::empty(), outcome); + 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/contracts/fixtures/data/invalid_contract_no_memory.wat b/substrate/frame/contracts/fixtures/data/invalid_contract_no_memory.wat deleted file mode 100644 index 0aeefbcb7ebfe27f55fb84de0f14a3c7ec12b05c..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/invalid_contract_no_memory.wat +++ /dev/null @@ -1,5 +0,0 @@ -;; A valid contract which does nothing at all -(module - (func (export "deploy")) - (func (export "call")) -) diff --git a/substrate/frame/contracts/fixtures/data/invalid_module.wat b/substrate/frame/contracts/fixtures/data/invalid_module.wat deleted file mode 100644 index e4a72f74273f9350420f737e0974516b3e5c4132..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/invalid_module.wat +++ /dev/null @@ -1,8 +0,0 @@ -;; An invalid module -(module - (func (export "deploy")) - (func (export "call") - ;; imbalanced stack - (i32.const 7) - ) -) diff --git a/substrate/frame/contracts/fixtures/data/run_out_of_gas_start_fn.wat b/substrate/frame/contracts/fixtures/data/run_out_of_gas_start_fn.wat deleted file mode 100644 index 6591d7ede78c20e0d19c7c60c183b4c58e98d2dc..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/run_out_of_gas_start_fn.wat +++ /dev/null @@ -1,10 +0,0 @@ -(module - (import "env" "memory" (memory 1 1)) - (start $start) - (func $start - (loop $inf (br $inf)) ;; just run out of gas - (unreachable) - ) - (func (export "call")) - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/data/seal_input_noop.wat b/substrate/frame/contracts/fixtures/data/seal_input_noop.wat deleted file mode 100644 index 7b5a1e32af4d61ba8dfecdc978e5f4d3a09e1480..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/seal_input_noop.wat +++ /dev/null @@ -1,14 +0,0 @@ -;; Everything prepared for the host function call, but no call is performed. -(module - (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - ;; [0, 8) buffer to write input - - ;; [8, 12) size of the input buffer - (data (i32.const 8) "\04") - - (func (export "call")) - - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/data/seal_input_once.wat b/substrate/frame/contracts/fixtures/data/seal_input_once.wat deleted file mode 100644 index 919a03a9b6903df3ff4917347b9c40a2d260b8f1..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/seal_input_once.wat +++ /dev/null @@ -1,22 +0,0 @@ -;; Stores a value of the passed size. The host function is called once. -(module - (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - ;; [0, 8) buffer to write input - - ;; [8, 12) size of the input buffer - (data (i32.const 8) "\04") - - (func (export "call") - ;; instructions to consume engine fuel - (drop - (i32.const 42) - ) - - (call $seal_input (i32.const 0) (i32.const 8)) - - ) - - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/data/seal_input_twice.wat b/substrate/frame/contracts/fixtures/data/seal_input_twice.wat deleted file mode 100644 index 3a8be814efb045078494294961686931c4cd7ab4..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/fixtures/data/seal_input_twice.wat +++ /dev/null @@ -1,28 +0,0 @@ -;; Stores a value of the passed size. The host function is called twice. -(module - (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "env" "memory" (memory 1 1)) - - ;; [0, 8) buffer to write input - - ;; [8, 12) size of the input buffer - (data (i32.const 8) "\04") - - (func (export "call") - ;; instructions to consume engine fuel - (drop - (i32.const 42) - ) - - (call $seal_input (i32.const 0) (i32.const 8)) - - ;; instructions to consume engine fuel - (drop - (i32.const 42) - ) - - (call $seal_input (i32.const 0) (i32.const 8)) - ) - - (func (export "deploy")) -) diff --git a/substrate/frame/contracts/fixtures/src/lib.rs b/substrate/frame/contracts/fixtures/src/lib.rs index e0d9d4f8bd5b1512f7998d497adacf695c5092e1..56a8e2321c5c3cc2181cbfeb676700993e3f0a77 100644 --- a/substrate/frame/contracts/fixtures/src/lib.rs +++ b/substrate/frame/contracts/fixtures/src/lib.rs @@ -16,34 +16,7 @@ // limitations under the License. use sp_runtime::traits::Hash; -use std::{env::var, fs, path::PathBuf}; - -fn wat_root_dir() -> PathBuf { - match (var("CARGO_MANIFEST_DIR"), var("CARGO_PKG_NAME")) { - // When `CARGO_MANIFEST_DIR` is not set, Rust resolves relative paths from the root folder - (Err(_), _) => "substrate/frame/contracts/fixtures/data".into(), - (Ok(path), Ok(s)) if s == "pallet-contracts" => PathBuf::from(path).join("fixtures/data"), - (Ok(path), Ok(s)) if s == "pallet-contracts-mock-network" => - PathBuf::from(path).parent().unwrap().join("fixtures/data"), - (Ok(_), pkg_name) => panic!("Failed to resolve fixture dir for tests from {pkg_name:?}."), - } -} - -/// Load a given wasm module represented by a .wat file and returns a wasm binary contents along -/// with it's hash. -/// -/// The fixture files are located under the `fixtures/` directory. -fn legacy_compile_module( - fixture_name: &str, -) -> anyhow::Result<(Vec, ::Output)> -where - T: frame_system::Config, -{ - let fixture_path = wat_root_dir().join(format!("{fixture_name}.wat")); - let wasm_binary = wat::parse_file(fixture_path)?; - let code_hash = T::Hashing::hash(&wasm_binary); - Ok((wasm_binary, code_hash)) -} +use std::{fs, path::PathBuf}; /// Load a given wasm module and returns a wasm binary contents along with it's hash. /// Use the legacy compile_module as fallback, if the rust fixture does not exist yet. @@ -53,15 +26,11 @@ pub fn compile_module( where T: frame_system::Config, { - let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let out_dir: PathBuf = env!("OUT_DIR").into(); let fixture_path = out_dir.join(format!("{fixture_name}.wasm")); - match fs::read(fixture_path) { - Ok(wasm_binary) => { - let code_hash = T::Hashing::hash(&wasm_binary); - Ok((wasm_binary, code_hash)) - }, - Err(_) => legacy_compile_module::(fixture_name), - } + let binary = fs::read(fixture_path)?; + let code_hash = T::Hashing::hash(&binary); + Ok((binary, code_hash)) } #[cfg(test)] diff --git a/substrate/frame/contracts/mock-network/Cargo.toml b/substrate/frame/contracts/mock-network/Cargo.toml index 7b570eed155c1b01b911ce82ca73291b9eab7dff..0c4cd1356f4dfddb5c8b3e194131a1de89693e89 100644 --- a/substrate/frame/contracts/mock-network/Cargo.toml +++ b/substrate/frame/contracts/mock-network/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-contracts-mock-network" -version = "1.0.0" +version = "3.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -55,7 +55,6 @@ std = [ "frame-support/std", "frame-system/std", "pallet-balances/std", - "pallet-contracts-proc-macro/full", "pallet-contracts/std", "pallet-insecure-randomness-collective-flip/std", "pallet-proxy/std", diff --git a/substrate/frame/contracts/mock-network/src/lib.rs b/substrate/frame/contracts/mock-network/src/lib.rs index eea9dde062c83172dbc106333f053657a7267cda..8a17a3f2fa781703f1393f2d69ca955384a080a2 100644 --- a/substrate/frame/contracts/mock-network/src/lib.rs +++ b/substrate/frame/contracts/mock-network/src/lib.rs @@ -26,7 +26,8 @@ use crate::primitives::{AccountId, UNITS}; use sp_runtime::BuildStorage; use xcm::latest::prelude::*; use xcm_executor::traits::ConvertLocation; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; +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]); diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index 90fc23af48d37783c2e5ed5cf3ac14b8dac78e72..7a60a66b3145dd7268f20924cd53ec81f2d32447 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -37,13 +37,12 @@ use sp_runtime::traits::{Get, IdentityLookup, MaybeEquivalence}; use sp_std::prelude::*; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, - ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, + ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, IsConcrete, NativeAsset, + NoChecking, ParentAsSuperuser, ParentIsPreset, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, WithComputedOrigin, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -94,7 +93,6 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; - type MaxHolds = ConstU32<1>; type MaxLocks = MaxLocks; type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; @@ -184,9 +182,8 @@ pub fn estimate_fee_for_weight(weight: Weight) -> u128 { units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128) } -#[allow(deprecated)] pub type LocalBalancesTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + FungibleAdapter, SovereignAccountOf, AccountId, ()>; pub struct FromLocationToAsset(PhantomData<(Location, AssetId)>); impl MaybeEquivalence @@ -284,6 +281,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl mock_msg_queue::Config for Runtime { diff --git a/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs b/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs index dadba394e26453366d0b90ce8ff18931ab00743f..3c06131dd6088a2b044b68cfbf3b4a918eb37e3e 100644 --- a/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs @@ -25,7 +25,7 @@ use frame_support::{ traits::{ConstBool, ConstU32, Contains, Randomness}, weights::Weight, }; -use frame_system::pallet_prelude::BlockNumberFor; +use frame_system::{pallet_prelude::BlockNumberFor, EnsureSigned}; use pallet_xcm::BalanceOf; use sp_runtime::{traits::Convert, Perbill}; @@ -90,9 +90,12 @@ impl pallet_contracts::Config for Runtime { type Schedule = Schedule; type Time = super::Timestamp; type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; type WeightInfo = (); type WeightPrice = Self; type Debug = (); type Environment = (); + type ApiVersion = (); type Xcm = pallet_xcm::Pallet; } diff --git a/substrate/frame/contracts/mock-network/src/relay_chain.rs b/substrate/frame/contracts/mock-network/src/relay_chain.rs index 9671857fb7886104240e9a498ec5f725d89e67b4..6eb9b4e53855c0c62e65392a1dd0a63adc239b0b 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -29,14 +29,12 @@ use sp_runtime::traits::IdentityLookup; use polkadot_parachain_primitives::primitives::Id as ParaId; use polkadot_runtime_parachains::{configuration, origin, shared}; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, DescribeAllTerminal, DescribeFamily, FixedRateOfFungible, - FixedWeightBounds, HashedDescription, IsConcrete, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, + FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, }; use xcm_executor::{Config, XcmExecutor}; @@ -93,7 +91,6 @@ impl pallet_balances::Config for Runtime { type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; @@ -120,9 +117,8 @@ pub type SovereignAccountOf = ( ChildParachainConvertsVia, ); -#[allow(deprecated)] pub type LocalBalancesTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + FungibleAdapter, SovereignAccountOf, AccountId, ()>; pub type AssetTransactors = LocalBalancesTransactor; @@ -185,6 +181,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index c25373bcbe3aeaaad5d0ac7396e70ed61a4729c6..d22221fe8ee051182471249182d54b02b7f47c2c 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -21,7 +21,6 @@ use crate::{ primitives::{AccountId, CENTS}, relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, }; -use assert_matches::assert_matches; use codec::{Decode, Encode}; use frame_support::{ assert_err, @@ -31,11 +30,18 @@ use frame_support::{ use pallet_balances::{BalanceLock, Reasons}; use pallet_contracts::{Code, CollectEvents, DebugInfo, Determinism}; use pallet_contracts_fixtures::compile_module; +use pallet_contracts_uapi::ReturnErrorCode; use xcm::{v4::prelude::*, VersionedLocation, VersionedXcm}; use xcm_simulator::TestExt; type ParachainContracts = pallet_contracts::Pallet; +macro_rules! assert_return_code { + ( $x:expr , $y:expr $(,)? ) => {{ + assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); + }}; +} + /// 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(); @@ -100,19 +106,57 @@ fn test_xcm_execute() { DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, - ) - .result - .unwrap(); + ); - let mut data = &result.data[..]; - let outcome = Outcome::decode(&mut data).expect("Failed to decode xcm_execute Outcome"); - assert_matches!(outcome, Outcome::Complete { .. }); + 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!(parachain::Assets::balance(0, contract_addr), initial); 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(|| { + // The XCM used to transfer funds to Bob. + let message: Xcm<()> = Xcm(vec![ + WithdrawAsset(vec![(Here, amount).into()].into()), + // This will fail as the contract does not have enough balance to complete both + // withdrawals. + WithdrawAsset(vec![(Here, INITIAL_BALANCE).into()].into()), + DepositAsset { + assets: All.into(), + beneficiary: AccountId32 { network: None, id: BOB.clone().into() }.into(), + }, + ]); + + let result = ParachainContracts::bare_call( + ALICE, + contract_addr.clone(), + 0, + Weight::MAX, + None, + VersionedXcm::V4(message).encode(), + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + Determinism::Enforced, + ); + + 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); }); } @@ -182,17 +226,10 @@ fn test_xcm_execute_reentrant_call() { DebugInfo::UnsafeDebug, CollectEvents::UnsafeCollect, Determinism::Enforced, - ) - .result - .unwrap(); - - let mut data = &result.data[..]; - let outcome = Outcome::decode(&mut data).expect("Failed to decode xcm_execute Outcome"); - assert_matches!( - outcome, - Outcome::Incomplete { used: _, error: XcmError::ExpectationFalse } ); + assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); + // Funds should not change hands as the XCM transact failed. assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); }); diff --git a/substrate/frame/contracts/proc-macro/Cargo.toml b/substrate/frame/contracts/proc-macro/Cargo.toml index 1c9802f6fde073941604e4173e2d298dbb440551..4080cd0442dbc516231dc983a3b6609a211523db 100644 --- a/substrate/frame/contracts/proc-macro/Cargo.toml +++ b/substrate/frame/contracts/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-contracts-proc-macro" -version = "4.0.0-dev" +version = "18.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,11 +19,5 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.56" -quote = "1.0.28" -syn = { version = "2.0.48", features = ["full"] } - -[dev-dependencies] - -[features] -# If set the full output is generated. Do NOT set when generating for wasm runtime. -full = [] +quote = { workspace = true } +syn = { features = ["full"], workspace = true } diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs index fa141548635f71ade63fbe881dadbc3fa620d422..f43c86664dd5f6bd6237774a01c4926e90a90cab 100644 --- a/substrate/frame/contracts/proc-macro/src/lib.rs +++ b/substrate/frame/contracts/proc-macro/src/lib.rs @@ -20,23 +20,13 @@ //! Most likely you should use the [`#[define_env]`][`macro@define_env`] attribute macro which hides //! boilerplate of defining external environment for a wasm module. -#![no_std] - -extern crate alloc; - -use alloc::{ - collections::BTreeMap, - format, - string::{String, ToString}, - vec::Vec, -}; use core::cmp::Reverse; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned, ToTokens}; use syn::{ parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DeriveInput, - FnArg, Ident, + Fields, FnArg, Ident, }; /// This derives `Debug` for a struct where each field must be of some numeric type. @@ -44,17 +34,6 @@ use syn::{ /// it is readable by humans. #[proc_macro_derive(WeightDebug)] pub fn derive_weight_debug(input: TokenStream) -> TokenStream { - derive_debug(input, format_weight) -} - -/// This is basically identical to the std libs Debug derive but without adding any -/// bounds to existing generics. -#[proc_macro_derive(ScheduleDebug)] -pub fn derive_schedule_debug(input: TokenStream) -> TokenStream { - derive_debug(input, format_default) -} - -fn derive_debug(input: TokenStream, fmt: impl Fn(&Ident) -> TokenStream2) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); @@ -68,45 +47,15 @@ fn derive_debug(input: TokenStream, fmt: impl Fn(&Ident) -> TokenStream2) -> Tok .into() }; - #[cfg(feature = "full")] - let fields = iterate_fields(data, fmt); - - #[cfg(not(feature = "full"))] - let fields = { - drop(fmt); - let _ = data; - TokenStream2::new() - }; - - let tokens = quote! { - impl #impl_generics core::fmt::Debug for #name #ty_generics #where_clause { - fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - use ::sp_runtime::{FixedPointNumber, FixedU128 as Fixed}; - let mut formatter = formatter.debug_struct(stringify!(#name)); - #fields - formatter.finish() - } - } - }; - - tokens.into() -} - -/// This is only used then the `full` feature is activated. -#[cfg(feature = "full")] -fn iterate_fields(data: &syn::DataStruct, fmt: impl Fn(&Ident) -> TokenStream2) -> TokenStream2 { - use syn::Fields; - - match &data.fields { + let fields = match &data.fields { Fields::Named(fields) => { let recurse = fields.named.iter().filter_map(|f| { let name = f.ident.as_ref()?; if name.to_string().starts_with('_') { return None } - let value = fmt(name); let ret = quote_spanned! { f.span() => - formatter.field(stringify!(#name), #value); + formatter.field(stringify!(#name), &HumanWeight(self.#name)); }; Some(ret) }); @@ -119,39 +68,53 @@ fn iterate_fields(data: &syn::DataStruct, fmt: impl Fn(&Ident) -> TokenStream2) compile_error!("Unnamed fields are not supported") }, Fields::Unit => quote!(), - } -} + }; -fn format_weight(field: &Ident) -> TokenStream2 { - quote_spanned! { field.span() => - &if self.#field.ref_time() > 1_000_000_000 { - format!( - "{:.1?} ms, {} bytes", - Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000_000).to_float(), - self.#field.proof_size() - ) - } else if self.#field.ref_time() > 1_000_000 { - format!( - "{:.1?} µs, {} bytes", - Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000).to_float(), - self.#field.proof_size() - ) - } else if self.#field.ref_time() > 1_000 { - format!( - "{:.1?} ns, {} bytes", - Fixed::saturating_from_rational(self.#field.ref_time(), 1_000).to_float(), - self.#field.proof_size() - ) - } else { - format!("{} ps, {} bytes", self.#field.ref_time(), self.#field.proof_size()) + let tokens = quote! { + impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> core::fmt::Result { + use ::sp_runtime::{FixedPointNumber, FixedU128 as Fixed}; + use ::core::{fmt, write}; + + struct HumanWeight(Weight); + + impl fmt::Debug for HumanWeight { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0.ref_time() > 1_000_000_000 { + write!( + formatter, + "{} ms, {} bytes", + Fixed::saturating_from_rational(self.0.ref_time(), 1_000_000_000).into_inner() / Fixed::accuracy(), + self.0.proof_size() + ) + } else if self.0.ref_time() > 1_000_000 { + write!( + formatter, + "{} µs, {} bytes", + Fixed::saturating_from_rational(self.0.ref_time(), 1_000_000).into_inner() / Fixed::accuracy(), + self.0.proof_size() + ) + } else if self.0.ref_time() > 1_000 { + write!( + formatter, + "{} ns, {} bytes", + Fixed::saturating_from_rational(self.0.ref_time(), 1_000).into_inner() / Fixed::accuracy(), + self.0.proof_size() + ) + } else { + write!(formatter, "{} ps, {} bytes", self.0.ref_time(), self.0.proof_size()) + } + } + } + + let mut formatter = formatter.debug_struct(stringify!(#name)); + #fields + formatter.finish() + } } - } -} + }; -fn format_default(field: &Ident) -> TokenStream2 { - quote_spanned! { field.span() => - &self.#field - } + tokens.into() } /// Parsed environment definition. @@ -480,7 +443,7 @@ fn expand_func_doc(func: &HostFn) -> TokenStream2 { fn expand_docs(def: &EnvDef) -> TokenStream2 { // Create the `Current` trait with only the newest versions // we sort so that only the newest versions make it into `docs` - let mut current_docs = BTreeMap::new(); + let mut current_docs = std::collections::HashMap::new(); let mut funcs: Vec<_> = def.host_funcs.iter().filter(|f| f.alias_to.is_none()).collect(); funcs.sort_unstable_by_key(|func| Reverse(func.version)); for func in funcs { @@ -493,7 +456,7 @@ fn expand_docs(def: &EnvDef) -> TokenStream2 { // Create the `legacy` module with all functions // Maps from version to list of functions that have this version - let mut legacy_doc = BTreeMap::>::new(); + let mut legacy_doc = std::collections::BTreeMap::>::new(); for func in def.host_funcs.iter() { legacy_doc.entry(func.version).or_default().push(expand_func_doc(&func)); } @@ -534,9 +497,14 @@ fn expand_docs(def: &EnvDef) -> TokenStream2 { fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 { let impls = expand_impls(def); let docs = docs.then_some(expand_docs(def)).unwrap_or(TokenStream2::new()); + let stable_api_count = def.host_funcs.iter().filter(|f| f.is_stable).count(); quote! { pub struct Env; + + #[cfg(test)] + pub const STABLE_API_COUNT: usize = #stable_api_count; + #impls /// Documentation of the API (host functions) available to contracts. /// @@ -675,37 +643,34 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) }; let sync_gas_before = if expand_blocks { quote! { - // Gas left in the gas meter right before switching to engine execution. - let __gas_before__ = { - let engine_consumed_total = + // Write gas from wasmi into pallet-contracts before entering the host function. + let __gas_left_before__ = { + let executor_total = __caller__.fuel_consumed().expect("Fuel metering is enabled; qed"); - let gas_meter = __caller__.data_mut().ext().gas_meter_mut(); - gas_meter - .charge_fuel(engine_consumed_total) + __caller__ + .data_mut() + .ext() + .gas_meter_mut() + .sync_from_executor(executor_total) .map_err(TrapReason::from) .map_err(#into_host)? - .ref_time() }; } } else { quote! { } }; - // Gas left in the gas meter right after returning from engine execution. + // Write gas from pallet-contracts into wasmi after leaving the host function. let sync_gas_after = if expand_blocks { quote! { - let mut gas_after = __caller__.data_mut().ext().gas_meter().gas_left().ref_time(); - let mut host_consumed = __gas_before__.saturating_sub(gas_after); - // Possible undercharge of at max 1 fuel here, if host consumed less than `instruction_weights.base` - // Not a problem though, as soon as host accounts its spent gas properly. - let fuel_consumed = host_consumed - .checked_div(__caller__.data_mut().ext().schedule().instruction_weights.base as u64) - .ok_or(Error::::InvalidSchedule) - .map_err(TrapReason::from) - .map_err(#into_host)?; + let fuel_consumed = __caller__ + .data_mut() + .ext() + .gas_meter_mut() + .sync_to_executor(__gas_left_before__) + .map_err(TrapReason::from)?; __caller__ - .consume_fuel(fuel_consumed) - .map_err(|_| TrapReason::from(Error::::OutOfGas)) - .map_err(#into_host)?; + .consume_fuel(fuel_consumed.into()) + .map_err(|_| TrapReason::from(Error::::OutOfGas))?; } } else { quote! { } diff --git a/substrate/frame/contracts/src/benchmarking/code.rs b/substrate/frame/contracts/src/benchmarking/code.rs index 0b6ab6b3c0e1b99b2dfb34642568d29cc9dfdd91..96ce11f8c4183a53d7e8e75fc0f6d826e861c54b 100644 --- a/substrate/frame/contracts/src/benchmarking/code.rs +++ b/substrate/frame/contracts/src/benchmarking/code.rs @@ -26,13 +26,13 @@ use crate::Config; use frame_support::traits::Get; -use sp_runtime::traits::Hash; +use sp_runtime::{traits::Hash, Saturating}; use sp_std::{borrow::ToOwned, prelude::*}; use wasm_instrument::parity_wasm::{ builder, elements::{ - self, BlockType, CustomSection, External, FuncBody, Instruction, Instructions, Local, - Module, Section, ValueType, + self, BlockType, CustomSection, FuncBody, Instruction, Instructions, Local, Section, + ValueType, }, }; @@ -238,24 +238,6 @@ impl From for WasmModule { } impl WasmModule { - /// Uses the supplied wasm module. - pub fn from_code(code: &[u8]) -> Self { - let module = Module::from_bytes(code).unwrap(); - let limits = *module - .import_section() - .unwrap() - .entries() - .iter() - .find_map(|e| if let External::Memory(mem) = e.external() { Some(mem) } else { None }) - .unwrap() - .limits(); - let code = module.into_bytes().unwrap(); - let hash = T::Hashing::hash(&code); - let memory = - ImportedMemory { min_pages: limits.initial(), max_pages: limits.maximum().unwrap() }; - Self { code: code.into(), hash, memory: Some(memory) } - } - /// Creates a wasm module with an empty `call` and `deploy` function and nothing else. pub fn dummy() -> Self { ModuleDefinition::default().into() @@ -280,22 +262,25 @@ impl WasmModule { /// `instantiate_with_code` for different sizes of wasm modules. The generated module maximizes /// instrumentation runtime by nesting blocks as deeply as possible given the byte budget. /// `code_location`: Whether to place the code into `deploy` or `call`. - pub fn sized(target_bytes: u32, code_location: Location) -> Self { + pub fn sized(target_bytes: u32, code_location: Location, use_float: bool) -> Self { use self::elements::Instruction::{End, GetLocal, If, Return}; // Base size of a contract is 63 bytes and each expansion adds 6 bytes. // We do one expansion less to account for the code section and function body // size fields inside the binary wasm module representation which are leb128 encoded // and therefore grow in size when the contract grows. We are not allowed to overshoot // because of the maximum code size that is enforced by `instantiate_with_code`. - let expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1); + let mut expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1); const EXPANSION: [Instruction; 4] = [GetLocal(0), If(BlockType::NoResult), Return, End]; + let mut locals = vec![Local::new(1, ValueType::I32)]; + if use_float { + locals.push(Local::new(1, ValueType::F32)); + locals.push(Local::new(2, ValueType::F32)); + locals.push(Local::new(3, ValueType::F32)); + expansions.saturating_dec(); + } let mut module = ModuleDefinition { memory: Some(ImportedMemory::max::()), ..Default::default() }; - let body = Some(body::repeated_with_locals( - &[Local::new(1, ValueType::I32)], - expansions, - &EXPANSION, - )); + let body = Some(body::repeated_with_locals(&locals, expansions, &EXPANSION)); match code_location { Location::Call => module.call_body = body, Location::Deploy => module.deploy_body = body, diff --git a/substrate/frame/contracts/src/benchmarking/mod.rs b/substrate/frame/contracts/src/benchmarking/mod.rs index f68f51c29066fe01d4ce0747eb734d342b583604..794777ef05cfae248542c61a811875925deaed45 100644 --- a/substrate/frame/contracts/src/benchmarking/mod.rs +++ b/substrate/frame/contracts/src/benchmarking/mod.rs @@ -29,7 +29,7 @@ use self::{ sandbox::Sandbox, }; use crate::{ - exec::{AccountIdOf, Key}, + exec::Key, migration::{ codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, v15, MigrationStep, }, @@ -184,24 +184,6 @@ fn caller_funding() -> BalanceOf { BalanceOf::::max_value() / 10_000u32.into() } -/// Load the specified contract file from disk by including it into the runtime. -/// -/// We need to load a different version of ink! contracts when the benchmark is run as -/// a test. This is because ink! contracts depend on the sizes of types that are defined -/// differently in the test environment. Solang is more lax in that regard. -macro_rules! load_benchmark { - ($name:expr) => {{ - #[cfg(not(test))] - { - include_bytes!(concat!("../../benchmarks/", $name, ".wasm")) - } - #[cfg(test)] - { - include_bytes!(concat!("../../benchmarks/", $name, "_test.wasm")) - } - }}; -} - benchmarks! { where_clause { where as codec::HasCompact>::Type: Clone + Eq + PartialEq + sp_std::fmt::Debug + scale_info::TypeInfo + codec::Encode, @@ -380,7 +362,7 @@ benchmarks! { call_with_code_per_byte { let c in 0 .. T::MaxCodeLen::get(); let instance = Contract::::with_caller( - whitelisted_caller(), WasmModule::sized(c, Location::Deploy), vec![], + whitelisted_caller(), WasmModule::sized(c, Location::Deploy, false), vec![], )?; let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); @@ -407,7 +389,7 @@ benchmarks! { let value = Pallet::::min_balance(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); + let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call, false); let origin = RawOrigin::Signed(caller.clone()); let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); }: _(origin, value, Weight::MAX, None, code, input, salt) @@ -486,19 +468,36 @@ benchmarks! { // It creates a maximum number of metering blocks per byte. // `c`: Size of the code in bytes. #[pov_mode = Measured] - upload_code { + upload_code_determinism_enforced { let c in 0 .. T::MaxCodeLen::get(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); + let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call, false); let origin = RawOrigin::Signed(caller.clone()); - }: _(origin, code, None, Determinism::Enforced) + }: upload_code(origin, code, None, Determinism::Enforced) verify { // uploading the code reserves some balance in the callers account assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into()); assert!(>::code_exists(&hash)); } + // Uploading code with [`Determinism::Relaxed`] should be more expensive than uploading code with [`Determinism::Enforced`], + // as we always try to save the code with [`Determinism::Enforced`] first. + #[pov_mode = Measured] + upload_code_determinism_relaxed { + let c in 0 .. T::MaxCodeLen::get(); + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call, true); + let origin = RawOrigin::Signed(caller.clone()); + }: upload_code(origin, code, None, Determinism::Relaxed) + verify { + assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into()); + assert!(>::code_exists(&hash)); + // Ensure that the benchmark follows the most expensive path, i.e., the code is saved with [`Determinism::Relaxed`] after trying to save it with [`Determinism::Enforced`]. + assert_eq!(CodeInfoOf::::get(&hash).unwrap().determinism(), Determinism::Relaxed); + } + // 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`). @@ -913,7 +912,7 @@ benchmarks! { }, ImportedFunction { module: "seal0", - name: "add_delegate_dependency", + name: "lock_delegate_dependency", params: vec![ValueType::I32], return_type: None, } @@ -928,7 +927,7 @@ benchmarks! { value: code_hashes_bytes, }, ], - deploy_body: Some(body::repeated_dyn(r, vec![ + deploy_body: Some(body::repeated_dyn(T::MaxDelegateDependencies::get(), vec![ Counter(beneficiary_len as u32, code_hash_len as u32), // code_hash_ptr Regular(Instruction::Call(1)), ])), @@ -944,6 +943,7 @@ benchmarks! { assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into()); assert_eq!(T::Currency::balance(&instance.account_id), Pallet::::min_balance() * 2u32.into()); assert_ne!(T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &instance.account_id), 0u32.into()); + assert_eq!(ContractInfoOf::::get(&instance.account_id).unwrap().delegate_dependencies_count() as u32, T::MaxDelegateDependencies::get()); }: call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![]) verify { if r > 0 { @@ -2420,7 +2420,7 @@ benchmarks! { }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) #[pov_mode = Measured] - add_delegate_dependency { + lock_delegate_dependency { let r in 0 .. T::MaxDelegateDependencies::get(); let code_hashes = (0..r) .map(|i| { @@ -2438,7 +2438,7 @@ benchmarks! { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { module: "seal0", - name: "add_delegate_dependency", + name: "lock_delegate_dependency", params: vec![ValueType::I32], return_type: None, }], @@ -2458,7 +2458,7 @@ benchmarks! { let origin = RawOrigin::Signed(instance.caller.clone()); }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) - remove_delegate_dependency { + unlock_delegate_dependency { let r in 0 .. T::MaxDelegateDependencies::get(); let code_hashes = (0..r) .map(|i| { @@ -2477,12 +2477,12 @@ benchmarks! { memory: Some(ImportedMemory::max::()), imported_functions: vec![ImportedFunction { module: "seal0", - name: "remove_delegate_dependency", + name: "unlock_delegate_dependency", params: vec![ValueType::I32], return_type: None, }, ImportedFunction { module: "seal0", - name: "add_delegate_dependency", + name: "lock_delegate_dependency", params: vec![ValueType::I32], return_type: None }], @@ -2628,107 +2628,21 @@ benchmarks! { } // This is no benchmark. It merely exist to have an easy way to pretty print the currently - // configured `Schedule` during benchmark development. - // It can be outputted using the following command: - // cargo run --manifest-path=bin/node/cli/Cargo.toml \ - // --features runtime-benchmarks -- benchmark pallet --extra --dev --execution=native \ - // -p pallet_contracts -e print_schedule --no-median-slopes --no-min-squares + // configured `Schedule` during benchmark development. Check the README on how to print this. #[extra] #[pov_mode = Ignored] print_schedule { - #[cfg(feature = "std")] - { - let max_weight = ::BlockWeights::get().max_block; - let (weight_per_key, key_budget) = ContractInfo::::deletion_budget(max_weight); - println!("{:#?}", Schedule::::default()); - println!("###############################################"); - println!("Lazy deletion weight per key: {weight_per_key}"); - println!("Lazy deletion throughput per block: {key_budget}"); - } - #[cfg(not(feature = "std"))] - Err("Run this bench with a native runtime in order to see the schedule.")?; + let max_weight = ::BlockWeights::get().max_block; + let (weight_per_key, key_budget) = ContractInfo::::deletion_budget(max_weight); + let schedule = T::Schedule::get(); + log::info!(target: LOG_TARGET, " + {schedule:#?} + ############################################### + Lazy deletion weight per key: {weight_per_key} + Lazy deletion keys per block: {key_budget} + "); }: {} - // Execute one erc20 transfer using the ink! erc20 example contract. - #[extra] - #[pov_mode = Measured] - ink_erc20_transfer { - let code = load_benchmark!("ink_erc20"); - let data = { - let new: ([u8; 4], BalanceOf) = ([0x9b, 0xae, 0x9d, 0x5e], 1000u32.into()); - new.encode() - }; - let instance = Contract::::new( - WasmModule::from_code(code), data, - )?; - let data = { - let transfer: ([u8; 4], AccountIdOf, BalanceOf) = ( - [0x84, 0xa1, 0x5d, 0xa1], - account::("receiver", 0, 0), - 1u32.into(), - ); - transfer.encode() - }; - }: { - >::bare_call( - instance.caller, - instance.account_id, - 0u32.into(), - Weight::MAX, - None, - data, - DebugInfo::Skip, - CollectEvents::Skip, - Determinism::Enforced, - ) - .result?; - } - - // Execute one erc20 transfer using the open zeppelin erc20 contract compiled with solang. - #[extra] - #[pov_mode = Measured] - solang_erc20_transfer { - let code = include_bytes!("../../benchmarks/solang_erc20.wasm"); - let caller = account::("instantiator", 0, 0); - let mut balance = [0u8; 32]; - balance[0] = 100; - let data = { - let new: ([u8; 4], &str, &str, [u8; 32], AccountIdOf) = ( - [0xa6, 0xf1, 0xf5, 0xe1], - "KSM", - "K", - balance, - caller.clone(), - ); - new.encode() - }; - let instance = Contract::::with_caller( - caller, WasmModule::from_code(code), data, - )?; - balance[0] = 1; - let data = { - let transfer: ([u8; 4], AccountIdOf, [u8; 32]) = ( - [0x6a, 0x46, 0x73, 0x94], - account::("receiver", 0, 0), - balance, - ); - transfer.encode() - }; - }: { - >::bare_call( - instance.caller, - instance.account_id, - 0u32.into(), - Weight::MAX, - None, - data, - DebugInfo::Skip, - CollectEvents::Skip, - Determinism::Enforced, - ) - .result?; - } - impl_benchmark_test_suite!( Contracts, crate::tests::ExtBuilder::default().build(), diff --git a/substrate/frame/contracts/src/exec.rs b/substrate/frame/contracts/src/exec.rs index 2183d6b96cc5d4f84cbfa6777e0c1b0bbeec4f86..7da321af547d5b8f5539673f7540c328299945b6 100644 --- a/substrate/frame/contracts/src/exec.rs +++ b/substrate/frame/contracts/src/exec.rs @@ -287,6 +287,9 @@ pub trait Ext: sealing::Sealed { /// 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; @@ -345,20 +348,20 @@ pub trait Ext: sealing::Sealed { /// - [`Error::::MaxDelegateDependenciesReached`] /// - [`Error::::CannotAddSelfAsDelegateDependency`] /// - [`Error::::DelegateDependencyAlreadyExists`] - fn add_delegate_dependency( + fn lock_delegate_dependency( &mut self, code_hash: CodeHash, ) -> Result<(), DispatchError>; /// Removes a delegate dependency from [`ContractInfo`]'s `delegate_dependencies` field. /// - /// This is the counterpart of [`Self::add_delegate_dependency`]. It decreases the reference - /// count and refunds the deposit that was charged by [`Self::add_delegate_dependency`]. + /// 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 remove_delegate_dependency( + fn unlock_delegate_dependency( &mut self, code_hash: &CodeHash, ) -> Result<(), DispatchError>; @@ -834,7 +837,7 @@ where contract_info: CachedContract::Cached(contract_info), account_id, entry_point, - nested_gas: gas_meter.nested(gas_limit)?, + nested_gas: gas_meter.nested(gas_limit), nested_storage: storage_meter.nested(deposit_limit), allows_reentry: true, }; @@ -1429,6 +1432,10 @@ where 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 @@ -1547,7 +1554,7 @@ where }); } - fn add_delegate_dependency( + fn lock_delegate_dependency( &mut self, code_hash: CodeHash, ) -> Result<(), DispatchError> { @@ -1558,7 +1565,7 @@ where let code_info = CodeInfoOf::::get(code_hash).ok_or(Error::::CodeNotFound)?; let deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit()); - info.add_delegate_dependency(code_hash, deposit)?; + info.lock_delegate_dependency(code_hash, deposit)?; Self::increment_refcount(code_hash)?; frame .nested_storage @@ -1566,14 +1573,14 @@ where Ok(()) } - fn remove_delegate_dependency( + fn unlock_delegate_dependency( &mut self, code_hash: &CodeHash, ) -> Result<(), DispatchError> { let frame = self.top_frame_mut(); let info = frame.contract_info.get(&frame.account_id); - let deposit = info.remove_delegate_dependency(code_hash)?; + let deposit = info.unlock_delegate_dependency(code_hash)?; Self::decrement_refcount(*code_hash); frame .nested_storage diff --git a/substrate/frame/contracts/src/gas.rs b/substrate/frame/contracts/src/gas.rs index 363ddfad975b1eb92c6a4ebcc23982f585eef08c..b9d91f38f16f957ae6bc40ef08010ecf146dbf03 100644 --- a/substrate/frame/contracts/src/gas.rs +++ b/substrate/frame/contracts/src/gas.rs @@ -16,14 +16,14 @@ // limitations under the License. use crate::{exec::ExecError, Config, Error}; +use core::marker::PhantomData; use frame_support::{ dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, PostDispatchInfo}, weights::Weight, DefaultNoBound, }; use sp_core::Get; -use sp_runtime::{traits::Zero, DispatchError}; -use sp_std::marker::PhantomData; +use sp_runtime::{traits::Zero, DispatchError, Saturating}; #[cfg(test)] use std::{any::Any, fmt::Debug}; @@ -37,6 +37,24 @@ impl ChargedAmount { } } +/// 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(u64); + +impl From for u64 { + fn from(from: Syncable) -> u64 { + from.0 + } +} + #[cfg(not(test))] pub trait TestAuxiliaries {} #[cfg(not(test))] @@ -63,6 +81,11 @@ pub trait Token: Copy + Clone + TestAuxiliaries { /// 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`. @@ -79,8 +102,13 @@ pub struct GasMeter { gas_left: Weight, /// Due to `adjust_gas` and `nested` the `gas_left` can temporarily dip below its final value. gas_left_lowest: Weight, - /// Amount of fuel consumed by the engine from the last host function call. - engine_consumed: u64, + /// The amount of resources that was consumed by the execution engine. + /// + /// This should be equivalent to `self.gas_consumed().ref_time()` but expressed in whatever + /// unit the execution engine uses to track resource consumption. 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. + executor_consumed: u64, _phantom: PhantomData, #[cfg(test)] tokens: Vec, @@ -92,7 +120,7 @@ impl GasMeter { gas_limit, gas_left: gas_limit, gas_left_lowest: gas_limit, - engine_consumed: Default::default(), + executor_consumed: 0, _phantom: PhantomData, #[cfg(test)] tokens: Vec::new(), @@ -104,9 +132,7 @@ impl GasMeter { /// # Note /// /// Passing `0` as amount is interpreted as "all remaining gas". - pub fn nested(&mut self, amount: Weight) -> Result { - // NOTE that it is ok to allocate all available gas since it still ensured - // by `charge` that it doesn't reach zero. + pub fn nested(&mut self, amount: Weight) -> Self { let amount = Weight::from_parts( if amount.ref_time().is_zero() { self.gas_left().ref_time() @@ -118,33 +144,17 @@ impl GasMeter { } else { amount.proof_size() }, - ); - self.gas_left = self.gas_left.checked_sub(&amount).ok_or_else(|| >::OutOfGas)?; - Ok(GasMeter::new(amount)) + ) + .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) { - if self.gas_left.ref_time().is_zero() { - // All of the remaining gas was inherited by the nested gas meter. When absorbing - // we can therefore safely inherit the lowest gas that the nested gas meter experienced - // as long as it is lower than the lowest gas that was experienced by the parent. - // We cannot call `self.gas_left_lowest()` here because in the state that this - // code is run the parent gas meter has `0` gas left. - *self.gas_left_lowest.ref_time_mut() = - nested.gas_left_lowest().ref_time().min(self.gas_left_lowest.ref_time()); - } else { - // The nested gas meter was created with a fixed amount that did not consume all of the - // parents (self) gas. The lowest gas that self will experience is when the nested - // gas was pre charged with the fixed amount. - *self.gas_left_lowest.ref_time_mut() = self.gas_left_lowest().ref_time(); - } - if self.gas_left.proof_size().is_zero() { - *self.gas_left_lowest.proof_size_mut() = - nested.gas_left_lowest().proof_size().min(self.gas_left_lowest.proof_size()); - } else { - *self.gas_left_lowest.proof_size_mut() = self.gas_left_lowest().proof_size(); - } + 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; } @@ -178,37 +188,48 @@ impl GasMeter { /// 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) { - self.gas_left_lowest = self.gas_left_lowest(); + 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); } - /// This method is used for gas syncs with the engine. + /// Hand over the gas metering responsibility from the executor to this meter. /// - /// Updates internal `engine_comsumed` tracker of engine fuel consumption. + /// 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, + executor_total: u64, + ) -> Result { + let chargable_reftime = executor_total + .saturating_sub(self.executor_consumed) + .saturating_mul(u64::from(T::Schedule::get().instruction_weights.base)); + self.executor_consumed = executor_total; + self.gas_left + .checked_reduce(Weight::from_parts(chargable_reftime, 0)) + .ok_or_else(|| Error::::OutOfGas)?; + Ok(RefTimeLeft(self.gas_left.ref_time())) + } + + /// Hand over the gas metering responsibility from this meter to the executor. /// - /// Charges self with the `ref_time` Weight corresponding to wasmi fuel consumed on the engine - /// side since last sync. Passed value is scaled by multiplying it by the weight of a basic - /// operation, as such an operation in wasmi engine costs 1. + /// 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. /// - /// Returns the updated `gas_left` `Weight` value from the meter. - /// Normally this would never fail, as engine should fail first when out of gas. - pub fn charge_fuel(&mut self, wasmi_fuel_total: u64) -> Result { - // Take the part consumed since the last update. - let wasmi_fuel = wasmi_fuel_total.saturating_sub(self.engine_consumed); - if !wasmi_fuel.is_zero() { - self.engine_consumed = wasmi_fuel_total; - let reftime_consumed = - wasmi_fuel.saturating_mul(T::Schedule::get().instruction_weights.base as u64); - let ref_time_left = self - .gas_left - .ref_time() - .checked_sub(reftime_consumed) - .ok_or_else(|| Error::::OutOfGas)?; - - *(self.gas_left.ref_time_mut()) = ref_time_left; - } - Ok(self.gas_left) + /// 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 chargable_executor_resource = before + .0 + .saturating_sub(self.gas_left().ref_time()) + .checked_div(u64::from(T::Schedule::get().instruction_weights.base)) + .ok_or(Error::::InvalidSchedule)?; + self.executor_consumed.saturating_accrue(chargable_executor_resource); + Ok(Syncable(chargable_executor_resource)) } /// Returns the amount of gas that is required to run the same call. diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 533085f2b874f6cc1d6c122fa6db5c8498c3db0c..de84d5220c548a29cf046c85b8fea0520a5d3e91 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -214,6 +214,27 @@ pub struct Environment { block_number: EnvironmentType>, } +/// Defines the current version of the HostFn APIs. +/// This is used to communicate the available APIs in pallet-contracts. +/// +/// The version is bumped any time a new HostFn is added or stabilized. +#[derive(Encode, Decode, TypeInfo)] +pub struct ApiVersion(u16); +impl Default for ApiVersion { + fn default() -> Self { + Self(2) + } +} + +#[test] +fn api_version_is_up_to_date() { + assert_eq!( + 109, + crate::wasm::STABLE_API_COUNT, + "Stable API count has changed. Bump the returned value of ApiVersion::default() and update the test." + ); +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -222,7 +243,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_runtime::Perbill; - /// The current storage version. + /// The in-code storage version. pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(15); #[pallet::pallet] @@ -325,7 +346,7 @@ pub mod pallet { 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::add_delegate_dependency`] + /// 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] @@ -347,7 +368,7 @@ pub mod pallet { type MaxStorageKeyLen: Get; /// The maximum number of delegate_dependencies that a contract can lock with - /// [`chain_extension::Ext::add_delegate_dependency`]. + /// [`chain_extension::Ext::lock_delegate_dependency`]. #[pallet::constant] type MaxDelegateDependencies: Get; @@ -367,6 +388,24 @@ pub mod pallet { #[pallet::constant] type MaxDebugBufferLen: Get; + /// Origin allowed to upload code. + /// + /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to upload contract + /// code. + 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. + type InstantiateOrigin: EnsureOrigin; + /// Overarching hold reason. type RuntimeHoldReason: From; @@ -402,6 +441,12 @@ pub mod pallet { #[pallet::constant] type Environment: Get>; + /// The version of the HostFn APIs that are available in the runtime. + /// + /// Only valid value is `()`. + #[pallet::constant] + type ApiVersion: Get; + /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and /// execute XCM programs. type Xcm: xcm_builder::Controller< @@ -609,8 +654,17 @@ pub mod pallet { /// 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`]. + /// + /// Use [`Determinism::Relaxed`] exclusively for non-deterministic code. If the uploaded + /// code is deterministic, specifying [`Determinism::Relaxed`] will be disregarded and + /// result in higher gas costs. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))] + #[pallet::weight( + match determinism { + Determinism::Enforced => T::WeightInfo::upload_code_determinism_enforced(code.len() as u32), + Determinism::Relaxed => T::WeightInfo::upload_code_determinism_relaxed(code.len() as u32), + } + )] pub fn upload_code( origin: OriginFor, code: Vec, @@ -618,7 +672,7 @@ pub mod pallet { determinism: Determinism, ) -> DispatchResult { Migration::::ensure_migrated()?; - let origin = ensure_signed(origin)?; + let origin = T::UploadOrigin::ensure_origin(origin)?; Self::bare_upload_code(origin, code, storage_deposit_limit.map(Into::into), determinism) .map(|_| ()) } @@ -767,11 +821,17 @@ pub mod pallet { salt: Vec, ) -> DispatchResultWithPostInfo { Migration::::ensure_migrated()?; - let origin = ensure_signed(origin)?; + + // These two origins will usually be the same; however, we treat them as separate since + // it is possible for the `Success` value of `UploadOrigin` and `InstantiateOrigin` to + // differ. + let upload_origin = T::UploadOrigin::ensure_origin(origin.clone())?; + let instantiate_origin = T::InstantiateOrigin::ensure_origin(origin)?; + let code_len = code.len() as u32; let (module, upload_deposit) = Self::try_upload_code( - origin.clone(), + upload_origin, code, storage_deposit_limit.clone().map(Into::into), Determinism::Enforced, @@ -785,7 +845,7 @@ pub mod pallet { let data_len = data.len() as u32; let salt_len = salt.len() as u32; let common = CommonInput { - origin: Origin::from_account_id(origin), + origin: Origin::from_account_id(instantiate_origin), value, data, gas_limit, @@ -826,10 +886,11 @@ pub mod pallet { salt: Vec, ) -> DispatchResultWithPostInfo { Migration::::ensure_migrated()?; + let origin = T::InstantiateOrigin::ensure_origin(origin)?; let data_len = data.len() as u32; let salt_len = salt.len() as u32; let common = CommonInput { - origin: Origin::from_runtime_origin(origin)?, + origin: Origin::from_account_id(origin), value, data, gas_limit, diff --git a/substrate/frame/contracts/src/migration.rs b/substrate/frame/contracts/src/migration.rs index 6d61cb6b1e1af158b9d4942efabc73267f8e44f7..f30ae1ebfadee55eeca000e74b4aac76f93f7551 100644 --- a/substrate/frame/contracts/src/migration.rs +++ b/substrate/frame/contracts/src/migration.rs @@ -263,10 +263,10 @@ impl Migration { impl OnRuntimeUpgrade for Migration { fn on_runtime_upgrade() -> Weight { let name = >::name(); - let current_version = >::current_storage_version(); + let in_code_version = >::in_code_storage_version(); let on_chain_version = >::on_chain_storage_version(); - if on_chain_version == current_version { + if on_chain_version == in_code_version { log::warn!( target: LOG_TARGET, "{name}: No Migration performed storage_version = latest_version = {:?}", @@ -289,7 +289,7 @@ impl OnRuntimeUpgrade for Migration OnRuntimeUpgrade for Migration>::on_chain_storage_version(); - let current_version = >::current_storage_version(); + let in_code_version = >::in_code_storage_version(); - if on_chain_version == current_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 {:?}(current storage version)", - >::name(), on_chain_version, current_version + "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, current_version), - "Unsupported upgrade: VERSION_RANGE should be (on-chain storage version + 1, current storage version)" + 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()) @@ -421,7 +421,7 @@ impl Migration { }, StepResult::Completed { steps_done } => { in_progress_version.put::>(); - if >::current_storage_version() != in_progress_version { + if >::in_code_storage_version() != in_progress_version { log::info!( target: LOG_TARGET, "{name}: Next migration is {:?},", diff --git a/substrate/frame/contracts/src/migration/v09.rs b/substrate/frame/contracts/src/migration/v09.rs index 98fcccc2c0becedccd4bd15eed98b36fd52662ee..f19bff9d6747f8359558e2c2d5a289fad668bfaa 100644 --- a/substrate/frame/contracts/src/migration/v09.rs +++ b/substrate/frame/contracts/src/migration/v09.rs @@ -28,7 +28,7 @@ use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound, Identity}; use sp_runtime::TryRuntimeError; use sp_std::prelude::*; -mod old { +mod v8 { use super::*; #[derive(Encode, Decode)] @@ -50,14 +50,14 @@ mod old { #[cfg(feature = "runtime-benchmarks")] pub fn store_old_dummy_code(len: usize) { use sp_runtime::traits::Hash; - let module = old::PrefabWasmModule { + let module = v8::PrefabWasmModule { instruction_weights_version: 0, initial: 0, maximum: 0, code: vec![42u8; len], }; let hash = T::Hashing::hash(&module.code); - old::CodeStorage::::insert(hash, module); + v8::CodeStorage::::insert(hash, module); } #[derive(Encode, Decode)] @@ -89,9 +89,9 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_key) = self.last_code_hash.take() { - old::CodeStorage::::iter_from(old::CodeStorage::::hashed_key_for(last_key)) + v8::CodeStorage::::iter_from(v8::CodeStorage::::hashed_key_for(last_key)) } else { - old::CodeStorage::::iter() + v8::CodeStorage::::iter() }; if let Some((key, old)) = iter.next() { @@ -115,7 +115,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let sample: Vec<_> = old::CodeStorage::::iter().take(100).collect(); + let sample: Vec<_> = v8::CodeStorage::::iter().take(100).collect(); log::debug!(target: LOG_TARGET, "Taking sample of {} contract codes", sample.len()); Ok(sample.encode()) @@ -123,7 +123,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { - let sample = , old::PrefabWasmModule)> as Decode>::decode(&mut &state[..]) + let sample = , v8::PrefabWasmModule)> as Decode>::decode(&mut &state[..]) .expect("pre_upgrade_step provides a valid state; qed"); log::debug!(target: LOG_TARGET, "Validating sample of {} contract codes", sample.len()); diff --git a/substrate/frame/contracts/src/migration/v10.rs b/substrate/frame/contracts/src/migration/v10.rs index 22fad38739e751c9b68dfb3151751e08331f5218..1bee86e6a773a9b0d7720438644a4ae84d9bd332 100644 --- a/substrate/frame/contracts/src/migration/v10.rs +++ b/substrate/frame/contracts/src/migration/v10.rs @@ -25,7 +25,10 @@ use crate::{ CodeHash, Config, Pallet, TrieId, Weight, LOG_TARGET, }; use codec::{Decode, Encode}; -use core::cmp::{max, min}; +use core::{ + cmp::{max, min}, + ops::Deref, +}; use frame_support::{ pallet_prelude::*, storage_alias, @@ -42,9 +45,9 @@ use sp_runtime::{ traits::{Hash, TrailingZeroInput, Zero}, Perbill, Saturating, }; -use sp_std::{ops::Deref, prelude::*}; +use sp_std::prelude::*; -mod old { +mod v9 { use super::*; pub type BalanceOf = ( ) where OldCurrency: ReservableCurrency<::AccountId> + 'static, { - let info = old::ContractInfo { + let info = v9::ContractInfo { trie_id: info.trie_id, code_hash: info.code_hash, storage_bytes: Default::default(), @@ -91,7 +94,7 @@ pub fn store_old_contract_info( storage_item_deposit: Default::default(), storage_base_deposit: Default::default(), }; - old::ContractInfoOf::::insert(account, info); + v9::ContractInfoOf::::insert(account, info); } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)] @@ -117,9 +120,9 @@ where pub code_hash: CodeHash, storage_bytes: u32, storage_items: u32, - pub storage_byte_deposit: old::BalanceOf, - storage_item_deposit: old::BalanceOf, - storage_base_deposit: old::BalanceOf, + pub storage_byte_deposit: v9::BalanceOf, + storage_item_deposit: v9::BalanceOf, + storage_base_deposit: v9::BalanceOf, } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] @@ -149,7 +152,7 @@ fn deposit_address( impl MigrationStep for Migration where OldCurrency: ReservableCurrency<::AccountId> - + Inspect<::AccountId, Balance = old::BalanceOf>, + + Inspect<::AccountId, Balance = v9::BalanceOf>, { const VERSION: u16 = 10; @@ -159,11 +162,11 @@ where fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_account) = self.last_account.take() { - old::ContractInfoOf::::iter_from( - old::ContractInfoOf::::hashed_key_for(last_account), + v9::ContractInfoOf::::iter_from( + v9::ContractInfoOf::::hashed_key_for(last_account), ) } else { - old::ContractInfoOf::::iter() + v9::ContractInfoOf::::iter() }; if let Some((account, contract)) = iter.next() { @@ -273,7 +276,7 @@ where #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let sample: Vec<_> = old::ContractInfoOf::::iter().take(10).collect(); + let sample: Vec<_> = v9::ContractInfoOf::::iter().take(10).collect(); log::debug!(target: LOG_TARGET, "Taking sample of {} contracts", sample.len()); Ok(sample.encode()) @@ -281,7 +284,7 @@ where #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { - let sample = )> as Decode>::decode( + let sample = )> as Decode>::decode( &mut &state[..], ) .expect("pre_upgrade_step provides a valid state; qed"); diff --git a/substrate/frame/contracts/src/migration/v11.rs b/substrate/frame/contracts/src/migration/v11.rs index a5b11f6e08977fbbeb737bc2650ad318ddac97c8..9bfbb25edfb44a8072d2d0073ec4cdb4d27cc360 100644 --- a/substrate/frame/contracts/src/migration/v11.rs +++ b/substrate/frame/contracts/src/migration/v11.rs @@ -29,7 +29,7 @@ use sp_runtime::TryRuntimeError; use codec::{Decode, Encode}; use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound}; use sp_std::{marker::PhantomData, prelude::*}; -mod old { +mod v10 { use super::*; #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)] @@ -51,11 +51,11 @@ pub struct DeletionQueueManager { #[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime"))] pub fn fill_old_queue(len: usize) { - let queue: Vec = - core::iter::repeat_with(|| old::DeletedContract { trie_id: Default::default() }) + let queue: Vec = + core::iter::repeat_with(|| v10::DeletedContract { trie_id: Default::default() }) .take(len) .collect(); - old::DeletionQueue::::set(Some(queue)); + v10::DeletionQueue::::set(Some(queue)); } #[storage_alias] @@ -80,7 +80,7 @@ impl MigrationStep for Migration { } fn step(&mut self) -> (IsFinished, Weight) { - let Some(old_queue) = old::DeletionQueue::::take() else { + let Some(old_queue) = v10::DeletionQueue::::take() else { return (IsFinished::Yes, Weight::zero()) }; let len = old_queue.len(); @@ -106,7 +106,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let old_queue = old::DeletionQueue::::take().unwrap_or_default(); + let old_queue = v10::DeletionQueue::::take().unwrap_or_default(); if old_queue.is_empty() { let len = 10u32; diff --git a/substrate/frame/contracts/src/migration/v12.rs b/substrate/frame/contracts/src/migration/v12.rs index 7dee31503101ba753b0e807d11621be711d4b58b..d9128286df389f84b451660e8d00f4a886b629d4 100644 --- a/substrate/frame/contracts/src/migration/v12.rs +++ b/substrate/frame/contracts/src/migration/v12.rs @@ -34,7 +34,7 @@ use sp_runtime::TryRuntimeError; use sp_runtime::{traits::Zero, FixedPointNumber, FixedU128, Saturating}; use sp_std::prelude::*; -mod old { +mod v11 { use super::*; pub type BalanceOf = , #[codec(compact)] - deposit: old::BalanceOf, + deposit: v11::BalanceOf, #[codec(compact)] refcount: u64, determinism: Determinism, @@ -112,17 +112,17 @@ where let hash = T::Hashing::hash(&code); PristineCode::::insert(hash, code.clone()); - let module = old::PrefabWasmModule { + let module = v11::PrefabWasmModule { instruction_weights_version: Default::default(), initial: Default::default(), maximum: Default::default(), code, determinism: Determinism::Enforced, }; - old::CodeStorage::::insert(hash, module); + v11::CodeStorage::::insert(hash, module); - let info = old::OwnerInfo { owner: account, deposit: u32::MAX.into(), refcount: u64::MAX }; - old::OwnerInfoOf::::insert(hash, info); + let info = v11::OwnerInfo { owner: account, deposit: u32::MAX.into(), refcount: u64::MAX }; + v11::OwnerInfoOf::::insert(hash, info); } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] @@ -148,16 +148,16 @@ where fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_key) = self.last_code_hash.take() { - old::OwnerInfoOf::::iter_from( - old::OwnerInfoOf::::hashed_key_for(last_key), + v11::OwnerInfoOf::::iter_from( + v11::OwnerInfoOf::::hashed_key_for(last_key), ) } else { - old::OwnerInfoOf::::iter() + v11::OwnerInfoOf::::iter() }; if let Some((hash, old_info)) = iter.next() { log::debug!(target: LOG_TARGET, "Migrating OwnerInfo for code_hash {:?}", hash); - let module = old::CodeStorage::::take(hash) + let module = v11::CodeStorage::::take(hash) .expect(format!("No PrefabWasmModule found for code_hash: {:?}", hash).as_str()); let code_len = module.code.len(); @@ -184,7 +184,7 @@ where let bytes_before = module .encoded_size() .saturating_add(code_len) - .saturating_add(old::OwnerInfo::::max_encoded_len()) + .saturating_add(v11::OwnerInfo::::max_encoded_len()) as u32; let items_before = 3u32; let deposit_expected_before = price_per_byte @@ -241,10 +241,10 @@ where fn pre_upgrade_step() -> Result, TryRuntimeError> { let len = 100; log::debug!(target: LOG_TARGET, "Taking sample of {} OwnerInfo(s)", len); - let sample: Vec<_> = old::OwnerInfoOf::::iter() + let sample: Vec<_> = v11::OwnerInfoOf::::iter() .take(len) .map(|(k, v)| { - let module = old::CodeStorage::::get(k) + let module = v11::CodeStorage::::get(k) .expect("No PrefabWasmModule found for code_hash: {:?}"); let info: CodeInfo = CodeInfo { determinism: module.determinism, @@ -258,9 +258,9 @@ where .collect(); let storage: u32 = - old::CodeStorage::::iter().map(|(_k, v)| v.encoded_size() as u32).sum(); - let mut deposit: old::BalanceOf = Default::default(); - old::OwnerInfoOf::::iter().for_each(|(_k, v)| deposit += v.deposit); + v11::CodeStorage::::iter().map(|(_k, v)| v.encoded_size() as u32).sum(); + let mut deposit: v11::BalanceOf = Default::default(); + v11::OwnerInfoOf::::iter().for_each(|(_k, v)| deposit += v.deposit); Ok((sample, deposit, storage).encode()) } @@ -269,7 +269,7 @@ where fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { let state = <( Vec<(CodeHash, CodeInfo)>, - old::BalanceOf, + v11::BalanceOf, u32, ) as Decode>::decode(&mut &state[..]) .unwrap(); @@ -283,7 +283,7 @@ where ensure!(info.refcount == old.refcount, "invalid refcount"); } - if let Some((k, _)) = old::CodeStorage::::iter().next() { + if let Some((k, _)) = v11::CodeStorage::::iter().next() { log::warn!( target: LOG_TARGET, "CodeStorage is still NOT empty, found code_hash: {:?}", @@ -292,7 +292,7 @@ where } else { log::debug!(target: LOG_TARGET, "CodeStorage is empty."); } - if let Some((k, _)) = old::OwnerInfoOf::::iter().next() { + if let Some((k, _)) = v11::OwnerInfoOf::::iter().next() { log::warn!( target: LOG_TARGET, "OwnerInfoOf is still NOT empty, found code_hash: {:?}", @@ -302,7 +302,7 @@ where log::debug!(target: LOG_TARGET, "OwnerInfoOf is empty."); } - let mut deposit: old::BalanceOf = Default::default(); + let mut deposit: v11::BalanceOf = Default::default(); let mut items = 0u32; let mut storage_info = 0u32; CodeInfoOf::::iter().for_each(|(_k, v)| { diff --git a/substrate/frame/contracts/src/migration/v13.rs b/substrate/frame/contracts/src/migration/v13.rs index dd2eb12eb62a5daf92cec8b5910c03f6d81ae0c0..498c44d53abce9730e9833fcbd27e1b2dbb2fe38 100644 --- a/substrate/frame/contracts/src/migration/v13.rs +++ b/substrate/frame/contracts/src/migration/v13.rs @@ -28,7 +28,7 @@ use frame_support::{pallet_prelude::*, storage_alias, DefaultNoBound}; use sp_runtime::BoundedBTreeMap; use sp_std::prelude::*; -mod old { +mod v12 { use super::*; #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] @@ -59,7 +59,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co let entropy = (b"contract_depo_v1", account.clone()).using_encoded(T::Hashing::hash); let deposit_account = Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) .expect("infinite length input; no invalid inputs for type; qed"); - let info = old::ContractInfo { + let info = v12::ContractInfo { trie_id: info.trie_id.clone(), deposit_account, code_hash: info.code_hash, @@ -69,7 +69,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co storage_item_deposit: Default::default(), storage_base_deposit: Default::default(), }; - old::ContractInfoOf::::insert(account, info); + v12::ContractInfoOf::::insert(account, info); } #[storage_alias] @@ -104,11 +104,11 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_account) = self.last_account.take() { - old::ContractInfoOf::::iter_from(old::ContractInfoOf::::hashed_key_for( + v12::ContractInfoOf::::iter_from(v12::ContractInfoOf::::hashed_key_for( last_account, )) } else { - old::ContractInfoOf::::iter() + v12::ContractInfoOf::::iter() }; if let Some((key, old)) = iter.next() { diff --git a/substrate/frame/contracts/src/migration/v14.rs b/substrate/frame/contracts/src/migration/v14.rs index 94534d05fdf889d05227149efd57ede584ecb013..09da09e5bcfb1f9ab4396a651e67519a75e09df2 100644 --- a/substrate/frame/contracts/src/migration/v14.rs +++ b/substrate/frame/contracts/src/migration/v14.rs @@ -44,7 +44,7 @@ use sp_runtime::{traits::Zero, Saturating}; #[cfg(feature = "try-runtime")] use sp_std::collections::btree_map::BTreeMap; -mod old { +mod v13 { use super::*; pub type BalanceOf = , #[codec(compact)] - pub deposit: old::BalanceOf, + pub deposit: v13::BalanceOf, #[codec(compact)] pub refcount: u64, pub determinism: Determinism, @@ -86,14 +86,14 @@ where let code = vec![42u8; len as usize]; let hash = T::Hashing::hash(&code); - let info = old::CodeInfo { + let info = v13::CodeInfo { owner: account, deposit: 10_000u32.into(), refcount: u64::MAX, determinism: Determinism::Enforced, code_len: len, }; - old::CodeInfoOf::::insert(hash, info); + v13::CodeInfoOf::::insert(hash, info); } #[cfg(feature = "try-runtime")] @@ -105,9 +105,9 @@ where OldCurrency: ReservableCurrency<::AccountId>, { /// Total reserved balance as code upload deposit for the owner. - reserved: old::BalanceOf, + reserved: v13::BalanceOf, /// Total balance of the owner. - total: old::BalanceOf, + total: v13::BalanceOf, } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] @@ -134,11 +134,11 @@ where fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_hash) = self.last_code_hash.take() { - old::CodeInfoOf::::iter_from( - old::CodeInfoOf::::hashed_key_for(last_hash), + v13::CodeInfoOf::::iter_from( + v13::CodeInfoOf::::hashed_key_for(last_hash), ) } else { - old::CodeInfoOf::::iter() + v13::CodeInfoOf::::iter() }; if let Some((hash, code_info)) = iter.next() { @@ -194,7 +194,7 @@ where #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let info: Vec<_> = old::CodeInfoOf::::iter().collect(); + let info: Vec<_> = v13::CodeInfoOf::::iter().collect(); let mut owner_balance_allocation = BTreeMap::, BalanceAllocation>::new(); diff --git a/substrate/frame/contracts/src/migration/v15.rs b/substrate/frame/contracts/src/migration/v15.rs index 180fe855ca66728ba18b17555363eeac11a340ba..c77198d6fea0f6b7c305d40b981330cbb50e355d 100644 --- a/substrate/frame/contracts/src/migration/v15.rs +++ b/substrate/frame/contracts/src/migration/v15.rs @@ -46,7 +46,7 @@ use sp_runtime::{traits::Zero, Saturating}; #[cfg(feature = "try-runtime")] use sp_std::vec::Vec; -mod old { +mod v14 { use super::*; #[derive( @@ -81,7 +81,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co let entropy = (b"contract_depo_v1", account.clone()).using_encoded(T::Hashing::hash); let deposit_account = Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) .expect("infinite length input; no invalid inputs for type; qed"); - let info = old::ContractInfo { + let info = v14::ContractInfo { trie_id: info.trie_id.clone(), deposit_account, code_hash: info.code_hash, @@ -92,7 +92,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co storage_base_deposit: info.storage_base_deposit(), delegate_dependencies: info.delegate_dependencies().clone(), }; - old::ContractInfoOf::::insert(account, info); + v14::ContractInfoOf::::insert(account, info); } #[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] @@ -127,11 +127,11 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_account) = self.last_account.take() { - old::ContractInfoOf::::iter_from(old::ContractInfoOf::::hashed_key_for( + v14::ContractInfoOf::::iter_from(v14::ContractInfoOf::::hashed_key_for( last_account, )) } else { - old::ContractInfoOf::::iter() + v14::ContractInfoOf::::iter() }; if let Some((account, old_contract)) = iter.next() { @@ -243,11 +243,11 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let sample: Vec<_> = old::ContractInfoOf::::iter().take(100).collect(); + let sample: Vec<_> = v14::ContractInfoOf::::iter().take(100).collect(); log::debug!(target: LOG_TARGET, "Taking sample of {} contracts", sample.len()); - let state: Vec<(T::AccountId, old::ContractInfo, BalanceOf, BalanceOf)> = sample + let state: Vec<(T::AccountId, v14::ContractInfo, BalanceOf, BalanceOf)> = sample .iter() .map(|(account, contract)| { ( @@ -265,7 +265,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { let sample = - , BalanceOf, BalanceOf)> as Decode>::decode( + , BalanceOf, BalanceOf)> as Decode>::decode( &mut &state[..], ) .expect("pre_upgrade_step provides a valid state; qed"); diff --git a/substrate/frame/contracts/src/schedule.rs b/substrate/frame/contracts/src/schedule.rs index 9fa5217da43f8469804f4f47f4834e433eb39569..b2e3801deaec1a36aef589427a6bf9dd324183b8 100644 --- a/substrate/frame/contracts/src/schedule.rs +++ b/substrate/frame/contracts/src/schedule.rs @@ -21,13 +21,11 @@ use crate::{weights::WeightInfo, Config}; use codec::{Decode, Encode}; +use core::marker::PhantomData; use frame_support::{weights::Weight, DefaultNoBound}; -use pallet_contracts_proc_macro::{ScheduleDebug, WeightDebug}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use sp_runtime::RuntimeDebug; -use sp_std::marker::PhantomData; /// Definition of the cost schedule and other parameterizations for the wasm vm. /// @@ -41,11 +39,7 @@ use sp_std::marker::PhantomData; /// fn create_schedule() -> Schedule { /// Schedule { /// limits: Limits { -/// globals: 3, -/// parameters: 3, /// memory_pages: 16, -/// table_size: 3, -/// br_table_size: 3, /// .. Default::default() /// }, /// instruction_weights: InstructionWeights { @@ -57,7 +51,8 @@ use sp_std::marker::PhantomData; /// ``` #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))] -#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug, DefaultNoBound, TypeInfo)] +#[cfg_attr(feature = "runtime-benchmarks", derive(frame_support::DebugNoBound))] +#[derive(Clone, Encode, Decode, PartialEq, Eq, DefaultNoBound, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct Schedule { /// Describes the upper limits on various metrics. @@ -72,43 +67,15 @@ pub struct Schedule { /// Describes the upper limits on various metrics. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "runtime-benchmarks", derive(Debug))] +#[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] pub struct Limits { /// The maximum number of topics supported by an event. pub event_topics: u32, - /// Maximum number of globals a module is allowed to declare. - /// - /// Globals are not limited through the linear memory limit `memory_pages`. - pub globals: u32, - - /// Maximum number of locals a function can have. - /// - /// As wasm engine initializes each of the local, we need to limit their number to confine - /// execution costs. - pub locals: u32, - - /// Maximum numbers of parameters a function can have. - /// - /// Those need to be limited to prevent a potentially exploitable interaction with - /// the stack height instrumentation: The costs of executing the stack height - /// instrumentation for an indirectly called function scales linearly with the amount - /// of parameters of this function. Because the stack height instrumentation itself is - /// is not weight metered its costs must be static (via this limit) and included in - /// the costs of the instructions that cause them (call, call_indirect). - pub parameters: u32, - /// Maximum number of memory pages allowed for a contract. pub memory_pages: u32, - /// Maximum number of elements allowed in a table. - /// - /// Currently, the only type of element that is allowed in a table is funcref. - pub table_size: u32, - - /// Maximum number of elements that can appear as immediate value to the br_table instruction. - pub br_table_size: u32, - /// The maximum length of a subject in bytes used for PRNG generation. pub subject_len: u32, @@ -130,7 +97,8 @@ impl Limits { /// Gas metering of Wasm executed instructions is being done on the engine side. /// This struct holds a reference value used to gas units scaling between host and engine. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug, TypeInfo)] +#[cfg_attr(feature = "runtime-benchmarks", derive(frame_support::DebugNoBound))] +#[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct InstructionWeights { /// Base instruction `ref_time` Weight. @@ -143,7 +111,8 @@ pub struct InstructionWeights { /// Describes the weight for each imported function that a contract is allowed to call. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug, TypeInfo)] +#[cfg_attr(feature = "runtime-benchmarks", derive(pallet_contracts_proc_macro::WeightDebug))] +#[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct HostFnWeights { /// Weight of calling `seal_caller`. @@ -329,11 +298,11 @@ pub struct HostFnWeights { /// Weight of calling `instantiation_nonce`. pub instantiation_nonce: Weight, - /// Weight of calling `add_delegate_dependency`. - pub add_delegate_dependency: Weight, + /// Weight of calling `lock_delegate_dependency`. + pub lock_delegate_dependency: Weight, - /// Weight of calling `remove_delegate_dependency`. - pub remove_delegate_dependency: Weight, + /// Weight of calling `unlock_delegate_dependency`. + pub unlock_delegate_dependency: Weight, /// The type parameter is used in the default implementation. #[codec(skip)] @@ -368,13 +337,7 @@ impl Default for Limits { fn default() -> Self { Self { event_topics: 4, - globals: 256, - locals: 1024, - parameters: 128, memory_pages: 16, - // 4k function pointers (This is in count not bytes). - table_size: 4096, - br_table_size: 256, subject_len: 32, payload_len: 16 * 1024, runtime_memory: 1024 * 1024 * 128, @@ -472,21 +435,9 @@ impl Default for HostFnWeights { reentrance_count: cost!(seal_reentrance_count), account_reentrance_count: cost!(seal_account_reentrance_count), instantiation_nonce: cost!(seal_instantiation_nonce), - add_delegate_dependency: cost!(add_delegate_dependency), - remove_delegate_dependency: cost!(remove_delegate_dependency), + lock_delegate_dependency: cost!(lock_delegate_dependency), + unlock_delegate_dependency: cost!(unlock_delegate_dependency), _phantom: PhantomData, } } } - -#[cfg(test)] -mod test { - use super::*; - use crate::tests::Test; - - #[test] - fn print_test_schedule() { - let schedule = Schedule::::default(); - println!("{:#?}", schedule); - } -} diff --git a/substrate/frame/contracts/src/storage.rs b/substrate/frame/contracts/src/storage.rs index d4d261f4ec1ffcfc68c5245864b00ea2f47928ed..e99afd5d42ee4684adc874d68bdba325feb58358 100644 --- a/substrate/frame/contracts/src/storage.rs +++ b/substrate/frame/contracts/src/storage.rs @@ -66,7 +66,7 @@ pub struct ContractInfo { storage_base_deposit: BalanceOf, /// Map of code hashes and deposit balances. /// - /// Tracks the code hash and deposit held for adding delegate dependencies. Dependencies added + /// 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: BoundedBTreeMap, BalanceOf, T::MaxDelegateDependencies>, @@ -108,6 +108,11 @@ impl ContractInfo { 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()) @@ -233,7 +238,7 @@ impl ContractInfo { /// /// Returns an error if the maximum number of delegate_dependencies is reached or if /// the delegate dependency already exists. - pub fn add_delegate_dependency( + pub fn lock_delegate_dependency( &mut self, code_hash: CodeHash, amount: BalanceOf, @@ -249,7 +254,7 @@ impl ContractInfo { /// dependency. /// /// Returns an error if the entry doesn't exist. - pub fn remove_delegate_dependency( + pub fn unlock_delegate_dependency( &mut self, code_hash: &CodeHash, ) -> Result, DispatchError> { @@ -322,7 +327,8 @@ impl ContractInfo { KillStorageResult::SomeRemaining(_) => return weight_limit, KillStorageResult::AllRemoved(keys_removed) => { entry.remove(); - remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed); + // charge at least one key even if none were removed. + remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1)); }, }; } diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index db438f7ff6dd10d56a1ecf7f888898eac220e7bd..4c1f65e28d5f73e04db8e1ffd301e12f9fe0c8f9 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -35,16 +35,17 @@ use crate::{ tests::test_utils::{get_contract, get_contract_checked}, wasm::{Determinism, ReturnErrorCode as RuntimeReturnCode}, weights::WeightInfo, - BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, - DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, + Array, BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, + ContractInfoOf, DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, MigrationInProgress, Origin, Pallet, PristineCode, Schedule, }; use assert_matches::assert_matches; -use codec::Encode; +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::{ @@ -217,8 +218,6 @@ impl ChainExtension for TestExtension { where E: Ext, { - use codec::Decode; - let func_id = env.func_id(); let id = env.ext_id() as u32 | func_id as u32; match func_id { @@ -334,29 +333,10 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; type AccountId = AccountId32; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } impl pallet_insecure_randomness_collective_flip::Config for Test {} impl pallet_balances::Config for Test { @@ -373,7 +353,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = ConstU32<1>; } impl pallet_timestamp::Config for Test { @@ -456,6 +435,33 @@ impl Contains for TestFilter { } } +parameter_types! { + pub static UploadAccount: Option<::AccountId> = None; + pub static InstantiateAccount: Option<::AccountId> = None; +} + +pub struct EnsureAccount(sp_std::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; } @@ -480,6 +486,8 @@ impl Config for Test { type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = UnstableInterface; + type UploadOrigin = EnsureAccount; + type InstantiateOrigin = EnsureAccount; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type RuntimeHoldReason = RuntimeHoldReason; type Migrations = crate::migration::codegen::BenchMigrations; @@ -487,6 +495,7 @@ impl Config for Test { type MaxDelegateDependencies = MaxDelegateDependencies; type Debug = TestDebug; type Environment = (); + type ApiVersion = (); type Xcm = (); } @@ -843,27 +852,6 @@ fn deposit_event_max_value_limit() { }); } -// Fail out of fuel (ref_time weight) inside the start function. -#[test] -fn run_out_of_fuel_start_fun() { - let (wasm, _code_hash) = compile_module::("run_out_of_gas_start_fn").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = ::Currency::set_balance(&ALICE, 1_000_000); - assert_err_ignore_postinfo!( - Contracts::instantiate_with_code( - RuntimeOrigin::signed(ALICE), - 0, - Weight::from_parts(1_000_000_000_000, u64::MAX), - None, - wasm, - vec![], - vec![], - ), - Error::::OutOfGas, - ); - }); -} - // Fail out of fuel (ref_time weight) in the engine. #[test] fn run_out_of_fuel_engine() { @@ -947,50 +935,15 @@ fn run_out_of_fuel_host() { #[test] fn gas_syncs_work() { - let (wasm0, _code_hash) = compile_module::("seal_input_noop").unwrap(); - let (wasm1, _code_hash) = compile_module::("seal_input_once").unwrap(); - let (wasm2, _code_hash) = compile_module::("seal_input_twice").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); - // Instantiate noop contract. - let addr0 = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm0), - vec![], - vec![], - DebugInfo::Skip, - CollectEvents::Skip, - ) - .result - .unwrap() - .account_id; - - // Instantiate 1st contract. - let addr1 = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm1), - vec![], - vec![], - DebugInfo::Skip, - CollectEvents::Skip, - ) - .result - .unwrap() - .account_id; - - // Instantiate 2nd contract. - let addr2 = Contracts::bare_instantiate( + let addr = Contracts::bare_instantiate( ALICE, 0, GAS_LIMIT, None, - Code::Upload(wasm2), + Code::Upload(code), vec![], vec![], DebugInfo::Skip, @@ -1002,11 +955,11 @@ fn gas_syncs_work() { let result = Contracts::bare_call( ALICE, - addr0, + addr.clone(), 0, GAS_LIMIT, None, - 1u8.to_le_bytes().to_vec(), + 0u32.encode(), DebugInfo::Skip, CollectEvents::Skip, Determinism::Enforced, @@ -1016,27 +969,28 @@ fn gas_syncs_work() { let result = Contracts::bare_call( ALICE, - addr1, + addr.clone(), 0, GAS_LIMIT, None, - 1u8.to_le_bytes().to_vec(), + 1u32.encode(), DebugInfo::Skip, CollectEvents::Skip, Determinism::Enforced, ); assert_ok!(result.result); let gas_consumed_once = result.gas_consumed.ref_time(); - let host_consumed_once = ::Schedule::get().host_fn_weights.input.ref_time(); + let host_consumed_once = + ::Schedule::get().host_fn_weights.caller_is_origin.ref_time(); let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop; let result = Contracts::bare_call( ALICE, - addr2, + addr, 0, GAS_LIMIT, None, - 1u8.to_le_bytes().to_vec(), + 2u32.encode(), DebugInfo::Skip, CollectEvents::Skip, Determinism::Enforced, @@ -2925,12 +2879,13 @@ fn debug_message_invalid_utf8() { } #[test] -fn gas_estimation_nested_call_fixed_limit() { +fn gas_estimation_for_subcalls() { let (caller_code, _caller_hash) = compile_module::("call_with_limit").unwrap(); - let (callee_code, _callee_hash) = compile_module::("dummy").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, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 2_000 * min_balance); let addr_caller = Contracts::bare_instantiate( ALICE, @@ -2939,7 +2894,7 @@ fn gas_estimation_nested_call_fixed_limit() { None, Code::Upload(caller_code), vec![], - vec![0], + vec![], DebugInfo::Skip, CollectEvents::Skip, ) @@ -2947,14 +2902,14 @@ fn gas_estimation_nested_call_fixed_limit() { .unwrap() .account_id; - let addr_callee = Contracts::bare_instantiate( + let addr_dummy = Contracts::bare_instantiate( ALICE, min_balance * 100, GAS_LIMIT, None, - Code::Upload(callee_code), + Code::Upload(dummy_code), + vec![], vec![], - vec![1], DebugInfo::Skip, CollectEvents::Skip, ) @@ -2962,68 +2917,136 @@ fn gas_estimation_nested_call_fixed_limit() { .unwrap() .account_id; - let input: Vec = AsRef::<[u8]>::as_ref(&addr_callee) - .iter() - .cloned() - .chain((GAS_LIMIT / 5).ref_time().to_le_bytes()) - .chain((GAS_LIMIT / 5).proof_size().to_le_bytes()) - .collect(); - - // Call in order to determine the gas that is required for this call - let result = Contracts::bare_call( + let addr_call_runtime = Contracts::bare_instantiate( ALICE, - addr_caller.clone(), - 0, + min_balance * 100, GAS_LIMIT, None, - input.clone(), + Code::Upload(call_runtime_code), + vec![], + vec![], DebugInfo::Skip, CollectEvents::Skip, - Determinism::Enforced, - ); - assert_ok!(&result.result); + ) + .result + .unwrap() + .account_id; - // We have a subcall with a fixed gas limit. This constitutes precharging. - assert!(result.gas_required.all_gt(result.gas_consumed)); + // 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), + ]; - // Make the same call using the estimated gas. Should succeed. - assert_ok!( - Contracts::bare_call( - ALICE, - addr_caller.clone(), - 0, - result.gas_required, - Some(result.storage_deposit.charge_or_zero()), - input.clone(), - DebugInfo::Skip, - CollectEvents::Skip, - Determinism::Enforced, - ) - .result - ); + // 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(); - // Make the same call using proof_size but less than estimated. Should fail with OutOfGas. - let result = Contracts::bare_call( - ALICE, - addr_caller, - 0, - result.gas_required.sub_proof_size(1), - Some(result.storage_deposit.charge_or_zero()), - input, - DebugInfo::Skip, - CollectEvents::Skip, - Determinism::Enforced, - ) - .result; - assert_err!(result, >::OutOfGas); + // 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 = Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + GAS_LIMIT, + None, + input.clone(), + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ); + assert_ok!(&result.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.gas_required.all_gt(result.gas_consumed)); + >::ContractTrapped.into() + } else { + assert_eq!(result.gas_required, result.gas_consumed); + >::OutOfGas.into() + }; + + // Make the same call using the estimated gas. Should succeed. + assert_ok!( + Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + result.gas_required, + Some(result.storage_deposit.charge_or_zero()), + input.clone(), + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ) + .result + ); + + // Check that it fails with too little ref_time + assert_err!( + Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + result.gas_required.sub_ref_time(1), + Some(result.storage_deposit.charge_or_zero()), + input.clone(), + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ) + .result, + error, + ); + + // Check that it fails with too little proof_size + assert_err!( + Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + result.gas_required.sub_proof_size(1), + Some(result.storage_deposit.charge_or_zero()), + input, + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ) + .result, + error, + ); + } + } }); } #[test] fn gas_estimation_call_runtime() { - use codec::Decode; 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); @@ -3044,25 +3067,11 @@ fn gas_estimation_call_runtime() { .unwrap() .account_id; - Contracts::bare_instantiate( - ALICE, - min_balance * 100, - GAS_LIMIT, - None, - Code::Upload(callee_code), - vec![], - vec![1], - DebugInfo::Skip, - CollectEvents::Skip, - ) - .result - .unwrap(); - // 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, 0), - actual_weight: Weight::from_parts(100, 0), + pre_charge: Weight::from_parts(10_000_000, 1_000), + actual_weight: Weight::from_parts(100, 100), }); let result = Contracts::bare_call( ALICE, @@ -3078,7 +3087,7 @@ fn gas_estimation_call_runtime() { // 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.ref_time() >= result.gas_consumed.ref_time()); + assert!(result.gas_required.all_gt(result.gas_consumed)); // Make the same call using the required gas. Should succeed. assert_ok!( @@ -4389,96 +4398,6 @@ fn contract_reverted() { }); } -#[test] -fn code_rejected_error_works() { - ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = ::Currency::set_balance(&ALICE, 1_000_000); - - let (wasm, _) = compile_module::("invalid_module").unwrap(); - assert_noop!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - wasm.clone(), - None, - Determinism::Enforced - ), - >::CodeRejected, - ); - let result = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm), - vec![], - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - ); - assert_err!(result.result, >::CodeRejected); - assert_eq!( - std::str::from_utf8(&result.debug_message).unwrap(), - "Can't load the module into wasmi!" - ); - - let (wasm, _) = compile_module::("invalid_contract_no_call").unwrap(); - assert_noop!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - wasm.clone(), - None, - Determinism::Enforced - ), - >::CodeRejected, - ); - - let result = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm), - vec![], - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - ); - assert_err!(result.result, >::CodeRejected); - assert_eq!( - std::str::from_utf8(&result.debug_message).unwrap(), - "call function isn't exported" - ); - - let (wasm, _) = compile_module::("invalid_contract_no_memory").unwrap(); - assert_noop!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - wasm.clone(), - None, - Determinism::Enforced - ), - >::CodeRejected, - ); - - let result = Contracts::bare_instantiate( - ALICE, - 0, - GAS_LIMIT, - None, - Code::Upload(wasm), - vec![], - vec![], - DebugInfo::UnsafeDebug, - CollectEvents::Skip, - ); - assert_err!(result.result, >::CodeRejected); - assert_eq!( - std::str::from_utf8(&result.debug_message).unwrap(), - "No memory import found in the module" - ); - }); -} - #[test] fn set_code_hash() { let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); @@ -5121,6 +5040,26 @@ fn deposit_limit_honors_min_leftover() { }); } +#[test] +fn upload_should_enforce_deterministic_mode_when_possible() { + let upload = |fixture, determinism| { + let (wasm, code_hash) = compile_module::(fixture).unwrap(); + ExtBuilder::default() + .build() + .execute_with(|| -> Result { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + Contracts::bare_upload_code(ALICE, wasm, None, determinism)?; + let info = CodeInfoOf::::get(code_hash).unwrap(); + Ok(info.determinism()) + }) + }; + + assert_eq!(upload("dummy", Determinism::Enforced), Ok(Determinism::Enforced)); + assert_eq!(upload("dummy", Determinism::Relaxed), Ok(Determinism::Enforced)); + assert_eq!(upload("float_instruction", Determinism::Relaxed), Ok(Determinism::Relaxed)); + assert!(upload("float_instruction", Determinism::Enforced).is_err()); +} + #[test] fn cannot_instantiate_indeterministic_code() { let (wasm, code_hash) = compile_module::("float_instruction").unwrap(); @@ -5373,21 +5312,21 @@ fn delegate_call_indeterministic_code() { } #[test] -fn add_remove_delegate_dependency_works() { +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)); MAX_DELEGATE_DEPENDENCIES.with(|c| *c.borrow_mut() = 1); let (wasm_caller, self_code_hash) = - compile_module::("add_remove_delegate_dependency").unwrap(); + compile_module::("locking_delegate_dependency").unwrap(); let (wasm_callee, code_hash) = compile_module::("dummy").unwrap(); let (wasm_other, other_code_hash) = compile_module::("call").unwrap(); - // Define inputs with various actions to test adding / removing delegate_dependencies. + // Define inputs with various actions to test locking / unlocking delegate_dependencies. // See the contract for more details. let noop_input = (0u32, code_hash); - let add_delegate_dependency_input = (1u32, code_hash); - let remove_delegate_dependency_input = (2u32, code_hash); + let lock_delegate_dependency_input = (1u32, code_hash); + let unlock_delegate_dependency_input = (2u32, code_hash); let terminate_input = (3u32, code_hash); // Instantiate the caller contract with the given input. @@ -5424,9 +5363,9 @@ fn add_remove_delegate_dependency_works() { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); - // Instantiate with add_delegate_dependency should fail since the code is not yet on chain. + // Instantiate with lock_delegate_dependency should fail since the code is not yet on chain. assert_err!( - instantiate(&add_delegate_dependency_input).result, + instantiate(&lock_delegate_dependency_input).result, Error::::CodeNotFound ); @@ -5436,7 +5375,7 @@ fn add_remove_delegate_dependency_works() { .unwrap(); // Instantiate should now work. - let addr_caller = instantiate(&add_delegate_dependency_input).result.unwrap().account_id; + 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); @@ -5457,27 +5396,27 @@ fn add_remove_delegate_dependency_works() { >::CodeInUse ); - // Adding an already existing dependency should fail. + // Locking an already existing dependency should fail. assert_err!( - call(&addr_caller, &add_delegate_dependency_input).result, + call(&addr_caller, &lock_delegate_dependency_input).result, Error::::DelegateDependencyAlreadyExists ); - // Adding a dependency to self should fail. + // Locking self should fail. assert_err!( call(&addr_caller, &(1u32, self_code_hash)).result, Error::::CannotAddSelfAsDelegateDependency ); - // Adding more than the maximum allowed delegate_dependencies should fail. + // Locking more than the maximum allowed delegate_dependencies should fail. Contracts::bare_upload_code(ALICE, wasm_other, None, Determinism::Enforced).unwrap(); assert_err!( call(&addr_caller, &(1u32, other_code_hash)).result, Error::::MaxDelegateDependenciesReached ); - // Removing dependency should work. - assert_ok!(call(&addr_caller, &remove_delegate_dependency_input).result); + // Unlocking dependency should work. + assert_ok!(call(&addr_caller, &unlock_delegate_dependency_input).result); // Dependency should be removed, and deposit should be returned. let contract = test_utils::get_contract(&addr_caller); @@ -5492,18 +5431,18 @@ fn add_remove_delegate_dependency_works() { // Removing an unexisting dependency should fail. assert_err!( - call(&addr_caller, &remove_delegate_dependency_input).result, + call(&addr_caller, &unlock_delegate_dependency_input).result, Error::::DelegateDependencyNotFound ); - // Adding a dependency with a storage limit too low should fail. + // Locking a dependency with a storage limit too low should fail. DEFAULT_DEPOSIT_LIMIT.with(|c| *c.borrow_mut() = dependency_deposit - 1); assert_err!( - call(&addr_caller, &add_delegate_dependency_input).result, + call(&addr_caller, &lock_delegate_dependency_input).result, Error::::StorageDepositLimitExhausted ); - // Since we removed the dependency we should now be able to remove the code. + // Since we unlocked the dependency we should now be able to remove the code. assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash)); // Calling should fail since the delegated contract is not on chain anymore. @@ -5512,7 +5451,7 @@ fn add_remove_delegate_dependency_works() { // Restore initial deposit limit and add the dependency back. DEFAULT_DEPOSIT_LIMIT.with(|c| *c.borrow_mut() = 10_000_000); Contracts::bare_upload_code(ALICE, wasm_callee, None, Determinism::Enforced).unwrap(); - call(&addr_caller, &add_delegate_dependency_input).result.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); @@ -5890,7 +5829,106 @@ fn root_cannot_instantiate() { vec![], vec![], ), - DispatchError::RootNotAllowed + 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(), + None, + Determinism::Enforced, + ), + DispatchError::BadOrigin + ); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(BOB), + wasm.clone(), + None, + Determinism::Enforced, + ), + DispatchError::BadOrigin + ); + + // Only alice is allowed to upload contract code. + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + None, + Determinism::Enforced, + )); + }); +} + +#[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!( + Contracts::instantiate_with_code( + RuntimeOrigin::root(), + 0, + GAS_LIMIT, + None, + code.clone(), + vec![], + vec![], + ), + DispatchError::BadOrigin + ); + + assert_err_ignore_postinfo!( + Contracts::instantiate_with_code( + RuntimeOrigin::signed(BOB), + 0, + GAS_LIMIT, + None, + code.clone(), + vec![], + vec![], + ), + DispatchError::BadOrigin + ); + + // Only Alice can instantiate + assert_ok!(Contracts::instantiate_with_code( + RuntimeOrigin::signed(ALICE), + 0, + GAS_LIMIT, + None, + code, + vec![], + vec![], + ),); + + // Bob cannot instantiate with either `instantiate_with_code` or `instantiate`. + assert_err_ignore_postinfo!( + Contracts::instantiate( + RuntimeOrigin::signed(BOB), + 0, + GAS_LIMIT, + None, + code_hash, + vec![], + vec![], + ), + DispatchError::BadOrigin ); }); } @@ -5944,3 +5982,53 @@ fn balance_api_returns_free_balance() { ); }); } + +#[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 = Contracts::bare_instantiate( + ALICE, + 0, + GAS_LIMIT, + None, + Code::Upload(code), + vec![], + vec![], + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .account_id; + + let max_call_depth = ::CallStack::size() as u32; + let [gas_0, gas_1, gas_2, gas_max] = { + [0u32, 1u32, 2u32, max_call_depth] + .iter() + .map(|i| { + let result = Contracts::bare_call( + ALICE, + addr.clone(), + 0, + GAS_LIMIT, + None, + i.encode(), + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ); + 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 * max_call_depth as u64); + }); +} diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs index 76ef524f3f960d2979f35bbdb8665de59b8cf5d3..8842b863ae309ab3d3e3e79b9d977bc463ca58b8 100644 --- a/substrate/frame/contracts/src/wasm/mod.rs +++ b/substrate/frame/contracts/src/wasm/mod.rs @@ -21,6 +21,9 @@ mod prepare; mod runtime; +#[cfg(test)] +pub use runtime::STABLE_API_COUNT; + #[cfg(doc)] pub use crate::wasm::runtime::api_doc; @@ -315,6 +318,11 @@ impl CodeInfo { } } + /// Returns the determinism of the module. + pub fn determinism(&self) -> Determinism { + self.determinism + } + /// Returns reference count of the module. pub fn refcount(&self) -> u64 { self.refcount @@ -389,7 +397,7 @@ impl Executable for WasmBlob { let engine_consumed_total = store.fuel_consumed().expect("Fuel metering is enabled; qed"); let gas_meter = store.data_mut().ext().gas_meter_mut(); - gas_meter.charge_fuel(engine_consumed_total)?; + let _ = gas_meter.sync_from_executor(engine_consumed_total)?; store.into_data().to_execution_result(result) }; @@ -690,6 +698,10 @@ mod tests { &mut self.gas_meter } fn charge_storage(&mut self, _diff: &crate::storage::meter::Diff) {} + + fn debug_buffer_enabled(&self) -> bool { + true + } fn append_debug_buffer(&mut self, msg: &str) -> bool { self.debug_buffer.extend(msg.as_bytes()); true @@ -732,14 +744,14 @@ mod tests { Ok(()) } fn decrement_refcount(_code_hash: CodeHash) {} - fn add_delegate_dependency( + fn lock_delegate_dependency( &mut self, code: CodeHash, ) -> Result<(), DispatchError> { self.delegate_dependencies.borrow_mut().insert(code); Ok(()) } - fn remove_delegate_dependency( + fn unlock_delegate_dependency( &mut self, code: &CodeHash, ) -> Result<(), DispatchError> { @@ -1820,17 +1832,6 @@ mod tests { assert!(weight_left.all_gt(actual_left), "gas_left must be greater than final"); } - /// Test that [`frame_support::weights::OldWeight`] en/decodes the same as our - /// [`crate::OldWeight`]. - #[test] - fn old_weight_decode() { - #![allow(deprecated)] - let sp = frame_support::weights::OldWeight(42).encode(); - let our = crate::OldWeight::decode(&mut &*sp).unwrap(); - - assert_eq!(our, 42); - } - const CODE_VALUE_TRANSFERRED: &str = r#" (module (import "seal0" "seal_value_transferred" (func $seal_value_transferred (param i32 i32))) @@ -3398,16 +3399,16 @@ mod tests { } #[test] - fn add_remove_delegate_dependency() { - const CODE_ADD_REMOVE_DELEGATE_DEPENDENCY: &str = r#" + fn lock_unlock_delegate_dependency() { + const CODE_LOCK_UNLOCK_DELEGATE_DEPENDENCY: &str = r#" (module - (import "seal0" "add_delegate_dependency" (func $add_delegate_dependency (param i32))) - (import "seal0" "remove_delegate_dependency" (func $remove_delegate_dependency (param i32))) + (import "seal0" "lock_delegate_dependency" (func $lock_delegate_dependency (param i32))) + (import "seal0" "unlock_delegate_dependency" (func $unlock_delegate_dependency (param i32))) (import "env" "memory" (memory 1 1)) (func (export "call") - (call $add_delegate_dependency (i32.const 0)) - (call $add_delegate_dependency (i32.const 32)) - (call $remove_delegate_dependency (i32.const 32)) + (call $lock_delegate_dependency (i32.const 0)) + (call $lock_delegate_dependency (i32.const 32)) + (call $unlock_delegate_dependency (i32.const 32)) ) (func (export "deploy")) @@ -3425,7 +3426,7 @@ mod tests { ) "#; let mut mock_ext = MockExt::default(); - assert_ok!(execute(&CODE_ADD_REMOVE_DELEGATE_DEPENDENCY, vec![], &mut mock_ext)); + assert_ok!(execute(&CODE_LOCK_UNLOCK_DELEGATE_DEPENDENCY, vec![], &mut mock_ext)); let delegate_dependencies: Vec<_> = mock_ext.delegate_dependencies.into_inner().into_iter().collect(); assert_eq!(delegate_dependencies.len(), 1); @@ -3447,4 +3448,22 @@ mod tests { runtime.read_sandbox_memory_as(&memory, 0u32).unwrap(); assert_eq!(decoded.into_inner(), data); } + + #[test] + fn run_out_of_gas_in_start_fn() { + const CODE: &str = r#" +(module + (import "env" "memory" (memory 1 1)) + (start $start) + (func $start + (loop $inf (br $inf)) ;; just run out of gas + (unreachable) + ) + (func (export "call")) + (func (export "deploy")) +) +"#; + let mut mock_ext = MockExt::default(); + assert_err!(execute(&CODE, vec![], &mut mock_ext), >::OutOfGas); + } } diff --git a/substrate/frame/contracts/src/wasm/prepare.rs b/substrate/frame/contracts/src/wasm/prepare.rs index 141365489942ce435f6f0f964a01de139aa3a84f..e0936795d9377e68ea551a464c1561838c7ba5c7 100644 --- a/substrate/frame/contracts/src/wasm/prepare.rs +++ b/substrate/frame/contracts/src/wasm/prepare.rs @@ -80,7 +80,10 @@ impl LoadedModule { } let engine = Engine::new(&config); - let module = Module::new(&engine, code).map_err(|_| "Can't load the module into wasmi!")?; + let module = Module::new(&engine, code).map_err(|err| { + log::debug!(target: LOG_TARGET, "Module creation failed: {:?}", err); + "Can't load the module into wasmi!" + })?; // Return a `LoadedModule` instance with // __valid__ module. @@ -221,7 +224,7 @@ impl LoadedModule { fn validate( code: &[u8], schedule: &Schedule, - determinism: Determinism, + determinism: &mut Determinism, ) -> Result<(), (DispatchError, &'static str)> where E: Environment<()>, @@ -230,8 +233,28 @@ where (|| { // We check that the module is generally valid, // and does not have restricted WebAssembly features, here. - let contract_module = - LoadedModule::new::(code, determinism, None, CompilationMode::Eager)?; + let contract_module = match *determinism { + Determinism::Relaxed => + if let Ok(module) = LoadedModule::new::( + code, + Determinism::Enforced, + None, + CompilationMode::Eager, + ) { + *determinism = Determinism::Enforced; + module + } else { + LoadedModule::new::( + code, + Determinism::Relaxed, + None, + CompilationMode::Eager, + )? + }, + Determinism::Enforced => + LoadedModule::new::(code, Determinism::Enforced, None, CompilationMode::Eager)?, + }; + // The we check that module satisfies constraints the pallet puts on contracts. contract_module.scan_exports()?; contract_module.scan_imports::(schedule)?; @@ -254,7 +277,7 @@ where &code, (), schedule, - determinism, + *determinism, stack_limits, AllowDeprecatedInterface::No, CompilationMode::Eager, @@ -279,13 +302,13 @@ pub fn prepare( code: CodeVec, schedule: &Schedule, owner: AccountIdOf, - determinism: Determinism, + mut determinism: Determinism, ) -> Result, (DispatchError, &'static str)> where E: Environment<()>, T: Config, { - validate::(code.as_ref(), schedule, determinism)?; + validate::(code.as_ref(), schedule, &mut determinism)?; // Calculate deposit for storing contract code and `code_info` in two different storage items. let code_len = code.len() as u32; @@ -388,12 +411,7 @@ mod tests { let wasm = wat::parse_str($wat).unwrap().try_into().unwrap(); let schedule = Schedule { limits: Limits { - globals: 3, - locals: 3, - parameters: 3, memory_pages: 16, - table_size: 3, - br_table_size: 3, .. Default::default() }, .. Default::default() diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 5e4ec78c1ee3b7cfb1a26e4af59c946c11dd5ccb..120efa9014936d5934a0dbfb98701a4ce78f0c63 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -21,7 +21,6 @@ use crate::{ exec::{ExecError, ExecResult, Ext, Key, TopicOf}, gas::{ChargedAmount, Token}, primitives::ExecReturnValue, - schedule::HostFnWeights, BalanceOf, CodeHash, Config, DebugBufferVec, Error, SENTINEL, }; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; @@ -235,6 +234,8 @@ pub enum RuntimeCosts { 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` @@ -245,16 +246,24 @@ pub enum RuntimeCosts { AccountEntranceCount, /// Weight of calling `instantiation_nonce` InstantationNonce, - /// Weight of calling `add_delegate_dependency` - AddDelegateDependency, - /// Weight of calling `remove_delegate_dependency` - RemoveDelegateDependency, + /// Weight of calling `lock_delegate_dependency` + LockDelegateDependency, + /// Weight of calling `unlock_delegate_dependency` + UnlockDelegateDependency, } -impl RuntimeCosts { - fn token(&self, s: &HostFnWeights) -> RuntimeToken { +impl Token for RuntimeCosts { + fn influence_lowest_gas_limit(&self) -> bool { + match self { + &Self::CallXcmExecute(_) => false, + _ => true, + } + } + + fn weight(&self) -> Weight { + let s = T::Schedule::get().host_fn_weights; use self::RuntimeCosts::*; - let weight = match *self { + match *self { CopyFromContract(len) => s.return_per_byte.saturating_mul(len.into()), CopyToContract(len) => s.input_per_byte.saturating_mul(len.into()), Caller => s.caller, @@ -323,20 +332,14 @@ impl RuntimeCosts { Sr25519Verify(len) => s .sr25519_verify .saturating_add(s.sr25519_verify_per_byte.saturating_mul(len.into())), - ChainExtension(weight) => weight, - CallRuntime(weight) => weight, + ChainExtension(weight) | CallRuntime(weight) | CallXcmExecute(weight) => weight, SetCodeHash => s.set_code_hash, EcdsaToEthAddress => s.ecdsa_to_eth_address, ReentrantCount => s.reentrance_count, AccountEntranceCount => s.account_reentrance_count, InstantationNonce => s.instantiation_nonce, - AddDelegateDependency => s.add_delegate_dependency, - RemoveDelegateDependency => s.remove_delegate_dependency, - }; - RuntimeToken { - #[cfg(test)] - _created_from: *self, - weight, + LockDelegateDependency => s.lock_delegate_dependency, + UnlockDelegateDependency => s.unlock_delegate_dependency, } } } @@ -347,25 +350,10 @@ impl RuntimeCosts { /// a function won't work out. macro_rules! charge_gas { ($runtime:expr, $costs:expr) => {{ - let token = $costs.token(&$runtime.ext.schedule().host_fn_weights); - $runtime.ext.gas_meter_mut().charge(token) + $runtime.ext.gas_meter_mut().charge($costs) }}; } -#[cfg_attr(test, derive(Debug, PartialEq, Eq))] -#[derive(Copy, Clone)] -struct RuntimeToken { - #[cfg(test)] - _created_from: RuntimeCosts, - weight: Weight, -} - -impl Token for RuntimeToken { - fn weight(&self) -> Weight { - self.weight - } -} - /// The kind of call that should be performed. enum CallType { /// Execute another instantiated contract @@ -510,28 +498,25 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { /// 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) { - let token = actual_costs.token(&self.ext.schedule().host_fn_weights); - self.ext.gas_meter_mut().adjust_gas(charged, token); + self.ext.gas_meter_mut().adjust_gas(charged, actual_costs); } /// Charge, Run and adjust gas, for executing the given dispatchable. - fn call_dispatchable< - ErrorReturnCode: Get, - F: FnOnce(&mut Self) -> DispatchResultWithPostInfo, - >( + fn call_dispatchable>( &mut self, dispatch_info: DispatchInfo, - run: F, + 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(RuntimeCosts::CallRuntime(dispatch_info.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, RuntimeCosts::CallRuntime(actual_weight)); + self.adjust_gas(charged, runtime_cost(actual_weight)); match result { Ok(_) => Ok(ReturnErrorCode::Success), Err(e) => { - if self.ext.append_debug_buffer("") { + if self.ext.debug_buffer_enabled() { self.ext.append_debug_buffer("call failed with: "); self.ext.append_debug_buffer(e.into()); }; @@ -1006,7 +991,6 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // for every function. #[define_env(doc)] pub mod env { - /// Set the value at the given key in the contract storage. /// See [`pallet_contracts_uapi::HostFn::set_storage`] #[prefixed_alias] @@ -1235,7 +1219,6 @@ pub mod env { /// Make a call to another contract. /// See [`pallet_contracts_uapi::HostFn::call_v2`]. #[version(2)] - #[unstable] fn call( ctx: _, memory: _, @@ -1372,7 +1355,6 @@ pub mod env { /// Instantiate a contract with the specified code hash. /// See [`pallet_contracts_uapi::HostFn::instantiate_v2`]. #[version(2)] - #[unstable] fn instantiate( ctx: _, memory: _, @@ -1546,7 +1528,6 @@ pub mod env { /// Checks whether the caller of the current contract is root. /// See [`pallet_contracts_uapi::HostFn::caller_is_root`]. - #[unstable] fn caller_is_root(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsRoot)?; Ok(ctx.ext.caller_is_root() as u32) @@ -2118,9 +2099,11 @@ pub mod env { ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; let call: ::RuntimeCall = ctx.read_sandbox_memory_as_unbounded(memory, call_ptr, call_len)?; - ctx.call_dispatchable::(call.get_dispatch_info(), |ctx| { - ctx.ext.call_runtime(call) - }) + ctx.call_dispatchable::( + call.get_dispatch_info(), + RuntimeCosts::CallRuntime, + |ctx| ctx.ext.call_runtime(call), + ) } /// Execute an XCM program locally, using the contract's address as the origin. @@ -2131,7 +2114,6 @@ pub mod env { memory: _, msg_ptr: u32, msg_len: u32, - output_ptr: u32, ) -> Result { use frame_support::dispatch::DispatchInfo; use xcm::VersionedXcm; @@ -2140,26 +2122,27 @@ pub mod env { ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; let message: VersionedXcm> = ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; + ensure_executable::(&message)?; let execute_weight = <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); let weight = ctx.ext.gas_meter().gas_left().max(execute_weight); let dispatch_info = DispatchInfo { weight, ..Default::default() }; - ensure_executable::(&message)?; - ctx.call_dispatchable::(dispatch_info, |ctx| { - let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); - let outcome = <::Xcm>::execute( - origin, - Box::new(message), - weight.saturating_sub(execute_weight), - )?; + ctx.call_dispatchable::( + dispatch_info, + RuntimeCosts::CallXcmExecute, + |ctx| { + let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); + let weight_used = <::Xcm>::execute( + origin, + Box::new(message), + weight.saturating_sub(execute_weight), + )?; - ctx.write_sandbox_memory(memory, output_ptr, &outcome.encode())?; - let pre_dispatch_weight = - <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); - Ok(Some(outcome.weight_used().saturating_add(pre_dispatch_weight)).into()) - }) + Ok(Some(weight_used.saturating_add(execute_weight)).into()) + }, + ) } /// Send an XCM program from the contract to the specified destination. @@ -2324,22 +2307,20 @@ pub mod env { } /// Adds a new delegate dependency to the contract. - /// See [`pallet_contracts_uapi::HostFn::add_delegate_dependency`]. - #[unstable] - fn add_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { - ctx.charge_gas(RuntimeCosts::AddDelegateDependency)?; + /// See [`pallet_contracts_uapi::HostFn::lock_delegate_dependency`]. + fn lock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { + ctx.charge_gas(RuntimeCosts::LockDelegateDependency)?; let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; - ctx.ext.add_delegate_dependency(code_hash)?; + ctx.ext.lock_delegate_dependency(code_hash)?; Ok(()) } /// Removes the delegate dependency from the contract. - /// see [`pallet_contracts_uapi::HostFn::remove_delegate_dependency`]. - #[unstable] - fn remove_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { - ctx.charge_gas(RuntimeCosts::RemoveDelegateDependency)?; + /// see [`pallet_contracts_uapi::HostFn::unlock_delegate_dependency`]. + fn unlock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { + ctx.charge_gas(RuntimeCosts::UnlockDelegateDependency)?; let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; - ctx.ext.remove_delegate_dependency(&code_hash)?; + ctx.ext.unlock_delegate_dependency(&code_hash)?; Ok(()) } } diff --git a/substrate/frame/contracts/src/weights.rs b/substrate/frame/contracts/src/weights.rs index a6cd4e03c9eeb94f1dc8184c67c1dbbbb7192806..6e09120c1413a28e456e9114dcc0cf3d6be1c781 100644 --- a/substrate/frame/contracts/src/weights.rs +++ b/substrate/frame/contracts/src/weights.rs @@ -17,10 +17,10 @@ //! Autogenerated weights for `pallet_contracts` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-p5qp1txx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -67,7 +67,8 @@ pub trait WeightInfo { fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight; fn instantiate(i: u32, s: u32, ) -> Weight; fn call() -> Weight; - fn upload_code(c: u32, ) -> 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 seal_caller(r: u32, ) -> Weight; @@ -124,8 +125,8 @@ pub trait WeightInfo { fn seal_ecdsa_recover(r: u32, ) -> Weight; fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight; fn seal_set_code_hash(r: u32, ) -> Weight; - fn add_delegate_dependency(r: u32, ) -> Weight; - fn remove_delegate_dependency(r: u32, ) -> Weight; + fn lock_delegate_dependency(r: u32, ) -> Weight; + fn unlock_delegate_dependency(r: u32, ) -> Weight; fn seal_reentrance_count(r: u32, ) -> Weight; fn seal_account_reentrance_count(r: u32, ) -> Weight; fn seal_instantiation_nonce(r: u32, ) -> Weight; @@ -141,8 +142,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 1_874_000 picoseconds. - Weight::from_parts(2_009_000, 1627) + // Minimum execution time: 2_054_000 picoseconds. + Weight::from_parts(2_178_000, 1627) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -152,10 +153,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `452 + k * (69 ±0)` // Estimated: `442 + k * (70 ±0)` - // Minimum execution time: 12_036_000 picoseconds. - Weight::from_parts(12_491_000, 442) - // Standard Error: 868 - .saturating_add(Weight::from_parts(1_105_983, 0).saturating_mul(k.into())) + // Minimum execution time: 12_119_000 picoseconds. + Weight::from_parts(12_536_000, 442) + // Standard Error: 1_254 + .saturating_add(Weight::from_parts(1_161_885, 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)) @@ -169,10 +170,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `211 + c * (1 ±0)` // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 8_095_000 picoseconds. - Weight::from_parts(8_657_690, 6149) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_197, 0).saturating_mul(c.into())) + // Minimum execution time: 8_146_000 picoseconds. + Weight::from_parts(8_560_745, 6149) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_182, 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())) @@ -185,8 +186,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `510` // Estimated: `6450` - // Minimum execution time: 16_735_000 picoseconds. - Weight::from_parts(17_412_000, 6450) + // Minimum execution time: 16_183_000 picoseconds. + Weight::from_parts(16_825_000, 6450) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -199,10 +200,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `171 + k * (1 ±0)` // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_443_000 picoseconds. - Weight::from_parts(3_573_000, 3635) - // Standard Error: 644 - .saturating_add(Weight::from_parts(1_233_677, 0).saturating_mul(k.into())) + // Minimum execution time: 3_645_000 picoseconds. + Weight::from_parts(604_365, 3635) + // Standard Error: 1_013 + .saturating_add(Weight::from_parts(1_100_277, 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()))) @@ -212,6 +213,8 @@ impl WeightInfo for SubstrateWeight { /// 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: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// 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) @@ -219,13 +222,13 @@ impl WeightInfo for SubstrateWeight { /// 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: 16_647_000 picoseconds. - Weight::from_parts(16_864_784, 6263) - // Standard Error: 0 - .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `328 + c * (1 ±0)` + // Estimated: `6266 + c * (1 ±0)` + // Minimum execution time: 19_500_000 picoseconds. + Weight::from_parts(20_074_895, 6266) + // Standard Error: 1 + .saturating_add(Weight::from_parts(422, 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())) } @@ -235,8 +238,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `440` // Estimated: `6380` - // Minimum execution time: 12_742_000 picoseconds. - Weight::from_parts(13_476_000, 6380) + // Minimum execution time: 12_530_000 picoseconds. + Weight::from_parts(13_362_000, 6380) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -245,13 +248,13 @@ impl WeightInfo for SubstrateWeight { /// 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(157), added: 2632, mode: `Measured`) + /// 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: 46_489_000 picoseconds. - Weight::from_parts(47_393_000, 6292) + // Minimum execution time: 44_275_000 picoseconds. + Weight::from_parts(45_718_000, 6292) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -263,8 +266,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `594` // Estimated: `6534` - // Minimum execution time: 53_480_000 picoseconds. - Weight::from_parts(57_484_000, 6534) + // Minimum execution time: 55_095_000 picoseconds. + Weight::from_parts(58_009_000, 6534) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -274,8 +277,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 2_488_000 picoseconds. - Weight::from_parts(2_594_000, 1627) + // Minimum execution time: 2_455_000 picoseconds. + Weight::from_parts(2_608_000, 1627) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -287,8 +290,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `166` // Estimated: `3631` - // Minimum execution time: 11_388_000 picoseconds. - Weight::from_parts(11_967_000, 3631) + // Minimum execution time: 11_207_000 picoseconds. + Weight::from_parts(11_802_000, 3631) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -298,8 +301,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 4_704_000 picoseconds. - Weight::from_parts(4_872_000, 3607) + // Minimum execution time: 4_581_000 picoseconds. + Weight::from_parts(4_781_000, 3607) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -310,8 +313,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `167` // Estimated: `3632` - // Minimum execution time: 5_947_000 picoseconds. - Weight::from_parts(6_396_000, 3632) + // Minimum execution time: 5_887_000 picoseconds. + Weight::from_parts(6_393_000, 3632) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -322,13 +325,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 5_997_000 picoseconds. - Weight::from_parts(6_343_000, 3607) + // Minimum execution time: 6_025_000 picoseconds. + Weight::from_parts(6_298_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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -344,22 +349,24 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `801 + c * (1 ±0)` - // Estimated: `6739 + c * (1 ±0)` - // Minimum execution time: 278_560_000 picoseconds. - Weight::from_parts(300_384_831, 6739) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_333, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `804 + c * (1 ±0)` + // Estimated: `9217 + c * (1 ±0)` + // Minimum execution time: 294_976_000 picoseconds. + Weight::from_parts(277_235_948, 9217) + // Standard Error: 65 + .saturating_add(Weight::from_parts(34_445, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(4_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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:3 w:3) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::Nonce` (r:1 w:1) @@ -377,17 +384,17 @@ impl WeightInfo for SubstrateWeight { /// 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: `8737` - // Minimum execution time: 4_026_879_000 picoseconds. - Weight::from_parts(463_062_002, 8737) - // Standard Error: 98 - .saturating_add(Weight::from_parts(93_133, 0).saturating_mul(c.into())) - // Standard Error: 11 - .saturating_add(Weight::from_parts(1_790, 0).saturating_mul(i.into())) - // Standard Error: 11 - .saturating_add(Weight::from_parts(1_739, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `326` + // Estimated: `8740` + // Minimum execution time: 3_864_558_000 picoseconds. + Weight::from_parts(421_676_889, 8740) + // Standard Error: 188 + .saturating_add(Weight::from_parts(103_788, 0).saturating_mul(c.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_715, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_774, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -396,6 +403,8 @@ impl WeightInfo for SubstrateWeight { /// 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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -407,24 +416,26 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// 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: `6504` - // Minimum execution time: 2_036_447_000 picoseconds. - Weight::from_parts(356_170_042, 6504) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_763, 0).saturating_mul(i.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_727, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(10_u64)) + // Measured: `563` + // Estimated: `8982` + // Minimum execution time: 2_069_276_000 picoseconds. + Weight::from_parts(377_210_279, 8982) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_719, 0).saturating_mul(i.into())) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_728, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(13_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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -439,33 +450,59 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `826` - // Estimated: `6766` - // Minimum execution time: 192_704_000 picoseconds. - Weight::from_parts(200_106_000, 6766) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `829` + // Estimated: `9244` + // Minimum execution time: 199_037_000 picoseconds. + Weight::from_parts(213_931_000, 9244) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, 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(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 275_168_000 picoseconds. - Weight::from_parts(311_726_799, 3607) - // Standard Error: 55 - .saturating_add(Weight::from_parts(89_926, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6085` + // Minimum execution time: 278_482_000 picoseconds. + Weight::from_parts(262_753_879, 6085) + // Standard Error: 112 + .saturating_add(Weight::from_parts(66_129, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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: `System::EventTopics` (r:1 w:1) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, 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: `145` + // Estimated: `6085` + // Minimum execution time: 286_902_000 picoseconds. + Weight::from_parts(269_003_959, 6085) + // Standard Error: 123 + .saturating_add(Weight::from_parts(66_629, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -473,7 +510,7 @@ impl WeightInfo for SubstrateWeight { /// 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(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:0 w:1) @@ -482,8 +519,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `315` // Estimated: `3780` - // Minimum execution time: 44_103_000 picoseconds. - Weight::from_parts(45_540_000, 3780) + // Minimum execution time: 44_128_000 picoseconds. + Weight::from_parts(46_044_000, 3780) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -499,8 +536,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `552` // Estimated: `8967` - // Minimum execution time: 33_871_000 picoseconds. - Weight::from_parts(34_859_000, 8967) + // Minimum execution time: 33_567_000 picoseconds. + Weight::from_parts(35_057_000, 8967) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -508,6 +545,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -521,13 +560,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `866 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 256_494_000 picoseconds. - Weight::from_parts(282_336_050, 6806) - // Standard Error: 614 - .saturating_add(Weight::from_parts(388_578, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `869 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 272_376_000 picoseconds. + Weight::from_parts(284_162_161, 9284) + // Standard Error: 501 + .saturating_add(Weight::from_parts(341_575, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -535,6 +574,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -548,13 +589,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `922 + r * (209 ±0)` - // Estimated: `6826 + r * (2684 ±0)` - // Minimum execution time: 267_047_000 picoseconds. - Weight::from_parts(136_857_840, 6826) - // Standard Error: 5_305 - .saturating_add(Weight::from_parts(3_749_573, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `925 + r * (209 ±0)` + // Estimated: `9304 + r * (2684 ±0)` + // Minimum execution time: 256_990_000 picoseconds. + Weight::from_parts(107_167_044, 9304) + // Standard Error: 7_545 + .saturating_add(Weight::from_parts(3_628_112, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2684).saturating_mul(r.into())) @@ -563,6 +604,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -576,13 +619,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (213 ±0)` - // Estimated: `6830 + r * (2688 ±0)` - // Minimum execution time: 257_198_000 picoseconds. - Weight::from_parts(122_900_097, 6830) - // Standard Error: 6_078 - .saturating_add(Weight::from_parts(4_637_847, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `924 + r * (213 ±0)` + // Estimated: `9308 + r * (2688 ±0)` + // Minimum execution time: 272_889_000 picoseconds. + Weight::from_parts(110_341_799, 9308) + // Standard Error: 7_881 + .saturating_add(Weight::from_parts(4_427_043, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2688).saturating_mul(r.into())) @@ -591,6 +634,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -604,13 +649,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (6 ±0)` - // Estimated: `6815 + r * (6 ±0)` - // Minimum execution time: 250_669_000 picoseconds. - Weight::from_parts(278_169_003, 6815) - // Standard Error: 948 - .saturating_add(Weight::from_parts(475_823, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `876 + r * (6 ±0)` + // Estimated: `9293 + r * (6 ±0)` + // Minimum execution time: 275_027_000 picoseconds. + Weight::from_parts(283_302_223, 9293) + // Standard Error: 1_179 + .saturating_add(Weight::from_parts(436_023, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -618,6 +663,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -631,18 +678,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 247_545_000 picoseconds. - Weight::from_parts(278_451_059, 6804) - // Standard Error: 415 - .saturating_add(Weight::from_parts(207_099, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 270_137_000 picoseconds. + Weight::from_parts(282_756_362, 9282) + // Standard Error: 384 + .saturating_add(Weight::from_parts(171_660, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -656,13 +705,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_root(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `753 + r * (3 ±0)` - // Estimated: `6693 + r * (3 ±0)` - // Minimum execution time: 237_427_000 picoseconds. - Weight::from_parts(266_803_438, 6693) - // Standard Error: 395 - .saturating_add(Weight::from_parts(187_185, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(7_u64)) + // Measured: `756 + r * (3 ±0)` + // Estimated: `9171 + r * (3 ±0)` + // Minimum execution time: 255_131_000 picoseconds. + Weight::from_parts(269_207_006, 9171) + // Standard Error: 542 + .saturating_add(Weight::from_parts(161_597, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -670,6 +719,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -683,13 +734,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `867 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 263_390_000 picoseconds. - Weight::from_parts(284_284_826, 6807) - // Standard Error: 710 - .saturating_add(Weight::from_parts(385_936, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `870 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 272_172_000 picoseconds. + Weight::from_parts(283_747_134, 9285) + // Standard Error: 885 + .saturating_add(Weight::from_parts(343_968, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -697,6 +748,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -710,13 +763,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 253_041_000 picoseconds. - Weight::from_parts(277_521_867, 6806) - // Standard Error: 807 - .saturating_add(Weight::from_parts(426_831, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 263_220_000 picoseconds. + Weight::from_parts(277_062_382, 9284) + // Standard Error: 1_783 + .saturating_add(Weight::from_parts(399_631, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -724,6 +777,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:2 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -737,13 +792,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1007 + r * (6 ±0)` - // Estimated: `6931 + r * (6 ±0)` - // Minimum execution time: 250_907_000 picoseconds. - Weight::from_parts(288_852_260, 6931) - // Standard Error: 32_981 - .saturating_add(Weight::from_parts(1_717_744, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1010 + r * (6 ±0)` + // Estimated: `9409 + r * (6 ±0)` + // Minimum execution time: 253_218_000 picoseconds. + Weight::from_parts(282_251_452, 9409) + // Standard Error: 2_367 + .saturating_add(Weight::from_parts(1_604_060, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -751,6 +806,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -764,13 +821,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (6 ±0)` - // Estimated: `6823 + r * (6 ±0)` - // Minimum execution time: 252_691_000 picoseconds. - Weight::from_parts(279_439_224, 6823) - // Standard Error: 682 - .saturating_add(Weight::from_parts(384_517, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `880 + r * (6 ±0)` + // Estimated: `9301 + r * (6 ±0)` + // Minimum execution time: 271_797_000 picoseconds. + Weight::from_parts(288_594_393, 9301) + // Standard Error: 846 + .saturating_add(Weight::from_parts(335_063, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -778,6 +835,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -791,13 +850,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (6 ±0)` - // Estimated: `6816 + r * (6 ±0)` - // Minimum execution time: 249_815_000 picoseconds. - Weight::from_parts(282_327_335, 6816) - // Standard Error: 655 - .saturating_add(Weight::from_parts(381_065, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `878 + r * (6 ±0)` + // Estimated: `9294 + r * (6 ±0)` + // Minimum execution time: 254_997_000 picoseconds. + Weight::from_parts(284_331_894, 9294) + // Standard Error: 885 + .saturating_add(Weight::from_parts(337_073, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -805,6 +864,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -818,13 +879,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (6 ±0)` - // Estimated: `6819 + r * (6 ±0)` - // Minimum execution time: 251_512_000 picoseconds. - Weight::from_parts(282_334_651, 6819) - // Standard Error: 894 - .saturating_add(Weight::from_parts(385_540, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `875 + r * (6 ±0)` + // Estimated: `9297 + r * (6 ±0)` + // Minimum execution time: 273_845_000 picoseconds. + Weight::from_parts(285_291_242, 9297) + // Standard Error: 690 + .saturating_add(Weight::from_parts(332_783, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -832,6 +893,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -845,13 +908,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6804 + r * (6 ±0)` - // Minimum execution time: 252_765_000 picoseconds. - Weight::from_parts(281_388_473, 6804) - // Standard Error: 733 - .saturating_add(Weight::from_parts(389_343, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9282 + r * (6 ±0)` + // Minimum execution time: 268_302_000 picoseconds. + Weight::from_parts(280_463_257, 9282) + // Standard Error: 1_035 + .saturating_add(Weight::from_parts(345_811, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -859,6 +922,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -874,13 +939,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `937 + r * (14 ±0)` - // Estimated: `6872 + r * (14 ±0)` - // Minimum execution time: 252_542_000 picoseconds. - Weight::from_parts(287_097_586, 6872) - // Standard Error: 843 - .saturating_add(Weight::from_parts(1_211_320, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `940 + r * (14 ±0)` + // Estimated: `9350 + r * (14 ±0)` + // Minimum execution time: 263_433_000 picoseconds. + Weight::from_parts(290_098_370, 9350) + // Standard Error: 1_905 + .saturating_add(Weight::from_parts(805_299, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 14).saturating_mul(r.into())) } @@ -888,6 +953,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -901,13 +968,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `865 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 251_332_000 picoseconds. - Weight::from_parts(279_047_507, 6807) - // Standard Error: 506 - .saturating_add(Weight::from_parts(338_066, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `868 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 273_588_000 picoseconds. + Weight::from_parts(287_133_565, 9285) + // Standard Error: 799 + .saturating_add(Weight::from_parts(254_114, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -915,6 +982,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -928,19 +997,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869` - // Estimated: `6809` - // Minimum execution time: 259_176_000 picoseconds. - Weight::from_parts(226_783_195, 6809) - // Standard Error: 22 - .saturating_add(Weight::from_parts(1_043, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `872` + // Estimated: `9287` + // Minimum execution time: 257_843_000 picoseconds. + Weight::from_parts(228_177_495, 9287) + // Standard Error: 24 + .saturating_add(Weight::from_parts(985, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -954,13 +1025,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `853 + r * (45 ±0)` - // Estimated: `6793 + r * (45 ±0)` - // Minimum execution time: 242_002_000 picoseconds. - Weight::from_parts(270_118_702, 6793) - // Standard Error: 901_814 - .saturating_add(Weight::from_parts(5_456_997, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `856 + r * (45 ±0)` + // Estimated: `9271 + r * (45 ±0)` + // Minimum execution time: 248_332_000 picoseconds. + Weight::from_parts(274_998_906, 9271) + // Standard Error: 949_561 + .saturating_add(Weight::from_parts(5_570_693, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) } @@ -968,6 +1039,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -981,19 +1054,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863` - // Estimated: `6810` - // Minimum execution time: 253_553_000 picoseconds. - Weight::from_parts(274_145_636, 6810) - // Standard Error: 0 - .saturating_add(Weight::from_parts(393, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866` + // Estimated: `9288` + // Minimum execution time: 272_055_000 picoseconds. + Weight::from_parts(282_280_090, 9288) + // Standard Error: 1 + .saturating_add(Weight::from_parts(330, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_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: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:1 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1007,28 +1082,30 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, 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 `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2972 + r * (316 ±0)` - // Estimated: `8912 + r * (5266 ±0)` - // Minimum execution time: 269_543_000 picoseconds. - Weight::from_parts(297_167_442, 8912) - // Standard Error: 816_260 - .saturating_add(Weight::from_parts(120_306_557, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(r.into()))) + // Measured: `2902 + r * (529 ±0)` + // Estimated: `11317 + r * (5479 ±0)` + // Minimum execution time: 274_842_000 picoseconds. + Weight::from_parts(299_824_497, 11317) + // Standard Error: 902_686 + .saturating_add(Weight::from_parts(106_562_902, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((10_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 5266).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 5479).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1044,13 +1121,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `944 + r * (10 ±0)` - // Estimated: `6885 + r * (10 ±0)` - // Minimum execution time: 254_432_000 picoseconds. - Weight::from_parts(280_280_937, 6885) - // Standard Error: 1_667 - .saturating_add(Weight::from_parts(1_321_779, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `947 + r * (10 ±0)` + // Estimated: `9363 + r * (10 ±0)` + // Minimum execution time: 255_393_000 picoseconds. + Weight::from_parts(309_814_338, 9363) + // Standard Error: 2_978 + .saturating_add(Weight::from_parts(1_203_507, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -1058,6 +1135,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1071,13 +1150,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (10 ±0)` - // Estimated: `6805 + r * (10 ±0)` - // Minimum execution time: 266_819_000 picoseconds. - Weight::from_parts(288_308_884, 6805) - // Standard Error: 837 - .saturating_add(Weight::from_parts(1_980_950, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `866 + r * (10 ±0)` + // Estimated: `9283 + r * (10 ±0)` + // Minimum execution time: 250_378_000 picoseconds. + Weight::from_parts(287_003_144, 9283) + // Standard Error: 2_726 + .saturating_add(Weight::from_parts(1_850_967, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -1085,6 +1164,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1099,15 +1180,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `880 + t * (32 ±0)` - // Estimated: `6825 + t * (2508 ±0)` - // Minimum execution time: 266_947_000 picoseconds. - Weight::from_parts(281_286_473, 6825) - // Standard Error: 94_213 - .saturating_add(Weight::from_parts(3_770_905, 0).saturating_mul(t.into())) - // Standard Error: 26 - .saturating_add(Weight::from_parts(503, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `883 + t * (32 ±0)` + // Estimated: `9303 + t * (2508 ±0)` + // Minimum execution time: 266_787_000 picoseconds. + Weight::from_parts(284_368_661, 9303) + // Standard Error: 121_315 + .saturating_add(Weight::from_parts(2_690_211, 0).saturating_mul(t.into())) + // Standard Error: 33 + .saturating_add(Weight::from_parts(789, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -1117,6 +1198,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1130,13 +1213,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (7 ±0)` - // Estimated: `6807 + r * (7 ±0)` - // Minimum execution time: 155_353_000 picoseconds. - Weight::from_parts(172_423_459, 6807) - // Standard Error: 374 - .saturating_add(Weight::from_parts(299_934, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `865 + r * (7 ±0)` + // Estimated: `9285 + r * (7 ±0)` + // Minimum execution time: 166_804_000 picoseconds. + Weight::from_parts(175_118_291, 9285) + // Standard Error: 398 + .saturating_add(Weight::from_parts(220_961, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) } @@ -1144,6 +1227,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -1157,13 +1242,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 1048576]`. fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125813` - // Estimated: `131755` - // Minimum execution time: 418_146_000 picoseconds. - Weight::from_parts(392_022_620, 131755) + // Measured: `125816` + // Estimated: `131758` + // Minimum execution time: 398_436_000 picoseconds. + Weight::from_parts(385_003_285, 131758) // Standard Error: 12 - .saturating_add(Weight::from_parts(1_098, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_038, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1171,13 +1256,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `924 + r * (292 ±0)` - // Estimated: `926 + r * (293 ±0)` - // Minimum execution time: 246_236_000 picoseconds. - Weight::from_parts(185_383_364, 926) - // Standard Error: 9_561 - .saturating_add(Weight::from_parts(6_465_948, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `927 + r * (292 ±0)` + // Estimated: `929 + r * (293 ±0)` + // Minimum execution time: 274_099_000 picoseconds. + Weight::from_parts(174_282_817, 929) + // Standard Error: 10_547 + .saturating_add(Weight::from_parts(6_262_173, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1188,13 +1273,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1447` - // Estimated: `1430` - // Minimum execution time: 266_282_000 picoseconds. - Weight::from_parts(335_348_736, 1430) - // Standard Error: 71 - .saturating_add(Weight::from_parts(722, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) + // Measured: `1450` + // Estimated: `1433` + // Minimum execution time: 274_061_000 picoseconds. + Weight::from_parts(334_755_333, 1433) + // Standard Error: 66 + .saturating_add(Weight::from_parts(771, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1202,11 +1287,11 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1253 + n * (1 ±0)` - // Estimated: `1253 + n * (1 ±0)` - // Minimum execution time: 267_258_000 picoseconds. - Weight::from_parts(294_884_737, 1253) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1256 + n * (1 ±0)` + // Estimated: `1256 + n * (1 ±0)` + // Minimum execution time: 272_657_000 picoseconds. + Weight::from_parts(299_061_594, 1256) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1215,13 +1300,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (288 ±0)` - // Estimated: `927 + r * (289 ±0)` - // Minimum execution time: 249_393_000 picoseconds. - Weight::from_parts(189_185_750, 927) - // Standard Error: 9_002 - .saturating_add(Weight::from_parts(6_314_214, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `924 + r * (288 ±0)` + // Estimated: `930 + r * (289 ±0)` + // Minimum execution time: 270_524_000 picoseconds. + Weight::from_parts(177_959_667, 930) + // Standard Error: 10_397 + .saturating_add(Weight::from_parts(6_270_181, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1232,11 +1317,11 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_clear_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1249 + n * (1 ±0)` - // Estimated: `1249 + n * (1 ±0)` - // Minimum execution time: 268_399_000 picoseconds. - Weight::from_parts(296_233_783, 1249) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1252 + n * (1 ±0)` + // Estimated: `1252 + n * (1 ±0)` + // Minimum execution time: 270_757_000 picoseconds. + Weight::from_parts(298_627_896, 1252) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1245,13 +1330,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (296 ±0)` - // Estimated: `923 + r * (297 ±0)` - // Minimum execution time: 248_415_000 picoseconds. - Weight::from_parts(206_980_176, 923) - // Standard Error: 7_237 - .saturating_add(Weight::from_parts(5_326_052, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `924 + r * (296 ±0)` + // Estimated: `926 + r * (297 ±0)` + // Minimum execution time: 268_082_000 picoseconds. + Weight::from_parts(202_583_730, 926) + // Standard Error: 9_029 + .saturating_add(Weight::from_parts(5_028_517, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) @@ -1261,13 +1346,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_get_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1265 + n * (1 ±0)` - // Estimated: `1265 + n * (1 ±0)` - // Minimum execution time: 269_414_000 picoseconds. - Weight::from_parts(292_066_705, 1265) - // Standard Error: 32 - .saturating_add(Weight::from_parts(948, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1268 + n * (1 ±0)` + // Estimated: `1268 + n * (1 ±0)` + // Minimum execution time: 274_100_000 picoseconds. + Weight::from_parts(296_981_515, 1268) + // Standard Error: 34 + .saturating_add(Weight::from_parts(578, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1276,13 +1361,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `932 + r * (288 ±0)` - // Estimated: `929 + r * (289 ±0)` - // Minimum execution time: 264_510_000 picoseconds. - Weight::from_parts(207_899_194, 929) - // Standard Error: 7_461 - .saturating_add(Weight::from_parts(5_040_079, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `935 + r * (288 ±0)` + // Estimated: `932 + r * (289 ±0)` + // Minimum execution time: 269_697_000 picoseconds. + Weight::from_parts(198_346_516, 932) + // Standard Error: 8_623 + .saturating_add(Weight::from_parts(4_964_116, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) @@ -1292,13 +1377,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_contains_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1252 + n * (1 ±0)` - // Estimated: `1252 + n * (1 ±0)` - // Minimum execution time: 265_740_000 picoseconds. - Weight::from_parts(289_814_750, 1252) - // Standard Error: 33 - .saturating_add(Weight::from_parts(182, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1255 + n * (1 ±0)` + // Estimated: `1255 + n * (1 ±0)` + // Minimum execution time: 264_210_000 picoseconds. + Weight::from_parts(291_387_652, 1255) + // Standard Error: 36 + .saturating_add(Weight::from_parts(524, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1307,13 +1392,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `914 + r * (296 ±0)` - // Estimated: `919 + r * (297 ±0)` - // Minimum execution time: 254_987_000 picoseconds. - Weight::from_parts(182_376_044, 919) - // Standard Error: 9_569 - .saturating_add(Weight::from_parts(6_656_191, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `917 + r * (296 ±0)` + // Estimated: `922 + r * (297 ±0)` + // Minimum execution time: 267_219_000 picoseconds. + Weight::from_parts(175_866_982, 922) + // Standard Error: 11_275 + .saturating_add(Weight::from_parts(6_424_755, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1324,13 +1409,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 16384]`. fn seal_take_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1266 + n * (1 ±0)` - // Estimated: `1266 + n * (1 ±0)` - // Minimum execution time: 272_780_000 picoseconds. - Weight::from_parts(299_681_117, 1266) - // Standard Error: 30 - .saturating_add(Weight::from_parts(713, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1269 + n * (1 ±0)` + // Estimated: `1269 + n * (1 ±0)` + // Minimum execution time: 274_634_000 picoseconds. + Weight::from_parts(294_272_009, 1269) + // Standard Error: 36 + .saturating_add(Weight::from_parts(1_219, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1338,6 +1423,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1602 w:1601) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1351,13 +1438,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1415 + r * (45 ±0)` - // Estimated: `7307 + r * (2520 ±0)` - // Minimum execution time: 268_642_000 picoseconds. - Weight::from_parts(205_633_613, 7307) - // Standard Error: 26_597 - .saturating_add(Weight::from_parts(32_775_614, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1418 + r * (45 ±0)` + // Estimated: `9785 + r * (2520 ±0)` + // Minimum execution time: 266_833_000 picoseconds. + Weight::from_parts(186_049_741, 9785) + // Standard Error: 40_311 + .saturating_add(Weight::from_parts(31_365_585, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1367,6 +1454,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -1380,13 +1469,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1260 + r * (245 ±0)` - // Estimated: `9440 + r * (2721 ±0)` - // Minimum execution time: 260_911_000 picoseconds. - Weight::from_parts(274_255_000, 9440) - // Standard Error: 213_855 - .saturating_add(Weight::from_parts(239_435_012, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `1263 + r * (245 ±0)` + // Estimated: `9635 + r * (2721 ±0)` + // Minimum execution time: 272_140_000 picoseconds. + Weight::from_parts(275_009_000, 9635) + // Standard Error: 99_187 + .saturating_add(Weight::from_parts(240_702_479, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -1396,6 +1485,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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:736 w:0) @@ -1410,12 +1501,12 @@ impl WeightInfo for SubstrateWeight { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (576 ±0)` - // Estimated: `6812 + r * (2637 ±3)` - // Minimum execution time: 259_239_000 picoseconds. - Weight::from_parts(266_900_000, 6812) - // Standard Error: 136_573 - .saturating_add(Weight::from_parts(238_160_538, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Estimated: `9290 + r * (2637 ±10)` + // Minimum execution time: 263_664_000 picoseconds. + Weight::from_parts(277_240_000, 9290) + // Standard Error: 169_147 + .saturating_add(Weight::from_parts(240_084_929, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1425,6 +1516,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -1439,15 +1532,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1307 + t * (277 ±0)` - // Estimated: `12197 + t * (5227 ±0)` - // Minimum execution time: 458_122_000 picoseconds. - Weight::from_parts(91_568_476, 12197) - // Standard Error: 11_267_459 - .saturating_add(Weight::from_parts(339_871_716, 0).saturating_mul(t.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_026, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(13_u64)) + // Measured: `1310 + t * (277 ±0)` + // Estimated: `12200 + t * (5227 ±0)` + // Minimum execution time: 464_644_000 picoseconds. + Weight::from_parts(40_377_313, 12200) + // Standard Error: 12_060_226 + .saturating_add(Weight::from_parts(380_635_982, 0).saturating_mul(t.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_014, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) @@ -1457,6 +1550,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:802 w:802) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:801 w:800) @@ -1470,17 +1565,17 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:803 w:803) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:800 w:800) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `r` is `[1, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1278 + r * (255 ±0)` - // Estimated: `9620 + r * (2731 ±0)` - // Minimum execution time: 618_921_000 picoseconds. - Weight::from_parts(638_593_000, 9620) - // Standard Error: 304_946 - .saturating_add(Weight::from_parts(357_251_226, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `1281 + r * (255 ±0)` + // Estimated: `9623 + r * (2731 ±0)` + // Minimum execution time: 641_845_000 picoseconds. + Weight::from_parts(649_908_000, 9623) + // Standard Error: 278_514 + .saturating_add(Weight::from_parts(361_164_598, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(7_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) @@ -1490,6 +1585,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:1) @@ -1503,21 +1600,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 983040]`. /// The range of component `s` is `[0, 983040]`. fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1303 + t * (104 ±0)` - // Estimated: `12211 + t * (2549 ±1)` - // Minimum execution time: 2_282_246_000 picoseconds. - Weight::from_parts(1_183_712_345, 12211) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_161, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_265, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(16_u64)) + // Measured: `1306 + t * (104 ±0)` + // Estimated: `12214 + t * (2549 ±1)` + // Minimum execution time: 2_111_632_000 picoseconds. + Weight::from_parts(1_213_125_745, 12214) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_055, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_192, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(11_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -1527,6 +1624,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1540,13 +1639,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (8 ±0)` - // Estimated: `6801 + r * (8 ±0)` - // Minimum execution time: 247_913_000 picoseconds. - Weight::from_parts(270_725_296, 6801) - // Standard Error: 1_042 - .saturating_add(Weight::from_parts(459_972, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `865 + r * (8 ±0)` + // Estimated: `9279 + r * (8 ±0)` + // Minimum execution time: 264_227_000 picoseconds. + Weight::from_parts(281_015_995, 9279) + // Standard Error: 710 + .saturating_add(Weight::from_parts(365_734, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1554,6 +1653,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1567,19 +1668,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `870` - // Estimated: `6808` - // Minimum execution time: 260_418_000 picoseconds. - Weight::from_parts(262_195_985, 6808) + // Measured: `873` + // Estimated: `9286` + // Minimum execution time: 270_709_000 picoseconds. + Weight::from_parts(268_416_051, 9286) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_121, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_080, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1593,13 +1696,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6806 + r * (8 ±0)` - // Minimum execution time: 249_692_000 picoseconds. - Weight::from_parts(281_644_768, 6806) - // Standard Error: 616 - .saturating_add(Weight::from_parts(849_731, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9284 + r * (8 ±0)` + // Minimum execution time: 267_283_000 picoseconds. + Weight::from_parts(280_706_659, 9284) + // Standard Error: 540 + .saturating_add(Weight::from_parts(790_312, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1607,6 +1710,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1620,19 +1725,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6814` - // Minimum execution time: 258_522_000 picoseconds. - Weight::from_parts(275_659_556, 6814) + // Measured: `875` + // Estimated: `9292` + // Minimum execution time: 262_010_000 picoseconds. + Weight::from_parts(273_278_744, 9292) // Standard Error: 1 - .saturating_add(Weight::from_parts(3_378, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(3_362, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1646,13 +1753,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6808 + r * (8 ±0)` - // Minimum execution time: 247_311_000 picoseconds. - Weight::from_parts(277_744_830, 6808) - // Standard Error: 556 - .saturating_add(Weight::from_parts(526_371, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9286 + r * (8 ±0)` + // Minimum execution time: 268_698_000 picoseconds. + Weight::from_parts(282_890_578, 9286) + // Standard Error: 622 + .saturating_add(Weight::from_parts(441_131, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1660,6 +1767,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1673,19 +1782,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6813` - // Minimum execution time: 270_091_000 picoseconds. - Weight::from_parts(268_084_237, 6813) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_231, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9291` + // Minimum execution time: 252_846_000 picoseconds. + Weight::from_parts(267_657_561, 9291) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_208, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1699,13 +1810,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6805 + r * (8 ±0)` - // Minimum execution time: 248_860_000 picoseconds. - Weight::from_parts(278_347_052, 6805) - // Standard Error: 465 - .saturating_add(Weight::from_parts(524_134, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9283 + r * (8 ±0)` + // Minimum execution time: 266_899_000 picoseconds. + Weight::from_parts(276_862_067, 9283) + // Standard Error: 1_249 + .saturating_add(Weight::from_parts(443_970, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -1713,6 +1824,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1726,19 +1839,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6811` - // Minimum execution time: 267_853_000 picoseconds. - Weight::from_parts(269_016_694, 6811) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_231, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9289` + // Minimum execution time: 265_413_000 picoseconds. + Weight::from_parts(265_600_840, 9289) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_203, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1752,13 +1867,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `997 + n * (1 ±0)` - // Estimated: `6934 + n * (1 ±0)` - // Minimum execution time: 311_197_000 picoseconds. - Weight::from_parts(337_829_093, 6934) - // Standard Error: 9 - .saturating_add(Weight::from_parts(5_969, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `1000 + n * (1 ±0)` + // Estimated: `9412 + n * (1 ±0)` + // Minimum execution time: 328_341_000 picoseconds. + Weight::from_parts(341_038_581, 9412) + // Standard Error: 10 + .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1766,6 +1881,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1779,13 +1896,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 160]`. fn seal_sr25519_verify(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `807 + r * (112 ±0)` - // Estimated: `6748 + r * (112 ±0)` - // Minimum execution time: 256_287_000 picoseconds. - Weight::from_parts(294_925_139, 6748) - // Standard Error: 37_103 - .saturating_add(Weight::from_parts(48_635_344, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `808 + r * (112 ±0)` + // Estimated: `9226 + r * (112 ±0)` + // Minimum execution time: 272_730_000 picoseconds. + Weight::from_parts(339_349_232, 9226) + // Standard Error: 13_004 + .saturating_add(Weight::from_parts(41_649_026, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) } @@ -1793,6 +1910,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1806,13 +1925,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `907 + r * (76 ±0)` - // Estimated: `6801 + r * (77 ±0)` - // Minimum execution time: 254_882_000 picoseconds. - Weight::from_parts(308_441_954, 6801) - // Standard Error: 10_967 - .saturating_add(Weight::from_parts(45_956_602, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `910 + r * (76 ±0)` + // Estimated: `9279 + r * (77 ±0)` + // Minimum execution time: 273_892_000 picoseconds. + Weight::from_parts(352_853_960, 9279) + // Standard Error: 16_745 + .saturating_add(Weight::from_parts(45_853_294, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) } @@ -1820,6 +1939,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1833,13 +1954,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (42 ±0)` - // Estimated: `6816 + r * (42 ±0)` - // Minimum execution time: 251_751_000 picoseconds. - Weight::from_parts(293_622_198, 6816) - // Standard Error: 6_442 - .saturating_add(Weight::from_parts(12_149_268, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `880 + r * (42 ±0)` + // Estimated: `9294 + r * (42 ±0)` + // Minimum execution time: 268_431_000 picoseconds. + Weight::from_parts(319_727_796, 9294) + // Standard Error: 10_276 + .saturating_add(Weight::from_parts(11_928_882, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) } @@ -1847,6 +1968,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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:1536 w:1536) @@ -1861,12 +1984,12 @@ impl WeightInfo for SubstrateWeight { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (965 ±0)` - // Estimated: `6807 + r * (3090 ±7)` - // Minimum execution time: 249_436_000 picoseconds. - Weight::from_parts(268_239_000, 6807) - // Standard Error: 42_505 - .saturating_add(Weight::from_parts(23_424_800, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Estimated: `9285 + r * (3090 ±7)` + // Minimum execution time: 275_482_000 picoseconds. + Weight::from_parts(279_155_000, 9285) + // Standard Error: 65_388 + .saturating_add(Weight::from_parts(26_634_187, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -1876,6 +1999,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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:33 w:32) @@ -1887,24 +2012,26 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 32]`. - fn add_delegate_dependency(r: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `928 + r * (131 ±0)` - // Estimated: `6878 + r * (2606 ±0)` - // Minimum execution time: 259_345_000 picoseconds. - Weight::from_parts(288_169_413, 6878) - // Standard Error: 22_399 - .saturating_add(Weight::from_parts(6_749_371, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + fn lock_delegate_dependency(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `937 + r * (131 ±0)` + // Estimated: `9346 + r * (2607 ±0)` + // Minimum execution time: 270_001_000 picoseconds. + Weight::from_parts(286_792_689, 9346) + // Standard Error: 21_074 + .saturating_add(Weight::from_parts(6_465_885, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2606).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2607).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:33 w:32) @@ -1916,15 +2043,15 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 32]`. - fn remove_delegate_dependency(r: u32, ) -> Weight { + fn unlock_delegate_dependency(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `969 + r * (183 ±0)` + // Measured: `972 + r * (184 ±0)` // Estimated: `129453 + r * (2568 ±0)` - // Minimum execution time: 257_547_000 picoseconds. - Weight::from_parts(287_042_678, 129453) - // Standard Error: 20_158 - .saturating_add(Weight::from_parts(5_960_974, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Minimum execution time: 270_652_000 picoseconds. + Weight::from_parts(293_369_452, 129453) + // Standard Error: 24_321 + .saturating_add(Weight::from_parts(5_575_600, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -1934,6 +2061,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1947,13 +2076,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `858 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 250_859_000 picoseconds. - Weight::from_parts(280_426_768, 6804) - // Standard Error: 415 - .saturating_add(Weight::from_parts(200_312, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `861 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 267_749_000 picoseconds. + Weight::from_parts(280_373_341, 9282) + // Standard Error: 464 + .saturating_add(Weight::from_parts(170_398, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -1961,6 +2090,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -1974,13 +2105,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2109 + r * (39 ±0)` - // Estimated: `7899 + r * (40 ±0)` - // Minimum execution time: 252_902_000 picoseconds. - Weight::from_parts(297_688_691, 7899) - // Standard Error: 1_589 - .saturating_add(Weight::from_parts(416_052, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) + // Measured: `2112 + r * (39 ±0)` + // Estimated: `10377 + r * (40 ±0)` + // Minimum execution time: 270_260_000 picoseconds. + Weight::from_parts(337_969_172, 10377) + // Standard Error: 1_557 + .saturating_add(Weight::from_parts(305_735, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } @@ -1988,6 +2119,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2003,13 +2136,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `861 + r * (3 ±0)` - // Estimated: `6801 + r * (3 ±0)` - // Minimum execution time: 256_614_000 picoseconds. - Weight::from_parts(274_483_287, 6801) - // Standard Error: 387 - .saturating_add(Weight::from_parts(186_165, 0).saturating_mul(r.into())) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `864 + r * (3 ±0)` + // Estimated: `9279 + r * (3 ±0)` + // Minimum execution time: 265_607_000 picoseconds. + Weight::from_parts(283_396_630, 9279) + // Standard Error: 449 + .saturating_add(Weight::from_parts(149_018, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -2018,10 +2151,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_463_000 picoseconds. - Weight::from_parts(2_810_154, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(6_083, 0).saturating_mul(r.into())) + // Minimum execution time: 1_813_000 picoseconds. + Weight::from_parts(1_264_945, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(15_098, 0).saturating_mul(r.into())) } } @@ -2033,8 +2166,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 1_874_000 picoseconds. - Weight::from_parts(2_009_000, 1627) + // Minimum execution time: 2_054_000 picoseconds. + Weight::from_parts(2_178_000, 1627) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -2044,10 +2177,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `452 + k * (69 ±0)` // Estimated: `442 + k * (70 ±0)` - // Minimum execution time: 12_036_000 picoseconds. - Weight::from_parts(12_491_000, 442) - // Standard Error: 868 - .saturating_add(Weight::from_parts(1_105_983, 0).saturating_mul(k.into())) + // Minimum execution time: 12_119_000 picoseconds. + Weight::from_parts(12_536_000, 442) + // Standard Error: 1_254 + .saturating_add(Weight::from_parts(1_161_885, 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)) @@ -2061,10 +2194,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `211 + c * (1 ±0)` // Estimated: `6149 + c * (1 ±0)` - // Minimum execution time: 8_095_000 picoseconds. - Weight::from_parts(8_657_690, 6149) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_197, 0).saturating_mul(c.into())) + // Minimum execution time: 8_146_000 picoseconds. + Weight::from_parts(8_560_745, 6149) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_182, 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())) @@ -2077,8 +2210,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `510` // Estimated: `6450` - // Minimum execution time: 16_735_000 picoseconds. - Weight::from_parts(17_412_000, 6450) + // Minimum execution time: 16_183_000 picoseconds. + Weight::from_parts(16_825_000, 6450) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2091,10 +2224,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `171 + k * (1 ±0)` // Estimated: `3635 + k * (1 ±0)` - // Minimum execution time: 3_443_000 picoseconds. - Weight::from_parts(3_573_000, 3635) - // Standard Error: 644 - .saturating_add(Weight::from_parts(1_233_677, 0).saturating_mul(k.into())) + // Minimum execution time: 3_645_000 picoseconds. + Weight::from_parts(604_365, 3635) + // Standard Error: 1_013 + .saturating_add(Weight::from_parts(1_100_277, 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()))) @@ -2104,6 +2237,8 @@ impl WeightInfo for () { /// 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: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// 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) @@ -2111,13 +2246,13 @@ impl WeightInfo for () { /// 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: 16_647_000 picoseconds. - Weight::from_parts(16_864_784, 6263) - // Standard Error: 0 - .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `328 + c * (1 ±0)` + // Estimated: `6266 + c * (1 ±0)` + // Minimum execution time: 19_500_000 picoseconds. + Weight::from_parts(20_074_895, 6266) + // Standard Error: 1 + .saturating_add(Weight::from_parts(422, 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())) } @@ -2127,8 +2262,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `440` // Estimated: `6380` - // Minimum execution time: 12_742_000 picoseconds. - Weight::from_parts(13_476_000, 6380) + // Minimum execution time: 12_530_000 picoseconds. + Weight::from_parts(13_362_000, 6380) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2137,13 +2272,13 @@ impl WeightInfo for () { /// 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(157), added: 2632, mode: `Measured`) + /// 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: 46_489_000 picoseconds. - Weight::from_parts(47_393_000, 6292) + // Minimum execution time: 44_275_000 picoseconds. + Weight::from_parts(45_718_000, 6292) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2155,8 +2290,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `594` // Estimated: `6534` - // Minimum execution time: 53_480_000 picoseconds. - Weight::from_parts(57_484_000, 6534) + // Minimum execution time: 55_095_000 picoseconds. + Weight::from_parts(58_009_000, 6534) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2166,8 +2301,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1627` - // Minimum execution time: 2_488_000 picoseconds. - Weight::from_parts(2_594_000, 1627) + // Minimum execution time: 2_455_000 picoseconds. + Weight::from_parts(2_608_000, 1627) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2179,8 +2314,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `166` // Estimated: `3631` - // Minimum execution time: 11_388_000 picoseconds. - Weight::from_parts(11_967_000, 3631) + // Minimum execution time: 11_207_000 picoseconds. + Weight::from_parts(11_802_000, 3631) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2190,8 +2325,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 4_704_000 picoseconds. - Weight::from_parts(4_872_000, 3607) + // Minimum execution time: 4_581_000 picoseconds. + Weight::from_parts(4_781_000, 3607) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -2202,8 +2337,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `167` // Estimated: `3632` - // Minimum execution time: 5_947_000 picoseconds. - Weight::from_parts(6_396_000, 3632) + // Minimum execution time: 5_887_000 picoseconds. + Weight::from_parts(6_393_000, 3632) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) @@ -2214,13 +2349,15 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3607` - // Minimum execution time: 5_997_000 picoseconds. - Weight::from_parts(6_343_000, 3607) + // Minimum execution time: 6_025_000 picoseconds. + Weight::from_parts(6_298_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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2236,22 +2373,24 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 125952]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `801 + c * (1 ±0)` - // Estimated: `6739 + c * (1 ±0)` - // Minimum execution time: 278_560_000 picoseconds. - Weight::from_parts(300_384_831, 6739) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_333, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `804 + c * (1 ±0)` + // Estimated: `9217 + c * (1 ±0)` + // Minimum execution time: 294_976_000 picoseconds. + Weight::from_parts(277_235_948, 9217) + // Standard Error: 65 + .saturating_add(Weight::from_parts(34_445, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(4_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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:3 w:3) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::Nonce` (r:1 w:1) @@ -2269,17 +2408,17 @@ impl WeightInfo for () { /// 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: `8737` - // Minimum execution time: 4_026_879_000 picoseconds. - Weight::from_parts(463_062_002, 8737) - // Standard Error: 98 - .saturating_add(Weight::from_parts(93_133, 0).saturating_mul(c.into())) - // Standard Error: 11 - .saturating_add(Weight::from_parts(1_790, 0).saturating_mul(i.into())) - // Standard Error: 11 - .saturating_add(Weight::from_parts(1_739, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `326` + // Estimated: `8740` + // Minimum execution time: 3_864_558_000 picoseconds. + Weight::from_parts(421_676_889, 8740) + // Standard Error: 188 + .saturating_add(Weight::from_parts(103_788, 0).saturating_mul(c.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_715, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_774, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -2288,6 +2427,8 @@ impl WeightInfo for () { /// 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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2299,24 +2440,26 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// 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: `6504` - // Minimum execution time: 2_036_447_000 picoseconds. - Weight::from_parts(356_170_042, 6504) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_763, 0).saturating_mul(i.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_727, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(10_u64)) + // Measured: `563` + // Estimated: `8982` + // Minimum execution time: 2_069_276_000 picoseconds. + Weight::from_parts(377_210_279, 8982) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_719, 0).saturating_mul(i.into())) + // Standard Error: 9 + .saturating_add(Weight::from_parts(1_728, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(13_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: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2331,33 +2474,59 @@ impl WeightInfo for () { /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `826` - // Estimated: `6766` - // Minimum execution time: 192_704_000 picoseconds. - Weight::from_parts(200_106_000, 6766) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `829` + // Estimated: `9244` + // Minimum execution time: 199_037_000 picoseconds. + Weight::from_parts(213_931_000, 9244) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, 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(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 275_168_000 picoseconds. - Weight::from_parts(311_726_799, 3607) - // Standard Error: 55 - .saturating_add(Weight::from_parts(89_926, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6085` + // Minimum execution time: 278_482_000 picoseconds. + Weight::from_parts(262_753_879, 6085) + // Standard Error: 112 + .saturating_add(Weight::from_parts(66_129, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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: `System::EventTopics` (r:1 w:1) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, 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: `145` + // Estimated: `6085` + // Minimum execution time: 286_902_000 picoseconds. + Weight::from_parts(269_003_959, 6085) + // Standard Error: 123 + .saturating_add(Weight::from_parts(66_629, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) @@ -2365,7 +2534,7 @@ impl WeightInfo for () { /// 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(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// Storage: `System::EventTopics` (r:1 w:1) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Contracts::PristineCode` (r:0 w:1) @@ -2374,8 +2543,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `315` // Estimated: `3780` - // Minimum execution time: 44_103_000 picoseconds. - Weight::from_parts(45_540_000, 3780) + // Minimum execution time: 44_128_000 picoseconds. + Weight::from_parts(46_044_000, 3780) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -2391,8 +2560,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `552` // Estimated: `8967` - // Minimum execution time: 33_871_000 picoseconds. - Weight::from_parts(34_859_000, 8967) + // Minimum execution time: 33_567_000 picoseconds. + Weight::from_parts(35_057_000, 8967) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -2400,6 +2569,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2413,13 +2584,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `866 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 256_494_000 picoseconds. - Weight::from_parts(282_336_050, 6806) - // Standard Error: 614 - .saturating_add(Weight::from_parts(388_578, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `869 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 272_376_000 picoseconds. + Weight::from_parts(284_162_161, 9284) + // Standard Error: 501 + .saturating_add(Weight::from_parts(341_575, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2427,6 +2598,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2440,13 +2613,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_is_contract(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `922 + r * (209 ±0)` - // Estimated: `6826 + r * (2684 ±0)` - // Minimum execution time: 267_047_000 picoseconds. - Weight::from_parts(136_857_840, 6826) - // Standard Error: 5_305 - .saturating_add(Weight::from_parts(3_749_573, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `925 + r * (209 ±0)` + // Estimated: `9304 + r * (2684 ±0)` + // Minimum execution time: 256_990_000 picoseconds. + Weight::from_parts(107_167_044, 9304) + // Standard Error: 7_545 + .saturating_add(Weight::from_parts(3_628_112, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2684).saturating_mul(r.into())) @@ -2455,6 +2628,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:1601 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -2468,13 +2643,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (213 ±0)` - // Estimated: `6830 + r * (2688 ±0)` - // Minimum execution time: 257_198_000 picoseconds. - Weight::from_parts(122_900_097, 6830) - // Standard Error: 6_078 - .saturating_add(Weight::from_parts(4_637_847, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `924 + r * (213 ±0)` + // Estimated: `9308 + r * (2688 ±0)` + // Minimum execution time: 272_889_000 picoseconds. + Weight::from_parts(110_341_799, 9308) + // Standard Error: 7_881 + .saturating_add(Weight::from_parts(4_427_043, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2688).saturating_mul(r.into())) @@ -2483,6 +2658,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2496,13 +2673,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_own_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `873 + r * (6 ±0)` - // Estimated: `6815 + r * (6 ±0)` - // Minimum execution time: 250_669_000 picoseconds. - Weight::from_parts(278_169_003, 6815) - // Standard Error: 948 - .saturating_add(Weight::from_parts(475_823, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `876 + r * (6 ±0)` + // Estimated: `9293 + r * (6 ±0)` + // Minimum execution time: 275_027_000 picoseconds. + Weight::from_parts(283_302_223, 9293) + // Standard Error: 1_179 + .saturating_add(Weight::from_parts(436_023, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2510,6 +2687,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2523,18 +2702,20 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_origin(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 247_545_000 picoseconds. - Weight::from_parts(278_451_059, 6804) - // Standard Error: 415 - .saturating_add(Weight::from_parts(207_099, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 270_137_000 picoseconds. + Weight::from_parts(282_756_362, 9282) + // Standard Error: 384 + .saturating_add(Weight::from_parts(171_660, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2548,13 +2729,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_caller_is_root(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `753 + r * (3 ±0)` - // Estimated: `6693 + r * (3 ±0)` - // Minimum execution time: 237_427_000 picoseconds. - Weight::from_parts(266_803_438, 6693) - // Standard Error: 395 - .saturating_add(Weight::from_parts(187_185, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(7_u64)) + // Measured: `756 + r * (3 ±0)` + // Estimated: `9171 + r * (3 ±0)` + // Minimum execution time: 255_131_000 picoseconds. + Weight::from_parts(269_207_006, 9171) + // Standard Error: 542 + .saturating_add(Weight::from_parts(161_597, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -2562,6 +2743,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2575,13 +2758,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `867 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 263_390_000 picoseconds. - Weight::from_parts(284_284_826, 6807) - // Standard Error: 710 - .saturating_add(Weight::from_parts(385_936, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `870 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 272_172_000 picoseconds. + Weight::from_parts(283_747_134, 9285) + // Standard Error: 885 + .saturating_add(Weight::from_parts(343_968, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2589,6 +2772,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2602,13 +2787,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_gas_left(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6806 + r * (6 ±0)` - // Minimum execution time: 253_041_000 picoseconds. - Weight::from_parts(277_521_867, 6806) - // Standard Error: 807 - .saturating_add(Weight::from_parts(426_831, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9284 + r * (6 ±0)` + // Minimum execution time: 263_220_000 picoseconds. + Weight::from_parts(277_062_382, 9284) + // Standard Error: 1_783 + .saturating_add(Weight::from_parts(399_631, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2616,6 +2801,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:2 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2629,13 +2816,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1007 + r * (6 ±0)` - // Estimated: `6931 + r * (6 ±0)` - // Minimum execution time: 250_907_000 picoseconds. - Weight::from_parts(288_852_260, 6931) - // Standard Error: 32_981 - .saturating_add(Weight::from_parts(1_717_744, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1010 + r * (6 ±0)` + // Estimated: `9409 + r * (6 ±0)` + // Minimum execution time: 253_218_000 picoseconds. + Weight::from_parts(282_251_452, 9409) + // Standard Error: 2_367 + .saturating_add(Weight::from_parts(1_604_060, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2643,6 +2830,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2656,13 +2845,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_value_transferred(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (6 ±0)` - // Estimated: `6823 + r * (6 ±0)` - // Minimum execution time: 252_691_000 picoseconds. - Weight::from_parts(279_439_224, 6823) - // Standard Error: 682 - .saturating_add(Weight::from_parts(384_517, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `880 + r * (6 ±0)` + // Estimated: `9301 + r * (6 ±0)` + // Minimum execution time: 271_797_000 picoseconds. + Weight::from_parts(288_594_393, 9301) + // Standard Error: 846 + .saturating_add(Weight::from_parts(335_063, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2670,6 +2859,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2683,13 +2874,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_minimum_balance(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `875 + r * (6 ±0)` - // Estimated: `6816 + r * (6 ±0)` - // Minimum execution time: 249_815_000 picoseconds. - Weight::from_parts(282_327_335, 6816) - // Standard Error: 655 - .saturating_add(Weight::from_parts(381_065, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `878 + r * (6 ±0)` + // Estimated: `9294 + r * (6 ±0)` + // Minimum execution time: 254_997_000 picoseconds. + Weight::from_parts(284_331_894, 9294) + // Standard Error: 885 + .saturating_add(Weight::from_parts(337_073, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2697,6 +2888,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2710,13 +2903,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_block_number(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872 + r * (6 ±0)` - // Estimated: `6819 + r * (6 ±0)` - // Minimum execution time: 251_512_000 picoseconds. - Weight::from_parts(282_334_651, 6819) - // Standard Error: 894 - .saturating_add(Weight::from_parts(385_540, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `875 + r * (6 ±0)` + // Estimated: `9297 + r * (6 ±0)` + // Minimum execution time: 273_845_000 picoseconds. + Weight::from_parts(285_291_242, 9297) + // Standard Error: 690 + .saturating_add(Weight::from_parts(332_783, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2724,6 +2917,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2737,13 +2932,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_now(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (6 ±0)` - // Estimated: `6804 + r * (6 ±0)` - // Minimum execution time: 252_765_000 picoseconds. - Weight::from_parts(281_388_473, 6804) - // Standard Error: 733 - .saturating_add(Weight::from_parts(389_343, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (6 ±0)` + // Estimated: `9282 + r * (6 ±0)` + // Minimum execution time: 268_302_000 picoseconds. + Weight::from_parts(280_463_257, 9282) + // Standard Error: 1_035 + .saturating_add(Weight::from_parts(345_811, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2751,6 +2946,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2766,13 +2963,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_weight_to_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `937 + r * (14 ±0)` - // Estimated: `6872 + r * (14 ±0)` - // Minimum execution time: 252_542_000 picoseconds. - Weight::from_parts(287_097_586, 6872) - // Standard Error: 843 - .saturating_add(Weight::from_parts(1_211_320, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `940 + r * (14 ±0)` + // Estimated: `9350 + r * (14 ±0)` + // Minimum execution time: 263_433_000 picoseconds. + Weight::from_parts(290_098_370, 9350) + // Standard Error: 1_905 + .saturating_add(Weight::from_parts(805_299, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 14).saturating_mul(r.into())) } @@ -2780,6 +2977,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2793,13 +2992,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_input(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `865 + r * (6 ±0)` - // Estimated: `6807 + r * (6 ±0)` - // Minimum execution time: 251_332_000 picoseconds. - Weight::from_parts(279_047_507, 6807) - // Standard Error: 506 - .saturating_add(Weight::from_parts(338_066, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `868 + r * (6 ±0)` + // Estimated: `9285 + r * (6 ±0)` + // Minimum execution time: 273_588_000 picoseconds. + Weight::from_parts(287_133_565, 9285) + // Standard Error: 799 + .saturating_add(Weight::from_parts(254_114, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 6).saturating_mul(r.into())) } @@ -2807,6 +3006,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2820,19 +3021,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_input_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `869` - // Estimated: `6809` - // Minimum execution time: 259_176_000 picoseconds. - Weight::from_parts(226_783_195, 6809) - // Standard Error: 22 - .saturating_add(Weight::from_parts(1_043, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `872` + // Estimated: `9287` + // Minimum execution time: 257_843_000 picoseconds. + Weight::from_parts(228_177_495, 9287) + // Standard Error: 24 + .saturating_add(Weight::from_parts(985, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2846,13 +3049,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1]`. fn seal_return(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `853 + r * (45 ±0)` - // Estimated: `6793 + r * (45 ±0)` - // Minimum execution time: 242_002_000 picoseconds. - Weight::from_parts(270_118_702, 6793) - // Standard Error: 901_814 - .saturating_add(Weight::from_parts(5_456_997, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `856 + r * (45 ±0)` + // Estimated: `9271 + r * (45 ±0)` + // Minimum execution time: 248_332_000 picoseconds. + Weight::from_parts(274_998_906, 9271) + // Standard Error: 949_561 + .saturating_add(Weight::from_parts(5_570_693, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 45).saturating_mul(r.into())) } @@ -2860,6 +3063,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2873,19 +3078,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_return_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863` - // Estimated: `6810` - // Minimum execution time: 253_553_000 picoseconds. - Weight::from_parts(274_145_636, 6810) - // Standard Error: 0 - .saturating_add(Weight::from_parts(393, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866` + // Estimated: `9288` + // Minimum execution time: 272_055_000 picoseconds. + Weight::from_parts(282_280_090, 9288) + // Standard Error: 1 + .saturating_add(Weight::from_parts(330, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_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: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:1 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2899,28 +3106,30 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, 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 `r` is `[0, 1]`. fn seal_terminate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2972 + r * (316 ±0)` - // Estimated: `8912 + r * (5266 ±0)` - // Minimum execution time: 269_543_000 picoseconds. - Weight::from_parts(297_167_442, 8912) - // Standard Error: 816_260 - .saturating_add(Weight::from_parts(120_306_557, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(r.into()))) + // Measured: `2902 + r * (529 ±0)` + // Estimated: `11317 + r * (5479 ±0)` + // Minimum execution time: 274_842_000 picoseconds. + Weight::from_parts(299_824_497, 11317) + // Standard Error: 902_686 + .saturating_add(Weight::from_parts(106_562_902, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((10_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 5266).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 5479).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2936,13 +3145,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_random(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `944 + r * (10 ±0)` - // Estimated: `6885 + r * (10 ±0)` - // Minimum execution time: 254_432_000 picoseconds. - Weight::from_parts(280_280_937, 6885) - // Standard Error: 1_667 - .saturating_add(Weight::from_parts(1_321_779, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `947 + r * (10 ±0)` + // Estimated: `9363 + r * (10 ±0)` + // Minimum execution time: 255_393_000 picoseconds. + Weight::from_parts(309_814_338, 9363) + // Standard Error: 2_978 + .saturating_add(Weight::from_parts(1_203_507, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -2950,6 +3159,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2963,13 +3174,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_deposit_event(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `863 + r * (10 ±0)` - // Estimated: `6805 + r * (10 ±0)` - // Minimum execution time: 266_819_000 picoseconds. - Weight::from_parts(288_308_884, 6805) - // Standard Error: 837 - .saturating_add(Weight::from_parts(1_980_950, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `866 + r * (10 ±0)` + // Estimated: `9283 + r * (10 ±0)` + // Minimum execution time: 250_378_000 picoseconds. + Weight::from_parts(287_003_144, 9283) + // Standard Error: 2_726 + .saturating_add(Weight::from_parts(1_850_967, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 10).saturating_mul(r.into())) } @@ -2977,6 +3188,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -2991,15 +3204,15 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_deposit_event_per_topic_and_byte(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `880 + t * (32 ±0)` - // Estimated: `6825 + t * (2508 ±0)` - // Minimum execution time: 266_947_000 picoseconds. - Weight::from_parts(281_286_473, 6825) - // Standard Error: 94_213 - .saturating_add(Weight::from_parts(3_770_905, 0).saturating_mul(t.into())) - // Standard Error: 26 - .saturating_add(Weight::from_parts(503, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `883 + t * (32 ±0)` + // Estimated: `9303 + t * (2508 ±0)` + // Minimum execution time: 266_787_000 picoseconds. + Weight::from_parts(284_368_661, 9303) + // Standard Error: 121_315 + .saturating_add(Weight::from_parts(2_690_211, 0).saturating_mul(t.into())) + // Standard Error: 33 + .saturating_add(Weight::from_parts(789, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -3009,6 +3222,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3022,13 +3237,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_debug_message(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (7 ±0)` - // Estimated: `6807 + r * (7 ±0)` - // Minimum execution time: 155_353_000 picoseconds. - Weight::from_parts(172_423_459, 6807) - // Standard Error: 374 - .saturating_add(Weight::from_parts(299_934, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `865 + r * (7 ±0)` + // Estimated: `9285 + r * (7 ±0)` + // Minimum execution time: 166_804_000 picoseconds. + Weight::from_parts(175_118_291, 9285) + // Standard Error: 398 + .saturating_add(Weight::from_parts(220_961, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) } @@ -3036,6 +3251,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) @@ -3049,13 +3266,13 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 1048576]`. fn seal_debug_message_per_byte(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `125813` - // Estimated: `131755` - // Minimum execution time: 418_146_000 picoseconds. - Weight::from_parts(392_022_620, 131755) + // Measured: `125816` + // Estimated: `131758` + // Minimum execution time: 398_436_000 picoseconds. + Weight::from_parts(385_003_285, 131758) // Standard Error: 12 - .saturating_add(Weight::from_parts(1_098, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_038, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -3063,13 +3280,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_set_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `924 + r * (292 ±0)` - // Estimated: `926 + r * (293 ±0)` - // Minimum execution time: 246_236_000 picoseconds. - Weight::from_parts(185_383_364, 926) - // Standard Error: 9_561 - .saturating_add(Weight::from_parts(6_465_948, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `927 + r * (292 ±0)` + // Estimated: `929 + r * (293 ±0)` + // Minimum execution time: 274_099_000 picoseconds. + Weight::from_parts(174_282_817, 929) + // Standard Error: 10_547 + .saturating_add(Weight::from_parts(6_262_173, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3080,13 +3297,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_new_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1447` - // Estimated: `1430` - // Minimum execution time: 266_282_000 picoseconds. - Weight::from_parts(335_348_736, 1430) - // Standard Error: 71 - .saturating_add(Weight::from_parts(722, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) + // Measured: `1450` + // Estimated: `1433` + // Minimum execution time: 274_061_000 picoseconds. + Weight::from_parts(334_755_333, 1433) + // Standard Error: 66 + .saturating_add(Weight::from_parts(771, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -3094,11 +3311,11 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_set_storage_per_old_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1253 + n * (1 ±0)` - // Estimated: `1253 + n * (1 ±0)` - // Minimum execution time: 267_258_000 picoseconds. - Weight::from_parts(294_884_737, 1253) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1256 + n * (1 ±0)` + // Estimated: `1256 + n * (1 ±0)` + // Minimum execution time: 272_657_000 picoseconds. + Weight::from_parts(299_061_594, 1256) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3107,13 +3324,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_clear_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (288 ±0)` - // Estimated: `927 + r * (289 ±0)` - // Minimum execution time: 249_393_000 picoseconds. - Weight::from_parts(189_185_750, 927) - // Standard Error: 9_002 - .saturating_add(Weight::from_parts(6_314_214, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `924 + r * (288 ±0)` + // Estimated: `930 + r * (289 ±0)` + // Minimum execution time: 270_524_000 picoseconds. + Weight::from_parts(177_959_667, 930) + // Standard Error: 10_397 + .saturating_add(Weight::from_parts(6_270_181, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3124,11 +3341,11 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_clear_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1249 + n * (1 ±0)` - // Estimated: `1249 + n * (1 ±0)` - // Minimum execution time: 268_399_000 picoseconds. - Weight::from_parts(296_233_783, 1249) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1252 + n * (1 ±0)` + // Estimated: `1252 + n * (1 ±0)` + // Minimum execution time: 270_757_000 picoseconds. + Weight::from_parts(298_627_896, 1252) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3137,13 +3354,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_get_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `921 + r * (296 ±0)` - // Estimated: `923 + r * (297 ±0)` - // Minimum execution time: 248_415_000 picoseconds. - Weight::from_parts(206_980_176, 923) - // Standard Error: 7_237 - .saturating_add(Weight::from_parts(5_326_052, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `924 + r * (296 ±0)` + // Estimated: `926 + r * (297 ±0)` + // Minimum execution time: 268_082_000 picoseconds. + Weight::from_parts(202_583_730, 926) + // Standard Error: 9_029 + .saturating_add(Weight::from_parts(5_028_517, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 297).saturating_mul(r.into())) @@ -3153,13 +3370,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_get_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1265 + n * (1 ±0)` - // Estimated: `1265 + n * (1 ±0)` - // Minimum execution time: 269_414_000 picoseconds. - Weight::from_parts(292_066_705, 1265) - // Standard Error: 32 - .saturating_add(Weight::from_parts(948, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1268 + n * (1 ±0)` + // Estimated: `1268 + n * (1 ±0)` + // Minimum execution time: 274_100_000 picoseconds. + Weight::from_parts(296_981_515, 1268) + // Standard Error: 34 + .saturating_add(Weight::from_parts(578, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3168,13 +3385,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_contains_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `932 + r * (288 ±0)` - // Estimated: `929 + r * (289 ±0)` - // Minimum execution time: 264_510_000 picoseconds. - Weight::from_parts(207_899_194, 929) - // Standard Error: 7_461 - .saturating_add(Weight::from_parts(5_040_079, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `935 + r * (288 ±0)` + // Estimated: `932 + r * (289 ±0)` + // Minimum execution time: 269_697_000 picoseconds. + Weight::from_parts(198_346_516, 932) + // Standard Error: 8_623 + .saturating_add(Weight::from_parts(4_964_116, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 289).saturating_mul(r.into())) @@ -3184,13 +3401,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_contains_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1252 + n * (1 ±0)` - // Estimated: `1252 + n * (1 ±0)` - // Minimum execution time: 265_740_000 picoseconds. - Weight::from_parts(289_814_750, 1252) - // Standard Error: 33 - .saturating_add(Weight::from_parts(182, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1255 + n * (1 ±0)` + // Estimated: `1255 + n * (1 ±0)` + // Minimum execution time: 264_210_000 picoseconds. + Weight::from_parts(291_387_652, 1255) + // Standard Error: 36 + .saturating_add(Weight::from_parts(524, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3199,13 +3416,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_take_storage(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `914 + r * (296 ±0)` - // Estimated: `919 + r * (297 ±0)` - // Minimum execution time: 254_987_000 picoseconds. - Weight::from_parts(182_376_044, 919) - // Standard Error: 9_569 - .saturating_add(Weight::from_parts(6_656_191, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `917 + r * (296 ±0)` + // Estimated: `922 + r * (297 ±0)` + // Minimum execution time: 267_219_000 picoseconds. + Weight::from_parts(175_866_982, 922) + // Standard Error: 11_275 + .saturating_add(Weight::from_parts(6_424_755, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3216,13 +3433,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 16384]`. fn seal_take_storage_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1266 + n * (1 ±0)` - // Estimated: `1266 + n * (1 ±0)` - // Minimum execution time: 272_780_000 picoseconds. - Weight::from_parts(299_681_117, 1266) - // Standard Error: 30 - .saturating_add(Weight::from_parts(713, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1269 + n * (1 ±0)` + // Estimated: `1269 + n * (1 ±0)` + // Minimum execution time: 274_634_000 picoseconds. + Weight::from_parts(294_272_009, 1269) + // Standard Error: 36 + .saturating_add(Weight::from_parts(1_219, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3230,6 +3447,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1602 w:1601) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3243,13 +3462,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_transfer(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1415 + r * (45 ±0)` - // Estimated: `7307 + r * (2520 ±0)` - // Minimum execution time: 268_642_000 picoseconds. - Weight::from_parts(205_633_613, 7307) - // Standard Error: 26_597 - .saturating_add(Weight::from_parts(32_775_614, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1418 + r * (45 ±0)` + // Estimated: `9785 + r * (2520 ±0)` + // Minimum execution time: 266_833_000 picoseconds. + Weight::from_parts(186_049_741, 9785) + // Standard Error: 40_311 + .saturating_add(Weight::from_parts(31_365_585, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3259,6 +3478,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -3272,13 +3493,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 800]`. fn seal_call(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1260 + r * (245 ±0)` - // Estimated: `9440 + r * (2721 ±0)` - // Minimum execution time: 260_911_000 picoseconds. - Weight::from_parts(274_255_000, 9440) - // Standard Error: 213_855 - .saturating_add(Weight::from_parts(239_435_012, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `1263 + r * (245 ±0)` + // Estimated: `9635 + r * (2721 ±0)` + // Minimum execution time: 272_140_000 picoseconds. + Weight::from_parts(275_009_000, 9635) + // Standard Error: 99_187 + .saturating_add(Weight::from_parts(240_702_479, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -3288,6 +3509,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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:736 w:0) @@ -3302,12 +3525,12 @@ impl WeightInfo for () { fn seal_delegate_call(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (576 ±0)` - // Estimated: `6812 + r * (2637 ±3)` - // Minimum execution time: 259_239_000 picoseconds. - Weight::from_parts(266_900_000, 6812) - // Standard Error: 136_573 - .saturating_add(Weight::from_parts(238_160_538, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Estimated: `9290 + r * (2637 ±10)` + // Minimum execution time: 263_664_000 picoseconds. + Weight::from_parts(277_240_000, 9290) + // Standard Error: 169_147 + .saturating_add(Weight::from_parts(240_084_929, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3317,6 +3540,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) @@ -3331,15 +3556,15 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 1048576]`. fn seal_call_per_transfer_clone_byte(t: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1307 + t * (277 ±0)` - // Estimated: `12197 + t * (5227 ±0)` - // Minimum execution time: 458_122_000 picoseconds. - Weight::from_parts(91_568_476, 12197) - // Standard Error: 11_267_459 - .saturating_add(Weight::from_parts(339_871_716, 0).saturating_mul(t.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_026, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(13_u64)) + // Measured: `1310 + t * (277 ±0)` + // Estimated: `12200 + t * (5227 ±0)` + // Minimum execution time: 464_644_000 picoseconds. + Weight::from_parts(40_377_313, 12200) + // Standard Error: 12_060_226 + .saturating_add(Weight::from_parts(380_635_982, 0).saturating_mul(t.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(1_014, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(t.into()))) @@ -3349,6 +3574,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:802 w:802) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:801 w:801) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:801 w:800) @@ -3362,17 +3589,17 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:803 w:803) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:800 w:800) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `r` is `[1, 800]`. fn seal_instantiate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1278 + r * (255 ±0)` - // Estimated: `9620 + r * (2731 ±0)` - // Minimum execution time: 618_921_000 picoseconds. - Weight::from_parts(638_593_000, 9620) - // Standard Error: 304_946 - .saturating_add(Weight::from_parts(357_251_226, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `1281 + r * (255 ±0)` + // Estimated: `9623 + r * (2731 ±0)` + // Minimum execution time: 641_845_000 picoseconds. + Weight::from_parts(649_908_000, 9623) + // Standard Error: 278_514 + .saturating_add(Weight::from_parts(361_164_598, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(7_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(r.into()))) @@ -3382,6 +3609,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `Measured`) /// Storage: `Contracts::ContractInfoOf` (r:2 w:2) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) /// Storage: `Contracts::CodeInfoOf` (r:2 w:1) @@ -3395,21 +3624,21 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:4 w:4) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) /// The range of component `t` is `[0, 1]`. /// The range of component `i` is `[0, 983040]`. /// The range of component `s` is `[0, 983040]`. fn seal_instantiate_per_transfer_input_salt_byte(t: u32, i: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1303 + t * (104 ±0)` - // Estimated: `12211 + t * (2549 ±1)` - // Minimum execution time: 2_282_246_000 picoseconds. - Weight::from_parts(1_183_712_345, 12211) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_161, 0).saturating_mul(i.into())) - // Standard Error: 16 - .saturating_add(Weight::from_parts(1_265, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(16_u64)) + // Measured: `1306 + t * (104 ±0)` + // Estimated: `12214 + t * (2549 ±1)` + // Minimum execution time: 2_111_632_000 picoseconds. + Weight::from_parts(1_213_125_745, 12214) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_055, 0).saturating_mul(i.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_192, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(11_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) @@ -3419,6 +3648,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3432,13 +3663,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_sha2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `862 + r * (8 ±0)` - // Estimated: `6801 + r * (8 ±0)` - // Minimum execution time: 247_913_000 picoseconds. - Weight::from_parts(270_725_296, 6801) - // Standard Error: 1_042 - .saturating_add(Weight::from_parts(459_972, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `865 + r * (8 ±0)` + // Estimated: `9279 + r * (8 ±0)` + // Minimum execution time: 264_227_000 picoseconds. + Weight::from_parts(281_015_995, 9279) + // Standard Error: 710 + .saturating_add(Weight::from_parts(365_734, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3446,6 +3677,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3459,19 +3692,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_sha2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `870` - // Estimated: `6808` - // Minimum execution time: 260_418_000 picoseconds. - Weight::from_parts(262_195_985, 6808) + // Measured: `873` + // Estimated: `9286` + // Minimum execution time: 270_709_000 picoseconds. + Weight::from_parts(268_416_051, 9286) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_121, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(1_080, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3485,13 +3720,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_keccak_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6806 + r * (8 ±0)` - // Minimum execution time: 249_692_000 picoseconds. - Weight::from_parts(281_644_768, 6806) - // Standard Error: 616 - .saturating_add(Weight::from_parts(849_731, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9284 + r * (8 ±0)` + // Minimum execution time: 267_283_000 picoseconds. + Weight::from_parts(280_706_659, 9284) + // Standard Error: 540 + .saturating_add(Weight::from_parts(790_312, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3499,6 +3734,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3512,19 +3749,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_keccak_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6814` - // Minimum execution time: 258_522_000 picoseconds. - Weight::from_parts(275_659_556, 6814) + // Measured: `875` + // Estimated: `9292` + // Minimum execution time: 262_010_000 picoseconds. + Weight::from_parts(273_278_744, 9292) // Standard Error: 1 - .saturating_add(Weight::from_parts(3_378, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(Weight::from_parts(3_362, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3538,13 +3777,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_256(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6808 + r * (8 ±0)` - // Minimum execution time: 247_311_000 picoseconds. - Weight::from_parts(277_744_830, 6808) - // Standard Error: 556 - .saturating_add(Weight::from_parts(526_371, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9286 + r * (8 ±0)` + // Minimum execution time: 268_698_000 picoseconds. + Weight::from_parts(282_890_578, 9286) + // Standard Error: 622 + .saturating_add(Weight::from_parts(441_131, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3552,6 +3791,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3565,19 +3806,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_256_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6813` - // Minimum execution time: 270_091_000 picoseconds. - Weight::from_parts(268_084_237, 6813) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_231, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9291` + // Minimum execution time: 252_846_000 picoseconds. + Weight::from_parts(267_657_561, 9291) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_208, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3591,13 +3834,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_hash_blake2_128(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `864 + r * (8 ±0)` - // Estimated: `6805 + r * (8 ±0)` - // Minimum execution time: 248_860_000 picoseconds. - Weight::from_parts(278_347_052, 6805) - // Standard Error: 465 - .saturating_add(Weight::from_parts(524_134, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `867 + r * (8 ±0)` + // Estimated: `9283 + r * (8 ±0)` + // Minimum execution time: 266_899_000 picoseconds. + Weight::from_parts(276_862_067, 9283) + // Standard Error: 1_249 + .saturating_add(Weight::from_parts(443_970, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) } @@ -3605,6 +3848,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3618,19 +3863,21 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 1048576]`. fn seal_hash_blake2_128_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `872` - // Estimated: `6811` - // Minimum execution time: 267_853_000 picoseconds. - Weight::from_parts(269_016_694, 6811) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_231, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `875` + // Estimated: `9289` + // Minimum execution time: 265_413_000 picoseconds. + Weight::from_parts(265_600_840, 9289) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_203, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_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: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3644,13 +3891,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 125697]`. fn seal_sr25519_verify_per_byte(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `997 + n * (1 ±0)` - // Estimated: `6934 + n * (1 ±0)` - // Minimum execution time: 311_197_000 picoseconds. - Weight::from_parts(337_829_093, 6934) - // Standard Error: 9 - .saturating_add(Weight::from_parts(5_969, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `1000 + n * (1 ±0)` + // Estimated: `9412 + n * (1 ±0)` + // Minimum execution time: 328_341_000 picoseconds. + Weight::from_parts(341_038_581, 9412) + // Standard Error: 10 + .saturating_add(Weight::from_parts(5_863, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -3658,6 +3905,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3671,13 +3920,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 160]`. fn seal_sr25519_verify(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `807 + r * (112 ±0)` - // Estimated: `6748 + r * (112 ±0)` - // Minimum execution time: 256_287_000 picoseconds. - Weight::from_parts(294_925_139, 6748) - // Standard Error: 37_103 - .saturating_add(Weight::from_parts(48_635_344, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `808 + r * (112 ±0)` + // Estimated: `9226 + r * (112 ±0)` + // Minimum execution time: 272_730_000 picoseconds. + Weight::from_parts(339_349_232, 9226) + // Standard Error: 13_004 + .saturating_add(Weight::from_parts(41_649_026, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(r.into())) } @@ -3685,6 +3934,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3698,13 +3949,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_recover(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `907 + r * (76 ±0)` - // Estimated: `6801 + r * (77 ±0)` - // Minimum execution time: 254_882_000 picoseconds. - Weight::from_parts(308_441_954, 6801) - // Standard Error: 10_967 - .saturating_add(Weight::from_parts(45_956_602, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `910 + r * (76 ±0)` + // Estimated: `9279 + r * (77 ±0)` + // Minimum execution time: 273_892_000 picoseconds. + Weight::from_parts(352_853_960, 9279) + // Standard Error: 16_745 + .saturating_add(Weight::from_parts(45_853_294, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 77).saturating_mul(r.into())) } @@ -3712,6 +3963,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3725,13 +3978,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 160]`. fn seal_ecdsa_to_eth_address(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `877 + r * (42 ±0)` - // Estimated: `6816 + r * (42 ±0)` - // Minimum execution time: 251_751_000 picoseconds. - Weight::from_parts(293_622_198, 6816) - // Standard Error: 6_442 - .saturating_add(Weight::from_parts(12_149_268, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `880 + r * (42 ±0)` + // Estimated: `9294 + r * (42 ±0)` + // Minimum execution time: 268_431_000 picoseconds. + Weight::from_parts(319_727_796, 9294) + // Standard Error: 10_276 + .saturating_add(Weight::from_parts(11_928_882, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 42).saturating_mul(r.into())) } @@ -3739,6 +3992,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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:1536 w:1536) @@ -3753,12 +4008,12 @@ impl WeightInfo for () { fn seal_set_code_hash(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + r * (965 ±0)` - // Estimated: `6807 + r * (3090 ±7)` - // Minimum execution time: 249_436_000 picoseconds. - Weight::from_parts(268_239_000, 6807) - // Standard Error: 42_505 - .saturating_add(Weight::from_parts(23_424_800, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Estimated: `9285 + r * (3090 ±7)` + // Minimum execution time: 275_482_000 picoseconds. + Weight::from_parts(279_155_000, 9285) + // Standard Error: 65_388 + .saturating_add(Weight::from_parts(26_634_187, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r.into()))) @@ -3768,6 +4023,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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:33 w:32) @@ -3779,24 +4036,26 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 32]`. - fn add_delegate_dependency(r: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `928 + r * (131 ±0)` - // Estimated: `6878 + r * (2606 ±0)` - // Minimum execution time: 259_345_000 picoseconds. - Weight::from_parts(288_169_413, 6878) - // Standard Error: 22_399 - .saturating_add(Weight::from_parts(6_749_371, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + fn lock_delegate_dependency(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `937 + r * (131 ±0)` + // Estimated: `9346 + r * (2607 ±0)` + // Minimum execution time: 270_001_000 picoseconds. + Weight::from_parts(286_792_689, 9346) + // Standard Error: 21_074 + .saturating_add(Weight::from_parts(6_465_885, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) - .saturating_add(Weight::from_parts(0, 2606).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 2607).saturating_mul(r.into())) } /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `MaxEncodedLen`) /// Storage: `Contracts::CodeInfoOf` (r:33 w:32) @@ -3808,15 +4067,15 @@ impl WeightInfo for () { /// Storage: `System::EventTopics` (r:2 w:2) /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 32]`. - fn remove_delegate_dependency(r: u32, ) -> Weight { + fn unlock_delegate_dependency(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `969 + r * (183 ±0)` + // Measured: `972 + r * (184 ±0)` // Estimated: `129453 + r * (2568 ±0)` - // Minimum execution time: 257_547_000 picoseconds. - Weight::from_parts(287_042_678, 129453) - // Standard Error: 20_158 - .saturating_add(Weight::from_parts(5_960_974, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Minimum execution time: 270_652_000 picoseconds. + Weight::from_parts(293_369_452, 129453) + // Standard Error: 24_321 + .saturating_add(Weight::from_parts(5_575_600, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) @@ -3826,6 +4085,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3839,13 +4100,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `858 + r * (3 ±0)` - // Estimated: `6804 + r * (3 ±0)` - // Minimum execution time: 250_859_000 picoseconds. - Weight::from_parts(280_426_768, 6804) - // Standard Error: 415 - .saturating_add(Weight::from_parts(200_312, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `861 + r * (3 ±0)` + // Estimated: `9282 + r * (3 ±0)` + // Minimum execution time: 267_749_000 picoseconds. + Weight::from_parts(280_373_341, 9282) + // Standard Error: 464 + .saturating_add(Weight::from_parts(170_398, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -3853,6 +4114,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3866,13 +4129,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_account_reentrance_count(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2109 + r * (39 ±0)` - // Estimated: `7899 + r * (40 ±0)` - // Minimum execution time: 252_902_000 picoseconds. - Weight::from_parts(297_688_691, 7899) - // Standard Error: 1_589 - .saturating_add(Weight::from_parts(416_052, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + // Measured: `2112 + r * (39 ±0)` + // Estimated: `10377 + r * (40 ±0)` + // Minimum execution time: 270_260_000 picoseconds. + Weight::from_parts(337_969_172, 10377) + // Standard Error: 1_557 + .saturating_add(Weight::from_parts(305_735, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 40).saturating_mul(r.into())) } @@ -3880,6 +4143,8 @@ impl WeightInfo for () { /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Parameters::Parameters` (r:3 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, 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) @@ -3895,13 +4160,13 @@ impl WeightInfo for () { /// The range of component `r` is `[0, 1600]`. fn seal_instantiation_nonce(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `861 + r * (3 ±0)` - // Estimated: `6801 + r * (3 ±0)` - // Minimum execution time: 256_614_000 picoseconds. - Weight::from_parts(274_483_287, 6801) - // Standard Error: 387 - .saturating_add(Weight::from_parts(186_165, 0).saturating_mul(r.into())) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `864 + r * (3 ±0)` + // Estimated: `9279 + r * (3 ±0)` + // Minimum execution time: 265_607_000 picoseconds. + Weight::from_parts(283_396_630, 9279) + // Standard Error: 449 + .saturating_add(Weight::from_parts(149_018, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 3).saturating_mul(r.into())) } @@ -3910,9 +4175,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_463_000 picoseconds. - Weight::from_parts(2_810_154, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(6_083, 0).saturating_mul(r.into())) + // Minimum execution time: 1_813_000 picoseconds. + Weight::from_parts(1_264_945, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(15_098, 0).saturating_mul(r.into())) } } diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index eb12c4361f1f24c0ad8c71a397fc7a533f2536c2..a5081af2a2d280cc1956dcf2dbb69de18588ccd4 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-contracts-uapi" -version = "4.0.0-dev" +version = "5.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,7 +21,10 @@ scale = { package = "parity-scale-codec", version = "3.6.1", default-features = ], optional = true } [target.'cfg(target_arch = "riscv32")'.dependencies] -polkavm-derive = '0.4.0' +polkavm-derive = '0.5.0' + +[package.metadata.docs.rs] +default-target = ["wasm32-unknown-unknown"] [features] default = ["scale"] diff --git a/substrate/frame/contracts/uapi/src/host.rs b/substrate/frame/contracts/uapi/src/host.rs index 21350bc4eb3e42ffbdfd8b951cd66fdffbd5360e..04f58895ab4f6ccebb6452ebeec20038ce7f0d78 100644 --- a/substrate/frame/contracts/uapi/src/host.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -93,7 +93,7 @@ pub trait HostFn { /// - `output`: A reference to the output data buffer to write the address. fn address(output: &mut &mut [u8]); - /// Adds a new delegate dependency to the contract. + /// 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. @@ -102,10 +102,7 @@ pub trait HostFn { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn add_delegate_dependency(code_hash: &[u8]); + fn lock_delegate_dependency(code_hash: &[u8]); /// Stores the *free* balance of the current account into the supplied buffer. /// @@ -142,6 +139,7 @@ pub trait HostFn { /// /// Equivalent to the newer [`Self::call_v2`] version but works with /// *ref_time* Weight only + #[deprecated(note = "Deprecated, use newer version instead")] fn call_v1( flags: CallFlags, callee: &[u8], @@ -178,14 +176,11 @@ pub trait HostFn { /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] fn call_v2( flags: CallFlags, callee: &[u8], ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit: Option<&[u8]>, value: &[u8], input_data: &[u8], @@ -277,9 +272,6 @@ pub trait HostFn { /// /// 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. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] fn caller_is_root() -> u32; /// Clear the value at the given key in the contract storage. @@ -483,6 +475,7 @@ pub trait HostFn { /// /// Equivalent to the newer [`Self::instantiate_v2`] version but works /// with *ref_time* Weight only. + #[deprecated(note = "Deprecated, use newer version instead")] fn instantiate_v1( code_hash: &[u8], gas: u64, @@ -527,9 +520,6 @@ pub trait HostFn { /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] fn instantiate_v2( code_hash: &[u8], ref_time_limit: u64, @@ -605,10 +595,7 @@ pub trait HostFn { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - #[deprecated( - note = "Unstable function. Behaviour can change without further notice. Use only for testing." - )] - fn remove_delegate_dependency(code_hash: &[u8]); + fn unlock_delegate_dependency(code_hash: &[u8]); /// Cease contract execution and save a data buffer as a result of the execution. /// @@ -795,7 +782,7 @@ pub trait HostFn { #[deprecated( note = "Unstable function. Behaviour can change without further notice. Use only for testing." )] - fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result; + 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 @@ -807,7 +794,6 @@ pub trait HostFn { /// 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. - /// - `output`: A reference to the output data buffer to write the [XcmHash](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/type.XcmHash.html) /// /// # Return /// diff --git a/substrate/frame/contracts/uapi/src/host/riscv32.rs b/substrate/frame/contracts/uapi/src/host/riscv32.rs index b1934cc469e4b5cf92d8bb861bbbe073415b1789..561ab28747df9e55d105d6db09f4776d5e453e28 100644 --- a/substrate/frame/contracts/uapi/src/host/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/host/riscv32.rs @@ -130,7 +130,7 @@ impl HostFn for HostFnImpl { flags: CallFlags, callee: &[u8], ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit: Option<&[u8]>, value: &[u8], input_data: &[u8], @@ -281,11 +281,11 @@ impl HostFn for HostFnImpl { todo!() } - fn add_delegate_dependency(code_hash: &[u8]) { + fn lock_delegate_dependency(code_hash: &[u8]) { todo!() } - fn remove_delegate_dependency(code_hash: &[u8]) { + fn unlock_delegate_dependency(code_hash: &[u8]) { todo!() } @@ -297,7 +297,7 @@ impl HostFn for HostFnImpl { todo!() } - fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result { + fn xcm_execute(msg: &[u8]) -> Result { todo!() } diff --git a/substrate/frame/contracts/uapi/src/host/wasm32.rs b/substrate/frame/contracts/uapi/src/host/wasm32.rs index 77cf22891e2f5e325aeed102748603b6b3cb1143..bc697238061ab16e47d304bd9583f51f7eb99c0b 100644 --- a/substrate/frame/contracts/uapi/src/host/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/host/wasm32.rs @@ -23,7 +23,7 @@ mod sys { extern "C" { pub fn account_reentrance_count(account_ptr: *const u8) -> u32; - pub fn add_delegate_dependency(code_hash_ptr: *const u8); + pub fn lock_delegate_dependency(code_hash_ptr: *const u8); pub fn address(output_ptr: *mut u8, output_len_ptr: *mut u32); @@ -125,7 +125,7 @@ mod sys { pub fn reentrance_count() -> u32; - pub fn remove_delegate_dependency(code_hash_ptr: *const u8); + pub fn unlock_delegate_dependency(code_hash_ptr: *const u8); pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32) -> !; @@ -160,7 +160,7 @@ mod sys { pub fn weight_to_fee(gas: u64, output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32, output_ptr: *mut u8) -> ReturnCode; + pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32) -> ReturnCode; pub fn xcm_send( dest_ptr: *const u8, @@ -223,7 +223,7 @@ mod sys { pub fn weight_to_fee( ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, output_ptr: *mut u8, output_len_ptr: *mut u32, ); @@ -239,7 +239,7 @@ mod sys { flags: u32, callee_ptr: *const u8, ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit_ptr: *const u8, transferred_value_ptr: *const u8, input_data_ptr: *const u8, @@ -251,7 +251,7 @@ mod sys { pub fn instantiate( code_hash_ptr: *const u8, ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit_ptr: *const u8, value_ptr: *const u8, input_ptr: *const u8, @@ -301,6 +301,7 @@ macro_rules! impl_wrapper_for { unsafe { $( $mod )::*::$name(output.as_mut_ptr(), &mut output_len); } + extract_from_slice(output, output_len as usize) } } }; @@ -487,7 +488,7 @@ impl HostFn for HostFnImpl { flags: CallFlags, callee: &[u8], ref_time_limit: u64, - proof_time_limit: u64, + proof_size_limit: u64, deposit: Option<&[u8]>, value: &[u8], input_data: &[u8], @@ -501,7 +502,7 @@ impl HostFn for HostFnImpl { flags.bits(), callee.as_ptr(), ref_time_limit, - proof_time_limit, + proof_size_limit, deposit_ptr, value.as_ptr(), input_data.as_ptr(), @@ -803,12 +804,12 @@ impl HostFn for HostFnImpl { unsafe { sys::account_reentrance_count(account.as_ptr()) } } - fn add_delegate_dependency(code_hash: &[u8]) { - unsafe { sys::add_delegate_dependency(code_hash.as_ptr()) } + fn lock_delegate_dependency(code_hash: &[u8]) { + unsafe { sys::lock_delegate_dependency(code_hash.as_ptr()) } } - fn remove_delegate_dependency(code_hash: &[u8]) { - unsafe { sys::remove_delegate_dependency(code_hash.as_ptr()) } + fn unlock_delegate_dependency(code_hash: &[u8]) { + unsafe { sys::unlock_delegate_dependency(code_hash.as_ptr()) } } fn instantiation_nonce() -> u64 { @@ -819,9 +820,8 @@ impl HostFn for HostFnImpl { unsafe { sys::reentrance_count() } } - fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result { - let ret_code = - unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _, output.as_mut_ptr()) }; + fn xcm_execute(msg: &[u8]) -> Result { + let ret_code = unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _) }; ret_code.into() } diff --git a/substrate/frame/conviction-voting/Cargo.toml b/substrate/frame/conviction-voting/Cargo.toml index 78f524e3e665b6b1c928bafcc95f9a86e44915cc..ff5af995026f39527780d964aea16ec13be1ec2b 100644 --- a/substrate/frame/conviction-voting/Cargo.toml +++ b/substrate/frame/conviction-voting/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-conviction-voting" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -22,7 +22,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "max-encoded-len", ] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", features = ["derive"], optional = true } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/conviction-voting/src/lib.rs b/substrate/frame/conviction-voting/src/lib.rs index 1d6fbaa38694233a6766410728bf8efec0671d0e..466fc70a619b649e4b1aade0002e506dde430a1e 100644 --- a/substrate/frame/conviction-voting/src/lib.rs +++ b/substrate/frame/conviction-voting/src/lib.rs @@ -185,7 +185,7 @@ pub mod pallet { /// The account is already delegating. AlreadyDelegating, /// The account currently has votes attached to it and the operation cannot succeed until - /// these are removed, either through `unvote` or `reap_vote`. + /// these are removed through `remove_vote`. AlreadyVoting, /// Too high a balance was provided that the account cannot afford. InsufficientFunds, @@ -231,8 +231,8 @@ pub mod pallet { /// /// The dispatch origin of this call must be _Signed_, and the signing account must either: /// - be delegating already; or - /// - have no voting activity (if there is, then it will need to be removed/consolidated - /// through `reap_vote` or `unvote`). + /// - have no voting activity (if there is, then it will need to be removed through + /// `remove_vote`). /// /// - `to`: The account whose voting the `target` account's voting power will follow. /// - `class`: The class of polls to delegate. To delegate multiple classes, multiple calls diff --git a/substrate/frame/conviction-voting/src/tests.rs b/substrate/frame/conviction-voting/src/tests.rs index d7bac505a542066cba3fd51fa63b00b8aba67878..dbcd643b60ff7c5ac2ec0a0a351650c69c200e37 100644 --- a/substrate/frame/conviction-voting/src/tests.rs +++ b/substrate/frame/conviction-voting/src/tests.rs @@ -23,11 +23,7 @@ use frame_support::{ assert_noop, assert_ok, derive_impl, parameter_types, traits::{ConstU32, ConstU64, Contains, Polling, VoteTally}, }; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; use super::*; use crate as pallet_conviction_voting; @@ -53,29 +49,8 @@ impl Contains for BaseFilter { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = BaseFilter; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -92,7 +67,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/substrate/frame/conviction-voting/src/weights.rs b/substrate/frame/conviction-voting/src/weights.rs index 225f5c2cadd6fc3d4cb1dc735cf164d92150327f..75d9e8499ed8e0b3f71d1a15e23b9c26638e44f1 100644 --- a/substrate/frame/conviction-voting/src/weights.rs +++ b/substrate/frame/conviction-voting/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_conviction_voting +//! Autogenerated weights for `pallet_conviction_voting` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/conviction-voting/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/conviction-voting/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_conviction_voting. +/// Weight functions needed for `pallet_conviction_voting`. pub trait WeightInfo { fn vote_new() -> Weight; fn vote_existing() -> Weight; @@ -61,280 +60,300 @@ pub trait WeightInfo { fn unlock() -> Weight; } -/// Weights for pallet_conviction_voting using the Substrate node and recommended hardware. +/// Weights for `pallet_conviction_voting` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `13074` + // Measured: `13141` // Estimated: `219984` - // Minimum execution time: 112_936_000 picoseconds. - Weight::from_parts(116_972_000, 219984) + // Minimum execution time: 102_539_000 picoseconds. + Weight::from_parts(105_873_000, 219984) .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `20216` + // Measured: `20283` // Estimated: `219984` - // Minimum execution time: 291_971_000 picoseconds. - Weight::from_parts(301_738_000, 219984) + // Minimum execution time: 275_424_000 picoseconds. + Weight::from_parts(283_690_000, 219984) .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn remove_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `19968` + // Measured: `20035` // Estimated: `219984` - // Minimum execution time: 262_582_000 picoseconds. - Weight::from_parts(270_955_000, 219984) + // Minimum execution time: 275_109_000 picoseconds. + Weight::from_parts(281_315_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn remove_other_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `12675` + // Measured: `12742` // Estimated: `30706` - // Minimum execution time: 52_909_000 picoseconds. - Weight::from_parts(56_365_000, 30706) + // Minimum execution time: 49_629_000 picoseconds. + Weight::from_parts(51_300_000, 30706) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `240 + r * (1627 ±0)` + // Measured: `306 + r * (1628 ±0)` // Estimated: `109992 + r * (109992 ±0)` - // Minimum execution time: 54_640_000 picoseconds. - Weight::from_parts(57_185_281, 109992) - // Standard Error: 193_362 - .saturating_add(Weight::from_parts(44_897_418, 0).saturating_mul(r.into())) + // Minimum execution time: 45_776_000 picoseconds. + Weight::from_parts(47_917_822, 109992) + // Standard Error: 124_174 + .saturating_add(Weight::from_parts(43_171_077, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 1]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `406 + r * (1376 ±0)` + // Measured: `472 + r * (1377 ±0)` // Estimated: `109992 + r * (109992 ±0)` - // Minimum execution time: 26_514_000 picoseconds. - Weight::from_parts(28_083_732, 109992) - // Standard Error: 104_905 - .saturating_add(Weight::from_parts(40_722_467, 0).saturating_mul(r.into())) + // Minimum execution time: 23_600_000 picoseconds. + Weight::from_parts(25_001_426, 109992) + // Standard Error: 72_034 + .saturating_add(Weight::from_parts(37_851_873, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn unlock() -> Weight { // Proof Size summary in bytes: - // Measured: `11734` + // Measured: `11800` // Estimated: `30706` - // Minimum execution time: 71_140_000 picoseconds. - Weight::from_parts(77_388_000, 30706) + // Minimum execution time: 66_247_000 picoseconds. + Weight::from_parts(67_552_000, 30706) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `13074` + // Measured: `13141` // Estimated: `219984` - // Minimum execution time: 112_936_000 picoseconds. - Weight::from_parts(116_972_000, 219984) + // Minimum execution time: 102_539_000 picoseconds. + Weight::from_parts(105_873_000, 219984) .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `20216` + // Measured: `20283` // Estimated: `219984` - // Minimum execution time: 291_971_000 picoseconds. - Weight::from_parts(301_738_000, 219984) + // Minimum execution time: 275_424_000 picoseconds. + Weight::from_parts(283_690_000, 219984) .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn remove_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `19968` + // Measured: `20035` // Estimated: `219984` - // Minimum execution time: 262_582_000 picoseconds. - Weight::from_parts(270_955_000, 219984) + // Minimum execution time: 275_109_000 picoseconds. + Weight::from_parts(281_315_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn remove_other_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `12675` + // Measured: `12742` // Estimated: `30706` - // Minimum execution time: 52_909_000 picoseconds. - Weight::from_parts(56_365_000, 30706) + // Minimum execution time: 49_629_000 picoseconds. + Weight::from_parts(51_300_000, 30706) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 1]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `240 + r * (1627 ±0)` + // Measured: `306 + r * (1628 ±0)` // Estimated: `109992 + r * (109992 ±0)` - // Minimum execution time: 54_640_000 picoseconds. - Weight::from_parts(57_185_281, 109992) - // Standard Error: 193_362 - .saturating_add(Weight::from_parts(44_897_418, 0).saturating_mul(r.into())) + // Minimum execution time: 45_776_000 picoseconds. + Weight::from_parts(47_917_822, 109992) + // Standard Error: 124_174 + .saturating_add(Weight::from_parts(43_171_077, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes((4_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 1]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `406 + r * (1376 ±0)` + // Measured: `472 + r * (1377 ±0)` // Estimated: `109992 + r * (109992 ±0)` - // Minimum execution time: 26_514_000 picoseconds. - Weight::from_parts(28_083_732, 109992) - // Standard Error: 104_905 - .saturating_add(Weight::from_parts(40_722_467, 0).saturating_mul(r.into())) + // Minimum execution time: 23_600_000 picoseconds. + Weight::from_parts(25_001_426, 109992) + // Standard Error: 72_034 + .saturating_add(Weight::from_parts(37_851_873, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes((4_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(59), added: 2534, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn unlock() -> Weight { // Proof Size summary in bytes: - // Measured: `11734` + // Measured: `11800` // Estimated: `30706` - // Minimum execution time: 71_140_000 picoseconds. - Weight::from_parts(77_388_000, 30706) + // Minimum execution time: 66_247_000 picoseconds. + Weight::from_parts(67_552_000, 30706) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/substrate/frame/core-fellowship/Cargo.toml b/substrate/frame/core-fellowship/Cargo.toml index d223ecd4f24c17b6afabe90fc962021ff58c87e0..3e678d3274463f46619c19e71be97350fc1c5955 100644 --- a/substrate/frame/core-fellowship/Cargo.toml +++ b/substrate/frame/core-fellowship/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-core-fellowship" -version = "4.0.0-dev" +version = "12.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.16", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } @@ -27,15 +27,18 @@ sp-core = { path = "../../primitives/core", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } +pallet-ranked-collective = { path = "../ranked-collective", default-features = false, optional = true } [features] default = ["std"] std = [ "codec/std", "frame-benchmarking?/std", + "frame-support/experimental", "frame-support/std", "frame-system/std", "log/std", + "pallet-ranked-collective/std", "scale-info/std", "sp-arithmetic/std", "sp-core/std", @@ -47,10 +50,12 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-ranked-collective/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "pallet-ranked-collective?/try-runtime", "sp-runtime/try-runtime", ] diff --git a/substrate/frame/core-fellowship/src/benchmarking.rs b/substrate/frame/core-fellowship/src/benchmarking.rs index a3c410fac0a13ed6f41a5ce41b41d978136e7911..ddde70bd7ce16ec1b2de7ef5899fec5a5213bb79 100644 --- a/substrate/frame/core-fellowship/src/benchmarking.rs +++ b/substrate/frame/core-fellowship/src/benchmarking.rs @@ -227,7 +227,7 @@ mod benchmarks { impl_benchmark_test_suite! { CoreFellowship, - crate::tests::new_test_ext(), - crate::tests::Test, + crate::tests::unit::new_test_ext(), + crate::tests::unit::Test, } } diff --git a/substrate/frame/core-fellowship/src/lib.rs b/substrate/frame/core-fellowship/src/lib.rs index a0a45c7c594da76c50d6c7bab30a3cae30b4904a..d1b81c3ca134fbe30a14034164e45710cea12317 100644 --- a/substrate/frame/core-fellowship/src/lib.rs +++ b/substrate/frame/core-fellowship/src/lib.rs @@ -56,7 +56,6 @@ //! cannot be approved - they must proceed only to promotion prior to the offboard timeout elapsing. #![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit = "128"] use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -65,10 +64,12 @@ use sp_runtime::RuntimeDebug; use sp_std::{marker::PhantomData, prelude::*}; use frame_support::{ + defensive, dispatch::DispatchResultWithPostInfo, ensure, impl_ensure_origin_with_arg_ignoring_arg, traits::{ tokens::Balance as BalanceTrait, EnsureOrigin, EnsureOriginWithArg, Get, RankedMembers, + RankedMembersSwapHandler, }, BoundedVec, }; @@ -249,6 +250,8 @@ pub mod pallet { }, /// Pre-ranked account has been inducted at their current rank. Imported { who: T::AccountId, rank: RankOf }, + /// A member had its AccountId swapped. + Swapped { who: T::AccountId, new_who: T::AccountId }, } #[pallet::error] @@ -603,3 +606,38 @@ impl_ensure_origin_with_arg_ignoring_arg! { EnsureOriginWithArg for EnsureInducted {} } + +impl, I: 'static> RankedMembersSwapHandler for Pallet { + fn swapped(old: &T::AccountId, new: &T::AccountId, _rank: u16) { + if old == new { + defensive!("Should not try to swap with self"); + return + } + if !Member::::contains_key(old) { + defensive!("Should not try to swap non-member"); + return + } + if Member::::contains_key(new) { + defensive!("Should not try to overwrite existing member"); + return + } + + if let Some(member) = Member::::take(old) { + Member::::insert(new, member); + } + if let Some(we) = MemberEvidence::::take(old) { + MemberEvidence::::insert(new, we); + } + + Self::deposit_event(Event::::Swapped { who: old.clone(), new_who: new.clone() }); + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl, I: 'static> + pallet_ranked_collective::BenchmarkSetup<::AccountId> for Pallet +{ + fn ensure_member(who: &::AccountId) { + Self::import(frame_system::RawOrigin::Signed(who.clone()).into()).unwrap(); + } +} diff --git a/substrate/frame/core-fellowship/src/tests/integration.rs b/substrate/frame/core-fellowship/src/tests/integration.rs new file mode 100644 index 0000000000000000000000000000000000000000..6f177ba66db37fa791b06eb3f9b7c10998af0b98 --- /dev/null +++ b/substrate/frame/core-fellowship/src/tests/integration.rs @@ -0,0 +1,280 @@ +// 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. + +//! Integration test together with the ranked-collective pallet. + +use frame_support::{ + assert_noop, assert_ok, derive_impl, hypothetically, ord_parameter_types, + pallet_prelude::Weight, + parameter_types, + traits::{ConstU16, EitherOf, IsInVec, MapSuccess, PollStatus, Polling, TryMapSuccess}, +}; +use frame_system::EnsureSignedBy; +use pallet_ranked_collective::{EnsureRanked, Geometric, Rank, TallyOf, Votes}; +use sp_core::Get; +use sp_runtime::{ + traits::{Convert, ReduceBy, ReplaceWithDefault, TryMorphInto}, + BuildStorage, DispatchError, +}; +type Class = Rank; + +use crate as pallet_core_fellowship; +use crate::*; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + CoreFellowship: pallet_core_fellowship, + Club: pallet_ranked_collective, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, u64::max_value())); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; +} + +parameter_types! { + pub static MinRankOfClassDelta: Rank = 0; +} + +parameter_types! { + pub ZeroToNine: Vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + pub EvidenceSize: u32 = 1024; +} +ord_parameter_types! { + pub const One: u64 = 1; +} + +impl Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Members = Club; + type Balance = u64; + type ParamsOrigin = EnsureSignedBy; + type InductOrigin = EnsureInducted; + type ApproveOrigin = TryMapSuccess, u64>, TryMorphInto>; + type PromoteOrigin = TryMapSuccess, u64>, TryMorphInto>; + type EvidenceSize = EvidenceSize; +} + +pub struct TestPolls; +impl Polling> for TestPolls { + type Index = u8; + type Votes = Votes; + type Moment = u64; + type Class = Class; + + fn classes() -> Vec { + unimplemented!() + } + fn as_ongoing(_: u8) -> Option<(TallyOf, Self::Class)> { + unimplemented!() + } + fn access_poll( + _: Self::Index, + _: impl FnOnce(PollStatus<&mut TallyOf, Self::Moment, Self::Class>) -> R, + ) -> R { + unimplemented!() + } + fn try_access_poll( + _: Self::Index, + _: impl FnOnce( + PollStatus<&mut TallyOf, Self::Moment, Self::Class>, + ) -> Result, + ) -> Result { + unimplemented!() + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_ongoing(_: Self::Class) -> Result { + unimplemented!() + } + + #[cfg(feature = "runtime-benchmarks")] + fn end_ongoing(_: Self::Index, _: bool) -> Result<(), ()> { + unimplemented!() + } +} + +/// Convert the tally class into the minimum rank required to vote on the poll. +/// MinRank(Class) = Class - Delta +pub struct MinRankOfClass(PhantomData); +impl> Convert for MinRankOfClass { + fn convert(a: Class) -> Rank { + a.saturating_sub(Delta::get()) + } +} + +impl pallet_ranked_collective::Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type PromoteOrigin = EitherOf< + // Root can promote arbitrarily. + frame_system::EnsureRootWithSuccess>, + // Members can promote up to the rank of 2 below them. + MapSuccess, ReduceBy>>, + >; + type AddOrigin = MapSuccess>; + type DemoteOrigin = EitherOf< + // Root can demote arbitrarily. + frame_system::EnsureRootWithSuccess>, + // Members can demote up to the rank of 3 below them. + MapSuccess, ReduceBy>>, + >; + type RemoveOrigin = Self::DemoteOrigin; + type ExchangeOrigin = EitherOf< + // Root can exchange arbitrarily. + frame_system::EnsureRootWithSuccess>, + // Members can exchange up to the rank of 2 below them. + MapSuccess, ReduceBy>>, + >; + type Polls = TestPolls; + type MinRankOfClass = MinRankOfClass; + type MemberSwappedHandler = CoreFellowship; + type VoteWeight = Geometric; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = CoreFellowship; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + let params = ParamsType { + active_salary: [10, 20, 30, 40, 50, 60, 70, 80, 90], + passive_salary: [1, 2, 3, 4, 5, 6, 7, 8, 9], + demotion_period: [2, 4, 6, 8, 10, 12, 14, 16, 18], + min_promotion_period: [3, 6, 9, 12, 15, 18, 21, 24, 27], + offboard_timeout: 1, + }; + assert_ok!(CoreFellowship::set_params(signed(1), Box::new(params))); + System::set_block_number(1); + }); + ext +} + +fn promote_n_times(acc: u64, r: u16) { + for _ in 0..r { + assert_ok!(Club::promote_member(RuntimeOrigin::root(), acc)); + } +} + +fn signed(who: u64) -> RuntimeOrigin { + RuntimeOrigin::signed(who) +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + let frame_system::EventRecord { event, .. } = events.last().expect("Event expected"); + assert_eq!(event, &system_event.into()); +} + +fn evidence(e: u32) -> Evidence { + e.encode() + .into_iter() + .cycle() + .take(1024) + .collect::>() + .try_into() + .expect("Static length matches") +} + +#[test] +fn swap_simple_works() { + new_test_ext().execute_with(|| { + for i in 0u16..9 { + let acc = i as u64; + + assert_ok!(Club::add_member(RuntimeOrigin::root(), acc)); + promote_n_times(acc, i); + assert_ok!(CoreFellowship::import(signed(acc))); + + // Swapping normally works: + assert_ok!(Club::exchange_member(RuntimeOrigin::root(), acc, acc + 10)); + assert_last_event(Event::Swapped { who: acc, new_who: acc + 10 }.into()); + } + }); +} + +/// Exhaustively test that adding member `1` is equivalent to adding member `0` and then swapping. +/// +/// The member also submits evidence before the swap. +#[test] +fn swap_exhaustive_works() { + new_test_ext().execute_with(|| { + let root_add = hypothetically!({ + assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); + promote_n_times(1, 4); + assert_ok!(CoreFellowship::import(signed(1))); + assert_ok!(CoreFellowship::submit_evidence(signed(1), Wish::Retention, evidence(1))); + + // The events mess up the storage root: + System::reset_events(); + sp_io::storage::root(sp_runtime::StateVersion::V1) + }); + + let root_swap = hypothetically!({ + assert_ok!(Club::add_member(RuntimeOrigin::root(), 0)); + promote_n_times(0, 4); + assert_ok!(CoreFellowship::import(signed(0))); + assert_ok!(CoreFellowship::submit_evidence(signed(0), Wish::Retention, evidence(1))); + + // Now we swap: + assert_ok!(Club::exchange_member(RuntimeOrigin::root(), 0, 1)); + + System::reset_events(); + sp_io::storage::root(sp_runtime::StateVersion::V1) + }); + + assert_eq!(root_add, root_swap); + // Ensure that we dont compare trivial stuff like `()` from a type error above. + assert_eq!(root_add.len(), 32); + }); +} + +#[test] +fn swap_bad_noops() { + new_test_ext().execute_with(|| { + assert_ok!(Club::add_member(RuntimeOrigin::root(), 0)); + promote_n_times(0, 0); + assert_ok!(CoreFellowship::import(signed(0))); + assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); + promote_n_times(1, 1); + assert_ok!(CoreFellowship::import(signed(1))); + + // Swapping for another member is a noop: + assert_noop!( + Club::exchange_member(RuntimeOrigin::root(), 0, 1), + pallet_ranked_collective::Error::::AlreadyMember + ); + // Swapping for the same member is a noop: + assert_noop!( + Club::exchange_member(RuntimeOrigin::root(), 0, 0), + pallet_ranked_collective::Error::::SameMember + ); + }); +} diff --git a/substrate/frame/core-fellowship/src/tests/mod.rs b/substrate/frame/core-fellowship/src/tests/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..9ec619d0fb6b6aada7e6f535ce1038cacebf80aa --- /dev/null +++ b/substrate/frame/core-fellowship/src/tests/mod.rs @@ -0,0 +1,23 @@ +// 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(test)] + +//! Tests for this crate. + +pub(crate) mod integration; +pub(crate) mod unit; diff --git a/substrate/frame/core-fellowship/src/tests.rs b/substrate/frame/core-fellowship/src/tests/unit.rs similarity index 92% rename from substrate/frame/core-fellowship/src/tests.rs rename to substrate/frame/core-fellowship/src/tests/unit.rs index 838bd9bbdc80e57a760ea13693bf2c07fdcd62b4..de8cd858bdfc05d4881ff31331745906e001dc95 100644 --- a/substrate/frame/core-fellowship/src/tests.rs +++ b/substrate/frame/core-fellowship/src/tests/unit.rs @@ -19,22 +19,18 @@ use std::collections::BTreeMap; +use core::cell::RefCell; use frame_support::{ assert_noop, assert_ok, derive_impl, ord_parameter_types, pallet_prelude::Weight, parameter_types, - traits::{tokens::GetSalary, ConstU32, ConstU64, Everything, IsInVec, TryMapSuccess}, + traits::{tokens::GetSalary, ConstU32, IsInVec, TryMapSuccess}, }; use frame_system::EnsureSignedBy; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup, TryMorphInto}, - BuildStorage, DispatchError, DispatchResult, -}; -use sp_std::cell::RefCell; +use sp_runtime::{traits::TryMorphInto, BuildStorage, DispatchError, DispatchResult}; -use super::*; use crate as pallet_core_fellowship; +use crate::*; type Block = frame_system::mocking::MockBlock; @@ -53,29 +49,7 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } thread_local! { diff --git a/substrate/frame/core-fellowship/src/weights.rs b/substrate/frame/core-fellowship/src/weights.rs index 8bbfd1a4dd81da2146994f668d722f1e8afa27b6..fce1d3747a8bb283c1ccaf5b8d7be1c113d39d92 100644 --- a/substrate/frame/core-fellowship/src/weights.rs +++ b/substrate/frame/core-fellowship/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_core_fellowship +//! Autogenerated weights for `pallet_core_fellowship` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/core-fellowship/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/core-fellowship/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_core_fellowship. +/// Weight functions needed for `pallet_core_fellowship`. pub trait WeightInfo { fn set_params() -> Weight; fn bump_offboard() -> Weight; @@ -64,336 +63,344 @@ pub trait WeightInfo { fn submit_evidence() -> Weight; } -/// Weights for pallet_core_fellowship using the Substrate node and recommended hardware. +/// Weights for `pallet_core_fellowship` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: CoreFellowship Params (r:0 w:1) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Params` (r:0 w:1) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) fn set_params() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_454_000 picoseconds. - Weight::from_parts(9_804_000, 0) + // Minimum execution time: 7_146_000 picoseconds. + Weight::from_parts(7_426_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Params (r:1 w:0) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:1 w:0) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Params` (r:1 w:0) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:1 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn bump_offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `16887` + // Measured: `17274` // Estimated: `19894` - // Minimum execution time: 58_489_000 picoseconds. - Weight::from_parts(60_202_000, 19894) + // Minimum execution time: 54_511_000 picoseconds. + Weight::from_parts(56_995_000, 19894) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Params (r:1 w:0) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:1 w:0) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Params` (r:1 w:0) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:1 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn bump_demote() -> Weight { // Proof Size summary in bytes: - // Measured: `16997` + // Measured: `17384` // Estimated: `19894` - // Minimum execution time: 60_605_000 picoseconds. - Weight::from_parts(63_957_000, 19894) + // Minimum execution time: 56_453_000 picoseconds. + Weight::from_parts(59_030_000, 19894) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn set_active() -> Weight { // Proof Size summary in bytes: // Measured: `388` // Estimated: `3514` - // Minimum execution time: 17_816_000 picoseconds. - Weight::from_parts(18_524_000, 3514) + // Minimum execution time: 15_940_000 picoseconds. + Weight::from_parts(16_381_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `146` // Estimated: `3514` - // Minimum execution time: 27_249_000 picoseconds. - Weight::from_parts(28_049_000, 3514) + // Minimum execution time: 24_193_000 picoseconds. + Weight::from_parts(24_963_000, 3514) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship Params (r:1 w:0) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Params` (r:1 w:0) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn promote() -> Weight { // Proof Size summary in bytes: // Measured: `16865` // Estimated: `19894` - // Minimum execution time: 56_642_000 picoseconds. - Weight::from_parts(59_353_000, 19894) + // Minimum execution time: 48_138_000 picoseconds. + Weight::from_parts(50_007_000, 19894) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:0 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:0 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) fn offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `359` + // Measured: `293` // Estimated: `3514` - // Minimum execution time: 17_459_000 picoseconds. - Weight::from_parts(18_033_000, 3514) + // Minimum execution time: 15_225_000 picoseconds. + Weight::from_parts(15_730_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) fn import() -> Weight { // Proof Size summary in bytes: // Measured: `313` // Estimated: `3514` - // Minimum execution time: 16_728_000 picoseconds. - Weight::from_parts(17_263_000, 3514) + // Minimum execution time: 14_507_000 picoseconds. + Weight::from_parts(14_935_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) fn approve() -> Weight { // Proof Size summary in bytes: // Measured: `16843` // Estimated: `19894` - // Minimum execution time: 41_487_000 picoseconds. - Weight::from_parts(43_459_000, 19894) + // Minimum execution time: 34_050_000 picoseconds. + Weight::from_parts(36_323_000, 19894) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: CoreFellowship Member (r:1 w:0) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:0) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) fn submit_evidence() -> Weight { // Proof Size summary in bytes: // Measured: `79` // Estimated: `19894` - // Minimum execution time: 26_033_000 picoseconds. - Weight::from_parts(26_612_000, 19894) + // Minimum execution time: 24_016_000 picoseconds. + Weight::from_parts(24_607_000, 19894) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: CoreFellowship Params (r:0 w:1) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Params` (r:0 w:1) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) fn set_params() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_454_000 picoseconds. - Weight::from_parts(9_804_000, 0) + // Minimum execution time: 7_146_000 picoseconds. + Weight::from_parts(7_426_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Params (r:1 w:0) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:1 w:0) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Params` (r:1 w:0) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:1 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn bump_offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `16887` + // Measured: `17274` // Estimated: `19894` - // Minimum execution time: 58_489_000 picoseconds. - Weight::from_parts(60_202_000, 19894) + // Minimum execution time: 54_511_000 picoseconds. + Weight::from_parts(56_995_000, 19894) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Params (r:1 w:0) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:1 w:0) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Params` (r:1 w:0) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:1 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn bump_demote() -> Weight { // Proof Size summary in bytes: - // Measured: `16997` + // Measured: `17384` // Estimated: `19894` - // Minimum execution time: 60_605_000 picoseconds. - Weight::from_parts(63_957_000, 19894) + // Minimum execution time: 56_453_000 picoseconds. + Weight::from_parts(59_030_000, 19894) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn set_active() -> Weight { // Proof Size summary in bytes: // Measured: `388` // Estimated: `3514` - // Minimum execution time: 17_816_000 picoseconds. - Weight::from_parts(18_524_000, 3514) + // Minimum execution time: 15_940_000 picoseconds. + Weight::from_parts(16_381_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `146` // Estimated: `3514` - // Minimum execution time: 27_249_000 picoseconds. - Weight::from_parts(28_049_000, 3514) + // Minimum execution time: 24_193_000 picoseconds. + Weight::from_parts(24_963_000, 3514) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship Params (r:1 w:0) - /// Proof: CoreFellowship Params (max_values: Some(1), max_size: Some(364), added: 859, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Params` (r:1 w:0) + /// Proof: `CoreFellowship::Params` (`max_values`: Some(1), `max_size`: Some(364), added: 859, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn promote() -> Weight { // Proof Size summary in bytes: // Measured: `16865` // Estimated: `19894` - // Minimum execution time: 56_642_000 picoseconds. - Weight::from_parts(59_353_000, 19894) + // Minimum execution time: 48_138_000 picoseconds. + Weight::from_parts(50_007_000, 19894) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:0 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:0 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) fn offboard() -> Weight { // Proof Size summary in bytes: - // Measured: `359` + // Measured: `293` // Estimated: `3514` - // Minimum execution time: 17_459_000 picoseconds. - Weight::from_parts(18_033_000, 3514) + // Minimum execution time: 15_225_000 picoseconds. + Weight::from_parts(15_730_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) fn import() -> Weight { // Proof Size summary in bytes: // Measured: `313` // Estimated: `3514` - // Minimum execution time: 16_728_000 picoseconds. - Weight::from_parts(17_263_000, 3514) + // Minimum execution time: 14_507_000 picoseconds. + Weight::from_parts(14_935_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: CoreFellowship Member (r:1 w:1) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:1 w:1) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) fn approve() -> Weight { // Proof Size summary in bytes: // Measured: `16843` // Estimated: `19894` - // Minimum execution time: 41_487_000 picoseconds. - Weight::from_parts(43_459_000, 19894) + // Minimum execution time: 34_050_000 picoseconds. + Weight::from_parts(36_323_000, 19894) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: CoreFellowship Member (r:1 w:0) - /// Proof: CoreFellowship Member (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: CoreFellowship MemberEvidence (r:1 w:1) - /// Proof: CoreFellowship MemberEvidence (max_values: None, max_size: Some(16429), added: 18904, mode: MaxEncodedLen) + /// Storage: `CoreFellowship::Member` (r:1 w:0) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:1) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) fn submit_evidence() -> Weight { // Proof Size summary in bytes: // Measured: `79` // Estimated: `19894` - // Minimum execution time: 26_033_000 picoseconds. - Weight::from_parts(26_612_000, 19894) + // Minimum execution time: 24_016_000 picoseconds. + Weight::from_parts(24_607_000, 19894) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/democracy/Cargo.toml b/substrate/frame/democracy/Cargo.toml index cecb1df60d7f23b039db4ad06d85498bd021c473..9a55cda5340c2fa795fd79b17ee4e2b5f568bed2 100644 --- a/substrate/frame/democracy/Cargo.toml +++ b/substrate/frame/democracy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-democracy" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -20,7 +20,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", ] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", features = ["derive"], optional = true } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } @@ -28,7 +28,7 @@ sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] pallet-balances = { path = "../balances" } diff --git a/substrate/frame/democracy/src/conviction.rs b/substrate/frame/democracy/src/conviction.rs index d2f685f7d99efb79e12628ac38370fb69445a44d..54f4ff524f2a9be397a12eadb4bb0c9e88cbf501 100644 --- a/substrate/frame/democracy/src/conviction.rs +++ b/substrate/frame/democracy/src/conviction.rs @@ -19,12 +19,12 @@ use crate::types::Delegations; use codec::{Decode, Encode, MaxEncodedLen}; +use core::result::Result; use scale_info::TypeInfo; use sp_runtime::{ traits::{Bounded, CheckedDiv, CheckedMul, Zero}, RuntimeDebug, }; -use sp_std::{prelude::*, result::Result}; /// A value denoting the strength of conviction of a vote. #[derive( diff --git a/substrate/frame/democracy/src/lib.rs b/substrate/frame/democracy/src/lib.rs index 089556191cd14eee735d12976ba6b0ed471a4eac..08e2a7599f5542a8b87ce80df8d96b5cf4ee9af5 100644 --- a/substrate/frame/democracy/src/lib.rs +++ b/substrate/frame/democracy/src/lib.rs @@ -211,7 +211,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/democracy/src/migrations/v1.rs b/substrate/frame/democracy/src/migrations/v1.rs index 64baea8f3af7039eb8a2422b48e897aaaa93a664..5e423b9ab6eff7d2a96f94416bdd425827869fba 100644 --- a/substrate/frame/democracy/src/migrations/v1.rs +++ b/substrate/frame/democracy/src/migrations/v1.rs @@ -54,7 +54,7 @@ pub mod v1 { use super::*; /// Migration for translating bare `Hash`es into `Bounded`s. - pub struct Migration(sp_std::marker::PhantomData); + pub struct Migration(core::marker::PhantomData); impl> OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] diff --git a/substrate/frame/democracy/src/tests.rs b/substrate/frame/democracy/src/tests.rs index 888d7e596146fbc100042c4447afe0576de4b6b2..973e0c28eb2f7307d8e18ff3470c7df8d106a609 100644 --- a/substrate/frame/democracy/src/tests.rs +++ b/substrate/frame/democracy/src/tests.rs @@ -29,9 +29,8 @@ use frame_support::{ }; use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; use pallet_balances::{BalanceLock, Error as BalancesError}; -use sp_core::H256; use sp_runtime::{ - traits::{BadOrigin, BlakeTwo256, Hash, IdentityLookup}, + traits::{BadOrigin, BlakeTwo256, Hash}, BuildStorage, Perbill, }; mod cancellation; @@ -81,28 +80,8 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = BaseFilter; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; @@ -143,7 +122,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { pub static PreimageByteDeposit: u64 = 0; diff --git a/substrate/frame/democracy/src/vote_threshold.rs b/substrate/frame/democracy/src/vote_threshold.rs index e8efa179ed8bf1ad68ac34aa2f19d90985143125..82d6ed178f13783f2e9495ce064378b3f8de5ecf 100644 --- a/substrate/frame/democracy/src/vote_threshold.rs +++ b/substrate/frame/democracy/src/vote_threshold.rs @@ -19,11 +19,11 @@ use crate::Tally; use codec::{Decode, Encode, MaxEncodedLen}; +use core::ops::{Add, Div, Mul, Rem}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_runtime::traits::{IntegerSquareRoot, Zero}; -use sp_std::ops::{Add, Div, Mul, Rem}; /// A means of determining if a vote is past pass threshold. #[derive( diff --git a/substrate/frame/democracy/src/weights.rs b/substrate/frame/democracy/src/weights.rs index 241f6c3cb38de2c270411cd2d39913f857bbe807..d6097922a82f2cfed77f974dcbaaa64a69c8d82d 100644 --- a/substrate/frame/democracy/src/weights.rs +++ b/substrate/frame/democracy/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_democracy +//! Autogenerated weights for `pallet_democracy` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/democracy/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/democracy/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_democracy. +/// Weight functions needed for `pallet_democracy`. pub trait WeightInfo { fn propose() -> Weight; fn second() -> Weight; @@ -82,904 +81,916 @@ pub trait WeightInfo { fn clear_referendum_metadata() -> Weight; } -/// Weights for pallet_democracy using the Substrate node and recommended hardware. +/// Weights for `pallet_democracy` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Democracy PublicPropCount (r:1 w:1) - /// Proof: Democracy PublicPropCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy PublicProps (r:1 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:1 w:0) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) - /// Storage: Democracy DepositOf (r:0 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicPropCount` (r:1 w:1) + /// Proof: `Democracy::PublicPropCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::PublicProps` (r:1 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:1 w:0) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) + /// Storage: `Democracy::DepositOf` (r:0 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) fn propose() -> Weight { // Proof Size summary in bytes: - // Measured: `4801` + // Measured: `4834` // Estimated: `18187` - // Minimum execution time: 49_339_000 picoseconds. - Weight::from_parts(50_942_000, 18187) + // Minimum execution time: 39_930_000 picoseconds. + Weight::from_parts(41_746_000, 18187) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Democracy DepositOf (r:1 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: `Democracy::DepositOf` (r:1 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) fn second() -> Weight { // Proof Size summary in bytes: - // Measured: `3556` + // Measured: `3589` // Estimated: `6695` - // Minimum execution time: 43_291_000 picoseconds. - Weight::from_parts(44_856_000, 6695) + // Minimum execution time: 36_490_000 picoseconds. + Weight::from_parts(37_615_000, 6695) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `3470` + // Measured: `3503` // Estimated: `7260` - // Minimum execution time: 61_890_000 picoseconds. - Weight::from_parts(63_626_000, 7260) + // Minimum execution time: 54_257_000 picoseconds. + Weight::from_parts(55_912_000, 7260) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `3492` + // Measured: `3525` // Estimated: `7260` - // Minimum execution time: 67_802_000 picoseconds. - Weight::from_parts(69_132_000, 7260) + // Minimum execution time: 56_878_000 picoseconds. + Weight::from_parts(58_796_000, 7260) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy Cancellations (r:1 w:1) - /// Proof: Democracy Cancellations (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Cancellations` (r:1 w:1) + /// Proof: `Democracy::Cancellations` (`max_values`: None, `max_size`: Some(33), added: 2508, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn emergency_cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `366` + // Measured: `399` // Estimated: `3666` - // Minimum execution time: 25_757_000 picoseconds. - Weight::from_parts(27_226_000, 3666) + // Minimum execution time: 22_700_000 picoseconds. + Weight::from_parts(23_539_000, 3666) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Democracy PublicProps (r:1 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy DepositOf (r:1 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:3 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:0 w:1) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::DepositOf` (r:1 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:3 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:0 w:1) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) fn blacklist() -> Weight { // Proof Size summary in bytes: - // Measured: `5910` + // Measured: `5943` // Estimated: `18187` - // Minimum execution time: 113_060_000 picoseconds. - Weight::from_parts(114_813_000, 18187) + // Minimum execution time: 95_398_000 picoseconds. + Weight::from_parts(97_261_000, 18187) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:1 w:0) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:1 w:0) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) fn external_propose() -> Weight { // Proof Size summary in bytes: - // Measured: `3416` + // Measured: `3449` // Estimated: `6703` - // Minimum execution time: 13_413_000 picoseconds. - Weight::from_parts(13_794_000, 6703) + // Minimum execution time: 11_745_000 picoseconds. + Weight::from_parts(12_304_000, 6703) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:0 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:0 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) fn external_propose_majority() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_213_000 picoseconds. - Weight::from_parts(3_429_000, 0) + // Minimum execution time: 2_710_000 picoseconds. + Weight::from_parts(2_918_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:0 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:0 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) fn external_propose_default() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_280_000 picoseconds. - Weight::from_parts(3_389_000, 0) + // Minimum execution time: 2_664_000 picoseconds. + Weight::from_parts(2_776_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumCount (r:1 w:1) - /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:2) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:0 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumCount` (r:1 w:1) + /// Proof: `Democracy::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:2) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:0 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) fn fast_track() -> Weight { // Proof Size summary in bytes: - // Measured: `286` + // Measured: `319` // Estimated: `3518` - // Minimum execution time: 28_142_000 picoseconds. - Weight::from_parts(28_862_000, 3518) + // Minimum execution time: 22_585_000 picoseconds. + Weight::from_parts(23_689_000, 3518) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:1 w:1) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:1 w:1) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn veto_external() -> Weight { // Proof Size summary in bytes: - // Measured: `3519` + // Measured: `3552` // Estimated: `6703` - // Minimum execution time: 32_395_000 picoseconds. - Weight::from_parts(33_617_000, 6703) + // Minimum execution time: 26_391_000 picoseconds. + Weight::from_parts(27_141_000, 6703) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Democracy PublicProps (r:1 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy DepositOf (r:1 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::DepositOf` (r:1 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn cancel_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `5821` + // Measured: `5854` // Estimated: `18187` - // Minimum execution time: 92_255_000 picoseconds. - Weight::from_parts(93_704_000, 18187) + // Minimum execution time: 77_905_000 picoseconds. + Weight::from_parts(79_628_000, 18187) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:0 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:0 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) fn cancel_referendum() -> Weight { // Proof Size summary in bytes: - // Measured: `271` + // Measured: `304` // Estimated: `3518` - // Minimum execution time: 19_623_000 picoseconds. - Weight::from_parts(20_545_000, 3518) + // Minimum execution time: 15_735_000 picoseconds. + Weight::from_parts(16_525_000, 3518) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Democracy LowestUnbaked (r:1 w:1) - /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumCount (r:1 w:0) - /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:0) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::LowestUnbaked` (r:1 w:1) + /// Proof: `Democracy::LowestUnbaked` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumCount` (r:1 w:0) + /// Proof: `Democracy::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:0) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (86 ±0)` + // Measured: `277 + r * (86 ±0)` // Estimated: `1489 + r * (2676 ±0)` - // Minimum execution time: 7_032_000 picoseconds. - Weight::from_parts(7_931_421, 1489) - // Standard Error: 7_395 - .saturating_add(Weight::from_parts(3_236_964, 0).saturating_mul(r.into())) + // Minimum execution time: 5_274_000 picoseconds. + Weight::from_parts(6_162_399, 1489) + // Standard Error: 6_924 + .saturating_add(Weight::from_parts(3_186_702, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy LowestUnbaked (r:1 w:1) - /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumCount (r:1 w:0) - /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy LastTabledWasExternal (r:1 w:0) - /// Proof: Democracy LastTabledWasExternal (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Democracy NextExternal (r:1 w:0) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy PublicProps (r:1 w:0) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:0) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::LowestUnbaked` (r:1 w:1) + /// Proof: `Democracy::LowestUnbaked` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumCount` (r:1 w:0) + /// Proof: `Democracy::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::LastTabledWasExternal` (r:1 w:0) + /// Proof: `Democracy::LastTabledWasExternal` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `Democracy::NextExternal` (r:1 w:0) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::PublicProps` (r:1 w:0) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:0) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (86 ±0)` + // Measured: `277 + r * (86 ±0)` // Estimated: `18187 + r * (2676 ±0)` - // Minimum execution time: 10_524_000 picoseconds. - Weight::from_parts(10_369_064, 18187) - // Standard Error: 8_385 - .saturating_add(Weight::from_parts(3_242_334, 0).saturating_mul(r.into())) + // Minimum execution time: 7_950_000 picoseconds. + Weight::from_parts(7_381_228, 18187) + // Standard Error: 6_650 + .saturating_add(Weight::from_parts(3_198_515, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy VotingOf (r:3 w:3) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:99) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:3 w:3) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:99) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `830 + r * (108 ±0)` + // Measured: `863 + r * (108 ±0)` // Estimated: `19800 + r * (2676 ±0)` - // Minimum execution time: 46_106_000 picoseconds. - Weight::from_parts(48_936_654, 19800) - // Standard Error: 8_879 - .saturating_add(Weight::from_parts(4_708_141, 0).saturating_mul(r.into())) + // Minimum execution time: 40_109_000 picoseconds. + Weight::from_parts(43_164_384, 19800) + // Standard Error: 7_267 + .saturating_add(Weight::from_parts(4_161_526, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy VotingOf (r:2 w:2) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:99) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:2 w:2) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:99) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `493 + r * (108 ±0)` + // Measured: `526 + r * (108 ±0)` // Estimated: `13530 + r * (2676 ±0)` - // Minimum execution time: 21_078_000 picoseconds. - Weight::from_parts(22_732_737, 13530) - // Standard Error: 7_969 - .saturating_add(Weight::from_parts(4_626_458, 0).saturating_mul(r.into())) + // Minimum execution time: 17_466_000 picoseconds. + Weight::from_parts(18_004_456, 13530) + // Standard Error: 6_327 + .saturating_add(Weight::from_parts(4_194_583, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy PublicProps (r:0 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:0 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) fn clear_public_proposals() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_229_000 picoseconds. - Weight::from_parts(3_415_000, 0) + // Minimum execution time: 2_824_000 picoseconds. + Weight::from_parts(2_948_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, 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 `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `596` // Estimated: `7260` - // Minimum execution time: 25_735_000 picoseconds. - Weight::from_parts(41_341_468, 7260) - // Standard Error: 3_727 - .saturating_add(Weight::from_parts(94_755, 0).saturating_mul(r.into())) + // Minimum execution time: 23_373_000 picoseconds. + Weight::from_parts(34_306_582, 7260) + // Standard Error: 2_849 + .saturating_add(Weight::from_parts(85_027, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, 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 `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `564 + r * (22 ±0)` + // Measured: `597 + r * (22 ±0)` // Estimated: `7260` - // Minimum execution time: 36_233_000 picoseconds. - Weight::from_parts(39_836_017, 7260) - // Standard Error: 1_791 - .saturating_add(Weight::from_parts(132_158, 0).saturating_mul(r.into())) + // Minimum execution time: 31_574_000 picoseconds. + Weight::from_parts(33_906_658, 7260) + // Standard Error: 1_514 + .saturating_add(Weight::from_parts(124_471, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `728 + r * (26 ±0)` + // Measured: `761 + r * (26 ±0)` // Estimated: `7260` - // Minimum execution time: 16_081_000 picoseconds. - Weight::from_parts(19_624_101, 7260) - // Standard Error: 1_639 - .saturating_add(Weight::from_parts(133_630, 0).saturating_mul(r.into())) + // Minimum execution time: 15_204_000 picoseconds. + Weight::from_parts(18_405_879, 7260) + // Standard Error: 1_851 + .saturating_add(Weight::from_parts(119_018, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `728 + r * (26 ±0)` + // Measured: `761 + r * (26 ±0)` // Estimated: `7260` - // Minimum execution time: 15_634_000 picoseconds. - Weight::from_parts(19_573_407, 7260) - // Standard Error: 1_790 - .saturating_add(Weight::from_parts(139_707, 0).saturating_mul(r.into())) + // Minimum execution time: 15_120_000 picoseconds. + Weight::from_parts(18_282_222, 7260) + // Standard Error: 1_669 + .saturating_add(Weight::from_parts(127_649, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Democracy NextExternal (r:1 w:0) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:0 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:0) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:0 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn set_external_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `356` + // Measured: `456` // Estimated: `3556` - // Minimum execution time: 18_344_000 picoseconds. - Weight::from_parts(18_727_000, 3556) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Minimum execution time: 17_351_000 picoseconds. + Weight::from_parts(17_964_000, 3556) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:1 w:0) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:0) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn clear_external_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `286` + // Measured: `319` // Estimated: `3518` - // Minimum execution time: 16_497_000 picoseconds. - Weight::from_parts(16_892_000, 3518) + // Minimum execution time: 13_669_000 picoseconds. + Weight::from_parts(14_410_000, 3518) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy PublicProps (r:1 w:0) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:0 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:0) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:0 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn set_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4888` + // Measured: `4988` // Estimated: `18187` - // Minimum execution time: 39_517_000 picoseconds. - Weight::from_parts(40_632_000, 18187) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Minimum execution time: 39_162_000 picoseconds. + Weight::from_parts(40_109_000, 18187) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy PublicProps (r:1 w:0) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:0) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn clear_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4822` + // Measured: `4855` // Estimated: `18187` - // Minimum execution time: 37_108_000 picoseconds. - Weight::from_parts(37_599_000, 18187) + // Minimum execution time: 34_141_000 picoseconds. + Weight::from_parts(34_732_000, 18187) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:0 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:0 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn set_referendum_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 13_997_000 picoseconds. - Weight::from_parts(14_298_000, 3556) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Minimum execution time: 13_413_000 picoseconds. + Weight::from_parts(14_039_000, 3556) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:0) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:0) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn clear_referendum_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `302` + // Measured: `335` // Estimated: `3666` - // Minimum execution time: 18_122_000 picoseconds. - Weight::from_parts(18_655_000, 3666) + // Minimum execution time: 16_010_000 picoseconds. + Weight::from_parts(16_474_000, 3666) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Democracy PublicPropCount (r:1 w:1) - /// Proof: Democracy PublicPropCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy PublicProps (r:1 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:1 w:0) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) - /// Storage: Democracy DepositOf (r:0 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicPropCount` (r:1 w:1) + /// Proof: `Democracy::PublicPropCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::PublicProps` (r:1 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:1 w:0) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) + /// Storage: `Democracy::DepositOf` (r:0 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) fn propose() -> Weight { // Proof Size summary in bytes: - // Measured: `4801` + // Measured: `4834` // Estimated: `18187` - // Minimum execution time: 49_339_000 picoseconds. - Weight::from_parts(50_942_000, 18187) + // Minimum execution time: 39_930_000 picoseconds. + Weight::from_parts(41_746_000, 18187) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Democracy DepositOf (r:1 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) + /// Storage: `Democracy::DepositOf` (r:1 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) fn second() -> Weight { // Proof Size summary in bytes: - // Measured: `3556` + // Measured: `3589` // Estimated: `6695` - // Minimum execution time: 43_291_000 picoseconds. - Weight::from_parts(44_856_000, 6695) + // Minimum execution time: 36_490_000 picoseconds. + Weight::from_parts(37_615_000, 6695) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `3470` + // Measured: `3503` // Estimated: `7260` - // Minimum execution time: 61_890_000 picoseconds. - Weight::from_parts(63_626_000, 7260) + // Minimum execution time: 54_257_000 picoseconds. + Weight::from_parts(55_912_000, 7260) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `3492` + // Measured: `3525` // Estimated: `7260` - // Minimum execution time: 67_802_000 picoseconds. - Weight::from_parts(69_132_000, 7260) + // Minimum execution time: 56_878_000 picoseconds. + Weight::from_parts(58_796_000, 7260) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy Cancellations (r:1 w:1) - /// Proof: Democracy Cancellations (max_values: None, max_size: Some(33), added: 2508, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Cancellations` (r:1 w:1) + /// Proof: `Democracy::Cancellations` (`max_values`: None, `max_size`: Some(33), added: 2508, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn emergency_cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `366` + // Measured: `399` // Estimated: `3666` - // Minimum execution time: 25_757_000 picoseconds. - Weight::from_parts(27_226_000, 3666) + // Minimum execution time: 22_700_000 picoseconds. + Weight::from_parts(23_539_000, 3666) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Democracy PublicProps (r:1 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy DepositOf (r:1 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:3 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:0 w:1) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::DepositOf` (r:1 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:3 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:0 w:1) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) fn blacklist() -> Weight { // Proof Size summary in bytes: - // Measured: `5910` + // Measured: `5943` // Estimated: `18187` - // Minimum execution time: 113_060_000 picoseconds. - Weight::from_parts(114_813_000, 18187) + // Minimum execution time: 95_398_000 picoseconds. + Weight::from_parts(97_261_000, 18187) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:1 w:0) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:1 w:0) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) fn external_propose() -> Weight { // Proof Size summary in bytes: - // Measured: `3416` + // Measured: `3449` // Estimated: `6703` - // Minimum execution time: 13_413_000 picoseconds. - Weight::from_parts(13_794_000, 6703) + // Minimum execution time: 11_745_000 picoseconds. + Weight::from_parts(12_304_000, 6703) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:0 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:0 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) fn external_propose_majority() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_213_000 picoseconds. - Weight::from_parts(3_429_000, 0) + // Minimum execution time: 2_710_000 picoseconds. + Weight::from_parts(2_918_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:0 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:0 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) fn external_propose_default() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_280_000 picoseconds. - Weight::from_parts(3_389_000, 0) + // Minimum execution time: 2_664_000 picoseconds. + Weight::from_parts(2_776_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumCount (r:1 w:1) - /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:2) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:0 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumCount` (r:1 w:1) + /// Proof: `Democracy::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:2) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:0 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) fn fast_track() -> Weight { // Proof Size summary in bytes: - // Measured: `286` + // Measured: `319` // Estimated: `3518` - // Minimum execution time: 28_142_000 picoseconds. - Weight::from_parts(28_862_000, 3518) + // Minimum execution time: 22_585_000 picoseconds. + Weight::from_parts(23_689_000, 3518) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Democracy NextExternal (r:1 w:1) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy Blacklist (r:1 w:1) - /// Proof: Democracy Blacklist (max_values: None, max_size: Some(3238), added: 5713, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:1) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::Blacklist` (r:1 w:1) + /// Proof: `Democracy::Blacklist` (`max_values`: None, `max_size`: Some(3238), added: 5713, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn veto_external() -> Weight { // Proof Size summary in bytes: - // Measured: `3519` + // Measured: `3552` // Estimated: `6703` - // Minimum execution time: 32_395_000 picoseconds. - Weight::from_parts(33_617_000, 6703) + // Minimum execution time: 26_391_000 picoseconds. + Weight::from_parts(27_141_000, 6703) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Democracy PublicProps (r:1 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy DepositOf (r:1 w:1) - /// Proof: Democracy DepositOf (max_values: None, max_size: Some(3230), added: 5705, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::DepositOf` (r:1 w:1) + /// Proof: `Democracy::DepositOf` (`max_values`: None, `max_size`: Some(3230), added: 5705, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn cancel_proposal() -> Weight { // Proof Size summary in bytes: - // Measured: `5821` + // Measured: `5854` // Estimated: `18187` - // Minimum execution time: 92_255_000 picoseconds. - Weight::from_parts(93_704_000, 18187) + // Minimum execution time: 77_905_000 picoseconds. + Weight::from_parts(79_628_000, 18187) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:0 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:0 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) fn cancel_referendum() -> Weight { // Proof Size summary in bytes: - // Measured: `271` + // Measured: `304` // Estimated: `3518` - // Minimum execution time: 19_623_000 picoseconds. - Weight::from_parts(20_545_000, 3518) + // Minimum execution time: 15_735_000 picoseconds. + Weight::from_parts(16_525_000, 3518) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Democracy LowestUnbaked (r:1 w:1) - /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumCount (r:1 w:0) - /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:0) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::LowestUnbaked` (r:1 w:1) + /// Proof: `Democracy::LowestUnbaked` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumCount` (r:1 w:0) + /// Proof: `Democracy::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:0) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (86 ±0)` + // Measured: `277 + r * (86 ±0)` // Estimated: `1489 + r * (2676 ±0)` - // Minimum execution time: 7_032_000 picoseconds. - Weight::from_parts(7_931_421, 1489) - // Standard Error: 7_395 - .saturating_add(Weight::from_parts(3_236_964, 0).saturating_mul(r.into())) + // Minimum execution time: 5_274_000 picoseconds. + Weight::from_parts(6_162_399, 1489) + // Standard Error: 6_924 + .saturating_add(Weight::from_parts(3_186_702, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy LowestUnbaked (r:1 w:1) - /// Proof: Democracy LowestUnbaked (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumCount (r:1 w:0) - /// Proof: Democracy ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Democracy LastTabledWasExternal (r:1 w:0) - /// Proof: Democracy LastTabledWasExternal (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: Democracy NextExternal (r:1 w:0) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy PublicProps (r:1 w:0) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:0) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::LowestUnbaked` (r:1 w:1) + /// Proof: `Democracy::LowestUnbaked` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumCount` (r:1 w:0) + /// Proof: `Democracy::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Democracy::LastTabledWasExternal` (r:1 w:0) + /// Proof: `Democracy::LastTabledWasExternal` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `Democracy::NextExternal` (r:1 w:0) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::PublicProps` (r:1 w:0) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:0) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `244 + r * (86 ±0)` + // Measured: `277 + r * (86 ±0)` // Estimated: `18187 + r * (2676 ±0)` - // Minimum execution time: 10_524_000 picoseconds. - Weight::from_parts(10_369_064, 18187) - // Standard Error: 8_385 - .saturating_add(Weight::from_parts(3_242_334, 0).saturating_mul(r.into())) + // Minimum execution time: 7_950_000 picoseconds. + Weight::from_parts(7_381_228, 18187) + // Standard Error: 6_650 + .saturating_add(Weight::from_parts(3_198_515, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy VotingOf (r:3 w:3) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:99) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:3 w:3) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:99) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `830 + r * (108 ±0)` + // Measured: `863 + r * (108 ±0)` // Estimated: `19800 + r * (2676 ±0)` - // Minimum execution time: 46_106_000 picoseconds. - Weight::from_parts(48_936_654, 19800) - // Standard Error: 8_879 - .saturating_add(Weight::from_parts(4_708_141, 0).saturating_mul(r.into())) + // Minimum execution time: 40_109_000 picoseconds. + Weight::from_parts(43_164_384, 19800) + // Standard Error: 7_267 + .saturating_add(Weight::from_parts(4_161_526, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy VotingOf (r:2 w:2) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Democracy ReferendumInfoOf (r:99 w:99) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:2 w:2) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Democracy::ReferendumInfoOf` (r:99 w:99) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `493 + r * (108 ±0)` + // Measured: `526 + r * (108 ±0)` // Estimated: `13530 + r * (2676 ±0)` - // Minimum execution time: 21_078_000 picoseconds. - Weight::from_parts(22_732_737, 13530) - // Standard Error: 7_969 - .saturating_add(Weight::from_parts(4_626_458, 0).saturating_mul(r.into())) + // Minimum execution time: 17_466_000 picoseconds. + Weight::from_parts(18_004_456, 13530) + // Standard Error: 6_327 + .saturating_add(Weight::from_parts(4_194_583, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 2676).saturating_mul(r.into())) } - /// Storage: Democracy PublicProps (r:0 w:1) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:0 w:1) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) fn clear_public_proposals() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_229_000 picoseconds. - Weight::from_parts(3_415_000, 0) + // Minimum execution time: 2_824_000 picoseconds. + Weight::from_parts(2_948_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, 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 `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563` + // Measured: `596` // Estimated: `7260` - // Minimum execution time: 25_735_000 picoseconds. - Weight::from_parts(41_341_468, 7260) - // Standard Error: 3_727 - .saturating_add(Weight::from_parts(94_755, 0).saturating_mul(r.into())) + // Minimum execution time: 23_373_000 picoseconds. + Weight::from_parts(34_306_582, 7260) + // Standard Error: 2_849 + .saturating_add(Weight::from_parts(85_027, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, 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 `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `564 + r * (22 ±0)` + // Measured: `597 + r * (22 ±0)` // Estimated: `7260` - // Minimum execution time: 36_233_000 picoseconds. - Weight::from_parts(39_836_017, 7260) - // Standard Error: 1_791 - .saturating_add(Weight::from_parts(132_158, 0).saturating_mul(r.into())) + // Minimum execution time: 31_574_000 picoseconds. + Weight::from_parts(33_906_658, 7260) + // Standard Error: 1_514 + .saturating_add(Weight::from_parts(124_471, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `728 + r * (26 ±0)` + // Measured: `761 + r * (26 ±0)` // Estimated: `7260` - // Minimum execution time: 16_081_000 picoseconds. - Weight::from_parts(19_624_101, 7260) - // Standard Error: 1_639 - .saturating_add(Weight::from_parts(133_630, 0).saturating_mul(r.into())) + // Minimum execution time: 15_204_000 picoseconds. + Weight::from_parts(18_405_879, 7260) + // Standard Error: 1_851 + .saturating_add(Weight::from_parts(119_018, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:1) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy VotingOf (r:1 w:1) - /// Proof: Democracy VotingOf (max_values: None, max_size: Some(3795), added: 6270, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:1) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::VotingOf` (r:1 w:1) + /// Proof: `Democracy::VotingOf` (`max_values`: None, `max_size`: Some(3795), added: 6270, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `728 + r * (26 ±0)` + // Measured: `761 + r * (26 ±0)` // Estimated: `7260` - // Minimum execution time: 15_634_000 picoseconds. - Weight::from_parts(19_573_407, 7260) - // Standard Error: 1_790 - .saturating_add(Weight::from_parts(139_707, 0).saturating_mul(r.into())) + // Minimum execution time: 15_120_000 picoseconds. + Weight::from_parts(18_282_222, 7260) + // Standard Error: 1_669 + .saturating_add(Weight::from_parts(127_649, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Democracy NextExternal (r:1 w:0) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:0 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:0) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:0 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn set_external_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `356` + // Measured: `456` // Estimated: `3556` - // Minimum execution time: 18_344_000 picoseconds. - Weight::from_parts(18_727_000, 3556) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Minimum execution time: 17_351_000 picoseconds. + Weight::from_parts(17_964_000, 3556) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy NextExternal (r:1 w:0) - /// Proof: Democracy NextExternal (max_values: Some(1), max_size: Some(132), added: 627, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::NextExternal` (r:1 w:0) + /// Proof: `Democracy::NextExternal` (`max_values`: Some(1), `max_size`: Some(132), added: 627, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn clear_external_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `286` + // Measured: `319` // Estimated: `3518` - // Minimum execution time: 16_497_000 picoseconds. - Weight::from_parts(16_892_000, 3518) + // Minimum execution time: 13_669_000 picoseconds. + Weight::from_parts(14_410_000, 3518) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy PublicProps (r:1 w:0) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:0 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:0) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:0 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn set_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4888` + // Measured: `4988` // Estimated: `18187` - // Minimum execution time: 39_517_000 picoseconds. - Weight::from_parts(40_632_000, 18187) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Minimum execution time: 39_162_000 picoseconds. + Weight::from_parts(40_109_000, 18187) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy PublicProps (r:1 w:0) - /// Proof: Democracy PublicProps (max_values: Some(1), max_size: Some(16702), added: 17197, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::PublicProps` (r:1 w:0) + /// Proof: `Democracy::PublicProps` (`max_values`: Some(1), `max_size`: Some(16702), added: 17197, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn clear_proposal_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `4822` + // Measured: `4855` // Estimated: `18187` - // Minimum execution time: 37_108_000 picoseconds. - Weight::from_parts(37_599_000, 18187) + // Minimum execution time: 34_141_000 picoseconds. + Weight::from_parts(34_732_000, 18187) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:0 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:0 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn set_referendum_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 13_997_000 picoseconds. - Weight::from_parts(14_298_000, 3556) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Minimum execution time: 13_413_000 picoseconds. + Weight::from_parts(14_039_000, 3556) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Democracy ReferendumInfoOf (r:1 w:0) - /// Proof: Democracy ReferendumInfoOf (max_values: None, max_size: Some(201), added: 2676, mode: MaxEncodedLen) - /// Storage: Democracy MetadataOf (r:1 w:1) - /// Proof: Democracy MetadataOf (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: `Democracy::ReferendumInfoOf` (r:1 w:0) + /// Proof: `Democracy::ReferendumInfoOf` (`max_values`: None, `max_size`: Some(201), added: 2676, mode: `MaxEncodedLen`) + /// Storage: `Democracy::MetadataOf` (r:1 w:1) + /// Proof: `Democracy::MetadataOf` (`max_values`: None, `max_size`: Some(53), added: 2528, mode: `MaxEncodedLen`) fn clear_referendum_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `302` + // Measured: `335` // Estimated: `3666` - // Minimum execution time: 18_122_000 picoseconds. - Weight::from_parts(18_655_000, 3666) + // Minimum execution time: 16_010_000 picoseconds. + Weight::from_parts(16_474_000, 3666) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/election-provider-multi-phase/Cargo.toml b/substrate/frame/election-provider-multi-phase/Cargo.toml index be3a77065b433ee02af76879988b98b212c06546..eadce8c1ff847b2902d2d3ca9eb21d797060da4f 100644 --- a/substrate/frame/election-provider-multi-phase/Cargo.toml +++ b/substrate/frame/election-provider-multi-phase/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-election-provider-multi-phase" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,7 +21,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = scale-info = { version = "2.10.0", default-features = false, features = [ "derive", ] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/election-provider-multi-phase/src/benchmarking.rs b/substrate/frame/election-provider-multi-phase/src/benchmarking.rs index 4a2855f1361f2c7f098a023e74344e1062be7506..957ae51b8f1daf89eb47045c82ca09a6a64bdf93 100644 --- a/substrate/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/substrate/frame/election-provider-multi-phase/src/benchmarking.rs @@ -496,7 +496,7 @@ frame_benchmarking::benchmarks! { let (_, stake, _) = voters[*idx]; stake }).unwrap_or_default(); - sp_std::cmp::Reverse(stake) + core::cmp::Reverse(stake) }); let mut index_assignments = assignments diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index 04325a40d0ada022b3a875e20df0d4143bbf0b44..6bf4dfe4f1eb993e35a99a34bd21b2bde81d4355 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -274,14 +274,14 @@ pub mod migrations; pub mod signed; pub mod unsigned; pub mod weights; -use unsigned::VoterOf; -pub use weights::WeightInfo; pub use signed::{ BalanceOf, GeometricDepositBase, NegativeImbalanceOf, PositiveImbalanceOf, SignedSubmission, SignedSubmissionOf, SignedSubmissions, SubmissionIndicesOf, }; +use unsigned::VoterOf; pub use unsigned::{Miner, MinerConfig}; +pub use weights::WeightInfo; /// The solution type used by this crate. pub type SolutionOf = ::Solution; @@ -1275,6 +1275,7 @@ pub mod pallet { /// Snapshot data of the round. /// /// This is created at the beginning of the signed phase and cleared upon calling `elect`. + /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. #[pallet::storage] #[pallet::getter(fn snapshot)] pub type Snapshot = StorageValue<_, RoundSnapshot>>; @@ -1282,6 +1283,7 @@ pub mod pallet { /// Desired number of targets to elect for this round. /// /// Only exists when [`Snapshot`] is present. + /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. #[pallet::storage] #[pallet::getter(fn desired_targets)] pub type DesiredTargets = StorageValue<_, u32>; @@ -1289,6 +1291,7 @@ pub mod pallet { /// The metadata of the [`RoundSnapshot`] /// /// Only exists when [`Snapshot`] is present. + /// Note: This storage type must only be mutated through [`SnapshotWrapper`]. #[pallet::storage] #[pallet::getter(fn snapshot_metadata)] pub type SnapshotMetadata = StorageValue<_, SolutionOrSnapshotSize>; @@ -1340,7 +1343,7 @@ pub mod pallet { #[pallet::getter(fn minimum_untrusted_score)] pub type MinimumUntrustedScore = StorageValue<_, ElectionScore>; - /// The current storage version. + /// The in-code storage version. /// /// v1: https://github.com/paritytech/substrate/pull/12237/ const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -1351,6 +1354,39 @@ pub mod pallet { pub struct Pallet(_); } +/// This wrapper is created for handling the synchronization of [`Snapshot`], [`SnapshotMetadata`] +/// and [`DesiredTargets`] storage items. +pub struct SnapshotWrapper(sp_std::marker::PhantomData); + +impl SnapshotWrapper { + /// Kill all snapshot related storage items at the same time. + pub fn kill() { + >::kill(); + >::kill(); + >::kill(); + } + /// Set all snapshot related storage items at the same time. + pub fn set(metadata: SolutionOrSnapshotSize, desired_targets: u32, buffer: &[u8]) { + >::put(metadata); + >::put(desired_targets); + sp_io::storage::set(&>::hashed_key(), &buffer); + } + + /// Check if all of the storage items exist at the same time or all of the storage items do not + /// exist. + #[cfg(feature = "try-runtime")] + pub fn is_consistent() -> bool { + let snapshots = [ + >::exists(), + >::exists(), + >::exists(), + ]; + + // All should either exist or not exist + snapshots.iter().skip(1).all(|v| snapshots[0] == *v) + } +} + impl Pallet { /// Internal logic of the offchain worker, to be executed only when the offchain lock is /// acquired with success. @@ -1402,9 +1438,6 @@ impl Pallet { SolutionOrSnapshotSize { voters: voters.len() as u32, targets: targets.len() as u32 }; log!(info, "creating a snapshot with metadata {:?}", metadata); - >::put(metadata); - >::put(desired_targets); - // instead of using storage APIs, we do a manual encoding into a fixed-size buffer. // `encoded_size` encodes it without storing it anywhere, this should not cause any // allocation. @@ -1419,7 +1452,7 @@ impl Pallet { // buffer should have not re-allocated since. debug_assert!(buffer.len() == size && size == buffer.capacity()); - sp_io::storage::set(&>::hashed_key(), &buffer); + SnapshotWrapper::::set(metadata, desired_targets, &buffer); } /// Parts of [`create_snapshot`] that happen outside of this pallet. @@ -1500,13 +1533,6 @@ impl Pallet { ); } - /// Kill everything created by [`Pallet::create_snapshot`]. - pub fn kill_snapshot() { - >::kill(); - >::kill(); - >::kill(); - } - /// Checks the feasibility of a solution. pub fn feasibility_check( raw_solution: RawSolution>, @@ -1541,8 +1567,8 @@ impl Pallet { // Phase is off now. Self::phase_transition(Phase::Off); - // Kill snapshots. - Self::kill_snapshot(); + // Kill snapshot and relevant metadata (everything created by [`SnapshotMetadata::set`]). + SnapshotWrapper::::kill(); } fn do_elect() -> Result, ElectionError> { @@ -1613,15 +1639,7 @@ impl Pallet { // - [`DesiredTargets`] exists if and only if [`Snapshot`] is present. // - [`SnapshotMetadata`] exist if and only if [`Snapshot`] is present. fn try_state_snapshot() -> Result<(), TryRuntimeError> { - if >::exists() && - >::exists() && - >::exists() - { - Ok(()) - } else if !>::exists() && - !>::exists() && - !>::exists() - { + if SnapshotWrapper::::is_consistent() { Ok(()) } else { Err("If snapshot exists, metadata and desired targets should be set too. Otherwise, none should be set.".into()) @@ -1756,18 +1774,14 @@ mod feasibility_check { assert!(MultiPhase::current_phase().is_signed()); let solution = raw_solution(); - // For whatever reason it might be: - >::kill(); + // kill `Snapshot`, `SnapshotMetadata` and `DesiredTargets` for the storage state to + // be consistent, by using the `SnapshotWrapper` for the try_state checks to pass. + >::kill(); assert_noop!( MultiPhase::feasibility_check(solution, COMPUTE), FeasibilityError::SnapshotUnavailable ); - - // kill also `SnapshotMetadata` and `DesiredTargets` for the storage state to be - // consistent for the try_state checks to pass. - >::kill(); - >::kill(); }) } diff --git a/substrate/frame/election-provider-multi-phase/src/migrations.rs b/substrate/frame/election-provider-multi-phase/src/migrations.rs index 50b821e6db6ae8c7a2cc68192f9dd8cb39e9f460..156f1c02e27cd23e3e37014650fdff66115cd18f 100644 --- a/substrate/frame/election-provider-multi-phase/src/migrations.rs +++ b/substrate/frame/election-provider-multi-phase/src/migrations.rs @@ -27,12 +27,12 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); diff --git a/substrate/frame/election-provider-multi-phase/src/mock.rs b/substrate/frame/election-provider-multi-phase/src/mock.rs index 507e8f2c39bd911d1979215dc2d966db86dd454b..312dc5570900beb19812e5d7667804568997a7a2 100644 --- a/substrate/frame/election-provider-multi-phase/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/src/mock.rs @@ -259,7 +259,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } #[derive(Default, Eq, PartialEq, Debug, Clone, Copy)] @@ -442,7 +441,7 @@ where type Extrinsic = Extrinsic; } -pub type Extrinsic = sp_runtime::testing::TestXt; +pub type Extrinsic = sp_runtime::generic::UncheckedExtrinsic; parameter_types! { pub MaxNominations: u32 = ::LIMIT as u32; diff --git a/substrate/frame/election-provider-multi-phase/src/unsigned.rs b/substrate/frame/election-provider-multi-phase/src/unsigned.rs index 94348181334061a3c2de81b90742f53c4390ddc9..94cfd059b6c5b05a5add5c08cd946d011d7085ad 100644 --- a/substrate/frame/election-provider-multi-phase/src/unsigned.rs +++ b/substrate/frame/election-provider-multi-phase/src/unsigned.rs @@ -1813,7 +1813,7 @@ mod tests { let encoded = pool.read().transactions[0].clone(); let extrinsic: Extrinsic = codec::Decode::decode(&mut &*encoded).unwrap(); - let call = extrinsic.call; + let call = extrinsic.function; assert!(matches!(call, RuntimeCall::MultiPhase(Call::submit_unsigned { .. }))); }) } @@ -1830,7 +1830,7 @@ mod tests { let encoded = pool.read().transactions[0].clone(); let extrinsic = Extrinsic::decode(&mut &*encoded).unwrap(); - let call = match extrinsic.call { + let call = match extrinsic.function { RuntimeCall::MultiPhase(call @ Call::submit_unsigned { .. }) => call, _ => panic!("bad call: unexpected submission"), }; diff --git a/substrate/frame/election-provider-multi-phase/src/weights.rs b/substrate/frame/election-provider-multi-phase/src/weights.rs index be578fac8c435769de3b030a4c7fb20832786b83..ed3e942716e265837dd0021b917222b1d3eba4aa 100644 --- a/substrate/frame/election-provider-multi-phase/src/weights.rs +++ b/substrate/frame/election-provider-multi-phase/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_election_provider_multi_phase +//! Autogenerated weights for `pallet_election_provider_multi_phase` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/election-provider-multi-phase/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/election-provider-multi-phase/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_election_provider_multi_phase. +/// Weight functions needed for `pallet_election_provider_multi_phase`. pub trait WeightInfo { fn on_initialize_nothing() -> Weight; fn on_initialize_open_signed() -> Weight; @@ -64,169 +63,171 @@ pub trait WeightInfo { fn feasibility_check(v: u32, t: u32, a: u32, d: u32, ) -> Weight; } -/// Weights for pallet_election_provider_multi_phase using the Substrate node and recommended hardware. +/// Weights for `pallet_election_provider_multi_phase` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CurrentPlannedSession (r:1 w:0) - /// Proof: Staking CurrentPlannedSession (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStartSessionIndex (r:1 w:0) - /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: Babe EpochIndex (r:1 w:0) - /// Proof: Babe EpochIndex (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe GenesisSlot (r:1 w:0) - /// Proof: Babe GenesisSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe CurrentSlot (r:1 w:0) - /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Staking ForceEra (r:1 w:0) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::CurrentPlannedSession` (r:1 w:0) + /// Proof: `Staking::CurrentPlannedSession` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasStartSessionIndex` (r:1 w:0) + /// Proof: `Staking::ErasStartSessionIndex` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `Babe::EpochIndex` (r:1 w:0) + /// Proof: `Babe::EpochIndex` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::GenesisSlot` (r:1 w:0) + /// Proof: `Babe::GenesisSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::CurrentSlot` (r:1 w:0) + /// Proof: `Babe::CurrentSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Staking::ForceEra` (r:1 w:0) + /// Proof: `Staking::ForceEra` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_initialize_nothing() -> Weight { // Proof Size summary in bytes: - // Measured: `1028` + // Measured: `1061` // Estimated: `3481` - // Minimum execution time: 22_089_000 picoseconds. - Weight::from_parts(22_677_000, 3481) + // Minimum execution time: 19_340_000 picoseconds. + Weight::from_parts(19_886_000, 3481) .saturating_add(T::DbWeight::get().reads(8_u64)) } - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_initialize_open_signed() -> Weight { // Proof Size summary in bytes: // Measured: `148` // Estimated: `1633` - // Minimum execution time: 11_986_000 picoseconds. - Weight::from_parts(12_445_000, 1633) + // Minimum execution time: 8_067_000 picoseconds. + Weight::from_parts(8_508_000, 1633) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_initialize_open_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `148` // Estimated: `1633` - // Minimum execution time: 12_988_000 picoseconds. - Weight::from_parts(13_281_000, 1633) + // Minimum execution time: 8_810_000 picoseconds. + Weight::from_parts(9_061_000, 1633) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase QueuedSolution (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (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: `ElectionProviderMultiPhase::QueuedSolution` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::QueuedSolution` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn finalize_signed_phase_accept_solution() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 32_659_000 picoseconds. - Weight::from_parts(33_281_000, 3593) + // Minimum execution time: 24_339_000 picoseconds. + Weight::from_parts(25_322_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// 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`) fn finalize_signed_phase_reject_solution() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 22_471_000 picoseconds. - Weight::from_parts(23_046_000, 3593) + // Minimum execution time: 16_635_000 picoseconds. + Weight::from_parts(17_497_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. fn create_snapshot_internal(v: u32, _t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 262_360_000 picoseconds. - Weight::from_parts(279_313_000, 0) - // Standard Error: 2_384 - .saturating_add(Weight::from_parts(176_415, 0).saturating_mul(v.into())) + // Minimum execution time: 170_730_000 picoseconds. + Weight::from_parts(175_009_000, 0) + // Standard Error: 2_010 + .saturating_add(Weight::from_parts(224_974, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Round (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionIndices` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionsMap` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionsMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::QueuedSolution` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::QueuedSolution` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn elect_queued(a: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `371 + a * (768 ±0) + d * (48 ±0)` // Estimated: `3923 + a * (768 ±0) + d * (49 ±0)` - // Minimum execution time: 301_283_000 picoseconds. - Weight::from_parts(324_586_000, 3923) - // Standard Error: 4_763 - .saturating_add(Weight::from_parts(279_812, 0).saturating_mul(a.into())) + // Minimum execution time: 280_705_000 picoseconds. + Weight::from_parts(303_018_000, 3923) + // Standard Error: 4_633 + .saturating_add(Weight::from_parts(307_274, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) .saturating_add(Weight::from_parts(0, 768).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 49).saturating_mul(d.into())) } - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) - /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionIndices` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionsMap` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionsMap` (`max_values`: None, `max_size`: None, mode: `Measured`) fn submit() -> Weight { // Proof Size summary in bytes: // Measured: `927` // Estimated: `2412` - // Minimum execution time: 52_276_000 picoseconds. - Weight::from_parts(53_846_000, 2412) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Minimum execution time: 43_405_000 picoseconds. + Weight::from_parts(45_734_000, 2412) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::QueuedSolution` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::QueuedSolution` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::MinimumUntrustedScore` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::MinimumUntrustedScore` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. @@ -235,25 +236,25 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `253 + t * (32 ±0) + v * (553 ±0)` // Estimated: `1738 + t * (32 ±0) + v * (553 ±0)` - // Minimum execution time: 5_448_459_000 picoseconds. - Weight::from_parts(5_525_622_000, 1738) - // Standard Error: 21_478 - .saturating_add(Weight::from_parts(256_345, 0).saturating_mul(v.into())) - // Standard Error: 63_648 - .saturating_add(Weight::from_parts(5_103_224, 0).saturating_mul(a.into())) + // Minimum execution time: 5_059_092_000 picoseconds. + Weight::from_parts(5_263_076_000, 1738) + // Standard Error: 17_317 + .saturating_add(Weight::from_parts(384_051, 0).saturating_mul(v.into())) + // Standard Error: 51_319 + .saturating_add(Weight::from_parts(4_095_128, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) } - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::MinimumUntrustedScore` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::MinimumUntrustedScore` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. @@ -262,180 +263,182 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `228 + t * (32 ±0) + v * (553 ±0)` // Estimated: `1713 + t * (32 ±0) + v * (553 ±0)` - // Minimum execution time: 4_724_399_000 picoseconds. - Weight::from_parts(4_886_472_000, 1713) - // Standard Error: 15_220 - .saturating_add(Weight::from_parts(365_569, 0).saturating_mul(v.into())) - // Standard Error: 45_104 - .saturating_add(Weight::from_parts(3_176_675, 0).saturating_mul(a.into())) + // Minimum execution time: 4_426_416_000 picoseconds. + Weight::from_parts(4_466_923_000, 1713) + // Standard Error: 15_415 + .saturating_add(Weight::from_parts(334_047, 0).saturating_mul(v.into())) + // Standard Error: 45_682 + .saturating_add(Weight::from_parts(3_097_318, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CurrentPlannedSession (r:1 w:0) - /// Proof: Staking CurrentPlannedSession (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStartSessionIndex (r:1 w:0) - /// Proof: Staking ErasStartSessionIndex (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: Babe EpochIndex (r:1 w:0) - /// Proof: Babe EpochIndex (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe GenesisSlot (r:1 w:0) - /// Proof: Babe GenesisSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe CurrentSlot (r:1 w:0) - /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Staking ForceEra (r:1 w:0) - /// Proof: Staking ForceEra (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::CurrentPlannedSession` (r:1 w:0) + /// Proof: `Staking::CurrentPlannedSession` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasStartSessionIndex` (r:1 w:0) + /// Proof: `Staking::ErasStartSessionIndex` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `Babe::EpochIndex` (r:1 w:0) + /// Proof: `Babe::EpochIndex` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::GenesisSlot` (r:1 w:0) + /// Proof: `Babe::GenesisSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::CurrentSlot` (r:1 w:0) + /// Proof: `Babe::CurrentSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Staking::ForceEra` (r:1 w:0) + /// Proof: `Staking::ForceEra` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_initialize_nothing() -> Weight { // Proof Size summary in bytes: - // Measured: `1028` + // Measured: `1061` // Estimated: `3481` - // Minimum execution time: 22_089_000 picoseconds. - Weight::from_parts(22_677_000, 3481) + // Minimum execution time: 19_340_000 picoseconds. + Weight::from_parts(19_886_000, 3481) .saturating_add(RocksDbWeight::get().reads(8_u64)) } - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_initialize_open_signed() -> Weight { // Proof Size summary in bytes: // Measured: `148` // Estimated: `1633` - // Minimum execution time: 11_986_000 picoseconds. - Weight::from_parts(12_445_000, 1633) + // Minimum execution time: 8_067_000 picoseconds. + Weight::from_parts(8_508_000, 1633) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_initialize_open_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `148` // Estimated: `1633` - // Minimum execution time: 12_988_000 picoseconds. - Weight::from_parts(13_281_000, 1633) + // Minimum execution time: 8_810_000 picoseconds. + Weight::from_parts(9_061_000, 1633) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase QueuedSolution (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (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: `ElectionProviderMultiPhase::QueuedSolution` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::QueuedSolution` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn finalize_signed_phase_accept_solution() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 32_659_000 picoseconds. - Weight::from_parts(33_281_000, 3593) + // Minimum execution time: 24_339_000 picoseconds. + Weight::from_parts(25_322_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// 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`) fn finalize_signed_phase_reject_solution() -> Weight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `3593` - // Minimum execution time: 22_471_000 picoseconds. - Weight::from_parts(23_046_000, 3593) + // Minimum execution time: 16_635_000 picoseconds. + Weight::from_parts(17_497_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. fn create_snapshot_internal(v: u32, _t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 262_360_000 picoseconds. - Weight::from_parts(279_313_000, 0) - // Standard Error: 2_384 - .saturating_add(Weight::from_parts(176_415, 0).saturating_mul(v.into())) + // Minimum execution time: 170_730_000 picoseconds. + Weight::from_parts(175_009_000, 0) + // Standard Error: 2_010 + .saturating_add(Weight::from_parts(224_974, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Round (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionIndices` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionsMap` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionsMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::QueuedSolution` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::QueuedSolution` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `a` is `[500, 800]`. /// The range of component `d` is `[200, 400]`. fn elect_queued(a: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `371 + a * (768 ±0) + d * (48 ±0)` // Estimated: `3923 + a * (768 ±0) + d * (49 ±0)` - // Minimum execution time: 301_283_000 picoseconds. - Weight::from_parts(324_586_000, 3923) - // Standard Error: 4_763 - .saturating_add(Weight::from_parts(279_812, 0).saturating_mul(a.into())) + // Minimum execution time: 280_705_000 picoseconds. + Weight::from_parts(303_018_000, 3923) + // Standard Error: 4_633 + .saturating_add(Weight::from_parts(307_274, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) .saturating_add(Weight::from_parts(0, 768).saturating_mul(a.into())) .saturating_add(Weight::from_parts(0, 49).saturating_mul(d.into())) } - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) - /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase SignedSubmissionIndices (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionIndices (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionNextIndex (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionNextIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SignedSubmissionsMap (r:0 w:1) - /// Proof Skipped: ElectionProviderMultiPhase SignedSubmissionsMap (max_values: None, max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionIndices` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionNextIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::SignedSubmissionsMap` (r:0 w:1) + /// Proof: `ElectionProviderMultiPhase::SignedSubmissionsMap` (`max_values`: None, `max_size`: None, mode: `Measured`) fn submit() -> Weight { // Proof Size summary in bytes: // Measured: `927` // Estimated: `2412` - // Minimum execution time: 52_276_000 picoseconds. - Weight::from_parts(53_846_000, 2412) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Minimum execution time: 43_405_000 picoseconds. + Weight::from_parts(45_734_000, 2412) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase QueuedSolution (r:1 w:1) - /// Proof Skipped: ElectionProviderMultiPhase QueuedSolution (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase SnapshotMetadata (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase SnapshotMetadata (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::QueuedSolution` (r:1 w:1) + /// Proof: `ElectionProviderMultiPhase::QueuedSolution` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::SnapshotMetadata` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::SnapshotMetadata` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::MinimumUntrustedScore` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::MinimumUntrustedScore` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. @@ -444,25 +447,25 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `253 + t * (32 ±0) + v * (553 ±0)` // Estimated: `1738 + t * (32 ±0) + v * (553 ±0)` - // Minimum execution time: 5_448_459_000 picoseconds. - Weight::from_parts(5_525_622_000, 1738) - // Standard Error: 21_478 - .saturating_add(Weight::from_parts(256_345, 0).saturating_mul(v.into())) - // Standard Error: 63_648 - .saturating_add(Weight::from_parts(5_103_224, 0).saturating_mul(a.into())) + // Minimum execution time: 5_059_092_000 picoseconds. + Weight::from_parts(5_263_076_000, 1738) + // Standard Error: 17_317 + .saturating_add(Weight::from_parts(384_051, 0).saturating_mul(v.into())) + // Standard Error: 51_319 + .saturating_add(Weight::from_parts(4_095_128, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) } - /// Storage: ElectionProviderMultiPhase DesiredTargets (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase DesiredTargets (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Snapshot (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Snapshot (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase Round (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase Round (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ElectionProviderMultiPhase MinimumUntrustedScore (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase MinimumUntrustedScore (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `ElectionProviderMultiPhase::DesiredTargets` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::DesiredTargets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Snapshot` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Snapshot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::Round` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::Round` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ElectionProviderMultiPhase::MinimumUntrustedScore` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::MinimumUntrustedScore` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1000, 2000]`. /// The range of component `t` is `[500, 1000]`. /// The range of component `a` is `[500, 800]`. @@ -471,12 +474,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `228 + t * (32 ±0) + v * (553 ±0)` // Estimated: `1713 + t * (32 ±0) + v * (553 ±0)` - // Minimum execution time: 4_724_399_000 picoseconds. - Weight::from_parts(4_886_472_000, 1713) - // Standard Error: 15_220 - .saturating_add(Weight::from_parts(365_569, 0).saturating_mul(v.into())) - // Standard Error: 45_104 - .saturating_add(Weight::from_parts(3_176_675, 0).saturating_mul(a.into())) + // Minimum execution time: 4_426_416_000 picoseconds. + Weight::from_parts(4_466_923_000, 1713) + // Standard Error: 15_415 + .saturating_add(Weight::from_parts(334_047, 0).saturating_mul(v.into())) + // Standard Error: 45_682 + .saturating_add(Weight::from_parts(3_097_318, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(t.into())) .saturating_add(Weight::from_parts(0, 553).saturating_mul(v.into())) diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml index 05c6a6d404629f8bdc5a9cdb1d2ccdb785972693..e6384450a6fd632f206b7c9bf2bbc870604143e7 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] parking_lot = "0.12.1" codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } scale-info = { version = "2.10.0", features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } sp-runtime = { path = "../../../primitives/runtime" } sp-io = { path = "../../../primitives/io" } @@ -35,7 +35,23 @@ frame-election-provider-support = { path = "../../election-provider-support" } pallet-election-provider-multi-phase = { path = ".." } pallet-staking = { path = "../../staking" } +pallet-nomination-pools = { path = "../../nomination-pools" } pallet-bags-list = { path = "../../bags-list" } pallet-balances = { path = "../../balances" } pallet-timestamp = { path = "../../timestamp" } pallet-session = { path = "../../session" } + +[features] +try-runtime = [ + "frame-election-provider-support/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-bags-list/try-runtime", + "pallet-balances/try-runtime", + "pallet-election-provider-multi-phase/try-runtime", + "pallet-nomination-pools/try-runtime", + "pallet-session/try-runtime", + "pallet-staking/try-runtime", + "pallet-timestamp/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index 1d3f4712b1d604988047f5643ddd86296f453878..53bff50f7482fd61fb2c77594fa9c3a8957c0a8a 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -53,9 +53,9 @@ fn log_current_time() { #[test] fn block_progression_works() { - let (mut ext, pool_state, _) = ExtBuilder::default().build_offchainify(); + let (ext, pool_state, _) = ExtBuilder::default().build_offchainify(); - ext.execute_with(|| { + execute_with(ext, || { assert_eq!(active_era(), 0); assert_eq!(Session::current_index(), 0); assert!(ElectionProviderMultiPhase::current_phase().is_off()); @@ -70,9 +70,9 @@ fn block_progression_works() { assert!(ElectionProviderMultiPhase::current_phase().is_signed()); }); - let (mut ext, pool_state, _) = ExtBuilder::default().build_offchainify(); + let (ext, pool_state, _) = ExtBuilder::default().build_offchainify(); - ext.execute_with(|| { + execute_with(ext, || { assert_eq!(active_era(), 0); assert_eq!(Session::current_index(), 0); assert!(ElectionProviderMultiPhase::current_phase().is_off()); @@ -93,12 +93,12 @@ fn offchainify_works() { let staking_builder = StakingExtBuilder::default(); let epm_builder = EpmExtBuilder::default(); - let (mut ext, pool_state, _) = ExtBuilder::default() + let (ext, pool_state, _) = ExtBuilder::default() .epm(epm_builder) .staking(staking_builder) .build_offchainify(); - ext.execute_with(|| { + execute_with(ext, || { // test ocw progression and solution queue if submission when unsigned phase submission is // not delayed. for _ in 0..100 { @@ -142,9 +142,9 @@ fn offchainify_works() { /// restarts. Note that in this test case, the emergency throttling is disabled. fn enters_emergency_phase_after_forcing_before_elect() { let epm_builder = EpmExtBuilder::default().disable_emergency_throttling(); - let (mut ext, pool_state, _) = ExtBuilder::default().epm(epm_builder).build_offchainify(); + let (ext, pool_state, _) = ExtBuilder::default().epm(epm_builder).build_offchainify(); - ext.execute_with(|| { + execute_with(ext, || { log!( trace, "current validators (staking): {:?}", @@ -213,12 +213,12 @@ fn continous_slashes_below_offending_threshold() { let staking_builder = StakingExtBuilder::default().validator_count(10); let epm_builder = EpmExtBuilder::default().disable_emergency_throttling(); - let (mut ext, pool_state, _) = ExtBuilder::default() + let (ext, pool_state, _) = ExtBuilder::default() .epm(epm_builder) .staking(staking_builder) .build_offchainify(); - ext.execute_with(|| { + execute_with(ext, || { assert_eq!(Session::validators().len(), 10); let mut active_validator_set = Session::validators(); @@ -271,12 +271,12 @@ fn set_validation_intention_after_chilled() { use frame_election_provider_support::SortedListProvider; use pallet_staking::{Event, Forcing, Nominators}; - let (mut ext, pool_state, _) = ExtBuilder::default() + let (ext, pool_state, _) = ExtBuilder::default() .epm(EpmExtBuilder::default()) .staking(StakingExtBuilder::default()) .build_offchainify(); - ext.execute_with(|| { + execute_with(ext, || { assert_eq!(active_era(), 0); // validator is part of the validator set. assert!(Session::validators().contains(&41)); @@ -334,10 +334,10 @@ fn set_validation_intention_after_chilled() { fn ledger_consistency_active_balance_below_ed() { use pallet_staking::{Error, Event}; - let (mut ext, pool_state, _) = + let (ext, pool_state, _) = ExtBuilder::default().staking(StakingExtBuilder::default()).build_offchainify(); - ext.execute_with(|| { + execute_with(ext, || { assert_eq!(Staking::ledger(11.into()).unwrap().active, 1000); // unbonding total of active stake fails because the active ledger balance would fall @@ -387,3 +387,141 @@ fn ledger_consistency_active_balance_below_ed() { assert!(Staking::ledger(11.into()).is_err()); }); } + +#[test] +/// Automatic withdrawal of unlocking funds in staking propagates to the nomination pools and its +/// state correctly. +/// +/// The staking pallet may withdraw unlocking funds from a pool's bonded account without a pool +/// member or operator calling explicitly `Call::withdraw*`. This test verifies that the member's +/// are eventually paid and the `TotalValueLocked` is kept in sync in those cases. +fn automatic_unbonding_pools() { + use pallet_nomination_pools::TotalValueLocked; + + // closure to fetch the staking unlocking chunks of an account. + let unlocking_chunks_of = |account: AccountId| -> usize { + Staking::ledger(sp_staking::StakingAccount::Controller(account)) + .unwrap() + .unlocking + .len() + }; + + let (ext, pool_state, _) = ExtBuilder::default() + .pools(PoolsExtBuilder::default().max_unbonding(1)) + .staking(StakingExtBuilder::default().max_unlocking(1).bonding_duration(2)) + .build_offchainify(); + + execute_with(ext, || { + assert_eq!(::MaxUnlockingChunks::get(), 1); + assert_eq!(::BondingDuration::get(), 2); + assert_eq!(::MaxUnbonding::get(), 1); + + // init state of pool members. + let init_free_balance_2 = Balances::free_balance(2); + let init_free_balance_3 = Balances::free_balance(3); + + let pool_bonded_account = Pools::create_bonded_account(1); + + // creates a pool with 5 bonded, owned by 1. + assert_ok!(Pools::create(RuntimeOrigin::signed(1), 5, 1, 1, 1)); + assert_eq!(locked_amount_for(pool_bonded_account), 5); + + let init_tvl = TotalValueLocked::::get(); + + // 2 joins the pool. + assert_ok!(Pools::join(RuntimeOrigin::signed(2), 10, 1)); + assert_eq!(locked_amount_for(pool_bonded_account), 15); + + // 3 joins the pool. + assert_ok!(Pools::join(RuntimeOrigin::signed(3), 10, 1)); + assert_eq!(locked_amount_for(pool_bonded_account), 25); + + assert_eq!(TotalValueLocked::::get(), 25); + + // currently unlocking 0 chunks in the bonded pools ledger. + assert_eq!(unlocking_chunks_of(pool_bonded_account), 0); + + // unbond 2 from pool. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(2), 2, 10)); + + // amount is still locked in the pool, needs to wait for unbonding period. + assert_eq!(locked_amount_for(pool_bonded_account), 25); + + // max chunks in the ledger are now filled up (`MaxUnlockingChunks == 1`). + assert_eq!(unlocking_chunks_of(pool_bonded_account), 1); + + // tries to unbond 3 from pool. it will fail since there are no unlocking chunks left + // available and the current in the queue haven't been there for more than bonding + // duration. + assert_err!( + Pools::unbond(RuntimeOrigin::signed(3), 3, 10), + pallet_staking::Error::::NoMoreChunks + ); + + assert_eq!(current_era(), 0); + + // progress over bonding duration. + for _ in 0..=::BondingDuration::get() { + start_next_active_era(pool_state.clone()).unwrap(); + } + assert_eq!(current_era(), 3); + System::reset_events(); + + let locked_before_withdraw_pool = locked_amount_for(pool_bonded_account); + assert_eq!(Balances::free_balance(pool_bonded_account), 26); + + // now unbonding 3 will work, although the pool's ledger still has the unlocking chunks + // filled up. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(3), 3, 10)); + assert_eq!(unlocking_chunks_of(pool_bonded_account), 1); + + assert_eq!( + staking_events(), + [ + // auto-withdraw happened as expected to release 2's unbonding funds, but the funds + // were not transfered to 2 and stay in the pool's tranferrable balance instead. + pallet_staking::Event::Withdrawn { stash: 7939698191839293293, amount: 10 }, + pallet_staking::Event::Unbonded { stash: 7939698191839293293, amount: 10 } + ] + ); + + // balance of the pool remains the same, it hasn't withdraw explicitly from the pool yet. + assert_eq!(Balances::free_balance(pool_bonded_account), 26); + // but the locked amount in the pool's account decreases due to the auto-withdraw: + assert_eq!(locked_before_withdraw_pool - 10, locked_amount_for(pool_bonded_account)); + + // TVL correctly updated. + assert_eq!(TotalValueLocked::::get(), 25 - 10); + + // however, note that the withdrawing from the pool still works for 2, the funds are taken + // from the pool's free balance. + assert_eq!(Balances::free_balance(pool_bonded_account), 26); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(2), 2, 10)); + assert_eq!(Balances::free_balance(pool_bonded_account), 16); + + assert_eq!(Balances::free_balance(2), 20); + assert_eq!(TotalValueLocked::::get(), 15); + + // 3 cannot withdraw yet. + assert_err!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(3), 3, 10), + pallet_nomination_pools::Error::::CannotWithdrawAny + ); + + // progress over bonding duration. + for _ in 0..=::BondingDuration::get() { + start_next_active_era(pool_state.clone()).unwrap(); + } + assert_eq!(current_era(), 6); + System::reset_events(); + + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(3), 3, 10)); + + // final conditions are the expected. + assert_eq!(Balances::free_balance(pool_bonded_account), 6); // 5 init bonded + ED + assert_eq!(Balances::free_balance(2), init_free_balance_2); + assert_eq!(Balances::free_balance(3), init_free_balance_3); + + assert_eq!(TotalValueLocked::::get(), init_tvl); + }); +} diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index 04d218acf8fd14ec82b9e299999273223cae2964..7fade251e6d1c62b1b64a7ec1a86e29e0e18f40d 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -62,13 +62,14 @@ pub const INIT_TIMESTAMP: BlockNumber = 30_000; pub const BLOCK_TIME: BlockNumber = 1000; type Block = frame_system::mocking::MockBlockU32; -type Extrinsic = testing::TestXt; +type Extrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( pub enum Runtime { System: frame_system, ElectionProviderMultiPhase: pallet_election_provider_multi_phase, Staking: pallet_staking, + Pools: pallet_nomination_pools, Balances: pallet_balances, BagsList: pallet_bags_list, Session: pallet_session, @@ -111,11 +112,10 @@ impl pallet_balances::Config for Runtime { type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type MaxHolds = ConstU32<1>; type MaxFreezes = traits::ConstU32<1>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); + type FreezeIdentifier = RuntimeFreezeReason; type WeightInfo = (); } @@ -234,7 +234,7 @@ const THRESHOLDS: [VoteWeight; 9] = [10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_00 parameter_types! { pub static BagThresholds: &'static [sp_npos_elections::VoteWeight] = &THRESHOLDS; pub const SessionsPerEra: sp_staking::SessionIndex = 2; - pub const BondingDuration: sp_staking::EraIndex = 28; + pub static BondingDuration: sp_staking::EraIndex = 28; pub const SlashDeferDuration: sp_staking::EraIndex = 7; // 1/4 the bonding duration. pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(40); pub HistoryDepth: u32 = 84; @@ -248,6 +248,45 @@ impl pallet_bags_list::Config for Runtime { type Score = VoteWeight; } +pub struct BalanceToU256; +impl sp_runtime::traits::Convert for BalanceToU256 { + fn convert(n: Balance) -> sp_core::U256 { + n.into() + } +} + +pub struct U256ToBalance; +impl sp_runtime::traits::Convert for U256ToBalance { + fn convert(n: sp_core::U256) -> Balance { + n.try_into().unwrap() + } +} + +parameter_types! { + pub const PoolsPalletId: frame_support::PalletId = frame_support::PalletId(*b"py/nopls"); + pub static MaxUnbonding: u32 = 8; +} + +impl pallet_nomination_pools::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; + type RuntimeFreezeReason = RuntimeFreezeReason; + type RewardCounter = sp_runtime::FixedU128; + type BalanceToU256 = BalanceToU256; + type U256ToBalance = U256ToBalance; + type Staking = Staking; + type PostUnbondingPoolsWindow = ConstU32<2>; + type PalletId = PoolsPalletId; + type MaxMetadataLen = ConstU32<256>; + type MaxUnbonding = MaxUnbonding; + type MaxPointsToBalance = frame_support::traits::ConstU8<10>; +} + +parameter_types! { + pub static MaxUnlockingChunks: u32 = 32; +} + /// Upper limit on the number of NPOS nominations. const MAX_QUOTA_NOMINATIONS: u32 = 16; @@ -274,10 +313,10 @@ impl pallet_staking::Config for Runtime { type VoterList = BagsList; type NominationsQuota = pallet_staking::FixedNominationsQuota; type TargetList = pallet_staking::UseValidatorsMap; - type MaxUnlockingChunks = ConstU32<32>; + type MaxUnlockingChunks = MaxUnlockingChunks; type MaxControllersInDeprecationBatch = ConstU32<100>; type HistoryDepth = HistoryDepth; - type EventListeners = (); + type EventListeners = Pools; type WeightInfo = pallet_staking::weights::SubstrateWeight; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; } @@ -395,6 +434,14 @@ impl StakingExtBuilder { self.validator_count = n; self } + pub fn max_unlocking(self, max: u32) -> Self { + ::set(max); + self + } + pub fn bonding_duration(self, eras: EraIndex) -> Self { + ::set(eras); + self + } } pub struct EpmExtBuilder {} @@ -418,6 +465,21 @@ impl EpmExtBuilder { } } +pub struct PoolsExtBuilder {} + +impl Default for PoolsExtBuilder { + fn default() -> Self { + PoolsExtBuilder {} + } +} + +impl PoolsExtBuilder { + pub fn max_unbonding(self, max: u32) -> Self { + ::set(max); + self + } +} + pub struct BalancesExtBuilder { balances: Vec<(AccountId, Balance)>, } @@ -465,6 +527,7 @@ pub struct ExtBuilder { staking_builder: StakingExtBuilder, epm_builder: EpmExtBuilder, balances_builder: BalancesExtBuilder, + pools_builder: PoolsExtBuilder, } impl Default for ExtBuilder { @@ -473,6 +536,7 @@ impl Default for ExtBuilder { staking_builder: StakingExtBuilder::default(), epm_builder: EpmExtBuilder::default(), balances_builder: BalancesExtBuilder::default(), + pools_builder: PoolsExtBuilder::default(), } } } @@ -549,6 +613,11 @@ impl ExtBuilder { self } + pub fn pools(mut self, builder: PoolsExtBuilder) -> Self { + self.pools_builder = builder; + self + } + pub fn balances(mut self, builder: BalancesExtBuilder) -> Self { self.balances_builder = builder; self @@ -568,20 +637,20 @@ impl ExtBuilder { (ext, pool_state, offchain_state) } +} - pub fn build_and_execute(self, test: impl FnOnce() -> ()) { - let mut ext = self.build(); - ext.execute_with(test); +pub(crate) fn execute_with(mut ext: sp_io::TestExternalities, test: impl FnOnce() -> ()) { + ext.execute_with(test); - #[cfg(feature = "try-runtime")] - ext.execute_with(|| { - let bn = System::block_number(); + #[cfg(feature = "try-runtime")] + ext.execute_with(|| { + let bn = System::block_number(); - assert_ok!(>::try_state(bn)); - assert_ok!(>::try_state(bn)); - assert_ok!(>::try_state(bn)); - }); - } + assert_ok!(>::try_state(bn)); + assert_ok!(>::try_state(bn)); + assert_ok!(>::try_state(bn)); + assert_ok!(>::try_state(bn)); + }); } // Progress to given block, triggering session and era changes as we progress and ensuring that @@ -607,6 +676,7 @@ pub fn roll_to(n: BlockNumber, delay_solution: bool) { if b != n { Staking::on_finalize(System::block_number()); } + Pools::on_initialize(b); log_current_time(); } @@ -629,7 +699,7 @@ pub fn roll_to_with_ocw(n: BlockNumber, pool: Arc>, delay_solu for encoded in &pool.read().transactions { let extrinsic = Extrinsic::decode(&mut &encoded[..]).unwrap(); - let _ = match extrinsic.call { + let _ = match extrinsic.function { RuntimeCall::ElectionProviderMultiPhase( call @ Call::submit_unsigned { .. }, ) => { @@ -861,6 +931,11 @@ pub(crate) fn set_minimum_election_score( .map_err(|_| ()) } +pub(crate) fn locked_amount_for(account_id: AccountId) -> Balance { + let lock = pallet_balances::Locks::::get(account_id); + lock[0].amount +} + pub(crate) fn staking_events() -> Vec> { System::events() .into_iter() diff --git a/substrate/frame/election-provider-support/Cargo.toml b/substrate/frame/election-provider-support/Cargo.toml index 8182863d79665ea9f2c026b3cdc4b2ae10d4e0c7..b182b831ea0d12c8b791d43ab793337a408dbd26 100644 --- a/substrate/frame/election-provider-support/Cargo.toml +++ b/substrate/frame/election-provider-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-election-provider-support" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/election-provider-support/benchmarking/Cargo.toml b/substrate/frame/election-provider-support/benchmarking/Cargo.toml index 7a2ad5cafb49fff6d181a6b1ce47e562d64fb7df..6e13f17bec1bbf237914df6baffdc2ede46c05f9 100644 --- a/substrate/frame/election-provider-support/benchmarking/Cargo.toml +++ b/substrate/frame/election-provider-support/benchmarking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-election-provider-support-benchmarking" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/election-provider-support/solution-type/Cargo.toml b/substrate/frame/election-provider-support/solution-type/Cargo.toml index 508c049e490c3acf026390b1f14c8d3b1009f96d..1bf1165229a7dd20700bb561d4dd776ca7c250bb 100644 --- a/substrate/frame/election-provider-support/solution-type/Cargo.toml +++ b/substrate/frame/election-provider-support/solution-type/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-election-provider-solution-type" -version = "4.0.0-dev" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,8 +18,8 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -syn = { version = "2.0.48", features = ["full", "visit"] } -quote = "1.0.28" +syn = { features = ["full", "visit"], workspace = true } +quote = { workspace = true } proc-macro2 = "1.0.56" proc-macro-crate = "3.0.0" diff --git a/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml b/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml index 7200d207aeccf9c3b83202bce7ad61bfc4c289d5..8a73dd38fa2df251fcd0ef5826f51b6cf56e0160 100644 --- a/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml +++ b/substrate/frame/election-provider-support/solution-type/fuzzer/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["small_rng", "std"] } diff --git a/substrate/frame/election-provider-support/src/tests.rs b/substrate/frame/election-provider-support/src/tests.rs index 73ce1427cf2f00883865a4b8c6cc5fad864d9c13..6e3deb9e38346629bbff92dfc708e9bbf5ad5a9d 100644 --- a/substrate/frame/election-provider-support/src/tests.rs +++ b/substrate/frame/election-provider-support/src/tests.rs @@ -29,7 +29,7 @@ mod solution_type { // these need to come from the same dev-dependency `frame-election-provider-support`, not from // the crate. use crate::{generate_solution_type, Assignment, Error as NposError, NposSolution}; - use sp_std::fmt::Debug; + use core::fmt::Debug; #[allow(dead_code)] mod __private { diff --git a/substrate/frame/election-provider-support/src/weights.rs b/substrate/frame/election-provider-support/src/weights.rs index addb6ad8d03069aecd6fda3b2725cdbc37e5ba5d..09b0cb108f7faa39bce20aaf9e4ae3dd9edcf4a5 100644 --- a/substrate/frame/election-provider-support/src/weights.rs +++ b/substrate/frame/election-provider-support/src/weights.rs @@ -41,7 +41,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_election_provider_support_benchmarking. pub trait WeightInfo { diff --git a/substrate/frame/elections-phragmen/Cargo.toml b/substrate/frame/elections-phragmen/Cargo.toml index 3c0bc56a1d04ba7f90c00f2e7421ebcb5721438b..4dc4a3454aa031da708f536aadcbe9764b093726 100644 --- a/substrate/frame/elections-phragmen/Cargo.toml +++ b/substrate/frame/elections-phragmen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-elections-phragmen" -version = "5.0.0-dev" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -log = { version = "0.4.14", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/elections-phragmen/src/lib.rs b/substrate/frame/elections-phragmen/src/lib.rs index 2b599ab5292bb07de37d42566e70a8c209d9cfae..e08412a6e87f49a9d66377ab07e57b5792a9ee6b 100644 --- a/substrate/frame/elections-phragmen/src/lib.rs +++ b/substrate/frame/elections-phragmen/src/lib.rs @@ -188,7 +188,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] @@ -1313,39 +1313,13 @@ mod tests { traits::{ConstU32, ConstU64, OnInitialize}, }; use frame_system::ensure_signed; - use sp_core::H256; - use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, - }; + use sp_runtime::{testing::Header, BuildStorage}; use substrate_test_utils::assert_eq_uvec; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -1362,7 +1336,6 @@ mod tests { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } frame_support::parameter_types! { @@ -1449,7 +1422,7 @@ mod tests { pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = - sp_runtime::generic::UncheckedExtrinsic; + sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( pub enum Test diff --git a/substrate/frame/elections-phragmen/src/weights.rs b/substrate/frame/elections-phragmen/src/weights.rs index b7ed13dae9f734e524072bcd6ac5e116fca632bc..cd67918e85b2f60cb30ac06a38d097b430c6812e 100644 --- a/substrate/frame/elections-phragmen/src/weights.rs +++ b/substrate/frame/elections-phragmen/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_elections_phragmen +//! Autogenerated weights for `pallet_elections_phragmen` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/elections-phragmen/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/elections-phragmen/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_elections_phragmen. +/// Weight functions needed for `pallet_elections_phragmen`. pub trait WeightInfo { fn vote_equal(v: u32, ) -> Weight; fn vote_more(v: u32, ) -> Weight; @@ -66,165 +65,165 @@ pub trait WeightInfo { fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight; } -/// Weights for pallet_elections_phragmen using the Substrate node and recommended hardware. +/// Weights for `pallet_elections_phragmen` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` // Estimated: `4764 + v * (80 ±0)` - // Minimum execution time: 33_028_000 picoseconds. - Weight::from_parts(34_073_914, 4764) - // Standard Error: 3_474 - .saturating_add(Weight::from_parts(205_252, 0).saturating_mul(v.into())) + // Minimum execution time: 29_390_000 picoseconds. + Weight::from_parts(30_525_476, 4764) + // Standard Error: 3_185 + .saturating_add(Weight::from_parts(143_073, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `371 + v * (80 ±0)` // Estimated: `4764 + v * (80 ±0)` - // Minimum execution time: 45_725_000 picoseconds. - Weight::from_parts(47_169_586, 4764) - // Standard Error: 5_148 - .saturating_add(Weight::from_parts(213_742, 0).saturating_mul(v.into())) + // Minimum execution time: 39_765_000 picoseconds. + Weight::from_parts(41_374_102, 4764) + // Standard Error: 4_310 + .saturating_add(Weight::from_parts(153_015, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` // Estimated: `4764 + v * (80 ±0)` - // Minimum execution time: 45_519_000 picoseconds. - Weight::from_parts(47_339_108, 4764) - // Standard Error: 5_501 - .saturating_add(Weight::from_parts(195_247, 0).saturating_mul(v.into())) + // Minimum execution time: 39_647_000 picoseconds. + Weight::from_parts(41_474_523, 4764) + // Standard Error: 5_503 + .saturating_add(Weight::from_parts(149_029, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn remove_voter() -> Weight { // Proof Size summary in bytes: // Measured: `925` // Estimated: `4764` - // Minimum execution time: 50_386_000 picoseconds. - Weight::from_parts(51_378_000, 4764) + // Minimum execution time: 41_882_000 picoseconds. + Weight::from_parts(42_794_000, 4764) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Candidates` (r:1 w:1) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 64]`. fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1570 + c * (48 ±0)` // Estimated: `3055 + c * (48 ±0)` - // Minimum execution time: 38_987_000 picoseconds. - Weight::from_parts(41_302_276, 3055) - // Standard Error: 2_047 - .saturating_add(Weight::from_parts(125_200, 0).saturating_mul(c.into())) + // Minimum execution time: 33_719_000 picoseconds. + Weight::from_parts(35_017_073, 3055) + // Standard Error: 1_587 + .saturating_add(Weight::from_parts(121_130, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Candidates` (r:1 w:1) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 64]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `285 + c * (48 ±0)` // Estimated: `1770 + c * (48 ±0)` - // Minimum execution time: 33_510_000 picoseconds. - Weight::from_parts(34_947_760, 1770) - // Standard Error: 1_781 - .saturating_add(Weight::from_parts(78_851, 0).saturating_mul(c.into())) + // Minimum execution time: 27_263_000 picoseconds. + Weight::from_parts(28_215_666, 1770) + // Standard Error: 1_196 + .saturating_add(Weight::from_parts(86_804, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:1) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:0 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn renounce_candidacy_members() -> Weight { // Proof Size summary in bytes: - // Measured: `1900` - // Estimated: `3385` - // Minimum execution time: 50_603_000 picoseconds. - Weight::from_parts(51_715_000, 3385) + // Measured: `1933` + // Estimated: `3418` + // Minimum execution time: 41_531_000 picoseconds. + Weight::from_parts(42_937_000, 3418) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn renounce_candidacy_runners_up() -> Weight { // Proof Size summary in bytes: // Measured: `880` // Estimated: `2365` - // Minimum execution time: 33_441_000 picoseconds. - Weight::from_parts(34_812_000, 2365) + // Minimum execution time: 27_680_000 picoseconds. + Weight::from_parts(28_810_000, 2365) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_member_without_replacement() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -232,87 +231,90 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 2_000_000_000_000 picoseconds. Weight::from_parts(2_000_000_000_000, 0) } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (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: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:1) + /// Proof: `Elections::Members` (`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: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:0 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn remove_member_with_replacement() -> Weight { // Proof Size summary in bytes: - // Measured: `1900` + // Measured: `1933` // Estimated: `3593` - // Minimum execution time: 57_289_000 picoseconds. - Weight::from_parts(58_328_000, 3593) + // Minimum execution time: 45_333_000 picoseconds. + Weight::from_parts(46_523_000, 3593) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Elections Voting (r:513 w:512) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Balances Locks (r:512 w:512) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:512 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:512 w:512) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Elections::Voting` (r:257 w:256) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:256 w:256) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:256 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:256 w:256) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `v` is `[256, 512]`. /// The range of component `d` is `[0, 256]`. - fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { + fn clean_defunct_voters(v: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1149 + v * (811 ±0)` - // Estimated: `4621 + v * (3774 ±0)` - // Minimum execution time: 18_774_231_000 picoseconds. - Weight::from_parts(18_933_040_000, 4621) - // Standard Error: 301_534 - .saturating_add(Weight::from_parts(44_306_903, 0).saturating_mul(v.into())) + // Measured: `0 + d * (818 ±0) + v * (57 ±0)` + // Estimated: `24906 + d * (3774 ±1) + v * (24 ±0)` + // Minimum execution time: 5_620_000 picoseconds. + Weight::from_parts(5_817_000, 24906) + // Standard Error: 18_357 + .saturating_add(Weight::from_parts(106_164, 0).saturating_mul(v.into())) + // Standard Error: 39_980 + .saturating_add(Weight::from_parts(52_456_337, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(v.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 3774).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 24).saturating_mul(v.into())) } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:513 w:0) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:44 w:44) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Elections ElectionRounds (r:1 w:1) - /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:0 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Candidates` (r:1 w:1) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:1) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:513 w:0) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:44 w:44) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Elections::ElectionRounds` (r:1 w:1) + /// Proof: `Elections::ElectionRounds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:0 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:0 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 64]`. /// The range of component `v` is `[1, 512]`. /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + e * (28 ±0) + v * (606 ±0)` - // Estimated: `178887 + c * (2135 ±7) + e * (12 ±0) + v * (2653 ±6)` - // Minimum execution time: 1_281_877_000 picoseconds. - Weight::from_parts(1_288_147_000, 178887) - // Standard Error: 528_851 - .saturating_add(Weight::from_parts(17_761_407, 0).saturating_mul(v.into())) - // Standard Error: 33_932 - .saturating_add(Weight::from_parts(698_277, 0).saturating_mul(e.into())) + // Estimated: `178920 + c * (2135 ±7) + e * (12 ±0) + v * (2653 ±6)` + // Minimum execution time: 1_082_582_000 picoseconds. + Weight::from_parts(1_084_730_000, 178920) + // Standard Error: 594_096 + .saturating_add(Weight::from_parts(19_096_288, 0).saturating_mul(v.into())) + // Standard Error: 38_118 + .saturating_add(Weight::from_parts(792_945, 0).saturating_mul(e.into())) .saturating_add(T::DbWeight::get().reads(21_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) @@ -324,164 +326,164 @@ impl WeightInfo for SubstrateWeight { } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `v` is `[1, 16]`. fn vote_equal(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` // Estimated: `4764 + v * (80 ±0)` - // Minimum execution time: 33_028_000 picoseconds. - Weight::from_parts(34_073_914, 4764) - // Standard Error: 3_474 - .saturating_add(Weight::from_parts(205_252, 0).saturating_mul(v.into())) + // Minimum execution time: 29_390_000 picoseconds. + Weight::from_parts(30_525_476, 4764) + // Standard Error: 3_185 + .saturating_add(Weight::from_parts(143_073, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `v` is `[2, 16]`. fn vote_more(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `371 + v * (80 ±0)` // Estimated: `4764 + v * (80 ±0)` - // Minimum execution time: 45_725_000 picoseconds. - Weight::from_parts(47_169_586, 4764) - // Standard Error: 5_148 - .saturating_add(Weight::from_parts(213_742, 0).saturating_mul(v.into())) + // Minimum execution time: 39_765_000 picoseconds. + Weight::from_parts(41_374_102, 4764) + // Standard Error: 4_310 + .saturating_add(Weight::from_parts(153_015, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `v` is `[2, 16]`. fn vote_less(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `403 + v * (80 ±0)` // Estimated: `4764 + v * (80 ±0)` - // Minimum execution time: 45_519_000 picoseconds. - Weight::from_parts(47_339_108, 4764) - // Standard Error: 5_501 - .saturating_add(Weight::from_parts(195_247, 0).saturating_mul(v.into())) + // Minimum execution time: 39_647_000 picoseconds. + Weight::from_parts(41_474_523, 4764) + // Standard Error: 5_503 + .saturating_add(Weight::from_parts(149_029, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 80).saturating_mul(v.into())) } - /// Storage: Elections Voting (r:1 w:1) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Elections::Voting` (r:1 w:1) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) fn remove_voter() -> Weight { // Proof Size summary in bytes: // Measured: `925` // Estimated: `4764` - // Minimum execution time: 50_386_000 picoseconds. - Weight::from_parts(51_378_000, 4764) + // Minimum execution time: 41_882_000 picoseconds. + Weight::from_parts(42_794_000, 4764) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Candidates` (r:1 w:1) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 64]`. fn submit_candidacy(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1570 + c * (48 ±0)` // Estimated: `3055 + c * (48 ±0)` - // Minimum execution time: 38_987_000 picoseconds. - Weight::from_parts(41_302_276, 3055) - // Standard Error: 2_047 - .saturating_add(Weight::from_parts(125_200, 0).saturating_mul(c.into())) + // Minimum execution time: 33_719_000 picoseconds. + Weight::from_parts(35_017_073, 3055) + // Standard Error: 1_587 + .saturating_add(Weight::from_parts(121_130, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Candidates` (r:1 w:1) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 64]`. fn renounce_candidacy_candidate(c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `285 + c * (48 ±0)` // Estimated: `1770 + c * (48 ±0)` - // Minimum execution time: 33_510_000 picoseconds. - Weight::from_parts(34_947_760, 1770) - // Standard Error: 1_781 - .saturating_add(Weight::from_parts(78_851, 0).saturating_mul(c.into())) + // Minimum execution time: 27_263_000 picoseconds. + Weight::from_parts(28_215_666, 1770) + // Standard Error: 1_196 + .saturating_add(Weight::from_parts(86_804, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 48).saturating_mul(c.into())) } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:1) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:0 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn renounce_candidacy_members() -> Weight { // Proof Size summary in bytes: - // Measured: `1900` - // Estimated: `3385` - // Minimum execution time: 50_603_000 picoseconds. - Weight::from_parts(51_715_000, 3385) + // Measured: `1933` + // Estimated: `3418` + // Minimum execution time: 41_531_000 picoseconds. + Weight::from_parts(42_937_000, 3418) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn renounce_candidacy_runners_up() -> Weight { // Proof Size summary in bytes: // Measured: `880` // Estimated: `2365` - // Minimum execution time: 33_441_000 picoseconds. - Weight::from_parts(34_812_000, 2365) + // Minimum execution time: 27_680_000 picoseconds. + Weight::from_parts(28_810_000, 2365) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Benchmark Override (r:0 w:0) - /// Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_member_without_replacement() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -489,87 +491,90 @@ impl WeightInfo for () { // Minimum execution time: 2_000_000_000_000 picoseconds. Weight::from_parts(2_000_000_000_000, 0) } - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (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: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:1 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:1) + /// Proof: `Elections::Members` (`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: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:1 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:0 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn remove_member_with_replacement() -> Weight { // Proof Size summary in bytes: - // Measured: `1900` + // Measured: `1933` // Estimated: `3593` - // Minimum execution time: 57_289_000 picoseconds. - Weight::from_parts(58_328_000, 3593) + // Minimum execution time: 45_333_000 picoseconds. + Weight::from_parts(46_523_000, 3593) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Elections Voting (r:513 w:512) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:0) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Candidates (r:1 w:0) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Balances Locks (r:512 w:512) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:512 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:512 w:512) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Elections::Voting` (r:257 w:256) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:0) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Candidates` (r:1 w:0) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Locks` (r:256 w:256) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:256 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:256 w:256) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `v` is `[256, 512]`. /// The range of component `d` is `[0, 256]`. - fn clean_defunct_voters(v: u32, _d: u32, ) -> Weight { + fn clean_defunct_voters(v: u32, d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1149 + v * (811 ±0)` - // Estimated: `4621 + v * (3774 ±0)` - // Minimum execution time: 18_774_231_000 picoseconds. - Weight::from_parts(18_933_040_000, 4621) - // Standard Error: 301_534 - .saturating_add(Weight::from_parts(44_306_903, 0).saturating_mul(v.into())) + // Measured: `0 + d * (818 ±0) + v * (57 ±0)` + // Estimated: `24906 + d * (3774 ±1) + v * (24 ±0)` + // Minimum execution time: 5_620_000 picoseconds. + Weight::from_parts(5_817_000, 24906) + // Standard Error: 18_357 + .saturating_add(Weight::from_parts(106_164, 0).saturating_mul(v.into())) + // Standard Error: 39_980 + .saturating_add(Weight::from_parts(52_456_337, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(v.into()))) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) - .saturating_add(Weight::from_parts(0, 3774).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(d.into()))) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 3774).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 24).saturating_mul(v.into())) } - /// Storage: Elections Candidates (r:1 w:1) - /// Proof Skipped: Elections Candidates (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:1) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections RunnersUp (r:1 w:1) - /// Proof Skipped: Elections RunnersUp (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Elections Voting (r:513 w:0) - /// Proof Skipped: Elections Voting (max_values: None, max_size: None, mode: Measured) - /// Storage: Council Proposals (r:1 w:0) - /// Proof Skipped: Council Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Account (r:44 w:44) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Elections ElectionRounds (r:1 w:1) - /// Proof Skipped: Elections ElectionRounds (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Members (r:0 w:1) - /// Proof Skipped: Council Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Council Prime (r:0 w:1) - /// Proof Skipped: Council Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Elections::Candidates` (r:1 w:1) + /// Proof: `Elections::Candidates` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:1) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::RunnersUp` (r:1 w:1) + /// Proof: `Elections::RunnersUp` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Voting` (r:513 w:0) + /// Proof: `Elections::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::Proposals` (r:1 w:0) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:44 w:44) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Elections::ElectionRounds` (r:1 w:1) + /// Proof: `Elections::ElectionRounds` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Members` (r:0 w:1) + /// Proof: `Council::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Prime` (r:0 w:1) + /// Proof: `Council::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 64]`. /// The range of component `v` is `[1, 512]`. /// The range of component `e` is `[512, 8192]`. fn election_phragmen(c: u32, v: u32, e: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + e * (28 ±0) + v * (606 ±0)` - // Estimated: `178887 + c * (2135 ±7) + e * (12 ±0) + v * (2653 ±6)` - // Minimum execution time: 1_281_877_000 picoseconds. - Weight::from_parts(1_288_147_000, 178887) - // Standard Error: 528_851 - .saturating_add(Weight::from_parts(17_761_407, 0).saturating_mul(v.into())) - // Standard Error: 33_932 - .saturating_add(Weight::from_parts(698_277, 0).saturating_mul(e.into())) + // Estimated: `178920 + c * (2135 ±7) + e * (12 ±0) + v * (2653 ±6)` + // Minimum execution time: 1_082_582_000 picoseconds. + Weight::from_parts(1_084_730_000, 178920) + // Standard Error: 594_096 + .saturating_add(Weight::from_parts(19_096_288, 0).saturating_mul(v.into())) + // Standard Error: 38_118 + .saturating_add(Weight::from_parts(792_945, 0).saturating_mul(e.into())) .saturating_add(RocksDbWeight::get().reads(21_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) diff --git a/substrate/frame/examples/Cargo.toml b/substrate/frame/examples/Cargo.toml index eb6355edd312a1ff5f865c235ffd034f085a3027..45c7440eb89135eca98cf00a4aaf5d6ad2c094ca 100644 --- a/substrate/frame/examples/Cargo.toml +++ b/substrate/frame/examples/Cargo.toml @@ -23,6 +23,7 @@ pallet-example-frame-crate = { path = "frame-crate", default-features = false } pallet-example-kitchensink = { path = "kitchensink", default-features = false } pallet-example-offchain-worker = { path = "offchain-worker", default-features = false } pallet-example-split = { path = "split", default-features = false } +pallet-example-single-block-migrations = { path = "single-block-migrations", default-features = false } pallet-example-tasks = { path = "tasks", default-features = false } [features] @@ -34,6 +35,7 @@ std = [ "pallet-example-frame-crate/std", "pallet-example-kitchensink/std", "pallet-example-offchain-worker/std", + "pallet-example-single-block-migrations/std", "pallet-example-split/std", "pallet-example-tasks/std", ] @@ -43,6 +45,7 @@ try-runtime = [ "pallet-example-basic/try-runtime", "pallet-example-kitchensink/try-runtime", "pallet-example-offchain-worker/try-runtime", + "pallet-example-single-block-migrations/try-runtime", "pallet-example-split/try-runtime", "pallet-example-tasks/try-runtime", ] diff --git a/substrate/frame/examples/basic/Cargo.toml b/substrate/frame/examples/basic/Cargo.toml index 3be1a2e558d2c92aeb28ff15bf7bed03f8fe0a59..e4ab5112201d1661d1bbc991489bb6d847d6c32d 100644 --- a/substrate/frame/examples/basic/Cargo.toml +++ b/substrate/frame/examples/basic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-example-basic" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true } frame-support = { path = "../../support", default-features = false } diff --git a/substrate/frame/examples/basic/src/benchmarking.rs b/substrate/frame/examples/basic/src/benchmarking.rs index 4b2ebb41fbda1d6d8a0d12a65b32391510f1a488..65ca3089aba4247cae65932cd4bca9b68d520b75 100644 --- a/substrate/frame/examples/basic/src/benchmarking.rs +++ b/substrate/frame/examples/basic/src/benchmarking.rs @@ -48,7 +48,7 @@ mod benchmarks { set_dummy(RawOrigin::Root, value); // The execution phase is just running `set_dummy` extrinsic call // This is the optional benchmark verification phase, asserting certain states. - assert_eq!(Pallet::::dummy(), Some(value)) + assert_eq!(Dummy::::get(), Some(value)) } // An example method that returns a Result that can be called within a benchmark diff --git a/substrate/frame/examples/basic/src/lib.rs b/substrate/frame/examples/basic/src/lib.rs index 5eff74922cabfa8e10b747ba186b7974e81c49f4..94b2f276e00b7482febf9e69fe6af15b39e1b09a 100644 --- a/substrate/frame/examples/basic/src/lib.rs +++ b/substrate/frame/examples/basic/src/lib.rs @@ -46,14 +46,16 @@ //! use the [`Config::WeightInfo`] trait to calculate call weights. This can also be overridden, //! as demonstrated by [`Call::set_dummy`]. //! - A private function that performs a storage update. -//! - A simple signed extension implementation (see: [`sp_runtime::traits::SignedExtension`]) which -//! increases the priority of the [`Call::set_dummy`] if it's present and drops any transaction -//! with an encoded length higher than 200 bytes. +//! - A simple transaction extension implementation (see: +//! [`sp_runtime::traits::TransactionExtension`]) which increases the priority of the +//! [`Call::set_dummy`] if it's present and drops any transaction with an encoded length higher +//! than 200 bytes. // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; +use core::marker::PhantomData; use frame_support::{ dispatch::{ClassifyDispatch, DispatchClass, DispatchResult, Pays, PaysFee, WeighData}, traits::IsSubType, @@ -63,12 +65,14 @@ use frame_system::ensure_signed; use log::info; use scale_info::TypeInfo; use sp_runtime::{ - traits::{Bounded, DispatchInfoOf, SaturatedConversion, Saturating, SignedExtension}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + impl_tx_ext_default, + traits::{ + Bounded, DispatchInfoOf, OriginOf, SaturatedConversion, Saturating, TransactionExtension, + TransactionExtensionBase, ValidateResult, }, + transaction_validity::{InvalidTransaction, ValidTransaction}, }; -use sp_std::{marker::PhantomData, prelude::*}; +use sp_std::vec::Vec; // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; @@ -285,9 +289,7 @@ pub mod pallet { let _sender = ensure_signed(origin)?; // Read the value of dummy from storage. - // let dummy = Self::dummy(); - // Will also work using the `::get` on the storage item type itself: - // let dummy = >::get(); + // let dummy = Dummy::::get(); // Calculate the new value. // let new_dummy = dummy.map_or(increase_by, |dummy| dummy + increase_by); @@ -380,20 +382,14 @@ pub mod pallet { // - `Foo::put(1); Foo::get()` returns `1`; // - `Foo::kill(); Foo::get()` returns `0` (u32::default()). #[pallet::storage] - // The getter attribute generate a function on `Pallet` placeholder: - // `fn getter_name() -> Type` for basic value items or - // `fn getter_name(key: KeyType) -> ValueType` for map items. - #[pallet::getter(fn dummy)] pub(super) type Dummy = StorageValue<_, T::Balance>; // A map that has enumerable entries. #[pallet::storage] - #[pallet::getter(fn bar)] pub(super) type Bar = StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance>; // this one uses the query kind: `ValueQuery`, we'll demonstrate the usage of 'mutate' API. #[pallet::storage] - #[pallet::getter(fn foo)] pub(super) type Foo = StorageValue<_, T::Balance, ValueQuery>; #[pallet::storage] @@ -432,10 +428,10 @@ impl Pallet { fn accumulate_foo(origin: T::RuntimeOrigin, increase_by: T::Balance) -> DispatchResult { let _sender = ensure_signed(origin)?; - let prev = >::get(); + let prev = Foo::::get(); // Because Foo has 'default', the type of 'foo' in closure is the raw type instead of an // Option<> type. - let result = >::mutate(|foo| { + let result = Foo::::mutate(|foo| { *foo = foo.saturating_add(increase_by); *foo }); @@ -447,8 +443,8 @@ impl Pallet { // Similar to other FRAME pallets, your pallet can also define a signed extension and perform some // checks and [pre/post]processing [before/after] the transaction. A signed extension can be any -// decodable type that implements `SignedExtension`. See the trait definition for the full list of -// bounds. As a convention, you can follow this approach to create an extension for your pallet: +// decodable type that implements `TransactionExtension`. See the trait definition for the full list +// of bounds. As a convention, you can follow this approach to create an extension for your pallet: // - If the extension does not carry any data, then use a tuple struct with just a `marker` // (needed for the compiler to accept `T: Config`) will suffice. // - Otherwise, create a tuple struct which contains the external data. Of course, for the entire @@ -462,18 +458,18 @@ impl Pallet { // // Using the extension, you can add some hooks to the life cycle of each transaction. Note that by // default, an extension is applied to all `Call` functions (i.e. all transactions). the `Call` enum -// variant is given to each function of `SignedExtension`. Hence, you can filter based on pallet or -// a particular call if needed. +// variant is given to each function of `TransactionExtension`. Hence, you can filter based on +// pallet or a particular call if needed. // // Some extra information, such as encoded length, some static dispatch info like weight and the // sender of the transaction (if signed) are also provided. // // The full list of hooks that can be added to a signed extension can be found -// [here](https://paritytech.github.io/polkadot-sdk/master/sp_runtime/traits/trait.SignedExtension.html). +// [here](https://paritytech.github.io/polkadot-sdk/master/sp_runtime/traits/trait.TransactionExtension.html). // // The signed extensions are aggregated in the runtime file of a substrate chain. All extensions // should be aggregated in a tuple and passed to the `CheckedExtrinsic` and `UncheckedExtrinsic` -// types defined in the runtime. Lookup `pub type SignedExtra = (...)` in `node/runtime` and +// types defined in the runtime. Lookup `pub type TxExtension = (...)` in `node/runtime` and // `node-template` for an example of this. /// A simple signed extension that checks for the `set_dummy` call. In that case, it increases the @@ -485,58 +481,51 @@ impl Pallet { #[scale_info(skip_type_params(T))] pub struct WatchDummy(PhantomData); -impl sp_std::fmt::Debug for WatchDummy { - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { +impl core::fmt::Debug for WatchDummy { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "WatchDummy") } } -impl SignedExtension for WatchDummy +impl TransactionExtensionBase for WatchDummy { + const IDENTIFIER: &'static str = "WatchDummy"; + type Implicit = (); +} +impl + TransactionExtension<::RuntimeCall, Context> for WatchDummy where ::RuntimeCall: IsSubType>, { - const IDENTIFIER: &'static str = "WatchDummy"; - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = (); type Pre = (); - - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } + type Val = (); fn validate( &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: OriginOf<::RuntimeCall>, + call: &::RuntimeCall, + _info: &DispatchInfoOf<::RuntimeCall>, len: usize, - ) -> TransactionValidity { + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult::RuntimeCall> { // if the transaction is too big, just drop it. if len > 200 { - return InvalidTransaction::ExhaustsResources.into() + return Err(InvalidTransaction::ExhaustsResources.into()) } // check for `set_dummy` - match call.is_sub_type() { + let validity = match call.is_sub_type() { Some(Call::set_dummy { .. }) => { sp_runtime::print("set_dummy was received."); let valid_tx = ValidTransaction { priority: Bounded::max_value(), ..Default::default() }; - Ok(valid_tx) + valid_tx }, - _ => Ok(Default::default()), - } + _ => Default::default(), + }; + Ok((validity, (), origin)) } + impl_tx_ext_default!(::RuntimeCall; Context; prepare); } diff --git a/substrate/frame/examples/basic/src/tests.rs b/substrate/frame/examples/basic/src/tests.rs index 2a6fbf0f054ca1ce3b6b0afc5bf72af5ddda1ee8..e460ad0992f064c72aaca7a13ce33db6d96d5dcd 100644 --- a/substrate/frame/examples/basic/src/tests.rs +++ b/substrate/frame/examples/basic/src/tests.rs @@ -27,7 +27,7 @@ use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, DispatchTransaction, IdentityLookup}, BuildStorage, }; // Reexport crate as its pallet name for construct_runtime. @@ -86,7 +86,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl Config for Test { @@ -120,25 +119,25 @@ fn it_works_for_optional_value() { // Check that GenesisBuilder works properly. let val1 = 42; let val2 = 27; - assert_eq!(Example::dummy(), Some(val1)); + assert_eq!(Dummy::::get(), Some(val1)); // Check that accumulate works when we have Some value in Dummy already. assert_ok!(Example::accumulate_dummy(RuntimeOrigin::signed(1), val2)); - assert_eq!(Example::dummy(), Some(val1 + val2)); + assert_eq!(Dummy::::get(), Some(val1 + val2)); // Check that accumulate works when we Dummy has None in it. >::on_initialize(2); assert_ok!(Example::accumulate_dummy(RuntimeOrigin::signed(1), val1)); - assert_eq!(Example::dummy(), Some(val1 + val2 + val1)); + assert_eq!(Dummy::::get(), Some(val1 + val2 + val1)); }); } #[test] fn it_works_for_default_value() { new_test_ext().execute_with(|| { - assert_eq!(Example::foo(), 24); + assert_eq!(Foo::::get(), 24); assert_ok!(Example::accumulate_foo(RuntimeOrigin::signed(1), 1)); - assert_eq!(Example::foo(), 25); + assert_eq!(Foo::::get(), 25); }); } @@ -147,7 +146,7 @@ fn set_dummy_works() { new_test_ext().execute_with(|| { let test_val = 133; assert_ok!(Example::set_dummy(RuntimeOrigin::root(), test_val.into())); - assert_eq!(Example::dummy(), Some(test_val)); + assert_eq!(Dummy::::get(), Some(test_val)); }); } @@ -159,13 +158,16 @@ fn signed_ext_watch_dummy_works() { assert_eq!( WatchDummy::(PhantomData) - .validate(&1, &call, &info, 150) + .validate_only(Some(1).into(), &call, &info, 150) .unwrap() + .0 .priority, u64::MAX, ); assert_eq!( - WatchDummy::(PhantomData).validate(&1, &call, &info, 250), + WatchDummy::(PhantomData) + .validate_only(Some(1).into(), &call, &info, 250) + .unwrap_err(), InvalidTransaction::ExhaustsResources.into(), ); }) diff --git a/substrate/frame/examples/basic/src/weights.rs b/substrate/frame/examples/basic/src/weights.rs index def944054cce8a8b5230781385fd2427e8949603..dbb9170aa67e0565b91b9bf920435f27c188773d 100644 --- a/substrate/frame/examples/basic/src/weights.rs +++ b/substrate/frame/examples/basic/src/weights.rs @@ -42,7 +42,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions needed for pallet_example_basic. pub trait WeightInfo { diff --git a/substrate/frame/examples/default-config/Cargo.toml b/substrate/frame/examples/default-config/Cargo.toml index 01ddf9d383446237fb6cf483a0142776ba31fa35..e40845a425a29d8d2868e823c1ba34dbabd136c0 100644 --- a/substrate/frame/examples/default-config/Cargo.toml +++ b/substrate/frame/examples/default-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-default-config-example" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../../support", default-features = false } frame-system = { path = "../../system", default-features = false } diff --git a/substrate/frame/examples/default-config/src/lib.rs b/substrate/frame/examples/default-config/src/lib.rs index cd1653e6c764358165c8306d4810c1d4c9bea3b4..224faf9dfd431a0ce9281c406738b7aa2fddb0f6 100644 --- a/substrate/frame/examples/default-config/src/lib.rs +++ b/substrate/frame/examples/default-config/src/lib.rs @@ -108,7 +108,7 @@ pub mod pallet { } /// A type providing default configurations for this pallet in another environment. Examples - /// could be a parachain, or a solo-chain. + /// could be a parachain, or a solochain. /// /// Appropriate derive for `frame_system::DefaultConfig` needs to be provided. In this /// example, we simple derive `frame_system::config_preludes::TestDefaultConfig` again. @@ -149,7 +149,7 @@ pub mod tests { } ); - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { // these items are defined by frame-system as `no_default`, so we must specify them here. type Block = Block; diff --git a/substrate/frame/examples/dev-mode/Cargo.toml b/substrate/frame/examples/dev-mode/Cargo.toml index f634d21cf2c958400a108549545adecacbd70087..a9c4e3f3b1fccb9085f1bc9a6a2f018a055d683a 100644 --- a/substrate/frame/examples/dev-mode/Cargo.toml +++ b/substrate/frame/examples/dev-mode/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-dev-mode" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../../support", default-features = false } frame-system = { path = "../../system", default-features = false } diff --git a/substrate/frame/examples/dev-mode/src/tests.rs b/substrate/frame/examples/dev-mode/src/tests.rs index c1501a5b4cf7682d39eaa9bfb497404d211deb1f..c13152533fdbb9bbbcf624ae9ed2c8fa9617242d 100644 --- a/substrate/frame/examples/dev-mode/src/tests.rs +++ b/substrate/frame/examples/dev-mode/src/tests.rs @@ -80,7 +80,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = (); } impl Config for Test { diff --git a/substrate/frame/examples/kitchensink/Cargo.toml b/substrate/frame/examples/kitchensink/Cargo.toml index 4255ebb66b650efb77264b250396c0a05ba0763a..37384107530ee4dca645f12f1b50e4af5547dabc 100644 --- a/substrate/frame/examples/kitchensink/Cargo.toml +++ b/substrate/frame/examples/kitchensink/Cargo.toml @@ -17,10 +17,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -frame-support = { path = "../../support", default-features = false } +frame-support = { path = "../../support", default-features = false, features = ["experimental"] } frame-system = { path = "../../system", default-features = false } sp-io = { path = "../../../primitives/io", default-features = false } diff --git a/substrate/frame/examples/kitchensink/src/benchmarking.rs b/substrate/frame/examples/kitchensink/src/benchmarking.rs index 24da581fc967b8e8c8586bf3b41a4452a89a4dcd..5f1d378e06fe67ee3978124ce7b557ad39b89e0a 100644 --- a/substrate/frame/examples/kitchensink/src/benchmarking.rs +++ b/substrate/frame/examples/kitchensink/src/benchmarking.rs @@ -51,7 +51,7 @@ mod benchmarks { set_foo(RawOrigin::Root, value, 10u128); // The execution phase is just running `set_foo` extrinsic call // This is the optional benchmark verification phase, asserting certain states. - assert_eq!(Pallet::::foo(), Some(value)) + assert_eq!(Foo::::get(), Some(value)) } // This line generates test cases for benchmarking, and could be run by: diff --git a/substrate/frame/examples/kitchensink/src/lib.rs b/substrate/frame/examples/kitchensink/src/lib.rs index 18429bc967d7c1e1f7b181e708ca3a86ef6f251c..b7425b0c0846afc3e9488a490144a89e7b647301 100644 --- a/substrate/frame/examples/kitchensink/src/lib.rs +++ b/substrate/frame/examples/kitchensink/src/lib.rs @@ -125,7 +125,6 @@ pub mod pallet { #[pallet::storage] #[pallet::unbounded] // optional #[pallet::storage_prefix = "OtherFoo"] // optional - #[pallet::getter(fn foo)] // optional pub type Foo = StorageValue; #[pallet::type_value] diff --git a/substrate/frame/examples/kitchensink/src/tests.rs b/substrate/frame/examples/kitchensink/src/tests.rs index 3b88f68d1c39cb6420c652cda838acc79a7a8710..7f62671893087119a4aff817689f54e5066d7384 100644 --- a/substrate/frame/examples/kitchensink/src/tests.rs +++ b/substrate/frame/examples/kitchensink/src/tests.rs @@ -57,7 +57,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { diff --git a/substrate/frame/examples/offchain-worker/Cargo.toml b/substrate/frame/examples/offchain-worker/Cargo.toml index 464719375c64994d9fcbe42bb53253f769d6350a..fc5151ff292b4571d9cb898d6246668d5f3b4dc8 100644 --- a/substrate/frame/examples/offchain-worker/Cargo.toml +++ b/substrate/frame/examples/offchain-worker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-example-offchain-worker" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } lite-json = { version = "0.2.0", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../../support", default-features = false } frame-system = { path = "../../system", default-features = false } diff --git a/substrate/frame/examples/offchain-worker/src/lib.rs b/substrate/frame/examples/offchain-worker/src/lib.rs index 6c1fa6ea8ec42dbee21875a0610b3724aadff802..d21c8b4cfd24971f945b73c558463ee7a1f47f77 100644 --- a/substrate/frame/examples/offchain-worker/src/lib.rs +++ b/substrate/frame/examples/offchain-worker/src/lib.rs @@ -332,7 +332,6 @@ pub mod pallet { /// /// This is used to calculate average price, should have bounded size. #[pallet::storage] - #[pallet::getter(fn prices)] pub(super) type Prices = StorageValue<_, BoundedVec, ValueQuery>; /// Defines the block when next unsigned transaction will be accepted. @@ -341,7 +340,6 @@ pub mod pallet { /// we only allow one transaction every `T::UnsignedInterval` blocks. /// This storage entry defines when new transaction is going to be accepted. #[pallet::storage] - #[pallet::getter(fn next_unsigned_at)] pub(super) type NextUnsignedAt = StorageValue<_, BlockNumberFor, ValueQuery>; } @@ -479,7 +477,7 @@ impl Pallet { ) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if next_unsigned_at > block_number { return Err("Too early to send unsigned transaction") } @@ -513,7 +511,7 @@ impl Pallet { ) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if next_unsigned_at > block_number { return Err("Too early to send unsigned transaction") } @@ -543,7 +541,7 @@ impl Pallet { ) -> Result<(), &'static str> { // Make sure we don't fetch the price if unsigned transaction is going to be rejected // anyway. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if next_unsigned_at > block_number { return Err("Too early to send unsigned transaction") } @@ -664,7 +662,7 @@ impl Pallet { /// Calculate current average price. fn average_price() -> Option { - let prices = >::get(); + let prices = Prices::::get(); if prices.is_empty() { None } else { @@ -677,7 +675,7 @@ impl Pallet { new_price: &u32, ) -> TransactionValidity { // Now let's check if the transaction has any chance to succeed. - let next_unsigned_at = >::get(); + let next_unsigned_at = NextUnsignedAt::::get(); if &next_unsigned_at > block_number { return InvalidTransaction::Stale.into() } diff --git a/substrate/frame/examples/offchain-worker/src/tests.rs b/substrate/frame/examples/offchain-worker/src/tests.rs index ea37a2da493d69ae6550c8d999fe2eb9dcfefad1..81f2a40840badd286be14b381df1db904aeeb2b1 100644 --- a/substrate/frame/examples/offchain-worker/src/tests.rs +++ b/substrate/frame/examples/offchain-worker/src/tests.rs @@ -30,7 +30,7 @@ use sp_core::{ use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; use sp_runtime::{ - testing::TestXt, + generic::UncheckedExtrinsic, traits::{BlakeTwo256, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, Verify}, RuntimeAppPublic, }; @@ -73,7 +73,7 @@ impl frame_system::Config for Test { type MaxConsumers = ConstU32<16>; } -type Extrinsic = TestXt; +type Extrinsic = UncheckedExtrinsic; type AccountId = <::Signer as IdentifyAccount>::AccountId; impl frame_system::offchain::SigningTypes for Test { @@ -99,7 +99,7 @@ where _account: AccountId, nonce: u64, ) -> Option<(RuntimeCall, ::SignaturePayload)> { - Some((call, (nonce, ()))) + Some((call, (nonce, (), ()))) } } @@ -219,8 +219,8 @@ fn should_submit_signed_transaction_on_chain() { let tx = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature.unwrap().0, 0); - assert_eq!(tx.call, RuntimeCall::Example(crate::Call::submit_price { price: 15523 })); + assert!(matches!(tx.preamble, sp_runtime::generic::Preamble::Signed(0, (), ()))); + assert_eq!(tx.function, RuntimeCall::Example(crate::Call::submit_price { price: 15523 })); }); } @@ -259,11 +259,11 @@ fn should_submit_unsigned_transaction_on_chain_for_any_account() { // then let tx = pool_state.write().transactions.pop().unwrap(); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature, None); + assert!(tx.is_inherent()); if let RuntimeCall::Example(crate::Call::submit_price_unsigned_with_signed_payload { price_payload: body, signature, - }) = tx.call + }) = tx.function { assert_eq!(body, price_payload); @@ -313,11 +313,11 @@ fn should_submit_unsigned_transaction_on_chain_for_all_accounts() { // then let tx = pool_state.write().transactions.pop().unwrap(); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature, None); + assert!(tx.is_inherent()); if let RuntimeCall::Example(crate::Call::submit_price_unsigned_with_signed_payload { price_payload: body, signature, - }) = tx.call + }) = tx.function { assert_eq!(body, price_payload); @@ -353,9 +353,9 @@ fn should_submit_raw_unsigned_transaction_on_chain() { let tx = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature, None); + assert!(tx.is_inherent()); assert_eq!( - tx.call, + tx.function, RuntimeCall::Example(crate::Call::submit_price_unsigned { block_number: 1, price: 15523 diff --git a/substrate/frame/examples/single-block-migrations/Cargo.toml b/substrate/frame/examples/single-block-migrations/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5059b2433c77139568d99326473cded73a5af9e9 --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "pallet-example-single-block-migrations" +version = "0.0.1" +authors.workspace = true +edition.workspace = true +license = "MIT-0" +homepage = "https://substrate.io" +repository.workspace = true +description = "FRAME example pallet demonstrating best-practices for writing storage migrations." +publish = false + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +docify = { version = "0.2.3", default-features = false } +log = { version = "0.4.20", default-features = false } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +frame-support = { path = "../../support", default-features = false } +frame-executive = { path = "../../executive", default-features = false } +frame-system = { path = "../../system", default-features = false } +frame-try-runtime = { path = "../../try-runtime", default-features = false, optional = true } +pallet-balances = { path = "../../balances", default-features = false } +sp-std = { path = "../../../primitives/std", default-features = false } +sp-runtime = { path = "../../../primitives/runtime", default-features = false } +sp-core = { path = "../../../primitives/core", default-features = false } +sp-io = { path = "../../../primitives/io", default-features = false } +sp-version = { path = "../../../primitives/version", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-executive/std", + "frame-support/std", + "frame-system/std", + "frame-try-runtime/std", + "log/std", + "pallet-balances/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-version/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/examples/single-block-migrations/src/lib.rs b/substrate/frame/examples/single-block-migrations/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..86a9e5d6e95bbeaa0230b81153c3f4149f332436 --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/lib.rs @@ -0,0 +1,213 @@ +// 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. + +//! # Single Block Migration Example Pallet +//! +//! An example pallet demonstrating best-practices for writing single-block migrations in the +//! context of upgrading pallet storage. +//! +//! ## Forwarning +//! +//! Single block migrations **MUST** execute in a single block, therefore when executed on a +//! parachain are only appropriate when guaranteed to not exceed block weight limits. If a +//! parachain submits a block that exceeds the block weight limit it will **brick the chain**! +//! +//! If weight is a concern or you are not sure which type of migration to use, you should probably +//! use a multi-block migration. +//! +//! TODO: Link above to multi-block migration example. +//! +//! ## Pallet Overview +//! +//! This example pallet contains a single storage item [`Value`](pallet::Value), which may be set by +//! any signed origin by calling the [`set_value`](crate::Call::set_value) extrinsic. +//! +//! For the purposes of this exercise, we imagine that in [`StorageVersion`] V0 of this pallet +//! [`Value`](pallet::Value) is a `u32`, and this what is currently stored on-chain. +//! +//! ```ignore +//! // (Old) Storage Version V0 representation of `Value` +//! #[pallet::storage] +//! pub type Value = StorageValue<_, u32>; +//! ``` +//! +//! In [`StorageVersion`] V1 of the pallet a new struct [`CurrentAndPreviousValue`] is introduced: +#![doc = docify::embed!("src/lib.rs", CurrentAndPreviousValue)] +//! and [`Value`](pallet::Value) is updated to store this new struct instead of a `u32`: +#![doc = docify::embed!("src/lib.rs", Value)] +//! +//! In StorageVersion V1 of the pallet when [`set_value`](crate::Call::set_value) is called, the +//! new value is stored in the `current` field of [`CurrentAndPreviousValue`], and the previous +//! value (if it exists) is stored in the `previous` field. +#![doc = docify::embed!("src/lib.rs", pallet_calls)] +//! +//! ## Why a migration is necessary +//! +//! Without a migration, there will be a discrepancy between the on-chain storage for [`Value`] (in +//! V0 it is a `u32`) and the current storage for [`Value`] (in V1 it was changed to a +//! [`CurrentAndPreviousValue`] struct). +//! +//! The on-chain storage for [`Value`] would be a `u32` but the runtime would try to read it as a +//! [`CurrentAndPreviousValue`]. This would result in unacceptable undefined behavior. +//! +//! ## Adding a migration module +//! +//! Writing a pallets migrations in a seperate module is strongly recommended. +//! +//! Here's how the migration module is defined for this pallet: +//! +//! ```text +//! substrate/frame/examples/single-block-migrations/src/ +//! ├── lib.rs <-- pallet definition +//! ├── Cargo.toml <-- pallet manifest +//! └── migrations/ +//! ├── mod.rs <-- migrations module definition +//! └── v1.rs <-- migration logic for the V0 to V1 transition +//! ``` +//! +//! This structure allows keeping migration logic separate from the pallet logic and +//! easily adding new migrations in the future. +//! +//! ## Writing the Migration +//! +//! All code related to the migration can be found under +//! [`v1.rs`](migrations::v1). +//! +//! See the migration source code for detailed comments. +//! +//! To keep the migration logic organised, it is split across additional modules: +//! +//! ### `mod v0` +//! +//! Here we define a [`storage_alias`](frame_support::storage_alias) for the old v0 [`Value`] +//! format. +//! +//! This allows reading the old v0 value from storage during the migration. +//! +//! ### `mod version_unchecked` +//! +//! Here we define our raw migration logic, +//! `version_unchecked::MigrateV0ToV1` which implements the [`OnRuntimeUpgrade`] trait. +//! +//! Importantly, it is kept in a private module so that it cannot be accidentally used in a runtime. +//! +//! Private modules cannot be referenced in docs, so please read the code directly. +//! +//! #### Standalone Struct or Pallet Hook? +//! +//! Note that the storage migration logic is attached to a standalone struct implementing +//! [`OnRuntimeUpgrade`], rather than implementing the +//! [`Hooks::on_runtime_upgrade`](frame_support::traits::Hooks::on_runtime_upgrade) hook directly on +//! the pallet. The pallet hook is better suited for special types of logic that need to execute on +//! every runtime upgrade, but not so much for one-off storage migrations. +//! +//! ### `pub mod versioned` +//! +//! Here, `version_unchecked::MigrateV0ToV1` is wrapped in a +//! [`VersionedMigration`] to define +//! [`versioned::MigrateV0ToV1`](crate::migrations::v1::versioned::MigrateV0ToV1), which may be used +//! in runtimes. +//! +//! Using [`VersionedMigration`] ensures that +//! - The migration only runs once when the on-chain storage version is `0` +//! - The on-chain storage version is updated to `1` after the migration executes +//! - Reads and writes from checking and setting the on-chain storage version are accounted for in +//! the final [`Weight`](frame_support::weights::Weight) +//! +//! This is the only public module exported from `v1`. +//! +//! ### `mod test` +//! +//! Here basic unit tests are defined for the migration. +//! +//! When writing migration tests, don't forget to check: +//! - `on_runtime_upgrade` returns the expected weight +//! - `post_upgrade` succeeds when given the bytes returned by `pre_upgrade` +//! - Pallet storage is in the expected state after the migration +//! +//! [`VersionedMigration`]: frame_support::migrations::VersionedMigration +//! [`GetStorageVersion`]: frame_support::traits::GetStorageVersion +//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade +//! [`MigrateV0ToV1`]: crate::migrations::v1::versioned::MigrationV0ToV1 + +// We make sure this pallet uses `no_std` for compiling to Wasm. +#![cfg_attr(not(feature = "std"), no_std)] +// allow non-camel-case names for storage version V0 value +#![allow(non_camel_case_types)] + +// Re-export pallet items so that they can be accessed from the crate namespace. +pub use pallet::*; + +// Export migrations so they may be used in the runtime. +pub mod migrations; +#[doc(hidden)] +mod mock; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::StorageVersion; +use sp_runtime::RuntimeDebug; + +/// Example struct holding the most recently set [`u32`] and the +/// second most recently set [`u32`] (if one existed). +#[docify::export] +#[derive( + Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen, +)] +pub struct CurrentAndPreviousValue { + /// The most recently set value. + pub current: u32, + /// The previous value, if one existed. + pub previous: Option, +} + +// Pallet for demonstrating storage migrations. +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// Define the current [`StorageVersion`] of the pallet. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + /// [`StorageVersion`] V1 of [`Value`]. + /// + /// Currently used. + #[docify::export] + #[pallet::storage] + pub type Value = StorageValue<_, CurrentAndPreviousValue>; + + #[docify::export(pallet_calls)] + #[pallet::call] + impl Pallet { + pub fn set_value(origin: OriginFor, value: u32) -> DispatchResult { + ensure_signed(origin)?; + + let previous = Value::::get().map(|v| v.current); + let new_struct = CurrentAndPreviousValue { current: value, previous }; + >::put(new_struct); + + Ok(()) + } + } +} diff --git a/substrate/frame/examples/single-block-migrations/src/migrations/mod.rs b/substrate/frame/examples/single-block-migrations/src/migrations/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..80a33f69941aa1a49563971ab0c9e13a88ecda19 --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/migrations/mod.rs @@ -0,0 +1,20 @@ +// 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. + +/// Module containing all logic associated with the example migration from +/// [`StorageVersion`](frame_support::traits::StorageVersion) V0 to V1. +pub mod v1; diff --git a/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs new file mode 100644 index 0000000000000000000000000000000000000000..b46640a320207551ab55b1dc793574ce68c4c26c --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs @@ -0,0 +1,222 @@ +// 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 frame_support::{ + storage_alias, + traits::{Get, OnRuntimeUpgrade}, +}; + +#[cfg(feature = "try-runtime")] +use sp_std::vec::Vec; + +/// Collection of storage item formats from the previous storage version. +/// +/// Required so we can read values in the v0 storage format during the migration. +mod v0 { + use super::*; + + /// V0 type for [`crate::Value`]. + #[storage_alias] + pub type Value = StorageValue, u32>; +} + +/// Private module containing *version unchecked* migration logic. +/// +/// Should only be used by the [`VersionedMigration`](frame_support::migrations::VersionedMigration) +/// type in this module to create something to export. +/// +/// The unversioned migration should be kept private so the unversioned migration cannot +/// accidentally be used in any runtimes. +/// +/// For more about this pattern of keeping items private, see +/// - +/// - +mod version_unchecked { + use super::*; + + /// Implements [`OnRuntimeUpgrade`], migrating the state of this pallet from V0 to V1. + /// + /// In V0 of the template [`crate::Value`] is just a `u32`. In V1, it has been upgraded to + /// contain the struct [`crate::CurrentAndPreviousValue`]. + /// + /// In this migration, update the on-chain storage for the pallet to reflect the new storage + /// layout. + pub struct MigrateV0ToV1(sp_std::marker::PhantomData); + + impl OnRuntimeUpgrade for MigrateV0ToV1 { + /// Return the existing [`crate::Value`] so we can check that it was correctly set in + /// `version_unchecked::MigrateV0ToV1::post_upgrade`. + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + use codec::Encode; + + // Access the old value using the `storage_alias` type + let old_value = v0::Value::::get(); + // Return it as an encoded `Vec` + Ok(old_value.encode()) + } + + /// Migrate the storage from V0 to V1. + /// + /// - If the value doesn't exist, there is nothing to do. + /// - If the value exists, it is read and then written back to storage inside a + /// [`crate::CurrentAndPreviousValue`]. + fn on_runtime_upgrade() -> frame_support::weights::Weight { + // Read the old value from storage + if let Some(old_value) = v0::Value::::take() { + // Write the new value to storage + let new = crate::CurrentAndPreviousValue { current: old_value, previous: None }; + crate::Value::::put(new); + // One read for the old value, one write for the new value + T::DbWeight::get().reads_writes(1, 1) + } else { + // One read for trying to access the old value + T::DbWeight::get().reads(1) + } + } + + /// Verifies the storage was migrated correctly. + /// + /// - If there was no old value, the new value should not be set. + /// - If there was an old value, the new value should be a + /// [`crate::CurrentAndPreviousValue`]. + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use codec::Decode; + use frame_support::ensure; + + let maybe_old_value = Option::::decode(&mut &state[..]).map_err(|_| { + sp_runtime::TryRuntimeError::Other("Failed to decode old value from storage") + })?; + + match maybe_old_value { + Some(old_value) => { + let expected_new_value = + crate::CurrentAndPreviousValue { current: old_value, previous: None }; + let actual_new_value = crate::Value::::get(); + + ensure!(actual_new_value.is_some(), "New value not set"); + ensure!( + actual_new_value == Some(expected_new_value), + "New value not set correctly" + ); + }, + None => { + ensure!(crate::Value::::get().is_none(), "New value unexpectedly set"); + }, + }; + Ok(()) + } + } +} + +/// Public module containing *version checked* migration logic. +/// +/// This is the only module that should be exported from this module. +/// +/// See [`VersionedMigration`](frame_support::migrations::VersionedMigration) docs for more about +/// how it works. +pub mod versioned { + use super::*; + + /// `version_unchecked::MigrateV0ToV1` wrapped in a + /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that: + /// - The migration only runs once when the on-chain storage version is 0 + /// - The on-chain storage version is updated to `1` after the migration executes + /// - Reads/Writes from checking/settings the on-chain storage version are accounted for + pub type MigrateV0ToV1 = frame_support::migrations::VersionedMigration< + 0, // The migration will only execute when the on-chain storage version is 0 + 1, // The on-chain storage version will be set to 1 after the migration is complete + version_unchecked::MigrateV0ToV1, + crate::pallet::Pallet, + ::DbWeight, + >; +} + +/// Tests for our migration. +/// +/// When writing migration tests, it is important to check: +/// 1. `on_runtime_upgrade` returns the expected weight +/// 2. `post_upgrade` succeeds when given the bytes returned by `pre_upgrade` +/// 3. The storage is in the expected state after the migration +#[cfg(any(all(feature = "try-runtime", test), doc))] +mod test { + use super::*; + use crate::mock::{new_test_ext, MockRuntime}; + use frame_support::assert_ok; + use version_unchecked::MigrateV0ToV1; + + #[test] + fn handles_no_existing_value() { + new_test_ext().execute_with(|| { + // By default, no value should be set. Verify this assumption. + assert!(crate::Value::::get().is_none()); + assert!(v0::Value::::get().is_none()); + + // Get the pre_upgrade bytes + let bytes = match MigrateV0ToV1::::pre_upgrade() { + Ok(bytes) => bytes, + Err(e) => panic!("pre_upgrade failed: {:?}", e), + }; + + // Execute the migration + let weight = MigrateV0ToV1::::on_runtime_upgrade(); + + // Verify post_upgrade succeeds + assert_ok!(MigrateV0ToV1::::post_upgrade(bytes)); + + // The weight should be just 1 read for trying to access the old value. + assert_eq!(weight, ::DbWeight::get().reads(1)); + + // After the migration, no value should have been set. + assert!(crate::Value::::get().is_none()); + }) + } + + #[test] + fn handles_existing_value() { + new_test_ext().execute_with(|| { + // Set up an initial value + let initial_value = 42; + v0::Value::::put(initial_value); + + // Get the pre_upgrade bytes + let bytes = match MigrateV0ToV1::::pre_upgrade() { + Ok(bytes) => bytes, + Err(e) => panic!("pre_upgrade failed: {:?}", e), + }; + + // Execute the migration + let weight = MigrateV0ToV1::::on_runtime_upgrade(); + + // Verify post_upgrade succeeds + assert_ok!(MigrateV0ToV1::::post_upgrade(bytes)); + + // The weight used should be 1 read for the old value, and 1 write for the new + // value. + assert_eq!( + weight, + ::DbWeight::get().reads_writes(1, 1) + ); + + // After the migration, the new value should be set as the `current` value. + let expected_new_value = + crate::CurrentAndPreviousValue { current: initial_value, previous: None }; + assert_eq!(crate::Value::::get(), Some(expected_new_value)); + }) + } +} diff --git a/substrate/frame/examples/single-block-migrations/src/mock.rs b/substrate/frame/examples/single-block-migrations/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..02f72e01836f2462de2043cae737ea46ec2072ed --- /dev/null +++ b/substrate/frame/examples/single-block-migrations/src/mock.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. + +#![cfg(any(all(feature = "try-runtime", test), doc))] + +use crate::*; +use frame_support::{derive_impl, traits::ConstU64, weights::constants::ParityDbWeight}; + +// Re-export crate as its pallet name for construct_runtime. +use crate as pallet_example_storage_migration; + +type Block = frame_system::mocking::MockBlock; + +// For testing the pallet, we construct a mock runtime. +frame_support::construct_runtime!( + pub struct MockRuntime { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Example: pallet_example_storage_migration::{Pallet, Call, Storage}, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for MockRuntime { + type Block = Block; + type AccountData = pallet_balances::AccountData; + type DbWeight = ParityDbWeight; +} + +impl pallet_balances::Config for MockRuntime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); +} + +impl Config for MockRuntime {} + +pub fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; + + let t = RuntimeGenesisConfig { system: Default::default(), balances: Default::default() } + .build_storage() + .unwrap(); + t.into() +} diff --git a/substrate/frame/examples/split/Cargo.toml b/substrate/frame/examples/split/Cargo.toml index 97f9062f18189f30542baa0fd08de9b2d98de52d..d140fc3eef43b04ca7b9175efbdae2830e2d6282 100644 --- a/substrate/frame/examples/split/Cargo.toml +++ b/substrate/frame/examples/split/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-example-split" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "MIT-0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../../support", default-features = false } diff --git a/substrate/frame/examples/split/src/lib.rs b/substrate/frame/examples/split/src/lib.rs index 74d2e0cc24b7bf5789da19f2657c5e7d2514bbfe..5245d90e390cff1ccab79ecc279fe57188cde030 100644 --- a/substrate/frame/examples/split/src/lib.rs +++ b/substrate/frame/examples/split/src/lib.rs @@ -107,7 +107,7 @@ pub mod pallet { let _who = ensure_signed(origin)?; // Read a value from storage. - match >::get() { + match Something::::get() { // Return an error if the value has not been set. None => return Err(Error::::NoneValue.into()), Some(old) => { diff --git a/substrate/frame/examples/src/lib.rs b/substrate/frame/examples/src/lib.rs index f38bbe52dc114900e4b497cb17da6deb4406512c..dee23a41379fc8b732252b9fe4db39bf809699fd 100644 --- a/substrate/frame/examples/src/lib.rs +++ b/substrate/frame/examples/src/lib.rs @@ -43,6 +43,9 @@ //! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be //! built using only the `frame` umbrella crate. //! +//! - [`pallet_example_single_block_migrations`]: An example pallet demonstrating best-practices for +//! writing storage migrations. +//! //! - [`pallet_example_tasks`]: This pallet demonstrates the use of `Tasks` to execute service work. //! //! **Tip**: Use `cargo doc --package --open` to view each pallet's documentation. diff --git a/substrate/frame/examples/tasks/Cargo.toml b/substrate/frame/examples/tasks/Cargo.toml index 438cb60c756fa0512c9c9e2c7fb4b44d35a95ebe..41521114366a298861c883b4d6f14b0eaf6c4276 100644 --- a/substrate/frame/examples/tasks/Cargo.toml +++ b/substrate/frame/examples/tasks/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-example-tasks" -version = "1.0.0-dev" +version = "1.0.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../../support", default-features = false } @@ -24,7 +24,7 @@ frame-system = { path = "../../system", default-features = false } sp-io = { path = "../../../primitives/io", default-features = false } sp-runtime = { path = "../../../primitives/runtime", default-features = false } sp-std = { path = "../../../primitives/std", default-features = false } -sp-core = { version = "21.0.0", default-features = false, path = "../../../primitives/core" } +sp-core = { default-features = false, path = "../../../primitives/core" } frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true } diff --git a/substrate/frame/examples/tasks/src/weights.rs b/substrate/frame/examples/tasks/src/weights.rs index 793af6e962201fd9f92e0260ea0e24f5bc39753d..c9ddea6f9a8ab4ed19d12f8db89fa3dff59cca3f 100644 --- a/substrate/frame/examples/tasks/src/weights.rs +++ b/substrate/frame/examples/tasks/src/weights.rs @@ -15,30 +15,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for `pallet_example_tasks` +//! Autogenerated weights for `tasks_example` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-02, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `MacBook.local`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/release/node-template +// ./target/production/substrate-node // benchmark // pallet -// --chain -// dev -// --pallet -// pallet_example_tasks -// --extrinsic -// * -// --steps -// 20 -// --repeat -// 10 -// --output -// frame/examples/tasks/src/weights.rs +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=tasks_example +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/examples/tasks/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,37 +49,42 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_template. +/// Weight functions needed for `tasks_example`. pub trait WeightInfo { fn add_number_into_total() -> Weight; } -/// Weight functions for `pallet_example_kitchensink`. +/// Weights for `tasks_example` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Kitchensink OtherFoo (r:0 w:1) - /// Proof Skipped: Kitchensink OtherFoo (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TasksExample::Numbers` (r:1 w:1) + /// Proof: `TasksExample::Numbers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TasksExample::Total` (r:1 w:1) + /// Proof: `TasksExample::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn add_number_into_total() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 1_000_000 picoseconds. - Weight::from_parts(1_000_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(1)) + // Measured: `149` + // Estimated: `3614` + // Minimum execution time: 5_776_000 picoseconds. + Weight::from_parts(6_178_000, 3614) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Kitchensink OtherFoo (r:0 w:1) - /// Proof Skipped: Kitchensink OtherFoo (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TasksExample::Numbers` (r:1 w:1) + /// Proof: `TasksExample::Numbers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TasksExample::Total` (r:1 w:1) + /// Proof: `TasksExample::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn add_number_into_total() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 1_000_000 picoseconds. - Weight::from_parts(1_000_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Measured: `149` + // Estimated: `3614` + // Minimum execution time: 5_776_000 picoseconds. + Weight::from_parts(6_178_000, 3614) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/substrate/frame/executive/Cargo.toml b/substrate/frame/executive/Cargo.toml index b98ceb0ba9a57042f56ccb9d782e84535a9a3c53..63285e4cb4939c10db3342b5d59eeffc63103d48 100644 --- a/substrate/frame/executive/Cargo.toml +++ b/substrate/frame/executive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-executive" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,10 +16,11 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +aquamarine = "0.3.2" codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } @@ -44,6 +45,7 @@ default = ["std"] with-tracing = ["sp-tracing/with-tracing"] std = [ "codec/std", + "frame-support/experimental", "frame-support/std", "frame-system/std", "frame-try-runtime/std", diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index cfeb585b0fdcb152379022fabd6a7a974e00b37f..3028eaf318e0881c1b6f4d4ea6ac17adfe99a75f 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -15,6 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![cfg_attr(not(feature = "std"), no_std)] + //! # Executive Module //! //! The Executive module acts as the orchestration layer for the runtime. It dispatches incoming @@ -35,6 +37,8 @@ //! - Finalize a block. //! - Start an off-chain worker. //! +//! The flow of their application in a block is explained in the [block flowchart](block_flowchart). +//! //! ### Implementations //! //! The Executive module provides the following implementations: @@ -114,17 +118,51 @@ //! pub type Executive = executive::Executive; //! ``` -#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(doc)] +#[cfg_attr(doc, aquamarine::aquamarine)] +/// # Block Execution +/// +/// These are the steps of block execution as done by [`Executive::execute_block`]. A block is +/// invalid if any of them fail. +/// +/// ```mermaid +/// flowchart TD +/// Executive::execute_block --> on_runtime_upgrade +/// on_runtime_upgrade --> System::initialize +/// Executive::initialize_block --> System::initialize +/// System::initialize --> on_initialize +/// on_initialize --> PreInherents[System::PreInherents] +/// PreInherents --> Inherents[Apply Inherents] +/// Inherents --> PostInherents[System::PostInherents] +/// PostInherents --> Check{MBM ongoing?} +/// Check -->|No| poll +/// Check -->|Yes| post_transactions_2[System::PostTransaction] +/// post_transactions_2 --> Step[MBMs::step] +/// Step --> on_finalize +/// poll --> transactions[Apply Transactions] +/// transactions --> post_transactions_1[System::PostTransaction] +/// post_transactions_1 --> CheckIdle{Weight remaining?} +/// CheckIdle -->|Yes| on_idle +/// CheckIdle -->|No| on_finalize +/// on_idle --> on_finalize +/// ``` +pub mod block_flowchart {} + +#[cfg(test)] +mod tests; use codec::{Codec, Encode}; use frame_support::{ + defensive_assert, dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + migrations::MultiStepMigrator, pallet_prelude::InvalidTransaction, traits::{ BeforeAllRuntimeMigrations, EnsureInherentsAreFirst, ExecuteBlock, OffchainWorker, - OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade, + OnFinalize, OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, PostInherents, + PostTransactions, PreInherents, }, - weights::Weight, + weights::{Weight, WeightMeter}, }; use frame_system::pallet_prelude::BlockNumberFor; use sp_runtime::{ @@ -134,7 +172,7 @@ use sp_runtime::{ ValidateUnsigned, Zero, }, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, + ApplyExtrinsicResult, ExtrinsicInclusionMode, }; use sp_std::{marker::PhantomData, prelude::*}; @@ -198,7 +236,8 @@ impl< + OnInitialize> + OnIdle> + OnFinalize> - + OffchainWorker>, + + OffchainWorker> + + OnPoll>, COnRuntimeUpgrade: OnRuntimeUpgrade, > ExecuteBlock for Executive @@ -237,6 +276,7 @@ impl< + OnIdle> + OnFinalize> + OffchainWorker> + + OnPoll> + TryState> + TryDecodeEntireStorage, COnRuntimeUpgrade: OnRuntimeUpgrade, @@ -272,36 +312,50 @@ where select, ); - Self::initialize_block(block.header()); - Self::initial_checks(&block); - + let mode = Self::initialize_block(block.header()); + let num_inherents = Self::initial_checks(&block) as usize; let (header, extrinsics) = block.deconstruct(); + // Check if there are any forbidden non-inherents in the block. + if mode == ExtrinsicInclusionMode::OnlyInherents && extrinsics.len() > num_inherents { + return Err("Only inherents allowed".into()) + } + let try_apply_extrinsic = |uxt: Block::Extrinsic| -> ApplyExtrinsicResult { sp_io::init_tracing(); let encoded = uxt.encode(); let encoded_len = encoded.len(); + let is_inherent = System::is_inherent(&uxt); // skip signature verification. let xt = if signature_check { uxt.check(&Default::default()) } else { uxt.unchecked_into_checked_i_know_what_i_am_doing(&Default::default()) }?; - >::note_extrinsic(encoded); let dispatch_info = xt.get_dispatch_info(); + if !is_inherent && !>::inherents_applied() { + Self::inherents_applied(); + } + + >::note_extrinsic(encoded); let r = Applyable::apply::(xt, &dispatch_info, encoded_len)?; + if r.is_err() && dispatch_info.class == DispatchClass::Mandatory { + return Err(InvalidTransaction::BadMandatory.into()) + } + >::note_applied_extrinsic(&r, dispatch_info); Ok(r.map(|_| ()).map_err(|e| e.error)) }; - for e in extrinsics { + // Apply extrinsics: + for e in extrinsics.iter() { if let Err(err) = try_apply_extrinsic(e.clone()) { log::error!( - target: LOG_TARGET, "executing transaction {:?} failed due to {:?}. Aborting the rest of the block execution.", + target: LOG_TARGET, "transaction {:?} failed due to {:?}. Aborting the rest of the block execution.", e, err, ); @@ -309,9 +363,17 @@ where } } + // In this case there were no transactions to trigger this state transition: + if !>::inherents_applied() { + Self::inherents_applied(); + } + // post-extrinsics book-keeping >::note_finished_extrinsics(); - Self::idle_and_finalize_hook(*header.number()); + ::PostTransactions::post_transactions(); + + Self::on_idle_hook(*header.number()); + Self::on_finalize_hook(*header.number()); // run the try-state checks of all pallets, ensuring they don't alter any state. let _guard = frame_support::StorageNoopGuard::default(); @@ -449,7 +511,8 @@ impl< + OnInitialize> + OnIdle> + OnFinalize> - + OffchainWorker>, + + OffchainWorker> + + OnPoll>, COnRuntimeUpgrade: OnRuntimeUpgrade, > Executive where @@ -464,16 +527,36 @@ where pub fn execute_on_runtime_upgrade() -> Weight { let before_all_weight = ::before_all_runtime_migrations(); - <(COnRuntimeUpgrade, AllPalletsWithSystem) as OnRuntimeUpgrade>::on_runtime_upgrade() - .saturating_add(before_all_weight) + + let runtime_upgrade_weight = <( + COnRuntimeUpgrade, + ::SingleBlockMigrations, + // We want to run the migrations before we call into the pallets as they may + // access any state that would then not be migrated. + AllPalletsWithSystem, + ) as OnRuntimeUpgrade>::on_runtime_upgrade(); + + before_all_weight.saturating_add(runtime_upgrade_weight) } /// Start the execution of a particular block. - pub fn initialize_block(header: &frame_system::pallet_prelude::HeaderFor) { + pub fn initialize_block( + header: &frame_system::pallet_prelude::HeaderFor, + ) -> ExtrinsicInclusionMode { sp_io::init_tracing(); sp_tracing::enter_span!(sp_tracing::Level::TRACE, "init_block"); let digests = Self::extract_pre_digest(header); Self::initialize_block_impl(header.number(), header.parent_hash(), &digests); + + Self::extrinsic_mode() + } + + fn extrinsic_mode() -> ExtrinsicInclusionMode { + if ::MultiBlockMigrator::ongoing() { + ExtrinsicInclusionMode::OnlyInherents + } else { + ExtrinsicInclusionMode::AllExtrinsics + } } fn extract_pre_digest(header: &frame_system::pallet_prelude::HeaderFor) -> Digest { @@ -519,6 +602,7 @@ where ); frame_system::Pallet::::note_finished_initialize(); + ::PreInherents::pre_inherents(); } /// Returns if the runtime has been upgraded, based on [`frame_system::LastRuntimeUpgrade`]. @@ -529,7 +613,8 @@ where last.map(|v| v.was_upgraded(¤t)).unwrap_or(true) } - fn initial_checks(block: &Block) { + /// Returns the number of inherents in the block. + fn initial_checks(block: &Block) -> u32 { sp_tracing::enter_span!(sp_tracing::Level::TRACE, "initial_checks"); let header = block.header(); @@ -542,8 +627,9 @@ where "Parent hash should be valid.", ); - if let Err(i) = System::ensure_inherents_are_first(block) { - panic!("Invalid inherent position for extrinsic at index {}", i); + match System::ensure_inherents_are_first(block) { + Ok(num) => num, + Err(i) => panic!("Invalid inherent position for extrinsic at index {}", i), } } @@ -552,53 +638,90 @@ where sp_io::init_tracing(); sp_tracing::within_span! { sp_tracing::info_span!("execute_block", ?block); + // Execute `on_runtime_upgrade` and `on_initialize`. + let mode = Self::initialize_block(block.header()); + let num_inherents = Self::initial_checks(&block) as usize; + let (header, extrinsics) = block.deconstruct(); + let num_extrinsics = extrinsics.len(); - Self::initialize_block(block.header()); + if mode == ExtrinsicInclusionMode::OnlyInherents && num_extrinsics > num_inherents { + // Invalid block + panic!("Only inherents are allowed in this block") + } - // any initial checks - Self::initial_checks(&block); + Self::apply_extrinsics(extrinsics.into_iter()); - // execute extrinsics - let (header, extrinsics) = block.deconstruct(); - Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number()); + // In this case there were no transactions to trigger this state transition: + if !>::inherents_applied() { + defensive_assert!(num_inherents == num_extrinsics); + Self::inherents_applied(); + } - // any final checks + >::note_finished_extrinsics(); + ::PostTransactions::post_transactions(); + + Self::on_idle_hook(*header.number()); + Self::on_finalize_hook(*header.number()); Self::final_checks(&header); } } - /// Execute given extrinsics and take care of post-extrinsics book-keeping. - fn execute_extrinsics_with_book_keeping( - extrinsics: Vec, - block_number: NumberFor, - ) { + /// Logic that runs directly after inherent application. + /// + /// It advances the Multi-Block-Migrations or runs the `on_poll` hook. + pub fn inherents_applied() { + >::note_inherents_applied(); + ::PostInherents::post_inherents(); + + if ::MultiBlockMigrator::ongoing() { + let used_weight = ::MultiBlockMigrator::step(); + >::register_extra_weight_unchecked( + used_weight, + DispatchClass::Mandatory, + ); + } else { + let block_number = >::block_number(); + Self::on_poll_hook(block_number); + } + } + + /// Execute given extrinsics. + fn apply_extrinsics(extrinsics: impl Iterator) { extrinsics.into_iter().for_each(|e| { if let Err(e) = Self::apply_extrinsic(e) { let err: &'static str = e.into(); panic!("{}", err) } }); - - // post-extrinsics book-keeping - >::note_finished_extrinsics(); - - Self::idle_and_finalize_hook(block_number); } /// Finalize the block - it is up the caller to ensure that all header fields are valid /// except state-root. + // Note: Only used by the block builder - not Executive itself. pub fn finalize_block() -> frame_system::pallet_prelude::HeaderFor { sp_io::init_tracing(); sp_tracing::enter_span!(sp_tracing::Level::TRACE, "finalize_block"); - >::note_finished_extrinsics(); - let block_number = >::block_number(); - Self::idle_and_finalize_hook(block_number); + // In this case there were no transactions to trigger this state transition: + if !>::inherents_applied() { + Self::inherents_applied(); + } + >::note_finished_extrinsics(); + ::PostTransactions::post_transactions(); + let block_number = >::block_number(); + Self::on_idle_hook(block_number); + Self::on_finalize_hook(block_number); >::finalize() } - fn idle_and_finalize_hook(block_number: NumberFor) { + /// Run the `on_idle` hook of all pallet, but only if there is weight remaining and there are no + /// ongoing MBMs. + fn on_idle_hook(block_number: NumberFor) { + if ::MultiBlockMigrator::ongoing() { + return + } + let weight = >::block_weight(); let max_weight = >::get().max_block; let remaining_weight = max_weight.saturating_sub(weight.total()); @@ -613,7 +736,33 @@ where DispatchClass::Mandatory, ); } + } + + fn on_poll_hook(block_number: NumberFor) { + defensive_assert!( + !::MultiBlockMigrator::ongoing(), + "on_poll should not be called during migrations" + ); + let weight = >::block_weight(); + let max_weight = >::get().max_block; + let remaining = max_weight.saturating_sub(weight.total()); + + if remaining.all_gt(Weight::zero()) { + let mut meter = WeightMeter::with_limit(remaining); + >>::on_poll( + block_number, + &mut meter, + ); + >::register_extra_weight_unchecked( + meter.consumed(), + DispatchClass::Mandatory, + ); + } + } + + /// Run the `on_finalize` hook of all pallet. + fn on_finalize_hook(block_number: NumberFor) { >>::on_finalize(block_number); } @@ -627,8 +776,18 @@ where let encoded_len = encoded.len(); sp_tracing::enter_span!(sp_tracing::info_span!("apply_extrinsic", ext=?sp_core::hexdisplay::HexDisplay::from(&encoded))); + + // We use the dedicated `is_inherent` check here, since just relying on `Mandatory` dispatch + // class does not capture optional inherents. + let is_inherent = System::is_inherent(&uxt); + // Verify that the signature is good. let xt = uxt.check(&Default::default())?; + let dispatch_info = xt.get_dispatch_info(); + + if !is_inherent && !>::inherents_applied() { + Self::inherents_applied(); + } // We don't need to make sure to `note_extrinsic` only after we know it's going to be // executed to prevent it from leaking in storage since at this point, it will either @@ -637,8 +796,6 @@ where // AUDIT: Under no circumstances may this function panic from here onwards. - // Decode parameters and dispatch - let dispatch_info = xt.get_dispatch_info(); let r = Applyable::apply::(xt, &dispatch_info, encoded_len)?; // Mandatory(inherents) are not allowed to fail. @@ -745,957 +902,3 @@ where ) } } - -#[cfg(test)] -mod tests { - use super::*; - - use sp_core::H256; - use sp_runtime::{ - generic::{DigestItem, Era}, - testing::{Block, Digest, Header}, - traits::{BlakeTwo256, Block as BlockT, Header as HeaderT, IdentityLookup}, - transaction_validity::{ - InvalidTransaction, TransactionValidityError, UnknownTransaction, ValidTransaction, - }, - BuildStorage, DispatchError, - }; - - use frame_support::{ - assert_err, derive_impl, parameter_types, - traits::{fungible, ConstU32, ConstU64, ConstU8, Currency}, - weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, WeightToFee}, - }; - use frame_system::{ChainContext, LastRuntimeUpgrade, LastRuntimeUpgradeInfo}; - use pallet_balances::Call as BalancesCall; - use pallet_transaction_payment::CurrencyAdapter; - - const TEST_KEY: &[u8] = b":test:key:"; - - #[frame_support::pallet(dev_mode)] - mod custom { - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::hooks] - impl Hooks> for Pallet { - // module hooks. - // one with block number arg and one without - fn on_initialize(n: BlockNumberFor) -> Weight { - println!("on_initialize({})", n); - Weight::from_parts(175, 0) - } - - fn on_idle(n: BlockNumberFor, remaining_weight: Weight) -> Weight { - println!("on_idle{}, {})", n, remaining_weight); - Weight::from_parts(175, 0) - } - - fn on_finalize(n: BlockNumberFor) { - println!("on_finalize({})", n); - } - - fn on_runtime_upgrade() -> Weight { - sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); - Weight::from_parts(200, 0) - } - - fn offchain_worker(n: BlockNumberFor) { - assert_eq!(BlockNumberFor::::from(1u32), n); - } - } - - #[pallet::call] - impl Pallet { - pub fn some_function(origin: OriginFor) -> DispatchResult { - // NOTE: does not make any different. - frame_system::ensure_signed(origin)?; - Ok(()) - } - - #[pallet::weight((200, DispatchClass::Operational))] - pub fn some_root_operation(origin: OriginFor) -> DispatchResult { - frame_system::ensure_root(origin)?; - Ok(()) - } - - pub fn some_unsigned_message(origin: OriginFor) -> DispatchResult { - frame_system::ensure_none(origin)?; - Ok(()) - } - - pub fn allowed_unsigned(origin: OriginFor) -> DispatchResult { - frame_system::ensure_root(origin)?; - Ok(()) - } - - pub fn unallowed_unsigned(origin: OriginFor) -> DispatchResult { - frame_system::ensure_root(origin)?; - Ok(()) - } - - #[pallet::weight((0, DispatchClass::Mandatory))] - pub fn inherent_call(origin: OriginFor) -> DispatchResult { - frame_system::ensure_none(origin)?; - Ok(()) - } - - pub fn calculate_storage_root(_origin: OriginFor) -> DispatchResult { - let root = sp_io::storage::root(sp_runtime::StateVersion::V1); - sp_io::storage::set("storage_root".as_bytes(), &root); - Ok(()) - } - } - - #[pallet::inherent] - impl ProvideInherent for Pallet { - type Call = Call; - - type Error = sp_inherents::MakeFatalError<()>; - - const INHERENT_IDENTIFIER: [u8; 8] = *b"test1234"; - - fn create_inherent(_data: &InherentData) -> Option { - None - } - - fn is_inherent(call: &Self::Call) -> bool { - *call == Call::::inherent_call {} - } - } - - #[pallet::validate_unsigned] - impl ValidateUnsigned for Pallet { - type Call = Call; - - // Inherent call is accepted for being dispatched - fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { - match call { - Call::allowed_unsigned { .. } => Ok(()), - Call::inherent_call { .. } => Ok(()), - _ => Err(UnknownTransaction::NoUnsignedValidator.into()), - } - } - - // Inherent call is not validated as unsigned - fn validate_unsigned( - _source: TransactionSource, - call: &Self::Call, - ) -> TransactionValidity { - match call { - Call::allowed_unsigned { .. } => Ok(Default::default()), - _ => UnknownTransaction::NoUnsignedValidator.into(), - } - } - } - } - - frame_support::construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - TransactionPayment: pallet_transaction_payment, - Custom: custom, - } - ); - - parameter_types! { - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::builder() - .base_block(Weight::from_parts(10, 0)) - .for_class(DispatchClass::all(), |weights| weights.base_extrinsic = Weight::from_parts(5, 0)) - .for_class(DispatchClass::non_mandatory(), |weights| weights.max_total = Weight::from_parts(1024, u64::MAX).into()) - .build_or_panic(); - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { - read: 10, - write: 100, - }; - } - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = sp_core::H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Block = TestBlock; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = RuntimeVersion; - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; - } - - type Balance = u64; - impl pallet_balances::Config for Runtime { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<1>; - type RuntimeHoldReason = (); - type RuntimeFreezeReason = (); - type MaxHolds = ConstU32<1>; - } - - parameter_types! { - pub const TransactionByteFee: Balance = 0; - } - impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = ConstantMultiplier; - type FeeMultiplierUpdate = (); - } - impl custom::Config for Runtime {} - - pub struct RuntimeVersion; - impl frame_support::traits::Get for RuntimeVersion { - fn get() -> sp_version::RuntimeVersion { - RuntimeVersionTestValues::get().clone() - } - } - - parameter_types! { - pub static RuntimeVersionTestValues: sp_version::RuntimeVersion = - Default::default(); - } - - type SignedExtra = ( - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, - pallet_transaction_payment::ChargeTransactionPayment, - ); - type TestXt = sp_runtime::testing::TestXt; - type TestBlock = Block; - - // Will contain `true` when the custom runtime logic was called. - const CUSTOM_ON_RUNTIME_KEY: &[u8] = b":custom:on_runtime"; - - struct CustomOnRuntimeUpgrade; - impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { - fn on_runtime_upgrade() -> Weight { - sp_io::storage::set(TEST_KEY, "custom_upgrade".as_bytes()); - sp_io::storage::set(CUSTOM_ON_RUNTIME_KEY, &true.encode()); - System::deposit_event(frame_system::Event::CodeUpdated); - - assert_eq!(0, System::last_runtime_upgrade_spec_version()); - - Weight::from_parts(100, 0) - } - } - - type Executive = super::Executive< - Runtime, - Block, - ChainContext, - Runtime, - AllPalletsWithSystem, - CustomOnRuntimeUpgrade, - >; - - fn extra(nonce: u64, fee: Balance) -> SignedExtra { - ( - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(nonce), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(fee), - ) - } - - fn sign_extra(who: u64, nonce: u64, fee: Balance) -> Option<(u64, SignedExtra)> { - Some((who, extra(nonce, fee))) - } - - fn call_transfer(dest: u64, value: u64) -> RuntimeCall { - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) - } - - #[test] - fn balance_transfer_dispatch_works() { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(1, 211)] } - .assimilate_storage(&mut t) - .unwrap(); - let xt = TestXt::new(call_transfer(2, 69), sign_extra(1, 0, 0)); - let weight = xt.get_dispatch_info().weight + - ::BlockWeights::get() - .get(DispatchClass::Normal) - .base_extrinsic; - let fee: Balance = - ::WeightToFee::weight_to_fee(&weight); - let mut t = sp_io::TestExternalities::new(t); - t.execute_with(|| { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - let r = Executive::apply_extrinsic(xt); - assert!(r.is_ok()); - assert_eq!(>::total_balance(&1), 142 - fee); - assert_eq!(>::total_balance(&2), 69); - }); - } - - fn new_test_ext(balance_factor: Balance) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } - .assimilate_storage(&mut t) - .unwrap(); - t.into() - } - - fn new_test_ext_v0(balance_factor: Balance) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } - .assimilate_storage(&mut t) - .unwrap(); - (t, sp_runtime::StateVersion::V0).into() - } - - #[test] - fn block_import_works() { - block_import_works_inner( - new_test_ext_v0(1), - array_bytes::hex_n_into_unchecked( - "65e953676859e7a33245908af7ad3637d6861eb90416d433d485e95e2dd174a1", - ), - ); - block_import_works_inner( - new_test_ext(1), - array_bytes::hex_n_into_unchecked( - "5a19b3d6fdb7241836349fdcbe2d9df4d4f945b949d979e31ad50bff1cbcd1c2", - ), - ); - } - fn block_import_works_inner(mut ext: sp_io::TestExternalities, state_root: H256) { - ext.execute_with(|| { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root, - extrinsics_root: array_bytes::hex_n_into_unchecked( - "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", - ), - digest: Digest { logs: vec![] }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - #[should_panic] - fn block_import_of_bad_state_root_fails() { - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: [0u8; 32].into(), - extrinsics_root: array_bytes::hex_n_into_unchecked( - "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", - ), - digest: Digest { logs: vec![] }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - #[should_panic] - fn block_import_of_bad_extrinsic_root_fails() { - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block { - header: Header { - parent_hash: [69u8; 32].into(), - number: 1, - state_root: array_bytes::hex_n_into_unchecked( - "75e7d8f360d375bbe91bcf8019c01ab6362448b4a89e3b329717eb9d910340e5", - ), - extrinsics_root: [0u8; 32].into(), - digest: Digest { logs: vec![] }, - }, - extrinsics: vec![], - }); - }); - } - - #[test] - fn bad_extrinsic_not_inserted() { - let mut t = new_test_ext(1); - // bad nonce check! - let xt = TestXt::new(call_transfer(33, 69), sign_extra(1, 30, 0)); - t.execute_with(|| { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - assert_err!( - Executive::apply_extrinsic(xt), - TransactionValidityError::Invalid(InvalidTransaction::Future) - ); - assert_eq!(>::extrinsic_index(), Some(0)); - }); - } - - #[test] - fn block_weight_limit_enforced() { - let mut t = new_test_ext(10000); - // given: TestXt uses the encoded len as fixed Len: - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - let encoded = xt.encode(); - let encoded_len = encoded.len() as u64; - // on_initialize weight + base block execution weight - let block_weights = ::BlockWeights::get(); - let base_block_weight = Weight::from_parts(175, 0) + block_weights.base_block; - let limit = block_weights.get(DispatchClass::Normal).max_total.unwrap() - base_block_weight; - let num_to_exhaust_block = limit.ref_time() / (encoded_len + 5); - t.execute_with(|| { - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - // Base block execution weight + `on_initialize` weight from the custom module. - assert_eq!(>::block_weight().total(), base_block_weight); - - for nonce in 0..=num_to_exhaust_block { - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { - dest: 33, - value: 0, - }), - sign_extra(1, nonce.into(), 0), - ); - let res = Executive::apply_extrinsic(xt); - if nonce != num_to_exhaust_block { - assert!(res.is_ok()); - assert_eq!( - >::block_weight().total(), - //--------------------- on_initialize + block_execution + extrinsic_base weight - Weight::from_parts((encoded_len + 5) * (nonce + 1), 0) + base_block_weight, - ); - assert_eq!( - >::extrinsic_index(), - Some(nonce as u32 + 1) - ); - } else { - assert_eq!(res, Err(InvalidTransaction::ExhaustsResources.into())); - } - } - }); - } - - #[test] - fn block_weight_and_size_is_stored_per_tx() { - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - let x1 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 1, 0), - ); - let x2 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 2, 0), - ); - let len = xt.clone().encode().len() as u32; - let mut t = new_test_ext(1); - t.execute_with(|| { - // Block execution weight + on_initialize weight from custom module - let base_block_weight = Weight::from_parts(175, 0) + - ::BlockWeights::get().base_block; - - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - assert_eq!(>::block_weight().total(), base_block_weight); - assert_eq!(>::all_extrinsics_len(), 0); - - assert!(Executive::apply_extrinsic(xt.clone()).unwrap().is_ok()); - assert!(Executive::apply_extrinsic(x1.clone()).unwrap().is_ok()); - assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok()); - - // default weight for `TestXt` == encoded length. - let extrinsic_weight = Weight::from_parts(len as u64, 0) + - ::BlockWeights::get() - .get(DispatchClass::Normal) - .base_extrinsic; - assert_eq!( - >::block_weight().total(), - base_block_weight + 3u64 * extrinsic_weight, - ); - assert_eq!(>::all_extrinsics_len(), 3 * len); - - let _ = >::finalize(); - // All extrinsics length cleaned on `System::finalize` - assert_eq!(>::all_extrinsics_len(), 0); - - // New Block - Executive::initialize_block(&Header::new( - 2, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - // Block weight cleaned up on `System::initialize` - assert_eq!(>::block_weight().total(), base_block_weight); - }); - } - - #[test] - fn validate_unsigned() { - let valid = TestXt::new(RuntimeCall::Custom(custom::Call::allowed_unsigned {}), None); - let invalid = TestXt::new(RuntimeCall::Custom(custom::Call::unallowed_unsigned {}), None); - let mut t = new_test_ext(1); - - t.execute_with(|| { - assert_eq!( - Executive::validate_transaction( - TransactionSource::InBlock, - valid.clone(), - Default::default(), - ), - Ok(ValidTransaction::default()), - ); - assert_eq!( - Executive::validate_transaction( - TransactionSource::InBlock, - invalid.clone(), - Default::default(), - ), - Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)), - ); - assert_eq!(Executive::apply_extrinsic(valid), Ok(Err(DispatchError::BadOrigin))); - assert_eq!( - Executive::apply_extrinsic(invalid), - Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)) - ); - }); - } - - #[test] - fn can_not_pay_for_tx_fee_on_full_lock() { - let mut t = new_test_ext(1); - t.execute_with(|| { - as fungible::MutateFreeze>::set_freeze( - &(), - &1, - 110, - ) - .unwrap(); - let xt = TestXt::new( - RuntimeCall::System(frame_system::Call::remark { remark: vec![1u8] }), - sign_extra(1, 0, 0), - ); - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - assert_eq!(Executive::apply_extrinsic(xt), Err(InvalidTransaction::Payment.into()),); - assert_eq!(>::total_balance(&1), 111); - }); - } - - #[test] - fn block_hooks_weight_is_stored() { - new_test_ext(1).execute_with(|| { - Executive::initialize_block(&Header::new_from_number(1)); - Executive::finalize_block(); - // NOTE: might need updates over time if new weights are introduced. - // For now it only accounts for the base block execution weight and - // the `on_initialize` weight defined in the custom test module. - assert_eq!( - >::block_weight().total(), - Weight::from_parts(175 + 175 + 10, 0) - ); - }) - } - - #[test] - fn runtime_upgraded_should_work() { - new_test_ext(1).execute_with(|| { - RuntimeVersionTestValues::mutate(|v| *v = Default::default()); - // It should be added at genesis - assert!(LastRuntimeUpgrade::::exists()); - assert!(!Executive::runtime_upgraded()); - - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - assert!(Executive::runtime_upgraded()); - - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { - spec_version: 1, - spec_name: "test".into(), - ..Default::default() - } - }); - assert!(Executive::runtime_upgraded()); - - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { - spec_version: 0, - impl_version: 2, - ..Default::default() - } - }); - assert!(!Executive::runtime_upgraded()); - - LastRuntimeUpgrade::::take(); - assert!(Executive::runtime_upgraded()); - }) - } - - #[test] - fn last_runtime_upgrade_was_upgraded_works() { - let test_data = vec![ - (0, "", 1, "", true), - (1, "", 1, "", false), - (1, "", 1, "test", true), - (1, "", 0, "", false), - (1, "", 0, "test", true), - ]; - - for (spec_version, spec_name, c_spec_version, c_spec_name, result) in test_data { - let current = sp_version::RuntimeVersion { - spec_version: c_spec_version, - spec_name: c_spec_name.into(), - ..Default::default() - }; - - let last = LastRuntimeUpgradeInfo { - spec_version: spec_version.into(), - spec_name: spec_name.into(), - }; - - assert_eq!(result, last.was_upgraded(¤t)); - } - } - - #[test] - fn custom_runtime_upgrade_is_called_before_modules() { - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); - assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); - assert_eq!( - Some(RuntimeVersionTestValues::get().into()), - LastRuntimeUpgrade::::get(), - ) - }); - } - - #[test] - fn event_from_runtime_upgrade_is_included() { - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - // set block number to non zero so events are not excluded - System::set_block_number(1); - - Executive::initialize_block(&Header::new( - 2, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - System::assert_last_event(frame_system::Event::::CodeUpdated.into()); - }); - } - - /// Regression test that ensures that the custom on runtime upgrade is called when executive is - /// used through the `ExecuteBlock` trait. - #[test] - fn custom_runtime_upgrade_is_called_when_using_execute_block_trait() { - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - - let header = new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - // Reset to get the correct new genesis below. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 0, ..Default::default() } - }); - - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called. - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - >>::execute_block(Block::new(header, vec![xt])); - - assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); - assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); - }); - } - - #[test] - fn all_weights_are_recorded_correctly() { - // Reset to get the correct new genesis below. - RuntimeVersionTestValues::take(); - - new_test_ext(1).execute_with(|| { - // Make sure `on_runtime_upgrade` is called for maximum complexity - RuntimeVersionTestValues::mutate(|v| { - *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } - }); - - let block_number = 1; - - Executive::initialize_block(&Header::new( - block_number, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - // Reset the last runtime upgrade info, to make the second call to `on_runtime_upgrade` - // succeed. - LastRuntimeUpgrade::::take(); - - // All weights that show up in the `initialize_block_impl` - let custom_runtime_upgrade_weight = CustomOnRuntimeUpgrade::on_runtime_upgrade(); - let runtime_upgrade_weight = - ::on_runtime_upgrade(); - let on_initialize_weight = - >::on_initialize(block_number); - let base_block_weight = - ::BlockWeights::get().base_block; - - // Weights are recorded correctly - assert_eq!( - frame_system::Pallet::::block_weight().total(), - custom_runtime_upgrade_weight + - runtime_upgrade_weight + - on_initialize_weight + base_block_weight, - ); - }); - } - - #[test] - fn offchain_worker_works_as_expected() { - new_test_ext(1).execute_with(|| { - let parent_hash = sp_core::H256::from([69u8; 32]); - let mut digest = Digest::default(); - digest.push(DigestItem::Seal([1, 2, 3, 4], vec![5, 6, 7, 8])); - - let header = - Header::new(1, H256::default(), H256::default(), parent_hash, digest.clone()); - - Executive::offchain_worker(&header); - - assert_eq!(digest, System::digest()); - assert_eq!(parent_hash, System::block_hash(0)); - assert_eq!(header.hash(), System::block_hash(1)); - }); - } - - #[test] - fn calculating_storage_root_twice_works() { - let call = RuntimeCall::Custom(custom::Call::calculate_storage_root {}); - let xt = TestXt::new(call, sign_extra(1, 0, 0)); - - let header = new_test_ext(1).execute_with(|| { - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new(header, vec![xt])); - }); - } - - #[test] - #[should_panic(expected = "Invalid inherent position for extrinsic at index 1")] - fn invalid_inherent_position_fail() { - let xt1 = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - let xt2 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None); - - let header = new_test_ext(1).execute_with(|| { - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); - Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new(header, vec![xt1, xt2])); - }); - } - - #[test] - fn valid_inherents_position_works() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); - - let header = new_test_ext(1).execute_with(|| { - // Let's build some fake block. - Executive::initialize_block(&Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - )); - - Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); - Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); - - Executive::finalize_block() - }); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new(header, vec![xt1, xt2])); - }); - } - - #[test] - #[should_panic(expected = "A call was labelled as mandatory, but resulted in an Error.")] - fn invalid_inherents_fail_block_execution() { - let xt1 = - TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), sign_extra(1, 0, 0)); - - new_test_ext(1).execute_with(|| { - Executive::execute_block(Block::new( - Header::new( - 1, - H256::default(), - H256::default(), - [69u8; 32].into(), - Digest::default(), - ), - vec![xt1], - )); - }); - } - - // Inherents are created by the runtime and don't need to be validated. - #[test] - fn inherents_fail_validate_block() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None); - - new_test_ext(1).execute_with(|| { - assert_eq!( - Executive::validate_transaction(TransactionSource::External, xt1, H256::random()) - .unwrap_err(), - InvalidTransaction::MandatoryValidation.into() - ); - }) - } -} diff --git a/substrate/frame/executive/src/tests.rs b/substrate/frame/executive/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3f70a9fc3c2a081b52a08f3e9a8a617ab75c7a9 --- /dev/null +++ b/substrate/frame/executive/src/tests.rs @@ -0,0 +1,1476 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test the `frame-executive` crate. + +use super::*; + +use sp_core::H256; +use sp_runtime::{ + generic::{DigestItem, Era}, + testing::{Block, Digest, Header}, + traits::{Block as BlockT, Header as HeaderT}, + transaction_validity::{ + InvalidTransaction, TransactionValidityError, UnknownTransaction, ValidTransaction, + }, + BuildStorage, DispatchError, +}; + +use frame_support::{ + assert_err, assert_ok, derive_impl, + migrations::MultiStepMigrator, + pallet_prelude::*, + parameter_types, + traits::{fungible, ConstU8, Currency, IsInherent}, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, WeightMeter, WeightToFee}, +}; +use frame_system::{pallet_prelude::*, ChainContext, LastRuntimeUpgrade, LastRuntimeUpgradeInfo}; +use pallet_balances::Call as BalancesCall; +use pallet_transaction_payment::CurrencyAdapter; + +const TEST_KEY: &[u8] = b":test:key:"; + +#[frame_support::pallet(dev_mode)] +mod custom { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::hooks] + impl Hooks> for Pallet { + // module hooks. + // one with block number arg and one without + fn on_initialize(_: BlockNumberFor) -> Weight { + Weight::from_parts(175, 0) + } + + fn on_idle(_: BlockNumberFor, _: Weight) -> Weight { + Weight::from_parts(175, 0) + } + + fn on_poll(_: BlockNumberFor, _: &mut WeightMeter) {} + + fn on_finalize(_: BlockNumberFor) {} + + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); + Weight::from_parts(200, 0) + } + + fn offchain_worker(n: BlockNumberFor) { + assert_eq!(BlockNumberFor::::from(1u32), n); + } + } + + #[pallet::call] + impl Pallet { + pub fn some_function(origin: OriginFor) -> DispatchResult { + // NOTE: does not make any difference. + frame_system::ensure_signed(origin)?; + Ok(()) + } + + #[pallet::weight((200, DispatchClass::Operational))] + pub fn some_root_operation(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + pub fn some_unsigned_message(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + Ok(()) + } + + pub fn allowed_unsigned(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + pub fn unallowed_unsigned(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn inherent(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + Ok(()) + } + + pub fn calculate_storage_root(_origin: OriginFor) -> DispatchResult { + let root = sp_io::storage::root(sp_runtime::StateVersion::V1); + sp_io::storage::set("storage_root".as_bytes(), &root); + Ok(()) + } + } + + #[pallet::inherent] + impl ProvideInherent for Pallet { + type Call = Call; + + type Error = sp_inherents::MakeFatalError<()>; + + const INHERENT_IDENTIFIER: [u8; 8] = *b"test1234"; + + fn create_inherent(_data: &InherentData) -> Option { + None + } + + fn is_inherent(call: &Self::Call) -> bool { + *call == Call::::inherent {} + } + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + + // Inherent call is accepted for being dispatched + fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { + match call { + Call::allowed_unsigned { .. } => Ok(()), + Call::inherent { .. } => Ok(()), + _ => Err(UnknownTransaction::NoUnsignedValidator.into()), + } + } + + // Inherent call is not validated as unsigned + fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { + match call { + Call::allowed_unsigned { .. } => Ok(Default::default()), + _ => UnknownTransaction::NoUnsignedValidator.into(), + } + } + } +} + +#[frame_support::pallet(dev_mode)] +mod custom2 { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::hooks] + impl Hooks> for Pallet { + // module hooks. + // one with block number arg and one without + fn on_initialize(_: BlockNumberFor) -> Weight { + assert!( + !MockedSystemCallbacks::pre_inherent_called(), + "Pre inherent hook goes after on_initialize" + ); + + Weight::from_parts(0, 0) + } + + fn on_idle(_: BlockNumberFor, _: Weight) -> Weight { + assert!( + MockedSystemCallbacks::post_transactions_called(), + "Post transactions hook goes before after on_idle" + ); + Weight::from_parts(0, 0) + } + + fn on_finalize(_: BlockNumberFor) { + assert!( + MockedSystemCallbacks::post_transactions_called(), + "Post transactions hook goes before after on_finalize" + ); + } + + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); + Weight::from_parts(0, 0) + } + } + + #[pallet::call] + impl Pallet { + pub fn allowed_unsigned(origin: OriginFor) -> DispatchResult { + frame_system::ensure_root(origin)?; + Ok(()) + } + + pub fn some_call(_: OriginFor) -> DispatchResult { + assert!(MockedSystemCallbacks::post_inherent_called()); + assert!(!MockedSystemCallbacks::post_transactions_called()); + assert!(System::inherents_applied()); + + Ok(()) + } + + #[pallet::weight({0})] + pub fn optional_inherent(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + + assert!(MockedSystemCallbacks::pre_inherent_called()); + assert!(!MockedSystemCallbacks::post_inherent_called(), "Should not already be called"); + assert!(!System::inherents_applied()); + + Ok(()) + } + + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn inherent(origin: OriginFor) -> DispatchResult { + frame_system::ensure_none(origin)?; + + assert!(MockedSystemCallbacks::pre_inherent_called()); + assert!(!MockedSystemCallbacks::post_inherent_called(), "Should not already be called"); + assert!(!System::inherents_applied()); + + Ok(()) + } + } + + #[pallet::inherent] + impl ProvideInherent for Pallet { + type Call = Call; + + type Error = sp_inherents::MakeFatalError<()>; + + const INHERENT_IDENTIFIER: [u8; 8] = *b"test1235"; + + fn create_inherent(_data: &InherentData) -> Option { + None + } + + fn is_inherent(call: &Self::Call) -> bool { + matches!(call, Call::::inherent {} | Call::::optional_inherent {}) + } + } + + #[pallet::validate_unsigned] + impl ValidateUnsigned for Pallet { + type Call = Call; + + // Inherent call is accepted for being dispatched + fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { + match call { + Call::allowed_unsigned { .. } | + Call::optional_inherent { .. } | + Call::inherent { .. } => Ok(()), + _ => Err(UnknownTransaction::NoUnsignedValidator.into()), + } + } + + // Inherent call is not validated as unsigned + fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { + match call { + Call::allowed_unsigned { .. } => Ok(Default::default()), + _ => UnknownTransaction::NoUnsignedValidator.into(), + } + } + } +} + +frame_support::construct_runtime!( + pub struct Runtime + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + Custom: custom::{Pallet, Call, ValidateUnsigned, Inherent}, + Custom2: custom2::{Pallet, Call, ValidateUnsigned, Inherent}, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::builder() + .base_block(Weight::from_parts(10, 0)) + .for_class(DispatchClass::all(), |weights| weights.base_extrinsic = Weight::from_parts(5, 0)) + .for_class(DispatchClass::non_mandatory(), |weights| weights.max_total = Weight::from_parts(1024, u64::MAX).into()) + .build_or_panic(); + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 10, + write: 100, + }; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type BlockWeights = BlockWeights; + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type RuntimeCall = RuntimeCall; + type Block = TestBlock; + type RuntimeEvent = RuntimeEvent; + type Version = RuntimeVersion; + type AccountData = pallet_balances::AccountData; + type PreInherents = MockedSystemCallbacks; + type PostInherents = MockedSystemCallbacks; + type PostTransactions = MockedSystemCallbacks; + type MultiBlockMigrator = MockedModeGetter; +} + +type Balance = u64; + +pub struct BalancesWeights; +impl pallet_balances::WeightInfo for BalancesWeights { + fn transfer_allow_death() -> Weight { + Weight::from_parts(25, 0) + } + fn transfer_keep_alive() -> Weight { + Weight::zero() + } + fn force_set_balance_creating() -> Weight { + Weight::zero() + } + fn force_set_balance_killing() -> Weight { + Weight::zero() + } + fn force_transfer() -> Weight { + Weight::zero() + } + fn transfer_all() -> Weight { + Weight::zero() + } + fn force_unreserve() -> Weight { + Weight::zero() + } + fn upgrade_accounts(_u: u32) -> Weight { + Weight::zero() + } + fn force_adjust_total_issuance() -> Weight { + Weight::zero() + } +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type AccountStore = System; + type WeightInfo = BalancesWeights; +} + +parameter_types! { + pub const TransactionByteFee: Balance = 0; +} +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = (); + type WeightInfo = (); +} + +impl custom::Config for Runtime {} +impl custom2::Config for Runtime {} + +pub struct RuntimeVersion; +impl frame_support::traits::Get for RuntimeVersion { + fn get() -> sp_version::RuntimeVersion { + RuntimeVersionTestValues::get().clone() + } +} + +#[derive(Clone, Debug, Encode, codec::Decode, PartialEq, Eq, scale_info::TypeInfo)] +pub struct AccountU64(u64); +impl sp_runtime::traits::IdentifyAccount for AccountU64 { + type AccountId = u64; + fn into_account(self) -> u64 { + self.0 + } +} + +impl sp_runtime::traits::Verify for AccountU64 { + type Signer = AccountU64; + fn verify>( + &self, + _msg: L, + _signer: &::AccountId, + ) -> bool { + true + } +} + +impl From for AccountU64 { + fn from(value: u64) -> Self { + Self(value) + } +} + +parameter_types! { + pub static RuntimeVersionTestValues: sp_version::RuntimeVersion = + Default::default(); +} + +type TxExtension = ( + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +type UncheckedXt = + sp_runtime::generic::UncheckedExtrinsic; +type TestBlock = Block; + +// Will contain `true` when the custom runtime logic was called. +const CUSTOM_ON_RUNTIME_KEY: &[u8] = b":custom:on_runtime"; + +struct CustomOnRuntimeUpgrade; +impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(TEST_KEY, "custom_upgrade".as_bytes()); + sp_io::storage::set(CUSTOM_ON_RUNTIME_KEY, &true.encode()); + System::deposit_event(frame_system::Event::CodeUpdated); + + assert_eq!(0, System::last_runtime_upgrade_spec_version()); + + Weight::from_parts(100, 0) + } +} + +type Executive = super::Executive< + Runtime, + Block, + ChainContext, + Runtime, + AllPalletsWithSystem, + CustomOnRuntimeUpgrade, +>; + +parameter_types! { + pub static SystemCallbacksCalled: u32 = 0; +} + +pub struct MockedSystemCallbacks; +impl PreInherents for MockedSystemCallbacks { + fn pre_inherents() { + assert_eq!(SystemCallbacksCalled::get(), 0); + SystemCallbacksCalled::set(1); + // Change the storage to modify the root hash: + frame_support::storage::unhashed::put(b":pre_inherent", b"0"); + } +} + +impl PostInherents for MockedSystemCallbacks { + fn post_inherents() { + assert_eq!(SystemCallbacksCalled::get(), 1); + SystemCallbacksCalled::set(2); + // Change the storage to modify the root hash: + frame_support::storage::unhashed::put(b":post_inherent", b"0"); + } +} + +impl PostTransactions for MockedSystemCallbacks { + fn post_transactions() { + assert_eq!(SystemCallbacksCalled::get(), 2); + SystemCallbacksCalled::set(3); + // Change the storage to modify the root hash: + frame_support::storage::unhashed::put(b":post_transaction", b"0"); + } +} + +impl MockedSystemCallbacks { + fn pre_inherent_called() -> bool { + SystemCallbacksCalled::get() >= 1 + } + + fn post_inherent_called() -> bool { + SystemCallbacksCalled::get() >= 2 + } + + fn post_transactions_called() -> bool { + SystemCallbacksCalled::get() >= 3 + } + + fn reset() { + SystemCallbacksCalled::set(0); + frame_support::storage::unhashed::kill(b":pre_inherent"); + frame_support::storage::unhashed::kill(b":post_inherent"); + frame_support::storage::unhashed::kill(b":post_transaction"); + } +} + +parameter_types! { + pub static MbmActive: bool = false; +} + +pub struct MockedModeGetter; +impl MultiStepMigrator for MockedModeGetter { + fn ongoing() -> bool { + MbmActive::get() + } + + fn step() -> Weight { + Weight::zero() + } +} + +fn tx_ext(nonce: u64, fee: Balance) -> TxExtension { + ( + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(nonce), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(fee), + ) + .into() +} + +fn call_transfer(dest: u64, value: u64) -> RuntimeCall { + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) +} + +#[test] +fn balance_transfer_dispatch_works() { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(1, 211)] } + .assimilate_storage(&mut t) + .unwrap(); + let xt = UncheckedXt::new_signed(call_transfer(2, 69), 1, 1.into(), tx_ext(0, 0)); + let weight = xt.get_dispatch_info().weight + + ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let fee: Balance = + ::WeightToFee::weight_to_fee(&weight); + let mut t = sp_io::TestExternalities::new(t); + t.execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + let r = Executive::apply_extrinsic(xt); + assert!(r.is_ok()); + assert_eq!(>::total_balance(&1), 142 - fee); + assert_eq!(>::total_balance(&2), 69); + }); +} + +fn new_test_ext(balance_factor: Balance) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext: sp_io::TestExternalities = t.into(); + ext.execute_with(|| { + SystemCallbacksCalled::set(0); + }); + ext +} + +fn new_test_ext_v0(balance_factor: Balance) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(1, 111 * balance_factor)] } + .assimilate_storage(&mut t) + .unwrap(); + (t, sp_runtime::StateVersion::V0).into() +} + +#[test] +fn block_import_works() { + block_import_works_inner( + new_test_ext_v0(1), + array_bytes::hex_n_into_unchecked( + "4826d3bdf87dbbc883d2ab274cbe272f58ed94a904619b59953e48294d1142d2", + ), + ); + block_import_works_inner( + new_test_ext(1), + array_bytes::hex_n_into_unchecked( + "d6b465f5a50c9f8d5a6edc0f01d285a6b19030f097d3aaf1649b7be81649f118", + ), + ); +} +fn block_import_works_inner(mut ext: sp_io::TestExternalities, state_root: H256) { + ext.execute_with(|| { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root, + extrinsics_root: array_bytes::hex_n_into_unchecked( + "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", + ), + digest: Digest { logs: vec![] }, + }, + extrinsics: vec![], + }); + }); +} + +#[test] +#[should_panic] +fn block_import_of_bad_state_root_fails() { + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: [0u8; 32].into(), + extrinsics_root: array_bytes::hex_n_into_unchecked( + "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314", + ), + digest: Digest { logs: vec![] }, + }, + extrinsics: vec![], + }); + }); +} + +#[test] +#[should_panic] +fn block_import_of_bad_extrinsic_root_fails() { + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block { + header: Header { + parent_hash: [69u8; 32].into(), + number: 1, + state_root: array_bytes::hex_n_into_unchecked( + "75e7d8f360d375bbe91bcf8019c01ab6362448b4a89e3b329717eb9d910340e5", + ), + extrinsics_root: [0u8; 32].into(), + digest: Digest { logs: vec![] }, + }, + extrinsics: vec![], + }); + }); +} + +#[test] +fn bad_extrinsic_not_inserted() { + let mut t = new_test_ext(1); + // bad nonce check! + let xt = UncheckedXt::new_signed(call_transfer(33, 69), 1, 1.into(), tx_ext(30, 0)); + t.execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + assert_err!( + Executive::apply_extrinsic(xt), + TransactionValidityError::Invalid(InvalidTransaction::Future) + ); + assert_eq!(>::extrinsic_index(), Some(0)); + }); +} + +#[test] +fn block_weight_limit_enforced() { + let mut t = new_test_ext(10000); + let transfer_weight = + <::WeightInfo as pallet_balances::WeightInfo>::transfer_allow_death(); + // on_initialize weight + base block execution weight + let block_weights = ::BlockWeights::get(); + let base_block_weight = Weight::from_parts(175, 0) + block_weights.base_block; + let limit = block_weights.get(DispatchClass::Normal).max_total.unwrap() - base_block_weight; + let num_to_exhaust_block = limit.ref_time() / (transfer_weight.ref_time() + 5); + t.execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + // Base block execution weight + `on_initialize` weight from the custom module. + assert_eq!(>::block_weight().total(), base_block_weight); + + for nonce in 0..=num_to_exhaust_block { + let xt = UncheckedXt::new_signed( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + 1, + 1.into(), + tx_ext(nonce.into(), 0), + ); + let res = Executive::apply_extrinsic(xt); + if nonce != num_to_exhaust_block { + assert!(res.is_ok()); + assert_eq!( + >::block_weight().total(), + //--------------------- on_initialize + block_execution + extrinsic_base weight + Weight::from_parts((transfer_weight.ref_time() + 5) * (nonce + 1), 0) + + base_block_weight, + ); + assert_eq!( + >::extrinsic_index(), + Some(nonce as u32 + 1) + ); + } else { + assert_eq!(res, Err(InvalidTransaction::ExhaustsResources.into())); + } + } + }); +} + +#[test] +fn block_weight_and_size_is_stored_per_tx() { + let xt = UncheckedXt::new_signed( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + 1, + 1.into(), + tx_ext(0, 0), + ); + let x1 = UncheckedXt::new_signed( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + 1, + 1.into(), + tx_ext(1, 0), + ); + let x2 = UncheckedXt::new_signed( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + 1, + 1.into(), + tx_ext(2, 0), + ); + let len = xt.clone().encode().len() as u32; + let transfer_weight = <::WeightInfo as pallet_balances::WeightInfo>::transfer_allow_death(); + let mut t = new_test_ext(1); + t.execute_with(|| { + // Block execution weight + on_initialize weight from custom module + let base_block_weight = Weight::from_parts(175, 0) + + ::BlockWeights::get().base_block; + + Executive::initialize_block(&Header::new_from_number(1)); + + assert_eq!(>::block_weight().total(), base_block_weight); + assert_eq!(>::all_extrinsics_len(), 0); + + assert!(Executive::apply_extrinsic(xt.clone()).unwrap().is_ok()); + assert!(Executive::apply_extrinsic(x1.clone()).unwrap().is_ok()); + assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok()); + + let extrinsic_weight = transfer_weight + + ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + assert_eq!( + >::block_weight().total(), + base_block_weight + 3u64 * extrinsic_weight, + ); + assert_eq!(>::all_extrinsics_len(), 3 * len); + + let _ = >::finalize(); + // All extrinsics length cleaned on `System::finalize` + assert_eq!(>::all_extrinsics_len(), 0); + + // Reset to a new block. + SystemCallbacksCalled::take(); + Executive::initialize_block(&Header::new_from_number(2)); + + // Block weight cleaned up on `System::initialize` + assert_eq!(>::block_weight().total(), base_block_weight); + }); +} + +#[test] +fn validate_unsigned() { + let valid = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::allowed_unsigned {})); + let invalid = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::unallowed_unsigned {})); + let mut t = new_test_ext(1); + + t.execute_with(|| { + assert_eq!( + Executive::validate_transaction( + TransactionSource::InBlock, + valid.clone(), + Default::default(), + ), + Ok(ValidTransaction::default()), + ); + assert_eq!( + Executive::validate_transaction( + TransactionSource::InBlock, + invalid.clone(), + Default::default(), + ), + Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)), + ); + // Need to initialize the block before applying extrinsics for the `MockedSystemCallbacks` + // check. + Executive::initialize_block(&Header::new_from_number(1)); + assert_eq!(Executive::apply_extrinsic(valid), Ok(Err(DispatchError::BadOrigin))); + assert_eq!( + Executive::apply_extrinsic(invalid), + Err(TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)) + ); + }); +} + +#[test] +fn can_not_pay_for_tx_fee_on_full_lock() { + let mut t = new_test_ext(1); + t.execute_with(|| { + as fungible::MutateFreeze>::set_freeze(&(), &1, 110) + .unwrap(); + let xt = UncheckedXt::new_signed( + RuntimeCall::System(frame_system::Call::remark { remark: vec![1u8] }), + 1, + 1.into(), + tx_ext(0, 0), + ); + Executive::initialize_block(&Header::new_from_number(1)); + + assert_eq!(Executive::apply_extrinsic(xt), Err(InvalidTransaction::Payment.into()),); + assert_eq!(>::total_balance(&1), 111); + }); +} + +#[test] +fn block_hooks_weight_is_stored() { + new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + Executive::finalize_block(); + // NOTE: might need updates over time if new weights are introduced. + // For now it only accounts for the base block execution weight and + // the `on_initialize` weight defined in the custom test module. + assert_eq!( + >::block_weight().total(), + Weight::from_parts(175 + 175 + 10, 0) + ); + }) +} + +#[test] +fn runtime_upgraded_should_work() { + new_test_ext(1).execute_with(|| { + RuntimeVersionTestValues::mutate(|v| *v = Default::default()); + // It should be added at genesis + assert!(LastRuntimeUpgrade::::exists()); + assert!(!Executive::runtime_upgraded()); + + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + assert!(Executive::runtime_upgraded()); + + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { + spec_version: 1, + spec_name: "test".into(), + ..Default::default() + } + }); + assert!(Executive::runtime_upgraded()); + + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { + spec_version: 0, + impl_version: 2, + ..Default::default() + } + }); + assert!(!Executive::runtime_upgraded()); + + LastRuntimeUpgrade::::take(); + assert!(Executive::runtime_upgraded()); + }) +} + +#[test] +fn last_runtime_upgrade_was_upgraded_works() { + let test_data = vec![ + (0, "", 1, "", true), + (1, "", 1, "", false), + (1, "", 1, "test", true), + (1, "", 0, "", false), + (1, "", 0, "test", true), + ]; + + for (spec_version, spec_name, c_spec_version, c_spec_name, result) in test_data { + let current = sp_version::RuntimeVersion { + spec_version: c_spec_version, + spec_name: c_spec_name.into(), + ..Default::default() + }; + + let last = LastRuntimeUpgradeInfo { + spec_version: spec_version.into(), + spec_name: spec_name.into(), + }; + + assert_eq!(result, last.was_upgraded(¤t)); + } +} + +#[test] +fn custom_runtime_upgrade_is_called_before_modules() { + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + Executive::initialize_block(&Header::new_from_number(1)); + + assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); + assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); + assert_eq!( + Some(RuntimeVersionTestValues::get().into()), + LastRuntimeUpgrade::::get(), + ) + }); +} + +#[test] +fn event_from_runtime_upgrade_is_included() { + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + // set block number to non zero so events are not excluded + System::set_block_number(1); + + Executive::initialize_block(&Header::new_from_number(2)); + System::assert_last_event(frame_system::Event::::CodeUpdated.into()); + }); +} + +/// Regression test that ensures that the custom on runtime upgrade is called when executive is +/// used through the `ExecuteBlock` trait. +#[test] +fn custom_runtime_upgrade_is_called_when_using_execute_block_trait() { + let xt = UncheckedXt::new_signed( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + 1, + 1.into(), + tx_ext(0, 0), + ); + + let header = new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + // Reset to get the correct new genesis below. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 0, ..Default::default() } + }); + + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + >>::execute_block(Block::new( + header, + vec![xt], + )); + + assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); + assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); + }); +} + +#[test] +fn all_weights_are_recorded_correctly() { + // Reset to get the correct new genesis below. + RuntimeVersionTestValues::take(); + + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called for maximum complexity + RuntimeVersionTestValues::mutate(|v| { + *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } + }); + + let block_number = 1; + + Executive::initialize_block(&Header::new_from_number(block_number)); + + // Reset the last runtime upgrade info, to make the second call to `on_runtime_upgrade` + // succeed. + LastRuntimeUpgrade::::take(); + MockedSystemCallbacks::reset(); + + // All weights that show up in the `initialize_block_impl` + let custom_runtime_upgrade_weight = CustomOnRuntimeUpgrade::on_runtime_upgrade(); + let runtime_upgrade_weight = + ::on_runtime_upgrade(); + let on_initialize_weight = + >::on_initialize(block_number); + let base_block_weight = ::BlockWeights::get().base_block; + + // Weights are recorded correctly + assert_eq!( + frame_system::Pallet::::block_weight().total(), + custom_runtime_upgrade_weight + + runtime_upgrade_weight + + on_initialize_weight + + base_block_weight, + ); + }); +} + +#[test] +fn offchain_worker_works_as_expected() { + new_test_ext(1).execute_with(|| { + let parent_hash = sp_core::H256::from([69u8; 32]); + let mut digest = Digest::default(); + digest.push(DigestItem::Seal([1, 2, 3, 4], vec![5, 6, 7, 8])); + + let header = Header::new(1, H256::default(), H256::default(), parent_hash, digest.clone()); + + Executive::offchain_worker(&header); + + assert_eq!(digest, System::digest()); + assert_eq!(parent_hash, System::block_hash(0)); + assert_eq!(header.hash(), System::block_hash(1)); + }); +} + +#[test] +fn calculating_storage_root_twice_works() { + let call = RuntimeCall::Custom(custom::Call::calculate_storage_root {}); + let xt = UncheckedXt::new_signed(call, 1, 1.into(), tx_ext(0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new(header, vec![xt])); + }); +} + +#[test] +#[should_panic(expected = "Invalid inherent position for extrinsic at index 1")] +fn invalid_inherent_position_fail() { + let xt1 = UncheckedXt::new_signed( + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), + 1, + 1.into(), + tx_ext(0, 0), + ); + let xt2 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +#[test] +fn valid_inherents_position_works() { + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +#[test] +#[should_panic(expected = "A call was labelled as mandatory, but resulted in an Error.")] +fn invalid_inherents_fail_block_execution() { + let xt1 = UncheckedXt::new_signed( + RuntimeCall::Custom(custom::Call::inherent {}), + 1, + 1.into(), + tx_ext(0, 0), + ); + + new_test_ext(1).execute_with(|| { + Executive::execute_block(Block::new( + Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default()), + vec![xt1], + )); + }); +} + +// Inherents are created by the runtime and don't need to be validated. +#[test] +fn inherents_fail_validate_block() { + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + + new_test_ext(1).execute_with(|| { + assert_eq!( + Executive::validate_transaction(TransactionSource::External, xt1, H256::random()) + .unwrap_err(), + InvalidTransaction::MandatoryValidation.into() + ); + }) +} + +/// Inherents still work while `initialize_block` forbids transactions. +#[test] +fn inherents_ok_while_exts_forbidden_works() { + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + // This is not applied: + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + // Tell `initialize_block` to forbid extrinsics: + Executive::execute_block(Block::new(header, vec![xt1])); + }); +} + +/// Refuses to import blocks with transactions during `OnlyInherents` mode. +#[test] +#[should_panic = "Only inherents are allowed in this block"] +fn transactions_in_only_inherents_block_errors() { + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + MbmActive::set(true); + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +/// Same as above but no error. +#[test] +fn transactions_in_normal_block_works() { + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + // Tell `initialize_block` to forbid extrinsics: + Executive::execute_block(Block::new(header, vec![xt1, xt2])); + }); +} + +#[test] +#[cfg(feature = "try-runtime")] +fn try_execute_block_works() { + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); + + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + Executive::try_execute_block( + Block::new(header, vec![xt1, xt2]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + }); +} + +/// Same as `extrinsic_while_exts_forbidden_errors` but using the try-runtime function. +#[test] +#[cfg(feature = "try-runtime")] +#[should_panic = "Only inherents allowed"] +fn try_execute_tx_forbidden_errors() { + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt2.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + MbmActive::set(true); + Executive::try_execute_block( + Block::new(header, vec![xt1, xt2]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + }); +} + +/// Check that `ensure_inherents_are_first` reports the correct indices. +#[test] +fn ensure_inherents_are_first_works() { + let in1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let in2 = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); + + // Mocked empty header: + let header = new_test_ext(1).execute_with(|| { + Executive::initialize_block(&Header::new_from_number(1)); + Executive::finalize_block() + }); + + new_test_ext(1).execute_with(|| { + assert_ok!(Runtime::ensure_inherents_are_first(&Block::new(header.clone(), vec![]),), 0); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new(header.clone(), vec![xt2.clone()]),), + 0 + ); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new(header.clone(), vec![in1.clone()])), + 1 + ); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![in1.clone(), xt2.clone()] + ),), + 1 + ); + assert_ok!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![in2.clone(), in1.clone(), xt2.clone()] + ),), + 2 + ); + + assert_eq!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![xt2.clone(), in1.clone()] + ),), + Err(1) + ); + assert_eq!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![xt2.clone(), xt2.clone(), in1.clone()] + ),), + Err(2) + ); + assert_eq!( + Runtime::ensure_inherents_are_first(&Block::new( + header.clone(), + vec![xt2.clone(), xt2.clone(), xt2.clone(), in2.clone()] + ),), + Err(3) + ); + }); +} + +/// Check that block execution rejects blocks with transactions in them while MBMs are active and +/// also that all the system callbacks are called correctly. +#[test] +fn callbacks_in_block_execution_works() { + callbacks_in_block_execution_works_inner(false); + callbacks_in_block_execution_works_inner(true); +} + +fn callbacks_in_block_execution_works_inner(mbms_active: bool) { + MbmActive::set(mbms_active); + + for (n_in, n_tx) in (0..10usize).zip(0..10usize) { + let mut extrinsics = Vec::new(); + + let header = new_test_ext(10).execute_with(|| { + MockedSystemCallbacks::reset(); + Executive::initialize_block(&Header::new_from_number(1)); + assert_eq!(SystemCallbacksCalled::get(), 1); + + for i in 0..n_in { + let xt = if i % 2 == 0 { + UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})) + } else { + UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::optional_inherent {})) + }; + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + extrinsics.push(xt); + } + + for t in 0..n_tx { + let xt = UncheckedXt::new_signed( + RuntimeCall::Custom2(custom2::Call::some_call {}), + 1, + 1.into(), + tx_ext(t as u64, 0), + ); + // Extrinsics can be applied even when MBMs are active. Only the `execute_block` + // will reject it. + Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); + extrinsics.push(xt); + } + + Executive::finalize_block() + }); + assert_eq!(SystemCallbacksCalled::get(), 3); + + new_test_ext(10).execute_with(|| { + let header = std::panic::catch_unwind(|| { + Executive::execute_block(Block::new(header, extrinsics)); + }); + + match header { + Err(e) => { + let err = e.downcast::<&str>().unwrap(); + assert_eq!(*err, "Only inherents are allowed in this block"); + assert!( + MbmActive::get() && n_tx > 0, + "Transactions should be rejected when MBMs are active" + ); + }, + Ok(_) => { + assert_eq!(SystemCallbacksCalled::get(), 3); + assert!( + !MbmActive::get() || n_tx == 0, + "MBMs should be deactivated after finalization" + ); + }, + } + }); + } +} + +#[test] +fn post_inherent_called_after_all_inherents() { + let in1 = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::inherent {})); + let xt1 = UncheckedXt::new_signed( + RuntimeCall::Custom2(custom2::Call::some_call {}), + 1, + 1.into(), + tx_ext(0, 0), + ); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(in1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + #[cfg(feature = "try-runtime")] + new_test_ext(1).execute_with(|| { + Executive::try_execute_block( + Block::new(header.clone(), vec![in1.clone(), xt1.clone()]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); + + new_test_ext(1).execute_with(|| { + MockedSystemCallbacks::reset(); + Executive::execute_block(Block::new(header, vec![in1, xt1])); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); +} + +/// Regression test for AppSec finding #40. +#[test] +fn post_inherent_called_after_all_optional_inherents() { + let in1 = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::optional_inherent {})); + let xt1 = UncheckedXt::new_signed( + RuntimeCall::Custom2(custom2::Call::some_call {}), + 1, + 1.into(), + tx_ext(0, 0), + ); + + let header = new_test_ext(1).execute_with(|| { + // Let's build some fake block. + Executive::initialize_block(&Header::new_from_number(1)); + + Executive::apply_extrinsic(in1.clone()).unwrap().unwrap(); + Executive::apply_extrinsic(xt1.clone()).unwrap().unwrap(); + + Executive::finalize_block() + }); + + #[cfg(feature = "try-runtime")] + new_test_ext(1).execute_with(|| { + Executive::try_execute_block( + Block::new(header.clone(), vec![in1.clone(), xt1.clone()]), + true, + true, + frame_try_runtime::TryStateSelect::All, + ) + .unwrap(); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); + + new_test_ext(1).execute_with(|| { + MockedSystemCallbacks::reset(); + Executive::execute_block(Block::new(header, vec![in1, xt1])); + assert!(MockedSystemCallbacks::post_transactions_called()); + }); +} + +#[test] +fn is_inherent_works() { + let ext = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::inherent {})); + assert!(Runtime::is_inherent(&ext)); + let ext = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::optional_inherent {})); + assert!(Runtime::is_inherent(&ext)); + + let ext = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); + assert!(!Runtime::is_inherent(&ext)); + + let ext = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::allowed_unsigned {})); + assert!(!Runtime::is_inherent(&ext), "Unsigned ext are not automatically inherents"); +} diff --git a/substrate/frame/fast-unstake/Cargo.toml b/substrate/frame/fast-unstake/Cargo.toml index 673a2f71d6c8cfb839463b7bfc779ecccfb1a86e..eca8247845e2a5990ee98426866fdf5ac929034d 100644 --- a/substrate/frame/fast-unstake/Cargo.toml +++ b/substrate/frame/fast-unstake/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-fast-unstake" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../support", default-features = false } @@ -30,7 +30,7 @@ frame-election-provider-support = { path = "../election-provider-support", defau frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } -docify = "0.2.6" +docify = "0.2.7" [dev-dependencies] pallet-staking-reward-curve = { path = "../staking/reward-curve" } diff --git a/substrate/frame/fast-unstake/src/migrations.rs b/substrate/frame/fast-unstake/src/migrations.rs index 564388407045e0ae86b87c7823f3f07c5613e012..97ad86bfff42bf6ee1e258f5663d9bb53144373c 100644 --- a/substrate/frame/fast-unstake/src/migrations.rs +++ b/substrate/frame/fast-unstake/src/migrations.rs @@ -33,12 +33,12 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); diff --git a/substrate/frame/fast-unstake/src/mock.rs b/substrate/frame/fast-unstake/src/mock.rs index de1772264f16226f6ce1b2da7eeaa42c1fa8598a..78d881965a5531dea8a56a2f94015cf39afa2d90 100644 --- a/substrate/frame/fast-unstake/src/mock.rs +++ b/substrate/frame/fast-unstake/src/mock.rs @@ -77,7 +77,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/substrate/frame/fast-unstake/src/weights.rs b/substrate/frame/fast-unstake/src/weights.rs index 9c25a409f74099fc4d13158f99fae9036b34eff8..d783ba921bf9c11fcf1475d25ad87c6ca600ebf1 100644 --- a/substrate/frame/fast-unstake/src/weights.rs +++ b/substrate/frame/fast-unstake/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_fast_unstake +//! Autogenerated weights for `pallet_fast_unstake` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/fast-unstake/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/fast-unstake/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_fast_unstake. +/// Weight functions needed for `pallet_fast_unstake`. pub trait WeightInfo { fn on_idle_unstake(b: u32, ) -> Weight; fn on_idle_check(v: u32, b: u32, ) -> Weight; @@ -59,301 +58,305 @@ pub trait WeightInfo { fn control() -> Weight; } -/// Weights for pallet_fast_unstake using the Substrate node and recommended hardware. +/// Weights for `pallet_fast_unstake` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ValidatorCount (r:1 w:0) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:1) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:0) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:64 w:0) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Bonded (r:64 w:64) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:64 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:64 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: System Account (r:64 w:64) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:64 w:64) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:64 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:0 w:64) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:64) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ValidatorCount` (r:1 w:0) + /// Proof: `Staking::ValidatorCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:1) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:0) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::SlashingSpans` (r:64 w:0) + /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Staking::Bonded` (r:64 w:64) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:64 w:64) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:64 w:64) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:64 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:64 w:64) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::Validators` (r:64 w:0) + /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + /// Storage: `Staking::Nominators` (r:64 w:0) + /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Staking::Payee` (r:0 w:64) + /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 64]`. fn on_idle_unstake(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1378 + b * (343 ±0)` + // Measured: `1475 + b * (452 ±0)` // Estimated: `7253 + b * (3774 ±0)` - // Minimum execution time: 92_847_000 picoseconds. - Weight::from_parts(42_300_813, 7253) - // Standard Error: 40_514 - .saturating_add(Weight::from_parts(58_412_402, 0).saturating_mul(b.into())) + // Minimum execution time: 89_005_000 picoseconds. + Weight::from_parts(50_257_055, 7253) + // Standard Error: 68_836 + .saturating_add(Weight::from_parts(57_329_950, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().reads((7_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().reads((8_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 3774).saturating_mul(b.into())) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ValidatorCount (r:1 w:0) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:1) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:0) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStakers (r:257 w:0) - /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ValidatorCount` (r:1 w:0) + /// Proof: `Staking::ValidatorCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:1) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:0) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasStakers` (r:1 w:0) + /// Proof: `Staking::ErasStakers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ErasStakersPaged` (r:257 w:0) + /// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1, 256]`. /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1546 + b * (48 ±0) + v * (10037 ±0)` - // Estimated: `7253 + b * (49 ±0) + v * (12513 ±0)` - // Minimum execution time: 1_685_784_000 picoseconds. - Weight::from_parts(1_693_370_000, 7253) - // Standard Error: 13_295_842 - .saturating_add(Weight::from_parts(425_349_148, 0).saturating_mul(v.into())) - // Standard Error: 53_198_180 - .saturating_add(Weight::from_parts(1_673_328_444, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(7_u64)) + // Measured: `1879 + b * (55 ±0) + v * (10055 ±0)` + // Estimated: `7253 + b * (56 ±0) + v * (12531 ±0)` + // Minimum execution time: 1_737_131_000 picoseconds. + Weight::from_parts(1_746_770_000, 7253) + // Standard Error: 13_401_143 + .saturating_add(Weight::from_parts(426_946_450, 0).saturating_mul(v.into())) + // Standard Error: 53_619_501 + .saturating_add(Weight::from_parts(1_664_681_508, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 49).saturating_mul(b.into())) - .saturating_add(Weight::from_parts(0, 12513).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 56).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 12531).saturating_mul(v.into())) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: FastUnstake Queue (r:1 w:1) - /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:0) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:1 w:1) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:1) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Queue` (r:1 w:1) + /// Proof: `FastUnstake::Queue` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:0) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Validators` (r:1 w:0) + /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + /// Storage: `Staking::Nominators` (r:1 w:1) + /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Staking::CounterForNominators` (r:1 w:1) + /// Proof: `Staking::CounterForNominators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListNodes` (r:1 w:1) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:1 w:1) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) + /// Storage: `VoterList::CounterForListNodes` (r:1 w:1) + /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:1) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn register_fast_unstake() -> Weight { // Proof Size summary in bytes: - // Measured: `1964` + // Measured: `1955` // Estimated: `7253` - // Minimum execution time: 125_512_000 picoseconds. - Weight::from_parts(129_562_000, 7253) + // Minimum execution time: 112_632_000 picoseconds. + Weight::from_parts(117_267_000, 7253) .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(9_u64)) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: FastUnstake Queue (r:1 w:1) - /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:0) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:1) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Queue` (r:1 w:1) + /// Proof: `FastUnstake::Queue` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:0) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:1) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `1223` + // Measured: `1251` // Estimated: `7253` - // Minimum execution time: 43_943_000 picoseconds. - Weight::from_parts(45_842_000, 7253) + // Minimum execution time: 39_253_000 picoseconds. + Weight::from_parts(40_053_000, 7253) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:0 w:1) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn control() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_677_000 picoseconds. - Weight::from_parts(2_849_000, 0) + // Minimum execution time: 2_386_000 picoseconds. + Weight::from_parts(2_508_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ValidatorCount (r:1 w:0) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:1) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:0) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking SlashingSpans (r:64 w:0) - /// Proof Skipped: Staking SlashingSpans (max_values: None, max_size: None, mode: Measured) - /// Storage: Staking Bonded (r:64 w:64) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:64 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:64 w:0) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: System Account (r:64 w:64) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:64 w:64) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:64 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:0 w:64) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Staking Payee (r:0 w:64) - /// Proof: Staking Payee (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ValidatorCount` (r:1 w:0) + /// Proof: `Staking::ValidatorCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:1) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:0) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::SlashingSpans` (r:64 w:0) + /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Staking::Bonded` (r:64 w:64) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:64 w:64) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:64 w:64) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:64 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:64 w:64) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::Validators` (r:64 w:0) + /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + /// Storage: `Staking::Nominators` (r:64 w:0) + /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Staking::Payee` (r:0 w:64) + /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 64]`. fn on_idle_unstake(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1378 + b * (343 ±0)` + // Measured: `1475 + b * (452 ±0)` // Estimated: `7253 + b * (3774 ±0)` - // Minimum execution time: 92_847_000 picoseconds. - Weight::from_parts(42_300_813, 7253) - // Standard Error: 40_514 - .saturating_add(Weight::from_parts(58_412_402, 0).saturating_mul(b.into())) + // Minimum execution time: 89_005_000 picoseconds. + Weight::from_parts(50_257_055, 7253) + // Standard Error: 68_836 + .saturating_add(Weight::from_parts(57_329_950, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().reads((7_u64).saturating_mul(b.into()))) + .saturating_add(RocksDbWeight::get().reads((8_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 3774).saturating_mul(b.into())) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ValidatorCount (r:1 w:0) - /// Proof: Staking ValidatorCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:1) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:0) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: ElectionProviderMultiPhase CurrentPhase (r:1 w:0) - /// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking ErasStakers (r:257 w:0) - /// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ValidatorCount` (r:1 w:0) + /// Proof: `Staking::ValidatorCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:1) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:0) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ElectionProviderMultiPhase::CurrentPhase` (r:1 w:0) + /// Proof: `ElectionProviderMultiPhase::CurrentPhase` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasStakers` (r:1 w:0) + /// Proof: `Staking::ErasStakers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ErasStakersPaged` (r:257 w:0) + /// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `v` is `[1, 256]`. /// The range of component `b` is `[1, 64]`. fn on_idle_check(v: u32, b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1546 + b * (48 ±0) + v * (10037 ±0)` - // Estimated: `7253 + b * (49 ±0) + v * (12513 ±0)` - // Minimum execution time: 1_685_784_000 picoseconds. - Weight::from_parts(1_693_370_000, 7253) - // Standard Error: 13_295_842 - .saturating_add(Weight::from_parts(425_349_148, 0).saturating_mul(v.into())) - // Standard Error: 53_198_180 - .saturating_add(Weight::from_parts(1_673_328_444, 0).saturating_mul(b.into())) - .saturating_add(RocksDbWeight::get().reads(7_u64)) + // Measured: `1879 + b * (55 ±0) + v * (10055 ±0)` + // Estimated: `7253 + b * (56 ±0) + v * (12531 ±0)` + // Minimum execution time: 1_737_131_000 picoseconds. + Weight::from_parts(1_746_770_000, 7253) + // Standard Error: 13_401_143 + .saturating_add(Weight::from_parts(426_946_450, 0).saturating_mul(v.into())) + // Standard Error: 53_619_501 + .saturating_add(Weight::from_parts(1_664_681_508, 0).saturating_mul(b.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 49).saturating_mul(b.into())) - .saturating_add(Weight::from_parts(0, 12513).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 56).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 12531).saturating_mul(v.into())) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:1) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: FastUnstake Queue (r:1 w:1) - /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:0) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: Staking Bonded (r:1 w:0) - /// Proof: Staking Bonded (max_values: None, max_size: Some(72), added: 2547, mode: MaxEncodedLen) - /// Storage: Staking Validators (r:1 w:0) - /// Proof: Staking Validators (max_values: None, max_size: Some(45), added: 2520, mode: MaxEncodedLen) - /// Storage: Staking Nominators (r:1 w:1) - /// Proof: Staking Nominators (max_values: None, max_size: Some(558), added: 3033, mode: MaxEncodedLen) - /// Storage: Staking CounterForNominators (r:1 w:1) - /// Proof: Staking CounterForNominators (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: VoterList ListNodes (r:1 w:1) - /// Proof: VoterList ListNodes (max_values: None, max_size: Some(154), added: 2629, mode: MaxEncodedLen) - /// Storage: VoterList ListBags (r:1 w:1) - /// Proof: VoterList ListBags (max_values: None, max_size: Some(82), added: 2557, mode: MaxEncodedLen) - /// Storage: VoterList CounterForListNodes (r:1 w:1) - /// Proof: VoterList CounterForListNodes (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking CurrentEra (r:1 w:0) - /// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:1) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:1) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Queue` (r:1 w:1) + /// Proof: `FastUnstake::Queue` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:0) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Validators` (r:1 w:0) + /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) + /// Storage: `Staking::Nominators` (r:1 w:1) + /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Staking::CounterForNominators` (r:1 w:1) + /// Proof: `Staking::CounterForNominators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListNodes` (r:1 w:1) + /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) + /// Storage: `VoterList::ListBags` (r:1 w:1) + /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) + /// Storage: `VoterList::CounterForListNodes` (r:1 w:1) + /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::CurrentEra` (r:1 w:0) + /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:1) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn register_fast_unstake() -> Weight { // Proof Size summary in bytes: - // Measured: `1964` + // Measured: `1955` // Estimated: `7253` - // Minimum execution time: 125_512_000 picoseconds. - Weight::from_parts(129_562_000, 7253) + // Minimum execution time: 112_632_000 picoseconds. + Weight::from_parts(117_267_000, 7253) .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(9_u64)) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:1 w:0) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: FastUnstake Queue (r:1 w:1) - /// Proof: FastUnstake Queue (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) - /// Storage: FastUnstake Head (r:1 w:0) - /// Proof: FastUnstake Head (max_values: Some(1), max_size: Some(5768), added: 6263, mode: MaxEncodedLen) - /// Storage: FastUnstake CounterForQueue (r:1 w:1) - /// Proof: FastUnstake CounterForQueue (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:1 w:0) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Queue` (r:1 w:1) + /// Proof: `FastUnstake::Queue` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::Head` (r:1 w:0) + /// Proof: `FastUnstake::Head` (`max_values`: Some(1), `max_size`: Some(5768), added: 6263, mode: `MaxEncodedLen`) + /// Storage: `FastUnstake::CounterForQueue` (r:1 w:1) + /// Proof: `FastUnstake::CounterForQueue` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `1223` + // Measured: `1251` // Estimated: `7253` - // Minimum execution time: 43_943_000 picoseconds. - Weight::from_parts(45_842_000, 7253) + // Minimum execution time: 39_253_000 picoseconds. + Weight::from_parts(40_053_000, 7253) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: FastUnstake ErasToCheckPerBlock (r:0 w:1) - /// Proof: FastUnstake ErasToCheckPerBlock (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `FastUnstake::ErasToCheckPerBlock` (r:0 w:1) + /// Proof: `FastUnstake::ErasToCheckPerBlock` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn control() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_677_000 picoseconds. - Weight::from_parts(2_849_000, 0) + // Minimum execution time: 2_386_000 picoseconds. + Weight::from_parts(2_508_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/glutton/Cargo.toml b/substrate/frame/glutton/Cargo.toml index 068fb4e821cbad5efd83cc5a08ffd78cf1a2e9d0..7de18080b879edb369811a321501c8fe0630e3b7 100644 --- a/substrate/frame/glutton/Cargo.toml +++ b/substrate/frame/glutton/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-glutton" -version = "4.0.0-dev" +version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] blake2 = { version = "0.10.4", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -log = { version = "0.4.14", default-features = false } +log = { workspace = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/glutton/src/mock.rs b/substrate/frame/glutton/src/mock.rs index 26863811e29a76b1f8b3249050287e90c39a713b..0049800d95298386a9ed63e23c608d4a2938c758 100644 --- a/substrate/frame/glutton/src/mock.rs +++ b/substrate/frame/glutton/src/mock.rs @@ -18,15 +18,8 @@ use super::*; use crate as pallet_glutton; -use frame_support::{ - assert_ok, derive_impl, - traits::{ConstU32, ConstU64}, -}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use frame_support::{assert_ok, derive_impl}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -40,29 +33,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl Config for Test { diff --git a/substrate/frame/glutton/src/weights.rs b/substrate/frame/glutton/src/weights.rs index cbc0fb022f510889e66a403f1624c025faa5a7f8..b2e28dc488a6f24dc6da4690ff4939129ea9d2f3 100644 --- a/substrate/frame/glutton/src/weights.rs +++ b/substrate/frame/glutton/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_glutton +//! Autogenerated weights for `pallet_glutton` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/glutton/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/glutton/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_glutton. +/// Weight functions needed for `pallet_glutton`. pub trait WeightInfo { fn initialize_pallet_grow(n: u32, ) -> Weight; fn initialize_pallet_shrink(n: u32, ) -> Weight; @@ -63,39 +62,39 @@ pub trait WeightInfo { fn set_storage() -> Weight; } -/// Weights for pallet_glutton using the Substrate node and recommended hardware. +/// Weights for `pallet_glutton` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Glutton TrashDataCount (r:1 w:1) - /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:0 w:1000) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::TrashDataCount` (r:1 w:1) + /// Proof: `Glutton::TrashDataCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:0 w:1000) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn initialize_pallet_grow(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1489` - // Minimum execution time: 11_488_000 picoseconds. - Weight::from_parts(93_073_710, 1489) - // Standard Error: 22_390 - .saturating_add(Weight::from_parts(9_572_012, 0).saturating_mul(n.into())) + // Minimum execution time: 8_443_000 picoseconds. + Weight::from_parts(103_698_651, 1489) + // Standard Error: 21_777 + .saturating_add(Weight::from_parts(9_529_476, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) } - /// Storage: Glutton TrashDataCount (r:1 w:1) - /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:0 w:1000) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::TrashDataCount` (r:1 w:1) + /// Proof: `Glutton::TrashDataCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:0 w:1000) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn initialize_pallet_shrink(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `119` // Estimated: `1489` - // Minimum execution time: 11_378_000 picoseconds. - Weight::from_parts(5_591_508, 1489) - // Standard Error: 1_592 - .saturating_add(Weight::from_parts(1_163_758, 0).saturating_mul(n.into())) + // Minimum execution time: 8_343_000 picoseconds. + Weight::from_parts(304_498, 1489) + // Standard Error: 1_568 + .saturating_add(Weight::from_parts(1_146_553, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -105,119 +104,119 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 669_000 picoseconds. - Weight::from_parts(990_745, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(105_224, 0).saturating_mul(i.into())) + // Minimum execution time: 656_000 picoseconds. + Weight::from_parts(1_875_128, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(103_381, 0).saturating_mul(i.into())) } - /// Storage: Glutton TrashData (r:5000 w:0) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::TrashData` (r:5000 w:0) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 5000]`. fn waste_proof_size_some(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `119114 + i * (1022 ±0)` // Estimated: `990 + i * (3016 ±0)` - // Minimum execution time: 435_000 picoseconds. - Weight::from_parts(66_547_542, 990) - // Standard Error: 4_557 - .saturating_add(Weight::from_parts(5_851_324, 0).saturating_mul(i.into())) + // Minimum execution time: 454_000 picoseconds. + Weight::from_parts(521_000, 990) + // Standard Error: 1_940 + .saturating_add(Weight::from_parts(5_729_831, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } - /// Storage: Glutton Storage (r:1 w:0) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton Compute (r:1 w:0) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:1737 w:0) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:1 w:0) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::Compute` (r:1 w:0) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:1737 w:0) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `1900497` // Estimated: `5239782` - // Minimum execution time: 67_699_845_000 picoseconds. - Weight::from_parts(67_893_204_000, 5239782) + // Minimum execution time: 55_403_909_000 picoseconds. + Weight::from_parts(55_472_412_000, 5239782) .saturating_add(T::DbWeight::get().reads(1739_u64)) } - /// Storage: Glutton Storage (r:1 w:0) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton Compute (r:1 w:0) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:5 w:0) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:1 w:0) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::Compute` (r:1 w:0) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:5 w:0) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `9547` // Estimated: `16070` - // Minimum execution time: 122_297_527_000 picoseconds. - Weight::from_parts(122_394_818_000, 16070) + // Minimum execution time: 97_959_007_000 picoseconds. + Weight::from_parts(98_030_476_000, 16070) .saturating_add(T::DbWeight::get().reads(7_u64)) } - /// Storage: Glutton Storage (r:1 w:0) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton Compute (r:1 w:0) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:1 w:0) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::Compute` (r:1 w:0) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn empty_on_idle() -> Weight { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1493` - // Minimum execution time: 5_882_000 picoseconds. - Weight::from_parts(6_138_000, 1493) + // Minimum execution time: 5_011_000 picoseconds. + Weight::from_parts(5_183_000, 1493) .saturating_add(T::DbWeight::get().reads(2_u64)) } - /// Storage: Glutton Compute (r:0 w:1) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Glutton::Compute` (r:0 w:1) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set_compute() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_830_000 picoseconds. - Weight::from_parts(8_366_000, 0) + // Minimum execution time: 5_591_000 picoseconds. + Weight::from_parts(5_970_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Glutton Storage (r:0 w:1) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:0 w:1) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_933_000 picoseconds. - Weight::from_parts(8_213_000, 0) + // Minimum execution time: 5_689_000 picoseconds. + Weight::from_parts(6_038_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Glutton TrashDataCount (r:1 w:1) - /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:0 w:1000) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::TrashDataCount` (r:1 w:1) + /// Proof: `Glutton::TrashDataCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:0 w:1000) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn initialize_pallet_grow(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1489` - // Minimum execution time: 11_488_000 picoseconds. - Weight::from_parts(93_073_710, 1489) - // Standard Error: 22_390 - .saturating_add(Weight::from_parts(9_572_012, 0).saturating_mul(n.into())) + // Minimum execution time: 8_443_000 picoseconds. + Weight::from_parts(103_698_651, 1489) + // Standard Error: 21_777 + .saturating_add(Weight::from_parts(9_529_476, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) } - /// Storage: Glutton TrashDataCount (r:1 w:1) - /// Proof: Glutton TrashDataCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:0 w:1000) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::TrashDataCount` (r:1 w:1) + /// Proof: `Glutton::TrashDataCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:0 w:1000) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. fn initialize_pallet_shrink(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `119` // Estimated: `1489` - // Minimum execution time: 11_378_000 picoseconds. - Weight::from_parts(5_591_508, 1489) - // Standard Error: 1_592 - .saturating_add(Weight::from_parts(1_163_758, 0).saturating_mul(n.into())) + // Minimum execution time: 8_343_000 picoseconds. + Weight::from_parts(304_498, 1489) + // Standard Error: 1_568 + .saturating_add(Weight::from_parts(1_146_553, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -227,83 +226,83 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 669_000 picoseconds. - Weight::from_parts(990_745, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(105_224, 0).saturating_mul(i.into())) + // Minimum execution time: 656_000 picoseconds. + Weight::from_parts(1_875_128, 0) + // Standard Error: 8 + .saturating_add(Weight::from_parts(103_381, 0).saturating_mul(i.into())) } - /// Storage: Glutton TrashData (r:5000 w:0) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::TrashData` (r:5000 w:0) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 5000]`. fn waste_proof_size_some(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `119114 + i * (1022 ±0)` // Estimated: `990 + i * (3016 ±0)` - // Minimum execution time: 435_000 picoseconds. - Weight::from_parts(66_547_542, 990) - // Standard Error: 4_557 - .saturating_add(Weight::from_parts(5_851_324, 0).saturating_mul(i.into())) + // Minimum execution time: 454_000 picoseconds. + Weight::from_parts(521_000, 990) + // Standard Error: 1_940 + .saturating_add(Weight::from_parts(5_729_831, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3016).saturating_mul(i.into())) } - /// Storage: Glutton Storage (r:1 w:0) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton Compute (r:1 w:0) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:1737 w:0) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:1 w:0) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::Compute` (r:1 w:0) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:1737 w:0) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) fn on_idle_high_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `1900497` // Estimated: `5239782` - // Minimum execution time: 67_699_845_000 picoseconds. - Weight::from_parts(67_893_204_000, 5239782) + // Minimum execution time: 55_403_909_000 picoseconds. + Weight::from_parts(55_472_412_000, 5239782) .saturating_add(RocksDbWeight::get().reads(1739_u64)) } - /// Storage: Glutton Storage (r:1 w:0) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton Compute (r:1 w:0) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton TrashData (r:5 w:0) - /// Proof: Glutton TrashData (max_values: Some(65000), max_size: Some(1036), added: 3016, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:1 w:0) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::Compute` (r:1 w:0) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::TrashData` (r:5 w:0) + /// Proof: `Glutton::TrashData` (`max_values`: Some(65000), `max_size`: Some(1036), added: 3016, mode: `MaxEncodedLen`) fn on_idle_low_proof_waste() -> Weight { // Proof Size summary in bytes: // Measured: `9547` // Estimated: `16070` - // Minimum execution time: 122_297_527_000 picoseconds. - Weight::from_parts(122_394_818_000, 16070) + // Minimum execution time: 97_959_007_000 picoseconds. + Weight::from_parts(98_030_476_000, 16070) .saturating_add(RocksDbWeight::get().reads(7_u64)) } - /// Storage: Glutton Storage (r:1 w:0) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Glutton Compute (r:1 w:0) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:1 w:0) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Glutton::Compute` (r:1 w:0) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn empty_on_idle() -> Weight { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1493` - // Minimum execution time: 5_882_000 picoseconds. - Weight::from_parts(6_138_000, 1493) + // Minimum execution time: 5_011_000 picoseconds. + Weight::from_parts(5_183_000, 1493) .saturating_add(RocksDbWeight::get().reads(2_u64)) } - /// Storage: Glutton Compute (r:0 w:1) - /// Proof: Glutton Compute (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Glutton::Compute` (r:0 w:1) + /// Proof: `Glutton::Compute` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set_compute() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_830_000 picoseconds. - Weight::from_parts(8_366_000, 0) + // Minimum execution time: 5_591_000 picoseconds. + Weight::from_parts(5_970_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Glutton Storage (r:0 w:1) - /// Proof: Glutton Storage (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Glutton::Storage` (r:0 w:1) + /// Proof: `Glutton::Storage` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_933_000 picoseconds. - Weight::from_parts(8_213_000, 0) + // Minimum execution time: 5_689_000 picoseconds. + Weight::from_parts(6_038_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/grandpa/Cargo.toml b/substrate/frame/grandpa/Cargo.toml index b4f51d88c6d21a7d1367f5a29cf7398660125006..db540564fbe7bda05b006b5fb7d2b6da2e33d7c5 100644 --- a/substrate/frame/grandpa/Cargo.toml +++ b/substrate/frame/grandpa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-grandpa" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/grandpa/src/lib.rs b/substrate/frame/grandpa/src/lib.rs index 0b9f2b3582792e9d93695d34656912a8d70a131f..90bcd8721dfa1f6d091edc34da883c2172d9f6ca 100644 --- a/substrate/frame/grandpa/src/lib.rs +++ b/substrate/frame/grandpa/src/lib.rs @@ -73,7 +73,7 @@ pub mod pallet { use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); #[pallet::pallet] diff --git a/substrate/frame/grandpa/src/migrations.rs b/substrate/frame/grandpa/src/migrations.rs index 3a484eb60d284c3988a1c2e1fad49bdc10d03b6a..9d0522c3a6e7281d206d4d7e606a22362a801f49 100644 --- a/substrate/frame/grandpa/src/migrations.rs +++ b/substrate/frame/grandpa/src/migrations.rs @@ -35,7 +35,7 @@ mod v5; /// This migration should be added with a runtime upgrade that introduces the /// `MaxSetIdSessionEntries` constant to the pallet (although it could also be /// done later on). -pub struct CleanupSetIdSessionMap(sp_std::marker::PhantomData); +pub struct CleanupSetIdSessionMap(core::marker::PhantomData); impl OnRuntimeUpgrade for CleanupSetIdSessionMap { fn on_runtime_upgrade() -> Weight { // NOTE: since this migration will loop over all stale entries in the diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index f1f51e0b118163ec1eee16fb951260306832e6da..e1be487dbb75cf2c7523e808ec8dc8ec015eadb1 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -35,11 +35,8 @@ use sp_consensus_grandpa::{RoundNumber, SetId, GRANDPA_ENGINE_ID}; use sp_core::{crypto::KeyTypeId, H256}; use sp_keyring::Ed25519Keyring; use sp_runtime::{ - curve::PiecewiseLinear, - impl_opaque_keys, - testing::{TestXt, UintAuthorityId}, - traits::{IdentityLookup, OpaqueKeys}, - BuildStorage, DigestItem, Perbill, + curve::PiecewiseLinear, generic::UncheckedExtrinsic, impl_opaque_keys, + testing::UintAuthorityId, traits::OpaqueKeys, BuildStorage, DigestItem, Perbill, }; use sp_staking::{EraIndex, SessionIndex}; @@ -68,29 +65,8 @@ impl_opaque_keys! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = sp_runtime::traits::BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } impl frame_system::offchain::SendTransactionTypes for Test @@ -98,7 +74,7 @@ where RuntimeCall: From, { type OverarchingCall = RuntimeCall; - type Extrinsic = TestXt; + type Extrinsic = UncheckedExtrinsic; } parameter_types! { @@ -143,7 +119,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_timestamp::Config for Test { diff --git a/substrate/frame/identity/Cargo.toml b/substrate/frame/identity/Cargo.toml index 1197a37ecae3cd2bbd492348018175c7bac51ba5..912444bf603606e885d7b0c38c8f8bccbbe93d34 100644 --- a/substrate/frame/identity/Cargo.toml +++ b/substrate/frame/identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-identity" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } enumflags2 = { version = "0.7.7" } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/identity/src/benchmarking.rs b/substrate/frame/identity/src/benchmarking.rs index fe2fb0b048933c13425c0127b752bf773e00bea2..e0b45eeecd1cf5494f52f83ec835037075155ea7 100644 --- a/substrate/frame/identity/src/benchmarking.rs +++ b/substrate/frame/identity/src/benchmarking.rs @@ -23,9 +23,7 @@ use super::*; use crate::Pallet as Identity; use codec::Encode; -use frame_benchmarking::{ - account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError, -}; +use frame_benchmarking::{account, v2::*, whitelisted_caller, BenchmarkError}; use frame_support::{ assert_ok, ensure, traits::{EnsureOrigin, Get, OnFinalize, OnInitialize}, diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs index a2b26120254af03d26969c028ef3c700f66660d5..60866f12baa61783aa27333f66a07193059ce727 100644 --- a/substrate/frame/identity/src/tests.rs +++ b/substrate/frame/identity/src/tests.rs @@ -55,29 +55,10 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -94,7 +75,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { diff --git a/substrate/frame/identity/src/weights.rs b/substrate/frame/identity/src/weights.rs index 1feb8252c845383ff8a31f2f992e6cf6b3c0001d..81de520d7f2bca8f298e136b7fb58d35ba3f28d8 100644 --- a/substrate/frame/identity/src/weights.rs +++ b/substrate/frame/identity/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_identity +//! Autogenerated weights for `pallet_identity` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/identity/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/identity/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_identity. +/// Weight functions needed for `pallet_identity`. pub trait WeightInfo { fn add_registrar(r: u32, ) -> Weight; fn set_identity(r: u32, ) -> Weight; @@ -77,278 +76,274 @@ pub trait WeightInfo { fn remove_dangling_username() -> Weight; } -/// Weights for pallet_identity using the Substrate node and recommended hardware. +/// Weights for `pallet_identity` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 11_683_000 picoseconds. - Weight::from_parts(12_515_830, 2626) - // Standard Error: 2_154 - .saturating_add(Weight::from_parts(147_919, 0).saturating_mul(r.into())) + // Minimum execution time: 8_857_000 picoseconds. + Weight::from_parts(9_331_464, 2626) + // Standard Error: 1_745 + .saturating_add(Weight::from_parts(94_096, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. - fn set_identity(r: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `442 + r * (5 ±0)` - // Estimated: `11003` - // Minimum execution time: 32_949_000 picoseconds. - Weight::from_parts(31_329_634, 11003) - // Standard Error: 4_496 - .saturating_add(Weight::from_parts(203_570, 0).saturating_mul(r.into())) + fn set_identity(_r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `6978 + r * (5 ±0)` + // Estimated: `11037` + // Minimum execution time: 96_522_000 picoseconds. + Weight::from_parts(102_738_605, 11037) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:100 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:100 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `11003 + s * (2589 ±0)` - // Minimum execution time: 9_157_000 picoseconds. - Weight::from_parts(24_917_444, 11003) - // Standard Error: 4_554 - .saturating_add(Weight::from_parts(3_279_868, 0).saturating_mul(s.into())) + // Estimated: `11037 + s * (2589 ±0)` + // Minimum execution time: 9_112_000 picoseconds. + Weight::from_parts(22_676_873, 11037) + // Standard Error: 6_494 + .saturating_add(Weight::from_parts(3_279_196, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 2589).saturating_mul(s.into())) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `194 + p * (32 ±0)` - // Estimated: `11003` - // Minimum execution time: 9_240_000 picoseconds. - Weight::from_parts(23_326_035, 11003) - // Standard Error: 3_664 - .saturating_add(Weight::from_parts(1_439_873, 0).saturating_mul(p.into())) + // Estimated: `11037` + // Minimum execution time: 8_902_000 picoseconds. + Weight::from_parts(21_168_031, 11037) + // Standard Error: 3_576 + .saturating_add(Weight::from_parts(1_431_542, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - fn clear_identity(r: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 55_687_000 picoseconds. - Weight::from_parts(30_695_182, 11003) - // Standard Error: 9_921 - .saturating_add(Weight::from_parts(162_357, 0).saturating_mul(r.into())) - // Standard Error: 1_937 - .saturating_add(Weight::from_parts(1_427_998, 0).saturating_mul(s.into())) + fn clear_identity(_r: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `7070 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 52_468_000 picoseconds. + Weight::from_parts(56_065_452, 11037) + // Standard Error: 2_274 + .saturating_add(Weight::from_parts(1_399_051, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `367 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 34_876_000 picoseconds. - Weight::from_parts(32_207_018, 11003) - // Standard Error: 5_247 - .saturating_add(Weight::from_parts(249_156, 0).saturating_mul(r.into())) + // Measured: `6968 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 67_951_000 picoseconds. + Weight::from_parts(69_591_058, 11037) + // Standard Error: 4_420 + .saturating_add(Weight::from_parts(209_239, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `398 + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 30_689_000 picoseconds. - Weight::from_parts(31_967_170, 11003) - // Standard Error: 5_387 - .saturating_add(Weight::from_parts(42_676, 0).saturating_mul(r.into())) + // Measured: `6999` + // Estimated: `11037` + // Minimum execution time: 67_818_000 picoseconds. + Weight::from_parts(70_356_319, 11037) + // Standard Error: 5_116 + .saturating_add(Weight::from_parts(4_264, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_357_000 picoseconds. - Weight::from_parts(7_932_950, 2626) - // Standard Error: 1_804 - .saturating_add(Weight::from_parts(132_653, 0).saturating_mul(r.into())) + // Minimum execution time: 6_096_000 picoseconds. + Weight::from_parts(6_470_752, 2626) + // Standard Error: 1_328 + .saturating_add(Weight::from_parts(94_764, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_437_000 picoseconds. - Weight::from_parts(8_051_889, 2626) - // Standard Error: 1_997 - .saturating_add(Weight::from_parts(129_592, 0).saturating_mul(r.into())) + // Minimum execution time: 6_278_000 picoseconds. + Weight::from_parts(6_678_997, 2626) + // Standard Error: 1_396 + .saturating_add(Weight::from_parts(87_207, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_385_000 picoseconds. - Weight::from_parts(7_911_589, 2626) - // Standard Error: 1_791 - .saturating_add(Weight::from_parts(125_788, 0).saturating_mul(r.into())) + // Minimum execution time: 6_130_000 picoseconds. + Weight::from_parts(6_516_146, 2626) + // Standard Error: 1_356 + .saturating_add(Weight::from_parts(88_455, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `445 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 24_073_000 picoseconds. - Weight::from_parts(17_817_684, 11003) - // Standard Error: 8_612 - .saturating_add(Weight::from_parts(406_251, 0).saturating_mul(r.into())) + // Measured: `7046 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 87_100_000 picoseconds. + Weight::from_parts(87_601_945, 11037) + // Standard Error: 22_724 + .saturating_add(Weight::from_parts(458_296, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 73_981_000 picoseconds. - Weight::from_parts(51_684_057, 11003) - // Standard Error: 12_662 - .saturating_add(Weight::from_parts(145_285, 0).saturating_mul(r.into())) - // Standard Error: 2_472 - .saturating_add(Weight::from_parts(1_421_039, 0).saturating_mul(s.into())) + // Measured: `7277 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 65_925_000 picoseconds. + Weight::from_parts(70_786_250, 11037) + // Standard Error: 19_680 + .saturating_add(Weight::from_parts(29_782, 0).saturating_mul(r.into())) + // Standard Error: 3_839 + .saturating_add(Weight::from_parts(1_377_604, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `475 + s * (36 ±0)` - // Estimated: `11003` - // Minimum execution time: 29_367_000 picoseconds. - Weight::from_parts(34_214_998, 11003) - // Standard Error: 1_522 - .saturating_add(Weight::from_parts(114_551, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 25_916_000 picoseconds. + Weight::from_parts(29_781_270, 11037) + // Standard Error: 1_326 + .saturating_add(Weight::from_parts(101_515, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + s * (3 ±0)` - // Estimated: `11003` - // Minimum execution time: 12_384_000 picoseconds. - Weight::from_parts(14_417_903, 11003) - // Standard Error: 539 - .saturating_add(Weight::from_parts(38_371, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 12_142_000 picoseconds. + Weight::from_parts(14_179_741, 11037) + // Standard Error: 538 + .saturating_add(Weight::from_parts(38_847, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `638 + s * (35 ±0)` - // Estimated: `11003` - // Minimum execution time: 33_327_000 picoseconds. - Weight::from_parts(36_208_941, 11003) - // Standard Error: 1_240 - .saturating_add(Weight::from_parts(105_805, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 29_327_000 picoseconds. + Weight::from_parts(32_163_402, 11037) + // Standard Error: 1_047 + .saturating_add(Weight::from_parts(87_159, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `704 + s * (37 ±0)` // Estimated: `6723` - // Minimum execution time: 23_764_000 picoseconds. - Weight::from_parts(26_407_731, 6723) - // Standard Error: 1_025 - .saturating_add(Weight::from_parts(101_112, 0).saturating_mul(s.into())) + // Minimum execution time: 22_380_000 picoseconds. + Weight::from_parts(24_557_574, 6723) + // Standard Error: 1_131 + .saturating_add(Weight::from_parts(86_358, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -358,367 +353,359 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_873_000 picoseconds. - Weight::from_parts(13_873_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(1)) + // Minimum execution time: 6_791_000 picoseconds. + Weight::from_parts(7_126_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Identity::UsernameAuthorities` (r:0 w:1) + /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn remove_username_authority() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 10_653_000 picoseconds. - Weight::from_parts(10_653_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(1)) + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 9_657_000 picoseconds. + Weight::from_parts(9_922_000, 3517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Identity::PendingUsernames` (r:1 w:0) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_username_for() -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` - // Minimum execution time: 75_928_000 picoseconds. - Weight::from_parts(75_928_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + // Minimum execution time: 67_928_000 picoseconds. + Weight::from_parts(69_993_000, 11037) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:0 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn accept_username() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `115` // Estimated: `11037` - // Minimum execution time: 38_157_000 picoseconds. - Weight::from_parts(38_157_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(3)) + // Minimum execution time: 20_496_000 picoseconds. + Weight::from_parts(20_971_000, 11037) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn remove_expired_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `3542` - // Minimum execution time: 46_821_000 picoseconds. - Weight::from_parts(46_821_000, 0) - .saturating_add(Weight::from_parts(0, 3542)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) + // Measured: `115` + // Estimated: `3550` + // Minimum execution time: 13_845_000 picoseconds. + Weight::from_parts(16_201_000, 3550) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:0) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_primary_username() -> Weight { // Proof Size summary in bytes: - // Measured: `247` + // Measured: `257` // Estimated: `11037` - // Minimum execution time: 22_515_000 picoseconds. - Weight::from_parts(22_515_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Minimum execution time: 15_900_000 picoseconds. + Weight::from_parts(16_716_000, 11037) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:0) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn remove_dangling_username() -> Weight { // Proof Size summary in bytes: - // Measured: `126` + // Measured: `98` // Estimated: `11037` - // Minimum execution time: 15_997_000 picoseconds. - Weight::from_parts(15_997_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Minimum execution time: 11_538_000 picoseconds. + Weight::from_parts(11_898_000, 11037) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 11_683_000 picoseconds. - Weight::from_parts(12_515_830, 2626) - // Standard Error: 2_154 - .saturating_add(Weight::from_parts(147_919, 0).saturating_mul(r.into())) + // Minimum execution time: 8_857_000 picoseconds. + Weight::from_parts(9_331_464, 2626) + // Standard Error: 1_745 + .saturating_add(Weight::from_parts(94_096, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. - fn set_identity(r: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `442 + r * (5 ±0)` - // Estimated: `11003` - // Minimum execution time: 32_949_000 picoseconds. - Weight::from_parts(31_329_634, 11003) - // Standard Error: 4_496 - .saturating_add(Weight::from_parts(203_570, 0).saturating_mul(r.into())) + fn set_identity(_r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `6978 + r * (5 ±0)` + // Estimated: `11037` + // Minimum execution time: 96_522_000 picoseconds. + Weight::from_parts(102_738_605, 11037) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:100 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:100 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `11003 + s * (2589 ±0)` - // Minimum execution time: 9_157_000 picoseconds. - Weight::from_parts(24_917_444, 11003) - // Standard Error: 4_554 - .saturating_add(Weight::from_parts(3_279_868, 0).saturating_mul(s.into())) + // Estimated: `11037 + s * (2589 ±0)` + // Minimum execution time: 9_112_000 picoseconds. + Weight::from_parts(22_676_873, 11037) + // Standard Error: 6_494 + .saturating_add(Weight::from_parts(3_279_196, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 2589).saturating_mul(s.into())) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `194 + p * (32 ±0)` - // Estimated: `11003` - // Minimum execution time: 9_240_000 picoseconds. - Weight::from_parts(23_326_035, 11003) - // Standard Error: 3_664 - .saturating_add(Weight::from_parts(1_439_873, 0).saturating_mul(p.into())) + // Estimated: `11037` + // Minimum execution time: 8_902_000 picoseconds. + Weight::from_parts(21_168_031, 11037) + // Standard Error: 3_576 + .saturating_add(Weight::from_parts(1_431_542, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - fn clear_identity(r: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 55_687_000 picoseconds. - Weight::from_parts(30_695_182, 11003) - // Standard Error: 9_921 - .saturating_add(Weight::from_parts(162_357, 0).saturating_mul(r.into())) - // Standard Error: 1_937 - .saturating_add(Weight::from_parts(1_427_998, 0).saturating_mul(s.into())) + fn clear_identity(_r: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `7070 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 52_468_000 picoseconds. + Weight::from_parts(56_065_452, 11037) + // Standard Error: 2_274 + .saturating_add(Weight::from_parts(1_399_051, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `367 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 34_876_000 picoseconds. - Weight::from_parts(32_207_018, 11003) - // Standard Error: 5_247 - .saturating_add(Weight::from_parts(249_156, 0).saturating_mul(r.into())) + // Measured: `6968 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 67_951_000 picoseconds. + Weight::from_parts(69_591_058, 11037) + // Standard Error: 4_420 + .saturating_add(Weight::from_parts(209_239, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `398 + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 30_689_000 picoseconds. - Weight::from_parts(31_967_170, 11003) - // Standard Error: 5_387 - .saturating_add(Weight::from_parts(42_676, 0).saturating_mul(r.into())) + // Measured: `6999` + // Estimated: `11037` + // Minimum execution time: 67_818_000 picoseconds. + Weight::from_parts(70_356_319, 11037) + // Standard Error: 5_116 + .saturating_add(Weight::from_parts(4_264, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_357_000 picoseconds. - Weight::from_parts(7_932_950, 2626) - // Standard Error: 1_804 - .saturating_add(Weight::from_parts(132_653, 0).saturating_mul(r.into())) + // Minimum execution time: 6_096_000 picoseconds. + Weight::from_parts(6_470_752, 2626) + // Standard Error: 1_328 + .saturating_add(Weight::from_parts(94_764, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_437_000 picoseconds. - Weight::from_parts(8_051_889, 2626) - // Standard Error: 1_997 - .saturating_add(Weight::from_parts(129_592, 0).saturating_mul(r.into())) + // Minimum execution time: 6_278_000 picoseconds. + Weight::from_parts(6_678_997, 2626) + // Standard Error: 1_396 + .saturating_add(Weight::from_parts(87_207, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_385_000 picoseconds. - Weight::from_parts(7_911_589, 2626) - // Standard Error: 1_791 - .saturating_add(Weight::from_parts(125_788, 0).saturating_mul(r.into())) + // Minimum execution time: 6_130_000 picoseconds. + Weight::from_parts(6_516_146, 2626) + // Standard Error: 1_356 + .saturating_add(Weight::from_parts(88_455, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `445 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 24_073_000 picoseconds. - Weight::from_parts(17_817_684, 11003) - // Standard Error: 8_612 - .saturating_add(Weight::from_parts(406_251, 0).saturating_mul(r.into())) + // Measured: `7046 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 87_100_000 picoseconds. + Weight::from_parts(87_601_945, 11037) + // Standard Error: 22_724 + .saturating_add(Weight::from_parts(458_296, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 73_981_000 picoseconds. - Weight::from_parts(51_684_057, 11003) - // Standard Error: 12_662 - .saturating_add(Weight::from_parts(145_285, 0).saturating_mul(r.into())) - // Standard Error: 2_472 - .saturating_add(Weight::from_parts(1_421_039, 0).saturating_mul(s.into())) + // Measured: `7277 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 65_925_000 picoseconds. + Weight::from_parts(70_786_250, 11037) + // Standard Error: 19_680 + .saturating_add(Weight::from_parts(29_782, 0).saturating_mul(r.into())) + // Standard Error: 3_839 + .saturating_add(Weight::from_parts(1_377_604, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `475 + s * (36 ±0)` - // Estimated: `11003` - // Minimum execution time: 29_367_000 picoseconds. - Weight::from_parts(34_214_998, 11003) - // Standard Error: 1_522 - .saturating_add(Weight::from_parts(114_551, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 25_916_000 picoseconds. + Weight::from_parts(29_781_270, 11037) + // Standard Error: 1_326 + .saturating_add(Weight::from_parts(101_515, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + s * (3 ±0)` - // Estimated: `11003` - // Minimum execution time: 12_384_000 picoseconds. - Weight::from_parts(14_417_903, 11003) - // Standard Error: 539 - .saturating_add(Weight::from_parts(38_371, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 12_142_000 picoseconds. + Weight::from_parts(14_179_741, 11037) + // Standard Error: 538 + .saturating_add(Weight::from_parts(38_847, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `638 + s * (35 ±0)` - // Estimated: `11003` - // Minimum execution time: 33_327_000 picoseconds. - Weight::from_parts(36_208_941, 11003) - // Standard Error: 1_240 - .saturating_add(Weight::from_parts(105_805, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 29_327_000 picoseconds. + Weight::from_parts(32_163_402, 11037) + // Standard Error: 1_047 + .saturating_add(Weight::from_parts(87_159, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `704 + s * (37 ±0)` // Estimated: `6723` - // Minimum execution time: 23_764_000 picoseconds. - Weight::from_parts(26_407_731, 6723) - // Standard Error: 1_025 - .saturating_add(Weight::from_parts(101_112, 0).saturating_mul(s.into())) + // Minimum execution time: 22_380_000 picoseconds. + Weight::from_parts(24_557_574, 6723) + // Standard Error: 1_131 + .saturating_add(Weight::from_parts(86_358, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -728,92 +715,88 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_873_000 picoseconds. - Weight::from_parts(13_873_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Minimum execution time: 6_791_000 picoseconds. + Weight::from_parts(7_126_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Identity::UsernameAuthorities` (r:0 w:1) + /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn remove_username_authority() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 10_653_000 picoseconds. - Weight::from_parts(10_653_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 9_657_000 picoseconds. + Weight::from_parts(9_922_000, 3517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Identity::PendingUsernames` (r:1 w:0) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_username_for() -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` - // Minimum execution time: 75_928_000 picoseconds. - Weight::from_parts(75_928_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(RocksDbWeight::get().reads(3)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Minimum execution time: 67_928_000 picoseconds. + Weight::from_parts(69_993_000, 11037) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:0 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn accept_username() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `115` // Estimated: `11037` - // Minimum execution time: 38_157_000 picoseconds. - Weight::from_parts(38_157_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Minimum execution time: 20_496_000 picoseconds. + Weight::from_parts(20_971_000, 11037) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn remove_expired_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `3542` - // Minimum execution time: 46_821_000 picoseconds. - Weight::from_parts(46_821_000, 0) - .saturating_add(Weight::from_parts(0, 3542)) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Measured: `115` + // Estimated: `3550` + // Minimum execution time: 13_845_000 picoseconds. + Weight::from_parts(16_201_000, 3550) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:0) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_primary_username() -> Weight { // Proof Size summary in bytes: - // Measured: `247` + // Measured: `257` // Estimated: `11037` - // Minimum execution time: 22_515_000 picoseconds. - Weight::from_parts(22_515_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Minimum execution time: 15_900_000 picoseconds. + Weight::from_parts(16_716_000, 11037) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:0) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn remove_dangling_username() -> Weight { // Proof Size summary in bytes: - // Measured: `126` + // Measured: `98` // Estimated: `11037` - // Minimum execution time: 15_997_000 picoseconds. - Weight::from_parts(15_997_000, 0) - .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Minimum execution time: 11_538_000 picoseconds. + Weight::from_parts(11_898_000, 11037) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/im-online/Cargo.toml b/substrate/frame/im-online/Cargo.toml index b5b01858c898a6cc4596e55e76b85433f79e9ca6..038cbbcd678cca4d947eb4750e11314f643f2ad9 100644 --- a/substrate/frame/im-online/Cargo.toml +++ b/substrate/frame/im-online/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-im-online" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/im-online/src/lib.rs b/substrate/frame/im-online/src/lib.rs index 1de89dd00c8121c64a19f8b45f24fc6500126dce..f14093aa09afa98da1c37b0e961319e80821fe40 100644 --- a/substrate/frame/im-online/src/lib.rs +++ b/substrate/frame/im-online/src/lib.rs @@ -251,7 +251,7 @@ type OffchainResult = Result>>; pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/im-online/src/migration.rs b/substrate/frame/im-online/src/migration.rs index 84652965972e3aecbdd8edba67cb3f5f1b33e49c..754a2e672e6cfaec32ed0e44c9af54469fddbb04 100644 --- a/substrate/frame/im-online/src/migration.rs +++ b/substrate/frame/im-online/src/migration.rs @@ -56,7 +56,7 @@ pub mod v1 { use super::*; /// Simple migration that replaces `ReceivedHeartbeats` values with `true`. - pub struct Migration(sp_std::marker::PhantomData); + pub struct Migration(core::marker::PhantomData); impl OnRuntimeUpgrade for Migration { #[cfg(feature = "try-runtime")] @@ -72,7 +72,7 @@ pub mod v1 { if StorageVersion::get::>() != 0 { log::warn!( target: TARGET, - "Skipping migration because current storage version is not 0" + "Skipping migration because in-code storage version is not 0" ); return weight } @@ -116,6 +116,21 @@ pub mod v1 { } } +/// Clears the pallet's offchain storage. +/// +/// Must be put in `OffchainWorkerApi::offchain_worker` after +/// the pallet was removed. +pub fn clear_offchain_storage(validator_set_size: u32) { + (0..validator_set_size).for_each(|idx| { + let key = { + let mut key = DB_PREFIX.to_vec(); + key.extend(idx.encode()); + key + }; + sp_runtime::offchain::storage::StorageValueRef::persistent(&key).clear(); + }); +} + #[cfg(all(feature = "try-runtime", test))] mod test { use super::*; diff --git a/substrate/frame/im-online/src/mock.rs b/substrate/frame/im-online/src/mock.rs index 9dad148b10fa1b0942e93f1c5d183264ab282f05..c0ba9143e1385ebc0b57445c864a270d28936b8d 100644 --- a/substrate/frame/im-online/src/mock.rs +++ b/substrate/frame/im-online/src/mock.rs @@ -27,7 +27,7 @@ use frame_support::{ use pallet_session::historical as pallet_session_historical; use sp_core::H256; use sp_runtime::{ - testing::{TestXt, UintAuthorityId}, + testing::UintAuthorityId, traits::{BlakeTwo256, ConvertInto, IdentityLookup}, BuildStorage, Permill, }; @@ -78,7 +78,7 @@ impl pallet_session::historical::SessionManager for TestSessionManager } /// An extrinsic type used for tests. -pub type Extrinsic = TestXt; +pub type Extrinsic = sp_runtime::generic::UncheckedExtrinsic; type IdentificationTuple = (u64, u64); type Offence = crate::UnresponsivenessOffence; diff --git a/substrate/frame/im-online/src/tests.rs b/substrate/frame/im-online/src/tests.rs index 79036760c2d427c2895163b150f3806bf17222f2..7efca926eb0dc4f0098af01967b5214ab371e143 100644 --- a/substrate/frame/im-online/src/tests.rs +++ b/substrate/frame/im-online/src/tests.rs @@ -231,7 +231,7 @@ fn should_generate_heartbeats() { // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.call { + let heartbeat = match ex.function { crate::mock::RuntimeCall::ImOnline(crate::Call::heartbeat { heartbeat, .. }) => heartbeat, e => panic!("Unexpected call: {:?}", e), @@ -345,7 +345,7 @@ fn should_not_send_a_report_if_already_online() { assert_eq!(pool_state.read().transactions.len(), 0); // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.call { + let heartbeat = match ex.function { crate::mock::RuntimeCall::ImOnline(crate::Call::heartbeat { heartbeat, .. }) => heartbeat, e => panic!("Unexpected call: {:?}", e), diff --git a/substrate/frame/im-online/src/weights.rs b/substrate/frame/im-online/src/weights.rs index c3db02af2578209d9ab97a777ef4a7447d45b738..11357b1e7b7d38983966ff6d03e91ddec0bd10cf 100644 --- a/substrate/frame/im-online/src/weights.rs +++ b/substrate/frame/im-online/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_im_online +//! Autogenerated weights for `pallet_im_online` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/im-online/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/im-online/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,60 +49,60 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_im_online. +/// Weight functions needed for `pallet_im_online`. pub trait WeightInfo { fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight; } -/// Weights for pallet_im_online using the Substrate node and recommended hardware. +/// Weights for `pallet_im_online` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ImOnline Keys (r:1 w:0) - /// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen) - /// Storage: ImOnline ReceivedHeartbeats (r:1 w:1) - /// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(25), added: 2500, mode: MaxEncodedLen) - /// Storage: ImOnline AuthoredBlocks (r:1 w:0) - /// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ImOnline::Keys` (r:1 w:0) + /// Proof: `ImOnline::Keys` (`max_values`: Some(1), `max_size`: Some(320002), added: 320497, mode: `MaxEncodedLen`) + /// Storage: `ImOnline::ReceivedHeartbeats` (r:1 w:1) + /// Proof: `ImOnline::ReceivedHeartbeats` (`max_values`: None, `max_size`: Some(25), added: 2500, mode: `MaxEncodedLen`) + /// Storage: `ImOnline::AuthoredBlocks` (r:1 w:0) + /// Proof: `ImOnline::AuthoredBlocks` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) /// The range of component `k` is `[1, 1000]`. fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `295 + k * (32 ±0)` + // Measured: `327 + k * (32 ±0)` // Estimated: `321487 + k * (1761 ±0)` - // Minimum execution time: 80_568_000 picoseconds. - Weight::from_parts(95_175_595, 321487) - // Standard Error: 627 - .saturating_add(Weight::from_parts(39_094, 0).saturating_mul(k.into())) + // Minimum execution time: 65_515_000 picoseconds. + Weight::from_parts(74_765_329, 321487) + // Standard Error: 500 + .saturating_add(Weight::from_parts(39_171, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1761).saturating_mul(k.into())) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Session Validators (r:1 w:0) - /// Proof Skipped: Session Validators (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Session CurrentIndex (r:1 w:0) - /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ImOnline Keys (r:1 w:0) - /// Proof: ImOnline Keys (max_values: Some(1), max_size: Some(320002), added: 320497, mode: MaxEncodedLen) - /// Storage: ImOnline ReceivedHeartbeats (r:1 w:1) - /// Proof: ImOnline ReceivedHeartbeats (max_values: None, max_size: Some(25), added: 2500, mode: MaxEncodedLen) - /// Storage: ImOnline AuthoredBlocks (r:1 w:0) - /// Proof: ImOnline AuthoredBlocks (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::CurrentIndex` (r:1 w:0) + /// Proof: `Session::CurrentIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ImOnline::Keys` (r:1 w:0) + /// Proof: `ImOnline::Keys` (`max_values`: Some(1), `max_size`: Some(320002), added: 320497, mode: `MaxEncodedLen`) + /// Storage: `ImOnline::ReceivedHeartbeats` (r:1 w:1) + /// Proof: `ImOnline::ReceivedHeartbeats` (`max_values`: None, `max_size`: Some(25), added: 2500, mode: `MaxEncodedLen`) + /// Storage: `ImOnline::AuthoredBlocks` (r:1 w:0) + /// Proof: `ImOnline::AuthoredBlocks` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) /// The range of component `k` is `[1, 1000]`. fn validate_unsigned_and_then_heartbeat(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `295 + k * (32 ±0)` + // Measured: `327 + k * (32 ±0)` // Estimated: `321487 + k * (1761 ±0)` - // Minimum execution time: 80_568_000 picoseconds. - Weight::from_parts(95_175_595, 321487) - // Standard Error: 627 - .saturating_add(Weight::from_parts(39_094, 0).saturating_mul(k.into())) + // Minimum execution time: 65_515_000 picoseconds. + Weight::from_parts(74_765_329, 321487) + // Standard Error: 500 + .saturating_add(Weight::from_parts(39_171, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1761).saturating_mul(k.into())) diff --git a/substrate/frame/indices/Cargo.toml b/substrate/frame/indices/Cargo.toml index 4f0c780c6af35d4273bc1824830a663c54220fd3..f810ea36a707fa0c47a8fbb51c523f9ad65fea90 100644 --- a/substrate/frame/indices/Cargo.toml +++ b/substrate/frame/indices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-indices" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/indices/src/mock.rs b/substrate/frame/indices/src/mock.rs index 7ea524a31670a9d553a5adf5371a1ffcd8a2c6a5..5cf82305178d419d0542d283db914eb8dc481aa7 100644 --- a/substrate/frame/indices/src/mock.rs +++ b/substrate/frame/indices/src/mock.rs @@ -79,7 +79,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl Config for Test { diff --git a/substrate/frame/indices/src/weights.rs b/substrate/frame/indices/src/weights.rs index d081cc738b1dbe6c1991b30ccb454307e4ca19ab..6b571164eb69e3ac25732a19067ee445ead52514 100644 --- a/substrate/frame/indices/src/weights.rs +++ b/substrate/frame/indices/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_indices +//! Autogenerated weights for `pallet_indices` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/indices/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/indices/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_indices. +/// Weight functions needed for `pallet_indices`. pub trait WeightInfo { fn claim() -> Weight; fn transfer() -> Weight; @@ -59,128 +58,128 @@ pub trait WeightInfo { fn freeze() -> Weight; } -/// Weights for pallet_indices using the Substrate node and recommended hardware. +/// Weights for `pallet_indices` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn claim() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3534` - // Minimum execution time: 25_491_000 picoseconds. - Weight::from_parts(26_456_000, 3534) + // Minimum execution time: 20_825_000 picoseconds. + Weight::from_parts(21_507_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` // Estimated: `3593` - // Minimum execution time: 38_027_000 picoseconds. - Weight::from_parts(38_749_000, 3593) + // Minimum execution time: 31_091_000 picoseconds. + Weight::from_parts(31_923_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn free() -> Weight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 26_652_000 picoseconds. - Weight::from_parts(27_273_000, 3534) + // Minimum execution time: 21_832_000 picoseconds. + Weight::from_parts(22_436_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` // Estimated: `3593` - // Minimum execution time: 29_464_000 picoseconds. - Weight::from_parts(30_959_000, 3593) + // Minimum execution time: 23_876_000 picoseconds. + Weight::from_parts(24_954_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 29_015_000 picoseconds. - Weight::from_parts(29_714_000, 3534) + // Minimum execution time: 22_954_000 picoseconds. + Weight::from_parts(23_792_000, 3534) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn claim() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3534` - // Minimum execution time: 25_491_000 picoseconds. - Weight::from_parts(26_456_000, 3534) + // Minimum execution time: 20_825_000 picoseconds. + Weight::from_parts(21_507_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` // Estimated: `3593` - // Minimum execution time: 38_027_000 picoseconds. - Weight::from_parts(38_749_000, 3593) + // Minimum execution time: 31_091_000 picoseconds. + Weight::from_parts(31_923_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn free() -> Weight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 26_652_000 picoseconds. - Weight::from_parts(27_273_000, 3534) + // Minimum execution time: 21_832_000 picoseconds. + Weight::from_parts(22_436_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `275` // Estimated: `3593` - // Minimum execution time: 29_464_000 picoseconds. - Weight::from_parts(30_959_000, 3593) + // Minimum execution time: 23_876_000 picoseconds. + Weight::from_parts(24_954_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn freeze() -> Weight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `3534` - // Minimum execution time: 29_015_000 picoseconds. - Weight::from_parts(29_714_000, 3534) + // Minimum execution time: 22_954_000 picoseconds. + Weight::from_parts(23_792_000, 3534) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/insecure-randomness-collective-flip/Cargo.toml b/substrate/frame/insecure-randomness-collective-flip/Cargo.toml index fb1447d10457a94e255bacc4af2d73c0ca084c73..f26bfa95bfd0628f5f5e4a81979d3611102e0847 100644 --- a/substrate/frame/insecure-randomness-collective-flip/Cargo.toml +++ b/substrate/frame/insecure-randomness-collective-flip/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-insecure-randomness-collective-flip" -version = "4.0.0-dev" +version = "16.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/insecure-randomness-collective-flip/src/lib.rs b/substrate/frame/insecure-randomness-collective-flip/src/lib.rs index 00f1055f6f271c9ef21b914a499c652161f551f1..04f8cda6541dd37685b86b7a25f7b84d5880de45 100644 --- a/substrate/frame/insecure-randomness-collective-flip/src/lib.rs +++ b/substrate/frame/insecure-randomness-collective-flip/src/lib.rs @@ -163,14 +163,11 @@ mod tests { use crate as pallet_insecure_randomness_collective_flip; use sp_core::H256; - use sp_runtime::{ - traits::{BlakeTwo256, Header as _, IdentityLookup}, - BuildStorage, - }; + use sp_runtime::{traits::Header as _, BuildStorage}; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU32, ConstU64, OnInitialize, Randomness}, + traits::{OnInitialize, Randomness}, }; use frame_system::limits; @@ -191,29 +188,7 @@ mod tests { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = BlockLength; - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_insecure_randomness_collective_flip::Config for Test {} diff --git a/substrate/frame/lottery/Cargo.toml b/substrate/frame/lottery/Cargo.toml index 49f84b04b2578094e72dcb090b32c735a873d769..3930ff32fc915dd87c12ad59d0a22b7dce1764b8 100644 --- a/substrate/frame/lottery/Cargo.toml +++ b/substrate/frame/lottery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-lottery" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/lottery/src/benchmarking.rs b/substrate/frame/lottery/src/benchmarking.rs index 1510d250dbeaf6814a35f1262f08ba727b340ee2..123b425b976f3b5b2842e748498513611dca5bc2 100644 --- a/substrate/frame/lottery/src/benchmarking.rs +++ b/substrate/frame/lottery/src/benchmarking.rs @@ -23,7 +23,6 @@ use super::*; use crate::Pallet as Lottery; use frame_benchmarking::{ - impl_benchmark_test_suite, v1::{account, whitelisted_caller, BenchmarkError}, v2::*, }; diff --git a/substrate/frame/lottery/src/mock.rs b/substrate/frame/lottery/src/mock.rs index 884f003aadafe7ceb35edb8394674380643523c4..563ce7202ec39c5b16af1efc58c0cde077400db7 100644 --- a/substrate/frame/lottery/src/mock.rs +++ b/substrate/frame/lottery/src/mock.rs @@ -26,11 +26,7 @@ use frame_support::{ }; use frame_support_test::TestRandomness; use frame_system::EnsureRoot; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, Perbill, -}; +use sp_runtime::{BuildStorage, Perbill}; type Block = frame_system::mocking::MockBlock; @@ -49,29 +45,8 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -88,7 +63,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { diff --git a/substrate/frame/lottery/src/weights.rs b/substrate/frame/lottery/src/weights.rs index 3b4e562375345befa93f431145a2aff776ec7517..7e56cdf90db5c7844dc8183b0a14381a910fdd24 100644 --- a/substrate/frame/lottery/src/weights.rs +++ b/substrate/frame/lottery/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_lottery +//! Autogenerated weights for `pallet_lottery` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/lottery/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/lottery/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_lottery. +/// Weight functions needed for `pallet_lottery`. pub trait WeightInfo { fn buy_ticket() -> Weight; fn set_calls(n: u32, ) -> Weight; @@ -60,214 +59,222 @@ pub trait WeightInfo { fn on_initialize_repeat() -> Weight; } -/// Weights for pallet_lottery using the Substrate node and recommended hardware. +/// Weights for `pallet_lottery` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Lottery Lottery (r:1 w:0) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: Lottery CallIndices (r:1 w:0) - /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) - /// Storage: Lottery TicketsCount (r:1 w:1) - /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Lottery Participants (r:1 w:1) - /// Proof: Lottery Participants (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: Lottery LotteryIndex (r:1 w:0) - /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Lottery Tickets (r:0 w:1) - /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Lottery` (r:1 w:0) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `Lottery::CallIndices` (r:1 w:0) + /// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) + /// Storage: `Lottery::TicketsCount` (r:1 w:1) + /// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Participants` (r:1 w:1) + /// Proof: `Lottery::Participants` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Lottery::LotteryIndex` (r:1 w:0) + /// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Tickets` (r:0 w:1) + /// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) fn buy_ticket() -> Weight { // Proof Size summary in bytes: - // Measured: `452` - // Estimated: `3593` - // Minimum execution time: 60_298_000 picoseconds. - Weight::from_parts(62_058_000, 3593) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `492` + // Estimated: `3997` + // Minimum execution time: 57_891_000 picoseconds. + Weight::from_parts(59_508_000, 3997) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Lottery CallIndices (r:0 w:1) - /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) + /// Storage: `Lottery::CallIndices` (r:0 w:1) + /// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 10]`. fn set_calls(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_291_000 picoseconds. - Weight::from_parts(8_178_186, 0) - // Standard Error: 3_048 - .saturating_add(Weight::from_parts(330_871, 0).saturating_mul(n.into())) + // Minimum execution time: 5_026_000 picoseconds. + Weight::from_parts(5_854_666, 0) + // Standard Error: 3_233 + .saturating_add(Weight::from_parts(334_818, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: Lottery LotteryIndex (r:1 w:1) - /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `Lottery::LotteryIndex` (r:1 w:1) + /// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn start_lottery() -> Weight { // Proof Size summary in bytes: - // Measured: `161` + // Measured: `194` // Estimated: `3593` - // Minimum execution time: 36_741_000 picoseconds. - Weight::from_parts(38_288_000, 3593) + // Minimum execution time: 26_216_000 picoseconds. + Weight::from_parts(27_216_000, 3593) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) fn stop_repeat() -> Weight { // Proof Size summary in bytes: - // Measured: `219` + // Measured: `252` // Estimated: `1514` - // Minimum execution time: 7_270_000 picoseconds. - Weight::from_parts(7_578_000, 1514) + // Minimum execution time: 6_208_000 picoseconds. + Weight::from_parts(6_427_000, 1514) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Lottery TicketsCount (r:1 w:1) - /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Lottery Tickets (r:1 w:0) - /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Lottery::TicketsCount` (r:1 w:1) + /// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Tickets` (r:1 w:0) + /// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) fn on_initialize_end() -> Weight { // Proof Size summary in bytes: - // Measured: `558` + // Measured: `591` // Estimated: `6196` - // Minimum execution time: 76_611_000 picoseconds. - Weight::from_parts(78_107_000, 6196) + // Minimum execution time: 58_660_000 picoseconds. + Weight::from_parts(59_358_000, 6196) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Lottery TicketsCount (r:1 w:1) - /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Lottery Tickets (r:1 w:0) - /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) - /// Storage: Lottery LotteryIndex (r:1 w:1) - /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Lottery::TicketsCount` (r:1 w:1) + /// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Tickets` (r:1 w:0) + /// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `Lottery::LotteryIndex` (r:1 w:1) + /// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn on_initialize_repeat() -> Weight { // Proof Size summary in bytes: - // Measured: `558` + // Measured: `591` // Estimated: `6196` - // Minimum execution time: 78_731_000 picoseconds. - Weight::from_parts(80_248_000, 6196) + // Minimum execution time: 59_376_000 picoseconds. + Weight::from_parts(60_598_000, 6196) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Lottery Lottery (r:1 w:0) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: Lottery CallIndices (r:1 w:0) - /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) - /// Storage: Lottery TicketsCount (r:1 w:1) - /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Lottery Participants (r:1 w:1) - /// Proof: Lottery Participants (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: Lottery LotteryIndex (r:1 w:0) - /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Lottery Tickets (r:0 w:1) - /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Lottery` (r:1 w:0) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `Lottery::CallIndices` (r:1 w:0) + /// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) + /// Storage: `Lottery::TicketsCount` (r:1 w:1) + /// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Participants` (r:1 w:1) + /// Proof: `Lottery::Participants` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Lottery::LotteryIndex` (r:1 w:0) + /// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Tickets` (r:0 w:1) + /// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) fn buy_ticket() -> Weight { // Proof Size summary in bytes: - // Measured: `452` - // Estimated: `3593` - // Minimum execution time: 60_298_000 picoseconds. - Weight::from_parts(62_058_000, 3593) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `492` + // Estimated: `3997` + // Minimum execution time: 57_891_000 picoseconds. + Weight::from_parts(59_508_000, 3997) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Lottery CallIndices (r:0 w:1) - /// Proof: Lottery CallIndices (max_values: Some(1), max_size: Some(21), added: 516, mode: MaxEncodedLen) + /// Storage: `Lottery::CallIndices` (r:0 w:1) + /// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 10]`. fn set_calls(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_291_000 picoseconds. - Weight::from_parts(8_178_186, 0) - // Standard Error: 3_048 - .saturating_add(Weight::from_parts(330_871, 0).saturating_mul(n.into())) + // Minimum execution time: 5_026_000 picoseconds. + Weight::from_parts(5_854_666, 0) + // Standard Error: 3_233 + .saturating_add(Weight::from_parts(334_818, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: Lottery LotteryIndex (r:1 w:1) - /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `Lottery::LotteryIndex` (r:1 w:1) + /// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn start_lottery() -> Weight { // Proof Size summary in bytes: - // Measured: `161` + // Measured: `194` // Estimated: `3593` - // Minimum execution time: 36_741_000 picoseconds. - Weight::from_parts(38_288_000, 3593) + // Minimum execution time: 26_216_000 picoseconds. + Weight::from_parts(27_216_000, 3593) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) fn stop_repeat() -> Weight { // Proof Size summary in bytes: - // Measured: `219` + // Measured: `252` // Estimated: `1514` - // Minimum execution time: 7_270_000 picoseconds. - Weight::from_parts(7_578_000, 1514) + // Minimum execution time: 6_208_000 picoseconds. + Weight::from_parts(6_427_000, 1514) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Lottery TicketsCount (r:1 w:1) - /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Lottery Tickets (r:1 w:0) - /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Lottery::TicketsCount` (r:1 w:1) + /// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Tickets` (r:1 w:0) + /// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) fn on_initialize_end() -> Weight { // Proof Size summary in bytes: - // Measured: `558` + // Measured: `591` // Estimated: `6196` - // Minimum execution time: 76_611_000 picoseconds. - Weight::from_parts(78_107_000, 6196) + // Minimum execution time: 58_660_000 picoseconds. + Weight::from_parts(59_358_000, 6196) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: RandomnessCollectiveFlip RandomMaterial (r:1 w:0) - /// Proof: RandomnessCollectiveFlip RandomMaterial (max_values: Some(1), max_size: Some(2594), added: 3089, mode: MaxEncodedLen) - /// Storage: Lottery Lottery (r:1 w:1) - /// Proof: Lottery Lottery (max_values: Some(1), max_size: Some(29), added: 524, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Lottery TicketsCount (r:1 w:1) - /// Proof: Lottery TicketsCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Lottery Tickets (r:1 w:0) - /// Proof: Lottery Tickets (max_values: None, max_size: Some(44), added: 2519, mode: MaxEncodedLen) - /// Storage: Lottery LotteryIndex (r:1 w:1) - /// Proof: Lottery LotteryIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Lottery` (r:1 w:1) + /// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Lottery::TicketsCount` (r:1 w:1) + /// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Lottery::Tickets` (r:1 w:0) + /// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `Lottery::LotteryIndex` (r:1 w:1) + /// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn on_initialize_repeat() -> Weight { // Proof Size summary in bytes: - // Measured: `558` + // Measured: `591` // Estimated: `6196` - // Minimum execution time: 78_731_000 picoseconds. - Weight::from_parts(80_248_000, 6196) + // Minimum execution time: 59_376_000 picoseconds. + Weight::from_parts(60_598_000, 6196) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } diff --git a/substrate/frame/membership/Cargo.toml b/substrate/frame/membership/Cargo.toml index c4c94e202a4db090e8988afb09eafa9a1ed2da14..64214670292739395dc38a024085dec52868d275 100644 --- a/substrate/frame/membership/Cargo.toml +++ b/substrate/frame/membership/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-membership" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/membership/src/lib.rs b/substrate/frame/membership/src/lib.rs index 0e03c097d1233e5b253fbf3be86faf44b065f145..f4ac697130de87ea48fdb6be522289c6368d10c8 100644 --- a/substrate/frame/membership/src/lib.rs +++ b/substrate/frame/membership/src/lib.rs @@ -27,7 +27,7 @@ use frame_support::{ traits::{ChangeMembers, Contains, Get, InitializeMembers, SortedMembers}, BoundedVec, }; -use sp_runtime::traits::StaticLookup; +use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; use sp_std::prelude::*; pub mod migrations; @@ -46,7 +46,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] @@ -163,12 +163,16 @@ pub mod pallet { /// /// May only be called from `T::AddOrigin`. #[pallet::call_index(0)] - #[pallet::weight({50_000_000})] - pub fn add_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::add_member(T::MaxMembers::get()))] + pub fn add_member( + origin: OriginFor, + who: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { T::AddOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; let mut members = >::get(); + let init_length = members.len(); let location = members.binary_search(&who).err().ok_or(Error::::AlreadyMember)?; members .try_insert(location, who.clone()) @@ -179,19 +183,24 @@ pub mod pallet { T::MembershipChanged::change_members_sorted(&[who], &[], &members[..]); Self::deposit_event(Event::MemberAdded); - Ok(()) + + Ok(Some(T::WeightInfo::add_member(init_length as u32)).into()) } /// Remove a member `who` from the set. /// /// May only be called from `T::RemoveOrigin`. #[pallet::call_index(1)] - #[pallet::weight({50_000_000})] - pub fn remove_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::remove_member(T::MaxMembers::get()))] + pub fn remove_member( + origin: OriginFor, + who: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { T::RemoveOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; let mut members = >::get(); + let init_length = members.len(); let location = members.binary_search(&who).ok().ok_or(Error::::NotMember)?; members.remove(location); @@ -201,7 +210,7 @@ pub mod pallet { Self::rejig_prime(&members); Self::deposit_event(Event::MemberRemoved); - Ok(()) + Ok(Some(T::WeightInfo::remove_member(init_length as u32)).into()) } /// Swap out one member `remove` for another `add`. @@ -210,18 +219,18 @@ pub mod pallet { /// /// Prime membership is *not* passed from `remove` to `add`, if extant. #[pallet::call_index(2)] - #[pallet::weight({50_000_000})] + #[pallet::weight(T::WeightInfo::swap_member(T::MaxMembers::get()))] pub fn swap_member( origin: OriginFor, remove: AccountIdLookupOf, add: AccountIdLookupOf, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { T::SwapOrigin::ensure_origin(origin)?; let remove = T::Lookup::lookup(remove)?; let add = T::Lookup::lookup(add)?; if remove == add { - return Ok(()) + return Ok(().into()); } let mut members = >::get(); @@ -236,7 +245,7 @@ pub mod pallet { Self::rejig_prime(&members); Self::deposit_event(Event::MembersSwapped); - Ok(()) + Ok(Some(T::WeightInfo::swap_member(members.len() as u32)).into()) } /// Change the membership to a new set, disregarding the existing membership. Be nice and @@ -244,7 +253,7 @@ pub mod pallet { /// /// May only be called from `T::ResetOrigin`. #[pallet::call_index(3)] - #[pallet::weight({50_000_000})] + #[pallet::weight(T::WeightInfo::reset_members(members.len().unique_saturated_into()))] pub fn reset_members(origin: OriginFor, members: Vec) -> DispatchResult { T::ResetOrigin::ensure_origin(origin)?; @@ -267,56 +276,65 @@ pub mod pallet { /// /// Prime membership is passed from the origin account to `new`, if extant. #[pallet::call_index(4)] - #[pallet::weight({50_000_000})] - pub fn change_key(origin: OriginFor, new: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::change_key(T::MaxMembers::get()))] + pub fn change_key( + origin: OriginFor, + new: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { let remove = ensure_signed(origin)?; let new = T::Lookup::lookup(new)?; - if remove != new { - let mut members = >::get(); - let location = - members.binary_search(&remove).ok().ok_or(Error::::NotMember)?; - let _ = members.binary_search(&new).err().ok_or(Error::::AlreadyMember)?; - members[location] = new.clone(); - members.sort(); - - >::put(&members); - - T::MembershipChanged::change_members_sorted( - &[new.clone()], - &[remove.clone()], - &members[..], - ); - - if Prime::::get() == Some(remove) { - Prime::::put(&new); - T::MembershipChanged::set_prime(Some(new)); - } + if remove == new { + return Ok(().into()); + } + + let mut members = >::get(); + let members_length = members.len() as u32; + let location = members.binary_search(&remove).ok().ok_or(Error::::NotMember)?; + let _ = members.binary_search(&new).err().ok_or(Error::::AlreadyMember)?; + members[location] = new.clone(); + members.sort(); + + >::put(&members); + + T::MembershipChanged::change_members_sorted( + &[new.clone()], + &[remove.clone()], + &members[..], + ); + + if Prime::::get() == Some(remove) { + Prime::::put(&new); + T::MembershipChanged::set_prime(Some(new)); } Self::deposit_event(Event::KeyChanged); - Ok(()) + Ok(Some(T::WeightInfo::change_key(members_length)).into()) } /// Set the prime member. Must be a current member. /// /// May only be called from `T::PrimeOrigin`. #[pallet::call_index(5)] - #[pallet::weight({50_000_000})] - pub fn set_prime(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { + #[pallet::weight(T::WeightInfo::set_prime(T::MaxMembers::get()))] + pub fn set_prime( + origin: OriginFor, + who: AccountIdLookupOf, + ) -> DispatchResultWithPostInfo { T::PrimeOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; - Self::members().binary_search(&who).ok().ok_or(Error::::NotMember)?; + let members = Self::members(); + members.binary_search(&who).ok().ok_or(Error::::NotMember)?; Prime::::put(&who); T::MembershipChanged::set_prime(Some(who)); - Ok(()) + Ok(Some(T::WeightInfo::set_prime(members.len() as u32)).into()) } /// Remove the prime member if it exists. /// /// May only be called from `T::PrimeOrigin`. #[pallet::call_index(6)] - #[pallet::weight({50_000_000})] + #[pallet::weight(T::WeightInfo::clear_prime())] pub fn clear_prime(origin: OriginFor) -> DispatchResult { T::PrimeOrigin::ensure_origin(origin)?; Prime::::kill(); @@ -442,7 +460,7 @@ mod benchmark { } // er keep the prime common between incoming and outgoing to make sure it is rejigged. - reset_member { + reset_members { let m in 1 .. T::MaxMembers::get(); let members = (1..m+1).map(|i| account("member", i, SEED)).collect::>(); @@ -500,8 +518,7 @@ mod benchmark { } clear_prime { - let m in 1 .. T::MaxMembers::get(); - let members = (0..m).map(|i| account("member", i, SEED)).collect::>(); + let members = (0..T::MaxMembers::get()).map(|i| account("member", i, SEED)).collect::>(); let prime = members.last().cloned().unwrap(); set_members::(members, None); }: { @@ -523,16 +540,12 @@ mod tests { use super::*; use crate as pallet_membership; - use sp_core::H256; - use sp_runtime::{ - bounded_vec, - traits::{BadOrigin, BlakeTwo256, IdentityLookup}, - BuildStorage, - }; + use sp_runtime::{bounded_vec, traits::BadOrigin, BuildStorage}; use frame_support::{ - assert_noop, assert_ok, derive_impl, ord_parameter_types, parameter_types, - traits::{ConstU32, ConstU64, StorageVersion}, + assert_noop, assert_ok, assert_storage_noop, derive_impl, ord_parameter_types, + parameter_types, + traits::{ConstU32, StorageVersion}, }; use frame_system::EnsureSignedBy; @@ -553,29 +566,7 @@ mod tests { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } ord_parameter_types! { pub const One: u64 = 1; @@ -743,6 +734,17 @@ mod tests { }); } + #[test] + fn swap_member_with_identical_arguments_changes_nothing() { + new_test_ext().execute_with(|| { + assert_storage_noop!(assert_ok!(Membership::swap_member( + RuntimeOrigin::signed(3), + 10, + 10 + ))); + }); + } + #[test] fn change_key_works() { new_test_ext().execute_with(|| { @@ -772,6 +774,13 @@ mod tests { }); } + #[test] + fn change_key_with_same_caller_as_argument_changes_nothing() { + new_test_ext().execute_with(|| { + assert_storage_noop!(assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 10))); + }); + } + #[test] fn reset_members_works() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/membership/src/weights.rs b/substrate/frame/membership/src/weights.rs index 18ea7fcb315a3134747743f1a913856d084e5ad6..f21867d687079a7dd5396320e621d26ee2af8996 100644 --- a/substrate/frame/membership/src/weights.rs +++ b/substrate/frame/membership/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_membership +//! Autogenerated weights for `pallet_membership` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/membership/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/membership/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,316 +49,310 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_membership. +/// Weight functions needed for `pallet_membership`. pub trait WeightInfo { fn add_member(m: u32, ) -> Weight; fn remove_member(m: u32, ) -> Weight; fn swap_member(m: u32, ) -> Weight; - fn reset_member(m: u32, ) -> Weight; + fn reset_members(m: u32, ) -> Weight; fn change_key(m: u32, ) -> Weight; fn set_prime(m: u32, ) -> Weight; - fn clear_prime(m: u32, ) -> Weight; + fn clear_prime() -> Weight; } -/// Weights for pallet_membership using the Substrate node and recommended hardware. +/// Weights for `pallet_membership` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 99]`. fn add_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `208 + m * (64 ±0)` + // Measured: `207 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 17_040_000 picoseconds. - Weight::from_parts(18_344_571, 4687) - // Standard Error: 847 - .saturating_add(Weight::from_parts(50_842, 0).saturating_mul(m.into())) + // Minimum execution time: 12_126_000 picoseconds. + Weight::from_parts(13_085_583, 4687) + // Standard Error: 659 + .saturating_add(Weight::from_parts(36_103, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:0) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:0) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[2, 100]`. fn remove_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 20_088_000 picoseconds. - Weight::from_parts(21_271_384, 4687) - // Standard Error: 786 - .saturating_add(Weight::from_parts(44_806, 0).saturating_mul(m.into())) + // Minimum execution time: 14_571_000 picoseconds. + Weight::from_parts(15_532_232, 4687) + // Standard Error: 531 + .saturating_add(Weight::from_parts(35_757, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:0) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:0) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[2, 100]`. fn swap_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 20_308_000 picoseconds. - Weight::from_parts(21_469_843, 4687) - // Standard Error: 782 - .saturating_add(Weight::from_parts(56_893, 0).saturating_mul(m.into())) + // Minimum execution time: 14_833_000 picoseconds. + Weight::from_parts(15_657_084, 4687) + // Standard Error: 650 + .saturating_add(Weight::from_parts(44_467, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:0) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:0) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. - fn reset_member(m: u32, ) -> Weight { + fn reset_members(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 19_464_000 picoseconds. - Weight::from_parts(21_223_702, 4687) - // Standard Error: 1_068 - .saturating_add(Weight::from_parts(165_438, 0).saturating_mul(m.into())) + // Minimum execution time: 14_629_000 picoseconds. + Weight::from_parts(15_578_203, 4687) + // Standard Error: 910 + .saturating_add(Weight::from_parts(145_101, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:1) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:1) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. fn change_key(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 20_965_000 picoseconds. - Weight::from_parts(22_551_007, 4687) - // Standard Error: 860 - .saturating_add(Weight::from_parts(52_397, 0).saturating_mul(m.into())) + // Minimum execution time: 15_218_000 picoseconds. + Weight::from_parts(16_388_690, 4687) + // Standard Error: 626 + .saturating_add(Weight::from_parts(46_204, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:0) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalMembership Prime (r:0 w:1) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:0) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalMembership::Prime` (r:0 w:1) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. fn set_prime(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32 + m * (32 ±0)` + // Measured: `31 + m * (32 ±0)` // Estimated: `4687 + m * (32 ±0)` - // Minimum execution time: 7_481_000 picoseconds. - Weight::from_parts(7_959_053, 4687) - // Standard Error: 364 - .saturating_add(Weight::from_parts(18_653, 0).saturating_mul(m.into())) + // Minimum execution time: 5_954_000 picoseconds. + Weight::from_parts(6_544_638, 4687) + // Standard Error: 346 + .saturating_add(Weight::from_parts(17_638, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Prime (r:0 w:1) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `m` is `[1, 100]`. - fn clear_prime(m: u32, ) -> Weight { + /// Storage: `TechnicalMembership::Prime` (r:0 w:1) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn clear_prime() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_373_000 picoseconds. - Weight::from_parts(3_750_452, 0) - // Standard Error: 142 - .saturating_add(Weight::from_parts(505, 0).saturating_mul(m.into())) + // Minimum execution time: 2_569_000 picoseconds. + Weight::from_parts(2_776_000, 0) .saturating_add(T::DbWeight::get().writes(2_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 99]`. fn add_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `208 + m * (64 ±0)` + // Measured: `207 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 17_040_000 picoseconds. - Weight::from_parts(18_344_571, 4687) - // Standard Error: 847 - .saturating_add(Weight::from_parts(50_842, 0).saturating_mul(m.into())) + // Minimum execution time: 12_126_000 picoseconds. + Weight::from_parts(13_085_583, 4687) + // Standard Error: 659 + .saturating_add(Weight::from_parts(36_103, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:0) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:0) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[2, 100]`. fn remove_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 20_088_000 picoseconds. - Weight::from_parts(21_271_384, 4687) - // Standard Error: 786 - .saturating_add(Weight::from_parts(44_806, 0).saturating_mul(m.into())) + // Minimum execution time: 14_571_000 picoseconds. + Weight::from_parts(15_532_232, 4687) + // Standard Error: 531 + .saturating_add(Weight::from_parts(35_757, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:0) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:0) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[2, 100]`. fn swap_member(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 20_308_000 picoseconds. - Weight::from_parts(21_469_843, 4687) - // Standard Error: 782 - .saturating_add(Weight::from_parts(56_893, 0).saturating_mul(m.into())) + // Minimum execution time: 14_833_000 picoseconds. + Weight::from_parts(15_657_084, 4687) + // Standard Error: 650 + .saturating_add(Weight::from_parts(44_467, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:0) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:0) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. - fn reset_member(m: u32, ) -> Weight { + fn reset_members(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 19_464_000 picoseconds. - Weight::from_parts(21_223_702, 4687) - // Standard Error: 1_068 - .saturating_add(Weight::from_parts(165_438, 0).saturating_mul(m.into())) + // Minimum execution time: 14_629_000 picoseconds. + Weight::from_parts(15_578_203, 4687) + // Standard Error: 910 + .saturating_add(Weight::from_parts(145_101, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:1) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Proposals (r:1 w:0) - /// Proof Skipped: TechnicalCommittee Proposals (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalMembership Prime (r:1 w:1) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Members (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:1) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Proposals` (r:1 w:0) + /// Proof: `TechnicalCommittee::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalMembership::Prime` (r:1 w:1) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Members` (r:0 w:1) + /// Proof: `TechnicalCommittee::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. fn change_key(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `312 + m * (64 ±0)` + // Measured: `311 + m * (64 ±0)` // Estimated: `4687 + m * (64 ±0)` - // Minimum execution time: 20_965_000 picoseconds. - Weight::from_parts(22_551_007, 4687) - // Standard Error: 860 - .saturating_add(Weight::from_parts(52_397, 0).saturating_mul(m.into())) + // Minimum execution time: 15_218_000 picoseconds. + Weight::from_parts(16_388_690, 4687) + // Standard Error: 626 + .saturating_add(Weight::from_parts(46_204, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Members (r:1 w:0) - /// Proof: TechnicalMembership Members (max_values: Some(1), max_size: Some(3202), added: 3697, mode: MaxEncodedLen) - /// Storage: TechnicalMembership Prime (r:0 w:1) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TechnicalMembership::Members` (r:1 w:0) + /// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`) + /// Storage: `TechnicalMembership::Prime` (r:0 w:1) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `m` is `[1, 100]`. fn set_prime(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32 + m * (32 ±0)` + // Measured: `31 + m * (32 ±0)` // Estimated: `4687 + m * (32 ±0)` - // Minimum execution time: 7_481_000 picoseconds. - Weight::from_parts(7_959_053, 4687) - // Standard Error: 364 - .saturating_add(Weight::from_parts(18_653, 0).saturating_mul(m.into())) + // Minimum execution time: 5_954_000 picoseconds. + Weight::from_parts(6_544_638, 4687) + // Standard Error: 346 + .saturating_add(Weight::from_parts(17_638, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } - /// Storage: TechnicalMembership Prime (r:0 w:1) - /// Proof: TechnicalMembership Prime (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TechnicalCommittee Prime (r:0 w:1) - /// Proof Skipped: TechnicalCommittee Prime (max_values: Some(1), max_size: None, mode: Measured) - /// The range of component `m` is `[1, 100]`. - fn clear_prime(m: u32, ) -> Weight { + /// Storage: `TechnicalMembership::Prime` (r:0 w:1) + /// Proof: `TechnicalMembership::Prime` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TechnicalCommittee::Prime` (r:0 w:1) + /// Proof: `TechnicalCommittee::Prime` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn clear_prime() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_373_000 picoseconds. - Weight::from_parts(3_750_452, 0) - // Standard Error: 142 - .saturating_add(Weight::from_parts(505, 0).saturating_mul(m.into())) + // Minimum execution time: 2_569_000 picoseconds. + Weight::from_parts(2_776_000, 0) .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/substrate/frame/merkle-mountain-range/Cargo.toml b/substrate/frame/merkle-mountain-range/Cargo.toml index eaa17d88e9959ee8e51f42f55b46adc45187b04b..d623e25cec2613d590562e5b17d077a10a833357 100644 --- a/substrate/frame/merkle-mountain-range/Cargo.toml +++ b/substrate/frame/merkle-mountain-range/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-mmr" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/merkle-mountain-range/src/mmr/mod.rs b/substrate/frame/merkle-mountain-range/src/mmr/mod.rs index 536faa68e4e9f05f8d11941bb5cd81fe48e1948f..93fefe910e45df386d0464b455e6f55c44aaa8e0 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/mod.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/mod.rs @@ -30,7 +30,7 @@ pub type NodeOf = Node<>::Hashing, L>; pub type Node = DataOrHash; /// Default Merging & Hashing behavior for MMR. -pub struct Hasher(sp_std::marker::PhantomData<(H, L)>); +pub struct Hasher(core::marker::PhantomData<(H, L)>); impl mmr_lib::Merge for Hasher { type Item = Node; diff --git a/substrate/frame/message-queue/Cargo.toml b/substrate/frame/message-queue/Cargo.toml index e3ab370727ea865fb7cf03884d9f99d392089cc4..8d9da7df39ea58ad1f91d45e5db1bc026031ed89 100644 --- a/substrate/frame/message-queue/Cargo.toml +++ b/substrate/frame/message-queue/Cargo.toml @@ -2,7 +2,7 @@ authors.workspace = true edition.workspace = true name = "pallet-message-queue" -version = "7.0.0-dev" +version = "31.0.0" license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true @@ -14,8 +14,8 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +log = { workspace = true } environmental = { version = "1.1.4", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } @@ -30,6 +30,7 @@ frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } [dev-dependencies] +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } sp-tracing = { path = "../../primitives/tracing" } rand = "0.8.5" rand_distr = "0.4.3" diff --git a/substrate/frame/message-queue/src/integration_test.rs b/substrate/frame/message-queue/src/integration_test.rs index aa6f019d650df0f2d97be7b3584dba706eb686f7..ce8eb80805ab0401bf7f4ff4e2448983333c7b36 100644 --- a/substrate/frame/message-queue/src/integration_test.rs +++ b/substrate/frame/message-queue/src/integration_test.rs @@ -37,14 +37,9 @@ use crate::{ }; use crate as pallet_message_queue; -use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU32, ConstU64}, -}; +use frame_support::{derive_impl, parameter_types}; use rand::{rngs::StdRng, Rng, SeedableRng}; use rand_distr::Pareto; -use sp_core::H256; -use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use std::collections::{BTreeMap, BTreeSet}; type Block = frame_system::mocking::MockBlock; @@ -59,29 +54,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } parameter_types! { @@ -357,6 +330,11 @@ fn process_some_messages(num_msgs: u32) { ServiceWeight::set(Some(weight)); let consumed = next_block(); + for origin in BookStateFor::::iter_keys() { + let fp = MessageQueue::footprint(origin); + assert_eq!(fp.pages, fp.ready_pages); + } + assert_eq!(consumed, weight, "\n{}", MessageQueue::debug_info()); assert_eq!(NumMessagesProcessed::take(), num_msgs as usize); } diff --git a/substrate/frame/message-queue/src/lib.rs b/substrate/frame/message-queue/src/lib.rs index 07eb0041985342522a113339769b98f834ef4a17..61028057394f6f3bedda173288fa1c9aea3e9e9a 100644 --- a/substrate/frame/message-queue/src/lib.rs +++ b/substrate/frame/message-queue/src/lib.rs @@ -208,8 +208,9 @@ use frame_support::{ defensive, pallet_prelude::*, traits::{ - Defensive, DefensiveTruncateFrom, EnqueueMessage, ExecuteOverweightError, Footprint, - ProcessMessage, ProcessMessageError, QueueFootprint, QueuePausedQuery, ServiceQueues, + Defensive, DefensiveSaturating, DefensiveTruncateFrom, EnqueueMessage, + ExecuteOverweightError, Footprint, ProcessMessage, ProcessMessageError, QueueFootprint, + QueuePausedQuery, ServiceQueues, }, BoundedSlice, CloneNoBound, DefaultNoBound, }; @@ -442,6 +443,7 @@ impl From> for QueueFootprint { fn from(book: BookState) -> Self { QueueFootprint { pages: book.count, + ready_pages: book.end.defensive_saturating_sub(book.begin), storage: Footprint { count: book.message_count, size: book.size }, } } @@ -1281,6 +1283,9 @@ impl Pallet { ensure!(book.message_count < 1 << 30, "Likely overflow or corruption"); ensure!(book.size < 1 << 30, "Likely overflow or corruption"); ensure!(book.count < 1 << 30, "Likely overflow or corruption"); + + let fp: QueueFootprint = book.into(); + ensure!(fp.ready_pages <= fp.pages, "There cannot be more ready than total pages"); } //loop around this origin diff --git a/substrate/frame/message-queue/src/mock.rs b/substrate/frame/message-queue/src/mock.rs index f1067ed961e8adfd0b206a480a6d76bda32428a2..d176a981ca8ee2f6911176b1a25907c7bc197566 100644 --- a/substrate/frame/message-queue/src/mock.rs +++ b/substrate/frame/message-queue/src/mock.rs @@ -23,15 +23,8 @@ pub use super::mock_helpers::*; use super::*; use crate as pallet_message_queue; -use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU32, ConstU64}, -}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use frame_support::{derive_impl, parameter_types}; +use sp_runtime::BuildStorage; use sp_std::collections::btree_map::BTreeMap; type Block = frame_system::mocking::MockBlock; @@ -46,29 +39,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } parameter_types! { pub const HeapSize: u32 = 24; @@ -384,8 +355,8 @@ pub fn num_overweight_enqueued_events() -> u32 { .count() as u32 } -pub fn fp(pages: u32, count: u64, size: u64) -> QueueFootprint { - QueueFootprint { storage: Footprint { count, size }, pages } +pub fn fp(pages: u32, ready_pages: u32, count: u64, size: u64) -> QueueFootprint { + QueueFootprint { storage: Footprint { count, size }, pages, ready_pages } } /// A random seed that can be overwritten with `MQ_SEED`. diff --git a/substrate/frame/message-queue/src/tests.rs b/substrate/frame/message-queue/src/tests.rs index 9198e65e2f9c0a5c2ee3bac97a63c1dd844be63f..dbef94b3b10354a8b503b795bd46bf90010bb80f 100644 --- a/substrate/frame/message-queue/src/tests.rs +++ b/substrate/frame/message-queue/src/tests.rs @@ -23,7 +23,7 @@ use crate::{mock::*, *}; use frame_support::{assert_noop, assert_ok, assert_storage_noop, StorageNoopGuard}; use rand::{rngs::StdRng, Rng, SeedableRng}; -use sp_core::blake2_256; +use sp_crypto_hashing::blake2_256; #[test] fn mocked_weight_works() { @@ -1064,13 +1064,13 @@ fn footprint_num_pages_works() { MessageQueue::enqueue_message(msg("weight=2"), Here); MessageQueue::enqueue_message(msg("weight=3"), Here); - assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 16)); + assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 2, 16)); // Mark the messages as overweight. assert_eq!(MessageQueue::service_queues(1.into_weight()), 0.into_weight()); assert_eq!(System::events().len(), 2); - // Overweight does not change the footprint. - assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 16)); + // `ready_pages` decreases but `page` count does not. + assert_eq!(MessageQueue::footprint(Here), fp(2, 0, 2, 16)); // Now execute the second message. assert_eq!( @@ -1078,7 +1078,7 @@ fn footprint_num_pages_works() { .unwrap(), 3.into_weight() ); - assert_eq!(MessageQueue::footprint(Here), fp(1, 1, 8)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 1, 8)); // And the first one: assert_eq!( ::execute_overweight(2.into_weight(), (Here, 0, 0)) @@ -1086,6 +1086,11 @@ fn footprint_num_pages_works() { 2.into_weight() ); assert_eq!(MessageQueue::footprint(Here), Default::default()); + assert_eq!(MessageQueue::footprint(Here), fp(0, 0, 0, 0)); + + // `ready_pages` and normal `pages` increases again: + MessageQueue::enqueue_message(msg("weight=3"), Here); + assert_eq!(MessageQueue::footprint(Here), fp(1, 1, 1, 8)); }) } diff --git a/substrate/frame/message-queue/src/weights.rs b/substrate/frame/message-queue/src/weights.rs index e86f23e274ff246aa9f557626e06a42a4930154c..001e2834e1cc619b48500035c4505d453707dc03 100644 --- a/substrate/frame/message-queue/src/weights.rs +++ b/substrate/frame/message-queue/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_message_queue +//! Autogenerated weights for `pallet_message_queue` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/message-queue/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/message-queue/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_message_queue. +/// Weight functions needed for `pallet_message_queue`. pub trait WeightInfo { fn ready_ring_knit() -> Weight; fn ready_ring_unknit() -> Weight; @@ -64,246 +63,256 @@ pub trait WeightInfo { fn execute_overweight_page_updated() -> Weight; } -/// Weights for pallet_message_queue using the Substrate node and recommended hardware. +/// Weights for `pallet_message_queue` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: MessageQueue ServiceHead (r:1 w:0) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:0) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `267` + // Measured: `301` // Estimated: `6038` - // Minimum execution time: 12_025_000 picoseconds. - Weight::from_parts(12_597_000, 6038) + // Minimum execution time: 11_563_000 picoseconds. + Weight::from_parts(11_956_000, 6038) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `267` + // Measured: `301` // Estimated: `6038` - // Minimum execution time: 11_563_000 picoseconds. - Weight::from_parts(11_785_000, 6038) + // Minimum execution time: 10_263_000 picoseconds. + Weight::from_parts(10_638_000, 6038) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn service_queue_base() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3514` - // Minimum execution time: 4_467_000 picoseconds. - Weight::from_parts(4_655_000, 3514) + // Minimum execution time: 4_335_000 picoseconds. + Weight::from_parts(4_552_000, 3514) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: // Measured: `147` // Estimated: `69049` - // Minimum execution time: 6_103_000 picoseconds. - Weight::from_parts(6_254_000, 69049) + // Minimum execution time: 6_016_000 picoseconds. + Weight::from_parts(6_224_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: // Measured: `147` // Estimated: `69049` - // Minimum execution time: 6_320_000 picoseconds. - Weight::from_parts(6_565_000, 69049) + // Minimum execution time: 6_183_000 picoseconds. + Weight::from_parts(6_348_000, 69049) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: `MessageQueue::BookStateFor` (r:0 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn service_page_item() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 66_062_000 picoseconds. - Weight::from_parts(66_371_000, 0) + // Minimum execution time: 112_864_000 picoseconds. + Weight::from_parts(114_269_000, 0) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `246` // Estimated: `3514` - // Minimum execution time: 6_788_000 picoseconds. - Weight::from_parts(7_176_000, 3514) + // Minimum execution time: 6_665_000 picoseconds. + Weight::from_parts(7_108_000, 3514) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn reap_page() -> Weight { // Proof Size summary in bytes: // Measured: `65744` // Estimated: `69049` - // Minimum execution time: 52_865_000 picoseconds. - Weight::from_parts(54_398_000, 69049) + // Minimum execution time: 51_420_000 picoseconds. + Weight::from_parts(52_252_000, 69049) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: // Measured: `65744` // Estimated: `69049` - // Minimum execution time: 69_168_000 picoseconds. - Weight::from_parts(70_560_000, 69049) + // Minimum execution time: 71_195_000 picoseconds. + Weight::from_parts(72_981_000, 69049) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: // Measured: `65744` // Estimated: `69049` - // Minimum execution time: 80_947_000 picoseconds. - Weight::from_parts(82_715_000, 69049) + // Minimum execution time: 83_238_000 picoseconds. + Weight::from_parts(84_422_000, 69049) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: MessageQueue ServiceHead (r:1 w:0) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:0) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `267` + // Measured: `301` // Estimated: `6038` - // Minimum execution time: 12_025_000 picoseconds. - Weight::from_parts(12_597_000, 6038) + // Minimum execution time: 11_563_000 picoseconds. + Weight::from_parts(11_956_000, 6038) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `267` + // Measured: `301` // Estimated: `6038` - // Minimum execution time: 11_563_000 picoseconds. - Weight::from_parts(11_785_000, 6038) + // Minimum execution time: 10_263_000 picoseconds. + Weight::from_parts(10_638_000, 6038) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn service_queue_base() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3514` - // Minimum execution time: 4_467_000 picoseconds. - Weight::from_parts(4_655_000, 3514) + // Minimum execution time: 4_335_000 picoseconds. + Weight::from_parts(4_552_000, 3514) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: // Measured: `147` // Estimated: `69049` - // Minimum execution time: 6_103_000 picoseconds. - Weight::from_parts(6_254_000, 69049) + // Minimum execution time: 6_016_000 picoseconds. + Weight::from_parts(6_224_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: // Measured: `147` // Estimated: `69049` - // Minimum execution time: 6_320_000 picoseconds. - Weight::from_parts(6_565_000, 69049) + // Minimum execution time: 6_183_000 picoseconds. + Weight::from_parts(6_348_000, 69049) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: `MessageQueue::BookStateFor` (r:0 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn service_page_item() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 66_062_000 picoseconds. - Weight::from_parts(66_371_000, 0) + // Minimum execution time: 112_864_000 picoseconds. + Weight::from_parts(114_269_000, 0) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `246` // Estimated: `3514` - // Minimum execution time: 6_788_000 picoseconds. - Weight::from_parts(7_176_000, 3514) + // Minimum execution time: 6_665_000 picoseconds. + Weight::from_parts(7_108_000, 3514) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn reap_page() -> Weight { // Proof Size summary in bytes: // Measured: `65744` // Estimated: `69049` - // Minimum execution time: 52_865_000 picoseconds. - Weight::from_parts(54_398_000, 69049) + // Minimum execution time: 51_420_000 picoseconds. + Weight::from_parts(52_252_000, 69049) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: // Measured: `65744` // Estimated: `69049` - // Minimum execution time: 69_168_000 picoseconds. - Weight::from_parts(70_560_000, 69049) + // Minimum execution time: 71_195_000 picoseconds. + Weight::from_parts(72_981_000, 69049) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65584), added: 68059, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(65584), added: 68059, mode: `MaxEncodedLen`) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: // Measured: `65744` // Estimated: `69049` - // Minimum execution time: 80_947_000 picoseconds. - Weight::from_parts(82_715_000, 69049) + // Minimum execution time: 83_238_000 picoseconds. + Weight::from_parts(84_422_000, 69049) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/substrate/frame/migrations/Cargo.toml b/substrate/frame/migrations/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..2914aa9ea973eafc3eee5419b3b8d8562b23c4f4 --- /dev/null +++ b/substrate/frame/migrations/Cargo.toml @@ -0,0 +1,64 @@ +[package] +name = "pallet-migrations" +version = "1.0.0" +description = "FRAME pallet to execute multi-block migrations." +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +docify = "0.1.14" +impl-trait-for-tuples = "0.2.2" +log = "0.4.18" +scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } + +frame-benchmarking = { default-features = false, optional = true, path = "../benchmarking" } +frame-support = { default-features = false, path = "../support" } +frame-system = { default-features = false, path = "../system" } +sp-core = { path = "../../primitives/core", default-features = false } +sp-std = { path = "../../primitives/std", default-features = false } +sp-runtime = { path = "../../primitives/runtime", default-features = false } + +[dev-dependencies] +frame-executive = { path = "../executive" } +sp-api = { path = "../../primitives/api", features = ["std"] } +sp-block-builder = { path = "../../primitives/block-builder", features = ["std"] } +sp-io = { path = "../../primitives/io", features = ["std"] } +sp-tracing = { path = "../../primitives/tracing", features = ["std"] } +sp-version = { path = "../../primitives/version", features = ["std"] } + +pretty_assertions = "1.3.0" + +[features] +default = ["std"] + +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] + +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/migrations/src/benchmarking.rs b/substrate/frame/migrations/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..8ad1fa50d14985842051c9ac6fb3f95e30bdb030 --- /dev/null +++ b/substrate/frame/migrations/src/benchmarking.rs @@ -0,0 +1,222 @@ +// 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(feature = "runtime-benchmarks")] + +use super::*; + +use frame_benchmarking::{v2::*, BenchmarkError}; +use frame_system::{Pallet as System, RawOrigin}; +use sp_runtime::traits::One; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[benchmarks] +mod benches { + use super::*; + use frame_support::traits::Hooks; + + #[benchmark] + fn onboard_new_mbms() { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert!(!Cursor::::exists()); + + #[block] + { + Pallet::::onboard_new_mbms(); + } + + assert_last_event::(Event::UpgradeStarted { migrations: 1 }.into()); + } + + #[benchmark] + fn progress_mbms_none() { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert!(!Cursor::::exists()); + + #[block] + { + Pallet::::progress_mbms(One::one()); + } + } + + /// All migrations completed. + #[benchmark] + fn exec_migration_completed() -> Result<(), BenchmarkError> { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 1, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::UpgradeCompleted {}.into()); + + Ok(()) + } + + /// No migration runs since it is skipped as historic. + #[benchmark] + fn exec_migration_skipped_historic() -> Result<(), BenchmarkError> { + T::Migrations::set_fail_after(0); // Should not be called anyway. + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + + let id: IdentifierOf = T::Migrations::nth_id(0).unwrap().try_into().unwrap(); + Historic::::insert(id, ()); + + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::MigrationSkipped { index: 0 }.into()); + + Ok(()) + } + + /// Advance a migration by one step. + #[benchmark] + fn exec_migration_advance() -> Result<(), BenchmarkError> { + T::Migrations::set_success_after(1); + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::MigrationAdvanced { index: 0, took: One::one() }.into()); + + Ok(()) + } + + /// Successfully complete a migration. + #[benchmark] + fn exec_migration_complete() -> Result<(), BenchmarkError> { + T::Migrations::set_success_after(0); + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::MigrationCompleted { index: 0, took: One::one() }.into()); + + Ok(()) + } + + #[benchmark] + fn exec_migration_fail() -> Result<(), BenchmarkError> { + T::Migrations::set_fail_after(0); + assert_eq!(T::Migrations::len(), 1, "Setup failed"); + let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() }; + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + System::::set_block_number(1u32.into()); + + #[block] + { + Pallet::::exec_migration(c, false, &mut meter); + } + + assert_last_event::(Event::UpgradeFailed {}.into()); + + Ok(()) + } + + #[benchmark] + fn on_init_loop() { + T::Migrations::set_fail_after(0); // Should not be called anyway. + System::::set_block_number(1u32.into()); + Pallet::::on_runtime_upgrade(); + + #[block] + { + Pallet::::on_initialize(1u32.into()); + } + } + + #[benchmark] + fn force_set_cursor() { + #[extrinsic_call] + _(RawOrigin::Root, Some(cursor::())); + } + + #[benchmark] + fn force_set_active_cursor() { + #[extrinsic_call] + _(RawOrigin::Root, 0, None, None); + } + + #[benchmark] + fn force_onboard_mbms() { + #[extrinsic_call] + _(RawOrigin::Root); + } + + #[benchmark] + fn clear_historic(n: Linear<0, { DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 }>) { + let id_max_len = ::IdentifierMaxLen::get(); + assert!(id_max_len >= 4, "Precondition violated"); + + for i in 0..DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 { + let id = IdentifierOf::::truncate_from( + i.encode().into_iter().cycle().take(id_max_len as usize).collect::>(), + ); + + Historic::::insert(&id, ()); + } + + #[extrinsic_call] + _( + RawOrigin::Root, + HistoricCleanupSelector::Wildcard { limit: n.into(), previous_cursor: None }, + ); + } + + fn cursor() -> CursorOf { + // Note: The weight of a function can depend on the weight of reading the `inner_cursor`. + // `Cursor` is a user provided type. Now instead of requiring something like `Cursor: + // From`, we instead rely on the fact that it is MEL and the PoV benchmarking will + // therefore already take the MEL bound, even when the cursor in storage is `None`. + MigrationCursor::Active(ActiveCursor { + index: u32::MAX, + inner_cursor: None, + started_at: 0u32.into(), + }) + } + + // Implements a test for each benchmark. Execute with: + // `cargo test -p pallet-migrations --features runtime-benchmarks`. + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/substrate/frame/migrations/src/lib.rs b/substrate/frame/migrations/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..cd57d89f440f79d39f98a3da81ed57b6cb5bfcf5 --- /dev/null +++ b/substrate/frame/migrations/src/lib.rs @@ -0,0 +1,746 @@ +// 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. + +#![deny(missing_docs)] +#![deny(rustdoc::broken_intra_doc_links)] + +//! # `pallet-migrations` +//! +//! Provides multi block migrations for FRAME runtimes. +//! +//! ## Overview +//! +//! The pallet takes care of executing a batch of multi-step migrations over multiple blocks. The +//! process starts on each runtime upgrade. Normal and operational transactions are paused while +//! migrations are on-going. +//! +//! ### Example +//! +//! This example demonstrates a simple mocked walk through of a basic success scenario. The pallet +//! is configured with two migrations: one succeeding after just one step, and the second one +//! succeeding after two steps. A runtime upgrade is then enacted and the block number is advanced +//! until all migrations finish executing. Afterwards, the recorded historic migrations are +//! checked and events are asserted. +#![doc = docify::embed!("substrate/frame/migrations/src/tests.rs", simple_works)] +//! +//! ## Pallet API +//! +//! See the [`pallet`] module for more information about the interfaces this pallet exposes, +//! including its configuration trait, dispatchables, storage items, events and errors. +//! +//! Otherwise noteworthy API of this pallet include its implementation of the +//! [`MultiStepMigrator`] trait. This must be plugged into +//! [`frame_system::Config::MultiBlockMigrator`] for proper function. +//! +//! The API contains some calls for emergency management. They are all prefixed with `force_` and +//! should normally not be needed. Pay special attention prior to using them. +//! +//! ### Design Goals +//! +//! 1. Must automatically execute migrations over multiple blocks. +//! 2. Must expose information about whether migrations are ongoing. +//! 3. Must respect pessimistic weight bounds of migrations. +//! 4. Must execute migrations in order. Skipping is not allowed; migrations are run on a +//! all-or-nothing basis. +//! 5. Must prevent re-execution of past migrations. +//! 6. Must provide transactional storage semantics for migrations. +//! 7. Must guarantee progress. +//! +//! ### Design +//! +//! Migrations are provided to the pallet through the associated type [`Config::Migrations`] of type +//! [`SteppedMigrations`]. This allows multiple migrations to be aggregated through a tuple. It +//! simplifies the trait bounds since all associated types of the trait must be provided by the +//! pallet. The actual progress of the pallet is stored in the [`Cursor`] storage item. This can +//! either be [`MigrationCursor::Active`] or [`MigrationCursor::Stuck`]. In the active case it +//! points to the currently active migration and stores its inner cursor. The inner cursor can then +//! be used by the migration to store its inner state and advance. Each time when the migration +//! returns `Some(cursor)`, it signals the pallet that it is not done yet. +//! The cursor is reset on each runtime upgrade. This ensures that it starts to execute at the +//! first migration in the vector. The pallets cursor is only ever incremented or set to `Stuck` +//! once it encounters an error (Goal 4). Once in the stuck state, the pallet will stay stuck until +//! it is fixed through manual governance intervention. +//! As soon as the cursor of the pallet becomes `Some(_)`; [`MultiStepMigrator::ongoing`] returns +//! `true` (Goal 2). This can be used by upstream code to possibly pause transactions. +//! In `on_initialize` the pallet will load the current migration and check whether it was already +//! executed in the past by checking for membership of its ID in the [`Historic`] set. Historic +//! migrations are skipped without causing an error. Each successfully executed migration is added +//! to this set (Goal 5). +//! This proceeds until no more migrations remain. At that point, the event `UpgradeCompleted` is +//! emitted (Goal 1). +//! The execution of each migration happens by calling [`SteppedMigration::transactional_step`]. +//! This function wraps the inner `step` function into a transactional layer to allow rollback in +//! the error case (Goal 6). +//! Weight limits must be checked by the migration itself. The pallet provides a [`WeightMeter`] for +//! that purpose. The pallet may return [`SteppedMigrationError::InsufficientWeight`] at any point. +//! In that scenario, one of two things will happen: if that migration was exclusively executed +//! in this block, and therefore required more than the maximum amount of weight possible, the +//! process becomes `Stuck`. Otherwise, one re-attempt is executed with the same logic in the next +//! block (Goal 3). Progress through the migrations is guaranteed by providing a timeout for each +//! migration via [`SteppedMigration::max_steps`]. The pallet **ONLY** guarantees progress if this +//! is set to sensible limits (Goal 7). +//! +//! ### Scenario: Governance cleanup +//! +//! Every now and then, governance can make use of the [`clear_historic`][Pallet::clear_historic] +//! call. This ensures that no old migrations pile up in the [`Historic`] set. This can be done very +//! rarely, since the storage should not grow quickly and the lookup weight does not suffer much. +//! Another possibility would be to have a synchronous single-block migration perpetually deployed +//! that cleans them up before the MBMs start. +//! +//! ### Scenario: Successful upgrade +//! +//! The standard procedure for a successful runtime upgrade can look like this: +//! 1. Migrations are configured in the `Migrations` config item. All migrations expose +//! [`max_steps`][SteppedMigration::max_steps], are error tolerant, check their weight bounds and +//! have a unique identifier. +//! 2. The runtime upgrade is enacted. An `UpgradeStarted` event is +//! followed by lots of `MigrationAdvanced` and `MigrationCompleted` events. Finally +//! `UpgradeCompleted` is emitted. +//! 3. Cleanup as described in the governance scenario be executed at any time after the migrations +//! completed. +//! +//! ### Advice: Failed upgrades +//! +//! Failed upgrades cannot be recovered from automatically and require governance intervention. Set +//! up monitoring for `UpgradeFailed` events to be made aware of any failures. The hook +//! [`FailedMigrationHandler::failed`] should be setup in a way that it allows governance to act, +//! but still prevent other transactions from interacting with the inconsistent storage state. Note +//! that this is paramount, since the inconsistent state might contain a faulty balance amount or +//! similar that could cause great harm if user transactions don't remain suspended. One way to +//! implement this would be to use the `SafeMode` or `TxPause` pallets that can prevent most user +//! interactions but still allow a whitelisted set of governance calls. +//! +//! ### Remark: Failed migrations +//! +//! Failed migrations are not added to the `Historic` set. This means that an erroneous +//! migration must be removed and fixed manually. This already applies, even before considering the +//! historic set. +//! +//! ### Remark: Transactional processing +//! +//! You can see the transactional semantics for migration steps as mostly useless, since in the +//! stuck case the state is already messed up. This just prevents it from becoming even more messed +//! up, but doesn't prevent it in the first place. + +#![cfg_attr(not(feature = "std"), no_std)] + +mod benchmarking; +mod mock; +pub mod mock_helpers; +mod tests; +pub mod weights; + +pub use pallet::*; +pub use weights::WeightInfo; + +use codec::{Decode, Encode, MaxEncodedLen}; +use core::ops::ControlFlow; +use frame_support::{ + defensive, defensive_assert, + migrations::*, + traits::Get, + weights::{Weight, WeightMeter}, + BoundedVec, +}; +use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System}; +use sp_runtime::Saturating; +use sp_std::vec::Vec; + +/// Points to the next migration to execute. +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] +pub enum MigrationCursor { + /// Points to the currently active migration and its inner cursor. + Active(ActiveCursor), + + /// Migration got stuck and cannot proceed. This is bad. + Stuck, +} + +impl MigrationCursor { + /// Try to return self as an [`ActiveCursor`]. + pub fn as_active(&self) -> Option<&ActiveCursor> { + match self { + MigrationCursor::Active(active) => Some(active), + MigrationCursor::Stuck => None, + } + } +} + +impl From> + for MigrationCursor +{ + fn from(active: ActiveCursor) -> Self { + MigrationCursor::Active(active) + } +} + +/// Points to the currently active migration and its inner cursor. +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] +pub struct ActiveCursor { + /// The index of the migration in the MBM tuple. + pub index: u32, + /// The cursor of the migration that is referenced by `index`. + pub inner_cursor: Option, + /// The block number that the migration started at. + /// + /// This is used to calculate how many blocks it took. + pub started_at: BlockNumber, +} + +impl ActiveCursor { + /// Advance the overarching cursor to the next migration. + pub(crate) fn goto_next_migration(&mut self, current_block: BlockNumber) { + self.index.saturating_inc(); + self.inner_cursor = None; + self.started_at = current_block; + } +} + +/// How to clear the records of historic migrations. +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +pub enum HistoricCleanupSelector { + /// Clear exactly these entries. + /// + /// This is the advised way of doing it. + Specific(Vec), + + /// Clear up to this many entries + Wildcard { + /// How many should be cleared in this call at most. + limit: Option, + /// The cursor that was emitted from any previous `HistoricCleared`. + /// + /// Does not need to be passed when clearing the first batch. + previous_cursor: Option>, + }, +} + +/// The default number of entries that should be cleared by a `HistoricCleanupSelector::Wildcard`. +/// +/// The caller can explicitly specify a higher amount. Benchmarks are run with twice this value. +const DEFAULT_HISTORIC_BATCH_CLEAR_SIZE: u32 = 128; + +impl HistoricCleanupSelector { + /// The maximal number of entries that this will remove. + /// + /// Needed for weight calculation. + pub fn limit(&self) -> u32 { + match self { + Self::Specific(ids) => ids.len() as u32, + Self::Wildcard { limit, .. } => limit.unwrap_or(DEFAULT_HISTORIC_BATCH_CLEAR_SIZE), + } + } +} + +/// Convenience alias for [`MigrationCursor`]. +pub type CursorOf = MigrationCursor, BlockNumberFor>; + +/// Convenience alias for the raw inner cursor of a migration. +pub type RawCursorOf = BoundedVec::CursorMaxLen>; + +/// Convenience alias for the identifier of a migration. +pub type IdentifierOf = BoundedVec::IdentifierMaxLen>; + +/// Convenience alias for [`ActiveCursor`]. +pub type ActiveCursorOf = ActiveCursor, BlockNumberFor>; + +/// Trait for a tuple of No-OP migrations with one element. +pub trait MockedMigrations: SteppedMigrations { + /// The migration should fail after `n` steps. + fn set_fail_after(n: u32); + /// The migration should succeed after `n` steps. + fn set_success_after(n: u32); +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type of the runtime. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// All the multi-block migrations to run. + /// + /// Should only be updated in a runtime-upgrade once all the old migrations have completed. + /// (Check that [`Cursor`] is `None`). + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations: SteppedMigrations; + + /// Mocked migrations for benchmarking only. + /// + /// Should be configured to [`crate::mock_helpers::MockedMigrations`] in benchmarks. + #[cfg(feature = "runtime-benchmarks")] + type Migrations: MockedMigrations; + + /// The maximal length of an encoded cursor. + /// + /// A good default needs to selected such that no migration will ever have a cursor with MEL + /// above this limit. This is statically checked in `integrity_test`. + #[pallet::constant] + type CursorMaxLen: Get; + + /// The maximal length of an encoded identifier. + /// + /// A good default needs to selected such that no migration will ever have an identifier + /// with MEL above this limit. This is statically checked in `integrity_test`. + #[pallet::constant] + type IdentifierMaxLen: Get; + + /// Notifications for status updates of a runtime upgrade. + /// + /// Could be used to pause XCM etc. + type MigrationStatusHandler: MigrationStatusHandler; + + /// Handler for failed migrations. + type FailedMigrationHandler: FailedMigrationHandler; + + /// The maximum weight to spend each block to execute migrations. + type MaxServiceWeight: Get; + + /// Weight information for the calls and functions of this pallet. + type WeightInfo: WeightInfo; + } + + /// The currently active migration to run and its cursor. + /// + /// `None` indicates that no migration is running. + #[pallet::storage] + pub type Cursor = StorageValue<_, CursorOf, OptionQuery>; + + /// Set of all successfully executed migrations. + /// + /// This is used as blacklist, to not re-execute migrations that have not been removed from the + /// codebase yet. Governance can regularly clear this out via `clear_historic`. + #[pallet::storage] + pub type Historic = StorageMap<_, Twox64Concat, IdentifierOf, (), OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A Runtime upgrade started. + /// + /// Its end is indicated by `UpgradeCompleted` or `UpgradeFailed`. + UpgradeStarted { + /// The number of migrations that this upgrade contains. + /// + /// This can be used to design a progress indicator in combination with counting the + /// `MigrationCompleted` and `MigrationSkipped` events. + migrations: u32, + }, + /// The current runtime upgrade completed. + /// + /// This implies that all of its migrations completed successfully as well. + UpgradeCompleted, + /// Runtime upgrade failed. + /// + /// This is very bad and will require governance intervention. + UpgradeFailed, + /// A migration was skipped since it was already executed in the past. + MigrationSkipped { + /// The index of the skipped migration within the [`Config::Migrations`] list. + index: u32, + }, + /// A migration progressed. + MigrationAdvanced { + /// The index of the migration within the [`Config::Migrations`] list. + index: u32, + /// The number of blocks that this migration took so far. + took: BlockNumberFor, + }, + /// A Migration completed. + MigrationCompleted { + /// The index of the migration within the [`Config::Migrations`] list. + index: u32, + /// The number of blocks that this migration took so far. + took: BlockNumberFor, + }, + /// A Migration failed. + /// + /// This implies that the whole upgrade failed and governance intervention is required. + MigrationFailed { + /// The index of the migration within the [`Config::Migrations`] list. + index: u32, + /// The number of blocks that this migration took so far. + took: BlockNumberFor, + }, + /// The set of historical migrations has been cleared. + HistoricCleared { + /// Should be passed to `clear_historic` in a successive call. + next_cursor: Option>, + }, + } + + #[pallet::error] + pub enum Error { + /// The operation cannot complete since some MBMs are ongoing. + Ongoing, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_runtime_upgrade() -> Weight { + Self::onboard_new_mbms() + } + + #[cfg(feature = "std")] + fn integrity_test() { + // Check that the migrations tuple is legit. + frame_support::assert_ok!(T::Migrations::integrity_test()); + + // Very important! Ensure that the pallet is configured in `System::Config`. + { + assert!(!Cursor::::exists(), "Externalities storage should be clean"); + assert!(!::MultiBlockMigrator::ongoing()); + + Cursor::::put(MigrationCursor::Stuck); + assert!(::MultiBlockMigrator::ongoing()); + + Cursor::::kill(); + } + + // The per-block service weight is sane. + #[cfg(not(test))] + { + let want = T::MaxServiceWeight::get(); + let max = ::BlockWeights::get().max_block; + + assert!(want.all_lte(max), "Service weight is larger than a block: {want} > {max}",); + } + + // Cursor MEL + { + let mel = T::Migrations::cursor_max_encoded_len(); + let max_mel = T::CursorMaxLen::get() as usize; + assert!( + mel <= max_mel, + "A Cursor is not guaranteed to fit into the storage: {mel} > {max_mel}", + ); + } + + // Identifier MEL + { + let mel = T::Migrations::identifier_max_encoded_len(); + let max_mel = T::IdentifierMaxLen::get() as usize; + assert!( + mel <= max_mel, + "An Identifier is not guaranteed to fit into the storage: {mel} > {max_mel}", + ); + } + } + } + + #[pallet::call(weight = T::WeightInfo)] + impl Pallet { + /// Allows root to set a cursor to forcefully start, stop or forward the migration process. + /// + /// Should normally not be needed and is only in place as emergency measure. Note that + /// restarting the migration process in this manner will not call the + /// [`MigrationStatusHandler::started`] hook or emit an `UpgradeStarted` event. + #[pallet::call_index(0)] + pub fn force_set_cursor( + origin: OriginFor, + cursor: Option>, + ) -> DispatchResult { + ensure_root(origin)?; + + Cursor::::set(cursor); + + Ok(()) + } + + /// Allows root to set an active cursor to forcefully start/forward the migration process. + /// + /// This is an edge-case version of [`Self::force_set_cursor`] that allows to set the + /// `started_at` value to the next block number. Otherwise this would not be possible, since + /// `force_set_cursor` takes an absolute block number. Setting `started_at` to `None` + /// indicates that the current block number plus one should be used. + #[pallet::call_index(1)] + pub fn force_set_active_cursor( + origin: OriginFor, + index: u32, + inner_cursor: Option>, + started_at: Option>, + ) -> DispatchResult { + ensure_root(origin)?; + + let started_at = started_at.unwrap_or( + System::::block_number().saturating_add(sp_runtime::traits::One::one()), + ); + Cursor::::put(MigrationCursor::Active(ActiveCursor { + index, + inner_cursor, + started_at, + })); + + Ok(()) + } + + /// Forces the onboarding of the migrations. + /// + /// This process happens automatically on a runtime upgrade. It is in place as an emergency + /// measurement. The cursor needs to be `None` for this to succeed. + #[pallet::call_index(2)] + pub fn force_onboard_mbms(origin: OriginFor) -> DispatchResult { + ensure_root(origin)?; + + ensure!(!Cursor::::exists(), Error::::Ongoing); + Self::onboard_new_mbms(); + + Ok(()) + } + + /// Clears the `Historic` set. + /// + /// `map_cursor` must be set to the last value that was returned by the + /// `HistoricCleared` event. The first time `None` can be used. `limit` must be chosen in a + /// way that will result in a sensible weight. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::clear_historic(selector.limit()))] + pub fn clear_historic( + origin: OriginFor, + selector: HistoricCleanupSelector>, + ) -> DispatchResult { + ensure_root(origin)?; + + match &selector { + HistoricCleanupSelector::Specific(ids) => { + for id in ids { + Historic::::remove(id); + } + Self::deposit_event(Event::HistoricCleared { next_cursor: None }); + }, + HistoricCleanupSelector::Wildcard { previous_cursor, .. } => { + let next = Historic::::clear(selector.limit(), previous_cursor.as_deref()); + Self::deposit_event(Event::HistoricCleared { next_cursor: next.maybe_cursor }); + }, + } + + Ok(()) + } + } +} + +impl Pallet { + /// Onboard all new Multi-Block-Migrations and start the process of executing them. + /// + /// Should only be called once all previous migrations completed. + fn onboard_new_mbms() -> Weight { + if let Some(cursor) = Cursor::::get() { + log::error!("Ongoing migrations interrupted - chain stuck"); + + let maybe_index = cursor.as_active().map(|c| c.index); + Self::upgrade_failed(maybe_index); + return T::WeightInfo::onboard_new_mbms() + } + + let migrations = T::Migrations::len(); + log::debug!("Onboarding {migrations} new MBM migrations"); + + if migrations > 0 { + // Set the cursor to the first migration: + Cursor::::set(Some( + ActiveCursor { + index: 0, + inner_cursor: None, + started_at: System::::block_number(), + } + .into(), + )); + Self::deposit_event(Event::UpgradeStarted { migrations }); + T::MigrationStatusHandler::started(); + } + + T::WeightInfo::onboard_new_mbms() + } + + /// Tries to make progress on the Multi-Block-Migrations process. + fn progress_mbms(n: BlockNumberFor) -> Weight { + let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get()); + meter.consume(T::WeightInfo::progress_mbms_none()); + + let mut cursor = match Cursor::::get() { + None => { + log::trace!("[Block {n:?}] Waiting for cursor to become `Some`."); + return meter.consumed() + }, + Some(MigrationCursor::Active(cursor)) => { + log::debug!("Progressing MBM #{}", cursor.index); + cursor + }, + Some(MigrationCursor::Stuck) => { + log::error!("Migration stuck. Governance intervention required."); + return meter.consumed() + }, + }; + debug_assert!(Self::ongoing()); + + // The limit here is a defensive measure to prevent an infinite loop. It expresses that we + // allow no more than 8 MBMs to finish in a single block. This should be harmless, since we + // generally expect *Multi*-Block-Migrations to take *multiple* blocks. + for i in 0..8 { + match Self::exec_migration(cursor, i == 0, &mut meter) { + None => return meter.consumed(), + Some(ControlFlow::Continue(next_cursor)) => { + cursor = next_cursor; + }, + Some(ControlFlow::Break(last_cursor)) => { + cursor = last_cursor; + break + }, + } + } + + Cursor::::set(Some(cursor.into())); + + meter.consumed() + } + + /// Try to make progress on the current migration. + /// + /// Returns whether processing should continue or break for this block. The return value means: + /// - `None`: The migration process is completely finished. + /// - `ControlFlow::Break`: Continue in the *next* block with the given cursor. + /// - `ControlFlow::Continue`: Continue in the *current* block with the given cursor. + fn exec_migration( + mut cursor: ActiveCursorOf, + is_first: bool, + meter: &mut WeightMeter, + ) -> Option, ActiveCursorOf>> { + // The differences between the single branches' weights is not that big. And since we do + // only one step per block, we can just use the maximum instead of more precise accounting. + if meter.try_consume(Self::exec_migration_max_weight()).is_err() { + defensive_assert!(!is_first, "There should be enough weight to do this at least once"); + return Some(ControlFlow::Break(cursor)) + } + + let Some(id) = T::Migrations::nth_id(cursor.index) else { + // No more migrations in the tuple - we are done. + defensive_assert!(cursor.index == T::Migrations::len(), "Inconsistent MBMs tuple"); + Self::deposit_event(Event::UpgradeCompleted); + Cursor::::kill(); + T::MigrationStatusHandler::completed(); + return None; + }; + + let Ok(bounded_id): Result, _> = id.try_into() else { + defensive!("integrity_test ensures that all identifiers' MEL bounds fit into CursorMaxLen; qed."); + Self::upgrade_failed(Some(cursor.index)); + return None + }; + + if Historic::::contains_key(&bounded_id) { + Self::deposit_event(Event::MigrationSkipped { index: cursor.index }); + cursor.goto_next_migration(System::::block_number()); + return Some(ControlFlow::Continue(cursor)) + } + + let max_steps = T::Migrations::nth_max_steps(cursor.index); + let next_cursor = T::Migrations::nth_transactional_step( + cursor.index, + cursor.inner_cursor.clone().map(|c| c.into_inner()), + meter, + ); + let Some((max_steps, next_cursor)) = max_steps.zip(next_cursor) else { + defensive!("integrity_test ensures that the tuple is valid; qed"); + Self::upgrade_failed(Some(cursor.index)); + return None + }; + + let took = System::::block_number().saturating_sub(cursor.started_at); + match next_cursor { + Ok(Some(next_cursor)) => { + let Ok(bound_next_cursor) = next_cursor.try_into() else { + defensive!("The integrity check ensures that all cursors' MEL bound fits into CursorMaxLen; qed"); + Self::upgrade_failed(Some(cursor.index)); + return None + }; + + Self::deposit_event(Event::MigrationAdvanced { index: cursor.index, took }); + cursor.inner_cursor = Some(bound_next_cursor); + + if max_steps.map_or(false, |max| took > max.into()) { + Self::deposit_event(Event::MigrationFailed { index: cursor.index, took }); + Self::upgrade_failed(Some(cursor.index)); + None + } else { + // A migration cannot progress more than one step per block, we therefore break. + Some(ControlFlow::Break(cursor)) + } + }, + Ok(None) => { + // A migration is done when it returns cursor `None`. + Self::deposit_event(Event::MigrationCompleted { index: cursor.index, took }); + Historic::::insert(&bounded_id, ()); + cursor.goto_next_migration(System::::block_number()); + Some(ControlFlow::Continue(cursor)) + }, + Err(SteppedMigrationError::InsufficientWeight { required }) => { + if is_first || required.any_gt(meter.limit()) { + Self::deposit_event(Event::MigrationFailed { index: cursor.index, took }); + Self::upgrade_failed(Some(cursor.index)); + None + } else { + // Retry and hope that there is more weight in the next block. + Some(ControlFlow::Break(cursor)) + } + }, + Err(SteppedMigrationError::InvalidCursor | SteppedMigrationError::Failed) => { + Self::deposit_event(Event::MigrationFailed { index: cursor.index, took }); + Self::upgrade_failed(Some(cursor.index)); + None + }, + } + } + + /// Fail the current runtime upgrade, caused by `migration`. + fn upgrade_failed(migration: Option) { + use FailedMigrationHandling::*; + Self::deposit_event(Event::UpgradeFailed); + + match T::FailedMigrationHandler::failed(migration) { + KeepStuck => Cursor::::set(Some(MigrationCursor::Stuck)), + ForceUnstuck => Cursor::::kill(), + Ignore => {}, + } + } + + fn exec_migration_max_weight() -> Weight { + T::WeightInfo::exec_migration_complete() + .max(T::WeightInfo::exec_migration_completed()) + .max(T::WeightInfo::exec_migration_skipped_historic()) + .max(T::WeightInfo::exec_migration_advance()) + .max(T::WeightInfo::exec_migration_fail()) + } +} + +impl MultiStepMigrator for Pallet { + fn ongoing() -> bool { + Cursor::::exists() + } + + fn step() -> Weight { + Self::progress_mbms(System::::block_number()) + } +} diff --git a/substrate/frame/migrations/src/mock.rs b/substrate/frame/migrations/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..3729264755442a008641668b0a14214764d255b9 --- /dev/null +++ b/substrate/frame/migrations/src/mock.rs @@ -0,0 +1,163 @@ +// 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. + +//! Mocked runtime for testing the migrations pallet. + +#![cfg(test)] + +use crate::{mock_helpers::*, Event, Historic}; + +use frame_support::{ + derive_impl, + migrations::*, + traits::{OnFinalize, OnInitialize}, + weights::Weight, +}; +use frame_system::EventRecord; +use sp_core::{ConstU32, H256}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test { + System: frame_system, + Migrations: crate, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type PalletInfo = PalletInfo; + type MultiBlockMigrator = Migrations; +} + +frame_support::parameter_types! { + pub const MaxServiceWeight: Weight = Weight::MAX.div(10); +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Migrations = MockedMigrations; + type CursorMaxLen = ConstU32<65_536>; + type IdentifierMaxLen = ConstU32<256>; + type MigrationStatusHandler = MockedMigrationStatusHandler; + type FailedMigrationHandler = MockedFailedMigrationHandler; + type MaxServiceWeight = MaxServiceWeight; + type WeightInfo = (); +} + +frame_support::parameter_types! { + /// The number of started upgrades. + pub static UpgradesStarted: u32 = 0; + /// The number of completed upgrades. + pub static UpgradesCompleted: u32 = 0; + /// The migrations that failed. + pub static UpgradesFailed: Vec> = vec![]; + /// Return value of [`MockedFailedMigrationHandler::failed`]. + pub static FailedUpgradeResponse: FailedMigrationHandling = FailedMigrationHandling::KeepStuck; +} + +/// Records all started and completed upgrades in `UpgradesStarted` and `UpgradesCompleted`. +pub struct MockedMigrationStatusHandler; +impl MigrationStatusHandler for MockedMigrationStatusHandler { + fn started() { + log::info!("MigrationStatusHandler started"); + UpgradesStarted::mutate(|v| *v += 1); + } + + fn completed() { + log::info!("MigrationStatusHandler completed"); + UpgradesCompleted::mutate(|v| *v += 1); + } +} + +/// Records all failed upgrades in `UpgradesFailed`. +pub struct MockedFailedMigrationHandler; +impl FailedMigrationHandler for MockedFailedMigrationHandler { + fn failed(migration: Option) -> FailedMigrationHandling { + UpgradesFailed::mutate(|v| v.push(migration)); + let res = FailedUpgradeResponse::get(); + log::error!("FailedMigrationHandler failed at: {migration:?}, handling as {res:?}"); + res + } +} + +/// Returns the number of `(started, completed, failed)` upgrades and resets their numbers. +pub fn upgrades_started_completed_failed() -> (u32, u32, u32) { + (UpgradesStarted::take(), UpgradesCompleted::take(), UpgradesFailed::take().len() as u32) +} + +/// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new(Default::default()) +} + +/// Run this closure in test externalities. +pub fn test_closure(f: impl FnOnce() -> R) -> R { + let mut ext = new_test_ext(); + ext.execute_with(f) +} + +pub fn run_to_block(n: u32) { + while System::block_number() < n as u64 { + log::debug!("Block {}", System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Migrations::on_initialize(System::block_number()); + // Executive calls this: + ::step(); + + Migrations::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + } +} + +/// Returns the historic migrations, sorted by their identifier. +pub fn historic() -> Vec { + let mut historic = Historic::::iter_keys().collect::>(); + historic.sort(); + historic +} + +// Traits to make using events less insufferable: +pub trait IntoRecord { + fn into_record(self) -> EventRecord<::RuntimeEvent, H256>; +} + +impl IntoRecord for Event { + fn into_record(self) -> EventRecord<::RuntimeEvent, H256> { + let re: ::RuntimeEvent = self.into(); + EventRecord { phase: frame_system::Phase::Initialization, event: re, topics: vec![] } + } +} + +pub trait IntoRecords { + fn into_records(self) -> Vec::RuntimeEvent, H256>>; +} + +impl IntoRecords for Vec { + fn into_records(self) -> Vec::RuntimeEvent, H256>> { + self.into_iter().map(|e| e.into_record()).collect() + } +} + +pub fn assert_events(events: Vec) { + pretty_assertions::assert_eq!(events.into_records(), System::events()); + System::reset_events(); +} diff --git a/substrate/frame/migrations/src/mock_helpers.rs b/substrate/frame/migrations/src/mock_helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..c5e23efb4e314e6f5562c50521a533c82f94669e --- /dev/null +++ b/substrate/frame/migrations/src/mock_helpers.rs @@ -0,0 +1,142 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test helpers for internal and external usage. + +#![allow(missing_docs)] + +use codec::{Decode, Encode}; +use frame_support::{ + migrations::*, + weights::{Weight, WeightMeter}, +}; +use sp_core::ConstU32; +use sp_runtime::BoundedVec; +use sp_std::{vec, vec::Vec}; + +/// Opaque identifier of a migration. +pub type MockedIdentifier = BoundedVec>; + +/// How a mocked migration should behave. +#[derive(Debug, Clone, Copy, Encode, Decode)] +pub enum MockedMigrationKind { + /// Succeed after its number of steps elapsed. + SucceedAfter, + /// Fail after its number of steps elapsed. + FailAfter, + /// Never terminate. + TimeoutAfter, + /// Cause an [`SteppedMigrationError::InsufficientWeight`] error after its number of steps + /// elapsed. + HighWeightAfter(Weight), +} +use MockedMigrationKind::*; // C style + +/// Creates a migration identifier with a specific `kind` and `steps`. +pub fn mocked_id(kind: MockedMigrationKind, steps: u32) -> MockedIdentifier { + (b"MockedMigration", kind, steps).encode().try_into().unwrap() +} + +frame_support::parameter_types! { + /// The configs for the migrations to run. + storage MIGRATIONS: Vec<(MockedMigrationKind, u32)> = vec![]; +} + +/// Allows to set the migrations to run at runtime instead of compile-time. +/// +/// It achieves this by using the storage to store the migrations to run. +pub struct MockedMigrations; +impl SteppedMigrations for MockedMigrations { + fn len() -> u32 { + MIGRATIONS::get().len() as u32 + } + + fn nth_id(n: u32) -> Option> { + let k = MIGRATIONS::get().get(n as usize).copied(); + k.map(|(kind, steps)| mocked_id(kind, steps).into_inner()) + } + + fn nth_step( + n: u32, + cursor: Option>, + _meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + let (kind, steps) = MIGRATIONS::get()[n as usize]; + + let mut count: u32 = + cursor.as_ref().and_then(|c| Decode::decode(&mut &c[..]).ok()).unwrap_or(0); + log::debug!("MockedMigration: Step {}", count); + if count != steps || matches!(kind, TimeoutAfter) { + count += 1; + return Some(Ok(Some(count.encode()))) + } + + Some(match kind { + SucceedAfter => { + log::debug!("MockedMigration: Succeeded after {} steps", count); + Ok(None) + }, + HighWeightAfter(required) => { + log::debug!("MockedMigration: Not enough weight after {} steps", count); + Err(SteppedMigrationError::InsufficientWeight { required }) + }, + FailAfter => { + log::debug!("MockedMigration: Failed after {} steps", count); + Err(SteppedMigrationError::Failed) + }, + TimeoutAfter => unreachable!(), + }) + } + + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + // This is a hack but should be fine. We dont need it in testing. + Self::nth_step(n, cursor, meter) + } + + fn nth_max_steps(n: u32) -> Option> { + MIGRATIONS::get().get(n as usize).map(|(_, s)| Some(*s)) + } + + fn cursor_max_encoded_len() -> usize { + 65_536 + } + + fn identifier_max_encoded_len() -> usize { + 256 + } +} + +impl MockedMigrations { + /// Set the migrations to run. + pub fn set(migrations: Vec<(MockedMigrationKind, u32)>) { + MIGRATIONS::set(&migrations); + } +} + +impl crate::MockedMigrations for MockedMigrations { + fn set_fail_after(steps: u32) { + MIGRATIONS::set(&vec![(FailAfter, steps)]); + } + + fn set_success_after(steps: u32) { + MIGRATIONS::set(&vec![(SucceedAfter, steps)]); + } +} diff --git a/substrate/frame/migrations/src/tests.rs b/substrate/frame/migrations/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c9043d37a62e5f1df3d12da979b68251621c9d0 --- /dev/null +++ b/substrate/frame/migrations/src/tests.rs @@ -0,0 +1,335 @@ +// 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(test)] + +use crate::{ + mock::{Test as T, *}, + mock_helpers::{MockedMigrationKind::*, *}, + Cursor, Event, FailedMigrationHandling, MigrationCursor, +}; +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, 0), (SucceedAfter, 1), (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, 0), + mocked_id(SucceedAfter, 1), + mocked_id(SucceedAfter, 2), + ] + ); + + // Check that we got all events. + assert_events(vec![ + UpgradeStarted { migrations: 3 }, + MigrationCompleted { index: 0, took: 1 }, + MigrationAdvanced { index: 1, took: 0 }, + MigrationCompleted { index: 1, took: 1 }, + MigrationAdvanced { index: 2, took: 0 }, + MigrationAdvanced { index: 2, took: 1 }, + MigrationCompleted { index: 2, took: 2 }, + UpgradeCompleted, + ]); + }); +} + +#[test] +fn failing_migration_sets_cursor_to_stuck() { + test_closure(|| { + FailedUpgradeResponse::set(FailedMigrationHandling::KeepStuck); + MockedMigrations::set(vec![(FailAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Failed migrations are not recorded in `Historical`. + assert!(historic().is_empty()); + // Check that we got all events. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationFailed { index: 0, took: 3 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(UpgradesStarted::take(), 1); + assert_eq!(UpgradesCompleted::take(), 0); + assert_eq!(UpgradesFailed::take(), vec![Some(0)]); + + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck), "Must stuck the chain"); + }); +} + +#[test] +fn failing_migration_force_unstuck_works() { + test_closure(|| { + FailedUpgradeResponse::set(FailedMigrationHandling::ForceUnstuck); + MockedMigrations::set(vec![(FailAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Failed migrations are not recorded in `Historical`. + assert!(historic().is_empty()); + // Check that we got all events. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationFailed { index: 0, took: 3 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(UpgradesStarted::take(), 1); + assert_eq!(UpgradesCompleted::take(), 0); + assert_eq!(UpgradesFailed::take(), vec![Some(0)]); + + assert!(Cursor::::get().is_none(), "Must unstuck the chain"); + }); +} + +/// A migration that reports not getting enough weight errors if it is the first one to run in that +/// block. +#[test] +fn high_weight_migration_singular_fails() { + test_closure(|| { + MockedMigrations::set(vec![(HighWeightAfter(Weight::zero()), 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Failed migrations are not recorded in `Historical`. + assert!(historic().is_empty()); + // Check that we got all events. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationFailed { index: 0, took: 3 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + }); +} + +/// A migration that reports of not getting enough weight is retried once, if it is not the first +/// one to run in a block. +#[test] +fn high_weight_migration_retries_once() { + test_closure(|| { + MockedMigrations::set(vec![(SucceedAfter, 0), (HighWeightAfter(Weight::zero()), 0)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + assert_eq!(historic(), vec![mocked_id(SucceedAfter, 0)]); + // Check that we got all events. + assert_events::>(vec![ + Event::UpgradeStarted { migrations: 2 }, + Event::MigrationCompleted { index: 0, took: 1 }, + // `took=1` means that it was retried once. + Event::MigrationFailed { index: 1, took: 1 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + }); +} + +/// If a migration uses more weight than the limit, then it will not retry but fail even when it is +/// not the first one in the block. +// Note: Same as `high_weight_migration_retries_once` but with different required weight for the +// migration. +#[test] +fn high_weight_migration_permanently_overweight_fails() { + test_closure(|| { + MockedMigrations::set(vec![(SucceedAfter, 0), (HighWeightAfter(Weight::MAX), 0)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + assert_eq!(historic(), vec![mocked_id(SucceedAfter, 0)]); + // Check that we got all events. + assert_events::>(vec![ + Event::UpgradeStarted { migrations: 2 }, + Event::MigrationCompleted { index: 0, took: 1 }, + // `blocks=0` means that it was not retried. + Event::MigrationFailed { index: 1, took: 0 }, + Event::UpgradeFailed, + ]); + + // Check that the handler was called correctly. + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + }); +} + +#[test] +fn historic_skipping_works() { + test_closure(|| { + MockedMigrations::set(vec![ + (SucceedAfter, 0), + (SucceedAfter, 0), // duplicate + (SucceedAfter, 1), + (SucceedAfter, 2), + (SucceedAfter, 1), // duplicate + ]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Just three historical ones, since two were added twice. + assert_eq!( + historic(), + vec![ + mocked_id(SucceedAfter, 0), + mocked_id(SucceedAfter, 1), + mocked_id(SucceedAfter, 2), + ] + ); + // Events received. + assert_events(vec![ + Event::UpgradeStarted { migrations: 5 }, + Event::MigrationCompleted { index: 0, took: 1 }, + Event::MigrationSkipped { index: 1 }, + Event::MigrationAdvanced { index: 2, took: 0 }, + Event::MigrationCompleted { index: 2, took: 1 }, + Event::MigrationAdvanced { index: 3, took: 0 }, + Event::MigrationAdvanced { index: 3, took: 1 }, + Event::MigrationCompleted { index: 3, took: 2 }, + Event::MigrationSkipped { index: 4 }, + Event::UpgradeCompleted, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 1, 0)); + + // Now go for another upgrade; just to make sure that it wont execute again. + System::reset_events(); + Migrations::on_runtime_upgrade(); + run_to_block(20); + + // Same historical ones as before. + assert_eq!( + historic(), + vec![ + mocked_id(SucceedAfter, 0), + mocked_id(SucceedAfter, 1), + mocked_id(SucceedAfter, 2), + ] + ); + + // Everything got skipped. + assert_events(vec![ + Event::UpgradeStarted { migrations: 5 }, + Event::MigrationSkipped { index: 0 }, + Event::MigrationSkipped { index: 1 }, + Event::MigrationSkipped { index: 2 }, + Event::MigrationSkipped { index: 3 }, + Event::MigrationSkipped { index: 4 }, + Event::UpgradeCompleted, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 1, 0)); + }); +} + +/// When another upgrade happens while a migration is still running, it should set the cursor to +/// stuck. +#[test] +fn upgrade_fails_when_migration_active() { + test_closure(|| { + MockedMigrations::set(vec![(SucceedAfter, 10)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(3); + + // Events received. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 0, 0)); + + // Upgrade again. + Migrations::on_runtime_upgrade(); + // -- Defensive path -- + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + assert_events(vec![Event::UpgradeFailed]); + assert_eq!(upgrades_started_completed_failed(), (0, 0, 1)); + }); +} + +#[test] +fn migration_timeout_errors() { + test_closure(|| { + MockedMigrations::set(vec![(TimeoutAfter, 3)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(5); + + // Times out after taking more than 3 steps. + assert_events(vec![ + Event::UpgradeStarted { migrations: 1 }, + Event::MigrationAdvanced { index: 0, took: 1 }, + Event::MigrationAdvanced { index: 0, took: 2 }, + Event::MigrationAdvanced { index: 0, took: 3 }, + Event::MigrationAdvanced { index: 0, took: 4 }, + Event::MigrationFailed { index: 0, took: 4 }, + Event::UpgradeFailed, + ]); + assert_eq!(upgrades_started_completed_failed(), (1, 0, 1)); + + // Failed migrations are not black-listed. + assert!(historic().is_empty()); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + + Migrations::on_runtime_upgrade(); + run_to_block(6); + + assert_events(vec![Event::UpgradeFailed]); + assert_eq!(Cursor::::get(), Some(MigrationCursor::Stuck)); + assert_eq!(upgrades_started_completed_failed(), (0, 0, 1)); + }); +} diff --git a/substrate/frame/migrations/src/weights.rs b/substrate/frame/migrations/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..9eca48ac3fd9ca8663a0b54a0258591e9b4023aa --- /dev/null +++ b/substrate/frame/migrations/src/weights.rs @@ -0,0 +1,361 @@ +// 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_migrations` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_migrations +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/migrations/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --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_migrations`. +pub trait WeightInfo { + fn onboard_new_mbms() -> Weight; + fn progress_mbms_none() -> Weight; + fn exec_migration_completed() -> Weight; + fn exec_migration_skipped_historic() -> Weight; + fn exec_migration_advance() -> Weight; + fn exec_migration_complete() -> Weight; + fn exec_migration_fail() -> Weight; + fn on_init_loop() -> Weight; + fn force_set_cursor() -> Weight; + fn force_set_active_cursor() -> Weight; + fn force_onboard_mbms() -> Weight; + fn clear_historic(n: u32, ) -> Weight; +} + +/// Weights for `pallet_migrations` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `67035` + // Minimum execution time: 7_932_000 picoseconds. + Weight::from_parts(8_428_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 2_229_000 picoseconds. + Weight::from_parts(2_329_000, 67035) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 6_051_000 picoseconds. + Weight::from_parts(6_483_000, 3599) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `330` + // Estimated: `3795` + // Minimum execution time: 10_066_000 picoseconds. + Weight::from_parts(10_713_000, 3795) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 10_026_000 picoseconds. + Weight::from_parts(10_379_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 11_680_000 picoseconds. + Weight::from_parts(12_184_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 12_334_000 picoseconds. + Weight::from_parts(12_899_000, 3741) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 187_000 picoseconds. + Weight::from_parts(209_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_688_000 picoseconds. + Weight::from_parts(2_874_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_108_000 picoseconds. + Weight::from_parts(3_263_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `67035` + // Minimum execution time: 5_993_000 picoseconds. + Weight::from_parts(6_359_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1122 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 16_003_000 picoseconds. + Weight::from_parts(14_453_014, 3834) + // Standard Error: 3_305 + .saturating_add(Weight::from_parts(1_325_026, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn onboard_new_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `67035` + // Minimum execution time: 7_932_000 picoseconds. + Weight::from_parts(8_428_000, 67035) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn progress_mbms_none() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 2_229_000 picoseconds. + Weight::from_parts(2_329_000, 67035) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_completed() -> Weight { + // Proof Size summary in bytes: + // Measured: `134` + // Estimated: `3599` + // Minimum execution time: 6_051_000 picoseconds. + Weight::from_parts(6_483_000, 3599) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_skipped_historic() -> Weight { + // Proof Size summary in bytes: + // Measured: `330` + // Estimated: `3795` + // Minimum execution time: 10_066_000 picoseconds. + Weight::from_parts(10_713_000, 3795) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_advance() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 10_026_000 picoseconds. + Weight::from_parts(10_379_000, 3741) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:1) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + fn exec_migration_complete() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 11_680_000 picoseconds. + Weight::from_parts(12_184_000, 3741) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Storage: `MultiBlockMigrations::Historic` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn exec_migration_fail() -> Weight { + // Proof Size summary in bytes: + // Measured: `276` + // Estimated: `3741` + // Minimum execution time: 12_334_000 picoseconds. + Weight::from_parts(12_899_000, 3741) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + fn on_init_loop() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 187_000 picoseconds. + Weight::from_parts(209_000, 0) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_688_000 picoseconds. + Weight::from_parts(2_874_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:0 w:1) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + fn force_set_active_cursor() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_108_000 picoseconds. + Weight::from_parts(3_263_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x583359fe0e84d953a9dd84e8addb08a5` (r:1 w:0) + fn force_onboard_mbms() -> Weight { + // Proof Size summary in bytes: + // Measured: `251` + // Estimated: `67035` + // Minimum execution time: 5_993_000 picoseconds. + Weight::from_parts(6_359_000, 67035) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: `MultiBlockMigrations::Historic` (r:256 w:256) + /// Proof: `MultiBlockMigrations::Historic` (`max_values`: None, `max_size`: Some(266), added: 2741, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 256]`. + fn clear_historic(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1122 + n * (271 ±0)` + // Estimated: `3834 + n * (2740 ±0)` + // Minimum execution time: 16_003_000 picoseconds. + Weight::from_parts(14_453_014, 3834) + // Standard Error: 3_305 + .saturating_add(Weight::from_parts(1_325_026, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2740).saturating_mul(n.into())) + } +} diff --git a/substrate/frame/mixnet/Cargo.toml b/substrate/frame/mixnet/Cargo.toml index ea8b1b00e49c83550e26e587c725022a19a21662..d1bb01dde1a47e70587dc37ad27123a0632cdcf7 100644 --- a/substrate/frame/mixnet/Cargo.toml +++ b/substrate/frame/mixnet/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "FRAME's mixnet pallet" name = "pallet-mixnet" -version = "0.1.0-dev" +version = "0.4.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition = "2021" @@ -20,9 +20,9 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = frame-benchmarking = { default-features = false, optional = true, path = "../benchmarking" } frame-support = { default-features = false, path = "../support" } frame-system = { default-features = false, path = "../system" } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["derive"] } +serde = { features = ["derive"], workspace = true } sp-application-crypto = { default-features = false, path = "../../primitives/application-crypto" } sp-arithmetic = { default-features = false, path = "../../primitives/arithmetic" } sp-io = { default-features = false, path = "../../primitives/io" } diff --git a/substrate/frame/multisig/Cargo.toml b/substrate/frame/multisig/Cargo.toml index 40b0f4973a8db275cfc964fe2083e84dd0b76569..1d2a79bdc52f2fadd6fe2aa782029936c76a28e0 100644 --- a/substrate/frame/multisig/Cargo.toml +++ b/substrate/frame/multisig/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-multisig" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -26,7 +26,7 @@ sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } # third party -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] pallet-balances = { path = "../balances" } diff --git a/substrate/frame/multisig/src/lib.rs b/substrate/frame/multisig/src/lib.rs index e4426c64b4125fe267a1e30a42a7196274b05e4e..a83b78e316f500ddc9c615420c2ac627b90ee7e9 100644 --- a/substrate/frame/multisig/src/lib.rs +++ b/substrate/frame/multisig/src/lib.rs @@ -168,7 +168,7 @@ pub mod pallet { type WeightInfo: WeightInfo; } - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/multisig/src/migrations.rs b/substrate/frame/multisig/src/migrations.rs index 330613bb3dfda94224b223a2f3f1fdf6735179b0..e6402600d0d368783e30f43e0afc3adad4d0a1ba 100644 --- a/substrate/frame/multisig/src/migrations.rs +++ b/substrate/frame/multisig/src/migrations.rs @@ -39,7 +39,7 @@ pub mod v1 { (OpaqueCall, ::AccountId, BalanceOf), >; - pub struct MigrateToV1(sp_std::marker::PhantomData); + pub struct MigrateToV1(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { @@ -51,7 +51,7 @@ pub mod v1 { fn on_runtime_upgrade() -> Weight { use sp_runtime::Saturating; - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); if onchain > 0 { diff --git a/substrate/frame/multisig/src/weights.rs b/substrate/frame/multisig/src/weights.rs index 7b87d258d383d43aaf8ef8aaddc0546c60b5813e..4cbe7bb03b7d5fecc8b9b03edab6613c1d42f34e 100644 --- a/substrate/frame/multisig/src/weights.rs +++ b/substrate/frame/multisig/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_multisig +//! Autogenerated weights for `pallet_multisig` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/multisig/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/multisig/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_multisig. +/// Weight functions needed for `pallet_multisig`. pub trait WeightInfo { fn as_multi_threshold_1(z: u32, ) -> Weight; fn as_multi_create(s: u32, z: u32, ) -> Weight; @@ -61,220 +60,238 @@ pub trait WeightInfo { fn cancel_as_multi(s: u32, ) -> Weight; } -/// Weights for pallet_multisig using the Substrate node and recommended hardware. +/// Weights for `pallet_multisig` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `z` is `[0, 10000]`. fn as_multi_threshold_1(z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 13_452_000 picoseconds. - Weight::from_parts(14_425_869, 0) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 18_893_000 picoseconds. + Weight::from_parts(20_278_307, 3997) // Standard Error: 4 - .saturating_add(Weight::from_parts(493, 0).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(488, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 46_012_000 picoseconds. - Weight::from_parts(34_797_344, 6811) - // Standard Error: 833 - .saturating_add(Weight::from_parts(127_671, 0).saturating_mul(s.into())) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_498, 0).saturating_mul(z.into())) + // Minimum execution time: 39_478_000 picoseconds. + Weight::from_parts(29_195_487, 6811) + // Standard Error: 739 + .saturating_add(Weight::from_parts(118_766, 0).saturating_mul(s.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_511, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[3, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 29_834_000 picoseconds. - Weight::from_parts(20_189_154, 6811) - // Standard Error: 637 - .saturating_add(Weight::from_parts(110_080, 0).saturating_mul(s.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_483, 0).saturating_mul(z.into())) + // Minimum execution time: 26_401_000 picoseconds. + Weight::from_parts(17_277_296, 6811) + // Standard Error: 492 + .saturating_add(Weight::from_parts(101_763, 0).saturating_mul(s.into())) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_486, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `426 + s * (33 ±0)` + // Measured: `571 + s * (33 ±0)` // Estimated: `6811` - // Minimum execution time: 51_464_000 picoseconds. - Weight::from_parts(39_246_644, 6811) - // Standard Error: 1_251 - .saturating_add(Weight::from_parts(143_313, 0).saturating_mul(s.into())) + // Minimum execution time: 52_430_000 picoseconds. + Weight::from_parts(40_585_478, 6811) + // Standard Error: 1_240 + .saturating_add(Weight::from_parts(161_405, 0).saturating_mul(s.into())) // Standard Error: 12 - .saturating_add(Weight::from_parts(1_523, 0).saturating_mul(z.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_parts(1_547, 0).saturating_mul(z.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 33_275_000 picoseconds. - Weight::from_parts(34_073_221, 6811) - // Standard Error: 1_163 - .saturating_add(Weight::from_parts(124_815, 0).saturating_mul(s.into())) + // Minimum execution time: 27_797_000 picoseconds. + Weight::from_parts(29_064_584, 6811) + // Standard Error: 817 + .saturating_add(Weight::from_parts(108_179, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 18_411_000 picoseconds. - Weight::from_parts(19_431_787, 6811) - // Standard Error: 694 - .saturating_add(Weight::from_parts(107_220, 0).saturating_mul(s.into())) + // Minimum execution time: 15_236_000 picoseconds. + Weight::from_parts(16_360_247, 6811) + // Standard Error: 584 + .saturating_add(Weight::from_parts(94_917, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `492 + s * (1 ±0)` // Estimated: `6811` - // Minimum execution time: 33_985_000 picoseconds. - Weight::from_parts(35_547_970, 6811) - // Standard Error: 1_135 - .saturating_add(Weight::from_parts(116_537, 0).saturating_mul(s.into())) + // Minimum execution time: 28_730_000 picoseconds. + Weight::from_parts(30_056_661, 6811) + // Standard Error: 792 + .saturating_add(Weight::from_parts(108_212, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `z` is `[0, 10000]`. fn as_multi_threshold_1(z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 13_452_000 picoseconds. - Weight::from_parts(14_425_869, 0) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 18_893_000 picoseconds. + Weight::from_parts(20_278_307, 3997) // Standard Error: 4 - .saturating_add(Weight::from_parts(493, 0).saturating_mul(z.into())) + .saturating_add(Weight::from_parts(488, 0).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 46_012_000 picoseconds. - Weight::from_parts(34_797_344, 6811) - // Standard Error: 833 - .saturating_add(Weight::from_parts(127_671, 0).saturating_mul(s.into())) - // Standard Error: 8 - .saturating_add(Weight::from_parts(1_498, 0).saturating_mul(z.into())) + // Minimum execution time: 39_478_000 picoseconds. + Weight::from_parts(29_195_487, 6811) + // Standard Error: 739 + .saturating_add(Weight::from_parts(118_766, 0).saturating_mul(s.into())) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_511, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[3, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 29_834_000 picoseconds. - Weight::from_parts(20_189_154, 6811) - // Standard Error: 637 - .saturating_add(Weight::from_parts(110_080, 0).saturating_mul(s.into())) - // Standard Error: 6 - .saturating_add(Weight::from_parts(1_483, 0).saturating_mul(z.into())) + // Minimum execution time: 26_401_000 picoseconds. + Weight::from_parts(17_277_296, 6811) + // Standard Error: 492 + .saturating_add(Weight::from_parts(101_763, 0).saturating_mul(s.into())) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_486, 0).saturating_mul(z.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `426 + s * (33 ±0)` + // Measured: `571 + s * (33 ±0)` // Estimated: `6811` - // Minimum execution time: 51_464_000 picoseconds. - Weight::from_parts(39_246_644, 6811) - // Standard Error: 1_251 - .saturating_add(Weight::from_parts(143_313, 0).saturating_mul(s.into())) + // Minimum execution time: 52_430_000 picoseconds. + Weight::from_parts(40_585_478, 6811) + // Standard Error: 1_240 + .saturating_add(Weight::from_parts(161_405, 0).saturating_mul(s.into())) // Standard Error: 12 - .saturating_add(Weight::from_parts(1_523, 0).saturating_mul(z.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(Weight::from_parts(1_547, 0).saturating_mul(z.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `301 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 33_275_000 picoseconds. - Weight::from_parts(34_073_221, 6811) - // Standard Error: 1_163 - .saturating_add(Weight::from_parts(124_815, 0).saturating_mul(s.into())) + // Minimum execution time: 27_797_000 picoseconds. + Weight::from_parts(29_064_584, 6811) + // Standard Error: 817 + .saturating_add(Weight::from_parts(108_179, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `320` // Estimated: `6811` - // Minimum execution time: 18_411_000 picoseconds. - Weight::from_parts(19_431_787, 6811) - // Standard Error: 694 - .saturating_add(Weight::from_parts(107_220, 0).saturating_mul(s.into())) + // Minimum execution time: 15_236_000 picoseconds. + Weight::from_parts(16_360_247, 6811) + // Standard Error: 584 + .saturating_add(Weight::from_parts(94_917, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `492 + s * (1 ±0)` // Estimated: `6811` - // Minimum execution time: 33_985_000 picoseconds. - Weight::from_parts(35_547_970, 6811) - // Standard Error: 1_135 - .saturating_add(Weight::from_parts(116_537, 0).saturating_mul(s.into())) + // Minimum execution time: 28_730_000 picoseconds. + Weight::from_parts(30_056_661, 6811) + // Standard Error: 792 + .saturating_add(Weight::from_parts(108_212, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/nft-fractionalization/Cargo.toml b/substrate/frame/nft-fractionalization/Cargo.toml index 355bb2a5d3e7ffe65f40c54c01457f29929cea0a..8002b7e1165f1648d5edd39486c996f4eabe2bc6 100644 --- a/substrate/frame/nft-fractionalization/Cargo.toml +++ b/substrate/frame/nft-fractionalization/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nft-fractionalization" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/nft-fractionalization/src/mock.rs b/substrate/frame/nft-fractionalization/src/mock.rs index 855109adcbee08d5e7cfc6897370be85582c73e9..a41386150091de0d77153593dfe6ce68a07cfe18 100644 --- a/substrate/frame/nft-fractionalization/src/mock.rs +++ b/substrate/frame/nft-fractionalization/src/mock.rs @@ -27,9 +27,8 @@ use frame_support::{ }; use frame_system::EnsureSigned; use pallet_nfts::PalletFeatures; -use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + traits::{IdentifyAccount, IdentityLookup, Verify}, BuildStorage, MultiSignature, }; @@ -52,29 +51,10 @@ construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -89,7 +69,6 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = ConstU32<1>; type FreezeIdentifier = (); type MaxFreezes = (); } diff --git a/substrate/frame/nft-fractionalization/src/weights.rs b/substrate/frame/nft-fractionalization/src/weights.rs index ebb4aa0fbcfba2df71b6b7ecb9368540ef8b9ffa..07872ebaea9083d9e9cf5326b5b93c1da7bcf9e3 100644 --- a/substrate/frame/nft-fractionalization/src/weights.rs +++ b/substrate/frame/nft-fractionalization/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_nft_fractionalization +//! Autogenerated weights for `pallet_nft_fractionalization` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/nft-fractionalization/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/nft-fractionalization/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,136 +49,136 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_nft_fractionalization. +/// Weight functions needed for `pallet_nft_fractionalization`. pub trait WeightInfo { fn fractionalize() -> Weight; fn unify() -> Weight; } -/// Weights for pallet_nft_fractionalization using the Substrate node and recommended hardware. +/// Weights for `pallet_nft_fractionalization` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: NftFractionalization NftToAsset (r:0 w:1) - /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + /// Storage: `NftFractionalization::NftToAsset` (r:0 w:1) + /// Proof: `NftFractionalization::NftToAsset` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`) fn fractionalize() -> Weight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4326` - // Minimum execution time: 187_416_000 picoseconds. - Weight::from_parts(191_131_000, 4326) + // Minimum execution time: 164_764_000 picoseconds. + Weight::from_parts(168_243_000, 4326) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } - /// Storage: NftFractionalization NftToAsset (r:1 w:1) - /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `NftFractionalization::NftToAsset` (r:1 w:1) + /// Proof: `NftFractionalization::NftToAsset` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn unify() -> Weight { // Proof Size summary in bytes: // Measured: `1422` // Estimated: `4326` - // Minimum execution time: 134_159_000 picoseconds. - Weight::from_parts(136_621_000, 4326) + // Minimum execution time: 120_036_000 picoseconds. + Weight::from_parts(123_550_000, 4326) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Assets Metadata (r:1 w:1) - /// Proof: Assets Metadata (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: NftFractionalization NftToAsset (r:0 w:1) - /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Assets::Metadata` (r:1 w:1) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + /// Storage: `NftFractionalization::NftToAsset` (r:0 w:1) + /// Proof: `NftFractionalization::NftToAsset` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`) fn fractionalize() -> Weight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4326` - // Minimum execution time: 187_416_000 picoseconds. - Weight::from_parts(191_131_000, 4326) + // Minimum execution time: 164_764_000 picoseconds. + Weight::from_parts(168_243_000, 4326) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } - /// Storage: NftFractionalization NftToAsset (r:1 w:1) - /// Proof: NftFractionalization NftToAsset (max_values: None, max_size: Some(92), added: 2567, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `NftFractionalization::NftToAsset` (r:1 w:1) + /// Proof: `NftFractionalization::NftToAsset` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn unify() -> Weight { // Proof Size summary in bytes: // Measured: `1422` // Estimated: `4326` - // Minimum execution time: 134_159_000 picoseconds. - Weight::from_parts(136_621_000, 4326) + // Minimum execution time: 120_036_000 picoseconds. + Weight::from_parts(123_550_000, 4326) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } diff --git a/substrate/frame/nfts/Cargo.toml b/substrate/frame/nfts/Cargo.toml index 0d3f542c55266c0bf68333119d533860f840e945..69e9ea170b14c97ef81a9a5a4fa8ae855cce94d2 100644 --- a/substrate/frame/nfts/Cargo.toml +++ b/substrate/frame/nfts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nfts" -version = "4.0.0-dev" +version = "22.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } enumflags2 = { version = "0.7.7" } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/nfts/runtime-api/Cargo.toml b/substrate/frame/nfts/runtime-api/Cargo.toml index 8eb6726552bb6c3142a375401dac9473746aa4a9..84cbd1f51c98c673df2f9084d34fad77ec69dd54 100644 --- a/substrate/frame/nfts/runtime-api/Cargo.toml +++ b/substrate/frame/nfts/runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nfts-runtime-api" -version = "4.0.0-dev" +version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/nfts/src/lib.rs b/substrate/frame/nfts/src/lib.rs index a7d505e2e397dbca5f547ed9ed1a7c3cc4235aa5..7cf7cdc61a7d58c1cee641cee1fde92e92836876 100644 --- a/substrate/frame/nfts/src/lib.rs +++ b/substrate/frame/nfts/src/lib.rs @@ -76,7 +76,7 @@ pub mod pallet { use frame_support::{pallet_prelude::*, traits::ExistenceRequirement}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/nfts/src/macros.rs b/substrate/frame/nfts/src/macros.rs index 1a601ce0927fa2f4c3cfae880d91e892e83d62fd..d313c8785b27149977badb4be7cefcdef8f8180e 100644 --- a/substrate/frame/nfts/src/macros.rs +++ b/substrate/frame/nfts/src/macros.rs @@ -42,7 +42,7 @@ macro_rules! impl_codec_bitflags { impl Decode for $wrapper { fn decode( input: &mut I, - ) -> sp_std::result::Result { + ) -> ::core::result::Result { let field = <$size>::decode(input)?; Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?)) } diff --git a/substrate/frame/nfts/src/migration.rs b/substrate/frame/nfts/src/migration.rs index 94635a96aeba991fb458f2cc7830922060727dd7..8f82e092262fb2d63cf1dda563afb0dd11c6f6ac 100644 --- a/substrate/frame/nfts/src/migration.rs +++ b/substrate/frame/nfts/src/migration.rs @@ -51,20 +51,20 @@ pub mod v1 { } /// A migration utility to update the storage version from v0 to v1 for the pallet. - pub struct MigrateToV1(sp_std::marker::PhantomData); + pub struct MigrateToV1(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); log::info!( target: LOG_TARGET, - "Running migration with current storage version {:?} / onchain {:?}", - current_version, - onchain_version + "Running migration with in-code storage version {:?} / onchain {:?}", + in_code_version, + on_chain_version ); - if onchain_version == 0 && current_version == 1 { + if on_chain_version == 0 && in_code_version == 1 { let mut translated = 0u64; let mut configs_iterated = 0u64; Collection::::translate::< @@ -77,13 +77,13 @@ pub mod v1 { Some(old_value.migrate_to_v1(item_configs)) }); - current_version.put::>(); + in_code_version.put::>(); log::info!( target: LOG_TARGET, "Upgraded {} records, storage to version {:?}", translated, - current_version + in_code_version ); T::DbWeight::get().reads_writes(translated + configs_iterated + 1, translated + 1) } else { diff --git a/substrate/frame/nfts/src/mock.rs b/substrate/frame/nfts/src/mock.rs index 95443cf41c4e556e15054c6532b8cffba0f2772a..e86fafd07e965ab995a04e512bd9794833735d4e 100644 --- a/substrate/frame/nfts/src/mock.rs +++ b/substrate/frame/nfts/src/mock.rs @@ -24,10 +24,9 @@ use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, }; -use sp_core::H256; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ - traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + traits::{IdentifyAccount, IdentityLookup, Verify}, BuildStorage, MultiSignature, }; @@ -48,29 +47,10 @@ pub type AccountId = ::AccountId; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -87,7 +67,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { diff --git a/substrate/frame/nfts/src/weights.rs b/substrate/frame/nfts/src/weights.rs index 6b8c577bb12e5d19bf5751d2a5019d60aea743b0..4fcc1e601f4116b75c4f86d7a19e41043f891876 100644 --- a/substrate/frame/nfts/src/weights.rs +++ b/substrate/frame/nfts/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_nfts +//! Autogenerated weights for `pallet_nfts` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -37,9 +37,9 @@ // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/nfts/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/nfts/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -49,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_nfts. +/// Weight functions needed for `pallet_nfts`. pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; @@ -92,564 +92,568 @@ pub trait WeightInfo { fn set_attributes_pre_signed(n: u32, ) -> Weight; } -/// Weights for pallet_nfts using the Substrate node and recommended hardware. +/// Weights for `pallet_nfts` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Nfts NextCollectionId (r:1 w:1) - /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:1) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::NextCollectionId` (r:1 w:1) + /// Proof: `Nfts::NextCollectionId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:0 w:1) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:1) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3549` - // Minimum execution time: 40_489_000 picoseconds. - Weight::from_parts(41_320_000, 3549) + // Minimum execution time: 34_035_000 picoseconds. + Weight::from_parts(35_228_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Nfts NextCollectionId (r:1 w:1) - /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:1) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::NextCollectionId` (r:1 w:1) + /// Proof: `Nfts::NextCollectionId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:0 w:1) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:1) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3549` - // Minimum execution time: 23_257_000 picoseconds. - Weight::from_parts(23_770_000, 3549) + // Minimum execution time: 19_430_000 picoseconds. + Weight::from_parts(20_054_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:0) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1001 w:1000) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1000 w:1000) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts CollectionMetadataOf (r:0 w:1) - /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:1) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:1) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1001 w:1000) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1000 w:1000) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionMetadataOf` (r:0 w:1) + /// Proof: `Nfts::CollectionMetadataOf` (`max_values`: None, `max_size`: Some(294), added: 2769, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:1) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// The range of component `m` is `[0, 1000]`. /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. fn destroy(_m: u32, _c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32220 + a * (332 ±0)` - // Estimated: `2523990 + a * (2921 ±0)` - // Minimum execution time: 1_310_198_000 picoseconds. - Weight::from_parts(1_479_261_043, 2523990) - // Standard Error: 4_415 - .saturating_add(Weight::from_parts(6_016_212, 0).saturating_mul(a.into())) + // Measured: `32204 + a * (366 ±0)` + // Estimated: `2523990 + a * (2954 ±0)` + // Minimum execution time: 1_249_733_000 picoseconds. + Weight::from_parts(1_293_703_849, 2523990) + // Standard Error: 4_764 + .saturating_add(Weight::from_parts(6_433_523, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1004_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1005_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(a.into())) - } - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(a.into())) + } + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `455` // Estimated: `4326` - // Minimum execution time: 51_910_000 picoseconds. - Weight::from_parts(53_441_000, 4326) + // Minimum execution time: 48_645_000 picoseconds. + Weight::from_parts(50_287_000, 4326) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) fn force_mint() -> Weight { // Proof Size summary in bytes: // Measured: `455` // Estimated: `4326` - // Minimum execution time: 50_168_000 picoseconds. - Weight::from_parts(51_380_000, 4326) + // Minimum execution time: 46_688_000 picoseconds. + Weight::from_parts(47_680_000, 4326) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:0) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `Nfts::Attribute` (r:1 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:0 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `564` // Estimated: `4326` - // Minimum execution time: 50_738_000 picoseconds. - Weight::from_parts(51_850_000, 4326) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Minimum execution time: 51_771_000 picoseconds. + Weight::from_parts(53_492_000, 4326) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:0) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:2) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:2) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `593` // Estimated: `4326` - // Minimum execution time: 41_055_000 picoseconds. - Weight::from_parts(42_336_000, 4326) + // Minimum execution time: 39_166_000 picoseconds. + Weight::from_parts(40_128_000, 4326) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:5000 w:5000) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:5000 w:5000) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `763 + i * (108 ±0)` // Estimated: `3549 + i * (3336 ±0)` - // Minimum execution time: 15_688_000 picoseconds. - Weight::from_parts(15_921_000, 3549) - // Standard Error: 14_827 - .saturating_add(Weight::from_parts(17_105_395, 0).saturating_mul(i.into())) + // Minimum execution time: 13_804_000 picoseconds. + Weight::from_parts(14_159_000, 3549) + // Standard Error: 13_812 + .saturating_add(Weight::from_parts(14_661_284, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3336).saturating_mul(i.into())) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn lock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `435` // Estimated: `3534` - // Minimum execution time: 19_981_000 picoseconds. - Weight::from_parts(20_676_000, 3534) + // Minimum execution time: 17_485_000 picoseconds. + Weight::from_parts(18_412_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn unlock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `435` // Estimated: `3534` - // Minimum execution time: 19_911_000 picoseconds. - Weight::from_parts(20_612_000, 3534) + // Minimum execution time: 17_335_000 picoseconds. + Weight::from_parts(18_543_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn lock_collection() -> Weight { // Proof Size summary in bytes: // Measured: `340` // Estimated: `3549` - // Minimum execution time: 16_441_000 picoseconds. - Weight::from_parts(16_890_000, 3549) + // Minimum execution time: 14_608_000 picoseconds. + Weight::from_parts(15_696_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts OwnershipAcceptance (r:1 w:1) - /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:2) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) + /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:2) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `388` - // Estimated: `3549` - // Minimum execution time: 22_610_000 picoseconds. - Weight::from_parts(23_422_000, 3549) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + // Measured: `562` + // Estimated: `3593` + // Minimum execution time: 25_686_000 picoseconds. + Weight::from_parts(26_433_000, 3593) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:2 w:4) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:2 w:4) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `369` // Estimated: `6078` - // Minimum execution time: 39_739_000 picoseconds. - Weight::from_parts(41_306_000, 6078) + // Minimum execution time: 37_192_000 picoseconds. + Weight::from_parts(38_561_000, 6078) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:2) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:2) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn force_collection_owner() -> Weight { // Proof Size summary in bytes: // Measured: `311` // Estimated: `3549` - // Minimum execution time: 17_685_000 picoseconds. - Weight::from_parts(18_258_000, 3549) + // Minimum execution time: 15_401_000 picoseconds. + Weight::from_parts(15_826_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn force_collection_config() -> Weight { // Proof Size summary in bytes: // Measured: `276` // Estimated: `3549` - // Minimum execution time: 13_734_000 picoseconds. - Weight::from_parts(14_337_000, 3549) + // Minimum execution time: 11_683_000 picoseconds. + Weight::from_parts(12_255_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn lock_item_properties() -> Weight { // Proof Size summary in bytes: // Measured: `435` // Estimated: `3534` - // Minimum execution time: 19_269_000 picoseconds. - Weight::from_parts(19_859_000, 3534) + // Minimum execution time: 16_899_000 picoseconds. + Weight::from_parts(17_404_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) fn set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `539` - // Estimated: `3911` - // Minimum execution time: 51_540_000 picoseconds. - Weight::from_parts(52_663_000, 3911) + // Estimated: `3944` + // Minimum execution time: 46_768_000 picoseconds. + Weight::from_parts(47_834_000, 3944) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) fn force_set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `344` - // Estimated: `3911` - // Minimum execution time: 26_529_000 picoseconds. - Weight::from_parts(27_305_000, 3911) + // Estimated: `3944` + // Minimum execution time: 23_356_000 picoseconds. + Weight::from_parts(24_528_000, 3944) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `950` - // Estimated: `3911` - // Minimum execution time: 46_951_000 picoseconds. - Weight::from_parts(48_481_000, 3911) + // Measured: `983` + // Estimated: `3944` + // Minimum execution time: 43_061_000 picoseconds. + Weight::from_parts(44_024_000, 3944) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:1 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: // Measured: `381` // Estimated: `4326` - // Minimum execution time: 17_222_000 picoseconds. - Weight::from_parts(17_819_000, 4326) + // Minimum execution time: 14_929_000 picoseconds. + Weight::from_parts(15_344_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1001 w:1000) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:1 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1001 w:1000) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, 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 `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `837 + n * (364 ±0)` - // Estimated: `4326 + n * (2921 ±0)` - // Minimum execution time: 26_185_000 picoseconds. - Weight::from_parts(27_038_000, 4326) - // Standard Error: 2_378 - .saturating_add(Weight::from_parts(6_067_888, 0).saturating_mul(n.into())) + // Measured: `831 + n * (398 ±0)` + // Estimated: `4326 + n * (2954 ±0)` + // Minimum execution time: 23_707_000 picoseconds. + Weight::from_parts(24_688_000, 4326) + // Standard Error: 3_813 + .saturating_add(Weight::from_parts(6_422_256, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) - } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:1) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) + } + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `539` - // Estimated: `3605` - // Minimum execution time: 42_120_000 picoseconds. - Weight::from_parts(43_627_000, 3605) + // Estimated: `3812` + // Minimum execution time: 37_882_000 picoseconds. + Weight::from_parts(39_222_000, 3812) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:1) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `642` - // Estimated: `3605` - // Minimum execution time: 40_732_000 picoseconds. - Weight::from_parts(42_760_000, 3605) + // Measured: `849` + // Estimated: `3812` + // Minimum execution time: 36_629_000 picoseconds. + Weight::from_parts(37_351_000, 3812) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionMetadataOf (r:1 w:1) - /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionMetadataOf` (r:1 w:1) + /// Proof: `Nfts::CollectionMetadataOf` (`max_values`: None, `max_size`: Some(294), added: 2769, mode: `MaxEncodedLen`) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `398` - // Estimated: `3552` - // Minimum execution time: 39_443_000 picoseconds. - Weight::from_parts(40_482_000, 3552) + // Estimated: `3759` + // Minimum execution time: 34_853_000 picoseconds. + Weight::from_parts(35_914_000, 3759) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionMetadataOf (r:1 w:1) - /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionMetadataOf` (r:1 w:1) + /// Proof: `Nfts::CollectionMetadataOf` (`max_values`: None, `max_size`: Some(294), added: 2769, mode: `MaxEncodedLen`) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `509` - // Estimated: `3552` - // Minimum execution time: 37_676_000 picoseconds. - Weight::from_parts(39_527_000, 3552) + // Measured: `716` + // Estimated: `3759` + // Minimum execution time: 33_759_000 picoseconds. + Weight::from_parts(34_729_000, 3759) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `410` // Estimated: `4326` - // Minimum execution time: 20_787_000 picoseconds. - Weight::from_parts(21_315_000, 4326) + // Minimum execution time: 17_583_000 picoseconds. + Weight::from_parts(18_675_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `418` // Estimated: `4326` - // Minimum execution time: 18_200_000 picoseconds. - Weight::from_parts(19_064_000, 4326) + // Minimum execution time: 15_036_000 picoseconds. + Weight::from_parts(15_995_000, 4326) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) fn clear_all_transfer_approvals() -> Weight { // Proof Size summary in bytes: // Measured: `418` // Estimated: `4326` - // Minimum execution time: 17_128_000 picoseconds. - Weight::from_parts(17_952_000, 4326) + // Minimum execution time: 14_666_000 picoseconds. + Weight::from_parts(15_152_000, 4326) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts OwnershipAcceptance (r:1 w:1) - /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) + /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3517` - // Minimum execution time: 14_667_000 picoseconds. - Weight::from_parts(15_262_000, 3517) + // Minimum execution time: 12_393_000 picoseconds. + Weight::from_parts(12_895_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionConfigOf (r:1 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: // Measured: `340` // Estimated: `3549` - // Minimum execution time: 18_435_000 picoseconds. - Weight::from_parts(18_775_000, 3549) + // Minimum execution time: 16_034_000 picoseconds. + Weight::from_parts(16_617_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_mint_settings() -> Weight { // Proof Size summary in bytes: // Measured: `323` // Estimated: `3538` - // Minimum execution time: 18_125_000 picoseconds. - Weight::from_parts(18_415_000, 3538) + // Minimum execution time: 15_812_000 picoseconds. + Weight::from_parts(16_644_000, 3538) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) fn set_price() -> Weight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `4326` - // Minimum execution time: 23_237_000 picoseconds. - Weight::from_parts(24_128_000, 4326) + // Minimum execution time: 21_650_000 picoseconds. + Weight::from_parts(22_443_000, 4326) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:1 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:0) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:2) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:1 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:2) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn buy_item() -> Weight { // Proof Size summary in bytes: // Measured: `705` // Estimated: `4326` - // Minimum execution time: 53_291_000 picoseconds. - Weight::from_parts(54_614_000, 4326) + // Minimum execution time: 49_463_000 picoseconds. + Weight::from_parts(50_937_000, 4326) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -658,681 +662,685 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_192_000 picoseconds. - Weight::from_parts(4_039_901, 0) - // Standard Error: 10_309 - .saturating_add(Weight::from_parts(3_934_017, 0).saturating_mul(n.into())) - } - /// Storage: Nfts Item (r:2 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + // Minimum execution time: 2_029_000 picoseconds. + Weight::from_parts(3_749_829, 0) + // Standard Error: 8_497 + .saturating_add(Weight::from_parts(1_913_514, 0).saturating_mul(n.into())) + } + /// Storage: `Nfts::Item` (r:2 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn create_swap() -> Weight { // Proof Size summary in bytes: // Measured: `494` // Estimated: `7662` - // Minimum execution time: 21_011_000 picoseconds. - Weight::from_parts(22_065_000, 7662) + // Minimum execution time: 18_181_000 picoseconds. + Weight::from_parts(18_698_000, 7662) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts PendingSwapOf (r:1 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::PendingSwapOf` (r:1 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) fn cancel_swap() -> Weight { // Proof Size summary in bytes: // Measured: `513` // Estimated: `4326` - // Minimum execution time: 21_423_000 picoseconds. - Weight::from_parts(21_743_000, 4326) + // Minimum execution time: 18_228_000 picoseconds. + Weight::from_parts(18_940_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:2 w:2) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:1 w:2) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:2 w:0) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:2 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:4) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:2) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:2 w:2) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:1 w:2) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:2 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:2 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:4) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:2) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) fn claim_swap() -> Weight { // Proof Size summary in bytes: // Measured: `834` // Estimated: `7662` - // Minimum execution time: 86_059_000 picoseconds. - Weight::from_parts(88_401_000, 7662) + // Minimum execution time: 77_983_000 picoseconds. + Weight::from_parts(79_887_000, 7662) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(10_u64)) } - /// Storage: Nfts CollectionRoleOf (r:2 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:10 w:10) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:1) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:2 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:10 w:10) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 10]`. fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `629` - // Estimated: `6078 + n * (2921 ±0)` - // Minimum execution time: 146_746_000 picoseconds. - Weight::from_parts(152_885_862, 6078) - // Standard Error: 40_442 - .saturating_add(Weight::from_parts(32_887_800, 0).saturating_mul(n.into())) + // Estimated: `6078 + n * (2954 ±0)` + // Minimum execution time: 126_998_000 picoseconds. + Weight::from_parts(134_149_389, 6078) + // Standard Error: 33_180 + .saturating_add(Weight::from_parts(30_711_206, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) - } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:10 w:10) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) + } + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:1 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:10 w:10) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, 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 `n` is `[0, 10]`. fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `659` - // Estimated: `4326 + n * (2921 ±0)` - // Minimum execution time: 83_960_000 picoseconds. - Weight::from_parts(98_609_885, 4326) - // Standard Error: 85_991 - .saturating_add(Weight::from_parts(32_633_495, 0).saturating_mul(n.into())) + // Estimated: `4326 + n * (2954 ±0)` + // Minimum execution time: 66_213_000 picoseconds. + Weight::from_parts(81_661_819, 4326) + // Standard Error: 87_003 + .saturating_add(Weight::from_parts(29_550_476, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Nfts NextCollectionId (r:1 w:1) - /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:1) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::NextCollectionId` (r:1 w:1) + /// Proof: `Nfts::NextCollectionId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:0 w:1) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:1) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3549` - // Minimum execution time: 40_489_000 picoseconds. - Weight::from_parts(41_320_000, 3549) + // Minimum execution time: 34_035_000 picoseconds. + Weight::from_parts(35_228_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Nfts NextCollectionId (r:1 w:1) - /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:0 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:1) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::NextCollectionId` (r:1 w:1) + /// Proof: `Nfts::NextCollectionId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:0 w:1) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:1) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn force_create() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3549` - // Minimum execution time: 23_257_000 picoseconds. - Weight::from_parts(23_770_000, 3549) + // Minimum execution time: 19_430_000 picoseconds. + Weight::from_parts(20_054_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:0) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:1) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1001 w:1000) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1000 w:1000) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts CollectionMetadataOf (r:0 w:1) - /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:1) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:1) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1001 w:1000) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1000 w:1000) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionMetadataOf` (r:0 w:1) + /// Proof: `Nfts::CollectionMetadataOf` (`max_values`: None, `max_size`: Some(294), added: 2769, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:1) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// The range of component `m` is `[0, 1000]`. /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. fn destroy(_m: u32, _c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32220 + a * (332 ±0)` - // Estimated: `2523990 + a * (2921 ±0)` - // Minimum execution time: 1_310_198_000 picoseconds. - Weight::from_parts(1_479_261_043, 2523990) - // Standard Error: 4_415 - .saturating_add(Weight::from_parts(6_016_212, 0).saturating_mul(a.into())) + // Measured: `32204 + a * (366 ±0)` + // Estimated: `2523990 + a * (2954 ±0)` + // Minimum execution time: 1_249_733_000 picoseconds. + Weight::from_parts(1_293_703_849, 2523990) + // Standard Error: 4_764 + .saturating_add(Weight::from_parts(6_433_523, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1004_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1005_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(a.into())) - } - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(a.into())) + } + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) fn mint() -> Weight { // Proof Size summary in bytes: // Measured: `455` // Estimated: `4326` - // Minimum execution time: 51_910_000 picoseconds. - Weight::from_parts(53_441_000, 4326) + // Minimum execution time: 48_645_000 picoseconds. + Weight::from_parts(50_287_000, 4326) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) fn force_mint() -> Weight { // Proof Size summary in bytes: // Measured: `455` // Estimated: `4326` - // Minimum execution time: 50_168_000 picoseconds. - Weight::from_parts(51_380_000, 4326) + // Minimum execution time: 46_688_000 picoseconds. + Weight::from_parts(47_680_000, 4326) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:0) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:0 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `Nfts::Attribute` (r:1 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:0 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn burn() -> Weight { // Proof Size summary in bytes: // Measured: `564` // Estimated: `4326` - // Minimum execution time: 50_738_000 picoseconds. - Weight::from_parts(51_850_000, 4326) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Minimum execution time: 51_771_000 picoseconds. + Weight::from_parts(53_492_000, 4326) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:0) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:2) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:2) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: // Measured: `593` // Estimated: `4326` - // Minimum execution time: 41_055_000 picoseconds. - Weight::from_parts(42_336_000, 4326) + // Minimum execution time: 39_166_000 picoseconds. + Weight::from_parts(40_128_000, 4326) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:5000 w:5000) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:5000 w:5000) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `763 + i * (108 ±0)` // Estimated: `3549 + i * (3336 ±0)` - // Minimum execution time: 15_688_000 picoseconds. - Weight::from_parts(15_921_000, 3549) - // Standard Error: 14_827 - .saturating_add(Weight::from_parts(17_105_395, 0).saturating_mul(i.into())) + // Minimum execution time: 13_804_000 picoseconds. + Weight::from_parts(14_159_000, 3549) + // Standard Error: 13_812 + .saturating_add(Weight::from_parts(14_661_284, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3336).saturating_mul(i.into())) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn lock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `435` // Estimated: `3534` - // Minimum execution time: 19_981_000 picoseconds. - Weight::from_parts(20_676_000, 3534) + // Minimum execution time: 17_485_000 picoseconds. + Weight::from_parts(18_412_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn unlock_item_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `435` // Estimated: `3534` - // Minimum execution time: 19_911_000 picoseconds. - Weight::from_parts(20_612_000, 3534) + // Minimum execution time: 17_335_000 picoseconds. + Weight::from_parts(18_543_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn lock_collection() -> Weight { // Proof Size summary in bytes: // Measured: `340` // Estimated: `3549` - // Minimum execution time: 16_441_000 picoseconds. - Weight::from_parts(16_890_000, 3549) + // Minimum execution time: 14_608_000 picoseconds. + Weight::from_parts(15_696_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts OwnershipAcceptance (r:1 w:1) - /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:2) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) + /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:2) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `388` - // Estimated: `3549` - // Minimum execution time: 22_610_000 picoseconds. - Weight::from_parts(23_422_000, 3549) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Measured: `562` + // Estimated: `3593` + // Minimum execution time: 25_686_000 picoseconds. + Weight::from_parts(26_433_000, 3593) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:2 w:4) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:2 w:4) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn set_team() -> Weight { // Proof Size summary in bytes: // Measured: `369` // Estimated: `6078` - // Minimum execution time: 39_739_000 picoseconds. - Weight::from_parts(41_306_000, 6078) + // Minimum execution time: 37_192_000 picoseconds. + Weight::from_parts(38_561_000, 6078) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionAccount (r:0 w:2) - /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionAccount` (r:0 w:2) + /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn force_collection_owner() -> Weight { // Proof Size summary in bytes: // Measured: `311` // Estimated: `3549` - // Minimum execution time: 17_685_000 picoseconds. - Weight::from_parts(18_258_000, 3549) + // Minimum execution time: 15_401_000 picoseconds. + Weight::from_parts(15_826_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:0 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:0 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn force_collection_config() -> Weight { // Proof Size summary in bytes: // Measured: `276` // Estimated: `3549` - // Minimum execution time: 13_734_000 picoseconds. - Weight::from_parts(14_337_000, 3549) + // Minimum execution time: 11_683_000 picoseconds. + Weight::from_parts(12_255_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn lock_item_properties() -> Weight { // Proof Size summary in bytes: // Measured: `435` // Estimated: `3534` - // Minimum execution time: 19_269_000 picoseconds. - Weight::from_parts(19_859_000, 3534) + // Minimum execution time: 16_899_000 picoseconds. + Weight::from_parts(17_404_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) fn set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `539` - // Estimated: `3911` - // Minimum execution time: 51_540_000 picoseconds. - Weight::from_parts(52_663_000, 3911) + // Estimated: `3944` + // Minimum execution time: 46_768_000 picoseconds. + Weight::from_parts(47_834_000, 3944) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) fn force_set_attribute() -> Weight { // Proof Size summary in bytes: // Measured: `344` - // Estimated: `3911` - // Minimum execution time: 26_529_000 picoseconds. - Weight::from_parts(27_305_000, 3911) + // Estimated: `3944` + // Minimum execution time: 23_356_000 picoseconds. + Weight::from_parts(24_528_000, 3944) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Nfts Attribute (r:1 w:1) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: `Nfts::Attribute` (r:1 w:1) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `950` - // Estimated: `3911` - // Minimum execution time: 46_951_000 picoseconds. - Weight::from_parts(48_481_000, 3911) + // Measured: `983` + // Estimated: `3944` + // Minimum execution time: 43_061_000 picoseconds. + Weight::from_parts(44_024_000, 3944) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:1 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) fn approve_item_attributes() -> Weight { // Proof Size summary in bytes: // Measured: `381` // Estimated: `4326` - // Minimum execution time: 17_222_000 picoseconds. - Weight::from_parts(17_819_000, 4326) + // Minimum execution time: 14_929_000 picoseconds. + Weight::from_parts(15_344_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1001 w:1000) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:1 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1001 w:1000) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, 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 `n` is `[0, 1000]`. fn cancel_item_attributes_approval(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `837 + n * (364 ±0)` - // Estimated: `4326 + n * (2921 ±0)` - // Minimum execution time: 26_185_000 picoseconds. - Weight::from_parts(27_038_000, 4326) - // Standard Error: 2_378 - .saturating_add(Weight::from_parts(6_067_888, 0).saturating_mul(n.into())) + // Measured: `831 + n * (398 ±0)` + // Estimated: `4326 + n * (2954 ±0)` + // Minimum execution time: 23_707_000 picoseconds. + Weight::from_parts(24_688_000, 4326) + // Standard Error: 3_813 + .saturating_add(Weight::from_parts(6_422_256, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) - } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:1) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) + } + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `539` - // Estimated: `3605` - // Minimum execution time: 42_120_000 picoseconds. - Weight::from_parts(43_627_000, 3605) + // Estimated: `3812` + // Minimum execution time: 37_882_000 picoseconds. + Weight::from_parts(39_222_000, 3812) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:1) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `642` - // Estimated: `3605` - // Minimum execution time: 40_732_000 picoseconds. - Weight::from_parts(42_760_000, 3605) + // Measured: `849` + // Estimated: `3812` + // Minimum execution time: 36_629_000 picoseconds. + Weight::from_parts(37_351_000, 3812) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionMetadataOf (r:1 w:1) - /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionMetadataOf` (r:1 w:1) + /// Proof: `Nfts::CollectionMetadataOf` (`max_values`: None, `max_size`: Some(294), added: 2769, mode: `MaxEncodedLen`) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `398` - // Estimated: `3552` - // Minimum execution time: 39_443_000 picoseconds. - Weight::from_parts(40_482_000, 3552) + // Estimated: `3759` + // Minimum execution time: 34_853_000 picoseconds. + Weight::from_parts(35_914_000, 3759) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts CollectionMetadataOf (r:1 w:1) - /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionMetadataOf` (r:1 w:1) + /// Proof: `Nfts::CollectionMetadataOf` (`max_values`: None, `max_size`: Some(294), added: 2769, mode: `MaxEncodedLen`) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `509` - // Estimated: `3552` - // Minimum execution time: 37_676_000 picoseconds. - Weight::from_parts(39_527_000, 3552) + // Measured: `716` + // Estimated: `3759` + // Minimum execution time: 33_759_000 picoseconds. + Weight::from_parts(34_729_000, 3759) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn approve_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `410` // Estimated: `4326` - // Minimum execution time: 20_787_000 picoseconds. - Weight::from_parts(21_315_000, 4326) + // Minimum execution time: 17_583_000 picoseconds. + Weight::from_parts(18_675_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) fn cancel_approval() -> Weight { // Proof Size summary in bytes: // Measured: `418` // Estimated: `4326` - // Minimum execution time: 18_200_000 picoseconds. - Weight::from_parts(19_064_000, 4326) + // Minimum execution time: 15_036_000 picoseconds. + Weight::from_parts(15_995_000, 4326) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) fn clear_all_transfer_approvals() -> Weight { // Proof Size summary in bytes: // Measured: `418` // Estimated: `4326` - // Minimum execution time: 17_128_000 picoseconds. - Weight::from_parts(17_952_000, 4326) + // Minimum execution time: 14_666_000 picoseconds. + Weight::from_parts(15_152_000, 4326) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts OwnershipAcceptance (r:1 w:1) - /// Proof: Nfts OwnershipAcceptance (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) + /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `3517` - // Minimum execution time: 14_667_000 picoseconds. - Weight::from_parts(15_262_000, 3517) + // Minimum execution time: 12_393_000 picoseconds. + Weight::from_parts(12_895_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionConfigOf (r:1 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) fn set_collection_max_supply() -> Weight { // Proof Size summary in bytes: // Measured: `340` // Estimated: `3549` - // Minimum execution time: 18_435_000 picoseconds. - Weight::from_parts(18_775_000, 3549) + // Minimum execution time: 16_034_000 picoseconds. + Weight::from_parts(16_617_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts CollectionRoleOf (r:1 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:1) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:1) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_mint_settings() -> Weight { // Proof Size summary in bytes: // Measured: `323` // Estimated: `3538` - // Minimum execution time: 18_125_000 picoseconds. - Weight::from_parts(18_415_000, 3538) + // Minimum execution time: 15_812_000 picoseconds. + Weight::from_parts(16_644_000, 3538) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) fn set_price() -> Weight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `4326` - // Minimum execution time: 23_237_000 picoseconds. - Weight::from_parts(24_128_000, 4326) + // Minimum execution time: 21_650_000 picoseconds. + Weight::from_parts(22_443_000, 4326) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:1 w:1) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:1 w:0) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:2) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:1 w:1) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:1 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:2) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn buy_item() -> Weight { // Proof Size summary in bytes: // Measured: `705` // Estimated: `4326` - // Minimum execution time: 53_291_000 picoseconds. - Weight::from_parts(54_614_000, 4326) + // Minimum execution time: 49_463_000 picoseconds. + Weight::from_parts(50_937_000, 4326) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1341,120 +1349,120 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_192_000 picoseconds. - Weight::from_parts(4_039_901, 0) - // Standard Error: 10_309 - .saturating_add(Weight::from_parts(3_934_017, 0).saturating_mul(n.into())) - } - /// Storage: Nfts Item (r:2 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:0 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) + // Minimum execution time: 2_029_000 picoseconds. + Weight::from_parts(3_749_829, 0) + // Standard Error: 8_497 + .saturating_add(Weight::from_parts(1_913_514, 0).saturating_mul(n.into())) + } + /// Storage: `Nfts::Item` (r:2 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn create_swap() -> Weight { // Proof Size summary in bytes: // Measured: `494` // Estimated: `7662` - // Minimum execution time: 21_011_000 picoseconds. - Weight::from_parts(22_065_000, 7662) + // Minimum execution time: 18_181_000 picoseconds. + Weight::from_parts(18_698_000, 7662) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts PendingSwapOf (r:1 w:1) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: `Nfts::PendingSwapOf` (r:1 w:1) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) fn cancel_swap() -> Weight { // Proof Size summary in bytes: // Measured: `513` // Estimated: `4326` - // Minimum execution time: 21_423_000 picoseconds. - Weight::from_parts(21_743_000, 4326) + // Minimum execution time: 18_228_000 picoseconds. + Weight::from_parts(18_940_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nfts Item (r:2 w:2) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts PendingSwapOf (r:1 w:2) - /// Proof: Nfts PendingSwapOf (max_values: None, max_size: Some(71), added: 2546, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:0) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:2 w:0) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:2 w:0) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:4) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) - /// Storage: Nfts ItemPriceOf (r:0 w:2) - /// Proof: Nfts ItemPriceOf (max_values: None, max_size: Some(89), added: 2564, mode: MaxEncodedLen) + /// Storage: `Nfts::Item` (r:2 w:2) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::PendingSwapOf` (r:1 w:2) + /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:2 w:0) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:2 w:0) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:4) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemPriceOf` (r:0 w:2) + /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) fn claim_swap() -> Weight { // Proof Size summary in bytes: // Measured: `834` // Estimated: `7662` - // Minimum execution time: 86_059_000 picoseconds. - Weight::from_parts(88_401_000, 7662) + // Minimum execution time: 77_983_000 picoseconds. + Weight::from_parts(79_887_000, 7662) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(10_u64)) } - /// Storage: Nfts CollectionRoleOf (r:2 w:0) - /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Item (r:1 w:1) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts ItemConfigOf (r:1 w:1) - /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:10 w:10) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: Nfts ItemMetadataOf (r:1 w:1) - /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(140), added: 2615, mode: MaxEncodedLen) - /// Storage: Nfts Account (r:0 w:1) - /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: `Nfts::CollectionRoleOf` (r:2 w:0) + /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Item` (r:1 w:1) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:10 w:10) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) + /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Account` (r:0 w:1) + /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 10]`. fn mint_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `629` - // Estimated: `6078 + n * (2921 ±0)` - // Minimum execution time: 146_746_000 picoseconds. - Weight::from_parts(152_885_862, 6078) - // Standard Error: 40_442 - .saturating_add(Weight::from_parts(32_887_800, 0).saturating_mul(n.into())) + // Estimated: `6078 + n * (2954 ±0)` + // Minimum execution time: 126_998_000 picoseconds. + Weight::from_parts(134_149_389, 6078) + // Standard Error: 33_180 + .saturating_add(Weight::from_parts(30_711_206, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) - } - /// Storage: Nfts Item (r:1 w:0) - /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) - /// Storage: Nfts ItemAttributesApprovalsOf (r:1 w:1) - /// Proof: Nfts ItemAttributesApprovalsOf (max_values: None, max_size: Some(681), added: 3156, mode: MaxEncodedLen) - /// Storage: Nfts CollectionConfigOf (r:1 w:0) - /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) - /// Storage: Nfts Collection (r:1 w:1) - /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) - /// Storage: Nfts Attribute (r:10 w:10) - /// Proof: Nfts Attribute (max_values: None, max_size: Some(446), added: 2921, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) + } + /// Storage: `Nfts::Item` (r:1 w:0) + /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemAttributesApprovalsOf` (r:1 w:1) + /// Proof: `Nfts::ItemAttributesApprovalsOf` (`max_values`: None, `max_size`: Some(681), added: 3156, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Collection` (r:1 w:1) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::Attribute` (r:10 w:10) + /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, 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 `n` is `[0, 10]`. fn set_attributes_pre_signed(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `659` - // Estimated: `4326 + n * (2921 ±0)` - // Minimum execution time: 83_960_000 picoseconds. - Weight::from_parts(98_609_885, 4326) - // Standard Error: 85_991 - .saturating_add(Weight::from_parts(32_633_495, 0).saturating_mul(n.into())) + // Estimated: `4326 + n * (2954 ±0)` + // Minimum execution time: 66_213_000 picoseconds. + Weight::from_parts(81_661_819, 4326) + // Standard Error: 87_003 + .saturating_add(Weight::from_parts(29_550_476, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2921).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) } } diff --git a/substrate/frame/nicks/Cargo.toml b/substrate/frame/nicks/Cargo.toml deleted file mode 100644 index 7d43f64cfe23c0ad37b0c75c312b9e2f56bb0c93..0000000000000000000000000000000000000000 --- a/substrate/frame/nicks/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "pallet-nicks" -version = "4.0.0-dev" -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" -homepage = "https://substrate.io" -repository.workspace = true -description = "FRAME pallet for nick management" -readme = "README.md" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -frame-support = { path = "../support", default-features = false } -frame-system = { path = "../system", default-features = false } -sp-io = { path = "../../primitives/io", default-features = false } -sp-runtime = { path = "../../primitives/runtime", default-features = false } -sp-std = { path = "../../primitives/std", default-features = false } - -[dev-dependencies] -pallet-balances = { path = "../balances" } -sp-core = { path = "../../primitives/core" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/substrate/frame/nicks/README.md b/substrate/frame/nicks/README.md deleted file mode 100644 index 2b05f32d33448e9041b041653dcb1ffd8d3fba26..0000000000000000000000000000000000000000 --- a/substrate/frame/nicks/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Nicks Module - -- [`Config`](https://docs.rs/pallet-nicks/latest/pallet_nicks/pallet/trait.Config.html) -- [`Call`](https://docs.rs/pallet-nicks/latest/pallet_nicks/pallet/enum.Call.html) - -## Overview - -Nicks is an example module for keeping track of account names on-chain. It makes no effort to -create a name hierarchy, be a DNS replacement or provide reverse lookups. Furthermore, the -weights attached to this module's dispatchable functions are for demonstration purposes only and -have not been designed to be economically secure. Do not use this pallet as-is in production. - -## Interface - -### Dispatchable Functions - -- `set_name` - Set the associated name of an account; a small deposit is reserved if not already - taken. -- `clear_name` - Remove an account's associated name; the deposit is returned. -- `kill_name` - Forcibly remove the associated name; the deposit is lost. - -[`Call`]: ./enum.Call.html -[`Config`]: ./trait.Config.html - -License: Apache-2.0 diff --git a/substrate/frame/nicks/src/lib.rs b/substrate/frame/nicks/src/lib.rs deleted file mode 100644 index 540777f87cab739d787e4950d03b440cffad138e..0000000000000000000000000000000000000000 --- a/substrate/frame/nicks/src/lib.rs +++ /dev/null @@ -1,424 +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. - -//! # Nicks Pallet -//! -//! - [`Config`] -//! - [`Call`] -//! -//! ## Overview -//! -//! Nicks is an example pallet for keeping track of account names on-chain. It makes no effort to -//! create a name hierarchy, be a DNS replacement or provide reverse lookups. Furthermore, the -//! weights attached to this pallet's dispatchable functions are for demonstration purposes only and -//! have not been designed to be economically secure. Do not use this pallet as-is in production. -//! -//! ## Interface -//! -//! ### Dispatchable Functions -//! -//! * `set_name` - Set the associated name of an account; a small deposit is reserved if not already -//! taken. -//! * `clear_name` - Remove an account's associated name; the deposit is returned. -//! * `kill_name` - Forcibly remove the associated name; the deposit is lost. - -#![deny(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::traits::{Currency, OnUnbalanced, ReservableCurrency}; -pub use pallet::*; -use sp_runtime::traits::{StaticLookup, Zero}; -use sp_std::prelude::*; - -type AccountIdOf = ::AccountId; -type BalanceOf = <::Currency as Currency>>::Balance; -type NegativeImbalanceOf = - <::Currency as Currency>>::NegativeImbalance; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// The currency trait. - type Currency: ReservableCurrency; - - /// Reservation fee. - #[pallet::constant] - type ReservationFee: Get>; - - /// What to do with slashed funds. - type Slashed: OnUnbalanced>; - - /// The origin which may forcibly set or remove a name. Root can always do this. - type ForceOrigin: EnsureOrigin; - - /// The minimum length a name may be. - #[pallet::constant] - type MinLength: Get; - - /// The maximum length a name may be. - #[pallet::constant] - type MaxLength: Get; - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// A name was set. - NameSet { - /// The account for which the name was set. - who: T::AccountId, - }, - /// A name was forcibly set. - NameForced { - /// The account whose name was forcibly set. - target: T::AccountId, - }, - /// A name was changed. - NameChanged { - /// The account for which the name was changed. - who: T::AccountId, - }, - /// A name was cleared, and the given balance returned. - NameCleared { - /// The account for which the name was cleared. - who: T::AccountId, - /// The deposit returned. - deposit: BalanceOf, - }, - /// A name was removed and the given balance slashed. - NameKilled { - /// The account for which the name was removed. - target: T::AccountId, - /// The deposit returned. - deposit: BalanceOf, - }, - } - - /// Error for the Nicks pallet. - #[pallet::error] - pub enum Error { - /// A name is too short. - TooShort, - /// A name is too long. - TooLong, - /// An account isn't named. - Unnamed, - } - - /// The lookup table for names. - #[pallet::storage] - pub(super) type NameOf = - StorageMap<_, Twox64Concat, T::AccountId, (BoundedVec, BalanceOf)>; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::call] - impl Pallet { - /// Set an account's name. The name should be a UTF-8-encoded string by convention, though - /// we don't check it. - /// - /// The name may not be more than `T::MaxLength` bytes, nor less than `T::MinLength` bytes. - /// - /// If the account doesn't already have a name, then a fee of `ReservationFee` is reserved - /// in the account. - /// - /// The dispatch origin for this call must be _Signed_. - /// - /// ## Complexity - /// - O(1). - #[pallet::call_index(0)] - #[pallet::weight({50_000_000})] - pub fn set_name(origin: OriginFor, name: Vec) -> DispatchResult { - let sender = ensure_signed(origin)?; - - let bounded_name: BoundedVec<_, _> = - name.try_into().map_err(|_| Error::::TooLong)?; - ensure!(bounded_name.len() >= T::MinLength::get() as usize, Error::::TooShort); - - let deposit = if let Some((_, deposit)) = >::get(&sender) { - Self::deposit_event(Event::::NameChanged { who: sender.clone() }); - deposit - } else { - let deposit = T::ReservationFee::get(); - T::Currency::reserve(&sender, deposit)?; - Self::deposit_event(Event::::NameSet { who: sender.clone() }); - deposit - }; - - >::insert(&sender, (bounded_name, deposit)); - Ok(()) - } - - /// Clear an account's name and return the deposit. Fails if the account was not named. - /// - /// The dispatch origin for this call must be _Signed_. - /// - /// ## Complexity - /// - O(1). - #[pallet::call_index(1)] - #[pallet::weight({70_000_000})] - pub fn clear_name(origin: OriginFor) -> DispatchResult { - let sender = ensure_signed(origin)?; - - let deposit = >::take(&sender).ok_or(Error::::Unnamed)?.1; - - let err_amount = T::Currency::unreserve(&sender, deposit); - debug_assert!(err_amount.is_zero()); - - Self::deposit_event(Event::::NameCleared { who: sender, deposit }); - Ok(()) - } - - /// Remove an account's name and take charge of the deposit. - /// - /// Fails if `target` has not been named. The deposit is dealt with through `T::Slashed` - /// imbalance handler. - /// - /// The dispatch origin for this call must match `T::ForceOrigin`. - /// - /// ## Complexity - /// - O(1). - #[pallet::call_index(2)] - #[pallet::weight({70_000_000})] - pub fn kill_name(origin: OriginFor, target: AccountIdLookupOf) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - - // Figure out who we're meant to be clearing. - let target = T::Lookup::lookup(target)?; - // Grab their deposit (and check that they have one). - let deposit = >::take(&target).ok_or(Error::::Unnamed)?.1; - // Slash their deposit from them. - T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit).0); - - Self::deposit_event(Event::::NameKilled { target, deposit }); - Ok(()) - } - - /// Set a third-party account's name with no deposit. - /// - /// No length checking is done on the name. - /// - /// The dispatch origin for this call must match `T::ForceOrigin`. - /// - /// ## Complexity - /// - O(1). - #[pallet::call_index(3)] - #[pallet::weight({70_000_000})] - pub fn force_name( - origin: OriginFor, - target: AccountIdLookupOf, - name: Vec, - ) -> DispatchResult { - T::ForceOrigin::ensure_origin(origin)?; - - let bounded_name: BoundedVec<_, _> = - name.try_into().map_err(|_| Error::::TooLong)?; - let target = T::Lookup::lookup(target)?; - let deposit = >::get(&target).map(|x| x.1).unwrap_or_else(Zero::zero); - >::insert(&target, (bounded_name, deposit)); - - Self::deposit_event(Event::::NameForced { target }); - Ok(()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate as pallet_nicks; - - use frame_support::{ - assert_noop, assert_ok, derive_impl, ord_parameter_types, - traits::{ConstU32, ConstU64}, - }; - use frame_system::EnsureSignedBy; - use sp_core::H256; - use sp_runtime::{ - traits::{BadOrigin, BlakeTwo256, IdentityLookup}, - BuildStorage, - }; - - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Test - { - System: frame_system, - Balances: pallet_balances, - Nicks: pallet_nicks, - } - ); - - #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; - } - - impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; - type AccountStore = System; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeHoldReason = (); - type RuntimeFreezeReason = (); - type MaxHolds = (); - } - - ord_parameter_types! { - pub const One: u64 = 1; - } - impl Config for Test { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type ReservationFee = ConstU64<2>; - type Slashed = (); - type ForceOrigin = EnsureSignedBy; - type MinLength = ConstU32<3>; - type MaxLength = ConstU32<16>; - } - - fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(1, 10), (2, 10)] } - .assimilate_storage(&mut t) - .unwrap(); - t.into() - } - - #[test] - fn kill_name_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Nicks::set_name(RuntimeOrigin::signed(2), b"Dave".to_vec())); - assert_eq!(Balances::total_balance(&2), 10); - assert_ok!(Nicks::kill_name(RuntimeOrigin::signed(1), 2)); - assert_eq!(Balances::total_balance(&2), 8); - assert_eq!(>::get(2), None); - }); - } - - #[test] - fn force_name_should_work() { - new_test_ext().execute_with(|| { - assert_noop!( - Nicks::set_name(RuntimeOrigin::signed(2), b"Dr. David Brubeck, III".to_vec()), - Error::::TooLong, - ); - - assert_ok!(Nicks::set_name(RuntimeOrigin::signed(2), b"Dave".to_vec())); - assert_eq!(Balances::reserved_balance(2), 2); - assert_noop!( - Nicks::force_name(RuntimeOrigin::signed(1), 2, b"Dr. David Brubeck, III".to_vec()), - Error::::TooLong, - ); - assert_ok!(Nicks::force_name( - RuntimeOrigin::signed(1), - 2, - b"Dr. Brubeck, III".to_vec() - )); - assert_eq!(Balances::reserved_balance(2), 2); - let (name, amount) = >::get(2).unwrap(); - assert_eq!(name, b"Dr. Brubeck, III".to_vec()); - assert_eq!(amount, 2); - }); - } - - #[test] - fn normal_operation_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Nicks::set_name(RuntimeOrigin::signed(1), b"Gav".to_vec())); - assert_eq!(Balances::reserved_balance(1), 2); - assert_eq!(Balances::free_balance(1), 8); - assert_eq!(>::get(1).unwrap().0, b"Gav".to_vec()); - - assert_ok!(Nicks::set_name(RuntimeOrigin::signed(1), b"Gavin".to_vec())); - assert_eq!(Balances::reserved_balance(1), 2); - assert_eq!(Balances::free_balance(1), 8); - assert_eq!(>::get(1).unwrap().0, b"Gavin".to_vec()); - - assert_ok!(Nicks::clear_name(RuntimeOrigin::signed(1))); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(1), 10); - }); - } - - #[test] - fn error_catching_should_work() { - new_test_ext().execute_with(|| { - assert_noop!(Nicks::clear_name(RuntimeOrigin::signed(1)), Error::::Unnamed); - - assert_noop!( - Nicks::set_name(RuntimeOrigin::signed(3), b"Dave".to_vec()), - pallet_balances::Error::::InsufficientBalance - ); - - assert_noop!( - Nicks::set_name(RuntimeOrigin::signed(1), b"Ga".to_vec()), - Error::::TooShort - ); - assert_noop!( - Nicks::set_name(RuntimeOrigin::signed(1), b"Gavin James Wood, Esquire".to_vec()), - Error::::TooLong - ); - assert_ok!(Nicks::set_name(RuntimeOrigin::signed(1), b"Dave".to_vec())); - assert_noop!(Nicks::kill_name(RuntimeOrigin::signed(2), 1), BadOrigin); - assert_noop!( - Nicks::force_name(RuntimeOrigin::signed(2), 1, b"Whatever".to_vec()), - BadOrigin - ); - }); - } -} diff --git a/substrate/frame/nis/Cargo.toml b/substrate/frame/nis/Cargo.toml index f95ebc5864c889a73e82c9f878f33e95b157a4a3..025daa07b0c5314011b325570781474356209d02 100644 --- a/substrate/frame/nis/Cargo.toml +++ b/substrate/frame/nis/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nis" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/nis/src/mock.rs b/substrate/frame/nis/src/mock.rs index be6e79ac6f663c89a80cee89556a51093791741c..03976bc66c4df6844c81232fef7f912cb02b5292 100644 --- a/substrate/frame/nis/src/mock.rs +++ b/substrate/frame/nis/src/mock.rs @@ -21,19 +21,13 @@ use crate::{self as pallet_nis, Perquintill, WithMaximumOf}; use frame_support::{ derive_impl, ord_parameter_types, parameter_types, - traits::{ - fungible::Inspect, ConstU16, ConstU32, ConstU64, Everything, OnFinalize, OnInitialize, - StorageMapShim, - }, + traits::{fungible::Inspect, ConstU32, ConstU64, OnFinalize, OnInitialize, StorageMapShim}, weights::Weight, PalletId, }; use pallet_balances::{Instance1, Instance2}; -use sp_core::{ConstU128, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_core::ConstU128; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -52,29 +46,8 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -91,7 +64,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = ConstU32<1>; } impl pallet_balances::Config for Test { @@ -112,7 +84,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { diff --git a/substrate/frame/nis/src/weights.rs b/substrate/frame/nis/src/weights.rs index cba2f0049055b8f3e69aeba7d7d572ba19030801..6390827139119eaa2e98192313b7702766d0ccd1 100644 --- a/substrate/frame/nis/src/weights.rs +++ b/substrate/frame/nis/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_nis +//! Autogenerated weights for `pallet_nis` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/nis/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/nis/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_nis. +/// Weight functions needed for `pallet_nis`. pub trait WeightInfo { fn place_bid(l: u32, ) -> Weight; fn place_bid_max() -> Weight; @@ -65,367 +64,367 @@ pub trait WeightInfo { fn process_bid() -> Weight; } -/// Weights for pallet_nis using the Substrate node and recommended hardware. +/// Weights for `pallet_nis` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6176 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 49_410_000 picoseconds. - Weight::from_parts(57_832_282, 51487) - // Standard Error: 288 - .saturating_add(Weight::from_parts(51_621, 0).saturating_mul(l.into())) + // Minimum execution time: 47_908_000 picoseconds. + Weight::from_parts(50_096_676, 51487) + // Standard Error: 208 + .saturating_add(Weight::from_parts(41_318, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn place_bid_max() -> Weight { // Proof Size summary in bytes: // Measured: `54178` // Estimated: `51487` - // Minimum execution time: 119_696_000 picoseconds. - Weight::from_parts(121_838_000, 51487) + // Minimum execution time: 100_836_000 picoseconds. + Weight::from_parts(102_497_000, 51487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6176 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 50_843_000 picoseconds. - Weight::from_parts(54_237_365, 51487) - // Standard Error: 243 - .saturating_add(Weight::from_parts(67_732, 0).saturating_mul(l.into())) + // Minimum execution time: 45_830_000 picoseconds. + Weight::from_parts(46_667_676, 51487) + // Standard Error: 130 + .saturating_add(Weight::from_parts(33_007, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Nis Summary (r:1 w:0) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:0) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn fund_deficit() -> Weight { // Proof Size summary in bytes: // Measured: `191` // Estimated: `3593` - // Minimum execution time: 40_752_000 picoseconds. - Weight::from_parts(41_899_000, 3593) + // Minimum execution time: 30_440_000 picoseconds. + Weight::from_parts(31_240_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn communify() -> Weight { // Proof Size summary in bytes: // Measured: `668` // Estimated: `3675` - // Minimum execution time: 79_779_000 picoseconds. - Weight::from_parts(82_478_000, 3675) + // Minimum execution time: 71_017_000 picoseconds. + Weight::from_parts(72_504_000, 3675) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn privatize() -> Weight { // Proof Size summary in bytes: // Measured: `829` // Estimated: `3675` - // Minimum execution time: 99_588_000 picoseconds. - Weight::from_parts(102_340_000, 3675) + // Minimum execution time: 89_138_000 picoseconds. + Weight::from_parts(91_290_000, 3675) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// 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(85), added: 2560, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// 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(193), added: 2668, mode: `MaxEncodedLen`) fn thaw_private() -> Weight { // Proof Size summary in bytes: // Measured: `354` - // Estimated: `3593` - // Minimum execution time: 53_094_000 picoseconds. - Weight::from_parts(54_543_000, 3593) + // Estimated: `3658` + // Minimum execution time: 47_917_000 picoseconds. + Weight::from_parts(49_121_000, 3658) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn thaw_communal() -> Weight { // Proof Size summary in bytes: // Measured: `773` // Estimated: `3675` - // Minimum execution time: 107_248_000 picoseconds. - Weight::from_parts(109_923_000, 3675) + // Minimum execution time: 91_320_000 picoseconds. + Weight::from_parts(93_080_000, 3675) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn process_queues() -> Weight { // Proof Size summary in bytes: // Measured: `6624` // Estimated: `7487` - // Minimum execution time: 27_169_000 picoseconds. - Weight::from_parts(29_201_000, 7487) + // Minimum execution time: 20_117_000 picoseconds. + Weight::from_parts(20_829_000, 7487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) fn process_queue() -> Weight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `51487` - // Minimum execution time: 4_540_000 picoseconds. - Weight::from_parts(4_699_000, 51487) + // Minimum execution time: 4_460_000 picoseconds. + Weight::from_parts(4_797_000, 51487) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Nis Receipts (r:0 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:0 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn process_bid() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_085_000 picoseconds. - Weight::from_parts(7_336_000, 0) + // Minimum execution time: 4_609_000 picoseconds. + Weight::from_parts(4_834_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6176 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 49_410_000 picoseconds. - Weight::from_parts(57_832_282, 51487) - // Standard Error: 288 - .saturating_add(Weight::from_parts(51_621, 0).saturating_mul(l.into())) + // Minimum execution time: 47_908_000 picoseconds. + Weight::from_parts(50_096_676, 51487) + // Standard Error: 208 + .saturating_add(Weight::from_parts(41_318, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn place_bid_max() -> Weight { // Proof Size summary in bytes: // Measured: `54178` // Estimated: `51487` - // Minimum execution time: 119_696_000 picoseconds. - Weight::from_parts(121_838_000, 51487) + // Minimum execution time: 100_836_000 picoseconds. + Weight::from_parts(102_497_000, 51487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6176 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 50_843_000 picoseconds. - Weight::from_parts(54_237_365, 51487) - // Standard Error: 243 - .saturating_add(Weight::from_parts(67_732, 0).saturating_mul(l.into())) + // Minimum execution time: 45_830_000 picoseconds. + Weight::from_parts(46_667_676, 51487) + // Standard Error: 130 + .saturating_add(Weight::from_parts(33_007, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Nis Summary (r:1 w:0) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:0) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn fund_deficit() -> Weight { // Proof Size summary in bytes: // Measured: `191` // Estimated: `3593` - // Minimum execution time: 40_752_000 picoseconds. - Weight::from_parts(41_899_000, 3593) + // Minimum execution time: 30_440_000 picoseconds. + Weight::from_parts(31_240_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn communify() -> Weight { // Proof Size summary in bytes: // Measured: `668` // Estimated: `3675` - // Minimum execution time: 79_779_000 picoseconds. - Weight::from_parts(82_478_000, 3675) + // Minimum execution time: 71_017_000 picoseconds. + Weight::from_parts(72_504_000, 3675) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn privatize() -> Weight { // Proof Size summary in bytes: // Measured: `829` // Estimated: `3675` - // Minimum execution time: 99_588_000 picoseconds. - Weight::from_parts(102_340_000, 3675) + // Minimum execution time: 89_138_000 picoseconds. + Weight::from_parts(91_290_000, 3675) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// 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(85), added: 2560, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// 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(193), added: 2668, mode: `MaxEncodedLen`) fn thaw_private() -> Weight { // Proof Size summary in bytes: // Measured: `354` - // Estimated: `3593` - // Minimum execution time: 53_094_000 picoseconds. - Weight::from_parts(54_543_000, 3593) + // Estimated: `3658` + // Minimum execution time: 47_917_000 picoseconds. + Weight::from_parts(49_121_000, 3658) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:1 w:1) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn thaw_communal() -> Weight { // Proof Size summary in bytes: // Measured: `773` // Estimated: `3675` - // Minimum execution time: 107_248_000 picoseconds. - Weight::from_parts(109_923_000, 3675) + // Minimum execution time: 91_320_000 picoseconds. + Weight::from_parts(93_080_000, 3675) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn process_queues() -> Weight { // Proof Size summary in bytes: // Measured: `6624` // Estimated: `7487` - // Minimum execution time: 27_169_000 picoseconds. - Weight::from_parts(29_201_000, 7487) + // Minimum execution time: 20_117_000 picoseconds. + Weight::from_parts(20_829_000, 7487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) fn process_queue() -> Weight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `51487` - // Minimum execution time: 4_540_000 picoseconds. - Weight::from_parts(4_699_000, 51487) + // Minimum execution time: 4_460_000 picoseconds. + Weight::from_parts(4_797_000, 51487) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Nis Receipts (r:0 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:0 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn process_bid() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_085_000 picoseconds. - Weight::from_parts(7_336_000, 0) + // Minimum execution time: 4_609_000 picoseconds. + Weight::from_parts(4_834_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/node-authorization/Cargo.toml b/substrate/frame/node-authorization/Cargo.toml index 46fc0b34514e46275a1361b8e0dee8186ec0a2c4..a39b0ec4eff8b7fce397866acc7be3a8b812c827 100644 --- a/substrate/frame/node-authorization/Cargo.toml +++ b/substrate/frame/node-authorization/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-node-authorization" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/node-authorization/src/mock.rs b/substrate/frame/node-authorization/src/mock.rs index bd648de2b243c781c6db8c7e9375add8ee07dcf4..84ca4d7eff701118e8ec4d140d164e709cc1c8f6 100644 --- a/substrate/frame/node-authorization/src/mock.rs +++ b/substrate/frame/node-authorization/src/mock.rs @@ -20,16 +20,9 @@ use super::*; use crate as pallet_node_authorization; -use frame_support::{ - derive_impl, ord_parameter_types, - traits::{ConstU32, ConstU64}, -}; +use frame_support::{derive_impl, ord_parameter_types, traits::ConstU32}; use frame_system::EnsureSignedBy; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -43,29 +36,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type DbWeight = (); - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } ord_parameter_types! { diff --git a/substrate/frame/node-authorization/src/weights.rs b/substrate/frame/node-authorization/src/weights.rs index a4529c845c7c0e4b15a542cdaa07fc01e5279efe..881eeaf7a4c090573eab463494fd9a6846ae4b40 100644 --- a/substrate/frame/node-authorization/src/weights.rs +++ b/substrate/frame/node-authorization/src/weights.rs @@ -22,7 +22,7 @@ #![allow(unused_imports)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; pub trait WeightInfo { fn add_well_known_node() -> Weight; diff --git a/substrate/frame/nomination-pools/Cargo.toml b/substrate/frame/nomination-pools/Cargo.toml index 00c90b414dece3ae9fbc3b7a9551f4bcbb746aff..9830f31d5fa9accedeb5edbde0c526df4ecefee2 100644 --- a/substrate/frame/nomination-pools/Cargo.toml +++ b/substrate/frame/nomination-pools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nomination-pools" -version = "1.0.0" +version = "25.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,8 +16,12 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } # FRAME frame-support = { path = "../support", default-features = false } @@ -27,7 +31,7 @@ sp-std = { path = "../../primitives/std", default-features = false } sp-staking = { path = "../../primitives/staking", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } -log = { version = "0.4.0", default-features = false } +log = { workspace = true } # Optional: use for testing and/or fuzzing pallet-balances = { path = "../balances", optional = true } diff --git a/substrate/frame/nomination-pools/benchmarking/Cargo.toml b/substrate/frame/nomination-pools/benchmarking/Cargo.toml index 8a4ee07dd744947fa28fc3cf664f422fd0ca598e..3693ad1866dd4ca0dfeb5d25d9844a3c0b28c111 100644 --- a/substrate/frame/nomination-pools/benchmarking/Cargo.toml +++ b/substrate/frame/nomination-pools/benchmarking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nomination-pools-benchmarking" -version = "1.0.0" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/nomination-pools/benchmarking/src/mock.rs b/substrate/frame/nomination-pools/benchmarking/src/mock.rs index 77124ae388811adc157f612259dc9144c0c19cc9..4e57c00849f2d54a140deb36d7b60109aa59e9c9 100644 --- a/substrate/frame/nomination-pools/benchmarking/src/mock.rs +++ b/substrate/frame/nomination-pools/benchmarking/src/mock.rs @@ -79,7 +79,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<1>; type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/substrate/frame/nomination-pools/fuzzer/Cargo.toml b/substrate/frame/nomination-pools/fuzzer/Cargo.toml index 52f49b28457c26c3c247dd4c137565cd9564dcc8..c0d63a2685937a10e6a53288403099bc78ed1fa9 100644 --- a/substrate/frame/nomination-pools/fuzzer/Cargo.toml +++ b/substrate/frame/nomination-pools/fuzzer/Cargo.toml @@ -29,7 +29,7 @@ sp-io = { path = "../../../primitives/io" } sp-tracing = { path = "../../../primitives/tracing" } rand = { version = "0.8.5", features = ["small_rng"] } -log = "0.4.17" +log = { workspace = true, default-features = true } [[bin]] name = "call" diff --git a/substrate/frame/nomination-pools/runtime-api/Cargo.toml b/substrate/frame/nomination-pools/runtime-api/Cargo.toml index 12a897cc6b6fa16761c94f5d2a3c3e5b5892d790..7828f26fe6fe1378942479f44f40e325da265817 100644 --- a/substrate/frame/nomination-pools/runtime-api/Cargo.toml +++ b/substrate/frame/nomination-pools/runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-nomination-pools-runtime-api" -version = "1.0.0-dev" +version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index c538f12e20bf07179ef014408a8d0faeb222971c..a0dbdb3aaa47f6108d6b1f0fec92dc4d6826d624 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -357,10 +357,7 @@ use frame_support::{ pallet_prelude::{MaxEncodedLen, *}, storage::bounded_btree_map::BoundedBTreeMap, traits::{ - fungible::{ - Inspect as FunInspect, InspectFreeze, Mutate as FunMutate, - MutateFreeze as FunMutateFreeze, - }, + fungible::{Inspect, InspectFreeze, Mutate, MutateFreeze}, tokens::{Fortitude, Preservation}, Defensive, DefensiveOption, DefensiveResult, DefensiveSaturating, Get, }, @@ -408,7 +405,7 @@ pub use weights::WeightInfo; /// The balance type used by the currency system. pub type BalanceOf = - <::Currency as FunInspect<::AccountId>>::Balance; + <::Currency as Inspect<::AccountId>>::Balance; /// Type used for unique identifier of each pool. pub type PoolId = u32; @@ -481,8 +478,16 @@ impl Default for ClaimPermission { } /// A member in a pool. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebugNoBound, CloneNoBound)] -#[cfg_attr(feature = "std", derive(frame_support::PartialEqNoBound, DefaultNoBound))] +#[derive( + Encode, + Decode, + MaxEncodedLen, + TypeInfo, + RuntimeDebugNoBound, + CloneNoBound, + frame_support::PartialEqNoBound, +)] +#[cfg_attr(feature = "std", derive(DefaultNoBound))] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct PoolMember { @@ -1288,27 +1293,6 @@ impl BondedPool { }); }; } - - /// Withdraw all the funds that are already unlocked from staking for the - /// [`BondedPool::bonded_account`]. - /// - /// Also reduces the [`TotalValueLocked`] by the difference of the - /// [`T::Staking::total_stake`] of the [`BondedPool::bonded_account`] that might occur by - /// [`T::Staking::withdraw_unbonded`]. - /// - /// Returns the result of [`T::Staking::withdraw_unbonded`] - fn withdraw_from_staking(&self, num_slashing_spans: u32) -> Result { - let bonded_account = self.bonded_account(); - - let prev_total = T::Staking::total_stake(&bonded_account.clone()).unwrap_or_default(); - let outcome = T::Staking::withdraw_unbonded(bonded_account.clone(), num_slashing_spans); - let diff = prev_total - .defensive_saturating_sub(T::Staking::total_stake(&bonded_account).unwrap_or_default()); - TotalValueLocked::::mutate(|tvl| { - tvl.saturating_reduce(diff); - }); - outcome - } } /// A reward pool. @@ -1592,7 +1576,7 @@ pub mod pallet { use frame_system::{ensure_signed, pallet_prelude::*}; use sp_runtime::Perbill; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(8); #[pallet::pallet] @@ -1608,8 +1592,8 @@ pub mod pallet { type WeightInfo: weights::WeightInfo; /// The currency type used for nomination pool. - type Currency: FunMutate - + FunMutateFreeze; + type Currency: Mutate + + MutateFreeze; /// The overarching freeze reason. type RuntimeFreezeReason: From; @@ -1728,7 +1712,7 @@ pub mod pallet { CountedStorageMap<_, Twox64Concat, PoolId, BondedPoolInner>; /// Reward pools. This is where there rewards for each pool accumulate. When a members payout is - /// claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account. + /// claimed, the balance comes out of the reward pool. Keyed by the bonded pools account. #[pallet::storage] pub type RewardPools = CountedStorageMap<_, Twox64Concat, PoolId, RewardPool>; @@ -1748,8 +1732,8 @@ pub mod pallet { /// A reverse lookup from the pool's account id to its id. /// - /// This is only used for slashing. In all other instances, the pool id is used, and the - /// accounts are deterministically derived from it. + /// This is only used for slashing and on automatic withdraw update. In all other instances, the + /// pool id is used, and the accounts are deterministically derived from it. #[pallet::storage] pub type ReversePoolIdLookup = CountedStorageMap<_, Twox64Concat, T::AccountId, PoolId, OptionQuery>; @@ -2143,7 +2127,12 @@ pub mod pallet { bonded_pool.points, bonded_pool.commission.current(), )?; - let _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; + let _ = Self::do_reward_payout( + &member_account, + &mut member, + &mut bonded_pool, + &mut reward_pool, + )?; let current_era = T::Staking::current_era(); let unbond_era = T::Staking::bonding_duration().saturating_add(current_era); @@ -2213,7 +2202,7 @@ pub mod pallet { // For now we only allow a pool to withdraw unbonded if its not destroying. If the pool // is destroying then `withdraw_unbonded` can be used. ensure!(pool.state != PoolState::Destroying, Error::::NotDestroying); - pool.withdraw_from_staking(num_slashing_spans)?; + T::Staking::withdraw_unbonded(pool.bonded_account(), num_slashing_spans)?; Ok(()) } @@ -2265,7 +2254,8 @@ pub mod pallet { // Before calculating the `balance_to_unbond`, we call withdraw unbonded to ensure the // `transferrable_balance` is correct. - let stash_killed = bonded_pool.withdraw_from_staking(num_slashing_spans)?; + let stash_killed = + T::Staking::withdraw_unbonded(bonded_pool.bonded_account(), num_slashing_spans)?; // defensive-only: the depositor puts enough funds into the stash so that it will only // be destroyed when they are leaving. @@ -2411,6 +2401,11 @@ pub mod pallet { /// /// This directly forward the call to the staking pallet, on behalf of the pool bonded /// account. + /// + /// # Note + /// + /// In addition to a `root` or `nominator` role of `origin`, pool's depositor needs to have + /// at least `depositor_min_bond` in the pool to start nominating. #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::nominate(validators.len() as u32))] pub fn nominate( @@ -2421,6 +2416,16 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); + + let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) + .ok_or(Error::::PoolMemberNotFound)? + .active_points(); + + ensure!( + bonded_pool.points_to_balance(depositor_points) >= Self::depositor_min_bond(), + Error::::MinimumBondNotMet + ); + T::Staking::nominate(&bonded_pool.bonded_account(), validators) } @@ -2583,17 +2588,37 @@ pub mod pallet { /// Chill on behalf of the pool. /// - /// The dispatch origin of this call must be signed by the pool nominator or the pool + /// The dispatch origin of this call can be signed by the pool nominator or the pool /// root role, same as [`Pallet::nominate`]. /// + /// Under certain conditions, this call can be dispatched permissionlessly (i.e. by any + /// account). + /// + /// # Conditions for a permissionless dispatch: + /// * When pool depositor has less than `MinNominatorBond` staked, otherwise pool members + /// are unable to unbond. + /// + /// # Conditions for permissioned dispatch: + /// * The caller has a nominator or root role of the pool. /// This directly forward the call to the staking pallet, on behalf of the pool bonded /// account. #[pallet::call_index(13)] #[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!(bonded_pool.can_nominate(&who), Error::::NotNominator); + + let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) + .ok_or(Error::::PoolMemberNotFound)? + .active_points(); + + if bonded_pool.points_to_balance(depositor_points) >= + T::Staking::minimum_nominator_bond() + { + ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); + } + T::Staking::chill(&bonded_pool.bonded_account()) } @@ -2932,6 +2957,11 @@ impl Pallet { bonded_pool: BondedPool, reward_pool: RewardPool, ) { + // The pool id of a member cannot change in any case, so we use it to make sure + // `member_account` is the right one. + debug_assert_eq!(PoolMembers::::get(member_account).unwrap().pool_id, member.pool_id); + debug_assert_eq!(member.pool_id, bonded_pool.id); + bonded_pool.put(); RewardPools::insert(member.pool_id, reward_pool); PoolMembers::::insert(member_account, member); @@ -2998,6 +3028,7 @@ impl Pallet { reward_pool: &mut RewardPool, ) -> Result, DispatchError> { debug_assert_eq!(member.pool_id, bonded_pool.id); + debug_assert_eq!(&mut PoolMembers::::get(member_account).unwrap(), member); // a member who has no skin in the game anymore cannot claim any rewards. ensure!(!member.active_points().is_zero(), Error::::FullyUnbonding); @@ -3114,18 +3145,19 @@ impl Pallet { fn do_bond_extra( signer: T::AccountId, - who: T::AccountId, + member_account: T::AccountId, extra: BondExtra>, ) -> DispatchResult { - if signer != who { + if signer != member_account { ensure!( - ClaimPermissions::::get(&who).can_bond_extra(), + ClaimPermissions::::get(&member_account).can_bond_extra(), Error::::DoesNotHavePermission ); ensure!(extra == BondExtra::Rewards, Error::::BondExtraRestricted); } - let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&who)?; + let (mut member, mut bonded_pool, mut reward_pool) = + Self::get_member_with_pools(&member_account)?; // payout related stuff: we must claim the payouts, and updated recorded payout data // before updating the bonded pool points, similar to that of `join` transaction. @@ -3134,14 +3166,18 @@ impl Pallet { bonded_pool.points, bonded_pool.commission.current(), )?; - let claimed = - Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; + let claimed = Self::do_reward_payout( + &member_account, + &mut member, + &mut bonded_pool, + &mut reward_pool, + )?; let (points_issued, bonded) = match extra { BondExtra::FreeBalance(amount) => - (bonded_pool.try_bond_funds(&who, amount, BondType::Later)?, amount), + (bonded_pool.try_bond_funds(&member_account, amount, BondType::Later)?, amount), BondExtra::Rewards => - (bonded_pool.try_bond_funds(&who, claimed, BondType::Later)?, claimed), + (bonded_pool.try_bond_funds(&member_account, claimed, BondType::Later)?, claimed), }; bonded_pool.ok_to_be_open()?; @@ -3149,12 +3185,12 @@ impl Pallet { member.points.checked_add(&points_issued).ok_or(Error::::OverflowRisk)?; Self::deposit_event(Event::::Bonded { - member: who.clone(), + member: member_account.clone(), pool_id: member.pool_id, bonded, joined: false, }); - Self::put_member_with_pools(&who, member, bonded_pool, reward_pool); + Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool); Ok(()) } @@ -3203,18 +3239,27 @@ impl Pallet { Ok(()) } - fn do_claim_payout(signer: T::AccountId, who: T::AccountId) -> DispatchResult { - if signer != who { + pub(crate) fn do_claim_payout( + signer: T::AccountId, + member_account: T::AccountId, + ) -> DispatchResult { + if signer != member_account { ensure!( - ClaimPermissions::::get(&who).can_claim_payout(), + ClaimPermissions::::get(&member_account).can_claim_payout(), Error::::DoesNotHavePermission ); } - let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&who)?; - - let _ = Self::do_reward_payout(&who, &mut member, &mut bonded_pool, &mut reward_pool)?; + let (mut member, mut bonded_pool, mut reward_pool) = + Self::get_member_with_pools(&member_account)?; + + let _ = Self::do_reward_payout( + &member_account, + &mut member, + &mut bonded_pool, + &mut reward_pool, + )?; - Self::put_member_with_pools(&who, member, bonded_pool, reward_pool); + Self::put_member_with_pools(&member_account, member, bonded_pool, reward_pool); Ok(()) } @@ -3598,4 +3643,14 @@ impl sp_staking::OnStakingUpdate> for Pall } Self::deposit_event(Event::::PoolSlashed { pool_id, balance: slashed_bonded }); } + + /// Reduces the overall `TotalValueLocked` if a withdrawal happened for a pool involved in the + /// staking withdraw. + fn on_withdraw(pool_account: &T::AccountId, amount: BalanceOf) { + if ReversePoolIdLookup::::get(pool_account).is_some() { + TotalValueLocked::::mutate(|tvl| { + tvl.saturating_reduce(amount); + }); + } + } } diff --git a/substrate/frame/nomination-pools/src/migration.rs b/substrate/frame/nomination-pools/src/migration.rs index 3adfd926d95cf7e90994597263db2e17d36f3978..14410339f59a4dc809187feb6d289b16ec52f79c 100644 --- a/substrate/frame/nomination-pools/src/migration.rs +++ b/substrate/frame/nomination-pools/src/migration.rs @@ -56,27 +56,63 @@ pub mod versioned { >; } -pub mod v8 { +pub mod unversioned { use super::*; - #[derive(Decode)] - pub struct OldCommission { - pub current: Option<(Perbill, T::AccountId)>, - pub max: Option, - pub change_rate: Option>>, - pub throttle_from: Option>, - } + /// Checks and updates `TotalValueLocked` if out of sync. + pub struct TotalValueLockedSync(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for TotalValueLockedSync { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + Ok(Vec::new()) + } - #[derive(Decode)] - pub struct OldBondedPoolInner { - pub commission: OldCommission, - pub member_counter: u32, - pub points: BalanceOf, - pub roles: PoolRoles, - pub state: PoolState, + fn on_runtime_upgrade() -> Weight { + let migrated = BondedPools::::count(); + + // recalcuate the `TotalValueLocked` to compare with the current on-chain TVL which may + // be out of sync. + let tvl: BalanceOf = helpers::calculate_tvl_by_total_stake::(); + let onchain_tvl = TotalValueLocked::::get(); + + let writes = if tvl != onchain_tvl { + TotalValueLocked::::set(tvl); + + log!( + info, + "on-chain TVL was out of sync, update. Old: {:?}, new: {:?}", + onchain_tvl, + tvl + ); + + // writes: onchain version + set total value locked. + 2 + } else { + log!(info, "on-chain TVL was OK: {:?}", tvl); + + // writes: onchain version write. + 1 + }; + + // reads: migrated * (BondedPools + Staking::total_stake) + count + onchain + // version + // + // writes: current version + (maybe) TVL + T::DbWeight::get() + .reads_writes(migrated.saturating_mul(2).saturating_add(2).into(), writes) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), TryRuntimeError> { + Ok(()) + } } +} - impl OldBondedPoolInner { +pub mod v8 { + use super::{v7::V7BondedPoolInner, *}; + + impl V7BondedPoolInner { fn migrate_to_v8(self) -> BondedPoolInner { BondedPoolInner { commission: Commission { @@ -104,7 +140,7 @@ pub mod v8 { fn on_runtime_upgrade() -> Weight { let mut translated = 0u64; - BondedPools::::translate::, _>(|_key, old_value| { + BondedPools::::translate::, _>(|_key, old_value| { translated.saturating_inc(); Some(old_value.migrate_to_v8()) }); @@ -128,30 +164,59 @@ pub mod v8 { /// /// WARNING: This migration works under the assumption that the [`BondedPools`] cannot be inflated /// arbitrarily. Otherwise this migration could fail due to too high weight. -mod v7 { +pub(crate) mod v7 { use super::*; - pub struct VersionUncheckedMigrateV6ToV7(sp_std::marker::PhantomData); - impl VersionUncheckedMigrateV6ToV7 { - fn calculate_tvl_by_total_stake() -> BalanceOf { - BondedPools::::iter() - .map(|(id, inner)| { - T::Staking::total_stake( - &BondedPool { id, inner: inner.clone() }.bonded_account(), - ) - .unwrap_or_default() - }) - .reduce(|acc, total_balance| acc + total_balance) - .unwrap_or_default() + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] + #[codec(mel_bound(T: Config))] + #[scale_info(skip_type_params(T))] + pub struct V7Commission { + pub current: Option<(Perbill, T::AccountId)>, + pub max: Option, + pub change_rate: Option>>, + pub throttle_from: Option>, + } + + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] + #[codec(mel_bound(T: Config))] + #[scale_info(skip_type_params(T))] + pub struct V7BondedPoolInner { + pub commission: V7Commission, + pub member_counter: u32, + pub points: BalanceOf, + pub roles: PoolRoles, + pub state: PoolState, + } + + #[allow(dead_code)] + #[derive(RuntimeDebugNoBound)] + #[cfg_attr(feature = "std", derive(Clone, PartialEq))] + pub struct V7BondedPool { + /// The identifier of the pool. + id: PoolId, + /// The inner fields. + inner: V7BondedPoolInner, + } + + impl V7BondedPool { + #[allow(dead_code)] + fn bonded_account(&self) -> T::AccountId { + Pallet::::create_bonded_account(self.id) } } + // NOTE: We cannot put a V7 prefix here since that would change the storage key. + #[frame_support::storage_alias] + pub type BondedPools = + CountedStorageMap, Twox64Concat, PoolId, V7BondedPoolInner>; + + pub struct VersionUncheckedMigrateV6ToV7(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for VersionUncheckedMigrateV6ToV7 { fn on_runtime_upgrade() -> Weight { let migrated = BondedPools::::count(); // The TVL should be the sum of all the funds that are actively staked and in the // unbonding process of the account of each pool. - let tvl: BalanceOf = Self::calculate_tvl_by_total_stake(); + let tvl: BalanceOf = helpers::calculate_tvl_by_total_stake::(); TotalValueLocked::::set(tvl); @@ -173,7 +238,7 @@ mod v7 { fn post_upgrade(_data: Vec) -> Result<(), TryRuntimeError> { // check that the `TotalValueLocked` written is actually the sum of `total_stake` of the // `BondedPools`` - let tvl: BalanceOf = Self::calculate_tvl_by_total_stake(); + let tvl: BalanceOf = helpers::calculate_tvl_by_total_stake::(); ensure!( TotalValueLocked::::get() == tvl, "TVL written is not equal to `Staking::total_stake` of all `BondedPools`." @@ -277,25 +342,25 @@ pub mod v5 { pub struct MigrateToV5(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV5 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let in_code = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", - current, + "Running migration with in-code storage version {:?} / onchain {:?}", + in_code, onchain ); - if current == 5 && onchain == 4 { + if in_code == 5 && onchain == 4 { let mut translated = 0u64; RewardPools::::translate::, _>(|_id, old_value| { translated.saturating_inc(); Some(old_value.migrate_to_v5()) }); - current.put::>(); - log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); + in_code.put::>(); + log!(info, "Upgraded {} pools, storage to version {:?}", translated, in_code); // reads: translated + onchain version. // writes: translated + current.put. @@ -433,12 +498,12 @@ pub mod v4 { #[allow(deprecated)] impl> OnRuntimeUpgrade for MigrateToV4 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); @@ -514,13 +579,13 @@ pub mod v3 { pub struct MigrateToV3(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV3 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); if onchain == 2 { log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); @@ -794,12 +859,12 @@ pub mod v2 { impl OnRuntimeUpgrade for MigrateToV2 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); @@ -911,12 +976,12 @@ pub mod v1 { pub struct MigrateToV1(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV1 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let current = Pallet::::in_code_storage_version(); let onchain = Pallet::::on_chain_storage_version(); log!( info, - "Running migration with current storage version {:?} / onchain {:?}", + "Running migration with in-code storage version {:?} / onchain {:?}", current, onchain ); @@ -952,3 +1017,17 @@ pub mod v1 { } } } + +mod helpers { + use super::*; + + pub(crate) fn calculate_tvl_by_total_stake() -> BalanceOf { + BondedPools::::iter() + .map(|(id, inner)| { + T::Staking::total_stake(&BondedPool { id, inner: inner.clone() }.bonded_account()) + .unwrap_or_default() + }) + .reduce(|acc, total_balance| acc + total_balance) + .unwrap_or_default() + } +} diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index cd91fbb33c1dc5d58aa4e5d92ae8467addd08812..f982b72c63564c0cff6106ded6ad934fec10f48c 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -134,11 +134,24 @@ impl sp_staking::StakingInterface for StakingMock { fn withdraw_unbonded(who: Self::AccountId, _: u32) -> Result { let mut unbonding_map = UnbondingBalanceMap::get(); + + // closure to calculate the current unlocking funds across all eras/accounts. + let unlocking = |pair: &Vec<(EraIndex, Balance)>| -> Balance { + pair.iter() + .try_fold(Zero::zero(), |acc: Balance, (_at, amount)| acc.checked_add(*amount)) + .unwrap() + }; + let staker_map = unbonding_map.get_mut(&who).ok_or("Nothing to unbond")?; + let unlocking_before = unlocking(&staker_map); let current_era = Self::current_era(); + staker_map.retain(|(unlocking_at, _amount)| *unlocking_at > current_era); + // if there was a withdrawal, notify the pallet. + Pools::on_withdraw(&who, unlocking_before.saturating_sub(unlocking(&staker_map))); + UnbondingBalanceMap::set(&unbonding_map); Ok(UnbondingBalanceMap::get().is_empty() && BondedBalanceMap::get().is_empty()) } @@ -254,7 +267,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<1>; type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } pub struct BalanceToU256; diff --git a/substrate/frame/nomination-pools/src/tests.rs b/substrate/frame/nomination-pools/src/tests.rs index 657b50e8594aa4f41bacacc301a6049ea703e30b..8fb2b41b88a190df444162a58d0a134936d81272 100644 --- a/substrate/frame/nomination-pools/src/tests.rs +++ b/substrate/frame/nomination-pools/src/tests.rs @@ -23,7 +23,7 @@ use sp_runtime::{bounded_btree_map, traits::Dispatchable, FixedU128}; macro_rules! unbonding_pools_with_era { ($($k:expr => $v:expr),* $(,)?) => {{ - use sp_std::iter::{Iterator, IntoIterator}; + use ::core::iter::{Iterator, IntoIterator}; let not_bounded: BTreeMap<_, _> = Iterator::collect(IntoIterator::into_iter([$(($k, $v),)*])); BoundedBTreeMap::, TotalUnbondingPools>::try_from(not_bounded).unwrap() }}; @@ -1182,6 +1182,12 @@ mod claim_payout { assert_eq!(payout, 0); assert_eq!(member, del(0.0)); assert_eq!(reward_pool, rew(0, 0, 0)); + Pools::put_member_with_pools( + &10, + member.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // Given the pool has earned some rewards for the first time deposit_rewards(5); @@ -1203,6 +1209,12 @@ mod claim_payout { assert_eq!(payout, 5); assert_eq!(reward_pool, rew(0, 0, 5)); assert_eq!(member, del(0.5)); + Pools::put_member_with_pools( + &10, + member.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // Given the pool has earned rewards again deposit_rewards(10); @@ -1220,6 +1232,12 @@ mod claim_payout { assert_eq!(payout, 10); assert_eq!(reward_pool, rew(0, 0, 15)); assert_eq!(member, del(1.5)); + Pools::put_member_with_pools( + &10, + member.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // Given the pool has earned no new rewards Currency::set_balance(&default_reward_account(), ed); @@ -1234,6 +1252,12 @@ mod claim_payout { assert_eq!(payout, 0); assert_eq!(reward_pool, rew(0, 0, 15)); assert_eq!(member, del(1.5)); + Pools::put_member_with_pools( + &10, + member.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); }); } @@ -1279,6 +1303,12 @@ mod claim_payout { assert_eq!(payout, 10); assert_eq!(del_10, del(10, 1)); assert_eq!(reward_pool, rew(0, 0, 10)); + Pools::put_member_with_pools( + &10, + del_10.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // When let payout = @@ -1293,6 +1323,12 @@ mod claim_payout { assert_eq!(payout, 40); assert_eq!(del_40, del(40, 1)); assert_eq!(reward_pool, rew(0, 0, 50)); + Pools::put_member_with_pools( + &40, + del_40.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // When let payout = @@ -1307,6 +1343,12 @@ mod claim_payout { assert_eq!(payout, 50); assert_eq!(del_50, del(50, 1)); assert_eq!(reward_pool, rew(0, 0, 100)); + Pools::put_member_with_pools( + &50, + del_50.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // Given the reward pool has some new rewards deposit_rewards(50); @@ -1324,6 +1366,12 @@ mod claim_payout { assert_eq!(payout, 5); assert_eq!(del_10, del_float(10, 1.5)); assert_eq!(reward_pool, rew(0, 0, 105)); + Pools::put_member_with_pools( + &10, + del_10.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // When let payout = @@ -1338,6 +1386,12 @@ mod claim_payout { assert_eq!(payout, 20); assert_eq!(del_40, del_float(40, 1.5)); assert_eq!(reward_pool, rew(0, 0, 125)); + Pools::put_member_with_pools( + &40, + del_40.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // Given del_50 hasn't claimed and the reward pools has just earned 50 deposit_rewards(50); @@ -1355,6 +1409,12 @@ mod claim_payout { assert_eq!(payout, 50); assert_eq!(del_50, del_float(50, 2.0)); assert_eq!(reward_pool, rew(0, 0, 175)); + Pools::put_member_with_pools( + &50, + del_50.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // When let payout = @@ -1369,6 +1429,12 @@ mod claim_payout { assert_eq!(payout, 5); assert_eq!(del_10, del_float(10, 2.0)); assert_eq!(reward_pool, rew(0, 0, 180)); + Pools::put_member_with_pools( + &10, + del_10.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // Given del_40 hasn't claimed and the reward pool has just earned 400 deposit_rewards(400); @@ -1386,6 +1452,12 @@ mod claim_payout { assert_eq!(payout, 40); assert_eq!(del_10, del_float(10, 6.0)); assert_eq!(reward_pool, rew(0, 0, 220)); + Pools::put_member_with_pools( + &10, + del_10.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // Given del_40 + del_50 haven't claimed and the reward pool has earned 20 deposit_rewards(20); @@ -1399,6 +1471,12 @@ mod claim_payout { assert_eq!(payout, 2); assert_eq!(del_10, del_float(10, 6.2)); assert_eq!(reward_pool, rew(0, 0, 222)); + Pools::put_member_with_pools( + &10, + del_10.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // When let payout = @@ -1409,6 +1487,12 @@ mod claim_payout { assert_eq!(payout, 188); // 20 (from the 50) + 160 (from the 400) + 8 (from the 20) assert_eq!(del_40, del_float(40, 6.2)); assert_eq!(reward_pool, rew(0, 0, 410)); + Pools::put_member_with_pools( + &40, + del_40.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); // When let payout = @@ -1419,6 +1503,12 @@ mod claim_payout { assert_eq!(payout, 210); // 200 (from the 400) + 10 (from the 20) assert_eq!(del_50, del_float(50, 6.2)); assert_eq!(reward_pool, rew(0, 0, 620)); + Pools::put_member_with_pools( + &50, + del_50.clone(), + bonded_pool.clone(), + reward_pool.clone(), + ); }); } @@ -2490,6 +2580,38 @@ mod unbond { }) } + #[test] + fn member_unbond_destroying_with_pending_rewards() { + ExtBuilder::default() + .min_join_bond(10) + .add_members(vec![(20, 20)]) + .build_and_execute(|| { + unsafe_set_state(1, PoolState::Destroying); + let random = 123; + + // given the pool some pending rewards. + assert_eq!(pending_rewards_for_delegator(20), 0); + deposit_rewards(10); + assert_eq!(pending_rewards_for_delegator(20), 6); + + // any random user can unbond 20 now. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(random), 20, 20)); + assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::PaidOut { member: 20, pool_id: 1, payout: 6 }, + Event::Unbonded { member: 20, pool_id: 1, balance: 20, points: 20, era: 3 } + ] + ); + }) + } + #[test] fn depositor_unbond_open() { // depositor in pool, pool state open @@ -4726,6 +4848,18 @@ mod nominate { Error::::NotNominator ); + // if `depositor` stake is less than the `MinimumNominatorBond`, they can't nominate + StakingMinBond::set(20); + + // Can't nominate if depositor's stake is less than the `MinimumNominatorBond` + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21]), + Error::::MinimumBondNotMet + ); + + // restore `MinimumNominatorBond` + StakingMinBond::set(10); + // Root can nominate assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21])); assert_eq!(Nominations::get().unwrap(), vec![21]); @@ -7216,3 +7350,33 @@ mod slash { }); } } + +mod chill { + use super::*; + + #[test] + fn chill_works() { + ExtBuilder::default().build_and_execute(|| { + // only nominator or root can chill + assert_noop!( + Pools::chill(RuntimeOrigin::signed(10), 1), + Error::::NotNominator + ); + + // root can chill and re-nominate + assert_ok!(Pools::chill(RuntimeOrigin::signed(900), 1)); + assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![31])); + + // nominator can chill and re-nominate + assert_ok!(Pools::chill(RuntimeOrigin::signed(901), 1)); + assert_ok!(Pools::nominate(RuntimeOrigin::signed(901), 1, vec![31])); + + // if `depositor` stake is less than the `MinimumNominatorBond`, then this call + // becomes permissionless; + StakingMinBond::set(20); + + // any account can chill + assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1)); + }) + } +} diff --git a/substrate/frame/nomination-pools/src/weights.rs b/substrate/frame/nomination-pools/src/weights.rs index 047a17c3f9a278ac9564562edf318f13ecc2f7ca..987ddd7170310a315ad6ad93dc43062b60f563ac 100644 --- a/substrate/frame/nomination-pools/src/weights.rs +++ b/substrate/frame/nomination-pools/src/weights.rs @@ -17,10 +17,10 @@ //! Autogenerated weights for `pallet_nomination_pools` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-07, 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-p5qp1txx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -112,8 +112,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3425` // Estimated: `8877` - // Minimum execution time: 184_295_000 picoseconds. - Weight::from_parts(188_860_000, 8877) + // Minimum execution time: 182_643_000 picoseconds. + Weight::from_parts(186_106_000, 8877) .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -145,8 +145,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3435` // Estimated: `8877` - // Minimum execution time: 188_777_000 picoseconds. - Weight::from_parts(192_646_000, 8877) + // Minimum execution time: 181_464_000 picoseconds. + Weight::from_parts(184_672_000, 8877) .saturating_add(T::DbWeight::get().reads(17_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -180,8 +180,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3500` // Estimated: `8877` - // Minimum execution time: 221_728_000 picoseconds. - Weight::from_parts(227_569_000, 8877) + // Minimum execution time: 216_182_000 picoseconds. + Weight::from_parts(218_448_000, 8877) .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(14_u64)) } @@ -201,8 +201,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1172` // Estimated: `3719` - // Minimum execution time: 75_310_000 picoseconds. - Weight::from_parts(77_709_000, 3719) + // Minimum execution time: 73_830_000 picoseconds. + Weight::from_parts(75_271_000, 3719) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -242,8 +242,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3622` // Estimated: `27847` - // Minimum execution time: 170_656_000 picoseconds. - Weight::from_parts(174_950_000, 27847) + // Minimum execution time: 166_099_000 picoseconds. + Weight::from_parts(170_355_000, 27847) .saturating_add(T::DbWeight::get().reads(20_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -259,18 +259,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::TotalValueLocked` (r:1 w:1) /// Proof: `NominationPools::TotalValueLocked` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1817` + // Measured: `1848` // Estimated: `4764` - // Minimum execution time: 68_866_000 picoseconds. - Weight::from_parts(72_312_887, 4764) - // Standard Error: 1_635 - .saturating_add(Weight::from_parts(41_679, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(7_u64)) + // Minimum execution time: 64_787_000 picoseconds. + Weight::from_parts(67_920_914, 4764) + // Standard Error: 1_482 + .saturating_add(Weight::from_parts(43_264, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -291,6 +293,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::TotalValueLocked` (r:1 w:1) /// Proof: `NominationPools::TotalValueLocked` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `NominationPools::CounterForPoolMembers` (r:1 w:1) @@ -300,13 +304,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2207` + // Measured: `2238` // Estimated: `27847` - // Minimum execution time: 131_383_000 picoseconds. - Weight::from_parts(136_595_971, 27847) - // Standard Error: 2_715 - .saturating_add(Weight::from_parts(52_351, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Minimum execution time: 124_990_000 picoseconds. + Weight::from_parts(129_041_398, 27847) + // Standard Error: 2_335 + .saturating_add(Weight::from_parts(67_889, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(9_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -333,12 +337,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:1) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::TotalValueLocked` (r:1 w:1) /// Proof: `NominationPools::TotalValueLocked` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `NominationPools::CounterForPoolMembers` (r:1 w:1) /// Proof: `NominationPools::CounterForPoolMembers` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:1) - /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::CounterForReversePoolIdLookup` (r:1 w:1) /// Proof: `NominationPools::CounterForReversePoolIdLookup` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `NominationPools::RewardPools` (r:1 w:1) @@ -356,12 +360,14 @@ impl WeightInfo for SubstrateWeight { /// Storage: `NominationPools::ClaimPermissions` (r:0 w:1) /// Proof: `NominationPools::ClaimPermissions` (`max_values`: None, `max_size`: Some(41), added: 2516, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2525` // Estimated: `27847` - // Minimum execution time: 233_314_000 picoseconds. - Weight::from_parts(241_694_316, 27847) + // Minimum execution time: 221_955_000 picoseconds. + Weight::from_parts(230_244_437, 27847) + // Standard Error: 4_059 + .saturating_add(Weight::from_parts(7_522, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(24_u64)) .saturating_add(T::DbWeight::get().writes(20_u64)) } @@ -413,19 +419,25 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1169` // Estimated: `8538` - // Minimum execution time: 171_465_000 picoseconds. - Weight::from_parts(176_478_000, 8538) + // Minimum execution time: 165_358_000 picoseconds. + Weight::from_parts(169_683_000, 8538) .saturating_add(T::DbWeight::get().reads(23_u64)) .saturating_add(T::DbWeight::get().writes(17_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinCreateBond` (r:1 w:0) + /// Proof: `NominationPools::MinCreateBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinJoinBond` (r:1 w:0) + /// Proof: `NominationPools::MinJoinBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:1 w:0) @@ -445,13 +457,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1808` + // Measured: `1976` // Estimated: `4556 + n * (2520 ±0)` - // Minimum execution time: 63_588_000 picoseconds. - Weight::from_parts(64_930_584, 4556) - // Standard Error: 9_167 - .saturating_add(Weight::from_parts(1_595_779, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(12_u64)) + // Minimum execution time: 74_483_000 picoseconds. + Weight::from_parts(76_611_288, 4556) + // Standard Error: 9_013 + .saturating_add(Weight::from_parts(1_475_128, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) @@ -466,8 +478,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1434` // Estimated: `4556` - // Minimum execution time: 32_899_000 picoseconds. - Weight::from_parts(33_955_000, 4556) + // Minimum execution time: 32_598_000 picoseconds. + Weight::from_parts(33_350_000, 4556) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -482,10 +494,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3735` - // Minimum execution time: 13_778_000 picoseconds. - Weight::from_parts(14_770_006, 3735) - // Standard Error: 151 - .saturating_add(Weight::from_parts(1_900, 0).saturating_mul(n.into())) + // Minimum execution time: 13_705_000 picoseconds. + Weight::from_parts(14_594_211, 3735) + // Standard Error: 141 + .saturating_add(Weight::from_parts(2_119, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -505,8 +517,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_550_000 picoseconds. - Weight::from_parts(4_935_000, 0) + // Minimum execution time: 4_222_000 picoseconds. + Weight::from_parts(4_527_000, 0) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -515,17 +527,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_759_000 picoseconds. - Weight::from_parts(17_346_000, 3719) + // Minimum execution time: 16_106_000 picoseconds. + Weight::from_parts(17_365_000, 3719) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::MinNominatorBond` (r:1 w:0) + /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -540,11 +556,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1971` + // Measured: `2143` // Estimated: `4556` - // Minimum execution time: 61_970_000 picoseconds. - Weight::from_parts(63_738_000, 4556) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Minimum execution time: 71_301_000 picoseconds. + Weight::from_parts(73_626_000, 4556) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -559,8 +575,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `804` // Estimated: `3719` - // Minimum execution time: 31_950_000 picoseconds. - Weight::from_parts(33_190_000, 3719) + // Minimum execution time: 32_363_000 picoseconds. + Weight::from_parts(33_400_000, 3719) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -572,8 +588,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `572` // Estimated: `3719` - // Minimum execution time: 16_807_000 picoseconds. - Weight::from_parts(17_733_000, 3719) + // Minimum execution time: 16_686_000 picoseconds. + Weight::from_parts(17_294_000, 3719) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -583,8 +599,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_710_000 picoseconds. - Weight::from_parts(17_563_000, 3719) + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_028_000, 3719) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -594,8 +610,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_493_000 picoseconds. - Weight::from_parts(17_022_000, 3719) + // Minimum execution time: 16_350_000 picoseconds. + Weight::from_parts(16_905_000, 3719) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -607,8 +623,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `542` // Estimated: `3702` - // Minimum execution time: 14_248_000 picoseconds. - Weight::from_parts(15_095_000, 3702) + // Minimum execution time: 14_081_000 picoseconds. + Weight::from_parts(14_608_000, 3702) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -624,8 +640,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1002` // Estimated: `3719` - // Minimum execution time: 61_969_000 picoseconds. - Weight::from_parts(63_965_000, 3719) + // Minimum execution time: 62_178_000 picoseconds. + Weight::from_parts(64_039_000, 3719) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -641,8 +657,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `901` // Estimated: `4764` - // Minimum execution time: 65_462_000 picoseconds. - Weight::from_parts(67_250_000, 4764) + // Minimum execution time: 64_880_000 picoseconds. + Weight::from_parts(66_541_000, 4764) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -686,8 +702,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3425` // Estimated: `8877` - // Minimum execution time: 184_295_000 picoseconds. - Weight::from_parts(188_860_000, 8877) + // Minimum execution time: 182_643_000 picoseconds. + Weight::from_parts(186_106_000, 8877) .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -719,8 +735,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3435` // Estimated: `8877` - // Minimum execution time: 188_777_000 picoseconds. - Weight::from_parts(192_646_000, 8877) + // Minimum execution time: 181_464_000 picoseconds. + Weight::from_parts(184_672_000, 8877) .saturating_add(RocksDbWeight::get().reads(17_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -754,8 +770,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3500` // Estimated: `8877` - // Minimum execution time: 221_728_000 picoseconds. - Weight::from_parts(227_569_000, 8877) + // Minimum execution time: 216_182_000 picoseconds. + Weight::from_parts(218_448_000, 8877) .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(14_u64)) } @@ -775,8 +791,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1172` // Estimated: `3719` - // Minimum execution time: 75_310_000 picoseconds. - Weight::from_parts(77_709_000, 3719) + // Minimum execution time: 73_830_000 picoseconds. + Weight::from_parts(75_271_000, 3719) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -816,8 +832,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3622` // Estimated: `27847` - // Minimum execution time: 170_656_000 picoseconds. - Weight::from_parts(174_950_000, 27847) + // Minimum execution time: 166_099_000 picoseconds. + Weight::from_parts(170_355_000, 27847) .saturating_add(RocksDbWeight::get().reads(20_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -833,18 +849,20 @@ impl WeightInfo for () { /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::TotalValueLocked` (r:1 w:1) /// Proof: `NominationPools::TotalValueLocked` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1817` + // Measured: `1848` // Estimated: `4764` - // Minimum execution time: 68_866_000 picoseconds. - Weight::from_parts(72_312_887, 4764) - // Standard Error: 1_635 - .saturating_add(Weight::from_parts(41_679, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(7_u64)) + // Minimum execution time: 64_787_000 picoseconds. + Weight::from_parts(67_920_914, 4764) + // Standard Error: 1_482 + .saturating_add(Weight::from_parts(43_264, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -865,6 +883,8 @@ impl WeightInfo for () { /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::TotalValueLocked` (r:1 w:1) /// Proof: `NominationPools::TotalValueLocked` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `NominationPools::CounterForPoolMembers` (r:1 w:1) @@ -874,13 +894,13 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2207` + // Measured: `2238` // Estimated: `27847` - // Minimum execution time: 131_383_000 picoseconds. - Weight::from_parts(136_595_971, 27847) - // Standard Error: 2_715 - .saturating_add(Weight::from_parts(52_351, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Minimum execution time: 124_990_000 picoseconds. + Weight::from_parts(129_041_398, 27847) + // Standard Error: 2_335 + .saturating_add(Weight::from_parts(67_889, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(9_u64)) } /// Storage: `NominationPools::PoolMembers` (r:1 w:1) @@ -907,12 +927,12 @@ impl WeightInfo for () { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:0) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:1) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::TotalValueLocked` (r:1 w:1) /// Proof: `NominationPools::TotalValueLocked` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `NominationPools::CounterForPoolMembers` (r:1 w:1) /// Proof: `NominationPools::CounterForPoolMembers` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:1) - /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// Storage: `NominationPools::CounterForReversePoolIdLookup` (r:1 w:1) /// Proof: `NominationPools::CounterForReversePoolIdLookup` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `NominationPools::RewardPools` (r:1 w:1) @@ -930,12 +950,14 @@ impl WeightInfo for () { /// Storage: `NominationPools::ClaimPermissions` (r:0 w:1) /// Proof: `NominationPools::ClaimPermissions` (`max_values`: None, `max_size`: Some(41), added: 2516, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `2525` // Estimated: `27847` - // Minimum execution time: 233_314_000 picoseconds. - Weight::from_parts(241_694_316, 27847) + // Minimum execution time: 221_955_000 picoseconds. + Weight::from_parts(230_244_437, 27847) + // Standard Error: 4_059 + .saturating_add(Weight::from_parts(7_522, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(24_u64)) .saturating_add(RocksDbWeight::get().writes(20_u64)) } @@ -987,19 +1009,25 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1169` // Estimated: `8538` - // Minimum execution time: 171_465_000 picoseconds. - Weight::from_parts(176_478_000, 8538) + // Minimum execution time: 165_358_000 picoseconds. + Weight::from_parts(169_683_000, 8538) .saturating_add(RocksDbWeight::get().reads(23_u64)) .saturating_add(RocksDbWeight::get().writes(17_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::MinNominatorBond` (r:1 w:0) /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinCreateBond` (r:1 w:0) + /// Proof: `NominationPools::MinCreateBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::MinJoinBond` (r:1 w:0) + /// Proof: `NominationPools::MinJoinBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) /// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:1 w:0) @@ -1019,13 +1047,13 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1808` + // Measured: `1976` // Estimated: `4556 + n * (2520 ±0)` - // Minimum execution time: 63_588_000 picoseconds. - Weight::from_parts(64_930_584, 4556) - // Standard Error: 9_167 - .saturating_add(Weight::from_parts(1_595_779, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(12_u64)) + // Minimum execution time: 74_483_000 picoseconds. + Weight::from_parts(76_611_288, 4556) + // Standard Error: 9_013 + .saturating_add(Weight::from_parts(1_475_128, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into())) @@ -1040,8 +1068,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1434` // Estimated: `4556` - // Minimum execution time: 32_899_000 picoseconds. - Weight::from_parts(33_955_000, 4556) + // Minimum execution time: 32_598_000 picoseconds. + Weight::from_parts(33_350_000, 4556) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1056,10 +1084,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3735` - // Minimum execution time: 13_778_000 picoseconds. - Weight::from_parts(14_770_006, 3735) - // Standard Error: 151 - .saturating_add(Weight::from_parts(1_900, 0).saturating_mul(n.into())) + // Minimum execution time: 13_705_000 picoseconds. + Weight::from_parts(14_594_211, 3735) + // Standard Error: 141 + .saturating_add(Weight::from_parts(2_119, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1079,8 +1107,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_550_000 picoseconds. - Weight::from_parts(4_935_000, 0) + // Minimum execution time: 4_222_000 picoseconds. + Weight::from_parts(4_527_000, 0) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -1089,17 +1117,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_759_000 picoseconds. - Weight::from_parts(17_346_000, 3719) + // Minimum execution time: 16_106_000 picoseconds. + Weight::from_parts(17_365_000, 3719) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:0) /// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::PoolMembers` (r:1 w:0) + /// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:0) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Staking::MinNominatorBond` (r:1 w:0) + /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -1114,11 +1146,11 @@ impl WeightInfo for () { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1971` + // Measured: `2143` // Estimated: `4556` - // Minimum execution time: 61_970_000 picoseconds. - Weight::from_parts(63_738_000, 4556) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Minimum execution time: 71_301_000 picoseconds. + Weight::from_parts(73_626_000, 4556) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: `NominationPools::BondedPools` (r:1 w:1) @@ -1133,8 +1165,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `804` // Estimated: `3719` - // Minimum execution time: 31_950_000 picoseconds. - Weight::from_parts(33_190_000, 3719) + // Minimum execution time: 32_363_000 picoseconds. + Weight::from_parts(33_400_000, 3719) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1146,8 +1178,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `572` // Estimated: `3719` - // Minimum execution time: 16_807_000 picoseconds. - Weight::from_parts(17_733_000, 3719) + // Minimum execution time: 16_686_000 picoseconds. + Weight::from_parts(17_294_000, 3719) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1157,8 +1189,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_710_000 picoseconds. - Weight::from_parts(17_563_000, 3719) + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_028_000, 3719) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1168,8 +1200,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `532` // Estimated: `3719` - // Minimum execution time: 16_493_000 picoseconds. - Weight::from_parts(17_022_000, 3719) + // Minimum execution time: 16_350_000 picoseconds. + Weight::from_parts(16_905_000, 3719) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1181,8 +1213,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `542` // Estimated: `3702` - // Minimum execution time: 14_248_000 picoseconds. - Weight::from_parts(15_095_000, 3702) + // Minimum execution time: 14_081_000 picoseconds. + Weight::from_parts(14_608_000, 3702) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1198,8 +1230,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1002` // Estimated: `3719` - // Minimum execution time: 61_969_000 picoseconds. - Weight::from_parts(63_965_000, 3719) + // Minimum execution time: 62_178_000 picoseconds. + Weight::from_parts(64_039_000, 3719) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1215,8 +1247,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `901` // Estimated: `4764` - // Minimum execution time: 65_462_000 picoseconds. - Weight::from_parts(67_250_000, 4764) + // Minimum execution time: 64_880_000 picoseconds. + Weight::from_parts(66_541_000, 4764) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/substrate/frame/nomination-pools/test-staking/Cargo.toml b/substrate/frame/nomination-pools/test-staking/Cargo.toml index 845535ae04f567bbe5fa4c4c7f22854b077176b5..9c7b12e4c6345b2080ef4b989171929c505c8a40 100644 --- a/substrate/frame/nomination-pools/test-staking/Cargo.toml +++ b/substrate/frame/nomination-pools/test-staking/Cargo.toml @@ -37,4 +37,4 @@ pallet-staking-reward-curve = { path = "../../staking/reward-curve" } pallet-nomination-pools = { path = ".." } sp-tracing = { path = "../../../primitives/tracing" } -log = { version = "0.4.0" } +log = { workspace = true, default-features = true } diff --git a/substrate/frame/nomination-pools/test-staking/src/lib.rs b/substrate/frame/nomination-pools/test-staking/src/lib.rs index 9108da510a3a5339999046e772fecaa30cd11baa..98bff537c90819be34625d099cb55abcaafb10ae 100644 --- a/substrate/frame/nomination-pools/test-staking/src/lib.rs +++ b/substrate/frame/nomination-pools/test-staking/src/lib.rs @@ -22,10 +22,12 @@ mod mock; use frame_support::{assert_noop, assert_ok, traits::Currency}; use mock::*; use pallet_nomination_pools::{ - BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, PoolMembers, - PoolState, + BondExtra, BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, + PoolMembers, PoolState, +}; +use pallet_staking::{ + CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination, }; -use pallet_staking::{CurrentEra, Event as StakingEvent, Payee, RewardDestination}; use sp_runtime::{bounded_btree_map, traits::Zero}; #[test] @@ -191,6 +193,131 @@ fn pool_lifecycle_e2e() { }) } +#[test] +fn pool_chill_e2e() { + new_test_ext().execute_with(|| { + assert_eq!(Balances::minimum_balance(), 5); + assert_eq!(Staking::current_era(), None); + + // create the pool, we know this has id 1. + 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 }, + ] + ); + + // have two members join + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, + 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 }, + PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true }, + ] + ); + + // in case depositor does not have more than `MinNominatorBond` staked, we can end up in + // situation where a member unbonding would cause pool balance to drop below + // `MinNominatorBond` and hence not allowed. This can happen if the `MinNominatorBond` is + // increased after the pool is created. + assert_ok!(Staking::set_staking_configs( + RuntimeOrigin::root(), + pallet_staking::ConfigOp::Set(55), // minimum nominator bond + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, + )); + + // members can unbond as long as total stake of the pool is above min nominator bond + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10),); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_eras.len(), 1); + assert_eq!(PoolMembers::::get(20).unwrap().points, 0); + + // this member cannot unbond since it will cause `pool stake < MinNominatorBond` + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(21), 21, 10), + StakingError::::InsufficientBond, + ); + + // members can call `chill` permissionlessly now + assert_ok!(Pools::chill(RuntimeOrigin::signed(20), 1)); + + // now another member can unbond. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); + assert_eq!(PoolMembers::::get(21).unwrap().unbonding_eras.len(), 1); + assert_eq!(PoolMembers::::get(21).unwrap().points, 0); + + // nominator can not resume nomination until depositor have enough stake + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), + PoolsError::::MinimumBondNotMet, + ); + + // other members joining pool does not affect the depositor's ability to resume nomination + assert_ok!(Pools::join(RuntimeOrigin::signed(22), 10, 1)); + + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), + PoolsError::::MinimumBondNotMet, + ); + + // depositor can bond extra stake + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + + // `chill` can not be called permissionlessly anymore + assert_noop!( + Pools::chill(RuntimeOrigin::signed(20), 1), + PoolsError::::NotNominator, + ); + + // now nominator can resume nomination + assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); + + // skip to make the unbonding period end. + CurrentEra::::set(Some(BondingDuration::get())); + + // members can now withdraw. + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Chilled { stash: POOL1_BONDED }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // other member bonding + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // depositor bond extra + StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 }, + ] + ); + }) +} + #[test] fn pool_slash_e2e() { new_test_ext().execute_with(|| { @@ -214,7 +341,10 @@ fn pool_slash_e2e() { ] ); - assert_eq!(Payee::::get(POOL1_BONDED), RewardDestination::Account(POOL1_REWARD)); + assert_eq!( + Payee::::get(POOL1_BONDED), + Some(RewardDestination::Account(POOL1_REWARD)) + ); // have two members join assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index cbd7a4d07589af2a68e755caca1be1c588fb28fa..ce97e13d640b63ed7da25ad36f8d2625e58d3c50 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -90,7 +90,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<1>; type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } pallet_staking_reward_curve::build! { @@ -237,6 +236,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pallet_staking::ConfigOp::Noop, pallet_staking::ConfigOp::Noop, pallet_staking::ConfigOp::Noop, + pallet_staking::ConfigOp::Noop, )); }); diff --git a/substrate/frame/offences/Cargo.toml b/substrate/frame/offences/Cargo.toml index cda247325ef0559c9e32eca4e6e8d1bb011a5b85..b3ef4671ce56f4b66d7e7cb4ce7ec7faec05d3fe 100644 --- a/substrate/frame/offences/Cargo.toml +++ b/substrate/frame/offences/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-offences" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,9 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } pallet-balances = { path = "../balances", default-features = false } diff --git a/substrate/frame/offences/benchmarking/Cargo.toml b/substrate/frame/offences/benchmarking/Cargo.toml index cddbd6aa4d5ebebd1b259d2b8b18cc9b70ba1a6d..8dcce84d257e596609e4062f8a46d684a72b2683 100644 --- a/substrate/frame/offences/benchmarking/Cargo.toml +++ b/substrate/frame/offences/benchmarking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-offences-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -32,7 +32,7 @@ pallet-staking = { path = "../../staking", default-features = false } sp-runtime = { path = "../../../primitives/runtime", default-features = false } sp-staking = { path = "../../../primitives/staking", default-features = false } sp-std = { path = "../../../primitives/std", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] pallet-staking-reward-curve = { path = "../../staking/reward-curve" } diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs index 38c6fb6ea5f6e948dae40c86c83902e460f698d6..b3260f0841f06490246a0c0f079864d5f1224a0e 100644 --- a/substrate/frame/offences/benchmarking/src/mock.rs +++ b/substrate/frame/offences/benchmarking/src/mock.rs @@ -81,7 +81,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_timestamp::Config for Test { @@ -149,7 +148,7 @@ parameter_types! { pub static ElectionsBounds: ElectionBounds = ElectionBoundsBuilder::default().build(); } -pub type Extrinsic = sp_runtime::testing::TestXt; +pub type Extrinsic = sp_runtime::generic::UncheckedExtrinsic; pub struct OnChainSeqPhragmen; impl onchain::Config for OnChainSeqPhragmen { diff --git a/substrate/frame/paged-list/Cargo.toml b/substrate/frame/paged-list/Cargo.toml index 2370f84898ba12e905f459d8e5c3a5455b806ef6..6a2af120f32a914452fd67279a18f8cabe54025b 100644 --- a/substrate/frame/paged-list/Cargo.toml +++ b/substrate/frame/paged-list/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-paged-list" -version = "0.1.0" +version = "0.6.0" description = "FRAME pallet that provides a paged list data structure." authors.workspace = true homepage = "https://substrate.io" @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -docify = "0.2.6" +docify = "0.2.7" scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } diff --git a/substrate/frame/paged-list/fuzzer/Cargo.toml b/substrate/frame/paged-list/fuzzer/Cargo.toml index 5c245cc72c713975459eb025bea3464e3b819bbb..6ff07ba1ddd2fa3571e65a8e29c53e5ef324c0de 100644 --- a/substrate/frame/paged-list/fuzzer/Cargo.toml +++ b/substrate/frame/paged-list/fuzzer/Cargo.toml @@ -17,7 +17,7 @@ name = "pallet-paged-list-fuzzer" path = "src/paged_list.rs" [dependencies] -arbitrary = "1.3.0" +arbitrary = "1.3.2" honggfuzz = "0.5.49" frame-support = { path = "../../support", default-features = false, features = ["std"] } diff --git a/substrate/frame/parameters/Cargo.toml b/substrate/frame/parameters/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..07ebeea52d5298be0db8b00c09ab73641d20080b --- /dev/null +++ b/substrate/frame/parameters/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "pallet-parameters" +description = "Pallet to store and configure parameters." +repository.workspace = true +license = "Apache-2.0" +version = "0.0.1" +authors = ["Acala Developers", "Parity Technologies "] +edition.workspace = true + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["max-encoded-len"] } +scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } +paste = { version = "1.0.14", default-features = false } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } +docify = "0.2.5" + +frame-support = { path = "../support", default-features = false, features = ["experimental"] } +frame-system = { path = "../system", default-features = false } +sp-core = { path = "../../primitives/core", default-features = false } +sp-runtime = { path = "../../primitives/runtime", default-features = false } +sp-std = { path = "../../primitives/std", default-features = false } +frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } + +[dev-dependencies] +sp-core = { path = "../../primitives/core", features = ["std"] } +sp-io = { path = "../../primitives/io", features = ["std"] } +pallet-example-basic = { path = "../examples/basic", features = ["std"] } +pallet-balances = { path = "../balances", features = ["std"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-example-basic/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-example-basic/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/parameters/src/benchmarking.rs b/substrate/frame/parameters/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..1f22e026cbca07b236c4680570a3312d8c4b85e5 --- /dev/null +++ b/substrate/frame/parameters/src/benchmarking.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. + +//! Parameters pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +#[cfg(test)] +use crate::Pallet as Parameters; + +use frame_benchmarking::v2::*; + +#[benchmarks(where T::RuntimeParameters: Default)] +mod benchmarks { + use super::*; + + #[benchmark] + fn set_parameter() -> Result<(), BenchmarkError> { + let kv = T::RuntimeParameters::default(); + let k = kv.clone().into_parts().0; + + let origin = + T::AdminOrigin::try_successful_origin(&k).map_err(|_| BenchmarkError::Weightless)?; + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, kv); + + Ok(()) + } + + impl_benchmark_test_suite! { + Parameters, + crate::tests::mock::new_test_ext(), + crate::tests::mock::Runtime, + } +} diff --git a/substrate/frame/parameters/src/lib.rs b/substrate/frame/parameters/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..91cf10ba93f7f9ebf03363cfe3bf8a9b4ff89172 --- /dev/null +++ b/substrate/frame/parameters/src/lib.rs @@ -0,0 +1,270 @@ +// 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)] +#![deny(missing_docs)] +// Need to enable this one since we document feature-gated stuff. +#![allow(rustdoc::broken_intra_doc_links)] + +//! # **⚠️ WARNING ⚠️** +//! +//!
+//! THIS CRATE IS NOT AUDITED AND SHOULD NOT BE USED IN PRODUCTION. +//!
+//! +//! # Parameters +//! +//! Allows to update configuration parameters at runtime. +//! +//! ## Pallet API +//! +//! This pallet exposes two APIs; one *inbound* side to update parameters, and one *outbound* side +//! to access said parameters. Parameters themselves are defined in the runtime config and will be +//! aggregated into an enum. Each parameter is addressed by a `key` and can have a default value. +//! This is not done by the pallet but through the [`frame_support::dynamic_params::dynamic_params`] +//! macro or alternatives. +//! +//! Note that this is incurring one storage read per access. This should not be a problem in most +//! cases but must be considered in weight-restrained code. +//! +//! ### Inbound +//! +//! The inbound side solely consists of the [`Pallet::set_parameter`] extrinsic to update the value +//! of a parameter. Each parameter can have their own admin origin as given by the +//! [`Config::AdminOrigin`]. +//! +//! ### Outbound +//! +//! The outbound side is runtime facing for the most part. More general, it provides a `Get` +//! implementation and can be used in every spot where that is accepted. Two macros are in place: +//! [`frame_support::dynamic_params::define_parameters` and +//! [`frame_support::dynamic_params:dynamic_pallet_params`] to define and expose parameters in a +//! typed manner. +//! +//! See the [`pallet`] module for more information about the interfaces this pallet exposes, +//! including its configuration trait, dispatchables, storage items, events and errors. +//! +//! ## Overview +//! +//! This pallet is a good fit for updating parameters without a runtime upgrade. It is very handy to +//! not require a runtime upgrade for a simple parameter change since runtime upgrades require a lot +//! of diligence and always bear risks. It seems overkill to update the whole runtime for a simple +//! parameter change. This pallet allows for fine-grained control over who can update what. +//! The only down-side is that it trades off performance with convenience and should therefore only +//! be used in places where that is proven to be uncritical. Values that are rarely accessed but +//! change often would be a perfect fit. +//! +//! ### Example Configuration +//! +//! Here is an example of how to define some parameters, including their default values: +#![doc = docify::embed!("src/tests/mock.rs", dynamic_params)] +//! +//! A permissioned origin can be define on a per-key basis like this: +#![doc = docify::embed!("src/tests/mock.rs", custom_origin)] +//! +//! The pallet will also require a default value for benchmarking. Ideally this is the variant with +//! the longest encoded length. Although in either case the PoV benchmarking will take the worst +//! case over the whole enum. +#![doc = docify::embed!("src/tests/mock.rs", benchmarking_default)] +//! +//! Now the aggregated parameter needs to be injected into the pallet config: +#![doc = docify::embed!("src/tests/mock.rs", impl_config)] +//! +//! As last step, the parameters can now be used in other pallets 🙌 +#![doc = docify::embed!("src/tests/mock.rs", usage)] +//! +//! ### Examples Usage +//! +//! Now to demonstrate how the values can be updated: +#![doc = docify::embed!("src/tests/unit.rs", set_parameters_example)] +//! +//! ## Low Level / Implementation Details +//! +//! The pallet stores the parameters in a storage map and implements the matching `Get` for +//! each `Key` type. The `Get` then accesses the `Parameters` map to retrieve the value. An event is +//! emitted every time that a value was updated. It is even emitted when the value is changed to the +//! same. +//! +//! The key and value types themselves are defined by macros and aggregated into a runtime wide +//! enum. This enum is then injected into the pallet. This allows it to be used without any changes +//! to the pallet that the parameter will be utilized by. +//! +//! ### Design Goals +//! +//! 1. Easy to update without runtime upgrade. +//! 2. Exposes metadata and docs for user convenience. +//! 3. Can be permissioned on a per-key base. +//! +//! ### Design +//! +//! 1. Everything is done at runtime without the need for `const` values. `Get` allows for this - +//! which is coincidentally an upside and a downside. 2. The types are defined through macros, which +//! allows to expose metadata and docs. 3. Access control is done through the `EnsureOriginWithArg` +//! trait, that allows to pass data along to the origin check. It gets passed in the key. The +//! implementor can then match on the key and the origin to decide whether the origin is +//! permissioned to set the value. + +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +use frame_support::traits::{ + dynamic_params::{AggregratedKeyValue, IntoKey, Key, RuntimeParameterStore, TryIntoKey}, + EnsureOriginWithArg, +}; + +mod benchmarking; +#[cfg(test)] +mod tests; +mod weights; + +pub use pallet::*; +pub use weights::WeightInfo; + +/// The key type of a parameter. +type KeyOf = <::RuntimeParameters as AggregratedKeyValue>::Key; + +/// The value type of a parameter. +type ValueOf = <::RuntimeParameters as AggregratedKeyValue>::Value; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config(with_default)] + pub trait Config: frame_system::Config { + /// The overarching event type. + #[pallet::no_default_bounds] + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The overarching KV type of the parameters. + /// + /// Usually created by [`frame_support::dynamic_params`] or equivalent. + #[pallet::no_default_bounds] + type RuntimeParameters: AggregratedKeyValue; + + /// The origin which may update a parameter. + /// + /// The key of the parameter is passed in as second argument to allow for fine grained + /// control. + #[pallet::no_default_bounds] + type AdminOrigin: EnsureOriginWithArg>; + + /// Weight information for extrinsics in this module. + type WeightInfo: WeightInfo; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// A Parameter was set. + /// + /// Is also emitted when the value was not changed. + Updated { + /// The key that was updated. + key: ::Key, + /// The old value before this call. + old_value: Option<::Value>, + /// The new value after this call. + new_value: Option<::Value>, + }, + } + + /// Stored parameters. + #[pallet::storage] + pub type Parameters = + StorageMap<_, Blake2_128Concat, KeyOf, ValueOf, OptionQuery>; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + /// Set the value of a parameter. + /// + /// The dispatch origin of this call must be `AdminOrigin` for the given `key`. Values be + /// deleted by setting them to `None`. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::set_parameter())] + pub fn set_parameter( + origin: OriginFor, + key_value: T::RuntimeParameters, + ) -> DispatchResult { + let (key, new) = key_value.into_parts(); + T::AdminOrigin::ensure_origin(origin, &key)?; + + let mut old = None; + Parameters::::mutate(&key, |v| { + old = v.clone(); + *v = new.clone(); + }); + + Self::deposit_event(Event::Updated { key, old_value: old, new_value: new }); + + Ok(()) + } + } + /// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`]. + pub mod config_preludes { + use super::*; + use frame_support::derive_impl; + + /// A configuration for testing. + pub struct TestDefaultConfig; + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, 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 RuntimeParameters = (); + + type AdminOrigin = frame_support::traits::AsEnsureOriginWithArg< + frame_system::EnsureRoot, + >; + + type WeightInfo = (); + } + } +} + +impl RuntimeParameterStore for Pallet { + type AggregratedKeyValue = T::RuntimeParameters; + + fn get(key: K) -> Option + where + KV: AggregratedKeyValue, + K: Key + Into<::Key>, + ::Key: IntoKey< + <::AggregratedKeyValue as AggregratedKeyValue>::Key, + >, + <::AggregratedKeyValue as AggregratedKeyValue>::Value: + TryIntoKey<::Value>, + ::Value: TryInto, + { + let key: ::Key = key.into(); + let val = Parameters::::get(key.into_key()); + val.and_then(|v| { + let val: ::Value = v.try_into_key().ok()?; + let val: K::WrappedValue = val.try_into().ok()?; + let val = val.into(); + Some(val) + }) + } +} diff --git a/substrate/frame/parameters/src/tests/mock.rs b/substrate/frame/parameters/src/tests/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..98612dc6a6d959c91b78570f562bce9074c43f61 --- /dev/null +++ b/substrate/frame/parameters/src/tests/mock.rs @@ -0,0 +1,152 @@ +// 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(any(test, feature = "runtime-benchmarks"))] + +//! Mock runtime that configures the `pallet_example_basic` to use dynamic params for testing. + +use frame_support::{ + construct_runtime, derive_impl, + dynamic_params::{dynamic_pallet_params, dynamic_params}, + traits::EnsureOriginWithArg, +}; + +use crate as pallet_parameters; +use crate::*; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = frame_system::mocking::MockBlock; + type AccountData = pallet_balances::AccountData<::Balance>; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Runtime { + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; +} + +#[docify::export] +#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::)] +pub mod dynamic_params { + use super::*; + + #[dynamic_pallet_params] + #[codec(index = 3)] + pub mod pallet1 { + #[codec(index = 0)] + pub static Key1: u64 = 0; + #[codec(index = 1)] + pub static Key2: u32 = 1; + #[codec(index = 2)] + pub static Key3: u128 = 2; + } + + #[dynamic_pallet_params] + #[codec(index = 1)] + pub mod pallet2 { + #[codec(index = 2)] + pub static Key1: u64 = 0; + #[codec(index = 1)] + pub static Key2: u32 = 2; + #[codec(index = 0)] + pub static Key3: u128 = 4; + } +} + +#[docify::export(benchmarking_default)] +#[cfg(feature = "runtime-benchmarks")] +impl Default for RuntimeParameters { + fn default() -> Self { + RuntimeParameters::Pallet1(dynamic_params::pallet1::Parameters::Key1( + dynamic_params::pallet1::Key1, + Some(123), + )) + } +} + +#[docify::export] +mod custom_origin { + use super::*; + pub struct ParamsManager; + + impl EnsureOriginWithArg for ParamsManager { + type Success = (); + + fn try_origin( + origin: RuntimeOrigin, + key: &RuntimeParametersKey, + ) -> Result { + // Account 123 is allowed to set parameters in benchmarking only: + #[cfg(feature = "runtime-benchmarks")] + if ensure_signed(origin.clone()).map_or(false, |acc| acc == 123) { + return Ok(()); + } + + match key { + RuntimeParametersKey::Pallet1(_) => ensure_root(origin.clone()), + RuntimeParametersKey::Pallet2(_) => ensure_signed(origin.clone()).map(|_| ()), + } + .map_err(|_| origin) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_key: &RuntimeParametersKey) -> Result { + Ok(RuntimeOrigin::signed(123)) + } + } +} + +#[docify::export(impl_config)] +#[derive_impl(pallet_parameters::config_preludes::TestDefaultConfig as pallet_parameters::DefaultConfig)] +impl Config for Runtime { + type AdminOrigin = custom_origin::ParamsManager; + // RuntimeParameters is injected by the `derive_impl` macro. + // RuntimeEvent is injected by the `derive_impl` macro. + // WeightInfo is injected by the `derive_impl` macro. +} + +#[docify::export(usage)] +impl pallet_example_basic::Config for Runtime { + // Use the dynamic key in the pallet config: + type MagicNumber = dynamic_params::pallet1::Key1; + + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + PalletParameters: crate, + Example: pallet_example_basic, + Balances: pallet_balances, + } +); + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut ext = sp_io::TestExternalities::new(Default::default()); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub(crate) fn assert_last_event(generic_event: RuntimeEvent) { + let events = frame_system::Pallet::::events(); + // compare to the last event record + let frame_system::EventRecord { event, .. } = &events.last().expect("Event expected"); + assert_eq!(event, &generic_event); +} diff --git a/cumulus/parachains/common/src/wococo.rs b/substrate/frame/parameters/src/tests/mod.rs similarity index 88% rename from cumulus/parachains/common/src/wococo.rs rename to substrate/frame/parameters/src/tests/mod.rs index 5cd6121135a3321369fee2132b696aef8a2bae81..0a2d16906aadd394c5facb8c2bdbf45195574246 100644 --- a/cumulus/parachains/common/src/wococo.rs +++ b/substrate/frame/parameters/src/tests/mod.rs @@ -1,3 +1,5 @@ +// This file is part of Substrate. + // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 @@ -13,5 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// re-export rococo -pub use crate::rococo::{consensus, currency, fee}; +pub(crate) mod mock; +mod test_renamed; +mod unit; diff --git a/substrate/frame/parameters/src/tests/test_renamed.rs b/substrate/frame/parameters/src/tests/test_renamed.rs new file mode 100644 index 0000000000000000000000000000000000000000..b2e0c1fd9661b4122663a4633e1d4500ac6fad1c --- /dev/null +++ b/substrate/frame/parameters/src/tests/test_renamed.rs @@ -0,0 +1,168 @@ +// 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(any(test, feature = "runtime-benchmarks"))] + +//! Tests that the runtime params can be renamed. + +use frame_support::{ + assert_noop, assert_ok, construct_runtime, derive_impl, + dynamic_params::{dynamic_pallet_params, dynamic_params}, + traits::AsEnsureOriginWithArg, +}; +use frame_system::EnsureRoot; + +use crate as pallet_parameters; +use crate::*; +use dynamic_params::*; +use RuntimeParametersRenamed::*; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = frame_system::mocking::MockBlock; + type AccountData = pallet_balances::AccountData<::Balance>; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Runtime { + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; +} + +#[dynamic_params(RuntimeParametersRenamed, pallet_parameters::Parameters::)] +pub mod dynamic_params { + use super::*; + + #[dynamic_pallet_params] + #[codec(index = 3)] + pub mod pallet1 { + #[codec(index = 0)] + pub static Key1: u64 = 0; + #[codec(index = 1)] + pub static Key2: u32 = 1; + #[codec(index = 2)] + pub static Key3: u128 = 2; + } + + #[dynamic_pallet_params] + #[codec(index = 1)] + pub mod pallet2 { + #[codec(index = 2)] + pub static Key1: u64 = 0; + #[codec(index = 1)] + pub static Key2: u32 = 2; + #[codec(index = 0)] + pub static Key3: u128 = 4; + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default for RuntimeParametersRenamed { + fn default() -> Self { + RuntimeParametersRenamed::Pallet1(dynamic_params::pallet1::Parameters::Key1( + dynamic_params::pallet1::Key1, + Some(123), + )) + } +} + +#[derive_impl(pallet_parameters::config_preludes::TestDefaultConfig as pallet_parameters::DefaultConfig)] +impl Config for Runtime { + type AdminOrigin = AsEnsureOriginWithArg>; + type RuntimeParameters = RuntimeParametersRenamed; + // RuntimeEvent is injected by the `derive_impl` macro. + // WeightInfo is injected by the `derive_impl` macro. +} + +impl pallet_example_basic::Config for Runtime { + // Use the dynamic key in the pallet config: + type MagicNumber = dynamic_params::pallet1::Key1; + + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + PalletParameters: crate, + Example: pallet_example_basic, + Balances: pallet_balances, + } +); + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut ext = sp_io::TestExternalities::new(Default::default()); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub(crate) fn assert_last_event(generic_event: RuntimeEvent) { + let events = frame_system::Pallet::::events(); + // compare to the last event record + let frame_system::EventRecord { event, .. } = &events.last().expect("Event expected"); + assert_eq!(event, &generic_event); +} + +#[test] +fn set_parameters_example() { + new_test_ext().execute_with(|| { + assert_eq!(pallet1::Key3::get(), 2, "Default works"); + + // This gets rejected since the origin is not root. + assert_noop!( + PalletParameters::set_parameter( + RuntimeOrigin::signed(1), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + ), + DispatchError::BadOrigin + ); + + assert_ok!(PalletParameters::set_parameter( + RuntimeOrigin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + )); + + assert_eq!(pallet1::Key3::get(), 123, "Update works"); + assert_last_event( + crate::Event::Updated { + key: RuntimeParametersRenamedKey::Pallet1(pallet1::ParametersKey::Key3( + pallet1::Key3, + )), + old_value: None, + new_value: Some(RuntimeParametersRenamedValue::Pallet1( + pallet1::ParametersValue::Key3(123), + )), + } + .into(), + ); + }); +} + +#[test] +fn get_through_external_pallet_works() { + new_test_ext().execute_with(|| { + assert_eq!(::MagicNumber::get(), 0); + + assert_ok!(PalletParameters::set_parameter( + RuntimeOrigin::root(), + Pallet1(pallet1::Parameters::Key1(pallet1::Key1, Some(123))), + )); + + assert_eq!(::MagicNumber::get(), 123); + }); +} diff --git a/substrate/frame/parameters/src/tests/unit.rs b/substrate/frame/parameters/src/tests/unit.rs new file mode 100644 index 0000000000000000000000000000000000000000..d3f11ba96403514fbe77ba89f57872e85cbf655a --- /dev/null +++ b/substrate/frame/parameters/src/tests/unit.rs @@ -0,0 +1,311 @@ +// 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. + +//! Unit tests for the parameters pallet. + +#![cfg(test)] + +use crate::tests::mock::{ + assert_last_event, dynamic_params::*, new_test_ext, PalletParameters, Runtime, + RuntimeOrigin as Origin, RuntimeParameters, RuntimeParameters::*, RuntimeParametersKey, + RuntimeParametersValue, +}; +use codec::Encode; +use frame_support::{assert_noop, assert_ok, traits::dynamic_params::AggregratedKeyValue}; +use sp_core::Get; +use sp_runtime::DispatchError; + +#[docify::export] +#[test] +fn set_parameters_example() { + new_test_ext().execute_with(|| { + assert_eq!(pallet1::Key3::get(), 2, "Default works"); + + // This gets rejected since the origin is not root. + assert_noop!( + PalletParameters::set_parameter( + Origin::signed(1), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + ), + DispatchError::BadOrigin + ); + + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + )); + + assert_eq!(pallet1::Key3::get(), 123, "Update works"); + assert_last_event( + crate::Event::Updated { + key: RuntimeParametersKey::Pallet1(pallet1::ParametersKey::Key3(pallet1::Key3)), + old_value: None, + new_value: Some(RuntimeParametersValue::Pallet1(pallet1::ParametersValue::Key3( + 123, + ))), + } + .into(), + ); + }); +} + +#[test] +fn set_parameters_same_is_noop() { + new_test_ext().execute_with(|| { + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + )); + + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + )); + + assert_eq!(pallet1::Key3::get(), 123, "Update works"); + }); +} + +#[test] +fn set_parameters_twice_works() { + new_test_ext().execute_with(|| { + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + )); + + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(432))), + )); + + assert_eq!(pallet1::Key3::get(), 432, "Update works"); + }); +} + +#[test] +fn set_parameters_removing_restores_default_works() { + new_test_ext().execute_with(|| { + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + )); + + assert_eq!(pallet1::Key3::get(), 123, "Update works"); + assert!( + crate::Parameters::::contains_key(RuntimeParametersKey::Pallet1( + pallet1::ParametersKey::Key3(pallet1::Key3) + )), + "Key inserted" + ); + + // Removing the value restores the default. + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, None)), + )); + + assert_eq!(pallet1::Key3::get(), 2, "Default restored"); + assert!( + !crate::Parameters::::contains_key(RuntimeParametersKey::Pallet1( + pallet1::ParametersKey::Key3(pallet1::Key3) + )), + "Key removed" + ); + }); +} + +#[test] +fn set_parameters_to_default_emits_events_works() { + new_test_ext().execute_with(|| { + assert_eq!(pallet1::Key3::get(), 2); + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(2))), + )); + assert_eq!(pallet1::Key3::get(), 2); + + assert!( + crate::Parameters::::contains_key(RuntimeParametersKey::Pallet1( + pallet1::ParametersKey::Key3(pallet1::Key3) + )), + "Key inserted" + ); + assert_last_event( + crate::Event::Updated { + key: RuntimeParametersKey::Pallet1(pallet1::ParametersKey::Key3(pallet1::Key3)), + old_value: None, + new_value: Some(RuntimeParametersValue::Pallet1(pallet1::ParametersValue::Key3(2))), + } + .into(), + ); + + // It will also emit a second event: + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(2))), + )); + assert_eq!(frame_system::Pallet::::events().len(), 2); + }); +} + +#[test] +fn set_parameters_wrong_origin_errors() { + new_test_ext().execute_with(|| { + // Pallet1 is root origin only: + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(123))), + )); + + assert_noop!( + PalletParameters::set_parameter( + Origin::signed(1), + Pallet1(pallet1::Parameters::Key3(pallet1::Key3, Some(432))), + ), + DispatchError::BadOrigin + ); + + // Pallet2 is signed origin only: + assert_ok!(PalletParameters::set_parameter( + Origin::signed(1), + Pallet2(pallet2::Parameters::Key3(pallet2::Key3, Some(123))), + )); + + assert_noop!( + PalletParameters::set_parameter( + Origin::root(), + Pallet2(pallet2::Parameters::Key3(pallet2::Key3, Some(432))), + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn get_through_external_pallet_works() { + new_test_ext().execute_with(|| { + assert_eq!(::MagicNumber::get(), 0); + + assert_ok!(PalletParameters::set_parameter( + Origin::root(), + Pallet1(pallet1::Parameters::Key1(pallet1::Key1, Some(123))), + )); + + assert_eq!(::MagicNumber::get(), 123); + }); +} + +#[test] +fn test_define_parameters_key_convert() { + let key1 = pallet1::Key1; + let parameter_key: pallet1::ParametersKey = key1.clone().into(); + let key1_2: pallet1::Key1 = parameter_key.clone().try_into().unwrap(); + + assert_eq!(key1, key1_2); + assert_eq!(parameter_key, pallet1::ParametersKey::Key1(key1)); + + let key2 = pallet1::Key2; + let parameter_key: pallet1::ParametersKey = key2.clone().into(); + let key2_2: pallet1::Key2 = parameter_key.clone().try_into().unwrap(); + + assert_eq!(key2, key2_2); + assert_eq!(parameter_key, pallet1::ParametersKey::Key2(key2)); +} + +#[test] +fn test_define_parameters_value_convert() { + let value1 = pallet1::Key1Value(1); + let parameter_value: pallet1::ParametersValue = value1.clone().into(); + let value1_2: pallet1::Key1Value = parameter_value.clone().try_into().unwrap(); + + assert_eq!(value1, value1_2); + assert_eq!(parameter_value, pallet1::ParametersValue::Key1(1)); + + let value2 = pallet1::Key2Value(2); + let parameter_value: pallet1::ParametersValue = value2.clone().into(); + let value2_2: pallet1::Key2Value = parameter_value.clone().try_into().unwrap(); + + assert_eq!(value2, value2_2); + assert_eq!(parameter_value, pallet1::ParametersValue::Key2(2)); +} + +#[test] +fn test_define_parameters_aggregrated_key_value() { + let kv1 = pallet1::Parameters::Key1(pallet1::Key1, None); + let (key1, value1) = kv1.clone().into_parts(); + + assert_eq!(key1, pallet1::ParametersKey::Key1(pallet1::Key1)); + assert_eq!(value1, None); + + let kv2 = pallet1::Parameters::Key2(pallet1::Key2, Some(2)); + let (key2, value2) = kv2.clone().into_parts(); + + assert_eq!(key2, pallet1::ParametersKey::Key2(pallet1::Key2)); + assert_eq!(value2, Some(pallet1::ParametersValue::Key2(2))); +} + +#[test] +fn test_define_aggregrated_parameters_key_convert() { + use codec::Encode; + + let key1 = pallet1::Key1; + let parameter_key: pallet1::ParametersKey = key1.clone().into(); + let runtime_key: RuntimeParametersKey = parameter_key.clone().into(); + + assert_eq!(runtime_key, RuntimeParametersKey::Pallet1(pallet1::ParametersKey::Key1(key1))); + assert_eq!(runtime_key.encode(), vec![3, 0]); + + let key2 = pallet2::Key2; + let parameter_key: pallet2::ParametersKey = key2.clone().into(); + let runtime_key: RuntimeParametersKey = parameter_key.clone().into(); + + assert_eq!(runtime_key, RuntimeParametersKey::Pallet2(pallet2::ParametersKey::Key2(key2))); + assert_eq!(runtime_key.encode(), vec![1, 1]); +} + +#[test] +fn test_define_aggregrated_parameters_aggregrated_key_value() { + let kv1 = RuntimeParameters::Pallet1(pallet1::Parameters::Key1(pallet1::Key1, None)); + let (key1, value1) = kv1.clone().into_parts(); + + assert_eq!(key1, RuntimeParametersKey::Pallet1(pallet1::ParametersKey::Key1(pallet1::Key1))); + assert_eq!(value1, None); + + let kv2 = RuntimeParameters::Pallet2(pallet2::Parameters::Key2(pallet2::Key2, Some(2))); + let (key2, value2) = kv2.clone().into_parts(); + + assert_eq!(key2, RuntimeParametersKey::Pallet2(pallet2::ParametersKey::Key2(pallet2::Key2))); + assert_eq!(value2, Some(RuntimeParametersValue::Pallet2(pallet2::ParametersValue::Key2(2)))); +} + +#[test] +fn codec_index_works() { + let enc = RuntimeParametersKey::Pallet1(pallet1::ParametersKey::Key1(pallet1::Key1)).encode(); + assert_eq!(enc, vec![3, 0]); + let enc = RuntimeParametersKey::Pallet1(pallet1::ParametersKey::Key2(pallet1::Key2)).encode(); + assert_eq!(enc, vec![3, 1]); + let enc = RuntimeParametersKey::Pallet1(pallet1::ParametersKey::Key3(pallet1::Key3)).encode(); + assert_eq!(enc, vec![3, 2]); + + let enc = RuntimeParametersKey::Pallet2(pallet2::ParametersKey::Key1(pallet2::Key1)).encode(); + assert_eq!(enc, vec![1, 2]); + let enc = RuntimeParametersKey::Pallet2(pallet2::ParametersKey::Key2(pallet2::Key2)).encode(); + assert_eq!(enc, vec![1, 1]); + let enc = RuntimeParametersKey::Pallet2(pallet2::ParametersKey::Key3(pallet2::Key3)).encode(); + assert_eq!(enc, vec![1, 0]); +} diff --git a/substrate/frame/parameters/src/weights.rs b/substrate/frame/parameters/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..340eb9e31b77c507deb1d81794980f856d01971d --- /dev/null +++ b/substrate/frame/parameters/src/weights.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. + +//! Autogenerated weights for `pallet_parameters` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_parameters +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/parameters/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --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_parameters`. +pub trait WeightInfo { + fn set_parameter() -> Weight; +} + +/// Weights for `pallet_parameters` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Parameters::Parameters` (r:1 w:1) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_parameter() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3501` + // Minimum execution time: 8_400_000 picoseconds. + Weight::from_parts(8_682_000, 3501) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `Parameters::Parameters` (r:1 w:1) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_parameter() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3501` + // Minimum execution time: 8_400_000 picoseconds. + Weight::from_parts(8_682_000, 3501) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/substrate/frame/preimage/Cargo.toml b/substrate/frame/preimage/Cargo.toml index 2aa21d2a7136ec7e31bbb42fe7fe8e9d7010536f..10a15f97bd5a72417243b86c81cc7b3ddf1db2a6 100644 --- a/substrate/frame/preimage/Cargo.toml +++ b/substrate/frame/preimage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-preimage" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,7 +21,7 @@ sp-core = { path = "../../primitives/core", default-features = false, optional = sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] pallet-balances = { path = "../balances" } diff --git a/substrate/frame/preimage/src/lib.rs b/substrate/frame/preimage/src/lib.rs index e344bdfe2d8feff5a25ed686d7301f462fa41189..4e474685166631ba41eed644d3754a332a290663 100644 --- a/substrate/frame/preimage/src/lib.rs +++ b/substrate/frame/preimage/src/lib.rs @@ -102,7 +102,7 @@ pub const MAX_HASH_UPGRADE_BULK_COUNT: u32 = 1024; pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::config] diff --git a/substrate/frame/preimage/src/mock.rs b/substrate/frame/preimage/src/mock.rs index 357f088f5ba24ab0f65b720de1887d1bd1a17718..a43e8347d76bf219c2f2fe3a36e4505f1d48c4a0 100644 --- a/substrate/frame/preimage/src/mock.rs +++ b/substrate/frame/preimage/src/mock.rs @@ -21,14 +21,13 @@ use super::*; use crate as pallet_preimage; use frame_support::{ - derive_impl, ord_parameter_types, - traits::{fungible::HoldConsideration, ConstU32, ConstU64, Everything}, - weights::constants::RocksDbWeight, + derive_impl, ord_parameter_types, parameter_types, + traits::{fungible::HoldConsideration, ConstU32, ConstU64}, }; use frame_system::EnsureSignedBy; use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, Convert, IdentityLookup}, + traits::{BlakeTwo256, Convert}, BuildStorage, }; @@ -45,29 +44,8 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -82,15 +60,18 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; type FreezeIdentifier = (); type MaxFreezes = ConstU32<1>; - type RuntimeHoldReason = (); + type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = (); - type MaxHolds = ConstU32<2>; } ord_parameter_types! { pub const One: u64 = 1; } +parameter_types! { + pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); +} + pub struct ConvertDeposit; impl Convert for ConvertDeposit { fn convert(a: Footprint) -> u64 { @@ -103,7 +84,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type ManagerOrigin = EnsureSignedBy; - type Consideration = HoldConsideration; + type Consideration = HoldConsideration; } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/substrate/frame/preimage/src/tests.rs b/substrate/frame/preimage/src/tests.rs index 7609ec83e9039a66ce22b1fbcd4c49a591c86187..69f6d172ae83acafc0881a055fcc767bec810470 100644 --- a/substrate/frame/preimage/src/tests.rs +++ b/substrate/frame/preimage/src/tests.rs @@ -58,7 +58,7 @@ pub fn make_bounded_values() -> ( fn user_note_preimage_works() { new_test_ext().execute_with(|| { assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1])); - assert_eq!(Balances::balance_on_hold(&(), &2), 3); + assert_eq!(Balances::balance_on_hold(&PreimageHoldReason::get(), &2), 3); assert_eq!(Balances::free_balance(2), 97); let h = hashed([1]); @@ -255,14 +255,14 @@ fn unrequest_preimage_works() { fn user_noted_then_requested_preimage_is_refunded_once_only() { new_test_ext().execute_with(|| { assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1; 3])); - assert_eq!(Balances::balance_on_hold(&(), &2), 5); + assert_eq!(Balances::balance_on_hold(&PreimageHoldReason::get(), &2), 5); assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1])); - assert_eq!(Balances::balance_on_hold(&(), &2), 8); + assert_eq!(Balances::balance_on_hold(&PreimageHoldReason::get(), &2), 8); assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1]))); assert_ok!(Preimage::unrequest_preimage(RuntimeOrigin::signed(1), hashed([1]))); assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(2), hashed([1]))); // Still have hold from `vec[1; 3]`. - assert_eq!(Balances::balance_on_hold(&(), &2), 5); + assert_eq!(Balances::balance_on_hold(&PreimageHoldReason::get(), &2), 5); }); } diff --git a/substrate/frame/preimage/src/weights.rs b/substrate/frame/preimage/src/weights.rs index c11ab74c1e551a0fcdd6e239a657ab5df0809aad..6167cd5302918ab0da7f1a243ccfeb4743f23540 100644 --- a/substrate/frame/preimage/src/weights.rs +++ b/substrate/frame/preimage/src/weights.rs @@ -17,26 +17,28 @@ //! Autogenerated weights for `pallet_preimage` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-mia4uyug-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_preimage +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_preimage -// --chain=dev -// --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/preimage/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -70,200 +72,209 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `42` - // Estimated: `3556` - // Minimum execution time: 15_936_000 picoseconds. - Weight::from_parts(16_271_000, 3556) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_916, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `112` + // Estimated: `6012` + // Minimum execution time: 48_893_000 picoseconds. + Weight::from_parts(44_072_327, 6012) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_684, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 16_468_000 picoseconds. - Weight::from_parts(17_031_000, 3556) + // Minimum execution time: 15_675_000 picoseconds. + Weight::from_parts(4_564_145, 3556) // Standard Error: 2 - .saturating_add(Weight::from_parts(1_948, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_678, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 16_342_000 picoseconds. - Weight::from_parts(16_535_000, 3556) + // Minimum execution time: 14_959_000 picoseconds. + Weight::from_parts(15_335_000, 3556) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_906, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_687, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `172` - // Estimated: `3556` - // Minimum execution time: 31_047_000 picoseconds. - Weight::from_parts(34_099_000, 3556) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `311` + // Estimated: `3658` + // Minimum execution time: 47_378_000 picoseconds. + Weight::from_parts(48_776_000, 3658) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 32_559_000 picoseconds. - Weight::from_parts(36_677_000, 3556) + // Minimum execution time: 20_939_000 picoseconds. + Weight::from_parts(21_577_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `172` + // Measured: `255` // Estimated: `3556` - // Minimum execution time: 27_887_000 picoseconds. - Weight::from_parts(30_303_000, 3556) + // Minimum execution time: 17_945_000 picoseconds. + Weight::from_parts(18_448_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 17_256_000 picoseconds. - Weight::from_parts(19_481_000, 3556) + // Minimum execution time: 12_132_000 picoseconds. + Weight::from_parts(12_710_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_unnoted_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `42` + // Measured: `109` // Estimated: `3556` - // Minimum execution time: 22_344_000 picoseconds. - Weight::from_parts(23_868_000, 3556) + // Minimum execution time: 13_014_000 picoseconds. + Weight::from_parts(13_726_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_requested_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 10_542_000 picoseconds. - Weight::from_parts(11_571_000, 3556) + // Minimum execution time: 9_785_000 picoseconds. + Weight::from_parts(10_266_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unrequest_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 29_054_000 picoseconds. - Weight::from_parts(32_996_000, 3556) + // Minimum execution time: 18_764_000 picoseconds. + Weight::from_parts(19_635_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_unnoted_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 10_775_000 picoseconds. - Weight::from_parts(11_937_000, 3556) + // Minimum execution time: 9_624_000 picoseconds. + Weight::from_parts(10_044_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_multi_referenced_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 10_696_000 picoseconds. - Weight::from_parts(11_717_000, 3556) + // Minimum execution time: 9_432_000 picoseconds. + Weight::from_parts(9_991_000, 3556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Preimage::StatusFor` (r:1024 w:1024) + /// Storage: `Preimage::StatusFor` (r:1023 w:1023) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:1023 w:1023) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Preimage::RequestStatusFor` (r:0 w:1024) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 1024]`. + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1023 w:1023) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:0 w:1023) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1024]`. fn ensure_updated(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + n * (91 ±0)` - // Estimated: `3593 + n * (2566 ±0)` - // Minimum execution time: 2_452_000 picoseconds. - Weight::from_parts(2_641_000, 3593) - // Standard Error: 19_797 - .saturating_add(Weight::from_parts(15_620_946, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2566).saturating_mul(n.into())) + // Measured: `0 + n * (227 ±0)` + // Estimated: `6012 + n * (2668 ±0)` + // Minimum execution time: 54_056_000 picoseconds. + Weight::from_parts(54_912_000, 6012) + // Standard Error: 42_469 + .saturating_add(Weight::from_parts(50_710_258, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2668).saturating_mul(n.into())) } } @@ -272,199 +283,208 @@ impl WeightInfo for () { /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `42` - // Estimated: `3556` - // Minimum execution time: 15_936_000 picoseconds. - Weight::from_parts(16_271_000, 3556) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_916, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `112` + // Estimated: `6012` + // Minimum execution time: 48_893_000 picoseconds. + Weight::from_parts(44_072_327, 6012) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_684, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 16_468_000 picoseconds. - Weight::from_parts(17_031_000, 3556) + // Minimum execution time: 15_675_000 picoseconds. + Weight::from_parts(4_564_145, 3556) // Standard Error: 2 - .saturating_add(Weight::from_parts(1_948, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_678, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 16_342_000 picoseconds. - Weight::from_parts(16_535_000, 3556) + // Minimum execution time: 14_959_000 picoseconds. + Weight::from_parts(15_335_000, 3556) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_906, 0).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(1_687, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `172` - // Estimated: `3556` - // Minimum execution time: 31_047_000 picoseconds. - Weight::from_parts(34_099_000, 3556) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `311` + // Estimated: `3658` + // Minimum execution time: 47_378_000 picoseconds. + Weight::from_parts(48_776_000, 3658) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 32_559_000 picoseconds. - Weight::from_parts(36_677_000, 3556) + // Minimum execution time: 20_939_000 picoseconds. + Weight::from_parts(21_577_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `172` + // Measured: `255` // Estimated: `3556` - // Minimum execution time: 27_887_000 picoseconds. - Weight::from_parts(30_303_000, 3556) + // Minimum execution time: 17_945_000 picoseconds. + Weight::from_parts(18_448_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 17_256_000 picoseconds. - Weight::from_parts(19_481_000, 3556) + // Minimum execution time: 12_132_000 picoseconds. + Weight::from_parts(12_710_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_unnoted_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `42` + // Measured: `109` // Estimated: `3556` - // Minimum execution time: 22_344_000 picoseconds. - Weight::from_parts(23_868_000, 3556) + // Minimum execution time: 13_014_000 picoseconds. + Weight::from_parts(13_726_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_requested_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 10_542_000 picoseconds. - Weight::from_parts(11_571_000, 3556) + // Minimum execution time: 9_785_000 picoseconds. + Weight::from_parts(10_266_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:0 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unrequest_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `144` + // Measured: `211` // Estimated: `3556` - // Minimum execution time: 29_054_000 picoseconds. - Weight::from_parts(32_996_000, 3556) + // Minimum execution time: 18_764_000 picoseconds. + Weight::from_parts(19_635_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_unnoted_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 10_775_000 picoseconds. - Weight::from_parts(11_937_000, 3556) + // Minimum execution time: 9_624_000 picoseconds. + Weight::from_parts(10_044_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_multi_referenced_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `173` // Estimated: `3556` - // Minimum execution time: 10_696_000 picoseconds. - Weight::from_parts(11_717_000, 3556) + // Minimum execution time: 9_432_000 picoseconds. + Weight::from_parts(9_991_000, 3556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Preimage::StatusFor` (r:1024 w:1024) + /// Storage: `Preimage::StatusFor` (r:1023 w:1023) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:1023 w:1023) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Preimage::RequestStatusFor` (r:0 w:1024) - /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 1024]`. + /// Storage: `Parameters::Parameters` (r:2 w:0) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1023 w:1023) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:0 w:1023) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1024]`. fn ensure_updated(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + n * (91 ±0)` - // Estimated: `3593 + n * (2566 ±0)` - // Minimum execution time: 2_452_000 picoseconds. - Weight::from_parts(2_641_000, 3593) - // Standard Error: 19_797 - .saturating_add(Weight::from_parts(15_620_946, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2566).saturating_mul(n.into())) + // Measured: `0 + n * (227 ±0)` + // Estimated: `6012 + n * (2668 ±0)` + // Minimum execution time: 54_056_000 picoseconds. + Weight::from_parts(54_912_000, 6012) + // Standard Error: 42_469 + .saturating_add(Weight::from_parts(50_710_258, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((4_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2668).saturating_mul(n.into())) } } diff --git a/substrate/frame/proxy/Cargo.toml b/substrate/frame/proxy/Cargo.toml index fd163e71bc1b062848c9c3700c21bb101f208de1..17930079afd226da977d4796f4bb29ef690f9d3b 100644 --- a/substrate/frame/proxy/Cargo.toml +++ b/substrate/frame/proxy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-proxy" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/proxy/src/weights.rs b/substrate/frame/proxy/src/weights.rs index f30fe73d27ae665fca89761585226d906fd5112c..3c37c91d500e640d7c36e9b056f193e48162630b 100644 --- a/substrate/frame/proxy/src/weights.rs +++ b/substrate/frame/proxy/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_proxy +//! Autogenerated weights for `pallet_proxy` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/proxy/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/proxy/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_proxy. +/// Weight functions needed for `pallet_proxy`. pub trait WeightInfo { fn proxy(p: u32, ) -> Weight; fn proxy_announced(a: u32, p: u32, ) -> Weight; @@ -64,336 +63,352 @@ pub trait WeightInfo { fn kill_pure(p: u32, ) -> Weight; } -/// Weights for pallet_proxy using the Substrate node and recommended hardware. +/// Weights for `pallet_proxy` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Proxy Proxies (r:1 w:0) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 31]`. fn proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `161 + p * (37 ±0)` + // Measured: `306 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 15_182_000 picoseconds. - Weight::from_parts(15_919_146, 4706) - // Standard Error: 1_586 - .saturating_add(Weight::from_parts(31_768, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Minimum execution time: 18_437_000 picoseconds. + Weight::from_parts(19_610_577, 4706) + // Standard Error: 2_531 + .saturating_add(Weight::from_parts(26_001, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) } - /// 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) + /// 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`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, 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: `488 + a * (68 ±0) + p * (37 ±0)` + // Measured: `633 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 40_256_000 picoseconds. - Weight::from_parts(40_373_648, 5698) - // Standard Error: 3_978 - .saturating_add(Weight::from_parts(166_936, 0).saturating_mul(a.into())) - // Standard Error: 4_110 - .saturating_add(Weight::from_parts(54_329, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Minimum execution time: 40_426_000 picoseconds. + Weight::from_parts(40_200_295, 5698) + // Standard Error: 2_922 + .saturating_add(Weight::from_parts(161_885, 0).saturating_mul(a.into())) + // Standard Error: 3_019 + .saturating_add(Weight::from_parts(69_710, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// 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) + /// 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: `403 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 25_040_000 picoseconds. - Weight::from_parts(25_112_188, 5698) - // Standard Error: 2_143 - .saturating_add(Weight::from_parts(189_027, 0).saturating_mul(a.into())) - // Standard Error: 2_214 - .saturating_add(Weight::from_parts(26_683, 0).saturating_mul(p.into())) + // Minimum execution time: 21_905_000 picoseconds. + Weight::from_parts(22_717_430, 5698) + // Standard Error: 2_004 + .saturating_add(Weight::from_parts(153_390, 0).saturating_mul(a.into())) + // Standard Error: 2_071 + .saturating_add(Weight::from_parts(5_676, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// 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) + /// 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: `403 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 24_884_000 picoseconds. - Weight::from_parts(25_359_291, 5698) - // Standard Error: 2_019 - .saturating_add(Weight::from_parts(181_470, 0).saturating_mul(a.into())) - // Standard Error: 2_086 - .saturating_add(Weight::from_parts(17_725, 0).saturating_mul(p.into())) + // Minimum execution time: 21_974_000 picoseconds. + Weight::from_parts(22_484_324, 5698) + // Standard Error: 1_846 + .saturating_add(Weight::from_parts(153_904, 0).saturating_mul(a.into())) + // Standard Error: 1_907 + .saturating_add(Weight::from_parts(9_616, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// 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) + /// 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: `420 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 35_039_000 picoseconds. - Weight::from_parts(36_727_868, 5698) - // Standard Error: 4_463 - .saturating_add(Weight::from_parts(167_060, 0).saturating_mul(a.into())) - // Standard Error: 4_611 - .saturating_add(Weight::from_parts(59_836, 0).saturating_mul(p.into())) + // Minimum execution time: 30_454_000 picoseconds. + Weight::from_parts(32_128_158, 5698) + // Standard Error: 3_778 + .saturating_add(Weight::from_parts(137_366, 0).saturating_mul(a.into())) + // Standard Error: 3_904 + .saturating_add(Weight::from_parts(53_040, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_697_000 picoseconds. - Weight::from_parts(26_611_090, 4706) - // Standard Error: 2_306 - .saturating_add(Weight::from_parts(85_165, 0).saturating_mul(p.into())) + // Minimum execution time: 21_391_000 picoseconds. + Weight::from_parts(22_202_614, 4706) + // Standard Error: 1_750 + .saturating_add(Weight::from_parts(49_639, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_638_000 picoseconds. - Weight::from_parts(26_904_510, 4706) - // Standard Error: 2_669 - .saturating_add(Weight::from_parts(61_668, 0).saturating_mul(p.into())) + // Minimum execution time: 21_375_000 picoseconds. + Weight::from_parts(22_392_601, 4706) + // Standard Error: 2_415 + .saturating_add(Weight::from_parts(40_345, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 22_737_000 picoseconds. - Weight::from_parts(23_618_441, 4706) - // Standard Error: 1_729 - .saturating_add(Weight::from_parts(44_009, 0).saturating_mul(p.into())) + // Minimum execution time: 19_833_000 picoseconds. + Weight::from_parts(20_839_747, 4706) + // Standard Error: 1_742 + .saturating_add(Weight::from_parts(40_874, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `173` // Estimated: `4706` - // Minimum execution time: 27_364_000 picoseconds. - Weight::from_parts(28_632_271, 4706) - // Standard Error: 1_613 - .saturating_add(Weight::from_parts(2_453, 0).saturating_mul(p.into())) + // Minimum execution time: 22_231_000 picoseconds. + Weight::from_parts(23_370_995, 4706) + // Standard Error: 1_521 + .saturating_add(Weight::from_parts(4_892, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `198 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 23_552_000 picoseconds. - Weight::from_parts(24_874_553, 4706) - // Standard Error: 1_919 - .saturating_add(Weight::from_parts(38_799, 0).saturating_mul(p.into())) + // Minimum execution time: 20_614_000 picoseconds. + Weight::from_parts(21_845_970, 4706) + // Standard Error: 1_636 + .saturating_add(Weight::from_parts(34_480, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Proxy Proxies (r:1 w:0) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 31]`. fn proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `161 + p * (37 ±0)` + // Measured: `306 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 15_182_000 picoseconds. - Weight::from_parts(15_919_146, 4706) - // Standard Error: 1_586 - .saturating_add(Weight::from_parts(31_768, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Minimum execution time: 18_437_000 picoseconds. + Weight::from_parts(19_610_577, 4706) + // Standard Error: 2_531 + .saturating_add(Weight::from_parts(26_001, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) } - /// 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) + /// 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`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, 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: `488 + a * (68 ±0) + p * (37 ±0)` + // Measured: `633 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 40_256_000 picoseconds. - Weight::from_parts(40_373_648, 5698) - // Standard Error: 3_978 - .saturating_add(Weight::from_parts(166_936, 0).saturating_mul(a.into())) - // Standard Error: 4_110 - .saturating_add(Weight::from_parts(54_329, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Minimum execution time: 40_426_000 picoseconds. + Weight::from_parts(40_200_295, 5698) + // Standard Error: 2_922 + .saturating_add(Weight::from_parts(161_885, 0).saturating_mul(a.into())) + // Standard Error: 3_019 + .saturating_add(Weight::from_parts(69_710, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// 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) + /// 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: `403 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 25_040_000 picoseconds. - Weight::from_parts(25_112_188, 5698) - // Standard Error: 2_143 - .saturating_add(Weight::from_parts(189_027, 0).saturating_mul(a.into())) - // Standard Error: 2_214 - .saturating_add(Weight::from_parts(26_683, 0).saturating_mul(p.into())) + // Minimum execution time: 21_905_000 picoseconds. + Weight::from_parts(22_717_430, 5698) + // Standard Error: 2_004 + .saturating_add(Weight::from_parts(153_390, 0).saturating_mul(a.into())) + // Standard Error: 2_071 + .saturating_add(Weight::from_parts(5_676, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// 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) + /// 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: `403 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 24_884_000 picoseconds. - Weight::from_parts(25_359_291, 5698) - // Standard Error: 2_019 - .saturating_add(Weight::from_parts(181_470, 0).saturating_mul(a.into())) - // Standard Error: 2_086 - .saturating_add(Weight::from_parts(17_725, 0).saturating_mul(p.into())) + // Minimum execution time: 21_974_000 picoseconds. + Weight::from_parts(22_484_324, 5698) + // Standard Error: 1_846 + .saturating_add(Weight::from_parts(153_904, 0).saturating_mul(a.into())) + // Standard Error: 1_907 + .saturating_add(Weight::from_parts(9_616, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// 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) + /// 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: `420 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 35_039_000 picoseconds. - Weight::from_parts(36_727_868, 5698) - // Standard Error: 4_463 - .saturating_add(Weight::from_parts(167_060, 0).saturating_mul(a.into())) - // Standard Error: 4_611 - .saturating_add(Weight::from_parts(59_836, 0).saturating_mul(p.into())) + // Minimum execution time: 30_454_000 picoseconds. + Weight::from_parts(32_128_158, 5698) + // Standard Error: 3_778 + .saturating_add(Weight::from_parts(137_366, 0).saturating_mul(a.into())) + // Standard Error: 3_904 + .saturating_add(Weight::from_parts(53_040, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_697_000 picoseconds. - Weight::from_parts(26_611_090, 4706) - // Standard Error: 2_306 - .saturating_add(Weight::from_parts(85_165, 0).saturating_mul(p.into())) + // Minimum execution time: 21_391_000 picoseconds. + Weight::from_parts(22_202_614, 4706) + // Standard Error: 1_750 + .saturating_add(Weight::from_parts(49_639, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_638_000 picoseconds. - Weight::from_parts(26_904_510, 4706) - // Standard Error: 2_669 - .saturating_add(Weight::from_parts(61_668, 0).saturating_mul(p.into())) + // Minimum execution time: 21_375_000 picoseconds. + Weight::from_parts(22_392_601, 4706) + // Standard Error: 2_415 + .saturating_add(Weight::from_parts(40_345, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `161 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 22_737_000 picoseconds. - Weight::from_parts(23_618_441, 4706) - // Standard Error: 1_729 - .saturating_add(Weight::from_parts(44_009, 0).saturating_mul(p.into())) + // Minimum execution time: 19_833_000 picoseconds. + Weight::from_parts(20_839_747, 4706) + // Standard Error: 1_742 + .saturating_add(Weight::from_parts(40_874, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `173` // Estimated: `4706` - // Minimum execution time: 27_364_000 picoseconds. - Weight::from_parts(28_632_271, 4706) - // Standard Error: 1_613 - .saturating_add(Weight::from_parts(2_453, 0).saturating_mul(p.into())) + // Minimum execution time: 22_231_000 picoseconds. + Weight::from_parts(23_370_995, 4706) + // Standard Error: 1_521 + .saturating_add(Weight::from_parts(4_892, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// 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: `198 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 23_552_000 picoseconds. - Weight::from_parts(24_874_553, 4706) - // Standard Error: 1_919 - .saturating_add(Weight::from_parts(38_799, 0).saturating_mul(p.into())) + // Minimum execution time: 20_614_000 picoseconds. + Weight::from_parts(21_845_970, 4706) + // Standard Error: 1_636 + .saturating_add(Weight::from_parts(34_480, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/ranked-collective/Cargo.toml b/substrate/frame/ranked-collective/Cargo.toml index 39075b2abf911cc5e4c5a979d0df03e8a7d7eeef..54e84c0b55887d55017f8c16447424a594185f10 100644 --- a/substrate/frame/ranked-collective/Cargo.toml +++ b/substrate/frame/ranked-collective/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-ranked-collective" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.16", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } @@ -27,6 +27,7 @@ sp-core = { path = "../../primitives/core", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } +impl-trait-for-tuples = "0.2.2" [features] default = ["std"] diff --git a/substrate/frame/ranked-collective/src/benchmarking.rs b/substrate/frame/ranked-collective/src/benchmarking.rs index 518428880e4dead922ae32609833ef506db64a43..462f55a238d2a867f900e4653369e5fa9368d874 100644 --- a/substrate/frame/ranked-collective/src/benchmarking.rs +++ b/substrate/frame/ranked-collective/src/benchmarking.rs @@ -33,12 +33,16 @@ 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 make_member, I: 'static>(rank: Rank) -> T::AccountId { let who = account::("member", MemberCount::::get(0), SEED); let who_lookup = T::Lookup::unlookup(who.clone()); assert_ok!(Pallet::::add_member( - T::PromoteOrigin::try_successful_origin() - .expect("PromoteOrigin has no successful origin required for the benchmark"), + T::AddOrigin::try_successful_origin() + .expect("AddOrigin has no successful origin required for the benchmark"), who_lookup.clone(), )); for _ in 0..rank { @@ -56,7 +60,7 @@ benchmarks_instance_pallet! { let who = account::("member", 0, SEED); let who_lookup = T::Lookup::unlookup(who.clone()); let origin = - T::PromoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::AddOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::add_member { who: who_lookup }; }: { call.dispatch_bypass_filter(origin)? } verify { @@ -73,7 +77,7 @@ benchmarks_instance_pallet! { let last = make_member::(rank); let last_index = (0..=rank).map(|r| IdToIndex::::get(r, &last).unwrap()).collect::>(); let origin = - T::DemoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::RemoveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::remove_member { who: who_lookup, min_rank: rank }; }: { call.dispatch_bypass_filter(origin)? } verify { @@ -121,23 +125,11 @@ benchmarks_instance_pallet! { } vote { - let caller: T::AccountId = whitelisted_caller(); - let caller_lookup = T::Lookup::unlookup(caller.clone()); - assert_ok!(Pallet::::add_member( - T::PromoteOrigin::try_successful_origin() - .expect("PromoteOrigin has no successful origin required for the benchmark"), - caller_lookup.clone(), - )); - // Create a poll let class = T::Polls::classes().into_iter().next().unwrap(); let rank = T::MinRankOfClass::convert(class.clone()); - for _ in 0..rank { - assert_ok!(Pallet::::promote_member( - T::PromoteOrigin::try_successful_origin() - .expect("PromoteOrigin has no successful origin required for the benchmark"), - caller_lookup.clone(), - )); - } + + let caller = make_member::(rank); + let caller_lookup = T::Lookup::unlookup(caller.clone()); let poll = T::Polls::create_ongoing(class).expect("Must always be able to create a poll for rank 0"); @@ -173,5 +165,21 @@ benchmarks_instance_pallet! { assert_eq!(Voting::::iter().count(), 0); } - impl_benchmark_test_suite!(RankedCollective, crate::tests::new_test_ext(), crate::tests::Test); + exchange_member { + let who = make_member::(1); + T::BenchmarkSetup::ensure_member(&who); + let who_lookup = T::Lookup::unlookup(who.clone()); + let new_who = account::("new-member", 0, SEED); + let new_who_lookup = T::Lookup::unlookup(new_who.clone()); + let origin = + T::ExchangeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let call = Call::::exchange_member { who: who_lookup, new_who: new_who_lookup }; + }: { call.dispatch_bypass_filter(origin)? } + verify { + assert_eq!(Members::::get(&new_who).unwrap().rank, 1); + assert_eq!(Members::::get(&who), None); + assert_has_event::(Event::MemberExchanged { who, new_who }.into()); + } + + impl_benchmark_test_suite!(RankedCollective, crate::tests::ExtBuilder::default().build(), crate::tests::Test); } diff --git a/substrate/frame/ranked-collective/src/lib.rs b/substrate/frame/ranked-collective/src/lib.rs index 51ee7d7144b14d40e0bb97c1374dcdc8b39c8467..ceaf03de211008ee49bf4a4a1fc961bf8825971a 100644 --- a/substrate/frame/ranked-collective/src/lib.rs +++ b/substrate/frame/ranked-collective/src/lib.rs @@ -39,7 +39,6 @@ //! least a particular rank. #![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit = "128"] use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -54,7 +53,10 @@ use sp_std::{marker::PhantomData, prelude::*}; use frame_support::{ dispatch::{DispatchResultWithPostInfo, PostDispatchInfo}, ensure, impl_ensure_origin_with_arg_ignoring_arg, - traits::{EnsureOrigin, EnsureOriginWithArg, PollStatus, Polling, RankedMembers, VoteTally}, + traits::{ + EnsureOrigin, EnsureOriginWithArg, PollStatus, Polling, RankedMembers, + RankedMembersSwapHandler, VoteTally, + }, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; @@ -161,6 +163,7 @@ impl, I: 'static, M: GetMaxVoters>> crate::Pallet::::do_add_member_to_rank( who, T::MinRankOfClass::convert(class.clone()), + true, ) .expect("could not add members for benchmarks"); } @@ -299,7 +302,7 @@ impl, I: 'static> EnsureOriginWithArg for E #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(min_rank: &Rank) -> Result { let who = frame_benchmarking::account::("successful_origin", 0, 0); - crate::Pallet::::do_add_member_to_rank(who.clone(), *min_rank) + crate::Pallet::::do_add_member_to_rank(who.clone(), *min_rank, true) .expect("Could not add members for benchmarks"); Ok(frame_system::RawOrigin::Signed(who).into()) } @@ -352,7 +355,7 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { let who = frame_benchmarking::account::("successful_origin", 0, 0); - crate::Pallet::::do_add_member_to_rank(who.clone(), MIN_RANK) + crate::Pallet::::do_add_member_to_rank(who.clone(), MIN_RANK, true) .expect("Could not add members for benchmarks"); Ok(frame_system::RawOrigin::Signed(who).into()) } @@ -364,6 +367,13 @@ impl_ensure_origin_with_arg_ignoring_arg! { {} } +/// Helper functions to setup benchmarking. +#[impl_trait_for_tuples::impl_for_tuples(8)] +pub trait BenchmarkSetup { + /// Ensure that this member is registered correctly. + fn ensure_member(acc: &AccountId); +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -382,14 +392,25 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The origin required to add or promote a mmember. The success value indicates the + /// The origin required to add a member. + type AddOrigin: EnsureOrigin; + + /// The origin required to remove a member. + /// + /// The success value indicates the maximum rank *from which* the removal may be. + type RemoveOrigin: EnsureOrigin; + + /// The origin required to promote a member. The success value indicates the /// maximum rank *to which* the promotion may be. type PromoteOrigin: EnsureOrigin; - /// The origin required to demote or remove a member. The success value indicates the - /// maximum rank *from which* the demotion/removal may be. + /// The origin required to demote a member. The success value indicates the + /// maximum rank *from which* the demotion may be. type DemoteOrigin: EnsureOrigin; + /// The origin that can swap the account of a member. + type ExchangeOrigin: EnsureOrigin; + /// The polling system used for our voting. type Polls: Polling, Votes = Votes, Moment = BlockNumberFor>; @@ -398,11 +419,21 @@ pub mod pallet { /// "a rank of at least the poll class". type MinRankOfClass: Convert, Rank>; + /// An external handler that will be notified when two members are swapped. + type MemberSwappedHandler: RankedMembersSwapHandler< + as RankedMembers>::AccountId, + as RankedMembers>::Rank, + >; + /// Convert a rank_delta into a number of votes the rank gets. /// /// Rank_delta is defined as the number of ranks above the minimum required to take part /// in the poll. type VoteWeight: Convert; + + /// Setup a member for benchmarking. + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup: BenchmarkSetup; } /// The number of members in the collective who have at least the rank according to the index @@ -454,6 +485,8 @@ pub mod pallet { /// The member `who` has voted for the `poll` with the given `vote` leading to an updated /// `tally`. Voted { who: T::AccountId, poll: PollIndexOf, vote: VoteRecord, tally: TallyOf }, + /// The member `who` had their `AccountId` changed to `new_who`. + MemberExchanged { who: T::AccountId, new_who: T::AccountId }, } #[pallet::error] @@ -476,28 +509,29 @@ pub mod pallet { InvalidWitness, /// The origin is not sufficiently privileged to do the operation. NoPermission, + /// The new member to exchange is the same as the old member + SameMember, } #[pallet::call] impl, I: 'static> Pallet { /// Introduce a new member. /// - /// - `origin`: Must be the `AdminOrigin`. + /// - `origin`: Must be the `AddOrigin`. /// - `who`: Account of non-member which will become a member. - /// - `rank`: The rank to give the new member. /// /// Weight: `O(1)` #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::add_member())] pub fn add_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { - let _ = T::PromoteOrigin::ensure_origin(origin)?; + T::AddOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; - Self::do_add_member(who) + Self::do_add_member(who, true) } /// Increment the rank of an existing member by one. /// - /// - `origin`: Must be the `AdminOrigin`. + /// - `origin`: Must be the `PromoteOrigin`. /// - `who`: Account of existing member. /// /// Weight: `O(1)` @@ -506,13 +540,13 @@ pub mod pallet { pub fn promote_member(origin: OriginFor, who: AccountIdLookupOf) -> DispatchResult { let max_rank = T::PromoteOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; - Self::do_promote_member(who, Some(max_rank)) + Self::do_promote_member(who, Some(max_rank), true) } /// Decrement the rank of an existing member by one. If the member is already at rank zero, /// then they are removed entirely. /// - /// - `origin`: Must be the `AdminOrigin`. + /// - `origin`: Must be the `DemoteOrigin`. /// - `who`: Account of existing member of rank greater than zero. /// /// Weight: `O(1)`, less if the member's index is highest in its rank. @@ -526,7 +560,7 @@ pub mod pallet { /// Remove the member entirely. /// - /// - `origin`: Must be the `AdminOrigin`. + /// - `origin`: Must be the `RemoveOrigin`. /// - `who`: Account of existing member of rank greater than zero. /// - `min_rank`: The rank of the member or greater. /// @@ -538,16 +572,13 @@ pub mod pallet { who: AccountIdLookupOf, min_rank: Rank, ) -> DispatchResultWithPostInfo { - let max_rank = T::DemoteOrigin::ensure_origin(origin)?; + let max_rank = T::RemoveOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(who)?; let MemberRecord { rank, .. } = Self::ensure_member(&who)?; ensure!(min_rank >= rank, Error::::InvalidWitness); ensure!(max_rank >= rank, Error::::NoPermission); - for r in 0..=rank { - Self::remove_from_rank(&who, r)?; - } - Members::::remove(&who); + Self::do_remove_member_from_rank(&who, rank)?; Self::deposit_event(Event::MemberRemoved { who, rank }); Ok(PostDispatchInfo { actual_weight: Some(T::WeightInfo::remove_member(rank as u32)), @@ -650,6 +681,46 @@ pub mod pallet { pays_fee: Pays::No, }) } + + /// Exchanges a member with a new account and the same existing rank. + /// + /// - `origin`: Must be the `ExchangeOrigin`. + /// - `who`: Account of existing member of rank greater than zero to be exchanged. + /// - `new_who`: New Account of existing member of rank greater than zero to exchanged to. + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::exchange_member())] + pub fn exchange_member( + origin: OriginFor, + who: AccountIdLookupOf, + new_who: AccountIdLookupOf, + ) -> DispatchResult { + T::ExchangeOrigin::ensure_origin(origin)?; + let who = T::Lookup::lookup(who)?; + let new_who = T::Lookup::lookup(new_who)?; + + ensure!(who != new_who, Error::::SameMember); + + let MemberRecord { rank, .. } = Self::ensure_member(&who)?; + + Self::do_remove_member_from_rank(&who, rank)?; + Self::do_add_member_to_rank(new_who.clone(), rank, false)?; + + Self::deposit_event(Event::MemberExchanged { + who: who.clone(), + new_who: new_who.clone(), + }); + T::MemberSwappedHandler::swapped(&who, &new_who, rank); + + Ok(()) + } + } + + #[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 { @@ -683,7 +754,7 @@ pub mod pallet { /// Adds a member into the ranked collective at level 0. /// /// No origin checks are executed. - pub fn do_add_member(who: T::AccountId) -> DispatchResult { + pub fn do_add_member(who: T::AccountId, emit_event: bool) -> DispatchResult { ensure!(!Members::::contains_key(&who), Error::::AlreadyMember); let index = MemberCount::::get(0); let count = index.checked_add(1).ok_or(Overflow)?; @@ -692,7 +763,9 @@ pub mod pallet { IdToIndex::::insert(0, &who, index); IndexToId::::insert(0, index, &who); MemberCount::::insert(0, count); - Self::deposit_event(Event::MemberAdded { who }); + if emit_event { + Self::deposit_event(Event::MemberAdded { who }); + } Ok(()) } @@ -703,6 +776,7 @@ pub mod pallet { pub fn do_promote_member( who: T::AccountId, maybe_max_rank: Option, + emit_event: bool, ) -> DispatchResult { let record = Self::ensure_member(&who)?; let rank = record.rank.checked_add(1).ok_or(Overflow)?; @@ -714,7 +788,9 @@ pub mod pallet { IdToIndex::::insert(rank, &who, index); IndexToId::::insert(rank, index, &who); Members::::insert(&who, MemberRecord { rank }); - Self::deposit_event(Event::RankChanged { who, rank }); + if emit_event { + Self::deposit_event(Event::RankChanged { who, rank }); + } Ok(()) } @@ -747,10 +823,14 @@ pub mod pallet { /// Add a member to the rank collective, and continue to promote them until a certain rank /// is reached. - pub fn do_add_member_to_rank(who: T::AccountId, rank: Rank) -> DispatchResult { - Self::do_add_member(who.clone())?; + pub fn do_add_member_to_rank( + who: T::AccountId, + rank: Rank, + emit_event: bool, + ) -> DispatchResult { + Self::do_add_member(who.clone(), emit_event)?; for _ in 0..rank { - Self::do_promote_member(who.clone(), None)?; + Self::do_promote_member(who.clone(), None, emit_event)?; } Ok(()) } @@ -763,6 +843,141 @@ pub mod pallet { use frame_support::traits::CallerTrait; o.as_signed().and_then(Self::rank_of) } + + /// Removes a member from the rank collective + pub fn do_remove_member_from_rank(who: &T::AccountId, rank: Rank) -> DispatchResult { + for r in 0..=rank { + Self::remove_from_rank(&who, r)?; + } + Members::::remove(&who); + Ok(()) + } + } + + #[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::try_state_members()?; + Self::try_state_index()?; + + Ok(()) + } + + /// ### Invariants of Member storage items + /// + /// Total number of [`Members`] in storage should be >= [`MemberIndex`] of a [`Rank`] in + /// [`MemberCount`]. + /// [`Rank`] in Members should be in [`MemberCount`] + /// [`Sum`] of [`MemberCount`] index should be the same as the sum of all the index attained + /// for rank possessed by [`Members`] + fn try_state_members() -> Result<(), sp_runtime::TryRuntimeError> { + MemberCount::::iter().try_for_each(|(_, member_index)| -> DispatchResult { + let total_members = Members::::iter().count(); + ensure!( + total_members as u32 >= member_index, + "Total count of `Members` should be greater than or equal to the number of `MemberIndex` of a particular `Rank` in `MemberCount`." + ); + + Ok(()) + })?; + + let mut sum_of_member_rank_indexes = 0; + Members::::iter().try_for_each(|(_, member_record)| -> DispatchResult { + ensure!( + Self::is_rank_in_member_count(member_record.rank.into()), + "`Rank` in Members should be in `MemberCount`" + ); + + sum_of_member_rank_indexes += Self::determine_index_of_a_rank(member_record.rank); + + Ok(()) + })?; + + let sum_of_all_member_count_indexes = + MemberCount::::iter_values().fold(0, |sum, index| sum + index); + ensure!( + sum_of_all_member_count_indexes == sum_of_member_rank_indexes as u32, + "Sum of `MemberCount` index should be the same as the sum of all the index attained for rank possessed by `Members`" + ); + Ok(()) + } + + /// ### Invariants of Index storage items + /// [`Member`] in storage of [`IdToIndex`] should be the same as [`Member`] in [`IndexToId`] + /// [`Rank`] in [`IdToIndex`] should be the same as the the [`Rank`] in [`IndexToId`] + /// [`Rank`] of the member [`who`] in [`IdToIndex`] should be the same as the [`Rank`] of + /// the member [`who`] in [`Members`] + fn try_state_index() -> Result<(), sp_runtime::TryRuntimeError> { + IdToIndex::::iter().try_for_each( + |(rank, who, member_index)| -> DispatchResult { + let who_from_index = IndexToId::::get(rank, member_index).unwrap(); + ensure!( + who == who_from_index, + "`Member` in storage of `IdToIndex` should be the same as `Member` in `IndexToId`." + ); + + ensure!( + Self::is_rank_in_index_to_id_storage(rank.into()), + "`Rank` in `IdToIndex` should be the same as the `Rank` in `IndexToId`" + ); + Ok(()) + }, + )?; + + Members::::iter().try_for_each(|(who, member_record)| -> DispatchResult { + ensure!( + Self::is_who_rank_in_id_to_index_storage(who, member_record.rank), + "`Rank` of the member `who` in `IdToIndex` should be the same as the `Rank` of the member `who` in `Members`" + ); + + Ok(()) + })?; + + Ok(()) + } + + /// Checks if a rank is part of the `MemberCount` + fn is_rank_in_member_count(rank: u32) -> bool { + for (r, _) in MemberCount::::iter() { + if r as u32 == rank { + return true; + } + } + + return false; + } + + /// Checks if a rank is the same as the rank `IndexToId` + fn is_rank_in_index_to_id_storage(rank: u32) -> bool { + for (r, _, _) in IndexToId::::iter() { + if r as u32 == rank { + return true; + } + } + + return false; + } + + /// Checks if a member(who) rank is the same as the rank of a member(who) in `IdToIndex` + fn is_who_rank_in_id_to_index_storage(who: T::AccountId, rank: u16) -> bool { + for (rank_, who_, _) in IdToIndex::::iter() { + if who == who_ && rank == rank_ { + return true; + } + } + + return false; + } + + /// Determines the total index for a rank + fn determine_index_of_a_rank(rank: u16) -> u16 { + let mut sum = 0; + for _ in 0..rank + 1 { + sum += 1; + } + sum + } } impl, I: 'static> RankedMembers for Pallet { @@ -778,11 +993,11 @@ pub mod pallet { } fn induct(who: &Self::AccountId) -> DispatchResult { - Self::do_add_member(who.clone()) + Self::do_add_member(who.clone(), true) } fn promote(who: &Self::AccountId) -> DispatchResult { - Self::do_promote_member(who.clone(), None) + Self::do_promote_member(who.clone(), None, true) } fn demote(who: &Self::AccountId) -> DispatchResult { diff --git a/substrate/frame/ranked-collective/src/tests.rs b/substrate/frame/ranked-collective/src/tests.rs index b3605169339af6a298ca3d623c2f05383729d92b..31add52d90afa9c8756be88b425a08b453b60892 100644 --- a/substrate/frame/ranked-collective/src/tests.rs +++ b/substrate/frame/ranked-collective/src/tests.rs @@ -26,7 +26,10 @@ use frame_support::{ traits::{ConstU16, EitherOf, MapSuccess, Polling}, }; use sp_core::Get; -use sp_runtime::{traits::ReduceBy, BuildStorage}; +use sp_runtime::{ + traits::{ReduceBy, ReplaceWithDefault}, + BuildStorage, +}; use super::*; use crate as pallet_ranked_collective; @@ -152,6 +155,8 @@ parameter_types! { impl Config for Test { type WeightInfo = (); type RuntimeEvent = RuntimeEvent; + type AddOrigin = MapSuccess>; + type RemoveOrigin = Self::DemoteOrigin; type PromoteOrigin = EitherOf< // Root can promote arbitrarily. frame_system::EnsureRootWithSuccess>, @@ -164,16 +169,42 @@ impl Config for Test { // Members can demote up to the rank of 3 below them. MapSuccess, ReduceBy>>, >; + type ExchangeOrigin = EitherOf< + // Root can exchange arbitrarily. + frame_system::EnsureRootWithSuccess>, + // Members can exchange up to the rank of 2 below them. + MapSuccess, ReduceBy>>, + >; type Polls = TestPolls; type MinRankOfClass = MinRankOfClass; + type MemberSwappedHandler = (); type VoteWeight = Geometric; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = (); } -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext +pub struct ExtBuilder {} + +impl Default for ExtBuilder { + fn default() -> Self { + Self {} + } +} + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } + + pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + self.build().execute_with(|| { + test(); + Club::do_try_state().expect("All invariants must hold after a test"); + }) + } } fn next_block() { @@ -211,14 +242,14 @@ fn completed_poll_should_panic() { #[test] fn basic_stuff() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); }); } #[test] fn member_lifecycle_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::demote_member(RuntimeOrigin::root(), 1)); @@ -230,7 +261,7 @@ fn member_lifecycle_works() { #[test] fn add_remove_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_noop!(Club::add_member(RuntimeOrigin::signed(1), 1), DispatchError::BadOrigin); assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); assert_eq!(member_count(0), 1); @@ -260,7 +291,7 @@ fn add_remove_works() { #[test] fn promote_demote_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_noop!(Club::add_member(RuntimeOrigin::signed(1), 1), DispatchError::BadOrigin); assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); assert_eq!(member_count(0), 1); @@ -291,7 +322,7 @@ fn promote_demote_works() { #[test] fn promote_demote_by_rank_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); for _ in 0..7 { assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); @@ -358,7 +389,7 @@ fn promote_demote_by_rank_works() { #[test] fn voting_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_ok!(Club::add_member(RuntimeOrigin::root(), 0)); assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); @@ -392,7 +423,7 @@ fn voting_works() { #[test] fn cleanup_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::add_member(RuntimeOrigin::root(), 2)); @@ -419,7 +450,7 @@ fn cleanup_works() { #[test] fn remove_member_cleanup_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::add_member(RuntimeOrigin::root(), 2)); @@ -445,7 +476,7 @@ fn remove_member_cleanup_works() { #[test] fn ensure_ranked_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); assert_ok!(Club::add_member(RuntimeOrigin::root(), 2)); @@ -514,10 +545,10 @@ fn ensure_ranked_works() { #[test] fn do_add_member_to_rank_works() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { let max_rank = 9u16; - assert_ok!(Club::do_add_member_to_rank(69, max_rank / 2)); - assert_ok!(Club::do_add_member_to_rank(1337, max_rank)); + assert_ok!(Club::do_add_member_to_rank(69, max_rank / 2, true)); + assert_ok!(Club::do_add_member_to_rank(1337, max_rank, true)); for i in 0..=max_rank { if i <= max_rank / 2 { assert_eq!(member_count(i), 2); @@ -531,7 +562,7 @@ fn do_add_member_to_rank_works() { #[test] fn tally_support_correct() { - new_test_ext().execute_with(|| { + ExtBuilder::default().build_and_execute(|| { // add members, // rank 1: accounts 1, 2, 3 // rank 2: accounts 2, 3 @@ -568,3 +599,49 @@ fn tally_support_correct() { MinRankOfClassDelta::set(0); }); } + +#[test] +fn exchange_member_works() { + ExtBuilder::default().build_and_execute(|| { + assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); + assert_eq!(member_count(0), 1); + + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); + + let member_record = MemberRecord { rank: 1 }; + assert_eq!(Members::::get(1), Some(member_record.clone())); + assert_eq!(Members::::get(2), None); + + assert_ok!(Club::exchange_member(RuntimeOrigin::root(), 1, 2)); + assert_eq!(member_count(0), 1); + + assert_eq!(Members::::get(1), None); + assert_eq!(Members::::get(2), Some(member_record)); + + assert_ok!(Club::add_member(RuntimeOrigin::root(), 3)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 3)); + + assert_noop!( + Club::exchange_member(RuntimeOrigin::signed(3), 2, 1), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn exchange_member_same_noops() { + ExtBuilder::default().build_and_execute(|| { + assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); + assert_ok!(Club::add_member(RuntimeOrigin::root(), 2)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 2)); + + // Swapping the same accounts is a noop: + assert_noop!(Club::exchange_member(RuntimeOrigin::root(), 1, 1), Error::::SameMember); + // Swapping with a different member is a noop: + assert_noop!( + Club::exchange_member(RuntimeOrigin::root(), 1, 2), + Error::::AlreadyMember + ); + }); +} diff --git a/substrate/frame/ranked-collective/src/weights.rs b/substrate/frame/ranked-collective/src/weights.rs index 9f1a0a8180446cd81e13f1420e5db73e070a2739..5721858f7e2f1d53ada65c2adc689dc9bd8c5a3d 100644 --- a/substrate/frame/ranked-collective/src/weights.rs +++ b/substrate/frame/ranked-collective/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_ranked_collective +//! Autogenerated weights for `pallet_ranked_collective` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/ranked-collective/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/ranked-collective/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_ranked_collective. +/// Weight functions needed for `pallet_ranked_collective`. pub trait WeightInfo { fn add_member() -> Weight; fn remove_member(r: u32, ) -> Weight; @@ -58,247 +57,298 @@ pub trait WeightInfo { fn demote_member(r: u32, ) -> Weight; fn vote() -> Weight; fn cleanup_poll(n: u32, ) -> Weight; + fn exchange_member() -> Weight; } -/// Weights for pallet_ranked_collective using the Substrate node and recommended hardware. +/// Weights for `pallet_ranked_collective` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn add_member() -> Weight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3507` - // Minimum execution time: 17_245_000 picoseconds. - Weight::from_parts(17_930_000, 3507) + // Minimum execution time: 15_389_000 picoseconds. + Weight::from_parts(15_901_000, 3507) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:11 w:11) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:11 w:11) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:11 w:11) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:11 w:11) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:11 w:22) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:11 w:22) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn remove_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `616 + r * (281 ±0)` // Estimated: `3519 + r * (2529 ±0)` - // Minimum execution time: 29_534_000 picoseconds. - Weight::from_parts(32_847_495, 3519) - // Standard Error: 24_211 - .saturating_add(Weight::from_parts(13_949_639, 0).saturating_mul(r.into())) + // Minimum execution time: 29_541_000 picoseconds. + Weight::from_parts(32_239_358, 3519) + // Standard Error: 23_406 + .saturating_add(Weight::from_parts(16_030_191, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(6_u64)) + .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 2529).saturating_mul(r.into())) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn promote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `314 + r * (17 ±0)` // Estimated: `3507` - // Minimum execution time: 20_333_000 picoseconds. - Weight::from_parts(21_592_224, 3507) - // Standard Error: 6_423 - .saturating_add(Weight::from_parts(321_314, 0).saturating_mul(r.into())) + // Minimum execution time: 17_939_000 picoseconds. + Weight::from_parts(19_290_416, 3507) + // Standard Error: 5_710 + .saturating_add(Weight::from_parts(374_399, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:1 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:1 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:1 w:2) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:1 w:2) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn demote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `632 + r * (72 ±0)` // Estimated: `3519` - // Minimum execution time: 29_446_000 picoseconds. - Weight::from_parts(32_447_715, 3519) - // Standard Error: 28_791 - .saturating_add(Weight::from_parts(822_890, 0).saturating_mul(r.into())) + // Minimum execution time: 29_609_000 picoseconds. + Weight::from_parts(32_686_167, 3519) + // Standard Error: 27_588 + .saturating_add(Weight::from_parts(789_212, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedPolls ReferendumInfoFor (r:1 w:1) - /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) - /// Storage: RankedCollective Voting (r:1 w:1) - /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedPolls::ReferendumInfoFor` (r:1 w:1) + /// Proof: `RankedPolls::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(330), added: 2805, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Voting` (r:1 w:1) + /// Proof: `RankedCollective::Voting` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote() -> Weight { // Proof Size summary in bytes: // Measured: `628` // Estimated: `219984` - // Minimum execution time: 45_474_000 picoseconds. - Weight::from_parts(47_228_000, 219984) + // Minimum execution time: 41_151_000 picoseconds. + Weight::from_parts(42_435_000, 219984) .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: RankedPolls ReferendumInfoFor (r:1 w:0) - /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) - /// Storage: RankedCollective VotingCleanup (r:1 w:0) - /// Proof: RankedCollective VotingCleanup (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: RankedCollective Voting (r:100 w:100) - /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `RankedPolls::ReferendumInfoFor` (r:1 w:0) + /// Proof: `RankedPolls::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(330), added: 2805, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::VotingCleanup` (r:1 w:0) + /// Proof: `RankedCollective::VotingCleanup` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Voting` (r:100 w:100) + /// Proof: `RankedCollective::Voting` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. fn cleanup_poll(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `462 + n * (50 ±0)` // Estimated: `3795 + n * (2540 ±0)` - // Minimum execution time: 13_903_000 picoseconds. - Weight::from_parts(18_209_102, 3795) - // Standard Error: 2_556 - .saturating_add(Weight::from_parts(1_237_454, 0).saturating_mul(n.into())) + // Minimum execution time: 13_563_000 picoseconds. + Weight::from_parts(17_495_215, 3795) + // Standard Error: 2_294 + .saturating_add(Weight::from_parts(1_207_393, 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((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2540).saturating_mul(n.into())) } + /// Storage: `RankedCollective::Members` (r:2 w:2) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:2 w:2) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:2 w:4) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:2 w:2) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:0) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:2 w:2) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:2) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + fn exchange_member() -> Weight { + // Proof Size summary in bytes: + // Measured: `625` + // Estimated: `19894` + // Minimum execution time: 70_713_000 picoseconds. + Weight::from_parts(72_831_000, 19894) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)) + } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) fn add_member() -> Weight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `3507` - // Minimum execution time: 17_245_000 picoseconds. - Weight::from_parts(17_930_000, 3507) + // Minimum execution time: 15_389_000 picoseconds. + Weight::from_parts(15_901_000, 3507) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:11 w:11) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:11 w:11) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:11 w:11) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:11 w:11) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:11 w:22) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:11 w:22) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn remove_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `616 + r * (281 ±0)` // Estimated: `3519 + r * (2529 ±0)` - // Minimum execution time: 29_534_000 picoseconds. - Weight::from_parts(32_847_495, 3519) - // Standard Error: 24_211 - .saturating_add(Weight::from_parts(13_949_639, 0).saturating_mul(r.into())) + // Minimum execution time: 29_541_000 picoseconds. + Weight::from_parts(32_239_358, 3519) + // Standard Error: 23_406 + .saturating_add(Weight::from_parts(16_030_191, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 2529).saturating_mul(r.into())) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:0 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:0 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:1) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:0 w:1) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn promote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `314 + r * (17 ±0)` // Estimated: `3507` - // Minimum execution time: 20_333_000 picoseconds. - Weight::from_parts(21_592_224, 3507) - // Standard Error: 6_423 - .saturating_add(Weight::from_parts(321_314, 0).saturating_mul(r.into())) + // Minimum execution time: 17_939_000 picoseconds. + Weight::from_parts(19_290_416, 3507) + // Standard Error: 5_710 + .saturating_add(Weight::from_parts(374_399, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: RankedCollective Members (r:1 w:1) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedCollective MemberCount (r:1 w:1) - /// Proof: RankedCollective MemberCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: RankedCollective IdToIndex (r:1 w:1) - /// Proof: RankedCollective IdToIndex (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) - /// Storage: RankedCollective IndexToId (r:1 w:1) - /// Proof: RankedCollective IndexToId (max_values: None, max_size: Some(54), added: 2529, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:1) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:1 w:1) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:1 w:2) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:1 w:2) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 10]`. fn demote_member(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `632 + r * (72 ±0)` // Estimated: `3519` - // Minimum execution time: 29_446_000 picoseconds. - Weight::from_parts(32_447_715, 3519) - // Standard Error: 28_791 - .saturating_add(Weight::from_parts(822_890, 0).saturating_mul(r.into())) + // Minimum execution time: 29_609_000 picoseconds. + Weight::from_parts(32_686_167, 3519) + // Standard Error: 27_588 + .saturating_add(Weight::from_parts(789_212, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: RankedPolls ReferendumInfoFor (r:1 w:1) - /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) - /// Storage: RankedCollective Voting (r:1 w:1) - /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedPolls::ReferendumInfoFor` (r:1 w:1) + /// Proof: `RankedPolls::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(330), added: 2805, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Voting` (r:1 w:1) + /// Proof: `RankedCollective::Voting` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote() -> Weight { // Proof Size summary in bytes: // Measured: `628` // Estimated: `219984` - // Minimum execution time: 45_474_000 picoseconds. - Weight::from_parts(47_228_000, 219984) + // Minimum execution time: 41_151_000 picoseconds. + Weight::from_parts(42_435_000, 219984) .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: RankedPolls ReferendumInfoFor (r:1 w:0) - /// Proof: RankedPolls ReferendumInfoFor (max_values: None, max_size: Some(330), added: 2805, mode: MaxEncodedLen) - /// Storage: RankedCollective VotingCleanup (r:1 w:0) - /// Proof: RankedCollective VotingCleanup (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: RankedCollective Voting (r:100 w:100) - /// Proof: RankedCollective Voting (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `RankedPolls::ReferendumInfoFor` (r:1 w:0) + /// Proof: `RankedPolls::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(330), added: 2805, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::VotingCleanup` (r:1 w:0) + /// Proof: `RankedCollective::VotingCleanup` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Voting` (r:100 w:100) + /// Proof: `RankedCollective::Voting` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 100]`. fn cleanup_poll(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `462 + n * (50 ±0)` // Estimated: `3795 + n * (2540 ±0)` - // Minimum execution time: 13_903_000 picoseconds. - Weight::from_parts(18_209_102, 3795) - // Standard Error: 2_556 - .saturating_add(Weight::from_parts(1_237_454, 0).saturating_mul(n.into())) + // Minimum execution time: 13_563_000 picoseconds. + Weight::from_parts(17_495_215, 3795) + // Standard Error: 2_294 + .saturating_add(Weight::from_parts(1_207_393, 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((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2540).saturating_mul(n.into())) } + /// Storage: `RankedCollective::Members` (r:2 w:2) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::MemberCount` (r:2 w:2) + /// Proof: `RankedCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IdToIndex` (r:2 w:4) + /// Proof: `RankedCollective::IdToIndex` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::Member` (r:2 w:2) + /// Proof: `CoreFellowship::Member` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `CoreFellowship::MemberEvidence` (r:1 w:0) + /// Proof: `CoreFellowship::MemberEvidence` (`max_values`: None, `max_size`: Some(16429), added: 18904, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:2 w:2) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::IndexToId` (r:0 w:2) + /// Proof: `RankedCollective::IndexToId` (`max_values`: None, `max_size`: Some(54), added: 2529, mode: `MaxEncodedLen`) + fn exchange_member() -> Weight { + // Proof Size summary in bytes: + // Measured: `625` + // Estimated: `19894` + // Minimum execution time: 70_713_000 picoseconds. + Weight::from_parts(72_831_000, 19894) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(14_u64)) + } } diff --git a/substrate/frame/recovery/Cargo.toml b/substrate/frame/recovery/Cargo.toml index 6afd494bf7e1cd27d5d3d8f7f8f0960b70092251..421c799513731d06daf9ae704a940819443405bd 100644 --- a/substrate/frame/recovery/Cargo.toml +++ b/substrate/frame/recovery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-recovery" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/recovery/src/mock.rs b/substrate/frame/recovery/src/mock.rs index 9adb2b80931a81794923d88baf82bcd63d9b3d6d..89374527e069c4eac7eac1173502768d1cef684d 100644 --- a/substrate/frame/recovery/src/mock.rs +++ b/substrate/frame/recovery/src/mock.rs @@ -22,13 +22,9 @@ use super::*; use crate as recovery; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU32, ConstU64, OnFinalize, OnInitialize}, -}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, + traits::{OnFinalize, OnInitialize}, }; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -43,29 +39,8 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } parameter_types! { @@ -86,7 +61,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { diff --git a/substrate/frame/recovery/src/weights.rs b/substrate/frame/recovery/src/weights.rs index 84b19ae694eec1107b9229c1894646cb8368bc0d..fe5dbcfc2222d2a53cf1f6d7438bb2175470d1f2 100644 --- a/substrate/frame/recovery/src/weights.rs +++ b/substrate/frame/recovery/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_recovery +//! Autogenerated weights for `pallet_recovery` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/recovery/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/recovery/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_recovery. +/// Weight functions needed for `pallet_recovery`. pub trait WeightInfo { fn as_recovered() -> Weight; fn set_recovered() -> Weight; @@ -63,258 +62,266 @@ pub trait WeightInfo { fn cancel_recovered() -> Weight; } -/// Weights for pallet_recovery using the Substrate node and recommended hardware. +/// Weights for `pallet_recovery` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Recovery Proxy (r:1 w:0) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Proxy` (r:1 w:0) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn as_recovered() -> Weight { // Proof Size summary in bytes: - // Measured: `281` - // Estimated: `3545` - // Minimum execution time: 9_360_000 picoseconds. - Weight::from_parts(9_773_000, 3545) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `497` + // Estimated: `3997` + // Minimum execution time: 14_898_000 picoseconds. + Weight::from_parts(15_464_000, 3997) + .saturating_add(T::DbWeight::get().reads(3_u64)) } - /// Storage: Recovery Proxy (r:0 w:1) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Proxy` (r:0 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) fn set_recovered() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_146_000 picoseconds. - Weight::from_parts(9_507_000, 0) + // Minimum execution time: 7_424_000 picoseconds. + Weight::from_parts(7_830_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:1) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn create_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `175` + // Measured: `246` // Estimated: `3816` - // Minimum execution time: 26_472_000 picoseconds. - Weight::from_parts(27_917_651, 3816) - // Standard Error: 7_129 - .saturating_add(Weight::from_parts(59_239, 0).saturating_mul(n.into())) + // Minimum execution time: 21_968_000 picoseconds. + Weight::from_parts(23_594_232, 3816) + // Standard Error: 5_848 + .saturating_add(Weight::from_parts(24_843, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:0) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) - /// Storage: Recovery ActiveRecoveries (r:1 w:1) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) fn initiate_recovery() -> Weight { // Proof Size summary in bytes: - // Measured: `272` + // Measured: `343` // Estimated: `3854` - // Minimum execution time: 29_618_000 picoseconds. - Weight::from_parts(30_192_000, 3854) + // Minimum execution time: 25_494_000 picoseconds. + Weight::from_parts(26_290_000, 3854) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:0) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) - /// Storage: Recovery ActiveRecoveries (r:1 w:1) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn vouch_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `360 + n * (64 ±0)` + // Measured: `431 + n * (64 ±0)` // Estimated: `3854` - // Minimum execution time: 19_464_000 picoseconds. - Weight::from_parts(20_642_522, 3854) - // Standard Error: 5_974 - .saturating_add(Weight::from_parts(142_308, 0).saturating_mul(n.into())) + // Minimum execution time: 17_044_000 picoseconds. + Weight::from_parts(18_299_617, 3854) + // Standard Error: 5_580 + .saturating_add(Weight::from_parts(187_568, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:0) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) - /// Storage: Recovery ActiveRecoveries (r:1 w:0) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) - /// Storage: Recovery Proxy (r:1 w:1) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn claim_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `392 + n * (64 ±0)` + // Measured: `463 + n * (64 ±0)` // Estimated: `3854` - // Minimum execution time: 23_656_000 picoseconds. - Weight::from_parts(24_903_269, 3854) - // Standard Error: 5_771 - .saturating_add(Weight::from_parts(117_343, 0).saturating_mul(n.into())) + // Minimum execution time: 22_186_000 picoseconds. + Weight::from_parts(23_476_807, 3854) + // Standard Error: 6_392 + .saturating_add(Weight::from_parts(89_770, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Recovery ActiveRecoveries (r:1 w:1) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, 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 `n` is `[1, 9]`. fn close_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `513 + n * (32 ±0)` + // Measured: `584 + n * (32 ±0)` // Estimated: `3854` - // Minimum execution time: 34_866_000 picoseconds. - Weight::from_parts(36_368_748, 3854) - // Standard Error: 6_600 - .saturating_add(Weight::from_parts(118_610, 0).saturating_mul(n.into())) + // Minimum execution time: 31_045_000 picoseconds. + Weight::from_parts(32_623_578, 3854) + // Standard Error: 7_203 + .saturating_add(Weight::from_parts(61_466, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Recovery ActiveRecoveries (r:1 w:0) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) - /// Storage: Recovery Recoverable (r:1 w:1) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn remove_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `270 + n * (32 ±0)` + // Measured: `341 + n * (32 ±0)` // Estimated: `3854` - // Minimum execution time: 31_405_000 picoseconds. - Weight::from_parts(32_552_838, 3854) - // Standard Error: 8_043 - .saturating_add(Weight::from_parts(171_605, 0).saturating_mul(n.into())) + // Minimum execution time: 27_925_000 picoseconds. + Weight::from_parts(29_258_264, 3854) + // Standard Error: 8_192 + .saturating_add(Weight::from_parts(128_124, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Recovery Proxy (r:1 w:1) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) fn cancel_recovered() -> Weight { // Proof Size summary in bytes: - // Measured: `281` + // Measured: `352` // Estimated: `3545` - // Minimum execution time: 11_530_000 picoseconds. - Weight::from_parts(11_851_000, 3545) + // Minimum execution time: 11_623_000 picoseconds. + Weight::from_parts(12_043_000, 3545) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Recovery Proxy (r:1 w:0) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Proxy` (r:1 w:0) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn as_recovered() -> Weight { // Proof Size summary in bytes: - // Measured: `281` - // Estimated: `3545` - // Minimum execution time: 9_360_000 picoseconds. - Weight::from_parts(9_773_000, 3545) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `497` + // Estimated: `3997` + // Minimum execution time: 14_898_000 picoseconds. + Weight::from_parts(15_464_000, 3997) + .saturating_add(RocksDbWeight::get().reads(3_u64)) } - /// Storage: Recovery Proxy (r:0 w:1) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Proxy` (r:0 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) fn set_recovered() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_146_000 picoseconds. - Weight::from_parts(9_507_000, 0) + // Minimum execution time: 7_424_000 picoseconds. + Weight::from_parts(7_830_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:1) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn create_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `175` + // Measured: `246` // Estimated: `3816` - // Minimum execution time: 26_472_000 picoseconds. - Weight::from_parts(27_917_651, 3816) - // Standard Error: 7_129 - .saturating_add(Weight::from_parts(59_239, 0).saturating_mul(n.into())) + // Minimum execution time: 21_968_000 picoseconds. + Weight::from_parts(23_594_232, 3816) + // Standard Error: 5_848 + .saturating_add(Weight::from_parts(24_843, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:0) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) - /// Storage: Recovery ActiveRecoveries (r:1 w:1) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) fn initiate_recovery() -> Weight { // Proof Size summary in bytes: - // Measured: `272` + // Measured: `343` // Estimated: `3854` - // Minimum execution time: 29_618_000 picoseconds. - Weight::from_parts(30_192_000, 3854) + // Minimum execution time: 25_494_000 picoseconds. + Weight::from_parts(26_290_000, 3854) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:0) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) - /// Storage: Recovery ActiveRecoveries (r:1 w:1) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn vouch_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `360 + n * (64 ±0)` + // Measured: `431 + n * (64 ±0)` // Estimated: `3854` - // Minimum execution time: 19_464_000 picoseconds. - Weight::from_parts(20_642_522, 3854) - // Standard Error: 5_974 - .saturating_add(Weight::from_parts(142_308, 0).saturating_mul(n.into())) + // Minimum execution time: 17_044_000 picoseconds. + Weight::from_parts(18_299_617, 3854) + // Standard Error: 5_580 + .saturating_add(Weight::from_parts(187_568, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Recovery Recoverable (r:1 w:0) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) - /// Storage: Recovery ActiveRecoveries (r:1 w:0) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) - /// Storage: Recovery Proxy (r:1 w:1) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn claim_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `392 + n * (64 ±0)` + // Measured: `463 + n * (64 ±0)` // Estimated: `3854` - // Minimum execution time: 23_656_000 picoseconds. - Weight::from_parts(24_903_269, 3854) - // Standard Error: 5_771 - .saturating_add(Weight::from_parts(117_343, 0).saturating_mul(n.into())) + // Minimum execution time: 22_186_000 picoseconds. + Weight::from_parts(23_476_807, 3854) + // Standard Error: 6_392 + .saturating_add(Weight::from_parts(89_770, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Recovery ActiveRecoveries (r:1 w:1) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, 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 `n` is `[1, 9]`. fn close_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `513 + n * (32 ±0)` + // Measured: `584 + n * (32 ±0)` // Estimated: `3854` - // Minimum execution time: 34_866_000 picoseconds. - Weight::from_parts(36_368_748, 3854) - // Standard Error: 6_600 - .saturating_add(Weight::from_parts(118_610, 0).saturating_mul(n.into())) + // Minimum execution time: 31_045_000 picoseconds. + Weight::from_parts(32_623_578, 3854) + // Standard Error: 7_203 + .saturating_add(Weight::from_parts(61_466, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Recovery ActiveRecoveries (r:1 w:0) - /// Proof: Recovery ActiveRecoveries (max_values: None, max_size: Some(389), added: 2864, mode: MaxEncodedLen) - /// Storage: Recovery Recoverable (r:1 w:1) - /// Proof: Recovery Recoverable (max_values: None, max_size: Some(351), added: 2826, mode: MaxEncodedLen) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 9]`. fn remove_recovery(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `270 + n * (32 ±0)` + // Measured: `341 + n * (32 ±0)` // Estimated: `3854` - // Minimum execution time: 31_405_000 picoseconds. - Weight::from_parts(32_552_838, 3854) - // Standard Error: 8_043 - .saturating_add(Weight::from_parts(171_605, 0).saturating_mul(n.into())) + // Minimum execution time: 27_925_000 picoseconds. + Weight::from_parts(29_258_264, 3854) + // Standard Error: 8_192 + .saturating_add(Weight::from_parts(128_124, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Recovery Proxy (r:1 w:1) - /// Proof: Recovery Proxy (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) fn cancel_recovered() -> Weight { // Proof Size summary in bytes: - // Measured: `281` + // Measured: `352` // Estimated: `3545` - // Minimum execution time: 11_530_000 picoseconds. - Weight::from_parts(11_851_000, 3545) + // Minimum execution time: 11_623_000 picoseconds. + Weight::from_parts(12_043_000, 3545) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/referenda/Cargo.toml b/substrate/frame/referenda/Cargo.toml index d38be0d283e9bc9a98b7b5672e7c57b4b66bb752..2dfb25a2fd3a5b50bdc7828e50c8e247cd7f936a 100644 --- a/substrate/frame/referenda/Cargo.toml +++ b/substrate/frame/referenda/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-referenda" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,7 +21,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", ] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", features = ["derive"], optional = true } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } sp-arithmetic = { path = "../../primitives/arithmetic", default-features = false } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } @@ -29,7 +29,7 @@ frame-system = { path = "../system", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] assert_matches = { version = "1.5" } diff --git a/substrate/frame/referenda/src/lib.rs b/substrate/frame/referenda/src/lib.rs index 8912f9ad217371846fc193f495ff1e49b0d18f00..e616056c3022abe796cd494e91fa4e9565843e60 100644 --- a/substrate/frame/referenda/src/lib.rs +++ b/substrate/frame/referenda/src/lib.rs @@ -143,7 +143,7 @@ pub mod pallet { use frame_support::{pallet_prelude::*, traits::EnsureOriginWithArg}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] @@ -433,6 +433,11 @@ pub mod pallet { Self::do_try_state()?; Ok(()) } + + #[cfg(any(feature = "std", test))] + fn integrity_test() { + T::Tracks::check_integrity().expect("Static tracks configuration is valid."); + } } #[pallet::call] diff --git a/substrate/frame/referenda/src/migration.rs b/substrate/frame/referenda/src/migration.rs index a80897242eec67aad194f8deaf1cd6527533eab9..631eb7340e567e8115e1755d513980fb148700c5 100644 --- a/substrate/frame/referenda/src/migration.rs +++ b/substrate/frame/referenda/src/migration.rs @@ -109,16 +109,16 @@ pub mod v1 { } fn on_runtime_upgrade() -> Weight { - let current_version = Pallet::::current_storage_version(); - let onchain_version = Pallet::::on_chain_storage_version(); + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::on_chain_storage_version(); let mut weight = T::DbWeight::get().reads(1); log::info!( target: TARGET, - "running migration with current storage version {:?} / onchain {:?}.", - current_version, - onchain_version + "running migration with in-code storage version {:?} / onchain {:?}.", + in_code_version, + on_chain_version ); - if onchain_version != 0 { + if on_chain_version != 0 { log::warn!(target: TARGET, "skipping migration from v0 to v1."); return weight } @@ -149,8 +149,8 @@ pub mod v1 { #[cfg(feature = "try-runtime")] fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { - let onchain_version = Pallet::::on_chain_storage_version(); - ensure!(onchain_version == 1, "must upgrade from version 0 to 1."); + let on_chain_version = Pallet::::on_chain_storage_version(); + ensure!(on_chain_version == 1, "must upgrade from version 0 to 1."); let pre_referendum_count: u32 = Decode::decode(&mut &state[..]) .expect("failed to decode the state from pre-upgrade."); let post_referendum_count = ReferendumInfoFor::::iter().count() as u32; diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs index b75558723e9bd36a366c980565faa35ef62ca67e..bfafc107c28bc4ba422d6409b861f60a6c1d26d9 100644 --- a/substrate/frame/referenda/src/mock.rs +++ b/substrate/frame/referenda/src/mock.rs @@ -29,9 +29,8 @@ use frame_support::{ weights::Weight, }; use frame_system::{EnsureRoot, EnsureSignedBy}; -use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, Hash, IdentityLookup}, + traits::{BlakeTwo256, Hash}, BuildStorage, DispatchResult, Perbill, }; @@ -62,28 +61,8 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { type BaseCallFilter = BaseFilter; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_preimage::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -118,7 +97,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { pub static AlarmInterval: u64 = 1; diff --git a/substrate/frame/referenda/src/types.rs b/substrate/frame/referenda/src/types.rs index 8d6a13ef27c981b3996425b18062e2ef04a2f68b..b3c583322cce384bae7e2a9340071d5af6b2f455 100644 --- a/substrate/frame/referenda/src/types.rs +++ b/substrate/frame/referenda/src/types.rs @@ -19,6 +19,7 @@ use super::*; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use core::fmt::Debug; use frame_support::{ traits::{schedule::v3::Anon, Bounded}, Parameter, @@ -26,7 +27,6 @@ use frame_support::{ use scale_info::TypeInfo; use sp_arithmetic::{Rounding::*, SignedRounding::*}; use sp_runtime::{FixedI64, PerThing, RuntimeDebug}; -use sp_std::fmt::Debug; pub type BalanceOf = <>::Currency as Currency<::AccountId>>::Balance; @@ -144,7 +144,10 @@ pub trait TracksInfo { /// The origin type from which a track is implied. type RuntimeOrigin; - /// Return the array of known tracks and their information. + /// Sorted array of known tracks and their information. + /// + /// The array MUST be sorted by `Id`. Consumers of this trait are advised to assert + /// [`Self::check_integrity`] prior to any use. fn tracks() -> &'static [(Self::Id, TrackInfo)]; /// Determine the voting track for the given `origin`. @@ -152,7 +155,23 @@ pub trait TracksInfo { /// Return the track info for track `id`, by default this just looks it up in `Self::tracks()`. fn info(id: Self::Id) -> Option<&'static TrackInfo> { - Self::tracks().iter().find(|x| x.0 == id).map(|x| &x.1) + let tracks = Self::tracks(); + let maybe_index = tracks.binary_search_by_key(&id, |t| t.0).ok()?; + + tracks.get(maybe_index).map(|(_, info)| info) + } + + /// Check assumptions about the static data that this trait provides. + fn check_integrity() -> Result<(), &'static str> + where + Balance: 'static, + Moment: 'static, + { + if Self::tracks().windows(2).all(|w| w[0].0 < w[1].0) { + Ok(()) + } else { + Err("The tracks that were returned by `tracks` were not sorted by `Id`") + } } } @@ -670,4 +689,72 @@ mod tests { assert_eq!(c.delay(pc(30).less_epsilon()), pc(100)); assert_eq!(c.delay(pc(0)), pc(100)); } + + #[test] + fn tracks_integrity_check_detects_unsorted() { + use crate::mock::RuntimeOrigin; + + pub struct BadTracksInfo; + impl TracksInfo for BadTracksInfo { + type Id = u8; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, TrackInfo)] { + static DATA: [(u8, TrackInfo); 2] = [ + ( + 1u8, + TrackInfo { + name: "root", + max_deciding: 1, + decision_deposit: 10, + prepare_period: 4, + decision_period: 4, + confirm_period: 2, + min_enactment_period: 4, + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(50), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(0), + ceil: Perbill::from_percent(100), + }, + }, + ), + ( + 0u8, + TrackInfo { + name: "none", + max_deciding: 3, + decision_deposit: 1, + prepare_period: 2, + decision_period: 2, + confirm_period: 1, + min_enactment_period: 2, + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(95), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(90), + ceil: Perbill::from_percent(100), + }, + }, + ), + ]; + &DATA[..] + } + fn track_for(_: &Self::RuntimeOrigin) -> Result { + unimplemented!() + } + } + + assert_eq!( + BadTracksInfo::check_integrity(), + Err("The tracks that were returned by `tracks` were not sorted by `Id`") + ); + } } diff --git a/substrate/frame/referenda/src/weights.rs b/substrate/frame/referenda/src/weights.rs index 4b89379b311da6ac8cd10fd84a127e3693ab00de..1f1b69873ebc44fc045ac8f7864e8c2397e927cf 100644 --- a/substrate/frame/referenda/src/weights.rs +++ b/substrate/frame/referenda/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_referenda +//! Autogenerated weights for `pallet_referenda` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/referenda/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/referenda/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_referenda. +/// Weight functions needed for `pallet_referenda`. pub trait WeightInfo { fn submit() -> Weight; fn place_decision_deposit_preparing() -> Weight; @@ -84,842 +83,874 @@ pub trait WeightInfo { fn clear_metadata() -> Weight; } -/// Weights for pallet_referenda using the Substrate node and recommended hardware. +/// Weights for `pallet_referenda` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Referenda ReferendumCount (r:1 w:1) - /// Proof: Referenda ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:0 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumCount` (r:1 w:1) + /// Proof: `Referenda::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `220` + // Measured: `286` // Estimated: `110487` - // Minimum execution time: 40_175_000 picoseconds. - Weight::from_parts(41_107_000, 110487) + // Minimum execution time: 31_536_000 picoseconds. + Weight::from_parts(32_797_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `473` + // Measured: `539` // Estimated: `219984` - // Minimum execution time: 50_922_000 picoseconds. - Weight::from_parts(52_179_000, 219984) + // Minimum execution time: 42_579_000 picoseconds. + Weight::from_parts(43_877_000, 219984) .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3260` + // Measured: `3326` // Estimated: `110487` - // Minimum execution time: 69_559_000 picoseconds. - Weight::from_parts(72_143_000, 110487) + // Minimum execution time: 61_439_000 picoseconds. + Weight::from_parts(63_681_000, 110487) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3280` + // Measured: `3346` // Estimated: `110487` - // Minimum execution time: 68_833_000 picoseconds. - Weight::from_parts(70_987_000, 110487) + // Minimum execution time: 60_136_000 picoseconds. + Weight::from_parts(61_841_000, 110487) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `473` + // Measured: `539` // Estimated: `219984` - // Minimum execution time: 61_794_000 picoseconds. - Weight::from_parts(62_846_000, 219984) + // Minimum execution time: 49_865_000 picoseconds. + Weight::from_parts(51_347_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `473` + // Measured: `539` // Estimated: `219984` - // Minimum execution time: 58_664_000 picoseconds. - Weight::from_parts(60_195_000, 219984) + // Minimum execution time: 47_887_000 picoseconds. + Weight::from_parts(49_927_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `351` + // Measured: `417` // Estimated: `3831` - // Minimum execution time: 30_850_000 picoseconds. - Weight::from_parts(32_130_000, 3831) + // Minimum execution time: 25_721_000 picoseconds. + Weight::from_parts(26_922_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `341` + // Measured: `407` // Estimated: `3831` - // Minimum execution time: 30_747_000 picoseconds. - Weight::from_parts(32_196_000, 3831) + // Minimum execution time: 25_840_000 picoseconds. + Weight::from_parts(26_498_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `219984` - // Minimum execution time: 36_139_000 picoseconds. - Weight::from_parts(37_252_000, 219984) + // Minimum execution time: 30_530_000 picoseconds. + Weight::from_parts(31_547_000, 219984) .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Referenda MetadataOf (r:1 w:0) - /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:0) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `622` + // Measured: `688` // Estimated: `219984` - // Minimum execution time: 80_862_000 picoseconds. - Weight::from_parts(83_045_000, 219984) + // Minimum execution time: 64_011_000 picoseconds. + Weight::from_parts(65_240_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Referenda TrackQueue (r:1 w:0) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: `Referenda::TrackQueue` (r:1 w:0) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `240` // Estimated: `5477` - // Minimum execution time: 10_136_000 picoseconds. - Weight::from_parts(10_638_000, 5477) + // Minimum execution time: 9_568_000 picoseconds. + Weight::from_parts(10_004_000, 5477) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `3150` + // Measured: `3216` // Estimated: `110487` - // Minimum execution time: 52_022_000 picoseconds. - Weight::from_parts(53_910_000, 110487) + // Minimum execution time: 43_325_000 picoseconds. + Weight::from_parts(45_335_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `3150` + // Measured: `3216` // Estimated: `110487` - // Minimum execution time: 53_683_000 picoseconds. - Weight::from_parts(55_707_000, 110487) + // Minimum execution time: 44_287_000 picoseconds. + Weight::from_parts(45_164_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `3011` + // Measured: `3077` // Estimated: `5477` - // Minimum execution time: 24_043_000 picoseconds. - Weight::from_parts(24_512_000, 5477) + // Minimum execution time: 21_909_000 picoseconds. + Weight::from_parts(22_641_000, 5477) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `3011` + // Measured: `3077` // Estimated: `5477` - // Minimum execution time: 23_588_000 picoseconds. - Weight::from_parts(24_422_000, 5477) + // Minimum execution time: 21_626_000 picoseconds. + Weight::from_parts(22_338_000, 5477) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3015` + // Measured: `3081` // Estimated: `5477` - // Minimum execution time: 31_443_000 picoseconds. - Weight::from_parts(32_725_000, 5477) + // Minimum execution time: 29_245_000 picoseconds. + Weight::from_parts(30_147_000, 5477) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3035` + // Measured: `3101` // Estimated: `5477` - // Minimum execution time: 30_319_000 picoseconds. - Weight::from_parts(31_652_000, 5477) + // Minimum execution time: 28_512_000 picoseconds. + Weight::from_parts(29_781_000, 5477) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `333` + // Measured: `399` // Estimated: `110487` - // Minimum execution time: 23_062_000 picoseconds. - Weight::from_parts(23_614_000, 110487) + // Minimum execution time: 19_208_000 picoseconds. + Weight::from_parts(19_947_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `110487` - // Minimum execution time: 23_537_000 picoseconds. - Weight::from_parts(24_267_000, 110487) + // Minimum execution time: 19_708_000 picoseconds. + Weight::from_parts(20_783_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `278` + // Measured: `344` // Estimated: `3831` - // Minimum execution time: 16_388_000 picoseconds. - Weight::from_parts(16_676_000, 3831) + // Minimum execution time: 12_947_000 picoseconds. + Weight::from_parts(13_582_000, 3831) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `110487` - // Minimum execution time: 32_801_000 picoseconds. - Weight::from_parts(34_053_000, 110487) + // Minimum execution time: 26_699_000 picoseconds. + Weight::from_parts(28_048_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `110487` - // Minimum execution time: 35_704_000 picoseconds. - Weight::from_parts(36_451_000, 110487) + // Minimum execution time: 28_132_000 picoseconds. + Weight::from_parts(29_520_000, 110487) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `434` + // Measured: `500` // Estimated: `110487` - // Minimum execution time: 29_151_000 picoseconds. - Weight::from_parts(30_055_000, 110487) + // Minimum execution time: 23_212_000 picoseconds. + Weight::from_parts(24_200_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `483` // Estimated: `110487` - // Minimum execution time: 29_265_000 picoseconds. - Weight::from_parts(30_213_000, 110487) + // Minimum execution time: 22_807_000 picoseconds. + Weight::from_parts(23_858_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `434` + // Measured: `500` // Estimated: `110487` - // Minimum execution time: 27_760_000 picoseconds. - Weight::from_parts(28_381_000, 110487) + // Minimum execution time: 22_862_000 picoseconds. + Weight::from_parts(23_627_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `438` + // Measured: `504` // Estimated: `110487` - // Minimum execution time: 25_464_000 picoseconds. - Weight::from_parts(26_348_000, 110487) + // Minimum execution time: 21_030_000 picoseconds. + Weight::from_parts(21_710_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `438` + // Measured: `504` // Estimated: `219984` - // Minimum execution time: 42_629_000 picoseconds. - Weight::from_parts(43_732_000, 219984) + // Minimum execution time: 33_031_000 picoseconds. + Weight::from_parts(34_518_000, 219984) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `434` + // Measured: `500` // Estimated: `110487` - // Minimum execution time: 30_015_000 picoseconds. - Weight::from_parts(30_827_000, 110487) + // Minimum execution time: 23_198_000 picoseconds. + Weight::from_parts(24_238_000, 110487) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Referenda MetadataOf (r:0 w:1) - /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:0 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `422` + // Measured: `555` // Estimated: `3831` - // Minimum execution time: 19_901_000 picoseconds. - Weight::from_parts(20_681_000, 3831) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Minimum execution time: 19_502_000 picoseconds. + Weight::from_parts(20_105_000, 3831) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda MetadataOf (r:1 w:1) - /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `355` + // Measured: `421` // Estimated: `3831` - // Minimum execution time: 17_323_000 picoseconds. - Weight::from_parts(18_227_000, 3831) + // Minimum execution time: 15_417_000 picoseconds. + Weight::from_parts(15_916_000, 3831) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Referenda ReferendumCount (r:1 w:1) - /// Proof: Referenda ReferendumCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:0 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumCount` (r:1 w:1) + /// Proof: `Referenda::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `220` + // Measured: `286` // Estimated: `110487` - // Minimum execution time: 40_175_000 picoseconds. - Weight::from_parts(41_107_000, 110487) + // Minimum execution time: 31_536_000 picoseconds. + Weight::from_parts(32_797_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `473` + // Measured: `539` // Estimated: `219984` - // Minimum execution time: 50_922_000 picoseconds. - Weight::from_parts(52_179_000, 219984) + // Minimum execution time: 42_579_000 picoseconds. + Weight::from_parts(43_877_000, 219984) .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3260` + // Measured: `3326` // Estimated: `110487` - // Minimum execution time: 69_559_000 picoseconds. - Weight::from_parts(72_143_000, 110487) + // Minimum execution time: 61_439_000 picoseconds. + Weight::from_parts(63_681_000, 110487) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3280` + // Measured: `3346` // Estimated: `110487` - // Minimum execution time: 68_833_000 picoseconds. - Weight::from_parts(70_987_000, 110487) + // Minimum execution time: 60_136_000 picoseconds. + Weight::from_parts(61_841_000, 110487) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `473` + // Measured: `539` // Estimated: `219984` - // Minimum execution time: 61_794_000 picoseconds. - Weight::from_parts(62_846_000, 219984) + // Minimum execution time: 49_865_000 picoseconds. + Weight::from_parts(51_347_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `473` + // Measured: `539` // Estimated: `219984` - // Minimum execution time: 58_664_000 picoseconds. - Weight::from_parts(60_195_000, 219984) + // Minimum execution time: 47_887_000 picoseconds. + Weight::from_parts(49_927_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `351` + // Measured: `417` // Estimated: `3831` - // Minimum execution time: 30_850_000 picoseconds. - Weight::from_parts(32_130_000, 3831) + // Minimum execution time: 25_721_000 picoseconds. + Weight::from_parts(26_922_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `341` + // Measured: `407` // Estimated: `3831` - // Minimum execution time: 30_747_000 picoseconds. - Weight::from_parts(32_196_000, 3831) + // Minimum execution time: 25_840_000 picoseconds. + Weight::from_parts(26_498_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `219984` - // Minimum execution time: 36_139_000 picoseconds. - Weight::from_parts(37_252_000, 219984) + // Minimum execution time: 30_530_000 picoseconds. + Weight::from_parts(31_547_000, 219984) .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Referenda MetadataOf (r:1 w:0) - /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:0) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `622` + // Measured: `688` // Estimated: `219984` - // Minimum execution time: 80_862_000 picoseconds. - Weight::from_parts(83_045_000, 219984) + // Minimum execution time: 64_011_000 picoseconds. + Weight::from_parts(65_240_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Referenda TrackQueue (r:1 w:0) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) + /// Storage: `Referenda::TrackQueue` (r:1 w:0) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `240` // Estimated: `5477` - // Minimum execution time: 10_136_000 picoseconds. - Weight::from_parts(10_638_000, 5477) + // Minimum execution time: 9_568_000 picoseconds. + Weight::from_parts(10_004_000, 5477) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `3150` + // Measured: `3216` // Estimated: `110487` - // Minimum execution time: 52_022_000 picoseconds. - Weight::from_parts(53_910_000, 110487) + // Minimum execution time: 43_325_000 picoseconds. + Weight::from_parts(45_335_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `3150` + // Measured: `3216` // Estimated: `110487` - // Minimum execution time: 53_683_000 picoseconds. - Weight::from_parts(55_707_000, 110487) + // Minimum execution time: 44_287_000 picoseconds. + Weight::from_parts(45_164_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `3011` + // Measured: `3077` // Estimated: `5477` - // Minimum execution time: 24_043_000 picoseconds. - Weight::from_parts(24_512_000, 5477) + // Minimum execution time: 21_909_000 picoseconds. + Weight::from_parts(22_641_000, 5477) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `3011` + // Measured: `3077` // Estimated: `5477` - // Minimum execution time: 23_588_000 picoseconds. - Weight::from_parts(24_422_000, 5477) + // Minimum execution time: 21_626_000 picoseconds. + Weight::from_parts(22_338_000, 5477) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3015` + // Measured: `3081` // Estimated: `5477` - // Minimum execution time: 31_443_000 picoseconds. - Weight::from_parts(32_725_000, 5477) + // Minimum execution time: 29_245_000 picoseconds. + Weight::from_parts(30_147_000, 5477) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:0) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Referenda TrackQueue (r:1 w:1) - /// Proof: Referenda TrackQueue (max_values: None, max_size: Some(2012), added: 4487, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3035` + // Measured: `3101` // Estimated: `5477` - // Minimum execution time: 30_319_000 picoseconds. - Weight::from_parts(31_652_000, 5477) + // Minimum execution time: 28_512_000 picoseconds. + Weight::from_parts(29_781_000, 5477) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `333` + // Measured: `399` // Estimated: `110487` - // Minimum execution time: 23_062_000 picoseconds. - Weight::from_parts(23_614_000, 110487) + // Minimum execution time: 19_208_000 picoseconds. + Weight::from_parts(19_947_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `110487` - // Minimum execution time: 23_537_000 picoseconds. - Weight::from_parts(24_267_000, 110487) + // Minimum execution time: 19_708_000 picoseconds. + Weight::from_parts(20_783_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `278` + // Measured: `344` // Estimated: `3831` - // Minimum execution time: 16_388_000 picoseconds. - Weight::from_parts(16_676_000, 3831) + // Minimum execution time: 12_947_000 picoseconds. + Weight::from_parts(13_582_000, 3831) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `110487` - // Minimum execution time: 32_801_000 picoseconds. - Weight::from_parts(34_053_000, 110487) + // Minimum execution time: 26_699_000 picoseconds. + Weight::from_parts(28_048_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda DecidingCount (r:1 w:1) - /// Proof: Referenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `381` + // Measured: `447` // Estimated: `110487` - // Minimum execution time: 35_704_000 picoseconds. - Weight::from_parts(36_451_000, 110487) + // Minimum execution time: 28_132_000 picoseconds. + Weight::from_parts(29_520_000, 110487) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `434` + // Measured: `500` // Estimated: `110487` - // Minimum execution time: 29_151_000 picoseconds. - Weight::from_parts(30_055_000, 110487) + // Minimum execution time: 23_212_000 picoseconds. + Weight::from_parts(24_200_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `483` // Estimated: `110487` - // Minimum execution time: 29_265_000 picoseconds. - Weight::from_parts(30_213_000, 110487) + // Minimum execution time: 22_807_000 picoseconds. + Weight::from_parts(23_858_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `434` + // Measured: `500` // Estimated: `110487` - // Minimum execution time: 27_760_000 picoseconds. - Weight::from_parts(28_381_000, 110487) + // Minimum execution time: 22_862_000 picoseconds. + Weight::from_parts(23_627_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `438` + // Measured: `504` // Estimated: `110487` - // Minimum execution time: 25_464_000 picoseconds. - Weight::from_parts(26_348_000, 110487) + // Minimum execution time: 21_030_000 picoseconds. + Weight::from_parts(21_710_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `438` + // Measured: `504` // Estimated: `219984` - // Minimum execution time: 42_629_000 picoseconds. - Weight::from_parts(43_732_000, 219984) + // Minimum execution time: 33_031_000 picoseconds. + Weight::from_parts(34_518_000, 219984) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `434` + // Measured: `500` // Estimated: `110487` - // Minimum execution time: 30_015_000 picoseconds. - Weight::from_parts(30_827_000, 110487) + // Minimum execution time: 23_198_000 picoseconds. + Weight::from_parts(24_238_000, 110487) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:0) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Referenda MetadataOf (r:0 w:1) - /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:0 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `422` + // Measured: `555` // Estimated: `3831` - // Minimum execution time: 19_901_000 picoseconds. - Weight::from_parts(20_681_000, 3831) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Minimum execution time: 19_502_000 picoseconds. + Weight::from_parts(20_105_000, 3831) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) - /// Storage: Referenda MetadataOf (r:1 w:1) - /// Proof: Referenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(366), added: 2841, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `355` + // Measured: `421` // Estimated: `3831` - // Minimum execution time: 17_323_000 picoseconds. - Weight::from_parts(18_227_000, 3831) + // Minimum execution time: 15_417_000 picoseconds. + Weight::from_parts(15_916_000, 3831) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/remark/Cargo.toml b/substrate/frame/remark/Cargo.toml index 1570d2dba27d821ef69edb096c40b00d066ab0d2..45710f0539e255b48caf4e7dce9a99f46600d4e5 100644 --- a/substrate/frame/remark/Cargo.toml +++ b/substrate/frame/remark/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-remark" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/remark/src/mock.rs b/substrate/frame/remark/src/mock.rs index 5a5e6a3ccdff28ff29799ed7c31cf2c1c73c77bb..d89583513b60d16a79aa076fb963fdc0e1459b76 100644 --- a/substrate/frame/remark/src/mock.rs +++ b/substrate/frame/remark/src/mock.rs @@ -18,15 +18,8 @@ //! Test environment for remarks pallet. use crate as pallet_remark; -use frame_support::{ - derive_impl, - traits::{ConstU16, ConstU32, ConstU64}, -}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use frame_support::derive_impl; +use sp_runtime::BuildStorage; pub type Block = frame_system::mocking::MockBlock; @@ -41,29 +34,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = ConstU16<42>; - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_remark::Config for Test { diff --git a/substrate/frame/remark/src/weights.rs b/substrate/frame/remark/src/weights.rs index 46475db163ffd5827486f8ad562c2f3ecc6deb3d..61cca5b7d05643c200b8d9c79ccc44bde9901bf2 100644 --- a/substrate/frame/remark/src/weights.rs +++ b/substrate/frame/remark/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_remark +//! Autogenerated weights for `pallet_remark` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/remark/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/remark/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,12 +49,12 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_remark. +/// Weight functions needed for `pallet_remark`. pub trait WeightInfo { fn store(l: u32, ) -> Weight; } -/// Weights for pallet_remark using the Substrate node and recommended hardware. +/// Weights for `pallet_remark` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `l` is `[1, 1048576]`. @@ -63,23 +62,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_471_000 picoseconds. - Weight::from_parts(8_586_000, 0) + // Minimum execution time: 6_623_000 picoseconds. + Weight::from_parts(6_757_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_359, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_368, 0).saturating_mul(l.into())) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { /// The range of component `l` is `[1, 1048576]`. fn store(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_471_000 picoseconds. - Weight::from_parts(8_586_000, 0) + // Minimum execution time: 6_623_000 picoseconds. + Weight::from_parts(6_757_000, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_359, 0).saturating_mul(l.into())) + .saturating_add(Weight::from_parts(1_368, 0).saturating_mul(l.into())) } } diff --git a/substrate/frame/root-offences/Cargo.toml b/substrate/frame/root-offences/Cargo.toml index 0f3d3a2883d5a5d92686e0f7fda014d146e4a420..80b3309506815223e77ab4658dcddaf1cef859f6 100644 --- a/substrate/frame/root-offences/Cargo.toml +++ b/substrate/frame/root-offences/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-root-offences" -version = "1.0.0-dev" +version = "25.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/root-offences/src/mock.rs b/substrate/frame/root-offences/src/mock.rs index 583ba5b92fb0f46895b8e55df4fb0c6d7484087a..1f7cce27769f17b9965774300070d62e78bcaabd 100644 --- a/substrate/frame/root-offences/src/mock.rs +++ b/substrate/frame/root-offences/src/mock.rs @@ -27,13 +27,7 @@ use frame_support::{ traits::{ConstU32, ConstU64, Hooks, OneSessionHandler}, }; use pallet_staking::StakerStatus; -use sp_core::H256; -use sp_runtime::{ - curve::PiecewiseLinear, - testing::UintAuthorityId, - traits::{BlakeTwo256, IdentityLookup, Zero}, - BuildStorage, -}; +use sp_runtime::{curve::PiecewiseLinear, testing::UintAuthorityId, traits::Zero, BuildStorage}; use sp_staking::{EraIndex, SessionIndex}; use sp_std::collections::btree_map::BTreeMap; @@ -86,29 +80,8 @@ impl sp_runtime::BoundToRuntimeAppPublic for OtherSessionHandler { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -125,7 +98,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } pallet_staking_reward_curve::build! { diff --git a/substrate/frame/root-testing/Cargo.toml b/substrate/frame/root-testing/Cargo.toml index 78aed99a56d712f65d458bd3956070b96c8e6f08..51b72665b81a5edef0ec0709c26450de5a59eaeb 100644 --- a/substrate/frame/root-testing/Cargo.toml +++ b/substrate/frame/root-testing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-root-testing" -version = "1.0.0-dev" +version = "4.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/safe-mode/Cargo.toml b/substrate/frame/safe-mode/Cargo.toml index f86332483c4a740265a7f660b739700090fa567b..0c59740bef30a8169490ddd27be7898b975c82ff 100644 --- a/substrate/frame/safe-mode/Cargo.toml +++ b/substrate/frame/safe-mode/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-safe-mode" -version = "4.0.0-dev" +version = "9.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -docify = "0.2.6" +docify = "0.2.7" frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/safe-mode/src/benchmarking.rs b/substrate/frame/safe-mode/src/benchmarking.rs index 566150e982afca39748f987e135222b43bc7b1a1..7561cf305f454465a6805c04a75930dc4794a7cd 100644 --- a/substrate/frame/safe-mode/src/benchmarking.rs +++ b/substrate/frame/safe-mode/src/benchmarking.rs @@ -20,11 +20,11 @@ use super::{Pallet as SafeMode, *}; use frame_benchmarking::v2::*; -use frame_support::traits::{fungible::Mutate as FunMutate, UnfilteredDispatchable}; +use frame_support::traits::{fungible, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, One, Zero}; -#[benchmarks(where T::Currency: FunMutate)] +#[benchmarks(where T::Currency: fungible::Mutate)] mod benchmarks { use super::*; @@ -58,7 +58,7 @@ mod benchmarks { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, init_bal::()); + >::set_balance(&caller, init_bal::()); #[extrinsic_call] _(origin); @@ -91,7 +91,7 @@ mod benchmarks { T::ExtendDepositAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); - T::Currency::set_balance(&alice, init_bal::()); + >::set_balance(&alice, init_bal::()); System::::set_block_number(1u32.into()); assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); @@ -152,7 +152,7 @@ mod benchmarks { let alice: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(alice.clone()); - T::Currency::set_balance(&alice, init_bal::()); + >::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: BlockNumberFor = 1u32.into(); let bal: BalanceOf = 1u32.into(); @@ -169,7 +169,7 @@ mod benchmarks { _(origin, alice.clone(), 1u32.into()); assert!(!Deposits::::contains_key(&alice, &block)); - assert_eq!(T::Currency::balance(&alice), init_bal::()); + assert_eq!(>::balance(&alice), init_bal::()); Ok(()) } @@ -180,7 +180,7 @@ mod benchmarks { .map_err(|_| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); - T::Currency::set_balance(&alice, init_bal::()); + >::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: BlockNumberFor = 1u32.into(); @@ -189,7 +189,10 @@ mod benchmarks { T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); - assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); + assert_eq!( + >::balance(&alice), + init_bal::() - 1u32.into() + ); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); System::::set_block_number(System::::block_number() + One::one()); @@ -200,7 +203,7 @@ mod benchmarks { _(force_origin as T::RuntimeOrigin, alice.clone(), block); assert!(!Deposits::::contains_key(&alice, block)); - assert_eq!(T::Currency::balance(&alice), init_bal::()); + assert_eq!(>::balance(&alice), init_bal::()); Ok(()) } @@ -210,7 +213,7 @@ mod benchmarks { .map_err(|_| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); - T::Currency::set_balance(&alice, init_bal::()); + >::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: BlockNumberFor = 1u32.into(); @@ -224,7 +227,10 @@ mod benchmarks { _(force_origin as T::RuntimeOrigin, alice.clone(), block); assert!(!Deposits::::contains_key(&alice, block)); - assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); + assert_eq!( + >::balance(&alice), + init_bal::() - 1u32.into() + ); Ok(()) } diff --git a/substrate/frame/safe-mode/src/lib.rs b/substrate/frame/safe-mode/src/lib.rs index 554f509db63ea9c9bdfb6a9328dd3eaaecaa617a..2bf2ebee0a4ac88aebe6d2ccc498ac8b5d958352 100644 --- a/substrate/frame/safe-mode/src/lib.rs +++ b/substrate/frame/safe-mode/src/lib.rs @@ -79,13 +79,14 @@ pub mod mock; mod tests; pub mod weights; +use core::convert::TryInto; use frame_support::{ defensive_assert, pallet_prelude::*, traits::{ fungible::{ - hold::{Inspect as FunHoldInspect, Mutate as FunHoldMutate}, - Inspect as FunInspect, + self, + hold::{Inspect, Mutate}, }, tokens::{Fortitude, Precision}, CallMetadata, Contains, Defensive, GetCallMetadata, PalletInfoAccess, SafeModeNotify, @@ -96,13 +97,12 @@ use frame_support::{ use frame_system::pallet_prelude::*; use sp_arithmetic::traits::Zero; use sp_runtime::traits::Saturating; -use sp_std::{convert::TryInto, prelude::*}; pub use pallet::*; pub use weights::*; type BalanceOf = - <::Currency as FunInspect<::AccountId>>::Balance; + <::Currency as fungible::Inspect<::AccountId>>::Balance; #[frame_support::pallet] pub mod pallet { @@ -117,8 +117,8 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Currency type for this pallet, used for Deposits. - type Currency: FunHoldInspect - + FunHoldMutate; + type Currency: Inspect + + Mutate; /// The hold reason when reserving funds for entering or extending the safe-mode. type RuntimeHoldReason: From; diff --git a/substrate/frame/safe-mode/src/mock.rs b/substrate/frame/safe-mode/src/mock.rs index 7574d64d59ddba40cee17993ad9edb00f66d08d0..b4d7a624ea21751fe0dc88d7059ccecc130036b4 100644 --- a/substrate/frame/safe-mode/src/mock.rs +++ b/substrate/frame/safe-mode/src/mock.rs @@ -82,7 +82,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<10>; type MaxFreezes = ConstU32<0>; } diff --git a/substrate/frame/safe-mode/src/weights.rs b/substrate/frame/safe-mode/src/weights.rs index f72bebcab9a4db74fba3ef282692324e32858fc3..d326fab0f0f41d3d383a613cbfe9c6dab2ebd981 100644 --- a/substrate/frame/safe-mode/src/weights.rs +++ b/substrate/frame/safe-mode/src/weights.rs @@ -17,27 +17,29 @@ //! Autogenerated weights for `pallet_safe_mode` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-aahe6cbd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_safe_mode +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_safe_mode -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/safe-mode/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/safe-mode/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -70,8 +72,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1489` - // Minimum execution time: 2_500_000 picoseconds. - Weight::from_parts(2_594_000, 1489) + // Minimum execution time: 2_216_000 picoseconds. + Weight::from_parts(2_309_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) @@ -80,23 +82,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `169` // Estimated: `1489` - // Minimum execution time: 8_868_000 picoseconds. - Weight::from_parts(9_415_000, 1489) + // Minimum execution time: 6_124_000 picoseconds. + Weight::from_parts(6_601_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `SafeMode::Deposits` (r:0 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn enter() -> Weight { // Proof Size summary in bytes: // Measured: `142` - // Estimated: `3550` - // Minimum execution time: 50_541_000 picoseconds. - Weight::from_parts(51_558_000, 3550) + // Estimated: `3658` + // Minimum execution time: 44_825_000 picoseconds. + Weight::from_parts(45_845_000, 3658) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -106,23 +108,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1489` - // Minimum execution time: 10_489_000 picoseconds. - Weight::from_parts(10_833_000, 1489) + // Minimum execution time: 7_603_000 picoseconds. + Weight::from_parts(7_954_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `SafeMode::Deposits` (r:0 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn extend() -> Weight { // Proof Size summary in bytes: // Measured: `169` - // Estimated: `3550` - // Minimum execution time: 50_818_000 picoseconds. - Weight::from_parts(51_873_000, 3550) + // Estimated: `3658` + // Minimum execution time: 44_716_000 picoseconds. + Weight::from_parts(46_039_000, 3658) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -132,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `169` // Estimated: `1489` - // Minimum execution time: 10_843_000 picoseconds. - Weight::from_parts(11_314_000, 1489) + // Minimum execution time: 8_231_000 picoseconds. + Weight::from_parts(8_731_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -143,8 +145,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `169` // Estimated: `1489` - // Minimum execution time: 10_382_000 picoseconds. - Weight::from_parts(10_814_000, 1489) + // Minimum execution time: 8_015_000 picoseconds. + Weight::from_parts(8_247_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -153,39 +155,39 @@ impl WeightInfo for SubstrateWeight { /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `292` - // Estimated: `3550` - // Minimum execution time: 42_828_000 picoseconds. - Weight::from_parts(43_752_000, 3550) + // Estimated: `3658` + // Minimum execution time: 39_149_000 picoseconds. + Weight::from_parts(40_005_000, 3658) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SafeMode::Deposits` (r:1 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn force_release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `292` - // Estimated: `3550` - // Minimum execution time: 40_196_000 picoseconds. - Weight::from_parts(41_298_000, 3550) + // Estimated: `3658` + // Minimum execution time: 37_528_000 picoseconds. + Weight::from_parts(38_473_000, 3658) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SafeMode::Deposits` (r:1 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn force_slash_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `292` - // Estimated: `3550` - // Minimum execution time: 33_660_000 picoseconds. - Weight::from_parts(34_426_000, 3550) + // Estimated: `3658` + // Minimum execution time: 29_417_000 picoseconds. + Weight::from_parts(30_195_000, 3658) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -199,8 +201,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1489` - // Minimum execution time: 2_500_000 picoseconds. - Weight::from_parts(2_594_000, 1489) + // Minimum execution time: 2_216_000 picoseconds. + Weight::from_parts(2_309_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) @@ -209,23 +211,23 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `169` // Estimated: `1489` - // Minimum execution time: 8_868_000 picoseconds. - Weight::from_parts(9_415_000, 1489) + // Minimum execution time: 6_124_000 picoseconds. + Weight::from_parts(6_601_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `SafeMode::Deposits` (r:0 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn enter() -> Weight { // Proof Size summary in bytes: // Measured: `142` - // Estimated: `3550` - // Minimum execution time: 50_541_000 picoseconds. - Weight::from_parts(51_558_000, 3550) + // Estimated: `3658` + // Minimum execution time: 44_825_000 picoseconds. + Weight::from_parts(45_845_000, 3658) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -235,23 +237,23 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `142` // Estimated: `1489` - // Minimum execution time: 10_489_000 picoseconds. - Weight::from_parts(10_833_000, 1489) + // Minimum execution time: 7_603_000 picoseconds. + Weight::from_parts(7_954_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) /// Storage: `SafeMode::Deposits` (r:0 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn extend() -> Weight { // Proof Size summary in bytes: // Measured: `169` - // Estimated: `3550` - // Minimum execution time: 50_818_000 picoseconds. - Weight::from_parts(51_873_000, 3550) + // Estimated: `3658` + // Minimum execution time: 44_716_000 picoseconds. + Weight::from_parts(46_039_000, 3658) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -261,8 +263,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `169` // Estimated: `1489` - // Minimum execution time: 10_843_000 picoseconds. - Weight::from_parts(11_314_000, 1489) + // Minimum execution time: 8_231_000 picoseconds. + Weight::from_parts(8_731_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -272,8 +274,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `169` // Estimated: `1489` - // Minimum execution time: 10_382_000 picoseconds. - Weight::from_parts(10_814_000, 1489) + // Minimum execution time: 8_015_000 picoseconds. + Weight::from_parts(8_247_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -282,39 +284,39 @@ impl WeightInfo for () { /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `292` - // Estimated: `3550` - // Minimum execution time: 42_828_000 picoseconds. - Weight::from_parts(43_752_000, 3550) + // Estimated: `3658` + // Minimum execution time: 39_149_000 picoseconds. + Weight::from_parts(40_005_000, 3658) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SafeMode::Deposits` (r:1 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn force_release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `292` - // Estimated: `3550` - // Minimum execution time: 40_196_000 picoseconds. - Weight::from_parts(41_298_000, 3550) + // Estimated: `3658` + // Minimum execution time: 37_528_000 picoseconds. + Weight::from_parts(38_473_000, 3658) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SafeMode::Deposits` (r:1 w:1) /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn force_slash_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `292` - // Estimated: `3550` - // Minimum execution time: 33_660_000 picoseconds. - Weight::from_parts(34_426_000, 3550) + // Estimated: `3658` + // Minimum execution time: 29_417_000 picoseconds. + Weight::from_parts(30_195_000, 3658) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/substrate/frame/salary/Cargo.toml b/substrate/frame/salary/Cargo.toml index 929151a9c2082c60419d55c47c892b15e26acf15..ba57fd46eebb44f0164ffd8aa2a5edf936ab3835 100644 --- a/substrate/frame/salary/Cargo.toml +++ b/substrate/frame/salary/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-salary" -version = "4.0.0-dev" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.16", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } @@ -27,15 +27,18 @@ sp-core = { path = "../../primitives/core", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } +pallet-ranked-collective = { path = "../ranked-collective", default-features = false, optional = true } [features] default = ["std"] std = [ "codec/std", "frame-benchmarking?/std", + "frame-support/experimental", "frame-support/std", "frame-system/std", "log/std", + "pallet-ranked-collective/std", "scale-info/std", "sp-arithmetic/std", "sp-core/std", @@ -47,10 +50,12 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-ranked-collective/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "pallet-ranked-collective?/try-runtime", "sp-runtime/try-runtime", ] diff --git a/substrate/frame/salary/src/benchmarking.rs b/substrate/frame/salary/src/benchmarking.rs index 7528293506aece90da5592d99208b36c86aceb5b..aeae8d2d67f88ba8509f0aed7031b7983233a1d3 100644 --- a/substrate/frame/salary/src/benchmarking.rs +++ b/substrate/frame/salary/src/benchmarking.rs @@ -177,7 +177,7 @@ mod benchmarks { impl_benchmark_test_suite! { Salary, - crate::tests::new_test_ext(), - crate::tests::Test, + crate::tests::unit::new_test_ext(), + crate::tests::unit::Test, } } diff --git a/substrate/frame/salary/src/lib.rs b/substrate/frame/salary/src/lib.rs index f2903f527f25a5c21348e8214d5a2afe5bc4f14b..efb4f5d3c5422d96b2b2038122de8c2d391cc855 100644 --- a/substrate/frame/salary/src/lib.rs +++ b/substrate/frame/salary/src/lib.rs @@ -18,20 +18,20 @@ //! Make periodic payment to members of a ranked collective according to rank. #![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit = "128"] use codec::{Decode, Encode, MaxEncodedLen}; +use core::marker::PhantomData; use scale_info::TypeInfo; use sp_arithmetic::traits::{Saturating, Zero}; use sp_runtime::{Perbill, RuntimeDebug}; -use sp_std::{marker::PhantomData, prelude::*}; use frame_support::{ + defensive, dispatch::DispatchResultWithPostInfo, ensure, traits::{ tokens::{GetSalary, Pay, PaymentStatus}, - RankedMembers, + RankedMembers, RankedMembersSwapHandler, }, }; @@ -173,6 +173,8 @@ pub mod pallet { }, /// The next cycle begins. CycleStarted { index: CycleIndexOf }, + /// A member swapped their account. + Swapped { who: T::AccountId, new_who: T::AccountId }, } #[pallet::error] @@ -447,3 +449,40 @@ pub mod pallet { } } } + +impl, I: 'static> + RankedMembersSwapHandler::Rank> for Pallet +{ + fn swapped( + who: &T::AccountId, + new_who: &T::AccountId, + _rank: ::Rank, + ) { + if who == new_who { + defensive!("Should not try to swap with self"); + return + } + if Claimant::::contains_key(new_who) { + defensive!("Should not try to overwrite existing claimant"); + return + } + + let Some(claimant) = Claimant::::take(who) else { + frame_support::defensive!("Claimant should exist when swapping"); + return; + }; + + Claimant::::insert(new_who, claimant); + Self::deposit_event(Event::::Swapped { who: who.clone(), new_who: new_who.clone() }); + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl, I: 'static> + pallet_ranked_collective::BenchmarkSetup<::AccountId> for Pallet +{ + fn ensure_member(who: &::AccountId) { + Self::init(frame_system::RawOrigin::Signed(who.clone()).into()).unwrap(); + Self::induct(frame_system::RawOrigin::Signed(who.clone()).into()).unwrap(); + } +} diff --git a/substrate/frame/salary/src/tests/integration.rs b/substrate/frame/salary/src/tests/integration.rs new file mode 100644 index 0000000000000000000000000000000000000000..a49b5637b8ae42b6618fe98b12ceba77f35fe89b --- /dev/null +++ b/substrate/frame/salary/src/tests/integration.rs @@ -0,0 +1,280 @@ +// 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. + +//! The crate's tests. + +use frame_support::{ + assert_noop, assert_ok, derive_impl, hypothetically, + pallet_prelude::Weight, + parameter_types, + traits::{ConstU64, EitherOf, MapSuccess, PollStatus, Polling}, +}; +use pallet_ranked_collective::{EnsureRanked, Geometric, TallyOf, Votes}; +use sp_core::{ConstU16, Get}; +use sp_runtime::{ + traits::{Convert, ReduceBy, ReplaceWithDefault}, + BuildStorage, DispatchError, +}; + +use crate as pallet_salary; +use crate::*; + +type Rank = u16; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Salary: pallet_salary, + Club: pallet_ranked_collective, + } +); + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1_000_000, 0)); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; +} + +pub struct TestPolls; +impl Polling> for TestPolls { + type Index = u8; + type Votes = Votes; + type Moment = u64; + type Class = Rank; + + fn classes() -> Vec { + unimplemented!() + } + fn as_ongoing(_index: u8) -> Option<(TallyOf, Self::Class)> { + unimplemented!() + } + fn access_poll( + _index: Self::Index, + _f: impl FnOnce(PollStatus<&mut TallyOf, Self::Moment, Self::Class>) -> R, + ) -> R { + unimplemented!() + } + fn try_access_poll( + _index: Self::Index, + _f: impl FnOnce( + PollStatus<&mut TallyOf, Self::Moment, Self::Class>, + ) -> Result, + ) -> Result { + unimplemented!() + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_ongoing(_class: Self::Class) -> Result { + unimplemented!() + } + + #[cfg(feature = "runtime-benchmarks")] + fn end_ongoing(_index: Self::Index, _approved: bool) -> Result<(), ()> { + unimplemented!() + } +} + +pub struct MinRankOfClass(PhantomData); +impl> Convert for MinRankOfClass { + fn convert(a: u16) -> Rank { + a.saturating_sub(Delta::get()) + } +} + +pub struct TestPay; +impl Pay for TestPay { + type Beneficiary = u64; + type Balance = u64; + type Id = u64; + type AssetKind = (); + type Error = (); + + fn pay( + _: &Self::Beneficiary, + _: Self::AssetKind, + _: Self::Balance, + ) -> Result { + unreachable!() + } + fn check_payment(_: Self::Id) -> PaymentStatus { + unreachable!() + } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(_: &Self::Beneficiary, _: Self::AssetKind, _: Self::Balance) {} + #[cfg(feature = "runtime-benchmarks")] + fn ensure_concluded(_: Self::Id) { + unreachable!() + } +} + +parameter_types! { + pub static Budget: u64 = 10; +} + +impl Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Paymaster = TestPay; + type Members = Club; + type Salary = FixedSalary; + type RegistrationPeriod = ConstU64<2>; + type PayoutPeriod = ConstU64<2>; + type Budget = Budget; +} + +pub struct FixedSalary; +impl GetSalary for FixedSalary { + fn get_salary(_rank: u16, _who: &u64) -> u64 { + 123 + } +} + +parameter_types! { + pub static MinRankOfClassDelta: Rank = 0; +} + +impl pallet_ranked_collective::Config for Test { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type PromoteOrigin = EitherOf< + // Root can promote arbitrarily. + frame_system::EnsureRootWithSuccess>, + // Members can promote up to the rank of 2 below them. + MapSuccess, ReduceBy>>, + >; + type AddOrigin = MapSuccess>; + type DemoteOrigin = EitherOf< + // Root can demote arbitrarily. + frame_system::EnsureRootWithSuccess>, + // Members can demote up to the rank of 3 below them. + MapSuccess, ReduceBy>>, + >; + type RemoveOrigin = Self::DemoteOrigin; + type ExchangeOrigin = EitherOf< + // Root can exchange arbitrarily. + frame_system::EnsureRootWithSuccess>, + // Members can exchange up to the rank of 2 below them. + MapSuccess, ReduceBy>>, + >; + type Polls = TestPolls; + type MinRankOfClass = MinRankOfClass; + type MemberSwappedHandler = Salary; + type VoteWeight = Geometric; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkSetup = Salary; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + let frame_system::EventRecord { event, .. } = events.last().expect("Event expected"); + assert_eq!(event, &system_event.into()); +} + +fn promote_n_times(acc: u64, r: u16) { + for _ in 0..r { + assert_ok!(Club::promote_member(RuntimeOrigin::root(), acc)); + } +} + +#[test] +fn swap_simple_works() { + new_test_ext().execute_with(|| { + for i in 0u16..9 { + let acc = i as u64; + + assert_ok!(Club::add_member(RuntimeOrigin::root(), acc)); + promote_n_times(acc, i); + let _ = Salary::init(RuntimeOrigin::signed(acc)); + assert_ok!(Salary::induct(RuntimeOrigin::signed(acc))); + + // Swapping normally works: + assert_ok!(Club::exchange_member(RuntimeOrigin::root(), acc, acc + 10)); + assert_last_event(Event::Swapped { who: acc, new_who: acc + 10 }.into()); + } + }); +} + +#[test] +fn swap_exhaustive_works() { + new_test_ext().execute_with(|| { + let root_add = hypothetically!({ + assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1)); + assert_ok!(Salary::init(RuntimeOrigin::signed(1))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + + // The events mess up the storage root: + System::reset_events(); + sp_io::storage::root(sp_runtime::StateVersion::V1) + }); + + let root_swap = hypothetically!({ + assert_ok!(Club::add_member(RuntimeOrigin::root(), 0)); + assert_ok!(Club::promote_member(RuntimeOrigin::root(), 0)); + assert_ok!(Salary::init(RuntimeOrigin::signed(0))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(0))); + + assert_ok!(Club::exchange_member(RuntimeOrigin::root(), 0, 1)); + + // The events mess up the storage root: + System::reset_events(); + sp_io::storage::root(sp_runtime::StateVersion::V1) + }); + + assert_eq!(root_add, root_swap); + // Ensure that we dont compare trivial stuff like `()` from a type error above. + assert_eq!(root_add.len(), 32); + }); +} + +#[test] +fn swap_bad_noops() { + new_test_ext().execute_with(|| { + assert_ok!(Club::add_member(RuntimeOrigin::root(), 0)); + promote_n_times(0, 0); + assert_ok!(Salary::init(RuntimeOrigin::signed(0))); + assert_ok!(Salary::induct(RuntimeOrigin::signed(0))); + assert_ok!(Club::add_member(RuntimeOrigin::root(), 1)); + promote_n_times(1, 1); + assert_ok!(Salary::induct(RuntimeOrigin::signed(1))); + + // Swapping for another member is a noop: + assert_noop!( + Club::exchange_member(RuntimeOrigin::root(), 0, 1), + pallet_ranked_collective::Error::::AlreadyMember + ); + // Swapping for the same member is a noop: + assert_noop!( + Club::exchange_member(RuntimeOrigin::root(), 0, 0), + pallet_ranked_collective::Error::::SameMember + ); + }); +} diff --git a/substrate/frame/salary/src/tests/mod.rs b/substrate/frame/salary/src/tests/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..cd29d8fabeed734925a847c836a9d6aca1bd1d98 --- /dev/null +++ b/substrate/frame/salary/src/tests/mod.rs @@ -0,0 +1,23 @@ +// 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(test)] + +//! Unit and integration tests for the salary pallet. + +pub(crate) mod integration; +pub(crate) mod unit; diff --git a/substrate/frame/salary/src/tests.rs b/substrate/frame/salary/src/tests/unit.rs similarity index 95% rename from substrate/frame/salary/src/tests.rs rename to substrate/frame/salary/src/tests/unit.rs index f25acd934528445226271824faf754541f86503c..b3fd00ec76b9b018a8949e5dc6a36385d99ad8d5 100644 --- a/substrate/frame/salary/src/tests.rs +++ b/substrate/frame/salary/src/tests/unit.rs @@ -19,21 +19,17 @@ use std::collections::BTreeMap; +use core::cell::RefCell; use frame_support::{ assert_noop, assert_ok, derive_impl, pallet_prelude::Weight, parameter_types, - traits::{tokens::ConvertRank, ConstU32, ConstU64, Everything}, + traits::{tokens::ConvertRank, ConstU64}, }; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, Identity, IdentityLookup}, - BuildStorage, DispatchResult, -}; -use sp_std::cell::RefCell; +use sp_runtime::{traits::Identity, BuildStorage, DispatchResult}; -use super::*; use crate as pallet_salary; +use crate::*; type Block = frame_system::mocking::MockBlock; @@ -52,29 +48,7 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } thread_local! { diff --git a/substrate/frame/salary/src/weights.rs b/substrate/frame/salary/src/weights.rs index 3d3b9e315959be023cfe09ae00e993cabedea99a..c4f22a027eb42f011c98900571fc9dae2cb5d503 100644 --- a/substrate/frame/salary/src/weights.rs +++ b/substrate/frame/salary/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_salary +//! Autogenerated weights for `pallet_salary` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/salary/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/salary/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_salary. +/// Weight functions needed for `pallet_salary`. pub trait WeightInfo { fn init() -> Weight; fn bump() -> Weight; @@ -61,204 +60,204 @@ pub trait WeightInfo { fn check_payment() -> Weight; } -/// Weights for pallet_salary using the Substrate node and recommended hardware. +/// Weights for `pallet_salary` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) fn init() -> Weight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1541` - // Minimum execution time: 10_778_000 picoseconds. - Weight::from_parts(11_084_000, 1541) + // Minimum execution time: 7_421_000 picoseconds. + Weight::from_parts(7_819_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) fn bump() -> Weight { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1541` - // Minimum execution time: 12_042_000 picoseconds. - Weight::from_parts(12_645_000, 1541) + // Minimum execution time: 8_651_000 picoseconds. + Weight::from_parts(9_056_000, 1541) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Salary Status (r:1 w:0) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:0) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3543` - // Minimum execution time: 18_374_000 picoseconds. - Weight::from_parts(19_200_000, 3543) + // Minimum execution time: 16_513_000 picoseconds. + Weight::from_parts(17_305_000, 3543) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: // Measured: `462` // Estimated: `3543` - // Minimum execution time: 22_696_000 picoseconds. - Weight::from_parts(23_275_000, 3543) + // Minimum execution time: 18_913_000 picoseconds. + Weight::from_parts(19_867_000, 3543) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) fn payout() -> Weight { // Proof Size summary in bytes: // Measured: `462` // Estimated: `3543` - // Minimum execution time: 63_660_000 picoseconds. - Weight::from_parts(65_006_000, 3543) + // Minimum execution time: 53_297_000 picoseconds. + Weight::from_parts(55_144_000, 3543) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn payout_other() -> Weight { // Proof Size summary in bytes: // Measured: `462` // Estimated: `3593` - // Minimum execution time: 64_706_000 picoseconds. - Weight::from_parts(65_763_000, 3593) + // Minimum execution time: 53_638_000 picoseconds. + Weight::from_parts(55_328_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) fn check_payment() -> Weight { // Proof Size summary in bytes: // Measured: `170` // Estimated: `3543` - // Minimum execution time: 11_838_000 picoseconds. - Weight::from_parts(12_323_000, 3543) + // Minimum execution time: 10_557_000 picoseconds. + Weight::from_parts(11_145_000, 3543) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) fn init() -> Weight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `1541` - // Minimum execution time: 10_778_000 picoseconds. - Weight::from_parts(11_084_000, 1541) + // Minimum execution time: 7_421_000 picoseconds. + Weight::from_parts(7_819_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) fn bump() -> Weight { // Proof Size summary in bytes: // Measured: `86` // Estimated: `1541` - // Minimum execution time: 12_042_000 picoseconds. - Weight::from_parts(12_645_000, 1541) + // Minimum execution time: 8_651_000 picoseconds. + Weight::from_parts(9_056_000, 1541) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Salary Status (r:1 w:0) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:0) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) fn induct() -> Weight { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3543` - // Minimum execution time: 18_374_000 picoseconds. - Weight::from_parts(19_200_000, 3543) + // Minimum execution time: 16_513_000 picoseconds. + Weight::from_parts(17_305_000, 3543) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: // Measured: `462` // Estimated: `3543` - // Minimum execution time: 22_696_000 picoseconds. - Weight::from_parts(23_275_000, 3543) + // Minimum execution time: 18_913_000 picoseconds. + Weight::from_parts(19_867_000, 3543) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) fn payout() -> Weight { // Proof Size summary in bytes: // Measured: `462` // Estimated: `3543` - // Minimum execution time: 63_660_000 picoseconds. - Weight::from_parts(65_006_000, 3543) + // Minimum execution time: 53_297_000 picoseconds. + Weight::from_parts(55_144_000, 3543) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) - /// Storage: RankedCollective Members (r:1 w:0) - /// Proof: RankedCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) + /// Storage: `RankedCollective::Members` (r:1 w:0) + /// Proof: `RankedCollective::Members` (`max_values`: None, `max_size`: Some(42), added: 2517, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn payout_other() -> Weight { // Proof Size summary in bytes: // Measured: `462` // Estimated: `3593` - // Minimum execution time: 64_706_000 picoseconds. - Weight::from_parts(65_763_000, 3593) + // Minimum execution time: 53_638_000 picoseconds. + Weight::from_parts(55_328_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Salary Status (r:1 w:1) - /// Proof: Salary Status (max_values: Some(1), max_size: Some(56), added: 551, mode: MaxEncodedLen) - /// Storage: Salary Claimant (r:1 w:1) - /// Proof: Salary Claimant (max_values: None, max_size: Some(78), added: 2553, mode: MaxEncodedLen) + /// Storage: `Salary::Status` (r:1 w:1) + /// Proof: `Salary::Status` (`max_values`: Some(1), `max_size`: Some(56), added: 551, mode: `MaxEncodedLen`) + /// Storage: `Salary::Claimant` (r:1 w:1) + /// Proof: `Salary::Claimant` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) fn check_payment() -> Weight { // Proof Size summary in bytes: // Measured: `170` // Estimated: `3543` - // Minimum execution time: 11_838_000 picoseconds. - Weight::from_parts(12_323_000, 3543) + // Minimum execution time: 10_557_000 picoseconds. + Weight::from_parts(11_145_000, 3543) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/substrate/frame/sassafras/Cargo.toml b/substrate/frame/sassafras/Cargo.toml index ad4c0ba12f0b10f60f18c853975637355b7172c6..325a39bf59799f6b314d54ad131b892eec61ec44 100644 --- a/substrate/frame/sassafras/Cargo.toml +++ b/substrate/frame/sassafras/Cargo.toml @@ -22,7 +22,7 @@ scale-info = { version = "2.5.0", default-features = false, features = ["derive" frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } sp-consensus-sassafras = { path = "../../primitives/consensus/sassafras", default-features = false, features = ["serde"] } sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } @@ -31,6 +31,7 @@ sp-std = { path = "../../primitives/std", default-features = false } [dev-dependencies] array-bytes = "6.1" sp-core = { path = "../../primitives/core" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } [features] default = ["std"] diff --git a/substrate/frame/sassafras/src/data/tickets-sort.md b/substrate/frame/sassafras/src/data/tickets-sort.md index 4d96a6825c889b152bbf0471c006e0d85dbed635..64fc45e4fb00af4842aed79b9ea730391e7038d8 100644 --- a/substrate/frame/sassafras/src/data/tickets-sort.md +++ b/substrate/frame/sassafras/src/data/tickets-sort.md @@ -267,7 +267,7 @@ buffer (i.e. how many tickets we retain from the last processed segment) | 3 | 14 | | 2 | 17 | | 1 | 9 | -| 0 | 13 +| 0 | 13 | # Graph of the same data diff --git a/substrate/frame/sassafras/src/mock.rs b/substrate/frame/sassafras/src/mock.rs index 5aca815cc2140a1f04f05c55e60c2d77d69bcb7e..4c6efaa63a9ea46bdda14f0d5114184c5c42c43b 100644 --- a/substrate/frame/sassafras/src/mock.rs +++ b/substrate/frame/sassafras/src/mock.rs @@ -34,7 +34,8 @@ use sp_core::{ H256, U256, }; use sp_runtime::{ - testing::{Digest, DigestItem, Header, TestXt}, + generic::UncheckedExtrinsic, + testing::{Digest, DigestItem, Header}, BuildStorage, }; @@ -53,7 +54,7 @@ where RuntimeCall: From, { type OverarchingCall = RuntimeCall; - type Extrinsic = TestXt; + type Extrinsic = UncheckedExtrinsic; } impl pallet_sassafras::Config for Test { @@ -99,7 +100,7 @@ pub fn new_test_ext_with_pairs( pallet_sassafras::GenesisConfig:: { authorities: authorities.clone(), epoch_config: TEST_EPOCH_CONFIGURATION, - _phantom: sp_std::marker::PhantomData, + _phantom: core::marker::PhantomData, } .assimilate_storage(&mut storage) .unwrap(); @@ -209,7 +210,7 @@ pub fn make_ticket_body(attempt_idx: u32, pair: &AuthorityPair) -> (TicketId, Ti } pub fn make_dummy_ticket_body(attempt_idx: u32) -> (TicketId, TicketBody) { - let hash = sp_core::hashing::blake2_256(&attempt_idx.to_le_bytes()); + let hash = sp_crypto_hashing::blake2_256(&attempt_idx.to_le_bytes()); let erased_public = EphemeralPublic::unchecked_from(hash); let revealed_public = erased_public; diff --git a/substrate/frame/scheduler/Cargo.toml b/substrate/frame/scheduler/Cargo.toml index c27276c607e6c44828388e5e4346cfe82bc051af..f50f6afdc06379f0ffaca3460df314e1eb0e4945 100644 --- a/substrate/frame/scheduler/Cargo.toml +++ b/substrate/frame/scheduler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-scheduler" -version = "4.0.0-dev" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -14,7 +14,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } @@ -23,7 +23,7 @@ sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } sp-weights = { path = "../../primitives/weights", default-features = false } -docify = "0.2.6" +docify = "0.2.7" [dev-dependencies] pallet-preimage = { path = "../preimage" } diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index cc86a17973780870970a7b604b4fd5d30cffe263..18441d54b39a2244f1eb9677d38be2576e141d94 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -22,12 +22,13 @@ use frame_benchmarking::v1::{account, benchmarks, BenchmarkError}; use frame_support::{ ensure, traits::{schedule::Priority, BoundedInline}, + weights::WeightMeter, }; use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use sp_std::{prelude::*, vec}; use crate::Pallet as Scheduler; -use frame_system::Call as SystemCall; +use frame_system::{Call as SystemCall, EventRecord}; const SEED: u32 = 0; @@ -35,6 +36,14 @@ const BLOCK_NUMBER: u32 = 2; type SystemOrigin = ::RuntimeOrigin; +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + /// Add `n` items to the schedule. /// /// For `resolved`: @@ -306,5 +315,105 @@ benchmarks! { ); } + schedule_retry { + let s in 1 .. T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + + fill_schedule::(when, s)?; + let name = u32_to_name(s - 1); + let address = Lookup::::get(name).unwrap(); + let period: BlockNumberFor = 1u32.into(); + let root: ::PalletsOrigin = frame_system::RawOrigin::Root.into(); + let retry_config = RetryConfig { total_retries: 10, remaining: 10, period }; + Retries::::insert(address, retry_config); + let (mut when, index) = address; + let task = Agenda::::get(when)[index as usize].clone().unwrap(); + let mut weight_counter = WeightMeter::with_limit(T::MaximumWeight::get()); + }: { + Scheduler::::schedule_retry(&mut weight_counter, when, when, index, &task, retry_config); + } verify { + when = when + BlockNumberFor::::one(); + assert_eq!( + Retries::::get((when, 0)), + Some(RetryConfig { total_retries: 10, remaining: 9, period }) + ); + } + + set_retry { + let s = T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + + fill_schedule::(when, s)?; + let name = u32_to_name(s - 1); + let address = Lookup::::get(name).unwrap(); + let (when, index) = address; + let period = BlockNumberFor::::one(); + }: _(RawOrigin::Root, (when, index), 10, period) + verify { + assert_eq!( + Retries::::get((when, index)), + Some(RetryConfig { total_retries: 10, remaining: 10, period }) + ); + assert_last_event::( + Event::RetrySet { task: address, id: None, period, retries: 10 }.into(), + ); + } + + set_retry_named { + let s = T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + + fill_schedule::(when, s)?; + let name = u32_to_name(s - 1); + let address = Lookup::::get(name).unwrap(); + let (when, index) = address; + let period = BlockNumberFor::::one(); + }: _(RawOrigin::Root, name, 10, period) + verify { + assert_eq!( + Retries::::get((when, index)), + Some(RetryConfig { total_retries: 10, remaining: 10, period }) + ); + assert_last_event::( + Event::RetrySet { task: address, id: Some(name), period, retries: 10 }.into(), + ); + } + + cancel_retry { + let s = T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + + fill_schedule::(when, s)?; + let name = u32_to_name(s - 1); + let address = Lookup::::get(name).unwrap(); + let (when, index) = address; + let period = BlockNumberFor::::one(); + assert!(Scheduler::::set_retry(RawOrigin::Root.into(), (when, index), 10, period).is_ok()); + }: _(RawOrigin::Root, (when, index)) + verify { + assert!(!Retries::::contains_key((when, index))); + assert_last_event::( + Event::RetryCancelled { task: address, id: None }.into(), + ); + } + + cancel_retry_named { + let s = T::MaxScheduledPerBlock::get(); + let when = BLOCK_NUMBER.into(); + + fill_schedule::(when, s)?; + let name = u32_to_name(s - 1); + let address = Lookup::::get(name).unwrap(); + let (when, index) = address; + let period = BlockNumberFor::::one(); + assert!(Scheduler::::set_retry_named(RawOrigin::Root.into(), name, 10, period).is_ok()); + }: _(RawOrigin::Root, name) + verify { + assert!(!Retries::::contains_key((when, index))); + assert_last_event::( + Event::RetryCancelled { task: address, id: Some(name) }.into(), + ); + } + impl_benchmark_test_suite!(Scheduler, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index e94f154eee32f908dc449ba7ff9e54853cc4b915..af3abd8ac4f2a81b363c4553f3652a2cba972212 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -122,6 +122,17 @@ pub type CallOrHashOf = pub type BoundedCallOf = Bounded<::RuntimeCall, ::Hashing>; +/// The configuration of the retry mechanism for a given task along with its current state. +#[derive(Clone, Copy, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] +pub struct RetryConfig { + /// Initial amount of retries allowed. + total_retries: u8, + /// Amount of retries left. + remaining: u8, + /// Period of time between retry attempts. + period: Period, +} + #[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))] #[derive(Clone, RuntimeDebug, Encode, Decode)] struct ScheduledV1 { @@ -148,6 +159,26 @@ pub struct Scheduled { _phantom: PhantomData, } +impl + Scheduled +where + Call: Clone, + PalletsOrigin: Clone, +{ + /// Create a new task to be used for retry attempts of the original one. The cloned task will + /// have the same `priority`, `call` and `origin`, but will always be non-periodic and unnamed. + pub fn as_retry(&self) -> Self { + Self { + maybe_id: None, + priority: self.priority, + call: self.call.clone(), + maybe_periodic: None, + origin: self.origin.clone(), + _phantom: Default::default(), + } + } +} + use crate::{Scheduled as ScheduledV3, Scheduled as ScheduledV2}; pub type ScheduledV2Of = ScheduledV2< @@ -198,7 +229,7 @@ pub mod pallet { use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*}; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] @@ -273,6 +304,16 @@ pub mod pallet { ValueQuery, >; + /// Retry configurations for items to be executed, indexed by task address. + #[pallet::storage] + pub type Retries = StorageMap< + _, + Blake2_128Concat, + TaskAddress>, + RetryConfig>, + OptionQuery, + >; + /// Lookup from a name to the block number and index of the task. /// /// For v3 -> v4 the previously unbounded identities are Blake2-256 hashed to form the v4 @@ -295,10 +336,22 @@ pub mod pallet { id: Option, result: DispatchResult, }, + /// Set a retry configuration for some task. + RetrySet { + task: TaskAddress>, + id: Option, + period: BlockNumberFor, + retries: u8, + }, + /// Cancel a retry configuration for some task. + RetryCancelled { task: TaskAddress>, id: Option }, /// The call for the provided hash was not found so the task has been aborted. CallUnavailable { task: TaskAddress>, id: Option }, /// The given task was unable to be renewed since the agenda is full at that block. PeriodicFailed { task: TaskAddress>, id: Option }, + /// The given task was unable to be retried since the agenda is full at that block or there + /// was not enough weight to reschedule it. + RetryFailed { task: TaskAddress>, id: Option }, /// The given task can never be executed since it is overweight. PermanentlyOverweight { task: TaskAddress>, id: Option }, } @@ -440,6 +493,111 @@ pub mod pallet { )?; Ok(()) } + + /// Set a retry configuration for a task so that, in case its scheduled run fails, it will + /// be retried after `period` blocks, for a total amount of `retries` retries or until it + /// succeeds. + /// + /// Tasks which need to be scheduled for a retry are still subject to weight metering and + /// agenda space, same as a regular task. If a periodic task fails, it will be scheduled + /// normally while the task is retrying. + /// + /// Tasks scheduled as a result of a retry for a periodic task are unnamed, non-periodic + /// clones of the original task. Their retry configuration will be derived from the + /// original task's configuration, but will have a lower value for `remaining` than the + /// original `total_retries`. + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::set_retry())] + pub fn set_retry( + origin: OriginFor, + task: TaskAddress>, + retries: u8, + period: BlockNumberFor, + ) -> DispatchResult { + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::RuntimeOrigin::from(origin); + let (when, index) = task; + let agenda = Agenda::::get(when); + let scheduled = agenda + .get(index as usize) + .and_then(Option::as_ref) + .ok_or(Error::::NotFound)?; + Self::ensure_privilege(origin.caller(), &scheduled.origin)?; + Retries::::insert( + (when, index), + RetryConfig { total_retries: retries, remaining: retries, period }, + ); + Self::deposit_event(Event::RetrySet { task, id: None, period, retries }); + Ok(()) + } + + /// Set a retry configuration for a named task so that, in case its scheduled run fails, it + /// will be retried after `period` blocks, for a total amount of `retries` retries or until + /// it succeeds. + /// + /// Tasks which need to be scheduled for a retry are still subject to weight metering and + /// agenda space, same as a regular task. If a periodic task fails, it will be scheduled + /// normally while the task is retrying. + /// + /// Tasks scheduled as a result of a retry for a periodic task are unnamed, non-periodic + /// clones of the original task. Their retry configuration will be derived from the + /// original task's configuration, but will have a lower value for `remaining` than the + /// original `total_retries`. + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::set_retry_named())] + pub fn set_retry_named( + origin: OriginFor, + id: TaskName, + retries: u8, + period: BlockNumberFor, + ) -> DispatchResult { + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::RuntimeOrigin::from(origin); + let (when, agenda_index) = Lookup::::get(&id).ok_or(Error::::NotFound)?; + let agenda = Agenda::::get(when); + let scheduled = agenda + .get(agenda_index as usize) + .and_then(Option::as_ref) + .ok_or(Error::::NotFound)?; + Self::ensure_privilege(origin.caller(), &scheduled.origin)?; + Retries::::insert( + (when, agenda_index), + RetryConfig { total_retries: retries, remaining: retries, period }, + ); + Self::deposit_event(Event::RetrySet { + task: (when, agenda_index), + id: Some(id), + period, + retries, + }); + Ok(()) + } + + /// Removes the retry configuration of a task. + #[pallet::call_index(8)] + #[pallet::weight(::WeightInfo::cancel_retry())] + pub fn cancel_retry( + origin: OriginFor, + task: TaskAddress>, + ) -> DispatchResult { + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::RuntimeOrigin::from(origin); + Self::do_cancel_retry(origin.caller(), task)?; + Self::deposit_event(Event::RetryCancelled { task, id: None }); + Ok(()) + } + + /// Cancel the retry configuration of a named task. + #[pallet::call_index(9)] + #[pallet::weight(::WeightInfo::cancel_retry_named())] + pub fn cancel_retry_named(origin: OriginFor, id: TaskName) -> DispatchResult { + T::ScheduleOrigin::ensure_origin(origin.clone())?; + let origin = ::RuntimeOrigin::from(origin); + let task = Lookup::::get(&id).ok_or(Error::::NotFound)?; + Self::do_cancel_retry(origin.caller(), task)?; + Self::deposit_event(Event::RetryCancelled { task, id: Some(id) }); + Ok(()) + } } } @@ -838,12 +996,7 @@ impl Pallet { Ok(None), |s| -> Result>, DispatchError> { if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) { - if matches!( - T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin), - Some(Ordering::Less) | None - ) { - return Err(BadOrigin.into()) - } + Self::ensure_privilege(o, &s.origin)?; }; Ok(s.take()) }, @@ -854,6 +1007,7 @@ impl Pallet { if let Some(id) = s.maybe_id { Lookup::::remove(id); } + Retries::::remove((when, index)); Self::cleanup_agenda(when); Self::deposit_event(Event::Canceled { when, index }); Ok(()) @@ -931,12 +1085,8 @@ impl Pallet { Agenda::::try_mutate(when, |agenda| -> DispatchResult { if let Some(s) = agenda.get_mut(i) { if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) { - if matches!( - T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin), - Some(Ordering::Less) | None - ) { - return Err(BadOrigin.into()) - } + Self::ensure_privilege(o, &s.origin)?; + Retries::::remove((when, index)); T::Preimages::drop(&s.call); } *s = None; @@ -973,6 +1123,20 @@ impl Pallet { Self::deposit_event(Event::Canceled { when, index }); Self::place_task(new_time, task).map_err(|x| x.0) } + + fn do_cancel_retry( + origin: &T::PalletsOrigin, + (when, index): TaskAddress>, + ) -> Result<(), DispatchError> { + let agenda = Agenda::::get(when); + let scheduled = agenda + .get(index as usize) + .and_then(Option::as_ref) + .ok_or(Error::::NotFound)?; + Self::ensure_privilege(origin, &scheduled.origin)?; + Retries::::remove((when, index)); + Ok(()) + } } enum ServiceTaskError { @@ -1124,11 +1288,21 @@ impl Pallet { }, Err(()) => Err((Overweight, Some(task))), Ok(result) => { + let failed = result.is_err(); + let maybe_retry_config = Retries::::take((when, agenda_index)); Self::deposit_event(Event::Dispatched { task: (when, agenda_index), id: task.maybe_id, result, }); + + match maybe_retry_config { + Some(retry_config) if failed => { + Self::schedule_retry(weight, now, when, agenda_index, &task, retry_config); + }, + _ => {}, + } + if let &Some((period, count)) = &task.maybe_periodic { if count > 1 { task.maybe_periodic = Some((period, count - 1)); @@ -1137,7 +1311,10 @@ impl Pallet { } let wake = now.saturating_add(period); match Self::place_task(wake, task) { - Ok(_) => {}, + Ok(new_address) => + if let Some(retry_config) = maybe_retry_config { + Retries::::insert(new_address, retry_config); + }, Err((_, task)) => { // TODO: Leave task in storage somewhere for it to be rescheduled // manually. @@ -1192,6 +1369,70 @@ impl Pallet { let _ = weight.try_consume(call_weight); Ok(result) } + + /// Check if a task has a retry configuration in place and, if so, try to reschedule it. + /// + /// Possible causes for failure to schedule a retry for a task: + /// - there wasn't enough weight to run the task reschedule logic + /// - there was no retry configuration in place + /// - there were no more retry attempts left + /// - the agenda was full. + fn schedule_retry( + weight: &mut WeightMeter, + now: BlockNumberFor, + when: BlockNumberFor, + agenda_index: u32, + task: &ScheduledOf, + retry_config: RetryConfig>, + ) { + if weight + .try_consume(T::WeightInfo::schedule_retry(T::MaxScheduledPerBlock::get())) + .is_err() + { + Self::deposit_event(Event::RetryFailed { + task: (when, agenda_index), + id: task.maybe_id, + }); + return; + } + + let RetryConfig { total_retries, mut remaining, period } = retry_config; + remaining = match remaining.checked_sub(1) { + Some(n) => n, + None => return, + }; + let wake = now.saturating_add(period); + match Self::place_task(wake, task.as_retry()) { + Ok(address) => { + // Reinsert the retry config to the new address of the task after it was + // placed. + Retries::::insert(address, RetryConfig { total_retries, remaining, period }); + }, + Err((_, task)) => { + // TODO: Leave task in storage somewhere for it to be + // rescheduled manually. + T::Preimages::drop(&task.call); + Self::deposit_event(Event::RetryFailed { + task: (when, agenda_index), + id: task.maybe_id, + }); + }, + } + } + + /// Ensure that `left` has at least the same level of privilege or higher than `right`. + /// + /// Returns an error if `left` has a lower level of privilege or the two cannot be compared. + fn ensure_privilege( + left: &::PalletsOrigin, + right: &::PalletsOrigin, + ) -> Result<(), DispatchError> { + if matches!(T::OriginPrivilegeCmp::cmp_privilege(left, right), Some(Ordering::Less) | None) + { + return Err(BadOrigin.into()); + } + Ok(()) + } } impl schedule::v2::Anon, ::RuntimeCall, T::PalletsOrigin> diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs index 76e2e04b49cc6fc7d9f886090de4df44911c0333..c2e956035a767031ceb64e4e0d42cebfb527e64d 100644 --- a/substrate/frame/scheduler/src/migration.rs +++ b/substrate/frame/scheduler/src/migration.rs @@ -81,7 +81,7 @@ pub mod v3 { StorageMap, Twox64Concat, Vec, TaskAddress>>; /// Migrate the scheduler pallet from V3 to V4. - pub struct MigrateToV4(sp_std::marker::PhantomData); + pub struct MigrateToV4(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV4 { #[cfg(feature = "try-runtime")] @@ -194,7 +194,7 @@ pub mod v4 { /// /// This should be run on a scheduler that does not have /// since it piles up `None`-only agendas. This does not modify the pallet version. - pub struct CleanupAgendas(sp_std::marker::PhantomData); + pub struct CleanupAgendas(core::marker::PhantomData); impl OnRuntimeUpgrade for CleanupAgendas { #[cfg(feature = "try-runtime")] diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs index d22b9fcf8d99e35ecc7a39ff2097676eb788eee6..bf7dac0d53ae2596f6e9672e1753d96879b7c2be 100644 --- a/substrate/frame/scheduler/src/mock.rs +++ b/substrate/frame/scheduler/src/mock.rs @@ -22,17 +22,10 @@ use super::*; use crate as scheduler; use frame_support::{ derive_impl, ord_parameter_types, parameter_types, - traits::{ - ConstU32, ConstU64, Contains, EitherOfDiverse, EqualPrivilegeOnly, OnFinalize, OnInitialize, - }, - weights::constants::RocksDbWeight, + traits::{ConstU32, Contains, EitherOfDiverse, EqualPrivilegeOnly, OnFinalize, OnInitialize}, }; use frame_system::{EnsureRoot, EnsureSignedBy}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, Perbill, -}; +use sp_runtime::{BuildStorage, Perbill}; // Logger module to track execution. #[frame_support::pallet] @@ -51,6 +44,17 @@ pub mod logger { #[pallet::pallet] pub struct Pallet(_); + #[pallet::storage] + pub type Threshold = StorageValue<_, (BlockNumberFor, BlockNumberFor)>; + + #[pallet::error] + pub enum Error { + /// Under the threshold. + TooEarly, + /// Over the threshold. + TooLate, + } + #[pallet::hooks] impl Hooks> for Pallet {} @@ -89,6 +93,20 @@ pub mod logger { }); Ok(()) } + + #[pallet::call_index(2)] + #[pallet::weight(*weight)] + pub fn timed_log(origin: OriginFor, i: u32, weight: Weight) -> DispatchResult { + let now = frame_system::Pallet::::block_number(); + let (start, end) = Threshold::::get().unwrap_or((0u32.into(), u32::MAX.into())); + ensure!(now >= start, Error::::TooEarly); + ensure!(now <= end, Error::::TooLate); + Self::deposit_event(Event::Logged(i, weight)); + Log::mutate(|log| { + log.push((origin.caller().clone(), i)); + }); + Ok(()) + } } } @@ -122,28 +140,7 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl system::Config for Test { type BaseCallFilter = BaseFilter; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl logger::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -198,6 +195,21 @@ impl WeightInfo for TestWeightInfo { fn cancel_named(_s: u32) -> Weight { Weight::from_parts(50, 0) } + fn schedule_retry(_s: u32) -> Weight { + Weight::from_parts(100000, 0) + } + fn set_retry() -> Weight { + Weight::from_parts(50, 0) + } + fn set_retry_named() -> Weight { + Weight::from_parts(50, 0) + } + fn cancel_retry() -> Weight { + Weight::from_parts(50, 0) + } + fn cancel_retry_named() -> Weight { + Weight::from_parts(50, 0) + } } parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index 1bf8b3e5f3a03bd2c9d233a24351d1ae2d923c45..1ed2ca9e2f36d0b3bf4b3a02983ab1ab7539c30b 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -19,7 +19,8 @@ use super::*; use crate::mock::{ - logger, new_test_ext, root, run_to_block, LoggerCall, RuntimeCall, Scheduler, Test, *, + logger::{self, Threshold}, + new_test_ext, root, run_to_block, LoggerCall, RuntimeCall, Scheduler, Test, *, }; use frame_support::{ assert_err, assert_noop, assert_ok, @@ -179,6 +180,865 @@ fn periodic_scheduling_works() { }); } +#[test] +fn retry_scheduling_works() { + new_test_ext().execute_with(|| { + // task fails until block 8 is reached + Threshold::::put((8, 100)); + // task 42 at #4 + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + assert!(Agenda::::get(4)[0].is_some()); + // retry 10 times every 3 blocks + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 3)); + assert_eq!(Retries::::iter().count(), 1); + run_to_block(3); + assert!(logger::log().is_empty()); + assert!(Agenda::::get(4)[0].is_some()); + // task should be retried in block 7 + run_to_block(4); + assert!(Agenda::::get(4).is_empty()); + assert!(Agenda::::get(7)[0].is_some()); + assert!(logger::log().is_empty()); + run_to_block(6); + assert!(Agenda::::get(7)[0].is_some()); + assert!(logger::log().is_empty()); + // task still fails, should be retried in block 10 + run_to_block(7); + assert!(Agenda::::get(7).is_empty()); + assert!(Agenda::::get(10)[0].is_some()); + assert!(logger::log().is_empty()); + run_to_block(8); + assert!(Agenda::::get(10)[0].is_some()); + assert!(logger::log().is_empty()); + run_to_block(9); + assert!(logger::log().is_empty()); + assert_eq!(Retries::::iter().count(), 1); + // finally it should succeed + run_to_block(10); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + assert_eq!(Retries::::iter().count(), 0); + run_to_block(11); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(12); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(100); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + }); +} + +#[test] +fn named_retry_scheduling_works() { + new_test_ext().execute_with(|| { + // task fails until block 8 is reached + Threshold::::put((8, 100)); + // task 42 at #4 + let call = RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0), + }); + assert_eq!( + Scheduler::do_schedule_named( + [1u8; 32], + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + ) + .unwrap(), + (4, 0) + ); + assert!(Agenda::::get(4)[0].is_some()); + // retry 10 times every 3 blocks + assert_ok!(Scheduler::set_retry_named(root().into(), [1u8; 32], 10, 3)); + assert_eq!(Retries::::iter().count(), 1); + run_to_block(3); + assert!(logger::log().is_empty()); + assert!(Agenda::::get(4)[0].is_some()); + // task should be retried in block 7 + run_to_block(4); + assert!(Agenda::::get(4).is_empty()); + assert!(Agenda::::get(7)[0].is_some()); + assert!(logger::log().is_empty()); + run_to_block(6); + assert!(Agenda::::get(7)[0].is_some()); + assert!(logger::log().is_empty()); + // task still fails, should be retried in block 10 + run_to_block(7); + assert!(Agenda::::get(7).is_empty()); + assert!(Agenda::::get(10)[0].is_some()); + assert!(logger::log().is_empty()); + run_to_block(8); + assert!(Agenda::::get(10)[0].is_some()); + assert!(logger::log().is_empty()); + run_to_block(9); + assert!(logger::log().is_empty()); + assert_eq!(Retries::::iter().count(), 1); + // finally it should succeed + run_to_block(10); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + assert_eq!(Retries::::iter().count(), 0); + run_to_block(11); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(12); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(100); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + }); +} + +#[test] +fn retry_scheduling_multiple_tasks_works() { + new_test_ext().execute_with(|| { + // task fails until block 8 is reached + Threshold::::put((8, 100)); + // task 20 at #4 + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 20, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + // task 42 at #4 + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert_eq!(Agenda::::get(4).len(), 2); + // task 20 will be retried 3 times every block + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 3, 1)); + // task 42 will be retried 10 times every 3 blocks + assert_ok!(Scheduler::set_retry(root().into(), (4, 1), 10, 3)); + assert_eq!(Retries::::iter().count(), 2); + run_to_block(3); + assert!(logger::log().is_empty()); + assert_eq!(Agenda::::get(4).len(), 2); + // both tasks fail + run_to_block(4); + assert!(Agenda::::get(4).is_empty()); + // 20 is rescheduled for next block + assert_eq!(Agenda::::get(5).len(), 1); + // 42 is rescheduled for block 7 + assert_eq!(Agenda::::get(7).len(), 1); + assert!(logger::log().is_empty()); + // 20 still fails + run_to_block(5); + // 20 rescheduled for next block + assert_eq!(Agenda::::get(6).len(), 1); + assert_eq!(Agenda::::get(7).len(), 1); + assert_eq!(Retries::::iter().count(), 2); + assert!(logger::log().is_empty()); + // 20 still fails + run_to_block(6); + // rescheduled for next block together with 42 + assert_eq!(Agenda::::get(7).len(), 2); + assert_eq!(Retries::::iter().count(), 2); + assert!(logger::log().is_empty()); + // both tasks will fail, for 20 it was the last retry so it's dropped + run_to_block(7); + assert!(Agenda::::get(7).is_empty()); + assert!(Agenda::::get(8).is_empty()); + // 42 is rescheduled for block 10 + assert_eq!(Agenda::::get(10).len(), 1); + assert_eq!(Retries::::iter().count(), 1); + assert!(logger::log().is_empty()); + run_to_block(8); + assert_eq!(Agenda::::get(10).len(), 1); + assert!(logger::log().is_empty()); + run_to_block(9); + assert!(logger::log().is_empty()); + assert_eq!(Retries::::iter().count(), 1); + // 42 runs successfully + run_to_block(10); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + assert_eq!(Retries::::iter().count(), 0); + run_to_block(11); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(12); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(100); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + }); +} + +#[test] +fn retry_scheduling_multiple_named_tasks_works() { + new_test_ext().execute_with(|| { + // task fails until we reach block 8 + Threshold::::put((8, 100)); + // task 20 at #4 + assert_ok!(Scheduler::do_schedule_named( + [20u8; 32], + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 20, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + // task 42 at #4 + assert_ok!(Scheduler::do_schedule_named( + [42u8; 32], + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert_eq!(Agenda::::get(4).len(), 2); + // task 20 will be retried 3 times every block + assert_ok!(Scheduler::set_retry_named(root().into(), [20u8; 32], 3, 1)); + // task 42 will be retried 10 times every 3 block + assert_ok!(Scheduler::set_retry_named(root().into(), [42u8; 32], 10, 3)); + assert_eq!(Retries::::iter().count(), 2); + run_to_block(3); + assert!(logger::log().is_empty()); + assert_eq!(Agenda::::get(4).len(), 2); + // both tasks fail + run_to_block(4); + assert!(Agenda::::get(4).is_empty()); + // 42 is rescheduled for block 7 + assert_eq!(Agenda::::get(7).len(), 1); + // 20 is rescheduled for next block + assert_eq!(Agenda::::get(5).len(), 1); + assert!(logger::log().is_empty()); + // 20 still fails + run_to_block(5); + // 20 rescheduled for next block + assert_eq!(Agenda::::get(6).len(), 1); + assert_eq!(Agenda::::get(7).len(), 1); + assert_eq!(Retries::::iter().count(), 2); + assert!(logger::log().is_empty()); + // 20 still fails + run_to_block(6); + // 20 rescheduled for next block together with 42 + assert_eq!(Agenda::::get(7).len(), 2); + assert_eq!(Retries::::iter().count(), 2); + assert!(logger::log().is_empty()); + // both tasks will fail, for 20 it was the last retry so it's dropped + run_to_block(7); + assert!(Agenda::::get(7).is_empty()); + assert!(Agenda::::get(8).is_empty()); + // 42 is rescheduled for block 10 + assert_eq!(Agenda::::get(10).len(), 1); + assert_eq!(Retries::::iter().count(), 1); + assert!(logger::log().is_empty()); + run_to_block(8); + assert_eq!(Agenda::::get(10).len(), 1); + assert!(logger::log().is_empty()); + run_to_block(9); + assert!(logger::log().is_empty()); + assert_eq!(Retries::::iter().count(), 1); + // 42 runs successfully + run_to_block(10); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + assert_eq!(Retries::::iter().count(), 0); + run_to_block(11); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(12); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + run_to_block(100); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + }); +} + +#[test] +fn retry_scheduling_with_period_works() { + new_test_ext().execute_with(|| { + // tasks fail until we reach block 4 and after we're past block 8 + Threshold::::put((4, 8)); + // task 42 at #4, every 3 blocks, 6 times + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + Some((3, 6)), + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert!(Agenda::::get(4)[0].is_some()); + // 42 will be retried 10 times every 2 blocks + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 2)); + assert_eq!(Retries::::iter().count(), 1); + run_to_block(3); + assert!(logger::log().is_empty()); + assert!(Agenda::::get(4)[0].is_some()); + // 42 runs successfully once, it will run again at block 7 + run_to_block(4); + assert!(Agenda::::get(4).is_empty()); + assert!(Agenda::::get(7)[0].is_some()); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // nothing changed + run_to_block(6); + assert!(Agenda::::get(7)[0].is_some()); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // 42 runs successfully again, it will run again at block 10 + run_to_block(7); + assert!(Agenda::::get(7).is_empty()); + assert!(Agenda::::get(10)[0].is_some()); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + run_to_block(9); + assert!(Agenda::::get(10)[0].is_some()); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // 42 has 10 retries left out of a total of 10 + assert_eq!(Retries::::get((10, 0)).unwrap().remaining, 10); + // 42 will fail because we're outside the set threshold (block number in `4..8`), so it + // should be retried in 2 blocks (at block 12) + run_to_block(10); + // should be queued for the normal period of 3 blocks + assert!(Agenda::::get(13)[0].is_some()); + // should also be queued to be retried in 2 blocks + assert!(Agenda::::get(12)[0].is_some()); + // 42 has consumed one retry attempt + assert_eq!(Retries::::get((12, 0)).unwrap().remaining, 9); + assert_eq!(Retries::::get((13, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // 42 will fail again + run_to_block(12); + // should still be queued for the normal period + assert!(Agenda::::get(13)[0].is_some()); + // should be queued to be retried in 2 blocks + assert!(Agenda::::get(14)[0].is_some()); + // 42 has consumed another retry attempt + assert_eq!(Retries::::get((14, 0)).unwrap().remaining, 8); + assert_eq!(Retries::::get((13, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // 42 will fail for the regular periodic run + run_to_block(13); + // should still be queued for the normal period + assert!(Agenda::::get(16)[0].is_some()); + // should still be queued to be retried next block + assert!(Agenda::::get(14)[0].is_some()); + // 42 consumed another periodic run, which failed, so another retry is queued for block 15 + assert!(Agenda::::get(16)[0].as_ref().unwrap().maybe_periodic.is_some()); + assert!(Agenda::::get(15)[0].as_ref().unwrap().maybe_periodic.is_none()); + assert!(Agenda::::get(14)[0].as_ref().unwrap().maybe_periodic.is_none()); + assert_eq!(Retries::::iter().count(), 3); + assert!(Retries::::get((14, 0)).unwrap().remaining == 8); + assert!(Retries::::get((15, 0)).unwrap().remaining == 9); + assert!(Retries::::get((16, 0)).unwrap().remaining == 10); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // change the threshold to allow the task to succeed + Threshold::::put((14, 100)); + // first retry should now succeed + run_to_block(14); + assert!(Agenda::::get(15)[0].as_ref().unwrap().maybe_periodic.is_none()); + assert_eq!(Agenda::::get(16).iter().filter(|entry| entry.is_some()).count(), 1); + assert!(Agenda::::get(16)[0].is_some()); + assert_eq!(Retries::::get((15, 0)).unwrap().remaining, 9); + assert_eq!(Retries::::get((16, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]); + // second retry should also succeed + run_to_block(15); + assert_eq!(Agenda::::get(16).iter().filter(|entry| entry.is_some()).count(), 1); + assert!(Agenda::::get(16)[0].is_some()); + assert!(Agenda::::get(17).is_empty()); + assert_eq!(Retries::::get((16, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!( + logger::log(), + vec![(root(), 42u32), (root(), 42u32), (root(), 42u32), (root(), 42u32)] + ); + // normal periodic run on block 16 will succeed + run_to_block(16); + // next periodic run at block 19 + assert!(Agenda::::get(19)[0].is_some()); + assert!(Agenda::::get(18).is_empty()); + assert!(Agenda::::get(17).is_empty()); + assert_eq!(Retries::::get((19, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!( + logger::log(), + vec![ + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32) + ] + ); + // final periodic run on block 19 will succeed + run_to_block(19); + // next periodic run at block 19 + assert_eq!(Agenda::::iter().count(), 0); + assert_eq!(Retries::::iter().count(), 0); + assert_eq!( + logger::log(), + vec![ + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32) + ] + ); + }); +} + +#[test] +fn named_retry_scheduling_with_period_works() { + new_test_ext().execute_with(|| { + // tasks fail until we reach block 4 and after we're past block 8 + Threshold::::put((4, 8)); + // task 42 at #4, every 3 blocks, 6 times + assert_ok!(Scheduler::do_schedule_named( + [42u8; 32], + DispatchTime::At(4), + Some((3, 6)), + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert!(Agenda::::get(4)[0].is_some()); + // 42 will be retried 10 times every 2 blocks + assert_ok!(Scheduler::set_retry_named(root().into(), [42u8; 32], 10, 2)); + assert_eq!(Retries::::iter().count(), 1); + run_to_block(3); + assert!(logger::log().is_empty()); + assert!(Agenda::::get(4)[0].is_some()); + // 42 runs successfully once, it will run again at block 7 + run_to_block(4); + assert!(Agenda::::get(4).is_empty()); + assert!(Agenda::::get(7)[0].is_some()); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // nothing changed + run_to_block(6); + assert!(Agenda::::get(7)[0].is_some()); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // 42 runs successfully again, it will run again at block 10 + run_to_block(7); + assert!(Agenda::::get(7).is_empty()); + assert!(Agenda::::get(10)[0].is_some()); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + run_to_block(9); + assert!(Agenda::::get(10)[0].is_some()); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // 42 has 10 retries left out of a total of 10 + assert_eq!(Retries::::get((10, 0)).unwrap().remaining, 10); + // 42 will fail because we're outside the set threshold (block number in `4..8`), so it + // should be retried in 2 blocks (at block 12) + run_to_block(10); + // should be queued for the normal period of 3 blocks + assert!(Agenda::::get(13)[0].is_some()); + // should also be queued to be retried in 2 blocks + assert!(Agenda::::get(12)[0].is_some()); + // 42 has consumed one retry attempt + assert_eq!(Retries::::get((12, 0)).unwrap().remaining, 9); + assert_eq!(Retries::::get((13, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(Lookup::::get([42u8; 32]).unwrap(), (13, 0)); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // 42 will fail again + run_to_block(12); + // should still be queued for the normal period + assert!(Agenda::::get(13)[0].is_some()); + // should be queued to be retried in 2 blocks + assert!(Agenda::::get(14)[0].is_some()); + // 42 has consumed another retry attempt + assert_eq!(Retries::::get((14, 0)).unwrap().remaining, 8); + assert_eq!(Retries::::get((13, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // 42 will fail for the regular periodic run + run_to_block(13); + // should still be queued for the normal period + assert!(Agenda::::get(16)[0].is_some()); + // should still be queued to be retried next block + assert!(Agenda::::get(14)[0].is_some()); + // 42 consumed another periodic run, which failed, so another retry is queued for block 15 + assert!(Agenda::::get(16)[0].as_ref().unwrap().maybe_periodic.is_some()); + assert!(Agenda::::get(15)[0].as_ref().unwrap().maybe_periodic.is_none()); + assert!(Agenda::::get(14)[0].as_ref().unwrap().maybe_periodic.is_none()); + assert_eq!(Retries::::iter().count(), 3); + assert!(Retries::::get((14, 0)).unwrap().remaining == 8); + assert!(Retries::::get((15, 0)).unwrap().remaining == 9); + assert!(Retries::::get((16, 0)).unwrap().remaining == 10); + assert_eq!(Lookup::::get([42u8; 32]).unwrap(), (16, 0)); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + // change the threshold to allow the task to succeed + Threshold::::put((14, 100)); + // first retry should now succeed + run_to_block(14); + assert!(Agenda::::get(15)[0].as_ref().unwrap().maybe_periodic.is_none()); + assert_eq!(Agenda::::get(16).iter().filter(|entry| entry.is_some()).count(), 1); + assert!(Agenda::::get(16)[0].is_some()); + assert_eq!(Retries::::get((15, 0)).unwrap().remaining, 9); + assert_eq!(Retries::::get((16, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]); + // second retry should also succeed + run_to_block(15); + assert_eq!(Agenda::::get(16).iter().filter(|entry| entry.is_some()).count(), 1); + assert!(Agenda::::get(16)[0].is_some()); + assert!(Agenda::::get(17).is_empty()); + assert_eq!(Retries::::get((16, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(Lookup::::get([42u8; 32]).unwrap(), (16, 0)); + assert_eq!( + logger::log(), + vec![(root(), 42u32), (root(), 42u32), (root(), 42u32), (root(), 42u32)] + ); + // normal periodic run on block 16 will succeed + run_to_block(16); + // next periodic run at block 19 + assert!(Agenda::::get(19)[0].is_some()); + assert!(Agenda::::get(18).is_empty()); + assert!(Agenda::::get(17).is_empty()); + assert_eq!(Retries::::get((19, 0)).unwrap().remaining, 10); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(Lookup::::get([42u8; 32]).unwrap(), (19, 0)); + assert_eq!( + logger::log(), + vec![ + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32) + ] + ); + // final periodic run on block 19 will succeed + run_to_block(19); + // next periodic run at block 19 + assert_eq!(Agenda::::iter().count(), 0); + assert_eq!(Retries::::iter().count(), 0); + assert_eq!(Lookup::::iter().count(), 0); + assert_eq!( + logger::log(), + vec![ + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32), + (root(), 42u32) + ] + ); + }); +} + +#[test] +fn retry_scheduling_expires() { + new_test_ext().execute_with(|| { + // task will fail if we're past block 3 + Threshold::::put((1, 3)); + // task 42 at #4 + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + assert!(Agenda::::get(4)[0].is_some()); + // task 42 will be retried 3 times every block + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 3, 1)); + assert_eq!(Retries::::iter().count(), 1); + run_to_block(3); + assert!(logger::log().is_empty()); + // task 42 is scheduled for next block + assert!(Agenda::::get(4)[0].is_some()); + // task fails because we're past block 3 + run_to_block(4); + // task is scheduled for next block + assert!(Agenda::::get(4).is_empty()); + assert!(Agenda::::get(5)[0].is_some()); + // one retry attempt is consumed + assert_eq!(Retries::::get((5, 0)).unwrap().remaining, 2); + assert!(logger::log().is_empty()); + // task fails again + run_to_block(5); + // task is scheduled for next block + assert!(Agenda::::get(5).is_empty()); + assert!(Agenda::::get(6)[0].is_some()); + // another retry attempt is consumed + assert_eq!(Retries::::get((6, 0)).unwrap().remaining, 1); + assert!(logger::log().is_empty()); + // task fails again + run_to_block(6); + // task is scheduled for next block + assert!(Agenda::::get(6).is_empty()); + assert!(Agenda::::get(7)[0].is_some()); + // another retry attempt is consumed + assert_eq!(Retries::::get((7, 0)).unwrap().remaining, 0); + assert!(logger::log().is_empty()); + // task fails again + run_to_block(7); + // task ran out of retries so it gets dropped + assert_eq!(Agenda::::iter().count(), 0); + assert_eq!(Retries::::iter().count(), 0); + assert!(logger::log().is_empty()); + }); +} + +#[test] +fn set_retry_bad_origin() { + new_test_ext().execute_with(|| { + // task 42 at #4 with account 101 as origin + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + 101.into(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert!(Agenda::::get(4)[0].is_some()); + // try to change the retry config with a different (non-root) account + let res: Result<(), DispatchError> = + Scheduler::set_retry(RuntimeOrigin::signed(102), (4, 0), 10, 2); + assert_eq!(res, Err(BadOrigin.into())); + }); +} + +#[test] +fn set_named_retry_bad_origin() { + new_test_ext().execute_with(|| { + // task 42 at #4 with account 101 as origin + assert_ok!(Scheduler::do_schedule_named( + [42u8; 32], + DispatchTime::At(4), + None, + 127, + 101.into(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert!(Agenda::::get(4)[0].is_some()); + // try to change the retry config with a different (non-root) account + let res: Result<(), DispatchError> = + Scheduler::set_retry_named(RuntimeOrigin::signed(102), [42u8; 32], 10, 2); + assert_eq!(res, Err(BadOrigin.into())); + }); +} + +#[test] +fn set_retry_works() { + new_test_ext().execute_with(|| { + // task 42 at #4 + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert!(Agenda::::get(4)[0].is_some()); + // make sure the retry configuration was stored + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 2)); + assert_eq!( + Retries::::get((4, 0)), + Some(RetryConfig { total_retries: 10, remaining: 10, period: 2 }) + ); + }); +} + +#[test] +fn set_named_retry_works() { + new_test_ext().execute_with(|| { + // task 42 at #4 with account 101 as origin + assert_ok!(Scheduler::do_schedule_named( + [42u8; 32], + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert!(Agenda::::get(4)[0].is_some()); + // make sure the retry configuration was stored + assert_ok!(Scheduler::set_retry_named(root().into(), [42u8; 32], 10, 2)); + let address = Lookup::::get([42u8; 32]).unwrap(); + assert_eq!( + Retries::::get(address), + Some(RetryConfig { total_retries: 10, remaining: 10, period: 2 }) + ); + }); +} + +#[test] +fn retry_periodic_full_cycle() { + new_test_ext().execute_with(|| { + // tasks fail after we pass block 1000 + Threshold::::put((1, 1000)); + // task 42 at #4, every 100 blocks, 4 times + assert_ok!(Scheduler::do_schedule_named( + [42u8; 32], + DispatchTime::At(10), + Some((100, 4)), + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert!(Agenda::::get(10)[0].is_some()); + // 42 will be retried 2 times every block + assert_ok!(Scheduler::set_retry_named(root().into(), [42u8; 32], 2, 1)); + assert_eq!(Retries::::iter().count(), 1); + run_to_block(9); + assert!(logger::log().is_empty()); + assert!(Agenda::::get(10)[0].is_some()); + // 42 runs successfully once, it will run again at block 110 + run_to_block(10); + assert!(Agenda::::get(10).is_empty()); + assert!(Agenda::::get(110)[0].is_some()); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // nothing changed + run_to_block(109); + assert!(Agenda::::get(110)[0].is_some()); + // original task still has 2 remaining retries + assert_eq!(Retries::::get((110, 0)).unwrap().remaining, 2); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // make 42 fail next block + Threshold::::put((1, 2)); + // 42 will fail because we're outside the set threshold (block number in `1..2`), so it + // should be retried next block (at block 111) + run_to_block(110); + // should be queued for the normal period of 100 blocks + assert!(Agenda::::get(210)[0].is_some()); + // should also be queued to be retried next block + assert!(Agenda::::get(111)[0].is_some()); + // 42 retry clone has consumed one retry attempt + assert_eq!(Retries::::get((111, 0)).unwrap().remaining, 1); + // 42 original task still has the original remaining attempts + assert_eq!(Retries::::get((210, 0)).unwrap().remaining, 2); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // 42 retry will fail again + run_to_block(111); + // should still be queued for the normal period + assert!(Agenda::::get(210)[0].is_some()); + // should be queued to be retried next block + assert!(Agenda::::get(112)[0].is_some()); + // 42 has consumed another retry attempt + assert_eq!(Retries::::get((210, 0)).unwrap().remaining, 2); + assert_eq!(Retries::::get((112, 0)).unwrap().remaining, 0); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // 42 retry will fail again + run_to_block(112); + // should still be queued for the normal period + assert!(Agenda::::get(210)[0].is_some()); + // 42 retry clone ran out of retries, must have been evicted + assert_eq!(Agenda::::iter().count(), 1); + + // advance + run_to_block(209); + // should still be queued for the normal period + assert!(Agenda::::get(210)[0].is_some()); + // 42 retry clone ran out of retries, must have been evicted + assert_eq!(Agenda::::iter().count(), 1); + // 42 should fail again and should spawn another retry clone + run_to_block(210); + // should be queued for the normal period of 100 blocks + assert!(Agenda::::get(310)[0].is_some()); + // should also be queued to be retried next block + assert!(Agenda::::get(211)[0].is_some()); + // 42 retry clone has consumed one retry attempt + assert_eq!(Retries::::get((211, 0)).unwrap().remaining, 1); + // 42 original task still has the original remaining attempts + assert_eq!(Retries::::get((310, 0)).unwrap().remaining, 2); + assert_eq!(Retries::::iter().count(), 2); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // make 42 run successfully again + Threshold::::put((1, 1000)); + // 42 retry clone should now succeed + run_to_block(211); + // should be queued for the normal period of 100 blocks + assert!(Agenda::::get(310)[0].is_some()); + // retry was successful, retry task should have been discarded + assert_eq!(Agenda::::iter().count(), 1); + // 42 original task still has the original remaining attempts + assert_eq!(Retries::::get((310, 0)).unwrap().remaining, 2); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]); + + // fast forward to the last periodic run of 42 + run_to_block(310); + // 42 was successful, the period ended as this was the 4th scheduled periodic run so 42 must + // have been discarded + assert_eq!(Agenda::::iter().count(), 0); + // agenda is empty so no retries should exist + assert_eq!(Retries::::iter().count(), 0); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]); + }); +} + #[test] fn reschedule_works() { new_test_ext().execute_with(|| { @@ -430,6 +1290,117 @@ fn scheduler_respects_weight_limits() { }); } +#[test] +fn retry_respects_weight_limits() { + let max_weight: Weight = ::MaximumWeight::get(); + new_test_ext().execute_with(|| { + // schedule 42 + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 3 * 2 }); + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(8), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + )); + // schedule 20 with a call that will fail until we reach block 8 + Threshold::::put((8, 100)); + let call = RuntimeCall::Logger(LoggerCall::timed_log { i: 20, weight: max_weight / 3 * 2 }); + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + )); + // set a retry config for 20 for 10 retries every block + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 1)); + // 20 should fail and be retried later + run_to_block(4); + assert!(Agenda::::get(5)[0].is_some()); + assert!(Agenda::::get(8)[0].is_some()); + assert_eq!(Retries::::iter().count(), 1); + assert!(logger::log().is_empty()); + // 20 still fails but is scheduled next block together with 42 + run_to_block(7); + assert_eq!(Agenda::::get(8).len(), 2); + assert_eq!(Retries::::iter().count(), 1); + assert!(logger::log().is_empty()); + // 20 and 42 do not fit together + // 42 is executed as it was first in the queue + // 20 is still on the 8th block's agenda + run_to_block(8); + assert!(Agenda::::get(8)[0].is_none()); + assert!(Agenda::::get(8)[1].is_some()); + assert_eq!(Retries::::iter().count(), 1); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // 20 is executed and the schedule is cleared + run_to_block(9); + assert_eq!(Agenda::::iter().count(), 0); + assert_eq!(Retries::::iter().count(), 0); + assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 20u32)]); + }); +} + +#[test] +fn try_schedule_retry_respects_weight_limits() { + let max_weight: Weight = ::MaximumWeight::get(); + new_test_ext().execute_with(|| { + let service_agendas_weight = ::WeightInfo::service_agendas_base(); + let service_agenda_weight = ::WeightInfo::service_agenda_base( + ::MaxScheduledPerBlock::get(), + ); + let actual_service_agenda_weight = ::WeightInfo::service_agenda_base(1); + // Some weight for `service_agenda` will be refunded, so we need to make sure the weight + // `try_schedule_retry` is going to ask for is greater than this difference, and we take a + // safety factor of 10 to make sure we're over that limit. + let meter = WeightMeter::with_limit( + ::WeightInfo::schedule_retry( + ::MaxScheduledPerBlock::get(), + ) / 10, + ); + assert!(meter.can_consume(service_agenda_weight - actual_service_agenda_weight)); + + let reference_call = + RuntimeCall::Logger(LoggerCall::timed_log { i: 20, weight: max_weight / 3 * 2 }); + let bounded = ::Preimages::bound(reference_call).unwrap(); + let base_weight = ::WeightInfo::service_task( + bounded.lookup_len().map(|x| x as usize), + false, + false, + ); + // we make the call cost enough so that all checks have enough weight to run aside from + // `try_schedule_retry` + let call_weight = max_weight - service_agendas_weight - service_agenda_weight - base_weight; + let call = RuntimeCall::Logger(LoggerCall::timed_log { i: 20, weight: call_weight }); + // schedule 20 with a call that will fail until we reach block 8 + Threshold::::put((8, 100)); + + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + )); + // set a retry config for 20 for 10 retries every block + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 1)); + // 20 should fail and, because of insufficient weight, it should not be scheduled again + run_to_block(4); + // nothing else should be scheduled + assert_eq!(Agenda::::iter().count(), 0); + assert_eq!(Retries::::iter().count(), 0); + assert_eq!(logger::log(), vec![]); + // check the `RetryFailed` event happened + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = + Event::RetryFailed { task: (4, 0), id: None }.into(); + // compare to the last event record + let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); + }); +} + /// Permanently overweight calls are not deleted but also not executed. #[test] fn scheduler_does_not_delete_permanently_overweight_call() { @@ -877,6 +1848,134 @@ fn should_check_origin_for_cancel() { }); } +#[test] +fn cancel_removes_retry_entry() { + new_test_ext().execute_with(|| { + // task fails until block 99 is reached + Threshold::::put((99, 100)); + // task 20 at #4 + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 20, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + // named task 42 at #4 + assert_ok!(Scheduler::do_schedule_named( + [1u8; 32], + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert_eq!(Agenda::::get(4).len(), 2); + // task 20 will be retried 3 times every block + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 1)); + // task 42 will be retried 10 times every 3 blocks + assert_ok!(Scheduler::set_retry_named(root().into(), [1u8; 32], 10, 1)); + assert_eq!(Retries::::iter().count(), 2); + run_to_block(3); + assert!(logger::log().is_empty()); + assert_eq!(Agenda::::get(4).len(), 2); + // both tasks fail + run_to_block(4); + assert!(Agenda::::get(4).is_empty()); + // 42 and 20 are rescheduled for next block + assert_eq!(Agenda::::get(5).len(), 2); + assert!(logger::log().is_empty()); + // 42 and 20 still fail + run_to_block(5); + // 42 and 20 rescheduled for next block + assert_eq!(Agenda::::get(6).len(), 2); + assert_eq!(Retries::::iter().count(), 2); + assert!(logger::log().is_empty()); + + // even though 42 is being retried, the tasks scheduled for retries are not named + assert_eq!(Lookup::::iter().count(), 0); + assert!(Scheduler::cancel(root().into(), 6, 0).is_ok()); + + // 20 is removed, 42 still fails + run_to_block(6); + // 42 rescheduled for next block + assert_eq!(Agenda::::get(7).len(), 1); + // 20's retry entry is removed + assert!(!Retries::::contains_key((4, 0))); + assert_eq!(Retries::::iter().count(), 1); + assert!(logger::log().is_empty()); + + assert!(Scheduler::cancel(root().into(), 7, 0).is_ok()); + + // both tasks are canceled, everything is removed now + run_to_block(7); + assert!(Agenda::::get(8).is_empty()); + assert_eq!(Retries::::iter().count(), 0); + }); +} + +#[test] +fn cancel_retries_works() { + new_test_ext().execute_with(|| { + // task fails until block 99 is reached + Threshold::::put((99, 100)); + // task 20 at #4 + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 20, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + // named task 42 at #4 + assert_ok!(Scheduler::do_schedule_named( + [1u8; 32], + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(RuntimeCall::Logger(logger::Call::timed_log { + i: 42, + weight: Weight::from_parts(10, 0) + })) + .unwrap() + )); + + assert_eq!(Agenda::::get(4).len(), 2); + // task 20 will be retried 3 times every block + assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 1)); + // task 42 will be retried 10 times every 3 blocks + assert_ok!(Scheduler::set_retry_named(root().into(), [1u8; 32], 10, 1)); + assert_eq!(Retries::::iter().count(), 2); + run_to_block(3); + assert!(logger::log().is_empty()); + assert_eq!(Agenda::::get(4).len(), 2); + // cancel the retry config for 20 + assert_ok!(Scheduler::cancel_retry(root().into(), (4, 0))); + assert_eq!(Retries::::iter().count(), 1); + // cancel the retry config for 42 + assert_ok!(Scheduler::cancel_retry_named(root().into(), [1u8; 32])); + assert_eq!(Retries::::iter().count(), 0); + run_to_block(4); + // both tasks failed and there are no more retries, so they are evicted + assert_eq!(Agenda::::get(4).len(), 0); + assert_eq!(Retries::::iter().count(), 0); + }); +} + #[test] fn migration_to_v4_works() { new_test_ext().execute_with(|| { @@ -1054,6 +2153,8 @@ fn test_migrate_origin() { match self { 3u32 => system::RawOrigin::Root.into(), 2u32 => system::RawOrigin::None.into(), + 101u32 => system::RawOrigin::Signed(101).into(), + 102u32 => system::RawOrigin::Signed(102).into(), _ => unreachable!("test make no use of it"), } } diff --git a/substrate/frame/scheduler/src/weights.rs b/substrate/frame/scheduler/src/weights.rs index 58d711862591d033b66251def364acd5ebbac7a7..6c98145d266fdd7603cbd625ef55968013b3cd32 100644 --- a/substrate/frame/scheduler/src/weights.rs +++ b/substrate/frame/scheduler/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_scheduler +//! Autogenerated weights for `pallet_scheduler` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/scheduler/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/scheduler/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_scheduler. +/// Weight functions needed for `pallet_scheduler`. pub trait WeightInfo { fn service_agendas_base() -> Weight; fn service_agenda_base(s: u32, ) -> Weight; @@ -64,33 +63,38 @@ pub trait WeightInfo { fn cancel(s: u32, ) -> Weight; fn schedule_named(s: u32, ) -> Weight; fn cancel_named(s: u32, ) -> Weight; + fn schedule_retry(s: u32, ) -> Weight; + fn set_retry() -> Weight; + fn set_retry_named() -> Weight; + fn cancel_retry() -> Weight; + fn cancel_retry_named() -> Weight; } -/// Weights for pallet_scheduler using the Substrate node and recommended hardware. +/// Weights for `pallet_scheduler` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Scheduler IncompleteSince (r:1 w:1) - /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Scheduler::IncompleteSince` (r:1 w:1) + /// Proof: `Scheduler::IncompleteSince` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn service_agendas_base() -> Weight { // Proof Size summary in bytes: // Measured: `31` // Estimated: `1489` - // Minimum execution time: 3_991_000 picoseconds. - Weight::from_parts(4_174_000, 1489) + // Minimum execution time: 3_128_000 picoseconds. + Weight::from_parts(3_372_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 512]`. fn service_agenda_base(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 3_581_000 picoseconds. - Weight::from_parts(7_413_174, 110487) - // Standard Error: 971 - .saturating_add(Weight::from_parts(348_077, 0).saturating_mul(s.into())) + // Minimum execution time: 3_560_000 picoseconds. + Weight::from_parts(6_356_795, 110487) + // Standard Error: 493 + .saturating_add(Weight::from_parts(315_098, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -98,145 +102,228 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(5_549_000, 0) + // Minimum execution time: 3_501_000 picoseconds. + Weight::from_parts(3_722_000, 0) } - /// Storage: Preimage PreimageFor (r:1 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `179 + s * (1 ±0)` - // Estimated: `3644 + s * (1 ±0)` - // Minimum execution time: 20_089_000 picoseconds. - Weight::from_parts(20_376_000, 3644) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_170, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `246 + s * (1 ±0)` + // Estimated: `3711 + s * (1 ±0)` + // Minimum execution time: 17_976_000 picoseconds. + Weight::from_parts(18_137_000, 3711) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_173, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) } - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn service_task_named() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_998_000 picoseconds. - Weight::from_parts(7_303_000, 0) + // Minimum execution time: 4_935_000 picoseconds. + Weight::from_parts(5_133_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_078_000 picoseconds. - Weight::from_parts(5_315_000, 0) + // Minimum execution time: 3_467_000 picoseconds. + Weight::from_parts(3_654_000, 0) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_228_000 picoseconds. - Weight::from_parts(2_352_000, 0) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 6_528_000 picoseconds. + Weight::from_parts(6_820_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_226_000 picoseconds. - Weight::from_parts(2_371_000, 0) + // Minimum execution time: 2_202_000 picoseconds. + Weight::from_parts(2_360_000, 0) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 12_683_000 picoseconds. - Weight::from_parts(16_951_846, 110487) - // Standard Error: 1_046 - .saturating_add(Weight::from_parts(380_842, 0).saturating_mul(s.into())) + // Minimum execution time: 10_222_000 picoseconds. + Weight::from_parts(13_654_958, 110487) + // Standard Error: 676 + .saturating_add(Weight::from_parts(338_633, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 16_201_000 picoseconds. - Weight::from_parts(18_259_422, 110487) - // Standard Error: 1_344 - .saturating_add(Weight::from_parts(545_863, 0).saturating_mul(s.into())) + // Minimum execution time: 15_517_000 picoseconds. + Weight::from_parts(17_464_075, 110487) + // Standard Error: 952 + .saturating_add(Weight::from_parts(495_806, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `596 + s * (178 ±0)` // Estimated: `110487` - // Minimum execution time: 16_180_000 picoseconds. - Weight::from_parts(25_128_925, 110487) - // Standard Error: 1_118 - .saturating_add(Weight::from_parts(375_631, 0).saturating_mul(s.into())) + // Minimum execution time: 13_091_000 picoseconds. + Weight::from_parts(19_101_313, 110487) + // Standard Error: 662 + .saturating_add(Weight::from_parts(342_468, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `709 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 18_244_000 picoseconds. - Weight::from_parts(21_439_366, 110487) - // Standard Error: 1_084 - .saturating_add(Weight::from_parts(557_691, 0).saturating_mul(s.into())) + // Minimum execution time: 17_579_000 picoseconds. + Weight::from_parts(20_561_921, 110487) + // Standard Error: 792 + .saturating_add(Weight::from_parts(500_463, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 512]`. + fn schedule_retry(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `118` + // Estimated: `110487` + // Minimum execution time: 8_996_000 picoseconds. + Weight::from_parts(11_393_234, 110487) + // Standard Error: 190 + .saturating_add(Weight::from_parts(6_714, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `90705` + // Estimated: `110487` + // Minimum execution time: 121_505_000 picoseconds. + Weight::from_parts(124_306_000, 110487) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `91747` + // Estimated: `110487` + // Minimum execution time: 128_070_000 picoseconds. + Weight::from_parts(132_683_000, 110487) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `90717` + // Estimated: `110487` + // Minimum execution time: 118_260_000 picoseconds. + Weight::from_parts(119_722_000, 110487) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `91759` + // Estimated: `110487` + // Minimum execution time: 129_036_000 picoseconds. + Weight::from_parts(133_975_000, 110487) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Scheduler IncompleteSince (r:1 w:1) - /// Proof: Scheduler IncompleteSince (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `Scheduler::IncompleteSince` (r:1 w:1) + /// Proof: `Scheduler::IncompleteSince` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn service_agendas_base() -> Weight { // Proof Size summary in bytes: // Measured: `31` // Estimated: `1489` - // Minimum execution time: 3_991_000 picoseconds. - Weight::from_parts(4_174_000, 1489) + // Minimum execution time: 3_128_000 picoseconds. + Weight::from_parts(3_372_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 512]`. fn service_agenda_base(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 3_581_000 picoseconds. - Weight::from_parts(7_413_174, 110487) - // Standard Error: 971 - .saturating_add(Weight::from_parts(348_077, 0).saturating_mul(s.into())) + // Minimum execution time: 3_560_000 picoseconds. + Weight::from_parts(6_356_795, 110487) + // Standard Error: 493 + .saturating_add(Weight::from_parts(315_098, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -244,117 +331,200 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(5_549_000, 0) + // Minimum execution time: 3_501_000 picoseconds. + Weight::from_parts(3_722_000, 0) } - /// Storage: Preimage PreimageFor (r:1 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `s` is `[128, 4194304]`. fn service_task_fetched(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `179 + s * (1 ±0)` - // Estimated: `3644 + s * (1 ±0)` - // Minimum execution time: 20_089_000 picoseconds. - Weight::from_parts(20_376_000, 3644) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_170, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `246 + s * (1 ±0)` + // Estimated: `3711 + s * (1 ±0)` + // Minimum execution time: 17_976_000 picoseconds. + Weight::from_parts(18_137_000, 3711) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_173, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) } - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn service_task_named() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_998_000 picoseconds. - Weight::from_parts(7_303_000, 0) + // Minimum execution time: 4_935_000 picoseconds. + Weight::from_parts(5_133_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn service_task_periodic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_078_000 picoseconds. - Weight::from_parts(5_315_000, 0) + // Minimum execution time: 3_467_000 picoseconds. + Weight::from_parts(3_654_000, 0) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_228_000 picoseconds. - Weight::from_parts(2_352_000, 0) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 6_528_000 picoseconds. + Weight::from_parts(6_820_000, 3997) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_226_000 picoseconds. - Weight::from_parts(2_371_000, 0) + // Minimum execution time: 2_202_000 picoseconds. + Weight::from_parts(2_360_000, 0) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 12_683_000 picoseconds. - Weight::from_parts(16_951_846, 110487) - // Standard Error: 1_046 - .saturating_add(Weight::from_parts(380_842, 0).saturating_mul(s.into())) + // Minimum execution time: 10_222_000 picoseconds. + Weight::from_parts(13_654_958, 110487) + // Standard Error: 676 + .saturating_add(Weight::from_parts(338_633, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) - /// Storage: Scheduler Lookup (r:0 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:0 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `81 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 16_201_000 picoseconds. - Weight::from_parts(18_259_422, 110487) - // Standard Error: 1_344 - .saturating_add(Weight::from_parts(545_863, 0).saturating_mul(s.into())) + // Minimum execution time: 15_517_000 picoseconds. + Weight::from_parts(17_464_075, 110487) + // Standard Error: 952 + .saturating_add(Weight::from_parts(495_806, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `596 + s * (178 ±0)` // Estimated: `110487` - // Minimum execution time: 16_180_000 picoseconds. - Weight::from_parts(25_128_925, 110487) - // Standard Error: 1_118 - .saturating_add(Weight::from_parts(375_631, 0).saturating_mul(s.into())) + // Minimum execution time: 13_091_000 picoseconds. + Weight::from_parts(19_101_313, 110487) + // Standard Error: 662 + .saturating_add(Weight::from_parts(342_468, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Scheduler Lookup (r:1 w:1) - /// Proof: Scheduler Lookup (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `709 + s * (177 ±0)` // Estimated: `110487` - // Minimum execution time: 18_244_000 picoseconds. - Weight::from_parts(21_439_366, 110487) - // Standard Error: 1_084 - .saturating_add(Weight::from_parts(557_691, 0).saturating_mul(s.into())) + // Minimum execution time: 17_579_000 picoseconds. + Weight::from_parts(20_561_921, 110487) + // Standard Error: 792 + .saturating_add(Weight::from_parts(500_463, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + /// The range of component `s` is `[1, 512]`. + fn schedule_retry(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `118` + // Estimated: `110487` + // Minimum execution time: 8_996_000 picoseconds. + Weight::from_parts(11_393_234, 110487) + // Standard Error: 190 + .saturating_add(Weight::from_parts(6_714, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `90705` + // Estimated: `110487` + // Minimum execution time: 121_505_000 picoseconds. + Weight::from_parts(124_306_000, 110487) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn set_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `91747` + // Estimated: `110487` + // Minimum execution time: 128_070_000 picoseconds. + Weight::from_parts(132_683_000, 110487) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry() -> Weight { + // Proof Size summary in bytes: + // Measured: `90717` + // Estimated: `110487` + // Minimum execution time: 118_260_000 picoseconds. + Weight::from_parts(119_722_000, 110487) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Scheduler::Lookup` (r:1 w:0) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:0) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(107022), added: 109497, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) + fn cancel_retry_named() -> Weight { + // Proof Size summary in bytes: + // Measured: `91759` + // Estimated: `110487` + // Minimum execution time: 129_036_000 picoseconds. + Weight::from_parts(133_975_000, 110487) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/substrate/frame/scored-pool/Cargo.toml b/substrate/frame/scored-pool/Cargo.toml index 7a534ddd79d1a54264cb39493cae9c165068c9f5..ae6ade5189d0f51b683f35f846c5391a569b163a 100644 --- a/substrate/frame/scored-pool/Cargo.toml +++ b/substrate/frame/scored-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-scored-pool" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/scored-pool/src/mock.rs b/substrate/frame/scored-pool/src/mock.rs index 00818f4aad066590b23239b49d9c97d939803c17..6fba1bb3d5376ba13273b1133691537234791dca 100644 --- a/substrate/frame/scored-pool/src/mock.rs +++ b/substrate/frame/scored-pool/src/mock.rs @@ -25,12 +25,7 @@ use frame_support::{ traits::{ConstU32, ConstU64}, }; use frame_system::EnsureSignedBy; -use sp_core::H256; -use sp_runtime::{ - bounded_vec, - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::{bounded_vec, BuildStorage}; type Block = frame_system::mocking::MockBlock; @@ -53,29 +48,8 @@ ord_parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -92,7 +66,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { diff --git a/substrate/frame/session/Cargo.toml b/substrate/frame/session/Cargo.toml index 4589dbb427a01697fc9749df0f65ae04c80b637c..de041307f7022678235d98372bf7119eb4d48eaa 100644 --- a/substrate/frame/session/Cargo.toml +++ b/substrate/frame/session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-session" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/session/benchmarking/Cargo.toml b/substrate/frame/session/benchmarking/Cargo.toml index 16f85048d8d281366776205b15dccb304a7a1464..291fda3c4c7a4bfb56224593778288b7557daa95 100644 --- a/substrate/frame/session/benchmarking/Cargo.toml +++ b/substrate/frame/session/benchmarking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-session-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/session/benchmarking/src/mock.rs b/substrate/frame/session/benchmarking/src/mock.rs index c305641e185c61ce68c8fff94f10c5fdded6e58e..2ef8989f4b057da5baa34c5b6ec606fd0575861a 100644 --- a/substrate/frame/session/benchmarking/src/mock.rs +++ b/substrate/frame/session/benchmarking/src/mock.rs @@ -86,7 +86,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_timestamp::Config for Test { diff --git a/substrate/frame/session/src/historical/mod.rs b/substrate/frame/session/src/historical/mod.rs index d74e9dd0b7c54042840c62feff0693359f97c6c9..b9cecea1a7f7144fb7f846548b827455dce0a1e5 100644 --- a/substrate/frame/session/src/historical/mod.rs +++ b/substrate/frame/session/src/historical/mod.rs @@ -58,7 +58,7 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] diff --git a/substrate/frame/session/src/lib.rs b/substrate/frame/session/src/lib.rs index 178d43f596b2742af69d35437592d78bd030a63d..7d8128dbc1fe64c647994f6da901d4b3bd9e008f 100644 --- a/substrate/frame/session/src/lib.rs +++ b/substrate/frame/session/src/lib.rs @@ -368,7 +368,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] @@ -460,27 +460,13 @@ pub mod pallet { ); self.keys.iter().map(|x| x.1.clone()).collect() }); - assert!( - !initial_validators_0.is_empty(), - "Empty validator set for session 0 in genesis block!" - ); let initial_validators_1 = T::SessionManager::new_session_genesis(1) .unwrap_or_else(|| initial_validators_0.clone()); - assert!( - !initial_validators_1.is_empty(), - "Empty validator set for session 1 in genesis block!" - ); let queued_keys: Vec<_> = initial_validators_1 - .iter() - .cloned() - .map(|v| { - ( - v.clone(), - Pallet::::load_keys(&v).expect("Validator in session 1 missing keys!"), - ) - }) + .into_iter() + .filter_map(|v| Pallet::::load_keys(&v).map(|k| (v, k))) .collect(); // Tell everyone about the genesis session keys diff --git a/substrate/frame/session/src/migrations/v1.rs b/substrate/frame/session/src/migrations/v1.rs index 394a1f4a5227c2bc3bedf0d177e1f9ee88a89528..b6838099837a00a7f440cf8229fbdc2c9bd5b896 100644 --- a/substrate/frame/session/src/migrations/v1.rs +++ b/substrate/frame/session/src/migrations/v1.rs @@ -15,8 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::str; use sp_io::hashing::twox_128; -use sp_std::str; use frame_support::{ storage::{generator::StorageValue, StoragePrefixedMap}, diff --git a/substrate/frame/session/src/mock.rs b/substrate/frame/session/src/mock.rs index eb9eddfd847e6275363ff297671d4318fefd3283..89804f72cd6268314b28724329229882bf9a2103 100644 --- a/substrate/frame/session/src/mock.rs +++ b/substrate/frame/session/src/mock.rs @@ -24,20 +24,12 @@ use crate::historical as pallet_session_historical; use std::collections::BTreeMap; -use sp_core::{crypto::key_types::DUMMY, H256}; -use sp_runtime::{ - impl_opaque_keys, - testing::UintAuthorityId, - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_core::crypto::key_types::DUMMY; +use sp_runtime::{impl_opaque_keys, testing::UintAuthorityId, BuildStorage}; use sp_staking::SessionIndex; use sp_state_machine::BasicExternalities; -use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU32, ConstU64}, -}; +use frame_support::{derive_impl, parameter_types, traits::ConstU64}; impl_opaque_keys! { pub struct MockSessionKeys { @@ -234,29 +226,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_timestamp::Config for Test { diff --git a/substrate/frame/session/src/weights.rs b/substrate/frame/session/src/weights.rs index dd9848fd2c177913f9121f98ab269bdae7e0f40e..09eb665ff3d815b2fe7ebc1fb363c97364f93634 100644 --- a/substrate/frame/session/src/weights.rs +++ b/substrate/frame/session/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_session +//! Autogenerated weights for `pallet_session` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/session/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/session/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,77 +49,77 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_session. +/// Weight functions needed for `pallet_session`. pub trait WeightInfo { fn set_keys() -> Weight; fn purge_keys() -> Weight; } -/// Weights for pallet_session using the Substrate node and recommended hardware. +/// Weights for `pallet_session` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Session NextKeys (r:1 w:1) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session KeyOwner (r:4 w:4) - /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Session::NextKeys` (r:1 w:1) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::KeyOwner` (r:6 w:6) + /// Proof: `Session::KeyOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1924` - // Estimated: `12814` - // Minimum execution time: 55_459_000 picoseconds. - Weight::from_parts(56_180_000, 12814) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) + // Measured: `1919` + // Estimated: `17759` + // Minimum execution time: 57_921_000 picoseconds. + Weight::from_parts(58_960_000, 17759) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Session NextKeys (r:1 w:1) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session KeyOwner (r:0 w:4) - /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Session::NextKeys` (r:1 w:1) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::KeyOwner` (r:0 w:6) + /// Proof: `Session::KeyOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn purge_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1791` - // Estimated: `5256` - // Minimum execution time: 40_194_000 picoseconds. - Weight::from_parts(41_313_000, 5256) + // Measured: `1817` + // Estimated: `5282` + // Minimum execution time: 40_983_000 picoseconds. + Weight::from_parts(42_700_000, 5282) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Session NextKeys (r:1 w:1) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session KeyOwner (r:4 w:4) - /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Session::NextKeys` (r:1 w:1) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::KeyOwner` (r:6 w:6) + /// Proof: `Session::KeyOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1924` - // Estimated: `12814` - // Minimum execution time: 55_459_000 picoseconds. - Weight::from_parts(56_180_000, 12814) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) + // Measured: `1919` + // Estimated: `17759` + // Minimum execution time: 57_921_000 picoseconds. + Weight::from_parts(58_960_000, 17759) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } - /// Storage: Staking Ledger (r:1 w:0) - /// Proof: Staking Ledger (max_values: None, max_size: Some(1091), added: 3566, mode: MaxEncodedLen) - /// Storage: Session NextKeys (r:1 w:1) - /// Proof Skipped: Session NextKeys (max_values: None, max_size: None, mode: Measured) - /// Storage: Session KeyOwner (r:0 w:4) - /// Proof Skipped: Session KeyOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Session::NextKeys` (r:1 w:1) + /// Proof: `Session::NextKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::KeyOwner` (r:0 w:6) + /// Proof: `Session::KeyOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn purge_keys() -> Weight { // Proof Size summary in bytes: - // Measured: `1791` - // Estimated: `5256` - // Minimum execution time: 40_194_000 picoseconds. - Weight::from_parts(41_313_000, 5256) + // Measured: `1817` + // Estimated: `5282` + // Minimum execution time: 40_983_000 picoseconds. + Weight::from_parts(42_700_000, 5282) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } } diff --git a/substrate/frame/society/Cargo.toml b/substrate/frame/society/Cargo.toml index 46b4f7a7d6621b1d44d3edc92968168d9960fa2f..3dab082b3954b7bc1f5e20463c6df5192305550b 100644 --- a/substrate/frame/society/Cargo.toml +++ b/substrate/frame/society/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-society" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = { version = "0.4.17", default-features = false } +log = { workspace = true } rand_chacha = { version = "0.2", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } @@ -33,6 +33,7 @@ frame-system = { path = "../system", default-features = false } frame-support-test = { path = "../support/test" } pallet-balances = { path = "../balances" } sp-core = { path = "../../primitives/core" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } sp-io = { path = "../../primitives/io" } [features] diff --git a/substrate/frame/society/src/migrations.rs b/substrate/frame/society/src/migrations.rs index a995c9d7be7f2a1c46fee6d222862fcd1f03ae58..6a1029114519db8c41c56301197d04de26bfb6c8 100644 --- a/substrate/frame/society/src/migrations.rs +++ b/substrate/frame/society/src/migrations.rs @@ -29,7 +29,7 @@ const TARGET: &'static str = "runtime::society::migration"; /// This migration moves all the state to v2 of Society. pub struct VersionUncheckedMigrateToV2, I: 'static, PastPayouts>( - sp_std::marker::PhantomData<(T, I, PastPayouts)>, + core::marker::PhantomData<(T, I, PastPayouts)>, ); impl< @@ -40,11 +40,11 @@ impl< { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { - let current = Pallet::::current_storage_version(); - let onchain = Pallet::::on_chain_storage_version(); - ensure!(onchain == 0 && current == 2, "pallet_society: invalid version"); + let in_code = Pallet::::in_code_storage_version(); + let on_chain = Pallet::::on_chain_storage_version(); + ensure!(on_chain == 0 && in_code == 2, "pallet_society: invalid version"); - Ok((old::Candidates::::get(), old::Members::::get()).encode()) + Ok((v0::Candidates::::get(), v0::Members::::get()).encode()) } fn on_runtime_upgrade() -> Weight { @@ -103,7 +103,7 @@ pub type MigrateToV2 = frame_support::migrations::VersionedMi ::DbWeight, >; -pub(crate) mod old { +pub(crate) mod v0 { use super::*; use frame_support::storage_alias; @@ -230,37 +230,37 @@ pub fn assert_internal_consistency, I: Instance + 'static>() { } // We don't use these - make sure they don't exist. - assert_eq!(old::SuspendedCandidates::::iter().count(), 0); - assert_eq!(old::Strikes::::iter().count(), 0); - assert_eq!(old::Vouching::::iter().count(), 0); - assert!(!old::Defender::::exists()); - assert!(!old::Members::::exists()); + assert_eq!(v0::SuspendedCandidates::::iter().count(), 0); + assert_eq!(v0::Strikes::::iter().count(), 0); + assert_eq!(v0::Vouching::::iter().count(), 0); + assert!(!v0::Defender::::exists()); + assert!(!v0::Members::::exists()); } pub fn from_original, I: Instance + 'static>( past_payouts: &mut [(::AccountId, BalanceOf)], ) -> Result { // Migrate Bids from old::Bids (just a trunctation). - Bids::::put(BoundedVec::<_, T::MaxBids>::truncate_from(old::Bids::::take())); + Bids::::put(BoundedVec::<_, T::MaxBids>::truncate_from(v0::Bids::::take())); // Initialise round counter. RoundCount::::put(0); // Migrate Candidates from old::Candidates - for Bid { who: candidate, kind, value } in old::Candidates::::take().into_iter() { + for Bid { who: candidate, kind, value } in v0::Candidates::::take().into_iter() { let mut tally = Tally::default(); // Migrate Votes from old::Votes // No need to drain, since we're overwriting values. - for (voter, vote) in old::Votes::::iter_prefix(&candidate) { + for (voter, vote) in v0::Votes::::iter_prefix(&candidate) { Votes::::insert( &candidate, &voter, - Vote { approve: vote == old::Vote::Approve, weight: 1 }, + Vote { approve: vote == v0::Vote::Approve, weight: 1 }, ); match vote { - old::Vote::Approve => tally.approvals.saturating_inc(), - old::Vote::Reject => tally.rejections.saturating_inc(), - old::Vote::Skeptic => Skeptic::::put(&voter), + v0::Vote::Approve => tally.approvals.saturating_inc(), + v0::Vote::Reject => tally.rejections.saturating_inc(), + v0::Vote::Skeptic => Skeptic::::put(&voter), } } Candidates::::insert( @@ -271,9 +271,9 @@ pub fn from_original, I: Instance + 'static>( // Migrate Members from old::Members old::Strikes old::Vouching let mut member_count = 0; - for member in old::Members::::take() { - let strikes = old::Strikes::::take(&member); - let vouching = old::Vouching::::take(&member); + for member in v0::Members::::take() { + let strikes = v0::Strikes::::take(&member); + let vouching = v0::Vouching::::take(&member); let record = MemberRecord { index: member_count, rank: 0, strikes, vouching }; Members::::insert(&member, record); MemberByIndex::::insert(member_count, &member); @@ -314,7 +314,7 @@ pub fn from_original, I: Instance + 'static>( // Migrate Payouts from: old::Payouts and raw info (needed since we can't query old chain // state). past_payouts.sort(); - for (who, mut payouts) in old::Payouts::::iter() { + for (who, mut payouts) in v0::Payouts::::iter() { payouts.truncate(T::MaxPayouts::get() as usize); // ^^ Safe since we already truncated. let paid = past_payouts @@ -329,19 +329,19 @@ pub fn from_original, I: Instance + 'static>( } // Migrate SuspendedMembers from old::SuspendedMembers old::Strikes old::Vouching. - for who in old::SuspendedMembers::::iter_keys() { - let strikes = old::Strikes::::take(&who); - let vouching = old::Vouching::::take(&who); + for who in v0::SuspendedMembers::::iter_keys() { + let strikes = v0::Strikes::::take(&who); + let vouching = v0::Vouching::::take(&who); let record = MemberRecord { index: 0, rank: 0, strikes, vouching }; SuspendedMembers::::insert(&who, record); } // Any suspended candidates remaining are rejected. - let _ = old::SuspendedCandidates::::clear(u32::MAX, None); + let _ = v0::SuspendedCandidates::::clear(u32::MAX, None); // We give the current defender the benefit of the doubt. - old::Defender::::kill(); - let _ = old::DefenderVotes::::clear(u32::MAX, None); + v0::Defender::::kill(); + let _ = v0::DefenderVotes::::clear(u32::MAX, None); Ok(T::BlockWeights::get().max_block) } diff --git a/substrate/frame/society/src/mock.rs b/substrate/frame/society/src/mock.rs index 749d196b6e1b54607f2eaed033be536b0625c833..04f3e85f539aa94dc78142d7e9e5b27736dbc2af 100644 --- a/substrate/frame/society/src/mock.rs +++ b/substrate/frame/society/src/mock.rs @@ -26,11 +26,7 @@ use frame_support::{ }; use frame_support_test::TestRandomness; use frame_system::EnsureSignedBy; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::{traits::IdentityLookup, BuildStorage}; use RuntimeOrigin as Origin; @@ -60,46 +56,16 @@ ord_parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; type AccountId = u128; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type OnNewAccount = (); - type OnKilledAccount = (); type AccountData = pallet_balances::AccountData; - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; + type Lookup = IdentityLookup; } +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; type AccountStore = System; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeHoldReason = (); - type RuntimeFreezeReason = (); - type MaxHolds = (); } impl Config for Test { diff --git a/substrate/frame/society/src/tests.rs b/substrate/frame/society/src/tests.rs index 2163575a86080843e7061ae39a8ca123c05303cc..411567e1dedfde46450c0e778bcb14bb00883962 100644 --- a/substrate/frame/society/src/tests.rs +++ b/substrate/frame/society/src/tests.rs @@ -18,11 +18,11 @@ //! Tests for the module. use super::*; -use migrations::old; +use migrations::v0; use mock::*; use frame_support::{assert_noop, assert_ok}; -use sp_core::blake2_256; +use sp_crypto_hashing::blake2_256; use sp_runtime::traits::BadOrigin; use BidKind::*; use VouchingStatus::*; @@ -32,41 +32,41 @@ use RuntimeOrigin as Origin; #[test] fn migration_works() { EnvBuilder::new().founded(false).execute(|| { - use old::Vote::*; + use v0::Vote::*; // Initialise the old storage items. Founder::::put(10); Head::::put(30); - old::Members::::put(vec![10, 20, 30]); - old::Vouching::::insert(30, Vouching); - old::Vouching::::insert(40, Banned); - old::Strikes::::insert(20, 1); - old::Strikes::::insert(30, 2); - old::Strikes::::insert(40, 5); - old::Payouts::::insert(20, vec![(1, 1)]); - old::Payouts::::insert( + v0::Members::::put(vec![10, 20, 30]); + v0::Vouching::::insert(30, Vouching); + v0::Vouching::::insert(40, Banned); + v0::Strikes::::insert(20, 1); + v0::Strikes::::insert(30, 2); + v0::Strikes::::insert(40, 5); + v0::Payouts::::insert(20, vec![(1, 1)]); + v0::Payouts::::insert( 30, (0..=::MaxPayouts::get()) .map(|i| (i as u64, i as u64)) .collect::>(), ); - old::SuspendedMembers::::insert(40, true); + v0::SuspendedMembers::::insert(40, true); - old::Defender::::put(20); - old::DefenderVotes::::insert(10, Approve); - old::DefenderVotes::::insert(20, Approve); - old::DefenderVotes::::insert(30, Reject); + v0::Defender::::put(20); + v0::DefenderVotes::::insert(10, Approve); + v0::DefenderVotes::::insert(20, Approve); + v0::DefenderVotes::::insert(30, Reject); - old::SuspendedCandidates::::insert(50, (10, Deposit(100))); + v0::SuspendedCandidates::::insert(50, (10, Deposit(100))); - old::Candidates::::put(vec![ + v0::Candidates::::put(vec![ Bid { who: 60, kind: Deposit(100), value: 200 }, Bid { who: 70, kind: Vouch(30, 30), value: 100 }, ]); - old::Votes::::insert(60, 10, Approve); - old::Votes::::insert(70, 10, Reject); - old::Votes::::insert(70, 20, Approve); - old::Votes::::insert(70, 30, Approve); + v0::Votes::::insert(60, 10, Approve); + v0::Votes::::insert(70, 10, Reject); + v0::Votes::::insert(70, 20, Approve); + v0::Votes::::insert(70, 30, Approve); let bids = (0..=::MaxBids::get()) .map(|i| Bid { @@ -75,7 +75,7 @@ fn migration_works() { value: 10u64 + i as u64, }) .collect::>(); - old::Bids::::put(bids); + v0::Bids::::put(bids); migrations::from_original::(&mut [][..]).expect("migration failed"); migrations::assert_internal_consistency::(); diff --git a/substrate/frame/society/src/weights.rs b/substrate/frame/society/src/weights.rs index 7c59aed8449a21c72afb39bdd11e14a315d1c659..1245f76297216aea9bcc4c0c4b674f66d02a10c2 100644 --- a/substrate/frame/society/src/weights.rs +++ b/substrate/frame/society/src/weights.rs @@ -7,7 +7,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, @@ -15,35 +15,41 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_society +//! Autogenerated weights for `pallet_society` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-13, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev // --steps=50 // --repeat=20 // --pallet=pallet_society +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled -// --template=./.maintain/frame-weight-template.hbs -// --header=./HEADER-APACHE2 -// --output=./frame/society/src/weights.rs +// --heap-pages=4096 +// --output=./substrate/frame/society/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --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 sp_std::marker::PhantomData; +use core::marker::PhantomData; -/// Weight functions needed for pallet_society. +/// Weight functions needed for `pallet_society`. pub trait WeightInfo { fn bid() -> Weight; fn unbid() -> Weight; @@ -67,309 +73,739 @@ pub trait WeightInfo { fn cleanup_challenge() -> Weight; } -/// Weights for pallet_society using the Substrate node and recommended hardware. +/// Weights for `pallet_society` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Society Bids (r:1 w:1) - // Storage: Society Candidates (r:1 w:0) - // Storage: Society Members (r:1 w:0) - // Storage: Society SuspendedMembers (r:1 w:0) - // Storage: Society Parameters (r:1 w:0) + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:0) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::SuspendedMembers` (r:1 w:0) + /// Proof: `Society::SuspendedMembers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn bid() -> Weight { - Weight::zero() - } - // Storage: Society Bids (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `444` + // Estimated: `3909` + // Minimum execution time: 29_905_000 picoseconds. + Weight::from_parts(31_031_000, 3909) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn unbid() -> Weight { - Weight::zero() - } - // Storage: Society Bids (r:1 w:1) - // Storage: Society Candidates (r:1 w:0) - // Storage: Society Members (r:2 w:1) - // Storage: Society SuspendedMembers (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `461` + // Estimated: `1946` + // Minimum execution time: 23_038_000 picoseconds. + Weight::from_parts(23_904_000, 1946) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:0) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:2 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::SuspendedMembers` (r:1 w:0) + /// Proof: `Society::SuspendedMembers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn vouch() -> Weight { - Weight::zero() - } - // Storage: Society Bids (r:1 w:1) - // Storage: Society Members (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `481` + // Estimated: `6421` + // Minimum execution time: 21_197_000 picoseconds. + Weight::from_parts(22_043_000, 6421) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unvouch() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society Members (r:1 w:0) - // Storage: Society Votes (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `535` + // Estimated: `4000` + // Minimum execution time: 14_402_000 picoseconds. + Weight::from_parts(15_171_000, 4000) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:1 w:1) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn vote() -> Weight { - Weight::zero() - } - // Storage: Society Defending (r:1 w:1) - // Storage: Society Members (r:1 w:0) - // Storage: Society ChallengeRoundCount (r:1 w:0) - // Storage: Society DefenderVotes (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `569` + // Estimated: `4034` + // Minimum execution time: 21_930_000 picoseconds. + Weight::from_parts(22_666_000, 4034) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Defending` (r:1 w:1) + /// Proof: `Society::Defending` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::ChallengeRoundCount` (r:1 w:0) + /// Proof: `Society::ChallengeRoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::DefenderVotes` (r:1 w:1) + /// Proof: `Society::DefenderVotes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn defender_vote() -> Weight { - Weight::zero() - } - // Storage: Society Members (r:1 w:0) - // Storage: Society Payouts (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `561` + // Estimated: `4026` + // Minimum execution time: 18_821_000 picoseconds. + Weight::from_parts(19_633_000, 4026) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Payouts` (r:1 w:1) + /// Proof: `Society::Payouts` (`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`) fn payout() -> Weight { - Weight::zero() - } - // Storage: Society Members (r:1 w:1) - // Storage: Society Payouts (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `650` + // Estimated: `4115` + // Minimum execution time: 47_262_000 picoseconds. + Weight::from_parts(48_313_000, 4115) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Members` (r:1 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Payouts` (r:1 w:1) + /// Proof: `Society::Payouts` (`max_values`: None, `max_size`: None, mode: `Measured`) fn waive_repay() -> Weight { - Weight::zero() - } - // Storage: Society Head (r:1 w:1) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society MemberByIndex (r:0 w:1) - // Storage: Society Founder (r:0 w:1) - // Storage: Society Rules (r:0 w:1) - // Storage: Society Members (r:0 w:1) - // Storage: Society Parameters (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `547` + // Estimated: `4012` + // Minimum execution time: 18_189_000 picoseconds. + Weight::from_parts(19_038_000, 4012) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Head` (r:1 w:1) + /// Proof: `Society::Head` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberByIndex` (r:0 w:1) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Founder` (r:0 w:1) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Rules` (r:0 w:1) + /// Proof: `Society::Rules` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:0 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:0 w:1) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn found_society() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:1) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society Head (r:0 w:1) - // Storage: Society Defending (r:0 w:1) - // Storage: Society ChallengeRoundCount (r:0 w:1) - // Storage: Society MemberByIndex (r:0 w:5) - // Storage: Society Skeptic (r:0 w:1) - // Storage: Society Candidates (r:0 w:4) - // Storage: Society Pot (r:0 w:1) - // Storage: Society Rules (r:0 w:1) - // Storage: Society Votes (r:0 w:4) - // Storage: Society Members (r:0 w:5) - // Storage: Society RoundCount (r:0 w:1) - // Storage: Society Bids (r:0 w:1) - // Storage: Society Parameters (r:0 w:1) - // Storage: Society NextHead (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `1665` + // Minimum execution time: 14_815_000 picoseconds. + Weight::from_parts(15_426_000, 1665) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: `Society::Founder` (r:1 w:1) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:5 w:5) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberByIndex` (r:5 w:5) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:4 w:4) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:4 w:4) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Head` (r:0 w:1) + /// Proof: `Society::Head` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Defending` (r:0 w:1) + /// Proof: `Society::Defending` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::ChallengeRoundCount` (r:0 w:1) + /// Proof: `Society::ChallengeRoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Skeptic` (r:0 w:1) + /// Proof: `Society::Skeptic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Pot` (r:0 w:1) + /// Proof: `Society::Pot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Rules` (r:0 w:1) + /// Proof: `Society::Rules` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:0 w:1) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Bids` (r:0 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:0 w:1) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::NextHead` (r:0 w:1) + /// Proof: `Society::NextHead` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn dissolve() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society SuspendedMembers (r:1 w:1) - // Storage: Society Payouts (r:1 w:0) - // Storage: Society Pot (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `1654` + // Estimated: `15019` + // Minimum execution time: 57_787_000 picoseconds. + Weight::from_parts(59_489_000, 15019) + .saturating_add(T::DbWeight::get().reads(20_u64)) + .saturating_add(T::DbWeight::get().writes(30_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::SuspendedMembers` (r:1 w:1) + /// Proof: `Society::SuspendedMembers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Payouts` (r:1 w:0) + /// Proof: `Society::Payouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Pot` (r:1 w:1) + /// Proof: `Society::Pot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn judge_suspended_member() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society MemberCount (r:1 w:0) - // Storage: Society Parameters (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `3970` + // Minimum execution time: 19_262_000 picoseconds. + Weight::from_parts(19_752_000, 3970) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:0) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:0 w:1) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_parameters() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) - // Storage: Society Skeptic (r:1 w:0) - // Storage: Society Votes (r:1 w:0) - // Storage: Society Members (r:1 w:1) - // Storage: Society Parameters (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `387` + // Estimated: `1872` + // Minimum execution time: 10_656_000 picoseconds. + Weight::from_parts(11_063_000, 1872) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Skeptic` (r:1 w:0) + /// Proof: `Society::Skeptic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:1 w:0) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn punish_skeptic() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) - // Storage: Society Parameters (r:1 w:0) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society NextHead (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Society MemberByIndex (r:0 w:1) - // Storage: Society Members (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `636` + // Estimated: `4101` + // Minimum execution time: 22_837_000 picoseconds. + Weight::from_parts(23_738_000, 4101) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::NextHead` (r:1 w:1) + /// Proof: `Society::NextHead` (`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: `Society::MemberByIndex` (r:0 w:1) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:0 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) fn claim_membership() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) - // Storage: Society Parameters (r:1 w:0) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society NextHead (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Society MemberByIndex (r:0 w:1) - // Storage: Society Members (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `632` + // Estimated: `4097` + // Minimum execution time: 35_142_000 picoseconds. + Weight::from_parts(36_811_000, 4097) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::NextHead` (r:1 w:1) + /// Proof: `Society::NextHead` (`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: `Society::MemberByIndex` (r:0 w:1) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:0 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) fn bestow_membership() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `650` + // Estimated: `4115` + // Minimum execution time: 37_133_000 picoseconds. + Weight::from_parts(38_366_000, 4115) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`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`) fn kick_candidate() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `776` + // Estimated: `6196` + // Minimum execution time: 37_033_000 picoseconds. + Weight::from_parts(38_293_000, 6196) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`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`) fn resign_candidacy() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `746` + // Estimated: `6196` + // Minimum execution time: 35_203_000 picoseconds. + Weight::from_parts(36_252_000, 6196) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`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`) fn drop_candidate() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:0) - // Storage: Society VoteClearCursor (r:1 w:0) - // Storage: Society Votes (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `758` + // Estimated: `6196` + // Minimum execution time: 35_518_000 picoseconds. + Weight::from_parts(36_508_000, 6196) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:0) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::VoteClearCursor` (r:1 w:0) + /// Proof: `Society::VoteClearCursor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:2 w:2) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn cleanup_candidacy() -> Weight { - Weight::zero() - } - // Storage: Society ChallengeRoundCount (r:1 w:0) - // Storage: Society DefenderVotes (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 17_001_000 picoseconds. + Weight::from_parts(17_845_000, 6492) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Society::ChallengeRoundCount` (r:1 w:0) + /// Proof: `Society::ChallengeRoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::DefenderVotes` (r:1 w:1) + /// Proof: `Society::DefenderVotes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn cleanup_challenge() -> Weight { - Weight::zero() + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `3975` + // Minimum execution time: 11_583_000 picoseconds. + Weight::from_parts(12_134_000, 3975) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - // Storage: Society Bids (r:1 w:1) - // Storage: Society Candidates (r:1 w:0) - // Storage: Society Members (r:1 w:0) - // Storage: Society SuspendedMembers (r:1 w:0) - // Storage: Society Parameters (r:1 w:0) + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:0) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::SuspendedMembers` (r:1 w:0) + /// Proof: `Society::SuspendedMembers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn bid() -> Weight { - Weight::zero() - } - // Storage: Society Bids (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `444` + // Estimated: `3909` + // Minimum execution time: 29_905_000 picoseconds. + Weight::from_parts(31_031_000, 3909) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn unbid() -> Weight { - Weight::zero() - } - // Storage: Society Bids (r:1 w:1) - // Storage: Society Candidates (r:1 w:0) - // Storage: Society Members (r:2 w:1) - // Storage: Society SuspendedMembers (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `461` + // Estimated: `1946` + // Minimum execution time: 23_038_000 picoseconds. + Weight::from_parts(23_904_000, 1946) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:0) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:2 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::SuspendedMembers` (r:1 w:0) + /// Proof: `Society::SuspendedMembers` (`max_values`: None, `max_size`: None, mode: `Measured`) fn vouch() -> Weight { - Weight::zero() - } - // Storage: Society Bids (r:1 w:1) - // Storage: Society Members (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `481` + // Estimated: `6421` + // Minimum execution time: 21_197_000 picoseconds. + Weight::from_parts(22_043_000, 6421) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Bids` (r:1 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unvouch() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society Members (r:1 w:0) - // Storage: Society Votes (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `535` + // Estimated: `4000` + // Minimum execution time: 14_402_000 picoseconds. + Weight::from_parts(15_171_000, 4000) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:1 w:1) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn vote() -> Weight { - Weight::zero() - } - // Storage: Society Defending (r:1 w:1) - // Storage: Society Members (r:1 w:0) - // Storage: Society ChallengeRoundCount (r:1 w:0) - // Storage: Society DefenderVotes (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `569` + // Estimated: `4034` + // Minimum execution time: 21_930_000 picoseconds. + Weight::from_parts(22_666_000, 4034) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Defending` (r:1 w:1) + /// Proof: `Society::Defending` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::ChallengeRoundCount` (r:1 w:0) + /// Proof: `Society::ChallengeRoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::DefenderVotes` (r:1 w:1) + /// Proof: `Society::DefenderVotes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn defender_vote() -> Weight { - Weight::zero() - } - // Storage: Society Members (r:1 w:0) - // Storage: Society Payouts (r:1 w:1) - // Storage: System Account (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `561` + // Estimated: `4026` + // Minimum execution time: 18_821_000 picoseconds. + Weight::from_parts(19_633_000, 4026) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Members` (r:1 w:0) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Payouts` (r:1 w:1) + /// Proof: `Society::Payouts` (`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`) fn payout() -> Weight { - Weight::zero() - } - // Storage: Society Members (r:1 w:1) - // Storage: Society Payouts (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `650` + // Estimated: `4115` + // Minimum execution time: 47_262_000 picoseconds. + Weight::from_parts(48_313_000, 4115) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Members` (r:1 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Payouts` (r:1 w:1) + /// Proof: `Society::Payouts` (`max_values`: None, `max_size`: None, mode: `Measured`) fn waive_repay() -> Weight { - Weight::zero() - } - // Storage: Society Head (r:1 w:1) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society MemberByIndex (r:0 w:1) - // Storage: Society Founder (r:0 w:1) - // Storage: Society Rules (r:0 w:1) - // Storage: Society Members (r:0 w:1) - // Storage: Society Parameters (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `547` + // Estimated: `4012` + // Minimum execution time: 18_189_000 picoseconds. + Weight::from_parts(19_038_000, 4012) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Head` (r:1 w:1) + /// Proof: `Society::Head` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberByIndex` (r:0 w:1) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Founder` (r:0 w:1) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Rules` (r:0 w:1) + /// Proof: `Society::Rules` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:0 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:0 w:1) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn found_society() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:1) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society Head (r:0 w:1) - // Storage: Society Defending (r:0 w:1) - // Storage: Society ChallengeRoundCount (r:0 w:1) - // Storage: Society MemberByIndex (r:0 w:5) - // Storage: Society Skeptic (r:0 w:1) - // Storage: Society Candidates (r:0 w:4) - // Storage: Society Pot (r:0 w:1) - // Storage: Society Rules (r:0 w:1) - // Storage: Society Votes (r:0 w:4) - // Storage: Society Members (r:0 w:5) - // Storage: Society RoundCount (r:0 w:1) - // Storage: Society Bids (r:0 w:1) - // Storage: Society Parameters (r:0 w:1) - // Storage: Society NextHead (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `1665` + // Minimum execution time: 14_815_000 picoseconds. + Weight::from_parts(15_426_000, 1665) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: `Society::Founder` (r:1 w:1) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:5 w:5) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberByIndex` (r:5 w:5) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:4 w:4) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:4 w:4) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Head` (r:0 w:1) + /// Proof: `Society::Head` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Defending` (r:0 w:1) + /// Proof: `Society::Defending` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::ChallengeRoundCount` (r:0 w:1) + /// Proof: `Society::ChallengeRoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Skeptic` (r:0 w:1) + /// Proof: `Society::Skeptic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Pot` (r:0 w:1) + /// Proof: `Society::Pot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Rules` (r:0 w:1) + /// Proof: `Society::Rules` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:0 w:1) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Bids` (r:0 w:1) + /// Proof: `Society::Bids` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:0 w:1) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::NextHead` (r:0 w:1) + /// Proof: `Society::NextHead` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn dissolve() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society SuspendedMembers (r:1 w:1) - // Storage: Society Payouts (r:1 w:0) - // Storage: Society Pot (r:1 w:1) + // Proof Size summary in bytes: + // Measured: `1654` + // Estimated: `15019` + // Minimum execution time: 57_787_000 picoseconds. + Weight::from_parts(59_489_000, 15019) + .saturating_add(RocksDbWeight::get().reads(20_u64)) + .saturating_add(RocksDbWeight::get().writes(30_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::SuspendedMembers` (r:1 w:1) + /// Proof: `Society::SuspendedMembers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Payouts` (r:1 w:0) + /// Proof: `Society::Payouts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Pot` (r:1 w:1) + /// Proof: `Society::Pot` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn judge_suspended_member() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society MemberCount (r:1 w:0) - // Storage: Society Parameters (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `505` + // Estimated: `3970` + // Minimum execution time: 19_262_000 picoseconds. + Weight::from_parts(19_752_000, 3970) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:0) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:0 w:1) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_parameters() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) - // Storage: Society Skeptic (r:1 w:0) - // Storage: Society Votes (r:1 w:0) - // Storage: Society Members (r:1 w:1) - // Storage: Society Parameters (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `387` + // Estimated: `1872` + // Minimum execution time: 10_656_000 picoseconds. + Weight::from_parts(11_063_000, 1872) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Skeptic` (r:1 w:0) + /// Proof: `Society::Skeptic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:1 w:0) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:1 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn punish_skeptic() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) - // Storage: Society Parameters (r:1 w:0) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society NextHead (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Society MemberByIndex (r:0 w:1) - // Storage: Society Members (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `636` + // Estimated: `4101` + // Minimum execution time: 22_837_000 picoseconds. + Weight::from_parts(23_738_000, 4101) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::NextHead` (r:1 w:1) + /// Proof: `Society::NextHead` (`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: `Society::MemberByIndex` (r:0 w:1) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:0 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) fn claim_membership() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) - // Storage: Society Parameters (r:1 w:0) - // Storage: Society MemberCount (r:1 w:1) - // Storage: Society NextHead (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Society MemberByIndex (r:0 w:1) - // Storage: Society Members (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `632` + // Estimated: `4097` + // Minimum execution time: 35_142_000 picoseconds. + Weight::from_parts(36_811_000, 4097) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Parameters` (r:1 w:0) + /// Proof: `Society::Parameters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::MemberCount` (r:1 w:1) + /// Proof: `Society::MemberCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::NextHead` (r:1 w:1) + /// Proof: `Society::NextHead` (`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: `Society::MemberByIndex` (r:0 w:1) + /// Proof: `Society::MemberByIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Members` (r:0 w:1) + /// Proof: `Society::Members` (`max_values`: None, `max_size`: None, mode: `Measured`) fn bestow_membership() -> Weight { - Weight::zero() - } - // Storage: Society Founder (r:1 w:0) - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `650` + // Estimated: `4115` + // Minimum execution time: 37_133_000 picoseconds. + Weight::from_parts(38_366_000, 4115) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `Society::Founder` (r:1 w:0) + /// Proof: `Society::Founder` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`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`) fn kick_candidate() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `776` + // Estimated: `6196` + // Minimum execution time: 37_033_000 picoseconds. + Weight::from_parts(38_293_000, 6196) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`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`) fn resign_candidacy() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:1) - // Storage: Society RoundCount (r:1 w:0) + // Proof Size summary in bytes: + // Measured: `746` + // Estimated: `6196` + // Minimum execution time: 35_203_000 picoseconds. + Weight::from_parts(36_252_000, 6196) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:1) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::RoundCount` (r:1 w:0) + /// Proof: `Society::RoundCount` (`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`) fn drop_candidate() -> Weight { - Weight::zero() - } - // Storage: Society Candidates (r:1 w:0) - // Storage: Society VoteClearCursor (r:1 w:0) - // Storage: Society Votes (r:0 w:2) + // Proof Size summary in bytes: + // Measured: `758` + // Estimated: `6196` + // Minimum execution time: 35_518_000 picoseconds. + Weight::from_parts(36_508_000, 6196) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Society::Candidates` (r:1 w:0) + /// Proof: `Society::Candidates` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::VoteClearCursor` (r:1 w:0) + /// Proof: `Society::VoteClearCursor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Society::Votes` (r:2 w:2) + /// Proof: `Society::Votes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn cleanup_candidacy() -> Weight { - Weight::zero() - } - // Storage: Society ChallengeRoundCount (r:1 w:0) - // Storage: Society DefenderVotes (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 17_001_000 picoseconds. + Weight::from_parts(17_845_000, 6492) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Society::ChallengeRoundCount` (r:1 w:0) + /// Proof: `Society::ChallengeRoundCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Society::DefenderVotes` (r:1 w:1) + /// Proof: `Society::DefenderVotes` (`max_values`: None, `max_size`: None, mode: `Measured`) fn cleanup_challenge() -> Weight { - Weight::zero() + // Proof Size summary in bytes: + // Measured: `510` + // Estimated: `3975` + // Minimum execution time: 11_583_000 picoseconds. + Weight::from_parts(12_134_000, 3975) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index ddf4e7ea2c4fc3a4468ad2b07fdc196d280809aa..549e177491d32ffa18e390a2f83027d3152f0066 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -163,6 +163,12 @@ pub mod runtime { ConstU32, ConstU64, ConstU8, }; + /// Primary types used to parameterize `EnsureOrigin` and `EnsureRootWithArg`. + pub use frame_system::{ + EnsureNever, EnsureNone, EnsureRoot, EnsureRootWithSuccess, EnsureSigned, + EnsureSignedBy, + }; + /// Types to define your runtime version. pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion}; @@ -191,7 +197,7 @@ pub mod runtime { // Types often used in the runtime APIs. pub use sp_core::OpaqueMetadata; pub use sp_inherents::{CheckInherentsResult, InherentData}; - pub use sp_runtime::ApplyExtrinsicResult; + pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; pub use frame_system_rpc_runtime_api::*; pub use sp_api::{self, *}; @@ -243,8 +249,8 @@ pub mod runtime { /// The block type, which should be fed into [`frame_system::Config`]. /// - /// Should be parameterized with `T: frame_system::Config` and a tuple of `SignedExtension`. - /// When in doubt, use [`SystemSignedExtensionsOf`]. + /// Should be parameterized with `T: frame_system::Config` and a tuple of + /// `TransactionExtension`. When in doubt, use [`SystemTransactionExtensionsOf`]. // Note that this cannot be dependent on `T` for block-number because it would lead to a // circular dependency (self-referential generics). pub type BlockOf = generic::Block>; @@ -258,7 +264,7 @@ pub mod runtime { /// Default set of signed extensions exposed from the `frame_system`. /// /// crucially, this does NOT contain any tx-payment extension. - pub type SystemSignedExtensionsOf = ( + pub type SystemTransactionExtensionsOf = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -307,8 +313,8 @@ pub mod primitives { /// This is already part of the [`prelude`]. pub mod derive { pub use frame_support::{ - CloneNoBound, DebugNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, - RuntimeDebugNoBound, + CloneNoBound, DebugNoBound, DefaultNoBound, EqNoBound, OrdNoBound, PartialEqNoBound, + PartialOrdNoBound, RuntimeDebugNoBound, }; pub use parity_scale_codec::{Decode, Encode}; pub use scale_info::TypeInfo; diff --git a/substrate/frame/staking/Cargo.toml b/substrate/frame/staking/Cargo.toml index 7e933ed951eeff3e415b612f262e733d76f2f2e5..d2a46146931b8863ae347277db8076850dc76d68 100644 --- a/substrate/frame/staking/Cargo.toml +++ b/substrate/frame/staking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-staking" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"] } +serde = { features = ["alloc", "derive"], workspace = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } @@ -33,7 +33,7 @@ pallet-session = { path = "../session", default-features = false, features = [ pallet-authorship = { path = "../authorship", default-features = false } sp-application-crypto = { path = "../../primitives/application-crypto", default-features = false, features = ["serde"] } frame-election-provider-support = { path = "../election-provider-support", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } # Optional imports for benchmarking frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } diff --git a/substrate/frame/staking/reward-curve/Cargo.toml b/substrate/frame/staking/reward-curve/Cargo.toml index 35bf240907a8f6b815307c2efeab461608c91d45..e2a2782db2da1527eff9d9948020fb72bd02e370 100644 --- a/substrate/frame/staking/reward-curve/Cargo.toml +++ b/substrate/frame/staking/reward-curve/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-staking-reward-curve" -version = "4.0.0-dev" +version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -20,8 +20,8 @@ proc-macro = true [dependencies] proc-macro-crate = "3.0.0" proc-macro2 = "1.0.56" -quote = "1.0.28" -syn = { version = "2.0.48", features = ["full", "visit"] } +quote = { workspace = true } +syn = { features = ["full", "visit"], workspace = true } [dev-dependencies] sp-runtime = { path = "../../../primitives/runtime" } diff --git a/substrate/frame/staking/reward-fn/Cargo.toml b/substrate/frame/staking/reward-fn/Cargo.toml index 80a27cc0f5340cdfc849503ec0399f39062769f4..5169db5072e2fc80805605c3517d8c6e779e5620 100644 --- a/substrate/frame/staking/reward-fn/Cargo.toml +++ b/substrate/frame/staking/reward-fn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-staking-reward-fn" -version = "4.0.0-dev" +version = "19.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [lib] [dependencies] -log = { version = "0.4.17", default-features = false } +log = { workspace = true } sp-arithmetic = { path = "../../../primitives/arithmetic", default-features = false } [features] diff --git a/substrate/frame/staking/runtime-api/Cargo.toml b/substrate/frame/staking/runtime-api/Cargo.toml index b3fd4cfda017f2dff72aee122c44906d7db59d0e..50a19be92da8e6be4ddaf8fe29e649ee7b59c256 100644 --- a/substrate/frame/staking/runtime-api/Cargo.toml +++ b/substrate/frame/staking/runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-staking-runtime-api" -version = "4.0.0-dev" +version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,8 +17,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } -sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/staking" } +sp-api = { default-features = false, path = "../../../primitives/api" } +sp-staking = { default-features = false, path = "../../../primitives/staking" } [features] default = ["std"] diff --git a/substrate/frame/staking/src/benchmarking.rs b/substrate/frame/staking/src/benchmarking.rs index f1159c06aa11da252f4c448e47d8a968183ad93d..a83060873973cbaf568fc729535ce514cd90fcd6 100644 --- a/substrate/frame/staking/src/benchmarking.rs +++ b/substrate/frame/staking/src/benchmarking.rs @@ -176,7 +176,7 @@ impl ListScenario { let (origin_stash1, origin_controller1) = create_stash_controller_with_balance::( USER_SEED + 2, origin_weight, - Default::default(), + RewardDestination::Staked, )?; Staking::::nominate( RawOrigin::Signed(origin_controller1.clone()).into(), @@ -187,7 +187,7 @@ impl ListScenario { let (_origin_stash2, origin_controller2) = create_stash_controller_with_balance::( USER_SEED + 3, origin_weight, - Default::default(), + RewardDestination::Staked, )?; Staking::::nominate( RawOrigin::Signed(origin_controller2).into(), @@ -207,7 +207,7 @@ impl ListScenario { let (_dest_stash1, dest_controller1) = create_stash_controller_with_balance::( USER_SEED + 1, dest_weight, - Default::default(), + RewardDestination::Staked, )?; Staking::::nominate( RawOrigin::Signed(dest_controller1).into(), @@ -291,7 +291,7 @@ benchmarks! { withdraw_unbonded_update { // Slashing Spans let s in 0 .. MAX_SPANS; - let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; + let (stash, controller) = create_stash_controller::(0, 100, RewardDestination::Staked)?; add_slashing_spans::(&stash, s); let amount = T::Currency::minimum_balance() * 5u32.into(); // Half of total Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; @@ -340,7 +340,7 @@ benchmarks! { let (stash, controller) = create_stash_controller::( MaxNominationsOf::::get() - 1, 100, - Default::default(), + RewardDestination::Staked, )?; // because it is chilled. assert!(!T::VoterList::contains(&stash)); @@ -368,7 +368,7 @@ benchmarks! { let (stash, controller) = create_stash_controller::( MaxNominationsOf::::get() - 1, 100, - Default::default(), + RewardDestination::Staked, )?; let stash_lookup = T::Lookup::unlookup(stash.clone()); @@ -383,7 +383,7 @@ benchmarks! { let (n_stash, n_controller) = create_stash_controller::( MaxNominationsOf::::get() + i, 100, - Default::default(), + RewardDestination::Staked, )?; // bake the nominations; we first clone them from the rest of the validators. @@ -431,7 +431,7 @@ benchmarks! { let (stash, controller) = create_stash_controller_with_balance::( SEED + MaxNominationsOf::::get() + 1, // make sure the account does not conflict with others origin_weight, - Default::default(), + RewardDestination::Staked, ).unwrap(); assert!(!Nominators::::contains_key(&stash)); @@ -466,11 +466,11 @@ benchmarks! { set_payee { let (stash, controller) = create_stash_controller::(USER_SEED, 100, RewardDestination::Staked)?; - assert_eq!(Payee::::get(&stash), RewardDestination::Staked); + assert_eq!(Payee::::get(&stash), Some(RewardDestination::Staked)); whitelist_account!(controller); }: _(RawOrigin::Signed(controller.clone()), RewardDestination::Account(controller.clone())) verify { - assert_eq!(Payee::::get(&stash), RewardDestination::Account(controller)); + assert_eq!(Payee::::get(&stash), Some(RewardDestination::Account(controller))); } update_payee { @@ -482,7 +482,7 @@ benchmarks! { whitelist_account!(controller); }: _(RawOrigin::Signed(controller.clone()), controller.clone()) verify { - assert_eq!(Payee::::get(&stash), RewardDestination::Account(controller)); + assert_eq!(Payee::::get(&stash), Some(RewardDestination::Account(controller))); } set_controller { @@ -784,7 +784,7 @@ benchmarks! { #[extra] do_slash { let l in 1 .. T::MaxUnlockingChunks::get() as u32; - let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; + let (stash, controller) = create_stash_controller::(0, 100, RewardDestination::Staked)?; let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); let unlock_chunk = UnlockChunk::> { value: 1u32.into(), @@ -855,7 +855,8 @@ benchmarks! { ConfigOp::Set(u32::MAX), ConfigOp::Set(u32::MAX), ConfigOp::Set(Percent::max_value()), - ConfigOp::Set(Perbill::max_value()) + ConfigOp::Set(Perbill::max_value()), + ConfigOp::Set(Percent::max_value()) ) verify { assert_eq!(MinNominatorBond::::get(), BalanceOf::::max_value()); assert_eq!(MinValidatorBond::::get(), BalanceOf::::max_value()); @@ -863,6 +864,7 @@ benchmarks! { assert_eq!(MaxValidatorsCount::::get(), Some(u32::MAX)); assert_eq!(ChillThreshold::::get(), Some(Percent::from_percent(100))); assert_eq!(MinCommission::::get(), Perbill::from_percent(100)); + assert_eq!(MaxStakedRewards::::get(), Some(Percent::from_percent(100))); } set_staking_configs_all_remove { @@ -873,6 +875,7 @@ benchmarks! { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Remove, + ConfigOp::Remove, ConfigOp::Remove ) verify { assert!(!MinNominatorBond::::exists()); @@ -881,6 +884,7 @@ benchmarks! { assert!(!MaxValidatorsCount::::exists()); assert!(!ChillThreshold::::exists()); assert!(!MinCommission::::exists()); + assert!(!MaxStakedRewards::::exists()); } chill_other { @@ -904,6 +908,7 @@ benchmarks! { ConfigOp::Set(0), ConfigOp::Set(Percent::from_percent(0)), ConfigOp::Set(Zero::zero()), + ConfigOp::Noop, )?; let caller = whitelisted_caller(); diff --git a/substrate/frame/staking/src/election_size_tracker.rs b/substrate/frame/staking/src/election_size_tracker.rs index 283ae0140ee6894b19dce2f6bb67bbcc771029f0..36e7fa48fda5a48709d85ab5e53fc22eb6a61e89 100644 --- a/substrate/frame/staking/src/election_size_tracker.rs +++ b/substrate/frame/staking/src/election_size_tracker.rs @@ -84,7 +84,7 @@ use frame_election_provider_support::{ pub struct StaticTracker { pub size: usize, pub counter: usize, - _marker: sp_std::marker::PhantomData, + _marker: core::marker::PhantomData, } impl Default for StaticTracker { diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index 84bb4d167dcb0118ed0f933d044f76d74bc10560..5947adb9028b3a63b9f5b75eff4190c83749895f 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -126,7 +126,7 @@ impl StakingLedger { /// default reward destination. pub(crate) fn reward_destination( account: StakingAccount, - ) -> RewardDestination { + ) -> Option> { let stash = match account { StakingAccount::Stash(stash) => Some(stash), StakingAccount::Controller(controller) => @@ -137,7 +137,7 @@ impl StakingLedger { >::get(stash) } else { defensive!("fetched reward destination from unbonded stash {}", stash); - RewardDestination::default() + None } } diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 41cb2a12c3a32861c1d9f093c5126e58076221ad..5a92b6c855f2d044e671d714d612ad1753ee3e28 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -202,6 +202,12 @@ //! ```nocompile //! remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout //! ``` +//! +//! Note, however, that it is possible to set a cap on the total `staker_payout` for the era through +//! the `MaxStakersRewards` storage type. The `era_payout` implementor must ensure that the +//! `max_payout = remaining_payout + (staker_payout * max_stakers_rewards)`. The excess payout that +//! is not allocated for stakers is the era remaining reward. +//! //! The remaining reward is send to the configurable end-point [`Config::RewardRemainder`]. //! //! ### Reward Calculation @@ -407,12 +413,6 @@ pub enum RewardDestination { None, } -impl Default for RewardDestination { - fn default() -> Self { - RewardDestination::Staked - } -} - /// Preference of what happens regarding validation. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default, MaxEncodedLen)] pub struct ValidatorPrefs { @@ -903,8 +903,10 @@ impl EraPayout for () { /// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for /// backwards compatibility. pub struct ConvertCurve(sp_std::marker::PhantomData); -impl>> - EraPayout for ConvertCurve +impl EraPayout for ConvertCurve +where + Balance: AtLeast32BitUnsigned + Clone + Copy, + T: Get<&'static PiecewiseLinear<'static>>, { fn era_payout( total_staked: Balance, @@ -918,7 +920,7 @@ impl = StorageValue, ObsoleteReleases, Value pub mod v14 { use super::*; - pub struct MigrateToV14(sp_std::marker::PhantomData); + pub struct MigrateToV14(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV14 { fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let in_code = Pallet::::in_code_storage_version(); let on_chain = Pallet::::on_chain_storage_version(); - if current == 14 && on_chain == 13 { - current.put::>(); + if in_code == 14 && on_chain == 13 { + in_code.put::>(); log!(info, "v14 applied successfully."); T::DbWeight::get().reads_writes(1, 1) @@ -95,7 +95,7 @@ pub mod v14 { pub mod v13 { use super::*; - pub struct MigrateToV13(sp_std::marker::PhantomData); + pub struct MigrateToV13(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV13 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { @@ -108,12 +108,12 @@ pub mod v13 { } fn on_runtime_upgrade() -> Weight { - let current = Pallet::::current_storage_version(); + let in_code = Pallet::::in_code_storage_version(); let onchain = StorageVersion::::get(); - if current == 13 && onchain == ObsoleteReleases::V12_0_0 { + if in_code == 13 && onchain == ObsoleteReleases::V12_0_0 { StorageVersion::::kill(); - current.put::>(); + in_code.put::>(); log!(info, "v13 applied successfully"); T::DbWeight::get().reads_writes(1, 2) @@ -151,7 +151,7 @@ pub mod v12 { /// /// We will be depending on the configurable value of `T::HistoryDepth` post /// this release. - pub struct MigrateToV12(sp_std::marker::PhantomData); + pub struct MigrateToV12(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV12 { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { @@ -205,7 +205,7 @@ pub mod v11 { #[cfg(feature = "try-runtime")] use sp_io::hashing::twox_128; - pub struct MigrateToV11(sp_std::marker::PhantomData<(T, P, N)>); + pub struct MigrateToV11(core::marker::PhantomData<(T, P, N)>); impl> OnRuntimeUpgrade for MigrateToV11 { @@ -301,7 +301,7 @@ pub mod v10 { /// That means we might slash someone a bit too early, but we will definitely /// won't forget to slash them. The cap of 512 is somewhat randomly taken to /// prevent us from iterating over an arbitrary large number of keys `on_runtime_upgrade`. - pub struct MigrateToV10(sp_std::marker::PhantomData); + pub struct MigrateToV10(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV10 { fn on_runtime_upgrade() -> frame_support::weights::Weight { if StorageVersion::::get() == ObsoleteReleases::V9_0_0 { diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 5332dbfdd5b2d0b1203d71a72fc1e0698becd37f..24311cb9e78266240f2ded70fcacfe3b4f265d3f 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -31,14 +31,8 @@ use frame_support::{ weights::constants::RocksDbWeight, }; use frame_system::{EnsureRoot, EnsureSignedBy}; -use sp_core::H256; use sp_io; -use sp_runtime::{ - curve::PiecewiseLinear, - testing::UintAuthorityId, - traits::{IdentityLookup, Zero}, - BuildStorage, -}; +use sp_runtime::{curve::PiecewiseLinear, testing::UintAuthorityId, traits::Zero, BuildStorage}; use sp_staking::{ offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, OnStakingUpdate, @@ -49,7 +43,6 @@ pub const BLOCK_TIME: u64 = 1000; /// The AccountId alias in this test module. pub(crate) type AccountId = u64; -pub(crate) type Nonce = u64; pub(crate) type BlockNumber = u64; pub(crate) type Balance = u128; @@ -127,29 +120,9 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type Nonce = Nonce; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = frame_support::traits::ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; } impl pallet_balances::Config for Test { type MaxLocks = frame_support::traits::ConstU32<1024>; @@ -165,7 +138,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } sp_runtime::impl_opaque_keys! { @@ -345,6 +317,11 @@ where pub(crate) type StakingCall = crate::Call; pub(crate) type TestCall = ::RuntimeCall; +parameter_types! { + // if true, skips the try-state for the test running. + pub static SkipTryStateCheck: bool = false; +} + pub struct ExtBuilder { nominate: bool, validator_count: u32, @@ -454,6 +431,10 @@ impl ExtBuilder { self.balance_factor = factor; self } + pub fn try_state(self, enable: bool) -> Self { + SkipTryStateCheck::set(!enable); + self + } fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); let mut storage = frame_system::GenesisConfig::::default().build_storage().unwrap(); @@ -582,7 +563,9 @@ impl ExtBuilder { let mut ext = self.build(); ext.execute_with(test); ext.execute_with(|| { - Staking::do_try_state(System::block_number()).unwrap(); + if !SkipTryStateCheck::get() { + Staking::do_try_state(System::block_number()).unwrap(); + } }); } } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 80b4079dbd64feabcd53c2833ebb2af035f72137..757c46f4faf93d0e5ac79c21741774b2e36d2a89 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -36,12 +36,12 @@ use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use pallet_session::historical; use sp_runtime::{ traits::{Bounded, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero}, - Perbill, + Perbill, Percent, }; use sp_staking::{ currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, - EraIndex, Page, SessionIndex, Stake, + EraIndex, OnStakingUpdate, Page, SessionIndex, Stake, StakingAccount::{self, Controller, Stash}, StakingInterface, }; @@ -75,7 +75,7 @@ impl Pallet { StakingLedger::::get(account) } - pub fn payee(account: StakingAccount) -> RewardDestination { + pub fn payee(account: StakingAccount) -> Option> { StakingLedger::::reward_destination(account) } @@ -150,6 +150,9 @@ impl Pallet { // Already checked that this won't overflow by entry condition. let value = old_total.defensive_saturating_sub(new_total); Self::deposit_event(Event::::Withdrawn { stash, amount: value }); + + // notify listeners. + T::EventListeners::on_withdraw(controller, value); } Ok(used_weight) @@ -336,11 +339,10 @@ impl Pallet { if amount.is_zero() { return None } + let dest = Self::payee(StakingAccount::Stash(stash.clone()))?; - let dest = Self::payee(StakingAccount::Stash(stash.clone())); let maybe_imbalance = match dest { - RewardDestination::Stash => - T::Currency::deposit_into_existing(stash, amount).ok(), + RewardDestination::Stash => T::Currency::deposit_into_existing(stash, amount).ok(), RewardDestination::Staked => Self::ledger(Stash(stash.clone())) .and_then(|mut ledger| { ledger.active += amount; @@ -354,7 +356,7 @@ impl Pallet { Ok(r) }) .unwrap_or_default(), - RewardDestination::Account(dest_account) => + RewardDestination::Account(ref dest_account) => Some(T::Currency::deposit_creating(&dest_account, amount)), RewardDestination::None => None, #[allow(deprecated)] @@ -366,8 +368,7 @@ impl Pallet { T::Currency::deposit_creating(&controller, amount) }), }; - maybe_imbalance - .map(|imbalance| (imbalance, Self::payee(StakingAccount::Stash(stash.clone())))) + maybe_imbalance.map(|imbalance| (imbalance, dest)) } /// Plan a new session potentially trigger a new era. @@ -506,9 +507,18 @@ impl Pallet { .saturated_into::(); let staked = Self::eras_total_stake(&active_era.index); let issuance = T::Currency::total_issuance(); + let (validator_payout, remainder) = T::EraPayout::era_payout(staked, issuance, era_duration); + let total_payout = validator_payout.saturating_add(remainder); + let max_staked_rewards = + MaxStakedRewards::::get().unwrap_or(Percent::from_percent(100)); + + // apply cap to validators payout and add difference to remainder. + let validator_payout = validator_payout.min(max_staked_rewards * total_payout); + let remainder = total_payout.saturating_sub(validator_payout); + Self::deposit_event(Event::::EraPaid { era_index: active_era.index, validator_payout, @@ -1826,6 +1836,7 @@ impl Pallet { "VoterList contains non-staker" ); + Self::check_payees()?; Self::check_nominators()?; Self::check_exposures()?; Self::check_paged_exposures()?; @@ -1833,6 +1844,23 @@ impl Pallet { Self::check_count() } + /// Invariants: + /// * A bonded ledger should always have an assigned `Payee`. + /// * The number of entries in `Payee` and of bonded staking ledgers *must* match. + fn check_payees() -> Result<(), TryRuntimeError> { + for (stash, _) in Bonded::::iter() { + ensure!(Payee::::get(&stash).is_some(), "bonded ledger does not have payee set"); + } + + ensure!( + (Ledger::::iter().count() == Payee::::iter().count()) && + (Ledger::::iter().count() == Bonded::::iter().count()), + "number of entries in payee storage items does not match the number of bonded ledgers", + ); + + Ok(()) + } + fn check_count() -> Result<(), TryRuntimeError> { ensure!( ::VoterList::count() == diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index b914545a76b989069476f14360cb0314167ede94..992a7fe2c9c6218c7d132571a2f0cab0ee6618a4 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -66,7 +66,7 @@ pub mod pallet { use super::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(14); #[pallet::pallet] @@ -275,7 +275,7 @@ pub mod pallet { /// Something that listens to staking updates and performs actions based on the data it /// receives. /// - /// WARNING: this only reports slashing events for the time being. + /// WARNING: this only reports slashing and withdraw events for the time being. type EventListeners: sp_staking::OnStakingUpdate>; /// Some parameters of the benchmarking. @@ -339,7 +339,7 @@ pub mod pallet { /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] pub type Payee = - StorageMap<_, Twox64Concat, T::AccountId, RewardDestination, ValueQuery>; + StorageMap<_, Twox64Concat, T::AccountId, RewardDestination, OptionQuery>; /// The map from (wannabe) validator stash key to the preferences of that validator. /// @@ -564,6 +564,12 @@ pub mod pallet { #[pallet::getter(fn force_era)] pub type ForceEra = StorageValue<_, Forcing, ValueQuery>; + /// Maximum staked rewards, i.e. the percentage of the era inflation that + /// is used for stake rewards. + /// See [Era payout](./index.html#era-payout). + #[pallet::storage] + pub type MaxStakedRewards = StorageValue<_, Percent, OptionQuery>; + /// The percentage of the slash that is distributed to reporters. /// /// The rest of the slashed value is handled by the `Slash`. @@ -1717,6 +1723,7 @@ pub mod pallet { max_validator_count: ConfigOp, chill_threshold: ConfigOp, min_commission: ConfigOp, + max_staked_rewards: ConfigOp, ) -> DispatchResult { ensure_root(origin)?; @@ -1736,6 +1743,7 @@ pub mod pallet { config_op_exp!(MaxValidatorsCount, max_validator_count); config_op_exp!(ChillThreshold, chill_threshold); config_op_exp!(MinCommission, min_commission); + config_op_exp!(MaxStakedRewards, max_staked_rewards); Ok(()) } /// Declare a `controller` to stop participating as either a validator or nominator. @@ -1911,7 +1919,7 @@ pub mod pallet { ensure!( (Payee::::get(&ledger.stash) == { #[allow(deprecated)] - RewardDestination::Controller + Some(RewardDestination::Controller) }), Error::::NotController ); @@ -1948,7 +1956,7 @@ pub mod pallet { // `Controller` variant, skip deprecating this account. let payee_deprecated = Payee::::get(&ledger.stash) == { #[allow(deprecated)] - RewardDestination::Controller + Some(RewardDestination::Controller) }; if ledger.stash != *controller && !payee_deprecated { diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index f469ace0bc41118608d7ffd3c08c5d5b6703bfeb..3f4e28b1f6af0cdfac939cdc4e11ef5dcc1fd908 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -55,6 +55,7 @@ fn set_staking_configs_works() { ConfigOp::Set(10), ConfigOp::Set(20), ConfigOp::Set(Percent::from_percent(75)), + ConfigOp::Set(Zero::zero()), ConfigOp::Set(Zero::zero()) )); assert_eq!(MinNominatorBond::::get(), 1_500); @@ -63,6 +64,7 @@ fn set_staking_configs_works() { assert_eq!(MaxValidatorsCount::::get(), Some(20)); assert_eq!(ChillThreshold::::get(), Some(Percent::from_percent(75))); assert_eq!(MinCommission::::get(), Perbill::from_percent(0)); + assert_eq!(MaxStakedRewards::::get(), Some(Percent::from_percent(0))); // noop does nothing assert_storage_noop!(assert_ok!(Staking::set_staking_configs( @@ -72,6 +74,7 @@ fn set_staking_configs_works() { ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop, + ConfigOp::Noop, ConfigOp::Noop ))); @@ -83,6 +86,7 @@ fn set_staking_configs_works() { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Remove, + ConfigOp::Remove, ConfigOp::Remove )); assert_eq!(MinNominatorBond::::get(), 0); @@ -91,6 +95,7 @@ fn set_staking_configs_works() { assert_eq!(MaxValidatorsCount::::get(), None); assert_eq!(ChillThreshold::::get(), None); assert_eq!(MinCommission::::get(), Perbill::from_percent(0)); + assert_eq!(MaxStakedRewards::::get(), None); }); } @@ -771,12 +776,12 @@ fn double_staking_should_fail() { // * an account already bonded as stash cannot be be stashed again. // * an account already bonded as stash cannot nominate. // * an account already bonded as controller can nominate. - ExtBuilder::default().build_and_execute(|| { + ExtBuilder::default().try_state(false).build_and_execute(|| { let arbitrary_value = 5; let (stash, controller) = testing_utils::create_unique_stash_controller::( 0, arbitrary_value, - RewardDestination::default(), + RewardDestination::Staked, false, ) .unwrap(); @@ -786,7 +791,7 @@ fn double_staking_should_fail() { Staking::bond( RuntimeOrigin::signed(stash), arbitrary_value.into(), - RewardDestination::default() + RewardDestination::Staked, ), Error::::AlreadyBonded, ); @@ -805,12 +810,12 @@ fn double_controlling_attempt_should_fail() { // should test (in the same order): // * an account already bonded as controller CANNOT be reused as the controller of another // account. - ExtBuilder::default().build_and_execute(|| { + ExtBuilder::default().try_state(false).build_and_execute(|| { let arbitrary_value = 5; let (stash, _) = testing_utils::create_unique_stash_controller::( 0, arbitrary_value, - RewardDestination::default(), + RewardDestination::Staked, false, ) .unwrap(); @@ -820,7 +825,7 @@ fn double_controlling_attempt_should_fail() { Staking::bond( RuntimeOrigin::signed(stash), arbitrary_value.into(), - RewardDestination::default() + RewardDestination::Staked, ), Error::::AlreadyBonded, ); @@ -1068,8 +1073,8 @@ fn reward_destination_works() { mock::start_active_era(1); mock::make_all_reward_payment(0); - // Check that RewardDestination is Staked (default) - assert_eq!(Staking::payee(11.into()), RewardDestination::Staked); + // Check that RewardDestination is Staked + assert_eq!(Staking::payee(11.into()), Some(RewardDestination::Staked)); // Check that reward went to the stash account of validator assert_eq!(Balances::free_balance(11), 1000 + total_payout_0); // Check that amount at stake increased accordingly @@ -1098,7 +1103,7 @@ fn reward_destination_works() { mock::make_all_reward_payment(1); // Check that RewardDestination is Stash - assert_eq!(Staking::payee(11.into()), RewardDestination::Stash); + assert_eq!(Staking::payee(11.into()), Some(RewardDestination::Stash)); // Check that reward went to the stash account assert_eq!(Balances::free_balance(11), 1000 + total_payout_0 + total_payout_1); // Record this value @@ -1132,7 +1137,7 @@ fn reward_destination_works() { mock::make_all_reward_payment(2); // Check that RewardDestination is Account(11) - assert_eq!(Staking::payee(11.into()), RewardDestination::Account(11)); + assert_eq!(Staking::payee(11.into()), Some(RewardDestination::Account(11))); // Check that reward went to the controller account assert_eq!(Balances::free_balance(11), recorded_stash_balance + total_payout_2); // Check that amount at stake is NOT increased @@ -1739,6 +1744,74 @@ fn rebond_emits_right_value_in_event() { }); } +#[test] +fn max_staked_rewards_default_works() { + ExtBuilder::default().build_and_execute(|| { + assert_eq!(>::get(), None); + + let default_stakers_payout = current_total_payout_for_duration(reward_time_per_era()); + assert!(default_stakers_payout > 0); + start_active_era(1); + + // the final stakers reward is the same as the reward before applied the cap. + assert_eq!(ErasValidatorReward::::get(0).unwrap(), default_stakers_payout); + + // which is the same behaviour if the `MaxStakedRewards` is set to 100%. + >::set(Some(Percent::from_parts(100))); + + let default_stakers_payout = current_total_payout_for_duration(reward_time_per_era()); + assert_eq!(ErasValidatorReward::::get(0).unwrap(), default_stakers_payout); + }) +} + +#[test] +fn max_staked_rewards_works() { + ExtBuilder::default().nominate(true).build_and_execute(|| { + let max_staked_rewards = 10; + + // sets new max staked rewards through set_staking_configs. + assert_ok!(Staking::set_staking_configs( + RuntimeOrigin::root(), + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Set(Percent::from_percent(max_staked_rewards)), + )); + + assert_eq!(>::get(), Some(Percent::from_percent(10))); + + // check validators account state. + assert_eq!(Session::validators().len(), 2); + assert!(Session::validators().contains(&11) & Session::validators().contains(&21)); + // balance of the mock treasury account is 0 + assert_eq!(RewardRemainderUnbalanced::get(), 0); + + let max_stakers_payout = current_total_payout_for_duration(reward_time_per_era()); + + start_active_era(1); + + let treasury_payout = RewardRemainderUnbalanced::get(); + let validators_payout = ErasValidatorReward::::get(0).unwrap(); + let total_payout = treasury_payout + validators_payout; + + // max stakers payout (without max staked rewards cap applied) is larger than the final + // validator rewards. The final payment and remainder should be adjusted by redestributing + // the era inflation to apply the cap... + assert!(max_stakers_payout > validators_payout); + + // .. which means that the final validator payout is 10% of the total payout.. + assert_eq!(validators_payout, Percent::from_percent(max_staked_rewards) * total_payout); + // .. and the remainder 90% goes to the treasury. + assert_eq!( + treasury_payout, + Percent::from_percent(100 - max_staked_rewards) * (treasury_payout + validators_payout) + ); + }) +} + #[test] fn reward_to_stake_works() { ExtBuilder::default() @@ -1746,6 +1819,7 @@ fn reward_to_stake_works() { .set_status(31, StakerStatus::Idle) .set_status(41, StakerStatus::Idle) .set_stake(21, 2000) + .try_state(false) .build_and_execute(|| { assert_eq!(Staking::validator_count(), 2); // Confirm account 10 and 20 are validators @@ -2200,7 +2274,7 @@ fn reward_validator_slashing_validator_does_not_overflow() { let _ = Balances::make_free_balance_be(&2, stake); // only slashes out of bonded stake are applied. without this line, it is 0. - Staking::bond(RuntimeOrigin::signed(2), stake - 1, RewardDestination::default()).unwrap(); + Staking::bond(RuntimeOrigin::signed(2), stake - 1, RewardDestination::Staked).unwrap(); // Override exposure of 11 EraInfo::::set_exposure( 0, @@ -5016,7 +5090,7 @@ mod election_data_provider { assert_eq!(MinNominatorBond::::get(), 1); assert_eq!(::VoterList::count(), 4); - assert_ok!(Staking::bond(RuntimeOrigin::signed(4), 5, Default::default(),)); + assert_ok!(Staking::bond(RuntimeOrigin::signed(4), 5, RewardDestination::Staked,)); assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![1])); assert_eq!(::VoterList::count(), 5); @@ -5415,6 +5489,28 @@ fn count_check_works() { }) } +#[test] +#[should_panic = "called `Result::unwrap()` on an `Err` value: Other(\"number of entries in payee storage items does not match the number of bonded ledgers\")"] +fn check_payee_invariant1_works() { + // A bonded ledger should always have an assigned `Payee` This test should panic as we verify + // that a bad state will panic due to the `try_state` checks in the `post_checks` in `mock`. + ExtBuilder::default().build_and_execute(|| { + let rogue_ledger = StakingLedger::::new(123456, 20); + Ledger::::insert(123456, rogue_ledger); + }) +} + +#[test] +#[should_panic = "called `Result::unwrap()` on an `Err` value: Other(\"number of entries in payee storage items does not match the number of bonded ledgers\")"] +fn check_payee_invariant2_works() { + // The number of entries in both `Payee` and of bonded staking ledgers should match. This test + // should panic as we verify that a bad state will panic due to the `try_state` checks in the + // `post_checks` in `mock`. + ExtBuilder::default().build_and_execute(|| { + Payee::::insert(1111, RewardDestination::Staked); + }) +} + #[test] fn min_bond_checks_work() { ExtBuilder::default() @@ -5520,7 +5616,8 @@ fn chill_other_works() { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Remove, - ConfigOp::Remove + ConfigOp::Remove, + ConfigOp::Noop, )); // Still can't chill these users @@ -5541,7 +5638,8 @@ fn chill_other_works() { ConfigOp::Set(10), ConfigOp::Set(10), ConfigOp::Noop, - ConfigOp::Noop + ConfigOp::Noop, + ConfigOp::Noop, )); // Still can't chill these users @@ -5562,7 +5660,8 @@ fn chill_other_works() { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Noop, - ConfigOp::Noop + ConfigOp::Noop, + ConfigOp::Noop, )); // Still can't chill these users @@ -5583,7 +5682,8 @@ fn chill_other_works() { ConfigOp::Set(10), ConfigOp::Set(10), ConfigOp::Set(Percent::from_percent(75)), - ConfigOp::Noop + ConfigOp::Noop, + ConfigOp::Noop, )); // 16 people total because tests start with 2 active one @@ -5629,6 +5729,7 @@ fn capped_stakers_works() { ConfigOp::Set(max), ConfigOp::Remove, ConfigOp::Remove, + ConfigOp::Noop, )); // can create `max - validator_count` validators @@ -5699,6 +5800,7 @@ fn capped_stakers_works() { ConfigOp::Remove, ConfigOp::Noop, ConfigOp::Noop, + ConfigOp::Noop, )); assert_ok!(Staking::nominate(RuntimeOrigin::signed(last_nominator), vec![1])); assert_ok!(Staking::validate( @@ -5734,6 +5836,7 @@ fn min_commission_works() { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Set(Perbill::from_percent(10)), + ConfigOp::Noop, )); // can't make it less than 10 now @@ -6734,7 +6837,7 @@ mod ledger { #[test] fn paired_account_works() { - ExtBuilder::default().build_and_execute(|| { + ExtBuilder::default().try_state(false).build_and_execute(|| { assert_ok!(Staking::bond( RuntimeOrigin::signed(10), 100, @@ -6769,7 +6872,7 @@ mod ledger { #[test] fn get_ledger_works() { - ExtBuilder::default().build_and_execute(|| { + ExtBuilder::default().try_state(false).build_and_execute(|| { // stash does not exist assert!(StakingLedger::::get(StakingAccount::Stash(42)).is_err()); @@ -6819,7 +6922,7 @@ mod ledger { assert_ok!(ledger.clone().bond(reward_dest)); assert!(StakingLedger::::is_bonded(StakingAccount::Stash(42))); assert!(>::get(&42).is_some()); - assert_eq!(>::get(&42), reward_dest); + assert_eq!(>::get(&42), Some(reward_dest)); // cannot bond again. assert!(ledger.clone().bond(reward_dest).is_err()); @@ -6857,7 +6960,7 @@ mod ledger { Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller), Error::::ControllerDeprecated ); - assert_eq!(Payee::::get(&11), RewardDestination::Staked); + assert_eq!(Payee::::get(&11), Some(RewardDestination::Staked)); }) } @@ -6867,18 +6970,18 @@ mod ledger { ExtBuilder::default().build_and_execute(|| { // migrate a `Controller` variant to `Account` variant. Payee::::insert(11, RewardDestination::Controller); - assert_eq!(Payee::::get(&11), RewardDestination::Controller); + assert_eq!(Payee::::get(&11), Some(RewardDestination::Controller)); assert_ok!(Staking::update_payee(RuntimeOrigin::signed(11), 11)); - assert_eq!(Payee::::get(&11), RewardDestination::Account(11)); + assert_eq!(Payee::::get(&11), Some(RewardDestination::Account(11))); // Do not migrate a variant if not `Controller`. Payee::::insert(21, RewardDestination::Stash); - assert_eq!(Payee::::get(&21), RewardDestination::Stash); + assert_eq!(Payee::::get(&21), Some(RewardDestination::Stash)); assert_noop!( Staking::update_payee(RuntimeOrigin::signed(11), 21), Error::::NotController ); - assert_eq!(Payee::::get(&21), RewardDestination::Stash); + assert_eq!(Payee::::get(&21), Some(RewardDestination::Stash)); }) } @@ -7016,7 +7119,7 @@ mod ledger { #[test] fn deprecate_controller_batch_skips_unmigrated_controller_payees() { - ExtBuilder::default().build_and_execute(|| { + ExtBuilder::default().try_state(false).build_and_execute(|| { // Given: let stash: u64 = 1000; diff --git a/substrate/frame/staking/src/weights.rs b/substrate/frame/staking/src/weights.rs index 7c9a050016406a5ae5b8f98bcd98b0093858628a..8288591a787bb3f1e8df0bdb19c383f5ecfca396 100644 --- a/substrate/frame/staking/src/weights.rs +++ b/substrate/frame/staking/src/weights.rs @@ -17,26 +17,28 @@ //! Autogenerated weights for `pallet_staking` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-12-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-itmxxexx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_staking +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_staking -// --chain=dev -// --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/staking/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -99,8 +101,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `927` // Estimated: `4764` - // Minimum execution time: 42_491_000 picoseconds. - Weight::from_parts(44_026_000, 4764) + // Minimum execution time: 41_318_000 picoseconds. + Weight::from_parts(43_268_000, 4764) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -120,8 +122,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1990` // Estimated: `8877` - // Minimum execution time: 88_756_000 picoseconds. - Weight::from_parts(91_000_000, 8877) + // Minimum execution time: 85_666_000 picoseconds. + Weight::from_parts(88_749_000, 8877) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -147,8 +149,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2195` // Estimated: `8877` - // Minimum execution time: 91_331_000 picoseconds. - Weight::from_parts(94_781_000, 8877) + // Minimum execution time: 90_282_000 picoseconds. + Weight::from_parts(92_332_000, 8877) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -162,16 +164,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1115` + // Measured: `1297` // Estimated: `4764` - // Minimum execution time: 42_495_000 picoseconds. - Weight::from_parts(44_189_470, 4764) - // Standard Error: 1_389 - .saturating_add(Weight::from_parts(47_484, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Minimum execution time: 44_626_000 picoseconds. + Weight::from_parts(47_254_657, 4764) + // Standard Error: 1_179 + .saturating_add(Weight::from_parts(57_657, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) @@ -207,10 +211,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 89_004_000 picoseconds. - Weight::from_parts(96_677_570, 6248) - // Standard Error: 4_635 - .saturating_add(Weight::from_parts(1_387_718, 0).saturating_mul(s.into())) + // Minimum execution time: 86_769_000 picoseconds. + Weight::from_parts(95_212_867, 6248) + // Standard Error: 3_706 + .saturating_add(Weight::from_parts(1_320_752, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(11_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -242,8 +246,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1372` // Estimated: `4556` - // Minimum execution time: 51_532_000 picoseconds. - Weight::from_parts(53_308_000, 4556) + // Minimum execution time: 50_410_000 picoseconds. + Weight::from_parts(52_576_000, 4556) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -256,10 +260,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1280 + k * (569 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 28_955_000 picoseconds. - Weight::from_parts(29_609_869, 4556) - // Standard Error: 6_793 - .saturating_add(Weight::from_parts(6_412_124, 0).saturating_mul(k.into())) + // Minimum execution time: 29_017_000 picoseconds. + Weight::from_parts(33_019_206, 4556) + // Standard Error: 6_368 + .saturating_add(Weight::from_parts(6_158_681, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -292,10 +296,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1866 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 64_080_000 picoseconds. - Weight::from_parts(61_985_382, 6248) - // Standard Error: 13_320 - .saturating_add(Weight::from_parts(4_030_513, 0).saturating_mul(n.into())) + // Minimum execution time: 63_452_000 picoseconds. + Weight::from_parts(60_924_066, 6248) + // Standard Error: 14_416 + .saturating_add(Weight::from_parts(3_921_589, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -319,8 +323,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1650` // Estimated: `6248` - // Minimum execution time: 54_194_000 picoseconds. - Weight::from_parts(55_578_000, 6248) + // Minimum execution time: 54_253_000 picoseconds. + Weight::from_parts(55_286_000, 6248) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -334,8 +338,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `902` // Estimated: `4556` - // Minimum execution time: 16_597_000 picoseconds. - Weight::from_parts(16_980_000, 4556) + // Minimum execution time: 16_812_000 picoseconds. + Weight::from_parts(17_380_000, 4556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -349,8 +353,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `969` // Estimated: `4556` - // Minimum execution time: 20_626_000 picoseconds. - Weight::from_parts(21_242_000, 4556) + // Minimum execution time: 20_590_000 picoseconds. + Weight::from_parts(21_096_000, 4556) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -362,8 +366,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `902` // Estimated: `4556` - // Minimum execution time: 19_972_000 picoseconds. - Weight::from_parts(20_470_000, 4556) + // Minimum execution time: 19_923_000 picoseconds. + Weight::from_parts(20_880_000, 4556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -373,8 +377,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_571_000 picoseconds. - Weight::from_parts(2_720_000, 0) + // Minimum execution time: 2_599_000 picoseconds. + Weight::from_parts(2_751_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -383,8 +387,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_056_000 picoseconds. - Weight::from_parts(8_413_000, 0) + // Minimum execution time: 6_941_000 picoseconds. + Weight::from_parts(7_288_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -393,8 +397,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_162_000 picoseconds. - Weight::from_parts(8_497_000, 0) + // Minimum execution time: 6_757_000 picoseconds. + Weight::from_parts(7_200_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -403,8 +407,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_320_000 picoseconds. - Weight::from_parts(8_564_000, 0) + // Minimum execution time: 7_028_000 picoseconds. + Weight::from_parts(7_366_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::Invulnerables` (r:0 w:1) @@ -414,10 +418,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_470_000 picoseconds. - Weight::from_parts(3_110_242, 0) - // Standard Error: 63 - .saturating_add(Weight::from_parts(11_786, 0).saturating_mul(v.into())) + // Minimum execution time: 2_741_000 picoseconds. + Weight::from_parts(3_212_598, 0) + // Standard Error: 68 + .saturating_add(Weight::from_parts(11_695, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::Ledger` (r:5900 w:11800) @@ -431,10 +435,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1356 + i * (151 ±0)` // Estimated: `990 + i * (3566 ±0)` - // Minimum execution time: 2_101_000 picoseconds. - Weight::from_parts(2_238_000, 990) - // Standard Error: 56_753 - .saturating_add(Weight::from_parts(18_404_902, 0).saturating_mul(i.into())) + // Minimum execution time: 2_132_000 picoseconds. + Weight::from_parts(2_289_000, 990) + // Standard Error: 34_227 + .saturating_add(Weight::from_parts(17_006_583, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3566).saturating_mul(i.into())) @@ -472,10 +476,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 86_765_000 picoseconds. - Weight::from_parts(95_173_565, 6248) - // Standard Error: 4_596 - .saturating_add(Weight::from_parts(1_354_849, 0).saturating_mul(s.into())) + // Minimum execution time: 85_308_000 picoseconds. + Weight::from_parts(93_689_468, 6248) + // Standard Error: 5_425 + .saturating_add(Weight::from_parts(1_307_604, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -488,10 +492,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `66672` // Estimated: `70137` - // Minimum execution time: 104_490_000 picoseconds. - Weight::from_parts(1_162_956_951, 70137) - // Standard Error: 76_760 - .saturating_add(Weight::from_parts(6_485_569, 0).saturating_mul(s.into())) + // Minimum execution time: 103_242_000 picoseconds. + Weight::from_parts(1_162_296_080, 70137) + // Standard Error: 76_741 + .saturating_add(Weight::from_parts(6_487_522, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -528,10 +532,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `33297 + n * (377 ±0)` // Estimated: `30944 + n * (3774 ±0)` - // Minimum execution time: 144_790_000 picoseconds. - Weight::from_parts(36_764_791, 30944) - // Standard Error: 89_592 - .saturating_add(Weight::from_parts(49_620_105, 0).saturating_mul(n.into())) + // Minimum execution time: 140_740_000 picoseconds. + Weight::from_parts(182_886_963, 30944) + // Standard Error: 39_852 + .saturating_add(Weight::from_parts(43_140_752, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) @@ -555,10 +559,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1991 + l * (7 ±0)` // Estimated: `8877` - // Minimum execution time: 81_768_000 picoseconds. - Weight::from_parts(85_332_982, 8877) - // Standard Error: 5_380 - .saturating_add(Weight::from_parts(70_298, 0).saturating_mul(l.into())) + // Minimum execution time: 80_372_000 picoseconds. + Weight::from_parts(83_335_027, 8877) + // Standard Error: 4_780 + .saturating_add(Weight::from_parts(72_180, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -593,10 +597,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 96_123_000 picoseconds. - Weight::from_parts(100_278_672, 6248) - // Standard Error: 3_487 - .saturating_add(Weight::from_parts(1_326_503, 0).saturating_mul(s.into())) + // Minimum execution time: 93_920_000 picoseconds. + Weight::from_parts(98_022_911, 6248) + // Standard Error: 4_096 + .saturating_add(Weight::from_parts(1_287_745, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(11_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -642,12 +646,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 572_893_000 picoseconds. - Weight::from_parts(578_010_000, 512390) - // Standard Error: 2_094_268 - .saturating_add(Weight::from_parts(68_419_710, 0).saturating_mul(v.into())) - // Standard Error: 208_682 - .saturating_add(Weight::from_parts(18_826_175, 0).saturating_mul(n.into())) + // Minimum execution time: 556_012_000 picoseconds. + Weight::from_parts(560_339_000, 512390) + // Standard Error: 2_115_076 + .saturating_add(Weight::from_parts(69_456_497, 0).saturating_mul(v.into())) + // Standard Error: 210_755 + .saturating_add(Weight::from_parts(17_974_873, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -678,12 +682,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3175 + n * (911 ±0) + v * (395 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 33_836_205_000 picoseconds. - Weight::from_parts(34_210_443_000, 512390) - // Standard Error: 441_692 - .saturating_add(Weight::from_parts(6_122_533, 0).saturating_mul(v.into())) - // Standard Error: 441_692 - .saturating_add(Weight::from_parts(4_418_264, 0).saturating_mul(n.into())) + // Minimum execution time: 32_792_819_000 picoseconds. + Weight::from_parts(32_986_606_000, 512390) + // Standard Error: 360_905 + .saturating_add(Weight::from_parts(5_260_151, 0).saturating_mul(v.into())) + // Standard Error: 360_905 + .saturating_add(Weight::from_parts(3_599_219, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -700,10 +704,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `979 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_454_689_000 picoseconds. - Weight::from_parts(161_771_064, 3510) - // Standard Error: 31_022 - .saturating_add(Weight::from_parts(4_820_158, 0).saturating_mul(v.into())) + // Minimum execution time: 2_434_755_000 picoseconds. + Weight::from_parts(38_574_764, 3510) + // Standard Error: 14_467 + .saturating_add(Weight::from_parts(4_821_702, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -714,6 +718,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -724,9 +730,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_073_000 picoseconds. - Weight::from_parts(5_452_000, 0) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Minimum execution time: 5_765_000 picoseconds. + Weight::from_parts(6_140_000, 0) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::MinCommission` (r:0 w:1) /// Proof: `Staking::MinCommission` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -734,6 +740,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -744,9 +752,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_465_000 picoseconds. - Weight::from_parts(4_832_000, 0) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Minimum execution time: 5_025_000 picoseconds. + Weight::from_parts(5_354_000, 0) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) @@ -774,8 +782,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1939` // Estimated: `6248` - // Minimum execution time: 71_239_000 picoseconds. - Weight::from_parts(74_649_000, 6248) + // Minimum execution time: 69_656_000 picoseconds. + Weight::from_parts(71_574_000, 6248) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -787,8 +795,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `691` // Estimated: `3510` - // Minimum execution time: 12_525_000 picoseconds. - Weight::from_parts(13_126_000, 3510) + // Minimum execution time: 12_523_000 picoseconds. + Weight::from_parts(13_315_000, 3510) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -798,8 +806,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_918_000 picoseconds. - Weight::from_parts(3_176_000, 0) + // Minimum execution time: 3_125_000 picoseconds. + Weight::from_parts(3_300_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -820,8 +828,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `927` // Estimated: `4764` - // Minimum execution time: 42_491_000 picoseconds. - Weight::from_parts(44_026_000, 4764) + // Minimum execution time: 41_318_000 picoseconds. + Weight::from_parts(43_268_000, 4764) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -841,8 +849,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1990` // Estimated: `8877` - // Minimum execution time: 88_756_000 picoseconds. - Weight::from_parts(91_000_000, 8877) + // Minimum execution time: 85_666_000 picoseconds. + Weight::from_parts(88_749_000, 8877) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -868,8 +876,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2195` // Estimated: `8877` - // Minimum execution time: 91_331_000 picoseconds. - Weight::from_parts(94_781_000, 8877) + // Minimum execution time: 90_282_000 picoseconds. + Weight::from_parts(92_332_000, 8877) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -883,16 +891,18 @@ impl WeightInfo for () { /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) + /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1115` + // Measured: `1297` // Estimated: `4764` - // Minimum execution time: 42_495_000 picoseconds. - Weight::from_parts(44_189_470, 4764) - // Standard Error: 1_389 - .saturating_add(Weight::from_parts(47_484, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Minimum execution time: 44_626_000 picoseconds. + Weight::from_parts(47_254_657, 4764) + // Standard Error: 1_179 + .saturating_add(Weight::from_parts(57_657, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) @@ -928,10 +938,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 89_004_000 picoseconds. - Weight::from_parts(96_677_570, 6248) - // Standard Error: 4_635 - .saturating_add(Weight::from_parts(1_387_718, 0).saturating_mul(s.into())) + // Minimum execution time: 86_769_000 picoseconds. + Weight::from_parts(95_212_867, 6248) + // Standard Error: 3_706 + .saturating_add(Weight::from_parts(1_320_752, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(11_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -963,8 +973,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1372` // Estimated: `4556` - // Minimum execution time: 51_532_000 picoseconds. - Weight::from_parts(53_308_000, 4556) + // Minimum execution time: 50_410_000 picoseconds. + Weight::from_parts(52_576_000, 4556) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -977,10 +987,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1280 + k * (569 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 28_955_000 picoseconds. - Weight::from_parts(29_609_869, 4556) - // Standard Error: 6_793 - .saturating_add(Weight::from_parts(6_412_124, 0).saturating_mul(k.into())) + // Minimum execution time: 29_017_000 picoseconds. + Weight::from_parts(33_019_206, 4556) + // Standard Error: 6_368 + .saturating_add(Weight::from_parts(6_158_681, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -1013,10 +1023,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1866 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 64_080_000 picoseconds. - Weight::from_parts(61_985_382, 6248) - // Standard Error: 13_320 - .saturating_add(Weight::from_parts(4_030_513, 0).saturating_mul(n.into())) + // Minimum execution time: 63_452_000 picoseconds. + Weight::from_parts(60_924_066, 6248) + // Standard Error: 14_416 + .saturating_add(Weight::from_parts(3_921_589, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1040,8 +1050,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1650` // Estimated: `6248` - // Minimum execution time: 54_194_000 picoseconds. - Weight::from_parts(55_578_000, 6248) + // Minimum execution time: 54_253_000 picoseconds. + Weight::from_parts(55_286_000, 6248) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1055,8 +1065,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `902` // Estimated: `4556` - // Minimum execution time: 16_597_000 picoseconds. - Weight::from_parts(16_980_000, 4556) + // Minimum execution time: 16_812_000 picoseconds. + Weight::from_parts(17_380_000, 4556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1070,8 +1080,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `969` // Estimated: `4556` - // Minimum execution time: 20_626_000 picoseconds. - Weight::from_parts(21_242_000, 4556) + // Minimum execution time: 20_590_000 picoseconds. + Weight::from_parts(21_096_000, 4556) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1083,8 +1093,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `902` // Estimated: `4556` - // Minimum execution time: 19_972_000 picoseconds. - Weight::from_parts(20_470_000, 4556) + // Minimum execution time: 19_923_000 picoseconds. + Weight::from_parts(20_880_000, 4556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1094,8 +1104,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_571_000 picoseconds. - Weight::from_parts(2_720_000, 0) + // Minimum execution time: 2_599_000 picoseconds. + Weight::from_parts(2_751_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1104,8 +1114,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_056_000 picoseconds. - Weight::from_parts(8_413_000, 0) + // Minimum execution time: 6_941_000 picoseconds. + Weight::from_parts(7_288_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1114,8 +1124,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_162_000 picoseconds. - Weight::from_parts(8_497_000, 0) + // Minimum execution time: 6_757_000 picoseconds. + Weight::from_parts(7_200_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1124,8 +1134,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_320_000 picoseconds. - Weight::from_parts(8_564_000, 0) + // Minimum execution time: 7_028_000 picoseconds. + Weight::from_parts(7_366_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::Invulnerables` (r:0 w:1) @@ -1135,10 +1145,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_470_000 picoseconds. - Weight::from_parts(3_110_242, 0) - // Standard Error: 63 - .saturating_add(Weight::from_parts(11_786, 0).saturating_mul(v.into())) + // Minimum execution time: 2_741_000 picoseconds. + Weight::from_parts(3_212_598, 0) + // Standard Error: 68 + .saturating_add(Weight::from_parts(11_695, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::Ledger` (r:5900 w:11800) @@ -1152,10 +1162,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1356 + i * (151 ±0)` // Estimated: `990 + i * (3566 ±0)` - // Minimum execution time: 2_101_000 picoseconds. - Weight::from_parts(2_238_000, 990) - // Standard Error: 56_753 - .saturating_add(Weight::from_parts(18_404_902, 0).saturating_mul(i.into())) + // Minimum execution time: 2_132_000 picoseconds. + Weight::from_parts(2_289_000, 990) + // Standard Error: 34_227 + .saturating_add(Weight::from_parts(17_006_583, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 3566).saturating_mul(i.into())) @@ -1193,10 +1203,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 86_765_000 picoseconds. - Weight::from_parts(95_173_565, 6248) - // Standard Error: 4_596 - .saturating_add(Weight::from_parts(1_354_849, 0).saturating_mul(s.into())) + // Minimum execution time: 85_308_000 picoseconds. + Weight::from_parts(93_689_468, 6248) + // Standard Error: 5_425 + .saturating_add(Weight::from_parts(1_307_604, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1209,10 +1219,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `66672` // Estimated: `70137` - // Minimum execution time: 104_490_000 picoseconds. - Weight::from_parts(1_162_956_951, 70137) - // Standard Error: 76_760 - .saturating_add(Weight::from_parts(6_485_569, 0).saturating_mul(s.into())) + // Minimum execution time: 103_242_000 picoseconds. + Weight::from_parts(1_162_296_080, 70137) + // Standard Error: 76_741 + .saturating_add(Weight::from_parts(6_487_522, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1249,10 +1259,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `33297 + n * (377 ±0)` // Estimated: `30944 + n * (3774 ±0)` - // Minimum execution time: 144_790_000 picoseconds. - Weight::from_parts(36_764_791, 30944) - // Standard Error: 89_592 - .saturating_add(Weight::from_parts(49_620_105, 0).saturating_mul(n.into())) + // Minimum execution time: 140_740_000 picoseconds. + Weight::from_parts(182_886_963, 30944) + // Standard Error: 39_852 + .saturating_add(Weight::from_parts(43_140_752, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) @@ -1276,10 +1286,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1991 + l * (7 ±0)` // Estimated: `8877` - // Minimum execution time: 81_768_000 picoseconds. - Weight::from_parts(85_332_982, 8877) - // Standard Error: 5_380 - .saturating_add(Weight::from_parts(70_298, 0).saturating_mul(l.into())) + // Minimum execution time: 80_372_000 picoseconds. + Weight::from_parts(83_335_027, 8877) + // Standard Error: 4_780 + .saturating_add(Weight::from_parts(72_180, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -1314,10 +1324,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2196 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 96_123_000 picoseconds. - Weight::from_parts(100_278_672, 6248) - // Standard Error: 3_487 - .saturating_add(Weight::from_parts(1_326_503, 0).saturating_mul(s.into())) + // Minimum execution time: 93_920_000 picoseconds. + Weight::from_parts(98_022_911, 6248) + // Standard Error: 4_096 + .saturating_add(Weight::from_parts(1_287_745, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(11_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -1363,12 +1373,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 572_893_000 picoseconds. - Weight::from_parts(578_010_000, 512390) - // Standard Error: 2_094_268 - .saturating_add(Weight::from_parts(68_419_710, 0).saturating_mul(v.into())) - // Standard Error: 208_682 - .saturating_add(Weight::from_parts(18_826_175, 0).saturating_mul(n.into())) + // Minimum execution time: 556_012_000 picoseconds. + Weight::from_parts(560_339_000, 512390) + // Standard Error: 2_115_076 + .saturating_add(Weight::from_parts(69_456_497, 0).saturating_mul(v.into())) + // Standard Error: 210_755 + .saturating_add(Weight::from_parts(17_974_873, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1399,12 +1409,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3175 + n * (911 ±0) + v * (395 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 33_836_205_000 picoseconds. - Weight::from_parts(34_210_443_000, 512390) - // Standard Error: 441_692 - .saturating_add(Weight::from_parts(6_122_533, 0).saturating_mul(v.into())) - // Standard Error: 441_692 - .saturating_add(Weight::from_parts(4_418_264, 0).saturating_mul(n.into())) + // Minimum execution time: 32_792_819_000 picoseconds. + Weight::from_parts(32_986_606_000, 512390) + // Standard Error: 360_905 + .saturating_add(Weight::from_parts(5_260_151, 0).saturating_mul(v.into())) + // Standard Error: 360_905 + .saturating_add(Weight::from_parts(3_599_219, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1421,10 +1431,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `979 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_454_689_000 picoseconds. - Weight::from_parts(161_771_064, 3510) - // Standard Error: 31_022 - .saturating_add(Weight::from_parts(4_820_158, 0).saturating_mul(v.into())) + // Minimum execution time: 2_434_755_000 picoseconds. + Weight::from_parts(38_574_764, 3510) + // Standard Error: 14_467 + .saturating_add(Weight::from_parts(4_821_702, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -1435,6 +1445,8 @@ impl WeightInfo for () { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -1445,9 +1457,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_073_000 picoseconds. - Weight::from_parts(5_452_000, 0) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Minimum execution time: 5_765_000 picoseconds. + Weight::from_parts(6_140_000, 0) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::MinCommission` (r:0 w:1) /// Proof: `Staking::MinCommission` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -1455,6 +1467,8 @@ impl WeightInfo for () { /// Proof: `Staking::MinValidatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxValidatorsCount` (r:0 w:1) /// Proof: `Staking::MaxValidatorsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Staking::MaxStakedRewards` (r:0 w:1) + /// Proof: `Staking::MaxStakedRewards` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::ChillThreshold` (r:0 w:1) /// Proof: `Staking::ChillThreshold` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Staking::MaxNominatorsCount` (r:0 w:1) @@ -1465,9 +1479,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_465_000 picoseconds. - Weight::from_parts(4_832_000, 0) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Minimum execution time: 5_025_000 picoseconds. + Weight::from_parts(5_354_000, 0) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) @@ -1495,8 +1509,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1939` // Estimated: `6248` - // Minimum execution time: 71_239_000 picoseconds. - Weight::from_parts(74_649_000, 6248) + // Minimum execution time: 69_656_000 picoseconds. + Weight::from_parts(71_574_000, 6248) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1508,8 +1522,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `691` // Estimated: `3510` - // Minimum execution time: 12_525_000 picoseconds. - Weight::from_parts(13_126_000, 3510) + // Minimum execution time: 12_523_000 picoseconds. + Weight::from_parts(13_315_000, 3510) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1519,8 +1533,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_918_000 picoseconds. - Weight::from_parts(3_176_000, 0) + // Minimum execution time: 3_125_000 picoseconds. + Weight::from_parts(3_300_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/substrate/frame/state-trie-migration/Cargo.toml b/substrate/frame/state-trie-migration/Cargo.toml index e27d4c469269a639411a6e5ef30c3583628c23ea..e837956613edd2a30146bc6e41b4518d55c95095 100644 --- a/substrate/frame/state-trie-migration/Cargo.toml +++ b/substrate/frame/state-trie-migration/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-state-trie-migration" -version = "4.0.0-dev" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,9 +16,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } thousands = { version = "0.2.0", optional = true } zstd = { version = "0.12.4", default-features = false, optional = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } diff --git a/substrate/frame/state-trie-migration/src/lib.rs b/substrate/frame/state-trie-migration/src/lib.rs index c71002f420ce4185130e6dca49cd35bda8537810..b89159ef3ec44ede131eb3009a29f011082cb55d 100644 --- a/substrate/frame/state-trie-migration/src/lib.rs +++ b/substrate/frame/state-trie-migration/src/lib.rs @@ -79,7 +79,11 @@ pub mod pallet { dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, ensure, pallet_prelude::*, - traits::{Currency, Get}, + traits::{ + fungible::{hold::Balanced, Inspect, InspectHold, Mutate, MutateHold}, + tokens::{Fortitude, Precision}, + Get, + }, }; use frame_system::{self, pallet_prelude::*}; use sp_core::{ @@ -92,7 +96,7 @@ pub mod pallet { use sp_std::{ops::Deref, prelude::*}; pub(crate) type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; + <::Currency as Inspect<::AccountId>>::Balance; /// The progress of either the top or child keys. #[derive( @@ -441,20 +445,58 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + /// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`]. + pub mod config_preludes { + use super::*; + use frame_support::derive_impl; + + pub struct TestDefaultConfig; + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, 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 = (); + } + } + + /// The reason for this pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The funds are held as a deposit for slashing. + #[codec(index = 0)] + SlashForMigrate, + } + /// Configurations of this pallet. - #[pallet::config] + #[pallet::config(with_default)] pub trait Config: frame_system::Config { /// Origin that can control the configurations of this pallet. - type ControlOrigin: frame_support::traits::EnsureOrigin; + #[pallet::no_default] + type ControlOrigin: EnsureOrigin; /// Filter on which origin that trigger the manual migrations. + #[pallet::no_default] type SignedFilter: EnsureOrigin; /// The overarching event type. + #[pallet::no_default_bounds] type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The currency provider type. - type Currency: Currency; + #[pallet::no_default] + type Currency: InspectHold + + Mutate + + MutateHold + + Balanced; + + /// The overarching runtime hold reason. + #[pallet::no_default_bounds] + type RuntimeHoldReason: From; /// Maximal number of bytes that a key can have. /// @@ -479,19 +521,23 @@ pub mod pallet { /// #[pallet::constant] + #[pallet::no_default] type MaxKeyLen: Get; /// The amount of deposit collected per item in advance, for signed migrations. /// /// This should reflect the average storage value size in the worse case. + #[pallet::no_default] type SignedDepositPerItem: Get>; /// The base value of [`Config::SignedDepositPerItem`]. /// /// Final deposit is `items * SignedDepositPerItem + SignedDepositBase`. + #[pallet::no_default] type SignedDepositBase: Get>; /// The weight information of this pallet. + #[pallet::no_default] type WeightInfo: WeightInfo; } @@ -600,8 +646,11 @@ pub mod pallet { ); // ensure they can pay more than the fee. - let deposit = T::SignedDepositPerItem::get().saturating_mul(limits.item.into()); - ensure!(T::Currency::can_slash(&who, deposit), Error::::NotEnoughFunds); + let deposit = Self::calculate_deposit_for(limits.item); + ensure!( + T::Currency::can_hold(&HoldReason::SlashForMigrate.into(), &who, deposit), + Error::::NotEnoughFunds + ); let mut task = Self::migration_process(); ensure!( @@ -618,10 +667,7 @@ pub mod pallet { // ensure that the migration witness data was correct. if real_size_upper < task.dyn_size { - // let the imbalance burn. - let (_imbalance, _remainder) = T::Currency::slash(&who, deposit); - Self::deposit_event(Event::::Slashed { who, amount: deposit }); - debug_assert!(_remainder.is_zero()); + Self::slash(who, deposit)?; return Ok(().into()) } @@ -665,10 +711,11 @@ pub mod pallet { let who = T::SignedFilter::ensure_origin(origin)?; // ensure they can pay more than the fee. - let deposit = T::SignedDepositBase::get().saturating_add( - T::SignedDepositPerItem::get().saturating_mul((keys.len() as u32).into()), + let deposit = Self::calculate_deposit_for(keys.len() as u32); + ensure!( + T::Currency::can_hold(&HoldReason::SlashForMigrate.into(), &who, deposit), + Error::::NotEnoughFunds ); - ensure!(T::Currency::can_slash(&who, deposit), "not enough funds"); let mut dyn_size = 0u32; for key in &keys { @@ -679,9 +726,7 @@ pub mod pallet { } if dyn_size > witness_size { - let (_imbalance, _remainder) = T::Currency::slash(&who, deposit); - Self::deposit_event(Event::::Slashed { who, amount: deposit }); - debug_assert!(_remainder.is_zero()); + Self::slash(who, deposit)?; Ok(().into()) } else { Self::deposit_event(Event::::Migrated { @@ -724,13 +769,11 @@ pub mod pallet { let who = T::SignedFilter::ensure_origin(origin)?; // ensure they can pay more than the fee. - let deposit = T::SignedDepositBase::get().saturating_add( - T::SignedDepositPerItem::get().saturating_mul((child_keys.len() as u32).into()), + let deposit = Self::calculate_deposit_for(child_keys.len() as u32); + ensure!( + T::Currency::can_hold(&HoldReason::SlashForMigrate.into(), &who, deposit), + Error::::NotEnoughFunds ); - sp_std::if_std! { - println!("+ {:?} / {:?} / {:?}", who, deposit, T::Currency::free_balance(&who)); - } - ensure!(T::Currency::can_slash(&who, deposit), "not enough funds"); let mut dyn_size = 0u32; let transformed_child_key = Self::transform_child_key(&root).ok_or("bad child key")?; @@ -742,9 +785,7 @@ pub mod pallet { } if dyn_size != total_size { - let (_imbalance, _remainder) = T::Currency::slash(&who, deposit); - debug_assert!(_remainder.is_zero()); - Self::deposit_event(Event::::Slashed { who, amount: deposit }); + Self::slash(who, deposit)?; Ok(PostDispatchInfo { actual_weight: Some(T::WeightInfo::migrate_custom_child_fail()), pays_fee: Pays::Yes, @@ -887,20 +928,47 @@ pub mod pallet { string.extend_from_slice(root.as_ref()); string } + + /// Calculate the deposit required for migrating a specific number of keys. + pub(crate) fn calculate_deposit_for(keys_count: u32) -> BalanceOf { + T::SignedDepositBase::get() + .saturating_add(T::SignedDepositPerItem::get().saturating_mul(keys_count.into())) + } + + /// Slash an account for migration. + fn slash(who: T::AccountId, amount: BalanceOf) -> Result<(), DispatchError> { + T::Currency::hold(&HoldReason::SlashForMigrate.into(), &who, amount)?; + // let the imbalance burn. + let _burned = T::Currency::burn_all_held( + &HoldReason::SlashForMigrate.into(), + &who, + Precision::BestEffort, + Fortitude::Force, + )?; + debug_assert!(amount.saturating_sub(_burned).is_zero()); + Self::deposit_event(Event::::Slashed { who, amount }); + Ok(()) + } } } #[cfg(feature = "runtime-benchmarks")] mod benchmarks { use super::{pallet::Pallet as StateTrieMigration, *}; - use frame_support::traits::{Currency, Get}; - use sp_runtime::traits::Saturating; + use frame_support::traits::fungible::{Inspect, Mutate}; use sp_std::prelude::*; // The size of the key seemingly makes no difference in the read/write time, so we make it // constant. const KEY: &[u8] = b"key"; + fn set_balance_for_deposit(caller: &T::AccountId, item: u32) -> BalanceOf { + let deposit = StateTrieMigration::::calculate_deposit_for(item); + let stash = T::Currency::minimum_balance() * BalanceOf::::from(1000u32) + deposit; + T::Currency::set_balance(caller, stash); + stash + } + frame_benchmarking::benchmarks! { continue_migrate { // note that this benchmark should migrate nothing, as we only want the overhead weight @@ -908,11 +976,13 @@ mod benchmarks { // function. let null = MigrationLimits::default(); let caller = frame_benchmarking::whitelisted_caller(); + let stash = set_balance_for_deposit::(&caller, null.item); // Allow signed migrations. SignedMigrationMaxLimits::::put(MigrationLimits { size: 1024, item: 5 }); - }: _(frame_system::RawOrigin::Signed(caller), null, 0, StateTrieMigration::::migration_process()) + }: _(frame_system::RawOrigin::Signed(caller.clone()), null, 0, StateTrieMigration::::migration_process()) verify { - assert_eq!(StateTrieMigration::::migration_process(), Default::default()) + assert_eq!(StateTrieMigration::::migration_process(), Default::default()); + assert_eq!(T::Currency::balance(&caller), stash) } continue_migrate_wrong_witness { @@ -936,26 +1006,18 @@ mod benchmarks { migrate_custom_top_success { let null = MigrationLimits::default(); - let caller = frame_benchmarking::whitelisted_caller(); - let deposit = T::SignedDepositBase::get().saturating_add( - T::SignedDepositPerItem::get().saturating_mul(1u32.into()), - ); - let stash = T::Currency::minimum_balance() * BalanceOf::::from(1000u32) + deposit; - T::Currency::make_free_balance_be(&caller, stash); + let caller: T::AccountId = frame_benchmarking::whitelisted_caller(); + let stash = set_balance_for_deposit::(&caller, null.item); }: migrate_custom_top(frame_system::RawOrigin::Signed(caller.clone()), Default::default(), 0) verify { assert_eq!(StateTrieMigration::::migration_process(), Default::default()); - assert_eq!(T::Currency::free_balance(&caller), stash) + assert_eq!(T::Currency::balance(&caller), stash) } migrate_custom_top_fail { let null = MigrationLimits::default(); - let caller = frame_benchmarking::whitelisted_caller(); - let deposit = T::SignedDepositBase::get().saturating_add( - T::SignedDepositPerItem::get().saturating_mul(1u32.into()), - ); - let stash = T::Currency::minimum_balance() * BalanceOf::::from(1000u32) + deposit; - T::Currency::make_free_balance_be(&caller, stash); + let caller: T::AccountId = frame_benchmarking::whitelisted_caller(); + let stash = set_balance_for_deposit::(&caller, null.item); // for tests, we need to make sure there is _something_ in storage that is being // migrated. sp_io::storage::set(b"foo", vec![1u8;33].as_ref()); @@ -971,24 +1033,19 @@ mod benchmarks { frame_system::Pallet::::assert_last_event( ::RuntimeEvent::from(crate::Event::Slashed { who: caller.clone(), - amount: T::SignedDepositBase::get() - .saturating_add(T::SignedDepositPerItem::get().saturating_mul(1u32.into())), + amount: StateTrieMigration::::calculate_deposit_for(1u32), }).into(), ); } verify { assert_eq!(StateTrieMigration::::migration_process(), Default::default()); // must have gotten slashed - assert!(T::Currency::free_balance(&caller) < stash) + assert!(T::Currency::balance(&caller) < stash) } migrate_custom_child_success { - let caller = frame_benchmarking::whitelisted_caller(); - let deposit = T::SignedDepositBase::get().saturating_add( - T::SignedDepositPerItem::get().saturating_mul(1u32.into()), - ); - let stash = T::Currency::minimum_balance() * BalanceOf::::from(1000u32) + deposit; - T::Currency::make_free_balance_be(&caller, stash); + let caller: T::AccountId = frame_benchmarking::whitelisted_caller(); + let stash = set_balance_for_deposit::(&caller, 0); }: migrate_custom_child( frame_system::RawOrigin::Signed(caller.clone()), StateTrieMigration::::childify(Default::default()), @@ -997,16 +1054,12 @@ mod benchmarks { ) verify { assert_eq!(StateTrieMigration::::migration_process(), Default::default()); - assert_eq!(T::Currency::free_balance(&caller), stash); + assert_eq!(T::Currency::balance(&caller), stash); } migrate_custom_child_fail { - let caller = frame_benchmarking::whitelisted_caller(); - let deposit = T::SignedDepositBase::get().saturating_add( - T::SignedDepositPerItem::get().saturating_mul(1u32.into()), - ); - let stash = T::Currency::minimum_balance() * BalanceOf::::from(1000u32) + deposit; - T::Currency::make_free_balance_be(&caller, stash); + let caller: T::AccountId = frame_benchmarking::whitelisted_caller(); + let stash = set_balance_for_deposit::(&caller, 1); // for tests, we need to make sure there is _something_ in storage that is being // migrated. sp_io::default_child_storage::set(b"top", b"foo", vec![1u8;33].as_ref()); @@ -1023,7 +1076,7 @@ mod benchmarks { verify { assert_eq!(StateTrieMigration::::migration_process(), Default::default()); // must have gotten slashed - assert!(T::Currency::free_balance(&caller) < stash) + assert!(T::Currency::balance(&caller) < stash) } process_top_key { @@ -1052,7 +1105,7 @@ mod mock { use crate as pallet_state_trie_migration; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU32, ConstU64, Hooks}, + traits::{ConstU32, Hooks}, weights::Weight, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -1060,10 +1113,7 @@ mod mock { storage::{ChildInfo, StateVersion}, H256, }; - use sp_runtime::{ - traits::{BlakeTwo256, Header as _, IdentityLookup}, - BuildStorage, StorageChild, - }; + use sp_runtime::{traits::Header as _, BuildStorage, StorageChild}; type Block = frame_system::mocking::MockBlockU32; @@ -1083,29 +1133,9 @@ mod mock { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU32<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = SS58Prefix; - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } parameter_types! { @@ -1114,21 +1144,10 @@ mod mock { pub const MigrationMaxKeyLen: u32 = 512; } + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] impl pallet_balances::Config for Test { - type Balance = u64; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeHoldReason = (); - type RuntimeFreezeReason = (); - type MaxHolds = (); + type AccountStore = System; } /// Test only Weights for state migration. @@ -1158,8 +1177,8 @@ mod mock { } } + #[derive_impl(super::config_preludes::TestDefaultConfig as pallet_state_trie_migration::DefaultConfig)] impl pallet_state_trie_migration::Config for Test { - type RuntimeEvent = RuntimeEvent; type ControlOrigin = EnsureRoot; type Currency = Balances; type MaxKeyLen = MigrationMaxKeyLen; @@ -1270,6 +1289,7 @@ mod mock { #[cfg(test)] mod test { use super::{mock::*, *}; + use frame_support::assert_ok; use sp_runtime::{bounded_vec, traits::Bounded, StateVersion}; #[test] @@ -1499,10 +1519,9 @@ mod test { while !MigrationProcess::::get().finished() { // first we compute the task to get the accurate consumption. let mut task = StateTrieMigration::migration_process(); - let result = task.migrate_until_exhaustion( + assert_ok!(task.migrate_until_exhaustion( StateTrieMigration::signed_migration_max_limits().unwrap(), - ); - assert!(result.is_ok()); + )); frame_support::assert_ok!(StateTrieMigration::continue_migrate( RuntimeOrigin::signed(1), @@ -1513,6 +1532,7 @@ mod test { // no funds should remain reserved. assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::free_balance(&1), 1000); // and the task should be updated assert!(matches!( @@ -1523,6 +1543,37 @@ mod test { }); } + #[test] + fn continue_migrate_slashing_works() { + new_test_ext(StateVersion::V0, true, None, None).execute_with(|| { + assert_eq!(MigrationProcess::::get(), Default::default()); + + // Allow signed migrations. + SignedMigrationMaxLimits::::put(MigrationLimits { size: 1024, item: 5 }); + + // first we compute the task to get the accurate consumption. + let mut task = StateTrieMigration::migration_process(); + assert_ok!(task.migrate_until_exhaustion( + StateTrieMigration::signed_migration_max_limits().unwrap(), + )); + + // can't submit with `real_size_upper` < `task.dyn_size` expect slashing + frame_support::assert_ok!(StateTrieMigration::continue_migrate( + RuntimeOrigin::signed(1), + StateTrieMigration::signed_migration_max_limits().unwrap(), + task.dyn_size - 1, + MigrationProcess::::get() + )); + // no funds should remain reserved. + assert_eq!(Balances::reserved_balance(&1), 0); + // user was slashed + assert_eq!( + Balances::free_balance(&1), + 1000 - StateTrieMigration::calculate_deposit_for(5) + ); + }); + } + #[test] fn custom_migrate_top_works() { let correct_witness = 3 + sp_core::storage::TRIE_VALUE_NODE_THRESHOLD * 3 + 1 + 2 + 3; @@ -1565,7 +1616,7 @@ mod test { assert_eq!(Balances::reserved_balance(&1), 0); assert_eq!( Balances::free_balance(&1), - 1000 - (3 * SignedDepositPerItem::get() + SignedDepositBase::get()) + 1000 - StateTrieMigration::calculate_deposit_for(3) ); }); } @@ -1600,7 +1651,7 @@ mod test { assert_eq!(Balances::reserved_balance(&1), 0); assert_eq!( Balances::free_balance(&1), - 1000 - (2 * SignedDepositPerItem::get() + SignedDepositBase::get()) + 1000 - StateTrieMigration::calculate_deposit_for(2) ); }); } @@ -1750,7 +1801,7 @@ mod remote_tests_local { use std::env::var as env_var; // we only use the hash type from this, so using the mock should be fine. - type Extrinsic = sp_runtime::testing::TestXt; + type Extrinsic = sp_runtime::generic::UncheckedExtrinsic; type Block = sp_runtime::testing::Block; #[tokio::test] diff --git a/substrate/frame/state-trie-migration/src/weights.rs b/substrate/frame/state-trie-migration/src/weights.rs index df3338fdc17d3154c6febfdcae51b702614edc49..23dad9e33800bd307b295cdf46ad286edd3af7b7 100644 --- a/substrate/frame/state-trie-migration/src/weights.rs +++ b/substrate/frame/state-trie-migration/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_state_trie_migration +//! Autogenerated weights for `pallet_state_trie_migration` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/state-trie-migration/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/state-trie-migration/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_state_trie_migration. +/// Weight functions needed for `pallet_state_trie_migration`. pub trait WeightInfo { fn continue_migrate() -> Weight; fn continue_migrate_wrong_witness() -> Weight; @@ -61,157 +60,181 @@ pub trait WeightInfo { fn process_top_key(v: u32, ) -> Weight; } -/// Weights for pallet_state_trie_migration using the Substrate node and recommended hardware. +/// Weights for `pallet_state_trie_migration` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) - /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: StateTrieMigration MigrationProcess (r:1 w:1) - /// Proof: StateTrieMigration MigrationProcess (max_values: Some(1), max_size: Some(1042), added: 1537, mode: MaxEncodedLen) + /// Storage: `StateTrieMigration::SignedMigrationMaxLimits` (r:1 w:0) + /// Proof: `StateTrieMigration::SignedMigrationMaxLimits` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `StateTrieMigration::MigrationProcess` (r:1 w:1) + /// Proof: `StateTrieMigration::MigrationProcess` (`max_values`: Some(1), `max_size`: Some(1042), added: 1537, mode: `MaxEncodedLen`) fn continue_migrate() -> Weight { // Proof Size summary in bytes: // Measured: `108` - // Estimated: `2527` - // Minimum execution time: 14_297_000 picoseconds. - Weight::from_parts(14_832_000, 2527) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Estimated: `3658` + // Minimum execution time: 17_661_000 picoseconds. + Weight::from_parts(18_077_000, 3658) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) - /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `StateTrieMigration::SignedMigrationMaxLimits` (r:1 w:0) + /// Proof: `StateTrieMigration::SignedMigrationMaxLimits` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn continue_migrate_wrong_witness() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1493` - // Minimum execution time: 4_237_000 picoseconds. - Weight::from_parts(4_646_000, 1493) + // Minimum execution time: 4_036_000 picoseconds. + Weight::from_parts(4_267_000, 1493) .saturating_add(T::DbWeight::get().reads(1_u64)) } + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn migrate_custom_top_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_898_000 picoseconds. - Weight::from_parts(9_237_000, 0) + // Estimated: `3658` + // Minimum execution time: 11_348_000 picoseconds. + Weight::from_parts(11_899_000, 3658) + .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: unknown `0x666f6f` (r:1 w:1) - /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x666f6f` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x666f6f` (r:1 w:1) fn migrate_custom_top_fail() -> Weight { // Proof Size summary in bytes: // Measured: `113` - // Estimated: `3578` - // Minimum execution time: 29_291_000 picoseconds. - Weight::from_parts(30_424_000, 3578) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Estimated: `3658` + // Minimum execution time: 59_819_000 picoseconds. + Weight::from_parts(61_066_000, 3658) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn migrate_custom_child_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_094_000 picoseconds. - Weight::from_parts(9_544_000, 0) + // Estimated: `3658` + // Minimum execution time: 11_424_000 picoseconds. + Weight::from_parts(11_765_000, 3658) + .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: unknown `0x666f6f` (r:1 w:1) - /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x666f6f` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x666f6f` (r:1 w:1) fn migrate_custom_child_fail() -> Weight { // Proof Size summary in bytes: - // Measured: `105` - // Estimated: `3570` - // Minimum execution time: 30_286_000 picoseconds. - Weight::from_parts(30_948_000, 3570) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Measured: `106` + // Estimated: `3658` + // Minimum execution time: 61_086_000 picoseconds. + Weight::from_parts(62_546_000, 3658) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: unknown `0x6b6579` (r:1 w:1) - /// Proof Skipped: unknown `0x6b6579` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x6b6579` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x6b6579` (r:1 w:1) /// The range of component `v` is `[1, 4194304]`. fn process_top_key(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `197 + v * (1 ±0)` // Estimated: `3662 + v * (1 ±0)` - // Minimum execution time: 5_420_000 picoseconds. - Weight::from_parts(5_560_000, 3662) + // Minimum execution time: 5_375_000 picoseconds. + Weight::from_parts(5_552_000, 3662) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_139, 0).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(1_146, 0).saturating_mul(v.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(v.into())) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) - /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: StateTrieMigration MigrationProcess (r:1 w:1) - /// Proof: StateTrieMigration MigrationProcess (max_values: Some(1), max_size: Some(1042), added: 1537, mode: MaxEncodedLen) + /// Storage: `StateTrieMigration::SignedMigrationMaxLimits` (r:1 w:0) + /// Proof: `StateTrieMigration::SignedMigrationMaxLimits` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `StateTrieMigration::MigrationProcess` (r:1 w:1) + /// Proof: `StateTrieMigration::MigrationProcess` (`max_values`: Some(1), `max_size`: Some(1042), added: 1537, mode: `MaxEncodedLen`) fn continue_migrate() -> Weight { // Proof Size summary in bytes: // Measured: `108` - // Estimated: `2527` - // Minimum execution time: 14_297_000 picoseconds. - Weight::from_parts(14_832_000, 2527) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Estimated: `3658` + // Minimum execution time: 17_661_000 picoseconds. + Weight::from_parts(18_077_000, 3658) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: StateTrieMigration SignedMigrationMaxLimits (r:1 w:0) - /// Proof: StateTrieMigration SignedMigrationMaxLimits (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `StateTrieMigration::SignedMigrationMaxLimits` (r:1 w:0) + /// Proof: `StateTrieMigration::SignedMigrationMaxLimits` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn continue_migrate_wrong_witness() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1493` - // Minimum execution time: 4_237_000 picoseconds. - Weight::from_parts(4_646_000, 1493) + // Minimum execution time: 4_036_000 picoseconds. + Weight::from_parts(4_267_000, 1493) .saturating_add(RocksDbWeight::get().reads(1_u64)) } + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn migrate_custom_top_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_898_000 picoseconds. - Weight::from_parts(9_237_000, 0) + // Estimated: `3658` + // Minimum execution time: 11_348_000 picoseconds. + Weight::from_parts(11_899_000, 3658) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: unknown `0x666f6f` (r:1 w:1) - /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x666f6f` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x666f6f` (r:1 w:1) fn migrate_custom_top_fail() -> Weight { // Proof Size summary in bytes: // Measured: `113` - // Estimated: `3578` - // Minimum execution time: 29_291_000 picoseconds. - Weight::from_parts(30_424_000, 3578) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Estimated: `3658` + // Minimum execution time: 59_819_000 picoseconds. + Weight::from_parts(61_066_000, 3658) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) fn migrate_custom_child_success() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `0` - // Minimum execution time: 9_094_000 picoseconds. - Weight::from_parts(9_544_000, 0) + // Estimated: `3658` + // Minimum execution time: 11_424_000 picoseconds. + Weight::from_parts(11_765_000, 3658) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: unknown `0x666f6f` (r:1 w:1) - /// Proof Skipped: unknown `0x666f6f` (r:1 w:1) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x666f6f` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x666f6f` (r:1 w:1) fn migrate_custom_child_fail() -> Weight { // Proof Size summary in bytes: - // Measured: `105` - // Estimated: `3570` - // Minimum execution time: 30_286_000 picoseconds. - Weight::from_parts(30_948_000, 3570) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Measured: `106` + // Estimated: `3658` + // Minimum execution time: 61_086_000 picoseconds. + Weight::from_parts(62_546_000, 3658) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: unknown `0x6b6579` (r:1 w:1) - /// Proof Skipped: unknown `0x6b6579` (r:1 w:1) + /// Storage: UNKNOWN KEY `0x6b6579` (r:1 w:1) + /// Proof: UNKNOWN KEY `0x6b6579` (r:1 w:1) /// The range of component `v` is `[1, 4194304]`. fn process_top_key(v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `197 + v * (1 ±0)` // Estimated: `3662 + v * (1 ±0)` - // Minimum execution time: 5_420_000 picoseconds. - Weight::from_parts(5_560_000, 3662) + // Minimum execution time: 5_375_000 picoseconds. + Weight::from_parts(5_552_000, 3662) // Standard Error: 1 - .saturating_add(Weight::from_parts(1_139, 0).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(1_146, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(v.into())) diff --git a/substrate/frame/statement/Cargo.toml b/substrate/frame/statement/Cargo.toml index d41afc3244b4fd36a0583128ee182ca26d0dcbda..6827dbda962b3d4973133f38b723579ab1ddfd4d 100644 --- a/substrate/frame/statement/Cargo.toml +++ b/substrate/frame/statement/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-statement" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -25,7 +25,7 @@ sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] pallet-balances = { path = "../balances" } diff --git a/substrate/frame/statement/src/mock.rs b/substrate/frame/statement/src/mock.rs index 192baa1f218602bec663174e965b75c5f4995d92..4ab9cf9e0f96a33fe70de6c019234b9a3829eb7e 100644 --- a/substrate/frame/statement/src/mock.rs +++ b/substrate/frame/statement/src/mock.rs @@ -22,14 +22,10 @@ use super::*; use crate as pallet_statement; use frame_support::{ derive_impl, ord_parameter_types, - traits::{ConstU32, ConstU64, Everything}, - weights::constants::RocksDbWeight, -}; -use sp_core::{Pair, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - AccountId32, BuildStorage, + traits::{ConstU32, ConstU64}, }; +use sp_core::Pair; +use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage}; type Block = frame_system::mocking::MockBlock; @@ -49,29 +45,10 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = RocksDbWeight; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = AccountId32; type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -88,7 +65,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = (); } ord_parameter_types! { diff --git a/substrate/frame/sudo/Cargo.toml b/substrate/frame/sudo/Cargo.toml index 027716ce3179fd5e7eecac5912c3e1b4ade2a3ef..409104aeca111866c2d8023763ec4a2e47de8a3c 100644 --- a/substrate/frame/sudo/Cargo.toml +++ b/substrate/frame/sudo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-sudo" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -25,7 +25,7 @@ sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } -docify = "0.2.6" +docify = "0.2.7" [dev-dependencies] sp-core = { path = "../../primitives/core" } diff --git a/substrate/frame/sudo/src/benchmarking.rs b/substrate/frame/sudo/src/benchmarking.rs index e64233fe7480a9d82c9c07709dd9b403544bff6a..cdd7d707600e1d2393e989add59f16349acb3492 100644 --- a/substrate/frame/sudo/src/benchmarking.rs +++ b/substrate/frame/sudo/src/benchmarking.rs @@ -20,14 +20,23 @@ use super::*; use crate::Pallet; use frame_benchmarking::v2::*; +use frame_support::dispatch::DispatchInfo; use frame_system::RawOrigin; +use sp_runtime::traits::{AsSystemOriginSigner, DispatchTransaction, Dispatchable}; fn assert_last_event(generic_event: crate::Event) { let re: ::RuntimeEvent = generic_event.into(); frame_system::Pallet::::assert_last_event(re.into()); } -#[benchmarks(where ::RuntimeCall: From>)] +#[benchmarks(where + T: Send + Sync, + ::RuntimeCall: From>, + ::RuntimeCall: Dispatchable, + <::RuntimeCall as Dispatchable>::PostInfo: From<()>, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner + Clone, +)] mod benchmarks { use super::*; @@ -85,5 +94,23 @@ mod benchmarks { assert_last_event::(Event::KeyRemoved {}); } + #[benchmark] + fn check_only_sudo_account() { + let caller: T::AccountId = whitelisted_caller(); + Key::::put(&caller); + + let call = frame_system::Call::remark { remark: vec![] }.into(); + let info = DispatchInfo { ..Default::default() }; + let ext = CheckOnlySudoAccount::::new(); + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(().into())) + .unwrap() + .is_ok()); + } + } + impl_benchmark_test_suite!(Pallet, crate::mock::new_bench_ext(), crate::mock::Test); } diff --git a/substrate/frame/sudo/src/extension.rs b/substrate/frame/sudo/src/extension.rs index c717ff3567268ae7bf868120d6411d1abe63c712..2cd8f945eddb9e406ec16651cb436b6a60d3ade6 100644 --- a/substrate/frame/sudo/src/extension.rs +++ b/substrate/frame/sudo/src/extension.rs @@ -15,15 +15,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{Config, Pallet}; +use crate::{Config, Key}; use codec::{Decode, Encode}; use frame_support::{dispatch::DispatchInfo, ensure}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, SignedExtension}, + impl_tx_ext_default, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, TransactionExtension, + TransactionExtensionBase, + }, transaction_validity::{ - InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError, - UnknownTransaction, ValidTransaction, + InvalidTransaction, TransactionPriority, TransactionValidityError, UnknownTransaction, + ValidTransaction, }, }; use sp_std::{fmt, marker::PhantomData}; @@ -59,49 +63,61 @@ impl fmt::Debug for CheckOnlySudoAccount { } impl CheckOnlySudoAccount { - /// Creates new `SignedExtension` to check sudo key. + /// Creates new `TransactionExtension` to check sudo key. pub fn new() -> Self { Self::default() } } -impl SignedExtension for CheckOnlySudoAccount -where - ::RuntimeCall: Dispatchable, -{ +impl TransactionExtensionBase for CheckOnlySudoAccount { const IDENTIFIER: &'static str = "CheckOnlySudoAccount"; - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = (); - type Pre = (); + type Implicit = (); - fn additional_signed(&self) -> Result { - Ok(()) + fn weight(&self) -> frame_support::weights::Weight { + use crate::weights::WeightInfo; + T::WeightInfo::check_only_sudo_account() } +} +impl + TransactionExtension<::RuntimeCall, Context> for CheckOnlySudoAccount +where + ::RuntimeCall: Dispatchable, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner + Clone, +{ + type Pre = (); + type Val = (); fn validate( &self, - who: &Self::AccountId, - _call: &Self::Call, - info: &DispatchInfoOf, + origin: <::RuntimeCall as Dispatchable>::RuntimeOrigin, + _call: &::RuntimeCall, + info: &DispatchInfoOf<::RuntimeCall>, _len: usize, - ) -> TransactionValidity { - let sudo_key: T::AccountId = >::key().ok_or(UnknownTransaction::CannotLookup)?; + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + ( + ValidTransaction, + Self::Val, + <::RuntimeCall as Dispatchable>::RuntimeOrigin, + ), + TransactionValidityError, + > { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + let sudo_key: T::AccountId = Key::::get().ok_or(UnknownTransaction::CannotLookup)?; ensure!(*who == sudo_key, InvalidTransaction::BadSigner); - Ok(ValidTransaction { - priority: info.weight.ref_time() as TransactionPriority, - ..Default::default() - }) + Ok(( + ValidTransaction { + priority: info.weight.ref_time() as TransactionPriority, + ..Default::default() + }, + (), + origin, + )) } - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } + impl_tx_ext_default!(::RuntimeCall; Context; prepare); } diff --git a/substrate/frame/sudo/src/lib.rs b/substrate/frame/sudo/src/lib.rs index 4f14c32ff76b0e0038597c9d715d632985eceeea..2c953368b2215981320ba7cfd9957815c8f7a519 100644 --- a/substrate/frame/sudo/src/lib.rs +++ b/substrate/frame/sudo/src/lib.rs @@ -85,8 +85,8 @@ //! meant to be used by constructing runtime calls from outside the runtime. //! //! -//! This pallet also defines a [`SignedExtension`](sp_runtime::traits::SignedExtension) called -//! [`CheckOnlySudoAccount`] to ensure that only signed transactions by the sudo account are +//! This pallet also defines a [`TransactionExtension`](sp_runtime::traits::TransactionExtension) +//! called [`CheckOnlySudoAccount`] to ensure that only signed transactions by the sudo account are //! accepted by the transaction pool. The intended use of this signed extension is to prevent other //! accounts from spamming the transaction pool for the initial phase of a chain, during which //! developers may only want a sudo account to be able to make transactions. @@ -329,7 +329,6 @@ pub mod pallet { /// The `AccountId` of the sudo key. #[pallet::storage] - #[pallet::getter(fn key)] pub(super) type Key = StorageValue<_, T::AccountId, OptionQuery>; #[pallet::genesis_config] @@ -352,7 +351,7 @@ pub mod pallet { let sender = ensure_signed_or_root(origin)?; if let Some(sender) = sender { - if Self::key().map_or(false, |k| k == sender) { + if Key::::get().map_or(false, |k| k == sender) { Ok(()) } else { Err(Error::::RequireSudo.into()) diff --git a/substrate/frame/sudo/src/mock.rs b/substrate/frame/sudo/src/mock.rs index 9ad14804524a9400fa5e876aa9119d44ee45857c..3b907a27168366c4b4b56ceabde60d78ff9e42fd 100644 --- a/substrate/frame/sudo/src/mock.rs +++ b/substrate/frame/sudo/src/mock.rs @@ -19,16 +19,9 @@ use super::*; use crate as sudo; -use frame_support::{ - derive_impl, - traits::{ConstU32, Contains}, -}; -use sp_core::{ConstU64, H256}; +use frame_support::{derive_impl, traits::Contains}; use sp_io; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; // Logger module to track execution. #[frame_support::pallet] @@ -113,29 +106,7 @@ impl Contains for BlockEverything { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = BlockEverything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } // Implement the logger module's `Config` on the Test runtime. diff --git a/substrate/frame/sudo/src/tests.rs b/substrate/frame/sudo/src/tests.rs index 73689415a737fd3e2acba663099f9042f88ace4c..00bb86cc2686f2410d09077d9b388750914eb74c 100644 --- a/substrate/frame/sudo/src/tests.rs +++ b/substrate/frame/sudo/src/tests.rs @@ -28,7 +28,7 @@ use mock::{ fn test_setup_works() { // Environment setup, logger storage, and sudo `key` retrieval should work as expected. new_test_ext(1).execute_with(|| { - assert_eq!(Sudo::key(), Some(1u64)); + assert_eq!(Key::::get(), Some(1u64)); assert!(Logger::i32_log().is_empty()); assert!(Logger::account_log().is_empty()); }); @@ -135,7 +135,7 @@ fn set_key_basics() { new_test_ext(1).execute_with(|| { // A root `key` can change the root `key` assert_ok!(Sudo::set_key(RuntimeOrigin::signed(1), 2)); - assert_eq!(Sudo::key(), Some(2u64)); + assert_eq!(Key::::get(), Some(2u64)); }); new_test_ext(1).execute_with(|| { @@ -161,7 +161,7 @@ fn set_key_emits_events_correctly() { fn remove_key_works() { new_test_ext(1).execute_with(|| { assert_ok!(Sudo::remove_key(RuntimeOrigin::signed(1))); - assert!(Sudo::key().is_none()); + assert!(Key::::get().is_none()); System::assert_has_event(TestEvent::Sudo(Event::KeyRemoved {})); assert_noop!(Sudo::remove_key(RuntimeOrigin::signed(1)), Error::::RequireSudo); @@ -173,11 +173,11 @@ fn remove_key_works() { fn using_root_origin_works() { new_test_ext(1).execute_with(|| { assert_ok!(Sudo::remove_key(RuntimeOrigin::root())); - assert!(Sudo::key().is_none()); + assert!(Key::::get().is_none()); System::assert_has_event(TestEvent::Sudo(Event::KeyRemoved {})); assert_ok!(Sudo::set_key(RuntimeOrigin::root(), 1)); - assert_eq!(Some(1), Sudo::key()); + assert_eq!(Some(1), Key::::get()); }); } diff --git a/substrate/frame/sudo/src/weights.rs b/substrate/frame/sudo/src/weights.rs index 10d1a9f7a513b7ae98947e27832842a42ab0c589..aa8f69fd4e6bfaa40a5ee791b9b1fabd47b71534 100644 --- a/substrate/frame/sudo/src/weights.rs +++ b/substrate/frame/sudo/src/weights.rs @@ -17,26 +17,28 @@ //! Autogenerated weights for `pallet_sudo` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, 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-bn-ce5rx-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_sudo +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_sudo -// --chain=dev -// --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/sudo/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -53,6 +55,7 @@ pub trait WeightInfo { fn sudo() -> Weight; fn sudo_as() -> Weight; fn remove_key() -> Weight; + fn check_only_sudo_account() -> Weight; } /// Weights for `pallet_sudo` using the Substrate node and recommended hardware. @@ -64,8 +67,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 9_600_000 picoseconds. - Weight::from_parts(10_076_000, 1517) + // Minimum execution time: 9_590_000 picoseconds. + Weight::from_parts(9_924_000, 1517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -75,8 +78,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 10_453_000 picoseconds. - Weight::from_parts(10_931_000, 1517) + // Minimum execution time: 9_825_000 picoseconds. + Weight::from_parts(10_373_000, 1517) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Sudo::Key` (r:1 w:0) @@ -85,8 +88,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 10_202_000 picoseconds. - Weight::from_parts(10_800_000, 1517) + // Minimum execution time: 10_140_000 picoseconds. + Weight::from_parts(10_382_000, 1517) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Sudo::Key` (r:1 w:1) @@ -95,11 +98,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 8_555_000 picoseconds. - Weight::from_parts(8_846_000, 1517) + // Minimum execution time: 8_610_000 picoseconds. + Weight::from_parts(8_975_000, 1517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 3_416_000 picoseconds. + Weight::from_parts(3_645_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } } // For backwards compatibility and tests. @@ -110,8 +123,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 9_600_000 picoseconds. - Weight::from_parts(10_076_000, 1517) + // Minimum execution time: 9_590_000 picoseconds. + Weight::from_parts(9_924_000, 1517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -121,8 +134,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 10_453_000 picoseconds. - Weight::from_parts(10_931_000, 1517) + // Minimum execution time: 9_825_000 picoseconds. + Weight::from_parts(10_373_000, 1517) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Sudo::Key` (r:1 w:0) @@ -131,8 +144,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 10_202_000 picoseconds. - Weight::from_parts(10_800_000, 1517) + // Minimum execution time: 10_140_000 picoseconds. + Weight::from_parts(10_382_000, 1517) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Sudo::Key` (r:1 w:1) @@ -141,9 +154,19 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 8_555_000 picoseconds. - Weight::from_parts(8_846_000, 1517) + // Minimum execution time: 8_610_000 picoseconds. + Weight::from_parts(8_975_000, 1517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 3_416_000 picoseconds. + Weight::from_parts(3_645_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } } diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index dafb86bd660e0ecc3ed2000912f4d561d1c4a882..113af41751ed3f69dd23bcfe25e2ccb3715585fc 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,14 +17,25 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { version = "6.1", default-features = false } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"] } -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -frame-metadata = { version = "16.0.0", default-features = false, features = ["current"] } -sp-api = { path = "../../primitives/api", default-features = false, features = ["frame-metadata"] } +serde = { features = ["alloc", "derive"], workspace = true } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", + "max-encoded-len", +] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } +frame-metadata = { version = "16.0.0", default-features = false, features = [ + "current", +] } +sp-api = { path = "../../primitives/api", default-features = false, features = [ + "frame-metadata", +] } sp-std = { path = "../../primitives/std", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } -sp-runtime = { path = "../../primitives/runtime", default-features = false, features = ["serde"] } +sp-runtime = { path = "../../primitives/runtime", default-features = false, features = [ + "serde", +] } sp-tracing = { path = "../../primitives/tracing", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } sp-arithmetic = { path = "../../primitives/arithmetic", default-features = false } @@ -41,13 +52,13 @@ sp-state-machine = { path = "../../primitives/state-machine", default-features = bitflags = "1.3" impl-trait-for-tuples = "0.2.2" smallvec = "1.11.0" -log = { version = "0.4.17", default-features = false } -sp-core-hashing-proc-macro = { path = "../../primitives/core/hashing/proc-macro" } +log = { workspace = true } +sp-crypto-hashing-proc-macro = { path = "../../primitives/crypto/hashing/proc-macro" } k256 = { version = "0.13.1", default-features = false, features = ["ecdsa"] } environmental = { version = "1.1.4", default-features = false } sp-genesis-builder = { path = "../../primitives/genesis-builder", default-features = false } -serde_json = { version = "1.0.111", default-features = false, features = ["alloc"] } -docify = "0.2.6" +serde_json = { features = ["alloc"], workspace = true } +docify = "0.2.7" static_assertions = "1.1.0" aquamarine = { version = "0.5.0" } @@ -55,7 +66,9 @@ aquamarine = { version = "0.5.0" } [dev-dependencies] assert_matches = "1.3.0" pretty_assertions = "1.2.1" +sp-timestamp = { path = "../../primitives/timestamp", default-features = false } frame-system = { path = "../system" } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing" } [features] default = ["std"] @@ -82,6 +95,7 @@ std = [ "sp-staking/std", "sp-state-machine/std", "sp-std/std", + "sp-timestamp/std", "sp-tracing/std", "sp-weights/std", ] diff --git a/substrate/frame/support/procedural/Cargo.toml b/substrate/frame/support/procedural/Cargo.toml index 25debb7fc2bfef3d3dc541291ecaf5d066c85082..859475038020aba9afa0a9aa110c51d699cc5358 100644 --- a/substrate/frame/support/procedural/Cargo.toml +++ b/substrate/frame/support/procedural/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support-procedural" -version = "4.0.0-dev" +version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -23,20 +23,20 @@ Inflector = "0.11.4" cfg-expr = "0.15.5" itertools = "0.10.3" proc-macro2 = "1.0.56" -quote = "1.0.28" -syn = { version = "2.0.48", features = ["full"] } +quote = { workspace = true } +syn = { features = ["full", "visit-mut"], workspace = true } frame-support-procedural-tools = { path = "tools" } macro_magic = { version = "0.5.0", features = ["proc_support"] } proc-macro-warning = { version = "1.0.0", default-features = false } expander = "2.0.0" -sp-core-hashing = { path = "../../../primitives/core/hashing", default-features = false } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing", default-features = false } [dev-dependencies] regex = "1" [features] default = ["std"] -std = ["sp-core-hashing/std"] +std = ["sp-crypto-hashing/std"] no-metadata-docs = [] # Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of # pallets in a runtime grows. Does increase the compile time! diff --git a/substrate/frame/support/procedural/src/benchmark.rs b/substrate/frame/support/procedural/src/benchmark.rs index 6ded82d91aa5cd9bb9cceb4594ae9768d4948483..27c75a7f054cfbbcd71750f11985665b60cc3aed 100644 --- a/substrate/frame/support/procedural/src/benchmark.rs +++ b/substrate/frame/support/procedural/src/benchmark.rs @@ -65,6 +65,7 @@ struct RangeArgs { start: syn::GenericArgument, _comma: Comma, end: syn::GenericArgument, + _trailing_comma: Option, _gt_token: Gt, } @@ -431,7 +432,7 @@ pub fn benchmarks( let mut benchmarks_by_name_mappings: Vec = Vec::new(); let test_idents: Vec = benchmark_names_str .iter() - .map(|n| Ident::new(format!("test_{}", n).as_str(), Span::call_site())) + .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) .collect(); for i in 0..benchmark_names.len() { let name_ident = &benchmark_names[i]; @@ -441,6 +442,37 @@ pub fn benchmarks( benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) } + let impl_test_function = content + .iter_mut() + .find_map(|item| { + let Item::Macro(item_macro) = item else { + return None; + }; + + if !item_macro + .mac + .path + .segments + .iter() + .any(|s| s.ident == "impl_benchmark_test_suite") + { + return None; + } + + let tokens = item_macro.mac.tokens.clone(); + *item = Item::Verbatim(quote! {}); + + Some(quote! { + impl_test_function!( + (#( {} #benchmark_names )*) + (#( #extra_benchmark_names )*) + (#( #skip_meta_benchmark_names )*) + #tokens + ); + }) + }) + .unwrap_or(quote! {}); + // emit final quoted tokens let res = quote! { #(#mod_attrs) @@ -676,6 +708,8 @@ pub fn benchmarks( } } } + + #impl_test_function } #mod_vis use #mod_name::*; }; @@ -733,7 +767,8 @@ fn expand_benchmark( let setup_stmts = benchmark_def.setup_stmts; let verify_stmts = benchmark_def.verify_stmts; let last_stmt = benchmark_def.last_stmt; - let test_ident = Ident::new(format!("test_{}", name.to_string()).as_str(), Span::call_site()); + let test_ident = + Ident::new(format!("test_benchmark_{}", name.to_string()).as_str(), Span::call_site()); // unroll params (prepare for quoting) let unrolled = UnrolledParams::from(&benchmark_def.params); diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs index ce2aa0942794d40cc3bfe0f4d65fdd80f8140d52..b0041ccc07541966863e51d40f4b6e26e50a396f 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs @@ -178,7 +178,7 @@ pub fn expand_outer_dispatch( type PostInfo = #scrate::dispatch::PostDispatchInfo; fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { if !::filter_call(&origin, &self) { - return #scrate::__private::sp_std::result::Result::Err( + return ::core::result::Result::Err( #system_path::Error::<#runtime>::CallFiltered.into() ); } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/composite_helper.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/composite_helper.rs index 3c81d2360cb7bec33b7da67de08bc24a34d5ea34..101a476fb25727277c035c5343bf829b8a44f7e1 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/composite_helper.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/composite_helper.rs @@ -68,3 +68,34 @@ pub(crate) fn expand_variant( } } } + +pub(crate) fn expand_variant_count( + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, +) -> TokenStream { + let composite_name = quote::format_ident!("{}", composite_name); + + if let Some(inst) = instance { + quote! { + #path::#composite_name::<#path::#inst>::VARIANT_COUNT + } + } else { + // Wrapped `<`..`>` means: use default type parameter for enum. + // + // This is used for pallets without instance support or pallets with instance support when + // we don't specify instance: + // + // ``` + // pub struct Pallet{..} + // + // #[pallet::composite_enum] + // pub enum HoldReason {..} + // + // Pallet1: pallet_x, // <- default type parameter + // ``` + quote! { + <#path::#composite_name>::VARIANT_COUNT + } + } +} diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs index ffe55bceb80ef96bc9e7814cfbd8bec6c62ab562..dbbe6ba6e6c32ec09a8514033108617883075243 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs @@ -76,10 +76,6 @@ pub fn expand_outer_config( #fields } - #[cfg(any(feature = "std", test))] - #[deprecated(note = "GenesisConfig is planned to be removed in December 2023. Use `RuntimeGenesisConfig` instead.")] - pub type GenesisConfig = RuntimeGenesisConfig; - #[cfg(any(feature = "std", test))] impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { fn assimilate_storage( @@ -99,6 +95,17 @@ pub fn expand_outer_config( ::on_genesis(); } } + + /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. + #[cfg(test)] + #[test] + fn test_genesis_config_builds() { + #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { + ::build( + &RuntimeGenesisConfig::default() + ); + }); + } } } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs index 55696cc6c6e3c30ef162a05b12992951a652fa2c..f12f995266422e2a6337114d09e193d267885b24 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/freeze_reason.rs @@ -23,6 +23,7 @@ use quote::quote; pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { let mut conversion_fns = Vec::new(); let mut freeze_reason_variants = Vec::new(); + let mut freeze_reason_variants_count = Vec::new(); for decl in pallet_decls { if let Some(_) = decl.find_part("FreezeReason") { let variant_name = &decl.name; @@ -44,9 +45,14 @@ pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) instance, variant_name, )); + + freeze_reason_variants_count.push(composite_helper::expand_variant_count( + "FreezeReason", + path, + instance, + )); } } - let freeze_reason_variants_count = freeze_reason_variants.len() as u32; quote! { /// A reason for placing a freeze on funds. @@ -61,7 +67,7 @@ pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) } impl #scrate::traits::VariantCount for RuntimeFreezeReason { - const VARIANT_COUNT: u32 = #freeze_reason_variants_count; + const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; } #( #conversion_fns )* diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs index 3856c4a2bb945b6b80a102e4bba5de9300836c43..cdab92712fddcadbaee67c48119c7bf8c527e6cc 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/hold_reason.rs @@ -23,6 +23,7 @@ use quote::quote; pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { let mut conversion_fns = Vec::new(); let mut hold_reason_variants = Vec::new(); + let mut hold_reason_variants_count = Vec::new(); for decl in pallet_decls { if let Some(_) = decl.find_part("HoldReason") { let variant_name = &decl.name; @@ -44,9 +45,14 @@ pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) - instance, variant_name, )); + + hold_reason_variants_count.push(composite_helper::expand_variant_count( + "HoldReason", + path, + instance, + )); } } - let hold_reason_variants_count = hold_reason_variants.len() as u32; quote! { /// A reason for placing a hold on funds. @@ -61,7 +67,7 @@ pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) - } impl #scrate::traits::VariantCount for RuntimeHoldReason { - const VARIANT_COUNT: u32 = #hold_reason_variants_count; + const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; } #( #conversion_fns )* diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs index 34b9d21d8ce84fafb7958278227a3f572d8d2f74..d21cfef4a927c8033c393719247d9c5a09d83e1a 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs @@ -73,11 +73,9 @@ pub fn expand_outer_inherent( #( #pallet_attrs if let Some(inherent) = #pallet_names::create_inherent(self) { - let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( + let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new_inherent( inherent.into(), - None, - ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ - `Some`; qed"); + ); inherents.push(inherent); } @@ -123,7 +121,7 @@ pub fn expand_outer_inherent( for xt in block.extrinsics() { // Inherents are before any other extrinsics. // And signed extrinsics are not inherents. - if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { + if !(#scrate::sp_runtime::traits::Extrinsic::is_bare(xt)) { break } @@ -161,10 +159,9 @@ pub fn expand_outer_inherent( match #pallet_names::is_inherent_required(self) { Ok(Some(e)) => { let found = block.extrinsics().iter().any(|xt| { - let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) - .unwrap_or(false); + let is_bare = #scrate::sp_runtime::traits::Extrinsic::is_bare(xt); - if !is_signed { + if is_bare { let call = < #unchecked_extrinsic as ExtrinsicCall >::call(xt); @@ -204,47 +201,51 @@ pub fn expand_outer_inherent( } } + impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { + fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + + let is_bare = #scrate::sp_runtime::traits::Extrinsic::is_bare(ext); + if !is_bare { + // Signed extrinsics are not inherents. + return false + } + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if <#pallet_names as ProvideInherent>::is_inherent(&call) { + return true; + } + } + } + )* + false + } + } + impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { - fn ensure_inherents_are_first(block: &#block) -> Result<(), u32> { + fn ensure_inherents_are_first(block: &#block) -> Result { use #scrate::inherent::ProvideInherent; use #scrate::traits::{IsSubType, ExtrinsicCall}; use #scrate::sp_runtime::traits::Block as _; - let mut first_signed_observed = false; + let mut num_inherents = 0u32; for (i, xt) in block.extrinsics().iter().enumerate() { - let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) - .unwrap_or(false); - - let is_inherent = if is_signed { - // Signed extrinsics are not inherents. - false - } else { - let mut is_inherent = false; - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if #pallet_names::is_inherent(&call) { - is_inherent = true; - } - } - } - )* - is_inherent - }; - - if !is_inherent { - first_signed_observed = true; - } + if >::is_inherent(xt) { + if num_inherents != i as u32 { + return Err(i as u32); + } - if first_signed_observed && is_inherent { - return Err(i as u32) + num_inherents += 1; // Safe since we are in an `enumerate` loop. } } - Ok(()) + Ok(num_inherents) } } } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs index 0e76f9a92469a73d3a616da454dc46ded5034afc..f55ca677d89c372261a885572d48b36f395d1a5b 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -119,13 +119,13 @@ pub fn expand_runtime_metadata( call_ty, signature_ty, extra_ty, - signed_extensions: < + extensions: < < #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata - >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension + >::Extra as #scrate::sp_runtime::traits::TransactionExtensionBase >::metadata() .into_iter() - .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { + .map(|meta| #scrate::__private::metadata_ir::TransactionExtensionMetadataIR { identifier: meta.identifier, ty: meta.ty, additional_signed: meta.additional_signed, diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs index b421d2aaffabe077450d81e3396976f3067b264a..341621deb098c1905557bd953d750283dc45ae51 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs @@ -104,7 +104,7 @@ pub fn expand_outer_origin( #[doc = #doc_string] #[derive(Clone)] pub struct RuntimeOrigin { - caller: OriginCaller, + pub caller: OriginCaller, filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, } @@ -153,6 +153,10 @@ pub fn expand_outer_origin( self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); } + fn set_caller(&mut self, caller: OriginCaller) { + self.caller = caller; + } + fn set_caller_from(&mut self, other: impl Into) { self.caller = other.into().caller; } @@ -301,6 +305,16 @@ pub fn expand_outer_origin( } } + impl #scrate::__private::AsSystemOriginSigner<<#runtime as #system_path::Config>::AccountId> for RuntimeOrigin { + fn as_system_origin_signer(&self) -> Option<&<#runtime as #system_path::Config>::AccountId> { + if let OriginCaller::system(#system_path::Origin::<#runtime>::Signed(ref signed)) = &self.caller { + Some(signed) + } else { + None + } + } + } + #pallet_conversions }) } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/outer_enums.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/outer_enums.rs index df69c19a4b617bbe7194bb5958848fc04d8d546a..80b242ccbe493607a59e672b7f0958fc9d0a213c 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/outer_enums.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/outer_enums.rs @@ -134,7 +134,6 @@ pub fn expand_outer_enum( enum_ty, )); enum_conversions.extend(expand_enum_conversion( - scrate, pallet_decl, &pallet_enum, &enum_name_ident, @@ -220,7 +219,6 @@ fn expand_enum_variant( } fn expand_enum_conversion( - scrate: &TokenStream, pallet: &Pallet, pallet_enum: &TokenStream, enum_name_ident: &Ident, @@ -247,7 +245,7 @@ fn expand_enum_conversion( impl TryInto<#pallet_enum> for #enum_name_ident { type Error = (); - fn try_into(self) -> #scrate::__private::sp_std::result::Result<#pallet_enum, Self::Error> { + fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { match self { Self::#variant_name(evt) => Ok(evt), _ => Err(()), diff --git a/substrate/frame/support/procedural/src/construct_runtime/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/mod.rs index 54ed15f7b1d36d6ee5eb724381152af03bef60c8..6e95fdf116a6fefe92e3fa94f91f4424013aa35f 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/mod.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/mod.rs @@ -233,25 +233,38 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { let input_copy = input.clone(); let definition = syn::parse_macro_input!(input as RuntimeDeclaration); - let res = match definition { - RuntimeDeclaration::Implicit(implicit_def) => - check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()).and_then( - |_| construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), - ), - RuntimeDeclaration::Explicit(explicit_decl) => check_pallet_number( - input_copy.clone().into(), - explicit_decl.pallets.len(), - ) - .and_then(|_| { - construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl) - }), - RuntimeDeclaration::ExplicitExpanded(explicit_decl) => - check_pallet_number(input_copy.into(), explicit_decl.pallets.len()) - .and_then(|_| construct_runtime_final_expansion(explicit_decl)), + let (check_pallet_number_res, res) = match definition { + RuntimeDeclaration::Implicit(implicit_def) => ( + check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), + construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), + ), + RuntimeDeclaration::Explicit(explicit_decl) => ( + check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), + construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), + ), + RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( + check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), + construct_runtime_final_expansion(explicit_decl), + ), }; let res = res.unwrap_or_else(|e| e.to_compile_error()); + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + let res = expander::Expander::new("construct_runtime") .dry(std::env::var("EXPAND_MACROS").is_err()) .verbose(true) diff --git a/substrate/frame/support/procedural/src/derive_impl.rs b/substrate/frame/support/procedural/src/derive_impl.rs index d6d5bf68efd5689af2e96250e24cef3cd7faf47b..8740ccd401abedcbcb27e19c77ef3e885207342a 100644 --- a/substrate/frame/support/procedural/src/derive_impl.rs +++ b/substrate/frame/support/procedural/src/derive_impl.rs @@ -172,6 +172,33 @@ fn combine_impls( final_impl } +/// Computes the disambiguation path for the `derive_impl` attribute macro. +/// +/// When specified explicitly using `as [disambiguation_path]` in the macro attr, the +/// disambiguation is used as is. If not, we infer the disambiguation path from the +/// `foreign_impl_path` and the computed scope. +fn compute_disambiguation_path( + disambiguation_path: Option, + foreign_impl: ItemImpl, + default_impl_path: Path, +) -> Result { + match (disambiguation_path, foreign_impl.clone().trait_) { + (Some(disambiguation_path), _) => Ok(disambiguation_path), + (None, Some((_, foreign_impl_path, _))) => + if default_impl_path.segments.len() > 1 { + let scope = default_impl_path.segments.first(); + Ok(parse_quote!(#scope :: #foreign_impl_path)) + } else { + Ok(foreign_impl_path) + }, + _ => Err(syn::Error::new( + default_impl_path.span(), + "Impl statement must have a defined type being implemented \ + for a defined type such as `impl A for B`", + )), + } +} + /// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`). /// /// `default_impl_path`: the module path of the external `impl` statement whose tokens we are @@ -194,18 +221,11 @@ pub fn derive_impl( let foreign_impl = parse2::(foreign_tokens)?; let default_impl_path = parse2::(default_impl_path)?; - // have disambiguation_path default to the item being impl'd in the foreign impl if we - // don't specify an `as [disambiguation_path]` in the macro attr - let disambiguation_path = match (disambiguation_path, foreign_impl.clone().trait_) { - (Some(disambiguation_path), _) => disambiguation_path, - (None, Some((_, foreign_impl_path, _))) => foreign_impl_path, - _ => - return Err(syn::Error::new( - foreign_impl.span(), - "Impl statement must have a defined type being implemented \ - for a defined type such as `impl A for B`", - )), - }; + let disambiguation_path = compute_disambiguation_path( + disambiguation_path, + foreign_impl.clone(), + default_impl_path.clone(), + )?; // generate the combined impl let combined_impl = combine_impls( @@ -257,3 +277,27 @@ fn test_runtime_type_with_doc() { } } } + +#[test] +fn test_disambugation_path() { + let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); + let default_impl_path: Path = parse_quote!(SomeScope::SomeType); + + // disambiguation path is specified + let disambiguation_path = compute_disambiguation_path( + Some(parse_quote!(SomeScope::SomePath)), + foreign_impl.clone(), + default_impl_path.clone(), + ); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath)); + + // disambiguation path is not specified and the default_impl_path has more than one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait)); + + // disambiguation path is not specified and the default_impl_path has only one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); +} diff --git a/substrate/frame/support/procedural/src/dynamic_params.rs b/substrate/frame/support/procedural/src/dynamic_params.rs new file mode 100644 index 0000000000000000000000000000000000000000..b718ccbc9558480587a1f50f4e85a992e5c750da --- /dev/null +++ b/substrate/frame/support/procedural/src/dynamic_params.rs @@ -0,0 +1,563 @@ +// 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. + +//! Code for the `#[dynamic_params]`, `#[dynamic_pallet_params]` and +//! `#[dynamic_aggregated_params_internal]` macros. + +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use inflector::Inflector; +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, ToTokens}; +use syn::{parse2, spanned::Spanned, visit_mut, visit_mut::VisitMut, Result, Token}; + +/// Parse and expand a `#[dynamic_params(..)]` module. +pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result { + DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) +} + +/// Parse and expand `#[dynamic_pallet_params(..)]` attribute. +pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result { + DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) +} + +/// Parse and expand `#[dynamic_aggregated_params_internal]` attribute. +pub fn dynamic_aggregated_params_internal( + _attr: TokenStream, + item: TokenStream, +) -> Result { + parse2::(item).map(ToTokens::into_token_stream) +} + +/// A top `#[dynamic_params(..)]` attribute together with a mod. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicParamModAttr { + params_mod: syn::ItemMod, + meta: DynamicParamModAttrMeta, +} + +/// The inner meta of a `#[dynamic_params(..)]` attribute. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicParamModAttrMeta { + name: syn::Ident, + _comma: Option, + #[parse_if(_comma.is_some())] + params_pallet: Option, +} + +impl DynamicParamModAttr { + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + let params_mod = parse2(item)?; + let meta = parse2(attr)?; + Ok(Self { params_mod, meta }) + } + + pub fn inner_mods(&self) -> Vec { + self.params_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Mod(m) => Some(m), + _ => None, + }) + .cloned() + .collect() + }) + } +} + +impl ToTokens for DynamicParamModAttr { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); + let dynam_params_ident = ¶ms_mod.ident; + + let mut quoted_enum = quote! {}; + for m in self.inner_mods() { + let aggregate_name = + syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); + let mod_name = &m.ident; + + let mut attrs = m.attrs.clone(); + attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); + if let Err(err) = ensure_codec_index(&attrs, m.span()) { + tokens.extend(err.into_compile_error()); + return + } + + quoted_enum.extend(quote! { + #(#attrs)* + #aggregate_name(#dynam_params_ident::#mod_name::Parameters), + }); + } + + // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. + if let Some(params_pallet) = &self.meta.params_pallet { + MacroInjectArgs { runtime_params: name.clone(), params_pallet: params_pallet.clone() } + .visit_item_mod_mut(&mut params_mod); + } + + tokens.extend(quote! { + #params_mod + + #[#scrate::dynamic_params::dynamic_aggregated_params_internal] + pub enum #name { + #quoted_enum + } + }); + } +} + +/// Ensure there is a `#[codec(index = ..)]` attribute. +fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { + let mut found = false; + + for attr in attrs.iter() { + if attr.path().is_ident("codec") { + let meta: syn::ExprAssign = attr.parse_args()?; + if meta.left.to_token_stream().to_string() == "index" { + found = true; + break + } + } + } + + if !found { + Err(syn::Error::new(span, "Missing explicit `#[codec(index = ..)]` attribute")) + } else { + Ok(()) + } +} + +/// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute. +/// +/// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that dont need +/// to be repeated every time. +struct MacroInjectArgs { + runtime_params: syn::Ident, + params_pallet: syn::Type, +} +impl VisitMut for MacroInjectArgs { + fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { + // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. + let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params")); + + if let Some(attr) = attr { + match &attr.meta { + syn::Meta::Path(path) => + assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params"), + _ => (), + } + + let runtime_params = &self.runtime_params; + let params_pallet = &self.params_pallet; + + attr.meta = syn::parse2::(quote! { + dynamic_pallet_params(#runtime_params, #params_pallet) + }) + .unwrap() + .into(); + } + + visit_mut::visit_item_mod_mut(self, item); + } +} +/// The helper attribute of a `#[dynamic_pallet_params(runtime_params, params_pallet)]` +/// attribute. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicPalletParamAttr { + inner_mod: syn::ItemMod, + meta: DynamicPalletParamAttrMeta, +} + +/// The inner meta of a `#[dynamic_pallet_params(..)]` attribute. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicPalletParamAttrMeta { + runtime_params: syn::Ident, + _comma: Token![,], + parameter_pallet: syn::Type, +} + +impl DynamicPalletParamAttr { + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + Ok(Self { inner_mod: parse2(item)?, meta: parse2(attr)? }) + } + + pub fn statics(&self) -> Vec { + self.inner_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Static(s) => Some(s), + _ => None, + }) + .cloned() + .collect() + }) + } +} + +impl ToTokens for DynamicPalletParamAttr { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (params_mod, parameter_pallet, runtime_params) = + (&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params); + + let aggregate_name = + syn::Ident::new(¶ms_mod.ident.to_string().to_class_case(), params_mod.ident.span()); + let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); + let statics = self.statics(); + + let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = Default::default(); + + for s in statics.iter() { + if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { + tokens.extend(err.into_compile_error()); + return + } + + key_names.push(&s.ident); + key_values.push(format_ident!("{}Value", &s.ident)); + defaults.push(&s.expr); + attrs.push(&s.attrs); + value_types.push(&s.ty); + } + + let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); + let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); + let runtime_key_ident = format_ident!("{}Key", runtime_params); + let runtime_value_ident = format_ident!("{}Value", runtime_params); + + tokens.extend(quote! { + pub mod #mod_name { + use super::*; + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum Parameters { + #( + #(#attrs)* + #key_names(#key_names, Option<#value_types>), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #key_ident { + #( + #(#attrs)* + #key_names(#key_names), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #value_ident { + #( + #(#attrs)* + #key_names(#value_types), + )* + } + + impl #scrate::traits::dynamic_params::AggregratedKeyValue for Parameters { + type Key = #key_ident; + type Value = #value_ident; + + fn into_parts(self) -> (Self::Key, Option) { + match self { + #( + Parameters::#key_names(key, value) => { + (#key_ident::#key_names(key), value.map(#value_ident::#key_names)) + }, + )* + } + } + } + + #( + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis struct #key_names; + + impl #scrate::__private::Get<#value_types> for #key_names { + fn get() -> #value_types { + match + <#parameter_pallet as + #scrate::storage::StorageMap<#runtime_key_ident, #runtime_value_ident> + >::get(#runtime_key_ident::#aggregate_name(#key_ident::#key_names(#key_names))) + { + Some(#runtime_value_ident::#aggregate_name( + #value_ident::#key_names(inner))) => inner, + Some(_) => { + #scrate::defensive!("Unexpected value type at key - returning default"); + #defaults + }, + None => #defaults, + } + } + } + + impl #scrate::traits::dynamic_params::Key for #key_names { + type Value = #value_types; + type WrappedValue = #key_values; + } + + impl From<#key_names> for #key_ident { + fn from(key: #key_names) -> Self { + #key_ident::#key_names(key) + } + } + + impl TryFrom<#key_ident> for #key_names { + type Error = (); + + fn try_from(key: #key_ident) -> Result { + match key { + #key_ident::#key_names(key) => Ok(key), + _ => Err(()), + } + } + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::sp_runtime::RuntimeDebug, + )] + #vis struct #key_values(pub #value_types); + + impl From<#key_values> for #value_ident { + fn from(value: #key_values) -> Self { + #value_ident::#key_names(value.0) + } + } + + impl From<(#key_names, #value_types)> for Parameters { + fn from((key, value): (#key_names, #value_types)) -> Self { + Parameters::#key_names(key, Some(value)) + } + } + + impl From<#key_names> for Parameters { + fn from(key: #key_names) -> Self { + Parameters::#key_names(key, None) + } + } + + impl TryFrom<#value_ident> for #key_values { + type Error = (); + + fn try_from(value: #value_ident) -> Result { + match value { + #value_ident::#key_names(value) => Ok(#key_values(value)), + _ => Err(()), + } + } + } + + impl From<#key_values> for #value_types { + fn from(value: #key_values) -> Self { + value.0 + } + } + )* + } + }); + } +} + +#[derive(derive_syn_parse::Parse)] +pub struct DynamicParamAggregatedEnum { + aggregated_enum: syn::ItemEnum, +} + +impl ToTokens for DynamicParamAggregatedEnum { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let params_enum = &self.aggregated_enum; + let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); + + let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = + Default::default(); + let mut attributes = Vec::new(); + for (i, variant) in params_enum.variants.iter().enumerate() { + indices.push(i); + param_names.push(&variant.ident); + attributes.push(&variant.attrs); + + param_types.push(match &variant.fields { + syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, + _ => { + *tokens = quote! { compile_error!("Only unnamed enum variants with one inner item are supported") }; + return + }, + }); + } + + let params_key_ident = format_ident!("{}Key", params_enum.ident); + let params_value_ident = format_ident!("{}Value", params_enum.ident); + + tokens.extend(quote! { + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #name { + #( + //#[codec(index = #indices)] + #(#attributes)* + #param_names(#param_types), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #params_key_ident { + #( + #(#attributes)* + #param_names(<#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Key), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #params_value_ident { + #( + #(#attributes)* + #param_names(<#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Value), + )* + } + + impl #scrate::traits::dynamic_params::AggregratedKeyValue for #name { + type Key = #params_key_ident; + type Value = #params_value_ident; + + fn into_parts(self) -> (Self::Key, Option) { + match self { + #( + #name::#param_names(parameter) => { + let (key, value) = parameter.into_parts(); + (#params_key_ident::#param_names(key), value.map(#params_value_ident::#param_names)) + }, + )* + } + } + } + + #( + impl ::core::convert::From<<#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Key> for #params_key_ident { + fn from(key: <#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Key) -> Self { + #params_key_ident::#param_names(key) + } + } + + impl ::core::convert::TryFrom<#params_value_ident> for <#param_types as #scrate::traits::dynamic_params::AggregratedKeyValue>::Value { + type Error = (); + + fn try_from(value: #params_value_ident) -> Result { + match value { + #params_value_ident::#param_names(value) => Ok(value), + _ => Err(()), + } + } + } + )* + }); + } +} + +/// Get access to the current crate and convert the error to a compile error. +fn crate_access() -> core::result::Result { + generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) +} diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs index 349b6ee6599c27539f9fc11d50521b64cc14ee86..036da52a7badc4c31c28db364c819bef29bb1f62 100644 --- a/substrate/frame/support/procedural/src/lib.rs +++ b/substrate/frame/support/procedural/src/lib.rs @@ -18,12 +18,14 @@ //! Proc macro of Support code for the runtime. #![recursion_limit = "512"] +#![deny(rustdoc::broken_intra_doc_links)] mod benchmark; mod construct_runtime; mod crate_version; mod derive_impl; mod dummy_part_checker; +mod dynamic_params; mod key_prefix; mod match_and_insert; mod no_bound; @@ -186,133 +188,11 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { construct_runtime::construct_runtime(input) } -/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to specify -/// pallet information. /// -/// The struct must be defined as follows: -/// ```ignore -/// #[pallet::pallet] -/// pub struct Pallet(_); -/// ``` -/// I.e. a regular struct definition named `Pallet`, with generic T and no where clause. -/// -/// ## Macro expansion: -/// -/// The macro adds this attribute to the struct definition: -/// ```ignore -/// #[derive( -/// frame_support::CloneNoBound, -/// frame_support::EqNoBound, -/// frame_support::PartialEqNoBound, -/// frame_support::RuntimeDebugNoBound, -/// )] -/// ``` -/// and replaces the type `_` with `PhantomData`. It also implements on the pallet: -/// * `GetStorageVersion` -/// * `OnGenesis`: contains some logic to write the pallet version into storage. -/// * `PalletErrorTypeInfo`: provides the type information for the pallet error, if defined. -/// -/// It declares `type Module` type alias for `Pallet`, used by `construct_runtime`. -/// -/// It implements `PalletInfoAccess` on `Pallet` to ease access to pallet information given by -/// `frame_support::traits::PalletInfo`. (The implementation uses the associated type -/// `frame_system::Config::PalletInfo`). -/// -/// It implements `StorageInfoTrait` on `Pallet` which give information about all storages. -/// -/// If the attribute `generate_store` is set then the macro creates the trait `Store` and -/// implements it on `Pallet`. -/// -/// If the attribute `set_storage_max_encoded_len` is set then the macro calls -/// `StorageInfoTrait` for each storage in the implementation of `StorageInfoTrait` for the -/// pallet. Otherwise it implements `StorageInfoTrait` for the pallet using the -/// `PartialStorageInfoTrait` implementation of storages. -/// -/// ## Dev Mode (`#[pallet(dev_mode)]`) -/// -/// Specifying the argument `dev_mode` will allow you to enable dev mode for a pallet. The aim -/// of dev mode is to loosen some of the restrictions and requirements placed on production -/// pallets for easy tinkering and development. Dev mode pallets should not be used in -/// production. Enabling dev mode has the following effects: -/// -/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By default, dev -/// mode pallets will assume a weight of zero (`0`) if a weight is not specified. This is -/// equivalent to specifying `#[weight(0)]` on all calls that do not specify a weight. -/// * Call indices no longer need to be specified on every `#[pallet::call]` declaration. By -/// default, dev mode pallets will assume a call index based on the order of the call. -/// * All storages are marked as unbounded, meaning you do not need to implement `MaxEncodedLen` on -/// storage types. This is equivalent to specifying `#[pallet::unbounded]` on all storage type -/// definitions. -/// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, these -/// will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` can simply -/// be ignored when in `dev_mode`. -/// -/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or -/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument -/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]` -/// attribute macro. -/// -///

-/// WARNING:
-/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
-/// and therefore should never be done. Once you are done tinkering, you should remove the
-/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
-/// attempting to use your pallet in a production scenario.
-/// 
-/// -/// See `frame_support::pallet` docs for more info. -/// -/// ## Runtime Metadata Documentation -/// -/// The documentation added to this pallet is included in the runtime metadata. -/// -/// The documentation can be defined in the following ways: -/// -/// ```ignore -/// #[pallet::pallet] -/// /// Documentation for pallet 1 -/// #[doc = "Documentation for pallet 2"] -/// #[doc = include_str!("../README.md")] -/// #[pallet_doc("../doc1.md")] -/// #[pallet_doc("../doc2.md")] -/// pub mod pallet {} -/// ``` -/// -/// The runtime metadata for this pallet contains the following -/// - " Documentation for pallet 1" (captured from `///`) -/// - "Documentation for pallet 2" (captured from `#[doc]`) -/// - content of ../README.md (captured from `#[doc]` with `include_str!`) -/// - content of "../doc1.md" (captured from `pallet_doc`) -/// - content of "../doc2.md" (captured from `pallet_doc`) -/// -/// ### `doc` attribute -/// -/// The value of the `doc` attribute is included in the runtime metadata, as well as -/// expanded on the pallet module. The previous example is expanded to: -/// -/// ```ignore -/// /// Documentation for pallet 1 -/// /// Documentation for pallet 2 -/// /// Content of README.md -/// pub mod pallet {} -/// ``` -/// -/// If you want to specify the file from which the documentation is loaded, you can use the -/// `include_str` macro. However, if you only want the documentation to be included in the -/// runtime metadata, use the `pallet_doc` attribute. -/// -/// ### `pallet_doc` attribute -/// -/// Unlike the `doc` attribute, the documentation provided to the `pallet_doc` attribute is -/// not inserted on the module. -/// -/// The `pallet_doc` attribute can only be provided with one argument, -/// which is the file path that holds the documentation to be added to the metadata. +/// --- /// -/// This approach is beneficial when you use the `include_str` macro at the beginning of the file -/// and want that documentation to extend to the runtime metadata, without reiterating the -/// documentation on the pallet module itself. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet`. #[proc_macro_attribute] pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) @@ -406,19 +286,28 @@ pub fn transactional(attr: TokenStream, input: TokenStream) -> TokenStream { transactional::transactional(attr, input).unwrap_or_else(|e| e.to_compile_error().into()) } +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::require_transactional`. #[proc_macro_attribute] pub fn require_transactional(attr: TokenStream, input: TokenStream) -> TokenStream { transactional::require_transactional(attr, input) .unwrap_or_else(|e| e.to_compile_error().into()) } -/// Derive [`Clone`] but do not bound any generic. Docs are at `frame_support::CloneNoBound`. +/// Derive [`Clone`] but do not bound any generic. +/// +/// Docs at `frame_support::CloneNoBound`. #[proc_macro_derive(CloneNoBound)] pub fn derive_clone_no_bound(input: TokenStream) -> TokenStream { no_bound::clone::derive_clone_no_bound(input) } -/// Derive [`Debug`] but do not bound any generics. Docs are at `frame_support::DebugNoBound`. +/// Derive [`Debug`] but do not bound any generics. +/// +/// Docs at `frame_support::DebugNoBound`. #[proc_macro_derive(DebugNoBound)] pub fn derive_debug_no_bound(input: TokenStream) -> TokenStream { no_bound::debug::derive_debug_no_bound(input) @@ -432,10 +321,7 @@ pub fn derive_runtime_debug_no_bound(input: TokenStream) -> TokenStream { if cfg!(any(feature = "std", feature = "try-runtime")) { no_bound::debug::derive_debug_no_bound(input) } else { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input = syn::parse_macro_input!(input as syn::DeriveInput); let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); @@ -453,20 +339,20 @@ pub fn derive_runtime_debug_no_bound(input: TokenStream) -> TokenStream { } } -/// Derive [`PartialEq`] but do not bound any generic. Docs are at -/// `frame_support::PartialEqNoBound`. +/// Derive [`PartialEq`] but do not bound any generic. +/// +/// Docs at `frame_support::PartialEqNoBound`. #[proc_macro_derive(PartialEqNoBound)] pub fn derive_partial_eq_no_bound(input: TokenStream) -> TokenStream { no_bound::partial_eq::derive_partial_eq_no_bound(input) } -/// derive Eq but do no bound any generic. Docs are at `frame_support::EqNoBound`. +/// DeriveEq but do no bound any generic. +/// +/// Docs at `frame_support::EqNoBound`. #[proc_macro_derive(EqNoBound)] pub fn derive_eq_no_bound(input: TokenStream) -> TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input = syn::parse_macro_input!(input as syn::DeriveInput); let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); @@ -479,12 +365,26 @@ pub fn derive_eq_no_bound(input: TokenStream) -> TokenStream { .into() } +/// Derive [`PartialOrd`] but do not bound any generic. Docs are at +/// `frame_support::PartialOrdNoBound`. +#[proc_macro_derive(PartialOrdNoBound)] +pub fn derive_partial_ord_no_bound(input: TokenStream) -> TokenStream { + no_bound::partial_ord::derive_partial_ord_no_bound(input) +} + +/// Derive [`Ord`] but do no bound any generic. Docs are at `frame_support::OrdNoBound`. +#[proc_macro_derive(OrdNoBound)] +pub fn derive_ord_no_bound(input: TokenStream) -> TokenStream { + no_bound::ord::derive_ord_no_bound(input) +} + /// derive `Default` but do no bound any generic. Docs are at `frame_support::DefaultNoBound`. #[proc_macro_derive(DefaultNoBound, attributes(default))] pub fn derive_default_no_bound(input: TokenStream) -> TokenStream { no_bound::default::derive_default_no_bound(input) } +/// Macro used internally in FRAME to generate the crate version for a pallet. #[proc_macro] pub fn crate_to_crate_version(input: TokenStream) -> TokenStream { crate_version::crate_to_crate_version(input) @@ -546,6 +446,11 @@ pub fn __create_tt_macro(input: TokenStream) -> TokenStream { tt_macro::create_tt_return_macro(input) } +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::storage_alias`. #[proc_macro_attribute] pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream { storage_alias::storage_alias(attributes.into(), input.into()) @@ -594,6 +499,11 @@ pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream /// default to `A` from the `impl A for B` part of the default impl. This is useful for scenarios /// where all of the relevant types are already in scope via `use` statements. /// +/// In case the `default_impl_path` is scoped to a different module such as +/// `some::path::TestTraitImpl`, the same scope is assumed for the `disambiguation_path`, i.e. +/// `some::A`. This enables the use of `derive_impl` attribute without having to specify the +/// `disambiguation_path` in most (if not all) uses within FRAME's context. +/// /// Conversely, the `default_impl_path` argument is required and cannot be omitted. /// /// Optionally, `no_aggregated_types` can be specified as follows: @@ -776,24 +686,21 @@ pub fn derive_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { .into() } -/// The optional attribute `#[pallet::no_default]` can be attached to trait items within a -/// `Config` trait impl that has [`#[pallet::config(with_default)]`](`macro@config`) attached. /// -/// Attaching this attribute to a trait item ensures that that trait item will not be used as a -/// default with the [`#[derive_impl(..)]`](`macro@derive_impl`) attribute macro. +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::no_default`. #[proc_macro_attribute] pub fn no_default(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::no_default_bounds]` can be attached to trait items within a -/// `Config` trait impl that has [`#[pallet::config(with_default)]`](`macro@config`) attached. /// -/// Attaching this attribute to a trait item ensures that the generated trait `DefaultConfig` -/// will not have any bounds for this trait item. +/// --- /// -/// As an example, if you have a trait item `type AccountId: SomeTrait;` in your `Config` trait, -/// the generated `DefaultConfig` will only have `type AccountId;` with no trait bound. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::no_default_bounds`. #[proc_macro_attribute] pub fn no_default_bounds(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -873,6 +780,11 @@ pub fn register_default_impl(attrs: TokenStream, tokens: TokenStream) -> TokenSt } } +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_prelude::inject_runtime_type`. #[proc_macro_attribute] pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream { let item = tokens.clone(); @@ -883,12 +795,13 @@ pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream { item.ident != "RuntimeOrigin" && item.ident != "RuntimeHoldReason" && item.ident != "RuntimeFreezeReason" && + item.ident != "RuntimeParameters" && item.ident != "PalletInfo" { return syn::Error::new_spanned( item, "`#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, \ - `RuntimeTask`, `RuntimeOrigin` or `PalletInfo`", + `RuntimeTask`, `RuntimeOrigin`, `RuntimeParameters` or `PalletInfo`", ) .to_compile_error() .into() @@ -905,75 +818,11 @@ fn pallet_macro_stub() -> TokenStream { .into() } -/// The mandatory attribute `#[pallet::config]` defines the configurable options for the pallet. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::config] -/// pub trait Config: frame_system::Config + $optionally_some_other_supertraits -/// $optional_where_clause -/// { -/// ... -/// } -/// ``` /// -/// I.e. a regular trait definition named `Config`, with the supertrait -/// `frame_system::pallet::Config`, and optionally other supertraits and a where clause. -/// (Specifying other supertraits here is known as [tight -/// coupling](https://docs.substrate.io/reference/how-to-guides/pallet-design/use-tight-coupling/)) -/// -/// The associated type `RuntimeEvent` is reserved. If defined, it must have the bounds -/// `From` and `IsType<::RuntimeEvent>`. -/// -/// [`pallet::event`](`macro@event`) must be present if `RuntimeEvent` exists as a config item -/// in your `#[pallet::config]`. -/// -/// ## Optional: `with_default` -/// -/// An optional `with_default` argument may also be specified. Doing so will automatically -/// generate a `DefaultConfig` trait inside your pallet which is suitable for use with -/// [`[#[derive_impl(..)]`](`macro@derive_impl`) to derive a default testing config: -/// -/// ```ignore -/// #[pallet::config(with_default)] -/// pub trait Config: frame_system::Config { -/// type RuntimeEvent: Parameter -/// + Member -/// + From> -/// + Debug -/// + IsType<::RuntimeEvent>; -/// -/// #[pallet::no_default] -/// type BaseCallFilter: Contains; -/// // ... -/// } -/// ``` -/// -/// As shown above, you may also attach the [`#[pallet::no_default]`](`macro@no_default`) -/// attribute to specify that a particular trait item _cannot_ be used as a default when a test -/// `Config` is derived using the [`#[derive_impl(..)]`](`macro@derive_impl`) attribute macro. -/// This will cause that particular trait item to simply not appear in default testing configs -/// based on this config (the trait item will not be included in `DefaultConfig`). -/// -/// ### `DefaultConfig` Caveats -/// -/// The auto-generated `DefaultConfig` trait: -/// - is always a _subset_ of your pallet's `Config` trait. -/// - can only contain items that don't rely on externalities, such as `frame_system::Config`. -/// -/// Trait items that _do_ rely on externalities should be marked with -/// [`#[pallet::no_default]`](`macro@no_default`) -/// -/// Consequently: -/// - Any items that rely on externalities _must_ be marked with -/// [`#[pallet::no_default]`](`macro@no_default`) or your trait will fail to compile when used -/// with [`derive_impl`](`macro@derive_impl`). -/// - Items marked with [`#[pallet::no_default]`](`macro@no_default`) are entirely excluded from the -/// `DefaultConfig` trait, and therefore any impl of `DefaultConfig` doesn't need to implement -/// such items. +/// --- /// -/// For more information, see [`macro@derive_impl`]. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::config`. #[proc_macro_attribute] pub fn config(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -982,7 +831,7 @@ pub fn config(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::constant`. #[proc_macro_attribute] pub fn constant(_: TokenStream, _: TokenStream) -> TokenStream { @@ -992,106 +841,58 @@ pub fn constant(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::constant_name`. #[proc_macro_attribute] pub fn constant_name(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// To bypass the `frame_system::Config` supertrait check, use the attribute -/// `pallet::disable_frame_system_supertrait_check`, e.g.: /// -/// ```ignore -/// #[pallet::config] -/// #[pallet::disable_frame_system_supertrait_check] -/// pub trait Config: pallet_timestamp::Config {} -/// ``` +/// --- /// -/// NOTE: Bypassing the `frame_system::Config` supertrait check is typically desirable when you -/// want to write an alternative to the `frame_system` pallet. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::disable_frame_system_supertrait_check`. #[proc_macro_attribute] pub fn disable_frame_system_supertrait_check(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// To generate a `Store` trait associating all storages, annotate your `Pallet` struct with -/// the attribute `#[pallet::generate_store($vis trait Store)]`, e.g.: /// -/// ```ignore -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(_); -/// ``` -/// More precisely, the `Store` trait contains an associated type for each storage. It is -/// implemented for `Pallet` allowing access to the storage from pallet struct. -/// -/// Thus when defining a storage named `Foo`, it can later be accessed from `Pallet` using -/// `::Foo`. +/// --- /// -/// NOTE: this attribute is only valid when applied _directly_ to your `Pallet` struct -/// definition. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::generate_store`. #[proc_macro_attribute] pub fn generate_store(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Because the `pallet::pallet` macro implements `GetStorageVersion`, the current storage -/// version needs to be communicated to the macro. This can be done by using the -/// `pallet::storage_version` attribute: /// -/// ```ignore -/// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); -/// -/// #[pallet::pallet] -/// #[pallet::storage_version(STORAGE_VERSION)] -/// pub struct Pallet(_); -/// ``` +/// --- /// -/// If not present, the current storage version is set to the default value. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::storage_version`. #[proc_macro_attribute] pub fn storage_version(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::hooks]` attribute allows you to specify a `Hooks` implementation for -/// `Pallet` that specifies pallet-specific logic. -/// -/// The item the attribute attaches to must be defined as follows: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet $optional_where_clause { -/// ... -/// } -/// ``` -/// I.e. a regular trait implementation with generic bound: `T: Config`, for the trait -/// `Hooks>` (they are defined in preludes), for the type `Pallet` and -/// with an optional where clause. -/// -/// If no `#[pallet::hooks]` exists, then the following default implementation is -/// automatically generated: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet {} -/// ``` -/// -/// ## Macro expansion /// -/// The macro implements the traits `OnInitialize`, `OnIdle`, `OnFinalize`, `OnRuntimeUpgrade`, -/// `OffchainWorker`, and `IntegrityTest` using the provided `Hooks` implementation. -/// -/// NOTE: `OnRuntimeUpgrade` is implemented with `Hooks::on_runtime_upgrade` and some -/// additional logic. E.g. logic to write the pallet version into storage. +/// --- /// -/// NOTE: The macro also adds some tracing logic when implementing the above traits. The -/// following hooks emit traces: `on_initialize`, `on_finalize` and `on_runtime_upgrade`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::hooks`. #[proc_macro_attribute] pub fn hooks(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Each dispatchable needs to define a weight with `#[pallet::weight($expr)]` attribute, the -/// first argument must be `origin: OriginFor`. +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::weight`. #[proc_macro_attribute] pub fn weight(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1100,8 +901,8 @@ pub fn weight(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// [`frame_support::pallet_macros::call`](../../frame_support/pallet_macros/attr.call.html). +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::compact`. #[proc_macro_attribute] pub fn compact(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1110,8 +911,8 @@ pub fn compact(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// [`frame_support::pallet_macros::call`](../../frame_support/pallet_macros/attr.call.html). +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::call`. #[proc_macro_attribute] pub fn call(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1122,164 +923,60 @@ pub fn call(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// [`frame_support::pallet_macros::call`](../../frame_support/pallet_macros/attr.call.html). +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::call_index`. #[proc_macro_attribute] pub fn call_index(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Each dispatchable may be annotated with the `#[pallet::feeless_if($closure)]` attribute, -/// which explicitly defines the condition for the dispatchable to be feeless. -/// -/// The arguments for the closure must be the referenced arguments of the dispatchable function. /// -/// The closure must return `bool`. -/// -/// ### Example -/// ```ignore -/// #[pallet::feeless_if(|_origin: &OriginFor, something: &u32| -> bool { -/// *something == 0 -/// })] -/// pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { -/// .... -/// } -/// ``` -/// -/// Please note that this only works for signed dispatchables and requires a signed extension -/// such as `SkipCheckIfFeeless` as defined in `pallet-skip-feeless-payment` to wrap the existing -/// payment extension. Else, this is completely ignored and the dispatchable is still charged. +/// --- /// -/// ### Macro expansion +/// Rust-Analyzer Users: Documentation for this macro can be found at /// -/// The macro implements the `CheckIfFeeless` trait on the dispatchable and calls the corresponding -/// closure in the implementation. +/// `frame_support::pallet_macros::feeless_if`. #[proc_macro_attribute] pub fn feeless_if(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Allows you to define some extra constants to be added into constant metadata. /// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::extra_constants] -/// impl Pallet where $optional_where_clause { -/// /// $some_doc -/// $vis fn $fn_name() -> $some_return_type { -/// ... -/// } -/// ... -/// } -/// ``` -/// I.e. a regular rust `impl` block with some optional where clause and functions with 0 args, -/// 0 generics, and some return type. +/// --- /// -/// ## Macro expansion +/// Rust-Analyzer Users: Documentation for this macro can be found at /// -/// The macro add some extra constants to pallet constant metadata. +/// `frame_support::pallet_macros::extra_constants`. #[proc_macro_attribute] pub fn extra_constants(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::error]` attribute allows you to define an error enum that will be returned -/// from the dispatchable when an error occurs. The information for this error type is then -/// stored in metadata. /// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::error] -/// pub enum Error { -/// /// $some_optional_doc -/// $SomeFieldLessVariant, -/// /// $some_more_optional_doc -/// $SomeVariantWithOneField(FieldType), -/// ... -/// } -/// ``` -/// I.e. a regular enum named `Error`, with generic `T` and fieldless or multiple-field -/// variants. -/// -/// Any field type in the enum variants must implement `TypeInfo` in order to be properly used -/// in the metadata, and its encoded size should be as small as possible, preferably 1 byte in -/// size in order to reduce storage size. The error enum itself has an absolute maximum encoded -/// size specified by `MAX_MODULE_ERROR_ENCODED_SIZE`. -/// -/// (1 byte can still be 256 different errors. The more specific the error, the easier it is to -/// diagnose problems and give a better experience to the user. Don't skimp on having lots of -/// individual error conditions.) -/// -/// Field types in enum variants must also implement `PalletError`, otherwise the pallet will -/// fail to compile. Rust primitive types have already implemented the `PalletError` trait -/// along with some commonly used stdlib types such as [`Option`] and `PhantomData`, and hence -/// in most use cases, a manual implementation is not necessary and is discouraged. -/// -/// The generic `T` must not bound anything and a `where` clause is not allowed. That said, -/// bounds and/or a where clause should not needed for any use-case. -/// -/// ## Macro expansion -/// -/// The macro implements the [`Debug`] trait and functions `as_u8` using variant position, and -/// `as_str` using variant doc. +/// --- /// -/// The macro also implements `From>` for `&'static str` and `From>` for -/// `DispatchError`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::error`. #[proc_macro_attribute] pub fn error(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::event]` attribute allows you to define pallet events. Pallet events are -/// stored under the `system` / `events` key when the block is applied (and then replaced when -/// the next block writes it's events). -/// -/// The Event enum must be defined as follows: -/// -/// ```ignore -/// #[pallet::event] -/// #[pallet::generate_deposit($visibility fn deposit_event)] // Optional -/// pub enum Event<$some_generic> $optional_where_clause { -/// /// Some doc -/// $SomeName($SomeType, $YetanotherType, ...), -/// ... -/// } -/// ``` /// -/// I.e. an enum (with named or unnamed fields variant), named `Event`, with generic: none or -/// `T` or `T: Config`, and optional w here clause. +/// --- /// -/// Each field must implement [`Clone`], [`Eq`], [`PartialEq`], `Encode`, `Decode`, and -/// [`Debug`] (on std only). For ease of use, bound by the trait `Member`, available in -/// `frame_support::pallet_prelude`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::event`. #[proc_macro_attribute] pub fn event(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The attribute `#[pallet::generate_deposit($visibility fn deposit_event)]` generates a -/// helper function on `Pallet` that handles deposit events. -/// -/// NOTE: For instantiable pallets, the event must be generic over `T` and `I`. -/// -/// ## Macro expansion -/// -/// The macro will add on enum `Event` the attributes: -/// * `#[derive(frame_support::CloneNoBound)]` -/// * `#[derive(frame_support::EqNoBound)]` -/// * `#[derive(frame_support::PartialEqNoBound)]` -/// * `#[derive(frame_support::RuntimeDebugNoBound)]` -/// * `#[derive(codec::Encode)]` -/// * `#[derive(codec::Decode)]` /// -/// The macro implements `From>` for (). -/// -/// The macro implements a metadata function on `Event` returning the `EventMetadata`. +/// --- /// -/// If `#[pallet::generate_deposit]` is present then the macro implements `fn deposit_event` on -/// `Pallet`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::generate_deposit`. #[proc_macro_attribute] pub fn generate_deposit(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1288,105 +985,68 @@ pub fn generate_deposit(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::storage`. #[proc_macro_attribute] pub fn storage(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::getter(fn $my_getter_fn_name)]` allows you to define a -/// getter function on `Pallet`. /// -/// Also see [`pallet::storage`](`macro@storage`) +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::getter`. #[proc_macro_attribute] pub fn getter(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::storage_prefix = "SomeName"]` allows you to define the -/// storage prefix to use. This is helpful if you wish to rename the storage field but don't -/// want to perform a migration. -/// -/// E.g: /// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::storage_prefix = "foo"] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap; -/// ``` -/// -/// or +/// --- /// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap<_, Blake2_128Concat, u32, u32>; -/// ``` +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::storage_prefix`. #[proc_macro_attribute] pub fn storage_prefix(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::unbounded]` declares the storage as unbounded. When -/// implementating the storage info (when `#[pallet::generate_storage_info]` is specified on -/// the pallet struct placeholder), the size of the storage will be declared as unbounded. This -/// can be useful for storage which can never go into PoV (Proof of Validity). +/// +/// --- +/// +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::unbounded`. #[proc_macro_attribute] pub fn unbounded(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The optional attribute `#[pallet::whitelist_storage]` will declare the -/// storage as whitelisted from benchmarking. Doing so will exclude reads of -/// that value's storage key from counting towards weight calculations during -/// benchmarking. /// -/// This attribute should only be attached to storages that are known to be -/// read/used in every block. This will result in a more accurate benchmarking weight. -/// -/// ### Example -/// ```ignore -/// #[pallet::storage] -/// #[pallet::whitelist_storage] -/// pub(super) type Number = StorageValue<_, frame_system::pallet_prelude::BlockNumberFor::, ValueQuery>; -/// ``` +/// --- /// -/// NOTE: As with all `pallet::*` attributes, this one _must_ be written as -/// `#[pallet::whitelist_storage]` and can only be placed inside a `pallet` module in order for -/// it to work properly. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::whitelist_storage`. #[proc_macro_attribute] pub fn whitelist_storage(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::type_value]` attribute lets you define a struct implementing the `Get` trait -/// to ease the use of storage types. This attribute is meant to be used alongside -/// [`#[pallet::storage]`](`macro@storage`) to define a storage's default value. This attribute -/// can be used multiple times. /// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::type_value] -/// fn $MyDefaultName<$some_generic>() -> $default_type $optional_where_clause { $expr } -/// ``` -/// -/// I.e.: a function definition with generics none or `T: Config` and a returned type. -/// -/// E.g.: +/// --- /// -/// ```ignore -/// #[pallet::type_value] -/// fn MyDefault() -> T::Balance { 3.into() } -/// ``` +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::disable_try_decode_storage`. +#[proc_macro_attribute] +pub fn disable_try_decode_storage(_: TokenStream, _: TokenStream) -> TokenStream { + pallet_macro_stub() +} + /// -/// ## Macro expansion +/// --- /// -/// The macro renames the function to some internal name, generates a struct with the original -/// name of the function and its generic, and implements `Get<$ReturnType>` by calling the user -/// defined function. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::type_value`. #[proc_macro_attribute] pub fn type_value(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1395,7 +1055,7 @@ pub fn type_value(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::genesis_config`. #[proc_macro_attribute] pub fn genesis_config(_: TokenStream, _: TokenStream) -> TokenStream { @@ -1405,114 +1065,48 @@ pub fn genesis_config(_: TokenStream, _: TokenStream) -> TokenStream { /// /// --- /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// Rust-Analyzer Users: Documentation for this macro can be found at /// `frame_support::pallet_macros::genesis_build`. #[proc_macro_attribute] pub fn genesis_build(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::inherent]` attribute allows the pallet to provide some -/// [inherent](https://docs.substrate.io/fundamentals/transaction-types/#inherent-transactions). -/// An inherent is some piece of data that is inserted by a block authoring node at block -/// creation time and can either be accepted or rejected by validators based on whether the -/// data falls within an acceptable range. -/// -/// The most common inherent is the `timestamp` that is inserted into every block. Since there -/// is no way to validate timestamps, validators simply check that the timestamp reported by -/// the block authoring node falls within an acceptable range. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::inherent] -/// impl ProvideInherent for Pallet { -/// // ... regular trait implementation -/// } -/// ``` -/// -/// I.e. a trait implementation with bound `T: Config`, of trait `ProvideInherent` for type -/// `Pallet`, and some optional where clause. /// -/// ## Macro expansion +/// --- /// -/// The macro currently makes no use of this information, but it might use this information in -/// the future to give information directly to `construct_runtime`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::inherent`. #[proc_macro_attribute] pub fn inherent(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::validate_unsigned]` attribute allows the pallet to validate some unsigned -/// transaction: -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::validate_unsigned] -/// impl ValidateUnsigned for Pallet { -/// // ... regular trait implementation -/// } -/// ``` -/// -/// I.e. a trait implementation with bound `T: Config`, of trait `ValidateUnsigned` for type -/// `Pallet`, and some optional where clause. -/// -/// NOTE: There is also the `sp_runtime::traits::SignedExtension` trait that can be used to add -/// some specific logic for transaction validation. /// -/// ## Macro expansion +/// --- /// -/// The macro currently makes no use of this information, but it might use this information in -/// the future to give information directly to `construct_runtime`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::validate_unsigned`. #[proc_macro_attribute] pub fn validate_unsigned(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::origin]` attribute allows you to define some origin for the pallet. -/// -/// Item must be either a type alias, an enum, or a struct. It needs to be public. /// -/// E.g.: -/// -/// ```ignore -/// #[pallet::origin] -/// pub struct Origin(PhantomData<(T)>); -/// ``` -/// -/// **WARNING**: modifying origin changes the outer runtime origin. This outer runtime origin -/// can be stored on-chain (e.g. in `pallet-scheduler`), thus any change must be done with care -/// as it might require some migration. +/// --- /// -/// NOTE: for instantiable pallets, the origin must be generic over `T` and `I`. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::origin`. #[proc_macro_attribute] pub fn origin(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// The `#[pallet::composite_enum]` attribute allows you to define an enum that gets composed as an -/// aggregate enum by `construct_runtime`. This is similar in principle with `#[pallet::event]` and -/// `#[pallet::error]`. /// -/// The attribute currently only supports enum definitions, and identifiers that are named -/// `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. Arbitrary identifiers for the enum are -/// not supported. The aggregate enum generated by `construct_runtime` will have the name of -/// `RuntimeFreezeReason`, `RuntimeHoldReason`, `RuntimeLockId` and `RuntimeSlashReason` -/// respectively. -/// -/// NOTE: The aggregate enum generated by `construct_runtime` generates a conversion function from -/// the pallet enum to the aggregate enum, and automatically derives the following traits: -/// -/// ```ignore -/// Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo, -/// RuntimeDebug -/// ``` +/// --- /// -/// For ease of usage, when no `#[derive]` attributes are found for the enum under -/// `#[pallet::composite_enum]`, the aforementioned traits are automatically derived for it. The -/// inverse is also true: if there are any `#[derive]` attributes found for the enum, then no traits -/// will automatically be derived for it. +/// Rust-Analyzer Users: Documentation for this macro can be found at +/// `frame_support::pallet_macros::composite_enum`. #[proc_macro_attribute] pub fn composite_enum(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1568,25 +1162,11 @@ pub fn task_index(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } -/// Can be attached to a module. Doing so will declare that module as importable into a pallet -/// via [`#[import_section]`](`macro@import_section`). -/// -/// Note that sections are imported by their module name/ident, and should be referred to by -/// their _full path_ from the perspective of the target pallet. Do not attempt to make use -/// of `use` statements to bring pallet sections into scope, as this will not work (unless -/// you do so as part of a wildcard import, in which case it will work). /// -/// ## Naming Logistics -/// -/// Also note that because of how `#[pallet_section]` works, pallet section names must be -/// globally unique _within the crate in which they are defined_. For more information on -/// why this must be the case, see macro_magic's -/// [`#[export_tokens]`](https://docs.rs/macro_magic/latest/macro_magic/attr.export_tokens.html) macro. +/// --- /// -/// Optionally, you may provide an argument to `#[pallet_section]` such as -/// `#[pallet_section(some_ident)]`, in the event that there is another pallet section in -/// same crate with the same ident/name. The ident you specify can then be used instead of -/// the module's ident name when you go to import it via `#[import_section]`. +/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// `frame_support::pallet_macros::pallet_section`. #[proc_macro_attribute] pub fn pallet_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { let tokens_clone = tokens.clone(); @@ -1600,39 +1180,11 @@ pub fn pallet_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { } } -/// An attribute macro that can be attached to a module declaration. Doing so will -/// Imports the contents of the specified external pallet section that was defined -/// previously using [`#[pallet_section]`](`macro@pallet_section`). -/// -/// ## Example -/// ```ignore -/// #[import_section(some_section)] -/// #[pallet] -/// pub mod pallet { -/// // ... -/// } -/// ``` -/// where `some_section` was defined elsewhere via: -/// ```ignore -/// #[pallet_section] -/// pub mod some_section { -/// // ... -/// } -/// ``` /// -/// This will result in the contents of `some_section` being _verbatim_ imported into -/// the pallet above. Note that since the tokens for `some_section` are essentially -/// copy-pasted into the target pallet, you cannot refer to imports that don't also -/// exist in the target pallet, but this is easily resolved by including all relevant -/// `use` statements within your pallet section, so they are imported as well, or by -/// otherwise ensuring that you have the same imports on the target pallet. -/// -/// It is perfectly permissible to import multiple pallet sections into the same pallet, -/// which can be done by having multiple `#[import_section(something)]` attributes -/// attached to the pallet. +/// --- /// -/// Note that sections are imported by their module name/ident, and should be referred to by -/// their _full path_ from the perspective of the target pallet. +/// **Rust-Analyzer users**: See the documentation of the Rust item in +/// `frame_support::pallet_macros::import_section`. #[import_tokens_attr { format!( "{}::macro_magic", @@ -1677,3 +1229,52 @@ pub fn import_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { } .into() } + +/// Mark a module that contains dynamic parameters. +/// +/// See the `pallet_parameters` for a full example. +/// +/// # Arguments +/// +/// The macro accepts two positional arguments, of which the second is optional. +/// +/// ## Aggregated Enum Name +/// +/// This sets the name that the aggregated Key-Value enum will be named after. Common names would be +/// `RuntimeParameters`, akin to `RuntimeCall`, `RuntimeOrigin` etc. There is no default value for +/// this argument. +/// +/// ## Parameter Storage Backend +/// +/// The second argument provides access to the storage of the parameters. It can either be set on +/// on this attribute, or on the inner ones. If set on both, the inner one takes precedence. +#[proc_macro_attribute] +pub fn dynamic_params(attrs: TokenStream, input: TokenStream) -> TokenStream { + dynamic_params::dynamic_params(attrs.into(), input.into()) + .unwrap_or_else(|r| r.into_compile_error()) + .into() +} + +/// Define a module inside a [`macro@dynamic_params`] module that contains dynamic parameters. +/// +/// See the `pallet_parameters` for a full example. +/// +/// # Argument +/// +/// This attribute takes one optional argument. The argument can either be put here or on the +/// surrounding `#[dynamic_params]` attribute. If set on both, the inner one takes precedence. +#[proc_macro_attribute] +pub fn dynamic_pallet_params(attrs: TokenStream, input: TokenStream) -> TokenStream { + dynamic_params::dynamic_pallet_params(attrs.into(), input.into()) + .unwrap_or_else(|r| r.into_compile_error()) + .into() +} + +/// Used internally by [`dynamic_params`]. +#[doc(hidden)] +#[proc_macro_attribute] +pub fn dynamic_aggregated_params_internal(attrs: TokenStream, input: TokenStream) -> TokenStream { + dynamic_params::dynamic_aggregated_params_internal(attrs.into(), input.into()) + .unwrap_or_else(|r| r.into_compile_error()) + .into() +} diff --git a/substrate/frame/support/procedural/src/no_bound/clone.rs b/substrate/frame/support/procedural/src/no_bound/clone.rs index 8e57a10d34c607c0a3d8de29eb6429ce73a4c8b9..346bf450f11c803b03af3c5550a0e7c07f7cdc11 100644 --- a/substrate/frame/support/procedural/src/no_bound/clone.rs +++ b/substrate/frame/support/procedural/src/no_bound/clone.rs @@ -19,10 +19,7 @@ use syn::spanned::Spanned; /// Derive Clone but do not bound any generic. pub fn derive_clone_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input = syn::parse_macro_input!(input as syn::DeriveInput); let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); diff --git a/substrate/frame/support/procedural/src/no_bound/debug.rs b/substrate/frame/support/procedural/src/no_bound/debug.rs index dd14b9cd564c9ccce9a8442b3669ae418fc18027..a1b3f4f0d3bbc32af89469527da9b9193ffea329 100644 --- a/substrate/frame/support/procedural/src/no_bound/debug.rs +++ b/substrate/frame/support/procedural/src/no_bound/debug.rs @@ -19,10 +19,7 @@ use syn::spanned::Spanned; /// Derive Debug but do not bound any generics. pub fn derive_debug_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input = syn::parse_macro_input!(input as syn::DeriveInput); let input_ident = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); diff --git a/substrate/frame/support/procedural/src/no_bound/mod.rs b/substrate/frame/support/procedural/src/no_bound/mod.rs index 2f76b01726150e97f784ecd93dec3b8ba79ba3fd..9e0377ddaf2540cf2c815563ef4b4a16fb40f15d 100644 --- a/substrate/frame/support/procedural/src/no_bound/mod.rs +++ b/substrate/frame/support/procedural/src/no_bound/mod.rs @@ -20,4 +20,6 @@ pub mod clone; pub mod debug; pub mod default; +pub mod ord; pub mod partial_eq; +pub mod partial_ord; diff --git a/substrate/frame/support/procedural/src/no_bound/ord.rs b/substrate/frame/support/procedural/src/no_bound/ord.rs new file mode 100644 index 0000000000000000000000000000000000000000..b24d27c04ee6a6763cc0b604c9ccc6fbbdb138b8 --- /dev/null +++ b/substrate/frame/support/procedural/src/no_bound/ord.rs @@ -0,0 +1,75 @@ +// 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 syn::spanned::Spanned; + +/// Derive Ord but do not bound any generic. +pub fn derive_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + }, + syn::Fields::Unit => { + quote::quote!(core::cmp::Ordering::Equal) + }, + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + #impl_ + } + } + }; + ) + .into() +} diff --git a/substrate/frame/support/procedural/src/no_bound/partial_eq.rs b/substrate/frame/support/procedural/src/no_bound/partial_eq.rs index 7cf5701b193b8a31143f3f636eaa2ab511536434..a1be71a961333445ab08d1def5c514271a2b7e63 100644 --- a/substrate/frame/support/procedural/src/no_bound/partial_eq.rs +++ b/substrate/frame/support/procedural/src/no_bound/partial_eq.rs @@ -19,10 +19,7 @@ use syn::spanned::Spanned; /// Derive PartialEq but do not bound any generic. pub fn derive_partial_eq_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input = syn::parse_macro_input!(input as syn::DeriveInput); let name = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); diff --git a/substrate/frame/support/procedural/src/no_bound/partial_ord.rs b/substrate/frame/support/procedural/src/no_bound/partial_ord.rs new file mode 100644 index 0000000000000000000000000000000000000000..86aa42be9f9a37aa8bca3e78e6de39db0151bd96 --- /dev/null +++ b/substrate/frame/support/procedural/src/no_bound/partial_ord.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 syn::spanned::Spanned; + +/// Derive PartialOrd but do not bound any generic. +pub fn derive_partial_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + syn::Data::Struct(struct_) => + match struct_.fields { + syn::Fields::Named(named) => { + let fields = + named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), + ); + + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = + unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map( + |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), + ); + + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + }, + syn::Fields::Unit => { + quote::quote!(Some(core::cmp::Ordering::Equal)) + }, + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { + fn partial_cmp(&self, other: &Self) -> Option { + #impl_ + } + } + }; + ) + .into() +} diff --git a/substrate/frame/support/procedural/src/pallet/expand/call.rs b/substrate/frame/support/procedural/src/pallet/expand/call.rs index 624cde018dc40258cf12014c5923a43996f496bd..f395872c8a80a6b9a2ddd391ea106284140d53ba 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::{CallVariantDef, CallWeightDef}, + parse::call::CallWeightDef, Def, }, COUNTER, @@ -112,22 +112,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { } debug_assert_eq!(fn_weight.len(), methods.len()); - let map_fn_docs = if !def.dev_mode { - // Emit the [`Pallet::method`] documentation only for non-dev modes. - |method: &CallVariantDef| { - let reference = format!("See [`Pallet::{}`].", method.name); - quote!(#reference) - } - } else { - // For the dev-mode do not provide a documenation link as it will break the - // `cargo doc` if the pallet is private inside a test. - |method: &CallVariantDef| { - let reference = format!("See `Pallet::{}`.", method.name); - quote!(#reference) - } - }; - - let fn_doc = methods.iter().map(map_fn_docs).collect::>(); + let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); let args_name = methods .iter() @@ -261,6 +246,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { }); quote::quote_spanned!(span => + #[doc(hidden)] mod warnings { #( #call_index_warnings @@ -270,6 +256,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { )* } + #[allow(unused_imports)] #[doc(hidden)] pub mod __substrate_call_check { #[macro_export] @@ -302,12 +289,12 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { #[doc(hidden)] #[codec(skip)] __Ignore( - #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen,)>, + ::core::marker::PhantomData<(#type_use_gen,)>, #frame_support::Never, ), #( #cfg_attrs - #[doc = #fn_doc] + #( #[doc = #fn_doc] )* #[codec(index = #call_index)] #fn_name { #( @@ -455,6 +442,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { } impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { + #[allow(dead_code)] #[doc(hidden)] pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() diff --git a/substrate/frame/support/procedural/src/pallet/expand/composite.rs b/substrate/frame/support/procedural/src/pallet/expand/composite.rs new file mode 100644 index 0000000000000000000000000000000000000000..d449afe8f2a8ec2c7b17d46b394ce319fe68fae4 --- /dev/null +++ b/substrate/frame/support/procedural/src/pallet/expand/composite.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. + +use crate::pallet::Def; +use proc_macro2::TokenStream; + +/// Expands `composite_enum` and adds the `VariantCount` implementation for it. +pub fn expand_composites(def: &mut Def) -> TokenStream { + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; + + for composite in &def.composites { + let name = &composite.ident; + let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); + let variants_count = composite.variant_count; + + // add `VariantCount` implementation for `composite_enum` + expand.extend(quote::quote_spanned!(composite.attr_span => + impl #impl_generics #frame_support::traits::VariantCount for #name #ty_generics #where_clause { + const VARIANT_COUNT: u32 = #variants_count; + } + )); + } + + expand +} diff --git a/substrate/frame/support/procedural/src/pallet/expand/event.rs b/substrate/frame/support/procedural/src/pallet/expand/event.rs index 2713f45fc3d54429ccab3ba82972adddd6e13a98..655fc5507d2654cfed4db4553ce5a997e4221125 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/event.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/event.rs @@ -87,7 +87,7 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { #[doc(hidden)] #[codec(skip)] __Ignore( - #frame_support::__private::sp_std::marker::PhantomData<(#event_use_gen)>, + ::core::marker::PhantomData<(#event_use_gen)>, #frame_support::Never, ) ); diff --git a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs index 5044d4285bb64aceeccd14959743a494f3c94f85..3623b595268d081ee7b5750a5431f208383f8517 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs @@ -42,7 +42,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { >::name::().unwrap_or("") }; - let initialize_on_chain_storage_version = if let Some(current_version) = + let initialize_on_chain_storage_version = if let Some(in_code_version) = &def.pallet_struct.storage_version { quote::quote! { @@ -50,9 +50,9 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { target: #frame_support::LOG_TARGET, "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", #pallet_name, - #current_version + #in_code_version ); - #current_version.put::(); + #in_code_version.put::(); } } else { quote::quote! { @@ -73,10 +73,10 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::log::info!( target: #frame_support::LOG_TARGET, "⚠️ {} declares internal migrations (which *might* execute). \ - On-chain `{:?}` vs current storage version `{:?}`", + On-chain `{:?}` vs in-code storage version `{:?}`", #pallet_name, ::on_chain_storage_version(), - ::current_storage_version(), + ::in_code_storage_version(), ); } } else { @@ -102,23 +102,23 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { }; // If a storage version is set, we should ensure that the storage version on chain matches the - // current storage version. This assumes that `Executive` is running custom migrations before + // in-code storage version. This assumes that `Executive` is running custom migrations before // the pallets are called. let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { quote::quote! { let on_chain_version = ::on_chain_storage_version(); - let current_version = ::current_storage_version(); + let in_code_version = ::in_code_storage_version(); - if on_chain_version != current_version { + if on_chain_version != in_code_version { #frame_support::__private::log::error!( target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} doesn't match current storage version {:?}.", + "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", #pallet_name, on_chain_version, - current_version, + in_code_version, ); - return Err("On chain and current storage version do not match. Missing runtime upgrade?".into()); + return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); } } } else { @@ -175,6 +175,22 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { } } + impl<#type_impl_gen> + #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_poll( + n: #frame_system::pallet_prelude::BlockNumberFor::, + weight: &mut #frame_support::weights::WeightMeter + ) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_poll(n, weight); + } + } + impl<#type_impl_gen> #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> for #pallet_ident<#type_use_gen> #where_clause diff --git a/substrate/frame/support/procedural/src/pallet/expand/mod.rs b/substrate/frame/support/procedural/src/pallet/expand/mod.rs index db242df781b124f86e14eb6bd084578fda723e73..3da7d9293c7cc1a1676e5cb0e4afe9ab08b25948 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/mod.rs @@ -16,6 +16,7 @@ // limitations under the License. mod call; +mod composite; mod config; mod constants; mod doc_only; @@ -76,6 +77,7 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream { let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); let doc_only = doc_only::expand_doc_only(&mut def); + let composites = composite::expand_composites(&mut def); def.item.attrs.insert( 0, @@ -117,6 +119,7 @@ storage item. Otherwise, all storage items are listed among [*Type Definitions*] #validate_unsigned #tt_default_parts #doc_only + #composites ); def.item diff --git a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs index c2102f0284dbeeadb08fc9122e5fe824032dfb82..7cdf6bde9de87d0bd4eed70de93173bc9bf4e3ff 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -160,7 +160,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { } ); - let (storage_version, current_storage_version_ty) = + let (storage_version, in_code_storage_version_ty) = if let Some(v) = def.pallet_struct.storage_version.as_ref() { (quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion }) } else { @@ -203,9 +203,9 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { for #pallet_ident<#type_use_gen> #config_where_clause { - type CurrentStorageVersion = #current_storage_version_ty; + type InCodeStorageVersion = #in_code_storage_version_ty; - fn current_storage_version() -> Self::CurrentStorageVersion { + fn in_code_storage_version() -> Self::InCodeStorageVersion { #storage_version } diff --git a/substrate/frame/support/procedural/src/pallet/expand/storage.rs b/substrate/frame/support/procedural/src/pallet/expand/storage.rs index 96c2c8e3120b8f8fb76e8ee10b067ce3f13f62e9..937b068cfabd14e04b05177be2f0242eccd3abec 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/storage.rs @@ -834,7 +834,10 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { .storages .iter() .filter_map(|storage| { - if storage.cfg_attrs.is_empty() { + // A little hacky; don't generate for cfg gated storages to not get compile errors + // when building "frame-feature-testing" gated storages in the "frame-support-test" + // crate. + if storage.try_decode && storage.cfg_attrs.is_empty() { let ident = &storage.ident; let gen = &def.type_use_generics(storage.attr_span); Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) diff --git a/substrate/frame/support/procedural/src/pallet/parse/composite.rs b/substrate/frame/support/procedural/src/pallet/parse/composite.rs index fa5f47dfdfa184d25a15ba05842f14c63fdbede3..c3ac74846bf7c664289dab8d046b51370fe28f5f 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/composite.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/composite.rs @@ -91,8 +91,14 @@ pub struct CompositeDef { pub index: usize, /// The composite keyword used (contains span). pub composite_keyword: keyword::CompositeKeyword, + /// Name of the associated type. + pub ident: syn::Ident, + /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. + pub generics: syn::Generics, /// The span of the pallet::composite_enum attribute. pub attr_span: proc_macro2::Span, + /// Variant count of the pallet::composite_enum. + pub variant_count: u32, } impl CompositeDef { @@ -103,6 +109,19 @@ impl CompositeDef { item: &mut syn::Item, ) -> syn::Result { let item = if let syn::Item::Enum(item) = item { + // check variants: composite enums support only field-less enum variants. This is + // because fields can introduce too many possibilities, making it challenging to compute + // a fixed variant count. + for variant in &item.variants { + match variant.fields { + syn::Fields::Named(_) | syn::Fields::Unnamed(_) => + return Err(syn::Error::new( + variant.ident.span(), + "The composite enum does not support variants with fields!", + )), + syn::Fields::Unit => (), + } + } item } else { return Err(syn::Error::new( @@ -152,7 +171,7 @@ impl CompositeDef { #[doc(hidden)] #[codec(skip)] __Ignore( - #scrate::__private::sp_std::marker::PhantomData, + ::core::marker::PhantomData, ) }); } @@ -160,6 +179,13 @@ impl CompositeDef { let composite_keyword = syn::parse2::(item.ident.to_token_stream())?; - Ok(CompositeDef { index, composite_keyword, attr_span }) + Ok(CompositeDef { + index, + composite_keyword, + attr_span, + generics: item.generics.clone(), + variant_count: item.variants.len() as u32, + ident: item.ident.clone(), + }) } } diff --git a/substrate/frame/support/procedural/src/pallet/parse/helper.rs b/substrate/frame/support/procedural/src/pallet/parse/helper.rs index 446ec203d2ba5878304f8ee081e870cc08101a4c..538226a8745fe4d0792e36b9aaad9cbed9d4f88e 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/helper.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/helper.rs @@ -613,7 +613,7 @@ pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { } pub(crate) fn two128_str(s: &str) -> TokenStream { - bytes_to_array(sp_core_hashing::twox_128(s.as_bytes()).into_iter()) + bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) } pub(crate) fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { diff --git a/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs index f4af86aa3e9936a3e77036f062ba8d4bf1a6c601..c2855ae38ef05aaf4c5b9b9a3f96a461a87bb48c 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/pallet_struct.rs @@ -44,7 +44,7 @@ pub struct PalletStructDef { /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. /// Contains the span of the attribute. pub without_storage_info: Option, - /// The current storage version of the pallet. + /// The in-code storage version of the pallet. pub storage_version: Option, } diff --git a/substrate/frame/support/procedural/src/pallet/parse/storage.rs b/substrate/frame/support/procedural/src/pallet/parse/storage.rs index d1c7ba2e5e3c6788827577dfa843205d0be69174..9d96a18b56943db4715fabd30e8bc0f1c0aa7d28 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/storage.rs @@ -29,6 +29,7 @@ mod keyword { syn::custom_keyword!(storage_prefix); syn::custom_keyword!(unbounded); syn::custom_keyword!(whitelist_storage); + syn::custom_keyword!(disable_try_decode_storage); syn::custom_keyword!(OptionQuery); syn::custom_keyword!(ResultQuery); syn::custom_keyword!(ValueQuery); @@ -39,11 +40,13 @@ mod keyword { /// * `#[pallet::storage_prefix = "CustomName"]` /// * `#[pallet::unbounded]` /// * `#[pallet::whitelist_storage] +/// * `#[pallet::disable_try_decode_storage]` pub enum PalletStorageAttr { Getter(syn::Ident, proc_macro2::Span), StorageName(syn::LitStr, proc_macro2::Span), Unbounded(proc_macro2::Span), WhitelistStorage(proc_macro2::Span), + DisableTryDecodeStorage(proc_macro2::Span), } impl PalletStorageAttr { @@ -53,6 +56,7 @@ impl PalletStorageAttr { Self::StorageName(_, span) | Self::Unbounded(span) | Self::WhitelistStorage(span) => *span, + Self::DisableTryDecodeStorage(span) => *span, } } } @@ -93,6 +97,9 @@ impl syn::parse::Parse for PalletStorageAttr { } else if lookahead.peek(keyword::whitelist_storage) { content.parse::()?; Ok(Self::WhitelistStorage(attr_span)) + } else if lookahead.peek(keyword::disable_try_decode_storage) { + content.parse::()?; + Ok(Self::DisableTryDecodeStorage(attr_span)) } else { Err(lookahead.error()) } @@ -104,6 +111,7 @@ struct PalletStorageAttrInfo { rename_as: Option, unbounded: bool, whitelisted: bool, + try_decode: bool, } impl PalletStorageAttrInfo { @@ -112,6 +120,7 @@ impl PalletStorageAttrInfo { let mut rename_as = None; let mut unbounded = false; let mut whitelisted = false; + let mut disable_try_decode_storage = false; for attr in attrs { match attr { PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), @@ -119,6 +128,8 @@ impl PalletStorageAttrInfo { rename_as = Some(name), PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, + PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => + disable_try_decode_storage = true, attr => return Err(syn::Error::new( attr.attr_span(), @@ -127,7 +138,13 @@ impl PalletStorageAttrInfo { } } - Ok(PalletStorageAttrInfo { getter, rename_as, unbounded, whitelisted }) + Ok(PalletStorageAttrInfo { + getter, + rename_as, + unbounded, + whitelisted, + try_decode: !disable_try_decode_storage, + }) } } @@ -186,6 +203,8 @@ pub struct StorageDef { pub unbounded: bool, /// Whether or not reads to this storage key will be ignored by benchmarking pub whitelisted: bool, + /// Whether or not to try to decode the storage key when running try-runtime checks. + pub try_decode: bool, /// Whether or not a default hasher is allowed to replace `_` pub use_default_hasher: bool, } @@ -775,7 +794,7 @@ impl StorageDef { }; let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted } = + let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } = PalletStorageAttrInfo::from_attrs(attrs)?; // set all storages to be unbounded if dev_mode is enabled @@ -921,6 +940,7 @@ impl StorageDef { named_generics, unbounded, whitelisted, + try_decode, use_default_hasher, }) } diff --git a/substrate/frame/support/procedural/src/storage_alias.rs b/substrate/frame/support/procedural/src/storage_alias.rs index c0b4089a2748f0b8be6683e591a36fb496af16a4..06f62768ff80f76e3ffb38e1882eadf105cd5b3d 100644 --- a/substrate/frame/support/procedural/src/storage_alias.rs +++ b/substrate/frame/support/procedural/src/storage_alias.rs @@ -623,7 +623,7 @@ fn generate_storage_instance( quote! { #visibility struct #counter_name< #impl_generics >( - #crate_::__private::sp_std::marker::PhantomData<(#type_generics)> + ::core::marker::PhantomData<(#type_generics)> ) #where_clause; impl<#impl_generics> #crate_::traits::StorageInstance @@ -653,7 +653,7 @@ fn generate_storage_instance( let code = quote! { #[allow(non_camel_case_types)] #visibility struct #name< #impl_generics >( - #crate_::__private::sp_std::marker::PhantomData<(#type_generics)> + ::core::marker::PhantomData<(#type_generics)> ) #where_clause; impl<#impl_generics> #crate_::traits::StorageInstance diff --git a/substrate/frame/support/procedural/tools/Cargo.toml b/substrate/frame/support/procedural/tools/Cargo.toml index 6d1b9507797d9834691f9a527e73cd352e1c8d69..a75307aca79b6ff241611b49b60c931ee1f83373 100644 --- a/substrate/frame/support/procedural/tools/Cargo.toml +++ b/substrate/frame/support/procedural/tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support-procedural-tools" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,6 +17,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] proc-macro-crate = "3.0.0" proc-macro2 = "1.0.56" -quote = "1.0.28" -syn = { version = "2.0.48", features = ["extra-traits", "full", "visit"] } +quote = { workspace = true } +syn = { features = ["extra-traits", "full", "visit"], workspace = true } frame-support-procedural-tools-derive = { path = "derive" } diff --git a/substrate/frame/support/procedural/tools/derive/Cargo.toml b/substrate/frame/support/procedural/tools/derive/Cargo.toml index d2d34d477146882c30dcb47ed557bb627ce8aed6..b39d99a822fb7aed533bc7795daa53c903cc2952 100644 --- a/substrate/frame/support/procedural/tools/derive/Cargo.toml +++ b/substrate/frame/support/procedural/tools/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support-procedural-tools-derive" -version = "3.0.0" +version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,5 +19,5 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.56" -quote = { version = "1.0.28", features = ["proc-macro"] } -syn = { version = "2.0.48", features = ["extra-traits", "full", "parsing", "proc-macro"] } +quote = { features = ["proc-macro"], workspace = true } +syn = { features = ["extra-traits", "full", "parsing", "proc-macro"], workspace = true } diff --git a/substrate/frame/support/procedural/tools/derive/src/lib.rs b/substrate/frame/support/procedural/tools/derive/src/lib.rs index f7c57c08674fcee985fab90225e980675cd1096e..a3590263558f491f4629eaa8b3b7997c1919b5ea 100644 --- a/substrate/frame/support/procedural/tools/derive/src/lib.rs +++ b/substrate/frame/support/procedural/tools/derive/src/lib.rs @@ -19,8 +19,6 @@ //! Use to derive parsing for parsing struct. // end::description[] -#![recursion_limit = "128"] - use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; diff --git a/substrate/frame/support/src/dispatch.rs b/substrate/frame/support/src/dispatch.rs index 4a313551aca634b88bed7d0f989943f7075a5050..61787a74012827f5c12b389ecedb0fd254bccae2 100644 --- a/substrate/frame/support/src/dispatch.rs +++ b/substrate/frame/support/src/dispatch.rs @@ -25,7 +25,7 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::{ generic::{CheckedExtrinsic, UncheckedExtrinsic}, - traits::SignedExtension, + traits::Dispatchable, DispatchError, RuntimeDebug, }; use sp_std::fmt; @@ -268,7 +268,8 @@ pub fn extract_actual_weight(result: &DispatchResultWithPostInfo, info: &Dispatc .calc_actual_weight(info) } -/// Extract the actual pays_fee from a dispatch result if any or fall back to the default weight. +/// Extract the actual pays_fee from a dispatch result if any or fall back to the default +/// weight. pub fn extract_actual_pays_fee(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Pays { match result { Ok(post_info) => post_info, @@ -368,11 +369,10 @@ where } /// Implementation for unchecked extrinsic. -impl GetDispatchInfo - for UncheckedExtrinsic +impl GetDispatchInfo + for UncheckedExtrinsic where - Call: GetDispatchInfo, - Extra: SignedExtension, + Call: GetDispatchInfo + Dispatchable, { fn get_dispatch_info(&self) -> DispatchInfo { self.function.get_dispatch_info() @@ -380,7 +380,7 @@ where } /// Implementation for checked extrinsic. -impl GetDispatchInfo for CheckedExtrinsic +impl GetDispatchInfo for CheckedExtrinsic where Call: GetDispatchInfo, { @@ -389,21 +389,6 @@ where } } -/// Implementation for test extrinsic. -#[cfg(feature = "std")] -impl GetDispatchInfo - for sp_runtime::testing::TestXt -{ - fn get_dispatch_info(&self) -> DispatchInfo { - // for testing: weight == size. - DispatchInfo { - weight: Weight::from_parts(self.encode().len() as _, 0), - pays_fee: Pays::Yes, - class: self.call.get_dispatch_info().class, - } - } -} - /// A struct holding value for each `DispatchClass`. #[derive(Clone, Eq, PartialEq, Default, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] pub struct PerDispatchClass { diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 95478d1a8cca77273b762c94087e0a5efe6454c7..6ea7fa33e774e1e3f6c91940186f4b4eb3c759de 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -44,8 +44,8 @@ pub mod __private { pub use paste; pub use scale_info; pub use serde; - pub use sp_core::{OpaqueMetadata, Void}; - pub use sp_core_hashing_proc_macro; + pub use sp_core::{Get, OpaqueMetadata, Void}; + pub use sp_crypto_hashing_proc_macro; pub use sp_inherents; #[cfg(feature = "std")] pub use sp_io::TestExternalities; @@ -54,7 +54,8 @@ pub mod __private { #[cfg(feature = "std")] pub use sp_runtime::{bounded_btree_map, bounded_vec}; pub use sp_runtime::{ - traits::Dispatchable, DispatchError, RuntimeDebug, StateVersion, TransactionOutcome, + traits::{AsSystemOriginSigner, Dispatchable}, + DispatchError, RuntimeDebug, StateVersion, TransactionOutcome, }; #[cfg(feature = "std")] pub use sp_state_machine::BasicExternalities; @@ -75,6 +76,7 @@ pub mod storage; #[cfg(test)] mod tests; pub mod traits; +pub mod transaction_extensions; pub mod weights; #[doc(hidden)] pub mod unsigned { @@ -175,6 +177,14 @@ pub use frame_support_procedural::storage_alias; pub use frame_support_procedural::derive_impl; +/// Experimental macros for defining dynamic params that can be used in pallet configs. +#[cfg(feature = "experimental")] +pub mod dynamic_params { + pub use frame_support_procedural::{ + dynamic_aggregated_params_internal, dynamic_pallet_params, dynamic_params, + }; +} + /// Create new implementations of the [`Get`](crate::traits::Get) trait. /// /// The so-called parameter type can be created in four different ways: @@ -330,7 +340,7 @@ macro_rules! parameter_types { impl< $($ty_params),* > $name< $($ty_params),* > { /// Returns the key for this parameter type. pub fn key() -> [u8; 16] { - $crate::__private::sp_core_hashing_proc_macro::twox_128!(b":", $name, b":") + $crate::__private::sp_crypto_hashing_proc_macro::twox_128!(b":", $name, b":") } /// Set the value of this parameter type in the storage. @@ -557,6 +567,42 @@ pub use frame_support_procedural::EqNoBound; /// ``` pub use frame_support_procedural::PartialEqNoBound; +/// Derive [`Ord`] but do not bound any generic. +/// +/// This is useful for type generic over runtime: +/// ``` +/// # use frame_support::{OrdNoBound, PartialOrdNoBound, EqNoBound, PartialEqNoBound}; +/// trait Config { +/// type C: Ord; +/// } +/// +/// // Foo implements [`Ord`] because `C` bounds [`Ord`]. +/// // Otherwise compilation will fail with an output telling `c` doesn't implement [`Ord`]. +/// #[derive(EqNoBound, OrdNoBound, PartialEqNoBound, PartialOrdNoBound)] +/// struct Foo { +/// c: T::C, +/// } +/// ``` +pub use frame_support_procedural::OrdNoBound; + +/// Derive [`PartialOrd`] but do not bound any generic. +/// +/// This is useful for type generic over runtime: +/// ``` +/// # use frame_support::{OrdNoBound, PartialOrdNoBound, EqNoBound, PartialEqNoBound}; +/// trait Config { +/// type C: PartialOrd; +/// } +/// +/// // Foo implements [`PartialOrd`] because `C` bounds [`PartialOrd`]. +/// // Otherwise compilation will fail with an output telling `c` doesn't implement [`PartialOrd`]. +/// #[derive(PartialOrdNoBound, PartialEqNoBound, EqNoBound)] +/// struct Foo { +/// c: T::C, +/// } +/// ``` +pub use frame_support_procedural::PartialOrdNoBound; + /// Derive [`Debug`] but do not bound any generic. /// /// This is useful for type generic over runtime: @@ -736,7 +782,7 @@ macro_rules! assert_err_with_weight { $crate::assert_err!($call.map(|_| ()).map_err(|e| e.error), $err); assert_eq!(dispatch_err_with_post.post_info.actual_weight, $weight); } else { - panic!("expected Err(_), got Ok(_).") + ::core::panic!("expected Err(_), got Ok(_).") } }; } @@ -856,18 +902,20 @@ pub mod pallet_prelude { }; pub use codec::{Decode, Encode, MaxEncodedLen}; pub use frame_support::pallet_macros::*; + /// The optional attribute `#[inject_runtime_type]` can be attached to `RuntimeCall`, /// `RuntimeEvent`, `RuntimeOrigin` or `PalletInfo` in an impl statement that has /// `#[register_default_impl]` attached to indicate that this item is generated by /// `construct_runtime`. /// /// Attaching this attribute to such an item ensures that the combined impl generated via - /// [`#[derive_impl(..)]`](`macro@super::derive_impl`) will use the correct type - /// auto-generated by `construct_runtime!`. + /// [`#[derive_impl(..)]`](`frame_support::derive_impl`) will use the correct + /// type auto-generated by + /// `construct_runtime!`. #[doc = docify::embed!("src/tests/inject_runtime_type.rs", derive_impl_works_with_runtime_type_injection)] /// /// However, if `no_aggregated_types` is specified while using - /// `[`#[derive_impl(..)]`](`macro@super::derive_impl`)`, then these items are attached + /// `[`#[derive_impl(..)]`](`frame_support::derive_impl`)`, then these items are attached /// verbatim to the combined impl. #[doc = docify::embed!("src/tests/inject_runtime_type.rs", derive_impl_works_with_no_aggregated_types)] pub use frame_support_procedural::inject_runtime_type; @@ -887,129 +935,26 @@ pub mod pallet_prelude { pub use sp_weights::Weight; } -/// The `pallet` attribute macro defines a pallet that can be used with -/// [`construct_runtime!`]. It must be attached to a module named `pallet` as follows: +/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to +/// specify pallet information. /// -/// ```ignore -/// #[pallet] -/// pub mod pallet { -/// ... -/// } +/// The struct must be defined as follows: /// ``` +/// #[frame_support::pallet] +/// mod pallet { +/// #[pallet::pallet] // <- the macro +/// pub struct Pallet(_); // <- the struct definition /// -/// Note that various types can be automatically imported using -/// [`frame_support::pallet_prelude`] and `frame_system::pallet_prelude`: -/// -/// ```ignore -/// #[pallet] -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; -/// use frame_system::pallet_prelude::*; -/// ... +/// #[pallet::config] +/// pub trait Config: frame_system::Config {} /// } /// ``` /// -/// # pallet::* Attributes -/// -/// The `pallet` macro will parse any items within your `pallet` module that are annotated with -/// `#[pallet::*]` attributes. Some of these attributes are mandatory and some are optional, -/// and they can attach to different types of items within your pallet depending on the -/// attribute in question. The full list of `#[pallet::*]` attributes is shown below in the -/// order in which they are mentioned in this document: -/// -/// * [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory) -/// * [`pallet::config`](#config-trait-palletconfig-mandatory) -/// * [`pallet::constant`](#palletconstant) -/// * [`pallet::disable_frame_system_supertrait_check`](#disable_supertrait_check) -/// * [`pallet::generate_store($vis trait Store)`](#palletgenerate_storevis-trait-store) -/// * [`pallet::storage_version`](#palletstorage_version) -/// * [`pallet::hooks`](#hooks-pallethooks-optional) -/// * [`pallet::call`](#call-palletcall-optional) -/// * [`pallet::weight($expr)`](#palletweightexpr) -/// * [`pallet::compact`](#palletcompact-some_arg-some_type) -/// * [`pallet::call_index($idx)`](#palletcall_indexidx) -/// * [`pallet::extra_constants`](#extra-constants-palletextra_constants-optional) -/// * [`pallet::error`](#error-palleterror-optional) -/// * [`pallet::event`](#event-palletevent-optional) -/// * [`pallet::generate_deposit($visibility fn -/// deposit_event)`](#palletgenerate_depositvisibility-fn-deposit_event) -/// * [`pallet::storage`](#storage-palletstorage-optional) -/// * [`pallet::getter(fn $my_getter_fn_name)`](#palletgetterfn-my_getter_fn_name-optional) -/// * [`pallet::storage_prefix = "SomeName"`](#palletstorage_prefix--somename-optional) -/// * [`pallet::unbounded`](#palletunbounded-optional) -/// * [`pallet::whitelist_storage`](#palletwhitelist_storage-optional) -/// * [`cfg(..)`](#cfg-for-storage) (on storage items) -/// * [`pallet::type_value`](#type-value-pallettype_value-optional) -/// * [`pallet::genesis_config`](#genesis-config-palletgenesis_config-optional) -/// * [`pallet::genesis_build`](#genesis-build-palletgenesis_build-optional) -/// * [`pallet::inherent`](#inherent-palletinherent-optional) -/// * [`pallet::validate_unsigned`](#validate-unsigned-palletvalidate_unsigned-optional) -/// * [`pallet::origin`](#origin-palletorigin-optional) -/// * [`pallet::composite_enum`](#composite-enum-palletcomposite_enum-optional) -/// -/// Note that at compile-time, the `#[pallet]` macro will analyze and expand all of these -/// attributes, ultimately removing their AST nodes before they can be parsed as real -/// attribute macro calls. This means that technically we do not need attribute macro -/// definitions for any of these attributes, however, for consistency and discoverability -/// reasons, we still maintain stub attribute macro definitions for all of these attributes in -/// the [`pallet_macros`] module which is automatically included in all pallets as part of the -/// pallet prelude. The actual "work" for all of these attribute macros can be found in the -/// macro expansion for `#[pallet]`. -/// -/// Also note that in this document, pallet attributes are explained using the syntax of -/// non-instantiable pallets. For an example of an instantiable pallet, see [this -/// example](#example-of-an-instantiable-pallet). -/// -/// # Dev Mode (`#[pallet(dev_mode)]`) -/// -/// Specifying the argument `dev_mode` on the `#[pallet]` or `#[frame_support::pallet]` -/// attribute attached to your pallet module will allow you to enable dev mode for a pallet. -/// The aim of dev mode is to loosen some of the restrictions and requirements placed on -/// production pallets for easy tinkering and development. Dev mode pallets should not be used -/// in production. Enabling dev mode has the following effects: -/// -/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By -/// default, dev mode pallets will assume a weight of zero (`0`) if a weight is not -/// specified. This is equivalent to specifying `#[weight(0)]` on all calls that do not -/// specify a weight. -/// * Call indices no longer need to be specified on every `#[pallet::call]` declaration. By -/// default, dev mode pallets will assume a call index based on the order of the call. -/// * All storages are marked as unbounded, meaning you do not need to implement -/// `MaxEncodedLen` on storage types. This is equivalent to specifying `#[pallet::unbounded]` -/// on all storage type definitions. -/// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, -/// these will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` -/// can simply be ignored when in `dev_mode`. -/// -/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or -/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument -/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]` -/// attribute macro. -/// -///
-/// WARNING:
-/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
-/// and therefore should never be done. Once you are done tinkering, you should remove the
-/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
-/// attempting to use your pallet in a production scenario.
-/// 
-/// -/// # Pallet struct placeholder: `#[pallet::pallet]` (mandatory) -/// -/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to specify -/// pallet information. -/// -/// The struct must be defined as follows: -/// ```ignore -/// #[pallet::pallet] -/// pub struct Pallet(_); -/// ``` /// I.e. a regular struct definition named `Pallet`, with generic T and no where clause. /// /// ## Macro expansion: /// -/// The macro adds this attribute to the struct definition: +/// The macro adds this attribute to the Pallet struct definition: /// ```ignore /// #[derive( /// frame_support::CloneNoBound, @@ -1018,1225 +963,946 @@ pub mod pallet_prelude { /// frame_support::RuntimeDebugNoBound, /// )] /// ``` -/// and replaces the type `_` with `PhantomData`. It also implements on the pallet: -/// * [`GetStorageVersion`](`traits::GetStorageVersion`) -/// * [`OnGenesis`](`traits::OnGenesis`): contains some logic to write the pallet version into -/// storage. -/// * `PalletErrorTypeInfo`: provides the type information for the pallet error, if defined. -/// -/// It declares `type Module` type alias for `Pallet`, used by `construct_runtime`. +/// and replaces the type `_` with `PhantomData`. /// -/// It implements [`PalletInfoAccess`](`traits::PalletInfoAccess') on `Pallet` to ease access -/// to pallet information given by [`frame_support::traits::PalletInfo`]. (The implementation -/// uses the associated type `frame_system::Config::PalletInfo`). +/// It also implements on the pallet: /// -/// It implements [`StorageInfoTrait`](`traits::StorageInfoTrait`) on `Pallet` which give -/// information about all storages. -/// -/// If the attribute `generate_store` is set then the macro creates the trait `Store` and -/// implements it on `Pallet`. +/// * [`GetStorageVersion`](frame_support::traits::GetStorageVersion) +/// * [`OnGenesis`](frame_support::traits::OnGenesis): contains some logic to write the pallet +/// version into storage. +/// * [`PalletInfoAccess`](frame_support::traits::PalletInfoAccess) to ease access to pallet +/// information given by [`frame_support::traits::PalletInfo`]. (The implementation uses the +/// associated type [`frame_support::traits::PalletInfo`]). +/// * [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) to give information about +/// storages. /// /// If the attribute `set_storage_max_encoded_len` is set then the macro calls -/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for each storage in the implementation of -/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for the pallet. Otherwise it implements -/// [`StorageInfoTrait`](`traits::StorageInfoTrait`) for the pallet using the -/// [`PartialStorageInfoTrait`](`traits::PartialStorageInfoTrait`) implementation of storages. -/// -/// # Config trait: `#[pallet::config]` (mandatory) -/// -/// The mandatory attribute `#[pallet::config]` defines the configurable options for the -/// pallet. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::config] -/// pub trait Config: frame_system::Config + $optionally_some_other_supertraits -/// $optional_where_clause -/// { -/// ... -/// } -/// ``` -/// -/// I.e. a regular trait definition named `Config`, with the supertrait -/// `frame_system::pallet::Config`, and optionally other supertraits and a where clause. -/// (Specifying other supertraits here is known as [tight -/// coupling](https://docs.substrate.io/reference/how-to-guides/pallet-design/use-tight-coupling/)) -/// -/// The associated type `RuntimeEvent` is reserved. If defined, it must have the bounds -/// `From` and `IsType<::RuntimeEvent>`. -/// -/// [`pallet::event`](`frame_support::pallet_macros::event`) must be present if `RuntimeEvent` -/// exists as a config item in your `#[pallet::config]`. -/// -/// Also see [`pallet::config`](`frame_support::pallet_macros::config`) -/// -/// ## `pallet::constant` -/// -/// The `#[pallet::constant]` attribute can be used to add an associated type trait bounded by -/// [`Get`](crate::traits::Get) from [`pallet::config`](#palletconfig) into metadata, e.g.: -/// -/// ```ignore -/// #[pallet::config] -/// pub trait Config: frame_system::Config { -/// #[pallet::constant] -/// type Foo: Get; -/// } -/// ``` -/// -/// Also see [`pallet::constant`](`frame_support::pallet_macros::constant`) -/// -/// ## `pallet::disable_frame_system_supertrait_check` -/// -/// -/// To bypass the `frame_system::Config` supertrait check, use the attribute -/// `pallet::disable_frame_system_supertrait_check`, e.g.: -/// -/// ```ignore -/// #[pallet::config] -/// #[pallet::disable_frame_system_supertrait_check] -/// pub trait Config: pallet_timestamp::Config {} -/// ``` -/// -/// NOTE: Bypassing the `frame_system::Config` supertrait check is typically desirable when you -/// want to write an alternative to the `frame_system` pallet. -/// -/// Also see -/// [`pallet::disable_frame_system_supertrait_check`](`frame_support::pallet_macros::disable_frame_system_supertrait_check`) -/// -/// ## Macro expansion: -/// -/// The macro expands pallet constant metadata with the information given by -/// `#[pallet::constant]`. -/// -/// # `pallet::generate_store($vis trait Store)` -/// -/// To generate a `Store` trait associating all storages, annotate your `Pallet` struct with -/// the attribute `#[pallet::generate_store($vis trait Store)]`, e.g.: -/// -/// ```ignore -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(_); -/// ``` -/// More precisely, the `Store` trait contains an associated type for each storage. It is -/// implemented for `Pallet` allowing access to the storage from pallet struct. -/// -/// Thus when defining a storage named `Foo`, it can later be accessed from `Pallet` using -/// `::Foo`. -/// -/// NOTE: this attribute is only valid when applied _directly_ to your `Pallet` struct -/// definition. -/// -/// Also see [`pallet::generate_store`](`frame_support::pallet_macros::generate_store`). -/// -/// # `pallet::storage_version` -/// -/// Because the [`pallet::pallet`](#pallet-struct-placeholder-palletpallet-mandatory) macro -/// implements [`traits::GetStorageVersion`], the current storage version needs to be -/// communicated to the macro. This can be done by using the `pallet::storage_version` -/// attribute: -/// -/// ```ignore -/// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); -/// -/// #[pallet::pallet] -/// #[pallet::storage_version(STORAGE_VERSION)] -/// pub struct Pallet(_); -/// ``` -/// -/// If not present, the current storage version is set to the default value. -/// -/// Also see [`pallet::storage_version`](`frame_support::pallet_macros::storage_version`) -/// -/// # Hooks: `#[pallet::hooks]` (optional) -/// -/// The `pallet::hooks` attribute allows you to specify a `Hooks` implementation for `Pallet` -/// that specifies pallet-specific logic. -/// -/// The item the attribute attaches to must be defined as follows: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet $optional_where_clause { -/// ... -/// } -/// ``` -/// I.e. a regular trait implementation with generic bound: `T: Config`, for the trait -/// `Hooks>` (they are defined in preludes), for the type `Pallet` and -/// with an optional where clause. -/// -/// If no `#[pallet::hooks]` exists, then the following default implementation is -/// automatically generated: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks> for Pallet {} -/// ``` -/// -/// Also see [`pallet::hooks`](`frame_support::pallet_macros::hooks`) -/// -/// # Call: `#[pallet::call]` (optional) -/// -/// Implementation of pallet dispatchables. -/// -/// Item must be defined as: -/// ```ignore -/// #[pallet::call] -/// impl Pallet { -/// /// $some_doc -/// #[pallet::weight($ExpressionResultingInWeight)] -/// pub fn $fn_name( -/// origin: OriginFor, -/// $some_arg: $some_type, -/// // or with compact attribute: #[pallet::compact] $some_arg: $some_type, -/// ... -/// ) -> DispatchResultWithPostInfo { // or `-> DispatchResult` -/// ... -/// } -/// ... -/// } -/// ``` -/// I.e. a regular type implementation, with generic `T: Config`, on type `Pallet`, with -/// an optional where clause. -/// -/// ## `#[pallet::weight($expr)]` -/// -/// Each dispatchable needs to define a weight with `#[pallet::weight($expr)]` attribute, the -/// first argument must be `origin: OriginFor`. -/// -/// Also see [`pallet::weight`](`frame_support::pallet_macros::weight`) -/// -/// ### `#[pallet::compact] $some_arg: $some_type` -/// -/// Compact encoding for arguments can be achieved via `#[pallet::compact]`. The function must -/// return a `DispatchResultWithPostInfo` or `DispatchResult`. -/// -/// Also see [`pallet::compact`](`frame_support::pallet_macros::compact`) -/// -/// ## `#[pallet::call_index($idx)]` +/// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for each storage in the +/// implementation of [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the +/// pallet. Otherwise it implements +/// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the pallet using the +/// [`PartialStorageInfoTrait`](frame_support::traits::PartialStorageInfoTrait) +/// implementation of storages. /// -/// Each dispatchable may also be annotated with the `#[pallet::call_index($idx)]` attribute, -/// which explicitly defines the codec index for the dispatchable function in the `Call` enum. +/// ## Dev Mode (`#[pallet(dev_mode)]`) /// -/// All call indexes start from 0, until it encounters a dispatchable function with a defined -/// call index. The dispatchable function that lexically follows the function with a defined -/// call index will have that call index, but incremented by 1, e.g. if there are 3 -/// dispatchable functions `fn foo`, `fn bar` and `fn qux` in that order, and only `fn bar` -/// has a call index of 10, then `fn qux` will have an index of 11, instead of 1. +/// Specifying the argument `dev_mode` will allow you to enable dev mode for a pallet. The +/// aim of dev mode is to loosen some of the restrictions and requirements placed on +/// production pallets for easy tinkering and development. Dev mode pallets should not be +/// used in production. Enabling dev mode has the following effects: /// -/// **WARNING**: modifying dispatchables, changing their order, removing some, etc., must be -/// done with care. Indeed this will change the outer runtime call type (which is an enum with -/// one variant per pallet), this outer runtime call can be stored on-chain (e.g. in -/// `pallet-scheduler`). Thus migration might be needed. To mitigate against some of this, the -/// `#[pallet::call_index($idx)]` attribute can be used to fix the order of the dispatchable so -/// that the `Call` enum encoding does not change after modification. As a general rule of -/// thumb, it is therefore adventageous to always add new calls to the end so you can maintain -/// the existing order of calls. -/// -/// Also see [`pallet::call_index`](`frame_support::pallet_macros::call_index`) -/// -/// # Extra constants: `#[pallet::extra_constants]` (optional) -/// -/// Allows you to define some extra constants to be added into constant metadata. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::extra_constants] -/// impl Pallet where $optional_where_clause { -/// /// $some_doc -/// $vis fn $fn_name() -> $some_return_type { -/// ... -/// } -/// ... -/// } -/// ``` -/// I.e. a regular rust `impl` block with some optional where clause and functions with 0 args, -/// 0 generics, and some return type. -/// -/// ## Macro expansion -/// -/// The macro add some extra constants to pallet constant metadata. -/// -/// Also see: [`pallet::extra_constants`](`frame_support::pallet_macros::extra_constants`) -/// -/// # Error: `#[pallet::error]` (optional) -/// -/// The `#[pallet::error]` attribute allows you to define an error enum that will be returned -/// from the dispatchable when an error occurs. The information for this error type is then -/// stored in metadata. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::error] -/// pub enum Error { -/// /// $some_optional_doc -/// $SomeFieldLessVariant, -/// /// $some_more_optional_doc -/// $SomeVariantWithOneField(FieldType), -/// ... -/// } -/// ``` -/// I.e. a regular enum named `Error`, with generic `T` and fieldless or multiple-field -/// variants. -/// -/// Any field type in the enum variants must implement [`scale_info::TypeInfo`] in order to be -/// properly used in the metadata, and its encoded size should be as small as possible, -/// preferably 1 byte in size in order to reduce storage size. The error enum itself has an -/// absolute maximum encoded size specified by [`MAX_MODULE_ERROR_ENCODED_SIZE`]. -/// -/// (1 byte can still be 256 different errors. The more specific the error, the easier it is to -/// diagnose problems and give a better experience to the user. Don't skimp on having lots of -/// individual error conditions.) -/// -/// Field types in enum variants must also implement [`PalletError`](traits::PalletError), -/// otherwise the pallet will fail to compile. Rust primitive types have already implemented -/// the [`PalletError`](traits::PalletError) trait along with some commonly used stdlib types -/// such as [`Option`] and -/// [`PhantomData`](`frame_support::__private::sp_std::marker::PhantomData`), and hence in most -/// use cases, a manual implementation is not necessary and is discouraged. -/// -/// The generic `T` must not bound anything and a `where` clause is not allowed. That said, -/// bounds and/or a where clause should not needed for any use-case. -/// -/// Also see: [`pallet::error`](`frame_support::pallet_macros::error`) -/// -/// # Event: `#[pallet::event]` (optional) -/// -/// Allows you to define pallet events. Pallet events are stored under the `system` / `events` -/// key when the block is applied (and then replaced when the next block writes it's events). -/// -/// The Event enum must be defined as follows: -/// -/// ```ignore -/// #[pallet::event] -/// #[pallet::generate_deposit($visibility fn deposit_event)] // Optional -/// pub enum Event<$some_generic> $optional_where_clause { -/// /// Some doc -/// $SomeName($SomeType, $YetanotherType, ...), -/// ... -/// } -/// ``` -/// -/// I.e. an enum (with named or unnamed fields variant), named `Event`, with generic: none or -/// `T` or `T: Config`, and optional w here clause. -/// -/// Each field must implement [`Clone`], [`Eq`], [`PartialEq`], [`Encode`], [`Decode`], and -/// [`Debug`] (on std only). For ease of use, bound by the trait -/// [`Member`](`frame_support::pallet_prelude::Member`), available in -/// frame_support::pallet_prelude. -/// -/// Also see [`pallet::event`](`frame_support::pallet_macros::event`) -/// -/// ## `#[pallet::generate_deposit($visibility fn deposit_event)]` -/// -/// The attribute `#[pallet::generate_deposit($visibility fn deposit_event)]` generates a -/// helper function on `Pallet` that handles deposit events. -/// -/// NOTE: For instantiable pallets, the event must be generic over `T` and `I`. -/// -/// Also see [`pallet::generate_deposit`](`frame_support::pallet_macros::generate_deposit`) -/// -/// # Storage: `#[pallet::storage]` (optional) -/// -/// The `#[pallet::storage]` attribute lets you define some abstract storage inside of runtime -/// storage and also set its metadata. This attribute can be used multiple times. -/// -/// Item should be defined as: -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn $getter_name)] // optional -/// $vis type $StorageName<$some_generic> $optional_where_clause -/// = $StorageType<$generic_name = $some_generics, $other_name = $some_other, ...>; -/// ``` -/// -/// or with unnamed generic: -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn $getter_name)] // optional -/// $vis type $StorageName<$some_generic> $optional_where_clause -/// = $StorageType<_, $some_generics, ...>; -/// ``` -/// -/// I.e. it must be a type alias, with generics: `T` or `T: Config`. The aliased type must be -/// one of [`StorageValue`](`pallet_prelude::StorageValue`), -/// [`StorageMap`](`pallet_prelude::StorageMap`) or -/// [`StorageDoubleMap`](`pallet_prelude::StorageDoubleMap`). The generic arguments of the -/// storage type can be given in two manners: named and unnamed. For named generic arguments, -/// the name for each argument should match the name defined for it on the storage struct: -/// * [`StorageValue`](`pallet_prelude::StorageValue`) expects `Value` and optionally -/// `QueryKind` and `OnEmpty`, -/// * [`StorageMap`](`pallet_prelude::StorageMap`) expects `Hasher`, `Key`, `Value` and -/// optionally `QueryKind` and `OnEmpty`, -/// * [`CountedStorageMap`](`pallet_prelude::CountedStorageMap`) expects `Hasher`, `Key`, -/// `Value` and optionally `QueryKind` and `OnEmpty`, -/// * [`StorageDoubleMap`](`pallet_prelude::StorageDoubleMap`) expects `Hasher1`, `Key1`, -/// `Hasher2`, `Key2`, `Value` and optionally `QueryKind` and `OnEmpty`. -/// -/// For unnamed generic arguments: Their first generic must be `_` as it is replaced by the -/// macro and other generic must declared as a normal generic type declaration. -/// -/// The `Prefix` generic written by the macro is generated using -/// `PalletInfo::name::>()` and the name of the storage type. E.g. if runtime names -/// the pallet "MyExample" then the storage `type Foo = ...` should use the prefix: -/// `Twox128(b"MyExample") ++ Twox128(b"Foo")`. -/// -/// For the [`CountedStorageMap`](`pallet_prelude::CountedStorageMap`) variant, the `Prefix` -/// also implements -/// [`CountedStorageMapInstance`](`frame_support::storage::types::CountedStorageMapInstance`). -/// It also associates a [`CounterPrefix`](`pallet_prelude::CounterPrefix'), which is -/// implemented the same as above, but the storage prefix is prepend with `"CounterFor"`. E.g. -/// if runtime names the pallet "MyExample" then the storage `type Foo = -/// CountedStorageaMap<...>` will store its counter at the prefix: `Twox128(b"MyExample") ++ -/// Twox128(b"CounterForFoo")`. -/// -/// E.g: -/// -/// ```ignore -/// #[pallet::storage] -/// pub(super) type MyStorage = StorageMap; -/// ``` -/// -/// In this case the final prefix used by the map is `Twox128(b"MyExample") ++ -/// Twox128(b"OtherName")`. -/// -/// Also see [`pallet::storage`](`frame_support::pallet_macros::storage`) -/// -/// ## `#[pallet::getter(fn $my_getter_fn_name)]` (optional) -/// -/// The optional attribute `#[pallet::getter(fn $my_getter_fn_name)]` allows you to define a -/// getter function on `Pallet`. -/// -/// Also see [`pallet::getter`](`frame_support::pallet_macros::getter`) -/// -/// ## `#[pallet::storage_prefix = "SomeName"]` (optional) -/// -/// The optional attribute `#[pallet::storage_prefix = "SomeName"]` allows you to define the -/// storage prefix to use, see how `Prefix` generic is implemented above. This is helpful if -/// you wish to rename the storage field but don't want to perform a migration. -/// -/// E.g: -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::storage_prefix = "foo"] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap; -/// ``` -/// -/// or -/// -/// ```ignore -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap<_, Blake2_128Concat, u32, u32>; -/// ``` -/// -/// Also see [`pallet::storage_prefix`](`frame_support::pallet_macros::storage_prefix`) -/// -/// ## `#[pallet::unbounded]` (optional) -/// -/// The optional attribute `#[pallet::unbounded]` declares the storage as unbounded. When -/// implementating the storage info (when `#[pallet::generate_storage_info]` is specified on -/// the pallet struct placeholder), the size of the storage will be declared as unbounded. This -/// can be useful for storage which can never go into PoV (Proof of Validity). -/// -/// Also see [`pallet::unbounded`](`frame_support::pallet_macros::unbounded`) -/// -/// ## `#[pallet::whitelist_storage]` (optional) -/// -/// The optional attribute `#[pallet::whitelist_storage]` will declare the storage as -/// whitelisted from benchmarking. -/// -/// See -/// [`pallet::whitelist_storage`](frame_support::pallet_macros::whitelist_storage) -/// for more info. -/// -/// ## `#[cfg(..)]` (for storage) -/// The optional attributes `#[cfg(..)]` allow conditional compilation for the storage. -/// -/// E.g: -/// -/// ```ignore -/// #[cfg(feature = "my-feature")] -/// #[pallet::storage] -/// pub(super) type MyStorage = StorageValue; -/// ``` -/// -/// All the `cfg` attributes are automatically copied to the items generated for the storage, -/// i.e. the getter, storage prefix, and the metadata element etc. -/// -/// Any type placed as the `QueryKind` parameter must implement -/// [`frame_support::storage::types::QueryKindTrait`]. There are 3 implementations of this -/// trait by default: -/// -/// 1. [`OptionQuery`](`frame_support::storage::types::OptionQuery`), the default `QueryKind` -/// used when this type parameter is omitted. Specifying this as the `QueryKind` would cause -/// storage map APIs that return a `QueryKind` to instead return an [`Option`], returning -/// `Some` when a value does exist under a specified storage key, and `None` otherwise. -/// 2. [`ValueQuery`](`frame_support::storage::types::ValueQuery`) causes storage map APIs that -/// return a `QueryKind` to instead return the value type. In cases where a value does not -/// exist under a specified storage key, the `OnEmpty` type parameter on `QueryKindTrait` is -/// used to return an appropriate value. -/// 3. [`ResultQuery`](`frame_support::storage::types::ResultQuery`) causes storage map APIs -/// that return a `QueryKind` to instead return a `Result`, with `T` being the value -/// type and `E` being the pallet error type specified by the `#[pallet::error]` attribute. -/// In cases where a value does not exist under a specified storage key, an `Err` with the -/// specified pallet error variant is returned. -/// -/// NOTE: If the `QueryKind` generic parameter is still generic at this stage or is using some -/// type alias then the generation of the getter might fail. In this case the getter can be -/// implemented manually. -/// -/// NOTE: The generic `Hasher` must implement the [`StorageHasher`] trait (or the type is not -/// usable at all). We use [`StorageHasher::METADATA`] for the metadata of the hasher of the -/// storage item. Thus generic hasher is supported. -/// -/// ## Macro expansion -/// -/// For each storage item the macro generates a struct named -/// `_GeneratedPrefixForStorage$NameOfStorage`, and implements -/// [`StorageInstance`](traits::StorageInstance) on it using the pallet and storage name. It -/// then uses it as the first generic of the aliased type. For -/// [`CountedStorageMap`](`pallet_prelude::CountedStorageMap`), -/// [`CountedStorageMapInstance`](`frame_support::storage::types::CountedStorageMapInstance`) -/// is implemented, and another similar struct is generated. -/// -/// For a named generic, the macro will reorder the generics, and remove the names. -/// -/// The macro implements the function `storage_metadata` on the `Pallet` implementing the -/// metadata for all storage items based on their kind: -/// * for a storage value, the type of the value is copied into the metadata -/// * for a storage map, the type of the values and the key's type is copied into the metadata -/// * for a storage double map, the type of the values, and the types of `key1` and `key2` are -/// copied into the metadata. -/// -/// # Type value: `#[pallet::type_value]` (optional) -/// -/// The `#[pallet::type_value]` attribute lets you define a struct implementing the -/// [`Get`](crate::traits::Get) trait to ease use of storage types. This attribute is meant to -/// be used alongside [`#[pallet::storage]`](#storage-palletstorage-optional) to define a -/// storage's default value. This attribute can be used multiple times. -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::type_value] -/// fn $MyDefaultName<$some_generic>() -> $default_type $optional_where_clause { $expr } -/// ``` -/// -/// I.e.: a function definition with generics none or `T: Config` and a returned type. -/// -/// E.g.: -/// -/// ```ignore -/// #[pallet::type_value] -/// fn MyDefault() -> T::Balance { 3.into() } -/// ``` -/// -/// Also see [`pallet::type_value`](`frame_support::pallet_macros::type_value`) -/// -/// # Genesis config: `#[pallet::genesis_config]` (optional) -/// -/// The `#[pallet::genesis_config]` attribute allows you to define the genesis configuration -/// for the pallet. -/// -/// Item is defined as either an enum or a struct. It needs to be public and implement the -/// trait [`BuildGenesisConfig`](`traits::BuildGenesisConfig`) with -/// [`#[pallet::genesis_build]`](#genesis-build-palletgenesis_build-optional). The type -/// generics are constrained to be either none, or `T` or `T: Config`. -/// -/// E.g: -/// -/// ```ignore -/// #[pallet::genesis_config] -/// pub struct GenesisConfig { -/// _myfield: BalanceOf, -/// } -/// ``` -/// -/// Also see [`pallet::genesis_config`](`frame_support::pallet_macros::genesis_config`) -/// -/// # Genesis build: `#[pallet::genesis_build]` (optional) -/// -/// The `#[pallet::genesis_build]` attribute allows you to define how `genesis_configuration` -/// is built. This takes as input the `GenesisConfig` type (as `self`) and constructs the -/// pallet's initial state. -/// -/// The impl must be defined as: -/// -/// ```ignore -/// #[pallet::genesis_build] -/// impl GenesisBuild for GenesisConfig<$maybe_generics> { -/// fn build(&self) { $expr } -/// } -/// ``` -/// -/// I.e. a trait implementation with generic `T: Config`, of trait `GenesisBuild` on -/// type `GenesisConfig` with generics none or `T`. -/// -/// E.g.: -/// -/// ```ignore -/// #[pallet::genesis_build] -/// impl GenesisBuild for GenesisConfig { -/// fn build(&self) {} -/// } -/// ``` -/// -/// Also see [`pallet::genesis_build`](`frame_support::pallet_macros::genesis_build`) -/// -/// # Inherent: `#[pallet::inherent]` (optional) -/// -/// The `#[pallet::inherent]` attribute allows the pallet to provide some -/// [inherent](https://docs.substrate.io/fundamentals/transaction-types/#inherent-transactions). -/// An inherent is some piece of data that is inserted by a block authoring node at block -/// creation time and can either be accepted or rejected by validators based on whether the -/// data falls within an acceptable range. -/// -/// The most common inherent is the `timestamp` that is inserted into every block. Since there -/// is no way to validate timestamps, validators simply check that the timestamp reported by -/// the block authoring node falls within an acceptable range. +/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By +/// default, dev mode pallets will assume a weight of zero (`0`) if a weight is not +/// specified. This is equivalent to specifying `#[weight(0)]` on all calls that do not +/// specify a weight. +/// * Call indices no longer need to be specified on every `#[pallet::call]` declaration. By +/// default, dev mode pallets will assume a call index based on the order of the call. +/// * All storages are marked as unbounded, meaning you do not need to implement +/// [`MaxEncodedLen`](frame_support::pallet_prelude::MaxEncodedLen) on storage types. This is +/// equivalent to specifying `#[pallet::unbounded]` on all storage type definitions. +/// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, +/// these will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` +/// can simply be ignored when in `dev_mode`. /// -/// Item must be defined as: +/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or +/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This +/// argument cannot be specified anywhere else, including but not limited to the +/// `#[pallet::pallet]` attribute macro. /// -/// ```ignore -/// #[pallet::inherent] -/// impl ProvideInherent for Pallet { -/// // ... regular trait implementation -/// } -/// ``` -/// -/// I.e. a trait implementation with bound `T: Config`, of trait -/// [`ProvideInherent`](`pallet_prelude::ProvideInherent`) for type `Pallet`, and some -/// optional where clause. -/// -/// Also see [`pallet::inherent`](`frame_support::pallet_macros::inherent`) -/// -/// # Validate unsigned: `#[pallet::validate_unsigned]` (optional) -/// -/// The `#[pallet::validate_unsigned]` attribute allows the pallet to validate some unsigned -/// transaction: -/// -/// Item must be defined as: -/// -/// ```ignore -/// #[pallet::validate_unsigned] -/// impl ValidateUnsigned for Pallet { -/// // ... regular trait implementation -/// } -/// ``` -/// -/// I.e. a trait implementation with bound `T: Config`, of trait -/// [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`) for type `Pallet`, and some -/// optional where clause. -/// -/// NOTE: There is also the [`sp_runtime::traits::SignedExtension`] trait that can be used to -/// add some specific logic for transaction validation. -/// -/// Also see [`pallet::validate_unsigned`](`frame_support::pallet_macros::validate_unsigned`) -/// -/// # Origin: `#[pallet::origin]` (optional) -/// -/// The `#[pallet::origin]` attribute allows you to define some origin for the pallet. -/// -/// Item must be either a type alias, an enum, or a struct. It needs to be public. -/// -/// E.g.: -/// -/// ```ignore -/// #[pallet::origin] -/// pub struct Origin(PhantomData<(T)>); -/// ``` -/// -/// **WARNING**: modifying origin changes the outer runtime origin. This outer runtime origin -/// can be stored on-chain (e.g. in `pallet-scheduler`), thus any change must be done with care -/// as it might require some migration. -/// -/// NOTE: for instantiable pallets, the origin must be generic over `T` and `I`. -/// -/// Also see [`pallet::origin`](`frame_support::pallet_macros::origin`) -/// -/// # Composite enum `#[pallet::composite_enum]` (optional) -/// -/// The `#[pallet::composite_enum]` attribute allows you to define an enum on the pallet which -/// will then instruct `construct_runtime` to amalgamate all similarly-named enums from other -/// pallets into an aggregate enum. This is similar in principle with how the aggregate enum is -/// generated for `#[pallet::event]` or `#[pallet::error]`. -/// -/// The item tagged with `#[pallet::composite_enum]` MUST be an enum declaration, and can ONLY -/// be the following identifiers: `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. -/// Custom identifiers are not supported. -/// -/// NOTE: For ease of usage, when no `#[derive]` attributes are detected, the -/// `#[pallet::composite_enum]` attribute will automatically derive the following traits for -/// the enum: -/// -/// ```ignore -/// Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug -/// ``` -/// -/// The inverse is also true: if there are any #[derive] attributes present for the enum, then -/// the attribute will not automatically derive any of the traits described above. -/// -/// # General notes on instantiable pallets -/// -/// An instantiable pallet is one where Config is generic, i.e. `Config`. This allows -/// runtime to implement multiple instances of the pallet, by using different types for the -/// generic. This is the sole purpose of the generic `I`, but because -/// [`PalletInfo`](`traits::PalletInfo`) requires the `Pallet` placeholder to be static, it is -/// important to bound by `'static` whenever [`PalletInfo`](`traits::PalletInfo`) can be used. -/// Additionally, in order to make an instantiable pallet usable as a regular pallet without an -/// instance, it is important to bound by `= ()` on every type. -/// -/// Thus impl bound looks like `impl, I: 'static>`, and types look like -/// `SomeType` or `SomeType, I: 'static = ()>`. -/// -/// # Example of a non-instantiable pallet -/// -/// ``` -/// pub use pallet::*; // reexport in crate namespace for `construct_runtime!` -/// -/// #[frame_support::pallet] -/// // NOTE: The name of the pallet is provided by `construct_runtime` and is used as -/// // the unique identifier for the pallet's storage. It is not defined in the pallet itself. -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; // Import various types used in the pallet definition -/// use frame_system::pallet_prelude::*; // Import some system helper types. -/// -/// type BalanceOf = ::Balance; -/// -/// // Define the generic parameter of the pallet -/// // The macro parses `#[pallet::constant]` attributes and uses them to generate metadata -/// // for the pallet's constants. -/// #[pallet::config] -/// pub trait Config: frame_system::Config { -/// #[pallet::constant] // put the constant in metadata -/// type MyGetParam: Get; -/// type Balance: Parameter + MaxEncodedLen + From; -/// type RuntimeEvent: From> + IsType<::RuntimeEvent>; -/// } -/// -/// // Define some additional constant to put into the constant metadata. -/// #[pallet::extra_constants] -/// impl Pallet { -/// /// Some description -/// fn exra_constant_name() -> u128 { 4u128 } -/// } -/// -/// // Define the pallet struct placeholder, various pallet function are implemented on it. -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(_); -/// -/// // Implement the pallet hooks. -/// #[pallet::hooks] -/// impl Hooks> for Pallet { -/// fn on_initialize(_n: BlockNumberFor) -> Weight { -/// unimplemented!(); -/// } -/// -/// // can implement also: on_finalize, on_runtime_upgrade, offchain_worker, ... -/// // see `Hooks` trait -/// } -/// -/// // Declare Call struct and implement dispatchables. -/// // -/// // WARNING: Each parameter used in functions must implement: Clone, Debug, Eq, PartialEq, -/// // Codec. -/// // -/// // The macro parses `#[pallet::compact]` attributes on function arguments and implements -/// // the `Call` encoding/decoding accordingly. -/// #[pallet::call] -/// impl Pallet { -/// /// Doc comment put in metadata -/// #[pallet::weight(0)] // Defines weight for call (function parameters are in scope) -/// pub fn toto( -/// origin: OriginFor, -/// #[pallet::compact] _foo: u32, -/// ) -> DispatchResultWithPostInfo { -/// let _ = origin; -/// unimplemented!(); -/// } -/// } -/// -/// // Declare the pallet `Error` enum (this is optional). -/// // The macro generates error metadata using the doc comment on each variant. -/// #[pallet::error] -/// pub enum Error { -/// /// doc comment put into metadata -/// InsufficientProposersBalance, -/// } -/// -/// // Declare pallet Event enum (this is optional). -/// // -/// // WARNING: Each type used in variants must implement: Clone, Debug, Eq, PartialEq, Codec. -/// // -/// // The macro generates event metadata, and derive Clone, Debug, Eq, PartialEq and Codec -/// #[pallet::event] -/// // Generate a funciton on Pallet to deposit an event. -/// #[pallet::generate_deposit(pub(super) fn deposit_event)] -/// pub enum Event { -/// /// doc comment put in metadata -/// // `::AccountId` is not defined in metadata list, the last -/// // Thus the metadata is `::AccountId`. -/// Proposed(::AccountId), -/// /// doc -/// // here metadata will be `Balance` as define in metadata list -/// Spending(BalanceOf), -/// // here metadata will be `Other` as define in metadata list -/// Something(u32), -/// } -/// -/// // Define a struct which implements `frame_support::traits::Get` (optional). -/// #[pallet::type_value] -/// pub(super) fn MyDefault() -> T::Balance { 3.into() } -/// -/// // Declare a storage item. Any amount of storage items can be declared (optional). -/// // -/// // Is expected either `StorageValue`, `StorageMap` or `StorageDoubleMap`. -/// // The macro generates the prefix type and replaces the first generic `_`. -/// // -/// // The macro expands the metadata for the storage item with the type used: -/// // * for a storage value the type of the value is copied into the metadata -/// // * for a storage map the type of the values and the type of the key is copied into the metadata -/// // * for a storage double map the types of the values and keys are copied into the -/// // metadata. -/// // -/// // NOTE: The generic `Hasher` must implement the `StorageHasher` trait (or the type is not -/// // usable at all). We use [`StorageHasher::METADATA`] for the metadata of the hasher of the -/// // storage item. Thus generic hasher is supported. -/// #[pallet::storage] -/// pub(super) type MyStorageValue = -/// StorageValue>; -/// -/// // Another storage declaration -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// #[pallet::storage_prefix = "SomeOtherName"] -/// pub(super) type MyStorage = -/// StorageMap; -/// -/// // Declare the genesis config (optional). -/// // -/// // The macro accepts either a struct or an enum; it checks that generics are consistent. -/// // -/// // Type must implement the `Default` trait. -/// #[pallet::genesis_config] -/// #[derive(frame_support::DefaultNoBound)] -/// pub struct GenesisConfig { -/// _config: sp_std::marker::PhantomData, -/// _myfield: u32, -/// } -/// -/// // Declare genesis builder. (This is need only if GenesisConfig is declared) -/// #[pallet::genesis_build] -/// impl BuildGenesisConfig for GenesisConfig { -/// fn build(&self) {} -/// } -/// -/// // Declare a pallet origin (this is optional). -/// // -/// // The macro accept type alias or struct or enum, it checks generics are consistent. -/// #[pallet::origin] -/// pub struct Origin(PhantomData); -/// -/// // Declare a hold reason (this is optional). -/// // -/// // Creates a hold reason for this pallet that is aggregated by `construct_runtime`. -/// // A similar enum can be defined for `FreezeReason`, `LockId` or `SlashReason`. -/// #[pallet::composite_enum] -/// pub enum HoldReason { -/// SomeHoldReason -/// } -/// -/// // Declare validate_unsigned implementation (this is optional). -/// #[pallet::validate_unsigned] -/// impl ValidateUnsigned for Pallet { -/// type Call = Call; -/// fn validate_unsigned( -/// source: TransactionSource, -/// call: &Self::Call -/// ) -> TransactionValidity { -/// Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) -/// } -/// } -/// -/// // Declare inherent provider for pallet (this is optional). -/// #[pallet::inherent] -/// impl ProvideInherent for Pallet { -/// type Call = Call; -/// type Error = InherentError; -/// -/// const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; -/// -/// fn create_inherent(_data: &InherentData) -> Option { -/// unimplemented!(); -/// } -/// -/// fn is_inherent(_call: &Self::Call) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// // Regular rust code needed for implementing ProvideInherent trait -/// -/// #[derive(codec::Encode, sp_runtime::RuntimeDebug)] -/// #[cfg_attr(feature = "std", derive(codec::Decode))] -/// pub enum InherentError { -/// } -/// -/// impl sp_inherents::IsFatalError for InherentError { -/// fn is_fatal_error(&self) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall"; -/// } -/// ``` -/// -/// # Example of an instantiable pallet -/// -/// ``` -/// pub use pallet::*; -/// -/// #[frame_support::pallet] -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; -/// use frame_system::pallet_prelude::*; -/// -/// type BalanceOf = >::Balance; -/// -/// #[pallet::config] -/// pub trait Config: frame_system::Config { -/// #[pallet::constant] -/// type MyGetParam: Get; -/// type Balance: Parameter + MaxEncodedLen + From; -/// type RuntimeEvent: From> + IsType<::RuntimeEvent>; -/// } -/// -/// #[pallet::extra_constants] -/// impl, I: 'static> Pallet { -/// /// Some description -/// fn extra_constant_name() -> u128 { 4u128 } -/// } -/// -/// #[pallet::pallet] -/// #[pallet::generate_store(pub(super) trait Store)] -/// pub struct Pallet(PhantomData<(T, I)>); -/// -/// #[pallet::hooks] -/// impl, I: 'static> Hooks> for Pallet { -/// } -/// -/// #[pallet::call] -/// impl, I: 'static> Pallet { -/// /// Doc comment put in metadata -/// #[pallet::weight(0)] -/// pub fn toto(origin: OriginFor, #[pallet::compact] _foo: u32) -> DispatchResultWithPostInfo { -/// let _ = origin; -/// unimplemented!(); -/// } -/// } -/// -/// #[pallet::error] -/// pub enum Error { -/// /// doc comment put into metadata -/// InsufficientProposersBalance, -/// } -/// -/// #[pallet::event] -/// #[pallet::generate_deposit(pub(super) fn deposit_event)] -/// pub enum Event, I: 'static = ()> { -/// /// doc comment put in metadata -/// Proposed(::AccountId), -/// /// doc -/// Spending(BalanceOf), -/// Something(u32), -/// } -/// -/// #[pallet::type_value] -/// pub(super) fn MyDefault, I: 'static>() -> T::Balance { 3.into() } -/// -/// #[pallet::storage] -/// pub(super) type MyStorageValue, I: 'static = ()> = -/// StorageValue>; -/// -/// #[pallet::storage] -/// #[pallet::getter(fn my_storage)] -/// #[pallet::storage_prefix = "SomeOtherName"] -/// pub(super) type MyStorage = -/// StorageMap; -/// -/// #[pallet::genesis_config] -/// #[derive(frame_support::DefaultNoBound)] -/// pub struct GenesisConfig, I: 'static = ()> { -/// _config: sp_std::marker::PhantomData<(T,I)>, -/// _myfield: u32, -/// } -/// -/// #[pallet::genesis_build] -/// impl, I: 'static> BuildGenesisConfig for GenesisConfig { -/// fn build(&self) {} -/// } -/// -/// #[pallet::origin] -/// pub struct Origin(PhantomData<(T, I)>); -/// -/// #[pallet::composite_enum] -/// pub enum HoldReason { -/// SomeHoldReason -/// } -/// -/// #[pallet::validate_unsigned] -/// impl, I: 'static> ValidateUnsigned for Pallet { -/// type Call = Call; -/// fn validate_unsigned( -/// source: TransactionSource, -/// call: &Self::Call -/// ) -> TransactionValidity { -/// Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) -/// } -/// } -/// -/// #[pallet::inherent] -/// impl, I: 'static> ProvideInherent for Pallet { -/// type Call = Call; -/// type Error = InherentError; -/// -/// const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; -/// -/// fn create_inherent(_data: &InherentData) -> Option { -/// unimplemented!(); -/// } -/// -/// fn is_inherent(_call: &Self::Call) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// // Regular rust code needed for implementing ProvideInherent trait -/// -/// #[derive(codec::Encode, sp_runtime::RuntimeDebug)] -/// #[cfg_attr(feature = "std", derive(codec::Decode))] -/// pub enum InherentError { -/// } -/// -/// impl sp_inherents::IsFatalError for InherentError { -/// fn is_fatal_error(&self) -> bool { -/// unimplemented!(); -/// } -/// } -/// -/// pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"testpall"; -/// } -/// ``` -/// -/// # Upgrade guidelines -/// -/// 1. Export the metadata of the pallet for later checks -/// - run your node with the pallet active -/// - query the metadata using the `state_getMetadata` RPC and curl, or use `subsee -p -/// > meta.json` -/// 2. Generate the template upgrade for the pallet provided by `decl_storage` with the -/// environment variable `PRINT_PALLET_UPGRADE`: `PRINT_PALLET_UPGRADE=1 cargo check -p -/// my_pallet`. This template can be used as it contains all information for storages, -/// genesis config and genesis build. -/// 3. Reorganize the pallet to have the trait `Config`, `decl_*` macros, -/// [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`), -/// [`ProvideInherent`](`pallet_prelude::ProvideInherent`), and Origin` all together in one -/// file. Suggested order: -/// * `Config`, -/// * `decl_module`, -/// * `decl_event`, -/// * `decl_error`, -/// * `decl_storage`, -/// * `origin`, -/// * `validate_unsigned`, -/// * `provide_inherent`, so far it should compile and all be correct. -/// 4. start writing the new pallet module -/// ```ignore -/// pub use pallet::*; -/// -/// #[frame_support::pallet] -/// pub mod pallet { -/// use frame_support::pallet_prelude::*; -/// use frame_system::pallet_prelude::*; -/// use super::*; -/// -/// #[pallet::pallet] -/// #[pallet::generate_store($visibility_of_trait_store trait Store)] -/// // NOTE: if the visibility of trait store is private but you want to make it available -/// // in super, then use `pub(super)` or `pub(crate)` to make it available in crate. -/// pub struct Pallet(_); -/// // pub struct Pallet(PhantomData); // for instantiable pallet -/// } -/// ``` -/// 5. **migrate Config**: move trait into the module with -/// * all const in `decl_module` to [`#[pallet::constant]`](#palletconstant) -/// * add the bound `IsType<::RuntimeEvent>` to `type -/// RuntimeEvent` -/// 7. **migrate decl_module**: write: -/// ```ignore -/// #[pallet::hooks] -/// impl Hooks for Pallet { -/// } -/// ``` -/// and write inside `on_initialize`, `on_finalize`, `on_runtime_upgrade`, -/// `offchain_worker`, and `integrity_test`. -/// -/// then write: -/// ```ignore -/// #[pallet::call] -/// impl Pallet { -/// } -/// ``` -/// and write inside all the calls in `decl_module` with a few changes in the signature: -/// - origin must now be written completely, e.g. `origin: OriginFor` -/// - result type must be `DispatchResultWithPostInfo`, you need to write it and also you -/// might need to put `Ok(().into())` at the end or the function. -/// - `#[compact]` must now be written -/// [`#[pallet::compact]`](#palletcompact-some_arg-some_type) -/// - `#[weight = ..]` must now be written [`#[pallet::weight(..)]`](#palletweightexpr) -/// -/// 7. **migrate event**: rewrite as a simple enum with the attribute -/// [`#[pallet::event]`](#event-palletevent-optional), use [`#[pallet::generate_deposit($vis -/// fn deposit_event)]`](#event-palletevent-optional) to generate `deposit_event`, -/// 8. **migrate error**: rewrite it with attribute -/// [`#[pallet::error]`](#error-palleterror-optional). -/// 9. **migrate storage**: `decl_storage` provide an upgrade template (see 3.). All storages, -/// genesis config, genesis build and default implementation of genesis config can be taken -/// from it directly. -/// -/// Otherwise here is the manual process: -/// -/// first migrate the genesis logic. write: -/// ```ignore -/// #[pallet::genesis_config] -/// struct GenesisConfig { -/// // fields of add_extra_genesis -/// } -/// impl Default for GenesisConfig { -/// // type default or default provided for fields -/// } -/// #[pallet::genesis_build] -/// impl GenesisBuild for GenesisConfig { -/// // for instantiable pallet: -/// // `impl GenesisBuild for GenesisConfig { -/// fn build() { -/// // The add_extra_genesis build logic -/// } -/// } -/// ``` -/// for each storage, if it contains `config(..)` then add fields, and make it default to -/// the value in `= ..;` or the type default if none, if it contains no build then also add -/// the logic to build the value. for each storage if it contains `build(..)` then add the -/// logic to `genesis_build`. -/// -/// NOTE: within `decl_storage`: the individual config is executed first, followed by the -/// build and finally the `add_extra_genesis` build. -/// -/// Once this is done you can migrate storages individually, a few notes: -/// - for private storage use `pub(crate) type ` or `pub(super) type` or nothing, -/// - for storages with `get(fn ..)` use [`#[pallet::getter(fn -/// ...)]`](#palletgetterfn-my_getter_fn_name-optional) -/// - for storages with value being `Option<$something>` make generic `Value` being -/// `$something` and generic `QueryKind` being `OptionQuery` (note: this is default). -/// Otherwise make `Value` the complete value type and `QueryKind` being `ValueQuery`. -/// - for storages with default value: `= $expr;` provide some specific `OnEmpty` generic. -/// To do so use of `#[pallet::type_value]` to generate the wanted struct to put. -/// example: `MyStorage: u32 = 3u32` would be written: -/// -/// ```ignore -/// #[pallet::type_value] fn MyStorageOnEmpty() -> u32 { 3u32 } -/// #[pallet::storage] -/// pub(super) type MyStorage = StorageValue<_, u32, ValueQuery, MyStorageOnEmpty>; -/// ``` -/// -/// NOTE: `decl_storage` also generates the functions `assimilate_storage` and -/// `build_storage` directly on `GenesisConfig`, and these are sometimes used in tests. -/// In order not to break they can be implemented manually, one can implement those -/// functions by calling the `GenesisBuild` implementation. -/// 10. **migrate origin**: move the origin to the pallet module to be under a -/// [`#[pallet::origin]`](#origin-palletorigin-optional) attribute -/// 11. **migrate validate_unsigned**: move the -/// [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`) implementation to the pallet -/// module under a -/// [`#[pallet::validate_unsigned]`](#validate-unsigned-palletvalidate_unsigned-optional) -/// attribute -/// 12. **migrate provide_inherent**: move the -/// [`ProvideInherent`](`pallet_prelude::ProvideInherent`) implementation to the pallet -/// module under a [`#[pallet::inherent]`](#inherent-palletinherent-optional) attribute -/// 13. rename the usage of `Module` to `Pallet` inside the crate. -/// 14. migration is done, now double check the migration with the checking migration -/// guidelines shown below. -/// -/// # Checking upgrade guidelines: -/// -/// * compare metadata. Use [subsee](https://github.com/ascjones/subsee) to fetch the metadata -/// and do a diff of the resulting json before and after migration. This checks for: -/// * call, names, signature, docs -/// * event names, docs -/// * error names, docs -/// * storage names, hasher, prefixes, default value -/// * error, error, constant -/// * manually check that: -/// * `Origin` was moved inside the macro under -/// [`#[pallet::origin]`](#origin-palletorigin-optional) if it exists -/// * [`ValidateUnsigned`](`pallet_prelude::ValidateUnsigned`) was moved inside the macro -/// under -/// [`#[pallet::validate_unsigned)]`](#validate-unsigned-palletvalidate_unsigned-optional) -/// if it exists -/// * [`ProvideInherent`](`pallet_prelude::ProvideInherent`) was moved inside the macro -/// under [`#[pallet::inherent)]`](#inherent-palletinherent-optional) if it exists -/// * `on_initialize` / `on_finalize` / `on_runtime_upgrade` / `offchain_worker` were moved -/// to the `Hooks` implementation -/// * storages with `config(..)` were converted to `GenesisConfig` field, and their default -/// is `= $expr;` if the storage has a default value -/// * storages with `build($expr)` or `config(..)` were built in `GenesisBuild::build` -/// * `add_extra_genesis` fields were converted to `GenesisConfig` field with their correct -/// default if specified -/// * `add_extra_genesis` build was written into `GenesisBuild::build` -/// * storage items defined with [`pallet`] use the name of the pallet provided by -/// [`traits::PalletInfo::name`] as `pallet_prefix` (in `decl_storage`, storage items used -/// the `pallet_prefix` given as input of `decl_storage` with the syntax `as Example`). Thus -/// a runtime using the pallet must be careful with this change. To handle this change: -/// * either ensure that the name of the pallet given to `construct_runtime!` is the same -/// as the name the pallet was giving to `decl_storage`, -/// * or do a storage migration from the old prefix used to the new prefix used. -/// -/// NOTE: The prefixes used by storage items are in metadata. Thus, ensuring the metadata -/// hasn't changed ensures that the `pallet_prefix`s used by the storage items haven't changed. -/// -/// # Notes when macro fails to show proper error message spans: -/// -/// Rustc loses span for some macro input. Some tips to fix it: -/// * do not use inner attribute: -/// ```ignore -/// #[pallet] -/// pub mod pallet { -/// //! This inner attribute will make span fail -/// .. -/// } -/// ``` -/// * use the newest nightly possible. +///
+/// WARNING:
+/// You should not deploy or use dev mode pallets in production. Doing so can break your
+/// chain and therefore should never be done. Once you are done tinkering, you should
+/// remove the 'dev_mode' argument from your #[pallet] declaration and fix any compile
+/// errors before attempting to use your pallet in a production scenario.
+/// 
pub use frame_support_procedural::pallet; -/// Contains macro stubs for all of the pallet:: macros +/// Contains macro stubs for all of the `pallet::` macros pub mod pallet_macros { - pub use frame_support_procedural::{ - composite_enum, config, disable_frame_system_supertrait_check, error, event, - extra_constants, feeless_if, generate_deposit, generate_store, getter, hooks, - import_section, inherent, no_default, no_default_bounds, origin, pallet_section, - storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight, - whitelist_storage, - }; + /// Declare the storage as whitelisted from benchmarking. + /// + /// Doing so will exclude reads of that value's storage key from counting towards weight + /// calculations during benchmarking. + /// + /// This attribute should only be attached to storages that are known to be + /// read/used in every block. This will result in a more accurate benchmarking weight. + /// + /// ### Example + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::whitelist_storage] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::whitelist_storage; - /// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. In - /// slightly simplified terms, this macro declares the set of "transactions" of a pallet. + /// Allows specifying the weight of a call. + /// + /// Each dispatchable needs to define a weight with the `#[pallet::weight($expr)]` + /// attribute. The first argument must be `origin: OriginFor`. + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::weight({0})] // <- set actual weight here + /// #[pallet::call_index(0)] + /// pub fn something( + /// _: OriginFor, + /// foo: u32, + /// ) -> DispatchResult { + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::weight; + + /// Allows whitelisting a storage item from decoding during try-runtime checks. + /// + /// The optional attribute `#[pallet::disable_try_decode_storage]` will declare the + /// storage as whitelisted from decoding during try-runtime checks. This should only be + /// attached to transient storage which cannot be migrated during runtime upgrades. + /// + /// ### Example + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::disable_try_decode_storage] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::disable_try_decode_storage; + + /// Declares a storage as unbounded in potential size. + /// + /// When implementating the storage info (when `#[pallet::generate_storage_info]` is + /// specified on the pallet struct placeholder), the size of the storage will be declared + /// as unbounded. This can be useful for storage which can never go into PoV (Proof of + /// Validity). + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::unbounded] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::unbounded; + + /// Defines what storage prefix to use for a storage item when building the trie. + /// + /// This is helpful if you wish to rename the storage field but don't want to perform a + /// migration. + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::storage_prefix = "foo"] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + pub use frame_support_procedural::storage_prefix; + + /// Ensures the generated `DefaultConfig` will not have any bounds for + /// that trait item. + /// + /// Attaching this attribute to a trait item ensures that the generated trait + /// `DefaultConfig` will not have any bounds for this trait item. + /// + /// As an example, if you have a trait item `type AccountId: SomeTrait;` in your `Config` + /// trait, the generated `DefaultConfig` will only have `type AccountId;` with no trait + /// bound. + pub use frame_support_procedural::no_default_bounds; + + /// Ensures the trait item will not be used as a default with the + /// `#[derive_impl(..)]` attribute macro. + /// + /// The optional attribute `#[pallet::no_default]` can be attached to trait items within a + /// `Config` trait impl that has [`#[pallet::config(with_default)]`](`config`) + /// attached. + pub use frame_support_procedural::no_default; + + /// Declares a module as importable into a pallet via + /// [`#[import_section]`](`import_section`). + /// + /// Note that sections are imported by their module name/ident, and should be referred to + /// by their _full path_ from the perspective of the target pallet. Do not attempt to make + /// use of `use` statements to bring pallet sections into scope, as this will not work + /// (unless you do so as part of a wildcard import, in which case it will work). + /// + /// ## Naming Logistics + /// + /// Also note that because of how `#[pallet_section]` works, pallet section names must be + /// globally unique _within the crate in which they are defined_. For more information on + /// why this must be the case, see macro_magic's + /// [`#[export_tokens]`](https://docs.rs/macro_magic/latest/macro_magic/attr.export_tokens.html) macro. + /// + /// Optionally, you may provide an argument to `#[pallet_section]` such as + /// `#[pallet_section(some_ident)]`, in the event that there is another pallet section in + /// same crate with the same ident/name. The ident you specify can then be used instead of + /// the module's ident name when you go to import it via + /// [`#[import_section]`](`import_section`). + pub use frame_support_procedural::pallet_section; + + /// The `#[pallet::inherent]` attribute allows the pallet to provide + /// [inherents](https://docs.substrate.io/fundamentals/transaction-types/#inherent-transactions). + /// + /// An inherent is some piece of data that is inserted by a block authoring node at block + /// creation time and can either be accepted or rejected by validators based on whether the + /// data falls within an acceptable range. + /// + /// The most common inherent is the `timestamp` that is inserted into every block. Since + /// there is no way to validate timestamps, validators simply check that the timestamp + /// reported by the block authoring node falls within an acceptable range. + /// + /// Example usage: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_support::inherent::IsFatalError; + /// # use sp_timestamp::InherentError; + /// # use sp_std::result; + /// # + /// // Example inherent identifier + /// pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"timstap0"; + /// + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::inherent] + /// impl ProvideInherent for Pallet { + /// type Call = Call; + /// type Error = InherentError; + /// const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + /// + /// fn create_inherent(data: &InherentData) -> Option { + /// unimplemented!() + /// } + /// + /// fn check_inherent( + /// call: &Self::Call, + /// data: &InherentData, + /// ) -> result::Result<(), Self::Error> { + /// unimplemented!() + /// } + /// + /// fn is_inherent(call: &Self::Call) -> bool { + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// I.e. a trait implementation with bound `T: Config`, of trait `ProvideInherent` for type + /// `Pallet`, and some optional where clause. + /// + /// ## Macro expansion + /// + /// The macro currently makes no use of this information, but it might use this information + /// in the future to give information directly to `construct_runtime`. + pub use frame_support_procedural::inherent; + + /// Splits a pallet declaration into multiple parts. + /// + /// An attribute macro that can be attached to a module declaration. Doing so will + /// import the contents of the specified external pallet section that is defined + /// elsewhere using [`#[pallet_section]`](`pallet_section`). + /// + /// ## Example + /// ``` + /// # use frame_support::pallet_macros::pallet_section; + /// # use frame_support::pallet_macros::import_section; + /// # + /// /// A [`pallet_section`] that defines the events for a pallet. + /// /// This can later be imported into the pallet using [`import_section`]. + /// #[pallet_section] + /// mod events { + /// #[pallet::event] + /// #[pallet::generate_deposit(pub(super) fn deposit_event)] + /// pub enum Event { + /// /// Event documentation should end with an array that provides descriptive names for event + /// /// parameters. [something, who] + /// SomethingStored { something: u32, who: T::AccountId }, + /// } + /// } + /// + /// #[import_section(events)] + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config { + /// # type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// # } + /// } + /// ``` + /// + /// This will result in the contents of `some_section` being _verbatim_ imported into + /// the pallet above. Note that since the tokens for `some_section` are essentially + /// copy-pasted into the target pallet, you cannot refer to imports that don't also + /// exist in the target pallet, but this is easily resolved by including all relevant + /// `use` statements within your pallet section, so they are imported as well, or by + /// otherwise ensuring that you have the same imports on the target pallet. + /// + /// It is perfectly permissible to import multiple pallet sections into the same pallet, + /// which can be done by having multiple `#[import_section(something)]` attributes + /// attached to the pallet. + /// + /// Note that sections are imported by their module name/ident, and should be referred to + /// by their _full path_ from the perspective of the target pallet. + pub use frame_support_procedural::import_section; + + /// Allows defining getter functions on `Pallet` storage. + /// + /// ## Example + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// #[pallet::getter(fn my_getter_fn_name)] + /// pub type MyStorage = StorageValue<_, u32>; + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// See [`pallet::storage`](`frame_support::pallet_macros::storage`) for more info. + pub use frame_support_procedural::getter; + + /// Allows generating the `Store` trait for all storages. + /// + /// DEPRECATED: Will be removed, do not use. + /// See for more details. + /// + /// To generate a `Store` trait associating all storages, annotate your `Pallet` struct + /// with the attribute `#[pallet::generate_store($vis trait Store)]`, e.g.: + /// + /// ```ignore + /// #[pallet::pallet] + /// #[pallet::generate_store(pub(super) trait Store)] + /// pub struct Pallet(_); + /// ``` + /// More precisely, the `Store` trait contains an associated type for each storage. It is + /// implemented for `Pallet` allowing access to the storage from pallet struct. + /// + /// Thus when defining a storage named `Foo`, it can later be accessed from `Pallet` using + /// `::Foo`. + pub use frame_support_procedural::generate_store; + + /// Defines constants that are added to the constant field of + /// [`PalletMetadata`](frame_metadata::v15::PalletMetadata) struct for this pallet. + /// + /// Must be defined like: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// # + /// #[pallet::extra_constants] + /// impl Pallet // $optional_where_clause + /// { + /// #[pallet::constant_name(SomeU32ConstantName)] + /// /// Some doc + /// fn some_u32_constant() -> u32 { + /// 100u32 + /// } + /// } + /// } + /// ``` + /// + /// I.e. a regular rust `impl` block with some optional where clause and functions with 0 + /// args, 0 generics, and some return type. + pub use frame_support_procedural::extra_constants; + + #[rustfmt::skip] + /// Allows bypassing the `frame_system::Config` supertrait check. + /// + /// To bypass the syntactic `frame_system::Config` supertrait check, use the attribute + /// `pallet::disable_frame_system_supertrait_check`. + /// + /// Note this bypass is purely syntactic, and does not actually remove the requirement that your + /// pallet implements `frame_system::Config`. When using this check, your config is still required to implement + /// `frame_system::Config` either via + /// - Implementing a trait that itself implements `frame_system::Config` + /// - Tightly coupling it with another pallet which itself implements `frame_system::Config` + /// + /// e.g. + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// trait OtherTrait: frame_system::Config {} + /// + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::config] + /// #[pallet::disable_frame_system_supertrait_check] + /// pub trait Config: OtherTrait {} + /// } + /// ``` + /// + /// To learn more about supertraits, see the + /// [trait_based_programming](../../polkadot_sdk_docs/reference_docs/trait_based_programming/index.html) + /// reference doc. + pub use frame_support_procedural::disable_frame_system_supertrait_check; + + /// The mandatory attribute allowing definition of configurable types for the pallet. + /// + /// Item must be defined as: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::config] + /// pub trait Config: frame_system::Config // + $optionally_some_other_supertraits + /// // $optional_where_clause + /// { + /// // config items here + /// } + /// } + /// ``` + /// + /// I.e. a regular trait definition named `Config`, with the supertrait + /// [`frame_system::pallet::Config`](../../frame_system/pallet/trait.Config.html), and + /// optionally other supertraits and a where clause. (Specifying other supertraits here is + /// known as [tight coupling](https://docs.substrate.io/reference/how-to-guides/pallet-design/use-tight-coupling/)) + /// + /// The associated type `RuntimeEvent` is reserved. If defined, it must have the bounds + /// `From` and `IsType<::RuntimeEvent>`. + /// + /// [`#[pallet::event]`](`event`) must be present if `RuntimeEvent` + /// exists as a config item in your `#[pallet::config]`. + /// + /// ## Optional: `with_default` + /// + /// An optional `with_default` argument may also be specified. Doing so will automatically + /// generate a `DefaultConfig` trait inside your pallet which is suitable for use with + /// [`#[derive_impl(..)`](`frame_support::derive_impl`) to derive a default testing + /// config: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # use core::fmt::Debug; + /// # use frame_support::traits::Contains; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::config(with_default)] // <- with_default is optional + /// pub trait Config: frame_system::Config { + /// /// The overarching event type. + /// #[pallet::no_default_bounds] // Default is not supported for RuntimeEvent + /// type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// + /// // ...other config items get default + /// } + /// + /// #[pallet::event] + /// pub enum Event { + /// SomeEvent(u16, u32), + /// } + /// } + /// ``` + /// + /// As shown above, you may also attach the [`#[pallet::no_default]`](`no_default`) + /// attribute to specify that a particular trait item _cannot_ be used as a default when a + /// test `Config` is derived using the [`#[derive_impl(..)]`](`frame_support::derive_impl`) + /// attribute macro. This will cause that particular trait item to simply not appear in + /// default testing configs based on this config (the trait item will not be included in + /// `DefaultConfig`). + /// + /// ### `DefaultConfig` Caveats + /// + /// The auto-generated `DefaultConfig` trait: + /// - is always a _subset_ of your pallet's `Config` trait. + /// - can only contain items that don't rely on externalities, such as + /// `frame_system::Config`. + /// + /// Trait items that _do_ rely on externalities should be marked with + /// [`#[pallet::no_default]`](`no_default`) + /// + /// Consequently: + /// - Any items that rely on externalities _must_ be marked with + /// [`#[pallet::no_default]`](`no_default`) or your trait will fail to compile when used + /// with [`derive_impl`](`frame_support::derive_impl`). + /// - Items marked with [`#[pallet::no_default]`](`no_default`) are entirely excluded from + /// the `DefaultConfig` trait, and therefore any impl of `DefaultConfig` doesn't need to + /// implement such items. + /// + /// For more information, see [`frame_support::derive_impl`]. + pub use frame_support_procedural::config; + + /// Allows defining an enum that gets composed as an aggregate enum by `construct_runtime`. + /// + /// The `#[pallet::composite_enum]` attribute allows you to define an enum that gets + /// composed as an aggregate enum by `construct_runtime`. This is similar in principle with + /// [frame_support_procedural::event] and [frame_support_procedural::error]. + /// + /// The attribute currently only supports enum definitions, and identifiers that are named + /// `FreezeReason`, `HoldReason`, `LockId` or `SlashReason`. Arbitrary identifiers for the + /// enum are not supported. The aggregate enum generated by + /// [`frame_support::construct_runtime`] will have the name of `RuntimeFreezeReason`, + /// `RuntimeHoldReason`, `RuntimeLockId` and `RuntimeSlashReason` respectively. + /// + /// NOTE: The aggregate enum generated by `construct_runtime` generates a conversion + /// function from the pallet enum to the aggregate enum, and automatically derives the + /// following traits: + /// + /// ```ignore + /// Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo, + /// RuntimeDebug + /// ``` + /// + /// For ease of usage, when no `#[derive]` attributes are found for the enum under + /// [`#[pallet::composite_enum]`](composite_enum), the aforementioned traits are + /// automatically derived for it. The inverse is also true: if there are any `#[derive]` + /// attributes found for the enum, then no traits will automatically be derived for it. + /// + /// e.g, defining `HoldReason` in a pallet + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::composite_enum] + /// pub enum HoldReason { + /// /// The NIS Pallet has reserved it for a non-fungible receipt. + /// #[codec(index = 0)] + /// SomeHoldReason, + /// #[codec(index = 1)] + /// SomeOtherHoldReason, + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + pub use frame_support_procedural::composite_enum; + + /// Allows the pallet to validate unsigned transactions. + /// + /// Item must be defined as: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::validate_unsigned] + /// impl sp_runtime::traits::ValidateUnsigned for Pallet { + /// type Call = Call; + /// + /// fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity { + /// // Your implementation details here + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// I.e. a trait implementation with bound `T: Config`, of trait + /// [`ValidateUnsigned`](frame_support::pallet_prelude::ValidateUnsigned) for + /// type `Pallet`, and some optional where clause. + /// + /// NOTE: There is also the [`sp_runtime::traits::TransactionExtension`] trait that can be + /// used to add some specific logic for transaction validation. + /// + /// ## Macro expansion + /// + /// The macro currently makes no use of this information, but it might use this information + /// in the future to give information directly to [`frame_support::construct_runtime`]. + pub use frame_support_procedural::validate_unsigned; + + /// Allows defining a struct implementing the [`Get`](frame_support::traits::Get) trait to + /// ease the use of storage types. + /// + /// This attribute is meant to be used alongside [`#[pallet::storage]`](`storage`) to + /// define a storage's default value. This attribute can be used multiple times. + /// + /// Item must be defined as: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use sp_runtime::FixedU128; + /// # use frame_support::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::storage] + /// pub(super) type SomeStorage = + /// StorageValue<_, FixedU128, ValueQuery, DefaultForSomeValue>; + /// + /// // Define default for ParachainId + /// #[pallet::type_value] + /// pub fn DefaultForSomeValue() -> FixedU128 { + /// FixedU128::from_u32(1) + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// ## Macro expansion + /// + /// The macro renames the function to some internal name, generates a struct with the + /// original name of the function and its generic, and implements `Get<$ReturnType>` by + /// calling the user defined function. + pub use frame_support_procedural::type_value; + + /// Allows defining a storage version for the pallet. + /// + /// Because the `pallet::pallet` macro implements + /// [`GetStorageVersion`](frame_support::traits::GetStorageVersion), the current storage + /// version needs to be communicated to the macro. This can be done by using the + /// `pallet::storage_version` attribute: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::StorageVersion; + /// # use frame_support::traits::GetStorageVersion; + /// # + /// const STORAGE_VERSION: StorageVersion = StorageVersion::new(5); + /// + /// #[pallet::pallet] + /// #[pallet::storage_version(STORAGE_VERSION)] + /// pub struct Pallet(_); + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// If not present, the current storage version is set to the default value. + pub use frame_support_procedural::storage_version; + + /// The `#[pallet::hooks]` attribute allows you to specify a + /// [`frame_support::traits::Hooks`] implementation for `Pallet` that specifies + /// pallet-specific logic. + /// + /// The item the attribute attaches to must be defined as follows: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::hooks] + /// impl Hooks> for Pallet { + /// // Implement hooks here + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// I.e. a regular trait implementation with generic bound: `T: Config`, for the trait + /// `Hooks>` (they are defined in preludes), for the type `Pallet`. + /// + /// Optionally, you could add a where clause. + /// + /// ## Macro expansion + /// + /// The macro implements the traits + /// [`OnInitialize`](frame_support::traits::OnInitialize), + /// [`OnIdle`](frame_support::traits::OnIdle), + /// [`OnFinalize`](frame_support::traits::OnFinalize), + /// [`OnRuntimeUpgrade`](frame_support::traits::OnRuntimeUpgrade), + /// [`OffchainWorker`](frame_support::traits::OffchainWorker), and + /// [`IntegrityTest`](frame_support::traits::IntegrityTest) using + /// the provided [`Hooks`](frame_support::traits::Hooks) implementation. + /// + /// NOTE: `OnRuntimeUpgrade` is implemented with `Hooks::on_runtime_upgrade` and some + /// additional logic. E.g. logic to write the pallet version into storage. + /// + /// NOTE: The macro also adds some tracing logic when implementing the above traits. The + /// following hooks emit traces: `on_initialize`, `on_finalize` and `on_runtime_upgrade`. + pub use frame_support_procedural::hooks; + + /// Generates a helper function on `Pallet` that handles deposit events. + /// + /// NOTE: For instantiable pallets, the event must be generic over `T` and `I`. + /// + /// ## Macro expansion + /// + /// The macro will add on enum `Event` the attributes: + /// * `#[derive(`[`frame_support::CloneNoBound`]`)]` + /// * `#[derive(`[`frame_support::EqNoBound`]`)]` + /// * `#[derive(`[`frame_support::PartialEqNoBound`]`)]` + /// * `#[derive(`[`frame_support::RuntimeDebugNoBound`]`)]` + /// * `#[derive(`[`codec::Encode`]`)]` + /// * `#[derive(`[`codec::Decode`]`)]` + /// + /// The macro implements `From>` for (). + /// + /// The macro implements a metadata function on `Event` returning the `EventMetadata`. + /// + /// If `#[pallet::generate_deposit]` is present then the macro implements `fn + /// deposit_event` on `Pallet`. + pub use frame_support_procedural::generate_deposit; + + /// Allows defining logic to make an extrinsic call feeless. + /// + /// Each dispatchable may be annotated with the `#[pallet::feeless_if($closure)]` + /// attribute, which explicitly defines the condition for the dispatchable to be feeless. + /// + /// The arguments for the closure must be the referenced arguments of the dispatchable + /// function. + /// + /// The closure must return `bool`. + /// + /// ### Example + /// + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # use frame_system::pallet_prelude::*; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::call_index(0)] + /// /// Marks this call as feeless if `foo` is zero. + /// #[pallet::feeless_if(|_origin: &OriginFor, foo: &u32| -> bool { + /// *foo == 0 + /// })] + /// pub fn something( + /// _: OriginFor, + /// foo: u32, + /// ) -> DispatchResult { + /// unimplemented!() + /// } + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// + /// Please note that this only works for signed dispatchables and requires a signed + /// extension such as [`pallet_skip_feeless_payment::SkipCheckIfFeeless`] to wrap the + /// existing payment extension. Else, this is completely ignored and the dispatchable is + /// still charged. + /// + /// ### Macro expansion + /// + /// The macro implements the [`pallet_skip_feeless_payment::CheckIfFeeless`] trait on the + /// dispatchable and calls the corresponding closure in the implementation. + /// + /// [`pallet_skip_feeless_payment::SkipCheckIfFeeless`]: ../../pallet_skip_feeless_payment/struct.SkipCheckIfFeeless.html + /// [`pallet_skip_feeless_payment::CheckIfFeeless`]: ../../pallet_skip_feeless_payment/struct.SkipCheckIfFeeless.html + pub use frame_support_procedural::feeless_if; + + /// Allows defining an error enum that will be returned from the dispatchable when an error + /// occurs. + /// + /// The information for this error type is then stored in runtime metadata. + /// + /// Item must be defined as so: + /// + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::error] + /// pub enum Error { + /// /// SomeFieldLessVariant doc + /// SomeFieldLessVariant, + /// /// SomeVariantWithOneField doc + /// SomeVariantWithOneField(u32), + /// } + /// # + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// } + /// ``` + /// I.e. a regular enum named `Error`, with generic `T` and fieldless or multiple-field + /// variants. + /// + /// Any field type in the enum variants must implement [`scale_info::TypeInfo`] in order to + /// be properly used in the metadata, and its encoded size should be as small as possible, + /// preferably 1 byte in size in order to reduce storage size. The error enum itself has an + /// absolute maximum encoded size specified by + /// [`frame_support::MAX_MODULE_ERROR_ENCODED_SIZE`]. + /// + /// (1 byte can still be 256 different errors. The more specific the error, the easier it + /// is to diagnose problems and give a better experience to the user. Don't skimp on having + /// lots of individual error conditions.) + /// + /// Field types in enum variants must also implement [`frame_support::PalletError`], + /// otherwise the pallet will fail to compile. Rust primitive types have already + /// implemented the [`frame_support::PalletError`] trait along with some commonly used + /// stdlib types such as [`Option`] and [`sp_std::marker::PhantomData`], and hence + /// in most use cases, a manual implementation is not necessary and is discouraged. + /// + /// The generic `T` must not bound anything and a `where` clause is not allowed. That said, + /// bounds and/or a where clause should not needed for any use-case. + /// + /// ## Macro expansion + /// + /// The macro implements the [`Debug`] trait and functions `as_u8` using variant position, + /// and `as_str` using variant doc. + /// + /// The macro also implements `From>` for `&'static str` and `From>` for + /// `DispatchError`. + pub use frame_support_procedural::error; + + /// Allows defining pallet events. + /// + /// Pallet events are stored under the `system` / `events` key when the block is applied + /// (and then replaced when the next block writes it's events). + /// + /// The Event enum can be defined as follows: + /// + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// # use frame_support::pallet_prelude::IsType; + /// # + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::event] + /// #[pallet::generate_deposit(fn deposit_event)] // Optional + /// pub enum Event { + /// /// SomeEvent doc + /// SomeEvent(u16, u32), // SomeEvent with two fields + /// } + /// + /// #[pallet::config] + /// pub trait Config: frame_system::Config { + /// /// The overarching runtime event type. + /// type RuntimeEvent: From> + /// + IsType<::RuntimeEvent>; + /// } + /// } + /// ``` + /// + /// I.e. an enum (with named or unnamed fields variant), named `Event`, with generic: none + /// or `T` or `T: Config`, and optional w here clause. + /// + /// `RuntimeEvent` must be defined in the `Config`, as shown in the example. + /// + /// Each field must implement [`Clone`], [`Eq`], [`PartialEq`], [`codec::Encode`], + /// [`codec::Decode`], and [`Debug`] (on std only). For ease of use, bound by the trait + /// `Member`, available in [`frame_support::pallet_prelude`]. + pub use frame_support_procedural::event; + + /// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. + /// + /// In slightly simplified terms, this macro declares the set of "transactions" of a + /// pallet. /// /// > The exact definition of **extrinsic** can be found in /// > [`sp_runtime::generic::UncheckedExtrinsic`]. @@ -2338,14 +2004,24 @@ pub mod pallet_macros { /// If no `#[pallet::call]` exists, then a default implementation corresponding to the /// following code is automatically generated: /// - /// ```ignore - /// #[pallet::call] - /// impl Pallet {} + /// ``` + /// #[frame_support::pallet(dev_mode)] + /// mod pallet { + /// #[pallet::pallet] + /// pub struct Pallet(_); + /// + /// #[pallet::call] // <- automatically generated + /// impl Pallet {} // <- automatically generated + /// + /// #[pallet::config] + /// pub trait Config: frame_system::Config {} + /// } /// ``` pub use frame_support_procedural::call; - /// Enforce the index of a variant in the generated `enum Call`. See [`call`] for more - /// information. + /// Enforce the index of a variant in the generated `enum Call`. + /// + /// See [`call`] for more information. /// /// All call indexes start from 0, until it encounters a dispatchable function with a /// defined call index. The dispatchable function that lexically follows the function with @@ -2355,7 +2031,9 @@ pub mod pallet_macros { pub use frame_support_procedural::call_index; /// Declares the arguments of a [`call`] function to be encoded using - /// [`codec::Compact`]. This will results in smaller extrinsic encoding. + /// [`codec::Compact`]. + /// + /// This will results in smaller extrinsic encoding. /// /// A common example of `compact` is for numeric values that are often times far far away /// from their theoretical maximum. For example, in the context of a crypto-currency, the @@ -2451,8 +2129,8 @@ pub mod pallet_macros { /// ``` pub use frame_support_procedural::genesis_build; - /// The `#[pallet::constant]` attribute can be used to add an associated type trait bounded - /// by [`Get`](frame_support::pallet_prelude::Get) from [`pallet::config`](`macro@config`) + /// Allows adding an associated type trait bounded by + /// [`Get`](frame_support::pallet_prelude::Get) from [`pallet::config`](`macro@config`) /// into metadata. /// /// ## Example @@ -2473,9 +2151,10 @@ pub mod pallet_macros { /// ``` pub use frame_support_procedural::constant; - /// Declares a type alias as a storage item. Storage items are pointers to data stored - /// on-chain (the *blockchain state*), under a specific key. The exact key is dependent on - /// the type of the storage. + /// Declares a type alias as a storage item. + /// + /// Storage items are pointers to data stored on-chain (the *blockchain state*), under a + /// specific key. The exact key is dependent on the type of the storage. /// /// > From the perspective of this pallet, the entire blockchain state is abstracted behind /// > a key-value api, namely [`sp_io::storage`]. @@ -2655,6 +2334,8 @@ pub mod pallet_macros { /// * [`macro@getter`]: Creates a custom getter function. /// * [`macro@storage_prefix`]: Overrides the default prefix of the storage item. /// * [`macro@unbounded`]: Declares the storage item as unbounded. + /// * [`macro@disable_try_decode_storage`]: Declares that try-runtime checks should not + /// attempt to decode the storage item. /// /// #### Example /// ``` @@ -2670,10 +2351,14 @@ pub mod pallet_macros { /// #[pallet::getter(fn foo)] /// #[pallet::storage_prefix = "OtherFoo"] /// #[pallet::unbounded] + /// #[pallet::disable_try_decode_storage] /// pub type Foo = StorageValue<_, u32, ValueQuery>; /// } /// ``` pub use frame_support_procedural::storage; + + /// Allows defining conditions for a task to run. + /// /// This attribute is attached to a function inside an `impl` block annoated with /// [`pallet::tasks_experimental`](`tasks_experimental`) to define the conditions for a /// given work item to be valid. @@ -2682,6 +2367,9 @@ pub mod pallet_macros { /// should have the same signature as the function it is attached to, except that it should /// return a `bool` instead. pub use frame_support_procedural::task_condition; + + /// Allows defining an index for a task. + /// /// This attribute is attached to a function inside an `impl` block annoated with /// [`pallet::tasks_experimental`](`tasks_experimental`) to define the index of a given /// work item. @@ -2689,22 +2377,29 @@ pub mod pallet_macros { /// It takes an integer literal as input, which is then used to define the index. This /// index should be unique for each function in the `impl` block. pub use frame_support_procedural::task_index; + + /// Allows defining an iterator over available work items for a task. + /// /// This attribute is attached to a function inside an `impl` block annoated with - /// [`pallet::tasks_experimental`](`tasks_experimental`) to define an iterator over the - /// available work items for a task. + /// [`pallet::tasks_experimental`](`tasks_experimental`). /// /// It takes an iterator as input that yields a tuple with same types as the function /// arguments. pub use frame_support_procedural::task_list; + + /// Allows defining the weight of a task. + /// /// This attribute is attached to a function inside an `impl` block annoated with /// [`pallet::tasks_experimental`](`tasks_experimental`) define the weight of a given work /// item. /// /// It takes a closure as input, which should return a `Weight` value. pub use frame_support_procedural::task_weight; + /// Allows you to define some service work that can be recognized by a script or an - /// off-chain worker. Such a script can then create and submit all such work items at any - /// given time. + /// off-chain worker. + /// + /// Such a script can then create and submit all such work items at any given time. /// /// These work items are defined as instances of the [`Task`](frame_support::traits::Task) /// trait. [`pallet:tasks_experimental`](`tasks_experimental`) when attached to an `impl` @@ -2729,6 +2424,64 @@ pub mod pallet_macros { /// Now, this can be executed as follows: #[doc = docify::embed!("src/tests/tasks.rs", tasks_work)] pub use frame_support_procedural::tasks_experimental; + + /// Allows a pallet to declare a type as an origin. + /// + /// If defined as such, this type will be amalgamated at the runtime level into + /// `RuntimeOrigin`, very similar to [`call`], [`error`] and [`event`]. See + /// [`composite_enum`] for similar cases. + /// + /// Origin is a complex FRAME topics and is further explained in `polkadot_sdk_docs`. + /// + /// ## Syntax Variants + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// # #[pallet::pallet] + /// # pub struct Pallet(_); + /// /// On the spot declaration. + /// #[pallet::origin] + /// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + /// pub enum Origin { + /// Foo, + /// Bar, + /// } + /// } + /// ``` + /// + /// Or, more commonly used: + /// + /// ``` + /// #[frame_support::pallet] + /// mod pallet { + /// # use frame_support::pallet_prelude::*; + /// # #[pallet::config] + /// # pub trait Config: frame_system::Config {} + /// # #[pallet::pallet] + /// # pub struct Pallet(_); + /// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + /// pub enum RawOrigin { + /// Foo, + /// Bar, + /// } + /// + /// #[pallet::origin] + /// pub type Origin = RawOrigin; + /// } + /// ``` + /// + /// ## Warning + /// + /// Modifying any pallet's origin type will cause the runtime level origin type to also + /// change in encoding. If stored anywhere on-chain, this will require a data migration. + /// + /// Read more about origins at the [Origin Reference + /// Docs](../../polkadot_sdk_docs/reference_docs/frame_origin/index.html). + pub use frame_support_procedural::origin; } #[deprecated(note = "Will be removed after July 2023; Use `sp_runtime::traits` directly instead.")] diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index d059a992a8664cd8c3583f7f6fd1bff6af102770..2ceab44cb16bd4bcf356caa07a3506aa76ba79db 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -16,13 +16,21 @@ // limitations under the License. use crate::{ - traits::{GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, StorageVersion}, - weights::{RuntimeDbWeight, Weight}, + defensive, + storage::transactional::with_transaction_opaque_err, + traits::{ + Defensive, GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, SafeMode, + StorageVersion, + }, + weights::{RuntimeDbWeight, Weight, WeightMeter}, }; +use codec::{Decode, Encode, MaxEncodedLen}; use impl_trait_for_tuples::impl_for_tuples; +use sp_arithmetic::traits::Bounded; use sp_core::Get; use sp_io::{hashing::twox_128, storage::clear_prefix, KillStorageResult}; -use sp_std::marker::PhantomData; +use sp_runtime::traits::Zero; +use sp_std::{marker::PhantomData, vec::Vec}; /// Handles storage migration pallet versioning. /// @@ -43,12 +51,30 @@ use sp_std::marker::PhantomData; /// Otherwise, a warning is logged notifying the developer that the upgrade was a noop and should /// probably be removed. /// +/// It is STRONGLY RECOMMENDED to write the unversioned migration logic in a private module and +/// only export the versioned migration logic to prevent accidentally using the unversioned +/// migration in any runtimes. +/// /// ### Examples /// ```ignore /// // In file defining migrations -/// pub struct VersionUncheckedMigrateV5ToV6(sp_std::marker::PhantomData); -/// impl OnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6 { -/// // OnRuntimeUpgrade implementation... +/// +/// /// Private module containing *version unchecked* migration logic. +/// /// +/// /// Should only be used by the [`VersionedMigration`] type in this module to create something to +/// /// export. +/// /// +/// /// We keep this private so the unversioned migration cannot accidentally be used in any runtimes. +/// /// +/// /// For more about this pattern of keeping items private, see +/// /// - https://github.com/rust-lang/rust/issues/30905 +/// /// - https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504/40 +/// mod version_unchecked { +/// use super::*; +/// pub struct MigrateV5ToV6(sp_std::marker::PhantomData); +/// impl OnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6 { +/// // OnRuntimeUpgrade implementation... +/// } /// } /// /// pub type MigrateV5ToV6 = @@ -73,7 +99,7 @@ pub struct VersionedMigration), @@ -91,7 +117,7 @@ impl< const FROM: u16, const TO: u16, Inner: crate::traits::OnRuntimeUpgrade, - Pallet: GetStorageVersion + PalletInfoAccess, + Pallet: GetStorageVersion + PalletInfoAccess, DbWeight: Get, > crate::traits::OnRuntimeUpgrade for VersionedMigration { @@ -100,7 +126,6 @@ impl< /// migration ran or not. #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - use codec::Encode; let on_chain_version = Pallet::on_chain_storage_version(); if on_chain_version == FROM { Ok(VersionedPostUpgradeData::MigrationExecuted(Inner::pre_upgrade()?).encode()) @@ -163,25 +188,25 @@ impl< } } -/// Can store the current pallet version in storage. -pub trait StoreCurrentStorageVersion { - /// Write the current storage version to the storage. - fn store_current_storage_version(); +/// Can store the in-code pallet version on-chain. +pub trait StoreInCodeStorageVersion { + /// Write the in-code storage version on-chain. + fn store_in_code_storage_version(); } -impl + PalletInfoAccess> - StoreCurrentStorageVersion for StorageVersion +impl + PalletInfoAccess> + StoreInCodeStorageVersion for StorageVersion { - fn store_current_storage_version() { - let version = ::current_storage_version(); + fn store_in_code_storage_version() { + let version = ::in_code_storage_version(); version.put::(); } } -impl + PalletInfoAccess> - StoreCurrentStorageVersion for NoStorageVersionSet +impl + PalletInfoAccess> + StoreInCodeStorageVersion for NoStorageVersionSet { - fn store_current_storage_version() { + fn store_in_code_storage_version() { StorageVersion::default().put::(); } } @@ -193,7 +218,7 @@ pub trait PalletVersionToStorageVersionHelper { impl PalletVersionToStorageVersionHelper for T where - T::CurrentStorageVersion: StoreCurrentStorageVersion, + T::InCodeStorageVersion: StoreInCodeStorageVersion, { fn migrate(db_weight: &RuntimeDbWeight) -> Weight { const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; @@ -204,8 +229,7 @@ where sp_io::storage::clear(&pallet_version_key(::name())); - >::store_current_storage_version( - ); + >::store_in_code_storage_version(); db_weight.writes(2) } @@ -226,7 +250,7 @@ impl PalletVersionToStorageVersionHelper for T { /// Migrate from the `PalletVersion` struct to the new [`StorageVersion`] struct. /// -/// This will remove all `PalletVersion's` from the state and insert the current storage version. +/// This will remove all `PalletVersion's` from the state and insert the in-code storage version. pub fn migrate_from_pallet_version_to_storage_version< Pallets: PalletVersionToStorageVersionHelper, >( @@ -344,3 +368,622 @@ impl, DbWeight: Get> frame_support::traits Ok(()) } } + +/// A migration that can proceed in multiple steps. +pub trait SteppedMigration { + /// The cursor type that stores the progress (aka. state) of this migration. + type Cursor: codec::FullCodec + codec::MaxEncodedLen; + + /// The unique identifier type of this migration. + type Identifier: codec::FullCodec + codec::MaxEncodedLen; + + /// The unique identifier of this migration. + /// + /// If two migrations have the same identifier, then they are assumed to be identical. + fn id() -> Self::Identifier; + + /// The maximum number of steps that this migration can take. + /// + /// This can be used to enforce progress and prevent migrations becoming stuck forever. A + /// migration that exceeds its max steps is treated as failed. `None` means that there is no + /// limit. + fn max_steps() -> Option { + None + } + + /// Try to migrate as much as possible with the given weight. + /// + /// **ANY STORAGE CHANGES MUST BE ROLLED-BACK BY THE CALLER UPON ERROR.** This is necessary + /// since the caller cannot return a cursor in the error case. [`Self::transactional_step`] is + /// provided as convenience for a caller. A cursor of `None` implies that the migration is at + /// its end. A migration that once returned `Nonce` is guaranteed to never be called again. + fn step( + cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError>; + + /// Same as [`Self::step`], but rolls back pending changes in the error case. + fn transactional_step( + mut cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + with_transaction_opaque_err(move || match Self::step(cursor, meter) { + Ok(new_cursor) => { + cursor = new_cursor; + sp_runtime::TransactionOutcome::Commit(Ok(cursor)) + }, + Err(err) => sp_runtime::TransactionOutcome::Rollback(Err(err)), + }) + .map_err(|()| SteppedMigrationError::Failed)? + } +} + +/// Error that can occur during a [`SteppedMigration`]. +#[derive(Debug, Encode, Decode, MaxEncodedLen, scale_info::TypeInfo)] +pub enum SteppedMigrationError { + // Transient errors: + /// The remaining weight is not enough to do anything. + /// + /// Can be resolved by calling with at least `required` weight. Note that calling it with + /// exactly `required` weight could cause it to not make any progress. + InsufficientWeight { + /// Amount of weight required to make progress. + required: Weight, + }, + // Permanent errors: + /// The migration cannot decode its cursor and therefore not proceed. + /// + /// This should not happen unless (1) the migration itself returned an invalid cursor in a + /// previous iteration, (2) the storage got corrupted or (3) there is a bug in the caller's + /// code. + InvalidCursor, + /// The migration encountered a permanent error and cannot continue. + Failed, +} + +/// Notification handler for status updates regarding Multi-Block-Migrations. +#[impl_trait_for_tuples::impl_for_tuples(8)] +pub trait MigrationStatusHandler { + /// Notifies of the start of a runtime migration. + fn started() {} + + /// Notifies of the completion of a runtime migration. + fn completed() {} +} + +/// Handles a failed runtime migration. +/// +/// This should never happen, but is here for completeness. +pub trait FailedMigrationHandler { + /// Infallibly handle a failed runtime migration. + /// + /// Gets passed in the optional index of the migration in the batch that caused the failure. + /// Returning `None` means that no automatic handling should take place and the callee decides + /// in the implementation what to do. + fn failed(migration: Option) -> FailedMigrationHandling; +} + +/// Do now allow any transactions to be processed after a runtime upgrade failed. +/// +/// This is **not a sane default**, since it prevents governance intervention. +pub struct FreezeChainOnFailedMigration; + +impl FailedMigrationHandler for FreezeChainOnFailedMigration { + fn failed(_migration: Option) -> FailedMigrationHandling { + FailedMigrationHandling::KeepStuck + } +} + +/// Enter safe mode on a failed runtime upgrade. +/// +/// This can be very useful to manually intervene and fix the chain state. `Else` is used in case +/// that the safe mode could not be entered. +pub struct EnterSafeModeOnFailedMigration( + PhantomData<(SM, Else)>, +); + +impl FailedMigrationHandler + for EnterSafeModeOnFailedMigration +where + ::BlockNumber: Bounded, +{ + fn failed(migration: Option) -> FailedMigrationHandling { + let entered = if SM::is_entered() { + SM::extend(Bounded::max_value()) + } else { + SM::enter(Bounded::max_value()) + }; + + // If we could not enter or extend safe mode (for whatever reason), then we try the next. + if entered.is_err() { + Else::failed(migration) + } else { + FailedMigrationHandling::KeepStuck + } + } +} + +/// How to proceed after a runtime upgrade failed. +/// +/// There is NO SANE DEFAULT HERE. All options are very dangerous and should be used with care. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FailedMigrationHandling { + /// Resume extrinsic processing of the chain. This will not resume the upgrade. + /// + /// This should be supplemented with additional measures to ensure that the broken chain state + /// does not get further messed up by user extrinsics. + ForceUnstuck, + /// Set the cursor to `Stuck` and keep blocking extrinsics. + KeepStuck, + /// Don't do anything with the cursor and let the handler decide. + /// + /// This can be useful in cases where the other two options would overwrite any changes that + /// were done by the handler to the cursor. + Ignore, +} + +/// Something that can do multi step migrations. +pub trait MultiStepMigrator { + /// Hint for whether [`Self::step`] should be called. + fn ongoing() -> bool; + + /// Do the next step in the MBM process. + /// + /// Must gracefully handle the case that it is currently not upgrading. + fn step() -> Weight; +} + +impl MultiStepMigrator for () { + fn ongoing() -> bool { + false + } + + fn step() -> Weight { + Weight::zero() + } +} + +/// Multiple [`SteppedMigration`]. +pub trait SteppedMigrations { + /// The number of migrations that `Self` aggregates. + fn len() -> u32; + + /// The `n`th [`SteppedMigration::id`]. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_id(n: u32) -> Option>; + + /// The [`SteppedMigration::max_steps`] of the `n`th migration. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_max_steps(n: u32) -> Option>; + + /// Do a [`SteppedMigration::step`] on the `n`th migration. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>>; + + /// Do a [`SteppedMigration::transactional_step`] on the `n`th migration. + /// + /// Is guaranteed to return `Some` if `n < Self::len()`. + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>>; + + /// The maximal encoded length across all cursors. + fn cursor_max_encoded_len() -> usize; + + /// The maximal encoded length across all identifiers. + fn identifier_max_encoded_len() -> usize; + + /// Assert the integrity of the migrations. + /// + /// Should be executed as part of a test prior to runtime usage. May or may not need + /// externalities. + #[cfg(feature = "std")] + fn integrity_test() -> Result<(), &'static str> { + use crate::ensure; + let l = Self::len(); + + for n in 0..l { + ensure!(Self::nth_id(n).is_some(), "id is None"); + ensure!(Self::nth_max_steps(n).is_some(), "steps is None"); + + // The cursor that we use does not matter. Hence use empty. + ensure!( + Self::nth_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(), + "steps is None" + ); + ensure!( + Self::nth_transactional_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(), + "steps is None" + ); + } + + Ok(()) + } +} + +impl SteppedMigrations for () { + fn len() -> u32 { + 0 + } + + fn nth_id(_n: u32) -> Option> { + None + } + + fn nth_max_steps(_n: u32) -> Option> { + None + } + + fn nth_step( + _n: u32, + _cursor: Option>, + _meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + None + } + + fn nth_transactional_step( + _n: u32, + _cursor: Option>, + _meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + None + } + + fn cursor_max_encoded_len() -> usize { + 0 + } + + fn identifier_max_encoded_len() -> usize { + 0 + } +} + +// A collection consisting of only a single migration. +impl SteppedMigrations for T { + fn len() -> u32 { + 1 + } + + fn nth_id(_n: u32) -> Option> { + Some(T::id().encode()) + } + + fn nth_max_steps(n: u32) -> Option> { + // It should be generally fine to call with n>0, but the code should not attempt to. + n.is_zero() + .then_some(T::max_steps()) + .defensive_proof("nth_max_steps should only be called with n==0") + } + + fn nth_step( + _n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + if !_n.is_zero() { + defensive!("nth_step should only be called with n==0"); + return None + } + + let cursor = match cursor { + Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) { + Ok(cursor) => Some(cursor), + Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)), + }, + None => None, + }; + + Some(T::step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode()))) + } + + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + if n != 0 { + defensive!("nth_transactional_step should only be called with n==0"); + return None + } + + let cursor = match cursor { + Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) { + Ok(cursor) => Some(cursor), + Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)), + }, + None => None, + }; + + Some( + T::transactional_step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode())), + ) + } + + fn cursor_max_encoded_len() -> usize { + T::Cursor::max_encoded_len() + } + + fn identifier_max_encoded_len() -> usize { + T::Identifier::max_encoded_len() + } +} + +#[impl_trait_for_tuples::impl_for_tuples(1, 30)] +impl SteppedMigrations for Tuple { + fn len() -> u32 { + for_tuples!( #( Tuple::len() )+* ) + } + + fn nth_id(n: u32) -> Option> { + let mut i = 0; + + for_tuples!( #( + if (i + Tuple::len()) > n { + return Tuple::nth_id(n - i) + } + + i += Tuple::len(); + )* ); + + None + } + + fn nth_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + let mut i = 0; + + for_tuples!( #( + if (i + Tuple::len()) > n { + return Tuple::nth_step(n - i, cursor, meter) + } + + i += Tuple::len(); + )* ); + + None + } + + fn nth_transactional_step( + n: u32, + cursor: Option>, + meter: &mut WeightMeter, + ) -> Option>, SteppedMigrationError>> { + let mut i = 0; + + for_tuples! ( #( + if (i + Tuple::len()) > n { + return Tuple::nth_transactional_step(n - i, cursor, meter) + } + + i += Tuple::len(); + )* ); + + None + } + + fn nth_max_steps(n: u32) -> Option> { + let mut i = 0; + + for_tuples!( #( + if (i + Tuple::len()) > n { + return Tuple::nth_max_steps(n - i) + } + + i += Tuple::len(); + )* ); + + None + } + + fn cursor_max_encoded_len() -> usize { + let mut max_len = 0; + + for_tuples!( #( + max_len = max_len.max(Tuple::cursor_max_encoded_len()); + )* ); + + max_len + } + + fn identifier_max_encoded_len() -> usize { + let mut max_len = 0; + + for_tuples!( #( + max_len = max_len.max(Tuple::identifier_max_encoded_len()); + )* ); + + max_len + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{assert_ok, storage::unhashed}; + + #[derive(Decode, Encode, MaxEncodedLen, Eq, PartialEq)] + pub enum Either { + Left(L), + Right(R), + } + + pub struct M0; + impl SteppedMigration for M0 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 0 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("M0"); + unhashed::put(&[0], &()); + Ok(None) + } + } + + pub struct M1; + impl SteppedMigration for M1 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 1 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("M1"); + unhashed::put(&[1], &()); + Ok(None) + } + + fn max_steps() -> Option { + Some(1) + } + } + + pub struct M2; + impl SteppedMigration for M2 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 2 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("M2"); + unhashed::put(&[2], &()); + Ok(None) + } + + fn max_steps() -> Option { + Some(2) + } + } + + pub struct F0; + impl SteppedMigration for F0 { + type Cursor = (); + type Identifier = u8; + + fn id() -> Self::Identifier { + 3 + } + + fn step( + _cursor: Option, + _meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + log::info!("F0"); + unhashed::put(&[3], &()); + Err(SteppedMigrationError::Failed) + } + } + + // Three migrations combined to execute in order: + type Triple = (M0, (M1, M2)); + // Six migrations, just concatenating the ones from before: + type Hextuple = (Triple, Triple); + + #[test] + fn singular_migrations_work() { + assert_eq!(M0::max_steps(), None); + assert_eq!(M1::max_steps(), Some(1)); + assert_eq!(M2::max_steps(), Some(2)); + + assert_eq!(<(M0, M1)>::nth_max_steps(0), Some(None)); + assert_eq!(<(M0, M1)>::nth_max_steps(1), Some(Some(1))); + assert_eq!(<(M0, M1, M2)>::nth_max_steps(2), Some(Some(2))); + + assert_eq!(<(M0, M1)>::nth_max_steps(2), None); + } + + #[test] + fn tuple_migrations_work() { + assert_eq!(<() as SteppedMigrations>::len(), 0); + assert_eq!(<((), ((), ())) as SteppedMigrations>::len(), 0); + assert_eq!(::len(), 3); + assert_eq!(::len(), 6); + + // Check the IDs. The index specific functions all return an Option, + // to account for the out-of-range case. + assert_eq!(::nth_id(0), Some(0u8.encode())); + assert_eq!(::nth_id(1), Some(1u8.encode())); + assert_eq!(::nth_id(2), Some(2u8.encode())); + + sp_io::TestExternalities::default().execute_with(|| { + for n in 0..3 { + ::nth_step( + n, + Default::default(), + &mut WeightMeter::new(), + ); + } + }); + } + + #[test] + fn integrity_test_works() { + sp_io::TestExternalities::default().execute_with(|| { + assert_ok!(<() as SteppedMigrations>::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + assert_ok!(::integrity_test()); + }); + } + + #[test] + fn transactional_rollback_works() { + sp_io::TestExternalities::default().execute_with(|| { + assert_ok!(<(M0, F0) as SteppedMigrations>::nth_transactional_step( + 0, + Default::default(), + &mut WeightMeter::new() + ) + .unwrap()); + assert!(unhashed::exists(&[0])); + + let _g = crate::StorageNoopGuard::new(); + assert!(<(M0, F0) as SteppedMigrations>::nth_transactional_step( + 1, + Default::default(), + &mut WeightMeter::new() + ) + .unwrap() + .is_err()); + assert!(<(F0, M1) as SteppedMigrations>::nth_transactional_step( + 0, + Default::default(), + &mut WeightMeter::new() + ) + .unwrap() + .is_err()); + }); + } +} diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index c77de1f976f60f18ab87013e03b59bd6ee88db8f..8ebe7b31da80d3511b4a0003637d1f45b52d5b11 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -1712,7 +1712,7 @@ mod test { use bounded_vec::BoundedVec; use frame_support::traits::ConstU32; use generator::StorageValue as _; - use sp_core::hashing::twox_128; + use sp_crypto_hashing::twox_128; use sp_io::TestExternalities; use weak_bounded_vec::WeakBoundedVec; diff --git a/substrate/frame/support/src/storage/transactional.rs b/substrate/frame/support/src/storage/transactional.rs index d42e1809e91292a8e0ad367af4e203b8b1b17f87..0671db4a3a86bc76f7706df6eefe28a8c34c0701 100644 --- a/substrate/frame/support/src/storage/transactional.rs +++ b/substrate/frame/support/src/storage/transactional.rs @@ -127,6 +127,22 @@ where } } +/// Same as [`with_transaction`] but casts any internal error to `()`. +/// +/// This rids `E` of the `From` bound that is required by `with_transaction`. +pub fn with_transaction_opaque_err(f: F) -> Result, ()> +where + F: FnOnce() -> TransactionOutcome>, +{ + with_transaction(move || -> TransactionOutcome, DispatchError>> { + match f() { + TransactionOutcome::Commit(res) => TransactionOutcome::Commit(Ok(res)), + TransactionOutcome::Rollback(res) => TransactionOutcome::Rollback(Ok(res)), + } + }) + .map_err(|_| ()) +} + /// Same as [`with_transaction`] but without a limit check on nested transactional layers. /// /// This is mostly for backwards compatibility before there was a transactional layer limit. diff --git a/substrate/frame/support/src/tests/mod.rs b/substrate/frame/support/src/tests/mod.rs index c6a0b6cde7737e7510ab124bacef86a1a55aca96..c63bfb181c3ff0ae56197432046dc381761f4dae 100644 --- a/substrate/frame/support/src/tests/mod.rs +++ b/substrate/frame/support/src/tests/mod.rs @@ -171,7 +171,7 @@ pub mod frame_system { pub data: Vec<(u32, u64)>, pub test_config: Vec<(u32, u32, u64)>, #[serde(skip)] - pub _config: sp_std::marker::PhantomData, + pub _config: core::marker::PhantomData, } impl Default for GenesisConfig { diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 9afd9c161303410c5d21903eab6511d02f3569c9..1997d8fc223efe7ff1d1be17e2bc10fb973d2cb9 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -37,7 +37,7 @@ pub use members::{AllowAll, DenyAll, Filter}; pub use members::{ AsContains, ChangeMembers, Contains, ContainsLengthBound, ContainsPair, Equals, Everything, EverythingBut, FromContainsPair, InitializeMembers, InsideBoth, IsInVec, Nothing, - RankedMembers, SortedMembers, TheseExcept, + RankedMembers, RankedMembersSwapHandler, SortedMembers, TheseExcept, }; mod validation; @@ -59,9 +59,10 @@ pub use misc::{ AccountTouch, Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, DefensiveMax, DefensiveMin, DefensiveSaturating, DefensiveTruncateFrom, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, - ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, - Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, - TryCollect, TryDrop, TypedGet, UnixTime, VariantCount, WrapperKeepOpaque, WrapperOpaque, + ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsInherent, + IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, + SameOrOther, Time, TryCollect, TryDrop, TypedGet, UnixTime, VariantCount, VariantCountOf, + WrapperKeepOpaque, WrapperOpaque, }; #[allow(deprecated)] pub use misc::{PreimageProvider, PreimageRecipient}; @@ -85,7 +86,8 @@ mod hooks; pub use hooks::GenesisBuild; pub use hooks::{ BeforeAllRuntimeMigrations, BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis, - OnIdle, OnInitialize, OnRuntimeUpgrade, OnTimestampSet, + OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, OnTimestampSet, PostInherents, + PostTransactions, PreInherents, }; pub mod schedule; @@ -123,6 +125,8 @@ pub use safe_mode::{SafeMode, SafeModeError, SafeModeNotify}; mod tx_pause; pub use tx_pause::{TransactionPause, TransactionPauseError}; +pub mod dynamic_params; + pub mod tasks; pub use tasks::Task; diff --git a/substrate/frame/support/src/traits/dispatch.rs b/substrate/frame/support/src/traits/dispatch.rs index d0cedb708cf1d7c24d1d0a6be3ee5596a4c2df10..212c3080352d7a4350a2638ae9204cc0020e4e22 100644 --- a/substrate/frame/support/src/traits/dispatch.rs +++ b/substrate/frame/support/src/traits/dispatch.rs @@ -19,11 +19,11 @@ use crate::dispatch::{DispatchResultWithPostInfo, Parameter, RawOrigin}; use codec::MaxEncodedLen; +use core::{cmp::Ordering, marker::PhantomData}; use sp_runtime::{ traits::{BadOrigin, Get, Member, Morph, TryMorph}, Either, }; -use sp_std::{cmp::Ordering, marker::PhantomData}; use super::misc; @@ -85,7 +85,7 @@ pub trait EnsureOrigin { /// ```rust /// # use frame_support::traits::{EnsureOriginEqualOrHigherPrivilege, PrivilegeCmp, EnsureOrigin as _}; /// # use sp_runtime::traits::{parameter_types, Get}; -/// # use sp_std::cmp::Ordering; +/// # use core::cmp::Ordering; /// /// #[derive(Eq, PartialEq, Debug)] /// pub enum Origin { @@ -124,7 +124,7 @@ pub trait EnsureOrigin { /// assert!(EnsureOrigin::ensure_origin(Origin::NormalUser).is_err()); /// ``` pub struct EnsureOriginEqualOrHigherPrivilege( - sp_std::marker::PhantomData<(Origin, PrivilegeCmp)>, + core::marker::PhantomData<(Origin, PrivilegeCmp)>, ); impl EnsureOrigin @@ -218,7 +218,7 @@ macro_rules! impl_ensure_origin_with_arg_ignoring_arg { } /// [`EnsureOrigin`] implementation that always fails. -pub struct NeverEnsureOrigin(sp_std::marker::PhantomData); +pub struct NeverEnsureOrigin(core::marker::PhantomData); impl EnsureOrigin for NeverEnsureOrigin { type Success = Success; fn try_origin(o: OO) -> Result { @@ -235,7 +235,7 @@ impl_ensure_origin_with_arg_ignoring_arg! { {} } -pub struct AsEnsureOriginWithArg(sp_std::marker::PhantomData); +pub struct AsEnsureOriginWithArg(core::marker::PhantomData); impl> EnsureOriginWithArg for AsEnsureOriginWithArg { @@ -353,7 +353,7 @@ impl< /// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. /// /// Successful origin is derived from the left side. -pub struct EitherOfDiverse(sp_std::marker::PhantomData<(L, R)>); +pub struct EitherOfDiverse(core::marker::PhantomData<(L, R)>); impl, R: EnsureOrigin> EnsureOrigin for EitherOfDiverse { @@ -402,7 +402,7 @@ pub type EnsureOneOf = EitherOfDiverse; /// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. /// /// Successful origin is derived from the left side. -pub struct EitherOf(sp_std::marker::PhantomData<(L, R)>); +pub struct EitherOf(core::marker::PhantomData<(L, R)>); impl< OuterOrigin, L: EnsureOrigin, @@ -482,7 +482,7 @@ pub trait OriginTrait: Sized { type Call; /// The caller origin, overarching type of all pallets origins. - type PalletsOrigin: Into + CallerTrait + MaxEncodedLen; + type PalletsOrigin: Send + Sync + Into + CallerTrait + MaxEncodedLen; /// The AccountId used across the system. type AccountId; @@ -490,12 +490,20 @@ pub trait OriginTrait: Sized { /// Add a filter to the origin. fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static); - /// Reset origin filters to default one, i.e `frame_system::Config::BaseCallFilter`. + /// Reset origin filters to default one, i.e `frame_system::1fig::BaseCallFilter`. fn reset_filter(&mut self); /// Replace the caller with caller from the other origin fn set_caller_from(&mut self, other: impl Into); + /// Replace the caller with caller from the other origin + fn set_caller(&mut self, caller: Self::PalletsOrigin); + + /// Replace the caller with caller from the other origin + fn set_caller_from_signed(&mut self, caller_account: Self::AccountId) { + self.set_caller(Self::PalletsOrigin::from(RawOrigin::Signed(caller_account))) + } + /// Filter the call if caller is not root, if false is returned then the call must be filtered /// out. /// @@ -544,6 +552,17 @@ pub trait OriginTrait: Sized { fn as_system_ref(&self) -> Option<&RawOrigin> { self.caller().as_system_ref() } + + /// Extract a reference to the sytsem signer, if that's what the caller is. + fn as_system_signer(&self) -> Option<&Self::AccountId> { + self.caller().as_system_ref().and_then(|s| { + if let RawOrigin::Signed(ref who) = s { + Some(who) + } else { + None + } + }) + } } #[cfg(test)] diff --git a/substrate/frame/support/src/traits/dynamic_params.rs b/substrate/frame/support/src/traits/dynamic_params.rs new file mode 100644 index 0000000000000000000000000000000000000000..8881df04141cc13a97f79bc6df3ed41b8db0323d --- /dev/null +++ b/substrate/frame/support/src/traits/dynamic_params.rs @@ -0,0 +1,153 @@ +// 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 and traits for dynamic parameters. +//! +//! Can be used by 3rd party macros to define dynamic parameters that are compatible with the the +//! `parameters` pallet. + +use codec::MaxEncodedLen; +use frame_support::Parameter; + +/// A dynamic parameter store across an aggregated KV type. +pub trait RuntimeParameterStore { + type AggregratedKeyValue: AggregratedKeyValue; + + /// Get the value of a parametrized key. + /// + /// Should return `None` if no explicit value was set instead of a default. + fn get(key: K) -> Option + where + KV: AggregratedKeyValue, + K: Key + Into<::Key>, + ::Key: IntoKey< + <::AggregratedKeyValue as AggregratedKeyValue>::Key, + >, + <::AggregratedKeyValue as AggregratedKeyValue>::Value: + TryIntoKey<::Value>, + ::Value: TryInto; +} + +/// A dynamic parameter store across a concrete KV type. +pub trait ParameterStore { + /// Get the value of a parametrized key. + fn get(key: K) -> Option + where + K: Key + Into<::Key>, + ::Value: TryInto; +} + +/// Key of a dynamic parameter. +pub trait Key { + /// The value that the key is parametrized with. + type Value; + + /// An opaque representation of `Self::Value`. + type WrappedValue: Into; +} + +/// The aggregated key-value type of a dynamic parameter store. +pub trait AggregratedKeyValue: Parameter { + /// The aggregated key type. + type Key: Parameter + MaxEncodedLen; + + /// The aggregated value type. + type Value: Parameter + MaxEncodedLen; + + /// Split the aggregated key-value type into its parts. + fn into_parts(self) -> (Self::Key, Option); +} + +impl AggregratedKeyValue for () { + type Key = (); + type Value = (); + + fn into_parts(self) -> (Self::Key, Option) { + ((), None) + } +} + +/// Allows to create a `ParameterStore` from a `RuntimeParameterStore`. +/// +/// This concretization is useful when configuring pallets, since a pallet will require a parameter +/// store for its own KV type and not the aggregated runtime-wide KV type. +pub struct ParameterStoreAdapter(sp_std::marker::PhantomData<(PS, KV)>); + +impl ParameterStore for ParameterStoreAdapter +where + PS: RuntimeParameterStore, + KV: AggregratedKeyValue, + ::Key: + IntoKey<<::AggregratedKeyValue as AggregratedKeyValue>::Key>, + ::Value: TryFromKey< + <::AggregratedKeyValue as AggregratedKeyValue>::Value, + >, +{ + fn get(key: K) -> Option + where + K: Key + Into<::Key>, + ::Value: TryInto, + { + PS::get::(key) + } +} + +// workaround for rust bug https://github.com/rust-lang/rust/issues/51445 +mod workaround { + pub trait FromKey: Sized { + #[must_use] + fn from_key(value: T) -> Self; + } + + pub trait IntoKey: Sized { + #[must_use] + fn into_key(self) -> T; + } + + impl IntoKey for T + where + U: FromKey, + { + fn into_key(self) -> U { + U::from_key(self) + } + } + + pub trait TryIntoKey: Sized { + type Error; + + fn try_into_key(self) -> Result; + } + + pub trait TryFromKey: Sized { + type Error; + + fn try_from_key(value: T) -> Result; + } + + impl TryIntoKey for T + where + U: TryFromKey, + { + type Error = U::Error; + + fn try_into_key(self) -> Result { + U::try_from_key(self) + } + } +} +pub use workaround::*; diff --git a/substrate/frame/support/src/traits/error.rs b/substrate/frame/support/src/traits/error.rs index 0f30e266da2dffebef980088e332b312187081ab..7b577fe9995a4d304c991efe92055c779e71c445 100644 --- a/substrate/frame/support/src/traits/error.rs +++ b/substrate/frame/support/src/traits/error.rs @@ -17,7 +17,7 @@ //! Traits for describing and constraining pallet error types. use codec::{Compact, Decode, Encode}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Trait indicating that the implementing type is going to be included as a field in a variant of /// the `#[pallet::error]` enum type. diff --git a/substrate/frame/support/src/traits/hooks.rs b/substrate/frame/support/src/traits/hooks.rs index 20788ce932d8b397990767c6ae8a0d82016cccbd..7d0e5aa1e89ca1d76e81d526881041c09c6e6cf3 100644 --- a/substrate/frame/support/src/traits/hooks.rs +++ b/substrate/frame/support/src/traits/hooks.rs @@ -25,10 +25,74 @@ use crate::weights::Weight; use impl_trait_for_tuples::impl_for_tuples; use sp_runtime::traits::AtLeast32BitUnsigned; use sp_std::prelude::*; +use sp_weights::WeightMeter; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; +/// Provides a callback to execute logic before the all inherents. +pub trait PreInherents { + /// Called before all inherents were applied but after `on_initialize`. + fn pre_inherents() {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl PreInherents for Tuple { + fn pre_inherents() { + for_tuples!( #( Tuple::pre_inherents(); )* ); + } +} + +/// Provides a callback to execute logic after the all inherents. +pub trait PostInherents { + /// Called after all inherents were applied. + fn post_inherents() {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl PostInherents for Tuple { + fn post_inherents() { + for_tuples!( #( Tuple::post_inherents(); )* ); + } +} + +/// Provides a callback to execute logic before the all transactions. +pub trait PostTransactions { + /// Called after all transactions were applied but before `on_finalize`. + fn post_transactions() {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl PostTransactions for Tuple { + fn post_transactions() { + for_tuples!( #( Tuple::post_transactions(); )* ); + } +} + +/// Periodically executes logic. Is not guaranteed to run within a specific timeframe and should +/// only be used on logic that has no deadline. +pub trait OnPoll { + /// Code to execute every now and then at the beginning of the block after inherent application. + /// + /// The remaining weight limit must be respected. + fn on_poll(_n: BlockNumber, _weight: &mut WeightMeter) {} +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl OnPoll for Tuple { + fn on_poll(n: BlockNumber, weight: &mut WeightMeter) { + for_tuples!( #( Tuple::on_poll(n.clone(), weight); )* ); + } +} + /// See [`Hooks::on_initialize`]. pub trait OnInitialize { /// See [`Hooks::on_initialize`]. @@ -90,7 +154,7 @@ impl OnIdle for Tuple { /// /// Implementing this trait for a pallet let's you express operations that should /// happen at genesis. It will be called in an externalities provided environment and -/// will see the genesis state after all pallets have written their genesis state. +/// will set the genesis state after all pallets have written their genesis state. #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] #[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] #[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] @@ -306,19 +370,23 @@ pub trait IntegrityTest { /// end /// ``` /// -/// * `OnRuntimeUpgrade` is only executed before everything else if a code -/// * `OnRuntimeUpgrade` is mandatorily at the beginning of the block body (extrinsics) being -/// processed. change is detected. -/// * Extrinsics start with inherents, and continue with other signed or unsigned extrinsics. -/// * `OnIdle` optionally comes after extrinsics. -/// `OnFinalize` mandatorily comes after `OnIdle`. +/// * [`OnRuntimeUpgrade`](Hooks::OnRuntimeUpgrade) hooks are only executed when a code change is +/// detected. +/// * [`OnRuntimeUpgrade`](Hooks::OnRuntimeUpgrade) hooks are mandatorily executed at the very +/// beginning of the block body, before any extrinsics are processed. +/// * [`Inherents`](sp_inherents) are always executed before any other other signed or unsigned +/// extrinsics. +/// * [`OnIdle`](Hooks::OnIdle) hooks are executed after extrinsics if there is weight remaining in +/// the block. +/// * [`OnFinalize`](Hooks::OnFinalize) hooks are mandatorily executed after +/// [`OnIdle`](Hooks::OnIdle). /// -/// > `OffchainWorker` is not part of this flow, as it is not really part of the consensus/main -/// > block import path, and is called optionally, and in other circumstances. See -/// > [`crate::traits::misc::OffchainWorker`] for more information. +/// > [`OffchainWorker`](crate::traits::misc::OffchainWorker) hooks are not part of this flow, +/// > because they are not part of the consensus/main block building logic. See +/// > [`OffchainWorker`](crate::traits::misc::OffchainWorker) for more information. /// -/// To learn more about the execution of hooks see `frame-executive` as this component is is charge -/// of dispatching extrinsics and placing the hooks in the correct order. +/// To learn more about the execution of hooks see the FRAME `Executive` pallet which is in charge +/// of dispatching extrinsics and calling hooks in the correct order. pub trait Hooks { /// Block initialization hook. This is called at the very beginning of block execution. /// @@ -370,30 +438,44 @@ pub trait Hooks { Weight::zero() } - /// Hook executed when a code change (aka. a "runtime upgrade") is detected by FRAME. + /// A hook to run logic after inherent application. + /// + /// Is not guaranteed to execute in a block and should therefore only be used in no-deadline + /// scenarios. + fn on_poll(_n: BlockNumber, _weight: &mut WeightMeter) {} + + /// Hook executed when a code change (aka. a "runtime upgrade") is detected by the FRAME + /// `Executive` pallet. /// /// Be aware that this is called before [`Hooks::on_initialize`] of any pallet; therefore, a lot /// of the critical storage items such as `block_number` in system pallet might have not been - /// set. + /// set yet. /// - /// Vert similar to [`Hooks::on_initialize`], any code in this block is mandatory and MUST - /// execute. Use with care. + /// Similar to [`Hooks::on_initialize`], any code in this block is mandatory and MUST execute. + /// It is strongly recommended to dry-run the execution of these hooks using + /// [try-runtime-cli](https://github.com/paritytech/try-runtime-cli) to ensure they will not + /// produce and overweight block which can brick your chain. Use with care! /// - /// ## Implementation Note: Versioning + /// ## Implementation Note: Standalone Migrations /// - /// 1. An implementation of this should typically follow a pattern where the version of the - /// pallet is checked against the onchain version, and a decision is made about what needs to be - /// done. This is helpful to prevent accidental repetitive execution of this hook, which can be - /// catastrophic. + /// Additional migrations can be created by directly implementing [`OnRuntimeUpgrade`] on + /// structs and passing them to `Executive`. /// - /// Alternatively, [`frame_support::migrations::VersionedMigration`] can be used to assist with - /// this. + /// ## Implementation Note: Pallet Versioning /// - /// ## Implementation Note: Runtime Level Migration + /// Implementations of this hook are typically wrapped in + /// [`crate::migrations::VersionedMigration`] to ensure the migration is executed exactly + /// once and only when it is supposed to. /// - /// Additional "upgrade hooks" can be created by pallets by a manual implementation of - /// [`Hooks::on_runtime_upgrade`] which can be passed on to `Executive` at the top level - /// runtime. + /// Alternatively, developers can manually implement version checks. + /// + /// Failure to adequately check storage versions can result in accidental repetitive execution + /// of the hook, which can be catastrophic. + /// + /// ## Implementation Note: Weight + /// + /// Typically, implementations of this method are simple enough that weights can be calculated + /// manually. However, if required, a benchmark can also be used. fn on_runtime_upgrade() -> Weight { Weight::zero() } @@ -403,7 +485,7 @@ pub trait Hooks { /// It should focus on certain checks to ensure that the state is sensible. This is never /// executed in a consensus code-path, therefore it can consume as much weight as it needs. /// - /// This hook should not alter any storage. + /// This hook must not alter any storage. #[cfg(feature = "try-runtime")] fn try_state(_n: BlockNumber) -> Result<(), TryRuntimeError> { Ok(()) @@ -415,7 +497,7 @@ pub trait Hooks { /// which will be passed to `post_upgrade` after upgrading for post-check. An empty vector /// should be returned if there is no such need. /// - /// This hook is never meant to be executed on-chain but is meant to be used by testing tools. + /// This hook is never executed on-chain but instead used by testing tools. #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, TryRuntimeError> { Ok(Vec::new()) @@ -434,7 +516,7 @@ pub trait Hooks { } /// Implementing this function on a pallet allows you to perform long-running tasks that are - /// dispatched as separate threads, and entirely independent of the main wasm runtime. + /// dispatched as separate threads, and entirely independent of the main blockchain execution. /// /// This function can freely read from the state, but any change it makes to the state is /// meaningless. Writes can be pushed back to the chain by submitting extrinsics from the diff --git a/substrate/frame/support/src/traits/members.rs b/substrate/frame/support/src/traits/members.rs index d667eaa7e9d8d2b46b39254fccf5e0c6052c9751..3a6e3719593a22c33ad24c24c56f858ea82779ee 100644 --- a/substrate/frame/support/src/traits/members.rs +++ b/substrate/frame/support/src/traits/members.rs @@ -297,6 +297,13 @@ pub trait RankedMembers { fn demote(who: &Self::AccountId) -> DispatchResult; } +/// Handler that can deal with the swap of two members. +#[impl_trait_for_tuples::impl_for_tuples(16)] +pub trait RankedMembersSwapHandler { + /// Member `old` was swapped with `new` at `rank`. + fn swapped(who: &AccountId, new_who: &AccountId, rank: Rank); +} + /// Trait for type that can handle the initialization of account IDs at genesis. pub trait InitializeMembers { /// Initialize the members to the given `members`. diff --git a/substrate/frame/support/src/traits/messages.rs b/substrate/frame/support/src/traits/messages.rs index 995ac4f717911195e4dba202d350c6cbc09ae340..f3d893bcc1d899fce06ef00a38d687bfb3ea0181 100644 --- a/substrate/frame/support/src/traits/messages.rs +++ b/substrate/frame/support/src/traits/messages.rs @@ -123,6 +123,8 @@ impl ServiceQueues for NoopServiceQueues { pub struct QueueFootprint { /// The number of pages in the queue (including overweight pages). pub pages: u32, + /// The number of pages that are ready (not yet processed and also not overweight). + pub ready_pages: u32, /// The storage footprint of the queue (including overweight messages). pub storage: Footprint, } diff --git a/substrate/frame/support/src/traits/metadata.rs b/substrate/frame/support/src/traits/metadata.rs index 0af8d06719fe3543f95fcbd638f4340b321aae49..8bda4186bc967b29a6342ea96dd0a1cdc5072438 100644 --- a/substrate/frame/support/src/traits/metadata.rs +++ b/substrate/frame/support/src/traits/metadata.rs @@ -261,48 +261,71 @@ impl Add for StorageVersion { } } -/// Special marker struct if no storage version is set for a pallet. +/// Special marker struct used when [`storage_version`](crate::pallet_macros::storage_version) is +/// not defined for a pallet. /// /// If you (the reader) end up here, it probably means that you tried to compare /// [`GetStorageVersion::on_chain_storage_version`] against -/// [`GetStorageVersion::current_storage_version`]. This basically means that the -/// [`storage_version`](crate::pallet_macros::storage_version) is missing in the pallet where the -/// mentioned functions are being called. +/// [`GetStorageVersion::in_code_storage_version`]. This basically means that the +/// [`storage_version`](crate::pallet_macros::storage_version) is missing from the pallet where the +/// mentioned functions are being called, and needs to be defined. #[derive(Debug, Default)] pub struct NoStorageVersionSet; -/// Provides information about the storage version of a pallet. +/// Provides information about a pallet's storage versions. /// -/// It differentiates between current and on-chain storage version. Both should be only out of sync -/// when a new runtime upgrade was applied and the runtime migrations did not yet executed. -/// Otherwise it means that the pallet works with an unsupported storage version and unforeseen -/// stuff can happen. +/// Every pallet has two storage versions: +/// 1. An in-code storage version +/// 2. An on-chain storage version /// -/// The current storage version is the version of the pallet as supported at runtime. The active -/// storage version is the version of the pallet in the storage. +/// The in-code storage version is the version of the pallet as defined in the runtime blob, and the +/// on-chain storage version is the version of the pallet stored on-chain. /// -/// It is required to update the on-chain storage version manually when a migration was applied. +/// Storage versions should be only ever be out of sync when a pallet has been updated to a new +/// version and the in-code version is incremented, but the migration has not yet been executed +/// on-chain as part of a runtime upgrade. +/// +/// It is the responsibility of the developer to ensure that the on-chain storage version is set +/// correctly during a migration so that it matches the in-code storage version. pub trait GetStorageVersion { - /// This will be filled out by the [`pallet`](crate::pallet) macro. + /// This type is generated by the [`pallet`](crate::pallet) macro. + /// + /// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't specified, + /// this is set to [`NoStorageVersionSet`] to signify that it is missing. + /// + /// If the [`storage_version`](crate::pallet_macros::storage_version) attribute is specified, + /// this is be set to a [`StorageVersion`] corresponding to the attribute. /// - /// If the [`storage_version`](crate::pallet_macros::storage_version) attribute isn't given - /// this is set to [`NoStorageVersionSet`] to inform the user that the attribute is missing. - /// This should prevent that the user forgets to set a storage version when required. However, - /// this will only work when the user actually tries to call [`Self::current_storage_version`] - /// to compare it against the [`Self::on_chain_storage_version`]. If the attribute is given, - /// this will be set to [`StorageVersion`]. - type CurrentStorageVersion; - - /// Returns the current storage version as supported by the pallet. - fn current_storage_version() -> Self::CurrentStorageVersion; - /// Returns the on-chain storage version of the pallet as stored in the storage. + /// The intention of using [`NoStorageVersionSet`] instead of defaulting to a [`StorageVersion`] + /// of zero is to prevent developers from forgetting to set + /// [`storage_version`](crate::pallet_macros::storage_version) when it is required, like in the + /// case that they wish to compare the in-code storage version to the on-chain storage version. + type InCodeStorageVersion; + + #[deprecated( + note = "This method has been renamed to `in_code_storage_version` and will be removed after March 2024." + )] + /// DEPRECATED: Use [`Self::current_storage_version`] instead. + /// + /// Returns the in-code storage version as specified in the + /// [`storage_version`](crate::pallet_macros::storage_version) attribute, or + /// [`NoStorageVersionSet`] if the attribute is missing. + fn current_storage_version() -> Self::InCodeStorageVersion { + Self::in_code_storage_version() + } + + /// Returns the in-code storage version as specified in the + /// [`storage_version`](crate::pallet_macros::storage_version) attribute, or + /// [`NoStorageVersionSet`] if the attribute is missing. + fn in_code_storage_version() -> Self::InCodeStorageVersion; + /// Returns the storage version of the pallet as last set in the actual on-chain storage. fn on_chain_storage_version() -> StorageVersion; } #[cfg(test)] mod tests { use super::*; - use sp_core::twox_128; + use sp_crypto_hashing::twox_128; struct Pallet1; impl PalletInfoAccess for Pallet1 { diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index bf3053a3f8f59b6d55acadb208a8731c7f080e41..fba546ce21b5414aab43a406922bfd7197c12ca2 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -23,6 +23,7 @@ use impl_trait_for_tuples::impl_for_tuples; use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter}; use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, One, Saturating}; use sp_core::bounded::bounded_vec::TruncateFrom; + #[doc(hidden)] pub use sp_runtime::traits::{ ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, @@ -37,8 +38,6 @@ pub const DEFENSIVE_OP_PUBLIC_ERROR: &str = "a defensive failure has been trigge pub const DEFENSIVE_OP_INTERNAL_ERROR: &str = "Defensive failure has been triggered!"; /// Trait to get the number of variants in any enum. -/// -/// NOTE: can be removed once is stable. pub trait VariantCount { /// Get the number of variants. const VARIANT_COUNT: u32; @@ -48,6 +47,14 @@ impl VariantCount for () { const VARIANT_COUNT: u32 = 0; } +/// Adapter for `Get` to access `VARIANT_COUNT` from `trait pub trait VariantCount {`. +pub struct VariantCountOf(sp_std::marker::PhantomData); +impl Get for VariantCountOf { + fn get() -> u32 { + T::VARIANT_COUNT + } +} + /// Generic function to mark an execution path as ONLY defensive. /// /// Similar to mark a match arm or `if/else` branch as `unreachable!`. @@ -889,11 +896,21 @@ pub trait GetBacking { /// A trait to ensure the inherent are before non-inherent in a block. /// /// This is typically implemented on runtime, through `construct_runtime!`. -pub trait EnsureInherentsAreFirst { +pub trait EnsureInherentsAreFirst: + IsInherent<::Extrinsic> +{ /// Ensure the position of inherent is correct, i.e. they are before non-inherents. /// - /// On error return the index of the inherent with invalid position (counting from 0). - fn ensure_inherents_are_first(block: &Block) -> Result<(), u32>; + /// On error return the index of the inherent with invalid position (counting from 0). On + /// success it returns the index of the last inherent. `0` therefore means that there are no + /// inherents. + fn ensure_inherents_are_first(block: &Block) -> Result; +} + +/// A trait to check if an extrinsic is an inherent. +pub trait IsInherent { + /// Whether this extrinsic is an inherent. + fn is_inherent(ext: &Extrinsic) -> bool; } /// An extrinsic on which we can get access to call. @@ -902,24 +919,13 @@ pub trait ExtrinsicCall: sp_runtime::traits::Extrinsic { fn call(&self) -> &Self::Call; } -#[cfg(feature = "std")] -impl ExtrinsicCall for sp_runtime::testing::TestXt -where - Call: codec::Codec + Sync + Send + TypeInfo, - Extra: TypeInfo, -{ - fn call(&self) -> &Self::Call { - &self.call - } -} - impl ExtrinsicCall for sp_runtime::generic::UncheckedExtrinsic where Address: TypeInfo, Call: TypeInfo, Signature: TypeInfo, - Extra: sp_runtime::traits::SignedExtension + TypeInfo, + Extra: TypeInfo, { fn call(&self) -> &Self::Call { &self.function diff --git a/substrate/frame/support/src/traits/stored_map.rs b/substrate/frame/support/src/traits/stored_map.rs index cbe70f29323491179d945027bfccaaf5cb880671..15df2ef0af55d49c28d271aef10f4ae018163f11 100644 --- a/substrate/frame/support/src/traits/stored_map.rs +++ b/substrate/frame/support/src/traits/stored_map.rs @@ -81,7 +81,7 @@ pub trait StoredMap { /// be the default value), or where the account is being removed or reset back to the default value /// where previously it did exist (though may have been in a default state). This works well with /// system module's `CallOnCreatedAccount` and `CallKillAccount`. -pub struct StorageMapShim(sp_std::marker::PhantomData<(S, K, T)>); +pub struct StorageMapShim(core::marker::PhantomData<(S, K, T)>); impl, K: FullCodec, T: FullCodec + Default> StoredMap for StorageMapShim { diff --git a/substrate/frame/support/src/traits/tokens/currency.rs b/substrate/frame/support/src/traits/tokens/currency.rs index 0030e1261dac1be87f572b1ee26220b2992798cd..8b773115011de27b9920de70860491ec404a54e1 100644 --- a/substrate/frame/support/src/traits/tokens/currency.rs +++ b/substrate/frame/support/src/traits/tokens/currency.rs @@ -211,7 +211,7 @@ pub trait Currency { /// A non-const `Get` implementation parameterised by a `Currency` impl which provides the result /// of `total_issuance`. -pub struct TotalIssuanceOf, A>(sp_std::marker::PhantomData<(C, A)>); +pub struct TotalIssuanceOf, A>(core::marker::PhantomData<(C, A)>); impl, A> Get for TotalIssuanceOf { fn get() -> C::Balance { C::total_issuance() @@ -220,7 +220,7 @@ impl, A> Get for TotalIssuanceOf { /// A non-const `Get` implementation parameterised by a `Currency` impl which provides the result /// of `active_issuance`. -pub struct ActiveIssuanceOf, A>(sp_std::marker::PhantomData<(C, A)>); +pub struct ActiveIssuanceOf, A>(core::marker::PhantomData<(C, A)>); impl, A> Get for ActiveIssuanceOf { fn get() -> C::Balance { C::active_issuance() diff --git a/substrate/frame/support/src/traits/tokens/fungible/regular.rs b/substrate/frame/support/src/traits/tokens/fungible/regular.rs index f15c3418d0ac1247690a488ec3972bce2a3fd592..0157b08bd137b2813236f34e193799cf8267b88a 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/regular.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/regular.rs @@ -321,7 +321,7 @@ where let _extra = Self::can_withdraw(source, amount).into_result(preservation != Expendable)?; Self::can_deposit(dest, amount, Extant).into_result()?; if source == dest { - return Ok(amount); + return Ok(amount) } Self::decrease_balance(source, amount, BestEffort, preservation, Polite)?; diff --git a/substrate/frame/support/src/traits/tokens/imbalance.rs b/substrate/frame/support/src/traits/tokens/imbalance.rs index 9dd8531324dc9d4a2845fd40b09a0d32746a99b6..8eb9b355a4cf6677b1a9f3e1eb77a879a6e0dc70 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance.rs @@ -19,8 +19,8 @@ //! with unbalanced operations. use crate::traits::misc::{SameOrOther, TryDrop}; +use core::ops::Div; use sp_runtime::traits::Saturating; -use sp_std::ops::Div; mod on_unbalanced; mod signed_imbalance; diff --git a/substrate/frame/support/src/traits/voting.rs b/substrate/frame/support/src/traits/voting.rs index f5c9e285ef3a6fb49cb7eadeffea4d44d7a813f3..f18d4ed4e90b33425a16a753cddf57630a8da91f 100644 --- a/substrate/frame/support/src/traits/voting.rs +++ b/substrate/frame/support/src/traits/voting.rs @@ -25,14 +25,24 @@ use sp_runtime::{traits::Member, DispatchError}; use sp_std::prelude::*; pub trait VoteTally { + /// Initializes a new tally. fn new(_: Class) -> Self; + /// Returns the number of positive votes for the tally. fn ayes(&self, class: Class) -> Votes; + /// Returns the approval ratio (positive to total votes) for the tally, without multipliers + /// (e.g. conviction, ranks, etc.). fn support(&self, class: Class) -> Perbill; + /// Returns the approval ratio (positive to total votes) for the tally. fn approval(&self, class: Class) -> Perbill; + /// Returns an instance of the tally representing a unanimous approval, for benchmarking + /// purposes. #[cfg(feature = "runtime-benchmarks")] fn unanimity(class: Class) -> Self; + /// Returns an instance of the tally representing a rejecting state, for benchmarking purposes. #[cfg(feature = "runtime-benchmarks")] fn rejection(class: Class) -> Self; + /// Returns an instance of the tally given some `approval` and `support`, for benchmarking + /// purposes. #[cfg(feature = "runtime-benchmarks")] fn from_requirements(support: Perbill, approval: Perbill, class: Class) -> Self; #[cfg(feature = "runtime-benchmarks")] diff --git a/substrate/frame/support/src/transaction_extensions.rs b/substrate/frame/support/src/transaction_extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..98d78b9b4e4777e4e379975c63a2757f06b47553 --- /dev/null +++ b/substrate/frame/support/src/transaction_extensions.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. + +//! Transaction extensions. + +use crate::{CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_io::hashing::blake2_256; +use sp_runtime::{ + impl_tx_ext_default, + traits::{ + transaction_extension::{TransactionExtensionBase, TransactionExtensionInterior}, + DispatchInfoOf, Dispatchable, IdentifyAccount, TransactionExtension, Verify, + }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, +}; + +#[derive( + CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, +)] +#[codec(encode_bound())] +#[codec(decode_bound())] +pub struct VerifyMultiSignature +where + V: TransactionExtensionInterior, + ::AccountId: TransactionExtensionInterior, +{ + signature: V, + account: ::AccountId, +} + +impl TransactionExtensionBase for VerifyMultiSignature +where + V: TransactionExtensionInterior, + ::AccountId: TransactionExtensionInterior, +{ + const IDENTIFIER: &'static str = "VerifyMultiSignature"; + type Implicit = (); +} + +impl TransactionExtension + for VerifyMultiSignature +where + V: TransactionExtensionInterior, + ::AccountId: TransactionExtensionInterior, + ::RuntimeOrigin: From::AccountId>>, +{ + type Val = (); + type Pre = (); + impl_tx_ext_default!(Call; Context; prepare); + + fn validate( + &self, + _origin: ::RuntimeOrigin, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + _: &mut Context, + _: (), + inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let msg = inherited_implication.using_encoded(blake2_256); + + if !self.signature.verify(&msg[..], &self.account) { + Err(InvalidTransaction::BadProof)? + } + // We clobber the original origin. Maybe we shuld check that it's none? + let origin = Some(self.account.clone()).into(); + Ok((ValidTransaction::default(), (), origin)) + } +} diff --git a/substrate/frame/support/src/weights/block_weights.rs b/substrate/frame/support/src/weights/block_weights.rs index 57a68554755abb007165e751df687d097cd48f17..647b619835757a380d37acd6c8003f758137ba93 100644 --- a/substrate/frame/support/src/weights/block_weights.rs +++ b/substrate/frame/support/src/weights/block_weights.rs @@ -15,24 +15,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16 (Y/M/D) -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01 (Y/M/D) +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! //! SHORT-NAME: `block`, LONG-NAME: `BlockExecution`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` -//! WEIGHT-PATH: `./frame/support/src/weights/` +//! WEIGHT-PATH: `./substrate/frame/support/src/weights/` //! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1.0`, WEIGHT-ADD: `0` // Executed Command: -// ./target/production/substrate +// ./target/production/substrate-node // benchmark // overhead // --chain=dev -// --execution=wasm // --wasm-execution=compiled -// --weight-path=./frame/support/src/weights/ -// --header=./HEADER-APACHE2 +// --weight-path=./substrate/frame/support/src/weights/ +// --header=./substrate/HEADER-APACHE2 // --warmup=10 // --repeat=100 @@ -44,17 +43,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 376_949, 622_462 - /// Average: 390_584 - /// Median: 386_322 - /// Std-Dev: 24792.0 + /// Min, Max: 424_332, 493_017 + /// Average: 437_118 + /// Median: 434_920 + /// Std-Dev: 8798.01 /// /// Percentiles nanoseconds: - /// 99th: 433_299 - /// 95th: 402_688 - /// 75th: 391_645 + /// 99th: 460_074 + /// 95th: 451_580 + /// 75th: 440_307 pub const BlockExecutionWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(390_584), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(437_118), 0); } #[cfg(test)] diff --git a/substrate/frame/support/src/weights/extrinsic_weights.rs b/substrate/frame/support/src/weights/extrinsic_weights.rs index a304f089ff782c34ab9941705503c5e766bb08b0..99b392c4369a523ef2ce08bfdd9f9dfed86a9f94 100644 --- a/substrate/frame/support/src/weights/extrinsic_weights.rs +++ b/substrate/frame/support/src/weights/extrinsic_weights.rs @@ -15,24 +15,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16 (Y/M/D) -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01 (Y/M/D) +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! //! SHORT-NAME: `extrinsic`, LONG-NAME: `ExtrinsicBase`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` -//! WEIGHT-PATH: `./frame/support/src/weights/` +//! WEIGHT-PATH: `./substrate/frame/support/src/weights/` //! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1.0`, WEIGHT-ADD: `0` // Executed Command: -// ./target/production/substrate +// ./target/production/substrate-node // benchmark // overhead // --chain=dev -// --execution=wasm // --wasm-execution=compiled -// --weight-path=./frame/support/src/weights/ -// --header=./HEADER-APACHE2 +// --weight-path=./substrate/frame/support/src/weights/ +// --header=./substrate/HEADER-APACHE2 // --warmup=10 // --repeat=100 @@ -44,17 +43,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 123_875, 128_419 - /// Average: 124_414 - /// Median: 124_332 - /// Std-Dev: 497.74 + /// Min, Max: 106_053, 107_403 + /// Average: 106_446 + /// Median: 106_415 + /// Std-Dev: 216.17 /// /// Percentiles nanoseconds: - /// 99th: 125_245 - /// 95th: 124_989 - /// 75th: 124_498 + /// 99th: 107_042 + /// 95th: 106_841 + /// 75th: 106_544 pub const ExtrinsicBaseWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(124_414), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(106_446), 0); } #[cfg(test)] diff --git a/substrate/frame/support/test/Cargo.toml b/substrate/frame/support/test/Cargo.toml index 8b61f25f569aa51d3ac221f15d7f3ffc156b6739..ae2c56a531fd428e712a0825153afc7c89185c78 100644 --- a/substrate/frame/support/test/Cargo.toml +++ b/substrate/frame/support/test/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] static_assertions = "1.1.0" -serde = { version = "1.0.195", default-features = false, features = ["derive"] } +serde = { features = ["derive"], workspace = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-metadata = { version = "16.0.0", default-features = false, features = ["current"] } diff --git a/substrate/frame/support/test/pallet/Cargo.toml b/substrate/frame/support/test/pallet/Cargo.toml index 48adbcab5826157ea3edfb4eb931f77b9b9794a1..ca889faef876b197cb15a338e0a65b30536b79ec 100644 --- a/substrate/frame/support/test/pallet/Cargo.toml +++ b/substrate/frame/support/test/pallet/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["derive"] } +serde = { features = ["derive"], workspace = true } frame-support = { path = "../..", default-features = false } frame-system = { path = "../../../system", default-features = false } sp-runtime = { path = "../../../../primitives/runtime", default-features = false } diff --git a/substrate/frame/support/test/tests/composite_enum.rs b/substrate/frame/support/test/tests/composite_enum.rs new file mode 100644 index 0000000000000000000000000000000000000000..b9e9c23c4bca8ffb1e1a93d4800d040e010bbb13 --- /dev/null +++ b/substrate/frame/support/test/tests/composite_enum.rs @@ -0,0 +1,274 @@ +// 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. + +//! General tests for composite_enum macro and its handling, test for: +//! * variant_count works + +#![recursion_limit = "128"] + +use codec::Encode; +use frame_support::{derive_impl, traits::VariantCount}; +use sp_core::sr25519; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, Verify}, +}; + +#[frame_support::pallet(dev_mode)] +mod module_single_instance { + + #[pallet::composite_enum] + pub enum HoldReason { + ModuleSingleInstanceReason1, + ModuleSingleInstanceReason2, + } + + #[pallet::composite_enum] + pub enum FreezeReason { + ModuleSingleInstanceReason1, + ModuleSingleInstanceReason2, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeHoldReason: From; + type RuntimeFreezeReason: From; + } +} + +#[frame_support::pallet(dev_mode)] +mod module_multi_instance { + + #[pallet::composite_enum] + pub enum HoldReason { + ModuleMultiInstanceReason1, + ModuleMultiInstanceReason2, + ModuleMultiInstanceReason3, + } + + #[pallet::composite_enum] + pub enum FreezeReason { + ModuleMultiInstanceReason1, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeHoldReason: From>; + type RuntimeFreezeReason: From>; + } +} + +#[frame_support::pallet(dev_mode)] +mod module_composite_enum_consumer { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + // consume `HoldReason` `composite_enum` + type RuntimeHoldReason: VariantCount; + // consume `FreezeReason` `composite_enum` + type RuntimeFreezeReason: VariantCount; + } +} + +pub type BlockNumber = u64; +pub type Signature = sr25519::Signature; +pub type AccountId = ::Signer; +pub type Header = generic::Header; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type Block = generic::Block; + +frame_support::construct_runtime!( + pub enum Runtime + { + System: frame_system, + ModuleSingleInstance: module_single_instance, + ModuleMultiInstance0: module_multi_instance, + ModuleMultiInstance1: module_multi_instance::, + ModuleMultiInstance2: module_multi_instance::, + ModuleMultiInstance3: module_multi_instance::, + ModuleCompositeEnumConsumer: module_composite_enum_consumer, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; +} + +impl module_single_instance::Config for Runtime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} + +impl module_multi_instance::Config for Runtime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} +impl module_multi_instance::Config for Runtime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} +impl module_multi_instance::Config for Runtime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} +impl module_multi_instance::Config for Runtime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} + +impl module_composite_enum_consumer::Config for Runtime { + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} + +fn list_all_hold_reason_variants() -> Vec { + let variants = vec![ + RuntimeHoldReason::ModuleSingleInstance(module_single_instance::HoldReason::ModuleSingleInstanceReason1), + RuntimeHoldReason::ModuleSingleInstance(module_single_instance::HoldReason::ModuleSingleInstanceReason2), + RuntimeHoldReason::ModuleMultiInstance0(::ModuleMultiInstanceReason1), + RuntimeHoldReason::ModuleMultiInstance0(::ModuleMultiInstanceReason2), + RuntimeHoldReason::ModuleMultiInstance0(::ModuleMultiInstanceReason3), + RuntimeHoldReason::ModuleMultiInstance0(::__Ignore(Default::default())), + RuntimeHoldReason::ModuleMultiInstance1(module_multi_instance::HoldReason::::ModuleMultiInstanceReason1), + RuntimeHoldReason::ModuleMultiInstance1(module_multi_instance::HoldReason::::ModuleMultiInstanceReason2), + RuntimeHoldReason::ModuleMultiInstance1(module_multi_instance::HoldReason::::ModuleMultiInstanceReason3), + RuntimeHoldReason::ModuleMultiInstance1(module_multi_instance::HoldReason::::__Ignore(Default::default())), + RuntimeHoldReason::ModuleMultiInstance2(module_multi_instance::HoldReason::::ModuleMultiInstanceReason1), + RuntimeHoldReason::ModuleMultiInstance2(module_multi_instance::HoldReason::::ModuleMultiInstanceReason2), + RuntimeHoldReason::ModuleMultiInstance2(module_multi_instance::HoldReason::::ModuleMultiInstanceReason3), + RuntimeHoldReason::ModuleMultiInstance2(module_multi_instance::HoldReason::::__Ignore(Default::default())), + RuntimeHoldReason::ModuleMultiInstance3(module_multi_instance::HoldReason::::ModuleMultiInstanceReason1), + RuntimeHoldReason::ModuleMultiInstance3(module_multi_instance::HoldReason::::ModuleMultiInstanceReason2), + RuntimeHoldReason::ModuleMultiInstance3(module_multi_instance::HoldReason::::ModuleMultiInstanceReason3), + RuntimeHoldReason::ModuleMultiInstance3(module_multi_instance::HoldReason::::__Ignore(Default::default())), + ]; + // check that we didn't miss any value + for v in &variants { + match v { + RuntimeHoldReason::ModuleSingleInstance(inner) => match inner { + module_single_instance::HoldReason::ModuleSingleInstanceReason1 + | module_single_instance::HoldReason::ModuleSingleInstanceReason2 => (), + } + RuntimeHoldReason::ModuleMultiInstance0(inner) => match inner { + ::ModuleMultiInstanceReason1 + | ::ModuleMultiInstanceReason2 + | ::ModuleMultiInstanceReason3 + | module_multi_instance::HoldReason::<()>::__Ignore(_) => (), + } + RuntimeHoldReason::ModuleMultiInstance1(inner) => match inner { + module_multi_instance::HoldReason::::ModuleMultiInstanceReason1 + | module_multi_instance::HoldReason::::ModuleMultiInstanceReason2 + | module_multi_instance::HoldReason::::ModuleMultiInstanceReason3 + | module_multi_instance::HoldReason::::__Ignore(_) => (), + } + RuntimeHoldReason::ModuleMultiInstance2(inner) => match inner { + module_multi_instance::HoldReason::::ModuleMultiInstanceReason1 + | module_multi_instance::HoldReason::::ModuleMultiInstanceReason2 + | module_multi_instance::HoldReason::::ModuleMultiInstanceReason3 + | module_multi_instance::HoldReason::::__Ignore(_) => (), + } + RuntimeHoldReason::ModuleMultiInstance3(inner) => match inner { + module_multi_instance::HoldReason::::ModuleMultiInstanceReason1 + | module_multi_instance::HoldReason::::ModuleMultiInstanceReason2 + | module_multi_instance::HoldReason::::ModuleMultiInstanceReason3 + | module_multi_instance::HoldReason::::__Ignore(_) => (), + } + } + } + variants +} + +fn list_all_freeze_reason_variants() -> Vec { + let variants = vec![ + RuntimeFreezeReason::ModuleSingleInstance(module_single_instance::FreezeReason::ModuleSingleInstanceReason1), + RuntimeFreezeReason::ModuleSingleInstance(module_single_instance::FreezeReason::ModuleSingleInstanceReason2), + RuntimeFreezeReason::ModuleMultiInstance0(::ModuleMultiInstanceReason1), + RuntimeFreezeReason::ModuleMultiInstance0(::__Ignore(Default::default())), + RuntimeFreezeReason::ModuleMultiInstance1(module_multi_instance::FreezeReason::::ModuleMultiInstanceReason1), + RuntimeFreezeReason::ModuleMultiInstance1(module_multi_instance::FreezeReason::::__Ignore(Default::default())), + RuntimeFreezeReason::ModuleMultiInstance2(module_multi_instance::FreezeReason::::ModuleMultiInstanceReason1), + RuntimeFreezeReason::ModuleMultiInstance2(module_multi_instance::FreezeReason::::__Ignore(Default::default())), + RuntimeFreezeReason::ModuleMultiInstance3(module_multi_instance::FreezeReason::::ModuleMultiInstanceReason1), + RuntimeFreezeReason::ModuleMultiInstance3(module_multi_instance::FreezeReason::::__Ignore(Default::default())), + ]; + // check that we didn't miss any value + for v in &variants { + match v { + RuntimeFreezeReason::ModuleSingleInstance(inner) => match inner { + module_single_instance::FreezeReason::ModuleSingleInstanceReason1 + | module_single_instance::FreezeReason::ModuleSingleInstanceReason2 => (), + } + RuntimeFreezeReason::ModuleMultiInstance0(inner) => match inner { + ::ModuleMultiInstanceReason1 + | module_multi_instance::FreezeReason::<()>::__Ignore(_) => (), + } + RuntimeFreezeReason::ModuleMultiInstance1(inner) => match inner { + module_multi_instance::FreezeReason::::ModuleMultiInstanceReason1 + | module_multi_instance::FreezeReason::::__Ignore(_) => (), + } + RuntimeFreezeReason::ModuleMultiInstance2(inner) => match inner { + module_multi_instance::FreezeReason::::ModuleMultiInstanceReason1 + | module_multi_instance::FreezeReason::::__Ignore(_) => (), + } + RuntimeFreezeReason::ModuleMultiInstance3(inner) => match inner { + module_multi_instance::FreezeReason::::ModuleMultiInstanceReason1 + | module_multi_instance::FreezeReason::::__Ignore(_) => (), + } + } + } + variants +} + +#[test] +fn runtime_hold_reason_variant_count_works() { + assert_eq!(RuntimeHoldReason::VARIANT_COUNT as usize, list_all_hold_reason_variants().len()); +} + +#[test] +fn runtime_freeze_reason_variant_count_works() { + assert_eq!( + RuntimeFreezeReason::VARIANT_COUNT as usize, + list_all_freeze_reason_variants().len() + ); +} + +#[test] +fn check_unique_encodings_for_hold_reason() { + let variants = list_all_hold_reason_variants(); + let unique_encoded_variants = + variants.iter().map(|v| v.encode()).collect::>(); + assert_eq!(unique_encoded_variants.len(), variants.len()); +} + +#[test] +fn check_unique_encodings_for_freeze_reason() { + let variants = list_all_freeze_reason_variants(); + let unique_encoded_variants = + variants.iter().map(|v| v.encode()).collect::>(); + assert_eq!(unique_encoded_variants.len(), variants.len()); +} diff --git a/substrate/frame/support/test/tests/construct_runtime.rs b/substrate/frame/support/test/tests/construct_runtime.rs index b8341b25cb0985915706ccb68b870474118d33cb..83691451ccdf07833dd276441ea5cedf4066beaf 100644 --- a/substrate/frame/support/test/tests/construct_runtime.rs +++ b/substrate/frame/support/test/tests/construct_runtime.rs @@ -683,7 +683,7 @@ fn test_metadata() { name: "Version", ty: meta_type::(), value: RuntimeVersion::default().encode(), - docs: maybe_docs(vec![ " Get the chain's current version."]), + docs: maybe_docs(vec![ " Get the chain's in-code version."]), }, PalletConstantMetadata { name: "SS58Prefix", @@ -815,7 +815,7 @@ fn test_metadata() { ty: meta_type::(), version: 4, signed_extensions: vec![SignedExtensionMetadata { - identifier: "UnitSignedExtension", + identifier: "UnitTransactionExtension", ty: meta_type::<()>(), additional_signed: meta_type::<()>(), }], diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr index b08efb3a84213e107916dee6c43c62d9ee90e2d4..c677ddcd672bc048b96e0c7274c36ffccf31e5ef 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr @@ -347,7 +347,7 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied 26 | System: frame_system::{Pallet, Call, Storage, Config, Event}, | ^^^^^^ the trait `Config` is not implemented for `Runtime` | -note: required by a bound in `frame_system::GenesisConfig` +note: required by a bound in `GenesisConfig` --> $WORKSPACE/substrate/frame/system/src/lib.rs | | pub struct GenesisConfig { @@ -449,3 +449,69 @@ note: required by a bound in `Result` | pub enum Result { | ^ required by this bound in `Result` = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::codec::Decode` which comes from the expansion of the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Runtime: Config` is not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | // construct_runtime! { +21 | || pub struct Runtime where +22 | || Block = Block, +23 | || NodeBlock = Block, +... || +27 | || } +28 | || } + | ||_- in this macro invocation +... | + | + = note: required for `Pallet` to implement `Callable` +note: required because it appears within the type `RuntimeCall` + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | // construct_runtime! { +21 | || pub struct Runtime where +22 | || Block = Block, +23 | || NodeBlock = Block, +... || +27 | || } +28 | || } + | ||_- in this macro invocation +... | +note: required by a bound in `frame_support::sp_runtime::traits::Dispatchable::Config` + --> $WORKSPACE/substrate/primitives/runtime/src/traits/mod.rs + | + | type Config; + | ^^^^^^^^^^^^ required by this bound in `Dispatchable::Config` + = note: this error originates in the macro `frame_support::construct_runtime` which comes from the expansion of the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Runtime: Config` is not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | // construct_runtime! { +21 | || pub struct Runtime where +22 | || Block = Block, +23 | || NodeBlock = Block, +... || +27 | || } +28 | || } + | ||_- in this macro invocation +... | + | + = note: required for `Pallet` to implement `Callable` +note: required because it appears within the type `RuntimeCall` + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | // construct_runtime! { +21 | || pub struct Runtime where +22 | || Block = Block, +23 | || NodeBlock = Block, +... || +27 | || } +28 | || } + | ||_- in this macro invocation +... | +note: required by a bound in `frame_support::pallet_prelude::ValidateUnsigned::Call` + --> $WORKSPACE/substrate/primitives/runtime/src/traits/mod.rs + | + | type Call; + | ^^^^^^^^^^ required by this bound in `ValidateUnsigned::Call` + = note: this error originates in the macro `frame_support::construct_runtime` which comes from the expansion of the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr index 3b6329c650fa65208e01c7d991e321f256b7eecf..6160f8234a350cc3ee0a0761cab0eed6ea022525 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.stderr @@ -4,88 +4,24 @@ error: The number of pallets exceeds the maximum number of tuple elements. To in 67 | pub struct Runtime | ^^^ -error[E0412]: cannot find type `RuntimeCall` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:35:64 - | -35 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - | ^^^^^^^^^^^ not found in this scope - | -help: you might be missing a type parameter - | -35 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; - | +++++++++++++ - -error[E0412]: cannot find type `Runtime` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:37:25 - | -37 | impl pallet::Config for Runtime {} - | ^^^^^^^ not found in this scope - -error[E0412]: cannot find type `Runtime` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:40:31 - | -40 | impl frame_system::Config for Runtime { - | ^^^^^^^ not found in this scope - -error[E0412]: cannot find type `RuntimeOrigin` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:42:23 - | -42 | type RuntimeOrigin = RuntimeOrigin; - | ^^^^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -42 | type RuntimeOrigin = Self::RuntimeOrigin; - | ++++++ - -error[E0412]: cannot find type `RuntimeCall` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:44:21 - | -44 | type RuntimeCall = RuntimeCall; - | ^^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -44 | type RuntimeCall = Self::RuntimeCall; - | ++++++ - -error[E0412]: cannot find type `RuntimeEvent` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:50:22 - | -50 | type RuntimeEvent = RuntimeEvent; - | ^^^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -50 | type RuntimeEvent = Self::RuntimeEvent; - | ++++++ - -error[E0412]: cannot find type `PalletInfo` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:56:20 - | -56 | type PalletInfo = PalletInfo; - | ^^^^^^^^^^ - | -help: you might have meant to use the associated type - | -56 | type PalletInfo = Self::PalletInfo; - | ++++++ -help: consider importing one of these items - | -18 + use frame_benchmarking::__private::traits::PalletInfo; - | -18 + use frame_support::traits::PalletInfo; - | - -error[E0412]: cannot find type `RuntimeTask` in this scope - --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:39:1 - | -39 | #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `frame_system::config_preludes::TestDefaultConfig` which comes from the expansion of the macro `frame_support::macro_magic::forward_tokens_verbatim` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might have meant to use the associated type - --> $WORKSPACE/substrate/frame/system/src/lib.rs - | - | type Self::RuntimeTask = (); - | ++++++ +error: recursion limit reached while expanding `frame_support::__private::tt_return!` + --> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:22:1 + | +22 | / #[frame_support::pallet] +23 | | mod pallet { +24 | | #[pallet::config] +25 | | pub trait Config: frame_system::Config {} +... | +66 | |/ construct_runtime! { +67 | || pub struct Runtime +68 | || { +69 | || System: frame_system::{Pallet, Call, Storage, Config, Event}, +... || +180 | || } +181 | || } + | ||_^ + | |_| + | in this macro invocation + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`) + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr index a4c7ecf786588a782f96b3ae890c67973c2841fc..30005c07cb631dd48425117507006faa870866cd 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr @@ -53,8 +53,9 @@ error[E0599]: no function or associated item named `is_inherent` found for struc | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `is_inherent`, perhaps you need to implement it: + = note: the following traits define an item `is_inherent`, perhaps you need to implement one of them: candidate #1: `ProvideInherent` + candidate #2: `IsInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: no function or associated item named `check_inherent` found for struct `pallet::Pallet` in the current scope @@ -119,3 +120,23 @@ error[E0599]: no function or associated item named `is_inherent_required` found = note: the following trait defines an item `is_inherent_required`, perhaps you need to implement it: candidate #1: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `pallet::Pallet: ProvideInherent` is not satisfied + --> tests/construct_runtime_ui/undefined_inherent_part.rs:70:3 + | +70 | Pallet: pallet expanded::{}::{Pallet, Inherent}, + | ^^^^^^ the trait `ProvideInherent` is not implemented for `pallet::Pallet` + +error[E0277]: the trait bound `pallet::Pallet: ProvideInherent` is not satisfied + --> tests/construct_runtime_ui/undefined_inherent_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, Inherent}, +71 | | } +72 | | } + | |_^ the trait `ProvideInherent` is not implemented for `pallet::Pallet` + | + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/derive_impl_ui/inject_runtime_type_invalid.stderr b/substrate/frame/support/test/tests/derive_impl_ui/inject_runtime_type_invalid.stderr index cda20288984ae535c0755fd1e6814384d124c515..c7159b34afb3d22737fb5d8ed6662027f04a3bd6 100644 --- a/substrate/frame/support/test/tests/derive_impl_ui/inject_runtime_type_invalid.stderr +++ b/substrate/frame/support/test/tests/derive_impl_ui/inject_runtime_type_invalid.stderr @@ -1,4 +1,4 @@ -error: `#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, `RuntimeTask`, `RuntimeOrigin` or `PalletInfo` +error: `#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, `RuntimeTask`, `RuntimeOrigin`, `RuntimeParameters` or `PalletInfo` --> tests/derive_impl_ui/inject_runtime_type_invalid.rs:32:5 | 32 | type RuntimeInfo = (); diff --git a/substrate/frame/support/test/tests/derive_no_bound.rs b/substrate/frame/support/test/tests/derive_no_bound.rs index af2633dc53cae4e8f9c52d0ee08327f339000bb4..48a6413c3ac508cad684500969f5be1cbeb01135 100644 --- a/substrate/frame/support/test/tests/derive_no_bound.rs +++ b/substrate/frame/support/test/tests/derive_no_bound.rs @@ -15,11 +15,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tests for DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound, and -//! RuntimeDebugNoBound +//! Tests for DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound, +//! RuntimeDebugNoBound, PartialOrdNoBound and OrdNoBound use frame_support::{ - CloneNoBound, DebugNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, + CloneNoBound, DebugNoBound, DefaultNoBound, EqNoBound, OrdNoBound, PartialEqNoBound, + PartialOrdNoBound, RuntimeDebugNoBound, }; #[derive(RuntimeDebugNoBound)] @@ -32,7 +33,7 @@ fn runtime_debug_no_bound_display_correctly() { } trait Config { - type C: std::fmt::Debug + Clone + Eq + PartialEq + Default; + type C: std::fmt::Debug + Clone + Eq + PartialEq + Default + PartialOrd + Ord; } struct Runtime; @@ -42,7 +43,15 @@ impl Config for Runtime { type C = u32; } -#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)] +#[derive( + DebugNoBound, + CloneNoBound, + EqNoBound, + PartialEqNoBound, + DefaultNoBound, + PartialOrdNoBound, + OrdNoBound, +)] struct StructNamed { a: u32, b: u64, @@ -96,9 +105,18 @@ fn test_struct_named() { }; assert!(b != a_1); + assert!(b > a_1); } -#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)] +#[derive( + DebugNoBound, + CloneNoBound, + EqNoBound, + PartialEqNoBound, + DefaultNoBound, + PartialOrdNoBound, + OrdNoBound, +)] struct StructUnnamed(u32, u64, T::C, core::marker::PhantomData<(U, V)>); #[rustversion::attr(not(stable), ignore)] @@ -128,9 +146,18 @@ fn test_struct_unnamed() { let b = StructUnnamed::(1, 2, 4, Default::default()); assert!(b != a_1); + assert!(b > a_1); } -#[derive(DebugNoBound, CloneNoBound, EqNoBound, PartialEqNoBound, DefaultNoBound)] +#[derive( + DebugNoBound, + CloneNoBound, + EqNoBound, + PartialEqNoBound, + DefaultNoBound, + PartialOrdNoBound, + OrdNoBound, +)] struct StructNoGenerics { field1: u32, field2: u64, @@ -254,3 +281,63 @@ fn test_enum() { assert!(variant_2.clone() == variant_2); assert!(variant_3.clone() == variant_3); } + +// Return all the combinations of (0, 0, 0), (0, 0, 1), (0, 1, 0), ... +// Used to test all the possible struct orderings +fn combinations() -> Vec<(u32, u64, u32)> { + let mut v = vec![]; + + for a in 0..=1 { + for b in 0..=1 { + for c in 0..=1 { + v.push((a, b, c)); + } + } + } + + v +} + +// Ensure that the PartialOrdNoBound follows the same rules as the native PartialOrd +#[derive(Debug, Clone, Eq, PartialEq, Default, PartialOrd, Ord)] +struct StructNamedRust { + a: u32, + b: u64, + c: u32, + phantom: core::marker::PhantomData<(ImplNone, ImplNone)>, +} + +#[derive(Debug, Clone, Eq, PartialEq, Default, PartialOrd, Ord)] +struct StructUnnamedRust(u32, u64, u32, core::marker::PhantomData<(ImplNone, ImplNone)>); + +#[test] +fn struct_named_same_as_native_rust() { + for (a, b, c) in combinations() { + let a_1 = + StructNamed:: { a, b, c, phantom: Default::default() }; + let b_1 = StructNamedRust { a, b, c, phantom: Default::default() }; + for (a, b, c) in combinations() { + let a_2 = + StructNamed:: { a, b, c, phantom: Default::default() }; + let b_2 = StructNamedRust { a, b, c, phantom: Default::default() }; + assert_eq!(a_1.partial_cmp(&a_2), b_1.partial_cmp(&b_2)); + assert_eq!(a_1.cmp(&a_2), b_1.cmp(&b_2)); + assert_eq!(a_1.eq(&a_2), b_1.eq(&b_2)); + } + } +} + +#[test] +fn struct_unnamed_same_as_native_rust() { + for (a, b, c) in combinations() { + let a_1 = StructUnnamed::(a, b, c, Default::default()); + let b_1 = StructUnnamedRust(a, b, c, Default::default()); + for (a, b, c) in combinations() { + let a_2 = StructUnnamed::(a, b, c, Default::default()); + let b_2 = StructUnnamedRust(a, b, c, Default::default()); + assert_eq!(a_1.partial_cmp(&a_2), b_1.partial_cmp(&b_2)); + assert_eq!(a_1.cmp(&a_2), b_1.cmp(&b_2)); + assert_eq!(a_1.eq(&a_2), b_1.eq(&b_2)); + } + } +} diff --git a/substrate/frame/support/test/tests/derive_no_bound_ui/ord.rs b/substrate/frame/support/test/tests/derive_no_bound_ui/ord.rs new file mode 100644 index 0000000000000000000000000000000000000000..308b22ed02222a834b462ae22ff696da4b5d45dd --- /dev/null +++ b/substrate/frame/support/test/tests/derive_no_bound_ui/ord.rs @@ -0,0 +1,27 @@ +// 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. + +trait Config { + type C; +} + +#[derive(frame_support::OrdNoBound, frame_support::EqNoBound)] +struct Foo { + c: T::C, +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/derive_no_bound_ui/ord.stderr b/substrate/frame/support/test/tests/derive_no_bound_ui/ord.stderr new file mode 100644 index 0000000000000000000000000000000000000000..db8a507960770dcfeb8d5768ebf5b9ed6cc3da12 --- /dev/null +++ b/substrate/frame/support/test/tests/derive_no_bound_ui/ord.stderr @@ -0,0 +1,25 @@ +error[E0277]: can't compare `Foo` with `Foo` + --> tests/derive_no_bound_ui/ord.rs:23:8 + | +23 | struct Foo { + | ^^^^^^^^^^^^^^ no implementation for `Foo < Foo` and `Foo > Foo` + | + = help: the trait `PartialOrd` is not implemented for `Foo` +note: required by a bound in `Ord` + --> $RUST/core/src/cmp.rs + | + | pub trait Ord: Eq + PartialOrd { + | ^^^^^^^^^^^^^^^^ required by this bound in `Ord` + +error[E0277]: can't compare `Foo` with `Foo` + --> tests/derive_no_bound_ui/ord.rs:23:8 + | +23 | struct Foo { + | ^^^^^^^^^^^^^^ no implementation for `Foo == Foo` + | + = help: the trait `PartialEq` is not implemented for `Foo` +note: required by a bound in `std::cmp::Eq` + --> $RUST/core/src/cmp.rs + | + | pub trait Eq: PartialEq { + | ^^^^^^^^^^^^^^^ required by this bound in `Eq` diff --git a/substrate/frame/support/test/tests/derive_no_bound_ui/partial_ord.rs b/substrate/frame/support/test/tests/derive_no_bound_ui/partial_ord.rs new file mode 100644 index 0000000000000000000000000000000000000000..c958f223c4d84c6e2f1896a880322fa76fb42662 --- /dev/null +++ b/substrate/frame/support/test/tests/derive_no_bound_ui/partial_ord.rs @@ -0,0 +1,27 @@ +// 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. + +trait Config { + type C; +} + +#[derive(frame_support::PartialOrdNoBound, frame_support::PartialEqNoBound)] +struct Foo { + c: T::C, +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/derive_no_bound_ui/partial_ord.stderr b/substrate/frame/support/test/tests/derive_no_bound_ui/partial_ord.stderr new file mode 100644 index 0000000000000000000000000000000000000000..33cdd790e17d19b8c30634aeb1bde54cbca52d39 --- /dev/null +++ b/substrate/frame/support/test/tests/derive_no_bound_ui/partial_ord.stderr @@ -0,0 +1,15 @@ +error[E0599]: `::C` is not an iterator + --> tests/derive_no_bound_ui/partial_ord.rs:24:2 + | +24 | c: T::C, + | ^ `::C` is not an iterator + | + = note: the following trait bounds were not satisfied: + `::C: Iterator` + which is required by `&mut ::C: Iterator` + +error[E0369]: binary operation `==` cannot be applied to type `::C` + --> tests/derive_no_bound_ui/partial_ord.rs:24:2 + | +24 | c: T::C, + | ^ diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index 0223979d7f0e2420d67871ec799272ad017f25f2..4d2737d411ee8238124571d5c64260ac3dd79c03 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -28,6 +28,7 @@ use frame_support::{ UnfilteredDispatchable, }, weights::{RuntimeDbWeight, Weight}, + OrdNoBound, PartialOrdNoBound, }; use scale_info::{meta_type, TypeInfo}; use sp_io::{ @@ -208,7 +209,7 @@ pub mod pallet { where T::AccountId: From + From + SomeAssociation1, { - /// Doc comment put in metadata + /// call foo doc comment put in metadata #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(*foo as u64, 0))] pub fn foo( @@ -224,7 +225,7 @@ pub mod pallet { Ok(().into()) } - /// Doc comment put in metadata + /// call foo_storage_layer doc comment put in metadata #[pallet::call_index(1)] #[pallet::weight({1})] pub fn foo_storage_layer( @@ -269,7 +270,7 @@ pub mod pallet { #[pallet::error] #[derive(PartialEq, Eq)] pub enum Error { - /// doc comment put into metadata + /// error doc comment put in metadata InsufficientProposersBalance, NonExistentStorageValue, Code(u8), @@ -286,9 +287,8 @@ pub mod pallet { where T::AccountId: SomeAssociation1 + From, { - /// doc comment put in metadata + /// event doc comment put in metadata Proposed(::AccountId), - /// doc Spending(BalanceOf), Something(u32), SomethingElse(::_1), @@ -467,6 +467,8 @@ pub mod pallet { RuntimeDebugNoBound, CloneNoBound, PartialEqNoBound, + PartialOrdNoBound, + OrdNoBound, Encode, Decode, TypeInfo, @@ -587,7 +589,7 @@ pub mod pallet2 { Self::deposit_event(Event::Something(31)); if UpdateStorageVersion::get() { - Self::current_storage_version().put::(); + Self::in_code_storage_version().put::(); } Weight::zero() @@ -741,14 +743,43 @@ impl pallet5::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; } +#[derive(Clone, Debug, codec::Encode, codec::Decode, PartialEq, Eq, scale_info::TypeInfo)] +pub struct AccountU64(u64); +impl sp_runtime::traits::IdentifyAccount for AccountU64 { + type AccountId = u64; + fn into_account(self) -> u64 { + self.0 + } +} + +impl sp_runtime::traits::Verify for AccountU64 { + type Signer = AccountU64; + fn verify>( + &self, + _msg: L, + _signer: &::AccountId, + ) -> bool { + true + } +} + +impl From for AccountU64 { + fn from(value: u64) -> Self { + Self(value) + } +} + pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = - sp_runtime::testing::TestXt>; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic< + u64, + RuntimeCall, + AccountU64, + frame_system::CheckNonZeroSender, +>; frame_support::construct_runtime!( - pub struct 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, @@ -769,6 +800,14 @@ fn _ensure_call_is_correctly_excluded_and_included(call: RuntimeCall) { } } +fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { + if cfg!(feature = "no-metadata-docs") { + vec![] + } else { + doc + } +} + #[test] fn transactional_works() { TestExternalities::default().execute_with(|| { @@ -887,10 +926,8 @@ fn inherent_expand() { let inherents = InherentData::new().create_extrinsics(); - let expected = vec![UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }]; + let expected = + vec![UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {}))]; assert_eq!(expected, inherents); let block = Block::new( @@ -902,14 +939,11 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 0, + })), ], ); @@ -924,14 +958,11 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 0, bar: 0 }), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 0, + bar: 0, + })), ], ); @@ -945,10 +976,9 @@ fn inherent_expand() { BlakeTwo256::hash(b"test"), Digest::default(), ), - vec![UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), - signature: None, - }], + vec![UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_storage_layer { + foo: 0, + }))], ); let mut inherent = InherentData::new(); @@ -963,10 +993,12 @@ fn inherent_expand() { BlakeTwo256::hash(b"test"), Digest::default(), ), - vec![UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: Some((1, Default::default())), - }], + vec![UncheckedExtrinsic::new_signed( + RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + 1, + 1.into(), + Default::default(), + )], ); let mut inherent = InherentData::new(); @@ -982,14 +1014,13 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 1, + })), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_storage_layer { + foo: 0, + })), ], ); @@ -1004,18 +1035,14 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 1, + })), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_storage_layer { + foo: 0, + })), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), ], ); @@ -1030,18 +1057,17 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), - signature: Some((1, Default::default())), - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 1, + })), + UncheckedExtrinsic::new_signed( + RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), + 1, + 1.into(), + Default::default(), + ), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), ], ); @@ -1307,7 +1333,7 @@ fn pallet_on_genesis() { assert_eq!(pallet::Pallet::::on_chain_storage_version(), StorageVersion::new(0)); pallet::Pallet::::on_genesis(); assert_eq!( - pallet::Pallet::::current_storage_version(), + pallet::Pallet::::in_code_storage_version(), pallet::Pallet::::on_chain_storage_version(), ); }) @@ -1359,19 +1385,47 @@ fn migrate_from_pallet_version_to_storage_version() { }); } +#[test] +fn pallet_item_docs_in_metadata() { + // call + let call_variants = match meta_type::>().type_info().type_def { + scale_info::TypeDef::Variant(variants) => variants.variants, + _ => unreachable!(), + }; + + assert_eq!(call_variants[0].docs, maybe_docs(vec!["call foo doc comment put in metadata"])); + assert_eq!( + call_variants[1].docs, + maybe_docs(vec!["call foo_storage_layer doc comment put in metadata"]) + ); + assert!(call_variants[2].docs.is_empty()); + + // event + let event_variants = match meta_type::>().type_info().type_def { + scale_info::TypeDef::Variant(variants) => variants.variants, + _ => unreachable!(), + }; + + assert_eq!(event_variants[0].docs, maybe_docs(vec!["event doc comment put in metadata"])); + assert!(event_variants[1].docs.is_empty()); + + // error + let error_variants = match meta_type::>().type_info().type_def { + scale_info::TypeDef::Variant(variants) => variants.variants, + _ => unreachable!(), + }; + + assert_eq!(error_variants[0].docs, maybe_docs(vec!["error doc comment put in metadata"])); + assert!(error_variants[1].docs.is_empty()); + + // storage is already covered in the main `fn metadata` test. +} + #[test] fn metadata() { use codec::Decode; use frame_metadata::{v15::*, *}; - fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { - if cfg!(feature = "no-metadata-docs") { - vec![] - } else { - doc - } - } - let readme = "Support code for the runtime.\n\nLicense: Apache-2.0\n"; let expected_pallet_doc = vec![" Pallet documentation", readme, readme]; @@ -1896,7 +1950,7 @@ fn extrinsic_metadata_ir_types() { >(), ir.signature_ty ); - assert_eq!(meta_type::<()>(), ir.signature_ty); + assert_eq!(meta_type::(), ir.signature_ty); assert_eq!(meta_type::<<::SignaturePayload as SignaturePayloadT>::SignatureExtra>(), ir.extra_ty); assert_eq!(meta_type::>(), ir.extra_ty); @@ -2254,10 +2308,10 @@ fn pallet_on_chain_storage_version_initializes_correctly() { AllPalletsWithSystem, >; - // Simple example of a pallet with current version 10 being added to the runtime for the first + // Simple example of a pallet with in-code version 10 being added to the runtime for the first // time. TestExternalities::default().execute_with(|| { - let current_version = Example::current_storage_version(); + let in_code_version = Example::in_code_storage_version(); // Check the pallet has no storage items set. let pallet_hashed_prefix = twox_128(Example::name().as_bytes()); @@ -2268,14 +2322,14 @@ fn pallet_on_chain_storage_version_initializes_correctly() { // version. Executive::execute_on_runtime_upgrade(); - // Check that the storage version was initialized to the current version + // Check that the storage version was initialized to the in-code version let on_chain_version_after = StorageVersion::get::(); - assert_eq!(on_chain_version_after, current_version); + assert_eq!(on_chain_version_after, in_code_version); }); - // Pallet with no current storage version should have the on-chain version initialized to 0. + // Pallet with no in-code storage version should have the on-chain version initialized to 0. TestExternalities::default().execute_with(|| { - // Example4 current_storage_version is NoStorageVersionSet. + // Example4 in_code_storage_version is NoStorageVersionSet. // Check the pallet has no storage items set. let pallet_hashed_prefix = twox_128(Example4::name().as_bytes()); @@ -2305,7 +2359,7 @@ fn post_runtime_upgrade_detects_storage_version_issues() { impl OnRuntimeUpgrade for CustomUpgrade { fn on_runtime_upgrade() -> Weight { - Example2::current_storage_version().put::(); + Example2::in_code_storage_version().put::(); Default::default() } @@ -2348,14 +2402,14 @@ fn post_runtime_upgrade_detects_storage_version_issues() { >; TestExternalities::default().execute_with(|| { - // Set the on-chain version to one less than the current version for `Example`, simulating a + // Set the on-chain version to one less than the in-code version for `Example`, simulating a // forgotten migration StorageVersion::new(9).put::(); // The version isn't changed, we should detect it. assert!( Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap_err() == - "On chain and current storage version do not match. Missing runtime upgrade?" + "On chain and in-code storage version do not match. Missing runtime upgrade?" .into() ); }); diff --git a/substrate/frame/support/test/tests/pallet_instance.rs b/substrate/frame/support/test/tests/pallet_instance.rs index e9ac03302b21435ff7305cbcb25be3fcf0d15496..505400a7c217df1321c2a5f5a3746b6b4bc1d671 100644 --- a/substrate/frame/support/test/tests/pallet_instance.rs +++ b/substrate/frame/support/test/tests/pallet_instance.rs @@ -26,6 +26,7 @@ use frame_support::{ UnfilteredDispatchable, }, weights::Weight, + OrdNoBound, PartialOrdNoBound, }; use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, @@ -208,6 +209,8 @@ pub mod pallet { RuntimeDebugNoBound, CloneNoBound, PartialEqNoBound, + PartialOrdNoBound, + OrdNoBound, Encode, Decode, scale_info::TypeInfo, @@ -940,7 +943,7 @@ fn metadata() { ty: scale_info::meta_type::(), version: 4, signed_extensions: vec![SignedExtensionMetadata { - identifier: "UnitSignedExtension", + identifier: "UnitTransactionExtension", ty: scale_info::meta_type::<()>(), additional_signed: scale_info::meta_type::<()>(), }], 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 79e9d6786717a5a27717cda754575c2ecd478aa3..33b96dea9486396629e19274de8437f0ca3c3162 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs @@ -90,6 +90,7 @@ fn module_error_outer_enum_expand_explicit() { frame_system::Error::NonDefaultComposite => (), frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), + frame_system::Error::MultiBlockMigrationsOngoing => (), #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), #[cfg(feature = "experimental")] 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 4bd8ee0bb39a574b2ff3f59b571c1209fc675da4..db006fe79359aa9f096963ce9333933329c23508 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs @@ -90,6 +90,7 @@ fn module_error_outer_enum_expand_implicit() { frame_system::Error::NonDefaultComposite => (), frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), + frame_system::Error::MultiBlockMigrationsOngoing => (), #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), #[cfg(feature = "experimental")] diff --git a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs index 840a6dee20ccc7a836619a1ef334cca6e22e7887..d2ca9fc80991a87dbb6f20323f07d8adcd29449d 100644 --- a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs +++ b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.rs @@ -29,7 +29,7 @@ mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_runtime_upgrade() -> Weight { - if Self::current_storage_version() != Self::on_chain_storage_version() { + if Self::in_code_storage_version() != Self::on_chain_storage_version() { } diff --git a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr index 1b48197cc9ed1049ea0f52bb011fbc95654c59c0..3256e69528a2d28527ccfe1fa4d77dadfc77baa1 100644 --- a/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/compare_unset_storage_version.stderr @@ -1,7 +1,7 @@ error[E0369]: binary operation `!=` cannot be applied to type `NoStorageVersionSet` --> tests/pallet_ui/compare_unset_storage_version.rs:32:39 | -32 | if Self::current_storage_version() != Self::on_chain_storage_version() { +32 | if Self::in_code_storage_version() != Self::on_chain_storage_version() { | ------------------------------- ^^ -------------------------------- StorageVersion | | | NoStorageVersionSet diff --git a/substrate/frame/support/test/tests/pallet_ui/default_config_with_no_default_in_system.stderr b/substrate/frame/support/test/tests/pallet_ui/default_config_with_no_default_in_system.stderr index a86fd96ea28dcd1e9926dde8e64fe645ca3748e0..5a5b480973693005a3b103f6bb9d467e26079744 100644 --- a/substrate/frame/support/test/tests/pallet_ui/default_config_with_no_default_in_system.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/default_config_with_no_default_in_system.stderr @@ -2,4 +2,4 @@ error[E0220]: associated type `Block` not found for `Self` --> tests/pallet_ui/default_config_with_no_default_in_system.rs:25:31 | 25 | type MyGetParam2: Get; - | ^^^^^ there is a similarly named associated type `Block` in the trait `frame_system::Config` + | ^^^^^ there is an associated type `Block` in the trait `frame_system::Config` diff --git a/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.rs b/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.rs index 2e92f5358eabc368fe01b517b3c90274869a079e..3c6730471cb51aa2c6acc3fbb6b2c6035ce790cc 100644 --- a/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.rs +++ b/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.rs @@ -16,6 +16,7 @@ // limitations under the License. #[frame_support::pallet] +#[allow(unused_imports)] mod pallet { #[pallet::config] pub trait Config: frame_system::Config {} @@ -32,5 +33,4 @@ mod pallet { #[derive(scale_info::TypeInfo, codec::Encode, codec::Decode)] enum MyError {} -fn main() { -} +fn main() {} diff --git a/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.stderr b/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.stderr index 9cefd2f4899a159c2b0e2962fb4fb542d000672c..44d8d3fcadbfdfe0b5e66d4a71796e8a7b40f7f8 100644 --- a/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/error_does_not_derive_pallet_error.stderr @@ -1,7 +1,7 @@ error[E0277]: the trait bound `MyError: PalletError` is not satisfied - --> tests/pallet_ui/error_does_not_derive_pallet_error.rs:28:15 + --> tests/pallet_ui/error_does_not_derive_pallet_error.rs:29:15 | -28 | CustomError(crate::MyError), +29 | CustomError(crate::MyError), | ^^^^^^^^^^^^^^ the trait `PalletError` is not implemented for `MyError` | = help: the following other types implement trait `PalletError`: diff --git a/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.rs b/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.rs index a7e6d213318fb5e0fc00d5062f7561db9c0cf4fc..33603d6f559a8770e7719e507bcbd5453c6c9472 100644 --- a/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.rs +++ b/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.rs @@ -16,6 +16,7 @@ // limitations under the License. #[frame_support::pallet] +#[allow(unused_imports)] mod pallet { use frame_support::pallet_prelude::{Hooks, IsType}; use frame_system::pallet_prelude::BlockNumberFor; @@ -41,5 +42,4 @@ mod pallet { } } -fn main() { -} +fn main() {} diff --git a/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.stderr b/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.stderr index fc4a33b721500a09ef03f36836f7ebea832a2547..44660d269060386ab10d749e4a16cd4e65b97950 100644 --- a/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/event_field_not_member.stderr @@ -1,19 +1,19 @@ error[E0277]: the trait bound `::Bar: Clone` is not satisfied - --> tests/pallet_ui/event_field_not_member.rs:40:7 + --> tests/pallet_ui/event_field_not_member.rs:41:7 | -40 | B { b: T::Bar }, +41 | B { b: T::Bar }, | ^ the trait `Clone` is not implemented for `::Bar` error[E0369]: binary operation `==` cannot be applied to type `&::Bar` - --> tests/pallet_ui/event_field_not_member.rs:40:7 + --> tests/pallet_ui/event_field_not_member.rs:41:7 | -40 | B { b: T::Bar }, +41 | B { b: T::Bar }, | ^ error[E0277]: `::Bar` doesn't implement `std::fmt::Debug` - --> tests/pallet_ui/event_field_not_member.rs:40:7 + --> tests/pallet_ui/event_field_not_member.rs:41:7 | -40 | B { b: T::Bar }, +41 | B { b: T::Bar }, | ^ `::Bar` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` | = help: the trait `std::fmt::Debug` is not implemented for `::Bar` diff --git a/substrate/frame/support/test/tests/pallet_ui/pass/error_nested_types.rs b/substrate/frame/support/test/tests/pallet_ui/pass/error_nested_types.rs index 2e0df1ac398af57a3c1b44fe28175d36ab4d496e..0e0dc5890519fa65cbbc1b7c0d845646a2f6bb9e 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pass/error_nested_types.rs +++ b/substrate/frame/support/test/tests/pallet_ui/pass/error_nested_types.rs @@ -19,6 +19,7 @@ use codec::{Decode, Encode}; use frame_support::PalletError; #[frame_support::pallet] +#[allow(unused_imports)] mod pallet { #[pallet::config] pub trait Config: frame_system::Config {} @@ -34,25 +35,24 @@ mod pallet { #[derive(Encode, Decode, PalletError, scale_info::TypeInfo)] pub enum MyError { - Foo, - Bar, - Baz(NestedError), - Struct(MyStruct), - Wrapper(Wrapper), + Foo, + Bar, + Baz(NestedError), + Struct(MyStruct), + Wrapper(Wrapper), } #[derive(Encode, Decode, PalletError, scale_info::TypeInfo)] pub enum NestedError { - Quux + Quux, } #[derive(Encode, Decode, PalletError, scale_info::TypeInfo)] pub struct MyStruct { - field: u8, + field: u8, } #[derive(Encode, Decode, PalletError, scale_info::TypeInfo)] pub struct Wrapper(bool); -fn main() { -} +fn main() {} diff --git a/substrate/frame/support/test/tests/pallet_ui/pass/event_type_bound_system_config_assoc_type.rs b/substrate/frame/support/test/tests/pallet_ui/pass/event_type_bound_system_config_assoc_type.rs index 0a70c4a5e68a8f67fa17f09fda2d11eb6e557efc..d84d320b0b24a0d4fc7f6725678db6d1f578bdee 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pass/event_type_bound_system_config_assoc_type.rs +++ b/substrate/frame/support/test/tests/pallet_ui/pass/event_type_bound_system_config_assoc_type.rs @@ -16,6 +16,7 @@ // limitations under the License. #[frame_support::pallet] +#[allow(unused_imports)] mod pallet { use frame_support::pallet_prelude::{Hooks, IsType}; use frame_system::pallet_prelude::BlockNumberFor; diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr index 7f125526edf26abddf82f2228da9a510200f0735..519fadaa6049cb26430719379d3f9847b81efb62 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_invalid_attribute.stderr @@ -1,4 +1,4 @@ -error: expected one of: `getter`, `storage_prefix`, `unbounded`, `whitelist_storage` +error: expected one of: `getter`, `storage_prefix`, `unbounded`, `whitelist_storage`, `disable_try_decode_storage` --> tests/pallet_ui/storage_invalid_attribute.rs:33:12 | 33 | #[pallet::generate_store(pub trait Store)] diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.rs b/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.rs index 4171596ddde7b5c7c4d77a58a427ec3ec2cad516..60f8b9a0cad8b4bb94554a187b5bc2377863b16c 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.rs +++ b/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.rs @@ -16,6 +16,7 @@ // limitations under the License. #[frame_support::pallet] +#[allow(unused_imports)] mod pallet { use frame_support::pallet_prelude::*; @@ -34,5 +35,4 @@ mod pallet { type Foo = StorageValue<_, u8, ResultQuery>; } -fn main() { -} +fn main() {} diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr index 7ca7ec8bbb7d7269fd22d8e6594fc40ee9b2659f..af8ff50bf421799c64ecc2cc730d7ba88583fb9f 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_result_query_missing_generics.stderr @@ -1,15 +1,15 @@ error[E0107]: missing generics for enum `pallet::Error` - --> tests/pallet_ui/storage_result_query_missing_generics.rs:34:56 + --> tests/pallet_ui/storage_result_query_missing_generics.rs:35:56 | -34 | type Foo = StorageValue<_, u8, ResultQuery>; +35 | type Foo = StorageValue<_, u8, ResultQuery>; | ^^^^^ expected 1 generic argument | note: enum defined here, with 1 generic parameter: `T` - --> tests/pallet_ui/storage_result_query_missing_generics.rs:29:11 + --> tests/pallet_ui/storage_result_query_missing_generics.rs:30:11 | -29 | pub enum Error { +30 | pub enum Error { | ^^^^^ - help: add missing generic argument | -34 | type Foo = StorageValue<_, u8, ResultQuery::NonExistentValue>>; +35 | type Foo = StorageValue<_, u8, ResultQuery::NonExistentValue>>; | +++ diff --git a/substrate/frame/support/test/tests/runtime_metadata.rs b/substrate/frame/support/test/tests/runtime_metadata.rs index bb7f7d2822e7cf1a15c41de1ca47686dd8440bfc..40e70b219ba94062af2bb75391628c73bc3a3233 100644 --- a/substrate/frame/support/test/tests/runtime_metadata.rs +++ b/substrate/frame/support/test/tests/runtime_metadata.rs @@ -101,7 +101,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } @@ -200,8 +200,8 @@ fn runtime_metadata() { name: "header", ty: meta_type::<&::Header>(), }], - output: meta_type::<()>(), - docs: maybe_docs(vec![" Initialize a block with the given header."]), + output: meta_type::(), + docs: maybe_docs(vec![" Initialize a block with the given header and return the runtime executive mode."]), }, ], docs: maybe_docs(vec![ diff --git a/substrate/frame/system/Cargo.toml b/substrate/frame/system/Cargo.toml index b0bab4ec756ae662c8617b4467f5b5e075bf79bc..416969e9c4776aa161dde65281b7847156bdbf56 100644 --- a/substrate/frame/system/Cargo.toml +++ b/substrate/frame/system/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-system" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,9 +18,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] cfg-if = "1.0" codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"] } +serde = { features = ["alloc", "derive"], workspace = true } frame-support = { path = "../support", default-features = false } sp-core = { path = "../../primitives/core", default-features = false, features = ["serde"] } sp-io = { path = "../../primitives/io", default-features = false } @@ -28,7 +28,7 @@ sp-runtime = { path = "../../primitives/runtime", default-features = false, feat sp-std = { path = "../../primitives/std", default-features = false } sp-version = { path = "../../primitives/version", default-features = false, features = ["serde"] } sp-weights = { path = "../../primitives/weights", default-features = false, features = ["serde"] } -docify = "0.2.6" +docify = "0.2.7" [dev-dependencies] criterion = "0.4.0" diff --git a/substrate/frame/system/benchmarking/Cargo.toml b/substrate/frame/system/benchmarking/Cargo.toml index 8b9873f44b861d23fc367f6e610f0a8369292f82..80fdff756c02b25f8ddbf24bd75330458159d199 100644 --- a/substrate/frame/system/benchmarking/Cargo.toml +++ b/substrate/frame/system/benchmarking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-system-benchmarking" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/system/benchmarking/src/extensions.rs b/substrate/frame/system/benchmarking/src/extensions.rs new file mode 100644 index 0000000000000000000000000000000000000000..bfc3b4343a6ca162cbbe65b79856e4b8a5a6f4f7 --- /dev/null +++ b/substrate/frame/system/benchmarking/src/extensions.rs @@ -0,0 +1,213 @@ +// 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 System Extensions + +#![cfg(feature = "runtime-benchmarks")] + +use frame_benchmarking::{account, v2::*, BenchmarkError}; +use frame_support::{ + dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo}, + weights::Weight, +}; +use frame_system::{ + pallet_prelude::*, CheckGenesis, CheckMortality, CheckNonZeroSender, CheckNonce, + CheckSpecVersion, CheckTxVersion, CheckWeight, Config, Pallet as System, RawOrigin, +}; +use sp_runtime::{ + generic::Era, + traits::{AsSystemOriginSigner, DispatchTransaction, Dispatchable, Get}, +}; +use sp_std::prelude::*; + +pub struct Pallet(System); + +#[benchmarks(where + T: Send + Sync, + T::RuntimeCall: Dispatchable, + ::RuntimeOrigin: AsSystemOriginSigner + Clone) +] +mod benchmarks { + use super::*; + + #[benchmark] + fn check_genesis() -> Result<(), BenchmarkError> { + let len = 0_usize; + let caller = account("caller", 0, 0); + let info = DispatchInfo { weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + CheckGenesis::::new() + .test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + + Ok(()) + } + + #[benchmark] + fn check_mortality() -> Result<(), BenchmarkError> { + let len = 0_usize; + let ext = CheckMortality::::from(Era::mortal(16, 256)); + let block_number: BlockNumberFor = 17u32.into(); + System::::set_block_number(block_number); + let prev_block: BlockNumberFor = 16u32.into(); + let default_hash: T::Hash = Default::default(); + frame_system::BlockHash::::insert(prev_block, default_hash); + let caller = account("caller", 0, 0); + let info = DispatchInfo { + weight: Weight::from_parts(100, 0), + class: DispatchClass::Normal, + ..Default::default() + }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_non_zero_sender() -> Result<(), BenchmarkError> { + let len = 0_usize; + let ext = CheckNonZeroSender::::new(); + let caller = account("caller", 0, 0); + let info = DispatchInfo { weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_nonce() -> Result<(), BenchmarkError> { + let caller: T::AccountId = account("caller", 0, 0); + let mut info = frame_system::AccountInfo::default(); + info.nonce = 1u32.into(); + info.providers = 1; + let expected_nonce = info.nonce + 1u32.into(); + frame_system::Account::::insert(caller.clone(), info); + let len = 0_usize; + let ext = CheckNonce::::from(1u32.into()); + let info = DispatchInfo { weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, len, |_| { + Ok(().into()) + }) + .unwrap() + .unwrap(); + } + + let updated_info = frame_system::Account::::get(caller.clone()); + assert_eq!(updated_info.nonce, expected_nonce); + Ok(()) + } + + #[benchmark] + fn check_spec_version() -> Result<(), BenchmarkError> { + let len = 0_usize; + let caller = account("caller", 0, 0); + let info = DispatchInfo { weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + CheckSpecVersion::::new() + .test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_tx_version() -> Result<(), BenchmarkError> { + let len = 0_usize; + let caller = account("caller", 0, 0); + let info = DispatchInfo { weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + CheckTxVersion::::new() + .test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_weight() -> Result<(), BenchmarkError> { + let caller = account("caller", 0, 0); + let base_extrinsic = ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let info = DispatchInfo { + weight: Weight::from_parts(base_extrinsic.ref_time() * 5, 0), + class: DispatchClass::Normal, + ..Default::default() + }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(base_extrinsic.ref_time() * 2, 0)), + pays_fee: Default::default(), + }; + let len = 0_usize; + let base_extrinsic = ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + + let ext = CheckWeight::::new(); + + let initial_block_weight = Weight::from_parts(base_extrinsic.ref_time() * 2, 0); + frame_system::BlockWeight::::mutate(|current_weight| { + current_weight.set(Weight::zero(), DispatchClass::Mandatory); + current_weight.set(initial_block_weight, DispatchClass::Normal); + }); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(post_info)) + .unwrap() + .unwrap(); + } + + assert_eq!( + System::::block_weight().total(), + initial_block_weight + base_extrinsic + post_info.actual_weight.unwrap(), + ); + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test,); +} diff --git a/substrate/frame/system/benchmarking/src/lib.rs b/substrate/frame/system/benchmarking/src/lib.rs index 18bfb85f52dfd4c2b6a5c8a57e78494d97fbdeeb..c9f0869d3bc8945e9af1be6bf38bd5eb8447e4ea 100644 --- a/substrate/frame/system/benchmarking/src/lib.rs +++ b/substrate/frame/system/benchmarking/src/lib.rs @@ -21,13 +21,14 @@ #![cfg(feature = "runtime-benchmarks")] use codec::Encode; -use frame_benchmarking::{impl_benchmark_test_suite, v2::*}; +use frame_benchmarking::v2::*; use frame_support::{dispatch::DispatchClass, storage, traits::Get}; use frame_system::{Call, Pallet as System, RawOrigin}; use sp_core::storage::well_known_keys; use sp_runtime::traits::Hash; use sp_std::{prelude::*, vec}; +pub mod extensions; mod mock; pub struct Pallet(System); diff --git a/substrate/frame/system/benchmarking/src/mock.rs b/substrate/frame/system/benchmarking/src/mock.rs index edbf74a9a51ac9bd90ee65f208c7d96be08430e3..1f84dd530040619f4b902242edf29002f383c073 100644 --- a/substrate/frame/system/benchmarking/src/mock.rs +++ b/substrate/frame/system/benchmarking/src/mock.rs @@ -57,6 +57,7 @@ impl frame_system::Config for Test { type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); + type ExtensionsWeightInfo = (); type SS58Prefix = (); type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; diff --git a/substrate/frame/system/rpc/runtime-api/Cargo.toml b/substrate/frame/system/rpc/runtime-api/Cargo.toml index 8cec5de8d1e527862c0a91316f6c84bb2ec89b22..70e66769a8b3de7c87e2350367ea4477d10d3776 100644 --- a/substrate/frame/system/rpc/runtime-api/Cargo.toml +++ b/substrate/frame/system/rpc/runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-system-rpc-runtime-api" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/system/src/extensions/check_genesis.rs b/substrate/frame/system/src/extensions/check_genesis.rs index 76a711a823e7d7a4b1092d9220b846584b21603f..3f11f745cd9b7c509a6827100acae66413c80e5d 100644 --- a/substrate/frame/system/src/extensions/check_genesis.rs +++ b/substrate/frame/system/src/extensions/check_genesis.rs @@ -19,7 +19,8 @@ use crate::{pallet_prelude::BlockNumberFor, Config, Pallet}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension, Zero}, + impl_tx_ext_default, + traits::{TransactionExtension, TransactionExtensionBase, Zero}, transaction_validity::TransactionValidityError, }; @@ -46,30 +47,26 @@ impl sp_std::fmt::Debug for CheckGenesis { } impl CheckGenesis { - /// Creates new `SignedExtension` to check genesis hash. + /// Creates new `TransactionExtension` to check genesis hash. pub fn new() -> Self { Self(sp_std::marker::PhantomData) } } -impl SignedExtension for CheckGenesis { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = T::Hash; - type Pre = (); +impl TransactionExtensionBase for CheckGenesis { const IDENTIFIER: &'static str = "CheckGenesis"; - - fn additional_signed(&self) -> Result { + type Implicit = T::Hash; + fn implicit(&self) -> Result { Ok(>::block_hash(BlockNumberFor::::zero())) } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + fn weight(&self) -> sp_weights::Weight { + ::check_genesis() } } +impl TransactionExtension + for CheckGenesis +{ + type Val = (); + type Pre = (); + impl_tx_ext_default!(T::RuntimeCall; Context; validate prepare); +} diff --git a/substrate/frame/system/src/extensions/check_mortality.rs b/substrate/frame/system/src/extensions/check_mortality.rs index 148dfd4aad471b8a51aa3106581531b17090c20a..deb44880d6441429cdd1513bdc4ac0fbfc6c0421 100644 --- a/substrate/frame/system/src/extensions/check_mortality.rs +++ b/substrate/frame/system/src/extensions/check_mortality.rs @@ -20,10 +20,12 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ generic::Era, - traits::{DispatchInfoOf, SaturatedConversion, SignedExtension}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + impl_tx_ext_default, + traits::{ + DispatchInfoOf, SaturatedConversion, TransactionExtension, TransactionExtensionBase, + ValidateResult, }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, }; /// Check for transaction mortality. @@ -54,29 +56,11 @@ impl sp_std::fmt::Debug for CheckMortality { } } -impl SignedExtension for CheckMortality { - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = T::Hash; - type Pre = (); +impl TransactionExtensionBase for CheckMortality { const IDENTIFIER: &'static str = "CheckMortality"; + type Implicit = T::Hash; - fn validate( - &self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - let current_u64 = >::block_number().saturated_into::(); - let valid_till = self.0.death(current_u64); - Ok(ValidTransaction { - longevity: valid_till.saturating_sub(current_u64), - ..Default::default() - }) - } - - fn additional_signed(&self) -> Result { + fn implicit(&self) -> Result { let current_u64 = >::block_number().saturated_into::(); let n = self.0.birth(current_u64).saturated_into::>(); if !>::contains_key(n) { @@ -85,16 +69,38 @@ impl SignedExtension for CheckMortality { Ok(>::block_hash(n)) } } + fn weight(&self) -> sp_weights::Weight { + ::check_mortality() + } +} +impl TransactionExtension + for CheckMortality +{ + type Pre = (); + type Val = (); - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + fn validate( + &self, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let current_u64 = >::block_number().saturated_into::(); + let valid_till = self.0.death(current_u64); + Ok(( + ValidTransaction { + longevity: valid_till.saturating_sub(current_u64), + ..Default::default() + }, + (), + origin, + )) } + impl_tx_ext_default!(T::RuntimeCall; Context; prepare); } #[cfg(test)] @@ -106,23 +112,21 @@ mod tests { weights::Weight, }; use sp_core::H256; + use sp_runtime::traits::DispatchTransaction; #[test] fn signed_ext_check_era_should_work() { new_test_ext().execute_with(|| { // future assert_eq!( - CheckMortality::::from(Era::mortal(4, 2)) - .additional_signed() - .err() - .unwrap(), + CheckMortality::::from(Era::mortal(4, 2)).implicit().err().unwrap(), InvalidTransaction::AncientBirthBlock.into(), ); // correct System::set_block_number(13); >::insert(12, H256::repeat_byte(1)); - assert!(CheckMortality::::from(Era::mortal(4, 12)).additional_signed().is_ok()); + assert!(CheckMortality::::from(Era::mortal(4, 12)).implicit().is_ok()); }) } @@ -142,7 +146,10 @@ mod tests { System::set_block_number(17); >::insert(16, H256::repeat_byte(1)); - assert_eq!(ext.validate(&1, CALL, &normal, len).unwrap().longevity, 15); + assert_eq!( + ext.validate_only(Some(1).into(), CALL, &normal, len).unwrap().0.longevity, + 15 + ); }) } } diff --git a/substrate/frame/system/src/extensions/check_non_zero_sender.rs b/substrate/frame/system/src/extensions/check_non_zero_sender.rs index 92eed60fc66b53dec19a86c1fac20f1af8ff4d7b..115ffac9b038a722c404cb714442306dc704a75d 100644 --- a/substrate/frame/system/src/extensions/check_non_zero_sender.rs +++ b/substrate/frame/system/src/extensions/check_non_zero_sender.rs @@ -17,13 +17,14 @@ use crate::Config; use codec::{Decode, Encode}; -use frame_support::{dispatch::DispatchInfo, DefaultNoBound}; +use frame_support::{traits::OriginTrait, DefaultNoBound}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, SignedExtension}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + impl_tx_ext_default, + traits::{ + transaction_extension::TransactionExtensionBase, DispatchInfoOf, TransactionExtension, }, + transaction_validity::InvalidTransaction, }; use sp_std::{marker::PhantomData, prelude::*}; @@ -45,66 +46,82 @@ impl sp_std::fmt::Debug for CheckNonZeroSender { } impl CheckNonZeroSender { - /// Create new `SignedExtension` to check runtime version. + /// Create new `TransactionExtension` to check runtime version. pub fn new() -> Self { Self(sp_std::marker::PhantomData) } } -impl SignedExtension for CheckNonZeroSender -where - T::RuntimeCall: Dispatchable, -{ - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); - type Pre = (); +impl TransactionExtensionBase for CheckNonZeroSender { const IDENTIFIER: &'static str = "CheckNonZeroSender"; - - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) + type Implicit = (); + fn weight(&self) -> sp_weights::Weight { + ::check_non_zero_sender() } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } - +} +impl TransactionExtension + for CheckNonZeroSender +{ + type Val = (); + type Pre = (); fn validate( &self, - who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { - if who.using_encoded(|d| d.iter().all(|x| *x == 0)) { - return Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner)) + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> sp_runtime::traits::ValidateResult { + if let Some(who) = origin.as_system_signer() { + if who.using_encoded(|d| d.iter().all(|x| *x == 0)) { + return Err(InvalidTransaction::BadSigner.into()) + } } - Ok(ValidTransaction::default()) + Ok((Default::default(), (), origin)) } + impl_tx_ext_default!(T::RuntimeCall; Context; prepare); } #[cfg(test)] mod tests { use super::*; use crate::mock::{new_test_ext, Test, CALL}; - use frame_support::{assert_noop, assert_ok}; + use frame_support::{assert_ok, dispatch::DispatchInfo}; + use sp_runtime::{traits::DispatchTransaction, TransactionValidityError}; #[test] fn zero_account_ban_works() { new_test_ext().execute_with(|| { let info = DispatchInfo::default(); let len = 0_usize; - assert_noop!( - CheckNonZeroSender::::new().validate(&0, CALL, &info, len), - InvalidTransaction::BadSigner + assert_eq!( + CheckNonZeroSender::::new() + .validate_only(Some(0).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::from(InvalidTransaction::BadSigner) ); - assert_ok!(CheckNonZeroSender::::new().validate(&1, CALL, &info, len)); + assert_ok!(CheckNonZeroSender::::new().validate_only( + Some(1).into(), + CALL, + &info, + len + )); + }) + } + + #[test] + fn unsigned_origin_works() { + new_test_ext().execute_with(|| { + let info = DispatchInfo::default(); + let len = 0_usize; + assert_ok!(CheckNonZeroSender::::new().validate_only( + None.into(), + CALL, + &info, + len + )); }) } } diff --git a/substrate/frame/system/src/extensions/check_nonce.rs b/substrate/frame/system/src/extensions/check_nonce.rs index 7504a814aef13b0230e1067c0d95e0104ad74275..ead9d9f7c8177ca1392b092b9418b3b2ae0b93ea 100644 --- a/substrate/frame/system/src/extensions/check_nonce.rs +++ b/substrate/frame/system/src/extensions/check_nonce.rs @@ -15,16 +15,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::Config; +use crate::{AccountInfo, Config}; use codec::{Decode, Encode}; use frame_support::dispatch::DispatchInfo; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, One, SignedExtension, Zero}, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, One, TransactionExtension, + TransactionExtensionBase, ValidateResult, Zero, + }, transaction_validity::{ - InvalidTransaction, TransactionLongevity, TransactionValidity, TransactionValidityError, - ValidTransaction, + InvalidTransaction, TransactionLongevity, TransactionValidityError, ValidTransaction, }, + Saturating, }; use sp_std::vec; @@ -58,75 +61,78 @@ impl sp_std::fmt::Debug for CheckNonce { } } -impl SignedExtension for CheckNonce +impl TransactionExtensionBase for CheckNonce { + const IDENTIFIER: &'static str = "CheckNonce"; + type Implicit = (); + fn weight(&self) -> sp_weights::Weight { + ::check_nonce() + } +} +impl TransactionExtension for CheckNonce where T::RuntimeCall: Dispatchable, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, { - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); + type Val = Option<(T::AccountId, AccountInfo)>; type Pre = (); - const IDENTIFIER: &'static str = "CheckNonce"; - - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result<(), TransactionValidityError> { - let mut account = crate::Account::::get(who); - if account.providers.is_zero() && account.sufficients.is_zero() { - // Nonce storage not paid for - return Err(InvalidTransaction::Payment.into()) - } - if self.0 != account.nonce { - return Err(if self.0 < account.nonce { - InvalidTransaction::Stale - } else { - InvalidTransaction::Future - } - .into()) - } - account.nonce += T::Nonce::one(); - crate::Account::::insert(who, account); - Ok(()) - } fn validate( &self, - who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let Some(who) = origin.as_system_origin_signer() else { + return Ok((Default::default(), None, origin)) + }; let account = crate::Account::::get(who); if account.providers.is_zero() && account.sufficients.is_zero() { // Nonce storage not paid for - return InvalidTransaction::Payment.into() + return Err(InvalidTransaction::Payment.into()) } if self.0 < account.nonce { - return InvalidTransaction::Stale.into() + return Err(InvalidTransaction::Stale.into()) } - let provides = vec![Encode::encode(&(who, self.0))]; + let provides = vec![Encode::encode(&(who.clone(), self.0))]; let requires = if account.nonce < self.0 { - vec![Encode::encode(&(who, self.0 - One::one()))] + vec![Encode::encode(&(who.clone(), self.0.saturating_sub(One::one())))] } else { vec![] }; - Ok(ValidTransaction { + let validity = ValidTransaction { priority: 0, requires, provides, longevity: TransactionLongevity::max_value(), propagate: true, - }) + }; + + Ok((validity, Some((who.clone(), account)), origin)) + } + + fn prepare( + self, + val: Self::Val, + _origin: &T::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _context: &Context, + ) -> Result { + let Some((who, mut account)) = val else { return Ok(()) }; + // `self.0 < account.nonce` already checked in `validate`. + if self.0 > account.nonce { + return Err(InvalidTransaction::Future.into()) + } + account.nonce.saturating_inc(); + crate::Account::::insert(who, account); + Ok(()) } } @@ -134,7 +140,8 @@ where mod tests { use super::*; use crate::mock::{new_test_ext, Test, CALL}; - use frame_support::{assert_noop, assert_ok}; + use frame_support::assert_ok; + use sp_runtime::traits::DispatchTransaction; #[test] fn signed_ext_check_nonce_works() { @@ -152,22 +159,33 @@ mod tests { let info = DispatchInfo::default(); let len = 0_usize; // stale - assert_noop!( - CheckNonce::(0).validate(&1, CALL, &info, len), - InvalidTransaction::Stale + assert_eq!( + CheckNonce::(0) + .validate_only(Some(1).into(), CALL, &info, len,) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Stale) ); - assert_noop!( - CheckNonce::(0).pre_dispatch(&1, CALL, &info, len), - InvalidTransaction::Stale + assert_eq!( + CheckNonce::(0) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + InvalidTransaction::Stale.into() ); // correct - assert_ok!(CheckNonce::(1).validate(&1, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&1, CALL, &info, len)); + assert_ok!(CheckNonce::(1).validate_only(Some(1).into(), CALL, &info, len)); + assert_ok!(CheckNonce::(1).validate_and_prepare( + Some(1).into(), + CALL, + &info, + len + )); // future - assert_ok!(CheckNonce::(5).validate(&1, CALL, &info, len)); - assert_noop!( - CheckNonce::(5).pre_dispatch(&1, CALL, &info, len), - InvalidTransaction::Future + assert_ok!(CheckNonce::(5).validate_only(Some(1).into(), CALL, &info, len)); + assert_eq!( + CheckNonce::(5) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + InvalidTransaction::Future.into() ); }) } @@ -198,20 +216,44 @@ mod tests { let info = DispatchInfo::default(); let len = 0_usize; // Both providers and sufficients zero - assert_noop!( - CheckNonce::(1).validate(&1, CALL, &info, len), - InvalidTransaction::Payment + assert_eq!( + CheckNonce::(1) + .validate_only(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Payment) ); - assert_noop!( - CheckNonce::(1).pre_dispatch(&1, CALL, &info, len), - InvalidTransaction::Payment + assert_eq!( + CheckNonce::(1) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Payment) ); // Non-zero providers - assert_ok!(CheckNonce::(1).validate(&2, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&2, CALL, &info, len)); + assert_ok!(CheckNonce::(1).validate_only(Some(2).into(), CALL, &info, len)); + assert_ok!(CheckNonce::(1).validate_and_prepare( + Some(2).into(), + CALL, + &info, + len + )); // Non-zero sufficients - assert_ok!(CheckNonce::(1).validate(&3, CALL, &info, len)); - assert_ok!(CheckNonce::(1).pre_dispatch(&3, CALL, &info, len)); + assert_ok!(CheckNonce::(1).validate_only(Some(3).into(), CALL, &info, len)); + assert_ok!(CheckNonce::(1).validate_and_prepare( + Some(3).into(), + CALL, + &info, + len + )); + }) + } + + #[test] + fn unsigned_check_nonce_works() { + new_test_ext().execute_with(|| { + let info = DispatchInfo::default(); + let len = 0_usize; + assert_ok!(CheckNonce::(1).validate_only(None.into(), CALL, &info, len)); + assert_ok!(CheckNonce::(1).validate_and_prepare(None.into(), CALL, &info, len)); }) } } diff --git a/substrate/frame/system/src/extensions/check_spec_version.rs b/substrate/frame/system/src/extensions/check_spec_version.rs index 24d5ef9cafb17b0dd8c1ac02fc127f7068cf6cca..3109708f48efe467b6c727c6813db2275b70c548 100644 --- a/substrate/frame/system/src/extensions/check_spec_version.rs +++ b/substrate/frame/system/src/extensions/check_spec_version.rs @@ -19,7 +19,8 @@ use crate::{Config, Pallet}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, + impl_tx_ext_default, + traits::{transaction_extension::TransactionExtensionBase, TransactionExtension}, transaction_validity::TransactionValidityError, }; @@ -46,30 +47,26 @@ impl sp_std::fmt::Debug for CheckSpecVersion { } impl CheckSpecVersion { - /// Create new `SignedExtension` to check runtime version. + /// Create new `TransactionExtension` to check runtime version. pub fn new() -> Self { Self(sp_std::marker::PhantomData) } } -impl SignedExtension for CheckSpecVersion { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = u32; - type Pre = (); +impl TransactionExtensionBase for CheckSpecVersion { const IDENTIFIER: &'static str = "CheckSpecVersion"; - - fn additional_signed(&self) -> Result { + type Implicit = u32; + fn implicit(&self) -> Result { Ok(>::runtime_version().spec_version) } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + fn weight(&self) -> sp_weights::Weight { + ::check_spec_version() } } +impl TransactionExtension<::RuntimeCall, Context> + for CheckSpecVersion +{ + type Val = (); + type Pre = (); + impl_tx_ext_default!(::RuntimeCall; Context; validate prepare); +} diff --git a/substrate/frame/system/src/extensions/check_tx_version.rs b/substrate/frame/system/src/extensions/check_tx_version.rs index 3f9d6a1903fe1d08d05266a46625aeacba3c273c..ee1d507e7bc83d8bd90f08d1fb04f7b67842a011 100644 --- a/substrate/frame/system/src/extensions/check_tx_version.rs +++ b/substrate/frame/system/src/extensions/check_tx_version.rs @@ -19,7 +19,8 @@ use crate::{Config, Pallet}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, + impl_tx_ext_default, + traits::{transaction_extension::TransactionExtensionBase, TransactionExtension}, transaction_validity::TransactionValidityError, }; @@ -46,29 +47,26 @@ impl sp_std::fmt::Debug for CheckTxVersion { } impl CheckTxVersion { - /// Create new `SignedExtension` to check transaction version. + /// Create new `TransactionExtension` to check transaction version. pub fn new() -> Self { Self(sp_std::marker::PhantomData) } } -impl SignedExtension for CheckTxVersion { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = u32; - type Pre = (); +impl TransactionExtensionBase for CheckTxVersion { const IDENTIFIER: &'static str = "CheckTxVersion"; - - fn additional_signed(&self) -> Result { + type Implicit = u32; + fn implicit(&self) -> Result { Ok(>::runtime_version().transaction_version) } - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + fn weight(&self) -> sp_weights::Weight { + ::check_tx_version() } } +impl TransactionExtension<::RuntimeCall, Context> + for CheckTxVersion +{ + type Val = (); + type Pre = (); + impl_tx_ext_default!(::RuntimeCall; Context; validate prepare); +} diff --git a/substrate/frame/system/src/extensions/check_weight.rs b/substrate/frame/system/src/extensions/check_weight.rs index 70d1e75633278c665f4c06ca22f02a96668ef964..5c35acfb3f355c6784b4f6f9e2ca1269463346f0 100644 --- a/substrate/frame/system/src/extensions/check_weight.rs +++ b/substrate/frame/system/src/extensions/check_weight.rs @@ -23,9 +23,12 @@ use frame_support::{ }; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension}, - transaction_validity::{InvalidTransaction, TransactionValidity, TransactionValidityError}, - DispatchResult, + traits::{ + DispatchInfoOf, Dispatchable, PostDispatchInfoOf, TransactionExtension, + TransactionExtensionBase, ValidateResult, + }, + transaction_validity::{InvalidTransaction, TransactionValidityError}, + DispatchResult, ValidTransaction, }; use sp_weights::Weight; @@ -100,39 +103,43 @@ where } } - /// Creates new `SignedExtension` to check weight of the extrinsic. + /// Creates new `TransactionExtension` to check weight of the extrinsic. pub fn new() -> Self { Self(Default::default()) } - /// Do the pre-dispatch checks. This can be applied to both signed and unsigned. + /// Do the validate checks. This can be applied to both signed and unsigned. /// - /// It checks and notes the new weight and length. - pub fn do_pre_dispatch( + /// It only checks that the block weight and length limit will not exceed. + /// + /// Returns the transaction validity and the next block length, to be used in `prepare`. + pub fn do_validate( info: &DispatchInfoOf, len: usize, - ) -> Result<(), TransactionValidityError> { + ) -> Result<(ValidTransaction, u32), TransactionValidityError> { + // ignore the next length. If they return `Ok`, then it is below the limit. let next_len = Self::check_block_length(info, len)?; - let next_weight = Self::check_block_weight(info)?; + // during validation we skip block limit check. Since the `validate_transaction` + // call runs on an empty block anyway, by this we prevent `on_initialize` weight + // consumption from causing false negatives. Self::check_extrinsic_weight(info)?; - crate::AllExtrinsicsLen::::put(next_len); - crate::BlockWeight::::put(next_weight); - Ok(()) + Ok((Default::default(), next_len)) } - /// Do the validate checks. This can be applied to both signed and unsigned. + /// Do the pre-dispatch checks. This can be applied to both signed and unsigned. /// - /// It only checks that the block weight and length limit will not exceed. - pub fn do_validate(info: &DispatchInfoOf, len: usize) -> TransactionValidity { - // ignore the next length. If they return `Ok`, then it is below the limit. - let _ = Self::check_block_length(info, len)?; - // during validation we skip block limit check. Since the `validate_transaction` - // call runs on an empty block anyway, by this we prevent `on_initialize` weight - // consumption from causing false negatives. - Self::check_extrinsic_weight(info)?; + /// It checks and notes the new weight and length. + pub fn do_prepare( + info: &DispatchInfoOf, + next_len: u32, + ) -> Result<(), TransactionValidityError> { + let next_weight = Self::check_block_weight(info)?; + // Extrinsic weight already checked in `validate`. - Ok(Default::default()) + crate::AllExtrinsicsLen::::put(next_len); + crate::BlockWeight::::put(next_weight); + Ok(()) } } @@ -201,62 +208,55 @@ where Ok(all_weight) } -impl SignedExtension for CheckWeight +impl TransactionExtensionBase for CheckWeight { + const IDENTIFIER: &'static str = "CheckWeight"; + type Implicit = (); + + fn weight(&self) -> Weight { + ::check_weight() + } +} +impl TransactionExtension + for CheckWeight where T::RuntimeCall: Dispatchable, { - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); type Pre = (); - const IDENTIFIER: &'static str = "CheckWeight"; - - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result<(), TransactionValidityError> { - Self::do_pre_dispatch(info, len) - } + type Val = u32; /* next block length */ fn validate( &self, - _who: &Self::AccountId, - _call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> TransactionValidity { - Self::do_validate(info, len) - } - - fn pre_dispatch_unsigned( - _call: &Self::Call, - info: &DispatchInfoOf, + origin: T::RuntimeOrigin, + _call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> Result<(), TransactionValidityError> { - Self::do_pre_dispatch(info, len) + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let (validity, next_len) = Self::do_validate(info, len)?; + Ok((validity, next_len, origin)) } - fn validate_unsigned( - _call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> TransactionValidity { - Self::do_validate(info, len) + fn prepare( + self, + val: Self::Val, + _origin: &T::RuntimeOrigin, + _call: &T::RuntimeCall, + info: &DispatchInfoOf, + _len: usize, + _context: &Context, + ) -> Result { + Self::do_prepare(info, val) } fn post_dispatch( - _pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + _pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, _len: usize, _result: &DispatchResult, + _context: &Context, ) -> Result<(), TransactionValidityError> { let unspent = post_info.calc_unspent(info); if unspent.any_gt(Weight::zero()) { @@ -301,6 +301,7 @@ mod tests { AllExtrinsicsLen, BlockWeight, DispatchClass, }; use frame_support::{assert_err, assert_ok, dispatch::Pays, weights::Weight}; + use sp_runtime::traits::DispatchTransaction; use sp_std::marker::PhantomData; fn block_weights() -> crate::limits::BlockWeights { @@ -338,7 +339,8 @@ mod tests { } check(|max, len| { - assert_ok!(CheckWeight::::do_pre_dispatch(max, len)); + let next_len = CheckWeight::::check_block_length(max, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(max, next_len)); assert_eq!(System::block_weight().total(), Weight::MAX); assert!(System::block_weight().total().ref_time() > block_weight_limit().ref_time()); }); @@ -419,9 +421,11 @@ mod tests { let len = 0_usize; - assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); + let next_len = CheckWeight::::check_block_length(&max_normal, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&max_normal, next_len)); assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0)); - assert_ok!(CheckWeight::::do_pre_dispatch(&rest_operational, len)); + let next_len = CheckWeight::::check_block_length(&rest_operational, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&rest_operational, next_len)); assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0)); // Checking single extrinsic should not take current block weight into account. @@ -443,10 +447,12 @@ mod tests { let len = 0_usize; - assert_ok!(CheckWeight::::do_pre_dispatch(&rest_operational, len)); + let next_len = CheckWeight::::check_block_length(&rest_operational, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&rest_operational, next_len)); // Extra 20 here from block execution + base extrinsic weight assert_eq!(System::block_weight().total(), Weight::from_parts(266, 0)); - assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); + let next_len = CheckWeight::::check_block_length(&max_normal, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&max_normal, next_len)); assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0)); }); @@ -469,16 +475,19 @@ mod tests { }; let len = 0_usize; + let next_len = CheckWeight::::check_block_length(&dispatch_normal, len).unwrap(); assert_err!( - CheckWeight::::do_pre_dispatch(&dispatch_normal, len), + CheckWeight::::do_prepare(&dispatch_normal, next_len), InvalidTransaction::ExhaustsResources ); + let next_len = + CheckWeight::::check_block_length(&dispatch_operational, len).unwrap(); // Thank goodness we can still do an operational transaction to possibly save the // blockchain. - assert_ok!(CheckWeight::::do_pre_dispatch(&dispatch_operational, len)); + assert_ok!(CheckWeight::::do_prepare(&dispatch_operational, next_len)); // Not too much though assert_err!( - CheckWeight::::do_pre_dispatch(&dispatch_operational, len), + CheckWeight::::do_prepare(&dispatch_operational, next_len), InvalidTransaction::ExhaustsResources ); // Even with full block, validity of single transaction should be correct. @@ -503,21 +512,35 @@ mod tests { current_weight.set(normal_limit, DispatchClass::Normal) }); // will not fit. - assert_err!( - CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &normal, len), - InvalidTransaction::ExhaustsResources + assert_eq!( + CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &normal, len) + .unwrap_err(), + InvalidTransaction::ExhaustsResources.into() ); // will fit. - assert_ok!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &op, len)); + assert_ok!(CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + &op, + len + )); // likewise for length limit. let len = 100_usize; AllExtrinsicsLen::::put(normal_length_limit()); - assert_err!( - CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &normal, len), - InvalidTransaction::ExhaustsResources + assert_eq!( + CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &normal, len) + .unwrap_err(), + InvalidTransaction::ExhaustsResources.into() ); - assert_ok!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &op, len)); + assert_ok!(CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + &op, + len + )); }) } @@ -528,7 +551,12 @@ mod tests { let normal_limit = normal_weight_limit().ref_time() as usize; let reset_check_weight = |tx, s, f| { AllExtrinsicsLen::::put(0); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, tx, s); + let r = CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + tx, + s, + ); if f { assert!(r.is_err()) } else { @@ -571,7 +599,12 @@ mod tests { BlockWeight::::mutate(|current_weight| { current_weight.set(s, DispatchClass::Normal) }); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, i, len); + let r = CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + i, + len, + ); if f { assert!(r.is_err()) } else { @@ -604,18 +637,22 @@ mod tests { .set(Weight::from_parts(256, 0) - base_extrinsic, DispatchClass::Normal); }); - let pre = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap(); + let pre = CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap() + .0; assert_eq!( BlockWeight::::get().total(), info.weight + Weight::from_parts(256, 0) ); assert_ok!(CheckWeight::::post_dispatch( - Some(pre), + pre, &info, &post_info, len, - &Ok(()) + &Ok(()), + &() )); assert_eq!( BlockWeight::::get().total(), @@ -639,7 +676,10 @@ mod tests { current_weight.set(Weight::from_parts(128, 0), DispatchClass::Normal); }); - let pre = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap(); + let pre = CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap() + .0; assert_eq!( BlockWeight::::get().total(), info.weight + @@ -648,11 +688,12 @@ mod tests { ); assert_ok!(CheckWeight::::post_dispatch( - Some(pre), + pre, &info, &post_info, len, - &Ok(()) + &Ok(()), + &() )); assert_eq!( BlockWeight::::get().total(), @@ -672,7 +713,12 @@ mod tests { // Initial weight from `weights.base_block` assert_eq!(System::block_weight().total(), weights.base_block); - assert_ok!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &free, len)); + assert_ok!(CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + &free, + len + )); assert_eq!( System::block_weight().total(), weights.get(DispatchClass::Normal).base_extrinsic + weights.base_block @@ -696,9 +742,11 @@ mod tests { let len = 0_usize; - assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); + let next_len = CheckWeight::::check_block_length(&max_normal, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&max_normal, next_len)); assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0)); - assert_ok!(CheckWeight::::do_pre_dispatch(&mandatory, len)); + let next_len = CheckWeight::::check_block_length(&mandatory, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&mandatory, next_len)); assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), Weight::from_parts(1024 + 768, 0)); assert_eq!(CheckWeight::::check_extrinsic_weight(&mandatory), Ok(())); diff --git a/substrate/frame/system/src/extensions/mod.rs b/substrate/frame/system/src/extensions/mod.rs index a88c9fbf96ebdac86d9c45a8e5b07020c3325bb0..d79104d224035450f9ca69ba7d4b502b9ff0289d 100644 --- a/substrate/frame/system/src/extensions/mod.rs +++ b/substrate/frame/system/src/extensions/mod.rs @@ -22,3 +22,6 @@ pub mod check_nonce; pub mod check_spec_version; pub mod check_tx_version; pub mod check_weight; +pub mod weights; + +pub use weights::WeightInfo; diff --git a/substrate/frame/system/src/extensions/weights.rs b/substrate/frame/system/src/extensions/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..0d2fcb15ee66e927bb21c159f5b5b7a3dcbb22f5 --- /dev/null +++ b/substrate/frame/system/src/extensions/weights.rs @@ -0,0 +1,196 @@ +// 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 `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/system/src/extensions/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --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 `frame_system_extensions`. +pub trait WeightInfo { + fn check_genesis() -> Weight; + fn check_mortality() -> Weight; + fn check_non_zero_sender() -> Weight; + fn check_nonce() -> Weight; + fn check_spec_version() -> Weight; + fn check_tx_version() -> Weight; + fn check_weight() -> Weight; +} + +/// Weights for `frame_system_extensions` 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 check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_876_000 picoseconds. + Weight::from_parts(4_160_000, 3509) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 6_296_000 picoseconds. + Weight::from_parts(6_523_000, 3509) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 449_000 picoseconds. + Weight::from_parts(527_000, 0) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 5_689_000 picoseconds. + Weight::from_parts(6_000_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 399_000 picoseconds. + Weight::from_parts(461_000, 0) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 390_000 picoseconds. + Weight::from_parts(439_000, 0) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 4_375_000 picoseconds. + Weight::from_parts(4_747_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_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 check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_876_000 picoseconds. + Weight::from_parts(4_160_000, 3509) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 6_296_000 picoseconds. + Weight::from_parts(6_523_000, 3509) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 449_000 picoseconds. + Weight::from_parts(527_000, 0) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 5_689_000 picoseconds. + Weight::from_parts(6_000_000, 3593) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 399_000 picoseconds. + Weight::from_parts(461_000, 0) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 390_000 picoseconds. + Weight::from_parts(439_000, 0) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 4_375_000 picoseconds. + Weight::from_parts(4_747_000, 1489) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index 069217bcee46b4663c836a750bfd90a568c1a4d2..0318f77342e00380bc6b663e3973e01eb629efa7 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -130,11 +130,13 @@ use frame_support::{ DispatchResult, DispatchResultWithPostInfo, PerDispatchClass, PostDispatchInfo, }, ensure, impl_ensure_origin_with_arg_ignoring_arg, + migrations::MultiStepMigrator, pallet_prelude::Pays, storage::{self, StorageStreamIter}, traits::{ ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Get, HandleLifetime, - OnKilledAccount, OnNewAccount, OriginTrait, PalletInfo, SortedMembers, StoredMap, TypedGet, + OnKilledAccount, OnNewAccount, OnRuntimeUpgrade, OriginTrait, PalletInfo, SortedMembers, + StoredMap, TypedGet, }, Parameter, }; @@ -148,6 +150,7 @@ use sp_io::TestExternalities; pub mod limits; #[cfg(test)] pub(crate) mod mock; + pub mod offchain; mod extensions; @@ -163,11 +166,12 @@ pub use extensions::{ check_genesis::CheckGenesis, check_mortality::CheckMortality, check_non_zero_sender::CheckNonZeroSender, check_nonce::CheckNonce, check_spec_version::CheckSpecVersion, check_tx_version::CheckTxVersion, - check_weight::CheckWeight, + check_weight::CheckWeight, WeightInfo as ExtensionsWeightInfo, }; // Backward compatible re-export. pub use extensions::check_mortality::CheckMortality as CheckEra; pub use frame_support::dispatch::RawOrigin; +use frame_support::traits::{PostInherents, PostTransactions, PreInherents}; pub use weights::WeightInfo; const LOG_TARGET: &str = "runtime::system"; @@ -280,6 +284,7 @@ pub mod pallet { type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); + type ExtensionsWeightInfo = (); type SS58Prefix = (); type Version = (); type BlockWeights = (); @@ -298,9 +303,14 @@ pub mod pallet { type BaseCallFilter = frame_support::traits::Everything; type BlockHashCount = frame_support::traits::ConstU64<10>; type OnSetCode = (); + type SingleBlockMigrations = (); + type MultiBlockMigrator = (); + type PreInherents = (); + type PostInherents = (); + type PostTransactions = (); } - /// Default configurations of this pallet in a solo-chain environment. + /// Default configurations of this pallet in a solochain environment. /// /// ## Considerations: /// @@ -347,6 +357,9 @@ pub mod pallet { /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = (); + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = (); + /// This is used as an identifier of the chain. type SS58Prefix = (); @@ -392,6 +405,11 @@ pub mod pallet { /// The set code logic, just the default since we're not a parachain. type OnSetCode = (); + type SingleBlockMigrations = (); + type MultiBlockMigrator = (); + type PreInherents = (); + type PostInherents = (); + type PostTransactions = (); } /// Default configurations of this pallet in a relay-chain environment. @@ -451,6 +469,7 @@ pub mod pallet { + Clone + OriginTrait; + #[docify::export(system_runtime_call)] /// The aggregated `RuntimeCall` type. #[pallet::no_default_bounds] type RuntimeCall: Parameter @@ -523,7 +542,7 @@ pub mod pallet { #[pallet::constant] type DbWeight: Get; - /// Get the chain's current version. + /// Get the chain's in-code version. #[pallet::constant] type Version: Get; @@ -548,8 +567,12 @@ pub mod pallet { /// All resources should be cleaned up associated with the given account. type OnKilledAccount: OnKilledAccount; + /// Weight information for the extrinsics of this pallet. type SystemWeightInfo: WeightInfo; + /// Weight information for the transaction extensions of this pallet. + type ExtensionsWeightInfo: extensions::WeightInfo; + /// The designated SS58 prefix of this chain. /// /// This replaces the "ss58Format" property declared in the chain spec. Reason is @@ -570,6 +593,35 @@ pub mod pallet { /// The maximum number of consumers allowed on a single account. type MaxConsumers: ConsumerLimits; + + /// All migrations that should run in the next runtime upgrade. + /// + /// These used to be formerly configured in `Executive`. Parachains need to ensure that + /// running all these migrations in one block will not overflow the weight limit of a block. + /// The migrations are run *before* the pallet `on_runtime_upgrade` hooks, just like the + /// `OnRuntimeUpgrade` migrations. + type SingleBlockMigrations: OnRuntimeUpgrade; + + /// The migrator that is used to run Multi-Block-Migrations. + /// + /// Can be set to [`pallet-migrations`] or an alternative implementation of the interface. + /// The diagram in `frame_executive::block_flowchart` explains when it runs. + type MultiBlockMigrator: MultiStepMigrator; + + /// A callback that executes in *every block* directly before all inherents were applied. + /// + /// See `frame_executive::block_flowchart` for a in-depth explanation when it runs. + type PreInherents: PreInherents; + + /// A callback that executes in *every block* directly after all inherents were applied. + /// + /// See `frame_executive::block_flowchart` for a in-depth explanation when it runs. + type PostInherents: PostInherents; + + /// A callback that executes in *every block* directly after all transactions were applied. + /// + /// See `frame_executive::block_flowchart` for a in-depth explanation when it runs. + type PostTransactions: PostTransactions; } #[pallet::pallet] @@ -617,6 +669,9 @@ pub mod pallet { } /// Set the new runtime code without doing any checks of the given `code`. + /// + /// Note that runtime upgrades will not run if this is called with a not-increasing spec + /// version! #[pallet::call_index(3)] #[pallet::weight((T::SystemWeightInfo::set_code(), DispatchClass::Operational))] pub fn set_code_without_checks( @@ -812,6 +867,8 @@ pub mod pallet { NonZeroRefCount, /// The origin filter prevent the call to be dispatched. CallFiltered, + /// A multi-block migration is ongoing and prevents the current code from being replaced. + MultiBlockMigrationsOngoing, #[cfg(feature = "experimental")] /// The specified [`Task`] is not valid. InvalidTask, @@ -843,11 +900,15 @@ pub mod pallet { #[pallet::storage] pub(super) type ExtrinsicCount = StorageValue<_, u32>; + /// Whether all inherents have been applied. + #[pallet::storage] + pub type InherentsApplied = StorageValue<_, bool, ValueQuery>; + /// The current weight for the block. #[pallet::storage] #[pallet::whitelist_storage] #[pallet::getter(fn block_weight)] - pub(super) type BlockWeight = StorageValue<_, ConsumedWeight, ValueQuery>; + pub type BlockWeight = StorageValue<_, ConsumedWeight, ValueQuery>; /// Total length (in bytes) for all extrinsics put together, for the current block. #[pallet::storage] @@ -892,6 +953,7 @@ pub mod pallet { /// just in case someone still reads them from within the runtime. #[pallet::storage] #[pallet::whitelist_storage] + #[pallet::disable_try_decode_storage] #[pallet::unbounded] pub(super) type Events = StorageValue<_, Vec>>, ValueQuery>; @@ -1370,6 +1432,19 @@ impl Pallet { Self::deposit_event(Event::CodeUpdated); } + /// Whether all inherents have been applied. + pub fn inherents_applied() -> bool { + InherentsApplied::::get() + } + + /// Note that all inherents have been applied. + /// + /// Should be called immediately after all inherents have been applied. Must be called at least + /// once per block. + pub fn note_inherents_applied() { + InherentsApplied::::put(true); + } + /// Increment the reference counter on an account. #[deprecated = "Use `inc_consumers` instead"] pub fn inc_ref(who: &T::AccountId) { @@ -1689,6 +1764,7 @@ impl Pallet { >::put(digest); >::put(parent_hash); >::insert(*number - One::one(), parent_hash); + >::kill(); // Remove previous block data from storage BlockWeight::::kill(); @@ -1735,6 +1811,7 @@ impl Pallet { ExecutionPhase::::kill(); AllExtrinsicsLen::::kill(); storage::unhashed::kill(well_known_keys::INTRABLOCK_ENTROPY); + InherentsApplied::::kill(); // The following fields // @@ -1978,10 +2055,14 @@ impl Pallet { /// Determine whether or not it is possible to update the code. /// - /// Checks the given code if it is a valid runtime wasm blob by instantianting + /// Checks the given code if it is a valid runtime wasm blob by instantiating /// it and extracting the runtime version of it. It checks that the runtime version /// of the old and new runtime has the same spec name and that the spec version is increasing. pub fn can_set_code(code: &[u8]) -> Result<(), sp_runtime::DispatchError> { + if T::MultiBlockMigrator::ongoing() { + return Err(Error::::MultiBlockMigrationsOngoing.into()) + } + let current_version = T::Version::get(); let new_version = sp_io::misc::runtime_version(code) .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) diff --git a/substrate/frame/system/src/mock.rs b/substrate/frame/system/src/mock.rs index 06527a9bb2912b4253186307456ad0885cf2c17e..7059845a7df33104a66348803fee6396bf2f93d2 100644 --- a/substrate/frame/system/src/mock.rs +++ b/substrate/frame/system/src/mock.rs @@ -16,15 +16,8 @@ // limitations under the License. use crate::{self as frame_system, *}; -use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU32, ConstU64}, -}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, Perbill, -}; +use frame_support::{derive_impl, parameter_types}; +use sp_runtime::{BuildStorage, Perbill}; type Block = mocking::MockBlock; @@ -87,29 +80,28 @@ impl OnKilledAccount for RecordKilled { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl Config for Test { - type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<10>; - type DbWeight = DbWeight; type Version = Version; - type PalletInfo = PalletInfo; type AccountData = u32; - type OnNewAccount = (); type OnKilledAccount = RecordKilled; - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; + type MultiBlockMigrator = MockedMigrator; +} + +parameter_types! { + pub static Ongoing: bool = false; +} + +pub struct MockedMigrator; +impl frame_support::migrations::MultiStepMigrator for MockedMigrator { + fn ongoing() -> bool { + Ongoing::get() + } + + fn step() -> Weight { + Weight::zero() + } } pub type SysEvent = frame_system::Event; diff --git a/substrate/frame/system/src/offchain.rs b/substrate/frame/system/src/offchain.rs index a019cfd666e8cb3ddccc02aa706692cbe6cee2e3..ee73e33fe31302ea1fb864b7e88164f3feb02e39 100644 --- a/substrate/frame/system/src/offchain.rs +++ b/substrate/frame/system/src/offchain.rs @@ -79,6 +79,9 @@ pub struct SubmitTransaction, Overarchi _phantom: sp_std::marker::PhantomData<(T, OverarchingCall)>, } +// TODO [#2415]: Avoid splitting call and the totally opaque `signature`; `CreateTransaction` trait +// should provide something which impls `Encode`, which can be sent onwards to +// `sp_io::offchain::submit_transaction`. There's no great need to split things up as in here. impl SubmitTransaction where T: SendTransactionTypes, @@ -88,6 +91,8 @@ where call: >::OverarchingCall, signature: Option<::SignaturePayload>, ) -> Result<(), ()> { + // TODO: Use regular transaction API instead. + #[allow(deprecated)] let xt = T::Extrinsic::new(call, signature).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) } @@ -471,7 +476,7 @@ pub trait SendTransactionTypes { /// /// This trait is meant to be implemented by the runtime and is responsible for constructing /// a payload to be signed and contained within the extrinsic. -/// This will most likely include creation of `SignedExtra` (a set of `SignedExtensions`). +/// This will most likely include creation of `TxExtension` (a tuple of `TransactionExtension`s). /// Note that the result can be altered by inspecting the `Call` (for instance adjusting /// fees, or mortality depending on the `pallet` being called). pub trait CreateSignedTransaction: @@ -621,14 +626,17 @@ mod tests { use crate::mock::{RuntimeCall, Test as TestRuntime, CALL}; use codec::Decode; use sp_core::offchain::{testing, TransactionPoolExt}; - use sp_runtime::testing::{TestSignature, TestXt, UintAuthorityId}; + use sp_runtime::{ + generic::UncheckedExtrinsic, + testing::{TestSignature, UintAuthorityId}, + }; impl SigningTypes for TestRuntime { type Public = UintAuthorityId; type Signature = TestSignature; } - type Extrinsic = TestXt; + type Extrinsic = UncheckedExtrinsic; impl SendTransactionTypes for TestRuntime { type Extrinsic = Extrinsic; @@ -693,7 +701,7 @@ mod tests { let _tx3 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } @@ -724,7 +732,7 @@ mod tests { let tx1 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } @@ -758,7 +766,7 @@ mod tests { let _tx2 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } @@ -790,7 +798,7 @@ mod tests { let tx1 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } } diff --git a/substrate/frame/system/src/tests.rs b/substrate/frame/system/src/tests.rs index e437e7f9f39b0845d9f7eef33e3c90546c8dbbd2..b889b5ca046efe557e7706870c11febce8a358b2 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -675,6 +675,28 @@ fn set_code_with_real_wasm_blob() { }); } +#[test] +fn set_code_rejects_during_mbm() { + Ongoing::set(true); + + let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = new_test_ext(); + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + ext.execute_with(|| { + System::set_block_number(1); + let res = System::set_code( + RawOrigin::Root.into(), + substrate_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(), + ); + assert_eq!( + res, + Err(DispatchErrorWithPostInfo::from(Error::::MultiBlockMigrationsOngoing)) + ); + + assert!(System::events().is_empty()); + }); +} + #[test] fn set_code_via_authorization_works() { let executor = substrate_test_runtime_client::new_native_or_wasm_executor(); diff --git a/substrate/frame/system/src/weights.rs b/substrate/frame/system/src/weights.rs index 41807dea1c55f9cd246db231fc9102d4b9eb5c4f..bd64bbf37639f4b257b0e2d3b75e812131b16e72 100644 --- a/substrate/frame/system/src/weights.rs +++ b/substrate/frame/system/src/weights.rs @@ -15,30 +15,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for frame_system +//! Autogenerated weights for `frame_system` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-s7kdgajz-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=frame_system +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=frame-system -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/system/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/system/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for frame_system. +/// Weight functions needed for `frame_system`. pub trait WeightInfo { fn remark(b: u32, ) -> Weight; fn remark_with_event(b: u32, ) -> Weight; @@ -61,7 +62,7 @@ pub trait WeightInfo { fn apply_authorized_upgrade() -> Weight; } -/// Weights for frame_system using the Substrate node and recommended hardware. +/// Weights for `frame_system` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[0, 3932160]`. @@ -69,84 +70,86 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_004_000 picoseconds. - Weight::from_parts(2_119_000, 0) + // Minimum execution time: 2_130_000 picoseconds. + Weight::from_parts(2_976_430, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(390, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(386, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_032_000 picoseconds. - Weight::from_parts(8_097_000, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_455, 0).saturating_mul(b.into())) + // Minimum execution time: 5_690_000 picoseconds. + Weight::from_parts(15_071_416, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_387, 0).saturating_mul(b.into())) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a686561707061676573` (r:0 w:1) - /// Proof Skipped: unknown `0x3a686561707061676573` (r:0 w:1) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) fn set_heap_pages() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 4_446_000 picoseconds. - Weight::from_parts(4_782_000, 1485) + // Minimum execution time: 3_822_000 picoseconds. + Weight::from_parts(4_099_000, 1485) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a636f6465` (r:0 w:1) - /// Proof Skipped: unknown `0x3a636f6465` (r:0 w:1) + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `1485` - // Minimum execution time: 84_000_503_000 picoseconds. - Weight::from_parts(87_586_619_000, 1485) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 81_512_045_000 picoseconds. + Weight::from_parts(82_321_281_000, 67035) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn set_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_086_000 picoseconds. - Weight::from_parts(2_175_000, 0) - // Standard Error: 1_056 - .saturating_add(Weight::from_parts(841_511, 0).saturating_mul(i.into())) + // Minimum execution time: 2_074_000 picoseconds. + Weight::from_parts(2_137_000, 0) + // Standard Error: 879 + .saturating_add(Weight::from_parts(797_224, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn kill_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_000_000 picoseconds. - Weight::from_parts(2_255_000, 0) - // Standard Error: 1_425 - .saturating_add(Weight::from_parts(662_473, 0).saturating_mul(i.into())) + // Minimum execution time: 2_122_000 picoseconds. + Weight::from_parts(2_208_000, 0) + // Standard Error: 855 + .saturating_add(Weight::from_parts(594_034, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `p` is `[0, 1000]`. fn kill_prefix(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `115 + p * (69 ±0)` - // Estimated: `128 + p * (70 ±0)` - // Minimum execution time: 4_189_000 picoseconds. - Weight::from_parts(4_270_000, 128) - // Standard Error: 2_296 - .saturating_add(Weight::from_parts(1_389_650, 0).saturating_mul(p.into())) + // Measured: `129 + p * (69 ±0)` + // Estimated: `135 + p * (70 ±0)` + // Minimum execution time: 3_992_000 picoseconds. + Weight::from_parts(4_170_000, 135) + // Standard Error: 1_377 + .saturating_add(Weight::from_parts(1_267_892, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) @@ -157,114 +160,116 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 33_027_000 picoseconds. - Weight::from_parts(33_027_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(1)) + // Minimum execution time: 8_872_000 picoseconds. + Weight::from_parts(9_513_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `System::AuthorizedUpgrade` (r:1 w:1) /// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) /// Storage: `System::Digest` (r:1 w:1) /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) /// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) fn apply_authorized_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `22` - // Estimated: `1518` - // Minimum execution time: 118_101_992_000 picoseconds. - Weight::from_parts(118_101_992_000, 0) - .saturating_add(Weight::from_parts(0, 1518)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(3)) + // Measured: `164` + // Estimated: `67035` + // Minimum execution time: 85_037_546_000 picoseconds. + Weight::from_parts(85_819_414_000, 67035) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { /// The range of component `b` is `[0, 3932160]`. fn remark(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_004_000 picoseconds. - Weight::from_parts(2_119_000, 0) + // Minimum execution time: 2_130_000 picoseconds. + Weight::from_parts(2_976_430, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(390, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(386, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_032_000 picoseconds. - Weight::from_parts(8_097_000, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_455, 0).saturating_mul(b.into())) + // Minimum execution time: 5_690_000 picoseconds. + Weight::from_parts(15_071_416, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_387, 0).saturating_mul(b.into())) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a686561707061676573` (r:0 w:1) - /// Proof Skipped: unknown `0x3a686561707061676573` (r:0 w:1) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) fn set_heap_pages() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 4_446_000 picoseconds. - Weight::from_parts(4_782_000, 1485) + // Minimum execution time: 3_822_000 picoseconds. + Weight::from_parts(4_099_000, 1485) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a636f6465` (r:0 w:1) - /// Proof Skipped: unknown `0x3a636f6465` (r:0 w:1) + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `1485` - // Minimum execution time: 84_000_503_000 picoseconds. - Weight::from_parts(87_586_619_000, 1485) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `142` + // Estimated: `67035` + // Minimum execution time: 81_512_045_000 picoseconds. + Weight::from_parts(82_321_281_000, 67035) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn set_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_086_000 picoseconds. - Weight::from_parts(2_175_000, 0) - // Standard Error: 1_056 - .saturating_add(Weight::from_parts(841_511, 0).saturating_mul(i.into())) + // Minimum execution time: 2_074_000 picoseconds. + Weight::from_parts(2_137_000, 0) + // Standard Error: 879 + .saturating_add(Weight::from_parts(797_224, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn kill_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_000_000 picoseconds. - Weight::from_parts(2_255_000, 0) - // Standard Error: 1_425 - .saturating_add(Weight::from_parts(662_473, 0).saturating_mul(i.into())) + // Minimum execution time: 2_122_000 picoseconds. + Weight::from_parts(2_208_000, 0) + // Standard Error: 855 + .saturating_add(Weight::from_parts(594_034, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `p` is `[0, 1000]`. fn kill_prefix(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `115 + p * (69 ±0)` - // Estimated: `128 + p * (70 ±0)` - // Minimum execution time: 4_189_000 picoseconds. - Weight::from_parts(4_270_000, 128) - // Standard Error: 2_296 - .saturating_add(Weight::from_parts(1_389_650, 0).saturating_mul(p.into())) + // Measured: `129 + p * (69 ±0)` + // Estimated: `135 + p * (70 ±0)` + // Minimum execution time: 3_992_000 picoseconds. + Weight::from_parts(4_170_000, 135) + // Standard Error: 1_377 + .saturating_add(Weight::from_parts(1_267_892, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) @@ -275,25 +280,25 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 33_027_000 picoseconds. - Weight::from_parts(33_027_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Minimum execution time: 8_872_000 picoseconds. + Weight::from_parts(9_513_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `System::AuthorizedUpgrade` (r:1 w:1) /// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0) + /// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`) /// Storage: `System::Digest` (r:1 w:1) /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) /// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) fn apply_authorized_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `22` - // Estimated: `1518` - // Minimum execution time: 118_101_992_000 picoseconds. - Weight::from_parts(118_101_992_000, 0) - .saturating_add(Weight::from_parts(0, 1518)) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(3)) + // Measured: `164` + // Estimated: `67035` + // Minimum execution time: 85_037_546_000 picoseconds. + Weight::from_parts(85_819_414_000, 67035) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/substrate/frame/timestamp/Cargo.toml b/substrate/frame/timestamp/Cargo.toml index bcf26d622b08066f233e1c688861309cb19b454a..28e57fcab0a79d18ea8d8a1b2b8be90cb05198db 100644 --- a/substrate/frame/timestamp/Cargo.toml +++ b/substrate/frame/timestamp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-timestamp" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } @@ -30,7 +30,7 @@ sp-std = { path = "../../primitives/std", default-features = false } sp-storage = { path = "../../primitives/storage", default-features = false } sp-timestamp = { path = "../../primitives/timestamp", default-features = false } -docify = "0.2.6" +docify = "0.2.7" [dev-dependencies] sp-core = { path = "../../primitives/core" } diff --git a/substrate/frame/timestamp/src/mock.rs b/substrate/frame/timestamp/src/mock.rs index 5cbc5211368d4e6ed8027b56a06a49f02f1d3f86..244b66a4bb2c64428011b1e90fea0ec76d5ba6f2 100644 --- a/substrate/frame/timestamp/src/mock.rs +++ b/substrate/frame/timestamp/src/mock.rs @@ -20,16 +20,9 @@ use super::*; use crate as pallet_timestamp; -use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU32, ConstU64}, -}; -use sp_core::H256; +use frame_support::{derive_impl, parameter_types, traits::ConstU64}; use sp_io::TestExternalities; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; type Moment = u64; @@ -44,29 +37,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } parameter_types! { diff --git a/substrate/frame/timestamp/src/weights.rs b/substrate/frame/timestamp/src/weights.rs index 46c544734869442c17d2be8b354bed916e197b83..2df68ba0f1e716ed5c390f7a173ff449c35e88d2 100644 --- a/substrate/frame/timestamp/src/weights.rs +++ b/substrate/frame/timestamp/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_timestamp +//! Autogenerated weights for `pallet_timestamp` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/timestamp/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/timestamp/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,57 +49,57 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_timestamp. +/// Weight functions needed for `pallet_timestamp`. pub trait WeightInfo { fn set() -> Weight; fn on_finalize() -> Weight; } -/// Weights for pallet_timestamp using the Substrate node and recommended hardware. +/// Weights for `pallet_timestamp` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Timestamp Now (r:1 w:1) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe CurrentSlot (r:1 w:0) - /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::CurrentSlot` (r:1 w:0) + /// Proof: `Babe::CurrentSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set() -> Weight { // Proof Size summary in bytes: - // Measured: `312` + // Measured: `345` // Estimated: `1493` - // Minimum execution time: 9_857_000 picoseconds. - Weight::from_parts(10_492_000, 1493) + // Minimum execution time: 8_417_000 picoseconds. + Weight::from_parts(8_932_000, 1493) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn on_finalize() -> Weight { // Proof Size summary in bytes: - // Measured: `161` + // Measured: `194` // Estimated: `0` - // Minimum execution time: 4_175_000 picoseconds. - Weight::from_parts(4_334_000, 0) + // Minimum execution time: 3_911_000 picoseconds. + Weight::from_parts(4_092_000, 0) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Timestamp Now (r:1 w:1) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe CurrentSlot (r:1 w:0) - /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::CurrentSlot` (r:1 w:0) + /// Proof: `Babe::CurrentSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set() -> Weight { // Proof Size summary in bytes: - // Measured: `312` + // Measured: `345` // Estimated: `1493` - // Minimum execution time: 9_857_000 picoseconds. - Weight::from_parts(10_492_000, 1493) + // Minimum execution time: 8_417_000 picoseconds. + Weight::from_parts(8_932_000, 1493) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn on_finalize() -> Weight { // Proof Size summary in bytes: - // Measured: `161` + // Measured: `194` // Estimated: `0` - // Minimum execution time: 4_175_000 picoseconds. - Weight::from_parts(4_334_000, 0) + // Minimum execution time: 3_911_000 picoseconds. + Weight::from_parts(4_092_000, 0) } } diff --git a/substrate/frame/tips/Cargo.toml b/substrate/frame/tips/Cargo.toml index 2516e0e993d5d6614ff23ad73c94cb9eb25f9c82..7339cf0a8cce86ec15aebe6a55115f10360b1a7f 100644 --- a/substrate/frame/tips/Cargo.toml +++ b/substrate/frame/tips/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-tips" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,9 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", features = ["derive"], optional = true } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/tips/src/lib.rs b/substrate/frame/tips/src/lib.rs index 4c7cfc3028a9f04c87022ffc89892b59ce3c5610..8c360fb57d72488553529ab4264eb7f8b444c064 100644 --- a/substrate/frame/tips/src/lib.rs +++ b/substrate/frame/tips/src/lib.rs @@ -122,7 +122,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. + /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] diff --git a/substrate/frame/tips/src/migrations/v4.rs b/substrate/frame/tips/src/migrations/v4.rs index 2404c6de1a16bb0657bd7771a3019186db8d0976..8b0e65c58dbde4b754892b09da93187a25dec087 100644 --- a/substrate/frame/tips/src/migrations/v4.rs +++ b/substrate/frame/tips/src/migrations/v4.rs @@ -15,8 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::str; use sp_io::hashing::twox_128; -use sp_std::str; use super::super::LOG_TARGET; use frame_support::{ diff --git a/substrate/frame/tips/src/tests.rs b/substrate/frame/tips/src/tests.rs index b8e528bbe7ab435100f3ac212cedf3f08b10978c..0e7ea1f47817c5ae14dace2dce7ce7e555a68a03 100644 --- a/substrate/frame/tips/src/tests.rs +++ b/substrate/frame/tips/src/tests.rs @@ -59,29 +59,10 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -98,7 +79,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { static TenToFourteenTestValue: Vec = vec![10,11,12,13,14]; diff --git a/substrate/frame/tips/src/weights.rs b/substrate/frame/tips/src/weights.rs index ec6228667159ddbdbfecd12ccb5ff6250390829a..dbe1ed246ff015567e94e2396141d583b2a17a6c 100644 --- a/substrate/frame/tips/src/weights.rs +++ b/substrate/frame/tips/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_tips +//! Autogenerated weights for `pallet_tips` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/tips/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/tips/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_tips. +/// Weight functions needed for `pallet_tips`. pub trait WeightInfo { fn report_awesome(r: u32, ) -> Weight; fn retract_tip() -> Weight; @@ -60,220 +59,220 @@ pub trait WeightInfo { fn slash_tip(t: u32, ) -> Weight; } -/// Weights for pallet_tips using the Substrate node and recommended hardware. +/// Weights for `pallet_tips` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Tips Reasons (r:1 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Reasons` (r:1 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 300]`. fn report_awesome(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `3469` - // Minimum execution time: 29_576_000 picoseconds. - Weight::from_parts(30_722_650, 3469) - // Standard Error: 192 - .saturating_add(Weight::from_parts(2_601, 0).saturating_mul(r.into())) + // Minimum execution time: 25_145_000 picoseconds. + Weight::from_parts(26_085_800, 3469) + // Standard Error: 216 + .saturating_add(Weight::from_parts(5_121, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Reasons (r:0 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Reasons` (r:0 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) fn retract_tip() -> Weight { // Proof Size summary in bytes: // Measured: `221` // Estimated: `3686` - // Minimum execution time: 28_522_000 picoseconds. - Weight::from_parts(29_323_000, 3686) + // Minimum execution time: 25_302_000 picoseconds. + Weight::from_parts(26_229_000, 3686) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Tips Reasons (r:1 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Tips (r:0 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Reasons` (r:1 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Tips` (r:0 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. fn tip_new(r: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `526 + t * (64 ±0)` // Estimated: `3991 + t * (64 ±0)` - // Minimum execution time: 19_650_000 picoseconds. - Weight::from_parts(19_837_982, 3991) - // Standard Error: 151 - .saturating_add(Weight::from_parts(1_746, 0).saturating_mul(r.into())) - // Standard Error: 3_588 - .saturating_add(Weight::from_parts(102_359, 0).saturating_mul(t.into())) + // Minimum execution time: 16_600_000 picoseconds. + Weight::from_parts(17_071_638, 3991) + // Standard Error: 131 + .saturating_add(Weight::from_parts(1_138, 0).saturating_mul(r.into())) + // Standard Error: 3_125 + .saturating_add(Weight::from_parts(82_996, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(t.into())) } - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[1, 13]`. fn tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `747 + t * (112 ±0)` // Estimated: `4212 + t * (112 ±0)` - // Minimum execution time: 15_641_000 picoseconds. - Weight::from_parts(15_745_460, 4212) - // Standard Error: 5_106 - .saturating_add(Weight::from_parts(229_475, 0).saturating_mul(t.into())) + // Minimum execution time: 13_972_000 picoseconds. + Weight::from_parts(14_269_356, 4212) + // Standard Error: 4_695 + .saturating_add(Weight::from_parts(205_433, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (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: Tips Reasons (r:0 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`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: `Tips::Reasons` (r:0 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[1, 13]`. fn close_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `786 + t * (112 ±0)` // Estimated: `4242 + t * (112 ±0)` - // Minimum execution time: 62_059_000 picoseconds. - Weight::from_parts(64_604_554, 4242) - // Standard Error: 11_818 - .saturating_add(Weight::from_parts(116_297, 0).saturating_mul(t.into())) + // Minimum execution time: 51_878_000 picoseconds. + Weight::from_parts(54_513_477, 4242) + // Standard Error: 12_281 + .saturating_add(Weight::from_parts(126_605, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Reasons (r:0 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Reasons` (r:0 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[1, 13]`. fn slash_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `269` // Estimated: `3734` - // Minimum execution time: 14_133_000 picoseconds. - Weight::from_parts(14_957_547, 3734) - // Standard Error: 2_765 - .saturating_add(Weight::from_parts(22_138, 0).saturating_mul(t.into())) + // Minimum execution time: 11_800_000 picoseconds. + Weight::from_parts(12_520_984, 3734) + // Standard Error: 2_360 + .saturating_add(Weight::from_parts(28_777, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Tips Reasons (r:1 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Reasons` (r:1 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 300]`. fn report_awesome(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `4` // Estimated: `3469` - // Minimum execution time: 29_576_000 picoseconds. - Weight::from_parts(30_722_650, 3469) - // Standard Error: 192 - .saturating_add(Weight::from_parts(2_601, 0).saturating_mul(r.into())) + // Minimum execution time: 25_145_000 picoseconds. + Weight::from_parts(26_085_800, 3469) + // Standard Error: 216 + .saturating_add(Weight::from_parts(5_121, 0).saturating_mul(r.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Reasons (r:0 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Reasons` (r:0 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) fn retract_tip() -> Weight { // Proof Size summary in bytes: // Measured: `221` // Estimated: `3686` - // Minimum execution time: 28_522_000 picoseconds. - Weight::from_parts(29_323_000, 3686) + // Minimum execution time: 25_302_000 picoseconds. + Weight::from_parts(26_229_000, 3686) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Tips Reasons (r:1 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Tips (r:0 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Reasons` (r:1 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Tips` (r:0 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `r` is `[0, 300]`. /// The range of component `t` is `[1, 13]`. fn tip_new(r: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `526 + t * (64 ±0)` // Estimated: `3991 + t * (64 ±0)` - // Minimum execution time: 19_650_000 picoseconds. - Weight::from_parts(19_837_982, 3991) - // Standard Error: 151 - .saturating_add(Weight::from_parts(1_746, 0).saturating_mul(r.into())) - // Standard Error: 3_588 - .saturating_add(Weight::from_parts(102_359, 0).saturating_mul(t.into())) + // Minimum execution time: 16_600_000 picoseconds. + Weight::from_parts(17_071_638, 3991) + // Standard Error: 131 + .saturating_add(Weight::from_parts(1_138, 0).saturating_mul(r.into())) + // Standard Error: 3_125 + .saturating_add(Weight::from_parts(82_996, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(t.into())) } - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[1, 13]`. fn tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `747 + t * (112 ±0)` // Estimated: `4212 + t * (112 ±0)` - // Minimum execution time: 15_641_000 picoseconds. - Weight::from_parts(15_745_460, 4212) - // Standard Error: 5_106 - .saturating_add(Weight::from_parts(229_475, 0).saturating_mul(t.into())) + // Minimum execution time: 13_972_000 picoseconds. + Weight::from_parts(14_269_356, 4212) + // Standard Error: 4_695 + .saturating_add(Weight::from_parts(205_433, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) - /// Storage: Elections Members (r:1 w:0) - /// Proof Skipped: Elections Members (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: Tips Reasons (r:0 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Elections::Members` (r:1 w:0) + /// Proof: `Elections::Members` (`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: `Tips::Reasons` (r:0 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[1, 13]`. fn close_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `786 + t * (112 ±0)` // Estimated: `4242 + t * (112 ±0)` - // Minimum execution time: 62_059_000 picoseconds. - Weight::from_parts(64_604_554, 4242) - // Standard Error: 11_818 - .saturating_add(Weight::from_parts(116_297, 0).saturating_mul(t.into())) + // Minimum execution time: 51_878_000 picoseconds. + Weight::from_parts(54_513_477, 4242) + // Standard Error: 12_281 + .saturating_add(Weight::from_parts(126_605, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 112).saturating_mul(t.into())) } - /// Storage: Tips Tips (r:1 w:1) - /// Proof Skipped: Tips Tips (max_values: None, max_size: None, mode: Measured) - /// Storage: Tips Reasons (r:0 w:1) - /// Proof Skipped: Tips Reasons (max_values: None, max_size: None, mode: Measured) + /// Storage: `Tips::Tips` (r:1 w:1) + /// Proof: `Tips::Tips` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Tips::Reasons` (r:0 w:1) + /// Proof: `Tips::Reasons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[1, 13]`. fn slash_tip(t: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `269` // Estimated: `3734` - // Minimum execution time: 14_133_000 picoseconds. - Weight::from_parts(14_957_547, 3734) - // Standard Error: 2_765 - .saturating_add(Weight::from_parts(22_138, 0).saturating_mul(t.into())) + // Minimum execution time: 11_800_000 picoseconds. + Weight::from_parts(12_520_984, 3734) + // Standard Error: 2_360 + .saturating_add(Weight::from_parts(28_777, 0).saturating_mul(t.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/substrate/frame/transaction-payment/Cargo.toml b/substrate/frame/transaction-payment/Cargo.toml index d52e8e11c82927d459bb8cdbc963f7046e288c7a..66e0bef1436f1a197a5d450680c88a72ddd885e0 100644 --- a/substrate/frame/transaction-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-transaction-payment" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -20,7 +20,8 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", ] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } +frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } @@ -29,13 +30,14 @@ sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } pallet-balances = { path = "../balances" } [features] default = ["std"] std = [ "codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "pallet-balances/std", @@ -46,6 +48,13 @@ std = [ "sp-runtime/std", "sp-std/std", ] +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", diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml b/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml index 0bfe37a5267924b414aa840d0e6b1fca254c0113..528f29e8e4ae7738ea9d72ed8dcd54a9c6f659b0 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-asset-conversion-tx-payment" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,6 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] # Substrate dependencies sp-runtime = { path = "../../../primitives/runtime", default-features = false } sp-std = { path = "../../../primitives/std", default-features = false } +frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true } frame-support = { path = "../../support", default-features = false } frame-system = { path = "../../system", default-features = false } pallet-asset-conversion = { path = "../../asset-conversion", default-features = false } @@ -37,6 +38,7 @@ pallet-balances = { path = "../../balances" } default = ["std"] std = [ "codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "pallet-asset-conversion/std", @@ -50,6 +52,16 @@ std = [ "sp-std/std", "sp-storage/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md b/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md index eccba773673e690a6d415a4c61d267ba75a1c12e..fcd1527526e98ecf45c7979bb40a2d59aad93452 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md @@ -16,6 +16,6 @@ asset. ### Integration This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means you should include both pallets in your `construct_runtime` macro, but only include this -pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]). +pallet's [`TransactionExtension`] ([`ChargeAssetTxPayment`]). License: Apache-2.0 diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/benchmarking.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..0bffb991e4a3b53eac2c385f6e901eeef0c6800c --- /dev/null +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/benchmarking.rs @@ -0,0 +1,125 @@ +// 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 Asset Conversion Tx Payment Pallet's transaction extension + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::{ + dispatch::{DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, +}; +use frame_system::RawOrigin; +use sp_runtime::traits::{AsSystemOriginSigner, DispatchTransaction, Dispatchable}; + +#[benchmarks(where + T::RuntimeCall: Dispatchable, + AssetBalanceOf: Send + Sync, + BalanceOf: Send + + Sync + + From + + Into> + + Into> + + From>, + ChargeAssetIdOf: Send + Sync + Default, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn charge_asset_tx_payment_zero() { + let caller: T::AccountId = whitelisted_caller(); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(0u64.into(), None); + let inner = frame_system::Call::remark { remark: vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + weight: Weight::zero(), + class: DispatchClass::Normal, + pays_fee: Pays::No, + }; + let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Pays::No }; + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_native() { + let caller: T::AccountId = whitelisted_caller(); + let (fun_asset_id, _) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool(fun_asset_id, caller.clone()); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(10u64.into(), None); + let inner = frame_system::Call::remark { remark: vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + weight: Weight::from_parts(10, 0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_asset() { + let caller: T::AccountId = whitelisted_caller(); + let (fun_asset_id, asset_id) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool(fun_asset_id, caller.clone()); + + let tip = 10u64.into(); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(tip, Some(asset_id)); + let inner = frame_system::Call::remark { remark: vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + weight: Weight::from_parts(10, 0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, 0, |_| Ok( + post_info + )) + .unwrap() + .is_ok()); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs index ed0ed56e6e074b49db7abf6d43ddb9305d7fb616..8ef7c2ba38e27f8b50c3489504db8ba195b88cf0 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs @@ -20,8 +20,8 @@ //! //! ## Overview //! -//! This pallet provides a `SignedExtension` with an optional `AssetId` that specifies the asset -//! to be used for payment (defaulting to the native token on `None`). It expects an +//! This pallet provides a `TransactionExtension` with an optional `AssetId` that specifies the +//! asset to be used for payment (defaulting to the native token on `None`). It expects an //! [`OnChargeAssetTransaction`] implementation analogous to [`pallet-transaction-payment`]. The //! included [`AssetConversionAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the //! fee amount by converting the fee calculated by [`pallet-transaction-payment`] in the native @@ -31,7 +31,7 @@ //! //! This pallet does not have any dispatchable calls or storage. It wraps FRAME's Transaction //! Payment pallet and functions as a replacement. This means you should include both pallets in -//! your `construct_runtime` macro, but only include this pallet's [`SignedExtension`] +//! your `construct_runtime` macro, but only include this pallet's [`TransactionExtension`] //! ([`ChargeAssetTxPayment`]). //! //! ## Terminology @@ -53,23 +53,29 @@ use frame_support::{ }, DefaultNoBound, }; -use pallet_transaction_payment::OnChargeTransaction; +use pallet_transaction_payment::{ChargeTransactionPayment, OnChargeTransaction}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, + TransactionExtension, TransactionExtensionBase, ValidateResult, Zero, }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, }; #[cfg(test)] mod mock; #[cfg(test)] mod tests; +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod payment; -use frame_support::traits::tokens::AssetId; +use frame_support::{pallet_prelude::Weight, traits::tokens::AssetId}; pub use payment::*; +pub use weights::WeightInfo; /// Type aliases used for interaction with `OnChargeTransaction`. pub(crate) type OnChargeTransactionOf = @@ -125,11 +131,30 @@ pub mod pallet { type Fungibles: Balanced; /// The actual transaction charging logic that charges the fees. type OnChargeAssetTransaction: OnChargeAssetTransaction; + /// The weight information of this pallet. + type WeightInfo: WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + /// Benchmark helper + type BenchmarkHelper: BenchmarkHelperTrait< + Self::AccountId, + <::Fungibles as Inspect>::AssetId, + <::OnChargeAssetTransaction as OnChargeAssetTransaction>::AssetId, + >; } #[pallet::pallet] pub struct Pallet(_); + #[cfg(feature = "runtime-benchmarks")] + /// Helper trait to benchmark the `ChargeAssetTxPayment` transaction extension. + pub trait BenchmarkHelperTrait { + /// Returns the `AssetId` to be used in the liquidity pool by the benchmarking code. + fn create_asset_id_parameter(id: u32) -> (FunAssetIdParameter, AssetIdParameter); + /// Create a liquidity pool for a given asset and sufficiently endow accounts to benchmark + /// the extension. + fn setup_balances_and_pool(asset_id: FunAssetIdParameter, account: AccountId); + } + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -179,9 +204,8 @@ where who: &T::AccountId, call: &T::RuntimeCall, info: &DispatchInfoOf, - len: usize, + fee: BalanceOf, ) -> Result<(BalanceOf, InitialPayment), TransactionValidityError> { - let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); if fee.is_zero() { Ok((fee, InitialPayment::Nothing)) @@ -225,9 +249,8 @@ impl sp_std::fmt::Debug for ChargeAssetTxPayment { } } -impl SignedExtension for ChargeAssetTxPayment +impl TransactionExtensionBase for ChargeAssetTxPayment where - T::RuntimeCall: Dispatchable, AssetBalanceOf: Send + Sync, BalanceOf: Send + Sync @@ -238,109 +261,145 @@ where ChargeAssetIdOf: Send + Sync, { const IDENTIFIER: &'static str = "ChargeAssetTxPayment"; - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); + type Implicit = (); + + fn weight(&self) -> Weight { + if self.asset_id.is_some() { + ::WeightInfo::charge_asset_tx_payment_asset() + } else { + ::WeightInfo::charge_asset_tx_payment_native() + } + } +} + +impl TransactionExtension for ChargeAssetTxPayment +where + T::RuntimeCall: Dispatchable, + AssetBalanceOf: Send + Sync, + BalanceOf: Send + + Sync + + From + + Into> + + Into> + + From>, + ChargeAssetIdOf: Send + Sync, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, +{ + type Val = ( + // tip + BalanceOf, + // who paid the fee + T::AccountId, + // transaction fee + BalanceOf, + ); type Pre = ( // tip BalanceOf, // who paid the fee - Self::AccountId, + T::AccountId, // imbalance resulting from withdrawing the fee InitialPayment, // asset_id for the transaction payment Option>, ); - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { - use pallet_transaction_payment::ChargeTransactionPayment; - let (fee, _) = self.withdraw_fee(who, call, info, len)?; + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + // Non-mutating call of `compute_fee` to calculate the fee used in the transaction priority. + let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); let priority = ChargeTransactionPayment::::get_priority(info, len, self.tip, fee); - Ok(ValidTransaction { priority, ..Default::default() }) + let validity = ValidTransaction { priority, ..Default::default() }; + let val = (self.tip, who.clone(), fee); + Ok((validity, val, origin)) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + val: Self::Val, + _origin: &::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + _len: usize, + _context: &Context, ) -> Result { - let (_fee, initial_payment) = self.withdraw_fee(who, call, info, len)?; - Ok((self.tip, who.clone(), initial_payment, self.asset_id)) + let (tip, who, fee) = val; + // Mutating call of `withdraw_fee` to actually charge for the transaction. + let (_fee, initial_payment) = self.withdraw_fee(&who, call, info, fee)?; + Ok((tip, who, initial_payment, self.asset_id.clone())) } fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, result: &DispatchResult, + _context: &Context, ) -> Result<(), TransactionValidityError> { - if let Some((tip, who, initial_payment, asset_id)) = pre { - match initial_payment { - InitialPayment::Native(already_withdrawn) => { - debug_assert!( - asset_id.is_none(), - "For that payment type the `asset_id` should be None" - ); - pallet_transaction_payment::ChargeTransactionPayment::::post_dispatch( - Some((tip, who, already_withdrawn)), + let (tip, who, initial_payment, asset_id) = pre; + match initial_payment { + InitialPayment::Native(already_withdrawn) => { + debug_assert!( + asset_id.is_none(), + "For that payment type the `asset_id` should be None" + ); + pallet_transaction_payment::ChargeTransactionPayment::::post_dispatch( + (tip, who, already_withdrawn), + info, + post_info, + len, + result, + &(), + )?; + }, + InitialPayment::Asset(already_withdrawn) => { + debug_assert!( + asset_id.is_some(), + "For that payment type the `asset_id` should be set" + ); + let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( + len as u32, info, post_info, tip, + ); + + if let Some(asset_id) = asset_id { + let (used_for_fee, received_exchanged, asset_consumed) = already_withdrawn; + let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee( + &who, info, post_info, - len, - result, + actual_fee.into(), + tip.into(), + used_for_fee.into(), + received_exchanged.into(), + asset_id.clone(), + asset_consumed.into(), )?; - }, - InitialPayment::Asset(already_withdrawn) => { - debug_assert!( - asset_id.is_some(), - "For that payment type the `asset_id` should be set" - ); - let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( - len as u32, info, post_info, tip, - ); - - if let Some(asset_id) = asset_id { - let (used_for_fee, received_exchanged, asset_consumed) = already_withdrawn; - let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee( - &who, - info, - post_info, - actual_fee.into(), - tip.into(), - used_for_fee.into(), - received_exchanged.into(), - asset_id.clone(), - asset_consumed.into(), - )?; - Pallet::::deposit_event(Event::::AssetTxFeePaid { - who, - actual_fee: converted_fee, - tip, - asset_id, - }); - } - }, - InitialPayment::Nothing => { - // `actual_fee` should be zero here for any signed extrinsic. It would be - // non-zero here in case of unsigned extrinsics as they don't pay fees but - // `compute_actual_fee` is not aware of them. In both cases it's fine to just - // move ahead without adjusting the fee, though, so we do nothing. - debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); - }, - } + Pallet::::deposit_event(Event::::AssetTxFeePaid { + who, + actual_fee: converted_fee, + tip, + asset_id, + }); + } + }, + InitialPayment::Nothing => { + // `actual_fee` should be zero here for any signed extrinsic. It would be + // non-zero here in case of unsigned extrinsics as they don't pay fees but + // `compute_actual_fee` is not aware of them. In both cases it's fine to just + // move ahead without adjusting the fee, though, so we do nothing. + debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); + }, } Ok(()) diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index 52ff3eb990575ff92f44738f3479728039c39bfd..853753d41cfb16b4b84d8ff4317a89ca32fbf309 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -129,7 +129,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl WeightToFeeT for WeightToFee { @@ -169,12 +168,12 @@ impl OnUnbalanced> for DealWithFees } } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig as pallet_transaction_payment::DefaultConfig)] impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter; type WeightToFee = WeightToFee; type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; } @@ -270,4 +269,72 @@ impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type Fungibles = Assets; type OnChargeAssetTransaction = AssetConversionAdapter; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = Helper; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + let base_weight = 5; + let balance_factor = 100; + crate::tests::ExtBuilder::default() + .balance_factor(balance_factor) + .base_weight(Weight::from_parts(base_weight, 0)) + .build() +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct Helper; + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelperTrait for Helper { + fn create_asset_id_parameter(id: u32) -> (u32, u32) { + (id, id) + } + + fn setup_balances_and_pool(asset_id: u32, account: u64) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + use sp_runtime::traits::StaticLookup; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + 42, /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = 12; + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), lp_provider, u64::MAX / 2)); + let lp_provider_account = ::Lookup::unlookup(lp_provider); + assert_ok!(Assets::mint_into(asset_id.into(), &lp_provider_account, u64::MAX / 2)); + + let token_1 = Box::new(NativeOrWithId::Native); + let token_2 = Box::new(NativeOrWithId::WithId(asset_id)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider), + token_1.clone(), + token_2.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider), + token_1, + token_2, + (u32::MAX / 8).into(), // 1 desired + u32::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider_account, + )); + + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&account, u32::MAX.into()); + + let beneficiary = ::Lookup::unlookup(account); + let balance = 1000; + + assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); + assert_eq!(Assets::balance(asset_id, account), balance); + } } diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs index 62faed269d377cc1dc2b75091d79210401cd0a26..619077c0a5ef42e47e4f445ce1cd1623172a2580 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs @@ -28,7 +28,10 @@ use frame_support::{ use frame_system as system; use mock::{ExtrinsicBaseWeight, *}; use pallet_balances::Call as BalancesCall; -use sp_runtime::{traits::StaticLookup, BuildStorage}; +use sp_runtime::{ + traits::{DispatchTransaction, StaticLookup}, + BuildStorage, +}; const CALL: &::RuntimeCall = &RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); @@ -160,33 +163,35 @@ fn transaction_payment_in_native_possible() { .build() .execute_with(|| { let len = 10; - let pre = ChargeAssetTxPayment::::from(0, None) - .pre_dispatch(&1, CALL, &info_from_weight(WEIGHT_5), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, None) + .validate_and_prepare(Some(1).into(), CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); let initial_balance = 10 * balance_factor; assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(WEIGHT_5), &default_post_info(), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); - let pre = ChargeAssetTxPayment::::from(5 /* tipped */, None) - .pre_dispatch(&2, CALL, &info_from_weight(WEIGHT_100), len) + let (pre, _) = ChargeAssetTxPayment::::from(5 /* tipped */, None) + .validate_and_prepare(Some(2).into(), CALL, &info_from_weight(WEIGHT_100), len) .unwrap(); let initial_balance_for_2 = 20 * balance_factor; assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 5); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(WEIGHT_100), &post_info_from_weight(WEIGHT_50), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 50 - 5); }); @@ -237,8 +242,8 @@ fn transaction_payment_in_asset_possible() { let fee_in_asset = input_quote.unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -247,11 +252,12 @@ fn transaction_payment_in_asset_possible() { assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(WEIGHT_5), // estimated tx weight &default_post_info(), // weight actually used == estimated len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); @@ -289,12 +295,8 @@ fn transaction_payment_in_asset_fails_if_no_pool_for_that_asset() { assert_eq!(Assets::balance(asset_id, caller), balance); let len = 10; - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)).pre_dispatch( - &caller, - CALL, - &info_from_weight(WEIGHT_5), - len, - ); + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len); // As there is no pool in the dex set up for this asset, conversion should fail. assert!(pre.is_err()); @@ -344,8 +346,8 @@ fn transaction_payment_without_fee() { assert_eq!(input_quote, Some(201)); let fee_in_asset = input_quote.unwrap(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); // assert that native balance is not used @@ -363,11 +365,12 @@ fn transaction_payment_without_fee() { assert_eq!(refund, 199); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(WEIGHT_5), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), + &() )); // caller should get refunded @@ -419,8 +422,8 @@ fn asset_transaction_payment_with_tip_and_refund() { assert_eq!(input_quote, Some(1206)); let fee_in_asset = input_quote.unwrap(); - let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len) + let (pre, _) = ChargeAssetTxPayment::::from(tip, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_100), len) .unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); @@ -435,11 +438,12 @@ fn asset_transaction_payment_with_tip_and_refund() { .unwrap(); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(WEIGHT_100), &post_info_from_weight(WEIGHT_50), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(TipUnbalancedAmount::get(), tip); @@ -500,8 +504,8 @@ fn payment_from_account_with_only_assets() { .unwrap(); assert_eq!(fee_in_asset, 301); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); assert_eq!(Balances::free_balance(caller), ed); // check that fee was charged in the given asset @@ -516,11 +520,12 @@ fn payment_from_account_with_only_assets() { .unwrap(); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(WEIGHT_5), &default_post_info(), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset + refund); assert_eq!(Balances::free_balance(caller), 0); @@ -565,18 +570,19 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { // there will be no conversion when the fee is zero { - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies there are no fees assert_eq!(Assets::balance(asset_id, caller), balance); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance); } @@ -591,17 +597,23 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { ) .unwrap(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); }); @@ -641,8 +653,8 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { // calculated fee is greater than 0 assert!(fee > 0); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies no pre-dispatch fees @@ -658,62 +670,12 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the // initial fee) assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::Yes), len, - &Ok(()) - )); - assert_eq!(Assets::balance(asset_id, caller), balance); - }); -} - -#[test] -fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { - let base_weight = 1; - ExtBuilder::default() - .balance_factor(100) - .base_weight(Weight::from_parts(base_weight, 0)) - .build() - .execute_with(|| { - // create the asset - let asset_id = 1; - let min_balance = 100; - assert_ok!(Assets::force_create( - RuntimeOrigin::root(), - asset_id.into(), - 42, /* owner */ - true, /* is_sufficient */ - min_balance - )); - - // mint into the caller account - let caller = 333; - let beneficiary = ::Lookup::unlookup(caller); - let balance = 1000; - - assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); - assert_eq!(Assets::balance(asset_id, caller), balance); - - let weight = 1; - let len = 1; - ChargeAssetTxPayment::::pre_dispatch_unsigned( - CALL, - &info_from_weight(Weight::from_parts(weight, 0)), - len, - ) - .unwrap(); - - assert_eq!(Assets::balance(asset_id, caller), balance); - - // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the - // initial fee) - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - None, - &info_from_weight(Weight::from_parts(weight, 0)), - &post_info_from_pays(Pays::Yes), - len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance); }); diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..f95e49f80730318d4957538ded3812c661009be0 --- /dev/null +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs @@ -0,0 +1,150 @@ +// 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_asset_conversion_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_asset_conversion_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --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_asset_conversion_tx_payment`. +pub trait WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight; + fn charge_asset_tx_payment_native() -> Weight; + fn charge_asset_tx_payment_asset() -> Weight; +} + +/// Weights for `pallet_asset_conversion_tx_payment` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 628_000 picoseconds. + Weight::from_parts(694_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 34_410_000 picoseconds. + Weight::from_parts(35_263_000, 1733) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `888` + // Estimated: `6208` + // Minimum execution time: 112_432_000 picoseconds. + Weight::from_parts(113_992_000, 6208) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 628_000 picoseconds. + Weight::from_parts(694_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 34_410_000 picoseconds. + Weight::from_parts(35_263_000, 1733) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `888` + // Estimated: `6208` + // Minimum execution time: 112_432_000 picoseconds. + Weight::from_parts(113_992_000, 6208) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } +} diff --git a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml index 0cd0cea59e60bc87ce570b3e9b0819f7056e5556..06d9c30792e6f8a93d43ce48a06a9c0d34f5cc55 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-asset-tx-payment" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -30,10 +30,10 @@ frame-benchmarking = { path = "../../benchmarking", default-features = false, op # Other dependencies codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } sp-storage = { path = "../../../primitives/storage", default-features = false } @@ -66,6 +66,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ diff --git a/substrate/frame/transaction-payment/asset-tx-payment/README.md b/substrate/frame/transaction-payment/asset-tx-payment/README.md index fc860347d85fa37fc98890b5d4b2f56722040a8e..933ce13b0ee6f7a23110772a82b51ab15fc7dd2a 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/README.md +++ b/substrate/frame/transaction-payment/asset-tx-payment/README.md @@ -16,6 +16,6 @@ asset. ### Integration This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means you should include both pallets in your `construct_runtime` macro, but only include this -pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]). +pallet's [`TransactionExtension`] ([`ChargeAssetTxPayment`]). License: Apache-2.0 diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/benchmarking.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..17e96e2b02562d226a50800ffbb2109bd66c276e --- /dev/null +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/benchmarking.rs @@ -0,0 +1,123 @@ +// 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 Asset Tx Payment Pallet's transaction extension + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::{ + dispatch::{DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, +}; +use frame_system::RawOrigin; +use sp_runtime::traits::{AsSystemOriginSigner, DispatchTransaction, Dispatchable}; + +#[benchmarks(where + T::RuntimeCall: Dispatchable, + AssetBalanceOf: Send + Sync, + BalanceOf: Send + Sync + From + IsType>, + ChargeAssetIdOf: Send + Sync, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + Credit: IsType>, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn charge_asset_tx_payment_zero() { + let caller: T::AccountId = whitelisted_caller(); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(0u32.into(), None); + let inner = frame_system::Call::remark { remark: vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + weight: Weight::zero(), + class: DispatchClass::Normal, + pays_fee: Pays::No, + }; + let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Pays::No }; + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_native() { + let caller: T::AccountId = whitelisted_caller(); + let (fun_asset_id, _) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool(fun_asset_id, caller.clone()); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(10u32.into(), None); + let inner = frame_system::Call::remark { remark: vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + weight: Weight::from_parts(10, 0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_asset() { + let caller: T::AccountId = whitelisted_caller(); + let (fun_asset_id, asset_id) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool( + fun_asset_id.clone(), + caller.clone(), + ); + let tip = 10u32.into(); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(tip, Some(asset_id)); + let inner = frame_system::Call::remark { remark: vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + weight: Weight::from_parts(10, 0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, 0, |_| Ok( + post_info + )) + .unwrap() + .is_ok()); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs index 753fae747a37ec914abb439fc3829c4caca9a448..991b5f314061639665ecd825ea1ace2ff38745ce 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -31,7 +31,7 @@ //! This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means //! you should include both pallets in your `construct_runtime` macro, but only include this -//! pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]). +//! pallet's [`TransactionExtension`] ([`ChargeAssetTxPayment`]). #![cfg_attr(not(feature = "std"), no_std)] @@ -40,6 +40,7 @@ use sp_std::prelude::*; use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo}, + pallet_prelude::Weight, traits::{ tokens::{ fungibles::{Balanced, Credit, Inspect}, @@ -52,10 +53,11 @@ use frame_support::{ use pallet_transaction_payment::OnChargeTransaction; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, + TransactionExtension, TransactionExtensionBase, Zero, }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, }; #[cfg(test)] @@ -63,8 +65,14 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + mod payment; +pub mod weights; + pub use payment::*; +pub use weights::WeightInfo; /// Type aliases used for interaction with `OnChargeTransaction`. pub(crate) type OnChargeTransactionOf = @@ -120,11 +128,30 @@ pub mod pallet { type Fungibles: Balanced; /// The actual transaction charging logic that charges the fees. type OnChargeAssetTransaction: OnChargeAssetTransaction; + /// The weight information of this pallet. + type WeightInfo: WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + /// Benchmark helper + type BenchmarkHelper: BenchmarkHelperTrait< + Self::AccountId, + <::Fungibles as Inspect>::AssetId, + <::OnChargeAssetTransaction as OnChargeAssetTransaction>::AssetId, + >; } #[pallet::pallet] pub struct Pallet(_); + #[cfg(feature = "runtime-benchmarks")] + /// Helper trait to benchmark the `ChargeAssetTxPayment` transaction extension. + pub trait BenchmarkHelperTrait { + /// Returns the `AssetId` to be used in the liquidity pool by the benchmarking code. + fn create_asset_id_parameter(id: u32) -> (FunAssetIdParameter, AssetIdParameter); + /// Create a liquidity pool for a given asset and sufficiently endow accounts to benchmark + /// the extension. + fn setup_balances_and_pool(asset_id: FunAssetIdParameter, account: AccountId); + } + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -172,9 +199,8 @@ where who: &T::AccountId, call: &T::RuntimeCall, info: &DispatchInfoOf, - len: usize, + fee: BalanceOf, ) -> Result<(BalanceOf, InitialPayment), TransactionValidityError> { - let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); if fee.is_zero() { Ok((fee, InitialPayment::Nothing)) @@ -209,104 +235,139 @@ impl sp_std::fmt::Debug for ChargeAssetTxPayment { } } -impl SignedExtension for ChargeAssetTxPayment +impl TransactionExtensionBase for ChargeAssetTxPayment where - T::RuntimeCall: Dispatchable, AssetBalanceOf: Send + Sync, BalanceOf: Send + Sync + From + IsType>, ChargeAssetIdOf: Send + Sync, Credit: IsType>, { const IDENTIFIER: &'static str = "ChargeAssetTxPayment"; - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); + type Implicit = (); + + fn weight(&self) -> Weight { + if self.asset_id.is_some() { + ::WeightInfo::charge_asset_tx_payment_asset() + } else { + ::WeightInfo::charge_asset_tx_payment_native() + } + } +} + +impl TransactionExtension for ChargeAssetTxPayment +where + T::RuntimeCall: Dispatchable, + AssetBalanceOf: Send + Sync, + BalanceOf: Send + Sync + From + IsType>, + ChargeAssetIdOf: Send + Sync, + Credit: IsType>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, +{ + type Val = ( + // tip + BalanceOf, + // who paid the fee + T::AccountId, + // transaction fee + BalanceOf, + ); type Pre = ( // tip BalanceOf, // who paid the fee - Self::AccountId, + T::AccountId, // imbalance resulting from withdrawing the fee InitialPayment, // asset_id for the transaction payment Option>, ); - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { use pallet_transaction_payment::ChargeTransactionPayment; - let (fee, _) = self.withdraw_fee(who, call, info, len)?; + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + // Non-mutating call of `compute_fee` to calculate the fee used in the transaction priority. + let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); let priority = ChargeTransactionPayment::::get_priority(info, len, self.tip, fee); - Ok(ValidTransaction { priority, ..Default::default() }) + let val = (self.tip, who.clone(), fee); + let validity = ValidTransaction { priority, ..Default::default() }; + Ok((validity, val, origin)) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + val: Self::Val, + _origin: &::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + _len: usize, + _context: &Context, ) -> Result { - let (_fee, initial_payment) = self.withdraw_fee(who, call, info, len)?; - Ok((self.tip, who.clone(), initial_payment, self.asset_id)) + let (tip, who, fee) = val; + // Mutating call of `withdraw_fee` to actually charge for the transaction. + let (_fee, initial_payment) = self.withdraw_fee(&who, call, info, fee)?; + Ok((tip, who, initial_payment, self.asset_id)) } fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, result: &DispatchResult, + _context: &Context, ) -> Result<(), TransactionValidityError> { - if let Some((tip, who, initial_payment, asset_id)) = pre { - match initial_payment { - InitialPayment::Native(already_withdrawn) => { - pallet_transaction_payment::ChargeTransactionPayment::::post_dispatch( - Some((tip, who, already_withdrawn)), + let (tip, who, initial_payment, asset_id) = pre; + match initial_payment { + InitialPayment::Native(already_withdrawn) => { + pallet_transaction_payment::ChargeTransactionPayment::::post_dispatch( + (tip, who, already_withdrawn), + info, + post_info, + len, + result, + &(), + )?; + }, + InitialPayment::Asset(already_withdrawn) => { + let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( + len as u32, info, post_info, tip, + ); + + let (converted_fee, converted_tip) = + T::OnChargeAssetTransaction::correct_and_deposit_fee( + &who, info, post_info, - len, - result, + actual_fee.into(), + tip.into(), + already_withdrawn.into(), )?; - }, - InitialPayment::Asset(already_withdrawn) => { - let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( - len as u32, info, post_info, tip, - ); - - let (converted_fee, converted_tip) = - T::OnChargeAssetTransaction::correct_and_deposit_fee( - &who, - info, - post_info, - actual_fee.into(), - tip.into(), - already_withdrawn.into(), - )?; - Pallet::::deposit_event(Event::::AssetTxFeePaid { - who, - actual_fee: converted_fee, - tip: converted_tip, - asset_id, - }); - }, - InitialPayment::Nothing => { - // `actual_fee` should be zero here for any signed extrinsic. It would be - // non-zero here in case of unsigned extrinsics as they don't pay fees but - // `compute_actual_fee` is not aware of them. In both cases it's fine to just - // move ahead without adjusting the fee, though, so we do nothing. - debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); - }, - } + Pallet::::deposit_event(Event::::AssetTxFeePaid { + who, + actual_fee: converted_fee, + tip: converted_tip, + asset_id, + }); + }, + InitialPayment::Nothing => { + // `actual_fee` should be zero here for any signed extrinsic. It would be + // non-zero here in case of unsigned extrinsics as they don't pay fees but + // `compute_actual_fee` is not aware of them. In both cases it's fine to just + // move ahead without adjusting the fee, though, so we do nothing. + debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); + }, } Ok(()) diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs index 03142d6ae3766eeb00b297ed369b6fccb6352a2c..bb484795e82598f52f518edb15dcf26443d9f118 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -116,7 +116,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl WeightToFeeT for WeightToFee { @@ -137,12 +136,12 @@ impl WeightToFeeT for TransactionByteFee { } } +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig as pallet_transaction_payment::DefaultConfig)] impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter; type WeightToFee = WeightToFee; type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; } @@ -206,4 +205,56 @@ impl Config for Runtime { pallet_assets::BalanceToAssetBalance, CreditToBlockAuthor, >; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = Helper; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + let base_weight = 5; + let balance_factor = 100; + crate::tests::ExtBuilder::default() + .balance_factor(balance_factor) + .base_weight(Weight::from_parts(base_weight, 0)) + .build() +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct Helper; + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelperTrait for Helper { + fn create_asset_id_parameter(id: u32) -> (u32, u32) { + (id.into(), id.into()) + } + + fn setup_balances_and_pool(asset_id: u32, account: u64) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + use sp_runtime::traits::StaticLookup; + let min_balance = 1; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + 42, /* owner */ + true, /* is_sufficient */ + min_balance + )); + + // mint into the caller account + let caller = 2; + let beneficiary = ::Lookup::unlookup(caller); + let balance = 1000; + assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); + assert_eq!(Assets::balance(asset_id, caller), balance); + + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&account, u32::MAX.into()); + + let beneficiary = ::Lookup::unlookup(account); + let balance = 1000; + + assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); + assert_eq!(Assets::balance(asset_id, account), balance); + } } diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs index 8df98ceda9971565788576ee839792f6d58da2b0..42b8f2cf5fabb8b74aa0a99ac2b67fefa656728a 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -25,7 +25,10 @@ use frame_support::{ use frame_system as system; use mock::{ExtrinsicBaseWeight, *}; use pallet_balances::Call as BalancesCall; -use sp_runtime::{traits::StaticLookup, BuildStorage}; +use sp_runtime::{ + traits::{DispatchTransaction, StaticLookup}, + BuildStorage, +}; const CALL: &::RuntimeCall = &RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); @@ -116,33 +119,45 @@ fn transaction_payment_in_native_possible() { .build() .execute_with(|| { let len = 10; - let pre = ChargeAssetTxPayment::::from(0, None) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(5, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, None) + .validate_and_prepare( + Some(1).into(), + CALL, + &info_from_weight(Weight::from_parts(5, 0)), + len, + ) .unwrap(); let initial_balance = 10 * balance_factor; assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(5, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); - let pre = ChargeAssetTxPayment::::from(5 /* tipped */, None) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(5 /* tipped */, None) + .validate_and_prepare( + Some(2).into(), + CALL, + &info_from_weight(Weight::from_parts(100, 0)), + len, + ) .unwrap(); let initial_balance_for_2 = 20 * balance_factor; assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 5); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(100, 0)), &post_info_from_weight(Weight::from_parts(50, 0)), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 50 - 5); }); @@ -179,8 +194,13 @@ fn transaction_payment_in_asset_possible() { // we convert the from weight to fee based on the ratio between asset min balance and // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -189,11 +209,12 @@ fn transaction_payment_in_asset_possible() { assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), 0); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance - fee); // check that the block author gets rewarded @@ -232,8 +253,13 @@ fn transaction_payment_without_fee() { // we convert the from weight to fee based on the ratio between asset min balance and // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -242,11 +268,12 @@ fn transaction_payment_without_fee() { assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), 0); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(weight, 0)), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), + &() )); // caller should be refunded assert_eq!(Assets::balance(asset_id, caller), balance); @@ -287,18 +314,24 @@ fn asset_transaction_payment_with_tip_and_refund() { // existential deposit let fee_with_tip = (base_weight + weight + len as u64 + tip) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(tip, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_with_tip); let final_weight = 50; assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(weight, 0)), &post_info_from_weight(Weight::from_parts(final_weight, 0)), len, - &Ok(()) + &Ok(()), + &() )); let final_fee = fee_with_tip - (weight - final_weight) * min_balance / ExistentialDeposit::get(); @@ -339,19 +372,25 @@ fn payment_from_account_with_only_assets() { // we convert the from weight to fee based on the ratio between asset min balance and // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); assert_eq!(Balances::free_balance(caller), 0); // check that fee was charged in the given asset assert_eq!(Assets::balance(asset_id, caller), balance - fee); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance - fee); assert_eq!(Balances::free_balance(caller), 0); @@ -372,7 +411,12 @@ fn payment_only_with_existing_sufficient_asset() { let len = 10; // pre_dispatch fails for non-existent asset assert!(ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len + ) .is_err()); // create the non-sufficient asset @@ -386,7 +430,12 @@ fn payment_only_with_existing_sufficient_asset() { )); // pre_dispatch fails for non-sufficient asset assert!(ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len + ) .is_err()); }); } @@ -424,33 +473,40 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { // naive fee calculation would round down to zero assert_eq!(fee, 0); { - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` still implies no fees assert_eq!(Assets::balance(asset_id, caller), balance); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance); } - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); // check that at least one coin was charged in the given asset assert_eq!(Assets::balance(asset_id, caller), balance - 1); assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance - 1); }); @@ -488,8 +544,8 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); // calculated fee is greater than 0 assert!(fee > 0); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies no pre-dispatch fees assert_eq!(Assets::balance(asset_id, caller), balance); @@ -503,60 +559,12 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the // initial fee) assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::Yes), len, - &Ok(()) - )); - assert_eq!(Assets::balance(asset_id, caller), balance); - }); -} - -#[test] -fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { - let base_weight = 1; - ExtBuilder::default() - .balance_factor(100) - .base_weight(Weight::from_parts(base_weight, 0)) - .build() - .execute_with(|| { - // create the asset - let asset_id = 1; - let min_balance = 100; - assert_ok!(Assets::force_create( - RuntimeOrigin::root(), - asset_id.into(), - 42, /* owner */ - true, /* is_sufficient */ - min_balance - )); - - // mint into the caller account - let caller = 333; - let beneficiary = ::Lookup::unlookup(caller); - let balance = 100; - assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); - assert_eq!(Assets::balance(asset_id, caller), balance); - let weight = 1; - let len = 1; - ChargeAssetTxPayment::::pre_dispatch_unsigned( - CALL, - &info_from_weight(Weight::from_parts(weight, 0)), - len, - ) - .unwrap(); - - assert_eq!(Assets::balance(asset_id, caller), balance); - - // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the - // initial fee) - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - None, - &info_from_weight(Weight::from_parts(weight, 0)), - &post_info_from_pays(Pays::Yes), - len, - &Ok(()) + &Ok(()), + &() )); assert_eq!(Assets::balance(asset_id, caller), balance); }); diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..1af1c94177d26b3316d506d1419a3d6cb052d4c2 --- /dev/null +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs @@ -0,0 +1,146 @@ +// 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_asset_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_asset_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --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_asset_tx_payment`. +pub trait WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight; + fn charge_asset_tx_payment_native() -> Weight; + fn charge_asset_tx_payment_asset() -> Weight; +} + +/// Weights for `pallet_asset_tx_payment` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 542_000 picoseconds. + Weight::from_parts(597_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 33_162_000 picoseconds. + Weight::from_parts(34_716_000, 1733) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `747` + // Estimated: `3675` + // Minimum execution time: 44_230_000 picoseconds. + Weight::from_parts(45_297_000, 3675) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 542_000 picoseconds. + Weight::from_parts(597_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 33_162_000 picoseconds. + Weight::from_parts(34_716_000, 1733) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `747` + // Estimated: `3675` + // Minimum execution time: 44_230_000 picoseconds. + Weight::from_parts(45_297_000, 3675) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } +} diff --git a/substrate/frame/transaction-payment/rpc/Cargo.toml b/substrate/frame/transaction-payment/rpc/Cargo.toml index 5a574a944d82f2ba2c0936528749a946b3abbe65..6d7f632af82813050d4c0523dcd61dc27ea512f1 100644 --- a/substrate/frame/transaction-payment/rpc/Cargo.toml +++ b/substrate/frame/transaction-payment/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-transaction-payment-rpc" -version = "4.0.0-dev" +version = "30.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } pallet-transaction-payment-rpc-runtime-api = { path = "runtime-api" } sp-api = { path = "../../../primitives/api" } sp-blockchain = { path = "../../../primitives/blockchain" } diff --git a/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml b/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml index e384fcef692e43217f3675c2304323db3d3ae13d..913dccc05c496807a7698dd3445eaaba5431002a 100644 --- a/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml +++ b/substrate/frame/transaction-payment/rpc/runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-transaction-payment-rpc-runtime-api" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/transaction-payment/rpc/src/lib.rs b/substrate/frame/transaction-payment/rpc/src/lib.rs index 7f8ed4b80267578f81d6043e64ffa64596f7f904..f5323cf852e97df015aaaebac7a36f1d7525397d 100644 --- a/substrate/frame/transaction-payment/rpc/src/lib.rs +++ b/substrate/frame/transaction-payment/rpc/src/lib.rs @@ -21,9 +21,12 @@ use std::{convert::TryInto, sync::Arc}; use codec::{Codec, Decode}; use jsonrpsee::{ - core::{Error as JsonRpseeError, RpcResult}, + core::RpcResult, proc_macros::rpc, - types::error::{CallError, ErrorCode, ErrorObject}, + types::{ + error::{ErrorCode, ErrorObject}, + ErrorObjectOwned, + }, }; use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, InclusionFee, RuntimeDispatchInfo}; use sp_api::ProvideRuntimeApi; @@ -100,19 +103,15 @@ where let encoded_len = encoded_xt.len() as u32; let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::DecodeError.into(), "Unable to query dispatch info.", Some(format!("{:?}", e)), - )) + ) })?; - fn map_err(error: impl ToString, desc: &'static str) -> CallError { - CallError::Custom(ErrorObject::owned( - Error::RuntimeError.into(), - desc, - Some(error.to_string()), - )) + fn map_err(error: impl ToString, desc: &'static str) -> ErrorObjectOwned { + ErrorObject::owned(Error::RuntimeError.into(), desc, Some(error.to_string())) } let res = api @@ -137,27 +136,27 @@ where let encoded_len = encoded_xt.len() as u32; let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::DecodeError.into(), "Unable to query fee details.", Some(format!("{:?}", e)), - )) + ) })?; let fee_details = api.query_fee_details(at_hash, uxt, encoded_len).map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::RuntimeError.into(), "Unable to query fee details.", Some(e.to_string()), - )) + ) })?; let try_into_rpc_balance = |value: Balance| { value.try_into().map_err(|_| { - JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( + ErrorObject::owned( ErrorCode::InvalidParams.code(), format!("{} doesn't fit in NumberOrHex representation", value), None::<()>, - ))) + ) }) }; diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/Cargo.toml b/substrate/frame/transaction-payment/skip-feeless-payment/Cargo.toml index 0e3744626d3f7cbfafd4c8e9661c4ec92e3cab39..8a6ee09f8dd0a4d17566ef573f242e9762cf6b1e 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/skip-feeless-payment/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-skip-feeless-payment" -version = "1.0.0-dev" +version = "3.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs index 6c34c26ce9236dd76ff90224d3ce2d59dbd3157a..9810ea08c790d2ad964d5bf98906d12b76fe8f91 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs @@ -21,7 +21,7 @@ //! //! ## Overview //! -//! It does this by wrapping an existing [`SignedExtension`] implementation (e.g. +//! It does this by wrapping an existing [`TransactionExtension`] implementation (e.g. //! [`pallet-transaction-payment`]) and checking if the dispatchable is feeless before applying the //! wrapped extension. If the dispatchable is indeed feeless, the extension is skipped and a custom //! event is emitted instead. Otherwise, the extension is applied as usual. @@ -31,7 +31,8 @@ //! //! This pallet wraps an existing transaction payment pallet. This means you should both pallets //! in your `construct_runtime` macro and include this pallet's -//! [`SignedExtension`] ([`SkipCheckIfFeeless`]) that would accept the existing one as an argument. +//! [`TransactionExtension`] ([`SkipCheckIfFeeless`]) that would accept the existing one as an +//! argument. #![cfg_attr(not(feature = "std"), no_std)] @@ -42,7 +43,10 @@ use frame_support::{ }; use scale_info::{StaticTypeInfo, TypeInfo}; use sp_runtime::{ - traits::{DispatchInfoOf, PostDispatchInfoOf, SignedExtension}, + traits::{ + DispatchInfoOf, OriginOf, PostDispatchInfoOf, TransactionExtension, + TransactionExtensionBase, ValidateResult, + }, transaction_validity::TransactionValidityError, }; @@ -70,11 +74,11 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// A transaction fee was skipped. - FeeSkipped { who: T::AccountId }, + FeeSkipped { origin: ::PalletsOrigin }, } } -/// A [`SignedExtension`] that skips the wrapped extension if the dispatchable is feeless. +/// A [`TransactionExtension`] that skips the wrapped extension if the dispatchable is feeless. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct SkipCheckIfFeeless(pub S, sp_std::marker::PhantomData); @@ -103,53 +107,96 @@ impl From for SkipCheckIfFeeless { } } -impl> SignedExtension +pub enum Intermediate { + /// The wrapped extension should be applied. + Apply(T), + /// The wrapped extension should be skipped. + Skip(O), +} +use Intermediate::*; + +impl TransactionExtensionBase for SkipCheckIfFeeless -where - S::Call: CheckIfFeeless>, { - type AccountId = T::AccountId; - type Call = S::Call; - type AdditionalSigned = S::AdditionalSigned; - type Pre = (Self::AccountId, Option<::Pre>); // From the outside this extension should be "invisible", because it just extends the wrapped // extension with an extra check in `pre_dispatch` and `post_dispatch`. Thus, we should forward // the identifier of the wrapped extension to let wallets see this extension as it would only be // the wrapped extension itself. const IDENTIFIER: &'static str = S::IDENTIFIER; + type Implicit = S::Implicit; + + fn implicit(&self) -> Result { + self.0.implicit() + } - fn additional_signed(&self) -> Result { - self.0.additional_signed() + fn weight(&self) -> frame_support::weights::Weight { + self.0.weight() } +} - fn pre_dispatch( +impl> + TransactionExtension for SkipCheckIfFeeless +where + T::RuntimeCall: CheckIfFeeless>, +{ + type Val = Intermediate as OriginTrait>::PalletsOrigin>; + type Pre = Intermediate as OriginTrait>::PalletsOrigin>; + + fn validate( + &self, + origin: OriginOf, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + len: usize, + context: &mut Context, + self_implicit: S::Implicit, + inherited_implication: &impl Encode, + ) -> ValidateResult { + if call.is_feeless(&origin) { + Ok((Default::default(), Skip(origin.caller().clone()), origin)) + } else { + let (x, y, z) = self.0.validate( + origin, + call, + info, + len, + context, + self_implicit, + inherited_implication, + )?; + Ok((x, Apply(y), z)) + } + } + + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + val: Self::Val, + origin: &OriginOf, + call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, + context: &Context, ) -> Result { - if call.is_feeless(&::RuntimeOrigin::signed(who.clone())) { - Ok((who.clone(), None)) - } else { - Ok((who.clone(), Some(self.0.pre_dispatch(who, call, info, len)?))) + match val { + Apply(val) => self.0.prepare(val, origin, call, info, len, context).map(Apply), + Skip(origin) => Ok(Skip(origin)), } } fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, result: &DispatchResult, + context: &Context, ) -> Result<(), TransactionValidityError> { - if let Some(pre) = pre { - if let Some(pre) = pre.1 { - S::post_dispatch(Some(pre), info, post_info, len, result)?; - } else { - Pallet::::deposit_event(Event::::FeeSkipped { who: pre.0 }); - } + match pre { + Apply(pre) => S::post_dispatch(pre, info, post_info, len, result, context), + Skip(origin) => { + Pallet::::deposit_event(Event::::FeeSkipped { origin }); + Ok(()) + }, } - Ok(()) } } diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs index 17c4c773997eb591c1f8aac06a607c3e35df8db0..06948de4c3ce972ba0e38eddac6ae3ab23804bbd 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs @@ -18,9 +18,12 @@ use crate as pallet_skip_feeless_payment; use frame_support::{derive_impl, parameter_types}; use frame_system as system; +use sp_runtime::{ + impl_tx_ext_default, + traits::{OriginOf, TransactionExtension}, +}; type Block = frame_system::mocking::MockBlock; -type AccountId = u64; #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Runtime { @@ -38,21 +41,22 @@ parameter_types! { #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)] pub struct DummyExtension; -impl SignedExtension for DummyExtension { - type AccountId = AccountId; - type Call = RuntimeCall; - type AdditionalSigned = (); - type Pre = (); +impl TransactionExtensionBase for DummyExtension { const IDENTIFIER: &'static str = "DummyExtension"; - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - fn pre_dispatch( + type Implicit = (); +} +impl TransactionExtension for DummyExtension { + type Val = (); + type Pre = (); + impl_tx_ext_default!(RuntimeCall; C; validate); + fn prepare( self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, + _val: Self::Val, + _origin: &OriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, _len: usize, + _context: &C, ) -> Result { PreDispatchCount::mutate(|c| *c += 1); Ok(()) diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs index 4b4dd6997418f9c9e39ebe0ab294051a688f2f87..96b4af7e203e0bcbd1773969f2baefdfe24150d6 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs @@ -16,18 +16,19 @@ use super::*; use crate::mock::{pallet_dummy::Call, DummyExtension, PreDispatchCount, Runtime, RuntimeCall}; use frame_support::dispatch::DispatchInfo; +use sp_runtime::traits::DispatchTransaction; #[test] fn skip_feeless_payment_works() { let call = RuntimeCall::DummyPallet(Call::::aux { data: 1 }); SkipCheckIfFeeless::::from(DummyExtension) - .pre_dispatch(&0, &call, &DispatchInfo::default(), 0) + .validate_and_prepare(Some(0).into(), &call, &DispatchInfo::default(), 0) .unwrap(); assert_eq!(PreDispatchCount::get(), 1); let call = RuntimeCall::DummyPallet(Call::::aux { data: 0 }); SkipCheckIfFeeless::::from(DummyExtension) - .pre_dispatch(&0, &call, &DispatchInfo::default(), 0) + .validate_and_prepare(Some(0).into(), &call, &DispatchInfo::default(), 0) .unwrap(); assert_eq!(PreDispatchCount::get(), 1); } diff --git a/substrate/frame/transaction-payment/src/benchmarking.rs b/substrate/frame/transaction-payment/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..d63c5a7d746e4cb5030438a2c9c82b1a307f965e --- /dev/null +++ b/substrate/frame/transaction-payment/src/benchmarking.rs @@ -0,0 +1,81 @@ +// 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 Transaction Payment Pallet's transaction extension + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::{EventRecord, RawOrigin}; +use sp_runtime::traits::{DispatchTransaction, Dispatchable}; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +#[benchmarks(where + T: Config, + T::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + From, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn charge_transaction_payment() { + let caller: T::AccountId = whitelisted_caller(); + >::endow_account( + &caller, + >::minimum_balance() * 1000.into(), + ); + let tip = >::minimum_balance(); + let ext: ChargeTransactionPayment = ChargeTransactionPayment::from(tip); + let inner = frame_system::Call::remark { remark: vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + weight: Weight::from_parts(100, 0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, 10, |_| Ok( + post_info + )) + .unwrap() + .is_ok()); + } + + let actual_fee = Pallet::::compute_actual_fee(10, &info, &post_info, tip); + assert_last_event::( + Event::::TransactionFeePaid { who: caller, actual_fee, tip }.into(), + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index efadfd60bdd30126f8627fccea7c02f4c42c68a3..6b17616bdc7eb1ba22dddb135926b0ab83b5592e 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -62,23 +62,28 @@ pub use payment::*; use sp_runtime::{ traits::{ Convert, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf, SaturatedConversion, - Saturating, SignedExtension, Zero, + Saturating, TransactionExtension, TransactionExtensionBase, Zero, }, transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransaction, + InvalidTransaction, TransactionPriority, TransactionValidityError, ValidTransaction, }, FixedPointNumber, FixedU128, Perbill, Perquintill, RuntimeDebug, }; use sp_std::prelude::*; pub use types::{FeeDetails, InclusionFee, RuntimeDispatchInfo}; +pub use weights::WeightInfo; #[cfg(test)] mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + mod payment; mod types; +pub mod weights; /// Fee multiplier. pub type Multiplier = FixedU128; @@ -335,6 +340,7 @@ pub mod pallet { type RuntimeEvent = (); type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = (); + type WeightInfo = (); } } @@ -387,6 +393,9 @@ pub mod pallet { /// transactions. #[pallet::constant] type OperationalFeeMultiplier: Get; + + /// The weight information of this pallet. + type WeightInfo: WeightInfo; } #[pallet::type_value] @@ -507,11 +516,11 @@ impl Pallet { // a very very little potential gain in the future. let dispatch_info = ::get_dispatch_info(&unchecked_extrinsic); - let partial_fee = if unchecked_extrinsic.is_signed().unwrap_or(false) { - Self::compute_fee(len, &dispatch_info, 0u32.into()) - } else { - // Unsigned extrinsics have no partial fee. + let partial_fee = if unchecked_extrinsic.is_bare() { + // Bare extrinsics have no partial fee. 0u32.into() + } else { + Self::compute_fee(len, &dispatch_info, 0u32.into()) }; let DispatchInfo { weight, class, .. } = dispatch_info; @@ -531,11 +540,11 @@ impl Pallet { let tip = 0u32.into(); - if unchecked_extrinsic.is_signed().unwrap_or(false) { - Self::compute_fee_details(len, &dispatch_info, tip) - } else { - // Unsigned extrinsics have no inclusion fee. + if unchecked_extrinsic.is_bare() { + // Bare extrinsics have no inclusion fee. FeeDetails { inclusion_fee: None, tip } + } else { + Self::compute_fee_details(len, &dispatch_info, tip) } } @@ -714,7 +723,7 @@ where who: &T::AccountId, call: &T::RuntimeCall, info: &DispatchInfoOf, - len: usize, + fee: BalanceOf, ) -> Result< ( BalanceOf, @@ -723,7 +732,6 @@ where TransactionValidityError, > { let tip = self.0; - let fee = Pallet::::compute_fee(len as u32, info, tip); <::OnChargeTransaction as OnChargeTransaction>::withdraw_fee( who, call, info, fee, tip, @@ -731,6 +739,22 @@ where .map(|i| (fee, i)) } + fn can_withdraw_fee( + &self, + who: &T::AccountId, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + len: usize, + ) -> Result, TransactionValidityError> { + let tip = self.0; + let fee = Pallet::::compute_fee(len as u32, info, tip); + + <::OnChargeTransaction as OnChargeTransaction>::can_withdraw_fee( + who, call, info, fee, tip, + )?; + Ok(fee) + } + /// Get an appropriate priority for a transaction with the given `DispatchInfo`, encoded length /// and user-included tip. /// @@ -817,67 +841,93 @@ impl sp_std::fmt::Debug for ChargeTransactionPayment { } } -impl SignedExtension for ChargeTransactionPayment +impl TransactionExtensionBase for ChargeTransactionPayment { + const IDENTIFIER: &'static str = "ChargeTransactionPayment"; + type Implicit = (); + + fn weight(&self) -> Weight { + T::WeightInfo::charge_transaction_payment() + } +} + +impl TransactionExtension + for ChargeTransactionPayment where BalanceOf: Send + Sync + From, T::RuntimeCall: Dispatchable, { - const IDENTIFIER: &'static str = "ChargeTransactionPayment"; - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); + type Val = ( + // tip + BalanceOf, + // who paid the fee + T::AccountId, + // computed fee + BalanceOf, + ); type Pre = ( // tip BalanceOf, - // who paid the fee - this is an option to allow for a Default impl. - Self::AccountId, + // who paid the fee + T::AccountId, // imbalance resulting from withdrawing the fee <::OnChargeTransaction as OnChargeTransaction>::LiquidityInfo, ); - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { - let (final_fee, _) = self.withdraw_fee(who, call, info, len)?; + _context: &mut Context, + _: (), + _implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let who = frame_system::ensure_signed(origin.clone()) + .map_err(|_| InvalidTransaction::BadSigner)?; + let final_fee = self.can_withdraw_fee(&who, call, info, len)?; let tip = self.0; - Ok(ValidTransaction { - priority: Self::get_priority(info, len, tip, final_fee), - ..Default::default() - }) + Ok(( + ValidTransaction { + priority: Self::get_priority(info, len, tip, final_fee), + ..Default::default() + }, + (self.0, who, final_fee), + origin, + )) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + val: Self::Val, + _origin: &::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + _len: usize, + _context: &Context, ) -> Result { - let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?; - Ok((self.0, who.clone(), imbalance)) + let (tip, who, fee) = val; + // Mutating call to `withdraw_fee` to actually charge for the transaction. + let (_final_fee, imbalance) = self.withdraw_fee(&who, call, info, fee)?; + Ok((tip, who, imbalance)) } fn post_dispatch( - maybe_pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + (tip, who, imbalance): Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, _result: &DispatchResult, + _context: &Context, ) -> Result<(), TransactionValidityError> { - if let Some((tip, who, imbalance)) = maybe_pre { - let actual_fee = Pallet::::compute_actual_fee(len as u32, info, post_info, tip); - T::OnChargeTransaction::correct_and_deposit_fee( - &who, info, post_info, actual_fee, tip, imbalance, - )?; - Pallet::::deposit_event(Event::::TransactionFeePaid { who, actual_fee, tip }); - } + let actual_fee = Pallet::::compute_actual_fee(len as u32, info, post_info, tip); + T::OnChargeTransaction::correct_and_deposit_fee( + &who, info, post_info, actual_fee, tip, imbalance, + )?; + Pallet::::deposit_event(Event::::TransactionFeePaid { who, actual_fee, tip }); Ok(()) } } diff --git a/substrate/frame/transaction-payment/src/mock.rs b/substrate/frame/transaction-payment/src/mock.rs index 2f025ae81d2561e7024af7d9b062002effd72c7d..eda234ffcae577f9794b4b310eb2492ff32843b4 100644 --- a/substrate/frame/transaction-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/src/mock.rs @@ -111,7 +111,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl WeightToFeeT for WeightToFee { @@ -158,4 +157,14 @@ impl Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = TransactionByteFee; type FeeMultiplierUpdate = (); + type WeightInfo = (); +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + crate::tests::ExtBuilder::default() + .base_weight(Weight::from_parts(100, 0)) + .byte_fee(10) + .balance_factor(0) + .build() } diff --git a/substrate/frame/transaction-payment/src/payment.rs b/substrate/frame/transaction-payment/src/payment.rs index 22b0ac7c742403ac068b64a75a89e552ef06c470..a13e73b50b0e931579a21995ca466ca6b3de4621 100644 --- a/substrate/frame/transaction-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/src/payment.rs @@ -18,11 +18,11 @@ /// ! Traits and default implementation for paying transaction fees. use crate::Config; +use core::marker::PhantomData; use sp_runtime::{ - traits::{DispatchInfoOf, PostDispatchInfoOf, Saturating, Zero}, + traits::{CheckedSub, DispatchInfoOf, PostDispatchInfoOf, Saturating, Zero}, transaction_validity::InvalidTransaction, }; -use sp_std::marker::PhantomData; use frame_support::{ traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReasons}, @@ -51,6 +51,17 @@ pub trait OnChargeTransaction { tip: Self::Balance, ) -> Result; + /// Check if the predicted fee from the transaction origin can be withdrawn. + /// + /// Note: The `fee` already includes the `tip`. + fn can_withdraw_fee( + who: &T::AccountId, + call: &T::RuntimeCall, + dispatch_info: &DispatchInfoOf, + fee: Self::Balance, + tip: Self::Balance, + ) -> Result<(), TransactionValidityError>; + /// After the transaction was executed the actual fee can be calculated. /// This function should refund any overpaid fees and optionally deposit /// the corrected amount. @@ -64,6 +75,12 @@ pub trait OnChargeTransaction { tip: Self::Balance, already_withdrawn: Self::LiquidityInfo, ) -> Result<(), TransactionValidityError>; + + #[cfg(feature = "runtime-benchmarks")] + fn endow_account(who: &T::AccountId, amount: Self::Balance); + + #[cfg(feature = "runtime-benchmarks")] + fn minimum_balance() -> Self::Balance; } /// Implements the transaction payment for a pallet implementing the `Currency` @@ -121,6 +138,33 @@ where } } + /// Check if the predicted fee from the transaction origin can be withdrawn. + /// + /// Note: The `fee` already includes the `tip`. + fn can_withdraw_fee( + who: &T::AccountId, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + fee: Self::Balance, + tip: Self::Balance, + ) -> Result<(), TransactionValidityError> { + if fee.is_zero() { + return Ok(()) + } + + let withdraw_reason = if tip.is_zero() { + WithdrawReasons::TRANSACTION_PAYMENT + } else { + WithdrawReasons::TRANSACTION_PAYMENT | WithdrawReasons::TIP + }; + + let new_balance = + C::free_balance(who).checked_sub(&fee).ok_or(InvalidTransaction::Payment)?; + C::ensure_can_withdraw(who, fee, withdraw_reason, new_balance) + .map(|_| ()) + .map_err(|_| InvalidTransaction::Payment.into()) + } + /// Hand the fee and the tip over to the `[OnUnbalanced]` implementation. /// Since the predicted fee might have been too high, parts of the fee may /// be refunded. @@ -153,4 +197,14 @@ where } Ok(()) } + + #[cfg(feature = "runtime-benchmarks")] + fn endow_account(who: &T::AccountId, amount: Self::Balance) { + let _ = C::deposit_creating(who, amount); + } + + #[cfg(feature = "runtime-benchmarks")] + fn minimum_balance() -> Self::Balance { + C::minimum_balance() + } } diff --git a/substrate/frame/transaction-payment/src/tests.rs b/substrate/frame/transaction-payment/src/tests.rs index d3a1721ccb9909b2f2a705d9bb9ddc9ebc7cb5ee..7a49f8111e2315de5784101fa7de86d7010b4c32 100644 --- a/substrate/frame/transaction-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/src/tests.rs @@ -21,11 +21,14 @@ use crate as pallet_transaction_payment; use codec::Encode; use sp_runtime::{ - testing::TestXt, traits::One, transaction_validity::InvalidTransaction, BuildStorage, + generic::UncheckedExtrinsic, + traits::{DispatchTransaction, One}, + transaction_validity::InvalidTransaction, + BuildStorage, }; use frame_support::{ - assert_noop, assert_ok, + assert_ok, dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, traits::Currency, weights::Weight, @@ -128,44 +131,37 @@ fn default_post_info() -> PostDispatchInfo { PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } } +type Ext = ChargeTransactionPayment; + #[test] -fn signed_extension_transaction_payment_work() { +fn transaction_extension_transaction_payment_work() { ExtBuilder::default() .balance_factor(10) .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(5, 0)), len) + let info = info_from_weight(Weight::from_parts(5, 0)); + Ext::from(0) + .test_run(Some(1).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); + Ok(default_post_info()) + }) + .unwrap() .unwrap(); assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(5, 0)), - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); assert_eq!(TipUnbalancedAmount::get(), 0); FeeUnbalancedAmount::mutate(|a| *a = 0); - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let info = info_from_weight(Weight::from_parts(100, 0)); + Ext::from(5 /* tipped */) + .test_run(Some(2).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + Ok(post_info_from_weight(Weight::from_parts(50, 0))) + }) + .unwrap() .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(50, 0)), - len, - &Ok(()) - )); assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); assert_eq!(TipUnbalancedAmount::get(), 5); @@ -173,43 +169,37 @@ fn signed_extension_transaction_payment_work() { } #[test] -fn signed_extension_transaction_payment_multiplied_refund_works() { +fn transaction_extension_transaction_payment_multiplied_refund_works() { ExtBuilder::default() .balance_factor(10) .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { - let len = 10; >::put(Multiplier::saturating_from_rational(3, 2)); - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let len = 10; + let origin = Some(2).into(); + let info = info_from_weight(Weight::from_parts(100, 0)); + Ext::from(5 /* tipped */) + .test_run(origin, CALL, &info, len, |_| { + // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); + Ok(post_info_from_weight(Weight::from_parts(50, 0))) + }) + .unwrap() .unwrap(); - // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(50, 0)), - len, - &Ok(()) - )); + // 75 (3/2 of the returned 50 units of weight) is refunded assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); }); } #[test] -fn signed_extension_transaction_payment_is_bounded() { +fn transaction_extension_transaction_payment_is_bounded() { ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { // maximum weight possible - assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( - &1, - CALL, - &info_from_weight(Weight::MAX), - 10 - )); + let info = info_from_weight(Weight::MAX); + assert_ok!(Ext::from(0).validate_and_prepare(Some(1).into(), CALL, &info, 10)); // fee will be proportional to what is the actual maximum weight in the runtime. assert_eq!( Balances::free_balance(&1), @@ -220,7 +210,7 @@ fn signed_extension_transaction_payment_is_bounded() { } #[test] -fn signed_extension_allows_free_transactions() { +fn transaction_extension_allows_free_transactions() { ExtBuilder::default() .base_weight(Weight::from_parts(100, 0)) .balance_factor(0) @@ -232,38 +222,28 @@ fn signed_extension_allows_free_transactions() { let len = 100; // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. - let operational_transaction = DispatchInfo { + let op_tx = DispatchInfo { weight: Weight::from_parts(0, 0), class: DispatchClass::Operational, pays_fee: Pays::No, }; - assert_ok!(ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &operational_transaction, - len - )); + assert_ok!(Ext::from(0).validate_only(Some(1).into(), CALL, &op_tx, len)); // like a InsecureFreeNormal - let free_transaction = DispatchInfo { + let free_tx = DispatchInfo { weight: Weight::from_parts(0, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - assert_noop!( - ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &free_transaction, - len - ), + assert_eq!( + Ext::from(0).validate_only(Some(1).into(), CALL, &free_tx, len).unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Payment), ); }); } #[test] -fn signed_ext_length_fee_is_also_updated_per_congestion() { +fn transaction_ext_length_fee_is_also_updated_per_congestion() { ExtBuilder::default() .base_weight(Weight::from_parts(5, 0)) .balance_factor(10) @@ -272,16 +252,15 @@ fn signed_ext_length_fee_is_also_updated_per_congestion() { // all fees should be x1.5 >::put(Multiplier::saturating_from_rational(3, 2)); let len = 10; - - assert_ok!(ChargeTransactionPayment::::from(10) // tipped - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(3, 0)), len)); + let info = &info_from_weight(Weight::from_parts(3, 0)); + assert_ok!(Ext::from(10).validate_and_prepare(Some(1).into(), CALL, info, len)); assert_eq!( Balances::free_balance(1), 100 // original - - 10 // tip - - 5 // base - - 10 // len - - (3 * 3 / 2) // adjusted weight + - 10 // tip + - 5 // base + - 10 // len + - (3 * 3 / 2) // adjusted weight ); }) } @@ -291,62 +270,62 @@ fn query_info_and_fee_details_works() { let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); let origin = 111111; let extra = (); - let xt = TestXt::new(call.clone(), Some((origin, extra))); + let xt = UncheckedExtrinsic::new_signed(call.clone(), origin, (), extra); let info = xt.get_dispatch_info(); let ext = xt.encode(); let len = ext.len() as u32; - let unsigned_xt = TestXt::<_, ()>::new(call, None); + let unsigned_xt = UncheckedExtrinsic::::new_bare(call); let unsigned_xt_info = unsigned_xt.get_dispatch_info(); ExtBuilder::default() - .base_weight(Weight::from_parts(5, 0)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_info(xt.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_info(unsigned_xt.clone(), len), - RuntimeDispatchInfo { - weight: unsigned_xt_info.weight, - class: unsigned_xt_info.class, - partial_fee: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(xt, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, - len_fee: len as u64, - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 - }), - tip: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(unsigned_xt, len), - FeeDetails { inclusion_fee: None, tip: 0 }, - ); - }); + .base_weight(Weight::from_parts(5, 0)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_info(xt.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_info(unsigned_xt.clone(), len), + RuntimeDispatchInfo { + weight: unsigned_xt_info.weight, + class: unsigned_xt_info.class, + partial_fee: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(xt, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, + len_fee: len as u64, + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 + }), + tip: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(unsigned_xt, len), + FeeDetails { inclusion_fee: None, tip: 0 }, + ); + }); } #[test] @@ -357,39 +336,39 @@ fn query_call_info_and_fee_details_works() { let len = encoded_call.len() as u32; ExtBuilder::default() - .base_weight(Weight::from_parts(5, 0)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_call_info(call.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_call_fee_details(call, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, /* base * weight_fee */ - len_fee: len as u64, /* len * 1 */ - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ - }), - tip: 0, - }, - ); - }); + .base_weight(Weight::from_parts(5, 0)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_call_info(call.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_call_fee_details(call, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, /* base * weight_fee */ + len_fee: len as u64, /* len * 1 */ + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ + }), + tip: 0, + }, + ); + }); } #[test] @@ -526,27 +505,23 @@ fn refund_does_not_recreate_account() { .execute_with(|| { // So events are emitted System::set_block_number(10); - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let info = info_from_weight(Weight::from_parts(100, 0)); + Ext::from(5 /* tipped */) + .test_run(Some(2).into(), CALL, &info, 10, |origin| { + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + // kill the account between pre and post dispatch + assert_ok!(Balances::transfer_allow_death( + origin, + 3, + Balances::free_balance(2) + )); + assert_eq!(Balances::free_balance(2), 0); + + Ok(post_info_from_weight(Weight::from_parts(50, 0))) + }) + .unwrap() .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - // kill the account between pre and post dispatch - assert_ok!(Balances::transfer_allow_death( - Some(2).into(), - 3, - Balances::free_balance(2) - )); - assert_eq!(Balances::free_balance(2), 0); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(50, 0)), - len, - &Ok(()) - )); assert_eq!(Balances::free_balance(2), 0); // Transfer Event System::assert_has_event(RuntimeEvent::Balances(pallet_balances::Event::Transfer { @@ -568,20 +543,15 @@ fn actual_weight_higher_than_max_refunds_nothing() { .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let info = info_from_weight(Weight::from_parts(100, 0)); + Ext::from(5 /* tipped */) + .test_run(Some(2).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + Ok(post_info_from_weight(Weight::from_parts(101, 0))) + }) + .unwrap() .unwrap(); assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(101, 0)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); }); } @@ -594,25 +564,20 @@ fn zero_transfer_on_free_transaction() { .execute_with(|| { // So events are emitted System::set_block_number(10); - let len = 10; - let dispatch_info = DispatchInfo { + let info = DispatchInfo { weight: Weight::from_parts(100, 0), pays_fee: Pays::No, class: DispatchClass::Normal, }; let user = 69; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&user, CALL, &dispatch_info, len) + Ext::from(0) + .test_run(Some(user).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::total_balance(&user), 0); + Ok(default_post_info()) + }) + .unwrap() .unwrap(); assert_eq!(Balances::total_balance(&user), 0); - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &dispatch_info, - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::total_balance(&user), 0); // TransactionFeePaid Event System::assert_has_event(RuntimeEvent::TransactionPayment( pallet_transaction_payment::Event::TransactionFeePaid { @@ -639,19 +604,11 @@ fn refund_consistent_with_actual_weight() { >::put(Multiplier::saturating_from_rational(5, 4)); - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) + Ext::from(tip) + .test_run(Some(2).into(), CALL, &info, len, |_| Ok(post_info)) + .unwrap() .unwrap(); - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - let refund_based_fee = prev_balance - Balances::free_balance(2); let actual_fee = Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); @@ -673,18 +630,13 @@ fn should_alter_operational_priority() { class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; assert_eq!(priority, 60); - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - + let ext = Ext::from(2 * tip); + let priority = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; assert_eq!(priority, 110); }); @@ -694,16 +646,13 @@ fn should_alter_operational_priority() { class: DispatchClass::Operational, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; assert_eq!(priority, 5810); - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + let ext = Ext::from(2 * tip); + let priority = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; assert_eq!(priority, 6110); }); } @@ -719,11 +668,8 @@ fn no_tip_has_some_priority() { class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; assert_eq!(priority, 10); }); @@ -733,10 +679,8 @@ fn no_tip_has_some_priority() { class: DispatchClass::Operational, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; assert_eq!(priority, 5510); }); } @@ -744,8 +688,8 @@ fn no_tip_has_some_priority() { #[test] fn higher_tip_have_higher_priority() { let get_priorities = |tip: u64| { - let mut priority1 = 0; - let mut priority2 = 0; + let mut pri1 = 0; + let mut pri2 = 0; let len = 10; ExtBuilder::default().balance_factor(100).build().execute_with(|| { let normal = DispatchInfo { @@ -753,10 +697,8 @@ fn higher_tip_have_higher_priority() { class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - priority1 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + pri1 = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; }); ExtBuilder::default().balance_factor(100).build().execute_with(|| { @@ -765,13 +707,11 @@ fn higher_tip_have_higher_priority() { class: DispatchClass::Operational, pays_fee: Pays::Yes, }; - priority2 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + pri2 = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; }); - (priority1, priority2) + (pri1, pri2) }; let mut prev_priorities = get_priorities(0); @@ -799,19 +739,11 @@ fn post_info_can_change_pays_fee() { >::put(Multiplier::saturating_from_rational(5, 4)); - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) + let post_info = ChargeTransactionPayment::::from(tip) + .test_run(Some(2).into(), CALL, &info, len, |_| Ok(post_info)) + .unwrap() .unwrap(); - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - let refund_based_fee = prev_balance - Balances::free_balance(2); let actual_fee = Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); diff --git a/substrate/frame/transaction-payment/src/types.rs b/substrate/frame/transaction-payment/src/types.rs index cbe85309b856a9415cd731164d9333ef54aa0922..bfb3012fef06033f9419708251865b8eadb619be 100644 --- a/substrate/frame/transaction-payment/src/types.rs +++ b/substrate/frame/transaction-payment/src/types.rs @@ -94,7 +94,7 @@ impl FeeDetails { /// Information related to a dispatchable's class, weight, and fee that can be queried from the /// runtime. #[derive(Eq, PartialEq, Encode, Decode, Default, TypeInfo)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize, Clone))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] #[cfg_attr( feature = "std", @@ -112,7 +112,7 @@ pub struct RuntimeDispatchInfo /// The inclusion fee of this dispatch. /// /// This does not include a tip or anything else that - /// depends on the signature (i.e. depends on a `SignedExtension`). + /// depends on the signature (i.e. depends on a `TransactionExtension`). #[cfg_attr(feature = "std", serde(with = "serde_balance"))] pub partial_fee: Balance, } diff --git a/substrate/frame/transaction-payment/src/weights.rs b/substrate/frame/transaction-payment/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..bcffb2eb331acf77f694f74510f61d8653db0f95 --- /dev/null +++ b/substrate/frame/transaction-payment/src/weights.rs @@ -0,0 +1,92 @@ +// 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_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/transaction-payment/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --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_transaction_payment`. +pub trait WeightInfo { + fn charge_transaction_payment() -> Weight; +} + +/// Weights for `pallet_transaction_payment` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 40_506_000 picoseconds. + Weight::from_parts(41_647_000, 1733) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 40_506_000 picoseconds. + Weight::from_parts(41_647_000, 1733) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } +} diff --git a/substrate/frame/transaction-storage/Cargo.toml b/substrate/frame/transaction-storage/Cargo.toml index 26f9f64fe35d4956378976ca355d149f0cae6685..1386d9b5a5691475bdc06a198a86cb944b7e8305 100644 --- a/substrate/frame/transaction-storage/Cargo.toml +++ b/substrate/frame/transaction-storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-transaction-storage" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] array-bytes = { version = "6.1", optional = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", optional = true } +serde = { optional = true, workspace = true, default-features = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } @@ -29,7 +29,7 @@ sp-io = { path = "../../primitives/io", default-features = false } sp-runtime = { path = "../../primitives/runtime", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } sp-transaction-storage-proof = { path = "../../primitives/transaction-storage-proof", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] sp-core = { path = "../../primitives/core", default-features = false } diff --git a/substrate/frame/transaction-storage/src/lib.rs b/substrate/frame/transaction-storage/src/lib.rs index fb8ada0f5f9954b9fc8455094aeb9ec641a165c2..d32cfe169ce0b3f3ef76040ea596673e7d02d2c7 100644 --- a/substrate/frame/transaction-storage/src/lib.rs +++ b/substrate/frame/transaction-storage/src/lib.rs @@ -32,10 +32,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ dispatch::GetDispatchInfo, traits::{ - fungible::{ - hold::Balanced as FnBalanced, Inspect as FnInspect, Mutate as FnMutate, - MutateHold as FnMutateHold, - }, + fungible::{hold::Balanced, Inspect, Mutate, MutateHold}, tokens::fungible::Credit, OnUnbalanced, }, @@ -49,7 +46,7 @@ use sp_transaction_storage_proof::{ /// A type alias for the balance type from this pallet's point of view. type BalanceOf = - <::Currency as FnInspect<::AccountId>>::Balance; + <::Currency as Inspect<::AccountId>>::Balance; pub type CreditOf = Credit<::AccountId, ::Currency>; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -111,9 +108,9 @@ pub mod pallet { + GetDispatchInfo + From>; /// The fungible type for this pallet. - type Currency: FnMutate - + FnMutateHold - + FnBalanced; + type Currency: Mutate + + MutateHold + + Balanced; /// The overarching runtime hold reason. type RuntimeHoldReason: From; /// Handler for the unbalanced decrease when fees are burned. diff --git a/substrate/frame/transaction-storage/src/mock.rs b/substrate/frame/transaction-storage/src/mock.rs index 4b2fe5a2a8ca3b12bd57d0039c6f933d7ca2fbab..31f539327cda2e5a8bd4593d3cbbbecc004ae296 100644 --- a/substrate/frame/transaction-storage/src/mock.rs +++ b/substrate/frame/transaction-storage/src/mock.rs @@ -50,12 +50,7 @@ impl frame_system::Config for Test { #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] impl pallet_balances::Config for Test { - type Balance = u64; - type ExistentialDeposit = ConstU64<1>; type AccountStore = System; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = ConstU32<128>; } impl pallet_transaction_storage::Config for Test { diff --git a/substrate/frame/transaction-storage/src/weights.rs b/substrate/frame/transaction-storage/src/weights.rs index 519317177c492f4e1179b9ee12f17da7d442b0e4..bfbed62c5a37202bb4b93ac9e07cedc0891251cc 100644 --- a/substrate/frame/transaction-storage/src/weights.rs +++ b/substrate/frame/transaction-storage/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_transaction_storage +//! Autogenerated weights for `pallet_transaction_storage` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/transaction-storage/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/transaction-storage/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,125 +49,133 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_transaction_storage. +/// Weight functions needed for `pallet_transaction_storage`. pub trait WeightInfo { fn store(l: u32, ) -> Weight; fn renew() -> Weight; fn check_proof_max() -> Weight; } -/// Weights for pallet_transaction_storage using the Substrate node and recommended hardware. +/// Weights for `pallet_transaction_storage` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: TransactionStorage ByteFee (r:1 w:0) - /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage EntryFee (r:1 w:0) - /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage BlockTransactions (r:1 w:1) - /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) + /// Storage: `TransactionStorage::ByteFee` (r:1 w:0) + /// Proof: `TransactionStorage::ByteFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::EntryFee` (r:1 w:0) + /// Proof: `TransactionStorage::EntryFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::BlockTransactions` (r:1 w:1) + /// Proof: `TransactionStorage::BlockTransactions` (`max_values`: Some(1), `max_size`: Some(36866), added: 37361, mode: `MaxEncodedLen`) /// The range of component `l` is `[1, 8388608]`. fn store(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `176` + // Measured: `242` // Estimated: `38351` - // Minimum execution time: 34_844_000 picoseconds. - Weight::from_parts(35_489_000, 38351) + // Minimum execution time: 57_912_000 picoseconds. + Weight::from_parts(58_642_000, 38351) // Standard Error: 11 - .saturating_add(Weight::from_parts(6_912, 0).saturating_mul(l.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(6_778, 0).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: TransactionStorage Transactions (r:1 w:0) - /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) - /// Storage: TransactionStorage ByteFee (r:1 w:0) - /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage EntryFee (r:1 w:0) - /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage BlockTransactions (r:1 w:1) - /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) + /// Storage: `TransactionStorage::Transactions` (r:1 w:0) + /// Proof: `TransactionStorage::Transactions` (`max_values`: None, `max_size`: Some(36886), added: 39361, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::ByteFee` (r:1 w:0) + /// Proof: `TransactionStorage::ByteFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::EntryFee` (r:1 w:0) + /// Proof: `TransactionStorage::EntryFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::BlockTransactions` (r:1 w:1) + /// Proof: `TransactionStorage::BlockTransactions` (`max_values`: Some(1), `max_size`: Some(36866), added: 37361, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `326` + // Measured: `430` // Estimated: `40351` - // Minimum execution time: 48_244_000 picoseconds. - Weight::from_parts(50_939_000, 40351) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Minimum execution time: 72_571_000 picoseconds. + Weight::from_parts(74_832_000, 40351) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: TransactionStorage ProofChecked (r:1 w:1) - /// Proof: TransactionStorage ProofChecked (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: TransactionStorage StoragePeriod (r:1 w:0) - /// Proof: TransactionStorage StoragePeriod (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: TransactionStorage ChunkCount (r:1 w:0) - /// Proof: TransactionStorage ChunkCount (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) - /// Storage: System ParentHash (r:1 w:0) - /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TransactionStorage Transactions (r:1 w:0) - /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) + /// Storage: `TransactionStorage::ProofChecked` (r:1 w:1) + /// Proof: `TransactionStorage::ProofChecked` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::StoragePeriod` (r:1 w:0) + /// Proof: `TransactionStorage::StoragePeriod` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::ChunkCount` (r:1 w:0) + /// Proof: `TransactionStorage::ChunkCount` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::Transactions` (r:1 w:0) + /// Proof: `TransactionStorage::Transactions` (`max_values`: None, `max_size`: Some(36886), added: 39361, mode: `MaxEncodedLen`) fn check_proof_max() -> Weight { // Proof Size summary in bytes: - // Measured: `37145` + // Measured: `37211` // Estimated: `40351` - // Minimum execution time: 80_913_000 picoseconds. - Weight::from_parts(84_812_000, 40351) + // Minimum execution time: 65_985_000 picoseconds. + Weight::from_parts(72_960_000, 40351) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: TransactionStorage ByteFee (r:1 w:0) - /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage EntryFee (r:1 w:0) - /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage BlockTransactions (r:1 w:1) - /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) + /// Storage: `TransactionStorage::ByteFee` (r:1 w:0) + /// Proof: `TransactionStorage::ByteFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::EntryFee` (r:1 w:0) + /// Proof: `TransactionStorage::EntryFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::BlockTransactions` (r:1 w:1) + /// Proof: `TransactionStorage::BlockTransactions` (`max_values`: Some(1), `max_size`: Some(36866), added: 37361, mode: `MaxEncodedLen`) /// The range of component `l` is `[1, 8388608]`. fn store(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `176` + // Measured: `242` // Estimated: `38351` - // Minimum execution time: 34_844_000 picoseconds. - Weight::from_parts(35_489_000, 38351) + // Minimum execution time: 57_912_000 picoseconds. + Weight::from_parts(58_642_000, 38351) // Standard Error: 11 - .saturating_add(Weight::from_parts(6_912, 0).saturating_mul(l.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(6_778, 0).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: TransactionStorage Transactions (r:1 w:0) - /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) - /// Storage: TransactionStorage ByteFee (r:1 w:0) - /// Proof: TransactionStorage ByteFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage EntryFee (r:1 w:0) - /// Proof: TransactionStorage EntryFee (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: TransactionStorage BlockTransactions (r:1 w:1) - /// Proof: TransactionStorage BlockTransactions (max_values: Some(1), max_size: Some(36866), added: 37361, mode: MaxEncodedLen) + /// Storage: `TransactionStorage::Transactions` (r:1 w:0) + /// Proof: `TransactionStorage::Transactions` (`max_values`: None, `max_size`: Some(36886), added: 39361, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::ByteFee` (r:1 w:0) + /// Proof: `TransactionStorage::ByteFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::EntryFee` (r:1 w:0) + /// Proof: `TransactionStorage::EntryFee` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::BlockTransactions` (r:1 w:1) + /// Proof: `TransactionStorage::BlockTransactions` (`max_values`: Some(1), `max_size`: Some(36866), added: 37361, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `326` + // Measured: `430` // Estimated: `40351` - // Minimum execution time: 48_244_000 picoseconds. - Weight::from_parts(50_939_000, 40351) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Minimum execution time: 72_571_000 picoseconds. + Weight::from_parts(74_832_000, 40351) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: TransactionStorage ProofChecked (r:1 w:1) - /// Proof: TransactionStorage ProofChecked (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: TransactionStorage StoragePeriod (r:1 w:0) - /// Proof: TransactionStorage StoragePeriod (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: TransactionStorage ChunkCount (r:1 w:0) - /// Proof: TransactionStorage ChunkCount (max_values: None, max_size: Some(24), added: 2499, mode: MaxEncodedLen) - /// Storage: System ParentHash (r:1 w:0) - /// Proof: System ParentHash (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: TransactionStorage Transactions (r:1 w:0) - /// Proof: TransactionStorage Transactions (max_values: None, max_size: Some(36886), added: 39361, mode: MaxEncodedLen) + /// Storage: `TransactionStorage::ProofChecked` (r:1 w:1) + /// Proof: `TransactionStorage::ProofChecked` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::StoragePeriod` (r:1 w:0) + /// Proof: `TransactionStorage::StoragePeriod` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::ChunkCount` (r:1 w:0) + /// Proof: `TransactionStorage::ChunkCount` (`max_values`: None, `max_size`: Some(24), added: 2499, mode: `MaxEncodedLen`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `TransactionStorage::Transactions` (r:1 w:0) + /// Proof: `TransactionStorage::Transactions` (`max_values`: None, `max_size`: Some(36886), added: 39361, mode: `MaxEncodedLen`) fn check_proof_max() -> Weight { // Proof Size summary in bytes: - // Measured: `37145` + // Measured: `37211` // Estimated: `40351` - // Minimum execution time: 80_913_000 picoseconds. - Weight::from_parts(84_812_000, 40351) + // Minimum execution time: 65_985_000 picoseconds. + Weight::from_parts(72_960_000, 40351) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/treasury/Cargo.toml b/substrate/frame/treasury/Cargo.toml index 01eb7c55a0a674e23d9b822079d221660b8ac866..5f90904123d9341425ff707ae7682e2f20f28ecf 100644 --- a/substrate/frame/treasury/Cargo.toml +++ b/substrate/frame/treasury/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-treasury" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -20,10 +20,10 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", "max-encoded-len", ] } -docify = "0.2.0" +docify = "0.2.7" impl-trait-for-tuples = "0.2.2" scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", features = ["derive"], optional = true } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index 602159262e46598823a8d581213196517646fede..b488300de99d5d0c77d8496b597f71c03935b61e 100644 --- a/substrate/frame/treasury/src/tests.rs +++ b/substrate/frame/treasury/src/tests.rs @@ -20,9 +20,8 @@ #![cfg(test)] use core::{cell::RefCell, marker::PhantomData}; -use sp_core::H256; use sp_runtime::{ - traits::{BadOrigin, BlakeTwo256, Dispatchable, IdentityLookup}, + traits::{BadOrigin, Dispatchable, IdentityLookup}, BuildStorage, }; @@ -56,29 +55,10 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { type MaxLocks = (); @@ -94,7 +74,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_utility::Config for Test { diff --git a/substrate/frame/treasury/src/weights.rs b/substrate/frame/treasury/src/weights.rs index 030e18980eb54f8b16452691b884e6046878aaa2..8ef4f89fd6519037d811666e0792cd13bcc2661a 100644 --- a/substrate/frame/treasury/src/weights.rs +++ b/substrate/frame/treasury/src/weights.rs @@ -15,27 +15,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_treasury +//! Autogenerated weights for `pallet_treasury` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-07, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cob`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/debug/substrate +// ./target/production/substrate-node // benchmark // pallet // --chain=dev -// --steps=20 -// --repeat=2 -// --pallet=pallet-treasury +// --steps=50 +// --repeat=20 +// --pallet=pallet_treasury +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/treasury/src/._weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/treasury/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -45,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_treasury. +/// Weight functions needed for `pallet_treasury`. pub trait WeightInfo { fn spend_local() -> Weight; fn propose_spend() -> Weight; @@ -59,304 +63,304 @@ pub trait WeightInfo { fn void_spend() -> Weight; } -/// Weights for pallet_treasury using the Substrate node and recommended hardware. +/// Weights for `pallet_treasury` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Treasury ProposalCount (r:1 w:1) - /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:0 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) fn spend_local() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1887` - // Minimum execution time: 179_000_000 picoseconds. - Weight::from_parts(190_000_000, 1887) + // Minimum execution time: 11_686_000 picoseconds. + Weight::from_parts(12_138_000, 1887) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Treasury ProposalCount (r:1 w:1) - /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:0 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) fn propose_spend() -> Weight { // Proof Size summary in bytes: // Measured: `177` // Estimated: `1489` - // Minimum execution time: 349_000_000 picoseconds. - Weight::from_parts(398_000_000, 1489) + // Minimum execution time: 23_773_000 picoseconds. + Weight::from_parts(24_801_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Treasury Proposals (r:1 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Treasury::Proposals` (r:1 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn reject_proposal() -> Weight { // Proof Size summary in bytes: // Measured: `335` // Estimated: `3593` - // Minimum execution time: 367_000_000 picoseconds. - Weight::from_parts(388_000_000, 3593) + // Minimum execution time: 24_533_000 picoseconds. + Weight::from_parts(25_731_000, 3593) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Treasury Proposals (r:1 w:0) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Proposals` (r:1 w:0) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 99]`. fn approve_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `483 + p * (9 ±0)` + // Measured: `504 + p * (8 ±0)` // Estimated: `3573` - // Minimum execution time: 111_000_000 picoseconds. - Weight::from_parts(108_813_243, 3573) - // Standard Error: 147_887 - .saturating_add(Weight::from_parts(683_216, 0).saturating_mul(p.into())) + // Minimum execution time: 8_245_000 picoseconds. + Weight::from_parts(11_300_222, 3573) + // Standard Error: 1_080 + .saturating_add(Weight::from_parts(71_375, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn remove_approval() -> Weight { // Proof Size summary in bytes: // Measured: `161` // Estimated: `1887` - // Minimum execution time: 71_000_000 picoseconds. - Weight::from_parts(78_000_000, 1887) + // Minimum execution time: 6_231_000 picoseconds. + Weight::from_parts(6_517_000, 1887) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Treasury Deactivated (r:1 w:1) - /// Proof: Treasury Deactivated (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:99 w:99) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: System Account (r:198 w:198) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Deactivated` (r:1 w:1) + /// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:99 w:99) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:198 w:198) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 99]`. fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `427 + p * (251 ±0)` + // Measured: `451 + p * (251 ±0)` // Estimated: `1887 + p * (5206 ±0)` - // Minimum execution time: 614_000_000 picoseconds. - Weight::from_parts(498_501_558, 1887) - // Standard Error: 1_070_260 - .saturating_add(Weight::from_parts(599_011_690, 0).saturating_mul(p.into())) + // Minimum execution time: 30_729_000 picoseconds. + Weight::from_parts(37_861_389, 1887) + // Standard Error: 18_741 + .saturating_add(Weight::from_parts(31_377_118, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:0) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Treasury SpendCount (r:1 w:1) - /// Proof: Treasury SpendCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Spends (r:0 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:0) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Treasury::SpendCount` (r:1 w:1) + /// Proof: `Treasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Spends` (r:0 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn spend() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `3501` - // Minimum execution time: 214_000_000 picoseconds. - Weight::from_parts(216_000_000, 3501) + // Minimum execution time: 13_700_000 picoseconds. + Weight::from_parts(14_519_000, 3501) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `705` + // Measured: `709` // Estimated: `6208` - // Minimum execution time: 760_000_000 picoseconds. - Weight::from_parts(822_000_000, 6208) + // Minimum execution time: 54_888_000 picoseconds. + Weight::from_parts(55_966_000, 6208) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn check_status() -> Weight { // Proof Size summary in bytes: - // Measured: `194` - // Estimated: `3534` - // Minimum execution time: 153_000_000 picoseconds. - Weight::from_parts(160_000_000, 3534) + // Measured: `198` + // Estimated: `3538` + // Minimum execution time: 11_802_000 picoseconds. + Weight::from_parts(12_421_000, 3538) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn void_spend() -> Weight { // Proof Size summary in bytes: - // Measured: `194` - // Estimated: `3534` - // Minimum execution time: 147_000_000 picoseconds. - Weight::from_parts(181_000_000, 3534) + // Measured: `198` + // Estimated: `3538` + // Minimum execution time: 10_571_000 picoseconds. + Weight::from_parts(11_052_000, 3538) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Treasury ProposalCount (r:1 w:1) - /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:0 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) fn spend_local() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `1887` - // Minimum execution time: 179_000_000 picoseconds. - Weight::from_parts(190_000_000, 1887) + // Minimum execution time: 11_686_000 picoseconds. + Weight::from_parts(12_138_000, 1887) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Treasury ProposalCount (r:1 w:1) - /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:0 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) fn propose_spend() -> Weight { // Proof Size summary in bytes: // Measured: `177` // Estimated: `1489` - // Minimum execution time: 349_000_000 picoseconds. - Weight::from_parts(398_000_000, 1489) + // Minimum execution time: 23_773_000 picoseconds. + Weight::from_parts(24_801_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Treasury Proposals (r:1 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Treasury::Proposals` (r:1 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn reject_proposal() -> Weight { // Proof Size summary in bytes: // Measured: `335` // Estimated: `3593` - // Minimum execution time: 367_000_000 picoseconds. - Weight::from_parts(388_000_000, 3593) + // Minimum execution time: 24_533_000 picoseconds. + Weight::from_parts(25_731_000, 3593) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Treasury Proposals (r:1 w:0) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Proposals` (r:1 w:0) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 99]`. fn approve_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `483 + p * (9 ±0)` + // Measured: `504 + p * (8 ±0)` // Estimated: `3573` - // Minimum execution time: 111_000_000 picoseconds. - Weight::from_parts(108_813_243, 3573) - // Standard Error: 147_887 - .saturating_add(Weight::from_parts(683_216, 0).saturating_mul(p.into())) + // Minimum execution time: 8_245_000 picoseconds. + Weight::from_parts(11_300_222, 3573) + // Standard Error: 1_080 + .saturating_add(Weight::from_parts(71_375, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn remove_approval() -> Weight { // Proof Size summary in bytes: // Measured: `161` // Estimated: `1887` - // Minimum execution time: 71_000_000 picoseconds. - Weight::from_parts(78_000_000, 1887) + // Minimum execution time: 6_231_000 picoseconds. + Weight::from_parts(6_517_000, 1887) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Treasury Deactivated (r:1 w:1) - /// Proof: Treasury Deactivated (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:99 w:99) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: System Account (r:198 w:198) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Deactivated` (r:1 w:1) + /// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:99 w:99) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:198 w:198) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 99]`. fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `427 + p * (251 ±0)` + // Measured: `451 + p * (251 ±0)` // Estimated: `1887 + p * (5206 ±0)` - // Minimum execution time: 614_000_000 picoseconds. - Weight::from_parts(498_501_558, 1887) - // Standard Error: 1_070_260 - .saturating_add(Weight::from_parts(599_011_690, 0).saturating_mul(p.into())) + // Minimum execution time: 30_729_000 picoseconds. + Weight::from_parts(37_861_389, 1887) + // Standard Error: 18_741 + .saturating_add(Weight::from_parts(31_377_118, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:0) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(36), added: 2511, mode: MaxEncodedLen) - /// Storage: Treasury SpendCount (r:1 w:1) - /// Proof: Treasury SpendCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Spends (r:0 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:0) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `Treasury::SpendCount` (r:1 w:1) + /// Proof: `Treasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Spends` (r:0 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn spend() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `3501` - // Minimum execution time: 214_000_000 picoseconds. - Weight::from_parts(216_000_000, 3501) + // Minimum execution time: 13_700_000 picoseconds. + Weight::from_parts(14_519_000, 3501) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: Assets Asset (r:1 w:1) - /// Proof: Assets Asset (max_values: None, max_size: Some(210), added: 2685, mode: MaxEncodedLen) - /// Storage: Assets Account (r:2 w:2) - /// Proof: Assets Account (max_values: None, max_size: Some(134), added: 2609, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `705` + // Measured: `709` // Estimated: `6208` - // Minimum execution time: 760_000_000 picoseconds. - Weight::from_parts(822_000_000, 6208) + // Minimum execution time: 54_888_000 picoseconds. + Weight::from_parts(55_966_000, 6208) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn check_status() -> Weight { // Proof Size summary in bytes: - // Measured: `194` - // Estimated: `3534` - // Minimum execution time: 153_000_000 picoseconds. - Weight::from_parts(160_000_000, 3534) + // Measured: `198` + // Estimated: `3538` + // Minimum execution time: 11_802_000 picoseconds. + Weight::from_parts(12_421_000, 3538) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn void_spend() -> Weight { // Proof Size summary in bytes: - // Measured: `194` - // Estimated: `3534` - // Minimum execution time: 147_000_000 picoseconds. - Weight::from_parts(181_000_000, 3534) + // Measured: `198` + // Estimated: `3538` + // Minimum execution time: 10_571_000 picoseconds. + Weight::from_parts(11_052_000, 3538) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/try-runtime/Cargo.toml b/substrate/frame/try-runtime/Cargo.toml index 1d036e004476a78361ee2ecc98d5b8f44b2c55f9..15c8ca5d27a71f9d4172d5d8bd31808d7badcc3b 100644 --- a/substrate/frame/try-runtime/Cargo.toml +++ b/substrate/frame/try-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-try-runtime" -version = "0.10.0-dev" +version = "0.34.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/tx-pause/Cargo.toml b/substrate/frame/tx-pause/Cargo.toml index 5958dcc2c30dbf27c6a1ff393cbe18a8350b39ae..ace2172454eb976946c4069c7ac69f781ca47f2d 100644 --- a/substrate/frame/tx-pause/Cargo.toml +++ b/substrate/frame/tx-pause/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-tx-pause" -version = "4.0.0-dev" +version = "9.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -docify = "0.2.6" +docify = "0.2.7" frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } frame-system = { path = "../system", default-features = false } diff --git a/substrate/frame/tx-pause/src/mock.rs b/substrate/frame/tx-pause/src/mock.rs index 4f1c981abc6f6281850c28b2ef925baceb97623b..8ccdc43a46cdb1b2865c768a81dc69453820d9d3 100644 --- a/substrate/frame/tx-pause/src/mock.rs +++ b/substrate/frame/tx-pause/src/mock.rs @@ -80,7 +80,6 @@ impl pallet_balances::Config for Test { type FreezeIdentifier = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } diff --git a/substrate/frame/tx-pause/src/weights.rs b/substrate/frame/tx-pause/src/weights.rs index b733e64b159dc40872e27dd540ed40820e5cc78d..14fead12a8e11647c15714b2937f06da7c8b2f99 100644 --- a/substrate/frame/tx-pause/src/weights.rs +++ b/substrate/frame/tx-pause/src/weights.rs @@ -17,27 +17,29 @@ //! Autogenerated weights for `pallet_tx_pause` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-aahe6cbd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_tx_pause +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_tx_pause -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/tx-pause/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/tx-pause/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -62,8 +64,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3` // Estimated: `3997` - // Minimum execution time: 15_096_000 picoseconds. - Weight::from_parts(15_437_000, 3997) + // Minimum execution time: 12_014_000 picoseconds. + Weight::from_parts(12_980_000, 3997) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -73,8 +75,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `565` // Estimated: `3997` - // Minimum execution time: 21_546_000 picoseconds. - Weight::from_parts(22_178_000, 3997) + // Minimum execution time: 17_955_000 picoseconds. + Weight::from_parts(18_348_000, 3997) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -88,8 +90,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3` // Estimated: `3997` - // Minimum execution time: 15_096_000 picoseconds. - Weight::from_parts(15_437_000, 3997) + // Minimum execution time: 12_014_000 picoseconds. + Weight::from_parts(12_980_000, 3997) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -99,8 +101,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `565` // Estimated: `3997` - // Minimum execution time: 21_546_000 picoseconds. - Weight::from_parts(22_178_000, 3997) + // Minimum execution time: 17_955_000 picoseconds. + Weight::from_parts(18_348_000, 3997) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate/frame/uniques/Cargo.toml b/substrate/frame/uniques/Cargo.toml index 218b4ffe4c054e48f5ee91b840898966eead67f2..4e5f21b3d8df8fa45da0735cdd0ebe1afac6d181 100644 --- a/substrate/frame/uniques/Cargo.toml +++ b/substrate/frame/uniques/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-uniques" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/uniques/src/migration.rs b/substrate/frame/uniques/src/migration.rs index 6b2bbf375e7541d58a30af413b6aad288d77e655..ba0855a6bb657698d5c642181c3483459774a04f 100644 --- a/substrate/frame/uniques/src/migration.rs +++ b/substrate/frame/uniques/src/migration.rs @@ -17,8 +17,8 @@ //! Various pieces of common functionality. use super::*; +use core::marker::PhantomData; use frame_support::traits::{Get, OnRuntimeUpgrade}; -use sp_std::marker::PhantomData; mod v1 { use super::*; diff --git a/substrate/frame/uniques/src/mock.rs b/substrate/frame/uniques/src/mock.rs index 9120108e0d4327625a088c030aebaefd99cccf35..eae125971635ec7d3b6ed7a8559e0a5e018592f7 100644 --- a/substrate/frame/uniques/src/mock.rs +++ b/substrate/frame/uniques/src/mock.rs @@ -24,11 +24,7 @@ use frame_support::{ construct_runtime, derive_impl, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, }; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -43,29 +39,8 @@ construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); - type BlockLength = (); - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -82,7 +57,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl Config for Test { diff --git a/substrate/frame/uniques/src/weights.rs b/substrate/frame/uniques/src/weights.rs index eb80ee550a1db2d97aaed001f5041e49fdbca864..6c7b60b82e52e2caa9ac4536a33dda4df687c122 100644 --- a/substrate/frame/uniques/src/weights.rs +++ b/substrate/frame/uniques/src/weights.rs @@ -17,27 +17,29 @@ //! Autogenerated weights for `pallet_uniques` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-gghbxkbs-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet +// --chain=dev // --steps=50 // --repeat=20 +// --pallet=pallet_uniques +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_uniques -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/uniques/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/uniques/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -88,8 +90,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `249` // Estimated: `3643` - // Minimum execution time: 31_393_000 picoseconds. - Weight::from_parts(32_933_000, 3643) + // Minimum execution time: 27_960_000 picoseconds. + Weight::from_parts(28_781_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -101,8 +103,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3643` - // Minimum execution time: 14_827_000 picoseconds. - Weight::from_parts(15_273_000, 3643) + // Minimum execution time: 11_970_000 picoseconds. + Weight::from_parts(12_512_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -111,13 +113,13 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Uniques::Asset` (r:1001 w:1000) /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1000 w:1000) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) /// Storage: `Uniques::Attribute` (r:1000 w:1000) - /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) + /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(172), added: 2647, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassAccount` (r:0 w:1) /// Proof: `Uniques::ClassAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassMetadataOf` (r:0 w:1) - /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(167), added: 2642, mode: `MaxEncodedLen`) /// Storage: `Uniques::Account` (r:0 w:1000) /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Uniques::CollectionMaxSupply` (r:0 w:1) @@ -128,15 +130,15 @@ impl WeightInfo for SubstrateWeight { fn destroy(n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `418 + a * (107 ±0) + m * (56 ±0) + n * (76 ±0)` - // Estimated: `3643 + a * (2839 ±0) + m * (2583 ±0) + n * (2597 ±0)` - // Minimum execution time: 3_281_673_000 picoseconds. - Weight::from_parts(3_443_387_000, 3643) - // Standard Error: 41_937 - .saturating_add(Weight::from_parts(7_914_842, 0).saturating_mul(n.into())) - // Standard Error: 41_937 - .saturating_add(Weight::from_parts(519_960, 0).saturating_mul(m.into())) - // Standard Error: 41_937 - .saturating_add(Weight::from_parts(462_690, 0).saturating_mul(a.into())) + // Estimated: `3643 + a * (2647 ±0) + m * (2662 ±0) + n * (2597 ±0)` + // Minimum execution time: 2_979_858_000 picoseconds. + Weight::from_parts(3_007_268_000, 3643) + // Standard Error: 29_899 + .saturating_add(Weight::from_parts(6_943_612, 0).saturating_mul(n.into())) + // Standard Error: 29_899 + .saturating_add(Weight::from_parts(398_114, 0).saturating_mul(m.into())) + // Standard Error: 29_899 + .saturating_add(Weight::from_parts(369_941, 0).saturating_mul(a.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().reads((1_u64).saturating_mul(m.into()))) @@ -145,8 +147,8 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2839).saturating_mul(a.into())) - .saturating_add(Weight::from_parts(0, 2583).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 2647).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2662).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) } /// Storage: `Uniques::Asset` (r:1 w:1) @@ -161,8 +163,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 38_122_000 picoseconds. - Weight::from_parts(38_924_000, 3643) + // Minimum execution time: 32_733_000 picoseconds. + Weight::from_parts(33_487_000, 3643) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -178,8 +180,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 38_835_000 picoseconds. - Weight::from_parts(39_754_000, 3643) + // Minimum execution time: 34_202_000 picoseconds. + Weight::from_parts(35_146_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -195,8 +197,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 27_032_000 picoseconds. - Weight::from_parts(27_793_000, 3643) + // Minimum execution time: 24_285_000 picoseconds. + Weight::from_parts(25_300_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -209,10 +211,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `805 + i * (76 ±0)` // Estimated: `3643 + i * (2597 ±0)` - // Minimum execution time: 14_737_000 picoseconds. - Weight::from_parts(15_070_000, 3643) - // Standard Error: 22_500 - .saturating_add(Weight::from_parts(18_855_468, 0).saturating_mul(i.into())) + // Minimum execution time: 11_641_000 picoseconds. + Weight::from_parts(12_261_000, 3643) + // Standard Error: 18_897 + .saturating_add(Weight::from_parts(15_263_570, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -227,8 +229,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 18_664_000 picoseconds. - Weight::from_parts(19_455_000, 3643) + // Minimum execution time: 16_122_000 picoseconds. + Weight::from_parts(16_627_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -240,8 +242,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 18_247_000 picoseconds. - Weight::from_parts(18_763_000, 3643) + // Minimum execution time: 15_824_000 picoseconds. + Weight::from_parts(16_522_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -251,8 +253,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 13_219_000 picoseconds. - Weight::from_parts(13_923_000, 3643) + // Minimum execution time: 10_802_000 picoseconds. + Weight::from_parts(11_206_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -262,8 +264,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 13_376_000 picoseconds. - Weight::from_parts(13_904_000, 3643) + // Minimum execution time: 10_805_000 picoseconds. + Weight::from_parts(11_340_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -271,16 +273,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Uniques::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassAccount` (r:0 w:2) /// Proof: `Uniques::ClassAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `423` + // Measured: `597` // Estimated: `3643` - // Minimum execution time: 22_353_000 picoseconds. - Weight::from_parts(23_222_000, 3643) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + // Minimum execution time: 24_213_000 picoseconds. + Weight::from_parts(25_547_000, 3643) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) @@ -288,8 +292,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 14_072_000 picoseconds. - Weight::from_parts(14_619_000, 3643) + // Minimum execution time: 11_256_000 picoseconds. + Weight::from_parts(11_585_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -301,90 +305,90 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 17_081_000 picoseconds. - Weight::from_parts(17_698_000, 3643) + // Minimum execution time: 14_616_000 picoseconds. + Weight::from_parts(15_064_000, 3643) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:0) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) /// Storage: `Uniques::Attribute` (r:1 w:1) - /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) + /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(172), added: 2647, mode: `MaxEncodedLen`) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `547` - // Estimated: `3829` - // Minimum execution time: 41_501_000 picoseconds. - Weight::from_parts(43_101_000, 3829) + // Measured: `626` + // Estimated: `3652` + // Minimum execution time: 35_640_000 picoseconds. + Weight::from_parts(37_007_000, 3652) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:0) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) /// Storage: `Uniques::Attribute` (r:1 w:1) - /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) + /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(172), added: 2647, mode: `MaxEncodedLen`) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `936` - // Estimated: `3829` - // Minimum execution time: 39_722_000 picoseconds. - Weight::from_parts(40_390_000, 3829) + // Measured: `823` + // Estimated: `3652` + // Minimum execution time: 34_410_000 picoseconds. + Weight::from_parts(35_431_000, 3652) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:1) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `415` - // Estimated: `3643` - // Minimum execution time: 30_726_000 picoseconds. - Weight::from_parts(31_557_000, 3643) + // Estimated: `3652` + // Minimum execution time: 26_187_000 picoseconds. + Weight::from_parts(27_837_000, 3652) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:1) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `547` - // Estimated: `3643` - // Minimum execution time: 31_303_000 picoseconds. - Weight::from_parts(32_389_000, 3643) + // Measured: `626` + // Estimated: `3652` + // Minimum execution time: 26_640_000 picoseconds. + Weight::from_parts(27_688_000, 3652) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassMetadataOf` (r:1 w:1) - /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(167), added: 2642, mode: `MaxEncodedLen`) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 32_155_000 picoseconds. - Weight::from_parts(32_885_000, 3643) + // Minimum execution time: 26_781_000 picoseconds. + Weight::from_parts(27_628_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:0) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassMetadataOf` (r:1 w:1) - /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(167), added: 2642, mode: `MaxEncodedLen`) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `540` // Estimated: `3643` - // Minimum execution time: 30_044_000 picoseconds. - Weight::from_parts(31_405_000, 3643) + // Minimum execution time: 26_295_000 picoseconds. + Weight::from_parts(26_903_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -396,8 +400,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 18_904_000 picoseconds. - Weight::from_parts(19_687_000, 3643) + // Minimum execution time: 16_678_000 picoseconds. + Weight::from_parts(17_548_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -409,8 +413,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `528` // Estimated: `3643` - // Minimum execution time: 19_144_000 picoseconds. - Weight::from_parts(19_706_000, 3643) + // Minimum execution time: 16_408_000 picoseconds. + Weight::from_parts(16_957_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -420,8 +424,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 15_339_000 picoseconds. - Weight::from_parts(15_918_000, 3517) + // Minimum execution time: 12_568_000 picoseconds. + Weight::from_parts(12_960_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -433,8 +437,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_387_000 picoseconds. - Weight::from_parts(15_726_000, 3643) + // Minimum execution time: 13_110_000 picoseconds. + Weight::from_parts(13_620_000, 3643) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -446,8 +450,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `326` // Estimated: `3587` - // Minimum execution time: 15_873_000 picoseconds. - Weight::from_parts(16_860_000, 3587) + // Minimum execution time: 13_568_000 picoseconds. + Weight::from_parts(14_211_000, 3587) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -463,8 +467,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `607` // Estimated: `3643` - // Minimum execution time: 37_245_000 picoseconds. - Weight::from_parts(38_383_000, 3643) + // Minimum execution time: 32_660_000 picoseconds. + Weight::from_parts(33_734_000, 3643) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -480,8 +484,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `249` // Estimated: `3643` - // Minimum execution time: 31_393_000 picoseconds. - Weight::from_parts(32_933_000, 3643) + // Minimum execution time: 27_960_000 picoseconds. + Weight::from_parts(28_781_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -493,8 +497,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3643` - // Minimum execution time: 14_827_000 picoseconds. - Weight::from_parts(15_273_000, 3643) + // Minimum execution time: 11_970_000 picoseconds. + Weight::from_parts(12_512_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -503,13 +507,13 @@ impl WeightInfo for () { /// Storage: `Uniques::Asset` (r:1001 w:1000) /// Proof: `Uniques::Asset` (`max_values`: None, `max_size`: Some(122), added: 2597, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1000 w:1000) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) /// Storage: `Uniques::Attribute` (r:1000 w:1000) - /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) + /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(172), added: 2647, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassAccount` (r:0 w:1) /// Proof: `Uniques::ClassAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassMetadataOf` (r:0 w:1) - /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(167), added: 2642, mode: `MaxEncodedLen`) /// Storage: `Uniques::Account` (r:0 w:1000) /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Uniques::CollectionMaxSupply` (r:0 w:1) @@ -520,15 +524,15 @@ impl WeightInfo for () { fn destroy(n: u32, m: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `418 + a * (107 ±0) + m * (56 ±0) + n * (76 ±0)` - // Estimated: `3643 + a * (2839 ±0) + m * (2583 ±0) + n * (2597 ±0)` - // Minimum execution time: 3_281_673_000 picoseconds. - Weight::from_parts(3_443_387_000, 3643) - // Standard Error: 41_937 - .saturating_add(Weight::from_parts(7_914_842, 0).saturating_mul(n.into())) - // Standard Error: 41_937 - .saturating_add(Weight::from_parts(519_960, 0).saturating_mul(m.into())) - // Standard Error: 41_937 - .saturating_add(Weight::from_parts(462_690, 0).saturating_mul(a.into())) + // Estimated: `3643 + a * (2647 ±0) + m * (2662 ±0) + n * (2597 ±0)` + // Minimum execution time: 2_979_858_000 picoseconds. + Weight::from_parts(3_007_268_000, 3643) + // Standard Error: 29_899 + .saturating_add(Weight::from_parts(6_943_612, 0).saturating_mul(n.into())) + // Standard Error: 29_899 + .saturating_add(Weight::from_parts(398_114, 0).saturating_mul(m.into())) + // Standard Error: 29_899 + .saturating_add(Weight::from_parts(369_941, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) @@ -537,8 +541,8 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) - .saturating_add(Weight::from_parts(0, 2839).saturating_mul(a.into())) - .saturating_add(Weight::from_parts(0, 2583).saturating_mul(m.into())) + .saturating_add(Weight::from_parts(0, 2647).saturating_mul(a.into())) + .saturating_add(Weight::from_parts(0, 2662).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 2597).saturating_mul(n.into())) } /// Storage: `Uniques::Asset` (r:1 w:1) @@ -553,8 +557,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 38_122_000 picoseconds. - Weight::from_parts(38_924_000, 3643) + // Minimum execution time: 32_733_000 picoseconds. + Weight::from_parts(33_487_000, 3643) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -570,8 +574,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 38_835_000 picoseconds. - Weight::from_parts(39_754_000, 3643) + // Minimum execution time: 34_202_000 picoseconds. + Weight::from_parts(35_146_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -587,8 +591,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 27_032_000 picoseconds. - Weight::from_parts(27_793_000, 3643) + // Minimum execution time: 24_285_000 picoseconds. + Weight::from_parts(25_300_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -601,10 +605,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `805 + i * (76 ±0)` // Estimated: `3643 + i * (2597 ±0)` - // Minimum execution time: 14_737_000 picoseconds. - Weight::from_parts(15_070_000, 3643) - // Standard Error: 22_500 - .saturating_add(Weight::from_parts(18_855_468, 0).saturating_mul(i.into())) + // Minimum execution time: 11_641_000 picoseconds. + Weight::from_parts(12_261_000, 3643) + // Standard Error: 18_897 + .saturating_add(Weight::from_parts(15_263_570, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) @@ -619,8 +623,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 18_664_000 picoseconds. - Weight::from_parts(19_455_000, 3643) + // Minimum execution time: 16_122_000 picoseconds. + Weight::from_parts(16_627_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -632,8 +636,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 18_247_000 picoseconds. - Weight::from_parts(18_763_000, 3643) + // Minimum execution time: 15_824_000 picoseconds. + Weight::from_parts(16_522_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -643,8 +647,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 13_219_000 picoseconds. - Weight::from_parts(13_923_000, 3643) + // Minimum execution time: 10_802_000 picoseconds. + Weight::from_parts(11_206_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -654,8 +658,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 13_376_000 picoseconds. - Weight::from_parts(13_904_000, 3643) + // Minimum execution time: 10_805_000 picoseconds. + Weight::from_parts(11_340_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -663,16 +667,18 @@ impl WeightInfo for () { /// Proof: `Uniques::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassAccount` (r:0 w:2) /// Proof: `Uniques::ClassAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `423` + // Measured: `597` // Estimated: `3643` - // Minimum execution time: 22_353_000 picoseconds. - Weight::from_parts(23_222_000, 3643) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Minimum execution time: 24_213_000 picoseconds. + Weight::from_parts(25_547_000, 3643) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) @@ -680,8 +686,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 14_072_000 picoseconds. - Weight::from_parts(14_619_000, 3643) + // Minimum execution time: 11_256_000 picoseconds. + Weight::from_parts(11_585_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -693,90 +699,90 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 17_081_000 picoseconds. - Weight::from_parts(17_698_000, 3643) + // Minimum execution time: 14_616_000 picoseconds. + Weight::from_parts(15_064_000, 3643) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:0) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) /// Storage: `Uniques::Attribute` (r:1 w:1) - /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) + /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(172), added: 2647, mode: `MaxEncodedLen`) fn set_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `547` - // Estimated: `3829` - // Minimum execution time: 41_501_000 picoseconds. - Weight::from_parts(43_101_000, 3829) + // Measured: `626` + // Estimated: `3652` + // Minimum execution time: 35_640_000 picoseconds. + Weight::from_parts(37_007_000, 3652) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:0) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) /// Storage: `Uniques::Attribute` (r:1 w:1) - /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(364), added: 2839, mode: `MaxEncodedLen`) + /// Proof: `Uniques::Attribute` (`max_values`: None, `max_size`: Some(172), added: 2647, mode: `MaxEncodedLen`) fn clear_attribute() -> Weight { // Proof Size summary in bytes: - // Measured: `936` - // Estimated: `3829` - // Minimum execution time: 39_722_000 picoseconds. - Weight::from_parts(40_390_000, 3829) + // Measured: `823` + // Estimated: `3652` + // Minimum execution time: 34_410_000 picoseconds. + Weight::from_parts(35_431_000, 3652) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:1) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) fn set_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `415` - // Estimated: `3643` - // Minimum execution time: 30_726_000 picoseconds. - Weight::from_parts(31_557_000, 3643) + // Estimated: `3652` + // Minimum execution time: 26_187_000 picoseconds. + Weight::from_parts(27_837_000, 3652) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::InstanceMetadataOf` (r:1 w:1) - /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Proof: `Uniques::InstanceMetadataOf` (`max_values`: None, `max_size`: Some(187), added: 2662, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `547` - // Estimated: `3643` - // Minimum execution time: 31_303_000 picoseconds. - Weight::from_parts(32_389_000, 3643) + // Measured: `626` + // Estimated: `3652` + // Minimum execution time: 26_640_000 picoseconds. + Weight::from_parts(27_688_000, 3652) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:1) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassMetadataOf` (r:1 w:1) - /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(167), added: 2642, mode: `MaxEncodedLen`) fn set_collection_metadata() -> Weight { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 32_155_000 picoseconds. - Weight::from_parts(32_885_000, 3643) + // Minimum execution time: 26_781_000 picoseconds. + Weight::from_parts(27_628_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Uniques::Class` (r:1 w:0) /// Proof: `Uniques::Class` (`max_values`: None, `max_size`: Some(178), added: 2653, mode: `MaxEncodedLen`) /// Storage: `Uniques::ClassMetadataOf` (r:1 w:1) - /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Uniques::ClassMetadataOf` (`max_values`: None, `max_size`: Some(167), added: 2642, mode: `MaxEncodedLen`) fn clear_collection_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `461` + // Measured: `540` // Estimated: `3643` - // Minimum execution time: 30_044_000 picoseconds. - Weight::from_parts(31_405_000, 3643) + // Minimum execution time: 26_295_000 picoseconds. + Weight::from_parts(26_903_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -788,8 +794,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `495` // Estimated: `3643` - // Minimum execution time: 18_904_000 picoseconds. - Weight::from_parts(19_687_000, 3643) + // Minimum execution time: 16_678_000 picoseconds. + Weight::from_parts(17_548_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -801,8 +807,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `528` // Estimated: `3643` - // Minimum execution time: 19_144_000 picoseconds. - Weight::from_parts(19_706_000, 3643) + // Minimum execution time: 16_408_000 picoseconds. + Weight::from_parts(16_957_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -812,8 +818,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 15_339_000 picoseconds. - Weight::from_parts(15_918_000, 3517) + // Minimum execution time: 12_568_000 picoseconds. + Weight::from_parts(12_960_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -825,8 +831,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3643` - // Minimum execution time: 15_387_000 picoseconds. - Weight::from_parts(15_726_000, 3643) + // Minimum execution time: 13_110_000 picoseconds. + Weight::from_parts(13_620_000, 3643) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -838,8 +844,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `326` // Estimated: `3587` - // Minimum execution time: 15_873_000 picoseconds. - Weight::from_parts(16_860_000, 3587) + // Minimum execution time: 13_568_000 picoseconds. + Weight::from_parts(14_211_000, 3587) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -855,8 +861,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `607` // Estimated: `3643` - // Minimum execution time: 37_245_000 picoseconds. - Weight::from_parts(38_383_000, 3643) + // Minimum execution time: 32_660_000 picoseconds. + Weight::from_parts(33_734_000, 3643) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/substrate/frame/utility/Cargo.toml b/substrate/frame/utility/Cargo.toml index 4aa75f9f616570966375a71f3bd6999e818f5617..ce5cd0fa61f2166b1993eb2150953c13fa1cc5e3 100644 --- a/substrate/frame/utility/Cargo.toml +++ b/substrate/frame/utility/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-utility" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/utility/src/tests.rs b/substrate/frame/utility/src/tests.rs index cfdbd6faf0dc4250f2ece5e9773977ac739012d7..8742513be95043186dbe840540247fe7160fdfaa 100644 --- a/substrate/frame/utility/src/tests.rs +++ b/substrate/frame/utility/src/tests.rs @@ -27,13 +27,12 @@ use frame_support::{ dispatch::{DispatchErrorWithPostInfo, Pays}, error::BadOrigin, parameter_types, storage, - traits::{ConstU32, ConstU64, Contains}, + traits::{ConstU64, Contains}, weights::Weight, }; use pallet_collective::{EnsureProportionAtLeast, Instance1}; -use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, Dispatchable, Hash, IdentityLookup}, + traits::{BlakeTwo256, Dispatchable, Hash}, BuildStorage, DispatchError, TokenError, }; @@ -148,27 +147,8 @@ parameter_types! { impl frame_system::Config for Test { type BaseCallFilter = TestBaseCallFilter; type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = frame_system::weights::SubstrateWeight; - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -185,7 +165,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_root_testing::Config for Test { diff --git a/substrate/frame/utility/src/weights.rs b/substrate/frame/utility/src/weights.rs index 1a3ea6c1f7fc85df4e90a6e8552fc211a51f5405..60b1c48f0868febb7d62cf8865e2bc9a2a79f0b2 100644 --- a/substrate/frame/utility/src/weights.rs +++ b/substrate/frame/utility/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_utility +//! Autogenerated weights for `pallet_utility` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/utility/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/utility/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_utility. +/// Weight functions needed for `pallet_utility`. pub trait WeightInfo { fn batch(c: u32, ) -> Weight; fn as_derivative() -> Weight; @@ -59,99 +58,139 @@ pub trait WeightInfo { fn force_batch(c: u32, ) -> Weight; } -/// Weights for pallet_utility using the Substrate node and recommended hardware. +/// Weights for `pallet_utility` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn batch(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_763_000 picoseconds. - Weight::from_parts(16_943_157, 0) - // Standard Error: 1_904 - .saturating_add(Weight::from_parts(4_653_855, 0).saturating_mul(c.into())) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 5_143_000 picoseconds. + Weight::from_parts(11_552_299, 3997) + // Standard Error: 2_325 + .saturating_add(Weight::from_parts(4_708_951, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn as_derivative() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_149_000 picoseconds. - Weight::from_parts(5_268_000, 0) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 9_103_000 picoseconds. + Weight::from_parts(9_549_000, 3997) + .saturating_add(T::DbWeight::get().reads(2_u64)) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_976_000 picoseconds. - Weight::from_parts(16_448_433, 0) - // Standard Error: 1_834 - .saturating_add(Weight::from_parts(4_796_983, 0).saturating_mul(c.into())) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 5_147_000 picoseconds. + Weight::from_parts(15_698_086, 3997) + // Standard Error: 3_066 + .saturating_add(Weight::from_parts(4_809_412, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_102_000 picoseconds. - Weight::from_parts(9_353_000, 0) + // Minimum execution time: 6_683_000 picoseconds. + Weight::from_parts(7_047_000, 0) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_840_000 picoseconds. - Weight::from_parts(17_748_474, 0) - // Standard Error: 2_059 - .saturating_add(Weight::from_parts(4_630_079, 0).saturating_mul(c.into())) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 4_982_000 picoseconds. + Weight::from_parts(14_474_780, 3997) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(4_632_643, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn batch(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_763_000 picoseconds. - Weight::from_parts(16_943_157, 0) - // Standard Error: 1_904 - .saturating_add(Weight::from_parts(4_653_855, 0).saturating_mul(c.into())) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 5_143_000 picoseconds. + Weight::from_parts(11_552_299, 3997) + // Standard Error: 2_325 + .saturating_add(Weight::from_parts(4_708_951, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn as_derivative() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 5_149_000 picoseconds. - Weight::from_parts(5_268_000, 0) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 9_103_000 picoseconds. + Weight::from_parts(9_549_000, 3997) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn batch_all(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_976_000 picoseconds. - Weight::from_parts(16_448_433, 0) - // Standard Error: 1_834 - .saturating_add(Weight::from_parts(4_796_983, 0).saturating_mul(c.into())) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 5_147_000 picoseconds. + Weight::from_parts(15_698_086, 3997) + // Standard Error: 3_066 + .saturating_add(Weight::from_parts(4_809_412, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_102_000 picoseconds. - Weight::from_parts(9_353_000, 0) + // Minimum execution time: 6_683_000 picoseconds. + Weight::from_parts(7_047_000, 0) } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) /// The range of component `c` is `[0, 1000]`. fn force_batch(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 6_840_000 picoseconds. - Weight::from_parts(17_748_474, 0) - // Standard Error: 2_059 - .saturating_add(Weight::from_parts(4_630_079, 0).saturating_mul(c.into())) + // Measured: `145` + // Estimated: `3997` + // Minimum execution time: 4_982_000 picoseconds. + Weight::from_parts(14_474_780, 3997) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(4_632_643, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } } diff --git a/substrate/frame/vesting/Cargo.toml b/substrate/frame/vesting/Cargo.toml index 3b5252d61810644e42913bb00dae47db96b61c0a..96938b95a2ad4c09f3ec821ecc14c5cc3bc68270 100644 --- a/substrate/frame/vesting/Cargo.toml +++ b/substrate/frame/vesting/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-vesting" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } frame-support = { path = "../support", default-features = false } diff --git a/substrate/frame/vesting/src/mock.rs b/substrate/frame/vesting/src/mock.rs index 420edd5e17a2c34e9e8e81cb3654e1f725aede91..8a0cd1351253663983774a7a79d17dd60583b3f5 100644 --- a/substrate/frame/vesting/src/mock.rs +++ b/substrate/frame/vesting/src/mock.rs @@ -17,13 +17,9 @@ use frame_support::{ derive_impl, parameter_types, - traits::{ConstU32, ConstU64, WithdrawReasons}, -}; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, Identity, IdentityLookup}, - BuildStorage, + traits::{ConstU32, WithdrawReasons}, }; +use sp_runtime::{traits::Identity, BuildStorage}; use super::*; use crate as pallet_vesting; @@ -42,28 +38,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { type AccountData = pallet_balances::AccountData; - type AccountId = u64; - type BaseCallFilter = frame_support::traits::Everything; - type BlockHashCount = ConstU64<250>; - type BlockLength = (); - type BlockWeights = (); - type RuntimeCall = RuntimeCall; - type DbWeight = (); - type RuntimeEvent = RuntimeEvent; - type Hash = H256; - type Hashing = BlakeTwo256; type Block = Block; - type Nonce = u64; - type Lookup = IdentityLookup; - type OnKilledAccount = (); - type OnNewAccount = (); - type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; - type RuntimeOrigin = RuntimeOrigin; - type PalletInfo = PalletInfo; - type SS58Prefix = (); - type SystemWeightInfo = (); - type Version = (); } impl pallet_balances::Config for Test { @@ -80,7 +55,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { pub const MinVestedTransfer: u64 = 256 * 2; diff --git a/substrate/frame/vesting/src/weights.rs b/substrate/frame/vesting/src/weights.rs index ddc7db8fa61fbedef4c2629117459dc37c5f8bcd..d91c47ae195ab380b2fc18e560877e499d0093aa 100644 --- a/substrate/frame/vesting/src/weights.rs +++ b/substrate/frame/vesting/src/weights.rs @@ -17,26 +17,28 @@ //! Autogenerated weights for `pallet_vesting` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, 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-bn-ce5rx-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_vesting +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_vesting -// --chain=dev -// --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/vesting/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -75,12 +77,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 32_846_000 picoseconds. - Weight::from_parts(30_974_459, 4764) - // Standard Error: 1_755 - .saturating_add(Weight::from_parts(73_138, 0).saturating_mul(l.into())) - // Standard Error: 3_123 - .saturating_add(Weight::from_parts(82_417, 0).saturating_mul(s.into())) + // Minimum execution time: 31_890_000 picoseconds. + Weight::from_parts(31_128_476, 4764) + // Standard Error: 1_193 + .saturating_add(Weight::from_parts(63_760, 0).saturating_mul(l.into())) + // Standard Error: 2_124 + .saturating_add(Weight::from_parts(57_247, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -96,12 +98,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 34_360_000 picoseconds. - Weight::from_parts(34_964_080, 4764) - // Standard Error: 1_996 - .saturating_add(Weight::from_parts(48_024, 0).saturating_mul(l.into())) - // Standard Error: 3_552 - .saturating_add(Weight::from_parts(34_411, 0).saturating_mul(s.into())) + // Minimum execution time: 33_958_000 picoseconds. + Weight::from_parts(33_537_005, 4764) + // Standard Error: 1_354 + .saturating_add(Weight::from_parts(60_604, 0).saturating_mul(l.into())) + // Standard Error: 2_410 + .saturating_add(Weight::from_parts(51_378, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -119,12 +121,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 33_859_000 picoseconds. - Weight::from_parts(32_950_681, 4764) - // Standard Error: 1_745 - .saturating_add(Weight::from_parts(69_323, 0).saturating_mul(l.into())) - // Standard Error: 3_105 - .saturating_add(Weight::from_parts(86_370, 0).saturating_mul(s.into())) + // Minimum execution time: 33_243_000 picoseconds. + Weight::from_parts(32_552_805, 4764) + // Standard Error: 1_413 + .saturating_add(Weight::from_parts(67_137, 0).saturating_mul(l.into())) + // Standard Error: 2_515 + .saturating_add(Weight::from_parts(70_000, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -142,12 +144,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 36_239_000 picoseconds. - Weight::from_parts(36_459_756, 4764) - // Standard Error: 2_051 - .saturating_add(Weight::from_parts(56_670, 0).saturating_mul(l.into())) - // Standard Error: 3_650 - .saturating_add(Weight::from_parts(49_663, 0).saturating_mul(s.into())) + // Minimum execution time: 35_396_000 picoseconds. + Weight::from_parts(35_774_949, 4764) + // Standard Error: 1_404 + .saturating_add(Weight::from_parts(55_349, 0).saturating_mul(l.into())) + // Standard Error: 2_497 + .saturating_add(Weight::from_parts(39_228, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -165,12 +167,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `555 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 70_330_000 picoseconds. - Weight::from_parts(71_196_328, 4764) - // Standard Error: 2_923 - .saturating_add(Weight::from_parts(67_238, 0).saturating_mul(l.into())) - // Standard Error: 5_201 - .saturating_add(Weight::from_parts(89_102, 0).saturating_mul(s.into())) + // Minimum execution time: 68_441_000 picoseconds. + Weight::from_parts(68_961_970, 4764) + // Standard Error: 2_509 + .saturating_add(Weight::from_parts(83_345, 0).saturating_mul(l.into())) + // Standard Error: 4_464 + .saturating_add(Weight::from_parts(124_158, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -188,12 +190,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `658 + l * (25 ±0) + s * (36 ±0)` // Estimated: `6196` - // Minimum execution time: 70_235_000 picoseconds. - Weight::from_parts(71_960_020, 6196) - // Standard Error: 2_493 - .saturating_add(Weight::from_parts(64_835, 0).saturating_mul(l.into())) - // Standard Error: 4_436 - .saturating_add(Weight::from_parts(102_159, 0).saturating_mul(s.into())) + // Minimum execution time: 70_190_000 picoseconds. + Weight::from_parts(72_355_984, 6196) + // Standard Error: 2_295 + .saturating_add(Weight::from_parts(59_968, 0).saturating_mul(l.into())) + // Standard Error: 4_084 + .saturating_add(Weight::from_parts(87_090, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -211,12 +213,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 34_352_000 picoseconds. - Weight::from_parts(33_697_027, 4764) - // Standard Error: 2_008 - .saturating_add(Weight::from_parts(79_270, 0).saturating_mul(l.into())) - // Standard Error: 3_710 - .saturating_add(Weight::from_parts(60_691, 0).saturating_mul(s.into())) + // Minimum execution time: 33_911_000 picoseconds. + Weight::from_parts(33_243_006, 4764) + // Standard Error: 1_199 + .saturating_add(Weight::from_parts(70_586, 0).saturating_mul(l.into())) + // Standard Error: 2_214 + .saturating_add(Weight::from_parts(60_985, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -234,12 +236,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 37_467_000 picoseconds. - Weight::from_parts(36_866_847, 4764) - // Standard Error: 1_692 - .saturating_add(Weight::from_parts(57_882, 0).saturating_mul(l.into())) - // Standard Error: 3_124 - .saturating_add(Weight::from_parts(80_266, 0).saturating_mul(s.into())) + // Minimum execution time: 36_242_000 picoseconds. + Weight::from_parts(35_812_994, 4764) + // Standard Error: 1_351 + .saturating_add(Weight::from_parts(68_256, 0).saturating_mul(l.into())) + // Standard Error: 2_496 + .saturating_add(Weight::from_parts(66_171, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -257,12 +259,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `555 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 41_497_000 picoseconds. - Weight::from_parts(38_763_834, 4764) - // Standard Error: 2_030 - .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) - // Standard Error: 3_750 - .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) + // Minimum execution time: 37_817_000 picoseconds. + Weight::from_parts(37_397_127, 4764) + // Standard Error: 1_665 + .saturating_add(Weight::from_parts(72_049, 0).saturating_mul(l.into())) + // Standard Error: 3_075 + .saturating_add(Weight::from_parts(60_973, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -282,12 +284,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 32_846_000 picoseconds. - Weight::from_parts(30_974_459, 4764) - // Standard Error: 1_755 - .saturating_add(Weight::from_parts(73_138, 0).saturating_mul(l.into())) - // Standard Error: 3_123 - .saturating_add(Weight::from_parts(82_417, 0).saturating_mul(s.into())) + // Minimum execution time: 31_890_000 picoseconds. + Weight::from_parts(31_128_476, 4764) + // Standard Error: 1_193 + .saturating_add(Weight::from_parts(63_760, 0).saturating_mul(l.into())) + // Standard Error: 2_124 + .saturating_add(Weight::from_parts(57_247, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -303,12 +305,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `381 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 34_360_000 picoseconds. - Weight::from_parts(34_964_080, 4764) - // Standard Error: 1_996 - .saturating_add(Weight::from_parts(48_024, 0).saturating_mul(l.into())) - // Standard Error: 3_552 - .saturating_add(Weight::from_parts(34_411, 0).saturating_mul(s.into())) + // Minimum execution time: 33_958_000 picoseconds. + Weight::from_parts(33_537_005, 4764) + // Standard Error: 1_354 + .saturating_add(Weight::from_parts(60_604, 0).saturating_mul(l.into())) + // Standard Error: 2_410 + .saturating_add(Weight::from_parts(51_378, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -326,12 +328,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 33_859_000 picoseconds. - Weight::from_parts(32_950_681, 4764) - // Standard Error: 1_745 - .saturating_add(Weight::from_parts(69_323, 0).saturating_mul(l.into())) - // Standard Error: 3_105 - .saturating_add(Weight::from_parts(86_370, 0).saturating_mul(s.into())) + // Minimum execution time: 33_243_000 picoseconds. + Weight::from_parts(32_552_805, 4764) + // Standard Error: 1_413 + .saturating_add(Weight::from_parts(67_137, 0).saturating_mul(l.into())) + // Standard Error: 2_515 + .saturating_add(Weight::from_parts(70_000, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -349,12 +351,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `484 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 36_239_000 picoseconds. - Weight::from_parts(36_459_756, 4764) - // Standard Error: 2_051 - .saturating_add(Weight::from_parts(56_670, 0).saturating_mul(l.into())) - // Standard Error: 3_650 - .saturating_add(Weight::from_parts(49_663, 0).saturating_mul(s.into())) + // Minimum execution time: 35_396_000 picoseconds. + Weight::from_parts(35_774_949, 4764) + // Standard Error: 1_404 + .saturating_add(Weight::from_parts(55_349, 0).saturating_mul(l.into())) + // Standard Error: 2_497 + .saturating_add(Weight::from_parts(39_228, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -372,12 +374,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `555 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 70_330_000 picoseconds. - Weight::from_parts(71_196_328, 4764) - // Standard Error: 2_923 - .saturating_add(Weight::from_parts(67_238, 0).saturating_mul(l.into())) - // Standard Error: 5_201 - .saturating_add(Weight::from_parts(89_102, 0).saturating_mul(s.into())) + // Minimum execution time: 68_441_000 picoseconds. + Weight::from_parts(68_961_970, 4764) + // Standard Error: 2_509 + .saturating_add(Weight::from_parts(83_345, 0).saturating_mul(l.into())) + // Standard Error: 4_464 + .saturating_add(Weight::from_parts(124_158, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -395,12 +397,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `658 + l * (25 ±0) + s * (36 ±0)` // Estimated: `6196` - // Minimum execution time: 70_235_000 picoseconds. - Weight::from_parts(71_960_020, 6196) - // Standard Error: 2_493 - .saturating_add(Weight::from_parts(64_835, 0).saturating_mul(l.into())) - // Standard Error: 4_436 - .saturating_add(Weight::from_parts(102_159, 0).saturating_mul(s.into())) + // Minimum execution time: 70_190_000 picoseconds. + Weight::from_parts(72_355_984, 6196) + // Standard Error: 2_295 + .saturating_add(Weight::from_parts(59_968, 0).saturating_mul(l.into())) + // Standard Error: 4_084 + .saturating_add(Weight::from_parts(87_090, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -418,12 +420,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 34_352_000 picoseconds. - Weight::from_parts(33_697_027, 4764) - // Standard Error: 2_008 - .saturating_add(Weight::from_parts(79_270, 0).saturating_mul(l.into())) - // Standard Error: 3_710 - .saturating_add(Weight::from_parts(60_691, 0).saturating_mul(s.into())) + // Minimum execution time: 33_911_000 picoseconds. + Weight::from_parts(33_243_006, 4764) + // Standard Error: 1_199 + .saturating_add(Weight::from_parts(70_586, 0).saturating_mul(l.into())) + // Standard Error: 2_214 + .saturating_add(Weight::from_parts(60_985, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -441,12 +443,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `482 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 37_467_000 picoseconds. - Weight::from_parts(36_866_847, 4764) - // Standard Error: 1_692 - .saturating_add(Weight::from_parts(57_882, 0).saturating_mul(l.into())) - // Standard Error: 3_124 - .saturating_add(Weight::from_parts(80_266, 0).saturating_mul(s.into())) + // Minimum execution time: 36_242_000 picoseconds. + Weight::from_parts(35_812_994, 4764) + // Standard Error: 1_351 + .saturating_add(Weight::from_parts(68_256, 0).saturating_mul(l.into())) + // Standard Error: 2_496 + .saturating_add(Weight::from_parts(66_171, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -464,12 +466,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `555 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 41_497_000 picoseconds. - Weight::from_parts(38_763_834, 4764) - // Standard Error: 2_030 - .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) - // Standard Error: 3_750 - .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) + // Minimum execution time: 37_817_000 picoseconds. + Weight::from_parts(37_397_127, 4764) + // Standard Error: 1_665 + .saturating_add(Weight::from_parts(72_049, 0).saturating_mul(l.into())) + // Standard Error: 3_075 + .saturating_add(Weight::from_parts(60_973, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/substrate/frame/whitelist/Cargo.toml b/substrate/frame/whitelist/Cargo.toml index 5d9a362f9aacb799cbbf3bd291c01a68f40bbd9e..1a867f8075d12a35f82e8fe37f2cf25230b710a3 100644 --- a/substrate/frame/whitelist/Cargo.toml +++ b/substrate/frame/whitelist/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-whitelist" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/frame/whitelist/src/mock.rs b/substrate/frame/whitelist/src/mock.rs index 200e589c6aa91a3b2bf2bd269d2abf36e059cc92..e323e806b8152b3765f60af46a59ec151f147154 100644 --- a/substrate/frame/whitelist/src/mock.rs +++ b/substrate/frame/whitelist/src/mock.rs @@ -21,16 +21,9 @@ use crate as pallet_whitelist; -use frame_support::{ - construct_runtime, derive_impl, - traits::{ConstU32, ConstU64, Nothing}, -}; +use frame_support::{construct_runtime, derive_impl, traits::ConstU64}; use frame_system::EnsureRoot; -use sp_core::H256; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; +use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -46,29 +39,8 @@ construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = Nothing; - type BlockWeights = (); - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Nonce = u64; - type Hash = H256; - type RuntimeCall = RuntimeCall; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -85,7 +57,6 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } impl pallet_preimage::Config for Test { diff --git a/substrate/frame/whitelist/src/weights.rs b/substrate/frame/whitelist/src/weights.rs index de42c5a5841cf8c2d3aea1227dafd35ec461b234..13b653f6ca7d367b83d5771f4805a61d8a7e40fd 100644 --- a/substrate/frame/whitelist/src/weights.rs +++ b/substrate/frame/whitelist/src/weights.rs @@ -15,16 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_whitelist +//! Autogenerated weights for `pallet_whitelist` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 +// ./target/production/substrate-node // benchmark // pallet // --chain=dev @@ -35,12 +35,11 @@ // --no-median-slopes // --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/whitelist/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/whitelist/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +49,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_whitelist. +/// Weight functions needed for `pallet_whitelist`. pub trait WeightInfo { fn whitelist_call() -> Weight; fn remove_whitelisted_call() -> Weight; @@ -58,133 +57,149 @@ pub trait WeightInfo { fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight; } -/// Weights for pallet_whitelist using the Substrate node and recommended hardware. +/// Weights for `pallet_whitelist` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn whitelist_call() -> Weight { // Proof Size summary in bytes: - // Measured: `217` + // Measured: `317` // Estimated: `3556` - // Minimum execution time: 19_914_000 picoseconds. - Weight::from_parts(20_892_000, 3556) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Minimum execution time: 18_503_000 picoseconds. + Weight::from_parts(19_497_000, 3556) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: - // Measured: `346` + // Measured: `446` // Estimated: `3556` - // Minimum execution time: 18_142_000 picoseconds. - Weight::from_parts(18_529_000, 3556) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Minimum execution time: 17_633_000 picoseconds. + Weight::from_parts(18_196_000, 3556) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:1 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `422 + n * (1 ±0)` - // Estimated: `3886 + n * (1 ±0)` - // Minimum execution time: 30_671_000 picoseconds. - Weight::from_parts(31_197_000, 3886) + // Measured: `522 + n * (1 ±0)` + // Estimated: `3986 + n * (1 ±0)` + // Minimum execution time: 28_020_000 picoseconds. + Weight::from_parts(28_991_000, 3986) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_163, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(1_171, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 10000]`. fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `346` + // Measured: `446` // Estimated: `3556` - // Minimum execution time: 22_099_000 picoseconds. - Weight::from_parts(23_145_477, 3556) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_422, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Minimum execution time: 21_916_000 picoseconds. + Weight::from_parts(23_082_065, 3556) + // Standard Error: 6 + .saturating_add(Weight::from_parts(1_388, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn whitelist_call() -> Weight { // Proof Size summary in bytes: - // Measured: `217` + // Measured: `317` // Estimated: `3556` - // Minimum execution time: 19_914_000 picoseconds. - Weight::from_parts(20_892_000, 3556) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Minimum execution time: 18_503_000 picoseconds. + Weight::from_parts(19_497_000, 3556) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: - // Measured: `346` + // Measured: `446` // Estimated: `3556` - // Minimum execution time: 18_142_000 picoseconds. - Weight::from_parts(18_529_000, 3556) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Minimum execution time: 17_633_000 picoseconds. + Weight::from_parts(18_196_000, 3556) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:1 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: Measured) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `422 + n * (1 ±0)` - // Estimated: `3886 + n * (1 ±0)` - // Minimum execution time: 30_671_000 picoseconds. - Weight::from_parts(31_197_000, 3886) + // Measured: `522 + n * (1 ±0)` + // Estimated: `3986 + n * (1 ±0)` + // Minimum execution time: 28_020_000 picoseconds. + Weight::from_parts(28_991_000, 3986) // Standard Error: 0 - .saturating_add(Weight::from_parts(1_163, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(1_171, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } - /// Storage: Whitelist WhitelistedCall (r:1 w:1) - /// Proof: Whitelist WhitelistedCall (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen) - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 10000]`. fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `346` + // Measured: `446` // Estimated: `3556` - // Minimum execution time: 22_099_000 picoseconds. - Weight::from_parts(23_145_477, 3556) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_422, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Minimum execution time: 21_916_000 picoseconds. + Weight::from_parts(23_082_065, 3556) + // Standard Error: 6 + .saturating_add(Weight::from_parts(1_388, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/substrate/primitives/api/Cargo.toml b/substrate/primitives/api/Cargo.toml index 346b9c35fa8d7bfc9d06a6d7df6b5a7e0334c350..f4b1d13c520399a9d5960bf90958fdd5b154d10a 100644 --- a/substrate/primitives/api/Cargo.toml +++ b/substrate/primitives/api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-api" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,15 +21,18 @@ sp-api-proc-macro = { path = "proc-macro", default-features = false } sp-core = { path = "../core", default-features = false } sp-std = { path = "../std", default-features = false } sp-runtime = { path = "../runtime", default-features = false } +sp-runtime-interface = { path = "../runtime-interface", default-features = false } sp-externalities = { path = "../externalities", default-features = false, optional = true } sp-version = { path = "../version", default-features = false } sp-state-machine = { path = "../state-machine", default-features = false, optional = true } sp-trie = { path = "../trie", default-features = false, optional = true } hash-db = { version = "0.16.0", optional = true } -thiserror = { version = "1.0.48", optional = true } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +thiserror = { optional = true, workspace = true } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } sp-metadata-ir = { path = "../metadata-ir", default-features = false, optional = true } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] sp-test-primitives = { path = "../test-primitives" } @@ -46,6 +49,7 @@ std = [ "sp-externalities", "sp-externalities?/std", "sp-metadata-ir?/std", + "sp-runtime-interface/std", "sp-runtime/std", "sp-state-machine/std", "sp-std/std", diff --git a/substrate/primitives/api/proc-macro/Cargo.toml b/substrate/primitives/api/proc-macro/Cargo.toml index eff66d3fb29dbaa747e44bc9cd46a6706e1a02c4..f5406758687ae20e9ce13a2cf02959c5cccbd576 100644 --- a/substrate/primitives/api/proc-macro/Cargo.toml +++ b/substrate/primitives/api/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-api-proc-macro" -version = "4.0.0-dev" +version = "15.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,8 +19,8 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -quote = "1.0.28" -syn = { version = "2.0.48", features = ["extra-traits", "fold", "full", "visit"] } +quote = { workspace = true } +syn = { features = ["extra-traits", "fold", "full", "visit"], workspace = true } proc-macro2 = "1.0.56" blake2 = { version = "0.10.4", default-features = false } proc-macro-crate = "3.0.0" diff --git a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs index 2b1e65ec88524a0236a464005fb2d1215c1157fe..e34e4c0e7672164afb41702ab3bae87082df9062 100644 --- a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -456,6 +456,7 @@ impl<'a> ToClientSideDecl<'a> { |err| #crate_::ApiError::FailedToDecodeReturnValue { function: #function_name, error: err, + raw: r.clone(), } ) ) 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 fd81fdb624c1f33bef6d5b71e759648e75899064..b7e5600a017a9978fb20286ad5724fc21936ca59 100644 --- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -237,12 +237,13 @@ fn generate_wasm_interface(impls: &[ItemImpl]) -> Result { #c::std_disabled! { #( #attrs )* #[no_mangle] - pub unsafe fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 { + #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), #c::__private::polkavm_export(abi = #c::__private::polkavm_abi))] + pub unsafe extern fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 { let mut #input = if input_len == 0 { &[0u8; 0] } else { unsafe { - #c::slice::from_raw_parts(input_data, input_len) + ::core::slice::from_raw_parts(input_data, input_len) } }; @@ -344,7 +345,7 @@ fn generate_runtime_api_base_structures() -> Result { &self, backend: &B, parent_hash: Block::Hash, - ) -> core::result::Result< + ) -> ::core::result::Result< #crate_::StorageChanges, String > where Self: Sized { diff --git a/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs index c1339ff6621b389e32c2f9ca2a7f19079c2d269e..1761e0ac9dbf450c15adae2659119c918f300334 100644 --- a/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/mock_impl_runtime_apis.rs @@ -158,7 +158,7 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result::Hash, _: &<#block_type as #crate_::BlockT>::Header, - ) -> std::result::Result<(), #crate_::ApiError> { + ) -> std::result::Result<#crate_::__private::ExtrinsicInclusionMode, #crate_::ApiError> { unimplemented!("`Core::initialize_block` not implemented for runtime api mocks") } } diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index 0a9b334a96f8ea0cfdcd48c4cebbfd88e4ddfe24..a945b9f21f3cff60d6a9d5e24aa07704a1dc8d31 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -101,10 +101,13 @@ pub mod __private { generic::BlockId, traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, NumberFor}, transaction_validity::TransactionValidity, - RuntimeString, TransactionOutcome, + ExtrinsicInclusionMode, RuntimeString, TransactionOutcome, }; pub use sp_std::{mem, slice, vec}; pub use sp_version::{create_apis_vec, ApiId, ApisVec, RuntimeVersion}; + + #[cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), substrate_runtime))] + pub use sp_runtime_interface::polkavm::{polkavm_abi, polkavm_export}; } #[cfg(feature = "std")] @@ -112,11 +115,11 @@ pub use sp_core::traits::CallContext; use sp_core::OpaqueMetadata; #[cfg(feature = "std")] use sp_externalities::{Extension, Extensions}; -use sp_runtime::traits::Block as BlockT; #[cfg(feature = "std")] use sp_runtime::traits::HashingFor; #[cfg(feature = "std")] pub use sp_runtime::TransactionOutcome; +use sp_runtime::{traits::Block as BlockT, ExtrinsicInclusionMode}; #[cfg(feature = "std")] pub use sp_state_machine::StorageProof; #[cfg(feature = "std")] @@ -277,7 +280,7 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// ```rust /// use sp_version::create_runtime_str; /// # -/// # use sp_runtime::traits::Block as BlockT; +/// # use sp_runtime::{ExtrinsicInclusionMode, traits::Block as BlockT}; /// # use sp_test_primitives::Block; /// # /// # /// The declaration of the `Runtime` type is done by the `construct_runtime!` macro @@ -304,7 +307,9 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// # unimplemented!() /// # } /// # fn execute_block(_block: Block) {} -/// # fn initialize_block(_header: &::Header) {} +/// # fn initialize_block(_header: &::Header) -> ExtrinsicInclusionMode { +/// # unimplemented!() +/// # } /// # } /// /// impl self::Balance for Runtime { @@ -537,11 +542,12 @@ pub fn init_runtime_logger() { #[cfg(feature = "std")] #[derive(Debug, thiserror::Error)] pub enum ApiError { - #[error("Failed to decode return value of {function}")] + #[error("Failed to decode return value of {function}: {error} raw data: {raw:?}")] FailedToDecodeReturnValue { function: &'static str, #[source] error: codec::Error, + raw: Vec, }, #[error("Failed to convert return value from runtime to node of {function}")] FailedToConvertReturnValue { @@ -797,15 +803,18 @@ pub fn deserialize_runtime_api_info(bytes: [u8; RUNTIME_API_INFO_SIZE]) -> ([u8; decl_runtime_apis! { /// The `Core` runtime api that every Substrate runtime needs to implement. #[core_trait] - #[api_version(4)] + #[api_version(5)] pub trait Core { /// Returns the version of the runtime. fn version() -> RuntimeVersion; /// Execute the given block. fn execute_block(block: Block); /// Initialize a block with the given header. + #[changed_in(5)] #[renamed("initialise_block", 2)] fn initialize_block(header: &::Header); + /// Initialize a block with the given header and return the runtime executive mode. + fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode; } /// The `Metadata` api trait that returns metadata for the runtime. diff --git a/substrate/primitives/api/test/Cargo.toml b/substrate/primitives/api/test/Cargo.toml index b0975082c44ecde816cfc332c940dfbd1783b04b..3a90553bbf000c1b1ea70ae11e7532a456f1c2f2 100644 --- a/substrate/primitives/api/test/Cargo.toml +++ b/substrate/primitives/api/test/Cargo.toml @@ -31,7 +31,7 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive [dev-dependencies] criterion = "0.4.0" futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } sp-core = { path = "../../core" } static_assertions = "1.1.0" diff --git a/substrate/primitives/api/test/tests/decl_and_impl.rs b/substrate/primitives/api/test/tests/decl_and_impl.rs index d68470551d20061aa09d277a9999db81654ff47d..211a08561fd4bcf601d9508d96447fc07f82657f 100644 --- a/substrate/primitives/api/test/tests/decl_and_impl.rs +++ b/substrate/primitives/api/test/tests/decl_and_impl.rs @@ -139,7 +139,7 @@ impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs index 43718e4cd04a3f25fcc805edec3c14283f1f5f56..262a874213a5662c0c87a79989b620c661e707c7 100644 --- a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs +++ b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.rs @@ -40,7 +40,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/impl_missing_version.rs b/substrate/primitives/api/test/tests/ui/impl_missing_version.rs index 560257b5168c921ce112a56fcbd7e9c303363c77..58850ab343fbb7bcc2c04b01c7aec9cb881023d0 100644 --- a/substrate/primitives/api/test/tests/ui/impl_missing_version.rs +++ b/substrate/primitives/api/test/tests/ui/impl_missing_version.rs @@ -45,7 +45,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.rs b/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.rs index 4647a78809ab4bcb68dfc8e367e6423e83182956..e6533b4ccda6cd2941d109678019d02a6cde0306 100644 --- a/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.rs +++ b/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.rs @@ -25,6 +25,7 @@ sp_api::decl_runtime_apis! { } } +#[allow(unused_imports)] mod second { sp_api::decl_runtime_apis! { pub trait Api { diff --git a/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.stderr b/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.stderr index 2197bbc99cfb1dcd4cd76c627a6280882c5517e2..c401c7076e5cd018c72f91925f3e09276e96238c 100644 --- a/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.stderr +++ b/substrate/primitives/api/test/tests/ui/impl_two_traits_with_same_name.stderr @@ -1,11 +1,11 @@ error: Two traits with the same name detected! The trait name is used to generate its ID. Please rename one trait at the declaration! - --> tests/ui/impl_two_traits_with_same_name.rs:41:15 + --> tests/ui/impl_two_traits_with_same_name.rs:42:15 | -41 | impl second::Api for Runtime { +42 | impl second::Api for Runtime { | ^^^ error: First trait implementation. - --> tests/ui/impl_two_traits_with_same_name.rs:37:13 + --> tests/ui/impl_two_traits_with_same_name.rs:38:13 | -37 | impl self::Api for Runtime { +38 | impl self::Api for Runtime { | ^^^ diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs b/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs index 6ead545f85a11e8f0382331e77145acb4a053d5e..70f75d065154afcb807e1712c95fc24eca5267b1 100644 --- a/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs +++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method.rs @@ -44,7 +44,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs index 8eebc1d79babcbd6fb95876a530a920397c035a6..63032000040b7d19f8e31fc4ac3221369aef9e7e 100644 --- a/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs +++ b/substrate/primitives/api/test/tests/ui/missing_versioned_method_multiple_vers.rs @@ -47,7 +47,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs b/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs index 594556d57be50dbd151de486dc22a63a733b933b..0858813bc99941be005e0c0141b2defc3a96a3d3 100644 --- a/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs +++ b/substrate/primitives/api/test/tests/ui/positive_cases/custom_where_bound.rs @@ -51,7 +51,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs b/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs index ae573238ffe144d26b7e5dd4528e8a9e5ab88a9e..3e0cb79156c8bf5c218e29e3024d181a69fc4da6 100644 --- a/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs +++ b/substrate/primitives/api/test/tests/ui/positive_cases/default_impls.rs @@ -46,7 +46,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs index 921bf0d04351dac4ddcdd18030619f1ebcf15008..b2caea7ab7e44d910628d0ac834017e66e7e5d2e 100644 --- a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs +++ b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.rs @@ -42,7 +42,7 @@ sp_api::impl_runtime_apis! { fn execute_block(_: Block) { unimplemented!() } - fn initialize_block(_: &::Header) { + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { unimplemented!() } } diff --git a/substrate/primitives/application-crypto/Cargo.toml b/substrate/primitives/application-crypto/Cargo.toml index d8aa2689aa271d4b9764aa55997ea7e2978d53c3..6f90a2b6262e50d7edd23e415924a8259f0a0d96 100644 --- a/substrate/primitives/application-crypto/Cargo.toml +++ b/substrate/primitives/application-crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-application-crypto" -version = "23.0.0" +version = "30.0.0" authors.workspace = true edition.workspace = true description = "Provides facilities for generating application specific crypto wrapper types." @@ -21,7 +21,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-core = { path = "../core", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, optional = true, features = ["alloc", "derive"] } +serde = { optional = true, features = ["alloc", "derive"], workspace = true } sp-std = { path = "../std", default-features = false } sp-io = { path = "../io", default-features = false } diff --git a/substrate/primitives/arithmetic/Cargo.toml b/substrate/primitives/arithmetic/Cargo.toml index 47d2902e267d03776566be3680fed8ed6b36cfa5..301821ad6893117661e439921f23d4d5d48f16b5 100644 --- a/substrate/primitives/arithmetic/Cargo.toml +++ b/substrate/primitives/arithmetic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-arithmetic" -version = "16.0.0" +version = "23.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -24,14 +24,14 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = integer-sqrt = "0.1.2" num-traits = { version = "0.2.17", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"], optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } static_assertions = "1.1.0" sp-std = { path = "../std", default-features = false } [dev-dependencies] criterion = "0.4.0" primitive-types = "0.12.0" -sp-core = { path = "../core", features = ["full_crypto"] } +sp-crypto-hashing = { path = "../crypto/hashing" } rand = "0.8.5" [features] @@ -41,7 +41,7 @@ std = [ "num-traits/std", "scale-info/std", "serde/std", - "sp-core/std", + "sp-crypto-hashing/std", "sp-std/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/arithmetic/fuzzer/Cargo.toml b/substrate/primitives/arithmetic/fuzzer/Cargo.toml index b881e8d46dbdcc4576bc685e03bbea23a2d7c00c..ace30e9c90e91d6b4c784cf090329b91e8095fad 100644 --- a/substrate/primitives/arithmetic/fuzzer/Cargo.toml +++ b/substrate/primitives/arithmetic/fuzzer/Cargo.toml @@ -17,7 +17,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -arbitrary = "1.3.0" +arbitrary = "1.3.2" fraction = "0.13.1" honggfuzz = "0.5.49" num-bigint = "0.4.3" diff --git a/substrate/primitives/arithmetic/src/helpers_128bit.rs b/substrate/primitives/arithmetic/src/helpers_128bit.rs index 9b9c74ba55774984f3891968861317208b4bb5d2..cbbad58443f92332277210f95a032c0bf5ed8e1a 100644 --- a/substrate/primitives/arithmetic/src/helpers_128bit.rs +++ b/substrate/primitives/arithmetic/src/helpers_128bit.rs @@ -22,7 +22,7 @@ //! multiplication implementation provided there. use crate::{biguint, Rounding}; -use sp_std::cmp::{max, min}; +use core::cmp::{max, min}; /// Helper gcd function used in Rational128 implementation. pub fn gcd(a: u128, b: u128) -> u128 { @@ -287,7 +287,7 @@ mod tests { } fn random_u128(seed: u32) -> u128 { - u128::decode(&mut &seed.using_encoded(sp_core::hashing::twox_128)[..]).unwrap_or(0) + u128::decode(&mut &seed.using_encoded(sp_crypto_hashing::twox_128)[..]).unwrap_or(0) } #[test] diff --git a/substrate/primitives/arithmetic/src/traits.rs b/substrate/primitives/arithmetic/src/traits.rs index 6fcc8248539ca1649a11014615ad5ab48da53119..66ec19c1b573a5b308d68555713d618e2595959b 100644 --- a/substrate/primitives/arithmetic/src/traits.rs +++ b/substrate/primitives/arithmetic/src/traits.rs @@ -18,6 +18,9 @@ //! Primitive traits for the runtime arithmetic. use codec::HasCompact; +use core::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, Shr, Sub, SubAssign, +}; pub use ensure::{ ensure_pow, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, @@ -28,9 +31,6 @@ pub use num_traits::{ checked_pow, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub, One, Signed, Unsigned, Zero, }; -use sp_std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, Shr, Sub, SubAssign, -}; use crate::MultiplyRational; @@ -262,7 +262,7 @@ pub trait Saturating { Self: One, { let mut o = Self::one(); - sp_std::mem::swap(&mut o, self); + core::mem::swap(&mut o, self); *self = o.saturating_add(One::one()); } @@ -272,7 +272,7 @@ pub trait Saturating { Self: One, { let mut o = Self::one(); - sp_std::mem::swap(&mut o, self); + core::mem::swap(&mut o, self); *self = o.saturating_sub(One::one()); } @@ -282,7 +282,7 @@ pub trait Saturating { Self: One, { let mut o = Self::one(); - sp_std::mem::swap(&mut o, self); + core::mem::swap(&mut o, self); *self = o.saturating_add(amount); } @@ -292,7 +292,7 @@ pub trait Saturating { Self: One, { let mut o = Self::one(); - sp_std::mem::swap(&mut o, self); + core::mem::swap(&mut o, self); *self = o.saturating_sub(amount); } } @@ -949,7 +949,7 @@ mod ensure { } } - impl sp_std::ops::Mul for Signum { + impl core::ops::Mul for Signum { type Output = Self; fn mul(self, rhs: Self) -> Self { diff --git a/substrate/primitives/authority-discovery/Cargo.toml b/substrate/primitives/authority-discovery/Cargo.toml index 82ec5a3eb9a492eb06b146f9c6e89ff83b086ff0..70f00897cdd6e804c98363016a8baabc9edde415 100644 --- a/substrate/primitives/authority-discovery/Cargo.toml +++ b/substrate/primitives/authority-discovery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-authority-discovery" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true description = "Authority discovery primitives" edition.workspace = true diff --git a/substrate/primitives/block-builder/Cargo.toml b/substrate/primitives/block-builder/Cargo.toml index de1ffd9d9e64a4708370db0f5d1d44eee56d5870..c1317facd7fc532267c8039d71b2d78b3d3f61c4 100644 --- a/substrate/primitives/block-builder/Cargo.toml +++ b/substrate/primitives/block-builder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-block-builder" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/blockchain/Cargo.toml b/substrate/primitives/blockchain/Cargo.toml index 38b3b2030dc62a77fd7060d2fc241df8a305be71..9d13d627eebc36dcc5bdce3efd073a7dc29012a0 100644 --- a/substrate/primitives/blockchain/Cargo.toml +++ b/substrate/primitives/blockchain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-blockchain" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,10 +19,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } parking_lot = "0.12.1" schnellru = "0.2.1" -thiserror = "1.0.48" +thiserror = { workspace = true } sp-api = { path = "../api" } sp-consensus = { path = "../consensus/common" } sp-database = { path = "../database" } diff --git a/substrate/primitives/consensus/aura/Cargo.toml b/substrate/primitives/consensus/aura/Cargo.toml index 6c797e15ae805e4d257b9fac1b057bfe8842b1e3..52f6bc22ba4080fbe1fccde71cf06ffa2c1a5327 100644 --- a/substrate/primitives/consensus/aura/Cargo.toml +++ b/substrate/primitives/consensus/aura/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus-aura" -version = "0.10.0-dev" +version = "0.32.0" authors.workspace = true description = "Primitives for Aura consensus" edition.workspace = true diff --git a/substrate/primitives/consensus/aura/src/inherents.rs b/substrate/primitives/consensus/aura/src/inherents.rs index 1ef25feb0ad62ef71a96c9bd17a6dc3ac8385cb8..733081dd790a1e94acb8c033192727df74bfc26c 100644 --- a/substrate/primitives/consensus/aura/src/inherents.rs +++ b/substrate/primitives/consensus/aura/src/inherents.rs @@ -69,7 +69,7 @@ impl InherentDataProvider { } #[cfg(feature = "std")] -impl sp_std::ops::Deref for InherentDataProvider { +impl core::ops::Deref for InherentDataProvider { type Target = InherentType; fn deref(&self) -> &Self::Target { diff --git a/substrate/primitives/consensus/babe/Cargo.toml b/substrate/primitives/consensus/babe/Cargo.toml index e48b4b4817b71db8e65ba29f429b8bf8e30ec913..8b3006f79a7ffc87a69a7eb2921f4b4f0533d1a1 100644 --- a/substrate/primitives/consensus/babe/Cargo.toml +++ b/substrate/primitives/consensus/babe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus-babe" -version = "0.10.0-dev" +version = "0.32.0" authors.workspace = true description = "Primitives for BABE consensus" edition.workspace = true @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] async-trait = { version = "0.1.74", optional = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"], optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-api = { path = "../../api", default-features = false } sp-application-crypto = { path = "../../application-crypto", default-features = false } sp-consensus-slots = { path = "../slots", default-features = false } diff --git a/substrate/primitives/consensus/babe/src/inherents.rs b/substrate/primitives/consensus/babe/src/inherents.rs index b01bd1c9221f2043a799f93450280ac7b280401b..54b7b64401673927602a3e816cc9d5dcb9ede14a 100644 --- a/substrate/primitives/consensus/babe/src/inherents.rs +++ b/substrate/primitives/consensus/babe/src/inherents.rs @@ -18,7 +18,6 @@ //! Inherents for BABE use sp_inherents::{Error, InherentData, InherentIdentifier}; -use sp_std::result::Result; /// The BABE inherent identifier. pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot"; @@ -75,7 +74,7 @@ impl InherentDataProvider { } #[cfg(feature = "std")] -impl sp_std::ops::Deref for InherentDataProvider { +impl core::ops::Deref for InherentDataProvider { type Target = InherentType; fn deref(&self) -> &Self::Target { diff --git a/substrate/primitives/consensus/babe/src/lib.rs b/substrate/primitives/consensus/babe/src/lib.rs index d6b2cdd55e0daddf7e49a132d48bbc8c1741736a..ff0b4568226ef129305d6ab516d6b7032c54c2a0 100644 --- a/substrate/primitives/consensus/babe/src/lib.rs +++ b/substrate/primitives/consensus/babe/src/lib.rs @@ -256,6 +256,12 @@ pub struct BabeEpochConfiguration { pub allowed_slots: AllowedSlots, } +impl Default for BabeEpochConfiguration { + fn default() -> Self { + Self { c: (1, 4), allowed_slots: AllowedSlots::PrimaryAndSecondaryVRFSlots } + } +} + /// Verifies the equivocation proof by making sure that: both headers have /// different hashes, are targetting the same slot, and have valid signatures by /// the same authority. diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml index 93a9be17747cce6af5dd8e4c5e963e81dce559ca..8ab817d52ef93469959c4de17a459a319b07b4e0 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus-beefy" -version = "4.0.0-dev" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,16 +17,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, optional = true, features = ["alloc", "derive"] } +serde = { optional = true, features = ["alloc", "derive"], workspace = true } sp-api = { path = "../../api", default-features = false } sp-application-crypto = { path = "../../application-crypto", default-features = false } sp-core = { path = "../../core", default-features = false } +sp-crypto-hashing = { path = "../../crypto/hashing", default-features = false } sp-io = { path = "../../io", default-features = false } sp-mmr-primitives = { path = "../../merkle-mountain-range", default-features = false } sp-runtime = { path = "../../runtime", default-features = false } +sp-keystore = { path = "../../keystore", default-features = false } sp-std = { path = "../../std", default-features = false } strum = { version = "0.24.1", features = ["derive"], default-features = false } -lazy_static = "1.4.0" +lazy_static = { version = "1.4.0", optional = true } [dev-dependencies] array-bytes = "6.1" @@ -36,12 +38,15 @@ w3f-bls = { version = "0.1.3", features = ["std"] } default = ["std"] std = [ "codec/std", + "dep:lazy_static", "scale-info/std", "serde/std", "sp-api/std", "sp-application-crypto/std", "sp-core/std", + "sp-crypto-hashing/std", "sp-io/std", + "sp-keystore/std", "sp-mmr-primitives/std", "sp-runtime/std", "sp-std/std", diff --git a/substrate/primitives/consensus/beefy/src/commitment.rs b/substrate/primitives/consensus/beefy/src/commitment.rs index 5b6ef9ae5ab36e94b045b1e6176e8b712cc20ceb..335c6b604f044872ca5dc20319d0e2de94313606 100644 --- a/substrate/primitives/consensus/beefy/src/commitment.rs +++ b/substrate/primitives/consensus/beefy/src/commitment.rs @@ -97,6 +97,19 @@ pub struct SignedCommitment { pub signatures: Vec>, } +impl sp_std::fmt::Display + for SignedCommitment +{ + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + let signatures_count = self.signatures.iter().filter(|s| s.is_some()).count(); + write!( + f, + "SignedCommitment(commitment: {:?}, signatures_count: {})", + self.commitment, signatures_count + ) + } +} + impl SignedCommitment { /// Return the number of collected signatures. pub fn no_of_signatures(&self) -> usize { @@ -241,6 +254,14 @@ pub enum VersionedFinalityProof { V1(SignedCommitment), } +impl sp_std::fmt::Display for VersionedFinalityProof { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + match self { + VersionedFinalityProof::V1(sc) => write!(f, "VersionedFinalityProof::V1({})", sc), + } + } +} + impl From> for VersionedFinalityProof { fn from(commitment: SignedCommitment) -> Self { VersionedFinalityProof::V1(commitment) @@ -253,7 +274,8 @@ mod tests { use super::*; use crate::{ecdsa_crypto::Signature as EcdsaSignature, known_payloads}; use codec::Decode; - use sp_core::{keccak_256, Pair}; + use sp_core::Pair; + use sp_crypto_hashing::keccak_256; #[cfg(feature = "bls-experimental")] use crate::bls_crypto::Signature as BlsSignature; @@ -397,7 +419,7 @@ mod tests { assert_eq!( encoded, array_bytes::hex2bytes_unchecked( - "046d68343048656c6c6f20576f726c642105000000000000000000000000000000000000000000000004300400000008558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba01667603fc041cf9d7147d22bf54b15e5778893d6986b71a929747befd3b4d233fbe668bc480e8865116b94db46ca25a01e03c71955f2582604e415da68f2c3c406b9d5f4ad416230ec5453f05ac16a50d8d0923dfb0413cc956ae3fa6334465bd1f2cacec8e9cd606438390fe2a29dc052d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a00df61d3b2be0963eb6caa243cc505d327aec73e1bb7ffe9a14b1354b0c406792ac6d6f47c06987c15dec9993f43eefa001d866fe0850d986702c414840f0d9ec0fdc04832ef91ae37c8d49e2f573ca50cb37f152801d489a19395cb04e5fc8f2ab6954b58a3bcc40ef9b6409d2ff7ef07" + "046d68343048656c6c6f20576f726c642105000000000000000000000000000000000000000000000004300400000008558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba015dd1c9b2237e54baa93d232cdf83a430b58a5efbc2f86ca1bab173a315ff6f15bef161425750c028055e9a23947b73002889a8b22168628438875a8ef25d76db998a80187b50719471286f054f3b3809b77a0cd87d7fe9c1a9d5d562683e25a70610f0804e92340549a43a7159b77b0c2d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a001074884b6998c82331bd57ffa0a02cbfd02483c765b9216eab6a1fc119206236bf7971be68acaebff7400edee943240006a6096c9cfa65e9eb4e67f025c27112d14b4574fb208c439500f45cf3a8060f6cf009044f3141cce0364a7c2710a19b1bdf4abf27f86e5e3db08bddd35a7d12" ) ); } diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs index e31c53237be24523da370725176265a978576835..1c3801e3a506bc9c04976713e1fae9cf02511a3d 100644 --- a/substrate/primitives/consensus/beefy/src/lib.rs +++ b/substrate/primitives/consensus/beefy/src/lib.rs @@ -32,20 +32,22 @@ //! while GRANDPA uses `ed25519`. mod commitment; -pub mod mmr; mod payload; -#[cfg(feature = "std")] -mod test_utils; + +pub mod mmr; pub mod witness; +/// Test utilities +#[cfg(feature = "std")] +pub mod test_utils; + pub use commitment::{Commitment, SignedCommitment, VersionedFinalityProof}; pub use payload::{known_payloads, BeefyPayloadId, Payload, PayloadProvider}; -#[cfg(feature = "std")] -pub use test_utils::*; use codec::{Codec, Decode, Encode}; +use core::fmt::{Debug, Display}; use scale_info::TypeInfo; -use sp_application_crypto::RuntimeAppPublic; +use sp_application_crypto::{AppCrypto, AppPublic, ByteArray, RuntimeAppPublic}; use sp_core::H256; use sp_runtime::traits::{Hash, Keccak256, NumberFor}; use sp_std::prelude::*; @@ -63,6 +65,25 @@ pub trait BeefyAuthorityId: RuntimeAppPublic { fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool; } +/// Hasher used for BEEFY signatures. +pub type BeefySignatureHasher = sp_runtime::traits::Keccak256; + +/// A trait bound which lists all traits which are required to be implemented by +/// a BEEFY AuthorityId type in order to be able to be used in BEEFY Keystore +pub trait AuthorityIdBound: + Codec + + Debug + + Clone + + AsRef<[u8]> + + ByteArray + + AppPublic + + AppCrypto + + RuntimeAppPublic + + Display + + BeefyAuthorityId +{ +} + /// BEEFY cryptographic types for ECDSA crypto /// /// This module basically introduces four crypto types: @@ -74,7 +95,7 @@ pub trait BeefyAuthorityId: RuntimeAppPublic { /// Your code should use the above types as concrete types for all crypto related /// functionality. pub mod ecdsa_crypto { - use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; + use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; use sp_application_crypto::{app_crypto, ecdsa}; use sp_core::crypto::Wraps; @@ -101,6 +122,7 @@ pub mod ecdsa_crypto { } } } + impl AuthorityIdBound for AuthorityId {} } /// BEEFY cryptographic types for BLS crypto @@ -116,7 +138,7 @@ pub mod ecdsa_crypto { #[cfg(feature = "bls-experimental")] pub mod bls_crypto { - use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; + use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; use sp_application_crypto::{app_crypto, bls377}; use sp_core::{bls377::Pair as BlsPair, crypto::Wraps, Pair as _}; @@ -134,13 +156,14 @@ pub mod bls_crypto { { fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { // `w3f-bls` library uses IETF hashing standard and as such does not expose - // a choice of hash to field function. + // a choice of hash-to-field function. // We are directly calling into the library to avoid introducing new host call. // and because BeefyAuthorityId::verify is being called in the runtime so we don't have BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref()) } } + impl AuthorityIdBound for AuthorityId {} } /// BEEFY cryptographic types for (ECDSA,BLS) crypto pair @@ -155,7 +178,7 @@ pub mod bls_crypto { /// functionality. #[cfg(feature = "bls-experimental")] pub mod ecdsa_bls_crypto { - use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; + use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE}; use sp_application_crypto::{app_crypto, ecdsa_bls377}; use sp_core::{crypto::Wraps, ecdsa_bls377::Pair as EcdsaBlsPair}; @@ -187,6 +210,8 @@ pub mod ecdsa_bls_crypto { ) } } + + impl AuthorityIdBound for AuthorityId {} } /// The `ConsensusEngineId` of BEEFY. @@ -437,7 +462,8 @@ sp_api::decl_runtime_apis! { mod tests { use super::*; use sp_application_crypto::ecdsa::{self, Public}; - use sp_core::{blake2_256, crypto::Wraps, keccak_256, Pair}; + use sp_core::crypto::{Pair, Wraps}; + use sp_crypto_hashing::{blake2_256, keccak_256}; use sp_runtime::traits::{BlakeTwo256, Keccak256}; #[test] diff --git a/substrate/primitives/consensus/beefy/src/test_utils.rs b/substrate/primitives/consensus/beefy/src/test_utils.rs index b83f657af38e3e77c5d861d8f9e545ae54cf1e60..ec13c9c690046ae795fe50af3bfc775721dfb6ee 100644 --- a/substrate/primitives/consensus/beefy/src/test_utils.rs +++ b/substrate/primitives/consensus/beefy/src/test_utils.rs @@ -15,18 +15,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![cfg(feature = "std")] +#[cfg(feature = "bls-experimental")] +use crate::ecdsa_bls_crypto; +use crate::{ + ecdsa_crypto, AuthorityIdBound, BeefySignatureHasher, Commitment, EquivocationProof, Payload, + ValidatorSetId, VoteMessage, +}; +use sp_application_crypto::{AppCrypto, AppPair, RuntimeAppPublic, Wraps}; +use sp_core::{ecdsa, Pair}; +use sp_runtime::traits::Hash; -use crate::{ecdsa_crypto, Commitment, EquivocationProof, Payload, ValidatorSetId, VoteMessage}; use codec::Encode; -use sp_core::{ecdsa, keccak_256, Pair}; -use std::collections::HashMap; +use std::{collections::HashMap, marker::PhantomData}; use strum::IntoEnumIterator; /// Set of test accounts using [`crate::ecdsa_crypto`] types. #[allow(missing_docs)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] -pub enum Keyring { +pub enum Keyring { Alice, Bob, Charlie, @@ -35,71 +41,110 @@ pub enum Keyring { Ferdie, One, Two, + _Marker(PhantomData), } -impl Keyring { +/// Trait representing BEEFY specific generation and signing behavior of authority id +/// +/// Accepts custom hashing fn for the message and custom convertor fn for the signer. +pub trait BeefySignerAuthority: AppPair { + /// Generate and return signature for `message` using custom hashing `MsgHash` + fn sign_with_hasher(&self, message: &[u8]) -> ::Signature; +} + +impl BeefySignerAuthority for ::Pair +where + MsgHash: Hash, + ::Output: Into<[u8; 32]>, +{ + fn sign_with_hasher(&self, message: &[u8]) -> ::Signature { + let hashed_message = ::hash(message).into(); + self.as_inner_ref().sign_prehashed(&hashed_message).into() + } +} + +#[cfg(feature = "bls-experimental")] +impl BeefySignerAuthority for ::Pair +where + MsgHash: Hash, + ::Output: Into<[u8; 32]>, +{ + fn sign_with_hasher(&self, message: &[u8]) -> ::Signature { + self.as_inner_ref().sign_with_hasher::(&message).into() + } +} + +/// Implement Keyring functionalities generically over AuthorityId +impl Keyring +where + AuthorityId: AuthorityIdBound + From<<::Pair as AppCrypto>::Public>, + ::Pair: BeefySignerAuthority, + ::Signature: + Send + Sync + From<<::Pair as AppCrypto>::Signature>, +{ /// Sign `msg`. - pub fn sign(self, msg: &[u8]) -> ecdsa_crypto::Signature { - // todo: use custom signature hashing type - let msg = keccak_256(msg); - ecdsa::Pair::from(self).sign_prehashed(&msg).into() + pub fn sign(&self, msg: &[u8]) -> ::Signature { + let key_pair: ::Pair = self.pair(); + key_pair.sign_with_hasher(&msg).into() } /// Return key pair. - pub fn pair(self) -> ecdsa_crypto::Pair { - ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into() + pub fn pair(&self) -> ::Pair { + ::Pair::from_string(self.to_seed().as_str(), None) + .unwrap() + .into() } /// Return public key. - pub fn public(self) -> ecdsa_crypto::Public { - self.pair().public() + pub fn public(&self) -> AuthorityId { + self.pair().public().into() } /// Return seed string. - pub fn to_seed(self) -> String { + pub fn to_seed(&self) -> String { format!("//{}", self) } /// Get Keyring from public key. - pub fn from_public(who: &ecdsa_crypto::Public) -> Option { - Self::iter().find(|&k| &ecdsa_crypto::Public::from(k) == who) + pub fn from_public(who: &AuthorityId) -> Option> { + Self::iter().find(|k| k.public() == *who) } } lazy_static::lazy_static! { - static ref PRIVATE_KEYS: HashMap = - Keyring::iter().map(|i| (i, i.pair())).collect(); - static ref PUBLIC_KEYS: HashMap = - PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect(); + static ref PRIVATE_KEYS: HashMap, ecdsa_crypto::Pair> = + Keyring::iter().map(|i| (i.clone(), i.pair())).collect(); + static ref PUBLIC_KEYS: HashMap, ecdsa_crypto::Public> = + PRIVATE_KEYS.iter().map(|(name, pair)| (name.clone(), sp_application_crypto::Pair::public(pair))).collect(); } -impl From for ecdsa_crypto::Pair { - fn from(k: Keyring) -> Self { +impl From> for ecdsa_crypto::Pair { + fn from(k: Keyring) -> Self { k.pair() } } -impl From for ecdsa::Pair { - fn from(k: Keyring) -> Self { +impl From> for ecdsa::Pair { + fn from(k: Keyring) -> Self { k.pair().into() } } -impl From for ecdsa_crypto::Public { - fn from(k: Keyring) -> Self { +impl From> for ecdsa_crypto::Public { + fn from(k: Keyring) -> Self { (*PUBLIC_KEYS).get(&k).cloned().unwrap() } } /// Create a new `EquivocationProof` based on given arguments. pub fn generate_equivocation_proof( - vote1: (u64, Payload, ValidatorSetId, &Keyring), - vote2: (u64, Payload, ValidatorSetId, &Keyring), + vote1: (u64, Payload, ValidatorSetId, &Keyring), + vote2: (u64, Payload, ValidatorSetId, &Keyring), ) -> EquivocationProof { let signed_vote = |block_number: u64, payload: Payload, validator_set_id: ValidatorSetId, - keyring: &Keyring| { + keyring: &Keyring| { let commitment = Commitment { validator_set_id, block_number, payload }; let signature = keyring.sign(&commitment.encode()); VoteMessage { commitment, id: keyring.public(), signature } diff --git a/substrate/primitives/consensus/beefy/src/witness.rs b/substrate/primitives/consensus/beefy/src/witness.rs index 3f2c2bcbe282937ad5f0406d36c9358e9221f013..b633453340bbee211f49bc5afcdf5867570cf7c0 100644 --- a/substrate/primitives/consensus/beefy/src/witness.rs +++ b/substrate/primitives/consensus/beefy/src/witness.rs @@ -77,7 +77,8 @@ impl #[cfg(test)] mod tests { - use sp_core::{keccak_256, Pair}; + use sp_core::Pair; + use sp_crypto_hashing::keccak_256; use super::*; use codec::Decode; diff --git a/substrate/primitives/consensus/common/Cargo.toml b/substrate/primitives/consensus/common/Cargo.toml index afb7a9895fcdc8b684fcfab2eb40d3eb802ea10f..048e31b0265f57f0404b3777897a27f42fe3de68 100644 --- a/substrate/primitives/consensus/common/Cargo.toml +++ b/substrate/primitives/consensus/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus" -version = "0.10.0-dev" +version = "0.32.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,8 +19,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.74" futures = { version = "0.3.21", features = ["thread-pool"] } -log = "0.4.17" -thiserror = "1.0.48" +log = { workspace = true, default-features = true } +thiserror = { workspace = true } sp-core = { path = "../../core" } sp-inherents = { path = "../../inherents" } sp-runtime = { path = "../../runtime" } diff --git a/substrate/primitives/consensus/grandpa/Cargo.toml b/substrate/primitives/consensus/grandpa/Cargo.toml index be22f5b23df31d48aab90353608d9b607d7451b0..b06208a4308b355031adde70de72e0e136debda5 100644 --- a/substrate/primitives/consensus/grandpa/Cargo.toml +++ b/substrate/primitives/consensus/grandpa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus-grandpa" -version = "4.0.0-dev" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,9 +19,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } grandpa = { package = "finality-grandpa", version = "0.16.2", default-features = false, features = ["derive-codec"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", features = ["alloc", "derive"], default-features = false, optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-api = { path = "../../api", default-features = false } sp-application-crypto = { path = "../../application-crypto", default-features = false } sp-core = { path = "../../core", default-features = false } diff --git a/substrate/primitives/consensus/pow/Cargo.toml b/substrate/primitives/consensus/pow/Cargo.toml index e528d8365ced3721001381f881ad8df1aa2c33c1..8147b063f6d3324b1e942d825c272f94720854c0 100644 --- a/substrate/primitives/consensus/pow/Cargo.toml +++ b/substrate/primitives/consensus/pow/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus-pow" -version = "0.10.0-dev" +version = "0.32.0" authors.workspace = true description = "Primitives for Aura consensus" edition.workspace = true diff --git a/substrate/primitives/consensus/sassafras/Cargo.toml b/substrate/primitives/consensus/sassafras/Cargo.toml index 6d44bc6c5a8fe9b879962e1ddb102b5638d9dc1b..b707ad18b5b9c6d4649a31a0c8f6a4dd4eeaf1f8 100644 --- a/substrate/primitives/consensus/sassafras/Cargo.toml +++ b/substrate/primitives/consensus/sassafras/Cargo.toml @@ -20,7 +20,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] scale-codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["derive"], optional = true } +serde = { features = ["derive"], optional = true, workspace = true } sp-api = { path = "../../api", default-features = false } sp-application-crypto = { path = "../../application-crypto", default-features = false, features = ["bandersnatch-experimental"] } sp-consensus-slots = { path = "../slots", default-features = false } diff --git a/substrate/primitives/consensus/slots/Cargo.toml b/substrate/primitives/consensus/slots/Cargo.toml index 91bbd1663a9c3d867624acdfd0a17412a8d2a49c..8372b2b04a6b662cd76a07b6d7245ba9ba46b251 100644 --- a/substrate/primitives/consensus/slots/Cargo.toml +++ b/substrate/primitives/consensus/slots/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus-slots" -version = "0.10.0-dev" +version = "0.32.0" authors.workspace = true description = "Primitives for slots-based consensus" edition.workspace = true @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"], optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-std = { path = "../../std", default-features = false } sp-timestamp = { path = "../../timestamp", default-features = false } diff --git a/substrate/primitives/consensus/slots/src/lib.rs b/substrate/primitives/consensus/slots/src/lib.rs index 23cb7f7365adaa431e84c268c3210f4c6dfa42c8..eb3b3d3a449f2f8279f7183446941c4dedd2610a 100644 --- a/substrate/primitives/consensus/slots/src/lib.rs +++ b/substrate/primitives/consensus/slots/src/lib.rs @@ -155,9 +155,9 @@ impl SlotDuration { #[cfg(feature = "std")] impl SlotDuration { - /// Returns `self` as [`sp_std::time::Duration`]. - pub const fn as_duration(&self) -> sp_std::time::Duration { - sp_std::time::Duration::from_millis(self.0) + /// Returns `self` as [`core::time::Duration`]. + pub const fn as_duration(&self) -> core::time::Duration { + core::time::Duration::from_millis(self.0) } } diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 73de8057d80baaab439772186cd574a5182a7786..70ca7be6a2d1a3a4a5fabb95a901c281bc306e2d 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-core" -version = "21.0.0" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,16 +18,16 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } -serde = { version = "1.0.195", optional = true, default-features = false, features = ["alloc", "derive"] } -bounded-collections = { version = "0.1.8", default-features = false } +log = { workspace = true } +serde = { optional = true, features = ["alloc", "derive"], workspace = true } +bounded-collections = { version = "0.2.0", default-features = false } primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info"] } impl-serde = { version = "0.4.0", default-features = false, optional = true } hash-db = { version = "0.16.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } bs58 = { version = "0.5.0", default-features = false, optional = true } rand = { version = "0.8.5", features = ["small_rng"], optional = true } -substrate-bip39 = { version = "0.4.4", optional = true } +substrate-bip39 = { path = "../../utils/substrate-bip39", optional = true } bip39 = { version = "2.0.0", default-features = false } zeroize = { version = "1.4.3", default-features = false } secrecy = { version = "0.8.0", default-features = false } @@ -39,7 +39,7 @@ sp-storage = { path = "../storage", default-features = false } sp-externalities = { path = "../externalities", optional = true } futures = { version = "0.3.21", optional = true } dyn-clonable = { version = "0.9.0", optional = true } -thiserror = { version = "1.0.48", optional = true } +thiserror = { optional = true, workspace = true } tracing = { version = "0.1.29", optional = true } bitflags = "1.3" paste = "1.0.7" @@ -53,7 +53,7 @@ libsecp256k1 = { version = "0.7", default-features = false, features = ["static- schnorrkel = { version = "0.11.4", features = ["preaudit_deprecated"], default-features = false } merlin = { version = "3.0", default-features = false } secp256k1 = { version = "0.28.0", default-features = false, features = ["alloc", "recovery"], optional = true } -sp-core-hashing = { path = "hashing", default-features = false, optional = true } +sp-crypto-hashing = { path = "../crypto/hashing", default-features = false, optional = true } sp-runtime-interface = { path = "../runtime-interface", default-features = false } # bls crypto @@ -63,10 +63,9 @@ bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "e9782f9", [dev-dependencies] criterion = "0.4.0" -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } lazy_static = "1.4.0" regex = "1.6.0" -sp-core-hashing-proc-macro = { path = "hashing/proc-macro" } [[bench]] name = "bench" @@ -110,14 +109,14 @@ std = [ "secp256k1/std", "secrecy/alloc", "serde/std", - "sp-core-hashing/std", + "sp-crypto-hashing/std", "sp-debug-derive/std", "sp-externalities/std", "sp-runtime-interface/std", "sp-std/std", "sp-storage/std", "ss58-registry/std", - "substrate-bip39", + "substrate-bip39/std", "thiserror", "tracing", "w3f-bls?/std", @@ -136,7 +135,7 @@ serde = [ "primitive-types/serde_no_std", "scale-info/serde", "secrecy/alloc", - "sp-core-hashing", + "sp-crypto-hashing", "sp-storage/serde", ] @@ -149,7 +148,7 @@ full_crypto = [ "ed25519-zebra", "libsecp256k1", "secp256k1", - "sp-core-hashing", + "sp-crypto-hashing", "sp-runtime-interface/disable_target_static_assertions", ] diff --git a/substrate/primitives/core/benches/bench.rs b/substrate/primitives/core/benches/bench.rs index ffca819b638e56fd329a3184b962ae568bae9102..0527c2b178cbde1b4331fd62a1d1909cb2b3a032 100644 --- a/substrate/primitives/core/benches/bench.rs +++ b/substrate/primitives/core/benches/bench.rs @@ -12,66 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion}; -use sp_core::{ - crypto::Pair as _, - hashing::{blake2_128, twox_128}, -}; - -const MAX_KEY_SIZE: u32 = 32; - -fn get_key(key_size: u32) -> Vec { - use rand::{Rng, SeedableRng}; - - let rnd: [u8; 32] = rand::rngs::StdRng::seed_from_u64(12).gen(); - let mut rnd = rnd.iter().cycle(); - - (0..key_size).map(|_| *rnd.next().unwrap()).collect() -} - -fn bench_blake2_128(b: &mut Bencher, key: &Vec) { - b.iter(|| { - let _a = blake2_128(black_box(key)); - }); -} - -fn bench_twox_128(b: &mut Bencher, key: &Vec) { - b.iter(|| { - let _a = twox_128(black_box(key)); - }); -} - -fn bench_hash_128_fix_size(c: &mut Criterion) { - let mut group = c.benchmark_group("fix size hashing"); - - let key = get_key(MAX_KEY_SIZE); - - group.bench_with_input("blake2_128", &key, bench_blake2_128); - group.bench_with_input("twox_128", &key, bench_twox_128); - - group.finish(); -} - -fn bench_hash_128_dyn_size(c: &mut Criterion) { - let mut group = c.benchmark_group("dyn size hashing"); - - for i in (2..MAX_KEY_SIZE).step_by(4) { - let key = get_key(i); - - group.bench_with_input( - BenchmarkId::new("blake2_128", format!("{}", i)), - &key, - bench_blake2_128, - ); - group.bench_with_input( - BenchmarkId::new("twox_128", format!("{}", i)), - &key, - bench_twox_128, - ); - } - - group.finish(); -} +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use sp_core::crypto::Pair as _; fn bench_ed25519(c: &mut Criterion) { let mut group = c.benchmark_group("ed25519"); @@ -145,12 +87,5 @@ fn bench_ecdsa(c: &mut Criterion) { group.finish(); } -criterion_group!( - benches, - bench_hash_128_fix_size, - bench_hash_128_dyn_size, - bench_ed25519, - bench_sr25519, - bench_ecdsa, -); +criterion_group!(benches, bench_ed25519, bench_sr25519, bench_ecdsa,); criterion_main!(benches); diff --git a/substrate/primitives/core/hashing/proc-macro/Cargo.toml b/substrate/primitives/core/hashing/proc-macro/Cargo.toml deleted file mode 100644 index 5c215bc7799347d5fd969a50d11054e273ea632a..0000000000000000000000000000000000000000 --- a/substrate/primitives/core/hashing/proc-macro/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "sp-core-hashing-proc-macro" -version = "9.0.0" -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" -homepage = "https://substrate.io" -repository.workspace = true -description = "This crate provides procedural macros for calculating static hash." -documentation = "https://docs.rs/sp-core-hashing-proc-macro" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[lib] -proc-macro = true - -[dependencies] -quote = "1.0.28" -syn = { version = "2.0.48", features = ["full", "parsing"] } -sp-core-hashing = { path = "..", default-features = false } diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs index 463d49fd88901cc3eb7cafaab25ff94c18c64d71..61e7162544a602ba2c5577807cfbc94d143dad22 100644 --- a/substrate/primitives/core/src/bandersnatch.rs +++ b/substrate/primitives/core/src/bandersnatch.rs @@ -258,7 +258,7 @@ impl TraitPair for Pair { _seed: Option, ) -> Result<(Pair, Option), DeriveError> { let derive_hard = |seed, cc| -> Seed { - ("bandersnatch-vrf-HDKD", seed, cc).using_encoded(sp_core_hashing::blake2_256) + ("bandersnatch-vrf-HDKD", seed, cc).using_encoded(sp_crypto_hashing::blake2_256) }; let mut seed = self.seed(); @@ -985,6 +985,19 @@ mod tests { assert!(res.is_err()); } + #[test] + fn generate_with_phrase_should_be_recoverable_with_from_string() { + let (pair, phrase, seed) = Pair::generate_with_phrase(None); + let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_seed.public()); + let (repair_phrase, reseed) = + Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid"); + assert_eq!(seed, reseed); + assert_eq!(pair.public(), repair_phrase.public()); + let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_string.public()); + } + #[test] fn sign_verify() { let pair = Pair::from_seed(DEV_SEED); diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index e519ba1806c41b340713f1efe8f21487e83c012d..0c84d0ba8e6c0fd295690ca5fddf48440f083379 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -15,7 +15,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Simple BLS (Boneh–Lynn–Shacham) Signature API. +//! BLS (Boneh–Lynn–Shacham) Signature along with efficiently verifiable Chaum-Pedersen proof API. +//! Signatures are implemented according to +//! [Efficient Aggregatable BLS Signatures with Chaum-Pedersen Proofs](https://eprint.iacr.org/2022/1611) +//! Hash-to-BLS-curve is using Simplified SWU for AB == 0 +//! [RFC 9380](https://datatracker.ietf.org/doc/rfc9380/) Sect 6.6.3. +//! Chaum-Pedersen proof uses the same hash-to-field specified in RFC 9380 for the field of the BLS +//! curve. #[cfg(feature = "serde")] use crate::crypto::Ss58Codec; @@ -428,7 +434,7 @@ trait HardJunctionId { /// Derive a single hard junction. #[cfg(feature = "full_crypto")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { - (T::ID, secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) + (T::ID, secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256) } #[cfg(feature = "full_crypto")] @@ -452,11 +458,12 @@ impl TraitPair for Pair { fn derive>( &self, path: Iter, - _seed: Option, + seed: Option, ) -> Result<(Self, Option), DeriveError> { - let mut acc: [u8; SECRET_KEY_SERIALIZED_SIZE] = self.0.secret.to_bytes().try_into().expect( - "Secret key serializer returns a vector of SECRET_KEY_SERIALIZED_SIZE size; qed", - ); + let mut acc: [u8; SECRET_KEY_SERIALIZED_SIZE] = + seed.unwrap_or(self.0.secret.to_bytes().try_into().expect( + "Secret key serializer returns a vector of SECRET_KEY_SERIALIZED_SIZE size; qed", + )); for j in path { match j { DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath), @@ -544,7 +551,7 @@ mod test { "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", ); let pair = Pair::from_seed(&seed); - // we are using hash to field so this is not going to work + // we are using hash-to-field so this is not going to work // assert_eq!(pair.seed(), seed); let path = vec![DeriveJunction::Hard([0u8; 32])]; let derived = pair.derive(path.into_iter(), None).ok().unwrap().0; @@ -588,12 +595,12 @@ mod test { assert_eq!( public, Public::unchecked_from(array_bytes::hex2array_unchecked( - "6dc6be608fab3c6bd894a606be86db346cc170db85c733853a371f3db54ae1b12052c0888d472760c81b537572a26f00db865e5963aef8634f9917571c51b538b564b2a9ceda938c8b930969ee3b832448e08e33a79e9ddd28af419a3ce45300f5dbc768b067781f44f3fe05a19e6b07b1c4196151ec3f8ea37e4f89a8963030d2101e931276bb9ebe1f20102239d780" + "7a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400" )) ); let message = b""; let signature = - array_bytes::hex2array_unchecked("bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c" + array_bytes::hex2array_unchecked("d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b" ); let expected_signature = Signature::unchecked_from(signature); println!("signature is {:?}", pair.sign(&message[..])); @@ -647,12 +654,30 @@ mod test { assert_eq!(pair1.public(), pair2.public()); } + #[test] + fn generate_with_phrase_should_be_recoverable_with_from_string() { + let (pair, phrase, seed) = Pair::generate_with_phrase(None); + let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_seed.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + let (repair_phrase, reseed) = + Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid"); + assert_eq!(seed, reseed); + assert_eq!(pair.public(), repair_phrase.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + + let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_string.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + } + #[test] fn password_does_something() { let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); assert_ne!(pair1.public(), pair2.public()); + assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec()); } #[test] diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index 58d8616368eb753f609d0e3edcaff2196ee801ab..2a8be2a2ba8503d7dca2d324e405f6a1af840247 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -152,7 +152,7 @@ impl DeriveJunction { let mut cc: [u8; JUNCTION_ID_LEN] = Default::default(); index.using_encoded(|data| { if data.len() > JUNCTION_ID_LEN { - cc.copy_from_slice(&sp_core_hashing::blake2_256(data)); + cc.copy_from_slice(&sp_crypto_hashing::blake2_256(data)); } else { cc[0..data.len()].copy_from_slice(data); } @@ -773,6 +773,8 @@ mod dummy { /// Similarly an empty password (ending the `SURI` with `///`) is perfectly valid and will /// generally be equivalent to no password at all. /// +/// The `password` is used as salt when generating the seed from the BIP-39 key phrase. +/// /// # Example /// /// Parse [`DEV_PHRASE`] secret uri with junction: diff --git a/substrate/primitives/core/src/ecdsa.rs b/substrate/primitives/core/src/ecdsa.rs index 471714582a6bba766cc05ad6e73e870c3d690a93..f172b3a7d02c7a1adb1ff2fc37933d3cf47a165f 100644 --- a/substrate/primitives/core/src/ecdsa.rs +++ b/substrate/primitives/core/src/ecdsa.rs @@ -27,10 +27,7 @@ use crate::crypto::{ ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, }; #[cfg(feature = "full_crypto")] -use crate::{ - crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}, - hashing::blake2_256, -}; +use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; #[cfg(all(feature = "full_crypto", not(feature = "std")))] use secp256k1::Secp256k1; #[cfg(feature = "std")] @@ -328,7 +325,7 @@ impl Signature { /// Recover the public key from this signature and a message. #[cfg(feature = "full_crypto")] pub fn recover>(&self, message: M) -> Option { - self.recover_prehashed(&blake2_256(message.as_ref())) + self.recover_prehashed(&sp_crypto_hashing::blake2_256(message.as_ref())) } /// Recover the public key from this signature and a pre-hashed message. @@ -365,7 +362,7 @@ impl From for Signature { /// Derive a single hard junction. #[cfg(feature = "full_crypto")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { - ("Secp256k1HDKD", secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) + ("Secp256k1HDKD", secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256) } /// A key pair. @@ -423,7 +420,7 @@ impl TraitPair for Pair { /// Sign a message. fn sign(&self, message: &[u8]) -> Signature { - self.sign_prehashed(&blake2_256(message)) + self.sign_prehashed(&sp_crypto_hashing::blake2_256(message)) } /// Verify a signature on a message. Returns true if the signature is good. @@ -481,7 +478,8 @@ impl Pair { /// Parses Signature using parse_overflowing_slice. #[deprecated(note = "please use `verify` instead")] pub fn verify_deprecated>(sig: &Signature, message: M, pubkey: &Public) -> bool { - let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref())); + let message = + libsecp256k1::Message::parse(&sp_crypto_hashing::blake2_256(message.as_ref())); let parse_signature_overflowing = |x: [u8; SIGNATURE_SERIALIZED_SIZE]| { let sig = libsecp256k1::Signature::parse_overflowing_slice(&x[..64]).ok()?; @@ -646,12 +644,29 @@ mod test { assert_eq!(pair1.public(), pair2.public()); } + #[test] + fn generate_with_phrase_should_be_recoverable_with_from_string() { + let (pair, phrase, seed) = Pair::generate_with_phrase(None); + let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_seed.public()); + assert_eq!(pair.secret, repair_seed.secret); + let (repair_phrase, reseed) = + Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid"); + assert_eq!(seed, reseed); + assert_eq!(pair.public(), repair_phrase.public()); + assert_eq!(pair.secret, repair_phrase.secret); + let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_string.public()); + assert_eq!(pair.secret, repair_string.secret); + } + #[test] fn password_does_something() { let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); assert_ne!(pair1.public(), pair2.public()); + assert_ne!(pair1.secret, pair2.secret); } #[test] @@ -766,7 +781,7 @@ mod test { // using pre-hashed `msg` works let msg = b"this should be hashed"; - let sig1 = pair.sign_prehashed(&blake2_256(msg)); + let sig1 = pair.sign_prehashed(&sp_crypto_hashing::blake2_256(msg)); let sig2 = pair.sign(msg); assert_eq!(sig1, sig2); } @@ -776,12 +791,12 @@ mod test { let (pair, _, _) = Pair::generate_with_phrase(Some("password")); // `msg` and `sig` match - let msg = blake2_256(b"this should be hashed"); + let msg = sp_crypto_hashing::blake2_256(b"this should be hashed"); let sig = pair.sign_prehashed(&msg); assert!(Pair::verify_prehashed(&sig, &msg, &pair.public())); // `msg` and `sig` don't match - let msg = blake2_256(b"this is a different message"); + let msg = sp_crypto_hashing::blake2_256(b"this is a different message"); assert!(!Pair::verify_prehashed(&sig, &msg, &pair.public())); } @@ -790,7 +805,7 @@ mod test { let (pair, _, _) = Pair::generate_with_phrase(Some("password")); // recovered key matches signing key - let msg = blake2_256(b"this should be hashed"); + let msg = sp_crypto_hashing::blake2_256(b"this should be hashed"); let sig = pair.sign_prehashed(&msg); let key = sig.recover_prehashed(&msg).unwrap(); assert_eq!(pair.public(), key); @@ -799,7 +814,7 @@ mod test { assert!(Pair::verify_prehashed(&sig, &msg, &key)); // recovered key and signing key don't match - let msg = blake2_256(b"this is a different message"); + let msg = sp_crypto_hashing::blake2_256(b"this is a different message"); let key = sig.recover_prehashed(&msg).unwrap(); assert_ne!(pair.public(), key); } diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs index 151a7229315ebc2d3e6a464929d5d16e7bc842dd..60ebd93e12d43d6d331c6758508f843145031e74 100644 --- a/substrate/primitives/core/src/ed25519.rs +++ b/substrate/primitives/core/src/ed25519.rs @@ -372,7 +372,7 @@ impl Derive for Public {} /// Derive a single hard junction. #[cfg(feature = "full_crypto")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { - ("Ed25519HDKD", secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) + ("Ed25519HDKD", secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256) } #[cfg(feature = "full_crypto")] @@ -501,6 +501,22 @@ mod test { ); } + #[test] + fn generate_with_phrase_should_be_recoverable_with_from_string() { + let (pair, phrase, seed) = Pair::generate_with_phrase(None); + let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_seed.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + let (repair_phrase, reseed) = + Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid"); + assert_eq!(seed, reseed); + assert_eq!(pair.public(), repair_phrase.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_string.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + } + #[test] fn test_vector_should_work() { let pair = Pair::from_seed(&array_bytes::hex2array_unchecked( @@ -590,6 +606,7 @@ mod test { let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); assert_ne!(pair1.public(), pair2.public()); + assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec()); } #[test] diff --git a/substrate/primitives/core/src/hasher.rs b/substrate/primitives/core/src/hasher.rs index 5c4c40cba8e2d89635b424e8af3c36ba98062966..fc613dba161ebfeedcddcf7f644556b3e39c6304 100644 --- a/substrate/primitives/core/src/hasher.rs +++ b/substrate/primitives/core/src/hasher.rs @@ -32,7 +32,7 @@ pub mod blake2 { const LENGTH: usize = 32; fn hash(x: &[u8]) -> Self::Out { - crate::hashing::blake2_256(x).into() + sp_crypto_hashing::blake2_256(x).into() } } } @@ -52,7 +52,7 @@ pub mod keccak { const LENGTH: usize = 32; fn hash(x: &[u8]) -> Self::Out { - crate::hashing::keccak_256(x).into() + sp_crypto_hashing::keccak_256(x).into() } } } diff --git a/substrate/primitives/core/src/hashing.rs b/substrate/primitives/core/src/hashing.rs deleted file mode 100644 index e71d0b54338030fc7adaa684765a373b26d6ef01..0000000000000000000000000000000000000000 --- a/substrate/primitives/core/src/hashing.rs +++ /dev/null @@ -1,66 +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. - -//! Hashing functions. -//! -//! This module is gated by `full-crypto` feature. If you intend to use any of the functions -//! defined here within your runtime, you should most likely rather use `sp_io::hashing` instead, -//! unless you know what you're doing. Using `sp_io` will be more performant, since instead of -//! computing the hash in WASM it delegates that computation to the host client. - -pub use sp_core_hashing::*; - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn blake2b() { - assert_eq!(sp_core_hashing_proc_macro::blake2b_64!(b""), blake2_64(b"")[..]); - assert_eq!(sp_core_hashing_proc_macro::blake2b_256!(b"test"), blake2_256(b"test")[..]); - assert_eq!(sp_core_hashing_proc_macro::blake2b_512!(b""), blake2_512(b"")[..]); - } - - #[test] - fn keccak() { - assert_eq!(sp_core_hashing_proc_macro::keccak_256!(b"test"), keccak_256(b"test")[..]); - assert_eq!(sp_core_hashing_proc_macro::keccak_512!(b"test"), keccak_512(b"test")[..]); - } - - #[test] - fn sha2() { - assert_eq!(sp_core_hashing_proc_macro::sha2_256!(b"test"), sha2_256(b"test")[..]); - } - - #[test] - fn twox() { - assert_eq!(sp_core_hashing_proc_macro::twox_128!(b"test"), twox_128(b"test")[..]); - assert_eq!(sp_core_hashing_proc_macro::twox_64!(b""), twox_64(b"")[..]); - } - - #[test] - fn twox_concats() { - assert_eq!( - sp_core_hashing_proc_macro::twox_128!(b"test", b"123", b"45", b"", b"67890"), - super::twox_128(&b"test1234567890"[..]), - ); - assert_eq!( - sp_core_hashing_proc_macro::twox_128!(b"test", test, b"45", b"", b"67890"), - super::twox_128(&b"testtest4567890"[..]), - ); - } -} diff --git a/substrate/primitives/core/src/lib.rs b/substrate/primitives/core/src/lib.rs index c7232563cb738a6087eeaebbaec678d36d0128be..0d43eea99629915715f452da409d53522d199e53 100644 --- a/substrate/primitives/core/src/lib.rs +++ b/substrate/primitives/core/src/lib.rs @@ -47,10 +47,12 @@ pub use sp_debug_derive::RuntimeDebug; pub use impl_serde::serialize as bytes; #[cfg(feature = "full_crypto")] -pub mod hashing; +#[deprecated( + since = "27.0.0", + note = "`sp-crypto-hashing` re-exports will be removed after June 2024. Use `sp-crypto-hashing` instead." +)] +pub use sp_crypto_hashing::{self as hashing, *}; -#[cfg(feature = "full_crypto")] -pub use hashing::{blake2_128, blake2_256, keccak_256, twox_128, twox_256, twox_64}; pub mod const_hex2array; pub mod crypto; pub mod hexdisplay; diff --git a/substrate/primitives/core/src/paired_crypto.rs b/substrate/primitives/core/src/paired_crypto.rs index 960b8469249e22b10352545f26d3940df3da2e77..20b32c339bd763d4847f4d36626635a4226f80d9 100644 --- a/substrate/primitives/core/src/paired_crypto.rs +++ b/substrate/primitives/core/src/paired_crypto.rs @@ -460,10 +460,11 @@ where path: Iter, seed: Option, ) -> Result<(Self, Option), DeriveError> { - let path: Vec<_> = path.collect(); + let left_path: Vec<_> = path.collect(); + let right_path: Vec<_> = left_path.clone(); - let left = self.left.derive(path.iter().cloned(), seed.map(|s| s.into()))?; - let right = self.right.derive(path.into_iter(), seed.map(|s| s.into()))?; + let left = self.left.derive(left_path.into_iter(), seed.map(|s| s.into()))?; + let right = self.right.derive(right_path.into_iter(), seed.map(|s| s.into()))?; let seed = match (left.1, right.1) { (Some(l), Some(r)) if l.as_ref() == r.as_ref() => Some(l.into()), @@ -542,13 +543,30 @@ mod test { ); } + #[test] + fn generate_with_phrase_should_be_recoverable_with_from_string() { + let (pair, phrase, seed) = Pair::generate_with_phrase(None); + let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_seed.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + + let (repair_phrase, reseed) = + Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid"); + assert_eq!(seed, reseed); + assert_eq!(pair.public(), repair_phrase.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_string.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + } + #[test] fn seed_and_derive_should_work() { let seed_for_right_and_left: [u8; SECURE_SEED_LEN] = array_bytes::hex2array_unchecked( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", ); let pair = Pair::from_seed(&seed_for_right_and_left); - // we are using hash to field so this is not going to work + // we are using hash-to-field so this is not going to work // assert_eq!(pair.seed(), seed); let path = vec![DeriveJunction::Hard([0u8; 32])]; let derived = pair.derive(path.into_iter(), None).ok().unwrap().0; @@ -599,13 +617,13 @@ mod test { assert_eq!( public, Public::unchecked_from( - array_bytes::hex2array_unchecked("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd916dc6be608fab3c6bd894a606be86db346cc170db85c733853a371f3db54ae1b12052c0888d472760c81b537572a26f00db865e5963aef8634f9917571c51b538b564b2a9ceda938c8b930969ee3b832448e08e33a79e9ddd28af419a3ce45300f5dbc768b067781f44f3fe05a19e6b07b1c4196151ec3f8ea37e4f89a8963030d2101e931276bb9ebe1f20102239d780" + array_bytes::hex2array_unchecked("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd917a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400" ), ), ); let message = b""; let signature = - array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c" + array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b" ); let signature = Signature::unchecked_from(signature); assert!(pair.sign(&message[..]) == signature); @@ -664,6 +682,7 @@ mod test { let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); assert_ne!(pair1.public(), pair2.public()); + assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec()); } #[test] diff --git a/substrate/primitives/core/src/sr25519.rs b/substrate/primitives/core/src/sr25519.rs index b821055e2c56713067bf096899c2b10b6155c877..7c02afc3cd5ff8fe378613d88c088c95ceb805c8 100644 --- a/substrate/primitives/core/src/sr25519.rs +++ b/substrate/primitives/core/src/sr25519.rs @@ -970,6 +970,22 @@ mod tests { assert!(Pair::verify(&signature, &message[..], &public)); } + #[test] + fn generate_with_phrase_should_be_recoverable_with_from_string() { + let (pair, phrase, seed) = Pair::generate_with_phrase(None); + let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_seed.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + let (repair_phrase, reseed) = + Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid"); + assert_eq!(seed, reseed); + assert_eq!(pair.public(), repair_phrase.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid"); + assert_eq!(pair.public(), repair_string.public()); + assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec()); + } + #[test] fn generated_pair_should_work() { let (pair, _) = Pair::generate(); diff --git a/substrate/primitives/core/src/testing.rs b/substrate/primitives/core/src/testing.rs index 947dcc387fc79605e4c51d9c28e88c2664dd28c7..c26e23d442f1f584ed888de7e65b5b81a230f70e 100644 --- a/substrate/primitives/core/src/testing.rs +++ b/substrate/primitives/core/src/testing.rs @@ -89,7 +89,7 @@ macro_rules! wasm_export_functions { &[0u8; 0] } else { unsafe { - $crate::sp_std::slice::from_raw_parts(input_data, input_len) + ::core::slice::from_raw_parts(input_data, input_len) } }; @@ -117,7 +117,7 @@ macro_rules! wasm_export_functions { &[0u8; 0] } else { unsafe { - $crate::sp_std::slice::from_raw_parts(input_data, input_len) + ::core::slice::from_raw_parts(input_data, input_len) } }; diff --git a/substrate/primitives/crypto/ec-utils/Cargo.toml b/substrate/primitives/crypto/ec-utils/Cargo.toml index 3baa8ea5b78421e37ab0e56fb0513c81f5c4a049..43daad0892187ff34c94c1fbc657534f071dc6f2 100644 --- a/substrate/primitives/crypto/ec-utils/Cargo.toml +++ b/substrate/primitives/crypto/ec-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-crypto-ec-utils" -version = "0.4.1" +version = "0.10.0" authors.workspace = true description = "Host functions for common Arkworks elliptic curve operations" edition.workspace = true diff --git a/substrate/primitives/core/hashing/Cargo.toml b/substrate/primitives/crypto/hashing/Cargo.toml similarity index 72% rename from substrate/primitives/core/hashing/Cargo.toml rename to substrate/primitives/crypto/hashing/Cargo.toml index 011d312ba90fce623a9d351e8e349b053b676595..3077e1e715e158322ce66882d725e19694bd6879 100644 --- a/substrate/primitives/core/hashing/Cargo.toml +++ b/substrate/primitives/crypto/hashing/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "sp-core-hashing" -version = "9.0.0" +name = "sp-crypto-hashing" +version = "0.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "Primitive core crate hashing implementation." -documentation = "https://docs.rs/sp-core-hashing" +description = "Hashing primitives." +documentation = "https://docs.rs/sp-crypto-hashing" [lints] workspace = true @@ -23,6 +23,17 @@ sha2 = { version = "0.10.7", default-features = false } sha3 = { version = "0.10.0", default-features = false } twox-hash = { version = "1.6.3", default-features = false, features = ["digest_0_10"] } +[dev-dependencies] +criterion = "0.4.0" +sp-crypto-hashing-proc-macro = { path = "proc-macro" } + +[[bench]] +name = "bench" +harness = false + +[lib] +bench = false + [features] default = ["std"] std = [ diff --git a/substrate/primitives/crypto/hashing/benches/bench.rs b/substrate/primitives/crypto/hashing/benches/bench.rs new file mode 100644 index 0000000000000000000000000000000000000000..9afbcd30e3226a7d3e38471ff53df5b6d3e56f65 --- /dev/null +++ b/substrate/primitives/crypto/hashing/benches/bench.rs @@ -0,0 +1,81 @@ +// 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 criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion}; + +// Min 32 bytes buffer +const MIN_EXP: usize = 5; +// Max 1 MB buffer +const MAX_EXP: usize = 20; + +fn bench_blake2_128(b: &mut Bencher, buf: &Vec) { + b.iter(|| { + let _a = sp_crypto_hashing::blake2_128(black_box(buf)); + }); +} + +fn bench_twox_128(b: &mut Bencher, buf: &Vec) { + b.iter(|| { + let _a = sp_crypto_hashing::twox_128(black_box(buf)); + }); +} + +fn bench_blake2_256(b: &mut Bencher, buf: &Vec) { + b.iter(|| { + let _a = sp_crypto_hashing::blake2_256(black_box(buf)); + }); +} + +fn bench_twox_256(b: &mut Bencher, buf: &Vec) { + b.iter(|| { + let _a = sp_crypto_hashing::twox_256(black_box(buf)); + }); +} + +fn bench_sha_256(b: &mut Bencher, buf: &Vec) { + b.iter(|| { + let _a = sp_crypto_hashing::sha2_256(black_box(buf)); + }); +} + +fn bench_keccak_256(b: &mut Bencher, buf: &Vec) { + b.iter(|| { + let _a = sp_crypto_hashing::keccak_256(black_box(buf)); + }); +} + +fn bench_hash(c: &mut Criterion) { + let mut group = c.benchmark_group("hashing-128"); + let buf = vec![0u8; 1 << MAX_EXP]; + + for i in MIN_EXP..=MAX_EXP { + let size = 1 << i; + group.bench_with_input(BenchmarkId::new("blake2-128", size), &buf, bench_blake2_128); + group.bench_with_input(BenchmarkId::new("twox-128", size), &buf, bench_twox_128); + } + group.finish(); + + let mut group = c.benchmark_group("hashing-256"); + for i in MIN_EXP..=MAX_EXP { + let size = 1 << i; + group.bench_with_input(BenchmarkId::new("blake2-256", size), &buf, bench_blake2_256); + group.bench_with_input(BenchmarkId::new("twox-256", size), &buf, bench_twox_256); + group.bench_with_input(BenchmarkId::new("sha-256", size), &buf, bench_sha_256); + group.bench_with_input(BenchmarkId::new("keccak-256", size), &buf, bench_keccak_256); + } + group.finish(); +} + +criterion_group!(benches, bench_hash); +criterion_main!(benches); diff --git a/substrate/primitives/crypto/hashing/proc-macro/Cargo.toml b/substrate/primitives/crypto/hashing/proc-macro/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..f244b02ca101670df10f5aea1a1482cb86cfdaed --- /dev/null +++ b/substrate/primitives/crypto/hashing/proc-macro/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "sp-crypto-hashing-proc-macro" +version = "0.0.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage = "https://substrate.io" +repository.workspace = true +description = "Procedural macros for calculating static hashes." +documentation = "https://docs.rs/sp-crypto-hashing-proc-macro" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[lib] +proc-macro = true + +[dependencies] +quote = { workspace = true } +syn = { features = ["full", "parsing"], workspace = true } +sp-crypto-hashing = { path = "..", default-features = false } diff --git a/substrate/primitives/core/hashing/proc-macro/src/impls.rs b/substrate/primitives/crypto/hashing/proc-macro/src/impls.rs similarity index 85% rename from substrate/primitives/core/hashing/proc-macro/src/impls.rs rename to substrate/primitives/crypto/hashing/proc-macro/src/impls.rs index 714852ae3594ab273aba8bd5c272b3eb4d5c087d..5eba7760b3de7a06b2b361bbf5515174763ee18d 100644 --- a/substrate/primitives/core/hashing/proc-macro/src/impls.rs +++ b/substrate/primitives/crypto/hashing/proc-macro/src/impls.rs @@ -83,35 +83,35 @@ impl Parse for MultipleInputBytes { } pub(super) fn twox_64(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::twox_64(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::twox_64(bytes.as_slice())) } pub(super) fn twox_128(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::twox_128(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::twox_128(bytes.as_slice())) } pub(super) fn blake2b_512(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::blake2_512(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::blake2_512(bytes.as_slice())) } pub(super) fn blake2b_256(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::blake2_256(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::blake2_256(bytes.as_slice())) } pub(super) fn blake2b_64(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::blake2_64(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::blake2_64(bytes.as_slice())) } pub(super) fn keccak_256(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::keccak_256(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::keccak_256(bytes.as_slice())) } pub(super) fn keccak_512(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::keccak_512(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::keccak_512(bytes.as_slice())) } pub(super) fn sha2_256(bytes: Vec) -> TokenStream { - bytes_to_array(sp_core_hashing::sha2_256(bytes.as_slice())) + bytes_to_array(sp_crypto_hashing::sha2_256(bytes.as_slice())) } fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { diff --git a/substrate/primitives/core/hashing/proc-macro/src/lib.rs b/substrate/primitives/crypto/hashing/proc-macro/src/lib.rs similarity index 89% rename from substrate/primitives/core/hashing/proc-macro/src/lib.rs rename to substrate/primitives/crypto/hashing/proc-macro/src/lib.rs index 69668cadb4e26276377064d1921b3ec86ef76329..0ebd30c8b4095ea14b69660ba95c34cf2c6e7e24 100644 --- a/substrate/primitives/core/hashing/proc-macro/src/lib.rs +++ b/substrate/primitives/crypto/hashing/proc-macro/src/lib.rs @@ -30,24 +30,24 @@ //! //! ```rust //! assert_eq!( -//! sp_core_hashing_proc_macro::blake2b_256!(b"test"), -//! sp_core_hashing::blake2_256(b"test"), +//! sp_crypto_hashing_proc_macro::blake2b_256!(b"test"), +//! sp_crypto_hashing::blake2_256(b"test"), //! ); //! assert_eq!( -//! sp_core_hashing_proc_macro::blake2b_256!([1u8]), -//! sp_core_hashing::blake2_256(&[1u8]), +//! sp_crypto_hashing_proc_macro::blake2b_256!([1u8]), +//! sp_crypto_hashing::blake2_256(&[1u8]), //! ); //! assert_eq!( -//! sp_core_hashing_proc_macro::blake2b_256!([1, 2, 3]), -//! sp_core_hashing::blake2_256(&[1, 2, 3]), +//! sp_crypto_hashing_proc_macro::blake2b_256!([1, 2, 3]), +//! sp_crypto_hashing::blake2_256(&[1, 2, 3]), //! ); //! assert_eq!( -//! sp_core_hashing_proc_macro::blake2b_256!(identifier), -//! sp_core_hashing::blake2_256(b"identifier"), +//! sp_crypto_hashing_proc_macro::blake2b_256!(identifier), +//! sp_crypto_hashing::blake2_256(b"identifier"), //! ); //! assert_eq!( -//! sp_core_hashing_proc_macro::blake2b_256!(identifier, b"/string"), -//! sp_core_hashing::blake2_256(b"identifier/string"), +//! sp_crypto_hashing_proc_macro::blake2b_256!(identifier, b"/string"), +//! sp_crypto_hashing::blake2_256(b"identifier/string"), //! ); //! ``` diff --git a/substrate/primitives/core/hashing/src/lib.rs b/substrate/primitives/crypto/hashing/src/lib.rs similarity index 76% rename from substrate/primitives/core/hashing/src/lib.rs rename to substrate/primitives/crypto/hashing/src/lib.rs index 33d777f85b014b5bdf997ea1f8e689467970b87d..04b7bb6f4de25ecea67867c17d04b21dae4c9d94 100644 --- a/substrate/primitives/core/hashing/src/lib.rs +++ b/substrate/primitives/crypto/hashing/src/lib.rs @@ -121,3 +121,44 @@ pub fn keccak_512(data: &[u8]) -> [u8; 64] { pub fn sha2_256(data: &[u8]) -> [u8; 32] { sha2::Sha256::digest(data).into() } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn blake2b() { + assert_eq!(sp_crypto_hashing_proc_macro::blake2b_64!(b""), blake2_64(b"")[..]); + assert_eq!(sp_crypto_hashing_proc_macro::blake2b_256!(b"test"), blake2_256(b"test")[..]); + assert_eq!(sp_crypto_hashing_proc_macro::blake2b_512!(b""), blake2_512(b"")[..]); + } + + #[test] + fn keccak() { + assert_eq!(sp_crypto_hashing_proc_macro::keccak_256!(b"test"), keccak_256(b"test")[..]); + assert_eq!(sp_crypto_hashing_proc_macro::keccak_512!(b"test"), keccak_512(b"test")[..]); + } + + #[test] + fn sha2() { + assert_eq!(sp_crypto_hashing_proc_macro::sha2_256!(b"test"), sha2_256(b"test")[..]); + } + + #[test] + fn twox() { + assert_eq!(sp_crypto_hashing_proc_macro::twox_128!(b"test"), twox_128(b"test")[..]); + assert_eq!(sp_crypto_hashing_proc_macro::twox_64!(b""), twox_64(b"")[..]); + } + + #[test] + fn twox_concats() { + assert_eq!( + sp_crypto_hashing_proc_macro::twox_128!(b"test", b"123", b"45", b"", b"67890"), + twox_128(&b"test1234567890"[..]), + ); + assert_eq!( + sp_crypto_hashing_proc_macro::twox_128!(b"test", test, b"45", b"", b"67890"), + twox_128(&b"testtest4567890"[..]), + ); + } +} diff --git a/substrate/primitives/database/Cargo.toml b/substrate/primitives/database/Cargo.toml index 00ccf97c83e95d4a979812b59284e00fe2e74545..081aad6075840c109d397d5830e3e654c3325172 100644 --- a/substrate/primitives/database/Cargo.toml +++ b/substrate/primitives/database/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-database" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/debug-derive/Cargo.toml b/substrate/primitives/debug-derive/Cargo.toml index b7a65d658cfc3500b3b3464149c37dde12f0e3fb..debf964aa3dfdf7cebd23e0f1d24e74b0d880ec0 100644 --- a/substrate/primitives/debug-derive/Cargo.toml +++ b/substrate/primitives/debug-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-debug-derive" -version = "8.0.0" +version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,8 +19,8 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -quote = "1.0.28" -syn = "2.0.48" +quote = { workspace = true } +syn = { workspace = true } proc-macro2 = "1.0.56" [features] diff --git a/substrate/primitives/externalities/Cargo.toml b/substrate/primitives/externalities/Cargo.toml index 4c7afc38b815f7579e527598b48d9543ac4543bb..6dc4f0a0dadf7d4ab906c21edca72cc3ac69a51a 100644 --- a/substrate/primitives/externalities/Cargo.toml +++ b/substrate/primitives/externalities/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-externalities" -version = "0.19.0" +version = "0.25.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true diff --git a/substrate/primitives/externalities/src/extensions.rs b/substrate/primitives/externalities/src/extensions.rs index 282e6ea914a84caae6a1c63af8d9ff0e125176fc..d99dfe6cf530ae29718a1cf0a24a5a6afa851894 100644 --- a/substrate/primitives/externalities/src/extensions.rs +++ b/substrate/primitives/externalities/src/extensions.rs @@ -78,16 +78,16 @@ macro_rules! decl_extension { $vis struct $ext_name (pub $inner); impl $crate::Extension for $ext_name { - fn as_mut_any(&mut self) -> &mut dyn std::any::Any { + fn as_mut_any(&mut self) -> &mut dyn core::any::Any { self } - fn type_id(&self) -> std::any::TypeId { - std::any::Any::type_id(self) + fn type_id(&self) -> core::any::TypeId { + core::any::Any::type_id(self) } } - impl std::ops::Deref for $ext_name { + impl core::ops::Deref for $ext_name { type Target = $inner; fn deref(&self) -> &Self::Target { @@ -95,7 +95,7 @@ macro_rules! decl_extension { } } - impl std::ops::DerefMut for $ext_name { + impl core::ops::DerefMut for $ext_name { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -115,12 +115,12 @@ macro_rules! decl_extension { $vis struct $ext_name; impl $crate::Extension for $ext_name { - fn as_mut_any(&mut self) -> &mut dyn std::any::Any { + fn as_mut_any(&mut self) -> &mut dyn core::any::Any { self } - fn type_id(&self) -> std::any::TypeId { - std::any::Any::type_id(self) + fn type_id(&self) -> core::any::TypeId { + core::any::Any::type_id(self) } } } diff --git a/substrate/primitives/genesis-builder/Cargo.toml b/substrate/primitives/genesis-builder/Cargo.toml index 263b48c9efe6841a8621cd6f5761e81cb8dfa5c6..15440b4811ec4664725f8b0df5335748917b20fc 100644 --- a/substrate/primitives/genesis-builder/Cargo.toml +++ b/substrate/primitives/genesis-builder/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "sp-genesis-builder" -version = "0.1.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "Substrate GenesisConfig builder API" +description = "Substrate RuntimeGenesisConfig builder API" readme = "README.md" [lints] @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-api = { path = "../api", default-features = false } sp-runtime = { path = "../runtime", default-features = false } sp-std = { path = "../std", default-features = false } -serde_json = { version = "1.0.111", default-features = false, features = ["alloc", "arbitrary_precision"] } +serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true } [features] default = ["std"] diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index e002cd3aa6f704d8889fd35d04fdd65b46a1b754..bb1a2a352488ac0c03cfcd35dac1b45444b1d9a1 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -19,36 +19,37 @@ //! Substrate genesis config builder //! -//! This Runtime API allows to construct `GenesisConfig`, in particular: -//! - serialize the runtime default `GenesisConfig` struct into json format, -//! - put the GenesisConfig struct into the storage. Internally this operation calls +//! This Runtime API allows to construct `RuntimeGenesisConfig`, in particular: +//! - serialize the runtime default `RuntimeGenesisConfig` struct into json format, +//! - put the RuntimeGenesisConfig struct into the storage. Internally this operation calls //! `GenesisBuild::build` function for all runtime pallets, which is typically provided by //! pallet's author. -//! - deserialize the `GenesisConfig` from given json blob and put `GenesisConfig` into the state -//! storage. Allows to build customized configuration. +//! - deserialize the `RuntimeGenesisConfig` from given json blob and put `RuntimeGenesisConfig` +//! into the state storage. Allows to build customized configuration. //! -//! Providing externalities with empty storage and putting `GenesisConfig` into storage allows to -//! catch and build the raw storage of `GenesisConfig` which is the foundation for genesis block. +//! Providing externalities with empty storage and putting `RuntimeGenesisConfig` into storage +//! allows to catch and build the raw storage of `RuntimeGenesisConfig` which is the foundation for +//! genesis block. /// The result type alias, used in build methods. `Err` contains formatted error message. pub type Result = core::result::Result<(), sp_runtime::RuntimeString>; sp_api::decl_runtime_apis! { - /// API to interact with GenesisConfig for the runtime + /// API to interact with RuntimeGenesisConfig for the runtime pub trait GenesisBuilder { - /// Creates the default `GenesisConfig` and returns it as a JSON blob. + /// Creates the default `RuntimeGenesisConfig` and returns it as a JSON blob. /// - /// This function instantiates the default `GenesisConfig` struct for the runtime and serializes it into a JSON - /// blob. It returns a `Vec` containing the JSON representation of the default `GenesisConfig`. + /// This function instantiates the default `RuntimeGenesisConfig` struct for the runtime and serializes it into a JSON + /// blob. It returns a `Vec` containing the JSON representation of the default `RuntimeGenesisConfig`. fn create_default_config() -> sp_std::vec::Vec; - /// Build `GenesisConfig` from a JSON blob not using any defaults and store it in the storage. + /// Build `RuntimeGenesisConfig` from a JSON blob not using any defaults and store it in the storage. /// - /// This function deserializes the full `GenesisConfig` from the given JSON blob and puts it into the storage. + /// This function deserializes the full `RuntimeGenesisConfig` from the given JSON blob and puts it into the storage. /// If the provided JSON blob is incorrect or incomplete or the deserialization fails, an error is returned. /// It is recommended to log any errors encountered during the process. /// - /// Please note that provided json blob must contain all `GenesisConfig` fields, no defaults will be used. + /// Please note that provided json blob must contain all `RuntimeGenesisConfig` fields, no defaults will be used. fn build_config(json: sp_std::vec::Vec) -> Result; } } diff --git a/substrate/primitives/inherents/Cargo.toml b/substrate/primitives/inherents/Cargo.toml index 5c13694ceac1624f7b8860b39dbcd4d7cce48239..bfb1d7733471fa7fd9f0a13c14d337e34188065d 100644 --- a/substrate/primitives/inherents/Cargo.toml +++ b/substrate/primitives/inherents/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-inherents" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,7 +21,7 @@ async-trait = { version = "0.1.74", optional = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" -thiserror = { version = "1.0.48", optional = true } +thiserror = { optional = true, workspace = true } sp-runtime = { path = "../runtime", default-features = false, optional = true } sp-std = { path = "../std", default-features = false } diff --git a/substrate/primitives/inherents/src/lib.rs b/substrate/primitives/inherents/src/lib.rs index dd7c294f1e245126bd02ef14a470595dc13841bb..415319a6849c97f9a0c89b663bb4248a50d5b7dd 100644 --- a/substrate/primitives/inherents/src/lib.rs +++ b/substrate/primitives/inherents/src/lib.rs @@ -98,10 +98,10 @@ //! and production. //! //! ``` -//! # use sp_runtime::testing::ExtrinsicWrapper; +//! # use sp_runtime::{generic::UncheckedExtrinsic, testing::MockCallU64}; //! # use sp_inherents::{InherentIdentifier, InherentData}; //! # use futures::FutureExt; -//! # type Block = sp_runtime::testing::Block>; +//! # type Block = sp_runtime::testing::Block>; //! # const INHERENT_IDENTIFIER: InherentIdentifier = *b"testinh0"; //! # struct InherentDataProvider; //! # #[async_trait::async_trait] diff --git a/substrate/primitives/io/Cargo.toml b/substrate/primitives/io/Cargo.toml index 47de957e6bf9eadc375c4f06f4c1beee9843a462..c78def9bf4429eab1e5c9822a52732ee3a3c74ca 100644 --- a/substrate/primitives/io/Cargo.toml +++ b/substrate/primitives/io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-io" -version = "23.0.0" +version = "30.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,11 +17,11 @@ workspace = true [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] - [dependencies] bytes = { version = "1.1.0", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bytes"] } sp-core = { path = "../core", default-features = false } +sp-crypto-hashing = { path = "../crypto/hashing", default-features = false } sp-keystore = { path = "../keystore", default-features = false, optional = true } sp-std = { path = "../std", default-features = false } libsecp256k1 = { version = "0.7", optional = true } @@ -30,7 +30,7 @@ sp-runtime-interface = { path = "../runtime-interface", default-features = false sp-trie = { path = "../trie", default-features = false, optional = true } sp-externalities = { path = "../externalities", default-features = false } sp-tracing = { path = "../tracing", default-features = false } -log = { version = "0.4.17", optional = true } +log = { optional = true, workspace = true, default-features = true } secp256k1 = { version = "0.28.0", features = ["global-context", "recovery"], optional = true } tracing = { version = "0.1.29", default-features = false } tracing-core = { version = "0.1.32", default-features = false } @@ -52,6 +52,7 @@ std = [ "log/std", "secp256k1", "sp-core/std", + "sp-crypto-hashing/std", "sp-externalities/std", "sp-keystore/std", "sp-runtime-interface/std", diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs index a300152ee66d1288de4633918d08590b483d6423..6d34199416a36deeb011003989f6e361d535a0e6 100644 --- a/substrate/primitives/io/src/lib.rs +++ b/substrate/primitives/io/src/lib.rs @@ -1251,42 +1251,42 @@ pub trait Crypto { pub trait Hashing { /// Conduct a 256-bit Keccak hash. fn keccak_256(data: &[u8]) -> [u8; 32] { - sp_core::hashing::keccak_256(data) + sp_crypto_hashing::keccak_256(data) } /// Conduct a 512-bit Keccak hash. fn keccak_512(data: &[u8]) -> [u8; 64] { - sp_core::hashing::keccak_512(data) + sp_crypto_hashing::keccak_512(data) } /// Conduct a 256-bit Sha2 hash. fn sha2_256(data: &[u8]) -> [u8; 32] { - sp_core::hashing::sha2_256(data) + sp_crypto_hashing::sha2_256(data) } /// Conduct a 128-bit Blake2 hash. fn blake2_128(data: &[u8]) -> [u8; 16] { - sp_core::hashing::blake2_128(data) + sp_crypto_hashing::blake2_128(data) } /// Conduct a 256-bit Blake2 hash. fn blake2_256(data: &[u8]) -> [u8; 32] { - sp_core::hashing::blake2_256(data) + sp_crypto_hashing::blake2_256(data) } /// Conduct four XX hashes to give a 256-bit result. fn twox_256(data: &[u8]) -> [u8; 32] { - sp_core::hashing::twox_256(data) + sp_crypto_hashing::twox_256(data) } /// Conduct two XX hashes to give a 128-bit result. fn twox_128(data: &[u8]) -> [u8; 16] { - sp_core::hashing::twox_128(data) + sp_crypto_hashing::twox_128(data) } /// Conduct two XX hashes to give a 64-bit result. fn twox_64(data: &[u8]) -> [u8; 8] { - sp_core::hashing::twox_64(data) + sp_crypto_hashing::twox_64(data) } } @@ -1737,20 +1737,20 @@ mod tracing_setup { pub use tracing_setup::init_tracing; -/// Allocator used by Substrate when executing the Wasm runtime. -#[cfg(all(target_arch = "wasm32", not(feature = "std")))] -struct WasmAllocator; +/// Allocator used by Substrate from within the runtime. +#[cfg(substrate_runtime)] +struct RuntimeAllocator; -#[cfg(all(target_arch = "wasm32", not(feature = "disable_allocator"), not(feature = "std")))] +#[cfg(all(not(feature = "disable_allocator"), substrate_runtime))] #[global_allocator] -static ALLOCATOR: WasmAllocator = WasmAllocator; +static ALLOCATOR: RuntimeAllocator = RuntimeAllocator; -#[cfg(all(target_arch = "wasm32", not(feature = "std")))] +#[cfg(substrate_runtime)] mod allocator_impl { use super::*; use core::alloc::{GlobalAlloc, Layout}; - unsafe impl GlobalAlloc for WasmAllocator { + unsafe impl GlobalAlloc for RuntimeAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { allocator::malloc(layout.size() as u32) } @@ -1761,8 +1761,27 @@ mod allocator_impl { } } -/// A default panic handler for WASM environment. -#[cfg(all(not(feature = "disable_panic_handler"), not(feature = "std")))] +/// Crashes the execution of the program. +/// +/// Equivalent to the WASM `unreachable` instruction, RISC-V `unimp` instruction, +/// or just the `unreachable!()` macro everywhere else. +pub fn unreachable() -> ! { + #[cfg(target_family = "wasm")] + { + core::arch::wasm32::unreachable(); + } + + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe { + core::arch::asm!("unimp", options(noreturn)); + } + + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64", target_family = "wasm")))] + unreachable!(); +} + +/// A default panic handler for the runtime environment. +#[cfg(all(not(feature = "disable_panic_handler"), substrate_runtime))] #[panic_handler] #[no_mangle] pub fn panic(info: &core::panic::PanicInfo) -> ! { @@ -1774,11 +1793,11 @@ pub fn panic(info: &core::panic::PanicInfo) -> ! { #[cfg(not(feature = "improved_panic_error_reporting"))] { logging::log(LogLevel::Error, "runtime", message.as_bytes()); - core::arch::wasm32::unreachable(); + unreachable(); } } -/// A default OOM handler for WASM environment. +/// A default OOM handler for the runtime environment. #[cfg(all(not(feature = "disable_oom"), enable_alloc_error_handler))] #[alloc_error_handler] pub fn oom(_: core::alloc::Layout) -> ! { @@ -1789,7 +1808,7 @@ pub fn oom(_: core::alloc::Layout) -> ! { #[cfg(not(feature = "improved_panic_error_reporting"))] { logging::log(LogLevel::Error, "runtime", b"Runtime memory exhausted. Aborting"); - core::arch::wasm32::unreachable(); + unreachable(); } } diff --git a/substrate/primitives/keyring/Cargo.toml b/substrate/primitives/keyring/Cargo.toml index 80d773b452aed4ffa41501640b723230f5ef6f8a..1c936b6685be86c54a70824ab902f616c0639945 100644 --- a/substrate/primitives/keyring/Cargo.toml +++ b/substrate/primitives/keyring/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-keyring" -version = "24.0.0" +version = "31.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/keystore/Cargo.toml b/substrate/primitives/keystore/Cargo.toml index d60f5d6c568c50c783554beb2756610d7cae9855..a34839358e18333154962f71e1957f9884a71a2f 100644 --- a/substrate/primitives/keystore/Cargo.toml +++ b/substrate/primitives/keystore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-keystore" -version = "0.27.0" +version = "0.34.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,8 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -parking_lot = { version = "0.12.1", default-features = false } -thiserror = "1.0" +parking_lot = { version = "0.12.1", default-features = false, optional = true } sp-core = { path = "../core", default-features = false } sp-externalities = { path = "../externalities", default-features = false } @@ -28,7 +27,7 @@ rand_chacha = "0.2.2" [features] default = ["std"] -std = ["codec/std", "sp-core/std", "sp-externalities/std"] +std = ["codec/std", "dep:parking_lot", "sp-core/std", "sp-externalities/std"] # This feature adds BLS crypto primitives. # It should not be used in production since the implementation and interface may still diff --git a/substrate/primitives/keystore/src/lib.rs b/substrate/primitives/keystore/src/lib.rs index 07c4e2d5fd1dc4b8cc903d34154dbc51a050426c..64f0e3ea49e8bd3a7e682ad024cd7c28fbcd3abd 100644 --- a/substrate/primitives/keystore/src/lib.rs +++ b/substrate/primitives/keystore/src/lib.rs @@ -17,6 +17,10 @@ //! Keystore traits +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + #[cfg(feature = "std")] pub mod testing; @@ -29,25 +33,35 @@ use sp_core::{ ecdsa, ed25519, sr25519, }; -use std::sync::Arc; +use alloc::{string::String, sync::Arc, vec::Vec}; /// Keystore error -#[derive(Debug, thiserror::Error)] +#[derive(Debug)] pub enum Error { /// Public key type is not supported - #[error("Key not supported: {0:?}")] KeyNotSupported(KeyTypeId), /// Validation error - #[error("Validation error: {0}")] ValidationError(String), /// Keystore unavailable - #[error("Keystore unavailable")] Unavailable, /// Programming errors - #[error("An unknown keystore error occurred: {0}")] Other(String), } +impl core::fmt::Display for Error { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + Error::KeyNotSupported(key_type) => write!(fmt, "Key not supported: {key_type:?}"), + Error::ValidationError(error) => write!(fmt, "Validation error: {error}"), + Error::Unavailable => fmt.write_str("Keystore unavailable"), + Error::Other(error) => write!(fmt, "An unknown keystore error occurred: {error}"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + /// Something that generates, stores and provides access to secret keys. pub trait Keystore: Send + Sync { /// Returns all the sr25519 public keys for the given key type. @@ -355,6 +369,24 @@ pub trait Keystore: Send + Sync { msg: &[u8], ) -> Result, Error>; + /// Hashes the `message` using keccak256 and then signs it using ECDSA + /// algorithm. It does not affect the behavior of BLS12-377 component. It generates + /// BLS12-377 Signature according to IETF standard. + /// + /// Receives [`KeyTypeId`] and a [`ecdsa_bls377::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns an [`ecdsa_bls377::Signature`] or `None` in case the given `key_type` + /// and `public` combination doesn't exist in the keystore. + /// An `Err` will be returned if generating the signature itself failed. + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls377_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls377::Public, + msg: &[u8], + ) -> Result, Error>; + /// Insert a new secret key. fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; @@ -661,6 +693,16 @@ impl Keystore for Arc { (**self).ecdsa_bls377_sign(key_type, public, msg) } + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls377_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls377::Public, + msg: &[u8], + ) -> Result, Error> { + (**self).ecdsa_bls377_sign_with_keccak256(key_type, public, msg) + } + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { (**self).insert(key_type, suri, public) } diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs index 585efba02831a442bda511844811a862442cc87d..e10660b126a33f23e00f5774412f65d665cc6737 100644 --- a/substrate/primitives/keystore/src/testing.rs +++ b/substrate/primitives/keystore/src/testing.rs @@ -22,7 +22,7 @@ use crate::{Error, Keystore, KeystorePtr}; #[cfg(feature = "bandersnatch-experimental")] use sp_core::bandersnatch; #[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381, ecdsa_bls377}; +use sp_core::{bls377, bls381, ecdsa_bls377, KeccakHasher}; use sp_core::{ crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, @@ -346,6 +346,19 @@ impl Keystore for MemoryKeystore { self.sign::(key_type, public, msg) } + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls377_sign_with_keccak256( + &self, + key_type: KeyTypeId, + public: &ecdsa_bls377::Public, + msg: &[u8], + ) -> Result, Error> { + let sig = self + .pair::(key_type, public) + .map(|pair| pair.sign_with_hasher::(msg)); + Ok(sig) + } + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { self.keys .write() @@ -479,17 +492,50 @@ mod tests { let suri = "//Alice"; let pair = ecdsa::Pair::from_string(suri, None).unwrap(); - let msg = sp_core::keccak_256(b"this should be a hashed message"); + // Let's pretend this to be the hash output as content doesn't really matter here. + let hash = [0xff; 32]; // no key in key store - let res = store.ecdsa_sign_prehashed(ECDSA, &pair.public(), &msg).unwrap(); + let res = store.ecdsa_sign_prehashed(ECDSA, &pair.public(), &hash).unwrap(); assert!(res.is_none()); // insert key, sign again store.insert(ECDSA, suri, pair.public().as_ref()).unwrap(); - let res = store.ecdsa_sign_prehashed(ECDSA, &pair.public(), &msg).unwrap(); + let res = store.ecdsa_sign_prehashed(ECDSA, &pair.public(), &hash).unwrap(); + assert!(res.is_some()); + } + + #[test] + #[cfg(feature = "bls-experimental")] + fn ecdsa_bls377_sign_with_keccak_works() { + use sp_core::testing::ECDSA_BLS377; + + let store = MemoryKeystore::new(); + + let suri = "//Alice"; + let pair = ecdsa_bls377::Pair::from_string(suri, None).unwrap(); + + let msg = b"this should be a normal unhashed message not "; + + // insert key, sign again + store.insert(ECDSA_BLS377, suri, pair.public().as_ref()).unwrap(); + + let res = store + .ecdsa_bls377_sign_with_keccak256(ECDSA_BLS377, &pair.public(), &msg[..]) + .unwrap(); + assert!(res.is_some()); + + // does not verify with default out-of-the-box verification + assert!(!ecdsa_bls377::Pair::verify(&res.clone().unwrap(), &msg[..], &pair.public())); + + // should verify using keccak256 as hasher + assert!(ecdsa_bls377::Pair::verify_with_hasher::( + &res.unwrap(), + msg, + &pair.public() + )); } #[test] diff --git a/substrate/primitives/maybe-compressed-blob/Cargo.toml b/substrate/primitives/maybe-compressed-blob/Cargo.toml index 86f73626c0a0bfdbcac66e8b5e6019602713ad03..fa5383d03b10d5c6560e50ff28be7b6e71ffe2b0 100644 --- a/substrate/primitives/maybe-compressed-blob/Cargo.toml +++ b/substrate/primitives/maybe-compressed-blob/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-maybe-compressed-blob" -version = "4.1.0-dev" +version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -14,5 +14,5 @@ readme = "README.md" workspace = true [dependencies] -thiserror = "1.0" +thiserror = { workspace = true } zstd = { version = "0.12.4", default-features = false } diff --git a/substrate/primitives/merkle-mountain-range/Cargo.toml b/substrate/primitives/merkle-mountain-range/Cargo.toml index c0d69a2c4f54b63f389a85f59aeadc331666326f..50d8477823948a4005a9cb0ccfabf73349f91222 100644 --- a/substrate/primitives/merkle-mountain-range/Cargo.toml +++ b/substrate/primitives/merkle-mountain-range/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-mmr-primitives" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,15 +17,15 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } -serde = { version = "1.0.195", features = ["alloc", "derive"], default-features = false, optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-api = { path = "../api", default-features = false } sp-core = { path = "../core", default-features = false } sp-debug-derive = { path = "../debug-derive", default-features = false } sp-runtime = { path = "../runtime", default-features = false } sp-std = { path = "../std", default-features = false } -thiserror = "1.0" +thiserror = { optional = true, workspace = true } [dev-dependencies] array-bytes = "6.1" @@ -34,6 +34,7 @@ array-bytes = "6.1" default = ["std"] std = [ "codec/std", + "dep:thiserror", "log/std", "mmr-lib/std", "scale-info/std", diff --git a/substrate/primitives/metadata-ir/Cargo.toml b/substrate/primitives/metadata-ir/Cargo.toml index 0dc496bab53130138933996c29a6022298779a59..e4203d0e378efb557ace209761e012bc571400fa 100644 --- a/substrate/primitives/metadata-ir/Cargo.toml +++ b/substrate/primitives/metadata-ir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-metadata-ir" -version = "0.1.0" +version = "0.6.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/metadata-ir/src/lib.rs b/substrate/primitives/metadata-ir/src/lib.rs index edfa58f8618942e34b6937733cbd2a5f5c4dffbb..66aff2c599fc9438c2e42bcd3c409d8fbabe6dca 100644 --- a/substrate/primitives/metadata-ir/src/lib.rs +++ b/substrate/primitives/metadata-ir/src/lib.rs @@ -84,7 +84,7 @@ mod test { call_ty: meta_type::<()>(), signature_ty: meta_type::<()>(), extra_ty: meta_type::<()>(), - signed_extensions: vec![], + extensions: vec![], }, ty: meta_type::<()>(), apis: vec![], diff --git a/substrate/primitives/metadata-ir/src/types.rs b/substrate/primitives/metadata-ir/src/types.rs index b107d20a8e2bfbf55d0d558bb60346cb7a52dd70..e60881ed6b8d86df2bc0b92c744b569168e3f75d 100644 --- a/substrate/primitives/metadata-ir/src/types.rs +++ b/substrate/primitives/metadata-ir/src/types.rs @@ -166,10 +166,11 @@ pub struct ExtrinsicMetadataIR { pub call_ty: T::Type, /// The type of the extrinsic's signature. pub signature_ty: T::Type, - /// The type of the outermost Extra enum. + /// The type of the outermost Extra/Extensions enum. + // TODO: metadata-v16: rename this to `extension_ty`. pub extra_ty: T::Type, - /// The signed extensions in the order they appear in the extrinsic. - pub signed_extensions: Vec>, + /// The transaction extensions in the order they appear in the extrinsic. + pub extensions: Vec>, } impl IntoPortable for ExtrinsicMetadataIR { @@ -183,27 +184,27 @@ impl IntoPortable for ExtrinsicMetadataIR { call_ty: registry.register_type(&self.call_ty), signature_ty: registry.register_type(&self.signature_ty), extra_ty: registry.register_type(&self.extra_ty), - signed_extensions: registry.map_into_portable(self.signed_extensions), + extensions: registry.map_into_portable(self.extensions), } } } /// Metadata of an extrinsic's signed extension. #[derive(Clone, PartialEq, Eq, Encode, Debug)] -pub struct SignedExtensionMetadataIR { +pub struct TransactionExtensionMetadataIR { /// The unique signed extension identifier, which may be different from the type name. pub identifier: T::String, /// The type of the signed extension, with the data to be included in the extrinsic. pub ty: T::Type, - /// The type of the additional signed data, with the data to be included in the signed payload + /// The type of the additional signed data, with the data to be included in the signed payload. pub additional_signed: T::Type, } -impl IntoPortable for SignedExtensionMetadataIR { - type Output = SignedExtensionMetadataIR; +impl IntoPortable for TransactionExtensionMetadataIR { + type Output = TransactionExtensionMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - SignedExtensionMetadataIR { + TransactionExtensionMetadataIR { identifier: self.identifier.into_portable(registry), ty: registry.register_type(&self.ty), additional_signed: registry.register_type(&self.additional_signed), diff --git a/substrate/primitives/metadata-ir/src/v14.rs b/substrate/primitives/metadata-ir/src/v14.rs index e1b7a24f76577c09f7f8c8040ae06bf433f9126d..ec08de3478622421214bc40f7ea4a5b1dca6cadc 100644 --- a/substrate/primitives/metadata-ir/src/v14.rs +++ b/substrate/primitives/metadata-ir/src/v14.rs @@ -20,8 +20,8 @@ use super::types::{ ExtrinsicMetadataIR, MetadataIR, PalletCallMetadataIR, PalletConstantMetadataIR, PalletErrorMetadataIR, PalletEventMetadataIR, PalletMetadataIR, PalletStorageMetadataIR, - SignedExtensionMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, - StorageHasherIR, + StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR, + TransactionExtensionMetadataIR, }; use frame_metadata::v14::{ @@ -137,8 +137,8 @@ impl From for PalletErrorMetadata { } } -impl From for SignedExtensionMetadata { - fn from(ir: SignedExtensionMetadataIR) -> Self { +impl From for SignedExtensionMetadata { + fn from(ir: TransactionExtensionMetadataIR) -> Self { SignedExtensionMetadata { identifier: ir.identifier, ty: ir.ty, @@ -152,7 +152,7 @@ impl From for ExtrinsicMetadata { ExtrinsicMetadata { ty: ir.ty, version: ir.version, - signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), + signed_extensions: ir.extensions.into_iter().map(Into::into).collect(), } } } diff --git a/substrate/primitives/metadata-ir/src/v15.rs b/substrate/primitives/metadata-ir/src/v15.rs index a942eb73223b2c80e2d71ef1cb070c45a2ca588f..dc5ce1c88fdfa5b32d4304a0e164c98b450f5005 100644 --- a/substrate/primitives/metadata-ir/src/v15.rs +++ b/substrate/primitives/metadata-ir/src/v15.rs @@ -21,7 +21,7 @@ use crate::OuterEnumsIR; use super::types::{ ExtrinsicMetadataIR, MetadataIR, PalletMetadataIR, RuntimeApiMetadataIR, - RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, SignedExtensionMetadataIR, + RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, TransactionExtensionMetadataIR, }; use frame_metadata::v15::{ @@ -87,8 +87,8 @@ impl From for PalletMetadata { } } -impl From for SignedExtensionMetadata { - fn from(ir: SignedExtensionMetadataIR) -> Self { +impl From for SignedExtensionMetadata { + fn from(ir: TransactionExtensionMetadataIR) -> Self { SignedExtensionMetadata { identifier: ir.identifier, ty: ir.ty, @@ -105,7 +105,7 @@ impl From for ExtrinsicMetadata { call_ty: ir.call_ty, signature_ty: ir.signature_ty, extra_ty: ir.extra_ty, - signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), + signed_extensions: ir.extensions.into_iter().map(Into::into).collect(), } } } diff --git a/substrate/primitives/mixnet/Cargo.toml b/substrate/primitives/mixnet/Cargo.toml index 6ea7a6cbe8c436359a763effe7075e7099787628..39cf684b9772a60f0b8b0932470ccdaf3e4d89ac 100644 --- a/substrate/primitives/mixnet/Cargo.toml +++ b/substrate/primitives/mixnet/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate mixnet types and runtime interface" name = "sp-mixnet" -version = "0.1.0-dev" +version = "0.4.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition = "2021" diff --git a/substrate/primitives/npos-elections/Cargo.toml b/substrate/primitives/npos-elections/Cargo.toml index 5d7e8704f4cb0729acf5944a4fabfa716ffc7c16..7373aa849cb8d8f47fe3cffefd5302eee28a7dfb 100644 --- a/substrate/primitives/npos-elections/Cargo.toml +++ b/substrate/primitives/npos-elections/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-npos-elections" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"], optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-arithmetic = { path = "../arithmetic", default-features = false } sp-core = { path = "../core", default-features = false } sp-runtime = { path = "../runtime", default-features = false } diff --git a/substrate/primitives/npos-elections/fuzzer/Cargo.toml b/substrate/primitives/npos-elections/fuzzer/Cargo.toml index 931773254365f82566b37c766046fcb2aacca653..bcd908b970508f19bd131d9eba08c18306b632c8 100644 --- a/substrate/primitives/npos-elections/fuzzer/Cargo.toml +++ b/substrate/primitives/npos-elections/fuzzer/Cargo.toml @@ -17,7 +17,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["small_rng", "std"] } sp-npos-elections = { path = ".." } diff --git a/substrate/primitives/offchain/Cargo.toml b/substrate/primitives/offchain/Cargo.toml index 19d66ae31e9fe6941937a08b50e8dd8fdc762a56..1f5bf0ce039328fbadc791cc48e36e76a88f05a9 100644 --- a/substrate/primitives/offchain/Cargo.toml +++ b/substrate/primitives/offchain/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Substrate offchain workers primitives" name = "sp-offchain" -version = "4.0.0-dev" +version = "26.0.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true diff --git a/substrate/primitives/panic-handler/Cargo.toml b/substrate/primitives/panic-handler/Cargo.toml index a0df527f56e0818c3eb78309b8171675538d15f6..9c7ba4ba0be8af3b6e4e8197aa071503a66f44e1 100644 --- a/substrate/primitives/panic-handler/Cargo.toml +++ b/substrate/primitives/panic-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-panic-handler" -version = "8.0.0" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/rpc/Cargo.toml b/substrate/primitives/rpc/Cargo.toml index 07bb3bf7293ba48c664f1a1d49eb156421026c4c..dce0eeee9f99bd9cd544e584e07cd3c29ca20ae1 100644 --- a/substrate/primitives/rpc/Cargo.toml +++ b/substrate/primitives/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-rpc" -version = "6.0.0" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,8 +17,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] rustc-hash = "1.1.0" -serde = { version = "1.0.195", features = ["derive"] } +serde = { features = ["derive"], workspace = true, default-features = true } sp-core = { path = "../core" } [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } diff --git a/substrate/primitives/rpc/src/list.rs b/substrate/primitives/rpc/src/list.rs index 860e5161b97c1d4414c63e418f648c13c82936cc..f1b06f3d2231e3c9deb926f2878e8fd9926ad729 100644 --- a/substrate/primitives/rpc/src/list.rs +++ b/substrate/primitives/rpc/src/list.rs @@ -29,7 +29,7 @@ use serde::{Deserialize, Serialize}; /// /// Also it's nice to be able to maintain backward compatibility for methods that /// were initially taking a value and now we want to expand them to take a list. -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[serde(untagged)] pub enum ListOrValue { /// A list of values of given type. diff --git a/substrate/primitives/runtime-interface/Cargo.toml b/substrate/primitives/runtime-interface/Cargo.toml index 0fa2d1f3276cfa01ca3ec3905ac12ba7ebd8bd49..b4fab17eeb7c1872404cbc9d03bc775be6736fe9 100644 --- a/substrate/primitives/runtime-interface/Cargo.toml +++ b/substrate/primitives/runtime-interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime-interface" -version = "17.0.0" +version = "24.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -29,6 +29,9 @@ primitive-types = { version = "0.12.0", default-features = false } sp-storage = { path = "../storage", default-features = false } impl-trait-for-tuples = "0.2.2" +[target.'cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), substrate_runtime))'.dependencies] +polkavm-derive = { workspace = true } + [dev-dependencies] sp-runtime-interface-test-wasm = { path = "test-wasm" } sp-state-machine = { path = "../state-machine" } diff --git a/substrate/primitives/runtime-interface/proc-macro/Cargo.toml b/substrate/primitives/runtime-interface/proc-macro/Cargo.toml index 869cad06e56cc0ac2c436baea17f223032d48666..7dbd810fea932fd4bf284f80c1de976be7009b6d 100644 --- a/substrate/primitives/runtime-interface/proc-macro/Cargo.toml +++ b/substrate/primitives/runtime-interface/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime-interface-proc-macro" -version = "11.0.0" +version = "17.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -22,6 +22,6 @@ proc-macro = true Inflector = "0.11.4" proc-macro-crate = "3.0.0" proc-macro2 = "1.0.56" -quote = "1.0.28" +quote = { workspace = true } expander = "2.0.0" -syn = { version = "2.0.48", features = ["extra-traits", "fold", "full", "visit"] } +syn = { features = ["extra-traits", "fold", "full", "visit"], workspace = true } diff --git a/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs b/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs index 77a29bec3807fa41d224bfb22dd3824e4ff1bb6e..32455b39eed6ff2a1757c325ee6907d14cfd8eb1 100644 --- a/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs +++ b/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs @@ -109,7 +109,12 @@ fn function_no_std_impl( }; let maybe_unreachable = if method.should_trap_on_return() { quote! { - ; core::arch::wasm32::unreachable(); + ; + #[cfg(target_family = "wasm")] + { core::arch::wasm32::unreachable(); } + + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + unsafe { core::arch::asm!("unimp", options(noreturn)); } } } else { quote! {} @@ -118,7 +123,7 @@ fn function_no_std_impl( let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version")); let cfg_wasm_only = if is_wasm_only { - quote! { #[cfg(target_arch = "wasm32")] } + quote! { #[cfg(substrate_runtime)] } } else { quote! {} }; diff --git a/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs b/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs index 77a9e56eecba5c91e3a36534f8f79da6ead30107..fc985157cdb7f3e5154d8647f38c01a1378e2fbe 100644 --- a/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs +++ b/substrate/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs @@ -116,8 +116,8 @@ fn generate_extern_host_function( #(#cfg_attrs)* #[doc = #doc_string] pub fn #function ( #( #args ),* ) #return_value { + #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), #crate_::polkavm::polkavm_import(abi = #crate_::polkavm::polkavm_abi))] extern "C" { - /// The extern function. pub fn #ext_function ( #( #arg_names: <#arg_types as #crate_::RIType>::FFIType ),* ) #ffi_return_value; diff --git a/substrate/primitives/runtime-interface/src/lib.rs b/substrate/primitives/runtime-interface/src/lib.rs index 1f1638880bb6c8655573ad260aac9cd40e6a5592..8b0edf1ec818e57237e6170c8d27f2d1f492cc46 100644 --- a/substrate/primitives/runtime-interface/src/lib.rs +++ b/substrate/primitives/runtime-interface/src/lib.rs @@ -376,6 +376,9 @@ pub use sp_externalities::{ #[doc(hidden)] pub use codec; +#[cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), substrate_runtime))] +pub mod polkavm; + #[cfg(feature = "std")] pub mod host; pub(crate) mod impls; diff --git a/substrate/primitives/runtime-interface/src/polkavm.rs b/substrate/primitives/runtime-interface/src/polkavm.rs new file mode 100644 index 0000000000000000000000000000000000000000..484a269fd14b7e911091ce9d3c2028df09727c2b --- /dev/null +++ b/substrate/primitives/runtime-interface/src/polkavm.rs @@ -0,0 +1,30 @@ +// 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 polkavm_derive::{polkavm_export, polkavm_import}; + +#[polkavm_derive::polkavm_define_abi(allow_extra_input_registers)] +pub mod polkavm_abi {} + +impl self::polkavm_abi::FromHost for *mut u8 { + type Regs = (u32,); + + #[inline] + fn from_host((value,): Self::Regs) -> Self { + value as *mut u8 + } +} diff --git a/substrate/primitives/runtime-interface/src/util.rs b/substrate/primitives/runtime-interface/src/util.rs index 8db32271a0e7e7b682fb4b7012ea897ca8d02640..86c8e4b50e29310358dd90dca772a9714d986a38 100644 --- a/substrate/primitives/runtime-interface/src/util.rs +++ b/substrate/primitives/runtime-interface/src/util.rs @@ -21,7 +21,7 @@ pub fn pack_ptr_and_len(ptr: u32, len: u32) -> u64 { // The static assertions from above are changed into a runtime check. #[cfg(all(not(feature = "std"), feature = "disable_target_static_assertions"))] - assert_eq!(4, sp_std::mem::size_of::()); + assert_eq!(4, core::mem::size_of::()); (u64::from(len) << 32) | u64::from(ptr) } @@ -34,7 +34,7 @@ pub fn pack_ptr_and_len(ptr: u32, len: u32) -> u64 { pub fn unpack_ptr_and_len(val: u64) -> (u32, u32) { // The static assertions from above are changed into a runtime check. #[cfg(all(not(feature = "std"), feature = "disable_target_static_assertions"))] - assert_eq!(4, sp_std::mem::size_of::()); + assert_eq!(4, core::mem::size_of::()); let ptr = (val & (!0u32 as u64)) as u32; let len = (val >> 32) as u32; diff --git a/substrate/primitives/runtime-interface/src/wasm.rs b/substrate/primitives/runtime-interface/src/wasm.rs index 91205addf21a0fead6c36391c24ff77ee97c951e..10bb50c64039895dd4e5fc6f738f0d249652bbe9 100644 --- a/substrate/primitives/runtime-interface/src/wasm.rs +++ b/substrate/primitives/runtime-interface/src/wasm.rs @@ -19,7 +19,7 @@ use crate::RIType; -use sp_std::cell::Cell; +use core::cell::Cell; /// Something that can be created from a ffi value. /// diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index f347a6cfe6de787374fd8f91bb08d459053da0d3..758cd8944fdc1603f569e966f0dea2347c830635 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime" -version = "24.0.0" +version = "31.0.1" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,28 +17,29 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +tuplex = { version = "0.1.2", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } either = { version = "1.5", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } impl-trait-for-tuples = "0.2.2" -log = { version = "0.4.17", default-features = false } +log = { workspace = true } paste = "1.0" rand = { version = "0.8.5", optional = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"], optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-application-crypto = { path = "../application-crypto", default-features = false } sp-arithmetic = { path = "../arithmetic", default-features = false } sp-core = { path = "../core", default-features = false } sp-io = { path = "../io", default-features = false } sp-std = { path = "../std", default-features = false } sp-weights = { path = "../weights", default-features = false } -docify = { version = "0.2.6" } +docify = { version = "0.2.7" } -simple-mermaid = { git = "https://github.com/kianenigma/simple-mermaid.git", rev = "e48b187bcfd5cc75111acd9d241f1bd36604344b", optional = true } +simple-mermaid = { version = "0.1.1", optional = true } [dev-dependencies] rand = "0.8.5" -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } zstd = { version = "0.12.4", default-features = false } sp-api = { path = "../api" } sp-state-machine = { path = "../state-machine" } @@ -67,6 +68,7 @@ std = [ "sp-std/std", "sp-tracing/std", "sp-weights/std", + "tuplex/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/runtime/docs b/substrate/primitives/runtime/docs new file mode 120000 index 0000000000000000000000000000000000000000..48c4a0c02f1a5599f2812bd64aabd0e98ff45f96 --- /dev/null +++ b/substrate/primitives/runtime/docs @@ -0,0 +1 @@ +../../../docs \ No newline at end of file diff --git a/substrate/primitives/runtime/src/generic/checked_extrinsic.rs b/substrate/primitives/runtime/src/generic/checked_extrinsic.rs index 44325920beee0c1026c4ec7dc68b6943652dfc92..e28c8fe424abc7a0d27d41a6751e1118f50d945d 100644 --- a/substrate/primitives/runtime/src/generic/checked_extrinsic.rs +++ b/substrate/primitives/runtime/src/generic/checked_extrinsic.rs @@ -18,81 +18,115 @@ //! Generic implementation of an extrinsic that has passed the verification //! stage. +use codec::Encode; + use crate::{ traits::{ - self, DispatchInfoOf, Dispatchable, MaybeDisplay, Member, PostDispatchInfoOf, - SignedExtension, ValidateUnsigned, + self, transaction_extension::TransactionExtension, DispatchInfoOf, DispatchTransaction, + Dispatchable, MaybeDisplay, Member, PostDispatchInfoOf, ValidateUnsigned, }, transaction_validity::{TransactionSource, TransactionValidity}, }; +/// The kind of extrinsic this is, including any fields required of that kind. This is basically +/// the full extrinsic except the `Call`. +#[derive(PartialEq, Eq, Clone, sp_core::RuntimeDebug)] +pub enum ExtrinsicFormat { + /// Extrinsic is bare; it must pass either the bare forms of `TransactionExtension` or + /// `ValidateUnsigned`, both deprecated, or alternatively a `ProvideInherent`. + Bare, + /// Extrinsic has a default `Origin` of `Signed(AccountId)` and must pass all + /// `TransactionExtension`s regular checks and includes all extension data. + Signed(AccountId, Extension), + /// Extrinsic has a default `Origin` of `None` and must pass all `TransactionExtension`s. + /// regular checks and includes all extension data. + General(Extension), +} + +// TODO: Rename ValidateUnsigned to ValidateInherent +// TODO: Consider changing ValidateInherent API to avoid need for duplicating validate +// code into pre_dispatch (rename that to `prepare`). +// TODO: New extrinsic type corresponding to `ExtrinsicFormat::General`, which is +// unsigned but includes extension data. +// TODO: Move usage of `signed` to `format`: +// - Inherent instead of None. +// - Signed(id, extension) instead of Some((id, extra)). +// - Introduce General(extension) for one without a signature. + /// Definition of something that the external world might want to say; its existence implies that it /// has been checked and is good, particularly with regards to the signature. /// /// This is typically passed into [`traits::Applyable::apply`], which should execute /// [`CheckedExtrinsic::function`], alongside all other bits and bobs. #[derive(PartialEq, Eq, Clone, sp_core::RuntimeDebug)] -pub struct CheckedExtrinsic { +pub struct CheckedExtrinsic { /// Who this purports to be from and the number of extrinsics have come before /// from the same signer, if anyone (note this is not a signature). - pub signed: Option<(AccountId, Extra)>, + pub format: ExtrinsicFormat, /// The function that should be called. pub function: Call, } -impl traits::Applyable - for CheckedExtrinsic +impl traits::Applyable + for CheckedExtrinsic where AccountId: Member + MaybeDisplay, - Call: Member + Dispatchable, - Extra: SignedExtension, + Call: Member + Dispatchable + Encode, + Extension: TransactionExtension, RuntimeOrigin: From>, { type Call = Call; - fn validate>( + fn validate>( &self, - // TODO [#5006;ToDr] should source be passed to `SignedExtension`s? - // Perhaps a change for 2.0 to avoid breaking too much APIs? source: TransactionSource, info: &DispatchInfoOf, len: usize, ) -> TransactionValidity { - if let Some((ref id, ref extra)) = self.signed { - Extra::validate(extra, id, &self.function, info, len) - } else { - let valid = Extra::validate_unsigned(&self.function, info, len)?; - let unsigned_validation = U::validate_unsigned(source, &self.function)?; - Ok(valid.combine_with(unsigned_validation)) + match self.format { + ExtrinsicFormat::Bare => { + let inherent_validation = I::validate_unsigned(source, &self.function)?; + #[allow(deprecated)] + let legacy_validation = Extension::validate_bare_compat(&self.function, info, len)?; + Ok(legacy_validation.combine_with(inherent_validation)) + }, + ExtrinsicFormat::Signed(ref signer, ref extension) => { + let origin = Some(signer.clone()).into(); + extension.validate_only(origin, &self.function, info, len).map(|x| x.0) + }, + ExtrinsicFormat::General(ref extension) => + extension.validate_only(None.into(), &self.function, info, len).map(|x| x.0), } } - fn apply>( + fn apply>( self, info: &DispatchInfoOf, len: usize, ) -> crate::ApplyExtrinsicResultWithInfo> { - let (maybe_who, maybe_pre) = if let Some((id, extra)) = self.signed { - let pre = Extra::pre_dispatch(extra, &id, &self.function, info, len)?; - (Some(id), Some(pre)) - } else { - Extra::pre_dispatch_unsigned(&self.function, info, len)?; - U::pre_dispatch(&self.function)?; - (None, None) - }; - let res = self.function.dispatch(RuntimeOrigin::from(maybe_who)); - let post_info = match res { - Ok(info) => info, - Err(err) => err.post_info, - }; - Extra::post_dispatch( - maybe_pre, - info, - &post_info, - len, - &res.map(|_| ()).map_err(|e| e.error), - )?; - Ok(res) + match self.format { + ExtrinsicFormat::Bare => { + I::pre_dispatch(&self.function)?; + // TODO: Remove below once `pre_dispatch_unsigned` is removed from `LegacyExtension` + // or `LegacyExtension` is removed. + #[allow(deprecated)] + Extension::validate_bare_compat(&self.function, info, len)?; + #[allow(deprecated)] + Extension::pre_dispatch_bare_compat(&self.function, info, len)?; + let res = self.function.dispatch(None.into()); + let post_info = res.unwrap_or_else(|err| err.post_info); + let pd_res = res.map(|_| ()).map_err(|e| e.error); + // TODO: Remove below once `pre_dispatch_unsigned` is removed from `LegacyExtension` + // or `LegacyExtension` is removed. + #[allow(deprecated)] + Extension::post_dispatch_bare_compat(info, &post_info, len, &pd_res)?; + Ok(res) + }, + ExtrinsicFormat::Signed(signer, extension) => + extension.dispatch_transaction(Some(signer).into(), self.function, info, len), + ExtrinsicFormat::General(extension) => + extension.dispatch_transaction(None.into(), self.function, info, len), + } } } diff --git a/substrate/primitives/runtime/src/generic/header.rs b/substrate/primitives/runtime/src/generic/header.rs index 0eeef363a06dc95e999a0277563e1c86f46e7028..d78aa5c8d3c29ea35eacecdbd0c40ebe0bfec926 100644 --- a/substrate/primitives/runtime/src/generic/header.rs +++ b/substrate/primitives/runtime/src/generic/header.rs @@ -133,7 +133,7 @@ where impl Header where Number: Member - + sp_std::hash::Hash + + core::hash::Hash + Copy + MaybeDisplay + AtLeast32BitUnsigned diff --git a/substrate/primitives/runtime/src/generic/mod.rs b/substrate/primitives/runtime/src/generic/mod.rs index 3687f7cdb3b2b24c087ac47c470b4d39721e2df5..5713c166b04c81045ccf458a538d4ca083178840 100644 --- a/substrate/primitives/runtime/src/generic/mod.rs +++ b/substrate/primitives/runtime/src/generic/mod.rs @@ -29,9 +29,9 @@ mod unchecked_extrinsic; pub use self::{ block::{Block, BlockId, SignedBlock}, - checked_extrinsic::CheckedExtrinsic, + checked_extrinsic::{CheckedExtrinsic, ExtrinsicFormat}, digest::{Digest, DigestItem, DigestItemRef, OpaqueDigestItemId}, era::{Era, Phase}, header::Header, - unchecked_extrinsic::{SignedPayload, UncheckedExtrinsic}, + unchecked_extrinsic::{Preamble, SignedPayload, UncheckedExtrinsic}, }; diff --git a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs index f066de2323da3726244757485ba3089792cfa022..44dfda13d9441c2a2408628d58e57f673719ffcf 100644 --- a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -18,10 +18,11 @@ //! Generic implementation of an unchecked (pre-verification) extrinsic. use crate::{ - generic::CheckedExtrinsic, + generic::{CheckedExtrinsic, ExtrinsicFormat}, traits::{ - self, Checkable, Extrinsic, ExtrinsicMetadata, IdentifyAccount, MaybeDisplay, Member, - SignaturePayload, SignedExtension, + self, transaction_extension::TransactionExtensionBase, Checkable, Dispatchable, Extrinsic, + ExtrinsicMetadata, IdentifyAccount, MaybeDisplay, Member, SignaturePayload, + TransactionExtension, }, transaction_validity::{InvalidTransaction, TransactionValidityError}, OpaqueExtrinsic, @@ -40,8 +41,60 @@ use sp_std::{fmt, prelude::*}; /// the decoding fails. const EXTRINSIC_FORMAT_VERSION: u8 = 4; -/// The `SingaturePayload` of `UncheckedExtrinsic`. -type UncheckedSignaturePayload = (Address, Signature, Extra); +/// The `SignaturePayload` of `UncheckedExtrinsic`. +type UncheckedSignaturePayload = (Address, Signature, Extension); + +impl SignaturePayload + for UncheckedSignaturePayload +{ + type SignatureAddress = Address; + type Signature = Signature; + type SignatureExtra = Extension; +} + +/// A "header" for extrinsics leading up to the call itself. Determines the type of extrinsic and +/// holds any necessary specialized data. +#[derive(Eq, PartialEq, Clone, Encode, Decode)] +pub enum Preamble { + /// An extrinsic without a signature or any extension. This means it's either an inherent or + /// an old-school "Unsigned" (we don't use that terminology any more since it's confusable with + /// the general transaction which is without a signature but does have an extension). + /// + /// NOTE: In the future, once we remove `ValidateUnsigned`, this will only serve Inherent + /// extrinsics and thus can be renamed to `Inherent`. + #[codec(index = 0b00000100)] + Bare, + /// An old-school transaction extrinsic which includes a signature of some hard-coded crypto. + #[codec(index = 0b10000100)] + Signed(Address, Signature, Extension), + /// A new-school transaction extrinsic which does not include a signature. + #[codec(index = 0b01000100)] + General(Extension), +} + +impl Preamble { + /// Returns `Some` if this is a signed extrinsic, together with the relevant inner fields. + pub fn to_signed(self) -> Option<(Address, Signature, Extension)> { + match self { + Self::Signed(a, s, e) => Some((a, s, e)), + _ => None, + } + } +} + +impl fmt::Debug for Preamble +where + Address: fmt::Debug, + Extension: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Bare => write!(f, "Bare"), + Self::Signed(address, _, tx_ext) => write!(f, "Signed({:?}, {:?})", address, tx_ext), + Self::General(tx_ext) => write!(f, "General({:?})", tx_ext), + } + } +} /// An extrinsic right from the external world. This is unchecked and so can contain a signature. /// @@ -57,7 +110,7 @@ type UncheckedSignaturePayload = (Address, Signature, /// could in principle be any other interaction. Transactions are either signed or unsigned. A /// sensible transaction pool should ensure that only transactions that are worthwhile are /// considered for block-building. -#[cfg_attr(feature = "std", doc = simple_mermaid::mermaid!("../../../../../docs/mermaid/extrinsics.mmd"))] +#[cfg_attr(feature = "std", doc = simple_mermaid::mermaid!("../../docs/mermaid/extrinsics.mmd"))] /// This type is by no means enforced within Substrate, but given its genericness, it is highly /// likely that for most use-cases it will suffice. Thus, the encoding of this type will dictate /// exactly what bytes should be sent to a runtime to transact with it. @@ -65,41 +118,28 @@ type UncheckedSignaturePayload = (Address, Signature, /// This can be checked using [`Checkable`], yielding a [`CheckedExtrinsic`], which is the /// counterpart of this type after its signature (and other non-negotiable validity checks) have /// passed. -#[derive(PartialEq, Eq, Clone)] -pub struct UncheckedExtrinsic -where - Extra: SignedExtension, -{ - /// The signature, address, number of extrinsics have come before from the same signer and an - /// era describing the longevity of this transaction, if this is a signed extrinsic. - /// - /// `None` if it is unsigned or an inherent. - pub signature: Option>, +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct UncheckedExtrinsic { + /// Information regarding the type of extrinsic this is (inherent or transaction) as well as + /// associated extension (`Extension`) data if it's a transaction and a possible signature. + pub preamble: Preamble, /// The function that should be called. pub function: Call, } -impl SignaturePayload - for UncheckedSignaturePayload -{ - type SignatureAddress = Address; - type Signature = Signature; - type SignatureExtra = Extra; -} - /// Manual [`TypeInfo`] implementation because of custom encoding. The data is a valid encoded /// `Vec`, but requires some logic to extract the signature and payload. /// /// See [`UncheckedExtrinsic::encode`] and [`UncheckedExtrinsic::decode`]. -impl TypeInfo - for UncheckedExtrinsic +impl TypeInfo + for UncheckedExtrinsic where Address: StaticTypeInfo, Call: StaticTypeInfo, Signature: StaticTypeInfo, - Extra: SignedExtension + StaticTypeInfo, + Extension: StaticTypeInfo, { - type Identity = UncheckedExtrinsic; + type Identity = UncheckedExtrinsic; fn type_info() -> Type { Type::builder() @@ -111,7 +151,7 @@ where TypeParameter::new("Address", Some(meta_type::
())), TypeParameter::new("Call", Some(meta_type::())), TypeParameter::new("Signature", Some(meta_type::())), - TypeParameter::new("Extra", Some(meta_type::())), + TypeParameter::new("Extra", Some(meta_type::())), ]) .docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"]) // Because of the custom encoding, we can only accurately describe the encoding as an @@ -121,66 +161,113 @@ where } } -impl - UncheckedExtrinsic -{ - /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(function: Call, signed: Address, signature: Signature, extra: Extra) -> Self { - Self { signature: Some((signed, signature, extra)), function } +impl UncheckedExtrinsic { + /// New instance of a bare (ne unsigned) extrinsic. This could be used for an inherent or an + /// old-school "unsigned transaction" (which are new being deprecated in favour of general + /// transactions). + #[deprecated = "Use new_bare instead"] + pub fn new_unsigned(function: Call) -> Self { + Self::new_bare(function) } - /// New instance of an unsigned extrinsic aka "inherent". - pub fn new_unsigned(function: Call) -> Self { - Self { signature: None, function } + /// Returns `true` if this extrinsic instance is an inherent, `false`` otherwise. + pub fn is_inherent(&self) -> bool { + matches!(self.preamble, Preamble::Bare) + } + + /// Returns `true` if this extrinsic instance is an old-school signed transaction, `false` + /// otherwise. + pub fn is_signed(&self) -> bool { + matches!(self.preamble, Preamble::Signed(..)) + } + + /// Create an `UncheckedExtrinsic` from a `Preamble` and the actual `Call`. + pub fn from_parts(function: Call, preamble: Preamble) -> Self { + Self { preamble, function } + } + + /// New instance of a bare (ne unsigned) extrinsic. + pub fn new_bare(function: Call) -> Self { + Self { preamble: Preamble::Bare, function } + } + + /// New instance of an old-school signed transaction. + pub fn new_signed( + function: Call, + signed: Address, + signature: Signature, + tx_ext: Extension, + ) -> Self { + Self { preamble: Preamble::Signed(signed, signature, tx_ext), function } + } + + /// New instance of an new-school unsigned transaction. + pub fn new_transaction(function: Call, tx_ext: Extension) -> Self { + Self { preamble: Preamble::General(tx_ext), function } } } -impl - Extrinsic for UncheckedExtrinsic +// TODO: We can get rid of this trait and just use UncheckedExtrinsic directly. + +impl Extrinsic + for UncheckedExtrinsic { type Call = Call; - type SignaturePayload = UncheckedSignaturePayload; + type SignaturePayload = UncheckedSignaturePayload; + + fn is_bare(&self) -> bool { + matches!(self.preamble, Preamble::Bare) + } fn is_signed(&self) -> Option { - Some(self.signature.is_some()) + Some(matches!(self.preamble, Preamble::Signed(..))) } fn new(function: Call, signed_data: Option) -> Option { Some(if let Some((address, signature, extra)) = signed_data { Self::new_signed(function, address, signature, extra) } else { - Self::new_unsigned(function) + Self::new_bare(function) }) } + + fn new_inherent(function: Call) -> Self { + Self::new_bare(function) + } } -impl Checkable - for UncheckedExtrinsic +impl Checkable + for UncheckedExtrinsic where LookupSource: Member + MaybeDisplay, - Call: Encode + Member, + Call: Encode + Member + Dispatchable, Signature: Member + traits::Verify, ::Signer: IdentifyAccount, - Extra: SignedExtension, + Extension: Encode + TransactionExtension, AccountId: Member + MaybeDisplay, Lookup: traits::Lookup, { - type Checked = CheckedExtrinsic; + type Checked = CheckedExtrinsic; fn check(self, lookup: &Lookup) -> Result { - Ok(match self.signature { - Some((signed, signature, extra)) => { + Ok(match self.preamble { + Preamble::Signed(signed, signature, tx_ext) => { let signed = lookup.lookup(signed)?; - let raw_payload = SignedPayload::new(self.function, extra)?; + // CHECK! Should this not contain implicit? + let raw_payload = SignedPayload::new(self.function, tx_ext)?; if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) { return Err(InvalidTransaction::BadProof.into()) } - - let (function, extra, _) = raw_payload.deconstruct(); - CheckedExtrinsic { signed: Some((signed, extra)), function } + let (function, tx_ext, _) = raw_payload.deconstruct(); + CheckedExtrinsic { format: ExtrinsicFormat::Signed(signed, tx_ext), function } + }, + Preamble::General(tx_ext) => CheckedExtrinsic { + format: ExtrinsicFormat::General(tx_ext), + function: self.function, }, - None => CheckedExtrinsic { signed: None, function: self.function }, + Preamble::Bare => + CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }, }) } @@ -189,91 +276,38 @@ where self, lookup: &Lookup, ) -> Result { - Ok(match self.signature { - Some((signed, _, extra)) => { + Ok(match self.preamble { + Preamble::Signed(signed, _, extra) => { let signed = lookup.lookup(signed)?; - let raw_payload = SignedPayload::new(self.function, extra)?; - let (function, extra, _) = raw_payload.deconstruct(); - CheckedExtrinsic { signed: Some((signed, extra)), function } + CheckedExtrinsic { + format: ExtrinsicFormat::Signed(signed, extra), + function: self.function, + } }, - None => CheckedExtrinsic { signed: None, function: self.function }, + Preamble::General(extra) => CheckedExtrinsic { + format: ExtrinsicFormat::General(extra), + function: self.function, + }, + Preamble::Bare => + CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }, }) } } -impl ExtrinsicMetadata - for UncheckedExtrinsic -where - Extra: SignedExtension, +impl> + ExtrinsicMetadata for UncheckedExtrinsic { const VERSION: u8 = EXTRINSIC_FORMAT_VERSION; - type SignedExtensions = Extra; -} - -/// A payload that has been signed for an unchecked extrinsics. -/// -/// Note that the payload that we sign to produce unchecked extrinsic signature -/// is going to be different than the `SignaturePayload` - so the thing the extrinsic -/// actually contains. -pub struct SignedPayload((Call, Extra, Extra::AdditionalSigned)); - -impl SignedPayload -where - Call: Encode, - Extra: SignedExtension, -{ - /// Create new `SignedPayload`. - /// - /// This function may fail if `additional_signed` of `Extra` is not available. - pub fn new(call: Call, extra: Extra) -> Result { - let additional_signed = extra.additional_signed()?; - let raw_payload = (call, extra, additional_signed); - Ok(Self(raw_payload)) - } - - /// Create new `SignedPayload` from raw components. - pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self { - Self((call, extra, additional_signed)) - } - - /// Deconstruct the payload into it's components. - pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) { - self.0 - } + type Extra = Extension; } -impl Encode for SignedPayload -where - Call: Encode, - Extra: SignedExtension, -{ - /// Get an encoded version of this payload. - /// - /// Payloads longer than 256 bytes are going to be `blake2_256`-hashed. - fn using_encoded R>(&self, f: F) -> R { - self.0.using_encoded(|payload| { - if payload.len() > 256 { - f(&blake2_256(payload)[..]) - } else { - f(payload) - } - }) - } -} - -impl EncodeLike for SignedPayload -where - Call: Encode, - Extra: SignedExtension, -{ -} - -impl Decode for UncheckedExtrinsic +impl Decode + for UncheckedExtrinsic where Address: Decode, Signature: Decode, Call: Decode, - Extra: SignedExtension, + Extension: Decode, { fn decode(input: &mut I) -> Result { // This is a little more complicated than usual since the binary format must be compatible @@ -282,15 +316,7 @@ where let expected_length: Compact = Decode::decode(input)?; let before_length = input.remaining_len()?; - let version = input.read_byte()?; - - let is_signed = version & 0b1000_0000 != 0; - let version = version & 0b0111_1111; - if version != EXTRINSIC_FORMAT_VERSION { - return Err("Invalid transaction version".into()) - } - - let signature = is_signed.then(|| Decode::decode(input)).transpose()?; + let preamble = Decode::decode(input)?; let function = Decode::decode(input)?; if let Some((before_length, after_length)) = @@ -303,31 +329,20 @@ where } } - Ok(Self { signature, function }) + Ok(Self { preamble, function }) } } #[docify::export(unchecked_extrinsic_encode_impl)] -impl Encode for UncheckedExtrinsic +impl Encode + for UncheckedExtrinsic where - Address: Encode, - Signature: Encode, + Preamble: Encode, Call: Encode, - Extra: SignedExtension, + Extension: Encode, { fn encode(&self) -> Vec { - let mut tmp = Vec::with_capacity(sp_std::mem::size_of::()); - - // 1 byte version id. - match self.signature.as_ref() { - Some(s) => { - tmp.push(EXTRINSIC_FORMAT_VERSION | 0b1000_0000); - s.encode_to(&mut tmp); - }, - None => { - tmp.push(EXTRINSIC_FORMAT_VERSION & 0b0111_1111); - }, - } + let mut tmp = self.preamble.encode(); self.function.encode_to(&mut tmp); let compact_len = codec::Compact::(tmp.len() as u32); @@ -342,19 +357,19 @@ where } } -impl EncodeLike - for UncheckedExtrinsic +impl EncodeLike + for UncheckedExtrinsic where Address: Encode, Signature: Encode, - Call: Encode, - Extra: SignedExtension, + Call: Encode + Dispatchable, + Extension: TransactionExtension, { } #[cfg(feature = "serde")] -impl serde::Serialize - for UncheckedExtrinsic +impl serde::Serialize + for UncheckedExtrinsic { fn serialize(&self, seq: S) -> Result where @@ -365,45 +380,88 @@ impl s } #[cfg(feature = "serde")] -impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extra: SignedExtension> - serde::Deserialize<'a> for UncheckedExtrinsic +impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extension: Decode> serde::Deserialize<'a> + for UncheckedExtrinsic { fn deserialize(de: D) -> Result where D: serde::Deserializer<'a>, { let r = sp_core::bytes::deserialize(de)?; - Decode::decode(&mut &r[..]) + Self::decode(&mut &r[..]) .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e))) } } -impl fmt::Debug - for UncheckedExtrinsic +/// A payload that has been signed for an unchecked extrinsics. +/// +/// Note that the payload that we sign to produce unchecked extrinsic signature +/// is going to be different than the `SignaturePayload` - so the thing the extrinsic +/// actually contains. +pub struct SignedPayload( + (Call, Extension, Extension::Implicit), +); + +impl SignedPayload where - Address: fmt::Debug, - Call: fmt::Debug, - Extra: SignedExtension, + Call: Encode + Dispatchable, + Extension: TransactionExtensionBase, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "UncheckedExtrinsic({:?}, {:?})", - self.signature.as_ref().map(|x| (&x.0, &x.2)), - self.function, - ) + /// Create new `SignedPayload`. + /// + /// This function may fail if `implicit` of `Extension` is not available. + pub fn new(call: Call, tx_ext: Extension) -> Result { + let implicit = Extension::implicit(&tx_ext)?; + let raw_payload = (call, tx_ext, implicit); + Ok(Self(raw_payload)) + } + + /// Create new `SignedPayload` from raw components. + pub fn from_raw(call: Call, tx_ext: Extension, implicit: Extension::Implicit) -> Self { + Self((call, tx_ext, implicit)) + } + + /// Deconstruct the payload into it's components. + pub fn deconstruct(self) -> (Call, Extension, Extension::Implicit) { + self.0 } } -impl From> - for OpaqueExtrinsic +impl Encode for SignedPayload +where + Call: Encode + Dispatchable, + Extension: TransactionExtensionBase, +{ + /// Get an encoded version of this payload. + /// + /// Payloads longer than 256 bytes are going to be `blake2_256`-hashed. + fn using_encoded R>(&self, f: F) -> R { + self.0.using_encoded(|payload| { + if payload.len() > 256 { + f(&blake2_256(payload)[..]) + } else { + f(payload) + } + }) + } +} + +impl EncodeLike for SignedPayload +where + Call: Encode + Dispatchable, + Extension: TransactionExtensionBase, +{ +} + +impl + From> for OpaqueExtrinsic where Address: Encode, Signature: Encode, Call: Encode, - Extra: SignedExtension, + Extension: Encode, { - fn from(extrinsic: UncheckedExtrinsic) -> Self { + fn from(extrinsic: UncheckedExtrinsic) -> Self { Self::from_bytes(extrinsic.encode().as_slice()).expect( "both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \ raw Vec encoding; qed", @@ -411,60 +469,198 @@ where } } +#[cfg(test)] +mod legacy { + use codec::{Compact, Decode, Encode, EncodeLike, Error, Input}; + use scale_info::{ + build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter, + }; + + pub type OldUncheckedSignaturePayload = (Address, Signature, Extra); + + #[derive(PartialEq, Eq, Clone, Debug)] + pub struct OldUncheckedExtrinsic { + pub signature: Option>, + pub function: Call, + } + + impl TypeInfo + for OldUncheckedExtrinsic + where + Address: StaticTypeInfo, + Call: StaticTypeInfo, + Signature: StaticTypeInfo, + Extra: StaticTypeInfo, + { + type Identity = OldUncheckedExtrinsic; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("UncheckedExtrinsic", module_path!())) + // Include the type parameter types, even though they are not used directly in any + // of the described fields. These type definitions can be used by downstream + // consumers to help construct the custom decoding from the opaque bytes (see + // below). + .type_params(vec![ + TypeParameter::new("Address", Some(meta_type::
())), + TypeParameter::new("Call", Some(meta_type::())), + TypeParameter::new("Signature", Some(meta_type::())), + TypeParameter::new("Extra", Some(meta_type::())), + ]) + .docs(&["OldUncheckedExtrinsic raw bytes, requires custom decoding routine"]) + // Because of the custom encoding, we can only accurately describe the encoding as + // an opaque `Vec`. Downstream consumers will need to manually implement the + // codec to encode/decode the `signature` and `function` fields. + .composite(Fields::unnamed().field(|f| f.ty::>())) + } + } + + impl OldUncheckedExtrinsic { + pub fn new_signed( + function: Call, + signed: Address, + signature: Signature, + extra: Extra, + ) -> Self { + Self { signature: Some((signed, signature, extra)), function } + } + + pub fn new_unsigned(function: Call) -> Self { + Self { signature: None, function } + } + } + + impl Decode + for OldUncheckedExtrinsic + where + Address: Decode, + Signature: Decode, + Call: Decode, + Extra: Decode, + { + fn decode(input: &mut I) -> Result { + // This is a little more complicated than usual since the binary format must be + // compatible with SCALE's generic `Vec` type. Basically this just means accepting + // that there will be a prefix of vector length. + let expected_length: Compact = Decode::decode(input)?; + let before_length = input.remaining_len()?; + + let version = input.read_byte()?; + + let is_signed = version & 0b1000_0000 != 0; + let version = version & 0b0111_1111; + if version != 4u8 { + return Err("Invalid transaction version".into()) + } + + let signature = is_signed.then(|| Decode::decode(input)).transpose()?; + let function = Decode::decode(input)?; + + if let Some((before_length, after_length)) = + input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a))) + { + let length = before_length.saturating_sub(after_length); + + if length != expected_length.0 as usize { + return Err("Invalid length prefix".into()) + } + } + + Ok(Self { signature, function }) + } + } + + #[docify::export(unchecked_extrinsic_encode_impl)] + impl Encode + for OldUncheckedExtrinsic + where + Address: Encode, + Signature: Encode, + Call: Encode, + Extra: Encode, + { + fn encode(&self) -> Vec { + let mut tmp = Vec::with_capacity(sp_std::mem::size_of::()); + + // 1 byte version id. + match self.signature.as_ref() { + Some(s) => { + tmp.push(4u8 | 0b1000_0000); + s.encode_to(&mut tmp); + }, + None => { + tmp.push(4u8 & 0b0111_1111); + }, + } + self.function.encode_to(&mut tmp); + + let compact_len = codec::Compact::(tmp.len() as u32); + + // Allocate the output buffer with the correct length + let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len()); + + compact_len.encode_to(&mut output); + output.extend(tmp); + + output + } + } + + impl EncodeLike + for OldUncheckedExtrinsic + where + Address: Encode, + Signature: Encode, + Call: Encode, + Extra: Encode, + { + } +} + #[cfg(test)] mod tests { - use super::*; + use super::{legacy::OldUncheckedExtrinsic, *}; use crate::{ codec::{Decode, Encode}, + impl_tx_ext_default, testing::TestSignature as TestSig, - traits::{DispatchInfoOf, IdentityLookup, SignedExtension}, + traits::{FakeDispatchable, IdentityLookup, TransactionExtension}, }; use sp_io::hashing::blake2_256; type TestContext = IdentityLookup; type TestAccountId = u64; - type TestCall = Vec; + type TestCall = FakeDispatchable>; const TEST_ACCOUNT: TestAccountId = 0; // NOTE: this is demonstration. One can simply use `()` for testing. #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd, TypeInfo)] - struct TestExtra; - impl SignedExtension for TestExtra { - const IDENTIFIER: &'static str = "TestExtra"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = (); + struct DummyExtension; + impl TransactionExtensionBase for DummyExtension { + const IDENTIFIER: &'static str = "DummyExtension"; + type Implicit = (); + } + impl TransactionExtension for DummyExtension { + type Val = (); type Pre = (); - - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } + impl_tx_ext_default!(TestCall; Context; validate prepare); } - type Ex = UncheckedExtrinsic; - type CEx = CheckedExtrinsic; + type Ex = UncheckedExtrinsic; + type CEx = CheckedExtrinsic; #[test] fn unsigned_codec_should_work() { - let ux = Ex::new_unsigned(vec![0u8; 0]); + let call: TestCall = vec![0u8; 0].into(); + let ux = Ex::new_inherent(call); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); } #[test] fn invalid_length_prefix_is_detected() { - let ux = Ex::new_unsigned(vec![0u8; 0]); + let ux = Ex::new_inherent(vec![0u8; 0].into()); let mut encoded = ux.encode(); let length = Compact::::decode(&mut &encoded[..]).unwrap(); @@ -473,13 +669,20 @@ mod tests { assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into())); } + #[test] + fn transaction_codec_should_work() { + let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); + } + #[test] fn signed_codec_should_work() { let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), - TestExtra, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()), + DummyExtension, ); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); @@ -488,13 +691,13 @@ mod tests { #[test] fn large_signed_codec_should_work() { let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, TestSig( TEST_ACCOUNT, - (vec![0u8; 257], TestExtra).using_encoded(blake2_256)[..].to_owned(), + (vec![0u8; 257], DummyExtension).using_encoded(blake2_256)[..].to_owned(), ), - TestExtra, + DummyExtension, ); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); @@ -502,44 +705,63 @@ mod tests { #[test] fn unsigned_check_should_work() { - let ux = Ex::new_unsigned(vec![0u8; 0]); - assert!(!ux.is_signed().unwrap_or(false)); - assert!(>::check(ux, &Default::default()).is_ok()); + let ux = Ex::new_inherent(vec![0u8; 0].into()); + assert!(ux.is_inherent()); + assert_eq!( + >::check(ux, &Default::default()), + Ok(CEx { format: ExtrinsicFormat::Bare, function: vec![0u8; 0].into() }), + ); } #[test] fn badly_signed_check_should_fail() { let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, vec![0u8; 0]), - TestExtra, + TestSig(TEST_ACCOUNT, vec![0u8; 0].into()), + DummyExtension, ); - assert!(ux.is_signed().unwrap_or(false)); + assert!(!ux.is_inherent()); assert_eq!( >::check(ux, &Default::default()), Err(InvalidTransaction::BadProof.into()), ); } + #[test] + fn transaction_check_should_work() { + let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension); + assert!(!ux.is_inherent()); + assert_eq!( + >::check(ux, &Default::default()), + Ok(CEx { + format: ExtrinsicFormat::General(DummyExtension), + function: vec![0u8; 0].into() + }), + ); + } + #[test] fn signed_check_should_work() { let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), - TestExtra, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()), + DummyExtension, ); - assert!(ux.is_signed().unwrap_or(false)); + assert!(!ux.is_inherent()); assert_eq!( >::check(ux, &Default::default()), - Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }), + Ok(CEx { + format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension), + function: vec![0u8; 0].into() + }), ); } #[test] fn encoding_matches_vec() { - let ex = Ex::new_unsigned(vec![0u8; 0]); + let ex = Ex::new_inherent(vec![0u8; 0].into()); let encoded = ex.encode(); let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); assert_eq!(decoded, ex); @@ -549,7 +771,7 @@ mod tests { #[test] fn conversion_to_opaque() { - let ux = Ex::new_unsigned(vec![0u8; 0]); + let ux = Ex::new_inherent(vec![0u8; 0].into()); let encoded = ux.encode(); let opaque: OpaqueExtrinsic = ux.into(); let opaque_encoded = opaque.encode(); @@ -558,10 +780,62 @@ mod tests { #[test] fn large_bad_prefix_should_work() { - let encoded = Compact::::from(u32::MAX).encode(); - assert_eq!( - Ex::decode(&mut &encoded[..]), - Err(Error::from("Not enough data to fill buffer")) - ); + let encoded = (Compact::::from(u32::MAX), Preamble::<(), (), ()>::Bare).encode(); + assert!(Ex::decode(&mut &encoded[..]).is_err()); + } + + #[test] + fn legacy_signed_encode_decode() { + let call: TestCall = vec![0u8; 0].into(); + let signed = TEST_ACCOUNT; + let signature = TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()); + let extension = DummyExtension; + + let new_ux = Ex::new_signed(call.clone(), signed, signature.clone(), extension.clone()); + let old_ux = + OldUncheckedExtrinsic::::new_signed( + call, signed, signature, extension, + ); + + let encoded_new_ux = new_ux.encode(); + let encoded_old_ux = old_ux.encode(); + + assert_eq!(encoded_new_ux, encoded_old_ux); + + let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap(); + let decoded_old_ux = + OldUncheckedExtrinsic::::decode( + &mut &encoded_old_ux[..], + ) + .unwrap(); + + assert_eq!(new_ux, decoded_new_ux); + assert_eq!(old_ux, decoded_old_ux); + } + + #[test] + fn legacy_unsigned_encode_decode() { + let call: TestCall = vec![0u8; 0].into(); + + let new_ux = Ex::new_bare(call.clone()); + let old_ux = + OldUncheckedExtrinsic::::new_unsigned( + call, + ); + + let encoded_new_ux = new_ux.encode(); + let encoded_old_ux = old_ux.encode(); + + assert_eq!(encoded_new_ux, encoded_old_ux); + + let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap(); + let decoded_old_ux = + OldUncheckedExtrinsic::::decode( + &mut &encoded_old_ux[..], + ) + .unwrap(); + + assert_eq!(new_ux, decoded_new_ux); + assert_eq!(old_ux, decoded_old_ux); } } diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index ddf92554c83056f192015a26285408b7fdf33695..70605970b4efa683deaef19fed7c2cfa920d3e51 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -125,6 +125,11 @@ pub use sp_arithmetic::{ Perquintill, Rational128, Rounding, UpperOf, }; +pub use transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidityError, UnknownTransaction, + ValidTransaction, +}; + pub use either::Either; /// The number of bytes of the module-specific `error` field defined in [`ModuleError`]. @@ -443,21 +448,21 @@ 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()) { + match self { + Self::Ed25519(ref sig) => match ed25519::Public::from_slice(signer.as_ref()) { Ok(signer) => sig.verify(msg, &signer), Err(()) => false, }, - (Self::Sr25519(ref sig), who) => match sr25519::Public::from_slice(who.as_ref()) { + Self::Sr25519(ref sig) => match sr25519::Public::from_slice(signer.as_ref()) { Ok(signer) => sig.verify(msg, &signer), Err(()) => false, }, - (Self::Ecdsa(ref sig), who) => { + Self::Ecdsa(ref 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), + >::as_ref(signer), _ => false, } }, @@ -944,9 +949,13 @@ impl<'a> ::serde::Deserialize<'a> for OpaqueExtrinsic { } } +// TODO: OpaqueExtrinsics cannot act like regular extrinsics, right?! impl traits::Extrinsic for OpaqueExtrinsic { type Call = (); type SignaturePayload = (); + fn is_bare(&self) -> bool { + false + } } /// Print something that implements `Printable` from the runtime. @@ -998,6 +1007,16 @@ impl TransactionOutcome { } } +/// Confines the kind of extrinsics that can be included in a block. +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Encode, Decode, TypeInfo)] +pub enum ExtrinsicInclusionMode { + /// All extrinsics are allowed to be included in this block. + #[default] + AllExtrinsics, + /// Inherents are allowed to be included. + OnlyInherents, +} + #[cfg(test)] mod tests { use crate::traits::BlakeTwo256; diff --git a/substrate/primitives/runtime/src/offchain/storage_lock.rs b/substrate/primitives/runtime/src/offchain/storage_lock.rs index a2da48721e7591909932e58cb20fffbeedb5f88d..56d0eeae527cf072c4b7756690956ab192c5e805 100644 --- a/substrate/primitives/runtime/src/offchain/storage_lock.rs +++ b/substrate/primitives/runtime/src/offchain/storage_lock.rs @@ -66,9 +66,9 @@ use crate::{ traits::BlockNumberProvider, }; use codec::{Codec, Decode, Encode}; +use core::fmt; use sp_core::offchain::{Duration, Timestamp}; use sp_io::offchain; -use sp_std::fmt; /// Default expiry duration for time based locks in milliseconds. const STORAGE_LOCK_DEFAULT_EXPIRY_DURATION: Duration = Duration::from_millis(20_000); diff --git a/substrate/primitives/runtime/src/testing.rs b/substrate/primitives/runtime/src/testing.rs index 5f94c834a8f295bee945e9d772019e12ed37c828..3d6e0b5ab8bc1c2836755f8622b1f0b7adb8d847 100644 --- a/substrate/primitives/runtime/src/testing.rs +++ b/substrate/primitives/runtime/src/testing.rs @@ -21,24 +21,16 @@ use crate::{ codec::{Codec, Decode, Encode, MaxEncodedLen}, generic, scale_info::TypeInfo, - traits::{ - self, Applyable, BlakeTwo256, Checkable, DispatchInfoOf, Dispatchable, OpaqueKeys, - PostDispatchInfoOf, SignaturePayload, SignedExtension, ValidateUnsigned, - }, - transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, - ApplyExtrinsicResultWithInfo, KeyTypeId, + traits::{self, BlakeTwo256, Dispatchable, OpaqueKeys}, + DispatchResultWithInfo, KeyTypeId, }; -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize}; use sp_core::{ crypto::{key_types, ByteArray, CryptoType, Dummy}, U256, }; pub use sp_core::{sr25519, H256}; -use std::{ - cell::RefCell, - fmt::{self, Debug}, - ops::Deref, -}; +use std::{cell::RefCell, fmt::Debug}; /// A dummy type which can be used instead of regular cryptographic primitives. /// @@ -198,42 +190,6 @@ impl Header { } } -/// An opaque extrinsic wrapper type. -#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] -pub struct ExtrinsicWrapper(Xt); - -impl traits::Extrinsic for ExtrinsicWrapper { - type Call = (); - type SignaturePayload = (); - - fn is_signed(&self) -> Option { - None - } -} - -impl serde::Serialize for ExtrinsicWrapper { - fn serialize(&self, seq: S) -> Result - where - S: ::serde::Serializer, - { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} - -impl From for ExtrinsicWrapper { - fn from(xt: Xt) -> Self { - ExtrinsicWrapper(xt) - } -} - -impl Deref for ExtrinsicWrapper { - type Target = Xt; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - /// Testing block #[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, TypeInfo)] pub struct Block { @@ -283,139 +239,22 @@ where } } -/// The signature payload of a `TestXt`. -type TxSingaturePayload = (u64, Extra); - -impl SignaturePayload for TxSingaturePayload { - type SignatureAddress = u64; - type Signature = (); - type SignatureExtra = Extra; -} - -/// Test transaction, tuple of (sender, call, signed_extra) -/// with index only used if sender is some. -/// -/// If sender is some then the transaction is signed otherwise it is unsigned. -#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)] -pub struct TestXt { - /// Signature of the extrinsic. - pub signature: Option>, - /// Call of the extrinsic. - pub call: Call, -} - -impl TestXt { - /// Create a new `TextXt`. - pub fn new(call: Call, signature: Option<(u64, Extra)>) -> Self { - Self { call, signature } - } -} - -impl Serialize for TestXt -where - TestXt: Encode, -{ - fn serialize(&self, seq: S) -> Result - where - S: Serializer, - { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} +/// Wrapper over a `u64` that can be used as a `RuntimeCall`. +#[derive(PartialEq, Eq, Debug, Clone, Encode, Decode, TypeInfo)] +pub struct MockCallU64(pub u64); -impl Debug for TestXt { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TestXt({:?}, ...)", self.signature.as_ref().map(|x| &x.0)) +impl Dispatchable for MockCallU64 { + type RuntimeOrigin = u64; + type Config = (); + type Info = (); + type PostInfo = (); + fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo { + Ok(()) } } -impl Checkable for TestXt { - type Checked = Self; - fn check(self, _: &Context) -> Result { - Ok(self) - } - - #[cfg(feature = "try-runtime")] - fn unchecked_into_checked_i_know_what_i_am_doing( - self, - _: &Context, - ) -> Result { - unreachable!() - } -} - -impl traits::Extrinsic - for TestXt -{ - type Call = Call; - type SignaturePayload = TxSingaturePayload; - - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } - - fn new(c: Call, sig: Option) -> Option { - Some(TestXt { signature: sig, call: c }) - } -} - -impl traits::ExtrinsicMetadata for TestXt -where - Call: Codec + Sync + Send, - Extra: SignedExtension, -{ - type SignedExtensions = Extra; - const VERSION: u8 = 0u8; -} - -impl Applyable for TestXt -where - Call: 'static - + Sized - + Send - + Sync - + Clone - + Eq - + Codec - + Debug - + Dispatchable, - Extra: SignedExtension, - Origin: From>, -{ - type Call = Call; - - /// Checks to see if this is a valid *transaction*. It returns information on it if so. - fn validate>( - &self, - source: TransactionSource, - info: &DispatchInfoOf, - len: usize, - ) -> TransactionValidity { - if let Some((ref id, ref extra)) = self.signature { - Extra::validate(extra, id, &self.call, info, len) - } else { - let valid = Extra::validate_unsigned(&self.call, info, len)?; - let unsigned_validation = U::validate_unsigned(source, &self.call)?; - Ok(valid.combine_with(unsigned_validation)) - } - } - - /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, - /// index and sender. - fn apply>( - self, - info: &DispatchInfoOf, - len: usize, - ) -> ApplyExtrinsicResultWithInfo> { - let maybe_who = if let Some((who, extra)) = self.signature { - Extra::pre_dispatch(extra, &who, &self.call, info, len)?; - Some(who) - } else { - Extra::pre_dispatch_unsigned(&self.call, info, len)?; - U::pre_dispatch(&self.call)?; - None - }; - - Ok(self.call.dispatch(maybe_who.into())) +impl From for MockCallU64 { + fn from(value: u64) -> Self { + Self(value) } } diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits/mod.rs similarity index 94% rename from substrate/primitives/runtime/src/traits.rs rename to substrate/primitives/runtime/src/traits/mod.rs index 4213117334ee7860f0600af2e8033c24b550938b..b217166d8de34e35b102aefd786e8e25e91b1bb8 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits/mod.rs @@ -19,7 +19,7 @@ use crate::{ generic::Digest, - scale_info::{MetaType, StaticTypeInfo, TypeInfo}, + scale_info::{StaticTypeInfo, TypeInfo}, transaction_validity::{ TransactionSource, TransactionValidity, TransactionValidityError, UnknownTransaction, ValidTransaction, @@ -52,6 +52,12 @@ use std::fmt::Display; #[cfg(feature = "std")] use std::str::FromStr; +pub mod transaction_extension; +pub use transaction_extension::{ + DispatchTransaction, TransactionExtension, TransactionExtensionBase, + TransactionExtensionInterior, TransactionExtensionMetadata, ValidateResult, +}; + /// A lazy value. pub trait Lazy { /// Get a reference to the underlying value. @@ -540,6 +546,9 @@ morph_types! { /// Morpher to disregard the source value and replace with another. pub type Replace = |_| -> V::Type { V::get() }; + /// Morpher to disregard the source value and replace with the default of `V`. + pub type ReplaceWithDefault = |_| -> V { Default::default() }; + /// Mutator which reduces a scalar by a particular amount. pub type ReduceBy = |r: N::Type| -> N::Type { r.checked_sub(&N::get()).unwrap_or(Zero::zero()) @@ -1250,7 +1259,7 @@ pub trait Header: // that is then used to define `UncheckedExtrinsic`. // ```ignore // pub type UncheckedExtrinsic = -// generic::UncheckedExtrinsic; +// generic::UncheckedExtrinsic; // ``` // This `UncheckedExtrinsic` is supplied to the `Block`. // ```ignore @@ -1320,19 +1329,31 @@ pub trait Extrinsic: Sized { /// Is this `Extrinsic` signed? /// If no information are available about signed/unsigned, `None` should be returned. + #[deprecated = "Use and implement `!is_bare()` instead"] fn is_signed(&self) -> Option { None } - /// Create new instance of the extrinsic. - /// - /// Extrinsics can be split into: - /// 1. Inherents (no signature; created by validators during block production) - /// 2. Unsigned Transactions (no signature; represent "system calls" or other special kinds of - /// calls) 3. Signed Transactions (with signature; a regular transactions with known origin) + /// Returns `true` if this `Extrinsic` is bare. + fn is_bare(&self) -> bool { + #[allow(deprecated)] + !self + .is_signed() + .expect("`is_signed` must return `Some` on production extrinsics; qed") + } + + /// Create a new old-school extrinsic, either a bare extrinsic if `_signed_data` is `None` or + /// a signed transaction is it is `Some`. + #[deprecated = "Use `new_inherent` or the `CreateTransaction` trait instead"] fn new(_call: Self::Call, _signed_data: Option) -> Option { None } + + /// Create a new inherent extrinsic. + fn new_inherent(function: Self::Call) -> Self { + #[allow(deprecated)] + Self::new(function, None).expect("Extrinsic must provide inherents; qed") + } } /// Something that acts like a [`SignaturePayload`](Extrinsic::SignaturePayload) of an @@ -1368,7 +1389,8 @@ pub trait ExtrinsicMetadata { const VERSION: u8; /// Signed extensions attached to this `Extrinsic`. - type SignedExtensions: SignedExtension; + // TODO: metadata-v16: rename to `Extension`. + type Extra; } /// Extract the hashing type for a block. @@ -1453,6 +1475,8 @@ pub trait Dispatchable { -> crate::DispatchResultWithInfo; } +/// Shortcut to reference the `Origin` type of a `Dispatchable`. +pub type OriginOf = ::RuntimeOrigin; /// Shortcut to reference the `Info` type of a `Dispatchable`. pub type DispatchInfoOf = ::Info; /// Shortcut to reference the `PostInfo` type of a `Dispatchable`. @@ -1471,8 +1495,49 @@ impl Dispatchable for () { } } +/// Dispatchable impl containing an arbitrary value which panics if it actually is dispatched. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct FakeDispatchable(pub Inner); +impl From for FakeDispatchable { + fn from(inner: Inner) -> Self { + Self(inner) + } +} +impl FakeDispatchable { + /// Take `self` and return the underlying inner value. + pub fn deconstruct(self) -> Inner { + self.0 + } +} +impl AsRef for FakeDispatchable { + fn as_ref(&self) -> &Inner { + &self.0 + } +} + +impl Dispatchable for FakeDispatchable { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + fn dispatch( + self, + _origin: Self::RuntimeOrigin, + ) -> crate::DispatchResultWithInfo { + panic!("This implementation should not be used for actual dispatch."); + } +} + +/// Runtime Origin which includes a System Origin variant whose `AccountId` is the parameter. +pub trait AsSystemOriginSigner { + /// Extract a reference of the inner value of the System `Origin::Signed` variant, if self has + /// that variant. + fn as_system_origin_signer(&self) -> Option<&AccountId>; +} + /// Means by which a transaction may be extended. This type embodies both the data and the logic /// that should be additionally associated with the transaction. It should be plain old data. +#[deprecated = "Use `TransactionExtension` instead."] pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo { @@ -1490,7 +1555,7 @@ pub trait SignedExtension: /// Any additional data that will go into the signed payload. This may be created dynamically /// from the transaction using the `additional_signed` function. - type AdditionalSigned: Encode + TypeInfo; + type AdditionalSigned: Codec + TypeInfo; /// The type that encodes information that can be passed from pre_dispatch to post-dispatch. type Pre; @@ -1529,38 +1594,6 @@ pub trait SignedExtension: len: usize, ) -> Result; - /// Validate an unsigned transaction for the transaction queue. - /// - /// This function can be called frequently by the transaction queue - /// to obtain transaction validity against current state. - /// It should perform all checks that determine a valid unsigned transaction, - /// and quickly eliminate ones that are stale or incorrect. - /// - /// Make sure to perform the same checks in `pre_dispatch_unsigned` function. - fn validate_unsigned( - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - Ok(ValidTransaction::default()) - } - - /// Do any pre-flight stuff for a unsigned transaction. - /// - /// Note this function by default delegates to `validate_unsigned`, so that - /// all checks performed for the transaction queue are also performed during - /// the dispatch phase (applying the extrinsic). - /// - /// If you ever override this function, you need to make sure to always - /// perform the same validation as in `validate_unsigned`. - fn pre_dispatch_unsigned( - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result<(), TransactionValidityError> { - Self::validate_unsigned(call, info, len).map(|_| ()).map_err(Into::into) - } - /// Do any post-flight stuff for an extrinsic. /// /// If the transaction is signed, then `_pre` will contain the output of `pre_dispatch`, @@ -1591,126 +1624,47 @@ pub trait SignedExtension: /// /// As a [`SignedExtension`] can be a tuple of [`SignedExtension`]s we need to return a `Vec` /// that holds the metadata of each one. Each individual `SignedExtension` must return - /// *exactly* one [`SignedExtensionMetadata`]. + /// *exactly* one [`TransactionExtensionMetadata`]. /// /// This method provides a default implementation that returns a vec containing a single - /// [`SignedExtensionMetadata`]. - fn metadata() -> Vec { - sp_std::vec![SignedExtensionMetadata { + /// [`TransactionExtensionMetadata`]. + fn metadata() -> Vec { + sp_std::vec![TransactionExtensionMetadata { identifier: Self::IDENTIFIER, ty: scale_info::meta_type::(), additional_signed: scale_info::meta_type::() }] } -} - -/// Information about a [`SignedExtension`] for the runtime metadata. -pub struct SignedExtensionMetadata { - /// The unique identifier of the [`SignedExtension`]. - pub identifier: &'static str, - /// The type of the [`SignedExtension`]. - pub ty: MetaType, - /// The type of the [`SignedExtension`] additional signed data for the payload. - pub additional_signed: MetaType, -} - -#[impl_for_tuples(1, 12)] -impl SignedExtension for Tuple { - for_tuples!( where #( Tuple: SignedExtension )* ); - type AccountId = AccountId; - type Call = Call; - const IDENTIFIER: &'static str = "You should call `identifier()`!"; - for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); - for_tuples!( type Pre = ( #( Tuple::Pre ),* ); ); - - fn additional_signed(&self) -> Result { - Ok(for_tuples!( ( #( Tuple.additional_signed()? ),* ) )) - } - - fn validate( - &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> TransactionValidity { - let valid = ValidTransaction::default(); - for_tuples!( #( let valid = valid.combine_with(Tuple.validate(who, call, info, len)?); )* ); - Ok(valid) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - Ok(for_tuples!( ( #( Tuple.pre_dispatch(who, call, info, len)? ),* ) )) - } + /// Validate an unsigned transaction for the transaction queue. + /// + /// This function can be called frequently by the transaction queue + /// to obtain transaction validity against current state. + /// It should perform all checks that determine a valid unsigned transaction, + /// and quickly eliminate ones that are stale or incorrect. fn validate_unsigned( - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, ) -> TransactionValidity { - let valid = ValidTransaction::default(); - for_tuples!( #( let valid = valid.combine_with(Tuple::validate_unsigned(call, info, len)?); )* ); - Ok(valid) + Ok(ValidTransaction::default()) } + /// Do any pre-flight stuff for an unsigned transaction. + /// + /// Note this function by default delegates to `validate_unsigned`, so that + /// all checks performed for the transaction queue are also performed during + /// the dispatch phase (applying the extrinsic). + /// + /// If you ever override this function, you need not perform the same validation as in + /// `validate_unsigned`. fn pre_dispatch_unsigned( - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, ) -> Result<(), TransactionValidityError> { - for_tuples!( #( Tuple::pre_dispatch_unsigned(call, info, len)?; )* ); Ok(()) } - - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, - len: usize, - result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - match pre { - Some(x) => { - for_tuples!( #( Tuple::post_dispatch(Some(x.Tuple), info, post_info, len, result)?; )* ); - }, - None => { - for_tuples!( #( Tuple::post_dispatch(None, info, post_info, len, result)?; )* ); - }, - } - Ok(()) - } - - fn metadata() -> Vec { - let mut ids = Vec::new(); - for_tuples!( #( ids.extend(Tuple::metadata()); )* ); - ids - } -} - -impl SignedExtension for () { - type AccountId = u64; - type AdditionalSigned = (); - type Call = (); - type Pre = (); - const IDENTIFIER: &'static str = "UnitSignedExtension"; - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } } /// An "executable" piece of information, used by the standard Substrate Executive in order to @@ -1759,6 +1713,7 @@ pub trait GetNodeBlockType { /// function is called right before dispatching the call wrapped by an unsigned extrinsic. The /// [`validate_unsigned`](Self::validate_unsigned) function is mainly being used in the context of /// the transaction pool to check the validity of the call wrapped by an unsigned extrinsic. +// TODO: Rename to ValidateBareTransaction (or just remove). pub trait ValidateUnsigned { /// The call to validate type Call; diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs b/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs new file mode 100644 index 0000000000000000000000000000000000000000..a834e88c0b4e6dae831b5eec3f138f9aae3764a8 --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs @@ -0,0 +1,133 @@ +// 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. + +//! The [AsTransactionExtension] adapter struct for adapting [SignedExtension]s to +//! [TransactionExtension]s. + +#![allow(deprecated)] + +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +use crate::{ + traits::{AsSystemOriginSigner, SignedExtension, ValidateResult}, + InvalidTransaction, +}; + +use super::*; + +/// Adapter to use a `SignedExtension` in the place of a `TransactionExtension`. +#[derive(TypeInfo, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +#[deprecated = "Convert your SignedExtension to a TransactionExtension."] +pub struct AsTransactionExtension(pub SE); + +impl Default for AsTransactionExtension { + fn default() -> Self { + Self(SE::default()) + } +} + +impl From for AsTransactionExtension { + fn from(value: SE) -> Self { + Self(value) + } +} + +impl TransactionExtensionBase for AsTransactionExtension { + const IDENTIFIER: &'static str = SE::IDENTIFIER; + type Implicit = SE::AdditionalSigned; + + fn implicit(&self) -> Result { + self.0.additional_signed() + } + fn metadata() -> Vec { + SE::metadata() + } +} + +impl TransactionExtension + for AsTransactionExtension +where + ::RuntimeOrigin: AsSystemOriginSigner + Clone, +{ + type Val = (); + type Pre = SE::Pre; + + fn validate( + &self, + origin: ::RuntimeOrigin, + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + let r = self.0.validate(who, call, info, len)?; + Ok((r, (), origin)) + } + + fn prepare( + self, + _: (), + origin: &::RuntimeOrigin, + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + _context: &Context, + ) -> Result { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + self.0.pre_dispatch(who, call, info, len) + } + + fn post_dispatch( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + _context: &Context, + ) -> Result<(), TransactionValidityError> { + SE::post_dispatch(Some(pre), info, post_info, len, result) + } + + fn validate_bare_compat( + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> TransactionValidity { + SE::validate_unsigned(call, info, len) + } + + fn pre_dispatch_bare_compat( + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(), TransactionValidityError> { + SE::pre_dispatch_unsigned(call, info, len) + } + + fn post_dispatch_bare_compat( + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + SE::post_dispatch(None, info, post_info, len, result) + } +} diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs b/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..8efa586aa0e77f60b5671bb868438d7aa7bdaf3d --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs @@ -0,0 +1,141 @@ +// 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. + +//! The [DispatchTransaction] trait. + +use super::*; + +/// Single-function utility trait with a blanket impl over [TransactionExtension] in order to +/// provide transaction dispatching functionality. We avoid implementing this directly on the trait +/// since we never want it to be overriden by the trait implementation. +pub trait DispatchTransaction { + /// The origin type of the transaction. + type Origin; + /// The info type. + type Info; + /// The resultant type. + type Result; + /// The `Val` of the extension. + type Val; + /// The `Pre` of the extension. + type Pre; + /// Just validate a transaction. + /// + /// The is basically the same as [validate](TransactionExtension::validate), except that there + /// is no need to supply the bond data. + fn validate_only( + &self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + ) -> Result<(ValidTransaction, Self::Val, Self::Origin), TransactionValidityError>; + /// Prepare and validate a transaction, ready for dispatch. + fn validate_and_prepare( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + ) -> Result<(Self::Pre, Self::Origin), TransactionValidityError>; + /// Dispatch a transaction with the given base origin and call. + fn dispatch_transaction( + self, + origin: Self::Origin, + call: Call, + info: &Self::Info, + len: usize, + ) -> Self::Result; + /// Do everything which would be done in a [dispatch_transaction](Self::dispatch_transaction), + /// but instead of executing the call, execute `substitute` instead. Since this doesn't actually + /// dispatch the call, it doesn't need to consume it and so `call` can be passed as a reference. + fn test_run( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + substitute: impl FnOnce( + Self::Origin, + ) -> crate::DispatchResultWithInfo<::PostInfo>, + ) -> Self::Result; +} + +impl, Call: Dispatchable + Encode> DispatchTransaction + for T +{ + type Origin = ::RuntimeOrigin; + type Info = DispatchInfoOf; + type Result = crate::ApplyExtrinsicResultWithInfo>; + type Val = T::Val; + type Pre = T::Pre; + + fn validate_only( + &self, + origin: Self::Origin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(ValidTransaction, T::Val, Self::Origin), TransactionValidityError> { + self.validate(origin, call, info, len, &mut (), self.implicit()?, call) + } + fn validate_and_prepare( + self, + origin: Self::Origin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(T::Pre, Self::Origin), TransactionValidityError> { + let (_, val, origin) = self.validate_only(origin, call, info, len)?; + let pre = self.prepare(val, &origin, &call, info, len, &())?; + Ok((pre, origin)) + } + fn dispatch_transaction( + self, + origin: ::RuntimeOrigin, + call: Call, + info: &DispatchInfoOf, + len: usize, + ) -> Self::Result { + let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?; + let res = call.dispatch(origin); + let post_info = res.unwrap_or_else(|err| err.post_info); + let pd_res = res.map(|_| ()).map_err(|e| e.error); + T::post_dispatch(pre, info, &post_info, len, &pd_res, &())?; + Ok(res) + } + fn test_run( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + substitute: impl FnOnce( + Self::Origin, + ) -> crate::DispatchResultWithInfo<::PostInfo>, + ) -> Self::Result { + let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?; + let res = substitute(origin); + let post_info = match res { + Ok(info) => info, + Err(err) => err.post_info, + }; + let pd_res = res.map(|_| ()).map_err(|e| e.error); + T::post_dispatch(pre, info, &post_info, len, &pd_res, &())?; + Ok(res) + } +} diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs b/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..69a0ba18adb72a67951d8548724182060916e0ee --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs @@ -0,0 +1,526 @@ +// 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. + +//! The transaction extension trait. + +use crate::{ + scale_info::{MetaType, StaticTypeInfo}, + transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, + DispatchResult, +}; +use codec::{Codec, Decode, Encode}; +use impl_trait_for_tuples::impl_for_tuples; +#[doc(hidden)] +pub use sp_std::marker::PhantomData; +use sp_std::{self, fmt::Debug, prelude::*}; +use sp_weights::Weight; +use tuplex::{PopFront, PushBack}; + +use super::{DispatchInfoOf, Dispatchable, OriginOf, PostDispatchInfoOf}; + +mod as_transaction_extension; +mod dispatch_transaction; +#[allow(deprecated)] +pub use as_transaction_extension::AsTransactionExtension; +pub use dispatch_transaction::DispatchTransaction; + +/// Shortcut for the result value of the `validate` function. +pub type ValidateResult = + Result<(ValidTransaction, Val, OriginOf), TransactionValidityError>; + +/// Simple blanket implementation trait to denote the bounds of a type which can be contained within +/// a [`TransactionExtension`]. +pub trait TransactionExtensionInterior: + Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo +{ +} +impl + TransactionExtensionInterior for T +{ +} + +/// Base for [TransactionExtension]s; this contains the associated types and does not require any +/// generic parameterization. +pub trait TransactionExtensionBase: TransactionExtensionInterior { + /// Unique identifier of this signed extension. + /// + /// This will be exposed in the metadata to identify the signed extension used in an extrinsic. + const IDENTIFIER: &'static str; + + /// Any additional data which was known at the time of transaction construction and can be + /// useful in authenticating the transaction. This is determined dynamically in part from the + /// on-chain environment using the `implicit` function and not directly contained in the + /// transaction itself and therefore is considered "implicit". + type Implicit: Codec + StaticTypeInfo; + + /// Determine any additional data which was known at the time of transaction construction and + /// can be useful in authenticating the transaction. The expected usage of this is to include in + /// any data which is signed and verified as part of transactiob validation. Also perform any + /// pre-signature-verification checks and return an error if needed. + fn implicit(&self) -> Result { + use crate::InvalidTransaction::IndeterminateImplicit; + Ok(Self::Implicit::decode(&mut &[][..]).map_err(|_| IndeterminateImplicit)?) + } + + /// The weight consumed by executing this extension instance fully during transaction dispatch. + fn weight(&self) -> Weight { + Weight::zero() + } + + /// Returns the metadata for this extension. + /// + /// As a [`TransactionExtension`] can be a tuple of [`TransactionExtension`]s we need to return + /// a `Vec` that holds the metadata of each one. Each individual `TransactionExtension` must + /// return *exactly* one [`TransactionExtensionMetadata`]. + /// + /// This method provides a default implementation that returns a vec containing a single + /// [`TransactionExtensionMetadata`]. + fn metadata() -> Vec { + sp_std::vec![TransactionExtensionMetadata { + identifier: Self::IDENTIFIER, + ty: scale_info::meta_type::(), + // TODO: Metadata-v16: Rename to "implicit" + additional_signed: scale_info::meta_type::() + }] + } +} + +/// Means by which a transaction may be extended. This type embodies both the data and the logic +/// that should be additionally associated with the transaction. It should be plain old data. +/// +/// The simplest transaction extension would be the Unit type (and empty pipeline) `()`. This +/// executes no additional logic and implies a dispatch of the transaction's call using the +/// inherited origin (either `None` or `Signed`, depending on whether this is a signed or general +/// transaction). +/// +/// Transaction extensions are capable of altering certain associated semantics: +/// +/// - They may define the origin with which the transaction's call should be dispatched. +/// - They may define various parameters used by the transction queue to determine under what +/// conditions the transaction should be retained and introduced on-chain. +/// - They may define whether this transaction is acceptable for introduction on-chain at all. +/// +/// Each of these semantics are defined by the `validate` function. +/// +/// **NOTE: Transaction extensions cannot under any circumctances alter the call itself.** +/// +/// Transaction extensions are capable of defining logic which is executed additionally to the +/// dispatch of the call: +/// +/// - They may define logic which must be executed prior to the dispatch of the call. +/// - They may also define logic which must be executed after the dispatch of the call. +/// +/// Each of these semantics are defined by the `prepare` and `post_dispatch` functions respectively. +/// +/// Finally, transaction extensions may define additional data to help define the implications of +/// the logic they introduce. This additional data may be explicitly defined by the transaction +/// author (in which case it is included as part of the transaction body), or it may be implicitly +/// defined by the transaction extension based around the on-chain state (which the transaction +/// author is assumed to know). This data may be utilized by the above logic to alter how a node's +/// transaction queue treats this transaction. +/// +/// ## Default implementations +/// +/// Of the 5 functions in this trait, 3 of them must return a value of an associated type on +/// success, and none of these types implement [Default] or anything like it. This means that +/// default implementations cannot be provided for these functions. However, a macro is provided +/// [impl_tx_ext_default](crate::impl_tx_ext_default) which is capable of generating default +/// implementations for each of these 3 functions. If you do not wish to introduce additional logic +/// into the transaction pipeline, then it is recommended that you use this macro to implement these +/// functions. +/// +/// ## Pipelines, Inherited Implications, and Authorized Origins +/// +/// Requiring a single transaction extension to define all of the above semantics would be +/// cumbersome and would lead to a lot of boilerplate. Instead, transaction extensions are +/// aggregated into pipelines, which are tuples of transaction extensions. Each extension in the +/// pipeline is executed in order, and the output of each extension is aggregated and/or relayed as +/// the input to the next extension in the pipeline. +/// +/// This ordered composition happens with all datatypes ([Val](TransactionExtension::Val), +/// [Pre](TransactionExtension::Pre) and [Implicit](TransactionExtensionBase::Implicit)) as well as +/// all functions. There are important consequences stemming from how the composition affects the +/// meaning of the `origin` and `implication` parameters as well as the results. Whereas the +/// [prepare](TransactionExtension::prepare) and +/// [post_dispatch](TransactionExtension::post_dispatch) functions are clear in their meaning, the +/// [validate](TransactionExtension::validate) function is fairly sophisticated and warrants +/// further explanation. +/// +/// Firstly, the `origin` parameter. The `origin` passed into the first item in a pipeline is simply +/// that passed into the tuple itself. It represents an authority who has authorized the implication +/// of the transaction, as of the extension it has been passed into *and any further extensions it +/// may pass though, all the way to, and including, the transaction's dispatch call itself. Each +/// following item in the pipeline is passed the origin which the previous item returned. The origin +/// returned from the final item in the pipeline is the origin which is returned by the tuple +/// itself. +/// +/// This means that if a constituent extension returns a different origin to the one it was called +/// with, then (assuming no other extension changes it further) *this new origin will be used for +/// all extensions following it in the pipeline, and will be returned from the pipeline to be used +/// as the origin for the call's dispatch*. The call itself as well as all these extensions +/// following may each imply consequence for this origin. We call this the *inherited implication*. +/// +/// The *inherited implication* is the cumulated on-chain effects born by whatever origin is +/// returned. It is expressed to the [validate](TransactionExtension::validate) function only as the +/// `implication` argument which implements the [Encode] trait. A transaction extension may define +/// its own implications through its own fields and the +/// [implicit](TransactionExtensionBase::implicit) function. This is only utilized by extensions +/// which preceed it in a pipeline or, if the transaction is an old-school signed trasnaction, the +/// underlying transaction verification logic. +/// +/// **The inherited implication passed as the `implication` parameter to +/// [validate](TransactionExtension::validate) does not include the extension's inner data itself +/// nor does it include the result of the extension's `implicit` function.** If you both provide an +/// implication and rely on the implication, then you need to manually aggregate your extensions +/// implication with the aggregated implication passed in. +pub trait TransactionExtension: TransactionExtensionBase { + /// The type that encodes information that can be passed from validate to prepare. + type Val; + + /// The type that encodes information that can be passed from prepare to post-dispatch. + type Pre; + + /// Validate a transaction for the transaction queue. + /// + /// This function can be called frequently by the transaction queue to obtain transaction + /// validity against current state. It should perform all checks that determine a valid + /// transaction, that can pay for its execution and quickly eliminate ones that are stale or + /// incorrect. + /// + /// Parameters: + /// - `origin`: The origin of the transaction which this extension inherited; coming from an + /// "old-school" *signed transaction*, this will be a system `RawOrigin::Signed` value. If the + /// transaction is a "new-school" *General Transaction*, then this will be a system + /// `RawOrigin::None` value. If this extension is an item in a composite, then it could be + /// anything which was previously returned as an `origin` value in the result of a `validate` + /// call. + /// - `call`: The `Call` wrapped by this extension. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// - `inherited_implication`: The *implication* which this extension inherits. This is a tuple + /// of the transaction's call and some additional opaque-but-encodable data. Coming directly + /// from a transaction, the latter is [()]. However, if this extension is expressed as part of + /// a composite type, then the latter component is equal to any further implications to which + /// the returned `origin` could potentially apply. See Pipelines, Inherited Implications, and + /// Authorized Origins for more information. + /// - `context`: Some opaque mutable context, as yet unused. + /// + /// Returns a [ValidateResult], which is a [Result] whose success type is a tuple of + /// [ValidTransaction] (defining useful metadata for the transaction queue), the [Self::Val] + /// token of this transaction, which gets passed into [prepare](TransactionExtension::prepare), + /// and the origin of the transaction, which gets passed into + /// [prepare](TransactionExtension::prepare) and is ultimately used for dispatch. + fn validate( + &self, + origin: OriginOf, + call: &Call, + info: &DispatchInfoOf, + len: usize, + context: &mut Context, + self_implicit: Self::Implicit, + inherited_implication: &impl Encode, + ) -> ValidateResult; + + /// Do any pre-flight stuff for a transaction after validation. + /// + /// This is for actions which do not happen in the transaction queue but only immediately prior + /// to the point of dispatch on-chain. This should not return an error, since errors should + /// already have been identified during the [validate](TransactionExtension::validate) call. If + /// an error is returned, the transaction will be considered invalid but no state changes will + /// happen and therefore work done in [validate](TransactionExtension::validate) will not be + /// paid for. + /// + /// Unlike `validate`, this function may consume `self`. + /// + /// Parameters: + /// - `val`: `Self::Val` returned by the result of the `validate` call. + /// - `origin`: The origin returned by the result of the `validate` call. + /// - `call`: The `Call` wrapped by this extension. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// - `context`: Some opaque mutable context, as yet unused. + /// + /// Returns a [Self::Pre] value on success, which gets passed into + /// [post_dispatch](TransactionExtension::post_dispatch) and after the call is dispatched. + /// + /// IMPORTANT: **Checks made in validation need not be repeated here.** + fn prepare( + self, + val: Self::Val, + origin: &OriginOf, + call: &Call, + info: &DispatchInfoOf, + len: usize, + context: &Context, + ) -> Result; + + /// Do any post-flight stuff for an extrinsic. + /// + /// `_pre` contains the output of `prepare`. + /// + /// This gets given the `DispatchResult` `_result` from the extrinsic and can, if desired, + /// introduce a `TransactionValidityError`, causing the block to become invalid for including + /// it. + /// + /// Parameters: + /// - `pre`: `Self::Pre` returned by the result of the `prepare` call prior to dispatch. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `post_info`: Information concerning the dispatch of the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// - `result`: The result of the dispatch. + /// - `context`: Some opaque mutable context, as yet unused. + /// + /// WARNING: It is dangerous to return an error here. To do so will fundamentally invalidate the + /// transaction and any block that it is included in, causing the block author to not be + /// compensated for their work in validating the transaction or producing the block so far. It + /// can only be used safely when you *know* that the transaction is one that would only be + /// introduced by the current block author. + fn post_dispatch( + _pre: Self::Pre, + _info: &DispatchInfoOf, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + _context: &Context, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } + + /// Compatibility function for supporting the `SignedExtension::validate_unsigned` function. + /// + /// DO NOT USE! THIS MAY BE REMOVED AT ANY TIME! + #[deprecated = "Only for compatibility. DO NOT USE."] + fn validate_bare_compat( + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + Ok(ValidTransaction::default()) + } + + /// Compatibility function for supporting the `SignedExtension::pre_dispatch_unsigned` function. + /// + /// DO NOT USE! THIS MAY BE REMOVED AT ANY TIME! + #[deprecated = "Only for compatibility. DO NOT USE."] + fn pre_dispatch_bare_compat( + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } + + /// Compatibility function for supporting the `SignedExtension::post_dispatch` function where + /// `pre` is `None`. + /// + /// DO NOT USE! THIS MAY BE REMOVED AT ANY TIME! + #[deprecated = "Only for compatibility. DO NOT USE."] + fn post_dispatch_bare_compat( + _info: &DispatchInfoOf, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } +} + +/// Implict +#[macro_export] +macro_rules! impl_tx_ext_default { + ($call:ty ; $context:ty ; , $( $rest:tt )*) => { + impl_tx_ext_default!{$call ; $context ; $( $rest )*} + }; + ($call:ty ; $context:ty ; validate $( $rest:tt )*) => { + fn validate( + &self, + origin: $crate::traits::OriginOf<$call>, + _call: &$call, + _info: &$crate::traits::DispatchInfoOf<$call>, + _len: usize, + _context: &mut $context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl $crate::codec::Encode, + ) -> $crate::traits::ValidateResult { + Ok((Default::default(), Default::default(), origin)) + } + impl_tx_ext_default!{$call ; $context ; $( $rest )*} + }; + ($call:ty ; $context:ty ; prepare $( $rest:tt )*) => { + fn prepare( + self, + _val: Self::Val, + _origin: &$crate::traits::OriginOf<$call>, + _call: &$call, + _info: &$crate::traits::DispatchInfoOf<$call>, + _len: usize, + _context: & $context, + ) -> Result { + Ok(Default::default()) + } + impl_tx_ext_default!{$call ; $context ; $( $rest )*} + }; + ($call:ty ; $context:ty ;) => {}; +} + +/// Information about a [`TransactionExtension`] for the runtime metadata. +pub struct TransactionExtensionMetadata { + /// The unique identifier of the [`TransactionExtension`]. + pub identifier: &'static str, + /// The type of the [`TransactionExtension`]. + pub ty: MetaType, + /// The type of the [`TransactionExtension`] additional signed data for the payload. + // TODO: Rename "implicit" + pub additional_signed: MetaType, +} + +#[impl_for_tuples(1, 12)] +impl TransactionExtensionBase for Tuple { + for_tuples!( where #( Tuple: TransactionExtensionBase )* ); + const IDENTIFIER: &'static str = "Use `metadata()`!"; + for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); ); + fn implicit(&self) -> Result { + Ok(for_tuples!( ( #( Tuple.implicit()? ),* ) )) + } + fn weight(&self) -> Weight { + let mut weight = Weight::zero(); + for_tuples!( #( weight += Tuple.weight(); )* ); + weight + } + fn metadata() -> Vec { + let mut ids = Vec::new(); + for_tuples!( #( ids.extend(Tuple::metadata()); )* ); + ids + } +} + +#[impl_for_tuples(1, 12)] +impl TransactionExtension for Tuple { + for_tuples!( where #( Tuple: TransactionExtension )* ); + for_tuples!( type Val = ( #( Tuple::Val ),* ); ); + for_tuples!( type Pre = ( #( Tuple::Pre ),* ); ); + + fn validate( + &self, + origin: ::RuntimeOrigin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + context: &mut Context, + self_implicit: Self::Implicit, + inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let valid = ValidTransaction::default(); + let val = (); + let following_explicit_implications = for_tuples!( ( #( &self.Tuple ),* ) ); + let following_implicit_implications = self_implicit; + + for_tuples!(#( + // Implication of this pipeline element not relevant for later items, so we pop it. + let (_item, following_explicit_implications) = following_explicit_implications.pop_front(); + let (item_implicit, following_implicit_implications) = following_implicit_implications.pop_front(); + let (item_valid, item_val, origin) = { + let implications = ( + // The first is the implications born of the fact we return the mutated + // origin. + inherited_implication, + // This is the explicitly made implication born of the fact the new origin is + // passed into the next items in this pipeline-tuple. + &following_explicit_implications, + // This is the implicitly made implication born of the fact the new origin is + // passed into the next items in this pipeline-tuple. + &following_implicit_implications, + ); + Tuple.validate(origin, call, info, len, context, item_implicit, &implications)? + }; + let valid = valid.combine_with(item_valid); + let val = val.push_back(item_val); + )* ); + Ok((valid, val, origin)) + } + + fn prepare( + self, + val: Self::Val, + origin: &::RuntimeOrigin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + context: &Context, + ) -> Result { + Ok(for_tuples!( ( #( + Tuple::prepare(self.Tuple, val.Tuple, origin, call, info, len, context)? + ),* ) )) + } + + fn post_dispatch( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + context: &Context, + ) -> Result<(), TransactionValidityError> { + for_tuples!( #( Tuple::post_dispatch(pre.Tuple, info, post_info, len, result, context)?; )* ); + Ok(()) + } +} + +impl TransactionExtensionBase for () { + const IDENTIFIER: &'static str = "UnitTransactionExtension"; + type Implicit = (); + fn implicit(&self) -> sp_std::result::Result { + Ok(()) + } + fn weight(&self) -> Weight { + Weight::zero() + } +} + +impl TransactionExtension for () { + type Val = (); + type Pre = (); + fn validate( + &self, + origin: ::RuntimeOrigin, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, (), ::RuntimeOrigin), + TransactionValidityError, + > { + Ok((ValidTransaction::default(), (), origin)) + } + fn prepare( + self, + _val: (), + _origin: &::RuntimeOrigin, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + _context: &Context, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } +} diff --git a/substrate/primitives/runtime/src/transaction_validity.rs b/substrate/primitives/runtime/src/transaction_validity.rs index 836948493823cb4d137ce90721788e9a78a8ac99..24124128083c0e48ef0b7cd6cf590ecf1c6bb7cb 100644 --- a/substrate/primitives/runtime/src/transaction_validity.rs +++ b/substrate/primitives/runtime/src/transaction_validity.rs @@ -82,6 +82,8 @@ pub enum InvalidTransaction { MandatoryValidation, /// The sending address is disabled or known to be invalid. BadSigner, + /// The implicit data was unable to be calculated. + IndeterminateImplicit, } impl InvalidTransaction { @@ -113,6 +115,8 @@ impl From for &'static str { "Transaction dispatch is mandatory; transactions must not be validated.", InvalidTransaction::Custom(_) => "InvalidTransaction custom error", InvalidTransaction::BadSigner => "Invalid signing address", + InvalidTransaction::IndeterminateImplicit => + "The implicit data was unable to be calculated", } } } @@ -338,7 +342,7 @@ pub struct ValidTransactionBuilder { impl ValidTransactionBuilder { /// Set the priority of a transaction. /// - /// Note that the final priority for `FRAME` is combined from all `SignedExtension`s. + /// Note that the final priority for `FRAME` is combined from all `TransactionExtension`s. /// Most likely for unsigned transactions you want the priority to be higher /// than for regular transactions. We recommend exposing a base priority for unsigned /// transactions as a runtime module parameter, so that the runtime can tune inter-module diff --git a/substrate/primitives/session/Cargo.toml b/substrate/primitives/session/Cargo.toml index 25700210feef2c0078992ace19597b72291625cc..99764c0a17f92b4772ee64a134633403835b1348 100644 --- a/substrate/primitives/session/Cargo.toml +++ b/substrate/primitives/session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-session" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/staking/Cargo.toml b/substrate/primitives/staking/Cargo.toml index 5ffe6fbeaf5da54cb362caac1ed6cdfc27049fe4..21346fbaca5383554d1285ceeaba43815b81857a 100644 --- a/substrate/primitives/staking/Cargo.toml +++ b/substrate/primitives/staking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-staking" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"], optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index c2ac5ae004b1b7621341020e614a9c769e27d2dd..f5b4a1ed63fb3da2e9d69f51f8bc10355dbd0730 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -150,6 +150,9 @@ pub trait OnStakingUpdate { _slashed_total: Balance, ) { } + + /// Fired when a portion of a staker's balance has been withdrawn. + fn on_withdraw(_stash: &AccountId, _amount: Balance) {} } /// A generic representation of a staking implementation. diff --git a/substrate/primitives/state-machine/Cargo.toml b/substrate/primitives/state-machine/Cargo.toml index f891a74dbf4d49459a55b198667f141bc256dc5d..09994f1ae91e1c9a7dba2faf169870f08a640b22 100644 --- a/substrate/primitives/state-machine/Cargo.toml +++ b/substrate/primitives/state-machine/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-state-machine" -version = "0.28.0" +version = "0.35.0" authors.workspace = true description = "Substrate State Machine" edition.workspace = true @@ -19,11 +19,11 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } hash-db = { version = "0.16.0", default-features = false } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } parking_lot = { version = "0.12.1", optional = true } rand = { version = "0.8.5", optional = true } smallvec = "1.11.0" -thiserror = { version = "1.0.48", optional = true } +thiserror = { optional = true, workspace = true } tracing = { version = "0.1.29", optional = true } sp-core = { path = "../core", default-features = false } sp-externalities = { path = "../externalities", default-features = false } diff --git a/substrate/primitives/state-machine/src/error.rs b/substrate/primitives/state-machine/src/error.rs index 4e8e02a26025ca9ef0f6899d3870e9e96c96da22..cf8ca4425c119e8960f33def2e6b2eaa35afbd44 100644 --- a/substrate/primitives/state-machine/src/error.rs +++ b/substrate/primitives/state-machine/src/error.rs @@ -16,7 +16,7 @@ // limitations under the License. /// State Machine Errors -use sp_std::fmt; +use core::fmt; /// State Machine Error bound. /// diff --git a/substrate/primitives/state-machine/src/in_memory_backend.rs b/substrate/primitives/state-machine/src/in_memory_backend.rs index ce551cec2a473ceacbc1ab99984af2e5ed4b64f4..06fe6d4162a7f4c920c8f09c2e4e8b05a3c72c03 100644 --- a/substrate/primitives/state-machine/src/in_memory_backend.rs +++ b/substrate/primitives/state-machine/src/in_memory_backend.rs @@ -79,7 +79,7 @@ where /// Apply the given transaction to this backend and set the root to the given value. pub fn apply_transaction(&mut self, root: H::Out, transaction: PrefixedMemoryDB) { - let mut storage = sp_std::mem::take(self).into_storage(); + let mut storage = core::mem::take(self).into_storage(); storage.consolidate(transaction); *self = TrieBackendBuilder::new(storage, root).build(); diff --git a/substrate/primitives/state-machine/src/stats.rs b/substrate/primitives/state-machine/src/stats.rs index 7c5510961a373268ca44693489bed842209c1e03..84ab7725660da607442b55c4015be656dcd4fa55 100644 --- a/substrate/primitives/state-machine/src/stats.rs +++ b/substrate/primitives/state-machine/src/stats.rs @@ -17,7 +17,7 @@ //! Usage statistics for state db -use sp_std::cell::RefCell; +use core::cell::RefCell; #[cfg(feature = "std")] use std::time::{Duration, Instant}; diff --git a/substrate/primitives/statement-store/Cargo.toml b/substrate/primitives/statement-store/Cargo.toml index cacfd08f3ebf4802ee3f2f64cfb6d6b8eee2f454..652ab3ef13aa695f13c9b9c0a525b3f90dff8f0c 100644 --- a/substrate/primitives/statement-store/Cargo.toml +++ b/substrate/primitives/statement-store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-statement-store" -version = "4.0.0-dev" +version = "10.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,13 +19,14 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } sp-core = { path = "../core", default-features = false } +sp-crypto-hashing = { path = "../crypto/hashing", default-features = false } sp-runtime = { path = "../runtime", default-features = false } sp-std = { path = "../std", default-features = false } sp-api = { path = "../api", default-features = false } sp-application-crypto = { path = "../application-crypto", default-features = false } sp-runtime-interface = { path = "../runtime-interface", default-features = false } sp-externalities = { path = "../externalities", default-features = false } -thiserror = { version = "1.0", optional = true } +thiserror = { optional = true, workspace = true } # ECIES dependencies ed25519-dalek = { version = "2.1", optional = true } @@ -52,6 +53,7 @@ std = [ "sp-api/std", "sp-application-crypto/std", "sp-core/std", + "sp-crypto-hashing/std", "sp-externalities/std", "sp-runtime-interface/std", "sp-runtime/std", diff --git a/substrate/primitives/statement-store/src/lib.rs b/substrate/primitives/statement-store/src/lib.rs index 67e7a7b3896b5df81c27539c52135669778882ae..04175f6d6160e50985673338c7b187539570bdfa 100644 --- a/substrate/primitives/statement-store/src/lib.rs +++ b/substrate/primitives/statement-store/src/lib.rs @@ -87,7 +87,7 @@ mod ecdsa { /// Returns blake2-256 hash for the encoded statement. #[cfg(feature = "std")] pub fn hash_encoded(data: &[u8]) -> [u8; 32] { - sp_core::hashing::blake2_256(data) + sp_crypto_hashing::blake2_256(data) } /// Statement proof. @@ -632,7 +632,7 @@ mod test { statement.sign_ecdsa_private(&secp256k1_kp); assert_eq!( statement.verify_signature(), - SignatureVerificationResult::Valid(sp_core::hashing::blake2_256( + SignatureVerificationResult::Valid(sp_crypto_hashing::blake2_256( &secp256k1_kp.public().0 )) ); diff --git a/substrate/primitives/std/Cargo.toml b/substrate/primitives/std/Cargo.toml index f349a7b119688fab1a651f4ea709b76274af2796..0b5276469efdd1954bbaf85f3e2843ea00a736a2 100644 --- a/substrate/primitives/std/Cargo.toml +++ b/substrate/primitives/std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-std" -version = "8.0.0" +version = "14.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/storage/Cargo.toml b/substrate/primitives/storage/Cargo.toml index 32f59b04a12ada5cb2a1dcf853070280796fe988..d3ade87ea47f848e8036ce0a4d374fa4d77184b2 100644 --- a/substrate/primitives/storage/Cargo.toml +++ b/substrate/primitives/storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-storage" -version = "13.0.0" +version = "19.0.0" authors.workspace = true edition.workspace = true description = "Storage related primitives" @@ -20,7 +20,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } impl-serde = { version = "0.4.0", optional = true, default-features = false } ref-cast = "1.0.0" -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"], optional = true } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } sp-debug-derive = { path = "../debug-derive", default-features = false } sp-std = { path = "../std", default-features = false } diff --git a/substrate/primitives/storage/src/lib.rs b/substrate/primitives/storage/src/lib.rs index 3528d0558f530b2d37711a564974cd5ab63343c9..79c090cabf8dbd686f6b92e3770c1944ed1feb05 100644 --- a/substrate/primitives/storage/src/lib.rs +++ b/substrate/primitives/storage/src/lib.rs @@ -178,7 +178,7 @@ pub struct Storage { /// Storage change set #[derive(RuntimeDebug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, PartialEq, Eq))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, PartialEq, Eq, Clone))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct StorageChangeSet { /// Block hash diff --git a/substrate/primitives/test-primitives/Cargo.toml b/substrate/primitives/test-primitives/Cargo.toml index 3649217cf74e136f88860d0c69466d3de9ef5a06..f310216dd58d737be7cde512d3fbdf27f4fbda25 100644 --- a/substrate/primitives/test-primitives/Cargo.toml +++ b/substrate/primitives/test-primitives/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["derive"], optional = true } +serde = { features = ["derive"], optional = true, workspace = true } sp-application-crypto = { path = "../application-crypto", default-features = false } sp-core = { path = "../core", default-features = false } sp-runtime = { path = "../runtime", default-features = false } diff --git a/substrate/primitives/timestamp/Cargo.toml b/substrate/primitives/timestamp/Cargo.toml index b61f36f2056b8a838e9d5c86d89614f834968f45..9e2b802bfb15e2e89169923d2e6f4274d2cdf1f2 100644 --- a/substrate/primitives/timestamp/Cargo.toml +++ b/substrate/primitives/timestamp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-timestamp" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.74", optional = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.48", optional = true } +thiserror = { optional = true, workspace = true } sp-inherents = { path = "../inherents", default-features = false } sp-runtime = { path = "../runtime", default-features = false } sp-std = { path = "../std", default-features = false } diff --git a/substrate/primitives/timestamp/src/lib.rs b/substrate/primitives/timestamp/src/lib.rs index d1bd2a3446e6b65ea2bf7a35942cae5f10f599be..a91ee16afe8e33a2ad2faa719db8c3a168540589 100644 --- a/substrate/primitives/timestamp/src/lib.rs +++ b/substrate/primitives/timestamp/src/lib.rs @@ -20,8 +20,8 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; +use core::time::Duration; use sp_inherents::{InherentData, InherentIdentifier, IsFatalError}; -use sp_std::time::Duration; /// The identifier for the `timestamp` inherent. pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"timstap0"; @@ -69,7 +69,7 @@ impl Timestamp { } } -impl sp_std::ops::Deref for Timestamp { +impl core::ops::Deref for Timestamp { type Target = u64; fn deref(&self) -> &Self::Target { @@ -219,7 +219,7 @@ impl InherentDataProvider { } #[cfg(feature = "std")] -impl sp_std::ops::Deref for InherentDataProvider { +impl core::ops::Deref for InherentDataProvider { type Target = InherentType; fn deref(&self) -> &Self::Target { diff --git a/substrate/primitives/tracing/Cargo.toml b/substrate/primitives/tracing/Cargo.toml index 0ad3cd0705b3423bdced8be291b45e13f25b672e..58b1e48c4622c3b2ddd97072a7e2c3524110e613 100644 --- a/substrate/primitives/tracing/Cargo.toml +++ b/substrate/primitives/tracing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-tracing" -version = "10.0.0" +version = "16.0.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true diff --git a/substrate/primitives/transaction-pool/Cargo.toml b/substrate/primitives/transaction-pool/Cargo.toml index 6e66910ac388576d761d0215b570f1c62f970e95..a7deda64efce46f57e84826fbef8ce76598ab73b 100644 --- a/substrate/primitives/transaction-pool/Cargo.toml +++ b/substrate/primitives/transaction-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-transaction-pool" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/primitives/transaction-storage-proof/Cargo.toml b/substrate/primitives/transaction-storage-proof/Cargo.toml index e1c50a09c59d28c40fa5c97343cc38621cfe99db..e2fb54dafdf1c97f5ebc0bb8662a213803f0901e 100644 --- a/substrate/primitives/transaction-storage-proof/Cargo.toml +++ b/substrate/primitives/transaction-storage-proof/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-transaction-storage-proof" -version = "4.0.0-dev" +version = "26.0.0" authors.workspace = true description = "Transaction storage proof primitives" edition.workspace = true diff --git a/substrate/primitives/trie/Cargo.toml b/substrate/primitives/trie/Cargo.toml index 79ed5c2000094b422e252b99e2f15505d1b8a3f8..16d3ca19a179ce97bbc2c5c42023ceb54bf8b32b 100644 --- a/substrate/primitives/trie/Cargo.toml +++ b/substrate/primitives/trie/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-trie" -version = "22.0.0" +version = "29.0.0" authors.workspace = true description = "Patricia trie stuff using a parity-scale-codec node format" repository.workspace = true @@ -30,7 +30,7 @@ nohash-hasher = { version = "0.2.0", optional = true } parking_lot = { version = "0.12.1", optional = true } rand = { version = "0.8", optional = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.48", optional = true } +thiserror = { optional = true, workspace = true } tracing = { version = "0.1.29", optional = true } trie-db = { version = "0.28.0", default-features = false } trie-root = { version = "0.18.0", default-features = false } diff --git a/substrate/primitives/trie/src/node_header.rs b/substrate/primitives/trie/src/node_header.rs index c118ee07b8cbd362b457d4b496861beec435d34d..dc33e6bd614928bef05c9c840b9708ca80f065f2 100644 --- a/substrate/primitives/trie/src/node_header.rs +++ b/substrate/primitives/trie/src/node_header.rs @@ -19,7 +19,7 @@ use crate::trie_constants; use codec::{Decode, Encode, Input, Output}; -use sp_std::iter::once; +use core::iter::once; /// A node header #[derive(Copy, Clone, PartialEq, Eq, sp_core::RuntimeDebug)] @@ -118,7 +118,7 @@ pub(crate) fn size_and_prefix_iterator( prefix_mask: usize, ) -> impl Iterator { let max_value = 255u8 >> prefix_mask; - let l1 = sp_std::cmp::min((max_value as usize).saturating_sub(1), size); + let l1 = core::cmp::min((max_value as usize).saturating_sub(1), size); let (first_byte, mut rem) = if size == l1 { (once(prefix + l1 as u8), 0) } else { @@ -138,7 +138,7 @@ pub(crate) fn size_and_prefix_iterator( None } }; - first_byte.chain(sp_std::iter::from_fn(next_bytes)) + first_byte.chain(core::iter::from_fn(next_bytes)) } /// Encodes size and prefix to a stream output. diff --git a/substrate/primitives/version/Cargo.toml b/substrate/primitives/version/Cargo.toml index 1ceda4e700f8aad52519f7864af8ad0ebfe3fe1d..a94e2322430b421d24b438b2899b67feec9dd059 100644 --- a/substrate/primitives/version/Cargo.toml +++ b/substrate/primitives/version/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-version" -version = "22.0.0" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,9 +21,9 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = impl-serde = { version = "0.4.0", default-features = false, optional = true } parity-wasm = { version = "0.45", optional = true } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"], optional = true } -thiserror = { version = "1.0.48", optional = true } -sp-core-hashing-proc-macro = { path = "../core/hashing/proc-macro" } +serde = { features = ["alloc", "derive"], optional = true, workspace = true } +thiserror = { optional = true, workspace = true } +sp-crypto-hashing-proc-macro = { path = "../crypto/hashing/proc-macro" } sp-runtime = { path = "../runtime", default-features = false } sp-std = { path = "../std", default-features = false } sp-version-proc-macro = { path = "proc-macro", default-features = false } diff --git a/substrate/primitives/version/proc-macro/Cargo.toml b/substrate/primitives/version/proc-macro/Cargo.toml index adf70dbd166192c7c318f32920a15fa5da16f398..f7abf88c9a67b7e3102d44254b520a9bbeb7a8a8 100644 --- a/substrate/primitives/version/proc-macro/Cargo.toml +++ b/substrate/primitives/version/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-version-proc-macro" -version = "8.0.0" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -21,8 +21,8 @@ proc-macro = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } proc-macro2 = "1.0.56" -quote = "1.0.28" -syn = { version = "2.0.48", features = ["extra-traits", "fold", "full", "visit"] } +quote = { workspace = true } +syn = { features = ["extra-traits", "fold", "full", "visit"], workspace = true } [dev-dependencies] sp-version = { path = ".." } diff --git a/substrate/primitives/version/src/lib.rs b/substrate/primitives/version/src/lib.rs index 13f4520f6e69480466c77644835f23c1dc79721f..9b14a809ac100de9e7a0655fdd7d5e55d23fbc0e 100644 --- a/substrate/primitives/version/src/lib.rs +++ b/substrate/primitives/version/src/lib.rs @@ -297,7 +297,7 @@ fn has_api_with bool>(apis: &ApisVec, id: &ApiId, predicate: P) -> /// Returns the version of the `Core` runtime api. pub fn core_version_from_apis(apis: &ApisVec) -> Option { - let id = sp_core_hashing_proc_macro::blake2b_64!(b"Core"); + let id = sp_crypto_hashing_proc_macro::blake2b_64!(b"Core"); apis.iter().find(|(s, _v)| s == &id).map(|(_s, v)| *v) } diff --git a/substrate/primitives/wasm-interface/Cargo.toml b/substrate/primitives/wasm-interface/Cargo.toml index 9fe5cc1f2d00ba48fc609521194534202e8d046b..f7d1038903eab6954374e6dac141b74683315a25 100644 --- a/substrate/primitives/wasm-interface/Cargo.toml +++ b/substrate/primitives/wasm-interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-wasm-interface" -version = "14.0.0" +version = "20.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" -log = { version = "0.4.17", optional = true } +log = { optional = true, workspace = true, default-features = true } wasmtime = { version = "8.0.1", default-features = false, optional = true } anyhow = { version = "1.0.68", optional = true } sp-std = { path = "../std", default-features = false } diff --git a/substrate/primitives/weights/Cargo.toml b/substrate/primitives/weights/Cargo.toml index d89182b6642cec040089f0701d68ab948b6f6a05..a7d61de001b5525af1a43148a6c7f6f284c4e51f 100644 --- a/substrate/primitives/weights/Cargo.toml +++ b/substrate/primitives/weights/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-weights" -version = "20.0.0" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,10 +16,10 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -bounded-collections = { version = "0.1.4", default-features = false } +bounded-collections = { version = "0.2.0", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.195", default-features = false, optional = true, features = ["alloc", "derive"] } +serde = { optional = true, features = ["alloc", "derive"], workspace = true } smallvec = "1.11.0" sp-arithmetic = { path = "../arithmetic", default-features = false } sp-debug-derive = { path = "../debug-derive", default-features = false } diff --git a/substrate/primitives/weights/src/lib.rs b/substrate/primitives/weights/src/lib.rs index ef431bddee265fab663efd1e5b039a1c3e78bdc4..b2c956266e2c3a346ca32c7b74ceb7f4c5cc2c0c 100644 --- a/substrate/primitives/weights/src/lib.rs +++ b/substrate/primitives/weights/src/lib.rs @@ -18,9 +18,6 @@ //! # Primitives for transaction weighting. #![cfg_attr(not(feature = "std"), no_std)] -// TODO remove once `OldWeight` is gone. I dont know why this is needed, maybe by one of the macros -// of `OldWeight`. -#![allow(deprecated)] extern crate self as sp_weights; @@ -28,7 +25,7 @@ mod weight_meter; mod weight_v2; use bounded_collections::Get; -use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -52,28 +49,6 @@ pub mod constants { pub const WEIGHT_PROOF_SIZE_PER_KB: u64 = 1024; } -/// The old weight type. -/// -/// NOTE: This type exists purely for compatibility purposes! Use [`weight_v2::Weight`] in all other -/// cases. -#[derive( - Decode, - Encode, - CompactAs, - PartialEq, - Eq, - Clone, - Copy, - RuntimeDebug, - Default, - MaxEncodedLen, - TypeInfo, -)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(transparent))] -#[deprecated(note = "Will be removed soon; use `Weight` instead.")] -pub struct OldWeight(pub u64); - /// The weight of database operations that the runtime can invoke. /// /// NOTE: This is currently only measured in computational time, and will probably @@ -235,7 +210,7 @@ where } /// Implementor of `WeightToFee` that maps one unit of weight to one unit of fee. -pub struct IdentityFee(sp_std::marker::PhantomData); +pub struct IdentityFee(core::marker::PhantomData); impl WeightToFee for IdentityFee where @@ -249,7 +224,7 @@ where } /// Implementor of [`WeightToFee`] such that it maps any unit of weight to a fixed fee. -pub struct FixedFee(sp_std::marker::PhantomData); +pub struct FixedFee(core::marker::PhantomData); impl WeightToFee for FixedFee where @@ -275,7 +250,7 @@ pub type NoFee = FixedFee<0, T>; /// // Results in a multiplier of 10 for each unit of weight (or length) /// type LengthToFee = ConstantMultiplier::>; /// ``` -pub struct ConstantMultiplier(sp_std::marker::PhantomData<(T, M)>); +pub struct ConstantMultiplier(core::marker::PhantomData<(T, M)>); impl WeightToFee for ConstantMultiplier where diff --git a/substrate/primitives/weights/src/weight_meter.rs b/substrate/primitives/weights/src/weight_meter.rs index 584d22304c3ae33ece669bc69927a2568fb9b288..cfe8396ae6d67ed36d038f6afc2a7700c56b815a 100644 --- a/substrate/primitives/weights/src/weight_meter.rs +++ b/substrate/primitives/weights/src/weight_meter.rs @@ -118,13 +118,6 @@ impl WeightMeter { debug_assert!(self.consumed.all_lte(self.limit), "Weight counter overflow"); } - /// Consume the given weight after checking that it can be consumed and return `true`. Otherwise - /// do nothing and return `false`. - #[deprecated(note = "Use `try_consume` instead. Will be removed after December 2023.")] - pub fn check_accrue(&mut self, w: Weight) -> bool { - self.try_consume(w).is_ok() - } - /// Consume the given weight after checking that it can be consumed. /// /// Returns `Ok` if the weight can be consumed or otherwise an `Err`. @@ -139,16 +132,15 @@ impl WeightMeter { }) } - /// Check if the given weight can be consumed. - #[deprecated(note = "Use `can_consume` instead. Will be removed after December 2023.")] - pub fn can_accrue(&self, w: Weight) -> bool { - self.can_consume(w) - } - /// Check if the given weight can be consumed. pub fn can_consume(&self, w: Weight) -> bool { self.consumed.checked_add(&w).map_or(false, |t| t.all_lte(self.limit)) } + + /// Reclaim the given weight. + pub fn reclaim_proof_size(&mut self, s: u64) { + self.consumed.saturating_reduce(Weight::from_parts(0, s)); + } } #[cfg(test)] @@ -160,80 +152,80 @@ mod tests { fn weight_meter_remaining_works() { let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20)); - assert!(meter.check_accrue(Weight::from_parts(5, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(())); assert_eq!(meter.consumed, Weight::from_parts(5, 0)); assert_eq!(meter.remaining(), Weight::from_parts(5, 20)); - assert!(meter.check_accrue(Weight::from_parts(2, 10))); + assert_eq!(meter.try_consume(Weight::from_parts(2, 10)), Ok(())); assert_eq!(meter.consumed, Weight::from_parts(7, 10)); assert_eq!(meter.remaining(), Weight::from_parts(3, 10)); - assert!(meter.check_accrue(Weight::from_parts(3, 10))); + assert_eq!(meter.try_consume(Weight::from_parts(3, 10)), Ok(())); assert_eq!(meter.consumed, Weight::from_parts(10, 20)); assert_eq!(meter.remaining(), Weight::from_parts(0, 0)); } #[test] - fn weight_meter_can_accrue_works() { + fn weight_meter_can_consume_works() { let meter = WeightMeter::with_limit(Weight::from_parts(1, 1)); - assert!(meter.can_accrue(Weight::from_parts(0, 0))); - assert!(meter.can_accrue(Weight::from_parts(1, 1))); - assert!(!meter.can_accrue(Weight::from_parts(0, 2))); - assert!(!meter.can_accrue(Weight::from_parts(2, 0))); - assert!(!meter.can_accrue(Weight::from_parts(2, 2))); + assert!(meter.can_consume(Weight::from_parts(0, 0))); + assert!(meter.can_consume(Weight::from_parts(1, 1))); + assert!(!meter.can_consume(Weight::from_parts(0, 2))); + assert!(!meter.can_consume(Weight::from_parts(2, 0))); + assert!(!meter.can_consume(Weight::from_parts(2, 2))); } #[test] - fn weight_meter_check_accrue_works() { + fn weight_meter_try_consume_works() { let mut meter = WeightMeter::with_limit(Weight::from_parts(2, 2)); - assert!(meter.check_accrue(Weight::from_parts(0, 0))); - assert!(meter.check_accrue(Weight::from_parts(1, 1))); - assert!(!meter.check_accrue(Weight::from_parts(0, 2))); - assert!(!meter.check_accrue(Weight::from_parts(2, 0))); - assert!(!meter.check_accrue(Weight::from_parts(2, 2))); - assert!(meter.check_accrue(Weight::from_parts(0, 1))); - assert!(meter.check_accrue(Weight::from_parts(1, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 0)), Ok(())); + assert_eq!(meter.try_consume(Weight::from_parts(1, 1)), Ok(())); + assert_eq!(meter.try_consume(Weight::from_parts(0, 2)), Err(())); + assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Err(())); + assert_eq!(meter.try_consume(Weight::from_parts(2, 2)), Err(())); + assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Ok(())); + assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Ok(())); } #[test] - fn weight_meter_check_and_can_accrue_works() { + fn weight_meter_check_and_can_consume_works() { let mut meter = WeightMeter::new(); - assert!(meter.can_accrue(Weight::from_parts(u64::MAX, 0))); - assert!(meter.check_accrue(Weight::from_parts(u64::MAX, 0))); + assert!(meter.can_consume(Weight::from_parts(u64::MAX, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(u64::MAX, 0)), Ok(())); - assert!(meter.can_accrue(Weight::from_parts(0, u64::MAX))); - assert!(meter.check_accrue(Weight::from_parts(0, u64::MAX))); + assert!(meter.can_consume(Weight::from_parts(0, u64::MAX))); + assert_eq!(meter.try_consume(Weight::from_parts(0, u64::MAX)), Ok(())); - assert!(!meter.can_accrue(Weight::from_parts(0, 1))); - assert!(!meter.check_accrue(Weight::from_parts(0, 1))); + assert!(!meter.can_consume(Weight::from_parts(0, 1))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Err(())); - assert!(!meter.can_accrue(Weight::from_parts(1, 0))); - assert!(!meter.check_accrue(Weight::from_parts(1, 0))); + assert!(!meter.can_consume(Weight::from_parts(1, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Err(())); - assert!(meter.can_accrue(Weight::zero())); - assert!(meter.check_accrue(Weight::zero())); + assert!(meter.can_consume(Weight::zero())); + assert_eq!(meter.try_consume(Weight::zero()), Ok(())); } #[test] fn consumed_ratio_works() { let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20)); - assert!(meter.check_accrue(Weight::from_parts(5, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(50)); - assert!(meter.check_accrue(Weight::from_parts(0, 12))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 12)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(60)); - assert!(meter.check_accrue(Weight::from_parts(2, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(70)); - assert!(meter.check_accrue(Weight::from_parts(0, 4))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(80)); - assert!(meter.check_accrue(Weight::from_parts(3, 0))); + assert_eq!(meter.try_consume(Weight::from_parts(3, 0)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100)); - assert!(meter.check_accrue(Weight::from_parts(0, 4))); + assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(())); assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100)); } @@ -277,6 +269,21 @@ mod tests { assert_eq!(meter.consumed(), Weight::from_parts(5, 10)); } + #[test] + #[cfg(debug_assertions)] + fn reclaim_works() { + let mut meter = WeightMeter::with_limit(Weight::from_parts(5, 10)); + + meter.consume(Weight::from_parts(5, 10)); + assert_eq!(meter.consumed(), Weight::from_parts(5, 10)); + + meter.reclaim_proof_size(3); + assert_eq!(meter.consumed(), Weight::from_parts(5, 7)); + + meter.reclaim_proof_size(10); + assert_eq!(meter.consumed(), Weight::from_parts(5, 0)); + } + #[test] #[cfg(debug_assertions)] #[should_panic(expected = "Weight counter overflow")] diff --git a/substrate/scripts/ci/node-template-release/Cargo.toml b/substrate/scripts/ci/node-template-release/Cargo.toml index a0b93184546288b1c326e1f2584545dc12a0c7d6..cc60d0a4510132c543162c3bcf9964e35644b24e 100644 --- a/substrate/scripts/ci/node-template-release/Cargo.toml +++ b/substrate/scripts/ci/node-template-release/Cargo.toml @@ -14,7 +14,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } flate2 = "1.0" fs_extra = "1.3" glob = "0.3" diff --git a/substrate/scripts/ci/node-template-release/src/main.rs b/substrate/scripts/ci/node-template-release/src/main.rs index fc8089f3051fe14a7608bcfad1418069d3bfcff9..2bcda2944fc90e62ed947af54c7be680c1038f38 100644 --- a/substrate/scripts/ci/node-template-release/src/main.rs +++ b/substrate/scripts/ci/node-template-release/src/main.rs @@ -18,6 +18,7 @@ use std::{ collections::HashMap, + ffi::OsString, fs::{self, File, OpenOptions}, path::{Path, PathBuf}, process::Command, @@ -56,9 +57,24 @@ struct Options { } /// Copy the `node-template` to the given path. -fn copy_node_template(node_template: &Path, dest_path: &Path) { +fn copy_node_template(node_template: &Path, node_template_folder: &OsString, dest_path: &Path) { let options = CopyOptions::new(); dir::copy(node_template, dest_path, &options).expect("Copies node-template to tmp dir"); + + let dest_path = dest_path.join(node_template_folder); + + dir::get_dir_content(dest_path.join("env-setup")) + .expect("`env-setup` directory should exist") + .files + .iter() + .for_each(|f| { + fs::copy( + f, + dest_path.join(PathBuf::from(f).file_name().expect("File has a file name.")), + ) + .expect("Copying from `env-setup` directory works"); + }); + dir::remove(dest_path.join("env-setup")).expect("Deleting `env-setup works`"); } /// Find all `Cargo.toml` files in the given path. @@ -195,6 +211,9 @@ fn update_root_cargo_toml( commit_id: &str, ) { let mut workspace = Table::new(); + + workspace.insert("resolver", value("2")); + workspace.insert("members", value(Array::from_iter(members.iter()))); let mut workspace_dependencies = Table::new(); deps.values() @@ -216,6 +235,8 @@ fn update_root_cargo_toml( workspace.insert("package", Item::Table(package)); workspace.insert("dependencies", Item::Table(workspace_dependencies)); + + workspace.insert("lints", Item::Table(Table::new())); cargo_toml.insert("workspace", Item::Table(workspace)); let mut panic_unwind = Table::new(); @@ -294,7 +315,7 @@ fn main() { .file_name() .expect("Node template folder is last element of path") .to_owned(); - copy_node_template(&options.node_template, build_dir.path()); + copy_node_template(&options.node_template, &node_template_folder, build_dir.path()); // The path to the node-template in the build dir. let node_template_path = build_dir.path().join(node_template_folder); @@ -429,6 +450,7 @@ frame-system = { workspace = true } ); let expected_toml = r#"[workspace] +resolver = "2" members = ["node", "pallets/template", "runtime"] [workspace.package] @@ -438,6 +460,8 @@ edition = "2021" frame-system = { version = "4.0.0-dev", default-features = true, git = "https://github.com/paritytech/polkadot-sdk.git", rev = "commit_id" } sp-io = { version = "7.0.0", git = "https://github.com/paritytech/polkadot-sdk.git", rev = "commit_id" } +[workspace.lints] + [profile] [profile.release] diff --git a/substrate/scripts/run_all_benchmarks.sh b/substrate/scripts/run_all_benchmarks.sh index 83848100a7e5189f312411fb42eae7d4eb6a43c4..fe5f89a5b56ea109cee20274ce7c1454b72b7009 100755 --- a/substrate/scripts/run_all_benchmarks.sh +++ b/substrate/scripts/run_all_benchmarks.sh @@ -59,11 +59,12 @@ done if [ "$skip_build" != true ] then echo "[+] Compiling Substrate benchmarks..." - cargo build --profile=production --locked --features=runtime-benchmarks --bin substrate + cargo build --profile=production --locked --features=runtime-benchmarks --bin substrate-node fi # The executable to use. -SUBSTRATE=./target/production/substrate +# Parent directory because of the monorepo structure. +SUBSTRATE=../target/production/substrate-node # Manually exclude some pallets. EXCLUDED_PALLETS=( @@ -80,11 +81,7 @@ EXCLUDED_PALLETS=( # Load all pallet names in an array. ALL_PALLETS=($( - $SUBSTRATE benchmark pallet --list --chain=dev |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq + $SUBSTRATE benchmark pallet --list=pallets --no-csv-header --chain=dev )) # Filter out the excluded pallets by concatenating the arrays and discarding duplicates. @@ -110,6 +107,19 @@ for PALLET in "${PALLETS[@]}"; do FOLDER="$(echo "${PALLET#*_}" | tr '_' '-')"; WEIGHT_FILE="./frame/${FOLDER}/src/weights.rs" + + # Special handling of custom weight paths. + if [ "$PALLET" == "frame_system_extensions" ] || [ "$PALLET" == "frame-system-extensions" ] + then + WEIGHT_FILE="./frame/system/src/extensions/weights.rs" + elif [ "$PALLET" == "pallet_asset_conversion_tx_payment" ] || [ "$PALLET" == "pallet-asset-conversion-tx-payment" ] + then + WEIGHT_FILE="./frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs" + elif [ "$PALLET" == "pallet_asset_tx_payment" ] || [ "$PALLET" == "pallet-asset-tx-payment" ] + then + WEIGHT_FILE="./frame/transaction-payment/asset-tx-payment/src/weights.rs" + fi + echo "[+] Benchmarking $PALLET with weight file $WEIGHT_FILE"; OUTPUT=$( diff --git a/substrate/test-utils/client/Cargo.toml b/substrate/test-utils/client/Cargo.toml index 79071e19e2816292d31f8dd8da54e54c4444f667..349b04d32d7baff5c24323e171ebe373730f464f 100644 --- a/substrate/test-utils/client/Cargo.toml +++ b/substrate/test-utils/client/Cargo.toml @@ -20,8 +20,8 @@ array-bytes = "6.1" async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" -serde = "1.0.195" -serde_json = "1.0.111" +serde = { workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } sc-client-api = { path = "../../client/api" } sc-client-db = { path = "../../client/db", default-features = false, features = [ "test-helpers", @@ -39,3 +39,4 @@ sp-keyring = { path = "../../primitives/keyring" } sp-keystore = { path = "../../primitives/keystore" } sp-runtime = { path = "../../primitives/runtime" } sp-state-machine = { path = "../../primitives/state-machine" } +tokio = { version = "1.22.0", features = ["sync"] } diff --git a/substrate/test-utils/client/src/lib.rs b/substrate/test-utils/client/src/lib.rs index f383f7c3dc3e06d1ce5bcdafbb8ab3729a20734f..d283b24f286aed743ba613f095f2cd319caba263 100644 --- a/substrate/test-utils/client/src/lib.rs +++ b/substrate/test-utils/client/src/lib.rs @@ -72,6 +72,7 @@ pub struct TestClientBuilder, bad_blocks: BadBlocks, enable_offchain_indexing_api: bool, + enable_import_proof_recording: bool, no_genesis: bool, } @@ -120,6 +121,7 @@ impl bad_blocks: None, enable_offchain_indexing_api: false, no_genesis: false, + enable_import_proof_recording: false, } } @@ -165,6 +167,12 @@ impl self } + /// Enable proof recording on import. + pub fn enable_import_proof_recording(mut self) -> Self { + self.enable_import_proof_recording = true; + self + } + /// Disable writing genesis. pub fn set_no_genesis(mut self) -> Self { self.no_genesis = true; @@ -202,6 +210,7 @@ impl }; let client_config = ClientConfig { + enable_import_proof_recording: self.enable_import_proof_recording, offchain_indexing_api: self.enable_offchain_indexing_api, no_genesis: self.no_genesis, ..Default::default() @@ -284,7 +293,7 @@ pub struct RpcTransactionOutput { /// The output string of the transaction if any. pub result: String, /// An async receiver if data will be returned via a callback. - pub receiver: futures::channel::mpsc::UnboundedReceiver, + pub receiver: tokio::sync::mpsc::Receiver, } impl std::fmt::Debug for RpcTransactionOutput { @@ -344,7 +353,7 @@ impl RpcHandlersExt for RpcHandlers { pub(crate) fn parse_rpc_result( result: String, - receiver: futures::channel::mpsc::UnboundedReceiver, + receiver: tokio::sync::mpsc::Receiver, ) -> Result { let json: serde_json::Value = serde_json::from_str(&result).expect("the result can only be a JSONRPC string; qed"); @@ -398,7 +407,7 @@ where mod tests { #[test] fn parses_error_properly() { - let (_, rx) = futures::channel::mpsc::unbounded(); + let (_, rx) = tokio::sync::mpsc::channel(1); assert!(super::parse_rpc_result( r#"{ "jsonrpc": "2.0", @@ -410,7 +419,7 @@ mod tests { ) .is_ok()); - let (_, rx) = futures::channel::mpsc::unbounded(); + let (_, rx) = tokio::sync::mpsc::channel(1); let error = super::parse_rpc_result( r#"{ "jsonrpc": "2.0", @@ -428,7 +437,7 @@ mod tests { assert_eq!(error.message, "Method not found"); assert!(error.data.is_none()); - let (_, rx) = futures::channel::mpsc::unbounded(); + let (_, rx) = tokio::sync::mpsc::channel(1); let error = super::parse_rpc_result( r#"{ "jsonrpc": "2.0", diff --git a/substrate/test-utils/runtime/Cargo.toml b/substrate/test-utils/runtime/Cargo.toml index 1eb50771a2ce99944a400d2835ab12058affffd9..3bba5cd5bf0473f9f26bfc56619fc9ee8d098414 100644 --- a/substrate/test-utils/runtime/Cargo.toml +++ b/substrate/test-utils/runtime/Cargo.toml @@ -27,6 +27,7 @@ sp-inherents = { path = "../../primitives/inherents", default-features = false } sp-keyring = { path = "../../primitives/keyring", optional = true } sp-offchain = { path = "../../primitives/offchain", default-features = false } sp-core = { path = "../../primitives/core", default-features = false } +sp-crypto-hashing = { path = "../../primitives/crypto/hashing", default-features = false } sp-std = { path = "../../primitives/std", default-features = false } sp-io = { path = "../../primitives/io", default-features = false } frame-support = { path = "../../frame/support", default-features = false } @@ -50,19 +51,19 @@ sp-externalities = { path = "../../primitives/externalities", default-features = # 3rd party array-bytes = { version = "6.1", optional = true } -log = { version = "0.4.17", default-features = false } +log = { workspace = true } [dev-dependencies] futures = "0.3.21" sc-block-builder = { path = "../../client/block-builder" } +sc-chain-spec = { path = "../../client/chain-spec" } sc-executor = { path = "../../client/executor" } sc-executor-common = { path = "../../client/executor/common" } sp-consensus = { path = "../../primitives/consensus/common" } substrate-test-runtime-client = { path = "client" } sp-tracing = { path = "../../primitives/tracing" } -json-patch = { version = "1.0.0", default-features = false } -serde = { version = "1.0.195", features = ["alloc", "derive"], default-features = false } -serde_json = { version = "1.0.111", default-features = false, features = ["alloc"] } +serde = { features = ["alloc", "derive"], workspace = true } +serde_json = { features = ["alloc"], workspace = true } [build-dependencies] substrate-wasm-builder = { path = "../../utils/wasm-builder", optional = true } @@ -93,6 +94,7 @@ std = [ "sp-consensus-babe/std", "sp-consensus-grandpa/std", "sp-core/std", + "sp-crypto-hashing/std", "sp-externalities/std", "sp-genesis-builder/std", "sp-inherents/std", diff --git a/substrate/test-utils/runtime/src/extrinsic.rs b/substrate/test-utils/runtime/src/extrinsic.rs index 05ffb7db5d5b95d6d6dbadf5f48da563c04a4c59..7eb1839e97b68be8c36015bc8c7d3ef9b9620515 100644 --- a/substrate/test-utils/runtime/src/extrinsic.rs +++ b/substrate/test-utils/runtime/src/extrinsic.rs @@ -25,7 +25,7 @@ use codec::Encode; use frame_system::{CheckNonce, CheckWeight}; use sp_core::crypto::Pair as TraitPair; use sp_keyring::AccountKeyring; -use sp_runtime::{transaction_validity::TransactionPriority, Perbill}; +use sp_runtime::{generic::Preamble, transaction_validity::TransactionPriority, Perbill}; use sp_std::prelude::*; /// Transfer used in test substrate pallet. Extrinsic is created and signed using this data. @@ -66,11 +66,11 @@ impl TryFrom<&Extrinsic> for TransferData { match uxt { Extrinsic { function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }), - signature: Some((from, _, (CheckNonce(nonce), ..))), + preamble: Preamble::Signed(from, _, ((CheckNonce(nonce), ..), ..)), } => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }), Extrinsic { function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }), - signature: None, + preamble: Preamble::Bare, } => Ok(transfer.clone()), _ => Err(()), } @@ -190,18 +190,17 @@ impl ExtrinsicBuilder { /// Build `Extrinsic` using embedded parameters pub fn build(self) -> Extrinsic { if let Some(signer) = self.signer { - let extra = ( - CheckNonce::from(self.nonce.unwrap_or(0)), - CheckWeight::new(), + let tx_ext = ( + (CheckNonce::from(self.nonce.unwrap_or(0)), CheckWeight::new()), CheckSubstrateCall {}, ); let raw_payload = - SignedPayload::from_raw(self.function.clone(), extra.clone(), ((), (), ())); + SignedPayload::from_raw(self.function.clone(), tx_ext.clone(), (((), ()), ())); let signature = raw_payload.using_encoded(|e| signer.sign(e)); - Extrinsic::new_signed(self.function, signer.public(), signature, extra) + Extrinsic::new_signed(self.function, signer.public(), signature, tx_ext) } else { - Extrinsic::new_unsigned(self.function) + Extrinsic::new_bare(self.function) } } } diff --git a/substrate/test-utils/runtime/src/genesismap.rs b/substrate/test-utils/runtime/src/genesismap.rs index 5ed9c8a645886f741611bc9fdc5d4eae01ae2704..9e972886b3771469284a7ee66e5f823623a75b07 100644 --- a/substrate/test-utils/runtime/src/genesismap.rs +++ b/substrate/test-utils/runtime/src/genesismap.rs @@ -124,7 +124,6 @@ impl GenesisStorageBuilder { .into_iter() .map(|x| (x.into(), 1)) .collect(), - epoch_config: Some(crate::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION), ..Default::default() }, substrate_test: substrate_test_pallet::GenesisConfig { diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 16ab467772f2f60a4b6496da88b9129cc04851f5..7ec5f6722c4dde6a6aa943154ac5aa9fb9062bd1 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -58,10 +58,12 @@ use sp_api::{decl_runtime_apis, impl_runtime_apis}; pub use sp_core::hash::H256; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ - create_runtime_str, impl_opaque_keys, - traits::{BlakeTwo256, Block as BlockT, DispatchInfoOf, NumberFor, Verify}, - transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, - ApplyExtrinsicResult, Perbill, + create_runtime_str, impl_opaque_keys, impl_tx_ext_default, + traits::{BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, NumberFor, Verify}, + transaction_validity::{ + TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, + }, + ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill, }; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; @@ -142,13 +144,14 @@ pub type Signature = sr25519::Signature; #[cfg(feature = "std")] pub type Pair = sp_core::sr25519::Pair; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = (CheckNonce, CheckWeight, CheckSubstrateCall); +// TODO: Remove after the Checks are migrated to TxExtension. +/// The extension to the basic transaction logic. +pub type TxExtension = ((CheckNonce, CheckWeight), CheckSubstrateCall); /// The payload being signed in transactions. -pub type SignedPayload = sp_runtime::generic::SignedPayload; +pub type SignedPayload = sp_runtime::generic::SignedPayload; /// Unchecked extrinsic type as expected by this runtime. pub type Extrinsic = - sp_runtime::generic::UncheckedExtrinsic; + sp_runtime::generic::UncheckedExtrinsic; /// An identifier for an account on this system. pub type AccountId = ::Signer; @@ -243,7 +246,7 @@ impl sp_runtime::traits::Printable for CheckSubstrateCall { } impl sp_runtime::traits::Dispatchable for CheckSubstrateCall { - type RuntimeOrigin = CheckSubstrateCall; + type RuntimeOrigin = RuntimeOrigin; type Config = CheckSubstrateCall; type Info = CheckSubstrateCall; type PostInfo = CheckSubstrateCall; @@ -256,42 +259,37 @@ impl sp_runtime::traits::Dispatchable for CheckSubstrateCall { } } -impl sp_runtime::traits::SignedExtension for CheckSubstrateCall { - type AccountId = AccountId; - type Call = RuntimeCall; - type AdditionalSigned = (); - type Pre = (); +impl sp_runtime::traits::TransactionExtensionBase for CheckSubstrateCall { const IDENTIFIER: &'static str = "CheckSubstrateCall"; - - fn additional_signed( - &self, - ) -> sp_std::result::Result { - Ok(()) - } + type Implicit = (); +} +impl sp_runtime::traits::TransactionExtension + for CheckSubstrateCall +{ + type Pre = (); + type Val = (); + impl_tx_ext_default!(RuntimeCall; Context; prepare); fn validate( &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { + _context: &mut Context, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { log::trace!(target: LOG_TARGET, "validate"); - match call { + let v = match call { RuntimeCall::SubstrateTest(ref substrate_test_call) => - substrate_test_pallet::validate_runtime_call(substrate_test_call), - _ => Ok(Default::default()), - } - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &sp_runtime::traits::DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(drop) + substrate_test_pallet::validate_runtime_call(substrate_test_call)?, + _ => Default::default(), + }; + Ok((v, (), origin)) } } @@ -364,6 +362,7 @@ impl frame_system::pallet::Config for Runtime { type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); + type ExtensionsWeightInfo = (); type SS58Prefix = (); type OnSetCode = (); type MaxConsumers = ConstU32<16>; @@ -398,7 +397,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type MaxHolds = ConstU32<1>; } impl substrate_test_pallet::Config for Runtime {} @@ -481,9 +479,9 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> ExtrinsicInclusionMode { log::trace!(target: LOG_TARGET, "initialize_block: {header:#?}"); - Executive::initialize_block(header); + Executive::initialize_block(header) } } @@ -673,7 +671,7 @@ impl_runtime_apis! { impl sp_offchain::OffchainWorkerApi for Runtime { fn offchain_worker(header: &::Header) { - let ext = Extrinsic::new_unsigned( + let ext = Extrinsic::new_bare( substrate_test_pallet::pallet::Call::storage_change{ key:b"some_key".encode(), value:Some(header.number.encode()) @@ -845,11 +843,11 @@ pub mod storage_key_generator { } fn concat_hashes(input: &Vec<&[u8]>) -> String { - input.iter().map(|s| sp_core::hashing::twox_128(s)).map(hex).collect() + input.iter().map(|s| sp_crypto_hashing::twox_128(s)).map(hex).collect() } fn twox_64_concat(x: &[u8]) -> Vec { - sp_core::hashing::twox_64(x).iter().chain(x.iter()).cloned().collect::>() + sp_crypto_hashing::twox_64(x).iter().chain(x.iter()).cloned().collect() } /// Generate the hashed storage keys from the raw literals. These keys are expected to be be in @@ -890,7 +888,7 @@ pub mod storage_key_generator { AccountKeyring::Charlie.public().to_vec(), ]) .map(|pubkey| { - sp_core::hashing::blake2_128(&pubkey) + sp_crypto_hashing::blake2_128(&pubkey) .iter() .chain(pubkey.iter()) .cloned() @@ -1026,7 +1024,7 @@ mod tests { use sp_core::{storage::well_known_keys::HEAP_PAGES, traits::CallContext}; use sp_keyring::AccountKeyring; use sp_runtime::{ - traits::{Hash as _, SignedExtension}, + traits::{DispatchTransaction, Hash as _}, transaction_validity::{InvalidTransaction, ValidTransaction}, }; use substrate_test_runtime_client::{ @@ -1175,31 +1173,33 @@ mod tests { fn check_substrate_check_signed_extension_works() { sp_tracing::try_init_simple(); new_test_ext().execute_with(|| { - let x = sp_keyring::AccountKeyring::Alice.into(); + let x: AccountId = sp_keyring::AccountKeyring::Alice.into(); let info = DispatchInfo::default(); let len = 0_usize; assert_eq!( CheckSubstrateCall {} - .validate( - &x, + .validate_only( + Some(x).into(), &ExtrinsicBuilder::new_call_with_priority(16).build().function, &info, - len + len, ) .unwrap() + .0 .priority, 16 ); assert_eq!( CheckSubstrateCall {} - .validate( - &x, + .validate_only( + Some(x).into(), &ExtrinsicBuilder::new_call_do_not_propagate().build().function, &info, - len + len, ) .unwrap() + .0 .propagate, false ); @@ -1292,7 +1292,7 @@ mod tests { let r = Vec::::decode(&mut &r[..]).unwrap(); let json = String::from_utf8(r.into()).expect("returned value is json. qed."); - let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; + let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":{"c":[1,4],"allowed_slots":"PrimaryAndSecondaryVRFSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; assert_eq!(expected.to_string(), json); } @@ -1410,7 +1410,7 @@ mod tests { } }); - json_patch::merge(&mut default_config, &patch); + sc_chain_spec::json_merge(&mut default_config, patch); // Build genesis config using custom json: let mut t = BasicExternalities::new_empty(); diff --git a/substrate/test-utils/runtime/transaction-pool/Cargo.toml b/substrate/test-utils/runtime/transaction-pool/Cargo.toml index b52a897438b6854a066a95e51ba49bd0eddd7f84..33e56e8e55e7a7479fb1c783af818f3120e1b7f4 100644 --- a/substrate/test-utils/runtime/transaction-pool/Cargo.toml +++ b/substrate/test-utils/runtime/transaction-pool/Cargo.toml @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" parking_lot = "0.12.1" -thiserror = "1.0" +thiserror = { workspace = true } sc-transaction-pool = { path = "../../../client/transaction-pool" } sc-transaction-pool-api = { path = "../../../client/transaction-pool/api" } sp-blockchain = { path = "../../../primitives/blockchain" } diff --git a/substrate/test-utils/runtime/transaction-pool/src/lib.rs b/substrate/test-utils/runtime/transaction-pool/src/lib.rs index 8c8345b06bd32e4efbe2f721ddebe2cc2f2f9c4e..5202e6e65154d62085a0656cc780cc39452e0ed9 100644 --- a/substrate/test-utils/runtime/transaction-pool/src/lib.rs +++ b/substrate/test-utils/runtime/transaction-pool/src/lib.rs @@ -81,6 +81,7 @@ pub struct ChainState { pub block_by_hash: HashMap, pub nonces: HashMap, pub invalid_hashes: HashSet, + pub priorities: HashMap, } /// Test Api for transaction pool. @@ -214,6 +215,22 @@ impl TestApi { self.chain.write().invalid_hashes.insert(Self::hash_and_length_inner(xts).0); } + /// Remove a transaction that was previously declared as invalid via `[Self::add_invalid]`. + /// + /// Next time transaction pool will try to validate this + /// extrinsic, api will succeed. + pub fn remove_invalid(&self, xts: &Extrinsic) { + self.chain.write().invalid_hashes.remove(&Self::hash_and_length_inner(xts).0); + } + + /// Set a transaction priority. + pub fn set_priority(&self, xts: &Extrinsic, priority: u64) { + self.chain + .write() + .priorities + .insert(Self::hash_and_length_inner(xts).0, priority); + } + /// Query validation requests received. pub fn validation_requests(&self) -> Vec { self.validation_requests.read().clone() @@ -300,8 +317,14 @@ impl ChainApi for TestApi { return ready(Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0))))) } - let mut validity = - ValidTransaction { priority: 1, requires, provides, longevity: 64, propagate: true }; + let priority = self.chain.read().priorities.get(&self.hash_and_length(&uxt).0).cloned(); + let mut validity = ValidTransaction { + priority: priority.unwrap_or(1), + requires, + provides, + longevity: 64, + propagate: true, + }; (self.valid_modifier.read())(&mut validity); diff --git a/substrate/utils/binary-merkle-tree/Cargo.toml b/substrate/utils/binary-merkle-tree/Cargo.toml index 441f89b790f1b3bde9db9a67877f978c60a824f2..6ba515afee175751840edadfb7e6fc2ab01c5e46 100644 --- a/substrate/utils/binary-merkle-tree/Cargo.toml +++ b/substrate/utils/binary-merkle-tree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "binary-merkle-tree" -version = "4.0.0-dev" +version = "13.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -13,7 +13,7 @@ workspace = true [dependencies] array-bytes = { version = "6.1", optional = true } -log = { version = "0.4", default-features = false, optional = true } +log = { optional = true, workspace = true } hash-db = { version = "0.16.0", default-features = false } [dev-dependencies] diff --git a/substrate/utils/build-script-utils/Cargo.toml b/substrate/utils/build-script-utils/Cargo.toml index 464647ea723e0398937a0dc49f0f00f749786e41..e1152cd9ac8e09144a244fb1952f7545df3f0d96 100644 --- a/substrate/utils/build-script-utils/Cargo.toml +++ b/substrate/utils/build-script-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-build-script-utils" -version = "3.0.0" +version = "11.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/utils/fork-tree/Cargo.toml b/substrate/utils/fork-tree/Cargo.toml index 27bb908986f8e35e9caafc9448167ba68c41ec51..87135ef2afb884249fd4dfb2683e453c2328180d 100644 --- a/substrate/utils/fork-tree/Cargo.toml +++ b/substrate/utils/fork-tree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fork-tree" -version = "3.0.0" +version = "12.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/utils/frame/benchmarking-cli/Cargo.toml b/substrate/utils/frame/benchmarking-cli/Cargo.toml index d6a96abb23f4f55e962ebae0373458855121f052..a2db83052c54508b14e37805cbb4b004542c6f30 100644 --- a/substrate/utils/frame/benchmarking-cli/Cargo.toml +++ b/substrate/utils/frame/benchmarking-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-benchmarking-cli" -version = "4.0.0-dev" +version = "32.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "6.1" chrono = "0.4" -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.6.1" } comfy-table = { version = "7.1.0", default-features = false } handlebars = "4.2.2" @@ -26,12 +26,12 @@ Inflector = "0.11.4" itertools = "0.10.3" lazy_static = "1.4.0" linked-hash-map = "0.5.4" -log = "0.4.17" +log = { workspace = true, default-features = true } rand = { version = "0.8.5", features = ["small_rng"] } rand_pcg = "0.3.1" -serde = "1.0.195" -serde_json = "1.0.111" -thiserror = "1.0.48" +serde = { workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +thiserror = { workspace = true } thousands = "0.2.0" frame-benchmarking = { path = "../../../frame/benchmarking" } frame-support = { path = "../../../frame/support" } diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/README.md b/substrate/utils/frame/benchmarking-cli/src/machine/README.md index d3c9a0ec84062dd26acf5cf5b20b89e4c6bb95d0..97740b6845acb9b0cc745bf215943d01493bcbf2 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/README.md +++ b/substrate/utils/frame/benchmarking-cli/src/machine/README.md @@ -78,6 +78,6 @@ License: Apache-2.0 [Blake2-256]: https://www.blake2.net/ -[blake2_256 function]: https://crates.parity.io/sp_core/hashing/fn.blake2_256.html +[blake2_256 function]: https://crates.parity.io/sp_crypto_hashing/fn.blake2_256.html [Curve25519]: https://en.wikipedia.org/wiki/Curve25519 [`--base-path`]: ../shared/README.md#arguments diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs index 5c76ca68e85f5708cbccde38c5f35cb4046033c5..c6ba282401672335a6a72e698533a4af35670523 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{writer, PalletCmd}; +use super::{writer, ListOutput, PalletCmd}; use codec::{Decode, Encode}; use frame_benchmarking::{ Analysis, BenchmarkBatch, BenchmarkBatchSplitResults, BenchmarkList, BenchmarkParameter, @@ -37,9 +37,15 @@ use sp_core::{ }; use sp_externalities::Extensions; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +use sp_runtime::traits::Hash; use sp_state_machine::StateMachine; -use std::{collections::HashMap, fmt::Debug, fs, str::FromStr, time}; +use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + fmt::Debug, + fs, + str::FromStr, + time, +}; /// Logging target const LOG_TARGET: &'static str = "frame::benchmark::pallet"; @@ -140,11 +146,10 @@ This could mean that you either did not build the node correctly with the \ not created by a node that was compiled with the flag"; impl PalletCmd { - /// Runs the command and benchmarks the chain. - pub fn run(&self, config: Configuration) -> Result<()> + /// Runs the command and benchmarks a pallet. + pub fn run(&self, config: Configuration) -> Result<()> where - BB: BlockT + Debug, - <<::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug, + Hasher: Hash, ExtraHostFunctions: sp_wasm_interface::HostFunctions, { let _d = self.execution.as_ref().map(|exec| { @@ -192,6 +197,7 @@ impl PalletCmd { let spec = config.chain_spec; let pallet = self.pallet.clone().unwrap_or_default(); let pallet = pallet.as_bytes(); + let extrinsic = self.extrinsic.clone().unwrap_or_default(); let extrinsic_split: Vec<&str> = extrinsic.split(',').collect(); let extrinsics: Vec<_> = extrinsic_split.iter().map(|x| x.trim().as_bytes()).collect(); @@ -199,7 +205,7 @@ impl PalletCmd { let genesis_storage = spec.build_storage()?; let mut changes = Default::default(); let cache_size = Some(self.database_cache_size as usize); - let state_with_tracking = BenchmarkingState::::new( + let state_with_tracking = BenchmarkingState::::new( genesis_storage.clone(), cache_size, // Record proof size @@ -207,7 +213,7 @@ impl PalletCmd { // Enable storage tracking true, )?; - let state_without_tracking = BenchmarkingState::::new( + let state_without_tracking = BenchmarkingState::::new( genesis_storage, cache_size, // Do not record proof size @@ -292,16 +298,23 @@ impl PalletCmd { // Convert `Vec` to `String` for better readability. let benchmarks_to_run: Vec<_> = benchmarks_to_run .into_iter() - .map(|b| { + .map(|(pallet, extrinsic, components, pov_modes)| { + let pallet_name = + String::from_utf8(pallet.clone()).expect("Encoded from String; qed"); + let extrinsic_name = + String::from_utf8(extrinsic.clone()).expect("Encoded from String; qed"); ( - b.0, - b.1, - b.2, - b.3.into_iter() + pallet, + extrinsic, + components, + pov_modes + .into_iter() .map(|(p, s)| { (String::from_utf8(p).unwrap(), String::from_utf8(s).unwrap()) }) .collect(), + pallet_name, + extrinsic_name, ) }) .collect(); @@ -310,9 +323,8 @@ impl PalletCmd { return Err("No benchmarks found which match your input.".into()) } - if self.list { - // List benchmarks instead of running them - list_benchmark(benchmarks_to_run); + if let Some(list_output) = self.list { + list_benchmark(benchmarks_to_run, list_output, self.no_csv_header); return Ok(()) } @@ -324,12 +336,12 @@ impl PalletCmd { let mut component_ranges = HashMap::<(Vec, Vec), Vec>::new(); let pov_modes = Self::parse_pov_modes(&benchmarks_to_run)?; - for (pallet, extrinsic, components, _) in benchmarks_to_run.clone() { + for (pallet, extrinsic, components, _, pallet_name, extrinsic_name) in + benchmarks_to_run.clone() + { log::info!( target: LOG_TARGET, - "Starting benchmark: {}::{}", - String::from_utf8(pallet.clone()).expect("Encoded from String; qed"), - String::from_utf8(extrinsic.clone()).expect("Encoded from String; qed"), + "Starting benchmark: {pallet_name}::{extrinsic_name}" ); let all_components = if components.is_empty() { vec![Default::default()] @@ -410,12 +422,7 @@ impl PalletCmd { ) .map_err(|e| format!("Failed to decode benchmark results: {:?}", e))? .map_err(|e| { - format!( - "Benchmark {}::{} failed: {}", - String::from_utf8_lossy(&pallet), - String::from_utf8_lossy(&extrinsic), - e - ) + format!("Benchmark {pallet_name}::{extrinsic_name} failed: {e}",) })?; } // Do one loop of DB tracking. @@ -489,11 +496,7 @@ impl PalletCmd { log::info!( target: LOG_TARGET, - "Running benchmark: {}.{}({} args) {}/{} {}/{}", - String::from_utf8(pallet.clone()) - .expect("Encoded from String; qed"), - String::from_utf8(extrinsic.clone()) - .expect("Encoded from String; qed"), + "Running benchmark: {pallet_name}::{extrinsic_name}({} args) {}/{} {}/{}", components.len(), s + 1, // s starts at 0. all_components.len(), @@ -702,12 +705,14 @@ impl PalletCmd { Vec, Vec<(BenchmarkParameter, u32, u32)>, Vec<(String, String)>, + String, + String, )>, ) -> Result { use std::collections::hash_map::Entry; let mut parsed = PovModesMap::new(); - for (pallet, call, _components, pov_modes) in benchmarks { + for (pallet, call, _components, pov_modes, _, _) in benchmarks { for (pallet_storage, mode) in pov_modes { let mode = PovEstimationMode::from_str(&mode)?; let splits = pallet_storage.split("::").collect::>(); @@ -756,19 +761,45 @@ impl CliConfiguration for PalletCmd { /// List the benchmarks available in the runtime, in a CSV friendly format. fn list_benchmark( - mut benchmarks_to_run: Vec<( + benchmarks_to_run: Vec<( Vec, Vec, Vec<(BenchmarkParameter, u32, u32)>, Vec<(String, String)>, + String, + String, )>, + list_output: ListOutput, + no_csv_header: bool, ) { - // Sort and de-dub by pallet and function name. - benchmarks_to_run.sort_by(|(pa, sa, _, _), (pb, sb, _, _)| (pa, sa).cmp(&(pb, sb))); - benchmarks_to_run.dedup_by(|(pa, sa, _, _), (pb, sb, _, _)| (pa, sa) == (pb, sb)); + let mut benchmarks = BTreeMap::new(); - println!("pallet, benchmark"); - for (pallet, extrinsic, _, _) in benchmarks_to_run { - println!("{}, {}", String::from_utf8_lossy(&pallet), String::from_utf8_lossy(&extrinsic)); + // Sort and de-dub by pallet and function name. + benchmarks_to_run.iter().for_each(|(_, _, _, _, pallet_name, extrinsic_name)| { + benchmarks + .entry(pallet_name) + .or_insert_with(BTreeSet::new) + .insert(extrinsic_name); + }); + + match list_output { + ListOutput::All => { + if !no_csv_header { + println!("pallet,extrinsic"); + } + for (pallet, extrinsics) in benchmarks { + for extrinsic in extrinsics { + println!("{pallet},{extrinsic}"); + } + } + }, + ListOutput::Pallets => { + if !no_csv_header { + println!("pallet"); + }; + for pallet in benchmarks.keys() { + println!("{pallet}"); + } + }, } } diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs index c69ce1765fc9dc490f1e0f40966ca07948b6b523..6dc56c0724eaffde1d70d25ef1161b29e01df0c4 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -19,6 +19,7 @@ mod command; mod writer; use crate::shared::HostInfoParams; +use clap::ValueEnum; use sc_cli::{ WasmExecutionMethod, WasmtimeInstantiationStrategy, DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, DEFAULT_WASM_EXECUTION_METHOD, @@ -31,17 +32,32 @@ fn parse_pallet_name(pallet: &str) -> std::result::Result { Ok(pallet.replace("-", "_")) } +/// List options for available benchmarks. +#[derive(Debug, Clone, Copy, ValueEnum)] +pub enum ListOutput { + /// List all available pallets and extrinsics. + All, + /// List all available pallets only. + Pallets, +} + /// Benchmark the extrinsic weight of FRAME Pallets. #[derive(Debug, clap::Parser)] pub struct PalletCmd { /// Select a FRAME Pallet to benchmark, or `*` for all (in which case `extrinsic` must be `*`). - #[arg(short, long, value_parser = parse_pallet_name, required_unless_present_any = ["list", "json_input"])] + #[arg(short, long, value_parser = parse_pallet_name, required_unless_present_any = ["list", "json_input", "all"], default_value_if("all", "true", Some("*".into())))] pub pallet: Option, /// Select an extrinsic inside the pallet to benchmark, or `*` for all. - #[arg(short, long, required_unless_present_any = ["list", "json_input"])] + #[arg(short, long, required_unless_present_any = ["list", "json_input", "all"], default_value_if("all", "true", Some("*".into())))] pub extrinsic: Option, + /// Run benchmarks for all pallets and extrinsics. + /// + /// This is equivalent to running `--pallet * --extrinsic *`. + #[arg(long)] + pub all: bool, + /// Select how many samples we should take across the variable components. #[arg(short, long, default_value_t = 50)] pub steps: u32, @@ -158,11 +174,15 @@ pub struct PalletCmd { #[arg(long = "db-cache", value_name = "MiB", default_value_t = 1024)] pub database_cache_size: u32, - /// List the benchmarks that match your query rather than running them. + /// List and print available benchmarks in a csv-friendly format. /// - /// When nothing is provided, we list all benchmarks. - #[arg(long)] - pub list: bool, + /// NOTE: `num_args` and `require_equals` are required to allow `--list` + #[arg(long, value_enum, ignore_case = true, num_args = 0..=1, require_equals = true, default_missing_value("All"))] + pub list: Option, + + /// Don't include csv header when listing benchmarks. + #[arg(long, requires("list"))] + pub no_csv_header: bool, /// If enabled, the storage info is not displayed in the output next to the analysis. /// diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs b/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs index 1e5e294acba263aa4f3abf97e4249ce5917d9d42..a044049a0d614e42530f30badee173a63a28f0f5 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs @@ -22,7 +22,11 @@ use core::marker::PhantomData; /// Weight functions for `{{pallet}}`. pub struct WeightInfo(PhantomData); +{{#if (eq pallet "frame_system_extensions")}} +impl frame_system::ExtensionsWeightInfo for WeightInfo { +{{else}} impl {{pallet}}::WeightInfo for WeightInfo { +{{/if}} {{#each benchmarks as |benchmark|}} {{#each benchmark.comments as |comment|}} /// {{comment}} diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs index 9493a693bbed321785bf0d23326d0f68b3efd534..bd4b65d8a2e378d543b54bebd8db4a1c3d378f98 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs @@ -153,8 +153,8 @@ fn map_results( continue } - let pallet_string = String::from_utf8(batch.pallet.clone()).unwrap(); - let instance_string = String::from_utf8(batch.instance.clone()).unwrap(); + let pallet_name = String::from_utf8(batch.pallet.clone()).unwrap(); + let instance_name = String::from_utf8(batch.instance.clone()).unwrap(); let benchmark_data = get_benchmark_data( batch, storage_info, @@ -166,7 +166,7 @@ fn map_results( worst_case_map_values, additional_trie_layers, ); - let pallet_benchmarks = all_benchmarks.entry((pallet_string, instance_string)).or_default(); + let pallet_benchmarks = all_benchmarks.entry((pallet_name, instance_name)).or_default(); pallet_benchmarks.push(benchmark_data); } Ok(all_benchmarks) @@ -571,19 +571,22 @@ pub(crate) fn process_storage_results( let mut prefix_result = result.clone(); let key_info = storage_info_map.get(&prefix); + let pallet_name = match key_info { + Some(k) => String::from_utf8(k.pallet_name.clone()).expect("encoded from string"), + None => "".to_string(), + }; + let storage_name = match key_info { + Some(k) => String::from_utf8(k.storage_name.clone()).expect("encoded from string"), + None => "".to_string(), + }; let max_size = key_info.and_then(|k| k.max_size); let override_pov_mode = match key_info { - Some(StorageInfo { pallet_name, storage_name, .. }) => { - let pallet_name = - String::from_utf8(pallet_name.clone()).expect("encoded from string"); - let storage_name = - String::from_utf8(storage_name.clone()).expect("encoded from string"); - + Some(_) => { // Is there an override for the storage key? - pov_modes.get(&(pallet_name.clone(), storage_name)).or( + pov_modes.get(&(pallet_name.clone(), storage_name.clone())).or( // .. or for the storage prefix? - pov_modes.get(&(pallet_name, "ALL".to_string())).or( + pov_modes.get(&(pallet_name.clone(), "ALL".to_string())).or( // .. or for the benchmark? pov_modes.get(&("ALL".to_string(), "ALL".to_string())), ), @@ -662,15 +665,10 @@ pub(crate) fn process_storage_results( // writes. if !is_prefix_identified { match key_info { - Some(key_info) => { + Some(_) => { let comment = format!( "Storage: `{}::{}` (r:{} w:{})", - String::from_utf8(key_info.pallet_name.clone()) - .expect("encoded from string"), - String::from_utf8(key_info.storage_name.clone()) - .expect("encoded from string"), - reads, - writes, + pallet_name, storage_name, reads, writes, ); comments.push(comment) }, @@ -698,11 +696,7 @@ pub(crate) fn process_storage_results( ) { Some(new_pov) => { let comment = format!( - "Proof: `{}::{}` (`max_values`: {:?}, `max_size`: {:?}, added: {}, mode: `{:?}`)", - String::from_utf8(key_info.pallet_name.clone()) - .expect("encoded from string"), - String::from_utf8(key_info.storage_name.clone()) - .expect("encoded from string"), + "Proof: `{pallet_name}::{storage_name}` (`max_values`: {:?}, `max_size`: {:?}, added: {}, mode: `{:?}`)", key_info.max_values, key_info.max_size, new_pov, @@ -711,13 +705,9 @@ pub(crate) fn process_storage_results( comments.push(comment) }, None => { - let pallet = String::from_utf8(key_info.pallet_name.clone()) - .expect("encoded from string"); - let item = String::from_utf8(key_info.storage_name.clone()) - .expect("encoded from string"); let comment = format!( "Proof: `{}::{}` (`max_values`: {:?}, `max_size`: {:?}, mode: `{:?}`)", - pallet, item, key_info.max_values, key_info.max_size, + pallet_name, storage_name, key_info.max_values, key_info.max_size, used_pov_mode, ); comments.push(comment); diff --git a/substrate/utils/frame/benchmarking-cli/src/storage/write.rs b/substrate/utils/frame/benchmarking-cli/src/storage/write.rs index 65941497bda41f94b1230f73d59b187895cee4d9..4fa56b6e818f8d90103eb080db2e071ba2a240eb 100644 --- a/substrate/utils/frame/benchmarking-cli/src/storage/write.rs +++ b/substrate/utils/frame/benchmarking-cli/src/storage/write.rs @@ -57,7 +57,7 @@ impl StorageCmd { let best_hash = client.usage_info().chain.best_hash; let header = client.header(best_hash)?.ok_or("Header not found")?; let original_root = *header.state_root(); - let trie = DbStateBuilder::::new(storage.clone(), original_root).build(); + let trie = DbStateBuilder::>::new(storage.clone(), original_root).build(); info!("Preparing keys from block {}", best_hash); // Load all KV pairs and randomly shuffle them. @@ -189,7 +189,7 @@ fn convert_tx( /// if `child_info` exist then it means this is a child tree key fn measure_write( db: Arc>, - trie: &DbState, + trie: &DbState>, key: Vec, new_v: Vec, version: StateVersion, @@ -220,7 +220,7 @@ fn measure_write( /// if `child_info` exist then it means this is a child tree key fn check_new_value( db: Arc>, - trie: &DbState, + trie: &DbState>, key: &Vec, new_v: &Vec, version: StateVersion, diff --git a/substrate/utils/frame/frame-utilities-cli/Cargo.toml b/substrate/utils/frame/frame-utilities-cli/Cargo.toml index 3cfb81e8c10f770f8921313da79e1dacc16f0a16..919016b2d8f09f4327ed34e4dc1a7d0fe9b3fe6b 100644 --- a/substrate/utils/frame/frame-utilities-cli/Cargo.toml +++ b/substrate/utils/frame/frame-utilities-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-frame-cli" -version = "4.0.0-dev" +version = "32.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -14,7 +14,7 @@ readme = "README.md" workspace = true [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } frame-support = { path = "../../../frame/support" } frame-system = { path = "../../../frame/system" } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/utils/frame/generate-bags/Cargo.toml b/substrate/utils/frame/generate-bags/Cargo.toml index 4afb2a80b7710beae446c5f95d6f4e8dc727ff9b..2688254bd5ea3e63b1634107d6f15d27c97843e2 100644 --- a/substrate/utils/frame/generate-bags/Cargo.toml +++ b/substrate/utils/frame/generate-bags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "generate-bags" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml b/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml index b35e06879df7ebb493ea09a9d4be2b32b3e6128c..68d4733614c1521c285109aa7d8e3c8e9b8ea188 100644 --- a/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml +++ b/substrate/utils/frame/generate-bags/node-runtime/Cargo.toml @@ -17,4 +17,4 @@ kitchensink-runtime = { path = "../../../../bin/node/runtime" } generate-bags = { path = ".." } # third-party -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } diff --git a/substrate/utils/frame/remote-externalities/Cargo.toml b/substrate/utils/frame/remote-externalities/Cargo.toml index ba0e8e869ccc27e7e12809ac4ac52cfb686f3713..61e0a861ee0ebd847a54c46280d5c3c10fce0ad2 100644 --- a/substrate/utils/frame/remote-externalities/Cargo.toml +++ b/substrate/utils/frame/remote-externalities/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-remote-externalities" -version = "0.10.0-dev" +version = "0.35.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -15,18 +15,19 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["http-client"] } +jsonrpsee = { version = "0.22", features = ["http-client"] } codec = { package = "parity-scale-codec", version = "3.6.1" } -log = "0.4.17" -serde = "1.0.195" +log = { workspace = true, default-features = true } +serde = { workspace = true, default-features = true } sp-core = { path = "../../../primitives/core" } +sp-crypto-hashing = { path = "../../../primitives/crypto/hashing" } sp-state-machine = { path = "../../../primitives/state-machine" } sp-io = { path = "../../../primitives/io" } sp-runtime = { path = "../../../primitives/runtime" } tokio = { version = "1.22.0", features = ["macros", "rt-multi-thread"] } substrate-rpc-client = { path = "../rpc/client" } futures = "0.3" -indicatif = "0.17.3" +indicatif = "0.17.7" spinners = "4.1.0" tokio-retry = "0.3.0" diff --git a/substrate/utils/frame/remote-externalities/src/lib.rs b/substrate/utils/frame/remote-externalities/src/lib.rs index 5c7a36867ff6ea1398daed763505b49e13b4ca03..d49479d9cda955770dc32df4800c7bef70082816 100644 --- a/substrate/utils/frame/remote-externalities/src/lib.rs +++ b/substrate/utils/frame/remote-externalities/src/lib.rs @@ -29,7 +29,6 @@ use jsonrpsee::{ use log::*; use serde::de::DeserializeOwned; use sp_core::{ - hashing::twox_128, hexdisplay::HexDisplay, storage::{ well_known_keys::{is_default_child_storage_key, DEFAULT_CHILD_STORAGE_KEY_PREFIX}, @@ -190,7 +189,8 @@ impl Transport { uri.clone() }; let http_client = HttpClientBuilder::default() - .max_request_body_size(u32::MAX) + .max_request_size(u32::MAX) + .max_response_size(u32::MAX) .request_timeout(std::time::Duration::from_secs(60 * 5)) .build(uri) .map_err(|e| { @@ -999,10 +999,11 @@ where // Then, a few transformation that we want to perform in the online config: let online_config = self.as_online_mut(); - online_config - .pallets - .iter() - .for_each(|p| online_config.hashed_prefixes.push(twox_128(p.as_bytes()).to_vec())); + online_config.pallets.iter().for_each(|p| { + online_config + .hashed_prefixes + .push(sp_crypto_hashing::twox_128(p.as_bytes()).to_vec()) + }); if online_config.child_trie { online_config.hashed_prefixes.push(DEFAULT_CHILD_STORAGE_KEY_PREFIX.to_vec()); @@ -1203,8 +1204,9 @@ where #[cfg(test)] mod test_prelude { pub(crate) use super::*; - pub(crate) use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper, H256 as Hash}; - pub(crate) type Block = RawBlock>; + pub(crate) use sp_runtime::testing::{Block as RawBlock, MockCallU64}; + pub(crate) type UncheckedXt = sp_runtime::generic::UncheckedExtrinsic; + pub(crate) type Block = RawBlock; pub(crate) fn init_logger() { sp_tracing::try_init_simple(); @@ -1262,7 +1264,11 @@ mod tests { #[cfg(all(test, feature = "remote-test"))] mod remote_tests { use super::test_prelude::*; - use std::os::unix::fs::MetadataExt; + use std::{env, os::unix::fs::MetadataExt}; + + fn endpoint() -> String { + env::var("TEST_WS").unwrap_or_else(|_| DEFAULT_HTTP_ENDPOINT.to_string()) + } #[tokio::test] async fn state_version_is_kept_and_can_be_altered() { @@ -1272,6 +1278,7 @@ mod remote_tests { // first, build a snapshot. let ext = Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), pallets: vec!["Proxy".to_owned()], child_trie: false, state_snapshot: Some(SnapshotConfig::new(CACHE)), @@ -1313,6 +1320,7 @@ mod remote_tests { // first, build a snapshot. let ext = Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), pallets: vec!["Proxy".to_owned()], child_trie: false, state_snapshot: Some(SnapshotConfig::new(CACHE)), @@ -1340,6 +1348,7 @@ mod remote_tests { // create an ext with children keys let child_ext = Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), pallets: vec!["Proxy".to_owned()], child_trie: true, state_snapshot: Some(SnapshotConfig::new(CACHE)), @@ -1352,6 +1361,7 @@ mod remote_tests { // create an ext without children keys let ext = Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), pallets: vec!["Proxy".to_owned()], child_trie: false, state_snapshot: Some(SnapshotConfig::new(CACHE)), @@ -1377,6 +1387,7 @@ mod remote_tests { .mode(Mode::OfflineOrElseOnline( OfflineConfig { state_snapshot: SnapshotConfig::new(CACHE) }, OnlineConfig { + transport: endpoint().clone().into(), pallets: vec!["Proxy".to_owned()], child_trie: false, state_snapshot: Some(SnapshotConfig::new(CACHE)), @@ -1418,6 +1429,7 @@ mod remote_tests { init_logger(); Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), pallets: vec!["Proxy".to_owned()], child_trie: false, ..Default::default() @@ -1433,6 +1445,7 @@ mod remote_tests { init_logger(); Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), pallets: vec!["Proxy".to_owned(), "Multisig".to_owned()], child_trie: false, ..Default::default() @@ -1450,6 +1463,7 @@ mod remote_tests { Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), state_snapshot: Some(SnapshotConfig::new(CACHE)), pallets: vec!["Proxy".to_owned()], child_trie: false, @@ -1479,6 +1493,7 @@ mod remote_tests { init_logger(); Builder::::new() .mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), state_snapshot: Some(SnapshotConfig::new(CACHE)), pallets: vec!["Crowdloan".to_owned()], child_trie: true, @@ -1510,7 +1525,7 @@ mod remote_tests { init_logger(); Builder::::new() .mode(Mode::Online(OnlineConfig { - transport: std::option_env!("TEST_WS").unwrap().to_owned().into(), + transport: endpoint().clone().into(), pallets: vec!["Staking".to_owned()], child_trie: false, ..Default::default() @@ -1529,7 +1544,7 @@ mod remote_tests { init_logger(); Builder::::new() .mode(Mode::Online(OnlineConfig { - transport: std::option_env!("TEST_WS").unwrap().to_owned().into(), + transport: endpoint().clone().into(), ..Default::default() })) .build() @@ -1542,9 +1557,10 @@ mod remote_tests { async fn can_fetch_in_parallel() { init_logger(); - let uri = String::from("wss://kusama-bridge-hub-rpc.polkadot.io:443"); - let mut builder = Builder::::new() - .mode(Mode::Online(OnlineConfig { transport: uri.into(), ..Default::default() })); + let mut builder = Builder::::new().mode(Mode::Online(OnlineConfig { + transport: endpoint().clone().into(), + ..Default::default() + })); builder.init_remote_client().await.unwrap(); let at = builder.as_online().at.unwrap(); diff --git a/substrate/utils/frame/rpc/client/Cargo.toml b/substrate/utils/frame/rpc/client/Cargo.toml index 1e8a298726eb8d710f9b78c02628e3da4a0bcb5f..b51e3f44f4e6457ee2f9e12dedace27728b96ac4 100644 --- a/substrate/utils/frame/rpc/client/Cargo.toml +++ b/substrate/utils/frame/rpc/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-rpc-client" -version = "0.10.0-dev" +version = "0.33.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -15,12 +15,12 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.16.2", features = ["ws-client"] } +jsonrpsee = { version = "0.22", features = ["ws-client"] } sc-rpc-api = { path = "../../../../client/rpc-api" } async-trait = "0.1.74" -serde = "1" +serde = { workspace = true, default-features = true } sp-runtime = { path = "../../../../primitives/runtime" } -log = "0.4" +log = { workspace = true, default-features = true } [dev-dependencies] tokio = { version = "1.22.0", features = ["macros", "rt-multi-thread", "sync"] } diff --git a/substrate/utils/frame/rpc/client/src/lib.rs b/substrate/utils/frame/rpc/client/src/lib.rs index a6a667bef5681005c381f8cf171d6185ecf03e51..4f4f4bbef56213f88ecfa6fe848e178d448d5560 100644 --- a/substrate/utils/frame/rpc/client/src/lib.rs +++ b/substrate/utils/frame/rpc/client/src/lib.rs @@ -44,9 +44,9 @@ use std::collections::VecDeque; pub use jsonrpsee::{ core::{ - client::{ClientT, Subscription, SubscriptionClientT}, + client::{ClientT, Error, Subscription, SubscriptionClientT}, params::BatchRequestBuilder, - Error, RpcResult, + RpcResult, }, rpc_params, ws_client::{WsClient, WsClientBuilder}, @@ -61,10 +61,11 @@ pub use sc_rpc_api::{ /// Create a new `WebSocket` connection with shared settings. pub async fn ws_client(uri: impl AsRef) -> Result { WsClientBuilder::default() - .max_request_body_size(u32::MAX) + .max_request_size(u32::MAX) + .max_response_size(u32::MAX) .request_timeout(std::time::Duration::from_secs(60 * 10)) .connection_timeout(std::time::Duration::from_secs(60)) - .max_notifs_per_subscription(1024) + .max_buffer_capacity_per_subscription(1024) .build(uri) .await .map_err(|e| format!("`WsClientBuilder` failed to build: {:?}", e)) @@ -198,11 +199,15 @@ where #[cfg(test)] mod tests { use super::*; - use sp_runtime::testing::{Block as TBlock, ExtrinsicWrapper, Header, H256}; + use sp_runtime::{ + generic::UncheckedExtrinsic, + testing::{Block as TBlock, Header, MockCallU64, H256}, + }; use std::sync::Arc; use tokio::sync::Mutex; - type Block = TBlock>; + type UncheckedXt = UncheckedExtrinsic; + type Block = TBlock; type BlockNumber = u64; type Hash = H256; diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index 6cd99e5a6fedc3e18464e9d3c6da621a223c0fe2..f9a45e21ce13d3690c1efe366e1d0f480f0f9b8f 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-state-trie-migration-rpc" -version = "4.0.0-dev" +version = "27.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,14 +17,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -serde = { version = "1", features = ["derive"] } +serde = { features = ["derive"], workspace = true, default-features = true } sp-core = { path = "../../../../primitives/core" } sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } trie-db = "0.28.0" -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } # Substrate Dependencies sc-client-api = { path = "../../../../client/api" } @@ -32,4 +32,4 @@ sc-rpc-api = { path = "../../../../client/rpc-api" } sp-runtime = { path = "../../../../primitives/runtime" } [dev-dependencies] -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } 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 d1175fdea907ad5f61a4e2515f19b62c29391910..f45258ea593d74c417bcb301c8ca755f0d396945 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 @@ -18,9 +18,9 @@ //! Rpc for state migration. use jsonrpsee::{ - core::{Error as JsonRpseeError, RpcResult}, + core::RpcResult, proc_macros::rpc, - types::error::{CallError, ErrorCode, ErrorObject}, + types::error::{ErrorCode, ErrorObject, ErrorObjectOwned}, }; use sc_rpc_api::DenyUnsafe; use serde::{Deserialize, Serialize}; @@ -112,7 +112,7 @@ where } /// Current state migration status. -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub struct MigrationStatusResult { @@ -168,10 +168,10 @@ where } } -fn error_into_rpc_err(err: impl std::fmt::Display) -> JsonRpseeError { - JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( +fn error_into_rpc_err(err: impl std::fmt::Display) -> ErrorObjectOwned { + ErrorObject::owned( ErrorCode::InternalError.code(), "Error while checking migration state", Some(err.to_string()), - ))) + ) } diff --git a/substrate/utils/frame/rpc/support/Cargo.toml b/substrate/utils/frame/rpc/support/Cargo.toml index 1cc6d8e98b365e5fcfd8bc6004b4d13b8c1a0ae4..2e4bb6a105784fafecd1344a909e69dbcbfa0e3f 100644 --- a/substrate/utils/frame/rpc/support/Cargo.toml +++ b/substrate/utils/frame/rpc/support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-frame-rpc-support" -version = "3.0.0" +version = "29.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -16,15 +16,15 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.16.2", features = ["jsonrpsee-types"] } -serde = "1" +jsonrpsee = { version = "0.22", features = ["jsonrpsee-types"] } +serde = { workspace = true, default-features = true } frame-support = { path = "../../../../frame/support" } sc-rpc-api = { path = "../../../../client/rpc-api" } sp-storage = { path = "../../../../primitives/storage" } [dev-dependencies] scale-info = "2.10.0" -jsonrpsee = { version = "0.16.2", features = ["jsonrpsee-types", "ws-client"] } +jsonrpsee = { version = "0.22", features = ["jsonrpsee-types", "ws-client"] } tokio = "1.22.0" sp-core = { path = "../../../../primitives/core" } sp-runtime = { path = "../../../../primitives/runtime" } diff --git a/substrate/utils/frame/rpc/support/src/lib.rs b/substrate/utils/frame/rpc/support/src/lib.rs index e3ccbd6965893ed81b9698e4ff0b4caebba3830c..8280c46aadf2629c1f2b1efccfa98d2c7a5f5bdf 100644 --- a/substrate/utils/frame/rpc/support/src/lib.rs +++ b/substrate/utils/frame/rpc/support/src/lib.rs @@ -23,7 +23,7 @@ use codec::{DecodeAll, FullCodec, FullEncode}; use core::marker::PhantomData; use frame_support::storage::generator::{StorageDoubleMap, StorageMap, StorageValue}; -use jsonrpsee::core::Error as RpcError; +use jsonrpsee::core::ClientError as RpcError; use sc_rpc_api::state::StateApiClient; use serde::{de::DeserializeOwned, Serialize}; use sp_storage::{StorageData, StorageKey}; @@ -31,7 +31,7 @@ use sp_storage::{StorageData, StorageKey}; /// A typed query on chain state usable from an RPC client. /// /// ```no_run -/// # use jsonrpsee::core::Error as RpcError; +/// # use jsonrpsee::core::ClientError as RpcError; /// # use jsonrpsee::ws_client::WsClientBuilder; /// # use codec::Encode; /// # use frame_support::{construct_runtime, derive_impl, traits::ConstU32}; diff --git a/substrate/utils/frame/rpc/system/Cargo.toml b/substrate/utils/frame/rpc/system/Cargo.toml index 84c3265c93d36e90fda0e6bea0cb261456fe6676..f9a84a01af82f9cf4746e003990e43cb2515a7bc 100644 --- a/substrate/utils/frame/rpc/system/Cargo.toml +++ b/substrate/utils/frame/rpc/system/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-frame-rpc-system" -version = "4.0.0-dev" +version = "28.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -17,9 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.16.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } futures = "0.3.21" -log = "0.4.17" +log = { workspace = true, default-features = true } frame-system-rpc-runtime-api = { path = "../../../../frame/system/rpc/runtime-api" } sc-rpc-api = { path = "../../../../client/rpc-api" } sc-transaction-pool-api = { path = "../../../../client/transaction-pool/api" } diff --git a/substrate/utils/frame/rpc/system/src/lib.rs b/substrate/utils/frame/rpc/system/src/lib.rs index f467a87989048d9cca418ba04c57d363cc05162d..bb0592599b2ad678acd7475e8ed868b4454f2d53 100644 --- a/substrate/utils/frame/rpc/system/src/lib.rs +++ b/substrate/utils/frame/rpc/system/src/lib.rs @@ -23,7 +23,7 @@ use codec::{self, Codec, Decode, Encode}; use jsonrpsee::{ core::{async_trait, RpcResult}, proc_macros::rpc, - types::error::{CallError, ErrorObject}, + types::error::ErrorObject, }; use sc_rpc_api::DenyUnsafe; @@ -103,11 +103,11 @@ where let best = self.client.info().best_hash; let nonce = api.account_nonce(best, account.clone()).map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::RuntimeError.into(), "Unable to query nonce.", Some(e.to_string()), - )) + ) })?; Ok(adjust_nonce(&*self.pool, account, nonce)) } @@ -125,28 +125,28 @@ where let uxt: ::Extrinsic = Decode::decode(&mut &*extrinsic).map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::DecodeError.into(), "Unable to dry run extrinsic", Some(e.to_string()), - )) + ) })?; let api_version = api .api_version::>(best_hash) .map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::RuntimeError.into(), "Unable to dry run extrinsic.", Some(e.to_string()), - )) + ) })? .ok_or_else(|| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::RuntimeError.into(), "Unable to dry run extrinsic.", Some(format!("Could not find `BlockBuilder` api for block `{:?}`.", best_hash)), - )) + ) })?; let result = if api_version < 6 { @@ -154,19 +154,19 @@ where api.apply_extrinsic_before_version_6(best_hash, uxt) .map(legacy::byte_sized_error::convert_to_latest) .map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::RuntimeError.into(), "Unable to dry run extrinsic.", Some(e.to_string()), - )) + ) })? } else { api.apply_extrinsic(best_hash, uxt).map_err(|e| { - CallError::Custom(ErrorObject::owned( + ErrorObject::owned( Error::RuntimeError.into(), "Unable to dry run extrinsic.", Some(e.to_string()), - )) + ) })? }; @@ -216,7 +216,6 @@ mod tests { use assert_matches::assert_matches; use futures::executor::block_on; - use jsonrpsee::{core::Error as JsonRpseeError, types::error::CallError}; use sc_transaction_pool::BasicPool; use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, @@ -274,7 +273,7 @@ mod tests { // when let res = accounts.dry_run(vec![].into(), None).await; - assert_matches!(res, Err(JsonRpseeError::Call(CallError::Custom(e))) => { + assert_matches!(res, Err(e) => { assert!(e.message().contains("RPC call is unsafe to be called externally")); }); } diff --git a/substrate/utils/frame/try-runtime/cli/Cargo.toml b/substrate/utils/frame/try-runtime/cli/Cargo.toml index 20da5c1675e182543d1d463eaa07906d45c3684f..d123fc5f62dc5848dc560f1eca37523435ebb331 100644 --- a/substrate/utils/frame/try-runtime/cli/Cargo.toml +++ b/substrate/utils/frame/try-runtime/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "try-runtime-cli" -version = "0.10.0-dev" +version = "0.38.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -38,12 +38,12 @@ frame-try-runtime = { path = "../../../../frame/try-runtime", optional = true } substrate-rpc-client = { path = "../../rpc/client" } async-trait = "0.1.74" -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } hex = { version = "0.4.3", default-features = false } -log = "0.4.17" +log = { workspace = true, default-features = true } parity-scale-codec = "3.6.1" -serde = "1.0.195" -serde_json = "1.0.111" +serde = { workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } zstd = { version = "0.12.4", default-features = false } [dev-dependencies] diff --git a/substrate/utils/prometheus/Cargo.toml b/substrate/utils/prometheus/Cargo.toml index 252998d94bd1cb79736bbee907b61700b4ec2724..36527ac6183bb921266023320345afb9f6a89246 100644 --- a/substrate/utils/prometheus/Cargo.toml +++ b/substrate/utils/prometheus/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Endpoint to expose Prometheus metrics" name = "substrate-prometheus-endpoint" -version = "0.10.0-dev" +version = "0.17.0" license = "Apache-2.0" authors.workspace = true edition.workspace = true @@ -17,9 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] hyper = { version = "0.14.16", default-features = false, features = ["http1", "server", "tcp"] } -log = "0.4.17" +log = { workspace = true, default-features = true } prometheus = { version = "0.13.0", default-features = false } -thiserror = "1.0" +thiserror = { workspace = true } tokio = { version = "1.22.0", features = ["parking_lot"] } [dev-dependencies] diff --git a/substrate/utils/substrate-bip39/Cargo.toml b/substrate/utils/substrate-bip39/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a46f81ee24d96d666495ee5cea5ca58b415a9cb0 --- /dev/null +++ b/substrate/utils/substrate-bip39/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "substrate-bip39" +version = "0.4.7" +license = "Apache-2.0" +description = "Converting BIP39 entropy to valid Substrate (sr25519) SecretKeys" +documentation = "https://docs.rs/substrate-bip39" +authors.workspace = true +edition.workspace = true +repository.workspace = true + +[dependencies] +hmac = "0.12.1" +pbkdf2 = { version = "0.12.2", default-features = false } +schnorrkel = { version = "0.11.4", default-features = false } +sha2 = { version = "0.10.7", default-features = false } +zeroize = { version = "1.4.3", default-features = false } + +[dev-dependencies] +bip39 = "2.0.0" +rustc-hex = "2.1.0" + +[features] +default = ["std"] +std = [ + "hmac/std", + "pbkdf2/std", + "schnorrkel/std", + "sha2/std", + "zeroize/alloc", + "zeroize/std", +] diff --git a/substrate/utils/substrate-bip39/README.md b/substrate/utils/substrate-bip39/README.md new file mode 100644 index 0000000000000000000000000000000000000000..368d18f406bb6cf36abad1daa4020882c1a2fe73 --- /dev/null +++ b/substrate/utils/substrate-bip39/README.md @@ -0,0 +1,55 @@ +# Substrate BIP39 + +This is a crate for deriving secret keys for Ristretto compressed Ed25519 (should be compatible with Ed25519 at this +time) from BIP39 phrases. + +## Why? + +The natural approach here would be to use the 64-byte seed generated from the BIP39 phrase, and use that to construct +the key. This approach, while reasonable and fairly straight forward to implement, also means we would have to inherit +all the characteristics of seed generation. Since we are breaking compatibility with both BIP32 and BIP44 anyway (which +we are free to do as we are no longer using the Secp256k1 curve), there is also no reason why we should adhere to BIP39 +seed generation from the mnemonic. + +BIP39 seed generation was designed to be compatible with user supplied brain wallet phrases as well as being extensible +to wallets providing their own dictionaries and checksum mechanism. Issues with those two points: + +1. Brain wallets are a horrible idea, simply because humans are bad entropy generators. It's next to impossible to + educate users on how to use that feature in a secure manner. The 2048 rounds of PBKDF2 is a mere inconvenience that + offers no real protection against dictionary attacks for anyone equipped with modern consumer hardware. Brain wallets + have given users false sense of security. _People have lost money_ this way and wallet providers today tend to stick + to CSPRNG supplied dictionary phrases. + +2. Providing own dictionaries felt into the _you ain't gonna need it_ anti-pattern category on day 1. Wallet providers + (be it hardware or software) typically want their products to be compatibile with other wallets so that users can + migrate to their product without having to migrate all their assets. + +To achieve the above phrases have to be precisely encoded in _The One True Canonical Encoding_, for which UTF-8 NFKD was +chosen. This is largely irrelevant (and even ignored) for English phrases, as they encode to basically just ASCII in +virtualy every character encoding known to mankind, but immedietly becomes a problem for dictionaries that do use +non-ASCII characters. Even if the right encoding is used and implemented correctly, there are still [other caveats +present for some non-english dictionaries](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md), +such as normalizing spaces to a canonical form, or making some latin based characters equivalent to their base in +dictionary lookups (eg. Spanish `ñ` and `n` are meant to be interchangeable). Thinking about all of this gives me a +headache, and opens doors for disagreements between buggy implementations, breaking compatibility. + +BIP39 does already provide a form of the mnemonic that is free from all of these issues: the entropy byte array. Since +veryfing the checksum requires that we recover the entropy from which the phrase was generated, no extra work is +actually needed here. Wallet implementators can encode the dictionaries in whatever encoding they find convenient (as +long as they are the standard BIP39 dictionaries), no harm in using UTF-16 string primitives that Java and JavaScript +provide. Since the dictionary is fixed and known, and the checksum is done on the entropy itself, the exact character +encoding used becomes irrelevant, as are the precise codepoints and amount of whitespace around the words. It is thus +much harder to create a buggy implementation. + +PBKDF2 was kept in place, along with the password. Using 24 words (with its 256 bits entropy) makes the extra hashing +redundant (if you could brute force 256 bit entropy, you can also just brute force secret keys), however some users +might be still using 12 word phrases from other applications. There is no good reason to prohibit users from recovering +their old wallets using 12 words that I can see, in which case the extra hashing does provide _some_ protection. +Passwords are also a feature that some power users find useful - particularly for creating a decoy address with a small +balance with empty password, while the funds proper are stored on an address that requires a password to be entered. + +## Why not ditch BIP39 altogether? + +Because there are hardware wallets that use a single phrase for the entire device, and operate multiple accounts on +multiple networks using that. A completely different wordlist would make their life much harder when it comes to +providing future Substrate support. diff --git a/substrate/utils/substrate-bip39/src/lib.rs b/substrate/utils/substrate-bip39/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..3673d20faed68e8897b97040c66d726a8877fd7a --- /dev/null +++ b/substrate/utils/substrate-bip39/src/lib.rs @@ -0,0 +1,232 @@ +// 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)] + +#[cfg(not(feature = "std"))] +extern crate alloc; +#[cfg(not(feature = "std"))] +use alloc::string::String; + +use hmac::Hmac; +use pbkdf2::pbkdf2; +use schnorrkel::keys::MiniSecretKey; +use sha2::Sha512; +use zeroize::Zeroize; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum Error { + InvalidEntropy, +} + +/// `entropy` should be a byte array from a correctly recovered and checksumed BIP39. +/// +/// This function accepts slices of different length for different word lengths: +/// +/// + 16 bytes for 12 words. +/// + 20 bytes for 15 words. +/// + 24 bytes for 18 words. +/// + 28 bytes for 21 words. +/// + 32 bytes for 24 words. +/// +/// Any other length will return an error. +/// +/// `password` is analog to BIP39 seed generation itself, with an empty string being defalt. +pub fn mini_secret_from_entropy(entropy: &[u8], password: &str) -> Result { + let seed = seed_from_entropy(entropy, password)?; + Ok(MiniSecretKey::from_bytes(&seed[..32]).expect("Length is always correct; qed")) +} + +/// Similar to `mini_secret_from_entropy`, except that it provides the 64-byte seed directly. +pub fn seed_from_entropy(entropy: &[u8], password: &str) -> Result<[u8; 64], Error> { + if entropy.len() < 16 || entropy.len() > 32 || entropy.len() % 4 != 0 { + return Err(Error::InvalidEntropy); + } + + let mut salt = String::with_capacity(8 + password.len()); + salt.push_str("mnemonic"); + salt.push_str(password); + + let mut seed = [0u8; 64]; + + pbkdf2::>(entropy, salt.as_bytes(), 2048, &mut seed) + .map_err(|_| Error::InvalidEntropy)?; + + salt.zeroize(); + + Ok(seed) +} + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + + use bip39::{Language, Mnemonic}; + use rustc_hex::FromHex; + + // phrase, entropy, seed, expanded secret_key + // + // ALL SEEDS GENERATED USING "Substrate" PASSWORD! + static VECTORS: &[[&str; 3]] = &[ + [ + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "00000000000000000000000000000000", + "44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453", + ], + [ + "legal winner thank year wave sausage worth useful legal winner thank yellow", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "4313249608fe8ac10fd5886c92c4579007272cb77c21551ee5b8d60b780416850f1e26c1f4b8d88ece681cb058ab66d6182bc2ce5a03181f7b74c27576b5c8bf", + ], + [ + "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", + "80808080808080808080808080808080", + "27f3eb595928c60d5bc91a4d747da40ed236328183046892ed6cd5aa9ae38122acd1183adf09a89839acb1e6eaa7fb563cc958a3f9161248d5a036e0d0af533d", + ], + [ + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "ffffffffffffffffffffffffffffffff", + "227d6256fd4f9ccaf06c45eaa4b2345969640462bbb00c5f51f43cb43418c7a753265f9b1e0c0822c155a9cabc769413ecc14553e135fe140fc50b6722c6b9df", + ], + [ + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "000000000000000000000000000000000000000000000000", + "44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453", + ], + [ + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "cb1d50e14101024a88905a098feb1553d4306d072d7460e167a60ccb3439a6817a0afc59060f45d999ddebc05308714733c9e1e84f30feccddd4ad6f95c8a445", + ], + [ + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "808080808080808080808080808080808080808080808080", + "9ddecf32ce6bee77f867f3c4bb842d1f0151826a145cb4489598fe71ac29e3551b724f01052d1bc3f6d9514d6df6aa6d0291cfdf997a5afdb7b6a614c88ab36a", + ], + [ + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "8971cb290e7117c64b63379c97ed3b5c6da488841bd9f95cdc2a5651ac89571e2c64d391d46e2475e8b043911885457cd23e99a28b5a18535fe53294dc8e1693", + ], + [ + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "0000000000000000000000000000000000000000000000000000000000000000", + "44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453", + ], + [ + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "3037276a5d05fcd7edf51869eb841bdde27c574dae01ac8cfb1ea476f6bea6ef57ab9afe14aea1df8a48f97ae25b37d7c8326e49289efb25af92ba5a25d09ed3", + ], + [ + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "8080808080808080808080808080808080808080808080808080808080808080", + "2c9c6144a06ae5a855453d98c3dea470e2a8ffb78179c2e9eb15208ccca7d831c97ddafe844ab933131e6eb895f675ede2f4e39837bb5769d4e2bc11df58ac42", + ], + [ + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "047e89ef7739cbfe30da0ad32eb1720d8f62441dd4f139b981b8e2d0bd412ed4eb14b89b5098c49db2301d4e7df4e89c21e53f345138e56a5e7d63fae21c5939", + ], + [ + "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic", + "9e885d952ad362caeb4efe34a8e91bd2", + "f4956be6960bc145cdab782e649a5056598fd07cd3f32ceb73421c3da27833241324dc2c8b0a4d847eee457e6d4c5429f5e625ece22abaa6a976e82f1ec5531d", + ], + [ + "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog", + "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b", + "fbcc5229ade0c0ff018cb7a329c5459f91876e4dde2a97ddf03c832eab7f26124366a543f1485479c31a9db0d421bda82d7e1fe562e57f3533cb1733b001d84d", + ], + [ + "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length", + "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c", + "7c60c555126c297deddddd59f8cdcdc9e3608944455824dd604897984b5cc369cad749803bb36eb8b786b570c9cdc8db275dbe841486676a6adf389f3be3f076", + ], + [ + "scheme spot photo card baby mountain device kick cradle pact join borrow", + "c0ba5a8e914111210f2bd131f3d5e08d", + "c12157bf2506526c4bd1b79a056453b071361538e9e2c19c28ba2cfa39b5f23034b974e0164a1e8acd30f5b4c4de7d424fdb52c0116bfc6a965ba8205e6cc121", + ], + [ + "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave", + "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3", + "23766723e970e6b79dec4d5e4fdd627fd27d1ee026eb898feb9f653af01ad22080c6f306d1061656d01c4fe9a14c05f991d2c7d8af8730780de4f94cd99bd819", + ], + [ + "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside", + "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863", + "f4c83c86617cb014d35cd87d38b5ef1c5d5c3d58a73ab779114438a7b358f457e0462c92bddab5a406fe0e6b97c71905cf19f925f356bc673ceb0e49792f4340", + ], + [ + "cat swing flag economy stadium alone churn speed unique patch report train", + "23db8160a31d3e0dca3688ed941adbf3", + "719d4d4de0638a1705bf5237262458983da76933e718b2d64eb592c470f3c5d222e345cc795337bb3da393b94375ff4a56cfcd68d5ea25b577ee9384d35f4246", + ], + [ + "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access", + "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0", + "7ae1291db32d16457c248567f2b101e62c5549d2a64cd2b7605d503ec876d58707a8d663641e99663bc4f6cc9746f4852e75e7e54de5bc1bd3c299c9a113409e", + ], + [ + "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform", + "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad", + "a911a5f4db0940b17ecb79c4dcf9392bf47dd18acaebdd4ef48799909ebb49672947cc15f4ef7e8ef47103a1a91a6732b821bda2c667e5b1d491c54788c69391", + ], + [ + "vessel ladder alter error federal sibling chat ability sun glass valve picture", + "f30f8c1da665478f49b001d94c5fc452", + "4e2314ca7d9eebac6fe5a05a5a8d3546bc891785414d82207ac987926380411e559c885190d641ff7e686ace8c57db6f6e4333c1081e3d88d7141a74cf339c8f", + ], + [ + "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump", + "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05", + "7a83851102849edc5d2a3ca9d8044d0d4f00e5c4a292753ed3952e40808593251b0af1dd3c9ed9932d46e8608eb0b928216a6160bd4fc775a6e6fbd493d7c6b2", + ], + [ + "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold", + "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f", + "938ba18c3f521f19bd4a399c8425b02c716844325b1a65106b9d1593fbafe5e0b85448f523f91c48e331995ff24ae406757cff47d11f240847352b348ff436ed", + ] + ]; + + #[test] + fn vectors_are_correct() { + for vector in VECTORS { + let phrase = vector[0]; + + let expected_entropy: Vec = vector[1].from_hex().unwrap(); + let expected_seed: Vec = vector[2].from_hex().unwrap(); + + let mnemonic = Mnemonic::parse_in(Language::English, phrase).unwrap(); + let seed = seed_from_entropy(&mnemonic.to_entropy(), "Substrate").unwrap(); + let secret = mini_secret_from_entropy(&mnemonic.to_entropy(), "Substrate") + .unwrap() + .to_bytes(); + + assert_eq!( + mnemonic.to_entropy(), + &expected_entropy[..], + "Entropy is incorrect for {}", + phrase + ); + assert_eq!(&seed[..], &expected_seed[..], "Seed is incorrect for {}", phrase); + assert_eq!(&secret[..], &expected_seed[..32], "Secret is incorrect for {}", phrase); + } + } +} diff --git a/substrate/utils/wasm-builder/Cargo.toml b/substrate/utils/wasm-builder/Cargo.toml index f882b85e4736237995c9e3844c1d29e1798214d8..7abd1a202848f6159ef33bb9efe06de61cac797c 100644 --- a/substrate/utils/wasm-builder/Cargo.toml +++ b/substrate/utils/wasm-builder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-wasm-builder" -version = "5.0.0-dev" +version = "17.0.0" authors.workspace = true description = "Utility for building WASM binaries" edition.workspace = true @@ -26,3 +26,4 @@ sp-maybe-compressed-blob = { path = "../../primitives/maybe-compressed-blob" } filetime = "0.2.16" wasm-opt = "0.116" parity-wasm = "0.45" +polkavm-linker = { workspace = true } diff --git a/substrate/utils/wasm-builder/src/builder.rs b/substrate/utils/wasm-builder/src/builder.rs index 9c1655d85623b3c0b8568699b923af9390837c16..d2aaff448bc5fdc2f2c1cb73774e0d7603ae90db 100644 --- a/substrate/utils/wasm-builder/src/builder.rs +++ b/substrate/utils/wasm-builder/src/builder.rs @@ -21,6 +21,8 @@ use std::{ process, }; +use crate::RuntimeTarget; + /// Returns the manifest dir from the `CARGO_MANIFEST_DIR` env. fn get_manifest_dir() -> PathBuf { env::var("CARGO_MANIFEST_DIR") @@ -49,6 +51,8 @@ impl WasmBuilderSelectProject { project_cargo_toml: get_manifest_dir().join("Cargo.toml"), features_to_enable: Vec::new(), disable_runtime_version_section_check: false, + export_heap_base: false, + import_memory: false, } } @@ -65,6 +69,8 @@ impl WasmBuilderSelectProject { project_cargo_toml: path, features_to_enable: Vec::new(), disable_runtime_version_section_check: false, + export_heap_base: false, + import_memory: false, }) } else { Err("Project path must point to the `Cargo.toml` of the project") @@ -97,6 +103,11 @@ pub struct WasmBuilder { features_to_enable: Vec, /// Should the builder not check that the `runtime_version` section exists in the wasm binary? disable_runtime_version_section_check: bool, + + /// Whether `__heap_base` should be exported (WASM-only). + export_heap_base: bool, + /// Whether `--import-memory` should be added to the link args (WASM-only). + import_memory: bool, } impl WasmBuilder { @@ -109,7 +120,7 @@ impl WasmBuilder { /// /// This adds `-Clink-arg=--export=__heap_base` to `RUST_FLAGS`. pub fn export_heap_base(mut self) -> Self { - self.rust_flags.push("-Clink-arg=--export=__heap_base".into()); + self.export_heap_base = true; self } @@ -127,7 +138,7 @@ impl WasmBuilder { /// /// This adds `-C link-arg=--import-memory` to `RUST_FLAGS`. pub fn import_memory(mut self) -> Self { - self.rust_flags.push("-C link-arg=--import-memory".into()); + self.import_memory = true; self } @@ -159,7 +170,18 @@ impl WasmBuilder { } /// Build the WASM binary. - pub fn build(self) { + pub fn build(mut self) { + let target = crate::runtime_target(); + if target == RuntimeTarget::Wasm { + if self.export_heap_base { + self.rust_flags.push("-Clink-arg=--export=__heap_base".into()); + } + + if self.import_memory { + self.rust_flags.push("-C link-arg=--import-memory".into()); + } + } + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("`OUT_DIR` is set by cargo!")); let file_path = out_dir.join(self.file_name.clone().unwrap_or_else(|| "wasm_binary.rs".into())); @@ -175,6 +197,7 @@ impl WasmBuilder { } build_project( + target, file_path, self.project_cargo_toml, self.rust_flags.into_iter().map(|f| format!("{} ", f)).collect(), @@ -248,6 +271,7 @@ fn generate_rerun_if_changed_instructions() { /// `check_for_runtime_version_section` - Should the wasm binary be checked for the /// `runtime_version` section? fn build_project( + target: RuntimeTarget, file_name: PathBuf, project_cargo_toml: PathBuf, default_rustflags: String, @@ -255,7 +279,7 @@ fn build_project( wasm_binary_name: Option, check_for_runtime_version_section: bool, ) { - let cargo_cmd = match crate::prerequisites::check() { + let cargo_cmd = match crate::prerequisites::check(target) { Ok(cmd) => cmd, Err(err_msg) => { eprintln!("{}", err_msg); @@ -264,6 +288,7 @@ fn build_project( }; let (wasm_binary, bloaty) = crate::wasm_project::create_and_compile( + target, &project_cargo_toml, &default_rustflags, cargo_cmd, diff --git a/substrate/utils/wasm-builder/src/lib.rs b/substrate/utils/wasm-builder/src/lib.rs index ec85fd1ffddbf8f9aa54aec0e840830c99861a11..5cde48c0950b341cd36f3a5594620be683c799ea 100644 --- a/substrate/utils/wasm-builder/src/lib.rs +++ b/substrate/utils/wasm-builder/src/lib.rs @@ -113,6 +113,7 @@ //! wasm32-unknown-unknown --toolchain nightly-2020-02-20`. use std::{ + collections::BTreeSet, env, fs, io::BufRead, path::{Path, PathBuf}, @@ -164,6 +165,9 @@ const WASM_BUILD_WORKSPACE_HINT: &str = "WASM_BUILD_WORKSPACE_HINT"; /// Environment variable to set whether we'll build `core`/`std`. const WASM_BUILD_STD: &str = "WASM_BUILD_STD"; +/// The target to use for the runtime. Valid values are `wasm` (default) or `riscv`. +const RUNTIME_TARGET: &str = "SUBSTRATE_RUNTIME_TARGET"; + /// Write to the given `file` if the `content` is different. fn write_file_if_changed(file: impl AsRef, content: impl AsRef) { if fs::read_to_string(file.as_ref()).ok().as_deref() != Some(content.as_ref()) { @@ -185,7 +189,7 @@ fn copy_file_if_changed(src: PathBuf, dst: PathBuf) { } /// Get a cargo command that should be used to invoke the compilation. -fn get_cargo_command() -> CargoCommand { +fn get_cargo_command(target: RuntimeTarget) -> CargoCommand { let env_cargo = CargoCommand::new(&env::var("CARGO").expect("`CARGO` env variable is always set by cargo")); let default_cargo = CargoCommand::new("cargo"); @@ -196,35 +200,33 @@ fn get_cargo_command() -> CargoCommand { wasm_toolchain.map(|t| CargoCommand::new_with_args("rustup", &["run", &t, "cargo"])) { cmd - } else if env_cargo.supports_substrate_wasm_env() { + } else if env_cargo.supports_substrate_runtime_env(target) { env_cargo - } else if default_cargo.supports_substrate_wasm_env() { + } else if default_cargo.supports_substrate_runtime_env(target) { default_cargo } else { // If no command before provided us with a cargo that supports our Substrate wasm env, we // try to search one with rustup. If that fails as well, we return the default cargo and let // the prequisities check fail. - get_rustup_command().unwrap_or(default_cargo) + get_rustup_command(target).unwrap_or(default_cargo) } } -/// Get the newest rustup command that supports our Substrate wasm env. +/// Get the newest rustup command that supports compiling a runtime. /// /// Stable versions are always favored over nightly versions even if the nightly versions are /// newer. -fn get_rustup_command() -> Option { - let host = format!("-{}", env::var("HOST").expect("`HOST` is always set by cargo")); - +fn get_rustup_command(target: RuntimeTarget) -> Option { let output = Command::new("rustup").args(&["toolchain", "list"]).output().ok()?.stdout; let lines = output.as_slice().lines(); let mut versions = Vec::new(); for line in lines.filter_map(|l| l.ok()) { - let rustup_version = line.trim_end_matches(&host); - + // Split by a space to get rid of e.g. " (default)" at the end. + let rustup_version = line.split(" ").next().unwrap(); let cmd = CargoCommand::new_with_args("rustup", &["run", &rustup_version, "cargo"]); - if !cmd.supports_substrate_wasm_env() { + if !cmd.supports_substrate_runtime_env(target) { continue } @@ -247,22 +249,26 @@ struct CargoCommand { program: String, args: Vec, version: Option, + target_list: Option>, } impl CargoCommand { fn new(program: &str) -> Self { let version = Self::extract_version(program, &[]); + let target_list = Self::extract_target_list(program, &[]); - CargoCommand { program: program.into(), args: Vec::new(), version } + CargoCommand { program: program.into(), args: Vec::new(), version, target_list } } fn new_with_args(program: &str, args: &[&str]) -> Self { let version = Self::extract_version(program, args); + let target_list = Self::extract_target_list(program, args); CargoCommand { program: program.into(), args: args.iter().map(ToString::to_string).collect(), version, + target_list, } } @@ -283,6 +289,23 @@ impl CargoCommand { Version::extract(&version) } + fn extract_target_list(program: &str, args: &[&str]) -> Option> { + // This is technically an unstable option, but we don't care because we only need this + // to build RISC-V runtimes, and those currently require a specific nightly toolchain + // anyway, so it's totally fine for this to fail in other cases. + let list = Command::new(program) + .args(args) + .args(&["rustc", "-Z", "unstable-options", "--print", "target-list"]) + // Make sure if we're called from within a `build.rs` the host toolchain won't override + // a rustup toolchain we've picked. + .env_remove("RUSTC") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok())?; + + Some(list.trim().split("\n").map(ToString::to_string).collect()) + } + /// Returns the version of this cargo command or `None` if it failed to extract the version. fn version(&self) -> Option { self.version @@ -294,12 +317,29 @@ impl CargoCommand { env::var("RUSTC_BOOTSTRAP").is_ok() } + /// Check if the supplied cargo command supports our runtime environment. + fn supports_substrate_runtime_env(&self, target: RuntimeTarget) -> bool { + match target { + RuntimeTarget::Wasm => self.supports_substrate_runtime_env_wasm(), + RuntimeTarget::Riscv => self.supports_substrate_runtime_env_riscv(), + } + } + + /// Check if the supplied cargo command supports our RISC-V runtime environment. + fn supports_substrate_runtime_env_riscv(&self) -> bool { + let Some(target_list) = self.target_list.as_ref() else { return false }; + // This is our custom target which currently doesn't exist on any upstream toolchain, + // so if it exists it's guaranteed to be our custom toolchain and have have everything + // we need, so any further version checks are unnecessary at this point. + target_list.contains("riscv32ema-unknown-none-elf") + } + /// Check if the supplied cargo command supports our Substrate wasm environment. /// /// This means that either the cargo version is at minimum 1.68.0 or this is a nightly cargo. /// /// Assumes that cargo version matches the rustc version. - fn supports_substrate_wasm_env(&self) -> bool { + fn supports_substrate_runtime_env_wasm(&self) -> bool { // `RUSTC_BOOTSTRAP` tells a stable compiler to behave like a nightly. So, when this env // variable is set, we can assume that whatever rust compiler we have, it is a nightly // compiler. For "more" information, see: @@ -365,5 +405,48 @@ fn get_bool_environment_variable(name: &str) -> Option { /// Returns whether we need to also compile the standard library when compiling the runtime. fn build_std_required() -> bool { - crate::get_bool_environment_variable(crate::WASM_BUILD_STD).unwrap_or(true) + let default = runtime_target() == RuntimeTarget::Wasm; + + crate::get_bool_environment_variable(crate::WASM_BUILD_STD).unwrap_or(default) +} + +#[derive(Copy, Clone, PartialEq, Eq)] +enum RuntimeTarget { + Wasm, + Riscv, +} + +impl RuntimeTarget { + fn rustc_target(self) -> &'static str { + match self { + RuntimeTarget::Wasm => "wasm32-unknown-unknown", + RuntimeTarget::Riscv => "riscv32ema-unknown-none-elf", + } + } + + fn build_subdirectory(self) -> &'static str { + // Keep the build directories separate so that when switching between + // the targets we won't trigger unnecessary rebuilds. + match self { + RuntimeTarget::Wasm => "wbuild", + RuntimeTarget::Riscv => "rbuild", + } + } +} + +fn runtime_target() -> RuntimeTarget { + let Some(value) = env::var_os(RUNTIME_TARGET) else { + return RuntimeTarget::Wasm; + }; + + if value == "wasm" { + RuntimeTarget::Wasm + } else if value == "riscv" { + RuntimeTarget::Riscv + } else { + build_helper::warning!( + "the '{RUNTIME_TARGET}' environment variable has an invalid value; it must be either 'wasm' or 'riscv'" + ); + std::process::exit(1); + } } diff --git a/substrate/utils/wasm-builder/src/prerequisites.rs b/substrate/utils/wasm-builder/src/prerequisites.rs index 99eb6ee1f18fc325fab0ce30b1a4c706da6faeb2..22caf8950637960310c62eb98c5358de55c1662d 100644 --- a/substrate/utils/wasm-builder/src/prerequisites.rs +++ b/substrate/utils/wasm-builder/src/prerequisites.rs @@ -15,14 +15,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{write_file_if_changed, CargoCommand, CargoCommandVersioned}; +use crate::{write_file_if_changed, CargoCommand, CargoCommandVersioned, RuntimeTarget}; use console::style; -use std::{fs, path::Path}; +use std::{ + fs, + path::{Path, PathBuf}, + process::Command, +}; + use tempfile::tempdir; -/// Print an error message. -fn print_error_message(message: &str) -> String { +/// Colorizes an error message, if color output is enabled. +fn colorize_error_message(message: &str) -> String { if super::color_output_enabled() { style(message).red().bold().to_string() } else { @@ -30,123 +35,177 @@ fn print_error_message(message: &str) -> String { } } +/// Colorizes an auxiliary message, if color output is enabled. +fn colorize_aux_message(message: &str) -> String { + if super::color_output_enabled() { + style(message).yellow().bold().to_string() + } else { + message.into() + } +} + /// Checks that all prerequisites are installed. /// /// Returns the versioned cargo command on success. -pub(crate) fn check() -> Result { - let cargo_command = crate::get_cargo_command(); - - if !cargo_command.supports_substrate_wasm_env() { - return Err(print_error_message( - "Cannot compile the WASM runtime: no compatible Rust compiler found!\n\ - Install at least Rust 1.68.0 or a recent nightly version.", - )) - } +pub(crate) fn check(target: RuntimeTarget) -> Result { + let cargo_command = crate::get_cargo_command(target); + match target { + RuntimeTarget::Wasm => { + if !cargo_command.supports_substrate_runtime_env(target) { + return Err(colorize_error_message( + "Cannot compile a WASM runtime: no compatible Rust compiler found!\n\ + Install at least Rust 1.68.0 or a recent nightly version.", + )); + } - check_wasm_toolchain_installed(cargo_command) + check_wasm_toolchain_installed(cargo_command) + }, + RuntimeTarget::Riscv => { + if !cargo_command.supports_substrate_runtime_env(target) { + return Err(colorize_error_message( + "Cannot compile a RISC-V runtime: no compatible Rust compiler found!\n\ + Install a toolchain from here and try again: https://github.com/paritytech/rustc-rv32e-toolchain/", + )); + } + + let dummy_crate = DummyCrate::new(&cargo_command, target); + let version = dummy_crate.get_rustc_version(); + Ok(CargoCommandVersioned::new(cargo_command, version)) + }, + } } -/// Creates a minimal dummy crate at the given path and returns the manifest path. -fn create_minimal_crate(project_dir: &Path) -> std::path::PathBuf { - fs::create_dir_all(project_dir.join("src")).expect("Creating src dir does not fail; qed"); - - let manifest_path = project_dir.join("Cargo.toml"); - write_file_if_changed( - &manifest_path, - r#" - [package] - name = "wasm-test" - version = "1.0.0" - edition = "2021" - - [workspace] - "#, - ); - - write_file_if_changed(project_dir.join("src/main.rs"), "fn main() {}"); - manifest_path +struct DummyCrate<'a> { + cargo_command: &'a CargoCommand, + temp: tempfile::TempDir, + manifest_path: PathBuf, + target: RuntimeTarget, } -fn check_wasm_toolchain_installed( - cargo_command: CargoCommand, -) -> Result { - let temp = tempdir().expect("Creating temp dir does not fail; qed"); - let manifest_path = create_minimal_crate(temp.path()).display().to_string(); +impl<'a> DummyCrate<'a> { + /// Creates a minimal dummy crate. + fn new(cargo_command: &'a CargoCommand, target: RuntimeTarget) -> Self { + let temp = tempdir().expect("Creating temp dir does not fail; qed"); + let project_dir = temp.path(); + fs::create_dir_all(project_dir.join("src")).expect("Creating src dir does not fail; qed"); + + let manifest_path = project_dir.join("Cargo.toml"); + write_file_if_changed( + &manifest_path, + r#" + [package] + name = "dummy-crate" + version = "1.0.0" + edition = "2021" + + [workspace] + "#, + ); + + write_file_if_changed(project_dir.join("src/main.rs"), "fn main() {}"); + DummyCrate { cargo_command, temp, manifest_path, target } + } - let prepare_command = |subcommand| { - let mut cmd = cargo_command.command(); + fn prepare_command(&self, subcommand: &str) -> Command { + let mut cmd = self.cargo_command.command(); // Chdir to temp to avoid including project's .cargo/config.toml // by accident - it can happen in some CI environments. - cmd.current_dir(&temp); - cmd.args(&[ - subcommand, - "--target=wasm32-unknown-unknown", - "--manifest-path", - &manifest_path, - ]); + cmd.current_dir(&self.temp); + cmd.arg(subcommand) + .arg(format!("--target={}", self.target.rustc_target())) + .args(&["--manifest-path", &self.manifest_path.display().to_string()]); if super::color_output_enabled() { cmd.arg("--color=always"); } // manually set the `CARGO_TARGET_DIR` to prevent a cargo deadlock - let target_dir = temp.path().join("target").display().to_string(); + let target_dir = self.temp.path().join("target").display().to_string(); cmd.env("CARGO_TARGET_DIR", &target_dir); // Make sure the host's flags aren't used here, e.g. if an alternative linker is specified // in the RUSTFLAGS then the check we do here will break unless we clear these. cmd.env_remove("CARGO_ENCODED_RUSTFLAGS"); cmd.env_remove("RUSTFLAGS"); + // Make sure if we're called from within a `build.rs` the host toolchain won't override a + // rustup toolchain we've picked. + cmd.env_remove("RUSTC"); cmd - }; + } - let err_msg = - print_error_message("Rust WASM toolchain is not properly installed; please install it!"); - let build_result = prepare_command("build").output().map_err(|_| err_msg.clone())?; - if !build_result.status.success() { - return match String::from_utf8(build_result.stderr) { - Ok(ref err) if err.contains("the `wasm32-unknown-unknown` target may not be installed") => - Err(print_error_message("Cannot compile the WASM runtime: the `wasm32-unknown-unknown` target is not installed!\n\ - You can install it with `rustup target add wasm32-unknown-unknown` if you're using `rustup`.")), + fn get_rustc_version(&self) -> String { + let mut run_cmd = self.prepare_command("rustc"); + run_cmd.args(&["-q", "--", "--version"]); + run_cmd + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .unwrap_or_else(|| "unknown rustc version".into()) + } - // Apparently this can happen when we're running on a non Tier 1 platform. - Ok(ref err) if err.contains("linker `rust-lld` not found") => - Err(print_error_message("Cannot compile the WASM runtime: `rust-lld` not found!")), + fn get_sysroot(&self) -> Option { + let mut sysroot_cmd = self.prepare_command("rustc"); + sysroot_cmd.args(&["-q", "--", "--print", "sysroot"]); + sysroot_cmd.output().ok().and_then(|o| String::from_utf8(o.stdout).ok()) + } - Ok(ref err) => Err(format!( - "{}\n\n{}\n{}\n{}{}\n", - err_msg, - style("Further error information:").yellow().bold(), - style("-".repeat(60)).yellow().bold(), - err, - style("-".repeat(60)).yellow().bold(), - )), - - Err(_) => Err(err_msg), - }; + fn get_toolchain(&self) -> Option { + let sysroot = self.get_sysroot()?; + Path::new(sysroot.trim()) + .file_name() + .and_then(|s| s.to_str()) + .map(|s| s.to_string()) } - let mut run_cmd = prepare_command("rustc"); - run_cmd.args(&["-q", "--", "--version"]); + fn try_build(&self) -> Result<(), Option> { + let Ok(result) = self.prepare_command("build").output() else { return Err(None) }; + if !result.status.success() { + return Err(Some(String::from_utf8_lossy(&result.stderr).into())); + } + Ok(()) + } +} - let version = run_cmd - .output() - .ok() - .and_then(|o| String::from_utf8(o.stdout).ok()) - .unwrap_or_else(|| "unknown rustc version".into()); +fn check_wasm_toolchain_installed( + cargo_command: CargoCommand, +) -> Result { + let dummy_crate = DummyCrate::new(&cargo_command, RuntimeTarget::Wasm); + + if let Err(error) = dummy_crate.try_build() { + let toolchain = dummy_crate.get_toolchain().unwrap_or("".to_string()); + let basic_error_message = colorize_error_message( + &format!("Rust WASM target for toolchain {toolchain} is not properly installed; please install it!") + ); + return match error { + None => Err(basic_error_message), + Some(error) if error.contains("the `wasm32-unknown-unknown` target may not be installed") => { + Err(colorize_error_message(&format!("Cannot compile the WASM runtime: the `wasm32-unknown-unknown` target is not installed!\n\ + You can install it with `rustup target add wasm32-unknown-unknown --toolchain {toolchain}` if you're using `rustup`."))) + }, + // Apparently this can happen when we're running on a non Tier 1 platform. + Some(ref error) if error.contains("linker `rust-lld` not found") => + Err(colorize_error_message("Cannot compile the WASM runtime: `rust-lld` not found!")), + Some(error) => Err(format!( + "{}\n\n{}\n{}\n{}{}\n", + basic_error_message, + colorize_aux_message("Further error information:"), + colorize_aux_message(&"-".repeat(60)), + error, + colorize_aux_message(&"-".repeat(60)), + )) + } + } + let version = dummy_crate.get_rustc_version(); if crate::build_std_required() { - let mut sysroot_cmd = prepare_command("rustc"); - sysroot_cmd.args(&["-q", "--", "--print", "sysroot"]); - if let Some(sysroot) = - sysroot_cmd.output().ok().and_then(|o| String::from_utf8(o.stdout).ok()) - { + if let Some(sysroot) = dummy_crate.get_sysroot() { let src_path = Path::new(sysroot.trim()).join("lib").join("rustlib").join("src").join("rust"); if !src_path.exists() { - return Err(print_error_message( - "Cannot compile the WASM runtime: no standard library sources found!\n\ - You can install them with `rustup component add rust-src` if you're using `rustup`.", + let toolchain = dummy_crate.get_toolchain().unwrap_or("".to_string()); + return Err(colorize_error_message( + &format!("Cannot compile the WASM runtime: no standard library sources found at {}!\n\ + You can install them with `rustup component add rust-src --toolchain {toolchain}` if you're using `rustup`.", src_path.display()), )) } } diff --git a/substrate/utils/wasm-builder/src/wasm_project.rs b/substrate/utils/wasm-builder/src/wasm_project.rs index ded6b2188b0b35c890ca9ad62ac658ee786ab553..9ffb5c72fd9593f60dfddb2ad6333a5d9887330b 100644 --- a/substrate/utils/wasm-builder/src/wasm_project.rs +++ b/substrate/utils/wasm-builder/src/wasm_project.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{write_file_if_changed, CargoCommandVersioned, OFFLINE}; +use crate::{write_file_if_changed, CargoCommandVersioned, RuntimeTarget, OFFLINE}; use build_helper::rerun_if_changed; use cargo_metadata::{DependencyKind, Metadata, MetadataCommand}; @@ -112,6 +112,7 @@ fn crate_metadata(cargo_manifest: &Path) -> Metadata { /// /// The path to the compact runtime binary and the bloaty runtime binary. pub(crate) fn create_and_compile( + target: RuntimeTarget, project_cargo_toml: &Path, default_rustflags: &str, cargo_cmd: CargoCommandVersioned, @@ -120,11 +121,12 @@ pub(crate) fn create_and_compile( check_for_runtime_version_section: bool, ) -> (Option, WasmBinaryBloaty) { let runtime_workspace_root = get_wasm_workspace_root(); - let runtime_workspace = runtime_workspace_root.join("wbuild"); + let runtime_workspace = runtime_workspace_root.join(target.build_subdirectory()); let crate_metadata = crate_metadata(project_cargo_toml); let project = create_project( + target, project_cargo_toml, &runtime_workspace, &crate_metadata, @@ -132,13 +134,58 @@ pub(crate) fn create_and_compile( features_to_enable, ); - let build_config = BuildConfiguration::detect(&project); + let build_config = BuildConfiguration::detect(target, &project); // Build the bloaty runtime blob - build_bloaty_blob(&build_config.blob_build_profile, &project, default_rustflags, cargo_cmd); + let raw_blob_path = build_bloaty_blob( + target, + &build_config.blob_build_profile, + &project, + default_rustflags, + cargo_cmd, + ); + + let (final_blob_binary, bloaty_blob_binary) = match target { + RuntimeTarget::Wasm => compile_wasm( + project_cargo_toml, + &project, + bloaty_blob_out_name_override, + check_for_runtime_version_section, + &build_config, + ), + RuntimeTarget::Riscv => { + let out_name = bloaty_blob_out_name_override + .unwrap_or_else(|| get_blob_name(target, project_cargo_toml)); + let out_path = project.join(format!("{out_name}.polkavm")); + fs::copy(raw_blob_path, &out_path).expect("copying the runtime blob should never fail"); + (None, WasmBinaryBloaty(out_path)) + }, + }; + + generate_rerun_if_changed_instructions( + project_cargo_toml, + &project, + &runtime_workspace, + final_blob_binary.as_ref(), + &bloaty_blob_binary, + ); + if let Err(err) = adjust_mtime(&bloaty_blob_binary, final_blob_binary.as_ref()) { + build_helper::warning!("Error while adjusting the mtime of the blob binaries: {}", err) + } + + (final_blob_binary, bloaty_blob_binary) +} + +fn compile_wasm( + project_cargo_toml: &Path, + project: &Path, + bloaty_blob_out_name_override: Option, + check_for_runtime_version_section: bool, + build_config: &BuildConfiguration, +) -> (Option, WasmBinaryBloaty) { // Get the name of the bloaty runtime blob. - let bloaty_blob_default_name = get_blob_name(project_cargo_toml); + let bloaty_blob_default_name = get_blob_name(RuntimeTarget::Wasm, project_cargo_toml); let bloaty_blob_out_name = bloaty_blob_out_name_override.unwrap_or_else(|| bloaty_blob_default_name.clone()); @@ -183,19 +230,6 @@ pub(crate) fn create_and_compile( }); let final_blob_binary = compact_compressed_blob_path.or(compact_blob_path); - - generate_rerun_if_changed_instructions( - project_cargo_toml, - &project, - &runtime_workspace, - final_blob_binary.as_ref(), - &bloaty_blob_binary, - ); - - if let Err(err) = adjust_mtime(&bloaty_blob_binary, final_blob_binary.as_ref()) { - build_helper::warning!("Error while adjusting the mtime of the blob binaries: {}", err) - } - (final_blob_binary, bloaty_blob_binary) } @@ -314,8 +348,12 @@ fn get_crate_name(cargo_manifest: &Path) -> String { } /// Returns the name for the blob binary. -fn get_blob_name(cargo_manifest: &Path) -> String { - get_crate_name(cargo_manifest).replace('-', "_") +fn get_blob_name(target: RuntimeTarget, cargo_manifest: &Path) -> String { + let crate_name = get_crate_name(cargo_manifest); + match target { + RuntimeTarget::Wasm => crate_name.replace('-', "_"), + RuntimeTarget::Riscv => crate_name, + } } /// Returns the root path of the wasm workspace. @@ -336,6 +374,7 @@ fn get_wasm_workspace_root() -> PathBuf { } fn create_project_cargo_toml( + target: RuntimeTarget, wasm_workspace: &Path, workspace_root_path: &Path, crate_name: &str, @@ -396,17 +435,18 @@ fn create_project_cargo_toml( } let mut package = Table::new(); - package.insert("name".into(), format!("{}-wasm", crate_name).into()); + package.insert("name".into(), format!("{}-blob", crate_name).into()); package.insert("version".into(), "1.0.0".into()); package.insert("edition".into(), "2021".into()); wasm_workspace_toml.insert("package".into(), package.into()); - let mut lib = Table::new(); - lib.insert("name".into(), wasm_binary.into()); - lib.insert("crate-type".into(), vec!["cdylib".to_string()].into()); - - wasm_workspace_toml.insert("lib".into(), lib.into()); + if target == RuntimeTarget::Wasm { + let mut lib = Table::new(); + lib.insert("name".into(), wasm_binary.into()); + lib.insert("crate-type".into(), vec!["cdylib".to_string()].into()); + wasm_workspace_toml.insert("lib".into(), lib.into()); + } let mut dependencies = Table::new(); @@ -422,6 +462,18 @@ fn create_project_cargo_toml( wasm_workspace_toml.insert("workspace".into(), Table::new().into()); + if target == RuntimeTarget::Riscv { + // This dependency currently doesn't compile under RISC-V, so patch it with our own fork. + // + // TODO: Remove this once a new version of `bitvec` (which uses a new version of `radium` + // which doesn't have this problem) is released on crates.io. + let patch = toml::toml! { + [crates-io] + radium = { git = "https://github.com/paritytech/radium-0.7-fork.git", rev = "a5da15a15c90fd169d661d206cf0db592487f52b" } + }; + wasm_workspace_toml.insert("patch".into(), patch.into()); + } + write_file_if_changed( wasm_workspace.join("Cargo.toml"), toml::to_string_pretty(&wasm_workspace_toml).expect("Wasm workspace toml is valid; qed"), @@ -527,6 +579,7 @@ fn has_runtime_wasm_feature_declared( /// /// The path to the created wasm project. fn create_project( + target: RuntimeTarget, project_cargo_toml: &Path, wasm_workspace: &Path, crate_metadata: &Metadata, @@ -535,7 +588,7 @@ fn create_project( ) -> PathBuf { let crate_name = get_crate_name(project_cargo_toml); let crate_path = project_cargo_toml.parent().expect("Parent path exists; qed"); - let wasm_binary = get_blob_name(project_cargo_toml); + let wasm_binary = get_blob_name(target, project_cargo_toml); let wasm_project_folder = wasm_workspace.join(&crate_name); fs::create_dir_all(wasm_project_folder.join("src")) @@ -552,6 +605,7 @@ fn create_project( enabled_features.extend(features_to_enable.into_iter()); create_project_cargo_toml( + target, &wasm_project_folder, workspace_root_path, &crate_name, @@ -560,10 +614,20 @@ fn create_project( enabled_features.into_iter(), ); - write_file_if_changed( - wasm_project_folder.join("src/lib.rs"), - "#![no_std] pub use wasm_project::*;", - ); + match target { + RuntimeTarget::Wasm => { + write_file_if_changed( + wasm_project_folder.join("src/lib.rs"), + "#![no_std] pub use wasm_project::*;", + ); + }, + RuntimeTarget::Riscv => { + write_file_if_changed( + wasm_project_folder.join("src/main.rs"), + "#![no_std] #![no_main] pub use wasm_project::*;", + ); + }, + } if let Some(crate_lock_file) = find_cargo_lock(project_cargo_toml) { // Use the `Cargo.lock` of the main project. @@ -641,14 +705,15 @@ impl BuildConfiguration { /// # Note /// /// Can be overriden by setting [`crate::WASM_BUILD_TYPE_ENV`]. - fn detect(wasm_project: &Path) -> Self { + fn detect(target: RuntimeTarget, wasm_project: &Path) -> Self { let (name, overriden) = if let Ok(name) = env::var(crate::WASM_BUILD_TYPE_ENV) { (name, true) } else { // First go backwards to the beginning of the target directory. - // Then go forwards to find the "wbuild" directory. + // Then go forwards to find the build subdirectory. // We need to go backwards first because when starting from the root there - // might be a chance that someone has a "wbuild" directory somewhere in the path. + // might be a chance that someone has a directory somewhere in the path with the same + // name. let name = wasm_project .components() .rev() @@ -656,9 +721,9 @@ impl BuildConfiguration { .collect::>() .iter() .rev() - .take_while(|c| c.as_os_str() != "wbuild") + .take_while(|c| c.as_os_str() != target.build_subdirectory()) .last() - .expect("We put the wasm project within a `target/.../wbuild` path; qed") + .expect("We put the runtime project within a `target/.../[rw]build` path; qed") .as_os_str() .to_str() .expect("All our profile directory names are ascii; qed") @@ -711,22 +776,34 @@ fn offline_build() -> bool { /// Build the project and create the bloaty runtime blob. fn build_bloaty_blob( + target: RuntimeTarget, blob_build_profile: &Profile, project: &Path, default_rustflags: &str, cargo_cmd: CargoCommandVersioned, -) { +) -> PathBuf { let manifest_path = project.join("Cargo.toml"); let mut build_cmd = cargo_cmd.command(); - let rustflags = format!( - "-C target-cpu=mvp -C target-feature=-sign-ext -C link-arg=--export-table {} {}", - default_rustflags, - env::var(crate::WASM_BUILD_RUSTFLAGS_ENV).unwrap_or_default(), - ); + let mut rustflags = String::new(); + match target { + RuntimeTarget::Wasm => { + rustflags.push_str( + "-C target-cpu=mvp -C target-feature=-sign-ext -C link-arg=--export-table ", + ); + }, + RuntimeTarget::Riscv => { + rustflags.push_str("-C target-feature=+lui-addi-fusion -C relocation-model=pie -C link-arg=--emit-relocs -C link-arg=--unique "); + }, + } + + rustflags.push_str(default_rustflags); + rustflags.push_str(" --cfg substrate_runtime "); + rustflags.push_str(&env::var(crate::WASM_BUILD_RUSTFLAGS_ENV).unwrap_or_default()); build_cmd - .args(&["rustc", "--target=wasm32-unknown-unknown"]) + .arg("rustc") + .arg(format!("--target={}", target.rustc_target())) .arg(format!("--manifest-path={}", manifest_path.display())) .env("RUSTFLAGS", rustflags) // Manually set the `CARGO_TARGET_DIR` to prevent a cargo deadlock (cargo locks a target dir @@ -737,6 +814,9 @@ fn build_bloaty_blob( // our own `RUSTFLAGS` and thus, we need to remove this. Otherwise cargo favors this // env variable. .env_remove("CARGO_ENCODED_RUSTFLAGS") + // Make sure if we're called from within a `build.rs` the host toolchain won't override a + // rustup toolchain we've picked. + .env_remove("RUSTC") // We don't want to call ourselves recursively .env(crate::SKIP_BUILD_ENV, ""); @@ -775,9 +855,55 @@ fn build_bloaty_blob( println!("{} {}", colorize_info_message("Using rustc version:"), cargo_cmd.rustc_version()); // Use `process::exit(1)` to have a clean error output. - if build_cmd.status().map(|s| s.success()).is_err() { + if !matches!(build_cmd.status().map(|s| s.success()), Ok(true)) { process::exit(1); } + + let blob_name = get_blob_name(target, &manifest_path); + let target_directory = project + .join("target") + .join(target.rustc_target()) + .join(blob_build_profile.directory()); + match target { + RuntimeTarget::Riscv => { + let elf_path = target_directory.join(&blob_name); + let elf_metadata = match elf_path.metadata() { + Ok(path) => path, + Err(error) => + panic!("internal error: couldn't read the metadata of {elf_path:?}: {error}"), + }; + + let polkavm_path = target_directory.join(format!("{}.polkavm", blob_name)); + if polkavm_path + .metadata() + .map(|polkavm_metadata| { + polkavm_metadata.modified().unwrap() < elf_metadata.modified().unwrap() + }) + .unwrap_or(true) + { + let blob_bytes = + std::fs::read(elf_path).expect("binary always exists after its built"); + + let mut config = polkavm_linker::Config::default(); + config.set_strip(true); // TODO: This shouldn't always be done. + + let program = match polkavm_linker::program_from_elf(config, &blob_bytes) { + Ok(program) => program, + Err(error) => { + println!("Failed to link the runtime blob; this is probably a bug!"); + println!("Linking error: {error}"); + process::exit(1); + }, + }; + + std::fs::write(&polkavm_path, program.as_bytes()) + .expect("writing the blob to a file always works"); + } + + polkavm_path + }, + RuntimeTarget::Wasm => target_directory.join(format!("{}.wasm", blob_name)), + } } fn compact_wasm( @@ -786,7 +912,7 @@ fn compact_wasm( cargo_manifest: &Path, out_name: &str, ) -> Option { - let default_out_name = get_blob_name(cargo_manifest); + let default_out_name = get_blob_name(RuntimeTarget::Wasm, cargo_manifest); let in_path = project .join("target/wasm32-unknown-unknown") .join(inner_profile.directory()) @@ -973,6 +1099,7 @@ fn generate_rerun_if_changed_instructions( println!("cargo:rerun-if-env-changed={}", crate::WASM_TARGET_DIRECTORY); println!("cargo:rerun-if-env-changed={}", crate::WASM_BUILD_TOOLCHAIN); println!("cargo:rerun-if-env-changed={}", crate::WASM_BUILD_STD); + println!("cargo:rerun-if-env-changed={}", crate::RUNTIME_TARGET); } /// Track files and paths related to the given package to rerun `build.rs` on any relevant change. @@ -1018,7 +1145,7 @@ fn copy_blob_to_target_directory(cargo_manifest: &Path, blob_binary: &WasmBinary fs::copy( blob_binary.wasm_binary_path(), - target_dir.join(format!("{}.wasm", get_blob_name(cargo_manifest))), + target_dir.join(format!("{}.wasm", get_blob_name(RuntimeTarget::Wasm, cargo_manifest))), ) .expect("Copies blob binary to `WASM_TARGET_DIRECTORY`."); } diff --git a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl index ea0f15e5d8ace1315e231c6db2e01399e0ce44f3..b68bce508c008396d699e67e08b98cdf74ba5138 100644 --- a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl +++ b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl @@ -34,8 +34,8 @@ bob: reports block height is at least {{DB_BLOCK_HEIGHT}} within 10 seconds alice: reports substrate_beefy_best_block is at least {{DB_BLOCK_HEIGHT}} within 180 seconds bob: reports substrate_beefy_best_block is at least {{DB_BLOCK_HEIGHT}} within 180 seconds -alice: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 60 seconds -bob: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 60 seconds +alice: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 180 seconds +bob: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 180 seconds alice: count of log lines containing "error" is 0 within 10 seconds bob: count of log lines containing "verification failed" is 0 within 10 seconds diff --git a/templates/minimal/README.md b/templates/minimal/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/templates/minimal/node/Cargo.toml b/templates/minimal/node/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..410cfc9f6c34844a2c6edb5390c6e16c50cc4406 --- /dev/null +++ b/templates/minimal/node/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "minimal-template-node" +description = "A miniaml Substrate-based Substrate node, ready for hacking." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false +build = "build.rs" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +clap = { version = "4.5.1", features = ["derive"] } +futures = { version = "0.3.21", features = ["thread-pool"] } +futures-timer = "3.0.1" +jsonrpsee = { version = "0.22", features = ["server"] } +serde_json = { workspace = true, default-features = true } + +sc-cli = { path = "../../../substrate/client/cli" } +sc-executor = { path = "../../../substrate/client/executor" } +sc-network = { path = "../../../substrate/client/network" } +sc-service = { path = "../../../substrate/client/service" } +sc-telemetry = { path = "../../../substrate/client/telemetry" } +sc-transaction-pool = { path = "../../../substrate/client/transaction-pool" } +sc-transaction-pool-api = { path = "../../../substrate/client/transaction-pool/api" } +sc-consensus = { path = "../../../substrate/client/consensus/common" } +sc-consensus-manual-seal = { path = "../../../substrate/client/consensus/manual-seal" } +sc-rpc-api = { path = "../../../substrate/client/rpc-api" } +sc-basic-authorship = { path = "../../../substrate/client/basic-authorship" } +sc-offchain = { path = "../../../substrate/client/offchain" } +sc-client-api = { path = "../../../substrate/client/api" } + +sp-timestamp = { path = "../../../substrate/primitives/timestamp" } +sp-keyring = { path = "../../../substrate/primitives/keyring" } +sp-api = { path = "../../../substrate/primitives/api" } +sp-blockchain = { path = "../../../substrate/primitives/blockchain" } +sp-block-builder = { path = "../../../substrate/primitives/block-builder" } +sp-io = { path = "../../../substrate/primitives/io" } +sp-runtime = { path = "../../../substrate/primitives/runtime" } + +substrate-frame-rpc-system = { path = "../../../substrate/utils/frame/rpc/system" } + +# Once the native runtime is gone, there should be little to no dependency on FRAME here, and +# certainly no dependency on the runtime. +frame = { path = "../../../substrate/frame", features = [ + "experimental", + "runtime", +] } +runtime = { package = "minimal-template-runtime", path = "../runtime" } + +[build-dependencies] +substrate-build-script-utils = { path = "../../../substrate/utils/build-script-utils" } + +[features] +default = [] diff --git a/substrate/bin/minimal/node/build.rs b/templates/minimal/node/build.rs similarity index 100% rename from substrate/bin/minimal/node/build.rs rename to templates/minimal/node/build.rs diff --git a/substrate/bin/minimal/node/src/chain_spec.rs b/templates/minimal/node/src/chain_spec.rs similarity index 100% rename from substrate/bin/minimal/node/src/chain_spec.rs rename to templates/minimal/node/src/chain_spec.rs diff --git a/substrate/bin/minimal/node/src/cli.rs b/templates/minimal/node/src/cli.rs similarity index 100% rename from substrate/bin/minimal/node/src/cli.rs rename to templates/minimal/node/src/cli.rs diff --git a/substrate/bin/minimal/node/src/command.rs b/templates/minimal/node/src/command.rs similarity index 100% rename from substrate/bin/minimal/node/src/command.rs rename to templates/minimal/node/src/command.rs diff --git a/substrate/bin/minimal/node/src/lib.rs b/templates/minimal/node/src/lib.rs similarity index 94% rename from substrate/bin/minimal/node/src/lib.rs rename to templates/minimal/node/src/lib.rs index c2065def736aeeb3b1102bade04063532128f3cd..cb8ed3bd209dd24d1bfa9d53b904d411acbea02e 100644 --- a/substrate/bin/minimal/node/src/lib.rs +++ b/templates/minimal/node/src/lib.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot Sdk. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/substrate/bin/minimal/node/src/main.rs b/templates/minimal/node/src/main.rs similarity index 100% rename from substrate/bin/minimal/node/src/main.rs rename to templates/minimal/node/src/main.rs diff --git a/substrate/bin/minimal/node/src/rpc.rs b/templates/minimal/node/src/rpc.rs similarity index 100% rename from substrate/bin/minimal/node/src/rpc.rs rename to templates/minimal/node/src/rpc.rs diff --git a/substrate/bin/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs similarity index 100% rename from substrate/bin/minimal/node/src/service.rs rename to templates/minimal/node/src/service.rs diff --git a/templates/minimal/pallets/template/Cargo.toml b/templates/minimal/pallets/template/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..9982e5ea53bc3eff28fb0331cfb14ad41a15eec7 --- /dev/null +++ b/templates/minimal/pallets/template/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "pallet-minimal-template" +description = "A minimal pallet built with FRAME, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", features = [ + "derive", +], default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } +frame = { path = "../../../../substrate/frame", default-features = false, features = [ + "experimental", + "runtime", +] } + + +[features] +default = ["std"] +std = ["codec/std", "frame/std", "scale-info/std"] diff --git a/templates/minimal/pallets/template/src/lib.rs b/templates/minimal/pallets/template/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..713f014bbe61fc9fa7df5019c00afddebf02bbc6 --- /dev/null +++ b/templates/minimal/pallets/template/src/lib.rs @@ -0,0 +1,19 @@ +//! A shell pallet built with [`frame`]. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame::prelude::*; + +// Re-export all pallet parts, this is needed to properly import the pallet into the runtime. +pub use pallet::*; + +#[frame::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); +} diff --git a/templates/minimal/runtime/Cargo.toml b/templates/minimal/runtime/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..20ffb706eb4992b71a7b34d638b7c4cdc70237fd --- /dev/null +++ b/templates/minimal/runtime/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "minimal-template-runtime" +description = "A solochain runtime template built with Substrate, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +parity-scale-codec = { version = "3.0.0", default-features = false } +scale-info = { version = "2.6.0", default-features = false } + +# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. +frame = { path = "../../../substrate/frame", default-features = false, features = [ + "experimental", + "runtime", +] } + +# pallets that we want to use +pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } +pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false } +pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-features = false } +pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { path = "../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } + +# genesis builder that allows us to interacto with runtime genesis config +sp-genesis-builder = { path = "../../../substrate/primitives/genesis-builder", default-features = false } + +# local pallet templates +pallet-minimal-template = { path = "../pallets/template", default-features = false } + +[build-dependencies] +substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } + +[features] +default = ["std"] +std = [ + "parity-scale-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", + "substrate-wasm-builder", +] diff --git a/substrate/bin/minimal/runtime/build.rs b/templates/minimal/runtime/build.rs similarity index 100% rename from substrate/bin/minimal/runtime/build.rs rename to templates/minimal/runtime/build.rs diff --git a/substrate/bin/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs similarity index 92% rename from substrate/bin/minimal/runtime/src/lib.rs rename to templates/minimal/runtime/src/lib.rs index 610289693d91b778295b3e48569fc887f805a204..6920511e2765cd8c62f9049b5d06e960885b371f 100644 --- a/substrate/bin/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -22,21 +22,24 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use frame::{ - deps::frame_support::weights::{FixedFee, NoFee}, + deps::frame_support::{ + genesis_builder_helper::{build_config, create_default_config}, + weights::{FixedFee, NoFee}, + }, prelude::*, runtime::{ apis::{ - self, impl_runtime_apis, ApplyExtrinsicResult, CheckInherentsResult, OpaqueMetadata, + self, impl_runtime_apis, ApplyExtrinsicResult, CheckInherentsResult, + ExtrinsicInclusionMode, OpaqueMetadata, }, prelude::*, }, }; -use frame_support::genesis_builder_helper::{build_config, create_default_config}; #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("minimal-runtime"), - impl_name: create_runtime_str!("minimal-runtime"), + spec_name: create_runtime_str!("minimal-template-runtime"), + impl_name: create_runtime_str!("minimal-template-runtime"), authoring_version: 1, spec_version: 0, impl_version: 1, @@ -51,7 +54,7 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -type SignedExtra = ( +type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -70,6 +73,9 @@ construct_runtime!( Balances: pallet_balances, Sudo: pallet_sudo, TransactionPayment: pallet_transaction_payment, + + // our local pallet + Template: pallet_minimal_template, } ); @@ -103,7 +109,9 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = FixedFee<1, ::Balance>; } -type Block = frame::runtime::types_common::BlockOf; +impl pallet_minimal_template::Config for Runtime {} + +type Block = frame::runtime::types_common::BlockOf; type Header = HeaderFor; type RuntimeExecutive = @@ -121,7 +129,7 @@ impl_runtime_apis! { RuntimeExecutive::execute_block(block) } - fn initialize_block(header: &Header) { + fn initialize_block(header: &Header) -> ExtrinsicInclusionMode { RuntimeExecutive::initialize_block(header) } } diff --git a/cumulus/parachain-template/LICENSE b/templates/parachain/LICENSE similarity index 100% rename from cumulus/parachain-template/LICENSE rename to templates/parachain/LICENSE diff --git a/cumulus/parachain-template/README.md b/templates/parachain/README.md similarity index 100% rename from cumulus/parachain-template/README.md rename to templates/parachain/README.md diff --git a/cumulus/parachain-template/node/Cargo.toml b/templates/parachain/node/Cargo.toml similarity index 72% rename from cumulus/parachain-template/node/Cargo.toml rename to templates/parachain/node/Cargo.toml index c12ee72f5cb3fe343aaa5b886a1bf8bbdaecb9bb..b4f3a504a4d9d7aebef6262d5677e6ff9967b8c2 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/templates/parachain/node/Cargo.toml @@ -1,26 +1,29 @@ [package] name = "parachain-template-node" -version = "0.1.0" -authors = ["Anonymous"] -description = "A new Cumulus FRAME-based Substrate Node, ready for hacking together a parachain." -license = "Unlicense" -homepage = "https://substrate.io" +description = "A parachain node template built with Substrate and Cumulus, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true repository.workspace = true edition.workspace = true -build = "build.rs" publish = false +build = "build.rs" [lints] workspace = true +# [[bin]] +# name = "parachain-template-node" + [dependencies] -clap = { version = "4.4.18", features = ["derive"] } -log = "0.4.20" +clap = { version = "4.5.1", features = ["derive"] } +log = { workspace = true, default-features = true } codec = { package = "parity-scale-codec", version = "3.0.0" } -serde = { version = "1.0.195", features = ["derive"] } -jsonrpsee = { version = "0.16.2", features = ["server"] } +serde = { features = ["derive"], workspace = true, default-features = true } +jsonrpsee = { version = "0.22", features = ["server"] } futures = "0.3.28" -serde_json = "1.0.111" +serde_json = { workspace = true, default-features = true } # Local parachain-template-runtime = { path = "../runtime" } @@ -63,15 +66,15 @@ polkadot-primitives = { path = "../../../polkadot/primitives" } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } # Cumulus -cumulus-client-cli = { path = "../../client/cli" } -cumulus-client-collator = { path = "../../client/collator" } -cumulus-client-consensus-aura = { path = "../../client/consensus/aura" } -cumulus-client-consensus-common = { path = "../../client/consensus/common" } -cumulus-client-consensus-proposer = { path = "../../client/consensus/proposer" } -cumulus-client-service = { path = "../../client/service" } -cumulus-primitives-core = { path = "../../primitives/core" } -cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent" } -cumulus-relay-chain-interface = { path = "../../client/relay-chain-interface" } +cumulus-client-cli = { path = "../../../cumulus/client/cli" } +cumulus-client-collator = { path = "../../../cumulus/client/collator" } +cumulus-client-consensus-aura = { path = "../../../cumulus/client/consensus/aura" } +cumulus-client-consensus-common = { path = "../../../cumulus/client/consensus/common" } +cumulus-client-consensus-proposer = { path = "../../../cumulus/client/consensus/proposer" } +cumulus-client-service = { path = "../../../cumulus/client/service" } +cumulus-primitives-core = { path = "../../../cumulus/primitives/core" } +cumulus-primitives-parachain-inherent = { path = "../../../cumulus/primitives/parachain-inherent" } +cumulus-relay-chain-interface = { path = "../../../cumulus/client/relay-chain-interface" } color-print = "0.3.4" [build-dependencies] diff --git a/cumulus/parachain-template/node/build.rs b/templates/parachain/node/build.rs similarity index 100% rename from cumulus/parachain-template/node/build.rs rename to templates/parachain/node/build.rs diff --git a/cumulus/parachain-template/node/src/chain_spec.rs b/templates/parachain/node/src/chain_spec.rs similarity index 93% rename from cumulus/parachain-template/node/src/chain_spec.rs rename to templates/parachain/node/src/chain_spec.rs index a79c78699c07102aca27ed06c73f93ccf86ed3ce..16c91865cdb4aa9ae3c5882652100f5ef56a988c 100644 --- a/cumulus/parachain-template/node/src/chain_spec.rs +++ b/templates/parachain/node/src/chain_spec.rs @@ -1,5 +1,6 @@ use cumulus_primitives_core::ParaId; -use parachain_template_runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT}; +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}; @@ -56,8 +57,8 @@ where /// 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) -> parachain_template_runtime::SessionKeys { - parachain_template_runtime::SessionKeys { aura: keys } +pub fn template_session_keys(keys: AuraId) -> runtime::SessionKeys { + runtime::SessionKeys { aura: keys } } pub fn development_config() -> ChainSpec { @@ -68,8 +69,7 @@ pub fn development_config() -> ChainSpec { properties.insert("ss58Format".into(), 42.into()); ChainSpec::builder( - parachain_template_runtime::WASM_BINARY - .expect("WASM binary was not built, please build it!"), + 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! @@ -120,8 +120,7 @@ pub fn local_testnet_config() -> ChainSpec { #[allow(deprecated)] ChainSpec::builder( - parachain_template_runtime::WASM_BINARY - .expect("WASM binary was not built, please build it!"), + 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! diff --git a/cumulus/parachain-template/node/src/cli.rs b/templates/parachain/node/src/cli.rs similarity index 100% rename from cumulus/parachain-template/node/src/cli.rs rename to templates/parachain/node/src/cli.rs diff --git a/cumulus/parachain-template/node/src/command.rs b/templates/parachain/node/src/command.rs similarity index 98% rename from cumulus/parachain-template/node/src/command.rs rename to templates/parachain/node/src/command.rs index 6ddb68a359a786be617e384b16d7292c3db45a88..82624ae0be59e3159477b6f35b95f86177a35754 100644 --- a/cumulus/parachain-template/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -1,5 +1,6 @@ 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}; use log::info; @@ -183,7 +184,7 @@ pub fn run() -> Result<()> { match cmd { BenchmarkCmd::Pallet(cmd) => if cfg!(feature = "runtime-benchmarks") { - runner.sync_run(|config| cmd.run::(config)) + runner.sync_run(|config| cmd.run::, ReclaimHostFunctions>(config)) } else { Err("Benchmarking wasn't enabled when building the node. \ You can enable it with `--features runtime-benchmarks`." diff --git a/cumulus/parachain-template/node/src/main.rs b/templates/parachain/node/src/main.rs similarity index 100% rename from cumulus/parachain-template/node/src/main.rs rename to templates/parachain/node/src/main.rs diff --git a/cumulus/parachain-template/node/src/rpc.rs b/templates/parachain/node/src/rpc.rs similarity index 100% rename from cumulus/parachain-template/node/src/rpc.rs rename to templates/parachain/node/src/rpc.rs diff --git a/cumulus/parachain-template/node/src/service.rs b/templates/parachain/node/src/service.rs similarity index 98% rename from cumulus/parachain-template/node/src/service.rs rename to templates/parachain/node/src/service.rs index 830b6e82f969190b69425bd59a939c49372bf9f3..4dd24803e9b124d386d4912236ae1abd42229802 100644 --- a/cumulus/parachain-template/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -40,7 +40,10 @@ use substrate_prometheus_endpoint::Registry; pub struct ParachainNativeExecutor; impl sc_executor::NativeExecutionDispatch for ParachainNativeExecutor { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + type ExtendHostFunctions = ( + cumulus_client_service::storage_proof_size::HostFunctions, + frame_benchmarking::benchmarking::HostFunctions, + ); fn dispatch(method: &str, data: &[u8]) -> Option> { parachain_template_runtime::api::dispatch(method, data) @@ -100,10 +103,11 @@ pub fn new_partial(config: &Configuration) -> Result let executor = ParachainExecutor::new_with_wasm_executor(wasm); let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( + sc_service::new_full_parts_record_import::( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, + true, )?; let client = Arc::new(client); diff --git a/cumulus/parachain-template/pallets/template/Cargo.toml b/templates/parachain/pallets/template/Cargo.toml similarity index 69% rename from cumulus/parachain-template/pallets/template/Cargo.toml rename to templates/parachain/pallets/template/Cargo.toml index b8d95b5cf7801efa9c5040b61b7d3118da9dd424..89eb9d5171630459e2e313ad94cee53e97921cac 100644 --- a/cumulus/parachain-template/pallets/template/Cargo.toml +++ b/templates/parachain/pallets/template/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-parachain-template" -authors = ["Anonymous"] description = "FRAME pallet template for defining custom runtime logic." -version = "0.1.0" -license = "Unlicense" -homepage = "https://substrate.io" +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true repository.workspace = true edition.workspace = true +publish = false [lints] workspace = true @@ -15,21 +16,22 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"], default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } -# Substrate +# frame deps frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } frame-support = { path = "../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../substrate/frame/system", default-features = false } [dev-dependencies] -serde = { version = "1.0.195" } - -# Substrate -sp-core = { path = "../../../../substrate/primitives/core", default-features = false } -sp-io = { path = "../../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +sp-core = { path = "../../../../substrate/primitives/core" } +sp-io = { path = "../../../../substrate/primitives/io" } +sp-runtime = { path = "../../../../substrate/primitives/runtime" } [features] default = ["std"] @@ -41,7 +43,7 @@ runtime-benchmarks = [ ] std = [ "codec/std", - "frame-benchmarking/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "scale-info/std", diff --git a/substrate/bin/node-template/pallets/template/README.md b/templates/parachain/pallets/template/README.md similarity index 100% rename from substrate/bin/node-template/pallets/template/README.md rename to templates/parachain/pallets/template/README.md diff --git a/substrate/bin/node-template/pallets/template/src/benchmarking.rs b/templates/parachain/pallets/template/src/benchmarking.rs similarity index 100% rename from substrate/bin/node-template/pallets/template/src/benchmarking.rs rename to templates/parachain/pallets/template/src/benchmarking.rs diff --git a/cumulus/parachain-template/pallets/template/src/lib.rs b/templates/parachain/pallets/template/src/lib.rs similarity index 96% rename from cumulus/parachain-template/pallets/template/src/lib.rs rename to templates/parachain/pallets/template/src/lib.rs index 5f3252bfc3a70aa731bf572493a3797bfef574bb..11587d1df426f485139eab9e333b292768c6c571 100644 --- a/cumulus/parachain-template/pallets/template/src/lib.rs +++ b/templates/parachain/pallets/template/src/lib.rs @@ -11,6 +11,8 @@ mod mock; #[cfg(test)] mod tests; +pub mod weights; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; @@ -24,6 +26,8 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// A type representing the weights required by the dispatchables of this pallet. + type WeightInfo: crate::weights::WeightInfo; } #[pallet::pallet] @@ -32,7 +36,6 @@ pub mod pallet { // The pallet's runtime storage items. // https://docs.substrate.io/v3/runtime/storage #[pallet::storage] - #[pallet::getter(fn something)] // Learn more about declaring storage items: // https://docs.substrate.io/v3/runtime/storage#declaring-storage-items pub type Something = StorageValue<_, u32>; diff --git a/cumulus/parachain-template/pallets/template/src/mock.rs b/templates/parachain/pallets/template/src/mock.rs similarity index 98% rename from cumulus/parachain-template/pallets/template/src/mock.rs rename to templates/parachain/pallets/template/src/mock.rs index 411a16b116c8f94757c29a686022842e159e6924..f510a8b773acf398ef2442254de25e75f867fcbb 100644 --- a/cumulus/parachain-template/pallets/template/src/mock.rs +++ b/templates/parachain/pallets/template/src/mock.rs @@ -51,6 +51,7 @@ impl system::Config for Test { impl crate::Config for Test { type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); } // Build genesis storage according to the mock runtime. diff --git a/cumulus/parachain-template/pallets/template/src/tests.rs b/templates/parachain/pallets/template/src/tests.rs similarity index 86% rename from cumulus/parachain-template/pallets/template/src/tests.rs rename to templates/parachain/pallets/template/src/tests.rs index 527aec8ed00c058e5a9329a50d60267d7f9d1851..9ad3076be2cc9927063e1b50c293cafadf8f3361 100644 --- a/cumulus/parachain-template/pallets/template/src/tests.rs +++ b/templates/parachain/pallets/template/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{mock::*, Error}; +use crate::{mock::*, Error, Something}; use frame_support::{assert_noop, assert_ok}; #[test] @@ -7,7 +7,7 @@ fn it_works_for_default_value() { // Dispatch a signed extrinsic. assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); + assert_eq!(Something::::get(), Some(42)); }); } diff --git a/substrate/bin/node-template/pallets/template/src/weights.rs b/templates/parachain/pallets/template/src/weights.rs similarity index 100% rename from substrate/bin/node-template/pallets/template/src/weights.rs rename to templates/parachain/pallets/template/src/weights.rs diff --git a/cumulus/parachain-template/polkadot-launch/config.json b/templates/parachain/polkadot-launch/config.json similarity index 100% rename from cumulus/parachain-template/polkadot-launch/config.json rename to templates/parachain/polkadot-launch/config.json diff --git a/cumulus/parachain-template/runtime/Cargo.toml b/templates/parachain/runtime/Cargo.toml similarity index 80% rename from cumulus/parachain-template/runtime/Cargo.toml rename to templates/parachain/runtime/Cargo.toml index 3944ff4ca08e0b0f2f6185d2e0037def823ceb63..44e9341b568af1f4dd039f275121b5baa535c820 100644 --- a/cumulus/parachain-template/runtime/Cargo.toml +++ b/templates/parachain/runtime/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "parachain-template-runtime" -version = "0.1.0" -authors = ["Anonymous"] -description = "A new Cumulus FRAME-based Substrate Runtime, ready for hacking together a parachain." -license = "Unlicense" -homepage = "https://substrate.io" +description = "A parachain runtime template built with Substrate and Cumulus, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true repository.workspace = true edition.workspace = true +publish = false [lints] workspace = true @@ -18,16 +19,20 @@ targets = ["x86_64-unknown-linux-gnu"] substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ + "derive", +] } hex-literal = { version = "0.4.1", optional = true } -log = { version = "0.4.20", default-features = false } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +log = { workspace = true } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } smallvec = "1.11.0" # Local pallet-parachain-template = { path = "../pallets/template", default-features = false } -# Substrate +# Substrate / FRAME frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } frame-executive = { path = "../../../substrate/frame/executive", default-features = false } frame-support = { path = "../../../substrate/frame/support", default-features = false } @@ -35,6 +40,8 @@ frame-system = { path = "../../../substrate/frame/system", default-features = fa frame-system-benchmarking = { path = "../../../substrate/frame/system/benchmarking", default-features = false, optional = true } frame-system-rpc-runtime-api = { path = "../../../substrate/frame/system/rpc/runtime-api", default-features = false } frame-try-runtime = { path = "../../../substrate/frame/try-runtime", default-features = false, optional = true } + +# FRAME Pallets pallet-aura = { path = "../../../substrate/frame/aura", default-features = false } pallet-authorship = { path = "../../../substrate/frame/authorship", default-features = false } pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } @@ -44,6 +51,8 @@ pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-features = false } pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } pallet-transaction-payment-rpc-runtime-api = { path = "../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } + +# Substrate Primitives sp-api = { path = "../../../substrate/primitives/api", default-features = false } sp-block-builder = { path = "../../../substrate/primitives/block-builder", default-features = false } sp-consensus-aura = { path = "../../../substrate/primitives/consensus/aura", default-features = false } @@ -66,29 +75,31 @@ xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/x xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } # Cumulus -cumulus-pallet-aura-ext = { path = "../../pallets/aura-ext", default-features = false } -cumulus-pallet-dmp-queue = { path = "../../pallets/dmp-queue", default-features = false } -cumulus-pallet-parachain-system = { path = "../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook"] } -cumulus-pallet-session-benchmarking = { path = "../../pallets/session-benchmarking", default-features = false } -cumulus-pallet-xcm = { path = "../../pallets/xcm", default-features = false } -cumulus-pallet-xcmp-queue = { path = "../../pallets/xcmp-queue", default-features = false } -cumulus-primitives-core = { path = "../../primitives/core", default-features = false } -cumulus-primitives-utility = { path = "../../primitives/utility", default-features = false } -pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false } -parachains-common = { path = "../../parachains/common", default-features = false } -parachain-info = { package = "staging-parachain-info", path = "../../parachains/pallets/parachain-info", default-features = false } +cumulus-pallet-aura-ext = { path = "../../../cumulus/pallets/aura-ext", default-features = false } +cumulus-pallet-parachain-system = { path = "../../../cumulus/pallets/parachain-system", default-features = false, features = [ + "parameterized-consensus-hook", +] } +cumulus-pallet-session-benchmarking = { path = "../../../cumulus/pallets/session-benchmarking", default-features = false } +cumulus-pallet-xcm = { path = "../../../cumulus/pallets/xcm", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../cumulus/pallets/xcmp-queue", default-features = false } +cumulus-primitives-core = { path = "../../../cumulus/primitives/core", default-features = false } +cumulus-primitives-utility = { path = "../../../cumulus/primitives/utility", default-features = false } +cumulus-primitives-storage-weight-reclaim = { path = "../../../cumulus/primitives/storage-weight-reclaim", default-features = false } +pallet-collator-selection = { path = "../../../cumulus/pallets/collator-selection", default-features = false } +parachains-common = { path = "../../../cumulus/parachains/common", default-features = false } +parachain-info = { package = "staging-parachain-info", path = "../../../cumulus/parachains/pallets/parachain-info", default-features = false } [features] default = ["std"] std = [ "codec/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-pallet-session-benchmarking/std", "cumulus-pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", "cumulus-primitives-core/std", + "cumulus-primitives-storage-weight-reclaim/std", "cumulus-primitives-utility/std", "frame-benchmarking?/std", "frame-executive/std", @@ -134,11 +145,11 @@ std = [ ] runtime-benchmarks = [ - "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-storage-weight-reclaim/runtime-benchmarks", "cumulus-primitives-utility/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", @@ -151,6 +162,7 @@ runtime-benchmarks = [ "pallet-parachain-template/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", @@ -162,7 +174,6 @@ runtime-benchmarks = [ try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-dmp-queue/try-runtime", "cumulus-pallet-parachain-system/try-runtime", "cumulus-pallet-xcm/try-runtime", "cumulus-pallet-xcmp-queue/try-runtime", diff --git a/cumulus/parachain-template/runtime/build.rs b/templates/parachain/runtime/build.rs similarity index 100% rename from cumulus/parachain-template/runtime/build.rs rename to templates/parachain/runtime/build.rs diff --git a/cumulus/parachain-template/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs similarity index 97% rename from cumulus/parachain-template/runtime/src/lib.rs rename to templates/parachain/runtime/src/lib.rs index 0ab36eba315ddf702145668b33ea3ece2a05e5a9..74ecd751f672c77424cdae2d3e48c143681127ec 100644 --- a/cumulus/parachain-template/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -97,8 +97,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -107,11 +107,12 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -179,8 +180,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("template-parachain"), - impl_name: create_runtime_str!("template-parachain"), + spec_name: create_runtime_str!("parachain-template-runtime"), + impl_name: create_runtime_str!("parachain-template-runtime"), authoring_version: 1, spec_version: 1, impl_version: 0, @@ -337,7 +338,6 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } @@ -353,6 +353,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = (); } impl pallet_sudo::Config for Runtime { @@ -489,6 +490,7 @@ impl pallet_collator_selection::Config for Runtime { /// Configure the pallet template in pallets/template. impl pallet_parachain_template::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_parachain_template::weights::SubstrateWeight; } // Create the runtime by composing the FRAME pallets that were previously configured. @@ -529,6 +531,7 @@ construct_runtime!( mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_session, SessionBench::] [pallet_timestamp, Timestamp] @@ -560,7 +563,7 @@ impl_runtime_apis! { Executive::execute_block(block) } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -712,6 +715,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; let mut list = Vec::::new(); @@ -727,6 +731,7 @@ impl_runtime_apis! { use frame_benchmarking::{BenchmarkError, Benchmarking, BenchmarkBatch}; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); diff --git a/cumulus/parachain-template/runtime/src/weights/block_weights.rs b/templates/parachain/runtime/src/weights/block_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/block_weights.rs rename to templates/parachain/runtime/src/weights/block_weights.rs diff --git a/cumulus/parachain-template/runtime/src/weights/extrinsic_weights.rs b/templates/parachain/runtime/src/weights/extrinsic_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/extrinsic_weights.rs rename to templates/parachain/runtime/src/weights/extrinsic_weights.rs diff --git a/cumulus/parachain-template/runtime/src/weights/mod.rs b/templates/parachain/runtime/src/weights/mod.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/mod.rs rename to templates/parachain/runtime/src/weights/mod.rs diff --git a/cumulus/parachain-template/runtime/src/weights/paritydb_weights.rs b/templates/parachain/runtime/src/weights/paritydb_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/paritydb_weights.rs rename to templates/parachain/runtime/src/weights/paritydb_weights.rs diff --git a/cumulus/parachain-template/runtime/src/weights/rocksdb_weights.rs b/templates/parachain/runtime/src/weights/rocksdb_weights.rs similarity index 100% rename from cumulus/parachain-template/runtime/src/weights/rocksdb_weights.rs rename to templates/parachain/runtime/src/weights/rocksdb_weights.rs diff --git a/cumulus/parachain-template/runtime/src/xcm_config.rs b/templates/parachain/runtime/src/xcm_config.rs similarity index 93% rename from cumulus/parachain-template/runtime/src/xcm_config.rs rename to templates/parachain/runtime/src/xcm_config.rs index d407292d0bd6d31b78cb1b1bcf88b78fb855cb28..b1230ba1e5d4151f12776040c76437b29152b468 100644 --- a/cumulus/parachain-template/runtime/src/xcm_config.rs +++ b/templates/parachain/runtime/src/xcm_config.rs @@ -12,15 +12,13 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use xcm::latest::prelude::*; -#[allow(deprecated)] -use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, - NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WithComputedOrigin, WithUniqueTopic, + DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, + FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::XcmExecutor; @@ -44,8 +42,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting assets on this chain. -#[allow(deprecated)] -pub type LocalAssetTransactor = CurrencyAdapter< +pub type LocalAssetTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -139,6 +136,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/substrate/bin/node-template/LICENSE b/templates/solochain/LICENSE similarity index 100% rename from substrate/bin/node-template/LICENSE rename to templates/solochain/LICENSE diff --git a/substrate/bin/node-template/README.md b/templates/solochain/README.md similarity index 100% rename from substrate/bin/node-template/README.md rename to templates/solochain/README.md diff --git a/substrate/bin/node-template/docs/rust-setup.md b/templates/solochain/docs/rust-setup.md similarity index 100% rename from substrate/bin/node-template/docs/rust-setup.md rename to templates/solochain/docs/rust-setup.md diff --git a/templates/solochain/env-setup/README.md b/templates/solochain/env-setup/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a7955872d3ff2172bc1c818e597a5b165005bb62 --- /dev/null +++ b/templates/solochain/env-setup/README.md @@ -0,0 +1,9 @@ +# Env setup + +Special files for setting up an environment to work with the template: + +- `rust-toolchain.toml` when working with `rustup`. +- `flake.nix` when working with `nix`. + +These files will be copied by the installer script to the main directory. They are +put into this special directory to not interfere with the normal CI. diff --git a/substrate/bin/node-template/flake.lock b/templates/solochain/env-setup/flake.lock similarity index 100% rename from substrate/bin/node-template/flake.lock rename to templates/solochain/env-setup/flake.lock diff --git a/substrate/bin/node-template/flake.nix b/templates/solochain/env-setup/flake.nix similarity index 100% rename from substrate/bin/node-template/flake.nix rename to templates/solochain/env-setup/flake.nix diff --git a/substrate/bin/node-template/rust-toolchain.toml b/templates/solochain/env-setup/rust-toolchain.toml similarity index 90% rename from substrate/bin/node-template/rust-toolchain.toml rename to templates/solochain/env-setup/rust-toolchain.toml index 2a35c6ed07c1c2a667729d1fa558e3da0cd0457f..f81199a2249993fce8c98bc4a721d77c6c91d3d7 100644 --- a/substrate/bin/node-template/rust-toolchain.toml +++ b/templates/solochain/env-setup/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly" +channel = "stable" components = [ "cargo", "clippy", diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..5e50e6106c1dc43481627faf6803cc76e59c101b --- /dev/null +++ b/templates/solochain/node/Cargo.toml @@ -0,0 +1,92 @@ +[package] +name = "solochain-template-node" +description = "A solochain node template built with Substrate, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +build = "build.rs" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +clap = { version = "4.5.1", features = ["derive"] } +futures = { version = "0.3.21", features = ["thread-pool"] } +serde_json = { workspace = true, default-features = true } +jsonrpsee = { version = "0.22", features = ["server"] } + +# substrate client +sc-cli = { path = "../../../substrate/client/cli" } +sp-core = { path = "../../../substrate/primitives/core" } +sc-executor = { path = "../../../substrate/client/executor" } +sc-network = { path = "../../../substrate/client/network" } +sc-service = { path = "../../../substrate/client/service" } +sc-telemetry = { path = "../../../substrate/client/telemetry" } +sc-transaction-pool = { path = "../../../substrate/client/transaction-pool" } +sc-transaction-pool-api = { path = "../../../substrate/client/transaction-pool/api" } +sc-offchain = { path = "../../../substrate/client/offchain" } +sc-consensus-aura = { path = "../../../substrate/client/consensus/aura" } +sp-consensus-aura = { path = "../../../substrate/primitives/consensus/aura" } +sc-consensus = { path = "../../../substrate/client/consensus/common" } +sc-consensus-grandpa = { path = "../../../substrate/client/consensus/grandpa" } +sp-consensus-grandpa = { path = "../../../substrate/primitives/consensus/grandpa" } +sc-client-api = { path = "../../../substrate/client/api" } +sc-rpc-api = { path = "../../../substrate/client/rpc-api" } +sc-basic-authorship = { path = "../../../substrate/client/basic-authorship" } + +# substrate primitives +sp-runtime = { path = "../../../substrate/primitives/runtime" } +sp-io = { path = "../../../substrate/primitives/io" } +sp-timestamp = { path = "../../../substrate/primitives/timestamp" } +sp-inherents = { path = "../../../substrate/primitives/inherents" } +sp-keyring = { path = "../../../substrate/primitives/keyring" } +sp-api = { path = "../../../substrate/primitives/api" } +sp-blockchain = { path = "../../../substrate/primitives/blockchain" } +sp-block-builder = { path = "../../../substrate/primitives/block-builder" } + +# frame and pallets +frame-system = { path = "../../../substrate/frame/system" } +pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } +pallet-transaction-payment-rpc = { path = "../../../substrate/frame/transaction-payment/rpc" } +substrate-frame-rpc-system = { path = "../../../substrate/utils/frame/rpc/system" } + +# These dependencies are used for runtime benchmarking +frame-benchmarking-cli = { path = "../../../substrate/utils/frame/benchmarking-cli" } + +# Local Dependencies +solochain-template-runtime = { path = "../runtime" } + +# CLI-specific dependencies +try-runtime-cli = { path = "../../../substrate/utils/frame/try-runtime/cli", optional = true } + +[build-dependencies] +substrate-build-script-utils = { path = "../../../substrate/utils/build-script-utils" } + +[features] +default = [] +# Dependencies that are only required if runtime benchmarking should be build. +runtime-benchmarks = [ + "frame-benchmarking-cli/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "solochain-template-runtime/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +# Enable features that allow the runtime to be tried and debugged. Name might be subject to change +# in the near future. +try-runtime = [ + "frame-system/try-runtime", + "pallet-transaction-payment/try-runtime", + "solochain-template-runtime/try-runtime", + "sp-runtime/try-runtime", + "try-runtime-cli/try-runtime", +] diff --git a/substrate/bin/node-template/node/build.rs b/templates/solochain/node/build.rs similarity index 100% rename from substrate/bin/node-template/node/build.rs rename to templates/solochain/node/build.rs diff --git a/substrate/bin/node-template/node/src/benchmarking.rs b/templates/solochain/node/src/benchmarking.rs similarity index 97% rename from substrate/bin/node-template/node/src/benchmarking.rs rename to templates/solochain/node/src/benchmarking.rs index 6e29ad1a123118d953c1fad654bad9343fd8ca53..8710f303e1f03bd8471b38b10c68217a45225d5a 100644 --- a/substrate/bin/node-template/node/src/benchmarking.rs +++ b/templates/solochain/node/src/benchmarking.rs @@ -4,10 +4,10 @@ use crate::service::FullClient; -use node_template_runtime as runtime; use runtime::{AccountId, Balance, BalancesCall, SystemCall}; use sc_cli::Result; use sc_client_api::BlockBackend; +use solochain_template_runtime as runtime; use sp_core::{Encode, Pair}; use sp_inherents::{InherentData, InherentDataProvider}; use sp_keyring::Sr25519Keyring; @@ -109,7 +109,7 @@ pub fn create_benchmark_extrinsic( .checked_next_power_of_two() .map(|c| c / 2) .unwrap_or(2) as u64; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -121,11 +121,12 @@ pub fn create_benchmark_extrinsic( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), - ); + ) + .into(); let raw_payload = runtime::SignedPayload::from_raw( call.clone(), - extra.clone(), + tx_ext.clone(), ( (), runtime::VERSION.spec_version, @@ -143,7 +144,7 @@ pub fn create_benchmark_extrinsic( call, sp_runtime::AccountId32::from(sender.public()).into(), runtime::Signature::Sr25519(signature), - extra, + tx_ext, ) } diff --git a/substrate/bin/node-template/node/src/chain_spec.rs b/templates/solochain/node/src/chain_spec.rs similarity index 97% rename from substrate/bin/node-template/node/src/chain_spec.rs rename to templates/solochain/node/src/chain_spec.rs index 6e0d78f647a594d7e3c247041a7499691edaad1e..be49f2c1fc731ee4e5ece7841151068abf0f0790 100644 --- a/substrate/bin/node-template/node/src/chain_spec.rs +++ b/templates/solochain/node/src/chain_spec.rs @@ -1,5 +1,5 @@ -use node_template_runtime::{AccountId, RuntimeGenesisConfig, Signature, WASM_BINARY}; use sc_service::ChainType; +use solochain_template_runtime::{AccountId, RuntimeGenesisConfig, Signature, WASM_BINARY}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{sr25519, Pair, Public}; diff --git a/substrate/bin/node-template/node/src/cli.rs b/templates/solochain/node/src/cli.rs similarity index 100% rename from substrate/bin/node-template/node/src/cli.rs rename to templates/solochain/node/src/cli.rs diff --git a/substrate/bin/node-template/node/src/command.rs b/templates/solochain/node/src/command.rs similarity index 97% rename from substrate/bin/node-template/node/src/command.rs rename to templates/solochain/node/src/command.rs index a25157693cd4392072703c28b14310f16dc01030..42d1477f22f18579d49fb48fdc91df39b10aaa2b 100644 --- a/substrate/bin/node-template/node/src/command.rs +++ b/templates/solochain/node/src/command.rs @@ -5,9 +5,9 @@ use crate::{ service, }; use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; -use node_template_runtime::{Block, EXISTENTIAL_DEPOSIT}; use sc_cli::SubstrateCli; use sc_service::PartialComponents; +use solochain_template_runtime::{Block, EXISTENTIAL_DEPOSIT}; use sp_keyring::Sr25519Keyring; impl SubstrateCli for Cli { @@ -117,7 +117,7 @@ pub fn run() -> sc_cli::Result<()> { ) } - cmd.run::(config) + cmd.run::, ()>(config) }, BenchmarkCmd::Block(cmd) => { let PartialComponents { client, .. } = service::new_partial(&config)?; diff --git a/substrate/bin/node-template/node/src/main.rs b/templates/solochain/node/src/main.rs similarity index 100% rename from substrate/bin/node-template/node/src/main.rs rename to templates/solochain/node/src/main.rs diff --git a/substrate/bin/node-template/node/src/rpc.rs b/templates/solochain/node/src/rpc.rs similarity index 96% rename from substrate/bin/node-template/node/src/rpc.rs rename to templates/solochain/node/src/rpc.rs index 246391adcbbe88a03b6cf9cf9043d82b8de18b60..fe2b6ca72ede5c8d9f8d878db88efa2da5c5ac97 100644 --- a/substrate/bin/node-template/node/src/rpc.rs +++ b/templates/solochain/node/src/rpc.rs @@ -8,8 +8,8 @@ use std::sync::Arc; use jsonrpsee::RpcModule; -use node_template_runtime::{opaque::Block, AccountId, Balance, Nonce}; use sc_transaction_pool_api::TransactionPool; +use solochain_template_runtime::{opaque::Block, AccountId, Balance, Nonce}; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; diff --git a/substrate/bin/node-template/node/src/service.rs b/templates/solochain/node/src/service.rs similarity index 94% rename from substrate/bin/node-template/node/src/service.rs rename to templates/solochain/node/src/service.rs index 25cd651178411c6338a2b0d1e7836804a3a5b676..dc25f7579129fe3ea86d0686732be5e6baec40ba 100644 --- a/substrate/bin/node-template/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -1,13 +1,13 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. use futures::FutureExt; -use node_template_runtime::{self, opaque::Block, RuntimeApi}; 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_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use solochain_template_runtime::{self, opaque::Block, RuntimeApi}; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use std::{sync::Arc, time::Duration}; @@ -80,23 +80,29 @@ pub fn new_partial(config: &Configuration) -> Result { telemetry.as_ref().map(|x| x.handle()), )?; - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - + let cidp_client = client.clone(); let import_queue = sc_consensus_aura::import_queue::(ImportQueueParams { block_import: grandpa_block_import.clone(), justification_import: Some(Box::new(grandpa_block_import.clone())), client: client.clone(), - create_inherent_data_providers: move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + create_inherent_data_providers: move |parent_hash, _| { + let cidp_client = cidp_client.clone(); + async move { + let slot_duration = sc_consensus_aura::standalone::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, - ); + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); - Ok((slot, timestamp)) + Ok((slot, timestamp)) + } }, spawner: &task_manager.spawn_essential_handle(), registry: config.prometheus_registry(), diff --git a/substrate/bin/node-template/pallets/template/Cargo.toml b/templates/solochain/pallets/template/Cargo.toml similarity index 55% rename from substrate/bin/node-template/pallets/template/Cargo.toml rename to templates/solochain/pallets/template/Cargo.toml index 51410a71c7bcee0267f36bbfcf20c616a5537ce3..bd2347151989df8950dc4dd96f039c74241d28a5 100644 --- a/substrate/bin/node-template/pallets/template/Cargo.toml +++ b/templates/solochain/pallets/template/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "pallet-template" -version = "4.0.0-dev" description = "FRAME pallet template for defining custom runtime logic." -authors = ["Substrate DevHub "] -homepage = "https://substrate.io" -edition.workspace = true +version = "0.0.0" license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true publish = false -repository = "https://github.com/substrate-developer-hub/substrate-node-template/" [lints] workspace = true @@ -19,16 +19,19 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -frame-benchmarking = { path = "../../../../frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../../frame/support", default-features = false } -frame-system = { path = "../../../../frame/system", default-features = false } -sp-std = { path = "../../../../primitives/std", default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", +] } + +# frame deps +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } [dev-dependencies] -sp-core = { path = "../../../../primitives/core" } -sp-io = { path = "../../../../primitives/io" } -sp-runtime = { path = "../../../../primitives/runtime" } +sp-core = { path = "../../../../substrate/primitives/core" } +sp-io = { path = "../../../../substrate/primitives/io" } +sp-runtime = { path = "../../../../substrate/primitives/runtime" } [features] default = ["std"] @@ -41,7 +44,6 @@ std = [ "sp-core/std", "sp-io/std", "sp-runtime/std", - "sp-std/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/templates/solochain/pallets/template/README.md b/templates/solochain/pallets/template/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9e4dc55267d69c47fff971cb0427bcb2e0ff871c --- /dev/null +++ b/templates/solochain/pallets/template/README.md @@ -0,0 +1 @@ +License: MIT-0 diff --git a/templates/solochain/pallets/template/src/benchmarking.rs b/templates/solochain/pallets/template/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..5a262417629c579c6ecf5ada30ae803217623766 --- /dev/null +++ b/templates/solochain/pallets/template/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/substrate/bin/node-template/pallets/template/src/lib.rs b/templates/solochain/pallets/template/src/lib.rs similarity index 98% rename from substrate/bin/node-template/pallets/template/src/lib.rs rename to templates/solochain/pallets/template/src/lib.rs index 4a2e53baa774ee5ca333602024d458aa078f9c10..90dfe370145856cc08483cdbce2bbc542b21b2b9 100644 --- a/substrate/bin/node-template/pallets/template/src/lib.rs +++ b/templates/solochain/pallets/template/src/lib.rs @@ -90,9 +90,7 @@ pub mod pallet { /// /// In this template, we are declaring a storage item called `Something` that stores a single /// `u32` value. Learn more about runtime storage here: - /// The [`getter`] macro generates a function to conveniently retrieve the value from storage. #[pallet::storage] - #[pallet::getter(fn something)] pub type Something = StorageValue<_, u32>; /// Events that functions in this pallet can emit. @@ -187,7 +185,7 @@ pub mod pallet { let _who = ensure_signed(origin)?; // Read a value from storage. - match Pallet::::something() { + match Something::::get() { // Return an error if the value has not been set. None => Err(Error::::NoneValue.into()), Some(old) => { diff --git a/substrate/bin/node-template/pallets/template/src/mock.rs b/templates/solochain/pallets/template/src/mock.rs similarity index 100% rename from substrate/bin/node-template/pallets/template/src/mock.rs rename to templates/solochain/pallets/template/src/mock.rs diff --git a/substrate/bin/node-template/pallets/template/src/tests.rs b/templates/solochain/pallets/template/src/tests.rs similarity index 88% rename from substrate/bin/node-template/pallets/template/src/tests.rs rename to templates/solochain/pallets/template/src/tests.rs index 7c2b853ee4dc56fdf526a17557fe37281a50e803..83e4bea7377b348d8ac1d389813a788f79b81970 100644 --- a/substrate/bin/node-template/pallets/template/src/tests.rs +++ b/templates/solochain/pallets/template/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{mock::*, Error, Event}; +use crate::{mock::*, Error, Event, Something}; use frame_support::{assert_noop, assert_ok}; #[test] @@ -9,7 +9,7 @@ fn it_works_for_default_value() { // Dispatch a signed extrinsic. assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); + assert_eq!(Something::::get(), Some(42)); // Assert that the correct event was deposited System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into()); }); diff --git a/templates/solochain/pallets/template/src/weights.rs b/templates/solochain/pallets/template/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..7c42936e09f292de831d28460a3bc39436c3323f --- /dev/null +++ b/templates/solochain/pallets/template/src/weights.rs @@ -0,0 +1,90 @@ + +//! Autogenerated weights for pallet_template +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ../../target/release/node-template +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_template +// --extrinsic +// * +// --steps=50 +// --repeat=20 +// --wasm-execution=compiled +// --output +// pallets/template/src/weights.rs +// --template +// ../../.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_template. +pub trait WeightInfo { + fn do_something() -> Weight; + fn cause_error() -> Weight; +} + +/// Weights for pallet_template using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/templates/solochain/runtime/Cargo.toml b/templates/solochain/runtime/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..4f22e7ff6a3b3500a5e788e93dc8c653f13443d9 --- /dev/null +++ b/templates/solochain/runtime/Cargo.toml @@ -0,0 +1,152 @@ +[package] +name = "solochain-template-runtime" +description = "A solochain runtime template built with Substrate, part of Polkadot Sdk." +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.10.0", default-features = false, features = [ + "derive", + "serde", +] } + +# frame +frame-support = { path = "../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../substrate/frame/system", default-features = false } +frame-try-runtime = { path = "../../../substrate/frame/try-runtime", default-features = false, optional = true } +frame-executive = { path = "../../../substrate/frame/executive", default-features = false } + +# frame pallets +pallet-aura = { path = "../../../substrate/frame/aura", default-features = false } +pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } +pallet-grandpa = { path = "../../../substrate/frame/grandpa", default-features = false } +pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false } +pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-features = false } +pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } + +# primitives +sp-api = { path = "../../../substrate/primitives/api", default-features = false } +sp-block-builder = { path = "../../../substrate/primitives/block-builder", default-features = false } +sp-consensus-aura = { path = "../../../substrate/primitives/consensus/aura", default-features = false, features = [ + "serde", +] } +sp-consensus-grandpa = { path = "../../../substrate/primitives/consensus/grandpa", default-features = false, features = [ + "serde", +] } +sp-core = { path = "../../../substrate/primitives/core", default-features = false, features = [ + "serde", +] } +sp-inherents = { path = "../../../substrate/primitives/inherents", default-features = false } +sp-offchain = { path = "../../../substrate/primitives/offchain", default-features = false } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false, features = [ + "serde", +] } +sp-session = { path = "../../../substrate/primitives/session", default-features = false } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } +sp-storage = { path = "../../../substrate/primitives/storage", default-features = false } +sp-transaction-pool = { path = "../../../substrate/primitives/transaction-pool", default-features = false } +sp-version = { path = "../../../substrate/primitives/version", default-features = false, features = [ + "serde", +] } +sp-genesis-builder = { default-features = false, path = "../../../substrate/primitives/genesis-builder" } + +# RPC related +frame-system-rpc-runtime-api = { path = "../../../substrate/frame/system/rpc/runtime-api", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { path = "../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } + +# Used for runtime benchmarking +frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-system-benchmarking = { path = "../../../substrate/frame/system/benchmarking", default-features = false, optional = true } + +# The pallet in this template. +pallet-template = { path = "../pallets/template", default-features = false } + +[build-dependencies] +substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder", optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + + "frame-executive/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + + "frame-benchmarking?/std", + "frame-try-runtime?/std", + + "pallet-aura/std", + "pallet-balances/std", + "pallet-grandpa/std", + "pallet-sudo/std", + "pallet-template/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-consensus-grandpa/std", + "sp-core/std", + "sp-genesis-builder/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-storage/std", + "sp-transaction-pool/std", + "sp-version/std", + + "substrate-wasm-builder", +] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-grandpa/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-template/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] + +try-runtime = [ + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-aura/try-runtime", + "pallet-balances/try-runtime", + "pallet-grandpa/try-runtime", + "pallet-sudo/try-runtime", + "pallet-template/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "sp-runtime/try-runtime", +] + +experimental = ["pallet-aura/experimental"] diff --git a/substrate/bin/node-template/runtime/build.rs b/templates/solochain/runtime/build.rs similarity index 100% rename from substrate/bin/node-template/runtime/build.rs rename to templates/solochain/runtime/build.rs diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs similarity index 95% rename from substrate/bin/node-template/runtime/src/lib.rs rename to templates/solochain/runtime/src/lib.rs index e9ccd5697dfeda34a302a9349b83efcb94843126..409b639f021e37b805eb114a4501498e12adad2d 100644 --- a/substrate/bin/node-template/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -1,8 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit = "256"] -// Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -22,7 +19,6 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; use frame_support::genesis_builder_helper::{build_config, create_default_config}; -// A few exports that help ease life for downstream crates. pub use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ @@ -95,8 +91,8 @@ pub mod opaque { // https://docs.substrate.io/main-docs/build/upgrade#runtime-versioning #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("node-template"), - impl_name: create_runtime_str!("node-template"), + spec_name: create_runtime_str!("solochain-template-runtime"), + impl_name: create_runtime_str!("solochain-template-runtime"), authoring_version: 1, // The version of the runtime specification. A full node will not attempt to use its native // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, @@ -228,7 +224,6 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); - type MaxHolds = (); } parameter_types! { @@ -242,6 +237,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = IdentityFee; type LengthToFee = IdentityFee; type FeeMultiplierUpdate = ConstFeeMultiplier; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } impl pallet_sudo::Config for Runtime { @@ -266,6 +262,7 @@ construct_runtime!( Balances: pallet_balances, TransactionPayment: pallet_transaction_payment, Sudo: pallet_sudo, + // Include the custom logic from the pallet-template in the runtime. TemplateModule: pallet_template, } @@ -277,8 +274,8 @@ pub type Address = sp_runtime::MultiAddress; pub type Header = generic::Header; /// Block type as expected by this runtime. pub type Block = generic::Block; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -297,9 +294,9 @@ type Migrations = (); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -310,15 +307,12 @@ pub type Executive = frame_executive::Executive< Migrations, >; -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_timestamp, Timestamp] [pallet_sudo, Sudo] @@ -336,7 +330,7 @@ impl_runtime_apis! { Executive::execute_block(block); } - fn initialize_block(header: &::Header) { + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { Executive::initialize_block(header) } } @@ -503,6 +497,7 @@ impl_runtime_apis! { use frame_benchmarking::{baseline, Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; let mut list = Vec::::new(); @@ -519,6 +514,7 @@ impl_runtime_apis! { use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch}; use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; impl frame_system_benchmarking::Config for Runtime {}